agent-relay 1.0.22 → 1.2.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/README.md +1 -1
- package/dist/bridge/shadow-cli.d.ts +17 -0
- package/dist/bridge/shadow-cli.d.ts.map +1 -0
- package/dist/bridge/shadow-cli.js +75 -0
- package/dist/bridge/shadow-cli.js.map +1 -0
- package/dist/bridge/shadow-config.d.ts +87 -0
- package/dist/bridge/shadow-config.d.ts.map +1 -0
- package/dist/bridge/shadow-config.js +134 -0
- package/dist/bridge/shadow-config.js.map +1 -0
- package/dist/bridge/spawner.d.ts +68 -1
- package/dist/bridge/spawner.d.ts.map +1 -1
- package/dist/bridge/spawner.js +360 -16
- package/dist/bridge/spawner.js.map +1 -1
- package/dist/bridge/types.d.ts +67 -0
- package/dist/bridge/types.d.ts.map +1 -1
- package/dist/cli/index.js +1196 -15
- package/dist/cli/index.js.map +1 -1
- package/dist/cloud/api/auth.d.ts +20 -0
- package/dist/cloud/api/auth.d.ts.map +1 -0
- package/dist/cloud/api/auth.js +128 -0
- package/dist/cloud/api/auth.js.map +1 -0
- package/dist/cloud/api/billing.d.ts +17 -0
- package/dist/cloud/api/billing.d.ts.map +1 -0
- package/dist/cloud/api/billing.js +353 -0
- package/dist/cloud/api/billing.js.map +1 -0
- package/dist/cloud/api/cli-pty-runner.d.ts +54 -0
- package/dist/cloud/api/cli-pty-runner.d.ts.map +1 -0
- package/dist/cloud/api/cli-pty-runner.js +119 -0
- package/dist/cloud/api/cli-pty-runner.js.map +1 -0
- package/dist/cloud/api/coordinators.d.ts +8 -0
- package/dist/cloud/api/coordinators.d.ts.map +1 -0
- package/dist/cloud/api/coordinators.js +347 -0
- package/dist/cloud/api/coordinators.js.map +1 -0
- package/dist/cloud/api/daemons.d.ts +12 -0
- package/dist/cloud/api/daemons.d.ts.map +1 -0
- package/dist/cloud/api/daemons.js +320 -0
- package/dist/cloud/api/daemons.js.map +1 -0
- package/dist/cloud/api/generic-webhooks.d.ts +8 -0
- package/dist/cloud/api/generic-webhooks.d.ts.map +1 -0
- package/dist/cloud/api/generic-webhooks.js +129 -0
- package/dist/cloud/api/generic-webhooks.js.map +1 -0
- package/dist/cloud/api/git.d.ts +8 -0
- package/dist/cloud/api/git.d.ts.map +1 -0
- package/dist/cloud/api/git.js +131 -0
- package/dist/cloud/api/git.js.map +1 -0
- package/dist/cloud/api/github-app.d.ts +11 -0
- package/dist/cloud/api/github-app.d.ts.map +1 -0
- package/dist/cloud/api/github-app.js +189 -0
- package/dist/cloud/api/github-app.js.map +1 -0
- package/dist/cloud/api/middleware/planLimits.d.ts +43 -0
- package/dist/cloud/api/middleware/planLimits.d.ts.map +1 -0
- package/dist/cloud/api/middleware/planLimits.js +202 -0
- package/dist/cloud/api/middleware/planLimits.js.map +1 -0
- package/dist/cloud/api/monitoring.d.ts +11 -0
- package/dist/cloud/api/monitoring.d.ts.map +1 -0
- package/dist/cloud/api/monitoring.js +578 -0
- package/dist/cloud/api/monitoring.js.map +1 -0
- package/dist/cloud/api/nango-auth.d.ts +9 -0
- package/dist/cloud/api/nango-auth.d.ts.map +1 -0
- package/dist/cloud/api/nango-auth.js +377 -0
- package/dist/cloud/api/nango-auth.js.map +1 -0
- package/dist/cloud/api/onboarding.d.ts +15 -0
- package/dist/cloud/api/onboarding.d.ts.map +1 -0
- package/dist/cloud/api/onboarding.js +588 -0
- package/dist/cloud/api/onboarding.js.map +1 -0
- package/dist/cloud/api/policy.d.ts +8 -0
- package/dist/cloud/api/policy.d.ts.map +1 -0
- package/dist/cloud/api/policy.js +229 -0
- package/dist/cloud/api/policy.js.map +1 -0
- package/dist/cloud/api/providers.d.ts +7 -0
- package/dist/cloud/api/providers.d.ts.map +1 -0
- package/dist/cloud/api/providers.js +507 -0
- package/dist/cloud/api/providers.js.map +1 -0
- package/dist/cloud/api/repos.d.ts +7 -0
- package/dist/cloud/api/repos.d.ts.map +1 -0
- package/dist/cloud/api/repos.js +314 -0
- package/dist/cloud/api/repos.js.map +1 -0
- package/dist/cloud/api/teams.d.ts +7 -0
- package/dist/cloud/api/teams.d.ts.map +1 -0
- package/dist/cloud/api/teams.js +279 -0
- package/dist/cloud/api/teams.js.map +1 -0
- package/dist/cloud/api/test-helpers.d.ts +10 -0
- package/dist/cloud/api/test-helpers.d.ts.map +1 -0
- package/dist/cloud/api/test-helpers.js +575 -0
- package/dist/cloud/api/test-helpers.js.map +1 -0
- package/dist/cloud/api/usage.d.ts +7 -0
- package/dist/cloud/api/usage.d.ts.map +1 -0
- package/dist/cloud/api/usage.js +98 -0
- package/dist/cloud/api/usage.js.map +1 -0
- package/dist/cloud/api/webhooks.d.ts +7 -0
- package/dist/cloud/api/webhooks.d.ts.map +1 -0
- package/dist/cloud/api/webhooks.js +496 -0
- package/dist/cloud/api/webhooks.js.map +1 -0
- package/dist/cloud/api/workspaces.d.ts +7 -0
- package/dist/cloud/api/workspaces.d.ts.map +1 -0
- package/dist/cloud/api/workspaces.js +727 -0
- package/dist/cloud/api/workspaces.js.map +1 -0
- package/dist/cloud/billing/index.d.ts +9 -0
- package/dist/cloud/billing/index.d.ts.map +1 -0
- package/dist/cloud/billing/index.js +9 -0
- package/dist/cloud/billing/index.js.map +1 -0
- package/dist/cloud/billing/plans.d.ts +39 -0
- package/dist/cloud/billing/plans.d.ts.map +1 -0
- package/dist/cloud/billing/plans.js +245 -0
- package/dist/cloud/billing/plans.js.map +1 -0
- package/dist/cloud/billing/service.d.ts +80 -0
- package/dist/cloud/billing/service.d.ts.map +1 -0
- package/dist/cloud/billing/service.js +388 -0
- package/dist/cloud/billing/service.js.map +1 -0
- package/dist/cloud/billing/types.d.ts +141 -0
- package/dist/cloud/billing/types.d.ts.map +1 -0
- package/dist/cloud/billing/types.js +7 -0
- package/dist/cloud/billing/types.js.map +1 -0
- package/dist/cloud/config.d.ts +66 -0
- package/dist/cloud/config.d.ts.map +1 -0
- package/dist/cloud/config.js +92 -0
- package/dist/cloud/config.js.map +1 -0
- package/dist/cloud/db/drizzle.d.ts +215 -0
- package/dist/cloud/db/drizzle.d.ts.map +1 -0
- package/dist/cloud/db/drizzle.js +1083 -0
- package/dist/cloud/db/drizzle.js.map +1 -0
- package/dist/cloud/db/index.d.ts +35 -0
- package/dist/cloud/db/index.d.ts.map +1 -0
- package/dist/cloud/db/index.js +52 -0
- package/dist/cloud/db/index.js.map +1 -0
- package/dist/cloud/db/schema.d.ts +4519 -0
- package/dist/cloud/db/schema.d.ts.map +1 -0
- package/dist/cloud/db/schema.js +547 -0
- package/dist/cloud/db/schema.js.map +1 -0
- package/dist/cloud/index.d.ts +12 -0
- package/dist/cloud/index.d.ts.map +1 -0
- package/dist/cloud/index.js +39 -0
- package/dist/cloud/index.js.map +1 -0
- package/dist/cloud/provisioner/index.d.ts +75 -0
- package/dist/cloud/provisioner/index.d.ts.map +1 -0
- package/dist/cloud/provisioner/index.js +977 -0
- package/dist/cloud/provisioner/index.js.map +1 -0
- package/dist/cloud/server.d.ts +17 -0
- package/dist/cloud/server.d.ts.map +1 -0
- package/dist/cloud/server.js +534 -0
- package/dist/cloud/server.js.map +1 -0
- package/dist/cloud/services/auto-scaler.d.ts +152 -0
- package/dist/cloud/services/auto-scaler.d.ts.map +1 -0
- package/dist/cloud/services/auto-scaler.js +439 -0
- package/dist/cloud/services/auto-scaler.js.map +1 -0
- package/dist/cloud/services/capacity-manager.d.ts +148 -0
- package/dist/cloud/services/capacity-manager.d.ts.map +1 -0
- package/dist/cloud/services/capacity-manager.js +449 -0
- package/dist/cloud/services/capacity-manager.js.map +1 -0
- package/dist/cloud/services/ci-agent-spawner.d.ts +49 -0
- package/dist/cloud/services/ci-agent-spawner.d.ts.map +1 -0
- package/dist/cloud/services/ci-agent-spawner.js +373 -0
- package/dist/cloud/services/ci-agent-spawner.js.map +1 -0
- package/dist/cloud/services/coordinator.d.ts +62 -0
- package/dist/cloud/services/coordinator.d.ts.map +1 -0
- package/dist/cloud/services/coordinator.js +389 -0
- package/dist/cloud/services/coordinator.js.map +1 -0
- package/dist/cloud/services/index.d.ts +12 -0
- package/dist/cloud/services/index.d.ts.map +1 -0
- package/dist/cloud/services/index.js +15 -0
- package/dist/cloud/services/index.js.map +1 -0
- package/dist/cloud/services/mention-handler.d.ts +65 -0
- package/dist/cloud/services/mention-handler.d.ts.map +1 -0
- package/dist/cloud/services/mention-handler.js +405 -0
- package/dist/cloud/services/mention-handler.js.map +1 -0
- package/dist/cloud/services/nango.d.ts +126 -0
- package/dist/cloud/services/nango.d.ts.map +1 -0
- package/dist/cloud/services/nango.js +191 -0
- package/dist/cloud/services/nango.js.map +1 -0
- package/dist/cloud/services/persistence.d.ts +131 -0
- package/dist/cloud/services/persistence.d.ts.map +1 -0
- package/dist/cloud/services/persistence.js +200 -0
- package/dist/cloud/services/persistence.js.map +1 -0
- package/dist/cloud/services/planLimits.d.ts +125 -0
- package/dist/cloud/services/planLimits.d.ts.map +1 -0
- package/dist/cloud/services/planLimits.js +282 -0
- package/dist/cloud/services/planLimits.js.map +1 -0
- package/dist/cloud/services/scaling-orchestrator.d.ts +159 -0
- package/dist/cloud/services/scaling-orchestrator.d.ts.map +1 -0
- package/dist/cloud/services/scaling-orchestrator.js +502 -0
- package/dist/cloud/services/scaling-orchestrator.js.map +1 -0
- package/dist/cloud/services/scaling-policy.d.ts +121 -0
- package/dist/cloud/services/scaling-policy.d.ts.map +1 -0
- package/dist/cloud/services/scaling-policy.js +415 -0
- package/dist/cloud/services/scaling-policy.js.map +1 -0
- package/dist/cloud/vault/index.d.ts +76 -0
- package/dist/cloud/vault/index.d.ts.map +1 -0
- package/dist/cloud/vault/index.js +219 -0
- package/dist/cloud/vault/index.js.map +1 -0
- package/dist/cloud/webhooks/index.d.ts +24 -0
- package/dist/cloud/webhooks/index.d.ts.map +1 -0
- package/dist/cloud/webhooks/index.js +29 -0
- package/dist/cloud/webhooks/index.js.map +1 -0
- package/dist/cloud/webhooks/parsers/github.d.ts +8 -0
- package/dist/cloud/webhooks/parsers/github.d.ts.map +1 -0
- package/dist/cloud/webhooks/parsers/github.js +234 -0
- package/dist/cloud/webhooks/parsers/github.js.map +1 -0
- package/dist/cloud/webhooks/parsers/index.d.ts +23 -0
- package/dist/cloud/webhooks/parsers/index.d.ts.map +1 -0
- package/dist/cloud/webhooks/parsers/index.js +30 -0
- package/dist/cloud/webhooks/parsers/index.js.map +1 -0
- package/dist/cloud/webhooks/parsers/linear.d.ts +9 -0
- package/dist/cloud/webhooks/parsers/linear.d.ts.map +1 -0
- package/dist/cloud/webhooks/parsers/linear.js +258 -0
- package/dist/cloud/webhooks/parsers/linear.js.map +1 -0
- package/dist/cloud/webhooks/parsers/slack.d.ts +9 -0
- package/dist/cloud/webhooks/parsers/slack.d.ts.map +1 -0
- package/dist/cloud/webhooks/parsers/slack.js +214 -0
- package/dist/cloud/webhooks/parsers/slack.js.map +1 -0
- package/dist/cloud/webhooks/responders/github.d.ts +8 -0
- package/dist/cloud/webhooks/responders/github.d.ts.map +1 -0
- package/dist/cloud/webhooks/responders/github.js +73 -0
- package/dist/cloud/webhooks/responders/github.js.map +1 -0
- package/dist/cloud/webhooks/responders/index.d.ts +23 -0
- package/dist/cloud/webhooks/responders/index.d.ts.map +1 -0
- package/dist/cloud/webhooks/responders/index.js +30 -0
- package/dist/cloud/webhooks/responders/index.js.map +1 -0
- package/dist/cloud/webhooks/responders/linear.d.ts +9 -0
- package/dist/cloud/webhooks/responders/linear.d.ts.map +1 -0
- package/dist/cloud/webhooks/responders/linear.js +149 -0
- package/dist/cloud/webhooks/responders/linear.js.map +1 -0
- package/dist/cloud/webhooks/responders/slack.d.ts +20 -0
- package/dist/cloud/webhooks/responders/slack.d.ts.map +1 -0
- package/dist/cloud/webhooks/responders/slack.js +178 -0
- package/dist/cloud/webhooks/responders/slack.js.map +1 -0
- package/dist/cloud/webhooks/router.d.ts +25 -0
- package/dist/cloud/webhooks/router.d.ts.map +1 -0
- package/dist/cloud/webhooks/router.js +504 -0
- package/dist/cloud/webhooks/router.js.map +1 -0
- package/dist/cloud/webhooks/rules-engine.d.ts +24 -0
- package/dist/cloud/webhooks/rules-engine.d.ts.map +1 -0
- package/dist/cloud/webhooks/rules-engine.js +287 -0
- package/dist/cloud/webhooks/rules-engine.js.map +1 -0
- package/dist/cloud/webhooks/types.d.ts +186 -0
- package/dist/cloud/webhooks/types.d.ts.map +1 -0
- package/dist/cloud/webhooks/types.js +8 -0
- package/dist/cloud/webhooks/types.js.map +1 -0
- package/dist/continuity/formatter.d.ts +51 -0
- package/dist/continuity/formatter.d.ts.map +1 -0
- package/dist/continuity/formatter.js +313 -0
- package/dist/continuity/formatter.js.map +1 -0
- package/dist/continuity/handoff-store.d.ts +67 -0
- package/dist/continuity/handoff-store.d.ts.map +1 -0
- package/dist/continuity/handoff-store.js +472 -0
- package/dist/continuity/handoff-store.js.map +1 -0
- package/dist/continuity/index.d.ts +45 -0
- package/dist/continuity/index.d.ts.map +1 -0
- package/dist/continuity/index.js +48 -0
- package/dist/continuity/index.js.map +1 -0
- package/dist/continuity/ledger-store.d.ts +110 -0
- package/dist/continuity/ledger-store.d.ts.map +1 -0
- package/dist/continuity/ledger-store.js +500 -0
- package/dist/continuity/ledger-store.js.map +1 -0
- package/dist/continuity/manager.d.ts +178 -0
- package/dist/continuity/manager.d.ts.map +1 -0
- package/dist/continuity/manager.js +562 -0
- package/dist/continuity/manager.js.map +1 -0
- package/dist/continuity/parser.d.ts +76 -0
- package/dist/continuity/parser.d.ts.map +1 -0
- package/dist/continuity/parser.js +579 -0
- package/dist/continuity/parser.js.map +1 -0
- package/dist/continuity/types.d.ts +180 -0
- package/dist/continuity/types.d.ts.map +1 -0
- package/dist/continuity/types.js +9 -0
- package/dist/continuity/types.js.map +1 -0
- package/dist/daemon/agent-manager.d.ts +114 -0
- package/dist/daemon/agent-manager.d.ts.map +1 -0
- package/dist/daemon/agent-manager.js +513 -0
- package/dist/daemon/agent-manager.js.map +1 -0
- package/dist/daemon/agent-registry.d.ts +34 -0
- package/dist/daemon/agent-registry.d.ts.map +1 -1
- package/dist/daemon/agent-registry.js +45 -2
- package/dist/daemon/agent-registry.js.map +1 -1
- package/dist/daemon/api.d.ts +81 -0
- package/dist/daemon/api.d.ts.map +1 -0
- package/dist/daemon/api.js +554 -0
- package/dist/daemon/api.js.map +1 -0
- package/dist/daemon/cli-auth.d.ts +67 -0
- package/dist/daemon/cli-auth.d.ts.map +1 -0
- package/dist/daemon/cli-auth.js +537 -0
- package/dist/daemon/cli-auth.js.map +1 -0
- package/dist/daemon/cloud-sync.d.ts +101 -0
- package/dist/daemon/cloud-sync.d.ts.map +1 -0
- package/dist/daemon/cloud-sync.js +263 -0
- package/dist/daemon/cloud-sync.js.map +1 -0
- package/dist/daemon/index.d.ts +4 -0
- package/dist/daemon/index.d.ts.map +1 -1
- package/dist/daemon/index.js +6 -0
- package/dist/daemon/index.js.map +1 -1
- package/dist/daemon/orchestrator.d.ts +155 -0
- package/dist/daemon/orchestrator.d.ts.map +1 -0
- package/dist/daemon/orchestrator.js +766 -0
- package/dist/daemon/orchestrator.js.map +1 -0
- package/dist/daemon/router.d.ts +29 -0
- package/dist/daemon/router.d.ts.map +1 -1
- package/dist/daemon/router.js +143 -21
- package/dist/daemon/router.js.map +1 -1
- package/dist/daemon/server.d.ts +42 -0
- package/dist/daemon/server.d.ts.map +1 -1
- package/dist/daemon/server.js +199 -16
- package/dist/daemon/server.js.map +1 -1
- package/dist/daemon/services/browser-testing.d.ts +88 -0
- package/dist/daemon/services/browser-testing.d.ts.map +1 -0
- package/dist/daemon/services/browser-testing.js +244 -0
- package/dist/daemon/services/browser-testing.js.map +1 -0
- package/dist/daemon/services/container-spawner.d.ts +135 -0
- package/dist/daemon/services/container-spawner.d.ts.map +1 -0
- package/dist/daemon/services/container-spawner.js +313 -0
- package/dist/daemon/services/container-spawner.js.map +1 -0
- package/dist/daemon/types.d.ts +131 -0
- package/dist/daemon/types.d.ts.map +1 -0
- package/dist/daemon/types.js +6 -0
- package/dist/daemon/types.js.map +1 -0
- package/dist/daemon/workspace-manager.d.ts +75 -0
- package/dist/daemon/workspace-manager.d.ts.map +1 -0
- package/dist/daemon/workspace-manager.js +289 -0
- package/dist/daemon/workspace-manager.js.map +1 -0
- package/dist/dashboard/out/404.html +1 -1
- package/dist/dashboard/out/_next/static/chunks/116-2502180def231162.js +1 -0
- package/dist/dashboard/out/_next/static/chunks/282-980c2eb8fff20123.js +1 -0
- package/dist/dashboard/out/_next/static/chunks/480-2d4111711d4e473c.js +1 -0
- package/dist/dashboard/out/_next/static/chunks/724-73c1ee5f60abe860.js +9 -0
- package/dist/dashboard/out/_next/static/chunks/766-c3a14283c88d815b.js +1 -0
- package/dist/dashboard/out/_next/static/chunks/app/app/page-7120be68bea622f3.js +1 -0
- package/dist/dashboard/out/_next/static/chunks/app/connect-repos/page-dc2e3a1a22478efc.js +1 -0
- package/dist/dashboard/out/_next/static/chunks/app/history/page-56a8b4616a90dc43.js +1 -0
- package/dist/dashboard/out/_next/static/chunks/app/layout-2433bb48965f4333.js +1 -0
- package/dist/dashboard/out/_next/static/chunks/app/login/page-3eac37ea6f5dd153.js +1 -0
- package/dist/dashboard/out/_next/static/chunks/app/metrics/page-1081dd190a331a91.js +1 -0
- package/dist/dashboard/out/_next/static/chunks/app/page-daf87e86f783f980.js +1 -0
- package/dist/dashboard/out/_next/static/chunks/app/pricing/page-4d72d5a5d8a9b618.js +1 -0
- package/dist/dashboard/out/_next/static/chunks/app/providers/page-b68a681526eb145e.js +1 -0
- package/dist/dashboard/out/_next/static/chunks/app/signup/page-fee4ed1709070bcd.js +1 -0
- package/dist/dashboard/out/_next/static/chunks/e868780c-48e5f147c90a3a41.js +18 -0
- package/dist/dashboard/out/_next/static/chunks/{main-e0a1f53fe0617a63.js → main-97850e03d723ea8c.js} +1 -1
- package/dist/dashboard/out/_next/static/chunks/main-app-5d692157a8eb1fd9.js +1 -0
- package/dist/dashboard/out/_next/static/chunks/webpack-1cdd8ed57114d5e1.js +1 -0
- package/dist/dashboard/out/_next/static/css/29852f26181969a0.css +1 -0
- package/dist/dashboard/out/_next/static/css/411ce23ffeae9f76.css +1 -0
- package/dist/dashboard/out/alt-logos/agent-relay-logo-128.png +0 -0
- package/dist/dashboard/out/alt-logos/agent-relay-logo-256.png +0 -0
- package/dist/dashboard/out/alt-logos/agent-relay-logo-32.png +0 -0
- package/dist/dashboard/out/alt-logos/agent-relay-logo-512.png +0 -0
- package/dist/dashboard/out/alt-logos/agent-relay-logo-64.png +0 -0
- package/dist/dashboard/out/alt-logos/agent-relay-logo.svg +45 -0
- package/dist/dashboard/out/alt-logos/logo.svg +38 -0
- package/dist/dashboard/out/alt-logos/monogram-logo-128.png +0 -0
- package/dist/dashboard/out/alt-logos/monogram-logo-256.png +0 -0
- package/dist/dashboard/out/alt-logos/monogram-logo-32.png +0 -0
- package/dist/dashboard/out/alt-logos/monogram-logo-512.png +0 -0
- package/dist/dashboard/out/alt-logos/monogram-logo-64.png +0 -0
- package/dist/dashboard/out/alt-logos/monogram-logo.svg +38 -0
- package/dist/dashboard/out/app.html +1 -0
- package/dist/dashboard/out/app.txt +7 -0
- package/dist/dashboard/out/connect-repos.html +1 -0
- package/dist/dashboard/out/connect-repos.txt +7 -0
- package/dist/dashboard/out/history.html +1 -0
- package/dist/dashboard/out/history.txt +7 -0
- package/dist/dashboard/out/index.html +1 -1
- package/dist/dashboard/out/index.txt +2 -2
- package/dist/dashboard/out/login.html +6 -0
- package/dist/dashboard/out/login.txt +7 -0
- package/dist/dashboard/out/metrics.html +1 -515
- package/dist/dashboard/out/metrics.txt +2 -2
- package/dist/dashboard/out/pricing.html +13 -0
- package/dist/dashboard/out/pricing.txt +7 -0
- package/dist/dashboard/out/providers.html +1 -0
- package/dist/dashboard/out/providers.txt +7 -0
- package/dist/dashboard/out/signup.html +6 -0
- package/dist/dashboard/out/signup.txt +7 -0
- package/dist/dashboard-server/metrics.d.ts.map +1 -1
- package/dist/dashboard-server/metrics.js +3 -2
- package/dist/dashboard-server/metrics.js.map +1 -1
- package/dist/dashboard-server/server.d.ts.map +1 -1
- package/dist/dashboard-server/server.js +2653 -130
- package/dist/dashboard-server/server.js.map +1 -1
- package/dist/hooks/emitter.d.ts +40 -0
- package/dist/hooks/emitter.d.ts.map +1 -0
- package/dist/hooks/emitter.js +63 -0
- package/dist/hooks/emitter.js.map +1 -0
- package/dist/hooks/index.d.ts +3 -0
- package/dist/hooks/index.d.ts.map +1 -1
- package/dist/hooks/index.js +3 -0
- package/dist/hooks/index.js.map +1 -1
- package/dist/hooks/registry.d.ts +173 -0
- package/dist/hooks/registry.d.ts.map +1 -0
- package/dist/hooks/registry.js +476 -0
- package/dist/hooks/registry.js.map +1 -0
- package/dist/hooks/trajectory-hooks.d.ts +52 -0
- package/dist/hooks/trajectory-hooks.d.ts.map +1 -0
- package/dist/hooks/trajectory-hooks.js +183 -0
- package/dist/hooks/trajectory-hooks.js.map +1 -0
- package/dist/hooks/types.d.ts +141 -0
- package/dist/hooks/types.d.ts.map +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -0
- package/dist/index.js.map +1 -1
- package/dist/memory/adapters/index.d.ts +8 -0
- package/dist/memory/adapters/index.d.ts.map +1 -0
- package/dist/memory/adapters/index.js +8 -0
- package/dist/memory/adapters/index.js.map +1 -0
- package/dist/memory/adapters/inmemory.d.ts +59 -0
- package/dist/memory/adapters/inmemory.d.ts.map +1 -0
- package/dist/memory/adapters/inmemory.js +195 -0
- package/dist/memory/adapters/inmemory.js.map +1 -0
- package/dist/memory/adapters/supermemory.d.ts +71 -0
- package/dist/memory/adapters/supermemory.d.ts.map +1 -0
- package/dist/memory/adapters/supermemory.js +338 -0
- package/dist/memory/adapters/supermemory.js.map +1 -0
- package/dist/memory/factory.d.ts +48 -0
- package/dist/memory/factory.d.ts.map +1 -0
- package/dist/memory/factory.js +143 -0
- package/dist/memory/factory.js.map +1 -0
- package/dist/memory/index.d.ts +32 -0
- package/dist/memory/index.d.ts.map +1 -0
- package/dist/memory/index.js +32 -0
- package/dist/memory/index.js.map +1 -0
- package/dist/memory/memory-hooks.d.ts +60 -0
- package/dist/memory/memory-hooks.d.ts.map +1 -0
- package/dist/memory/memory-hooks.js +313 -0
- package/dist/memory/memory-hooks.js.map +1 -0
- package/dist/memory/service.d.ts +49 -0
- package/dist/memory/service.d.ts.map +1 -0
- package/dist/memory/service.js +146 -0
- package/dist/memory/service.js.map +1 -0
- package/dist/memory/types.d.ts +195 -0
- package/dist/memory/types.d.ts.map +1 -0
- package/dist/memory/types.js +8 -0
- package/dist/memory/types.js.map +1 -0
- package/dist/policy/agent-policy.d.ts +225 -0
- package/dist/policy/agent-policy.d.ts.map +1 -0
- package/dist/policy/agent-policy.js +665 -0
- package/dist/policy/agent-policy.js.map +1 -0
- package/dist/policy/cloud-policy-fetcher.d.ts +12 -0
- package/dist/policy/cloud-policy-fetcher.d.ts.map +1 -0
- package/dist/policy/cloud-policy-fetcher.js +64 -0
- package/dist/policy/cloud-policy-fetcher.js.map +1 -0
- package/dist/protocol/types.d.ts +10 -1
- package/dist/protocol/types.d.ts.map +1 -1
- package/dist/resiliency/context-persistence.d.ts +140 -0
- package/dist/resiliency/context-persistence.d.ts.map +1 -0
- package/dist/resiliency/context-persistence.js +397 -0
- package/dist/resiliency/context-persistence.js.map +1 -0
- package/dist/resiliency/crash-insights.d.ts +156 -0
- package/dist/resiliency/crash-insights.d.ts.map +1 -0
- package/dist/resiliency/crash-insights.js +492 -0
- package/dist/resiliency/crash-insights.js.map +1 -0
- package/dist/resiliency/gossip-health.d.ts +137 -0
- package/dist/resiliency/gossip-health.d.ts.map +1 -0
- package/dist/resiliency/gossip-health.js +241 -0
- package/dist/resiliency/gossip-health.js.map +1 -0
- package/dist/resiliency/health-monitor.d.ts +97 -0
- package/dist/resiliency/health-monitor.d.ts.map +1 -0
- package/dist/resiliency/health-monitor.js +291 -0
- package/dist/resiliency/health-monitor.js.map +1 -0
- package/dist/resiliency/index.d.ts +68 -0
- package/dist/resiliency/index.d.ts.map +1 -0
- package/dist/resiliency/index.js +68 -0
- package/dist/resiliency/index.js.map +1 -0
- package/dist/resiliency/leader-watchdog.d.ts +109 -0
- package/dist/resiliency/leader-watchdog.d.ts.map +1 -0
- package/dist/resiliency/leader-watchdog.js +189 -0
- package/dist/resiliency/leader-watchdog.js.map +1 -0
- package/dist/resiliency/logger.d.ts +114 -0
- package/dist/resiliency/logger.d.ts.map +1 -0
- package/dist/resiliency/logger.js +250 -0
- package/dist/resiliency/logger.js.map +1 -0
- package/dist/resiliency/memory-monitor.d.ts +172 -0
- package/dist/resiliency/memory-monitor.d.ts.map +1 -0
- package/dist/resiliency/memory-monitor.js +593 -0
- package/dist/resiliency/memory-monitor.js.map +1 -0
- package/dist/resiliency/metrics.d.ts +115 -0
- package/dist/resiliency/metrics.d.ts.map +1 -0
- package/dist/resiliency/metrics.js +239 -0
- package/dist/resiliency/metrics.js.map +1 -0
- package/dist/resiliency/provider-context.d.ts +100 -0
- package/dist/resiliency/provider-context.d.ts.map +1 -0
- package/dist/resiliency/provider-context.js +360 -0
- package/dist/resiliency/provider-context.js.map +1 -0
- package/dist/resiliency/stateless-lead.d.ts +149 -0
- package/dist/resiliency/stateless-lead.d.ts.map +1 -0
- package/dist/resiliency/stateless-lead.js +308 -0
- package/dist/resiliency/stateless-lead.js.map +1 -0
- package/dist/resiliency/supervisor.d.ts +147 -0
- package/dist/resiliency/supervisor.d.ts.map +1 -0
- package/dist/resiliency/supervisor.js +459 -0
- package/dist/resiliency/supervisor.js.map +1 -0
- package/dist/shared/cli-auth-config.d.ts +91 -0
- package/dist/shared/cli-auth-config.d.ts.map +1 -0
- package/dist/shared/cli-auth-config.js +264 -0
- package/dist/shared/cli-auth-config.js.map +1 -0
- package/dist/storage/adapter.d.ts +3 -1
- package/dist/storage/adapter.d.ts.map +1 -1
- package/dist/storage/adapter.js +12 -2
- package/dist/storage/adapter.js.map +1 -1
- package/dist/storage/sqlite-adapter.d.ts.map +1 -1
- package/dist/storage/sqlite-adapter.js +18 -14
- package/dist/storage/sqlite-adapter.js.map +1 -1
- package/dist/trajectory/config.d.ts +84 -0
- package/dist/trajectory/config.d.ts.map +1 -0
- package/dist/trajectory/config.js +163 -0
- package/dist/trajectory/config.js.map +1 -0
- package/dist/trajectory/index.d.ts +8 -0
- package/dist/trajectory/index.d.ts.map +1 -0
- package/dist/trajectory/index.js +8 -0
- package/dist/trajectory/index.js.map +1 -0
- package/dist/trajectory/integration.d.ts +292 -0
- package/dist/trajectory/integration.d.ts.map +1 -0
- package/dist/trajectory/integration.js +834 -0
- package/dist/trajectory/integration.js.map +1 -0
- package/dist/utils/index.d.ts +1 -0
- package/dist/utils/index.d.ts.map +1 -1
- package/dist/utils/index.js +1 -0
- package/dist/utils/index.js.map +1 -1
- package/dist/utils/logger.d.ts +40 -0
- package/dist/utils/logger.d.ts.map +1 -0
- package/dist/utils/logger.js +84 -0
- package/dist/utils/logger.js.map +1 -0
- package/dist/utils/project-namespace.d.ts +24 -0
- package/dist/utils/project-namespace.d.ts.map +1 -1
- package/dist/utils/project-namespace.js +84 -0
- package/dist/utils/project-namespace.js.map +1 -1
- package/dist/wrapper/client.d.ts +16 -1
- package/dist/wrapper/client.d.ts.map +1 -1
- package/dist/wrapper/client.js +32 -1
- package/dist/wrapper/client.js.map +1 -1
- package/dist/wrapper/parser.d.ts +13 -0
- package/dist/wrapper/parser.d.ts.map +1 -1
- package/dist/wrapper/parser.js +217 -47
- package/dist/wrapper/parser.js.map +1 -1
- package/dist/wrapper/pty-wrapper.d.ts +219 -17
- package/dist/wrapper/pty-wrapper.d.ts.map +1 -1
- package/dist/wrapper/pty-wrapper.js +1050 -104
- package/dist/wrapper/pty-wrapper.js.map +1 -1
- package/dist/wrapper/shared.d.ts +165 -0
- package/dist/wrapper/shared.d.ts.map +1 -0
- package/dist/wrapper/shared.js +270 -0
- package/dist/wrapper/shared.js.map +1 -0
- package/dist/wrapper/tmux-wrapper.d.ts +78 -11
- package/dist/wrapper/tmux-wrapper.d.ts.map +1 -1
- package/dist/wrapper/tmux-wrapper.js +567 -106
- package/dist/wrapper/tmux-wrapper.js.map +1 -1
- package/docs/CLOUD-ARCHITECTURE.md +804 -0
- package/docs/CLOUD-ONBOARDING-DESIGN.md +1983 -0
- package/docs/HOOKS_API.md +394 -0
- package/docs/WRAPPER_EVENTS.md +358 -0
- package/docs/agent-policy-snippet.md +40 -0
- package/docs/agent-relay-protocol.md +238 -0
- package/docs/agent-relay-snippet.md +115 -6
- package/docs/archive/EXECUTIVE_SUMMARY.md +358 -0
- package/docs/archive/ROADMAP.md +329 -0
- package/docs/archive/TESTING_PRESENCE_FEATURES.md +327 -0
- package/docs/competitive/GASTOWN.md +451 -0
- package/docs/{COMPETITIVE_ANALYSIS.md → competitive/OVERVIEW.md} +1 -0
- package/docs/competitive/README.md +34 -0
- package/docs/competitive/TMUX_ORCHESTRATOR.md +605 -0
- package/docs/dashboard.png +0 -0
- package/docs/design/ci-failure-webhooks.md +812 -0
- package/docs/design/comprehensive-integrations.md +238 -0
- package/docs/design/e2b-sandbox-integration.md +504 -0
- package/docs/design/github-app-permissions.md +264 -0
- package/docs/guides/CLOUD.md +236 -0
- package/docs/guides/LOCAL.md +535 -0
- package/docs/guides/SELF-HOSTED.md +494 -0
- package/docs/local-testing.md +428 -0
- package/docs/proposals/continuous-claude-integration.md +622 -0
- package/docs/proposals/custom-commands.md +368 -0
- package/docs/proposals/shadow-as-subagent.md +765 -0
- package/docs/proposals/slack-bot-integration.md +1457 -0
- package/docs/tasks/global-skills-system.tasks.md +230 -0
- package/docs/tasks/webhook-integrations.tasks.md +184 -0
- package/docs/tasks/workspace-capabilities.tasks.md +121 -0
- package/docs/testing/RESILIENCY-TEST-PLAN-2026-01-01.md +366 -0
- package/package.json +45 -7
- package/scripts/cloud-setup.sh +96 -0
- package/scripts/manual-qa.sh +293 -0
- package/scripts/postinstall.js +60 -0
- package/scripts/run-cloud-qa.sh +220 -0
- package/scripts/test-cli-auth/Dockerfile +44 -0
- package/scripts/test-cli-auth/Dockerfile.real +79 -0
- package/scripts/test-cli-auth/README.md +286 -0
- package/scripts/test-cli-auth/ci-test-real-clis.ts +251 -0
- package/scripts/test-cli-auth/ci-test-runner.ts +263 -0
- package/scripts/test-cli-auth/mock-cli.sh +147 -0
- package/scripts/test-cli-auth/package.json +14 -0
- package/scripts/test-cli-auth/test-oauth-flow.ts +220 -0
- package/scripts/test-pty-input-auto.js +222 -0
- package/scripts/test-pty-input.js +150 -0
- package/dist/dashboard/out/_next/static/chunks/app/layout-c9d8c5d95e48c6bf.js +0 -1
- package/dist/dashboard/out/_next/static/chunks/app/metrics/page-8aa9936bc6c771ab.js +0 -1
- package/dist/dashboard/out/_next/static/chunks/app/page-4498be09a5157759.js +0 -1
- package/dist/dashboard/out/_next/static/chunks/main-app-bae2e535de00de50.js +0 -1
- package/dist/dashboard/out/_next/static/chunks/webpack-c81f7fd28659d64f.js +0 -1
- package/dist/dashboard/out/_next/static/css/50ed6996e3df7bdd.css +0 -1
- /package/dist/dashboard/out/_next/static/{DXFA-jj8wb3PcY5DX2xcU → H5aWG0udPB4iOUIl_gytz}/_buildManifest.js +0 -0
- /package/dist/dashboard/out/_next/static/{DXFA-jj8wb3PcY5DX2xcU → H5aWG0udPB4iOUIl_gytz}/_ssgManifest.js +0 -0
- /package/dist/dashboard/out/_next/static/chunks/{117-3bef7b19f3e60751.js → 117-b100311aff8d5c61.js} +0 -0
- /package/dist/dashboard/out/_next/static/chunks/{648-6cf686106c891ad3.js → 648-a13d3c2b1be45466.js} +0 -0
- /package/dist/dashboard/out/_next/static/chunks/app/_not-found/{page-8ff6572bc7c9bc61.js → page-a4973f3e3c82fb67.js} +0 -0
- /package/dist/dashboard/out/_next/static/chunks/{fd9d1056-26bd8d656b496dba.js → fd9d1056-bf46c09eb57e019c.js} +0 -0
- /package/docs/{CHANGELOG.md → archive/CHANGELOG.md} +0 -0
- /package/docs/{CLI-SIMPLIFICATION-COMPLETE.md → archive/CLI-SIMPLIFICATION-COMPLETE.md} +0 -0
- /package/docs/{DESIGN_BRIDGE_STAFFING.md → archive/DESIGN_BRIDGE_STAFFING.md} +0 -0
- /package/docs/{DESIGN_V2.md → archive/DESIGN_V2.md} +0 -0
- /package/docs/{MONETIZATION.md → archive/MONETIZATION.md} +0 -0
- /package/docs/{PROPOSAL-trajectories.md → archive/PROPOSAL-trajectories.md} +0 -0
- /package/docs/{SCALING_ANALYSIS.md → archive/SCALING_ANALYSIS.md} +0 -0
- /package/docs/{TMUX_IMPLEMENTATION_NOTES.md → archive/TMUX_IMPLEMENTATION_NOTES.md} +0 -0
- /package/docs/{TMUX_IMPROVEMENTS.md → archive/TMUX_IMPROVEMENTS.md} +0 -0
- /package/docs/{dashboard-v2-plan.md → archive/dashboard-v2-plan.md} +0 -0
- /package/docs/{removable-code-analysis.md → archive/removable-code-analysis.md} +0 -0
- /package/docs/{competitive-analysis-mcp-agent-mail.md → competitive/MCP_AGENT_MAIL.md} +0 -0
package/dist/cli/index.js
CHANGED
|
@@ -17,6 +17,8 @@ import { RelayClient } from '../wrapper/client.js';
|
|
|
17
17
|
import { generateAgentName } from '../utils/name-generator.js';
|
|
18
18
|
import { getTmuxPath } from '../utils/tmux-resolver.js';
|
|
19
19
|
import { readWorkersMetadata, getWorkerLogsDir } from '../bridge/spawner.js';
|
|
20
|
+
import { getShadowForAgent } from '../bridge/shadow-config.js';
|
|
21
|
+
import { selectShadowCli } from '../bridge/shadow-cli.js';
|
|
20
22
|
import { checkForUpdatesInBackground, checkForUpdates } from '../utils/update-checker.js';
|
|
21
23
|
import fs from 'node:fs';
|
|
22
24
|
import path from 'node:path';
|
|
@@ -53,6 +55,9 @@ program
|
|
|
53
55
|
.option('-n, --name <name>', 'Agent name (auto-generated if not set)')
|
|
54
56
|
.option('-q, --quiet', 'Disable debug output', false)
|
|
55
57
|
.option('--prefix <pattern>', 'Relay prefix pattern (default: ->relay:)')
|
|
58
|
+
.option('--dashboard-port <port>', 'Dashboard port for spawn/release API (auto-detected if not set)')
|
|
59
|
+
.option('--shadow <name>', 'Spawn a shadow agent with this name that monitors the primary')
|
|
60
|
+
.option('--shadow-role <role>', 'Shadow role: reviewer, auditor, or triggers (comma-separated: SESSION_END,CODE_WRITTEN,REVIEW_REQUEST,EXPLICIT_ASK,ALL_MESSAGES)')
|
|
56
61
|
.argument('[command...]', 'Command to wrap (e.g., claude)')
|
|
57
62
|
.action(async (commandParts, options) => {
|
|
58
63
|
// If no command provided, show help
|
|
@@ -81,8 +86,31 @@ program
|
|
|
81
86
|
}
|
|
82
87
|
const { TmuxWrapper } = await import('../wrapper/tmux-wrapper.js');
|
|
83
88
|
const { AgentSpawner } = await import('../bridge/spawner.js');
|
|
84
|
-
//
|
|
85
|
-
|
|
89
|
+
// Determine dashboard port for spawn/release API
|
|
90
|
+
// Priority: CLI flag > env var > auto-detect default port
|
|
91
|
+
let dashboardPort;
|
|
92
|
+
if (options.dashboardPort) {
|
|
93
|
+
dashboardPort = parseInt(options.dashboardPort, 10);
|
|
94
|
+
}
|
|
95
|
+
else {
|
|
96
|
+
// Try to detect if dashboard is running at default port
|
|
97
|
+
const defaultPort = parseInt(DEFAULT_DASHBOARD_PORT, 10);
|
|
98
|
+
try {
|
|
99
|
+
const response = await fetch(`http://localhost:${defaultPort}/api/status`, {
|
|
100
|
+
method: 'GET',
|
|
101
|
+
signal: AbortSignal.timeout(500), // Quick timeout for detection
|
|
102
|
+
});
|
|
103
|
+
if (response.ok) {
|
|
104
|
+
dashboardPort = defaultPort;
|
|
105
|
+
console.error(`Dashboard detected: http://localhost:${dashboardPort}`);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
catch {
|
|
109
|
+
// Dashboard not running - spawn/release will use fallback callbacks
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
// Create spawner as fallback for direct spawn (if dashboard API not available)
|
|
113
|
+
const spawner = new AgentSpawner(paths.projectRoot, undefined, dashboardPort);
|
|
86
114
|
const wrapper = new TmuxWrapper({
|
|
87
115
|
name: agentName,
|
|
88
116
|
command: mainCommand,
|
|
@@ -92,7 +120,9 @@ program
|
|
|
92
120
|
relayPrefix: options.prefix,
|
|
93
121
|
useInbox: true,
|
|
94
122
|
inboxDir: paths.dataDir, // Use the project-specific data directory for the inbox
|
|
95
|
-
//
|
|
123
|
+
// Use dashboard API for spawn/release when available (preferred - works from any context)
|
|
124
|
+
dashboardPort,
|
|
125
|
+
// Wire up spawn/release callbacks as fallback (if no dashboardPort)
|
|
96
126
|
onSpawn: async (workerName, workerCli, task) => {
|
|
97
127
|
console.error(`[${agentName}] Spawning ${workerName} (${workerCli})...`);
|
|
98
128
|
const result = await spawner.spawn({
|
|
@@ -121,10 +151,80 @@ program
|
|
|
121
151
|
});
|
|
122
152
|
process.on('SIGINT', async () => {
|
|
123
153
|
await spawner.releaseAll();
|
|
124
|
-
wrapper.stop();
|
|
154
|
+
await wrapper.stop();
|
|
125
155
|
process.exit(0);
|
|
126
156
|
});
|
|
127
157
|
await wrapper.start();
|
|
158
|
+
let shadowName;
|
|
159
|
+
let shadowRole;
|
|
160
|
+
let speakOn;
|
|
161
|
+
let shadowCli;
|
|
162
|
+
let shadowPrompt;
|
|
163
|
+
const rolePresets = {
|
|
164
|
+
reviewer: ['CODE_WRITTEN', 'REVIEW_REQUEST', 'EXPLICIT_ASK'],
|
|
165
|
+
auditor: ['SESSION_END', 'EXPLICIT_ASK'],
|
|
166
|
+
active: ['ALL_MESSAGES'],
|
|
167
|
+
};
|
|
168
|
+
if (options.shadow) {
|
|
169
|
+
// CLI flags provided
|
|
170
|
+
shadowName = options.shadow;
|
|
171
|
+
const role = options.shadowRole || 'EXPLICIT_ASK';
|
|
172
|
+
shadowRole = role;
|
|
173
|
+
if (rolePresets[role.toLowerCase()]) {
|
|
174
|
+
speakOn = rolePresets[role.toLowerCase()];
|
|
175
|
+
}
|
|
176
|
+
else {
|
|
177
|
+
speakOn = role.split(',').map((s) => s.trim().toUpperCase());
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
else {
|
|
181
|
+
// Check config file for shadow configuration
|
|
182
|
+
const shadowConfig = getShadowForAgent(paths.projectRoot, agentName);
|
|
183
|
+
if (shadowConfig) {
|
|
184
|
+
shadowName = shadowConfig.shadowName;
|
|
185
|
+
shadowRole = shadowConfig.roleName;
|
|
186
|
+
speakOn = shadowConfig.speakOn;
|
|
187
|
+
shadowCli = shadowConfig.cli;
|
|
188
|
+
shadowPrompt = shadowConfig.prompt;
|
|
189
|
+
console.error(`Shadow config: ${shadowName} (from .agent-relay.json)`);
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
// Spawn shadow if configured
|
|
193
|
+
if (shadowName && speakOn) {
|
|
194
|
+
// Decide how to run the shadow (subagent for Claude/OpenCode primaries)
|
|
195
|
+
let shadowSelection = null;
|
|
196
|
+
try {
|
|
197
|
+
shadowSelection = await selectShadowCli(mainCommand, { preferredShadowCli: shadowCli });
|
|
198
|
+
console.error(`[shadow] Mode: ${shadowSelection.mode} via ${shadowSelection.command || shadowSelection.cli} (primary: ${mainCommand})`);
|
|
199
|
+
}
|
|
200
|
+
catch (err) {
|
|
201
|
+
console.error(`[shadow] Shadow CLI selection failed: ${err.message}`);
|
|
202
|
+
}
|
|
203
|
+
// Subagent mode: do not spawn a separate shadow process
|
|
204
|
+
if (shadowSelection?.mode === 'subagent') {
|
|
205
|
+
console.error(`[shadow] ${shadowName} will run as ${shadowSelection.cli} subagent inside ${agentName}; no separate process spawned`);
|
|
206
|
+
return;
|
|
207
|
+
}
|
|
208
|
+
console.error(`Shadow: ${shadowName} (shadowing ${agentName}, speakOn: ${speakOn.join(',')})`);
|
|
209
|
+
// Wait for primary to register before spawning shadow
|
|
210
|
+
await new Promise(r => setTimeout(r, 3000));
|
|
211
|
+
// Build shadow task prompt
|
|
212
|
+
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.`;
|
|
213
|
+
const shadowTask = shadowPrompt || defaultPrompt;
|
|
214
|
+
const result = await spawner.spawn({
|
|
215
|
+
name: shadowName,
|
|
216
|
+
cli: shadowSelection?.command || shadowCli || mainCommand,
|
|
217
|
+
task: shadowTask,
|
|
218
|
+
shadowOf: agentName,
|
|
219
|
+
shadowSpeakOn: speakOn,
|
|
220
|
+
});
|
|
221
|
+
if (result.success) {
|
|
222
|
+
console.error(`Shadow ${shadowName} started [pid: ${result.pid}]`);
|
|
223
|
+
}
|
|
224
|
+
else {
|
|
225
|
+
console.error(`Failed to spawn shadow ${shadowName}: ${result.error}`);
|
|
226
|
+
}
|
|
227
|
+
}
|
|
128
228
|
});
|
|
129
229
|
// up - Start daemon + dashboard
|
|
130
230
|
program
|
|
@@ -134,7 +234,67 @@ program
|
|
|
134
234
|
.option('--port <port>', 'Dashboard port', DEFAULT_DASHBOARD_PORT)
|
|
135
235
|
.option('--spawn', 'Force spawn all agents from teams.json')
|
|
136
236
|
.option('--no-spawn', 'Do not auto-spawn agents (just start daemon)')
|
|
237
|
+
.option('--watch', 'Auto-restart daemon on crash (supervisor mode)')
|
|
238
|
+
.option('--max-restarts <n>', 'Max restarts in 60s before giving up (default: 5)', '5')
|
|
137
239
|
.action(async (options) => {
|
|
240
|
+
// If --watch is specified, run in supervisor mode
|
|
241
|
+
if (options.watch) {
|
|
242
|
+
const { spawn } = await import('node:child_process');
|
|
243
|
+
const maxRestarts = parseInt(options.maxRestarts, 10) || 5;
|
|
244
|
+
const restartWindow = 60_000; // 60 seconds
|
|
245
|
+
const restartTimes = [];
|
|
246
|
+
let child = null;
|
|
247
|
+
let shuttingDown = false;
|
|
248
|
+
const startDaemon = () => {
|
|
249
|
+
// Build args without --watch to prevent infinite recursion
|
|
250
|
+
const args = ['up'];
|
|
251
|
+
if (options.dashboard === false)
|
|
252
|
+
args.push('--no-dashboard');
|
|
253
|
+
if (options.port)
|
|
254
|
+
args.push('--port', options.port);
|
|
255
|
+
if (options.spawn === true)
|
|
256
|
+
args.push('--spawn');
|
|
257
|
+
if (options.spawn === false)
|
|
258
|
+
args.push('--no-spawn');
|
|
259
|
+
console.log(`[supervisor] Starting daemon...`);
|
|
260
|
+
child = spawn(process.execPath, [process.argv[1], ...args], {
|
|
261
|
+
stdio: 'inherit',
|
|
262
|
+
env: { ...process.env, AGENT_RELAY_SUPERVISED: '1' },
|
|
263
|
+
});
|
|
264
|
+
child.on('exit', (code, signal) => {
|
|
265
|
+
if (shuttingDown) {
|
|
266
|
+
process.exit(0);
|
|
267
|
+
return;
|
|
268
|
+
}
|
|
269
|
+
const now = Date.now();
|
|
270
|
+
restartTimes.push(now);
|
|
271
|
+
// Remove restarts outside the window
|
|
272
|
+
while (restartTimes.length > 0 && restartTimes[0] < now - restartWindow) {
|
|
273
|
+
restartTimes.shift();
|
|
274
|
+
}
|
|
275
|
+
if (restartTimes.length >= maxRestarts) {
|
|
276
|
+
console.error(`[supervisor] Daemon crashed ${maxRestarts} times in ${restartWindow / 1000}s, giving up`);
|
|
277
|
+
process.exit(1);
|
|
278
|
+
}
|
|
279
|
+
const exitReason = signal ? `signal ${signal}` : `code ${code}`;
|
|
280
|
+
console.log(`[supervisor] Daemon exited (${exitReason}), restarting in 2s... (${restartTimes.length}/${maxRestarts} restarts)`);
|
|
281
|
+
setTimeout(startDaemon, 2000);
|
|
282
|
+
});
|
|
283
|
+
};
|
|
284
|
+
process.on('SIGINT', () => {
|
|
285
|
+
console.log('\n[supervisor] Stopping...');
|
|
286
|
+
shuttingDown = true;
|
|
287
|
+
if (child)
|
|
288
|
+
child.kill('SIGINT');
|
|
289
|
+
});
|
|
290
|
+
process.on('SIGTERM', () => {
|
|
291
|
+
shuttingDown = true;
|
|
292
|
+
if (child)
|
|
293
|
+
child.kill('SIGTERM');
|
|
294
|
+
});
|
|
295
|
+
startDaemon();
|
|
296
|
+
return;
|
|
297
|
+
}
|
|
138
298
|
const { ensureProjectDir } = await import('../utils/project-namespace.js');
|
|
139
299
|
const { loadTeamsConfig } = await import('../bridge/teams-config.js');
|
|
140
300
|
const { AgentSpawner } = await import('../bridge/spawner.js');
|
|
@@ -157,6 +317,34 @@ program
|
|
|
157
317
|
});
|
|
158
318
|
// Create spawner for auto-spawn (will be initialized after dashboard starts)
|
|
159
319
|
let spawner = null;
|
|
320
|
+
// Track if we're already shutting down to prevent double-cleanup
|
|
321
|
+
let isShuttingDown = false;
|
|
322
|
+
const gracefulShutdown = async (reason) => {
|
|
323
|
+
if (isShuttingDown)
|
|
324
|
+
return;
|
|
325
|
+
isShuttingDown = true;
|
|
326
|
+
console.log(`\n[daemon] ${reason}, shutting down...`);
|
|
327
|
+
try {
|
|
328
|
+
if (spawner)
|
|
329
|
+
await spawner.releaseAll();
|
|
330
|
+
await daemon.stop();
|
|
331
|
+
}
|
|
332
|
+
catch (err) {
|
|
333
|
+
console.error('[daemon] Error during shutdown:', err);
|
|
334
|
+
}
|
|
335
|
+
process.exit(1);
|
|
336
|
+
};
|
|
337
|
+
// Handle uncaught exceptions - log and exit (supervisor will restart)
|
|
338
|
+
process.on('uncaughtException', (err) => {
|
|
339
|
+
console.error('[daemon] Uncaught exception:', err);
|
|
340
|
+
gracefulShutdown('Uncaught exception');
|
|
341
|
+
});
|
|
342
|
+
// Handle unhandled promise rejections
|
|
343
|
+
process.on('unhandledRejection', (reason, promise) => {
|
|
344
|
+
console.error('[daemon] Unhandled rejection at:', promise, 'reason:', reason);
|
|
345
|
+
// Don't exit on unhandled rejections - just log them
|
|
346
|
+
// Most are recoverable (e.g., failed message delivery)
|
|
347
|
+
});
|
|
160
348
|
process.on('SIGINT', async () => {
|
|
161
349
|
console.log('\nStopping...');
|
|
162
350
|
if (spawner) {
|
|
@@ -189,6 +377,13 @@ program
|
|
|
189
377
|
projectRoot: paths.projectRoot,
|
|
190
378
|
});
|
|
191
379
|
console.log(`Dashboard: http://localhost:${dashboardPort}`);
|
|
380
|
+
// Hook daemon log output to dashboard WebSocket
|
|
381
|
+
daemon.onLogOutput = (agentName, data, _timestamp) => {
|
|
382
|
+
const broadcast = global.__broadcastLogOutput;
|
|
383
|
+
if (broadcast) {
|
|
384
|
+
broadcast(agentName, data);
|
|
385
|
+
}
|
|
386
|
+
};
|
|
192
387
|
}
|
|
193
388
|
// Determine if we should auto-spawn agents
|
|
194
389
|
// --spawn: force spawn
|
|
@@ -288,9 +483,11 @@ program
|
|
|
288
483
|
.command('agents')
|
|
289
484
|
.description('List connected agents and spawned workers')
|
|
290
485
|
.option('--all', 'Include internal/CLI agents')
|
|
486
|
+
.option('--remote', 'Include agents from other linked machines (requires cloud link)')
|
|
291
487
|
.option('--json', 'Output as JSON')
|
|
292
488
|
.action(async (options) => {
|
|
293
489
|
const { getProjectPaths } = await import('../utils/project-namespace.js');
|
|
490
|
+
const os = await import('node:os');
|
|
294
491
|
const paths = getProjectPaths();
|
|
295
492
|
const agentsPath = path.join(paths.teamDir, 'agents.json');
|
|
296
493
|
// Load registered agents
|
|
@@ -311,6 +508,7 @@ program
|
|
|
311
508
|
lastSeen: agent.lastSeen,
|
|
312
509
|
team: worker?.team,
|
|
313
510
|
pid: worker?.pid,
|
|
511
|
+
location: 'local',
|
|
314
512
|
});
|
|
315
513
|
});
|
|
316
514
|
// Add workers not in registry (orphaned or not yet registered)
|
|
@@ -323,9 +521,51 @@ program
|
|
|
323
521
|
cli: worker.cli || '-',
|
|
324
522
|
team: worker.team,
|
|
325
523
|
pid: worker.pid,
|
|
524
|
+
location: 'local',
|
|
326
525
|
});
|
|
327
526
|
}
|
|
328
527
|
});
|
|
528
|
+
// Include remote agents if --remote flag is set
|
|
529
|
+
if (options.remote) {
|
|
530
|
+
const dataDir = process.env.AGENT_RELAY_DATA_DIR ||
|
|
531
|
+
path.join(os.homedir(), '.local', 'share', 'agent-relay');
|
|
532
|
+
const configPath = path.join(dataDir, 'cloud-config.json');
|
|
533
|
+
if (fs.existsSync(configPath)) {
|
|
534
|
+
try {
|
|
535
|
+
const config = JSON.parse(fs.readFileSync(configPath, 'utf-8'));
|
|
536
|
+
const response = await fetch(`${config.cloudUrl}/api/daemons/agents`, {
|
|
537
|
+
method: 'POST',
|
|
538
|
+
headers: {
|
|
539
|
+
'Authorization': `Bearer ${config.apiKey}`,
|
|
540
|
+
'Content-Type': 'application/json',
|
|
541
|
+
},
|
|
542
|
+
body: JSON.stringify({ agents: [] }),
|
|
543
|
+
});
|
|
544
|
+
if (response.ok) {
|
|
545
|
+
const data = await response.json();
|
|
546
|
+
// Add remote agents (exclude local ones by name)
|
|
547
|
+
const localNames = new Set(combined.map(a => a.name));
|
|
548
|
+
for (const agent of data.allAgents) {
|
|
549
|
+
if (!localNames.has(agent.name)) {
|
|
550
|
+
combined.push({
|
|
551
|
+
name: agent.name,
|
|
552
|
+
status: agent.status.toUpperCase(),
|
|
553
|
+
cli: '-',
|
|
554
|
+
location: agent.daemonName,
|
|
555
|
+
daemonId: agent.daemonId,
|
|
556
|
+
});
|
|
557
|
+
}
|
|
558
|
+
}
|
|
559
|
+
}
|
|
560
|
+
}
|
|
561
|
+
catch (err) {
|
|
562
|
+
console.error('[warn] Failed to fetch remote agents:', err.message);
|
|
563
|
+
}
|
|
564
|
+
}
|
|
565
|
+
else {
|
|
566
|
+
console.error('[warn] Cloud not linked. Run `agent-relay cloud link` to see remote agents.');
|
|
567
|
+
}
|
|
568
|
+
}
|
|
329
569
|
if (options.json) {
|
|
330
570
|
console.log(JSON.stringify(combined, null, 2));
|
|
331
571
|
return;
|
|
@@ -335,21 +575,39 @@ program
|
|
|
335
575
|
console.log(`No agents found. Ensure the daemon is running and agents are connected${hint}.`);
|
|
336
576
|
return;
|
|
337
577
|
}
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
578
|
+
const hasRemote = combined.some(a => a.location !== 'local');
|
|
579
|
+
if (hasRemote) {
|
|
580
|
+
console.log('NAME STATUS CLI LOCATION');
|
|
581
|
+
console.log('─'.repeat(55));
|
|
582
|
+
combined.forEach((agent) => {
|
|
583
|
+
const name = agent.name.padEnd(15);
|
|
584
|
+
const status = agent.status.padEnd(8);
|
|
585
|
+
const cli = agent.cli.padEnd(9);
|
|
586
|
+
const location = agent.location ?? 'local';
|
|
587
|
+
console.log(`${name} ${status} ${cli} ${location}`);
|
|
588
|
+
});
|
|
589
|
+
}
|
|
590
|
+
else {
|
|
591
|
+
console.log('NAME STATUS CLI TEAM');
|
|
592
|
+
console.log('─'.repeat(50));
|
|
593
|
+
combined.forEach((agent) => {
|
|
594
|
+
const name = agent.name.padEnd(15);
|
|
595
|
+
const status = agent.status.padEnd(8);
|
|
596
|
+
const cli = agent.cli.padEnd(9);
|
|
597
|
+
const team = agent.team ?? '-';
|
|
598
|
+
console.log(`${name} ${status} ${cli} ${team}`);
|
|
599
|
+
});
|
|
600
|
+
}
|
|
347
601
|
if (workers.length > 0) {
|
|
348
602
|
console.log('');
|
|
349
603
|
console.log('Commands:');
|
|
350
604
|
console.log(' agent-relay agents:logs <name> - View spawned agent output');
|
|
351
605
|
console.log(' agent-relay agents:kill <name> - Kill a spawned agent');
|
|
352
606
|
}
|
|
607
|
+
if (!options.remote) {
|
|
608
|
+
console.log('');
|
|
609
|
+
console.log('Tip: Use --remote to include agents from other linked machines.');
|
|
610
|
+
}
|
|
353
611
|
});
|
|
354
612
|
// who - Show currently active agents (online within last 30s)
|
|
355
613
|
program
|
|
@@ -541,6 +799,7 @@ program
|
|
|
541
799
|
.description('Bridge multiple projects as orchestrator')
|
|
542
800
|
.argument('[projects...]', 'Project paths to bridge')
|
|
543
801
|
.option('--cli <tool>', 'CLI tool override for all projects')
|
|
802
|
+
.option('--architect [cli]', 'Spawn an architect agent to coordinate all projects (default: claude)')
|
|
544
803
|
.action(async (projectPaths, options) => {
|
|
545
804
|
const { resolveProjects, validateDaemons } = await import('../bridge/config.js');
|
|
546
805
|
const { MultiProjectClient } = await import('../bridge/multi-project-client.js');
|
|
@@ -646,9 +905,106 @@ program
|
|
|
646
905
|
console.log('Connected to all projects.');
|
|
647
906
|
console.log('');
|
|
648
907
|
console.log('Cross-project messaging:');
|
|
649
|
-
console.log('
|
|
650
|
-
console.log('
|
|
908
|
+
console.log(' ->relay:projectId:agent Message');
|
|
909
|
+
console.log(' ->relay:*:lead Broadcast to all leads');
|
|
651
910
|
console.log('');
|
|
911
|
+
// Spawn architect agent if --architect flag is set
|
|
912
|
+
let architectWrapper = null;
|
|
913
|
+
if (options.architect !== undefined) {
|
|
914
|
+
const { TmuxWrapper } = await import('../wrapper/tmux-wrapper.js');
|
|
915
|
+
// Determine CLI to use (default to claude)
|
|
916
|
+
const architectCli = typeof options.architect === 'string' ? options.architect : 'claude';
|
|
917
|
+
// Use first project as the base for the architect
|
|
918
|
+
const baseProject = valid[0];
|
|
919
|
+
const basePaths = getProjectPaths(baseProject.path);
|
|
920
|
+
// Build project context for the architect
|
|
921
|
+
const projectContext = valid.map(p => `- ${p.id}: ${p.path} (Lead: ${p.leadName})`).join('\n');
|
|
922
|
+
// Create architect system prompt
|
|
923
|
+
const architectPrompt = `You are the Architect, a cross-project coordinator overseeing multiple codebases.
|
|
924
|
+
|
|
925
|
+
## Connected Projects
|
|
926
|
+
${projectContext}
|
|
927
|
+
|
|
928
|
+
## Your Role
|
|
929
|
+
- Coordinate high-level work across all projects
|
|
930
|
+
- Assign tasks to project leads
|
|
931
|
+
- Ensure consistency and resolve cross-project dependencies
|
|
932
|
+
- Review overall architecture decisions
|
|
933
|
+
|
|
934
|
+
## Cross-Project Messaging
|
|
935
|
+
|
|
936
|
+
Use this syntax to message agents in specific projects:
|
|
937
|
+
|
|
938
|
+
\`\`\`
|
|
939
|
+
->relay:${valid[0].id}:${valid[0].leadName} <<<
|
|
940
|
+
Your message to this project's lead>>>
|
|
941
|
+
|
|
942
|
+
->relay:${valid.length > 1 ? valid[1].id : valid[0].id}:* <<<
|
|
943
|
+
Broadcast to all agents in a project>>>
|
|
944
|
+
|
|
945
|
+
->relay:*:* <<<
|
|
946
|
+
Broadcast to ALL agents in ALL projects>>>
|
|
947
|
+
\`\`\`
|
|
948
|
+
|
|
949
|
+
Format: \`->relay:project-id:agent-name\`
|
|
950
|
+
|
|
951
|
+
## Getting Started
|
|
952
|
+
1. Check in with each project lead to understand current status
|
|
953
|
+
2. Identify cross-project dependencies
|
|
954
|
+
3. Coordinate work across teams
|
|
955
|
+
|
|
956
|
+
Start by greeting the project leads and asking for status updates.`;
|
|
957
|
+
console.log('Spawning Architect agent...');
|
|
958
|
+
console.log(` CLI: ${architectCli}`);
|
|
959
|
+
console.log(` Base project: ${baseProject.path}`);
|
|
960
|
+
console.log('');
|
|
961
|
+
// Determine command and args based on CLI
|
|
962
|
+
let command;
|
|
963
|
+
let args = [];
|
|
964
|
+
if (architectCli === 'claude' || architectCli.startsWith('claude:')) {
|
|
965
|
+
command = 'claude';
|
|
966
|
+
args = ['--dangerously-skip-permissions'];
|
|
967
|
+
// Add model if specified (e.g., claude:opus)
|
|
968
|
+
if (architectCli.includes(':')) {
|
|
969
|
+
const model = architectCli.split(':')[1];
|
|
970
|
+
args.push('--model', model);
|
|
971
|
+
}
|
|
972
|
+
}
|
|
973
|
+
else if (architectCli === 'codex') {
|
|
974
|
+
command = 'codex';
|
|
975
|
+
args = ['--dangerously-skip-permissions'];
|
|
976
|
+
}
|
|
977
|
+
else {
|
|
978
|
+
command = architectCli;
|
|
979
|
+
}
|
|
980
|
+
try {
|
|
981
|
+
architectWrapper = new TmuxWrapper({
|
|
982
|
+
name: 'Architect',
|
|
983
|
+
command,
|
|
984
|
+
args,
|
|
985
|
+
socketPath: basePaths.socketPath,
|
|
986
|
+
debug: false,
|
|
987
|
+
useInbox: true,
|
|
988
|
+
inboxDir: basePaths.dataDir,
|
|
989
|
+
});
|
|
990
|
+
await architectWrapper.start();
|
|
991
|
+
// Wait for agent to be ready, then inject the prompt
|
|
992
|
+
setTimeout(async () => {
|
|
993
|
+
try {
|
|
994
|
+
await architectWrapper.injectMessage(architectPrompt);
|
|
995
|
+
console.log('Architect agent started and initialized.');
|
|
996
|
+
console.log('Attach to session: tmux attach -t relay-Architect');
|
|
997
|
+
console.log('');
|
|
998
|
+
}
|
|
999
|
+
catch (err) {
|
|
1000
|
+
console.error('Failed to inject architect prompt:', err);
|
|
1001
|
+
}
|
|
1002
|
+
}, 3000);
|
|
1003
|
+
}
|
|
1004
|
+
catch (err) {
|
|
1005
|
+
console.error('Failed to spawn Architect agent:', err);
|
|
1006
|
+
}
|
|
1007
|
+
}
|
|
652
1008
|
// Handle messages from projects
|
|
653
1009
|
client.onMessage = (projectId, from, payload, messageId) => {
|
|
654
1010
|
console.log(`[${projectId}] ${from}: ${payload.body.substring(0, 80)}...`);
|
|
@@ -985,6 +1341,40 @@ program
|
|
|
985
1341
|
}
|
|
986
1342
|
}
|
|
987
1343
|
});
|
|
1344
|
+
// release - Release a spawned agent via API (works from any context, no terminal required)
|
|
1345
|
+
program
|
|
1346
|
+
.command('release')
|
|
1347
|
+
.description('Release a spawned agent via API (no terminal required)')
|
|
1348
|
+
.argument('<name>', 'Agent name to release')
|
|
1349
|
+
.option('--port <port>', 'Dashboard port', DEFAULT_DASHBOARD_PORT)
|
|
1350
|
+
.action(async (name, options) => {
|
|
1351
|
+
const port = options.port || DEFAULT_DASHBOARD_PORT;
|
|
1352
|
+
try {
|
|
1353
|
+
const response = await fetch(`http://localhost:${port}/api/spawned/${encodeURIComponent(name)}`, {
|
|
1354
|
+
method: 'DELETE',
|
|
1355
|
+
});
|
|
1356
|
+
const result = await response.json();
|
|
1357
|
+
if (result.success) {
|
|
1358
|
+
console.log(`Released agent: ${name}`);
|
|
1359
|
+
process.exit(0);
|
|
1360
|
+
}
|
|
1361
|
+
else {
|
|
1362
|
+
console.error(`Failed to release ${name}: ${result.error || 'Unknown error'}`);
|
|
1363
|
+
process.exit(1);
|
|
1364
|
+
}
|
|
1365
|
+
}
|
|
1366
|
+
catch (err) {
|
|
1367
|
+
// If API call fails, try to provide helpful error message
|
|
1368
|
+
if (err.code === 'ECONNREFUSED') {
|
|
1369
|
+
console.error(`Cannot connect to dashboard at port ${port}. Is the daemon running?`);
|
|
1370
|
+
console.log(`Run 'agent-relay up' to start the daemon.`);
|
|
1371
|
+
}
|
|
1372
|
+
else {
|
|
1373
|
+
console.error(`Failed to release ${name}: ${err.message}`);
|
|
1374
|
+
}
|
|
1375
|
+
process.exit(1);
|
|
1376
|
+
}
|
|
1377
|
+
});
|
|
988
1378
|
// agents:kill - Kill a spawned agent by PID
|
|
989
1379
|
program
|
|
990
1380
|
.command('agents:kill')
|
|
@@ -1039,5 +1429,796 @@ program
|
|
|
1039
1429
|
}
|
|
1040
1430
|
}
|
|
1041
1431
|
});
|
|
1432
|
+
// ============================================================================
|
|
1433
|
+
// Cloud commands
|
|
1434
|
+
// ============================================================================
|
|
1435
|
+
const cloudCommand = program
|
|
1436
|
+
.command('cloud')
|
|
1437
|
+
.description('Cloud account and sync commands');
|
|
1438
|
+
cloudCommand
|
|
1439
|
+
.command('link')
|
|
1440
|
+
.description('Link this machine to your Agent Relay Cloud account')
|
|
1441
|
+
.option('--name <name>', 'Name for this machine')
|
|
1442
|
+
.option('--cloud-url <url>', 'Cloud API URL', process.env.AGENT_RELAY_CLOUD_URL || 'https://api.agent-relay.com')
|
|
1443
|
+
.action(async (options) => {
|
|
1444
|
+
const os = await import('node:os');
|
|
1445
|
+
const crypto = await import('node:crypto');
|
|
1446
|
+
const readline = await import('node:readline');
|
|
1447
|
+
const cloudUrl = options.cloudUrl;
|
|
1448
|
+
const machineName = options.name || os.hostname();
|
|
1449
|
+
// Generate machine ID
|
|
1450
|
+
const dataDir = process.env.AGENT_RELAY_DATA_DIR ||
|
|
1451
|
+
path.join(os.homedir(), '.local', 'share', 'agent-relay');
|
|
1452
|
+
const machineIdPath = path.join(dataDir, 'machine-id');
|
|
1453
|
+
const configPath = path.join(dataDir, 'cloud-config.json');
|
|
1454
|
+
let machineId;
|
|
1455
|
+
if (fs.existsSync(machineIdPath)) {
|
|
1456
|
+
machineId = fs.readFileSync(machineIdPath, 'utf-8').trim();
|
|
1457
|
+
}
|
|
1458
|
+
else {
|
|
1459
|
+
machineId = `${os.hostname()}-${crypto.randomBytes(8).toString('hex')}`;
|
|
1460
|
+
fs.mkdirSync(dataDir, { recursive: true });
|
|
1461
|
+
fs.writeFileSync(machineIdPath, machineId);
|
|
1462
|
+
}
|
|
1463
|
+
console.log('');
|
|
1464
|
+
console.log('🔗 Agent Relay Cloud - Link Machine');
|
|
1465
|
+
console.log('');
|
|
1466
|
+
console.log(`Machine: ${machineName}`);
|
|
1467
|
+
console.log(`ID: ${machineId}`);
|
|
1468
|
+
console.log('');
|
|
1469
|
+
// Generate a temporary code for the browser auth flow
|
|
1470
|
+
const tempCode = crypto.randomBytes(16).toString('hex');
|
|
1471
|
+
// Store temp code for callback
|
|
1472
|
+
const tempCodePath = path.join(dataDir, '.link-code');
|
|
1473
|
+
fs.writeFileSync(tempCodePath, tempCode);
|
|
1474
|
+
const authUrl = `${cloudUrl.replace('/api', '')}/cloud/link?code=${tempCode}&machine=${encodeURIComponent(machineId)}&name=${encodeURIComponent(machineName)}`;
|
|
1475
|
+
console.log('Open this URL in your browser to authenticate:');
|
|
1476
|
+
console.log('');
|
|
1477
|
+
console.log(` ${authUrl}`);
|
|
1478
|
+
console.log('');
|
|
1479
|
+
// Try to open browser automatically
|
|
1480
|
+
try {
|
|
1481
|
+
const openCommand = process.platform === 'darwin' ? 'open' :
|
|
1482
|
+
process.platform === 'win32' ? 'start' : 'xdg-open';
|
|
1483
|
+
await execAsync(`${openCommand} "${authUrl}"`);
|
|
1484
|
+
console.log('(Browser opened automatically)');
|
|
1485
|
+
}
|
|
1486
|
+
catch {
|
|
1487
|
+
console.log('(Copy the URL above and paste it in your browser)');
|
|
1488
|
+
}
|
|
1489
|
+
console.log('');
|
|
1490
|
+
console.log('After authenticating, paste your API key here:');
|
|
1491
|
+
const rl = readline.createInterface({
|
|
1492
|
+
input: process.stdin,
|
|
1493
|
+
output: process.stdout,
|
|
1494
|
+
});
|
|
1495
|
+
const apiKey = await new Promise((resolve) => {
|
|
1496
|
+
rl.question('API Key: ', (answer) => {
|
|
1497
|
+
rl.close();
|
|
1498
|
+
resolve(answer.trim());
|
|
1499
|
+
});
|
|
1500
|
+
});
|
|
1501
|
+
if (!apiKey || !apiKey.startsWith('ar_live_')) {
|
|
1502
|
+
console.error('');
|
|
1503
|
+
console.error('Invalid API key format. Expected ar_live_...');
|
|
1504
|
+
process.exit(1);
|
|
1505
|
+
}
|
|
1506
|
+
// Verify the API key works
|
|
1507
|
+
console.log('');
|
|
1508
|
+
console.log('Verifying API key...');
|
|
1509
|
+
try {
|
|
1510
|
+
const response = await fetch(`${cloudUrl}/api/daemons/heartbeat`, {
|
|
1511
|
+
method: 'POST',
|
|
1512
|
+
headers: {
|
|
1513
|
+
'Authorization': `Bearer ${apiKey}`,
|
|
1514
|
+
'Content-Type': 'application/json',
|
|
1515
|
+
},
|
|
1516
|
+
body: JSON.stringify({
|
|
1517
|
+
agents: [],
|
|
1518
|
+
metrics: { linkedAt: new Date().toISOString() },
|
|
1519
|
+
}),
|
|
1520
|
+
});
|
|
1521
|
+
if (!response.ok) {
|
|
1522
|
+
const error = await response.text();
|
|
1523
|
+
console.error(`Failed to verify API key: ${error}`);
|
|
1524
|
+
process.exit(1);
|
|
1525
|
+
}
|
|
1526
|
+
// Save config
|
|
1527
|
+
const config = {
|
|
1528
|
+
apiKey,
|
|
1529
|
+
cloudUrl,
|
|
1530
|
+
machineId,
|
|
1531
|
+
machineName,
|
|
1532
|
+
linkedAt: new Date().toISOString(),
|
|
1533
|
+
};
|
|
1534
|
+
fs.writeFileSync(configPath, JSON.stringify(config, null, 2));
|
|
1535
|
+
fs.chmodSync(configPath, 0o600); // Secure the file
|
|
1536
|
+
// Clean up temp code
|
|
1537
|
+
if (fs.existsSync(tempCodePath)) {
|
|
1538
|
+
fs.unlinkSync(tempCodePath);
|
|
1539
|
+
}
|
|
1540
|
+
console.log('');
|
|
1541
|
+
console.log('✓ Machine linked successfully!');
|
|
1542
|
+
console.log('');
|
|
1543
|
+
console.log('Your daemon will now sync with Agent Relay Cloud.');
|
|
1544
|
+
console.log('Run `agent-relay up` to start with cloud sync enabled.');
|
|
1545
|
+
console.log('');
|
|
1546
|
+
}
|
|
1547
|
+
catch (err) {
|
|
1548
|
+
console.error(`Failed to connect to cloud: ${err.message}`);
|
|
1549
|
+
process.exit(1);
|
|
1550
|
+
}
|
|
1551
|
+
});
|
|
1552
|
+
cloudCommand
|
|
1553
|
+
.command('unlink')
|
|
1554
|
+
.description('Unlink this machine from Agent Relay Cloud')
|
|
1555
|
+
.action(async () => {
|
|
1556
|
+
const os = await import('node:os');
|
|
1557
|
+
const dataDir = process.env.AGENT_RELAY_DATA_DIR ||
|
|
1558
|
+
path.join(os.homedir(), '.local', 'share', 'agent-relay');
|
|
1559
|
+
const configPath = path.join(dataDir, 'cloud-config.json');
|
|
1560
|
+
if (!fs.existsSync(configPath)) {
|
|
1561
|
+
console.log('This machine is not linked to Agent Relay Cloud.');
|
|
1562
|
+
return;
|
|
1563
|
+
}
|
|
1564
|
+
// Read current config
|
|
1565
|
+
const config = JSON.parse(fs.readFileSync(configPath, 'utf-8'));
|
|
1566
|
+
// Delete config file
|
|
1567
|
+
fs.unlinkSync(configPath);
|
|
1568
|
+
console.log('');
|
|
1569
|
+
console.log('✓ Machine unlinked from Agent Relay Cloud');
|
|
1570
|
+
console.log('');
|
|
1571
|
+
console.log(`Machine ID: ${config.machineId}`);
|
|
1572
|
+
console.log(`Was linked since: ${config.linkedAt}`);
|
|
1573
|
+
console.log('');
|
|
1574
|
+
console.log('Note: The API key has been removed locally. To fully revoke access,');
|
|
1575
|
+
console.log('visit your Agent Relay Cloud dashboard and remove this machine.');
|
|
1576
|
+
console.log('');
|
|
1577
|
+
});
|
|
1578
|
+
cloudCommand
|
|
1579
|
+
.command('status')
|
|
1580
|
+
.description('Show cloud sync status')
|
|
1581
|
+
.action(async () => {
|
|
1582
|
+
const os = await import('node:os');
|
|
1583
|
+
const dataDir = process.env.AGENT_RELAY_DATA_DIR ||
|
|
1584
|
+
path.join(os.homedir(), '.local', 'share', 'agent-relay');
|
|
1585
|
+
const configPath = path.join(dataDir, 'cloud-config.json');
|
|
1586
|
+
if (!fs.existsSync(configPath)) {
|
|
1587
|
+
console.log('');
|
|
1588
|
+
console.log('Cloud sync: Not configured');
|
|
1589
|
+
console.log('');
|
|
1590
|
+
console.log('Run `agent-relay cloud link` to connect to Agent Relay Cloud.');
|
|
1591
|
+
console.log('');
|
|
1592
|
+
return;
|
|
1593
|
+
}
|
|
1594
|
+
const config = JSON.parse(fs.readFileSync(configPath, 'utf-8'));
|
|
1595
|
+
console.log('');
|
|
1596
|
+
console.log('Cloud sync: Enabled');
|
|
1597
|
+
console.log('');
|
|
1598
|
+
console.log(` Machine: ${config.machineName}`);
|
|
1599
|
+
console.log(` ID: ${config.machineId}`);
|
|
1600
|
+
console.log(` Cloud URL: ${config.cloudUrl}`);
|
|
1601
|
+
console.log(` Linked: ${new Date(config.linkedAt).toLocaleString()}`);
|
|
1602
|
+
console.log('');
|
|
1603
|
+
// Check if daemon is running and connected
|
|
1604
|
+
const { getProjectPaths } = await import('../utils/project-namespace.js');
|
|
1605
|
+
const paths = getProjectPaths();
|
|
1606
|
+
if (fs.existsSync(paths.socketPath)) {
|
|
1607
|
+
console.log(' Daemon: Running');
|
|
1608
|
+
// Try to get cloud sync status from daemon
|
|
1609
|
+
try {
|
|
1610
|
+
const response = await fetch(`${config.cloudUrl}/api/daemons/heartbeat`, {
|
|
1611
|
+
method: 'POST',
|
|
1612
|
+
headers: {
|
|
1613
|
+
'Authorization': `Bearer ${config.apiKey}`,
|
|
1614
|
+
'Content-Type': 'application/json',
|
|
1615
|
+
},
|
|
1616
|
+
body: JSON.stringify({ agents: [], metrics: {} }),
|
|
1617
|
+
});
|
|
1618
|
+
if (response.ok) {
|
|
1619
|
+
console.log(' Cloud connection: Online');
|
|
1620
|
+
}
|
|
1621
|
+
else {
|
|
1622
|
+
console.log(' Cloud connection: Error (API key may be invalid)');
|
|
1623
|
+
}
|
|
1624
|
+
}
|
|
1625
|
+
catch (err) {
|
|
1626
|
+
console.log(` Cloud connection: Offline (${err.message})`);
|
|
1627
|
+
}
|
|
1628
|
+
}
|
|
1629
|
+
else {
|
|
1630
|
+
console.log(' Daemon: Not running');
|
|
1631
|
+
console.log(' Cloud connection: Offline (daemon not started)');
|
|
1632
|
+
}
|
|
1633
|
+
console.log('');
|
|
1634
|
+
});
|
|
1635
|
+
cloudCommand
|
|
1636
|
+
.command('sync')
|
|
1637
|
+
.description('Manually sync credentials from cloud')
|
|
1638
|
+
.action(async () => {
|
|
1639
|
+
const os = await import('node:os');
|
|
1640
|
+
const dataDir = process.env.AGENT_RELAY_DATA_DIR ||
|
|
1641
|
+
path.join(os.homedir(), '.local', 'share', 'agent-relay');
|
|
1642
|
+
const configPath = path.join(dataDir, 'cloud-config.json');
|
|
1643
|
+
if (!fs.existsSync(configPath)) {
|
|
1644
|
+
console.error('Not linked to cloud. Run `agent-relay cloud link` first.');
|
|
1645
|
+
process.exit(1);
|
|
1646
|
+
}
|
|
1647
|
+
const config = JSON.parse(fs.readFileSync(configPath, 'utf-8'));
|
|
1648
|
+
console.log('Syncing credentials from cloud...');
|
|
1649
|
+
try {
|
|
1650
|
+
const response = await fetch(`${config.cloudUrl}/api/daemons/credentials`, {
|
|
1651
|
+
headers: {
|
|
1652
|
+
'Authorization': `Bearer ${config.apiKey}`,
|
|
1653
|
+
},
|
|
1654
|
+
});
|
|
1655
|
+
if (!response.ok) {
|
|
1656
|
+
const error = await response.text();
|
|
1657
|
+
console.error(`Failed to sync: ${error}`);
|
|
1658
|
+
process.exit(1);
|
|
1659
|
+
}
|
|
1660
|
+
const data = await response.json();
|
|
1661
|
+
console.log('');
|
|
1662
|
+
console.log(`Synced ${data.credentials.length} provider credentials:`);
|
|
1663
|
+
for (const cred of data.credentials) {
|
|
1664
|
+
console.log(` - ${cred.provider}`);
|
|
1665
|
+
}
|
|
1666
|
+
// Save credentials locally for daemon to use
|
|
1667
|
+
const credentialsPath = path.join(dataDir, 'cloud-credentials.json');
|
|
1668
|
+
fs.writeFileSync(credentialsPath, JSON.stringify(data.credentials, null, 2));
|
|
1669
|
+
fs.chmodSync(credentialsPath, 0o600);
|
|
1670
|
+
console.log('');
|
|
1671
|
+
console.log('✓ Credentials synced successfully');
|
|
1672
|
+
console.log('');
|
|
1673
|
+
}
|
|
1674
|
+
catch (err) {
|
|
1675
|
+
console.error(`Failed to sync: ${err.message}`);
|
|
1676
|
+
process.exit(1);
|
|
1677
|
+
}
|
|
1678
|
+
});
|
|
1679
|
+
// ============================================================================
|
|
1680
|
+
// TRAJECTORY COMMANDS (trail proxy)
|
|
1681
|
+
// ============================================================================
|
|
1682
|
+
// trail - Proxy to trail CLI for trajectory tracking
|
|
1683
|
+
program
|
|
1684
|
+
.command('trail')
|
|
1685
|
+
.description('Trajectory tracking commands (proxies to trail CLI)')
|
|
1686
|
+
.argument('[args...]', 'Arguments to pass to trail CLI')
|
|
1687
|
+
.allowUnknownOption()
|
|
1688
|
+
.action(async (args) => {
|
|
1689
|
+
const { spawn } = await import('node:child_process');
|
|
1690
|
+
const { getProjectPaths } = await import('../utils/project-namespace.js');
|
|
1691
|
+
const paths = getProjectPaths();
|
|
1692
|
+
// Check if trail is available
|
|
1693
|
+
const trailCheck = spawn('which', ['trail'], { stdio: 'pipe' });
|
|
1694
|
+
const trailExists = await new Promise((resolve) => {
|
|
1695
|
+
trailCheck.on('close', (code) => resolve(code === 0));
|
|
1696
|
+
trailCheck.on('error', () => resolve(false));
|
|
1697
|
+
});
|
|
1698
|
+
if (!trailExists) {
|
|
1699
|
+
console.error('trail CLI not found. Install with: npm install -g agent-trajectories');
|
|
1700
|
+
console.log('');
|
|
1701
|
+
console.log('The trail CLI provides trajectory tracking for agent work:');
|
|
1702
|
+
console.log(' trail start "<task>" Start tracking a new trajectory');
|
|
1703
|
+
console.log(' trail status Show current trajectory status');
|
|
1704
|
+
console.log(' trail phase <phase> Transition to PDERO phase');
|
|
1705
|
+
console.log(' trail decision "<choice>" Record a decision');
|
|
1706
|
+
console.log(' trail complete Complete the trajectory');
|
|
1707
|
+
console.log(' trail list List all trajectories');
|
|
1708
|
+
console.log('');
|
|
1709
|
+
console.log('PDERO phases: plan, design, execute, review, observe');
|
|
1710
|
+
process.exit(1);
|
|
1711
|
+
}
|
|
1712
|
+
// Spawn trail with the provided arguments
|
|
1713
|
+
const trailProc = spawn('trail', args, {
|
|
1714
|
+
cwd: paths.projectRoot,
|
|
1715
|
+
stdio: 'inherit',
|
|
1716
|
+
env: {
|
|
1717
|
+
...process.env,
|
|
1718
|
+
TRAJECTORIES_PROJECT: paths.projectId,
|
|
1719
|
+
TRAJECTORIES_DATA_DIR: paths.dataDir,
|
|
1720
|
+
},
|
|
1721
|
+
});
|
|
1722
|
+
trailProc.on('close', (code) => {
|
|
1723
|
+
process.exit(code ?? 0);
|
|
1724
|
+
});
|
|
1725
|
+
trailProc.on('error', (err) => {
|
|
1726
|
+
console.error(`Failed to run trail: ${err.message}`);
|
|
1727
|
+
process.exit(1);
|
|
1728
|
+
});
|
|
1729
|
+
});
|
|
1730
|
+
cloudCommand
|
|
1731
|
+
.command('agents')
|
|
1732
|
+
.description('List agents across all linked machines')
|
|
1733
|
+
.option('--json', 'Output as JSON')
|
|
1734
|
+
.action(async (options) => {
|
|
1735
|
+
const os = await import('node:os');
|
|
1736
|
+
const dataDir = process.env.AGENT_RELAY_DATA_DIR ||
|
|
1737
|
+
path.join(os.homedir(), '.local', 'share', 'agent-relay');
|
|
1738
|
+
const configPath = path.join(dataDir, 'cloud-config.json');
|
|
1739
|
+
if (!fs.existsSync(configPath)) {
|
|
1740
|
+
console.error('Not linked to cloud. Run `agent-relay cloud link` first.');
|
|
1741
|
+
process.exit(1);
|
|
1742
|
+
}
|
|
1743
|
+
const config = JSON.parse(fs.readFileSync(configPath, 'utf-8'));
|
|
1744
|
+
try {
|
|
1745
|
+
// Get agents from cloud
|
|
1746
|
+
const response = await fetch(`${config.cloudUrl}/api/daemons/agents`, {
|
|
1747
|
+
method: 'POST',
|
|
1748
|
+
headers: {
|
|
1749
|
+
'Authorization': `Bearer ${config.apiKey}`,
|
|
1750
|
+
'Content-Type': 'application/json',
|
|
1751
|
+
},
|
|
1752
|
+
body: JSON.stringify({ agents: [] }), // Report no agents, just fetch list
|
|
1753
|
+
});
|
|
1754
|
+
if (!response.ok) {
|
|
1755
|
+
const error = await response.text();
|
|
1756
|
+
console.error(`Failed to fetch agents: ${error}`);
|
|
1757
|
+
process.exit(1);
|
|
1758
|
+
}
|
|
1759
|
+
const data = await response.json();
|
|
1760
|
+
if (options.json) {
|
|
1761
|
+
console.log(JSON.stringify(data.allAgents, null, 2));
|
|
1762
|
+
return;
|
|
1763
|
+
}
|
|
1764
|
+
if (!data.allAgents.length) {
|
|
1765
|
+
console.log('No agents found across linked machines.');
|
|
1766
|
+
console.log('Make sure daemons are running on linked machines.');
|
|
1767
|
+
return;
|
|
1768
|
+
}
|
|
1769
|
+
console.log('');
|
|
1770
|
+
console.log('Agents across all linked machines:');
|
|
1771
|
+
console.log('');
|
|
1772
|
+
console.log('NAME STATUS DAEMON MACHINE');
|
|
1773
|
+
console.log('─'.repeat(65));
|
|
1774
|
+
// Group by daemon
|
|
1775
|
+
const byDaemon = new Map();
|
|
1776
|
+
for (const agent of data.allAgents) {
|
|
1777
|
+
const existing = byDaemon.get(agent.daemonName) || [];
|
|
1778
|
+
existing.push(agent);
|
|
1779
|
+
byDaemon.set(agent.daemonName, existing);
|
|
1780
|
+
}
|
|
1781
|
+
for (const [daemonName, agents] of byDaemon.entries()) {
|
|
1782
|
+
for (const agent of agents) {
|
|
1783
|
+
const name = agent.name.padEnd(15);
|
|
1784
|
+
const status = agent.status.padEnd(8);
|
|
1785
|
+
const daemon = daemonName.padEnd(18);
|
|
1786
|
+
const machine = agent.machineId.substring(0, 20);
|
|
1787
|
+
console.log(`${name} ${status} ${daemon} ${machine}`);
|
|
1788
|
+
}
|
|
1789
|
+
}
|
|
1790
|
+
console.log('');
|
|
1791
|
+
console.log(`Total: ${data.allAgents.length} agents on ${byDaemon.size} machines`);
|
|
1792
|
+
console.log('');
|
|
1793
|
+
}
|
|
1794
|
+
catch (err) {
|
|
1795
|
+
console.error(`Failed to fetch agents: ${err.message}`);
|
|
1796
|
+
process.exit(1);
|
|
1797
|
+
}
|
|
1798
|
+
});
|
|
1799
|
+
cloudCommand
|
|
1800
|
+
.command('send')
|
|
1801
|
+
.description('Send a message to an agent on any linked machine')
|
|
1802
|
+
.argument('<agent>', 'Target agent name')
|
|
1803
|
+
.argument('<message>', 'Message to send')
|
|
1804
|
+
.option('--from <name>', 'Sender name', 'cli')
|
|
1805
|
+
.action(async (agent, message, options) => {
|
|
1806
|
+
const os = await import('node:os');
|
|
1807
|
+
const dataDir = process.env.AGENT_RELAY_DATA_DIR ||
|
|
1808
|
+
path.join(os.homedir(), '.local', 'share', 'agent-relay');
|
|
1809
|
+
const configPath = path.join(dataDir, 'cloud-config.json');
|
|
1810
|
+
if (!fs.existsSync(configPath)) {
|
|
1811
|
+
console.error('Not linked to cloud. Run `agent-relay cloud link` first.');
|
|
1812
|
+
process.exit(1);
|
|
1813
|
+
}
|
|
1814
|
+
const config = JSON.parse(fs.readFileSync(configPath, 'utf-8'));
|
|
1815
|
+
console.log(`Sending message to ${agent}...`);
|
|
1816
|
+
try {
|
|
1817
|
+
// First, find which daemon the agent is on
|
|
1818
|
+
const agentsResponse = await fetch(`${config.cloudUrl}/api/daemons/agents`, {
|
|
1819
|
+
method: 'POST',
|
|
1820
|
+
headers: {
|
|
1821
|
+
'Authorization': `Bearer ${config.apiKey}`,
|
|
1822
|
+
'Content-Type': 'application/json',
|
|
1823
|
+
},
|
|
1824
|
+
body: JSON.stringify({ agents: [] }),
|
|
1825
|
+
});
|
|
1826
|
+
if (!agentsResponse.ok) {
|
|
1827
|
+
const error = await agentsResponse.text();
|
|
1828
|
+
console.error(`Failed to find agent: ${error}`);
|
|
1829
|
+
process.exit(1);
|
|
1830
|
+
}
|
|
1831
|
+
const agentsData = await agentsResponse.json();
|
|
1832
|
+
const targetAgent = agentsData.allAgents.find(a => a.name === agent);
|
|
1833
|
+
if (!targetAgent) {
|
|
1834
|
+
console.error(`Agent "${agent}" not found.`);
|
|
1835
|
+
console.log('Available agents:');
|
|
1836
|
+
for (const a of agentsData.allAgents) {
|
|
1837
|
+
console.log(` - ${a.name} (on ${a.daemonName})`);
|
|
1838
|
+
}
|
|
1839
|
+
process.exit(1);
|
|
1840
|
+
}
|
|
1841
|
+
// Send the message
|
|
1842
|
+
const sendResponse = await fetch(`${config.cloudUrl}/api/daemons/message`, {
|
|
1843
|
+
method: 'POST',
|
|
1844
|
+
headers: {
|
|
1845
|
+
'Authorization': `Bearer ${config.apiKey}`,
|
|
1846
|
+
'Content-Type': 'application/json',
|
|
1847
|
+
},
|
|
1848
|
+
body: JSON.stringify({
|
|
1849
|
+
targetDaemonId: targetAgent.daemonId,
|
|
1850
|
+
targetAgent: agent,
|
|
1851
|
+
message: {
|
|
1852
|
+
from: options.from,
|
|
1853
|
+
content: message,
|
|
1854
|
+
},
|
|
1855
|
+
}),
|
|
1856
|
+
});
|
|
1857
|
+
if (!sendResponse.ok) {
|
|
1858
|
+
const error = await sendResponse.text();
|
|
1859
|
+
console.error(`Failed to send message: ${error}`);
|
|
1860
|
+
process.exit(1);
|
|
1861
|
+
}
|
|
1862
|
+
console.log('');
|
|
1863
|
+
console.log(`✓ Message sent to ${agent} on ${targetAgent.daemonName}`);
|
|
1864
|
+
console.log('');
|
|
1865
|
+
}
|
|
1866
|
+
catch (err) {
|
|
1867
|
+
console.error(`Failed to send message: ${err.message}`);
|
|
1868
|
+
process.exit(1);
|
|
1869
|
+
}
|
|
1870
|
+
});
|
|
1871
|
+
cloudCommand
|
|
1872
|
+
.command('daemons')
|
|
1873
|
+
.description('List all linked daemon instances')
|
|
1874
|
+
.option('--json', 'Output as JSON')
|
|
1875
|
+
.action(async (_options) => {
|
|
1876
|
+
const os = await import('node:os');
|
|
1877
|
+
const dataDir = process.env.AGENT_RELAY_DATA_DIR ||
|
|
1878
|
+
path.join(os.homedir(), '.local', 'share', 'agent-relay');
|
|
1879
|
+
const configPath = path.join(dataDir, 'cloud-config.json');
|
|
1880
|
+
if (!fs.existsSync(configPath)) {
|
|
1881
|
+
console.error('Not linked to cloud. Run `agent-relay cloud link` first.');
|
|
1882
|
+
process.exit(1);
|
|
1883
|
+
}
|
|
1884
|
+
const config = JSON.parse(fs.readFileSync(configPath, 'utf-8'));
|
|
1885
|
+
try {
|
|
1886
|
+
// Get daemons list (requires browser auth, so we use a workaround)
|
|
1887
|
+
// For now, just show what we know about our own daemon
|
|
1888
|
+
console.log('');
|
|
1889
|
+
console.log('Linked Daemon:');
|
|
1890
|
+
console.log('');
|
|
1891
|
+
console.log(` Machine: ${config.machineName}`);
|
|
1892
|
+
console.log(` ID: ${config.machineId}`);
|
|
1893
|
+
console.log(` Cloud: ${config.cloudUrl}`);
|
|
1894
|
+
console.log(` Linked: ${new Date(config.linkedAt).toLocaleString()}`);
|
|
1895
|
+
console.log('');
|
|
1896
|
+
console.log('Note: To see all linked daemons, visit your cloud dashboard.');
|
|
1897
|
+
console.log('');
|
|
1898
|
+
}
|
|
1899
|
+
catch (err) {
|
|
1900
|
+
console.error(`Failed: ${err.message}`);
|
|
1901
|
+
process.exit(1);
|
|
1902
|
+
}
|
|
1903
|
+
});
|
|
1904
|
+
// ============================================================================
|
|
1905
|
+
// Monitoring commands (metrics, health, profiler)
|
|
1906
|
+
// ============================================================================
|
|
1907
|
+
// metrics - Show agent memory metrics
|
|
1908
|
+
program
|
|
1909
|
+
.command('metrics')
|
|
1910
|
+
.description('Show agent memory metrics and resource usage')
|
|
1911
|
+
.option('--agent <name>', 'Show metrics for specific agent')
|
|
1912
|
+
.option('--port <port>', 'Dashboard port', DEFAULT_DASHBOARD_PORT)
|
|
1913
|
+
.option('--json', 'Output as JSON')
|
|
1914
|
+
.option('--watch', 'Continuously update metrics')
|
|
1915
|
+
.option('--interval <ms>', 'Update interval for watch mode', '5000')
|
|
1916
|
+
.action(async (options) => {
|
|
1917
|
+
const port = options.port || DEFAULT_DASHBOARD_PORT;
|
|
1918
|
+
const fetchMetrics = async () => {
|
|
1919
|
+
try {
|
|
1920
|
+
const response = await fetch(`http://localhost:${port}/api/metrics/agents`);
|
|
1921
|
+
if (!response.ok) {
|
|
1922
|
+
throw new Error(`HTTP ${response.status}`);
|
|
1923
|
+
}
|
|
1924
|
+
return await response.json();
|
|
1925
|
+
}
|
|
1926
|
+
catch (err) {
|
|
1927
|
+
if (err.code === 'ECONNREFUSED') {
|
|
1928
|
+
console.error(`Cannot connect to dashboard at port ${port}. Is the daemon running?`);
|
|
1929
|
+
console.log(`Run 'agent-relay up' to start the daemon.`);
|
|
1930
|
+
}
|
|
1931
|
+
else {
|
|
1932
|
+
console.error(`Failed to fetch metrics: ${err.message}`);
|
|
1933
|
+
}
|
|
1934
|
+
process.exit(1);
|
|
1935
|
+
}
|
|
1936
|
+
};
|
|
1937
|
+
const formatBytes = (bytes) => {
|
|
1938
|
+
if (bytes === 0)
|
|
1939
|
+
return '0 B';
|
|
1940
|
+
const k = 1024;
|
|
1941
|
+
const sizes = ['B', 'KB', 'MB', 'GB'];
|
|
1942
|
+
const i = Math.floor(Math.log(Math.abs(bytes)) / Math.log(k));
|
|
1943
|
+
return `${(bytes / Math.pow(k, i)).toFixed(1)} ${sizes[i]}`;
|
|
1944
|
+
};
|
|
1945
|
+
const formatUptime = (ms) => {
|
|
1946
|
+
if (ms < 60000)
|
|
1947
|
+
return `${Math.floor(ms / 1000)}s`;
|
|
1948
|
+
if (ms < 3600000)
|
|
1949
|
+
return `${Math.floor(ms / 60000)}m`;
|
|
1950
|
+
return `${Math.floor(ms / 3600000)}h ${Math.floor((ms % 3600000) / 60000)}m`;
|
|
1951
|
+
};
|
|
1952
|
+
const displayMetrics = (data) => {
|
|
1953
|
+
let agents = data.agents;
|
|
1954
|
+
if (options.agent) {
|
|
1955
|
+
agents = agents.filter(a => a.name === options.agent);
|
|
1956
|
+
if (agents.length === 0) {
|
|
1957
|
+
console.error(`Agent "${options.agent}" not found`);
|
|
1958
|
+
return;
|
|
1959
|
+
}
|
|
1960
|
+
}
|
|
1961
|
+
if (options.json) {
|
|
1962
|
+
console.log(JSON.stringify({ agents, system: data.system }, null, 2));
|
|
1963
|
+
return;
|
|
1964
|
+
}
|
|
1965
|
+
if (options.watch) {
|
|
1966
|
+
// Clear screen for watch mode
|
|
1967
|
+
console.clear();
|
|
1968
|
+
console.log(`Agent Metrics (updating every ${options.interval}ms) [Ctrl+C to stop]`);
|
|
1969
|
+
console.log(`System: ${formatBytes(data.system.heapUsed)} heap / ${formatBytes(data.system.freeMemory)} free`);
|
|
1970
|
+
console.log('');
|
|
1971
|
+
}
|
|
1972
|
+
if (agents.length === 0) {
|
|
1973
|
+
console.log('No agents with memory metrics.');
|
|
1974
|
+
console.log('Ensure agents are running and memory monitoring is enabled.');
|
|
1975
|
+
return;
|
|
1976
|
+
}
|
|
1977
|
+
console.log('AGENT PID MEMORY CPU TREND ALERT UPTIME');
|
|
1978
|
+
console.log('─'.repeat(75));
|
|
1979
|
+
for (const agent of agents) {
|
|
1980
|
+
const name = agent.name.padEnd(15);
|
|
1981
|
+
const pid = (agent.pid?.toString() || '-').padEnd(8);
|
|
1982
|
+
const memory = formatBytes(agent.rssBytes || 0).padEnd(11);
|
|
1983
|
+
const cpu = ((agent.cpuPercent?.toFixed(1) || '0') + '%').padEnd(6);
|
|
1984
|
+
const trend = (agent.trend || 'unknown').padEnd(11);
|
|
1985
|
+
const alertColors = {
|
|
1986
|
+
normal: 'normal',
|
|
1987
|
+
warning: '\x1b[33mwarning\x1b[0m',
|
|
1988
|
+
critical: '\x1b[31mcritical\x1b[0m',
|
|
1989
|
+
oom_imminent: '\x1b[31;1mOOM!\x1b[0m',
|
|
1990
|
+
};
|
|
1991
|
+
const alert = (alertColors[agent.alertLevel || 'normal'] || agent.alertLevel || '-').padEnd(9);
|
|
1992
|
+
const uptime = formatUptime(agent.uptimeMs || 0);
|
|
1993
|
+
console.log(`${name} ${pid} ${memory} ${cpu} ${trend} ${alert} ${uptime}`);
|
|
1994
|
+
}
|
|
1995
|
+
if (!options.watch) {
|
|
1996
|
+
console.log('');
|
|
1997
|
+
console.log(`Total: ${agents.length} agent(s)`);
|
|
1998
|
+
if (agents.some(a => a.alertLevel && a.alertLevel !== 'normal')) {
|
|
1999
|
+
console.log('');
|
|
2000
|
+
console.log('⚠️ Some agents have elevated memory usage. Run `agent-relay health` for details.');
|
|
2001
|
+
}
|
|
2002
|
+
}
|
|
2003
|
+
};
|
|
2004
|
+
if (options.watch) {
|
|
2005
|
+
const interval = parseInt(options.interval || '5000', 10);
|
|
2006
|
+
const update = async () => {
|
|
2007
|
+
try {
|
|
2008
|
+
const data = await fetchMetrics();
|
|
2009
|
+
displayMetrics(data);
|
|
2010
|
+
}
|
|
2011
|
+
catch {
|
|
2012
|
+
// Error already logged in fetchMetrics
|
|
2013
|
+
}
|
|
2014
|
+
};
|
|
2015
|
+
process.on('SIGINT', () => {
|
|
2016
|
+
console.log('\nStopped watching metrics.');
|
|
2017
|
+
process.exit(0);
|
|
2018
|
+
});
|
|
2019
|
+
await update();
|
|
2020
|
+
setInterval(update, interval);
|
|
2021
|
+
}
|
|
2022
|
+
else {
|
|
2023
|
+
const data = await fetchMetrics();
|
|
2024
|
+
displayMetrics(data);
|
|
2025
|
+
}
|
|
2026
|
+
});
|
|
2027
|
+
// health - Show crash insights and system health
|
|
2028
|
+
program
|
|
2029
|
+
.command('health')
|
|
2030
|
+
.description('Show system health, crash insights, and recommendations')
|
|
2031
|
+
.option('--port <port>', 'Dashboard port', DEFAULT_DASHBOARD_PORT)
|
|
2032
|
+
.option('--json', 'Output as JSON')
|
|
2033
|
+
.option('--crashes', 'Show recent crash history')
|
|
2034
|
+
.option('--alerts', 'Show unacknowledged alerts')
|
|
2035
|
+
.action(async (options) => {
|
|
2036
|
+
const port = options.port || DEFAULT_DASHBOARD_PORT;
|
|
2037
|
+
try {
|
|
2038
|
+
const response = await fetch(`http://localhost:${port}/api/metrics/health`);
|
|
2039
|
+
if (!response.ok) {
|
|
2040
|
+
throw new Error(`HTTP ${response.status}`);
|
|
2041
|
+
}
|
|
2042
|
+
const data = await response.json();
|
|
2043
|
+
if (options.json) {
|
|
2044
|
+
console.log(JSON.stringify(data, null, 2));
|
|
2045
|
+
return;
|
|
2046
|
+
}
|
|
2047
|
+
// Health score with color
|
|
2048
|
+
const scoreColor = data.healthScore >= 80 ? '\x1b[32m' : // Green
|
|
2049
|
+
data.healthScore >= 50 ? '\x1b[33m' : // Yellow
|
|
2050
|
+
'\x1b[31m'; // Red
|
|
2051
|
+
const resetColor = '\x1b[0m';
|
|
2052
|
+
console.log('');
|
|
2053
|
+
console.log('═══════════════════════════════════════════════════════════════');
|
|
2054
|
+
console.log(` SYSTEM HEALTH: ${scoreColor}${data.healthScore}/100${resetColor}`);
|
|
2055
|
+
console.log('═══════════════════════════════════════════════════════════════');
|
|
2056
|
+
console.log('');
|
|
2057
|
+
console.log(` ${data.summary}`);
|
|
2058
|
+
console.log('');
|
|
2059
|
+
// Show stats
|
|
2060
|
+
console.log(` Agents: ${data.stats.agentCount}`);
|
|
2061
|
+
console.log(` Crashes (24h): ${data.stats.totalCrashes24h}`);
|
|
2062
|
+
console.log(` Alerts (24h): ${data.stats.totalAlerts24h}`);
|
|
2063
|
+
console.log('');
|
|
2064
|
+
// Show issues
|
|
2065
|
+
if (data.issues.length > 0) {
|
|
2066
|
+
console.log(' ISSUES:');
|
|
2067
|
+
for (const issue of data.issues) {
|
|
2068
|
+
const icon = issue.severity === 'critical' ? '🔴' :
|
|
2069
|
+
issue.severity === 'high' ? '🟠' :
|
|
2070
|
+
issue.severity === 'medium' ? '🟡' : '🔵';
|
|
2071
|
+
console.log(` ${icon} ${issue.message}`);
|
|
2072
|
+
}
|
|
2073
|
+
console.log('');
|
|
2074
|
+
}
|
|
2075
|
+
// Show recommendations
|
|
2076
|
+
if (data.recommendations.length > 0) {
|
|
2077
|
+
console.log(' RECOMMENDATIONS:');
|
|
2078
|
+
for (const rec of data.recommendations) {
|
|
2079
|
+
console.log(` → ${rec}`);
|
|
2080
|
+
}
|
|
2081
|
+
console.log('');
|
|
2082
|
+
}
|
|
2083
|
+
// Show crashes if requested
|
|
2084
|
+
if (options.crashes && data.crashes.length > 0) {
|
|
2085
|
+
console.log(' RECENT CRASHES:');
|
|
2086
|
+
console.log(' ─────────────────────────────────────────────────────────────');
|
|
2087
|
+
for (const crash of data.crashes.slice(0, 10)) {
|
|
2088
|
+
const time = new Date(crash.crashedAt).toLocaleString();
|
|
2089
|
+
console.log(` ${crash.agentName} - ${time}`);
|
|
2090
|
+
console.log(` Cause: ${crash.likelyCause} | ${crash.summary.slice(0, 60)}...`);
|
|
2091
|
+
}
|
|
2092
|
+
console.log('');
|
|
2093
|
+
}
|
|
2094
|
+
// Show alerts if requested
|
|
2095
|
+
if (options.alerts && data.alerts.length > 0) {
|
|
2096
|
+
console.log(' UNACKNOWLEDGED ALERTS:');
|
|
2097
|
+
console.log(' ─────────────────────────────────────────────────────────────');
|
|
2098
|
+
for (const alert of data.alerts.slice(0, 10)) {
|
|
2099
|
+
const _time = new Date(alert.createdAt).toLocaleString();
|
|
2100
|
+
const icon = alert.alertType === 'oom_imminent' ? '🔴' :
|
|
2101
|
+
alert.alertType === 'critical' ? '🟠' : '🟡';
|
|
2102
|
+
console.log(` ${icon} ${alert.agentName} - ${alert.alertType}`);
|
|
2103
|
+
console.log(` ${alert.message}`);
|
|
2104
|
+
}
|
|
2105
|
+
console.log('');
|
|
2106
|
+
}
|
|
2107
|
+
console.log('═══════════════════════════════════════════════════════════════');
|
|
2108
|
+
console.log('');
|
|
2109
|
+
if (!options.crashes && data.stats.totalCrashes24h > 0) {
|
|
2110
|
+
console.log(' Tip: Run `agent-relay health --crashes` to see crash details');
|
|
2111
|
+
}
|
|
2112
|
+
if (!options.alerts && data.stats.totalAlerts24h > 0) {
|
|
2113
|
+
console.log(' Tip: Run `agent-relay health --alerts` to see alerts');
|
|
2114
|
+
}
|
|
2115
|
+
console.log('');
|
|
2116
|
+
}
|
|
2117
|
+
catch (err) {
|
|
2118
|
+
if (err.code === 'ECONNREFUSED') {
|
|
2119
|
+
console.error(`Cannot connect to dashboard at port ${port}. Is the daemon running?`);
|
|
2120
|
+
console.log(`Run 'agent-relay up' to start the daemon.`);
|
|
2121
|
+
}
|
|
2122
|
+
else {
|
|
2123
|
+
console.error(`Failed to fetch health data: ${err.message}`);
|
|
2124
|
+
}
|
|
2125
|
+
process.exit(1);
|
|
2126
|
+
}
|
|
2127
|
+
});
|
|
2128
|
+
// profile - Run agent with profiling enabled
|
|
2129
|
+
program
|
|
2130
|
+
.command('profile')
|
|
2131
|
+
.description('Run an agent with memory profiling enabled')
|
|
2132
|
+
.argument('<command...>', 'Command to profile')
|
|
2133
|
+
.option('-n, --name <name>', 'Agent name')
|
|
2134
|
+
.option('--heap-snapshot-interval <ms>', 'Take heap snapshots at interval (ms)', '60000')
|
|
2135
|
+
.option('--output-dir <dir>', 'Directory for profile output', './profiles')
|
|
2136
|
+
.option('--expose-gc', 'Expose garbage collector for manual GC')
|
|
2137
|
+
.action(async (commandParts, options) => {
|
|
2138
|
+
const { getProjectPaths } = await import('../utils/project-namespace.js');
|
|
2139
|
+
if (!commandParts || commandParts.length === 0) {
|
|
2140
|
+
console.error('No command specified');
|
|
2141
|
+
process.exit(1);
|
|
2142
|
+
}
|
|
2143
|
+
const [cmd, ...args] = commandParts;
|
|
2144
|
+
const agentName = options.name ?? generateAgentName();
|
|
2145
|
+
const outputDir = options.outputDir || './profiles';
|
|
2146
|
+
const snapshotInterval = parseInt(options.heapSnapshotInterval || '60000', 10);
|
|
2147
|
+
// Create output directory
|
|
2148
|
+
if (!fs.existsSync(outputDir)) {
|
|
2149
|
+
fs.mkdirSync(outputDir, { recursive: true });
|
|
2150
|
+
}
|
|
2151
|
+
console.log('');
|
|
2152
|
+
console.log('🔬 Agent Relay Profiler');
|
|
2153
|
+
console.log('');
|
|
2154
|
+
console.log(` Agent: ${agentName}`);
|
|
2155
|
+
console.log(` Command: ${cmd} ${args.join(' ')}`);
|
|
2156
|
+
console.log(` Output: ${outputDir}`);
|
|
2157
|
+
console.log(` Heap snapshots: every ${snapshotInterval}ms`);
|
|
2158
|
+
console.log('');
|
|
2159
|
+
// Build Node.js flags for profiling
|
|
2160
|
+
const nodeFlags = [
|
|
2161
|
+
'--inspect', // Enable inspector
|
|
2162
|
+
'--inspect-brk=0', // Don't actually break, just enable
|
|
2163
|
+
];
|
|
2164
|
+
if (options.exposeGc) {
|
|
2165
|
+
nodeFlags.push('--expose-gc');
|
|
2166
|
+
}
|
|
2167
|
+
// Set environment variables for profiling
|
|
2168
|
+
const profileEnv = {
|
|
2169
|
+
...process.env,
|
|
2170
|
+
NODE_OPTIONS: `${process.env.NODE_OPTIONS || ''} ${nodeFlags.join(' ')}`.trim(),
|
|
2171
|
+
AGENT_RELAY_PROFILE_ENABLED: '1',
|
|
2172
|
+
AGENT_RELAY_PROFILE_OUTPUT: outputDir,
|
|
2173
|
+
AGENT_RELAY_PROFILE_INTERVAL: snapshotInterval.toString(),
|
|
2174
|
+
};
|
|
2175
|
+
console.log('Starting profiled agent...');
|
|
2176
|
+
console.log('');
|
|
2177
|
+
// Use the regular wrapper but with profiling environment
|
|
2178
|
+
const paths = getProjectPaths();
|
|
2179
|
+
const { TmuxWrapper } = await import('../wrapper/tmux-wrapper.js');
|
|
2180
|
+
const wrapper = new TmuxWrapper({
|
|
2181
|
+
name: agentName,
|
|
2182
|
+
command: cmd,
|
|
2183
|
+
args,
|
|
2184
|
+
socketPath: paths.socketPath,
|
|
2185
|
+
debug: true,
|
|
2186
|
+
env: profileEnv,
|
|
2187
|
+
useInbox: true,
|
|
2188
|
+
inboxDir: paths.dataDir,
|
|
2189
|
+
});
|
|
2190
|
+
// Start memory sampling
|
|
2191
|
+
const sampleInterval = setInterval(() => {
|
|
2192
|
+
const memUsage = process.memoryUsage();
|
|
2193
|
+
const timestamp = new Date().toISOString();
|
|
2194
|
+
const sample = {
|
|
2195
|
+
timestamp,
|
|
2196
|
+
heapUsed: memUsage.heapUsed,
|
|
2197
|
+
heapTotal: memUsage.heapTotal,
|
|
2198
|
+
external: memUsage.external,
|
|
2199
|
+
rss: memUsage.rss,
|
|
2200
|
+
};
|
|
2201
|
+
// Append to samples file
|
|
2202
|
+
const samplesFile = path.join(outputDir, `${agentName}-memory.jsonl`);
|
|
2203
|
+
fs.appendFileSync(samplesFile, JSON.stringify(sample) + '\n');
|
|
2204
|
+
}, 5000);
|
|
2205
|
+
process.on('SIGINT', async () => {
|
|
2206
|
+
clearInterval(sampleInterval);
|
|
2207
|
+
console.log('\n');
|
|
2208
|
+
console.log('Profiling stopped.');
|
|
2209
|
+
console.log('');
|
|
2210
|
+
console.log(`Profile data saved to: ${outputDir}/`);
|
|
2211
|
+
console.log(` - ${agentName}-memory.jsonl (memory samples)`);
|
|
2212
|
+
console.log('');
|
|
2213
|
+
console.log('To analyze:');
|
|
2214
|
+
console.log(` 1. Open chrome://inspect in Chrome`);
|
|
2215
|
+
console.log(` 2. Load CPU/heap profiles from ${outputDir}/`);
|
|
2216
|
+
console.log('');
|
|
2217
|
+
wrapper.stop();
|
|
2218
|
+
process.exit(0);
|
|
2219
|
+
});
|
|
2220
|
+
await wrapper.start();
|
|
2221
|
+
console.log(`Profiling ${agentName}... Press Ctrl+C to stop.`);
|
|
2222
|
+
});
|
|
1042
2223
|
program.parse();
|
|
1043
2224
|
//# sourceMappingURL=index.js.map
|