@fastagent-sh/fastagent 0.29.0 → 0.30.1

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.
Files changed (312) hide show
  1. package/README.md +9 -4
  2. package/dist/atomic-write.d.ts +4 -5
  3. package/dist/atomic-write.js +4 -5
  4. package/dist/authoring.d.ts +32 -15
  5. package/dist/authoring.js +147 -127
  6. package/dist/channels/agentcore-protocol.d.ts +8 -3
  7. package/dist/channels/agentcore-protocol.js +3 -1
  8. package/dist/channels/agentcore-service.d.ts +1 -0
  9. package/dist/channels/agentcore-service.js +14 -4
  10. package/dist/channels/agentcore.d.ts +9 -0
  11. package/dist/channels/agentcore.js +29 -4
  12. package/dist/channels/context.d.ts +2 -0
  13. package/dist/channels/context.js +5 -1
  14. package/dist/channels/feishu/feishu-api.d.ts +2 -1
  15. package/dist/channels/feishu/feishu.d.ts +0 -3
  16. package/dist/channels/feishu/feishu.js +10 -10
  17. package/dist/channels/feishu/register-app.js +3 -5
  18. package/dist/channels/feishu/scaffold/channel.ts +32 -34
  19. package/dist/channels/feishu/scaffold/feishu-send.ts +5 -5
  20. package/dist/channels/feishu/scaffold/feishu-threads.ts +3 -3
  21. package/dist/channels/feishu/setup-mode.d.ts +2 -12
  22. package/dist/channels/feishu/setup-mode.js +2 -22
  23. package/dist/channels/feishu/setup.d.ts +12 -0
  24. package/dist/channels/feishu/setup.js +197 -0
  25. package/dist/channels/kit/abortable.d.ts +8 -0
  26. package/dist/channels/kit/abortable.js +22 -0
  27. package/dist/channels/lark/lark.d.ts +0 -3
  28. package/dist/channels/lark/lark.js +0 -5
  29. package/dist/channels/lark/scaffold/channel.ts +33 -35
  30. package/dist/channels/lark/scaffold/lark-send.ts +5 -5
  31. package/dist/channels/lark/scaffold/lark-threads.ts +3 -3
  32. package/dist/channels/lark/setup.d.ts +9 -0
  33. package/dist/channels/lark/setup.js +57 -0
  34. package/dist/channels/setup.d.ts +112 -0
  35. package/dist/channels/setup.js +31 -0
  36. package/dist/channels/slack/history.js +1 -1
  37. package/dist/channels/slack/manifest.d.ts +17 -7
  38. package/dist/channels/slack/manifest.js +12 -16
  39. package/dist/channels/slack/scaffold/channel.ts +44 -30
  40. package/dist/channels/slack/scaffold/slack-send.ts +8 -8
  41. package/dist/channels/slack/scaffold/slack-threads.ts +4 -4
  42. package/dist/channels/slack/setup.d.ts +7 -0
  43. package/dist/channels/slack/setup.js +119 -0
  44. package/dist/channels/slack/slack-api.d.ts +2 -0
  45. package/dist/channels/slack/slack-api.js +4 -0
  46. package/dist/channels/slack/slack.d.ts +21 -2
  47. package/dist/channels/slack/slack.js +398 -338
  48. package/dist/channels/slack/socket.d.ts +57 -0
  49. package/dist/channels/slack/socket.js +305 -0
  50. package/dist/channels/telegram/polling.d.ts +27 -0
  51. package/dist/channels/telegram/polling.js +105 -0
  52. package/dist/channels/telegram/scaffold/channel.ts +52 -32
  53. package/dist/channels/telegram/scaffold/telegram-send.ts +9 -9
  54. package/dist/channels/telegram/setup.d.ts +2 -0
  55. package/dist/channels/telegram/setup.js +42 -0
  56. package/dist/channels/telegram/telegram-api.d.ts +27 -1
  57. package/dist/channels/telegram/telegram-api.js +7 -6
  58. package/dist/channels/telegram/telegram.d.ts +11 -3
  59. package/dist/channels/telegram/telegram.js +184 -157
  60. package/dist/channels/webhook-evidence.d.ts +11 -0
  61. package/dist/channels/webhook-evidence.js +71 -0
  62. package/dist/channels/webhooks.d.ts +37 -0
  63. package/dist/channels/webhooks.js +70 -0
  64. package/dist/child-env.d.ts +16 -0
  65. package/dist/child-env.js +30 -0
  66. package/dist/cli/box-login.js +4 -1
  67. package/dist/cli/channel-app.d.ts +3 -0
  68. package/dist/cli/channel-app.js +35 -0
  69. package/dist/cli/channel-setup.d.ts +38 -0
  70. package/dist/cli/channel-setup.js +195 -0
  71. package/dist/cli/commands/add.d.ts +6 -14
  72. package/dist/cli/commands/add.js +45 -303
  73. package/dist/cli/commands/auth.js +8 -2
  74. package/dist/cli/commands/build.d.ts +5 -0
  75. package/dist/cli/commands/build.js +16 -0
  76. package/dist/cli/commands/channels.d.ts +19 -0
  77. package/dist/cli/commands/channels.js +230 -0
  78. package/dist/cli/commands/content.d.ts +7 -3
  79. package/dist/cli/commands/content.js +16 -10
  80. package/dist/cli/commands/deploy/agentcore.js +55 -23
  81. package/dist/cli/commands/deploy/channel-apps.d.ts +17 -0
  82. package/dist/cli/commands/deploy/channel-apps.js +30 -0
  83. package/dist/cli/commands/deploy/docker.js +32 -65
  84. package/dist/cli/commands/deploy/fly.js +28 -12
  85. package/dist/cli/commands/deploy/railway.js +38 -13
  86. package/dist/cli/commands/deploy/secrets-step.d.ts +19 -0
  87. package/dist/cli/commands/deploy/secrets-step.js +66 -0
  88. package/dist/cli/commands/deploy/shared.d.ts +16 -5
  89. package/dist/cli/commands/deploy/shared.js +25 -8
  90. package/dist/cli/commands/deploy.js +53 -60
  91. package/dist/cli/commands/dev.d.ts +2 -0
  92. package/dist/cli/commands/dev.js +15 -3
  93. package/dist/cli/commands/env.js +1 -1
  94. package/dist/cli/commands/info.d.ts +2 -0
  95. package/dist/cli/commands/info.js +60 -25
  96. package/dist/cli/commands/init.js +3 -1
  97. package/dist/cli/commands/invoke.d.ts +2 -0
  98. package/dist/cli/commands/invoke.js +20 -7
  99. package/dist/cli/commands/login.d.ts +0 -7
  100. package/dist/cli/commands/login.js +22 -10
  101. package/dist/cli/commands/logout.js +1 -1
  102. package/dist/cli/commands/mcp.js +14 -6
  103. package/dist/cli/commands/models.js +3 -2
  104. package/dist/cli/commands/schedules.d.ts +4 -2
  105. package/dist/cli/commands/schedules.js +14 -10
  106. package/dist/cli/commands/secrets.d.ts +14 -0
  107. package/dist/cli/commands/secrets.js +213 -0
  108. package/dist/cli/commands/start.d.ts +14 -14
  109. package/dist/cli/commands/start.js +46 -60
  110. package/dist/cli/content-view.d.ts +4 -6
  111. package/dist/cli/content-view.js +5 -11
  112. package/dist/cli/first-run-channels.d.ts +3 -0
  113. package/dist/cli/first-run-channels.js +63 -0
  114. package/dist/cli/instance.d.ts +41 -0
  115. package/dist/cli/instance.js +155 -0
  116. package/dist/cli/invoke-stream.d.ts +5 -2
  117. package/dist/cli/invoke-stream.js +7 -2
  118. package/dist/cli/kernel.js +3 -0
  119. package/dist/cli/program.js +282 -110
  120. package/dist/cli/serve.d.ts +4 -5
  121. package/dist/cli/serve.js +4 -7
  122. package/dist/cli/shared.d.ts +11 -2
  123. package/dist/cli/shared.js +32 -19
  124. package/dist/cli/ticket.d.ts +7 -0
  125. package/dist/cli/ticket.js +106 -30
  126. package/dist/content/declare.d.ts +31 -37
  127. package/dist/content/declare.js +59 -60
  128. package/dist/content/file.d.ts +23 -12
  129. package/dist/content/file.js +48 -35
  130. package/dist/content/git.d.ts +0 -10
  131. package/dist/content/git.js +5 -39
  132. package/dist/content/resolve.d.ts +18 -22
  133. package/dist/content/resolve.js +32 -66
  134. package/dist/content/source.d.ts +7 -8
  135. package/dist/content/source.js +10 -28
  136. package/dist/credentials-page.d.ts +26 -0
  137. package/dist/credentials-page.js +185 -0
  138. package/dist/declared-secrets.d.ts +10 -3
  139. package/dist/declared-secrets.js +8 -4
  140. package/dist/deploy/agentcore/destroy.d.ts +4 -1
  141. package/dist/deploy/agentcore/destroy.js +25 -1
  142. package/dist/deploy/agentcore/forwarder.js +1 -1
  143. package/dist/deploy/agentcore/plan.d.ts +7 -21
  144. package/dist/deploy/agentcore/plan.js +47 -49
  145. package/dist/deploy/agentcore/run.d.ts +18 -10
  146. package/dist/deploy/agentcore/run.js +93 -70
  147. package/dist/deploy/agentcore/runtime-secrets.d.ts +7 -0
  148. package/dist/deploy/agentcore/runtime-secrets.js +108 -0
  149. package/dist/deploy/agentcore/shell.js +12 -19
  150. package/dist/deploy/agentcore/sigv4.d.ts +7 -0
  151. package/dist/deploy/agentcore/sigv4.js +15 -0
  152. package/dist/deploy/agentcore/slr.d.ts +18 -0
  153. package/dist/deploy/agentcore/slr.js +27 -0
  154. package/dist/deploy/agentcore/store.d.ts +7 -0
  155. package/dist/deploy/agentcore/store.js +115 -0
  156. package/dist/deploy/api.d.ts +14 -14
  157. package/dist/deploy/api.js +26 -24
  158. package/dist/deploy/box-shell.d.ts +3 -1
  159. package/dist/deploy/build-context.d.ts +4 -4
  160. package/dist/deploy/build-context.js +11 -15
  161. package/dist/deploy/channel-ingress.d.ts +35 -9
  162. package/dist/deploy/channel-ingress.js +119 -22
  163. package/dist/deploy/container.d.ts +35 -10
  164. package/dist/deploy/container.js +79 -37
  165. package/dist/deploy/delivered-text.d.ts +13 -0
  166. package/dist/deploy/delivered-text.js +86 -0
  167. package/dist/deploy/docker/plan.d.ts +7 -8
  168. package/dist/deploy/docker/plan.js +31 -52
  169. package/dist/deploy/docker/run.d.ts +5 -43
  170. package/dist/deploy/docker/run.js +9 -84
  171. package/dist/deploy/file-store.d.ts +2 -0
  172. package/dist/deploy/file-store.js +17 -0
  173. package/dist/deploy/fly/plan.d.ts +3 -2
  174. package/dist/deploy/fly/plan.js +20 -9
  175. package/dist/deploy/fly/run.d.ts +18 -8
  176. package/dist/deploy/fly/run.js +24 -42
  177. package/dist/deploy/fly/store.d.ts +12 -0
  178. package/dist/deploy/fly/store.js +83 -0
  179. package/dist/deploy/hosts.d.ts +1 -2
  180. package/dist/deploy/hosts.js +1 -2
  181. package/dist/deploy/preflight.d.ts +18 -5
  182. package/dist/deploy/preflight.js +135 -63
  183. package/dist/deploy/railway/ignore.d.ts +8 -0
  184. package/dist/deploy/railway/ignore.js +57 -0
  185. package/dist/deploy/railway/plan.d.ts +1 -2
  186. package/dist/deploy/railway/plan.js +8 -6
  187. package/dist/deploy/railway/run.d.ts +21 -15
  188. package/dist/deploy/railway/run.js +32 -78
  189. package/dist/deploy/railway/store.d.ts +29 -0
  190. package/dist/deploy/railway/store.js +143 -0
  191. package/dist/deploy/runner.d.ts +5 -2
  192. package/dist/deploy/runner.js +4 -3
  193. package/dist/deploy/secret-store.d.ts +63 -0
  194. package/dist/deploy/secret-store.js +98 -0
  195. package/dist/deploy/secrets.d.ts +29 -46
  196. package/dist/deploy/secrets.js +49 -122
  197. package/dist/deploy/workspace.d.ts +6 -12
  198. package/dist/deploy/workspace.js +18 -94
  199. package/dist/dev-supervisor.js +6 -8
  200. package/dist/environment/declare.js +3 -2
  201. package/dist/environment/mise.d.ts +3 -1
  202. package/dist/environment/mise.js +4 -2
  203. package/dist/feishu.d.ts +1 -1
  204. package/dist/feishu.js +1 -1
  205. package/dist/harnesses/models/catalog.d.ts +1 -0
  206. package/dist/harnesses/models/catalog.js +1 -2
  207. package/dist/harnesses/models/environment.d.ts +7 -2
  208. package/dist/harnesses/models/environment.js +9 -5
  209. package/dist/harnesses/models/host.d.ts +0 -7
  210. package/dist/harnesses/models/host.js +10 -1
  211. package/dist/harnesses/models/login.d.ts +0 -8
  212. package/dist/harnesses/models/login.js +5 -4
  213. package/dist/harnesses/models/models.d.ts +7 -2
  214. package/dist/harnesses/models/models.js +28 -4
  215. package/dist/harnesses/pi-durable/agent.js +59 -38
  216. package/dist/harnesses/pi-durable/build.d.ts +12 -0
  217. package/dist/harnesses/pi-durable/build.js +270 -0
  218. package/dist/harnesses/pi-durable/built.d.ts +24 -0
  219. package/dist/harnesses/pi-durable/built.js +136 -0
  220. package/dist/harnesses/pi-durable/config.d.ts +81 -0
  221. package/dist/harnesses/pi-durable/config.js +266 -0
  222. package/dist/harnesses/pi-durable/define.d.ts +63 -13
  223. package/dist/harnesses/pi-durable/define.js +23 -2
  224. package/dist/harnesses/pi-durable/definition.d.ts +72 -0
  225. package/dist/harnesses/pi-durable/definition.js +143 -0
  226. package/dist/harnesses/pi-durable/events.d.ts +4 -3
  227. package/dist/harnesses/pi-durable/events.js +179 -136
  228. package/dist/harnesses/pi-durable/execution-env.d.ts +17 -0
  229. package/dist/harnesses/pi-durable/execution-env.js +11 -0
  230. package/dist/harnesses/pi-durable/mcp.js +1 -3
  231. package/dist/harnesses/pi-durable/open.d.ts +35 -37
  232. package/dist/harnesses/pi-durable/open.js +91 -153
  233. package/dist/harnesses/pi-durable/reads.d.ts +0 -1
  234. package/dist/harnesses/pi-durable/reads.js +1 -3
  235. package/dist/harnesses/pi-durable/registry.d.ts +4 -6
  236. package/dist/harnesses/pi-durable/registry.js +29 -26
  237. package/dist/harnesses/pi-durable/release.d.ts +2 -2
  238. package/dist/harnesses/pi-durable/release.js +10 -7
  239. package/dist/harnesses/pi-durable/serve.d.ts +91 -15
  240. package/dist/harnesses/pi-durable/serve.js +109 -47
  241. package/dist/harnesses/pi-durable/sessions.js +15 -12
  242. package/dist/harnesses/pi-durable/text-sources.d.ts +30 -0
  243. package/dist/harnesses/pi-durable/text-sources.js +55 -0
  244. package/dist/harnesses/pi-durable/text.d.ts +16 -4
  245. package/dist/harnesses/pi-durable/text.js +115 -59
  246. package/dist/harnesses/pi-durable/wake.js +8 -8
  247. package/dist/http/client.js +9 -3
  248. package/dist/http/contract.d.ts +3 -3
  249. package/dist/http/server.d.ts +1 -1
  250. package/dist/http/server.js +1 -1
  251. package/dist/index.d.ts +8 -5
  252. package/dist/index.js +4 -1
  253. package/dist/instance-name.d.ts +7 -0
  254. package/dist/instance-name.js +26 -0
  255. package/dist/lark.d.ts +1 -1
  256. package/dist/lark.js +1 -1
  257. package/dist/mcp/client.js +11 -2
  258. package/dist/mcp/config.d.ts +70 -18
  259. package/dist/mcp/config.js +136 -53
  260. package/dist/mcp/host.d.ts +5 -2
  261. package/dist/mcp/host.js +14 -6
  262. package/dist/mcp/sign-in.js +2 -2
  263. package/dist/mcp-api.d.ts +14 -0
  264. package/dist/paths.d.ts +22 -8
  265. package/dist/paths.js +44 -11
  266. package/dist/runtime-websocket.d.ts +14 -0
  267. package/dist/runtime-websocket.js +39 -0
  268. package/dist/scaffold/add-channel.d.ts +9 -16
  269. package/dist/scaffold/add-channel.js +13 -83
  270. package/dist/scaffold/templates/env.example +4 -5
  271. package/dist/scaffold/templates/fastagent.config.ts +20 -14
  272. package/dist/scaffold/templates/gitignore +9 -6
  273. package/dist/scaffold/templates.d.ts +6 -2
  274. package/dist/scaffold/templates.js +7 -2
  275. package/dist/scaffold/vendor-skill.js +4 -2
  276. package/dist/service.d.ts +44 -6
  277. package/dist/service.js +110 -10
  278. package/dist/serving-command.d.ts +10 -0
  279. package/dist/serving-command.js +1 -0
  280. package/dist/slack.d.ts +1 -1
  281. package/dist/slack.js +1 -1
  282. package/dist/strip-edges.d.ts +10 -0
  283. package/dist/strip-edges.js +22 -0
  284. package/dist/telegram.d.ts +1 -1
  285. package/dist/telegram.js +1 -1
  286. package/dist/tunnel.d.ts +0 -1
  287. package/dist/tunnel.js +2 -8
  288. package/dist/value-file.d.ts +11 -6
  289. package/dist/value-file.js +21 -9
  290. package/dist/version.d.ts +7 -1
  291. package/dist/version.js +9 -3
  292. package/package.json +6 -5
  293. package/dist/channels/lark/credentials-page.d.ts +0 -25
  294. package/dist/channels/lark/credentials-page.js +0 -206
  295. package/dist/channels/lark/onboard.d.ts +0 -52
  296. package/dist/channels/lark/onboard.js +0 -86
  297. package/dist/channels/slack/config-api.d.ts +0 -69
  298. package/dist/channels/slack/config-api.js +0 -162
  299. package/dist/channels/slack/onboard.d.ts +0 -40
  300. package/dist/channels/slack/onboard.js +0 -136
  301. package/dist/channels/slack/onboarding-state.d.ts +0 -31
  302. package/dist/channels/slack/onboarding-state.js +0 -64
  303. package/dist/channels/slack/register-webhook.d.ts +0 -12
  304. package/dist/channels/slack/register-webhook.js +0 -67
  305. package/dist/channels/slack/setup-server.d.ts +0 -32
  306. package/dist/channels/slack/setup-server.js +0 -246
  307. package/dist/cli/add-feishu.d.ts +0 -41
  308. package/dist/cli/add-feishu.js +0 -391
  309. package/dist/cli/add-slack.d.ts +0 -18
  310. package/dist/cli/add-slack.js +0 -216
  311. package/dist/harnesses/pi-durable/parked.d.ts +0 -18
  312. package/dist/harnesses/pi-durable/parked.js +0 -27
@@ -1,14 +1,14 @@
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
3
  import { basename, dirname, join, resolve } from "node:path";
4
- import { webhookPaths } from "../../../deploy/channel-ingress.js";
5
- import { DOCKER_COMPOSE_FILE, DOCKER_VALUE_FILE, composeHasTunnelService, isGeneratedCompose, planDockerDeploy, toDockerProjectName, } from "../../../deploy/docker/plan.js";
4
+ import { ownPublicUrl } from "../../../deploy/container.js";
5
+ import { DOCKER_COMPOSE_FILE, DOCKER_VALUE_FILE, ingressNotes, 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
- import { openExternalUrl } from "../../../open-url.js";
9
- import { SECRET_FILE_MODE, exists, readTextIfExists, resolveStateRoot } from "../../../paths.js";
10
- import { announceWebhooks } from "../../../tunnel.js";
11
- import { assembleSecrets } from "../../../deploy/secrets.js";
8
+ import { SECRET_FILE_MODE, exists } from "../../../paths.js";
9
+ import { fileStore } from "../../../deploy/file-store.js";
10
+ import { dotEnvPath } from "../../../value-file.js";
11
+ import { deploySecrets } from "./secrets-step.js";
12
12
  import { processShell } from "../../../deploy/box-shell.js";
13
13
  import { failStartup } from "../../fail.js";
14
14
  import { boxLoginStep, leftToPerson } from "./shared.js";
@@ -26,7 +26,7 @@ export const dockerHost = {
26
26
  }
27
27
  },
28
28
  async deploy(ctx) {
29
- const { opts, agentDir, channels, webhookChannels, pre, write } = ctx;
29
+ const { opts, agentDir, channels, pre, write } = ctx;
30
30
  const { modelAuth, boxLogin, container, port, declaredSecrets, values, valueFile } = pre;
31
31
  // Compose requires its committed env_file to exist, even when no values are declared.
32
32
  const composeValueFile = join(agentDir, DOCKER_VALUE_FILE);
@@ -35,8 +35,8 @@ export const dockerHost = {
35
35
  await writeFile(composeValueFile, "", { mode: SECRET_FILE_MODE });
36
36
  // Compose interpolates `$VAR` INSIDE env_file values (`format: raw` needs Compose 2.30), so a credential
37
37
  // containing `$` reaches the container rewritten — silently, and differently from what this pre-flight read.
38
- // No escaping advice: this ONE file is also read literally by `dev`/`start` (`parseEnvContent`) and pushed
39
- // as-is by every other host, so `$$` would fix Docker by corrupting all of them.
38
+ // No escaping advice: this ONE file is also read literally by `dev`/`start` (`parseEnvContent`) and is the source
39
+ // `secrets push` sets every other host's values from, so `$$` would fix Docker by corrupting all of them.
40
40
  const dollarValues = [...values].filter(([, value]) => value.includes("$")).map(([name]) => name);
41
41
  if (dollarValues.length > 0) {
42
42
  console.error(`[fastagent] warn: ${dollarValues.join(", ")} contain "$", which Compose expands when reading ${valueFile} ` +
@@ -44,109 +44,76 @@ export const dockerHost = {
44
44
  `ONLY — \`fastagent dev\`/\`start\` and the other hosts read this file literally and would keep the extra ` +
45
45
  `"$". Prefer a value without "$".`);
46
46
  }
47
- const hasDeclaredChannels = channels.length > 0;
48
- const projectName = toDockerProjectName(basename(agentDir));
49
- const dockerPlan = (tunnel) => planDockerDeploy({
50
- projectName,
47
+ const plan = planDockerDeploy({
48
+ projectName: toDockerProjectName(basename(agentDir)),
51
49
  port,
52
50
  boxLogin,
53
51
  channels,
54
- tunnel,
55
52
  secrets: pre.secrets,
56
53
  valueFile,
57
54
  ...container,
58
55
  });
59
- const requestedTunnel = !!opts.tunnel && (!hasDeclaredChannels || webhookChannels.length > 0);
60
- if (opts.tunnel && hasDeclaredChannels && webhookChannels.length === 0) {
61
- console.error(`[fastagent] note: --tunnel skipped — every channel uses a long connection`);
62
- }
63
- let plan = dockerPlan(requestedTunnel);
64
- // An existing Compose file is authoritative: shape its comparison/runbook from the topology on disk, regardless
65
- // of the current flag.
66
- const composeFile = join(agentDir, plan.composePath);
67
- let keptWithoutRequestedTunnel = false;
68
- // Same ownership question as fly.toml: a hand-owned compose file survives --force, so the plan must describe the
69
- // topology that will actually be there.
70
- const composeText = await readTextIfExists(composeFile).catch(failStartup);
71
- if (composeText !== undefined && (!opts.force || !isGeneratedCompose(composeText))) {
72
- const existingHasTunnel = composeHasTunnelService(composeText);
73
- plan = dockerPlan(existingHasTunnel);
74
- keptWithoutRequestedTunnel = requestedTunnel && !existingHasTunnel;
75
- }
76
56
  await write(plan.artifacts, { force: !!opts.force });
77
57
  if (opts.run) {
78
58
  return runDeployDocker({
79
59
  agentDir,
80
60
  composeFile: plan.composePath,
81
61
  port,
82
- requireTunnel: requestedTunnel,
83
62
  modelAuth,
63
+ modelKey: pre.modelKey,
84
64
  boxLogin,
85
65
  answerer: ctx.answerer,
66
+ api: ctx.api,
86
67
  channels,
87
68
  declaredSecrets,
88
69
  values,
89
70
  valueFile,
71
+ publicUrl: ownPublicUrl(container.config),
72
+ config: opts.config,
90
73
  });
91
74
  }
92
- if (keptWithoutRequestedTunnel) {
93
- console.error(`[fastagent] warn: --tunnel was requested but kept ${plan.composePath} has no "tunnel" service — ` +
94
- `edit it, delete it and regenerate, or pass --force`);
95
- }
96
75
  console.log(plan.runbook.join("\n"));
97
76
  return `http://127.0.0.1:${port}`;
98
77
  },
99
78
  };
100
79
  /** `deploy docker --run`: reconcile the user-owned local topology, then log the box in if it must. */
101
80
  async function runDeployDocker(params) {
102
- const { agentDir, composeFile, port, requireTunnel, channels } = params;
81
+ const { agentDir, composeFile, port, channels } = params;
103
82
  const leave = leftToPerson(params.answerer);
104
- const { secrets, missingSecrets } = assembleSecrets({
83
+ const valueFile = dotEnvPath(agentDir);
84
+ const provide = deploySecrets({
85
+ host: "docker",
86
+ label: "Docker",
87
+ agentDir,
88
+ opts: { config: params.config },
89
+ answerer: params.answerer,
90
+ api: params.api,
105
91
  modelAuth: params.modelAuth,
106
- declared: params.declaredSecrets,
92
+ modelKey: params.modelKey,
93
+ declaredSecrets: params.declaredSecrets,
94
+ valueFile: params.valueFile,
107
95
  values: params.values,
108
96
  });
109
97
  const outcome = await deployDockerRun({
110
98
  composeFile,
111
99
  port,
112
- secrets,
113
- missingSecrets,
114
- valueFile: params.valueFile,
115
- requireTunnel,
116
- channels,
100
+ provideSecrets: () => provide(fileStore(agentDir, valueFile)),
117
101
  ...boxLoginStep("docker", params, () => composeShell(composeFile, agentDir)),
118
- announce: (tunnelUrl) => announceWebhooks(agentDir, tunnelUrl, channels, {
119
- // A ticket's process opens no browser: its person is told, through `leave`.
120
- ...(leave === undefined ? { openUrl: openExternalUrl } : { leave }),
121
- stateRoot: resolveStateRoot(agentDir),
122
- env: Object.fromEntries(params.values),
123
- }),
124
102
  }, spawnRunner("docker", agentDir), (message) => console.error(`[fastagent] ${message}`));
125
103
  const compose = `docker compose -f ${composeFile}`;
126
104
  // Compose reached "up" iff the driver could report where it is.
127
- const isUp = outcome.ok || outcome.url !== undefined || outcome.tunnelUrl !== undefined;
105
+ const isUp = outcome.ok || outcome.url !== undefined;
128
106
  if (outcome.url)
129
107
  console.error(`[fastagent] running → ${outcome.url}`);
130
108
  if (isUp) {
131
109
  console.error(`[fastagent] logs: ${compose} logs -f agent`);
132
110
  console.error(`[fastagent] stop: ${compose} down (state volume is kept)`);
133
111
  }
134
- // BEFORE failStartup: a registration gate says "re-run this deploy", and a re-run rebuilds the tunnel service.
135
- if (outcome.tunnelUrl) {
136
- console.error(`[fastagent] note: Quick Tunnel URLs are ephemeral — after the tunnel container/Docker daemon ` +
137
- `restarts, re-run this deploy so webhooks receive the new URL`);
138
- }
139
112
  if (!outcome.ok)
140
113
  failStartup(new Error(`deploy stopped: ${outcome.gate}`));
141
- if (outcome.tunnelUrl)
142
- return outcome.tunnelUrl;
143
- const paths = webhookPaths(channels);
144
- if (paths.length > 0) {
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);
114
+ for (const note of ingressNotes(channels, params.publicUrl, outcome.url && `${outcome.url}/health`)) {
115
+ console.error(`[fastagent] note: ${note}`);
116
+ leave?.(note);
150
117
  }
151
118
  return outcome.url;
152
119
  }
@@ -5,11 +5,12 @@ import { deployFlyRun } from "../../../deploy/fly/run.js";
5
5
  import { spawnRunner } from "../../../deploy/runner.js";
6
6
  import { readTextIfExists } from "../../../paths.js";
7
7
  import { residencyFor } from "../../../deploy/residency.js";
8
- import { assembleSecrets } from "../../../deploy/secrets.js";
9
8
  import { processShell } from "../../../deploy/box-shell.js";
9
+ import { withPublicUrl } from "../../../deploy/container.js";
10
10
  import { failStartup } from "../../fail.js";
11
+ import { deploySecrets } from "./secrets-step.js";
11
12
  import { FLY_LOGIN, hostLogin } from "./host-login.js";
12
- import { boxLoginStep, registrarsFor } from "./shared.js";
13
+ import { boxLoginStep, registrarsFor, urlRecorder } from "./shared.js";
13
14
  export const flyHost = {
14
15
  isOurs: (path, content) => path.endsWith("fly.toml") && isGeneratedFlyToml(content),
15
16
  async shell(agentDir) {
@@ -47,6 +48,8 @@ export const flyHost = {
47
48
  console.error(`[fastagent] warn: ${msg}`);
48
49
  }
49
50
  }
51
+ const reachedAt = `https://${appName}.fly.dev`;
52
+ const delivered = withPublicUrl(container.config, reachedAt);
50
53
  const plan = planFlyDeploy({
51
54
  appName,
52
55
  port,
@@ -55,21 +58,27 @@ export const flyHost = {
55
58
  secrets: pre.secrets,
56
59
  hasCron,
57
60
  ...container,
61
+ // The app's URL is known before the build, so the image names it: the box points its own channels there.
62
+ config: delivered.config,
63
+ publicUrl: delivered.publicUrl,
58
64
  });
59
65
  await write(plan.artifacts, { force: !!opts.force });
60
- const reachedAt = `https://${appName}.fly.dev`;
61
66
  if (opts.run) {
62
67
  await runDeployFly({
63
68
  agentDir,
64
69
  appName,
65
70
  modelAuth,
71
+ modelKey: pre.modelKey,
66
72
  boxLogin,
67
73
  answerer: ctx.answerer,
74
+ api: ctx.api,
68
75
  rerun: ctx.rerun,
69
76
  channels,
70
77
  declaredSecrets,
71
78
  values,
72
79
  valueFile,
80
+ publicUrl: delivered.publicUrl,
81
+ config: opts.config,
73
82
  });
74
83
  return reachedAt;
75
84
  }
@@ -85,23 +94,30 @@ async function runDeployFly(params) {
85
94
  if ((await fly(["version"], { capture: true })).code === 127) {
86
95
  failStartup(new Error(`flyctl not found — install it: https://fly.io/docs/flyctl/install, then re-run`));
87
96
  }
88
- const { secrets, missingSecrets } = assembleSecrets({
89
- modelAuth: params.modelAuth,
90
- declared: params.declaredSecrets,
91
- values: params.values,
92
- });
93
97
  const outcome = await deployFlyRun({
94
98
  appName,
95
- secrets,
96
- missingSecrets,
97
- valueFile: params.valueFile,
99
+ provideSecrets: deploySecrets({
100
+ host: "fly",
101
+ label: "Fly",
102
+ agentDir,
103
+ opts: { config: params.config },
104
+ answerer: params.answerer,
105
+ api: params.api,
106
+ modelAuth: params.modelAuth,
107
+ modelKey: params.modelKey,
108
+ declaredSecrets: params.declaredSecrets,
109
+ valueFile: params.valueFile,
110
+ values: params.values,
111
+ }),
98
112
  channels,
99
113
  flyConfig: "fly.toml",
100
114
  dockerfile: "Dockerfile",
101
115
  ...boxLoginStep("fly", params, () => flyShell(appName, agentDir)),
102
116
  ...hostLogin(FLY_LOGIN, params.answerer, agentDir),
103
117
  forAgent: `${params.rerun} --json`,
104
- }, fly, (m) => console.error(`[fastagent] ${m}`), registrarsFor(agentDir, params.values, params.answerer));
118
+ publicUrl: params.publicUrl,
119
+ ...urlRecorder(agentDir, params.config, params.values, params.answerer),
120
+ }, fly, (m) => console.error(`[fastagent] ${m}`), registrarsFor(params.values, params.answerer));
105
121
  if (!outcome.ok)
106
122
  failStartup(new Error(`deploy stopped: ${outcome.gate}`));
107
123
  console.error(`[fastagent] deployed → https://${appName}.fly.dev`);
@@ -3,18 +3,27 @@
3
3
  * planRailwayDeploy).
4
4
  */
5
5
  import { basename } from "node:path";
6
+ import { RAILWAYIGNORE_FILE, isGeneratedRailwayignore, keptRailwayignoreProblem, } from "../../../deploy/railway/ignore.js";
6
7
  import { planRailwayDeploy, toRailwayName } from "../../../deploy/railway/plan.js";
7
8
  import { deployRailwayRun } from "../../../deploy/railway/run.js";
8
9
  import { spawnRunner } from "../../../deploy/runner.js";
9
- import { assembleSecrets } from "../../../deploy/secrets.js";
10
10
  import { processShell } from "../../../deploy/box-shell.js";
11
+ import { deliveredConfigArtifact, withPublicUrl } from "../../../deploy/container.js";
11
12
  import { failStartup } from "../../fail.js";
13
+ import { deploySecrets } from "./secrets-step.js";
12
14
  import { RAILWAY_LOGIN, hostLogin } from "./host-login.js";
13
- import { boxLoginStep, registrarsFor } from "./shared.js";
15
+ import { boxLoginStep, registrarsFor, urlRecorder } from "./shared.js";
14
16
  export const railwayHost = {
15
- // Railway's only artifacts are the container's, which planArtifacts owns for every host.
16
- isOurs: () => false,
17
+ isOurs: (path, content) => path === RAILWAYIGNORE_FILE && isGeneratedRailwayignore(content),
17
18
  shell: async (agentDir) => railwayShell(toRailwayName(basename(agentDir)), agentDir),
19
+ validate({ opts, agentDir }) {
20
+ const problem = keptRailwayignoreProblem(agentDir);
21
+ if (problem === undefined)
22
+ return;
23
+ if (opts.run)
24
+ failStartup(new Error(`deploy stopped: ${problem}`));
25
+ console.error(`[fastagent] warn: ${problem}`);
26
+ },
18
27
  async deploy(ctx) {
19
28
  const { opts, agentDir, pre, channels, write } = ctx;
20
29
  const { hasCron, modelAuth, boxLogin, container, declaredSecrets, values, valueFile } = pre;
@@ -31,16 +40,25 @@ export const railwayHost = {
31
40
  if (opts.run) {
32
41
  return runDeployRailway({
33
42
  agentDir,
43
+ // The URL Railway mints is read before the upload, and the image delivers it as `http.publicUrl`.
44
+ deliverPublicUrl: async (url) => {
45
+ const delivered = withPublicUrl(container.config, url);
46
+ await write([deliveredConfigArtifact(delivered.config)], { force: true });
47
+ return delivered.publicUrl;
48
+ },
34
49
  name: serviceName,
35
50
  modelAuth,
51
+ modelKey: pre.modelKey,
36
52
  boxLogin,
37
53
  answerer: ctx.answerer,
54
+ api: ctx.api,
38
55
  rerun: ctx.rerun,
39
56
  channels,
40
57
  declaredSecrets,
41
58
  values,
42
59
  valueFile,
43
60
  intoLinked: !!opts.intoLinked,
61
+ config: opts.config,
44
62
  });
45
63
  }
46
64
  console.log(plan.runbook.join("\n"));
@@ -56,23 +74,30 @@ async function runDeployRailway(params) {
56
74
  if ((await railway(["--version"], { capture: true })).code === 127) {
57
75
  failStartup(new Error(`railway CLI not found — install it: https://docs.railway.com/guides/cli, then re-run`));
58
76
  }
59
- const { secrets, missingSecrets } = assembleSecrets({
60
- modelAuth: params.modelAuth,
61
- declared: params.declaredSecrets,
62
- values: params.values,
63
- });
64
77
  const outcome = await deployRailwayRun({
65
78
  name,
66
79
  mountPath: "/data",
67
- secrets,
68
- missingSecrets,
69
- valueFile: params.valueFile,
80
+ provideSecrets: deploySecrets({
81
+ host: "railway",
82
+ label: "Railway",
83
+ agentDir,
84
+ opts: { config: params.config },
85
+ answerer: params.answerer,
86
+ api: params.api,
87
+ modelAuth: params.modelAuth,
88
+ modelKey: params.modelKey,
89
+ declaredSecrets: params.declaredSecrets,
90
+ valueFile: params.valueFile,
91
+ values: params.values,
92
+ }),
70
93
  channels,
71
94
  intoLinked,
95
+ deliverPublicUrl: params.deliverPublicUrl,
96
+ ...urlRecorder(agentDir, params.config, params.values, params.answerer),
72
97
  ...boxLoginStep("railway", params, () => railwayShell(name, agentDir)),
73
98
  ...hostLogin(RAILWAY_LOGIN, params.answerer, agentDir),
74
99
  forAgent: `${params.rerun} --json`,
75
- }, railway, (m) => console.error(`[fastagent] ${m}`), registrarsFor(agentDir, params.values, params.answerer));
100
+ }, railway, (m) => console.error(`[fastagent] ${m}`), registrarsFor(params.values, params.answerer));
76
101
  if (!outcome.ok)
77
102
  failStartup(new Error(`deploy stopped: ${outcome.gate}`));
78
103
  console.error(`[fastagent] deployed → ${outcome.url}`);
@@ -0,0 +1,19 @@
1
+ import type { DeclaredSecret } from "../../../declared-secrets.ts";
2
+ import { type SecretStore } from "../../../deploy/secret-store.ts";
3
+ import { type DeploymentApi } from "../../../deploy/api.ts";
4
+ import { type ModelKey } from "../../../deploy/secrets.ts";
5
+ import type { Answerer, DeployOptions } from "./shared.ts";
6
+ export declare function deploySecrets(input: {
7
+ host: string;
8
+ label: string;
9
+ agentDir: string;
10
+ opts: Pick<DeployOptions, "config">;
11
+ answerer: Answerer;
12
+ api: DeploymentApi;
13
+ modelAuth: string | undefined;
14
+ modelKey: ModelKey;
15
+ declaredSecrets: readonly DeclaredSecret[];
16
+ /** The production value file this deploy loaded, and its values. */
17
+ valueFile: string;
18
+ values: ReadonlyMap<string, string>;
19
+ }): (store: SecretStore) => Promise<string | undefined>;
@@ -0,0 +1,66 @@
1
+ /**
2
+ * A deploy's step that makes the target hold every value the deployment needs (secret-store.ts), written once for the
3
+ * hosts that keep their values in a store of their own: who is asked is the deploy's answerer, and the file a source
4
+ * reads is the production value file the deploy already loaded.
5
+ */
6
+ import { confirm, isCancel, password } from "@clack/prompts";
7
+ import { provideSecrets, } from "../../../deploy/secret-store.js";
8
+ import { mintApiKey } from "../../../deploy/api.js";
9
+ import { declaredNames, secretNeeds } from "../../../deploy/secrets.js";
10
+ import { API_KEY_VARIABLE } from "../../../http/server.js";
11
+ import { unprotectedSecretsNote, writeValues } from "../../../value-file.js";
12
+ import { displayPath } from "../../../paths.js";
13
+ import { shellArg } from "../../shared.js";
14
+ /** The questions of a person at this terminal; a cancel stops the deploy. */
15
+ const terminalAsker = {
16
+ async confirm(question) {
17
+ const answer = await confirm({ message: question, initialValue: true });
18
+ if (isCancel(answer))
19
+ throw new Error("cancelled");
20
+ return answer;
21
+ },
22
+ async secret(need) {
23
+ const answer = await password({ message: `${need.name} (${need.hint})` });
24
+ if (isCancel(answer))
25
+ throw new Error("cancelled");
26
+ const value = String(answer);
27
+ if (value.trim() === "")
28
+ throw new Error(`${need.name} is empty`);
29
+ return value;
30
+ },
31
+ };
32
+ /** The `fastagent secrets` commands for `host` as this deploy would have them run: its agent directory and configuration. */
33
+ function secretsCommands(host, agentDir, config) {
34
+ const agent = displayPath(process.cwd(), agentDir);
35
+ const rest = `${agent === undefined ? "" : ` ${shellArg(agent)}`}${config === undefined ? "" : ` --config ${shellArg(config)}`}`;
36
+ return {
37
+ push: `fastagent secrets push ${host}${rest}`,
38
+ set: (name) => `fastagent secrets set ${host} ${name}${rest}`,
39
+ };
40
+ }
41
+ export function deploySecrets(input) {
42
+ const { host, label, agentDir, opts, answerer, api, modelAuth, modelKey, declaredSecrets, valueFile, values } = input;
43
+ return (store) => provideSecrets({
44
+ host: label,
45
+ store,
46
+ needs: secretNeeds(modelAuth, declaredSecrets, modelKey),
47
+ known: [...declaredNames(modelAuth, declaredSecrets, modelKey), ...(api.served ? [API_KEY_VARIABLE] : [])],
48
+ ...(values.size === 0 ? {} : { source: { file: valueFile, values } }),
49
+ ...(answerer.kind === "terminal" ? { asker: terminalAsker } : {}),
50
+ commands: secretsCommands(host, agentDir, opts.config),
51
+ say: (line) => {
52
+ console.error(`[fastagent] note: ${line}`);
53
+ if (answerer.kind === "ticket")
54
+ answerer.step.person.afterwards(line);
55
+ },
56
+ inform: (line) => console.error(`[fastagent] note: ${line}`),
57
+ ...(api.served ? { mint: [mintApiKey()] } : {}),
58
+ onMinted: async (minted) => {
59
+ const names = Object.keys(minted);
60
+ console.error(`[fastagent] made ${names.join(", ")} for the Agent API, set on ${label} and kept in ${valueFile}`);
61
+ const { unprotectedSecretsDir } = await writeValues(agentDir, "Agent API", names.map((name) => ({ name, value: minted[name] })), names);
62
+ if (unprotectedSecretsDir !== undefined)
63
+ console.error(`[fastagent] note: ${unprotectedSecretsNote(unprotectedSecretsDir)}`);
64
+ },
65
+ });
66
+ }
@@ -1,15 +1,17 @@
1
1
  import type { DeclaredChannel } from "../../../channels/discover.ts";
2
- import type { Registrars } from "../../../deploy/channel-ingress.ts";
2
+ import type { Registrars, UrlRecorder } from "../../../deploy/channel-ingress.ts";
3
3
  import type { DeployPreflight } from "../../../deploy/preflight.ts";
4
- import type { AgentConfig } from "../../../harnesses/pi-durable/define.ts";
4
+ import type { DeploymentApi } from "../../../deploy/api.ts";
5
+ import type { EffectiveConfig } from "../../../harnesses/pi-durable/config.ts";
5
6
  import { type BoxLoginStep, type BoxShell } from "../../../deploy/box-shell.ts";
6
7
  import type { TicketStep } from "../../ticket.ts";
7
8
  import type { DeployHost } from "../../../deploy/hosts.ts";
8
9
  export interface DeployOptions {
9
10
  run?: boolean;
10
- tunnel?: boolean;
11
11
  force?: boolean;
12
12
  intoLinked?: boolean;
13
+ /** `--config <file>`: the instance configuration the deployment delivers. */
14
+ config?: string;
13
15
  /** false ⇔ `--no-input`. */
14
16
  input?: boolean;
15
17
  /** `--json`: a coding agent's `--run`, run in a ticket that hands each step only a person can take to its user. */
@@ -48,10 +50,12 @@ interface DeployContext {
48
50
  /** The command that runs this deploy again, from where it ran: its agent directory and flags. */
49
51
  rerun: string;
50
52
  agentDir: string;
51
- config: AgentConfig;
53
+ config: EffectiveConfig;
52
54
  pre: Extract<DeployPreflight, {
53
55
  ok: true;
54
56
  }>;
57
+ /** Whether the deployment serves the Agent API, so whether the deploy mints its key. */
58
+ api: DeploymentApi;
55
59
  channels: readonly DeclaredChannel[];
56
60
  webhookChannels: readonly DeclaredChannel[];
57
61
  /** Write this host's planned artifacts into the agent directory under the ownership rule. */
@@ -78,8 +82,15 @@ export interface HostDeploy {
78
82
  }
79
83
  /** Where a step left to the person goes for `answerer`: a ticket's `afterwards`; nowhere else but the log. */
80
84
  export declare function leftToPerson(answerer: Answerer): ((step: string) => void) | undefined;
85
+ /**
86
+ * At a terminal, with a `--config` file to write to: record each URL a person sets on a platform's page once the box
87
+ * confirms it, so later deploys of that configuration, CI included, report it `registered` from the start.
88
+ */
89
+ export declare function urlRecorder(agentDir: string, config: string | undefined, values: ReadonlyMap<string, string>, answerer: Answerer): {
90
+ recordUrl?: UrlRecorder;
91
+ };
81
92
  /** The registrars every host driver gets; a channel's manual step also goes to `answerer`'s person. */
82
- export declare function registrarsFor(agentDir: string, values: ReadonlyMap<string, string>, answerer: Answerer): Registrars;
93
+ export declare function registrarsFor(values: ReadonlyMap<string, string>, answerer: Answerer): Registrars;
83
94
  /**
84
95
  * The login `--run` starts on the box it just deployed, before any webhook is pointed at it (a host driver's `boxLogin`): for
85
96
  * the model's provider, keeping a credential the box already holds (a redeploy), and asking only when a person can
@@ -3,28 +3,45 @@
3
3
  * ownership rule.
4
4
  */
5
5
  import { mkdir, readFile, writeFile } from "node:fs/promises";
6
- import { basename, dirname, join } from "node:path";
6
+ import { dirname, join, resolve } from "node:path";
7
7
  import { registerFeishuWebhook } from "../../../channels/feishu/register-webhook.js";
8
8
  import { DEPLOY_REGISTRATION_ATTEMPTS } from "../../../channels/registration.js";
9
- import { registerSlackWebhook } from "../../../channels/slack/register-webhook.js";
10
9
  import { registerTelegramWebhook } from "../../../channels/telegram/register-webhook.js";
11
- import { BUILD_OUTPUTS, IMAGE_DEFINITION_DIR, isGeneratedDockerfile, isGeneratedDockerignore, } from "../../../deploy/container.js";
12
- import { exists, resolveStateRoot } from "../../../paths.js";
10
+ import { recordChannelUrl } from "../../channel-app.js";
11
+ import { BUILD_OUTPUTS, IMAGE_APP_DIR, isGeneratedDockerfile, isGeneratedDockerignore, } from "../../../deploy/container.js";
12
+ import { exists } from "../../../paths.js";
13
13
  import { deploymentLoginCommand } from "../../../deploy/box-shell.js";
14
14
  import { loginOnBox, ticketLoginIO } from "../../box-login.js";
15
15
  /** Where a step left to the person goes for `answerer`: a ticket's `afterwards`; nowhere else but the log. */
16
16
  export function leftToPerson(answerer) {
17
17
  return answerer.kind === "ticket" ? (step) => answerer.step.person.afterwards(step) : undefined;
18
18
  }
19
+ /**
20
+ * At a terminal, with a `--config` file to write to: record each URL a person sets on a platform's page once the box
21
+ * confirms it, so later deploys of that configuration, CI included, report it `registered` from the start.
22
+ */
23
+ export function urlRecorder(agentDir, config, values, answerer) {
24
+ if (answerer.kind !== "terminal" || config === undefined)
25
+ return {};
26
+ const file = resolve(config);
27
+ return {
28
+ recordUrl: {
29
+ waitMs: 10 * 60_000,
30
+ write: async (kind, url) => {
31
+ await recordChannelUrl(agentDir, file, kind, url, values);
32
+ return file;
33
+ },
34
+ },
35
+ };
36
+ }
19
37
  /** The registrars every host driver gets; a channel's manual step also goes to `answerer`'s person. */
20
- export function registrarsFor(agentDir, values, answerer) {
38
+ export function registrarsFor(values, answerer) {
21
39
  const attempts = DEPLOY_REGISTRATION_ATTEMPTS;
22
40
  const env = Object.fromEntries(values);
23
41
  const leave = leftToPerson(answerer);
24
42
  return {
25
43
  ...(leave === undefined ? {} : { leave }),
26
44
  telegram: (baseUrl) => registerTelegramWebhook(baseUrl, { attempts, env }),
27
- slack: (baseUrl) => registerSlackWebhook(baseUrl, { stateRoot: resolveStateRoot(agentDir), environment: "production", attempts }),
28
45
  feishu: (baseUrl, kind) => registerFeishuWebhook(baseUrl, kind, { attempts, env }),
29
46
  };
30
47
  }
@@ -73,7 +90,7 @@ export async function planArtifacts(target, artifacts, options) {
73
90
  for (const a of artifacts) {
74
91
  const abs = join(target, a.path);
75
92
  // Pure build output, not operator-owned configuration. It must track the generated template/runbook.
76
- if (BUILD_OUTPUTS.includes(basename(a.path)) || options.alwaysWrite?.includes(a.path)) {
93
+ if (BUILD_OUTPUTS.includes(a.path) || options.alwaysWrite?.includes(a.path)) {
77
94
  write(a.path, abs, a.content);
78
95
  continue;
79
96
  }
@@ -86,7 +103,7 @@ export async function planArtifacts(target, artifacts, options) {
86
103
  (a.path.endsWith(".dockerignore")
87
104
  ? `; see the preflight warnings for what it must exclude`
88
105
  : a.path.endsWith("Dockerfile")
89
- ? `; deploy still assumes it listens on $PORT and runs \`fastagent start ${IMAGE_DEFINITION_DIR}\``
106
+ ? `; deploy still assumes it listens on $PORT and runs \`fastagent start ${IMAGE_APP_DIR}\``
90
107
  : ``));
91
108
  continue;
92
109
  }