@stage-labs/metro 0.1.0-beta.21 → 0.1.0-beta.210
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/dist/api.js +9 -101
- package/dist/claude-settings.js +40 -0
- package/dist/claude.js +152 -0
- package/dist/cli.js +52 -109
- package/dist/control.js +47 -75
- package/dist/hold.js +210 -0
- package/dist/local.js +43 -0
- package/dist/mcp-config.js +25 -0
- package/dist/node-name.js +71 -0
- package/dist/onboarding.js +44 -0
- package/dist/provider-flags.js +9 -0
- package/dist/route.js +58 -0
- package/dist/runtime-install.js +96 -0
- package/dist/runtime.js +29 -52
- package/dist/serve.js +157 -0
- package/dist/service.js +304 -0
- package/dist/tail.js +119 -0
- package/dist/update.js +13 -1
- package/package.json +2 -23
- package/runtime/marketplace/.claude-plugin/marketplace.json +12 -0
- package/runtime/marketplace/plugin/.claude-plugin/plugin.json +17 -0
- package/runtime/marketplace/plugin/.mcp.json +1 -0
- package/runtime/marketplace/plugin/bin/guard.mjs +71 -0
- package/runtime/marketplace/plugin/bin/metro-plugin.mjs +30 -0
- package/runtime/marketplace/plugin/bin/policy.mjs +97 -0
- package/runtime/marketplace/plugin/bin/session-start.mjs +31 -0
- package/runtime/marketplace/plugin/hooks/hooks.json +25 -0
- package/runtime/marketplace/plugin/orchestrator.md +26 -0
- package/runtime/node_modules/@metro-labs/core/package.json +47 -0
- package/runtime/node_modules/@metro-labs/core/src/endpoints.ts +7 -0
- package/runtime/node_modules/@metro-labs/{mcp/src/daemon → core/src}/events.ts +11 -7
- package/runtime/node_modules/@metro-labs/{mcp/src/db → core/src}/ids.ts +5 -0
- package/runtime/node_modules/@metro-labs/core/src/is-record.ts +2 -0
- package/runtime/node_modules/@metro-labs/{mcp/src/stations → core/src}/lines.ts +24 -28
- package/runtime/node_modules/@metro-labs/{mcp/src/daemon → core/src}/protocol.ts +7 -4
- package/runtime/node_modules/@metro-labs/{mcp/src/daemon → core/src}/secure-fs.ts +17 -0
- package/runtime/node_modules/@metro-labs/core/src/station-names.ts +13 -0
- package/runtime/node_modules/@metro-labs/core/src/stations/account-files.ts +41 -0
- package/runtime/node_modules/@metro-labs/core/src/stations/account-store.ts +83 -0
- package/runtime/node_modules/@metro-labs/{mcp → core}/src/stations/attachments.ts +0 -1
- package/runtime/node_modules/@metro-labs/core/src/stations/messaging-normalize.ts +16 -0
- package/runtime/node_modules/@metro-labs/core/src/stations/profile.ts +58 -0
- package/runtime/node_modules/@metro-labs/core/src/stations/sender-profile.ts +64 -0
- package/runtime/node_modules/@metro-labs/{mcp → core}/src/stations/station-runtime.ts +6 -11
- package/runtime/node_modules/@metro-labs/core/src/stations/train-boot.ts +42 -0
- package/runtime/node_modules/@metro-labs/core/src/stations/train-events.ts +84 -0
- package/runtime/node_modules/@metro-labs/{mcp → core}/src/stations/types.ts +18 -1
- package/runtime/node_modules/@metro-labs/core/src/tickets.ts +37 -0
- package/runtime/node_modules/@metro-labs/core/src/version.ts +2 -0
- package/runtime/node_modules/@metro-labs/daemon/package.json +48 -0
- package/runtime/node_modules/@metro-labs/daemon/src/agent-user/agent-fs.ts +134 -0
- package/runtime/node_modules/@metro-labs/daemon/src/agent-user/files-api.ts +12 -0
- package/runtime/node_modules/@metro-labs/daemon/src/agent-user/files.ts +89 -0
- package/runtime/node_modules/@metro-labs/daemon/src/agent-user/home-fs.ts +113 -0
- package/runtime/node_modules/@metro-labs/daemon/src/agent-user/job-env.ts +51 -0
- package/runtime/node_modules/@metro-labs/daemon/src/agent-user/provision.ts +80 -0
- package/runtime/node_modules/@metro-labs/daemon/src/agent-user/schedule-detail.ts +99 -0
- package/runtime/node_modules/@metro-labs/daemon/src/agent-user/schedules-api.ts +27 -0
- package/runtime/node_modules/@metro-labs/daemon/src/agent-user/schedules.ts +144 -0
- package/runtime/node_modules/@metro-labs/daemon/src/agent-user/unit-files.ts +37 -0
- package/runtime/node_modules/@metro-labs/daemon/src/agent-user/user.ts +89 -0
- package/runtime/node_modules/@metro-labs/daemon/src/agent-user/view.ts +87 -0
- package/runtime/node_modules/@metro-labs/daemon/src/agents/account-attach.ts +19 -0
- package/runtime/node_modules/@metro-labs/daemon/src/agents/account-lookups.ts +55 -0
- package/runtime/node_modules/@metro-labs/daemon/src/agents/account-routes.ts +76 -0
- package/runtime/node_modules/@metro-labs/daemon/src/agents/accounts-api.ts +357 -0
- package/runtime/node_modules/@metro-labs/daemon/src/agents/admin.ts +30 -0
- package/runtime/node_modules/@metro-labs/daemon/src/agents/allowlist.ts +59 -0
- package/runtime/node_modules/@metro-labs/daemon/src/agents/api.ts +131 -0
- package/runtime/node_modules/@metro-labs/daemon/src/agents/bundle.ts +132 -0
- package/runtime/node_modules/@metro-labs/daemon/src/agents/file-admin.ts +305 -0
- package/runtime/node_modules/@metro-labs/daemon/src/agents/files.ts +142 -0
- package/runtime/node_modules/@metro-labs/{mcp/src/db/key-map.ts → daemon/src/agents/keys.ts} +1 -11
- package/runtime/node_modules/@metro-labs/{mcp/src/db/agent-map.ts → daemon/src/agents/map.ts} +58 -1
- package/runtime/node_modules/@metro-labs/daemon/src/agents/policy-route.ts +27 -0
- package/runtime/node_modules/@metro-labs/{mcp/src/db/agent-scope.ts → daemon/src/agents/scope.ts} +2 -2
- package/runtime/node_modules/@metro-labs/daemon/src/agents/sender-cards.ts +94 -0
- package/runtime/node_modules/@metro-labs/daemon/src/agents/senders.ts +38 -0
- package/runtime/node_modules/@metro-labs/daemon/src/approvals/api.ts +55 -0
- package/runtime/node_modules/@metro-labs/daemon/src/approvals/pending.ts +150 -0
- package/runtime/node_modules/@metro-labs/daemon/src/approvals/preview.ts +66 -0
- package/runtime/node_modules/@metro-labs/daemon/src/boot/boot.ts +218 -0
- package/runtime/node_modules/@metro-labs/daemon/src/boot/connect-hint.ts +36 -0
- package/runtime/node_modules/@metro-labs/{mcp/src/daemon → daemon/src/boot}/crash-guard.ts +1 -1
- package/runtime/node_modules/@metro-labs/daemon/src/boot/local-owner.ts +21 -0
- package/runtime/node_modules/@metro-labs/{mcp/src/daemon → daemon/src/boot}/paths.ts +6 -34
- package/runtime/node_modules/@metro-labs/daemon/src/channels/addressed.ts +26 -0
- package/runtime/node_modules/@metro-labs/{mcp → daemon}/src/channels/inbound.ts +70 -45
- package/runtime/node_modules/@metro-labs/{mcp → daemon}/src/channels/media-note.ts +11 -7
- package/runtime/node_modules/@metro-labs/{mcp → daemon}/src/channels/pending.ts +4 -1
- package/runtime/node_modules/@metro-labs/{mcp → daemon}/src/channels/relay.ts +2 -2
- package/runtime/node_modules/@metro-labs/daemon/src/claude/api.ts +355 -0
- package/runtime/node_modules/@metro-labs/daemon/src/claude/files.ts +359 -0
- package/runtime/node_modules/@metro-labs/daemon/src/claude/login.ts +208 -0
- package/runtime/node_modules/@metro-labs/daemon/src/claude/memory.ts +32 -0
- package/runtime/node_modules/@metro-labs/daemon/src/claude/onboarding.ts +60 -0
- package/runtime/node_modules/@metro-labs/daemon/src/claude/plugin-install.ts +149 -0
- package/runtime/node_modules/@metro-labs/daemon/src/claude/session-files.ts +75 -0
- package/runtime/node_modules/@metro-labs/daemon/src/claude/session.ts +284 -0
- package/runtime/node_modules/@metro-labs/daemon/src/claude/settings.ts +109 -0
- package/runtime/node_modules/@metro-labs/daemon/src/claude/setup.ts +273 -0
- package/runtime/node_modules/@metro-labs/daemon/src/claude/skills.ts +120 -0
- package/runtime/node_modules/@metro-labs/daemon/src/claude/version.ts +113 -0
- package/runtime/node_modules/@metro-labs/{mcp/src/daemon/connector-api.ts → daemon/src/connectors/api.ts} +79 -113
- package/runtime/node_modules/@metro-labs/{mcp/src/db/connector-config.ts → daemon/src/connectors/config.ts} +67 -41
- package/runtime/node_modules/@metro-labs/daemon/src/connectors/gates.ts +72 -0
- package/runtime/node_modules/@metro-labs/daemon/src/connectors/health.ts +18 -0
- package/runtime/node_modules/@metro-labs/{mcp/src/daemon → daemon/src/connectors}/oauth-client.ts +14 -17
- package/runtime/node_modules/@metro-labs/{mcp/src/daemon → daemon/src/connectors}/oauth-discovery.ts +33 -20
- package/runtime/node_modules/@metro-labs/{mcp/src/daemon/connector-oauth-routes.ts → daemon/src/connectors/oauth-routes.ts} +30 -32
- package/runtime/node_modules/@metro-labs/{mcp/src/daemon/connector-oauth.ts → daemon/src/connectors/oauth.ts} +61 -22
- package/runtime/node_modules/@metro-labs/daemon/src/connectors/plugin-sync.ts +131 -0
- package/runtime/node_modules/@metro-labs/daemon/src/connectors/reach.ts +27 -0
- package/runtime/node_modules/@metro-labs/daemon/src/connectors/relay-policy.ts +90 -0
- package/runtime/node_modules/@metro-labs/daemon/src/connectors/relay.ts +347 -0
- package/runtime/node_modules/@metro-labs/daemon/src/connectors/store.ts +385 -0
- package/runtime/node_modules/@metro-labs/daemon/src/connectors/tools.ts +81 -0
- package/runtime/node_modules/@metro-labs/daemon/src/connectors/url.ts +37 -0
- package/runtime/node_modules/@metro-labs/daemon/src/connectors/verify.ts +106 -0
- package/runtime/node_modules/@metro-labs/{mcp/src/daemon → daemon/src/files}/attach-grant.ts +4 -4
- package/runtime/node_modules/@metro-labs/{mcp/src/daemon → daemon/src/files}/attach-owner.ts +3 -3
- package/runtime/node_modules/@metro-labs/daemon/src/files/attach-reaper.ts +174 -0
- package/runtime/node_modules/@metro-labs/{mcp/src/daemon → daemon/src/files}/attach-serve.ts +12 -13
- package/runtime/node_modules/@metro-labs/{mcp/src/daemon → daemon/src/files}/upload-api.ts +10 -27
- package/runtime/node_modules/@metro-labs/{mcp/src/daemon → daemon/src/files}/upload-store.ts +4 -4
- package/runtime/node_modules/@metro-labs/daemon/src/gateway/bedrock.ts +233 -0
- package/runtime/node_modules/@metro-labs/daemon/src/gateway/codex-auth.ts +208 -0
- package/runtime/node_modules/@metro-labs/daemon/src/gateway/codex-device.ts +91 -0
- package/runtime/node_modules/@metro-labs/daemon/src/gateway/codex-stream.ts +173 -0
- package/runtime/node_modules/@metro-labs/daemon/src/gateway/codex-translate.ts +195 -0
- package/runtime/node_modules/@metro-labs/daemon/src/gateway/codex-version.ts +44 -0
- package/runtime/node_modules/@metro-labs/daemon/src/gateway/codex.ts +128 -0
- package/runtime/node_modules/@metro-labs/daemon/src/gateway/effort.ts +77 -0
- package/runtime/node_modules/@metro-labs/daemon/src/gateway/eventstream.ts +123 -0
- package/runtime/node_modules/@metro-labs/daemon/src/gateway/forward.ts +237 -0
- package/runtime/node_modules/@metro-labs/daemon/src/gateway/frames.ts +118 -0
- package/runtime/node_modules/@metro-labs/daemon/src/gateway/gateway.ts +274 -0
- package/runtime/node_modules/@metro-labs/daemon/src/gateway/gemini-auth.ts +160 -0
- package/runtime/node_modules/@metro-labs/daemon/src/gateway/gemini-client.ts +46 -0
- package/runtime/node_modules/@metro-labs/daemon/src/gateway/gemini-setup.ts +150 -0
- package/runtime/node_modules/@metro-labs/daemon/src/gateway/gemini-stream.ts +139 -0
- package/runtime/node_modules/@metro-labs/daemon/src/gateway/gemini-translate.ts +224 -0
- package/runtime/node_modules/@metro-labs/daemon/src/gateway/gemini.ts +208 -0
- package/runtime/node_modules/@metro-labs/daemon/src/gateway/model-api.ts +199 -0
- package/runtime/node_modules/@metro-labs/daemon/src/gateway/model-config.ts +278 -0
- package/runtime/node_modules/@metro-labs/daemon/src/gateway/model-signin.ts +193 -0
- package/runtime/node_modules/@metro-labs/daemon/src/gateway/model-store.ts +70 -0
- package/runtime/node_modules/@metro-labs/daemon/src/gateway/openrouter.ts +90 -0
- package/runtime/node_modules/@metro-labs/daemon/src/gateway/pkce.ts +36 -0
- package/runtime/node_modules/@metro-labs/daemon/src/gateway/provider-models.ts +66 -0
- package/runtime/node_modules/@metro-labs/daemon/src/gateway/served.ts +18 -0
- package/runtime/node_modules/@metro-labs/daemon/src/gateway/subscription.ts +120 -0
- package/runtime/node_modules/@metro-labs/daemon/src/gateway/text.ts +14 -0
- package/runtime/node_modules/@metro-labs/daemon/src/gateway/usage.ts +244 -0
- package/runtime/node_modules/@metro-labs/{mcp → daemon}/src/mcp/accounts.ts +28 -17
- package/runtime/node_modules/@metro-labs/daemon/src/mcp/attachment-schema.ts +91 -0
- package/runtime/node_modules/@metro-labs/{mcp → daemon}/src/mcp/call-tools.ts +49 -20
- package/runtime/node_modules/@metro-labs/{mcp → daemon}/src/mcp/ctx.ts +4 -4
- package/runtime/node_modules/@metro-labs/{mcp → daemon}/src/mcp/event-store.ts +15 -16
- package/runtime/node_modules/@metro-labs/{mcp → daemon}/src/mcp/group-tools.ts +2 -2
- package/runtime/node_modules/@metro-labs/{mcp → daemon}/src/mcp/index.ts +24 -79
- package/runtime/node_modules/@metro-labs/daemon/src/mcp/instructions.ts +19 -0
- package/runtime/node_modules/@metro-labs/{mcp → daemon}/src/mcp/member-tools.ts +2 -2
- package/runtime/node_modules/@metro-labs/daemon/src/mcp/permission-prompt.ts +87 -0
- package/runtime/node_modules/@metro-labs/daemon/src/mcp/permission-relay.ts +79 -0
- package/runtime/node_modules/@metro-labs/daemon/src/mcp/policy-gate.ts +82 -0
- package/runtime/node_modules/@metro-labs/daemon/src/mcp/policy-snapshot.ts +79 -0
- package/runtime/node_modules/@metro-labs/daemon/src/mcp/profile-lookup.ts +60 -0
- package/runtime/node_modules/@metro-labs/daemon/src/mcp/profile-tool.ts +96 -0
- package/runtime/node_modules/@metro-labs/daemon/src/mcp/read-tool.ts +87 -0
- package/runtime/node_modules/@metro-labs/daemon/src/mcp/request-identity.ts +46 -0
- package/runtime/node_modules/@metro-labs/daemon/src/mcp/session-slot.ts +48 -0
- package/runtime/node_modules/@metro-labs/{mcp → daemon}/src/mcp/session.ts +33 -42
- package/runtime/node_modules/@metro-labs/daemon/src/mcp/tool-catalog.ts +88 -0
- package/runtime/node_modules/@metro-labs/daemon/src/mcp/tool-def.ts +9 -0
- package/runtime/node_modules/@metro-labs/{mcp → daemon}/src/mcp/tool-dispatch.ts +53 -40
- package/runtime/node_modules/@metro-labs/{mcp → daemon}/src/mcp/tool-schemas.ts +68 -119
- package/runtime/node_modules/@metro-labs/{mcp → daemon}/src/mcp/upload-tool.ts +6 -11
- package/runtime/node_modules/@metro-labs/daemon/src/metro-user/helper-script.ts +126 -0
- package/runtime/node_modules/@metro-labs/daemon/src/metro-user/install.ts +37 -0
- package/runtime/node_modules/@metro-labs/daemon/src/metro-user/privilege.ts +48 -0
- package/runtime/node_modules/@metro-labs/daemon/src/monitor/api.ts +132 -0
- package/runtime/node_modules/@metro-labs/daemon/src/net/tunnel.ts +316 -0
- package/runtime/node_modules/@metro-labs/daemon/src/policy/policy.ts +118 -0
- package/runtime/node_modules/@metro-labs/daemon/src/routes/bearer.ts +35 -0
- package/runtime/node_modules/@metro-labs/daemon/src/routes/body.ts +24 -0
- package/runtime/node_modules/@metro-labs/{mcp/src/daemon → daemon/src/routes}/http.ts +59 -51
- package/runtime/node_modules/@metro-labs/daemon/src/routes/local-mode.ts +205 -0
- package/runtime/node_modules/@metro-labs/daemon/src/routes/session-apis.ts +66 -0
- package/runtime/node_modules/@metro-labs/daemon/src/routes/session.ts +29 -0
- package/runtime/node_modules/@metro-labs/daemon/src/routes/threema-callback.ts +103 -0
- package/runtime/node_modules/@metro-labs/daemon/src/server/control.ts +39 -0
- package/runtime/node_modules/@metro-labs/daemon/src/server/machine.ts +58 -0
- package/runtime/node_modules/@metro-labs/daemon/src/server/owner.ts +26 -0
- package/runtime/node_modules/@metro-labs/daemon/src/server/update.ts +90 -0
- package/runtime/node_modules/@metro-labs/daemon/src/server.ts +2 -0
- package/runtime/node_modules/@metro-labs/daemon/src/stations/attach-driver.ts +42 -0
- package/runtime/node_modules/@metro-labs/{mcp → daemon}/src/stations/attach-inline.ts +29 -1
- package/runtime/node_modules/@metro-labs/{mcp → daemon}/src/stations/attach-interactive.ts +9 -36
- package/runtime/node_modules/@metro-labs/daemon/src/stations/attach-outlook.ts +63 -0
- package/runtime/node_modules/@metro-labs/{mcp → daemon}/src/stations/attach-resolve.ts +38 -13
- package/runtime/node_modules/@metro-labs/{mcp/src/daemon → daemon/src/stations}/attach-session.ts +32 -22
- package/runtime/node_modules/@metro-labs/{mcp → daemon}/src/stations/attach-xmtp.ts +8 -9
- package/runtime/node_modules/@metro-labs/{mcp → daemon}/src/stations/attach.ts +75 -9
- package/runtime/node_modules/@metro-labs/daemon/src/stations/materialize.ts +265 -0
- package/runtime/node_modules/@metro-labs/{mcp → daemon}/src/stations/registry.ts +22 -6
- package/runtime/node_modules/@metro-labs/daemon/src/stations/runtime-deps.ts +82 -0
- package/runtime/node_modules/@metro-labs/{mcp/src/daemon → daemon/src/stations}/supervisor-io.ts +3 -3
- package/runtime/node_modules/@metro-labs/{mcp/src/daemon → daemon/src/stations}/supervisor.ts +6 -9
- package/runtime/node_modules/@metro-labs/daemon/src/stations/threema-callbacks.ts +32 -0
- package/runtime/node_modules/@metro-labs/{mcp/src/daemon → daemon/src/stations}/train-call.ts +1 -1
- package/runtime/node_modules/@metro-labs/daemon/src/stations/webhook-endpoints.ts +51 -0
- package/runtime/node_modules/@metro-labs/daemon/src/terminal/api.ts +84 -0
- package/runtime/node_modules/@metro-labs/daemon/src/terminal/socket.ts +124 -0
- package/runtime/node_modules/@metro-labs/daemon/src/terminal/tickets.ts +15 -0
- package/runtime/node_modules/@metro-labs/daemon/src/vault/api.ts +45 -0
- package/runtime/node_modules/@metro-labs/daemon/src/vault/config.ts +51 -0
- package/runtime/node_modules/@metro-labs/daemon/src/vault/firewall.ts +9 -0
- package/runtime/node_modules/@metro-labs/daemon/src/vault/index.ts +96 -0
- package/runtime/node_modules/@metro-labs/daemon/src/vault/install.ts +56 -0
- package/runtime/node_modules/@metro-labs/daemon/src/vault/paths.ts +26 -0
- package/runtime/node_modules/@metro-labs/daemon/src/vault/proxy.ts +147 -0
- package/runtime/node_modules/@metro-labs/daemon/src/vault/store.ts +129 -0
- package/runtime/node_modules/@metro-labs/daemon/src/vault/trust.ts +44 -0
- package/runtime/node_modules/@metro-labs/{discord → discord-bot}/package.json +3 -5
- package/runtime/node_modules/@metro-labs/{discord → discord-bot}/src/accounts.ts +8 -28
- package/runtime/node_modules/@metro-labs/{discord → discord-bot}/src/actions.ts +19 -187
- package/runtime/node_modules/@metro-labs/{discord → discord-bot}/src/attachments.ts +3 -3
- package/runtime/node_modules/@metro-labs/{discord → discord-bot}/src/format.ts +31 -72
- package/runtime/node_modules/@metro-labs/{discord → discord-bot}/src/group-actions.ts +2 -2
- package/runtime/node_modules/@metro-labs/{discord → discord-bot}/src/group.ts +2 -2
- package/runtime/node_modules/@metro-labs/{discord → discord-bot}/src/index.ts +15 -21
- package/runtime/node_modules/@metro-labs/{discord → discord-bot}/src/members.ts +2 -2
- package/runtime/node_modules/@metro-labs/discord-bot/src/profile.ts +68 -0
- package/runtime/node_modules/@metro-labs/{discord → discord-bot}/src/send-files.ts +12 -13
- package/runtime/node_modules/@metro-labs/discord-bot/src/station.ts +23 -0
- package/runtime/node_modules/@metro-labs/http/package.json +29 -0
- package/runtime/node_modules/@metro-labs/http/src/api-http.ts +135 -0
- package/runtime/node_modules/@metro-labs/{mcp/src/daemon/session-api.ts → http/src/mode-api.ts} +13 -9
- package/runtime/node_modules/@metro-labs/http/src/return-to.ts +21 -0
- package/runtime/node_modules/@metro-labs/http/src/workos-token.ts +149 -0
- package/runtime/node_modules/@metro-labs/{telegram-user → outlook}/package.json +2 -5
- package/runtime/node_modules/@metro-labs/outlook/src/accounts.ts +93 -0
- package/runtime/node_modules/@metro-labs/outlook/src/actions.ts +14 -0
- package/runtime/node_modules/@metro-labs/outlook/src/attachments.ts +97 -0
- package/runtime/node_modules/@metro-labs/outlook/src/auth.ts +150 -0
- package/runtime/node_modules/@metro-labs/outlook/src/browser.ts +53 -0
- package/runtime/node_modules/@metro-labs/outlook/src/config.ts +36 -0
- package/runtime/node_modules/@metro-labs/outlook/src/format.ts +140 -0
- package/runtime/node_modules/@metro-labs/outlook/src/graph.ts +60 -0
- package/runtime/node_modules/@metro-labs/outlook/src/html.ts +72 -0
- package/runtime/node_modules/@metro-labs/outlook/src/inbound.ts +104 -0
- package/runtime/node_modules/@metro-labs/outlook/src/index.ts +12 -0
- package/runtime/node_modules/@metro-labs/outlook/src/login.ts +150 -0
- package/runtime/node_modules/@metro-labs/outlook/src/me.ts +41 -0
- package/runtime/node_modules/@metro-labs/outlook/src/outbound.ts +116 -0
- package/runtime/node_modules/@metro-labs/outlook/src/query.ts +119 -0
- package/runtime/node_modules/@metro-labs/outlook/src/state.ts +43 -0
- package/runtime/node_modules/@metro-labs/outlook/src/station.ts +15 -0
- package/runtime/node_modules/@metro-labs/outlook/src/trust.ts +70 -0
- package/runtime/node_modules/@metro-labs/telegram/package.json +3 -2
- package/runtime/node_modules/@metro-labs/telegram/src/accounts.ts +38 -96
- package/runtime/node_modules/@metro-labs/telegram/src/actions.ts +214 -253
- package/runtime/node_modules/@metro-labs/telegram/src/attachments.ts +67 -108
- package/runtime/node_modules/@metro-labs/{telegram-user → telegram}/src/client.ts +6 -6
- package/runtime/node_modules/@metro-labs/telegram/src/format.ts +101 -147
- package/runtime/node_modules/@metro-labs/{telegram-user → telegram}/src/group.ts +4 -4
- package/runtime/node_modules/@metro-labs/{telegram-user → telegram}/src/history.ts +1 -1
- package/runtime/node_modules/@metro-labs/telegram/src/inbound.ts +57 -0
- package/runtime/node_modules/@metro-labs/telegram/src/index.ts +6 -102
- package/runtime/node_modules/@metro-labs/{telegram-user → telegram}/src/login.ts +20 -20
- package/runtime/node_modules/@metro-labs/telegram/src/media-actions.ts +64 -151
- package/runtime/node_modules/@metro-labs/telegram/src/members.ts +61 -33
- package/runtime/node_modules/@metro-labs/telegram/src/profile.ts +70 -0
- package/runtime/node_modules/@metro-labs/{telegram-user → telegram}/src/reactions.ts +3 -3
- package/runtime/node_modules/@metro-labs/telegram/src/station.ts +11 -1
- package/runtime/node_modules/@metro-labs/telegram/src/types.ts +5 -47
- package/runtime/node_modules/@metro-labs/telegram-bot/package.json +25 -0
- package/runtime/node_modules/@metro-labs/telegram-bot/src/accounts.ts +86 -0
- package/runtime/node_modules/@metro-labs/telegram-bot/src/actions.ts +240 -0
- package/runtime/node_modules/@metro-labs/telegram-bot/src/attachments.ts +128 -0
- package/runtime/node_modules/@metro-labs/telegram-bot/src/format.ts +170 -0
- package/runtime/node_modules/@metro-labs/telegram-bot/src/index.ts +103 -0
- package/runtime/node_modules/@metro-labs/telegram-bot/src/media-actions.ts +90 -0
- package/runtime/node_modules/@metro-labs/telegram-bot/src/members.ts +47 -0
- package/runtime/node_modules/@metro-labs/telegram-bot/src/profile.ts +85 -0
- package/runtime/node_modules/@metro-labs/telegram-bot/src/station.ts +21 -0
- package/runtime/node_modules/@metro-labs/telegram-bot/src/types.ts +56 -0
- package/runtime/node_modules/@metro-labs/threema/package.json +26 -0
- package/runtime/node_modules/@metro-labs/threema/src/accounts.ts +95 -0
- package/runtime/node_modules/@metro-labs/threema/src/actions.ts +124 -0
- package/runtime/node_modules/@metro-labs/threema/src/api.ts +152 -0
- package/runtime/node_modules/@metro-labs/threema/src/crypto.ts +100 -0
- package/runtime/node_modules/@metro-labs/threema/src/files.ts +47 -0
- package/runtime/node_modules/@metro-labs/threema/src/format.ts +164 -0
- package/runtime/node_modules/@metro-labs/threema/src/groups.ts +96 -0
- package/runtime/node_modules/@metro-labs/threema/src/ids.ts +21 -0
- package/runtime/node_modules/@metro-labs/threema/src/inbound.ts +89 -0
- package/runtime/node_modules/@metro-labs/threema/src/index.ts +24 -0
- package/runtime/node_modules/@metro-labs/threema/src/messages.ts +264 -0
- package/runtime/node_modules/@metro-labs/threema/src/outbound.ts +160 -0
- package/runtime/node_modules/@metro-labs/threema/src/station.ts +13 -0
- package/runtime/node_modules/@metro-labs/threema/src/verify.ts +117 -0
- package/runtime/node_modules/@metro-labs/webhook/package.json +1 -1
- package/runtime/node_modules/@metro-labs/webhook/src/station.ts +5 -8
- package/runtime/node_modules/@metro-labs/whatsapp/package.json +2 -5
- package/runtime/node_modules/@metro-labs/whatsapp/src/accounts.ts +6 -28
- package/runtime/node_modules/@metro-labs/whatsapp/src/ack.ts +1 -1
- package/runtime/node_modules/@metro-labs/whatsapp/src/actions.ts +54 -11
- package/runtime/node_modules/@metro-labs/whatsapp/src/attachments.ts +2 -2
- package/runtime/node_modules/@metro-labs/whatsapp/src/auth-state.ts +1 -1
- package/runtime/node_modules/@metro-labs/whatsapp/src/client.ts +86 -4
- package/runtime/node_modules/@metro-labs/whatsapp/src/format.ts +7 -58
- package/runtime/node_modules/@metro-labs/whatsapp/src/inbound.ts +11 -28
- package/runtime/node_modules/@metro-labs/whatsapp/src/index.ts +4 -37
- package/runtime/node_modules/@metro-labs/whatsapp/src/keys.ts +1 -1
- package/runtime/node_modules/@metro-labs/whatsapp/src/login.ts +1 -1
- package/runtime/node_modules/@metro-labs/whatsapp/src/media.ts +1 -1
- package/runtime/node_modules/@metro-labs/whatsapp/src/names.ts +75 -0
- package/runtime/node_modules/@metro-labs/whatsapp/src/parse.ts +44 -0
- package/runtime/node_modules/@metro-labs/whatsapp/src/resolve.ts +52 -0
- package/runtime/node_modules/@metro-labs/whatsapp/src/station.ts +16 -1
- package/runtime/node_modules/@metro-labs/whatsapp/src/token-store.ts +5 -9
- package/runtime/node_modules/@metro-labs/whatsapp/src/types.ts +0 -1
- package/runtime/node_modules/@metro-labs/xmtp/package.json +3 -2
- package/runtime/node_modules/@metro-labs/xmtp/src/accounts.ts +10 -20
- package/runtime/node_modules/@metro-labs/xmtp/src/actions-close.ts +2 -2
- package/runtime/node_modules/@metro-labs/xmtp/src/actions-conv.ts +15 -204
- package/runtime/node_modules/@metro-labs/xmtp/src/actions-meta.ts +4 -43
- package/runtime/node_modules/@metro-labs/xmtp/src/actions.ts +41 -219
- package/runtime/node_modules/@metro-labs/xmtp/src/attachments.ts +3 -3
- package/runtime/node_modules/@metro-labs/xmtp/src/conv-helpers.ts +3 -3
- package/runtime/node_modules/@metro-labs/xmtp/src/emit-payloads.ts +7 -24
- package/runtime/node_modules/@metro-labs/xmtp/src/emit.ts +8 -18
- package/runtime/node_modules/@metro-labs/xmtp/src/group-members.ts +1 -1
- package/runtime/node_modules/@metro-labs/xmtp/src/group.ts +2 -2
- package/runtime/node_modules/@metro-labs/xmtp/src/identity.ts +30 -16
- package/runtime/node_modules/@metro-labs/xmtp/src/index.ts +12 -20
- package/runtime/node_modules/@metro-labs/xmtp/src/labels.ts +1 -19
- package/runtime/node_modules/@metro-labs/xmtp/src/member-args.ts +1 -1
- package/runtime/node_modules/@metro-labs/xmtp/src/names.ts +61 -0
- package/runtime/node_modules/@metro-labs/xmtp/src/profile.ts +153 -0
- package/runtime/node_modules/@metro-labs/xmtp/src/sender.ts +112 -0
- package/runtime/node_modules/@metro-labs/xmtp/src/smart.ts +72 -0
- package/runtime/node_modules/@metro-labs/xmtp/src/station.ts +5 -1
- package/runtime/node_modules/@metro-labs/xmtp/src/tools-handlers.ts +4 -51
- package/runtime/node_modules/@metro-labs/xmtp/src/tools.ts +30 -44
- package/runtime/node_modules/@metro-labs/xmtp/src/transcribe.ts +4 -6
- package/runtime/node_modules/@metro-labs/xmtp/src/verify.ts +19 -5
- package/runtime/node_modules/@metro-labs/xmtp/src/wire.ts +14 -4
- package/runtime/runtime.json +1 -1
- package/runtime/server.ts +1 -0
- package/runtime/stations.json +37 -0
- package/dist/prompt.js +0 -10
- package/dist/store.js +0 -70
- package/runtime/node_modules/@metro-labs/discord/src/station.ts +0 -19
- package/runtime/node_modules/@metro-labs/discord/src/voice-speak.ts +0 -195
- package/runtime/node_modules/@metro-labs/discord/src/voice-transcribe.ts +0 -292
- package/runtime/node_modules/@metro-labs/discord/src/voice.ts +0 -197
- package/runtime/node_modules/@metro-labs/discord/src/wire.ts +0 -5
- package/runtime/node_modules/@metro-labs/mcp/package.json +0 -66
- package/runtime/node_modules/@metro-labs/mcp/src/channels/dedupe.ts +0 -24
- package/runtime/node_modules/@metro-labs/mcp/src/daemon/account-api.ts +0 -311
- package/runtime/node_modules/@metro-labs/mcp/src/daemon/agent-api.ts +0 -335
- package/runtime/node_modules/@metro-labs/mcp/src/daemon/api-http.ts +0 -129
- package/runtime/node_modules/@metro-labs/mcp/src/daemon/boot.ts +0 -322
- package/runtime/node_modules/@metro-labs/mcp/src/daemon/cli-pair-api.ts +0 -111
- package/runtime/node_modules/@metro-labs/mcp/src/daemon/cli-pair.ts +0 -20
- package/runtime/node_modules/@metro-labs/mcp/src/daemon/collection-api.ts +0 -202
- package/runtime/node_modules/@metro-labs/mcp/src/daemon/connector-json.ts +0 -31
- package/runtime/node_modules/@metro-labs/mcp/src/daemon/connector-tools.ts +0 -100
- package/runtime/node_modules/@metro-labs/mcp/src/daemon/connector-url.ts +0 -57
- package/runtime/node_modules/@metro-labs/mcp/src/daemon/connector-verify.ts +0 -333
- package/runtime/node_modules/@metro-labs/mcp/src/daemon/google-auth.ts +0 -164
- package/runtime/node_modules/@metro-labs/mcp/src/daemon/google-oauth.ts +0 -208
- package/runtime/node_modules/@metro-labs/mcp/src/daemon/identity.ts +0 -79
- package/runtime/node_modules/@metro-labs/mcp/src/daemon/oauth-pending.ts +0 -65
- package/runtime/node_modules/@metro-labs/mcp/src/daemon/pair-codes.ts +0 -58
- package/runtime/node_modules/@metro-labs/mcp/src/daemon/project-api.ts +0 -214
- package/runtime/node_modules/@metro-labs/mcp/src/daemon/run-api.ts +0 -125
- package/runtime/node_modules/@metro-labs/mcp/src/daemon/run-pair.ts +0 -18
- package/runtime/node_modules/@metro-labs/mcp/src/daemon/runtime-source.ts +0 -105
- package/runtime/node_modules/@metro-labs/mcp/src/daemon/session-apis.ts +0 -41
- package/runtime/node_modules/@metro-labs/mcp/src/daemon/session.ts +0 -218
- package/runtime/node_modules/@metro-labs/mcp/src/daemon/tunnel.ts +0 -169
- package/runtime/node_modules/@metro-labs/mcp/src/db/account-attach.ts +0 -91
- package/runtime/node_modules/@metro-labs/mcp/src/db/agent-admin.ts +0 -215
- package/runtime/node_modules/@metro-labs/mcp/src/db/client.ts +0 -37
- package/runtime/node_modules/@metro-labs/mcp/src/db/connector-collections.ts +0 -218
- package/runtime/node_modules/@metro-labs/mcp/src/db/connectors.ts +0 -329
- package/runtime/node_modules/@metro-labs/mcp/src/db/materialize.ts +0 -364
- package/runtime/node_modules/@metro-labs/mcp/src/db/projects.ts +0 -300
- package/runtime/node_modules/@metro-labs/mcp/src/db/runtime-admin.ts +0 -31
- package/runtime/node_modules/@metro-labs/mcp/src/db/runtimes.ts +0 -105
- package/runtime/node_modules/@metro-labs/mcp/src/db/schema.ts +0 -131
- package/runtime/node_modules/@metro-labs/mcp/src/db/token-carry.ts +0 -45
- package/runtime/node_modules/@metro-labs/mcp/src/db/users.ts +0 -57
- package/runtime/node_modules/@metro-labs/mcp/src/db/whatsapp-login.ts +0 -18
- package/runtime/node_modules/@metro-labs/mcp/src/mcp/channel-owner.ts +0 -29
- package/runtime/node_modules/@metro-labs/mcp/src/mcp/frame-scope.ts +0 -21
- package/runtime/node_modules/@metro-labs/mcp/src/mcp/permission-relay.ts +0 -66
- package/runtime/node_modules/@metro-labs/mcp/src/mcp/request-identity.ts +0 -73
- package/runtime/node_modules/@metro-labs/mcp/src/mcp/session-registry.ts +0 -172
- package/runtime/node_modules/@metro-labs/mcp/src/mcp/session-route.ts +0 -43
- package/runtime/node_modules/@metro-labs/mcp/src/monitor/api.ts +0 -240
- package/runtime/node_modules/@metro-labs/mcp/src/server.ts +0 -2
- package/runtime/node_modules/@metro-labs/mcp/src/stations/account-store.ts +0 -80
- package/runtime/node_modules/@metro-labs/mcp/src/stations/messaging-normalize.ts +0 -88
- package/runtime/node_modules/@metro-labs/telegram/src/wire.ts +0 -3
- package/runtime/node_modules/@metro-labs/telegram-user/src/accounts.ts +0 -81
- package/runtime/node_modules/@metro-labs/telegram-user/src/actions.ts +0 -254
- package/runtime/node_modules/@metro-labs/telegram-user/src/attachments.ts +0 -87
- package/runtime/node_modules/@metro-labs/telegram-user/src/format.ts +0 -141
- package/runtime/node_modules/@metro-labs/telegram-user/src/inbound.ts +0 -78
- package/runtime/node_modules/@metro-labs/telegram-user/src/index.ts +0 -46
- package/runtime/node_modules/@metro-labs/telegram-user/src/media-actions.ts +0 -80
- package/runtime/node_modules/@metro-labs/telegram-user/src/members.ts +0 -75
- package/runtime/node_modules/@metro-labs/telegram-user/src/normalize.ts +0 -22
- package/runtime/node_modules/@metro-labs/telegram-user/src/station.ts +0 -24
- package/runtime/node_modules/@metro-labs/telegram-user/src/types.ts +0 -7
- package/runtime/node_modules/@metro-labs/telegram-user/src/wire.ts +0 -1
- package/runtime/node_modules/@metro-labs/whatsapp/src/normalize.ts +0 -22
- package/runtime/node_modules/@metro-labs/whatsapp/src/wire.ts +0 -5
- package/runtime/node_modules/@metro-labs/xmtp/src/actions-push.ts +0 -87
- package/runtime/node_modules/@metro-labs/xmtp/src/emit-core.ts +0 -55
- package/runtime/node_modules/@metro-labs/xmtp/src/push.ts +0 -349
- /package/runtime/node_modules/@metro-labs/{mcp/src/daemon → core/src}/log.ts +0 -0
- /package/runtime/node_modules/@metro-labs/{mcp/src/mcp → core/src}/str.ts +0 -0
- /package/runtime/node_modules/@metro-labs/{mcp/src/daemon → core/src}/train-error.ts +0 -0
- /package/runtime/node_modules/@metro-labs/{mcp → daemon}/src/channels/webhook-note.ts +0 -0
- /package/runtime/node_modules/@metro-labs/{mcp → daemon}/src/mcp/raw-get-stream.ts +0 -0
- /package/runtime/node_modules/@metro-labs/{discord → discord-bot}/src/api-base.ts +0 -0
- /package/runtime/node_modules/@metro-labs/{discord → discord-bot}/src/verify.ts +0 -0
- /package/runtime/node_modules/@metro-labs/{mcp/src/daemon → http/src}/api-error.ts +0 -0
- /package/runtime/node_modules/@metro-labs/{mcp/src/daemon → http/src}/cors.ts +0 -0
- /package/runtime/node_modules/@metro-labs/{telegram → telegram-bot}/src/verify.ts +0 -0
|
@@ -0,0 +1,273 @@
|
|
|
1
|
+
import { createHash } from 'node:crypto';
|
|
2
|
+
import { mkdirSync, rmSync } from 'node:fs';
|
|
3
|
+
import { existsSync, readFileSync, statSync } from '../agent-user/agent-fs.js';
|
|
4
|
+
import { join } from 'node:path';
|
|
5
|
+
import { fileURLToPath } from 'node:url';
|
|
6
|
+
import { errMsg, log } from '@metro-labs/core/log';
|
|
7
|
+
import { isRecord } from '@metro-labs/core/is-record';
|
|
8
|
+
import { readJson, writeAtomic, writeJson } from '@metro-labs/core/secure-fs';
|
|
9
|
+
import { agentsDir } from '../agents/files.js';
|
|
10
|
+
import { writeHomeText } from '../agent-user/home-fs.js';
|
|
11
|
+
import { claudeDir } from './files.js';
|
|
12
|
+
import { stagedMarketplaceDir } from './plugin-install.js';
|
|
13
|
+
import { readModelConfig, routedConnection, type ModelConfig } from '../gateway/model-config.js';
|
|
14
|
+
|
|
15
|
+
export const PRIVACY_ENV: Record<string, string> = {
|
|
16
|
+
DISABLE_TELEMETRY: '1',
|
|
17
|
+
DISABLE_ERROR_REPORTING: '1',
|
|
18
|
+
CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC: '1',
|
|
19
|
+
CLAUDE_CODE_GB_DISK_CACHE_WHEN_TELEMETRY_OFF: '1',
|
|
20
|
+
};
|
|
21
|
+
export const RETENTION_DAYS = 7;
|
|
22
|
+
const SKILL_NAME = 'metro-orchestrator';
|
|
23
|
+
const STATE_FILE = 'claude-setup.json';
|
|
24
|
+
const GUIDANCE = 'orchestrator.md';
|
|
25
|
+
const SYSTEM_PROMPT_FILE = 'system-prompt.md';
|
|
26
|
+
export const SYSTEM_PROMPT_MAX = 64 * 1024;
|
|
27
|
+
|
|
28
|
+
const WORKER_AGENT = `---
|
|
29
|
+
name: worker
|
|
30
|
+
description: Default execution agent for delegated work. Full tool access and maximum reasoning effort. Use for any substantive task the orchestrator main thread cannot perform itself - reading and writing code, running commands, research, analysis.
|
|
31
|
+
effort: xhigh
|
|
32
|
+
---
|
|
33
|
+
|
|
34
|
+
You are a worker agent. The main thread that dispatched you is an orchestrator with no file, shell or network access; it can only delegate and relay messages. That means you are where the actual work happens, and the quality of your output is the quality of the result.
|
|
35
|
+
|
|
36
|
+
Work to completion. Do not hand back a partial answer with a suggestion that someone else finish it; there is no one else. If part of the task is genuinely blocked, complete every other part in full and say plainly what you left undone and why. Before you finish, read your own last paragraph: if it is a plan, a question or a promise about work you have not done, do that work now.
|
|
37
|
+
|
|
38
|
+
Keep the change to what the task asks for. A pre-existing bug, a performance problem or behaviour the task does not mention is a follow-up to report at the end, not something to fix now, unless the requested behaviour cannot work without it. Where the task is ambiguous, build the reading its wording and the surrounding code support best, and state that assumption. Commit tests only where the task asks for them or the repository already keeps tests for that kind of change, sized like the neighbouring test files; scratch checks you used to verify yourself need not be kept. This is about extras only: every behaviour the task does ask for, implement completely.
|
|
39
|
+
|
|
40
|
+
Never call AskUserQuestion or ExitPlanMode. They are blocked by policy, because nobody is watching the terminal around the clock and a blocking prompt stalls indefinitely. When you hit an ambiguity: do everything that does not depend on it, then choose the most reasonable interpretation, state the assumption explicitly in your report, and continue.
|
|
41
|
+
|
|
42
|
+
Your final message is a report to the orchestrator, not a message to a human. It gets relayed onward, so make it self-contained and concise. Lead with the outcome. Include concrete evidence, command output, file paths with line numbers, test results, for anything you claim to have verified. Report failures faithfully: if a test failed, say so and show the output; if you skipped a step, say that too.
|
|
43
|
+
`;
|
|
44
|
+
|
|
45
|
+
export interface SetupDeps {
|
|
46
|
+
dir?: string;
|
|
47
|
+
agents?: string;
|
|
48
|
+
guidance?: string;
|
|
49
|
+
env?: NodeJS.ProcessEnv;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export type Placed = 'written' | 'present' | 'updated' | 'missing';
|
|
53
|
+
export type SettingsOutcome = 'written' | 'unchanged' | 'unreadable';
|
|
54
|
+
|
|
55
|
+
export interface SetupReport {
|
|
56
|
+
privacy: boolean;
|
|
57
|
+
guard: 'plugin';
|
|
58
|
+
worker: Placed;
|
|
59
|
+
skill: Placed;
|
|
60
|
+
settings: SettingsOutcome;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export interface SetupStatus {
|
|
64
|
+
privacy: boolean;
|
|
65
|
+
permissionMode: PermissionMode;
|
|
66
|
+
systemPrompt: string;
|
|
67
|
+
guard: 'plugin';
|
|
68
|
+
worker: boolean;
|
|
69
|
+
skill: boolean;
|
|
70
|
+
privacyApplied: boolean;
|
|
71
|
+
retentionDays: number | null;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
const statePath = (agents: string): string => join(agents, STATE_FILE);
|
|
75
|
+
|
|
76
|
+
const PERMISSION_MODES = ['auto', 'bypass'] as const;
|
|
77
|
+
export type PermissionMode = (typeof PERMISSION_MODES)[number];
|
|
78
|
+
|
|
79
|
+
function readState(agents: string): Record<string, unknown> {
|
|
80
|
+
const raw = readJson<unknown>(statePath(agents), null);
|
|
81
|
+
return isRecord(raw) ? raw : {};
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function writeState(agents: string, patch: Record<string, unknown>): void {
|
|
85
|
+
mkdirSync(agents, { recursive: true });
|
|
86
|
+
writeJson(statePath(agents), { ...readState(agents), ...patch });
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
const privacyEnabled = (agents = agentsDir()): boolean => readState(agents).privacy !== false;
|
|
90
|
+
|
|
91
|
+
export function setPrivacy(enabled: boolean, agents = agentsDir()): void {
|
|
92
|
+
writeState(agents, { privacy: enabled });
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
export const isPermissionMode = (value: unknown): value is PermissionMode => PERMISSION_MODES.some((m) => m === value);
|
|
96
|
+
|
|
97
|
+
export function permissionMode(agents = agentsDir()): PermissionMode {
|
|
98
|
+
const mode = readState(agents).permissionMode;
|
|
99
|
+
return isPermissionMode(mode) ? mode : 'auto';
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
export function setPermissionMode(mode: PermissionMode, agents = agentsDir()): void {
|
|
103
|
+
writeState(agents, { permissionMode: mode });
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
const promptPath = (agents: string): string => join(agents, SYSTEM_PROMPT_FILE);
|
|
107
|
+
|
|
108
|
+
export function systemPrompt(agents = agentsDir()): string {
|
|
109
|
+
const path = promptPath(agents);
|
|
110
|
+
if (!existsSync(path)) return '';
|
|
111
|
+
return readFileSync(path, 'utf8').trim();
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
export function setSystemPrompt(text: string, agents = agentsDir()): void {
|
|
115
|
+
const trimmed = text.trim();
|
|
116
|
+
if (trimmed === '') {
|
|
117
|
+
rmSync(promptPath(agents), { force: true });
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
120
|
+
writeAtomic(promptPath(agents), `${trimmed}\n`, 0o600);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
function guidancePath(env: NodeJS.ProcessEnv = process.env): string {
|
|
124
|
+
const staged = stagedMarketplaceDir(env);
|
|
125
|
+
if (staged !== null) {
|
|
126
|
+
const path = join(staged, 'plugin', GUIDANCE);
|
|
127
|
+
if (existsSync(path)) return path;
|
|
128
|
+
}
|
|
129
|
+
return fileURLToPath(new URL(`../../../../plugin/${GUIDANCE}`, import.meta.url));
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
const PRIOR_WORKER: ReadonlySet<string> = new Set(['08a0cd8710df285d6512245246bb8b658b7cfc7f3e89490514ee5376779eb2ef']);
|
|
133
|
+
const PRIOR_SKILL: ReadonlySet<string> = new Set([
|
|
134
|
+
'0b3d122ed95beff09d579cf912cd4238e1db524c41fce4b314de57d6ff5908ad',
|
|
135
|
+
'36f4fb57f231a119d717b5e3d6ccb654f94960fcca708683f376679aead8df25',
|
|
136
|
+
'3840bc50253e51f431691376cf29a925b8245d87b77b8b20804a159e378a6e67',
|
|
137
|
+
'e6b59eba825b4568dfa9ace5049fd9568a2e10d731719976ad2657084fd4e5f9',
|
|
138
|
+
]);
|
|
139
|
+
|
|
140
|
+
const digest = (text: string): string => createHash('sha256').update(text).digest('hex');
|
|
141
|
+
|
|
142
|
+
export function placeFile(path: string, text: string, prior: ReadonlySet<string> = new Set()): Placed {
|
|
143
|
+
if (!existsSync(path)) {
|
|
144
|
+
writeHomeText(path, text, 0o644);
|
|
145
|
+
return 'written';
|
|
146
|
+
}
|
|
147
|
+
const current = readFileSync(path, 'utf8');
|
|
148
|
+
if (current === text || !prior.has(digest(current))) return 'present';
|
|
149
|
+
writeHomeText(path, text, 0o644);
|
|
150
|
+
return 'updated';
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
function readSettings(path: string): Record<string, unknown> | null {
|
|
154
|
+
if (!existsSync(path)) return {};
|
|
155
|
+
try {
|
|
156
|
+
const parsed: unknown = JSON.parse(readFileSync(path, 'utf8'));
|
|
157
|
+
return isRecord(parsed) ? parsed : null;
|
|
158
|
+
} catch {
|
|
159
|
+
return null;
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
function withPrivacy(settings: Record<string, unknown>, enabled: boolean): Record<string, unknown> {
|
|
164
|
+
const current = isRecord(settings.env) ? settings.env : {};
|
|
165
|
+
if (enabled)
|
|
166
|
+
return { ...settings, env: { ...current, ...PRIVACY_ENV }, cleanupPeriodDays: settings.cleanupPeriodDays ?? RETENTION_DAYS };
|
|
167
|
+
const env = Object.fromEntries(Object.entries(current).filter(([key]) => !(key in PRIVACY_ENV)));
|
|
168
|
+
const rest = Object.fromEntries(Object.entries(settings).filter(([key]) => key !== 'env'));
|
|
169
|
+
return Object.keys(env).length === 0 ? rest : { ...rest, env };
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
function mergeSettings(dir: string, change: (settings: Record<string, unknown>) => Record<string, unknown>): SettingsOutcome {
|
|
173
|
+
const path = join(dir, 'settings.json');
|
|
174
|
+
const current = readSettings(path);
|
|
175
|
+
if (current === null) return 'unreadable';
|
|
176
|
+
const next = change(current);
|
|
177
|
+
if (existsSync(path) && JSON.stringify(next) === JSON.stringify(current)) return 'unchanged';
|
|
178
|
+
writeHomeText(path, `${JSON.stringify(next, null, 2)}\n`, existsSync(path) ? statSync(path).mode & 0o777 : 0o644);
|
|
179
|
+
return 'written';
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
function applyPrivacy(dir: string, enabled: boolean): SettingsOutcome {
|
|
183
|
+
return mergeSettings(dir, (current) => withPrivacy(current, enabled));
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
const skillPath = (dir: string): string => join(dir, 'skills', SKILL_NAME, 'SKILL.md');
|
|
187
|
+
const workerPath = (dir: string): string => join(dir, 'agents', 'worker.md');
|
|
188
|
+
|
|
189
|
+
function placeSkill(dir: string, guidance: string): Placed {
|
|
190
|
+
if (!existsSync(guidance)) return 'missing';
|
|
191
|
+
return placeFile(skillPath(dir), readFileSync(guidance, 'utf8'), PRIOR_SKILL);
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
const written = (placed: Placed): boolean => placed === 'written' || placed === 'updated';
|
|
195
|
+
|
|
196
|
+
export function ensureClaudeSetup(deps: SetupDeps = {}): SetupReport {
|
|
197
|
+
const dir = deps.dir ?? claudeDir();
|
|
198
|
+
const agents = deps.agents ?? agentsDir();
|
|
199
|
+
const privacy = privacyEnabled(agents);
|
|
200
|
+
const report: SetupReport = {
|
|
201
|
+
privacy,
|
|
202
|
+
guard: 'plugin',
|
|
203
|
+
worker: placeFile(workerPath(dir), WORKER_AGENT, PRIOR_WORKER),
|
|
204
|
+
skill: placeSkill(dir, deps.guidance ?? guidancePath(deps.env)),
|
|
205
|
+
settings: applyPrivacy(dir, privacy),
|
|
206
|
+
};
|
|
207
|
+
syncAvailableModelsQuietly({ ...deps, dir, agents });
|
|
208
|
+
if (written(report.worker) || written(report.skill) || report.settings === 'written')
|
|
209
|
+
log.info(report, 'claude-setup: applied the Claude Code setup for a metro box');
|
|
210
|
+
if (report.settings === 'unreadable') log.warn({ path: join(dir, 'settings.json') }, 'claude-setup: settings.json is not valid JSON, so the privacy settings were not written');
|
|
211
|
+
return report;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
export function claudeSetupStatus(deps: SetupDeps = {}): SetupStatus {
|
|
215
|
+
const dir = deps.dir ?? claudeDir();
|
|
216
|
+
const settings = readSettings(join(dir, 'settings.json'));
|
|
217
|
+
const env = settings !== null && isRecord(settings.env) ? settings.env : {};
|
|
218
|
+
const days = settings?.cleanupPeriodDays;
|
|
219
|
+
return {
|
|
220
|
+
privacy: privacyEnabled(deps.agents ?? agentsDir()),
|
|
221
|
+
permissionMode: permissionMode(deps.agents ?? agentsDir()),
|
|
222
|
+
systemPrompt: systemPrompt(deps.agents ?? agentsDir()),
|
|
223
|
+
guard: 'plugin',
|
|
224
|
+
worker: existsSync(workerPath(dir)),
|
|
225
|
+
skill: existsSync(skillPath(dir)),
|
|
226
|
+
privacyApplied: Object.keys(PRIVACY_ENV).every((key) => env[key] === '1'),
|
|
227
|
+
retentionDays: typeof days === 'number' ? days : null,
|
|
228
|
+
};
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
export function tryClaudeSetup(deps: SetupDeps = {}): void {
|
|
232
|
+
try {
|
|
233
|
+
ensureClaudeSetup(deps);
|
|
234
|
+
} catch (err) {
|
|
235
|
+
log.warn({ err: errMsg(err) }, 'claude-setup: could not apply the Claude Code setup');
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
const MODELS_KEY = 'availableModels';
|
|
240
|
+
const ENFORCE_KEY = 'enforceAvailableModels';
|
|
241
|
+
|
|
242
|
+
export function routeOf(cfg: ModelConfig): string | null {
|
|
243
|
+
const conn = routedConnection(cfg);
|
|
244
|
+
if (conn === null || conn.provider === 'anthropic' || conn.model === '') return null;
|
|
245
|
+
return `${conn.provider}:${conn.model}`;
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
function withAvailableModels(settings: Record<string, unknown>, route: string | null, metroWrote: boolean): Record<string, unknown> {
|
|
249
|
+
if (route !== null) return { ...settings, [MODELS_KEY]: [route], [ENFORCE_KEY]: true };
|
|
250
|
+
if (!metroWrote) return settings;
|
|
251
|
+
return Object.fromEntries(Object.entries(settings).filter(([key]) => key !== MODELS_KEY && key !== ENFORCE_KEY));
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
export function syncAvailableModels(cfg: ModelConfig, deps: SetupDeps = {}): SettingsOutcome {
|
|
255
|
+
const dir = deps.dir ?? claudeDir();
|
|
256
|
+
const agents = deps.agents ?? agentsDir();
|
|
257
|
+
const route = routeOf(cfg);
|
|
258
|
+
const state = readJson<unknown>(statePath(agents), null);
|
|
259
|
+
const metroWrote = isRecord(state) && state.modelsByMetro === true;
|
|
260
|
+
return mergeSettings(dir, (current) => {
|
|
261
|
+
mkdirSync(agents, { recursive: true });
|
|
262
|
+
writeJson(statePath(agents), { ...(isRecord(state) ? state : {}), modelsByMetro: route !== null });
|
|
263
|
+
return withAvailableModels(current, route, metroWrote);
|
|
264
|
+
});
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
export function syncAvailableModelsQuietly(deps: SetupDeps = {}, cfg?: ModelConfig): void {
|
|
268
|
+
try {
|
|
269
|
+
syncAvailableModels(cfg ?? readModelConfig(deps.agents ?? agentsDir()), deps);
|
|
270
|
+
} catch (err) {
|
|
271
|
+
log.warn({ err: errMsg(err) }, 'claude-setup: could not sync the model allowlist');
|
|
272
|
+
}
|
|
273
|
+
}
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
import { existsSync, readdirNames, readFileSync, statSync } from '../agent-user/agent-fs.js';
|
|
2
|
+
import { basename, dirname, join } from 'node:path';
|
|
3
|
+
import { ApiError } from '@metro-labs/http/api-error';
|
|
4
|
+
import { claudeDir } from './files.js';
|
|
5
|
+
import { removeHome, writeHomeText } from '../agent-user/home-fs.js';
|
|
6
|
+
|
|
7
|
+
const SKILL_MAX = 256 * 1024;
|
|
8
|
+
const SKILL_NAME_RE = /^[a-z0-9][a-z0-9-]{0,63}$/;
|
|
9
|
+
const FILE = 'SKILL.md';
|
|
10
|
+
const DEFAULT_MODE = 0o644;
|
|
11
|
+
const SUMMARY_MAX = 300;
|
|
12
|
+
|
|
13
|
+
export interface ClaudeSkill {
|
|
14
|
+
id: string;
|
|
15
|
+
name: string;
|
|
16
|
+
title: string;
|
|
17
|
+
description: string;
|
|
18
|
+
path: string;
|
|
19
|
+
editable: boolean;
|
|
20
|
+
updatedAt: string | null;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const frontmatterField = (text: string, field: string): string => {
|
|
24
|
+
const head = text.startsWith('---') ? text.slice(3, text.indexOf('\n---', 3)) : '';
|
|
25
|
+
for (const line of head.split('\n')) {
|
|
26
|
+
const at = line.indexOf(':');
|
|
27
|
+
if (at > 0 && line.slice(0, at).trim() === field) return line.slice(at + 1).trim().replace(/^["']|["']$/g, '').slice(0, SUMMARY_MAX);
|
|
28
|
+
}
|
|
29
|
+
return '';
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
function entryOf(name: string, path: string): ClaudeSkill {
|
|
33
|
+
const stat = statSync(path);
|
|
34
|
+
const editable = stat.size <= SKILL_MAX;
|
|
35
|
+
const text = editable ? readFileSync(path, 'utf8') : '';
|
|
36
|
+
return {
|
|
37
|
+
id: `user:${name}`,
|
|
38
|
+
name,
|
|
39
|
+
title: frontmatterField(text, 'name') || name,
|
|
40
|
+
description: frontmatterField(text, 'description'),
|
|
41
|
+
path,
|
|
42
|
+
editable,
|
|
43
|
+
updatedAt: stat.mtime.toISOString(),
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const userSkillsRoot = (dir: string): string => join(dir, 'skills');
|
|
48
|
+
|
|
49
|
+
function isFolder(path: string): boolean {
|
|
50
|
+
try {
|
|
51
|
+
return statSync(path).isDirectory();
|
|
52
|
+
} catch {
|
|
53
|
+
return false;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export function listClaudeSkills(dir = claudeDir()): ClaudeSkill[] {
|
|
58
|
+
const root = userSkillsRoot(dir);
|
|
59
|
+
if (!existsSync(root)) return [];
|
|
60
|
+
const out: ClaudeSkill[] = [];
|
|
61
|
+
for (const name of readdirNames(root)) {
|
|
62
|
+
if (!SKILL_NAME_RE.test(name) || !isFolder(join(root, name))) continue;
|
|
63
|
+
const path = join(root, name, FILE);
|
|
64
|
+
if (existsSync(path)) out.push(entryOf(name, path));
|
|
65
|
+
}
|
|
66
|
+
return out.sort((a, b) => (b.updatedAt ?? '').localeCompare(a.updatedAt ?? '') || a.name.localeCompare(b.name));
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function found(id: string, dir: string): ClaudeSkill {
|
|
70
|
+
const skill = listClaudeSkills(dir).find((entry) => entry.id === id);
|
|
71
|
+
if (skill === undefined) throw new ApiError('no such skill', 404);
|
|
72
|
+
return skill;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export function readClaudeSkill(id: string, dir = claudeDir()): ClaudeSkill & { text: string } {
|
|
76
|
+
const skill = found(id, dir);
|
|
77
|
+
return { ...skill, text: skill.editable ? readFileSync(skill.path, 'utf8') : '' };
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
function assertText(text: string): void {
|
|
81
|
+
if (text.length > SKILL_MAX) throw new ApiError('that is more text than a skill may hold', 413);
|
|
82
|
+
if (text.trim() === '') throw new ApiError('a skill needs some text', 400);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
export function writeClaudeSkill(
|
|
86
|
+
id: string,
|
|
87
|
+
text: string,
|
|
88
|
+
seenAt: string | null | undefined,
|
|
89
|
+
dir = claudeDir(),
|
|
90
|
+
): ClaudeSkill {
|
|
91
|
+
const skill = found(id, dir);
|
|
92
|
+
if (!skill.editable) throw new ApiError('that skill is too large to edit here', 409);
|
|
93
|
+
assertText(text);
|
|
94
|
+
if (seenAt !== undefined && seenAt !== skill.updatedAt)
|
|
95
|
+
throw new ApiError('that skill changed on disk since you opened it; reload it before saving', 409);
|
|
96
|
+
writeHomeText(skill.path, text, DEFAULT_MODE);
|
|
97
|
+
return entryOf(skill.name, skill.path);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
const skillTemplate = (name: string): string =>
|
|
101
|
+
['---', `name: ${name}`, 'description: what this skill does, and when Claude should reach for it', '---', '', `# ${name}`, '', 'Write the instructions here.', ''].join('\n');
|
|
102
|
+
|
|
103
|
+
export function createClaudeSkill(name: string, text: string | undefined, dir = claudeDir()): ClaudeSkill {
|
|
104
|
+
if (typeof name !== 'string' || !SKILL_NAME_RE.test(name))
|
|
105
|
+
throw new ApiError('a skill name is lowercase letters, digits and dashes, up to 64 characters', 400);
|
|
106
|
+
const path = join(userSkillsRoot(dir), name, FILE);
|
|
107
|
+
if (existsSync(path)) throw new ApiError('a skill by that name already lives there', 409);
|
|
108
|
+
const body = text === undefined || text.trim() === '' ? skillTemplate(name) : text;
|
|
109
|
+
assertText(body);
|
|
110
|
+
writeHomeText(path, body, DEFAULT_MODE);
|
|
111
|
+
return entryOf(name, path);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
export function deleteClaudeSkill(id: string, dir = claudeDir()): string {
|
|
115
|
+
const skill = found(id, dir);
|
|
116
|
+
const folder = dirname(skill.path);
|
|
117
|
+
if (basename(folder) !== skill.name) throw new ApiError('that skill does not live in its own folder', 409);
|
|
118
|
+
removeHome(folder, true);
|
|
119
|
+
return id;
|
|
120
|
+
}
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import { spawnSync } from 'node:child_process';
|
|
2
|
+
import { homedir } from 'node:os';
|
|
3
|
+
import { join } from 'node:path';
|
|
4
|
+
import { ApiError } from '@metro-labs/http/api-error';
|
|
5
|
+
import { errMsg, log } from '@metro-labs/core/log';
|
|
6
|
+
import { isRecord } from '@metro-labs/core/is-record';
|
|
7
|
+
import { sessionRunning, stopSession, type SessionDeps } from './session.js';
|
|
8
|
+
import { agentUser, asAgent, claudeBin } from '../agent-user/user.js';
|
|
9
|
+
|
|
10
|
+
const LATEST_URL = 'https://registry.npmjs.org/@anthropic-ai/claude-code/latest';
|
|
11
|
+
const VERSION_RE = /(\d+)\.(\d+)\.(\d+)/;
|
|
12
|
+
const CACHE_MS = 10 * 60_000;
|
|
13
|
+
const INSTALL_TIMEOUT_MS = 5 * 60_000;
|
|
14
|
+
const TAIL = 400;
|
|
15
|
+
|
|
16
|
+
export interface VersionDeps {
|
|
17
|
+
claude?: string;
|
|
18
|
+
fetchImpl?: typeof fetch;
|
|
19
|
+
session?: SessionDeps;
|
|
20
|
+
now?: () => number;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export interface ClaudeVersion {
|
|
24
|
+
installed: string | null;
|
|
25
|
+
latest: string | null;
|
|
26
|
+
newer: boolean;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
let cached: { latest: string | null; at: number } | null = null;
|
|
30
|
+
|
|
31
|
+
function candidates(deps: VersionDeps): string[] {
|
|
32
|
+
if (deps.claude !== undefined) return [deps.claude];
|
|
33
|
+
return agentUser() === null ? ['claude', join(homedir(), '.local', 'bin', 'claude')] : [claudeBin()];
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const parse = (text: string): string | null => {
|
|
37
|
+
const m = VERSION_RE.exec(text);
|
|
38
|
+
return m === null ? null : `${m[1] ?? ''}.${m[2] ?? ''}.${m[3] ?? ''}`;
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
function probe(bin: string): string | null {
|
|
42
|
+
const run = spawnSync(...asAgent(bin, ['--version']), { encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'] });
|
|
43
|
+
return run.error === undefined && run.status === 0 && typeof run.stdout === 'string' ? parse(run.stdout) : null;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function located(deps: VersionDeps): { bin: string; installed: string } | null {
|
|
47
|
+
for (const bin of candidates(deps)) {
|
|
48
|
+
const installed = probe(bin);
|
|
49
|
+
if (installed !== null) return { bin, installed };
|
|
50
|
+
}
|
|
51
|
+
return null;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export function newerThan(a: string, b: string): boolean {
|
|
55
|
+
const left = a.split('.').map(Number);
|
|
56
|
+
const right = b.split('.').map(Number);
|
|
57
|
+
for (let i = 0; i < 3; i += 1) {
|
|
58
|
+
const d = (left[i] ?? 0) - (right[i] ?? 0);
|
|
59
|
+
if (d !== 0) return d > 0;
|
|
60
|
+
}
|
|
61
|
+
return false;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
async function latestClaudeVersion(deps: VersionDeps = {}): Promise<string | null> {
|
|
65
|
+
const now = (deps.now ?? Date.now)();
|
|
66
|
+
if (cached !== null && now - cached.at < CACHE_MS) return cached.latest;
|
|
67
|
+
const fetchImpl = deps.fetchImpl ?? fetch;
|
|
68
|
+
let latest: string | null = null;
|
|
69
|
+
try {
|
|
70
|
+
const res = await fetchImpl(LATEST_URL, { headers: { accept: 'application/json' }, redirect: 'manual' });
|
|
71
|
+
const body: unknown = res.ok ? await res.json() : null;
|
|
72
|
+
latest = isRecord(body) && typeof body.version === 'string' ? parse(body.version) : null;
|
|
73
|
+
} catch (err) {
|
|
74
|
+
log.warn({ err: errMsg(err) }, 'claude-version: could not read the latest version from npm');
|
|
75
|
+
}
|
|
76
|
+
cached = { latest, at: now };
|
|
77
|
+
return latest;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export function forgetClaudeVersion(): void {
|
|
81
|
+
cached = null;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
export async function claudeVersion(deps: VersionDeps = {}): Promise<ClaudeVersion> {
|
|
85
|
+
const installed = located(deps)?.installed ?? null;
|
|
86
|
+
const latest = await latestClaudeVersion(deps);
|
|
87
|
+
return { installed, latest, newer: installed !== null && latest !== null && newerThan(latest, installed) };
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
function install(bin: string): void {
|
|
91
|
+
const run = spawnSync(...asAgent(bin, ['install', 'latest']), { encoding: 'utf8', stdio: ['ignore', 'pipe', 'pipe'], timeout: INSTALL_TIMEOUT_MS });
|
|
92
|
+
if (run.error !== undefined) throw new ApiError(`claude install latest failed: ${errMsg(run.error)}`, 502);
|
|
93
|
+
if (run.status === 0) return;
|
|
94
|
+
const said = `${run.stderr ?? ''}${run.stdout ?? ''}`.trim().slice(-TAIL);
|
|
95
|
+
throw new ApiError(`claude install latest failed: ${said}`, 502);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
function restartIfChanged(changed: boolean, session: SessionDeps): boolean {
|
|
99
|
+
if (!changed || !sessionRunning(session.tmux ?? 'tmux')) return false;
|
|
100
|
+
stopSession(session);
|
|
101
|
+
return true;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
export async function updateClaude(deps: VersionDeps = {}): Promise<ClaudeVersion & { restarted: boolean }> {
|
|
105
|
+
const found = located(deps);
|
|
106
|
+
if (found === null) throw new ApiError('Claude Code is not installed on this machine', 404);
|
|
107
|
+
install(found.bin);
|
|
108
|
+
forgetClaudeVersion();
|
|
109
|
+
const after = await claudeVersion(deps);
|
|
110
|
+
const restarted = restartIfChanged(after.installed !== null && after.installed !== found.installed, deps.session ?? {});
|
|
111
|
+
log.info({ from: found.installed, to: after.installed, restarted }, 'claude-version: Claude Code updated');
|
|
112
|
+
return { ...after, restarted };
|
|
113
|
+
}
|