@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
package/dist/slack.d.ts CHANGED
@@ -1,4 +1,4 @@
1
1
  /** `@fastagent-sh/fastagent/slack` — the first-party Slack Events API bot-channel surface. */
2
- export { slackChannel, defaultSlackRoute, slackEnvelope, verifySlackSignature, type SlackChannelOptions, type SlackEventEnvelope, type SlackFile, type SlackMessageEvent, type SlackRendering, type SlackRoute, type SlackFailure, } from "./channels/slack/slack.ts";
2
+ export { slackChannel, slackSocketChannel, defaultSlackRoute, slackEnvelope, verifySlackSignature, type SlackChannelOptions, type SlackSocketChannelOptions, type SlackEventEnvelope, type SlackFile, type SlackMessageEvent, type SlackRendering, type SlackRoute, type SlackFailure, } from "./channels/slack/slack.ts";
3
3
  export { slackThreads, slackTransport, type SlackTransport } from "./channels/slack/shared-api.ts";
4
4
  export type { SentSlackMessage, SlackTarget, UploadedSlackFile } from "./channels/slack/slack-api.ts";
package/dist/slack.js CHANGED
@@ -1,3 +1,3 @@
1
1
  /** `@fastagent-sh/fastagent/slack` — the first-party Slack Events API bot-channel surface. */
2
- export { slackChannel, defaultSlackRoute, slackEnvelope, verifySlackSignature, } from "./channels/slack/slack.js";
2
+ export { slackChannel, slackSocketChannel, defaultSlackRoute, slackEnvelope, verifySlackSignature, } from "./channels/slack/slack.js";
3
3
  export { slackThreads, slackTransport } from "./channels/slack/shared-api.js";
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Text without the characters at its ends that `isEdge` names, found by index. A regex for it (`/^-+|-+$/`, `/\/+$/`)
3
+ * backtracks quadratically on a long run that is not at the end, and what is trimmed here is often a name or a URL
4
+ * somebody else wrote.
5
+ */
6
+ export declare function stripEdges(text: string, isEdge: (char: string) => boolean): string;
7
+ /** `text` without the characters at its end that `isEdge` names. */
8
+ export declare function stripEnd(text: string, isEdge: (char: string) => boolean): string;
9
+ /** A URL or path without the `/` that ends it. */
10
+ export declare function stripTrailingSlashes(text: string): string;
@@ -0,0 +1,22 @@
1
+ /**
2
+ * Text without the characters at its ends that `isEdge` names, found by index. A regex for it (`/^-+|-+$/`, `/\/+$/`)
3
+ * backtracks quadratically on a long run that is not at the end, and what is trimmed here is often a name or a URL
4
+ * somebody else wrote.
5
+ */
6
+ export function stripEdges(text, isEdge) {
7
+ let start = 0;
8
+ while (start < text.length && isEdge(text[start]))
9
+ start++;
10
+ return stripEnd(text.slice(start), isEdge);
11
+ }
12
+ /** `text` without the characters at its end that `isEdge` names. */
13
+ export function stripEnd(text, isEdge) {
14
+ let end = text.length;
15
+ while (end > 0 && isEdge(text[end - 1]))
16
+ end--;
17
+ return text.slice(0, end);
18
+ }
19
+ /** A URL or path without the `/` that ends it. */
20
+ export function stripTrailingSlashes(text) {
21
+ return stripEnd(text, (char) => char === "/");
22
+ }
@@ -1,3 +1,3 @@
1
1
  /** `@fastagent-sh/fastagent/telegram` — the Telegram bot channel subpath export, kept off the root surface. */
2
- export { telegramChannel, defaultTelegramRoute, telegramEnvelope, telegramStop, type TelegramChannelOptions, type TelegramUpdate, type TelegramMessage, type TelegramRoute, type TelegramFailure, } from "./channels/telegram/telegram.ts";
2
+ export { telegramChannel, telegramPollingChannel, defaultTelegramRoute, telegramEnvelope, telegramStop, type TelegramChannelOptions, type TelegramPollingChannelOptions, type TelegramUpdate, type TelegramMessage, type TelegramRoute, type TelegramFailure, } from "./channels/telegram/telegram.ts";
3
3
  export { telegramTransport, type TelegramSendTarget, type TelegramSent, type TelegramTransport, } from "./channels/telegram/shared-api.ts";
package/dist/telegram.js CHANGED
@@ -1,3 +1,3 @@
1
1
  /** `@fastagent-sh/fastagent/telegram` — the Telegram bot channel subpath export, kept off the root surface. */
2
- export { telegramChannel, defaultTelegramRoute, telegramEnvelope, telegramStop, } from "./channels/telegram/telegram.js";
2
+ export { telegramChannel, telegramPollingChannel, defaultTelegramRoute, telegramEnvelope, telegramStop, } from "./channels/telegram/telegram.js";
3
3
  export { telegramTransport, } from "./channels/telegram/shared-api.js";
package/dist/tunnel.d.ts CHANGED
@@ -26,7 +26,6 @@ export declare function startCloudflareTunnel(port: number, spawnFn?: SpawnCloud
26
26
  /** Print the public URL and wire up first-party webhook channels found under `dir` (the agent ROOT). */
27
27
  export declare function announceWebhooks(dir: string, baseUrl: string, channels: readonly DeclaredChannel[], opts?: {
28
28
  openUrl?: (url: string) => void;
29
- stateRoot?: string;
30
29
  env?: NodeJS.ProcessEnv;
31
30
  /** {@link Registrars.leave}. */
32
31
  leave?: (step: string) => void;
package/dist/tunnel.js CHANGED
@@ -5,13 +5,12 @@
5
5
  */
6
6
  import { spawn } from "node:child_process";
7
7
  import { registerFeishuWebhook } from "./channels/feishu/register-webhook.js";
8
- import { registerSlackWebhook } from "./channels/slack/register-webhook.js";
9
8
  import { registerTelegramWebhook } from "./channels/telegram/register-webhook.js";
10
- import { pointChannelsAt, webhookKinds } from "./deploy/channel-ingress.js";
9
+ import { pointChannelsAt } from "./deploy/channel-ingress.js";
10
+ import { webhookKinds } from "./channels/webhooks.js";
11
11
  import { loadDotEnv } from "./env.js";
12
12
  import { dotEnvPath } from "./value-file.js";
13
13
  import { installProxyFetch } from "./proxy.js";
14
- import { AGENT_ENVIRONMENT_ENV, resolveStateRoot } from "./paths.js";
15
14
  import { log } from "./log.js";
16
15
  // `(?!api\.)`: cloudflared's ERROR lines mention its request endpoint (`https://api.trycloudflare.com/tunnel`, e.g.
17
16
  // "failed to request quick Tunnel: Post ... timeout" under a flaky proxy) — without the exclusion a transient error
@@ -157,11 +156,6 @@ export async function announceWebhooks(dir, baseUrl, channels, opts = {}) {
157
156
  registrars: {
158
157
  ...(opts.leave === undefined ? {} : { leave: opts.leave }),
159
158
  telegram: (url) => registerTelegramWebhook(url, { env: opts.env }),
160
- slack: (url) => registerSlackWebhook(url, {
161
- stateRoot: opts.stateRoot ?? resolveStateRoot(dir),
162
- environment: process.env[AGENT_ENVIRONMENT_ENV] === "production" ? "production" : "dev",
163
- log: (message) => log.info(message),
164
- }),
165
159
  feishu: (url, kind) => registerFeishuWebhook(url, kind, feishuOptions),
166
160
  },
167
161
  });
@@ -7,7 +7,9 @@ export declare function parseEnvContent(content: string): Map<string, string>;
7
7
  * is no values, which is normal.
8
8
  */
9
9
  export declare function loadEnvValues(file: string): Map<string, string>;
10
- /** The agent's `.env` file: `<resolved secrets dir>/.env`. */
10
+ /** Use `path` as the agent's value file from here on, or the default again. */
11
+ export declare function useValueFile(path: string | undefined): void;
12
+ /** The agent's `.env` file: the one `--env-file` named, else `<resolved secrets dir>/.env`. */
11
13
  export declare function dotEnvPath(agentDir: string, env?: NodeJS.ProcessEnv): string;
12
14
  /** The committable template: `<agentDir>/.secrets/.env.example`. */
13
15
  export declare function envExamplePath(agentDir: string): string;
@@ -31,11 +33,14 @@ export interface ValueWriteResult {
31
33
  /** Set when the secrets dir is an operator-chosen one (`FASTAGENT_SECRETS_DIR`) carrying no `.gitignore`. */
32
34
  unprotectedSecretsDir?: string;
33
35
  }
34
- /** What a command says when it wrote a secret into a secrets directory fastagent does not own and git may track. */
35
- export declare function unprotectedSecretsNote(dir: string): string;
36
+ /**
37
+ * What a command says when it wrote a secret into a secrets directory fastagent does not own and git may track; `chosenBy`
38
+ * is what put the secrets there (the operator's `FASTAGENT_SECRETS_DIR`, unless the caller knows better).
39
+ */
40
+ export declare function unprotectedSecretsNote(dir: string, chosenBy?: string): string;
36
41
  export declare function prepareSecretsDirectory(dir: string, secretsDir: string): Promise<string | undefined>;
37
42
  /**
38
- * Write `entries` into the selected environment's value file, under a `# --- <section> ---` line. A name with a
39
- * non-empty active value is kept unless `overwrite` names it.
43
+ * Write `entries` into `file` (default: the selected environment's value file), under a `# --- <section> ---` line. A
44
+ * name with a non-empty active value is kept unless `overwrite` names it.
40
45
  */
41
- export declare function writeValues(dir: string, section: string, entries: readonly ValueEntry[], overwrite?: readonly string[]): Promise<ValueWriteResult>;
46
+ export declare function writeValues(dir: string, section: string, entries: readonly ValueEntry[], overwrite?: readonly string[], file?: string): Promise<ValueWriteResult>;
@@ -44,9 +44,19 @@ export function loadEnvValues(file) {
44
44
  throw error;
45
45
  }
46
46
  }
47
- /** The agent's `.env` file: `<resolved secrets dir>/.env`. */
47
+ /**
48
+ * The value file this process's command was told to use (`--env-file`). Process state set at the command's entry, not
49
+ * an environment variable: a variable would be read from a shell, by commands that never took the flag, and by what
50
+ * the agent runs. A `dev` worker is spawned with its supervisor's arguments and takes the flag itself.
51
+ */
52
+ let selectedValueFile;
53
+ /** Use `path` as the agent's value file from here on, or the default again. */
54
+ export function useValueFile(path) {
55
+ selectedValueFile = path;
56
+ }
57
+ /** The agent's `.env` file: the one `--env-file` named, else `<resolved secrets dir>/.env`. */
48
58
  export function dotEnvPath(agentDir, env = process.env) {
49
- return join(resolveSecretsDir(agentDir, env), ".env");
59
+ return selectedValueFile ?? join(resolveSecretsDir(agentDir, env), ".env");
50
60
  }
51
61
  /** The committable template: `<agentDir>/.secrets/.env.example`. */
52
62
  export function envExamplePath(agentDir) {
@@ -64,9 +74,12 @@ export function agentValues(agentDir) {
64
74
  values[key] = value;
65
75
  return values;
66
76
  }
67
- /** What a command says when it wrote a secret into a secrets directory fastagent does not own and git may track. */
68
- export function unprotectedSecretsNote(dir) {
69
- return (`${dir} (FASTAGENT_SECRETS_DIR) now holds a secret and has no .gitignore — that directory is yours, so ` +
77
+ /**
78
+ * What a command says when it wrote a secret into a secrets directory fastagent does not own and git may track; `chosenBy`
79
+ * is what put the secrets there (the operator's `FASTAGENT_SECRETS_DIR`, unless the caller knows better).
80
+ */
81
+ export function unprotectedSecretsNote(dir, chosenBy = "FASTAGENT_SECRETS_DIR") {
82
+ return (`${dir} (${chosenBy}) now holds a secret and has no .gitignore — that directory is yours, so ` +
70
83
  "fastagent will not write one into it. Make sure git does not track what is in there.");
71
84
  }
72
85
  export async function prepareSecretsDirectory(dir, secretsDir) {
@@ -91,11 +104,10 @@ function mentionsEnvName(content, name) {
91
104
  return content.split("\n").some((line) => new RegExp(`^\\s*#?\\s*${name}\\s*=`).test(line));
92
105
  }
93
106
  /**
94
- * Write `entries` into the selected environment's value file, under a `# --- <section> ---` line. A name with a
95
- * non-empty active value is kept unless `overwrite` names it.
107
+ * Write `entries` into `file` (default: the selected environment's value file), under a `# --- <section> ---` line. A
108
+ * name with a non-empty active value is kept unless `overwrite` names it.
96
109
  */
97
- export async function writeValues(dir, section, entries, overwrite = []) {
98
- const file = dotEnvPath(dir);
110
+ export async function writeValues(dir, section, entries, overwrite = [], file = dotEnvPath(dir)) {
99
111
  const unprotectedSecretsDir = await prepareSecretsDirectory(dir, dirname(file));
100
112
  let current = "";
101
113
  try {
package/dist/version.d.ts CHANGED
@@ -3,8 +3,14 @@
3
3
  * scaffolded dependency range (`^${version}`), so `init` must never fall back and scaffold an uninstallable `^0.0.0`.
4
4
  */
5
5
  export declare function fastagentVersion(): Promise<string>;
6
- /** This build's version, and the exact versions of the harness packages it runs, for a new agent to depend on. */
6
+ /** The packages an agent's code imports besides fastagent: the harness its tools and providers are written for. */
7
+ export declare const HARNESS_PACKAGES: string[];
8
+ /**
9
+ * This build's version, the exact versions of the harness packages it runs, and the TypeScript `fastagent build`
10
+ * compiles with (FastAgent's own), for a new agent to depend on.
11
+ */
7
12
  export declare function fastagentBuild(): Promise<{
8
13
  version: string;
9
14
  harness: Record<string, string>;
15
+ typescript: string;
10
16
  }>;
package/dist/version.js CHANGED
@@ -8,8 +8,11 @@ export async function fastagentVersion() {
8
8
  return (await fastagentBuild()).version;
9
9
  }
10
10
  /** The packages an agent's code imports besides fastagent: the harness its tools and providers are written for. */
11
- const HARNESS_PACKAGES = ["@earendil-works/pi-durable", "@earendil-works/pi-ai"];
12
- /** This build's version, and the exact versions of the harness packages it runs, for a new agent to depend on. */
11
+ export const HARNESS_PACKAGES = ["@earendil-works/pi-durable", "@earendil-works/pi-ai", "@earendil-works/pi-schema"];
12
+ /**
13
+ * This build's version, the exact versions of the harness packages it runs, and the TypeScript `fastagent build`
14
+ * compiles with (FastAgent's own), for a new agent to depend on.
15
+ */
13
16
  export async function fastagentBuild() {
14
17
  const pkgPath = fileURLToPath(new URL("../package.json", import.meta.url));
15
18
  const pkg = JSON.parse(await readFile(pkgPath, "utf8"));
@@ -22,5 +25,8 @@ export async function fastagentBuild() {
22
25
  throw new Error(`${pkgPath} does not depend on ${name}`);
23
26
  harness[name] = range;
24
27
  }
25
- return { version: pkg.version, harness };
28
+ const typescript = pkg.devDependencies?.typescript;
29
+ if (typescript === undefined)
30
+ throw new Error(`${pkgPath} does not develop with typescript`);
31
+ return { version: pkg.version, harness, typescript };
26
32
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fastagent-sh/fastagent",
3
- "version": "0.29.0",
3
+ "version": "0.30.0",
4
4
  "description": "Vibe first. Then FastAgent: turn a local agent directory into a live service in your app, on Telegram, Slack, Feishu, or behind any channel.",
5
5
  "keywords": [
6
6
  "agent",
@@ -88,9 +88,10 @@
88
88
  },
89
89
  "dependencies": {
90
90
  "@clack/prompts": "^1.6.0",
91
- "@earendil-works/chord": "1.4.0",
92
- "@earendil-works/pi-ai": "1.4.0",
93
- "@earendil-works/pi-durable": "1.4.0",
91
+ "@earendil-works/chord": "1.5.0",
92
+ "@earendil-works/pi-ai": "1.5.0",
93
+ "@earendil-works/pi-durable": "1.5.0",
94
+ "@earendil-works/pi-schema": "1.5.0",
94
95
  "@effect/platform-bun": "^4.0.7",
95
96
  "@effect/platform-node": "^4.0.7",
96
97
  "@larksuiteoapi/node-sdk": "^1.76.0",
@@ -108,7 +109,7 @@
108
109
  },
109
110
  "devDependencies": {
110
111
  "@biomejs/biome": "^2.6.4",
111
- "@earendil-works/pi-coding-agent": "1.4.0",
112
+ "@earendil-works/pi-coding-agent": "1.5.0",
112
113
  "@hono/node-server": "^2.1.5",
113
114
  "@modelcontextprotocol/server": "2.4.0",
114
115
  "@types/express": "^5.0.6",
@@ -1,25 +0,0 @@
1
- export interface LarkCredentials {
2
- appId: string;
3
- appSecret: string;
4
- }
5
- export declare class LarkCredentialsRefused extends Error {
6
- readonly fix: string | undefined;
7
- readonly why: string | undefined;
8
- constructor(headline: string, options?: {
9
- fix?: string;
10
- why?: string;
11
- });
12
- }
13
- export interface LarkCredentialsPage<T = LarkCredentials> {
14
- url: string;
15
- credentials: Promise<T>;
16
- close(): Promise<void>;
17
- }
18
- export declare function startLarkCredentialsPage(input: {
19
- consoleUrl: string;
20
- check(credentials: LarkCredentials): Promise<void>;
21
- }): Promise<LarkCredentialsPage>;
22
- export declare function startLarkVerificationPage(input: {
23
- consoleUrl: string;
24
- save(token: string): Promise<void>;
25
- }): Promise<LarkCredentialsPage<string>>;
@@ -1,206 +0,0 @@
1
- /** Loopback-only, single-use forms for Lark setup secrets; values never pass through the agent's conversation. */
2
- import { randomBytes } from "node:crypto";
3
- import { createServer } from "node:http";
4
- import { field, formCard, openButton, privacy, prose, refusal, sendPage, setupRequestUrl, statusCard, step, submit, } from "../../setup-page.js";
5
- export class LarkCredentialsRefused extends Error {
6
- fix;
7
- why;
8
- constructor(headline, options = {}) {
9
- super(headline);
10
- this.name = "LarkCredentialsRefused";
11
- this.fix = options.fix;
12
- this.why = options.why;
13
- }
14
- }
15
- function readBody(request) {
16
- return new Promise((resolve) => {
17
- let body = "";
18
- let tooLarge = false;
19
- request.on("data", (chunk) => {
20
- if (tooLarge)
21
- return;
22
- body += String(chunk);
23
- if (Buffer.byteLength(body) > 16 * 1024)
24
- tooLarge = true;
25
- });
26
- request.on("end", () => resolve(tooLarge ? undefined : body));
27
- request.on("error", () => resolve(undefined));
28
- });
29
- }
30
- export function startLarkCredentialsPage(input) {
31
- return startPage({
32
- heading: "Lark credentials verified",
33
- form: (pasted, problem) => formCard("Connect Lark", [
34
- step(1, "Create the app in Lark", prose("It opens with the permissions and event your agent needs already set.") +
35
- openButton("Open Lark", input.consoleUrl)),
36
- step(2, "Paste its credentials", (problem
37
- ? refusal(problem.message, problem.fix, problem.why)
38
- : prose("Find them under <b>Credentials &amp; Basic Info</b>.")) +
39
- field({
40
- id: "app_id",
41
- label: "App ID",
42
- placeholder: "cli_…",
43
- value: pasted.get("app_id") ?? "",
44
- invalid: !!problem,
45
- }) +
46
- field({ id: "app_secret", label: "App Secret", secret: true, invalid: !!problem, autofocus: !!problem }) +
47
- submit("Save and check") +
48
- privacy()),
49
- ].join("")),
50
- accept: async (pasted) => {
51
- const pair = { appId: (pasted.get("app_id") ?? "").trim(), appSecret: (pasted.get("app_secret") ?? "").trim() };
52
- if (!pair.appId || !pair.appSecret) {
53
- throw new LarkCredentialsRefused("Both values are needed.", {
54
- fix: "Copy the App ID and the App Secret from Credentials & Basic Info.",
55
- });
56
- }
57
- await input.check(pair);
58
- return pair;
59
- },
60
- });
61
- }
62
- export function startLarkVerificationPage(input) {
63
- return startPage({
64
- heading: "Verification Token saved",
65
- form: (_pasted, problem) => formCard("Finish Lark webhook setup", [
66
- step(1, "Copy the Verification Token", prose("Find it under <b>Events &amp; Callbacks</b> → Encryption Strategy. Choose webhook mode when setting the Request URL.") + openButton("Open Lark", input.consoleUrl)),
67
- step(2, "Paste the token here", (problem ? refusal(problem.message, problem.fix) : "") +
68
- field({
69
- id: "verification_token",
70
- label: "Verification Token",
71
- secret: true,
72
- invalid: !!problem,
73
- autofocus: !!problem,
74
- }) +
75
- submit("Save token") +
76
- privacy()),
77
- ].join("")),
78
- accept: async (pasted) => {
79
- const token = (pasted.get("verification_token") ?? "").trim();
80
- if (!token)
81
- throw new LarkCredentialsRefused("Verification Token is required.");
82
- await input.save(token);
83
- return token;
84
- },
85
- });
86
- }
87
- // A browser can leave during validation, before either response event can be subscribed to.
88
- function afterResponse(response, complete) {
89
- if (response.destroyed || response.writableFinished)
90
- complete();
91
- else
92
- response.once("close", complete);
93
- }
94
- async function startPage(input) {
95
- const path = `/lark/credentials/${randomBytes(24).toString("hex")}`;
96
- const { promise: credentials, resolve, reject } = Promise.withResolvers();
97
- let checking = false;
98
- let used = false;
99
- let port = 0;
100
- const server = createServer(async (request, response) => {
101
- const url = setupRequestUrl(request, response);
102
- if (url === undefined)
103
- return;
104
- const host = request.headers.host ?? "";
105
- if (host !== `127.0.0.1:${port}` && host !== `localhost:${port}`) {
106
- response.writeHead(421).end("misdirected request\n");
107
- return;
108
- }
109
- if (url.pathname !== path || (request.method !== "GET" && request.method !== "POST")) {
110
- response.writeHead(404).end("not found\n");
111
- return;
112
- }
113
- const expired = () => sendPage(response, 404, {
114
- title: "FastAgent setup",
115
- card: statusCard({
116
- state: "failed",
117
- heading: "This page is no longer in use",
118
- next: "Go back to your coding agent.",
119
- }),
120
- });
121
- if (used) {
122
- expired();
123
- return;
124
- }
125
- if (request.method === "GET") {
126
- sendPage(response, 200, { title: "Connect Lark — FastAgent", card: input.form(new URLSearchParams()) });
127
- return;
128
- }
129
- const site = request.headers["sec-fetch-site"];
130
- if (site !== undefined && site !== "same-origin") {
131
- response.writeHead(403).end("forbidden\n");
132
- return;
133
- }
134
- const body = await readBody(request);
135
- if (body === undefined) {
136
- response.writeHead(413).end("payload too large\n");
137
- return;
138
- }
139
- if (used) {
140
- expired();
141
- return;
142
- }
143
- if (checking) {
144
- response.writeHead(409).end("another submission is being checked\n");
145
- return;
146
- }
147
- checking = true;
148
- const pasted = new URLSearchParams(body);
149
- let accepted;
150
- try {
151
- accepted = await input.accept(pasted);
152
- }
153
- catch (error) {
154
- checking = false;
155
- // Only explicit input refusals stay on the form. Infrastructure and persistence errors fail the ticket.
156
- if (error instanceof LarkCredentialsRefused) {
157
- sendPage(response, 200, { title: "Connect Lark — FastAgent", card: input.form(pasted, error) });
158
- return;
159
- }
160
- used = true;
161
- afterResponse(response, () => reject(error));
162
- sendPage(response, 200, {
163
- title: "Setup stopped — FastAgent",
164
- card: statusCard({
165
- state: "failed",
166
- heading: "Setup stopped",
167
- lede: error instanceof Error ? error.message : String(error),
168
- next: "Go back to your coding agent.",
169
- }),
170
- });
171
- return;
172
- }
173
- used = true;
174
- afterResponse(response, () => resolve(accepted));
175
- sendPage(response, 200, {
176
- title: `${input.heading} — FastAgent`,
177
- card: statusCard({
178
- state: "done",
179
- heading: input.heading,
180
- lede: "Your coding agent will say if anything is left to do in Lark.",
181
- next: "Go back to your coding agent.",
182
- }),
183
- });
184
- });
185
- await new Promise((resolve, reject) => {
186
- server.once("error", reject);
187
- server.listen(0, "127.0.0.1", () => {
188
- server.off("error", reject);
189
- resolve();
190
- });
191
- });
192
- const address = server.address();
193
- if (!address || typeof address === "string") {
194
- server.close();
195
- throw new Error("the Lark credentials page did not receive a local TCP port");
196
- }
197
- port = address.port;
198
- return {
199
- url: `http://127.0.0.1:${port}${path}`,
200
- credentials,
201
- close: () => new Promise((resolve) => {
202
- server.close(() => resolve());
203
- server.closeAllConnections();
204
- }),
205
- };
206
- }
@@ -1,52 +0,0 @@
1
- import { type FeishuSubscriptionMode } from "../feishu/setup-mode.ts";
2
- /** Guided Lark-international onboarding. */
3
- /**
4
- * Lark's one-click launcher, where the person creates the app. The launcher is unbound (the bound confirm page is broken
5
- * on the international cloud), but it merges the `addons` query parameter onto the app it creates, as Feishu's does: the
6
- * scopes and the event the channel needs arrive with the app, so nothing is ticked in the console afterwards. A tenant
7
- * may still withhold a scope; the permission check that follows creation names it.
8
- */
9
- export declare function larkConsoleUrl(ingress: FeishuSubscriptionMode): string;
10
- /** Directly where the fallback token lives (and where the successful automatic mode change is visible). */
11
- export declare function larkEventSecurityUrl(appId: string): string;
12
- export interface LarkOnboardIO {
13
- /** A page for the person, and what they do there. */
14
- openUrl(url: string, action: string): void;
15
- note(message: string): void;
16
- prompt(message: string, opts?: {
17
- hidden?: boolean;
18
- }): Promise<string | undefined>;
19
- verificationToken?(consoleUrl: string): Promise<string>;
20
- collect?(input: {
21
- /** Lark's launcher, with what the channel needs set on the app it creates. */
22
- consoleUrl: string;
23
- check(appId: string, appSecret: string): Promise<void>;
24
- }): Promise<{
25
- appId: string;
26
- appSecret: string;
27
- }>;
28
- }
29
- interface LarkBootstrapResult {
30
- /** Challenge-captured token: the PATCH also switched Subscription mode to webhook. */
31
- token?: string;
32
- /** Present only for a definitive config-route 404; tells the user why the manual path is active. */
33
- manualReason?: string;
34
- }
35
- export interface LarkOnboardOptions {
36
- /** Existing active .env values. */
37
- existing?: Readonly<Record<string, string | undefined>>;
38
- ingress?: FeishuSubscriptionMode;
39
- verifyCredentials(appId: string, appSecret: string): Promise<void>;
40
- keep?(appId: string, appSecret: string): Promise<void>;
41
- bootstrapWebhook?(appId: string, appSecret: string): Promise<LarkBootstrapResult>;
42
- /** The command that continues an interrupted setup: the one that started it (`add lark`, `deploy <host> --run`). */
43
- rerun?: string;
44
- }
45
- export interface LarkOnboardCredentials {
46
- LARK_APP_ID: string;
47
- LARK_APP_SECRET: string;
48
- LARK_VERIFICATION_TOKEN?: string;
49
- }
50
- /** Open the stable app console and collect everything the runtime needs. */
51
- export declare function onboardLarkApp(io: LarkOnboardIO, opts: LarkOnboardOptions): Promise<LarkOnboardCredentials>;
52
- export {};
@@ -1,86 +0,0 @@
1
- import { encodeAddons } from "../feishu/register-app.js";
2
- import { feishuAppAddons } from "../feishu/setup-mode.js";
3
- /** Guided Lark-international onboarding. */
4
- /**
5
- * Lark's one-click launcher, where the person creates the app. The launcher is unbound (the bound confirm page is broken
6
- * on the international cloud), but it merges the `addons` query parameter onto the app it creates, as Feishu's does: the
7
- * scopes and the event the channel needs arrive with the app, so nothing is ticked in the console afterwards. A tenant
8
- * may still withhold a scope; the permission check that follows creation names it.
9
- */
10
- export function larkConsoleUrl(ingress) {
11
- const url = new URL("https://open.larksuite.com/page/launcher");
12
- url.searchParams.set("from", "backend_oneclick");
13
- url.searchParams.set("addons", encodeAddons(feishuAppAddons(ingress)));
14
- return url.toString();
15
- }
16
- /** Directly where the fallback token lives (and where the successful automatic mode change is visible). */
17
- export function larkEventSecurityUrl(appId) {
18
- return `https://open.larksuite.com/app/${encodeURIComponent(appId)}/event?tab=safe`;
19
- }
20
- function required(value, name, rerun) {
21
- const trimmed = value?.trim();
22
- if (!trimmed)
23
- throw new Error(`${name} is required — re-run \`${rerun}\` to continue setup`);
24
- return trimmed;
25
- }
26
- /** Open the stable app console and collect everything the runtime needs. */
27
- export async function onboardLarkApp(io, opts) {
28
- const rerun = opts.rerun ?? "fastagent add lark";
29
- const existingId = opts.existing?.LARK_APP_ID?.trim();
30
- const existingSecret = opts.existing?.LARK_APP_SECRET?.trim();
31
- const reuseExistingApp = Boolean(existingId && existingSecret);
32
- // Credentials are one App-scoped set.
33
- // No ingress named is the webhook path below (a deploy's production app).
34
- const consoleUrl = larkConsoleUrl(opts.ingress ?? "webhook");
35
- let appId;
36
- let appSecret;
37
- const verify = async (appId, appSecret) => {
38
- await opts.verifyCredentials(appId, appSecret);
39
- if (!reuseExistingApp)
40
- await opts.keep?.(appId, appSecret);
41
- };
42
- if (reuseExistingApp) {
43
- io.note(`Reusing Lark app ${existingId}.`);
44
- appId = required(existingId, "LARK_APP_ID", rerun);
45
- appSecret = required(existingSecret, "LARK_APP_SECRET", rerun);
46
- await verify(appId, appSecret);
47
- }
48
- else if (io.collect) {
49
- ({ appId, appSecret } = await io.collect({ consoleUrl, check: verify }));
50
- }
51
- else {
52
- io.openUrl(consoleUrl, "create the app on Lark's one-click launcher; LARK_APP_ID and LARK_APP_SECRET are under Credentials & Basic Info");
53
- appId = required(await io.prompt("LARK_APP_ID (Credentials & Basic Info)", { hidden: false }), "LARK_APP_ID", rerun);
54
- appSecret = required(await io.prompt("LARK_APP_SECRET (Credentials & Basic Info)", { hidden: true }), "LARK_APP_SECRET", rerun);
55
- await verify(appId, appSecret);
56
- }
57
- const eventSecurityUrl = larkEventSecurityUrl(appId);
58
- io.note("App ID / Secret verified.");
59
- if (opts.ingress === "websocket") {
60
- io.openUrl(eventSecurityUrl, "in Events & Callbacks, choose long connection and subscribe im.message.receive_v1, but do not publish yet: " +
61
- "the permission check follows (no Verification Token or Request URL is needed)");
62
- return { LARK_APP_ID: appId, LARK_APP_SECRET: appSecret };
63
- }
64
- io.note("Trying automatic webhook-mode + Verification-Token bootstrap…");
65
- if (!opts.bootstrapWebhook)
66
- throw new Error("Lark webhook onboarding requires bootstrapWebhook");
67
- const bootstrap = await opts.bootstrapWebhook(appId, appSecret);
68
- if (bootstrap.token) {
69
- io.note("Verification Token captured; Subscription mode changed to webhook in the app draft.");
70
- }
71
- else {
72
- io.note(bootstrap.manualReason ?? "Automatic bootstrap unavailable.");
73
- io.openUrl(eventSecurityUrl, "in Events & Callbacks → Encryption Strategy, copy the Verification Token, and switch Subscription mode to " +
74
- "webhook when setting the Request URL");
75
- }
76
- const verificationToken = required(bootstrap.token ||
77
- (reuseExistingApp ? opts.existing?.LARK_VERIFICATION_TOKEN?.trim() : undefined) ||
78
- (io.verificationToken
79
- ? await io.verificationToken(eventSecurityUrl)
80
- : await io.prompt("LARK_VERIFICATION_TOKEN (Events & Callbacks → Encryption Strategy)", { hidden: true })), "LARK_VERIFICATION_TOKEN", rerun);
81
- return {
82
- LARK_APP_ID: appId,
83
- LARK_APP_SECRET: appSecret,
84
- LARK_VERIFICATION_TOKEN: verificationToken,
85
- };
86
- }