@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
@@ -0,0 +1,37 @@
1
+ /**
2
+ * A serve pointing its platforms at itself (SPEC.md §8, `Service.registerWebhooks`; channel-setup.md §5): which of its
3
+ * channels take a webhook, the step a person takes for each one the instance cannot set, and the registration that
4
+ * sets what the app's own credentials can set, on every call and without reading it first.
5
+ */
6
+ import type { DeclaredChannel } from "./discover.ts";
7
+ import { type RegistrationOutcome } from "./registration.ts";
8
+ /** One webhook channel's registration (SPEC.md §8). */
9
+ export interface WebhookRegistration {
10
+ /** The channel file's name. */
11
+ channel: string;
12
+ outcome: "pending" | RegistrationOutcome;
13
+ /** The URL it was, or is to be, pointed at; absent when no base URL is known. */
14
+ url?: string;
15
+ /** For `manual` and `failed`: what to do, said as the step to take. */
16
+ step?: string;
17
+ }
18
+ /** The first-party channels a webhook points at, in the order every report lists them. */
19
+ export type WebhookKind = "telegram" | "slack" | "feishu" | "lark";
20
+ /** The first-party channels among `channels` that receive by webhook, in {@link KINDS}' order. */
21
+ export declare function webhookKinds(channels: readonly DeclaredChannel[]): WebhookKind[];
22
+ /** The channel's default route. */
23
+ export declare function webhookPath(kind: WebhookKind): string;
24
+ /** The one line a person acts on to point `kind` at `baseUrl` by hand. */
25
+ export declare function manualWebhookStep(kind: WebhookKind, baseUrl: string): string;
26
+ /** Every webhook channel, not registered yet: what a serve reports until its registration ends. */
27
+ export declare function pendingWebhooks(kinds: readonly WebhookKind[]): WebhookRegistration[];
28
+ /**
29
+ * Point each of `kinds` at `<baseUrl>/<its path>` with the instance's own credentials (`env`): Telegram's `setWebhook`
30
+ * and Feishu's or Lark's event-subscription PATCH, set on every call (Telegram's `getWebhookInfo` returns no
31
+ * `secret_token`, and Feishu's route takes only a PATCH). Slack's Request URL is the app's configuration, which no
32
+ * credential the instance holds can change: its outcome is `manual`. Without a base URL nothing is pointed.
33
+ */
34
+ export declare function registerOwnWebhooks(baseUrl: string | undefined, kinds: readonly WebhookKind[], env: NodeJS.ProcessEnv, options?: {
35
+ attempts?: number;
36
+ retryMs?: number;
37
+ }): Promise<WebhookRegistration[]>;
@@ -0,0 +1,70 @@
1
+ import { registerFeishuWebhook } from "./feishu/register-webhook.js";
2
+ import { DEPLOY_REGISTRATION_ATTEMPTS } from "./registration.js";
3
+ import { registerTelegramWebhook } from "./telegram/register-webhook.js";
4
+ import { log } from "../log.js";
5
+ const feishuCloud = (kind) => ({
6
+ path: `/${kind}`,
7
+ manual: (url) => `${kind}: set the event Request URL in the developer console (Events & Callbacks) → ${url} (the app must be running when you save)`,
8
+ });
9
+ const KINDS = {
10
+ telegram: {
11
+ path: "/telegram",
12
+ manual: (url) => `telegram: set the webhook → ${url} (secret_token = TELEGRAM_SECRET_TOKEN)`,
13
+ },
14
+ slack: { path: "/slack", manual: (url) => `slack: set Event Subscriptions → Request URL → ${url}` },
15
+ feishu: feishuCloud("feishu"),
16
+ lark: feishuCloud("lark"),
17
+ };
18
+ /** The first-party channels among `channels` that receive by webhook, in {@link KINDS}' order. */
19
+ export function webhookKinds(channels) {
20
+ const declared = new Set(channels.filter((c) => c.ingress === "webhook").map((c) => c.name));
21
+ return Object.keys(KINDS).filter((kind) => declared.has(kind));
22
+ }
23
+ /** The channel's default route. */
24
+ export function webhookPath(kind) {
25
+ return KINDS[kind].path;
26
+ }
27
+ /** The one line a person acts on to point `kind` at `baseUrl` by hand. */
28
+ export function manualWebhookStep(kind, baseUrl) {
29
+ return KINDS[kind].manual(`${baseUrl}${KINDS[kind].path}`);
30
+ }
31
+ /** Every webhook channel, not registered yet: what a serve reports until its registration ends. */
32
+ export function pendingWebhooks(kinds) {
33
+ return kinds.map((channel) => ({ channel, outcome: "pending" }));
34
+ }
35
+ /**
36
+ * Point each of `kinds` at `<baseUrl>/<its path>` with the instance's own credentials (`env`): Telegram's `setWebhook`
37
+ * and Feishu's or Lark's event-subscription PATCH, set on every call (Telegram's `getWebhookInfo` returns no
38
+ * `secret_token`, and Feishu's route takes only a PATCH). Slack's Request URL is the app's configuration, which no
39
+ * credential the instance holds can change: its outcome is `manual`. Without a base URL nothing is pointed.
40
+ */
41
+ export async function registerOwnWebhooks(baseUrl, kinds, env, options = {}) {
42
+ const registrations = [];
43
+ for (const kind of kinds) {
44
+ if (baseUrl === undefined) {
45
+ registrations.push({
46
+ channel: kind,
47
+ outcome: "manual",
48
+ step: `${kind}: no http.publicUrl in the instance configuration, so nothing points the app at this instance`,
49
+ });
50
+ continue;
51
+ }
52
+ const url = `${baseUrl}${KINDS[kind].path}`;
53
+ const step = KINDS[kind].manual(url);
54
+ if (kind === "slack") {
55
+ registrations.push({ channel: kind, outcome: "manual", url, step });
56
+ continue;
57
+ }
58
+ const attempts = options.attempts ?? DEPLOY_REGISTRATION_ATTEMPTS;
59
+ const retry = { attempts, ...(options.retryMs === undefined ? {} : { retryMs: options.retryMs }) };
60
+ // A registrar that throws ends its channel `failed`, said with why, never left `pending` for a reader to wait on.
61
+ const outcome = await (kind === "telegram"
62
+ ? registerTelegramWebhook(baseUrl, { env, ...retry })
63
+ : registerFeishuWebhook(baseUrl, kind, { env, ...retry })).catch((error) => {
64
+ log.error(`[fastagent] ${kind}: could not register its webhook: ${String(error)}`);
65
+ return "failed";
66
+ });
67
+ registrations.push({ channel: kind, outcome, url, ...(outcome === "registered" ? {} : { step }) });
68
+ }
69
+ return registrations;
70
+ }
@@ -0,0 +1,16 @@
1
+ /** The base environment `source` gives a process: the variables of the base list it holds, nothing else. */
2
+ export declare function baseEnv(source: Readonly<Record<string, string | undefined>>): Record<string, string>;
3
+ export interface ProcessEnvironmentInputs {
4
+ /** The base environment (`baseEnv`). */
5
+ base: Readonly<Record<string, string>>;
6
+ /** The instance's secrets: only the declared names among them are passed on. */
7
+ secrets: Readonly<Record<string, string | undefined>>;
8
+ /** The names the definition declares (`needs()`, `context.json`'s environment variables). */
9
+ declared: readonly string[];
10
+ /** What the declared packages add (`mise env`): their commands ahead on the `PATH` the base gave. */
11
+ packages: Readonly<Record<string, string>>;
12
+ /** The agent's working directory. */
13
+ workdir: string;
14
+ }
15
+ /** What every process the agent starts gets, before an MCP server's own `env` is added for that server. */
16
+ export declare function processEnvironment(inputs: ProcessEnvironmentInputs, workspaceParent: string): Record<string, string>;
@@ -0,0 +1,30 @@
1
+ /**
2
+ * The environment of the processes an instance starts (agent-artifact.md §5): the shell tool, stdio MCP servers and
3
+ * skill scripts. Assembled from inputs, never the host process's whole `process.env`, so a stray variable of the host
4
+ * (an API key, a cloud credential) cannot reach a command, and a declared one reaches every command.
5
+ */
6
+ import { definedValues } from "./declared-secrets.js";
7
+ const BASE_NAMES = new Set([
8
+ "PATH",
9
+ "HOME",
10
+ "USER",
11
+ "SHELL",
12
+ "LANG",
13
+ "TZ",
14
+ "TMPDIR",
15
+ "HTTP_PROXY",
16
+ "HTTPS_PROXY",
17
+ "NO_PROXY",
18
+ ]);
19
+ /** The base environment `source` gives a process: the variables of the base list it holds, nothing else. */
20
+ export function baseEnv(source) {
21
+ return Object.fromEntries(Object.entries(definedValues(source)).filter(([name]) => BASE_NAMES.has(name) || name.startsWith("LC_")));
22
+ }
23
+ /** `GIT_CEILING_DIRECTORIES` at the workspace's parent, so git discovery stops at the workspace. */
24
+ const GIT_CEILING = "GIT_CEILING_DIRECTORIES";
25
+ /** What every process the agent starts gets, before an MCP server's own `env` is added for that server. */
26
+ export function processEnvironment(inputs, workspaceParent) {
27
+ const held = definedValues(inputs.secrets);
28
+ const declared = Object.fromEntries(inputs.declared.flatMap((name) => (Object.hasOwn(held, name) ? [[name, held[name]]] : [])));
29
+ return { ...inputs.base, ...declared, ...inputs.packages, [GIT_CEILING]: workspaceParent };
30
+ }
@@ -24,7 +24,10 @@ export async function loginOnBox(request) {
24
24
  ];
25
25
  let command;
26
26
  try {
27
- command = boxLoginCommand(args, shell.storage);
27
+ command = boxLoginCommand(args, {
28
+ ...(shell.storage === undefined ? {} : { storage: shell.storage }),
29
+ ...(shell.program === undefined ? {} : { program: shell.program }),
30
+ });
28
31
  }
29
32
  catch (error) {
30
33
  return error.message;
@@ -0,0 +1,3 @@
1
+ import type { WebhookKind } from "../channels/webhooks.ts";
2
+ /** Record `url` as the confirmed URL of `kind`'s app in the configuration file `config`. */
3
+ export declare function recordChannelUrl(agentDir: string, config: string, kind: WebhookKind, url: string, values: ReadonlyMap<string, string>): Promise<void>;
@@ -0,0 +1,35 @@
1
+ /**
2
+ * The binding of a channel whose URL only a person sets (Slack; Lark without its configuration API), recorded in an
3
+ * instance configuration once the deployment confirms the URL (`channels.<name>.app`, SPEC.md §8): what `deploy` at a
4
+ * terminal and `channels setup --record-url` both write.
5
+ */
6
+ import { setChannelConfig } from "../authoring.js";
7
+ import { createSlackApi } from "../channels/slack/slack-api.js";
8
+ /**
9
+ * The app a recorded URL belongs to: Lark's App ID from its values; Slack's from its bot token, which names the app a
10
+ * bot belongs to (`bots.info`, which needs the `users:read` scope the app is created with).
11
+ */
12
+ async function appOf(kind, values) {
13
+ if (kind === "lark") {
14
+ const id = values.get("LARK_APP_ID");
15
+ if (!id)
16
+ throw new Error("lark: no LARK_APP_ID in the values, so the URL cannot be recorded against its app");
17
+ return { id };
18
+ }
19
+ const botToken = values.get("SLACK_BOT_TOKEN");
20
+ if (!botToken)
21
+ throw new Error("slack: no SLACK_BOT_TOKEN in the values, so the URL cannot be recorded against its app");
22
+ const api = createSlackApi({ botToken });
23
+ const { teamId, botId } = await api.authTest();
24
+ const id = botId === undefined ? undefined : await api.botAppId(botId);
25
+ if (id === undefined)
26
+ throw new Error("slack: Slack named no app for this bot token (does it hold users:read?)");
27
+ return { id, ...(teamId === undefined ? {} : { team: teamId }) };
28
+ }
29
+ /** Record `url` as the confirmed URL of `kind`'s app in the configuration file `config`. */
30
+ export async function recordChannelUrl(agentDir, config, kind, url, values) {
31
+ if (kind !== "slack" && kind !== "lark") {
32
+ throw new Error(`${kind}: its URL the instance sets itself, so there is nothing to record`);
33
+ }
34
+ await setChannelConfig(agentDir, kind, { app: { ...(await appOf(kind, values)), url } }, { config });
35
+ }
@@ -0,0 +1,38 @@
1
+ import { type ChannelSetup, type SetupWay } from "../channels/setup.ts";
2
+ import { type Person } from "./person.ts";
3
+ declare const SETUPS: {
4
+ readonly telegram: ChannelSetup;
5
+ readonly slack: ChannelSetup;
6
+ readonly feishu: ChannelSetup;
7
+ readonly lark: ChannelSetup;
8
+ };
9
+ /** The channels whose app `channels setup` sets up. */
10
+ export type SetupKind = keyof typeof SETUPS;
11
+ export declare const isSetupKind: (name: string) => name is SetupKind;
12
+ export interface SetUpChannelInput {
13
+ kind: SetupKind;
14
+ agentDir: string;
15
+ /** The value file the credentials are kept in. */
16
+ file: string;
17
+ way: SetupWay;
18
+ /** Whether this is the development instance. */
19
+ dev: boolean;
20
+ /** The command that runs this setup again. */
21
+ rerun: string;
22
+ /** Where the platform will reach the instance, when its configuration names it. */
23
+ publicUrl?: string;
24
+ /** A coding agent's person, through a ticket: asked on a page. Absent: asked at this terminal. */
25
+ person?: Person;
26
+ }
27
+ export interface SetUpOutcome {
28
+ /** The variables this run wrote. */
29
+ written: string[];
30
+ /** The variables that were set already, and are accepted. */
31
+ kept: string[];
32
+ /** What is left to the person once it is done. */
33
+ after: string[];
34
+ }
35
+ /** The variables a channel's app needs for `way`, in the order setup asks for them. */
36
+ export declare function setupNames(kind: SetupKind, way: SetupWay): string[];
37
+ export declare function setUpChannel(input: SetUpChannelInput): Promise<SetUpOutcome>;
38
+ export {};
@@ -0,0 +1,195 @@
1
+ /**
2
+ * Setting up one channel's platform app for one instance (channel-setup.md §2): ask a person for the values only they
3
+ * have, check each with the platform, and keep what is accepted in the instance's value file. Safe to run again: a value
4
+ * already there that the platform still accepts is kept, one it refuses is asked for again, a value setup makes itself
5
+ * is made once, and an app already created is not created again.
6
+ *
7
+ * What differs per channel is in `channels/<kind>/setup.ts`; this is how they are asked: at a terminal, or, for a coding
8
+ * agent that has none, on a page of this machine's (credentials-page.ts) that its person is sent to through a ticket.
9
+ */
10
+ import { basename } from "node:path";
11
+ import { isCancel, log as clackLog, password, text } from "@clack/prompts";
12
+ import { feishuSetup } from "../channels/feishu/setup.js";
13
+ import { larkSetup } from "../channels/lark/setup.js";
14
+ import { slackSetup } from "../channels/slack/setup.js";
15
+ import { SetupRefused, } from "../channels/setup.js";
16
+ import { telegramSetup } from "../channels/telegram/setup.js";
17
+ import { startCredentialsPage } from "../credentials-page.js";
18
+ import { openExternalUrl } from "../open-url.js";
19
+ import { startCloudflareTunnel } from "../tunnel.js";
20
+ import { loadEnvValues, unprotectedSecretsNote, writeValues } from "../value-file.js";
21
+ import { terminalPerson } from "./person.js";
22
+ const SETUPS = {
23
+ telegram: telegramSetup,
24
+ slack: slackSetup,
25
+ feishu: feishuSetup,
26
+ lark: larkSetup,
27
+ };
28
+ const PRODUCTS = { telegram: "Telegram", slack: "Slack", feishu: "Feishu", lark: "Lark" };
29
+ export const isSetupKind = (name) => Object.hasOwn(SETUPS, name);
30
+ const say = (message) => console.error(`[fastagent] ${message}`);
31
+ /** The variables a channel's app needs for `way`, in the order setup asks for them. */
32
+ export function setupNames(kind, way) {
33
+ const plan = SETUPS[kind]({ way, appName: "", dev: true, rerun: "", startTunnel: startCloudflareTunnel });
34
+ return plan.values.flatMap((value) => value.kind === "paste" ? value.fields.map((one) => one.name) : value.kind === "create" ? value.names : [value.name]);
35
+ }
36
+ export async function setUpChannel(input) {
37
+ const { kind, agentDir, file, person } = input;
38
+ const plan = SETUPS[kind]({
39
+ way: input.way,
40
+ appName: basename(agentDir),
41
+ dev: input.dev,
42
+ rerun: input.rerun,
43
+ startTunnel: startCloudflareTunnel,
44
+ ...(input.publicUrl === undefined ? {} : { publicUrl: input.publicUrl }),
45
+ });
46
+ const written = [];
47
+ const kept = [];
48
+ /** A directory fastagent does not own that now holds a secret: said once, however many values went there. */
49
+ let unprotected;
50
+ const held = new Map();
51
+ for (const [name, value] of loadEnvValues(file))
52
+ if (value.trim() !== "")
53
+ held.set(name, value.trim());
54
+ /** Keep `entries` in one write: an app's values come together, and none of them may be lost to a later failure. */
55
+ const keep = async (entries, replace) => {
56
+ const names = entries.map((entry) => entry.name);
57
+ const result = await writeValues(agentDir, `${kind} channel`, entries, replace ? names : [], file);
58
+ unprotected ??= result.unprotectedSecretsDir;
59
+ for (const entry of entries)
60
+ held.set(entry.name, entry.value);
61
+ written.push(...names);
62
+ };
63
+ /**
64
+ * Whether this run has put an app's values in, or is about to: a value held beside them is an earlier app's (a token
65
+ * kept from one app must never go with the credentials of another), so it is asked for again.
66
+ */
67
+ let renewed = false;
68
+ const accept = async (step, values) => {
69
+ const note = await step.check(values);
70
+ renewed = true;
71
+ await keep(step.fields.map(({ name }) => ({ name, value: values[name] })), true);
72
+ say(`${step.fields.map((one) => one.name).join(", ")} saved to ${file}${note ? ` (${note})` : ""}`);
73
+ };
74
+ const pending = [];
75
+ /** Ask for the steps gathered so far, together: one page, however many tokens. */
76
+ const ask = async (steps) => {
77
+ if (steps.length === 0)
78
+ return;
79
+ if (person === undefined)
80
+ await askAtTerminal(steps, held, accept);
81
+ else
82
+ await askOnPage(kind, steps, held, accept, person);
83
+ };
84
+ const flush = () => ask(pending.splice(0));
85
+ for (const value of plan.values) {
86
+ if (value.kind === "make") {
87
+ if (held.has(value.name))
88
+ kept.push(value.name);
89
+ else
90
+ await keep([{ name: value.name, value: value.make() }], false);
91
+ continue;
92
+ }
93
+ if (value.kind === "create") {
94
+ if (value.names.every((name) => held.has(name))) {
95
+ kept.push(...value.names);
96
+ continue;
97
+ }
98
+ await flush();
99
+ renewed = true;
100
+ await keep(Object.entries(await value.create(person ?? terminalPerson)).map(([name, content]) => ({
101
+ name,
102
+ value: content,
103
+ })), true);
104
+ continue;
105
+ }
106
+ const names = value.fields.map((one) => one.name);
107
+ if (!renewed && pending.length === 0 && names.every((name) => held.has(name))) {
108
+ try {
109
+ const note = await value.check(Object.fromEntries(names.map((name) => [name, held.get(name)])));
110
+ kept.push(...names);
111
+ say(`${names.join(", ")} already set in ${file}${note ? ` (${note})` : ""} — keeping ${names.length > 1 ? "them" : "it"}`);
112
+ continue;
113
+ }
114
+ catch (error) {
115
+ if (!(error instanceof SetupRefused))
116
+ throw error;
117
+ say(`${names.join(", ")} in ${file} is refused (${error.message}) — asking for another`);
118
+ }
119
+ }
120
+ if (value.capture === undefined) {
121
+ pending.push(value);
122
+ continue;
123
+ }
124
+ await flush();
125
+ const captured = await value.capture(held);
126
+ if (captured === undefined) {
127
+ await ask([value]);
128
+ continue;
129
+ }
130
+ await keep([{ name: names[0], value: captured }], true);
131
+ say(`${names[0]} captured and saved to ${file}`);
132
+ }
133
+ await flush();
134
+ if (unprotected !== undefined)
135
+ say(`note: ${unprotectedSecretsNote(unprotected, "where this instance keeps its values")}`);
136
+ return { written, kept, after: await plan.finish(held) };
137
+ }
138
+ const guideOf = (step, held) => typeof step.guide === "function" ? step.guide(held) : step.guide;
139
+ async function askAtTerminal(steps, held, accept) {
140
+ for (const step of steps) {
141
+ const guide = guideOf(step, held);
142
+ clackLog.info(guide.instructions);
143
+ if (guide.url !== undefined) {
144
+ say(`opening ${guide.url}`);
145
+ openExternalUrl(guide.url);
146
+ }
147
+ for (;;) {
148
+ const values = {};
149
+ for (const one of step.fields) {
150
+ for (;;) {
151
+ const message = `${one.label}`;
152
+ const answer = one.secret === false ? await text({ message }) : await password({ message });
153
+ if (isCancel(answer))
154
+ throw new Error("setup cancelled");
155
+ const pasted = String(answer).trim();
156
+ if (pasted === "") {
157
+ clackLog.error(`${one.label} is needed.`);
158
+ continue;
159
+ }
160
+ values[one.name] = pasted;
161
+ break;
162
+ }
163
+ }
164
+ try {
165
+ await accept(step, values);
166
+ break;
167
+ }
168
+ catch (error) {
169
+ if (!(error instanceof SetupRefused))
170
+ throw error;
171
+ clackLog.error(`${error.message}${error.fix === undefined ? "" : ` ${error.fix}`}`);
172
+ }
173
+ }
174
+ }
175
+ }
176
+ async function askOnPage(kind, steps, held, accept, person) {
177
+ const product = PRODUCTS[kind];
178
+ const page = await startCredentialsPage({
179
+ title: `Connect ${product}`,
180
+ doneHeading: `${product} connected`,
181
+ steps: steps.map((step) => ({ label: step.label, guide: guideOf(step, held), fields: step.fields })),
182
+ accept: (index, values) => accept(steps[index], values),
183
+ });
184
+ const port = new URL(page.url).port;
185
+ try {
186
+ person.waitsOn(page.url, `paste ${steps.map((step) => step.label).join(" and ")} on this page. If the CLI runs remotely, forward this ` +
187
+ `loopback port to the same local port (SSH: ssh -N -L ${port}:127.0.0.1:${port} user@host), then open the ` +
188
+ "same URL in your browser. Keep the forwarding session open; never paste secrets into the conversation.");
189
+ await page.done;
190
+ person.carriesOn();
191
+ }
192
+ finally {
193
+ await page.close();
194
+ }
195
+ }
@@ -1,22 +1,14 @@
1
1
  import { type ChannelKind } from "../../scaffold/add-channel.ts";
2
2
  export interface AddChannelOptions {
3
- ingress?: string;
4
- onboard?: boolean;
5
- replaceConfig?: boolean;
6
- environment?: string;
7
- /** false ⇔ `--no-input`. */
8
- input?: boolean;
9
3
  /** `--json`: the outcome as one JSON object on stdout. */
10
4
  json?: boolean;
11
- /** `--check <ticket>`: where an app's setup started without a terminal stands. */
12
- check?: string;
13
- /** `--timeout <seconds>`: how long a setup started without a terminal stays open. */
14
- timeout?: string;
15
- /** `--ticket <id>`: the process that runs a ticket's setup. */
16
- ticket?: string;
17
5
  }
18
- /** `fastagent add <kind> [dir]`: scaffold `src/channels/<kind>.ts`, the adapter configured with a starter policy. */
19
- export declare function runAddChannel(channelKind: ChannelKind, dirArg: string, opts: AddChannelOptions): Promise<void>;
6
+ /**
7
+ * `add <channel>`: the program only (channel-setup.md §2). It writes the channel file and its tools and names the
8
+ * values they read in `.secrets/.env.example`; it asks nothing and creates no app. The app, and the values of the
9
+ * instance that runs it, are `channels setup`'s.
10
+ */
11
+ export declare function runAddChannel(kind: ChannelKind, dirArg: string, opts: AddChannelOptions): Promise<void>;
20
12
  /** `fastagent add skill <source> [dir]`: vendor an Agent Skills skill into <dir>/skills/<name>/. */
21
13
  export declare function runAddSkill(source: string | undefined, dirArg: string, opts: {
22
14
  update?: boolean;