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,112 @@
|
|
|
1
|
+
"""Provider configuration loader for the AI CLI framework.
|
|
2
|
+
|
|
3
|
+
Loads `server/config/ai_cli_providers.json` and exposes
|
|
4
|
+
`get_provider_config(name)` returning a typed `CliProviderConfig`. The
|
|
5
|
+
JSON file is the single source of truth for binary names, npm packages,
|
|
6
|
+
default flags, IDE lockfile paths, and per-provider feature flags.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from __future__ import annotations
|
|
10
|
+
|
|
11
|
+
import json
|
|
12
|
+
import os
|
|
13
|
+
import tempfile
|
|
14
|
+
from dataclasses import dataclass, field
|
|
15
|
+
from pathlib import Path
|
|
16
|
+
from typing import Any, Dict, List, Optional, Tuple
|
|
17
|
+
|
|
18
|
+
from core.logging import get_logger
|
|
19
|
+
|
|
20
|
+
logger = get_logger(__name__)
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
# ---------------------------------------------------------------------------
|
|
24
|
+
# Provider config dataclass
|
|
25
|
+
# ---------------------------------------------------------------------------
|
|
26
|
+
|
|
27
|
+
@dataclass
|
|
28
|
+
class CliProviderConfig:
|
|
29
|
+
"""Per-provider config from `ai_cli_providers.json`."""
|
|
30
|
+
name: str
|
|
31
|
+
package_name: str
|
|
32
|
+
binary_name: str
|
|
33
|
+
login_argv: Tuple[str, ...]
|
|
34
|
+
auth_status_argv: Optional[Tuple[str, ...]]
|
|
35
|
+
ide_lock_env_var: Optional[str]
|
|
36
|
+
ide_lockfile_dir: Optional[Path]
|
|
37
|
+
defaults: Dict[str, Any] = field(default_factory=dict)
|
|
38
|
+
supports: frozenset = field(default_factory=frozenset)
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
# ---------------------------------------------------------------------------
|
|
42
|
+
# Load config once at import time
|
|
43
|
+
# ---------------------------------------------------------------------------
|
|
44
|
+
|
|
45
|
+
def _config_path() -> Path:
|
|
46
|
+
return Path(__file__).parent.parent.parent / "config" / "ai_cli_providers.json"
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def _load_raw() -> Dict[str, Any]:
|
|
50
|
+
path = _config_path()
|
|
51
|
+
try:
|
|
52
|
+
with open(path, encoding="utf-8") as f:
|
|
53
|
+
return json.load(f)
|
|
54
|
+
except FileNotFoundError:
|
|
55
|
+
logger.warning(f"ai_cli_providers.json not found at {path}")
|
|
56
|
+
return {}
|
|
57
|
+
except Exception as e:
|
|
58
|
+
logger.warning(f"Failed to load ai_cli_providers.json: {e}")
|
|
59
|
+
return {}
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def _expand_lockfile_dir(raw: Optional[str]) -> Optional[Path]:
|
|
63
|
+
"""Expand ~ and the literal `<tmpdir>` placeholder in lockfile paths."""
|
|
64
|
+
if not raw:
|
|
65
|
+
return None
|
|
66
|
+
if raw.startswith("<tmpdir>"):
|
|
67
|
+
return Path(tempfile.gettempdir()) / raw[len("<tmpdir>") :].lstrip("/").lstrip("\\")
|
|
68
|
+
return Path(os.path.expanduser(raw))
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
def _build_configs() -> Dict[str, CliProviderConfig]:
|
|
72
|
+
raw = _load_raw()
|
|
73
|
+
configs: Dict[str, CliProviderConfig] = {}
|
|
74
|
+
for name, prov in raw.items():
|
|
75
|
+
# Pull known fields out; everything else lives in `defaults` so
|
|
76
|
+
# provider impls can read e.g. `default_model`, `default_max_turns`.
|
|
77
|
+
defaults = {
|
|
78
|
+
k: v for k, v in prov.items()
|
|
79
|
+
if k.startswith("default_")
|
|
80
|
+
}
|
|
81
|
+
configs[name] = CliProviderConfig(
|
|
82
|
+
name=name,
|
|
83
|
+
package_name=prov.get("package_name", ""),
|
|
84
|
+
binary_name=prov.get("binary_name", name),
|
|
85
|
+
login_argv=tuple(prov.get("login_argv", [])),
|
|
86
|
+
auth_status_argv=(
|
|
87
|
+
tuple(prov["auth_status_argv"])
|
|
88
|
+
if prov.get("auth_status_argv") else None
|
|
89
|
+
),
|
|
90
|
+
ide_lock_env_var=prov.get("ide_lock_env_var"),
|
|
91
|
+
ide_lockfile_dir=_expand_lockfile_dir(prov.get("ide_lockfile_dir")),
|
|
92
|
+
defaults=defaults,
|
|
93
|
+
supports=frozenset(prov.get("supports", [])),
|
|
94
|
+
)
|
|
95
|
+
return configs
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
PROVIDER_CONFIGS: Dict[str, CliProviderConfig] = _build_configs()
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
def get_provider_config(name: str) -> Optional[CliProviderConfig]:
|
|
102
|
+
return PROVIDER_CONFIGS.get(name)
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
def list_provider_names() -> List[str]:
|
|
106
|
+
return list(PROVIDER_CONFIGS.keys())
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
def reload_configs() -> None:
|
|
110
|
+
"""Reload `ai_cli_providers.json` (useful for tests)."""
|
|
111
|
+
global PROVIDER_CONFIGS
|
|
112
|
+
PROVIDER_CONFIGS = _build_configs()
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
"""Factory for `AICliProvider` instances.
|
|
2
|
+
|
|
3
|
+
Mirrors `services/llm/factory.py`: lazy imports keep optional providers
|
|
4
|
+
from being loaded until first use. v1 ships Claude + Codex; calling
|
|
5
|
+
`create_cli_provider("gemini")` raises `NotImplementedError` (v2).
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
from core.logging import get_logger
|
|
11
|
+
|
|
12
|
+
from services.cli_agent.protocol import AICliProvider
|
|
13
|
+
|
|
14
|
+
logger = get_logger(__name__)
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
SUPPORTED_PROVIDERS = frozenset({"claude", "codex"}) # v2 adds "gemini"
|
|
18
|
+
ALL_PROVIDERS = frozenset({"claude", "codex", "gemini"}) # union of v1+v2 names
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def create_cli_provider(name: str) -> AICliProvider:
|
|
22
|
+
"""Build a CLI provider by name.
|
|
23
|
+
|
|
24
|
+
Raises:
|
|
25
|
+
NotImplementedError: for `gemini` until the v2 implementation lands.
|
|
26
|
+
ValueError: for unknown names.
|
|
27
|
+
"""
|
|
28
|
+
if name == "claude":
|
|
29
|
+
from services.cli_agent.providers.anthropic_claude import AnthropicClaudeProvider
|
|
30
|
+
return AnthropicClaudeProvider()
|
|
31
|
+
|
|
32
|
+
if name == "codex":
|
|
33
|
+
from services.cli_agent.providers.openai_codex import OpenAICodexProvider
|
|
34
|
+
return OpenAICodexProvider()
|
|
35
|
+
|
|
36
|
+
if name == "gemini":
|
|
37
|
+
# v2 stub — surface a clean error so factory consumers can detect
|
|
38
|
+
# the deferred state and offer the user an actionable hint.
|
|
39
|
+
raise NotImplementedError(
|
|
40
|
+
"gemini provider deferred to v2. Use 'claude' or 'codex' in v1."
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
raise ValueError(f"Unknown CLI provider: {name!r}. Known: {sorted(ALL_PROVIDERS)}")
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def is_supported(name: str) -> bool:
|
|
47
|
+
"""True if the provider is fully implemented in this version."""
|
|
48
|
+
return name in SUPPORTED_PROVIDERS
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
"""VSCode-style IDE lockfile writer + remover + stale-PID sweep.
|
|
2
|
+
|
|
3
|
+
Each spawned CLI session writes a discovery lockfile that the CLI reads
|
|
4
|
+
to auto-connect to MachinaOs's MCP server. We mirror the format the
|
|
5
|
+
official VSCode Claude Code extension writes
|
|
6
|
+
(``~/.claude/ide/<pid>.lock`` containing
|
|
7
|
+
``{port, url, authToken, workspaceFolders, ideName, transport, pid}``)
|
|
8
|
+
and the path convention Gemini's VSCode companion uses
|
|
9
|
+
(``<tmpdir>/gemini/ide/gemini-ide-server-<pid>-<port>.json``).
|
|
10
|
+
|
|
11
|
+
The startup sweep (mirroring VSCode's own behaviour) removes lockfiles
|
|
12
|
+
whose PIDs are no longer alive — see ``main.py`` lifespan.
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
from __future__ import annotations
|
|
16
|
+
|
|
17
|
+
import json
|
|
18
|
+
import logging
|
|
19
|
+
import os
|
|
20
|
+
import sys
|
|
21
|
+
from pathlib import Path
|
|
22
|
+
from typing import List, Optional
|
|
23
|
+
|
|
24
|
+
logger = logging.getLogger(__name__)
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def _lockfile_path(
|
|
28
|
+
*, ide_lockfile_dir: Path, pid: int, port: int, ide_name: str,
|
|
29
|
+
) -> Path:
|
|
30
|
+
if ide_name == "gemini":
|
|
31
|
+
return ide_lockfile_dir / f"gemini-ide-server-{pid}-{port}.json"
|
|
32
|
+
return ide_lockfile_dir / f"{pid}.lock"
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
# Keep `lockfile_path` as a public alias for tests that exercise the
|
|
36
|
+
# path-shape contract.
|
|
37
|
+
lockfile_path = _lockfile_path
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def write_ide_lockfile(
|
|
41
|
+
*,
|
|
42
|
+
ide_lockfile_dir: Path,
|
|
43
|
+
pid: int,
|
|
44
|
+
port: int,
|
|
45
|
+
token: str,
|
|
46
|
+
workspace_dir: Path,
|
|
47
|
+
ide_name: str,
|
|
48
|
+
url: Optional[str] = None,
|
|
49
|
+
transport: str = "http",
|
|
50
|
+
) -> Path:
|
|
51
|
+
"""Write a VSCode-style IDE lockfile (mode 0600 on POSIX)."""
|
|
52
|
+
ide_lockfile_dir.mkdir(parents=True, exist_ok=True)
|
|
53
|
+
path = _lockfile_path(
|
|
54
|
+
ide_lockfile_dir=ide_lockfile_dir, pid=pid, port=port, ide_name=ide_name,
|
|
55
|
+
)
|
|
56
|
+
|
|
57
|
+
# FastMCP's ``streamable_http_app()`` registers the JSON-RPC route at
|
|
58
|
+
# ``/mcp`` of the sub-app; ``main.py`` mounts the sub-app at
|
|
59
|
+
# ``/mcp/ide``. The absolute endpoint the claude CLI POSTs to via
|
|
60
|
+
# ``--ide`` lockfile discovery is therefore ``/mcp/ide/mcp``. Older
|
|
61
|
+
# builds advertised ``/mcp/ide`` here and the CLI silently 404'd
|
|
62
|
+
# before reaching the bearer-token middleware — see ``[CC-Agent MCP
|
|
63
|
+
# auth]`` log lines.
|
|
64
|
+
payload = {
|
|
65
|
+
"port": port,
|
|
66
|
+
"url": url or f"http://127.0.0.1:{port}/mcp/ide/mcp",
|
|
67
|
+
"authToken": token,
|
|
68
|
+
"workspaceFolders": [str(workspace_dir)],
|
|
69
|
+
"ideName": ide_name,
|
|
70
|
+
"transport": transport,
|
|
71
|
+
"pid": pid,
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
tmp = path.with_suffix(path.suffix + ".tmp")
|
|
75
|
+
tmp.write_text(json.dumps(payload), encoding="utf-8")
|
|
76
|
+
if sys.platform != "win32":
|
|
77
|
+
try:
|
|
78
|
+
os.chmod(tmp, 0o600)
|
|
79
|
+
except OSError:
|
|
80
|
+
pass
|
|
81
|
+
tmp.replace(path)
|
|
82
|
+
return path
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
def remove_ide_lockfile(path: Optional[Path]) -> None:
|
|
86
|
+
"""Best-effort lockfile removal. Never raises."""
|
|
87
|
+
if not path:
|
|
88
|
+
return
|
|
89
|
+
try:
|
|
90
|
+
if path.exists():
|
|
91
|
+
path.unlink()
|
|
92
|
+
except OSError as exc:
|
|
93
|
+
logger.debug("Failed to remove lockfile %s: %s", path, exc)
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
def sweep_stale_lockfiles(ide_lockfile_dir: Optional[Path]) -> int:
|
|
97
|
+
"""Remove lockfiles whose PIDs are no longer alive.
|
|
98
|
+
|
|
99
|
+
Mirrors VSCode's startup behaviour. Safe on missing dirs. Never raises.
|
|
100
|
+
"""
|
|
101
|
+
if not ide_lockfile_dir or not ide_lockfile_dir.exists():
|
|
102
|
+
return 0
|
|
103
|
+
|
|
104
|
+
try:
|
|
105
|
+
import psutil
|
|
106
|
+
except ImportError:
|
|
107
|
+
return 0
|
|
108
|
+
|
|
109
|
+
removed = 0
|
|
110
|
+
for entry in ide_lockfile_dir.iterdir():
|
|
111
|
+
if not entry.is_file():
|
|
112
|
+
continue
|
|
113
|
+
try:
|
|
114
|
+
data = json.loads(entry.read_text(encoding="utf-8"))
|
|
115
|
+
except (OSError, json.JSONDecodeError):
|
|
116
|
+
continue
|
|
117
|
+
pid = data.get("pid")
|
|
118
|
+
if not isinstance(pid, int):
|
|
119
|
+
continue
|
|
120
|
+
try:
|
|
121
|
+
alive = psutil.pid_exists(pid)
|
|
122
|
+
except Exception:
|
|
123
|
+
alive = True
|
|
124
|
+
if alive:
|
|
125
|
+
continue
|
|
126
|
+
try:
|
|
127
|
+
entry.unlink()
|
|
128
|
+
removed += 1
|
|
129
|
+
except OSError as exc:
|
|
130
|
+
logger.debug("Failed to remove stale lockfile %s: %s", entry, exc)
|
|
131
|
+
|
|
132
|
+
if removed:
|
|
133
|
+
logger.info("Swept %d stale CLI lockfile(s) from %s", removed, ide_lockfile_dir)
|
|
134
|
+
return removed
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
def list_active_lockfiles(ide_lockfile_dir: Optional[Path]) -> List[Path]:
|
|
138
|
+
"""Diagnostic helper: list lockfiles in the discovery dir."""
|
|
139
|
+
if not ide_lockfile_dir or not ide_lockfile_dir.exists():
|
|
140
|
+
return []
|
|
141
|
+
return [p for p in ide_lockfile_dir.iterdir() if p.is_file()]
|