esoul-sdk 0.17.0 → 0.19.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 (54) hide show
  1. package/api-reference.md +1429 -11
  2. package/bin/esoul-device-exec.mjs +99 -0
  3. package/bin/esoul-device-program.mjs +146 -0
  4. package/dist/device.d.ts +161 -0
  5. package/dist/device.js +15 -0
  6. package/dist/index.d.ts +2 -0
  7. package/dist/index.js +2 -0
  8. package/dist/machine/capabilities.d.ts +92 -0
  9. package/dist/machine/capabilities.js +245 -0
  10. package/dist/machine/cli.d.ts +2 -0
  11. package/dist/machine/cli.js +232 -0
  12. package/dist/machine/client.d.ts +46 -0
  13. package/dist/machine/client.js +91 -0
  14. package/dist/machine/commands.d.ts +178 -0
  15. package/dist/machine/commands.js +281 -0
  16. package/dist/machine/digest.d.ts +26 -0
  17. package/dist/machine/digest.js +71 -0
  18. package/dist/machine/files.d.ts +188 -0
  19. package/dist/machine/files.js +511 -0
  20. package/dist/machine/grant.d.ts +49 -0
  21. package/dist/machine/grant.js +69 -0
  22. package/dist/machine/index.d.ts +21 -0
  23. package/dist/machine/index.js +21 -0
  24. package/dist/machine/program-api.d.ts +101 -0
  25. package/dist/machine/program-api.js +22 -0
  26. package/dist/machine/program-manager.d.ts +61 -0
  27. package/dist/machine/program-manager.js +600 -0
  28. package/dist/machine/protocol.d.ts +52 -0
  29. package/dist/machine/protocol.js +88 -0
  30. package/dist/machine/runner.d.ts +43 -0
  31. package/dist/machine/runner.js +255 -0
  32. package/dist/machine/service.d.ts +36 -0
  33. package/dist/machine/service.js +240 -0
  34. package/dist/machine/shell.d.ts +65 -0
  35. package/dist/machine/shell.js +474 -0
  36. package/dist/machine/state.d.ts +50 -0
  37. package/dist/machine/state.js +96 -0
  38. package/dist/machine/supervisor.d.ts +58 -0
  39. package/dist/machine/supervisor.js +275 -0
  40. package/dist/machine/wake.d.ts +31 -0
  41. package/dist/machine/wake.js +105 -0
  42. package/dist/manifest.d.ts +595 -0
  43. package/dist/manifest.js +72 -0
  44. package/dist/react.d.ts +104 -0
  45. package/dist/react.js +36 -0
  46. package/dist/server.d.ts +38 -0
  47. package/dist/server.js +20 -0
  48. package/docs/05-ui.md +37 -0
  49. package/docs/18-your-computer.md +350 -0
  50. package/llms-full.txt +391 -0
  51. package/llms.txt +1 -0
  52. package/package.json +10 -4
  53. package/schemas/plugin.schema.json +317 -0
  54. package/scripts/build-api-reference.mjs +1 -0
package/api-reference.md CHANGED
@@ -7,11 +7,11 @@ the complete list it teaches from. In a Forge workbench the source itself is rea
7
7
  Regenerate: `npm run docs:api`.
8
8
 
9
9
  ==============================================================================
10
- ## `esoul-sdk` — 185 exports
10
+ ## `esoul-sdk` — 196 exports
11
11
 
12
12
  Manifest, events, tools, ops, bindings, contracts, charts, access words — what app.tsx and the shared modules import.
13
13
 
14
- ### Functions and values (109)
14
+ ### Functions and values (115)
15
15
 
16
16
  #### `ASSET_APP_MAX_BYTES` — const · src/assets.ts
17
17
 
@@ -189,6 +189,14 @@ Every declared attribute a grant carries, typed; an undeclared or ill-typed one
189
189
  function coerceAttrs(types: Record<string, AttrType>, raw: Record<string, unknown> | undefined | null): Record<string, AttrValue>
190
190
  ```
191
191
 
192
+ #### `COMMAND_NAME_RE` — const · src/machine/commands.ts
193
+
194
+ What a command name may look like: lowercase, starts with a letter, up to 40 characters.
195
+
196
+ ```ts
197
+ const COMMAND_NAME_RE: RegExp
198
+ ```
199
+
192
200
  #### `compareFileNames` — function · src/files.ts
193
201
 
194
202
  Natural name order: "frame_2" before "frame_10".
@@ -245,6 +253,14 @@ What a mounted editor should do with the value the store now holds for the item
245
253
  function decideReconcile<T>(args: { incoming: T; /** What the editor last knew the server to hold (its merge base). */ base: T | undefined; /** What the editor shows right now. */ current: T; equal: (a: T, b: T) => boolean; /** A local save is scheduled and not yet dispatched. */ pending: boolean; /** The user is in the middle of editing (focus, an open field). */ editing: boolean; /** isOlderOwnWrite(...) for the incoming state. */ olderOwn: boolean; /** The editor supplies a three-way merge. */ canMerge: boolean; /** May a merge be applied while the person is editing? Default true. An editor that can only apply by * replacing its whole surface (an input in progress would be lost) sets false: it merges once idle. */ mergeWhileEditing?: boolean; }): ReconcileDecision
246
254
  ```
247
255
 
256
+ #### `DEFAULT_TIMEOUT_SECONDS` — const · src/machine/commands.ts
257
+
258
+ A command's time limit when it declares none: one hour.
259
+
260
+ ```ts
261
+ const DEFAULT_TIMEOUT_SECONDS: 3600
262
+ ```
263
+
248
264
  #### `defineBindingEvent` — function · src/bindings.ts
249
265
 
250
266
  The event definition for one app's slots. `knownSlots` comes from the manifest, so an event naming a slot the app never declared is ignored rather than inventing one.
@@ -277,6 +293,22 @@ Refold-stable id from event payload — the ONLY way a processor may mint an id
277
293
  function deterministicReducerId(prefix: string, seed: unknown): string
278
294
  ```
279
295
 
296
+ #### `deviceBlockProblems` — function · src/machine/commands.ts
297
+
298
+ Every problem with a `device` block, in words an author can act on. Empty = valid.
299
+
300
+ ```ts
301
+ function deviceBlockProblems(block: unknown): string[]
302
+ ```
303
+
304
+ #### `DeviceCommandError` — class · src/machine/commands.ts
305
+
306
+ Why a command was refused: `unknown_command` (not declared), `bad_param` (missing, extra or ill-typed), `bad_spec`. The message says which, in words.
307
+
308
+ ```ts
309
+ class DeviceCommandError extends Error { … }
310
+ ```
311
+
280
312
  #### `fenceMetaOf` — function · src/editor-sync.ts
281
313
 
282
314
  The fence record of `itemKey` in an app state (`__itemRevs`), if any.
@@ -509,6 +541,14 @@ Does an entry pass these listing options? The one rule every provider's listing
509
541
  function matchesListOptions(entry: Pick<FileEntry, "name" | "kind">, opts?: FileListOptions | null): boolean
510
542
  ```
511
543
 
544
+ #### `MAX_TIMEOUT_SECONDS` — const · src/machine/commands.ts
545
+
546
+ The longest time limit a command may declare: seven days.
547
+
548
+ ```ts
549
+ const MAX_TIMEOUT_SECONDS: number
550
+ ```
551
+
512
552
  #### `mergeFields` — function · src/editor-sync.ts
513
553
 
514
554
  Three-way merge of a flat record of fields (a form: to/cc/subject/body; a node's config). A field changed differently on both sides is a conflict → `null`.
@@ -714,7 +754,7 @@ const PluginConnectionSchema: z.ZodEffects<z.ZodObject<{ key: z.ZodString; kind:
714
754
  The zod schema `plugin.json` must satisfy — the sync and `check_app` refuse a manifest that fails it, with the path.
715
755
 
716
756
  ```ts
717
- const PluginManifestSchema: z.ZodObject<{ manifestVersion: z.ZodLiteral<1>; id: z.ZodString; name: z.ZodString; version: z.ZodString; description: z.ZodString; applicationType: z.ZodString; entry: z.ZodString; icon: z.ZodOptional<z.ZodString>; author: z.ZodOptional<z.ZodObject<{ name: z.ZodString; email: z.ZodOptional<z.ZodString>; url: z.ZodOptional<z.ZodString>; }, "strip", z.ZodTypeAny, { name?: string; email?: string; url?: string; }, { name?: string; email?: string; url?: string; }>>; kickableTasks: z.ZodDefault<z.ZodOptional<z.ZodUnion<[z.ZodArray<z.ZodString, "many">, z.ZodRecord<z.ZodString, z.ZodObject<{ access: z.ZodDefault<z.ZodOptional<z.ZodUnion<[z.ZodEnum<["write", "read", "public", "token"]>, z.ZodArray<z.ZodString, "many">]>>>; requires: z.ZodOptional<z.ZodLiteral<"account">>; }, "strict", z.ZodTypeAny, { access?: "write" | "read" | "public" | "token" | string[]; requires?: "account"; }, { access?: "write" | "read" | "public" | "token" | string[]; requires?: "account"; }>>]>>>; webhooks: z.ZodDefault<z.ZodOptional<z.ZodArray<z.ZodString, "many">>>; ops: z.ZodDefault<z.ZodOptional<z.ZodUnion<[z.ZodArray<z.ZodString, "many">, z.ZodRecord<z.ZodString, z.ZodObject<{ access: z.ZodDefault<z.ZodOptional<z.ZodUnion<[z.ZodEnum<["write", "read", "public", "token"]>, z.ZodArray<z.ZodString, "many">]>>>; requires: z.ZodOptional<z.ZodLiteral<"account">>; }, "strict", z.ZodTypeAny, { access?: "write" | "read" | "public" | "token" | string[]; requires?: "account"; }, { access?: "write" | "read" | "public" | "token" | string[]; requires?: "account"; }>>]>>>; routes: z.ZodDefault<z.ZodOptional<z.ZodUnion<[z.ZodArray<z.ZodString, "many">, z.ZodRecord<z.ZodString, z.ZodObject<{ access: z.ZodDefault<z.ZodOptional<z.ZodUnion<[z.ZodEnum<["write", "read", "public", "token"]>, z.ZodArray<z.ZodString, "many">]>>>; requires: z.ZodOptional<z.ZodLiteral<"account">>; }, "strict", z.ZodTypeAny, { access?: "write" | "read" | "public" | "token" | string[]; requires?: "account"; }, { access?: "write" | "read" | "public" | "token" | string[]; requires?: "account"; }>>]>>>; pollTasks: z.ZodDefault<z.ZodOptional<z.ZodArray<z.ZodObject<{ task: z.ZodString; everyMinutes: z.ZodNumber; }, "strip", z.ZodTypeAny, { task?: string; everyMinutes?: number; }, { task?: string; everyMinutes?: number; }>, "many">>>; platformApi: z.ZodOptional<z.ZodObject<{ min: z.ZodString; max: z.ZodOptional<z.ZodString>; }, "strip", z.ZodTypeAny, { min?: string; max?: string; }, { min?: string; max?: string; }>>; scopes: z.ZodDefault<z.ZodOptional<z.ZodArray<z.ZodString, "many">>>; roles: z.ZodOptional<z.ZodEffects<z.ZodObject<{ vocabulary: z.ZodArray<z.ZodString, "many">; default: z.ZodDefault<z.ZodOptional<z.ZodObject<{ owner: z.ZodOptional<z.ZodString>; "member-edit": z.ZodOptional<z.ZodString>; "member-readonly": z.ZodOptional<z.ZodString>; visitor: z.ZodOptional<z.ZodString>; anonymous: z.ZodOptional<z.ZodString>; agent: z.ZodOptional<z.ZodString>; }, "strict", z.ZodTypeAny, { owner?: string; "member-edit"?: string; "member-readonly"?: string; visitor?: string; anonymous?: string; agent?: string; }, { owner?: string; "member-edit"?: string; "member-readonly"?: string; visitor?: string; anonymous?: string; agent?: string; }>>>; describe: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>; attributes: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodEnum<["string", "int", "boolean"]>>>; custom: z.ZodOptional<z.ZodObject<{ attributes: z.ZodOptional<z.ZodArray<z.ZodString, "many">>; models: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodObject<{ where: z.ZodOptional<z.ZodArray<z.ZodString, "many">>; hide: z.ZodOptional<z.ZodArray<z.ZodString, "many">>; update: z.ZodOptional<z.ZodObject<{ fields: z.ZodOptional<z.ZodArray<z.ZodString, "many">>; transitions: z.ZodOptional<z.ZodString>; }, "strict", z.ZodTypeAny, { fields?: string[]; transitions?: string; }, { fields?: string[]; transitions?: string; }>>; }, "strict", z.ZodTypeAny, { where?: string[]; hide?: string[]; update?: { fields?: string[]; transitions?: string; }; }, { where?: string[]; hide?: string[]; update?: { fields?: string[]; transitions?: string; }; }>>>; ops: z.ZodOptional<z.ZodArray<z.ZodString, "many">>; }, "strict", z.ZodTypeAny, { ops?: string[]; attributes?: string[]; models?: Record<string, { where?: string[]; hide?: string[]; update?: { fields?: string[]; transitions?: string; }; }>; }, { ops?: string[]; attributes?: string[]; models?: Record<string, { where?: string[]; hide?: string[]; update?: { fields?: string[]; transitions?: string; }; }>; }>>; }, "strict", z.ZodTypeAny, { default?: { owner?: string; "member-edit"?: string; "member-readonly"?: string; visitor?: string; anonymous?: string; agent?: string; }; custom?: { ops?: string[]; attributes?: string[]; models?: Record<string, { where?: string[]; hide?: string[]; update?: { fields?: string[]; transitions?: string; }; }>; }; vocabulary?: string[]; describe?: Record<string, string>; attributes?: Record<string, "string" | "boolean" | "int">; }, { default?: { owner?: string; "member-edit"?: string; "member-readonly"?: string; visitor?: string; anonymous?: string; agent?: string; }; custom?: { ops?: string[]; attributes?: string[]; models?: Record<string, { where?: string[]; hide?: string[]; update?: { fields?: string[]; transitions?: string; }; }>; }; vocabulary?: string[]; describe?: Record<string, string>; attributes?: Record<string, "string" | "boolean" | "int">; }>, { default?: { owner?: string; "member-edit"?: string; "member-readonly"?: string; visitor?: string; anonymous?: string; agent?: string; }; custom?: { ops?: string[]; attributes?: string[]; models?: Record<string, { where?: string[]; hide?: string[]; update?: { fields?: string[]; transitions?: string; }; }>; }; vocabulary?: string[]; describe?: Record<string, string>; attributes?: Record<string, "string" | "boolean" | "int">; }, { default?: { owner?: string; "member-edit"?: string; "member-readonly"?: string; visitor?: string; anonymous?: string; agent?: string; }; custom?: { ops?: string[]; attributes?: string[]; models?: Record<string, { where?: string[]; hide?: string[]; update?: { fields?: string[]; transitions?: string; }; }>; }; vocabulary?: string[]; describe?: Record<string, string>; attributes?: Record<string, "string" | "boolean" | "int">; }>>; workspaceTools: z.ZodDefault<z.ZodOptional<z.ZodArray<z.ZodString, "many">>>; connections: z.ZodDefault<z.ZodOptional<z.ZodArray<z.ZodEffects<z.ZodObject<{ key: z.ZodString; kind: z.ZodEnum<["oauth2", "apiKey"]>; label: z.ZodString; description: z.ZodOptional<z.ZodString>; authorizeUrl: z.ZodOptional<z.ZodString>; tokenUrl: z.ZodOptional<z.ZodString>; oauthScopes: z.ZodOptional<z.ZodArray<z.ZodString, "many">>; clientIdEnv: z.ZodOptional<z.ZodString>; clientSecretEnv: z.ZodOptional<z.ZodString>; headerNames: z.ZodOptional<z.ZodArray<z.ZodString, "many">>; }, "strict", z.ZodTypeAny, { key?: string; kind?: "oauth2" | "apiKey"; label?: string; description?: string; authorizeUrl?: string; tokenUrl?: string; oauthScopes?: string[]; clientIdEnv?: string; clientSecretEnv?: string; headerNames?: string[]; }, { key?: string; kind?: "oauth2" | "apiKey"; label?: string; description?: string; authorizeUrl?: string; tokenUrl?: string; oauthScopes?: string[]; clientIdEnv?: string; clientSecretEnv?: string; headerNames?: string[]; }>, { key?: string; kind?: "oauth2" | "apiKey"; label?: string; description?: string; authorizeUrl?: string; tokenUrl?: string; oauthScopes?: string[]; clientIdEnv?: string; clientSecretEnv?: string; headerNames?: string[]; }, { key?: string; kind?: "oauth2" | "apiKey"; label?: string; description?: string; authorizeUrl?: string; tokenUrl?: string; oauthScopes?: string[]; clientIdEnv?: string; clientSecretEnv?: string; headerNames?: string[]; }>, "many">>>; fileSources: z.ZodOptional<z.ZodEffects<z.ZodObject<{ workspace: z.ZodOptional<z.ZodEnum<["read", "readwrite"]>>; providers: z.ZodDefault<z.ZodOptional<z.ZodArray<z.ZodString, "many">>>; write: z.ZodOptional<z.ZodArray<z.ZodString, "many">>; }, "strict", z.ZodTypeAny, { write?: string[]; workspace?: "read" | "readwrite"; providers?: string[]; }, { write?: string[]; workspace?: "read" | "readwrite"; providers?: string[]; }>, { write?: string[]; workspace?: "read" | "readwrite"; providers?: string[]; }, { write?: string[]; workspace?: "read" | "readwrite"; providers?: string[]; }>>; fileProviders: z.ZodDefault<z.ZodOptional<z.ZodArray<z.ZodObject<{ key: z.ZodString; connectionKey: z.ZodString; label: z.ZodString; }, "strict", z.ZodTypeAny, { key?: string; label?: string; connectionKey?: string; }, { key?: string; label?: string; connectionKey?: string; }>, "many">>>; uses: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodObject<{ contract: z.ZodString; label: z.ZodOptional<z.ZodString>; optional: z.ZodOptional<z.ZodBoolean>; }, "strict", z.ZodTypeAny, { label?: string; contract?: string; optional?: boolean; }, { label?: string; contract?: string; optional?: boolean; }>>>; provides: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodObject<{ tools: z.ZodOptional<z.ZodArray<z.ZodString, "many">>; events: z.ZodOptional<z.ZodArray<z.ZodString, "many">>; models: z.ZodOptional<z.ZodArray<z.ZodString, "many">>; }, "strict", z.ZodTypeAny, { models?: string[]; tools?: string[]; events?: string[]; }, { models?: string[]; tools?: string[]; events?: string[]; }>>>; channel: z.ZodOptional<z.ZodObject<{ topics: z.ZodRecord<z.ZodString, z.ZodObject<{ audience: z.ZodOptional<z.ZodUnion<[z.ZodLiteral<"all">, z.ZodLiteral<"viewer">, z.ZodString]>>; mayAddress: z.ZodOptional<z.ZodArray<z.ZodString, "many">>; description: z.ZodOptional<z.ZodString>; }, "strict", z.ZodTypeAny, { description?: string; audience?: string; mayAddress?: string[]; }, { description?: string; audience?: string; mayAddress?: string[]; }>>; }, "strict", z.ZodTypeAny, { topics?: Record<string, { description?: string; audience?: string; mayAddress?: string[]; }>; }, { topics?: Record<string, { description?: string; audience?: string; mayAddress?: string[]; }>; }>>; db: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodObject<{ scope: z.ZodOptional<z.ZodEnum<["instance", "workspace", "user"]>>; owner: z.ZodOptional<z.ZodLiteral<"creator">>; fields: z.ZodRecord<z.ZodString, z.ZodString>; sealed: z.ZodOptional<z.ZodArray<z.ZodString, "many">>; unique: z.ZodOptional<z.ZodArray<z.ZodArray<z.ZodString, "many">, "many">>; indexes: z.ZodOptional<z.ZodArray<z.ZodUnion<[z.ZodArray<z.ZodString, "many">, z.ZodObject<{ fields: z.ZodArray<z.ZodString, "many">; kind: z.ZodOptional<z.ZodEnum<["btree", "contains", "text"]>>; }, "strict", z.ZodTypeAny, { kind?: "btree" | "contains" | "text"; fields?: string[]; }, { kind?: "btree" | "contains" | "text"; fields?: string[]; }>]>, "many">>; rules: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>; }, "strict", z.ZodTypeAny, { owner?: "creator"; fields?: Record<string, string>; scope?: "workspace" | "instance" | "user"; sealed?: string[]; unique?: string[][]; indexes?: (string[] | { kind?: "btree" | "contains" | "text"; fields?: string[]; })[]; rules?: Record<string, unknown>; }, { owner?: "creator"; fields?: Record<string, string>; scope?: "workspace" | "instance" | "user"; sealed?: string[]; unique?: string[][]; indexes?: (string[] | { kind?: "btree" | "contains" | "text"; fields?: string[]; })[]; rules?: Record<string, unknown>; }>>>; }, "strict", z.ZodTypeAny, { roles?: { default?: { owner?: string; "member-edit"?: string; "member-readonly"?: string; visitor?: string; anonymous?: string; agent?: string; }; custom?: { ops?: string[]; attributes?: string[]; models?: Record<string, { where?: string[]; hide?: string[]; update?: { fields?: string[]; transitions?: string; }; }>; }; vocabulary?: string[]; describe?: Record<string, string>; attributes?: Record<string, "string" | "boolean" | "int">; }; description?: string; manifestVersion?: 1; id?: string; name?: string; version?: string; applicationType?: string; entry?: string; icon?: string; author?: { name?: string; email?: string; url?: string; }; kickableTasks?: string[] | Record<string, { access?: "write" | "read" | "public" | "token" | string[]; requires?: "account"; }>; webhooks?: string[]; ops?: string[] | Record<string, { access?: "write" | "read" | "public" | "token" | string[]; requires?: "account"; }>; routes?: string[] | Record<string, { access?: "write" | "read" | "public" | "token" | string[]; requires?: "account"; }>; pollTasks?: { task?: string; everyMinutes?: number; }[]; platformApi?: { min?: string; max?: string; }; scopes?: string[]; workspaceTools?: string[]; connections?: { key?: string; kind?: "oauth2" | "apiKey"; label?: string; description?: string; authorizeUrl?: string; tokenUrl?: string; oauthScopes?: string[]; clientIdEnv?: string; clientSecretEnv?: string; headerNames?: string[]; }[]; fileSources?: { write?: string[]; workspace?: "read" | "readwrite"; providers?: string[]; }; fileProviders?: { key?: string; label?: string; connectionKey?: string; }[]; uses?: Record<string, { label?: string; contract?: string; optional?: boolean; }>; provides?: Record<string, { models?: string[]; tools?: string[]; events?: string[]; }>; channel?: { topics?: Record<string, { description?: string; audience?: string; mayAddress?: string[]; }>; }; db?: Record<string, { owner?: "creator"; fields?: Record<string, string>; scope?: "workspace" | "instance" | "user"; sealed?: string[]; unique?: string[][]; indexes?: (string[] | { kind?: "btree" | "contains" | "text"; fields?: string[]; })[]; rules?: Record<string, unknown>; }>; }, { roles?: { default?: { owner?: string; "member-edit"?: string; "member-readonly"?: string; visitor?: string; anonymous?: string; agent?: string; }; custom?: { ops?: string[]; attributes?: string[]; models?: Record<string, { where?: string[]; hide?: string[]; update?: { fields?: string[]; transitions?: string; }; }>; }; vocabulary?: string[]; describe?: Record<string, string>; attributes?: Record<string, "string" | "boolean" | "int">; }; description?: string; manifestVersion?: 1; id?: string; name?: string; version?: string; applicationType?: string; entry?: string; icon?: string; author?: { name?: string; email?: string; url?: string; }; kickableTasks?: string[] | Record<string, { access?: "write" | "read" | "public" | "token" | string[]; requires?: "account"; }>; webhooks?: string[]; ops?: string[] | Record<string, { access?: "write" | "read" | "public" | "token" | string[]; requires?: "account"; }>; routes?: string[] | Record<string, { access?: "write" | "read" | "public" | "token" | string[]; requires?: "account"; }>; pollTasks?: { task?: string; everyMinutes?: number; }[]; platformApi?: { min?: string; max?: string; }; scopes?: string[]; workspaceTools?: string[]; connections?: { key?: string; kind?: "oauth2" | "apiKey"; label?: string; description?: string; authorizeUrl?: string; tokenUrl?: string; oauthScopes?: string[]; clientIdEnv?: string; clientSecretEnv?: string; headerNames?: string[]; }[]; fileSources?: { write?: string[]; workspace?: "read" | "readwrite"; providers?: string[]; }; fileProviders?: { key?: string; label?: string; connectionKey?: string; }[]; uses?: Record<string, { label?: string; contract?: string; optional?: boolean; }>; provides?: Record<string, { models?: string[]; tools?: string[]; events?: string[]; }>; channel?: { topics?: Record<string, { description?: string; audience?: string; mayAddress?: string[]; }>; }; db?: Record<string, { owner?: "creator"; fields?: Record<string, string>; scope?: "workspace" | "instance" | "user"; sealed?: string[]; unique?: string[][]; indexes?: (string[] | { kind?: "btree" | "contains" | "text"; fields?: string[]; })[]; rules?: Record<string, unknown>; }>; }>
757
+ const PluginManifestSchema: z.ZodObject<{ manifestVersion: z.ZodLiteral<1>; id: z.ZodString; name: z.ZodString; version: z.ZodString; description: z.ZodString; applicationType: z.ZodString; entry: z.ZodString; icon: z.ZodOptional<z.ZodString>; author: z.ZodOptional<z.ZodObject<{ name: z.ZodString; email: z.ZodOptional<z.ZodString>; url: z.ZodOptional<z.ZodString>; }, "strip", z.ZodTypeAny, { name?: string; email?: string; url?: string; }, { name?: string; email?: string; url?: string; }>>; kickableTasks: z.ZodDefault<z.ZodOptional<z.ZodUnion<[z.ZodArray<z.ZodString, "many">, z.ZodRecord<z.ZodString, z.ZodObject<{ access: z.ZodDefault<z.ZodOptional<z.ZodUnion<[z.ZodEnum<["write", "read", "public", "token"]>, z.ZodArray<z.ZodString, "many">]>>>; requires: z.ZodOptional<z.ZodLiteral<"account">>; }, "strict", z.ZodTypeAny, { access?: "write" | "read" | "public" | "token" | string[]; requires?: "account"; }, { access?: "write" | "read" | "public" | "token" | string[]; requires?: "account"; }>>]>>>; webhooks: z.ZodDefault<z.ZodOptional<z.ZodArray<z.ZodString, "many">>>; ops: z.ZodDefault<z.ZodOptional<z.ZodUnion<[z.ZodArray<z.ZodString, "many">, z.ZodRecord<z.ZodString, z.ZodObject<{ access: z.ZodDefault<z.ZodOptional<z.ZodUnion<[z.ZodEnum<["write", "read", "public", "token"]>, z.ZodArray<z.ZodString, "many">]>>>; requires: z.ZodOptional<z.ZodLiteral<"account">>; }, "strict", z.ZodTypeAny, { access?: "write" | "read" | "public" | "token" | string[]; requires?: "account"; }, { access?: "write" | "read" | "public" | "token" | string[]; requires?: "account"; }>>]>>>; routes: z.ZodDefault<z.ZodOptional<z.ZodUnion<[z.ZodArray<z.ZodString, "many">, z.ZodRecord<z.ZodString, z.ZodObject<{ access: z.ZodDefault<z.ZodOptional<z.ZodUnion<[z.ZodEnum<["write", "read", "public", "token"]>, z.ZodArray<z.ZodString, "many">]>>>; requires: z.ZodOptional<z.ZodLiteral<"account">>; }, "strict", z.ZodTypeAny, { access?: "write" | "read" | "public" | "token" | string[]; requires?: "account"; }, { access?: "write" | "read" | "public" | "token" | string[]; requires?: "account"; }>>]>>>; pollTasks: z.ZodDefault<z.ZodOptional<z.ZodArray<z.ZodObject<{ task: z.ZodString; everyMinutes: z.ZodNumber; }, "strip", z.ZodTypeAny, { task?: string; everyMinutes?: number; }, { task?: string; everyMinutes?: number; }>, "many">>>; platformApi: z.ZodOptional<z.ZodObject<{ min: z.ZodString; max: z.ZodOptional<z.ZodString>; }, "strip", z.ZodTypeAny, { min?: string; max?: string; }, { min?: string; max?: string; }>>; scopes: z.ZodDefault<z.ZodOptional<z.ZodArray<z.ZodString, "many">>>; roles: z.ZodOptional<z.ZodEffects<z.ZodObject<{ vocabulary: z.ZodArray<z.ZodString, "many">; default: z.ZodDefault<z.ZodOptional<z.ZodObject<{ owner: z.ZodOptional<z.ZodString>; "member-edit": z.ZodOptional<z.ZodString>; "member-readonly": z.ZodOptional<z.ZodString>; visitor: z.ZodOptional<z.ZodString>; anonymous: z.ZodOptional<z.ZodString>; agent: z.ZodOptional<z.ZodString>; }, "strict", z.ZodTypeAny, { owner?: string; "member-edit"?: string; "member-readonly"?: string; visitor?: string; anonymous?: string; agent?: string; }, { owner?: string; "member-edit"?: string; "member-readonly"?: string; visitor?: string; anonymous?: string; agent?: string; }>>>; describe: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>; attributes: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodEnum<["string", "int", "boolean"]>>>; custom: z.ZodOptional<z.ZodObject<{ attributes: z.ZodOptional<z.ZodArray<z.ZodString, "many">>; models: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodObject<{ where: z.ZodOptional<z.ZodArray<z.ZodString, "many">>; hide: z.ZodOptional<z.ZodArray<z.ZodString, "many">>; update: z.ZodOptional<z.ZodObject<{ fields: z.ZodOptional<z.ZodArray<z.ZodString, "many">>; transitions: z.ZodOptional<z.ZodString>; }, "strict", z.ZodTypeAny, { fields?: string[]; transitions?: string; }, { fields?: string[]; transitions?: string; }>>; }, "strict", z.ZodTypeAny, { where?: string[]; hide?: string[]; update?: { fields?: string[]; transitions?: string; }; }, { where?: string[]; hide?: string[]; update?: { fields?: string[]; transitions?: string; }; }>>>; ops: z.ZodOptional<z.ZodArray<z.ZodString, "many">>; }, "strict", z.ZodTypeAny, { ops?: string[]; attributes?: string[]; models?: Record<string, { where?: string[]; hide?: string[]; update?: { fields?: string[]; transitions?: string; }; }>; }, { ops?: string[]; attributes?: string[]; models?: Record<string, { where?: string[]; hide?: string[]; update?: { fields?: string[]; transitions?: string; }; }>; }>>; }, "strict", z.ZodTypeAny, { default?: { owner?: string; "member-edit"?: string; "member-readonly"?: string; visitor?: string; anonymous?: string; agent?: string; }; custom?: { ops?: string[]; attributes?: string[]; models?: Record<string, { where?: string[]; hide?: string[]; update?: { fields?: string[]; transitions?: string; }; }>; }; vocabulary?: string[]; describe?: Record<string, string>; attributes?: Record<string, "string" | "boolean" | "int">; }, { default?: { owner?: string; "member-edit"?: string; "member-readonly"?: string; visitor?: string; anonymous?: string; agent?: string; }; custom?: { ops?: string[]; attributes?: string[]; models?: Record<string, { where?: string[]; hide?: string[]; update?: { fields?: string[]; transitions?: string; }; }>; }; vocabulary?: string[]; describe?: Record<string, string>; attributes?: Record<string, "string" | "boolean" | "int">; }>, { default?: { owner?: string; "member-edit"?: string; "member-readonly"?: string; visitor?: string; anonymous?: string; agent?: string; }; custom?: { ops?: string[]; attributes?: string[]; models?: Record<string, { where?: string[]; hide?: string[]; update?: { fields?: string[]; transitions?: string; }; }>; }; vocabulary?: string[]; describe?: Record<string, string>; attributes?: Record<string, "string" | "boolean" | "int">; }, { default?: { owner?: string; "member-edit"?: string; "member-readonly"?: string; visitor?: string; anonymous?: string; agent?: string; }; custom?: { ops?: string[]; attributes?: string[]; models?: Record<string, { where?: string[]; hide?: string[]; update?: { fields?: string[]; transitions?: string; }; }>; }; vocabulary?: string[]; describe?: Record<string, string>; attributes?: Record<string, "string" | "boolean" | "int">; }>>; workspaceTools: z.ZodDefault<z.ZodOptional<z.ZodArray<z.ZodString, "many">>>; device: z.ZodOptional<z.ZodEffects<z.ZodObject<{ commands: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodObject<{ argv: z.ZodArray<z.ZodString, "many">; params: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnion<[z.ZodEnum<["int", "number", "bool", "string"]>, z.ZodObject<{ type: z.ZodEnum<["int", "number"]>; min: z.ZodOptional<z.ZodNumber>; max: z.ZodOptional<z.ZodNumber>; describe: z.ZodOptional<z.ZodString>; }, "strict", z.ZodTypeAny, { type?: "number" | "int"; min?: number; max?: number; describe?: string; }, { type?: "number" | "int"; min?: number; max?: number; describe?: string; }>, z.ZodObject<{ type: z.ZodLiteral<"string">; pattern: z.ZodOptional<z.ZodString>; maxLength: z.ZodOptional<z.ZodNumber>; allowDash: z.ZodOptional<z.ZodBoolean>; describe: z.ZodOptional<z.ZodString>; }, "strict", z.ZodTypeAny, { type?: "string"; maxLength?: number; describe?: string; pattern?: string; allowDash?: boolean; }, { type?: "string"; maxLength?: number; describe?: string; pattern?: string; allowDash?: boolean; }>, z.ZodObject<{ type: z.ZodLiteral<"enum">; values: z.ZodArray<z.ZodString, "many">; describe: z.ZodOptional<z.ZodString>; }, "strict", z.ZodTypeAny, { values?: string[]; type?: "enum"; describe?: string; }, { values?: string[]; type?: "enum"; describe?: string; }>, z.ZodObject<{ type: z.ZodLiteral<"bool">; describe: z.ZodOptional<z.ZodString>; }, "strict", z.ZodTypeAny, { type?: "bool"; describe?: string; }, { type?: "bool"; describe?: string; }>]>>>; cwd: z.ZodOptional<z.ZodString>; env: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>; config: z.ZodOptional<z.ZodArray<z.ZodString, "many">>; timeoutSeconds: z.ZodOptional<z.ZodNumber>; onRestart: z.ZodOptional<z.ZodEnum<["report", "rerun"]>>; output: z.ZodOptional<z.ZodObject<{ mode: z.ZodOptional<z.ZodEnum<["live", "batched", "final"]>>; flushMs: z.ZodOptional<z.ZodNumber>; maxChunkKb: z.ZodOptional<z.ZodNumber>; keep: z.ZodOptional<z.ZodEnum<["head", "tail", "both"]>>; maxKb: z.ZodOptional<z.ZodNumber>; }, "strict", z.ZodTypeAny, { mode?: "live" | "batched" | "final"; flushMs?: number; maxChunkKb?: number; keep?: "head" | "tail" | "both"; maxKb?: number; }, { mode?: "live" | "batched" | "final"; flushMs?: number; maxChunkKb?: number; keep?: "head" | "tail" | "both"; maxKb?: number; }>>; result: z.ZodOptional<z.ZodEnum<["text", "json"]>>; describe: z.ZodOptional<z.ZodString>; }, "strict", z.ZodTypeAny, { params?: Record<string, "string" | "number" | "int" | "bool" | { type?: "number" | "int"; min?: number; max?: number; describe?: string; } | { type?: "string"; maxLength?: number; describe?: string; pattern?: string; allowDash?: boolean; } | { values?: string[]; type?: "enum"; describe?: string; } | { type?: "bool"; describe?: string; }>; describe?: string; argv?: string[]; cwd?: string; env?: Record<string, string>; config?: string[]; timeoutSeconds?: number; onRestart?: "report" | "rerun"; output?: { mode?: "live" | "batched" | "final"; flushMs?: number; maxChunkKb?: number; keep?: "head" | "tail" | "both"; maxKb?: number; }; result?: "text" | "json"; }, { params?: Record<string, "string" | "number" | "int" | "bool" | { type?: "number" | "int"; min?: number; max?: number; describe?: string; } | { type?: "string"; maxLength?: number; describe?: string; pattern?: string; allowDash?: boolean; } | { values?: string[]; type?: "enum"; describe?: string; } | { type?: "bool"; describe?: string; }>; describe?: string; argv?: string[]; cwd?: string; env?: Record<string, string>; config?: string[]; timeoutSeconds?: number; onRestart?: "report" | "rerun"; output?: { mode?: "live" | "batched" | "final"; flushMs?: number; maxChunkKb?: number; keep?: "head" | "tail" | "both"; maxKb?: number; }; result?: "text" | "json"; }>>>; program: z.ZodOptional<z.ZodObject<{ main: z.ZodString; resident: z.ZodOptional<z.ZodBoolean>; setup: z.ZodOptional<z.ZodArray<z.ZodObject<{ id: z.ZodString; describe: z.ZodString; run: z.ZodArray<z.ZodString, "many">; inputs: z.ZodOptional<z.ZodArray<z.ZodString, "many">>; timeoutSeconds: z.ZodOptional<z.ZodNumber>; }, "strict", z.ZodTypeAny, { id?: string; describe?: string; timeoutSeconds?: number; run?: string[]; inputs?: string[]; }, { id?: string; describe?: string; timeoutSeconds?: number; run?: string[]; inputs?: string[]; }>, "many">>; selfTest: z.ZodOptional<z.ZodObject<{ run: z.ZodArray<z.ZodString, "many">; timeoutSeconds: z.ZodOptional<z.ZodNumber>; }, "strict", z.ZodTypeAny, { timeoutSeconds?: number; run?: string[]; }, { timeoutSeconds?: number; run?: string[]; }>>; }, "strict", z.ZodTypeAny, { main?: string; resident?: boolean; setup?: { id?: string; describe?: string; timeoutSeconds?: number; run?: string[]; inputs?: string[]; }[]; selfTest?: { timeoutSeconds?: number; run?: string[]; }; }, { main?: string; resident?: boolean; setup?: { id?: string; describe?: string; timeoutSeconds?: number; run?: string[]; inputs?: string[]; }[]; selfTest?: { timeoutSeconds?: number; run?: string[]; }; }>>; config: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodObject<{ secret: z.ZodOptional<z.ZodBoolean>; required: z.ZodOptional<z.ZodBoolean>; describe: z.ZodOptional<z.ZodString>; }, "strict", z.ZodTypeAny, { required?: boolean; describe?: string; secret?: boolean; }, { required?: boolean; describe?: string; secret?: boolean; }>>>; redact: z.ZodOptional<z.ZodArray<z.ZodString, "many">>; onAppDeleted: z.ZodOptional<z.ZodEnum<["kill", "finish"]>>; }, "strict", z.ZodTypeAny, { config?: Record<string, { required?: boolean; describe?: string; secret?: boolean; }>; commands?: Record<string, { params?: Record<string, "string" | "number" | "int" | "bool" | { type?: "number" | "int"; min?: number; max?: number; describe?: string; } | { type?: "string"; maxLength?: number; describe?: string; pattern?: string; allowDash?: boolean; } | { values?: string[]; type?: "enum"; describe?: string; } | { type?: "bool"; describe?: string; }>; describe?: string; argv?: string[]; cwd?: string; env?: Record<string, string>; config?: string[]; timeoutSeconds?: number; onRestart?: "report" | "rerun"; output?: { mode?: "live" | "batched" | "final"; flushMs?: number; maxChunkKb?: number; keep?: "head" | "tail" | "both"; maxKb?: number; }; result?: "text" | "json"; }>; program?: { main?: string; resident?: boolean; setup?: { id?: string; describe?: string; timeoutSeconds?: number; run?: string[]; inputs?: string[]; }[]; selfTest?: { timeoutSeconds?: number; run?: string[]; }; }; redact?: string[]; onAppDeleted?: "kill" | "finish"; }, { config?: Record<string, { required?: boolean; describe?: string; secret?: boolean; }>; commands?: Record<string, { params?: Record<string, "string" | "number" | "int" | "bool" | { type?: "number" | "int"; min?: number; max?: number; describe?: string; } | { type?: "string"; maxLength?: number; describe?: string; pattern?: string; allowDash?: boolean; } | { values?: string[]; type?: "enum"; describe?: string; } | { type?: "bool"; describe?: string; }>; describe?: string; argv?: string[]; cwd?: string; env?: Record<string, string>; config?: string[]; timeoutSeconds?: number; onRestart?: "report" | "rerun"; output?: { mode?: "live" | "batched" | "final"; flushMs?: number; maxChunkKb?: number; keep?: "head" | "tail" | "both"; maxKb?: number; }; result?: "text" | "json"; }>; program?: { main?: string; resident?: boolean; setup?: { id?: string; describe?: string; timeoutSeconds?: number; run?: string[]; inputs?: string[]; }[]; selfTest?: { timeoutSeconds?: number; run?: string[]; }; }; redact?: string[]; onAppDeleted?: "kill" | "finish"; }>, { config?: Record<string, { required?: boolean; describe?: string; secret?: boolean; }>; commands?: Record<string, { params?: Record<string, "string" | "number" | "int" | "bool" | { type?: "number" | "int"; min?: number; max?: number; describe?: string; } | { type?: "string"; maxLength?: number; describe?: string; pattern?: string; allowDash?: boolean; } | { values?: string[]; type?: "enum"; describe?: string; } | { type?: "bool"; describe?: string; }>; describe?: string; argv?: string[]; cwd?: string; env?: Record<string, string>; config?: string[]; timeoutSeconds?: number; onRestart?: "report" | "rerun"; output?: { mode?: "live" | "batched" | "final"; flushMs?: number; maxChunkKb?: number; keep?: "head" | "tail" | "both"; maxKb?: number; }; result?: "text" | "json"; }>; program?: { main?: string; resident?: boolean; setup?: { id?: string; describe?: string; timeoutSeconds?: number; run?: string[]; inputs?: string[]; }[]; selfTest?: { timeoutSeconds?: number; run?: string[]; }; }; redact?: string[]; onAppDeleted?: "kill" | "finish"; }, { config?: Record<string, { required?: boolean; describe?: string; secret?: boolean; }>; commands?: Record<string, { params?: Record<string, "string" | "number" | "int" | "bool" | { type?: "number" | "int"; min?: number; max?: number; describe?: string; } | { type?: "string"; maxLength?: number; describe?: string; pattern?: string; allowDash?: boolean; } | { values?: string[]; type?: "enum"; describe?: string; } | { type?: "bool"; describe?: string; }>; describe?: string; argv?: string[]; cwd?: string; env?: Record<string, string>; config?: string[]; timeoutSeconds?: number; onRestart?: "report" | "rerun"; output?: { mode?: "live" | "batched" | "final"; flushMs?: number; maxChunkKb?: number; keep?: "head" | "tail" | "both"; maxKb?: number; }; result?: "text" | "json"; }>; program?: { main?: string; resident?: boolean; setup?: { id?: string; describe?: string; timeoutSeconds?: number; run?: string[]; inputs?: string[]; }[]; selfTest?: { timeoutSeconds?: number; run?: string[]; }; }; redact?: string[]; onAppDeleted?: "kill" | "finish"; }>>; connections: z.ZodDefault<z.ZodOptional<z.ZodArray<z.ZodEffects<z.ZodObject<{ key: z.ZodString; kind: z.ZodEnum<["oauth2", "apiKey"]>; label: z.ZodString; description: z.ZodOptional<z.ZodString>; authorizeUrl: z.ZodOptional<z.ZodString>; tokenUrl: z.ZodOptional<z.ZodString>; oauthScopes: z.ZodOptional<z.ZodArray<z.ZodString, "many">>; clientIdEnv: z.ZodOptional<z.ZodString>; clientSecretEnv: z.ZodOptional<z.ZodString>; headerNames: z.ZodOptional<z.ZodArray<z.ZodString, "many">>; }, "strict", z.ZodTypeAny, { key?: string; kind?: "oauth2" | "apiKey"; label?: string; description?: string; authorizeUrl?: string; tokenUrl?: string; oauthScopes?: string[]; clientIdEnv?: string; clientSecretEnv?: string; headerNames?: string[]; }, { key?: string; kind?: "oauth2" | "apiKey"; label?: string; description?: string; authorizeUrl?: string; tokenUrl?: string; oauthScopes?: string[]; clientIdEnv?: string; clientSecretEnv?: string; headerNames?: string[]; }>, { key?: string; kind?: "oauth2" | "apiKey"; label?: string; description?: string; authorizeUrl?: string; tokenUrl?: string; oauthScopes?: string[]; clientIdEnv?: string; clientSecretEnv?: string; headerNames?: string[]; }, { key?: string; kind?: "oauth2" | "apiKey"; label?: string; description?: string; authorizeUrl?: string; tokenUrl?: string; oauthScopes?: string[]; clientIdEnv?: string; clientSecretEnv?: string; headerNames?: string[]; }>, "many">>>; fileSources: z.ZodOptional<z.ZodEffects<z.ZodObject<{ workspace: z.ZodOptional<z.ZodEnum<["read", "readwrite"]>>; providers: z.ZodDefault<z.ZodOptional<z.ZodArray<z.ZodString, "many">>>; write: z.ZodOptional<z.ZodArray<z.ZodString, "many">>; }, "strict", z.ZodTypeAny, { write?: string[]; workspace?: "read" | "readwrite"; providers?: string[]; }, { write?: string[]; workspace?: "read" | "readwrite"; providers?: string[]; }>, { write?: string[]; workspace?: "read" | "readwrite"; providers?: string[]; }, { write?: string[]; workspace?: "read" | "readwrite"; providers?: string[]; }>>; fileProviders: z.ZodDefault<z.ZodOptional<z.ZodArray<z.ZodObject<{ key: z.ZodString; connectionKey: z.ZodString; label: z.ZodString; }, "strict", z.ZodTypeAny, { key?: string; label?: string; connectionKey?: string; }, { key?: string; label?: string; connectionKey?: string; }>, "many">>>; uses: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodObject<{ contract: z.ZodString; label: z.ZodOptional<z.ZodString>; optional: z.ZodOptional<z.ZodBoolean>; }, "strict", z.ZodTypeAny, { label?: string; contract?: string; optional?: boolean; }, { label?: string; contract?: string; optional?: boolean; }>>>; provides: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodObject<{ tools: z.ZodOptional<z.ZodArray<z.ZodString, "many">>; events: z.ZodOptional<z.ZodArray<z.ZodString, "many">>; models: z.ZodOptional<z.ZodArray<z.ZodString, "many">>; }, "strict", z.ZodTypeAny, { models?: string[]; tools?: string[]; events?: string[]; }, { models?: string[]; tools?: string[]; events?: string[]; }>>>; channel: z.ZodOptional<z.ZodObject<{ topics: z.ZodRecord<z.ZodString, z.ZodObject<{ audience: z.ZodOptional<z.ZodUnion<[z.ZodLiteral<"all">, z.ZodLiteral<"viewer">, z.ZodString]>>; mayAddress: z.ZodOptional<z.ZodArray<z.ZodString, "many">>; description: z.ZodOptional<z.ZodString>; }, "strict", z.ZodTypeAny, { description?: string; audience?: string; mayAddress?: string[]; }, { description?: string; audience?: string; mayAddress?: string[]; }>>; }, "strict", z.ZodTypeAny, { topics?: Record<string, { description?: string; audience?: string; mayAddress?: string[]; }>; }, { topics?: Record<string, { description?: string; audience?: string; mayAddress?: string[]; }>; }>>; db: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodObject<{ scope: z.ZodOptional<z.ZodEnum<["instance", "workspace", "user"]>>; owner: z.ZodOptional<z.ZodLiteral<"creator">>; fields: z.ZodRecord<z.ZodString, z.ZodString>; sealed: z.ZodOptional<z.ZodArray<z.ZodString, "many">>; unique: z.ZodOptional<z.ZodArray<z.ZodArray<z.ZodString, "many">, "many">>; indexes: z.ZodOptional<z.ZodArray<z.ZodUnion<[z.ZodArray<z.ZodString, "many">, z.ZodObject<{ fields: z.ZodArray<z.ZodString, "many">; kind: z.ZodOptional<z.ZodEnum<["btree", "contains", "text"]>>; }, "strict", z.ZodTypeAny, { kind?: "text" | "btree" | "contains"; fields?: string[]; }, { kind?: "text" | "btree" | "contains"; fields?: string[]; }>]>, "many">>; rules: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>; }, "strict", z.ZodTypeAny, { owner?: "creator"; fields?: Record<string, string>; scope?: "workspace" | "instance" | "user"; sealed?: string[]; unique?: string[][]; indexes?: (string[] | { kind?: "text" | "btree" | "contains"; fields?: string[]; })[]; rules?: Record<string, unknown>; }, { owner?: "creator"; fields?: Record<string, string>; scope?: "workspace" | "instance" | "user"; sealed?: string[]; unique?: string[][]; indexes?: (string[] | { kind?: "text" | "btree" | "contains"; fields?: string[]; })[]; rules?: Record<string, unknown>; }>>>; }, "strict", z.ZodTypeAny, { roles?: { default?: { owner?: string; "member-edit"?: string; "member-readonly"?: string; visitor?: string; anonymous?: string; agent?: string; }; custom?: { ops?: string[]; attributes?: string[]; models?: Record<string, { where?: string[]; hide?: string[]; update?: { fields?: string[]; transitions?: string; }; }>; }; vocabulary?: string[]; describe?: Record<string, string>; attributes?: Record<string, "string" | "boolean" | "int">; }; description?: string; manifestVersion?: 1; id?: string; name?: string; version?: string; applicationType?: string; entry?: string; icon?: string; author?: { name?: string; email?: string; url?: string; }; kickableTasks?: string[] | Record<string, { access?: "write" | "read" | "public" | "token" | string[]; requires?: "account"; }>; webhooks?: string[]; ops?: string[] | Record<string, { access?: "write" | "read" | "public" | "token" | string[]; requires?: "account"; }>; routes?: string[] | Record<string, { access?: "write" | "read" | "public" | "token" | string[]; requires?: "account"; }>; pollTasks?: { task?: string; everyMinutes?: number; }[]; platformApi?: { min?: string; max?: string; }; scopes?: string[]; workspaceTools?: string[]; device?: { config?: Record<string, { required?: boolean; describe?: string; secret?: boolean; }>; commands?: Record<string, { params?: Record<string, "string" | "number" | "int" | "bool" | { type?: "number" | "int"; min?: number; max?: number; describe?: string; } | { type?: "string"; maxLength?: number; describe?: string; pattern?: string; allowDash?: boolean; } | { values?: string[]; type?: "enum"; describe?: string; } | { type?: "bool"; describe?: string; }>; describe?: string; argv?: string[]; cwd?: string; env?: Record<string, string>; config?: string[]; timeoutSeconds?: number; onRestart?: "report" | "rerun"; output?: { mode?: "live" | "batched" | "final"; flushMs?: number; maxChunkKb?: number; keep?: "head" | "tail" | "both"; maxKb?: number; }; result?: "text" | "json"; }>; program?: { main?: string; resident?: boolean; setup?: { id?: string; describe?: string; timeoutSeconds?: number; run?: string[]; inputs?: string[]; }[]; selfTest?: { timeoutSeconds?: number; run?: string[]; }; }; redact?: string[]; onAppDeleted?: "kill" | "finish"; }; connections?: { key?: string; kind?: "oauth2" | "apiKey"; label?: string; description?: string; authorizeUrl?: string; tokenUrl?: string; oauthScopes?: string[]; clientIdEnv?: string; clientSecretEnv?: string; headerNames?: string[]; }[]; fileSources?: { write?: string[]; workspace?: "read" | "readwrite"; providers?: string[]; }; fileProviders?: { key?: string; label?: string; connectionKey?: string; }[]; uses?: Record<string, { label?: string; contract?: string; optional?: boolean; }>; provides?: Record<string, { models?: string[]; tools?: string[]; events?: string[]; }>; channel?: { topics?: Record<string, { description?: string; audience?: string; mayAddress?: string[]; }>; }; db?: Record<string, { owner?: "creator"; fields?: Record<string, string>; scope?: "workspace" | "instance" | "user"; sealed?: string[]; unique?: string[][]; indexes?: (string[] | { kind?: "text" | "btree" | "contains"; fields?: string[]; })[]; rules?: Record<string, unknown>; }>; }, { roles?: { default?: { owner?: string; "member-edit"?: string; "member-readonly"?: string; visitor?: string; anonymous?: string; agent?: string; }; custom?: { ops?: string[]; attributes?: string[]; models?: Record<string, { where?: string[]; hide?: string[]; update?: { fields?: string[]; transitions?: string; }; }>; }; vocabulary?: string[]; describe?: Record<string, string>; attributes?: Record<string, "string" | "boolean" | "int">; }; description?: string; manifestVersion?: 1; id?: string; name?: string; version?: string; applicationType?: string; entry?: string; icon?: string; author?: { name?: string; email?: string; url?: string; }; kickableTasks?: string[] | Record<string, { access?: "write" | "read" | "public" | "token" | string[]; requires?: "account"; }>; webhooks?: string[]; ops?: string[] | Record<string, { access?: "write" | "read" | "public" | "token" | string[]; requires?: "account"; }>; routes?: string[] | Record<string, { access?: "write" | "read" | "public" | "token" | string[]; requires?: "account"; }>; pollTasks?: { task?: string; everyMinutes?: number; }[]; platformApi?: { min?: string; max?: string; }; scopes?: string[]; workspaceTools?: string[]; device?: { config?: Record<string, { required?: boolean; describe?: string; secret?: boolean; }>; commands?: Record<string, { params?: Record<string, "string" | "number" | "int" | "bool" | { type?: "number" | "int"; min?: number; max?: number; describe?: string; } | { type?: "string"; maxLength?: number; describe?: string; pattern?: string; allowDash?: boolean; } | { values?: string[]; type?: "enum"; describe?: string; } | { type?: "bool"; describe?: string; }>; describe?: string; argv?: string[]; cwd?: string; env?: Record<string, string>; config?: string[]; timeoutSeconds?: number; onRestart?: "report" | "rerun"; output?: { mode?: "live" | "batched" | "final"; flushMs?: number; maxChunkKb?: number; keep?: "head" | "tail" | "both"; maxKb?: number; }; result?: "text" | "json"; }>; program?: { main?: string; resident?: boolean; setup?: { id?: string; describe?: string; timeoutSeconds?: number; run?: string[]; inputs?: string[]; }[]; selfTest?: { timeoutSeconds?: number; run?: string[]; }; }; redact?: string[]; onAppDeleted?: "kill" | "finish"; }; connections?: { key?: string; kind?: "oauth2" | "apiKey"; label?: string; description?: string; authorizeUrl?: string; tokenUrl?: string; oauthScopes?: string[]; clientIdEnv?: string; clientSecretEnv?: string; headerNames?: string[]; }[]; fileSources?: { write?: string[]; workspace?: "read" | "readwrite"; providers?: string[]; }; fileProviders?: { key?: string; label?: string; connectionKey?: string; }[]; uses?: Record<string, { label?: string; contract?: string; optional?: boolean; }>; provides?: Record<string, { models?: string[]; tools?: string[]; events?: string[]; }>; channel?: { topics?: Record<string, { description?: string; audience?: string; mayAddress?: string[]; }>; }; db?: Record<string, { owner?: "creator"; fields?: Record<string, string>; scope?: "workspace" | "instance" | "user"; sealed?: string[]; unique?: string[][]; indexes?: (string[] | { kind?: "text" | "btree" | "contains"; fields?: string[]; })[]; rules?: Record<string, unknown>; }>; }>
718
758
  ```
719
759
 
720
760
  #### `PluginRolesSchema` — const · src/manifest.ts
@@ -765,6 +805,14 @@ The reducer behind `plugin/binding_set`. Deterministic: setting a slot replaces
765
805
  function reduceBinding( current: BindingMap, event: { slot?: unknown; nodeId?: unknown; applicationType?: unknown; via?: unknown; at?: unknown }, knownSlots: readonly string[], ): BindingMap
766
806
  ```
767
807
 
808
+ #### `renderCommand` — function · src/machine/commands.ts
809
+
810
+ Render one declared command. Throws `DeviceCommandError` with a sentence for anything undeclared or ill-typed.
811
+
812
+ ```ts
813
+ function renderCommand(block: DeviceBlock, name: string, params: Record<string, unknown> = {}): RenderedCommand
814
+ ```
815
+
768
816
  #### `resolveAppRole` — function · src/roles.ts
769
817
 
770
818
  The app's word for this caller.
@@ -885,7 +933,7 @@ A manifest with one entry removed (the bytes stay; they may be shared). Pure.
885
933
  function withoutAsset(assets: AssetsManifest | null | undefined, pluginId: string, name: string): AssetsManifest
886
934
  ```
887
935
 
888
- ### Types (76)
936
+ ### Types (81)
889
937
 
890
938
  #### `AgentRunToolContext` — interface · src/types.ts
891
939
 
@@ -1191,6 +1239,23 @@ interface CollapsibleConfig {
1191
1239
  }
1192
1240
  ```
1193
1241
 
1242
+ #### `CommandSpec` — interface · src/machine/commands.ts
1243
+
1244
+ ```ts
1245
+ interface CommandSpec {
1246
+ argv: string[];
1247
+ params?: Record<string, ParamSpec>;
1248
+ cwd?: string;
1249
+ env?: Record<string, string>;
1250
+ config?: string[];
1251
+ timeoutSeconds?: number;
1252
+ onRestart?: "report" | "rerun";
1253
+ output?: OutputSpec;
1254
+ result?: "text" | "json";
1255
+ describe?: string;
1256
+ }
1257
+ ```
1258
+
1194
1259
  #### `CompiledCustomRole` — interface · src/db/custom-roles.ts
1195
1260
 
1196
1261
  ```ts
@@ -1211,6 +1276,16 @@ interface CompiledCustomRole {
1211
1276
  }
1212
1277
  ```
1213
1278
 
1279
+ #### `ConfigKeySpec` — interface · src/machine/commands.ts
1280
+
1281
+ ```ts
1282
+ interface ConfigKeySpec {
1283
+ secret?: boolean;
1284
+ required?: boolean;
1285
+ describe?: string;
1286
+ }
1287
+ ```
1288
+
1214
1289
  #### `ContractDef` — interface · src/bindings.ts
1215
1290
 
1216
1291
  What a contract REQUIRES of whoever claims it. The platform ships these; an app may publish its own.
@@ -1321,6 +1396,18 @@ interface DerivedTool extends ApplicationTools {
1321
1396
  }
1322
1397
  ```
1323
1398
 
1399
+ #### `DeviceBlock` — interface · src/machine/commands.ts
1400
+
1401
+ ```ts
1402
+ interface DeviceBlock {
1403
+ commands: Record<string, CommandSpec>;
1404
+ program?: ProgramSpec;
1405
+ config?: Record<string, ConfigKeySpec>;
1406
+ redact?: string[];
1407
+ onAppDeleted?: "kill" | "finish";
1408
+ }
1409
+ ```
1410
+
1324
1411
  #### `EventData` — interface · src/types.ts
1325
1412
 
1326
1413
  ```ts
@@ -1635,6 +1722,22 @@ interface OpToolConfig<S extends OpInput, R> {
1635
1722
  }
1636
1723
  ```
1637
1724
 
1725
+ #### `ParamSpec` — type · src/machine/commands.ts
1726
+
1727
+ DECLARED COMMANDS — the wall between an app and the person's computer.
1728
+
1729
+ ```ts
1730
+ type ParamSpec =
1731
+ | "int"
1732
+ | "number"
1733
+ | "bool"
1734
+ | "string"
1735
+ | { type: "int" | "number"; min?: number; max?: number; describe?: string }
1736
+ | { type: "string"; pattern?: string; maxLength?: number; allowDash?: boolean; describe?: string }
1737
+ | { type: "enum"; values: string[]; describe?: string }
1738
+ | { type: "bool"; describe?: string };
1739
+ ```
1740
+
1638
1741
  #### `ParsedSourceId` — type · src/files.ts
1639
1742
 
1640
1743
  ```ts
@@ -1733,6 +1836,20 @@ type ReconcileDecision =
1733
1836
  | { kind: "adopt" };
1734
1837
  ```
1735
1838
 
1839
+ #### `RenderedCommand` — interface · src/machine/commands.ts
1840
+
1841
+ ```ts
1842
+ interface RenderedCommand {
1843
+ name: string;
1844
+ argv: string[];
1845
+ cwd?: string;
1846
+ env: Record<string, string>;
1847
+ config: string[];
1848
+ timeoutSeconds: number;
1849
+ onRestart: "report" | "rerun";
1850
+ }
1851
+ ```
1852
+
1736
1853
  #### `RequiredStateShape` — interface · src/helpers.ts
1737
1854
 
1738
1855
  Fields whose ABSENCE means "did not load" (they are seeded at genesis).
@@ -1810,11 +1927,11 @@ interface UsesDecl {
1810
1927
  ```
1811
1928
 
1812
1929
  ==============================================================================
1813
- ## `esoul-sdk/server` — 67 exports
1930
+ ## `esoul-sdk/server` — 79 exports
1814
1931
 
1815
1932
  Server code only (server.ts, ops, routes, tasks): the viewer, the app's database, files, connections, machines, charts, route tokens.
1816
1933
 
1817
- ### Functions and values (27)
1934
+ ### Functions and values (29)
1818
1935
 
1819
1936
  #### `APPROVAL_WAIT` — const · src/computer.ts
1820
1937
 
@@ -1848,6 +1965,22 @@ COMPOSE A ROLE, as the owner: a name of its own, a base word from the vocabulary
1848
1965
  function defineAppRole( _ctx: { pluginId: string; workspaceId: string; nodeId: string; viewer: PluginViewer }, _definition: CustomRoleDefinitionRaw, ): Promise<{ ok: boolean; name?: string; error?: string }>
1849
1966
  ```
1850
1967
 
1968
+ #### `DEVICE_RUN_TERMINAL` — const · src/device.ts
1969
+
1970
+ The statuses a run ends in: done, expired, cancelled, lost.
1971
+
1972
+ ```ts
1973
+ const DEVICE_RUN_TERMINAL: readonly DeviceRunStatus[]
1974
+ ```
1975
+
1976
+ #### `devices` — function · src/server.ts
1977
+
1978
+ PARTS OF THE APP THAT RUN ON THE PERSON'S COMPUTER (plugin-device-arm.md). The app declares its commands in plugin.json `device.commands`; the owner connects a computer with <ConnectComputer/> (esoul-sdk/react) and approves that list; then server code runs a declared command by name:
1979
+
1980
+ ```ts
1981
+ function devices(_ctx: { pluginId: string; nodeId: string; viewer?: { canWrite: boolean; kind: string; userId: string | null } }): Devices
1982
+ ```
1983
+
1851
1984
  #### `emitPluginAppEvent` — function · src/server.ts
1852
1985
 
1853
1986
  Cross-app events: dispatch the TARGET app's own events through the platform spine (target's dataCreator mints; triggers fire; every event is actor-stamped `{kind:"plugin", pluginId, sourceNodeId}`). Same-workspace only. HOST-ONLY.
@@ -2032,7 +2165,7 @@ WHO IS THIS, IN WORDS. The caller's own name and email, for an app with a reason
2032
2165
  function viewerProfile(_viewer: PluginViewer): Promise<ViewerProfile | null>
2033
2166
  ```
2034
2167
 
2035
- ### Types (40)
2168
+ ### Types (50)
2036
2169
 
2037
2170
  #### `AppRolePerson` — interface · src/server.ts
2038
2171
 
@@ -2263,6 +2396,98 @@ interface CustomRolesEnvelope {
2263
2396
  }
2264
2397
  ```
2265
2398
 
2399
+ #### `DeviceRunOptions` — interface · src/device.ts
2400
+
2401
+ ```ts
2402
+ interface DeviceRunOptions {
2403
+ linkId?: string;
2404
+ expiresInSeconds?: number;
2405
+ }
2406
+ ```
2407
+
2408
+ #### `DeviceRunStarted` — interface · src/device.ts
2409
+
2410
+ ```ts
2411
+ interface DeviceRunStarted {
2412
+ commandId: string;
2413
+ status: DeviceRunStatus;
2414
+ machineOnline: boolean;
2415
+ expiresAt: number;
2416
+ }
2417
+ ```
2418
+
2419
+ #### `DeviceRunStatus` — type · src/device.ts
2420
+
2421
+ ```ts
2422
+ type DeviceRunStatus = "queued" | "claimed" | "running" | "done" | "expired" | "cancelled" | "lost";
2423
+ ```
2424
+
2425
+ #### `DeviceRunView` — interface · src/device.ts
2426
+
2427
+ ```ts
2428
+ interface DeviceRunView {
2429
+ commandId: string;
2430
+ linkId: string;
2431
+ machineId: string;
2432
+ capability?: string;
2433
+ agent?: string | null;
2434
+ name: string;
2435
+ params: Record<string, unknown>;
2436
+ argv: string[];
2437
+ status: DeviceRunStatus;
2438
+ exitCode: number | null;
2439
+ signal: string | null;
2440
+ reason: string | null;
2441
+ output?: string;
2442
+ tail?: string;
2443
+ result?: unknown;
2444
+ resultError?: string | null;
2445
+ outputMode?: "live" | "batched" | "final";
2446
+ outputBytes: number;
2447
+ outputSeq: number;
2448
+ truncated: boolean;
2449
+ createdAt: number;
2450
+ startedAt: number | null;
2451
+ endedAt: number | null;
2452
+ expiresAt: number;
2453
+ }
2454
+ ```
2455
+
2456
+ #### `Devices` — interface · src/device.ts
2457
+
2458
+ ```ts
2459
+ interface Devices {
2460
+ list(): Promise<DeviceSummary[]>;
2461
+ run(name: string, params?: Record<string, unknown>, opts?: DeviceRunOptions): Promise<DeviceRunStarted>;
2462
+ get(commandId: string): Promise<DeviceRunView>;
2463
+ wait(commandId: string, opts?: { timeoutSeconds?: number }): Promise<DeviceRunView>;
2464
+ cancel(commandId: string): Promise<{ status: string }>;
2465
+ sh(line: string, opts: { agent: string; stdin?: string; linkId?: string; waitSeconds?: number }): Promise<ShellResult & { commandId: string; status: DeviceRunStatus }>;
2466
+ program(linkId: string): {
2467
+ send(topic: string, data?: unknown, opts?: { waitSeconds?: number }): Promise<{ commandId: string; status: DeviceRunStatus; ok: boolean; result: unknown; error: string | null }>;
2468
+ state(): Promise<ProgramState | null>;
2469
+ };
2470
+ }
2471
+ ```
2472
+
2473
+ #### `DeviceSummary` — interface · src/device.ts
2474
+
2475
+ One computer connected to this app instance.
2476
+
2477
+ ```ts
2478
+ interface DeviceSummary {
2479
+ linkId: string;
2480
+ status: "active" | "suspended";
2481
+ reason: string | null;
2482
+ commands: string[];
2483
+ pendingApproval: { added: string[]; changed: string[] };
2484
+ machine: { machineId: string; hostname: string; os: string; arch: string | null; runtimeVersion: string | null; lastSeenAt: number | null; online: boolean } | null;
2485
+ workspace?: WorkspaceGrant | null;
2486
+ program?: boolean;
2487
+ programState?: ProgramState | null;
2488
+ }
2489
+ ```
2490
+
2266
2491
  #### `EmitPluginAppEventArgs` — interface · src/server.ts
2267
2492
 
2268
2493
  ```ts
@@ -2500,7 +2725,8 @@ interface PluginViewer {
2500
2725
  attrs?: Record<string, AttrValue>;
2501
2726
  canWrite: boolean;
2502
2727
  shareId: string | null;
2503
- agent?: { runId?: string; onBehalfOf: Omit<PluginViewer, "agent"> };
2728
+ agent?: { runId?: string; onBehalfOf: Omit<PluginViewer, "agent"> };
2729
+ device?: { machineId: string; linkId: string; hostname: string };
2504
2730
  }
2505
2731
  ```
2506
2732
 
@@ -2541,6 +2767,38 @@ type PluginWebhookHandler = (
2541
2767
  ) => Promise<Response>;
2542
2768
  ```
2543
2769
 
2770
+ #### `ProgramState` — interface · src/machine/program-api.ts
2771
+
2772
+ Where an app's program stands on one computer — what the runtime reports and the app's Computers card renders. `phase` moves forward installing → setup → self-test → running (or ready, for a program started by its first message); `failed` and `refused` say why in `error`.
2773
+
2774
+ ```ts
2775
+ interface ProgramState {
2776
+ digest: string;
2777
+ phase: "installing" | "setup" | "self-test" | "ready" | "starting" | "running" | "stopped" | "failed" | "refused";
2778
+ steps: ProgramStepState[];
2779
+ status?: { text: string; progress?: number; ready?: boolean; at: number } | null;
2780
+ error?: string | null;
2781
+ logTail?: string[];
2782
+ restarts: number;
2783
+ updatedAt: number;
2784
+ }
2785
+ ```
2786
+
2787
+ #### `ProgramStepState` — interface · src/machine/program-api.ts
2788
+
2789
+ One setup step, as the Computers card shows it.
2790
+
2791
+ ```ts
2792
+ interface ProgramStepState {
2793
+ id: string;
2794
+ describe: string;
2795
+ state: "pending" | "running" | "done" | "cached" | "failed";
2796
+ startedAt?: number;
2797
+ endedAt?: number;
2798
+ detail?: string;
2799
+ }
2800
+ ```
2801
+
2544
2802
  #### `RouteTokenGrant` — interface · src/server.ts
2545
2803
 
2546
2804
  ```ts
@@ -2561,6 +2819,22 @@ interface RunOptions {
2561
2819
  }
2562
2820
  ```
2563
2821
 
2822
+ #### `ShellResult` — interface · src/device.ts
2823
+
2824
+ One line of the shared command line, answered (esoul-sdk/machine shell.ts): `text` is what a terminal would print (what an agent reads), `data` the same answer structured, `opId` the journal entry to `undo`.
2825
+
2826
+ ```ts
2827
+ interface ShellResult {
2828
+ ok: boolean;
2829
+ text: string;
2830
+ data?: unknown;
2831
+ exitCode?: number | null;
2832
+ error?: string;
2833
+ opId?: string;
2834
+ cwd: string;
2835
+ }
2836
+ ```
2837
+
2564
2838
  #### `ToEndOptions` — interface · src/computer.ts
2565
2839
 
2566
2840
  ```ts
@@ -2582,12 +2856,28 @@ interface ViewerProfile {
2582
2856
  }
2583
2857
  ```
2584
2858
 
2859
+ #### `WorkspaceGrant` — type · src/device.ts
2860
+
2861
+ What an app's agents may reach on a computer through the shared command line.
2862
+
2863
+ ```ts
2864
+ type WorkspaceGrant = { scope: "folder"; root: string } | { scope: "computer" };
2865
+ ```
2866
+
2585
2867
  ==============================================================================
2586
- ## `esoul-sdk/react` — 52 exports
2868
+ ## `esoul-sdk/react` — 64 exports
2587
2869
 
2588
2870
  The app's UI: hooks for the viewer, the app's state, realtime, workspace files and tools.
2589
2871
 
2590
- ### Functions and values (27)
2872
+ ### Functions and values (33)
2873
+
2874
+ #### `ConnectComputer` — function · src/react.ts
2875
+
2876
+ The connect panel for an app's config page: the computers already connected (online, last seen, Disconnect, Approve new commands) and "Connect a computer" — one command to paste in a terminal, the code the computer shows, Approve. Linux and macOS, Node 22+.
2877
+
2878
+ ```ts
2879
+ function ConnectComputer(_props: { workspaceId: string; nodeId: string; title?: string; className?: string }): any
2880
+ ```
2591
2881
 
2592
2882
  #### `describeFailure` — function · src/failed-requests.ts
2593
2883
 
@@ -2693,6 +2983,30 @@ True when the current viewer may mutate this workspace.
2693
2983
  function useAppCanEdit(): boolean
2694
2984
  ```
2695
2985
 
2986
+ #### `useDeviceActions` — function · src/react.ts
2987
+
2988
+ The owner's buttons from the UI: run a declared command, stop a run, disconnect a computer, approve an update's new commands. Needs edit access.
2989
+
2990
+ ```ts
2991
+ function useDeviceActions(_where: { nodeId: string }): { run(name: string, params?: Record<string, unknown>, opts?: { linkId?: string; expiresInSeconds?: number }): Promise<DeviceRunStarted>; cancel(commandId: string): Promise<{ status: string }>; disconnect(linkId: string): Promise<{ ok: boolean }>; approveUpdate(linkId: string): Promise<unknown>; recent(limit?: number): Promise<{ commands: DeviceRunView[] }>; /** What the app's agents may reach on that computer: none, its folder, or the whole computer. */ setWorkspace(linkId: string, workspace: "none" | "folder" | "computer"): Promise<unknown>; /** One line of the shared command line as a named agent; `result` is the answer once it ends. */ sh(line: string, opts: { agent: string; stdin?: string; linkId?: string; waitSeconds?: number }): Promise<DeviceRunView & { result?: import("./device.js").ShellResult }>; }
2992
+ ```
2993
+
2994
+ #### `useDeviceRun` — function · src/react.ts
2995
+
2996
+ One run, live: status as it changes, output as it streams. `finished` once it ended.
2997
+
2998
+ ```ts
2999
+ function useDeviceRun(_where: { workspaceId: string; nodeId: string; commandId: string | null | undefined }): { run: DeviceRunView | null; error: string | null; refetch: () => Promise<void>; finished: boolean }
3000
+ ```
3001
+
3002
+ #### `useDevices` — function · src/react.ts
3003
+
3004
+ The computers connected to this app instance, live (presence refreshes every 20 s).
3005
+
3006
+ ```ts
3007
+ function useDevices(_where: { workspaceId: string; nodeId: string }): { devices: DeviceSummary[] | null; error: string | null; refresh: () => Promise<void> }
3008
+ ```
3009
+
2696
3010
  #### `useFileSourceEntries` — function · src/react.ts
2697
3011
 
2698
3012
  One folder's listing; a failing source reports error/errorKind, never []. `opts` narrows AT THE SOURCE — `{ only: "folder" }`, `{ nameContains }`, `{ orderBy: "name", pageSize: 1000 }` — and `loadMore()` appends the next page.
@@ -2725,6 +3039,14 @@ A folder box with autocomplete: give it the text being typed ("sheets/quality_da
2725
3039
  function useFolderAutocomplete(_workspaceId: string | null, _sourceId: string | null, _text: string): FolderAutocompleteState
2726
3040
  ```
2727
3041
 
3042
+ #### `useMyComputers` — function · src/react.ts
3043
+
3044
+ The account's computers (computers belong to the person, not to an app; an app gets one by assignment). `assign(machineId, nodeId)` gives a computer to an app (approving its declared commands); `disconnect(machineId)` removes a computer from the account entirely.
3045
+
3046
+ ```ts
3047
+ function useMyComputers(): { computers: MyComputer[] | null; error: string | null; refresh: () => Promise<void>; assign(machineId: string, nodeId: string): Promise<{ linkId: string }>; disconnect(machineId: string): Promise<{ ok: boolean }> }
3048
+ ```
3049
+
2728
3050
  #### `usePluginCurrentChatId` — function · src/react.ts
2729
3051
 
2730
3052
  Current open chat id, for `chatIdSource` attribution.
@@ -2805,7 +3127,75 @@ Invoke tools of OTHER apps in the workspace (append a spreadsheet row, add a cal
2805
3127
  function useWorkspaceTools(_identity: { workspaceId: string; nodeId: string }): WorkspaceTools
2806
3128
  ```
2807
3129
 
2808
- ### Types (25)
3130
+ #### `YourComputers` — function · src/react.ts
3131
+
3132
+ The account's computers as a panel: online, the apps each serves, Disconnect.
3133
+
3134
+ ```ts
3135
+ function YourComputers(_props: { className?: string }): any
3136
+ ```
3137
+
3138
+ ### Types (31)
3139
+
3140
+ #### `DeviceRunStarted` — interface · src/device.ts
3141
+
3142
+ ```ts
3143
+ interface DeviceRunStarted {
3144
+ commandId: string;
3145
+ status: DeviceRunStatus;
3146
+ machineOnline: boolean;
3147
+ expiresAt: number;
3148
+ }
3149
+ ```
3150
+
3151
+ #### `DeviceRunView` — interface · src/device.ts
3152
+
3153
+ ```ts
3154
+ interface DeviceRunView {
3155
+ commandId: string;
3156
+ linkId: string;
3157
+ machineId: string;
3158
+ capability?: string;
3159
+ agent?: string | null;
3160
+ name: string;
3161
+ params: Record<string, unknown>;
3162
+ argv: string[];
3163
+ status: DeviceRunStatus;
3164
+ exitCode: number | null;
3165
+ signal: string | null;
3166
+ reason: string | null;
3167
+ output?: string;
3168
+ tail?: string;
3169
+ result?: unknown;
3170
+ resultError?: string | null;
3171
+ outputMode?: "live" | "batched" | "final";
3172
+ outputBytes: number;
3173
+ outputSeq: number;
3174
+ truncated: boolean;
3175
+ createdAt: number;
3176
+ startedAt: number | null;
3177
+ endedAt: number | null;
3178
+ expiresAt: number;
3179
+ }
3180
+ ```
3181
+
3182
+ #### `DeviceSummary` — interface · src/device.ts
3183
+
3184
+ One computer connected to this app instance.
3185
+
3186
+ ```ts
3187
+ interface DeviceSummary {
3188
+ linkId: string;
3189
+ status: "active" | "suspended";
3190
+ reason: string | null;
3191
+ commands: string[];
3192
+ pendingApproval: { added: string[]; changed: string[] };
3193
+ machine: { machineId: string; hostname: string; os: string; arch: string | null; runtimeVersion: string | null; lastSeenAt: number | null; online: boolean } | null;
3194
+ workspace?: WorkspaceGrant | null;
3195
+ program?: boolean;
3196
+ programState?: ProgramState | null;
3197
+ }
3198
+ ```
2809
3199
 
2810
3200
  #### `FailedRequest` — interface · src/failed-requests.ts
2811
3201
 
@@ -2923,6 +3313,23 @@ interface LabelClass {
2923
3313
  }
2924
3314
  ```
2925
3315
 
3316
+ #### `MyComputer` — interface · src/react.ts
3317
+
3318
+ One computer connected to the signed-in account, and the apps it is assigned to.
3319
+
3320
+ ```ts
3321
+ interface MyComputer {
3322
+ machineId: string;
3323
+ hostname: string;
3324
+ os: string;
3325
+ arch: string | null;
3326
+ runtimeVersion: string | null;
3327
+ lastSeenAt: number | null;
3328
+ online: boolean;
3329
+ apps: { linkId: string; nodeId: string; workspaceId: string; status: string; appName: string | null }[];
3330
+ }
3331
+ ```
3332
+
2926
3333
  #### `PluginRealtime` — interface · src/react.ts
2927
3334
 
2928
3335
  ```ts
@@ -3068,6 +3475,22 @@ interface ResolvedFilePath {
3068
3475
  }
3069
3476
  ```
3070
3477
 
3478
+ #### `ShellResult` — interface · src/device.ts
3479
+
3480
+ One line of the shared command line, answered (esoul-sdk/machine shell.ts): `text` is what a terminal would print (what an agent reads), `data` the same answer structured, `opId` the journal entry to `undo`.
3481
+
3482
+ ```ts
3483
+ interface ShellResult {
3484
+ ok: boolean;
3485
+ text: string;
3486
+ data?: unknown;
3487
+ exitCode?: number | null;
3488
+ error?: string;
3489
+ opId?: string;
3490
+ cwd: string;
3491
+ }
3492
+ ```
3493
+
3071
3494
  #### `SignInWall` — interface · src/react.ts
3072
3495
 
3073
3496
  ```ts
@@ -3091,6 +3514,14 @@ interface WorkspaceAppSummary {
3091
3514
  }
3092
3515
  ```
3093
3516
 
3517
+ #### `WorkspaceGrant` — type · src/device.ts
3518
+
3519
+ What an app's agents may reach on a computer through the shared command line.
3520
+
3521
+ ```ts
3522
+ type WorkspaceGrant = { scope: "folder"; root: string } | { scope: "computer" };
3523
+ ```
3524
+
3094
3525
  #### `WorkspaceNav` — interface · src/react.ts
3095
3526
 
3096
3527
  ```ts
@@ -3344,3 +3775,990 @@ interface TestManifest {
3344
3775
  ```ts
3345
3776
  type ViewerKind = RuleViewer["kind"];
3346
3777
  ```
3778
+
3779
+ ==============================================================================
3780
+ ## `esoul-sdk/machine` — 102 exports
3781
+
3782
+ The side of an app that runs on the person's computer: the signed door protocol, the declared-command rules, the runtime behind the `esoul-device` command. Node built-ins only.
3783
+
3784
+ ### Functions and values (62)
3785
+
3786
+ #### `AGENT_NAME_RE` — const · src/machine/shell.ts
3787
+
3788
+ What an agent may be called: lowercase letters, digits, - and _, up to 32, starting with a letter.
3789
+
3790
+ ```ts
3791
+ const AGENT_NAME_RE: RegExp
3792
+ ```
3793
+
3794
+ #### `announce` — function · src/machine/capabilities.ts
3795
+
3796
+ What the hello announces: ["commands@1", "workspace@1"].
3797
+
3798
+ ```ts
3799
+ function announce(reg: Map<string, Capability>): string[]
3800
+ ```
3801
+
3802
+ #### `applyEdits` — function · src/machine/files.ts
3803
+
3804
+ Find-and-replace edits, applied in order; each must match exactly once (unless `all`). All or nothing.
3805
+
3806
+ ```ts
3807
+ function applyEdits(text: string, edits: Edit[]): { text: string; applied: number }
3808
+ ```
3809
+
3810
+ #### `applyPatch` — function · src/machine/files.ts
3811
+
3812
+ Apply a unified diff with context. Each hunk's old lines (context + removed) are located exactly, preferring the line the diff names and searching outward, so a file that moved on still takes a hunk whose context is intact. A hunk whose context is gone is reported, never forced. All-or-nothing unless `partial`.
3813
+
3814
+ ```ts
3815
+ function applyPatch(text: string, diff: string, opts: { partial?: boolean } = {}): { text: string; results: HunkResult[] }
3816
+ ```
3817
+
3818
+ #### `approvedCommandNames` — function · src/machine/digest.ts
3819
+
3820
+ The command names an approval covers (without the program's entry).
3821
+
3822
+ ```ts
3823
+ function approvedCommandNames(approved: Record<string, string>): string[]
3824
+ ```
3825
+
3826
+ #### `builtInCapabilities` — function · src/machine/capabilities.ts
3827
+
3828
+ The capabilities this runtime is built with, as one registry (the supervisor dispatches through it).
3829
+
3830
+ ```ts
3831
+ function builtInCapabilities(startRun: (job: Job, redact: string[], env: Record<string, string>) => Promise<void>, programs: ProgramDelivery | null = null): Map<string, Capability>
3832
+ ```
3833
+
3834
+ #### `canonical` — function · src/machine/protocol.ts
3835
+
3836
+ The exact bytes that are signed. Path INCLUDES the query string.
3837
+
3838
+ ```ts
3839
+ function canonical(method: string, pathAndQuery: string, ts: number | string, body: string | Uint8Array): string
3840
+ ```
3841
+
3842
+ #### `COMMAND_NAME_RE` — const · src/machine/commands.ts
3843
+
3844
+ What a command name may look like: lowercase, starts with a letter, up to 40 characters.
3845
+
3846
+ ```ts
3847
+ const COMMAND_NAME_RE: RegExp
3848
+ ```
3849
+
3850
+ #### `commandDigest` — function · src/machine/digest.ts
3851
+
3852
+ Stable digest of one command's spec — what the owner approved.
3853
+
3854
+ ```ts
3855
+ function commandDigest(spec: CommandSpec): string
3856
+ ```
3857
+
3858
+ #### `commandDigests` — function · src/machine/digest.ts
3859
+
3860
+ Every declared command's digest, by name, and the program's under `@program` — what an approval records.
3861
+
3862
+ ```ts
3863
+ function commandDigests(block: DeviceBlock): Record<string, string>
3864
+ ```
3865
+
3866
+ #### `commandsCapability` — function · src/machine/capabilities.ts
3867
+
3868
+ The app's declared commands; the detached runner owns and reports each run.
3869
+
3870
+ ```ts
3871
+ function commandsCapability(deps: { startRun: (job: Job, redact: string[], env: Record<string, string>) => Promise<void> }): Capability
3872
+ ```
3873
+
3874
+ #### `confinedArgv` — function · src/machine/shell.ts
3875
+
3876
+ argv that runs `line` in `cwd` under the scope's confinement.
3877
+
3878
+ ```ts
3879
+ function confinedArgv(ws: Workspace, cwd: string, line: string): string[]
3880
+ ```
3881
+
3882
+ #### `connect` — function · src/machine/supervisor.ts
3883
+
3884
+ Answer a connect link from the app's page with this computer's key: print the code and the commands, wait for the owner's approval.
3885
+
3886
+ ```ts
3887
+ async function connect(opts: ConnectOptions): Promise<ConnectResult>
3888
+ ```
3889
+
3890
+ #### `connectCode` — function · src/machine/protocol.ts
3891
+
3892
+ The short code both the machine and the connect page show, so the owner can see the request on screen is the one from the machine in front of them. Derived from the machine's public key + the offer, so neither side invents it.
3893
+
3894
+ ```ts
3895
+ function connectCode(publicKey: string, offerId: string): string
3896
+ ```
3897
+
3898
+ #### `createProgramManager` — function · src/machine/program-manager.ts
3899
+
3900
+ The runtime's keeper of app programs: `reconcile(links)` after each hello brings every approved program to its approved digest (download, verify, setup, self-test, start) and stops the rest; `deliver` hands a program the app's message and resolves with its answer.
3901
+
3902
+ ```ts
3903
+ function createProgramManager(opts: ProgramManagerOptions)
3904
+ ```
3905
+
3906
+ #### `DEFAULT_TIMEOUT_SECONDS` — const · src/machine/commands.ts
3907
+
3908
+ A command's time limit when it declares none: one hour.
3909
+
3910
+ ```ts
3911
+ const DEFAULT_TIMEOUT_SECONDS: 3600
3912
+ ```
3913
+
3914
+ #### `defineProgram` — function · src/machine/program-api.ts
3915
+
3916
+ Identity helper for a program written in plain JavaScript (TypeScript can use `satisfies Program`).
3917
+
3918
+ ```ts
3919
+ function defineProgram(program: Program): Program
3920
+ ```
3921
+
3922
+ #### `describeGrant` — function · src/machine/grant.ts
3923
+
3924
+ A grant in words: "the folder /home/me/proj", "the whole computer", or "no workspace access".
3925
+
3926
+ ```ts
3927
+ function describeGrant(g: WorkspaceGrant | null): string
3928
+ ```
3929
+
3930
+ #### `deviceBlockProblems` — function · src/machine/commands.ts
3931
+
3932
+ Every problem with a `device` block, in words an author can act on. Empty = valid.
3933
+
3934
+ ```ts
3935
+ function deviceBlockProblems(block: unknown): string[]
3936
+ ```
3937
+
3938
+ #### `DeviceCommandError` — class · src/machine/commands.ts
3939
+
3940
+ Why a command was refused: `unknown_command` (not declared), `bad_param` (missing, extra or ill-typed), `bad_spec`. The message says which, in words.
3941
+
3942
+ ```ts
3943
+ class DeviceCommandError extends Error { … }
3944
+ ```
3945
+
3946
+ #### `deviceHome` — function · src/machine/state.ts
3947
+
3948
+ The runtime's folder on this computer: `explicit`, else ESOUL_DEVICE_HOME, else ~/.esoul-device.
3949
+
3950
+ ```ts
3951
+ function deviceHome(explicit?: string): string
3952
+ ```
3953
+
3954
+ #### `DoorError` — class · src/machine/client.ts
3955
+
3956
+ A door call that failed: `status` 0 means the door never answered (network); otherwise the door's own `code` and message.
3957
+
3958
+ ```ts
3959
+ class DoorError extends Error { … }
3960
+ ```
3961
+
3962
+ #### `effectiveGrant` — function · src/machine/grant.ts
3963
+
3964
+ What the runtime actually allows: the grant, if it fits inside the ceiling. A folder grant inside a folder ceiling keeps the grant's (narrower) root; a computer grant needs a computer ceiling.
3965
+
3966
+ ```ts
3967
+ function effectiveGrant(grant: WorkspaceGrant | null, ceiling: WorkspaceGrant | null): { ok: true; grant: WorkspaceGrant; why?: undefined } | { ok: false; why: string; grant?: undefined }
3968
+ ```
3969
+
3970
+ #### `execute` — function · src/machine/shell.ts
3971
+
3972
+ Run one line of the shared command line as a named agent in a workspace; never throws — failures come back as `ok: false` with an `error` code.
3973
+
3974
+ ```ts
3975
+ async function execute(call: ShellCall): Promise<ShellResult>
3976
+ ```
3977
+
3978
+ #### `FileError` — class · src/machine/files.ts
3979
+
3980
+ Why a file operation was refused: outside_scope, not_found, changed, hard_link, edit_failed, patch_failed, … — with a sentence and details.
3981
+
3982
+ ```ts
3983
+ class FileError extends Error { … }
3984
+ ```
3985
+
3986
+ #### `generateDeviceKey` — function · src/machine/protocol.ts
3987
+
3988
+ A new Ed25519 keypair for a computer: the public key in base64url, the private key as PKCS#8 PEM (it never leaves the computer).
3989
+
3990
+ ```ts
3991
+ function generateDeviceKey(): DeviceKeyPair
3992
+ ```
3993
+
3994
+ #### `grantFromChoice` — function · src/machine/grant.ts
3995
+
3996
+ The owner's choice turned into a grant under this computer's ceiling, or why it cannot be.
3997
+
3998
+ ```ts
3999
+ function grantFromChoice(choice: WorkspaceChoice, ceiling: WorkspaceGrant | null): { ok: true; grant: WorkspaceGrant | null; why?: undefined } | { ok: false; why: string; grant?: undefined }
4000
+ ```
4001
+
4002
+ #### `H` — const · src/machine/protocol.ts
4003
+
4004
+ Request headers. `key` names the signer: a machine id, or `connect:<id>` before approval.
4005
+
4006
+ ```ts
4007
+ const H: { readonly key: "x-esoul-key"; readonly ts: "x-esoul-ts"; readonly sig: "x-esoul-sig"; readonly runtime: "x-esoul-runtime"; }
4008
+ ```
4009
+
4010
+ #### `isPublicKey` — function · src/machine/protocol.ts
4011
+
4012
+ Whether `s` is a base64url Ed25519 public key (43 characters).
4013
+
4014
+ ```ts
4015
+ function isPublicKey(s: unknown): s is string
4016
+ ```
4017
+
4018
+ #### `isTransient` — function · src/machine/client.ts
4019
+
4020
+ Transient = worth retrying later without telling anyone.
4021
+
4022
+ ```ts
4023
+ function isTransient(e: unknown): boolean
4024
+ ```
4025
+
4026
+ #### `Journal` — class · src/machine/files.ts
4027
+
4028
+ The undo journal of one workspace: previous contents (content-addressed), directory snapshots, and the append-only log of operations.
4029
+
4030
+ ```ts
4031
+ class Journal { … }
4032
+ ```
4033
+
4034
+ #### `machineFingerprint` — function · src/machine/state.ts
4035
+
4036
+ This computer, stably: the OS machine id (Linux /etc/machine-id, macOS the platform UUID), the user, and the runtime home. Sent when connecting, so the platform RECOGNISES a reconnect (a reinstall, a new key) instead of listing a second entry for the same computer. A different home is deliberately a different computer (a second runtime, a test run).
4037
+
4038
+ ```ts
4039
+ function machineFingerprint(home: string): string
4040
+ ```
4041
+
4042
+ #### `makeClient` — function · src/machine/client.ts
4043
+
4044
+ A signed door client for one key: every call carries the signature, retries only what the door never answered.
4045
+
4046
+ ```ts
4047
+ function makeClient(args: { base: string; keyId: string; privateKeyPem: string; runtimeVersion: string; fetch?: FetchLike; onTiming?: (t: CallTiming) => void }): DoorClient
4048
+ ```
4049
+
4050
+ #### `MAX_SKEW_MS` — const · src/machine/protocol.ts
4051
+
4052
+ How far a request's clock may be from the server's. Laptops drift; five minutes is generous and still bounds a replay.
4053
+
4054
+ ```ts
4055
+ const MAX_SKEW_MS: number
4056
+ ```
4057
+
4058
+ #### `MAX_TIMEOUT_SECONDS` — const · src/machine/commands.ts
4059
+
4060
+ The longest time limit a command may declare: seven days.
4061
+
4062
+ ```ts
4063
+ const MAX_TIMEOUT_SECONDS: number
4064
+ ```
4065
+
4066
+ #### `newAuthority` — function · src/machine/digest.ts
4067
+
4068
+ What an app update asks of the owner. Removing a command, or leaving one untouched, needs nothing; ADDING a command or CHANGING one is new authority and waits for a fresh approval.
4069
+
4070
+ ```ts
4071
+ function newAuthority(approved: Record<string, string>, current: DeviceBlock): { added: string[]; changed: string[] }
4072
+ ```
4073
+
4074
+ #### `openWorkspace` — function · src/machine/shell.ts
4075
+
4076
+ Open (or create) the workspace for a scope under the runtime's home.
4077
+
4078
+ ```ts
4079
+ function openWorkspace(home: string, id: string, scope: Scope): Workspace
4080
+ ```
4081
+
4082
+ #### `OUTPUT_LIMITS` — const · src/machine/commands.ts
4083
+
4084
+ The ceilings of the output options: chunk 3 MB, stored head 8 MB, gather 60 s, tail 64 KB.
4085
+
4086
+ ```ts
4087
+ const OUTPUT_LIMITS: { readonly maxChunkKb: 3072; readonly maxKb: 8192; readonly flushMs: 60000; readonly tailChars: number; }
4088
+ ```
4089
+
4090
+ #### `OutputUploader` — class · src/machine/capabilities.ts
4091
+
4092
+ Ordered, batched, retried upload of one job's output (the run directory is the outbox for detached runs; inline jobs hold theirs here). `finish` drains the output, then reports the end with the result.
4093
+
4094
+ ```ts
4095
+ class OutputUploader { … }
4096
+ ```
4097
+
4098
+ #### `parseGrant` — function · src/machine/grant.ts
4099
+
4100
+ A grant from untrusted JSON, or null. Roots must be absolute and plain.
4101
+
4102
+ ```ts
4103
+ function parseGrant(v: unknown): WorkspaceGrant | null
4104
+ ```
4105
+
4106
+ #### `parseUnifiedDiff` — function · src/machine/files.ts
4107
+
4108
+ The hunks of a unified diff (`@@ -a,b +c,d
4109
+
4110
+ ```ts
4111
+ function parseUnifiedDiff(diff: string): Hunk[]
4112
+ ```
4113
+
4114
+ #### `PROGRAM_KEY` — const · src/machine/digest.ts
4115
+
4116
+ Where an approval records the program's digest, beside the commands' (no command name can start with "@").
4117
+
4118
+ ```ts
4119
+ const PROGRAM_KEY: "@program"
4120
+ ```
4121
+
4122
+ #### `PROGRAM_MAX_BYTES` — const · src/machine/commands.ts
4123
+
4124
+ A program's files: at most this many bytes in all (setup steps download the heavy things).
4125
+
4126
+ ```ts
4127
+ const PROGRAM_MAX_BYTES: number
4128
+ ```
4129
+
4130
+ #### `PROGRAM_MAX_FILES` — const · src/machine/commands.ts
4131
+
4132
+ A program's files: at most this many.
4133
+
4134
+ ```ts
4135
+ const PROGRAM_MAX_FILES: 200
4136
+ ```
4137
+
4138
+ #### `programCapability` — function · src/machine/capabilities.ts
4139
+
4140
+ A message from the app to its program on this computer; the program's answer is the job's result.
4141
+
4142
+ ```ts
4143
+ function programCapability(programs: ProgramDelivery | null): Capability
4144
+ ```
4145
+
4146
+ #### `programDigest` — function · src/machine/digest.ts
4147
+
4148
+ The digest of a program: its spec (without the computed fields) and every file, sorted by path. Two builds of the same files give the same digest.
4149
+
4150
+ ```ts
4151
+ function programDigest(spec: Omit<ProgramSpec, "digest" | "size">, files: { path: string; content: Buffer | Uint8Array | string }[]): string
4152
+ ```
4153
+
4154
+ #### `programEnv` — function · src/machine/program-manager.ts
4155
+
4156
+ The environment an app's program and its setup steps get: scrubbed (the runtime's own settings never reach app code), the runtime's Node FIRST on PATH — on a computer whose only Node is the one the bootstrap brought (showrack), `node` in a setup step must be it — and HOME its own data folder, or the person's real home when the whole computer was granted (unconfined).
4157
+
4158
+ ```ts
4159
+ function programEnv(env: Record<string, string | undefined>, nodePath: string, dataDir: string, opts: { unconfined?: boolean } = {}): Record<string, string>
4160
+ ```
4161
+
4162
+ #### `programPaths` — function · src/machine/program-manager.ts
4163
+
4164
+ Where an app's program lives in the runtime's folder: its version folder, its own data folder, setup markers and logs.
4165
+
4166
+ ```ts
4167
+ function programPaths(home: string, linkId: string, digest?: string)
4168
+ ```
4169
+
4170
+ #### `programProblems` — function · src/machine/commands.ts
4171
+
4172
+ What is wrong with a program spec, in words (the platform's build adds `digest` and `size`).
4173
+
4174
+ ```ts
4175
+ function programProblems(p: unknown): string[]
4176
+ ```
4177
+
4178
+ #### `PROTOCOL` — const · src/machine/protocol.ts
4179
+
4180
+ The protocol version, the first line of every signed request.
4181
+
4182
+ ```ts
4183
+ const PROTOCOL: "esoul-device/1"
4184
+ ```
4185
+
4186
+ #### `registry` — function · src/machine/capabilities.ts
4187
+
4188
+ The capability table the runtime dispatches jobs through, by name.
4189
+
4190
+ ```ts
4191
+ function registry(caps: Capability[]): Map<string, Capability>
4192
+ ```
4193
+
4194
+ #### `renderCommand` — function · src/machine/commands.ts
4195
+
4196
+ Render one declared command. Throws `DeviceCommandError` with a sentence for anything undeclared or ill-typed.
4197
+
4198
+ ```ts
4199
+ function renderCommand(block: DeviceBlock, name: string, params: Record<string, unknown> = {}): RenderedCommand
4200
+ ```
4201
+
4202
+ #### `resolveInScope` — function · src/machine/files.ts
4203
+
4204
+ A path as the agent wrote it → an absolute path the scope allows. `cwd` is the agent's current directory (absolute, inside the scope). `~` means the home directory, and is allowed only when the scope contains it.
4205
+
4206
+ ```ts
4207
+ function resolveInScope(scope: Scope, cwd: string, p: string): string
4208
+ ```
4209
+
4210
+ #### `resolveOutput` — function · src/machine/commands.ts
4211
+
4212
+ The spec's output choice with its defaults filled in — what the runtime and the door both use.
4213
+
4214
+ ```ts
4215
+ function resolveOutput(spec: OutputSpec | undefined): ResolvedOutput
4216
+ ```
4217
+
4218
+ #### `RUNTIME_CAPABILITIES` — const · src/machine/capabilities.ts
4219
+
4220
+ What this runtime can do, said at CONNECT as well as in every hello, so the platform knows it from the approval on: work asked for before the runtime's first hello waits in the queue instead of being refused.
4221
+
4222
+ ```ts
4223
+ const RUNTIME_CAPABILITIES: string[]
4224
+ ```
4225
+
4226
+ #### `sandboxFor` — function · src/machine/shell.ts
4227
+
4228
+ How `run` and `term` are confined. The computer scope runs processes as they are. A folder scope needs an OS sandbox — bubblewrap on Linux, Seatbelt on macOS — or processes are REFUSED (a path check cannot keep a shell in a folder; `cd ..` would do).
4229
+
4230
+ ```ts
4231
+ function sandboxFor(scope: Scope): Sandbox
4232
+ ```
4233
+
4234
+ #### `sha256Hex` — function · src/machine/protocol.ts
4235
+
4236
+ Hex SHA-256 of a request body, as signed.
4237
+
4238
+ ```ts
4239
+ function sha256Hex(body: string | Uint8Array): string
4240
+ ```
4241
+
4242
+ #### `signRequest` — function · src/machine/protocol.ts
4243
+
4244
+ Sign one request (method, path with query, body) now; returns the `x-esoul-ts` and `x-esoul-sig` header values.
4245
+
4246
+ ```ts
4247
+ function signRequest( privateKeyPem: string, method: string, pathAndQuery: string, body: string | Uint8Array, now: number = Date.now(), ): { ts: string; sig: string }
4248
+ ```
4249
+
4250
+ #### `startSupervisor` — function · src/machine/supervisor.ts
4251
+
4252
+ The runtime: one per computer, for every connected app — hello, wake, claim, re-render and run, report, and the app's lifecycle.
4253
+
4254
+ ```ts
4255
+ function startSupervisor(opts: SupervisorOptions)
4256
+ ```
4257
+
4258
+ #### `tokenize` — function · src/machine/shell.ts
4259
+
4260
+ Shell-like words: quotes ('…' literal, "…" with \" escapes), backslash escapes, whitespace separation.
4261
+
4262
+ ```ts
4263
+ function tokenize(line: string): string[]
4264
+ ```
4265
+
4266
+ #### `verifyRequest` — function · src/machine/protocol.ts
4267
+
4268
+ Pure verification. `publicKey` comes from the platform's row for `x-esoul-key`, never from the request.
4269
+
4270
+ ```ts
4271
+ function verifyRequest(args: { publicKey: string; method: string; pathAndQuery: string; body: string | Uint8Array; ts: string | null | undefined; sig: string | null | undefined; now?: number; }): { ok: true } | { ok: false; reason: VerifyFailure }
4272
+ ```
4273
+
4274
+ #### `workspaceCapability` — function · src/machine/capabilities.ts
4275
+
4276
+ The shared command line, under grant ∩ ceiling, one working directory per named agent.
4277
+
4278
+ ```ts
4279
+ function workspaceCapability(): Capability
4280
+ ```
4281
+
4282
+ ### Types (40)
4283
+
4284
+ #### `CallTiming` — interface · src/machine/client.ts
4285
+
4286
+ ```ts
4287
+ interface CallTiming {
4288
+ method: string;
4289
+ path: string;
4290
+ status: number;
4291
+ ms: number;
4292
+ server: string | null;
4293
+ }
4294
+ ```
4295
+
4296
+ #### `Capability` — interface · src/machine/capabilities.ts
4297
+
4298
+ ```ts
4299
+ interface Capability {
4300
+ name: string;
4301
+ version: number;
4302
+ refuse(job: Job, ctx: JobContext): string | null;
4303
+ start(job: Job, ctx: JobContext): Promise<void>;
4304
+ }
4305
+ ```
4306
+
4307
+ #### `CommandSpec` — interface · src/machine/commands.ts
4308
+
4309
+ ```ts
4310
+ interface CommandSpec {
4311
+ argv: string[];
4312
+ params?: Record<string, ParamSpec>;
4313
+ cwd?: string;
4314
+ env?: Record<string, string>;
4315
+ config?: string[];
4316
+ timeoutSeconds?: number;
4317
+ onRestart?: "report" | "rerun";
4318
+ output?: OutputSpec;
4319
+ result?: "text" | "json";
4320
+ describe?: string;
4321
+ }
4322
+ ```
4323
+
4324
+ #### `ConfigKeySpec` — interface · src/machine/commands.ts
4325
+
4326
+ ```ts
4327
+ interface ConfigKeySpec {
4328
+ secret?: boolean;
4329
+ required?: boolean;
4330
+ describe?: string;
4331
+ }
4332
+ ```
4333
+
4334
+ #### `ConnectOptions` — interface · src/machine/supervisor.ts
4335
+
4336
+ ```ts
4337
+ interface ConnectOptions {
4338
+ url: string;
4339
+ home?: string;
4340
+ runtimeVersion: string;
4341
+ fetch?: FetchLike;
4342
+ pollMs?: number;
4343
+ print?: (line: string) => void;
4344
+ fingerprint?: string;
4345
+ ceiling?: WorkspaceGrant | null;
4346
+ }
4347
+ ```
4348
+
4349
+ #### `ConnectResult` — interface · src/machine/supervisor.ts
4350
+
4351
+ ```ts
4352
+ interface ConnectResult {
4353
+ machineId: string;
4354
+ linkId: string | null;
4355
+ base: string;
4356
+ appName: string | null;
4357
+ }
4358
+ ```
4359
+
4360
+ #### `DeviceBlock` — interface · src/machine/commands.ts
4361
+
4362
+ ```ts
4363
+ interface DeviceBlock {
4364
+ commands: Record<string, CommandSpec>;
4365
+ program?: ProgramSpec;
4366
+ config?: Record<string, ConfigKeySpec>;
4367
+ redact?: string[];
4368
+ onAppDeleted?: "kill" | "finish";
4369
+ }
4370
+ ```
4371
+
4372
+ #### `DeviceKeyPair` — interface · src/machine/protocol.ts
4373
+
4374
+ ```ts
4375
+ interface DeviceKeyPair {
4376
+ publicKey: string;
4377
+ privateKeyPem: string;
4378
+ }
4379
+ ```
4380
+
4381
+ #### `DoorClient` — interface · src/machine/client.ts
4382
+
4383
+ ```ts
4384
+ interface DoorClient {
4385
+ call<T = Record<string, unknown>>(method: "GET" | "POST", path: string, body?: unknown, opts?: { timeoutMs?: number; tries?: number }): Promise<T>;
4386
+ base: string;
4387
+ }
4388
+ ```
4389
+
4390
+ #### `Edit` — interface · src/machine/files.ts
4391
+
4392
+ ```ts
4393
+ interface Edit {
4394
+ find: string;
4395
+ replace: string;
4396
+ all?: boolean;
4397
+ }
4398
+ ```
4399
+
4400
+ #### `FetchLike` — type · src/machine/client.ts
4401
+
4402
+ ```ts
4403
+ type FetchLike = (input: string, init: { method: string; headers: Record<string, string>; body?: string; signal?: AbortSignal }) => Promise<{ status: number; text(): Promise<string>; headers?: { get(name: string): string | null } }>;
4404
+ ```
4405
+
4406
+ #### `HunkResult` — interface · src/machine/files.ts
4407
+
4408
+ ```ts
4409
+ interface HunkResult {
4410
+ hunk: number;
4411
+ applied: boolean;
4412
+ at?: number;
4413
+ offset?: number;
4414
+ reason?: string;
4415
+ }
4416
+ ```
4417
+
4418
+ #### `Job` — interface · src/machine/capabilities.ts
4419
+
4420
+ ```ts
4421
+ interface Job extends ClaimedCommand {
4422
+ capability: string;
4423
+ agent: string | null;
4424
+ }
4425
+ ```
4426
+
4427
+ #### `JobContext` — interface · src/machine/capabilities.ts
4428
+
4429
+ ```ts
4430
+ interface JobContext {
4431
+ home: string;
4432
+ link: LocalLink;
4433
+ ceiling: WorkspaceGrant | null;
4434
+ client: DoorClient;
4435
+ signal: AbortSignal;
4436
+ log: (s: string) => void;
4437
+ }
4438
+ ```
4439
+
4440
+ #### `JobOutcome` — interface · src/machine/capabilities.ts
4441
+
4442
+ ```ts
4443
+ interface JobOutcome {
4444
+ exitCode: number | null;
4445
+ reason?: string | null;
4446
+ signal?: string | null;
4447
+ result?: unknown;
4448
+ }
4449
+ ```
4450
+
4451
+ #### `JournalEntry` — interface · src/machine/files.ts
4452
+
4453
+ ```ts
4454
+ interface JournalEntry {
4455
+ opId: string;
4456
+ at: number;
4457
+ agent: string;
4458
+ op: "write" | "edit" | "patch" | "rm" | "mv" | "mkdir" | "undo";
4459
+ path: string;
4460
+ to?: string;
4461
+ before: string | null;
4462
+ after: string | null;
4463
+ dirSnapshot?: string;
4464
+ }
4465
+ ```
4466
+
4467
+ #### `LocalLink` — interface · src/machine/state.ts
4468
+
4469
+ ```ts
4470
+ interface LocalLink {
4471
+ linkId: string;
4472
+ nodeId: string;
4473
+ workspaceId: string;
4474
+ pluginId: string;
4475
+ appName: string | null;
4476
+ status: "active" | "suspended" | "revoked";
4477
+ reason: string | null;
4478
+ commands: DeviceBlock | null;
4479
+ grants?: Record<string, unknown> | null;
4480
+ }
4481
+ ```
4482
+
4483
+ #### `LocalState` — interface · src/machine/state.ts
4484
+
4485
+ ```ts
4486
+ interface LocalState {
4487
+ base: string;
4488
+ machineId: string;
4489
+ links: Record<string, LocalLink>;
4490
+ ceiling?: { scope: "folder"; root: string } | { scope: "computer" } | null;
4491
+ }
4492
+ ```
4493
+
4494
+ #### `MachineShellResult` — interface · src/machine/shell.ts
4495
+
4496
+ ```ts
4497
+ interface ShellResult {
4498
+ ok: boolean;
4499
+ text: string;
4500
+ data?: unknown;
4501
+ exitCode?: number | null;
4502
+ error?: string;
4503
+ opId?: string;
4504
+ cwd: string;
4505
+ }
4506
+ ```
4507
+
4508
+ #### `OutputSpec` — interface · src/machine/commands.ts
4509
+
4510
+ The delivery tradeoff, chosen per command (esoul-sdk docs/18 §4):
4511
+
4512
+ ```ts
4513
+ interface OutputSpec {
4514
+ mode?: "live" | "batched" | "final";
4515
+ flushMs?: number;
4516
+ maxChunkKb?: number;
4517
+ keep?: "head" | "tail" | "both";
4518
+ maxKb?: number;
4519
+ }
4520
+ ```
4521
+
4522
+ #### `ParamSpec` — type · src/machine/commands.ts
4523
+
4524
+ DECLARED COMMANDS — the wall between an app and the person's computer.
4525
+
4526
+ ```ts
4527
+ type ParamSpec =
4528
+ | "int"
4529
+ | "number"
4530
+ | "bool"
4531
+ | "string"
4532
+ | { type: "int" | "number"; min?: number; max?: number; describe?: string }
4533
+ | { type: "string"; pattern?: string; maxLength?: number; allowDash?: boolean; describe?: string }
4534
+ | { type: "enum"; values: string[]; describe?: string }
4535
+ | { type: "bool"; describe?: string };
4536
+ ```
4537
+
4538
+ #### `Program` — interface · src/machine/program-api.ts
4539
+
4540
+ An app's program: `start` is called once per process; `stop` before the process ends, when it ends cleanly.
4541
+
4542
+ ```ts
4543
+ interface Program {
4544
+ start(p: ProgramApi): void | Promise<void>;
4545
+ stop?(): void | Promise<void>;
4546
+ }
4547
+ ```
4548
+
4549
+ #### `ProgramApi` — interface · src/machine/program-api.ts
4550
+
4551
+ The program's handle on the computer and on its app.
4552
+
4553
+ ```ts
4554
+ interface ProgramApi {
4555
+ readonly computer: { readonly hostname: string; readonly platform: string; readonly arch: string; readonly cpus: number; readonly memoryGb: number };
4556
+ readonly appName: string | null;
4557
+ readonly programDir: string;
4558
+ readonly dataDir: string;
4559
+ readonly workspaceRoot: string | null;
4560
+ readonly config: Readonly<Record<string, string>>;
4561
+ status(text: string, extra?: { progress?: number; ready?: boolean }): void;
4562
+ op<T = unknown>(name: string, args?: unknown, opts?: { key?: string }): Promise<T>;
4563
+ on(topic: string, handler: (data: unknown) => unknown | Promise<unknown>): void;
4564
+ every(ms: number, fn: () => unknown | Promise<unknown>): () => void;
4565
+ log(...args: unknown[]): void;
4566
+ }
4567
+ ```
4568
+
4569
+ #### `ProgramDelivery` — interface · src/machine/capabilities.ts
4570
+
4571
+ The part of the program manager a message job needs (program-manager.ts).
4572
+
4573
+ ```ts
4574
+ interface ProgramDelivery {
4575
+ has(linkId: string): boolean;
4576
+ deliver(linkId: string, topic: string, data: unknown, signal?: AbortSignal): Promise<{ ok: boolean; result?: unknown; error?: string }>;
4577
+ }
4578
+ ```
4579
+
4580
+ #### `ProgramManager` — type · src/machine/program-manager.ts
4581
+
4582
+ The program manager's handle (see createProgramManager).
4583
+
4584
+ ```ts
4585
+ type ProgramManager = ReturnType<typeof createProgramManager>;
4586
+ ```
4587
+
4588
+ #### `ProgramManagerOptions` — interface · src/machine/program-manager.ts
4589
+
4590
+ What the program manager needs from the runtime.
4591
+
4592
+ ```ts
4593
+ interface ProgramManagerOptions {
4594
+ home: string;
4595
+ client: DoorClient;
4596
+ hostScript: string;
4597
+ nodePath?: string;
4598
+ log: (line: string) => void;
4599
+ ceiling: () => WorkspaceGrant | null;
4600
+ sandbox?: Sandbox;
4601
+ }
4602
+ ```
4603
+
4604
+ #### `ProgramSpec` — interface · src/machine/commands.ts
4605
+
4606
+ The app's own program on the computer (`computer-apps.md` §4.1): the files of `device/` shipped per app version, set up once, then started — at approval when `resident`, or when the app first sends it a message.
4607
+
4608
+ ```ts
4609
+ interface ProgramSpec {
4610
+ main: string;
4611
+ resident?: boolean;
4612
+ setup?: SetupStepSpec[];
4613
+ selfTest?: { run: string[]; timeoutSeconds?: number };
4614
+ digest?: string;
4615
+ size?: number;
4616
+ }
4617
+ ```
4618
+
4619
+ #### `ProgramState` — interface · src/machine/program-api.ts
4620
+
4621
+ Where an app's program stands on one computer — what the runtime reports and the app's Computers card renders. `phase` moves forward installing → setup → self-test → running (or ready, for a program started by its first message); `failed` and `refused` say why in `error`.
4622
+
4623
+ ```ts
4624
+ interface ProgramState {
4625
+ digest: string;
4626
+ phase: "installing" | "setup" | "self-test" | "ready" | "starting" | "running" | "stopped" | "failed" | "refused";
4627
+ steps: ProgramStepState[];
4628
+ status?: { text: string; progress?: number; ready?: boolean; at: number } | null;
4629
+ error?: string | null;
4630
+ logTail?: string[];
4631
+ restarts: number;
4632
+ updatedAt: number;
4633
+ }
4634
+ ```
4635
+
4636
+ #### `ProgramStepState` — interface · src/machine/program-api.ts
4637
+
4638
+ One setup step, as the Computers card shows it.
4639
+
4640
+ ```ts
4641
+ interface ProgramStepState {
4642
+ id: string;
4643
+ describe: string;
4644
+ state: "pending" | "running" | "done" | "cached" | "failed";
4645
+ startedAt?: number;
4646
+ endedAt?: number;
4647
+ detail?: string;
4648
+ }
4649
+ ```
4650
+
4651
+ #### `RenderedCommand` — interface · src/machine/commands.ts
4652
+
4653
+ ```ts
4654
+ interface RenderedCommand {
4655
+ name: string;
4656
+ argv: string[];
4657
+ cwd?: string;
4658
+ env: Record<string, string>;
4659
+ config: string[];
4660
+ timeoutSeconds: number;
4661
+ onRestart: "report" | "rerun";
4662
+ }
4663
+ ```
4664
+
4665
+ #### `ResolvedOutput` — interface · src/machine/commands.ts
4666
+
4667
+ ```ts
4668
+ interface ResolvedOutput {
4669
+ mode: "live" | "batched" | "final";
4670
+ flushMs: number;
4671
+ maxChunkBytes: number;
4672
+ headMaxBytes: number;
4673
+ tailMaxChars: number;
4674
+ }
4675
+ ```
4676
+
4677
+ #### `Sandbox` — type · src/machine/shell.ts
4678
+
4679
+ ```ts
4680
+ type Sandbox = { kind: "none" } | { kind: "bwrap"; bin: string } | { kind: "seatbelt" } | { kind: "unavailable"; why: string };
4681
+ ```
4682
+
4683
+ #### `Scope` — type · src/machine/files.ts
4684
+
4685
+ ```ts
4686
+ type Scope = { kind: "folder"; root: string } | { kind: "computer" };
4687
+ ```
4688
+
4689
+ #### `SetupStepSpec` — interface · src/machine/commands.ts
4690
+
4691
+ One setup step of a program: argv (never a shell line), run once per program digest and declared inputs.
4692
+
4693
+ ```ts
4694
+ interface SetupStepSpec {
4695
+ id: string;
4696
+ describe: string;
4697
+ run: string[];
4698
+ inputs?: string[];
4699
+ timeoutSeconds?: number;
4700
+ }
4701
+ ```
4702
+
4703
+ #### `ShellCall` — interface · src/machine/shell.ts
4704
+
4705
+ ```ts
4706
+ interface ShellCall {
4707
+ ws: Workspace;
4708
+ agent: string;
4709
+ line: string;
4710
+ stdin?: string;
4711
+ onOutput?: (chunk: string) => void;
4712
+ signal?: AbortSignal;
4713
+ }
4714
+ ```
4715
+
4716
+ #### `SupervisorOptions` — interface · src/machine/supervisor.ts
4717
+
4718
+ ```ts
4719
+ interface SupervisorOptions {
4720
+ home?: string;
4721
+ runtimeVersion: string;
4722
+ execScript: string;
4723
+ nodePath?: string;
4724
+ fetch?: FetchLike;
4725
+ wake?: boolean;
4726
+ pollSeconds?: number;
4727
+ flushMs?: number;
4728
+ helloEveryMs?: number;
4729
+ log?: (line: string) => void;
4730
+ onEmpty?: (why: string) => void | Promise<void>;
4731
+ }
4732
+ ```
4733
+
4734
+ #### `VerifyFailure` — type · src/machine/protocol.ts
4735
+
4736
+ ```ts
4737
+ type VerifyFailure = "missing" | "bad_key" | "skew" | "bad_signature";
4738
+ ```
4739
+
4740
+ #### `Workspace` — interface · src/machine/shell.ts
4741
+
4742
+ ```ts
4743
+ interface Workspace {
4744
+ id: string;
4745
+ scope: Scope;
4746
+ journal: Journal;
4747
+ stateDir: string;
4748
+ sandbox: Sandbox;
4749
+ }
4750
+ ```
4751
+
4752
+ #### `WorkspaceChoice` — type · src/machine/grant.ts
4753
+
4754
+ ```ts
4755
+ type WorkspaceChoice = "none" | "folder" | "computer";
4756
+ ```
4757
+
4758
+ #### `WorkspaceGrant` — type · src/machine/grant.ts
4759
+
4760
+ WORKSPACE GRANTS (plugin-device-arm.md §15): what an app's agents may reach on a computer through the shared command line — one FOLDER, or the whole COMPUTER. Two parties must agree:
4761
+
4762
+ ```ts
4763
+ type WorkspaceGrant = { scope: "folder"; root: string } | { scope: "computer" };
4764
+ ```