mac: bundle web chat assets

This commit is contained in:
Peter Steinberger
2025-12-06 05:01:28 +01:00
parent 15cdeeddaf
commit 42d843297d
315 changed files with 16618 additions and 20 deletions

View File

@@ -0,0 +1,35 @@
import type { SandboxRuntimeProvider } from "./SandboxRuntimeProvider.js";
interface ArtifactsPanelLike {
artifacts: Map<string, {
content: string;
}>;
tool: {
execute(toolCallId: string, args: {
command: string;
filename: string;
content?: string;
}): Promise<any>;
};
}
interface AgentLike {
appendMessage(message: any): void;
}
/**
* Artifacts Runtime Provider
*
* Provides programmatic access to session artifacts from sandboxed code.
* Allows code to create, read, update, and delete artifacts dynamically.
* Supports both online (extension) and offline (downloaded HTML) modes.
*/
export declare class ArtifactsRuntimeProvider implements SandboxRuntimeProvider {
private artifactsPanel;
private agent?;
private readWrite;
constructor(artifactsPanel: ArtifactsPanelLike, agent?: AgentLike | undefined, readWrite?: boolean);
getData(): Record<string, any>;
getRuntime(): (sandboxId: string) => void;
handleMessage(message: any, respond: (response: any) => void): Promise<void>;
getDescription(): string;
}
export {};
//# sourceMappingURL=ArtifactsRuntimeProvider.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"ArtifactsRuntimeProvider.d.ts","sourceRoot":"","sources":["../../../src/components/sandbox/ArtifactsRuntimeProvider.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,6BAA6B,CAAC;AAG1E,UAAU,kBAAkB;IAC3B,SAAS,EAAE,GAAG,CAAC,MAAM,EAAE;QAAE,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAC5C,IAAI,EAAE;QACL,OAAO,CAAC,UAAU,EAAE,MAAM,EAAE,IAAI,EAAE;YAAE,OAAO,EAAE,MAAM,CAAC;YAAC,QAAQ,EAAE,MAAM,CAAC;YAAC,OAAO,CAAC,EAAE,MAAM,CAAA;SAAE,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC;KACzG,CAAC;CACF;AAED,UAAU,SAAS;IAClB,aAAa,CAAC,OAAO,EAAE,GAAG,GAAG,IAAI,CAAC;CAClC;AAED;;;;;;GAMG;AACH,qBAAa,wBAAyB,YAAW,sBAAsB;IAErE,OAAO,CAAC,cAAc;IACtB,OAAO,CAAC,KAAK,CAAC;IACd,OAAO,CAAC,SAAS;gBAFT,cAAc,EAAE,kBAAkB,EAClC,KAAK,CAAC,EAAE,SAAS,YAAA,EACjB,SAAS,GAAE,OAAc;IAGlC,OAAO,IAAI,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC;IAS9B,UAAU,IAAI,CAAC,SAAS,EAAE,MAAM,KAAK,IAAI;IAgGnC,aAAa,CAAC,OAAO,EAAE,GAAG,EAAE,OAAO,EAAE,CAAC,QAAQ,EAAE,GAAG,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC;IA8ElF,cAAc,IAAI,MAAM;CAGxB"}

View File

@@ -0,0 +1,189 @@
import { ARTIFACTS_RUNTIME_PROVIDER_DESCRIPTION_RO, ARTIFACTS_RUNTIME_PROVIDER_DESCRIPTION_RW, } from "../../prompts/prompts.js";
/**
* Artifacts Runtime Provider
*
* Provides programmatic access to session artifacts from sandboxed code.
* Allows code to create, read, update, and delete artifacts dynamically.
* Supports both online (extension) and offline (downloaded HTML) modes.
*/
export class ArtifactsRuntimeProvider {
constructor(artifactsPanel, agent, readWrite = true) {
this.artifactsPanel = artifactsPanel;
this.agent = agent;
this.readWrite = readWrite;
}
getData() {
// Inject artifact snapshot for offline mode
const snapshot = {};
this.artifactsPanel.artifacts.forEach((artifact, filename) => {
snapshot[filename] = artifact.content;
});
return { artifacts: snapshot };
}
getRuntime() {
// This function will be stringified, so no external references!
return (_sandboxId) => {
// Auto-parse/stringify for .json files
const isJsonFile = (filename) => filename.endsWith(".json");
window.listArtifacts = async () => {
// Online: ask extension
if (window.sendRuntimeMessage) {
const response = await window.sendRuntimeMessage({
type: "artifact-operation",
action: "list",
});
if (!response.success)
throw new Error(response.error);
return response.result;
}
// Offline: return snapshot keys
else {
return Object.keys(window.artifacts || {});
}
};
window.getArtifact = async (filename) => {
let content;
// Online: ask extension
if (window.sendRuntimeMessage) {
const response = await window.sendRuntimeMessage({
type: "artifact-operation",
action: "get",
filename,
});
if (!response.success)
throw new Error(response.error);
content = response.result;
}
// Offline: read snapshot
else {
if (!window.artifacts?.[filename]) {
throw new Error(`Artifact not found (offline mode): ${filename}`);
}
content = window.artifacts[filename];
}
// Auto-parse .json files
if (isJsonFile(filename)) {
try {
return JSON.parse(content);
}
catch (e) {
throw new Error(`Failed to parse JSON from ${filename}: ${e}`);
}
}
return content;
};
window.createOrUpdateArtifact = async (filename, content, mimeType) => {
if (!window.sendRuntimeMessage) {
throw new Error("Cannot create/update artifacts in offline mode (read-only)");
}
let finalContent = content;
// Auto-stringify .json files
if (isJsonFile(filename) && typeof content !== "string") {
finalContent = JSON.stringify(content, null, 2);
}
else if (typeof content !== "string") {
finalContent = JSON.stringify(content, null, 2);
}
const response = await window.sendRuntimeMessage({
type: "artifact-operation",
action: "createOrUpdate",
filename,
content: finalContent,
mimeType,
});
if (!response.success)
throw new Error(response.error);
};
window.deleteArtifact = async (filename) => {
if (!window.sendRuntimeMessage) {
throw new Error("Cannot delete artifacts in offline mode (read-only)");
}
const response = await window.sendRuntimeMessage({
type: "artifact-operation",
action: "delete",
filename,
});
if (!response.success)
throw new Error(response.error);
};
};
}
async handleMessage(message, respond) {
if (message.type !== "artifact-operation") {
return;
}
const { action, filename, content, mimeType } = message;
try {
switch (action) {
case "list": {
const filenames = Array.from(this.artifactsPanel.artifacts.keys());
respond({ success: true, result: filenames });
break;
}
case "get": {
const artifact = this.artifactsPanel.artifacts.get(filename);
if (!artifact) {
respond({ success: false, error: `Artifact not found: ${filename}` });
}
else {
respond({ success: true, result: artifact.content });
}
break;
}
case "createOrUpdate": {
try {
const exists = this.artifactsPanel.artifacts.has(filename);
const command = exists ? "rewrite" : "create";
const action = exists ? "update" : "create";
await this.artifactsPanel.tool.execute("", {
command,
filename,
content,
});
this.agent?.appendMessage({
role: "artifact",
action,
filename,
content,
...(action === "create" && { title: filename }),
timestamp: new Date().toISOString(),
});
respond({ success: true });
}
catch (err) {
respond({ success: false, error: err.message });
}
break;
}
case "delete": {
try {
await this.artifactsPanel.tool.execute("", {
command: "delete",
filename,
});
this.agent?.appendMessage({
role: "artifact",
action: "delete",
filename,
timestamp: new Date().toISOString(),
});
respond({ success: true });
}
catch (err) {
respond({ success: false, error: err.message });
}
break;
}
default:
respond({ success: false, error: `Unknown artifact action: ${action}` });
}
}
catch (error) {
respond({ success: false, error: error.message });
}
}
getDescription() {
return this.readWrite ? ARTIFACTS_RUNTIME_PROVIDER_DESCRIPTION_RW : ARTIFACTS_RUNTIME_PROVIDER_DESCRIPTION_RO;
}
}
//# sourceMappingURL=ArtifactsRuntimeProvider.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,17 @@
import type { Attachment } from "../../utils/attachment-utils.js";
import type { SandboxRuntimeProvider } from "./SandboxRuntimeProvider.js";
/**
* Attachments Runtime Provider
*
* OPTIONAL provider that provides file access APIs to sandboxed code.
* Only needed when attachments are present.
* Attachments are read-only snapshot data - no messaging needed.
*/
export declare class AttachmentsRuntimeProvider implements SandboxRuntimeProvider {
private attachments;
constructor(attachments: Attachment[]);
getData(): Record<string, any>;
getRuntime(): (sandboxId: string) => void;
getDescription(): string;
}
//# sourceMappingURL=AttachmentsRuntimeProvider.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"AttachmentsRuntimeProvider.d.ts","sourceRoot":"","sources":["../../../src/components/sandbox/AttachmentsRuntimeProvider.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,iCAAiC,CAAC;AAClE,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,6BAA6B,CAAC;AAE1E;;;;;;GAMG;AACH,qBAAa,0BAA2B,YAAW,sBAAsB;IAC5D,OAAO,CAAC,WAAW;gBAAX,WAAW,EAAE,UAAU,EAAE;IAE7C,OAAO,IAAI,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC;IAa9B,UAAU,IAAI,CAAC,SAAS,EAAE,MAAM,KAAK,IAAI;IAmCzC,cAAc,IAAI,MAAM;CAGxB"}

View File

@@ -0,0 +1,64 @@
import { ATTACHMENTS_RUNTIME_DESCRIPTION } from "../../prompts/prompts.js";
/**
* Attachments Runtime Provider
*
* OPTIONAL provider that provides file access APIs to sandboxed code.
* Only needed when attachments are present.
* Attachments are read-only snapshot data - no messaging needed.
*/
export class AttachmentsRuntimeProvider {
constructor(attachments) {
this.attachments = attachments;
}
getData() {
const attachmentsData = this.attachments.map((a) => ({
id: a.id,
fileName: a.fileName,
mimeType: a.mimeType,
size: a.size,
content: a.content,
extractedText: a.extractedText,
}));
return { attachments: attachmentsData };
}
getRuntime() {
// This function will be stringified, so no external references!
// These functions read directly from window.attachments
// Works both online AND offline (no messaging needed!)
return (_sandboxId) => {
window.listAttachments = () => (window.attachments || []).map((a) => ({
id: a.id,
fileName: a.fileName,
mimeType: a.mimeType,
size: a.size,
}));
window.readTextAttachment = (attachmentId) => {
const a = (window.attachments || []).find((x) => x.id === attachmentId);
if (!a)
throw new Error("Attachment not found: " + attachmentId);
if (a.extractedText)
return a.extractedText;
try {
return atob(a.content);
}
catch {
throw new Error("Failed to decode text content for: " + attachmentId);
}
};
window.readBinaryAttachment = (attachmentId) => {
const a = (window.attachments || []).find((x) => x.id === attachmentId);
if (!a)
throw new Error("Attachment not found: " + attachmentId);
const bin = atob(a.content);
const bytes = new Uint8Array(bin.length);
for (let i = 0; i < bin.length; i++)
bytes[i] = bin.charCodeAt(i);
return bytes;
};
};
}
getDescription() {
return ATTACHMENTS_RUNTIME_DESCRIPTION;
}
}
//# sourceMappingURL=AttachmentsRuntimeProvider.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"AttachmentsRuntimeProvider.js","sourceRoot":"","sources":["../../../src/components/sandbox/AttachmentsRuntimeProvider.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,+BAA+B,EAAE,MAAM,0BAA0B,CAAC;AAI3E;;;;;;GAMG;AACH,MAAM,OAAO,0BAA0B;IACtC,YAAoB,WAAyB;QAAzB,gBAAW,GAAX,WAAW,CAAc;IAAG,CAAC;IAEjD,OAAO;QACN,MAAM,eAAe,GAAG,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;YACpD,EAAE,EAAE,CAAC,CAAC,EAAE;YACR,QAAQ,EAAE,CAAC,CAAC,QAAQ;YACpB,QAAQ,EAAE,CAAC,CAAC,QAAQ;YACpB,IAAI,EAAE,CAAC,CAAC,IAAI;YACZ,OAAO,EAAE,CAAC,CAAC,OAAO;YAClB,aAAa,EAAE,CAAC,CAAC,aAAa;SAC9B,CAAC,CAAC,CAAC;QAEJ,OAAO,EAAE,WAAW,EAAE,eAAe,EAAE,CAAC;IACzC,CAAC;IAED,UAAU;QACT,gEAAgE;QAChE,wDAAwD;QACxD,uDAAuD;QACvD,OAAO,CAAC,UAAkB,EAAE,EAAE;YAC5B,MAAc,CAAC,eAAe,GAAG,GAAG,EAAE,CACtC,CAAE,MAAc,CAAC,WAAW,IAAI,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAM,EAAE,EAAE,CAAC,CAAC;gBACpD,EAAE,EAAE,CAAC,CAAC,EAAE;gBACR,QAAQ,EAAE,CAAC,CAAC,QAAQ;gBACpB,QAAQ,EAAE,CAAC,CAAC,QAAQ;gBACpB,IAAI,EAAE,CAAC,CAAC,IAAI;aACZ,CAAC,CAAC,CAAC;YAEJ,MAAc,CAAC,kBAAkB,GAAG,CAAC,YAAoB,EAAE,EAAE;gBAC7D,MAAM,CAAC,GAAG,CAAE,MAAc,CAAC,WAAW,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,CAAM,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,YAAY,CAAC,CAAC;gBACtF,IAAI,CAAC,CAAC;oBAAE,MAAM,IAAI,KAAK,CAAC,wBAAwB,GAAG,YAAY,CAAC,CAAC;gBACjE,IAAI,CAAC,CAAC,aAAa;oBAAE,OAAO,CAAC,CAAC,aAAa,CAAC;gBAC5C,IAAI,CAAC;oBACJ,OAAO,IAAI,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC;gBACxB,CAAC;gBAAC,MAAM,CAAC;oBACR,MAAM,IAAI,KAAK,CAAC,qCAAqC,GAAG,YAAY,CAAC,CAAC;gBACvE,CAAC;YACF,CAAC,CAAC;YAED,MAAc,CAAC,oBAAoB,GAAG,CAAC,YAAoB,EAAE,EAAE;gBAC/D,MAAM,CAAC,GAAG,CAAE,MAAc,CAAC,WAAW,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,CAAM,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,YAAY,CAAC,CAAC;gBACtF,IAAI,CAAC,CAAC;oBAAE,MAAM,IAAI,KAAK,CAAC,wBAAwB,GAAG,YAAY,CAAC,CAAC;gBACjE,MAAM,GAAG,GAAG,IAAI,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC;gBAC5B,MAAM,KAAK,GAAG,IAAI,UAAU,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;gBACzC,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,GAAG,CAAC,MAAM,EAAE,CAAC,EAAE;oBAAE,KAAK,CAAC,CAAC,CAAC,GAAG,GAAG,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC;gBAClE,OAAO,KAAK,CAAC;YACd,CAAC,CAAC;QACH,CAAC,CAAC;IACH,CAAC;IAED,cAAc;QACb,OAAO,+BAA+B,CAAC;IACxC,CAAC;CACD"}

View File

@@ -0,0 +1,42 @@
import type { SandboxRuntimeProvider } from "./SandboxRuntimeProvider.js";
export interface ConsoleLog {
type: "log" | "warn" | "error" | "info";
text: string;
args?: unknown[];
}
/**
* Console Runtime Provider
*
* REQUIRED provider that should always be included first.
* Provides console capture, error handling, and execution lifecycle management.
* Collects console output for retrieval by caller.
*/
export declare class ConsoleRuntimeProvider implements SandboxRuntimeProvider {
private logs;
private completionError;
private completed;
getData(): Record<string, any>;
getDescription(): string;
getRuntime(): (sandboxId: string) => void;
handleMessage(message: any, respond: (response: any) => void): Promise<void>;
/**
* Get collected console logs
*/
getLogs(): ConsoleLog[];
/**
* Get completion status
*/
isCompleted(): boolean;
/**
* Get completion error if any
*/
getCompletionError(): {
message: string;
stack: string;
} | null;
/**
* Reset state for reuse
*/
reset(): void;
}
//# sourceMappingURL=ConsoleRuntimeProvider.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"ConsoleRuntimeProvider.d.ts","sourceRoot":"","sources":["../../../src/components/sandbox/ConsoleRuntimeProvider.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,6BAA6B,CAAC;AAE1E,MAAM,WAAW,UAAU;IAC1B,IAAI,EAAE,KAAK,GAAG,MAAM,GAAG,OAAO,GAAG,MAAM,CAAC;IACxC,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,OAAO,EAAE,CAAC;CACjB;AAED;;;;;;GAMG;AACH,qBAAa,sBAAuB,YAAW,sBAAsB;IACpE,OAAO,CAAC,IAAI,CAAoB;IAChC,OAAO,CAAC,eAAe,CAAmD;IAC1E,OAAO,CAAC,SAAS,CAAS;IAE1B,OAAO,IAAI,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC;IAK9B,cAAc,IAAI,MAAM;IAIxB,UAAU,IAAI,CAAC,SAAS,EAAE,MAAM,KAAK,IAAI;IA4GnC,aAAa,CAAC,OAAO,EAAE,GAAG,EAAE,OAAO,EAAE,CAAC,QAAQ,EAAE,GAAG,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC;IAoBlF;;OAEG;IACH,OAAO,IAAI,UAAU,EAAE;IAIvB;;OAEG;IACH,WAAW,IAAI,OAAO;IAItB;;OAEG;IACH,kBAAkB,IAAI;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI;IAI/D;;OAEG;IACH,KAAK,IAAI,IAAI;CAKb"}

View File

@@ -0,0 +1,161 @@
/**
* Console Runtime Provider
*
* REQUIRED provider that should always be included first.
* Provides console capture, error handling, and execution lifecycle management.
* Collects console output for retrieval by caller.
*/
export class ConsoleRuntimeProvider {
constructor() {
this.logs = [];
this.completionError = null;
this.completed = false;
}
getData() {
// No data needed
return {};
}
getDescription() {
return "";
}
getRuntime() {
return (_sandboxId) => {
// Store truly original console methods on first wrap only
// This prevents accumulation of wrapper functions across multiple executions
if (!window.__originalConsole) {
window.__originalConsole = {
log: console.log.bind(console),
error: console.error.bind(console),
warn: console.warn.bind(console),
info: console.info.bind(console),
};
}
// Always use the truly original console, not the current (possibly wrapped) one
const originalConsole = window.__originalConsole;
// Track pending send promises to wait for them in onCompleted
const pendingSends = [];
["log", "error", "warn", "info"].forEach((method) => {
console[method] = (...args) => {
const text = args
.map((arg) => {
try {
return typeof arg === "object" ? JSON.stringify(arg) : String(arg);
}
catch {
return String(arg);
}
})
.join(" ");
// Always log locally too (using truly original console)
originalConsole[method].apply(console, args);
// Send immediately and track the promise (only in extension context)
if (window.sendRuntimeMessage) {
const sendPromise = window
.sendRuntimeMessage({
type: "console",
method,
text,
args,
})
.catch(() => { });
pendingSends.push(sendPromise);
}
};
});
// Register completion callback to wait for all pending sends
if (window.onCompleted) {
window.onCompleted(async (_success) => {
// Wait for all pending console sends to complete
if (pendingSends.length > 0) {
await Promise.all(pendingSends);
}
});
}
// Track errors for HTML artifacts
let lastError = null;
// Error handlers - track errors but don't log them
// (they'll be shown via execution-error message)
window.addEventListener("error", (e) => {
const text = (e.error?.stack || e.message || String(e)) + " at line " + (e.lineno || "?") + ":" + (e.colno || "?");
lastError = {
message: e.error?.message || e.message || String(e),
stack: e.error?.stack || text,
};
});
window.addEventListener("unhandledrejection", (e) => {
const text = "Unhandled promise rejection: " + (e.reason?.message || e.reason || "Unknown error");
lastError = {
message: e.reason?.message || String(e.reason) || "Unhandled promise rejection",
stack: e.reason?.stack || text,
};
});
// Expose complete() method for user code to call
let completionSent = false;
window.complete = async (error, returnValue) => {
if (completionSent)
return;
completionSent = true;
const finalError = error || lastError;
if (window.sendRuntimeMessage) {
if (finalError) {
await window.sendRuntimeMessage({
type: "execution-error",
error: finalError,
});
}
else {
await window.sendRuntimeMessage({
type: "execution-complete",
returnValue,
});
}
}
};
};
}
async handleMessage(message, respond) {
if (message.type === "console") {
// Collect console output
this.logs.push({
type: message.method === "error"
? "error"
: message.method === "warn"
? "warn"
: message.method === "info"
? "info"
: "log",
text: message.text,
args: message.args,
});
// Acknowledge receipt
respond({ success: true });
}
}
/**
* Get collected console logs
*/
getLogs() {
return this.logs;
}
/**
* Get completion status
*/
isCompleted() {
return this.completed;
}
/**
* Get completion error if any
*/
getCompletionError() {
return this.completionError;
}
/**
* Reset state for reuse
*/
reset() {
this.logs = [];
this.completionError = null;
this.completed = false;
}
}
//# sourceMappingURL=ConsoleRuntimeProvider.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"ConsoleRuntimeProvider.js","sourceRoot":"","sources":["../../../src/components/sandbox/ConsoleRuntimeProvider.ts"],"names":[],"mappings":"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"}

View File

@@ -0,0 +1,30 @@
import type { SandboxRuntimeProvider } from "./SandboxRuntimeProvider.js";
export interface DownloadableFile {
fileName: string;
content: string | Uint8Array;
mimeType: string;
}
/**
* File Download Runtime Provider
*
* Provides returnDownloadableFile() for creating user downloads.
* Files returned this way are NOT accessible to the LLM later (one-time download).
* Works both online (sends to extension) and offline (triggers browser download directly).
* Collects files for retrieval by caller.
*/
export declare class FileDownloadRuntimeProvider implements SandboxRuntimeProvider {
private files;
getData(): Record<string, any>;
getRuntime(): (sandboxId: string) => void;
handleMessage(message: any, respond: (response: any) => void): Promise<void>;
/**
* Get collected files
*/
getFiles(): DownloadableFile[];
/**
* Reset state for reuse
*/
reset(): void;
getDescription(): string;
}
//# sourceMappingURL=FileDownloadRuntimeProvider.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"FileDownloadRuntimeProvider.d.ts","sourceRoot":"","sources":["../../../src/components/sandbox/FileDownloadRuntimeProvider.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,6BAA6B,CAAC;AAE1E,MAAM,WAAW,gBAAgB;IAChC,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,GAAG,UAAU,CAAC;IAC7B,QAAQ,EAAE,MAAM,CAAC;CACjB;AAED;;;;;;;GAOG;AACH,qBAAa,2BAA4B,YAAW,sBAAsB;IACzE,OAAO,CAAC,KAAK,CAA0B;IAEvC,OAAO,IAAI,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC;IAK9B,UAAU,IAAI,CAAC,SAAS,EAAE,MAAM,KAAK,IAAI;IAuDnC,aAAa,CAAC,OAAO,EAAE,GAAG,EAAE,OAAO,EAAE,CAAC,QAAQ,EAAE,GAAG,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC;IAalF;;OAEG;IACH,QAAQ,IAAI,gBAAgB,EAAE;IAI9B;;OAEG;IACH,KAAK,IAAI,IAAI;IAIb,cAAc,IAAI,MAAM;CAGxB"}

View File

@@ -0,0 +1,97 @@
/**
* File Download Runtime Provider
*
* Provides returnDownloadableFile() for creating user downloads.
* Files returned this way are NOT accessible to the LLM later (one-time download).
* Works both online (sends to extension) and offline (triggers browser download directly).
* Collects files for retrieval by caller.
*/
export class FileDownloadRuntimeProvider {
constructor() {
this.files = [];
}
getData() {
// No data needed
return {};
}
getRuntime() {
return (_sandboxId) => {
window.returnDownloadableFile = async (fileName, content, mimeType) => {
let finalContent, finalMimeType;
if (content instanceof Blob) {
const arrayBuffer = await content.arrayBuffer();
finalContent = new Uint8Array(arrayBuffer);
finalMimeType = mimeType || content.type || "application/octet-stream";
if (!mimeType && !content.type) {
throw new Error("returnDownloadableFile: MIME type is required for Blob content. Please provide a mimeType parameter (e.g., 'image/png').");
}
}
else if (content instanceof Uint8Array) {
finalContent = content;
if (!mimeType) {
throw new Error("returnDownloadableFile: MIME type is required for Uint8Array content. Please provide a mimeType parameter (e.g., 'image/png').");
}
finalMimeType = mimeType;
}
else if (typeof content === "string") {
finalContent = content;
finalMimeType = mimeType || "text/plain";
}
else {
finalContent = JSON.stringify(content, null, 2);
finalMimeType = mimeType || "application/json";
}
// Send to extension if in extension context (online mode)
if (window.sendRuntimeMessage) {
const response = await window.sendRuntimeMessage({
type: "file-returned",
fileName,
content: finalContent,
mimeType: finalMimeType,
});
if (response.error)
throw new Error(response.error);
}
else {
// Offline mode: trigger browser download directly
const blob = new Blob([finalContent instanceof Uint8Array ? finalContent : finalContent], {
type: finalMimeType,
});
const url = URL.createObjectURL(blob);
const a = document.createElement("a");
a.href = url;
a.download = fileName;
a.click();
URL.revokeObjectURL(url);
}
};
};
}
async handleMessage(message, respond) {
if (message.type === "file-returned") {
// Collect file for caller
this.files.push({
fileName: message.fileName,
content: message.content,
mimeType: message.mimeType,
});
respond({ success: true });
}
}
/**
* Get collected files
*/
getFiles() {
return this.files;
}
/**
* Reset state for reuse
*/
reset() {
this.files = [];
}
getDescription() {
return "returnDownloadableFile(filename, content, mimeType?) - Create downloadable file for user (one-time download, not accessible later)";
}
}
//# sourceMappingURL=FileDownloadRuntimeProvider.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"FileDownloadRuntimeProvider.js","sourceRoot":"","sources":["../../../src/components/sandbox/FileDownloadRuntimeProvider.ts"],"names":[],"mappings":"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"}

View File

@@ -0,0 +1,19 @@
/**
* Generates sendRuntimeMessage() function for injection into execution contexts.
* Provides unified messaging API that works in both sandbox iframe and user script contexts.
*/
export type MessageType = "request-response" | "fire-and-forget";
export interface RuntimeMessageBridgeOptions {
context: "sandbox-iframe" | "user-script";
sandboxId: string;
}
export declare class RuntimeMessageBridge {
/**
* Generate sendRuntimeMessage() function as injectable string.
* Returns the function source code to be injected into target context.
*/
static generateBridgeCode(options: RuntimeMessageBridgeOptions): string;
private static generateSandboxBridge;
private static generateUserScriptBridge;
}
//# sourceMappingURL=RuntimeMessageBridge.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"RuntimeMessageBridge.d.ts","sourceRoot":"","sources":["../../../src/components/sandbox/RuntimeMessageBridge.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,MAAM,MAAM,WAAW,GAAG,kBAAkB,GAAG,iBAAiB,CAAC;AAEjE,MAAM,WAAW,2BAA2B;IAC3C,OAAO,EAAE,gBAAgB,GAAG,aAAa,CAAC;IAC1C,SAAS,EAAE,MAAM,CAAC;CAClB;AAGD,qBAAa,oBAAoB;IAChC;;;OAGG;IACH,MAAM,CAAC,kBAAkB,CAAC,OAAO,EAAE,2BAA2B,GAAG,MAAM;IAQvE,OAAO,CAAC,MAAM,CAAC,qBAAqB;IAwCpC,OAAO,CAAC,MAAM,CAAC,wBAAwB;CAevC"}

View File

@@ -0,0 +1,74 @@
/**
* Generates sendRuntimeMessage() function for injection into execution contexts.
* Provides unified messaging API that works in both sandbox iframe and user script contexts.
*/
// biome-ignore lint/complexity/noStaticOnlyClass: fine
export class RuntimeMessageBridge {
/**
* Generate sendRuntimeMessage() function as injectable string.
* Returns the function source code to be injected into target context.
*/
static generateBridgeCode(options) {
if (options.context === "sandbox-iframe") {
return RuntimeMessageBridge.generateSandboxBridge(options.sandboxId);
}
else {
return RuntimeMessageBridge.generateUserScriptBridge(options.sandboxId);
}
}
static generateSandboxBridge(sandboxId) {
// Returns stringified function that uses window.parent.postMessage
return `
window.__completionCallbacks = [];
window.sendRuntimeMessage = async (message) => {
const messageId = 'msg_' + Date.now() + '_' + Math.random().toString(36).substring(2, 9);
return new Promise((resolve, reject) => {
const handler = (e) => {
if (e.data.type === 'runtime-response' && e.data.messageId === messageId) {
window.removeEventListener('message', handler);
if (e.data.success) {
resolve(e.data);
} else {
reject(new Error(e.data.error || 'Operation failed'));
}
}
};
window.addEventListener('message', handler);
window.parent.postMessage({
...message,
sandboxId: ${JSON.stringify(sandboxId)},
messageId: messageId
}, '*');
// Timeout after 30s
setTimeout(() => {
window.removeEventListener('message', handler);
reject(new Error('Runtime message timeout'));
}, 30000);
});
};
window.onCompleted = (callback) => {
window.__completionCallbacks.push(callback);
};
`.trim();
}
static generateUserScriptBridge(sandboxId) {
// Returns stringified function that uses chrome.runtime.sendMessage
return `
window.__completionCallbacks = [];
window.sendRuntimeMessage = async (message) => {
return await chrome.runtime.sendMessage({
...message,
sandboxId: ${JSON.stringify(sandboxId)}
});
};
window.onCompleted = (callback) => {
window.__completionCallbacks.push(callback);
};
`.trim();
}
}
//# sourceMappingURL=RuntimeMessageBridge.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"RuntimeMessageBridge.js","sourceRoot":"","sources":["../../../src/components/sandbox/RuntimeMessageBridge.ts"],"names":[],"mappings":"AAAA;;;GAGG;AASH,uDAAuD;AACvD,MAAM,OAAO,oBAAoB;IAChC;;;OAGG;IACH,MAAM,CAAC,kBAAkB,CAAC,OAAoC;QAC7D,IAAI,OAAO,CAAC,OAAO,KAAK,gBAAgB,EAAE,CAAC;YAC1C,OAAO,oBAAoB,CAAC,qBAAqB,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;QACtE,CAAC;aAAM,CAAC;YACP,OAAO,oBAAoB,CAAC,wBAAwB,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;QACzE,CAAC;IACF,CAAC;IAEO,MAAM,CAAC,qBAAqB,CAAC,SAAiB;QACrD,mEAAmE;QACnE,OAAO;;;;;;;;;;;;;;;;;;;;;yBAqBgB,IAAI,CAAC,SAAS,CAAC,SAAS,CAAC;;;;;;;;;;;;;;CAcjD,CAAC,IAAI,EAAE,CAAC;IACR,CAAC;IAEO,MAAM,CAAC,wBAAwB,CAAC,SAAiB;QACxD,oEAAoE;QACpE,OAAO;;;;;qBAKY,IAAI,CAAC,SAAS,CAAC,SAAS,CAAC;;;;;;CAM7C,CAAC,IAAI,EAAE,CAAC;IACR,CAAC;CACD"}

View File

@@ -0,0 +1,65 @@
import type { SandboxRuntimeProvider } from "./SandboxRuntimeProvider.js";
/**
* Message consumer interface - components that want to receive messages from sandboxes
*/
export interface MessageConsumer {
/**
* Handle a message from a sandbox.
* All consumers receive all messages - decide internally what to handle.
*/
handleMessage(message: any): Promise<void>;
}
/**
* Centralized message router for all runtime communication.
*
* This singleton replaces all individual window.addEventListener("message") calls
* with a single global listener that routes messages to the appropriate handlers.
* Also handles user script messages from chrome.runtime.onUserScriptMessage.
*
* Benefits:
* - Single global listener instead of multiple independent listeners
* - Automatic cleanup when sandboxes are destroyed
* - Support for bidirectional communication (providers) and broadcasting (consumers)
* - Works with both sandbox iframes and user scripts
* - Clear lifecycle management
*/
export declare class RuntimeMessageRouter {
private sandboxes;
private messageListener;
private userScriptMessageListener;
/**
* Register a new sandbox with its runtime providers.
* Call this BEFORE creating the iframe (for sandbox contexts) or executing user script.
*/
registerSandbox(sandboxId: string, providers: SandboxRuntimeProvider[], consumers: MessageConsumer[]): void;
/**
* Update the iframe reference for a sandbox.
* Call this AFTER creating the iframe.
* This is needed so providers can send responses back to the sandbox.
*/
setSandboxIframe(sandboxId: string, iframe: HTMLIFrameElement): void;
/**
* Unregister a sandbox and remove all its consumers.
* Call this when the sandbox is destroyed.
*/
unregisterSandbox(sandboxId: string): void;
/**
* Add a message consumer for a sandbox.
* Consumers receive broadcast messages (console, execution-complete, etc.)
*/
addConsumer(sandboxId: string, consumer: MessageConsumer): void;
/**
* Remove a message consumer from a sandbox.
*/
removeConsumer(sandboxId: string, consumer: MessageConsumer): void;
/**
* Setup the global message listeners (called automatically)
*/
private setupListener;
}
/**
* Global singleton instance.
* Import this from wherever you need to interact with the message router.
*/
export declare const RUNTIME_MESSAGE_ROUTER: RuntimeMessageRouter;
//# sourceMappingURL=RuntimeMessageRouter.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"RuntimeMessageRouter.d.ts","sourceRoot":"","sources":["../../../src/components/sandbox/RuntimeMessageRouter.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,6BAA6B,CAAC;AAK1E;;GAEG;AACH,MAAM,WAAW,eAAe;IAC/B;;;OAGG;IACH,aAAa,CAAC,OAAO,EAAE,GAAG,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAC3C;AAYD;;;;;;;;;;;;;GAaG;AACH,qBAAa,oBAAoB;IAChC,OAAO,CAAC,SAAS,CAAqC;IACtD,OAAO,CAAC,eAAe,CAA4C;IACnE,OAAO,CAAC,yBAAyB,CAElB;IAEf;;;OAGG;IACH,eAAe,CAAC,SAAS,EAAE,MAAM,EAAE,SAAS,EAAE,sBAAsB,EAAE,EAAE,SAAS,EAAE,eAAe,EAAE,GAAG,IAAI;IAY3G;;;;OAIG;IACH,gBAAgB,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,iBAAiB,GAAG,IAAI;IAOpE;;;OAGG;IACH,iBAAiB,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI;IAmB1C;;;OAGG;IACH,WAAW,CAAC,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,eAAe,GAAG,IAAI;IAO/D;;OAEG;IACH,cAAc,CAAC,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,eAAe,GAAG,IAAI;IAOlE;;OAEG;IACH,OAAO,CAAC,aAAa;CAuFrB;AAED;;;GAGG;AACH,eAAO,MAAM,sBAAsB,sBAA6B,CAAC"}

View File

@@ -0,0 +1,168 @@
/**
* Centralized message router for all runtime communication.
*
* This singleton replaces all individual window.addEventListener("message") calls
* with a single global listener that routes messages to the appropriate handlers.
* Also handles user script messages from chrome.runtime.onUserScriptMessage.
*
* Benefits:
* - Single global listener instead of multiple independent listeners
* - Automatic cleanup when sandboxes are destroyed
* - Support for bidirectional communication (providers) and broadcasting (consumers)
* - Works with both sandbox iframes and user scripts
* - Clear lifecycle management
*/
export class RuntimeMessageRouter {
constructor() {
this.sandboxes = new Map();
this.messageListener = null;
this.userScriptMessageListener = null;
}
/**
* Register a new sandbox with its runtime providers.
* Call this BEFORE creating the iframe (for sandbox contexts) or executing user script.
*/
registerSandbox(sandboxId, providers, consumers) {
this.sandboxes.set(sandboxId, {
sandboxId,
iframe: null, // Will be set via setSandboxIframe() for sandbox contexts
providers,
consumers: new Set(consumers),
});
// Setup global listener if not already done
this.setupListener();
}
/**
* Update the iframe reference for a sandbox.
* Call this AFTER creating the iframe.
* This is needed so providers can send responses back to the sandbox.
*/
setSandboxIframe(sandboxId, iframe) {
const context = this.sandboxes.get(sandboxId);
if (context) {
context.iframe = iframe;
}
}
/**
* Unregister a sandbox and remove all its consumers.
* Call this when the sandbox is destroyed.
*/
unregisterSandbox(sandboxId) {
this.sandboxes.delete(sandboxId);
// If no more sandboxes, remove global listeners
if (this.sandboxes.size === 0) {
// Remove iframe listener
if (this.messageListener) {
window.removeEventListener("message", this.messageListener);
this.messageListener = null;
}
// Remove user script listener
if (this.userScriptMessageListener && typeof chrome !== "undefined" && chrome.runtime?.onUserScriptMessage) {
chrome.runtime.onUserScriptMessage.removeListener(this.userScriptMessageListener);
this.userScriptMessageListener = null;
}
}
}
/**
* Add a message consumer for a sandbox.
* Consumers receive broadcast messages (console, execution-complete, etc.)
*/
addConsumer(sandboxId, consumer) {
const context = this.sandboxes.get(sandboxId);
if (context) {
context.consumers.add(consumer);
}
}
/**
* Remove a message consumer from a sandbox.
*/
removeConsumer(sandboxId, consumer) {
const context = this.sandboxes.get(sandboxId);
if (context) {
context.consumers.delete(consumer);
}
}
/**
* Setup the global message listeners (called automatically)
*/
setupListener() {
// Setup sandbox iframe listener
if (!this.messageListener) {
this.messageListener = async (e) => {
const { sandboxId, messageId } = e.data;
if (!sandboxId)
return;
const context = this.sandboxes.get(sandboxId);
if (!context) {
return;
}
// Create respond() function for bidirectional communication
const respond = (response) => {
context.iframe?.contentWindow?.postMessage({
type: "runtime-response",
messageId,
sandboxId,
...response,
}, "*");
};
// 1. Try provider handlers first (for bidirectional comm)
for (const provider of context.providers) {
if (provider.handleMessage) {
await provider.handleMessage(e.data, respond);
// Don't stop - let consumers also handle the message
}
}
// 2. Broadcast to consumers (one-way messages or lifecycle events)
for (const consumer of context.consumers) {
await consumer.handleMessage(e.data);
// Don't stop - let all consumers see the message
}
};
window.addEventListener("message", this.messageListener);
}
// Setup user script message listener
if (!this.userScriptMessageListener) {
// Guard: check if we're in extension context
if (typeof chrome === "undefined" || !chrome.runtime?.onUserScriptMessage) {
return;
}
this.userScriptMessageListener = (message, _sender, sendResponse) => {
const { sandboxId } = message;
if (!sandboxId)
return false;
const context = this.sandboxes.get(sandboxId);
if (!context)
return false;
const respond = (response) => {
sendResponse({
...response,
sandboxId,
});
};
// Route to providers (async)
(async () => {
// 1. Try provider handlers first (for bidirectional comm)
for (const provider of context.providers) {
if (provider.handleMessage) {
await provider.handleMessage(message, respond);
// Don't stop - let consumers also handle the message
}
}
// 2. Broadcast to consumers (one-way messages or lifecycle events)
for (const consumer of context.consumers) {
await consumer.handleMessage(message);
// Don't stop - let all consumers see the message
}
})();
return true; // Indicates async response
};
chrome.runtime.onUserScriptMessage.addListener(this.userScriptMessageListener);
}
}
}
/**
* Global singleton instance.
* Import this from wherever you need to interact with the message router.
*/
export const RUNTIME_MESSAGE_ROUTER = new RuntimeMessageRouter();
//# sourceMappingURL=RuntimeMessageRouter.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"RuntimeMessageRouter.js","sourceRoot":"","sources":["../../../src/components/sandbox/RuntimeMessageRouter.ts"],"names":[],"mappings":"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"}

View File

@@ -0,0 +1,48 @@
/**
* Interface for providing runtime capabilities to sandboxed iframes.
* Each provider injects data and runtime functions into the sandbox context.
*/
export interface SandboxRuntimeProvider {
/**
* Returns data to inject into window scope.
* Keys become window properties (e.g., { attachments: [...] } -> window.attachments)
*/
getData(): Record<string, any>;
/**
* Returns a runtime function that will be stringified and executed in the sandbox.
* The function receives sandboxId and has access to data from getData() via window.
*
* IMPORTANT: This function will be converted to string via .toString() and injected
* into the sandbox, so it cannot reference external variables or imports.
*/
getRuntime(): (sandboxId: string) => void;
/**
* Optional message handler for bidirectional communication.
* All providers receive all messages - decide internally what to handle.
*
* @param message - The message from the sandbox
* @param respond - Function to send a response back to the sandbox
*/
handleMessage?(message: any, respond: (response: any) => void): Promise<void>;
/**
* Optional documentation describing what globals/functions this provider injects.
* This will be appended to tool descriptions dynamically so the LLM knows what's available.
*/
getDescription(): string;
/**
* Optional lifecycle callback invoked when sandbox execution starts.
* Providers can use this to track abort signals for cancellation of async operations.
*
* @param sandboxId - The unique identifier for this sandbox execution
* @param signal - Optional AbortSignal that will be triggered if execution is cancelled
*/
onExecutionStart?(sandboxId: string, signal?: AbortSignal): void;
/**
* Optional lifecycle callback invoked when sandbox execution ends (success, error, or abort).
* Providers can use this to clean up any resources associated with the sandbox.
*
* @param sandboxId - The unique identifier for this sandbox execution
*/
onExecutionEnd?(sandboxId: string): void;
}
//# sourceMappingURL=SandboxRuntimeProvider.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"SandboxRuntimeProvider.d.ts","sourceRoot":"","sources":["../../../src/components/sandbox/SandboxRuntimeProvider.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,MAAM,WAAW,sBAAsB;IACtC;;;OAGG;IACH,OAAO,IAAI,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IAE/B;;;;;;OAMG;IACH,UAAU,IAAI,CAAC,SAAS,EAAE,MAAM,KAAK,IAAI,CAAC;IAE1C;;;;;;OAMG;IACH,aAAa,CAAC,CAAC,OAAO,EAAE,GAAG,EAAE,OAAO,EAAE,CAAC,QAAQ,EAAE,GAAG,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAE9E;;;OAGG;IACH,cAAc,IAAI,MAAM,CAAC;IAEzB;;;;;;OAMG;IACH,gBAAgB,CAAC,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,WAAW,GAAG,IAAI,CAAC;IAEjE;;;;;OAKG;IACH,cAAc,CAAC,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;CACzC"}

View File

@@ -0,0 +1,2 @@
export {};
//# sourceMappingURL=SandboxRuntimeProvider.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"SandboxRuntimeProvider.js","sourceRoot":"","sources":["../../../src/components/sandbox/SandboxRuntimeProvider.ts"],"names":[],"mappings":""}