esoul-sdk 0.24.0 → 0.25.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -2,6 +2,64 @@
2
2
 
3
3
  Releases before 0.20.0 are recorded in the repository history only.
4
4
 
5
+ **Versions say whether an app must change** (from the release after 0.25.0). A *breaking* release
6
+ moves the leftmost non-zero number (0.25.x → 0.26.0; after 1.0, 1.x → 2.0.0) and lists every change under
7
+ `### Breaking`: one bullet per API, the API in backticks first, then what an app must do. Every
8
+ other release — features, fixes — moves the last number. The Forge reads this: a compatible update
9
+ is offered with one Update button; a breaking one is reviewed by the agent against the app before
10
+ anything is installed. The release check refuses a version that does not match its notes
11
+ (src/lib/forge/release-notes.pure.ts).
12
+
13
+ ## 0.25.2
14
+
15
+ ### Added
16
+
17
+ - `EventDefinition.resolveConcurrent(state, eventData, { theirsSess })`: an event that writes a PATCH (sub-items with the version each built on — a CAD edit's per-node sets with their bases, a notes page's paragraphs) merges a concurrent write itself before it becomes a conflict. Called by the fence only when the write would land as a conflict; returns the rewritten event, with `detail` naming what could not be merged (kept on the conflict marker for the app's own resolution UI), or `null` to keep the plain conflict. The hook block notes merges paragraphs with, now declarable by any SDK app (docs/17 §4).
18
+
19
+ ## 0.25.1
20
+
21
+ ### Added
22
+
23
+ - `headlessBridge(ctx, { url, boot, calls, viewport })` (esoul-sdk/server): a web page opened in a
24
+ headless browser ON THE PLATFORM and driven over `postMessage`, for an app whose engine only
25
+ exists as a page (a WebAssembly CAD kernel, a canvas renderer). The person's open tab is one
26
+ place such a page can run; this is the other — on Vercel, with nobody's tab open, so an agent on
27
+ a phone or over MCP gets the same work done. The page is loaded top-level and is its own
28
+ `parent`, so an iframe bridge answers unchanged; the browser and the loaded page stay warm on the
29
+ platform between calls for the same url (the first call pays the launch, later ones only the
30
+ calls). `url` must be https and public; at most 32 calls; a Forge box has no browser, so there
31
+ the call throws and says so (docs/06-server.md, "The headless bridge").
32
+
33
+ ## 0.25.0
34
+
35
+ ### Added
36
+
37
+ - Credential slots for ANY provider (docs/08): `family: "oauth2"` (any OAuth 2.0 provider —
38
+ Microsoft for Outlook / OneNote / OneDrive, Dropbox, GitHub… — with `provider: { name,
39
+ authorizeUrl, tokenUrl, scopes, hosts }`) and `family: "apiKey"` (a key the person pastes, with
40
+ `provider: { name, hosts, header?, prefix?, verifyUrl? }`), beside `google`. The person connects
41
+ the account once in Account settings → Accounts (their own registered OAuth client, entered once
42
+ and sealed, or one the platform holds for the app), assigns it to apps like a Google account, and
43
+ your code calls `credentials(ctx).slot(name).fetch(url)`: the platform adds the token or key and
44
+ sends it only to the declared hosts the person approved. The token, the refresh token, the key and
45
+ the client secret never reach app code. Each app's calls get a token narrowed to its own scopes
46
+ when one account serves several apps; a `connectUrl` opens Account settings → Accounts (nothing
47
+ signs in from a link); an account the provider does not name is refused (declare `accountUrl`
48
+ or ask for `openid`); "the same provider" means the same `authorizeUrl` and `tokenUrl`.
49
+ - `CredentialStatus.provider` (`{ name, family }`) and `missingHosts`; `useCredential` and
50
+ `<ConnectAccount/>` word themselves with the provider's name.
51
+ - `fakeCredentials(slots)` (esoul-sdk/testing): test code that uses an account, with the platform's
52
+ refusals and every call recorded.
53
+ - Manifest exports: `OAuth2ProviderSchema`, `ApiKeyProviderSchema`, `GoogleCredentialSlotSchema`,
54
+ `OAuth2CredentialSlotSchema`, `ApiKeyCredentialSlotSchema`, `CREDENTIAL_HOST_RE`,
55
+ `RESERVED_AUTHORIZE_PARAMS`, `credentialProviderKey`.
56
+
57
+ ### Changed
58
+
59
+ - `getPluginConnectionCredentials` (the older `connections`) answers only inside the app's own
60
+ running op, route or task, and only for a connection of the owner of the workspace the call runs
61
+ in. Prefer a credential slot (docs/08 §9).
62
+
5
63
  ## 0.24.0
6
64
 
7
65
  ### Added
package/README.md CHANGED
@@ -16,7 +16,11 @@ platform's implementations.
16
16
  - [Getting started](docs/01-getting-started.md)
17
17
  - [Manifest reference](docs/02-manifest.md)
18
18
  - [Documentation index](#documentation)
19
+ - [A service on your computer, connected to your app](docs/20-a-service-on-your-computer.md)
20
+ - [CHANGELOG.md](CHANGELOG.md) — what each version added
21
+ - `api-reference.md` — every export with its signature and doc comment
19
22
  - `llms.txt` and `llms-full.txt` — the documentation in one file, for coding models
23
+ - The package on npm: <https://www.npmjs.com/package/esoul-sdk>
20
24
 
21
25
  ## Features
22
26
 
@@ -80,7 +84,8 @@ test helpers. Functions that need the platform (`pluginDb`, `viewerProfile`, `mi
80
84
  throw `host only` when called outside it; they are exercised through the test harness or in a
81
85
  Forge workbench.
82
86
 
83
- Requirements: Node.js 20 or newer, TypeScript 5, React 19 for the UI entry point.
87
+ Requirements: Node.js 20 or newer (22 for `esoul-device` on a computer), TypeScript 5, React 18 or
88
+ newer for the UI entry point.
84
89
 
85
90
  ## Concepts
86
91
 
@@ -120,11 +125,13 @@ application that provides the named contract.
120
125
  | Import | Contents |
121
126
  |---|---|
122
127
  | `esoul-sdk` | Schema and event types, `defineOps`, `handleOp`, `opTool`, `definePluginChannel`, `defineBindingEvent`, `checkBinding`, `resolveAppRole`, `chartSvg`, `incompleteStateNotice`, `deterministicReducerId`, `callPluginOp`, `kickPluginTask`, `pluginRouteUrl`, `nanoid`, the manifest schema, `compileEnvelope`, `compileCustomRole` |
123
- | `esoul-sdk/server` | `pluginDb`, `viewerProfile`, `sseStream`, `mintRouteToken`, `readAppState`, `callWorkspaceTool`, `computer`, `emitPluginAppEvent`, `generateAppImage`, `renderChartImage`, `setAppRole`, `listAppRoles`, `defineAppRole`, `removeAppRole`, `getPluginConnectionCredentials`, `pluginFiles`, and the context types (`PluginOpContext`, `PluginRouteContext`, `PluginViewer`, `PluginServerModule`) |
124
- | `esoul-sdk/react` | `useViewer`, `useSignInWall`, `useAppCanEdit`, `usePluginEventDispatch`, `usePluginRealtime`, `useWorkspaceTools`, `usePluginWorkspaceFiles`, `usePluginFileUpload`, `useFileSources`, `useFileSourceEntries`; the types `PluginViewerPublic`, `SignInWall` |
125
- | `esoul-sdk/testing` | `memoryDb`, `fakeViewer`, `runOp`, `fakeApps`, `capture`, `startMockOAuth` |
128
+ | `esoul-sdk/server` | `pluginDb`, `viewerProfile`, `sseStream`, `mintRouteToken`, `readAppState`, `callWorkspaceTool`, `computer`, `devices`, `questions`, `credentials`, `llm`, `filesForOp`, `pluginFiles`, `emitPluginAppEvent`, `generateAppImage`, `renderChartImage`, `setAppRole`, `listAppRoles`, `defineAppRole`, `removeAppRole`, `getPluginConnectionCredentials`, and the context types (`PluginOpContext`, `PluginRouteContext`, `PluginViewer`, `PluginServerModule`) |
129
+ | `esoul-sdk/react` | `useViewer`, `useSignInWall`, `useAppCanEdit`, `usePluginEventDispatch`, `runOptimistic`, `useAppNav`, `usePluginRealtime`, `useWorkspaceTools`, `useRemoteReconcile`, `useCredential`, `<ConnectAccount/>`, `<ConnectComputer/>`, `useDevices`, `useDeviceRun`, `useDeviceActions`, the file hooks (`useFileSources`, `useFileSourceEntries`, `useFolderAutocomplete`, `useFileUrls`, `useTransfer`, `usePluginWorkspaceFiles`, `usePluginFileUpload`), `ImageLabeler`; the types `PluginViewerPublic`, `SignInWall` |
130
+ | `esoul-sdk/machine` | what runs ON the person's computer: the `Program` type for a device program (types only), and the `esoul-device` runtime |
131
+ | `esoul-sdk/testing` | `memoryDb`, `fakeViewer`, `runOp`, `fakeApps`, `capture`, `memoryFiles`, `scriptedModel`, `simGmail`, `fakeCredentials`, `startMockOAuth`, `listFailedRequests` |
126
132
  | `esoul-sdk/schemas/plugin.schema.json` | The JSON Schema of the manifest |
127
133
  | `esoul-app validate <dir>` | Command-line validator for a package folder |
134
+ | `esoul-device` | The runtime on a person's computer: `connect`, `status`, `sh`, `config set`, `disconnect`, `uninstall` (docs/18) |
128
135
 
129
136
  ## Getting started
130
137
 
@@ -498,7 +505,8 @@ Platform columns on every table: `id`, `workspaceId`, `nodeId`, `ownerId`, `crea
498
505
  | `setAppRole(ctx, { email, role, attrs? })` | The owner gives an account one of the application's role words, or a composed role, with attribute values (`{ customer: "204" }`) validated against `roles.attributes`. Recorded as an event. |
499
506
  | `listAppRoles(ctx)` | `{ people, roles, custom, envelope }`. |
500
507
  | `defineAppRole(ctx, definition)`, `removeAppRole(ctx, name)` | The owner composes or removes a role inside `roles.custom`. |
501
- | `getPluginConnectionCredentials(ctx)` | The credentials of the instance's bound connection. |
508
+ | `credentials(ctx).slot(name)` | The person's account for a declared slot — Google, any OAuth 2.0 provider (Microsoft, Dropbox…) or a pasted API key: `status()` and `fetch(url, init?)`, with the token or key added by the platform and sent only where the slot may reach. Your code never holds it (docs/08). |
509
+ | `getPluginConnectionCredentials(connectionId, pluginId)` | Older: the token of one of the app's own `connections`, readable only from the app's own running call for the workspace owner's connection. Prefer a `credentials` slot (docs/08 §9). |
502
510
  | `pluginFiles(ctx)`, `filesForOp(ctx)` | Workspace files and file sources from server code: read by ref or address (`"workspace:/Mail/a.pdf"`), `save` to a path, and `transfer` a list of files into a folder with platform progress, Stop and a clean Retry (docs/09). |
503
511
 
504
512
  ### `esoul-sdk/react`
@@ -523,6 +531,7 @@ Platform columns on every table: `id`, `workspaceId`, `nodeId`, `ownerId`, `crea
523
531
  | `runOp(server, opName, { viewer, args?, db?, apps?, notifyFails? })` | Calls `pluginServer.ops[opName]` with a platform-shaped context. Returns `{ result, notified, emitted }`; throws the op's refusals. |
524
532
  | `fakeApps(fixtures)` | Bound applications for `ctx.apps`. |
525
533
  | `capture()` | A recorder for callbacks. |
534
+ | `fakeCredentials(slots)` | A stand-in for `credentials(ctx)`: each slot answers as the test says (`status`, `hosts`, `fetch`), the platform's refusals apply, and `calls` records every request (docs/08 §7). |
526
535
  | `startMockOAuth(opts?)` | A local OAuth server for connection tests. |
527
536
 
528
537
  ### Command line
@@ -565,7 +574,7 @@ Exits 0 when the folder is a well-formed application package; otherwise prints e
565
574
  | [5. The UI](docs/05-ui.md) | React, hooks, theme, responsive rules |
566
575
  | [6. The server](docs/06-server.md) | Ops, routes, webhooks, `computer`, charts, calling other applications |
567
576
  | [7. Background tasks](docs/07-background-tasks.md) | The durable executor, the replay model, concurrency |
568
- | [8. Connections](docs/08-connections.md) | OAuth and API-key connections held by the platform |
577
+ | [8. Accounts](docs/08-connections.md) | The person's Google, Microsoft or any OAuth 2.0 / API-key account, used through the platform without your code holding the token |
569
578
  | [9. Files](docs/09-files.md) | Workspace files, Drive, file providers |
570
579
  | [10. Testing](docs/10-testing.md) | The fold contract, ops, tasks and rules as tests |
571
580
  | [11. Shipping](docs/11-shipping.md) | The repository, review, release, install, dependencies |
@@ -574,12 +583,20 @@ Exits 0 when the folder is a well-formed application package; otherwise prints e
574
583
  | [14. Your own tables](docs/14-database.md) | `db`, rules, scopes, sealed fields, indexes, migrations |
575
584
  | [15. Realtime](docs/15-realtime.md) | Topics, audiences, addressing |
576
585
  | [16. Bindings](docs/16-bindings.md) | Slots, contracts, reaching another application |
586
+ | [17. Editing and merging](docs/17-editing-and-merging.md) | An editor that takes changes from other devices and agents; merges |
587
+ | [18. Your computer](docs/18-your-computer.md) | The device arm: commands, the workspace, programs, `esoul-device` |
577
588
  | [19. Model calls](docs/19-model-calls.md) | `llm(ctx)`: billed model calls, budgets, `scriptedModel()` for tests |
589
+ | [20. A service on your computer](docs/20-a-service-on-your-computer.md) | A walk-through: a program that keeps a service running on the person's computer, talking to the app both ways — built in the Forge, then installed |
578
590
 
579
591
  ## Versions
580
592
 
593
+ From 0.20.0 on, every version is in [CHANGELOG.md](CHANGELOG.md) (accounts through the platform — Google, then any OAuth 2.0 or API-key provider in 0.25.0,
594
+ model calls, file transfers, opened-from-outside navigation, server-only events, the questions bell,
595
+ mail waits). Earlier:
596
+
581
597
  | Version | Changes |
582
598
  |---|---|
599
+ | 0.16.0 – 0.19.1 | `runOptimistic` and the failed-request banner; stable list paging; editor sync (`useRemoteReconcile`, `mergeLines` / `mergeFields` / `mergeRecordsById`); the device arm — `devices(ctx)`, `<ConnectComputer/>`, the `esoul-device` runtime, declared commands, the shared command line, an app's own program on the computer (docs/17, 18). |
583
600
  | 0.15.0 | **Files work in a Forge box**, and do more: `FilesApi.list(…, opts)` narrowed at the source, `listAll`, `resolvePath`, `readMany`, `write` (make or replace by name — a label file beside its image; manifest `fileSources.write`), `readGrant` + `fileGrantUrl` (signed, expiring URLs for an `<img>` in a preview and for a machine). UI: `useFolderAutocomplete`, `useFileUrls`, `useFileSourceEntries(…, opts)` with `loadMore`, `listFileEntries`, `resolveFilePath`. `ImageLabeler` / `PolygonCanvas` — the Explorer's polygon editor as a component. Pure: `pairImagesWithLabels`, `serializeLabelMe`, `parseLabelMe`, `labelFileNameFor`. Testing: `memoryFiles`. |
584
601
  | 0.14.0 | Attribute-scoped access: `roles.attributes` (typed; validated when granted; `viewer.attrs`); scoped rule principals `{ role, where }` with `viewer.<attribute>`, applied to reads, aggregates, updates and deletes and filling or refusing creates, in both database clients; a rule combining `creator` with a scoped role is refused. `compileManifestRules` is the one reader of a manifest's rules. VIEW AS personas carry attributes (`visitor-a?customer=204`). |
585
602
  | 0.13.0 | Composed roles: `roles.custom` envelope; `defineAppRole`, `removeAppRole`; `setAppRole` with attributes; `listAppRoles` returns composed roles and the envelope. Reads, updates and deletes are narrowed by the composition in both database clients; surfaces not handed to the role are refused; `useViewer().customRole` and `can`. `access: [<roles>]` on a surface. Composed roles are personas in the Forge. |