78 lines
2.7 KiB
Python
78 lines
2.7 KiB
Python
import sys
|
|
import json
|
|
import logging
|
|
import os
|
|
|
|
# Add project root to sys.path so we can import api_hook_client
|
|
sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), "..")))
|
|
|
|
try:
|
|
from api_hook_client import ApiHookClient
|
|
except ImportError:
|
|
# Fallback for if we are running from root or other locations
|
|
sys.path.append(os.path.abspath(os.path.dirname(__file__)))
|
|
from api_hook_client import ApiHookClient
|
|
|
|
def main():
|
|
# Setup basic logging to stderr so it doesn't interfere with stdout JSON
|
|
logging.basicConfig(level=logging.ERROR, stream=sys.stderr)
|
|
|
|
try:
|
|
# 1. Read JSON from sys.stdin
|
|
input_data = sys.stdin.read()
|
|
if not input_data:
|
|
return
|
|
|
|
hook_input = json.loads(input_data)
|
|
|
|
# 2. Extract 'tool_name' and 'tool_input'
|
|
tool_name = hook_input.get('tool_name')
|
|
tool_args = hook_input.get('tool_input', {})
|
|
|
|
# 3. Check context — if not running via Manual Slop, we pass through (allow)
|
|
# This prevents the hook from affecting normal CLI usage.
|
|
hook_context = os.environ.get("GEMINI_CLI_HOOK_CONTEXT")
|
|
if hook_context != "manual_slop":
|
|
print(json.dumps({
|
|
"decision": "allow",
|
|
"reason": "Non-programmatic usage (GEMINI_CLI_HOOK_CONTEXT not set)."
|
|
}))
|
|
return
|
|
|
|
# 4. Use 'ApiHookClient' (assuming GUI is on http://127.0.0.1:8999)
|
|
client = ApiHookClient(base_url="http://127.0.0.1:8999")
|
|
|
|
try:
|
|
# 5. Request confirmation
|
|
# This is a blocking call that waits for the user in the GUI
|
|
response = client.request_confirmation(tool_name, tool_args)
|
|
|
|
if response and response.get('approved') is True:
|
|
# 6. Print 'allow' decision
|
|
print(json.dumps({"decision": "allow"}))
|
|
else:
|
|
# 7. Print 'deny' decision
|
|
print(json.dumps({
|
|
"decision": "deny",
|
|
"reason": "User rejected tool execution in GUI."
|
|
}))
|
|
|
|
except Exception as e:
|
|
# 8. Handle cases where hook server is not reachable
|
|
# If we ARE in manual_slop context but can't reach the server, we should DENY
|
|
# because the user expects to be in control.
|
|
print(json.dumps({
|
|
"decision": "deny",
|
|
"reason": f"Manual Slop hook server unreachable: {str(e)}"
|
|
}))
|
|
|
|
except Exception as e:
|
|
# Fallback for unexpected parsing errors
|
|
print(json.dumps({
|
|
"decision": "deny",
|
|
"reason": f"Internal bridge error: {str(e)}"
|
|
}))
|
|
|
|
if __name__ == "__main__":
|
|
main()
|