@fastagent-sh/fastagent 0.21.0 → 0.22.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 (388) hide show
  1. package/README.md +9 -12
  2. package/dist/agent.d.ts +24 -37
  3. package/dist/agent.js +7 -15
  4. package/dist/atomic-write.d.ts +13 -17
  5. package/dist/atomic-write.js +18 -29
  6. package/dist/bind.d.ts +12 -22
  7. package/dist/bind.js +18 -27
  8. package/dist/channel.d.ts +11 -33
  9. package/dist/channels/agentcore-limits.d.ts +2 -13
  10. package/dist/channels/agentcore-limits.js +2 -13
  11. package/dist/channels/agentcore-protocol.d.ts +67 -58
  12. package/dist/channels/agentcore-protocol.js +23 -15
  13. package/dist/channels/agentcore-service.d.ts +19 -33
  14. package/dist/channels/agentcore-service.js +176 -59
  15. package/dist/channels/agentcore.d.ts +39 -33
  16. package/dist/channels/agentcore.js +90 -224
  17. package/dist/channels/body.d.ts +15 -2
  18. package/dist/channels/body.js +24 -2
  19. package/dist/channels/busy.d.ts +1 -21
  20. package/dist/channels/busy.js +2 -39
  21. package/dist/channels/control.d.ts +17 -62
  22. package/dist/channels/control.js +74 -182
  23. package/dist/channels/define-channel.d.ts +39 -0
  24. package/dist/channels/define-channel.js +9 -0
  25. package/dist/channels/discover.d.ts +10 -31
  26. package/dist/channels/discover.js +37 -31
  27. package/dist/channels/feishu/bootstrap-token.d.ts +1 -8
  28. package/dist/channels/feishu/bootstrap-token.js +7 -23
  29. package/dist/channels/feishu/card.d.ts +14 -28
  30. package/dist/channels/feishu/card.js +16 -30
  31. package/dist/channels/feishu/cloud.d.ts +1 -5
  32. package/dist/channels/feishu/context-buffer.d.ts +4 -23
  33. package/dist/channels/feishu/context-buffer.js +8 -31
  34. package/dist/channels/feishu/crypto.d.ts +10 -5
  35. package/dist/channels/feishu/crypto.js +12 -19
  36. package/dist/channels/feishu/feishu-api.d.ts +2 -1
  37. package/dist/channels/feishu/feishu-api.js +5 -4
  38. package/dist/channels/feishu/feishu.d.ts +4 -10
  39. package/dist/channels/feishu/feishu.js +113 -227
  40. package/dist/channels/feishu/invoke-turn.d.ts +5 -4
  41. package/dist/channels/feishu/invoke-turn.js +36 -54
  42. package/dist/channels/feishu/model.d.ts +4 -21
  43. package/dist/channels/feishu/model.js +1 -8
  44. package/dist/channels/feishu/normalize.d.ts +2 -10
  45. package/dist/channels/feishu/normalize.js +7 -42
  46. package/dist/channels/feishu/parse.d.ts +9 -29
  47. package/dist/channels/feishu/parse.js +8 -26
  48. package/dist/channels/feishu/preview.d.ts +12 -14
  49. package/dist/channels/feishu/preview.js +108 -207
  50. package/dist/channels/feishu/register-app.d.ts +8 -23
  51. package/dist/channels/feishu/register-app.js +7 -20
  52. package/dist/channels/feishu/register-webhook.d.ts +2 -9
  53. package/dist/channels/feishu/register-webhook.js +9 -33
  54. package/dist/channels/feishu/scaffold/channel.ts +27 -19
  55. package/dist/channels/feishu/scaffold/channel.websocket.ts +31 -0
  56. package/dist/channels/feishu/setup-mode.d.ts +11 -36
  57. package/dist/channels/feishu/setup-mode.js +7 -26
  58. package/dist/channels/feishu/ws-ingress.d.ts +1 -2
  59. package/dist/channels/feishu/ws-ingress.js +6 -8
  60. package/dist/channels/http.d.ts +15 -15
  61. package/dist/channels/http.js +19 -9
  62. package/dist/channels/kit/attachment-path.d.ts +4 -5
  63. package/dist/channels/kit/attachment-path.js +10 -34
  64. package/dist/channels/kit/context-buffer.d.ts +14 -21
  65. package/dist/channels/kit/context-buffer.js +25 -38
  66. package/dist/channels/kit/delivery.d.ts +35 -0
  67. package/dist/channels/kit/delivery.js +129 -0
  68. package/dist/channels/kit/event-stream.d.ts +5 -0
  69. package/dist/channels/kit/event-stream.js +29 -0
  70. package/dist/channels/kit/invoke-turn-kit.d.ts +53 -26
  71. package/dist/channels/kit/invoke-turn-kit.js +89 -32
  72. package/dist/channels/kit/preview-kit.d.ts +18 -72
  73. package/dist/channels/kit/preview-kit.js +22 -88
  74. package/dist/channels/kit/signature.d.ts +3 -9
  75. package/dist/channels/kit/signature.js +3 -9
  76. package/dist/channels/kit/state.d.ts +1 -2
  77. package/dist/channels/kit/state.js +5 -14
  78. package/dist/channels/kit/stop-command.d.ts +1 -2
  79. package/dist/channels/kit/stop-command.js +6 -9
  80. package/dist/channels/kit/tasks.d.ts +1 -1
  81. package/dist/channels/kit/tasks.js +8 -21
  82. package/dist/channels/kit/text.d.ts +2 -16
  83. package/dist/channels/kit/text.js +23 -31
  84. package/dist/channels/kit/thread-participants.d.ts +4 -17
  85. package/dist/channels/kit/thread-participants.js +8 -55
  86. package/dist/channels/kit/transport.d.ts +26 -0
  87. package/dist/channels/kit/transport.js +20 -0
  88. package/dist/channels/kit/turn-queue.d.ts +7 -11
  89. package/dist/channels/kit/turn-queue.js +21 -39
  90. package/dist/channels/kit/turn-runner.d.ts +32 -18
  91. package/dist/channels/kit/turn-runner.js +98 -53
  92. package/dist/channels/kit/turn-store.d.ts +32 -32
  93. package/dist/channels/kit/turn-store.js +45 -88
  94. package/dist/channels/lark/lark.d.ts +1 -5
  95. package/dist/channels/lark/onboard.d.ts +3 -12
  96. package/dist/channels/lark/onboard.js +3 -13
  97. package/dist/channels/lark/scaffold/channel.ts +27 -19
  98. package/dist/channels/lark/scaffold/channel.websocket.ts +31 -0
  99. package/dist/channels/registration.d.ts +9 -35
  100. package/dist/channels/registration.js +6 -37
  101. package/dist/channels/secret.js +2 -5
  102. package/dist/channels/serve.d.ts +47 -44
  103. package/dist/channels/serve.js +165 -101
  104. package/dist/channels/slack/config-api.d.ts +4 -27
  105. package/dist/channels/slack/config-api.js +5 -34
  106. package/dist/channels/slack/context-buffer.d.ts +2 -3
  107. package/dist/channels/slack/context-buffer.js +5 -5
  108. package/dist/channels/slack/invoke-turn.d.ts +3 -1
  109. package/dist/channels/slack/invoke-turn.js +25 -34
  110. package/dist/channels/slack/manifest.js +1 -3
  111. package/dist/channels/slack/model.d.ts +1 -2
  112. package/dist/channels/slack/model.js +1 -1
  113. package/dist/channels/slack/onboard.d.ts +4 -3
  114. package/dist/channels/slack/onboard.js +8 -11
  115. package/dist/channels/slack/onboarding-state.d.ts +3 -5
  116. package/dist/channels/slack/onboarding-state.js +9 -6
  117. package/dist/channels/slack/parse.d.ts +4 -13
  118. package/dist/channels/slack/parse.js +7 -28
  119. package/dist/channels/slack/preview.d.ts +12 -2
  120. package/dist/channels/slack/preview.js +292 -269
  121. package/dist/channels/slack/reaction.d.ts +11 -3
  122. package/dist/channels/slack/reaction.js +19 -8
  123. package/dist/channels/slack/register-webhook.js +4 -7
  124. package/dist/channels/slack/scaffold/channel.ts +20 -13
  125. package/dist/channels/slack/scaffold/slack-send.ts +2 -2
  126. package/dist/channels/slack/setup-server.d.ts +1 -5
  127. package/dist/channels/slack/setup-server.js +1 -5
  128. package/dist/channels/slack/shared-api.d.ts +1 -4
  129. package/dist/channels/slack/shared-api.js +2 -13
  130. package/dist/channels/slack/slack-api.d.ts +2 -1
  131. package/dist/channels/slack/slack-api.js +6 -4
  132. package/dist/channels/slack/slack.d.ts +11 -14
  133. package/dist/channels/slack/slack.js +121 -122
  134. package/dist/channels/sse.d.ts +1 -1
  135. package/dist/channels/sse.js +1 -1
  136. package/dist/channels/telegram/context-buffer.d.ts +7 -22
  137. package/dist/channels/telegram/context-buffer.js +6 -23
  138. package/dist/channels/telegram/invoke-turn.d.ts +7 -7
  139. package/dist/channels/telegram/invoke-turn.js +24 -43
  140. package/dist/channels/telegram/parse.d.ts +24 -51
  141. package/dist/channels/telegram/parse.js +32 -84
  142. package/dist/channels/telegram/preview.d.ts +10 -1
  143. package/dist/channels/telegram/preview.js +79 -100
  144. package/dist/channels/telegram/register-webhook.js +5 -23
  145. package/dist/channels/telegram/scaffold/channel.ts +25 -18
  146. package/dist/channels/telegram/scaffold/telegram-send.ts +6 -4
  147. package/dist/channels/telegram/telegram-api.d.ts +4 -3
  148. package/dist/channels/telegram/telegram-api.js +9 -7
  149. package/dist/channels/telegram/telegram.d.ts +4 -18
  150. package/dist/channels/telegram/telegram.js +38 -94
  151. package/dist/channels/telegram/turn-store.d.ts +11 -14
  152. package/dist/channels/telegram/turn-store.js +8 -6
  153. package/dist/channels/wait-health.d.ts +4 -6
  154. package/dist/channels/wait-health.js +10 -14
  155. package/dist/cli/add-feishu.d.ts +2 -12
  156. package/dist/cli/add-feishu.js +24 -71
  157. package/dist/cli/add-slack.d.ts +5 -3
  158. package/dist/cli/add-slack.js +53 -76
  159. package/dist/cli/auth-view.d.ts +2 -16
  160. package/dist/cli/auth-view.js +2 -16
  161. package/dist/cli/commands/add.js +25 -43
  162. package/dist/cli/commands/chat.d.ts +0 -1
  163. package/dist/cli/commands/chat.js +5 -6
  164. package/dist/cli/commands/deploy/agentcore.js +60 -65
  165. package/dist/cli/commands/deploy/docker.js +50 -23
  166. package/dist/cli/commands/deploy/fly.js +23 -61
  167. package/dist/cli/commands/deploy/railway.js +23 -18
  168. package/dist/cli/commands/deploy/shared.d.ts +38 -57
  169. package/dist/cli/commands/deploy/shared.js +40 -59
  170. package/dist/cli/commands/deploy.d.ts +10 -27
  171. package/dist/cli/commands/deploy.js +38 -60
  172. package/dist/cli/commands/destroy.d.ts +4 -0
  173. package/dist/cli/commands/destroy.js +56 -0
  174. package/dist/cli/commands/dev.d.ts +2 -1
  175. package/dist/cli/commands/dev.js +20 -23
  176. package/dist/cli/commands/info.d.ts +0 -2
  177. package/dist/cli/commands/info.js +83 -49
  178. package/dist/cli/commands/init.d.ts +1 -2
  179. package/dist/cli/commands/init.js +16 -55
  180. package/dist/cli/commands/invoke.d.ts +0 -1
  181. package/dist/cli/commands/invoke.js +5 -7
  182. package/dist/cli/commands/login.d.ts +2 -1
  183. package/dist/cli/commands/login.js +33 -38
  184. package/dist/cli/commands/logs.js +3 -4
  185. package/dist/cli/commands/models.js +1 -5
  186. package/dist/cli/commands/routine-run.d.ts +6 -0
  187. package/dist/cli/commands/routine-run.js +47 -0
  188. package/dist/cli/commands/routine.d.ts +25 -0
  189. package/dist/cli/commands/routine.js +122 -0
  190. package/dist/cli/commands/start.d.ts +31 -3
  191. package/dist/cli/commands/start.js +131 -81
  192. package/dist/cli/commands/tool.js +26 -12
  193. package/dist/cli/fail.d.ts +13 -23
  194. package/dist/cli/fail.js +20 -23
  195. package/dist/cli/invoke-stream.d.ts +1 -8
  196. package/dist/cli/kernel.d.ts +6 -38
  197. package/dist/cli/kernel.js +14 -77
  198. package/dist/cli/models-view.d.ts +1 -9
  199. package/dist/cli/models-view.js +11 -22
  200. package/dist/cli/program.d.ts +5 -6
  201. package/dist/cli/program.js +120 -157
  202. package/dist/cli/serve.d.ts +57 -41
  203. package/dist/cli/serve.js +94 -121
  204. package/dist/cli/shared.d.ts +15 -61
  205. package/dist/cli/shared.js +55 -181
  206. package/dist/cli.js +1 -8
  207. package/dist/collect.d.ts +6 -0
  208. package/dist/collect.js +6 -0
  209. package/dist/core.d.ts +4 -1
  210. package/dist/core.js +9 -7
  211. package/dist/declared-secrets.d.ts +55 -0
  212. package/dist/declared-secrets.js +88 -0
  213. package/dist/deploy/agentcore/aws-cli.d.ts +91 -0
  214. package/dist/deploy/agentcore/aws-cli.js +64 -0
  215. package/dist/deploy/agentcore/destroy.d.ts +51 -0
  216. package/dist/deploy/agentcore/destroy.js +273 -0
  217. package/dist/deploy/agentcore/forwarder.js +28 -79
  218. package/dist/deploy/agentcore/logs.d.ts +2 -18
  219. package/dist/deploy/agentcore/logs.js +27 -51
  220. package/dist/deploy/agentcore/plan.d.ts +85 -105
  221. package/dist/deploy/agentcore/plan.js +152 -314
  222. package/dist/deploy/agentcore/run.d.ts +28 -28
  223. package/dist/deploy/agentcore/run.js +227 -306
  224. package/dist/deploy/agentcore/zip.d.ts +2 -15
  225. package/dist/deploy/agentcore/zip.js +3 -17
  226. package/dist/deploy/build-context.d.ts +21 -0
  227. package/dist/deploy/build-context.js +137 -0
  228. package/dist/deploy/channel-ingress.d.ts +26 -39
  229. package/dist/deploy/channel-ingress.js +57 -28
  230. package/dist/deploy/container.d.ts +20 -34
  231. package/dist/deploy/container.js +49 -100
  232. package/dist/deploy/docker/plan.d.ts +12 -11
  233. package/dist/deploy/docker/plan.js +43 -24
  234. package/dist/deploy/docker/run.d.ts +22 -25
  235. package/dist/deploy/docker/run.js +39 -37
  236. package/dist/deploy/fly/plan.d.ts +15 -44
  237. package/dist/deploy/fly/plan.js +41 -73
  238. package/dist/deploy/fly/run.d.ts +16 -51
  239. package/dist/deploy/fly/run.js +42 -95
  240. package/dist/deploy/hosts.d.ts +24 -3
  241. package/dist/deploy/hosts.js +24 -3
  242. package/dist/deploy/preflight.d.ts +47 -41
  243. package/dist/deploy/preflight.js +282 -383
  244. package/dist/deploy/railway/plan.d.ts +16 -42
  245. package/dist/deploy/railway/plan.js +42 -60
  246. package/dist/deploy/railway/run.d.ts +30 -68
  247. package/dist/deploy/railway/run.js +86 -100
  248. package/dist/deploy/registration-gate.d.ts +6 -13
  249. package/dist/deploy/residency.d.ts +37 -0
  250. package/dist/deploy/residency.js +24 -0
  251. package/dist/deploy/runner.d.ts +10 -9
  252. package/dist/deploy/runner.js +7 -18
  253. package/dist/deploy/secrets.d.ts +40 -41
  254. package/dist/deploy/secrets.js +117 -103
  255. package/dist/deploy/workspace.d.ts +50 -0
  256. package/dist/deploy/workspace.js +197 -0
  257. package/dist/dev-supervisor.d.ts +1 -9
  258. package/dist/dev-supervisor.js +21 -64
  259. package/dist/effect-port.d.ts +50 -0
  260. package/dist/effect-port.js +77 -0
  261. package/dist/engines/pi/agent-session-factory.d.ts +50 -69
  262. package/dist/engines/pi/agent-session-factory.js +249 -146
  263. package/dist/engines/pi/auth.d.ts +15 -11
  264. package/dist/engines/pi/auth.js +96 -99
  265. package/dist/engines/pi/chat.d.ts +1 -5
  266. package/dist/engines/pi/chat.js +2 -14
  267. package/dist/engines/pi/config.d.ts +53 -51
  268. package/dist/engines/pi/config.js +83 -92
  269. package/dist/engines/pi/create.d.ts +60 -75
  270. package/dist/engines/pi/create.js +108 -141
  271. package/dist/engines/pi/definition.d.ts +14 -58
  272. package/dist/engines/pi/definition.js +21 -85
  273. package/dist/engines/pi/invoke-session.d.ts +5 -25
  274. package/dist/engines/pi/invoke-session.js +209 -244
  275. package/dist/engines/pi/login.d.ts +95 -35
  276. package/dist/engines/pi/login.js +259 -107
  277. package/dist/engines/pi/machine.d.ts +31 -0
  278. package/dist/engines/pi/machine.js +106 -0
  279. package/dist/engines/pi/models.d.ts +70 -89
  280. package/dist/engines/pi/models.js +188 -81
  281. package/dist/engines/pi/open.d.ts +65 -53
  282. package/dist/engines/pi/open.js +129 -69
  283. package/dist/engines/pi/report.d.ts +3 -12
  284. package/dist/engines/pi/report.js +2 -16
  285. package/dist/engines/pi/search-tools.d.ts +1 -9
  286. package/dist/engines/pi/search-tools.js +26 -57
  287. package/dist/engines/pi/service.d.ts +2 -16
  288. package/dist/engines/pi/service.js +2 -16
  289. package/dist/engines/pi/session-builder.d.ts +5 -9
  290. package/dist/engines/pi/session-builder.js +44 -82
  291. package/dist/engines/pi/session-control.js +286 -269
  292. package/dist/engines/pi/session-effects.d.ts +14 -0
  293. package/dist/engines/pi/session-effects.js +30 -0
  294. package/dist/engines/pi/session-inheritance.d.ts +9 -18
  295. package/dist/engines/pi/session-inheritance.js +103 -160
  296. package/dist/engines/pi/session-markers.d.ts +31 -30
  297. package/dist/engines/pi/session-markers.js +26 -25
  298. package/dist/engines/pi/session-settings.d.ts +6 -22
  299. package/dist/engines/pi/session-settings.js +10 -21
  300. package/dist/engines/pi/session-store.d.ts +26 -92
  301. package/dist/engines/pi/session-store.js +96 -310
  302. package/dist/engines/pi/tool-context.d.ts +17 -45
  303. package/dist/engines/pi/tool-context.js +7 -26
  304. package/dist/engines/pi/tool.d.ts +45 -36
  305. package/dist/engines/pi/tool.js +43 -57
  306. package/dist/engines/pi/turn-kit.d.ts +14 -52
  307. package/dist/engines/pi/turn-kit.js +18 -67
  308. package/dist/engines/pi/wake-tool.d.ts +2 -10
  309. package/dist/engines/pi/wake-tool.js +7 -27
  310. package/dist/env.d.ts +27 -28
  311. package/dist/env.js +45 -50
  312. package/dist/index.js +0 -2
  313. package/dist/lark.d.ts +1 -2
  314. package/dist/lark.js +1 -2
  315. package/dist/loader.d.ts +14 -55
  316. package/dist/loader.js +24 -43
  317. package/dist/log.d.ts +2 -12
  318. package/dist/log.js +3 -24
  319. package/dist/node.d.ts +2 -15
  320. package/dist/node.js +2 -18
  321. package/dist/observe.d.ts +2 -8
  322. package/dist/open-url.d.ts +1 -1
  323. package/dist/open-url.js +1 -1
  324. package/dist/paths.d.ts +70 -134
  325. package/dist/paths.js +165 -232
  326. package/dist/pi.d.ts +4 -6
  327. package/dist/pi.js +3 -3
  328. package/dist/proxy.d.ts +16 -6
  329. package/dist/proxy.js +52 -16
  330. package/dist/runtime.d.ts +18 -12
  331. package/dist/runtime.js +18 -11
  332. package/dist/scaffold/add-channel.d.ts +14 -31
  333. package/dist/scaffold/add-channel.js +48 -112
  334. package/dist/scaffold/init.d.ts +9 -31
  335. package/dist/scaffold/init.js +45 -138
  336. package/dist/scaffold/templates/env.example +6 -4
  337. package/dist/scaffold/templates/fastagent.config.ts +36 -0
  338. package/dist/scaffold/templates.d.ts +3 -3
  339. package/dist/scaffold/templates.js +5 -8
  340. package/dist/scaffold/vendor-skill.d.ts +2 -5
  341. package/dist/scaffold/vendor-skill.js +19 -29
  342. package/dist/schedule/cron.d.ts +9 -5
  343. package/dist/schedule/cron.js +9 -11
  344. package/dist/schedule/discover.d.ts +12 -12
  345. package/dist/schedule/discover.js +43 -34
  346. package/dist/schedule/routine.d.ts +43 -0
  347. package/dist/schedule/routine.js +32 -0
  348. package/dist/schedule/run.d.ts +55 -0
  349. package/dist/schedule/run.js +146 -0
  350. package/dist/schedule/scheduler.d.ts +44 -55
  351. package/dist/schedule/scheduler.js +291 -265
  352. package/dist/schedule/state.d.ts +105 -10
  353. package/dist/schedule/state.js +293 -19
  354. package/dist/schedule/wake-alarm.d.ts +10 -23
  355. package/dist/schedule/wake-alarm.js +91 -121
  356. package/dist/schedule/wakeups.d.ts +23 -39
  357. package/dist/schedule/wakeups.js +33 -75
  358. package/dist/secrets-gate.d.ts +32 -0
  359. package/dist/secrets-gate.js +33 -0
  360. package/dist/service.d.ts +137 -98
  361. package/dist/service.js +250 -252
  362. package/dist/session-remote.d.ts +33 -44
  363. package/dist/session-remote.js +203 -192
  364. package/dist/session.d.ts +153 -228
  365. package/dist/session.js +33 -55
  366. package/dist/tunnel.d.ts +8 -36
  367. package/dist/tunnel.js +24 -65
  368. package/dist/version.d.ts +2 -2
  369. package/dist/version.js +2 -2
  370. package/package.json +10 -16
  371. package/dist/channels/agentcore-state.d.ts +0 -77
  372. package/dist/channels/agentcore-state.js +0 -276
  373. package/dist/channels/github/github.d.ts +0 -31
  374. package/dist/channels/github/github.js +0 -89
  375. package/dist/channels/github/scaffold/channel.ts +0 -25
  376. package/dist/cli/commands/attach.d.ts +0 -108
  377. package/dist/cli/commands/attach.js +0 -637
  378. package/dist/cli/commands/fire.d.ts +0 -7
  379. package/dist/cli/commands/fire.js +0 -38
  380. package/dist/cli/commands/schedule.d.ts +0 -12
  381. package/dist/cli/commands/schedule.js +0 -87
  382. package/dist/github.d.ts +0 -3
  383. package/dist/github.js +0 -2
  384. package/dist/scaffold/templates/fastagent.config.mjs +0 -23
  385. package/dist/schedule/audit.d.ts +0 -18
  386. package/dist/schedule/audit.js +0 -57
  387. package/dist/schedule/schedule.d.ts +0 -35
  388. package/dist/schedule/schedule.js +0 -23
@@ -1,19 +1,13 @@
1
1
  /**
2
- * Readiness probe for a server THIS process can reach directly — the local port `deploy docker --run`
3
- * just published, and the live probes' own origins.
4
- *
5
- * NOT a probe for a public URL a platform must reach: a freshly minted hostname (a quick tunnel, a
6
- * fresh deploy) is routinely unreachable from here for a minute or more while the platform reaches it
7
- * fine, and polling it hard from t+0 makes that worse (#421). The webhook registrars therefore let the
8
- * platform's own URL verification be the probe, retried, instead of gating on this.
2
+ * Readiness probe for a server THIS process can reach over HTTP — the local port `deploy docker --run` just
3
+ * published, the live probes' own origins, and the public URL a host deploy must clear before it registers a webhook
4
+ * (`publicHealthGate` in deploy/channel-ingress.ts).
9
5
  */
10
6
  import { setTimeout as sleep } from "node:timers/promises";
11
- /**
12
- * Poll `healthUrl` until it responds 200, or the timeout elapses. Any error (not routable yet, DNS not
13
- * settled, connection refused) is a "keep waiting", not a failure — that IS the readiness signal. Each
14
- * probe has its own short timeout so one slow attempt can't eat the whole budget.
15
- */
16
- export async function waitForHealth(healthUrl, timeoutMs, intervalMs) {
7
+ /** Poll `healthUrl` until it responds 200, or the timeout elapses. */
8
+ export async function waitForHealth(healthUrl, timeoutMs, intervalMs,
9
+ /** Optional liveness answer from whoever runs the server. */
10
+ stillStarting) {
17
11
  const deadline = Date.now() + timeoutMs;
18
12
  for (;;) {
19
13
  try {
@@ -21,10 +15,12 @@ export async function waitForHealth(healthUrl, timeoutMs, intervalMs) {
21
15
  return true;
22
16
  }
23
17
  catch {
24
- /* not routable yet — keep polling until the deadline */
18
+ // not routable yet — keep polling until the deadline
25
19
  }
26
20
  if (Date.now() >= deadline)
27
21
  return false;
22
+ if (stillStarting && !(await stillStarting()))
23
+ return false;
28
24
  await sleep(intervalMs);
29
25
  }
30
26
  }
@@ -11,20 +11,10 @@ export declare function configureGroupBehavior(input: {
11
11
  apiBase: string;
12
12
  api: Pick<FeishuApi, "listAppScopes" | "addAppScopes">;
13
13
  behavior: FeishuGroupBehavior;
14
- /** Whether the author chose the behavior (flag or prompt). A defaulted "context" inspects and
15
- * reports only — it must never PATCH the sensitive scope into the app draft. */
14
+ /** Whether the author chose the behavior (flag or prompt). */
16
15
  explicit: boolean;
17
16
  note?: (message: string) => void;
18
17
  openUrl?: (url: string) => void;
19
18
  }): Promise<GroupBehaviorSetup>;
20
- /**
21
- * Create or resume the platform app behind `add feishu` / `add lark`. `target` is the AGENT DIR;
22
- * credentials land in its `.env` — whose real path is {@link dotEnvPath}, printed rather than spelled,
23
- * because `FASTAGENT_SECRETS_DIR` moves it and a hardcoded `.secrets/.env` would name a file this run
24
- * did not write. Returns credentials for the
25
- * caller's generic .env write (the guided Lark path), or undefined when nothing remains to write —
26
- * the feishu path persists its own two credential stages internally (the App ID/Secret boundary is
27
- * irreversible and must not wait for the caller). Throws on refusal (a non-interactive lark run);
28
- * the caller surfaces that as a startup failure.
29
- */
19
+ /** Create or resume the platform app behind `add feishu` / `add lark`. */
30
20
  export declare function onboardFeishuCloudApp(target: string, kind: "feishu" | "lark", ingress?: FeishuSubscriptionMode, groupBehavior?: GroupBehaviorChoice): Promise<Record<string, string> | undefined>;
@@ -1,13 +1,6 @@
1
1
  /**
2
- * `fastagent add feishu|lark` app onboarding — the cloud-facing half of runAdd, kept out of cli.ts
3
- * (which self-executes on import), mirroring models-view.ts/auth-view.ts. The POLICY already lives in
4
- * testable modules (register-app.ts, bootstrap-token.ts, lark/onboard.ts); this layer is the terminal
5
- * wiring: clack prompts, .env staging, browser opens, progress lines.
6
- *
7
- * Feishu (scan-to-create): the device flow creates the app and persists App ID/Secret at the
8
- * irreversible boundary. WebSocket stops there; webhook additionally captures the Verification Token
9
- * over a throwaway tunnel. Lark uses the unbound launcher + credential validation, then either stops
10
- * for WebSocket or probes the same webhook/token bootstrap with the config-route-404 manual fallback.
2
+ * `fastagent add feishu|lark` app onboarding — the cloud-facing half of runAdd, kept out of cli.ts (which
3
+ * self-executes on import), mirroring models-view.ts/auth-view.ts.
11
4
  */
12
5
  import { readFile } from "node:fs/promises";
13
6
  import { isCancel, log as clackLog, password, text as clackText } from "@clack/prompts";
@@ -36,14 +29,11 @@ export async function configureGroupBehavior(input) {
36
29
  scopes = [];
37
30
  }
38
31
  const granted = (name) => scopes.some((scope) => scope.name === name && scope.grantStatus === 1 && (scope.type === undefined || scope.type === "tenant"));
39
- // Same type filter as `granted`: a user-type entry is not the tenant scope this path needs, so
40
- // treating one as "on the app" would report an approval that can never arrive and skip the PATCH
41
- // that would actually add it.
32
+ // Same type filter as `granted`: a user-type entry is not the tenant scope this path needs, so treating one as "on
33
+ // the app" would report an approval that can never arrive and skip the PATCH that would actually add it.
42
34
  const onApp = (name) => scopes.some((scope) => scope.name === name && (scope.type === undefined || scope.type === "tenant"));
43
- // Both halves of the recommended path: delivery (the platform pushes un-mentioned group messages)
44
- // and reading a quoted message (so a thread's opening ask carries what it replies to).
45
- // The read capability has two spellings and `im:message` is the superset, so an app holding it needs
46
- // nothing added — asking anyway would cost the author a second tenant-admin approval round.
35
+ // Both halves of the recommended path: delivery (the platform pushes un-mentioned group messages) and reading a
36
+ // quoted message (so a thread's opening ask carries what it replies to).
47
37
  const missing = FEISHU_CONTEXT_ONBOARDING_SCOPES.filter((entry) => !scopeSatisfied(entry, granted));
48
38
  const missingNames = missing.map((entry) => entry.request);
49
39
  if (behavior === "mentions") {
@@ -72,10 +62,7 @@ export async function configureGroupBehavior(input) {
72
62
  return { publishReady: true };
73
63
  }
74
64
  const permissionUrl = `${apiBase}/app/${encodeURIComponent(appId)}/permission`;
75
- // A missing scope is in one of two states, and they need different actions: already on the app but
76
- // not yet approved (nothing to add — wait for the admin), or absent from the draft entirely.
77
- // Same superset rule as `missing`: a draft already requesting `im:message` is awaiting approval, not
78
- // missing something to add.
65
+ // A missing scope is in one of two states, and they need different actions.
79
66
  const awaitingApproval = missing.filter((entry) => scopeSatisfied(entry, onApp)).map((entry) => entry.request);
80
67
  const toRequest = missing.filter((entry) => !scopeSatisfied(entry, onApp)).map((entry) => entry.request);
81
68
  if (toRequest.length === 0) {
@@ -84,11 +71,8 @@ export async function configureGroupBehavior(input) {
84
71
  return { publishReady: false };
85
72
  }
86
73
  if (!explicit) {
87
- // Defaulted, not chosen: report the gap and how to opt in, but leave the app's requested
88
- // permission set untouched (a scripted re-run must not silently escalate a mention-only app).
89
- // Name what is ACTUALLY missing: the common upgrade has the delivery scope already granted and
90
- // only the read scope absent, and pointing at a granted permission sends the author looking in
91
- // the wrong place.
74
+ // Defaulted, not chosen: report the gap and how to opt in, but leave the app's requested permission set untouched
75
+ // (a scripted re-run must not silently escalate a mention-only app).
92
76
  note(`[fastagent] ${missingNames.join(" + ")} not granted (or could not be verified) — group behavior was ` +
93
77
  `defaulted, so nothing was requested. Re-run with --group-behavior context to add ${missing.length > 1 ? "them" : "it"} ` +
94
78
  `to the app draft, or --group-behavior mentions to stay least-privilege: ${permissionUrl}`);
@@ -104,16 +88,7 @@ export async function configureGroupBehavior(input) {
104
88
  openUrl(permissionUrl);
105
89
  return { publishReady: false };
106
90
  }
107
- /**
108
- * Create or resume the platform app behind `add feishu` / `add lark`. `target` is the AGENT DIR;
109
- * credentials land in its `.env` — whose real path is {@link dotEnvPath}, printed rather than spelled,
110
- * because `FASTAGENT_SECRETS_DIR` moves it and a hardcoded `.secrets/.env` would name a file this run
111
- * did not write. Returns credentials for the
112
- * caller's generic .env write (the guided Lark path), or undefined when nothing remains to write —
113
- * the feishu path persists its own two credential stages internally (the App ID/Secret boundary is
114
- * irreversible and must not wait for the caller). Throws on refusal (a non-interactive lark run);
115
- * the caller surfaces that as a startup failure.
116
- */
91
+ /** Create or resume the platform app behind `add feishu` / `add lark`. */
117
92
  export async function onboardFeishuCloudApp(target, kind, ingress = "webhook", groupBehavior = { behavior: "context", explicit: false }) {
118
93
  const env = dotEnvPath(target); // the file actually written — never the default spelling
119
94
  const { envPrefix, apiBase, capabilities } = cloudFor(kind);
@@ -125,8 +100,7 @@ export async function onboardFeishuCloudApp(target, kind, ingress = "webhook", g
125
100
  const existing = await activeDotEnvValues(target, requiredNames);
126
101
  if (Object.keys(existing).length === requiredNames.length) {
127
102
  console.error(`[fastagent] ${requiredNames.join("/")} already set in ${env} — keeping them`);
128
- // WebSocket still needs its console mode/publish guidance. A complete webhook can skip the rest of
129
- // onboarding, but group visibility must still be inspected/configured on every explicit re-run.
103
+ // WebSocket still needs its console mode/publish guidance.
130
104
  if (ingress === "webhook") {
131
105
  const appId = existing[`${envPrefix}_APP_ID`];
132
106
  const appSecret = existing[`${envPrefix}_APP_SECRET`];
@@ -176,8 +150,7 @@ export async function onboardFeishuCloudApp(target, kind, ingress = "webhook", g
176
150
  startTunnel: (port) => startCloudflareTunnel(port),
177
151
  onTunnelReady: (url) => console.error(`[fastagent] temporary tunnel ready → ${url}; registering webhook mode now…`),
178
152
  onPatchRetry: ({ error, attempt, attempts, retryMs }) => console.error(`[fastagent] Lark could not validate the fresh tunnel yet (${String(error)}); retrying PATCH ${attempt + 1}/${attempts} in ${Math.round(retryMs / 1000)}s…`),
179
- // A route-level 404 is definitive, not edge weather: fall back immediately. Retry only
180
- // actual edge/network weather; scope/auth/config failures remain immediate.
153
+ // A route-level 404 is definitive, not edge weather: fall back immediately.
181
154
  shouldRetryPatch: (error) => !isFeishuConfigApiMissing(error) && isTransientFeishuRegistrationError(error),
182
155
  });
183
156
  console.error(`[fastagent] Lark Verification Token captured; Subscription mode changed to webhook in the app draft`);
@@ -207,17 +180,7 @@ export async function onboardFeishuCloudApp(target, kind, ingress = "webhook", g
207
180
  });
208
181
  return Object.fromEntries(Object.entries(credentials).filter((entry) => typeof entry[1] === "string"));
209
182
  }
210
- /**
211
- * The scan-to-create flow `add feishu` runs by default. The device-authorization grant
212
- * creates a pre-configured agent app (bot capability, messaging scopes, event subscriptions) when the
213
- * user confirms a link, then persists App ID/Secret at the irreversible boundary. That completes
214
- * WebSocket credentials; webhook continues through challenge-captured Token persistence. The throwaway
215
- * Request URL is later replaced by `dev --tunnel` / `deploy --run`.
216
- *
217
- * Feishu is the reference cloud and the only kind that runs this BOUND device flow. Lark is an explicit
218
- * compatibility profile: its lagging control plane uses the unbound launcher + guided credentials,
219
- * then probes the canonical token/mode bootstrap with a manual fallback.
220
- */
183
+ /** The scan-to-create flow `add feishu` runs by default. */
221
184
  async function createFeishuAppFlow(target, existing, ingress, groupBehavior) {
222
185
  const env = dotEnvPath(target); // the file actually written — never the default spelling
223
186
  const { apiBase } = cloudFor("feishu");
@@ -233,8 +196,7 @@ async function createFeishuAppFlow(target, existing, ingress, groupBehavior) {
233
196
  const app = await registerFeishuApp({
234
197
  name: "{user}'s agent", // the platform expands {user} to the confirming user's name; editable on the page
235
198
  desc: "Served by fastagent",
236
- // The agent template alone is not enough to SERVE (see feishuAppAddons). Sensitive group
237
- // permission approval and version publishing remain explicit console work.
199
+ // The agent template alone is not enough to SERVE (see feishuAppAddons).
238
200
  addons: feishuAppAddons(),
239
201
  onVerificationUrl: ({ url, expiresInS }) => {
240
202
  console.error(`\n Opening the confirmation link in your browser (or open it in Feishu / render it as a QR code) — valid for ${Math.round(expiresInS / 60)} minutes:\n\n ${url}\n\n waiting for confirmation… (keep this running — the credentials are delivered here)`);
@@ -242,17 +204,13 @@ async function createFeishuAppFlow(target, existing, ingress, groupBehavior) {
242
204
  },
243
205
  });
244
206
  console.error(`[fastagent] app created: ${app.appId}${app.tenantBrand ? ` (${app.tenantBrand} tenant)` : ""}`);
245
- // A cross-brand confirmation should be impossible (each confirm page refuses the other brand's
246
- // code) — but if the platform ever reports one, the credentials would land in the WRONG kind's env
247
- // namespace and serve the wrong cloud. Fail visibly instead of writing them.
207
+ // A cross-brand confirmation should be impossible (each confirm page refuses the other brand's code).
248
208
  if (app.tenantBrand && app.tenantBrand !== "feishu") {
249
209
  throw new Error(`the confirming account is a ${app.tenantBrand} tenant, but this is \`add feishu\` — run \`fastagent add ${app.tenantBrand}\` instead`);
250
210
  }
251
211
  appId = app.appId;
252
212
  appSecret = app.appSecret;
253
- // IRREVERSIBLE BOUNDARY: the remote app now exists and its one-time Secret is in memory. Persist
254
- // both before any config read, temporary tunnel, or Token bootstrap can be interrupted. Partial old
255
- // lines are overwritten because these newly-minted credentials are authoritative as one pair.
213
+ // IRREVERSIBLE BOUNDARY: the remote app now exists and its one-time Secret is in memory.
256
214
  const staged = {
257
215
  FEISHU_APP_ID: appId,
258
216
  FEISHU_APP_SECRET: appSecret,
@@ -285,10 +243,6 @@ async function createFeishuAppFlow(target, existing, ingress, groupBehavior) {
285
243
  return;
286
244
  }
287
245
  // The webhook channel authenticates plaintext events by the platform-generated Verification Token.
288
- // Try the cheap read first (the v6 detail MAY someday return `encryption`), then the real path: the
289
- // token's only programmatic delivery is the url_verification challenge during registration — capture
290
- // it over a throwaway tunnel (bootstrap-token.ts). Failing both is a one-line manual copy; the staged
291
- // ID/Secret pair makes a re-run resume this App rather than mint another one.
292
246
  const tokenVar = "FEISHU_VERIFICATION_TOKEN";
293
247
  const api = createFeishuApi({ baseUrl: apiBase, appId, appSecret });
294
248
  let token;
@@ -298,7 +252,7 @@ async function createFeishuAppFlow(target, existing, ingress, groupBehavior) {
298
252
  token = cfg.verificationToken;
299
253
  }
300
254
  catch {
301
- /* the read surface is best-effort — the bootstrap below is the real path */
255
+ // the read surface is best-effort — the bootstrap below is the real path
302
256
  }
303
257
  if (!token) {
304
258
  console.error(`[fastagent] capturing the Verification Token — a throwaway webhook registration delivers it (spinning up a temporary tunnel; can take a few minutes on a slow edge)…`);
@@ -312,14 +266,12 @@ async function createFeishuAppFlow(target, existing, ingress, groupBehavior) {
312
266
  console.error(`[fastagent] Verification Token captured`);
313
267
  }
314
268
  catch (e) {
315
- // Transient tunnel weather is the usual cause. Do NOT suggest re-running `add feishu` as a new
316
- // scan: the staged pair makes the re-run resume THIS app; manual copy completes it too.
269
+ // Transient tunnel weather is the usual cause.
317
270
  console.error(`[fastagent] warn: could not capture the Verification Token: ${String(e)} — usually a transient tunnel issue; finish this app with the manual copy below`);
318
271
  }
319
272
  }
320
273
  if (token) {
321
- // Persist the second credential stage immediately too — opening the publish page and generic
322
- // scaffold finalization happen only after the complete runtime credential set is durable.
274
+ // Persist the second credential stage immediately too.
323
275
  const staged = await appendChannelDotEnv(target, "feishu", { [tokenVar]: token }, [tokenVar]);
324
276
  console.error(`[fastagent] wrote ${staged.written.join(", ")} to ${env}`);
325
277
  }
@@ -327,8 +279,7 @@ async function createFeishuAppFlow(target, existing, ingress, groupBehavior) {
327
279
  console.error(`[fastagent] copy it manually: developer console → Events & Callbacks → Encryption Strategy → Verification Token → ${tokenVar} in ${env}`);
328
280
  }
329
281
  if (webhookModeChanged) {
330
- // The bootstrap's PATCH flipped event mode in the DRAFT. It takes effect only after a version
331
- // publish, which has no API; later dev/deploy runs change only the Request URL immediately.
282
+ // The bootstrap's PATCH flipped event mode in the DRAFT.
332
283
  const versionUrl = `${apiBase}/app/${appId}/version`;
333
284
  if (groupSetup.publishReady) {
334
285
  console.error(`[fastagent] one console click remains: CREATE + PUBLISH a version (self-approved) — the switch to webhook mode takes effect on publish. Opening ${versionUrl}`);
@@ -339,8 +290,10 @@ async function createFeishuAppFlow(target, existing, ingress, groupBehavior) {
339
290
  }
340
291
  }
341
292
  }
342
- /** Active agent `.env` (`.secrets/.env`) values for the requested names — decided by THE .env
343
- * parser, so this check can never disagree with what `loadEnvFile` reads. Empty/commented values are absent. */
293
+ /**
294
+ * Active agent `.env` (`.secrets/.env`) values for the requested names — decided by THE .env parser, so this check can
295
+ * never disagree with what `loadDotEnv` reads.
296
+ */
344
297
  async function activeDotEnvValues(dir, names) {
345
298
  let content;
346
299
  try {
@@ -1,8 +1,10 @@
1
1
  import { type GroupBehaviorChoice } from "../scaffold/add-channel.ts";
2
- /** Interactive single-workspace internal-app creation + installation. Safe to re-run after interruption. */
2
+ /** Interactive single-workspace internal-app creation + installation. */
3
3
  export declare function onboardSlackInternalApp(input: {
4
- /** The AGENT DIR — credentials land in its `.env` ({@link dotEnvPath}: `FASTAGENT_SECRETS_DIR` moves
5
- * it, so messages print the resolved path rather than the default spelling). */
4
+ /**
5
+ * The AGENT DIR — credentials land in its `.env` ({@link dotEnvPath}: `FASTAGENT_SECRETS_DIR` moves it, so messages
6
+ * print the resolved path rather than the default spelling).
7
+ */
6
8
  target: string;
7
9
  stateRoot: string;
8
10
  groupBehavior: GroupBehaviorChoice;
@@ -3,10 +3,9 @@ import { basename } from "node:path";
3
3
  import { isCancel, log as clackLog, password, select, text as clackText } from "@clack/prompts";
4
4
  import { dotEnvPath, parseEnvContent } from "../env.js";
5
5
  import { openExternalUrl } from "../open-url.js";
6
- import { installProxyFetch } from "../proxy.js";
7
6
  import { appendChannelDotEnv } from "../scaffold/add-channel.js";
8
7
  import { newSlackOnboardingState, onboardSlackApp } from "../channels/slack/onboard.js";
9
- import { readSlackOnboardingState, writeSlackOnboardingState } from "../channels/slack/onboarding-state.js";
8
+ import { configTokenExpiry, readSlackOnboardingState, writeSlackOnboardingState, } from "../channels/slack/onboarding-state.js";
10
9
  import { startSlackSetupServer } from "../channels/slack/setup-server.js";
11
10
  import { startCloudflareTunnel } from "../tunnel.js";
12
11
  const CONFIG_TOKEN_URL = "https://api.slack.com/apps";
@@ -19,9 +18,39 @@ async function promptValue(message, hidden = false, initialValue) {
19
18
  throw new Error(`${message}: value is required`);
20
19
  return value;
21
20
  }
22
- /** Interactive single-workspace internal-app creation + installation. Safe to re-run after interruption. */
21
+ /** The one place an App Configuration token pair is asked for and validated. */
22
+ async function promptConfigTokens() {
23
+ console.error(`[fastagent] generate an App Configuration Token pair at ${CONFIG_TOKEN_URL}`);
24
+ openExternalUrl(CONFIG_TOKEN_URL);
25
+ const configToken = await promptValue("Slack configuration access token (xoxe.xoxp-…)", true);
26
+ const configRefreshToken = await promptValue("Slack configuration refresh token (xoxe-…)", true);
27
+ if (!configToken.startsWith("xoxe.") || !configRefreshToken.startsWith("xoxe-")) {
28
+ throw new Error("invalid Slack configuration token prefix (expected xoxe. access + xoxe- refresh)");
29
+ }
30
+ return { configToken, configRefreshToken, configTokenExpiresAt: configTokenExpiry() };
31
+ }
32
+ /** Keep the saved token pair, or paste a fresh one? `--replace-config` answers without asking. */
33
+ async function chooseTokenAction(prompt) {
34
+ if (prompt.forced)
35
+ return "replace-config";
36
+ const answer = await select({
37
+ message: prompt.message,
38
+ initialValue: "keep",
39
+ options: [
40
+ { value: "keep", label: prompt.keepLabel },
41
+ {
42
+ value: "replace-config",
43
+ label: prompt.replaceLabel,
44
+ ...(prompt.replaceHint ? { hint: prompt.replaceHint } : {}),
45
+ },
46
+ ],
47
+ });
48
+ if (isCancel(answer))
49
+ throw new Error("Slack onboarding cancelled");
50
+ return answer;
51
+ }
52
+ /** Interactive single-workspace internal-app creation + installation. */
23
53
  export async function onboardSlackInternalApp(input) {
24
- installProxyFetch();
25
54
  if (!(process.stdin.isTTY && process.stdout.isTTY)) {
26
55
  throw new Error("`add slack` needs an interactive terminal for internal-app creation and OAuth — " +
27
56
  "re-run in a terminal, or pass --no-onboard to scaffold only");
@@ -50,38 +79,15 @@ export async function onboardSlackInternalApp(input) {
50
79
  throw new Error(`the onboarded Slack app uses group behavior ${state.groupBehavior}; changing an installed app's ` +
51
80
  "OAuth scopes is a migration. Keep the existing choice, or remove the app + Slack onboarding state and create a new app");
52
81
  }
53
- let action = "replace-config";
54
- if (!input.replaceConfig) {
55
- const answer = await select({
56
- message: `Slack app ${state.appId ?? "(unknown)"} is already installed${state.teamName ? ` in ${state.teamName}` : ""}`,
57
- initialValue: "keep",
58
- options: [
59
- { value: "keep", label: "Keep the installed app" },
60
- {
61
- value: "replace-config",
62
- label: "Replace App Configuration tokens",
63
- hint: "repair automatic dev/deploy Request URL updates",
64
- },
65
- ],
66
- });
67
- if (isCancel(answer))
68
- throw new Error("Slack onboarding cancelled");
69
- action = answer;
70
- }
82
+ const action = await chooseTokenAction({
83
+ forced: input.replaceConfig === true,
84
+ message: `Slack app ${state.appId ?? "(unknown)"} is already installed${state.teamName ? ` in ${state.teamName}` : ""}`,
85
+ keepLabel: "Keep the installed app",
86
+ replaceLabel: "Replace App Configuration tokens",
87
+ replaceHint: "repair automatic dev/deploy Request URL updates",
88
+ });
71
89
  if (action === "replace-config") {
72
- console.error(`[fastagent] generate a fresh App Configuration Token pair at ${CONFIG_TOKEN_URL}`);
73
- openExternalUrl(CONFIG_TOKEN_URL);
74
- const configToken = await promptValue("Slack configuration access token (xoxe.xoxp-…)", true);
75
- const configRefreshToken = await promptValue("Slack configuration refresh token (xoxe-…)", true);
76
- if (!configToken.startsWith("xoxe.") || !configRefreshToken.startsWith("xoxe-")) {
77
- throw new Error("invalid Slack configuration token prefix (expected xoxe. access + xoxe- refresh)");
78
- }
79
- writeSlackOnboardingState(input.stateRoot, {
80
- ...state,
81
- configToken,
82
- configRefreshToken,
83
- configTokenExpiresAt: Date.now() + 11 * 60 * 60_000,
84
- });
90
+ writeSlackOnboardingState(input.stateRoot, { ...state, ...(await promptConfigTokens()) });
85
91
  console.error("[fastagent] replaced local Slack App Configuration tokens; runtime app credentials are unchanged");
86
92
  }
87
93
  else {
@@ -93,20 +99,10 @@ export async function onboardSlackInternalApp(input) {
93
99
  const appName = await promptValue("Slack app name", false, `FastAgent ${basename(input.target)}`);
94
100
  clackLog.info("Slack's configuration refresh token can manage apps owned by your user in this workspace. " +
95
101
  "FastAgent stores it only in owner-readable local state; it is never deployed.");
96
- console.error(`[fastagent] generate an App Configuration Token at ${CONFIG_TOKEN_URL}`);
97
- openExternalUrl(CONFIG_TOKEN_URL);
98
- const configToken = await promptValue("Slack configuration access token (xoxe.xoxp-…)", true);
99
- const configRefreshToken = await promptValue("Slack configuration refresh token (xoxe-…)", true);
100
- if (!configToken.startsWith("xoxe."))
101
- throw new Error("Slack configuration access token must start with xoxe.");
102
- if (!configRefreshToken.startsWith("xoxe-")) {
103
- throw new Error("Slack configuration refresh token must start with xoxe-");
104
- }
105
102
  state = newSlackOnboardingState({
106
103
  appName,
107
104
  groupBehavior: input.groupBehavior.behavior,
108
- configToken,
109
- configRefreshToken,
105
+ ...(await promptConfigTokens()),
110
106
  });
111
107
  writeSlackOnboardingState(input.stateRoot, state);
112
108
  }
@@ -118,35 +114,17 @@ export async function onboardSlackInternalApp(input) {
118
114
  throw new Error(`a prior Slack app creation attempt at ${state.createAttemptedAt} returned no app ID — ` +
119
115
  `inspect ${CONFIG_TOKEN_URL}; delete any incomplete app and ${input.stateRoot}/channels/slack/onboarding.json before retrying`);
120
116
  }
121
- // `--replace-config` also covers the created-but-not-installed state, where a revoked token would
122
- // otherwise strand the resume (rotation fails and no menu offers replacement).
117
+ // `--replace-config` also covers the created-but-not-installed state, where a revoked token would otherwise strand
118
+ // the resume (rotation fails and no menu offers replacement).
123
119
  if (resumed && (!state.appId || input.replaceConfig)) {
124
- let action = "replace-config";
125
- if (!input.replaceConfig) {
126
- const answer = await select({
127
- message: "Resume Slack onboarding with which App Configuration tokens?",
128
- initialValue: "keep",
129
- options: [
130
- { value: "keep", label: "Use the saved token pair" },
131
- { value: "replace-config", label: "Paste a fresh token pair" },
132
- ],
133
- });
134
- if (isCancel(answer))
135
- throw new Error("Slack onboarding cancelled");
136
- action = answer;
137
- }
120
+ const action = await chooseTokenAction({
121
+ forced: input.replaceConfig === true,
122
+ message: "Resume Slack onboarding with which App Configuration tokens?",
123
+ keepLabel: "Use the saved token pair",
124
+ replaceLabel: "Paste a fresh token pair",
125
+ });
138
126
  if (action === "replace-config") {
139
- console.error(`[fastagent] generate a fresh App Configuration Token pair at ${CONFIG_TOKEN_URL}`);
140
- openExternalUrl(CONFIG_TOKEN_URL);
141
- state = {
142
- ...state,
143
- configToken: await promptValue("Slack configuration access token (xoxe.xoxp-…)", true),
144
- configRefreshToken: await promptValue("Slack configuration refresh token (xoxe-…)", true),
145
- configTokenExpiresAt: Date.now() + 11 * 60 * 60_000,
146
- };
147
- if (!state.configToken.startsWith("xoxe.") || !state.configRefreshToken.startsWith("xoxe-")) {
148
- throw new Error("invalid Slack configuration token prefix (expected xoxe. access + xoxe- refresh)");
149
- }
127
+ state = { ...state, ...(await promptConfigTokens()) };
150
128
  writeSlackOnboardingState(input.stateRoot, state);
151
129
  }
152
130
  }
@@ -160,9 +138,8 @@ export async function onboardSlackInternalApp(input) {
160
138
  const redirectUrl = `${tunnel.url}${server.redirectPath}`;
161
139
  console.error(`[fastagent] temporary Slack setup tunnel ready → ${tunnel.url}`);
162
140
  try {
163
- // No local readiness probe: Slack challenges requestUrl from ITS network during app creation, and
164
- // that is the reachability that matters (#421) — this machine often cannot reach a fresh tunnel
165
- // hostname for a minute. onboardSlackApp retries the create while Slack cannot verify it yet.
141
+ // No local readiness probe: Slack challenges requestUrl from ITS network during app creation, and that is the
142
+ // reachability that matters (#421).
166
143
  await onboardSlackApp({ stateRoot: input.stateRoot, state, requestUrl, redirectUrl }, {
167
144
  note: (message) => clackLog.info(message),
168
145
  openUrl: openExternalUrl,
@@ -1,23 +1,9 @@
1
- /**
2
- * CLI presenter for the auth-status line `reportAuth` prints (invoke/dev/start). Kept out of cli.ts —
3
- * which self-executes on import — so the expired-vs-missing DECISION is unit-testable without a real
4
- * credential round-trip, mirroring models-view.ts.
5
- */
1
+ /** CLI presenter for the auth-status line `reportAuth` prints (invoke/dev/start). */
6
2
  /** A stored credential as `reportAuth` needs it: just its kind, for the status line. */
7
3
  export interface StoredCredentialInfo {
8
4
  type: string;
9
5
  }
10
- /**
11
- * Format the auth status for `spec`'s provider. `source` is {@link probeAuthSource}'s label (an env-var
12
- * name, "OAuth", a stored-key label) or undefined when nothing currently SATISFIES auth. Undefined has
13
- * TWO causes probeAuthSource can't distinguish (it swallows the throw): nothing stored, OR a credential
14
- * that IS stored but couldn't be made usable (an expired/revoked OAuth whose refresh failed). `stored`
15
- * — a refresh-FREE store read the caller does only in that case — tells them apart, so an expired login
16
- * reports "expired/unusable → `fastagent login`" instead of a misleading "(none found)" (which would then
17
- * be contradicted by the actual "OAuth refresh failed"). A malformed/unreadable store also reads as
18
- * undefined `stored` and lands in "(none found)" — but the store's own read warns about the corrupt file,
19
- * so the real signal is surfaced there; this line stays about credential PRESENCE.
20
- */
6
+ /** Format the auth status for `spec`'s provider. */
21
7
  export declare function formatAuthReport(provider: string, authPath: string, source: string | undefined, stored: StoredCredentialInfo | undefined): {
22
8
  line: string;
23
9
  warn?: string;
@@ -1,19 +1,5 @@
1
- /**
2
- * CLI presenter for the auth-status line `reportAuth` prints (invoke/dev/start). Kept out of cli.ts —
3
- * which self-executes on import — so the expired-vs-missing DECISION is unit-testable without a real
4
- * credential round-trip, mirroring models-view.ts.
5
- */
6
- /**
7
- * Format the auth status for `spec`'s provider. `source` is {@link probeAuthSource}'s label (an env-var
8
- * name, "OAuth", a stored-key label) or undefined when nothing currently SATISFIES auth. Undefined has
9
- * TWO causes probeAuthSource can't distinguish (it swallows the throw): nothing stored, OR a credential
10
- * that IS stored but couldn't be made usable (an expired/revoked OAuth whose refresh failed). `stored`
11
- * — a refresh-FREE store read the caller does only in that case — tells them apart, so an expired login
12
- * reports "expired/unusable → `fastagent login`" instead of a misleading "(none found)" (which would then
13
- * be contradicted by the actual "OAuth refresh failed"). A malformed/unreadable store also reads as
14
- * undefined `stored` and lands in "(none found)" — but the store's own read warns about the corrupt file,
15
- * so the real signal is surfaced there; this line stays about credential PRESENCE.
16
- */
1
+ /** CLI presenter for the auth-status line `reportAuth` prints (invoke/dev/start). */
2
+ /** Format the auth status for `spec`'s provider. */
17
3
  export function formatAuthReport(provider, authPath, source, stored) {
18
4
  if (source !== undefined)
19
5
  return { line: `auth: ${source} (${provider}) — ${authPath}` };