@desplega.ai/agent-swarm 1.49.0 → 1.52.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/openapi.json +2070 -728
- package/package.json +10 -1
- package/src/agentmail/handlers.ts +65 -10
- package/src/agentmail/templates.ts +111 -0
- package/src/be/db.ts +1233 -7
- package/src/be/migrations/014_prompt_templates.sql +33 -0
- package/src/be/migrations/015_workflow_workspace.sql +3 -0
- package/src/be/migrations/016_active_session_runner_session.sql +4 -0
- package/src/be/migrations/017_channel_activity_cursors.sql +6 -0
- package/src/be/migrations/018_fix_seed_double_version.sql +30 -0
- package/src/be/migrations/019_skills.sql +65 -0
- package/src/be/migrations/020_approval_requests.sql +41 -0
- package/src/be/seed.ts +62 -0
- package/src/be/skill-parser.ts +70 -0
- package/src/be/skill-sync.ts +106 -0
- package/src/commands/runner.ts +320 -132
- package/src/commands/templates.ts +172 -0
- package/src/github/handlers.ts +292 -77
- package/src/github/mentions-aliases.test.ts +73 -0
- package/src/github/mentions.test.ts +3 -3
- package/src/github/mentions.ts +32 -6
- package/src/github/templates.ts +398 -0
- package/src/gitlab/handlers.ts +63 -22
- package/src/gitlab/templates.ts +140 -0
- package/src/heartbeat/heartbeat.ts +19 -10
- package/src/heartbeat/templates.ts +30 -0
- package/src/http/active-sessions.ts +27 -0
- package/src/http/approval-requests.ts +247 -0
- package/src/http/config.ts +3 -3
- package/src/http/index.ts +9 -2
- package/src/http/poll.ts +135 -14
- package/src/http/prompt-templates.ts +412 -0
- package/src/http/schedules.ts +35 -0
- package/src/http/skills.ts +479 -0
- package/src/http/workflows.ts +8 -0
- package/src/linear/sync.ts +28 -4
- package/src/linear/templates.ts +47 -0
- package/src/prompts/base-prompt.ts +41 -490
- package/src/prompts/registry.ts +57 -0
- package/src/prompts/resolver.ts +296 -0
- package/src/prompts/session-templates.ts +604 -0
- package/src/providers/claude-adapter.ts +15 -2
- package/src/providers/pi-mono-extension.ts +5 -1
- package/src/scheduler/scheduler.ts +125 -91
- package/src/server.ts +44 -0
- package/src/slack/assistant.ts +7 -4
- package/src/slack/channel-activity.ts +177 -0
- package/src/slack/handlers.ts +21 -6
- package/src/slack/templates.ts +55 -0
- package/src/tests/approval-requests.test.ts +735 -0
- package/src/tests/artifact-sdk.test.ts +12 -12
- package/src/tests/base-prompt.test.ts +49 -49
- package/src/tests/channel-activity.test.ts +363 -0
- package/src/tests/heartbeat.test.ts +1 -0
- package/src/tests/linear-webhook.test.ts +7 -3
- package/src/tests/pool-session-logs.test.ts +199 -0
- package/src/tests/prompt-template-github.test.ts +682 -0
- package/src/tests/prompt-template-remaining.test.ts +504 -0
- package/src/tests/prompt-template-resolver.test.ts +621 -0
- package/src/tests/prompt-template-session.test.ts +363 -0
- package/src/tests/prompt-templates-db.test.ts +616 -0
- package/src/tests/self-improvement.test.ts +8 -7
- package/src/tests/skill-parser.test.ts +178 -0
- package/src/tests/skill-sync.test.ts +171 -0
- package/src/tests/slack-metadata-inheritance.test.ts +1 -1
- package/src/tests/slack-thread-followups.test.ts +1 -1
- package/src/tests/structured-output.test.ts +0 -4
- package/src/tests/tool-annotations.test.ts +2 -1
- package/src/tests/update-profile-agentid.test.ts +248 -0
- package/src/tests/update-profile-auth.test.ts +195 -0
- package/src/tests/workflow-async-v2.test.ts +126 -4
- package/src/tests/workflow-definition-validation.test.ts +76 -0
- package/src/tests/workflow-executors.test.ts +4 -2
- package/src/tests/workflow-retry-v2.test.ts +1 -1
- package/src/tests/workflow-schedule-trigger.test.ts +104 -0
- package/src/tests/workflow-workspace.test.ts +272 -0
- package/src/tools/prompt-templates/delete.ts +86 -0
- package/src/tools/prompt-templates/get.ts +89 -0
- package/src/tools/prompt-templates/index.ts +5 -0
- package/src/tools/prompt-templates/list.ts +95 -0
- package/src/tools/prompt-templates/preview.ts +84 -0
- package/src/tools/prompt-templates/set.ts +117 -0
- package/src/tools/request-human-input.ts +106 -0
- package/src/tools/skills/index.ts +11 -0
- package/src/tools/skills/skill-create.ts +105 -0
- package/src/tools/skills/skill-delete.ts +67 -0
- package/src/tools/skills/skill-get.ts +75 -0
- package/src/tools/skills/skill-install-remote.ts +152 -0
- package/src/tools/skills/skill-install.ts +101 -0
- package/src/tools/skills/skill-list.ts +77 -0
- package/src/tools/skills/skill-publish.ts +123 -0
- package/src/tools/skills/skill-search.ts +43 -0
- package/src/tools/skills/skill-sync-remote.ts +128 -0
- package/src/tools/skills/skill-uninstall.ts +60 -0
- package/src/tools/skills/skill-update.ts +128 -0
- package/src/tools/store-progress.ts +22 -4
- package/src/tools/task-action.ts +20 -0
- package/src/tools/templates.ts +53 -0
- package/src/tools/tool-config.ts +23 -0
- package/src/tools/update-profile.ts +106 -34
- package/src/tools/workflows/create-workflow.ts +19 -1
- package/src/tools/workflows/update-workflow.ts +16 -1
- package/src/types.ts +109 -2
- package/src/workflows/definition.ts +30 -12
- package/src/workflows/engine.ts +40 -14
- package/src/workflows/executors/agent-task.ts +14 -3
- package/src/workflows/executors/human-in-the-loop.ts +160 -0
- package/src/workflows/executors/registry.ts +2 -0
- package/src/workflows/index.ts +1 -1
- package/src/workflows/recovery.ts +72 -0
- package/src/workflows/resume.ts +162 -12
- package/src/workflows/triggers.ts +31 -2
- package/src/workflows/version.ts +2 -0
- package/.claude/settings.json +0 -84
- package/.claude/settings.local.json +0 -117
- package/.dockerignore +0 -61
- package/.editorconfig +0 -15
- package/.entire/settings.json +0 -4
- package/.env.docker.example +0 -56
- package/.env.example +0 -78
- package/.github/ISSUE_TEMPLATE/bug_report.yml +0 -78
- package/.github/ISSUE_TEMPLATE/community-template.yml +0 -77
- package/.github/ISSUE_TEMPLATE/config.yml +0 -8
- package/.github/ISSUE_TEMPLATE/feature_request.yml +0 -60
- package/.github/PULL_REQUEST_TEMPLATE/community-template.md +0 -29
- package/.github/workflows/ci.yml +0 -52
- package/.github/workflows/docker-and-deploy.yml +0 -132
- package/.github/workflows/merge-gate.yml +0 -233
- package/.opencode/plugins/entire.ts +0 -133
- package/.superset/config.json +0 -6
- package/.wts-config.json +0 -4
- package/.wts-setup.ts +0 -171
- package/CHANGELOG.md +0 -447
- package/CLAUDE.md +0 -521
- package/CONTRIBUTING.md +0 -315
- package/DEPLOYMENT.md +0 -622
- package/Dockerfile +0 -65
- package/Dockerfile.worker +0 -189
- package/MCP.md +0 -841
- package/UI.md +0 -40
- package/api-entrypoint.sh +0 -56
- package/assets/agent-swarm-logo-orange.png +0 -0
- package/assets/agent-swarm-logo.png +0 -0
- package/assets/agent-swarm.mp4 +0 -0
- package/assets/agent-swarm.png +0 -0
- package/biome.json +0 -39
- package/deploy/DEPLOY.md +0 -60
- package/deploy/agent-swarm.service +0 -17
- package/deploy/docker-push.ts +0 -30
- package/deploy/install.ts +0 -85
- package/deploy/prod-db.ts +0 -42
- package/deploy/uninstall.ts +0 -12
- package/deploy/update.ts +0 -21
- package/depot.json +0 -1
- package/docker-compose.example.yml +0 -350
- package/docker-compose.local.yml +0 -119
- package/docker-entrypoint.sh +0 -632
- package/docs-site/app/api/search/route.ts +0 -4
- package/docs-site/app/docs/[[...slug]]/page.tsx +0 -87
- package/docs-site/app/docs/layout.tsx +0 -12
- package/docs-site/app/globals.css +0 -24
- package/docs-site/app/layout.config.tsx +0 -34
- package/docs-site/app/layout.tsx +0 -119
- package/docs-site/app/llms-full.txt/route.ts +0 -11
- package/docs-site/app/llms.mdx/docs/[[...slug]]/route.ts +0 -24
- package/docs-site/app/llms.txt/route.ts +0 -8
- package/docs-site/app/page.tsx +0 -5
- package/docs-site/app/robots.ts +0 -13
- package/docs-site/app/sitemap.ts +0 -37
- package/docs-site/components/api-page.client.tsx +0 -4
- package/docs-site/components/api-page.tsx +0 -7
- package/docs-site/components/mdx/mermaid.tsx +0 -55
- package/docs-site/content/docs/(documentation)/architecture/agents.mdx +0 -117
- package/docs-site/content/docs/(documentation)/architecture/hooks.mdx +0 -77
- package/docs-site/content/docs/(documentation)/architecture/memory.mdx +0 -96
- package/docs-site/content/docs/(documentation)/architecture/meta.json +0 -4
- package/docs-site/content/docs/(documentation)/architecture/overview.mdx +0 -172
- package/docs-site/content/docs/(documentation)/concepts/epics.mdx +0 -98
- package/docs-site/content/docs/(documentation)/concepts/meta.json +0 -4
- package/docs-site/content/docs/(documentation)/concepts/scheduling.mdx +0 -136
- package/docs-site/content/docs/(documentation)/concepts/services.mdx +0 -104
- package/docs-site/content/docs/(documentation)/concepts/task-lifecycle.mdx +0 -148
- package/docs-site/content/docs/(documentation)/concepts/workflows.mdx +0 -209
- package/docs-site/content/docs/(documentation)/contributing.mdx +0 -158
- package/docs-site/content/docs/(documentation)/getting-started.mdx +0 -157
- package/docs-site/content/docs/(documentation)/guides/agentmail-integration.mdx +0 -79
- package/docs-site/content/docs/(documentation)/guides/deployment.mdx +0 -171
- package/docs-site/content/docs/(documentation)/guides/github-integration.mdx +0 -81
- package/docs-site/content/docs/(documentation)/guides/gitlab-integration.mdx +0 -93
- package/docs-site/content/docs/(documentation)/guides/linear-integration.mdx +0 -98
- package/docs-site/content/docs/(documentation)/guides/meta.json +0 -13
- package/docs-site/content/docs/(documentation)/guides/sentry-integration.mdx +0 -52
- package/docs-site/content/docs/(documentation)/guides/slack-integration.mdx +0 -179
- package/docs-site/content/docs/(documentation)/guides/x402-payments.mdx +0 -154
- package/docs-site/content/docs/(documentation)/index.mdx +0 -65
- package/docs-site/content/docs/(documentation)/meta.json +0 -19
- package/docs-site/content/docs/(documentation)/reference/cli.mdx +0 -241
- package/docs-site/content/docs/(documentation)/reference/environment-variables.mdx +0 -205
- package/docs-site/content/docs/(documentation)/reference/mcp-tools.mdx +0 -449
- package/docs-site/content/docs/(documentation)/reference/meta.json +0 -4
- package/docs-site/content/docs/api-reference/active-sessions.mdx +0 -9
- package/docs-site/content/docs/api-reference/agents.mdx +0 -9
- package/docs-site/content/docs/api-reference/channels.mdx +0 -9
- package/docs-site/content/docs/api-reference/config.mdx +0 -9
- package/docs-site/content/docs/api-reference/debug.mdx +0 -9
- package/docs-site/content/docs/api-reference/ecosystem.mdx +0 -9
- package/docs-site/content/docs/api-reference/epics.mdx +0 -9
- package/docs-site/content/docs/api-reference/index.mdx +0 -32
- package/docs-site/content/docs/api-reference/memory.mdx +0 -9
- package/docs-site/content/docs/api-reference/meta.json +0 -25
- package/docs-site/content/docs/api-reference/poll.mdx +0 -9
- package/docs-site/content/docs/api-reference/repos.mdx +0 -9
- package/docs-site/content/docs/api-reference/schedules.mdx +0 -9
- package/docs-site/content/docs/api-reference/session-data.mdx +0 -9
- package/docs-site/content/docs/api-reference/stats.mdx +0 -9
- package/docs-site/content/docs/api-reference/tasks.mdx +0 -9
- package/docs-site/content/docs/api-reference/trackers.mdx +0 -9
- package/docs-site/content/docs/api-reference/webhooks.mdx +0 -9
- package/docs-site/content/docs/api-reference/workflows.mdx +0 -9
- package/docs-site/content/docs/meta.json +0 -3
- package/docs-site/lib/get-llm-text.ts +0 -10
- package/docs-site/lib/openapi.ts +0 -23
- package/docs-site/lib/source.ts +0 -8
- package/docs-site/mdx-components.tsx +0 -13
- package/docs-site/next.config.mjs +0 -29
- package/docs-site/package.json +0 -35
- package/docs-site/pnpm-lock.yaml +0 -5407
- package/docs-site/postcss.config.mjs +0 -8
- package/docs-site/public/logo.png +0 -0
- package/docs-site/scripts/generate-docs.ts +0 -171
- package/docs-site/source.config.ts +0 -17
- package/docs-site/tsconfig.json +0 -46
- package/ecosystem.config.cjs +0 -66
- package/landing/next.config.ts +0 -14
- package/landing/package.json +0 -31
- package/landing/pnpm-lock.yaml +0 -1091
- package/landing/postcss.config.mjs +0 -8
- package/landing/public/apple-touch-icon.png +0 -0
- package/landing/public/favicon.ico +0 -0
- package/landing/public/logo.png +0 -0
- package/landing/public/og-image.png +0 -0
- package/landing/public/omghost-desplega.svg +0 -30
- package/landing/public/omghost-openfort.svg +0 -9
- package/landing/src/app/actions/waitlist.ts +0 -25
- package/landing/src/app/blog/openfort-hackathon/page.tsx +0 -863
- package/landing/src/app/blog/page.tsx +0 -162
- package/landing/src/app/blog/swarm-metrics/page.tsx +0 -685
- package/landing/src/app/examples/page.tsx +0 -174
- package/landing/src/app/examples/x402/page.tsx +0 -456
- package/landing/src/app/globals.css +0 -122
- package/landing/src/app/layout.tsx +0 -134
- package/landing/src/app/page.tsx +0 -27
- package/landing/src/app/robots.ts +0 -13
- package/landing/src/app/sitemap.ts +0 -44
- package/landing/src/components/architecture.tsx +0 -163
- package/landing/src/components/cta.tsx +0 -52
- package/landing/src/components/features.tsx +0 -160
- package/landing/src/components/footer.tsx +0 -100
- package/landing/src/components/hero.tsx +0 -217
- package/landing/src/components/how-it-works.tsx +0 -165
- package/landing/src/components/navbar.tsx +0 -147
- package/landing/src/components/waitlist.tsx +0 -110
- package/landing/src/components/why-choose.tsx +0 -149
- package/landing/src/components/workshops.tsx +0 -328
- package/landing/src/lib/utils.ts +0 -6
- package/landing/tsconfig.json +0 -41
- package/misc/transcripts/2026-03-09-pi-mono-e2e-verification.md +0 -154
- package/new-ui/CLAUDE.md +0 -92
- package/new-ui/README.md +0 -73
- package/new-ui/biome.json +0 -42
- package/new-ui/components.json +0 -21
- package/new-ui/index.html +0 -25
- package/new-ui/package.json +0 -49
- package/new-ui/pnpm-lock.yaml +0 -4845
- package/new-ui/public/logo.png +0 -0
- package/new-ui/src/api/client.ts +0 -814
- package/new-ui/src/api/hooks/index.ts +0 -64
- package/new-ui/src/api/hooks/use-agents.ts +0 -58
- package/new-ui/src/api/hooks/use-channels.ts +0 -115
- package/new-ui/src/api/hooks/use-config-api.ts +0 -46
- package/new-ui/src/api/hooks/use-costs.ts +0 -122
- package/new-ui/src/api/hooks/use-db-query.ts +0 -29
- package/new-ui/src/api/hooks/use-epics.ts +0 -75
- package/new-ui/src/api/hooks/use-repos.ts +0 -61
- package/new-ui/src/api/hooks/use-schedules.ts +0 -81
- package/new-ui/src/api/hooks/use-services.ts +0 -16
- package/new-ui/src/api/hooks/use-stats.ts +0 -27
- package/new-ui/src/api/hooks/use-tasks.ts +0 -89
- package/new-ui/src/api/hooks/use-workflows.ts +0 -109
- package/new-ui/src/api/types.ts +0 -549
- package/new-ui/src/app/App.tsx +0 -13
- package/new-ui/src/app/providers.tsx +0 -32
- package/new-ui/src/app/router.tsx +0 -52
- package/new-ui/src/components/layout/app-header.tsx +0 -47
- package/new-ui/src/components/layout/app-sidebar.tsx +0 -128
- package/new-ui/src/components/layout/breadcrumbs.tsx +0 -57
- package/new-ui/src/components/layout/config-guard.tsx +0 -22
- package/new-ui/src/components/layout/root-layout.tsx +0 -40
- package/new-ui/src/components/layout/swarm-switcher.tsx +0 -85
- package/new-ui/src/components/shared/command-menu.tsx +0 -131
- package/new-ui/src/components/shared/data-grid.tsx +0 -141
- package/new-ui/src/components/shared/empty-state.tsx +0 -24
- package/new-ui/src/components/shared/error-boundary.tsx +0 -72
- package/new-ui/src/components/shared/json-viewer.tsx +0 -47
- package/new-ui/src/components/shared/name-connection-modal.tsx +0 -99
- package/new-ui/src/components/shared/page-skeleton.tsx +0 -16
- package/new-ui/src/components/shared/session-log-viewer.tsx +0 -364
- package/new-ui/src/components/shared/stats-bar.tsx +0 -132
- package/new-ui/src/components/shared/status-badge.tsx +0 -131
- package/new-ui/src/components/shared/usage-summary.tsx +0 -179
- package/new-ui/src/components/ui/alert-dialog.tsx +0 -176
- package/new-ui/src/components/ui/alert.tsx +0 -60
- package/new-ui/src/components/ui/avatar.tsx +0 -96
- package/new-ui/src/components/ui/badge.tsx +0 -46
- package/new-ui/src/components/ui/button.tsx +0 -62
- package/new-ui/src/components/ui/card.tsx +0 -75
- package/new-ui/src/components/ui/command.tsx +0 -160
- package/new-ui/src/components/ui/dialog.tsx +0 -143
- package/new-ui/src/components/ui/dropdown-menu.tsx +0 -226
- package/new-ui/src/components/ui/input.tsx +0 -21
- package/new-ui/src/components/ui/label.tsx +0 -19
- package/new-ui/src/components/ui/progress.tsx +0 -26
- package/new-ui/src/components/ui/scroll-area.tsx +0 -54
- package/new-ui/src/components/ui/select.tsx +0 -175
- package/new-ui/src/components/ui/separator.tsx +0 -28
- package/new-ui/src/components/ui/sheet.tsx +0 -132
- package/new-ui/src/components/ui/sidebar.tsx +0 -691
- package/new-ui/src/components/ui/skeleton.tsx +0 -13
- package/new-ui/src/components/ui/sonner.tsx +0 -35
- package/new-ui/src/components/ui/switch.tsx +0 -33
- package/new-ui/src/components/ui/table.tsx +0 -92
- package/new-ui/src/components/ui/tabs.tsx +0 -79
- package/new-ui/src/components/ui/textarea.tsx +0 -18
- package/new-ui/src/components/ui/tooltip.tsx +0 -51
- package/new-ui/src/components/workflows/action-node.tsx +0 -53
- package/new-ui/src/components/workflows/condition-node.tsx +0 -50
- package/new-ui/src/components/workflows/graph-utils.ts +0 -124
- package/new-ui/src/components/workflows/json-tree.tsx +0 -189
- package/new-ui/src/components/workflows/node-styles.ts +0 -10
- package/new-ui/src/components/workflows/step-detail-sheet.tsx +0 -87
- package/new-ui/src/components/workflows/trigger-node.tsx +0 -41
- package/new-ui/src/components/workflows/workflow-graph.tsx +0 -65
- package/new-ui/src/hooks/use-auto-scroll.ts +0 -82
- package/new-ui/src/hooks/use-config.ts +0 -203
- package/new-ui/src/hooks/use-keyboard-shortcuts.ts +0 -41
- package/new-ui/src/hooks/use-mobile.ts +0 -19
- package/new-ui/src/hooks/use-theme.ts +0 -60
- package/new-ui/src/lib/config.ts +0 -188
- package/new-ui/src/lib/slugs.ts +0 -71
- package/new-ui/src/lib/utils.ts +0 -120
- package/new-ui/src/main.tsx +0 -11
- package/new-ui/src/pages/agents/[id]/page.tsx +0 -492
- package/new-ui/src/pages/agents/page.tsx +0 -134
- package/new-ui/src/pages/chat/page.tsx +0 -674
- package/new-ui/src/pages/config/page.tsx +0 -1109
- package/new-ui/src/pages/dashboard/page.tsx +0 -454
- package/new-ui/src/pages/debug/page.tsx +0 -275
- package/new-ui/src/pages/epics/[id]/page.tsx +0 -809
- package/new-ui/src/pages/epics/page.tsx +0 -321
- package/new-ui/src/pages/not-found/page.tsx +0 -18
- package/new-ui/src/pages/repos/page.tsx +0 -369
- package/new-ui/src/pages/schedules/[id]/page.tsx +0 -664
- package/new-ui/src/pages/schedules/page.tsx +0 -477
- package/new-ui/src/pages/services/page.tsx +0 -128
- package/new-ui/src/pages/tasks/[id]/page.tsx +0 -670
- package/new-ui/src/pages/tasks/page.tsx +0 -592
- package/new-ui/src/pages/usage/page.tsx +0 -195
- package/new-ui/src/pages/workflow-runs/[id]/page.tsx +0 -363
- package/new-ui/src/pages/workflows/[id]/page.tsx +0 -417
- package/new-ui/src/pages/workflows/page.tsx +0 -266
- package/new-ui/src/styles/ag-grid.css +0 -36
- package/new-ui/src/styles/globals.css +0 -213
- package/new-ui/test-results/.last-run.json +0 -4
- package/new-ui/tsconfig.app.json +0 -34
- package/new-ui/tsconfig.json +0 -4
- package/new-ui/tsconfig.node.json +0 -26
- package/new-ui/vercel.json +0 -4
- package/new-ui/vite.config.ts +0 -28
- package/plugin/README.md +0 -1
- package/plugin/build-pi-skills.ts +0 -233
- package/plugin/hooks/hooks.json +0 -71
- package/prek.toml +0 -75
- package/pyproject.toml +0 -9
- package/scripts/check-db-boundary.sh +0 -60
- package/scripts/e2e-docker-provider.ts +0 -820
- package/scripts/e2e-io-schemas-test.ts +0 -807
- package/scripts/e2e-provider-test.ts +0 -220
- package/scripts/e2e-workflow-redesign.sh +0 -229
- package/scripts/e2e-workflow-test.sh +0 -285
- package/scripts/e2e-workflow-test.ts +0 -857
- package/scripts/generate-mcp-docs.ts +0 -415
- package/scripts/generate-openapi.ts +0 -26
- package/scripts/measure-tool-tokens.ts +0 -118
- package/scripts/x402-e2e-test.ts +0 -195
- package/scripts/x402-test-server.ts +0 -236
- package/scripts/x402-testnet-e2e.ts +0 -668
- package/slack-manifest.json +0 -88
- package/templates-ui/README.md +0 -46
- package/templates-ui/components.json +0 -17
- package/templates-ui/eslint.config.mjs +0 -18
- package/templates-ui/next.config.ts +0 -7
- package/templates-ui/package.json +0 -35
- package/templates-ui/pnpm-lock.yaml +0 -4571
- package/templates-ui/postcss.config.mjs +0 -7
- package/templates-ui/public/file.svg +0 -1
- package/templates-ui/public/globe.svg +0 -1
- package/templates-ui/public/logo.png +0 -0
- package/templates-ui/public/next.svg +0 -1
- package/templates-ui/public/vercel.svg +0 -1
- package/templates-ui/public/window.svg +0 -1
- package/templates-ui/src/app/[category]/[name]/page.tsx +0 -89
- package/templates-ui/src/app/api/templates/[...slug]/route.ts +0 -52
- package/templates-ui/src/app/api/templates/route.ts +0 -18
- package/templates-ui/src/app/builder/page.tsx +0 -37
- package/templates-ui/src/app/globals.css +0 -94
- package/templates-ui/src/app/layout.tsx +0 -79
- package/templates-ui/src/app/page.tsx +0 -38
- package/templates-ui/src/app/robots.ts +0 -11
- package/templates-ui/src/app/sitemap.ts +0 -31
- package/templates-ui/src/components/compose-builder.tsx +0 -442
- package/templates-ui/src/components/compose-preview.tsx +0 -117
- package/templates-ui/src/components/file-preview.tsx +0 -77
- package/templates-ui/src/components/footer.tsx +0 -40
- package/templates-ui/src/components/header.tsx +0 -41
- package/templates-ui/src/components/template-card.tsx +0 -87
- package/templates-ui/src/components/template-detail.tsx +0 -125
- package/templates-ui/src/components/template-gallery.tsx +0 -263
- package/templates-ui/src/components/ui/badge.tsx +0 -36
- package/templates-ui/src/components/ui/button.tsx +0 -57
- package/templates-ui/src/components/ui/card.tsx +0 -76
- package/templates-ui/src/components/ui/separator.tsx +0 -31
- package/templates-ui/src/components/ui/tooltip.tsx +0 -32
- package/templates-ui/src/lib/compose-generator.ts +0 -241
- package/templates-ui/src/lib/templates.ts +0 -137
- package/templates-ui/src/lib/utils.ts +0 -6
- package/templates-ui/tsconfig.json +0 -34
- package/thoughts/research/2026-02-28-openfort-viem-x402-research.md +0 -679
- package/thoughts/research/2026-02-28-x402-payments-research.md +0 -686
- package/thoughts/researcher/plans/2026-02-20-agent-self-improvement-plan.md +0 -282
- package/thoughts/researcher/research/2026-02-20-agent-self-improvement.md +0 -492
- package/thoughts/shared/plans/.gitkeep +0 -0
- package/thoughts/shared/plans/2025-12-18-slack-integration.md +0 -1195
- package/thoughts/shared/plans/2025-12-19-agent-log-streaming.md +0 -732
- package/thoughts/shared/plans/2025-12-19-role-based-swarm-plugin.md +0 -361
- package/thoughts/shared/plans/2025-12-20-mobile-responsive-ui.md +0 -501
- package/thoughts/shared/plans/2025-12-20-startup-team-swarm.md +0 -560
- package/thoughts/shared/plans/2025-12-23-runner-level-polling.md +0 -934
- package/thoughts/shared/plans/2025-12-23-runner-session-logs.md +0 -1000
- package/thoughts/shared/plans/2025-12-23-worker-lead-spawn-triggers.md +0 -568
- package/thoughts/shared/plans/2026-01-09-inverse-teleport.md +0 -1516
- package/thoughts/shared/plans/2026-01-12-agent-rename-pm2-control.md +0 -1133
- package/thoughts/shared/plans/2026-01-12-github-app-integration.md +0 -380
- package/thoughts/shared/plans/2026-01-12-lead-inbox-model.md +0 -876
- package/thoughts/shared/plans/2026-01-12-ralph-wiggum-integration.md +0 -463
- package/thoughts/shared/plans/2026-01-13-agent-concurrency.md +0 -691
- package/thoughts/shared/plans/2026-01-13-github-assignment-handling.md +0 -690
- package/thoughts/shared/plans/2026-01-13-prevent-duplicate-trigger-processing.md +0 -1071
- package/thoughts/shared/plans/2026-01-14-fix-slack-thread-context.md +0 -507
- package/thoughts/shared/plans/2026-01-15-scheduled-tasks-implementation.md +0 -565
- package/thoughts/shared/plans/2026-01-15-usage-cost-tracking-ui.md +0 -1479
- package/thoughts/shared/plans/2026-01-16-epics-feature-implementation.md +0 -1230
- package/thoughts/shared/plans/2026-02-26-mcp-tool-context-reduction.md +0 -282
- package/thoughts/shared/plans/2026-03-02-claude-context-mode-integration.md +0 -328
- package/thoughts/shared/plans/2026-03-02-code-level-heartbeat.md +0 -224
- package/thoughts/shared/research/.gitkeep +0 -0
- package/thoughts/shared/research/2025-01-09-inverse-teleport-plan-review.md +0 -420
- package/thoughts/shared/research/2025-12-18-slack-integration.md +0 -442
- package/thoughts/shared/research/2025-12-19-agent-log-streaming.md +0 -339
- package/thoughts/shared/research/2025-12-19-agent-secrets-cli-research.md +0 -390
- package/thoughts/shared/research/2025-12-21-gemini-cli-integration.md +0 -376
- package/thoughts/shared/research/2025-12-22-runner-loop-architecture.md +0 -582
- package/thoughts/shared/research/2025-12-22-setup-experience-improvements.md +0 -264
- package/thoughts/shared/research/2026-01-13-lead-duplicate-trigger-processing.md +0 -223
- package/thoughts/shared/research/2026-01-14-lead-slack-thread-context.md +0 -277
- package/thoughts/shared/research/2026-01-15-ai-tracker-agent-swarm-integration.md +0 -376
- package/thoughts/shared/research/2026-01-15-auto-starting-processes-in-worker-containers.md +0 -787
- package/thoughts/shared/research/2026-01-15-scheduled-tasks.md +0 -390
- package/thoughts/shared/research/2026-01-16-epics-feature-research.md +0 -437
- package/thoughts/shared/research/2026-02-26-cliffy-mcp-tools.md +0 -159
- package/thoughts/shared/research/2026-03-03-database-migration-system-refactor.md +0 -337
- package/thoughts/swarm-researcher/plans/2026-02-23-openclaw-improvements-plan.md +0 -778
- package/thoughts/swarm-researcher/plans/2026-02-26-artifacts-localtunnel-plan.md +0 -1269
- package/thoughts/swarm-researcher/research/2026-02-23-openclaw-vs-agent-swarm-comparison.md +0 -411
- package/thoughts/swarm-researcher/research/2026-02-26-artifacts-localtunnel.md +0 -724
- package/thoughts/taras/brainstorms/2026-03-20-prompt-template-registry.md +0 -443
- package/thoughts/taras/brainstorms/2026-03-20-setup-cli-onboarding.md +0 -307
- package/thoughts/taras/plans/2026-01-22-agent-swarm-schemas.md +0 -98
- package/thoughts/taras/plans/2026-01-28-per-worker-claude-md.md +0 -617
- package/thoughts/taras/plans/2026-01-28-sentry-cli-integration.md +0 -214
- package/thoughts/taras/plans/2026-02-20-auto-improvement.md +0 -803
- package/thoughts/taras/plans/2026-02-20-env-management.md +0 -538
- package/thoughts/taras/plans/2026-02-20-memory-system.md +0 -882
- package/thoughts/taras/plans/2026-02-20-repos-knowledge.md +0 -806
- package/thoughts/taras/plans/2026-02-20-session-attach.md +0 -647
- package/thoughts/taras/plans/2026-02-20-worker-identity.md +0 -820
- package/thoughts/taras/plans/2026-02-25-feat-new-ui-visual-redesign-plan.md +0 -768
- package/thoughts/taras/plans/2026-03-04-fix-buildSystemPrompt-missing-fields.md +0 -77
- package/thoughts/taras/plans/2026-03-04-new-ui-missing-actions.md +0 -543
- package/thoughts/taras/plans/2026-03-06-one-time-scheduled-tasks.md +0 -373
- package/thoughts/taras/plans/2026-03-08-memory-self-improvement-enhancements.md +0 -512
- package/thoughts/taras/plans/2026-03-08-pi-mono-provider-implementation.md +0 -919
- package/thoughts/taras/plans/2026-03-09-templates-registry.md +0 -723
- package/thoughts/taras/plans/2026-03-10-task-working-directory.md +0 -371
- package/thoughts/taras/plans/2026-03-11-archil-per-agent-write-strategy.md +0 -621
- package/thoughts/taras/plans/2026-03-12-eliminate-inbox-route-to-tasks.md +0 -61
- package/thoughts/taras/plans/2026-03-12-slack-thread-followup-additive.md +0 -488
- package/thoughts/taras/plans/2026-03-13-slack-ai-improvements.md +0 -644
- package/thoughts/taras/plans/2026-03-16-route-wrapper-openapi.md +0 -636
- package/thoughts/taras/plans/2026-03-17-multi-api-config.md +0 -444
- package/thoughts/taras/plans/2026-03-18-agent-fs-integration.md +0 -591
- package/thoughts/taras/plans/2026-03-18-debug-db-explorer.md +0 -446
- package/thoughts/taras/plans/2026-03-18-workflow-redesign.md +0 -987
- package/thoughts/taras/plans/2026-03-19-compound-learnings.md +0 -403
- package/thoughts/taras/plans/2026-03-19-ticket-tracker-linear-integration.md +0 -860
- package/thoughts/taras/plans/2026-03-19-workflow-io-schemas-and-bugs.md +0 -899
- package/thoughts/taras/plans/2026-03-20-setup-cli-onboarding.md +0 -874
- package/thoughts/taras/plans/2026-03-20-workflow-structured-output-validation-workspace.md +0 -723
- package/thoughts/taras/research/2026-01-22-vercel-cli-integration.md +0 -287
- package/thoughts/taras/research/2026-01-27-excessive-polling-issue.md +0 -311
- package/thoughts/taras/research/2026-01-28-per-worker-claude-md.md +0 -383
- package/thoughts/taras/research/2026-01-28-sentry-cli-integration.md +0 -240
- package/thoughts/taras/research/2026-02-19-agent-native-swarm-architecture.md +0 -390
- package/thoughts/taras/research/2026-02-19-swarm-gaps-implementation.md +0 -594
- package/thoughts/taras/research/2026-02-25-dashboard-ui-design-best-practices.md +0 -825
- package/thoughts/taras/research/2026-02-26-task-detail-page-redesign.md +0 -393
- package/thoughts/taras/research/2026-03-03-new-ui-missing-actions.md +0 -168
- package/thoughts/taras/research/2026-03-05-pi-mono-provider-research.md +0 -230
- package/thoughts/taras/research/2026-03-06-workflow-engine-design.md +0 -445
- package/thoughts/taras/research/2026-03-08-drive-loop-concept.md +0 -375
- package/thoughts/taras/research/2026-03-08-pi-mono-deep-dive.md +0 -869
- package/thoughts/taras/research/2026-03-09-templates-registry.md +0 -373
- package/thoughts/taras/research/2026-03-10-agent-working-directory.md +0 -223
- package/thoughts/taras/research/2026-03-10-configurable-event-prompts.md +0 -339
- package/thoughts/taras/research/2026-03-11-archil-production-setup.md +0 -181
- package/thoughts/taras/research/2026-03-11-archil-shared-disk-write-strategies.md +0 -437
- package/thoughts/taras/research/2026-03-13-slack-ai-features.md +0 -258
- package/thoughts/taras/research/2026-03-16-openapi-docs-generation.md +0 -335
- package/thoughts/taras/research/2026-03-16-route-wrapper-openapi.md +0 -670
- package/thoughts/taras/research/2026-03-16-slack-thread-followups-e2e.md +0 -54
- package/thoughts/taras/research/2026-03-18-agent-fs-integration.md +0 -558
- package/thoughts/taras/research/2026-03-18-linear-integration-finalization.md +0 -526
- package/thoughts/taras/research/2026-03-18-workflow-redesign.md +0 -797
- package/thoughts/taras/research/2026-03-19-workflow-node-io-schemas-and-bugs.md +0 -563
- package/thoughts/taras/research/2026-03-19-workflow-structured-output-validation-workspace.md +0 -486
- package/thoughts/taras/research/2026-03-20-prompt-template-registry.md +0 -469
- package/tsconfig.json +0 -37
|
@@ -1,1133 +0,0 @@
|
|
|
1
|
-
# Agent Renaming & PM2 Control from Frontend Implementation Plan
|
|
2
|
-
|
|
3
|
-
## Overview
|
|
4
|
-
|
|
5
|
-
Implement two features to enhance frontend management capabilities:
|
|
6
|
-
1. **Agent name editing** - Allow changing agent names directly from the UI
|
|
7
|
-
2. **PM2 process control** - Stop/Start/Restart/Rename PM2 processes and view logs from the frontend
|
|
8
|
-
|
|
9
|
-
Since agents run on separate machines (distributed architecture), PM2 control requires a **command queue system** where the backend queues commands and agents poll for them, execute locally, and report results.
|
|
10
|
-
|
|
11
|
-
## Current State Analysis
|
|
12
|
-
|
|
13
|
-
### Key Discoveries:
|
|
14
|
-
|
|
15
|
-
- Agent names are stored in SQLite `agents` table but can only be set at registration time via `POST /api/agents` (`src/http.ts:227-279`)
|
|
16
|
-
- `updateAgentProfile()` function exists at `src/be/db.ts:1256-1286` but only updates `description`, `role`, `capabilities` - NOT `name`
|
|
17
|
-
- No PATCH endpoint exists for agents
|
|
18
|
-
- Services are tracked in `services` table with status (starting, healthy, unhealthy, stopped) at `src/be/db.ts:134-155`
|
|
19
|
-
- MCP tools exist for agents to register/unregister/update their own services at `src/tools/services.ts`
|
|
20
|
-
- Agents poll for triggers via `GET /api/poll` at `src/http.ts:282-379` with runner implementation at `src/commands/runner.ts:237-277`
|
|
21
|
-
- Frontend uses React Query hooks at `ui/src/hooks/queries.ts` and API client at `ui/src/lib/api.ts`
|
|
22
|
-
- ServicesPanel at `ui/src/components/ServicesPanel.tsx` is currently read-only display
|
|
23
|
-
|
|
24
|
-
## Desired End State
|
|
25
|
-
|
|
26
|
-
1. **Agent name editing**: Users can click an edit button next to agent name in AgentDetailPanel, modify the name inline, and save. The change persists to the database and UI updates immediately.
|
|
27
|
-
|
|
28
|
-
2. **PM2 control**: ServicesPanel displays action buttons (Stop, Start, Restart, Logs) for each service. Clicking a button:
|
|
29
|
-
- Creates a command in the database
|
|
30
|
-
- Agent polls and receives the command
|
|
31
|
-
- Agent executes PM2 command locally
|
|
32
|
-
- Agent reports result back to backend
|
|
33
|
-
- Frontend shows command status and result
|
|
34
|
-
|
|
35
|
-
3. **Log viewing**: A modal displays PM2 logs for a service with stdout/stderr filtering and auto-refresh.
|
|
36
|
-
|
|
37
|
-
## What We're NOT Doing
|
|
38
|
-
|
|
39
|
-
- WebSocket-based real-time updates (using polling for simplicity)
|
|
40
|
-
- Agent-to-agent direct communication for PM2 control
|
|
41
|
-
- PM2 process configuration editing (only lifecycle control)
|
|
42
|
-
- Bulk operations on multiple services at once
|
|
43
|
-
- Historical command audit UI (just current command status)
|
|
44
|
-
|
|
45
|
-
## Implementation Approach
|
|
46
|
-
|
|
47
|
-
The implementation follows a layered approach:
|
|
48
|
-
1. Backend database and API changes first
|
|
49
|
-
2. Agent-side command execution
|
|
50
|
-
3. Frontend UI components
|
|
51
|
-
|
|
52
|
-
For PM2 control, we use a command queue pattern since agents are distributed. This reuses the existing polling mechanism, extending the `Trigger` type to include `command_pending`.
|
|
53
|
-
|
|
54
|
-
---
|
|
55
|
-
|
|
56
|
-
## Phase 1: Agent Name Editing - Backend
|
|
57
|
-
|
|
58
|
-
### Overview
|
|
59
|
-
Add database function and API endpoint to update agent names.
|
|
60
|
-
|
|
61
|
-
### Changes Required:
|
|
62
|
-
|
|
63
|
-
#### 1. Database function for name update
|
|
64
|
-
**File**: `src/be/db.ts`
|
|
65
|
-
**Changes**: Add `updateAgentName()` function after `updateAgentProfile()` (around line 1286)
|
|
66
|
-
|
|
67
|
-
```typescript
|
|
68
|
-
export function updateAgentName(id: string, newName: string): Agent | null {
|
|
69
|
-
const agent = getAgentById(id);
|
|
70
|
-
if (!agent) return null;
|
|
71
|
-
|
|
72
|
-
const now = new Date().toISOString();
|
|
73
|
-
const row = getDb()
|
|
74
|
-
.prepare<AgentRow, [string, string, string]>(
|
|
75
|
-
`UPDATE agents SET name = ?, lastUpdatedAt = ? WHERE id = ? RETURNING *`,
|
|
76
|
-
)
|
|
77
|
-
.get(newName, now, id);
|
|
78
|
-
|
|
79
|
-
if (row) {
|
|
80
|
-
try {
|
|
81
|
-
createLogEntry({
|
|
82
|
-
eventType: "agent_profile_update",
|
|
83
|
-
agentId: id,
|
|
84
|
-
oldValue: agent.name,
|
|
85
|
-
newValue: newName,
|
|
86
|
-
metadata: { field: "name" },
|
|
87
|
-
});
|
|
88
|
-
} catch {}
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
return row ? rowToAgent(row) : null;
|
|
92
|
-
}
|
|
93
|
-
```
|
|
94
|
-
|
|
95
|
-
#### 2. PATCH endpoint for agents
|
|
96
|
-
**File**: `src/http.ts`
|
|
97
|
-
**Changes**: Add PATCH handler after GET `/api/agents/:id` endpoint (around line 527)
|
|
98
|
-
|
|
99
|
-
```typescript
|
|
100
|
-
// PATCH /api/agents/:id - Update agent properties
|
|
101
|
-
if (
|
|
102
|
-
req.method === "PATCH" &&
|
|
103
|
-
pathSegments[0] === "api" &&
|
|
104
|
-
pathSegments[1] === "agents" &&
|
|
105
|
-
pathSegments[2]
|
|
106
|
-
) {
|
|
107
|
-
const agentId = pathSegments[2];
|
|
108
|
-
|
|
109
|
-
const chunks: Buffer[] = [];
|
|
110
|
-
for await (const chunk of req) {
|
|
111
|
-
chunks.push(chunk);
|
|
112
|
-
}
|
|
113
|
-
const body = JSON.parse(Buffer.concat(chunks).toString());
|
|
114
|
-
|
|
115
|
-
const agent = getAgentById(agentId);
|
|
116
|
-
if (!agent) {
|
|
117
|
-
res.writeHead(404, { "Content-Type": "application/json" });
|
|
118
|
-
res.end(JSON.stringify({ error: "Agent not found" }));
|
|
119
|
-
return;
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
let updated = agent;
|
|
123
|
-
|
|
124
|
-
// Update name if provided
|
|
125
|
-
if (body.name && typeof body.name === "string") {
|
|
126
|
-
const result = updateAgentName(agentId, body.name);
|
|
127
|
-
if (result) updated = result;
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
// Update profile fields if provided
|
|
131
|
-
if (body.description || body.role || body.capabilities) {
|
|
132
|
-
const result = updateAgentProfile(agentId, {
|
|
133
|
-
description: body.description,
|
|
134
|
-
role: body.role,
|
|
135
|
-
capabilities: body.capabilities,
|
|
136
|
-
});
|
|
137
|
-
if (result) updated = result;
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
res.writeHead(200, { "Content-Type": "application/json" });
|
|
141
|
-
res.end(JSON.stringify(updated));
|
|
142
|
-
return;
|
|
143
|
-
}
|
|
144
|
-
```
|
|
145
|
-
|
|
146
|
-
#### 3. Update imports in http.ts
|
|
147
|
-
**File**: `src/http.ts`
|
|
148
|
-
**Changes**: Add `updateAgentName` to imports from `./be/db`
|
|
149
|
-
|
|
150
|
-
### Success Criteria:
|
|
151
|
-
|
|
152
|
-
#### Automated Verification:
|
|
153
|
-
- [ ] TypeScript compiles: `bun run build` or `bunx tsc --noEmit`
|
|
154
|
-
- [ ] Server starts without errors: `bun run src/http.ts`
|
|
155
|
-
- [ ] API test: `curl -X PATCH http://localhost:3013/api/agents/<id> -H "Content-Type: application/json" -d '{"name":"new-name"}'`
|
|
156
|
-
|
|
157
|
-
#### Manual Verification:
|
|
158
|
-
- [ ] Agent name is updated in database after PATCH request
|
|
159
|
-
- [ ] Agent log entry is created with `agent_profile_update` event
|
|
160
|
-
|
|
161
|
-
**Implementation Note**: After completing this phase and all automated verification passes, pause here for manual confirmation before proceeding to Phase 2.
|
|
162
|
-
|
|
163
|
-
---
|
|
164
|
-
|
|
165
|
-
## Phase 2: Agent Name Editing - Frontend
|
|
166
|
-
|
|
167
|
-
### Overview
|
|
168
|
-
Add API method, React Query mutation, and inline edit UI for agent names.
|
|
169
|
-
|
|
170
|
-
### Changes Required:
|
|
171
|
-
|
|
172
|
-
#### 1. API method for updating agents
|
|
173
|
-
**File**: `ui/src/lib/api.ts`
|
|
174
|
-
**Changes**: Add `updateAgent` method to `ApiClient` class
|
|
175
|
-
|
|
176
|
-
```typescript
|
|
177
|
-
async updateAgent(
|
|
178
|
-
id: string,
|
|
179
|
-
updates: { name?: string; description?: string; role?: string; capabilities?: string[] },
|
|
180
|
-
): Promise<Agent> {
|
|
181
|
-
const res = await this.request(`/api/agents/${id}`, {
|
|
182
|
-
method: "PATCH",
|
|
183
|
-
headers: { "Content-Type": "application/json" },
|
|
184
|
-
body: JSON.stringify(updates),
|
|
185
|
-
});
|
|
186
|
-
if (!res.ok) throw new Error("Failed to update agent");
|
|
187
|
-
return res.json();
|
|
188
|
-
}
|
|
189
|
-
```
|
|
190
|
-
|
|
191
|
-
#### 2. React Query mutation hook
|
|
192
|
-
**File**: `ui/src/hooks/queries.ts`
|
|
193
|
-
**Changes**: Add `useUpdateAgent` hook
|
|
194
|
-
|
|
195
|
-
```typescript
|
|
196
|
-
export function useUpdateAgent() {
|
|
197
|
-
const queryClient = useQueryClient();
|
|
198
|
-
return useMutation({
|
|
199
|
-
mutationFn: ({
|
|
200
|
-
id,
|
|
201
|
-
updates,
|
|
202
|
-
}: {
|
|
203
|
-
id: string;
|
|
204
|
-
updates: { name?: string; description?: string; role?: string };
|
|
205
|
-
}) => api.updateAgent(id, updates),
|
|
206
|
-
onSuccess: (_, variables) => {
|
|
207
|
-
queryClient.invalidateQueries({ queryKey: ["agents"] });
|
|
208
|
-
queryClient.invalidateQueries({ queryKey: ["agent", variables.id] });
|
|
209
|
-
},
|
|
210
|
-
});
|
|
211
|
-
}
|
|
212
|
-
```
|
|
213
|
-
|
|
214
|
-
#### 3. Add edit UI to AgentDetailPanel
|
|
215
|
-
**File**: `ui/src/components/AgentDetailPanel.tsx`
|
|
216
|
-
**Changes**: Add inline edit capability for agent name
|
|
217
|
-
|
|
218
|
-
- Add `useState` for edit mode and name value
|
|
219
|
-
- Add edit icon button next to agent name
|
|
220
|
-
- When editing, show input field with save/cancel buttons
|
|
221
|
-
- Call `useUpdateAgent` mutation on save
|
|
222
|
-
|
|
223
|
-
### Success Criteria:
|
|
224
|
-
|
|
225
|
-
#### Automated Verification:
|
|
226
|
-
- [ ] Frontend builds: `cd ui && bun run build`
|
|
227
|
-
- [ ] TypeScript compiles: `cd ui && bunx tsc --noEmit`
|
|
228
|
-
|
|
229
|
-
#### Manual Verification:
|
|
230
|
-
- [ ] Edit button appears next to agent name in detail panel
|
|
231
|
-
- [ ] Clicking edit shows input field with current name
|
|
232
|
-
- [ ] Saving updates the name and closes edit mode
|
|
233
|
-
- [ ] Cancel discards changes
|
|
234
|
-
- [ ] Agent list refreshes with new name
|
|
235
|
-
|
|
236
|
-
**Implementation Note**: After completing this phase and all automated verification passes, pause here for manual confirmation before proceeding to Phase 3.
|
|
237
|
-
|
|
238
|
-
---
|
|
239
|
-
|
|
240
|
-
## Phase 3: Command Queue System - Database & Types
|
|
241
|
-
|
|
242
|
-
### Overview
|
|
243
|
-
Add database table and types for the command queue system.
|
|
244
|
-
|
|
245
|
-
### Changes Required:
|
|
246
|
-
|
|
247
|
-
#### 1. Add command types
|
|
248
|
-
**File**: `src/types.ts`
|
|
249
|
-
**Changes**: Add command-related types
|
|
250
|
-
|
|
251
|
-
```typescript
|
|
252
|
-
// Command types for PM2 control
|
|
253
|
-
export type CommandType = "pm2_start" | "pm2_stop" | "pm2_restart" | "pm2_rename" | "pm2_logs";
|
|
254
|
-
export type CommandStatus = "pending" | "in_progress" | "completed" | "failed" | "expired";
|
|
255
|
-
|
|
256
|
-
export interface AgentCommand {
|
|
257
|
-
id: string;
|
|
258
|
-
agentId: string;
|
|
259
|
-
serviceId?: string;
|
|
260
|
-
commandType: CommandType;
|
|
261
|
-
payload?: Record<string, unknown>;
|
|
262
|
-
status: CommandStatus;
|
|
263
|
-
result?: string;
|
|
264
|
-
createdAt: string;
|
|
265
|
-
startedAt?: string;
|
|
266
|
-
completedAt?: string;
|
|
267
|
-
expiresAt: string;
|
|
268
|
-
createdBy: string;
|
|
269
|
-
}
|
|
270
|
-
```
|
|
271
|
-
|
|
272
|
-
#### 2. Add agent_commands table
|
|
273
|
-
**File**: `src/be/db.ts`
|
|
274
|
-
**Changes**: Add table creation in schema initialization (after services table)
|
|
275
|
-
|
|
276
|
-
```typescript
|
|
277
|
-
database.run(`
|
|
278
|
-
CREATE TABLE IF NOT EXISTS agent_commands (
|
|
279
|
-
id TEXT PRIMARY KEY,
|
|
280
|
-
agentId TEXT NOT NULL,
|
|
281
|
-
serviceId TEXT,
|
|
282
|
-
commandType TEXT NOT NULL CHECK(commandType IN ('pm2_start', 'pm2_stop', 'pm2_restart', 'pm2_rename', 'pm2_logs')),
|
|
283
|
-
payload TEXT,
|
|
284
|
-
status TEXT NOT NULL DEFAULT 'pending' CHECK(status IN ('pending', 'in_progress', 'completed', 'failed', 'expired')),
|
|
285
|
-
result TEXT,
|
|
286
|
-
createdAt TEXT NOT NULL,
|
|
287
|
-
startedAt TEXT,
|
|
288
|
-
completedAt TEXT,
|
|
289
|
-
expiresAt TEXT NOT NULL,
|
|
290
|
-
createdBy TEXT,
|
|
291
|
-
FOREIGN KEY (agentId) REFERENCES agents(id) ON DELETE CASCADE,
|
|
292
|
-
FOREIGN KEY (serviceId) REFERENCES services(id) ON DELETE SET NULL
|
|
293
|
-
)
|
|
294
|
-
`);
|
|
295
|
-
```
|
|
296
|
-
|
|
297
|
-
Add indexes:
|
|
298
|
-
```typescript
|
|
299
|
-
database.run(`CREATE INDEX IF NOT EXISTS idx_agent_commands_agentId ON agent_commands(agentId)`);
|
|
300
|
-
database.run(`CREATE INDEX IF NOT EXISTS idx_agent_commands_status ON agent_commands(status)`);
|
|
301
|
-
database.run(`CREATE INDEX IF NOT EXISTS idx_agent_commands_createdAt ON agent_commands(createdAt)`);
|
|
302
|
-
```
|
|
303
|
-
|
|
304
|
-
#### 3. Add CRUD functions for commands
|
|
305
|
-
**File**: `src/be/db.ts`
|
|
306
|
-
**Changes**: Add command management functions
|
|
307
|
-
|
|
308
|
-
```typescript
|
|
309
|
-
// Command row type
|
|
310
|
-
type CommandRow = {
|
|
311
|
-
id: string;
|
|
312
|
-
agentId: string;
|
|
313
|
-
serviceId: string | null;
|
|
314
|
-
commandType: CommandType;
|
|
315
|
-
payload: string | null;
|
|
316
|
-
status: CommandStatus;
|
|
317
|
-
result: string | null;
|
|
318
|
-
createdAt: string;
|
|
319
|
-
startedAt: string | null;
|
|
320
|
-
completedAt: string | null;
|
|
321
|
-
expiresAt: string;
|
|
322
|
-
createdBy: string | null;
|
|
323
|
-
};
|
|
324
|
-
|
|
325
|
-
function rowToCommand(row: CommandRow): AgentCommand {
|
|
326
|
-
return {
|
|
327
|
-
id: row.id,
|
|
328
|
-
agentId: row.agentId,
|
|
329
|
-
serviceId: row.serviceId ?? undefined,
|
|
330
|
-
commandType: row.commandType,
|
|
331
|
-
payload: row.payload ? JSON.parse(row.payload) : undefined,
|
|
332
|
-
status: row.status,
|
|
333
|
-
result: row.result ?? undefined,
|
|
334
|
-
createdAt: row.createdAt,
|
|
335
|
-
startedAt: row.startedAt ?? undefined,
|
|
336
|
-
completedAt: row.completedAt ?? undefined,
|
|
337
|
-
expiresAt: row.expiresAt,
|
|
338
|
-
createdBy: row.createdBy ?? undefined,
|
|
339
|
-
};
|
|
340
|
-
}
|
|
341
|
-
|
|
342
|
-
export interface CreateCommandOptions {
|
|
343
|
-
serviceId?: string;
|
|
344
|
-
payload?: Record<string, unknown>;
|
|
345
|
-
createdBy?: string;
|
|
346
|
-
expiresInMs?: number; // Default 5 minutes
|
|
347
|
-
}
|
|
348
|
-
|
|
349
|
-
export function createCommand(
|
|
350
|
-
agentId: string,
|
|
351
|
-
commandType: CommandType,
|
|
352
|
-
options?: CreateCommandOptions,
|
|
353
|
-
): AgentCommand {
|
|
354
|
-
const id = crypto.randomUUID();
|
|
355
|
-
const now = new Date().toISOString();
|
|
356
|
-
const expiresAt = new Date(Date.now() + (options?.expiresInMs ?? 5 * 60 * 1000)).toISOString();
|
|
357
|
-
|
|
358
|
-
const row = getDb()
|
|
359
|
-
.prepare<CommandRow, (string | null)[]>(
|
|
360
|
-
`INSERT INTO agent_commands (id, agentId, serviceId, commandType, payload, status, createdAt, expiresAt, createdBy)
|
|
361
|
-
VALUES (?, ?, ?, ?, ?, 'pending', ?, ?, ?) RETURNING *`,
|
|
362
|
-
)
|
|
363
|
-
.get(
|
|
364
|
-
id,
|
|
365
|
-
agentId,
|
|
366
|
-
options?.serviceId ?? null,
|
|
367
|
-
commandType,
|
|
368
|
-
options?.payload ? JSON.stringify(options.payload) : null,
|
|
369
|
-
now,
|
|
370
|
-
expiresAt,
|
|
371
|
-
options?.createdBy ?? null,
|
|
372
|
-
);
|
|
373
|
-
|
|
374
|
-
if (!row) throw new Error("Failed to create command");
|
|
375
|
-
return rowToCommand(row);
|
|
376
|
-
}
|
|
377
|
-
|
|
378
|
-
export function getCommandById(id: string): AgentCommand | null {
|
|
379
|
-
const row = getDb()
|
|
380
|
-
.prepare<CommandRow, [string]>("SELECT * FROM agent_commands WHERE id = ?")
|
|
381
|
-
.get(id);
|
|
382
|
-
return row ? rowToCommand(row) : null;
|
|
383
|
-
}
|
|
384
|
-
|
|
385
|
-
export function getPendingCommandsForAgent(agentId: string): AgentCommand[] {
|
|
386
|
-
const now = new Date().toISOString();
|
|
387
|
-
return getDb()
|
|
388
|
-
.prepare<CommandRow, [string, string]>(
|
|
389
|
-
`SELECT * FROM agent_commands
|
|
390
|
-
WHERE agentId = ? AND status = 'pending' AND expiresAt > ?
|
|
391
|
-
ORDER BY createdAt ASC`,
|
|
392
|
-
)
|
|
393
|
-
.all(agentId, now)
|
|
394
|
-
.map(rowToCommand);
|
|
395
|
-
}
|
|
396
|
-
|
|
397
|
-
export function updateCommandStatus(
|
|
398
|
-
id: string,
|
|
399
|
-
status: CommandStatus,
|
|
400
|
-
result?: string,
|
|
401
|
-
): AgentCommand | null {
|
|
402
|
-
const now = new Date().toISOString();
|
|
403
|
-
const timestampField = status === "in_progress" ? "startedAt" : "completedAt";
|
|
404
|
-
|
|
405
|
-
const row = getDb()
|
|
406
|
-
.prepare<CommandRow, [CommandStatus, string | null, string, string]>(
|
|
407
|
-
`UPDATE agent_commands
|
|
408
|
-
SET status = ?, result = ?, ${timestampField} = ?
|
|
409
|
-
WHERE id = ? RETURNING *`,
|
|
410
|
-
)
|
|
411
|
-
.get(status, result ?? null, now, id);
|
|
412
|
-
|
|
413
|
-
return row ? rowToCommand(row) : null;
|
|
414
|
-
}
|
|
415
|
-
|
|
416
|
-
export interface CommandFilters {
|
|
417
|
-
agentId?: string;
|
|
418
|
-
status?: CommandStatus;
|
|
419
|
-
serviceId?: string;
|
|
420
|
-
}
|
|
421
|
-
|
|
422
|
-
export function getAllCommands(filters?: CommandFilters): AgentCommand[] {
|
|
423
|
-
const conditions: string[] = [];
|
|
424
|
-
const params: string[] = [];
|
|
425
|
-
|
|
426
|
-
if (filters?.agentId) {
|
|
427
|
-
conditions.push("agentId = ?");
|
|
428
|
-
params.push(filters.agentId);
|
|
429
|
-
}
|
|
430
|
-
if (filters?.status) {
|
|
431
|
-
conditions.push("status = ?");
|
|
432
|
-
params.push(filters.status);
|
|
433
|
-
}
|
|
434
|
-
if (filters?.serviceId) {
|
|
435
|
-
conditions.push("serviceId = ?");
|
|
436
|
-
params.push(filters.serviceId);
|
|
437
|
-
}
|
|
438
|
-
|
|
439
|
-
const whereClause = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
|
|
440
|
-
const query = `SELECT * FROM agent_commands ${whereClause} ORDER BY createdAt DESC LIMIT 100`;
|
|
441
|
-
|
|
442
|
-
return getDb()
|
|
443
|
-
.prepare<CommandRow, string[]>(query)
|
|
444
|
-
.all(...params)
|
|
445
|
-
.map(rowToCommand);
|
|
446
|
-
}
|
|
447
|
-
|
|
448
|
-
export function deleteCommand(id: string): boolean {
|
|
449
|
-
const result = getDb().run("DELETE FROM agent_commands WHERE id = ?", [id]);
|
|
450
|
-
return result.changes > 0;
|
|
451
|
-
}
|
|
452
|
-
```
|
|
453
|
-
|
|
454
|
-
### Success Criteria:
|
|
455
|
-
|
|
456
|
-
#### Automated Verification:
|
|
457
|
-
- [ ] TypeScript compiles: `bunx tsc --noEmit`
|
|
458
|
-
- [ ] Server starts and initializes database: `bun run src/http.ts`
|
|
459
|
-
- [ ] Database has new table: `sqlite3 agent-swarm-db.sqlite ".schema agent_commands"`
|
|
460
|
-
|
|
461
|
-
#### Manual Verification:
|
|
462
|
-
- [ ] Table created with correct schema
|
|
463
|
-
- [ ] Indexes created
|
|
464
|
-
|
|
465
|
-
**Implementation Note**: After completing this phase and all automated verification passes, pause here for manual confirmation before proceeding to Phase 4.
|
|
466
|
-
|
|
467
|
-
---
|
|
468
|
-
|
|
469
|
-
## Phase 4: Command Queue System - API Endpoints
|
|
470
|
-
|
|
471
|
-
### Overview
|
|
472
|
-
Add REST endpoints for command management.
|
|
473
|
-
|
|
474
|
-
### Changes Required:
|
|
475
|
-
|
|
476
|
-
#### 1. Add command endpoints
|
|
477
|
-
**File**: `src/http.ts`
|
|
478
|
-
**Changes**: Add command CRUD endpoints
|
|
479
|
-
|
|
480
|
-
```typescript
|
|
481
|
-
// POST /api/commands - Create a new command
|
|
482
|
-
if (
|
|
483
|
-
req.method === "POST" &&
|
|
484
|
-
pathSegments[0] === "api" &&
|
|
485
|
-
pathSegments[1] === "commands" &&
|
|
486
|
-
!pathSegments[2]
|
|
487
|
-
) {
|
|
488
|
-
const chunks: Buffer[] = [];
|
|
489
|
-
for await (const chunk of req) {
|
|
490
|
-
chunks.push(chunk);
|
|
491
|
-
}
|
|
492
|
-
const body = JSON.parse(Buffer.concat(chunks).toString());
|
|
493
|
-
|
|
494
|
-
if (!body.agentId || !body.commandType) {
|
|
495
|
-
res.writeHead(400, { "Content-Type": "application/json" });
|
|
496
|
-
res.end(JSON.stringify({ error: "Missing agentId or commandType" }));
|
|
497
|
-
return;
|
|
498
|
-
}
|
|
499
|
-
|
|
500
|
-
// Verify agent exists
|
|
501
|
-
const agent = getAgentById(body.agentId);
|
|
502
|
-
if (!agent) {
|
|
503
|
-
res.writeHead(404, { "Content-Type": "application/json" });
|
|
504
|
-
res.end(JSON.stringify({ error: "Agent not found" }));
|
|
505
|
-
return;
|
|
506
|
-
}
|
|
507
|
-
|
|
508
|
-
const command = createCommand(body.agentId, body.commandType, {
|
|
509
|
-
serviceId: body.serviceId,
|
|
510
|
-
payload: body.payload,
|
|
511
|
-
createdBy: myAgentId || "frontend",
|
|
512
|
-
});
|
|
513
|
-
|
|
514
|
-
res.writeHead(201, { "Content-Type": "application/json" });
|
|
515
|
-
res.end(JSON.stringify(command));
|
|
516
|
-
return;
|
|
517
|
-
}
|
|
518
|
-
|
|
519
|
-
// GET /api/commands - List commands
|
|
520
|
-
if (
|
|
521
|
-
req.method === "GET" &&
|
|
522
|
-
pathSegments[0] === "api" &&
|
|
523
|
-
pathSegments[1] === "commands" &&
|
|
524
|
-
!pathSegments[2]
|
|
525
|
-
) {
|
|
526
|
-
const agentId = queryParams.get("agentId") || undefined;
|
|
527
|
-
const status = queryParams.get("status") as CommandStatus | undefined;
|
|
528
|
-
const serviceId = queryParams.get("serviceId") || undefined;
|
|
529
|
-
|
|
530
|
-
const commands = getAllCommands({ agentId, status, serviceId });
|
|
531
|
-
res.writeHead(200, { "Content-Type": "application/json" });
|
|
532
|
-
res.end(JSON.stringify({ commands }));
|
|
533
|
-
return;
|
|
534
|
-
}
|
|
535
|
-
|
|
536
|
-
// GET /api/commands/:id - Get single command
|
|
537
|
-
if (
|
|
538
|
-
req.method === "GET" &&
|
|
539
|
-
pathSegments[0] === "api" &&
|
|
540
|
-
pathSegments[1] === "commands" &&
|
|
541
|
-
pathSegments[2] &&
|
|
542
|
-
!pathSegments[3]
|
|
543
|
-
) {
|
|
544
|
-
const command = getCommandById(pathSegments[2]);
|
|
545
|
-
if (!command) {
|
|
546
|
-
res.writeHead(404, { "Content-Type": "application/json" });
|
|
547
|
-
res.end(JSON.stringify({ error: "Command not found" }));
|
|
548
|
-
return;
|
|
549
|
-
}
|
|
550
|
-
|
|
551
|
-
res.writeHead(200, { "Content-Type": "application/json" });
|
|
552
|
-
res.end(JSON.stringify(command));
|
|
553
|
-
return;
|
|
554
|
-
}
|
|
555
|
-
|
|
556
|
-
// POST /api/commands/:id/result - Agent reports command result
|
|
557
|
-
if (
|
|
558
|
-
req.method === "POST" &&
|
|
559
|
-
pathSegments[0] === "api" &&
|
|
560
|
-
pathSegments[1] === "commands" &&
|
|
561
|
-
pathSegments[2] &&
|
|
562
|
-
pathSegments[3] === "result"
|
|
563
|
-
) {
|
|
564
|
-
const commandId = pathSegments[2];
|
|
565
|
-
const command = getCommandById(commandId);
|
|
566
|
-
|
|
567
|
-
if (!command) {
|
|
568
|
-
res.writeHead(404, { "Content-Type": "application/json" });
|
|
569
|
-
res.end(JSON.stringify({ error: "Command not found" }));
|
|
570
|
-
return;
|
|
571
|
-
}
|
|
572
|
-
|
|
573
|
-
const chunks: Buffer[] = [];
|
|
574
|
-
for await (const chunk of req) {
|
|
575
|
-
chunks.push(chunk);
|
|
576
|
-
}
|
|
577
|
-
const body = JSON.parse(Buffer.concat(chunks).toString());
|
|
578
|
-
|
|
579
|
-
const status = body.success ? "completed" : "failed";
|
|
580
|
-
const updated = updateCommandStatus(commandId, status, JSON.stringify(body.result || body.error));
|
|
581
|
-
|
|
582
|
-
res.writeHead(200, { "Content-Type": "application/json" });
|
|
583
|
-
res.end(JSON.stringify(updated));
|
|
584
|
-
return;
|
|
585
|
-
}
|
|
586
|
-
|
|
587
|
-
// DELETE /api/commands/:id - Cancel pending command
|
|
588
|
-
if (
|
|
589
|
-
req.method === "DELETE" &&
|
|
590
|
-
pathSegments[0] === "api" &&
|
|
591
|
-
pathSegments[1] === "commands" &&
|
|
592
|
-
pathSegments[2]
|
|
593
|
-
) {
|
|
594
|
-
const command = getCommandById(pathSegments[2]);
|
|
595
|
-
|
|
596
|
-
if (!command) {
|
|
597
|
-
res.writeHead(404, { "Content-Type": "application/json" });
|
|
598
|
-
res.end(JSON.stringify({ error: "Command not found" }));
|
|
599
|
-
return;
|
|
600
|
-
}
|
|
601
|
-
|
|
602
|
-
if (command.status !== "pending") {
|
|
603
|
-
res.writeHead(400, { "Content-Type": "application/json" });
|
|
604
|
-
res.end(JSON.stringify({ error: "Can only cancel pending commands" }));
|
|
605
|
-
return;
|
|
606
|
-
}
|
|
607
|
-
|
|
608
|
-
deleteCommand(pathSegments[2]);
|
|
609
|
-
res.writeHead(204);
|
|
610
|
-
res.end();
|
|
611
|
-
return;
|
|
612
|
-
}
|
|
613
|
-
```
|
|
614
|
-
|
|
615
|
-
#### 2. Extend poll endpoint for commands
|
|
616
|
-
**File**: `src/http.ts`
|
|
617
|
-
**Changes**: Add command trigger to poll response (in the transaction, after other trigger checks)
|
|
618
|
-
|
|
619
|
-
```typescript
|
|
620
|
-
// Check for pending commands (before returning null trigger)
|
|
621
|
-
const pendingCommands = getPendingCommandsForAgent(myAgentId);
|
|
622
|
-
if (pendingCommands.length > 0) {
|
|
623
|
-
const cmd = pendingCommands[0];
|
|
624
|
-
// Mark as in_progress
|
|
625
|
-
updateCommandStatus(cmd.id, "in_progress");
|
|
626
|
-
return {
|
|
627
|
-
trigger: {
|
|
628
|
-
type: "command_pending",
|
|
629
|
-
commandId: cmd.id,
|
|
630
|
-
command: cmd,
|
|
631
|
-
},
|
|
632
|
-
};
|
|
633
|
-
}
|
|
634
|
-
```
|
|
635
|
-
|
|
636
|
-
#### 3. Update imports
|
|
637
|
-
**File**: `src/http.ts`
|
|
638
|
-
**Changes**: Add new imports from `./be/db`
|
|
639
|
-
|
|
640
|
-
```typescript
|
|
641
|
-
import {
|
|
642
|
-
// ... existing imports
|
|
643
|
-
createCommand,
|
|
644
|
-
getCommandById,
|
|
645
|
-
getPendingCommandsForAgent,
|
|
646
|
-
updateCommandStatus,
|
|
647
|
-
getAllCommands,
|
|
648
|
-
deleteCommand,
|
|
649
|
-
} from "./be/db";
|
|
650
|
-
```
|
|
651
|
-
|
|
652
|
-
### Success Criteria:
|
|
653
|
-
|
|
654
|
-
#### Automated Verification:
|
|
655
|
-
- [ ] TypeScript compiles: `bunx tsc --noEmit`
|
|
656
|
-
- [ ] Create command: `curl -X POST http://localhost:3013/api/commands -H "Content-Type: application/json" -d '{"agentId":"<id>","commandType":"pm2_stop","payload":{"processName":"test"}}'`
|
|
657
|
-
- [ ] List commands: `curl http://localhost:3013/api/commands`
|
|
658
|
-
|
|
659
|
-
#### Manual Verification:
|
|
660
|
-
- [ ] Command appears in database after creation
|
|
661
|
-
- [ ] Poll endpoint returns command_pending trigger
|
|
662
|
-
- [ ] Command status updates to in_progress after poll
|
|
663
|
-
|
|
664
|
-
**Implementation Note**: After completing this phase and all automated verification passes, pause here for manual confirmation before proceeding to Phase 5.
|
|
665
|
-
|
|
666
|
-
---
|
|
667
|
-
|
|
668
|
-
## Phase 5: Agent-Side Command Execution
|
|
669
|
-
|
|
670
|
-
### Overview
|
|
671
|
-
Add PM2 command execution on the agent side.
|
|
672
|
-
|
|
673
|
-
### Changes Required:
|
|
674
|
-
|
|
675
|
-
#### 1. Create PM2 executor module
|
|
676
|
-
**File**: `src/commands/pm2-executor.ts` (new file)
|
|
677
|
-
**Changes**: Create new module
|
|
678
|
-
|
|
679
|
-
```typescript
|
|
680
|
-
export interface CommandResult {
|
|
681
|
-
success: boolean;
|
|
682
|
-
message?: string;
|
|
683
|
-
data?: unknown;
|
|
684
|
-
error?: string;
|
|
685
|
-
}
|
|
686
|
-
|
|
687
|
-
export async function executePm2Command(
|
|
688
|
-
commandType: string,
|
|
689
|
-
payload: Record<string, unknown>,
|
|
690
|
-
): Promise<CommandResult> {
|
|
691
|
-
const processName = payload.processName as string;
|
|
692
|
-
|
|
693
|
-
if (!processName) {
|
|
694
|
-
return { success: false, error: "Missing processName in payload" };
|
|
695
|
-
}
|
|
696
|
-
|
|
697
|
-
try {
|
|
698
|
-
switch (commandType) {
|
|
699
|
-
case "pm2_stop":
|
|
700
|
-
await Bun.$`pm2 stop ${processName}`.quiet();
|
|
701
|
-
return { success: true, message: `Stopped ${processName}` };
|
|
702
|
-
|
|
703
|
-
case "pm2_start":
|
|
704
|
-
await Bun.$`pm2 start ${processName}`.quiet();
|
|
705
|
-
return { success: true, message: `Started ${processName}` };
|
|
706
|
-
|
|
707
|
-
case "pm2_restart":
|
|
708
|
-
await Bun.$`pm2 restart ${processName}`.quiet();
|
|
709
|
-
return { success: true, message: `Restarted ${processName}` };
|
|
710
|
-
|
|
711
|
-
case "pm2_rename": {
|
|
712
|
-
const newName = payload.newName as string;
|
|
713
|
-
if (!newName) {
|
|
714
|
-
return { success: false, error: "Missing newName for rename" };
|
|
715
|
-
}
|
|
716
|
-
// PM2 doesn't have rename, so we delete and restart with new name
|
|
717
|
-
const jlist = await Bun.$`pm2 jlist`.json();
|
|
718
|
-
const proc = (jlist as Array<{ name: string; pm2_env: { pm_exec_path: string } }>).find(
|
|
719
|
-
(p) => p.name === processName,
|
|
720
|
-
);
|
|
721
|
-
if (!proc) {
|
|
722
|
-
return { success: false, error: `Process ${processName} not found` };
|
|
723
|
-
}
|
|
724
|
-
await Bun.$`pm2 delete ${processName}`.quiet();
|
|
725
|
-
await Bun.$`pm2 start ${proc.pm2_env.pm_exec_path} --name ${newName}`.quiet();
|
|
726
|
-
return { success: true, message: `Renamed ${processName} to ${newName}` };
|
|
727
|
-
}
|
|
728
|
-
|
|
729
|
-
case "pm2_logs": {
|
|
730
|
-
const lines = (payload.lines as number) || 100;
|
|
731
|
-
const output = await Bun.$`pm2 logs ${processName} --lines ${lines} --nostream`.text();
|
|
732
|
-
return { success: true, data: { logs: output } };
|
|
733
|
-
}
|
|
734
|
-
|
|
735
|
-
default:
|
|
736
|
-
return { success: false, error: `Unknown command type: ${commandType}` };
|
|
737
|
-
}
|
|
738
|
-
} catch (error) {
|
|
739
|
-
return { success: false, error: String(error) };
|
|
740
|
-
}
|
|
741
|
-
}
|
|
742
|
-
|
|
743
|
-
export async function reportCommandResult(
|
|
744
|
-
apiUrl: string,
|
|
745
|
-
apiKey: string,
|
|
746
|
-
agentId: string,
|
|
747
|
-
commandId: string,
|
|
748
|
-
result: CommandResult,
|
|
749
|
-
): Promise<void> {
|
|
750
|
-
const headers: Record<string, string> = {
|
|
751
|
-
"Content-Type": "application/json",
|
|
752
|
-
"X-Agent-ID": agentId,
|
|
753
|
-
};
|
|
754
|
-
if (apiKey) {
|
|
755
|
-
headers.Authorization = `Bearer ${apiKey}`;
|
|
756
|
-
}
|
|
757
|
-
|
|
758
|
-
try {
|
|
759
|
-
await fetch(`${apiUrl}/api/commands/${commandId}/result`, {
|
|
760
|
-
method: "POST",
|
|
761
|
-
headers,
|
|
762
|
-
body: JSON.stringify(result),
|
|
763
|
-
});
|
|
764
|
-
} catch (error) {
|
|
765
|
-
console.warn(`[pm2-executor] Failed to report result: ${error}`);
|
|
766
|
-
}
|
|
767
|
-
}
|
|
768
|
-
```
|
|
769
|
-
|
|
770
|
-
#### 2. Extend trigger types in runner
|
|
771
|
-
**File**: `src/commands/runner.ts`
|
|
772
|
-
**Changes**: Add command_pending to Trigger interface (around line 174)
|
|
773
|
-
|
|
774
|
-
```typescript
|
|
775
|
-
interface Trigger {
|
|
776
|
-
type:
|
|
777
|
-
| "task_assigned"
|
|
778
|
-
| "task_offered"
|
|
779
|
-
| "unread_mentions"
|
|
780
|
-
| "pool_tasks_available"
|
|
781
|
-
| "tasks_finished"
|
|
782
|
-
| "command_pending"; // Add this
|
|
783
|
-
taskId?: string;
|
|
784
|
-
task?: unknown;
|
|
785
|
-
mentionsCount?: number;
|
|
786
|
-
count?: number;
|
|
787
|
-
tasks?: Array<{
|
|
788
|
-
id: string;
|
|
789
|
-
agentId?: string;
|
|
790
|
-
task: string;
|
|
791
|
-
status: string;
|
|
792
|
-
}>;
|
|
793
|
-
// Add command fields
|
|
794
|
-
commandId?: string;
|
|
795
|
-
command?: {
|
|
796
|
-
id: string;
|
|
797
|
-
commandType: string;
|
|
798
|
-
serviceId?: string;
|
|
799
|
-
payload?: Record<string, unknown>;
|
|
800
|
-
};
|
|
801
|
-
}
|
|
802
|
-
```
|
|
803
|
-
|
|
804
|
-
#### 3. Handle command execution in poll loop
|
|
805
|
-
**File**: `src/commands/runner.ts`
|
|
806
|
-
**Changes**: Add command handling in the poll loop (after trigger check, before building prompt)
|
|
807
|
-
|
|
808
|
-
Add import at top:
|
|
809
|
-
```typescript
|
|
810
|
-
import { executePm2Command, reportCommandResult } from "./pm2-executor";
|
|
811
|
-
```
|
|
812
|
-
|
|
813
|
-
Add in poll loop (around line 570, after `console.log(`[${role}] Trigger received: ${trigger.type}`);`):
|
|
814
|
-
```typescript
|
|
815
|
-
// Handle command triggers directly (no Claude needed)
|
|
816
|
-
if (trigger.type === "command_pending" && trigger.command && trigger.commandId) {
|
|
817
|
-
console.log(`[${role}] Executing command: ${trigger.command.commandType}`);
|
|
818
|
-
const result = await executePm2Command(
|
|
819
|
-
trigger.command.commandType,
|
|
820
|
-
trigger.command.payload || {},
|
|
821
|
-
);
|
|
822
|
-
console.log(`[${role}] Command result: ${result.success ? "success" : "failed"}`);
|
|
823
|
-
await reportCommandResult(apiUrl, apiKey, agentId, trigger.commandId, result);
|
|
824
|
-
console.log(`[${role}] Command result reported, polling for next trigger...`);
|
|
825
|
-
continue; // Skip Claude iteration for commands
|
|
826
|
-
}
|
|
827
|
-
```
|
|
828
|
-
|
|
829
|
-
### Success Criteria:
|
|
830
|
-
|
|
831
|
-
#### Automated Verification:
|
|
832
|
-
- [ ] TypeScript compiles: `bunx tsc --noEmit`
|
|
833
|
-
- [ ] PM2 executor module can be imported: `bun -e "import './src/commands/pm2-executor'"`
|
|
834
|
-
|
|
835
|
-
#### Manual Verification:
|
|
836
|
-
- [ ] Start a test PM2 process: `pm2 start --name test-process "sleep 3600"`
|
|
837
|
-
- [ ] Create stop command via API
|
|
838
|
-
- [ ] Agent polls and executes command
|
|
839
|
-
- [ ] PM2 process is stopped
|
|
840
|
-
- [ ] Command status updated to completed
|
|
841
|
-
|
|
842
|
-
**Implementation Note**: After completing this phase and all automated verification passes, pause here for manual confirmation before proceeding to Phase 6.
|
|
843
|
-
|
|
844
|
-
---
|
|
845
|
-
|
|
846
|
-
## Phase 6: Frontend PM2 Controls
|
|
847
|
-
|
|
848
|
-
### Overview
|
|
849
|
-
Add command API methods, React Query hooks, and action buttons to ServicesPanel.
|
|
850
|
-
|
|
851
|
-
### Changes Required:
|
|
852
|
-
|
|
853
|
-
#### 1. Add frontend types
|
|
854
|
-
**File**: `ui/src/types/api.ts`
|
|
855
|
-
**Changes**: Add command types
|
|
856
|
-
|
|
857
|
-
```typescript
|
|
858
|
-
export type CommandType = "pm2_start" | "pm2_stop" | "pm2_restart" | "pm2_rename" | "pm2_logs";
|
|
859
|
-
export type CommandStatus = "pending" | "in_progress" | "completed" | "failed" | "expired";
|
|
860
|
-
|
|
861
|
-
export interface AgentCommand {
|
|
862
|
-
id: string;
|
|
863
|
-
agentId: string;
|
|
864
|
-
serviceId?: string;
|
|
865
|
-
commandType: CommandType;
|
|
866
|
-
payload?: Record<string, unknown>;
|
|
867
|
-
status: CommandStatus;
|
|
868
|
-
result?: string;
|
|
869
|
-
createdAt: string;
|
|
870
|
-
startedAt?: string;
|
|
871
|
-
completedAt?: string;
|
|
872
|
-
expiresAt: string;
|
|
873
|
-
createdBy?: string;
|
|
874
|
-
}
|
|
875
|
-
```
|
|
876
|
-
|
|
877
|
-
#### 2. Add command API methods
|
|
878
|
-
**File**: `ui/src/lib/api.ts`
|
|
879
|
-
**Changes**: Add command methods to ApiClient
|
|
880
|
-
|
|
881
|
-
```typescript
|
|
882
|
-
async createCommand(params: {
|
|
883
|
-
agentId: string;
|
|
884
|
-
commandType: CommandType;
|
|
885
|
-
serviceId?: string;
|
|
886
|
-
payload?: Record<string, unknown>;
|
|
887
|
-
}): Promise<AgentCommand> {
|
|
888
|
-
const res = await this.request("/api/commands", {
|
|
889
|
-
method: "POST",
|
|
890
|
-
headers: { "Content-Type": "application/json" },
|
|
891
|
-
body: JSON.stringify(params),
|
|
892
|
-
});
|
|
893
|
-
if (!res.ok) throw new Error("Failed to create command");
|
|
894
|
-
return res.json();
|
|
895
|
-
}
|
|
896
|
-
|
|
897
|
-
async getCommand(id: string): Promise<AgentCommand> {
|
|
898
|
-
const res = await this.request(`/api/commands/${id}`);
|
|
899
|
-
if (!res.ok) throw new Error("Failed to get command");
|
|
900
|
-
return res.json();
|
|
901
|
-
}
|
|
902
|
-
|
|
903
|
-
async getCommands(filters?: {
|
|
904
|
-
agentId?: string;
|
|
905
|
-
status?: string;
|
|
906
|
-
serviceId?: string;
|
|
907
|
-
}): Promise<{ commands: AgentCommand[] }> {
|
|
908
|
-
const params = new URLSearchParams();
|
|
909
|
-
if (filters?.agentId) params.set("agentId", filters.agentId);
|
|
910
|
-
if (filters?.status) params.set("status", filters.status);
|
|
911
|
-
if (filters?.serviceId) params.set("serviceId", filters.serviceId);
|
|
912
|
-
const query = params.toString();
|
|
913
|
-
const res = await this.request(`/api/commands${query ? `?${query}` : ""}`);
|
|
914
|
-
if (!res.ok) throw new Error("Failed to get commands");
|
|
915
|
-
return res.json();
|
|
916
|
-
}
|
|
917
|
-
|
|
918
|
-
async cancelCommand(id: string): Promise<void> {
|
|
919
|
-
const res = await this.request(`/api/commands/${id}`, { method: "DELETE" });
|
|
920
|
-
if (!res.ok) throw new Error("Failed to cancel command");
|
|
921
|
-
}
|
|
922
|
-
```
|
|
923
|
-
|
|
924
|
-
#### 3. Add React Query hooks for commands
|
|
925
|
-
**File**: `ui/src/hooks/queries.ts`
|
|
926
|
-
**Changes**: Add command hooks
|
|
927
|
-
|
|
928
|
-
```typescript
|
|
929
|
-
export function useCreateCommand() {
|
|
930
|
-
const queryClient = useQueryClient();
|
|
931
|
-
return useMutation({
|
|
932
|
-
mutationFn: (params: {
|
|
933
|
-
agentId: string;
|
|
934
|
-
commandType: CommandType;
|
|
935
|
-
serviceId?: string;
|
|
936
|
-
payload?: Record<string, unknown>;
|
|
937
|
-
}) => api.createCommand(params),
|
|
938
|
-
onSuccess: () => {
|
|
939
|
-
queryClient.invalidateQueries({ queryKey: ["commands"] });
|
|
940
|
-
},
|
|
941
|
-
});
|
|
942
|
-
}
|
|
943
|
-
|
|
944
|
-
export function useCommand(id: string | undefined) {
|
|
945
|
-
return useQuery({
|
|
946
|
-
queryKey: ["command", id],
|
|
947
|
-
queryFn: () => (id ? api.getCommand(id) : null),
|
|
948
|
-
enabled: !!id,
|
|
949
|
-
refetchInterval: 2000, // Poll for status updates
|
|
950
|
-
});
|
|
951
|
-
}
|
|
952
|
-
|
|
953
|
-
export function useCommands(filters?: { agentId?: string; status?: string; serviceId?: string }) {
|
|
954
|
-
return useQuery({
|
|
955
|
-
queryKey: ["commands", filters],
|
|
956
|
-
queryFn: () => api.getCommands(filters),
|
|
957
|
-
select: (data) => data.commands,
|
|
958
|
-
refetchInterval: 5000,
|
|
959
|
-
});
|
|
960
|
-
}
|
|
961
|
-
```
|
|
962
|
-
|
|
963
|
-
#### 4. Add action buttons to ServicesPanel
|
|
964
|
-
**File**: `ui/src/components/ServicesPanel.tsx`
|
|
965
|
-
**Changes**: Add actions column with Stop/Start/Restart buttons
|
|
966
|
-
|
|
967
|
-
Add imports:
|
|
968
|
-
```typescript
|
|
969
|
-
import IconButton from "@mui/joy/IconButton";
|
|
970
|
-
import Tooltip from "@mui/joy/Tooltip";
|
|
971
|
-
import { useCreateCommand } from "../hooks/queries";
|
|
972
|
-
// Icons (use MUI Joy icons or simple text for now)
|
|
973
|
-
```
|
|
974
|
-
|
|
975
|
-
Add action column to table header:
|
|
976
|
-
```typescript
|
|
977
|
-
<th style={{ width: "12%" }}>ACTIONS</th>
|
|
978
|
-
```
|
|
979
|
-
|
|
980
|
-
Add action buttons to each row:
|
|
981
|
-
```typescript
|
|
982
|
-
const createCommand = useCreateCommand();
|
|
983
|
-
|
|
984
|
-
// In table row
|
|
985
|
-
<td>
|
|
986
|
-
<Box sx={{ display: "flex", gap: 0.5 }}>
|
|
987
|
-
<Tooltip title="Stop">
|
|
988
|
-
<IconButton
|
|
989
|
-
size="sm"
|
|
990
|
-
variant="soft"
|
|
991
|
-
color="danger"
|
|
992
|
-
onClick={() => createCommand.mutate({
|
|
993
|
-
agentId: service.agentId,
|
|
994
|
-
commandType: "pm2_stop",
|
|
995
|
-
serviceId: service.id,
|
|
996
|
-
payload: { processName: service.name },
|
|
997
|
-
})}
|
|
998
|
-
disabled={service.status === "stopped"}
|
|
999
|
-
>
|
|
1000
|
-
■
|
|
1001
|
-
</IconButton>
|
|
1002
|
-
</Tooltip>
|
|
1003
|
-
<Tooltip title="Start">
|
|
1004
|
-
<IconButton
|
|
1005
|
-
size="sm"
|
|
1006
|
-
variant="soft"
|
|
1007
|
-
color="success"
|
|
1008
|
-
onClick={() => createCommand.mutate({
|
|
1009
|
-
agentId: service.agentId,
|
|
1010
|
-
commandType: "pm2_start",
|
|
1011
|
-
serviceId: service.id,
|
|
1012
|
-
payload: { processName: service.name },
|
|
1013
|
-
})}
|
|
1014
|
-
disabled={service.status === "healthy"}
|
|
1015
|
-
>
|
|
1016
|
-
▶
|
|
1017
|
-
</IconButton>
|
|
1018
|
-
</Tooltip>
|
|
1019
|
-
<Tooltip title="Restart">
|
|
1020
|
-
<IconButton
|
|
1021
|
-
size="sm"
|
|
1022
|
-
variant="soft"
|
|
1023
|
-
color="warning"
|
|
1024
|
-
onClick={() => createCommand.mutate({
|
|
1025
|
-
agentId: service.agentId,
|
|
1026
|
-
commandType: "pm2_restart",
|
|
1027
|
-
serviceId: service.id,
|
|
1028
|
-
payload: { processName: service.name },
|
|
1029
|
-
})}
|
|
1030
|
-
>
|
|
1031
|
-
↻
|
|
1032
|
-
</IconButton>
|
|
1033
|
-
</Tooltip>
|
|
1034
|
-
</Box>
|
|
1035
|
-
</td>
|
|
1036
|
-
```
|
|
1037
|
-
|
|
1038
|
-
### Success Criteria:
|
|
1039
|
-
|
|
1040
|
-
#### Automated Verification:
|
|
1041
|
-
- [ ] Frontend builds: `cd ui && bun run build`
|
|
1042
|
-
- [ ] TypeScript compiles: `cd ui && bunx tsc --noEmit`
|
|
1043
|
-
|
|
1044
|
-
#### Manual Verification:
|
|
1045
|
-
- [ ] Action buttons appear in services table
|
|
1046
|
-
- [ ] Clicking Stop creates a command
|
|
1047
|
-
- [ ] Command appears in database
|
|
1048
|
-
- [ ] Agent executes command
|
|
1049
|
-
- [ ] Service status updates
|
|
1050
|
-
|
|
1051
|
-
**Implementation Note**: After completing this phase and all automated verification passes, the core functionality is complete. Phase 7 (Log Viewer) is optional but recommended.
|
|
1052
|
-
|
|
1053
|
-
---
|
|
1054
|
-
|
|
1055
|
-
## Phase 7: Log Viewer Modal (Optional Enhancement)
|
|
1056
|
-
|
|
1057
|
-
### Overview
|
|
1058
|
-
Add a modal to view PM2 logs for a service.
|
|
1059
|
-
|
|
1060
|
-
### Changes Required:
|
|
1061
|
-
|
|
1062
|
-
#### 1. Create LogViewerModal component
|
|
1063
|
-
**File**: `ui/src/components/LogViewerModal.tsx` (new file)
|
|
1064
|
-
|
|
1065
|
-
Modal that:
|
|
1066
|
-
- Opens when clicking Logs button on a service
|
|
1067
|
-
- Creates a pm2_logs command to fetch logs
|
|
1068
|
-
- Displays logs in a scrollable, monospace view
|
|
1069
|
-
- Has stdout/stderr filter tabs
|
|
1070
|
-
- Auto-refreshes while open
|
|
1071
|
-
|
|
1072
|
-
#### 2. Add logs button to ServicesPanel
|
|
1073
|
-
**File**: `ui/src/components/ServicesPanel.tsx`
|
|
1074
|
-
**Changes**: Add logs button and modal state
|
|
1075
|
-
|
|
1076
|
-
### Success Criteria:
|
|
1077
|
-
|
|
1078
|
-
#### Manual Verification:
|
|
1079
|
-
- [ ] Logs button opens modal
|
|
1080
|
-
- [ ] Logs are displayed
|
|
1081
|
-
- [ ] Filter tabs work
|
|
1082
|
-
- [ ] Modal closes properly
|
|
1083
|
-
|
|
1084
|
-
---
|
|
1085
|
-
|
|
1086
|
-
## Testing Strategy
|
|
1087
|
-
|
|
1088
|
-
### Unit Tests:
|
|
1089
|
-
- Database CRUD functions for commands
|
|
1090
|
-
- PM2 executor command handling
|
|
1091
|
-
- API endpoint request/response validation
|
|
1092
|
-
|
|
1093
|
-
### Integration Tests:
|
|
1094
|
-
- Full command flow: create → poll → execute → report
|
|
1095
|
-
- Frontend command creation → backend → agent → result
|
|
1096
|
-
|
|
1097
|
-
### Manual Testing Steps:
|
|
1098
|
-
1. Create an agent and a service via API
|
|
1099
|
-
2. Start a PM2 process manually: `pm2 start --name test "sleep 3600"`
|
|
1100
|
-
3. Click Stop in UI → verify process stops
|
|
1101
|
-
4. Click Start → verify process starts
|
|
1102
|
-
5. Click Restart → verify restart
|
|
1103
|
-
6. Edit agent name → verify update
|
|
1104
|
-
|
|
1105
|
-
## Performance Considerations
|
|
1106
|
-
|
|
1107
|
-
- Commands expire after 5 minutes to prevent stale commands
|
|
1108
|
-
- Poll interval is 2 seconds to balance responsiveness and load
|
|
1109
|
-
- Log fetching is on-demand, not streamed continuously
|
|
1110
|
-
- Frontend polls command status every 2 seconds while a command is pending
|
|
1111
|
-
|
|
1112
|
-
## References
|
|
1113
|
-
|
|
1114
|
-
- Existing polling mechanism: `src/commands/runner.ts:237-277`
|
|
1115
|
-
- Service table schema: `src/be/db.ts:134-155`
|
|
1116
|
-
- Agent profile update pattern: `src/be/db.ts:1256-1286`
|
|
1117
|
-
- Frontend React Query patterns: `ui/src/hooks/queries.ts`
|
|
1118
|
-
|
|
1119
|
-
## File Summary
|
|
1120
|
-
|
|
1121
|
-
| File | Action | Description |
|
|
1122
|
-
|------|--------|-------------|
|
|
1123
|
-
| `src/types.ts` | Modify | Add command types |
|
|
1124
|
-
| `src/be/db.ts` | Modify | Add table, CRUD functions |
|
|
1125
|
-
| `src/http.ts` | Modify | Add PATCH agent, command endpoints |
|
|
1126
|
-
| `src/commands/pm2-executor.ts` | Create | PM2 command execution |
|
|
1127
|
-
| `src/commands/runner.ts` | Modify | Handle command triggers |
|
|
1128
|
-
| `ui/src/types/api.ts` | Modify | Add command types |
|
|
1129
|
-
| `ui/src/lib/api.ts` | Modify | Add command API methods |
|
|
1130
|
-
| `ui/src/hooks/queries.ts` | Modify | Add command hooks |
|
|
1131
|
-
| `ui/src/components/ServicesPanel.tsx` | Modify | Add action buttons |
|
|
1132
|
-
| `ui/src/components/AgentDetailPanel.tsx` | Modify | Add name editing |
|
|
1133
|
-
| `ui/src/components/LogViewerModal.tsx` | Create | Optional log viewer |
|