@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
@@ -1,6 +1,6 @@
1
1
  import { fromForwarder } from "./agentcore-protocol.js";
2
2
  import { log } from "../log.js";
3
- import { routesFor, serviceOver, startSchedules, } from "../service.js";
3
+ import { routesFor, serviceOver, startSchedules, webhooksOf, } from "../service.js";
4
4
  import { agentcoreRoutes, agentcorePing } from "./agentcore.js";
5
5
  import * as Effect from "effect/Effect";
6
6
  import { fireScheduleOnce } from "../schedule/scheduler.js";
@@ -99,9 +99,10 @@ export async function mountAgentcoreService(opened) {
99
99
  // recurring schedule the container sets (`armAlarms`), ONE path per instant, so the reply to that delivery says
100
100
  // whether it ran. The clock still pumps wake-ups; a redelivered fire claims its slot once.
101
101
  const mirror = armAlarms(stateRoot, () => schedules);
102
+ const webhooks = webhooksOf(opened);
102
103
  const scheduled = startSchedules(agent, stateRoot, schedules, { localClock: false });
103
104
  const lazyChannels = async () => {
104
- const lazy = routesFor(agent, stateRoot, opened.channels);
105
+ const lazy = routesFor(agent, stateRoot, opened.channels, webhooks.list);
105
106
  if (lazy.longConnections.length > 0) {
106
107
  throw new Error(`long-connection channel(s) ${lazy.longConnections.map((c) => c.name).join(", ")} cannot serve on ` +
107
108
  `AgentCore (scale-to-zero severs resident connections) — use the channel's webhook form`);
@@ -113,13 +114,21 @@ export async function mountAgentcoreService(opened) {
113
114
  stateRoot,
114
115
  schedules: () => schedules,
115
116
  ...(mirror ? { onStateReady: mirror } : {}),
117
+ webhooks: {
118
+ list: webhooks.list,
119
+ // A custom domain the operator named in the configuration is where the platforms reach it, not the Function URL.
120
+ point: (forwarderUrl) => webhooks.register(opened.config.http?.publicUrl ?? forwarderUrl),
121
+ },
116
122
  channels: lazyChannels,
117
123
  });
118
124
  const handler = router(adapterRoutes);
119
125
  log.info(`[fastagent] agentcore: serving POST /invocations + GET /ping (FASTAGENT_AGENTCORE=1)`);
120
126
  return {
121
127
  // Nothing to open: no port of its own, no resident connections.
122
- ...serviceOver(handler, Promise.resolve(), async () => scheduled.stop()),
128
+ ...serviceOver(handler, Promise.resolve(), async () => {
129
+ webhooks.dispose();
130
+ await scheduled.stop();
131
+ }),
123
132
  // The adapter IS the surface here; the channel routes arrive lazily BEHIND it.
124
133
  routes: adapterRoutes,
125
134
  agentDir,
@@ -135,7 +144,7 @@ export async function mountAgentcoreService(opened) {
135
144
  * surface (channels/agentcore.ts).
136
145
  */
137
146
  export function mountAgentcore(options) {
138
- const { agent, stateRoot, schedules, onStateReady, channels } = options;
147
+ const { agent, stateRoot, schedules, onStateReady, channels, webhooks } = options;
139
148
  // OCCURRENCE SEMANTICS, because this host's clock is ours: the container set the recurring schedule that names this
140
149
  // instant and the forwarder relays it behind the ingress secret, so a claim means something (dedup across
141
150
  // EventBridge's redeliveries, a fire history, the overlap policy).
@@ -172,6 +181,7 @@ export function mountAgentcore(options) {
172
181
  ingressSecret: process.env.FASTAGENT_INGRESS_SECRET,
173
182
  onStateReady,
174
183
  fireSchedule,
184
+ ...(webhooks ? { webhooks } : {}),
175
185
  });
176
186
  }
177
187
  /**
@@ -1,4 +1,5 @@
1
1
  import type { ChannelHandler, Routes } from "../channel.ts";
2
+ import type { WebhookRegistration } from "./webhooks.ts";
2
3
  /**
3
4
  * What the lazy factory hands back: the CHANNELS' routes, and nothing else.
4
5
  *
@@ -33,6 +34,14 @@ export interface AgentcoreAdapterOptions {
33
34
  ingressSecret?: string;
34
35
  /** Runs once on activation, after accepting the current forwarder callback URL. */
35
36
  onStateReady?: () => void;
37
+ /**
38
+ * The webhook channels this container points at the forwarder: `point` is called once, on the first trusted
39
+ * envelope that constructed the channels, with the URL that envelope names; `list` is what a probe reports.
40
+ */
41
+ webhooks?: {
42
+ point(forwarderUrl: string): Promise<unknown>;
43
+ list(): readonly WebhookRegistration[];
44
+ };
36
45
  }
37
46
  export declare function agentcoreRoutes(options: AgentcoreAdapterOptions): Routes;
38
47
  export declare function agentcorePing(isBusy: () => boolean): ChannelHandler;
@@ -41,8 +41,28 @@ function createActivation(deps) {
41
41
  };
42
42
  }
43
43
  export function agentcoreRoutes(options) {
44
- const { channels, stateRoot, isBusy, fireSchedule, ingressSecret, onStateReady } = options;
44
+ const { channels, stateRoot, isBusy, fireSchedule, ingressSecret, onStateReady, webhooks } = options;
45
45
  const activation = createActivation({ stateRoot, onStateReady, channels });
46
+ // Under the process's busy signal: AgentCore reclaims a microVM with nothing in flight, and a registration is a
47
+ // call to the platform that must end.
48
+ let pointed = false;
49
+ const pointWebhooks = (url) => {
50
+ if (webhooks === undefined || pointed)
51
+ return;
52
+ pointed = true;
53
+ void (async () => {
54
+ const done = beginWork();
55
+ try {
56
+ await webhooks.point(url);
57
+ }
58
+ catch (error) {
59
+ log.error(`[agentcore] could not point the webhook channels at ${url}: ${String(error)}`);
60
+ }
61
+ finally {
62
+ done();
63
+ }
64
+ })();
65
+ };
46
66
  const carried = (request) => {
47
67
  const { method, path, query, headers, bodyB64 } = request;
48
68
  return new Request(`http://agentcore.local${path}${typeof query === "string" && query !== "" ? `?${query}` : ""}`, {
@@ -103,6 +123,8 @@ export function agentcoreRoutes(options) {
103
123
  log.error(`[agentcore] channel construction failed: ${constructionError}`);
104
124
  }
105
125
  }
126
+ if (dispatch && typeof envelope.wake?.url === "string")
127
+ pointWebhooks(envelope.wake.url);
106
128
  switch (envelope.kind) {
107
129
  case "webhook": {
108
130
  const { bodyB64 } = envelope;
@@ -160,9 +182,12 @@ export function agentcoreRoutes(options) {
160
182
  case "probe": {
161
183
  // The structured verdict (transport-200 — see the envelope doc). From the IAM door (untrusted) nothing was
162
184
  // activated, so `ok` says only that the definition opened; channel construction is the forwarder probe's.
163
- return json(constructionError === undefined
164
- ? { ok: true }
165
- : { ok: false, error: `channel construction failed: ${constructionError}` }, 200);
185
+ if (constructionError !== undefined) {
186
+ return json({ ok: false, error: `channel construction failed: ${constructionError}` }, 200);
187
+ }
188
+ return json(trusted
189
+ ? { ok: true, webhooks: (webhooks?.list() ?? []).map((w) => ({ channel: w.channel, outcome: w.outcome })) }
190
+ : { ok: true }, 200);
166
191
  }
167
192
  default:
168
193
  return text(`unknown envelope kind "${envelope.kind}"\n`, 400);
@@ -1,4 +1,6 @@
1
1
  import type { Agent } from "../agent-api.ts";
2
2
  import type { ChannelContext } from "../channel.ts";
3
+ /** The directory of the channel `name`'s own state under `stateRoot`: its context's `stateDir`. */
4
+ export declare function channelStateDir(stateRoot: string, name: string): string;
3
5
  /** The context of the channel `name` (its file's name under `src/channels/`), its state under `stateRoot`. */
4
6
  export declare function channelContext(agent: Agent, stateRoot: string, name: string): ChannelContext;
@@ -10,11 +10,15 @@ import { portError, portJoin } from "../effect-port.js";
10
10
  import { log } from "../log.js";
11
11
  import { ensureStateHome } from "../state-file.js";
12
12
  import { createTurnQueue } from "./turn-queue.js";
13
+ /** The directory of the channel `name`'s own state under `stateRoot`: its context's `stateDir`. */
14
+ export function channelStateDir(stateRoot, name) {
15
+ return join(stateRoot, "channels", name);
16
+ }
13
17
  /** The context of the channel `name` (its file's name under `src/channels/`), its state under `stateRoot`. */
14
18
  export function channelContext(agent, stateRoot, name) {
15
19
  if (!isAbsolute(stateRoot))
16
20
  throw new Error(`a channel's state root must be absolute, got "${stateRoot}"`);
17
- const stateDir = join(stateRoot, "channels", name);
21
+ const stateDir = channelStateDir(stateRoot, name);
18
22
  ensureStateHome(stateDir);
19
23
  const sessions = conversationSessions(join(stateDir, "sessions.json"));
20
24
  const turns = createTurnQueue({
@@ -61,7 +61,7 @@ export interface FeishuListedMessage {
61
61
  sender_type?: string;
62
62
  };
63
63
  }
64
- export interface FeishuAppScope {
64
+ interface FeishuAppScope {
65
65
  name: string;
66
66
  grantStatus: number;
67
67
  type?: "user" | "tenant";
@@ -177,3 +177,4 @@ export interface FeishuApi {
177
177
  /** Split text into chunks whose UTF-8 size fits the message cap, preferring a newline boundary. */
178
178
  export declare function chunkFeishuText(text: string, maxBytes?: number): string[];
179
179
  export declare function createFeishuApi(opts: FeishuApiOptions): FeishuApi;
180
+ export {};
@@ -1,6 +1,5 @@
1
1
  import type { ChannelModule, LongConnectionChannelModule } from "../../channel.ts";
2
2
  import { type FeishuCloudProfile } from "./cloud.ts";
3
- import { type FeishuSubscriptionMode } from "./setup-mode.ts";
4
3
  import { type FeishuMessage, type FeishuMessageEvent, type FeishuRoute, defaultFeishuRoute, feishuEnvelope } from "./parse.ts";
5
4
  import { type FeishuFailure } from "./preview.ts";
6
5
  import { connectFeishuWs } from "./ws-ingress.ts";
@@ -31,8 +30,6 @@ export type FeishuWebSocketChannelOptions = FeishuChannelBaseOptions & {
31
30
  };
32
31
  /** Build the canonical Feishu Request-URL webhook channel. */
33
32
  export declare function feishuChannel(opts: FeishuChannelOptions): ChannelModule;
34
- /** How this process receives the Feishu channel: `FEISHU_INGRESS`, else WebSocket under `dev` and webhook otherwise. */
35
- export declare function feishuIngress(): FeishuSubscriptionMode;
36
33
  /** Build the canonical Feishu WebSocket long-connection channel. */
37
34
  export declare function feishuWebSocketChannel(opts: FeishuWebSocketChannelOptions): LongConnectionChannelModule;
38
35
  /** Internal compatibility seams: protocol behavior comes from Feishu; the profile binds cloud edges. */
@@ -2,6 +2,7 @@
2
2
  * Canonical Feishu bot-channel engine: verified webhook or official-SDK WebSocket → dedup → route → persist → enqueue
3
3
  * → stream a live card.
4
4
  */
5
+ import { recordReached } from "../webhook-evidence.js";
5
6
  import { join } from "node:path";
6
7
  import { Effect } from "effect";
7
8
  import { log } from "../../log.js";
@@ -27,7 +28,7 @@ import { feishuTurnStream } from "./invoke-turn.js";
27
28
  import { createFeishuApi } from "./feishu-api.js";
28
29
  import { normalizeFeishuMessage } from "./normalize.js";
29
30
  import { createThreadParticipants } from "../kit/thread-participants.js";
30
- import { FEISHU_AGENT_SCOPES, feishuIngressFor, scopeSatisfied } from "./setup-mode.js";
31
+ import { FEISHU_AGENT_SCOPES, scopeSatisfied } from "./setup-mode.js";
31
32
  import { cloudEnvelope, defaultFeishuRoute, feishuEnvelope, placeKey, senderId, } from "./parse.js";
32
33
  import { defaultErrorMessage, mountFeishuPreview, settleFeishuPreview, feishuReply, } from "./preview.js";
33
34
  import { connectFeishuWs } from "./ws-ingress.js";
@@ -75,10 +76,6 @@ function isStoredFeishuTurn(t) {
75
76
  export function feishuChannel(opts) {
76
77
  return buildFeishuChannel(FEISHU_CLOUD, opts, feishuChannel.name);
77
78
  }
78
- /** How this process receives the Feishu channel: `FEISHU_INGRESS`, else WebSocket under `dev` and webhook otherwise. */
79
- export function feishuIngress() {
80
- return feishuIngressFor("feishu");
81
- }
82
79
  /** Build the canonical Feishu WebSocket long-connection channel. */
83
80
  export function feishuWebSocketChannel(opts) {
84
81
  return buildFeishuWebSocketChannel(FEISHU_CLOUD, opts, feishuWebSocketChannel.name);
@@ -418,7 +415,7 @@ function createFeishuRuntimeFactory(profile, opts, factoryName) {
418
415
  return { acceptEvent, turnsIdle: () => Promise.all([runner.idle(), sideTasks.drain()]).then(() => undefined) };
419
416
  };
420
417
  }
421
- function createFeishuWebhookRoutes(profile, opts, runtime) {
418
+ function createFeishuWebhookRoutes(profile, opts, runtime, stateDir) {
422
419
  const { verificationToken, encryptKey } = opts;
423
420
  const { kind, envPrefix } = profile;
424
421
  const label = `[${kind}]`;
@@ -496,6 +493,9 @@ function createFeishuWebhookRoutes(profile, opts, runtime) {
496
493
  log.warn(`${label} rejected an event: verification token mismatch (check ${envPrefix}_VERIFICATION_TOKEN against the console)`);
497
494
  return text("invalid token\n", 401);
498
495
  }
496
+ // The platform's token: the Request URL reaches this instance, at the address it was sent to (Lark's, set by a
497
+ // person where its configuration API is missing).
498
+ recordReached(stateDir, req);
499
499
  if (envelope.type === "url_verification" && typeof envelope.challenge === "string") {
500
500
  log.info(`${label} answered the console's url_verification challenge`);
501
501
  return Response.json({ challenge: envelope.challenge });
@@ -517,11 +517,11 @@ export function buildFeishuChannel(profile, opts, factoryName) {
517
517
  if (!opts.verificationToken) {
518
518
  // The scaffolded file reaches this under `start` with nothing set, so the error names both ways out.
519
519
  throw new Error(`${factoryName} receives by webhook and needs a non-empty verificationToken: \`fastagent deploy <host> --run\` ` +
520
- `or \`fastagent add ${profile.kind} --ingress webhook\` captures ${profile.envPrefix}_VERIFICATION_TOKEN ` +
521
- `(console → Events & Callbacks has it too), or set ${profile.envPrefix}_INGRESS=websocket to receive by ` +
522
- `WebSocket instead`);
520
+ `or \`fastagent channels setup ${profile.kind} --ingress webhook\` captures ${profile.envPrefix}_VERIFICATION_TOKEN ` +
521
+ `(console → Events & Callbacks has it too), or set channels.<name>.ingress to "connection" in the instance's ` +
522
+ `fastagent.json to receive by WebSocket instead`);
523
523
  }
524
- return createFeishuWebhookRoutes(profile, opts, createRuntime(ctx));
524
+ return createFeishuWebhookRoutes(profile, opts, createRuntime(ctx), ctx.stateDir);
525
525
  };
526
526
  }
527
527
  export function buildFeishuWebSocketChannel(profile, opts, factoryName, deps = {}) {
@@ -6,6 +6,7 @@
6
6
  * it behind something non-trivial, adopt the official SDK rather than chasing it.
7
7
  */
8
8
  import { gzipSync } from "node:zlib";
9
+ import { stripEnd } from "../../strip-edges.js";
9
10
  /** Feishu accounts endpoint (the flow starts here for every user; a Lark-tenant scan switches over). */
10
11
  const FEISHU_ACCOUNTS = "https://accounts.feishu.cn";
11
12
  const LARK_ACCOUNTS = "https://accounts.larksuite.com";
@@ -14,11 +15,8 @@ const ENDPOINT = "/oauth/v1/app/registration";
14
15
  const REQUEST_TIMEOUT_MS = 30_000;
15
16
  /** JSON → gzip → base64url — the platform's fixed addons encoding. */
16
17
  export function encodeAddons(addons) {
17
- return gzipSync(Buffer.from(JSON.stringify(addons), "utf8"))
18
- .toString("base64")
19
- .replace(/\+/g, "-")
20
- .replace(/\//g, "_")
21
- .replace(/=+$/, "");
18
+ const base64 = gzipSync(Buffer.from(JSON.stringify(addons), "utf8")).toString("base64");
19
+ return stripEnd(base64.replace(/\+/g, "-").replace(/\//g, "_"), (char) => char === "=");
22
20
  }
23
21
  /** RFC 8628 device-flow states whose non-2xx JSON bodies belong to the polling state machine. */
24
22
  const DEVICE_FLOW_ERRORS = new Set(["authorization_pending", "slow_down", "access_denied", "expired_token"]);
@@ -1,20 +1,17 @@
1
- import { needs } from "@fastagent-sh/fastagent";
2
- import {
3
- type FeishuChannelOptions,
4
- feishuChannel,
5
- feishuIngress,
6
- feishuWebSocketChannel,
7
- } from "@fastagent-sh/fastagent/feishu";
1
+ import { byIngress, needs } from "@fastagent-sh/fastagent";
2
+ import { type FeishuChannelOptions, feishuChannel, feishuWebSocketChannel } from "@fastagent-sh/fastagent/feishu";
8
3
 
9
4
  // fastagent's canonical Feishu adapter (verify + run + reply), configured with YOUR policy. Feishu
10
5
  // (open.feishu.cn) only; a Lark-international tenant uses `fastagent add lark` instead.
11
6
  //
12
- // How it receives is FEISHU_INGRESS (webhook | websocket). Unset, `fastagent dev` connects OUT by
13
- // WebSocket (no public URL, no tunnel), and everything else — `fastagent start`, every deployment —
14
- // receives by webhook at POST /feishu. Dev reads .secrets/.env; production reads
15
- // .secrets/production/.env. Interactive `deploy --run` creates a separate production app.
7
+ // How it receives is the instance's choice, `channels.feishu.ingress` in its fastagent.json (webhook |
8
+ // connection). Unset, `fastagent dev` opens a long connection OUT (no public URL, no tunnel), and everything
9
+ // else — `fastagent start`, every deployment — receives by webhook at POST /feishu, the way a host that
10
+ // wakes on a request is reached.
11
+ // Dev reads .secrets/.env; production reads .secrets/production/.env. Interactive `deploy --run`
12
+ // creates a separate production app.
16
13
  //
17
- // `fastagent add feishu` creates and configures the app (scan-to-create). For a hand-made app, in the
14
+ // `fastagent channels setup feishu` creates and configures the app (scan-to-create). For a hand-made app, in the
18
15
  // developer console:
19
16
  // 1. create a custom app → enable the BOT capability → copy App ID / App Secret into .env
20
17
  // 2. Permissions: add `im:message.p2p_msg:readonly` (direct messages), `im:message.group_at_msg:readonly`
@@ -45,25 +42,26 @@ const policy: Pick<FeishuChannelOptions, "onError" | "route"> = {
45
42
  // route: (e) => defaultFeishuRoute(e, { botOpenId: "ou_xxx" }) && { text: `${feishuEnvelope(e)}\n[extra]` },
46
43
  };
47
44
 
48
- // `needs` declares the env vars this channel reads: fastagent carries them to a deployed box and
49
- // refuses to serve while a required one is unset. FEISHU_ENCRYPT_KEY is optional.
50
- export default feishuIngress() === "webhook"
51
- ? needs(
52
- [
53
- "FEISHU_APP_ID",
54
- "FEISHU_APP_SECRET",
55
- "FEISHU_VERIFICATION_TOKEN",
56
- { name: "FEISHU_ENCRYPT_KEY", description: "when set, plaintext events are refused", optional: true },
57
- ],
58
- (secrets) =>
59
- feishuChannel({
60
- appId: secrets.FEISHU_APP_ID,
61
- appSecret: secrets.FEISHU_APP_SECRET,
62
- verificationToken: secrets.FEISHU_VERIFICATION_TOKEN, // authenticates inbound events
63
- encryptKey: secrets.FEISHU_ENCRYPT_KEY,
64
- ...policy,
65
- }),
66
- )
67
- : needs(["FEISHU_APP_ID", "FEISHU_APP_SECRET"], (secrets) =>
68
- feishuWebSocketChannel({ appId: secrets.FEISHU_APP_ID, appSecret: secrets.FEISHU_APP_SECRET, ...policy }),
69
- );
45
+ // Each way's `needs` declares the env vars it reads: an instance is asked only for its way's, and
46
+ // refused to serve while a required one is unset. FEISHU_ENCRYPT_KEY is optional.
47
+ export default byIngress({
48
+ webhook: needs(
49
+ [
50
+ "FEISHU_APP_ID",
51
+ "FEISHU_APP_SECRET",
52
+ "FEISHU_VERIFICATION_TOKEN",
53
+ { name: "FEISHU_ENCRYPT_KEY", description: "when set, plaintext events are refused", optional: true },
54
+ ],
55
+ (secrets) =>
56
+ feishuChannel({
57
+ appId: secrets.FEISHU_APP_ID,
58
+ appSecret: secrets.FEISHU_APP_SECRET,
59
+ verificationToken: secrets.FEISHU_VERIFICATION_TOKEN, // authenticates inbound events
60
+ encryptKey: secrets.FEISHU_ENCRYPT_KEY,
61
+ ...policy,
62
+ }),
63
+ ),
64
+ connection: needs(["FEISHU_APP_ID", "FEISHU_APP_SECRET"], (secrets) =>
65
+ feishuWebSocketChannel({ appId: secrets.FEISHU_APP_ID, appSecret: secrets.FEISHU_APP_SECRET, ...policy }),
66
+ ),
67
+ });
@@ -1,4 +1,4 @@
1
- import { Type } from "@earendil-works/pi-ai";
1
+ import { obj, opt, str } from "@earendil-works/pi-schema";
2
2
  import { defineTool } from "@earendil-works/pi-durable";
3
3
  import { needs } from "@fastagent-sh/fastagent";
4
4
  import { feishuTransport } from "@fastagent-sh/fastagent/feishu";
@@ -18,10 +18,10 @@ export default needs(["FEISHU_APP_ID", "FEISHU_APP_SECRET"], (secrets) =>
18
18
  "or memory); the [feishu: chat …] context line only identifies the chat you are answering, the " +
19
19
  "one chat this tool must not target in a chat turn. Pass exactly ONE of `text` (plain) or " +
20
20
  "`markdown` (rendered as a card: headings, bold, code blocks, links).",
21
- parameters: Type.Object({
22
- chatId: Type.String({ description: "target chat id (oc_…)" }),
23
- text: Type.Optional(Type.String({ description: "plain text message to send" })),
24
- markdown: Type.Optional(Type.String({ description: "markdown to send as a card" })),
21
+ parameters: obj({
22
+ chatId: str({ description: "target chat id (oc_…)" }),
23
+ text: opt(str({ description: "plain text message to send" })),
24
+ markdown: opt(str({ description: "markdown to send as a card" })),
25
25
  }),
26
26
  execute: async ({ chatId, text, markdown }) => {
27
27
  if ((text === undefined) === (markdown === undefined)) {
@@ -1,4 +1,4 @@
1
- import { Type } from "@earendil-works/pi-ai";
1
+ import { obj, opt, str } from "@earendil-works/pi-schema";
2
2
  import { defineTool } from "@earendil-works/pi-durable";
3
3
  import { sessionIdOf } from "@fastagent-sh/fastagent";
4
4
  import { feishuThreads } from "@fastagent-sh/fastagent/feishu";
@@ -14,8 +14,8 @@ export default defineTool({
14
14
  "it when a question here concerns something discussed in a thread, such as whether a problem raised there was " +
15
15
  "resolved: the answer is often what a person wrote in the thread. Only this chat's threads can be read, and " +
16
16
  "only in a group chat turn.",
17
- parameters: Type.Object({
18
- threadId: Type.Optional(Type.String({ description: "a thread id from the list (omt_…); omit it to list" })),
17
+ parameters: obj({
18
+ threadId: opt(str({ description: "a thread id from the list (omt_…); omit it to list" })),
19
19
  }),
20
20
  execute: async ({ threadId }, api, context) => {
21
21
  const threads = feishuThreads(await sessionIdOf(api, context));
@@ -1,15 +1,5 @@
1
- import { type FeishuCloudKind } from "./cloud.ts";
2
1
  /** Feishu/Lark app-level event subscription choice: how a channel receives, and what an app is set up for. */
3
- export type FeishuSubscriptionMode = "webhook" | "websocket";
4
- /** The settings {@link feishuIngressFor} reads, one per cloud: what `deploy` must read the way the box will. */
5
- export declare const FEISHU_INGRESS_SETTINGS: string[];
6
- /**
7
- * How THIS process receives a Feishu/Lark channel (docs/design/channel-environments.md): `<PREFIX>_INGRESS` when the
8
- * environment names one, else WebSocket under `dev` (no public URL, no reviewed scope) and webhook everywhere else,
9
- * which is what every deployment runs. The channel file asks at import, and so does every reader of its shape: `deploy`
10
- * inspects it in a process that is not `dev`, with the same value file loaded, so it sees what the box will serve.
11
- */
12
- export declare function feishuIngressFor(kind: FeishuCloudKind, env?: NodeJS.ProcessEnv): FeishuSubscriptionMode;
2
+ export type FeishuSubscriptionMode = "webhook" | "connection";
13
3
  /**
14
4
  * Configuring THIS app through the v7 config API: registering a webhook Request URL and switching the subscription
15
5
  * mode. Requested for a webhook app only. A tenant may review it, and a scope under review holds the app's whole first
@@ -46,7 +36,7 @@ export declare const FEISHU_AGENT_SCOPES: FeishuScopeRequest[];
46
36
  export declare function feishuAppScopes(ingress: FeishuSubscriptionMode): FeishuScopeRequest[];
47
37
  /**
48
38
  * What a created app carries on top of the platform's agent template — the `addons` merged onto the confirm page. A
49
- * scope that arrives here needs no console visit; one a tenant withholds is reported after creation (`add-feishu.ts`).
39
+ * scope that arrives here needs no console visit; one a tenant withholds is reported after creation (`setup.ts`).
50
40
  */
51
41
  export declare function feishuAppAddons(ingress: FeishuSubscriptionMode): {
52
42
  scopes: {
@@ -1,23 +1,3 @@
1
- import { servedByDev } from "../../serving-command.js";
2
- import { cloudFor } from "./cloud.js";
3
- /** The settings {@link feishuIngressFor} reads, one per cloud: what `deploy` must read the way the box will. */
4
- export const FEISHU_INGRESS_SETTINGS = ["feishu", "lark"].map((kind) => `${cloudFor(kind).envPrefix}_INGRESS`);
5
- /**
6
- * How THIS process receives a Feishu/Lark channel (docs/design/channel-environments.md): `<PREFIX>_INGRESS` when the
7
- * environment names one, else WebSocket under `dev` (no public URL, no reviewed scope) and webhook everywhere else,
8
- * which is what every deployment runs. The channel file asks at import, and so does every reader of its shape: `deploy`
9
- * inspects it in a process that is not `dev`, with the same value file loaded, so it sees what the box will serve.
10
- */
11
- export function feishuIngressFor(kind, env = process.env) {
12
- const name = `${cloudFor(kind).envPrefix}_INGRESS`;
13
- const raw = env[name]?.trim();
14
- if (!raw)
15
- return servedByDev(env) ? "websocket" : "webhook";
16
- if (raw !== "webhook" && raw !== "websocket") {
17
- throw new Error(`${name} must be "webhook" or "websocket", got "${raw}"`);
18
- }
19
- return raw;
20
- }
21
1
  /**
22
2
  * Configuring THIS app through the v7 config API: registering a webhook Request URL and switching the subscription
23
3
  * mode. Requested for a webhook app only. A tenant may review it, and a scope under review holds the app's whole first
@@ -57,7 +37,7 @@ export const FEISHU_AGENT_SCOPES = [
57
37
  /** The config scope as an app needs it: webhook only ({@link FEISHU_APP_CONFIG_SCOPE}). */
58
38
  const FEISHU_APP_CONFIG_REQUEST = {
59
39
  request: FEISHU_APP_CONFIG_SCOPE,
60
- withoutIt: "the Verification Token is not captured and no Request URL is registered for you (add, dev --tunnel, deploy --run)",
40
+ withoutIt: "the Verification Token is not captured and no Request URL is registered for you (channels setup, dev --tunnel, deploy --run)",
61
41
  };
62
42
  /**
63
43
  * Every scope an app of this ingress needs: what onboarding requests at creation AND what it checks the app holds, so a
@@ -68,7 +48,7 @@ export function feishuAppScopes(ingress) {
68
48
  }
69
49
  /**
70
50
  * What a created app carries on top of the platform's agent template — the `addons` merged onto the confirm page. A
71
- * scope that arrives here needs no console visit; one a tenant withholds is reported after creation (`add-feishu.ts`).
51
+ * scope that arrives here needs no console visit; one a tenant withholds is reported after creation (`setup.ts`).
72
52
  */
73
53
  export function feishuAppAddons(ingress) {
74
54
  return {
@@ -0,0 +1,12 @@
1
+ /**
2
+ * What a Feishu or Lark app's setup asks for (channels/setup.ts). The two share one engine: the app's credentials come
3
+ * first (Feishu creates the app by scan-to-create and keeps what it returns; Lark's person creates it in its console and
4
+ * pastes them), then, for a webhook, the Verification Token, captured by a throwaway webhook registration when the app
5
+ * allows it and pasted when it does not. What is left to the person is the permission work and the publish, which
6
+ * depend on what the platform says about the app.
7
+ */
8
+ import { type ChannelSetup, type SetupValue } from "../setup.ts";
9
+ import { type FeishuCloudKind } from "./cloud.ts";
10
+ /** The setup of a Feishu or Lark app whose credentials are `credentials`. */
11
+ export declare function feishuEngine(kind: FeishuCloudKind, credentials: SetupValue, context: Parameters<ChannelSetup>[0]): ReturnType<ChannelSetup>;
12
+ export declare const feishuSetup: ChannelSetup;