@botiverse/porthole 0.1.2 → 0.1.3
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/dist/annotate/types.d.ts +120 -0
- package/dist/annotate/types.js +42 -0
- package/dist/annotate/validate.d.ts +11 -0
- package/dist/annotate/validate.js +156 -0
- package/dist/bridge/bridge.d.ts +4 -0
- package/dist/bridge/bridge.js +12 -1
- package/dist/compile/policy.d.ts +4 -3
- package/dist/compile/policy.js +7 -4
- package/dist/config/capabilities.d.ts +37 -4
- package/dist/config/capabilities.js +20 -0
- package/dist/conformance/clipboard.d.ts +2 -0
- package/dist/conformance/clipboard.js +167 -0
- package/dist/conformance/kit.d.ts +1 -1
- package/dist/conformance/probes.js +2 -0
- package/dist/guide/guide.js +8 -4
- package/dist/host/clipboard.d.ts +11 -0
- package/dist/host/clipboard.js +43 -0
- package/dist/host/create-host.js +141 -0
- package/dist/host/types.d.ts +24 -0
- package/dist/index.d.ts +6 -3
- package/dist/index.js +6 -2
- package/dist/mcp/adapter.js +10 -1
- package/dist/protocol/mcp-apps.d.ts +5 -0
- package/dist/protocol/mcp-apps.js +5 -0
- package/dist/protocol/shell-adapter.js +6 -2
- package/dist/react/dialog.js +2 -0
- package/dist/react/frame.d.ts +18 -0
- package/dist/react/frame.js +82 -2
- package/dist/react/index.d.ts +2 -1
- package/dist/react/index.js +2 -1
- package/dist/react/toast.d.ts +33 -0
- package/dist/react/toast.js +42 -0
- package/dist/shell/annotate.d.ts +20 -0
- package/dist/shell/annotate.js +669 -0
- package/dist/shell/document.js +4 -1
- package/dist/shell/plan.d.ts +2 -0
- package/dist/shell/plan.js +2 -1
- package/dist/shell/runtime.d.ts +4 -1
- package/dist/shell/runtime.js +38 -2
- package/package.json +1 -1
- package/src/annotate/types.ts +126 -0
- package/src/annotate/validate.ts +186 -0
- package/src/bridge/bridge.ts +13 -0
- package/src/compile/policy.ts +7 -4
- package/src/config/capabilities.ts +33 -3
- package/src/conformance/clipboard.ts +184 -0
- package/src/conformance/kit.ts +1 -1
- package/src/conformance/probes.ts +2 -0
- package/src/guide/guide.ts +19 -4
- package/src/host/clipboard.ts +41 -0
- package/src/host/create-host.ts +155 -0
- package/src/host/types.ts +25 -0
- package/src/index.ts +22 -0
- package/src/mcp/adapter.ts +10 -1
- package/src/protocol/mcp-apps.ts +6 -0
- package/src/protocol/shell-adapter.ts +11 -2
- package/src/react/dialog.tsx +2 -0
- package/src/react/frame.tsx +117 -1
- package/src/react/index.ts +9 -1
- package/src/react/toast.tsx +96 -0
- package/src/shell/annotate.ts +669 -0
- package/src/shell/document.ts +6 -1
- package/src/shell/plan.ts +4 -1
- package/src/shell/runtime.ts +38 -2
package/src/shell/plan.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { capabilitySpec, type CapabilityName } from "../config/capabilities.ts";
|
|
1
|
+
import { MAX_COPY_CHARS, capabilitySpec, type CapabilityName } from "../config/capabilities.ts";
|
|
2
2
|
import type { PortholeConfig } from "../config/schema.ts";
|
|
3
3
|
import { declaredCapabilities, effectiveGates } from "../compile/policy.ts";
|
|
4
4
|
import { MCP_APPS_PROTOCOL_VERSION } from "../protocol/mcp-apps.ts";
|
|
@@ -31,6 +31,8 @@ export interface ShellPlan {
|
|
|
31
31
|
readonly storage: "memory" | "native";
|
|
32
32
|
readonly maxErrorReports: number;
|
|
33
33
|
readonly maxPromptChars: number;
|
|
34
|
+
/** The most text `copyText` carries. */
|
|
35
|
+
readonly maxCopyChars: number;
|
|
34
36
|
readonly protocolVersion: string;
|
|
35
37
|
}
|
|
36
38
|
|
|
@@ -64,6 +66,7 @@ export function shellPlan(config: PortholeConfig): ShellPlan | null {
|
|
|
64
66
|
storage: config.isolation === "opaque" ? "memory" : "native",
|
|
65
67
|
maxErrorReports: config.limits.maxErrorReports,
|
|
66
68
|
maxPromptChars: config.limits.maxPromptChars,
|
|
69
|
+
maxCopyChars: MAX_COPY_CHARS,
|
|
67
70
|
protocolVersion: MCP_APPS_PROTOCOL_VERSION,
|
|
68
71
|
};
|
|
69
72
|
}
|
package/src/shell/runtime.ts
CHANGED
|
@@ -13,7 +13,10 @@
|
|
|
13
13
|
* engine enforces `frame-src 'none'` for `srcdoc`/`about:blank` children); and the resource
|
|
14
14
|
* hints and refreshes a CSP doesn't govern;
|
|
15
15
|
* 4. closes the parser entry points that could build a shadow root step 3 can't see (declarative
|
|
16
|
-
* shadow DOM through `setHTMLUnsafe`, `parseHTMLUnsafe`, `document.write`)
|
|
16
|
+
* shadow DOM through `setHTMLUnsafe`, `parseHTMLUnsafe`, `document.write`);
|
|
17
|
+
* 5. keeps content off the clipboard: the async Clipboard API refuses, `execCommand` won't copy or
|
|
18
|
+
* cut, and copy and cut events never reach content, so what a person copies is what they
|
|
19
|
+
* selected (P051). Copy buttons go through the host (`copyText`, P052).
|
|
17
20
|
* A whole document (an artifact page, an MCP Apps view) is never written in: it is parsed inert,
|
|
18
21
|
* imported, and its scripts run in order, as a fragment's are.
|
|
19
22
|
*
|
|
@@ -263,13 +266,32 @@ export const SHELL_RUNTIME = String.raw`(() => {
|
|
|
263
266
|
this.innerHTML = String(html);
|
|
264
267
|
});
|
|
265
268
|
}
|
|
269
|
+
// insertHTML parses markup outside these guards; copy, cut and paste reach the clipboard (P051).
|
|
270
|
+
const REFUSED_COMMANDS = ["inserthtml", "copy", "cut", "paste"];
|
|
266
271
|
const execCommand = Document.prototype.execCommand;
|
|
267
272
|
if (typeof execCommand === "function") {
|
|
268
273
|
lock(Document.prototype, "execCommand", function (command, ...rest) {
|
|
269
|
-
return String(command).toLowerCase()
|
|
274
|
+
return REFUSED_COMMANDS.indexOf(String(command).toLowerCase()) !== -1 ? false : execCommand.call(this, command, ...rest);
|
|
270
275
|
});
|
|
271
276
|
}
|
|
272
277
|
|
|
278
|
+
// The clipboard is the host's to write (P051): content can neither write nor read it. A copy
|
|
279
|
+
// the person makes (their keys, their menu) copies their selection as the browser makes it: no
|
|
280
|
+
// copy or cut listener of the content's runs, so none can swap what goes on the clipboard.
|
|
281
|
+
const noClipboard = function () {
|
|
282
|
+
return Promise.reject(new DOMException("Content can't use the clipboard here" + (declared("copyText") ? ": call copyText(text) from a click" : ""), "NotAllowedError"));
|
|
283
|
+
};
|
|
284
|
+
if (typeof Clipboard === "function") {
|
|
285
|
+
for (const name of ["write", "writeText", "read", "readText"]) {
|
|
286
|
+
if (typeof Clipboard.prototype[name] === "function") {
|
|
287
|
+
lock(Clipboard.prototype, name, noClipboard);
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
for (const type of ["copy", "cut"]) {
|
|
292
|
+
window.addEventListener(type, (event) => event.stopImmediatePropagation(), true);
|
|
293
|
+
}
|
|
294
|
+
|
|
273
295
|
// A connected link or meta can't become a guarded one: the change is tried on a detached copy
|
|
274
296
|
// first, and if the copy would be guarded, the element leaves the document before it changes.
|
|
275
297
|
const watched = (element) => element && element.nodeType === 1 && (element.localName === "link" || element.localName === "meta");
|
|
@@ -534,6 +556,8 @@ export const SHELL_RUNTIME = String.raw`(() => {
|
|
|
534
556
|
}
|
|
535
557
|
};
|
|
536
558
|
|
|
559
|
+
__ANNOTATE__
|
|
560
|
+
|
|
537
561
|
if (plan.protocol !== "shell") {
|
|
538
562
|
// A view that speaks for itself.
|
|
539
563
|
if (typeof plan.document === "string") {
|
|
@@ -624,6 +648,18 @@ export const SHELL_RUNTIME = String.raw`(() => {
|
|
|
624
648
|
);
|
|
625
649
|
});
|
|
626
650
|
}
|
|
651
|
+
if (declared("copyText")) {
|
|
652
|
+
lock(window, "copyText", (text) => {
|
|
653
|
+
if (!gestured("copyText")) {
|
|
654
|
+
return refuse("copyText needs a click: call it from a button's handler");
|
|
655
|
+
}
|
|
656
|
+
const words = String(text);
|
|
657
|
+
if (words === "" || words.length > plan.maxCopyChars) {
|
|
658
|
+
return refuse("copyText takes 1 to " + plan.maxCopyChars + " characters");
|
|
659
|
+
}
|
|
660
|
+
return request("porthole/copy-text", { text: words }).then((result) => !(result && result.isError));
|
|
661
|
+
});
|
|
662
|
+
}
|
|
627
663
|
if (declared("openLink")) {
|
|
628
664
|
lock(window, "openLink", (url) => {
|
|
629
665
|
if (!gestured("openLink")) {
|