iterate 0.3.0 → 0.4.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 (241) hide show
  1. package/README.md +164 -82
  2. package/dist/{next/api.d.ts → api.d.ts} +197 -33
  3. package/dist/{next/app-server.d.ts → app-server.d.ts} +7 -0
  4. package/dist/{next/app-server.mjs → app-server.mjs} +17 -15
  5. package/dist/app-server.mjs.map +1 -0
  6. package/dist/{next/app-session.mjs → app-session.mjs} +12 -15
  7. package/dist/app-session.mjs.map +1 -0
  8. package/dist/{next/app.mjs → app.mjs} +53 -14
  9. package/dist/app.mjs.map +1 -0
  10. package/dist/{next/client → client}/live-state.d.ts +11 -11
  11. package/dist/client/oauth.d.ts +17 -0
  12. package/dist/{next/client → client}/react.d.ts +10 -42
  13. package/dist/{next/client → client}/socket.d.ts +1 -0
  14. package/dist/client.mjs +156 -4
  15. package/dist/client.mjs.map +1 -0
  16. package/dist/{next/expression.d.ts → expression.d.ts} +11 -69
  17. package/dist/{next/expression.mjs → expression.mjs} +11 -109
  18. package/dist/expression.mjs.map +1 -0
  19. package/dist/lib-BWr-5mFO.mjs +36 -0
  20. package/dist/lib-BWr-5mFO.mjs.map +1 -0
  21. package/dist/{next/lib.d.ts → lib.d.ts} +16 -2
  22. package/dist/{next/lib.mjs → lib.mjs} +32 -3
  23. package/dist/lib.mjs.map +1 -0
  24. package/dist/node.d.ts +15 -3
  25. package/dist/node.mjs +36 -174
  26. package/dist/node.mjs.map +1 -1
  27. package/dist/{next/oauth-scopes.mjs → oauth-scopes.mjs} +1 -1
  28. package/dist/oauth-scopes.mjs.map +1 -0
  29. package/dist/{next/oauth.mjs → oauth.mjs} +14 -2
  30. package/dist/oauth.mjs.map +1 -0
  31. package/dist/principal.d.ts +8 -0
  32. package/dist/principal.mjs +8 -0
  33. package/dist/principal.mjs.map +1 -0
  34. package/dist/project-ingress.d.ts +58 -0
  35. package/dist/project-ingress.mjs +104 -0
  36. package/dist/project-ingress.mjs.map +1 -0
  37. package/dist/{next/react.mjs → react.mjs} +12 -12
  38. package/dist/react.mjs.map +1 -0
  39. package/dist/sdk/auth.d.ts +25 -0
  40. package/dist/sdk/index.d.ts +155 -0
  41. package/dist/sdk/record-pipelined-steps.d.ts +19 -0
  42. package/dist/sdk.mjs +245 -2
  43. package/dist/sdk.mjs.map +1 -0
  44. package/dist/{next/stream → stream}/processor.d.ts +28 -23
  45. package/dist/{next/stream → stream}/processor.mjs +48 -25
  46. package/dist/stream/processor.mjs.map +1 -0
  47. package/dist/{next/stream → stream}/run.d.ts +9 -6
  48. package/dist/{next/stream → stream}/run.mjs +13 -8
  49. package/dist/stream/run.mjs.map +1 -0
  50. package/dist/stream/test-support.d.ts +45 -0
  51. package/dist/stream/test-support.mjs +196 -0
  52. package/dist/stream/test-support.mjs.map +1 -0
  53. package/package.json +65 -219
  54. package/THIRD_PARTY_NOTICES.md +0 -55
  55. package/bin/iterate.js +0 -94
  56. package/dist/api-url-B6404M82.mjs +0 -17
  57. package/dist/api-url-B6404M82.mjs.map +0 -1
  58. package/dist/app-ref-BipL0feU.mjs +0 -35
  59. package/dist/app-ref-BipL0feU.mjs.map +0 -1
  60. package/dist/app-ref-C1CrgXqX.mjs +0 -7
  61. package/dist/app-ref-C1CrgXqX.mjs.map +0 -1
  62. package/dist/app-ref-DYai_om1.mjs +0 -7
  63. package/dist/app-ref-DYai_om1.mjs.map +0 -1
  64. package/dist/cli-D0c-pDL_.mjs +0 -1010
  65. package/dist/cli-D0c-pDL_.mjs.map +0 -1
  66. package/dist/client.d.ts +0 -3
  67. package/dist/cloudflare-BTm90gQ4.mjs +0 -951
  68. package/dist/cloudflare-BTm90gQ4.mjs.map +0 -1
  69. package/dist/contract-s4FW4eES.mjs +0 -309
  70. package/dist/contract-s4FW4eES.mjs.map +0 -1
  71. package/dist/document-review/index.d.ts +0 -5
  72. package/dist/document-review/types.d.ts +0 -107
  73. package/dist/document-review.mjs +0 -7015
  74. package/dist/document-review.mjs.map +0 -1
  75. package/dist/durable-object-processor-durability-CNsTjAJS.mjs +0 -205
  76. package/dist/durable-object-processor-durability-CNsTjAJS.mjs.map +0 -1
  77. package/dist/idempotency-DleloJNt.mjs +0 -28
  78. package/dist/idempotency-DleloJNt.mjs.map +0 -1
  79. package/dist/index.d.mts +0 -5
  80. package/dist/index.mjs +0 -8
  81. package/dist/index.mjs.map +0 -1
  82. package/dist/itx/api-url.d.ts +0 -6
  83. package/dist/itx/itx-node-client.d.ts +0 -65
  84. package/dist/itx/itx-session.d.ts +0 -215
  85. package/dist/itx/owned-rpc-session.d.ts +0 -14
  86. package/dist/itx/query-client.d.ts +0 -10
  87. package/dist/itx-api.generated.d.ts +0 -6195
  88. package/dist/itx-session-sjud8GiT.mjs +0 -534
  89. package/dist/itx-session-sjud8GiT.mjs.map +0 -1
  90. package/dist/live-state-BJNqOwFw.mjs +0 -299
  91. package/dist/live-state-BJNqOwFw.mjs.map +0 -1
  92. package/dist/next/app-server.mjs.map +0 -1
  93. package/dist/next/app-session.mjs.map +0 -1
  94. package/dist/next/app.mjs.map +0 -1
  95. package/dist/next/client/oauth.d.ts +0 -12
  96. package/dist/next/client.mjs +0 -156
  97. package/dist/next/client.mjs.map +0 -1
  98. package/dist/next/expression.mjs.map +0 -1
  99. package/dist/next/lib.mjs.map +0 -1
  100. package/dist/next/oauth-scopes.mjs.map +0 -1
  101. package/dist/next/oauth.mjs.map +0 -1
  102. package/dist/next/principal.d.ts +0 -64
  103. package/dist/next/principal.mjs +0 -98
  104. package/dist/next/principal.mjs.map +0 -1
  105. package/dist/next/project-ingress.d.ts +0 -37
  106. package/dist/next/project-ingress.mjs +0 -75
  107. package/dist/next/project-ingress.mjs.map +0 -1
  108. package/dist/next/react.mjs.map +0 -1
  109. package/dist/next/sdk/auth.d.ts +0 -5
  110. package/dist/next/sdk/index.d.ts +0 -112
  111. package/dist/next/sdk.mjs +0 -139
  112. package/dist/next/sdk.mjs.map +0 -1
  113. package/dist/next/stream/processor.mjs.map +0 -1
  114. package/dist/next/stream/run.mjs.map +0 -1
  115. package/dist/next-node.d.ts +0 -15
  116. package/dist/next-node.mjs +0 -51
  117. package/dist/next-node.mjs.map +0 -1
  118. package/dist/processor-host-capabilities-BMFH3KTM.mjs +0 -56
  119. package/dist/processor-host-capabilities-BMFH3KTM.mjs.map +0 -1
  120. package/dist/processors/cloudflare.d.ts +0 -3
  121. package/dist/processors/durable-object-processor-durability.d.ts +0 -79
  122. package/dist/processors/event-consumption-metrics.d.ts +0 -82
  123. package/dist/processors/idempotency.d.ts +0 -13
  124. package/dist/processors/index.d.ts +0 -12
  125. package/dist/processors/processor-contracts.d.ts +0 -342
  126. package/dist/processors/processor-facet.d.ts +0 -186
  127. package/dist/processors/processor-host-capabilities.d.ts +0 -60
  128. package/dist/processors/prompt-sections.d.ts +0 -17
  129. package/dist/processors/rpc-types.d.ts +0 -515
  130. package/dist/processors/schemas.d.ts +0 -102
  131. package/dist/processors/stream-handle.d.ts +0 -45
  132. package/dist/processors/stream-processor-keepalive.d.ts +0 -95
  133. package/dist/processors/stream-processor-registry.d.ts +0 -233
  134. package/dist/processors/stream-processor-runner.d.ts +0 -289
  135. package/dist/processors/stream-processor.d.ts +0 -339
  136. package/dist/processors/stream-runtime-metrics.d.ts +0 -107
  137. package/dist/processors/testing.d.ts +0 -302
  138. package/dist/processors-BoNyeBfQ.mjs +0 -10
  139. package/dist/processors-BoNyeBfQ.mjs.map +0 -1
  140. package/dist/processors-cloudflare.mjs +0 -3
  141. package/dist/processors-testing.mjs +0 -435
  142. package/dist/processors-testing.mjs.map +0 -1
  143. package/dist/processors.mjs +0 -52
  144. package/dist/processors.mjs.map +0 -1
  145. package/dist/protocol-DnK_f2m6.mjs +0 -251
  146. package/dist/protocol-DnK_f2m6.mjs.map +0 -1
  147. package/dist/sdk/capnweb/index.d.ts +0 -2
  148. package/dist/sdk/capnweb/live-state/compact.d.ts +0 -5
  149. package/dist/sdk/capnweb/live-state/diff.d.ts +0 -41
  150. package/dist/sdk/capnweb/live-state/engine.d.ts +0 -44
  151. package/dist/sdk/capnweb/live-state/index.d.ts +0 -41
  152. package/dist/sdk/capnweb/live-state/protocol.d.ts +0 -87
  153. package/dist/sdk/capnweb/live-state/retain.d.ts +0 -23
  154. package/dist/sdk/capnweb/live-state/store.d.ts +0 -20
  155. package/dist/sdk/capnweb/live-state/types.d.ts +0 -11
  156. package/dist/sdk/capnweb/react.d.ts +0 -45
  157. package/dist/sdk/capnweb/react.mjs +0 -316
  158. package/dist/sdk/capnweb/react.mjs.map +0 -1
  159. package/dist/sdk/capnweb.mjs +0 -4
  160. package/dist/sdk/itx/react.d.ts +0 -191
  161. package/dist/sdk/itx/react.mjs +0 -383
  162. package/dist/sdk/itx/react.mjs.map +0 -1
  163. package/dist/sdk-DMB-IM11.mjs +0 -933
  164. package/dist/sdk-DMB-IM11.mjs.map +0 -1
  165. package/dist/sdk.d.ts +0 -339
  166. package/dist/serve-itx.d.ts +0 -46
  167. package/dist/starter-apps/flake-dashboard/app-ref.d.ts +0 -31
  168. package/dist/starter-apps/flake-dashboard/configured-worker.mjs +0 -1055
  169. package/dist/starter-apps/flake-dashboard/configured-worker.mjs.map +0 -1
  170. package/dist/starter-apps/flake-dashboard/contract.d.ts +0 -4839
  171. package/dist/starter-apps/flake-dashboard/contract.mjs +0 -2
  172. package/dist/starter-apps/flake-dashboard/index.d.ts +0 -17
  173. package/dist/starter-apps/flake-dashboard/index.mjs +0 -56
  174. package/dist/starter-apps/flake-dashboard/index.mjs.map +0 -1
  175. package/dist/starter-apps/flake-dashboard/worker.d.ts +0 -4607
  176. package/dist/starter-apps/github-ai-linter/ai-linter.d.ts +0 -8914
  177. package/dist/starter-apps/github-ai-linter/configured-worker.mjs +0 -17987
  178. package/dist/starter-apps/github-ai-linter/configured-worker.mjs.map +0 -1
  179. package/dist/starter-apps/github-ai-linter/contract.d.ts +0 -9193
  180. package/dist/starter-apps/github-ai-linter/index.d.ts +0 -10
  181. package/dist/starter-apps/github-ai-linter/index.mjs +0 -36
  182. package/dist/starter-apps/github-ai-linter/index.mjs.map +0 -1
  183. package/dist/starter-apps/github-ai-linter/prompt.d.ts +0 -13
  184. package/dist/starter-apps/github-ai-linter/review-bot.d.ts +0 -808
  185. package/dist/starter-apps/github-ai-linter/rules.d.ts +0 -34
  186. package/dist/starter-apps/github-ai-linter/worker-ref.d.ts +0 -19
  187. package/dist/starter-apps/github-ai-linter/worker.d.ts +0 -19
  188. package/dist/starter-apps/github-ai-linter/worker.mjs +0 -947
  189. package/dist/starter-apps/github-ai-linter/worker.mjs.map +0 -1
  190. package/dist/starter-apps/guestbook/app-ref.d.ts +0 -27
  191. package/dist/starter-apps/guestbook/client.d.ts +0 -7
  192. package/dist/starter-apps/guestbook/client.mjs +0 -59
  193. package/dist/starter-apps/guestbook/configured-worker.mjs +0 -205
  194. package/dist/starter-apps/guestbook/configured-worker.mjs.map +0 -1
  195. package/dist/starter-apps/guestbook/index.d.ts +0 -9
  196. package/dist/starter-apps/guestbook/index.mjs +0 -31
  197. package/dist/starter-apps/guestbook/index.mjs.map +0 -1
  198. package/dist/starter-apps/guestbook/processor.d.ts +0 -2267
  199. package/dist/starter-apps/guestbook/worker.d.ts +0 -26
  200. package/dist/starter-apps/guestbook/worker.mjs +0 -191
  201. package/dist/starter-apps/guestbook/worker.mjs.map +0 -1
  202. package/dist/starter-apps/media/configured-worker.mjs +0 -577
  203. package/dist/starter-apps/media/configured-worker.mjs.map +0 -1
  204. package/dist/starter-apps/media/index.mjs +0 -36
  205. package/dist/starter-apps/media/index.mjs.map +0 -1
  206. package/dist/starter-apps/media/ref.mjs +0 -20
  207. package/dist/starter-apps/media/ref.mjs.map +0 -1
  208. package/dist/starter-apps/media/worker.mjs +0 -579
  209. package/dist/starter-apps/media/worker.mjs.map +0 -1
  210. package/dist/starter-apps/notes/configured-worker.mjs +0 -6134
  211. package/dist/starter-apps/notes/configured-worker.mjs.map +0 -1
  212. package/dist/starter-apps/notes/index.mjs +0 -23
  213. package/dist/starter-apps/notes/index.mjs.map +0 -1
  214. package/dist/starter-apps/notes/ref.mjs +0 -21
  215. package/dist/starter-apps/notes/ref.mjs.map +0 -1
  216. package/dist/starter-apps/notes/worker.mjs +0 -427
  217. package/dist/starter-apps/notes/worker.mjs.map +0 -1
  218. package/dist/starter-apps/todo/client.mjs +0 -59
  219. package/dist/starter-apps/todo/configured-worker.mjs +0 -2864
  220. package/dist/starter-apps/todo/configured-worker.mjs.map +0 -1
  221. package/dist/starter-apps/todo/index.d.ts +0 -8
  222. package/dist/starter-apps/todo/index.mjs +0 -29
  223. package/dist/starter-apps/todo/index.mjs.map +0 -1
  224. package/dist/stream-processor-keepalive-DAQTP6m3.mjs +0 -2082
  225. package/dist/stream-processor-keepalive-DAQTP6m3.mjs.map +0 -1
  226. package/dist/usingCtx-mZx5nsAW.mjs +0 -11800
  227. package/dist/usingCtx-mZx5nsAW.mjs.map +0 -1
  228. package/dist/worker-ref-DZxPDmb_.mjs +0 -390
  229. package/dist/worker-ref-DZxPDmb_.mjs.map +0 -1
  230. package/dist/worker.d.mts +0 -33
  231. package/dist/worker.mjs +0 -18
  232. package/dist/worker.mjs.map +0 -1
  233. package/menubar/Iterate.entitlements +0 -12
  234. package/menubar/Iterate.swift +0 -914
  235. package/menubar/IterateIcon.swift +0 -145
  236. package/menubar/README.md +0 -28
  237. package/menubar/build-menubar-app.sh +0 -59
  238. /package/dist/{next/api.mjs → api.mjs} +0 -0
  239. /package/dist/{next/app-session.d.ts → app-session.d.ts} +0 -0
  240. /package/dist/{next/app.d.ts → app.d.ts} +0 -0
  241. /package/dist/{next/oauth-scopes.d.ts → oauth-scopes.d.ts} +0 -0
package/README.md CHANGED
@@ -1,107 +1,189 @@
1
1
  # iterate
2
2
 
3
- CLI for OS Next (`apps/os-next`). Requires Node >=22.15; no Bun runtime.
4
-
5
- ```sh
6
- npx iterate # offline help
7
- npx iterate login # browser OAuth with project consent
8
- npx iterate projects list
9
- npx iterate orgs list
10
- npx iterate ping
11
- npx iterate repl --project my-project # local Node REPL
12
- npx iterate itx run --project my-project --eval 'return await itx.whoami();'
13
- npx iterate use-my-computer --project my-project --name myComputer
14
- npx iterate menubar --project my-project # macOS app
15
- npx iterate logout
16
- ```
17
-
18
- The default server is `https://os.iterate2.com`. Login uses that server's OAuth
19
- issuer, PKCE and a loopback callback. Tokens refresh automatically before a
20
- command when close to expiry. `ITERATE_BEARER_TOKEN` supplies a token for scripts;
21
- `APP_CONFIG_ADMIN_API_SECRET` supplies operator credentials and takes precedence.
22
- `ITERATE_SKIP_BROWSER_OPEN=1` prints the login URL without opening a browser.
23
-
24
- ## Running scripts
25
-
26
- `itx run` executes a JavaScript function body on OS Next with `itx` in scope.
27
- Use `return` for the result. The server records the run and its settlement;
28
- the CLI never retries a script automatically. Scripts execute on the server,
29
- so local Node APIs and local filesystem access are unavailable.
30
-
31
- ```sh
32
- iterate itx run --project my-project --context /notes --eval '
33
- await itx.append({ type: "note", payload: { text: "hello" } });
34
- return await itx.readEvents(0, 10);
35
- '
36
- iterate itx run --project my-project --file ./script.js
37
- cat script.js | iterate itx run --project my-project --file -
38
- ```
3
+ The SDK for Iterate (`apps/os`): context APIs, stream processors, reactive clients, React
4
+ bindings, and OAuth app sessions, under `iterate/*`. The package exports source in this
5
+ workspace and compiled JavaScript with declarations when packed. The `iterate` command is
6
+ [`@iterate-com/cli`](../cli/README.md).
7
+
8
+ ## The SDK/platform line
9
+
10
+ The SDK holds what user code runs or speaks, and the platform is its first user: apps/os builds
11
+ its own entities on `iterate/sdk`, and the first-party apps' code uses only `iterate/*`. Each
12
+ subpath in `package.json`'s `exports` is one public module; nothing else is importable.
13
+
14
+ - A module belongs here when user code runs it or speaks it: a loaded worker, a facet, a
15
+ processor, a browser or Node client, or the wire contract between them and the platform. It
16
+ belongs in apps/os when only the platform's Worker runs it, and in packages/shared when more
17
+ than one app needs it and user code never does.
18
+ - Outside apps/os, no package and no app imports apps/os. `import-js/no-restricted-paths` in
19
+ `.oxlintrc.json` resolves each import under `packages/**` and `apps/**` to a file, so type
20
+ imports, re-exports, dynamic `import()` and an app added later are covered, and
21
+ `lint/oxlintrc-platform-line.test.ts` pins it. Tests may import apps/os's two harnesses,
22
+ `apps/os/e2e/support/` and `apps/os/__workers-tests__/support.ts`, which drive a real platform.
23
+ - The one known exception: the git codec (`@iterate-com/shared/git-wire`) and the GitHub template
24
+ reader live in packages/shared, though only the platform's Worker runs them. packages/shared is
25
+ private, so they do not cross the line.
26
+ - No private core package behind a thin `iterate`: apps/os would then import modules user code
27
+ cannot, and the SDK's types would have to be bundled or published anyway.
28
+
29
+ Follow-ups: move the git codec and the template reader into `apps/os/src/repo/`, and type the test
30
+ harnesses against `iterate/api`. The decision's reasons, and how workerd, the Agents SDK, Convex,
31
+ Supabase, tRPC, Hono and Wrangler draw the same line:
32
+ [the decision record](https://github.com/iterate/iterate/blob/d52a4e8e0f791c96b683fe178b56570532123c05/docs/2026-09-24-sdk-platform-line.md)
33
+ (#3018).
34
+
35
+ ## Reaching the context from loaded code
36
+
37
+ Code the platform loads for a project (a config worker, a facet, a worker behind a rewrite rule)
38
+ imports the SDK as `./processor.js` and reaches its context through `withItx`: one round trip,
39
+ after which the scope, every call made through it and every handle it awaited are released.
39
40
 
40
- Specify exactly one of `--eval` or `--file`. `--context` is a project-local path
41
- (default `/`). `--project` accepts an id or slug; otherwise the CLI uses the
42
- config's `defaultProject`, or the only project accessible to the session.
41
+ ```js
42
+ import { ConfigWorker, withItx } from "./processor.js";
43
43
 
44
- ## Use my computer
44
+ export default class extends ConfigWorker {
45
+ async fetch() {
46
+ // An SDK host (ConfigWorker, StreamProcessorDurableObject) has it as a method.
47
+ const { projectSlug } = await this.withItx((itx) => itx.whoami());
48
+ return new Response(`Homepage of ${projectSlug}`);
49
+ }
50
+ }
45
51
 
46
- `use-my-computer` shares a Mac as a live OS Next capability until Ctrl-C:
52
+ // Anywhere else: withItx(this.env.ITX, (itx) => itx.kv.get("key"))
53
+ ```
47
54
 
48
- - `itx.myComputer.ask({ question, buttons? })`: native choice dialog.
49
- - `itx.myComputer.notify({ message, title? })`: desktop notification.
50
- - `itx.myComputer.runSwift({ code })`: Swift with the owner's local permissions.
51
- - `itx.myComputer.__describe()`: usage instructions and method signatures.
55
+ Never keep what `env.ITX.get()` hands out, and answer data, not handles, from `withItx`: a kept
56
+ scope, step or handle keeps the context, and any facet holding it, resident after the project goes
57
+ idle. An object that needs
58
+ reach takes a `WithItx` accessor (`(call) => withItx(this.env.ITX, call)`), never a scope; work
59
+ that outlives the call runs under a processor's `runInBackground` claim. Lint refuses a raw
60
+ `ITX.get()` in this repository (`iterate/no-raw-itx-get`).
52
61
 
53
- The command requires macOS, AppleScript and Swift. Share only with a project
54
- you trust: its callers can run local code. The capability belongs to the live
55
- connection and is released on exit. A disconnect or token expiry ends sharing
56
- with an error; rerun the command to refresh authentication and reconnect.
62
+ ## Testing a processor
57
63
 
58
- ## Configs and migration
64
+ `iterate/stream/test-support` (Node) is the harness the SDK's own engine tests use:
59
65
 
60
- Configs live in `${XDG_CONFIG_HOME:-~/.config}/iterate/config.json`. Selection
61
- order is `--config`, a parent-directory workspace mapping, the default config,
62
- a single saved config, then built-in `prd`.
66
+ ```ts
67
+ import { reduceProcessor } from "iterate/stream/test-support";
63
68
 
64
- ```sh
65
- iterate config set --name next --os-base-url https://os.iterate2.com \
66
- --default-project my-project --set-default
67
- iterate --config next login
68
- iterate config set --name local --os-base-url http://localhost:8787 --set-workspace
69
- iterate config list
70
- iterate config get
69
+ // apps/os/src/client/presence/processor.test.ts: durable ticks are reduced, ephemeral pokes are not
70
+ const state = reduceProcessor(new PresenceProcessor(), [{ type: "tick" }, { type: "poke" }]);
71
+ // state.ticks === 1
71
72
  ```
72
73
 
73
- Existing configs keep their server URL. For a config that targets the old OS,
74
- set its `--os-base-url` to an OS Next deployment and log in again. Changing the
75
- server clears that config's session. There is no separate `authBaseUrl` setting.
76
-
77
- The old chat TUI and remotely discovered `iterate os ...` commands are removed.
78
- The menu bar supports sign-in and computer sharing. Approval code is retained
79
- but dormant until OS Next supports it. Use `iterate itx run` for scripts;
80
- its `--project` selects the project and `--context` selects a path within it.
81
-
82
- ## Node REPL
83
-
84
- `iterate repl` opens a local Node REPL with the authenticated session as `itx`
85
- (try `await itx.projects.list()`) and the transport's `RpcTarget` constructor.
86
- Pass `--project my-project --context /` to bind `itx` to a project context;
87
- a configured `defaultProject` also selects a project. Top-level `await`, Node APIs and `.load` are available. The bindings remain available after `.clear`; `.exit` releases the context and connection. A lost connection
88
- ends the REPL visibly; it never silently repeats your commands.
74
+ `memoryStream`, `memoryStorage` and `settle` drive a whole `ProcessorEngine` against an
75
+ in-memory log (`src/stream/processor.test.ts` shows how).
89
76
 
90
77
  ## Node connections
91
78
 
92
- `iterate/next/node` exposes a connection owner for OS Next scripts and live
79
+ `iterate/node` exposes a connection owner for Iterate scripts and live
93
80
  providers. It uses the same protocol and cleanup as the CLI:
94
81
 
95
82
  ```js
96
- import { connectOsNext } from "iterate/next/node";
83
+ import { connectIterate } from "iterate/node";
97
84
 
98
- using connection = await connectOsNext({
99
- baseUrl: "https://os.iterate2.com",
85
+ using connection = await connectIterate({
86
+ baseUrl: "https://os.iterate.com",
100
87
  auth: { type: "bearer", token: process.env.ITERATE_BEARER_TOKEN },
101
88
  });
102
89
  using project = await connection.session.projects.get("my-project");
103
90
  console.log(await project.run("async (itx) => await itx.whoami()"));
104
91
  ```
105
92
 
106
- The package launcher delegates to repository source during development and
107
- uses the published build when installed through `npx`.
93
+ ## Event types
94
+
95
+ A platform event type is `events.iterate.com/<namespace>/<event>`: one namespace segment and one
96
+ event segment, both lowercase kebab-case, and never a third segment.
97
+
98
+ Every type under `events.iterate.com/` follows these rules, test types included. A type without
99
+ that prefix belongs to whoever appends it and is opaque to the platform: tests use types like
100
+ `demo/ping` on purpose, and a project may use its own domain (`events.garple.com/sales/…`).
101
+
102
+ ### Namespaces
103
+
104
+ - **`itx`** holds the context engine's own events: everything the core contract
105
+ (`apps/os/src/stream/core-processor.ts`) reduces, validates or refuses, plus the records the
106
+ Stream, the context Durable Object and the SDK processor host write themselves. Where the schema
107
+ and the reduce live decides it, not which contexts hold the event: fetch routes and the apex
108
+ ingress target are core state, so they are `itx` even though only a project root's copy is read.
109
+ A domain processor may consume an `itx` event (the agent consumes `itx/run-*`, the Project
110
+ processor `itx/ingress-configured`); it names the core's catalog in its `processorDeps` rather than
111
+ defining the event itself. The core's checkpoint slug is `core`: it is a storage key, not a type
112
+ prefix.
113
+ - **A domain namespace** is the singular name of the kind of context whose log the event belongs
114
+ to, which is the defining contract's slug when there is one: `account`, `organization`,
115
+ `project`, `repo`, `workspace`, `secret`, `agent`, `voice-agent`. A fact cross-posted to another
116
+ log keeps its own namespace: `repo/created` on `/` is still a repo fact.
117
+ - **An integration** uses its own name as its namespace, for example `chrome`.
118
+ - **`test`** holds types that only tests append. Production code never matches a `test/*` type. A
119
+ test contract may keep a slug of its own (`counter`), but its events go under `test/`. A test must
120
+ not borrow a production namespace for a type that does not exist.
121
+
122
+ ### Event names
123
+
124
+ - **A fact is past tense**: `<object>-<verb-ed>`, or a bare `<verb-ed>` when the object is the
125
+ namespace's own subject (`itx/created` is the context, `agent/paused` is the agent). The object
126
+ comes first and is singular.
127
+ - **Spell words out.** Clipped words are not allowed (`spk`); a real word is (`mic`), and so is an
128
+ acronym the API already spells (`llm`, `rpc`, `itx`).
129
+ - **Asking and answering.** `<x>-requested` asks, and its offset identifies the ask. The answer
130
+ takes one of three shapes:
131
+ - `<x>-settled` is the one terminal fact when the asker reads a result. It names
132
+ `requestOffset` and carries the outcome: succeeded, failed or cancelled, a status, or an error.
133
+ Examples: `itx/run-*`, `agent/llm-request-*`, `project/hostname-add-*`.
134
+ - `<verb-ed>` or `<verb>-failed` is used when success is a fact that other logs wait on, like a
135
+ certificate: `create-requested` → `created` or `create-failed`, `delete-requested` → `deleted`,
136
+ `hostname-remove-requested` → `hostname-removed`. A failure that is retried rather than
137
+ reported gets no `-failed` fact.
138
+ - An answer that is also a fact of its own names the ask by id:
139
+ `voice-agent/delegation-requested` is answered by one `commentary-added` carrying its
140
+ `delegationId`.
141
+ - **One verb pair per kind of change:**
142
+ - `added` / `removed` for membership in a set: `organization/member-added`,
143
+ `organization/project-added`, hostnames.
144
+ - `created` / `deleted` for an entity with a lifecycle: projects, repos, workspaces, agents.
145
+ - `set` / `deleted` for a keyed value: `secret/*`.
146
+ - `set` / `cancelled` for a schedule: `itx/schedule-*`. Each occurrence is `fired` or `failed`.
147
+ - `-configured` for one fact that sets a row or clears it with `null`
148
+ (`itx/subscription-configured`, `itx/rewrite-rule-configured`, `itx/fetch-route-configured`),
149
+ sets a singleton (`itx/ingress-configured`), or merges a partial configuration
150
+ (`agent/configured`: omitted keys keep their values).
151
+ - **Things the platform does on its own** are plain facts about the object: `itx/schedule-fired`,
152
+ `itx/schedule-failed`, `itx/subscription-delivery-halted`.
153
+ - **Ephemeral events.** An ephemeral event that records something happening is named like any other
154
+ fact: `itx/rpc-stub-attached`, `itx/live-state-changed`, `chrome/navigated`. Three kinds may be
155
+ singular nouns: a sequenced slice of a live stream is a `<stream>-frame`
156
+ (`voice-agent/mic-frame`, `agent/llm-response-frame`), a heartbeat (`voice-agent/keepalive`), and
157
+ a diagnostic record (`itx/alarm-trace`). A durable event is never a noun.
158
+ - **Families and prefixes.** Code matches some families by prefix: `…/itx/run-`,
159
+ `…/itx/subscription-`, `…/itx/schedule-`, `…/project/hostname-`. Before naming a new type, check
160
+ it doesn't join one of these families by accident. Never match `…/itx/` as a whole: it is not a
161
+ permission boundary, and it catches live-state deltas, stub presence and child
162
+ announcements.
163
+ - **Code follows the type.** A constant, schema, test fixture or idempotency key built from a type
164
+ follows its name (`itx/child-created:<path>`). Broader concepts, modules and Workers log
165
+ event names keep theirs: the Stream, scheduled appends, `core`, `scheduled-append.completed`.
166
+ - **Renaming.** A rename has to serve one of these rules, not taste. If a type is stored outside the
167
+ platform's Durable Objects (device firmware, a published SDK, a project's config repo),
168
+ rename it only in a change that migrates that store too.
169
+
170
+ | Namespace | Defined in |
171
+ | ------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
172
+ | `itx` | `apps/os/src/stream/core-processor.ts` (and its leaf event catalog), `stream.ts`, `scheduled-appends.ts`, `subscription-delivery.ts`, `apps/os/src/context/built-ins.ts`, `apps/os/src/fetch-routes.ts`, `apps/os/src/iterate-context-durable-object.ts`, `packages/iterate/src/stream/{run,processor}.ts` |
173
+ | `account`, `organization`, `project`, `repo`, `workspace`, `secret` | `apps/os/src/<name>/contract.ts` (repo and workspace also use `project/entity-lifecycle.ts`) |
174
+ | `agent` | `apps/agents/runtime/contract.ts` |
175
+ | `voice-agent` | `apps/agents/voice/voice-agent.ts`, `apps/agents/voice/events.ts` |
176
+ | `chrome` | `apps/browser-extension/panel.js` |
177
+ | `test` | tests only |
178
+
179
+ Two types break these rules until the Kit firmware migrates:
180
+
181
+ - `voice-agent/spk-frame` will become `voice-agent/speaker-frame`.
182
+ - `voice-agent/conversation-ended` will become `voice-agent/call-ended`. It pairs with `call-started`
183
+ and names the activation; the provider session is the `conversation`.
184
+
185
+ `note/added` is only an example in the Agents composer; no contract defines `note`.
186
+ `email/received` is only an integration's transcript in an agent UI test; no contract defines
187
+ `email`.
188
+ `capability-host/script-run-*` is never written to a log: the agent UI's adapter builds it in
189
+ memory.
@@ -1,5 +1,5 @@
1
1
  import type { Ai } from "@cloudflare/workers-types";
2
- import type { FacetHandle, InvokeHandle, ItxExpressionInput } from "./expression.ts";
2
+ import type { InvokeHandle, ItxExpression, ItxExpressionInput } from "./expression.ts";
3
3
  import type { ConsentScope } from "./oauth-scopes.ts";
4
4
  import type { Principal } from "./principal.ts";
5
5
  import type { IngressRouting } from "./project-ingress.ts";
@@ -33,8 +33,8 @@ export type WaitForEventFilter = {
33
33
  afterOffset?: number;
34
34
  timeoutMs?: number;
35
35
  };
36
- /** The `rewrite-rule-configured` event's payload — what `provide` takes, what `itx.append` writes
37
- * durably: make `match` mean `target` (an expression, or `null` to deny). `description` is the one
36
+ /** The `rewrite-rule-configured` event's payload — what `itx.append` writes durably and `provide`
37
+ * writes for its session: make `match` mean `target` (an expression, or `null` to deny). `description` is the one
38
38
  * line a model reads for the name; it rides the row into `rewriteRules.list()`. */
39
39
  export type RewriteRuleConfigured = {
40
40
  match: ItxExpressionInput;
@@ -85,8 +85,8 @@ export type ScheduleReceipt = {
85
85
  /** A secret's material: one string (`getSecret("/secrets/<name>")` is the whole value) or a JSON
86
86
  * object whose string fields `getSecret("/secrets/<name>", { field: "a.b" })` picks — the
87
87
  * multidimensional shape a credential exchange needs (`{ username, password, accessToken }`,
88
- * `{ clientId, clientSecret, refreshToken, accessToken }`). A JSON STRING still works as an object
89
- * (`set(name, JSON.stringify({...}))`). */
88
+ * `{ clientId, clientSecret, refreshToken, accessToken }`). A string is always the one value: it has
89
+ * no fields, whether or not it parses as JSON. */
90
90
  export type SecretMaterial = string | Record<string, unknown>;
91
91
  /** How a token endpoint wants the client credential — the RFC 8414 `token_endpoint_auth_methods_supported`
92
92
  * registry values, so a provider's discovery document pastes straight in: `client_secret_basic`
@@ -141,6 +141,47 @@ export type CollectSecretLink = {
141
141
  path: string;
142
142
  url: string;
143
143
  };
144
+ /** WHICH requests a fetch route takes — every field given must hold: the host's routing slug
145
+ * (`blog` for `blog--<project>`), a `URLPattern` over the URL the app sees (its init's fields, each a
146
+ * pattern string), exact header values. `{}` takes every request. */
147
+ export type FetchRouteRequestMatcher = {
148
+ routingSlug?: string;
149
+ url?: {
150
+ protocol?: string;
151
+ username?: string;
152
+ password?: string;
153
+ hostname?: string;
154
+ port?: string;
155
+ pathname?: string;
156
+ search?: string;
157
+ hash?: string;
158
+ baseURL?: string;
159
+ };
160
+ headers?: Record<string, string>;
161
+ };
162
+ /** A route as `itx.fetchRoutes.set(name, route)` takes it: the requests it takes, the itx
163
+ * expression they go to, who may use it (`project-members`: the config worker answers anyone else
164
+ * the sign-in challenge; absent or null: public) and its priority (higher first, then by name). */
165
+ export type FetchRouteInput = {
166
+ requestMatcher: FetchRouteRequestMatcher;
167
+ target: ItxExpressionInput;
168
+ authRequirement?: {
169
+ visitors: "project-members";
170
+ } | null;
171
+ priority?: number;
172
+ };
173
+ /** A live route as `list()` and `match` answer it, its target parsed, with the offset of the
174
+ * `itx/fetch-route-configured` fact that set it. */
175
+ export type FetchRouteEntry = {
176
+ fetchRouteName: string;
177
+ requestMatcher: FetchRouteRequestMatcher;
178
+ target: ItxExpression;
179
+ authRequirement: {
180
+ visitors: "project-members";
181
+ } | null;
182
+ priority: number;
183
+ configuredOffset: number;
184
+ };
144
185
  /** A context (a project, a user, an organization): every `itx` root, reached through `invoke`. */
145
186
  export interface IterateContextApi {
146
187
  invoke(call: ItxExpressionInput, ...args: unknown[]): Promise<unknown>;
@@ -176,6 +217,24 @@ export interface IterateContextApi {
176
217
  projectUrl?: string;
177
218
  }>;
178
219
  append(...events: StreamEventInput[]): Promise<StreamEvent[]>;
220
+ /** This project's public URL over HTTP: the apex, or a routing slug's host (`blog--<project>`),
221
+ * at `path`. Only from a session, which carries the origin to compose it with. */
222
+ url(target?: {
223
+ routingSlug?: string;
224
+ path?: string;
225
+ }): Promise<string>;
226
+ /** RESET this context (Cloudflare's `ctx.abort`): its Durable Object drops everything it holds in
227
+ * memory and the next call starts a fresh incarnation from durable storage. Resolves with the
228
+ * `events.iterate.com/itx/aborted { reason?, callerPath?, app? }` event it recorded — durable
229
+ * and attributed to the caller before anything resets — and the reset follows the answer.
230
+ * SURVIVES: the log and everything derived from it (rewrite rules, subscriptions, schedules),
231
+ * every facet's storage, kv. GOES: in-memory state, every facet instance and its in-flight work,
232
+ * every socket on the context (a provider's re-dials), and every other call in flight there — it
233
+ * rejects with the reset's message (`itx.abort() reset the context <path>: <reason>`). A handle
234
+ * you kept names its target by expression, so its next call reaches the new incarnation.
235
+ * Another context of the project: `itx.cd(path).abort()`. A rewrite rule masks it like any name
236
+ * (`provide("itx.abort", null)`). */
237
+ abort(reason?: string): Promise<StreamEvent>;
179
238
  readEvents(afterOffset?: number, limit?: number, options?: {
180
239
  includeEphemeral?: boolean;
181
240
  }): Promise<StreamPage>;
@@ -217,6 +276,21 @@ export interface IterateContextApi {
217
276
  * from an agent context, a successful submission messages that same agent with the path only. */
218
277
  collectFromUser(input: CollectSecretInput): Promise<CollectSecretLink>;
219
278
  };
279
+ /** The project's fetch routes, on its root `/`: which requests on its hosts go to which itx
280
+ * expression. `set` appends one `itx/fetch-route-configured` fact (`null` deletes the route);
281
+ * `match` answers the route a request takes, which the config worker forwards with
282
+ * `env.ITX.fetch` naming `itx.fetchRoutes.fetch('<name>')` (a WebSocket upgrade included). */
283
+ fetchRoutes: {
284
+ set(fetchRouteName: string, route: FetchRouteInput | null): Promise<{
285
+ fetchRouteName: string;
286
+ }>;
287
+ list(): Promise<FetchRouteEntry[]>;
288
+ match(request: {
289
+ method: string;
290
+ url: string;
291
+ headers: Headers | Record<string, string> | [string, string][];
292
+ }): Promise<FetchRouteEntry | null>;
293
+ };
220
294
  /** The table this context resolves against, described — the tree a model reads. `list()` follows a
221
295
  * bare hop row into the context it names (a Durable Object hop, hence async). */
222
296
  rewriteRules: {
@@ -224,13 +298,26 @@ export interface IterateContextApi {
224
298
  get(match: string): Promise<RewriteRuleListEntry | null>;
225
299
  resolve(call: ItxExpressionInput): string[];
226
300
  };
301
+ /** A facet of this context: a caller reaches only what its class lists in `static publicMethods`
302
+ * (sdk/index.ts `FacetDurableObject`); anything else is refused FORBIDDEN. */
227
303
  facets: {
228
- get(name: string, spec?: FacetSpec): FacetHandle;
304
+ get(name: string, spec?: FacetSpec): InvokeHandle;
305
+ /** RESET one facet of this context, from the context that hosts it — any facet, whether or not
306
+ * it extends the SDK's host, including one that would never answer a call. Its instance and
307
+ * in-memory state go, and every call in flight on it rejects `FACET_ABORTED`; its storage
308
+ * stays, and its next call starts it fresh. The context itself is not reset. Resolves with the
309
+ * `events.iterate.com/itx/facet-aborted { name, reason?, callerPath?, app? }` event;
310
+ * `NO_FACET` for a name never hosted here. */
311
+ abort(name: string, reason?: string): Promise<StreamEvent>;
229
312
  };
230
313
  subscriptions: {
231
314
  list(): SubscriptionListEntry[];
232
315
  get(name: string): SubscriptionListEntry | null;
233
316
  };
317
+ /** The rpc stubs lent to this context right now, by key (a live session's `provide`). */
318
+ rpcStubs: {
319
+ list(): string[];
320
+ };
234
321
  processors: {
235
322
  enable(name: string, spec?: (FacetSpec & {
236
323
  consumes?: string[];
@@ -263,18 +350,19 @@ export interface IterateContextApi {
263
350
  }): Promise<{
264
351
  [Symbol.dispose](): void;
265
352
  }>;
266
- /** A rewrite rule of this context, session-scoped (the handle's dispose removes it): the event's
267
- * payload `{ match, target, description? }`, or the shorthand `(match, target)`. `target` is an
268
- * expression, or null to deny. The durable spelling is the same payload through `itx.append`. */
269
- provide(input: RewriteRuleConfigured): Promise<{
270
- [Symbol.dispose](): void;
271
- }>;
272
- provide(match: ItxExpressionInput, target: unknown): Promise<{
353
+ /** A rewrite rule of this context, session-scoped (the handle's dispose removes it): make `match`
354
+ * mean `target`, an expression, a live stub, or null to deny. `description` is the one line a
355
+ * model reads for the name. The durable spelling is the rule's event (`RewriteRuleConfigured`)
356
+ * through `itx.append`. */
357
+ provide(match: ItxExpressionInput, target: unknown, options?: {
358
+ description?: string;
359
+ }): Promise<{
273
360
  [Symbol.dispose](): void;
274
361
  }>;
275
362
  /** A script — the text of `async (itx) => { … }` — run once against this context, on its log:
276
- * `context/run-requested` under the caller, the context's runner, `run-settled` (JSON in, JSON
277
- * out); resolves with the result or rejects with the settlement's error. Never re-run. */
363
+ * `itx/run-requested` under the caller, the context's runner, `run-settled` (JSON in, JSON
364
+ * out); resolves with the result or rejects with the settlement's error. Never re-run. A script
365
+ * still running ten minutes after it started is settled failed (`failureKind: "deadline"`). */
278
366
  run(script: string): Promise<unknown>;
279
367
  /** The project's repos, workspaces and agents as domain objects — one shape each: `get(path)` is
280
368
  * the entity's facet on the context at `path` (its verbs, plus the typed `append` on that
@@ -343,21 +431,32 @@ export interface IterateContextApi {
343
431
  }[]>;
344
432
  };
345
433
  }
346
- /** One OAuth grant as `grants.list()` shows it: a session, a connected app, a minted token. */
434
+ /** What a grant is: a sign-in not yet exchanged, a device's key, a personal access token, or a
435
+ * browser or app session. A client labels it for display. */
436
+ export type GrantKind = "pending" | "device" | "personal" | "session";
437
+ /** One grant as `grants.list()` shows it: a session or a connected app (an OAuth grant), or a
438
+ * personal access token or a device's key (the account's own API key, `pat_…`). */
347
439
  export interface GrantRecord {
348
440
  id: string;
349
441
  clientId?: string;
350
442
  logoUri?: string;
351
443
  clientDomain?: string;
352
444
  name: string;
353
- kind: string;
445
+ kind: GrantKind;
446
+ /** An OAuth grant's one resource: Cap'n Web at `/api` (and the projects' hosts), or `/mcp`. A
447
+ * personal access token has none: it works at `/api`, at `/mcp` and on its projects' hosts. */
448
+ resource?: "api" | "mcp";
449
+ /** A personal access token's projects, by id: all it reaches. */
450
+ projects?: string[];
354
451
  createdAt: number;
355
452
  expiresAt: number | null;
356
453
  lastUsedAt: number | null;
357
- cleanupPending: boolean;
358
454
  expired: boolean;
359
455
  /** The grant this very session rides on. */
360
456
  current?: boolean;
457
+ /** A personal access token's: the grant of the session that minted it (listed here while it
458
+ * lives). */
459
+ mintedBy?: string;
361
460
  }
362
461
  /** What the consent screen shows for an authorization request. */
363
462
  export type ConsentAnswer = {
@@ -382,12 +481,20 @@ export type ConsentAnswer = {
382
481
  kind: "invalid";
383
482
  description: string;
384
483
  };
484
+ /** An organization's invitation link as its owners see it (`expiresAt` ISO). */
485
+ export interface InvitationRecord {
486
+ id: string;
487
+ orgId: string;
488
+ role: "owner" | "member";
489
+ emailHint: string | null;
490
+ expiresAt: string;
491
+ }
385
492
  /** An organization as the session lists it: its minted id, its free-text name, the person's role
386
493
  * in it, and how many projects it holds (every one of them, not only those this grant lists). */
387
494
  export interface OrgRecord {
388
495
  id: string;
389
496
  name: string;
390
- role?: string;
497
+ role?: "owner" | "member";
391
498
  projects: number;
392
499
  }
393
500
  /** A project as the catalog lists it: addressed by `id` everywhere (`projects.get`, a grant's list,
@@ -411,17 +518,9 @@ export interface IterateSessionApi {
411
518
  /** the MCP server's origin (the dash's connect page) — "" when this deployment serves none */
412
519
  mcpOrigin: string;
413
520
  };
414
- /** The organizations this session reaches. */
415
- orgs(): Promise<OrgRecord[]>;
416
- /** A new organization — `organizations:write`; the person is its owner. */
417
- createOrg(name: string): Promise<OrgRecord>;
418
- /** Rename an organization the person owns — `organizations:write`. */
419
- updateOrg(orgId: string, input: {
420
- name: string;
421
- }): Promise<OrgRecord>;
422
- /** Delete an organization the person owns, while it holds no project — `organizations:write`. */
423
- deleteOrg(orgId: string): Promise<void>;
424
- /** OAuth grants this session may manage (a signed-in person's): list, end, mint one for a device. */
521
+ /** The grants this session may manage (a signed-in person's with the `account` scope): list and
522
+ * end its sessions and personal access tokens, and mint a personal access token — its bearer
523
+ * answered once, `expiresAt` null for a key that never expires. */
425
524
  grants: {
426
525
  list(cursor?: string): Promise<{
427
526
  items: GrantRecord[];
@@ -431,9 +530,18 @@ export interface IterateSessionApi {
431
530
  }>;
432
531
  end(grantId: string): Promise<unknown>;
433
532
  endCurrent(): Promise<unknown>;
434
- mint(input: unknown): Promise<{
533
+ mint(input: {
534
+ name: string;
535
+ /** project ids, each one the person reaches */
536
+ projects: string[];
537
+ /** epoch ms; omitted, the key never expires */
538
+ expiresAt?: number;
539
+ /** a device's public client metadata document (Kit): the key is listed as that device */
540
+ clientId?: string;
541
+ }): Promise<{
542
+ id: string;
435
543
  token: string;
436
- expiresAt: number;
544
+ expiresAt: number | null;
437
545
  }>;
438
546
  };
439
547
  /** The consent screen's methods (the OAuth authorize flow): describe a request, approve it. */
@@ -452,7 +560,8 @@ export interface IterateSessionApi {
452
560
  list(): Promise<ProjectRecord[]>;
453
561
  /** the project's root context, by its slug or its id */
454
562
  get(project: string): Promise<IterateContextApi>;
455
- /** Config repository presets available on this platform. */
563
+ /** Config repository presets available on this platform, besides the default config a
564
+ * creation that names no template gets. */
456
565
  templates(): Promise<{
457
566
  label: string;
458
567
  reference: string;
@@ -467,8 +576,63 @@ export interface IterateSessionApi {
467
576
  restoreProjectId?: string;
468
577
  }): Promise<IterateContextApi>;
469
578
  };
579
+ /** The organizations this session reaches — the person's memberships (a grant narrowed to
580
+ * projects sees only their organizations, unless it holds `organizations:write`): the rows, the
581
+ * organization's context by membership, and the verbs (`organizations:write`; the person is the
582
+ * owner of what they create, and only an owner renames, deletes or changes members). Each verb is
583
+ * a request the control plane answers; a refusal is a coded error (FORBIDDEN, INVALID_INPUT). */
470
584
  organizations: {
585
+ list(): Promise<OrgRecord[]>;
586
+ /** the organization's context — `session.user` for an organization — by membership */
471
587
  get(orgId: string): Promise<IterateContextApi>;
588
+ create(input: {
589
+ name: string;
590
+ }): Promise<OrgRecord>;
591
+ rename(orgId: string, input: {
592
+ name: string;
593
+ }): Promise<OrgRecord>;
594
+ /** only while it holds no project */
595
+ delete(orgId: string): Promise<void>;
596
+ addMember(orgId: string, input: {
597
+ userId: string;
598
+ role?: "owner" | "member";
599
+ }): Promise<void>;
600
+ removeMember(orgId: string, input: {
601
+ userId: string;
602
+ }): Promise<void>;
603
+ /** the members with their emails — the operator's alone (the project-seed CLI) */
604
+ members(orgId: string): Promise<{
605
+ userId: string;
606
+ email: string;
607
+ role: "owner" | "member";
608
+ }[]>;
609
+ /** an owner's invitation link: whoever accepts it first joins in `role` (default member),
610
+ * until it expires (`expiresInDays`, default 7, 1–30). `token` is the link's secret, answered
611
+ * this once — the dash's `/invitations/<token>`. */
612
+ createInvitation(orgId: string, input?: {
613
+ role?: "owner" | "member";
614
+ emailHint?: string;
615
+ expiresInDays?: number;
616
+ }): Promise<InvitationRecord & {
617
+ token: string;
618
+ }>;
619
+ /** an owner withdraws an open link by its id */
620
+ revokeInvitation(orgId: string, input: {
621
+ invitationId: string;
622
+ }): Promise<void>;
623
+ /** what a link opens, for the signed-in person holding it; null when it names nothing */
624
+ invitation(token: string): Promise<(InvitationRecord & {
625
+ orgName: string;
626
+ status: "pending" | "accepted" | "revoked" | "expired";
627
+ /** the reader already belongs */
628
+ member: boolean;
629
+ /** the reader is the one who accepted it — accepting again answers the same and lands
630
+ * the membership's facts again */
631
+ acceptedByYou: boolean;
632
+ }) | null>;
633
+ /** join the organization a link opens, in its role — single use: the first person to accept
634
+ * consumes it (again by them answers the same; anyone after is refused INVALID_INPUT) */
635
+ acceptInvitation(token: string): Promise<OrgRecord>;
472
636
  };
473
637
  user: IterateContextApi;
474
638
  logout(): unknown;