@fastagent-sh/fastagent 0.29.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 (310) 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/program.js +282 -110
  119. package/dist/cli/serve.d.ts +4 -5
  120. package/dist/cli/serve.js +4 -7
  121. package/dist/cli/shared.d.ts +11 -2
  122. package/dist/cli/shared.js +32 -19
  123. package/dist/cli/ticket.js +50 -6
  124. package/dist/content/declare.d.ts +31 -37
  125. package/dist/content/declare.js +59 -60
  126. package/dist/content/file.d.ts +23 -12
  127. package/dist/content/file.js +48 -35
  128. package/dist/content/git.d.ts +0 -10
  129. package/dist/content/git.js +5 -39
  130. package/dist/content/resolve.d.ts +18 -22
  131. package/dist/content/resolve.js +32 -66
  132. package/dist/content/source.d.ts +7 -8
  133. package/dist/content/source.js +10 -28
  134. package/dist/credentials-page.d.ts +26 -0
  135. package/dist/credentials-page.js +185 -0
  136. package/dist/declared-secrets.d.ts +10 -3
  137. package/dist/declared-secrets.js +8 -4
  138. package/dist/deploy/agentcore/destroy.d.ts +4 -1
  139. package/dist/deploy/agentcore/destroy.js +25 -1
  140. package/dist/deploy/agentcore/forwarder.js +1 -1
  141. package/dist/deploy/agentcore/plan.d.ts +7 -21
  142. package/dist/deploy/agentcore/plan.js +47 -49
  143. package/dist/deploy/agentcore/run.d.ts +18 -10
  144. package/dist/deploy/agentcore/run.js +93 -70
  145. package/dist/deploy/agentcore/runtime-secrets.d.ts +7 -0
  146. package/dist/deploy/agentcore/runtime-secrets.js +108 -0
  147. package/dist/deploy/agentcore/shell.js +12 -19
  148. package/dist/deploy/agentcore/sigv4.d.ts +7 -0
  149. package/dist/deploy/agentcore/sigv4.js +15 -0
  150. package/dist/deploy/agentcore/slr.d.ts +18 -0
  151. package/dist/deploy/agentcore/slr.js +27 -0
  152. package/dist/deploy/agentcore/store.d.ts +7 -0
  153. package/dist/deploy/agentcore/store.js +115 -0
  154. package/dist/deploy/api.d.ts +14 -14
  155. package/dist/deploy/api.js +26 -24
  156. package/dist/deploy/box-shell.d.ts +3 -1
  157. package/dist/deploy/build-context.d.ts +4 -4
  158. package/dist/deploy/build-context.js +11 -15
  159. package/dist/deploy/channel-ingress.d.ts +35 -9
  160. package/dist/deploy/channel-ingress.js +119 -22
  161. package/dist/deploy/container.d.ts +35 -10
  162. package/dist/deploy/container.js +79 -37
  163. package/dist/deploy/delivered-text.d.ts +13 -0
  164. package/dist/deploy/delivered-text.js +86 -0
  165. package/dist/deploy/docker/plan.d.ts +7 -8
  166. package/dist/deploy/docker/plan.js +31 -52
  167. package/dist/deploy/docker/run.d.ts +5 -43
  168. package/dist/deploy/docker/run.js +9 -84
  169. package/dist/deploy/file-store.d.ts +2 -0
  170. package/dist/deploy/file-store.js +17 -0
  171. package/dist/deploy/fly/plan.d.ts +3 -2
  172. package/dist/deploy/fly/plan.js +20 -9
  173. package/dist/deploy/fly/run.d.ts +18 -8
  174. package/dist/deploy/fly/run.js +24 -42
  175. package/dist/deploy/fly/store.d.ts +12 -0
  176. package/dist/deploy/fly/store.js +83 -0
  177. package/dist/deploy/hosts.d.ts +1 -2
  178. package/dist/deploy/hosts.js +1 -2
  179. package/dist/deploy/preflight.d.ts +18 -5
  180. package/dist/deploy/preflight.js +135 -63
  181. package/dist/deploy/railway/ignore.d.ts +8 -0
  182. package/dist/deploy/railway/ignore.js +57 -0
  183. package/dist/deploy/railway/plan.d.ts +1 -2
  184. package/dist/deploy/railway/plan.js +8 -6
  185. package/dist/deploy/railway/run.d.ts +21 -15
  186. package/dist/deploy/railway/run.js +32 -78
  187. package/dist/deploy/railway/store.d.ts +29 -0
  188. package/dist/deploy/railway/store.js +143 -0
  189. package/dist/deploy/runner.d.ts +5 -2
  190. package/dist/deploy/runner.js +4 -3
  191. package/dist/deploy/secret-store.d.ts +63 -0
  192. package/dist/deploy/secret-store.js +98 -0
  193. package/dist/deploy/secrets.d.ts +29 -46
  194. package/dist/deploy/secrets.js +49 -122
  195. package/dist/deploy/workspace.d.ts +6 -12
  196. package/dist/deploy/workspace.js +18 -94
  197. package/dist/dev-supervisor.js +6 -8
  198. package/dist/environment/declare.js +3 -2
  199. package/dist/environment/mise.d.ts +3 -1
  200. package/dist/environment/mise.js +4 -2
  201. package/dist/feishu.d.ts +1 -1
  202. package/dist/feishu.js +1 -1
  203. package/dist/harnesses/models/catalog.d.ts +1 -0
  204. package/dist/harnesses/models/catalog.js +1 -2
  205. package/dist/harnesses/models/environment.d.ts +7 -2
  206. package/dist/harnesses/models/environment.js +9 -5
  207. package/dist/harnesses/models/host.d.ts +0 -7
  208. package/dist/harnesses/models/host.js +10 -1
  209. package/dist/harnesses/models/login.d.ts +0 -8
  210. package/dist/harnesses/models/login.js +5 -4
  211. package/dist/harnesses/models/models.d.ts +7 -2
  212. package/dist/harnesses/models/models.js +28 -4
  213. package/dist/harnesses/pi-durable/agent.js +59 -38
  214. package/dist/harnesses/pi-durable/build.d.ts +12 -0
  215. package/dist/harnesses/pi-durable/build.js +270 -0
  216. package/dist/harnesses/pi-durable/built.d.ts +24 -0
  217. package/dist/harnesses/pi-durable/built.js +136 -0
  218. package/dist/harnesses/pi-durable/config.d.ts +81 -0
  219. package/dist/harnesses/pi-durable/config.js +266 -0
  220. package/dist/harnesses/pi-durable/define.d.ts +63 -13
  221. package/dist/harnesses/pi-durable/define.js +23 -2
  222. package/dist/harnesses/pi-durable/definition.d.ts +72 -0
  223. package/dist/harnesses/pi-durable/definition.js +143 -0
  224. package/dist/harnesses/pi-durable/events.d.ts +4 -3
  225. package/dist/harnesses/pi-durable/events.js +179 -136
  226. package/dist/harnesses/pi-durable/execution-env.d.ts +17 -0
  227. package/dist/harnesses/pi-durable/execution-env.js +11 -0
  228. package/dist/harnesses/pi-durable/mcp.js +1 -3
  229. package/dist/harnesses/pi-durable/open.d.ts +35 -37
  230. package/dist/harnesses/pi-durable/open.js +91 -153
  231. package/dist/harnesses/pi-durable/reads.d.ts +0 -1
  232. package/dist/harnesses/pi-durable/reads.js +1 -3
  233. package/dist/harnesses/pi-durable/registry.d.ts +4 -6
  234. package/dist/harnesses/pi-durable/registry.js +29 -26
  235. package/dist/harnesses/pi-durable/release.d.ts +2 -2
  236. package/dist/harnesses/pi-durable/release.js +10 -7
  237. package/dist/harnesses/pi-durable/serve.d.ts +91 -15
  238. package/dist/harnesses/pi-durable/serve.js +109 -47
  239. package/dist/harnesses/pi-durable/sessions.js +15 -12
  240. package/dist/harnesses/pi-durable/text-sources.d.ts +30 -0
  241. package/dist/harnesses/pi-durable/text-sources.js +55 -0
  242. package/dist/harnesses/pi-durable/text.d.ts +16 -4
  243. package/dist/harnesses/pi-durable/text.js +115 -59
  244. package/dist/harnesses/pi-durable/wake.js +8 -8
  245. package/dist/http/client.js +9 -3
  246. package/dist/http/contract.d.ts +3 -3
  247. package/dist/http/server.d.ts +1 -1
  248. package/dist/http/server.js +1 -1
  249. package/dist/index.d.ts +8 -5
  250. package/dist/index.js +4 -1
  251. package/dist/instance-name.d.ts +7 -0
  252. package/dist/instance-name.js +26 -0
  253. package/dist/lark.d.ts +1 -1
  254. package/dist/lark.js +1 -1
  255. package/dist/mcp/client.js +11 -2
  256. package/dist/mcp/config.d.ts +70 -18
  257. package/dist/mcp/config.js +136 -53
  258. package/dist/mcp/host.d.ts +5 -2
  259. package/dist/mcp/host.js +14 -6
  260. package/dist/mcp/sign-in.js +2 -2
  261. package/dist/mcp-api.d.ts +14 -0
  262. package/dist/paths.d.ts +22 -8
  263. package/dist/paths.js +44 -11
  264. package/dist/runtime-websocket.d.ts +14 -0
  265. package/dist/runtime-websocket.js +39 -0
  266. package/dist/scaffold/add-channel.d.ts +9 -16
  267. package/dist/scaffold/add-channel.js +13 -83
  268. package/dist/scaffold/templates/env.example +4 -5
  269. package/dist/scaffold/templates/fastagent.config.ts +20 -14
  270. package/dist/scaffold/templates/gitignore +9 -6
  271. package/dist/scaffold/templates.d.ts +6 -2
  272. package/dist/scaffold/templates.js +7 -2
  273. package/dist/scaffold/vendor-skill.js +4 -2
  274. package/dist/service.d.ts +44 -6
  275. package/dist/service.js +110 -10
  276. package/dist/serving-command.d.ts +10 -0
  277. package/dist/serving-command.js +1 -0
  278. package/dist/slack.d.ts +1 -1
  279. package/dist/slack.js +1 -1
  280. package/dist/strip-edges.d.ts +10 -0
  281. package/dist/strip-edges.js +22 -0
  282. package/dist/telegram.d.ts +1 -1
  283. package/dist/telegram.js +1 -1
  284. package/dist/tunnel.d.ts +0 -1
  285. package/dist/tunnel.js +2 -8
  286. package/dist/value-file.d.ts +11 -6
  287. package/dist/value-file.js +21 -9
  288. package/dist/version.d.ts +7 -1
  289. package/dist/version.js +9 -3
  290. package/package.json +6 -5
  291. package/dist/channels/lark/credentials-page.d.ts +0 -25
  292. package/dist/channels/lark/credentials-page.js +0 -206
  293. package/dist/channels/lark/onboard.d.ts +0 -52
  294. package/dist/channels/lark/onboard.js +0 -86
  295. package/dist/channels/slack/config-api.d.ts +0 -69
  296. package/dist/channels/slack/config-api.js +0 -162
  297. package/dist/channels/slack/onboard.d.ts +0 -40
  298. package/dist/channels/slack/onboard.js +0 -136
  299. package/dist/channels/slack/onboarding-state.d.ts +0 -31
  300. package/dist/channels/slack/onboarding-state.js +0 -64
  301. package/dist/channels/slack/register-webhook.d.ts +0 -12
  302. package/dist/channels/slack/register-webhook.js +0 -67
  303. package/dist/channels/slack/setup-server.d.ts +0 -32
  304. package/dist/channels/slack/setup-server.js +0 -246
  305. package/dist/cli/add-feishu.d.ts +0 -41
  306. package/dist/cli/add-feishu.js +0 -391
  307. package/dist/cli/add-slack.d.ts +0 -18
  308. package/dist/cli/add-slack.js +0 -216
  309. package/dist/harnesses/pi-durable/parked.d.ts +0 -18
  310. 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, 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,8 +129,8 @@ 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
136
  const timeout = opts.timeout === undefined ? TICKET_TIMEOUT_SECONDS : Number(opts.timeout);
@@ -160,8 +154,8 @@ async function deploy(host, dirArg, opts, step) {
160
154
  "deploy",
161
155
  host,
162
156
  agentDir,
163
- ...(opts.tunnel ? ["--tunnel"] : []),
164
157
  ...(opts.force ? ["--force"] : []),
158
+ ...(delivered === undefined ? [] : ["--config", delivered.source.label]),
165
159
  ...(opts.intoLinked ? ["--into-linked"] : []),
166
160
  "--run",
167
161
  "--json",
@@ -175,50 +169,38 @@ async function deploy(host, dirArg, opts, step) {
175
169
  /** Everything past the pre-flight, its person's steps taken by `answerer`; the URL and what the API report says. */
176
170
  const deployWith = async (answerer) => {
177
171
  if (unprepared.length > 0) {
178
- const supplied = unprepared.flatMap(({ kind, ingress }) => feishuAppSecretNames(kind, ingress));
172
+ const supplied = unprepared.flatMap(channelAppNames);
179
173
  const kinds = unprepared.map(({ kind }) => kind);
180
174
  const { values } = pre;
181
175
  const absent = supplied.filter((name) => !values.get(name)?.trim());
182
- const { missingSecrets } = assembleSecrets({
183
- modelAuth: pre.modelAuth,
184
- declared: pre.declaredSecrets,
185
- values: pre.values,
186
- });
187
- const missing = missingValuesGate(missingSecrets.filter((name) => !supplied.includes(name)), pre.valueFile);
188
- if (missing)
189
- failStartup(new Error(`deploy stopped: ${missing}`));
190
- if (host === "docker" && !opts.tunnel && unprepared.some(({ ingress }) => ingress === "webhook")) {
176
+ if (host === "docker" && unprepared.some(({ way }) => way === "webhook")) {
191
177
  failStartup(new Error(`deploy stopped: ${kinds.join(", ")} receive by webhook here, and this Docker deployment has no ` +
192
- `public URL to point the app at — re-run with --tunnel (the app is prepared then), or set up your own ` +
193
- `ingress and supply ${absent.join(", ")} from a separate production app in ${pre.valueFile}, or ` +
194
- `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`));
195
181
  }
196
182
  if (answerer.kind === "nobody") {
197
183
  failUsage(`deploy stopped: ${pre.valueFile} has no ${absent.join(", ")}: setting up the separate production app ` +
198
184
  `needs a person — run this deploy in a terminal; from a coding agent, run \`${rerun} --json\`, which ` +
199
185
  `hands their part to its user`);
200
186
  }
201
- const person = answerer.kind === "ticket" ? answerer.step.person : terminalPerson;
202
187
  const again = answerer.kind === "ticket" ? `${rerun} --json` : rerun;
203
- await prepareFeishuApps(agentDir, unprepared, again, person).catch(failStartup);
204
- pre = await preflight();
205
- }
206
- independentApps(pre.values);
207
- if (api.served && api.generatesKey) {
208
- // Past every refusal the pre-flight makes, and read again, so this deploy carries it.
209
- const unprotected = await writeApiKey(agentDir).catch(failStartup);
210
- if (unprotected !== undefined)
211
- console.error(`[fastagent] note: ${unprotectedSecretsNote(unprotected)}`);
188
+ await prepareChannelApps(agentDir, unprepared, again, answerer.kind === "ticket" ? answerer.step.person : undefined).catch(failStartup);
212
189
  pre = await preflight();
213
190
  }
214
191
  for (const m of pre.messages)
215
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}`);
216
197
  const { channels } = pre;
217
198
  warnHostOnlyFlags(host, opts);
218
199
  // What follows is the host's work: a deadline meant for the person must never cut it halfway. Its own steps for
219
200
  // the person (a host sign-in, the box's login) wait again, and are bounded again.
220
201
  if (answerer.kind === "ticket")
221
202
  answerer.step.person.carriesOn();
203
+ let textDelivered = false;
222
204
  const reachedAt = await target.deploy({
223
205
  opts,
224
206
  answerer,
@@ -226,6 +208,7 @@ async function deploy(host, dirArg, opts, step) {
226
208
  agentDir,
227
209
  config,
228
210
  pre,
211
+ api,
229
212
  channels,
230
213
  webhookChannels: channels.filter((channel) => channel.ingress === "webhook"),
231
214
  // The ownership predicate is bound HERE, from the same lookup that chose the host, so no host module can pass
@@ -242,10 +225,20 @@ async function deploy(host, dirArg, opts, step) {
242
225
  `would deploy from them. Re-run with --force to regenerate, or remove each file's generated-by ` +
243
226
  `marker to own it yourself.`));
244
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
+ }
245
237
  await applyArtifactPlan(plan);
246
238
  },
247
239
  });
248
- 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);
249
242
  for (const line of notes)
250
243
  console.error(`[fastagent] ${line}`);
251
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,49 +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 { 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";
11
15
  import { inspectMcp } from "../../mcp/host.js";
12
- import { resolveStateRoot } from "../../paths.js";
16
+ import { resolveStateRoot, resolveWorkdir } from "../../paths.js";
13
17
  import { nextRun } from "../../schedule/cron.js";
14
18
  import { contentLines } from "../content-view.js";
15
19
  import { agentDirOrExit, failStartup } from "../fail.js";
16
20
  export async function runInfo(dirArg, opts) {
17
21
  const agentDir = agentDirOrExit(resolve(dirArg));
18
- enterAgentEnv(agentDir); // code may read its secrets, and fetch, when it is built
19
- const definition = await prepareDefinition(agentDir).catch(failStartup);
20
- 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);
21
34
  const modelSpec = modelSpecFor(config, opts.model);
22
- const manifest = secretsManifest(definition);
23
- const declared = manifest.flatMap(({ name, optional, declaredIn }) => declaredIn.map((source) => ({ name, optional, source })));
24
- 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);
25
44
  // Built only when every required secret has a value: building is what reads them. A module that is not what its
26
45
  // directory holds is said as the refusal `dev` and `start` would stop on.
27
46
  const code = unsetSecrets.length === 0
28
47
  ? await Promise.resolve()
29
- .then(() => buildDefinition(definition))
48
+ .then(() => buildDefinition(definition, process.env, config))
30
49
  .catch(failStartup)
31
50
  : undefined;
32
51
  // Reported, not fatal: `info` is how an author finds out a content entry is missing, or the environment refused.
33
52
  let content = [];
34
- let contentAbsent = [];
35
53
  let contentError;
36
54
  try {
37
- const declaration = loadContent(agentDir);
38
- content = resolveContent(agentDir, declaration);
39
- contentAbsent = contentAbsentHere(declaration);
55
+ content = resolveContent(loadContent(agentDir, instance), { dir: agentDir, workdir });
40
56
  }
41
57
  catch (error) {
42
58
  contentError = error.message;
43
59
  }
44
60
  // Read already, by the definition's secrets: a refused environment is a definition that does not open.
45
61
  const environment = loadEnvironment(agentDir);
46
- const models = await modelEnvironment(agentDir, {}, code?.providers ?? []);
62
+ const models = await modelEnvironment(agentDir, {}, code?.providers ?? [], {
63
+ environment: definedValues(process.env),
64
+ });
47
65
  const slash = modelSpec?.indexOf("/") ?? -1;
48
66
  const modelError = modelSpec === undefined || models.models.getModel(modelSpec.slice(0, slash), modelSpec.slice(slash + 1))
49
67
  ? undefined
@@ -58,29 +76,33 @@ export async function runInfo(dirArg, opts) {
58
76
  tz: s.tz ?? null,
59
77
  next: nextRun(s.cron, s.tz, new Date())?.toISOString() ?? null,
60
78
  }));
61
- 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 }));
62
83
  const stateRoot = resolveStateRoot(agentDir);
63
84
  const auth = models.auth;
64
- const mcp = await inspectMcp(agentDir).catch(failStartup);
85
+ const mcp = await inspectMcp(agentDir, instance === undefined ? {} : { config: instance.label }).catch(failStartup);
65
86
  if (opts.json) {
66
87
  console.log(JSON.stringify({
67
88
  agentDir,
68
- name: definition.name,
89
+ name: name,
69
90
  content,
70
- contentAbsent,
71
91
  contentError: contentError ?? null,
72
92
  environment: environment ?? null,
73
93
  model: modelSpec ?? null,
74
94
  modelError: modelError ?? null,
75
95
  thinkingLevel: config.thinkingLevel ?? null,
96
+ config: configReport(config, opts.model),
76
97
  tools: code?.tools.map(({ name }) => name) ?? null,
77
98
  mcp,
78
99
  extensions: code?.extensions.map(({ name }) => name) ?? null,
79
100
  channels: code === undefined ? null : channels,
80
101
  schedules: code === undefined ? null : schedules,
81
102
  commands,
82
- secrets: manifest,
103
+ secrets: definition.inputs.secrets,
83
104
  unsetSecrets: unsetSecrets.map(({ name }) => name),
105
+ workdir,
84
106
  stateRoot,
85
107
  authPath: auth?.path ?? null,
86
108
  fallbackAuthPath: auth?.fallback ?? null,
@@ -90,13 +112,13 @@ export async function runInfo(dirArg, opts) {
90
112
  const line = (label, value) => console.log(`${`${label}:`.padEnd(13)} ${value}`);
91
113
  /** A continuation under the previous line, aligned to the same column (no label, so no bare colon). */
92
114
  const cont = (value) => console.log(`${"".padEnd(13)} ${value}`);
93
- line("agent", `${definition.name} — ${agentDir}`);
94
- for (const [label, value] of contentLines(content, contentAbsent))
115
+ line("agent", `${name} — ${agentDir}`);
116
+ for (const [label, value] of contentLines(content))
95
117
  line(label, value);
96
118
  if (contentError)
97
119
  cont(`⚠ ${contentError}`);
98
120
  line("environment", environment ? describeEnvironment(environment) : "(none: the machine's commands)");
99
- 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)");
100
122
  if (modelError)
101
123
  cont(`⚠ does not resolve: ${modelError}`);
102
124
  if (config.thinkingLevel)
@@ -113,10 +135,23 @@ export async function runInfo(dirArg, opts) {
113
135
  if (unsetSecrets.length > 0) {
114
136
  cont(`⚠ dev, start and deployments refuse to boot until set: ${unsetSecrets.map((s) => s.name).join(", ")}`);
115
137
  }
138
+ line("workdir", workdir);
116
139
  line("state", stateRoot);
117
140
  if (auth)
118
141
  line("auth", auth.fallback === undefined ? auth.path : `${auth.path} (then ${auth.fallback})`);
119
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
+ }
120
155
  /** The packages and apt packages an environment declares, as one line. */
121
156
  function describeEnvironment(environment) {
122
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 {