machinaos 0.0.76 → 0.0.78
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +143 -107
- package/client/dist/assets/ActionBar-Du2MSFSz.js +1 -0
- package/client/dist/assets/ApiKeyInput-k2LBmBjb.js +1 -0
- package/client/dist/assets/ApiKeyPanel-C_bV9U0X.js +1 -0
- package/client/dist/assets/ApiUsageSection-CmVfwZzL.js +1 -0
- package/client/dist/assets/EmailPanel-CeKIMGu-.js +1 -0
- package/client/dist/assets/OAuthPanel-KA3t3Q2K.js +1 -0
- package/client/dist/assets/QrPairingPanel-NgNpJNuk.js +1 -0
- package/client/dist/assets/RateLimitSection-Du5YNVIA.js +1 -0
- package/client/dist/assets/StatusCard-DNLyayXc.js +1 -0
- package/client/dist/assets/index-DQ0nwhec.js +257 -0
- package/client/dist/assets/index-DxmbVskS.css +1 -0
- package/client/dist/assets/vendor-flow-CZmBvHRo.js +1 -0
- package/client/dist/assets/vendor-icons-CVrPjN2Q.js +22 -0
- package/client/dist/assets/vendor-markdown-CRou3yQ5.js +62 -0
- package/client/dist/assets/vendor-misc-C4VxKHs5.js +1 -0
- package/client/dist/assets/vendor-query-SzWcOU0G.js +1 -0
- package/client/dist/assets/vendor-radix-Dnos29jG.js +56 -0
- package/client/dist/assets/vendor-react-DvWIbVx0.js +1 -0
- package/client/dist/index.html +37 -3
- package/client/index.html +28 -1
- package/client/package.json +44 -40
- package/client/src/App.tsx +2 -0
- package/client/src/Dashboard.tsx +157 -45
- package/client/src/ParameterPanel.tsx +3 -5
- package/client/src/adapters/nodeSpecToDescription.ts +1 -0
- package/client/src/assets/icons/NodeIcon.tsx +32 -0
- package/client/src/assets/icons/index.ts +4 -0
- package/client/src/assets/icons/stripe.svg +1 -0
- package/client/src/assets/icons/themedGlyphs.ts +404 -0
- package/client/src/components/AIAgentNode.tsx +77 -53
- package/client/src/components/GenericNode.tsx +34 -52
- package/client/src/components/OutputPanel.tsx +64 -147
- package/client/src/components/ParameterRenderer.tsx +5 -3
- package/client/src/components/SkillEditorModal.tsx +9 -18
- package/client/src/components/SquareNode.tsx +97 -115
- package/client/src/components/StartNode.tsx +32 -42
- package/client/src/components/SvgFilterDefs.tsx +54 -0
- package/client/src/components/TeamMonitorNode.tsx +12 -14
- package/client/src/components/ToolkitNode.tsx +35 -60
- package/client/src/components/TriggerNode.tsx +43 -77
- package/client/src/components/__tests__/CredentialsModal.test.tsx +49 -45
- package/client/src/components/credentials/CredentialsModal.tsx +98 -30
- package/client/src/components/credentials/CredentialsPalette.tsx +73 -5
- package/client/src/components/credentials/catalogueAdapter.ts +17 -1
- package/client/src/components/credentials/panels/ApiKeyPanel.tsx +102 -37
- package/client/src/components/credentials/panels/EmailPanel.tsx +7 -19
- package/client/src/components/credentials/panels/OAuthPanel.tsx +5 -1
- package/client/src/components/credentials/panels/QrPairingPanel.tsx +1 -3
- package/client/src/components/credentials/primitives/ActionBar.tsx +7 -11
- package/client/src/components/credentials/primitives/OAuthConnect.tsx +19 -28
- package/client/src/components/credentials/sections/ProviderDefaultsSection.tsx +24 -3
- package/client/src/components/credentials/types.ts +12 -2
- package/client/src/components/credentials/useCredentialPanel.ts +43 -19
- package/client/src/components/icons/AIProviderIcons.tsx +16 -0
- package/client/src/components/onboarding/OnboardingWizard.tsx +23 -63
- package/client/src/components/onboarding/nodeRoleClasses.ts +23 -0
- package/client/src/components/onboarding/steps/CanvasStep.tsx +15 -21
- package/client/src/components/onboarding/steps/ConceptsStep.tsx +2 -11
- package/client/src/components/onboarding/steps/GetStartedStep.tsx +2 -10
- package/client/src/components/parameterPanel/InputSection.tsx +9 -7
- package/client/src/components/parameterPanel/MasterSkillEditor.tsx +84 -198
- package/client/src/components/parameterPanel/MiddleSection.tsx +57 -80
- package/client/src/components/parameterPanel/ToolSchemaEditor.tsx +31 -25
- package/client/src/components/parameterPanel/__tests__/InputSection.test.tsx +7 -2
- package/client/src/components/ui/AIResultModal.tsx +1 -1
- package/client/src/components/ui/CollapsibleSection.tsx +9 -5
- package/client/src/components/ui/CommandPalette.tsx +147 -0
- package/client/src/components/ui/CommandPaletteHost.tsx +189 -0
- package/client/src/components/ui/ComponentItem.tsx +13 -7
- package/client/src/components/ui/ComponentPalette.tsx +24 -13
- package/client/src/components/ui/ConsolePanel.tsx +19 -11
- package/client/src/components/ui/DropCap.tsx +28 -0
- package/client/src/components/ui/EditableNodeLabel.tsx +10 -2
- package/client/src/components/ui/InputNodesPanel.tsx +1 -1
- package/client/src/components/ui/Modal.tsx +38 -6
- package/client/src/components/ui/OutputDisplayPanel.tsx +1 -1
- package/client/src/components/ui/SettingsPanel.tsx +42 -13
- package/client/src/components/ui/StatusBar.tsx +108 -0
- package/client/src/components/ui/ThemeSwitcher.tsx +109 -0
- package/client/src/components/ui/TopToolbar.tsx +42 -25
- package/client/src/components/ui/WorkflowSidebar.tsx +32 -16
- package/client/src/components/ui/action-button.tsx +40 -15
- package/client/src/components/ui/button.tsx +24 -1
- package/client/src/components/ui/dropdown-menu.tsx +24 -2
- package/client/src/components/ui/input.tsx +19 -2
- package/client/src/components/ui/select.tsx +15 -0
- package/client/src/components/ui/textarea.tsx +15 -2
- package/client/src/contexts/AuthContext.tsx +148 -109
- package/client/src/contexts/ThemeContext.tsx +93 -17
- package/client/src/contexts/WebSocketContext.tsx +373 -206
- package/client/src/contexts/__tests__/AuthContext.test.tsx +221 -0
- package/client/src/hooks/__tests__/useDragVariable.test.ts +7 -1
- package/client/src/hooks/__tests__/useWorkflowOpsListener.test.ts +142 -0
- package/client/src/hooks/useAppTheme.ts +209 -7
- package/client/src/hooks/useAutoSkillEdges.ts +7 -2
- package/client/src/hooks/useCatalogueQuery.ts +67 -1
- package/client/src/hooks/useDragVariable.ts +1 -1
- package/client/src/hooks/useNodeAllowlist.ts +115 -8
- package/client/src/hooks/useOnboarding.ts +20 -8
- package/client/src/hooks/useParameterPanel.ts +2 -1
- package/client/src/hooks/useReactFlowNodes.ts +2 -1
- package/client/src/hooks/useSound.ts +185 -0
- package/client/src/hooks/useWorkflowManagement.ts +6 -8
- package/client/src/hooks/useWorkflowOpsListener.ts +90 -0
- package/client/src/index.css +65 -3
- package/client/src/lib/__tests__/connectionConfig.test.ts +91 -0
- package/client/src/lib/aiModelProviders.ts +8 -0
- package/client/src/lib/connectionConfig.ts +107 -0
- package/client/src/lib/queryPersist.ts +13 -5
- package/client/src/lib/sound.ts +393 -0
- package/client/src/main.tsx +20 -0
- package/client/src/store/useAppStore.ts +26 -0
- package/client/src/styles/canvasAnimations.ts +37 -36
- package/client/src/styles/theme.ts +36 -20
- package/client/src/test/setup.ts +1 -0
- package/client/src/themes/atomic.css +253 -0
- package/client/src/themes/base.css +373 -0
- package/client/src/themes/cyber.css +890 -0
- package/client/src/themes/dark.css +70 -0
- package/client/src/themes/edo.css +246 -0
- package/client/src/themes/greek.css +293 -0
- package/client/src/themes/light.css +78 -0
- package/client/src/themes/plague.css +253 -0
- package/client/src/themes/renaissance.css +727 -0
- package/client/src/themes/rot.css +249 -0
- package/client/src/themes/steampunk.css +272 -0
- package/client/src/themes/surveillance.css +289 -0
- package/client/src/themes/wasteland.css +250 -0
- package/client/src/types/INodeProperties.ts +5 -0
- package/client/src/types/NodeTypes.ts +11 -1
- package/client/src/types/__tests__/cloudEvents.test.ts +99 -0
- package/client/src/types/cloudEvents.ts +78 -0
- package/client/src/vite-env.d.ts +7 -0
- package/client/tsconfig.json +1 -1
- package/client/vite.config.js +62 -2
- package/install.ps1 +1 -1
- package/install.sh +1 -1
- package/machina/commands/build.py +51 -7
- package/machina/pyproject.toml +4 -0
- package/machina/supervisor.py +12 -2
- package/machina/tree.py +71 -21
- package/package.json +4 -4
- package/scripts/install.js +16 -1
- package/server/config/ai_cli_providers.json +54 -0
- package/server/config/credential_providers.json +109 -2
- package/server/config/llm_defaults.json +24 -0
- package/server/config/model_registry.json +338 -499
- package/server/config/node_allowlist.json +16 -1
- package/server/config/pricing.json +8 -0
- package/server/constants.py +38 -15
- package/server/core/container.py +2 -2
- package/server/core/credentials_database.py +35 -2
- package/server/core/logging.py +4 -3
- package/server/main.py +99 -13
- package/server/models/node_metadata.py +1 -0
- package/server/nodejs/package.json +8 -6
- package/server/nodejs/src/index.ts +22 -5
- package/server/nodes/README.md +31 -4
- package/server/nodes/agent/_inline.py +2 -0
- package/server/nodes/agent/_specialized.py +6 -3
- package/server/nodes/agent/ai_agent.py +13 -3
- package/server/nodes/agent/chat_agent.py +6 -3
- package/server/nodes/agent/claude_code_agent.py +287 -75
- package/server/nodes/agent/codex_agent.py +239 -0
- package/server/nodes/agent/deep_agent.py +3 -3
- package/server/nodes/agent/rlm_agent.py +3 -3
- package/server/nodes/android/__init__.py +31 -1
- package/server/nodes/android/_base.py +9 -5
- package/server/{services/android_service.py → nodes/android/_dispatcher.py} +2 -2
- package/server/nodes/android/_handlers.py +154 -0
- package/server/nodes/android/_option_loaders.py +44 -0
- package/server/nodes/android/_refresh.py +127 -0
- package/server/{services/android → nodes/android/_relay}/client.py +4 -4
- package/server/{routers/android.py → nodes/android/_router.py} +27 -8
- package/server/nodes/browser/browser.py +2 -2
- package/server/nodes/code/_base.py +6 -2
- package/server/nodes/code/_claude_code.py +134 -0
- package/server/nodes/document/embedding_generator.py +3 -3
- package/server/nodes/document/http_scraper.py +3 -3
- package/server/nodes/document/vector_store.py +5 -5
- package/server/nodes/email/__init__.py +11 -1
- package/server/nodes/email/_filters.py +21 -0
- package/server/{services/himalaya_service.py → nodes/email/_himalaya.py} +6 -10
- package/server/{services/email_service.py → nodes/email/_service.py} +9 -13
- package/server/nodes/email/email_read.py +1 -1
- package/server/nodes/email/email_receive.py +54 -5
- package/server/nodes/email/email_send.py +1 -1
- package/server/nodes/filesystem/shell.py +24 -1
- package/server/nodes/google/__init__.py +55 -1
- package/server/{services/handlers/google_auth.py → nodes/google/_auth_helper.py} +8 -5
- package/server/nodes/google/_base.py +2 -2
- package/server/nodes/google/_credentials.py +5 -5
- package/server/nodes/google/_filters.py +25 -0
- package/server/nodes/google/_handlers.py +57 -0
- package/server/{services/google_oauth.py → nodes/google/_oauth.py} +195 -162
- package/server/nodes/google/_option_loaders.py +107 -0
- package/server/nodes/google/_refresh.py +66 -0
- package/server/nodes/google/_router.py +131 -0
- package/server/nodes/google/gmail_receive.py +41 -4
- package/server/nodes/groups.py +1 -0
- package/server/nodes/location/_credentials.py +45 -1
- package/server/{services/maps.py → nodes/location/_service.py} +18 -3
- package/server/nodes/location/gmaps_create.py +4 -4
- package/server/nodes/location/gmaps_locations.py +4 -4
- package/server/nodes/location/gmaps_nearby_places.py +4 -4
- package/server/nodes/model/_base.py +8 -3
- package/server/nodes/model/_credentials.py +96 -8
- package/server/nodes/model/_local_validator.py +345 -0
- package/server/nodes/model/lmstudio_chat_model.py +23 -0
- package/server/nodes/model/ollama_chat_model.py +25 -0
- package/server/nodes/proxy/_usage.py +2 -2
- package/server/nodes/proxy/proxy_config.py +14 -14
- package/server/nodes/proxy/proxy_request.py +4 -4
- package/server/nodes/scraper/_credentials.py +29 -1
- package/server/nodes/scraper/apify_actor.py +9 -9
- package/server/nodes/scraper/crawlee_scraper.py +5 -5
- package/server/nodes/search/brave_search.py +4 -0
- package/server/nodes/search/perplexity_search.py +9 -0
- package/server/nodes/search/serper_search.py +3 -0
- package/server/nodes/skill/simple_memory.py +12 -0
- package/server/nodes/social/_base.py +2 -2
- package/server/nodes/stripe/__init__.py +46 -0
- package/server/nodes/stripe/_credentials.py +33 -0
- package/server/nodes/stripe/_handlers.py +270 -0
- package/server/nodes/stripe/_install.py +127 -0
- package/server/nodes/stripe/_source.py +174 -0
- package/server/nodes/stripe/stripe_action.py +81 -0
- package/server/nodes/stripe/stripe_receive.py +92 -0
- package/server/nodes/telegram/_credentials.py +52 -1
- package/server/nodes/telegram/_handlers.py +19 -18
- package/server/nodes/telegram/_service.py +134 -32
- package/server/nodes/telegram/telegram_send.py +5 -6
- package/server/nodes/text/file_handler.py +2 -2
- package/server/nodes/text/text_generator.py +2 -2
- package/server/nodes/tool/agent_builder.py +630 -0
- package/server/nodes/tool/task_manager.py +144 -2
- package/server/nodes/twitter/__init__.py +38 -1
- package/server/nodes/twitter/_base.py +7 -7
- package/server/nodes/twitter/_credentials.py +1 -1
- package/server/nodes/twitter/_filters.py +37 -0
- package/server/nodes/twitter/_handlers.py +77 -0
- package/server/nodes/twitter/_oauth.py +124 -0
- package/server/nodes/twitter/_refresh.py +78 -0
- package/server/nodes/twitter/_router.py +29 -0
- package/server/nodes/twitter/twitter_receive.py +4 -0
- package/server/nodes/visuals.json +64 -19
- package/server/nodes/whatsapp/__init__.py +45 -5
- package/server/nodes/whatsapp/_base.py +3 -3
- package/server/nodes/whatsapp/_filters.py +137 -0
- package/server/nodes/whatsapp/_handlers.py +167 -0
- package/server/nodes/whatsapp/_option_loaders.py +68 -0
- package/server/nodes/whatsapp/_refresh.py +62 -0
- package/server/nodes/whatsapp/_runtime.py +1 -1
- package/server/pyproject.toml +29 -7
- package/server/routers/schemas.py +2 -2
- package/server/routers/webhook.py +26 -9
- package/server/routers/websocket.py +149 -810
- package/server/services/ai.py +89 -8
- package/server/services/auth.py +220 -43
- package/server/services/claude_oauth.py +126 -100
- package/server/services/cli_agent/__init__.py +78 -0
- package/server/services/cli_agent/_handlers.py +237 -0
- package/server/services/cli_agent/config.py +112 -0
- package/server/services/cli_agent/factory.py +48 -0
- package/server/services/cli_agent/lockfile.py +141 -0
- package/server/services/cli_agent/mcp_server.py +482 -0
- package/server/services/cli_agent/protocol.py +173 -0
- package/server/services/cli_agent/providers/__init__.py +9 -0
- package/server/services/cli_agent/providers/anthropic_claude.py +419 -0
- package/server/services/cli_agent/providers/google_gemini.py +80 -0
- package/server/services/cli_agent/providers/openai_codex.py +310 -0
- package/server/services/cli_agent/service.py +607 -0
- package/server/services/cli_agent/session.py +618 -0
- package/server/services/cli_agent/types.py +227 -0
- package/server/services/cli_agent/workflow_tools.py +233 -0
- package/server/services/credential_registry.py +26 -1
- package/server/services/deployment/manager.py +26 -145
- package/server/services/deployment/poll_registry.py +59 -0
- package/server/services/event_waiter.py +76 -246
- package/server/services/events/__init__.py +54 -0
- package/server/services/events/cli.py +78 -0
- package/server/services/events/daemon.py +163 -0
- package/server/services/events/envelope.py +281 -0
- package/server/services/events/lifecycle.py +99 -0
- package/server/services/events/oauth_lifecycle.py +534 -0
- package/server/services/events/polling.py +60 -0
- package/server/services/events/push.py +36 -0
- package/server/services/events/source.py +63 -0
- package/server/services/events/triggers.py +118 -0
- package/server/services/events/verifiers/__init__.py +25 -0
- package/server/services/events/verifiers/base.py +28 -0
- package/server/services/events/verifiers/github.py +25 -0
- package/server/services/events/verifiers/hmac_basic.py +32 -0
- package/server/services/events/verifiers/standard_webhooks.py +47 -0
- package/server/services/events/verifiers/stripe.py +42 -0
- package/server/services/events/webhook.py +105 -0
- package/server/services/handlers/tools.py +28 -186
- package/server/services/llm/config.py +7 -0
- package/server/services/llm/factory.py +8 -2
- package/server/services/memory/__init__.py +52 -0
- package/server/services/memory/jsonl.py +80 -0
- package/server/services/memory/markdown.py +65 -0
- package/server/services/memory/state.py +112 -0
- package/server/services/memory/vector_store.py +40 -0
- package/server/services/model_registry.py +76 -0
- package/server/services/node_allowlist.py +71 -15
- package/server/services/node_executor.py +2 -2
- package/server/services/node_output_schemas.py +21 -10
- package/server/services/node_spec.py +1 -1
- package/server/services/oauth_utils.py +1 -1
- package/server/services/plugin/__init__.py +2 -0
- package/server/services/plugin/base.py +44 -2
- package/server/services/plugin/credential.py +288 -1
- package/server/services/plugin/deps.py +105 -0
- package/server/services/plugin/edge_walker.py +12 -4
- package/server/services/plugin/oauth.py +381 -0
- package/server/services/plugin/polling.py +247 -0
- package/server/services/plugin/registry.py +145 -0
- package/server/services/plugin/singleton.py +65 -0
- package/server/services/plugin/ws.py +81 -0
- package/server/services/process_service.py +31 -2
- package/server/services/status_broadcaster.py +155 -238
- package/server/services/temporal/workflow.py +7 -7
- package/server/services/workflow.py +21 -3
- package/server/services/ws_handler_registry.py +111 -28
- package/server/skills/GUIDE.md +16 -1
- package/server/skills/assistant/agent-builder-skill/SKILL.md +166 -0
- package/server/skills/payments_agent/stripe-skill/SKILL.md +306 -0
- package/server/tests/credentials/test_auth_service.py +16 -9
- package/server/tests/credentials/test_credential_broadcasts.py +219 -0
- package/server/tests/credentials/test_google_oauth.py +6 -6
- package/server/tests/credentials/test_oauth_utils.py +1 -1
- package/server/tests/credentials/test_twitter_oauth.py +2 -2
- package/server/tests/credentials/test_websocket_handlers.py +44 -20
- package/server/tests/llm/test_factory.py +1 -0
- package/server/tests/llm/test_wiring.py +5 -1
- package/server/tests/nodes/_compat.py +24 -24
- package/server/tests/nodes/test_agent_builder.py +439 -0
- package/server/tests/nodes/test_ai_tools.py +18 -14
- package/server/tests/nodes/test_code_fs_process.py +17 -8
- package/server/tests/nodes/test_email.py +10 -9
- package/server/tests/nodes/test_google_workspace.py +2 -2
- package/server/tests/nodes/test_specialized_agents.py +100 -53
- package/server/tests/nodes/test_stripe_plugin.py +293 -0
- package/server/tests/nodes/test_telegram_social.py +4 -4
- package/server/tests/nodes/test_twitter.py +1 -1
- package/server/tests/nodes/test_web_automation.py +2 -2
- package/server/tests/nodes/test_whatsapp.py +9 -9
- package/server/tests/services/cli_agent/__init__.py +0 -0
- package/server/tests/services/cli_agent/test_mcp_server.py +432 -0
- package/server/tests/services/cli_agent/test_providers.py +358 -0
- package/server/tests/services/cli_agent/test_service.py +298 -0
- package/server/tests/services/memory/__init__.py +0 -0
- package/server/tests/services/memory/test_jsonl.py +188 -0
- package/server/tests/services/test_events.py +333 -0
- package/server/tests/test_node_spec.py +56 -16
- package/server/tests/test_plugin_helpers.py +116 -0
- package/server/tests/test_plugin_self_containment.py +486 -0
- package/server/tests/test_status_broadcasts.py +425 -0
- package/workflows/{AI Assistant_workflow-1777421105154-0m4snkzjf.json → AI Assistant_workflow-1778504793388-ou1m1tz2x.json } +70 -266
- package/workflows/{AI Employee_workflow-1777720598005-u4cm858dv.json → AI Employee_example_workflow-1777720598005-u4cm858dv.json } +112 -112
- package/workflows/Claude Assistant_workflow-1778380124051-mdibn807c.json +709 -0
- package/client/dist/assets/ActionBar-vzPpSR77.js +0 -1
- package/client/dist/assets/ApiKeyInput-Ds7AKFe8.js +0 -1
- package/client/dist/assets/ApiKeyPanel-gfblELep.js +0 -1
- package/client/dist/assets/ApiUsageSection-BMNWTe2r.js +0 -1
- package/client/dist/assets/EmailPanel-B1Om64p5.js +0 -1
- package/client/dist/assets/OAuthPanel-CXyQYGBz.js +0 -1
- package/client/dist/assets/QrPairingPanel-BgNuI1we.js +0 -1
- package/client/dist/assets/RateLimitSection-YYK8sx1T.js +0 -1
- package/client/dist/assets/StatusCard-DuYA5hJR.js +0 -1
- package/client/dist/assets/index-D9tZfgvi.js +0 -363
- package/client/dist/assets/index-al7snTkG.css +0 -1
- package/client/src/components/credentials/providers.tsx +0 -177
- package/server/routers/google.py +0 -277
- package/server/routers/maps.py +0 -142
- package/server/routers/twitter.py +0 -365
- package/server/services/claude_code_service.py +0 -106
- package/server/services/memory.py +0 -159
- package/server/services/node_option_loaders/__init__.py +0 -77
- package/server/services/node_option_loaders/android_loaders.py +0 -55
- package/server/services/node_option_loaders/google_loaders.py +0 -97
- package/server/services/node_option_loaders/whatsapp_loaders.py +0 -69
- package/server/services/twitter_oauth.py +0 -411
- package/server/services/websocket_client.py +0 -29
- /package/server/{services/android → nodes/android/_relay}/__init__.py +0 -0
- /package/server/{services/android → nodes/android/_relay}/broadcaster.py +0 -0
- /package/server/{services/android → nodes/android/_relay}/manager.py +0 -0
- /package/server/{services/android → nodes/android/_relay}/protocol.py +0 -0
- /package/server/{services/browser_service.py → nodes/browser/_service.py} +0 -0
- /package/server/{services/whatsapp_service.py → nodes/whatsapp/_service.py} +0 -0
- /package/server/skills/{task_agent → assistant}/write-todos-skill/SKILL.md +0 -0
|
@@ -0,0 +1,419 @@
|
|
|
1
|
+
"""Anthropic Claude Code CLI provider.
|
|
2
|
+
|
|
3
|
+
Reference implementation for the `AICliProvider` Protocol. Full feature
|
|
4
|
+
set: sessions, resume, budget, turns, allowed_tools, permission_mode,
|
|
5
|
+
MCP lockfile, cost-in-JSON.
|
|
6
|
+
|
|
7
|
+
Subprocess: ``claude -p <prompt> --output-format stream-json --verbose
|
|
8
|
+
--include-partial-messages --include-hook-events --ide
|
|
9
|
+
[--session-id|--resume <UUID>] --model ... --max-turns ...
|
|
10
|
+
--max-budget-usd ... --allowedTools ... --permission-mode ...
|
|
11
|
+
--append-system-prompt ... [--effort ...] [--fallback-model ...]
|
|
12
|
+
[--add-dir ...] [--disallowedTools ...] [--agent ...]``
|
|
13
|
+
|
|
14
|
+
Binary + auth: shared with the auth surface via
|
|
15
|
+
``services.claude_oauth.claude_binary_path()`` — single project-local
|
|
16
|
+
install at ``<repo>/data/claude-machina/npm/`` and ``CLAUDE_CONFIG_DIR``
|
|
17
|
+
set on the spawn env so the agent picks up the same credentials the
|
|
18
|
+
Login button wrote.
|
|
19
|
+
|
|
20
|
+
Final event: ``type == "result"`` carries ``total_cost_usd``,
|
|
21
|
+
``duration_ms``, ``num_turns``, ``session_id``, and the assistant's
|
|
22
|
+
``result`` string.
|
|
23
|
+
"""
|
|
24
|
+
|
|
25
|
+
from __future__ import annotations
|
|
26
|
+
|
|
27
|
+
import json
|
|
28
|
+
from pathlib import Path
|
|
29
|
+
from typing import Any, Dict, List, Optional
|
|
30
|
+
|
|
31
|
+
from core.logging import get_logger
|
|
32
|
+
|
|
33
|
+
from services.claude_oauth import claude_binary_path
|
|
34
|
+
from services.cli_agent.config import get_provider_config
|
|
35
|
+
from services.cli_agent.protocol import CanonicalUsage
|
|
36
|
+
from services.cli_agent.types import ClaudeTaskSpec
|
|
37
|
+
|
|
38
|
+
logger = get_logger(__name__)
|
|
39
|
+
|
|
40
|
+
NAME = "claude"
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
class AnthropicClaudeProvider:
|
|
44
|
+
"""`AICliProvider` for Anthropic's Claude Code CLI."""
|
|
45
|
+
|
|
46
|
+
def __init__(self) -> None:
|
|
47
|
+
cfg = get_provider_config(NAME)
|
|
48
|
+
if cfg is None:
|
|
49
|
+
raise RuntimeError(
|
|
50
|
+
f"Provider config missing for {NAME!r}. Check ai_cli_providers.json."
|
|
51
|
+
)
|
|
52
|
+
self.name = NAME
|
|
53
|
+
self.package_name = cfg.package_name
|
|
54
|
+
self.binary_name = cfg.binary_name
|
|
55
|
+
self.ide_lock_env_var = cfg.ide_lock_env_var
|
|
56
|
+
self.ide_lockfile_dir = cfg.ide_lockfile_dir
|
|
57
|
+
self._defaults = cfg.defaults
|
|
58
|
+
self._supports = cfg.supports
|
|
59
|
+
self._login_argv = cfg.login_argv
|
|
60
|
+
self._auth_status_argv = cfg.auth_status_argv
|
|
61
|
+
|
|
62
|
+
# ---- spawn surface ---------------------------------------------------
|
|
63
|
+
|
|
64
|
+
def binary_path(self) -> Path:
|
|
65
|
+
"""Resolve the project-local `claude` binary.
|
|
66
|
+
|
|
67
|
+
Delegates to ``services.claude_oauth.claude_binary_path`` — same
|
|
68
|
+
path used by the credentials Login button. Lazy-installs into
|
|
69
|
+
``<repo>/data/claude-machina/npm/`` on first miss. Raises
|
|
70
|
+
``FileNotFoundError`` if ``npm`` isn't on PATH.
|
|
71
|
+
"""
|
|
72
|
+
return Path(claude_binary_path())
|
|
73
|
+
|
|
74
|
+
def headless_argv(
|
|
75
|
+
self,
|
|
76
|
+
task: Any, # ClaudeTaskSpec
|
|
77
|
+
*,
|
|
78
|
+
defaults: Dict[str, Any],
|
|
79
|
+
mcp_endpoint_url: Optional[str] = None,
|
|
80
|
+
mcp_bearer_token: Optional[str] = None,
|
|
81
|
+
connected_tool_names: Optional[List[str]] = None,
|
|
82
|
+
) -> List[str]:
|
|
83
|
+
"""Build the full argv (binary + flags) for one task.
|
|
84
|
+
|
|
85
|
+
``mcp_endpoint_url`` + ``mcp_bearer_token`` (if both set) are
|
|
86
|
+
emitted as a ``--mcp-config <json>`` block so the spawned
|
|
87
|
+
``claude -p`` registers MachinaOs's MCP server. The ``--ide`` /
|
|
88
|
+
lockfile path is for interactive IDE-host scenarios; in headless
|
|
89
|
+
mode the documented mechanism is ``--mcp-config`` (see
|
|
90
|
+
https://code.claude.com/docs/en/mcp)."""
|
|
91
|
+
if not isinstance(task, ClaudeTaskSpec):
|
|
92
|
+
raise TypeError(
|
|
93
|
+
"AnthropicClaudeProvider.headless_argv requires ClaudeTaskSpec, "
|
|
94
|
+
f"got {type(task).__name__}"
|
|
95
|
+
)
|
|
96
|
+
|
|
97
|
+
argv: List[str] = [str(self.binary_path())]
|
|
98
|
+
|
|
99
|
+
argv += [
|
|
100
|
+
"-p", task.prompt,
|
|
101
|
+
"--output-format", "stream-json",
|
|
102
|
+
"--verbose", # required by Claude CLI when using stream-json with --print
|
|
103
|
+
"--include-partial-messages",
|
|
104
|
+
"--include-hook-events", # surface SessionStart / hooks into stream-json
|
|
105
|
+
]
|
|
106
|
+
|
|
107
|
+
# MCP server registration — headless path. The shape mirrors
|
|
108
|
+
# the Claude Code MCP doc's ``mcp.json`` example and the
|
|
109
|
+
# ``claude mcp add --transport http <name> <url> --header
|
|
110
|
+
# "Authorization: Bearer ..."`` invocation.
|
|
111
|
+
if mcp_endpoint_url and mcp_bearer_token:
|
|
112
|
+
# `alwaysLoad: true` opts this server out of MCP tool-search
|
|
113
|
+
# deferral so all `mcp__machinaos__*` tools enter context at
|
|
114
|
+
# session start instead of waiting for a `ToolSearch` call
|
|
115
|
+
# that the agent often doesn't make
|
|
116
|
+
# (https://code.claude.com/docs/en/mcp#scale-with-mcp-tool-search).
|
|
117
|
+
# Startup blocks <=5s waiting for the FastMCP connection;
|
|
118
|
+
# acceptable since our server is already up before spawn.
|
|
119
|
+
mcp_payload = json.dumps({
|
|
120
|
+
"mcpServers": {
|
|
121
|
+
"machinaos": {
|
|
122
|
+
"type": "http",
|
|
123
|
+
"url": mcp_endpoint_url,
|
|
124
|
+
"headers": {
|
|
125
|
+
"Authorization": f"Bearer {mcp_bearer_token}",
|
|
126
|
+
},
|
|
127
|
+
"alwaysLoad": True,
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
})
|
|
131
|
+
argv += ["--mcp-config", mcp_payload, "--strict-mcp-config"]
|
|
132
|
+
|
|
133
|
+
# Model
|
|
134
|
+
model = (
|
|
135
|
+
task.model
|
|
136
|
+
or defaults.get("default_model")
|
|
137
|
+
or self._defaults.get("default_model", "claude-sonnet-4-6")
|
|
138
|
+
)
|
|
139
|
+
argv += ["--model", model]
|
|
140
|
+
|
|
141
|
+
# Session / resume — `resume_session_id` wins if both are set,
|
|
142
|
+
# since "resume" implies the user has a prior session.
|
|
143
|
+
if task.resume_session_id:
|
|
144
|
+
argv += ["--resume", task.resume_session_id]
|
|
145
|
+
elif task.session_id:
|
|
146
|
+
argv += ["--session-id", task.session_id]
|
|
147
|
+
|
|
148
|
+
# Max turns
|
|
149
|
+
max_turns = (
|
|
150
|
+
task.max_turns
|
|
151
|
+
if task.max_turns is not None
|
|
152
|
+
else int(defaults.get(
|
|
153
|
+
"default_max_turns",
|
|
154
|
+
self._defaults.get("default_max_turns", 10),
|
|
155
|
+
))
|
|
156
|
+
)
|
|
157
|
+
argv += ["--max-turns", str(max_turns)]
|
|
158
|
+
|
|
159
|
+
# Budget (USD)
|
|
160
|
+
max_budget = (
|
|
161
|
+
task.max_budget_usd
|
|
162
|
+
if task.max_budget_usd is not None
|
|
163
|
+
else float(defaults.get(
|
|
164
|
+
"default_max_budget_usd",
|
|
165
|
+
self._defaults.get("default_max_budget_usd", 5.0),
|
|
166
|
+
))
|
|
167
|
+
)
|
|
168
|
+
if max_budget > 0:
|
|
169
|
+
argv += ["--max-budget-usd", str(max_budget)]
|
|
170
|
+
|
|
171
|
+
# Allowed tools — built-in defaults plus every workflow tool we
|
|
172
|
+
# exposed via the per-batch FastMCP bridge. Without the
|
|
173
|
+
# `mcp__machinaos__<name>` entries here, claude sees the tools
|
|
174
|
+
# in `tools/list` but is denied permission when it tries to
|
|
175
|
+
# invoke them ("I don't have permission to run a web search…")
|
|
176
|
+
# because `--permission-mode acceptEdits` only auto-permits
|
|
177
|
+
# Edit; everything else falls through to the allowlist.
|
|
178
|
+
# Default fallback includes:
|
|
179
|
+
# - Read,Edit,Bash,Glob,Grep,Write — filesystem + shell escape hatches
|
|
180
|
+
# - Skill — invoke materialised `.claude/skills/<name>/SKILL.md`
|
|
181
|
+
# - WebSearch,WebFetch — escape hatches when no MCP tool matches
|
|
182
|
+
# `ToolSearch` is intentionally NOT here: it's permission-free per
|
|
183
|
+
# https://code.claude.com/docs/en/tools-reference#built-in-tools.
|
|
184
|
+
allowed = task.allowed_tools or defaults.get(
|
|
185
|
+
"default_allowed_tools",
|
|
186
|
+
self._defaults.get(
|
|
187
|
+
"default_allowed_tools",
|
|
188
|
+
"Read,Edit,Bash,Glob,Grep,Write,Skill,WebSearch,WebFetch",
|
|
189
|
+
),
|
|
190
|
+
)
|
|
191
|
+
allowed_list: List[str] = (
|
|
192
|
+
[t.strip() for t in allowed.split(",") if t.strip()]
|
|
193
|
+
if allowed else []
|
|
194
|
+
)
|
|
195
|
+
if connected_tool_names:
|
|
196
|
+
allowed_list += [
|
|
197
|
+
f"mcp__machinaos__{name}" for name in connected_tool_names
|
|
198
|
+
]
|
|
199
|
+
# Always permit MachinaOs's built-in MCP tools so the agent can
|
|
200
|
+
# discover skills + read workspace files without prompting.
|
|
201
|
+
allowed_list += [
|
|
202
|
+
"mcp__machinaos__getWorkspaceFiles",
|
|
203
|
+
"mcp__machinaos__listSkills",
|
|
204
|
+
"mcp__machinaos__getSkill",
|
|
205
|
+
"mcp__machinaos__getCredential",
|
|
206
|
+
"mcp__machinaos__broadcastLog",
|
|
207
|
+
]
|
|
208
|
+
if allowed_list:
|
|
209
|
+
argv += ["--allowedTools", ",".join(allowed_list)]
|
|
210
|
+
|
|
211
|
+
# Permission mode (default acceptEdits)
|
|
212
|
+
perm = task.permission_mode or defaults.get(
|
|
213
|
+
"default_permission_mode",
|
|
214
|
+
self._defaults.get("default_permission_mode", "acceptEdits"),
|
|
215
|
+
)
|
|
216
|
+
if perm:
|
|
217
|
+
argv += ["--permission-mode", perm]
|
|
218
|
+
|
|
219
|
+
# System prompt — appended to Claude Code's built-in system prompt
|
|
220
|
+
if task.system_prompt:
|
|
221
|
+
argv += ["--append-system-prompt", task.system_prompt]
|
|
222
|
+
logger.info(
|
|
223
|
+
"[CC-Agent argv] --append-system-prompt (task) "
|
|
224
|
+
"length=%d preview=%r",
|
|
225
|
+
len(task.system_prompt), task.system_prompt[:200],
|
|
226
|
+
)
|
|
227
|
+
|
|
228
|
+
# Connected-tools steering directive — ensures the agent prefers
|
|
229
|
+
# wired MCP tools over built-in escape hatches (WebSearch,
|
|
230
|
+
# WebFetch, Bash) when their purpose matches the user's request.
|
|
231
|
+
# Mirrors Cursor's per-request rule prepend pattern
|
|
232
|
+
# (https://cursor.com/docs/rules). Multiple `--append-system-prompt`
|
|
233
|
+
# flags concatenate per the CLI reference.
|
|
234
|
+
if connected_tool_names:
|
|
235
|
+
tool_list = ", ".join(
|
|
236
|
+
f"mcp__machinaos__{name}" for name in connected_tool_names
|
|
237
|
+
)
|
|
238
|
+
directive = (
|
|
239
|
+
f"Workflow tools wired to this agent: {tool_list}. "
|
|
240
|
+
"Prefer them over built-in equivalents (WebSearch, "
|
|
241
|
+
"WebFetch, Bash) when the user's request matches their "
|
|
242
|
+
"purpose."
|
|
243
|
+
)
|
|
244
|
+
argv += ["--append-system-prompt", directive]
|
|
245
|
+
logger.info(
|
|
246
|
+
"[CC-Agent argv] --append-system-prompt (tools-directive) "
|
|
247
|
+
"length=%d",
|
|
248
|
+
len(directive),
|
|
249
|
+
)
|
|
250
|
+
|
|
251
|
+
# Optional per-task overrides (documented at code.claude.com/docs/en/cli-reference)
|
|
252
|
+
if task.effort:
|
|
253
|
+
argv += ["--effort", task.effort]
|
|
254
|
+
if task.fallback_model:
|
|
255
|
+
argv += ["--fallback-model", task.fallback_model]
|
|
256
|
+
for path in task.add_dir:
|
|
257
|
+
argv += ["--add-dir", path]
|
|
258
|
+
if task.disallowed_tools:
|
|
259
|
+
argv += ["--disallowedTools", task.disallowed_tools]
|
|
260
|
+
if task.agent:
|
|
261
|
+
argv += ["--agent", task.agent]
|
|
262
|
+
|
|
263
|
+
return argv
|
|
264
|
+
|
|
265
|
+
# ---- native auth -----------------------------------------------------
|
|
266
|
+
|
|
267
|
+
def login_argv(self) -> List[str]:
|
|
268
|
+
return list(self._login_argv) or ["claude", "login"]
|
|
269
|
+
|
|
270
|
+
def auth_status_argv(self) -> Optional[List[str]]:
|
|
271
|
+
return list(self._auth_status_argv) if self._auth_status_argv else None
|
|
272
|
+
|
|
273
|
+
def detect_auth_error(self, stderr: str, exit_code: int) -> bool:
|
|
274
|
+
"""True if stderr/exit_code indicate the user isn't logged in."""
|
|
275
|
+
if not stderr and exit_code == 0:
|
|
276
|
+
return False
|
|
277
|
+
markers = (
|
|
278
|
+
"Please run 'claude login'",
|
|
279
|
+
"Please run `claude login`",
|
|
280
|
+
"Not authenticated",
|
|
281
|
+
"Authentication required",
|
|
282
|
+
"401 Unauthorized",
|
|
283
|
+
"Invalid API key",
|
|
284
|
+
)
|
|
285
|
+
return any(m in stderr for m in markers)
|
|
286
|
+
|
|
287
|
+
# ---- streaming output parsing ---------------------------------------
|
|
288
|
+
|
|
289
|
+
def parse_event(self, line: str) -> Optional[Dict[str, Any]]:
|
|
290
|
+
line = line.strip()
|
|
291
|
+
if not line:
|
|
292
|
+
return None
|
|
293
|
+
try:
|
|
294
|
+
return json.loads(line)
|
|
295
|
+
except json.JSONDecodeError:
|
|
296
|
+
return None
|
|
297
|
+
|
|
298
|
+
def is_final_event(self, event: Dict[str, Any]) -> bool:
|
|
299
|
+
return event.get("type") == "result"
|
|
300
|
+
|
|
301
|
+
def event_to_session_result(
|
|
302
|
+
self,
|
|
303
|
+
events: List[Dict[str, Any]],
|
|
304
|
+
stderr: str,
|
|
305
|
+
exit_code: int,
|
|
306
|
+
) -> Dict[str, Any]:
|
|
307
|
+
"""Reconstruct shared result fields from the event stream."""
|
|
308
|
+
final = next(
|
|
309
|
+
(e for e in reversed(events) if e.get("type") == "result"),
|
|
310
|
+
None,
|
|
311
|
+
)
|
|
312
|
+
|
|
313
|
+
# Session ID can come from `system.init` or `result`
|
|
314
|
+
session_id: Optional[str] = None
|
|
315
|
+
for evt in events:
|
|
316
|
+
sid = evt.get("session_id")
|
|
317
|
+
if sid:
|
|
318
|
+
session_id = sid
|
|
319
|
+
break
|
|
320
|
+
|
|
321
|
+
tool_calls = sum(
|
|
322
|
+
1 for evt in events
|
|
323
|
+
if evt.get("type") == "tool_use"
|
|
324
|
+
or (evt.get("type") == "assistant" and self._has_tool_use(evt))
|
|
325
|
+
)
|
|
326
|
+
|
|
327
|
+
provider_data: Dict[str, Any] = {}
|
|
328
|
+
for evt in events:
|
|
329
|
+
if evt.get("type") == "assistant":
|
|
330
|
+
msg = evt.get("message") or {}
|
|
331
|
+
rd = msg.get("reasoning_details") or msg.get("thinking")
|
|
332
|
+
if rd is not None:
|
|
333
|
+
provider_data.setdefault("reasoning_details", rd)
|
|
334
|
+
break
|
|
335
|
+
|
|
336
|
+
success = exit_code == 0 and final is not None
|
|
337
|
+
error: Optional[str] = None
|
|
338
|
+
if exit_code != 0:
|
|
339
|
+
error = stderr.strip()[-2000:] or f"claude exited with code {exit_code}"
|
|
340
|
+
elif final is None:
|
|
341
|
+
error = "no result event received"
|
|
342
|
+
|
|
343
|
+
response = ""
|
|
344
|
+
cost: Optional[float] = None
|
|
345
|
+
duration_ms: Optional[int] = None
|
|
346
|
+
num_turns: Optional[int] = None
|
|
347
|
+
if final:
|
|
348
|
+
response = str(final.get("result") or "")
|
|
349
|
+
cost = final.get("total_cost_usd")
|
|
350
|
+
duration_ms = final.get("duration_ms")
|
|
351
|
+
num_turns = final.get("num_turns")
|
|
352
|
+
if final.get("subtype") == "error":
|
|
353
|
+
success = False
|
|
354
|
+
error = error or response or "result event reports error"
|
|
355
|
+
|
|
356
|
+
cu = self.canonical_usage(events)
|
|
357
|
+
|
|
358
|
+
return {
|
|
359
|
+
"session_id": session_id,
|
|
360
|
+
"response": response,
|
|
361
|
+
"cost_usd": cost,
|
|
362
|
+
"duration_ms": duration_ms,
|
|
363
|
+
"num_turns": num_turns,
|
|
364
|
+
"tool_calls": tool_calls,
|
|
365
|
+
"canonical_usage": cu,
|
|
366
|
+
"provider_data": provider_data,
|
|
367
|
+
"success": success,
|
|
368
|
+
"error": error,
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
def canonical_usage(self, events: List[Dict[str, Any]]) -> CanonicalUsage:
|
|
372
|
+
"""Pull token counts from the `result` event's `usage` block.
|
|
373
|
+
|
|
374
|
+
Anthropic shape:
|
|
375
|
+
{
|
|
376
|
+
"input_tokens": int,
|
|
377
|
+
"output_tokens": int,
|
|
378
|
+
"cache_creation_input_tokens": int,
|
|
379
|
+
"cache_read_input_tokens": int,
|
|
380
|
+
}
|
|
381
|
+
"""
|
|
382
|
+
final = next(
|
|
383
|
+
(e for e in reversed(events) if e.get("type") == "result"),
|
|
384
|
+
None,
|
|
385
|
+
)
|
|
386
|
+
if not final:
|
|
387
|
+
return CanonicalUsage()
|
|
388
|
+
|
|
389
|
+
usage = final.get("usage") or {}
|
|
390
|
+
request_count = (
|
|
391
|
+
int(final.get("num_turns") or 0)
|
|
392
|
+
or sum(1 for e in events if e.get("type") == "assistant")
|
|
393
|
+
)
|
|
394
|
+
return CanonicalUsage(
|
|
395
|
+
input_tokens=int(usage.get("input_tokens", 0)),
|
|
396
|
+
output_tokens=int(usage.get("output_tokens", 0)),
|
|
397
|
+
cache_read=int(usage.get("cache_read_input_tokens", 0)),
|
|
398
|
+
cache_write=int(usage.get("cache_creation_input_tokens", 0)),
|
|
399
|
+
reasoning_tokens=0, # Claude doesn't expose this separately
|
|
400
|
+
request_count=request_count,
|
|
401
|
+
)
|
|
402
|
+
|
|
403
|
+
# ---- feature gating --------------------------------------------------
|
|
404
|
+
|
|
405
|
+
def supports(self, feature: str) -> bool:
|
|
406
|
+
return feature in self._supports
|
|
407
|
+
|
|
408
|
+
# ---- internals -------------------------------------------------------
|
|
409
|
+
|
|
410
|
+
@staticmethod
|
|
411
|
+
def _has_tool_use(event: Dict[str, Any]) -> bool:
|
|
412
|
+
msg = event.get("message") or {}
|
|
413
|
+
content = msg.get("content")
|
|
414
|
+
if isinstance(content, list):
|
|
415
|
+
return any(
|
|
416
|
+
isinstance(blk, dict) and blk.get("type") == "tool_use"
|
|
417
|
+
for blk in content
|
|
418
|
+
)
|
|
419
|
+
return False
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
"""Google Gemini CLI provider — v2 stub.
|
|
2
|
+
|
|
3
|
+
The factory raises ``NotImplementedError`` for ``"gemini"`` in v1, but
|
|
4
|
+
the type+config layer is wired so v2 activation is mechanical (replace
|
|
5
|
+
this stub, drop the factory branch). See `cli_agent_framework.md` →
|
|
6
|
+
"v2 follow-up — Gemini activation" for the full v2 PR shape.
|
|
7
|
+
|
|
8
|
+
Reference for v2 implementation:
|
|
9
|
+
- argv: ``gemini --prompt <p> --output-format stream-json --model ...
|
|
10
|
+
[--session-id <UUID>] [--resume latest|<idx>|<UUID>] [--yolo] [--sandbox]``
|
|
11
|
+
- Final event: ``type == "result"`` (from
|
|
12
|
+
``packages/cli/src/nonInteractiveCli.ts:JsonStreamEventType.RESULT``)
|
|
13
|
+
- ``result`` event carries ``session_id`` + ``stats.duration_ms``
|
|
14
|
+
- Exit code 1 = ``FatalAuthenticationError``
|
|
15
|
+
- Auth: ``gemini auth login`` writes ``~/.gemini/``
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
from __future__ import annotations
|
|
19
|
+
|
|
20
|
+
from pathlib import Path
|
|
21
|
+
from typing import Any, Dict, List, Optional
|
|
22
|
+
|
|
23
|
+
from services.cli_agent.protocol import CanonicalUsage
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class GoogleGeminiProvider:
|
|
27
|
+
"""v2 stub. Constructed only via direct instantiation in tests; the
|
|
28
|
+
factory bypasses this class entirely and raises NotImplementedError."""
|
|
29
|
+
|
|
30
|
+
name = "gemini"
|
|
31
|
+
|
|
32
|
+
def __init__(self) -> None:
|
|
33
|
+
raise NotImplementedError(
|
|
34
|
+
"GoogleGeminiProvider is a v2 stub. The factory raises "
|
|
35
|
+
"NotImplementedError for 'gemini' in v1."
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
# The Protocol surface below is declared so static type-checkers see
|
|
39
|
+
# the class as a complete `AICliProvider`. None of these are reachable
|
|
40
|
+
# because `__init__` raises.
|
|
41
|
+
|
|
42
|
+
package_name: str = "@google/gemini-cli"
|
|
43
|
+
binary_name: str = "gemini"
|
|
44
|
+
ide_lock_env_var: Optional[str] = "GEMINI_IDE_LOCK"
|
|
45
|
+
ide_lockfile_dir: Optional[Path] = None # config.py expands to <tmpdir>/gemini/ide
|
|
46
|
+
|
|
47
|
+
def binary_path(self) -> Path: # pragma: no cover
|
|
48
|
+
raise NotImplementedError
|
|
49
|
+
|
|
50
|
+
def headless_argv(self, task: Any, *, defaults: Dict[str, Any]) -> List[str]: # pragma: no cover
|
|
51
|
+
raise NotImplementedError
|
|
52
|
+
|
|
53
|
+
def login_argv(self) -> List[str]: # pragma: no cover
|
|
54
|
+
return ["gemini", "auth", "login"]
|
|
55
|
+
|
|
56
|
+
def auth_status_argv(self) -> Optional[List[str]]: # pragma: no cover
|
|
57
|
+
return ["gemini", "--version"]
|
|
58
|
+
|
|
59
|
+
def detect_auth_error(self, stderr: str, exit_code: int) -> bool: # pragma: no cover
|
|
60
|
+
return exit_code == 1 and "FatalAuthenticationError" in stderr
|
|
61
|
+
|
|
62
|
+
def parse_event(self, line: str) -> Optional[Dict[str, Any]]: # pragma: no cover
|
|
63
|
+
raise NotImplementedError
|
|
64
|
+
|
|
65
|
+
def is_final_event(self, event: Dict[str, Any]) -> bool: # pragma: no cover
|
|
66
|
+
return event.get("type") == "result"
|
|
67
|
+
|
|
68
|
+
def event_to_session_result(
|
|
69
|
+
self,
|
|
70
|
+
events: List[Dict[str, Any]],
|
|
71
|
+
stderr: str,
|
|
72
|
+
exit_code: int,
|
|
73
|
+
) -> Dict[str, Any]: # pragma: no cover
|
|
74
|
+
raise NotImplementedError
|
|
75
|
+
|
|
76
|
+
def canonical_usage(self, events: List[Dict[str, Any]]) -> CanonicalUsage: # pragma: no cover
|
|
77
|
+
return CanonicalUsage()
|
|
78
|
+
|
|
79
|
+
def supports(self, feature: str) -> bool: # pragma: no cover
|
|
80
|
+
return feature in {"session_id", "resume", "sandbox", "mcp_runtime", "ide_lockfile"}
|