wizards 0.5.0 → 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.
- package/README.md +45 -42
- package/apps/runtime/dist/agents/studio-assistant.js +3 -3
- package/apps/runtime/dist/app.js +7 -4
- package/apps/runtime/dist/auth/index.js +2 -2
- package/apps/runtime/dist/authoring/guide.js +1 -1
- package/apps/runtime/dist/cli/account.js +31 -28
- package/apps/runtime/dist/cli/autostart.js +5 -3
- package/apps/runtime/dist/cli/clients.js +1 -1
- package/apps/runtime/dist/cli/connect.js +24 -24
- package/apps/runtime/dist/cli/features.js +6 -6
- package/apps/runtime/dist/cli/main.js +51 -39
- package/apps/runtime/dist/cli/mcp.js +5 -4
- package/apps/runtime/dist/cli/menu.js +32 -30
- package/apps/runtime/dist/cli/report.js +7 -9
- package/apps/runtime/dist/cli/setup.js +42 -38
- package/apps/runtime/dist/cli/start.js +16 -15
- package/apps/runtime/dist/cli/status.js +20 -20
- package/apps/runtime/dist/cli/ui.js +9 -2
- package/apps/runtime/dist/cli/update.js +6 -6
- package/apps/runtime/dist/cli/validate.js +1 -1
- package/apps/runtime/dist/connectors/external.js +3 -2
- package/apps/runtime/dist/connectors/index.js +5 -5
- package/apps/runtime/dist/connectors/plugin-connections.js +2 -2
- package/apps/runtime/dist/connectors/via-account.js +5 -5
- package/apps/runtime/dist/db/control-schema.js +14 -0
- package/apps/runtime/dist/db/migrations/control/0009_plugin_claim.sql +9 -0
- package/apps/runtime/dist/db/migrations/control/meta/0009_snapshot.json +561 -0
- package/apps/runtime/dist/db/migrations/control/meta/_journal.json +7 -0
- package/apps/runtime/dist/docs/dev/index.md +16 -14
- package/apps/runtime/dist/docs/dev/plugins/connections.md +27 -24
- package/apps/runtime/dist/docs/dev/plugins/events.md +19 -17
- package/apps/runtime/dist/docs/dev/plugins/examples.md +13 -12
- package/apps/runtime/dist/docs/dev/plugins/index.md +29 -24
- package/apps/runtime/dist/docs/dev/plugins/layout.md +20 -19
- package/apps/runtime/dist/docs/dev/plugins/quick-start.md +16 -15
- package/apps/runtime/dist/docs/dev/plugins/reference.md +52 -40
- package/apps/runtime/dist/docs/dev/plugins/reloading.md +24 -23
- package/apps/runtime/dist/docs/dev/plugins/routes.md +26 -22
- package/apps/runtime/dist/docs/dev/plugins/runners.md +49 -44
- package/apps/runtime/dist/docs/dev/plugins/server.md +95 -43
- package/apps/runtime/dist/docs/dev/plugins/shipping.md +19 -16
- package/apps/runtime/dist/docs/dev/plugins/space.md +75 -69
- package/apps/runtime/dist/docs/dev/plugins/starters.md +22 -17
- package/apps/runtime/dist/docs/dev/plugins/studio.md +80 -37
- package/apps/runtime/dist/docs/dev/plugins/styling.md +44 -41
- package/apps/runtime/dist/docs/dev/plugins/tables.md +11 -11
- package/apps/runtime/dist/docs/dev/plugins/tools.md +26 -25
- package/apps/runtime/dist/docs/dev/wizard-format.md +35 -23
- package/apps/runtime/dist/docs/user/ai-apps.md +46 -43
- package/apps/runtime/dist/docs/user/assistant.md +41 -36
- package/apps/runtime/dist/docs/user/build-a-wizard.md +64 -39
- package/apps/runtime/dist/docs/user/command-line.md +47 -44
- package/apps/runtime/dist/docs/user/connectors.md +40 -35
- package/apps/runtime/dist/docs/user/create-account.md +27 -22
- package/apps/runtime/dist/docs/user/data.md +29 -29
- package/apps/runtime/dist/docs/user/first-start.md +26 -28
- package/apps/runtime/dist/docs/user/index.md +16 -17
- package/apps/runtime/dist/docs/user/install.md +28 -27
- package/apps/runtime/dist/docs/user/local-only.md +21 -17
- package/apps/runtime/dist/docs/user/models.md +92 -84
- package/apps/runtime/dist/docs/user/plugins.md +28 -23
- package/apps/runtime/dist/docs/user/run.md +51 -50
- package/apps/runtime/dist/docs/user/server.md +9 -9
- package/apps/runtime/dist/docs/user/space.md +87 -65
- package/apps/runtime/dist/docs/user/steps.md +73 -69
- package/apps/runtime/dist/docs/user/test-and-share.md +127 -105
- package/apps/runtime/dist/docs/user/troubleshooting.md +40 -38
- package/apps/runtime/dist/docs/user/wordpress.md +62 -54
- package/apps/runtime/dist/documents/parse.js +2 -2
- package/apps/runtime/dist/engenty/shims/web-ingest.js +17 -1
- package/apps/runtime/dist/engine/input.js +4 -4
- package/apps/runtime/dist/engine/push.js +2 -2
- package/apps/runtime/dist/engine/runner.js +8 -8
- package/apps/runtime/dist/engine/steps.js +2 -2
- package/apps/runtime/dist/engine/surface.js +1 -1
- package/apps/runtime/dist/film/step.js +3 -3
- package/apps/runtime/dist/harness/claude.js +1 -1
- package/apps/runtime/dist/harness/codex.js +1 -1
- package/apps/runtime/dist/harness/model.js +2 -2
- package/apps/runtime/dist/harness/terminal.js +3 -3
- package/apps/runtime/dist/mcp/handler.js +2 -2
- package/apps/runtime/dist/mcp/tools.js +9 -9
- package/apps/runtime/dist/models.js +15 -15
- package/apps/runtime/dist/plugins/claims.js +56 -0
- package/apps/runtime/dist/plugins/loader.js +2 -0
- package/apps/runtime/dist/plugins/public.js +2 -3
- package/apps/runtime/dist/plugins/runs.js +13 -8
- package/apps/runtime/dist/render/chromium.js +24 -11
- package/apps/runtime/dist/routes/api.js +15 -9
- package/apps/runtime/dist/routes/debug.js +1 -1
- package/apps/runtime/dist/routes/delivery.js +46 -0
- package/apps/runtime/dist/routes/lang.js +7 -0
- package/apps/runtime/dist/routes/marketplace.js +2 -1
- package/apps/runtime/dist/routes/plugin-public.js +2 -1
- package/apps/runtime/dist/routes/plugins.js +13 -14
- package/apps/runtime/dist/routes/runs.js +59 -49
- package/apps/runtime/dist/routes/studio.js +53 -23
- package/apps/runtime/dist/services/access.js +3 -3
- package/apps/runtime/dist/services/assistant-chats.js +1 -1
- package/apps/runtime/dist/services/brand.js +2 -2
- package/apps/runtime/dist/services/cloud.js +5 -4
- package/apps/runtime/dist/services/errors.js +2 -1
- package/apps/runtime/dist/services/knowledge-model.js +2 -2
- package/apps/runtime/dist/services/marketplace.js +2 -2
- package/apps/runtime/dist/services/package-file.js +12 -9
- package/apps/runtime/dist/services/package.js +2 -2
- package/apps/runtime/dist/services/project-files.js +10 -12
- package/apps/runtime/dist/services/projects.js +13 -8
- package/apps/runtime/dist/services/runners.js +4 -3
- package/apps/runtime/dist/services/runs.js +3 -3
- package/apps/runtime/dist/services/site.js +2 -2
- package/apps/runtime/dist/services/space-categories.js +4 -4
- package/apps/runtime/dist/services/space-data.js +13 -13
- package/apps/runtime/dist/services/space-origin.js +1 -1
- package/apps/runtime/dist/services/spaces.js +7 -7
- package/apps/runtime/dist/services/wizards.js +5 -5
- package/apps/runtime/dist/tools/net-guard.js +135 -24
- package/apps/runtime/dist/tools/plugin.js +1 -1
- package/apps/runtime/dist/tools/store.js +1 -1
- package/apps/runtime/dist/widgets/step.js +1 -1
- package/apps/web/dist/assets/AboutDialog-DaYwk84X.js +1 -0
- package/apps/web/dist/assets/{ChangelogDialog-BXpkgatF.js → ChangelogDialog-CsYbEayb.js} +1 -1
- package/apps/web/dist/assets/{ChatView-t7z44uFN.js → ChatView-BRD3fKZU.js} +1 -1
- package/apps/web/dist/assets/{EditorPage-4qxNJgKr.js → EditorPage-ocPlZD3E.js} +3 -3
- package/apps/web/dist/assets/{FlowPreview-WpS8DCPl.js → FlowPreview-Bm7sv746.js} +1 -1
- package/apps/web/dist/assets/{Harness-DL7Bse40.js → Harness-DKE-YNA5.js} +1 -1
- package/apps/web/dist/assets/{NewWizardChannels-Crt6PzH2.js → NewWizardChannels-CnWckqjO.js} +1 -1
- package/apps/web/dist/assets/{NewWizardPage-COiVmNsl.js → NewWizardPage-BXTpbHVM.js} +1 -1
- package/apps/web/dist/assets/{OnboardingPage-BwluEqXq.js → OnboardingPage-3Rz5uOsa.js} +1 -1
- package/apps/web/dist/assets/{ProjectPage-BIRU_AlN.js → ProjectPage-QdSJwm3k.js} +1 -1
- package/apps/web/dist/assets/{PublicRunner-BxMb4gEt.js → PublicRunner-DEpBer5g.js} +1 -1
- package/apps/web/dist/assets/{ReadOnly-D9r5ze5S.js → ReadOnly-rr_RJD-a.js} +1 -1
- package/apps/web/dist/assets/{RunDrawer-DmJl2QOJ.js → RunDrawer-BsZt0tL9.js} +1 -1
- package/apps/web/dist/assets/{RunnerView-CF_1rC-7.js → RunnerView-BbRukEdE.js} +1 -1
- package/apps/web/dist/assets/SettingsPage-qkWsQIMN.js +1 -0
- package/apps/web/dist/assets/{SetupPage-BGKtbS7j.js → SetupPage-PzvPPLfM.js} +1 -1
- package/apps/web/dist/assets/{SharePage-C4MqXKDA.js → SharePage-7vdDWG1n.js} +1 -1
- package/apps/web/dist/assets/changelog-BFdAYD28.js +1 -0
- package/apps/web/dist/assets/{esm-CsY8TkyE.js → esm-g0kaGp8t.js} +1 -1
- package/apps/web/dist/assets/{index-BjQvGQp_.js → index-BzPid1oQ.js} +4 -4
- package/apps/web/dist/assets/{lock-CI6TpWL-.js → lock-BK9RYIft.js} +1 -1
- package/apps/web/dist/assets/oss-credits-BJrR7BR1.js +1 -0
- package/apps/web/dist/assets/{phone-BrtQz-fG.js → phone-DtkY0NfU.js} +1 -1
- package/apps/web/dist/assets/{settings-menu-BtEKL72n.js → settings-menu-7EJQRFOi.js} +1 -1
- package/apps/web/dist/assets/{ui-4IaTD7LB.js → ui-B6S5hYPd.js} +3 -3
- package/apps/web/dist/assets/{upload-CWJAtPRo.js → upload-rYCAy7ox.js} +1 -1
- package/apps/web/dist/index.html +2 -2
- package/apps/web/dist/mcp/flow.html +3 -3
- package/bin/wizards.mjs +1 -1
- package/node_modules/@engenty-wizards/plugin-sdk/dist/index.d.ts +21 -2
- package/node_modules/@engenty-wizards/shared/dist/projects.js +1 -1
- package/node_modules/@engenty-wizards/shared/dist/run.d.ts +48 -48
- package/node_modules/@engenty-wizards/shared/dist/run.js +48 -48
- package/package.json +5 -4
- package/apps/web/dist/assets/AboutDialog-A_ILyeue.js +0 -1
- package/apps/web/dist/assets/SettingsPage-3xSInE-A.js +0 -1
- package/apps/web/dist/assets/changelog-CM_GdIVI.js +0 -1
- 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
|
+
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"
|
|
12
|
-
messenger
|
|
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
|
-
| `
|
|
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
|
|
34
|
-
| `speech.conversation()` | [Runners](./runners.md#a-call). `{ provider: "openai", apiKey, model }`: the tenant
|
|
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
|
|
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
|
-
| `
|
|
47
|
-
| `
|
|
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
|
|
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
|
|
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
|
|
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 "
|
|
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
|
|
246
|
-
| `studio.registerShareSetup({ runner, title, component, notice? })` | [Runners](./runners.md#setting-up-inside-the-dialog). What
|
|
247
|
-
| `studio.registerAssistantCard({ tool, component })` | Draws a tool
|
|
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
|
|
250
|
-
| `studio.useSpaceMenu(entries)` | A hook in a plugin
|
|
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
|
|
252
|
-
| `studio.Assistant` | Tells the studio
|
|
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
|
|
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
|
|
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
|
|
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
|
|
283
|
-
| `ALL /<id>/…` | The plugin
|
|
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
|
-
|
|
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
|
|
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
|
|
302
|
-
|
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
4
|
-
version: 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
|
|
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
|
|
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
|
|
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
|
|
18
|
-
| A new `.sql` file |
|
|
19
|
-
| An npm dependency of the plugin |
|
|
20
|
-
| The manifest
|
|
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
|
|
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
|
|
37
|
-
its routes answer `404`. Settings → Plugins shows the error. Save
|
|
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
|
|
42
|
+
Settings → Plugins has two buttons for owners and admins of a runtime that runs alone:
|
|
42
43
|
|
|
43
|
-
| Button |
|
|
44
|
+
| Button | What it does |
|
|
44
45
|
|---|---|
|
|
45
|
-
| Reload
|
|
46
|
-
| Look again | Loads every plugin again, finds new 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
|
|
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
|
|
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
|
|
68
|
-
updates
|
|
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
|
|
71
|
-
|
|
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
|
|
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
|
|
4
|
-
version: 0.
|
|
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
|
|
8
|
-
`/api/v1/plugins/<id>`. Every request is a signed-in person
|
|
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
|
|
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
|
|
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
|
|
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
|
|
84
|
-
|
|
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
|
|
92
|
-
person, with the role. A `role: "admin"` route answers a member
|
|
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.
|
|
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
|
|
98
|
-
An id
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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.
|