@fastagent-sh/fastagent 0.28.0 → 0.30.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.
Files changed (325) hide show
  1. package/README.md +10 -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 +3 -1
  15. package/dist/channels/feishu/feishu-api.js +4 -0
  16. package/dist/channels/feishu/feishu.d.ts +0 -3
  17. package/dist/channels/feishu/feishu.js +10 -10
  18. package/dist/channels/feishu/register-app.d.ts +2 -0
  19. package/dist/channels/feishu/register-app.js +4 -6
  20. package/dist/channels/feishu/scaffold/channel.ts +32 -34
  21. package/dist/channels/feishu/scaffold/feishu-send.ts +5 -5
  22. package/dist/channels/feishu/scaffold/feishu-threads.ts +3 -3
  23. package/dist/channels/feishu/setup-mode.d.ts +2 -12
  24. package/dist/channels/feishu/setup-mode.js +2 -22
  25. package/dist/channels/feishu/setup.d.ts +12 -0
  26. package/dist/channels/feishu/setup.js +197 -0
  27. package/dist/channels/kit/abortable.d.ts +8 -0
  28. package/dist/channels/kit/abortable.js +22 -0
  29. package/dist/channels/lark/lark.d.ts +0 -3
  30. package/dist/channels/lark/lark.js +0 -5
  31. package/dist/channels/lark/scaffold/channel.ts +33 -35
  32. package/dist/channels/lark/scaffold/lark-send.ts +5 -5
  33. package/dist/channels/lark/scaffold/lark-threads.ts +3 -3
  34. package/dist/channels/lark/setup.d.ts +9 -0
  35. package/dist/channels/lark/setup.js +57 -0
  36. package/dist/channels/setup.d.ts +112 -0
  37. package/dist/channels/setup.js +31 -0
  38. package/dist/channels/slack/history.js +1 -1
  39. package/dist/channels/slack/manifest.d.ts +17 -7
  40. package/dist/channels/slack/manifest.js +12 -16
  41. package/dist/channels/slack/scaffold/channel.ts +44 -30
  42. package/dist/channels/slack/scaffold/slack-send.ts +8 -8
  43. package/dist/channels/slack/scaffold/slack-threads.ts +4 -4
  44. package/dist/channels/slack/setup.d.ts +7 -0
  45. package/dist/channels/slack/setup.js +119 -0
  46. package/dist/channels/slack/slack-api.d.ts +2 -0
  47. package/dist/channels/slack/slack-api.js +4 -0
  48. package/dist/channels/slack/slack.d.ts +21 -2
  49. package/dist/channels/slack/slack.js +398 -338
  50. package/dist/channels/slack/socket.d.ts +57 -0
  51. package/dist/channels/slack/socket.js +305 -0
  52. package/dist/channels/telegram/polling.d.ts +27 -0
  53. package/dist/channels/telegram/polling.js +105 -0
  54. package/dist/channels/telegram/scaffold/channel.ts +52 -32
  55. package/dist/channels/telegram/scaffold/telegram-send.ts +9 -9
  56. package/dist/channels/telegram/setup.d.ts +2 -0
  57. package/dist/channels/telegram/setup.js +42 -0
  58. package/dist/channels/telegram/telegram-api.d.ts +27 -1
  59. package/dist/channels/telegram/telegram-api.js +7 -6
  60. package/dist/channels/telegram/telegram.d.ts +11 -3
  61. package/dist/channels/telegram/telegram.js +184 -157
  62. package/dist/channels/wait-health.js +1 -1
  63. package/dist/channels/webhook-evidence.d.ts +11 -0
  64. package/dist/channels/webhook-evidence.js +71 -0
  65. package/dist/channels/webhooks.d.ts +37 -0
  66. package/dist/channels/webhooks.js +70 -0
  67. package/dist/child-env.d.ts +16 -0
  68. package/dist/child-env.js +30 -0
  69. package/dist/cli/box-login.js +4 -1
  70. package/dist/cli/channel-app.d.ts +3 -0
  71. package/dist/cli/channel-app.js +35 -0
  72. package/dist/cli/channel-setup.d.ts +38 -0
  73. package/dist/cli/channel-setup.js +195 -0
  74. package/dist/cli/commands/add.d.ts +6 -14
  75. package/dist/cli/commands/add.js +45 -310
  76. package/dist/cli/commands/auth.js +8 -2
  77. package/dist/cli/commands/build.d.ts +5 -0
  78. package/dist/cli/commands/build.js +16 -0
  79. package/dist/cli/commands/channels.d.ts +19 -0
  80. package/dist/cli/commands/channels.js +230 -0
  81. package/dist/cli/commands/content.d.ts +7 -3
  82. package/dist/cli/commands/content.js +16 -10
  83. package/dist/cli/commands/deploy/agentcore.js +55 -23
  84. package/dist/cli/commands/deploy/channel-apps.d.ts +17 -0
  85. package/dist/cli/commands/deploy/channel-apps.js +30 -0
  86. package/dist/cli/commands/deploy/docker.js +32 -65
  87. package/dist/cli/commands/deploy/fly.js +28 -12
  88. package/dist/cli/commands/deploy/railway.js +38 -13
  89. package/dist/cli/commands/deploy/secrets-step.d.ts +19 -0
  90. package/dist/cli/commands/deploy/secrets-step.js +66 -0
  91. package/dist/cli/commands/deploy/shared.d.ts +16 -5
  92. package/dist/cli/commands/deploy/shared.js +25 -8
  93. package/dist/cli/commands/deploy.js +53 -67
  94. package/dist/cli/commands/dev.d.ts +2 -0
  95. package/dist/cli/commands/dev.js +15 -3
  96. package/dist/cli/commands/env.js +1 -1
  97. package/dist/cli/commands/info.d.ts +2 -0
  98. package/dist/cli/commands/info.js +63 -24
  99. package/dist/cli/commands/init.js +3 -1
  100. package/dist/cli/commands/invoke.d.ts +2 -0
  101. package/dist/cli/commands/invoke.js +20 -7
  102. package/dist/cli/commands/login.d.ts +0 -7
  103. package/dist/cli/commands/login.js +22 -10
  104. package/dist/cli/commands/logout.js +1 -1
  105. package/dist/cli/commands/mcp.d.ts +12 -0
  106. package/dist/cli/commands/mcp.js +164 -0
  107. package/dist/cli/commands/models.js +3 -2
  108. package/dist/cli/commands/schedules.d.ts +4 -2
  109. package/dist/cli/commands/schedules.js +14 -10
  110. package/dist/cli/commands/secrets.d.ts +14 -0
  111. package/dist/cli/commands/secrets.js +213 -0
  112. package/dist/cli/commands/start.d.ts +14 -14
  113. package/dist/cli/commands/start.js +46 -60
  114. package/dist/cli/content-view.d.ts +4 -6
  115. package/dist/cli/content-view.js +5 -11
  116. package/dist/cli/first-run-channels.d.ts +3 -0
  117. package/dist/cli/first-run-channels.js +63 -0
  118. package/dist/cli/instance.d.ts +41 -0
  119. package/dist/cli/instance.js +155 -0
  120. package/dist/cli/invoke-stream.d.ts +5 -2
  121. package/dist/cli/invoke-stream.js +7 -2
  122. package/dist/cli/program.js +339 -108
  123. package/dist/cli/serve.d.ts +4 -5
  124. package/dist/cli/serve.js +4 -7
  125. package/dist/cli/shared.d.ts +11 -2
  126. package/dist/cli/shared.js +32 -19
  127. package/dist/cli/ticket.js +50 -6
  128. package/dist/content/declare.d.ts +31 -37
  129. package/dist/content/declare.js +59 -60
  130. package/dist/content/file.d.ts +23 -12
  131. package/dist/content/file.js +48 -35
  132. package/dist/content/git.d.ts +0 -10
  133. package/dist/content/git.js +5 -39
  134. package/dist/content/resolve.d.ts +18 -22
  135. package/dist/content/resolve.js +32 -66
  136. package/dist/content/source.d.ts +7 -8
  137. package/dist/content/source.js +10 -28
  138. package/dist/credentials-page.d.ts +26 -0
  139. package/dist/credentials-page.js +185 -0
  140. package/dist/declared-secrets.d.ts +10 -3
  141. package/dist/declared-secrets.js +8 -4
  142. package/dist/deploy/agentcore/destroy.d.ts +4 -1
  143. package/dist/deploy/agentcore/destroy.js +26 -2
  144. package/dist/deploy/agentcore/forwarder.js +1 -1
  145. package/dist/deploy/agentcore/plan.d.ts +7 -21
  146. package/dist/deploy/agentcore/plan.js +47 -49
  147. package/dist/deploy/agentcore/run.d.ts +18 -10
  148. package/dist/deploy/agentcore/run.js +93 -70
  149. package/dist/deploy/agentcore/runtime-secrets.d.ts +7 -0
  150. package/dist/deploy/agentcore/runtime-secrets.js +108 -0
  151. package/dist/deploy/agentcore/shell.js +12 -19
  152. package/dist/deploy/agentcore/sigv4.d.ts +7 -0
  153. package/dist/deploy/agentcore/sigv4.js +15 -0
  154. package/dist/deploy/agentcore/slr.d.ts +18 -0
  155. package/dist/deploy/agentcore/slr.js +27 -0
  156. package/dist/deploy/agentcore/store.d.ts +7 -0
  157. package/dist/deploy/agentcore/store.js +115 -0
  158. package/dist/deploy/api.d.ts +14 -14
  159. package/dist/deploy/api.js +26 -24
  160. package/dist/deploy/box-shell.d.ts +3 -1
  161. package/dist/deploy/build-context.d.ts +4 -4
  162. package/dist/deploy/build-context.js +11 -11
  163. package/dist/deploy/channel-ingress.d.ts +35 -9
  164. package/dist/deploy/channel-ingress.js +119 -22
  165. package/dist/deploy/container.d.ts +35 -10
  166. package/dist/deploy/container.js +80 -39
  167. package/dist/deploy/delivered-text.d.ts +13 -0
  168. package/dist/deploy/delivered-text.js +86 -0
  169. package/dist/deploy/docker/plan.d.ts +7 -8
  170. package/dist/deploy/docker/plan.js +31 -52
  171. package/dist/deploy/docker/run.d.ts +18 -43
  172. package/dist/deploy/docker/run.js +57 -79
  173. package/dist/deploy/file-store.d.ts +2 -0
  174. package/dist/deploy/file-store.js +17 -0
  175. package/dist/deploy/fly/plan.d.ts +3 -2
  176. package/dist/deploy/fly/plan.js +20 -9
  177. package/dist/deploy/fly/run.d.ts +18 -22
  178. package/dist/deploy/fly/run.js +25 -44
  179. package/dist/deploy/fly/store.d.ts +12 -0
  180. package/dist/deploy/fly/store.js +83 -0
  181. package/dist/deploy/hosts.d.ts +1 -2
  182. package/dist/deploy/hosts.js +1 -2
  183. package/dist/deploy/preflight.d.ts +18 -5
  184. package/dist/deploy/preflight.js +135 -63
  185. package/dist/deploy/railway/ignore.d.ts +8 -0
  186. package/dist/deploy/railway/ignore.js +57 -0
  187. package/dist/deploy/railway/plan.d.ts +1 -2
  188. package/dist/deploy/railway/plan.js +8 -6
  189. package/dist/deploy/railway/run.d.ts +21 -15
  190. package/dist/deploy/railway/run.js +32 -78
  191. package/dist/deploy/railway/store.d.ts +29 -0
  192. package/dist/deploy/railway/store.js +143 -0
  193. package/dist/deploy/runner.d.ts +5 -2
  194. package/dist/deploy/runner.js +4 -3
  195. package/dist/deploy/secret-store.d.ts +63 -0
  196. package/dist/deploy/secret-store.js +98 -0
  197. package/dist/deploy/secrets.d.ts +29 -46
  198. package/dist/deploy/secrets.js +49 -122
  199. package/dist/deploy/workspace.d.ts +6 -12
  200. package/dist/deploy/workspace.js +18 -94
  201. package/dist/dev-supervisor.js +6 -7
  202. package/dist/environment/declare.js +3 -2
  203. package/dist/environment/mise.d.ts +3 -1
  204. package/dist/environment/mise.js +4 -2
  205. package/dist/feishu.d.ts +1 -1
  206. package/dist/feishu.js +1 -1
  207. package/dist/harnesses/models/catalog.d.ts +1 -0
  208. package/dist/harnesses/models/catalog.js +1 -2
  209. package/dist/harnesses/models/environment.d.ts +7 -2
  210. package/dist/harnesses/models/environment.js +9 -5
  211. package/dist/harnesses/models/host.d.ts +0 -7
  212. package/dist/harnesses/models/host.js +10 -1
  213. package/dist/harnesses/models/login.d.ts +0 -8
  214. package/dist/harnesses/models/login.js +5 -4
  215. package/dist/harnesses/models/models.d.ts +7 -2
  216. package/dist/harnesses/models/models.js +28 -4
  217. package/dist/harnesses/pi-durable/agent.js +59 -38
  218. package/dist/harnesses/pi-durable/build.d.ts +12 -0
  219. package/dist/harnesses/pi-durable/build.js +270 -0
  220. package/dist/harnesses/pi-durable/built.d.ts +24 -0
  221. package/dist/harnesses/pi-durable/built.js +136 -0
  222. package/dist/harnesses/pi-durable/config.d.ts +81 -0
  223. package/dist/harnesses/pi-durable/config.js +266 -0
  224. package/dist/harnesses/pi-durable/define.d.ts +63 -13
  225. package/dist/harnesses/pi-durable/define.js +23 -2
  226. package/dist/harnesses/pi-durable/definition.d.ts +72 -0
  227. package/dist/harnesses/pi-durable/definition.js +143 -0
  228. package/dist/harnesses/pi-durable/events.d.ts +4 -3
  229. package/dist/harnesses/pi-durable/events.js +179 -136
  230. package/dist/harnesses/pi-durable/execution-env.d.ts +17 -0
  231. package/dist/harnesses/pi-durable/execution-env.js +11 -0
  232. package/dist/harnesses/pi-durable/mcp.d.ts +3 -0
  233. package/dist/harnesses/pi-durable/mcp.js +32 -0
  234. package/dist/harnesses/pi-durable/open.d.ts +36 -38
  235. package/dist/harnesses/pi-durable/open.js +97 -150
  236. package/dist/harnesses/pi-durable/reads.d.ts +0 -1
  237. package/dist/harnesses/pi-durable/reads.js +1 -3
  238. package/dist/harnesses/pi-durable/registry.d.ts +4 -6
  239. package/dist/harnesses/pi-durable/registry.js +29 -26
  240. package/dist/harnesses/pi-durable/release.d.ts +2 -2
  241. package/dist/harnesses/pi-durable/release.js +10 -7
  242. package/dist/harnesses/pi-durable/serve.d.ts +95 -15
  243. package/dist/harnesses/pi-durable/serve.js +151 -59
  244. package/dist/harnesses/pi-durable/sessions.js +15 -12
  245. package/dist/harnesses/pi-durable/text-sources.d.ts +30 -0
  246. package/dist/harnesses/pi-durable/text-sources.js +55 -0
  247. package/dist/harnesses/pi-durable/text.d.ts +16 -4
  248. package/dist/harnesses/pi-durable/text.js +115 -59
  249. package/dist/harnesses/pi-durable/wake.js +8 -8
  250. package/dist/http/client.js +9 -3
  251. package/dist/http/server.d.ts +1 -1
  252. package/dist/http/server.js +1 -1
  253. package/dist/index.d.ts +11 -4
  254. package/dist/index.js +8 -1
  255. package/dist/instance-name.d.ts +7 -0
  256. package/dist/instance-name.js +26 -0
  257. package/dist/lark.d.ts +1 -1
  258. package/dist/lark.js +1 -1
  259. package/dist/locked-file.d.ts +2 -2
  260. package/dist/locked-file.js +8 -3
  261. package/dist/mcp/auth.d.ts +22 -0
  262. package/dist/mcp/auth.js +180 -0
  263. package/dist/mcp/client.d.ts +15 -0
  264. package/dist/mcp/client.js +179 -0
  265. package/dist/mcp/config.d.ts +86 -0
  266. package/dist/mcp/config.js +225 -0
  267. package/dist/mcp/grants.d.ts +4 -0
  268. package/dist/mcp/grants.js +132 -0
  269. package/dist/mcp/host.d.ts +19 -0
  270. package/dist/mcp/host.js +128 -0
  271. package/dist/mcp/network.d.ts +12 -0
  272. package/dist/mcp/network.js +150 -0
  273. package/dist/mcp/sign-in.d.ts +2 -0
  274. package/dist/mcp/sign-in.js +204 -0
  275. package/dist/mcp-api.d.ts +107 -0
  276. package/dist/mcp-api.js +10 -0
  277. package/dist/paths.d.ts +22 -8
  278. package/dist/paths.js +44 -11
  279. package/dist/runtime-websocket.d.ts +14 -0
  280. package/dist/runtime-websocket.js +39 -0
  281. package/dist/scaffold/add-channel.d.ts +9 -16
  282. package/dist/scaffold/add-channel.js +13 -83
  283. package/dist/scaffold/templates/env.example +4 -5
  284. package/dist/scaffold/templates/fastagent.config.ts +20 -14
  285. package/dist/scaffold/templates/gitignore +9 -6
  286. package/dist/scaffold/templates.d.ts +6 -2
  287. package/dist/scaffold/templates.js +7 -2
  288. package/dist/scaffold/vendor-skill.js +4 -2
  289. package/dist/service.d.ts +44 -6
  290. package/dist/service.js +110 -10
  291. package/dist/serving-command.d.ts +10 -0
  292. package/dist/serving-command.js +1 -0
  293. package/dist/setup-page.d.ts +51 -0
  294. package/dist/setup-page.js +149 -0
  295. package/dist/slack.d.ts +1 -1
  296. package/dist/slack.js +1 -1
  297. package/dist/strip-edges.d.ts +10 -0
  298. package/dist/strip-edges.js +22 -0
  299. package/dist/telegram.d.ts +1 -1
  300. package/dist/telegram.js +1 -1
  301. package/dist/tunnel.d.ts +0 -1
  302. package/dist/tunnel.js +2 -8
  303. package/dist/value-file.d.ts +12 -6
  304. package/dist/value-file.js +36 -25
  305. package/dist/version.d.ts +7 -1
  306. package/dist/version.js +9 -3
  307. package/package.json +8 -6
  308. package/dist/channels/lark/onboard.d.ts +0 -36
  309. package/dist/channels/lark/onboard.js +0 -56
  310. package/dist/channels/slack/config-api.d.ts +0 -69
  311. package/dist/channels/slack/config-api.js +0 -162
  312. package/dist/channels/slack/onboard.d.ts +0 -40
  313. package/dist/channels/slack/onboard.js +0 -136
  314. package/dist/channels/slack/onboarding-state.d.ts +0 -31
  315. package/dist/channels/slack/onboarding-state.js +0 -64
  316. package/dist/channels/slack/register-webhook.d.ts +0 -12
  317. package/dist/channels/slack/register-webhook.js +0 -67
  318. package/dist/channels/slack/setup-server.d.ts +0 -32
  319. package/dist/channels/slack/setup-server.js +0 -201
  320. package/dist/cli/add-feishu.d.ts +0 -50
  321. package/dist/cli/add-feishu.js +0 -367
  322. package/dist/cli/add-slack.d.ts +0 -18
  323. package/dist/cli/add-slack.js +0 -216
  324. package/dist/harnesses/pi-durable/parked.d.ts +0 -18
  325. package/dist/harnesses/pi-durable/parked.js +0 -27
@@ -2,21 +2,21 @@
2
2
  * `fastagent deploy <host> [agent]`: generate host artifacts from the resolved definition and print an ordered deploy
3
3
  * runbook.
4
4
  */
5
- import { relative } from "node:path";
6
- import { cloudFor } from "../../channels/feishu/cloud.js";
7
- import { FEISHU_INGRESS_SETTINGS } from "../../channels/feishu/setup-mode.js";
8
- import { assembleSecrets, divergentSettingsGate, missingValuesGate } from "../../deploy/secrets.js";
5
+ import { resolve } from "node:path";
9
6
  import { dotEnvPath, loadEnvValues } from "../../value-file.js";
10
7
  import { unmarkDevServe } from "../../serving-command.js";
11
- import { assertIndependentFeishuApps, feishuAppSecretNames, larkCredentialsNeeded, prepareFeishuApps, unpreparedFeishuApps, } from "../add-feishu.js";
8
+ import { channelAppNames, prepareChannelApps, unpreparedChannelApps } from "./deploy/channel-apps.js";
12
9
  import { displayPath, selectAgentEnvironment } from "../../paths.js";
10
+ import { buildProject } from "../../harnesses/pi-durable/build.js";
11
+ import { DELIVERED_CONFIG_DIR } from "../../deploy/container.js";
12
+ import { deliverText } from "../../deploy/delivered-text.js";
13
13
  import { preflightDeploy } from "../../deploy/preflight.js";
14
- import { apiReport, deploymentApi, writeApiKey } from "../../deploy/api.js";
15
- import { unprotectedSecretsNote } from "../../value-file.js";
16
- import { loadConfig } from "../../harnesses/pi-durable/open.js";
14
+ import { apiReport, deploymentApi } from "../../deploy/api.js";
15
+ import { API_KEY_VARIABLE, apiKeysFrom } from "../../http/server.js";
16
+ import { readInstanceConfigFile, resolveConfig } from "../../harnesses/pi-durable/config.js";
17
+ import { loadSourceDefinition } from "../../harnesses/pi-durable/open.js";
17
18
  import { failStartup, failUsage } from "../fail.js";
18
19
  import { enterAgentDirectory, isInteractive, shellArg } from "../shared.js";
19
- import { terminalPerson } from "../person.js";
20
20
  import { checkTicket, printReport, refusedCheck, runTicket, startTicket, stopTicket, ticketExitCode, ticketReport, } from "../ticket.js";
21
21
  import { agentcoreHost } from "./deploy/agentcore.js";
22
22
  import { dockerHost } from "./deploy/docker.js";
@@ -87,39 +87,33 @@ export async function runDeploy(host, dirArg, opts) {
87
87
  async function deploy(host, dirArg, opts, step) {
88
88
  if (opts.code !== undefined)
89
89
  failUsage("--code hands a code to a deploy's ticket: pass --check <ticket> too");
90
- if (opts.tunnel && host !== "docker") {
91
- // A flag/host combination the parser cannot see (host is an argument) — usage class, exit 2.
92
- failUsage(`deploy stopped: --tunnel is supported only by the local Docker target`);
93
- }
94
90
  if (opts.json && !opts.run)
95
91
  failUsage("--json reports a deploy --run drives: pass --run too");
96
92
  selectAgentEnvironment("production");
97
- const { agentDir } = await enterAgentDirectory(dirArg, opts);
98
- const config = await loadConfig(agentDir).catch(failStartup);
99
- // Never `dev`, whatever spawned this; and what decides a channel's shape is read here as the box will read it.
100
- unmarkDevServe();
101
- const shaped = divergentSettingsGate(FEISHU_INGRESS_SETTINGS, process.env, loadEnvValues(dotEnvPath(agentDir)), relative(agentDir, dotEnvPath(agentDir)));
102
- if (shaped)
103
- failStartup(new Error(`deploy stopped: ${shaped}`));
104
- const independentApps = (values) => {
105
- try {
106
- assertIndependentFeishuApps(agentDir, ["feishu", "lark"], values);
107
- }
108
- catch (error) {
109
- failStartup(error);
110
- }
111
- };
112
- independentApps(loadEnvValues(dotEnvPath(agentDir)));
113
- const api = deploymentApi({
114
- config,
115
- hostServes: host !== "agentcore",
116
- values: loadEnvValues(dotEnvPath(agentDir)),
93
+ // The configuration the box runs on: the program's defaults, with the instance configuration `--config` delivers,
94
+ // and never the project's own `fastagent.json`, the development instance's.
95
+ const delivered = opts.config === undefined
96
+ ? undefined
97
+ : await readInstanceConfigFile(resolve(opts.config))
98
+ .then((source) => ({ source, text: `${JSON.stringify(source.values, null, 2)}\n` }))
99
+ .catch(failStartup);
100
+ const deployed = async (dir) => ({
101
+ definition: await loadSourceDefinition(dir),
102
+ ...(delivered === undefined ? {} : { instance: delivered.source }),
117
103
  });
104
+ const { agentDir } = await enterAgentDirectory(dirArg, opts, undefined, deployed).catch(failStartup);
105
+ const config = await deployed(agentDir)
106
+ .then(({ definition }) => resolveConfig(definition, delivered?.source))
107
+ .catch(failStartup);
108
+ // Never `dev`, whatever spawned this: a channel's way of receiving is chosen as the box will choose it.
109
+ unmarkDevServe();
110
+ const api = deploymentApi({ config, hostServes: host !== "agentcore" });
118
111
  const target = HOSTS[host];
119
112
  const preflight = async () => {
120
113
  const facts = await preflightDeploy({
121
114
  agentDir,
122
115
  config,
116
+ ...(delivered === undefined ? {} : { delivered }),
123
117
  run: !!opts.run,
124
118
  force: !!opts.force,
125
119
  noResidentProcess: host === "agentcore", // alarms and webhooks wake the container there — no machine to keep up
@@ -135,17 +129,10 @@ async function deploy(host, dirArg, opts, step) {
135
129
  let pre = await preflight();
136
130
  const agentArg = displayPath(process.cwd(), agentDir);
137
131
  const rerun = `fastagent deploy ${host}${agentArg === undefined ? "" : ` ${shellArg(agentArg)}`}` +
138
- `${opts.tunnel ? " --tunnel" : ""}${opts.intoLinked ? " --into-linked" : ""} --run`;
139
- const unprepared = opts.run ? unpreparedFeishuApps(pre.channels, pre.values) : [];
132
+ `${opts.intoLinked ? " --into-linked" : ""} --run`;
133
+ const unprepared = opts.run ? unpreparedChannelApps(pre.channels, pre.values) : [];
140
134
  if (opts.json && step === undefined) {
141
135
  // A coding agent's deploy: what this machine can refuse is refused above, at once; the rest runs in a ticket.
142
- if (unprepared.some(({ kind }) => kind === "lark")) {
143
- const needed = await larkCredentialsNeeded(agentDir, rerun).catch(failStartup);
144
- if (needed !== undefined) {
145
- printReport(true, needed.json, needed.text);
146
- process.exit(0);
147
- }
148
- }
149
136
  const timeout = opts.timeout === undefined ? TICKET_TIMEOUT_SECONDS : Number(opts.timeout);
150
137
  if (!Number.isInteger(timeout) || timeout <= 0)
151
138
  failUsage("--timeout must be a whole number of seconds");
@@ -167,8 +154,8 @@ async function deploy(host, dirArg, opts, step) {
167
154
  "deploy",
168
155
  host,
169
156
  agentDir,
170
- ...(opts.tunnel ? ["--tunnel"] : []),
171
157
  ...(opts.force ? ["--force"] : []),
158
+ ...(delivered === undefined ? [] : ["--config", delivered.source.label]),
172
159
  ...(opts.intoLinked ? ["--into-linked"] : []),
173
160
  "--run",
174
161
  "--json",
@@ -182,50 +169,38 @@ async function deploy(host, dirArg, opts, step) {
182
169
  /** Everything past the pre-flight, its person's steps taken by `answerer`; the URL and what the API report says. */
183
170
  const deployWith = async (answerer) => {
184
171
  if (unprepared.length > 0) {
185
- const supplied = unprepared.flatMap(({ kind, ingress }) => feishuAppSecretNames(kind, ingress));
172
+ const supplied = unprepared.flatMap(channelAppNames);
186
173
  const kinds = unprepared.map(({ kind }) => kind);
187
174
  const { values } = pre;
188
175
  const absent = supplied.filter((name) => !values.get(name)?.trim());
189
- const { missingSecrets } = assembleSecrets({
190
- modelAuth: pre.modelAuth,
191
- declared: pre.declaredSecrets,
192
- values: pre.values,
193
- });
194
- const missing = missingValuesGate(missingSecrets.filter((name) => !supplied.includes(name)), pre.valueFile);
195
- if (missing)
196
- failStartup(new Error(`deploy stopped: ${missing}`));
197
- if (host === "docker" && !opts.tunnel && unprepared.some(({ ingress }) => ingress === "webhook")) {
176
+ if (host === "docker" && unprepared.some(({ way }) => way === "webhook")) {
198
177
  failStartup(new Error(`deploy stopped: ${kinds.join(", ")} receive by webhook here, and this Docker deployment has no ` +
199
- `public URL to point the app at — re-run with --tunnel (the app is prepared then), or set up your own ` +
200
- `ingress and supply ${absent.join(", ")} from a separate production app in ${pre.valueFile}, or ` +
201
- `set ${kinds.map((kind) => `${cloudFor(kind).envPrefix}_INGRESS=websocket`).join(", ")} there`));
178
+ `public URL to point the app at — set up your own ingress and supply ${absent.join(", ")} from a separate production app in ${pre.valueFile}, or ` +
179
+ `set ${kinds.map((kind) => `channels.${kind}.ingress`).join(", ")} to "connection" in the configuration ` +
180
+ `--config delivers`));
202
181
  }
203
182
  if (answerer.kind === "nobody") {
204
183
  failUsage(`deploy stopped: ${pre.valueFile} has no ${absent.join(", ")}: setting up the separate production app ` +
205
184
  `needs a person — run this deploy in a terminal; from a coding agent, run \`${rerun} --json\`, which ` +
206
185
  `hands their part to its user`);
207
186
  }
208
- const person = answerer.kind === "ticket" ? answerer.step.person : terminalPerson;
209
187
  const again = answerer.kind === "ticket" ? `${rerun} --json` : rerun;
210
- await prepareFeishuApps(agentDir, unprepared, again, person).catch(failStartup);
211
- pre = await preflight();
212
- }
213
- independentApps(pre.values);
214
- if (api.served && api.generatesKey) {
215
- // Past every refusal the pre-flight makes, and read again, so this deploy carries it.
216
- const unprotected = await writeApiKey(agentDir).catch(failStartup);
217
- if (unprotected !== undefined)
218
- console.error(`[fastagent] note: ${unprotectedSecretsNote(unprotected)}`);
188
+ await prepareChannelApps(agentDir, unprepared, again, answerer.kind === "ticket" ? answerer.step.person : undefined).catch(failStartup);
219
189
  pre = await preflight();
220
190
  }
221
191
  for (const m of pre.messages)
222
192
  console.error(`[fastagent] ${m.level}: ${m.text}`);
193
+ // The box starts the build, never the source: dist/ is made here, from what the image will hold.
194
+ const built = await buildProject(agentDir).catch(failStartup);
195
+ for (const warning of built.warnings)
196
+ console.error(`[fastagent] warn: ${warning}`);
223
197
  const { channels } = pre;
224
198
  warnHostOnlyFlags(host, opts);
225
199
  // What follows is the host's work: a deadline meant for the person must never cut it halfway. Its own steps for
226
200
  // the person (a host sign-in, the box's login) wait again, and are bounded again.
227
201
  if (answerer.kind === "ticket")
228
202
  answerer.step.person.carriesOn();
203
+ let textDelivered = false;
229
204
  const reachedAt = await target.deploy({
230
205
  opts,
231
206
  answerer,
@@ -233,6 +208,7 @@ async function deploy(host, dirArg, opts, step) {
233
208
  agentDir,
234
209
  config,
235
210
  pre,
211
+ api,
236
212
  channels,
237
213
  webhookChannels: channels.filter((channel) => channel.ingress === "webhook"),
238
214
  // The ownership predicate is bound HERE, from the same lookup that chose the host, so no host module can pass
@@ -249,10 +225,20 @@ async function deploy(host, dirArg, opts, step) {
249
225
  `would deploy from them. Re-run with --force to regenerate, or remove each file's generated-by ` +
250
226
  `marker to own it yourself.`));
251
227
  }
228
+ // Once, after the gate and before the first artifact lands: the directory is replaced whole, so a refused
229
+ // run must leave it as it was, and the build outputs the host writes next go into the fresh one.
230
+ if (!textDelivered) {
231
+ textDelivered = true;
232
+ const copied = await deliverText(agentDir, delivered?.source).catch(failStartup);
233
+ if (copied > 0) {
234
+ console.error(`[fastagent] delivered ${copied} path(s) of the instance's text into ${DELIVERED_CONFIG_DIR}/`);
235
+ }
236
+ }
252
237
  await applyArtifactPlan(plan);
253
238
  },
254
239
  });
255
- const notes = apiReport(api, { valueFile: pre.valueFile, agentArg }, reachedAt);
240
+ const keyInFile = apiKeysFrom(loadEnvValues(dotEnvPath(agentDir)).get(API_KEY_VARIABLE)).length > 0;
241
+ const notes = apiReport(api, { valueFile: pre.valueFile, agentArg, keyInFile, run: !!opts.run }, reachedAt);
256
242
  for (const line of notes)
257
243
  console.error(`[fastagent] ${line}`);
258
244
  return { ...(reachedAt === undefined ? {} : { url: reachedAt }), notes };
@@ -1,3 +1,4 @@
1
+ import { type InstanceFlags } from "../instance.ts";
1
2
  export interface DevOptions {
2
3
  port?: string;
3
4
  bind?: string;
@@ -7,5 +8,6 @@ export interface DevOptions {
7
8
  tunnel?: boolean;
8
9
  /** false ⇔ `--no-input`. */
9
10
  input?: boolean;
11
+ instance?: InstanceFlags;
10
12
  }
11
13
  export declare function runDev(dirArg: string, opts: DevOptions): Promise<void>;
@@ -6,10 +6,13 @@ import { listenForRestart, runDevSupervisor } from "../../dev-supervisor.js";
6
6
  import { setLogLevel } from "../../log.js";
7
7
  import { markDevServe } from "../../serving-command.js";
8
8
  import { selectAgentEnvironment } from "../../paths.js";
9
+ import { loadDevProject } from "../../harnesses/pi-durable/open.js";
9
10
  import { openServing } from "../../harnesses/pi-durable/serve.js";
10
11
  import { DEFAULT_HTTP_PORT, serveAgent } from "../../service.js";
11
12
  import { failStartup } from "../fail.js";
12
13
  import { assertTunnelBindable, cliMountOptions, serveService } from "../serve.js";
14
+ import { setUpMissingChannels } from "../first-run-channels.js";
15
+ import { instanceInputs } from "../instance.js";
13
16
  import { enterAgentCommand, parseBind, parsePort, reportAssembly } from "../shared.js";
14
17
  /**
15
18
  * `dev` binds loopback unless `--bind` says otherwise: a dev serve carries the agent's full tool authority and lands on
@@ -26,6 +29,9 @@ export async function runDev(dirArg, opts) {
26
29
  process.on("message", listenForRestart(() => process.kill(process.pid, "SIGTERM")));
27
30
  // The model is picked ONCE, in the parent process (a TTY; watch and --no-watch both).
28
31
  const { agentDir, modelSpec } = await enterAgentCommand(dirArg, { ...opts, input: isWorker ? false : opts.input });
32
+ // Once, in the parent: the worker inherits what it writes, and a restart does not ask again.
33
+ if (!isWorker)
34
+ await setUpMissingChannels(agentDir, opts);
29
35
  if (isWorker || opts.watch === false) {
30
36
  await serveOnce(agentDir, modelSpec, opts);
31
37
  return;
@@ -41,9 +47,15 @@ async function serveOnce(agentDir, modelSpec, opts) {
41
47
  const host = parseBind(opts.bind) ?? DEV_BIND;
42
48
  const tunnel = opts.tunnel ?? false;
43
49
  assertTunnelBindable(host, tunnel);
44
- const opened = await openServing(agentDir, { model: modelSpec }).catch(failStartup);
50
+ const opened = await loadDevProject(agentDir)
51
+ .then(({ definition, instance }) => openServing(definition, {
52
+ model: modelSpec,
53
+ ...instanceInputs(agentDir),
54
+ ...(instance === undefined ? {} : { config: instance }),
55
+ }))
56
+ .catch(failStartup);
45
57
  await reportAssembly(opened, modelSpec);
46
- const { config } = opened.definition;
58
+ const { config } = opened;
47
59
  const service = await serveAgent(opened, cliMountOptions(config)).catch(failStartup);
48
- serveService(service, { port: portFlag ?? config.http?.port ?? DEFAULT_HTTP_PORT, host }, { tunnel, agentDir, stateRoot: opened.stateRoot, config });
60
+ serveService(service, { port: portFlag ?? config.http?.port ?? DEFAULT_HTTP_PORT, host }, { tunnel, agentDir, config });
49
61
  }
@@ -100,7 +100,7 @@ export async function runEnvList(opts = {}) {
100
100
  const missing = missingAptPackages(declared.aptPackages);
101
101
  if (missing && missing.length > 0)
102
102
  log.warn(`[fastagent] ${aptAdvice(missing)}`);
103
- const unset = missingSecrets(variables).map(({ name }) => name);
103
+ const unset = missingSecrets(variables, process.env).map(({ name }) => name);
104
104
  if (unset.length > 0) {
105
105
  log.warn(`[fastagent] no value here for ${unset.join(", ")}: dev and start refuse to run until they have one — set ` +
106
106
  `them in ${dotEnvPath(agentDir)}`);
@@ -1,5 +1,7 @@
1
+ import { type InstanceFlags } from "../instance.ts";
1
2
  export interface InfoOptions {
2
3
  json?: boolean;
3
4
  model?: string;
5
+ instance?: InstanceFlags;
4
6
  }
5
7
  export declare function runInfo(dirArg: string, opts: InfoOptions): Promise<void>;
@@ -1,48 +1,67 @@
1
1
  /** `fastagent info [agent] [--json]`: print what the directory builds into, WITHOUT opening or serving the agent. */
2
+ import { definedValues } from "../../declared-secrets.js";
2
3
  import { resolve } from "node:path";
3
4
  import { channelIngress } from "../../channels/discover.js";
4
5
  import { loadContent, loadEnvironment } from "../../content/file.js";
5
- import { contentAbsentHere, resolveContent } from "../../content/resolve.js";
6
+ import { resolveContent } from "../../content/resolve.js";
6
7
  import { describeSecrets, missingSecrets } from "../../declared-secrets.js";
7
- import { enterAgentEnv } from "../../env.js";
8
+ import { enteredInstanceName, instanceName } from "../../instance-name.js";
9
+ import { enterInstance } from "../instance.js";
8
10
  import { modelEnvironment } from "../../harnesses/models/environment.js";
9
- import { buildDefinition, modelSpecFor, prepareDefinition, secretsManifest } from "../../harnesses/pi-durable/open.js";
10
- import { readCommands } from "../../harnesses/pi-durable/text.js";
11
- import { resolveStateRoot } from "../../paths.js";
11
+ import { configValue, resolveConfig } from "../../harnesses/pi-durable/config.js";
12
+ import { definitionSecrets } from "../../harnesses/pi-durable/definition.js";
13
+ import { buildDefinition, loadDevProject, modelSpecFor } from "../../harnesses/pi-durable/open.js";
14
+ import { openText, readCommands } from "../../harnesses/pi-durable/text.js";
15
+ import { inspectMcp } from "../../mcp/host.js";
16
+ import { resolveStateRoot, resolveWorkdir } from "../../paths.js";
12
17
  import { nextRun } from "../../schedule/cron.js";
13
18
  import { contentLines } from "../content-view.js";
14
19
  import { agentDirOrExit, failStartup } from "../fail.js";
15
20
  export async function runInfo(dirArg, opts) {
16
21
  const agentDir = agentDirOrExit(resolve(dirArg));
17
- enterAgentEnv(agentDir); // code may read its secrets, and fetch, when it is built
18
- const definition = await prepareDefinition(agentDir).catch(failStartup);
19
- const { config } = definition;
22
+ enterInstance(agentDir, opts.instance); // code may read its secrets, and fetch, when it is built
23
+ const workdir = resolveWorkdir(agentDir);
24
+ const { definition, instance } = await loadDevProject(agentDir).catch(failStartup);
25
+ const config = (() => {
26
+ try {
27
+ return resolveConfig(definition, instance);
28
+ }
29
+ catch (error) {
30
+ return failStartup(error);
31
+ }
32
+ })();
33
+ const name = instanceName(definition.name, enteredInstanceName() ?? config.name);
20
34
  const modelSpec = modelSpecFor(config, opts.model);
21
- const manifest = secretsManifest(definition);
22
- const declared = manifest.flatMap(({ name, optional, declaredIn }) => declaredIn.map((source) => ({ name, optional, source })));
23
- const unsetSecrets = missingSecrets(declared);
35
+ const declared = (() => {
36
+ try {
37
+ return definitionSecrets(definition, config, instance);
38
+ }
39
+ catch (error) {
40
+ return failStartup(error);
41
+ }
42
+ })();
43
+ const unsetSecrets = missingSecrets(declared, process.env);
24
44
  // Built only when every required secret has a value: building is what reads them. A module that is not what its
25
45
  // directory holds is said as the refusal `dev` and `start` would stop on.
26
46
  const code = unsetSecrets.length === 0
27
47
  ? await Promise.resolve()
28
- .then(() => buildDefinition(definition))
48
+ .then(() => buildDefinition(definition, process.env, config))
29
49
  .catch(failStartup)
30
50
  : undefined;
31
51
  // Reported, not fatal: `info` is how an author finds out a content entry is missing, or the environment refused.
32
52
  let content = [];
33
- let contentAbsent = [];
34
53
  let contentError;
35
54
  try {
36
- const declaration = loadContent(agentDir);
37
- content = resolveContent(agentDir, declaration);
38
- contentAbsent = contentAbsentHere(declaration);
55
+ content = resolveContent(loadContent(agentDir, instance), { dir: agentDir, workdir });
39
56
  }
40
57
  catch (error) {
41
58
  contentError = error.message;
42
59
  }
43
60
  // Read already, by the definition's secrets: a refused environment is a definition that does not open.
44
61
  const environment = loadEnvironment(agentDir);
45
- const models = await modelEnvironment(agentDir, {}, code?.providers ?? []);
62
+ const models = await modelEnvironment(agentDir, {}, code?.providers ?? [], {
63
+ environment: definedValues(process.env),
64
+ });
46
65
  const slash = modelSpec?.indexOf("/") ?? -1;
47
66
  const modelError = modelSpec === undefined || models.models.getModel(modelSpec.slice(0, slash), modelSpec.slice(slash + 1))
48
67
  ? undefined
@@ -57,27 +76,33 @@ export async function runInfo(dirArg, opts) {
57
76
  tz: s.tz ?? null,
58
77
  next: nextRun(s.cron, s.tz, new Date())?.toISOString() ?? null,
59
78
  }));
60
- const commands = readCommands(agentDir).map(({ name, description }) => ({ name, description: description ?? null }));
79
+ const text = await Promise.resolve()
80
+ .then(() => openText(agentDir, instance))
81
+ .catch(failStartup);
82
+ const commands = readCommands(text).map(({ name, description }) => ({ name, description: description ?? null }));
61
83
  const stateRoot = resolveStateRoot(agentDir);
62
84
  const auth = models.auth;
85
+ const mcp = await inspectMcp(agentDir, instance === undefined ? {} : { config: instance.label }).catch(failStartup);
63
86
  if (opts.json) {
64
87
  console.log(JSON.stringify({
65
88
  agentDir,
66
- name: definition.name,
89
+ name: name,
67
90
  content,
68
- contentAbsent,
69
91
  contentError: contentError ?? null,
70
92
  environment: environment ?? null,
71
93
  model: modelSpec ?? null,
72
94
  modelError: modelError ?? null,
73
95
  thinkingLevel: config.thinkingLevel ?? null,
96
+ config: configReport(config, opts.model),
74
97
  tools: code?.tools.map(({ name }) => name) ?? null,
98
+ mcp,
75
99
  extensions: code?.extensions.map(({ name }) => name) ?? null,
76
100
  channels: code === undefined ? null : channels,
77
101
  schedules: code === undefined ? null : schedules,
78
102
  commands,
79
- secrets: manifest,
103
+ secrets: definition.inputs.secrets,
80
104
  unsetSecrets: unsetSecrets.map(({ name }) => name),
105
+ workdir,
81
106
  stateRoot,
82
107
  authPath: auth?.path ?? null,
83
108
  fallbackAuthPath: auth?.fallback ?? null,
@@ -87,13 +112,13 @@ export async function runInfo(dirArg, opts) {
87
112
  const line = (label, value) => console.log(`${`${label}:`.padEnd(13)} ${value}`);
88
113
  /** A continuation under the previous line, aligned to the same column (no label, so no bare colon). */
89
114
  const cont = (value) => console.log(`${"".padEnd(13)} ${value}`);
90
- line("agent", `${definition.name} — ${agentDir}`);
91
- for (const [label, value] of contentLines(content, contentAbsent))
115
+ line("agent", `${name} — ${agentDir}`);
116
+ for (const [label, value] of contentLines(content))
92
117
  line(label, value);
93
118
  if (contentError)
94
119
  cont(`⚠ ${contentError}`);
95
120
  line("environment", environment ? describeEnvironment(environment) : "(none: the machine's commands)");
96
- line("model", modelSpec ?? "(not set — pass --model, set FASTAGENT_MODEL, or model in fastagent.config.ts)");
121
+ line("model", modelSpec ?? "(not set — pass --model, set FASTAGENT_MODEL, or defaults.model in fastagent.config.ts)");
97
122
  if (modelError)
98
123
  cont(`⚠ does not resolve: ${modelError}`);
99
124
  if (config.thinkingLevel)
@@ -102,6 +127,7 @@ export async function runInfo(dirArg, opts) {
102
127
  line("tools", code ? code.tools.map(({ name }) => name).join(", ") || "(none)" : unbuilt);
103
128
  if (code && code.extensions.length > 0)
104
129
  line("extensions", code.extensions.map(({ name }) => name).join(", "));
130
+ line("mcp", mcp.map((server) => `${server.name} (${server.transport}, ${server.authorization})`).join(", ") || "(none)");
105
131
  line("channels", code ? channels.map((c) => `${c.name} (${c.ingress})`).join(", ") || "(none)" : unbuilt);
106
132
  line("schedules", code ? schedules.map((s) => `${s.name} (next ${s.next ?? "never"})`).join(", ") || "(none)" : unbuilt);
107
133
  line("commands", commands.map(({ name }) => `/${name}`).join(", ") || "(none)");
@@ -109,10 +135,23 @@ export async function runInfo(dirArg, opts) {
109
135
  if (unsetSecrets.length > 0) {
110
136
  cont(`⚠ dev, start and deployments refuse to boot until set: ${unsetSecrets.map((s) => s.name).join(", ")}`);
111
137
  }
138
+ line("workdir", workdir);
112
139
  line("state", stateRoot);
113
140
  if (auth)
114
141
  line("auth", auth.fallback === undefined ? auth.path : `${auth.path} (then ${auth.fallback})`);
115
142
  }
143
+ /**
144
+ * Each value the configuration resolved, by its path, with where it is from: the program's defaults, the instance's
145
+ * file, and for the model the flag or variable that overrides both.
146
+ */
147
+ function configReport(config, modelFlag) {
148
+ const report = Object.fromEntries(Object.entries(config.sources).map(([path, source]) => [path, { value: configValue(config, path), source }]));
149
+ const override = modelFlag !== undefined ? "--model" : process.env.FASTAGENT_MODEL ? "FASTAGENT_MODEL" : undefined;
150
+ const model = modelSpecFor(config, modelFlag);
151
+ if (override !== undefined && model !== undefined)
152
+ report.model = { value: model, source: override };
153
+ return report;
154
+ }
116
155
  /** The packages and apt packages an environment declares, as one line. */
117
156
  function describeEnvironment(environment) {
118
157
  const packages = Object.entries(environment.packages).map(([name, version]) => `${name} ${version}`);
@@ -15,7 +15,9 @@ export async function runInit(dirArg, opts) {
15
15
  .catch(failStartup);
16
16
  let installFailed = false;
17
17
  const { created, content, repository } = await initAgent(dir, {
18
- content: read.map((source) => source.addition),
18
+ // A new agent's repositories are its program's: every instance of it reaches them, and a deploy carries them. A
19
+ // directory is this machine's, and goes to the instance configuration.
20
+ content: read.map(({ addition }) => ("github" in addition.entry ? { ...addition, program: true } : addition)),
19
21
  ...(opts.install
20
22
  ? {
21
23
  install: async (agentDir) => {
@@ -1,3 +1,4 @@
1
+ import { type InstanceFlags } from "../instance.ts";
1
2
  export interface InvokeOptions {
2
3
  model?: string;
3
4
  /** false ⇔ `--no-input`. */
@@ -8,5 +9,6 @@ export interface InvokeOptions {
8
9
  url?: string;
9
10
  /** The key `url` is called with; else the first in the agent's production values. */
10
11
  key?: string;
12
+ instance?: InstanceFlags;
11
13
  }
12
14
  export declare function runInvoke(message: string, dirArg: string, opts: InvokeOptions): Promise<void>;
@@ -14,6 +14,7 @@ import { API_KEY_VARIABLE, apiKeysFrom } from "../../http/server.js";
14
14
  import { resolveStateRoot, selectAgentEnvironment } from "../../paths.js";
15
15
  import { runInvokeJson, runInvokeStream } from "../invoke-stream.js";
16
16
  import { failStartup, failUsage, optionalAgentDirOrExit } from "../fail.js";
17
+ import { enterEnvFile, instanceInputs } from "../instance.js";
17
18
  /**
18
19
  * `invoke`'s sessions, apart from the instance's storage, so `invoke` runs beside a serve of this directory, which holds
19
20
  * that one; one `invoke` at a time holds this one.
@@ -37,11 +38,15 @@ async function sessionFor(agent, sessionId, settings, file) {
37
38
  /** A refusal as the line an operator reads: its code, then what it says. */
38
39
  const refusal = (error) => error instanceof AgentError ? new Error(`${error.code}: ${error.message}`) : error;
39
40
  /** Print the message's answer as it is written, then exit with what it ended as: 0 only for `completed`. */
40
- async function answer(events, json) {
41
+ /** What to do about a model with no credential, where the answer came from. */
42
+ const LOCAL_AUTH_HINT = "sign in with `fastagent login`, or set the provider's API key in .secrets/.env";
43
+ const DEPLOYED_AUTH_HINT = "give the deployment a credential: `fastagent secrets set <host> <the provider's API key variable>` " +
44
+ "(`fastagent secrets list <host>` names it), or sign its box in with `fastagent login <provider> --deployment <host>`";
45
+ async function answer(events, json, authHint) {
41
46
  // Both renderers exit 1 unless the message completed: CI gates on it.
42
47
  const exitCode = json
43
48
  ? await runInvokeJson(events, (line) => process.stdout.write(`${line}\n`))
44
- : await runInvokeStream(events, (text) => process.stdout.write(text), (line) => console.error(line));
49
+ : await runInvokeStream(events, (text) => process.stdout.write(text), (line) => console.error(line), authHint);
45
50
  if (!json)
46
51
  process.stdout.write("\n");
47
52
  return exitCode;
@@ -79,6 +84,10 @@ function enterForUrl(dirArg, key, url) {
79
84
  /** `--url`: the message sent to a served agent over its Agent API, in a session there; nothing opens here. */
80
85
  async function runRemoteInvoke(message, dirArg, opts) {
81
86
  const { url } = opts;
87
+ const { envFile, ...placed } = opts.instance ?? {};
88
+ for (const flag of Object.keys(placed))
89
+ failUsage(`--${flag} places an instance this run opens, and --url opens none`);
90
+ enterEnvFile(envFile);
82
91
  let settings;
83
92
  try {
84
93
  settings = opts.model === undefined ? undefined : { model: modelRefOf(opts.model, "--model") };
@@ -90,7 +99,7 @@ async function runRemoteInvoke(message, dirArg, opts) {
90
99
  console.error(`[fastagent] invoke: ${url}`);
91
100
  const sessionId = await remoteSession(agent, opts.session, settings, url).catch((error) => failStartup(refusal(error)));
92
101
  console.error(`[fastagent] session ${sessionId} (continue it with --session ${sessionId})`);
93
- process.exit(await answer(agent.messages.stream({ sessionId, content: message }), opts.json));
102
+ process.exit(await answer(agent.messages.stream({ sessionId, content: message }), opts.json, DEPLOYED_AUTH_HINT));
94
103
  }
95
104
  /** The served agent's session to send into: a new one, or `sessionId`, set to `--model` when one is given. */
96
105
  async function remoteSession(agent, sessionId, settings, url) {
@@ -110,28 +119,32 @@ export async function runInvoke(message, dirArg, opts) {
110
119
  return runRemoteInvoke(message, dirArg, { ...opts, url: opts.url });
111
120
  if (opts.key !== undefined)
112
121
  failUsage("--key is the key --url calls with: pass --url too");
113
- const [{ openServing }, { enterAgentCommand, reportAuth }] = await Promise.all([
122
+ const [{ openServing }, { loadDevProject }, { enterAgentCommand, reportAuth }] = await Promise.all([
114
123
  import("../../harnesses/pi-durable/serve.js"),
124
+ import("../../harnesses/pi-durable/open.js"),
115
125
  import("../shared.js"),
116
126
  ]);
117
127
  const { agentDir, modelSpec } = await enterAgentCommand(dirArg, opts);
118
128
  const file = invokeStorageFile(resolveStateRoot(agentDir));
119
- const { agent, models, definition } = await openServing(agentDir, {
129
+ const project = await loadDevProject(agentDir).catch(failStartup);
130
+ const { agent, models, config } = await openServing(project.definition, {
120
131
  model: modelSpec,
132
+ ...instanceInputs(agentDir),
133
+ ...(project.instance === undefined ? {} : { config: project.instance }),
121
134
  storage: { file, holders: "another `fastagent invoke` of this agent" },
122
135
  // Its messages do not outlive it: one a stopped invoke left is not run again by the next.
123
136
  unfinished: "abort",
124
137
  }).catch(failStartup);
125
138
  console.error(`[fastagent] invoke: ${agentDir} (${modelSpec})`);
126
139
  await reportAuth(models, modelSpec, agentDir);
127
- const { thinkingLevel } = definition.config;
140
+ const { thinkingLevel } = config;
128
141
  const settings = {
129
142
  model: modelRefOf(modelSpec, "the model"),
130
143
  ...(thinkingLevel === undefined ? {} : { thinkingLevel }),
131
144
  };
132
145
  const sessionId = await sessionFor(agent, opts.session, settings, file).catch(failStartup);
133
146
  console.error(`[fastagent] session ${sessionId} (continue it with --session ${sessionId})`);
134
- const exitCode = await answer(agent.messages.stream({ sessionId, content: message }), opts.json);
147
+ const exitCode = await answer(agent.messages.stream({ sessionId, content: message }), opts.json, LOCAL_AUTH_HINT);
135
148
  await agent.close();
136
149
  // Always exit explicitly: the undici proxy agent's keep-alive sockets would otherwise hold the event loop open
137
150
  // after a successful one-shot message.
@@ -1,10 +1,3 @@
1
- /**
2
- * `fastagent login [provider]`: sign in to a model provider, into the project-level auth file
3
- * (`<agentDir>/.secrets/auth.json`) by default, or `FASTAGENT_AUTH_PATH`. In a terminal it asks, as pi's `/login`
4
- * does; otherwise (a script, a coding agent: no terminal, `--no-input` or `--json`) nothing is asked and nothing
5
- * waits: what it needs is named, and a person's step is handed back with a ticket to check (ticket.ts).
6
- * `--deployment` runs the same login on this agent's deployed box instead (box-login.ts); `--stdio` is the box's half.
7
- */
8
1
  import type { AuthInteraction } from "@earendil-works/pi-ai";
9
2
  import { type TicketStep } from "../ticket.ts";
10
3
  export interface LoginOptions {