@fastagent-sh/fastagent 0.24.4 → 0.26.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 +54 -82
- package/dist/agent-api.d.ts +460 -0
- package/dist/agent-api.js +209 -0
- package/dist/answer-text.d.ts +8 -0
- package/dist/answer-text.js +18 -0
- package/dist/atomic-write.d.ts +2 -2
- package/dist/atomic-write.js +2 -2
- package/dist/authoring.d.ts +81 -0
- package/dist/authoring.js +245 -0
- package/dist/channel.d.ts +29 -15
- package/dist/channel.js +4 -1
- package/dist/channels/agentcore-protocol.d.ts +36 -50
- package/dist/channels/agentcore-protocol.js +3 -3
- package/dist/channels/agentcore-service.d.ts +8 -18
- package/dist/channels/agentcore-service.js +108 -127
- package/dist/channels/agentcore.d.ts +8 -24
- package/dist/channels/agentcore.js +32 -65
- package/dist/channels/body.d.ts +0 -16
- package/dist/channels/body.js +0 -25
- package/dist/channels/busy.js +6 -1
- package/dist/channels/context.d.ts +4 -0
- package/dist/channels/context.js +35 -0
- package/dist/channels/discover.d.ts +18 -20
- package/dist/channels/discover.js +41 -99
- package/dist/channels/feishu/feishu-api.d.ts +54 -4
- package/dist/channels/feishu/feishu-api.js +192 -149
- package/dist/channels/feishu/feishu.d.ts +3 -0
- package/dist/channels/feishu/feishu.js +117 -106
- package/dist/channels/feishu/history.d.ts +55 -0
- package/dist/channels/feishu/history.js +199 -0
- package/dist/channels/feishu/invoke-turn.d.ts +13 -11
- package/dist/channels/feishu/invoke-turn.js +9 -10
- package/dist/channels/feishu/model.d.ts +2 -1
- package/dist/channels/feishu/normalize.js +100 -0
- package/dist/channels/feishu/preview.d.ts +2 -2
- package/dist/channels/feishu/register-webhook.d.ts +1 -0
- package/dist/channels/feishu/register-webhook.js +3 -2
- package/dist/channels/feishu/scaffold/channel.ts +65 -46
- package/dist/channels/feishu/scaffold/feishu-send.ts +38 -33
- package/dist/channels/feishu/scaffold/feishu-threads.ts +25 -0
- package/dist/channels/feishu/setup-mode.d.ts +45 -25
- package/dist/channels/feishu/setup-mode.js +74 -23
- package/dist/channels/feishu/shared-api.d.ts +24 -6
- package/dist/channels/feishu/shared-api.js +30 -24
- package/dist/channels/kit/context-buffer.d.ts +11 -2
- package/dist/channels/kit/context-buffer.js +7 -4
- package/dist/channels/kit/delivery.d.ts +19 -3
- package/dist/channels/kit/delivery.js +74 -15
- package/dist/channels/kit/event-stream.js +2 -1
- package/dist/channels/kit/invoke-turn-kit.d.ts +41 -41
- package/dist/channels/kit/invoke-turn-kit.js +62 -71
- package/dist/channels/kit/place-history.d.ts +107 -0
- package/dist/channels/kit/place-history.js +182 -0
- package/dist/channels/kit/preview-kit.d.ts +9 -5
- package/dist/channels/kit/preview-kit.js +36 -15
- package/dist/channels/kit/room-threads.d.ts +46 -0
- package/dist/channels/kit/room-threads.js +81 -0
- package/dist/channels/kit/seen.js +1 -1
- package/dist/channels/kit/stop-command.d.ts +6 -3
- package/dist/channels/kit/stop-command.js +20 -18
- package/dist/channels/kit/thread-fork.d.ts +17 -0
- package/dist/channels/kit/thread-fork.js +43 -0
- package/dist/channels/kit/thread-participants.js +1 -1
- package/dist/channels/kit/transport.d.ts +5 -0
- package/dist/channels/kit/transport.js +5 -0
- package/dist/channels/kit/turn-runner.d.ts +10 -9
- package/dist/channels/kit/turn-runner.js +17 -19
- package/dist/channels/kit/turn-store.d.ts +5 -4
- package/dist/channels/kit/turn-store.js +5 -5
- package/dist/channels/lark/lark.d.ts +3 -0
- package/dist/channels/lark/lark.js +5 -0
- package/dist/channels/lark/onboard.d.ts +5 -3
- package/dist/channels/lark/onboard.js +14 -14
- package/dist/channels/lark/scaffold/channel.ts +67 -44
- package/dist/channels/lark/scaffold/lark-send.ts +38 -33
- package/dist/channels/lark/scaffold/lark-threads.ts +25 -0
- package/dist/channels/respond.d.ts +0 -4
- package/dist/channels/respond.js +1 -1
- package/dist/channels/router.d.ts +18 -0
- package/dist/channels/router.js +68 -0
- package/dist/channels/serve.d.ts +5 -70
- package/dist/channels/serve.js +105 -287
- package/dist/channels/slack/history.d.ts +42 -0
- package/dist/channels/slack/history.js +282 -0
- package/dist/channels/slack/invoke-turn.d.ts +12 -5
- package/dist/channels/slack/invoke-turn.js +7 -5
- package/dist/channels/slack/manifest.d.ts +1 -4
- package/dist/channels/slack/manifest.js +22 -10
- package/dist/channels/slack/model.d.ts +2 -1
- package/dist/channels/slack/onboard.d.ts +0 -2
- package/dist/channels/slack/onboard.js +2 -4
- package/dist/channels/slack/onboarding-state.d.ts +0 -2
- package/dist/channels/slack/onboarding-state.js +0 -1
- package/dist/channels/slack/parse.d.ts +0 -5
- package/dist/channels/slack/parse.js +1 -16
- package/dist/channels/slack/preview.d.ts +2 -3
- package/dist/channels/slack/preview.js +78 -52
- package/dist/channels/slack/register-webhook.d.ts +1 -0
- package/dist/channels/slack/register-webhook.js +6 -5
- package/dist/channels/slack/scaffold/channel.ts +23 -24
- package/dist/channels/slack/scaffold/slack-send.ts +50 -43
- package/dist/channels/slack/scaffold/slack-threads.ts +29 -0
- package/dist/channels/slack/setup-server.d.ts +19 -0
- package/dist/channels/slack/setup-server.js +113 -11
- package/dist/channels/slack/shared-api.d.ts +15 -4
- package/dist/channels/slack/shared-api.js +19 -14
- package/dist/channels/slack/slack-api.d.ts +61 -2
- package/dist/channels/slack/slack-api.js +28 -5
- package/dist/channels/slack/slack.js +97 -52
- package/dist/channels/slack/welcomed.js +1 -1
- package/dist/channels/telegram/context-buffer.d.ts +6 -1
- package/dist/channels/telegram/context-buffer.js +19 -0
- package/dist/channels/telegram/invoke-turn.d.ts +8 -4
- package/dist/channels/telegram/invoke-turn.js +7 -7
- package/dist/channels/telegram/parse.d.ts +7 -2
- package/dist/channels/telegram/parse.js +7 -0
- package/dist/channels/telegram/preview.d.ts +2 -2
- package/dist/channels/telegram/register-webhook.d.ts +1 -0
- package/dist/channels/telegram/register-webhook.js +3 -2
- package/dist/channels/telegram/scaffold/channel.ts +27 -28
- package/dist/channels/telegram/scaffold/telegram-send.ts +61 -108
- package/dist/channels/telegram/shared-api.d.ts +34 -0
- package/dist/channels/telegram/shared-api.js +79 -0
- package/dist/channels/telegram/telegram-api.d.ts +15 -5
- package/dist/channels/telegram/telegram-api.js +10 -6
- package/dist/channels/telegram/telegram.js +27 -21
- package/dist/channels/telegram/turn-store.js +1 -1
- package/dist/channels/{kit/turn-queue.d.ts → turn-queue.d.ts} +2 -2
- package/dist/channels/{kit/turn-queue.js → turn-queue.js} +10 -10
- package/dist/cli/add-feishu.d.ts +42 -12
- package/dist/cli/add-feishu.js +177 -124
- package/dist/cli/add-slack.d.ts +8 -3
- package/dist/cli/add-slack.js +116 -64
- package/dist/cli/auth-view.d.ts +10 -3
- package/dist/cli/auth-view.js +8 -6
- package/dist/cli/box-login.d.ts +18 -7
- package/dist/cli/box-login.js +46 -12
- package/dist/cli/commands/add.d.ts +15 -4
- package/dist/cli/commands/add.js +253 -143
- package/dist/cli/commands/auth.d.ts +6 -0
- package/dist/cli/commands/auth.js +50 -0
- package/dist/cli/commands/content.d.ts +6 -0
- package/dist/cli/commands/content.js +45 -0
- package/dist/cli/commands/deploy/agentcore.js +50 -77
- package/dist/cli/commands/deploy/docker.js +38 -39
- package/dist/cli/commands/deploy/fly.js +21 -18
- package/dist/cli/commands/deploy/host-login.d.ts +26 -0
- package/dist/cli/commands/deploy/host-login.js +90 -0
- package/dist/cli/commands/deploy/railway.js +21 -26
- package/dist/cli/commands/deploy/shared.d.ts +45 -17
- package/dist/cli/commands/deploy/shared.js +34 -15
- package/dist/cli/commands/deploy.d.ts +0 -4
- package/dist/cli/commands/deploy.js +237 -50
- package/dist/cli/commands/destroy.js +8 -6
- package/dist/cli/commands/dev.d.ts +0 -2
- package/dist/cli/commands/dev.js +20 -19
- package/dist/cli/commands/env.d.ts +12 -0
- package/dist/cli/commands/env.js +191 -0
- package/dist/cli/commands/info.js +95 -156
- package/dist/cli/commands/init.d.ts +2 -2
- package/dist/cli/commands/init.js +35 -33
- package/dist/cli/commands/invoke.d.ts +6 -0
- package/dist/cli/commands/invoke.js +132 -16
- package/dist/cli/commands/login.d.ts +31 -2
- package/dist/cli/commands/login.js +416 -65
- package/dist/cli/commands/logout.d.ts +4 -0
- package/dist/cli/commands/logout.js +53 -0
- package/dist/cli/commands/logs.js +8 -6
- package/dist/cli/commands/models.d.ts +2 -1
- package/dist/cli/commands/models.js +39 -37
- package/dist/cli/commands/schedules.d.ts +21 -0
- package/dist/cli/commands/schedules.js +143 -0
- package/dist/cli/commands/start.d.ts +11 -10
- package/dist/cli/commands/start.js +35 -62
- package/dist/cli/content-view.d.ts +9 -0
- package/dist/cli/content-view.js +26 -0
- package/dist/cli/fail.d.ts +9 -12
- package/dist/cli/fail.js +17 -14
- package/dist/cli/invoke-stream.d.ts +12 -3
- package/dist/cli/invoke-stream.js +85 -23
- package/dist/cli/kernel.d.ts +9 -2
- package/dist/cli/kernel.js +15 -6
- package/dist/cli/login-relay.d.ts +6 -4
- package/dist/cli/login-relay.js +19 -7
- package/dist/cli/models-view.d.ts +5 -1
- package/dist/cli/models-view.js +1 -1
- package/dist/cli/person.d.ts +10 -0
- package/dist/cli/person.js +23 -0
- package/dist/cli/program.js +421 -203
- package/dist/cli/serve.d.ts +13 -63
- package/dist/cli/serve.js +31 -121
- package/dist/cli/shared.d.ts +21 -48
- package/dist/cli/shared.js +70 -89
- package/dist/cli/ticket.d.ts +136 -0
- package/dist/cli/ticket.js +476 -0
- package/dist/client.d.ts +7 -0
- package/dist/client.js +2 -0
- package/dist/content/declare.d.ts +70 -0
- package/dist/content/declare.js +163 -0
- package/dist/content/file.d.ts +32 -0
- package/dist/content/file.js +119 -0
- package/dist/content/git.d.ts +41 -0
- package/dist/content/git.js +220 -0
- package/dist/content/mount.d.ts +2 -0
- package/dist/content/mount.js +15 -0
- package/dist/content/resolve.d.ts +52 -0
- package/dist/content/resolve.js +151 -0
- package/dist/content/source.d.ts +27 -0
- package/dist/content/source.js +53 -0
- package/dist/conversation-sessions.d.ts +26 -0
- package/dist/conversation-sessions.js +99 -0
- package/dist/declared-secrets.d.ts +19 -37
- package/dist/declared-secrets.js +16 -58
- package/dist/deploy/agentcore/destroy.d.ts +3 -3
- package/dist/deploy/agentcore/destroy.js +46 -28
- package/dist/deploy/agentcore/forwarder.js +105 -22
- package/dist/deploy/agentcore/logs.js +1 -1
- package/dist/deploy/agentcore/plan.d.ts +10 -24
- package/dist/deploy/agentcore/plan.js +31 -135
- package/dist/deploy/agentcore/run.d.ts +1 -1
- package/dist/deploy/agentcore/run.js +8 -5
- package/dist/deploy/agentcore/shell.d.ts +1 -1
- package/dist/deploy/agentcore/shell.js +5 -5
- package/dist/deploy/api.d.ts +34 -0
- package/dist/deploy/api.js +50 -0
- package/dist/deploy/box-shell.d.ts +6 -0
- package/dist/deploy/box-shell.js +7 -0
- package/dist/deploy/build-context.d.ts +7 -5
- package/dist/deploy/build-context.js +41 -35
- package/dist/deploy/channel-ingress.d.ts +6 -1
- package/dist/deploy/channel-ingress.js +18 -5
- package/dist/deploy/container.d.ts +22 -9
- package/dist/deploy/container.js +96 -55
- package/dist/deploy/docker/plan.d.ts +3 -2
- package/dist/deploy/docker/plan.js +13 -14
- package/dist/deploy/docker/run.d.ts +2 -2
- package/dist/deploy/docker/run.js +8 -4
- package/dist/deploy/fly/plan.d.ts +1 -1
- package/dist/deploy/fly/plan.js +12 -21
- package/dist/deploy/fly/run.d.ts +10 -6
- package/dist/deploy/fly/run.js +11 -6
- package/dist/deploy/hosts.d.ts +2 -1
- package/dist/deploy/hosts.js +2 -1
- package/dist/deploy/preflight.d.ts +21 -23
- package/dist/deploy/preflight.js +223 -202
- package/dist/deploy/railway/plan.d.ts +8 -6
- package/dist/deploy/railway/plan.js +15 -58
- package/dist/deploy/railway/run.d.ts +8 -3
- package/dist/deploy/railway/run.js +12 -8
- package/dist/deploy/residency.d.ts +5 -11
- package/dist/deploy/residency.js +0 -6
- package/dist/deploy/runner.d.ts +1 -1
- package/dist/deploy/runner.js +1 -1
- package/dist/deploy/secrets.d.ts +16 -2
- package/dist/deploy/secrets.js +33 -6
- package/dist/deploy/workspace.d.ts +11 -6
- package/dist/deploy/workspace.js +55 -38
- package/dist/dev-supervisor.d.ts +11 -2
- package/dist/dev-supervisor.js +52 -27
- package/dist/effect-port.d.ts +4 -4
- package/dist/effect-port.js +4 -4
- package/dist/env.d.ts +0 -13
- package/dist/env.js +1 -44
- package/dist/environment/apt.d.ts +4 -0
- package/dist/environment/apt.js +32 -0
- package/dist/environment/declare.d.ts +59 -0
- package/dist/environment/declare.js +210 -0
- package/dist/environment/mise.d.ts +51 -0
- package/dist/environment/mise.js +272 -0
- package/dist/feishu.d.ts +2 -2
- package/dist/feishu.js +2 -2
- package/dist/git.d.ts +13 -0
- package/dist/git.js +55 -0
- package/dist/harnesses/models/catalog.d.ts +20 -0
- package/dist/harnesses/models/catalog.js +254 -0
- package/dist/{engines/pi/auth.d.ts → harnesses/models/credentials.d.ts} +3 -3
- package/dist/{engines/pi/auth.js → harnesses/models/credentials.js} +3 -3
- package/dist/harnesses/models/environment.d.ts +48 -0
- package/dist/harnesses/models/environment.js +88 -0
- package/dist/harnesses/models/host.d.ts +30 -0
- package/dist/harnesses/models/host.js +97 -0
- package/dist/{engines/pi → harnesses/models}/login.d.ts +66 -70
- package/dist/harnesses/models/login.js +439 -0
- package/dist/harnesses/models/models.d.ts +84 -0
- package/dist/harnesses/models/models.js +197 -0
- package/dist/harnesses/pi-durable/agent.d.ts +42 -0
- package/dist/harnesses/pi-durable/agent.js +581 -0
- package/dist/harnesses/pi-durable/defaults.d.ts +28 -0
- package/dist/harnesses/pi-durable/defaults.js +26 -0
- package/dist/harnesses/pi-durable/define.d.ts +77 -0
- package/dist/harnesses/pi-durable/define.js +28 -0
- package/dist/harnesses/pi-durable/entries.d.ts +32 -0
- package/dist/harnesses/pi-durable/entries.js +126 -0
- package/dist/harnesses/pi-durable/events.d.ts +25 -0
- package/dist/harnesses/pi-durable/events.js +238 -0
- package/dist/harnesses/pi-durable/ids.d.ts +16 -0
- package/dist/harnesses/pi-durable/ids.js +55 -0
- package/dist/harnesses/pi-durable/messages.d.ts +41 -0
- package/dist/harnesses/pi-durable/messages.js +116 -0
- package/dist/harnesses/pi-durable/open.d.ts +95 -0
- package/dist/harnesses/pi-durable/open.js +330 -0
- package/dist/harnesses/pi-durable/reads.d.ts +30 -0
- package/dist/harnesses/pi-durable/reads.js +94 -0
- package/dist/harnesses/pi-durable/registry.d.ts +18 -0
- package/dist/harnesses/pi-durable/registry.js +56 -0
- package/dist/harnesses/pi-durable/release.d.ts +13 -0
- package/dist/harnesses/pi-durable/release.js +40 -0
- package/dist/harnesses/pi-durable/serve.d.ts +90 -0
- package/dist/harnesses/pi-durable/serve.js +186 -0
- package/dist/harnesses/pi-durable/sessions.d.ts +27 -0
- package/dist/harnesses/pi-durable/sessions.js +47 -0
- package/dist/harnesses/pi-durable/storage.d.ts +4 -0
- package/dist/harnesses/pi-durable/storage.js +49 -0
- package/dist/harnesses/pi-durable/stream.d.ts +20 -0
- package/dist/harnesses/pi-durable/stream.js +221 -0
- package/dist/harnesses/pi-durable/text.d.ts +30 -0
- package/dist/harnesses/pi-durable/text.js +292 -0
- package/dist/harnesses/pi-durable/wake.d.ts +11 -0
- package/dist/harnesses/pi-durable/wake.js +90 -0
- package/dist/http/client.d.ts +6 -0
- package/dist/http/client.js +256 -0
- package/dist/http/contract.d.ts +541 -0
- package/dist/http/contract.js +179 -0
- package/dist/http/server.d.ts +34 -0
- package/dist/http/server.js +194 -0
- package/dist/index.d.ts +19 -4
- package/dist/index.js +13 -5
- package/dist/lark.d.ts +2 -2
- package/dist/lark.js +2 -2
- package/dist/locked-file.d.ts +17 -0
- package/dist/{engines/pi/locked-file.js → locked-file.js} +16 -10
- package/dist/once.d.ts +6 -0
- package/dist/once.js +23 -0
- package/dist/paths.d.ts +36 -52
- package/dist/paths.js +66 -182
- package/dist/runtime.d.ts +25 -2
- package/dist/runtime.js +27 -0
- package/dist/scaffold/add-channel.d.ts +14 -27
- package/dist/scaffold/add-channel.js +82 -180
- package/dist/scaffold/init.d.ts +17 -18
- package/dist/scaffold/init.js +87 -99
- package/dist/scaffold/templates/SYSTEM.md +12 -0
- package/dist/scaffold/templates/env.example +4 -10
- package/dist/scaffold/templates/fastagent.config.ts +21 -33
- package/dist/scaffold/templates/gitignore +8 -1
- package/dist/scaffold/templates/skills/writing-great-skills/SKILL.md +1 -2
- package/dist/scaffold/templates.d.ts +9 -4
- package/dist/scaffold/templates.js +7 -5
- package/dist/scaffold/vendor-skill.d.ts +1 -4
- package/dist/scaffold/vendor-skill.js +5 -6
- package/dist/schedule/cron.d.ts +13 -0
- package/dist/schedule/cron.js +52 -0
- package/dist/schedule/eventbridge-cron.d.ts +17 -0
- package/dist/schedule/eventbridge-cron.js +88 -0
- package/dist/schedule/schedule.d.ts +23 -0
- package/dist/schedule/schedule.js +34 -0
- package/dist/schedule/scheduler.d.ts +34 -33
- package/dist/schedule/scheduler.js +251 -137
- package/dist/schedule/state.d.ts +4 -14
- package/dist/schedule/state.js +7 -7
- package/dist/schedule/wake-alarm.d.ts +22 -2
- package/dist/schedule/wake-alarm.js +66 -20
- package/dist/schedule/wakeups.d.ts +16 -15
- package/dist/schedule/wakeups.js +47 -45
- package/dist/service.d.ts +56 -169
- package/dist/service.js +91 -187
- package/dist/serving-command.d.ts +10 -0
- package/dist/serving-command.js +16 -0
- package/dist/sign-in-api.d.ts +147 -0
- package/dist/sign-in-api.js +9 -0
- package/dist/slack.d.ts +1 -1
- package/dist/slack.js +1 -1
- package/dist/{channels/kit/state.js → state-file.js} +6 -6
- package/dist/telegram.d.ts +1 -0
- package/dist/telegram.js +1 -0
- package/dist/tunnel.d.ts +3 -0
- package/dist/tunnel.js +14 -5
- package/dist/value-file.d.ts +40 -0
- package/dist/value-file.js +164 -0
- package/dist/version.d.ts +5 -0
- package/dist/version.js +14 -1
- package/package.json +18 -28
- package/dist/agent.d.ts +0 -106
- package/dist/agent.js +0 -21
- package/dist/channels/control.d.ts +0 -31
- package/dist/channels/control.js +0 -329
- package/dist/channels/define-channel.d.ts +0 -39
- package/dist/channels/define-channel.js +0 -9
- package/dist/channels/feishu/context-buffer.d.ts +0 -35
- package/dist/channels/feishu/context-buffer.js +0 -65
- package/dist/channels/feishu/scaffold/channel.websocket.ts +0 -31
- package/dist/channels/http.d.ts +0 -23
- package/dist/channels/http.js +0 -57
- package/dist/channels/lark/scaffold/channel.websocket.ts +0 -31
- package/dist/channels/slack/context-buffer.d.ts +0 -23
- package/dist/channels/slack/context-buffer.js +0 -37
- package/dist/channels/sse.d.ts +0 -4
- package/dist/channels/sse.js +0 -66
- package/dist/cli/commands/chat.d.ts +0 -3
- package/dist/cli/commands/chat.js +0 -14
- package/dist/cli/commands/routine-run.d.ts +0 -6
- package/dist/cli/commands/routine-run.js +0 -46
- package/dist/cli/commands/routine.d.ts +0 -25
- package/dist/cli/commands/routine.js +0 -122
- package/dist/cli/commands/tool.d.ts +0 -1
- package/dist/cli/commands/tool.js +0 -57
- package/dist/collect.d.ts +0 -63
- package/dist/collect.js +0 -90
- package/dist/core.d.ts +0 -13
- package/dist/core.js +0 -15
- package/dist/engines/pi/agent-models.d.ts +0 -83
- package/dist/engines/pi/agent-models.js +0 -120
- package/dist/engines/pi/agent-session-factory.d.ts +0 -121
- package/dist/engines/pi/agent-session-factory.js +0 -535
- package/dist/engines/pi/chat.d.ts +0 -3
- package/dist/engines/pi/chat.js +0 -8
- package/dist/engines/pi/config.d.ts +0 -71
- package/dist/engines/pi/config.js +0 -199
- package/dist/engines/pi/create.d.ts +0 -170
- package/dist/engines/pi/create.js +0 -343
- package/dist/engines/pi/definition.d.ts +0 -74
- package/dist/engines/pi/definition.js +0 -236
- package/dist/engines/pi/entry-images.d.ts +0 -14
- package/dist/engines/pi/entry-images.js +0 -23
- package/dist/engines/pi/invoke-session.d.ts +0 -18
- package/dist/engines/pi/invoke-session.js +0 -403
- package/dist/engines/pi/locked-file.d.ts +0 -9
- package/dist/engines/pi/login.js +0 -331
- package/dist/engines/pi/machine.d.ts +0 -53
- package/dist/engines/pi/machine.js +0 -137
- package/dist/engines/pi/models.d.ts +0 -200
- package/dist/engines/pi/models.js +0 -533
- package/dist/engines/pi/open.d.ts +0 -180
- package/dist/engines/pi/open.js +0 -270
- package/dist/engines/pi/openai-account-models.d.ts +0 -35
- package/dist/engines/pi/openai-account-models.js +0 -116
- package/dist/engines/pi/report.d.ts +0 -18
- package/dist/engines/pi/report.js +0 -59
- package/dist/engines/pi/retry-event.d.ts +0 -6
- package/dist/engines/pi/retry-event.js +0 -15
- package/dist/engines/pi/service.d.ts +0 -9
- package/dist/engines/pi/service.js +0 -14
- package/dist/engines/pi/session-builder.d.ts +0 -13
- package/dist/engines/pi/session-builder.js +0 -117
- package/dist/engines/pi/session-control.d.ts +0 -77
- package/dist/engines/pi/session-control.js +0 -908
- package/dist/engines/pi/session-effects.d.ts +0 -14
- package/dist/engines/pi/session-effects.js +0 -30
- package/dist/engines/pi/session-inheritance.d.ts +0 -21
- package/dist/engines/pi/session-inheritance.js +0 -204
- package/dist/engines/pi/session-markers.d.ts +0 -49
- package/dist/engines/pi/session-markers.js +0 -60
- package/dist/engines/pi/session-settings.d.ts +0 -67
- package/dist/engines/pi/session-settings.js +0 -119
- package/dist/engines/pi/session-store.d.ts +0 -69
- package/dist/engines/pi/session-store.js +0 -430
- package/dist/engines/pi/tool-context.d.ts +0 -45
- package/dist/engines/pi/tool-context.js +0 -31
- package/dist/engines/pi/tool.d.ts +0 -99
- package/dist/engines/pi/tool.js +0 -118
- package/dist/engines/pi/turn-kit.d.ts +0 -92
- package/dist/engines/pi/turn-kit.js +0 -288
- package/dist/engines/pi/wake-tool.d.ts +0 -9
- package/dist/engines/pi/wake-tool.js +0 -92
- package/dist/loader.d.ts +0 -41
- package/dist/loader.js +0 -116
- package/dist/node.d.ts +0 -3
- package/dist/node.js +0 -4
- package/dist/observe.d.ts +0 -6
- package/dist/observe.js +0 -47
- package/dist/pi.d.ts +0 -25
- package/dist/pi.js +0 -15
- package/dist/scaffold/templates/APPEND_SYSTEM.md +0 -12
- package/dist/scaffold/templates/tools/fetch-url.ts +0 -25
- package/dist/schedule/discover.d.ts +0 -17
- package/dist/schedule/discover.js +0 -61
- package/dist/schedule/routine.d.ts +0 -43
- package/dist/schedule/routine.js +0 -32
- package/dist/schedule/run.d.ts +0 -55
- package/dist/schedule/run.js +0 -146
- package/dist/secrets-gate.d.ts +0 -32
- package/dist/secrets-gate.js +0 -33
- package/dist/session-remote.d.ts +0 -47
- package/dist/session-remote.js +0 -460
- package/dist/session.d.ts +0 -466
- package/dist/session.js +0 -57
- /package/dist/{channels/kit/state.d.ts → state-file.d.ts} +0 -0
|
@@ -1,32 +1,26 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* `deploy agentcore`: one CloudFormation stack (runtime + forwarder Lambda behind a Function URL
|
|
3
|
-
* schedules
|
|
2
|
+
* `deploy agentcore`: one CloudFormation stack (runtime + forwarder Lambda behind a Function URL); the container
|
|
3
|
+
* sets its EventBridge schedules itself; no resident process.
|
|
4
4
|
*/
|
|
5
5
|
import { mkdtemp, readFile, rm, writeFile } from "node:fs/promises";
|
|
6
6
|
import { tmpdir } from "node:os";
|
|
7
7
|
import { basename, join } from "node:path";
|
|
8
|
-
import { FORWARDER_FILE, TEMPLATE_FILE, agentcoreName, agentcoreStackName, forwarderLogGroup, ingressSessionId, isGeneratedAgentcoreTemplate, planAgentcoreDeploy, } from "../../../deploy/agentcore/plan.js";
|
|
8
|
+
import { FORWARDER_FILE, TEMPLATE_FILE, agentcoreName, agentcoreStackName, forwarderLogGroup, PROBE_PAYLOAD, ingressSessionId, isGeneratedAgentcoreTemplate, planAgentcoreDeploy, } from "../../../deploy/agentcore/plan.js";
|
|
9
9
|
import { deployAgentcoreRun, pickStackOutputs } from "../../../deploy/agentcore/run.js";
|
|
10
10
|
import { awsCli, awsJson } from "../../../deploy/agentcore/aws-cli.js";
|
|
11
11
|
import { agentcoreShell } from "../../../deploy/agentcore/shell.js";
|
|
12
12
|
import { awsRunner, spawnRunner } from "../../../deploy/runner.js";
|
|
13
13
|
import { SECRET_FILE_MODE, exists } from "../../../paths.js";
|
|
14
|
-
import { loadRoutines } from "../../../schedule/discover.js";
|
|
15
14
|
import { assembleSecrets } from "../../../deploy/secrets.js";
|
|
16
15
|
import { failStartup } from "../../fail.js";
|
|
17
|
-
import {
|
|
16
|
+
import { shellArg } from "../../shared.js";
|
|
18
17
|
import { boxLoginStep, registrarsFor } from "./shared.js";
|
|
19
|
-
/** A copy/paste-safe POSIX shell argument for the command hints deploy prints. */
|
|
20
|
-
function shellArg(value) {
|
|
21
|
-
return /^[A-Za-z0-9_./:@%+=,-]+$/.test(value) ? value : `'${value.replaceAll("'", `'"'"'`)}'`;
|
|
22
|
-
}
|
|
23
18
|
export const agentcoreHost = {
|
|
24
19
|
isOurs: (path, content) => path.endsWith(TEMPLATE_FILE) && isGeneratedAgentcoreTemplate(content),
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
const name = agentcoreName(basename(workspace));
|
|
20
|
+
async shell(agentDir) {
|
|
21
|
+
const name = agentcoreName(basename(agentDir));
|
|
28
22
|
const stack = agentcoreStackName(name);
|
|
29
|
-
const aws = awsRunner(
|
|
23
|
+
const aws = awsRunner(agentDir);
|
|
30
24
|
const read = await awsCli(aws).read(["cloudformation", "describe-stacks", "--stack-name", stack, "--query", "Stacks[0].Outputs", "--output", "json"], awsJson(pickStackOutputs));
|
|
31
25
|
if ("absent" in read)
|
|
32
26
|
throw new Error(`no AgentCore stack ${stack} in this account/region — deploy it first`);
|
|
@@ -37,17 +31,8 @@ export const agentcoreHost = {
|
|
|
37
31
|
throw new Error(`stack ${stack} has no RuntimeArn output — redeploy it`);
|
|
38
32
|
return agentcoreShell(runtimeArn, ingressSessionId(name), aws);
|
|
39
33
|
},
|
|
40
|
-
|
|
41
|
-
const
|
|
42
|
-
const { modelAuth, boxLogin, container, declaredSecrets, values, valueFile } = pre;
|
|
43
|
-
if (boxLogin) {
|
|
44
|
-
console.error(`[fastagent] note: AgentCore resets the runtime's storage on every deploy AND after 14 idle days, and the ` +
|
|
45
|
-
`${boxLogin} login with it: every deploy of this agent ends with a login on the runtime, and after an idle ` +
|
|
46
|
-
`reset every turn fails until \`fastagent login ${boxLogin} --deployment agentcore\` (the runtime's log says ` +
|
|
47
|
-
`so). For an agent that must keep answering unattended, set ${boxLogin}'s API key in ${valueFile}.`);
|
|
48
|
-
}
|
|
49
|
-
// Long-connection channels are STRUCTURALLY unsupported: the connection is the ingress, and a reclaimed session
|
|
50
|
-
// has nothing to wake it.
|
|
34
|
+
validate({ opts, agentDir, pre }) {
|
|
35
|
+
const longConnectionChannels = pre.channels.filter((channel) => channel.ingress === "long-connection");
|
|
51
36
|
if (longConnectionChannels.length > 0) {
|
|
52
37
|
const msg = `long-connection channel (${longConnectionChannels.map((c) => c.name).join(", ")}) cannot run on AgentCore — there is no ` +
|
|
53
38
|
`resident process to hold the connection, and nothing wakes a reclaimed session. Switch the channel ` +
|
|
@@ -56,50 +41,37 @@ export const agentcoreHost = {
|
|
|
56
41
|
failStartup(new Error(`deploy stopped: ${msg}`));
|
|
57
42
|
console.error(`[fastagent] warn: ${msg}`);
|
|
58
43
|
}
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
`ingress secret and attaches it), and the plane has nothing of its own to verify. Steer sessions from a ` +
|
|
64
|
-
`host that serves it behind your own auth (docs/design/session-control.md §14).`);
|
|
65
|
-
}
|
|
66
|
-
// Wake-ups are a default capability here as everywhere: pending ones are mirrored into one-shot EventBridge
|
|
67
|
-
// schedules via the forwarder (the wake-alarm mechanism — see deploy/agentcore/plan.ts), which is why every stack
|
|
68
|
-
// has one.
|
|
69
|
-
const loaded = await loadRoutines(agentDir).catch(failStartup);
|
|
70
|
-
if (loaded.failures.length > 0) {
|
|
71
|
-
failStartup(new Error(`deploy stopped: cannot load routines: ${loaded.failures.map((x) => `${x.label}: ${x.message}`).join("; ")}`));
|
|
72
|
-
}
|
|
73
|
-
const acName = agentcoreName(basename(workspace));
|
|
74
|
-
// Every derived AWS name embeds acName; the tightest ceiling is the Lambda function name
|
|
75
|
-
// (`fastagent-<name>-forwarder` ≤ 64 chars).
|
|
76
|
-
if (acName.length > 40) {
|
|
77
|
-
failStartup(new Error(`deploy stopped: the directory name maps to "${acName}" (${acName.length} chars) — AWS resource ` +
|
|
44
|
+
const name = agentcoreName(basename(agentDir));
|
|
45
|
+
// The forwarder Lambda name embeds this name and must fit AWS's 64-character limit.
|
|
46
|
+
if (name.length > 40) {
|
|
47
|
+
failStartup(new Error(`deploy stopped: the directory name maps to "${name}" (${name.length} chars) — AWS resource ` +
|
|
78
48
|
`names derived from it exceed their limits past 40 chars. Deploy from a shorter directory name.`));
|
|
79
49
|
}
|
|
50
|
+
},
|
|
51
|
+
async deploy(ctx) {
|
|
52
|
+
const { opts, agentDir, config, channels, pre, write } = ctx;
|
|
53
|
+
const { modelAuth, boxLogin, container, declaredSecrets, values, valueFile } = pre;
|
|
54
|
+
if (boxLogin) {
|
|
55
|
+
console.error(`[fastagent] note: AgentCore resets the runtime's storage on every deploy AND after 14 idle days, and the ` +
|
|
56
|
+
`${boxLogin} login with it: every deploy of this agent ends with a login on the runtime, and after an idle ` +
|
|
57
|
+
`reset every turn fails until \`fastagent login ${boxLogin} --deployment agentcore\` (the runtime's log says ` +
|
|
58
|
+
`so). For an agent that must keep answering unattended, set ${boxLogin}'s API key in ${valueFile}.`);
|
|
59
|
+
}
|
|
60
|
+
// Schedules and wake-ups need nothing from the template beyond the forwarder every stack has: the container
|
|
61
|
+
// mirrors them into EventBridge schedules itself (schedule/wake-alarm.ts). A schedule that does not build is the
|
|
62
|
+
// pre-flight's to report.
|
|
63
|
+
const acName = agentcoreName(basename(agentDir));
|
|
80
64
|
const plan = planAgentcoreDeploy({
|
|
81
65
|
name: acName,
|
|
82
66
|
boxLogin,
|
|
83
67
|
channels,
|
|
84
68
|
secrets: pre.secrets,
|
|
85
|
-
// ONLY THE ONES WITH A CRON become rules. A routine without one is reached by NAME, through this host's
|
|
86
|
-
// IAM-gated `routine-run` envelope (channels/agentcore.ts) rather than a route — so it needs no rule and
|
|
87
|
-
// loses nothing by not having one.
|
|
88
|
-
schedules: loaded.routines.flatMap((r) => r.cron === undefined ? [] : [{ name: r.name, cron: r.cron, ...(r.tz !== undefined ? { tz: r.tz } : {}) }]),
|
|
89
69
|
idleTimeoutSeconds: config.deploy?.agentcore?.idleTimeoutSeconds,
|
|
90
70
|
...container,
|
|
91
71
|
});
|
|
92
|
-
|
|
93
|
-
// Same discipline as Fly's kept-toml time-trigger gate: a deploy whose schedule silently never fires is worse
|
|
94
|
-
// than a stopped deploy.
|
|
95
|
-
const msg = `schedule "${u.name}" cannot be expressed as an EventBridge rule — ${u.reason}`;
|
|
96
|
-
if (opts.run)
|
|
97
|
-
failStartup(new Error(`deploy stopped: ${msg}`));
|
|
98
|
-
console.error(`[fastagent] warn: ${msg} — it will NOT fire on this deployment`);
|
|
99
|
-
}
|
|
100
|
-
// The template IS the topology (EventBridge rules, wake wiring, secrets).
|
|
72
|
+
// The template IS the topology (forwarder, alarm wiring, secrets).
|
|
101
73
|
const templateArtifact = plan.artifacts.find((a) => a.path.endsWith(TEMPLATE_FILE));
|
|
102
|
-
const templateHome = join(
|
|
74
|
+
const templateHome = join(agentDir, templateArtifact?.path ?? TEMPLATE_FILE);
|
|
103
75
|
if (!opts.force && templateArtifact && (await exists(templateHome))) {
|
|
104
76
|
const existing = await readFile(templateHome, "utf8");
|
|
105
77
|
if (isGeneratedAgentcoreTemplate(existing) && existing !== templateArtifact.content) {
|
|
@@ -108,44 +80,45 @@ export const agentcoreHost = {
|
|
|
108
80
|
// rule (deploy.ts) for every host and every path — stating it a second time here is how the two
|
|
109
81
|
// wordings drift apart.
|
|
110
82
|
console.error(`[fastagent] warn: ${templateArtifact.path} no longer matches this definition ` +
|
|
111
|
-
`(channels
|
|
83
|
+
`(channels or a deploy.agentcore setting changed) — the kept template would ` +
|
|
112
84
|
`silently drop the difference.`);
|
|
113
85
|
}
|
|
114
86
|
}
|
|
115
87
|
await write(plan.artifacts, {
|
|
116
88
|
force: !!opts.force,
|
|
117
|
-
alwaysWrite: [
|
|
89
|
+
alwaysWrite: [FORWARDER_FILE],
|
|
118
90
|
});
|
|
119
91
|
if (opts.run) {
|
|
120
|
-
|
|
92
|
+
await runDeployAgentcore({
|
|
121
93
|
agentDir,
|
|
122
|
-
workspace,
|
|
123
|
-
agentPrefix: container.agentPrefix,
|
|
124
94
|
name: acName,
|
|
125
95
|
modelAuth,
|
|
126
96
|
boxLogin,
|
|
127
|
-
|
|
97
|
+
answerer: ctx.answerer,
|
|
98
|
+
rerun: ctx.rerun,
|
|
128
99
|
channels,
|
|
129
100
|
declaredSecrets,
|
|
130
101
|
values,
|
|
131
102
|
valueFile,
|
|
132
103
|
topology: plan.topology,
|
|
133
104
|
});
|
|
105
|
+
return undefined;
|
|
134
106
|
}
|
|
135
107
|
console.log(plan.runbook.join("\n"));
|
|
136
|
-
return;
|
|
108
|
+
return undefined;
|
|
137
109
|
},
|
|
138
110
|
};
|
|
139
111
|
/** `deploy agentcore --run`: drive aws + docker to completion. */
|
|
140
112
|
async function runDeployAgentcore(params) {
|
|
141
|
-
const { agentDir,
|
|
113
|
+
const { agentDir, name, channels, topology } = params;
|
|
142
114
|
// Decided before the first side effect: the deploy resets the runtime's storage and the login with it, so with nobody
|
|
143
115
|
// to log it in again it would end at "not logged in" AFTER replacing a runtime that was serving, with the webhooks
|
|
144
|
-
// a previous deploy registered still pointing at it.
|
|
145
|
-
if (params.boxLogin &&
|
|
116
|
+
// a previous deploy registered still pointing at it. A coding agent's ticket has someone: its user.
|
|
117
|
+
if (params.boxLogin && params.answerer.kind === "nobody") {
|
|
146
118
|
failStartup(new Error(`deploy stopped: every AgentCore deploy wipes the runtime's ${params.boxLogin} login, and without a terminal ` +
|
|
147
|
-
`nobody can log it in again — run this deploy in a terminal
|
|
148
|
-
|
|
119
|
+
`nobody can log it in again — run this deploy in a terminal; from a coding agent, run ` +
|
|
120
|
+
`\`${params.rerun} --json\`, which hands the login to its user; or set ` +
|
|
121
|
+
`${params.boxLogin}'s API key in ${params.valueFile}`));
|
|
149
122
|
}
|
|
150
123
|
const { secrets, missingSecrets } = assembleSecrets({
|
|
151
124
|
modelAuth: params.modelAuth,
|
|
@@ -162,8 +135,8 @@ async function runDeployAgentcore(params) {
|
|
|
162
135
|
try {
|
|
163
136
|
const outcome = await deployAgentcoreRun({
|
|
164
137
|
name,
|
|
165
|
-
templatePath:
|
|
166
|
-
dockerfilePath:
|
|
138
|
+
templatePath: TEMPLATE_FILE,
|
|
139
|
+
dockerfilePath: "Dockerfile",
|
|
167
140
|
tag: new Date()
|
|
168
141
|
.toISOString()
|
|
169
142
|
.replace(/[-:.TZ]/g, "")
|
|
@@ -174,8 +147,8 @@ async function runDeployAgentcore(params) {
|
|
|
174
147
|
valueFile: params.valueFile,
|
|
175
148
|
channels,
|
|
176
149
|
topology,
|
|
177
|
-
...boxLoginStep("agentcore", params, (runtimeArn) => agentcoreShell(runtimeArn, ingressSessionId(name), awsRunner(
|
|
178
|
-
}, awsRunner(
|
|
150
|
+
...boxLoginStep("agentcore", params, (runtimeArn) => agentcoreShell(runtimeArn, ingressSessionId(name), awsRunner(agentDir))),
|
|
151
|
+
}, awsRunner(agentDir), spawnRunner("docker", agentDir), (m) => console.error(`[fastagent] ${m}`), async (content) => {
|
|
179
152
|
const path = join(paramsDir, "params.json");
|
|
180
153
|
await writeFile(path, content, { mode: SECRET_FILE_MODE });
|
|
181
154
|
return path;
|
|
@@ -183,13 +156,13 @@ async function runDeployAgentcore(params) {
|
|
|
183
156
|
const path = join(paramsDir, "forwarder.zip");
|
|
184
157
|
await writeFile(path, bytes);
|
|
185
158
|
return path;
|
|
186
|
-
}, registrarsFor(agentDir));
|
|
159
|
+
}, registrarsFor(agentDir, params.values, params.answerer));
|
|
187
160
|
if (!outcome.ok)
|
|
188
161
|
failStartup(new Error(`deploy stopped: ${outcome.gate}`));
|
|
189
162
|
console.error(`[fastagent] deployed → ${outcome.runtimeArn}`);
|
|
190
163
|
if (outcome.url)
|
|
191
164
|
console.error(`[fastagent] webhook ingress → ${outcome.url}`);
|
|
192
|
-
const logsDir = shellArg(
|
|
165
|
+
const logsDir = shellArg(agentDir);
|
|
193
166
|
console.error(`[fastagent] runtime logs → fastagent logs agentcore ${logsDir} --follow`);
|
|
194
167
|
console.error(`[fastagent] forwarder logs → fastagent logs agentcore ${logsDir} --source forwarder --follow`);
|
|
195
168
|
// `--run` never prints the runbook, and this is the ONE step in it that nothing else will remind anyone of: a
|
|
@@ -200,9 +173,9 @@ async function runDeployAgentcore(params) {
|
|
|
200
173
|
`--log-group-name <the group \`fastagent logs agentcore ${logsDir} --follow\` resolves> --retention-in-days 14`);
|
|
201
174
|
console.error(`[fastagent] ...and for the forwarder: aws logs put-retention-policy ` +
|
|
202
175
|
`--log-group-name ${forwarderLogGroup(name)} --retention-in-days 14`);
|
|
203
|
-
console.error(`[fastagent]
|
|
176
|
+
console.error(`[fastagent] probe: aws bedrock-agentcore invoke-agent-runtime --agent-runtime-arn ${outcome.runtimeArn} \\\n` +
|
|
204
177
|
` --runtime-session-id "${ingressSessionId(name)}" \\\n` +
|
|
205
|
-
` --payload '{
|
|
178
|
+
` --payload '${PROBE_PAYLOAD}' --cli-binary-format raw-in-base64-out /dev/stdout`);
|
|
206
179
|
}
|
|
207
180
|
finally {
|
|
208
181
|
await rm(paramsDir, { recursive: true, force: true });
|
|
@@ -1,45 +1,38 @@
|
|
|
1
1
|
/** `deploy docker`: one app service + loopback port + state volume, as a user-owned Compose file. */
|
|
2
2
|
import { mkdir, writeFile } from "node:fs/promises";
|
|
3
|
-
import { basename, dirname, join,
|
|
3
|
+
import { basename, dirname, join, resolve } from "node:path";
|
|
4
4
|
import { webhookPaths } from "../../../deploy/channel-ingress.js";
|
|
5
|
-
import { DOCKER_COMPOSE_FILE, composeHasTunnelService, isGeneratedCompose, planDockerDeploy, toDockerProjectName, } from "../../../deploy/docker/plan.js";
|
|
5
|
+
import { DOCKER_COMPOSE_FILE, DOCKER_VALUE_FILE, composeHasTunnelService, isGeneratedCompose, planDockerDeploy, toDockerProjectName, } from "../../../deploy/docker/plan.js";
|
|
6
6
|
import { deployDockerRun } from "../../../deploy/docker/run.js";
|
|
7
7
|
import { spawnRunner } from "../../../deploy/runner.js";
|
|
8
8
|
import { openExternalUrl } from "../../../open-url.js";
|
|
9
|
-
import {
|
|
10
|
-
import { dotEnvPath } from "../../../env.js";
|
|
9
|
+
import { SECRET_FILE_MODE, exists, readTextIfExists, resolveStateRoot } from "../../../paths.js";
|
|
11
10
|
import { announceWebhooks } from "../../../tunnel.js";
|
|
12
11
|
import { assembleSecrets } from "../../../deploy/secrets.js";
|
|
13
12
|
import { processShell } from "../../../deploy/box-shell.js";
|
|
14
13
|
import { failStartup } from "../../fail.js";
|
|
15
|
-
import {
|
|
16
|
-
import { boxLoginStep } from "./shared.js";
|
|
14
|
+
import { boxLoginStep, leftToPerson } from "./shared.js";
|
|
17
15
|
export const dockerHost = {
|
|
18
16
|
isOurs: (path, content) => path.endsWith(DOCKER_COMPOSE_FILE) && isGeneratedCompose(content),
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
// The generated Compose names `<agent>/.secrets/.env` unconditionally, so it has to be there — Compose refuses
|
|
25
|
-
// an `env_file` entry pointing at a missing path, and this floor predates `required: false` (Compose 2.24).
|
|
26
|
-
// Creating it empty is honest: a deployment that declares nothing declares it in an empty file.
|
|
27
|
-
const composeValueFile = join(agentDir, SECRETS_DIRNAME, ".env");
|
|
28
|
-
await mkdir(dirname(composeValueFile), { recursive: true });
|
|
29
|
-
if (!(await exists(composeValueFile)))
|
|
30
|
-
await writeFile(composeValueFile, "", { mode: SECRET_FILE_MODE });
|
|
31
|
-
// The pre-flight read whatever `FASTAGENT_SECRETS_DIR` resolved to; the committed Compose cannot, because a
|
|
32
|
-
// builder's path would not mean the same thing anywhere else. Under `--run` the two files disagreeing is a
|
|
33
|
-
// DETERMINISTIC failure and gates like every other one: the missing-values gate would pass on the file this
|
|
34
|
-
// machine reads while `compose up` starts a container whose declared secrets and model are all absent.
|
|
35
|
-
if (resolve(dotEnvPath(agentDir)) !== resolve(composeValueFile)) {
|
|
36
|
-
const issue = `FASTAGENT_SECRETS_DIR points this run's values at ${valueFile}, but the generated Compose reads ` +
|
|
37
|
-
`${relative(workspace, composeValueFile)} (a committed artifact cannot carry this machine's path), so the ` +
|
|
17
|
+
shell: async (agentDir) => composeShell(DOCKER_COMPOSE_FILE, agentDir),
|
|
18
|
+
validate({ opts, agentDir, pre }) {
|
|
19
|
+
if (resolve(agentDir, pre.valueFile) !== join(agentDir, DOCKER_VALUE_FILE)) {
|
|
20
|
+
const issue = `FASTAGENT_SECRETS_DIR points this run's values at ${pre.valueFile}, but the generated Compose reads ` +
|
|
21
|
+
`${DOCKER_VALUE_FILE} (a committed artifact cannot carry this machine's path), so the ` +
|
|
38
22
|
`container would start with none of them. Unset FASTAGENT_SECRETS_DIR, or put the values in that file.`;
|
|
39
23
|
if (opts.run)
|
|
40
24
|
failStartup(new Error(`deploy stopped: ${issue}`));
|
|
41
25
|
console.error(`[fastagent] warn: ${issue}`);
|
|
42
26
|
}
|
|
27
|
+
},
|
|
28
|
+
async deploy(ctx) {
|
|
29
|
+
const { opts, agentDir, channels, webhookChannels, pre, write } = ctx;
|
|
30
|
+
const { modelAuth, boxLogin, container, port, declaredSecrets, values, valueFile } = pre;
|
|
31
|
+
// Compose requires its committed env_file to exist, even when no values are declared.
|
|
32
|
+
const composeValueFile = join(agentDir, DOCKER_VALUE_FILE);
|
|
33
|
+
await mkdir(dirname(composeValueFile), { recursive: true });
|
|
34
|
+
if (!(await exists(composeValueFile)))
|
|
35
|
+
await writeFile(composeValueFile, "", { mode: SECRET_FILE_MODE });
|
|
43
36
|
// Compose interpolates `$VAR` INSIDE env_file values (`format: raw` needs Compose 2.30), so a credential
|
|
44
37
|
// containing `$` reaches the container rewritten — silently, and differently from what this pre-flight read.
|
|
45
38
|
// No escaping advice: this ONE file is also read literally by `dev`/`start` (`parseEnvContent`) and pushed
|
|
@@ -52,7 +45,7 @@ export const dockerHost = {
|
|
|
52
45
|
`"$". Prefer a value without "$".`);
|
|
53
46
|
}
|
|
54
47
|
const hasDeclaredChannels = channels.length > 0;
|
|
55
|
-
const projectName = toDockerProjectName(basename(
|
|
48
|
+
const projectName = toDockerProjectName(basename(agentDir));
|
|
56
49
|
const dockerPlan = (tunnel) => planDockerDeploy({
|
|
57
50
|
projectName,
|
|
58
51
|
port,
|
|
@@ -70,7 +63,7 @@ export const dockerHost = {
|
|
|
70
63
|
let plan = dockerPlan(requestedTunnel);
|
|
71
64
|
// An existing Compose file is authoritative: shape its comparison/runbook from the topology on disk, regardless
|
|
72
65
|
// of the current flag.
|
|
73
|
-
const composeFile = join(
|
|
66
|
+
const composeFile = join(agentDir, plan.composePath);
|
|
74
67
|
let keptWithoutRequestedTunnel = false;
|
|
75
68
|
// Same ownership question as fly.toml: a hand-owned compose file survives --force, so the plan must describe the
|
|
76
69
|
// topology that will actually be there.
|
|
@@ -84,13 +77,12 @@ export const dockerHost = {
|
|
|
84
77
|
if (opts.run) {
|
|
85
78
|
return runDeployDocker({
|
|
86
79
|
agentDir,
|
|
87
|
-
workspace,
|
|
88
80
|
composeFile: plan.composePath,
|
|
89
81
|
port,
|
|
90
82
|
requireTunnel: requestedTunnel,
|
|
91
83
|
modelAuth,
|
|
92
84
|
boxLogin,
|
|
93
|
-
|
|
85
|
+
answerer: ctx.answerer,
|
|
94
86
|
channels,
|
|
95
87
|
declaredSecrets,
|
|
96
88
|
values,
|
|
@@ -102,12 +94,13 @@ export const dockerHost = {
|
|
|
102
94
|
`edit it, delete it and regenerate, or pass --force`);
|
|
103
95
|
}
|
|
104
96
|
console.log(plan.runbook.join("\n"));
|
|
105
|
-
return
|
|
97
|
+
return `http://127.0.0.1:${port}`;
|
|
106
98
|
},
|
|
107
99
|
};
|
|
108
100
|
/** `deploy docker --run`: reconcile the user-owned local topology, then log the box in if it must. */
|
|
109
101
|
async function runDeployDocker(params) {
|
|
110
|
-
const { agentDir,
|
|
102
|
+
const { agentDir, composeFile, port, requireTunnel, channels } = params;
|
|
103
|
+
const leave = leftToPerson(params.answerer);
|
|
111
104
|
const { secrets, missingSecrets } = assembleSecrets({
|
|
112
105
|
modelAuth: params.modelAuth,
|
|
113
106
|
declared: params.declaredSecrets,
|
|
@@ -121,12 +114,14 @@ async function runDeployDocker(params) {
|
|
|
121
114
|
valueFile: params.valueFile,
|
|
122
115
|
requireTunnel,
|
|
123
116
|
channels,
|
|
124
|
-
...boxLoginStep("docker", params, () => composeShell(composeFile,
|
|
117
|
+
...boxLoginStep("docker", params, () => composeShell(composeFile, agentDir)),
|
|
125
118
|
announce: (tunnelUrl) => announceWebhooks(agentDir, tunnelUrl, channels, {
|
|
126
|
-
|
|
119
|
+
// A ticket's process opens no browser: its person is told, through `leave`.
|
|
120
|
+
...(leave === undefined ? { openUrl: openExternalUrl } : { leave }),
|
|
127
121
|
stateRoot: resolveStateRoot(agentDir),
|
|
122
|
+
env: Object.fromEntries(params.values),
|
|
128
123
|
}),
|
|
129
|
-
}, spawnRunner("docker",
|
|
124
|
+
}, spawnRunner("docker", agentDir), (message) => console.error(`[fastagent] ${message}`));
|
|
130
125
|
const compose = `docker compose -f ${composeFile}`;
|
|
131
126
|
// Compose reached "up" iff the driver could report where it is.
|
|
132
127
|
const isUp = outcome.ok || outcome.url !== undefined || outcome.tunnelUrl !== undefined;
|
|
@@ -144,14 +139,18 @@ async function runDeployDocker(params) {
|
|
|
144
139
|
if (!outcome.ok)
|
|
145
140
|
failStartup(new Error(`deploy stopped: ${outcome.gate}`));
|
|
146
141
|
if (outcome.tunnelUrl)
|
|
147
|
-
return;
|
|
142
|
+
return outcome.tunnelUrl;
|
|
148
143
|
const paths = webhookPaths(channels);
|
|
149
144
|
if (paths.length > 0) {
|
|
150
|
-
|
|
151
|
-
`
|
|
145
|
+
const unreached = `${paths.join(", ")} receive nothing until a public URL reaches this container — re-run with --tunnel for a ` +
|
|
146
|
+
`Quick Tunnel, or configure your own tunnel/proxy and point those webhook path(s) (or your remapped channel ` +
|
|
147
|
+
`routes) at it`;
|
|
148
|
+
console.error(`[fastagent] note: ${unreached}`);
|
|
149
|
+
leave?.(unreached);
|
|
152
150
|
}
|
|
151
|
+
return outcome.url;
|
|
153
152
|
}
|
|
154
153
|
/** `docker compose exec` into the running agent service. */
|
|
155
|
-
function composeShell(composeFile,
|
|
156
|
-
return processShell("docker", (command) => ["compose", "-f", composeFile, "exec", "-T", "agent", "sh", "-c", command],
|
|
154
|
+
function composeShell(composeFile, agentDir) {
|
|
155
|
+
return processShell("docker", (command) => ["compose", "-f", composeFile, "exec", "-T", "agent", "sh", "-c", command], agentDir);
|
|
157
156
|
}
|
|
@@ -8,17 +8,16 @@ import { residencyFor } from "../../../deploy/residency.js";
|
|
|
8
8
|
import { assembleSecrets } from "../../../deploy/secrets.js";
|
|
9
9
|
import { processShell } from "../../../deploy/box-shell.js";
|
|
10
10
|
import { failStartup } from "../../fail.js";
|
|
11
|
-
import {
|
|
11
|
+
import { FLY_LOGIN, hostLogin } from "./host-login.js";
|
|
12
12
|
import { boxLoginStep, registrarsFor } from "./shared.js";
|
|
13
13
|
export const flyHost = {
|
|
14
14
|
isOurs: (path, content) => path.endsWith("fly.toml") && isGeneratedFlyToml(content),
|
|
15
|
-
|
|
16
|
-
async shell({ agentDir, workspace }) {
|
|
15
|
+
async shell(agentDir) {
|
|
17
16
|
const flyToml = await readTextIfExists(join(agentDir, "fly.toml"));
|
|
18
|
-
return flyShell((flyToml && parseFlyAppName(flyToml)) ?? toFlyAppName(basename(
|
|
17
|
+
return flyShell((flyToml && parseFlyAppName(flyToml)) ?? toFlyAppName(basename(agentDir)), agentDir);
|
|
19
18
|
},
|
|
20
19
|
async deploy(ctx) {
|
|
21
|
-
const { opts, agentDir,
|
|
20
|
+
const { opts, agentDir, channels, pre, write } = ctx;
|
|
22
21
|
const { hasCron, modelAuth, boxLogin, container, port, declaredSecrets, values, valueFile } = pre;
|
|
23
22
|
// Two consistent modes.
|
|
24
23
|
const flyTomlPath = join(agentDir, "fly.toml");
|
|
@@ -26,7 +25,7 @@ export const flyHost = {
|
|
|
26
25
|
// Every decision below turns on ONE question — will `writeArtifacts` keep this file?
|
|
27
26
|
const flyTomlKept = flyToml !== undefined && (!opts.force || !isGeneratedFlyToml(flyToml));
|
|
28
27
|
const keptApp = flyTomlKept ? parseFlyAppName(flyToml) : undefined;
|
|
29
|
-
const appName = keptApp ?? toFlyAppName(basename(
|
|
28
|
+
const appName = keptApp ?? toFlyAppName(basename(agentDir));
|
|
30
29
|
if (keptApp)
|
|
31
30
|
console.error(`[fastagent] app: ${keptApp} (from fly.toml)`);
|
|
32
31
|
if (flyToml !== undefined && !flyTomlKept) {
|
|
@@ -58,28 +57,30 @@ export const flyHost = {
|
|
|
58
57
|
...container,
|
|
59
58
|
});
|
|
60
59
|
await write(plan.artifacts, { force: !!opts.force });
|
|
60
|
+
const reachedAt = `https://${appName}.fly.dev`;
|
|
61
61
|
if (opts.run) {
|
|
62
|
-
|
|
62
|
+
await runDeployFly({
|
|
63
63
|
agentDir,
|
|
64
|
-
workspace,
|
|
65
|
-
agentPrefix: container.agentPrefix,
|
|
66
64
|
appName,
|
|
67
65
|
modelAuth,
|
|
68
66
|
boxLogin,
|
|
69
|
-
|
|
67
|
+
answerer: ctx.answerer,
|
|
68
|
+
rerun: ctx.rerun,
|
|
70
69
|
channels,
|
|
71
70
|
declaredSecrets,
|
|
72
71
|
values,
|
|
73
72
|
valueFile,
|
|
74
73
|
});
|
|
74
|
+
return reachedAt;
|
|
75
75
|
}
|
|
76
76
|
console.log(plan.runbook.join("\n"));
|
|
77
|
+
return reachedAt;
|
|
77
78
|
},
|
|
78
79
|
};
|
|
79
80
|
/** `deploy fly --run`: drive flyctl to completion (idempotent, resumable). */
|
|
80
81
|
async function runDeployFly(params) {
|
|
81
|
-
const { agentDir,
|
|
82
|
-
const fly = spawnRunner("fly",
|
|
82
|
+
const { agentDir, appName, channels } = params;
|
|
83
|
+
const fly = spawnRunner("fly", agentDir);
|
|
83
84
|
// Fail fast if flyctl is absent (spawn ENOENT → 127), with the install link — not a confusing auth gate.
|
|
84
85
|
if ((await fly(["version"], { capture: true })).code === 127) {
|
|
85
86
|
failStartup(new Error(`flyctl not found — install it: https://fly.io/docs/flyctl/install, then re-run`));
|
|
@@ -95,18 +96,20 @@ async function runDeployFly(params) {
|
|
|
95
96
|
missingSecrets,
|
|
96
97
|
valueFile: params.valueFile,
|
|
97
98
|
channels,
|
|
98
|
-
flyConfig:
|
|
99
|
-
dockerfile:
|
|
100
|
-
...boxLoginStep("fly", params, () => flyShell(appName,
|
|
101
|
-
|
|
99
|
+
flyConfig: "fly.toml",
|
|
100
|
+
dockerfile: "Dockerfile",
|
|
101
|
+
...boxLoginStep("fly", params, () => flyShell(appName, agentDir)),
|
|
102
|
+
...hostLogin(FLY_LOGIN, params.answerer, agentDir),
|
|
103
|
+
forAgent: `${params.rerun} --json`,
|
|
104
|
+
}, fly, (m) => console.error(`[fastagent] ${m}`), registrarsFor(agentDir, params.values, params.answerer));
|
|
102
105
|
if (!outcome.ok)
|
|
103
106
|
failStartup(new Error(`deploy stopped: ${outcome.gate}`));
|
|
104
107
|
console.error(`[fastagent] deployed → https://${appName}.fly.dev`);
|
|
105
108
|
}
|
|
106
109
|
/** `fly ssh console` into the app's machine, woken first: a suspended machine has no shell to open. */
|
|
107
|
-
function flyShell(app,
|
|
110
|
+
function flyShell(app, agentDir) {
|
|
108
111
|
return {
|
|
109
|
-
...processShell("fly", (command) => ["ssh", "console", "--app", app, "--quiet", "--command", `sh -c '${command}'`],
|
|
112
|
+
...processShell("fly", (command) => ["ssh", "console", "--app", app, "--quiet", "--command", `sh -c '${command}'`], agentDir),
|
|
110
113
|
// `fly ssh` fails with "no started VMs" while the machine is suspended, and a request is what resumes it. The
|
|
111
114
|
// answer is not the point: a machine that stays down makes the shell fail right after, with Fly's own reason.
|
|
112
115
|
wake: async () => {
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { Answerer } from "./shared.ts";
|
|
2
|
+
export interface BrowserLogin {
|
|
3
|
+
bin: string;
|
|
4
|
+
/** The login at a terminal: it opens the browser itself. */
|
|
5
|
+
atTerminal: string[];
|
|
6
|
+
/** The login with no terminal: it prints the address, and exits 0 once the person approved there. */
|
|
7
|
+
unattended: string[];
|
|
8
|
+
/** The address in what `unattended` prints. */
|
|
9
|
+
address: RegExp;
|
|
10
|
+
/** The code a device login's address carries, for the person to check against the page. */
|
|
11
|
+
userCode?(address: URL): string | undefined;
|
|
12
|
+
/**
|
|
13
|
+
* The login that finishes with the code the approval page shows, for a browser that could not reach this machine
|
|
14
|
+
* (Fly's): `deploy --check <ticket> --code <code>` hands it over.
|
|
15
|
+
*/
|
|
16
|
+
withCode?(code: string): string[];
|
|
17
|
+
}
|
|
18
|
+
export declare const FLY_LOGIN: BrowserLogin;
|
|
19
|
+
export declare const RAILWAY_LOGIN: BrowserLogin;
|
|
20
|
+
/**
|
|
21
|
+
* The host's sign-in, through whoever answers, as a driver's `hostLogin`; none for nobody, whose deploy stops at the
|
|
22
|
+
* host's own gate. Resolves undefined once signed in, else the line saying why not.
|
|
23
|
+
*/
|
|
24
|
+
export declare function hostLogin(login: BrowserLogin, answerer: Answerer, cwd: string): {
|
|
25
|
+
hostLogin?: () => Promise<string | undefined>;
|
|
26
|
+
};
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Signing a host's CLI in to the person's account, for a deploy that finds it signed out. At a terminal the host's own
|
|
3
|
+
* login runs there and opens the browser. In a deploy's ticket it runs with no terminal, where both CLIs print the
|
|
4
|
+
* address to approve at and finish on their own once approved; that address is the person's step. Never a token: one
|
|
5
|
+
* set in .secrets/production/.env would travel to the box with every other variable there.
|
|
6
|
+
*/
|
|
7
|
+
import { spawn } from "node:child_process";
|
|
8
|
+
export const FLY_LOGIN = {
|
|
9
|
+
bin: "fly",
|
|
10
|
+
atTerminal: ["auth", "login"],
|
|
11
|
+
unattended: ["auth", "login"],
|
|
12
|
+
address: /https:\/\/fly\.io\/app\/auth\/cli\/[A-Za-z0-9_-]+/,
|
|
13
|
+
withCode: (code) => ["auth", "login", "--code", code],
|
|
14
|
+
};
|
|
15
|
+
export const RAILWAY_LOGIN = {
|
|
16
|
+
bin: "railway",
|
|
17
|
+
atTerminal: ["login"],
|
|
18
|
+
unattended: ["login", "--browserless"],
|
|
19
|
+
address: /https:\/\/railway\.com\/activate\?user_code=[A-Za-z0-9-]+/,
|
|
20
|
+
userCode: (address) => address.searchParams.get("user_code") ?? undefined,
|
|
21
|
+
};
|
|
22
|
+
/**
|
|
23
|
+
* The host's sign-in, through whoever answers, as a driver's `hostLogin`; none for nobody, whose deploy stops at the
|
|
24
|
+
* host's own gate. Resolves undefined once signed in, else the line saying why not.
|
|
25
|
+
*/
|
|
26
|
+
export function hostLogin(login, answerer, cwd) {
|
|
27
|
+
if (answerer.kind === "nobody")
|
|
28
|
+
return {};
|
|
29
|
+
return { hostLogin: signIn(login, answerer, cwd) };
|
|
30
|
+
}
|
|
31
|
+
function signIn(login, answerer, cwd) {
|
|
32
|
+
if (answerer.kind === "terminal") {
|
|
33
|
+
return async () => {
|
|
34
|
+
const code = await exitOf(spawn(login.bin, login.atTerminal, { cwd, stdio: "inherit" }));
|
|
35
|
+
return code === 0 ? undefined : `\`${login.bin} ${login.atTerminal.join(" ")}\` did not sign in — re-run`;
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
const { step } = answerer;
|
|
39
|
+
return async () => {
|
|
40
|
+
const child = spawn(login.bin, login.unattended, { cwd, stdio: ["ignore", "pipe", "pipe"] });
|
|
41
|
+
let said = "";
|
|
42
|
+
let shown = false;
|
|
43
|
+
const hear = (chunk) => {
|
|
44
|
+
const text = String(chunk);
|
|
45
|
+
process.stderr.write(text);
|
|
46
|
+
said = `${said}${text}`.slice(-4096);
|
|
47
|
+
const found = shown ? null : login.address.exec(said);
|
|
48
|
+
if (found === null)
|
|
49
|
+
return;
|
|
50
|
+
shown = true;
|
|
51
|
+
const url = found[0];
|
|
52
|
+
const code = login.userCode?.(new URL(url));
|
|
53
|
+
step.waits(code ? { url, code } : login.withCode ? { url, takesAddress: true } : { url });
|
|
54
|
+
};
|
|
55
|
+
child.stdout.on("data", hear);
|
|
56
|
+
child.stderr.on("data", hear);
|
|
57
|
+
const exited = exitOf(child);
|
|
58
|
+
const handed = new AbortController();
|
|
59
|
+
const finished = await Promise.race([
|
|
60
|
+
exited.then((code) => ({ code })),
|
|
61
|
+
...(login.withCode
|
|
62
|
+
? [
|
|
63
|
+
step
|
|
64
|
+
.address(AbortSignal.any([step.signal, handed.signal]))
|
|
65
|
+
.then((code) => ({ handedCode: code.trim() }))
|
|
66
|
+
.catch(() => new Promise(() => { })),
|
|
67
|
+
]
|
|
68
|
+
: []),
|
|
69
|
+
]);
|
|
70
|
+
handed.abort();
|
|
71
|
+
if ("handedCode" in finished) {
|
|
72
|
+
child.kill();
|
|
73
|
+
const args = login.withCode(finished.handedCode);
|
|
74
|
+
const code = await exitOf(spawn(login.bin, args, { cwd, stdio: ["ignore", "inherit", "inherit"] }));
|
|
75
|
+
if (code !== 0)
|
|
76
|
+
return `\`${login.bin}\` refused the code the approval page showed — try the deploy again`;
|
|
77
|
+
}
|
|
78
|
+
else if (finished.code !== 0) {
|
|
79
|
+
return `\`${login.bin} ${login.unattended.join(" ")}\` did not sign in (exit ${finished.code}): ${said.trim()}`;
|
|
80
|
+
}
|
|
81
|
+
step.person.carriesOn();
|
|
82
|
+
return undefined;
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
function exitOf(child) {
|
|
86
|
+
return new Promise((resolve) => {
|
|
87
|
+
child.on("error", () => resolve(127));
|
|
88
|
+
child.on("close", (code) => resolve(code ?? 1));
|
|
89
|
+
});
|
|
90
|
+
}
|