agent-relay 1.6.0 → 2.0.0
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/.cursor/mcp.json +11 -0
- package/.gitleaks.toml +26 -0
- package/.mcp.json +11 -0
- package/.nvmrc +1 -1
- package/.turbo/cache/013415461711937f-meta.json +1 -0
- package/.turbo/cache/013415461711937f.tar.zst +0 -0
- package/.turbo/cache/0562b1ff326acd6d-meta.json +1 -0
- package/.turbo/cache/0562b1ff326acd6d.tar.zst +0 -0
- package/.turbo/cache/0b46e0e17254882f-meta.json +1 -0
- package/.turbo/cache/0b46e0e17254882f.tar.zst +0 -0
- package/.turbo/cache/3799eda981d53d14-meta.json +1 -0
- package/.turbo/cache/3799eda981d53d14.tar.zst +0 -0
- package/.turbo/cache/47e9d8f404ed064d-meta.json +1 -0
- package/.turbo/cache/47e9d8f404ed064d.tar.zst +0 -0
- package/.turbo/cache/4cde1d1e5b298099-meta.json +1 -0
- package/.turbo/cache/4cde1d1e5b298099.tar.zst +0 -0
- package/.turbo/cache/538eea955c0936ef-meta.json +1 -0
- package/.turbo/cache/538eea955c0936ef.tar.zst +0 -0
- package/.turbo/cache/5dceac7f229f5d5d-meta.json +1 -0
- package/.turbo/cache/5dceac7f229f5d5d.tar.zst +0 -0
- package/.turbo/cache/64c15b201819367d-meta.json +1 -0
- package/.turbo/cache/64c15b201819367d.tar.zst +0 -0
- package/.turbo/cache/6d6a21a05efca434-meta.json +1 -0
- package/.turbo/cache/6d6a21a05efca434.tar.zst +0 -0
- package/.turbo/cache/7562610cb03ec040-meta.json +1 -0
- package/.turbo/cache/7562610cb03ec040.tar.zst +0 -0
- package/.turbo/cache/81a2456e17af4d7f-meta.json +1 -0
- package/.turbo/cache/81a2456e17af4d7f.tar.zst +0 -0
- package/.turbo/cache/823fc2a7b12f724c-meta.json +1 -0
- package/.turbo/cache/823fc2a7b12f724c.tar.zst +0 -0
- package/.turbo/cache/9daad16a073d1f91-meta.json +1 -0
- package/.turbo/cache/9daad16a073d1f91.tar.zst +0 -0
- package/.turbo/cache/b81ccbab0a606b60-meta.json +1 -0
- package/.turbo/cache/b81ccbab0a606b60.tar.zst +0 -0
- package/.turbo/cache/cf98487988bfcf91-meta.json +1 -0
- package/.turbo/cache/cf98487988bfcf91.tar.zst +0 -0
- package/.turbo/cache/cfdf7c57dca71f27-meta.json +1 -0
- package/.turbo/cache/cfdf7c57dca71f27.tar.zst +0 -0
- package/.turbo/cache/d3063ef43811b1e5-meta.json +1 -0
- package/.turbo/cache/d3063ef43811b1e5.tar.zst +0 -0
- package/.turbo/cache/de28892eb7678e65-meta.json +1 -0
- package/.turbo/cache/de28892eb7678e65.tar.zst +0 -0
- package/.turbo/cache/ec29adce408132ba-meta.json +1 -0
- package/.turbo/cache/ec29adce408132ba.tar.zst +0 -0
- package/.turbo/cache/f70450d8d305f172-meta.json +1 -0
- package/.turbo/cache/f70450d8d305f172.tar.zst +0 -0
- package/.turbo/cache/fe384d5d6b7a983a-meta.json +1 -0
- package/.turbo/cache/fe384d5d6b7a983a.tar.zst +0 -0
- package/ARCHITECTURE.md +10 -10
- package/CHANGELOG.md +38 -0
- package/LICENSE +185 -17
- package/README.md +43 -5
- package/SESSION_HANDOFF.md +67 -0
- package/bin/relay-pty +0 -0
- package/bin/relay-pty-darwin-arm64 +0 -0
- package/bin/relay-pty-darwin-x64 +0 -0
- package/bin/relay-pty-linux-x64 +0 -0
- package/deploy/workspace/entrypoint.sh +79 -11
- package/deploy/workspace/git-credential-relay +152 -27
- package/deploy/workspace/git-credential-relay.test.sh +230 -0
- package/dist/dashboard/out/404.html +1 -1
- package/dist/dashboard/out/_next/static/chunks/116-a883fca163f3a5bc.js +1 -0
- package/dist/dashboard/out/_next/static/chunks/320-900169c942e31422.js +1 -0
- package/dist/dashboard/out/_next/static/chunks/631-af51bad94027527a.js +1 -0
- package/dist/dashboard/out/_next/static/chunks/766-2aea80818f7eb0d8.js +1 -0
- package/dist/dashboard/out/_next/static/chunks/891-5cb1513eeb97a891.js +1 -0
- package/dist/dashboard/out/_next/static/chunks/app/app/page-2e525b1dcc790967.js +1 -0
- package/dist/dashboard/out/_next/static/chunks/app/page-4e64923d73c35bc9.js +1 -0
- package/dist/dashboard/out/_next/static/chunks/app/providers/page-e65a0010da6ea5be.js +1 -0
- package/dist/dashboard/out/_next/static/chunks/app/providers/setup/[provider]/page-84161c802b020a1f.js +1 -0
- package/dist/dashboard/out/_next/static/css/99c2552394077586.css +1 -0
- package/dist/dashboard/out/app/onboarding.html +1 -1
- package/dist/dashboard/out/app/onboarding.txt +1 -1
- package/dist/dashboard/out/app.html +1 -1
- package/dist/dashboard/out/app.txt +2 -2
- package/dist/dashboard/out/cloud/link.html +1 -1
- package/dist/dashboard/out/cloud/link.txt +2 -2
- package/dist/dashboard/out/connect-repos.html +1 -1
- package/dist/dashboard/out/connect-repos.txt +1 -1
- package/dist/dashboard/out/history.html +1 -1
- package/dist/dashboard/out/history.txt +2 -2
- package/dist/dashboard/out/index.html +1 -1
- package/dist/dashboard/out/index.txt +2 -2
- package/dist/dashboard/out/login.html +2 -2
- package/dist/dashboard/out/login.txt +1 -1
- package/dist/dashboard/out/metrics.html +1 -1
- package/dist/dashboard/out/metrics.txt +2 -2
- package/dist/dashboard/out/pricing.html +3 -3
- package/dist/dashboard/out/pricing.txt +2 -2
- package/dist/dashboard/out/providers/setup/claude.html +1 -1
- package/dist/dashboard/out/providers/setup/claude.txt +2 -2
- package/dist/dashboard/out/providers/setup/codex.html +1 -1
- package/dist/dashboard/out/providers/setup/codex.txt +2 -2
- package/dist/dashboard/out/providers/setup/cursor.html +1 -0
- package/dist/dashboard/out/providers/setup/cursor.txt +8 -0
- package/dist/dashboard/out/providers.html +1 -1
- package/dist/dashboard/out/providers.txt +2 -2
- package/dist/dashboard/out/signup.html +2 -2
- package/dist/dashboard/out/signup.txt +1 -1
- package/dist/src/bridge/index.d.ts +8 -0
- package/dist/src/bridge/index.js +8 -0
- package/dist/src/cli/index.js +3089 -0
- package/dist/src/cloud/index.d.ts +8 -0
- package/dist/src/cloud/index.js +8 -0
- package/dist/src/config/relay-config.d.ts +5 -0
- package/dist/src/config/relay-config.js +5 -0
- package/dist/src/continuity/index.d.ts +5 -0
- package/dist/src/continuity/index.js +5 -0
- package/dist/src/daemon/index.d.ts +8 -0
- package/dist/src/daemon/index.js +9 -0
- package/dist/src/dashboard-server/index.d.ts +8 -0
- package/dist/src/dashboard-server/index.js +8 -0
- package/dist/src/hooks/index.d.ts +10 -0
- package/dist/src/hooks/index.js +10 -0
- package/dist/src/index.d.ts +13 -0
- package/dist/src/index.js +16 -0
- package/dist/src/memory/index.d.ts +5 -0
- package/dist/src/memory/index.js +5 -0
- package/dist/src/policy/index.d.ts +5 -0
- package/dist/src/policy/index.js +5 -0
- package/dist/src/protocol/index.d.ts +8 -0
- package/dist/src/protocol/index.js +8 -0
- package/dist/src/resiliency/index.d.ts +5 -0
- package/dist/src/resiliency/index.js +5 -0
- package/dist/src/shared/cli-auth-config.d.ts +5 -0
- package/dist/src/shared/cli-auth-config.js +5 -0
- package/dist/src/state/index.d.ts +5 -0
- package/dist/src/state/index.js +5 -0
- package/dist/src/storage/index.d.ts +8 -0
- package/dist/src/storage/index.js +8 -0
- package/dist/src/trajectory/index.d.ts +5 -0
- package/dist/src/trajectory/index.js +5 -0
- package/dist/src/utils/index.d.ts +5 -0
- package/dist/src/utils/index.js +5 -0
- package/dist/src/wrapper/index.d.ts +8 -0
- package/dist/src/wrapper/index.js +11 -0
- package/package.json +64 -19
- package/packages/api-types/dist/index.d.ts +21 -0
- package/packages/api-types/dist/index.js +22 -0
- package/packages/api-types/dist/schemas/agent.d.ts +259 -0
- package/packages/api-types/dist/schemas/agent.js +102 -0
- package/packages/api-types/dist/schemas/api.d.ts +290 -0
- package/packages/api-types/dist/schemas/api.js +162 -0
- package/packages/api-types/dist/schemas/decision.d.ts +230 -0
- package/packages/api-types/dist/schemas/decision.js +104 -0
- package/packages/api-types/dist/schemas/fleet.d.ts +615 -0
- package/packages/api-types/dist/schemas/fleet.js +71 -0
- package/packages/api-types/dist/schemas/history.d.ts +180 -0
- package/packages/api-types/dist/schemas/history.js +72 -0
- package/packages/api-types/dist/schemas/index.d.ts +14 -0
- package/packages/api-types/dist/schemas/index.js +22 -0
- package/packages/api-types/dist/schemas/message.d.ts +456 -0
- package/packages/api-types/dist/schemas/message.js +88 -0
- package/packages/api-types/dist/schemas/session.d.ts +60 -0
- package/packages/api-types/dist/schemas/session.js +36 -0
- package/packages/api-types/dist/schemas/task.d.ts +111 -0
- package/packages/api-types/dist/schemas/task.js +64 -0
- package/packages/api-types/package.json +61 -0
- package/packages/api-types/scripts/generate-openapi.ts +106 -0
- package/packages/bridge/dist/index.d.ts +8 -0
- package/packages/bridge/dist/index.js +9 -0
- package/packages/bridge/dist/multi-project-client.d.ts +99 -0
- package/packages/bridge/dist/multi-project-client.js +389 -0
- package/packages/bridge/dist/shadow-cli.js +75 -0
- package/packages/bridge/dist/spawner.d.ts +210 -0
- package/packages/bridge/dist/spawner.js +1276 -0
- package/packages/bridge/dist/types.d.ts +131 -0
- package/packages/bridge/dist/utils.d.ts +15 -0
- package/packages/bridge/dist/utils.js +60 -0
- package/packages/bridge/package.json +40 -0
- package/packages/cloud/dist/api/admin.js +225 -0
- package/packages/cloud/dist/api/billing.js +564 -0
- package/packages/cloud/dist/api/cli-pty-runner.d.ts +53 -0
- package/packages/cloud/dist/api/cli-pty-runner.js +193 -0
- package/packages/cloud/dist/api/codex-auth-helper.js +327 -0
- package/packages/cloud/dist/api/consensus.js +261 -0
- package/packages/cloud/dist/api/coordinators.js +750 -0
- package/packages/cloud/dist/api/daemons.js +535 -0
- package/packages/cloud/dist/api/generic-webhooks.js +129 -0
- package/packages/cloud/dist/api/github-app.js +223 -0
- package/packages/cloud/dist/api/monitoring.js +578 -0
- package/packages/cloud/dist/api/nango-auth.js +674 -0
- package/packages/cloud/dist/api/onboarding.d.ts +15 -0
- package/packages/cloud/dist/api/onboarding.js +679 -0
- package/packages/cloud/dist/api/policy.js +229 -0
- package/packages/cloud/dist/api/provider-env.d.ts +14 -0
- package/packages/cloud/dist/api/provider-env.js +75 -0
- package/packages/cloud/dist/api/providers.js +564 -0
- package/packages/cloud/dist/api/repos.js +577 -0
- package/packages/cloud/dist/api/sessions.d.ts +11 -0
- package/packages/cloud/dist/api/sessions.js +302 -0
- package/packages/cloud/dist/api/teams.js +281 -0
- package/packages/cloud/dist/api/test-helpers.js +745 -0
- package/packages/cloud/dist/api/workspaces.js +1799 -0
- package/packages/cloud/dist/billing/plans.js +245 -0
- package/packages/cloud/dist/config.d.ts +5 -0
- package/packages/cloud/dist/config.js +5 -0
- package/packages/cloud/dist/db/drizzle.d.ts +256 -0
- package/packages/cloud/dist/db/drizzle.js +1286 -0
- package/packages/cloud/dist/db/schema.d.ts +4873 -0
- package/packages/cloud/dist/db/schema.js +620 -0
- package/packages/cloud/dist/index.d.ts +11 -0
- package/packages/cloud/dist/index.js +38 -0
- package/packages/cloud/dist/provisioner/index.d.ts +207 -0
- package/packages/cloud/dist/provisioner/index.js +2114 -0
- package/packages/cloud/dist/server.js +1924 -0
- package/packages/cloud/dist/services/index.d.ts +17 -0
- package/packages/cloud/dist/services/index.js +25 -0
- package/packages/cloud/dist/services/intro-expiration.d.ts +60 -0
- package/packages/cloud/dist/services/intro-expiration.js +252 -0
- package/packages/cloud/dist/services/nango.d.ts +201 -0
- package/packages/cloud/dist/services/nango.js +392 -0
- package/packages/cloud/dist/services/persistence.d.ts +131 -0
- package/packages/cloud/dist/shims/consensus.d.ts +23 -0
- package/packages/cloud/dist/shims/consensus.js +5 -0
- package/packages/cloud/package.json +55 -0
- package/packages/config/dist/bridge-config.d.ts +52 -0
- package/packages/config/dist/bridge-config.js +143 -0
- package/packages/config/dist/bridge-utils.d.ts +30 -0
- package/packages/config/dist/bridge-utils.js +54 -0
- package/packages/config/dist/cli-auth-config.js +391 -0
- package/packages/config/dist/cloud-config.d.ts +75 -0
- package/packages/config/dist/cloud-config.js +109 -0
- package/packages/config/dist/index.d.ts +13 -0
- package/packages/config/dist/index.js +13 -0
- package/packages/config/dist/project-namespace.d.ts +73 -0
- package/packages/config/dist/project-namespace.js +280 -0
- package/packages/config/dist/relay-config.d.ts +25 -0
- package/packages/config/dist/relay-config.js +25 -0
- package/packages/config/dist/relay-file-writer.d.ts +200 -0
- package/packages/config/dist/relay-file-writer.js +407 -0
- package/packages/config/dist/schemas.d.ts +672 -0
- package/packages/config/dist/schemas.js +180 -0
- package/packages/config/dist/shadow-config.d.ts +87 -0
- package/packages/config/dist/trajectory-config.d.ts +102 -0
- package/packages/config/dist/trajectory-config.js +185 -0
- package/packages/config/package.json +98 -0
- package/packages/continuity/dist/index.d.ts +9 -0
- package/packages/continuity/dist/index.js +9 -0
- package/packages/continuity/dist/types.d.ts +180 -0
- package/packages/continuity/dist/types.js +2 -0
- package/packages/continuity/package.json +32 -0
- package/packages/daemon/dist/agent-manager.d.ts +134 -0
- package/packages/daemon/dist/agent-manager.js +578 -0
- package/packages/daemon/dist/agent-registry.js +213 -0
- package/packages/daemon/dist/api.d.ts +106 -0
- package/packages/daemon/dist/api.js +876 -0
- package/packages/daemon/dist/channel-membership-store.d.ts +55 -0
- package/packages/daemon/dist/channel-membership-store.js +176 -0
- package/packages/daemon/dist/cli-auth.d.ts +89 -0
- package/packages/daemon/dist/cli-auth.js +792 -0
- package/packages/daemon/dist/cloud-sync.d.ts +150 -0
- package/packages/daemon/dist/cloud-sync.js +446 -0
- package/packages/daemon/dist/connection.d.ts +130 -0
- package/packages/daemon/dist/connection.js +438 -0
- package/packages/daemon/dist/consensus-integration.js +371 -0
- package/packages/daemon/dist/delivery-tracker.d.ts +34 -0
- package/packages/daemon/dist/delivery-tracker.js +104 -0
- package/packages/daemon/dist/enhanced-features.d.ts +118 -0
- package/packages/daemon/dist/enhanced-features.js +176 -0
- package/packages/daemon/dist/index.d.ts +31 -0
- package/packages/daemon/dist/index.js +37 -0
- package/packages/daemon/dist/migrations/index.d.ts +73 -0
- package/packages/daemon/dist/migrations/index.js +241 -0
- package/packages/daemon/dist/orchestrator.d.ts +217 -0
- package/packages/daemon/dist/orchestrator.js +1143 -0
- package/packages/daemon/dist/relay-ledger.d.ts +261 -0
- package/packages/daemon/dist/relay-ledger.js +532 -0
- package/packages/daemon/dist/relay-watchdog.d.ts +125 -0
- package/packages/daemon/dist/relay-watchdog.js +611 -0
- package/packages/daemon/dist/repo-manager.js +384 -0
- package/packages/daemon/dist/router.d.ts +370 -0
- package/packages/daemon/dist/router.js +1437 -0
- package/packages/daemon/dist/server.d.ts +174 -0
- package/packages/daemon/dist/server.js +1001 -0
- package/packages/daemon/dist/spawn-manager.d.ts +78 -0
- package/packages/daemon/dist/spawn-manager.js +165 -0
- package/packages/daemon/dist/sync-queue.d.ts +116 -0
- package/packages/daemon/dist/sync-queue.js +361 -0
- package/packages/daemon/dist/types.d.ts +133 -0
- package/packages/daemon/dist/workspace-manager.js +314 -0
- package/packages/daemon/package.json +52 -0
- package/packages/dashboard/README.md +48 -0
- package/packages/dashboard/dist/health-worker-manager.d.ts +62 -0
- package/packages/dashboard/dist/health-worker-manager.js +144 -0
- package/packages/dashboard/dist/health-worker.d.ts +9 -0
- package/packages/dashboard/dist/health-worker.js +79 -0
- package/packages/dashboard/dist/index.d.ts +20 -0
- package/packages/dashboard/dist/index.js +19 -0
- package/packages/dashboard/dist/metrics.d.ts +105 -0
- package/packages/dashboard/dist/metrics.js +193 -0
- package/packages/dashboard/dist/needs-attention.d.ts +24 -0
- package/packages/dashboard/dist/needs-attention.js +78 -0
- package/packages/dashboard/dist/server.d.ts +25 -0
- package/packages/dashboard/dist/server.js +5107 -0
- package/packages/dashboard/dist/start.d.ts +6 -0
- package/packages/dashboard/dist/start.js +13 -0
- package/packages/dashboard/dist/types/threading.d.ts +8 -0
- package/packages/dashboard/dist/types/threading.js +2 -0
- package/packages/dashboard/dist/user-bridge.d.ts +154 -0
- package/packages/dashboard/dist/user-bridge.js +372 -0
- package/packages/dashboard/package.json +72 -0
- package/packages/dashboard/ui/.next/BUILD_ID +1 -0
- package/packages/dashboard/ui/.next/app-build-manifest.json +135 -0
- package/packages/dashboard/ui/.next/app-path-routes-manifest.json +1 -0
- package/packages/dashboard/ui/.next/build-manifest.json +32 -0
- package/packages/dashboard/ui/.next/cache/config.json +7 -0
- package/packages/dashboard/ui/.next/cache/eslint/.cache_1asv1h5 +1 -0
- package/packages/dashboard/ui/.next/cache/webpack/client-production/0.pack +0 -0
- package/packages/dashboard/ui/.next/cache/webpack/client-production/index.pack +0 -0
- package/packages/dashboard/ui/.next/cache/webpack/edge-server-production/0.pack +0 -0
- package/packages/dashboard/ui/.next/cache/webpack/edge-server-production/index.pack +0 -0
- package/packages/dashboard/ui/.next/cache/webpack/server-production/0.pack +0 -0
- package/packages/dashboard/ui/.next/cache/webpack/server-production/index.pack +0 -0
- package/packages/dashboard/ui/.next/export-detail.json +1 -0
- package/packages/dashboard/ui/.next/export-marker.json +1 -0
- package/packages/dashboard/ui/.next/images-manifest.json +1 -0
- package/packages/dashboard/ui/.next/next-minimal-server.js.nft.json +1 -0
- package/packages/dashboard/ui/.next/next-server.js.nft.json +1 -0
- package/packages/dashboard/ui/.next/package.json +1 -0
- package/packages/dashboard/ui/.next/prerender-manifest.json +1 -0
- package/packages/dashboard/ui/.next/react-loadable-manifest.json +1970 -0
- package/packages/dashboard/ui/.next/required-server-files.json +1 -0
- package/packages/dashboard/ui/.next/routes-manifest.json +1 -0
- package/packages/dashboard/ui/.next/server/app/_not-found/page.js +1 -0
- package/packages/dashboard/ui/.next/server/app/_not-found/page.js.nft.json +1 -0
- package/packages/dashboard/ui/.next/server/app/_not-found/page_client-reference-manifest.js +1 -0
- package/packages/dashboard/ui/.next/server/app/_not-found.html +1 -0
- package/packages/dashboard/ui/.next/server/app/_not-found.meta +6 -0
- package/packages/dashboard/ui/.next/server/app/_not-found.rsc +9 -0
- package/packages/dashboard/ui/.next/server/app/app/onboarding/page.js +6 -0
- package/packages/dashboard/ui/.next/server/app/app/onboarding/page.js.nft.json +1 -0
- package/packages/dashboard/ui/.next/server/app/app/onboarding/page_client-reference-manifest.js +1 -0
- package/packages/dashboard/ui/.next/server/app/app/onboarding.html +1 -0
- package/packages/dashboard/ui/.next/server/app/app/onboarding.meta +5 -0
- package/packages/dashboard/ui/.next/server/app/app/onboarding.rsc +7 -0
- package/packages/dashboard/ui/.next/server/app/app/page.js +436 -0
- package/packages/dashboard/ui/.next/server/app/app/page.js.nft.json +1 -0
- package/packages/dashboard/ui/.next/server/app/app/page_client-reference-manifest.js +1 -0
- package/packages/dashboard/ui/.next/server/app/app.html +1 -0
- package/packages/dashboard/ui/.next/server/app/app.meta +5 -0
- package/packages/dashboard/ui/.next/server/app/app.rsc +7 -0
- package/packages/dashboard/ui/.next/server/app/apple-icon.png/route.js +12 -0
- package/packages/dashboard/ui/.next/server/app/apple-icon.png/route.js.nft.json +1 -0
- package/packages/dashboard/ui/.next/server/app/apple-icon.png.body +0 -0
- package/packages/dashboard/ui/.next/server/app/apple-icon.png.meta +1 -0
- package/packages/dashboard/ui/.next/server/app/cloud/link/page.js +1 -0
- package/packages/dashboard/ui/.next/server/app/cloud/link/page.js.nft.json +1 -0
- package/packages/dashboard/ui/.next/server/app/cloud/link/page_client-reference-manifest.js +1 -0
- package/packages/dashboard/ui/.next/server/app/cloud/link.html +1 -0
- package/packages/dashboard/ui/.next/server/app/cloud/link.meta +5 -0
- package/packages/dashboard/ui/.next/server/app/cloud/link.rsc +7 -0
- package/packages/dashboard/ui/.next/server/app/connect-repos/page.js +6 -0
- package/packages/dashboard/ui/.next/server/app/connect-repos/page.js.nft.json +1 -0
- package/packages/dashboard/ui/.next/server/app/connect-repos/page_client-reference-manifest.js +1 -0
- package/packages/dashboard/ui/.next/server/app/connect-repos.html +1 -0
- package/packages/dashboard/ui/.next/server/app/connect-repos.meta +5 -0
- package/packages/dashboard/ui/.next/server/app/connect-repos.rsc +7 -0
- package/packages/dashboard/ui/.next/server/app/history/page.js +1 -0
- package/packages/dashboard/ui/.next/server/app/history/page.js.nft.json +1 -0
- package/packages/dashboard/ui/.next/server/app/history/page_client-reference-manifest.js +1 -0
- package/packages/dashboard/ui/.next/server/app/history.html +1 -0
- package/packages/dashboard/ui/.next/server/app/history.meta +5 -0
- package/packages/dashboard/ui/.next/server/app/history.rsc +7 -0
- package/packages/dashboard/ui/.next/server/app/index.html +1 -0
- package/packages/dashboard/ui/.next/server/app/index.meta +5 -0
- package/packages/dashboard/ui/.next/server/app/index.rsc +7 -0
- package/packages/dashboard/ui/.next/server/app/login/page.js +6 -0
- package/packages/dashboard/ui/.next/server/app/login/page.js.nft.json +1 -0
- package/packages/dashboard/ui/.next/server/app/login/page_client-reference-manifest.js +1 -0
- package/packages/dashboard/ui/.next/server/app/login.html +5 -0
- package/packages/dashboard/ui/.next/server/app/login.meta +5 -0
- package/packages/dashboard/ui/.next/server/app/login.rsc +7 -0
- package/packages/dashboard/ui/.next/server/app/metrics/page.js +1 -0
- package/packages/dashboard/ui/.next/server/app/metrics/page.js.nft.json +1 -0
- package/packages/dashboard/ui/.next/server/app/metrics/page_client-reference-manifest.js +1 -0
- package/packages/dashboard/ui/.next/server/app/metrics.html +1 -0
- package/packages/dashboard/ui/.next/server/app/metrics.meta +5 -0
- package/packages/dashboard/ui/.next/server/app/metrics.rsc +7 -0
- package/packages/dashboard/ui/.next/server/app/page.js +1 -0
- package/packages/dashboard/ui/.next/server/app/page.js.nft.json +1 -0
- package/packages/dashboard/ui/.next/server/app/page_client-reference-manifest.js +1 -0
- package/packages/dashboard/ui/.next/server/app/pricing/page.js +5 -0
- package/packages/dashboard/ui/.next/server/app/pricing/page.js.nft.json +1 -0
- package/packages/dashboard/ui/.next/server/app/pricing/page_client-reference-manifest.js +1 -0
- package/packages/dashboard/ui/.next/server/app/pricing.html +13 -0
- package/packages/dashboard/ui/.next/server/app/pricing.meta +5 -0
- package/packages/dashboard/ui/.next/server/app/pricing.rsc +7 -0
- package/packages/dashboard/ui/.next/server/app/providers/page.js +2 -0
- package/packages/dashboard/ui/.next/server/app/providers/page.js.nft.json +1 -0
- package/packages/dashboard/ui/.next/server/app/providers/page_client-reference-manifest.js +1 -0
- package/packages/dashboard/ui/.next/server/app/providers/setup/[provider]/page.js +1 -0
- package/packages/dashboard/ui/.next/server/app/providers/setup/[provider]/page.js.nft.json +1 -0
- package/packages/dashboard/ui/.next/server/app/providers/setup/[provider]/page_client-reference-manifest.js +1 -0
- package/packages/dashboard/ui/.next/server/app/providers/setup/claude.html +1 -0
- package/packages/dashboard/ui/.next/server/app/providers/setup/claude.meta +5 -0
- package/packages/dashboard/ui/.next/server/app/providers/setup/claude.rsc +8 -0
- package/packages/dashboard/ui/.next/server/app/providers/setup/codex.html +1 -0
- package/packages/dashboard/ui/.next/server/app/providers/setup/codex.meta +5 -0
- package/packages/dashboard/ui/.next/server/app/providers/setup/codex.rsc +8 -0
- package/packages/dashboard/ui/.next/server/app/providers/setup/cursor.html +1 -0
- package/packages/dashboard/ui/.next/server/app/providers/setup/cursor.meta +5 -0
- package/packages/dashboard/ui/.next/server/app/providers/setup/cursor.rsc +8 -0
- package/packages/dashboard/ui/.next/server/app/providers.html +1 -0
- package/packages/dashboard/ui/.next/server/app/providers.meta +5 -0
- package/packages/dashboard/ui/.next/server/app/providers.rsc +7 -0
- package/packages/dashboard/ui/.next/server/app/signup/page.js +6 -0
- package/packages/dashboard/ui/.next/server/app/signup/page.js.nft.json +1 -0
- package/packages/dashboard/ui/.next/server/app/signup/page_client-reference-manifest.js +1 -0
- package/packages/dashboard/ui/.next/server/app/signup.html +6 -0
- package/packages/dashboard/ui/.next/server/app/signup.meta +5 -0
- package/packages/dashboard/ui/.next/server/app/signup.rsc +7 -0
- package/packages/dashboard/ui/.next/server/app-paths-manifest.json +16 -0
- package/packages/dashboard/ui/.next/server/chunks/190.js +1 -0
- package/packages/dashboard/ui/.next/server/chunks/205.js +1 -0
- package/packages/dashboard/ui/.next/server/chunks/251.js +9 -0
- package/packages/dashboard/ui/.next/server/chunks/288.js +1 -0
- package/packages/dashboard/ui/.next/server/chunks/434.js +1 -0
- package/packages/dashboard/ui/.next/server/chunks/471.js +2 -0
- package/packages/dashboard/ui/.next/server/chunks/621.js +1 -0
- package/packages/dashboard/ui/.next/server/chunks/680.js +5 -0
- package/packages/dashboard/ui/.next/server/chunks/682.js +6 -0
- package/packages/dashboard/ui/.next/server/chunks/684.js +1 -0
- package/packages/dashboard/ui/.next/server/chunks/71.js +196 -0
- package/packages/dashboard/ui/.next/server/chunks/711.js +1 -0
- package/packages/dashboard/ui/.next/server/chunks/90.js +17 -0
- package/packages/dashboard/ui/.next/server/chunks/948.js +2 -0
- package/packages/dashboard/ui/.next/server/chunks/font-manifest.json +1 -0
- package/packages/dashboard/ui/.next/server/font-manifest.json +1 -0
- package/packages/dashboard/ui/.next/server/functions-config-manifest.json +1 -0
- package/packages/dashboard/ui/.next/server/interception-route-rewrite-manifest.js +1 -0
- package/packages/dashboard/ui/.next/server/middleware-build-manifest.js +1 -0
- package/packages/dashboard/ui/.next/server/middleware-manifest.json +6 -0
- package/packages/dashboard/ui/.next/server/middleware-react-loadable-manifest.js +1 -0
- package/packages/dashboard/ui/.next/server/next-font-manifest.js +1 -0
- package/packages/dashboard/ui/.next/server/next-font-manifest.json +1 -0
- package/packages/dashboard/ui/.next/server/pages/404.html +1 -0
- package/packages/dashboard/ui/.next/server/pages/500.html +1 -0
- package/packages/dashboard/ui/.next/server/pages/_app.js +1 -0
- package/packages/dashboard/ui/.next/server/pages/_app.js.nft.json +1 -0
- package/packages/dashboard/ui/.next/server/pages/_document.js +1 -0
- package/packages/dashboard/ui/.next/server/pages/_document.js.nft.json +1 -0
- package/packages/dashboard/ui/.next/server/pages/_error.js +1 -0
- package/packages/dashboard/ui/.next/server/pages/_error.js.nft.json +1 -0
- package/packages/dashboard/ui/.next/server/pages-manifest.json +1 -0
- package/packages/dashboard/ui/.next/server/server-reference-manifest.js +1 -0
- package/packages/dashboard/ui/.next/server/server-reference-manifest.json +1 -0
- package/packages/dashboard/ui/.next/server/webpack-runtime.js +1 -0
- package/packages/dashboard/ui/.next/static/HR7W9z1PPVPFqUboUVZFZ/_buildManifest.js +1 -0
- package/packages/dashboard/ui/.next/static/HR7W9z1PPVPFqUboUVZFZ/_ssgManifest.js +1 -0
- package/packages/dashboard/ui/.next/static/chunks/116-a883fca163f3a5bc.js +1 -0
- package/packages/dashboard/ui/.next/static/chunks/117-c8afed19e821a35d.js +2 -0
- package/packages/dashboard/ui/.next/static/chunks/282-980c2eb8fff20123.js +1 -0
- package/packages/dashboard/ui/.next/static/chunks/532-bace199897eeab37.js +9 -0
- package/packages/dashboard/ui/.next/static/chunks/631-af51bad94027527a.js +1 -0
- package/packages/dashboard/ui/.next/static/chunks/648-acb2ff9f77cbfbd3.js +1 -0
- package/packages/dashboard/ui/.next/static/chunks/677-30e60cb0b47875b6.js +1 -0
- package/packages/dashboard/ui/.next/static/chunks/766-2aea80818f7eb0d8.js +1 -0
- package/packages/dashboard/ui/.next/static/chunks/83-4f08122d4e7e79a6.js +1 -0
- package/packages/dashboard/ui/.next/static/chunks/847-f1f467060f32afff.js +1 -0
- package/packages/dashboard/ui/.next/static/chunks/891-5cb1513eeb97a891.js +1 -0
- package/packages/dashboard/ui/.next/static/chunks/app/_not-found/page-60501fddbafba9dc.js +1 -0
- package/packages/dashboard/ui/.next/static/chunks/app/app/onboarding/page-9914652442f7e4fb.js +1 -0
- package/packages/dashboard/ui/.next/static/chunks/app/app/page-44813aa26ad19681.js +1 -0
- package/packages/dashboard/ui/.next/static/chunks/app/cloud/link/page-fa1d5842aa90e8a6.js +1 -0
- package/packages/dashboard/ui/.next/static/chunks/app/connect-repos/page-113060009ef35bc2.js +1 -0
- package/packages/dashboard/ui/.next/static/chunks/app/history/page-9965d2483011b846.js +1 -0
- package/packages/dashboard/ui/.next/static/chunks/app/layout-6b91e33784c20610.js +1 -0
- package/packages/dashboard/ui/.next/static/chunks/app/login/page-a0ca6f7ca6a100b8.js +1 -0
- package/packages/dashboard/ui/.next/static/chunks/app/metrics/page-1e37ef8e73940b40.js +1 -0
- package/packages/dashboard/ui/.next/static/chunks/app/page-7993778218818ace.js +1 -0
- package/packages/dashboard/ui/.next/static/chunks/app/pricing/page-9db3ebdfa567a7c9.js +1 -0
- package/packages/dashboard/ui/.next/static/chunks/app/providers/page-bcf46064ac4474ce.js +1 -0
- package/packages/dashboard/ui/.next/static/chunks/app/providers/setup/[provider]/page-4dbe33f0f7691b7c.js +1 -0
- package/packages/dashboard/ui/.next/static/chunks/app/signup/page-1ede2205b58649ca.js +1 -0
- package/packages/dashboard/ui/.next/static/chunks/e868780c-48e5f147c90a3a41.js +18 -0
- package/packages/dashboard/ui/.next/static/chunks/fd9d1056-609918ca7b6280bb.js +1 -0
- package/packages/dashboard/ui/.next/static/chunks/framework-f66176bb897dc684.js +1 -0
- package/packages/dashboard/ui/.next/static/chunks/main-5a40a5ae29646e1b.js +1 -0
- package/packages/dashboard/ui/.next/static/chunks/main-app-fdbeb09028f57c9f.js +1 -0
- package/packages/dashboard/ui/.next/static/chunks/pages/_app-72b849fbd24ac258.js +1 -0
- package/packages/dashboard/ui/.next/static/chunks/pages/_error-7ba65e1336b92748.js +1 -0
- package/packages/dashboard/ui/.next/static/chunks/polyfills-42372ed130431b0a.js +1 -0
- package/packages/dashboard/ui/.next/static/chunks/webpack-1cdd8ed57114d5e1.js +1 -0
- package/packages/dashboard/ui/.next/static/css/4034f236dd1a3178.css +1 -0
- package/packages/dashboard/ui/.next/static/css/99c2552394077586.css +1 -0
- package/packages/dashboard/ui/.next/trace +63 -0
- package/packages/dashboard/ui/.next/types/app/app/onboarding/page.ts +79 -0
- package/packages/dashboard/ui/.next/types/app/app/page.ts +79 -0
- package/packages/dashboard/ui/.next/types/app/cloud/link/page.ts +79 -0
- package/packages/dashboard/ui/.next/types/app/connect-repos/page.ts +79 -0
- package/packages/dashboard/ui/.next/types/app/history/page.ts +79 -0
- package/packages/dashboard/ui/.next/types/app/layout.ts +79 -0
- package/packages/dashboard/ui/.next/types/app/login/page.ts +79 -0
- package/packages/dashboard/ui/.next/types/app/metrics/page.ts +79 -0
- package/packages/dashboard/ui/.next/types/app/page.ts +79 -0
- package/packages/dashboard/ui/.next/types/app/pricing/page.ts +79 -0
- package/packages/dashboard/ui/.next/types/app/providers/page.ts +79 -0
- package/packages/dashboard/ui/.next/types/app/providers/setup/[provider]/page.ts +79 -0
- package/packages/dashboard/ui/.next/types/app/signup/page.ts +79 -0
- package/packages/dashboard/ui/.next/types/package.json +1 -0
- package/packages/dashboard/ui/app/app/onboarding/page.tsx +394 -0
- package/packages/dashboard/ui/app/app/page.tsx +667 -0
- package/packages/dashboard/ui/app/apple-icon.png +0 -0
- package/packages/dashboard/ui/app/cloud/link/page.tsx +464 -0
- package/packages/dashboard/ui/app/connect-repos/page.tsx +410 -0
- package/packages/dashboard/ui/app/favicon.png +0 -0
- package/packages/dashboard/ui/app/globals.css +59 -0
- package/packages/dashboard/ui/app/history/page.tsx +658 -0
- package/packages/dashboard/ui/app/layout.tsx +25 -0
- package/packages/dashboard/ui/app/login/page.tsx +280 -0
- package/packages/dashboard/ui/app/metrics/page.tsx +751 -0
- package/packages/dashboard/ui/app/page.tsx +59 -0
- package/packages/dashboard/ui/app/pricing/page.tsx +7 -0
- package/packages/dashboard/ui/app/providers/page.tsx +193 -0
- package/packages/dashboard/ui/app/providers/setup/[provider]/ProviderSetupClient.tsx +148 -0
- package/packages/dashboard/ui/app/providers/setup/[provider]/constants.ts +35 -0
- package/packages/dashboard/ui/app/providers/setup/[provider]/page.tsx +42 -0
- package/packages/dashboard/ui/app/signup/page.tsx +343 -0
- package/packages/dashboard/ui/index.ts +49 -0
- package/packages/dashboard/ui/landing/LandingPage.tsx +713 -0
- package/packages/dashboard/ui/landing/PricingPage.tsx +559 -0
- package/packages/dashboard/ui/landing/index.ts +6 -0
- package/packages/dashboard/ui/landing/styles.css +2850 -0
- package/packages/dashboard/ui/lib/agent-merge.ts +35 -0
- package/packages/dashboard/ui/lib/api.ts +1155 -0
- package/packages/dashboard/ui/lib/cloudApi.ts +876 -0
- package/packages/dashboard/ui/lib/colors.ts +218 -0
- package/packages/dashboard/ui/lib/hierarchy.ts +242 -0
- package/packages/dashboard/ui/lib/stuckDetection.ts +142 -0
- package/packages/dashboard/ui/next-env.d.ts +5 -0
- package/packages/dashboard/ui/next.config.js +41 -0
- package/packages/dashboard/ui/package-lock.json +2882 -0
- package/packages/dashboard/ui/package.json +33 -0
- package/packages/dashboard/ui/postcss.config.js +5 -0
- package/packages/dashboard/ui/react-components/ActivityFeed.tsx +216 -0
- package/packages/dashboard/ui/react-components/AddWorkspaceModal.tsx +170 -0
- package/packages/dashboard/ui/react-components/AgentCard.tsx +587 -0
- package/packages/dashboard/ui/react-components/AgentList.tsx +411 -0
- package/packages/dashboard/ui/react-components/AgentProfilePanel.tsx +564 -0
- package/packages/dashboard/ui/react-components/App.tsx +3447 -0
- package/packages/dashboard/ui/react-components/BillingPanel.tsx +922 -0
- package/packages/dashboard/ui/react-components/BillingResult.tsx +447 -0
- package/packages/dashboard/ui/react-components/BroadcastComposer.tsx +690 -0
- package/packages/dashboard/ui/react-components/ChannelAdminPanel.tsx +773 -0
- package/packages/dashboard/ui/react-components/ChannelBrowser.tsx +385 -0
- package/packages/dashboard/ui/react-components/ChannelChat.tsx +307 -0
- package/packages/dashboard/ui/react-components/ChannelSidebar.tsx +399 -0
- package/packages/dashboard/ui/react-components/CloudSessionProvider.tsx +130 -0
- package/packages/dashboard/ui/react-components/CommandPalette.tsx +815 -0
- package/packages/dashboard/ui/react-components/ConfirmationDialog.tsx +133 -0
- package/packages/dashboard/ui/react-components/ConversationHistory.tsx +518 -0
- package/packages/dashboard/ui/react-components/CoordinatorPanel.tsx +944 -0
- package/packages/dashboard/ui/react-components/DecisionQueue.tsx +717 -0
- package/packages/dashboard/ui/react-components/DirectMessageView.tsx +164 -0
- package/packages/dashboard/ui/react-components/FileAutocomplete.tsx +368 -0
- package/packages/dashboard/ui/react-components/FleetOverview.tsx +278 -0
- package/packages/dashboard/ui/react-components/LogViewer.tsx +310 -0
- package/packages/dashboard/ui/react-components/LogViewerPanel.tsx +482 -0
- package/packages/dashboard/ui/react-components/Logo.tsx +284 -0
- package/packages/dashboard/ui/react-components/MentionAutocomplete.tsx +384 -0
- package/packages/dashboard/ui/react-components/MessageList.tsx +649 -0
- package/packages/dashboard/ui/react-components/MessageSenderName.tsx +91 -0
- package/packages/dashboard/ui/react-components/MessageStatusIndicator.tsx +142 -0
- package/packages/dashboard/ui/react-components/NewConversationModal.tsx +400 -0
- package/packages/dashboard/ui/react-components/NotificationToast.tsx +488 -0
- package/packages/dashboard/ui/react-components/OnlineUsersIndicator.tsx +164 -0
- package/packages/dashboard/ui/react-components/Pagination.tsx +124 -0
- package/packages/dashboard/ui/react-components/PricingPlans.tsx +386 -0
- package/packages/dashboard/ui/react-components/ProjectList.tsx +625 -0
- package/packages/dashboard/ui/react-components/ProviderAuthFlow.tsx +843 -0
- package/packages/dashboard/ui/react-components/ProviderConnectionList.tsx +363 -0
- package/packages/dashboard/ui/react-components/ProvisioningProgress.tsx +730 -0
- package/packages/dashboard/ui/react-components/RepoAccessPanel.tsx +392 -0
- package/packages/dashboard/ui/react-components/ServerCard.tsx +202 -0
- package/packages/dashboard/ui/react-components/SessionExpiredModal.tsx +128 -0
- package/packages/dashboard/ui/react-components/SpawnModal.tsx +704 -0
- package/packages/dashboard/ui/react-components/TaskAssignmentUI.tsx +375 -0
- package/packages/dashboard/ui/react-components/TerminalProviderSetup.tsx +608 -0
- package/packages/dashboard/ui/react-components/ThemeProvider.tsx +325 -0
- package/packages/dashboard/ui/react-components/ThinkingIndicator.tsx +231 -0
- package/packages/dashboard/ui/react-components/ThreadList.tsx +198 -0
- package/packages/dashboard/ui/react-components/ThreadPanel.tsx +346 -0
- package/packages/dashboard/ui/react-components/TrajectoryViewer.tsx +698 -0
- package/packages/dashboard/ui/react-components/TypingIndicator.tsx +69 -0
- package/packages/dashboard/ui/react-components/UsageBanner.tsx +231 -0
- package/packages/dashboard/ui/react-components/UserProfilePanel.tsx +233 -0
- package/packages/dashboard/ui/react-components/WorkspaceContext.tsx +107 -0
- package/packages/dashboard/ui/react-components/WorkspaceSelector.tsx +234 -0
- package/packages/dashboard/ui/react-components/WorkspaceStatusIndicator.tsx +370 -0
- package/packages/dashboard/ui/react-components/XTermInteractive.tsx +510 -0
- package/packages/dashboard/ui/react-components/XTermLogViewer.tsx +719 -0
- package/packages/dashboard/ui/react-components/channels/ChannelDialogs.tsx +1411 -0
- package/packages/dashboard/ui/react-components/channels/ChannelHeader.tsx +317 -0
- package/packages/dashboard/ui/react-components/channels/ChannelMessageList.tsx +463 -0
- package/packages/dashboard/ui/react-components/channels/ChannelViewV1.tsx +146 -0
- package/packages/dashboard/ui/react-components/channels/MessageInput.tsx +288 -0
- package/packages/dashboard/ui/react-components/channels/SearchInput.tsx +172 -0
- package/packages/dashboard/ui/react-components/channels/SearchResults.tsx +336 -0
- package/packages/dashboard/ui/react-components/channels/api.ts +697 -0
- package/packages/dashboard/ui/react-components/channels/index.ts +76 -0
- package/packages/dashboard/ui/react-components/channels/mockApi.ts +344 -0
- package/packages/dashboard/ui/react-components/channels/types.ts +566 -0
- package/packages/dashboard/ui/react-components/hooks/index.ts +57 -0
- package/packages/dashboard/ui/react-components/hooks/useAgentLogs.ts +394 -0
- package/packages/dashboard/ui/react-components/hooks/useAgents.ts +127 -0
- package/packages/dashboard/ui/react-components/hooks/useBroadcastDedup.ts +86 -0
- package/packages/dashboard/ui/react-components/hooks/useChannelAdmin.ts +329 -0
- package/packages/dashboard/ui/react-components/hooks/useChannelBrowser.ts +239 -0
- package/packages/dashboard/ui/react-components/hooks/useChannelCommands.ts +138 -0
- package/packages/dashboard/ui/react-components/hooks/useChannels.ts +328 -0
- package/packages/dashboard/ui/react-components/hooks/useDebounce.ts +29 -0
- package/packages/dashboard/ui/react-components/hooks/useDirectMessage.ts +141 -0
- package/packages/dashboard/ui/react-components/hooks/useMessages.ts +309 -0
- package/packages/dashboard/ui/react-components/hooks/useOrchestrator.ts +364 -0
- package/packages/dashboard/ui/react-components/hooks/usePinnedAgents.ts +140 -0
- package/packages/dashboard/ui/react-components/hooks/usePresence.ts +340 -0
- package/packages/dashboard/ui/react-components/hooks/useRecentRepos.ts +130 -0
- package/packages/dashboard/ui/react-components/hooks/useSession.ts +209 -0
- package/packages/dashboard/ui/react-components/hooks/useTrajectory.ts +265 -0
- package/packages/dashboard/ui/react-components/hooks/useWebSocket.ts +169 -0
- package/packages/dashboard/ui/react-components/hooks/useWorkspaceMembers.ts +120 -0
- package/packages/dashboard/ui/react-components/hooks/useWorkspaceRepos.ts +73 -0
- package/packages/dashboard/ui/react-components/hooks/useWorkspaceStatus.ts +237 -0
- package/packages/dashboard/ui/react-components/index.ts +81 -0
- package/packages/dashboard/ui/react-components/layout/Header.tsx +355 -0
- package/packages/dashboard/ui/react-components/layout/RepoContextHeader.tsx +361 -0
- package/packages/dashboard/ui/react-components/layout/Sidebar.archive.test.tsx +126 -0
- package/packages/dashboard/ui/react-components/layout/Sidebar.test.tsx +691 -0
- package/packages/dashboard/ui/react-components/layout/Sidebar.tsx +930 -0
- package/packages/dashboard/ui/react-components/layout/index.ts +7 -0
- package/packages/dashboard/ui/react-components/settings/BillingSettingsPanel.tsx +564 -0
- package/packages/dashboard/ui/react-components/settings/SettingsPage.tsx +544 -0
- package/packages/dashboard/ui/react-components/settings/TeamSettingsPanel.tsx +560 -0
- package/packages/dashboard/ui/react-components/settings/WorkspaceSettingsPanel.tsx +1329 -0
- package/packages/dashboard/ui/react-components/settings/index.ts +11 -0
- package/packages/dashboard/ui/react-components/settings/types.ts +53 -0
- package/packages/dashboard/ui/react-components/utils/messageFormatting.tsx +370 -0
- package/packages/dashboard/ui/tailwind.config.js +148 -0
- package/packages/dashboard/ui/types/index.ts +304 -0
- package/packages/dashboard/ui/types/threading.ts +7 -0
- package/packages/dashboard/ui-dist/404.html +1 -0
- package/packages/dashboard/ui-dist/_next/static/HR7W9z1PPVPFqUboUVZFZ/_buildManifest.js +1 -0
- package/packages/dashboard/ui-dist/_next/static/HR7W9z1PPVPFqUboUVZFZ/_ssgManifest.js +1 -0
- package/packages/dashboard/ui-dist/_next/static/ZCFjHbkF8yDKS2md3lVgb/_buildManifest.js +1 -0
- package/packages/dashboard/ui-dist/_next/static/ZCFjHbkF8yDKS2md3lVgb/_ssgManifest.js +1 -0
- package/packages/dashboard/ui-dist/_next/static/chunks/116-a883fca163f3a5bc.js +1 -0
- package/packages/dashboard/ui-dist/_next/static/chunks/117-c8afed19e821a35d.js +2 -0
- package/packages/dashboard/ui-dist/_next/static/chunks/282-980c2eb8fff20123.js +1 -0
- package/packages/dashboard/ui-dist/_next/static/chunks/320-900169c942e31422.js +1 -0
- package/packages/dashboard/ui-dist/_next/static/chunks/532-bace199897eeab37.js +9 -0
- package/packages/dashboard/ui-dist/_next/static/chunks/631-af51bad94027527a.js +1 -0
- package/packages/dashboard/ui-dist/_next/static/chunks/648-acb2ff9f77cbfbd3.js +1 -0
- package/packages/dashboard/ui-dist/_next/static/chunks/677-30e60cb0b47875b6.js +1 -0
- package/packages/dashboard/ui-dist/_next/static/chunks/766-2aea80818f7eb0d8.js +1 -0
- package/packages/dashboard/ui-dist/_next/static/chunks/83-4f08122d4e7e79a6.js +1 -0
- package/packages/dashboard/ui-dist/_next/static/chunks/847-f1f467060f32afff.js +1 -0
- package/packages/dashboard/ui-dist/_next/static/chunks/891-5cb1513eeb97a891.js +1 -0
- package/packages/dashboard/ui-dist/_next/static/chunks/app/_not-found/page-60501fddbafba9dc.js +1 -0
- package/packages/dashboard/ui-dist/_next/static/chunks/app/app/onboarding/page-9914652442f7e4fb.js +1 -0
- package/packages/dashboard/ui-dist/_next/static/chunks/app/app/onboarding/page-f746f29e01fffc43.js +1 -0
- package/packages/dashboard/ui-dist/_next/static/chunks/app/app/page-2e525b1dcc790967.js +1 -0
- package/packages/dashboard/ui-dist/_next/static/chunks/app/app/page-44813aa26ad19681.js +1 -0
- package/packages/dashboard/ui-dist/_next/static/chunks/app/cloud/link/page-5011ae044b90449d.js +1 -0
- package/packages/dashboard/ui-dist/_next/static/chunks/app/cloud/link/page-fa1d5842aa90e8a6.js +1 -0
- package/packages/dashboard/ui-dist/_next/static/chunks/app/connect-repos/page-03ac6f35a6654ea6.js +1 -0
- package/packages/dashboard/ui-dist/_next/static/chunks/app/connect-repos/page-113060009ef35bc2.js +1 -0
- package/packages/dashboard/ui-dist/_next/static/chunks/app/history/page-9965d2483011b846.js +1 -0
- package/packages/dashboard/ui-dist/_next/static/chunks/app/history/page-b2ce7c96ed0931da.js +1 -0
- package/packages/dashboard/ui-dist/_next/static/chunks/app/layout-6b91e33784c20610.js +1 -0
- package/packages/dashboard/ui-dist/_next/static/chunks/app/layout-c0d118c0f92d969c.js +1 -0
- package/packages/dashboard/ui-dist/_next/static/chunks/app/login/page-6ec54eee75877971.js +1 -0
- package/packages/dashboard/ui-dist/_next/static/chunks/app/login/page-a0ca6f7ca6a100b8.js +1 -0
- package/packages/dashboard/ui-dist/_next/static/chunks/app/metrics/page-1e37ef8e73940b40.js +1 -0
- package/packages/dashboard/ui-dist/_next/static/chunks/app/metrics/page-bf2cb1e5915bc92d.js +1 -0
- package/packages/dashboard/ui-dist/_next/static/chunks/app/page-4e64923d73c35bc9.js +1 -0
- package/packages/dashboard/ui-dist/_next/static/chunks/app/page-7993778218818ace.js +1 -0
- package/packages/dashboard/ui-dist/_next/static/chunks/app/pricing/page-0efa024c28ba4597.js +1 -0
- package/packages/dashboard/ui-dist/_next/static/chunks/app/pricing/page-9db3ebdfa567a7c9.js +1 -0
- package/packages/dashboard/ui-dist/_next/static/chunks/app/providers/page-bcf46064ac4474ce.js +1 -0
- package/packages/dashboard/ui-dist/_next/static/chunks/app/providers/page-e65a0010da6ea5be.js +1 -0
- package/packages/dashboard/ui-dist/_next/static/chunks/app/providers/setup/[provider]/page-4dbe33f0f7691b7c.js +1 -0
- package/packages/dashboard/ui-dist/_next/static/chunks/app/providers/setup/[provider]/page-84161c802b020a1f.js +1 -0
- package/packages/dashboard/ui-dist/_next/static/chunks/app/signup/page-18a4665665f6be11.js +1 -0
- package/packages/dashboard/ui-dist/_next/static/chunks/app/signup/page-1ede2205b58649ca.js +1 -0
- package/packages/dashboard/ui-dist/_next/static/chunks/e868780c-48e5f147c90a3a41.js +18 -0
- package/packages/dashboard/ui-dist/_next/static/chunks/fd9d1056-609918ca7b6280bb.js +1 -0
- package/packages/dashboard/ui-dist/_next/static/chunks/framework-f66176bb897dc684.js +1 -0
- package/packages/dashboard/ui-dist/_next/static/chunks/main-5a40a5ae29646e1b.js +1 -0
- package/packages/dashboard/ui-dist/_next/static/chunks/main-app-6e8e8d3ef4e0192a.js +1 -0
- package/packages/dashboard/ui-dist/_next/static/chunks/main-app-fdbeb09028f57c9f.js +1 -0
- package/packages/dashboard/ui-dist/_next/static/chunks/pages/_app-72b849fbd24ac258.js +1 -0
- package/packages/dashboard/ui-dist/_next/static/chunks/pages/_error-7ba65e1336b92748.js +1 -0
- package/packages/dashboard/ui-dist/_next/static/chunks/polyfills-42372ed130431b0a.js +1 -0
- package/packages/dashboard/ui-dist/_next/static/chunks/webpack-1cdd8ed57114d5e1.js +1 -0
- package/packages/dashboard/ui-dist/_next/static/css/4034f236dd1a3178.css +1 -0
- package/packages/dashboard/ui-dist/_next/static/css/99c2552394077586.css +1 -0
- package/packages/dashboard/ui-dist/alt-logos/agent-relay-logo-128.png +0 -0
- package/packages/dashboard/ui-dist/alt-logos/agent-relay-logo-256.png +0 -0
- package/packages/dashboard/ui-dist/alt-logos/agent-relay-logo-32.png +0 -0
- package/packages/dashboard/ui-dist/alt-logos/agent-relay-logo-512.png +0 -0
- package/packages/dashboard/ui-dist/alt-logos/agent-relay-logo-64.png +0 -0
- package/packages/dashboard/ui-dist/alt-logos/agent-relay-logo.svg +45 -0
- package/packages/dashboard/ui-dist/alt-logos/logo.svg +38 -0
- package/packages/dashboard/ui-dist/alt-logos/monogram-logo-128.png +0 -0
- package/packages/dashboard/ui-dist/alt-logos/monogram-logo-256.png +0 -0
- package/packages/dashboard/ui-dist/alt-logos/monogram-logo-32.png +0 -0
- package/packages/dashboard/ui-dist/alt-logos/monogram-logo-512.png +0 -0
- package/packages/dashboard/ui-dist/alt-logos/monogram-logo-64.png +0 -0
- package/packages/dashboard/ui-dist/alt-logos/monogram-logo.svg +38 -0
- package/packages/dashboard/ui-dist/app/onboarding.html +1 -0
- package/packages/dashboard/ui-dist/app/onboarding.txt +7 -0
- package/packages/dashboard/ui-dist/app.html +1 -0
- package/packages/dashboard/ui-dist/app.txt +7 -0
- package/packages/dashboard/ui-dist/apple-icon.png +0 -0
- package/packages/dashboard/ui-dist/cloud/link.html +1 -0
- package/packages/dashboard/ui-dist/cloud/link.txt +7 -0
- package/packages/dashboard/ui-dist/connect-repos.html +1 -0
- package/packages/dashboard/ui-dist/connect-repos.txt +7 -0
- package/packages/dashboard/ui-dist/history.html +1 -0
- package/packages/dashboard/ui-dist/history.txt +7 -0
- package/packages/dashboard/ui-dist/index.html +1 -0
- package/packages/dashboard/ui-dist/index.txt +7 -0
- package/packages/dashboard/ui-dist/login.html +5 -0
- package/packages/dashboard/ui-dist/login.txt +7 -0
- package/packages/dashboard/ui-dist/metrics.html +1 -0
- package/packages/dashboard/ui-dist/metrics.txt +7 -0
- package/packages/dashboard/ui-dist/pricing.html +13 -0
- package/packages/dashboard/ui-dist/pricing.txt +7 -0
- package/packages/dashboard/ui-dist/providers/setup/claude.html +1 -0
- package/packages/dashboard/ui-dist/providers/setup/claude.txt +8 -0
- package/packages/dashboard/ui-dist/providers/setup/codex.html +1 -0
- package/packages/dashboard/ui-dist/providers/setup/codex.txt +8 -0
- package/packages/dashboard/ui-dist/providers/setup/cursor.html +1 -0
- package/packages/dashboard/ui-dist/providers/setup/cursor.txt +8 -0
- package/packages/dashboard/ui-dist/providers.html +1 -0
- package/packages/dashboard/ui-dist/providers.txt +7 -0
- package/packages/dashboard/ui-dist/signup.html +6 -0
- package/packages/dashboard/ui-dist/signup.txt +7 -0
- package/packages/dashboard-server/dist/health-worker-manager.d.ts +62 -0
- package/packages/dashboard-server/dist/health-worker-manager.js +144 -0
- package/packages/dashboard-server/dist/health-worker.d.ts +9 -0
- package/packages/dashboard-server/dist/health-worker.js +79 -0
- package/packages/dashboard-server/dist/index.d.ts +18 -0
- package/packages/dashboard-server/dist/index.js +17 -0
- package/packages/dashboard-server/dist/server.js +5099 -0
- package/packages/dashboard-server/dist/start.js +13 -0
- package/packages/dashboard-server/dist/types/threading.d.ts +8 -0
- package/packages/dashboard-server/dist/types/threading.js +2 -0
- package/packages/dashboard-server/dist/user-bridge.d.ts +154 -0
- package/packages/dashboard-server/dist/user-bridge.js +372 -0
- package/packages/dashboard-server/package.json +49 -0
- package/packages/hooks/dist/browser.d.ts +2 -0
- package/packages/hooks/dist/browser.js +3 -0
- package/packages/hooks/dist/index.d.ts +11 -0
- package/packages/hooks/dist/index.js +11 -0
- package/packages/hooks/dist/registry.js +476 -0
- package/packages/hooks/dist/trajectory-hooks.js +183 -0
- package/packages/hooks/dist/types.d.ts +285 -0
- package/packages/hooks/dist/types.js +10 -0
- package/packages/hooks/package.json +52 -0
- package/packages/mcp/LICENSE +190 -0
- package/packages/mcp/README.md +214 -0
- package/packages/mcp/SPEC.md +1922 -0
- package/packages/mcp/STAFFING_PLAN.md +294 -0
- package/packages/mcp/dist/bin.d.ts +12 -0
- package/packages/mcp/dist/bin.js +127 -0
- package/packages/mcp/dist/client.d.ts +68 -0
- package/packages/mcp/dist/client.js +115 -0
- package/packages/mcp/dist/cloud.d.ts +108 -0
- package/packages/mcp/dist/cloud.js +279 -0
- package/packages/mcp/dist/errors.d.ts +27 -0
- package/packages/mcp/dist/errors.js +48 -0
- package/packages/mcp/dist/index.d.ts +10 -0
- package/packages/mcp/dist/index.js +16 -0
- package/packages/mcp/dist/install-cli.d.ts +35 -0
- package/packages/mcp/dist/install-cli.js +157 -0
- package/packages/mcp/dist/install.d.ts +101 -0
- package/packages/mcp/dist/install.js +398 -0
- package/packages/mcp/dist/prompts/index.d.ts +2 -0
- package/packages/mcp/dist/prompts/index.js +2 -0
- package/packages/mcp/dist/prompts/protocol.d.ts +11 -0
- package/packages/mcp/dist/prompts/protocol.js +168 -0
- package/packages/mcp/dist/resources/agents.d.ts +11 -0
- package/packages/mcp/dist/resources/agents.js +17 -0
- package/packages/mcp/dist/resources/inbox.d.ts +11 -0
- package/packages/mcp/dist/resources/inbox.js +17 -0
- package/packages/mcp/dist/resources/index.d.ts +4 -0
- package/packages/mcp/dist/resources/index.js +4 -0
- package/packages/mcp/dist/resources/project.d.ts +11 -0
- package/packages/mcp/dist/resources/project.js +21 -0
- package/packages/mcp/dist/server.d.ts +19 -0
- package/packages/mcp/dist/server.js +215 -0
- package/packages/mcp/dist/simple.d.ts +173 -0
- package/packages/mcp/dist/simple.js +120 -0
- package/packages/mcp/dist/tools/index.d.ts +10 -0
- package/packages/mcp/dist/tools/index.js +10 -0
- package/packages/mcp/dist/tools/relay-health.d.ts +23 -0
- package/packages/mcp/dist/tools/relay-health.js +138 -0
- package/packages/mcp/dist/tools/relay-inbox.d.ts +26 -0
- package/packages/mcp/dist/tools/relay-inbox.js +58 -0
- package/packages/mcp/dist/tools/relay-logs.d.ts +20 -0
- package/packages/mcp/dist/tools/relay-logs.js +88 -0
- package/packages/mcp/dist/tools/relay-metrics.d.ts +20 -0
- package/packages/mcp/dist/tools/relay-metrics.js +135 -0
- package/packages/mcp/dist/tools/relay-release.d.ts +20 -0
- package/packages/mcp/dist/tools/relay-release.js +44 -0
- package/packages/mcp/dist/tools/relay-send.d.ts +29 -0
- package/packages/mcp/dist/tools/relay-send.js +71 -0
- package/packages/mcp/dist/tools/relay-spawn.d.ts +36 -0
- package/packages/mcp/dist/tools/relay-spawn.js +73 -0
- package/packages/mcp/dist/tools/relay-status.d.ts +11 -0
- package/packages/mcp/dist/tools/relay-status.js +43 -0
- package/packages/mcp/dist/tools/relay-who.d.ts +20 -0
- package/packages/mcp/dist/tools/relay-who.js +47 -0
- package/packages/mcp/package.json +69 -0
- package/packages/memory/dist/memory-hooks.d.ts +60 -0
- package/packages/memory/package.json +35 -0
- package/packages/policy/dist/agent-policy.js +665 -0
- package/packages/policy/dist/index.d.ts +12 -0
- package/packages/policy/dist/index.js +12 -0
- package/packages/policy/package.json +35 -0
- package/packages/protocol/dist/channels.d.ts +137 -0
- package/packages/protocol/dist/channels.js +154 -0
- package/packages/protocol/dist/framing.d.ts +80 -0
- package/packages/protocol/dist/framing.js +206 -0
- package/packages/protocol/dist/index.d.ts +5 -0
- package/packages/protocol/dist/index.js +5 -0
- package/packages/protocol/dist/relay-pty-schemas.d.ts +258 -0
- package/packages/protocol/dist/types.d.ts +341 -0
- package/packages/protocol/dist/types.js +8 -0
- package/packages/protocol/package.json +56 -0
- package/packages/resiliency/dist/memory-monitor.js +599 -0
- package/packages/resiliency/dist/provider-context.d.ts +100 -0
- package/packages/resiliency/package.json +33 -0
- package/packages/sdk/README.md +171 -0
- package/packages/sdk/dist/client.d.ts +181 -0
- package/packages/sdk/dist/client.js +695 -0
- package/packages/sdk/dist/index.d.ts +32 -0
- package/packages/sdk/dist/index.js +36 -0
- package/packages/sdk/dist/protocol/framing.d.ts +80 -0
- package/packages/sdk/dist/protocol/framing.js +206 -0
- package/packages/sdk/dist/protocol/index.d.ts +6 -0
- package/packages/sdk/dist/protocol/index.js +6 -0
- package/packages/sdk/dist/protocol/types.d.ts +341 -0
- package/packages/sdk/dist/protocol/types.js +8 -0
- package/packages/sdk/dist/standalone.d.ts +87 -0
- package/packages/sdk/dist/standalone.js +126 -0
- package/packages/sdk/package.json +80 -0
- package/packages/spawner/API.md +256 -0
- package/packages/spawner/dist/index.d.ts +8 -0
- package/packages/spawner/dist/index.js +8 -0
- package/packages/spawner/dist/types.d.ts +552 -0
- package/packages/spawner/dist/types.js +193 -0
- package/packages/spawner/package.json +47 -0
- package/packages/state/dist/agent-state.js +120 -0
- package/packages/state/dist/index.d.ts +8 -0
- package/packages/state/dist/index.js +8 -0
- package/packages/state/package.json +32 -0
- package/packages/storage/dist/adapter.d.ts +156 -0
- package/packages/storage/dist/batched-sqlite-adapter.d.ts +75 -0
- package/packages/storage/dist/batched-sqlite-adapter.js +189 -0
- package/packages/storage/dist/index.d.ts +5 -0
- package/packages/storage/dist/index.js +6 -0
- package/packages/storage/dist/sqlite-adapter.d.ts +113 -0
- package/packages/storage/dist/sqlite-adapter.js +752 -0
- package/packages/storage/package.json +69 -0
- package/packages/trajectory/dist/index.d.ts +2 -0
- package/packages/trajectory/dist/index.js +2 -0
- package/packages/trajectory/dist/integration.js +987 -0
- package/packages/trajectory/package.json +35 -0
- package/packages/user-directory/dist/index.d.ts +7 -0
- package/packages/user-directory/dist/index.js +7 -0
- package/packages/user-directory/dist/user-directory.d.ts +121 -0
- package/packages/user-directory/dist/user-directory.js +267 -0
- package/packages/user-directory/package.json +35 -0
- package/packages/utils/dist/command-resolver.js +80 -0
- package/packages/utils/dist/error-tracking.d.ts +103 -0
- package/packages/utils/dist/error-tracking.js +149 -0
- package/packages/utils/dist/index.d.ts +9 -0
- package/packages/utils/dist/index.js +9 -0
- package/packages/utils/dist/model-mapping.d.ts +28 -0
- package/packages/utils/dist/model-mapping.js +55 -0
- package/packages/utils/package.json +75 -0
- package/packages/wrapper/dist/__fixtures__/claude-outputs.d.ts +49 -0
- package/packages/wrapper/dist/__fixtures__/claude-outputs.js +443 -0
- package/packages/wrapper/dist/__fixtures__/codex-outputs.d.ts +9 -0
- package/packages/wrapper/dist/__fixtures__/codex-outputs.js +94 -0
- package/packages/wrapper/dist/__fixtures__/gemini-outputs.d.ts +19 -0
- package/packages/wrapper/dist/__fixtures__/gemini-outputs.js +144 -0
- package/packages/wrapper/dist/__fixtures__/index.d.ts +68 -0
- package/packages/wrapper/dist/__fixtures__/index.js +44 -0
- package/packages/wrapper/dist/base-wrapper.d.ts +225 -0
- package/packages/wrapper/dist/base-wrapper.js +572 -0
- package/packages/wrapper/dist/client.d.ts +254 -0
- package/packages/wrapper/dist/client.js +801 -0
- package/packages/wrapper/dist/id-generator.d.ts +35 -0
- package/packages/wrapper/dist/id-generator.js +60 -0
- package/packages/wrapper/dist/idle-detector.d.ts +110 -0
- package/packages/wrapper/dist/idle-detector.js +304 -0
- package/packages/wrapper/dist/index.d.ts +37 -0
- package/packages/wrapper/dist/index.js +47 -0
- package/packages/wrapper/dist/parser.d.ts +236 -0
- package/packages/wrapper/dist/parser.js +1238 -0
- package/packages/wrapper/dist/relay-pty-orchestrator.d.ts +407 -0
- package/packages/wrapper/dist/relay-pty-orchestrator.js +1885 -0
- package/packages/wrapper/dist/shared.d.ts +201 -0
- package/packages/wrapper/dist/shared.js +341 -0
- package/packages/wrapper/dist/stuck-detector.d.ts +161 -0
- package/packages/wrapper/dist/stuck-detector.js +402 -0
- package/packages/wrapper/dist/tmux-wrapper.d.ts +345 -0
- package/packages/wrapper/dist/tmux-wrapper.js +1747 -0
- package/packages/wrapper/dist/trajectory-integration.d.ts +292 -0
- package/packages/wrapper/dist/trajectory-integration.js +979 -0
- package/packages/wrapper/dist/wrapper-types.d.ts +41 -0
- package/packages/wrapper/dist/wrapper-types.js +7 -0
- package/packages/wrapper/package.json +63 -0
- package/scripts/setup-stripe-products.ts +312 -0
- package/scripts/stress-test-orchestrator-integration.mts +1366 -0
- package/scripts/stress-test-orchestrator.mjs +584 -0
- package/scripts/stress-test-relay-pty.sh +452 -0
- package/scripts/verify-schema.js +1 -1
- package/turbo.json +37 -0
- package/dist/bridge/config.d.ts +0 -41
- package/dist/bridge/config.js +0 -143
- package/dist/bridge/index.d.ts +0 -10
- package/dist/bridge/index.js +0 -10
- package/dist/bridge/multi-project-client.d.ts +0 -99
- package/dist/bridge/multi-project-client.js +0 -389
- package/dist/bridge/shadow-cli.js +0 -75
- package/dist/bridge/shadow-config.d.ts +0 -87
- package/dist/bridge/spawner.d.ts +0 -186
- package/dist/bridge/spawner.js +0 -920
- package/dist/bridge/types.d.ts +0 -129
- package/dist/bridge/utils.d.ts +0 -30
- package/dist/bridge/utils.js +0 -54
- package/dist/cli/index.js +0 -2784
- package/dist/cloud/api/admin.js +0 -225
- package/dist/cloud/api/billing.js +0 -564
- package/dist/cloud/api/cli-pty-runner.d.ts +0 -54
- package/dist/cloud/api/cli-pty-runner.js +0 -119
- package/dist/cloud/api/codex-auth-helper.js +0 -327
- package/dist/cloud/api/consensus.js +0 -259
- package/dist/cloud/api/coordinators.js +0 -749
- package/dist/cloud/api/daemons.js +0 -535
- package/dist/cloud/api/generic-webhooks.js +0 -129
- package/dist/cloud/api/github-app.js +0 -223
- package/dist/cloud/api/monitoring.js +0 -578
- package/dist/cloud/api/nango-auth.js +0 -658
- package/dist/cloud/api/onboarding.d.ts +0 -15
- package/dist/cloud/api/onboarding.js +0 -666
- package/dist/cloud/api/policy.js +0 -229
- package/dist/cloud/api/provider-env.d.ts +0 -5
- package/dist/cloud/api/provider-env.js +0 -27
- package/dist/cloud/api/providers.js +0 -511
- package/dist/cloud/api/repos.js +0 -576
- package/dist/cloud/api/teams.js +0 -279
- package/dist/cloud/api/test-helpers.js +0 -745
- package/dist/cloud/api/workspaces.js +0 -1783
- package/dist/cloud/billing/plans.js +0 -245
- package/dist/cloud/config.d.ts +0 -75
- package/dist/cloud/config.js +0 -109
- package/dist/cloud/db/drizzle.d.ts +0 -246
- package/dist/cloud/db/drizzle.js +0 -1249
- package/dist/cloud/db/schema.d.ts +0 -4854
- package/dist/cloud/db/schema.js +0 -610
- package/dist/cloud/index.d.ts +0 -11
- package/dist/cloud/index.js +0 -38
- package/dist/cloud/provisioner/index.d.ts +0 -207
- package/dist/cloud/provisioner/index.js +0 -2069
- package/dist/cloud/server.js +0 -1599
- package/dist/cloud/services/index.d.ts +0 -17
- package/dist/cloud/services/index.js +0 -25
- package/dist/cloud/services/intro-expiration.d.ts +0 -55
- package/dist/cloud/services/intro-expiration.js +0 -211
- package/dist/cloud/services/nango.d.ts +0 -199
- package/dist/cloud/services/nango.js +0 -382
- package/dist/cloud/services/persistence.d.ts +0 -131
- package/dist/config/relay-config.d.ts +0 -23
- package/dist/config/relay-config.js +0 -23
- package/dist/continuity/index.d.ts +0 -45
- package/dist/continuity/index.js +0 -48
- package/dist/continuity/types.d.ts +0 -180
- package/dist/continuity/types.js +0 -9
- package/dist/daemon/agent-manager.d.ts +0 -134
- package/dist/daemon/agent-manager.js +0 -564
- package/dist/daemon/agent-registry.js +0 -213
- package/dist/daemon/api.d.ts +0 -83
- package/dist/daemon/api.js +0 -780
- package/dist/daemon/channel-membership-store.d.ts +0 -48
- package/dist/daemon/channel-membership-store.js +0 -149
- package/dist/daemon/cli-auth.d.ts +0 -82
- package/dist/daemon/cli-auth.js +0 -700
- package/dist/daemon/cloud-sync.d.ts +0 -150
- package/dist/daemon/cloud-sync.js +0 -424
- package/dist/daemon/connection.d.ts +0 -130
- package/dist/daemon/connection.js +0 -438
- package/dist/daemon/consensus-integration.js +0 -371
- package/dist/daemon/delivery-tracker.d.ts +0 -34
- package/dist/daemon/delivery-tracker.js +0 -104
- package/dist/daemon/enhanced-features.d.ts +0 -118
- package/dist/daemon/enhanced-features.js +0 -178
- package/dist/daemon/index.d.ts +0 -14
- package/dist/daemon/index.js +0 -17
- package/dist/daemon/orchestrator.d.ts +0 -157
- package/dist/daemon/orchestrator.js +0 -792
- package/dist/daemon/repo-manager.js +0 -384
- package/dist/daemon/router.d.ts +0 -358
- package/dist/daemon/router.js +0 -1333
- package/dist/daemon/server.d.ts +0 -159
- package/dist/daemon/server.js +0 -788
- package/dist/daemon/services/browser-testing.d.ts +0 -88
- package/dist/daemon/services/browser-testing.js +0 -244
- package/dist/daemon/services/container-spawner.d.ts +0 -135
- package/dist/daemon/services/container-spawner.js +0 -313
- package/dist/daemon/sync-queue.d.ts +0 -116
- package/dist/daemon/sync-queue.js +0 -361
- package/dist/daemon/types.d.ts +0 -131
- package/dist/daemon/user-directory.d.ts +0 -111
- package/dist/daemon/user-directory.js +0 -233
- package/dist/daemon/workspace-manager.js +0 -314
- package/dist/dashboard/out/_next/static/chunks/116-eacf84a131b80db9.js +0 -1
- package/dist/dashboard/out/_next/static/chunks/64-f4268c2ac6f4d7d4.js +0 -1
- package/dist/dashboard/out/_next/static/chunks/766-aa7c8c9900ff5f53.js +0 -1
- package/dist/dashboard/out/_next/static/chunks/891-a024fbe4b619cf6f.js +0 -1
- package/dist/dashboard/out/_next/static/chunks/app/app/page-ffad986adfcc8b31.js +0 -1
- package/dist/dashboard/out/_next/static/chunks/app/page-671037943b2f2e43.js +0 -1
- package/dist/dashboard/out/_next/static/chunks/app/providers/page-57cbd738c6a73859.js +0 -1
- package/dist/dashboard/out/_next/static/chunks/app/providers/setup/[provider]/page-5ab0854472b402b0.js +0 -1
- package/dist/dashboard/out/_next/static/css/8f9ed310f454e5a5.css +0 -1
- package/dist/dashboard-server/server.js +0 -4806
- package/dist/dashboard-server/start.js +0 -13
- package/dist/dashboard-server/user-bridge.d.ts +0 -138
- package/dist/dashboard-server/user-bridge.js +0 -348
- package/dist/hooks/index.d.ts +0 -10
- package/dist/hooks/index.js +0 -10
- package/dist/hooks/registry.js +0 -476
- package/dist/hooks/trajectory-hooks.js +0 -183
- package/dist/hooks/types.d.ts +0 -284
- package/dist/hooks/types.js +0 -8
- package/dist/index.d.ts +0 -13
- package/dist/index.js +0 -16
- package/dist/memory/memory-hooks.d.ts +0 -60
- package/dist/policy/agent-policy.js +0 -665
- package/dist/protocol/channels.d.ts +0 -211
- package/dist/protocol/channels.js +0 -154
- package/dist/protocol/framing.d.ts +0 -94
- package/dist/protocol/framing.js +0 -240
- package/dist/protocol/index.d.ts +0 -4
- package/dist/protocol/index.js +0 -4
- package/dist/protocol/relay-pty-schemas.d.ts +0 -209
- package/dist/protocol/types.d.ts +0 -168
- package/dist/protocol/types.js +0 -6
- package/dist/resiliency/memory-monitor.js +0 -593
- package/dist/resiliency/provider-context.d.ts +0 -100
- package/dist/shared/cli-auth-config.js +0 -320
- package/dist/state/agent-state.js +0 -120
- package/dist/storage/adapter.d.ts +0 -154
- package/dist/storage/batched-sqlite-adapter.d.ts +0 -71
- package/dist/storage/batched-sqlite-adapter.js +0 -183
- package/dist/storage/sqlite-adapter.d.ts +0 -107
- package/dist/storage/sqlite-adapter.js +0 -717
- package/dist/trajectory/config.d.ts +0 -102
- package/dist/trajectory/config.js +0 -185
- package/dist/trajectory/index.d.ts +0 -8
- package/dist/trajectory/index.js +0 -8
- package/dist/trajectory/integration.js +0 -987
- package/dist/utils/command-resolver.js +0 -76
- package/dist/utils/index.d.ts +0 -4
- package/dist/utils/index.js +0 -4
- package/dist/utils/project-namespace.d.ts +0 -70
- package/dist/utils/project-namespace.js +0 -216
- package/dist/wrapper/base-wrapper.d.ts +0 -217
- package/dist/wrapper/base-wrapper.js +0 -538
- package/dist/wrapper/client.d.ts +0 -199
- package/dist/wrapper/client.js +0 -677
- package/dist/wrapper/idle-detector.d.ts +0 -102
- package/dist/wrapper/idle-detector.js +0 -279
- package/dist/wrapper/index.d.ts +0 -4
- package/dist/wrapper/index.js +0 -7
- package/dist/wrapper/parser.d.ts +0 -230
- package/dist/wrapper/parser.js +0 -1178
- package/dist/wrapper/pty-wrapper.d.ts +0 -343
- package/dist/wrapper/pty-wrapper.js +0 -1593
- package/dist/wrapper/relay-pty-orchestrator.d.ts +0 -296
- package/dist/wrapper/relay-pty-orchestrator.js +0 -1088
- package/dist/wrapper/shared.d.ts +0 -168
- package/dist/wrapper/shared.js +0 -291
- package/dist/wrapper/stuck-detector.d.ts +0 -101
- package/dist/wrapper/stuck-detector.js +0 -228
- package/dist/wrapper/tmux-wrapper.d.ts +0 -344
- package/dist/wrapper/tmux-wrapper.js +0 -1711
- /package/dist/dashboard/out/_next/static/{BffXAqxm-_rUlj2mAnK26 → ZCFjHbkF8yDKS2md3lVgb}/_buildManifest.js +0 -0
- /package/dist/dashboard/out/_next/static/{BffXAqxm-_rUlj2mAnK26 → ZCFjHbkF8yDKS2md3lVgb}/_ssgManifest.js +0 -0
- /package/dist/dashboard/out/_next/static/chunks/app/cloud/link/{page-cfeb437f08a12ed9.js → page-5011ae044b90449d.js} +0 -0
- /package/dist/dashboard/out/_next/static/chunks/app/history/{page-240f91e8b06ba8ac.js → page-b2ce7c96ed0931da.js} +0 -0
- /package/dist/dashboard/out/_next/static/chunks/app/metrics/{page-82938ab8fcf44694.js → page-bf2cb1e5915bc92d.js} +0 -0
- /package/dist/{cli → src/cli}/index.d.ts +0 -0
- /package/dist/{health-worker-manager.d.ts → src/health-worker-manager.d.ts} +0 -0
- /package/dist/{health-worker-manager.js → src/health-worker-manager.js} +0 -0
- /package/dist/{health-worker.d.ts → src/health-worker.d.ts} +0 -0
- /package/dist/{health-worker.js → src/health-worker.js} +0 -0
- /package/{dist/bridge → packages/bridge/dist}/shadow-cli.d.ts +0 -0
- /package/{dist/bridge → packages/bridge/dist}/types.js +0 -0
- /package/{dist/cloud → packages/cloud/dist}/api/admin.d.ts +0 -0
- /package/{dist/cloud → packages/cloud/dist}/api/auth.d.ts +0 -0
- /package/{dist/cloud → packages/cloud/dist}/api/auth.js +0 -0
- /package/{dist/cloud → packages/cloud/dist}/api/billing.d.ts +0 -0
- /package/{dist/cloud → packages/cloud/dist}/api/codex-auth-helper.d.ts +0 -0
- /package/{dist/cloud → packages/cloud/dist}/api/consensus.d.ts +0 -0
- /package/{dist/cloud → packages/cloud/dist}/api/coordinators.d.ts +0 -0
- /package/{dist/cloud → packages/cloud/dist}/api/daemons.d.ts +0 -0
- /package/{dist/cloud → packages/cloud/dist}/api/generic-webhooks.d.ts +0 -0
- /package/{dist/cloud → packages/cloud/dist}/api/git.d.ts +0 -0
- /package/{dist/cloud → packages/cloud/dist}/api/git.js +0 -0
- /package/{dist/cloud → packages/cloud/dist}/api/github-app.d.ts +0 -0
- /package/{dist/cloud → packages/cloud/dist}/api/middleware/planLimits.d.ts +0 -0
- /package/{dist/cloud → packages/cloud/dist}/api/middleware/planLimits.js +0 -0
- /package/{dist/cloud → packages/cloud/dist}/api/monitoring.d.ts +0 -0
- /package/{dist/cloud → packages/cloud/dist}/api/nango-auth.d.ts +0 -0
- /package/{dist/cloud → packages/cloud/dist}/api/policy.d.ts +0 -0
- /package/{dist/cloud → packages/cloud/dist}/api/providers.d.ts +0 -0
- /package/{dist/cloud → packages/cloud/dist}/api/repos.d.ts +0 -0
- /package/{dist/cloud → packages/cloud/dist}/api/teams.d.ts +0 -0
- /package/{dist/cloud → packages/cloud/dist}/api/test-helpers.d.ts +0 -0
- /package/{dist/cloud → packages/cloud/dist}/api/usage.d.ts +0 -0
- /package/{dist/cloud → packages/cloud/dist}/api/usage.js +0 -0
- /package/{dist/cloud → packages/cloud/dist}/api/webhooks.d.ts +0 -0
- /package/{dist/cloud → packages/cloud/dist}/api/webhooks.js +0 -0
- /package/{dist/cloud → packages/cloud/dist}/api/workspaces.d.ts +0 -0
- /package/{dist/cloud → packages/cloud/dist}/billing/index.d.ts +0 -0
- /package/{dist/cloud → packages/cloud/dist}/billing/index.js +0 -0
- /package/{dist/cloud → packages/cloud/dist}/billing/plans.d.ts +0 -0
- /package/{dist/cloud → packages/cloud/dist}/billing/service.d.ts +0 -0
- /package/{dist/cloud → packages/cloud/dist}/billing/service.js +0 -0
- /package/{dist/cloud → packages/cloud/dist}/billing/types.d.ts +0 -0
- /package/{dist/cloud → packages/cloud/dist}/billing/types.js +0 -0
- /package/{dist/cloud → packages/cloud/dist}/db/bulk-ingest.d.ts +0 -0
- /package/{dist/cloud → packages/cloud/dist}/db/bulk-ingest.js +0 -0
- /package/{dist/cloud → packages/cloud/dist}/db/index.d.ts +0 -0
- /package/{dist/cloud → packages/cloud/dist}/db/index.js +0 -0
- /package/{dist/cloud → packages/cloud/dist}/server.d.ts +0 -0
- /package/{dist/cloud → packages/cloud/dist}/services/auto-scaler.d.ts +0 -0
- /package/{dist/cloud → packages/cloud/dist}/services/auto-scaler.js +0 -0
- /package/{dist/cloud → packages/cloud/dist}/services/capacity-manager.d.ts +0 -0
- /package/{dist/cloud → packages/cloud/dist}/services/capacity-manager.js +0 -0
- /package/{dist/cloud → packages/cloud/dist}/services/ci-agent-spawner.d.ts +0 -0
- /package/{dist/cloud → packages/cloud/dist}/services/ci-agent-spawner.js +0 -0
- /package/{dist/cloud → packages/cloud/dist}/services/cloud-message-bus.d.ts +0 -0
- /package/{dist/cloud → packages/cloud/dist}/services/cloud-message-bus.js +0 -0
- /package/{dist/cloud → packages/cloud/dist}/services/compute-enforcement.d.ts +0 -0
- /package/{dist/cloud → packages/cloud/dist}/services/compute-enforcement.js +0 -0
- /package/{dist/cloud → packages/cloud/dist}/services/coordinator.d.ts +0 -0
- /package/{dist/cloud → packages/cloud/dist}/services/coordinator.js +0 -0
- /package/{dist/cloud → packages/cloud/dist}/services/mention-handler.d.ts +0 -0
- /package/{dist/cloud → packages/cloud/dist}/services/mention-handler.js +0 -0
- /package/{dist/cloud → packages/cloud/dist}/services/persistence.js +0 -0
- /package/{dist/cloud → packages/cloud/dist}/services/planLimits.d.ts +0 -0
- /package/{dist/cloud → packages/cloud/dist}/services/planLimits.js +0 -0
- /package/{dist/cloud → packages/cloud/dist}/services/presence-registry.d.ts +0 -0
- /package/{dist/cloud → packages/cloud/dist}/services/presence-registry.js +0 -0
- /package/{dist/cloud → packages/cloud/dist}/services/scaling-orchestrator.d.ts +0 -0
- /package/{dist/cloud → packages/cloud/dist}/services/scaling-orchestrator.js +0 -0
- /package/{dist/cloud → packages/cloud/dist}/services/scaling-policy.d.ts +0 -0
- /package/{dist/cloud → packages/cloud/dist}/services/scaling-policy.js +0 -0
- /package/{dist/cloud → packages/cloud/dist}/services/ssh-security.d.ts +0 -0
- /package/{dist/cloud → packages/cloud/dist}/services/ssh-security.js +0 -0
- /package/{dist/cloud → packages/cloud/dist}/services/workspace-keepalive.d.ts +0 -0
- /package/{dist/cloud → packages/cloud/dist}/services/workspace-keepalive.js +0 -0
- /package/{dist/cloud → packages/cloud/dist}/webhooks/index.d.ts +0 -0
- /package/{dist/cloud → packages/cloud/dist}/webhooks/index.js +0 -0
- /package/{dist/cloud → packages/cloud/dist}/webhooks/parsers/github.d.ts +0 -0
- /package/{dist/cloud → packages/cloud/dist}/webhooks/parsers/github.js +0 -0
- /package/{dist/cloud → packages/cloud/dist}/webhooks/parsers/index.d.ts +0 -0
- /package/{dist/cloud → packages/cloud/dist}/webhooks/parsers/index.js +0 -0
- /package/{dist/cloud → packages/cloud/dist}/webhooks/parsers/linear.d.ts +0 -0
- /package/{dist/cloud → packages/cloud/dist}/webhooks/parsers/linear.js +0 -0
- /package/{dist/cloud → packages/cloud/dist}/webhooks/parsers/slack.d.ts +0 -0
- /package/{dist/cloud → packages/cloud/dist}/webhooks/parsers/slack.js +0 -0
- /package/{dist/cloud → packages/cloud/dist}/webhooks/responders/github.d.ts +0 -0
- /package/{dist/cloud → packages/cloud/dist}/webhooks/responders/github.js +0 -0
- /package/{dist/cloud → packages/cloud/dist}/webhooks/responders/index.d.ts +0 -0
- /package/{dist/cloud → packages/cloud/dist}/webhooks/responders/index.js +0 -0
- /package/{dist/cloud → packages/cloud/dist}/webhooks/responders/linear.d.ts +0 -0
- /package/{dist/cloud → packages/cloud/dist}/webhooks/responders/linear.js +0 -0
- /package/{dist/cloud → packages/cloud/dist}/webhooks/responders/slack.d.ts +0 -0
- /package/{dist/cloud → packages/cloud/dist}/webhooks/responders/slack.js +0 -0
- /package/{dist/cloud → packages/cloud/dist}/webhooks/router.d.ts +0 -0
- /package/{dist/cloud → packages/cloud/dist}/webhooks/router.js +0 -0
- /package/{dist/cloud → packages/cloud/dist}/webhooks/rules-engine.d.ts +0 -0
- /package/{dist/cloud → packages/cloud/dist}/webhooks/rules-engine.js +0 -0
- /package/{dist/cloud → packages/cloud/dist}/webhooks/types.d.ts +0 -0
- /package/{dist/cloud → packages/cloud/dist}/webhooks/types.js +0 -0
- /package/{dist/utils → packages/config/dist}/agent-config.d.ts +0 -0
- /package/{dist/utils → packages/config/dist}/agent-config.js +0 -0
- /package/{dist/shared → packages/config/dist}/cli-auth-config.d.ts +0 -0
- /package/{dist/bridge → packages/config/dist}/shadow-config.js +0 -0
- /package/{dist/bridge → packages/config/dist}/teams-config.d.ts +0 -0
- /package/{dist/bridge → packages/config/dist}/teams-config.js +0 -0
- /package/{dist/continuity → packages/continuity/dist}/formatter.d.ts +0 -0
- /package/{dist/continuity → packages/continuity/dist}/formatter.js +0 -0
- /package/{dist/continuity → packages/continuity/dist}/handoff-store.d.ts +0 -0
- /package/{dist/continuity → packages/continuity/dist}/handoff-store.js +0 -0
- /package/{dist/continuity → packages/continuity/dist}/ledger-store.d.ts +0 -0
- /package/{dist/continuity → packages/continuity/dist}/ledger-store.js +0 -0
- /package/{dist/continuity → packages/continuity/dist}/manager.d.ts +0 -0
- /package/{dist/continuity → packages/continuity/dist}/manager.js +0 -0
- /package/{dist/continuity → packages/continuity/dist}/parser.d.ts +0 -0
- /package/{dist/continuity → packages/continuity/dist}/parser.js +0 -0
- /package/{dist/daemon → packages/daemon/dist}/agent-registry.d.ts +0 -0
- /package/{dist/daemon → packages/daemon/dist}/agent-signing.d.ts +0 -0
- /package/{dist/daemon → packages/daemon/dist}/agent-signing.js +0 -0
- /package/{dist/daemon → packages/daemon/dist}/auth.d.ts +0 -0
- /package/{dist/daemon → packages/daemon/dist}/auth.js +0 -0
- /package/{dist/daemon → packages/daemon/dist}/consensus-integration.d.ts +0 -0
- /package/{dist/daemon → packages/daemon/dist}/consensus.d.ts +0 -0
- /package/{dist/daemon → packages/daemon/dist}/consensus.js +0 -0
- /package/{dist/daemon → packages/daemon/dist}/rate-limiter.d.ts +0 -0
- /package/{dist/daemon → packages/daemon/dist}/rate-limiter.js +0 -0
- /package/{dist/daemon → packages/daemon/dist}/registry.d.ts +0 -0
- /package/{dist/daemon → packages/daemon/dist}/registry.js +0 -0
- /package/{dist/daemon → packages/daemon/dist}/repo-manager.d.ts +0 -0
- /package/{dist/daemon → packages/daemon/dist}/types.js +0 -0
- /package/{dist/daemon → packages/daemon/dist}/workspace-manager.d.ts +0 -0
- /package/{dist/dashboard-server → packages/dashboard-server/dist}/metrics.d.ts +0 -0
- /package/{dist/dashboard-server → packages/dashboard-server/dist}/metrics.js +0 -0
- /package/{dist/dashboard-server → packages/dashboard-server/dist}/needs-attention.d.ts +0 -0
- /package/{dist/dashboard-server → packages/dashboard-server/dist}/needs-attention.js +0 -0
- /package/{dist/dashboard-server → packages/dashboard-server/dist}/server.d.ts +0 -0
- /package/{dist/dashboard-server → packages/dashboard-server/dist}/start.d.ts +0 -0
- /package/{dist/hooks → packages/hooks/dist}/emitter.d.ts +0 -0
- /package/{dist/hooks → packages/hooks/dist}/emitter.js +0 -0
- /package/{dist/hooks → packages/hooks/dist}/inbox-check/hook.d.ts +0 -0
- /package/{dist/hooks → packages/hooks/dist}/inbox-check/hook.js +0 -0
- /package/{dist/hooks → packages/hooks/dist}/inbox-check/index.d.ts +0 -0
- /package/{dist/hooks → packages/hooks/dist}/inbox-check/index.js +0 -0
- /package/{dist/hooks → packages/hooks/dist}/inbox-check/types.d.ts +0 -0
- /package/{dist/hooks → packages/hooks/dist}/inbox-check/types.js +0 -0
- /package/{dist/hooks → packages/hooks/dist}/inbox-check/utils.d.ts +0 -0
- /package/{dist/hooks → packages/hooks/dist}/inbox-check/utils.js +0 -0
- /package/{dist/hooks → packages/hooks/dist}/registry.d.ts +0 -0
- /package/{dist/hooks → packages/hooks/dist}/trajectory-hooks.d.ts +0 -0
- /package/{dist/memory → packages/memory/dist}/adapters/index.d.ts +0 -0
- /package/{dist/memory → packages/memory/dist}/adapters/index.js +0 -0
- /package/{dist/memory → packages/memory/dist}/adapters/inmemory.d.ts +0 -0
- /package/{dist/memory → packages/memory/dist}/adapters/inmemory.js +0 -0
- /package/{dist/memory → packages/memory/dist}/adapters/supermemory.d.ts +0 -0
- /package/{dist/memory → packages/memory/dist}/adapters/supermemory.js +0 -0
- /package/{dist/memory → packages/memory/dist}/context-compaction.d.ts +0 -0
- /package/{dist/memory → packages/memory/dist}/context-compaction.js +0 -0
- /package/{dist/memory → packages/memory/dist}/factory.d.ts +0 -0
- /package/{dist/memory → packages/memory/dist}/factory.js +0 -0
- /package/{dist/memory → packages/memory/dist}/index.d.ts +0 -0
- /package/{dist/memory → packages/memory/dist}/index.js +0 -0
- /package/{dist/memory → packages/memory/dist}/memory-hooks.js +0 -0
- /package/{dist/memory → packages/memory/dist}/service.d.ts +0 -0
- /package/{dist/memory → packages/memory/dist}/service.js +0 -0
- /package/{dist/memory → packages/memory/dist}/types.d.ts +0 -0
- /package/{dist/memory → packages/memory/dist}/types.js +0 -0
- /package/{dist/policy → packages/policy/dist}/agent-policy.d.ts +0 -0
- /package/{dist/policy → packages/policy/dist}/cloud-policy-fetcher.d.ts +0 -0
- /package/{dist/policy → packages/policy/dist}/cloud-policy-fetcher.js +0 -0
- /package/{dist/utils → packages/protocol/dist}/id-generator.d.ts +0 -0
- /package/{dist/utils → packages/protocol/dist}/id-generator.js +0 -0
- /package/{dist/protocol → packages/protocol/dist}/relay-pty-schemas.js +0 -0
- /package/{dist/resiliency → packages/resiliency/dist}/context-persistence.d.ts +0 -0
- /package/{dist/resiliency → packages/resiliency/dist}/context-persistence.js +0 -0
- /package/{dist/resiliency → packages/resiliency/dist}/crash-insights.d.ts +0 -0
- /package/{dist/resiliency → packages/resiliency/dist}/crash-insights.js +0 -0
- /package/{dist/resiliency → packages/resiliency/dist}/gossip-health.d.ts +0 -0
- /package/{dist/resiliency → packages/resiliency/dist}/gossip-health.js +0 -0
- /package/{dist/resiliency → packages/resiliency/dist}/health-monitor.d.ts +0 -0
- /package/{dist/resiliency → packages/resiliency/dist}/health-monitor.js +0 -0
- /package/{dist/resiliency → packages/resiliency/dist}/index.d.ts +0 -0
- /package/{dist/resiliency → packages/resiliency/dist}/index.js +0 -0
- /package/{dist/resiliency → packages/resiliency/dist}/leader-watchdog.d.ts +0 -0
- /package/{dist/resiliency → packages/resiliency/dist}/leader-watchdog.js +0 -0
- /package/{dist/resiliency → packages/resiliency/dist}/logger.d.ts +0 -0
- /package/{dist/resiliency → packages/resiliency/dist}/logger.js +0 -0
- /package/{dist/resiliency → packages/resiliency/dist}/memory-monitor.d.ts +0 -0
- /package/{dist/resiliency → packages/resiliency/dist}/metrics.d.ts +0 -0
- /package/{dist/resiliency → packages/resiliency/dist}/metrics.js +0 -0
- /package/{dist/resiliency → packages/resiliency/dist}/provider-context.js +0 -0
- /package/{dist/resiliency → packages/resiliency/dist}/stateless-lead.d.ts +0 -0
- /package/{dist/resiliency → packages/resiliency/dist}/stateless-lead.js +0 -0
- /package/{dist/resiliency → packages/resiliency/dist}/supervisor.d.ts +0 -0
- /package/{dist/resiliency → packages/resiliency/dist}/supervisor.js +0 -0
- /package/{dist/state → packages/state/dist}/agent-state.d.ts +0 -0
- /package/{dist/storage → packages/storage/dist}/adapter.js +0 -0
- /package/{dist/storage → packages/storage/dist}/dead-letter-queue.d.ts +0 -0
- /package/{dist/storage → packages/storage/dist}/dead-letter-queue.js +0 -0
- /package/{dist/storage → packages/storage/dist}/dlq-adapter.d.ts +0 -0
- /package/{dist/storage → packages/storage/dist}/dlq-adapter.js +0 -0
- /package/{dist/trajectory → packages/trajectory/dist}/integration.d.ts +0 -0
- /package/{dist/utils → packages/utils/dist}/command-resolver.d.ts +0 -0
- /package/{dist/utils → packages/utils/dist}/git-remote.d.ts +0 -0
- /package/{dist/utils → packages/utils/dist}/git-remote.js +0 -0
- /package/{dist/utils → packages/utils/dist}/logger.d.ts +0 -0
- /package/{dist/utils → packages/utils/dist}/logger.js +0 -0
- /package/{dist/utils → packages/utils/dist}/name-generator.d.ts +0 -0
- /package/{dist/utils → packages/utils/dist}/name-generator.js +0 -0
- /package/{dist/utils → packages/utils/dist}/precompiled-patterns.d.ts +0 -0
- /package/{dist/utils → packages/utils/dist}/precompiled-patterns.js +0 -0
- /package/{dist/utils → packages/utils/dist}/update-checker.d.ts +0 -0
- /package/{dist/utils → packages/utils/dist}/update-checker.js +0 -0
- /package/{dist/wrapper → packages/wrapper/dist}/auth-detection.d.ts +0 -0
- /package/{dist/wrapper → packages/wrapper/dist}/auth-detection.js +0 -0
- /package/{dist/wrapper → packages/wrapper/dist}/inbox.d.ts +0 -0
- /package/{dist/wrapper → packages/wrapper/dist}/inbox.js +0 -0
- /package/{dist/wrapper → packages/wrapper/dist}/prompt-composer.d.ts +0 -0
- /package/{dist/wrapper → packages/wrapper/dist}/prompt-composer.js +0 -0
- /package/{dist/utils → packages/wrapper/dist}/tmux-resolver.d.ts +0 -0
- /package/{dist/utils → packages/wrapper/dist}/tmux-resolver.js +0 -0
|
@@ -0,0 +1,3089 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Agent Relay CLI
|
|
4
|
+
*
|
|
5
|
+
* Commands:
|
|
6
|
+
* relay claude - Start daemon + Dashboard coordinator with Claude
|
|
7
|
+
* relay codex - Start daemon + Dasbboard coordinator with Codex
|
|
8
|
+
* relay create-agent <cmd> - Wrap agent with real-time messaging
|
|
9
|
+
* relay create-agent -n Name cmd - Wrap with specific agent name
|
|
10
|
+
* relay up - Start daemon + dashboard
|
|
11
|
+
* relay read <id> - Read full message by ID
|
|
12
|
+
* relay agents - List connected agents
|
|
13
|
+
* relay who - Show currently active agents
|
|
14
|
+
*/
|
|
15
|
+
import { Command } from 'commander';
|
|
16
|
+
import { config as dotenvConfig } from 'dotenv';
|
|
17
|
+
import { Daemon } from '@agent-relay/daemon';
|
|
18
|
+
import { RelayClient } from '@agent-relay/sdk';
|
|
19
|
+
import { RelayPtyOrchestrator, getTmuxPath } from '@agent-relay/wrapper';
|
|
20
|
+
import { AgentSpawner, readWorkersMetadata, getWorkerLogsDir, selectShadowCli } from '@agent-relay/bridge';
|
|
21
|
+
import { generateAgentName, checkForUpdatesInBackground, checkForUpdates } from '@agent-relay/utils';
|
|
22
|
+
import { getShadowForAgent } from '@agent-relay/config';
|
|
23
|
+
import fs from 'node:fs';
|
|
24
|
+
import path from 'node:path';
|
|
25
|
+
import { promisify } from 'node:util';
|
|
26
|
+
import { exec } from 'node:child_process';
|
|
27
|
+
import { fileURLToPath } from 'node:url';
|
|
28
|
+
dotenvConfig();
|
|
29
|
+
const DEFAULT_DASHBOARD_PORT = process.env.AGENT_RELAY_DASHBOARD_PORT || '3888';
|
|
30
|
+
// Read version from package.json
|
|
31
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
32
|
+
const __dirname = path.dirname(__filename);
|
|
33
|
+
// Find package.json by walking up from current directory
|
|
34
|
+
// Works for both src/cli/ and dist/src/cli/ locations
|
|
35
|
+
function findPackageJson(startDir) {
|
|
36
|
+
let dir = startDir;
|
|
37
|
+
while (dir !== path.dirname(dir)) {
|
|
38
|
+
const candidate = path.join(dir, 'package.json');
|
|
39
|
+
if (fs.existsSync(candidate)) {
|
|
40
|
+
return candidate;
|
|
41
|
+
}
|
|
42
|
+
dir = path.dirname(dir);
|
|
43
|
+
}
|
|
44
|
+
throw new Error('Could not find package.json');
|
|
45
|
+
}
|
|
46
|
+
const packageJsonPath = findPackageJson(__dirname);
|
|
47
|
+
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf-8'));
|
|
48
|
+
const VERSION = packageJson.version;
|
|
49
|
+
const execAsync = promisify(exec);
|
|
50
|
+
// Check for updates in background (non-blocking)
|
|
51
|
+
// Only show notification for interactive commands, not when wrapping agents or running update
|
|
52
|
+
const interactiveCommands = ['up', 'down', 'status', 'agents', 'who', 'version', '--version', '-V', '--help', '-h', 'create-agent', 'claude', 'codex'];
|
|
53
|
+
const shouldCheckUpdates = process.argv.length > 2 &&
|
|
54
|
+
interactiveCommands.includes(process.argv[2]);
|
|
55
|
+
if (shouldCheckUpdates) {
|
|
56
|
+
checkForUpdatesInBackground(VERSION);
|
|
57
|
+
}
|
|
58
|
+
const program = new Command();
|
|
59
|
+
function pidFilePathForSocket(socketPath) {
|
|
60
|
+
return `${socketPath}.pid`;
|
|
61
|
+
}
|
|
62
|
+
program
|
|
63
|
+
.name('agent-relay')
|
|
64
|
+
.description('Agent-to-agent messaging')
|
|
65
|
+
.version(VERSION, '-V, --version', 'Output the version number');
|
|
66
|
+
// create-agent - Wrap agent with real-time messaging
|
|
67
|
+
program
|
|
68
|
+
.command('create-agent')
|
|
69
|
+
.description('Wrap an agent with real-time messaging')
|
|
70
|
+
.option('-n, --name <name>', 'Agent name (auto-generated if not set)')
|
|
71
|
+
.option('-d, --debug', 'Enable debug output')
|
|
72
|
+
.option('--prefix <pattern>', 'Relay prefix pattern (default: ->relay:)')
|
|
73
|
+
.option('--dashboard-port <port>', 'Dashboard port for spawn/release API (auto-detected if not set)')
|
|
74
|
+
.option('--shadow <name>', 'Spawn a shadow agent with this name that monitors the primary')
|
|
75
|
+
.option('--shadow-role <role>', 'Shadow role: reviewer, auditor, or triggers (comma-separated: SESSION_END,CODE_WRITTEN,REVIEW_REQUEST,EXPLICIT_ASK,ALL_MESSAGES)')
|
|
76
|
+
.option('--skip-instructions', 'Skip initial instruction injection (use with --append-system-prompt)')
|
|
77
|
+
.argument('<command...>', 'Command to wrap (e.g., claude)')
|
|
78
|
+
.action(async (commandParts, options) => {
|
|
79
|
+
const { ensureProjectDir } = await import('@agent-relay/config');
|
|
80
|
+
const { findAgentConfig, isClaudeCli, buildClaudeArgs } = await import('@agent-relay/config');
|
|
81
|
+
// ensureProjectDir creates .agent-relay/ and adds to .gitignore on first run
|
|
82
|
+
const paths = ensureProjectDir();
|
|
83
|
+
const [mainCommand, ...commandArgs] = commandParts;
|
|
84
|
+
const agentName = options.name ?? generateAgentName();
|
|
85
|
+
console.error(`Agent: ${agentName}`);
|
|
86
|
+
console.error(`Project: ${paths.projectId}`);
|
|
87
|
+
// Auto-detect agent config and inject --model/--agent for Claude CLI
|
|
88
|
+
let finalArgs = commandArgs;
|
|
89
|
+
if (isClaudeCli(mainCommand)) {
|
|
90
|
+
const config = findAgentConfig(agentName, paths.projectRoot);
|
|
91
|
+
if (config) {
|
|
92
|
+
console.error(`Agent config: ${config.configPath}`);
|
|
93
|
+
if (config.model) {
|
|
94
|
+
console.error(`Model: ${config.model}`);
|
|
95
|
+
}
|
|
96
|
+
finalArgs = buildClaudeArgs(agentName, commandArgs, paths.projectRoot);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
// Determine dashboard port for spawn/release API
|
|
100
|
+
// Priority: CLI flag > env var > auto-detect default port
|
|
101
|
+
let dashboardPort;
|
|
102
|
+
if (options.dashboardPort) {
|
|
103
|
+
dashboardPort = parseInt(options.dashboardPort, 10);
|
|
104
|
+
}
|
|
105
|
+
else {
|
|
106
|
+
// Try to detect if dashboard is running at common ports
|
|
107
|
+
const portsToTry = [
|
|
108
|
+
parseInt(DEFAULT_DASHBOARD_PORT, 10),
|
|
109
|
+
3889, 3890, 3891, // Common fallback ports when default is in use
|
|
110
|
+
];
|
|
111
|
+
for (const port of portsToTry) {
|
|
112
|
+
try {
|
|
113
|
+
const response = await fetch(`http://localhost:${port}/api/health`, {
|
|
114
|
+
method: 'GET',
|
|
115
|
+
signal: AbortSignal.timeout(300), // Quick timeout for detection
|
|
116
|
+
});
|
|
117
|
+
if (response.ok) {
|
|
118
|
+
const health = await response.json();
|
|
119
|
+
if (health.status === 'healthy') {
|
|
120
|
+
dashboardPort = port;
|
|
121
|
+
console.error(`Dashboard detected: http://localhost:${dashboardPort}`);
|
|
122
|
+
break;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
catch {
|
|
127
|
+
// Try next port
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
// Create spawner as fallback for direct spawn (if dashboard API not available)
|
|
132
|
+
const spawner = new AgentSpawner(paths.projectRoot, undefined, dashboardPort);
|
|
133
|
+
const wrapper = new RelayPtyOrchestrator({
|
|
134
|
+
name: agentName,
|
|
135
|
+
command: mainCommand,
|
|
136
|
+
args: finalArgs,
|
|
137
|
+
socketPath: paths.socketPath,
|
|
138
|
+
cwd: paths.projectRoot,
|
|
139
|
+
relayPrefix: options.prefix,
|
|
140
|
+
skipInstructions: options.skipInstructions,
|
|
141
|
+
streamLogs: true,
|
|
142
|
+
// Use dashboard API for spawn/release when available (preferred - works from any context)
|
|
143
|
+
dashboardPort,
|
|
144
|
+
// Wire up spawn/release callbacks as fallback (if no dashboardPort)
|
|
145
|
+
onSpawn: async (workerName, workerCli, task) => {
|
|
146
|
+
console.error(`[${agentName}] Spawning ${workerName} (${workerCli})...`);
|
|
147
|
+
const result = await spawner.spawn({
|
|
148
|
+
name: workerName,
|
|
149
|
+
cli: workerCli,
|
|
150
|
+
task,
|
|
151
|
+
// No team by default - agents are flat unless team is specified
|
|
152
|
+
});
|
|
153
|
+
if (result.success) {
|
|
154
|
+
console.error(`[${agentName}] ✓ Spawned ${workerName} [pid: ${result.pid}]`);
|
|
155
|
+
}
|
|
156
|
+
else {
|
|
157
|
+
console.error(`[${agentName}] ✗ Failed to spawn ${workerName}: ${result.error}`);
|
|
158
|
+
}
|
|
159
|
+
},
|
|
160
|
+
onRelease: async (workerName) => {
|
|
161
|
+
console.error(`[${agentName}] Releasing ${workerName}...`);
|
|
162
|
+
const released = await spawner.release(workerName);
|
|
163
|
+
if (released) {
|
|
164
|
+
console.error(`[${agentName}] ✓ Released ${workerName}`);
|
|
165
|
+
}
|
|
166
|
+
else {
|
|
167
|
+
console.error(`[${agentName}] ✗ Worker ${workerName} not found`);
|
|
168
|
+
}
|
|
169
|
+
},
|
|
170
|
+
});
|
|
171
|
+
process.on('SIGINT', async () => {
|
|
172
|
+
await spawner.releaseAll();
|
|
173
|
+
await wrapper.stop();
|
|
174
|
+
process.exit(0);
|
|
175
|
+
});
|
|
176
|
+
await wrapper.start();
|
|
177
|
+
let shadowName;
|
|
178
|
+
let shadowRole;
|
|
179
|
+
let speakOn;
|
|
180
|
+
let shadowCli;
|
|
181
|
+
let shadowPrompt;
|
|
182
|
+
const rolePresets = {
|
|
183
|
+
reviewer: ['CODE_WRITTEN', 'REVIEW_REQUEST', 'EXPLICIT_ASK'],
|
|
184
|
+
auditor: ['SESSION_END', 'EXPLICIT_ASK'],
|
|
185
|
+
active: ['ALL_MESSAGES'],
|
|
186
|
+
};
|
|
187
|
+
if (options.shadow) {
|
|
188
|
+
// CLI flags provided
|
|
189
|
+
shadowName = options.shadow;
|
|
190
|
+
const role = options.shadowRole || 'EXPLICIT_ASK';
|
|
191
|
+
shadowRole = role;
|
|
192
|
+
if (rolePresets[role.toLowerCase()]) {
|
|
193
|
+
speakOn = rolePresets[role.toLowerCase()];
|
|
194
|
+
}
|
|
195
|
+
else {
|
|
196
|
+
speakOn = role.split(',').map((s) => s.trim().toUpperCase());
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
else {
|
|
200
|
+
// Check config file for shadow configuration
|
|
201
|
+
const shadowConfig = getShadowForAgent(paths.projectRoot, agentName);
|
|
202
|
+
if (shadowConfig) {
|
|
203
|
+
shadowName = shadowConfig.shadowName;
|
|
204
|
+
shadowRole = shadowConfig.roleName;
|
|
205
|
+
speakOn = shadowConfig.speakOn;
|
|
206
|
+
shadowCli = shadowConfig.cli;
|
|
207
|
+
shadowPrompt = shadowConfig.prompt;
|
|
208
|
+
console.error(`Shadow config: ${shadowName} (from .agent-relay.json)`);
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
// Spawn shadow if configured
|
|
212
|
+
if (shadowName && speakOn) {
|
|
213
|
+
// Decide how to run the shadow (subagent for Claude/OpenCode primaries)
|
|
214
|
+
let shadowSelection = null;
|
|
215
|
+
try {
|
|
216
|
+
shadowSelection = await selectShadowCli(mainCommand, { preferredShadowCli: shadowCli });
|
|
217
|
+
console.error(`[shadow] Mode: ${shadowSelection.mode} via ${shadowSelection.command || shadowSelection.cli} (primary: ${mainCommand})`);
|
|
218
|
+
}
|
|
219
|
+
catch (err) {
|
|
220
|
+
console.error(`[shadow] Shadow CLI selection failed: ${err.message}`);
|
|
221
|
+
}
|
|
222
|
+
// Subagent mode: do not spawn a separate shadow process
|
|
223
|
+
if (shadowSelection?.mode === 'subagent') {
|
|
224
|
+
console.error(`[shadow] ${shadowName} will run as ${shadowSelection.cli} subagent inside ${agentName}; no separate process spawned`);
|
|
225
|
+
return;
|
|
226
|
+
}
|
|
227
|
+
console.error(`Shadow: ${shadowName} (shadowing ${agentName}, speakOn: ${speakOn.join(',')})`);
|
|
228
|
+
// Wait for primary to register before spawning shadow
|
|
229
|
+
await new Promise(r => setTimeout(r, 3000));
|
|
230
|
+
// Build shadow task prompt
|
|
231
|
+
const defaultPrompt = `You are a shadow agent monitoring "${agentName}". You receive copies of their messages. Your role: ${shadowRole || 'observer'}. Stay passive unless your triggers activate.`;
|
|
232
|
+
const shadowTask = shadowPrompt || defaultPrompt;
|
|
233
|
+
const result = await spawner.spawn({
|
|
234
|
+
name: shadowName,
|
|
235
|
+
cli: shadowSelection?.command || shadowCli || mainCommand,
|
|
236
|
+
task: shadowTask,
|
|
237
|
+
shadowOf: agentName,
|
|
238
|
+
shadowSpeakOn: speakOn,
|
|
239
|
+
});
|
|
240
|
+
if (result.success) {
|
|
241
|
+
console.error(`Shadow ${shadowName} started [pid: ${result.pid}]`);
|
|
242
|
+
}
|
|
243
|
+
else {
|
|
244
|
+
console.error(`Failed to spawn shadow ${shadowName}: ${result.error}`);
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
});
|
|
248
|
+
// up - Start daemon (dashboard disabled by default)
|
|
249
|
+
program
|
|
250
|
+
.command('up')
|
|
251
|
+
.description('Start daemon (use --dashboard to enable web dashboard)')
|
|
252
|
+
.option('--dashboard', 'Enable web dashboard (disabled by default)')
|
|
253
|
+
.option('--port <port>', 'Dashboard port (requires --dashboard)', DEFAULT_DASHBOARD_PORT)
|
|
254
|
+
.option('--spawn', 'Force spawn all agents from teams.json')
|
|
255
|
+
.option('--no-spawn', 'Do not auto-spawn agents (just start daemon)')
|
|
256
|
+
.option('--watch', 'Auto-restart daemon on crash (supervisor mode)')
|
|
257
|
+
.option('--max-restarts <n>', 'Max restarts in 60s before giving up (default: 5)', '5')
|
|
258
|
+
.action(async (options) => {
|
|
259
|
+
// If --watch is specified, run in supervisor mode
|
|
260
|
+
if (options.watch) {
|
|
261
|
+
const { spawn } = await import('node:child_process');
|
|
262
|
+
const maxRestarts = parseInt(options.maxRestarts, 10) || 5;
|
|
263
|
+
const restartWindow = 60_000; // 60 seconds
|
|
264
|
+
const restartTimes = [];
|
|
265
|
+
let child = null;
|
|
266
|
+
let shuttingDown = false;
|
|
267
|
+
const startDaemon = () => {
|
|
268
|
+
// Build args without --watch to prevent infinite recursion
|
|
269
|
+
const args = ['up'];
|
|
270
|
+
if (options.dashboard === true) {
|
|
271
|
+
args.push('--dashboard');
|
|
272
|
+
if (options.port)
|
|
273
|
+
args.push('--port', options.port);
|
|
274
|
+
}
|
|
275
|
+
if (options.spawn === true)
|
|
276
|
+
args.push('--spawn');
|
|
277
|
+
if (options.spawn === false)
|
|
278
|
+
args.push('--no-spawn');
|
|
279
|
+
console.log(`[supervisor] Starting daemon...`);
|
|
280
|
+
child = spawn(process.execPath, [process.argv[1], ...args], {
|
|
281
|
+
stdio: 'inherit',
|
|
282
|
+
env: { ...process.env, AGENT_RELAY_SUPERVISED: '1' },
|
|
283
|
+
});
|
|
284
|
+
child.on('exit', (code, signal) => {
|
|
285
|
+
if (shuttingDown) {
|
|
286
|
+
process.exit(0);
|
|
287
|
+
return;
|
|
288
|
+
}
|
|
289
|
+
const now = Date.now();
|
|
290
|
+
restartTimes.push(now);
|
|
291
|
+
// Remove restarts outside the window
|
|
292
|
+
while (restartTimes.length > 0 && restartTimes[0] < now - restartWindow) {
|
|
293
|
+
restartTimes.shift();
|
|
294
|
+
}
|
|
295
|
+
if (restartTimes.length >= maxRestarts) {
|
|
296
|
+
console.error(`[supervisor] Daemon crashed ${maxRestarts} times in ${restartWindow / 1000}s, giving up`);
|
|
297
|
+
process.exit(1);
|
|
298
|
+
}
|
|
299
|
+
const exitReason = signal ? `signal ${signal}` : `code ${code}`;
|
|
300
|
+
console.log(`[supervisor] Daemon exited (${exitReason}), restarting in 2s... (${restartTimes.length}/${maxRestarts} restarts)`);
|
|
301
|
+
setTimeout(startDaemon, 2000);
|
|
302
|
+
});
|
|
303
|
+
};
|
|
304
|
+
process.on('SIGINT', () => {
|
|
305
|
+
console.log('\n[supervisor] Stopping...');
|
|
306
|
+
shuttingDown = true;
|
|
307
|
+
if (child)
|
|
308
|
+
child.kill('SIGINT');
|
|
309
|
+
});
|
|
310
|
+
process.on('SIGTERM', () => {
|
|
311
|
+
shuttingDown = true;
|
|
312
|
+
if (child)
|
|
313
|
+
child.kill('SIGTERM');
|
|
314
|
+
});
|
|
315
|
+
startDaemon();
|
|
316
|
+
return;
|
|
317
|
+
}
|
|
318
|
+
const { ensureProjectDir } = await import('@agent-relay/config');
|
|
319
|
+
const { loadTeamsConfig } = await import('@agent-relay/config');
|
|
320
|
+
const { AgentSpawner } = await import('@agent-relay/bridge');
|
|
321
|
+
const paths = ensureProjectDir();
|
|
322
|
+
const socketPath = paths.socketPath;
|
|
323
|
+
const dbPath = paths.dbPath;
|
|
324
|
+
const pidFilePath = pidFilePathForSocket(socketPath);
|
|
325
|
+
console.log(`Project: ${paths.projectRoot}`);
|
|
326
|
+
console.log(`Socket: ${socketPath}`);
|
|
327
|
+
// Load teams.json if present
|
|
328
|
+
const teamsConfig = loadTeamsConfig(paths.projectRoot);
|
|
329
|
+
if (teamsConfig) {
|
|
330
|
+
console.log(`Team: ${teamsConfig.team} (${teamsConfig.agents.length} agents defined)`);
|
|
331
|
+
}
|
|
332
|
+
const daemon = new Daemon({
|
|
333
|
+
socketPath,
|
|
334
|
+
pidFilePath,
|
|
335
|
+
storagePath: dbPath,
|
|
336
|
+
teamDir: paths.teamDir,
|
|
337
|
+
// TODO: Add daemon-based spawning support when SDK extraction is complete
|
|
338
|
+
// See: docs/SDK-MIGRATION-PLAN.md
|
|
339
|
+
});
|
|
340
|
+
// Create spawner for auto-spawn (will be initialized after dashboard starts)
|
|
341
|
+
let spawner = null;
|
|
342
|
+
// Track if we're already shutting down to prevent double-cleanup
|
|
343
|
+
let isShuttingDown = false;
|
|
344
|
+
const gracefulShutdown = async (reason) => {
|
|
345
|
+
if (isShuttingDown)
|
|
346
|
+
return;
|
|
347
|
+
isShuttingDown = true;
|
|
348
|
+
console.log(`\n[daemon] ${reason}, shutting down...`);
|
|
349
|
+
try {
|
|
350
|
+
if (spawner)
|
|
351
|
+
await spawner.releaseAll();
|
|
352
|
+
await daemon.stop();
|
|
353
|
+
}
|
|
354
|
+
catch (err) {
|
|
355
|
+
console.error('[daemon] Error during shutdown:', err);
|
|
356
|
+
}
|
|
357
|
+
process.exit(1);
|
|
358
|
+
};
|
|
359
|
+
// Handle uncaught exceptions - log and exit (supervisor will restart)
|
|
360
|
+
process.on('uncaughtException', (err) => {
|
|
361
|
+
console.error('[daemon] Uncaught exception:', err);
|
|
362
|
+
gracefulShutdown('Uncaught exception');
|
|
363
|
+
});
|
|
364
|
+
// Handle unhandled promise rejections
|
|
365
|
+
process.on('unhandledRejection', (reason, promise) => {
|
|
366
|
+
console.error('[daemon] Unhandled rejection at:', promise, 'reason:', reason);
|
|
367
|
+
// Don't exit on unhandled rejections - just log them
|
|
368
|
+
// Most are recoverable (e.g., failed message delivery)
|
|
369
|
+
});
|
|
370
|
+
process.on('SIGINT', async () => {
|
|
371
|
+
console.log('\nStopping...');
|
|
372
|
+
if (spawner) {
|
|
373
|
+
await spawner.releaseAll();
|
|
374
|
+
}
|
|
375
|
+
await daemon.stop();
|
|
376
|
+
process.exit(0);
|
|
377
|
+
});
|
|
378
|
+
process.on('SIGTERM', async () => {
|
|
379
|
+
if (spawner) {
|
|
380
|
+
await spawner.releaseAll();
|
|
381
|
+
}
|
|
382
|
+
await daemon.stop();
|
|
383
|
+
process.exit(0);
|
|
384
|
+
});
|
|
385
|
+
try {
|
|
386
|
+
await daemon.start();
|
|
387
|
+
console.log('Daemon started.');
|
|
388
|
+
let dashboardPort;
|
|
389
|
+
// Dashboard is disabled by default (use --dashboard to enable)
|
|
390
|
+
// Dashboard is an optional separate package: @agent-relay/dashboard
|
|
391
|
+
if (options.dashboard === true) {
|
|
392
|
+
const port = parseInt(options.port, 10);
|
|
393
|
+
try {
|
|
394
|
+
const { startDashboard } = await import('@agent-relay/dashboard');
|
|
395
|
+
dashboardPort = await startDashboard({
|
|
396
|
+
port,
|
|
397
|
+
dataDir: paths.dataDir,
|
|
398
|
+
teamDir: paths.teamDir,
|
|
399
|
+
dbPath,
|
|
400
|
+
enableSpawner: true,
|
|
401
|
+
projectRoot: paths.projectRoot,
|
|
402
|
+
// Pass spawn tracking callbacks so messages can be queued before HELLO completes
|
|
403
|
+
onMarkSpawning: (name) => daemon.markSpawning(name),
|
|
404
|
+
onClearSpawning: (name) => daemon.clearSpawning(name),
|
|
405
|
+
});
|
|
406
|
+
console.log(`Dashboard: http://localhost:${dashboardPort}`);
|
|
407
|
+
// Hook daemon log output to dashboard WebSocket
|
|
408
|
+
daemon.onLogOutput = (agentName, data, _timestamp) => {
|
|
409
|
+
const broadcast = global.__broadcastLogOutput;
|
|
410
|
+
if (broadcast) {
|
|
411
|
+
broadcast(agentName, data);
|
|
412
|
+
}
|
|
413
|
+
};
|
|
414
|
+
}
|
|
415
|
+
catch (err) {
|
|
416
|
+
const error = err;
|
|
417
|
+
if (error.code === 'ERR_MODULE_NOT_FOUND' || error.code === 'MODULE_NOT_FOUND') {
|
|
418
|
+
console.error(`
|
|
419
|
+
Dashboard package not installed.
|
|
420
|
+
|
|
421
|
+
The dashboard has moved to a separate optional package.
|
|
422
|
+
To use the web dashboard, install it:
|
|
423
|
+
|
|
424
|
+
npm install -g @agent-relay/dashboard
|
|
425
|
+
|
|
426
|
+
Then try again:
|
|
427
|
+
|
|
428
|
+
agent-relay up --dashboard
|
|
429
|
+
|
|
430
|
+
Or run without dashboard:
|
|
431
|
+
|
|
432
|
+
agent-relay up
|
|
433
|
+
`);
|
|
434
|
+
process.exit(1);
|
|
435
|
+
}
|
|
436
|
+
throw err;
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
// Determine if we should auto-spawn agents
|
|
440
|
+
// --spawn: force spawn
|
|
441
|
+
// --no-spawn: never spawn
|
|
442
|
+
// Neither: check teamsConfig.autoSpawn
|
|
443
|
+
const shouldSpawn = options.spawn === true
|
|
444
|
+
? true
|
|
445
|
+
: options.spawn === false
|
|
446
|
+
? false
|
|
447
|
+
: teamsConfig?.autoSpawn ?? false;
|
|
448
|
+
if (shouldSpawn && teamsConfig && teamsConfig.agents.length > 0) {
|
|
449
|
+
console.log('');
|
|
450
|
+
console.log('Auto-spawning agents from teams.json...');
|
|
451
|
+
spawner = new AgentSpawner({
|
|
452
|
+
projectRoot: paths.projectRoot,
|
|
453
|
+
dashboardPort,
|
|
454
|
+
onMarkSpawning: (name) => daemon.markSpawning(name),
|
|
455
|
+
onClearSpawning: (name) => daemon.clearSpawning(name),
|
|
456
|
+
});
|
|
457
|
+
for (const agent of teamsConfig.agents) {
|
|
458
|
+
console.log(` Spawning ${agent.name} (${agent.cli})...`);
|
|
459
|
+
const result = await spawner.spawn({
|
|
460
|
+
name: agent.name,
|
|
461
|
+
cli: agent.cli,
|
|
462
|
+
task: agent.task ?? '',
|
|
463
|
+
team: teamsConfig.team,
|
|
464
|
+
});
|
|
465
|
+
if (result.success) {
|
|
466
|
+
console.log(` ✓ ${agent.name} started [pid: ${result.pid}]`);
|
|
467
|
+
}
|
|
468
|
+
else {
|
|
469
|
+
console.error(` ✗ ${agent.name} failed: ${result.error}`);
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
console.log('');
|
|
473
|
+
}
|
|
474
|
+
else if (options.spawn === true && !teamsConfig) {
|
|
475
|
+
console.warn('Warning: --spawn specified but no teams.json found');
|
|
476
|
+
}
|
|
477
|
+
console.log('Press Ctrl+C to stop.');
|
|
478
|
+
await new Promise(() => { });
|
|
479
|
+
}
|
|
480
|
+
catch (err) {
|
|
481
|
+
console.error('Failed:', err);
|
|
482
|
+
process.exit(1);
|
|
483
|
+
}
|
|
484
|
+
});
|
|
485
|
+
// down - Stop daemon
|
|
486
|
+
program
|
|
487
|
+
.command('down')
|
|
488
|
+
.description('Stop daemon')
|
|
489
|
+
.action(async () => {
|
|
490
|
+
const { getProjectPaths } = await import('@agent-relay/config');
|
|
491
|
+
const paths = getProjectPaths();
|
|
492
|
+
const pidPath = pidFilePathForSocket(paths.socketPath);
|
|
493
|
+
if (!fs.existsSync(pidPath)) {
|
|
494
|
+
console.log('Not running');
|
|
495
|
+
return;
|
|
496
|
+
}
|
|
497
|
+
const pid = Number(fs.readFileSync(pidPath, 'utf-8').trim());
|
|
498
|
+
try {
|
|
499
|
+
process.kill(pid, 'SIGTERM');
|
|
500
|
+
console.log('Stopped');
|
|
501
|
+
}
|
|
502
|
+
catch {
|
|
503
|
+
fs.unlinkSync(pidPath);
|
|
504
|
+
console.log('Cleaned up stale pid');
|
|
505
|
+
}
|
|
506
|
+
});
|
|
507
|
+
// System prompt for Dashboard agent - plain text to avoid shell escaping issues
|
|
508
|
+
const MEGA_SYSTEM_PROMPT = [
|
|
509
|
+
'You are Dashboard, a lead coordinator in agent-relay.',
|
|
510
|
+
'Your PRIMARY job is to delegate - you should almost NEVER do implementation work yourself.',
|
|
511
|
+
'ALWAYS SPAWN AGENTS: For any non-trivial task, spawn specialized workers.',
|
|
512
|
+
].join(' ');
|
|
513
|
+
// Helper function for starting Dashboard coordinator with a specific provider
|
|
514
|
+
async function startDashboardCoordinator(operator) {
|
|
515
|
+
const { spawn } = await import('node:child_process');
|
|
516
|
+
const { getProjectPaths } = await import('@agent-relay/config');
|
|
517
|
+
const paths = getProjectPaths();
|
|
518
|
+
console.log(`Starting Dashboard with ${operator}...`);
|
|
519
|
+
console.log(`Project: ${paths.projectRoot}`);
|
|
520
|
+
// Step 1: Check if daemon is already running, start if needed
|
|
521
|
+
console.log('\n[1/3] Checking daemon...');
|
|
522
|
+
// Check if socket exists (daemon running)
|
|
523
|
+
const socketExists = fs.existsSync(paths.socketPath);
|
|
524
|
+
// Ports to try for dashboard detection
|
|
525
|
+
const portsToTry = [
|
|
526
|
+
parseInt(DEFAULT_DASHBOARD_PORT, 10),
|
|
527
|
+
3889, 3890, 3891,
|
|
528
|
+
];
|
|
529
|
+
// Check if dashboard is responding for THIS project
|
|
530
|
+
let dashboardReady = false;
|
|
531
|
+
let detectedPort;
|
|
532
|
+
// Helper to check health at a port
|
|
533
|
+
const checkPort = async (port) => {
|
|
534
|
+
try {
|
|
535
|
+
const response = await fetch(`http://localhost:${port}/api/health`, {
|
|
536
|
+
signal: AbortSignal.timeout(500),
|
|
537
|
+
});
|
|
538
|
+
if (response.ok) {
|
|
539
|
+
const health = await response.json();
|
|
540
|
+
return health.status === 'healthy';
|
|
541
|
+
}
|
|
542
|
+
}
|
|
543
|
+
catch {
|
|
544
|
+
// Port not responding
|
|
545
|
+
}
|
|
546
|
+
return false;
|
|
547
|
+
};
|
|
548
|
+
if (socketExists) {
|
|
549
|
+
for (const port of portsToTry) {
|
|
550
|
+
if (await checkPort(port)) {
|
|
551
|
+
dashboardReady = true;
|
|
552
|
+
detectedPort = port;
|
|
553
|
+
break;
|
|
554
|
+
}
|
|
555
|
+
}
|
|
556
|
+
}
|
|
557
|
+
if (dashboardReady && detectedPort) {
|
|
558
|
+
console.log(`Daemon already running at port ${detectedPort}, reusing...`);
|
|
559
|
+
}
|
|
560
|
+
else {
|
|
561
|
+
console.log('Starting daemon...');
|
|
562
|
+
const daemonProc = spawn(process.execPath, [process.argv[1], 'up', '--dashboard'], {
|
|
563
|
+
stdio: 'ignore',
|
|
564
|
+
detached: true,
|
|
565
|
+
});
|
|
566
|
+
daemonProc.unref();
|
|
567
|
+
// Wait for dashboard to be ready (up to 10 seconds)
|
|
568
|
+
const maxWait = 10000;
|
|
569
|
+
const startTime = Date.now();
|
|
570
|
+
while (Date.now() - startTime < maxWait) {
|
|
571
|
+
await new Promise((resolve) => setTimeout(resolve, 500));
|
|
572
|
+
for (const port of portsToTry) {
|
|
573
|
+
if (await checkPort(port)) {
|
|
574
|
+
dashboardReady = true;
|
|
575
|
+
detectedPort = port;
|
|
576
|
+
break;
|
|
577
|
+
}
|
|
578
|
+
}
|
|
579
|
+
if (dashboardReady)
|
|
580
|
+
break;
|
|
581
|
+
}
|
|
582
|
+
if (!dashboardReady) {
|
|
583
|
+
console.error('Warning: Dashboard may not be fully ready. Spawn might not work.');
|
|
584
|
+
detectedPort = parseInt(DEFAULT_DASHBOARD_PORT, 10); // Fallback
|
|
585
|
+
}
|
|
586
|
+
}
|
|
587
|
+
const dashboardPort = detectedPort || parseInt(DEFAULT_DASHBOARD_PORT, 10);
|
|
588
|
+
// Step 2: Install prpm snippet via npx
|
|
589
|
+
console.log('[2/3] Installing agent-relay snippet...');
|
|
590
|
+
const prpmArgs = operator.toLowerCase() === 'claude'
|
|
591
|
+
? ['prpm', 'install', '@agent-relay/agent-relay-snippet', '--location', 'CLAUDE.md']
|
|
592
|
+
: ['prpm', 'install', '@agent-relay/agent-relay-snippet'];
|
|
593
|
+
try {
|
|
594
|
+
await new Promise((resolve, reject) => {
|
|
595
|
+
const prpmProc = spawn('npx', prpmArgs, {
|
|
596
|
+
stdio: 'inherit',
|
|
597
|
+
});
|
|
598
|
+
prpmProc.on('close', (code) => {
|
|
599
|
+
if (code === 0)
|
|
600
|
+
resolve();
|
|
601
|
+
else
|
|
602
|
+
reject(new Error(`npx prpm exited with code ${code}`));
|
|
603
|
+
});
|
|
604
|
+
prpmProc.on('error', reject);
|
|
605
|
+
});
|
|
606
|
+
}
|
|
607
|
+
catch (err) {
|
|
608
|
+
console.warn(`Warning: prpm install failed: ${err.message}`);
|
|
609
|
+
console.warn('Continuing without snippet installation...');
|
|
610
|
+
}
|
|
611
|
+
// Step 3: Start Dashboard agent with system prompt
|
|
612
|
+
console.log(`[3/3] Starting Dashboard agent with ${operator}...`);
|
|
613
|
+
console.log('');
|
|
614
|
+
const op = operator.toLowerCase();
|
|
615
|
+
// Build CLI-specific arguments for system prompt
|
|
616
|
+
// These args go AFTER the operator command, passed through to the CLI
|
|
617
|
+
let cliArgs = [];
|
|
618
|
+
if (op === 'claude') {
|
|
619
|
+
// Claude: --append-system-prompt <content> (takes content directly, not file)
|
|
620
|
+
cliArgs = ['--append-system-prompt', MEGA_SYSTEM_PROMPT];
|
|
621
|
+
}
|
|
622
|
+
else if (op === 'codex') {
|
|
623
|
+
// Codex: --config developer_instructions="<content>"
|
|
624
|
+
cliArgs = ['--config', `developer_instructions=${MEGA_SYSTEM_PROMPT}`];
|
|
625
|
+
}
|
|
626
|
+
// Use '--' to separate agent-relay options from the command + its args
|
|
627
|
+
// Format: agent-relay create-agent -n Dashboard --skip-instructions --dashboard-port <port> -- claude --append-system-prompt "..."
|
|
628
|
+
const agentProc = spawn(process.execPath, [process.argv[1], 'create-agent', '-n', 'Dashboard', '--skip-instructions', '--dashboard-port', String(dashboardPort), '--', operator, ...cliArgs], { stdio: 'inherit' });
|
|
629
|
+
// Forward signals to agent process
|
|
630
|
+
process.on('SIGINT', () => {
|
|
631
|
+
agentProc.kill('SIGINT');
|
|
632
|
+
});
|
|
633
|
+
process.on('SIGTERM', () => {
|
|
634
|
+
agentProc.kill('SIGTERM');
|
|
635
|
+
});
|
|
636
|
+
agentProc.on('close', (code) => {
|
|
637
|
+
process.exit(code ?? 0);
|
|
638
|
+
});
|
|
639
|
+
}
|
|
640
|
+
// claude - Start daemon and spawn Dashboard coordinator with Claude
|
|
641
|
+
program
|
|
642
|
+
.command('claude')
|
|
643
|
+
.description('Start daemon and Dashboard coordinator with Claude')
|
|
644
|
+
.action(async () => {
|
|
645
|
+
await startDashboardCoordinator('claude');
|
|
646
|
+
});
|
|
647
|
+
// codex - Start daemon and spawn Dashboard coordinator with Codex
|
|
648
|
+
program
|
|
649
|
+
.command('codex')
|
|
650
|
+
.description('Start daemon and Dashboard coordinator with Codex')
|
|
651
|
+
.action(async () => {
|
|
652
|
+
await startDashboardCoordinator('codex');
|
|
653
|
+
});
|
|
654
|
+
// status - Check daemon status
|
|
655
|
+
program
|
|
656
|
+
.command('status')
|
|
657
|
+
.description('Check daemon status')
|
|
658
|
+
.action(async () => {
|
|
659
|
+
const { getProjectPaths } = await import('@agent-relay/config');
|
|
660
|
+
const paths = getProjectPaths();
|
|
661
|
+
const relaySessions = await discoverRelaySessions();
|
|
662
|
+
if (!fs.existsSync(paths.socketPath)) {
|
|
663
|
+
console.log('Status: STOPPED');
|
|
664
|
+
logRelaySessions(relaySessions);
|
|
665
|
+
return;
|
|
666
|
+
}
|
|
667
|
+
const client = new RelayClient({
|
|
668
|
+
agentName: '__status__',
|
|
669
|
+
socketPath: paths.socketPath,
|
|
670
|
+
reconnect: false,
|
|
671
|
+
});
|
|
672
|
+
try {
|
|
673
|
+
await client.connect();
|
|
674
|
+
console.log('Status: RUNNING');
|
|
675
|
+
console.log(`Socket: ${paths.socketPath}`);
|
|
676
|
+
logRelaySessions(relaySessions);
|
|
677
|
+
client.disconnect();
|
|
678
|
+
}
|
|
679
|
+
catch {
|
|
680
|
+
console.log('Status: STOPPED');
|
|
681
|
+
logRelaySessions(relaySessions);
|
|
682
|
+
}
|
|
683
|
+
});
|
|
684
|
+
// agents - List connected agents (from registry file) and spawned workers
|
|
685
|
+
program
|
|
686
|
+
.command('agents')
|
|
687
|
+
.description('List connected agents and spawned workers')
|
|
688
|
+
.option('--all', 'Include internal/CLI agents')
|
|
689
|
+
.option('--remote', 'Include agents from other linked machines (requires cloud link)')
|
|
690
|
+
.option('--json', 'Output as JSON')
|
|
691
|
+
.action(async (options) => {
|
|
692
|
+
const { getProjectPaths } = await import('@agent-relay/config');
|
|
693
|
+
const os = await import('node:os');
|
|
694
|
+
const paths = getProjectPaths();
|
|
695
|
+
const agentsPath = path.join(paths.teamDir, 'agents.json');
|
|
696
|
+
const connectedAgentsPath = path.join(paths.teamDir, 'connected-agents.json');
|
|
697
|
+
// Load registered agents
|
|
698
|
+
const allAgents = loadAgents(agentsPath);
|
|
699
|
+
const agents = options.all
|
|
700
|
+
? allAgents
|
|
701
|
+
: allAgents.filter(isVisibleAgent);
|
|
702
|
+
// Load spawned workers
|
|
703
|
+
const workers = readWorkersMetadata(paths.projectRoot);
|
|
704
|
+
// Load currently connected agents (agents with active socket connections)
|
|
705
|
+
let connectedAgentNames = new Set();
|
|
706
|
+
let connectedUpdatedAt = 0;
|
|
707
|
+
try {
|
|
708
|
+
if (fs.existsSync(connectedAgentsPath)) {
|
|
709
|
+
const connectedData = JSON.parse(fs.readFileSync(connectedAgentsPath, 'utf-8'));
|
|
710
|
+
connectedAgentNames = new Set([
|
|
711
|
+
...(connectedData.agents || []),
|
|
712
|
+
...(connectedData.users || []),
|
|
713
|
+
]);
|
|
714
|
+
connectedUpdatedAt = connectedData.updatedAt || 0;
|
|
715
|
+
}
|
|
716
|
+
}
|
|
717
|
+
catch {
|
|
718
|
+
// If file doesn't exist or is invalid, fall back to registry-based status
|
|
719
|
+
}
|
|
720
|
+
// Check if connected-agents.json is fresh (within 30 seconds)
|
|
721
|
+
const isConnectedAgentsStale = Date.now() - connectedUpdatedAt > 30_000;
|
|
722
|
+
const combined = [];
|
|
723
|
+
// Add registered agents
|
|
724
|
+
agents.forEach((agent) => {
|
|
725
|
+
const worker = workers.find(w => w.name === agent.name);
|
|
726
|
+
// Use connected-agents.json if fresh, otherwise fall back to registry lastSeen
|
|
727
|
+
let status;
|
|
728
|
+
if (!isConnectedAgentsStale && connectedAgentNames.size > 0) {
|
|
729
|
+
// We have fresh connected-agents data - use actual connection status
|
|
730
|
+
status = connectedAgentNames.has(agent.name ?? '') ? 'ONLINE' : 'OFFLINE';
|
|
731
|
+
}
|
|
732
|
+
else {
|
|
733
|
+
// Fall back to registry-based status
|
|
734
|
+
status = getAgentStatus(agent);
|
|
735
|
+
}
|
|
736
|
+
combined.push({
|
|
737
|
+
name: agent.name ?? 'unknown',
|
|
738
|
+
status,
|
|
739
|
+
cli: agent.cli ?? '-',
|
|
740
|
+
lastSeen: agent.lastSeen,
|
|
741
|
+
team: worker?.team,
|
|
742
|
+
pid: worker?.pid,
|
|
743
|
+
location: 'local',
|
|
744
|
+
});
|
|
745
|
+
});
|
|
746
|
+
// Add workers not in registry (orphaned or not yet registered)
|
|
747
|
+
workers.forEach((worker) => {
|
|
748
|
+
const existsInAgents = agents.some(a => a.name === worker.name);
|
|
749
|
+
if (!existsInAgents) {
|
|
750
|
+
combined.push({
|
|
751
|
+
name: worker.name || 'unknown',
|
|
752
|
+
status: 'ONLINE',
|
|
753
|
+
cli: worker.cli || '-',
|
|
754
|
+
team: worker.team,
|
|
755
|
+
pid: worker.pid,
|
|
756
|
+
location: 'local',
|
|
757
|
+
});
|
|
758
|
+
}
|
|
759
|
+
});
|
|
760
|
+
// Include remote agents if --remote flag is set
|
|
761
|
+
if (options.remote) {
|
|
762
|
+
const dataDir = process.env.AGENT_RELAY_DATA_DIR ||
|
|
763
|
+
path.join(os.homedir(), '.local', 'share', 'agent-relay');
|
|
764
|
+
const configPath = path.join(dataDir, 'cloud-config.json');
|
|
765
|
+
if (fs.existsSync(configPath)) {
|
|
766
|
+
try {
|
|
767
|
+
const config = JSON.parse(fs.readFileSync(configPath, 'utf-8'));
|
|
768
|
+
const response = await fetch(`${config.cloudUrl}/api/daemons/agents`, {
|
|
769
|
+
method: 'POST',
|
|
770
|
+
headers: {
|
|
771
|
+
'Authorization': `Bearer ${config.apiKey}`,
|
|
772
|
+
'Content-Type': 'application/json',
|
|
773
|
+
},
|
|
774
|
+
body: JSON.stringify({ agents: [] }),
|
|
775
|
+
});
|
|
776
|
+
if (response.ok) {
|
|
777
|
+
const data = await response.json();
|
|
778
|
+
// Add remote agents (exclude local ones by name)
|
|
779
|
+
const localNames = new Set(combined.map(a => a.name));
|
|
780
|
+
for (const agent of data.allAgents) {
|
|
781
|
+
if (!localNames.has(agent.name)) {
|
|
782
|
+
combined.push({
|
|
783
|
+
name: agent.name,
|
|
784
|
+
status: agent.status.toUpperCase(),
|
|
785
|
+
cli: '-',
|
|
786
|
+
location: agent.daemonName,
|
|
787
|
+
daemonId: agent.daemonId,
|
|
788
|
+
});
|
|
789
|
+
}
|
|
790
|
+
}
|
|
791
|
+
}
|
|
792
|
+
}
|
|
793
|
+
catch (err) {
|
|
794
|
+
console.error('[warn] Failed to fetch remote agents:', err.message);
|
|
795
|
+
}
|
|
796
|
+
}
|
|
797
|
+
else {
|
|
798
|
+
console.error('[warn] Cloud not linked. Run `agent-relay cloud link` to see remote agents.');
|
|
799
|
+
}
|
|
800
|
+
}
|
|
801
|
+
if (options.json) {
|
|
802
|
+
console.log(JSON.stringify(combined, null, 2));
|
|
803
|
+
return;
|
|
804
|
+
}
|
|
805
|
+
if (!combined.length) {
|
|
806
|
+
const hint = options.all ? '' : ' (use --all to include internal/cli agents)';
|
|
807
|
+
console.log(`No agents found. Ensure the daemon is running and agents are connected${hint}.`);
|
|
808
|
+
return;
|
|
809
|
+
}
|
|
810
|
+
const hasRemote = combined.some(a => a.location !== 'local');
|
|
811
|
+
if (hasRemote) {
|
|
812
|
+
console.log('NAME STATUS CLI LOCATION');
|
|
813
|
+
console.log('─'.repeat(55));
|
|
814
|
+
combined.forEach((agent) => {
|
|
815
|
+
const name = agent.name.padEnd(15);
|
|
816
|
+
const status = agent.status.padEnd(8);
|
|
817
|
+
const cli = agent.cli.padEnd(9);
|
|
818
|
+
const location = agent.location ?? 'local';
|
|
819
|
+
console.log(`${name} ${status} ${cli} ${location}`);
|
|
820
|
+
});
|
|
821
|
+
}
|
|
822
|
+
else {
|
|
823
|
+
console.log('NAME STATUS CLI TEAM');
|
|
824
|
+
console.log('─'.repeat(50));
|
|
825
|
+
combined.forEach((agent) => {
|
|
826
|
+
const name = agent.name.padEnd(15);
|
|
827
|
+
const status = agent.status.padEnd(8);
|
|
828
|
+
const cli = agent.cli.padEnd(9);
|
|
829
|
+
const team = agent.team ?? '-';
|
|
830
|
+
console.log(`${name} ${status} ${cli} ${team}`);
|
|
831
|
+
});
|
|
832
|
+
}
|
|
833
|
+
if (workers.length > 0) {
|
|
834
|
+
console.log('');
|
|
835
|
+
console.log('Commands:');
|
|
836
|
+
console.log(' agent-relay agents:logs <name> - View spawned agent output');
|
|
837
|
+
console.log(' agent-relay agents:kill <name> - Kill a spawned agent');
|
|
838
|
+
}
|
|
839
|
+
if (!options.remote) {
|
|
840
|
+
console.log('');
|
|
841
|
+
console.log('Tip: Use --remote to include agents from other linked machines.');
|
|
842
|
+
}
|
|
843
|
+
});
|
|
844
|
+
// who - Show currently active agents (online within last 30s)
|
|
845
|
+
program
|
|
846
|
+
.command('who')
|
|
847
|
+
.description('Show currently active agents (last seen within 30 seconds)')
|
|
848
|
+
.option('--all', 'Include internal/CLI agents')
|
|
849
|
+
.option('--json', 'Output as JSON')
|
|
850
|
+
.action(async (options) => {
|
|
851
|
+
const { getProjectPaths } = await import('@agent-relay/config');
|
|
852
|
+
const paths = getProjectPaths();
|
|
853
|
+
const agentsPath = path.join(paths.teamDir, 'agents.json');
|
|
854
|
+
const allAgents = loadAgents(agentsPath);
|
|
855
|
+
const visibleAgents = options.all
|
|
856
|
+
? allAgents
|
|
857
|
+
: allAgents.filter(a => !isInternalAgent(a.name));
|
|
858
|
+
const onlineAgents = visibleAgents.filter(isAgentOnline);
|
|
859
|
+
if (options.json) {
|
|
860
|
+
console.log(JSON.stringify(onlineAgents.map(a => ({ ...a, status: getAgentStatus(a) })), null, 2));
|
|
861
|
+
return;
|
|
862
|
+
}
|
|
863
|
+
if (!onlineAgents.length) {
|
|
864
|
+
const hint = options.all ? '' : ' (use --all to include internal/cli agents)';
|
|
865
|
+
console.log(`No active agents found${hint}.`);
|
|
866
|
+
return;
|
|
867
|
+
}
|
|
868
|
+
console.log('NAME STATUS CLI LAST SEEN');
|
|
869
|
+
console.log('---------------------------------------------');
|
|
870
|
+
onlineAgents.forEach((agent) => {
|
|
871
|
+
const name = (agent.name ?? 'unknown').padEnd(15);
|
|
872
|
+
const status = getAgentStatus(agent).padEnd(8);
|
|
873
|
+
const cli = (agent.cli ?? '-').padEnd(8);
|
|
874
|
+
const lastSeen = formatRelativeTime(agent.lastSeen);
|
|
875
|
+
console.log(`${name} ${status} ${cli} ${lastSeen}`);
|
|
876
|
+
});
|
|
877
|
+
});
|
|
878
|
+
// read - Read full message by ID (for truncated messages)
|
|
879
|
+
program
|
|
880
|
+
.command('read')
|
|
881
|
+
.description('Read full message by ID (for truncated messages)')
|
|
882
|
+
.argument('<id>', 'Message ID')
|
|
883
|
+
.action(async (messageId) => {
|
|
884
|
+
const { getProjectPaths } = await import('@agent-relay/config');
|
|
885
|
+
const { createStorageAdapter } = await import('@agent-relay/storage/adapter');
|
|
886
|
+
const paths = getProjectPaths();
|
|
887
|
+
const adapter = await createStorageAdapter(paths.dbPath);
|
|
888
|
+
if (!adapter.getMessageById) {
|
|
889
|
+
console.error('Storage does not support message lookup');
|
|
890
|
+
process.exit(1);
|
|
891
|
+
}
|
|
892
|
+
const msg = await adapter.getMessageById(messageId);
|
|
893
|
+
if (!msg) {
|
|
894
|
+
console.error(`Message not found: ${messageId}`);
|
|
895
|
+
process.exit(1);
|
|
896
|
+
}
|
|
897
|
+
console.log(`From: ${msg.from}`);
|
|
898
|
+
console.log(`To: ${msg.to}`);
|
|
899
|
+
console.log(`Time: ${new Date(msg.ts).toISOString()}`);
|
|
900
|
+
console.log('---');
|
|
901
|
+
console.log(msg.body);
|
|
902
|
+
await adapter.close?.();
|
|
903
|
+
});
|
|
904
|
+
// ============================================
|
|
905
|
+
// Hidden commands (for agents, not in --help)
|
|
906
|
+
// ============================================
|
|
907
|
+
// history - Show recent messages (hidden from help, for agent use)
|
|
908
|
+
program
|
|
909
|
+
.command('history', { hidden: true })
|
|
910
|
+
.description('Show recent messages')
|
|
911
|
+
.option('-n, --limit <count>', 'Number of messages to show', '50')
|
|
912
|
+
.option('-f, --from <agent>', 'Filter by sender')
|
|
913
|
+
.option('-t, --to <agent>', 'Filter by recipient')
|
|
914
|
+
.option('--since <time>', 'Since time (e.g., "1h", "2024-01-01")')
|
|
915
|
+
.option('--json', 'Output as JSON')
|
|
916
|
+
.action(async (options) => {
|
|
917
|
+
const { getProjectPaths } = await import('@agent-relay/config');
|
|
918
|
+
const { createStorageAdapter } = await import('@agent-relay/storage/adapter');
|
|
919
|
+
const paths = getProjectPaths();
|
|
920
|
+
const adapter = await createStorageAdapter(paths.dbPath);
|
|
921
|
+
const limit = Number.parseInt(options.limit ?? '50', 10) || 50;
|
|
922
|
+
const sinceTs = parseSince(options.since);
|
|
923
|
+
try {
|
|
924
|
+
const messages = await adapter.getMessages({
|
|
925
|
+
limit,
|
|
926
|
+
from: options.from,
|
|
927
|
+
to: options.to,
|
|
928
|
+
sinceTs,
|
|
929
|
+
order: 'desc',
|
|
930
|
+
});
|
|
931
|
+
if (options.json) {
|
|
932
|
+
const payload = messages.map((m) => ({
|
|
933
|
+
id: m.id,
|
|
934
|
+
ts: m.ts,
|
|
935
|
+
timestamp: new Date(m.ts).toISOString(),
|
|
936
|
+
from: m.from,
|
|
937
|
+
to: m.to,
|
|
938
|
+
topic: m.topic,
|
|
939
|
+
thread: m.thread,
|
|
940
|
+
kind: m.kind,
|
|
941
|
+
body: m.body,
|
|
942
|
+
}));
|
|
943
|
+
console.log(JSON.stringify(payload, null, 2));
|
|
944
|
+
return;
|
|
945
|
+
}
|
|
946
|
+
if (!messages.length) {
|
|
947
|
+
console.log('No messages found.');
|
|
948
|
+
return;
|
|
949
|
+
}
|
|
950
|
+
messages.forEach((msg) => {
|
|
951
|
+
const ts = new Date(msg.ts).toISOString();
|
|
952
|
+
const body = msg.body.length > 120 ? `${msg.body.slice(0, 117)}...` : msg.body;
|
|
953
|
+
console.log(`${ts} ${msg.from} -> ${msg.to}:${body}`);
|
|
954
|
+
});
|
|
955
|
+
}
|
|
956
|
+
finally {
|
|
957
|
+
await adapter.close?.();
|
|
958
|
+
}
|
|
959
|
+
});
|
|
960
|
+
// version - Show version info
|
|
961
|
+
program
|
|
962
|
+
.command('version')
|
|
963
|
+
.description('Show version information')
|
|
964
|
+
.action(() => {
|
|
965
|
+
console.log(`agent-relay v${VERSION}`);
|
|
966
|
+
});
|
|
967
|
+
// update - Check for updates and optionally install
|
|
968
|
+
program
|
|
969
|
+
.command('update')
|
|
970
|
+
.description('Check for updates and install if available')
|
|
971
|
+
.option('--check', 'Only check for updates, do not install')
|
|
972
|
+
.action(async (options) => {
|
|
973
|
+
console.log(`Current version: ${VERSION}`);
|
|
974
|
+
console.log('Checking for updates...');
|
|
975
|
+
const info = await checkForUpdates(VERSION);
|
|
976
|
+
if (info.error) {
|
|
977
|
+
console.error(`Failed to check for updates: ${info.error}`);
|
|
978
|
+
process.exit(1);
|
|
979
|
+
}
|
|
980
|
+
if (!info.updateAvailable) {
|
|
981
|
+
console.log('You are running the latest version.');
|
|
982
|
+
return;
|
|
983
|
+
}
|
|
984
|
+
console.log(`New version available: ${info.latestVersion}`);
|
|
985
|
+
if (options.check) {
|
|
986
|
+
console.log('Run `agent-relay update` to install.');
|
|
987
|
+
return;
|
|
988
|
+
}
|
|
989
|
+
console.log('Installing update...');
|
|
990
|
+
try {
|
|
991
|
+
const { stdout, stderr } = await execAsync('npm install -g agent-relay@latest');
|
|
992
|
+
if (stdout)
|
|
993
|
+
console.log(stdout);
|
|
994
|
+
if (stderr)
|
|
995
|
+
console.error(stderr);
|
|
996
|
+
console.log(`Successfully updated to ${info.latestVersion}`);
|
|
997
|
+
}
|
|
998
|
+
catch (err) {
|
|
999
|
+
console.error('Failed to install update:', err.message);
|
|
1000
|
+
console.log('Try running manually: npm install -g agent-relay@latest');
|
|
1001
|
+
process.exit(1);
|
|
1002
|
+
}
|
|
1003
|
+
});
|
|
1004
|
+
// check-tmux - Check tmux availability (hidden - for diagnostics)
|
|
1005
|
+
program
|
|
1006
|
+
.command('check-tmux', { hidden: true })
|
|
1007
|
+
.description('Check tmux availability and version')
|
|
1008
|
+
.action(async () => {
|
|
1009
|
+
const { resolveTmux, checkTmuxVersion } = await import('@agent-relay/wrapper');
|
|
1010
|
+
const info = resolveTmux();
|
|
1011
|
+
if (!info) {
|
|
1012
|
+
console.log('tmux: NOT FOUND');
|
|
1013
|
+
console.log('');
|
|
1014
|
+
console.log('Install tmux, then reinstall agent-relay:');
|
|
1015
|
+
console.log(' brew install tmux # macOS');
|
|
1016
|
+
console.log(' apt install tmux # Ubuntu/Debian');
|
|
1017
|
+
console.log(' npm install agent-relay # Reinstall to bundle tmux');
|
|
1018
|
+
process.exit(1);
|
|
1019
|
+
}
|
|
1020
|
+
console.log(`tmux: ${info.path}`);
|
|
1021
|
+
console.log(`Version: ${info.version}`);
|
|
1022
|
+
console.log(`Source: ${info.isBundled ? 'bundled' : 'system'}`);
|
|
1023
|
+
const versionCheck = checkTmuxVersion();
|
|
1024
|
+
if (!versionCheck.ok) {
|
|
1025
|
+
console.log(`Warning: tmux ${versionCheck.minimum}+ recommended`);
|
|
1026
|
+
}
|
|
1027
|
+
});
|
|
1028
|
+
// bridge - Multi-project orchestration
|
|
1029
|
+
program
|
|
1030
|
+
.command('bridge')
|
|
1031
|
+
.description('Bridge multiple projects as orchestrator')
|
|
1032
|
+
.argument('[projects...]', 'Project paths to bridge')
|
|
1033
|
+
.option('--cli <tool>', 'CLI tool override for all projects')
|
|
1034
|
+
.option('--architect [cli]', 'Spawn an architect agent to coordinate all projects (default: claude)')
|
|
1035
|
+
.action(async (projectPaths, options) => {
|
|
1036
|
+
const { resolveProjects, validateDaemons, getAgentOutboxTemplate } = await import('@agent-relay/config');
|
|
1037
|
+
const { MultiProjectClient } = await import('@agent-relay/bridge');
|
|
1038
|
+
const { getProjectPaths } = await import('@agent-relay/config');
|
|
1039
|
+
const fs = await import('node:fs');
|
|
1040
|
+
const pathModule = await import('node:path');
|
|
1041
|
+
// Resolve projects from args or config
|
|
1042
|
+
const projects = resolveProjects(projectPaths, options.cli);
|
|
1043
|
+
if (projects.length === 0) {
|
|
1044
|
+
console.error('No projects specified.');
|
|
1045
|
+
console.error('Usage: agent-relay bridge ~/project1 ~/project2');
|
|
1046
|
+
console.error(' or: Create ~/.agent-relay/bridge.json with project config');
|
|
1047
|
+
process.exit(1);
|
|
1048
|
+
}
|
|
1049
|
+
console.log('Bridge Mode - Multi-Project Orchestration');
|
|
1050
|
+
console.log('─'.repeat(40));
|
|
1051
|
+
// Check which daemons are running
|
|
1052
|
+
const { valid, missing } = validateDaemons(projects);
|
|
1053
|
+
if (missing.length > 0) {
|
|
1054
|
+
console.error('\nMissing daemons for:');
|
|
1055
|
+
for (const p of missing) {
|
|
1056
|
+
console.error(` - ${p.path}`);
|
|
1057
|
+
console.error(` Run: cd "${p.path}" && agent-relay up`);
|
|
1058
|
+
}
|
|
1059
|
+
console.error('');
|
|
1060
|
+
}
|
|
1061
|
+
if (valid.length === 0) {
|
|
1062
|
+
console.error('No projects have running daemons. Start them first.');
|
|
1063
|
+
process.exit(1);
|
|
1064
|
+
}
|
|
1065
|
+
console.log('\nConnecting to projects:');
|
|
1066
|
+
for (const p of valid) {
|
|
1067
|
+
console.log(` - ${p.id} (${p.path})`);
|
|
1068
|
+
console.log(` Lead: ${p.leadName}, CLI: ${p.cli}`);
|
|
1069
|
+
}
|
|
1070
|
+
console.log('');
|
|
1071
|
+
// Get data directories for ALL bridged projects (so each project's dashboard can show bridge state)
|
|
1072
|
+
const bridgeStatePaths = valid.map(p => {
|
|
1073
|
+
const projectPaths = getProjectPaths(p.path);
|
|
1074
|
+
// Ensure directory exists
|
|
1075
|
+
if (!fs.existsSync(projectPaths.dataDir)) {
|
|
1076
|
+
fs.mkdirSync(projectPaths.dataDir, { recursive: true });
|
|
1077
|
+
}
|
|
1078
|
+
return pathModule.join(projectPaths.dataDir, 'bridge-state.json');
|
|
1079
|
+
});
|
|
1080
|
+
const bridgeState = {
|
|
1081
|
+
projects: valid.map(p => ({
|
|
1082
|
+
id: p.id,
|
|
1083
|
+
name: pathModule.basename(p.path),
|
|
1084
|
+
path: p.path,
|
|
1085
|
+
connected: false,
|
|
1086
|
+
lead: { name: p.leadName, connected: false },
|
|
1087
|
+
agents: [],
|
|
1088
|
+
})),
|
|
1089
|
+
messages: [],
|
|
1090
|
+
connected: false,
|
|
1091
|
+
startedAt: new Date().toISOString(),
|
|
1092
|
+
};
|
|
1093
|
+
// Write bridge state to ALL project data directories
|
|
1094
|
+
const writeBridgeState = () => {
|
|
1095
|
+
const stateJson = JSON.stringify(bridgeState, null, 2);
|
|
1096
|
+
for (const statePath of bridgeStatePaths) {
|
|
1097
|
+
try {
|
|
1098
|
+
fs.writeFileSync(statePath, stateJson);
|
|
1099
|
+
}
|
|
1100
|
+
catch (err) {
|
|
1101
|
+
console.error(`[bridge] Failed to write state to ${statePath}:`, err);
|
|
1102
|
+
}
|
|
1103
|
+
}
|
|
1104
|
+
};
|
|
1105
|
+
// Initial state write
|
|
1106
|
+
writeBridgeState();
|
|
1107
|
+
console.log(`Bridge state written to ${bridgeStatePaths.length} project(s)`);
|
|
1108
|
+
// Connect to all project daemons
|
|
1109
|
+
const client = new MultiProjectClient(valid);
|
|
1110
|
+
// Track connection state changes (daemon connection, not agent registration)
|
|
1111
|
+
// Also track "reconnecting" state for UI feedback
|
|
1112
|
+
const wasConnected = new Map();
|
|
1113
|
+
client.onProjectStateChange = (projectId, connected) => {
|
|
1114
|
+
const project = bridgeState.projects.find(p => p.id === projectId);
|
|
1115
|
+
if (project) {
|
|
1116
|
+
const hadConnection = wasConnected.get(projectId) || false;
|
|
1117
|
+
project.connected = connected;
|
|
1118
|
+
// Set reconnecting if we lost connection (had it before, now disconnected)
|
|
1119
|
+
project.reconnecting = !connected && hadConnection;
|
|
1120
|
+
wasConnected.set(projectId, connected);
|
|
1121
|
+
// Note: lead.connected should only be true when an actual lead agent registers
|
|
1122
|
+
// The bridge connecting to daemon doesn't mean a lead agent is active
|
|
1123
|
+
}
|
|
1124
|
+
bridgeState.connected = bridgeState.projects.some(p => p.connected);
|
|
1125
|
+
writeBridgeState();
|
|
1126
|
+
};
|
|
1127
|
+
try {
|
|
1128
|
+
await client.connect();
|
|
1129
|
+
}
|
|
1130
|
+
catch (_err) {
|
|
1131
|
+
console.error('Failed to connect to all projects');
|
|
1132
|
+
writeBridgeState(); // Write final state before exit
|
|
1133
|
+
process.exit(1);
|
|
1134
|
+
}
|
|
1135
|
+
bridgeState.connected = true;
|
|
1136
|
+
writeBridgeState();
|
|
1137
|
+
console.log('Connected to all projects.');
|
|
1138
|
+
console.log('');
|
|
1139
|
+
console.log('Cross-project messaging:');
|
|
1140
|
+
console.log(' ->relay:projectId:agent Message');
|
|
1141
|
+
console.log(' ->relay:*:lead Broadcast to all leads');
|
|
1142
|
+
console.log('');
|
|
1143
|
+
// Spawn architect agent if --architect flag is set
|
|
1144
|
+
let architectWrapper = null;
|
|
1145
|
+
if (options.architect !== undefined) {
|
|
1146
|
+
// Determine CLI to use (default to claude)
|
|
1147
|
+
const architectCli = typeof options.architect === 'string' ? options.architect : 'claude';
|
|
1148
|
+
// Use first project as the base for the architect
|
|
1149
|
+
const baseProject = valid[0];
|
|
1150
|
+
const basePaths = getProjectPaths(baseProject.path);
|
|
1151
|
+
// Build project context for the architect
|
|
1152
|
+
const projectContext = valid.map(p => `- ${p.id}: ${p.path} (Lead: ${p.leadName})`).join('\n');
|
|
1153
|
+
// Get outbox path template for agent instructions (escaped for template literal)
|
|
1154
|
+
const outboxPath = getAgentOutboxTemplate().replace(/\$/g, '\\$');
|
|
1155
|
+
// Create architect system prompt
|
|
1156
|
+
const architectPrompt = `You are the Architect, a cross-project coordinator overseeing multiple codebases.
|
|
1157
|
+
|
|
1158
|
+
## Connected Projects
|
|
1159
|
+
${projectContext}
|
|
1160
|
+
|
|
1161
|
+
## Your Role
|
|
1162
|
+
- Coordinate high-level work across all projects
|
|
1163
|
+
- Assign tasks to project leads
|
|
1164
|
+
- Ensure consistency and resolve cross-project dependencies
|
|
1165
|
+
- Review overall architecture decisions
|
|
1166
|
+
|
|
1167
|
+
## Cross-Project Messaging
|
|
1168
|
+
|
|
1169
|
+
Write a file to your outbox, then output the trigger. Use project:AgentName syntax:
|
|
1170
|
+
|
|
1171
|
+
\`\`\`bash
|
|
1172
|
+
# Message specific project lead
|
|
1173
|
+
cat > ${outboxPath}/msg << 'EOF'
|
|
1174
|
+
TO: ${valid[0].id}:${valid[0].leadName}
|
|
1175
|
+
|
|
1176
|
+
Your message to this project's lead.
|
|
1177
|
+
EOF
|
|
1178
|
+
\`\`\`
|
|
1179
|
+
Then output: \`->relay-file:msg\`
|
|
1180
|
+
|
|
1181
|
+
\`\`\`bash
|
|
1182
|
+
# Broadcast to all agents in a project
|
|
1183
|
+
cat > ${outboxPath}/broadcast << 'EOF'
|
|
1184
|
+
TO: ${valid.length > 1 ? valid[1].id : valid[0].id}:*
|
|
1185
|
+
|
|
1186
|
+
Broadcast to all agents in a project.
|
|
1187
|
+
EOF
|
|
1188
|
+
\`\`\`
|
|
1189
|
+
Then output: \`->relay-file:broadcast\`
|
|
1190
|
+
|
|
1191
|
+
\`\`\`bash
|
|
1192
|
+
# Broadcast to ALL agents in ALL projects
|
|
1193
|
+
cat > ${outboxPath}/all << 'EOF'
|
|
1194
|
+
TO: *:*
|
|
1195
|
+
|
|
1196
|
+
Broadcast to ALL agents in ALL projects.
|
|
1197
|
+
EOF
|
|
1198
|
+
\`\`\`
|
|
1199
|
+
Then output: \`->relay-file:all\`
|
|
1200
|
+
|
|
1201
|
+
## Getting Started
|
|
1202
|
+
1. Check in with each project lead to understand current status
|
|
1203
|
+
2. Identify cross-project dependencies
|
|
1204
|
+
3. Coordinate work across teams
|
|
1205
|
+
|
|
1206
|
+
Start by greeting the project leads and asking for status updates.`;
|
|
1207
|
+
console.log('Spawning Architect agent...');
|
|
1208
|
+
console.log(` CLI: ${architectCli}`);
|
|
1209
|
+
console.log(` Base project: ${baseProject.path}`);
|
|
1210
|
+
console.log('');
|
|
1211
|
+
// Determine command and args based on CLI
|
|
1212
|
+
let command;
|
|
1213
|
+
let args = [];
|
|
1214
|
+
if (architectCli === 'claude' || architectCli.startsWith('claude:')) {
|
|
1215
|
+
command = 'claude';
|
|
1216
|
+
args = ['--dangerously-skip-permissions'];
|
|
1217
|
+
// Add model if specified (e.g., claude:opus)
|
|
1218
|
+
if (architectCli.includes(':')) {
|
|
1219
|
+
const model = architectCli.split(':')[1];
|
|
1220
|
+
args.push('--model', model);
|
|
1221
|
+
}
|
|
1222
|
+
}
|
|
1223
|
+
else if (architectCli === 'codex') {
|
|
1224
|
+
command = 'codex';
|
|
1225
|
+
args = ['--dangerously-skip-permissions'];
|
|
1226
|
+
}
|
|
1227
|
+
else {
|
|
1228
|
+
command = architectCli;
|
|
1229
|
+
}
|
|
1230
|
+
try {
|
|
1231
|
+
architectWrapper = new RelayPtyOrchestrator({
|
|
1232
|
+
name: 'Architect',
|
|
1233
|
+
command,
|
|
1234
|
+
args,
|
|
1235
|
+
socketPath: basePaths.socketPath,
|
|
1236
|
+
cwd: baseProject.path,
|
|
1237
|
+
streamLogs: true,
|
|
1238
|
+
});
|
|
1239
|
+
await architectWrapper.start();
|
|
1240
|
+
// Wait for agent to be ready, then inject the prompt
|
|
1241
|
+
setTimeout(async () => {
|
|
1242
|
+
try {
|
|
1243
|
+
await architectWrapper.injectMessage(architectPrompt);
|
|
1244
|
+
console.log('Architect agent started and initialized.');
|
|
1245
|
+
console.log('');
|
|
1246
|
+
}
|
|
1247
|
+
catch (err) {
|
|
1248
|
+
console.error('Failed to inject architect prompt:', err);
|
|
1249
|
+
}
|
|
1250
|
+
}, 3000);
|
|
1251
|
+
}
|
|
1252
|
+
catch (err) {
|
|
1253
|
+
console.error('Failed to spawn Architect agent:', err);
|
|
1254
|
+
}
|
|
1255
|
+
}
|
|
1256
|
+
// Handle messages from projects
|
|
1257
|
+
client.onMessage = (projectId, from, payload, messageId) => {
|
|
1258
|
+
console.log(`[${projectId}] ${from}: ${payload.body.substring(0, 80)}...`);
|
|
1259
|
+
// Track message in bridge state
|
|
1260
|
+
bridgeState.messages.push({
|
|
1261
|
+
id: messageId,
|
|
1262
|
+
from,
|
|
1263
|
+
to: '*', // Incoming messages are from agents
|
|
1264
|
+
body: payload.body,
|
|
1265
|
+
sourceProject: projectId,
|
|
1266
|
+
timestamp: new Date().toISOString(),
|
|
1267
|
+
});
|
|
1268
|
+
// Keep last 100 messages
|
|
1269
|
+
if (bridgeState.messages.length > 100) {
|
|
1270
|
+
bridgeState.messages = bridgeState.messages.slice(-100);
|
|
1271
|
+
}
|
|
1272
|
+
writeBridgeState();
|
|
1273
|
+
};
|
|
1274
|
+
// Clean up on exit
|
|
1275
|
+
const cleanup = () => {
|
|
1276
|
+
bridgeState.connected = false;
|
|
1277
|
+
bridgeState.projects.forEach(p => {
|
|
1278
|
+
p.connected = false;
|
|
1279
|
+
if (p.lead)
|
|
1280
|
+
p.lead.connected = false;
|
|
1281
|
+
});
|
|
1282
|
+
writeBridgeState();
|
|
1283
|
+
};
|
|
1284
|
+
// Keep running
|
|
1285
|
+
process.on('SIGINT', () => {
|
|
1286
|
+
console.log('\nDisconnecting...');
|
|
1287
|
+
cleanup();
|
|
1288
|
+
client.disconnect();
|
|
1289
|
+
process.exit(0);
|
|
1290
|
+
});
|
|
1291
|
+
// Start a simple REPL for sending messages
|
|
1292
|
+
const readline = await import('node:readline');
|
|
1293
|
+
const rl = readline.createInterface({
|
|
1294
|
+
input: process.stdin,
|
|
1295
|
+
output: process.stdout,
|
|
1296
|
+
});
|
|
1297
|
+
console.log('Enter messages as: projectId:agent message');
|
|
1298
|
+
console.log('Or: *:lead message (broadcast to all leads)');
|
|
1299
|
+
console.log('Type "quit" to exit.\n');
|
|
1300
|
+
const promptForInput = () => {
|
|
1301
|
+
rl.question('> ', (input) => {
|
|
1302
|
+
if (input.toLowerCase() === 'quit') {
|
|
1303
|
+
client.disconnect();
|
|
1304
|
+
rl.close();
|
|
1305
|
+
process.exit(0);
|
|
1306
|
+
}
|
|
1307
|
+
// Parse input: projectId:agent message
|
|
1308
|
+
const match = input.match(/^(\S+):(\S+)\s+(.+)$/);
|
|
1309
|
+
if (match) {
|
|
1310
|
+
const [, projectId, agent, message] = match;
|
|
1311
|
+
if (projectId === '*' && agent === 'lead') {
|
|
1312
|
+
client.broadcastToLeads(message);
|
|
1313
|
+
console.log('→ Broadcast to all leads');
|
|
1314
|
+
}
|
|
1315
|
+
else if (projectId === '*') {
|
|
1316
|
+
client.broadcastAll(message);
|
|
1317
|
+
console.log('→ Broadcast to all');
|
|
1318
|
+
}
|
|
1319
|
+
else {
|
|
1320
|
+
const sent = client.sendToProject(projectId, agent, message);
|
|
1321
|
+
if (sent) {
|
|
1322
|
+
console.log(`→ ${projectId}:${agent}`);
|
|
1323
|
+
}
|
|
1324
|
+
}
|
|
1325
|
+
}
|
|
1326
|
+
else {
|
|
1327
|
+
console.log('Format: projectId:agent message');
|
|
1328
|
+
}
|
|
1329
|
+
promptForInput();
|
|
1330
|
+
});
|
|
1331
|
+
};
|
|
1332
|
+
promptForInput();
|
|
1333
|
+
});
|
|
1334
|
+
// gc - Clean up orphaned tmux sessions (hidden - for agent use)
|
|
1335
|
+
program
|
|
1336
|
+
.command('gc', { hidden: true })
|
|
1337
|
+
.description('Clean up orphaned tmux sessions (sessions with no connected agent)')
|
|
1338
|
+
.option('--dry-run', 'Show what would be cleaned without actually doing it')
|
|
1339
|
+
.option('--force', 'Kill all relay sessions regardless of connection status')
|
|
1340
|
+
.action(async (options) => {
|
|
1341
|
+
const { getProjectPaths } = await import('@agent-relay/config');
|
|
1342
|
+
const paths = getProjectPaths();
|
|
1343
|
+
const agentsPath = path.join(paths.teamDir, 'agents.json');
|
|
1344
|
+
// Get all relay tmux sessions
|
|
1345
|
+
const sessions = await discoverRelaySessions();
|
|
1346
|
+
if (!sessions.length) {
|
|
1347
|
+
console.log('No relay tmux sessions found.');
|
|
1348
|
+
return;
|
|
1349
|
+
}
|
|
1350
|
+
// Get connected agents
|
|
1351
|
+
const connectedAgents = new Set();
|
|
1352
|
+
if (!options.force) {
|
|
1353
|
+
const agents = loadAgents(agentsPath);
|
|
1354
|
+
// Consider an agent "connected" if last seen within 30 seconds
|
|
1355
|
+
const staleThresholdMs = 30_000;
|
|
1356
|
+
const now = Date.now();
|
|
1357
|
+
agents.forEach(a => {
|
|
1358
|
+
if (a.name && a.lastSeen) {
|
|
1359
|
+
const lastSeenTs = Date.parse(a.lastSeen);
|
|
1360
|
+
if (!Number.isNaN(lastSeenTs) && now - lastSeenTs < staleThresholdMs) {
|
|
1361
|
+
connectedAgents.add(a.name);
|
|
1362
|
+
}
|
|
1363
|
+
}
|
|
1364
|
+
});
|
|
1365
|
+
}
|
|
1366
|
+
// Find orphaned sessions
|
|
1367
|
+
const orphaned = sessions.filter(s => options.force || (s.agentName && !connectedAgents.has(s.agentName)));
|
|
1368
|
+
if (!orphaned.length) {
|
|
1369
|
+
console.log(`All ${sessions.length} session(s) have active agents.`);
|
|
1370
|
+
return;
|
|
1371
|
+
}
|
|
1372
|
+
console.log(`Found ${orphaned.length} orphaned session(s):`);
|
|
1373
|
+
for (const session of orphaned) {
|
|
1374
|
+
console.log(` - ${session.sessionName} (agent: ${session.agentName ?? 'unknown'})`);
|
|
1375
|
+
}
|
|
1376
|
+
if (options.dryRun) {
|
|
1377
|
+
console.log('\nDry run - no sessions killed.');
|
|
1378
|
+
return;
|
|
1379
|
+
}
|
|
1380
|
+
// Kill orphaned sessions
|
|
1381
|
+
let killed = 0;
|
|
1382
|
+
const tmuxPath = getTmuxPath();
|
|
1383
|
+
for (const session of orphaned) {
|
|
1384
|
+
try {
|
|
1385
|
+
await execAsync(`"${tmuxPath}" kill-session -t ${session.sessionName}`);
|
|
1386
|
+
killed++;
|
|
1387
|
+
console.log(`Killed: ${session.sessionName}`);
|
|
1388
|
+
}
|
|
1389
|
+
catch (err) {
|
|
1390
|
+
console.error(`Failed to kill ${session.sessionName}: ${err.message}`);
|
|
1391
|
+
}
|
|
1392
|
+
}
|
|
1393
|
+
console.log(`\nCleaned up ${killed}/${orphaned.length} session(s).`);
|
|
1394
|
+
});
|
|
1395
|
+
async function discoverRelaySessions() {
|
|
1396
|
+
try {
|
|
1397
|
+
const tmuxPath = getTmuxPath();
|
|
1398
|
+
const { stdout } = await execAsync(`"${tmuxPath}" list-sessions -F "#{session_name}"`);
|
|
1399
|
+
const sessionNames = stdout
|
|
1400
|
+
.split('\n')
|
|
1401
|
+
.map(s => s.trim())
|
|
1402
|
+
.filter(Boolean);
|
|
1403
|
+
const relaySessions = sessionNames
|
|
1404
|
+
.map(name => {
|
|
1405
|
+
const match = name.match(/^relay-(.+)$/);
|
|
1406
|
+
if (!match)
|
|
1407
|
+
return undefined;
|
|
1408
|
+
return { sessionName: name, agentName: match[1] };
|
|
1409
|
+
})
|
|
1410
|
+
.filter((s) => Boolean(s));
|
|
1411
|
+
return await Promise.all(relaySessions.map(async (session) => {
|
|
1412
|
+
let cwd;
|
|
1413
|
+
try {
|
|
1414
|
+
const { stdout: cwdOut } = await execAsync(`"${tmuxPath}" display-message -t ${session.sessionName} -p '#{pane_current_path}'`);
|
|
1415
|
+
cwd = cwdOut.trim() || undefined;
|
|
1416
|
+
}
|
|
1417
|
+
catch {
|
|
1418
|
+
cwd = undefined;
|
|
1419
|
+
}
|
|
1420
|
+
return { ...session, cwd };
|
|
1421
|
+
}));
|
|
1422
|
+
}
|
|
1423
|
+
catch {
|
|
1424
|
+
return [];
|
|
1425
|
+
}
|
|
1426
|
+
}
|
|
1427
|
+
function logRelaySessions(sessions) {
|
|
1428
|
+
if (!sessions.length) {
|
|
1429
|
+
console.log('Relay tmux sessions: none detected');
|
|
1430
|
+
return;
|
|
1431
|
+
}
|
|
1432
|
+
console.log('Relay tmux sessions:');
|
|
1433
|
+
sessions.forEach((session) => {
|
|
1434
|
+
const parts = [
|
|
1435
|
+
`agent: ${session.agentName ?? 'unknown'}`,
|
|
1436
|
+
session.cwd ? `cwd: ${session.cwd}` : undefined,
|
|
1437
|
+
].filter(Boolean);
|
|
1438
|
+
console.log(`- ${session.sessionName}${parts.length ? ` (${parts.join(', ')})` : ''}`);
|
|
1439
|
+
});
|
|
1440
|
+
}
|
|
1441
|
+
function loadAgents(agentsPath) {
|
|
1442
|
+
if (!fs.existsSync(agentsPath)) {
|
|
1443
|
+
return [];
|
|
1444
|
+
}
|
|
1445
|
+
try {
|
|
1446
|
+
const raw = JSON.parse(fs.readFileSync(agentsPath, 'utf-8'));
|
|
1447
|
+
const agentsArray = Array.isArray(raw?.agents)
|
|
1448
|
+
? raw.agents
|
|
1449
|
+
: raw?.agents
|
|
1450
|
+
? Object.values(raw.agents)
|
|
1451
|
+
: [];
|
|
1452
|
+
return agentsArray
|
|
1453
|
+
.filter((a) => a?.name)
|
|
1454
|
+
.map((a) => ({
|
|
1455
|
+
id: a.id,
|
|
1456
|
+
name: a.name,
|
|
1457
|
+
cli: a.cli,
|
|
1458
|
+
workingDirectory: a.workingDirectory,
|
|
1459
|
+
firstSeen: a.firstSeen,
|
|
1460
|
+
lastSeen: a.lastSeen,
|
|
1461
|
+
messagesSent: typeof a.messagesSent === 'number' ? a.messagesSent : 0,
|
|
1462
|
+
messagesReceived: typeof a.messagesReceived === 'number' ? a.messagesReceived : 0,
|
|
1463
|
+
}));
|
|
1464
|
+
}
|
|
1465
|
+
catch (err) {
|
|
1466
|
+
console.error('Failed to read agents.json:', err.message);
|
|
1467
|
+
return [];
|
|
1468
|
+
}
|
|
1469
|
+
}
|
|
1470
|
+
const STALE_THRESHOLD_MS = 30_000;
|
|
1471
|
+
// Internal agents that should be hidden from `agents` and `who` by default
|
|
1472
|
+
const INTERNAL_AGENTS = new Set(['cli', 'Dashboard']);
|
|
1473
|
+
function isInternalAgent(name) {
|
|
1474
|
+
if (!name)
|
|
1475
|
+
return true;
|
|
1476
|
+
if (name.startsWith('__'))
|
|
1477
|
+
return true;
|
|
1478
|
+
return INTERNAL_AGENTS.has(name);
|
|
1479
|
+
}
|
|
1480
|
+
function getAgentStatus(agent) {
|
|
1481
|
+
if (!agent.lastSeen)
|
|
1482
|
+
return 'UNKNOWN';
|
|
1483
|
+
const ts = Date.parse(agent.lastSeen);
|
|
1484
|
+
if (Number.isNaN(ts))
|
|
1485
|
+
return 'UNKNOWN';
|
|
1486
|
+
return Date.now() - ts < STALE_THRESHOLD_MS ? 'ONLINE' : 'STALE';
|
|
1487
|
+
}
|
|
1488
|
+
function isAgentOnline(agent) {
|
|
1489
|
+
return getAgentStatus(agent) === 'ONLINE';
|
|
1490
|
+
}
|
|
1491
|
+
// Visible agents: not internal and not stale (used by `agents` command)
|
|
1492
|
+
function isVisibleAgent(agent) {
|
|
1493
|
+
if (isInternalAgent(agent.name))
|
|
1494
|
+
return false;
|
|
1495
|
+
if (getAgentStatus(agent) === 'STALE')
|
|
1496
|
+
return false;
|
|
1497
|
+
return true;
|
|
1498
|
+
}
|
|
1499
|
+
function formatRelativeTime(iso) {
|
|
1500
|
+
if (!iso)
|
|
1501
|
+
return 'unknown';
|
|
1502
|
+
const ts = Date.parse(iso);
|
|
1503
|
+
if (Number.isNaN(ts))
|
|
1504
|
+
return 'unknown';
|
|
1505
|
+
const diffMs = Date.now() - ts;
|
|
1506
|
+
const diffSec = Math.floor(diffMs / 1000);
|
|
1507
|
+
if (diffSec < 60)
|
|
1508
|
+
return `${diffSec}s ago`;
|
|
1509
|
+
const diffMin = Math.floor(diffSec / 60);
|
|
1510
|
+
if (diffMin < 60)
|
|
1511
|
+
return `${diffMin}m ago`;
|
|
1512
|
+
const diffHours = Math.floor(diffMin / 60);
|
|
1513
|
+
if (diffHours < 48)
|
|
1514
|
+
return `${diffHours}h ago`;
|
|
1515
|
+
const diffDays = Math.floor(diffHours / 24);
|
|
1516
|
+
return `${diffDays}d ago`;
|
|
1517
|
+
}
|
|
1518
|
+
function parseSince(input) {
|
|
1519
|
+
if (!input)
|
|
1520
|
+
return undefined;
|
|
1521
|
+
const trimmed = String(input).trim();
|
|
1522
|
+
if (!trimmed)
|
|
1523
|
+
return undefined;
|
|
1524
|
+
const durationMatch = trimmed.match(/^(-?\d+)([smhd])$/i);
|
|
1525
|
+
if (durationMatch) {
|
|
1526
|
+
const value = Number(durationMatch[1]);
|
|
1527
|
+
const unit = durationMatch[2].toLowerCase();
|
|
1528
|
+
const multipliers = {
|
|
1529
|
+
s: 1000,
|
|
1530
|
+
m: 60_000,
|
|
1531
|
+
h: 3_600_000,
|
|
1532
|
+
d: 86_400_000,
|
|
1533
|
+
};
|
|
1534
|
+
return Date.now() - value * multipliers[unit];
|
|
1535
|
+
}
|
|
1536
|
+
const parsed = Date.parse(trimmed);
|
|
1537
|
+
if (Number.isNaN(parsed))
|
|
1538
|
+
return undefined;
|
|
1539
|
+
return parsed;
|
|
1540
|
+
}
|
|
1541
|
+
// ============================================
|
|
1542
|
+
// Spawned agent debugging commands
|
|
1543
|
+
// ============================================
|
|
1544
|
+
// agents:logs - Show log file output for a spawned agent
|
|
1545
|
+
program
|
|
1546
|
+
.command('agents:logs')
|
|
1547
|
+
.description('Show recent output from a spawned agent')
|
|
1548
|
+
.argument('<name>', 'Agent name')
|
|
1549
|
+
.option('-n, --lines <n>', 'Number of lines to show', '50')
|
|
1550
|
+
.option('-f, --follow', 'Follow output (like tail -f)')
|
|
1551
|
+
.action(async (name, options) => {
|
|
1552
|
+
const { getProjectPaths } = await import('@agent-relay/config');
|
|
1553
|
+
const paths = getProjectPaths();
|
|
1554
|
+
const logsDir = getWorkerLogsDir(paths.projectRoot);
|
|
1555
|
+
const logFile = path.join(logsDir, `${name}.log`);
|
|
1556
|
+
if (!fs.existsSync(logFile)) {
|
|
1557
|
+
console.error(`No logs found for agent "${name}"`);
|
|
1558
|
+
console.log(`Log file not found: ${logFile}`);
|
|
1559
|
+
console.log(`Run 'agent-relay agents' to see available agents`);
|
|
1560
|
+
process.exit(1);
|
|
1561
|
+
}
|
|
1562
|
+
if (options.follow) {
|
|
1563
|
+
console.log(`Following logs for ${name} (Ctrl+C to stop)...`);
|
|
1564
|
+
console.log('─'.repeat(50));
|
|
1565
|
+
// Use tail -f approach
|
|
1566
|
+
const { spawn } = await import('child_process');
|
|
1567
|
+
const child = spawn('tail', ['-f', logFile], {
|
|
1568
|
+
stdio: ['ignore', 'inherit', 'inherit'],
|
|
1569
|
+
});
|
|
1570
|
+
process.on('SIGINT', () => {
|
|
1571
|
+
child.kill();
|
|
1572
|
+
console.log('\nStopped following');
|
|
1573
|
+
process.exit(0);
|
|
1574
|
+
});
|
|
1575
|
+
child.on('exit', () => {
|
|
1576
|
+
process.exit(0);
|
|
1577
|
+
});
|
|
1578
|
+
}
|
|
1579
|
+
else {
|
|
1580
|
+
try {
|
|
1581
|
+
const lines = parseInt(options.lines || '50', 10);
|
|
1582
|
+
const { stdout } = await execAsync(`tail -n ${lines} "${logFile}"`);
|
|
1583
|
+
console.log(`Logs for ${name} (last ${lines} lines):`);
|
|
1584
|
+
console.log('─'.repeat(50));
|
|
1585
|
+
console.log(stdout || '(empty)');
|
|
1586
|
+
}
|
|
1587
|
+
catch (err) {
|
|
1588
|
+
console.error('Failed to read logs:', err.message);
|
|
1589
|
+
}
|
|
1590
|
+
}
|
|
1591
|
+
});
|
|
1592
|
+
// spawn - Spawn an agent via API (works from any context, no tmux required)
|
|
1593
|
+
program
|
|
1594
|
+
.command('spawn', { hidden: true })
|
|
1595
|
+
.description('Spawn an agent via dashboard API (no tmux required, works in containers)')
|
|
1596
|
+
.argument('<name>', 'Agent name')
|
|
1597
|
+
.argument('<cli>', 'CLI to use (claude, codex, gemini, etc.)')
|
|
1598
|
+
.argument('[task]', 'Task description (can also be piped via stdin)')
|
|
1599
|
+
.option('--port <port>', 'Dashboard port', DEFAULT_DASHBOARD_PORT)
|
|
1600
|
+
.option('--team <team>', 'Team name for the agent')
|
|
1601
|
+
.option('--spawner <name>', 'Name of the agent requesting the spawn (for policy enforcement)')
|
|
1602
|
+
.option('--interactive', 'Disable auto-accept of permission prompts (for auth setup flows)')
|
|
1603
|
+
.option('--cwd <path>', 'Working directory for the agent')
|
|
1604
|
+
.option('--shadow-mode <mode>', 'Shadow execution mode: subagent or process')
|
|
1605
|
+
.option('--shadow-of <name>', 'Primary agent to shadow (if this agent is a shadow)')
|
|
1606
|
+
.option('--shadow-agent <profile>', 'Shadow agent profile to use')
|
|
1607
|
+
.option('--shadow-triggers <triggers>', 'When to trigger shadow (comma-separated: SESSION_END,CODE_WRITTEN,REVIEW_REQUEST,EXPLICIT_ASK,ALL_MESSAGES)')
|
|
1608
|
+
.option('--shadow-speak-on <triggers>', 'When shadow should speak (comma-separated, same values as --shadow-triggers)')
|
|
1609
|
+
.action(async (name, cli, task, options) => {
|
|
1610
|
+
const port = options.port || DEFAULT_DASHBOARD_PORT;
|
|
1611
|
+
// Read task from stdin if not provided as argument
|
|
1612
|
+
let finalTask = task;
|
|
1613
|
+
if (!finalTask && !process.stdin.isTTY) {
|
|
1614
|
+
const chunks = [];
|
|
1615
|
+
for await (const chunk of process.stdin) {
|
|
1616
|
+
chunks.push(chunk);
|
|
1617
|
+
}
|
|
1618
|
+
finalTask = Buffer.concat(chunks).toString('utf-8').trim();
|
|
1619
|
+
}
|
|
1620
|
+
if (!finalTask) {
|
|
1621
|
+
console.error('Error: Task description required (as argument or via stdin)');
|
|
1622
|
+
process.exit(1);
|
|
1623
|
+
}
|
|
1624
|
+
// Validate shadow mode if provided
|
|
1625
|
+
if (options.shadowMode && !['subagent', 'process'].includes(options.shadowMode)) {
|
|
1626
|
+
console.error('Error: --shadow-mode must be "subagent" or "process"');
|
|
1627
|
+
process.exit(1);
|
|
1628
|
+
}
|
|
1629
|
+
// Parse comma-separated trigger lists
|
|
1630
|
+
const parseTriggers = (value) => {
|
|
1631
|
+
if (!value)
|
|
1632
|
+
return undefined;
|
|
1633
|
+
const validTriggers = ['SESSION_END', 'CODE_WRITTEN', 'REVIEW_REQUEST', 'EXPLICIT_ASK', 'ALL_MESSAGES'];
|
|
1634
|
+
const triggers = value.split(',').map(t => t.trim().toUpperCase());
|
|
1635
|
+
const invalid = triggers.filter(t => !validTriggers.includes(t));
|
|
1636
|
+
if (invalid.length > 0) {
|
|
1637
|
+
console.error(`Error: Invalid triggers: ${invalid.join(', ')}`);
|
|
1638
|
+
console.error(`Valid triggers: ${validTriggers.join(', ')}`);
|
|
1639
|
+
process.exit(1);
|
|
1640
|
+
}
|
|
1641
|
+
return triggers;
|
|
1642
|
+
};
|
|
1643
|
+
// Build spawn request using the SpawnRequest type for consistency
|
|
1644
|
+
const spawnRequest = {
|
|
1645
|
+
name,
|
|
1646
|
+
cli,
|
|
1647
|
+
task: finalTask,
|
|
1648
|
+
team: options.team,
|
|
1649
|
+
spawnerName: options.spawner,
|
|
1650
|
+
interactive: options.interactive,
|
|
1651
|
+
cwd: options.cwd,
|
|
1652
|
+
shadowMode: options.shadowMode,
|
|
1653
|
+
shadowOf: options.shadowOf,
|
|
1654
|
+
shadowAgent: options.shadowAgent,
|
|
1655
|
+
shadowTriggers: parseTriggers(options.shadowTriggers),
|
|
1656
|
+
shadowSpeakOn: parseTriggers(options.shadowSpeakOn),
|
|
1657
|
+
};
|
|
1658
|
+
// Try daemon socket first (preferred path)
|
|
1659
|
+
try {
|
|
1660
|
+
const { getProjectPaths } = await import('@agent-relay/config');
|
|
1661
|
+
const paths = getProjectPaths();
|
|
1662
|
+
// TODO: Re-enable daemon-based spawning when client.spawn() is implemented
|
|
1663
|
+
// See: docs/SDK-MIGRATION-PLAN.md for planned implementation
|
|
1664
|
+
// For now, fall through to HTTP API
|
|
1665
|
+
throw new Error('Daemon-based spawn not yet implemented');
|
|
1666
|
+
}
|
|
1667
|
+
catch (daemonErr) {
|
|
1668
|
+
// Fall through to HTTP API
|
|
1669
|
+
// console.log('Daemon not available, trying HTTP API...');
|
|
1670
|
+
}
|
|
1671
|
+
// Fall back to HTTP API
|
|
1672
|
+
try {
|
|
1673
|
+
const response = await fetch(`http://localhost:${port}/api/spawn`, {
|
|
1674
|
+
method: 'POST',
|
|
1675
|
+
headers: { 'Content-Type': 'application/json' },
|
|
1676
|
+
body: JSON.stringify(spawnRequest),
|
|
1677
|
+
});
|
|
1678
|
+
const result = await response.json();
|
|
1679
|
+
if (result.success) {
|
|
1680
|
+
console.log(`Spawned agent: ${name} (pid: ${result.pid})`);
|
|
1681
|
+
process.exit(0);
|
|
1682
|
+
}
|
|
1683
|
+
else {
|
|
1684
|
+
if (result.policyDecision) {
|
|
1685
|
+
console.error(`Policy denied spawn: ${result.policyDecision.reason}`);
|
|
1686
|
+
console.error(`Policy source: ${result.policyDecision.policySource}`);
|
|
1687
|
+
}
|
|
1688
|
+
else {
|
|
1689
|
+
console.error(`Failed to spawn ${name}: ${result.error || 'Unknown error'}`);
|
|
1690
|
+
}
|
|
1691
|
+
process.exit(1);
|
|
1692
|
+
}
|
|
1693
|
+
}
|
|
1694
|
+
catch (err) {
|
|
1695
|
+
if (err.code === 'ECONNREFUSED') {
|
|
1696
|
+
console.error(`Cannot connect to dashboard at port ${port}. Is the daemon running?`);
|
|
1697
|
+
console.log(`Run 'agent-relay up' to start the daemon.`);
|
|
1698
|
+
}
|
|
1699
|
+
else {
|
|
1700
|
+
console.error(`Failed to spawn ${name}: ${err.message}`);
|
|
1701
|
+
}
|
|
1702
|
+
process.exit(1);
|
|
1703
|
+
}
|
|
1704
|
+
});
|
|
1705
|
+
// release - Release a spawned agent via API (works from any context, no terminal required)
|
|
1706
|
+
program
|
|
1707
|
+
.command('release')
|
|
1708
|
+
.description('Release a spawned agent via API (no terminal required)')
|
|
1709
|
+
.argument('<name>', 'Agent name to release')
|
|
1710
|
+
.option('--port <port>', 'Dashboard port', DEFAULT_DASHBOARD_PORT)
|
|
1711
|
+
.action(async (name, options) => {
|
|
1712
|
+
const port = options.port || DEFAULT_DASHBOARD_PORT;
|
|
1713
|
+
// Try daemon socket first (preferred path)
|
|
1714
|
+
try {
|
|
1715
|
+
const { getProjectPaths } = await import('@agent-relay/config');
|
|
1716
|
+
const paths = getProjectPaths();
|
|
1717
|
+
const client = new RelayClient({
|
|
1718
|
+
socketPath: paths.socketPath,
|
|
1719
|
+
agentName: '__cli_releaser__',
|
|
1720
|
+
quiet: true,
|
|
1721
|
+
reconnect: false,
|
|
1722
|
+
maxReconnectAttempts: 0,
|
|
1723
|
+
reconnectDelayMs: 0,
|
|
1724
|
+
reconnectMaxDelayMs: 0,
|
|
1725
|
+
});
|
|
1726
|
+
// TODO: Re-enable daemon-based release when client.release() is implemented
|
|
1727
|
+
// See: docs/SDK-MIGRATION-PLAN.md for planned implementation
|
|
1728
|
+
// For now, fall through to HTTP API
|
|
1729
|
+
throw new Error('Daemon-based release not yet implemented');
|
|
1730
|
+
}
|
|
1731
|
+
catch (daemonErr) {
|
|
1732
|
+
// Fall through to HTTP API
|
|
1733
|
+
// console.log('Daemon not available, trying HTTP API...');
|
|
1734
|
+
}
|
|
1735
|
+
// Fall back to HTTP API
|
|
1736
|
+
try {
|
|
1737
|
+
const response = await fetch(`http://localhost:${port}/api/spawned/${encodeURIComponent(name)}`, {
|
|
1738
|
+
method: 'DELETE',
|
|
1739
|
+
});
|
|
1740
|
+
const result = await response.json();
|
|
1741
|
+
if (result.success) {
|
|
1742
|
+
console.log(`Released agent: ${name}`);
|
|
1743
|
+
process.exit(0);
|
|
1744
|
+
}
|
|
1745
|
+
else {
|
|
1746
|
+
console.error(`Failed to release ${name}: ${result.error || 'Unknown error'}`);
|
|
1747
|
+
process.exit(1);
|
|
1748
|
+
}
|
|
1749
|
+
}
|
|
1750
|
+
catch (err) {
|
|
1751
|
+
// If API call fails, try to provide helpful error message
|
|
1752
|
+
if (err.code === 'ECONNREFUSED') {
|
|
1753
|
+
console.error(`Cannot connect to dashboard at port ${port}. Is the daemon running?`);
|
|
1754
|
+
console.log(`Run 'agent-relay up' to start the daemon.`);
|
|
1755
|
+
}
|
|
1756
|
+
else {
|
|
1757
|
+
console.error(`Failed to release ${name}: ${err.message}`);
|
|
1758
|
+
}
|
|
1759
|
+
process.exit(1);
|
|
1760
|
+
}
|
|
1761
|
+
});
|
|
1762
|
+
// agents:kill - Kill a spawned agent by PID
|
|
1763
|
+
program
|
|
1764
|
+
.command('agents:kill')
|
|
1765
|
+
.description('Kill a spawned agent')
|
|
1766
|
+
.argument('<name>', 'Agent name')
|
|
1767
|
+
.option('--force', 'Skip graceful shutdown, kill immediately')
|
|
1768
|
+
.action(async (name, options) => {
|
|
1769
|
+
const { getProjectPaths } = await import('@agent-relay/config');
|
|
1770
|
+
const paths = getProjectPaths();
|
|
1771
|
+
const workers = readWorkersMetadata(paths.projectRoot);
|
|
1772
|
+
const worker = workers.find(w => w.name === name);
|
|
1773
|
+
if (!worker) {
|
|
1774
|
+
console.error(`Spawned agent "${name}" not found`);
|
|
1775
|
+
console.log(`Run 'agent-relay agents' to see available agents`);
|
|
1776
|
+
process.exit(1);
|
|
1777
|
+
}
|
|
1778
|
+
if (!worker.pid) {
|
|
1779
|
+
console.error(`Agent "${name}" has no PID recorded`);
|
|
1780
|
+
process.exit(1);
|
|
1781
|
+
}
|
|
1782
|
+
try {
|
|
1783
|
+
if (!options.force) {
|
|
1784
|
+
// Try graceful shutdown first (SIGTERM)
|
|
1785
|
+
console.log(`Sending SIGTERM to ${name} (pid: ${worker.pid})...`);
|
|
1786
|
+
process.kill(worker.pid, 'SIGTERM');
|
|
1787
|
+
// Wait for graceful shutdown
|
|
1788
|
+
await new Promise(r => setTimeout(r, 2000));
|
|
1789
|
+
// Check if still running
|
|
1790
|
+
try {
|
|
1791
|
+
process.kill(worker.pid, 0); // Check if process exists
|
|
1792
|
+
console.log(`Agent still running, sending SIGKILL...`);
|
|
1793
|
+
process.kill(worker.pid, 'SIGKILL');
|
|
1794
|
+
}
|
|
1795
|
+
catch {
|
|
1796
|
+
// Process no longer exists, graceful shutdown worked
|
|
1797
|
+
}
|
|
1798
|
+
}
|
|
1799
|
+
else {
|
|
1800
|
+
// Force kill immediately
|
|
1801
|
+
console.log(`Force killing ${name} (pid: ${worker.pid})...`);
|
|
1802
|
+
process.kill(worker.pid, 'SIGKILL');
|
|
1803
|
+
}
|
|
1804
|
+
console.log(`Killed agent: ${name}`);
|
|
1805
|
+
}
|
|
1806
|
+
catch (err) {
|
|
1807
|
+
if (err.code === 'ESRCH') {
|
|
1808
|
+
console.log(`Agent ${name} is no longer running (pid: ${worker.pid})`);
|
|
1809
|
+
}
|
|
1810
|
+
else {
|
|
1811
|
+
console.error(`Failed to kill ${name}:`, err.message);
|
|
1812
|
+
process.exit(1);
|
|
1813
|
+
}
|
|
1814
|
+
}
|
|
1815
|
+
});
|
|
1816
|
+
// ============================================================================
|
|
1817
|
+
// Cloud commands
|
|
1818
|
+
// ============================================================================
|
|
1819
|
+
const cloudCommand = program
|
|
1820
|
+
.command('cloud')
|
|
1821
|
+
.description('Cloud account and sync commands');
|
|
1822
|
+
cloudCommand
|
|
1823
|
+
.command('link')
|
|
1824
|
+
.description('Link this machine to your Agent Relay Cloud account')
|
|
1825
|
+
.option('--name <name>', 'Name for this machine')
|
|
1826
|
+
.option('--cloud-url <url>', 'Cloud API URL', process.env.AGENT_RELAY_CLOUD_URL || 'https://agent-relay.com')
|
|
1827
|
+
.action(async (options) => {
|
|
1828
|
+
const os = await import('node:os');
|
|
1829
|
+
const crypto = await import('node:crypto');
|
|
1830
|
+
const readline = await import('node:readline');
|
|
1831
|
+
const cloudUrl = options.cloudUrl;
|
|
1832
|
+
const machineName = options.name || os.hostname();
|
|
1833
|
+
// Generate machine ID
|
|
1834
|
+
const dataDir = process.env.AGENT_RELAY_DATA_DIR ||
|
|
1835
|
+
path.join(os.homedir(), '.local', 'share', 'agent-relay');
|
|
1836
|
+
const machineIdPath = path.join(dataDir, 'machine-id');
|
|
1837
|
+
const configPath = path.join(dataDir, 'cloud-config.json');
|
|
1838
|
+
let machineId;
|
|
1839
|
+
if (fs.existsSync(machineIdPath)) {
|
|
1840
|
+
machineId = fs.readFileSync(machineIdPath, 'utf-8').trim();
|
|
1841
|
+
}
|
|
1842
|
+
else {
|
|
1843
|
+
machineId = `${os.hostname()}-${crypto.randomBytes(8).toString('hex')}`;
|
|
1844
|
+
fs.mkdirSync(dataDir, { recursive: true });
|
|
1845
|
+
fs.writeFileSync(machineIdPath, machineId);
|
|
1846
|
+
}
|
|
1847
|
+
console.log('');
|
|
1848
|
+
console.log('🔗 Agent Relay Cloud - Link Machine');
|
|
1849
|
+
console.log('');
|
|
1850
|
+
console.log(`Machine: ${machineName}`);
|
|
1851
|
+
console.log(`ID: ${machineId}`);
|
|
1852
|
+
console.log('');
|
|
1853
|
+
// Generate a temporary code for the browser auth flow
|
|
1854
|
+
const tempCode = crypto.randomBytes(16).toString('hex');
|
|
1855
|
+
// Store temp code for callback
|
|
1856
|
+
const tempCodePath = path.join(dataDir, '.link-code');
|
|
1857
|
+
fs.writeFileSync(tempCodePath, tempCode);
|
|
1858
|
+
const authUrl = `${cloudUrl.replace('/api', '')}/cloud/link?code=${tempCode}&machine=${encodeURIComponent(machineId)}&name=${encodeURIComponent(machineName)}`;
|
|
1859
|
+
console.log('Open this URL in your browser to authenticate:');
|
|
1860
|
+
console.log('');
|
|
1861
|
+
console.log(` ${authUrl}`);
|
|
1862
|
+
console.log('');
|
|
1863
|
+
// Try to open browser automatically
|
|
1864
|
+
try {
|
|
1865
|
+
const openCommand = process.platform === 'darwin' ? 'open' :
|
|
1866
|
+
process.platform === 'win32' ? 'start' : 'xdg-open';
|
|
1867
|
+
await execAsync(`${openCommand} "${authUrl}"`);
|
|
1868
|
+
console.log('(Browser opened automatically)');
|
|
1869
|
+
}
|
|
1870
|
+
catch {
|
|
1871
|
+
console.log('(Copy the URL above and paste it in your browser)');
|
|
1872
|
+
}
|
|
1873
|
+
console.log('');
|
|
1874
|
+
console.log('After authenticating, paste your API key here:');
|
|
1875
|
+
const rl = readline.createInterface({
|
|
1876
|
+
input: process.stdin,
|
|
1877
|
+
output: process.stdout,
|
|
1878
|
+
});
|
|
1879
|
+
const apiKey = await new Promise((resolve) => {
|
|
1880
|
+
rl.question('API Key: ', (answer) => {
|
|
1881
|
+
rl.close();
|
|
1882
|
+
resolve(answer.trim());
|
|
1883
|
+
});
|
|
1884
|
+
});
|
|
1885
|
+
if (!apiKey || !apiKey.startsWith('ar_live_')) {
|
|
1886
|
+
console.error('');
|
|
1887
|
+
console.error('Invalid API key format. Expected ar_live_...');
|
|
1888
|
+
process.exit(1);
|
|
1889
|
+
}
|
|
1890
|
+
// Verify the API key works
|
|
1891
|
+
console.log('');
|
|
1892
|
+
console.log('Verifying API key...');
|
|
1893
|
+
try {
|
|
1894
|
+
const response = await fetch(`${cloudUrl}/api/daemons/heartbeat`, {
|
|
1895
|
+
method: 'POST',
|
|
1896
|
+
headers: {
|
|
1897
|
+
'Authorization': `Bearer ${apiKey}`,
|
|
1898
|
+
'Content-Type': 'application/json',
|
|
1899
|
+
},
|
|
1900
|
+
body: JSON.stringify({
|
|
1901
|
+
agents: [],
|
|
1902
|
+
metrics: { linkedAt: new Date().toISOString() },
|
|
1903
|
+
}),
|
|
1904
|
+
});
|
|
1905
|
+
if (!response.ok) {
|
|
1906
|
+
const error = await response.text();
|
|
1907
|
+
console.error(`Failed to verify API key: ${error}`);
|
|
1908
|
+
process.exit(1);
|
|
1909
|
+
}
|
|
1910
|
+
// Save config
|
|
1911
|
+
const config = {
|
|
1912
|
+
apiKey,
|
|
1913
|
+
cloudUrl,
|
|
1914
|
+
machineId,
|
|
1915
|
+
machineName,
|
|
1916
|
+
linkedAt: new Date().toISOString(),
|
|
1917
|
+
};
|
|
1918
|
+
fs.writeFileSync(configPath, JSON.stringify(config, null, 2));
|
|
1919
|
+
fs.chmodSync(configPath, 0o600); // Secure the file
|
|
1920
|
+
// Clean up temp code
|
|
1921
|
+
if (fs.existsSync(tempCodePath)) {
|
|
1922
|
+
fs.unlinkSync(tempCodePath);
|
|
1923
|
+
}
|
|
1924
|
+
console.log('');
|
|
1925
|
+
console.log('✓ Machine linked successfully!');
|
|
1926
|
+
console.log('');
|
|
1927
|
+
console.log('Your daemon will now sync with Agent Relay Cloud.');
|
|
1928
|
+
console.log('Run `agent-relay up` to start with cloud sync enabled.');
|
|
1929
|
+
console.log('');
|
|
1930
|
+
}
|
|
1931
|
+
catch (err) {
|
|
1932
|
+
console.error(`Failed to connect to cloud: ${err.message}`);
|
|
1933
|
+
process.exit(1);
|
|
1934
|
+
}
|
|
1935
|
+
});
|
|
1936
|
+
cloudCommand
|
|
1937
|
+
.command('unlink')
|
|
1938
|
+
.description('Unlink this machine from Agent Relay Cloud')
|
|
1939
|
+
.action(async () => {
|
|
1940
|
+
const os = await import('node:os');
|
|
1941
|
+
const dataDir = process.env.AGENT_RELAY_DATA_DIR ||
|
|
1942
|
+
path.join(os.homedir(), '.local', 'share', 'agent-relay');
|
|
1943
|
+
const configPath = path.join(dataDir, 'cloud-config.json');
|
|
1944
|
+
if (!fs.existsSync(configPath)) {
|
|
1945
|
+
console.log('This machine is not linked to Agent Relay Cloud.');
|
|
1946
|
+
return;
|
|
1947
|
+
}
|
|
1948
|
+
// Read current config
|
|
1949
|
+
const config = JSON.parse(fs.readFileSync(configPath, 'utf-8'));
|
|
1950
|
+
// Delete config file
|
|
1951
|
+
fs.unlinkSync(configPath);
|
|
1952
|
+
console.log('');
|
|
1953
|
+
console.log('✓ Machine unlinked from Agent Relay Cloud');
|
|
1954
|
+
console.log('');
|
|
1955
|
+
console.log(`Machine ID: ${config.machineId}`);
|
|
1956
|
+
console.log(`Was linked since: ${config.linkedAt}`);
|
|
1957
|
+
console.log('');
|
|
1958
|
+
console.log('Note: The API key has been removed locally. To fully revoke access,');
|
|
1959
|
+
console.log('visit your Agent Relay Cloud dashboard and remove this machine.');
|
|
1960
|
+
console.log('');
|
|
1961
|
+
});
|
|
1962
|
+
cloudCommand
|
|
1963
|
+
.command('status')
|
|
1964
|
+
.description('Show cloud sync status')
|
|
1965
|
+
.action(async () => {
|
|
1966
|
+
const os = await import('node:os');
|
|
1967
|
+
const dataDir = process.env.AGENT_RELAY_DATA_DIR ||
|
|
1968
|
+
path.join(os.homedir(), '.local', 'share', 'agent-relay');
|
|
1969
|
+
const configPath = path.join(dataDir, 'cloud-config.json');
|
|
1970
|
+
if (!fs.existsSync(configPath)) {
|
|
1971
|
+
console.log('');
|
|
1972
|
+
console.log('Cloud sync: Not configured');
|
|
1973
|
+
console.log('');
|
|
1974
|
+
console.log('Run `agent-relay cloud link` to connect to Agent Relay Cloud.');
|
|
1975
|
+
console.log('');
|
|
1976
|
+
return;
|
|
1977
|
+
}
|
|
1978
|
+
const config = JSON.parse(fs.readFileSync(configPath, 'utf-8'));
|
|
1979
|
+
console.log('');
|
|
1980
|
+
console.log('Cloud sync: Enabled');
|
|
1981
|
+
console.log('');
|
|
1982
|
+
console.log(` Machine: ${config.machineName}`);
|
|
1983
|
+
console.log(` ID: ${config.machineId}`);
|
|
1984
|
+
console.log(` Cloud URL: ${config.cloudUrl}`);
|
|
1985
|
+
console.log(` Linked: ${new Date(config.linkedAt).toLocaleString()}`);
|
|
1986
|
+
console.log('');
|
|
1987
|
+
// Check if daemon is running and connected
|
|
1988
|
+
const { getProjectPaths } = await import('@agent-relay/config');
|
|
1989
|
+
const paths = getProjectPaths();
|
|
1990
|
+
if (fs.existsSync(paths.socketPath)) {
|
|
1991
|
+
console.log(' Daemon: Running');
|
|
1992
|
+
// Try to get cloud sync status from daemon
|
|
1993
|
+
try {
|
|
1994
|
+
const response = await fetch(`${config.cloudUrl}/api/daemons/heartbeat`, {
|
|
1995
|
+
method: 'POST',
|
|
1996
|
+
headers: {
|
|
1997
|
+
'Authorization': `Bearer ${config.apiKey}`,
|
|
1998
|
+
'Content-Type': 'application/json',
|
|
1999
|
+
},
|
|
2000
|
+
body: JSON.stringify({ agents: [], metrics: {} }),
|
|
2001
|
+
});
|
|
2002
|
+
if (response.ok) {
|
|
2003
|
+
console.log(' Cloud connection: Online');
|
|
2004
|
+
}
|
|
2005
|
+
else {
|
|
2006
|
+
console.log(' Cloud connection: Error (API key may be invalid)');
|
|
2007
|
+
}
|
|
2008
|
+
}
|
|
2009
|
+
catch (err) {
|
|
2010
|
+
console.log(` Cloud connection: Offline (${err.message})`);
|
|
2011
|
+
}
|
|
2012
|
+
}
|
|
2013
|
+
else {
|
|
2014
|
+
console.log(' Daemon: Not running');
|
|
2015
|
+
console.log(' Cloud connection: Offline (daemon not started)');
|
|
2016
|
+
}
|
|
2017
|
+
console.log('');
|
|
2018
|
+
});
|
|
2019
|
+
cloudCommand
|
|
2020
|
+
.command('sync')
|
|
2021
|
+
.description('Manually sync credentials from cloud')
|
|
2022
|
+
.action(async () => {
|
|
2023
|
+
const os = await import('node:os');
|
|
2024
|
+
const dataDir = process.env.AGENT_RELAY_DATA_DIR ||
|
|
2025
|
+
path.join(os.homedir(), '.local', 'share', 'agent-relay');
|
|
2026
|
+
const configPath = path.join(dataDir, 'cloud-config.json');
|
|
2027
|
+
if (!fs.existsSync(configPath)) {
|
|
2028
|
+
console.error('Not linked to cloud. Run `agent-relay cloud link` first.');
|
|
2029
|
+
process.exit(1);
|
|
2030
|
+
}
|
|
2031
|
+
const config = JSON.parse(fs.readFileSync(configPath, 'utf-8'));
|
|
2032
|
+
console.log('Syncing credentials from cloud...');
|
|
2033
|
+
try {
|
|
2034
|
+
const response = await fetch(`${config.cloudUrl}/api/daemons/credentials`, {
|
|
2035
|
+
headers: {
|
|
2036
|
+
'Authorization': `Bearer ${config.apiKey}`,
|
|
2037
|
+
},
|
|
2038
|
+
});
|
|
2039
|
+
if (!response.ok) {
|
|
2040
|
+
const error = await response.text();
|
|
2041
|
+
console.error(`Failed to sync: ${error}`);
|
|
2042
|
+
process.exit(1);
|
|
2043
|
+
}
|
|
2044
|
+
const data = await response.json();
|
|
2045
|
+
console.log('');
|
|
2046
|
+
console.log(`Synced ${data.credentials.length} provider credentials:`);
|
|
2047
|
+
for (const cred of data.credentials) {
|
|
2048
|
+
console.log(` - ${cred.provider}`);
|
|
2049
|
+
}
|
|
2050
|
+
// Save credentials locally for daemon to use
|
|
2051
|
+
const credentialsPath = path.join(dataDir, 'cloud-credentials.json');
|
|
2052
|
+
fs.writeFileSync(credentialsPath, JSON.stringify(data.credentials, null, 2));
|
|
2053
|
+
fs.chmodSync(credentialsPath, 0o600);
|
|
2054
|
+
console.log('');
|
|
2055
|
+
console.log('✓ Credentials synced successfully');
|
|
2056
|
+
console.log('');
|
|
2057
|
+
}
|
|
2058
|
+
catch (err) {
|
|
2059
|
+
console.error(`Failed to sync: ${err.message}`);
|
|
2060
|
+
process.exit(1);
|
|
2061
|
+
}
|
|
2062
|
+
});
|
|
2063
|
+
// ============================================================================
|
|
2064
|
+
// TRAJECTORY COMMANDS (trail proxy)
|
|
2065
|
+
// ============================================================================
|
|
2066
|
+
// trail - Proxy to trail CLI for trajectory tracking
|
|
2067
|
+
program
|
|
2068
|
+
.command('trail')
|
|
2069
|
+
.description('Trajectory tracking commands (proxies to trail CLI)')
|
|
2070
|
+
.argument('[args...]', 'Arguments to pass to trail CLI')
|
|
2071
|
+
.allowUnknownOption()
|
|
2072
|
+
.action(async (args) => {
|
|
2073
|
+
const { spawn } = await import('node:child_process');
|
|
2074
|
+
const { getProjectPaths } = await import('@agent-relay/config');
|
|
2075
|
+
const { getPrimaryTrajectoriesDir, ensureTrajectoriesDir } = await import('@agent-relay/config/trajectory-config');
|
|
2076
|
+
const paths = getProjectPaths();
|
|
2077
|
+
// Check if trail is available
|
|
2078
|
+
const trailCheck = spawn('which', ['trail'], { stdio: 'pipe' });
|
|
2079
|
+
const trailExists = await new Promise((resolve) => {
|
|
2080
|
+
trailCheck.on('close', (code) => resolve(code === 0));
|
|
2081
|
+
trailCheck.on('error', () => resolve(false));
|
|
2082
|
+
});
|
|
2083
|
+
if (!trailExists) {
|
|
2084
|
+
console.error('trail CLI not found. Install with: npm install -g agent-trajectories');
|
|
2085
|
+
console.log('');
|
|
2086
|
+
console.log('The trail CLI provides trajectory tracking for agent work:');
|
|
2087
|
+
console.log(' trail start "<task>" Start tracking a new trajectory');
|
|
2088
|
+
console.log(' trail status Show current trajectory status');
|
|
2089
|
+
console.log(' trail phase <phase> Transition to PDERO phase');
|
|
2090
|
+
console.log(' trail decision "<choice>" Record a decision');
|
|
2091
|
+
console.log(' trail complete Complete the trajectory');
|
|
2092
|
+
console.log(' trail list List all trajectories');
|
|
2093
|
+
console.log('');
|
|
2094
|
+
console.log('PDERO phases: plan, design, execute, review, observe');
|
|
2095
|
+
process.exit(1);
|
|
2096
|
+
}
|
|
2097
|
+
// Get trajectory storage path based on config (respects opt-in/opt-out)
|
|
2098
|
+
// Uses TRAJECTORIES_DATA_DIR env var which trail CLI reads
|
|
2099
|
+
const trajectoriesDir = getPrimaryTrajectoriesDir(paths.projectRoot);
|
|
2100
|
+
ensureTrajectoriesDir(paths.projectRoot);
|
|
2101
|
+
// Spawn trail with the provided arguments
|
|
2102
|
+
const trailProc = spawn('trail', args, {
|
|
2103
|
+
cwd: paths.projectRoot,
|
|
2104
|
+
stdio: 'inherit',
|
|
2105
|
+
env: {
|
|
2106
|
+
...process.env,
|
|
2107
|
+
// Trajectory env vars override parent shell settings
|
|
2108
|
+
// This ensures config-based TRAJECTORIES_DATA_DIR takes precedence
|
|
2109
|
+
TRAJECTORIES_PROJECT: paths.projectId,
|
|
2110
|
+
TRAJECTORIES_DATA_DIR: trajectoriesDir,
|
|
2111
|
+
},
|
|
2112
|
+
});
|
|
2113
|
+
trailProc.on('close', (code) => {
|
|
2114
|
+
process.exit(code ?? 0);
|
|
2115
|
+
});
|
|
2116
|
+
trailProc.on('error', (err) => {
|
|
2117
|
+
console.error(`Failed to run trail: ${err.message}`);
|
|
2118
|
+
process.exit(1);
|
|
2119
|
+
});
|
|
2120
|
+
});
|
|
2121
|
+
cloudCommand
|
|
2122
|
+
.command('agents')
|
|
2123
|
+
.description('List agents across all linked machines')
|
|
2124
|
+
.option('--json', 'Output as JSON')
|
|
2125
|
+
.action(async (options) => {
|
|
2126
|
+
const os = await import('node:os');
|
|
2127
|
+
const dataDir = process.env.AGENT_RELAY_DATA_DIR ||
|
|
2128
|
+
path.join(os.homedir(), '.local', 'share', 'agent-relay');
|
|
2129
|
+
const configPath = path.join(dataDir, 'cloud-config.json');
|
|
2130
|
+
if (!fs.existsSync(configPath)) {
|
|
2131
|
+
console.error('Not linked to cloud. Run `agent-relay cloud link` first.');
|
|
2132
|
+
process.exit(1);
|
|
2133
|
+
}
|
|
2134
|
+
const config = JSON.parse(fs.readFileSync(configPath, 'utf-8'));
|
|
2135
|
+
try {
|
|
2136
|
+
// Get agents from cloud
|
|
2137
|
+
const response = await fetch(`${config.cloudUrl}/api/daemons/agents`, {
|
|
2138
|
+
method: 'POST',
|
|
2139
|
+
headers: {
|
|
2140
|
+
'Authorization': `Bearer ${config.apiKey}`,
|
|
2141
|
+
'Content-Type': 'application/json',
|
|
2142
|
+
},
|
|
2143
|
+
body: JSON.stringify({ agents: [] }), // Report no agents, just fetch list
|
|
2144
|
+
});
|
|
2145
|
+
if (!response.ok) {
|
|
2146
|
+
const error = await response.text();
|
|
2147
|
+
console.error(`Failed to fetch agents: ${error}`);
|
|
2148
|
+
process.exit(1);
|
|
2149
|
+
}
|
|
2150
|
+
const data = await response.json();
|
|
2151
|
+
if (options.json) {
|
|
2152
|
+
console.log(JSON.stringify(data.allAgents, null, 2));
|
|
2153
|
+
return;
|
|
2154
|
+
}
|
|
2155
|
+
if (!data.allAgents.length) {
|
|
2156
|
+
console.log('No agents found across linked machines.');
|
|
2157
|
+
console.log('Make sure daemons are running on linked machines.');
|
|
2158
|
+
return;
|
|
2159
|
+
}
|
|
2160
|
+
console.log('');
|
|
2161
|
+
console.log('Agents across all linked machines:');
|
|
2162
|
+
console.log('');
|
|
2163
|
+
console.log('NAME STATUS DAEMON MACHINE');
|
|
2164
|
+
console.log('─'.repeat(65));
|
|
2165
|
+
// Group by daemon
|
|
2166
|
+
const byDaemon = new Map();
|
|
2167
|
+
for (const agent of data.allAgents) {
|
|
2168
|
+
const existing = byDaemon.get(agent.daemonName) || [];
|
|
2169
|
+
existing.push(agent);
|
|
2170
|
+
byDaemon.set(agent.daemonName, existing);
|
|
2171
|
+
}
|
|
2172
|
+
for (const [daemonName, agents] of byDaemon.entries()) {
|
|
2173
|
+
for (const agent of agents) {
|
|
2174
|
+
const name = agent.name.padEnd(15);
|
|
2175
|
+
const status = agent.status.padEnd(8);
|
|
2176
|
+
const daemon = daemonName.padEnd(18);
|
|
2177
|
+
const machine = agent.machineId.substring(0, 20);
|
|
2178
|
+
console.log(`${name} ${status} ${daemon} ${machine}`);
|
|
2179
|
+
}
|
|
2180
|
+
}
|
|
2181
|
+
console.log('');
|
|
2182
|
+
console.log(`Total: ${data.allAgents.length} agents on ${byDaemon.size} machines`);
|
|
2183
|
+
console.log('');
|
|
2184
|
+
}
|
|
2185
|
+
catch (err) {
|
|
2186
|
+
console.error(`Failed to fetch agents: ${err.message}`);
|
|
2187
|
+
process.exit(1);
|
|
2188
|
+
}
|
|
2189
|
+
});
|
|
2190
|
+
cloudCommand
|
|
2191
|
+
.command('send')
|
|
2192
|
+
.description('Send a message to an agent on any linked machine')
|
|
2193
|
+
.argument('<agent>', 'Target agent name')
|
|
2194
|
+
.argument('<message>', 'Message to send')
|
|
2195
|
+
.option('--from <name>', 'Sender name', 'cli')
|
|
2196
|
+
.action(async (agent, message, options) => {
|
|
2197
|
+
const os = await import('node:os');
|
|
2198
|
+
const dataDir = process.env.AGENT_RELAY_DATA_DIR ||
|
|
2199
|
+
path.join(os.homedir(), '.local', 'share', 'agent-relay');
|
|
2200
|
+
const configPath = path.join(dataDir, 'cloud-config.json');
|
|
2201
|
+
if (!fs.existsSync(configPath)) {
|
|
2202
|
+
console.error('Not linked to cloud. Run `agent-relay cloud link` first.');
|
|
2203
|
+
process.exit(1);
|
|
2204
|
+
}
|
|
2205
|
+
const config = JSON.parse(fs.readFileSync(configPath, 'utf-8'));
|
|
2206
|
+
console.log(`Sending message to ${agent}...`);
|
|
2207
|
+
try {
|
|
2208
|
+
// First, find which daemon the agent is on
|
|
2209
|
+
const agentsResponse = await fetch(`${config.cloudUrl}/api/daemons/agents`, {
|
|
2210
|
+
method: 'POST',
|
|
2211
|
+
headers: {
|
|
2212
|
+
'Authorization': `Bearer ${config.apiKey}`,
|
|
2213
|
+
'Content-Type': 'application/json',
|
|
2214
|
+
},
|
|
2215
|
+
body: JSON.stringify({ agents: [] }),
|
|
2216
|
+
});
|
|
2217
|
+
if (!agentsResponse.ok) {
|
|
2218
|
+
const error = await agentsResponse.text();
|
|
2219
|
+
console.error(`Failed to find agent: ${error}`);
|
|
2220
|
+
process.exit(1);
|
|
2221
|
+
}
|
|
2222
|
+
const agentsData = await agentsResponse.json();
|
|
2223
|
+
const targetAgent = agentsData.allAgents.find(a => a.name === agent);
|
|
2224
|
+
if (!targetAgent) {
|
|
2225
|
+
console.error(`Agent "${agent}" not found.`);
|
|
2226
|
+
console.log('Available agents:');
|
|
2227
|
+
for (const a of agentsData.allAgents) {
|
|
2228
|
+
console.log(` - ${a.name} (on ${a.daemonName})`);
|
|
2229
|
+
}
|
|
2230
|
+
process.exit(1);
|
|
2231
|
+
}
|
|
2232
|
+
// Send the message
|
|
2233
|
+
const sendResponse = await fetch(`${config.cloudUrl}/api/daemons/message`, {
|
|
2234
|
+
method: 'POST',
|
|
2235
|
+
headers: {
|
|
2236
|
+
'Authorization': `Bearer ${config.apiKey}`,
|
|
2237
|
+
'Content-Type': 'application/json',
|
|
2238
|
+
},
|
|
2239
|
+
body: JSON.stringify({
|
|
2240
|
+
targetDaemonId: targetAgent.daemonId,
|
|
2241
|
+
targetAgent: agent,
|
|
2242
|
+
message: {
|
|
2243
|
+
from: options.from,
|
|
2244
|
+
content: message,
|
|
2245
|
+
},
|
|
2246
|
+
}),
|
|
2247
|
+
});
|
|
2248
|
+
if (!sendResponse.ok) {
|
|
2249
|
+
const error = await sendResponse.text();
|
|
2250
|
+
console.error(`Failed to send message: ${error}`);
|
|
2251
|
+
process.exit(1);
|
|
2252
|
+
}
|
|
2253
|
+
console.log('');
|
|
2254
|
+
console.log(`✓ Message sent to ${agent} on ${targetAgent.daemonName}`);
|
|
2255
|
+
console.log('');
|
|
2256
|
+
}
|
|
2257
|
+
catch (err) {
|
|
2258
|
+
console.error(`Failed to send message: ${err.message}`);
|
|
2259
|
+
process.exit(1);
|
|
2260
|
+
}
|
|
2261
|
+
});
|
|
2262
|
+
cloudCommand
|
|
2263
|
+
.command('daemons')
|
|
2264
|
+
.description('List all linked daemon instances')
|
|
2265
|
+
.option('--json', 'Output as JSON')
|
|
2266
|
+
.action(async (_options) => {
|
|
2267
|
+
const os = await import('node:os');
|
|
2268
|
+
const dataDir = process.env.AGENT_RELAY_DATA_DIR ||
|
|
2269
|
+
path.join(os.homedir(), '.local', 'share', 'agent-relay');
|
|
2270
|
+
const configPath = path.join(dataDir, 'cloud-config.json');
|
|
2271
|
+
if (!fs.existsSync(configPath)) {
|
|
2272
|
+
console.error('Not linked to cloud. Run `agent-relay cloud link` first.');
|
|
2273
|
+
process.exit(1);
|
|
2274
|
+
}
|
|
2275
|
+
const config = JSON.parse(fs.readFileSync(configPath, 'utf-8'));
|
|
2276
|
+
try {
|
|
2277
|
+
// Get daemons list (requires browser auth, so we use a workaround)
|
|
2278
|
+
// For now, just show what we know about our own daemon
|
|
2279
|
+
console.log('');
|
|
2280
|
+
console.log('Linked Daemon:');
|
|
2281
|
+
console.log('');
|
|
2282
|
+
console.log(` Machine: ${config.machineName}`);
|
|
2283
|
+
console.log(` ID: ${config.machineId}`);
|
|
2284
|
+
console.log(` Cloud: ${config.cloudUrl}`);
|
|
2285
|
+
console.log(` Linked: ${new Date(config.linkedAt).toLocaleString()}`);
|
|
2286
|
+
console.log('');
|
|
2287
|
+
console.log('Note: To see all linked daemons, visit your cloud dashboard.');
|
|
2288
|
+
console.log('');
|
|
2289
|
+
}
|
|
2290
|
+
catch (err) {
|
|
2291
|
+
console.error(`Failed: ${err.message}`);
|
|
2292
|
+
process.exit(1);
|
|
2293
|
+
}
|
|
2294
|
+
});
|
|
2295
|
+
// ============================================================================
|
|
2296
|
+
// Monitoring commands (metrics, health, profiler)
|
|
2297
|
+
// ============================================================================
|
|
2298
|
+
// metrics - Show agent memory metrics
|
|
2299
|
+
program
|
|
2300
|
+
.command('metrics')
|
|
2301
|
+
.description('Show agent memory metrics and resource usage')
|
|
2302
|
+
.option('--agent <name>', 'Show metrics for specific agent')
|
|
2303
|
+
.option('--port <port>', 'Dashboard port', DEFAULT_DASHBOARD_PORT)
|
|
2304
|
+
.option('--json', 'Output as JSON')
|
|
2305
|
+
.option('--watch', 'Continuously update metrics')
|
|
2306
|
+
.option('--interval <ms>', 'Update interval for watch mode', '5000')
|
|
2307
|
+
.action(async (options) => {
|
|
2308
|
+
const port = options.port || DEFAULT_DASHBOARD_PORT;
|
|
2309
|
+
const fetchMetrics = async () => {
|
|
2310
|
+
try {
|
|
2311
|
+
const response = await fetch(`http://localhost:${port}/api/metrics/agents`);
|
|
2312
|
+
if (!response.ok) {
|
|
2313
|
+
throw new Error(`HTTP ${response.status}`);
|
|
2314
|
+
}
|
|
2315
|
+
return await response.json();
|
|
2316
|
+
}
|
|
2317
|
+
catch (err) {
|
|
2318
|
+
if (err.code === 'ECONNREFUSED') {
|
|
2319
|
+
console.error(`Cannot connect to dashboard at port ${port}. Is the daemon running?`);
|
|
2320
|
+
console.log(`Run 'agent-relay up' to start the daemon.`);
|
|
2321
|
+
}
|
|
2322
|
+
else {
|
|
2323
|
+
console.error(`Failed to fetch metrics: ${err.message}`);
|
|
2324
|
+
}
|
|
2325
|
+
process.exit(1);
|
|
2326
|
+
}
|
|
2327
|
+
};
|
|
2328
|
+
const formatBytes = (bytes) => {
|
|
2329
|
+
if (bytes === 0)
|
|
2330
|
+
return '0 B';
|
|
2331
|
+
const k = 1024;
|
|
2332
|
+
const sizes = ['B', 'KB', 'MB', 'GB'];
|
|
2333
|
+
const i = Math.floor(Math.log(Math.abs(bytes)) / Math.log(k));
|
|
2334
|
+
return `${(bytes / Math.pow(k, i)).toFixed(1)} ${sizes[i]}`;
|
|
2335
|
+
};
|
|
2336
|
+
const formatUptime = (ms) => {
|
|
2337
|
+
if (ms < 60000)
|
|
2338
|
+
return `${Math.floor(ms / 1000)}s`;
|
|
2339
|
+
if (ms < 3600000)
|
|
2340
|
+
return `${Math.floor(ms / 60000)}m`;
|
|
2341
|
+
return `${Math.floor(ms / 3600000)}h ${Math.floor((ms % 3600000) / 60000)}m`;
|
|
2342
|
+
};
|
|
2343
|
+
const displayMetrics = (data) => {
|
|
2344
|
+
let agents = data.agents;
|
|
2345
|
+
if (options.agent) {
|
|
2346
|
+
agents = agents.filter(a => a.name === options.agent);
|
|
2347
|
+
if (agents.length === 0) {
|
|
2348
|
+
console.error(`Agent "${options.agent}" not found`);
|
|
2349
|
+
return;
|
|
2350
|
+
}
|
|
2351
|
+
}
|
|
2352
|
+
if (options.json) {
|
|
2353
|
+
console.log(JSON.stringify({ agents, system: data.system }, null, 2));
|
|
2354
|
+
return;
|
|
2355
|
+
}
|
|
2356
|
+
if (options.watch) {
|
|
2357
|
+
// Clear screen for watch mode
|
|
2358
|
+
console.clear();
|
|
2359
|
+
console.log(`Agent Metrics (updating every ${options.interval}ms) [Ctrl+C to stop]`);
|
|
2360
|
+
console.log(`System: ${formatBytes(data.system.heapUsed)} heap / ${formatBytes(data.system.freeMemory)} free`);
|
|
2361
|
+
console.log('');
|
|
2362
|
+
}
|
|
2363
|
+
if (agents.length === 0) {
|
|
2364
|
+
console.log('No agents with memory metrics.');
|
|
2365
|
+
console.log('Ensure agents are running and memory monitoring is enabled.');
|
|
2366
|
+
return;
|
|
2367
|
+
}
|
|
2368
|
+
console.log('AGENT PID MEMORY CPU TREND ALERT UPTIME');
|
|
2369
|
+
console.log('─'.repeat(75));
|
|
2370
|
+
for (const agent of agents) {
|
|
2371
|
+
const name = agent.name.padEnd(15);
|
|
2372
|
+
const pid = (agent.pid?.toString() || '-').padEnd(8);
|
|
2373
|
+
const memory = formatBytes(agent.rssBytes || 0).padEnd(11);
|
|
2374
|
+
const cpu = ((agent.cpuPercent?.toFixed(1) || '0') + '%').padEnd(6);
|
|
2375
|
+
const trend = (agent.trend || 'unknown').padEnd(11);
|
|
2376
|
+
const alertColors = {
|
|
2377
|
+
normal: 'normal',
|
|
2378
|
+
warning: '\x1b[33mwarning\x1b[0m',
|
|
2379
|
+
critical: '\x1b[31mcritical\x1b[0m',
|
|
2380
|
+
oom_imminent: '\x1b[31;1mOOM!\x1b[0m',
|
|
2381
|
+
};
|
|
2382
|
+
const alert = (alertColors[agent.alertLevel || 'normal'] || agent.alertLevel || '-').padEnd(9);
|
|
2383
|
+
const uptime = formatUptime(agent.uptimeMs || 0);
|
|
2384
|
+
console.log(`${name} ${pid} ${memory} ${cpu} ${trend} ${alert} ${uptime}`);
|
|
2385
|
+
}
|
|
2386
|
+
if (!options.watch) {
|
|
2387
|
+
console.log('');
|
|
2388
|
+
console.log(`Total: ${agents.length} agent(s)`);
|
|
2389
|
+
if (agents.some(a => a.alertLevel && a.alertLevel !== 'normal')) {
|
|
2390
|
+
console.log('');
|
|
2391
|
+
console.log('⚠️ Some agents have elevated memory usage. Run `agent-relay health` for details.');
|
|
2392
|
+
}
|
|
2393
|
+
}
|
|
2394
|
+
};
|
|
2395
|
+
if (options.watch) {
|
|
2396
|
+
const interval = parseInt(options.interval || '5000', 10);
|
|
2397
|
+
const update = async () => {
|
|
2398
|
+
try {
|
|
2399
|
+
const data = await fetchMetrics();
|
|
2400
|
+
displayMetrics(data);
|
|
2401
|
+
}
|
|
2402
|
+
catch {
|
|
2403
|
+
// Error already logged in fetchMetrics
|
|
2404
|
+
}
|
|
2405
|
+
};
|
|
2406
|
+
process.on('SIGINT', () => {
|
|
2407
|
+
console.log('\nStopped watching metrics.');
|
|
2408
|
+
process.exit(0);
|
|
2409
|
+
});
|
|
2410
|
+
await update();
|
|
2411
|
+
setInterval(update, interval);
|
|
2412
|
+
}
|
|
2413
|
+
else {
|
|
2414
|
+
const data = await fetchMetrics();
|
|
2415
|
+
displayMetrics(data);
|
|
2416
|
+
}
|
|
2417
|
+
});
|
|
2418
|
+
// health - Show crash insights and system health
|
|
2419
|
+
program
|
|
2420
|
+
.command('health')
|
|
2421
|
+
.description('Show system health, crash insights, and recommendations')
|
|
2422
|
+
.option('--port <port>', 'Dashboard port', DEFAULT_DASHBOARD_PORT)
|
|
2423
|
+
.option('--json', 'Output as JSON')
|
|
2424
|
+
.option('--crashes', 'Show recent crash history')
|
|
2425
|
+
.option('--alerts', 'Show unacknowledged alerts')
|
|
2426
|
+
.action(async (options) => {
|
|
2427
|
+
const port = options.port || DEFAULT_DASHBOARD_PORT;
|
|
2428
|
+
try {
|
|
2429
|
+
const response = await fetch(`http://localhost:${port}/api/metrics/health`);
|
|
2430
|
+
if (!response.ok) {
|
|
2431
|
+
throw new Error(`HTTP ${response.status}`);
|
|
2432
|
+
}
|
|
2433
|
+
const data = await response.json();
|
|
2434
|
+
if (options.json) {
|
|
2435
|
+
console.log(JSON.stringify(data, null, 2));
|
|
2436
|
+
return;
|
|
2437
|
+
}
|
|
2438
|
+
// Health score with color
|
|
2439
|
+
const scoreColor = data.healthScore >= 80 ? '\x1b[32m' : // Green
|
|
2440
|
+
data.healthScore >= 50 ? '\x1b[33m' : // Yellow
|
|
2441
|
+
'\x1b[31m'; // Red
|
|
2442
|
+
const resetColor = '\x1b[0m';
|
|
2443
|
+
console.log('');
|
|
2444
|
+
console.log('═══════════════════════════════════════════════════════════════');
|
|
2445
|
+
console.log(` SYSTEM HEALTH: ${scoreColor}${data.healthScore}/100${resetColor}`);
|
|
2446
|
+
console.log('═══════════════════════════════════════════════════════════════');
|
|
2447
|
+
console.log('');
|
|
2448
|
+
console.log(` ${data.summary}`);
|
|
2449
|
+
console.log('');
|
|
2450
|
+
// Show stats
|
|
2451
|
+
console.log(` Agents: ${data.stats.agentCount}`);
|
|
2452
|
+
console.log(` Crashes (24h): ${data.stats.totalCrashes24h}`);
|
|
2453
|
+
console.log(` Alerts (24h): ${data.stats.totalAlerts24h}`);
|
|
2454
|
+
console.log('');
|
|
2455
|
+
// Show issues
|
|
2456
|
+
if (data.issues.length > 0) {
|
|
2457
|
+
console.log(' ISSUES:');
|
|
2458
|
+
for (const issue of data.issues) {
|
|
2459
|
+
const icon = issue.severity === 'critical' ? '🔴' :
|
|
2460
|
+
issue.severity === 'high' ? '🟠' :
|
|
2461
|
+
issue.severity === 'medium' ? '🟡' : '🔵';
|
|
2462
|
+
console.log(` ${icon} ${issue.message}`);
|
|
2463
|
+
}
|
|
2464
|
+
console.log('');
|
|
2465
|
+
}
|
|
2466
|
+
// Show recommendations
|
|
2467
|
+
if (data.recommendations.length > 0) {
|
|
2468
|
+
console.log(' RECOMMENDATIONS:');
|
|
2469
|
+
for (const rec of data.recommendations) {
|
|
2470
|
+
console.log(` → ${rec}`);
|
|
2471
|
+
}
|
|
2472
|
+
console.log('');
|
|
2473
|
+
}
|
|
2474
|
+
// Show crashes if requested
|
|
2475
|
+
if (options.crashes && data.crashes.length > 0) {
|
|
2476
|
+
console.log(' RECENT CRASHES:');
|
|
2477
|
+
console.log(' ─────────────────────────────────────────────────────────────');
|
|
2478
|
+
for (const crash of data.crashes.slice(0, 10)) {
|
|
2479
|
+
const time = new Date(crash.crashedAt).toLocaleString();
|
|
2480
|
+
console.log(` ${crash.agentName} - ${time}`);
|
|
2481
|
+
console.log(` Cause: ${crash.likelyCause} | ${crash.summary.slice(0, 60)}...`);
|
|
2482
|
+
}
|
|
2483
|
+
console.log('');
|
|
2484
|
+
}
|
|
2485
|
+
// Show alerts if requested
|
|
2486
|
+
if (options.alerts && data.alerts.length > 0) {
|
|
2487
|
+
console.log(' UNACKNOWLEDGED ALERTS:');
|
|
2488
|
+
console.log(' ─────────────────────────────────────────────────────────────');
|
|
2489
|
+
for (const alert of data.alerts.slice(0, 10)) {
|
|
2490
|
+
const _time = new Date(alert.createdAt).toLocaleString();
|
|
2491
|
+
const icon = alert.alertType === 'oom_imminent' ? '🔴' :
|
|
2492
|
+
alert.alertType === 'critical' ? '🟠' : '🟡';
|
|
2493
|
+
console.log(` ${icon} ${alert.agentName} - ${alert.alertType}`);
|
|
2494
|
+
console.log(` ${alert.message}`);
|
|
2495
|
+
}
|
|
2496
|
+
console.log('');
|
|
2497
|
+
}
|
|
2498
|
+
console.log('═══════════════════════════════════════════════════════════════');
|
|
2499
|
+
console.log('');
|
|
2500
|
+
if (!options.crashes && data.stats.totalCrashes24h > 0) {
|
|
2501
|
+
console.log(' Tip: Run `agent-relay health --crashes` to see crash details');
|
|
2502
|
+
}
|
|
2503
|
+
if (!options.alerts && data.stats.totalAlerts24h > 0) {
|
|
2504
|
+
console.log(' Tip: Run `agent-relay health --alerts` to see alerts');
|
|
2505
|
+
}
|
|
2506
|
+
console.log('');
|
|
2507
|
+
}
|
|
2508
|
+
catch (err) {
|
|
2509
|
+
if (err.code === 'ECONNREFUSED') {
|
|
2510
|
+
console.error(`Cannot connect to dashboard at port ${port}. Is the daemon running?`);
|
|
2511
|
+
console.log(`Run 'agent-relay up' to start the daemon.`);
|
|
2512
|
+
}
|
|
2513
|
+
else {
|
|
2514
|
+
console.error(`Failed to fetch health data: ${err.message}`);
|
|
2515
|
+
}
|
|
2516
|
+
process.exit(1);
|
|
2517
|
+
}
|
|
2518
|
+
});
|
|
2519
|
+
// profile - Run agent with profiling enabled
|
|
2520
|
+
program
|
|
2521
|
+
.command('profile')
|
|
2522
|
+
.description('Run an agent with memory profiling enabled')
|
|
2523
|
+
.argument('<command...>', 'Command to profile')
|
|
2524
|
+
.option('-n, --name <name>', 'Agent name')
|
|
2525
|
+
.option('--heap-snapshot-interval <ms>', 'Take heap snapshots at interval (ms)', '60000')
|
|
2526
|
+
.option('--output-dir <dir>', 'Directory for profile output', './profiles')
|
|
2527
|
+
.option('--expose-gc', 'Expose garbage collector for manual GC')
|
|
2528
|
+
.action(async (commandParts, options) => {
|
|
2529
|
+
const { getProjectPaths } = await import('@agent-relay/config');
|
|
2530
|
+
if (!commandParts || commandParts.length === 0) {
|
|
2531
|
+
console.error('No command specified');
|
|
2532
|
+
process.exit(1);
|
|
2533
|
+
}
|
|
2534
|
+
const [cmd, ...args] = commandParts;
|
|
2535
|
+
const agentName = options.name ?? generateAgentName();
|
|
2536
|
+
const outputDir = options.outputDir || './profiles';
|
|
2537
|
+
const snapshotInterval = parseInt(options.heapSnapshotInterval || '60000', 10);
|
|
2538
|
+
// Create output directory
|
|
2539
|
+
if (!fs.existsSync(outputDir)) {
|
|
2540
|
+
fs.mkdirSync(outputDir, { recursive: true });
|
|
2541
|
+
}
|
|
2542
|
+
console.log('');
|
|
2543
|
+
console.log('🔬 Agent Relay Profiler');
|
|
2544
|
+
console.log('');
|
|
2545
|
+
console.log(` Agent: ${agentName}`);
|
|
2546
|
+
console.log(` Command: ${cmd} ${args.join(' ')}`);
|
|
2547
|
+
console.log(` Output: ${outputDir}`);
|
|
2548
|
+
console.log(` Heap snapshots: every ${snapshotInterval}ms`);
|
|
2549
|
+
console.log('');
|
|
2550
|
+
// Build Node.js flags for profiling
|
|
2551
|
+
const nodeFlags = [
|
|
2552
|
+
'--inspect', // Enable inspector
|
|
2553
|
+
'--inspect-brk=0', // Don't actually break, just enable
|
|
2554
|
+
];
|
|
2555
|
+
if (options.exposeGc) {
|
|
2556
|
+
nodeFlags.push('--expose-gc');
|
|
2557
|
+
}
|
|
2558
|
+
// Set environment variables for profiling
|
|
2559
|
+
const profileEnv = {
|
|
2560
|
+
...process.env,
|
|
2561
|
+
NODE_OPTIONS: `${process.env.NODE_OPTIONS || ''} ${nodeFlags.join(' ')}`.trim(),
|
|
2562
|
+
AGENT_RELAY_PROFILE_ENABLED: '1',
|
|
2563
|
+
AGENT_RELAY_PROFILE_OUTPUT: outputDir,
|
|
2564
|
+
AGENT_RELAY_PROFILE_INTERVAL: snapshotInterval.toString(),
|
|
2565
|
+
};
|
|
2566
|
+
console.log('Starting profiled agent...');
|
|
2567
|
+
console.log('');
|
|
2568
|
+
// Use the regular wrapper but with profiling environment
|
|
2569
|
+
const paths = getProjectPaths();
|
|
2570
|
+
const wrapper = new RelayPtyOrchestrator({
|
|
2571
|
+
name: agentName,
|
|
2572
|
+
command: cmd,
|
|
2573
|
+
args,
|
|
2574
|
+
socketPath: paths.socketPath,
|
|
2575
|
+
cwd: paths.projectRoot,
|
|
2576
|
+
env: profileEnv,
|
|
2577
|
+
streamLogs: true,
|
|
2578
|
+
});
|
|
2579
|
+
// Start memory sampling
|
|
2580
|
+
const sampleInterval = setInterval(() => {
|
|
2581
|
+
const memUsage = process.memoryUsage();
|
|
2582
|
+
const timestamp = new Date().toISOString();
|
|
2583
|
+
const sample = {
|
|
2584
|
+
timestamp,
|
|
2585
|
+
heapUsed: memUsage.heapUsed,
|
|
2586
|
+
heapTotal: memUsage.heapTotal,
|
|
2587
|
+
external: memUsage.external,
|
|
2588
|
+
rss: memUsage.rss,
|
|
2589
|
+
};
|
|
2590
|
+
// Append to samples file
|
|
2591
|
+
const samplesFile = path.join(outputDir, `${agentName}-memory.jsonl`);
|
|
2592
|
+
fs.appendFileSync(samplesFile, JSON.stringify(sample) + '\n');
|
|
2593
|
+
}, 5000);
|
|
2594
|
+
process.on('SIGINT', async () => {
|
|
2595
|
+
clearInterval(sampleInterval);
|
|
2596
|
+
console.log('\n');
|
|
2597
|
+
console.log('Profiling stopped.');
|
|
2598
|
+
console.log('');
|
|
2599
|
+
console.log(`Profile data saved to: ${outputDir}/`);
|
|
2600
|
+
console.log(` - ${agentName}-memory.jsonl (memory samples)`);
|
|
2601
|
+
console.log('');
|
|
2602
|
+
console.log('To analyze:');
|
|
2603
|
+
console.log(` 1. Open chrome://inspect in Chrome`);
|
|
2604
|
+
console.log(` 2. Load CPU/heap profiles from ${outputDir}/`);
|
|
2605
|
+
console.log('');
|
|
2606
|
+
wrapper.stop();
|
|
2607
|
+
process.exit(0);
|
|
2608
|
+
});
|
|
2609
|
+
await wrapper.start();
|
|
2610
|
+
console.log(`Profiling ${agentName}... Press Ctrl+C to stop.`);
|
|
2611
|
+
});
|
|
2612
|
+
// ============================================================================
|
|
2613
|
+
// codex-auth - SSH tunnel helper for Codex/OpenAI authentication
|
|
2614
|
+
// ============================================================================
|
|
2615
|
+
program
|
|
2616
|
+
.command('codex-auth')
|
|
2617
|
+
.description('Connect Codex via SSH tunnel to workspace (run this when connecting Codex in Agent Relay)')
|
|
2618
|
+
.option('--workspace <id>', 'Workspace ID to connect to')
|
|
2619
|
+
.option('--cloud-url <url>', 'Cloud API URL', process.env.AGENT_RELAY_CLOUD_URL || 'https://agent-relay.com')
|
|
2620
|
+
.option('--token <token>', 'CLI authentication token (from dashboard)')
|
|
2621
|
+
.option('--session-cookie <cookie>', 'Session cookie for authentication (deprecated, use --token)')
|
|
2622
|
+
.option('--timeout <seconds>', 'Timeout in seconds (default: 300)', '300')
|
|
2623
|
+
.action(async (options) => {
|
|
2624
|
+
const TIMEOUT_MS = parseInt(options.timeout, 10) * 1000;
|
|
2625
|
+
const CLOUD_URL = options.cloudUrl.replace(/\/$/, '');
|
|
2626
|
+
const TUNNEL_PORT = 1455;
|
|
2627
|
+
// Colors for terminal output
|
|
2628
|
+
const cyan = (s) => `\x1b[36m${s}\x1b[0m`;
|
|
2629
|
+
const green = (s) => `\x1b[32m${s}\x1b[0m`;
|
|
2630
|
+
const yellow = (s) => `\x1b[33m${s}\x1b[0m`;
|
|
2631
|
+
const red = (s) => `\x1b[31m${s}\x1b[0m`;
|
|
2632
|
+
const dim = (s) => `\x1b[2m${s}\x1b[0m`;
|
|
2633
|
+
console.log('');
|
|
2634
|
+
console.log(cyan('═══════════════════════════════════════════════════'));
|
|
2635
|
+
console.log(cyan(' Codex Authentication Helper'));
|
|
2636
|
+
console.log(cyan('═══════════════════════════════════════════════════'));
|
|
2637
|
+
console.log('');
|
|
2638
|
+
if (!options.workspace) {
|
|
2639
|
+
console.log(red('Missing --workspace parameter.'));
|
|
2640
|
+
console.log('');
|
|
2641
|
+
console.log('To connect Codex, follow these steps:');
|
|
2642
|
+
console.log('');
|
|
2643
|
+
console.log(' 1. Go to the Agent Relay dashboard');
|
|
2644
|
+
console.log(' 2. Click "Connect with Codex" (Settings → AI Providers)');
|
|
2645
|
+
console.log(' 3. Copy the command shown (it includes the workspace ID and token)');
|
|
2646
|
+
console.log(' 4. Run the command in your terminal');
|
|
2647
|
+
console.log('');
|
|
2648
|
+
console.log('The command will look like:');
|
|
2649
|
+
console.log(cyan(' npx agent-relay codex-auth --workspace=<ID> --token=<TOKEN>'));
|
|
2650
|
+
console.log('');
|
|
2651
|
+
process.exit(1);
|
|
2652
|
+
}
|
|
2653
|
+
const workspaceId = options.workspace;
|
|
2654
|
+
console.log(`Workspace: ${workspaceId.slice(0, 8)}...`);
|
|
2655
|
+
// Get tunnel info from cloud API
|
|
2656
|
+
console.log('Getting workspace connection info...');
|
|
2657
|
+
const headers = { 'Content-Type': 'application/json' };
|
|
2658
|
+
if (options.sessionCookie) {
|
|
2659
|
+
headers['Cookie'] = options.sessionCookie;
|
|
2660
|
+
}
|
|
2661
|
+
// Validate token is provided
|
|
2662
|
+
if (!options.token && !options.sessionCookie) {
|
|
2663
|
+
console.log(red('Missing --token parameter.'));
|
|
2664
|
+
console.log('');
|
|
2665
|
+
console.log('The token is provided by the dashboard when you click "Connect with Codex".');
|
|
2666
|
+
console.log('Copy the complete command from the dashboard and paste it here.');
|
|
2667
|
+
console.log('');
|
|
2668
|
+
process.exit(1);
|
|
2669
|
+
}
|
|
2670
|
+
let tunnelInfo;
|
|
2671
|
+
try {
|
|
2672
|
+
// Build URL with token query parameter
|
|
2673
|
+
const tunnelInfoUrl = new URL(`${CLOUD_URL}/api/auth/codex-helper/tunnel-info/${workspaceId}`);
|
|
2674
|
+
if (options.token) {
|
|
2675
|
+
tunnelInfoUrl.searchParams.set('token', options.token);
|
|
2676
|
+
}
|
|
2677
|
+
const response = await fetch(tunnelInfoUrl.toString(), {
|
|
2678
|
+
method: 'GET',
|
|
2679
|
+
headers,
|
|
2680
|
+
credentials: 'include',
|
|
2681
|
+
});
|
|
2682
|
+
if (!response.ok) {
|
|
2683
|
+
const errorData = await response.json();
|
|
2684
|
+
console.log(red(`Failed to get tunnel info: ${errorData.error || response.statusText}`));
|
|
2685
|
+
process.exit(1);
|
|
2686
|
+
}
|
|
2687
|
+
tunnelInfo = await response.json();
|
|
2688
|
+
}
|
|
2689
|
+
catch (err) {
|
|
2690
|
+
console.log(red(`Failed to connect to cloud API: ${err instanceof Error ? err.message : String(err)}`));
|
|
2691
|
+
process.exit(1);
|
|
2692
|
+
}
|
|
2693
|
+
console.log(`Workspace: ${cyan(tunnelInfo.workspaceName)}`);
|
|
2694
|
+
console.log('');
|
|
2695
|
+
// Establish SSH tunnel using ssh2 library (no external tools needed)
|
|
2696
|
+
console.log(yellow('Establishing SSH tunnel...'));
|
|
2697
|
+
console.log(dim(` SSH: ${tunnelInfo.host}:${tunnelInfo.port}`));
|
|
2698
|
+
console.log(dim(` Tunnel: localhost:${TUNNEL_PORT} → workspace:${tunnelInfo.tunnelPort}`));
|
|
2699
|
+
console.log('');
|
|
2700
|
+
const { Client } = await import('ssh2');
|
|
2701
|
+
const net = await import('node:net');
|
|
2702
|
+
const sshClient = new Client();
|
|
2703
|
+
// Use object to hold server reference (avoids TypeScript narrowing issues)
|
|
2704
|
+
const tunnel = { server: null };
|
|
2705
|
+
let tunnelReady = false;
|
|
2706
|
+
let tunnelError = null;
|
|
2707
|
+
// Create a promise that resolves when tunnel is ready or rejects on error
|
|
2708
|
+
const tunnelPromise = new Promise((resolve, reject) => {
|
|
2709
|
+
sshClient.on('ready', () => {
|
|
2710
|
+
// Create local server that forwards connections through SSH
|
|
2711
|
+
tunnel.server = net.createServer((localSocket) => {
|
|
2712
|
+
sshClient.forwardOut('127.0.0.1', TUNNEL_PORT, 'localhost', tunnelInfo.tunnelPort, (err, stream) => {
|
|
2713
|
+
if (err) {
|
|
2714
|
+
localSocket.end();
|
|
2715
|
+
return;
|
|
2716
|
+
}
|
|
2717
|
+
localSocket.pipe(stream).pipe(localSocket);
|
|
2718
|
+
});
|
|
2719
|
+
});
|
|
2720
|
+
tunnel.server.on('error', (err) => {
|
|
2721
|
+
if (err.code === 'EADDRINUSE') {
|
|
2722
|
+
tunnelError = `Port ${TUNNEL_PORT} is already in use. Close any other applications using this port.`;
|
|
2723
|
+
}
|
|
2724
|
+
else {
|
|
2725
|
+
tunnelError = err.message;
|
|
2726
|
+
}
|
|
2727
|
+
reject(new Error(tunnelError));
|
|
2728
|
+
});
|
|
2729
|
+
tunnel.server.listen(TUNNEL_PORT, '127.0.0.1', () => {
|
|
2730
|
+
tunnelReady = true;
|
|
2731
|
+
resolve();
|
|
2732
|
+
});
|
|
2733
|
+
});
|
|
2734
|
+
sshClient.on('error', (err) => {
|
|
2735
|
+
if (err.message.includes('Authentication')) {
|
|
2736
|
+
tunnelError = 'SSH authentication failed. Check the password.';
|
|
2737
|
+
}
|
|
2738
|
+
else if (err.message.includes('ECONNREFUSED')) {
|
|
2739
|
+
tunnelError = `Cannot connect to SSH server at ${tunnelInfo.host}:${tunnelInfo.port}. Is the workspace running and SSH enabled?`;
|
|
2740
|
+
}
|
|
2741
|
+
else if (err.message.includes('ENOTFOUND') || err.message.includes('getaddrinfo')) {
|
|
2742
|
+
tunnelError = `Cannot resolve hostname: ${tunnelInfo.host}. Check network connectivity.`;
|
|
2743
|
+
}
|
|
2744
|
+
else if (err.message.includes('ETIMEDOUT')) {
|
|
2745
|
+
tunnelError = `Connection timed out to ${tunnelInfo.host}:${tunnelInfo.port}. Is the workspace running?`;
|
|
2746
|
+
}
|
|
2747
|
+
else {
|
|
2748
|
+
tunnelError = `SSH error: ${err.message}`;
|
|
2749
|
+
}
|
|
2750
|
+
reject(new Error(tunnelError));
|
|
2751
|
+
});
|
|
2752
|
+
sshClient.on('close', () => {
|
|
2753
|
+
if (!tunnelReady) {
|
|
2754
|
+
// Only set error if not already set by error handler
|
|
2755
|
+
if (!tunnelError) {
|
|
2756
|
+
tunnelError = `SSH connection to ${tunnelInfo.host}:${tunnelInfo.port} closed unexpectedly. The workspace may not have SSH enabled or the port may be blocked.`;
|
|
2757
|
+
}
|
|
2758
|
+
reject(new Error(tunnelError));
|
|
2759
|
+
}
|
|
2760
|
+
});
|
|
2761
|
+
// Connect to SSH server
|
|
2762
|
+
sshClient.connect({
|
|
2763
|
+
host: tunnelInfo.host,
|
|
2764
|
+
port: tunnelInfo.port,
|
|
2765
|
+
username: tunnelInfo.user,
|
|
2766
|
+
password: tunnelInfo.password,
|
|
2767
|
+
readyTimeout: 10000,
|
|
2768
|
+
// Disable host key checking for simplicity (workspace containers)
|
|
2769
|
+
hostVerifier: () => true,
|
|
2770
|
+
});
|
|
2771
|
+
});
|
|
2772
|
+
// Wait for tunnel to establish
|
|
2773
|
+
try {
|
|
2774
|
+
await Promise.race([
|
|
2775
|
+
tunnelPromise,
|
|
2776
|
+
new Promise((_, reject) => setTimeout(() => reject(new Error('SSH connection timeout')), 15000)),
|
|
2777
|
+
]);
|
|
2778
|
+
}
|
|
2779
|
+
catch (err) {
|
|
2780
|
+
console.log(red(`Failed to establish tunnel: ${err instanceof Error ? err.message : String(err)}`));
|
|
2781
|
+
sshClient.end();
|
|
2782
|
+
process.exit(1);
|
|
2783
|
+
}
|
|
2784
|
+
console.log(green('✓ SSH tunnel established!'));
|
|
2785
|
+
console.log('');
|
|
2786
|
+
// Handle Ctrl+C gracefully
|
|
2787
|
+
const cleanup = () => {
|
|
2788
|
+
console.log('');
|
|
2789
|
+
console.log(dim('Shutting down...'));
|
|
2790
|
+
if (tunnel.server) {
|
|
2791
|
+
tunnel.server.close();
|
|
2792
|
+
}
|
|
2793
|
+
sshClient.end();
|
|
2794
|
+
process.exit(0);
|
|
2795
|
+
};
|
|
2796
|
+
process.on('SIGINT', cleanup);
|
|
2797
|
+
process.on('SIGTERM', cleanup);
|
|
2798
|
+
// Display the OAuth URL
|
|
2799
|
+
if (tunnelInfo.authUrl) {
|
|
2800
|
+
console.log('');
|
|
2801
|
+
console.log(green('Ready! Open this URL in your browser to complete authentication:'));
|
|
2802
|
+
console.log('');
|
|
2803
|
+
console.log(cyan(tunnelInfo.authUrl));
|
|
2804
|
+
console.log('');
|
|
2805
|
+
console.log(dim('The browser will redirect to localhost:1455, which tunnels to the workspace.'));
|
|
2806
|
+
console.log(dim('The Codex CLI in the workspace will receive the callback and complete auth.'));
|
|
2807
|
+
console.log('');
|
|
2808
|
+
}
|
|
2809
|
+
else {
|
|
2810
|
+
console.log('');
|
|
2811
|
+
console.log(yellow('OAuth URL not available. Please start authentication from the dashboard.'));
|
|
2812
|
+
console.log('');
|
|
2813
|
+
}
|
|
2814
|
+
// Poll for authentication completion
|
|
2815
|
+
console.log(cyan(`Waiting for authentication... (timeout: ${options.timeout}s)`));
|
|
2816
|
+
const startTime = Date.now();
|
|
2817
|
+
let authenticated = false;
|
|
2818
|
+
while (!authenticated && (Date.now() - startTime) < TIMEOUT_MS) {
|
|
2819
|
+
await new Promise(resolve => setTimeout(resolve, 3000));
|
|
2820
|
+
try {
|
|
2821
|
+
// Build URL with token for authentication
|
|
2822
|
+
const authStatusUrl = new URL(`${CLOUD_URL}/api/auth/codex-helper/auth-status/${workspaceId}`);
|
|
2823
|
+
if (options.token) {
|
|
2824
|
+
authStatusUrl.searchParams.set('token', options.token);
|
|
2825
|
+
}
|
|
2826
|
+
const statusResponse = await fetch(authStatusUrl.toString(), { method: 'GET', headers, credentials: 'include' });
|
|
2827
|
+
if (statusResponse.ok) {
|
|
2828
|
+
const statusData = await statusResponse.json();
|
|
2829
|
+
if (statusData.authenticated) {
|
|
2830
|
+
authenticated = true;
|
|
2831
|
+
}
|
|
2832
|
+
}
|
|
2833
|
+
}
|
|
2834
|
+
catch {
|
|
2835
|
+
// Ignore polling errors
|
|
2836
|
+
}
|
|
2837
|
+
const elapsed = Math.floor((Date.now() - startTime) / 1000);
|
|
2838
|
+
if (!authenticated && elapsed > 0 && elapsed % 30 === 0) {
|
|
2839
|
+
console.log(` Still waiting... (${elapsed}s)`);
|
|
2840
|
+
}
|
|
2841
|
+
}
|
|
2842
|
+
// Cleanup SSH tunnel
|
|
2843
|
+
if (tunnel.server) {
|
|
2844
|
+
tunnel.server.close();
|
|
2845
|
+
}
|
|
2846
|
+
sshClient.end();
|
|
2847
|
+
if (authenticated) {
|
|
2848
|
+
console.log('');
|
|
2849
|
+
console.log(green('═══════════════════════════════════════════════════'));
|
|
2850
|
+
console.log(green(' Authentication Complete!'));
|
|
2851
|
+
console.log(green('═══════════════════════════════════════════════════'));
|
|
2852
|
+
console.log('');
|
|
2853
|
+
console.log('Your Codex account is now connected to the workspace.');
|
|
2854
|
+
console.log('You can close this terminal and return to the dashboard.');
|
|
2855
|
+
console.log('');
|
|
2856
|
+
}
|
|
2857
|
+
else {
|
|
2858
|
+
console.log('');
|
|
2859
|
+
console.log(red('Timeout waiting for authentication.'));
|
|
2860
|
+
console.log('');
|
|
2861
|
+
console.log('If you completed sign-in, the workspace may not have received');
|
|
2862
|
+
console.log('the callback. Check if the SSH tunnel was working correctly.');
|
|
2863
|
+
process.exit(1);
|
|
2864
|
+
}
|
|
2865
|
+
});
|
|
2866
|
+
// init - First-time setup wizard for Agent Relay
|
|
2867
|
+
async function runInit(options) {
|
|
2868
|
+
const readline = await import('node:readline');
|
|
2869
|
+
const { existsSync } = await import('node:fs');
|
|
2870
|
+
const { spawn } = await import('node:child_process');
|
|
2871
|
+
// Helper to prompt user
|
|
2872
|
+
const prompt = async (question, defaultYes = true) => {
|
|
2873
|
+
if (options.yes)
|
|
2874
|
+
return true;
|
|
2875
|
+
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
2876
|
+
const suffix = defaultYes ? '[Y/n]' : '[y/N]';
|
|
2877
|
+
const answer = await new Promise((resolve) => {
|
|
2878
|
+
rl.question(`${question} ${suffix} `, resolve);
|
|
2879
|
+
});
|
|
2880
|
+
rl.close();
|
|
2881
|
+
const normalized = answer.toLowerCase().trim();
|
|
2882
|
+
if (!normalized)
|
|
2883
|
+
return defaultYes;
|
|
2884
|
+
return normalized === 'y' || normalized === 'yes';
|
|
2885
|
+
};
|
|
2886
|
+
// Banner
|
|
2887
|
+
console.log('');
|
|
2888
|
+
console.log(' ╭─────────────────────────────────────╮');
|
|
2889
|
+
console.log(' │ │');
|
|
2890
|
+
console.log(' │ 🚀 Agent Relay - First Time Setup │');
|
|
2891
|
+
console.log(' │ │');
|
|
2892
|
+
console.log(' │ Real-time AI agent communication │');
|
|
2893
|
+
console.log(' │ │');
|
|
2894
|
+
console.log(' ╰─────────────────────────────────────╯');
|
|
2895
|
+
console.log('');
|
|
2896
|
+
// Step 1: Detect environment
|
|
2897
|
+
const isCloud = !!process.env.WORKSPACE_ID;
|
|
2898
|
+
if (isCloud) {
|
|
2899
|
+
console.log(' ℹ Detected: Cloud workspace');
|
|
2900
|
+
console.log(' MCP tools are pre-configured in cloud environments.');
|
|
2901
|
+
console.log('');
|
|
2902
|
+
return;
|
|
2903
|
+
}
|
|
2904
|
+
console.log(' ℹ Detected: Local environment');
|
|
2905
|
+
console.log('');
|
|
2906
|
+
// Step 2: Check daemon status
|
|
2907
|
+
let daemonRunning = false;
|
|
2908
|
+
const socketPath = process.env.RELAY_SOCKET;
|
|
2909
|
+
if (socketPath && existsSync(socketPath)) {
|
|
2910
|
+
daemonRunning = true;
|
|
2911
|
+
}
|
|
2912
|
+
else {
|
|
2913
|
+
// Check default locations
|
|
2914
|
+
const { homedir } = await import('node:os');
|
|
2915
|
+
const { join } = await import('node:path');
|
|
2916
|
+
const platform = process.platform;
|
|
2917
|
+
let dataDir;
|
|
2918
|
+
if (platform === 'darwin') {
|
|
2919
|
+
dataDir = join(homedir(), 'Library', 'Application Support', 'agent-relay');
|
|
2920
|
+
}
|
|
2921
|
+
else if (platform === 'win32') {
|
|
2922
|
+
dataDir = join(process.env.APPDATA || homedir(), 'agent-relay');
|
|
2923
|
+
}
|
|
2924
|
+
else {
|
|
2925
|
+
dataDir = join(process.env.XDG_DATA_HOME || join(homedir(), '.local', 'share'), 'agent-relay');
|
|
2926
|
+
}
|
|
2927
|
+
const defaultSocket = join(dataDir, 'projects', 'default', 'daemon.sock');
|
|
2928
|
+
if (existsSync(defaultSocket)) {
|
|
2929
|
+
daemonRunning = true;
|
|
2930
|
+
}
|
|
2931
|
+
}
|
|
2932
|
+
if (daemonRunning) {
|
|
2933
|
+
console.log(' ✓ Daemon is already running');
|
|
2934
|
+
}
|
|
2935
|
+
else {
|
|
2936
|
+
console.log(' ○ Daemon is not running');
|
|
2937
|
+
}
|
|
2938
|
+
console.log('');
|
|
2939
|
+
// Step 3: Install MCP for editors
|
|
2940
|
+
let mcpInstalled = false;
|
|
2941
|
+
if (!options.skipMcp) {
|
|
2942
|
+
console.log(' ┌─ Step 1: MCP Server for AI Editors ─────────────────────┐');
|
|
2943
|
+
console.log(' │ │');
|
|
2944
|
+
console.log(' │ MCP (Model Context Protocol) gives AI editors native │');
|
|
2945
|
+
console.log(' │ tools for agent communication: │');
|
|
2946
|
+
console.log(' │ │');
|
|
2947
|
+
console.log(' │ • relay_send - Send messages to agents/channels │');
|
|
2948
|
+
console.log(' │ • relay_spawn - Create worker agents │');
|
|
2949
|
+
console.log(' │ • relay_inbox - Check for messages │');
|
|
2950
|
+
console.log(' │ • relay_who - List online agents │');
|
|
2951
|
+
console.log(' │ │');
|
|
2952
|
+
console.log(' │ Supported editors: Claude Code, Cursor, VS Code │');
|
|
2953
|
+
console.log(' │ │');
|
|
2954
|
+
console.log(' └──────────────────────────────────────────────────────────┘');
|
|
2955
|
+
console.log('');
|
|
2956
|
+
const shouldInstallMcp = await prompt(' Install MCP server for your AI editors?');
|
|
2957
|
+
if (shouldInstallMcp) {
|
|
2958
|
+
console.log('');
|
|
2959
|
+
console.log(' Installing MCP server...');
|
|
2960
|
+
try {
|
|
2961
|
+
const { runInstall } = await import('@agent-relay/mcp/install');
|
|
2962
|
+
runInstall({ editor: undefined });
|
|
2963
|
+
mcpInstalled = true;
|
|
2964
|
+
}
|
|
2965
|
+
catch (err) {
|
|
2966
|
+
if (err.code === 'ERR_MODULE_NOT_FOUND') {
|
|
2967
|
+
console.log(' ⚠ MCP package not bundled. Install separately:');
|
|
2968
|
+
console.log(' npm install -g @agent-relay/mcp');
|
|
2969
|
+
}
|
|
2970
|
+
else {
|
|
2971
|
+
console.error(' ✗ Error:', err.message);
|
|
2972
|
+
}
|
|
2973
|
+
}
|
|
2974
|
+
console.log('');
|
|
2975
|
+
}
|
|
2976
|
+
}
|
|
2977
|
+
// Step 4: Start daemon
|
|
2978
|
+
if (!daemonRunning && !options.skipDaemon) {
|
|
2979
|
+
console.log(' ┌─ Step 2: Start the Relay Daemon ─────────────────────────┐');
|
|
2980
|
+
console.log(' │ │');
|
|
2981
|
+
console.log(' │ The daemon manages agent connections and message │');
|
|
2982
|
+
console.log(' │ routing. It runs in the background. │');
|
|
2983
|
+
console.log(' │ │');
|
|
2984
|
+
console.log(' └──────────────────────────────────────────────────────────┘');
|
|
2985
|
+
console.log('');
|
|
2986
|
+
const shouldStartDaemon = await prompt(' Start the relay daemon now?');
|
|
2987
|
+
if (shouldStartDaemon) {
|
|
2988
|
+
console.log('');
|
|
2989
|
+
console.log(' Starting daemon...');
|
|
2990
|
+
// Start daemon in background
|
|
2991
|
+
const daemonProcess = spawn(process.execPath, [process.argv[1], 'up', '--background'], {
|
|
2992
|
+
detached: true,
|
|
2993
|
+
stdio: 'ignore',
|
|
2994
|
+
});
|
|
2995
|
+
daemonProcess.unref();
|
|
2996
|
+
// Wait a moment for it to start
|
|
2997
|
+
await new Promise((resolve) => setTimeout(resolve, 2000));
|
|
2998
|
+
console.log(' ✓ Daemon started in background');
|
|
2999
|
+
console.log('');
|
|
3000
|
+
daemonRunning = true;
|
|
3001
|
+
}
|
|
3002
|
+
}
|
|
3003
|
+
// Summary
|
|
3004
|
+
console.log(' ╭─────────────────────────────────────────────────────────╮');
|
|
3005
|
+
console.log(' │ Setup Complete! │');
|
|
3006
|
+
console.log(' ╰─────────────────────────────────────────────────────────╯');
|
|
3007
|
+
console.log('');
|
|
3008
|
+
if (mcpInstalled || daemonRunning) {
|
|
3009
|
+
console.log(' Status:');
|
|
3010
|
+
if (mcpInstalled)
|
|
3011
|
+
console.log(' ✓ MCP server configured for editors');
|
|
3012
|
+
if (daemonRunning)
|
|
3013
|
+
console.log(' ✓ Daemon running');
|
|
3014
|
+
console.log('');
|
|
3015
|
+
}
|
|
3016
|
+
console.log(' Quick Start:');
|
|
3017
|
+
console.log('');
|
|
3018
|
+
console.log(' 1. Open Claude Code (or Cursor)');
|
|
3019
|
+
console.log('');
|
|
3020
|
+
console.log(' 2. The relay tools are ready! Try asking Claude:');
|
|
3021
|
+
console.log(' "Use relay_who to see online agents"');
|
|
3022
|
+
console.log('');
|
|
3023
|
+
console.log(' 3. Spawn a worker agent:');
|
|
3024
|
+
console.log(' "Spawn a worker named TestRunner to run the tests"');
|
|
3025
|
+
console.log('');
|
|
3026
|
+
console.log(' Commands:');
|
|
3027
|
+
console.log(' agent-relay up Start daemon with dashboard');
|
|
3028
|
+
console.log(' agent-relay status Check daemon status');
|
|
3029
|
+
console.log(' agent-relay who List online agents');
|
|
3030
|
+
console.log('');
|
|
3031
|
+
console.log(' Dashboard: http://localhost:3888 (when daemon is running)');
|
|
3032
|
+
console.log('');
|
|
3033
|
+
}
|
|
3034
|
+
program
|
|
3035
|
+
.command('init')
|
|
3036
|
+
.description('First-time setup wizard - install MCP and start daemon')
|
|
3037
|
+
.option('-y, --yes', 'Accept all defaults (non-interactive)')
|
|
3038
|
+
.option('--skip-daemon', 'Skip daemon startup prompt')
|
|
3039
|
+
.option('--skip-mcp', 'Skip MCP installation prompt')
|
|
3040
|
+
.action(runInit);
|
|
3041
|
+
// setup - Alias for init (backwards compatibility)
|
|
3042
|
+
program
|
|
3043
|
+
.command('setup')
|
|
3044
|
+
.description('Alias for "init" - first-time setup wizard')
|
|
3045
|
+
.option('-y, --yes', 'Accept all defaults')
|
|
3046
|
+
.option('--skip-daemon', 'Skip daemon startup')
|
|
3047
|
+
.option('--skip-mcp', 'Skip MCP installation')
|
|
3048
|
+
.action(runInit);
|
|
3049
|
+
// mcp - MCP server management
|
|
3050
|
+
program
|
|
3051
|
+
.command('mcp')
|
|
3052
|
+
.description('Manage MCP server for AI editors')
|
|
3053
|
+
.argument('<command>', 'Command to run (install, serve)')
|
|
3054
|
+
.option('-e, --editor <name>', 'Editor to configure')
|
|
3055
|
+
.option('-g, --global', 'Install globally')
|
|
3056
|
+
.action(async (cmd, options) => {
|
|
3057
|
+
try {
|
|
3058
|
+
if (cmd === 'install') {
|
|
3059
|
+
const { runInstall } = await import('@agent-relay/mcp/install');
|
|
3060
|
+
runInstall(options);
|
|
3061
|
+
}
|
|
3062
|
+
else if (cmd === 'serve') {
|
|
3063
|
+
const { createRelayClient, runMCPServer, discoverSocket } = await import('@agent-relay/mcp');
|
|
3064
|
+
const discovery = discoverSocket();
|
|
3065
|
+
const client = createRelayClient({
|
|
3066
|
+
agentName: process.env.RELAY_AGENT_NAME || `mcp-${process.pid}`,
|
|
3067
|
+
socketPath: discovery?.socketPath,
|
|
3068
|
+
project: discovery?.project,
|
|
3069
|
+
});
|
|
3070
|
+
await runMCPServer(client);
|
|
3071
|
+
}
|
|
3072
|
+
else {
|
|
3073
|
+
console.error(`Unknown mcp command: ${cmd}`);
|
|
3074
|
+
process.exit(1);
|
|
3075
|
+
}
|
|
3076
|
+
}
|
|
3077
|
+
catch (err) {
|
|
3078
|
+
if (err.code === 'ERR_MODULE_NOT_FOUND') {
|
|
3079
|
+
console.error('Error: @agent-relay/mcp package not found.');
|
|
3080
|
+
console.error('This command requires the MCP package to be installed.');
|
|
3081
|
+
}
|
|
3082
|
+
else {
|
|
3083
|
+
console.error('Error running MCP command:', err);
|
|
3084
|
+
}
|
|
3085
|
+
process.exit(1);
|
|
3086
|
+
}
|
|
3087
|
+
});
|
|
3088
|
+
program.parse();
|
|
3089
|
+
//# sourceMappingURL=index.js.map
|