wizards 0.4.7 → 0.5.1

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 (183) hide show
  1. package/README.md +45 -42
  2. package/apps/runtime/dist/agents/studio-assistant.js +8 -8
  3. package/apps/runtime/dist/app.js +7 -4
  4. package/apps/runtime/dist/assistant/primer.js +1 -1
  5. package/apps/runtime/dist/auth/index.js +2 -2
  6. package/apps/runtime/dist/authoring/guide.js +8 -8
  7. package/apps/runtime/dist/cli/account.js +31 -28
  8. package/apps/runtime/dist/cli/autostart.js +5 -3
  9. package/apps/runtime/dist/cli/clients.js +1 -1
  10. package/apps/runtime/dist/cli/connect.js +24 -24
  11. package/apps/runtime/dist/cli/features.js +6 -6
  12. package/apps/runtime/dist/cli/main.js +51 -39
  13. package/apps/runtime/dist/cli/mcp.js +5 -4
  14. package/apps/runtime/dist/cli/menu.js +32 -30
  15. package/apps/runtime/dist/cli/report.js +7 -9
  16. package/apps/runtime/dist/cli/setup.js +42 -38
  17. package/apps/runtime/dist/cli/start.js +16 -15
  18. package/apps/runtime/dist/cli/status.js +20 -20
  19. package/apps/runtime/dist/cli/ui.js +9 -2
  20. package/apps/runtime/dist/cli/update.js +6 -6
  21. package/apps/runtime/dist/cli/validate.js +1 -1
  22. package/apps/runtime/dist/connectors/external.js +3 -2
  23. package/apps/runtime/dist/connectors/index.js +5 -5
  24. package/apps/runtime/dist/connectors/plugin-connections.js +2 -2
  25. package/apps/runtime/dist/connectors/via-account.js +5 -5
  26. package/apps/runtime/dist/db/control-schema.js +14 -0
  27. package/apps/runtime/dist/db/migrations/control/0009_plugin_claim.sql +9 -0
  28. package/apps/runtime/dist/db/migrations/control/meta/0009_snapshot.json +561 -0
  29. package/apps/runtime/dist/db/migrations/control/meta/_journal.json +7 -0
  30. package/apps/runtime/dist/db/schema.js +9 -9
  31. package/apps/runtime/dist/docs/dev/index.md +16 -14
  32. package/apps/runtime/dist/docs/dev/plugins/connections.md +27 -24
  33. package/apps/runtime/dist/docs/dev/plugins/events.md +19 -17
  34. package/apps/runtime/dist/docs/dev/plugins/examples.md +13 -12
  35. package/apps/runtime/dist/docs/dev/plugins/index.md +29 -24
  36. package/apps/runtime/dist/docs/dev/plugins/layout.md +20 -19
  37. package/apps/runtime/dist/docs/dev/plugins/quick-start.md +16 -15
  38. package/apps/runtime/dist/docs/dev/plugins/reference.md +52 -40
  39. package/apps/runtime/dist/docs/dev/plugins/reloading.md +24 -23
  40. package/apps/runtime/dist/docs/dev/plugins/routes.md +26 -22
  41. package/apps/runtime/dist/docs/dev/plugins/runners.md +49 -44
  42. package/apps/runtime/dist/docs/dev/plugins/server.md +95 -43
  43. package/apps/runtime/dist/docs/dev/plugins/shipping.md +19 -16
  44. package/apps/runtime/dist/docs/dev/plugins/space.md +75 -69
  45. package/apps/runtime/dist/docs/dev/plugins/starters.md +22 -17
  46. package/apps/runtime/dist/docs/dev/plugins/studio.md +80 -37
  47. package/apps/runtime/dist/docs/dev/plugins/styling.md +44 -41
  48. package/apps/runtime/dist/docs/dev/plugins/tables.md +11 -11
  49. package/apps/runtime/dist/docs/dev/plugins/tools.md +26 -25
  50. package/apps/runtime/dist/docs/dev/wizard-format.md +35 -23
  51. package/apps/runtime/dist/docs/user/ai-apps.md +46 -43
  52. package/apps/runtime/dist/docs/user/assistant.md +41 -36
  53. package/apps/runtime/dist/docs/user/build-a-wizard.md +64 -39
  54. package/apps/runtime/dist/docs/user/command-line.md +47 -44
  55. package/apps/runtime/dist/docs/user/connectors.md +40 -35
  56. package/apps/runtime/dist/docs/user/create-account.md +27 -22
  57. package/apps/runtime/dist/docs/user/data.md +29 -29
  58. package/apps/runtime/dist/docs/user/first-start.md +26 -28
  59. package/apps/runtime/dist/docs/user/index.md +16 -17
  60. package/apps/runtime/dist/docs/user/install.md +28 -27
  61. package/apps/runtime/dist/docs/user/local-only.md +21 -17
  62. package/apps/runtime/dist/docs/user/models.md +92 -84
  63. package/apps/runtime/dist/docs/user/plugins.md +28 -23
  64. package/apps/runtime/dist/docs/user/run.md +51 -50
  65. package/apps/runtime/dist/docs/user/server.md +9 -9
  66. package/apps/runtime/dist/docs/user/space.md +87 -65
  67. package/apps/runtime/dist/docs/user/steps.md +73 -69
  68. package/apps/runtime/dist/docs/user/test-and-share.md +127 -105
  69. package/apps/runtime/dist/docs/user/troubleshooting.md +40 -38
  70. package/apps/runtime/dist/docs/user/wordpress.md +62 -54
  71. package/apps/runtime/dist/documents/parse.js +2 -2
  72. package/apps/runtime/dist/engenty/shims/web-ingest.js +17 -1
  73. package/apps/runtime/dist/engine/input.js +4 -4
  74. package/apps/runtime/dist/engine/push.js +2 -2
  75. package/apps/runtime/dist/engine/runner.js +8 -8
  76. package/apps/runtime/dist/engine/steps.js +4 -4
  77. package/apps/runtime/dist/engine/surface.js +1 -1
  78. package/apps/runtime/dist/film/step.js +3 -3
  79. package/apps/runtime/dist/harness/claude.js +1 -1
  80. package/apps/runtime/dist/harness/codex.js +1 -1
  81. package/apps/runtime/dist/harness/model.js +2 -2
  82. package/apps/runtime/dist/harness/terminal.js +3 -3
  83. package/apps/runtime/dist/mcp/handler.js +2 -2
  84. package/apps/runtime/dist/mcp/tools.js +9 -9
  85. package/apps/runtime/dist/models.js +15 -15
  86. package/apps/runtime/dist/plugins/claims.js +56 -0
  87. package/apps/runtime/dist/plugins/loader.js +2 -0
  88. package/apps/runtime/dist/plugins/public.js +2 -3
  89. package/apps/runtime/dist/plugins/runs.js +13 -8
  90. package/apps/runtime/dist/render/chromium.js +24 -11
  91. package/apps/runtime/dist/routes/api.js +16 -10
  92. package/apps/runtime/dist/routes/debug.js +1 -1
  93. package/apps/runtime/dist/routes/delivery.js +46 -0
  94. package/apps/runtime/dist/routes/lang.js +7 -0
  95. package/apps/runtime/dist/routes/marketplace.js +2 -1
  96. package/apps/runtime/dist/routes/plugin-public.js +2 -1
  97. package/apps/runtime/dist/routes/plugins.js +13 -14
  98. package/apps/runtime/dist/routes/runs.js +59 -49
  99. package/apps/runtime/dist/routes/studio.js +58 -28
  100. package/apps/runtime/dist/services/access.js +3 -3
  101. package/apps/runtime/dist/services/assistant-chats.js +1 -1
  102. package/apps/runtime/dist/services/brand.js +2 -2
  103. package/apps/runtime/dist/services/cloud.js +5 -4
  104. package/apps/runtime/dist/services/errors.js +2 -1
  105. package/apps/runtime/dist/services/knowledge-model.js +10 -10
  106. package/apps/runtime/dist/services/knowledge.js +19 -19
  107. package/apps/runtime/dist/services/marketplace.js +2 -2
  108. package/apps/runtime/dist/services/package-file.js +12 -9
  109. package/apps/runtime/dist/services/package.js +3 -3
  110. package/apps/runtime/dist/services/project-files.js +15 -17
  111. package/apps/runtime/dist/services/project-index.js +10 -10
  112. package/apps/runtime/dist/services/projects.js +13 -8
  113. package/apps/runtime/dist/services/relevance.js +3 -3
  114. package/apps/runtime/dist/services/runners.js +4 -3
  115. package/apps/runtime/dist/services/runs.js +3 -3
  116. package/apps/runtime/dist/services/site.js +10 -10
  117. package/apps/runtime/dist/services/space-categories.js +22 -22
  118. package/apps/runtime/dist/services/space-data.js +16 -16
  119. package/apps/runtime/dist/services/space-origin.js +4 -4
  120. package/apps/runtime/dist/services/spaces.js +10 -10
  121. package/apps/runtime/dist/services/wizards.js +5 -5
  122. package/apps/runtime/dist/tools/index.js +1 -1
  123. package/apps/runtime/dist/tools/net-guard.js +135 -24
  124. package/apps/runtime/dist/tools/pages.js +4 -4
  125. package/apps/runtime/dist/tools/plugin.js +1 -1
  126. package/apps/runtime/dist/tools/project.js +10 -10
  127. package/apps/runtime/dist/tools/store.js +1 -1
  128. package/apps/runtime/dist/widgets/step.js +1 -1
  129. package/apps/web/dist/assets/AboutDialog-DaYwk84X.js +1 -0
  130. package/apps/web/dist/assets/{ChangelogDialog-Cm25slr3.js → ChangelogDialog-CsYbEayb.js} +1 -1
  131. package/apps/web/dist/assets/{ChatView-BXtXWnqK.js → ChatView-BRD3fKZU.js} +1 -1
  132. package/apps/web/dist/assets/EditorPage-ocPlZD3E.js +3 -0
  133. package/apps/web/dist/assets/{FlowPreview-BsfjDVv8.js → FlowPreview-Bm7sv746.js} +1 -1
  134. package/apps/web/dist/assets/{Harness-B0hD5Pp7.js → Harness-DKE-YNA5.js} +1 -1
  135. package/apps/web/dist/assets/{NewWizardChannels-DMDp9Ao5.js → NewWizardChannels-CnWckqjO.js} +1 -1
  136. package/apps/web/dist/assets/{NewWizardPage-aOisxyk2.js → NewWizardPage-BXTpbHVM.js} +1 -1
  137. package/apps/web/dist/assets/{OnboardingPage-Czb0pOyy.js → OnboardingPage-3Rz5uOsa.js} +1 -1
  138. package/apps/web/dist/assets/{ProjectPage-DPCtb42K.js → ProjectPage-QdSJwm3k.js} +1 -1
  139. package/apps/web/dist/assets/{PublicRunner-CvyA-gkf.js → PublicRunner-DEpBer5g.js} +1 -1
  140. package/apps/web/dist/assets/ReadOnly-rr_RJD-a.js +1 -0
  141. package/apps/web/dist/assets/{RunDrawer-BCiPEz9g.js → RunDrawer-BsZt0tL9.js} +1 -1
  142. package/apps/web/dist/assets/{RunnerView-4z6DMzE6.js → RunnerView-BbRukEdE.js} +1 -1
  143. package/apps/web/dist/assets/SettingsPage-qkWsQIMN.js +1 -0
  144. package/apps/web/dist/assets/{SetupPage-YFoU_nXK.js → SetupPage-PzvPPLfM.js} +1 -1
  145. package/apps/web/dist/assets/{SharePage-BBHiJ34e.js → SharePage-7vdDWG1n.js} +1 -1
  146. package/apps/web/dist/assets/changelog-BFdAYD28.js +1 -0
  147. package/apps/web/dist/assets/{esm-hSq-jeOG.js → esm-g0kaGp8t.js} +1 -1
  148. package/apps/web/dist/assets/{index-CSeBGwy0.css → index-Bi2x2g7n.css} +1 -1
  149. package/apps/web/dist/assets/{index-DB_iwB35.js → index-BzPid1oQ.js} +4 -4
  150. package/apps/web/dist/assets/lock-BK9RYIft.js +1 -0
  151. package/apps/web/dist/assets/oss-credits-BJrR7BR1.js +1 -0
  152. package/apps/web/dist/assets/{phone-CeqyvwK-.js → phone-DtkY0NfU.js} +1 -1
  153. package/apps/web/dist/assets/{settings-menu-BK8WpcGv.js → settings-menu-7EJQRFOi.js} +1 -1
  154. package/apps/web/dist/assets/{ui-BbIh6SOJ.js → ui-B6S5hYPd.js} +3 -3
  155. package/apps/web/dist/assets/{upload-BQrAlHIG.js → upload-rYCAy7ox.js} +1 -1
  156. package/apps/web/dist/index.html +3 -3
  157. package/apps/web/dist/mcp/flow.html +4 -4
  158. package/bin/wizards.mjs +1 -1
  159. package/node_modules/@engenty-wizards/plugin-sdk/dist/index.d.ts +33 -14
  160. package/node_modules/@engenty-wizards/plugin-sdk/dist/studio.d.ts +3 -3
  161. package/node_modules/@engenty-wizards/shared/dist/definition.d.ts +690 -690
  162. package/node_modules/@engenty-wizards/shared/dist/definition.js +1 -1
  163. package/node_modules/@engenty-wizards/shared/dist/engenty/data-tables/columns.d.ts +76 -76
  164. package/node_modules/@engenty-wizards/shared/dist/feature-flags.d.ts +3 -3
  165. package/node_modules/@engenty-wizards/shared/dist/feature-flags.js +3 -3
  166. package/node_modules/@engenty-wizards/shared/dist/knowledge.d.ts +13 -13
  167. package/node_modules/@engenty-wizards/shared/dist/knowledge.js +4 -4
  168. package/node_modules/@engenty-wizards/shared/dist/marketplace.d.ts +4 -4
  169. package/node_modules/@engenty-wizards/shared/dist/projects.js +1 -1
  170. package/node_modules/@engenty-wizards/shared/dist/run.d.ts +48 -48
  171. package/node_modules/@engenty-wizards/shared/dist/run.js +48 -48
  172. package/node_modules/@engenty-wizards/shared/dist/space-data.d.ts +2 -2
  173. package/node_modules/@engenty-wizards/shared/dist/surface.d.ts +20 -20
  174. package/node_modules/@engenty-wizards/shared/dist/wizard-package.js +1 -1
  175. package/package.json +5 -4
  176. package/apps/web/dist/assets/AboutDialog-BtAuA44m.js +0 -1
  177. package/apps/web/dist/assets/EditorPage-B4jX_Ckc.js +0 -3
  178. package/apps/web/dist/assets/ReadOnly-DFysMEMF.js +0 -1
  179. package/apps/web/dist/assets/SettingsPage-BKkOfz6P.js +0 -1
  180. package/apps/web/dist/assets/changelog-D8WjOq7d.js +0 -1
  181. package/apps/web/dist/assets/circle-alert-BVj6idzu.js +0 -1
  182. package/apps/web/dist/assets/lock-BoWS-JhN.js +0 -1
  183. package/apps/web/dist/assets/oss-credits-BQQX0kvk.js +0 -1
@@ -1,15 +1,15 @@
1
1
  ---
2
2
  title: Reference
3
3
  description: Every call, type, address and setting of the plugin framework on one page.
4
- version: 0.4.4
4
+ version: 0.5.0
5
5
  ---
6
6
 
7
7
  ## Server half
8
8
 
9
9
  `import { definePlugin } from "@engenty-wizards/plugin-sdk"`
10
10
 
11
- `import { ThreadDoor } from "@engenty-wizards/plugin-sdk/thread"` — a wizard run in a
12
- messenger's thread, one message at a time; a plugin brings the surface. See
11
+ `import { ThreadDoor } from "@engenty-wizards/plugin-sdk/thread"`: a wizard run in a
12
+ messenger’s thread, one message at a time, for which a plugin brings the surface. See
13
13
  [Runners](./runners.md#a-thread-door-from-the-sdk).
14
14
 
15
15
  The default export is a `WizardsPluginFactory`: `(wizards: WizardsPluginApi) => void | Promise<void>`.
@@ -20,8 +20,9 @@ The default export is a `WizardsPluginFactory`: `(wizards: WizardsPluginApi) =>
20
20
  |---|---|---|
21
21
  | `plugin` | `PluginManifest` | The manifest |
22
22
  | `server` | `PluginServerApi` | Below |
23
- | `config.get(key)` | `string \| undefined` | A setting of the install |
24
- | `log.info`, `log.warn`, `log.error` | `(...args) => void` | A log line with the plugin's name |
23
+ | `config.get(key)` | `string \| undefined` | A setting of the install: a line of its `.env` |
24
+ | `features.has(name)`, `features.all()` | `Promise<boolean>`, `Promise<Record<string, boolean>>` | The tenant’s feature switches. A short name is the plugin’s own switch from its manifest (`<id>.<name>`); a full name asks for any switch. Off unless something turned it on |
25
+ | `log.info`, `log.warn`, `log.error` | `(...args) => void` | A log line with the plugin’s name |
25
26
  | `resolvePath(relative)` | `string` | A file of the plugin as an absolute path |
26
27
 
27
28
  ### `wizards.server`
@@ -30,29 +31,30 @@ The default export is a `WizardsPluginFactory`: `(wizards: WizardsPluginApi) =>
30
31
  |---|---|
31
32
  | `registerTool(tool: PluginTool)` | [Tools](./tools.md) |
32
33
  | `registerRunner(runner: PluginRunner)` | [Runners](./runners.md). A door a wizard runs through; `page` runners answer at `/w/<token>/<id>` |
33
- | `runs` | [Runners](./runners.md#drive-a-run). `start`, `report`, `wizards`, `answerPage`, `review`, `answerAsk`, `control`, `subscribe`, `handoffUrl`, `upload`, `transcribe`: the functions behind the MCP tools, for the plugin's door |
34
- | `speech.conversation()` | [Runners](./runners.md#a-call). `{ provider: "openai", apiKey, model }`: the tenant's realtime speech model, for a call a plugin drives itself. Throws `503` / `no_model` while none is set up |
34
+ | `runs` | [Runners](./runners.md#drive-a-run). `start`, `report`, `wizards`, `answerPage`, `review`, `answerAsk`, `control`, `subscribe`, `handoffUrl`, `upload`, `transcribe`: the functions behind the MCP tools, for the plugin’s door |
35
+ | `speech.conversation()` | [Runners](./runners.md#a-call). `{ provider: "openai", apiKey, model }`: the tenant’s realtime speech model, for a call a plugin drives itself. Throws `503` / `no_model` while none is set up |
35
36
  | `registerSpaceContext({ block, tools?, steps? })` | [The space](./space.md). `block(space): string \| null`; `steps`: `agent` (default), `html` (steps that write a document or a dashboard, without the tools) |
36
37
  | `spaces.brand(space)` | [The space](./space.md#the-spaces-brand). `{ name, colors, fonts }` of the space, or `null` |
37
38
  | `registerAssistantTool(tool: PluginAssistantTool)` | [The space](./space.md#tools-of-the-space-assistant) |
38
39
  | `index.put({ space, key, title, text, link? })`, `index.remove(space, key?)` | [The space](./space.md#the-search-index) |
39
- | `spaceData.putCategories`, `.putPage`, `.putTable`, `.putFile`, `.putDocument`, `.list`, `.readPages`, `.files`, `.readFile`, `.remove`, `.summarize` | [The space](./space.md#knowledge-a-plugin-writes). Knowledge of a space: pages, tables and files under keys of the plugin's own, with their Kategorien; and the space's own pages and files to read |
40
+ | `spaceData.putCategories`, `.putPage`, `.putTable`, `.putFile`, `.putDocument`, `.list`, `.readPages`, `.files`, `.readFile`, `.remove`, `.summarize` | [The space](./space.md#knowledge-a-plugin-writes). Knowledge of a space: pages, tables and files under keys of the plugin’s own, with their categories; and the space’s own pages and files to read |
40
41
  | `registerHttpRoute(route: PluginRoute)` | [Routes](./routes.md) |
41
42
  | `registerPublicRoute(route: PluginPublicRoute)` | [Routes](./routes.md#public-routes) |
42
43
  | `publicUrl(path): Promise<string>` | The full address of a public route for the current tenant |
43
44
  | `publicRef(): Promise<string>` | [Routes](./routes.md#shared-routes). The part of `publicUrl` that names the current tenant |
44
45
  | `registerSharedRoute(route: PluginSharedRoute)` | [Routes](./routes.md#shared-routes). One route for every tenant; `handler({ request, path, params, query, inTenant })` |
45
46
  | `sharedUrl(path): string` | The full address of a shared route, the same for every tenant |
46
- | `channels.get()`, `.request({ userId, country, wish?, lang })`, `.withdraw()`, `.book({ kind, quantity, key, runId? })` | [Numbers of the Manage-App](./server.md#numbers-of-the-manage-app). `get`: `{ number, request, shared, allowed }` or `null` without a Manage-App |
47
- | `registerMigrations(folder: string)` | [Tables](./tables.md). The folder is relative to the plugin's |
47
+ | `claims.take(key)`, `.put(key)`, `.drop(key)`, `.holder(key)`, `.mine(key)` | [Keys across tenants](./server.md#keys-across-tenants). Each key for one tenant at a time; `holder` gives the tenant’s ref for `inTenant` |
48
+ | `channels.get()`, `.request({ userId, country, wish?, lang })`, `.withdraw()`, `.book({ kind: "sms" \| "call" \| "whatsapp", quantity, key, runId? })` | [Numbers of the Manage-App](./server.md#numbers-of-the-manage-app). `get`: `{ number, request, shared, allowed }` or `null` without a Manage-App |
49
+ | `registerMigrations(folder: string)` | [Tables](./tables.md). The folder is relative to the plugin’s |
48
50
  | `on(event, listener)` | [Events](./events.md). `event`: `"run.done"`, `"run.failed"`, `"run.cancelled"`, `"space.created"`, `"space.updated"`, `"space.deleted"`, `"space.file.ready"`, `"space.file.removed"` |
49
51
  | `every(name, everyMs, handler)` | [Jobs](./server.md#jobs). Per tenant that has the plugin, at most once per `everyMs` (a minute at least). `handler({ tenantId, lastRun, signal })` |
50
52
  | `web.fetch(url, init?)`, `web.read(url, { signal? })` | [The web](./server.md#the-web) |
51
53
  | `documents.parse({ data, name, mime }, { signal? })` | [Documents](./server.md#documents). `{ markdown, pages }` |
52
- | `getTenantDb(): PluginDb` | The current tenant's database, a Drizzle libSQL database |
54
+ | `getTenantDb(): PluginDb` | The current tenant’s database, a Drizzle libSQL database |
53
55
  | `generate(request): Promise<{ text, object }>` | Asks a model of the current tenant: `prompt`, `system?`, `schema?`, `model?`, `maxOutputTokens?`, `signal?`, `files?` (pictures and PDFs: `{ data, mediaType, name? }[]`). See [The server half](./server.md#asking-a-model) |
54
- | `connections.available(ids)`, `.start({ space, connector, actions })`, `.connect({ space, connector, actions, credentials })`, `.list(space)`, `.get(id)`, `.call(id, action, input)`, `.remove(id)` | [Accounts](./connections.md) |
55
- | `secrets.seal(value): string`, `secrets.unseal(sealed)` | [Keys of the plugin's own](./server.md#keys-of-the-plugins-own). `unseal` gives null for a value sealed on another install |
56
+ | `connections.available(ids)`, `.start({ space, connector, actions, lang? })`, `.connect({ space, connector, actions, credentials })`, `.list(space)`, `.get(id)`, `.call(id, action, input)`, `.remove(id)` | [Accounts](./connections.md). `lang`: the language of the page the person comes back to |
57
+ | `secrets.seal(value): string`, `secrets.unseal(sealed)` | [Keys of the plugin’s own](./server.md#keys-of-the-plugins-own). `unseal` gives null for a value sealed on another install |
56
58
  | `registerAssistantContext({ block })` | [The space](./space.md#the-assistant-on-the-plugins-own-page). `block(space): string \| null` |
57
59
  | `registerStarter(starter: PluginStarter)` | [Starters](./starters.md). Known as `<plugin id>.<starter id>` |
58
60
  | `onUnload(fn)` | Runs when the plugin unloads or loads again |
@@ -97,11 +99,18 @@ interface PluginRunner {
97
99
  handoff: ("screen" | "thread" | "sms" | "push")[];
98
100
  };
99
101
  page?(request: PluginRunnerRequest): Response | Promise<Response>;
100
- problem?(): string | null | Promise<string | null>; // why not usable in this tenant right now
102
+ problem?(): string | PluginRunnerProblem | null | Promise<string | PluginRunnerProblem | null>; // why not usable in this tenant right now
101
103
  address?(wizard: { id: string; title: string; token: string }):
102
104
  | PluginRunnerAddress | null | Promise<PluginRunnerAddress | null>; // where a person reaches the wizard
103
105
  }
104
106
 
107
+ interface PluginRunnerProblem {
108
+ text: string; // in the owner's words
109
+ kind?: "setup" | "waiting" | "plan"; // default "setup"
110
+ note?: { de: string; en: string }; // what the share dialog's row says instead
111
+ plan?: string; // "plan": the plan that has it
112
+ }
113
+
105
114
  interface PluginRunnerAddress {
106
115
  number?: string | null; // the number people write to or call, with the country code
107
116
  keyword?: string | null; // the word that picks this wizard on that number
@@ -140,7 +149,7 @@ interface PluginRuns {
140
149
  ```
141
150
 
142
151
  `waitSeconds` is at most 45. `draft`: the answers a door has so far for the page shown, so a
143
- field's `shown` follows them. `wizards(runner)`: the published wizards that switched the runner
152
+ field’s `shown` follows them. `wizards(runner)`: the published wizards that switched the runner
144
153
  on. `transcribe`: a recording of the person written down, charged to the run.
145
154
 
146
155
  `PluginRunReport` is what `get_run` gives an MCP client: status, the step, `waitingFor` (a
@@ -214,7 +223,7 @@ interface PluginStarter {
214
223
  id: string; // lower case, digits and "-"
215
224
  title: string;
216
225
  description: string; // what it makes, in one or two sentences
217
- language?: "de" | "en"; // the language of its texts; default "de"
226
+ language?: "de" | "en"; // the language of its texts; default "en"
218
227
  definition: unknown; // a complete wizard definition, checked when it is registered
219
228
  files?: Record<string, string | Uint8Array>; // workspace files by path; a string is UTF-8
220
229
  }
@@ -242,20 +251,20 @@ The default export is a `StudioPlugin`: `(studio: StudioPluginContext) => void`.
242
251
  | `studio.registerPage({ path, component, wide? })` | A page at `/studio<path>` |
243
252
  | `studio.registerNav({ to, label, icon })` | An icon in the app bar. `label: () => string` |
244
253
  | `studio.registerSettingsSection({ id, label, icon, component, menu? })` | A section at `/studio/settings/<id>` |
245
- | `studio.registerShareSection({ runner, component })` | [Runners](./runners.md#the-runners-part-of-the-share-dialog). The runner's own settings for a wizard (a keyword), under the share dialog's gear once it is switched on and set up. `component` gets `{ wizard: { id, title } }` |
246
- | `studio.registerShareSetup({ runner, title, component, notice? })` | [Runners](./runners.md#setting-up-inside-the-dialog). What "Einrichten" in the runner's row opens inside the share dialog. `component` gets `{ wizard, Actions, done(banner?) }`, `notice` `{ wizard, open }` |
247
- | `studio.registerAssistantCard({ tool, component })` | Draws a tool's result in the space assistant's chat. `component` gets `{ data, send }` |
254
+ | `studio.registerShareSection({ runner, component })` | [Runners](./runners.md#the-runners-part-of-the-share-dialog). The runner’s own settings for a wizard (a keyword), under the share dialog’s gear once it is switched on and set up. `component` gets `{ wizard: { id, title } }` |
255
+ | `studio.registerShareSetup({ runner, title, component, notice? })` | [Runners](./runners.md#setting-up-inside-the-dialog). What “Set up” in the runner’s row opens inside the share dialog. `component` gets `{ wizard, Actions, done(banner?) }`, `notice` `{ wizard, open }` |
256
+ | `studio.registerAssistantCard({ tool, component })` | Draws a tool’s result in the space assistant’s chat. `component` gets `{ data, send }` |
248
257
  | `studio.registerSpaceSection({ id, group?, label, hint?, component })` | A section of a part of the space page at `/studio/space/<group>#<id>`; `group`: `info`, `knowledge` (default), `data` or `results` |
249
- | `studio.registerSpacePart({ id, label, icon, after?, component })` | [A part of the plugin's own](./studio.md#a-part-of-the-plugins-own) at `/studio/space/<id>`, after `info`, `knowledge` or `data` (default) in the menu |
250
- | `studio.useSpaceMenu(entries)` | A hook in a plugin's part: its menu, `{ id, label, icon? }[]`, each jumping to the element with that id |
251
- | `studio.useCrumbs(items)` | A hook: the trail in the top bar for the page shown, `{ label, to? }[]`, instead of a heading of the page's own |
252
- | `studio.Assistant` | Tells the studio's assistant what to say on a page of the plugin: `title?`, `hello?`, `placeholder?`, `suggestions?: { label, message?, fill? }[]`, `onChanged?`. Draws nothing |
258
+ | `studio.registerSpacePart({ id, label, icon, after?, component })` | [A part of the plugin’s own](./studio.md#a-part-of-the-plugins-own) at `/studio/space/<id>`, after `info`, `knowledge` or `data` (default) in the menu |
259
+ | `studio.useSpaceMenu(entries)` | A hook in a plugin’s part: its menu, `{ id, label, icon? }[]`, each jumping to the element with that id |
260
+ | `studio.useCrumbs(items)` | A hook: the trail in the top bar for the page shown, `{ label, to? }[]`, instead of a heading of the page’s own |
261
+ | `studio.Assistant` | Tells the studio’s assistant what to say on a page of the plugin: `title?`, `hello?`, `placeholder?`, `suggestions?: { label, message?, fill? }[]`, `onChanged?`. Draws nothing |
253
262
  | `studio.useAssistantPage(page)` | A hook: what the page shows, for the assistant: `open?: { kind, id?, label }`, `selection?`, `summary?`, `fields?: { id, label, value }[]`, `compact?`, and the words of `studio.Assistant` |
254
263
  | `studio.useAssistantTool(name, { description, parameters? }, run)` | A hook: a tool the assistant runs in the page while it is shown, as `<plugin>_<name>`. `parameters`: JSON Schema; `run(args)` returns what the assistant learns |
255
264
  | `studio.useSpace()` | A hook: `{ id, name, readOnly, brand }` of the space the studio shows, or `null`; `brand` is `{ name, colors, fonts }` |
256
- | `studio.i18n.register({ de, en })` | Gives `t(key, vars?)` |
265
+ | `studio.i18n.register({ de, en })` | Gives `t(key, vars?)`. A key missing in the language shown falls back to `de` |
257
266
  | `studio.i18n.lang()` | `"de"` or `"en"` |
258
- | `studio.api.get`, `.post`, `.put`, `.patch`, `.del` | The plugin's own routes |
267
+ | `studio.api.get`, `.post`, `.put`, `.patch`, `.del` | The plugin’s own routes |
259
268
  | `studio.me()` | `{ user: { id, name, email }, tenant: { id, role }, mode: "managed" \| "local" }` |
260
269
  | `studio.onUnload(fn)` | Runs when the studio half loads again |
261
270
 
@@ -269,8 +278,8 @@ The default export is a `StudioPlugin`: `(studio: StudioPluginContext) => void`.
269
278
  | Tool name for the model | The id with `_` for `.` and `-` | `run_log_recent` |
270
279
  | Starter id | Lower case, digits and `-`, starting with a letter, at most 40 characters | `book` |
271
280
  | Starter id in a list of templates | `<plugin id>.<starter id>` | `contacts.follow-up` |
272
- | Table name | Begins with the plugin's id | `run_log_entry` |
273
- | Runner id | Lower case, digits and `-`, starting with a letter, at most 24 characters; not `steps`, `chat` or a sub-path of a wizard's address | `kiosk` |
281
+ | Table name | Begins with the plugin’s id | `run_log_entry` |
282
+ | Runner id | Lower case, digits and `-`, starting with a letter, at most 24 characters; not a built-in runner (`steps`, `chat`, `talk`, `video`) or a sub-path of a wizard’s address (`runs`, `icons`) | `kiosk` |
274
283
  | Element around what a plugin draws | `data-plugin="<plugin id>"` | |
275
284
 
276
285
  ## Addresses
@@ -279,39 +288,42 @@ All below `/api/studio/plugins`, all signed in:
279
288
 
280
289
  | Address | | Who |
281
290
  |---|---|---|
282
- | `GET /` | The tenant's plugins: id, name, version, description, tools, where the built files are, why a server half did not load; and what looked like a plugin and is none | Everyone; what is none: owners and admins, alone |
283
- | `ALL /<id>/…` | The plugin's own routes | Members of a tenant that has the plugin; `role: "admin"` routes owners and admins |
291
+ | `GET /` | The tenant’s plugins: id, name, version, description, tools, where the built files are, why a server half did not load; and what looked like a plugin and is none | Everyone; what is none: owners and admins, alone |
292
+ | `ALL /<id>/…` | The plugin’s own routes | Members of a tenant that has the plugin; `role: "admin"` routes owners and admins |
284
293
  | `GET /-/assets/<id>/client.js`, `/client.css` | The built studio half | Members of a tenant that has the plugin |
285
294
  | `GET /-/events` | A stream that says when a plugin loaded again | Everyone, alone |
286
295
  | `POST /-/reload` | Loads every plugin again | Owners and admins, alone |
287
296
  | `POST /-/reload/<id>` | Loads one plugin again | Owners and admins, alone |
288
297
 
289
- "Alone": where the runtime runs alone, not as the runtime of engenty.ai.
298
+ “Alone”: where the runtime runs on its own, not as the runtime of engenty.ai.
290
299
 
291
300
  For programs, with an API key or an access token:
292
301
 
293
302
  | Address | | Who |
294
303
  |---|---|---|
295
- | `ALL /api/v1/plugins/<id>/…` | The plugin's own routes, as the key's person. [With a key](./routes.md#with-a-key) | `GET` with `wizards:read`, the rest with `wizards:write`; `403` `plugin` where the account lacks the plugin |
304
+ | `ALL /api/v1/plugins/<id>/…` | The plugin’s own routes, as the key’s person. [With a key](./routes.md#with-a-key) | `GET` with `wizards:read`, the rest with `wizards:write`; `403` `plugin` where the account lacks the plugin |
296
305
 
297
306
  And public, nobody signed in:
298
307
 
299
308
  | Address | |
300
309
  |---|---|
301
- | `ALL /api/public/plugins/<id>/<ref>/…` | A plugin's public routes; `<ref>` names the tenant. `server.publicUrl` gives the full address |
302
- | `/w/<token>/<runner>` | A plugin's page runner for a wizard that switched it on (built; from source `/api/public/wizards/<token>/runners/<runner>`) |
310
+ | `ALL /api/public/plugins/<id>/<ref>/…` | A plugin’s public routes; `<ref>` names the tenant. `server.publicUrl` gives the full address |
311
+ | `ALL /api/public/plugins/<id>/shared/…` | A plugin’s shared routes, one address for every tenant. `server.sharedUrl` gives the full address |
312
+ | `/w/<token>/<runner>` | A plugin’s page runner for a wizard that switched it on (built; from source `/api/public/wizards/<token>/runners/<runner>`) |
303
313
 
304
- The studio's addresses of a plugin: `/studio<path>` for its pages, `/studio/settings/<id>` for a
305
- settings section, `/studio/space/<group>#<id>` for a section of the space page.
314
+ The studio’s addresses of a plugin: `/studio<path>` for its pages, `/studio/settings/<id>` for a
315
+ settings section, `/studio/space/<group>#<id>` for a section of the space page and
316
+ `/studio/space/<id>` for a part of it.
306
317
 
307
318
  ## Settings
308
319
 
309
- In the install's `.env`:
320
+ In the install’s `.env`:
310
321
 
311
322
  | Setting | | Default |
312
323
  |---|---|---|
313
- | `PLUGINS_DIR` | Folders to look for plugins in, comma-separated, besides the runtime's `modules/` | Installed app: `~/.engenty/wizards/plugins`. Checkout: none |
314
- | `PLUGINS_WATCH` | `0`: plugins do not load again when their files change | `1` |
324
+ | `PLUGINS_DIR` | Folders to look for plugins in, comma-separated, besides the runtime’s `modules/` | Installed app: `~/.engenty/wizards/plugins`. Checkout: none |
325
+ | `PLUGINS_WATCH` | `0`: plugins do not load again when their files change. A runtime of engenty.ai never reloads them | `1` |
326
+ | `PLUGINS_DEFAULT` | On engenty.ai: the ids of the plugins every tenant has, comma-separated | none |
315
327
  | `PLUGIN_JOBS` | `0`: this runtime runs no jobs (`server.every`), for one whose jobs another process runs | `1` |
316
328
 
317
329
  ## Files
@@ -321,7 +333,7 @@ In the install's `.env`:
321
333
  | `engenty.plugin.json` | The manifest: [Files and manifest](./layout.md#the-manifest) |
322
334
  | `src/plugin.ts` | The server half |
323
335
  | `migrations/*.sql` | Tables, where the server half registers the folder |
324
- | `ui/plugin.tsx` | The studio half's source |
336
+ | `ui/plugin.tsx` | The studio half’s source |
325
337
  | `dist/client.js`, `dist/client.css` | The studio half, built by `node scripts/build-plugin.mjs <folder>` |
326
338
 
327
339
  ## In the repository
@@ -331,6 +343,6 @@ In the install's `.env`:
331
343
  | `packages/plugin-sdk/src/` | The types, with their comments |
332
344
  | `apps/runtime/src/plugins/` | Finding, loading, tables, events |
333
345
  | `apps/runtime/src/routes/plugins.ts` | The addresses above; `apps/runtime/src/routes/api.ts` the ones for programs |
334
- | `apps/web/src/plugins/` | The studio's side |
346
+ | `apps/web/src/plugins/` | The studio’s side |
335
347
  | `scripts/build-plugin.mjs` | The build of a studio half |
336
348
  | `apps/runtime/test/plugins.test.ts` | Loading, routes, tools, tables per tenant, reloading |
@@ -1,23 +1,23 @@
1
1
  ---
2
2
  title: While it runs
3
- description: Save a file and the plugin loads again — what reloads, what survives, and how to work on a plugin.
4
- version: 0.4.0
3
+ description: Save a file and the plugin loads again – what reloads, what survives, and how to work on a plugin.
4
+ version: 0.5.0
5
5
  ---
6
6
 
7
- A runtime that runs alone watches its plugins. You do not restart it while you write one.
7
+ A runtime that runs alone watches its plugins. You don’t need to restart it while you write one.
8
8
 
9
9
  ## What happens on a change
10
10
 
11
11
  | You change | What happens |
12
12
  |---|---|
13
- | A file of the server half | The plugin loads again: what it registered goes, its `onUnload` functions run, its files are read anew |
13
+ | A file of the server half | The plugin loads again: what it registered is removed, its `onUnload` functions run, and its files are read again |
14
14
  | `dist/client.js`, `dist/client.css` | The open studio takes the new files in place. A page of the plugin that is open stays open |
15
- | A file below `ui/` | Nothing, until it is built: the change counts once `dist/` changes |
15
+ | A file below `ui/` | Nothing happens until it is built: the change counts once `dist/` changes |
16
16
  | A new file or folder in a plugins folder | The runtime looks again: new plugins load, the others stay as they are |
17
- | A plugin's file or folder is removed | That plugin unloads. Its tables stay |
18
- | A new `.sql` file | Runs on the open tenant databases with the reload |
19
- | An npm dependency of the plugin | Needs a restart: Node keeps a package once it was imported |
20
- | The manifest's `id` | The plugin is gone under the old id and new under the new one |
17
+ | A plugin’s file or folder is removed | That plugin unloads. Its tables stay |
18
+ | A new `.sql` file | It runs on the open tenant databases with the reload |
19
+ | An npm dependency of the plugin | It needs a restart: Node keeps a package once it is imported |
20
+ | The manifest’s `id` | The plugin is gone under the old id and loads under the new one |
21
21
 
22
22
  A reload takes about as long as reading the files: well under a second.
23
23
 
@@ -25,7 +25,7 @@ A reload takes about as long as reading the files: well under a second.
25
25
 
26
26
  | | |
27
27
  |---|---|
28
- | Rows in the plugin's tables | Stay |
28
+ | Rows in the plugin’s tables | Stay |
29
29
  | Variables of the module | Start again |
30
30
  | A step that is running | Keeps the tools it started with |
31
31
  | Timers, sockets, watchers | Are yours to stop: `server.onUnload`, `studio.onUnload` |
@@ -33,25 +33,26 @@ A reload takes about as long as reading the files: well under a second.
33
33
 
34
34
  ## A plugin that breaks
35
35
 
36
- A plugin that fails when it loads again is out until its file is mended: its tools are gone and
37
- its routes answer `404`. Settings → Plugins shows the error. Save a mended file and it is back.
36
+ A plugin that fails when it loads again stays out until you fix its file: its tools are gone and
37
+ its routes answer `404`. Settings → Plugins shows the error. Save the fixed file and the plugin
38
+ is back.
38
39
 
39
40
  ## By hand
40
41
 
41
- Settings → Plugins, for owners and admins of a runtime that runs alone:
42
+ Settings → Plugins has two buttons for owners and admins of a runtime that runs alone:
42
43
 
43
- | Button | Does |
44
+ | Button | What it does |
44
45
  |---|---|
45
- | Reload, on a plugin | Loads that plugin again |
46
- | Look again | Loads every plugin again, finds new ones, drops missing ones |
46
+ | “Reload”, the arrow on a plugin’s row | Loads that plugin again |
47
+ | “Look again” | Loads every plugin again, finds new ones and drops missing ones |
47
48
 
48
- `PLUGINS_WATCH=0` in the install's `.env` turns the watching off; the buttons still work.
49
+ `PLUGINS_WATCH=0` in the install’s `.env` turns the watching off; the buttons still work.
49
50
 
50
51
  ## Working on a plugin
51
52
 
52
53
  A loop that needs no restart, from a checkout:
53
54
 
54
- 1. Put the plugin's folder somewhere and name its parent in `PLUGINS_DIR` (`.env.local`).
55
+ 1. Put the plugin’s folder somewhere and name its parent in `PLUGINS_DIR` (`.env.local`).
55
56
  2. Start the app:
56
57
 
57
58
  ```bash
@@ -64,12 +65,12 @@ A loop that needs no restart, from a checkout:
64
65
  node scripts/build-plugin.mjs <plugin folder> --watch
65
66
  ```
66
67
 
67
- 4. Open the plugin's page in the studio and edit. The server half reloads on save; the page
68
- updates when the build finished.
68
+ 4. Open the plugin’s page in the studio and edit. The server half reloads when you save, and
69
+ the page updates once the build has finished.
69
70
 
70
- The runtime's log names the plugins it loaded at start (`plugins: contacts, workdays`), and
71
- prints why one did not load.
71
+ The runtime’s log names the plugins it loaded at start (`plugins: contacts, workdays`) and says
72
+ why a plugin did not load.
72
73
 
73
74
  ## On engenty.ai
74
75
 
75
- Nothing reloads there: a new version of a plugin comes with a new start of the service.
76
+ Nothing reloads there: a new version of a plugin arrives when the service starts again.
@@ -1,11 +1,11 @@
1
1
  ---
2
2
  title: Routes
3
- description: HTTP routes below the plugin's own address — signed in, inside the person's tenant.
4
- version: 0.4.3
3
+ description: HTTP routes below the plugin's own address – signed in, inside the person's tenant.
4
+ version: 0.5.0
5
5
  ---
6
6
 
7
- A plugin's routes live below `/api/studio/plugins/<id>`, and for programs with a key below
8
- `/api/v1/plugins/<id>`. Every request is a signed-in person's, inside that person's tenant.
7
+ A plugin’s routes live below `/api/studio/plugins/<id>`, and for programs with a key below
8
+ `/api/v1/plugins/<id>`. Every request is a signed-in person’s, inside that person’s tenant.
9
9
 
10
10
  ```ts
11
11
  // GET /api/studio/plugins/contacts/
@@ -50,7 +50,7 @@ server.registerHttpRoute({
50
50
  | `request.query` | The query string, as `URLSearchParams` |
51
51
  | `request.json()` | The body as JSON. An empty body is `{}` |
52
52
  | `request.user` | `{ id, tenantId, role, name, email }`: the signed-in person. `role` is `owner`, `admin` or `member` |
53
- | `request.path` | The address below the plugin's own |
53
+ | `request.path` | The address below the plugin’s own |
54
54
  | `request.request` | The web `Request` as it came in: headers, a body that is not JSON |
55
55
 
56
56
  ## The answer
@@ -62,7 +62,7 @@ server.registerHttpRoute({
62
62
  | Throws an error with a `status` (400 to 599) and a `code` (a string) | That status with `{ "error": "<message>", "code": "<code>" }` |
63
63
  | Throws anything else | `500` with `{ "error": "The plugin failed.", "code": "plugin_failed" }`. The error itself is logged, not sent |
64
64
 
65
- Send a `code`, not a sentence for the person. The studio half says it in the person's language:
65
+ Send a `code`, not a sentence for the person. The studio half says it in the person’s language:
66
66
 
67
67
  ```ts
68
68
  throw Object.assign(new Error("No such contact."), { status: 404, code: "no_contact" });
@@ -72,7 +72,7 @@ throw Object.assign(new Error("No such contact."), { status: 404, code: "no_cont
72
72
 
73
73
  - The studio, signed in. A request without a session gets `401` before it reaches the plugin.
74
74
  - A program with a key, at a second address: [With a key](#with-a-key).
75
- - Only people of a tenant that has the plugin. For any other tenant the studio's address
75
+ - Only people of a tenant that has the plugin. For any other tenant the studio’s address
76
76
  answers `404`.
77
77
  - Not the people who run a wizard on its link, and not the outside world. For a webhook of
78
78
  another service, a plugin has [public routes](#public-routes).
@@ -80,28 +80,28 @@ throw Object.assign(new Error("No such contact."), { status: 404, code: "no_cont
80
80
  ## With a key
81
81
 
82
82
  A program that is not the studio, such as the [WordPress plugin](../../user/wordpress.md),
83
- calls the same routes below `/api/v1/plugins/<id>`. It signs in with an API key (on your own
84
- install Settings → Integrate makes one, on engenty.ai the account) or an access token of
85
- engenty.ai:
83
+ calls the same routes below `/api/v1/plugins/<id>`. It signs in with an API key or with an
84
+ access token of engenty.ai. On a runtime that runs alone, you create the key under Settings →
85
+ Integrate; on engenty.ai, in your account:
86
86
 
87
87
  ```bash
88
88
  curl -H "Authorization: Bearer $KEY" https://wizards.example.com/api/v1/plugins/contacts/
89
89
  ```
90
90
 
91
- - The route runs as the key's person, inside that person's tenant: `request.user` is that
92
- person, with the role. A `role: "admin"` route answers a member's key with `403`. `name` and
91
+ - The route runs as the key’s person, inside that person’s tenant: `request.user` is that
92
+ person, with the role. A `role: "admin"` route answers a member’s key with `403`. `name` and
93
93
  `email` can be empty.
94
94
  - `GET` needs the scope `wizards:read`, every other method `wizards:write`. A key has every
95
- scope, a token the ones the person granted. Without it: `403` with the code
95
+ scope, a token the ones the person granted. A call without the scope gets `403` with the code
96
96
  `insufficient_scope`.
97
- - A plugin the runtime has but the key's account does not answers `403` with the code `plugin`.
98
- An id it does not know answers `404`.
97
+ - A plugin that the runtime has but the key’s account does not have answers `403` with the code
98
+ `plugin`. An id the runtime does not know answers `404`.
99
99
  - `x-api-key: <key>` works in place of the `Authorization` header.
100
- - The answers and errors are the same as at the studio's address.
100
+ - The answers and errors are the same as at the studio’s address.
101
101
 
102
102
  ## Public routes
103
103
 
104
- A public route answers anyone: a CMS that says a page changed, another service's webhook. Its
104
+ A public route answers anyone: a CMS that says a page changed, another service’s webhook. Its
105
105
  address names the tenant without giving its id away; the plugin hands it out:
106
106
 
107
107
  ```ts
@@ -127,12 +127,12 @@ const address = await server.publicUrl(`/signal/${source.token}`);
127
127
  body. Keep only a hash of it.
128
128
  - The handler runs inside the tenant of the address: `server.getTenantDb()` is its database.
129
129
  - `publicUrl` gives the same address for a tenant every time.
130
- - A body is taken up to 4 MB. Answers and errors work as for the plugin's other routes.
130
+ - A body is taken up to 4 MB. Answers and errors work as for the plugin’s other routes.
131
131
  - A tenant without the plugin, or a suspended one, answers `404`.
132
132
 
133
133
  ## Shared routes
134
134
 
135
- Some services post to one webhook for everyone: one account of the runtime's, one app. A shared
135
+ Some services post to one webhook for everyone: one account of the runtime’s, one app. A shared
136
136
  route has one address for all tenants, and the call itself says which tenant it is for, with
137
137
  the ref the plugin handed the service before:
138
138
 
@@ -158,15 +158,19 @@ server.sharedUrl("/incoming");
158
158
  // https://…/api/public/plugins/<id>/shared/incoming
159
159
  ```
160
160
 
161
- - The handler runs outside any tenant: check the service's signature, then `inTenant`.
161
+ - The handler runs outside any tenant: check the service’s signature, then call `inTenant`.
162
162
  - `inTenant(ref, work)` runs `work` inside the tenant of that ref, as a public route of the
163
163
  tenant would run. A ref no tenant has, a suspended tenant and one without the plugin throw
164
164
  `404` / `not_found`.
165
165
  - `publicRef()` is the part of `publicUrl` that names the tenant; it is the same every time.
166
+ - A body is taken up to 4 MB, as on a public route.
167
+ - Where the call does not carry a ref (a keyword written to a number every tenant shares),
168
+ [`server.claims`](./server.md#keys-across-tenants) finds it: `holder(key)` is the ref of the
169
+ tenant that holds the key.
166
170
 
167
171
  ## From the studio half
168
172
 
169
- `studio.api` calls the plugin's own routes and gives the parsed JSON:
173
+ `studio.api` calls the plugin’s own routes and gives the parsed JSON:
170
174
 
171
175
  ```ts
172
176
  const { contacts } = await studio.api.get<{ contacts: Person[] }>("/");
@@ -177,5 +181,5 @@ See [The studio half](./studio.md#the-plugins-own-routes).
177
181
 
178
182
  ## Addresses that are taken
179
183
 
180
- `/api/studio/plugins/-/…` is the runtime's own part: the built files, reloading. No plugin is
184
+ `/api/studio/plugins/-/…` is the runtime’s own part: the built files, reloading. No plugin is
181
185
  called `-`, so the two never meet.