@openchamber/sdk 1.24.1 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/API.md +7 -3
- package/DOCUMENTATION.md +2 -0
- package/GUEST_SERVICES.md +84 -1
- package/README.md +1 -1
- package/dist/index.d.ts +6 -2
- package/dist/index.js +3 -1
- package/dist/manifest.d.ts +36 -0
- package/dist/manifest.js +20 -0
- package/dist/parse.d.ts +22 -0
- package/dist/parse.js +27 -3
- package/dist/schemas.d.ts +3 -0
- package/dist/schemas.js +2 -0
- package/dist/service-provider-schemas.d.ts +25 -0
- package/dist/service-provider-schemas.js +12 -0
- package/dist/service-providers.d.ts +218 -0
- package/dist/service-providers.js +73 -0
- package/dist/service-surface-schemas.d.ts +173 -0
- package/dist/service-surface-schemas.js +59 -0
- package/dist/service-surface.d.ts +116 -0
- package/dist/service-surface.js +154 -0
- package/dist/workspace.d.ts +1 -0
- package/package.json +1 -1
package/API.md
CHANGED
|
@@ -116,7 +116,7 @@ type GuestSessionItem = {
|
|
|
116
116
|
sessionId: string;
|
|
117
117
|
sessionTitle: string;
|
|
118
118
|
directory: string | null; // the session's project directory
|
|
119
|
-
messages?: Array<{ id: string; role: 'user' | 'assistant'; text: string; createdAt: number }>; // oldest first; only with payload ["messages"] and the conversation grant
|
|
119
|
+
messages?: Array<{ id: string; role: 'user' | 'assistant'; text: string; createdAt: number }>; // oldest first; only with payload ["messages"] and the conversation grant. Same messages the Markdown export writes: the conversation plus context the user attached (`user`); OpenCode's own plumbing messages are left out
|
|
120
120
|
truncated?: boolean; // the oldest messages were dropped so the item stays under 2 000 000 serialized chars
|
|
121
121
|
};
|
|
122
122
|
```
|
|
@@ -199,7 +199,7 @@ Snapshots carry `state: 'loading' | 'ready' | 'error'`; session snapshots also c
|
|
|
199
199
|
|
|
200
200
|
Projects contain `id`, `name`, `directory`. Worktrees contain `directory`, `name`, `branch`, and `status: 'ready' | 'pending' | 'invalid' | 'missing'`. Session records contain `id`, `title`, `projectId`, `directory`, `parentId`, `createdAt`, `updatedAt`, `archivedAt`, `worktree`, `activity`, `outcome`, and `items`. Item references contain only this extension's `id` and optional `data`.
|
|
201
201
|
|
|
202
|
-
`activity` is `unknown`, `idle`, `running`, `retrying`, `waiting-permission`, or `waiting-question
|
|
202
|
+
`activity` is `unknown`, `idle`, `running`, `retrying`, `waiting-permission`, or `waiting-question` (the agent put a form to the user and is waiting on the answer). `outcome` is the last observed `completed` or `failed` turn, or `null` when unknown or working. Outcomes are in memory for the latest 2,000 observed sessions, reset on runtime switch, and are not reconstructed from persisted history. A later idle event preserves an observed failure until another run starts. `completed` never means the extension's task is Done. Blocking-request contents and approve/reply actions are not exposed.
|
|
203
203
|
|
|
204
204
|
### Extension storage
|
|
205
205
|
|
|
@@ -435,7 +435,7 @@ Used by the OpenChamber host and by tools that validate packages. Guests rarely
|
|
|
435
435
|
| `tools` | Optional, 1–16 entries that say how the extension's tool calls look in the chat. `match` is the full tool name OpenCode reports (`mcp.jira.search`, `jira_search`), 1–128 chars of `[A-Za-z0-9_.:-]`, with `*` allowed once at the end as a suffix wildcard (`mcp.jira.*`). Optional `name` (1–40, the header title when `title` is absent or renders empty), `icon` (Remixicon name or package `.svg` path, same rules as `panel.icon`; the SVG is drawn in the text colour at the glyph size), `title` / `subtitle` templates (1–200, `{input.path}` / `{output.path}` / `{metadata.path}` placeholders, a missing path renders empty, values are cut at 200), `output` `"auto"` (default) \| `"text"` \| `"json"` \| `"markdown"` \| `"code"` \| `"table"`, `language` (code only), `columns` (table only, 1–16 dotted paths; rows are the output array or `output.items`). Bad shape is `invalid-tools`. An exact `match` beats a wildcard from any extension; among equals the first extension wins. Only an enabled, fully approved extension's rules apply |
|
|
436
436
|
| `filesystem` | Optional, 1–16 globs, each 1–256 chars, starting with `/` or `~/`; `**` spans folders, `*` / `?` stay in one segment; no `..`, empty segment, or backslash (`invalid-filesystem`). Declaring it adds the `filesystem` capability and the dialog lists the globs |
|
|
437
437
|
| `integration` | Optional. Exactly one of `oauth`, `token`, or `host` (`provider: "linear"` only) |
|
|
438
|
-
| `service` | Optional. `entry` must be a built `.js` file on disk. See [GUEST_SERVICES.md](https://github.com/openchamber/openchamber/blob/main/packages/sdk/GUEST_SERVICES.md)
|
|
438
|
+
| `service` | Optional. `entry` must be a built `.js` file on disk. `provides: ["browser"]` makes it the agent's browser when the user selects it; `surface: true` gives it a host-drawn live panel the user can take over (no `panel.entry` then). Neither needs a panel or background entry. See [GUEST_SERVICES.md](https://github.com/openchamber/openchamber/blob/main/packages/sdk/GUEST_SERVICES.md) |
|
|
439
439
|
|
|
440
440
|
|
|
441
441
|
Extra keys are dropped, not forwarded.
|
|
@@ -495,6 +495,10 @@ Panel → `serviceRequest` → host → `127.0.0.1:port` → service process →
|
|
|
495
495
|
|
|
496
496
|
Manifest sketch: `service.entry` (path to **built** JS, e.g. `service/main.js`), `runtime: "host"`, `permissions.sockets` and/or `permissions.exec`. Install refuses with `missing-build` when that file is absent. Declaring a service adds `service` to the capabilities the user approves at install; until then `serviceRequest` is `NO_SERVICE`.
|
|
497
497
|
|
|
498
|
+
A service with `provides: ["browser"]` answers the agent's `browser.*` actions at `POST /browser-control` instead of the desktop app's browser panel, once the user selects it in Settings → OpenChamber Tools. The host starts it on the first action and stops it when idle. Parse the body with `readBrowserProviderRequest`; request and answer types (`BrowserProviderRequest`, `BrowserProviderResult`, per-action `Browser*Parameters` / `Browser*Data`) and the limits (`BROWSER_PROVIDER_*`) are exported from `@openchamber/sdk`. Full contract in [GUEST_SERVICES.md](https://github.com/openchamber/openchamber/blob/main/packages/sdk/GUEST_SERVICES.md#browser-provider-provides-browser).
|
|
499
|
+
|
|
500
|
+
A service with `surface: true` shows a live picture in the extension's rail panel: the host pulls frames from `GET /surface/frame`, draws them, sends the user's input to `POST /surface/input`, and owns who is in control (nobody, the agent, the user). Paths, event types, and the `readSurface*` parsers are exported from `@openchamber/sdk`; contract in [GUEST_SERVICES.md](https://github.com/openchamber/openchamber/blob/main/packages/sdk/GUEST_SERVICES.md#shared-surface-surface-true).
|
|
501
|
+
|
|
498
502
|
Bundle the service with the Node target:
|
|
499
503
|
|
|
500
504
|
```bash
|
package/DOCUMENTATION.md
CHANGED
|
@@ -16,6 +16,8 @@ Public author pages live in `packages/docs/content/docs/sdk.mdx`, `sdk/host.mdx`
|
|
|
16
16
|
- `scripts/bundle-guest.ts`: official IIFE build, exposed as the `openchamber-guest-bundle` bin. Run `bunx openchamber-guest-bundle panel/main.ts panel/main.js` from the guest folder; paths resolve against the current directory. Needs Bun. Any bundler with an IIFE target is an equivalent substitute.
|
|
17
17
|
- `dist/`: the published output (`bun run --cwd packages/sdk build`, `tsc -p tsconfig.build.json`). `package.json` `exports` point here so Node loads the SDK from `node_modules` (packaged Electron, `openchamber serve`). Inside the monorepo, Vite aliases and tsconfig `paths` resolve `@openchamber/sdk` to `src/` so builds and type-checks never depend on a stale `dist`. The server under Bun and Node uses `dist`; the root `postinstall` builds it. The package ships to npm from the release workflow with `bun publish`, right before `@openchamber/web`; `scripts/bump-version.mjs` bumps it with the app, so the SDK version always equals the OpenChamber version it was released with.
|
|
18
18
|
- `GUEST_SERVICES.md`: local service contract (`contributes.service`, `serviceRequest`, grant, loopback).
|
|
19
|
+
- `src/service-providers.ts`: contracts a service fulfils for a host role (`contributes.service.provides`). Today only `browser`: the ten `browser.*` actions, their parameter and answer types, the limits, `BrowserProviderContext` (the calling project and chat, filled by the host from the tool call; `null` fields mean unknown), and `readBrowserProviderRequest` (a zod-free boundary parse for the service side). `src/service-provider-schemas.ts` is the host's zod parse of the answer, exported from `@openchamber/sdk/schemas`. A providing service needs no panel or background entry (`runtimeContributions` in `parse.ts`). The host side is `packages/web/server/lib/browser-control/provider.js`.
|
|
20
|
+
- `src/service-surface.ts`: the shared-surface contract (`contributes.service.surface`): loopback paths and headers, input event types, limits, and the zod-free `readSurfaceInputBatch` / `readSurfaceControlNotice` / `readSurfaceResizeRequest` for the service side. `src/service-surface-schemas.ts` (host entry) holds the viewer↔host socket messages (`surfaceViewerMessageSchema`, `surfaceHostMessageSchema`) shared by `packages/ui/src/lib/guests/surface-client.ts` and `packages/web/server/lib/guests/surface.js`, plus the service answer schemas. A surface service needs no entry of its own; with `panel.entry` that page is docked to one edge of the picture, `panel.dock` (`GUEST_SURFACE_DOCKS`) and `panel.size` px (`GUEST_SURFACE_DOCK_SIZE_*`), only valid with both. The rail panel is `packages/ui/src/components/layout/GuestSurfacePane.tsx` (host-drawn canvas, not an iframe); `ContextPanel` lays the page iframe (kept mounted) beside the pane (mounted only while shown) per `DOCK_LAYOUT`, and `lib/guests/surfaces.ts#guestSurfaceDocking` decides.
|
|
19
21
|
- `src/ui/`: guest drawing kit. Building blocks only (`mountButton`, `mountTextField`, `mountSearchField`, `mountSelect`, `mountCheckbox`, `mountSwitch`, `mountTabs`, `mountBadge`, `mountList`, `mountEmpty`, `mountSpinner`, `mountBanner`, `mountSeparator`, `mountProgress`, `mountMenu`, `mountText`); the guest composes screens. See `src/ui/DOCUMENTATION.md`. Import `@openchamber/sdk/ui`. Do not import this from `packages/ui`. Kit changes follow package semver, not `apiVersion`.
|
|
20
22
|
|
|
21
23
|
## Invariants
|
package/GUEST_SERVICES.md
CHANGED
|
@@ -88,6 +88,8 @@ Parse rules:
|
|
|
88
88
|
- `contributes.service` is optional. A guest without `service` is HTML-only.
|
|
89
89
|
- `service.entry` is a relative path inside the package. Ship compiled JS; the host never compiles TypeScript.
|
|
90
90
|
- `service.runtime` phase 1 accepts only `"host"`.
|
|
91
|
+
- `service.provides` is optional: roles the service stands in for on the host. Only `"browser"` exists today (below). A service that provides a role needs no `panel.entry` or `background.entry`; the host starts it itself.
|
|
92
|
+
- `service.surface` is optional (`true`): the service shows a live picture the host draws in the extension's rail panel and takes the user's input back (below). With `panel.entry` too, that page is docked to one edge of the picture (`panel.dock`: `top`, `bottom`, `left`, or `right`, default `top`; `panel.size` in CSS px across that edge, 24 to 480, default 40); without it, the picture is the whole panel.
|
|
91
93
|
- `service.permissions.sockets` and `service.permissions.exec` are shown to the user in the approval dialog. They describe intent and do not confine the process: a service runs with the user's full access. Declaring `contributes.service` adds the `service` capability to the package's request list; the user approves the whole list once when the package is installed (Settings → Extensions), and the first `serviceRequest` is refused with `NO_SERVICE` until then.
|
|
92
94
|
- The catalog adds `service.socketBindings`: `{ id, candidates, resolved, override }` for this host. The user can override a path in Extensions. Override empty clears it and the next spawn re-resolves.
|
|
93
95
|
- `contributes.integration` remains valid next to `service`. Cloud `request` and `serviceRequest` may both exist on one guest.
|
|
@@ -107,7 +109,8 @@ Error codes:
|
|
|
107
109
|
|
|
108
110
|
- `NO_SERVICE` — no service declared, not granted, not started, or already torn down
|
|
109
111
|
- `DISABLED` — extension paused in Settings → Extensions (service stopped; tokens/grants stay)
|
|
110
|
-
- `SERVICE_FAILED` — process crashed or never became ready
|
|
112
|
+
- `SERVICE_FAILED` — process crashed or never became ready; nothing was sent to it
|
|
113
|
+
- `REQUEST_FAILED` — the request was sent but got no usable answer (timeout, dropped connection); the service may have acted on it
|
|
111
114
|
- Existing: `HOST_TIMEOUT`, `HOST_REJECTED`, `HOST_UNAVAILABLE`, `BAD_PATH`
|
|
112
115
|
|
|
113
116
|
Server routes (authenticated UI session):
|
|
@@ -164,3 +167,83 @@ Ship `service/main.js` already built. Same packaging rule as `panel/main.js`.
|
|
|
164
167
|
## Example
|
|
165
168
|
|
|
166
169
|
`examples/service-echo` is a checked-in service extension: a Node HTTP server on loopback that the panel calls through `serviceRequest` and whose status it shows. Bundle the service with `--node`, install the folder from Settings → Extensions, allow the local service in the approval dialog, then open the rail panel. Streaming from a service to the panel (shell into a container, log tails) is deferred; it needs a streaming call on the SDK first.
|
|
170
|
+
|
|
171
|
+
## Browser provider (`provides: ["browser"]`)
|
|
172
|
+
|
|
173
|
+
The host's `openchamber_web` tool gives agents ten `browser.*` actions. By default a connected desktop app answers them with its browser panel; on a server with no desktop client the agent gets nothing. A service that declares `provides: ["browser"]` can answer instead, from a browser it runs itself (headless Chrome over CDP, for instance). The agent sees one tool either way.
|
|
174
|
+
|
|
175
|
+
The user picks the provider in Settings → General → OpenChamber Tools → Browser provider. The dropdown lists installed, enabled, fully approved extensions with the role; with none it shows only "OpenChamber Web", disabled. The choice is `browserProvider` in the instance's `settings.json` (`builtin` or the extension id) and is read on every action, so it applies to the next action without a restart. Pausing, removing, or withdrawing approval from the selected extension puts `builtin` back and every open client shows a toast saying so; the same happens on the next action if the extension became unusable any other way.
|
|
176
|
+
|
|
177
|
+
Lifecycle differs from a panel-driven service in two ways. The host starts the service on the first action, so the agent can browse with no panel and no viewer; the grant is the same one the user gave at install. After `BROWSER_PROVIDER_IDLE_MS` (ten minutes) without an action the host stops the process; the next action starts it again. A `browser.open` may take `BROWSER_PROVIDER_OPEN_TIMEOUT_MS` (45 s), every other action `BROWSER_PROVIDER_ACTION_TIMEOUT_MS` (20 s). Answers are read up to `BROWSER_PROVIDER_RESPONSE_MAX` (12 MB), enough for a screenshot.
|
|
178
|
+
|
|
179
|
+
### Wire
|
|
180
|
+
|
|
181
|
+
`POST /browser-control` on the service loopback, same bearer as every request, JSON body:
|
|
182
|
+
|
|
183
|
+
```json
|
|
184
|
+
{
|
|
185
|
+
"requestId": "browser-…",
|
|
186
|
+
"action": "browser.click",
|
|
187
|
+
"parameters": { "selector": "#save" },
|
|
188
|
+
"context": { "directory": "/Users/me/app", "sessionId": "ses_…" }
|
|
189
|
+
}
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
`readBrowserProviderRequest(body)` from `@openchamber/sdk` parses it (`null` → answer HTTP 400). The host validated `parameters` for the action before posting, so the service can trust the shape.
|
|
193
|
+
|
|
194
|
+
`context` says where the action came from: the project the agent works in and the chat it runs in. The host fills it from the tool call; the model never types it. A provider that keeps one browser per project or chat keys its targets on these; one that keeps a single browser ignores them. Either field is `null` when the host had none (an action sent from the CLI, for example); treat that as "unknown", not as a scope of its own. The shared surface is still one per service: if you keep several targets, choose which one the picture shows.
|
|
195
|
+
|
|
196
|
+
Answer HTTP 200 with one of:
|
|
197
|
+
|
|
198
|
+
```json
|
|
199
|
+
{ "ok": true, "data": { … } }
|
|
200
|
+
{ "ok": false, "error": "No element matches #save" }
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
`error` goes to the agent as the tool's error, so write it as what to do differently, not what broke inside. Any other status or shape is reported to the agent as "unknown page state".
|
|
204
|
+
|
|
205
|
+
### Actions
|
|
206
|
+
|
|
207
|
+
Parameters the host sends and `data` the service answers; types are exported from `@openchamber/sdk` (`BrowserOpenParameters`, `BrowserOpenData`, …). Every shape is what the desktop app's own panel answers, so an agent's prompt written against one works against the other.
|
|
208
|
+
|
|
209
|
+
| Action | Parameters | `data` |
|
|
210
|
+
|---|---|---|
|
|
211
|
+
| `browser.open` | `url` (absolute http/https), `viewport?` | `url`, `title`, `opened: true`, `settled` (`false` = still loading, not a failure), `viewport` |
|
|
212
|
+
| `browser.snapshot` | `selector?` (scope) | `url`, `title`, `scope`, `scrollY`, `maxScrollY`, `text`, `elements[]`, `viewport`, optional `textTruncated`/`textTotalChars`, `elementsTruncated`/`interactiveElementsOnPage`, `consoleProblems[]` |
|
|
213
|
+
| `browser.click` | `selector?` or `text?` (visible label) | `clicked` (selector), `label`, `url` |
|
|
214
|
+
| `browser.type` | `selector`, `value`, `submit` | `selector`, `url` |
|
|
215
|
+
| `browser.scroll` | `direction?` (`up`/`down`/`top`/`bottom`) or `selector?` | `scrollY`, `maxScrollY`, `atTop`, `atBottom`, optional `scrolledTo`/`direction` |
|
|
216
|
+
| `browser.back` / `browser.forward` | none | `url`, `title` |
|
|
217
|
+
| `browser.inspect` | `selector` | `selector`, `tag`, `label`, `bounds`, `inViewport`, `styles` (computed, as strings) |
|
|
218
|
+
| `browser.capture` | `label?` | `base64`, `mime`, `width`, `height`, `url`, `title`, `viewport`; the host writes the file into the project and returns its path |
|
|
219
|
+
| `browser.resize` | `viewport` (`mobile`/`tablet`/`desktop`/`fill`) | `viewport` |
|
|
220
|
+
|
|
221
|
+
`viewport` in answers is `{ mode, width, height }` (`mode` may be `custom`; `fill` has `null` sizes). Snapshot `elements` carry `selector`, `tag`, `bounds`, and only the fields that apply (`inViewport`, `type`, `role`, `label`, `disabled`, `missingAccessibleName`). Keep `text` and `elements` bounded yourself; report what was dropped with the truncation fields.
|
|
222
|
+
|
|
223
|
+
`examples/browser-provider-stub` is a checked-in provider with no browser: one in-memory page that answers every action. Install it to see the dropdown, the routing, and the idle stop before writing a real one.
|
|
224
|
+
|
|
225
|
+
## Shared surface (`surface: true`)
|
|
226
|
+
|
|
227
|
+
A surface lets a person watch what an agent is working in and step in for a manual step. It is not about browsers: a service that drives a browser, a simulator, or a desktop app can show one. The host draws it in the extension's rail panel (a canvas; the extension runs no code there), sends the user's pointer, keyboard, and paste back, and decides who is in control.
|
|
228
|
+
|
|
229
|
+
Control has three states. Nobody, the agent (for `SURFACE_AGENT_HOLD_MS`, 30 s, after each action the host ran against this extension, such as a browser provider action), or the user. The user takes control by acting: the first click, wheel, key, or paste while nobody or the agent holds it. Moving the pointer over the picture is looking, not acting; it takes nothing and is not even sent until the user holds control. While the user holds it, the host refuses the browser provider's actions with a message that tells the agent to wait or ask, and the panel shows a "Hand back to agent" button. A second device sees who holds control and cannot take it. Every change is posted to the service so its own automation can pause. The service is kept running while a viewer is attached, and returns to its idle window when the last one leaves.
|
|
230
|
+
|
|
231
|
+
### Wire
|
|
232
|
+
|
|
233
|
+
Plain HTTP on the service loopback, same bearer as everything else:
|
|
234
|
+
|
|
235
|
+
| Call | Body / answer |
|
|
236
|
+
|---|---|
|
|
237
|
+
| `GET /surface/frame?after=<seq>&wait=<ms>` | 200 with `image/jpeg` or `image/png` bytes and headers `x-surface-seq`, `x-surface-width`, `x-surface-height`, optional `x-surface-title`, optional `x-surface-agent-active: 1` while your own automation is driving; 204 when nothing newer than `seq` arrived within `wait` ms. Sequence numbers start at 1, so `after=0` is "the current picture, now". The host asks one frame at a time per viewer; a viewer that draws slowly skips frames rather than queueing them. Frames up to `SURFACE_FRAME_MAX_BYTES` (8 MB). |
|
|
238
|
+
| `POST /surface/input` | `{ events: SurfaceInputEvent[] }`: `pointer` (`down`/`up`/`move`, `x`, `y` in frame pixels, `button`, `buttons`, `modifiers`), `wheel` (`x`, `y`, `deltaX`, `deltaY`, `modifiers`), `key` (`down`/`up`, `key`, `code`, `modifiers`), `text` (pasted or composed text). Parse with `readSurfaceInputBatch`; `null` → 400. Sent only while the user holds control. |
|
|
239
|
+
| `POST /surface/control` | `{ controller: "none" \| "agent" \| "user" }` whenever control changes. Parse with `readSurfaceControlNotice`. Advisory. |
|
|
240
|
+
| `POST /surface/resize` | `{ width, height }` the panel can show, in device pixels. Answer `{ width, height }` you settled on, or 400 to keep your size. Parse with `readSurfaceResizeRequest`. |
|
|
241
|
+
| `GET /surface/clipboard` | `{ text }`: what the user copied inside the surface. The host asks after a copy chord and puts it on the user's clipboard. |
|
|
242
|
+
|
|
243
|
+
Input, control notices, resizes, and clipboard reads reach the service one at a time, in the order the viewer sent them, so a batch never overtakes the one before it. Copy and paste: the host sends `Ctrl/Cmd+C` as a `key` event and then, behind it, reads `/surface/clipboard`; it never sends the paste chord, it sends a `text` event with the pasted text instead. Every other key reaches you as pressed, including the host's own shortcuts, which stand down while the surface has focus.
|
|
244
|
+
|
|
245
|
+
### Your own controls beside the picture
|
|
246
|
+
|
|
247
|
+
Declare `panel.entry` as well and the host docks that page to one edge of the surface: `panel.dock` picks the edge (`top` by default; `bottom` for an inspector, `left` or `right` for a tool column) and `panel.size` its thickness in CSS pixels across that edge (default 40). It is an ordinary sandboxed panel page: it talks to your service through `host.serviceRequest`, gets the theme and the current session like any panel, and stays mounted while the tab is hidden. Put an address field, tabs, a device picker, or a console toggle there; the picture, the input, and who is in control stay with the host. The page and the surface are independent: the page does not see frames, and the host does not route input through it.
|
|
248
|
+
|
|
249
|
+
The types and paths are exported from `@openchamber/sdk` (`SURFACE_*`, `SurfaceInputEvent`, …). `examples/browser-provider-stub` also declares `surface: true` and paints its fake page with rectangles, so the viewer, the hand-off, and the input path can be seen working without a browser; its `panel/` is a one-line address bar docked above the picture.
|
package/README.md
CHANGED
|
@@ -96,7 +96,7 @@ Git installs also accept SSH addresses such as `git@github.com:owner/extension.g
|
|
|
96
96
|
- `tools` is optional: how your tool calls look in the chat, up to 16, no code. `match` is the tool name OpenCode reports (`mcp.tasks.*` matches every tool under that prefix); `name` and `icon` (a Remixicon name or an SVG inside the folder, like `panel.icon`) set the header, `title` and `subtitle` are templates like `{input.id}` or `{output.total} open`, and `output` picks the body: `text`, `json`, `markdown`, `code` (with `language`), or `table` (with `columns`, rows from the output array or `output.items`). Leave `output` out to keep the app's own detection.
|
|
97
97
|
- `capabilities` lists what needs the user's approval: `prompt` to send messages, `sessions` to create sessions and worktrees, `files` to read and write inside the open project, `model` for one-off text generation with the user's Small Model (`host.generate`, no session involved). An `integration` adds `network`, a `service` adds `service`, `filesystem` patterns (like `["~/.config/opencode/opencode.json"]`) add `filesystem`, which lets `readFile`, `writeFile`, `listDir`, and `stat` reach those paths outside the project, and a session action with `payload: ["messages"]` adds `conversation`. The user approves the whole list once at install. Calls outside it fail with `NOT_GRANTED`.
|
|
98
98
|
- `integration` is optional. It adds a card at Settings → Integrations. `token` takes a pasted API token (`scheme: "bearer"` for `Authorization: Bearer`, `"basic"` for a username and token pair as Jira Cloud wants), `oauth` runs an authorize flow with a pasted client id, and `host: { "provider": "linear" }` reuses the Linear account already connected in OpenChamber. The page never sees the token; OpenChamber makes the calls through `host.request`.
|
|
99
|
-
- `service` is optional. It declares a local process OpenChamber starts next to the extension. It runs with the user's full access and no sandbox, so declare one only when the page cannot do the job. See [GUEST_SERVICES.md](./GUEST_SERVICES.md).
|
|
99
|
+
- `service` is optional. It declares a local process OpenChamber starts next to the extension. It runs with the user's full access and no sandbox, so declare one only when the page cannot do the job. See [GUEST_SERVICES.md](./GUEST_SERVICES.md). With `provides: ["browser"]` the service can stand in for the agent's browser, so agents browse on the server with no desktop app open; the user picks it in Settings → OpenChamber Tools. With `surface: true` it shows a live picture in the rail that the user can watch and take over, and hand back to the agent; add `panel.entry` (with `panel.dock` and `panel.size`) for your own controls docked beside it.
|
|
100
100
|
|
|
101
101
|
## Actions without opening a panel
|
|
102
102
|
|
package/dist/index.d.ts
CHANGED
|
@@ -4,10 +4,14 @@ export type { GuestLoadState, GuestProject, GuestWorktree, GuestSessionActivity,
|
|
|
4
4
|
export { GUEST_STORAGE_KEY_MAX, GUEST_STORAGE_KEYS_MAX, GUEST_STORAGE_VALUE_BYTES, GUEST_STORAGE_TOTAL_BYTES } from './workspace.ts';
|
|
5
5
|
export type { OpenChamberManifestApiVersion } from './api-version.ts';
|
|
6
6
|
export { compareOpenChamberVersions, hostMeetsOpenChamberEngine, openChamberEngineMinimum, OPENCHAMBER_ENGINE_PATTERN, parseOpenChamberVersion, } from './host-version.ts';
|
|
7
|
-
export { DECLARED_GUEST_CAPABILITIES, GUEST_ACTIONS_MAX, GUEST_ACTION_LABEL_MAX, GUEST_CAPABILITIES, GUEST_COMMANDS_MAX, GUEST_COMMAND_DESCRIPTION_MAX, GUEST_COMMAND_NAME, GUEST_TOOLS_MAX, GUEST_TOOL_COLUMNS_MAX, GUEST_TOOL_COLUMN_MAX, GUEST_TOOL_LANGUAGE_MAX, GUEST_TOOL_MATCH, GUEST_TOOL_MATCH_MAX, GUEST_TOOL_NAME_MAX, GUEST_TOOL_OUTPUTS, GUEST_TOOL_TEMPLATE_MAX, GUEST_TOOL_TEMPLATE_VALUE_MAX, guestActionsNeedConversation, GUEST_FILESYSTEM_PATTERN_MAX, GUEST_FILESYSTEM_PATTERNS_MAX, HOST_LINEAR_API_ORIGIN, isGuestFilesystemPattern, hasGuestCapability, hasGuestPage, isGuestApproved, isGuestPackageSvgIcon, requestedGuestCapabilities, resolveAttachEntry, resolvePageEntry, resolveAttachMode, resolveIntegrationApi, resolveIntegrationAuth, toPublicService, toPublicIntegration, } from './manifest.ts';
|
|
8
|
-
export type { ServiceContribution, ServicePermissions, GuestActionContribution, GuestActionPayload, GuestActionRole, GuestActionWhere, GuestCommandContribution, GuestToolContribution, GuestToolOutput, AttachContribution, AttachContributionObject, AttachMode, DeclaredGuestCapability, GuestAuthorization, GuestCapability, IntegrationAuth, IntegrationContribution, IntegrationHost, IntegrationHostProvider, IntegrationOAuth, IntegrationOAuthAccount, IntegrationSettingField, IntegrationToken, IntegrationTokenScheme, OpenChamberContributes, OpenChamberEngines, OpenChamberManifest, PanelContribution, BackgroundContribution, PageContribution, ParseManifestErrorCode, ParseManifestFailure, ParseManifestResult, ParseManifestSuccess, PublicService, PublicServicePermissions, PublicGuestCapabilities, PublicIntegration, PublicIntegrationToken, PublicSocketBinding, ResolvedGuestApi, SocketBinding, SocketPlatform, } from './manifest.ts';
|
|
7
|
+
export { DECLARED_GUEST_CAPABILITIES, GUEST_ACTIONS_MAX, GUEST_ACTION_LABEL_MAX, GUEST_CAPABILITIES, GUEST_COMMANDS_MAX, GUEST_COMMAND_DESCRIPTION_MAX, GUEST_COMMAND_NAME, GUEST_TOOLS_MAX, GUEST_TOOL_COLUMNS_MAX, GUEST_TOOL_COLUMN_MAX, GUEST_TOOL_LANGUAGE_MAX, GUEST_TOOL_MATCH, GUEST_TOOL_MATCH_MAX, GUEST_TOOL_NAME_MAX, GUEST_TOOL_OUTPUTS, GUEST_TOOL_TEMPLATE_MAX, GUEST_TOOL_TEMPLATE_VALUE_MAX, guestActionsNeedConversation, GUEST_FILESYSTEM_PATTERN_MAX, GUEST_FILESYSTEM_PATTERNS_MAX, GUEST_SERVICE_PROVIDES, GUEST_SURFACE_DOCKS, GUEST_SURFACE_DOCK_DEFAULT, GUEST_SURFACE_DOCK_SIZE_DEFAULT, GUEST_SURFACE_DOCK_SIZE_MAX, GUEST_SURFACE_DOCK_SIZE_MIN, HOST_LINEAR_API_ORIGIN, isGuestFilesystemPattern, hasGuestCapability, hasGuestPage, isGuestApproved, isGuestPackageSvgIcon, requestedGuestCapabilities, resolveAttachEntry, resolvePageEntry, resolveAttachMode, resolveIntegrationApi, resolveIntegrationAuth, serviceProvides, toPublicService, toPublicIntegration, } from './manifest.ts';
|
|
8
|
+
export type { GuestServiceProvides, GuestSurfaceDock, ServiceContribution, ServicePermissions, GuestActionContribution, GuestActionPayload, GuestActionRole, GuestActionWhere, GuestCommandContribution, GuestToolContribution, GuestToolOutput, AttachContribution, AttachContributionObject, AttachMode, DeclaredGuestCapability, GuestAuthorization, GuestCapability, IntegrationAuth, IntegrationContribution, IntegrationHost, IntegrationHostProvider, IntegrationOAuth, IntegrationOAuthAccount, IntegrationSettingField, IntegrationToken, IntegrationTokenScheme, OpenChamberContributes, OpenChamberEngines, OpenChamberManifest, PanelContribution, BackgroundContribution, PageContribution, ParseManifestErrorCode, ParseManifestFailure, ParseManifestResult, ParseManifestSuccess, PublicService, PublicServicePermissions, PublicGuestCapabilities, PublicIntegration, PublicIntegrationToken, PublicSocketBinding, ResolvedGuestApi, SocketBinding, SocketPlatform, } from './manifest.ts';
|
|
9
9
|
export { connectHost, HostRequestError } from './host.ts';
|
|
10
10
|
export type { HostClient, HostClientOptions, HostFrame } from './host.ts';
|
|
11
11
|
export { clampAttachRequest, clampBadgeCount, clampPromptRequest, clampStartSessionRequest, guestFileScope, isAttachData, isJsonValue, isFileListResult, isFileReadResult, isFileStatResult, isGenerateResult, isFileWriteResult, isGuestFilePath, isGuestAttachItem, isGuestMessageItem, isGuestSessionItem, isServiceStatusResult, isGuestRequestPath, isGuestRequestResult, isHostRequestErrorCode, isPromptResult, isStartSessionResult, readHostMessage, resolveHostRequestErrorCode, } from './contract.ts';
|
|
12
12
|
export type { BadgeRequest, ActionResultPayload, GuestActionItem, GuestActionResultMessage, HostActionMessage, GuestBadgeMessage, GuestItem, GuestItemRole, GuestMessageItem, GuestResolveResultMessage, GuestSessionItem, GuestSessionItemMessage, HostResolveMessage, ResolveRequest, ResolveResultPayload, FileListEntry, FileListRequest, FileListResult, FileReadRequest, FileReadResult, FileStatRequest, FileStatResult, FileWriteRequest, FileWriteResult, GenerateRequest, GenerateResult, GuestFileEntryKind, GuestFileListMessage, GuestFileReadMessage, GuestFileScope, GuestFileStatKind, GuestFileStatMessage, GuestGenerateMessage, GuestFileWriteMessage, ServiceStatus, ServiceStatusResult, HostRequestErrorCode, AttachBranches, AttachIssueRequest, AttachThreadKind, JsonValue, HostResultPayload, PromptRequest, PromptResult, SessionLifecycleEvent, SessionLifecyclePhase, StartSessionRequest, StartSessionResult, StartSessionSent, ComposeRequest, GuestServiceRequestMessage, GuestServiceStatusMessage, GuestAttachMessage, GuestClipboardWriteMessage, GuestCloseMessage, GuestComposeMessage, GuestConnection, GuestHelloMessage, GuestMessage, GuestOauthDisconnectMessage, GuestOauthStartMessage, GuestOpenSurfaceMessage, GuestOpenUrlMessage, GuestRequest, GuestRequestMessage, GuestRequestMethod, GuestPromptMessage, GuestSessionLinkMessage, GuestStartSessionMessage, GuestRequestResult, GuestSettings, GuestToastMessage, GuestHostSurface, HostConnectionMessage, HostDirectoryMessage, HostItemMessage, HostMessage, HostReadyContext, HostReadyMessage, HostResultMessage, HostSessionLifecycleMessage, HostSessionMessage, HostSettingsMessage, HostTheme, HostThemeMode, HostThemeTokens, SessionSnapshot, ToastKind, ToastRequest, } from './contract.ts';
|
|
13
13
|
export { EMPTY_GUEST_CONNECTION, GUEST_BADGE_MAX, GUEST_ITEM_MESSAGE_TEXT_MAX, GUEST_ITEM_SESSION_MAX, GUEST_RESOLVE_ERROR_MAX, GUEST_FILE_CONTENT_MAX, GUEST_FILE_ENTRY_KINDS, GUEST_FILE_LIST_MAX, GUEST_FILE_PATH_MAX, GUEST_GENERATE_OUTPUT_TOKENS_MAX, GUEST_GENERATE_PROMPT_MAX, GUEST_GENERATE_SYSTEM_MAX, GUEST_GENERATE_TEXT_MAX, GUEST_GENERATE_TIMEOUT_MS, GUEST_FILE_STAT_KINDS, SERVICE_STATUS_VALUES, HOST_REQUEST_ERROR_CODES, SESSION_LIFECYCLE_PHASES, START_SESSION_SENT, GUEST_ACCOUNT_MAX, GUEST_SESSION_AGENT_MAX, GUEST_SESSION_MODEL_MAX, GUEST_ATTACH_AUTHOR_MAX, GUEST_ATTACH_BRANCH_MAX, GUEST_ATTACH_DATA_MAX, GUEST_ATTACH_ID_MAX, GUEST_ATTACH_TEXT_MAX, GUEST_ATTACH_TITLE_MAX, GUEST_ATTACH_URL_MAX, GUEST_CLIPBOARD_TEXT_MAX, GUEST_TOAST_MAX, GUEST_COMPOSE_TEXT_MAX, GUEST_REQUEST_BODY_MAX, GUEST_REQUEST_PATH_MAX, GUEST_REQUEST_RESPONSE_MAX, GUEST_REQUEST_TIMEOUT_MS, GUEST_SETTING_VALUE_MAX, } from './contract.ts';
|
|
14
|
+
export { BROWSER_CONTROL_ACTIONS, BROWSER_PROVIDER_ACTION_TIMEOUT_MS, BROWSER_PROVIDER_IDLE_MS, BROWSER_PROVIDER_OPEN_TIMEOUT_MS, BROWSER_PROVIDER_PATH, BROWSER_PROVIDER_RESPONSE_MAX, BROWSER_SCROLL_DIRECTIONS, BROWSER_VIEWPORT_MODES, isBrowserControlAction, readBrowserProviderRequest, } from './service-providers.ts';
|
|
15
|
+
export type { BrowserCaptureData, BrowserCaptureParameters, BrowserClickData, BrowserClickParameters, BrowserConsoleProblem, BrowserControlAction, BrowserElementBounds, BrowserInspectData, BrowserInspectParameters, BrowserNavigationData, BrowserOpenData, BrowserOpenParameters, BrowserProviderContext, BrowserProviderData, BrowserProviderRequest, BrowserProviderResult, BrowserResizeData, BrowserResizeParameters, BrowserScrollData, BrowserScrollDirection, BrowserScrollParameters, BrowserSnapshotData, BrowserSnapshotElement, BrowserSnapshotParameters, BrowserTypeData, BrowserTypeParameters, BrowserViewportMode, BrowserViewportSummary, } from './service-providers.ts';
|
|
16
|
+
export { SURFACE_AGENT_ACTIVE_HEADER, SURFACE_AGENT_HOLD_MS, SURFACE_CLIPBOARD_PATH, SURFACE_CONTROL_PATH, SURFACE_CONTROLLERS, SURFACE_DIMENSION_MAX, SURFACE_FRAME_MAX_BYTES, SURFACE_FRAME_MIMES, SURFACE_FRAME_PATH, SURFACE_FRAME_WAIT_MS, SURFACE_HEIGHT_HEADER, SURFACE_INPUT_BATCH_MAX, SURFACE_INPUT_PATH, SURFACE_RESIZE_PATH, SURFACE_SEQ_HEADER, SURFACE_TEXT_MAX, SURFACE_TITLE_HEADER, SURFACE_TITLE_MAX, SURFACE_WIDTH_HEADER, readSurfaceControlNotice, readSurfaceInputBatch, readSurfaceResizeRequest, } from './service-surface.ts';
|
|
17
|
+
export type { SurfaceClipboardAnswer, SurfaceControlNotice, SurfaceController, SurfaceFrameMime, SurfaceInputBatch, SurfaceInputEvent, SurfaceKeyEvent, SurfaceModifiers, SurfacePointerEvent, SurfaceResizeAnswer, SurfaceResizeRequest, SurfaceTextEvent, SurfaceWheelEvent, } from './service-surface.ts';
|
package/dist/index.js
CHANGED
|
@@ -2,7 +2,9 @@ export { OPENCHAMBER_SDK_API_VERSION, OPENCHAMBER_SDK_CHANNEL, OPENCHAMBER_SDK_M
|
|
|
2
2
|
export { GUEST_SCROLLBAR_CSS } from "./scrollbar-style.js";
|
|
3
3
|
export { GUEST_STORAGE_KEY_MAX, GUEST_STORAGE_KEYS_MAX, GUEST_STORAGE_VALUE_BYTES, GUEST_STORAGE_TOTAL_BYTES } from "./workspace.js";
|
|
4
4
|
export { compareOpenChamberVersions, hostMeetsOpenChamberEngine, openChamberEngineMinimum, OPENCHAMBER_ENGINE_PATTERN, parseOpenChamberVersion, } from "./host-version.js";
|
|
5
|
-
export { DECLARED_GUEST_CAPABILITIES, GUEST_ACTIONS_MAX, GUEST_ACTION_LABEL_MAX, GUEST_CAPABILITIES, GUEST_COMMANDS_MAX, GUEST_COMMAND_DESCRIPTION_MAX, GUEST_COMMAND_NAME, GUEST_TOOLS_MAX, GUEST_TOOL_COLUMNS_MAX, GUEST_TOOL_COLUMN_MAX, GUEST_TOOL_LANGUAGE_MAX, GUEST_TOOL_MATCH, GUEST_TOOL_MATCH_MAX, GUEST_TOOL_NAME_MAX, GUEST_TOOL_OUTPUTS, GUEST_TOOL_TEMPLATE_MAX, GUEST_TOOL_TEMPLATE_VALUE_MAX, guestActionsNeedConversation, GUEST_FILESYSTEM_PATTERN_MAX, GUEST_FILESYSTEM_PATTERNS_MAX, HOST_LINEAR_API_ORIGIN, isGuestFilesystemPattern, hasGuestCapability, hasGuestPage, isGuestApproved, isGuestPackageSvgIcon, requestedGuestCapabilities, resolveAttachEntry, resolvePageEntry, resolveAttachMode, resolveIntegrationApi, resolveIntegrationAuth, toPublicService, toPublicIntegration, } from "./manifest.js";
|
|
5
|
+
export { DECLARED_GUEST_CAPABILITIES, GUEST_ACTIONS_MAX, GUEST_ACTION_LABEL_MAX, GUEST_CAPABILITIES, GUEST_COMMANDS_MAX, GUEST_COMMAND_DESCRIPTION_MAX, GUEST_COMMAND_NAME, GUEST_TOOLS_MAX, GUEST_TOOL_COLUMNS_MAX, GUEST_TOOL_COLUMN_MAX, GUEST_TOOL_LANGUAGE_MAX, GUEST_TOOL_MATCH, GUEST_TOOL_MATCH_MAX, GUEST_TOOL_NAME_MAX, GUEST_TOOL_OUTPUTS, GUEST_TOOL_TEMPLATE_MAX, GUEST_TOOL_TEMPLATE_VALUE_MAX, guestActionsNeedConversation, GUEST_FILESYSTEM_PATTERN_MAX, GUEST_FILESYSTEM_PATTERNS_MAX, GUEST_SERVICE_PROVIDES, GUEST_SURFACE_DOCKS, GUEST_SURFACE_DOCK_DEFAULT, GUEST_SURFACE_DOCK_SIZE_DEFAULT, GUEST_SURFACE_DOCK_SIZE_MAX, GUEST_SURFACE_DOCK_SIZE_MIN, HOST_LINEAR_API_ORIGIN, isGuestFilesystemPattern, hasGuestCapability, hasGuestPage, isGuestApproved, isGuestPackageSvgIcon, requestedGuestCapabilities, resolveAttachEntry, resolvePageEntry, resolveAttachMode, resolveIntegrationApi, resolveIntegrationAuth, serviceProvides, toPublicService, toPublicIntegration, } from "./manifest.js";
|
|
6
6
|
export { connectHost, HostRequestError } from "./host.js";
|
|
7
7
|
export { clampAttachRequest, clampBadgeCount, clampPromptRequest, clampStartSessionRequest, guestFileScope, isAttachData, isJsonValue, isFileListResult, isFileReadResult, isFileStatResult, isGenerateResult, isFileWriteResult, isGuestFilePath, isGuestAttachItem, isGuestMessageItem, isGuestSessionItem, isServiceStatusResult, isGuestRequestPath, isGuestRequestResult, isHostRequestErrorCode, isPromptResult, isStartSessionResult, readHostMessage, resolveHostRequestErrorCode, } from "./contract.js";
|
|
8
8
|
export { EMPTY_GUEST_CONNECTION, GUEST_BADGE_MAX, GUEST_ITEM_MESSAGE_TEXT_MAX, GUEST_ITEM_SESSION_MAX, GUEST_RESOLVE_ERROR_MAX, GUEST_FILE_CONTENT_MAX, GUEST_FILE_ENTRY_KINDS, GUEST_FILE_LIST_MAX, GUEST_FILE_PATH_MAX, GUEST_GENERATE_OUTPUT_TOKENS_MAX, GUEST_GENERATE_PROMPT_MAX, GUEST_GENERATE_SYSTEM_MAX, GUEST_GENERATE_TEXT_MAX, GUEST_GENERATE_TIMEOUT_MS, GUEST_FILE_STAT_KINDS, SERVICE_STATUS_VALUES, HOST_REQUEST_ERROR_CODES, SESSION_LIFECYCLE_PHASES, START_SESSION_SENT, GUEST_ACCOUNT_MAX, GUEST_SESSION_AGENT_MAX, GUEST_SESSION_MODEL_MAX, GUEST_ATTACH_AUTHOR_MAX, GUEST_ATTACH_BRANCH_MAX, GUEST_ATTACH_DATA_MAX, GUEST_ATTACH_ID_MAX, GUEST_ATTACH_TEXT_MAX, GUEST_ATTACH_TITLE_MAX, GUEST_ATTACH_URL_MAX, GUEST_CLIPBOARD_TEXT_MAX, GUEST_TOAST_MAX, GUEST_COMPOSE_TEXT_MAX, GUEST_REQUEST_BODY_MAX, GUEST_REQUEST_PATH_MAX, GUEST_REQUEST_RESPONSE_MAX, GUEST_REQUEST_TIMEOUT_MS, GUEST_SETTING_VALUE_MAX, } from "./contract.js";
|
|
9
|
+
export { BROWSER_CONTROL_ACTIONS, BROWSER_PROVIDER_ACTION_TIMEOUT_MS, BROWSER_PROVIDER_IDLE_MS, BROWSER_PROVIDER_OPEN_TIMEOUT_MS, BROWSER_PROVIDER_PATH, BROWSER_PROVIDER_RESPONSE_MAX, BROWSER_SCROLL_DIRECTIONS, BROWSER_VIEWPORT_MODES, isBrowserControlAction, readBrowserProviderRequest, } from "./service-providers.js";
|
|
10
|
+
export { SURFACE_AGENT_ACTIVE_HEADER, SURFACE_AGENT_HOLD_MS, SURFACE_CLIPBOARD_PATH, SURFACE_CONTROL_PATH, SURFACE_CONTROLLERS, SURFACE_DIMENSION_MAX, SURFACE_FRAME_MAX_BYTES, SURFACE_FRAME_MIMES, SURFACE_FRAME_PATH, SURFACE_FRAME_WAIT_MS, SURFACE_HEIGHT_HEADER, SURFACE_INPUT_BATCH_MAX, SURFACE_INPUT_PATH, SURFACE_RESIZE_PATH, SURFACE_SEQ_HEADER, SURFACE_TEXT_MAX, SURFACE_TITLE_HEADER, SURFACE_TITLE_MAX, SURFACE_WIDTH_HEADER, readSurfaceControlNotice, readSurfaceInputBatch, readSurfaceResizeRequest, } from "./service-surface.js";
|
package/dist/manifest.d.ts
CHANGED
|
@@ -10,7 +10,24 @@ export type PanelContribution = {
|
|
|
10
10
|
name: string;
|
|
11
11
|
icon: string;
|
|
12
12
|
entry?: string;
|
|
13
|
+
/**
|
|
14
|
+
* Beside a shared surface (`service.surface`), the panel page is docked to
|
|
15
|
+
* one edge of the host-drawn picture: a toolbar above it, an inspector
|
|
16
|
+
* below, a tool column beside it. `dock` is the edge (default
|
|
17
|
+
* `GUEST_SURFACE_DOCK_DEFAULT`) and `size` the page's thickness in CSS
|
|
18
|
+
* pixels across that edge (default `GUEST_SURFACE_DOCK_SIZE_DEFAULT`).
|
|
19
|
+
* Both only make sense with `entry` and `service.surface` together.
|
|
20
|
+
*/
|
|
21
|
+
dock?: GuestSurfaceDock;
|
|
22
|
+
size?: number;
|
|
13
23
|
};
|
|
24
|
+
export declare const GUEST_SURFACE_DOCKS: readonly ["top", "bottom", "left", "right"];
|
|
25
|
+
export type GuestSurfaceDock = (typeof GUEST_SURFACE_DOCKS)[number];
|
|
26
|
+
export declare const GUEST_SURFACE_DOCK_DEFAULT: GuestSurfaceDock;
|
|
27
|
+
/** Thickness in CSS px of a docked `panel.entry` when the manifest names none. */
|
|
28
|
+
export declare const GUEST_SURFACE_DOCK_SIZE_DEFAULT = 40;
|
|
29
|
+
export declare const GUEST_SURFACE_DOCK_SIZE_MIN = 24;
|
|
30
|
+
export declare const GUEST_SURFACE_DOCK_SIZE_MAX = 480;
|
|
14
31
|
/** Sandboxed HTML loaded on demand for background actions and slash commands. */
|
|
15
32
|
export type BackgroundContribution = {
|
|
16
33
|
entry: string;
|
|
@@ -199,18 +216,37 @@ export type PublicSocketBinding = {
|
|
|
199
216
|
resolved: string | null;
|
|
200
217
|
override: string | null;
|
|
201
218
|
};
|
|
219
|
+
/**
|
|
220
|
+
* Host roles a service can stand in for. `browser` answers the agent's
|
|
221
|
+
* `browser.*` actions in place of the in-app browser view; the contract is in
|
|
222
|
+
* `service-providers.ts`. A service that provides a role needs no panel or
|
|
223
|
+
* background entry: the host starts it on the first action.
|
|
224
|
+
*/
|
|
225
|
+
export declare const GUEST_SERVICE_PROVIDES: readonly ["browser"];
|
|
226
|
+
export type GuestServiceProvides = (typeof GUEST_SERVICE_PROVIDES)[number];
|
|
202
227
|
export type ServiceContribution = {
|
|
203
228
|
entry: string;
|
|
204
229
|
runtime: 'host';
|
|
205
230
|
permissions?: ServicePermissions;
|
|
231
|
+
provides?: GuestServiceProvides[];
|
|
232
|
+
/**
|
|
233
|
+
* The service shows a live surface (frames out, input in) that the host
|
|
234
|
+
* draws in this extension's rail panel; see `service-surface.ts`. With
|
|
235
|
+
* `panel.entry` too, that page is docked to one edge of the picture (see
|
|
236
|
+
* `PanelContribution.dock`); without it, the panel is the surface alone.
|
|
237
|
+
*/
|
|
238
|
+
surface?: true;
|
|
206
239
|
};
|
|
207
240
|
/** Catalog card for a local service. Drops nothing secret; grant is host state. */
|
|
208
241
|
export type PublicService = {
|
|
209
242
|
runtime: 'host';
|
|
210
243
|
permissions?: PublicServicePermissions;
|
|
211
244
|
socketBindings?: PublicSocketBinding[];
|
|
245
|
+
provides?: GuestServiceProvides[];
|
|
246
|
+
surface?: true;
|
|
212
247
|
granted: boolean;
|
|
213
248
|
};
|
|
249
|
+
export declare const serviceProvides: (service: Pick<ServiceContribution, "provides"> | undefined, role: GuestServiceProvides) => boolean;
|
|
214
250
|
/**
|
|
215
251
|
* What a guest may do beyond drawing its own panel. The user approves the
|
|
216
252
|
* full list once, when the package is installed; a later package that asks
|
package/dist/manifest.js
CHANGED
|
@@ -1,4 +1,10 @@
|
|
|
1
1
|
export const PANEL_ID = /^[a-z][a-z0-9-]*$/;
|
|
2
|
+
export const GUEST_SURFACE_DOCKS = ['top', 'bottom', 'left', 'right'];
|
|
3
|
+
export const GUEST_SURFACE_DOCK_DEFAULT = 'top';
|
|
4
|
+
/** Thickness in CSS px of a docked `panel.entry` when the manifest names none. */
|
|
5
|
+
export const GUEST_SURFACE_DOCK_SIZE_DEFAULT = 40;
|
|
6
|
+
export const GUEST_SURFACE_DOCK_SIZE_MIN = 24;
|
|
7
|
+
export const GUEST_SURFACE_DOCK_SIZE_MAX = 480;
|
|
2
8
|
/** How many `contributes.actions` entries a package may declare. */
|
|
3
9
|
export const GUEST_ACTIONS_MAX = 8;
|
|
4
10
|
/** Characters in an action label. */
|
|
@@ -71,6 +77,14 @@ export const resolveIntegrationApi = (integration) => {
|
|
|
71
77
|
}
|
|
72
78
|
return null;
|
|
73
79
|
};
|
|
80
|
+
/**
|
|
81
|
+
* Host roles a service can stand in for. `browser` answers the agent's
|
|
82
|
+
* `browser.*` actions in place of the in-app browser view; the contract is in
|
|
83
|
+
* `service-providers.ts`. A service that provides a role needs no panel or
|
|
84
|
+
* background entry: the host starts it on the first action.
|
|
85
|
+
*/
|
|
86
|
+
export const GUEST_SERVICE_PROVIDES = ['browser'];
|
|
87
|
+
export const serviceProvides = (service, role) => Boolean(service?.provides?.includes(role));
|
|
74
88
|
/**
|
|
75
89
|
* What a guest may do beyond drawing its own panel. The user approves the
|
|
76
90
|
* full list once, when the package is installed; a later package that asks
|
|
@@ -217,6 +231,12 @@ export const toPublicService = (service, granted, socketBindings) => {
|
|
|
217
231
|
override: binding.override,
|
|
218
232
|
}));
|
|
219
233
|
}
|
|
234
|
+
if (service.provides && service.provides.length > 0) {
|
|
235
|
+
next.provides = [...service.provides];
|
|
236
|
+
}
|
|
237
|
+
if (service.surface) {
|
|
238
|
+
next.surface = true;
|
|
239
|
+
}
|
|
220
240
|
return next;
|
|
221
241
|
};
|
|
222
242
|
/** Guest package version: `1.2.3`, optional prerelease / build. */
|
package/dist/parse.d.ts
CHANGED
|
@@ -11,6 +11,13 @@ export declare const openChamberManifestSchema: z.ZodObject<{
|
|
|
11
11
|
name: z.ZodString;
|
|
12
12
|
icon: z.ZodString;
|
|
13
13
|
entry: z.ZodOptional<z.ZodString>;
|
|
14
|
+
dock: z.ZodOptional<z.ZodEnum<{
|
|
15
|
+
top: "top";
|
|
16
|
+
bottom: "bottom";
|
|
17
|
+
left: "left";
|
|
18
|
+
right: "right";
|
|
19
|
+
}>>;
|
|
20
|
+
size: z.ZodOptional<z.ZodNumber>;
|
|
14
21
|
}, z.core.$strip>;
|
|
15
22
|
background: z.ZodOptional<z.ZodObject<{
|
|
16
23
|
entry: z.ZodString;
|
|
@@ -97,6 +104,10 @@ export declare const openChamberManifestSchema: z.ZodObject<{
|
|
|
97
104
|
sockets?: SocketBinding[] | undefined;
|
|
98
105
|
exec?: string[] | undefined;
|
|
99
106
|
}>>>;
|
|
107
|
+
provides: z.ZodOptional<z.ZodArray<z.ZodEnum<{
|
|
108
|
+
browser: "browser";
|
|
109
|
+
}>>>;
|
|
110
|
+
surface: z.ZodOptional<z.ZodLiteral<true>>;
|
|
100
111
|
}, z.core.$strip>>;
|
|
101
112
|
filesystem: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
102
113
|
actions: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
@@ -156,6 +167,13 @@ export declare const packageManifestSchema: z.ZodObject<{
|
|
|
156
167
|
name: z.ZodString;
|
|
157
168
|
icon: z.ZodString;
|
|
158
169
|
entry: z.ZodOptional<z.ZodString>;
|
|
170
|
+
dock: z.ZodOptional<z.ZodEnum<{
|
|
171
|
+
top: "top";
|
|
172
|
+
bottom: "bottom";
|
|
173
|
+
left: "left";
|
|
174
|
+
right: "right";
|
|
175
|
+
}>>;
|
|
176
|
+
size: z.ZodOptional<z.ZodNumber>;
|
|
159
177
|
}, z.core.$strip>;
|
|
160
178
|
background: z.ZodOptional<z.ZodObject<{
|
|
161
179
|
entry: z.ZodString;
|
|
@@ -242,6 +260,10 @@ export declare const packageManifestSchema: z.ZodObject<{
|
|
|
242
260
|
sockets?: SocketBinding[] | undefined;
|
|
243
261
|
exec?: string[] | undefined;
|
|
244
262
|
}>>>;
|
|
263
|
+
provides: z.ZodOptional<z.ZodArray<z.ZodEnum<{
|
|
264
|
+
browser: "browser";
|
|
265
|
+
}>>>;
|
|
266
|
+
surface: z.ZodOptional<z.ZodLiteral<true>>;
|
|
245
267
|
}, z.core.$strip>>;
|
|
246
268
|
filesystem: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
247
269
|
actions: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
package/dist/parse.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
2
|
import { OPENCHAMBER_SDK_MANIFEST_API_VERSIONS } from "./api-version.js";
|
|
3
3
|
import { OPENCHAMBER_ENGINE_PATTERN } from "./host-version.js";
|
|
4
|
-
import { DECLARED_GUEST_CAPABILITIES, GUEST_ACTIONS_MAX, GUEST_ACTION_LABEL_MAX, GUEST_COMMANDS_MAX, GUEST_COMMAND_DESCRIPTION_MAX, GUEST_COMMAND_NAME, GUEST_FILESYSTEM_PATTERNS_MAX, GUEST_FILESYSTEM_PATTERN_MAX, GUEST_TOOLS_MAX, GUEST_TOOL_COLUMNS_MAX, GUEST_TOOL_COLUMN_MAX, GUEST_TOOL_LANGUAGE_MAX, GUEST_TOOL_MATCH, GUEST_TOOL_MATCH_MAX, GUEST_TOOL_NAME_MAX, GUEST_TOOL_OUTPUTS, GUEST_TOOL_TEMPLATE_MAX, PANEL_ID, hasGuestPage, isGuestFilesystemPattern, isGuestPackageSvgIcon, isSafeAssetPath, resolveIntegrationAuth, } from "./manifest.js";
|
|
4
|
+
import { DECLARED_GUEST_CAPABILITIES, GUEST_ACTIONS_MAX, GUEST_ACTION_LABEL_MAX, GUEST_COMMANDS_MAX, GUEST_COMMAND_DESCRIPTION_MAX, GUEST_COMMAND_NAME, GUEST_FILESYSTEM_PATTERNS_MAX, GUEST_FILESYSTEM_PATTERN_MAX, GUEST_SERVICE_PROVIDES, GUEST_TOOLS_MAX, GUEST_TOOL_COLUMNS_MAX, GUEST_TOOL_COLUMN_MAX, GUEST_TOOL_LANGUAGE_MAX, GUEST_TOOL_MATCH, GUEST_TOOL_MATCH_MAX, GUEST_TOOL_NAME_MAX, GUEST_TOOL_OUTPUTS, GUEST_TOOL_TEMPLATE_MAX, PANEL_ID, hasGuestPage, isGuestFilesystemPattern, isGuestPackageSvgIcon, isSafeAssetPath, resolveIntegrationAuth, GUEST_SURFACE_DOCKS, GUEST_SURFACE_DOCK_SIZE_MAX, GUEST_SURFACE_DOCK_SIZE_MIN, } from "./manifest.js";
|
|
5
5
|
const isPanelIcon = (value) => (PANEL_ID.test(value) || isGuestPackageSvgIcon(value));
|
|
6
6
|
const isHttpsUrl = (value) => {
|
|
7
7
|
try {
|
|
@@ -48,6 +48,8 @@ const panelSchema = z.object({
|
|
|
48
48
|
name: z.string().trim().min(1),
|
|
49
49
|
icon: z.string().trim().refine(isPanelIcon),
|
|
50
50
|
entry: z.string().trim().refine(isSafeAssetPath).optional(),
|
|
51
|
+
dock: z.enum(GUEST_SURFACE_DOCKS).optional(),
|
|
52
|
+
size: z.number().int().min(GUEST_SURFACE_DOCK_SIZE_MIN).max(GUEST_SURFACE_DOCK_SIZE_MAX).optional(),
|
|
51
53
|
});
|
|
52
54
|
const integrationSettingSchema = z.object({
|
|
53
55
|
id: z.string().trim().regex(PANEL_ID),
|
|
@@ -140,6 +142,10 @@ const serviceSchema = z.object({
|
|
|
140
142
|
entry: z.string().trim().refine(isSafeAssetPath),
|
|
141
143
|
runtime: z.literal('host'),
|
|
142
144
|
permissions: servicePermissionsSchema.optional(),
|
|
145
|
+
provides: z.array(z.enum(GUEST_SERVICE_PROVIDES)).min(1).max(GUEST_SERVICE_PROVIDES.length)
|
|
146
|
+
.refine((roles) => new Set(roles).size === roles.length, { message: 'provides entries must be unique' })
|
|
147
|
+
.optional(),
|
|
148
|
+
surface: z.literal(true).optional(),
|
|
143
149
|
});
|
|
144
150
|
const uniqueBy = (items, key) => (new Set(items.map(key)).size === items.length);
|
|
145
151
|
// `roles` only means something on a message action and `payload` only on a
|
|
@@ -208,7 +214,10 @@ const runtimeContributions = (contributes) => {
|
|
|
208
214
|
declared.push('capabilities');
|
|
209
215
|
if (contributes.integration !== undefined)
|
|
210
216
|
declared.push('integration');
|
|
211
|
-
|
|
217
|
+
// A service the host starts itself (it provides a role or a surface) needs
|
|
218
|
+
// no frame; one that only answers a panel's `serviceRequest` has no caller
|
|
219
|
+
// without one.
|
|
220
|
+
if (contributes.service !== undefined && !contributes.service.provides?.length && !contributes.service.surface)
|
|
212
221
|
declared.push('service');
|
|
213
222
|
if (contributes.filesystem !== undefined)
|
|
214
223
|
declared.push('filesystem');
|
|
@@ -224,6 +233,15 @@ export const openChamberManifestSchema = z.object({
|
|
|
224
233
|
openchamber: z.string().trim().regex(OPENCHAMBER_ENGINE_PATTERN),
|
|
225
234
|
}).strict().optional(),
|
|
226
235
|
contributes: contributesSchema.superRefine((contributes, ctx) => {
|
|
236
|
+
// Docking only means something for a page beside a shared surface.
|
|
237
|
+
const docked = contributes.panel.dock !== undefined || contributes.panel.size !== undefined;
|
|
238
|
+
if (docked && !(contributes.service?.surface && hasGuestPage(contributes))) {
|
|
239
|
+
ctx.addIssue({
|
|
240
|
+
code: 'custom', path: ['panel', 'dock'],
|
|
241
|
+
message: 'panel.dock and panel.size place panel.entry beside service.surface; they need both.',
|
|
242
|
+
});
|
|
243
|
+
return;
|
|
244
|
+
}
|
|
227
245
|
if (hasGuestPage(contributes))
|
|
228
246
|
return;
|
|
229
247
|
if (contributes.background) {
|
|
@@ -301,6 +319,12 @@ const failureFromIssue = (issue) => {
|
|
|
301
319
|
return fail('invalid-panel-icon', 'panel.icon must be a Remixicon name or a package .svg path.');
|
|
302
320
|
case 'contributes.panel.entry':
|
|
303
321
|
return fail('invalid-panel-entry', 'panel.entry must be a relative path inside the package.');
|
|
322
|
+
case 'contributes.panel.dock':
|
|
323
|
+
return fail('invalid-panel', issue.code === 'custom'
|
|
324
|
+
? issue.message
|
|
325
|
+
: `panel.dock is one of ${GUEST_SURFACE_DOCKS.join(', ')}.`);
|
|
326
|
+
case 'contributes.panel.size':
|
|
327
|
+
return fail('invalid-panel', `panel.size is a whole number of CSS pixels from ${GUEST_SURFACE_DOCK_SIZE_MIN} to ${GUEST_SURFACE_DOCK_SIZE_MAX}.`);
|
|
304
328
|
case 'contributes.capabilities':
|
|
305
329
|
return fail('invalid-capabilities', 'contributes.capabilities may list "prompt", "sessions", and "files".');
|
|
306
330
|
default:
|
|
@@ -334,7 +358,7 @@ const failureFromIssue = (issue) => {
|
|
|
334
358
|
return fail('invalid-integration', 'contributes.integration needs a name, description, and oauth, token, or host.');
|
|
335
359
|
}
|
|
336
360
|
if (path.startsWith('contributes.service')) {
|
|
337
|
-
return fail('invalid-service', 'contributes.service needs entry, runtime "host", and optional
|
|
361
|
+
return fail('invalid-service', 'contributes.service needs entry, runtime "host", optional permissions, optional provides ("browser"), and optional surface (true).');
|
|
338
362
|
}
|
|
339
363
|
return fail('missing-panel', 'contributes.panel is required.');
|
|
340
364
|
};
|
package/dist/schemas.d.ts
CHANGED
|
@@ -2,3 +2,6 @@ export { openChamberManifestSchema, PACKAGE_VERSION_PATTERN, packageManifestSche
|
|
|
2
2
|
export type { ManifestDocument } from './parse.ts';
|
|
3
3
|
export { guestStorageRequestSchema, guestStorageResultSchema, guestWorkspaceSnapshotSchema } from './workspace-schemas.ts';
|
|
4
4
|
export { guestMessageSchema, hostMessageSchema, parseGuestMessage, parseHostMessage, } from './protocol.ts';
|
|
5
|
+
export { browserControlActionSchema, browserProviderResultSchema } from './service-provider-schemas.ts';
|
|
6
|
+
export { surfaceClipboardAnswerSchema, surfaceControllerSchema, surfaceHostMessageSchema, surfaceInputEventSchema, surfaceResizeAnswerSchema, surfaceViewerMessageSchema, } from './service-surface-schemas.ts';
|
|
7
|
+
export type { SurfaceHostMessage, SurfaceViewerMessage } from './service-surface-schemas.ts';
|
package/dist/schemas.js
CHANGED
|
@@ -4,3 +4,5 @@
|
|
|
4
4
|
export { openChamberManifestSchema, PACKAGE_VERSION_PATTERN, packageManifestSchema, parseManifest, parseManifestJson, } from "./parse.js";
|
|
5
5
|
export { guestStorageRequestSchema, guestStorageResultSchema, guestWorkspaceSnapshotSchema } from "./workspace-schemas.js";
|
|
6
6
|
export { guestMessageSchema, hostMessageSchema, parseGuestMessage, parseHostMessage, } from "./protocol.js";
|
|
7
|
+
export { browserControlActionSchema, browserProviderResultSchema } from "./service-provider-schemas.js";
|
|
8
|
+
export { surfaceClipboardAnswerSchema, surfaceControllerSchema, surfaceHostMessageSchema, surfaceInputEventSchema, surfaceResizeAnswerSchema, surfaceViewerMessageSchema, } from "./service-surface-schemas.js";
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
/**
|
|
3
|
+
* Host-side parse of what a browser provider answers. `data` stays whatever
|
|
4
|
+
* the service produced: the host hands it to the agent as is, and only
|
|
5
|
+
* `browser.capture` is read further (the image the host writes to disk).
|
|
6
|
+
*/
|
|
7
|
+
export declare const browserProviderResultSchema: z.ZodUnion<readonly [z.ZodObject<{
|
|
8
|
+
ok: z.ZodLiteral<true>;
|
|
9
|
+
data: z.ZodObject<{}, z.core.$loose>;
|
|
10
|
+
}, z.core.$strip>, z.ZodObject<{
|
|
11
|
+
ok: z.ZodLiteral<false>;
|
|
12
|
+
error: z.ZodString;
|
|
13
|
+
}, z.core.$strip>]>;
|
|
14
|
+
export declare const browserControlActionSchema: z.ZodEnum<{
|
|
15
|
+
"browser.open": "browser.open";
|
|
16
|
+
"browser.snapshot": "browser.snapshot";
|
|
17
|
+
"browser.click": "browser.click";
|
|
18
|
+
"browser.type": "browser.type";
|
|
19
|
+
"browser.scroll": "browser.scroll";
|
|
20
|
+
"browser.back": "browser.back";
|
|
21
|
+
"browser.forward": "browser.forward";
|
|
22
|
+
"browser.inspect": "browser.inspect";
|
|
23
|
+
"browser.capture": "browser.capture";
|
|
24
|
+
"browser.resize": "browser.resize";
|
|
25
|
+
}>;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { BROWSER_CONTROL_ACTIONS } from "./service-providers.js";
|
|
3
|
+
/**
|
|
4
|
+
* Host-side parse of what a browser provider answers. `data` stays whatever
|
|
5
|
+
* the service produced: the host hands it to the agent as is, and only
|
|
6
|
+
* `browser.capture` is read further (the image the host writes to disk).
|
|
7
|
+
*/
|
|
8
|
+
export const browserProviderResultSchema = z.union([
|
|
9
|
+
z.object({ ok: z.literal(true), data: z.object({}).passthrough() }),
|
|
10
|
+
z.object({ ok: z.literal(false), error: z.string().min(1).max(2_000) }),
|
|
11
|
+
]);
|
|
12
|
+
export const browserControlActionSchema = z.enum(BROWSER_CONTROL_ACTIONS);
|