iterate 0.2.7 → 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.
- package/README.md +173 -81
- package/dist/api.d.ts +643 -0
- package/dist/api.mjs +0 -0
- package/dist/app-server.d.ts +51 -0
- package/dist/app-server.mjs +481 -0
- package/dist/app-server.mjs.map +1 -0
- package/dist/app-session.d.ts +49 -0
- package/dist/app-session.mjs +235 -0
- package/dist/app-session.mjs.map +1 -0
- package/dist/app.d.ts +29 -0
- package/dist/app.mjs +180 -0
- package/dist/app.mjs.map +1 -0
- package/dist/client/live-state.d.ts +63 -0
- package/dist/client/oauth.d.ts +17 -0
- package/dist/client/react.d.ts +77 -0
- package/dist/client/socket.d.ts +7 -0
- package/dist/client.mjs +156 -0
- package/dist/client.mjs.map +1 -0
- package/dist/expression.d.ts +88 -0
- package/dist/expression.mjs +301 -0
- package/dist/expression.mjs.map +1 -0
- package/dist/lib-BWr-5mFO.mjs +36 -0
- package/dist/lib-BWr-5mFO.mjs.map +1 -0
- package/dist/lib.d.ts +70 -0
- package/dist/lib.mjs +228 -0
- package/dist/lib.mjs.map +1 -0
- package/dist/node.d.ts +15 -0
- package/dist/node.mjs +47 -0
- package/dist/node.mjs.map +1 -0
- package/dist/oauth-scopes.d.ts +32 -0
- package/dist/oauth-scopes.mjs +40 -0
- package/dist/oauth-scopes.mjs.map +1 -0
- package/dist/oauth.mjs +41 -0
- package/dist/oauth.mjs.map +1 -0
- package/dist/principal.d.ts +8 -0
- package/dist/principal.mjs +8 -0
- package/dist/principal.mjs.map +1 -0
- package/dist/project-ingress.d.ts +58 -0
- package/dist/project-ingress.mjs +104 -0
- package/dist/project-ingress.mjs.map +1 -0
- package/dist/react.mjs +285 -0
- package/dist/react.mjs.map +1 -0
- package/dist/sdk/auth.d.ts +25 -0
- package/dist/sdk/index.d.ts +155 -0
- package/dist/sdk/record-pipelined-steps.d.ts +19 -0
- package/dist/sdk.mjs +245 -0
- package/dist/sdk.mjs.map +1 -0
- package/dist/stream/processor.d.ts +383 -0
- package/dist/stream/processor.mjs +605 -0
- package/dist/stream/processor.mjs.map +1 -0
- package/dist/stream/run.d.ts +61 -0
- package/dist/stream/run.mjs +45 -0
- package/dist/stream/run.mjs.map +1 -0
- package/dist/stream/test-support.d.ts +45 -0
- package/dist/stream/test-support.mjs +196 -0
- package/dist/stream/test-support.mjs.map +1 -0
- package/dist/usingCtx-inzbY1Qz.mjs +57 -0
- package/package.json +93 -30
- package/bin/iterate.js +0 -86
- package/dist/cli-DMS4kJph.mjs +0 -868
- package/dist/cli-DMS4kJph.mjs.map +0 -1
- package/dist/config-DtnR7Lv7.mjs +0 -170
- package/dist/config-DtnR7Lv7.mjs.map +0 -1
- package/dist/index.d.mts +0 -5
- package/dist/index.d.mts.map +0 -1
- package/dist/index.mjs +0 -8
- package/dist/index.mjs.map +0 -1
- package/dist/stream-tui/agent-chat-terminal.d.mts +0 -1
- package/dist/stream-tui/agent-chat-terminal.mjs +0 -933
- package/dist/stream-tui/agent-chat-terminal.mjs.map +0 -1
- package/dist/worker.d.mts +0 -33
- package/dist/worker.mjs +0 -18
- package/dist/worker.mjs.map +0 -1
package/dist/api.d.ts
ADDED
|
@@ -0,0 +1,643 @@
|
|
|
1
|
+
import type { Ai } from "@cloudflare/workers-types";
|
|
2
|
+
import type { InvokeHandle, ItxExpression, ItxExpressionInput } from "./expression.ts";
|
|
3
|
+
import type { ConsentScope } from "./oauth-scopes.ts";
|
|
4
|
+
import type { Principal } from "./principal.ts";
|
|
5
|
+
import type { IngressRouting } from "./project-ingress.ts";
|
|
6
|
+
import type { StreamEvent, StreamEventInput } from "./stream/processor.ts";
|
|
7
|
+
/** What `authenticate` accepts: the browser (its login cookie rode the upgrade), a device or script
|
|
8
|
+
* (its bearer token did — or, on a socket opened bare, presented here as `token`: a static page on
|
|
9
|
+
* another origin cannot put a header on a WebSocket), or the operator (the deployment's admin
|
|
10
|
+
* secret, verified in-band). */
|
|
11
|
+
export type SessionCredentials = {
|
|
12
|
+
type: "from-server-cookie";
|
|
13
|
+
} | {
|
|
14
|
+
type: "bearer";
|
|
15
|
+
token?: string;
|
|
16
|
+
} | {
|
|
17
|
+
type: "admin-secret";
|
|
18
|
+
secret: string;
|
|
19
|
+
as?: {
|
|
20
|
+
email: string;
|
|
21
|
+
};
|
|
22
|
+
};
|
|
23
|
+
/** One page of a context's durable log (`readEvents`). */
|
|
24
|
+
export interface StreamPage {
|
|
25
|
+
events: StreamEvent[];
|
|
26
|
+
scannedThroughOffset: number;
|
|
27
|
+
/** True iff the scan reached the durable mark: nothing more to read until the next commit. */
|
|
28
|
+
atHead: boolean;
|
|
29
|
+
}
|
|
30
|
+
/** `waitForEvent`'s filter: an event type (or one of a list), a floor, a timeout. */
|
|
31
|
+
export type WaitForEventFilter = {
|
|
32
|
+
type?: string | string[];
|
|
33
|
+
afterOffset?: number;
|
|
34
|
+
timeoutMs?: number;
|
|
35
|
+
};
|
|
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
|
+
* line a model reads for the name; it rides the row into `rewriteRules.list()`. */
|
|
39
|
+
export type RewriteRuleConfigured = {
|
|
40
|
+
match: ItxExpressionInput;
|
|
41
|
+
target: ItxExpressionInput | null;
|
|
42
|
+
/** What the name means here, in one line (≤ 500 chars). */
|
|
43
|
+
description?: string;
|
|
44
|
+
};
|
|
45
|
+
/** One row of `rewriteRules.list()` — the tree a context can spell. `context` is the path the row
|
|
46
|
+
* was read from: this context for its own rows and its implicit rows, the target context for the
|
|
47
|
+
* rows a bare hop row (`itx ⇒ itx.builtins.cd(path)`) reaches. A mask lists as `target: null`. */
|
|
48
|
+
export type RewriteRuleListEntry = {
|
|
49
|
+
match: string;
|
|
50
|
+
target: string | null;
|
|
51
|
+
description?: string;
|
|
52
|
+
context: string;
|
|
53
|
+
};
|
|
54
|
+
/** One row of `subscriptions.list()` / `processors.list()`. */
|
|
55
|
+
export type SubscriptionListEntry = {
|
|
56
|
+
name: string;
|
|
57
|
+
target: string;
|
|
58
|
+
consumes?: string[];
|
|
59
|
+
configuredAtOffset: number;
|
|
60
|
+
afterOffset?: number;
|
|
61
|
+
/** Set when this row hosts a facet (a processor). `restarts`: how many times the platform failed
|
|
62
|
+
* the facet at its start and the context restarted it under a fresh loaded identity (a platform
|
|
63
|
+
* defect the context works around; the count is the cheap way to ask "how often, here"). */
|
|
64
|
+
hostedFacet?: {
|
|
65
|
+
name: string;
|
|
66
|
+
className: string;
|
|
67
|
+
cacheKey?: string;
|
|
68
|
+
restarts: number;
|
|
69
|
+
};
|
|
70
|
+
};
|
|
71
|
+
/** A loaded worker's source: its modules, literally, or an itx expression that produces them (then
|
|
72
|
+
* `cacheKey` names the build, and the caller owns "same key ⇒ same code"). */
|
|
73
|
+
export type WorkerSource = Record<string, string> | ItxExpressionInput;
|
|
74
|
+
/** What hosts a class as a durable facet — `facets.get(name, spec)`, `processors.enable(name, spec)`. */
|
|
75
|
+
export type FacetSpec = {
|
|
76
|
+
source: WorkerSource;
|
|
77
|
+
cacheKey?: string;
|
|
78
|
+
className: string;
|
|
79
|
+
};
|
|
80
|
+
/** What `schedules.set` answers: the definition's identity, to cancel exactly it. */
|
|
81
|
+
export type ScheduleReceipt = {
|
|
82
|
+
key: string;
|
|
83
|
+
scheduledAtOffset: number;
|
|
84
|
+
};
|
|
85
|
+
/** A secret's material: one string (`getSecret("/secrets/<name>")` is the whole value) or a JSON
|
|
86
|
+
* object whose string fields `getSecret("/secrets/<name>", { field: "a.b" })` picks — the
|
|
87
|
+
* multidimensional shape a credential exchange needs (`{ username, password, accessToken }`,
|
|
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
|
+
export type SecretMaterial = string | Record<string, unknown>;
|
|
91
|
+
/** How a token endpoint wants the client credential — the RFC 8414 `token_endpoint_auth_methods_supported`
|
|
92
|
+
* registry values, so a provider's discovery document pastes straight in: `client_secret_basic`
|
|
93
|
+
* (HTTP Basic — Google, Slack, the petshop; the default), `client_secret_post` (`client_id` +
|
|
94
|
+
* `client_secret` as form fields — GitHub, Linear), `none` (a public client: `client_id` alone,
|
|
95
|
+
* PKCE stands in for the secret). RFC 6749 §2.3.1 forbids sending two forms at once. */
|
|
96
|
+
export type ClientAuth = "client_secret_basic" | "client_secret_post" | "none";
|
|
97
|
+
/** How the secret's facet re-mints an expired credential, in its own trusted code: the
|
|
98
|
+
* exchange reads this secret's own material, POSTs to an endpoint within the pin, and writes the
|
|
99
|
+
* answer back into the material — `accessToken` (and a rotated `refreshToken`). Triggered on a 401
|
|
100
|
+
* from the pinned host, and on first use when the placeholder's field is not there yet. */
|
|
101
|
+
export type SecretRefresh =
|
|
102
|
+
/** RFC 6749 §6, the refresh_token grant: `refreshToken` + `clientId` (+ `clientSecret` for a
|
|
103
|
+
* confidential client) from the material → `accessToken` (+ the newest `refreshToken`). Google,
|
|
104
|
+
* GitHub, an MCP server's authorization server, the petshop fixture. */
|
|
105
|
+
{
|
|
106
|
+
kind: "oauth-refresh-token";
|
|
107
|
+
tokenEndpoint: string;
|
|
108
|
+
clientAuth?: ClientAuth;
|
|
109
|
+
}
|
|
110
|
+
/** The username/password → session-token archetype's one instance so far, Waitrose's login: POST
|
|
111
|
+
* the Android app's `NewSession` GraphQL mutation with `username`/`password` from the material →
|
|
112
|
+
* `accessToken`. Waitrose has no refresh grant — re-login IS the refresh — so one strategy covers
|
|
113
|
+
* the first-use mint and the 401 re-mint. Vendor-specific on purpose: a caller-supplied login
|
|
114
|
+
* template would put an arbitrary request body in trusted code; a second vendor of this shape
|
|
115
|
+
* earns the generalization, not before. */
|
|
116
|
+
| {
|
|
117
|
+
kind: "waitrose-session";
|
|
118
|
+
graphqlUrl: string;
|
|
119
|
+
};
|
|
120
|
+
/** A secret's catalog entry — `secrets.list()` — its path, the pin, the strategy's KIND and when
|
|
121
|
+
* it was first set; never a value (the owner root's fold of the `secret/set` certificates). */
|
|
122
|
+
export type SecretCatalogEntry = {
|
|
123
|
+
path: string;
|
|
124
|
+
urls: string[];
|
|
125
|
+
refresh?: SecretRefresh["kind"];
|
|
126
|
+
createdAt: string;
|
|
127
|
+
};
|
|
128
|
+
/** The input an agent gives `itx.secrets.collectFromUser`: the write-only secret path, the
|
|
129
|
+
* origins its material may reach, and the short explanation the authenticated collection form
|
|
130
|
+
* shows its user. */
|
|
131
|
+
export type CollectSecretInput = {
|
|
132
|
+
path: string;
|
|
133
|
+
egress: {
|
|
134
|
+
urls: string[];
|
|
135
|
+
};
|
|
136
|
+
description?: string;
|
|
137
|
+
};
|
|
138
|
+
/** A secret collection link. Sending this asks the person to authenticate to the intended
|
|
139
|
+
* Iterate instance; it is not itself permission to write a secret. */
|
|
140
|
+
export type CollectSecretLink = {
|
|
141
|
+
path: string;
|
|
142
|
+
url: string;
|
|
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
|
+
};
|
|
185
|
+
/** A context (a project, a user, an organization): every `itx` root, reached through `invoke`. */
|
|
186
|
+
export interface IterateContextApi {
|
|
187
|
+
invoke(call: ItxExpressionInput, ...args: unknown[]): Promise<unknown>;
|
|
188
|
+
/** Another context of this project, by path (`..` and `/` allowed; the global namespace is not). */
|
|
189
|
+
cd(path: string): IterateContextApi;
|
|
190
|
+
/** Durable batches appended after a deadline (`afterMs`), at an instant (`at`) or on an interval
|
|
191
|
+
* (`everyMs`); a key set again is replaced; a receipt cancels exactly the definition it names. */
|
|
192
|
+
schedules: {
|
|
193
|
+
set(input: {
|
|
194
|
+
key: string | [string, string];
|
|
195
|
+
when: {
|
|
196
|
+
at: string;
|
|
197
|
+
} | {
|
|
198
|
+
afterMs: number;
|
|
199
|
+
} | {
|
|
200
|
+
everyMs: number;
|
|
201
|
+
};
|
|
202
|
+
events: StreamEventInput[];
|
|
203
|
+
}, options?: {
|
|
204
|
+
idempotencyKey?: string;
|
|
205
|
+
}): Promise<ScheduleReceipt>;
|
|
206
|
+
cancel(schedule: string | [string, string] | ScheduleReceipt): Promise<StreamEvent[]>;
|
|
207
|
+
};
|
|
208
|
+
whoami(): {
|
|
209
|
+
projectId: string;
|
|
210
|
+
path: string;
|
|
211
|
+
projectSlug?: string;
|
|
212
|
+
projectUrl?: string;
|
|
213
|
+
} | Promise<{
|
|
214
|
+
projectId: string;
|
|
215
|
+
path: string;
|
|
216
|
+
projectSlug?: string;
|
|
217
|
+
projectUrl?: string;
|
|
218
|
+
}>;
|
|
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>;
|
|
238
|
+
readEvents(afterOffset?: number, limit?: number, options?: {
|
|
239
|
+
includeEphemeral?: boolean;
|
|
240
|
+
}): Promise<StreamPage>;
|
|
241
|
+
waitForEvent(filter?: WaitForEventFilter): Promise<StreamEvent>;
|
|
242
|
+
fetch(request: Request): Promise<Response>;
|
|
243
|
+
kv: {
|
|
244
|
+
get(key: string): Promise<string | null>;
|
|
245
|
+
put(key: string, value: string): Promise<{
|
|
246
|
+
ok: true;
|
|
247
|
+
}>;
|
|
248
|
+
delete(key: string): Promise<{
|
|
249
|
+
ok: true;
|
|
250
|
+
}>;
|
|
251
|
+
list(prefix?: string): Promise<{
|
|
252
|
+
keys: string[];
|
|
253
|
+
}>;
|
|
254
|
+
};
|
|
255
|
+
/** The project's secrets, WRITE-ONLY: a secret IS its path (`/secrets/<name>`, the name
|
|
256
|
+
* `[a-zA-Z0-9._-]+`), and the path is what an outbound request's placeholder spells —
|
|
257
|
+
* `getSecret("/secrets/<name>")` in a URL or a header substitutes to the value at egress, and only
|
|
258
|
+
* towards the ORIGINS in `urls` (required: a secret is always pinned). `set` stores a string or a
|
|
259
|
+
* JSON object (`refresh` names the strategy that re-mints an expiring credential); `delete`
|
|
260
|
+
* forgets it (re-settable); `list` is the catalog — paths, pins, strategy kinds, when first set —
|
|
261
|
+
* never a value. Every change is one fact on the secret's path (`secret/set`, `secret/deleted`),
|
|
262
|
+
* attributed to the caller and cross-posted to the root, so the log says who set what and when. */
|
|
263
|
+
secrets: {
|
|
264
|
+
set(path: string, material: SecretMaterial, options: {
|
|
265
|
+
urls: string[];
|
|
266
|
+
refresh?: SecretRefresh;
|
|
267
|
+
}): Promise<{
|
|
268
|
+
path: string;
|
|
269
|
+
}>;
|
|
270
|
+
delete(path: string): Promise<{
|
|
271
|
+
path: string;
|
|
272
|
+
}>;
|
|
273
|
+
list(): Promise<SecretCatalogEntry[]>;
|
|
274
|
+
/** Build the authenticated Dash link where a person enters a value an agent must never see in
|
|
275
|
+
* chat. The link fixes the project, platform instance, secret path and egress pin. If called
|
|
276
|
+
* from an agent context, a successful submission messages that same agent with the path only. */
|
|
277
|
+
collectFromUser(input: CollectSecretInput): Promise<CollectSecretLink>;
|
|
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
|
+
};
|
|
294
|
+
/** The table this context resolves against, described — the tree a model reads. `list()` follows a
|
|
295
|
+
* bare hop row into the context it names (a Durable Object hop, hence async). */
|
|
296
|
+
rewriteRules: {
|
|
297
|
+
list(): Promise<RewriteRuleListEntry[]>;
|
|
298
|
+
get(match: string): Promise<RewriteRuleListEntry | null>;
|
|
299
|
+
resolve(call: ItxExpressionInput): string[];
|
|
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. */
|
|
303
|
+
facets: {
|
|
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>;
|
|
312
|
+
};
|
|
313
|
+
subscriptions: {
|
|
314
|
+
list(): SubscriptionListEntry[];
|
|
315
|
+
get(name: string): SubscriptionListEntry | null;
|
|
316
|
+
};
|
|
317
|
+
/** The rpc stubs lent to this context right now, by key (a live session's `provide`). */
|
|
318
|
+
rpcStubs: {
|
|
319
|
+
list(): string[];
|
|
320
|
+
};
|
|
321
|
+
processors: {
|
|
322
|
+
enable(name: string, spec?: (FacetSpec & {
|
|
323
|
+
consumes?: string[];
|
|
324
|
+
}) | {
|
|
325
|
+
consumes?: string[];
|
|
326
|
+
}): Promise<{
|
|
327
|
+
name: string;
|
|
328
|
+
}>;
|
|
329
|
+
disable(name: string): Promise<void>;
|
|
330
|
+
list(): SubscriptionListEntry[];
|
|
331
|
+
/** A hosted processor's claim on this context's alarm: "revive me by `at`" (a facet with a
|
|
332
|
+
* `runInBackground` attempt in flight), or `null` to release it. */
|
|
333
|
+
claim(name: string, at: number | null): Promise<void>;
|
|
334
|
+
};
|
|
335
|
+
workers: {
|
|
336
|
+
get(spec: {
|
|
337
|
+
source: WorkerSource;
|
|
338
|
+
cacheKey?: string;
|
|
339
|
+
className?: string;
|
|
340
|
+
props?: unknown;
|
|
341
|
+
}): InvokeHandle;
|
|
342
|
+
};
|
|
343
|
+
/** A subscription: a pure itx expression, or a live callback lent to the registry (what live state
|
|
344
|
+
* uses); `null` removes the row. The handle's dispose removes it too. */
|
|
345
|
+
subscribe(input: {
|
|
346
|
+
name?: string;
|
|
347
|
+
target: ItxExpressionInput | ((events: unknown[], range: unknown) => void) | null;
|
|
348
|
+
consumes?: string[];
|
|
349
|
+
afterOffset?: number;
|
|
350
|
+
}): Promise<{
|
|
351
|
+
[Symbol.dispose](): void;
|
|
352
|
+
}>;
|
|
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<{
|
|
360
|
+
[Symbol.dispose](): void;
|
|
361
|
+
}>;
|
|
362
|
+
/** A script — the text of `async (itx) => { … }` — run once against this context, on its log:
|
|
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"`). */
|
|
366
|
+
run(script: string): Promise<unknown>;
|
|
367
|
+
/** The project's repos, workspaces and agents as domain objects — one shape each: `get(path)` is
|
|
368
|
+
* the entity's facet on the context at `path` (its verbs, plus the typed `append` on that
|
|
369
|
+
* context), `list()` the project catalog, `create(path)` the creation saga on that path (the
|
|
370
|
+
* parent link the caller's context writes first, then the processor row, the request, the
|
|
371
|
+
* terminal fact — created, or create-failed thrown), `delete(path)` the deletion saga (the
|
|
372
|
+
* request, `deleted` cross-posted to `/`, the row disabled). A relative `path` means the caller's. */
|
|
373
|
+
repos: {
|
|
374
|
+
get(path: string): InvokeHandle;
|
|
375
|
+
list(): Promise<{
|
|
376
|
+
path: string;
|
|
377
|
+
createdAt: string;
|
|
378
|
+
}[]>;
|
|
379
|
+
create(path: string): Promise<{
|
|
380
|
+
path: string;
|
|
381
|
+
}>;
|
|
382
|
+
delete(path: string): Promise<{
|
|
383
|
+
path: string;
|
|
384
|
+
}>;
|
|
385
|
+
};
|
|
386
|
+
workspaces: {
|
|
387
|
+
get(path: string): InvokeHandle;
|
|
388
|
+
list(): Promise<{
|
|
389
|
+
path: string;
|
|
390
|
+
createdAt: string;
|
|
391
|
+
}[]>;
|
|
392
|
+
create(path: string): Promise<{
|
|
393
|
+
path: string;
|
|
394
|
+
}>;
|
|
395
|
+
delete(path: string): Promise<{
|
|
396
|
+
path: string;
|
|
397
|
+
}>;
|
|
398
|
+
};
|
|
399
|
+
/** Workers AI under this context's capability rules. */
|
|
400
|
+
ai: Ai;
|
|
401
|
+
/** Files stored in the project's object store. */
|
|
402
|
+
files: {
|
|
403
|
+
get(path: string): InvokeHandle & {
|
|
404
|
+
put(input: {
|
|
405
|
+
contentType?: string;
|
|
406
|
+
data: Uint8Array | ArrayBuffer | string;
|
|
407
|
+
}): Promise<{
|
|
408
|
+
path: string;
|
|
409
|
+
contentType: string;
|
|
410
|
+
size: number;
|
|
411
|
+
}>;
|
|
412
|
+
bytes(): Promise<Uint8Array>;
|
|
413
|
+
head(): Promise<{
|
|
414
|
+
path: string;
|
|
415
|
+
contentType: string;
|
|
416
|
+
size: number;
|
|
417
|
+
} | null>;
|
|
418
|
+
delete(): Promise<void>;
|
|
419
|
+
url(input?: {
|
|
420
|
+
method?: "GET" | "PUT";
|
|
421
|
+
expiresInSeconds?: number;
|
|
422
|
+
}): Promise<{
|
|
423
|
+
url: string;
|
|
424
|
+
expiresAt: string;
|
|
425
|
+
}>;
|
|
426
|
+
};
|
|
427
|
+
list(prefix?: string): Promise<{
|
|
428
|
+
path: string;
|
|
429
|
+
contentType: string;
|
|
430
|
+
size: number;
|
|
431
|
+
}[]>;
|
|
432
|
+
};
|
|
433
|
+
}
|
|
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_…`). */
|
|
439
|
+
export interface GrantRecord {
|
|
440
|
+
id: string;
|
|
441
|
+
clientId?: string;
|
|
442
|
+
logoUri?: string;
|
|
443
|
+
clientDomain?: string;
|
|
444
|
+
name: 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[];
|
|
451
|
+
createdAt: number;
|
|
452
|
+
expiresAt: number | null;
|
|
453
|
+
lastUsedAt: number | null;
|
|
454
|
+
expired: boolean;
|
|
455
|
+
/** The grant this very session rides on. */
|
|
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;
|
|
460
|
+
}
|
|
461
|
+
/** What the consent screen shows for an authorization request. */
|
|
462
|
+
export type ConsentAnswer = {
|
|
463
|
+
kind: "consent";
|
|
464
|
+
query: string;
|
|
465
|
+
clientName: string;
|
|
466
|
+
email: string;
|
|
467
|
+
projects: ProjectRecord[];
|
|
468
|
+
orgs: OrgRecord[];
|
|
469
|
+
projectBound: boolean;
|
|
470
|
+
/** the scopes the request asked for, each with the page's copy (oauth-scopes.ts) */
|
|
471
|
+
scopes: ConsentScope[];
|
|
472
|
+
denyLocation: string;
|
|
473
|
+
/** how projects are reached over HTTP (project-ingress.ts) — the page composes a project's URL */
|
|
474
|
+
ingressRouting: IngressRouting;
|
|
475
|
+
/** the onboarding step's first draft of an organization name, from the person's name or email */
|
|
476
|
+
suggestedOrganizationName: string;
|
|
477
|
+
} | {
|
|
478
|
+
kind: "redirect";
|
|
479
|
+
location: string;
|
|
480
|
+
} | {
|
|
481
|
+
kind: "invalid";
|
|
482
|
+
description: string;
|
|
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
|
+
}
|
|
492
|
+
/** An organization as the session lists it: its minted id, its free-text name, the person's role
|
|
493
|
+
* in it, and how many projects it holds (every one of them, not only those this grant lists). */
|
|
494
|
+
export interface OrgRecord {
|
|
495
|
+
id: string;
|
|
496
|
+
name: string;
|
|
497
|
+
role?: "owner" | "member";
|
|
498
|
+
projects: number;
|
|
499
|
+
}
|
|
500
|
+
/** A project as the catalog lists it: addressed by `id` everywhere (`projects.get`, a grant's list,
|
|
501
|
+
* an MCP call's `project`, an app's URL); `slug` is the label of its hostnames and its name to a
|
|
502
|
+
* person. The id is the one stable identifier. */
|
|
503
|
+
export interface ProjectRecord {
|
|
504
|
+
id: string;
|
|
505
|
+
slug: string;
|
|
506
|
+
orgId: string;
|
|
507
|
+
}
|
|
508
|
+
/** The session `authenticate` vends: who is calling, and the contexts they reach. */
|
|
509
|
+
export interface IterateSessionApi {
|
|
510
|
+
whoami(): Principal;
|
|
511
|
+
/** Safe bootstrap data for every app, regardless of which host serves it. */
|
|
512
|
+
info(): {
|
|
513
|
+
principal: Principal;
|
|
514
|
+
scopes: string[];
|
|
515
|
+
platformOrigin: string;
|
|
516
|
+
/** how projects are reached over HTTP (project-ingress.ts `projectUrlOf`); null ⇒ no ingress */
|
|
517
|
+
ingressRouting: IngressRouting;
|
|
518
|
+
/** the MCP server's origin (the dash's connect page) — "" when this deployment serves none */
|
|
519
|
+
mcpOrigin: string;
|
|
520
|
+
};
|
|
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. */
|
|
524
|
+
grants: {
|
|
525
|
+
list(cursor?: string): Promise<{
|
|
526
|
+
items: GrantRecord[];
|
|
527
|
+
cursor?: string;
|
|
528
|
+
projects: ProjectRecord[];
|
|
529
|
+
canMintToken: boolean;
|
|
530
|
+
}>;
|
|
531
|
+
end(grantId: string): Promise<unknown>;
|
|
532
|
+
endCurrent(): Promise<unknown>;
|
|
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;
|
|
543
|
+
token: string;
|
|
544
|
+
expiresAt: number | null;
|
|
545
|
+
}>;
|
|
546
|
+
};
|
|
547
|
+
/** The consent screen's methods (the OAuth authorize flow): describe a request, approve it. */
|
|
548
|
+
consent: {
|
|
549
|
+
describe(query: string): Promise<ConsentAnswer>;
|
|
550
|
+
approve(input: {
|
|
551
|
+
query: string;
|
|
552
|
+
projects: string[];
|
|
553
|
+
}): Promise<{
|
|
554
|
+
redirectTo: string;
|
|
555
|
+
} | {
|
|
556
|
+
error: string;
|
|
557
|
+
}>;
|
|
558
|
+
};
|
|
559
|
+
projects: {
|
|
560
|
+
list(): Promise<ProjectRecord[]>;
|
|
561
|
+
/** the project's root context, by its slug or its id */
|
|
562
|
+
get(project: string): Promise<IterateContextApi>;
|
|
563
|
+
/** Config repository presets available on this platform, besides the default config a
|
|
564
|
+
* creation that names no template gets. */
|
|
565
|
+
templates(): Promise<{
|
|
566
|
+
label: string;
|
|
567
|
+
reference: string;
|
|
568
|
+
}[]>;
|
|
569
|
+
/** a new project: `project` is slugged into its hostname label, its id is minted — the returned
|
|
570
|
+
* context's `whoami()` says it, so does `list()` */
|
|
571
|
+
create(input: {
|
|
572
|
+
project: string;
|
|
573
|
+
orgId?: string;
|
|
574
|
+
configRepoTemplate?: string;
|
|
575
|
+
/** Operator-only recovery: retain the source project identity from a project seed. */
|
|
576
|
+
restoreProjectId?: string;
|
|
577
|
+
}): Promise<IterateContextApi>;
|
|
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). */
|
|
584
|
+
organizations: {
|
|
585
|
+
list(): Promise<OrgRecord[]>;
|
|
586
|
+
/** the organization's context — `session.user` for an organization — by membership */
|
|
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>;
|
|
636
|
+
};
|
|
637
|
+
user: IterateContextApi;
|
|
638
|
+
logout(): unknown;
|
|
639
|
+
}
|
|
640
|
+
/** THE `/api` ROOT — the one thing a fresh capnweb connection holds. */
|
|
641
|
+
export interface IterateApi {
|
|
642
|
+
authenticate(credentials: SessionCredentials): Promise<IterateSessionApi>;
|
|
643
|
+
}
|
package/dist/api.mjs
ADDED
|
File without changes
|