@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,724 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
date: 2026-02-26T18:38:00Z
|
|
3
|
-
topic: "Agent Artifacts: Concept & Localtunnel Research"
|
|
4
|
-
author: "Researcher (Swarm Agent)"
|
|
5
|
-
status: "complete"
|
|
6
|
-
---
|
|
7
|
-
|
|
8
|
-
# Agent Artifacts: Concept & Localtunnel Research
|
|
9
|
-
|
|
10
|
-
## TL;DR
|
|
11
|
-
|
|
12
|
-
**Artifacts** are a way for swarm agents to serve HTML pages, interactive apps, and rich content via public URLs. By combining dynamically allocated local ports with our localtunnel infrastructure (`lt.desplega.ai`), agents can publish multiple artifacts simultaneously — each on its own port and subdomain — that anyone with credentials can access from a browser.
|
|
13
|
-
|
|
14
|
-
Our localtunnel forks already have the two key features needed: **deterministic subdomains** (with 409 conflict handling) and **HTTP Basic Auth** (per-tunnel, timing-safe). The infrastructure is ready to use today.
|
|
15
|
-
|
|
16
|
-
---
|
|
17
|
-
|
|
18
|
-
## Part 1: The Artifacts Concept
|
|
19
|
-
|
|
20
|
-
### What Are Artifacts?
|
|
21
|
-
|
|
22
|
-
Artifacts are publicly accessible web content served by swarm agents. Instead of agents only communicating through Slack messages and task outputs (text), artifacts let agents produce rich, interactive deliverables:
|
|
23
|
-
|
|
24
|
-
```
|
|
25
|
-
Agent Container (dynamic port per artifact)
|
|
26
|
-
|
|
|
27
|
-
| localtunnel client
|
|
28
|
-
v
|
|
29
|
-
lt.desplega.ai (localtunnel server)
|
|
30
|
-
|
|
|
31
|
-
| HTTPS + Basic Auth
|
|
32
|
-
v
|
|
33
|
-
Public URL: https://{agentId}.lt.desplega.ai
|
|
34
|
-
|
|
|
35
|
-
v
|
|
36
|
-
Browser (human or another agent)
|
|
37
|
-
```
|
|
38
|
-
|
|
39
|
-
### Use Cases
|
|
40
|
-
|
|
41
|
-
| Use Case | Description | Example |
|
|
42
|
-
|----------|-------------|---------|
|
|
43
|
-
| **Interactive reports** | Rich HTML dashboards with charts, tables, and drill-downs | PR review summary with file diffs, test coverage heatmap, dependency graph |
|
|
44
|
-
| **Approval flows** | Forms that let humans approve/reject agent proposals | "Agent wants to merge PR #42 — Approve / Reject / Request Changes" with a button that calls the swarm API |
|
|
45
|
-
| **Visual diffs** | Side-by-side visual comparisons | Before/after screenshots of UI changes, rendered markdown diffs |
|
|
46
|
-
| **Forms that create tasks** | Structured input that agents can parse | "Create a new feature request" form that submits a task to the swarm |
|
|
47
|
-
| **Live dashboards** | Real-time status pages | Swarm health dashboard, build pipeline status, Sentry issue tracker |
|
|
48
|
-
| **Documentation previews** | Rendered docs before merge | Preview of documentation changes with live navigation |
|
|
49
|
-
| **Data exploration** | Interactive data viewers | Log viewer, database query results with sorting/filtering |
|
|
50
|
-
|
|
51
|
-
### CLI Design — Detailed
|
|
52
|
-
|
|
53
|
-
The artifact system adds a new command to the `agent-swarm` CLI (`src/cli.tsx`). It follows the existing pattern where commands like `worker`, `lead`, and `hook` dispatch to their own modules.
|
|
54
|
-
|
|
55
|
-
#### `artifact serve` — Start serving content
|
|
56
|
-
|
|
57
|
-
```bash
|
|
58
|
-
# Serve a static directory
|
|
59
|
-
artifact serve ./dist --name "pr-review-42"
|
|
60
|
-
|
|
61
|
-
# Serve a custom server script (auto-assigns a free port)
|
|
62
|
-
artifact serve ./server.js --name "approval-flow"
|
|
63
|
-
|
|
64
|
-
# Serve with explicit port (otherwise auto-assigned)
|
|
65
|
-
artifact serve ./app.js --name "dashboard" --port 8080
|
|
66
|
-
|
|
67
|
-
# Serve without auth (for public content)
|
|
68
|
-
artifact serve ./dist --name "docs-preview" --no-auth
|
|
69
|
-
```
|
|
70
|
-
|
|
71
|
-
| Flag | Default | Description |
|
|
72
|
-
|------|---------|-------------|
|
|
73
|
-
| `--name` | Required | Human-readable name for the artifact |
|
|
74
|
-
| `--port` | auto | Local port (default: auto-assign a free port) |
|
|
75
|
-
| `--no-auth` | `false` | Disable Basic Auth on the tunnel |
|
|
76
|
-
| `--subdomain` | Agent UUID | Custom subdomain (overrides default) |
|
|
77
|
-
|
|
78
|
-
**What happens under the hood:**
|
|
79
|
-
|
|
80
|
-
1. If path points to a directory → starts a static file server (e.g., `sirv` or `serve-handler`) on an auto-assigned free port
|
|
81
|
-
2. If path points to a `.js`/`.ts` file → starts it via PM2 (`pm2 start <script> --name artifact-<name>`)
|
|
82
|
-
3. Creates a localtunnel: `localtunnel({ port, subdomain: '${agentId}-${name}', auth: API_KEY })`
|
|
83
|
-
- Each artifact gets its own subdomain: `{agentId}-{name}.lt.desplega.ai`
|
|
84
|
-
- A single worker can serve multiple artifacts simultaneously on different ports/subdomains
|
|
85
|
-
4. Registers in service registry via `register-service` MCP tool with metadata:
|
|
86
|
-
```json
|
|
87
|
-
{
|
|
88
|
-
"type": "artifact",
|
|
89
|
-
"artifactName": "pr-review-42",
|
|
90
|
-
"port": 49152,
|
|
91
|
-
"publicUrl": "https://{agentId}-pr-review-42.lt.desplega.ai",
|
|
92
|
-
"auth": { "username": "swarm", "password": "***" }
|
|
93
|
-
}
|
|
94
|
-
```
|
|
95
|
-
5. Posts to `#general` channel: "Artifact `pr-review-42` is live at <url>"
|
|
96
|
-
|
|
97
|
-
#### `artifact list` — List active artifacts
|
|
98
|
-
|
|
99
|
-
```bash
|
|
100
|
-
$ artifact list
|
|
101
|
-
NAME AGENT PORT URL STATUS
|
|
102
|
-
pr-review-42 researcher 49152 https://1699...cf73-pr-review-42.lt.desplega.ai healthy
|
|
103
|
-
approval-flow lead 49153 https://d454...f9c3-approval-flow.lt.desplega.ai healthy
|
|
104
|
-
dashboard picateclas 49200 https://a8b2...80f-dashboard.lt.desplega.ai unhealthy
|
|
105
|
-
```
|
|
106
|
-
|
|
107
|
-
Queries `list-services` MCP tool, filtering for services with `metadata.type === "artifact"`.
|
|
108
|
-
|
|
109
|
-
#### `artifact stop <name>` — Stop an artifact
|
|
110
|
-
|
|
111
|
-
```bash
|
|
112
|
-
$ artifact stop pr-review-42
|
|
113
|
-
# Stops PM2 process, closes localtunnel, unregisters service
|
|
114
|
-
```
|
|
115
|
-
|
|
116
|
-
1. Finds the artifact in service registry
|
|
117
|
-
2. `pm2 delete artifact-<name>`
|
|
118
|
-
3. Closes the localtunnel connection
|
|
119
|
-
4. `unregister-service`
|
|
120
|
-
|
|
121
|
-
#### `artifact open <name>` — Open in browser
|
|
122
|
-
|
|
123
|
-
```bash
|
|
124
|
-
$ artifact open pr-review-42
|
|
125
|
-
# Opens https://swarm:<apikey>@1699...cf73.lt.desplega.ai in default browser
|
|
126
|
-
```
|
|
127
|
-
|
|
128
|
-
Looks up the artifact URL from the service registry, embeds credentials, and opens via `xdg-open` (Linux) or `open` (macOS).
|
|
129
|
-
|
|
130
|
-
### Agent SDK — Detailed Design
|
|
131
|
-
|
|
132
|
-
The SDK has two sides: a **server-side TypeScript API** (used by agents to create artifacts programmatically) and a **browser-side JS SDK** (injected into served HTML for interactivity).
|
|
133
|
-
|
|
134
|
-
#### Server-Side SDK (`src/artifact-sdk/`)
|
|
135
|
-
|
|
136
|
-
This is a TypeScript module that lives **inside the agent-swarm repo** — no npm publishing required. Since agents already run inside agent-swarm's Docker container, the SDK is available as a direct import.
|
|
137
|
-
|
|
138
|
-
```typescript
|
|
139
|
-
import { createArtifactServer } from '../artifact-sdk';
|
|
140
|
-
|
|
141
|
-
// Minimal example — serve static HTML
|
|
142
|
-
const server = createArtifactServer({
|
|
143
|
-
name: 'pr-review-42',
|
|
144
|
-
// Serves files from this directory
|
|
145
|
-
static: './dist',
|
|
146
|
-
});
|
|
147
|
-
|
|
148
|
-
await server.start();
|
|
149
|
-
// → Finds a free port, starts server, creates tunnel, registers service
|
|
150
|
-
// → Logs: "Artifact live at https://{agentId}-pr-review-42.lt.desplega.ai"
|
|
151
|
-
```
|
|
152
|
-
|
|
153
|
-
```typescript
|
|
154
|
-
// Advanced example — custom Hono app with swarm API proxy
|
|
155
|
-
import { createArtifactServer } from '../artifact-sdk';
|
|
156
|
-
import { Hono } from 'hono';
|
|
157
|
-
|
|
158
|
-
const app = new Hono();
|
|
159
|
-
|
|
160
|
-
app.get('/', (c) => c.html(`
|
|
161
|
-
<h1>PR Review #42</h1>
|
|
162
|
-
<button id="approve">Approve</button>
|
|
163
|
-
<script src="/@swarm/sdk.js"></script>
|
|
164
|
-
<script>
|
|
165
|
-
const swarm = new SwarmSDK();
|
|
166
|
-
document.getElementById('approve').onclick = () =>
|
|
167
|
-
swarm.createTask({ task: 'Merge PR #42 — approved' });
|
|
168
|
-
</script>
|
|
169
|
-
`));
|
|
170
|
-
|
|
171
|
-
// Custom API routes (agent-specific logic)
|
|
172
|
-
app.post('/api/approve', async (c) => {
|
|
173
|
-
const { prNumber, comment } = await c.req.json();
|
|
174
|
-
// Agent logic here...
|
|
175
|
-
return c.json({ ok: true });
|
|
176
|
-
});
|
|
177
|
-
|
|
178
|
-
const server = createArtifactServer({
|
|
179
|
-
name: 'pr-review',
|
|
180
|
-
app, // Pass Hono app instead of static dir
|
|
181
|
-
});
|
|
182
|
-
|
|
183
|
-
await server.start();
|
|
184
|
-
```
|
|
185
|
-
|
|
186
|
-
**`createArtifactServer(options)` internals:**
|
|
187
|
-
|
|
188
|
-
```typescript
|
|
189
|
-
interface ArtifactServerOptions {
|
|
190
|
-
name: string; // Artifact name (used in subdomain + registry)
|
|
191
|
-
static?: string; // Path to static directory (mutually exclusive with app)
|
|
192
|
-
app?: HonoApp; // Custom Hono application
|
|
193
|
-
port?: number; // Local port (default: 0 = auto-assign free port)
|
|
194
|
-
auth?: boolean; // Enable Basic Auth (default: true)
|
|
195
|
-
subdomain?: string; // Custom subdomain (default: `${AGENT_ID}-${name}`)
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
interface ArtifactServer {
|
|
199
|
-
start(): Promise<void>; // Start server + tunnel + register
|
|
200
|
-
stop(): Promise<void>; // Stop everything + unregister
|
|
201
|
-
url: string; // Public URL (available after start)
|
|
202
|
-
port: number; // Actual port the server is listening on
|
|
203
|
-
tunnel: LocaltunnelInstance; // Raw tunnel object
|
|
204
|
-
}
|
|
205
|
-
```
|
|
206
|
-
|
|
207
|
-
**What `start()` does:**
|
|
208
|
-
|
|
209
|
-
1. Creates a Hono app wrapping the user's app or static handler
|
|
210
|
-
2. Injects middleware:
|
|
211
|
-
- `/@swarm/sdk.js` → serves the browser SDK bundle
|
|
212
|
-
- `/@swarm/api/*` → proxies to the swarm MCP server HTTP API (`$MCP_BASE_URL`)
|
|
213
|
-
- `/@swarm/config` → returns `{ agentId, artifactName }` as JSON
|
|
214
|
-
3. Starts HTTP server on port 0 (OS picks a free port) — or a user-specified port
|
|
215
|
-
4. Creates localtunnel with `{ port: server.port, subdomain: '${AGENT_ID}-${name}', auth: API_KEY }`
|
|
216
|
-
5. Registers via `POST $MCP_BASE_URL/api/services` (or `register-service` tool)
|
|
217
|
-
6. Sets up SIGTERM handler for graceful shutdown
|
|
218
|
-
|
|
219
|
-
#### Browser-Side SDK (`/@swarm/sdk.js`)
|
|
220
|
-
|
|
221
|
-
Auto-served by the artifact server. Provides a clean API for HTML pages to interact with the swarm.
|
|
222
|
-
|
|
223
|
-
```html
|
|
224
|
-
<script src="/@swarm/sdk.js"></script>
|
|
225
|
-
<script>
|
|
226
|
-
const swarm = new SwarmSDK();
|
|
227
|
-
|
|
228
|
-
// Task management
|
|
229
|
-
await swarm.createTask({ task: 'Do something', tags: ['from-artifact'] });
|
|
230
|
-
const tasks = await swarm.getTasks({ status: 'in_progress', limit: 10 });
|
|
231
|
-
const detail = await swarm.getTaskDetails(taskId);
|
|
232
|
-
|
|
233
|
-
// Messaging
|
|
234
|
-
await swarm.postMessage({ channel: 'general', content: 'Hello from artifact!' });
|
|
235
|
-
const messages = await swarm.readMessages({ channel: 'general', limit: 20 });
|
|
236
|
-
|
|
237
|
-
// Agent info
|
|
238
|
-
const agents = await swarm.getSwarm();
|
|
239
|
-
const myAgent = await swarm.getMyAgent();
|
|
240
|
-
|
|
241
|
-
// Epics
|
|
242
|
-
const epics = await swarm.listEpics({ status: 'active' });
|
|
243
|
-
|
|
244
|
-
// Services
|
|
245
|
-
const services = await swarm.listServices();
|
|
246
|
-
</script>
|
|
247
|
-
```
|
|
248
|
-
|
|
249
|
-
**How it works internally:**
|
|
250
|
-
|
|
251
|
-
```javascript
|
|
252
|
-
class SwarmSDK {
|
|
253
|
-
constructor() {
|
|
254
|
-
// Config is fetched from the artifact server on init
|
|
255
|
-
this._configPromise = fetch('/@swarm/config').then(r => r.json());
|
|
256
|
-
}
|
|
257
|
-
|
|
258
|
-
async createTask(opts) {
|
|
259
|
-
// Proxied through the artifact server to avoid CORS
|
|
260
|
-
return this._post('/@swarm/api/tasks', opts);
|
|
261
|
-
}
|
|
262
|
-
|
|
263
|
-
async getTasks(filters) {
|
|
264
|
-
const params = new URLSearchParams(filters);
|
|
265
|
-
return this._get(`/@swarm/api/tasks?${params}`);
|
|
266
|
-
}
|
|
267
|
-
|
|
268
|
-
async postMessage(opts) {
|
|
269
|
-
return this._post('/@swarm/api/messages', opts);
|
|
270
|
-
}
|
|
271
|
-
|
|
272
|
-
// ... other methods map to swarm HTTP API endpoints
|
|
273
|
-
|
|
274
|
-
async _post(url, body) {
|
|
275
|
-
const res = await fetch(url, {
|
|
276
|
-
method: 'POST',
|
|
277
|
-
headers: { 'Content-Type': 'application/json' },
|
|
278
|
-
body: JSON.stringify(body),
|
|
279
|
-
});
|
|
280
|
-
return res.json();
|
|
281
|
-
}
|
|
282
|
-
|
|
283
|
-
async _get(url) {
|
|
284
|
-
const res = await fetch(url);
|
|
285
|
-
return res.json();
|
|
286
|
-
}
|
|
287
|
-
}
|
|
288
|
-
```
|
|
289
|
-
|
|
290
|
-
**Security model:**
|
|
291
|
-
- The tunnel's Basic Auth gates access to the artifact (only credentialed users can reach it)
|
|
292
|
-
- The SDK calls go through the artifact server (`/@swarm/api/*`), which proxies to `$MCP_BASE_URL` with the agent's `Authorization` and `X-Agent-ID` headers
|
|
293
|
-
- The browser never has direct swarm API access — the artifact server is the proxy
|
|
294
|
-
- The agent can add middleware to filter/validate SDK requests before forwarding
|
|
295
|
-
|
|
296
|
-
### Agent Developer Experience — Step by Step
|
|
297
|
-
|
|
298
|
-
Here's how an agent (e.g., Researcher) would create and serve an artifact:
|
|
299
|
-
|
|
300
|
-
#### Step 1: Write the artifact content
|
|
301
|
-
|
|
302
|
-
```javascript
|
|
303
|
-
// /workspace/personal/artifacts/pr-review/index.html
|
|
304
|
-
// or
|
|
305
|
-
// /workspace/personal/artifacts/pr-review/server.js
|
|
306
|
-
```
|
|
307
|
-
|
|
308
|
-
The agent generates HTML/JS as part of its normal task work. For example, after analyzing PRs, it generates a review dashboard.
|
|
309
|
-
|
|
310
|
-
#### Step 2: Serve it
|
|
311
|
-
|
|
312
|
-
**Option A — Static content (simplest):**
|
|
313
|
-
```bash
|
|
314
|
-
# Agent runs this via Bash tool
|
|
315
|
-
cd /workspace/personal/artifacts/pr-review
|
|
316
|
-
artifact serve . --name "pr-review-42"
|
|
317
|
-
```
|
|
318
|
-
|
|
319
|
-
**Option B — Programmatic (inside a task script):**
|
|
320
|
-
```javascript
|
|
321
|
-
// The agent writes and runs this script
|
|
322
|
-
import { createArtifactServer } from '../artifact-sdk';
|
|
323
|
-
|
|
324
|
-
const server = createArtifactServer({
|
|
325
|
-
name: 'pr-review-42',
|
|
326
|
-
static: '/workspace/personal/artifacts/pr-review',
|
|
327
|
-
});
|
|
328
|
-
|
|
329
|
-
await server.start();
|
|
330
|
-
console.log(`Live at: ${server.url}`);
|
|
331
|
-
```
|
|
332
|
-
|
|
333
|
-
**Option C — Custom server with interactivity:**
|
|
334
|
-
```javascript
|
|
335
|
-
import { createArtifactServer } from '../artifact-sdk';
|
|
336
|
-
import { Hono } from 'hono';
|
|
337
|
-
|
|
338
|
-
const app = new Hono();
|
|
339
|
-
app.get('/', (c) => c.html(myGeneratedHtml));
|
|
340
|
-
app.post('/api/approve', async (c) => { /* handle approval */ });
|
|
341
|
-
|
|
342
|
-
// Each artifact auto-assigns a free port and gets its own subdomain
|
|
343
|
-
const server = createArtifactServer({ name: 'approval-flow', app });
|
|
344
|
-
await server.start();
|
|
345
|
-
// → Listening on port 49153, tunnel at https://{agentId}-approval-flow.lt.desplega.ai
|
|
346
|
-
|
|
347
|
-
// Workers can serve multiple artifacts simultaneously:
|
|
348
|
-
const dashboard = createArtifactServer({ name: 'dashboard', static: './dashboard-dist' });
|
|
349
|
-
await dashboard.start();
|
|
350
|
-
// → Listening on port 49154, tunnel at https://{agentId}-dashboard.lt.desplega.ai
|
|
351
|
-
```
|
|
352
|
-
|
|
353
|
-
#### Step 3: Share the URL
|
|
354
|
-
|
|
355
|
-
The agent posts the URL to Slack or includes it in task output:
|
|
356
|
-
|
|
357
|
-
```javascript
|
|
358
|
-
await swarm.slackReply({
|
|
359
|
-
taskId: currentTaskId,
|
|
360
|
-
message: `PR review artifact ready: ${server.url}`,
|
|
361
|
-
});
|
|
362
|
-
```
|
|
363
|
-
|
|
364
|
-
#### Step 4: Automatic cleanup
|
|
365
|
-
|
|
366
|
-
When the agent's task completes or the container restarts:
|
|
367
|
-
- PM2 manages the artifact server process
|
|
368
|
-
- The `Stop` hook in `src/hooks/hook.ts` could auto-close tunnels
|
|
369
|
-
- The service registry entry is cleaned up
|
|
370
|
-
- On container restart, PM2 auto-restarts via `ecosystem.config.cjs` from `/ecosystem` endpoint
|
|
371
|
-
|
|
372
|
-
### Integration with agent-swarm Project
|
|
373
|
-
|
|
374
|
-
Here's where artifacts hooks into the existing codebase:
|
|
375
|
-
|
|
376
|
-
```
|
|
377
|
-
src/
|
|
378
|
-
├── cli.tsx # Add `artifact` command dispatch
|
|
379
|
-
├── commands/
|
|
380
|
-
│ ├── artifact.ts # NEW: artifact serve/list/stop/open
|
|
381
|
-
│ └── runner.ts # No changes (artifacts are independent processes)
|
|
382
|
-
├── artifact-sdk/ # NEW: in-repo SDK (no npm publishing needed)
|
|
383
|
-
│ ├── index.ts # Public exports
|
|
384
|
-
│ ├── server.ts # createArtifactServer() — auto-assigns free port
|
|
385
|
-
│ ├── tunnel.ts # Localtunnel wrapper — per-artifact subdomains
|
|
386
|
-
│ ├── port.ts # Dynamic port allocation (getPort())
|
|
387
|
-
│ ├── proxy.ts # /@swarm/api/* proxy middleware
|
|
388
|
-
│ └── browser-sdk.ts # Browser SDK (bundled to sdk.js)
|
|
389
|
-
├── hooks/
|
|
390
|
-
│ └── hook.ts # Add tunnel cleanup to Stop hook
|
|
391
|
-
├── tools/
|
|
392
|
-
│ └── register-service.ts # Already handles service registration (no changes)
|
|
393
|
-
├── prompts/
|
|
394
|
-
│ └── base-prompt.ts # Add BASE_PROMPT_ARTIFACTS section
|
|
395
|
-
├── server.ts # Add 'artifacts' capability gate
|
|
396
|
-
└── http.ts # Add /@swarm/api proxy routes (or separate server)
|
|
397
|
-
|
|
398
|
-
Dockerfile.worker # Install @desplega.ai/localtunnel globally
|
|
399
|
-
docker-entrypoint.sh # Optional: auto-start tunnel on container boot
|
|
400
|
-
```
|
|
401
|
-
|
|
402
|
-
> **Why in-repo?** Agents run inside agent-swarm's Docker container, so the SDK code is already available at runtime. No npm publishing, no version drift, no extra install step. Agents import directly: `import { createArtifactServer } from '../artifact-sdk'`.
|
|
403
|
-
|
|
404
|
-
#### Specific integration points:
|
|
405
|
-
|
|
406
|
-
| Component | File | Change |
|
|
407
|
-
|-----------|------|--------|
|
|
408
|
-
| **CLI command** | `src/cli.tsx` | Add `case "artifact":` dispatch to `src/commands/artifact.ts` |
|
|
409
|
-
| **Capability** | `src/server.ts:72` | Add `"artifacts"` to default capabilities string |
|
|
410
|
-
| **Artifact SDK** | `src/artifact-sdk/` | In-repo module — no npm publishing needed. Direct import by agents |
|
|
411
|
-
| **Base prompt** | `src/prompts/base-prompt.ts` | Add `BASE_PROMPT_ARTIFACTS` with usage instructions, gated by `hasCapability("artifacts")` |
|
|
412
|
-
| **Stop hook** | `src/hooks/hook.ts` (Stop case) | Call `tunnel.close()` for all running artifact tunnels (multiple per worker) |
|
|
413
|
-
| **Service registry** | `src/tools/register-service.ts` | No changes — already supports metadata for custom service types |
|
|
414
|
-
| **Docker** | `Dockerfile.worker` | `npm install -g @desplega.ai/localtunnel` (client binary only) |
|
|
415
|
-
| **Entrypoint** | `docker-entrypoint.sh` | Optional: auto-create tunnel when PM2 starts a registered artifact service |
|
|
416
|
-
| **PM2 ecosystem** | `src/http.ts` (`/ecosystem`) | Already generates PM2 config from registered services — artifacts auto-restart on container boot |
|
|
417
|
-
|
|
418
|
-
---
|
|
419
|
-
|
|
420
|
-
## Part 2: Localtunnel Research
|
|
421
|
-
|
|
422
|
-
### Deterministic Subdomains
|
|
423
|
-
|
|
424
|
-
The client requests a specific subdomain by appending it as a URL path segment during tunnel negotiation:
|
|
425
|
-
|
|
426
|
-
```
|
|
427
|
-
# Without subdomain: GET https://lt.desplega.ai/?new
|
|
428
|
-
# With subdomain: GET https://lt.desplega.ai/my-agent-name
|
|
429
|
-
```
|
|
430
|
-
|
|
431
|
-
**CLI:**
|
|
432
|
-
```bash
|
|
433
|
-
lt --port 3000 --subdomain my-agent-name
|
|
434
|
-
# Result: https://my-agent-name.lt.desplega.ai
|
|
435
|
-
```
|
|
436
|
-
|
|
437
|
-
**Programmatic API:**
|
|
438
|
-
```javascript
|
|
439
|
-
const localtunnel = require('@desplega.ai/localtunnel');
|
|
440
|
-
|
|
441
|
-
const tunnel = await localtunnel({
|
|
442
|
-
port: 3000,
|
|
443
|
-
subdomain: 'my-agent-name',
|
|
444
|
-
// host defaults to 'https://lt.desplega.ai'
|
|
445
|
-
});
|
|
446
|
-
|
|
447
|
-
console.log(tunnel.url);
|
|
448
|
-
// https://my-agent-name.lt.desplega.ai
|
|
449
|
-
```
|
|
450
|
-
|
|
451
|
-
#### Subdomain Validation Rules (Server-Side)
|
|
452
|
-
|
|
453
|
-
The server validates subdomains with this regex:
|
|
454
|
-
|
|
455
|
-
```
|
|
456
|
-
/^(?:[a-z0-9][a-z0-9\-]{2,61}[a-z0-9]|[a-z0-9]{4,63})$/
|
|
457
|
-
```
|
|
458
|
-
|
|
459
|
-
| Rule | Detail |
|
|
460
|
-
|------|--------|
|
|
461
|
-
| Length | 4-63 characters |
|
|
462
|
-
| Allowed chars | Lowercase alphanumeric (`a-z`, `0-9`) and hyphens (`-`) |
|
|
463
|
-
| Start/end | Cannot start or end with a hyphen |
|
|
464
|
-
| Case | Must be lowercase |
|
|
465
|
-
|
|
466
|
-
#### Conflict Handling (409)
|
|
467
|
-
|
|
468
|
-
**Our fork differs from upstream here.** In upstream localtunnel, if a requested subdomain is taken, the server silently assigns a random one. Our fork returns a proper 409 Conflict:
|
|
469
|
-
|
|
470
|
-
**Server (`ClientManager.js`):**
|
|
471
|
-
```javascript
|
|
472
|
-
if (clients[id]) {
|
|
473
|
-
const err = new Error(`Subdomain '${id}' is already in use.`);
|
|
474
|
-
err.code = 'SUBDOMAIN_IN_USE';
|
|
475
|
-
throw err;
|
|
476
|
-
}
|
|
477
|
-
```
|
|
478
|
-
|
|
479
|
-
**Client (`Tunnel.js`):**
|
|
480
|
-
```javascript
|
|
481
|
-
if (err.response?.status === 409) {
|
|
482
|
-
const message = err.response.data?.message || 'Subdomain is already in use';
|
|
483
|
-
return cb(new Error(message)); // Fails immediately, no retry
|
|
484
|
-
}
|
|
485
|
-
```
|
|
486
|
-
|
|
487
|
-
#### Agent IDs as Subdomains — Tested
|
|
488
|
-
|
|
489
|
-
Agent IDs are UUIDs like `16990304-76e4-4017-b991-f3e37b34cf73`. These are 36 characters, contain only hex digits and hyphens, and are lowercase — they pass the subdomain validation regex.
|
|
490
|
-
|
|
491
|
-
**Tested against `lt.desplega.ai` on 2026-02-26:**
|
|
492
|
-
|
|
493
|
-
| Format | Example | Length | Result |
|
|
494
|
-
|--------|---------|--------|--------|
|
|
495
|
-
| Full UUID with hyphens | `16990304-76e4-4017-b991-f3e37b34cf73` | 36 | **Works** — tunnel created, HTTP 200 confirmed |
|
|
496
|
-
| Short UUID prefix | `16990304` | 8 | **Works** — tunnel created |
|
|
497
|
-
| UUID without hyphens | `1699030476e44017b991f3e37b34cf73` | 32 | **Works** — tunnel created |
|
|
498
|
-
|
|
499
|
-
```bash
|
|
500
|
-
# Actual test output:
|
|
501
|
-
$ lt --port 8787 --subdomain 16990304-76e4-4017-b991-f3e37b34cf73 --host https://lt.desplega.ai
|
|
502
|
-
your url is: https://16990304-76e4-4017-b991-f3e37b34cf73.lt.desplega.ai
|
|
503
|
-
|
|
504
|
-
$ curl -H "Bypass-Tunnel-Reminder: true" https://16990304-76e4-4017-b991-f3e37b34cf73.lt.desplega.ai
|
|
505
|
-
Hello from UUID subdomain test! # HTTP 200
|
|
506
|
-
```
|
|
507
|
-
|
|
508
|
-
No issues with UUID length, hyphens, or character set. The server validates and accepts all three formats.
|
|
509
|
-
|
|
510
|
-
**Recommendation:** Use full UUID. The URL is primarily for programmatic access, not for humans typing into browsers. The service registry provides a human-friendly lookup. This also matches the existing URL pattern: `https://{agentId}.{SWARM_URL}` (see `src/tools/register-service.ts:76`).
|
|
511
|
-
|
|
512
|
-
### HTTP Basic Auth
|
|
513
|
-
|
|
514
|
-
Auth is a feature **added by our fork** — it does not exist in upstream localtunnel.
|
|
515
|
-
|
|
516
|
-
**CLI (two modes):**
|
|
517
|
-
```bash
|
|
518
|
-
# Mode 1: Server generates an 18-char hex password
|
|
519
|
-
lt --port 3000 --subdomain my-agent --auth
|
|
520
|
-
# Username: "hi", Password: auto-generated
|
|
521
|
-
|
|
522
|
-
# Mode 2: Custom password
|
|
523
|
-
lt --port 3000 --subdomain my-agent --auth "my-secret-password"
|
|
524
|
-
# Username: "hi", Password: "my-secret-password"
|
|
525
|
-
```
|
|
526
|
-
|
|
527
|
-
**Programmatic API:**
|
|
528
|
-
```javascript
|
|
529
|
-
const tunnel = await localtunnel({
|
|
530
|
-
port: 3000,
|
|
531
|
-
subdomain: 'my-agent',
|
|
532
|
-
auth: true, // server generates password
|
|
533
|
-
// OR
|
|
534
|
-
auth: 'my-password', // custom password
|
|
535
|
-
});
|
|
536
|
-
|
|
537
|
-
// tunnel.url includes embedded credentials:
|
|
538
|
-
// https://hi:a1b2c3d4e5f6g7h8i9@my-agent.lt.desplega.ai
|
|
539
|
-
```
|
|
540
|
-
|
|
541
|
-
#### Auth Flow
|
|
542
|
-
|
|
543
|
-
1. **Client → Server (tunnel registration):** Query params `username=hi` and optionally `password=<value>` appended to the registration URL
|
|
544
|
-
2. **Server stores credentials:** If no password given, auto-generates 18-char hex string via `crypto.randomBytes(9).toString('hex')`
|
|
545
|
-
3. **Server → Client (registration response):** Credentials returned in response body and embedded in URL
|
|
546
|
-
4. **Browser/client → Server (accessing tunnel):** Standard HTTP Basic Auth — browser shows login prompt, or `Authorization: Basic <base64>` header
|
|
547
|
-
5. **Server validates:** Timing-safe comparison via `crypto.timingSafeEqual()`, returns `401 Unauthorized` with `WWW-Authenticate: Basic realm="Localtunnel"` on failure
|
|
548
|
-
6. **WebSocket auth:** Also enforced
|
|
549
|
-
|
|
550
|
-
#### Security Properties
|
|
551
|
-
|
|
552
|
-
| Property | Status |
|
|
553
|
-
|----------|--------|
|
|
554
|
-
| Timing-safe comparison | Yes (`crypto.timingSafeEqual`) |
|
|
555
|
-
| Per-tunnel isolation | Yes (each tunnel has its own credentials) |
|
|
556
|
-
| Credentials in URL | Yes (embedded in the response URL) — note: not ideal for logging |
|
|
557
|
-
| HTTPS | Yes (`lt.desplega.ai` uses TLS via Caddy) |
|
|
558
|
-
| WebSocket support | Yes (auth checked on upgrade) |
|
|
559
|
-
|
|
560
|
-
### Connection Management
|
|
561
|
-
|
|
562
|
-
| Parameter | Value | Source |
|
|
563
|
-
|-----------|-------|--------|
|
|
564
|
-
| Max TCP sockets per tunnel | 10 (hardcoded) | `TunnelAgent.js` — `--max-sockets` flag has a property name mismatch bug |
|
|
565
|
-
| Socket idle timeout | 10 minutes (default) | `TunnelAgent.js` — configurable via `SOCKET_TIMEOUT` env var (seconds) |
|
|
566
|
-
| Grace period (no connections) | 1 second | `Client.js` — tunnel removed if no client connects within 1s |
|
|
567
|
-
| WebSocket timeout | Disabled (0) | `Client.js` — WebSocket connections exempt from idle timeout |
|
|
568
|
-
|
|
569
|
-
**Auto-Reconnection (Client-Side):**
|
|
570
|
-
- Dead tunnel socket → immediately reopened
|
|
571
|
-
- Server unavailable → retry every 1s
|
|
572
|
-
- Local server refused → retry every 1s
|
|
573
|
-
- 409 subdomain conflict → immediate failure (no retry)
|
|
574
|
-
|
|
575
|
-
**Graceful Shutdown (fork addition):**
|
|
576
|
-
- `tunnel.close()` is async, destroys all resources
|
|
577
|
-
- CLI handles `SIGINT`/`SIGTERM`
|
|
578
|
-
|
|
579
|
-
**Env var configuration:** All CLI flags can be set via environment variables (yargs `.env(true)`).
|
|
580
|
-
|
|
581
|
-
---
|
|
582
|
-
|
|
583
|
-
## Part 3: Fork Modifications vs Upstream
|
|
584
|
-
|
|
585
|
-
### Client (`desplega-ai/localtunnel` v2.2.0)
|
|
586
|
-
|
|
587
|
-
| Modification | Description |
|
|
588
|
-
|-------------|-------------|
|
|
589
|
-
| **Default host** | Changed from `localtunnel.me` to `lt.desplega.ai` |
|
|
590
|
-
| **`--auth` flag** | New: HTTP Basic Auth support (boolean or string) |
|
|
591
|
-
| **409 handling** | New: subdomain conflict returns immediate error instead of retrying |
|
|
592
|
-
| **Graceful shutdown** | New: async `close()` with full resource cleanup |
|
|
593
|
-
| **Test suite** | Migrated from Mocha (JS) to Bun (TypeScript) |
|
|
594
|
-
| **Package name** | `@desplega.ai/localtunnel` (scoped) |
|
|
595
|
-
|
|
596
|
-
### Server (`desplega-ai/localtunnel-server` v0.1.3)
|
|
597
|
-
|
|
598
|
-
| Modification | Description |
|
|
599
|
-
|-------------|-------------|
|
|
600
|
-
| **HTTP Basic Auth** | Full implementation: `authUtils.js` + per-tunnel credential storage + timing-safe validation |
|
|
601
|
-
| **409 Conflict** | Returns proper 409 for subdomain collisions |
|
|
602
|
-
| **Socket timeout** | Increased from 60s to 10 minutes, configurable via `SOCKET_TIMEOUT` env var |
|
|
603
|
-
| **X-Forwarded-Proto/Host** | Sets forwarded headers for HTTPS-aware apps |
|
|
604
|
-
| **X-Robots-Tag** | All tunnel responses include `noindex, nofollow` |
|
|
605
|
-
| **Agent IP tracking** | `X-Localtunnel-Agent-Ips` header on responses |
|
|
606
|
-
| **ESM** | Converted to ES modules |
|
|
607
|
-
| **Docker** | Dockerfile + compose config, GHCR publishing |
|
|
608
|
-
| **CI/CD** | Test, E2E, and Docker publish workflows |
|
|
609
|
-
|
|
610
|
-
---
|
|
611
|
-
|
|
612
|
-
## Part 4: Design Decisions
|
|
613
|
-
|
|
614
|
-
### Auth Strategy
|
|
615
|
-
|
|
616
|
-
| Option | Description | Pros | Cons |
|
|
617
|
-
|--------|-------------|------|------|
|
|
618
|
-
| Per-tunnel random password | `auth: true` (server generates) | Zero config | Need to store/share the generated password |
|
|
619
|
-
| **user=`swarm`, pass=API key** | Use existing API key as tunnel password | Simple, no new secrets, already available in env | API key shared across all tunnels |
|
|
620
|
-
| Per-agent HMAC-derived password | `auth: hash(agentId + swarmSecret)` | Per-agent isolation, deterministic | Requires separate secret management |
|
|
621
|
-
|
|
622
|
-
**Recommendation:** Use `username=swarm`, `password=API_KEY` as the default. The API key (`API_KEY` env var) is already available in every worker container — it's set in `docker-entrypoint.sh` and used for MCP server auth. This means zero additional configuration.
|
|
623
|
-
|
|
624
|
-
For enhanced security in production, an HMAC-derived per-agent password (`crypto.createHmac('sha256', API_KEY).update(agentId).digest('hex')`) would provide per-agent isolation — if one tunnel's password leaks, others aren't compromised. But for MVP, the API key approach is simpler and sufficient.
|
|
625
|
-
|
|
626
|
-
> **Note:** The localtunnel client currently hardcodes `username="hi"`. To use `username="swarm"`, the client's `Tunnel.js` needs a minor patch to accept a custom username option. Alternatively, the server could be configured to only check the password (since the username is less important for security).
|
|
627
|
-
|
|
628
|
-
### Lifecycle Management
|
|
629
|
-
|
|
630
|
-
1. **Container restart:** Server frees subdomain after 1s grace. Agent reconnects with same subdomain immediately.
|
|
631
|
-
2. **Server restart:** All tunnels lost. Client retry loop handles reconnection (1s interval).
|
|
632
|
-
3. **Stale subdomains:** Held up to 10 minutes (socket timeout). Acceptable for MVP.
|
|
633
|
-
|
|
634
|
-
### Implementation Sketch
|
|
635
|
-
|
|
636
|
-
```typescript
|
|
637
|
-
// src/commands/artifact.ts — core artifact serve logic
|
|
638
|
-
import localtunnel from '@desplega.ai/localtunnel';
|
|
639
|
-
|
|
640
|
-
const AGENT_ID = process.env.AGENT_ID;
|
|
641
|
-
const API_KEY = process.env.API_KEY;
|
|
642
|
-
const MCP_BASE_URL = process.env.MCP_BASE_URL;
|
|
643
|
-
|
|
644
|
-
// Create tunnel with swarm auth
|
|
645
|
-
const tunnel = await localtunnel({
|
|
646
|
-
port: 3000,
|
|
647
|
-
subdomain: AGENT_ID,
|
|
648
|
-
auth: API_KEY, // username defaults to "hi", password = API key
|
|
649
|
-
// TODO: patch client to support username: 'swarm'
|
|
650
|
-
});
|
|
651
|
-
|
|
652
|
-
console.log(`Artifact URL: ${tunnel.url}`);
|
|
653
|
-
// https://hi:<API_KEY>@<agentId>.lt.desplega.ai
|
|
654
|
-
|
|
655
|
-
// Register in service registry via HTTP API
|
|
656
|
-
await fetch(`${MCP_BASE_URL}/api/services`, {
|
|
657
|
-
method: 'POST',
|
|
658
|
-
headers: {
|
|
659
|
-
'Authorization': `Bearer ${API_KEY}`,
|
|
660
|
-
'X-Agent-ID': AGENT_ID,
|
|
661
|
-
'Content-Type': 'application/json',
|
|
662
|
-
},
|
|
663
|
-
body: JSON.stringify({
|
|
664
|
-
script: '/workspace/personal/artifacts/server.js',
|
|
665
|
-
metadata: {
|
|
666
|
-
type: 'artifact',
|
|
667
|
-
artifactName: 'pr-review-42',
|
|
668
|
-
publicUrl: tunnel.url,
|
|
669
|
-
},
|
|
670
|
-
}),
|
|
671
|
-
});
|
|
672
|
-
|
|
673
|
-
// Graceful shutdown
|
|
674
|
-
process.on('SIGTERM', async () => {
|
|
675
|
-
await tunnel.close();
|
|
676
|
-
// Service unregistered via Stop hook
|
|
677
|
-
});
|
|
678
|
-
```
|
|
679
|
-
|
|
680
|
-
---
|
|
681
|
-
|
|
682
|
-
## Part 5: Known Issues & Risks
|
|
683
|
-
|
|
684
|
-
| Issue | Severity | Mitigation |
|
|
685
|
-
|-------|----------|------------|
|
|
686
|
-
| **10 max TCP sockets per tunnel** | Medium | Fine for artifacts. Fix the property name bug (`maxSockets` → `maxTcpSockets`). |
|
|
687
|
-
| **1-second grace period** | Low | Good for subdomain reuse, no overlap possible. |
|
|
688
|
-
| **No heartbeat mechanism** | Medium | Stale subdomains held up to 10 minutes. Acceptable for MVP. |
|
|
689
|
-
| **Hardcoded username "hi"** | Medium | Need to patch client to support `username: 'swarm'` for cleaner auth. Alternatively, server could validate password only. |
|
|
690
|
-
| **HTTP only (no raw TCP/UDP)** | Low | Artifacts are HTTP-served. Fine. |
|
|
691
|
-
| **No wildcard DNS guarantee** | Medium | Requires `*.lt.desplega.ai` DNS via Caddy. |
|
|
692
|
-
|
|
693
|
-
### `maxSockets` Bug Fix Needed
|
|
694
|
-
|
|
695
|
-
In `desplega-ai/localtunnel-server`, `ClientManager.js:43-46`:
|
|
696
|
-
|
|
697
|
-
```javascript
|
|
698
|
-
// Current (buggy):
|
|
699
|
-
const agent = new TunnelAgent({ clientId: id, maxSockets: 10 });
|
|
700
|
-
|
|
701
|
-
// Fix:
|
|
702
|
-
const agent = new TunnelAgent({ clientId: id, maxTcpSockets: maxSockets });
|
|
703
|
-
```
|
|
704
|
-
|
|
705
|
-
---
|
|
706
|
-
|
|
707
|
-
## Part 6: Next Steps
|
|
708
|
-
|
|
709
|
-
### Phase 1: Foundation (MVP)
|
|
710
|
-
1. **Patch localtunnel client** — add `username` option to `@desplega.ai/localtunnel` so we can use `swarm` instead of `hi`
|
|
711
|
-
2. **Fix the `maxSockets` property name bug** in `desplega-ai/localtunnel-server`
|
|
712
|
-
3. **Create `src/artifact-sdk/`** in-repo module with `createArtifactServer()`, dynamic port allocation, static serving, and tunnel management
|
|
713
|
-
4. **Add `artifact` CLI command** to `src/cli.tsx` with `serve`, `list`, `stop` subcommands
|
|
714
|
-
|
|
715
|
-
### Phase 2: Interactivity
|
|
716
|
-
5. **Build the browser SDK** (`/@swarm/sdk.js`) for HTML → swarm API calls
|
|
717
|
-
6. **Add `/@swarm/api/*` proxy** middleware in the artifact server
|
|
718
|
-
7. **Add `BASE_PROMPT_ARTIFACTS`** section to `src/prompts/base-prompt.ts`
|
|
719
|
-
|
|
720
|
-
### Phase 3: Polish
|
|
721
|
-
8. **Add tunnel cleanup to Stop hook** in `src/hooks/hook.ts`
|
|
722
|
-
9. **Add `artifact open`** command that opens URL with embedded credentials
|
|
723
|
-
10. **Install `@desplega.ai/localtunnel`** globally in `Dockerfile.worker`
|
|
724
|
-
11. **Health check endpoint** — `/health` on each artifact for monitoring
|