mirror of
https://github.com/t8y2/dbx.git
synced 2026-11-12 02:23:37 +08:00
feat(plugins): bridge clipboard reads with consent gate
This commit is contained in:
@@ -105,6 +105,7 @@ Manifest paths are relative to the package root. They cannot start with `/`, con
|
||||
"host.plans:read",
|
||||
"host.schema:read",
|
||||
"host.storage",
|
||||
"host.clipboard:read",
|
||||
"host.network:https://s3.example.com:443"
|
||||
],
|
||||
"entrypoints": {
|
||||
@@ -124,6 +125,7 @@ Manifest paths are relative to the package root. They cannot start with `/`, con
|
||||
- `host.schema:read` allows `host.getTableMetadata` to read narrow table schema metadata from an already-open connection; it grants no arbitrary SQL, writes, or implicit reconnect (see [Table Schema Metadata](#table-schema-metadata)).
|
||||
- `host.storage` gives the workbench UI a persistent key-value store scoped to this plugin (see [Persistent UI State](#persistent-ui-state)). Values never leave the plugin's own data directory.
|
||||
- `host.ai` lets the plugin open a conversation with a data snapshot in the built-in DBX AI panel, using its model selector, history, follow-up messages, cancellation and export. Older DBX builds reject the unknown permission at install time, so plugins that must install everywhere should treat `host.ai` as optional.
|
||||
- `host.clipboard:read` allows reading the system clipboard from the workbench UI (see [System Clipboard](#system-clipboard)). Clipboard writes need no permission; reads hand user data (passwords, tokens) to plugin code, so they are gated.
|
||||
- `host.network:https://host[:port]` declares a browser-accessible HTTPS origin added to CSP `connect-src`; at most eight origins are allowed, with no paths, wildcards, or tokens. The service's CORS rules still apply. This permission is not a native Sidecar network firewall.
|
||||
- `backend.transport` defaults to `stdio-jsonl`. Use `stdio-framed` for binary frames and declare `host.binary` as well.
|
||||
- `backend.executable`, `ui.root`, and `ui.entry` must point to package files. Native packages need a matching executable for every target.
|
||||
@@ -316,11 +318,12 @@ const assetUrl = await window.dbxPlugin.readAssetUrl("assets/empty-state.svg");
|
||||
| `getPlanCapabilities(connectionId)` | Reads what the host and this connection can plan; requires `host.plans:read`. |
|
||||
| `explainPlan({ connectionId, database?, schema?, sql, mode, timeoutMs? })` | Returns the estimated plan for `sql`; requires `host.plans:read`. |
|
||||
| `getTableMetadata({ connectionId, database?, schema?, table })` | Reads narrow table schema metadata from an already-open connection; requires `host.schema:read`. |
|
||||
| `capabilities` | `{ downloadFile, planApi, schemaMetadataApi, storage, ai }` from the init message. A missing or `false` entry means that Host API group is unavailable here, so gate the matching call on it instead of probing with a request. |
|
||||
| `capabilities` | `{ downloadFile, planApi, schemaMetadataApi, storage, ai, clipboardWrite, clipboardRead }` from the init message. A missing or `false` entry means that Host API group is unavailable here, so gate the matching call on it instead of probing with a request. |
|
||||
| `ai.openConversation({ title, prompt, context, send? })` | Opens a new plugin conversation in the built-in AI panel; `send` defaults to `false`, or starts analysis when `true`; requires `host.ai` and is advertised as `capabilities.ai` — gate on it instead of probing, since older hosts omit it. |
|
||||
| `onInit(fn)` / `onEvent(fn)` | Observes initialization, environment changes, or backend events; forwarding backend events requires `host.events`. |
|
||||
| `storage` | Persistent per-plugin key-value state; requires `host.storage`; see below. |
|
||||
| `fileTransfer` | Streams local files and OS drops (desktop only); see below. |
|
||||
| `clipboard` | System clipboard access from the sandbox; see below. |
|
||||
|
||||
Failed calls reject their Promise; show a useful error and offer retry. Host methods, parameters, and results are versioned API surface—do not call undocumented DBX internals.
|
||||
|
||||
@@ -423,6 +426,31 @@ if (window.dbxPlugin.capabilities.schemaMetadataApi) {
|
||||
|
||||
This API is part of Host API 1.3. A plugin that cannot work without it should declare `"engines": { "host_api": "^1.3" }` and still read `capabilities.schemaMetadataApi`, because older hosts omit that field. Without `host.schema:read`, the host rejects the request before it reaches the backend.
|
||||
|
||||
### System Clipboard
|
||||
|
||||
The plugin sandbox has an opaque origin and no clipboard permission, so `navigator.clipboard` is unavailable there. The host bridges the system clipboard instead:
|
||||
|
||||
- `window.dbxPlugin.copy(text)` / `window.dbxPlugin.clipboard.writeText(text)` write to the system clipboard. Both ride the same ungated bridge method: a write has the user's data as its input and is the low-risk half of the surface.
|
||||
- `window.dbxPlugin.clipboard.readText()` reads the system clipboard. It requires the `host.clipboard:read` permission — a read hands user data (passwords, tokens) to plugin code with no further user interaction — and is Host API 1.3: declare `"engines": { "host_api": "^1.3" }` if your workbench cannot function without it.
|
||||
|
||||
Gate reads on the init capability rather than probing: `capabilities.clipboardRead` (and `capabilities.clipboardWrite` for writes) are absent on older hosts, which makes them falsy. A rejected read (missing permission, web host without native clipboard access) rejects its Promise — degrade gracefully, e.g. fall back to a keyboard-paste path instead of failing the interaction.
|
||||
|
||||
```js
|
||||
await window.dbxPlugin.ready;
|
||||
const canRead = !!window.dbxPlugin.capabilities.clipboardRead;
|
||||
const canWrite = !!window.dbxPlugin.capabilities.clipboardWrite;
|
||||
|
||||
async function pasteFromClipboard() {
|
||||
if (!canRead) return null;
|
||||
try {
|
||||
return await window.dbxPlugin.clipboard.readText();
|
||||
} catch {
|
||||
// Permission missing or the clipboard read failed: keep the keyboard path.
|
||||
return null;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### File Transfer and OS Drops
|
||||
|
||||
Desktop hosts stream local files into plugin sandboxes through `window.dbxPlugin.fileTransfer`. Handles are opened only after explicit user consent: a native open/save dialog (`pick` / `beginSave`) or a file dropped from the OS onto this plugin's workbench area (`onDrop`). Transfers are chunked, so multi-gigabyte files never load into memory.
|
||||
|
||||
Reference in New Issue
Block a user