@gjermundgaraba/effect-actions 0.19.0 → 0.21.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 (326) hide show
  1. package/README.md +19 -41
  2. package/dist/Action.d.mts +140 -48
  3. package/dist/Action.mjs +88 -44
  4. package/dist/ActionCli.d.mts +70 -35
  5. package/dist/ActionCli.mjs +53 -34
  6. package/dist/ActionHttp.d.mts +70 -38
  7. package/dist/ActionHttp.mjs +76 -25
  8. package/dist/ActionMcp.d.mts +56 -24
  9. package/dist/ActionMcp.mjs +104 -61
  10. package/dist/ActionRpc.d.mts +222 -57
  11. package/dist/ActionRpc.mjs +301 -49
  12. package/dist/ActionToolkit.d.mts +22 -7
  13. package/dist/ActionToolkit.mjs +37 -14
  14. package/dist/Authentication.d.mts +42 -12
  15. package/dist/Authentication.mjs +38 -32
  16. package/dist/Event.d.mts +370 -0
  17. package/dist/Event.mjs +103 -0
  18. package/dist/Testing.d.mts +1 -3
  19. package/dist/Testing.mjs +2 -3
  20. package/dist/Webhook.d.mts +146 -0
  21. package/dist/Webhook.mjs +72 -0
  22. package/dist/authentication/provider.d.mts +35 -12
  23. package/dist/authentication/provider.mjs +2 -3
  24. package/dist/authentication/refusal.mjs +3 -4
  25. package/dist/cli/commands.d.mts +9 -7
  26. package/dist/cli/commands.mjs +85 -35
  27. package/dist/contract/ast.mjs +5 -3
  28. package/dist/contract/authorizer.d.mts +8 -0
  29. package/dist/contract/authorizer.mjs +20 -0
  30. package/dist/contract/call.d.mts +9 -8
  31. package/dist/contract/call.mjs +49 -8
  32. package/dist/contract/callback.d.mts +7 -0
  33. package/dist/contract/callback.mjs +27 -0
  34. package/dist/contract/caller.d.mts +12 -3
  35. package/dist/contract/caller.mjs +23 -5
  36. package/dist/contract/canonical.mjs +10 -0
  37. package/dist/contract/codec.d.mts +9 -0
  38. package/dist/contract/codec.mjs +9 -0
  39. package/dist/contract/codes.mjs +81 -0
  40. package/dist/contract/declared.d.mts +0 -2
  41. package/dist/contract/declared.mjs +26 -11
  42. package/dist/contract/digest.mjs +6 -0
  43. package/dist/contract/errors.d.mts +9 -5
  44. package/dist/contract/errors.mjs +2 -3
  45. package/dist/contract/feed.d.mts +127 -0
  46. package/dist/contract/feed.mjs +249 -0
  47. package/dist/contract/hints.mjs +1 -2
  48. package/dist/contract/hub.d.mts +94 -0
  49. package/dist/contract/hub.mjs +133 -0
  50. package/dist/contract/idempotency.d.mts +47 -0
  51. package/dist/contract/idempotency.mjs +99 -0
  52. package/dist/contract/implementation.d.mts +73 -51
  53. package/dist/contract/implementation.mjs +94 -60
  54. package/dist/contract/input-check.d.mts +15 -0
  55. package/dist/contract/input-check.mjs +16 -0
  56. package/dist/contract/lease.mjs +28 -0
  57. package/dist/contract/listed.d.mts +4 -0
  58. package/dist/contract/listed.mjs +22 -0
  59. package/dist/contract/log.d.mts +41 -0
  60. package/dist/contract/log.mjs +144 -0
  61. package/dist/contract/media.d.mts +1 -3
  62. package/dist/contract/media.mjs +1 -2
  63. package/dist/contract/names.mjs +15 -6
  64. package/dist/contract/occurrence.d.mts +29 -0
  65. package/dist/contract/occurrence.mjs +45 -0
  66. package/dist/contract/pages.d.mts +13 -0
  67. package/dist/contract/pages.mjs +39 -0
  68. package/dist/contract/progress.d.mts +26 -0
  69. package/dist/contract/progress.mjs +72 -0
  70. package/dist/contract/push.d.mts +20 -0
  71. package/dist/contract/push.mjs +96 -0
  72. package/dist/contract/receiving.d.mts +29 -0
  73. package/dist/contract/receiving.mjs +42 -0
  74. package/dist/contract/recent.mjs +12 -0
  75. package/dist/contract/reported.mjs +16 -0
  76. package/dist/contract/serving.mjs +63 -0
  77. package/dist/contract/status.mjs +15 -7
  78. package/dist/contract/stream.d.mts +34 -0
  79. package/dist/contract/stream.mjs +31 -0
  80. package/dist/contract/subscribe.d.mts +45 -0
  81. package/dist/contract/subscribe.mjs +50 -0
  82. package/dist/contract/transient.mjs +28 -0
  83. package/dist/contract/watch.d.mts +18 -0
  84. package/dist/contract/watch.mjs +105 -0
  85. package/dist/contract/webhooks.d.mts +42 -0
  86. package/dist/contract/webhooks.mjs +53 -0
  87. package/dist/http/client.d.mts +37 -14
  88. package/dist/http/client.mjs +52 -14
  89. package/dist/http/events.d.mts +56 -0
  90. package/dist/http/events.mjs +102 -0
  91. package/dist/http/placed.d.mts +36 -0
  92. package/dist/http/placed.mjs +71 -0
  93. package/dist/http/schema-errors.mjs +2 -3
  94. package/dist/http/streamed.d.mts +21 -0
  95. package/dist/http/streamed.mjs +94 -0
  96. package/dist/http/watch.mjs +50 -0
  97. package/dist/mcp/bridge.mjs +283 -0
  98. package/dist/mcp/client.d.mts +30 -99
  99. package/dist/mcp/client.mjs +60 -134
  100. package/dist/mcp/events.mjs +142 -0
  101. package/dist/mcp/progress.mjs +34 -0
  102. package/dist/mcp/protocol.mjs +40 -15
  103. package/dist/mcp/reply.d.mts +79 -0
  104. package/dist/mcp/reply.mjs +99 -0
  105. package/dist/mcp/request.d.mts +40 -0
  106. package/dist/mcp/request.mjs +51 -0
  107. package/dist/mcp/watch.mjs +115 -0
  108. package/dist/rpc/watch.mjs +39 -0
  109. package/dist/stdio/console.mjs +14 -7
  110. package/dist/testing/memory.d.mts +1 -3
  111. package/dist/testing/memory.mjs +4 -5
  112. package/dist/webhook/address.mjs +47 -0
  113. package/dist/webhook/attempt.mjs +65 -0
  114. package/dist/webhook/receiver.mjs +67 -0
  115. package/dist/webhook/signing.mjs +28 -0
  116. package/dist/webhook/stored.mjs +57 -0
  117. package/dist/webhook/subscriptions.mjs +342 -0
  118. package/docs/Action-failures.md +2 -2
  119. package/docs/Action-streams.md +115 -0
  120. package/docs/Action.md +58 -124
  121. package/docs/ActionCli-failures.md +1 -2
  122. package/docs/ActionCli.md +83 -88
  123. package/docs/ActionHttp-failures.md +3 -3
  124. package/docs/ActionHttp.md +90 -70
  125. package/docs/ActionMcp-failures.md +2 -1
  126. package/docs/ActionMcp.md +120 -92
  127. package/docs/ActionRpc-events.md +126 -0
  128. package/docs/ActionRpc-failures.md +1 -2
  129. package/docs/ActionRpc.md +108 -79
  130. package/docs/ActionToolkit-failures.md +1 -2
  131. package/docs/ActionToolkit.md +33 -90
  132. package/docs/Authentication-advanced.md +57 -61
  133. package/docs/Authentication-failures.md +5 -4
  134. package/docs/Authentication.md +23 -58
  135. package/docs/Event-failures.md +15 -0
  136. package/docs/Event.md +281 -0
  137. package/docs/README.md +51 -82
  138. package/docs/Testing-failures.md +1 -5
  139. package/docs/Testing.md +126 -86
  140. package/docs/Webhook-failures.md +8 -0
  141. package/docs/Webhook.md +319 -0
  142. package/docs/setup-failures.md +2 -3
  143. package/docs/setup.md +32 -53
  144. package/examples/README.md +12 -37
  145. package/examples/assistant/README.md +21 -26
  146. package/examples/assistant/src/agent.ts +66 -24
  147. package/examples/assistant/src/cli.ts +7 -4
  148. package/examples/assistant/src/commands.ts +9 -4
  149. package/examples/assistant/src/identity/Identity.ts +5 -4
  150. package/examples/assistant/src/identity/authorize.ts +13 -5
  151. package/examples/assistant/src/identity/handlers.ts +3 -2
  152. package/examples/assistant/src/identity/permissions.ts +9 -3
  153. package/examples/assistant/src/platform/tasks-file.ts +15 -11
  154. package/examples/assistant/src/scripted.ts +7 -4
  155. package/examples/assistant/src/tasks/Tasks.ts +20 -6
  156. package/examples/assistant/src/tasks/handlers.ts +9 -5
  157. package/examples/assistant/src/tasks/store.ts +38 -11
  158. package/examples/assistant/src/toolkit.ts +11 -7
  159. package/examples/assistant/test/commands.test.ts +53 -27
  160. package/examples/assistant/test/identity/identity.test.ts +27 -11
  161. package/examples/assistant/test/platform/tasks-file.test.ts +39 -23
  162. package/examples/assistant/test/tasks/tasks.test.ts +62 -34
  163. package/examples/assistant/test/toolkit.test.ts +104 -75
  164. package/examples/build-events/README.md +83 -0
  165. package/examples/build-events/src/Api.ts +27 -0
  166. package/examples/build-events/src/Rpc.ts +26 -0
  167. package/examples/build-events/src/builds/Builds.ts +108 -0
  168. package/examples/build-events/src/builds/events.ts +54 -0
  169. package/examples/build-events/src/builds/handlers.ts +58 -0
  170. package/examples/build-events/src/builds/log.ts +30 -0
  171. package/examples/build-events/src/builds/status.ts +28 -0
  172. package/examples/build-events/src/builds/store.ts +130 -0
  173. package/examples/build-events/src/cli.ts +18 -0
  174. package/examples/build-events/src/commands.ts +32 -0
  175. package/examples/build-events/src/deploys/Deploys.ts +44 -0
  176. package/examples/build-events/src/deploys/approvers.ts +55 -0
  177. package/examples/build-events/src/deploys/events.ts +32 -0
  178. package/examples/build-events/src/deploys/handlers.ts +38 -0
  179. package/examples/build-events/src/identity/Identity.ts +29 -0
  180. package/examples/build-events/src/identity/members.ts +7 -0
  181. package/examples/build-events/src/identity/verifier.ts +22 -0
  182. package/examples/build-events/src/notifier/Notices.ts +26 -0
  183. package/examples/build-events/src/notifier/routes.ts +71 -0
  184. package/examples/build-events/src/notifier.ts +48 -0
  185. package/examples/build-events/src/routes.ts +71 -0
  186. package/examples/build-events/src/server.ts +62 -0
  187. package/examples/build-events/test/builds/events.test.ts +68 -0
  188. package/examples/build-events/test/builds/log.test.ts +71 -0
  189. package/examples/build-events/test/builds/progress.test.ts +57 -0
  190. package/examples/build-events/test/builds/status.test.ts +42 -0
  191. package/examples/build-events/test/commands.test.ts +103 -0
  192. package/examples/build-events/test/deploys/events.test.ts +52 -0
  193. package/examples/build-events/test/http.test.ts +95 -0
  194. package/examples/build-events/test/notifier.test.ts +80 -0
  195. package/examples/build-events/test/routes.test.ts +96 -0
  196. package/examples/build-events/test/rpc.test.ts +84 -0
  197. package/examples/build-events/test/support/as.ts +8 -0
  198. package/examples/build-events/test/support/memory.ts +43 -0
  199. package/examples/build-events/test/webhooks.test.ts +143 -0
  200. package/examples/kitchen-sink/README.md +178 -166
  201. package/examples/kitchen-sink/src/Actions.ts +11 -3
  202. package/examples/kitchen-sink/src/Api.ts +12 -6
  203. package/examples/kitchen-sink/src/Rpc.ts +7 -5
  204. package/examples/kitchen-sink/src/admin/Admin.ts +21 -10
  205. package/examples/kitchen-sink/src/admin/authenticate.ts +8 -4
  206. package/examples/kitchen-sink/src/admin/authorize.ts +13 -5
  207. package/examples/kitchen-sink/src/admin/grants.ts +5 -3
  208. package/examples/kitchen-sink/src/admin/handlers.ts +9 -5
  209. package/examples/kitchen-sink/src/agent.ts +14 -6
  210. package/examples/kitchen-sink/src/assistant/Assistant.ts +21 -15
  211. package/examples/kitchen-sink/src/assistant/conversations.ts +14 -9
  212. package/examples/kitchen-sink/src/assistant/demo-model.ts +2 -2
  213. package/examples/kitchen-sink/src/assistant/handlers.ts +42 -22
  214. package/examples/kitchen-sink/src/attachments/Attachments.ts +6 -6
  215. package/examples/kitchen-sink/src/attachments/download.ts +7 -6
  216. package/examples/kitchen-sink/src/attachments/handlers.ts +9 -4
  217. package/examples/kitchen-sink/src/attachments/store.ts +25 -18
  218. package/examples/kitchen-sink/src/browser.ts +10 -4
  219. package/examples/kitchen-sink/src/cli.ts +28 -12
  220. package/examples/kitchen-sink/src/config.ts +1 -1
  221. package/examples/kitchen-sink/src/identity/Identity.ts +24 -10
  222. package/examples/kitchen-sink/src/identity/authenticate.ts +19 -11
  223. package/examples/kitchen-sink/src/identity/authorize.ts +14 -4
  224. package/examples/kitchen-sink/src/identity/directory.ts +16 -7
  225. package/examples/kitchen-sink/src/identity/handlers.ts +1 -0
  226. package/examples/kitchen-sink/src/identity/members.ts +7 -2
  227. package/examples/kitchen-sink/src/identity/quota.ts +23 -20
  228. package/examples/kitchen-sink/src/issues/Issues.ts +91 -15
  229. package/examples/kitchen-sink/src/issues/assistant.ts +4 -2
  230. package/examples/kitchen-sink/src/issues/events.ts +46 -0
  231. package/examples/kitchen-sink/src/issues/export.ts +35 -0
  232. package/examples/kitchen-sink/src/issues/guide.ts +4 -3
  233. package/examples/kitchen-sink/src/issues/handlers.ts +20 -7
  234. package/examples/kitchen-sink/src/issues/store.ts +156 -67
  235. package/examples/kitchen-sink/src/issues/write-limit.ts +44 -18
  236. package/examples/kitchen-sink/src/memory.ts +11 -2
  237. package/examples/kitchen-sink/src/ops.ts +10 -5
  238. package/examples/kitchen-sink/src/request-policy.ts +25 -9
  239. package/examples/kitchen-sink/src/routes.ts +67 -27
  240. package/examples/kitchen-sink/src/sdk.ts +22 -12
  241. package/examples/kitchen-sink/src/seed.ts +15 -9
  242. package/examples/kitchen-sink/src/server.ts +24 -10
  243. package/examples/kitchen-sink/src/stdio.ts +9 -3
  244. package/examples/kitchen-sink/src/workspaces/Workspaces.ts +10 -7
  245. package/examples/kitchen-sink/src/workspaces/handlers.ts +3 -2
  246. package/examples/kitchen-sink/src/workspaces/registry.ts +16 -10
  247. package/examples/kitchen-sink/src/workspaces/tenant.ts +12 -5
  248. package/examples/kitchen-sink/test/admin/admin.test.ts +38 -23
  249. package/examples/kitchen-sink/test/assistant/assistant.test.ts +471 -299
  250. package/examples/kitchen-sink/test/attachments/attachments.test.ts +135 -94
  251. package/examples/kitchen-sink/test/identity/identity.test.ts +110 -82
  252. package/examples/kitchen-sink/test/identity/session.test.ts +65 -43
  253. package/examples/kitchen-sink/test/issues/assistant.test.ts +50 -31
  254. package/examples/kitchen-sink/test/issues/events.test.ts +273 -0
  255. package/examples/kitchen-sink/test/issues/export.test.ts +120 -0
  256. package/examples/kitchen-sink/test/issues/issues.test.ts +105 -75
  257. package/examples/kitchen-sink/test/request-policy.test.ts +130 -33
  258. package/examples/kitchen-sink/test/routes.test.ts +102 -57
  259. package/examples/kitchen-sink/test/support/as.ts +9 -0
  260. package/examples/kitchen-sink/test/support/scripted.ts +6 -5
  261. package/examples/kitchen-sink/test/support/services.ts +17 -0
  262. package/examples/live-rpc/README.md +33 -35
  263. package/examples/live-rpc/src/Rpc.ts +9 -5
  264. package/examples/live-rpc/src/browser.ts +39 -11
  265. package/examples/live-rpc/src/chat/Chat.ts +30 -16
  266. package/examples/live-rpc/src/chat/authorize.ts +2 -1
  267. package/examples/live-rpc/src/chat/events.ts +74 -0
  268. package/examples/live-rpc/src/chat/handlers.ts +13 -5
  269. package/examples/live-rpc/src/chat/room.ts +9 -5
  270. package/examples/live-rpc/src/chat/store.ts +51 -14
  271. package/examples/live-rpc/src/client.ts +22 -5
  272. package/examples/live-rpc/src/identity/Identity.ts +3 -3
  273. package/examples/live-rpc/src/identity/verifier.ts +8 -4
  274. package/examples/live-rpc/src/page.ts +58 -12
  275. package/examples/live-rpc/src/routes.ts +14 -10
  276. package/examples/live-rpc/src/server.ts +15 -5
  277. package/examples/live-rpc/test/browser.test.ts +127 -50
  278. package/examples/live-rpc/test/chat/chat.test.ts +215 -23
  279. package/examples/live-rpc/test/routes.test.ts +130 -46
  280. package/examples/live-rpc/test/server.test.ts +94 -33
  281. package/examples/mcp-tools/README.md +24 -38
  282. package/examples/mcp-tools/src/platform/fake-farm.ts +63 -24
  283. package/examples/mcp-tools/src/platform/print-farm.ts +59 -29
  284. package/examples/mcp-tools/src/printers/Printers.ts +18 -7
  285. package/examples/mcp-tools/src/printers/farm.ts +15 -5
  286. package/examples/mcp-tools/src/printers/handlers.ts +12 -6
  287. package/examples/mcp-tools/src/routes.ts +8 -6
  288. package/examples/mcp-tools/src/server.ts +12 -5
  289. package/examples/mcp-tools/src/stdio.ts +3 -2
  290. package/examples/mcp-tools/test/printers/printers.test.ts +132 -89
  291. package/examples/mcp-tools/test/routes.test.ts +140 -81
  292. package/examples/notes-service/README.md +30 -45
  293. package/examples/notes-service/src/Api.ts +1 -0
  294. package/examples/notes-service/src/browser.ts +15 -14
  295. package/examples/notes-service/src/cli.ts +8 -3
  296. package/examples/notes-service/src/commands.ts +46 -20
  297. package/examples/notes-service/src/identity/Identity.ts +2 -2
  298. package/examples/notes-service/src/identity/authorize.ts +6 -2
  299. package/examples/notes-service/src/identity/handlers.ts +3 -2
  300. package/examples/notes-service/src/identity/users.ts +2 -1
  301. package/examples/notes-service/src/identity/verifier.ts +12 -6
  302. package/examples/notes-service/src/notes/Notes.ts +46 -22
  303. package/examples/notes-service/src/notes/handlers.ts +18 -9
  304. package/examples/notes-service/src/notes/store.ts +70 -43
  305. package/examples/notes-service/src/routes.ts +7 -5
  306. package/examples/notes-service/src/server.ts +12 -5
  307. package/examples/notes-service/src/status/Status.ts +1 -1
  308. package/examples/notes-service/src/status/handlers.ts +5 -3
  309. package/examples/notes-service/src/stdio.ts +7 -3
  310. package/examples/notes-service/src/worker.ts +6 -3
  311. package/examples/notes-service/test/commands.test.ts +164 -91
  312. package/examples/notes-service/test/notes/as.ts +3 -1
  313. package/examples/notes-service/test/notes/idempotency.test.ts +97 -67
  314. package/examples/notes-service/test/notes/in-process.test.ts +26 -15
  315. package/examples/notes-service/test/notes/notes.test.ts +49 -31
  316. package/examples/notes-service/test/routes.test.ts +106 -71
  317. package/examples/notes-service/test/worker.test.ts +21 -13
  318. package/examples/quickstart/README.md +2 -6
  319. package/examples/quickstart/src/Api.ts +1 -0
  320. package/examples/quickstart/src/client.ts +6 -3
  321. package/examples/quickstart/src/greeting/Greeting.ts +1 -1
  322. package/examples/quickstart/src/greeting/handlers.ts +5 -2
  323. package/examples/quickstart/src/routes.ts +3 -2
  324. package/examples/quickstart/src/server.ts +6 -2
  325. package/examples/quickstart/test/routes.test.ts +3 -2
  326. package/package.json +19 -17
@@ -1,16 +1,12 @@
1
1
  # kitchen-sink
2
2
 
3
- The reference application: one multi-tenant issue tracker that uses every module of the library
4
- and nearly every option. Members of a workspace file, discuss and close issues, and attach
5
- screenshots, over HTTP, MCP, Effect RPC, a CLI and an assistant, in process and behind a route.
6
- Operators suspend workspaces on a second port, or from a console on the host.
3
+ The reference application: one multi-tenant issue tracker that uses every module of the library and nearly every option. Members of a workspace file, discuss and close issues, and attach screenshots, over HTTP, MCP, Effect RPC, a CLI and an assistant, in process and behind a route. Operators suspend workspaces on a second port, or from a console on the host.
7
4
 
8
5
  The [index](#index) maps each export and option to the file that shows it.
9
6
 
10
7
  ## Run it
11
8
 
12
- From the repository root, after `vp install` (from an installed package, see
13
- [Run one](../README.md#run-one)):
9
+ From the repository root, after `vp install` (from an installed package, see [Run one](../README.md#run-one)):
14
10
 
15
11
  ```sh
16
12
  node --import tsx examples/kitchen-sink/src/server.ts # members on 127.0.0.1:3000, operators on :3001
@@ -22,6 +18,8 @@ node --import tsx examples/kitchen-sink/src/cli.ts list-issues -n 5
22
18
  node --import tsx examples/kitchen-sink/src/cli.ts get-issue ACME-1
23
19
  echo "It crashes on an empty draft" | node --import tsx examples/kitchen-sink/src/cli.ts create-issue --title Crash
24
20
  node --import tsx examples/kitchen-sink/src/cli.ts ask-assistant --prompt "Close what is done." # answers with a call to approve
21
+ node --import tsx examples/kitchen-sink/src/cli.ts export-issues # each issue as it arrives, with progress on stderr
22
+ node --import tsx examples/kitchen-sink/src/cli.ts watch issue.changed # each change, until stopped
25
23
 
26
24
  # The admin port takes an operator key, not a member's token.
27
25
  curl -s 127.0.0.1:3001/admin/suspendWorkspace -H 'Content-Type: application/json' \
@@ -33,25 +31,19 @@ node --import tsx examples/kitchen-sink/src/agent.ts # the assis
33
31
  vp test examples/kitchen-sink
34
32
  ```
35
33
 
36
- `HOST`, `PORT`, `ADMIN_PORT`, `PUBLIC_URL` and `MCP_ORIGIN` configure the server; `TRACKER_URL`
37
- points the CLI and the seed at it. `stdio.ts` is an MCP server for a local agent to start as a
38
- subprocess. `sdk.ts` and `browser.ts` are imported by client code, not run. The OpenAPI
39
- document is at `/api/v1/openapi.json`, Swagger UI at `/docs`, the MCP endpoint at `/mcp`, the
40
- socket at `/rpc`.
41
-
42
- | Credential | Who | May |
43
- | ----------------------------------------- | -------------------------- | -------------------------------------- |
44
- | `Authorization: Bearer ada` | ada, in acme or globex | read and write in acme, read in globex |
45
- | `Authorization: Bearer linus` | linus, in acme | read |
46
- | `Authorization: Bearer grace` | grace, in globex | read and write |
47
- | `Cookie: tracker_session=ada-at-acme` | ada in acme, on `/rpc` | read and write |
48
- | `Cookie: tracker_session=grace-at-globex` | grace in globex, on `/rpc` | read and write |
49
- | `X-Operator-Key: ops-key` | operator `ops` | list, suspend and resume workspaces |
50
- | `X-Operator-Key: support-key` | operator `support` | list workspaces |
51
-
52
- A bearer request names its workspace in `X-Workspace: acme`. `kay` (token `kay`, session
53
- `kay-at-acme`) is a member of acme granted no scope yet. The session cookie also opens
54
- `GET /attachments/<id>`, the image for a page of the web app; a token holder calls `getAttachment`.
34
+ `HOST`, `PORT`, `ADMIN_PORT`, `PUBLIC_URL` and `MCP_ORIGIN` configure the server; `TRACKER_URL` points the CLI and the seed at it. `stdio.ts` is an MCP server for a local agent to start as a subprocess. `sdk.ts` and `browser.ts` are imported by client code, not run. The OpenAPI document is at `/api/v1/openapi.json`, Swagger UI at `/docs`, the MCP endpoint at `/mcp`, the socket at `/rpc`.
35
+
36
+ | Credential | Who | May |
37
+ | --- | --- | --- |
38
+ | `Authorization: Bearer ada` | ada, in acme or globex | read and write in acme, read in globex |
39
+ | `Authorization: Bearer linus` | linus, in acme | read |
40
+ | `Authorization: Bearer grace` | grace, in globex | read and write |
41
+ | `Cookie: tracker_session=ada-at-acme` | ada in acme, on `/rpc` | read and write |
42
+ | `Cookie: tracker_session=grace-at-globex` | grace in globex, on `/rpc` | read and write |
43
+ | `X-Operator-Key: ops-key` | operator `ops` | list, suspend and resume workspaces |
44
+ | `X-Operator-Key: support-key` | operator `support` | list workspaces |
45
+
46
+ A bearer request names its workspace in `X-Workspace: acme`. `kay` (token `kay`, session `kay-at-acme`) is a member of acme granted no scope yet. The session cookie also opens `GET /attachments/<id>`, the image for a page of the web app; a token holder calls `getAttachment`.
55
47
 
56
48
  ## Layout
57
49
 
@@ -71,10 +63,12 @@ src/
71
63
  │ ├── tenant.ts Tenant, and the router middleware that resolves it per request
72
64
  │ └── handlers.ts
73
65
  ├── issues/
74
- │ ├── Issues.ts contract: schemas, errors, five actions
75
- │ ├── store.ts IssueStore, scoped by workspace
76
- │ ├── write-limit.ts the write limit, wrapped around handlers
66
+ │ ├── Issues.ts contract: schemas, errors, five actions, exportIssues, issue.changed
67
+ │ ├── store.ts IssueStore, scoped by workspace, and its changes
68
+ │ ├── write-limit.ts the write and export limits, wrapped around handlers
77
69
  │ ├── handlers.ts Action.implement over the store
70
+ │ ├── export.ts exportIssues: a stream, with progress
71
+ │ ├── events.ts issue.changed: a log source fed by the store's changes
78
72
  │ ├── guide.ts a native MCP resource and prompt
79
73
  │ └── assistant.ts the actions as a model's Toolkit, with approval
80
74
  ├── assistant/ the model loop, served: a turn per request
@@ -112,9 +106,10 @@ src/
112
106
  test/
113
107
  ├── routes.test.ts the host's surfaces: one port over HTTP and MCP, then both ports
114
108
  ├── request-policy.test.ts raw requests to each port as a web handler
115
- ├── issues/ in process, with Action.client; the assistant's tools
109
+ ├── issues/ in process, with Action.client; the assistant's tools; the export over
110
+ │ HTTP and RPC; issue.changed in process and by webhook
116
111
  ├── assistant/ the assistant over HTTP, with a scripted model
117
- ├── support/ the scripted model the assistant's tests give the routes
112
+ ├── support/ the scripted model; the services the public routes need in memory
118
113
  ├── identity/ what authentication adds: tokens over HTTP, sessions over RPC
119
114
  ├── attachments/ images over HTTP and MCP; the download route
120
115
  └── admin/ the operators' grants; the trusted operator
@@ -122,175 +117,192 @@ test/
122
117
 
123
118
  ## Where to look
124
119
 
125
- | To see | Read |
126
- | ----------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------- |
127
- | How one request is authenticated, placed in a tenant, authorized | `src/identity/authenticate.ts`, `src/workspaces/tenant.ts`, `src/identity/authorize.ts`, `src/routes.ts` |
128
- | Two audiences on two ports over one set of services | `src/server.ts`, `src/routes.ts`, `test/routes.test.ts` |
129
- | Which actions each surface serves, and why one is no tool | `src/Actions.ts` |
130
- | A limit before decoding and a limit in the handlers | `src/identity/quota.ts`, `src/issues/write-limit.ts` |
131
- | An image for a model and a client (an action), and for a page (a route) | `src/attachments/` |
132
- | A caller the host names itself | `src/stdio.ts`, `src/ops.ts`, `src/agent.ts` |
133
- | A model loop behind a route, approvals answered in a later request | `src/assistant/handlers.ts`, `src/routes.ts` |
134
- | What to test in process and what over the wire | `test/issues/issues.test.ts`, `test/routes.test.ts` |
120
+ | To see | Read |
121
+ | --- | --- |
122
+ | How one request is authenticated, placed in a tenant, authorized | `src/identity/authenticate.ts`, `src/workspaces/tenant.ts`, `src/identity/authorize.ts`, `src/routes.ts` |
123
+ | Two audiences on two ports over one set of services | `src/server.ts`, `src/routes.ts`, `test/routes.test.ts` |
124
+ | Which actions each surface serves, and why one is no tool | `src/Actions.ts` |
125
+ | A limit before decoding and a limit in the handlers | `src/identity/quota.ts`, `src/issues/write-limit.ts` |
126
+ | An image for a model and a client (an action), and for a page (a route) | `src/attachments/` |
127
+ | A caller the host names itself | `src/stdio.ts`, `src/ops.ts`, `src/agent.ts` |
128
+ | A model loop behind a route, approvals answered in a later request | `src/assistant/handlers.ts`, `src/routes.ts` |
129
+ | What to test in process and what over the wire | `test/issues/issues.test.ts`, `test/routes.test.ts` |
135
130
 
136
131
  ## Index
137
132
 
138
- Each export and option the example uses, and where. A symbol in parentheses is the name to
139
- search for in the file.
133
+ Each export and option the example uses, and where. A symbol in parentheses is the name to search for in the file.
140
134
 
141
135
  ### Action
142
136
 
143
- | Export or option | File |
144
- | -------------------------------------------------------------- | ------------------------------------------------------------------------------------ |
145
- | `make`: `input`, `success`, `error`, `readOnly` | `src/issues/Issues.ts` |
146
- | `make`: `caller: Action.Anyone`, a public action | `src/workspaces/Workspaces.ts` (`GetStatus`) |
147
- | `make`: `caller`, an identity service | `src/identity/Identity.ts` (`CurrentMember`, `WhoAmI`) |
148
- | `make`: `mcp` (`title`, hints) | `src/issues/Issues.ts` (`CreateIssue`, `CloseIssue`) |
149
- | `make`: no `input` | `src/identity/Identity.ts` (`WhoAmI`) |
150
- | `make`: an optional and a defaulted input field | `src/issues/Issues.ts` (`ListIssues`) |
151
- | `make`: errors with `httpApiStatus` | `src/issues/Issues.ts` (`IssueNotFound`, `RateLimited`) |
152
- | `make`: a shared error list | `src/issues/Issues.ts` (`IssueWriteErrors`), `src/attachments/Attachments.ts` |
153
- | `make`: an option bundle, spread | `src/issues/Issues.ts` (`change`), `src/admin/Admin.ts` (`onWorkspace`) |
154
- | `Image`, in a success and in an input | `src/attachments/Attachments.ts` |
155
- | `byName` | `src/issues/guide.ts` (`tools`) |
156
- | `implement`: one action, a plain handler | `src/identity/handlers.ts` |
157
- | `implement`: one action, a builder | `src/workspaces/handlers.ts` |
158
- | `implement`: several actions sharing a builder | `src/issues/handlers.ts` |
159
- | Startup services and per-request services | `src/issues/handlers.ts` |
160
- | A rate limit in handlers, shared by several | `src/issues/write-limit.ts`, `src/issues/handlers.ts`, `src/attachments/handlers.ts` |
161
- | `implement`: `authorize`, a rule from `readOnly` | `src/identity/authorize.ts` |
162
- | `implement`: `authorize`, an Effect building it | `src/admin/authorize.ts` |
163
- | `allowAll` | `src/identity/handlers.ts` |
164
- | `Forbidden` with `scopes`, and without | `src/identity/authorize.ts`, `src/admin/authorize.ts` |
165
- | `Unauthenticated` | `src/identity/directory.ts`, `src/admin/authenticate.ts` |
166
- | `InvalidInput` with `issues`, from a handler | `src/attachments/handlers.ts` |
167
- | `layer`, one builder run for an implementation two ports serve | `src/routes.ts` (`builders`), `src/server.ts`, `test/routes.test.ts` |
168
- | `client` | `src/agent.ts`, `test/issues/issues.test.ts` |
169
- | `client`: `actions` | `test/issues/issues.test.ts` |
170
- | `Any`, an authorizer's parameter | `src/identity/authorize.ts` |
137
+ | Export or option | File |
138
+ | --- | --- |
139
+ | `make`: `input`, `success`, `error`, `readOnly` | `src/issues/Issues.ts` |
140
+ | `make`: `caller: Action.Anyone`, a public action | `src/workspaces/Workspaces.ts` (`GetStatus`) |
141
+ | `make`: `caller`, an identity service | `src/identity/Identity.ts` (`CurrentMember`, `WhoAmI`) |
142
+ | `make`: `mcp` (`title`, hints) | `src/issues/Issues.ts` (`CreateIssue`, `CloseIssue`) |
143
+ | `make`: no `input` | `src/identity/Identity.ts` (`WhoAmI`) |
144
+ | `make`: an optional and a defaulted input field | `src/issues/Issues.ts` (`ListIssues`) |
145
+ | `make`: errors with `httpApiStatus` | `src/issues/Issues.ts` (`IssueNotFound`, `RateLimited`) |
146
+ | `make`: a shared error list | `src/issues/Issues.ts` (`IssueWriteErrors`), `src/attachments/Attachments.ts` |
147
+ | `make`: an option bundle, spread | `src/issues/Issues.ts` (`change`), `src/admin/Admin.ts` (`onWorkspace`) |
148
+ | `make`: `progress`, and `Stream`, a success answering a stream | `src/issues/Issues.ts` (`ExportIssues`) |
149
+ | A handler returning a `Stream`, reporting progress | `src/issues/export.ts` |
150
+ | `Image`, in a success and in an input | `src/attachments/Attachments.ts` |
151
+ | `byName` | `src/issues/guide.ts` (`tools`) |
152
+ | `implement`: one action, a plain handler | `src/identity/handlers.ts` |
153
+ | `implement`: one action, a builder | `src/workspaces/handlers.ts` |
154
+ | `implement`: several actions sharing a builder | `src/issues/handlers.ts` |
155
+ | Startup services and per-request services | `src/issues/handlers.ts` |
156
+ | A rate limit in handlers, shared by several | `src/issues/write-limit.ts`, `src/issues/handlers.ts`, `src/attachments/handlers.ts` |
157
+ | `implement`: `authorize`, a rule from `readOnly` | `src/identity/authorize.ts` |
158
+ | `implement`: `authorize`, an Effect building it | `src/admin/authorize.ts` |
159
+ | `allowAll` | `src/identity/handlers.ts` |
160
+ | `Forbidden` with `scopes`, and without | `src/identity/authorize.ts`, `src/admin/authorize.ts` |
161
+ | `Unauthenticated` | `src/identity/directory.ts`, `src/admin/authenticate.ts` |
162
+ | `InvalidInput` with `issues`, from a handler | `src/attachments/handlers.ts` |
163
+ | `layer`, one builder run for an implementation two ports serve | `src/routes.ts` (`builders`), `src/server.ts`, `test/routes.test.ts` |
164
+ | `client` | `src/agent.ts`, `test/issues/issues.test.ts` |
165
+ | `client`: `actions` | `test/issues/issues.test.ts` |
166
+ | `Any`, an authorizer's parameter | `src/identity/authorize.ts` |
171
167
 
172
168
  ### Authentication
173
169
 
174
- | Export or option | File |
175
- | -------------------------------------------------------------- | --------------------------------------------------------------- |
176
- | `make`, Bearer, with a declared `error` (503) | `src/identity/Identity.ts` (`Login`, `IssuerUnavailable`) |
177
- | `make`: `security`, a cookie, for the same identity | `src/identity/Identity.ts` (`Session`) |
178
- | `make`: `security`, an API key in a header | `src/admin/Admin.ts` (`OperatorKey`) |
179
- | `layer`, a verifier built from startup services | `src/identity/authenticate.ts` |
180
- | `layer`, a plain verifier | `src/admin/authenticate.ts` |
181
- | `layer`: `protectedResource`, an Effect over `Config` | `src/identity/authenticate.ts` (`protectedResource`) |
182
- | `ProtectedResource` | `src/identity/authenticate.ts` |
183
- | A request service the verifier reads, and its order | `src/workspaces/tenant.ts`, `src/routes.ts` (`publicRoutes`) |
184
- | `protect`, a route of the host's own, by the cookie descriptor | `src/attachments/download.ts` |
185
- | A trusted identity no verifier returns | `src/admin/Admin.ts`, `src/admin/authenticate.ts`, `src/ops.ts` |
186
- | A verifier's declared error over MCP | `test/identity/identity.test.ts` |
187
- | A test replacing a verifier's service | `test/identity/identity.test.ts` (`issuerDown`) |
170
+ | Export or option | File |
171
+ | --- | --- |
172
+ | `make`, Bearer, with a declared `error` (503) | `src/identity/Identity.ts` (`Login`, `IssuerUnavailable`) |
173
+ | `make`: `security`, a cookie, for the same identity | `src/identity/Identity.ts` (`Session`) |
174
+ | `make`: `security`, an API key in a header | `src/admin/Admin.ts` (`OperatorKey`) |
175
+ | `layer`, a verifier built from startup services | `src/identity/authenticate.ts` |
176
+ | `layer`, a plain verifier | `src/admin/authenticate.ts` |
177
+ | `layer`: `protectedResource`, an Effect over `Config` | `src/identity/authenticate.ts` (`protectedResource`) |
178
+ | `ProtectedResource` | `src/identity/authenticate.ts` |
179
+ | A request service the verifier reads, and its order | `src/workspaces/tenant.ts`, `src/routes.ts` (`publicRoutes`) |
180
+ | `protect`, a route of the host's own, by the cookie descriptor | `src/attachments/download.ts` |
181
+ | A trusted identity no verifier returns | `src/admin/Admin.ts`, `src/admin/authenticate.ts`, `src/ops.ts` |
182
+ | A verifier's declared error over MCP | `test/identity/identity.test.ts` |
183
+ | A test replacing a verifier's service | `test/identity/identity.test.ts` (`issuerDown`) |
184
+ | `make`: `principal`, one subscriber per workspace | `src/identity/Identity.ts` (`Login`) |
185
+
186
+ ### Event
187
+
188
+ | Export or option | File |
189
+ | --- | --- |
190
+ | `make`: `description`, `input` that filters and shapes, `payload`, `caller` | `src/issues/Issues.ts` (`IssueChanged`) |
191
+ | `implement`: a log source fed by a keyed `emit`, `key` reading the caller, `match`, `transform`, `maxAge` | `src/issues/events.ts` |
192
+ | `implement`: `authorize`, one rule for actions and events | `src/identity/authorize.ts` |
193
+ | `watch` in process, `Forbidden` for a subscriber | `test/issues/events.test.ts` |
194
+
195
+ ### Webhook
196
+
197
+ | Export or option | File |
198
+ | --- | --- |
199
+ | `layer`, for MCP and HTTP alike | `src/routes.ts` (`publicRoutes`) |
200
+ | The `HttpClient` and `Dns` delivery needs | `src/server.ts`, `test/support/services.ts` |
201
+ | A delivery, as the subscriber sees it | `test/issues/events.test.ts` |
188
202
 
189
203
  ### ActionHttp
190
204
 
191
- | Export or option | File |
192
- | -------------------------------------------------------------- | --------------------------------------------------------------------------- |
193
- | `make`: `prefix`, `authentication`, `error` | `src/Api.ts` (`Http`) |
194
- | A second binding for a second identity | `src/Api.ts` (`AdminHttp`) |
195
- | One action in two bindings | `src/Api.ts` (`GetStatus`), `src/routes.ts` (`adminRoutes`) |
196
- | `layer`: `actions` | `src/routes.ts` (`api`) |
197
- | `layer`: `middleware` | `src/routes.ts` (`api`), `src/identity/quota.ts` (`HttpQuota`) |
198
- | Global router middleware, before authentication and discovery | `src/request-policy.ts` (`requestPolicy`), `src/routes.ts` (`publicRoutes`) |
199
- | `Http.api`, `Http.prefix`: the OpenAPI document and Swagger UI | `src/routes.ts` (`documentation`) |
200
- | A request body limit | `src/server.ts` |
201
- | Two `HttpRouter.serve`, what they share provided outside both | `src/server.ts` |
202
- | `client`: `baseUrl`, `transformClient` | `src/seed.ts` |
203
- | `fetchClient` | `src/sdk.ts` |
204
- | Declared errors handled by tag | `src/seed.ts`, `src/sdk.ts` |
205
+ | Export or option | File |
206
+ | --- | --- |
207
+ | `make`: `prefix`, `authentication`, `error` | `src/Api.ts` (`Http`) |
208
+ | A second binding for a second identity | `src/Api.ts` (`AdminHttp`) |
209
+ | One action in two bindings | `src/Api.ts` (`GetStatus`), `src/routes.ts` (`adminRoutes`) |
210
+ | `layer`: `actions` | `src/routes.ts` (`api`) |
211
+ | `layer`: `middleware` | `src/routes.ts` (`api`), `src/identity/quota.ts` (`HttpQuota`) |
212
+ | Global router middleware, before authentication and discovery | `src/request-policy.ts` (`requestPolicy`), `src/routes.ts` (`publicRoutes`) |
213
+ | `Http.api`, `Http.prefix`: the OpenAPI document and Swagger UI | `src/routes.ts` (`documentation`) |
214
+ | A request body limit | `src/server.ts` |
215
+ | Two `HttpRouter.serve`, what they share provided outside both | `src/server.ts` |
216
+ | `client`: `baseUrl`, `transformClient` | `src/seed.ts` |
217
+ | `fetchClient` | `src/sdk.ts` |
218
+ | Declared errors handled by tag | `src/seed.ts`, `src/sdk.ts` |
219
+ | An event at `events/<name>`, polled or streamed | `src/Api.ts` |
220
+ | `layer`: an event given to one layer of a binding | `src/routes.ts` (`events`, `api`) |
221
+ | A stream action's route and method, with `onProgress` | `test/issues/export.test.ts` |
222
+ | An event's `subscribe` route | `test/issues/events.test.ts` |
205
223
 
206
224
  ### ActionMcp
207
225
 
208
- | Export or option | File |
209
- | ------------------------------------------------ | ------------------------------------------------------------- |
210
- | `layerHttp`, public and protected tools together | `src/routes.ts` (`mcp`) |
211
- | `layerHttp`: `authentication`, `instructions` | `src/routes.ts` (`mcp`) |
212
- | `layerHttp`: `actions`, one action left off | `src/routes.ts` (`mcp`), `src/Actions.ts` (`Tools`) |
213
- | `layerHttp`: `allowedOrigins` | `src/routes.ts` (`mcp`), `test/routes.test.ts` |
214
- | `layerHttp`: `features`, a resource and a prompt | `src/issues/guide.ts` |
215
- | CORS for a browser MCP client | `src/request-policy.ts` (`cors`) |
216
- | `runStdio`, the host naming the caller | `src/stdio.ts` |
217
- | An image block from a media field | `src/attachments/Attachments.ts` (`GetAttachment`) |
218
- | `client`: `transformClient` | `test/routes.test.ts` |
219
- | `client`, decoding an image | `test/attachments/attachments.test.ts` |
220
- | `client`: `authentication`, a verifier's error | `test/identity/identity.test.ts` |
221
- | `request`: `headers`, `url` | `test/attachments/attachments.test.ts`, `test/routes.test.ts` |
226
+ | Export or option | File |
227
+ | --- | --- |
228
+ | `layerHttp`, public and protected tools together | `src/routes.ts` (`mcp`) |
229
+ | `layerHttp`: `authentication`, `instructions` | `src/routes.ts` (`mcp`) |
230
+ | `layerHttp`: `actions`, one action left off | `src/routes.ts` (`mcp`), `src/Actions.ts` (`Tools`) |
231
+ | `layerHttp`: `allowedOrigins` | `src/routes.ts` (`mcp`), `test/routes.test.ts` |
232
+ | `layerHttp`: `features`, a resource and a prompt | `src/issues/guide.ts` |
233
+ | CORS for a browser MCP client | `src/request-policy.ts` (`cors`) |
234
+ | `runStdio`, the host naming the caller | `src/stdio.ts` |
235
+ | An image block from a media field | `src/attachments/Attachments.ts` (`GetAttachment`) |
236
+ | `client`: `transformClient` | `test/routes.test.ts` |
237
+ | `client`, decoding an image | `test/attachments/attachments.test.ts` |
238
+ | `client`: `authentication`, a verifier's error | `test/identity/identity.test.ts` |
239
+ | `request`: `headers`, `url` | `test/attachments/attachments.test.ts`, `test/routes.test.ts` |
240
+ | `layerHttp`: an event, polled, streamed or by webhook | `src/routes.ts` (`mcp`) |
222
241
 
223
242
  ### ActionRpc
224
243
 
225
- | Export or option | File |
226
- | --------------------------------- | ------------------------------------------------------------- |
227
- | `make`: `authentication`, `error` | `src/Rpc.ts` |
228
- | `layer`: `middleware` | `src/routes.ts` (`rpc`), `src/identity/quota.ts` (`RpcQuota`) |
229
- | A WebSocket protocol | `src/server.ts` (`socket`) |
230
- | `client`, over a WebSocket | `src/browser.ts` |
231
- | A credential per message | `test/identity/session.test.ts` (`as`) |
244
+ | Export or option | File |
245
+ | --- | --- |
246
+ | `make`: `authentication`, `error` | `src/Rpc.ts` |
247
+ | `layer`: `middleware` | `src/routes.ts` (`rpc`), `src/identity/quota.ts` (`RpcQuota`) |
248
+ | A WebSocket protocol | `src/server.ts` (`socket`) |
249
+ | `client`, over a WebSocket | `src/browser.ts` |
250
+ | A credential per message | `test/identity/session.test.ts` (`as`) |
251
+ | An event as a stream rpc | `src/Rpc.ts` |
252
+ | A stream action's method | `test/issues/export.test.ts` |
232
253
 
233
254
  ### ActionToolkit
234
255
 
235
- | Export or option | File |
236
- | --------------------------------------------------------- | ------------------------------------ |
237
- | `make`: `actions`, `needsApproval` (a boolean rule) | `src/issues/assistant.ts` |
238
- | `toolkit` given to `LanguageModel` | `src/issues/assistant.ts` (`triage`) |
239
- | `layer`, and the caller per turn | `src/agent.ts` |
240
- | `toolkit` and the model yielded in a builder | `src/assistant/handlers.ts` |
241
- | `layer` provided to the routes | `src/routes.ts` (`assistantTools`) |
242
- | An approval answered in a later request (`openApprovals`) | `src/assistant/handlers.ts` |
243
- | `tools.handle`, a call made by hand | `test/issues/assistant.test.ts` |
256
+ | Export or option | File |
257
+ | --- | --- |
258
+ | `make`: `actions`, `needsApproval` (a boolean rule) | `src/issues/assistant.ts` |
259
+ | `toolkit` given to `LanguageModel` | `src/issues/assistant.ts` (`triage`) |
260
+ | `layer`, and the caller per turn | `src/agent.ts` |
261
+ | `toolkit` and the model yielded in a builder | `src/assistant/handlers.ts` |
262
+ | `layer` provided to the routes | `src/routes.ts` (`assistantTools`) |
263
+ | An approval answered in a later request (`openApprovals`) | `src/assistant/handlers.ts` |
264
+ | `tools.handle`, a call made by hand | `test/issues/assistant.test.ts` |
244
265
 
245
266
  ### ActionCli
246
267
 
247
- | Export or option | File |
248
- | ----------------------------------------------------------------------------- | ------------------------------------------ |
249
- | `remote`: `name`, `commands`, `client` | `src/cli.ts` |
250
- | `commands`: `stdin`, `positional`, `aliases`, `render`, `name`, `description` | `src/cli.ts` |
251
- | A client from `Config`, its credentials optional | `src/cli.ts` |
252
- | `make`, with a trusted identity the host supplies | `src/ops.ts` |
253
- | `logToStderr` | `src/cli.ts`, `src/ops.ts`, `src/stdio.ts` |
268
+ | Export or option | File |
269
+ | --- | --- |
270
+ | `remote`: `name`, `commands`, `client` | `src/cli.ts` |
271
+ | `commands`: `stdin`, `positional`, `aliases`, `render`, `name`, `description` | `src/cli.ts` |
272
+ | A client from `Config`, its credentials optional | `src/cli.ts` |
273
+ | `make`, with a trusted identity the host supplies | `src/ops.ts` |
274
+ | `logToStderr` | `src/cli.ts`, `src/ops.ts`, `src/stdio.ts` |
275
+ | `render` of each element of a stream action | `src/cli.ts` (`exportIssues`) |
276
+ | `watch <event>`, derived from the binding | `src/cli.ts` |
254
277
 
255
278
  ### Testing
256
279
 
257
- | Export or option | File |
258
- | ------------------------------------------------ | ----------------------------------------------------------- |
259
- | `layer(routes)` | `test/identity/identity.test.ts` |
260
- | `layer(routes)`, twice, over shared builders | `test/routes.test.ts` (`shared`) |
261
- | `layer(handler)`, over `HttpRouter.toWebHandler` | `test/request-policy.test.ts` |
262
- | RPC in memory, over Effect's HTTP protocol | `test/identity/session.test.ts` |
263
- | A service the test and the routes both read | `test/identity/session.test.ts` (`served`) |
264
- | A test replacing one service | `test/identity/identity.test.ts` (`servedWith`) |
265
- | `Action.client`, a caller per call | `test/issues/issues.test.ts` |
266
- | A scripted `LanguageModel` | `test/issues/assistant.test.ts`, `test/support/scripted.ts` |
267
- | A test replacing the model the routes are given | `test/assistant/assistant.test.ts` (`served`) |
268
- | An error's message asserted beside its fields | `test/issues/issues.test.ts` |
280
+ | Export or option | File |
281
+ | --- | --- |
282
+ | `layer(routes)` | `test/identity/identity.test.ts` |
283
+ | `layer(routes)`, twice, over shared builders | `test/routes.test.ts` (`shared`) |
284
+ | `layer(handler)`, over `HttpRouter.toWebHandler` | `test/request-policy.test.ts` |
285
+ | RPC in memory, over Effect's HTTP protocol | `test/identity/session.test.ts` |
286
+ | A service the test and the routes both read | `test/identity/session.test.ts` (`served`) |
287
+ | A test replacing one service | `test/identity/identity.test.ts` (`servedWith`) |
288
+ | `Action.client`, a caller per call | `test/issues/issues.test.ts` |
289
+ | A scripted `LanguageModel` | `test/issues/assistant.test.ts`, `test/support/scripted.ts` |
290
+ | A test replacing the model the routes are given | `test/assistant/assistant.test.ts` (`served`) |
291
+ | An error's message asserted beside its fields | `test/issues/issues.test.ts` |
269
292
 
270
293
  ## Not shown
271
294
 
272
295
  Left out because this application has no honest place for them:
273
296
 
274
- - `Authentication.refusalResponse` and `bearerTokenOf`. They serve a caller the router never
275
- routes, such as a Node `upgrade` handler. Every caller here is routed: the socket is Effect's
276
- own WebSocket route, authenticated per message.
297
+ - `Authentication.refusalResponse` and `bearerTokenOf`. They serve a caller the router never routes, such as a Node `upgrade` handler. Every caller here is routed: the socket is Effect's own WebSocket route, authenticated per message.
277
298
  - A descriptor with `HttpApiSecurity.basic`, and an API key in the query.
278
- - `Action.implement` with a plain record of handlers: every implementation of several actions
279
- here has a builder. `implementation.actions`. `mcp._meta`.
280
- - `ActionCli.command` and `remoteCommand`, one action as one command; `make`'s and `remote`'s
281
- `actions`. Both CLIs here are aggregates of every action, and the remote one reads its client
282
- from `Config`. `subcommands` and `toUserError`, a command written by hand:
283
- [notes-service](../notes-service/) has two.
299
+ - `Action.implement` with a plain record of handlers: every implementation of several actions here has a builder. `implementation.actions`. `mcp._meta`.
300
+ - `ActionCli.command` and `remoteCommand`, one action as one command; `make`'s and `remote`'s `actions`. Both CLIs here are aggregates of every action, and the remote one reads its client from `Config`. `subcommands` and `toUserError`, a command written by hand: [notes-service](../notes-service/) has two.
284
301
  - `ActionRpc.layer`'s `actions`, `Rpc.group`, and `ActionRpc.client`'s options.
285
302
  - A binding's other fields, `Http.actions`, `Http.error` and `Http.authentication`.
286
- - `ActionMcp`'s `path`, `description`, `websiteUrl`, `icons`, `extensions`, `allowSubscriptions`
287
- and `requestScoped` ([notes-service](../notes-service/) has it), and `McpCallError`;
288
- `fetchClient`'s `fetch`.
303
+ - `ActionMcp`'s `path`, `description`, `websiteUrl`, `icons`, `extensions`, `allowSubscriptions` and `requestScoped` ([notes-service](../notes-service/) has it), and `McpCallError`; `fetchClient`'s `fetch`.
289
304
  - `needsApproval` as an Effect reading the caller: the rule here is a boolean of the action.
290
- - `Action.InvalidInput.fromSchemaError`, the `Action.Refusal` and `Action.BuiltIn` schemas, and the
291
- helper types: `Action.Issue`, `Errors`, `Handlers`, `Client`, `AnyImplementation`,
292
- `BuildServices`, `ActionHttp.Client`, `Authentication.Any`.
293
- - A handler sending MCP notifications through `McpServer.McpServer`, and one reading
294
- `McpSchema.McpRequestContext`.
295
- - A tenant taken from a subdomain. The workspace is a header, so the OAuth resource the server
296
- publishes is one fixed URL.
305
+ - `Action.InvalidInput.fromSchemaError`, the `Action.Refusal` and `Action.BuiltIn` schemas, and the helper types: `Action.Issue`, `Errors`, `Handlers`, `Client`, `AnyImplementation`, `BuildServices`, `ActionHttp.Client`, `Authentication.Any`.
306
+ - A handler sending MCP notifications through `McpServer.McpServer`, and one reading `McpSchema.McpRequestContext`.
307
+ - An event's `check` source and `subscription`, and a watch's `resume` and `mode`; and `Webhook.delivery`, `layerReceiver` and `persist`: [build-events](../build-events/) has them. No example sets a `subscription`'s `lease` or an occurrence's `timestamp`: the defaults (5 minutes, and when the occurrence arrived) serve both.
308
+ - A tenant taken from a subdomain. The workspace is a header, so the OAuth resource the server publishes is one fixed URL.
@@ -1,18 +1,26 @@
1
1
  import { Assistant } from "./assistant/Assistant.js";
2
2
  import { AttachImage, GetAttachment } from "./attachments/Attachments.js";
3
3
  import { WhoAmI } from "./identity/Identity.js";
4
- import { Issues } from "./issues/Issues.js";
4
+ import { ExportIssues, Issues } from "./issues/Issues.js";
5
5
  import { GetStatus } from "./workspaces/Workspaces.js";
6
6
 
7
7
  // Which actions each surface gets. A contract says nothing about where it is served: each
8
8
  // surface takes a list, and these are the lists, built from the contracts themselves.
9
9
 
10
10
  /** What a signed-in member may call: every protected action of the public port. */
11
- export const MemberActions = [WhoAmI, ...Issues, AttachImage, GetAttachment, ...Assistant] as const;
11
+ export const MemberActions = [
12
+ WhoAmI,
13
+ ...Issues,
14
+ ExportIssues,
15
+ AttachImage,
16
+ GetAttachment,
17
+ ...Assistant,
18
+ ] as const;
12
19
 
13
20
  /**
14
21
  * The MCP tools, for the HTTP endpoint and the stdio server alike. `attachImage` is left out:
15
22
  * listing it throws, since MCP takes no image as input. So is the assistant: it is a model
16
- * itself, and its tools are already here.
23
+ * itself, and its tools are already here. So is `exportIssues`: a tool collects its stream
24
+ * whole before answering, every issue of the workspace at once in a model's context.
17
25
  */
18
26
  export const Tools = [GetStatus, WhoAmI, ...Issues, GetAttachment] as const;
@@ -1,18 +1,24 @@
1
1
  import * as ActionHttp from "@gjermundgaraba/effect-actions/ActionHttp";
2
+
2
3
  import { MemberActions } from "./Actions.js";
3
4
  import { Admin, OperatorKey } from "./admin/Admin.js";
4
5
  import { Login, QuotaExceeded } from "./identity/Identity.js";
6
+ import { IssueChanged } from "./issues/Issues.js";
5
7
  import { GetStatus } from "./workspaces/Workspaces.js";
6
8
 
7
- // The members' HTTP binding: every action at `POST /api/v1/<name>`. It is plain data and
9
+ // The members' HTTP binding: every action at `POST /api/v1/<name>`, and `issue.changed` at
10
+ // `POST /api/v1/events/issue.changed`, polled or held open as a stream. It is plain data and
8
11
  // imports no server code, so the server, the CLI, the SDK and a test all import this one value.
9
12
  // - `authentication`: how the protected actions' caller proves who it is. `getStatus` is public.
10
13
  // - `error`: the quota middleware's, so a client decodes its 429 as a typed `QuotaExceeded`.
11
- export const Http = ActionHttp.make([GetStatus, ...MemberActions], {
12
- prefix: "/api/v1",
13
- authentication: Login,
14
- error: QuotaExceeded,
15
- });
14
+ export const Http = ActionHttp.make(
15
+ [GetStatus, ...MemberActions, IssueChanged],
16
+ {
17
+ prefix: "/api/v1",
18
+ authentication: Login,
19
+ error: QuotaExceeded,
20
+ }
21
+ );
16
22
 
17
23
  // The operators' binding: other actions, another identity, another descriptor, served on a port
18
24
  // of its own.
@@ -1,12 +1,14 @@
1
1
  import * as ActionRpc from "@gjermundgaraba/effect-actions/ActionRpc";
2
+
2
3
  import { MemberActions } from "./Actions.js";
3
4
  import { QuotaExceeded, Session } from "./identity/Identity.js";
5
+ import { IssueChanged } from "./issues/Issues.js";
4
6
 
5
- // The web app's binding: the members' actions as Effect RPC, one native rpc per action, over
6
- // one WebSocket. Browser-safe, as the HTTP binding is. It names the other descriptor of the
7
- // same identity: a page proves who it is with its session cookie, which the browser sends on
8
- // the socket's upgrade. Its `error` is what the layer's middleware, the quota, fails with.
9
- export const Rpc = ActionRpc.make(MemberActions, {
7
+ // The web app's binding: the members' actions as Effect RPC, one native rpc per action, and
8
+ // `issue.changed` as a stream rpc, over one WebSocket. Browser-safe, as the HTTP binding is. It
9
+ // names the other descriptor of the same identity: a page proves who it is with its session
10
+ // cookie, which the browser sends on the socket's upgrade. Its `error` is what the layer's middleware, the quota, fails with.
11
+ export const Rpc = ActionRpc.make([...MemberActions, IssueChanged], {
10
12
  authentication: Session,
11
13
  error: QuotaExceeded,
12
14
  });
@@ -1,7 +1,8 @@
1
- import { Context, Schema } from "effect";
2
- import { HttpApiSecurity } from "effect/http-api";
3
1
  import * as Action from "@gjermundgaraba/effect-actions/Action";
4
2
  import * as Authentication from "@gjermundgaraba/effect-actions/Authentication";
3
+ import { Context, Schema } from "effect";
4
+ import { HttpApiSecurity } from "effect/http-api";
5
+
5
6
  import { Workspace, WorkspaceNotFound } from "../workspaces/Workspaces.js";
6
7
 
7
8
  /** What a verified operator key can name. */
@@ -20,15 +21,20 @@ export interface TrustedOperator {
20
21
  * The operators' identity, another audience than the members: its own service, its own
21
22
  * descriptor, its own port. Typed in two parts, so the trusted one cannot arrive over the wire.
22
23
  */
23
- export class Operator extends Context.Service<Operator, KeyOperator | TrustedOperator>()(
24
- "tracker/Operator",
25
- ) {}
24
+ export class Operator extends Context.Service<
25
+ Operator,
26
+ KeyOperator | TrustedOperator
27
+ >()("tracker/Operator") {}
26
28
 
27
29
  // How a remote operator proves it: an API key in a header, a native scheme named by
28
30
  // `security`. A key cannot step up, and has no OAuth discovery.
29
- export const OperatorKey = Authentication.make("tracker.OperatorKey", Operator, {
30
- security: HttpApiSecurity.apiKey({ in: "header", key: "x-operator-key" }),
31
- });
31
+ export const OperatorKey = Authentication.make(
32
+ "tracker.OperatorKey",
33
+ Operator,
34
+ {
35
+ security: HttpApiSecurity.apiKey({ in: "header", key: "x-operator-key" }),
36
+ }
37
+ );
32
38
 
33
39
  // What the two actions on one workspace share, their input and success included.
34
40
  const onWorkspace = {
@@ -48,7 +54,8 @@ export const ListWorkspaces = Action.make("listWorkspaces", {
48
54
 
49
55
  export const SuspendWorkspace = Action.make("suspendWorkspace", {
50
56
  ...onWorkspace,
51
- description: "Suspend a workspace: its members are refused on the public port until resumed.",
57
+ description:
58
+ "Suspend a workspace: its members are refused on the public port until resumed.",
52
59
  });
53
60
 
54
61
  export const ResumeWorkspace = Action.make("resumeWorkspace", {
@@ -56,4 +63,8 @@ export const ResumeWorkspace = Action.make("resumeWorkspace", {
56
63
  description: "Resume a suspended workspace.",
57
64
  });
58
65
 
59
- export const Admin = [ListWorkspaces, SuspendWorkspace, ResumeWorkspace] as const;
66
+ export const Admin = [
67
+ ListWorkspaces,
68
+ SuspendWorkspace,
69
+ ResumeWorkspace,
70
+ ] as const;