@elyracode/web-ui 0.9.9 → 0.9.11
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/CHANGELOG.md +10 -0
- package/dist/ChatPanel.d.ts +1 -1
- package/dist/ChatPanel.d.ts.map +1 -1
- package/dist/app.css +2 -2
- package/dist/components/AgentInterface.d.ts +1 -1
- package/dist/components/AgentInterface.d.ts.map +1 -1
- package/dist/components/AgentInterface.js +1 -1
- package/dist/components/AgentInterface.js.map +1 -1
- package/dist/components/MessageEditor.d.ts +2 -2
- package/dist/components/MessageEditor.d.ts.map +1 -1
- package/dist/components/Messages.d.ts +3 -3
- package/dist/components/Messages.d.ts.map +1 -1
- package/dist/components/Messages.js.map +1 -1
- package/dist/components/ProviderKeyInput.js +2 -2
- package/dist/components/ProviderKeyInput.js.map +1 -1
- package/dist/components/SandboxedIframe.d.ts +1 -1
- package/dist/components/SandboxedIframe.d.ts.map +1 -1
- package/dist/components/SandboxedIframe.js +1 -1
- package/dist/components/SandboxedIframe.js.map +1 -1
- package/dist/components/message-renderer-registry.js.map +1 -1
- package/dist/components/sandbox/ArtifactsRuntimeProvider.d.ts +4 -3
- package/dist/components/sandbox/ArtifactsRuntimeProvider.d.ts.map +1 -1
- package/dist/components/sandbox/ArtifactsRuntimeProvider.js +31 -31
- package/dist/components/sandbox/ArtifactsRuntimeProvider.js.map +1 -1
- package/dist/components/sandbox/AttachmentsRuntimeProvider.d.ts +1 -1
- package/dist/components/sandbox/AttachmentsRuntimeProvider.d.ts.map +1 -1
- package/dist/components/sandbox/AttachmentsRuntimeProvider.js +6 -5
- package/dist/components/sandbox/AttachmentsRuntimeProvider.js.map +1 -1
- package/dist/components/sandbox/ConsoleRuntimeProvider.d.ts +3 -2
- package/dist/components/sandbox/ConsoleRuntimeProvider.d.ts.map +1 -1
- package/dist/components/sandbox/ConsoleRuntimeProvider.js +12 -11
- package/dist/components/sandbox/ConsoleRuntimeProvider.js.map +1 -1
- package/dist/components/sandbox/FileDownloadRuntimeProvider.d.ts +3 -2
- package/dist/components/sandbox/FileDownloadRuntimeProvider.d.ts.map +1 -1
- package/dist/components/sandbox/FileDownloadRuntimeProvider.js +6 -4
- package/dist/components/sandbox/FileDownloadRuntimeProvider.js.map +1 -1
- package/dist/components/sandbox/RuntimeMessageRouter.d.ts +2 -1
- package/dist/components/sandbox/RuntimeMessageRouter.d.ts.map +1 -1
- package/dist/components/sandbox/RuntimeMessageRouter.js.map +1 -1
- package/dist/components/sandbox/SandboxRuntimeProvider.d.ts +3 -2
- package/dist/components/sandbox/SandboxRuntimeProvider.d.ts.map +1 -1
- package/dist/components/sandbox/runtime-messages.d.ts +140 -0
- package/dist/components/sandbox/runtime-messages.d.ts.map +1 -0
- package/dist/components/sandbox/runtime-messages.js +9 -0
- package/dist/components/sandbox/runtime-messages.js.map +1 -0
- package/dist/dialogs/AttachmentOverlay.d.ts.map +1 -1
- package/dist/dialogs/AttachmentOverlay.js +4 -4
- package/dist/dialogs/AttachmentOverlay.js.map +1 -1
- package/dist/dialogs/ModelSelector.d.ts +3 -3
- package/dist/dialogs/ModelSelector.d.ts.map +1 -1
- package/dist/dialogs/ModelSelector.js.map +1 -1
- package/dist/storage/stores/custom-providers-store.d.ts +2 -2
- package/dist/storage/stores/custom-providers-store.d.ts.map +1 -1
- package/dist/storage/types.d.ts +2 -2
- package/dist/storage/types.d.ts.map +1 -1
- package/dist/tools/artifacts/DocxArtifact.js +1 -1
- package/dist/tools/artifacts/DocxArtifact.js.map +1 -1
- package/dist/tools/artifacts/ExcelArtifact.js +1 -1
- package/dist/tools/artifacts/ExcelArtifact.js.map +1 -1
- package/dist/tools/artifacts/HtmlArtifact.js.map +1 -1
- package/dist/tools/artifacts/PdfArtifact.d.ts.map +1 -1
- package/dist/tools/artifacts/PdfArtifact.js +1 -1
- package/dist/tools/artifacts/PdfArtifact.js.map +1 -1
- package/dist/tools/artifacts/artifacts-tool-renderer.d.ts.map +1 -1
- package/dist/tools/artifacts/artifacts-tool-renderer.js.map +1 -1
- package/dist/tools/artifacts/artifacts.js.map +1 -1
- package/dist/tools/extract-document.js +1 -1
- package/dist/tools/extract-document.js.map +1 -1
- package/dist/tools/index.d.ts +1 -1
- package/dist/tools/index.d.ts.map +1 -1
- package/dist/tools/index.js.map +1 -1
- package/dist/tools/javascript-repl.js.map +1 -1
- package/dist/tools/renderer-registry.d.ts +4 -3
- package/dist/tools/renderer-registry.d.ts.map +1 -1
- package/dist/tools/renderer-registry.js.map +1 -1
- package/dist/tools/renderers/BashRenderer.d.ts.map +1 -1
- package/dist/tools/renderers/BashRenderer.js.map +1 -1
- package/dist/tools/renderers/CalculateRenderer.d.ts.map +1 -1
- package/dist/tools/renderers/CalculateRenderer.js.map +1 -1
- package/dist/tools/renderers/DefaultRenderer.d.ts +1 -1
- package/dist/tools/renderers/DefaultRenderer.d.ts.map +1 -1
- package/dist/tools/renderers/DefaultRenderer.js.map +1 -1
- package/dist/tools/renderers/GetCurrentTimeRenderer.d.ts.map +1 -1
- package/dist/tools/renderers/GetCurrentTimeRenderer.js.map +1 -1
- package/dist/tools/types.d.ts +1 -1
- package/dist/tools/types.d.ts.map +1 -1
- package/dist/utils/attachment-utils.js +1 -1
- package/dist/utils/attachment-utils.js.map +1 -1
- package/dist/utils/format.d.ts +2 -2
- package/dist/utils/format.d.ts.map +1 -1
- package/dist/utils/format.js.map +1 -1
- package/dist/utils/model-discovery.d.ts +6 -6
- package/dist/utils/model-discovery.d.ts.map +1 -1
- package/dist/utils/model-discovery.js +3 -3
- package/dist/utils/model-discovery.js.map +1 -1
- package/dist/utils/proxy-utils.d.ts +1 -1
- package/example/package.json +23 -23
- package/example/src/css-modules.d.ts +3 -0
- package/package.json +6 -6
- package/src/ChatPanel.ts +1 -1
- package/src/components/AgentInterface.ts +5 -5
- package/src/components/MessageEditor.ts +2 -2
- package/src/components/Messages.ts +6 -6
- package/src/components/ProviderKeyInput.ts +3 -3
- package/src/components/SandboxedIframe.ts +7 -6
- package/src/components/message-renderer-registry.ts +1 -1
- package/src/components/sandbox/ArtifactsRuntimeProvider.ts +40 -44
- package/src/components/sandbox/AttachmentsRuntimeProvider.ts +9 -7
- package/src/components/sandbox/ConsoleRuntimeProvider.ts +19 -17
- package/src/components/sandbox/FileDownloadRuntimeProvider.ts +11 -7
- package/src/components/sandbox/RuntimeMessageRouter.ts +7 -8
- package/src/components/sandbox/SandboxRuntimeProvider.ts +4 -2
- package/src/components/sandbox/runtime-messages.ts +146 -0
- package/src/dialogs/AttachmentOverlay.ts +12 -11
- package/src/dialogs/CustomProviderDialog.ts +2 -2
- package/src/dialogs/ModelSelector.ts +11 -11
- package/src/storage/stores/custom-providers-store.ts +2 -2
- package/src/storage/types.ts +2 -2
- package/src/tools/artifacts/DocxArtifact.ts +2 -2
- package/src/tools/artifacts/ExcelArtifact.ts +3 -3
- package/src/tools/artifacts/HtmlArtifact.ts +1 -1
- package/src/tools/artifacts/PdfArtifact.ts +5 -4
- package/src/tools/artifacts/artifacts-tool-renderer.ts +4 -4
- package/src/tools/artifacts/artifacts.ts +4 -4
- package/src/tools/extract-document.ts +6 -6
- package/src/tools/index.ts +1 -1
- package/src/tools/javascript-repl.ts +3 -3
- package/src/tools/renderer-registry.ts +5 -5
- package/src/tools/renderers/BashRenderer.ts +3 -3
- package/src/tools/renderers/CalculateRenderer.ts +3 -3
- package/src/tools/renderers/DefaultRenderer.ts +5 -5
- package/src/tools/renderers/GetCurrentTimeRenderer.ts +5 -5
- package/src/tools/types.ts +1 -1
- package/src/utils/attachment-utils.ts +10 -3
- package/src/utils/format.ts +2 -2
- package/src/utils/model-discovery.ts +35 -22
- package/src/utils/proxy-utils.ts +1 -1
|
@@ -3,13 +3,14 @@ import {
|
|
|
3
3
|
ARTIFACTS_RUNTIME_PROVIDER_DESCRIPTION_RO,
|
|
4
4
|
ARTIFACTS_RUNTIME_PROVIDER_DESCRIPTION_RW,
|
|
5
5
|
} from "../../prompts/prompts.js";
|
|
6
|
+
import type { RuntimeMessage, RuntimeResponse, SandboxRuntimeWindow } from "./runtime-messages.js";
|
|
6
7
|
import type { SandboxRuntimeProvider } from "./SandboxRuntimeProvider.js";
|
|
7
8
|
|
|
8
9
|
// Define minimal interface for ArtifactsPanel to avoid circular dependencies
|
|
9
10
|
interface ArtifactsPanelLike {
|
|
10
11
|
artifacts: Map<string, { content: string }>;
|
|
11
12
|
tool: {
|
|
12
|
-
execute(toolCallId: string, args: { command: string; filename: string; content?: string }): Promise<
|
|
13
|
+
execute(toolCallId: string, args: { command: string; filename: string; content?: string }): Promise<unknown>;
|
|
13
14
|
};
|
|
14
15
|
}
|
|
15
16
|
|
|
@@ -41,7 +42,7 @@ export class ArtifactsRuntimeProvider implements SandboxRuntimeProvider {
|
|
|
41
42
|
}
|
|
42
43
|
}
|
|
43
44
|
|
|
44
|
-
getData(): Record<string,
|
|
45
|
+
getData(): Record<string, unknown> {
|
|
45
46
|
// Inject artifact snapshot for offline mode
|
|
46
47
|
const snapshot: Record<string, string> = {};
|
|
47
48
|
this.artifactsPanel.artifacts.forEach((artifact, filename) => {
|
|
@@ -53,44 +54,45 @@ export class ArtifactsRuntimeProvider implements SandboxRuntimeProvider {
|
|
|
53
54
|
getRuntime(): (sandboxId: string) => void {
|
|
54
55
|
// This function will be stringified, so no external references!
|
|
55
56
|
return (_sandboxId: string) => {
|
|
57
|
+
const win = window as unknown as SandboxRuntimeWindow;
|
|
56
58
|
// Auto-parse/stringify for .json files
|
|
57
59
|
const isJsonFile = (filename: string) => filename.endsWith(".json");
|
|
58
60
|
|
|
59
|
-
|
|
61
|
+
win.listArtifacts = async (): Promise<string[]> => {
|
|
60
62
|
// Online: ask extension
|
|
61
|
-
if (
|
|
62
|
-
const response = await
|
|
63
|
+
if (win.sendRuntimeMessage) {
|
|
64
|
+
const response = await win.sendRuntimeMessage({
|
|
63
65
|
type: "artifact-operation",
|
|
64
66
|
action: "list",
|
|
65
67
|
});
|
|
66
68
|
if (!response.success) throw new Error(response.error);
|
|
67
|
-
return response.result;
|
|
69
|
+
return response.result as string[];
|
|
68
70
|
}
|
|
69
71
|
// Offline: return snapshot keys
|
|
70
72
|
else {
|
|
71
|
-
return Object.keys(
|
|
73
|
+
return Object.keys(win.artifacts || {});
|
|
72
74
|
}
|
|
73
75
|
};
|
|
74
76
|
|
|
75
|
-
|
|
77
|
+
win.getArtifact = async (filename: string): Promise<unknown> => {
|
|
76
78
|
let content: string;
|
|
77
79
|
|
|
78
80
|
// Online: ask extension
|
|
79
|
-
if (
|
|
80
|
-
const response = await
|
|
81
|
+
if (win.sendRuntimeMessage) {
|
|
82
|
+
const response = await win.sendRuntimeMessage({
|
|
81
83
|
type: "artifact-operation",
|
|
82
84
|
action: "get",
|
|
83
85
|
filename,
|
|
84
86
|
});
|
|
85
87
|
if (!response.success) throw new Error(response.error);
|
|
86
|
-
content = response.result;
|
|
88
|
+
content = response.result as string;
|
|
87
89
|
}
|
|
88
90
|
// Offline: read snapshot
|
|
89
91
|
else {
|
|
90
|
-
if (!
|
|
92
|
+
if (!win.artifacts?.[filename]) {
|
|
91
93
|
throw new Error(`Artifact not found (offline mode): ${filename}`);
|
|
92
94
|
}
|
|
93
|
-
content =
|
|
95
|
+
content = win.artifacts[filename];
|
|
94
96
|
}
|
|
95
97
|
|
|
96
98
|
// Auto-parse .json files
|
|
@@ -104,12 +106,8 @@ export class ArtifactsRuntimeProvider implements SandboxRuntimeProvider {
|
|
|
104
106
|
return content;
|
|
105
107
|
};
|
|
106
108
|
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
content: any,
|
|
110
|
-
mimeType?: string,
|
|
111
|
-
): Promise<void> => {
|
|
112
|
-
if (!(window as any).sendRuntimeMessage) {
|
|
109
|
+
win.createOrUpdateArtifact = async (filename: string, content: unknown, mimeType?: string): Promise<void> => {
|
|
110
|
+
if (!win.sendRuntimeMessage) {
|
|
113
111
|
throw new Error("Cannot create/update artifacts in offline mode (read-only)");
|
|
114
112
|
}
|
|
115
113
|
|
|
@@ -121,7 +119,7 @@ export class ArtifactsRuntimeProvider implements SandboxRuntimeProvider {
|
|
|
121
119
|
finalContent = JSON.stringify(content, null, 2);
|
|
122
120
|
}
|
|
123
121
|
|
|
124
|
-
const response = await
|
|
122
|
+
const response = await win.sendRuntimeMessage({
|
|
125
123
|
type: "artifact-operation",
|
|
126
124
|
action: "createOrUpdate",
|
|
127
125
|
filename,
|
|
@@ -131,12 +129,12 @@ export class ArtifactsRuntimeProvider implements SandboxRuntimeProvider {
|
|
|
131
129
|
if (!response.success) throw new Error(response.error);
|
|
132
130
|
};
|
|
133
131
|
|
|
134
|
-
|
|
135
|
-
if (!
|
|
132
|
+
win.deleteArtifact = async (filename: string): Promise<void> => {
|
|
133
|
+
if (!win.sendRuntimeMessage) {
|
|
136
134
|
throw new Error("Cannot delete artifacts in offline mode (read-only)");
|
|
137
135
|
}
|
|
138
136
|
|
|
139
|
-
const response = await
|
|
137
|
+
const response = await win.sendRuntimeMessage({
|
|
140
138
|
type: "artifact-operation",
|
|
141
139
|
action: "delete",
|
|
142
140
|
filename,
|
|
@@ -146,15 +144,13 @@ export class ArtifactsRuntimeProvider implements SandboxRuntimeProvider {
|
|
|
146
144
|
};
|
|
147
145
|
}
|
|
148
146
|
|
|
149
|
-
async handleMessage(message:
|
|
147
|
+
async handleMessage(message: RuntimeMessage, respond: (response: RuntimeResponse) => void): Promise<void> {
|
|
150
148
|
if (message.type !== "artifact-operation") {
|
|
151
149
|
return;
|
|
152
150
|
}
|
|
153
151
|
|
|
154
|
-
const { action, filename, content } = message;
|
|
155
|
-
|
|
156
152
|
try {
|
|
157
|
-
switch (action) {
|
|
153
|
+
switch (message.action) {
|
|
158
154
|
case "list": {
|
|
159
155
|
const filenames = Array.from(this.artifactsPanel.artifacts.keys());
|
|
160
156
|
respond({ success: true, result: filenames });
|
|
@@ -162,9 +158,9 @@ export class ArtifactsRuntimeProvider implements SandboxRuntimeProvider {
|
|
|
162
158
|
}
|
|
163
159
|
|
|
164
160
|
case "get": {
|
|
165
|
-
const artifact = this.artifactsPanel.artifacts.get(filename);
|
|
161
|
+
const artifact = this.artifactsPanel.artifacts.get(message.filename);
|
|
166
162
|
if (!artifact) {
|
|
167
|
-
respond({ success: false, error: `Artifact not found: ${filename}` });
|
|
163
|
+
respond({ success: false, error: `Artifact not found: ${message.filename}` });
|
|
168
164
|
} else {
|
|
169
165
|
respond({ success: true, result: artifact.content });
|
|
170
166
|
}
|
|
@@ -173,26 +169,26 @@ export class ArtifactsRuntimeProvider implements SandboxRuntimeProvider {
|
|
|
173
169
|
|
|
174
170
|
case "createOrUpdate": {
|
|
175
171
|
try {
|
|
176
|
-
const exists = this.artifactsPanel.artifacts.has(filename);
|
|
172
|
+
const exists = this.artifactsPanel.artifacts.has(message.filename);
|
|
177
173
|
const command = exists ? "rewrite" : "create";
|
|
178
174
|
const action = exists ? "update" : "create";
|
|
179
175
|
|
|
180
176
|
await this.artifactsPanel.tool.execute("", {
|
|
181
177
|
command,
|
|
182
|
-
filename,
|
|
183
|
-
content,
|
|
178
|
+
filename: message.filename,
|
|
179
|
+
content: message.content,
|
|
184
180
|
});
|
|
185
181
|
this.pushAgentMessage({
|
|
186
182
|
role: "artifact",
|
|
187
183
|
action,
|
|
188
|
-
filename,
|
|
189
|
-
content,
|
|
190
|
-
...(action === "create" && { title: filename }),
|
|
184
|
+
filename: message.filename,
|
|
185
|
+
content: message.content,
|
|
186
|
+
...(action === "create" && { title: message.filename }),
|
|
191
187
|
timestamp: new Date().toISOString(),
|
|
192
188
|
});
|
|
193
189
|
respond({ success: true });
|
|
194
|
-
} catch (err
|
|
195
|
-
respond({ success: false, error: err.message });
|
|
190
|
+
} catch (err) {
|
|
191
|
+
respond({ success: false, error: err instanceof Error ? err.message : String(err) });
|
|
196
192
|
}
|
|
197
193
|
break;
|
|
198
194
|
}
|
|
@@ -201,26 +197,26 @@ export class ArtifactsRuntimeProvider implements SandboxRuntimeProvider {
|
|
|
201
197
|
try {
|
|
202
198
|
await this.artifactsPanel.tool.execute("", {
|
|
203
199
|
command: "delete",
|
|
204
|
-
filename,
|
|
200
|
+
filename: message.filename,
|
|
205
201
|
});
|
|
206
202
|
this.pushAgentMessage({
|
|
207
203
|
role: "artifact",
|
|
208
204
|
action: "delete",
|
|
209
|
-
filename,
|
|
205
|
+
filename: message.filename,
|
|
210
206
|
timestamp: new Date().toISOString(),
|
|
211
207
|
});
|
|
212
208
|
respond({ success: true });
|
|
213
|
-
} catch (err
|
|
214
|
-
respond({ success: false, error: err.message });
|
|
209
|
+
} catch (err) {
|
|
210
|
+
respond({ success: false, error: err instanceof Error ? err.message : String(err) });
|
|
215
211
|
}
|
|
216
212
|
break;
|
|
217
213
|
}
|
|
218
214
|
|
|
219
215
|
default:
|
|
220
|
-
respond({ success: false, error:
|
|
216
|
+
respond({ success: false, error: "Unknown artifact action" });
|
|
221
217
|
}
|
|
222
|
-
} catch (error
|
|
223
|
-
respond({ success: false, error: error.message });
|
|
218
|
+
} catch (error) {
|
|
219
|
+
respond({ success: false, error: error instanceof Error ? error.message : String(error) });
|
|
224
220
|
}
|
|
225
221
|
}
|
|
226
222
|
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { ATTACHMENTS_RUNTIME_DESCRIPTION } from "../../prompts/prompts.js";
|
|
2
2
|
import type { Attachment } from "../../utils/attachment-utils.js";
|
|
3
|
+
import type { SandboxAttachment, SandboxRuntimeWindow } from "./runtime-messages.js";
|
|
3
4
|
import type { SandboxRuntimeProvider } from "./SandboxRuntimeProvider.js";
|
|
4
5
|
|
|
5
6
|
/**
|
|
@@ -12,7 +13,7 @@ import type { SandboxRuntimeProvider } from "./SandboxRuntimeProvider.js";
|
|
|
12
13
|
export class AttachmentsRuntimeProvider implements SandboxRuntimeProvider {
|
|
13
14
|
constructor(private attachments: Attachment[]) {}
|
|
14
15
|
|
|
15
|
-
getData(): Record<string,
|
|
16
|
+
getData(): Record<string, unknown> {
|
|
16
17
|
const attachmentsData = this.attachments.map((a) => ({
|
|
17
18
|
id: a.id,
|
|
18
19
|
fileName: a.fileName,
|
|
@@ -30,16 +31,17 @@ export class AttachmentsRuntimeProvider implements SandboxRuntimeProvider {
|
|
|
30
31
|
// These functions read directly from window.attachments
|
|
31
32
|
// Works both online AND offline (no messaging needed!)
|
|
32
33
|
return (_sandboxId: string) => {
|
|
33
|
-
|
|
34
|
-
|
|
34
|
+
const win = window as unknown as SandboxRuntimeWindow;
|
|
35
|
+
win.listAttachments = () =>
|
|
36
|
+
(win.attachments || []).map((a: SandboxAttachment) => ({
|
|
35
37
|
id: a.id,
|
|
36
38
|
fileName: a.fileName,
|
|
37
39
|
mimeType: a.mimeType,
|
|
38
40
|
size: a.size,
|
|
39
41
|
}));
|
|
40
42
|
|
|
41
|
-
|
|
42
|
-
const a = (
|
|
43
|
+
win.readTextAttachment = (attachmentId: string) => {
|
|
44
|
+
const a = (win.attachments || []).find((x: SandboxAttachment) => x.id === attachmentId);
|
|
43
45
|
if (!a) throw new Error(`Attachment not found: ${attachmentId}`);
|
|
44
46
|
if (a.extractedText) return a.extractedText;
|
|
45
47
|
try {
|
|
@@ -49,8 +51,8 @@ export class AttachmentsRuntimeProvider implements SandboxRuntimeProvider {
|
|
|
49
51
|
}
|
|
50
52
|
};
|
|
51
53
|
|
|
52
|
-
|
|
53
|
-
const a = (
|
|
54
|
+
win.readBinaryAttachment = (attachmentId: string) => {
|
|
55
|
+
const a = (win.attachments || []).find((x: SandboxAttachment) => x.id === attachmentId);
|
|
54
56
|
if (!a) throw new Error(`Attachment not found: ${attachmentId}`);
|
|
55
57
|
const bin = atob(a.content);
|
|
56
58
|
const bytes = new Uint8Array(bin.length);
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { RuntimeMessage, RuntimeResponse, SandboxRuntimeWindow } from "./runtime-messages.js";
|
|
1
2
|
import type { SandboxRuntimeProvider } from "./SandboxRuntimeProvider.js";
|
|
2
3
|
|
|
3
4
|
export interface ConsoleLog {
|
|
@@ -18,7 +19,7 @@ export class ConsoleRuntimeProvider implements SandboxRuntimeProvider {
|
|
|
18
19
|
private completionError: { message: string; stack: string } | null = null;
|
|
19
20
|
private completed = false;
|
|
20
21
|
|
|
21
|
-
getData(): Record<string,
|
|
22
|
+
getData(): Record<string, unknown> {
|
|
22
23
|
// No data needed
|
|
23
24
|
return {};
|
|
24
25
|
}
|
|
@@ -29,10 +30,11 @@ export class ConsoleRuntimeProvider implements SandboxRuntimeProvider {
|
|
|
29
30
|
|
|
30
31
|
getRuntime(): (sandboxId: string) => void {
|
|
31
32
|
return (_sandboxId: string) => {
|
|
33
|
+
const win = window as unknown as SandboxRuntimeWindow;
|
|
32
34
|
// Store truly original console methods on first wrap only
|
|
33
35
|
// This prevents accumulation of wrapper functions across multiple executions
|
|
34
|
-
if (!
|
|
35
|
-
|
|
36
|
+
if (!win.__originalConsole) {
|
|
37
|
+
win.__originalConsole = {
|
|
36
38
|
log: console.log.bind(console),
|
|
37
39
|
error: console.error.bind(console),
|
|
38
40
|
warn: console.warn.bind(console),
|
|
@@ -41,13 +43,13 @@ export class ConsoleRuntimeProvider implements SandboxRuntimeProvider {
|
|
|
41
43
|
}
|
|
42
44
|
|
|
43
45
|
// Always use the truly original console, not the current (possibly wrapped) one
|
|
44
|
-
const originalConsole =
|
|
46
|
+
const originalConsole = win.__originalConsole;
|
|
45
47
|
|
|
46
48
|
// Track pending send promises to wait for them in onCompleted
|
|
47
|
-
const pendingSends: Promise<
|
|
49
|
+
const pendingSends: Promise<unknown>[] = [];
|
|
48
50
|
|
|
49
|
-
["log", "error", "warn", "info"].forEach((method) => {
|
|
50
|
-
|
|
51
|
+
(["log", "error", "warn", "info"] as const).forEach((method) => {
|
|
52
|
+
console[method] = (...args: unknown[]) => {
|
|
51
53
|
const text = args
|
|
52
54
|
.map((arg) => {
|
|
53
55
|
try {
|
|
@@ -59,11 +61,11 @@ export class ConsoleRuntimeProvider implements SandboxRuntimeProvider {
|
|
|
59
61
|
.join(" ");
|
|
60
62
|
|
|
61
63
|
// Always log locally too (using truly original console)
|
|
62
|
-
|
|
64
|
+
originalConsole[method].apply(console, args);
|
|
63
65
|
|
|
64
66
|
// Send immediately and track the promise (only in extension context)
|
|
65
|
-
if (
|
|
66
|
-
const sendPromise =
|
|
67
|
+
if (win.sendRuntimeMessage) {
|
|
68
|
+
const sendPromise = win
|
|
67
69
|
.sendRuntimeMessage({
|
|
68
70
|
type: "console",
|
|
69
71
|
method,
|
|
@@ -77,8 +79,8 @@ export class ConsoleRuntimeProvider implements SandboxRuntimeProvider {
|
|
|
77
79
|
});
|
|
78
80
|
|
|
79
81
|
// Register completion callback to wait for all pending sends
|
|
80
|
-
if (
|
|
81
|
-
|
|
82
|
+
if (win.onCompleted) {
|
|
83
|
+
win.onCompleted(async (_success: boolean) => {
|
|
82
84
|
// Wait for all pending console sends to complete
|
|
83
85
|
if (pendingSends.length > 0) {
|
|
84
86
|
await Promise.all(pendingSends);
|
|
@@ -111,20 +113,20 @@ export class ConsoleRuntimeProvider implements SandboxRuntimeProvider {
|
|
|
111
113
|
|
|
112
114
|
// Expose complete() method for user code to call
|
|
113
115
|
let completionSent = false;
|
|
114
|
-
|
|
116
|
+
win.complete = async (error?: { message: string; stack: string } | null, returnValue?: unknown) => {
|
|
115
117
|
if (completionSent) return;
|
|
116
118
|
completionSent = true;
|
|
117
119
|
|
|
118
120
|
const finalError = error || lastError;
|
|
119
121
|
|
|
120
|
-
if (
|
|
122
|
+
if (win.sendRuntimeMessage) {
|
|
121
123
|
if (finalError) {
|
|
122
|
-
await
|
|
124
|
+
await win.sendRuntimeMessage({
|
|
123
125
|
type: "execution-error",
|
|
124
126
|
error: finalError,
|
|
125
127
|
});
|
|
126
128
|
} else {
|
|
127
|
-
await
|
|
129
|
+
await win.sendRuntimeMessage({
|
|
128
130
|
type: "execution-complete",
|
|
129
131
|
returnValue,
|
|
130
132
|
});
|
|
@@ -134,7 +136,7 @@ export class ConsoleRuntimeProvider implements SandboxRuntimeProvider {
|
|
|
134
136
|
};
|
|
135
137
|
}
|
|
136
138
|
|
|
137
|
-
async handleMessage(message:
|
|
139
|
+
async handleMessage(message: RuntimeMessage, respond: (response: RuntimeResponse) => void): Promise<void> {
|
|
138
140
|
if (message.type === "console") {
|
|
139
141
|
// Collect console output
|
|
140
142
|
this.logs.push({
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { RuntimeMessage, RuntimeResponse, SandboxRuntimeWindow } from "./runtime-messages.js";
|
|
1
2
|
import type { SandboxRuntimeProvider } from "./SandboxRuntimeProvider.js";
|
|
2
3
|
|
|
3
4
|
export interface DownloadableFile {
|
|
@@ -17,15 +18,16 @@ export interface DownloadableFile {
|
|
|
17
18
|
export class FileDownloadRuntimeProvider implements SandboxRuntimeProvider {
|
|
18
19
|
private files: DownloadableFile[] = [];
|
|
19
20
|
|
|
20
|
-
getData(): Record<string,
|
|
21
|
+
getData(): Record<string, unknown> {
|
|
21
22
|
// No data needed
|
|
22
23
|
return {};
|
|
23
24
|
}
|
|
24
25
|
|
|
25
26
|
getRuntime(): (sandboxId: string) => void {
|
|
26
27
|
return (_sandboxId: string) => {
|
|
27
|
-
|
|
28
|
-
|
|
28
|
+
const win = window as unknown as SandboxRuntimeWindow;
|
|
29
|
+
win.returnDownloadableFile = async (fileName: string, content: unknown, mimeType?: string) => {
|
|
30
|
+
let finalContent: string | Uint8Array, finalMimeType: string;
|
|
29
31
|
|
|
30
32
|
if (content instanceof Blob) {
|
|
31
33
|
const arrayBuffer = await content.arrayBuffer();
|
|
@@ -53,8 +55,8 @@ export class FileDownloadRuntimeProvider implements SandboxRuntimeProvider {
|
|
|
53
55
|
}
|
|
54
56
|
|
|
55
57
|
// Send to extension if in extension context (online mode)
|
|
56
|
-
if (
|
|
57
|
-
const response = await
|
|
58
|
+
if (win.sendRuntimeMessage) {
|
|
59
|
+
const response = await win.sendRuntimeMessage({
|
|
58
60
|
type: "file-returned",
|
|
59
61
|
fileName,
|
|
60
62
|
content: finalContent,
|
|
@@ -63,7 +65,9 @@ export class FileDownloadRuntimeProvider implements SandboxRuntimeProvider {
|
|
|
63
65
|
if (response.error) throw new Error(response.error);
|
|
64
66
|
} else {
|
|
65
67
|
// Offline mode: trigger browser download directly
|
|
66
|
-
const
|
|
68
|
+
const blobPart: BlobPart =
|
|
69
|
+
typeof finalContent === "string" ? finalContent : new Uint8Array(finalContent);
|
|
70
|
+
const blob = new Blob([blobPart], {
|
|
67
71
|
type: finalMimeType,
|
|
68
72
|
});
|
|
69
73
|
const url = URL.createObjectURL(blob);
|
|
@@ -77,7 +81,7 @@ export class FileDownloadRuntimeProvider implements SandboxRuntimeProvider {
|
|
|
77
81
|
};
|
|
78
82
|
}
|
|
79
83
|
|
|
80
|
-
async handleMessage(message:
|
|
84
|
+
async handleMessage(message: RuntimeMessage, respond: (response: RuntimeResponse) => void): Promise<void> {
|
|
81
85
|
if (message.type === "file-returned") {
|
|
82
86
|
// Collect file for caller
|
|
83
87
|
this.files.push({
|
|
@@ -1,7 +1,8 @@
|
|
|
1
|
+
import type { ChromeApi, RuntimeMessage, RuntimeResponse, UserScriptMessageListener } from "./runtime-messages.js";
|
|
1
2
|
import type { SandboxRuntimeProvider } from "./SandboxRuntimeProvider.js";
|
|
2
3
|
|
|
3
4
|
// Type declaration for chrome extension API (when available)
|
|
4
|
-
declare const chrome:
|
|
5
|
+
declare const chrome: ChromeApi | undefined;
|
|
5
6
|
|
|
6
7
|
/**
|
|
7
8
|
* Message consumer interface - components that want to receive messages from sandboxes
|
|
@@ -11,7 +12,7 @@ export interface MessageConsumer {
|
|
|
11
12
|
* Handle a message from a sandbox.
|
|
12
13
|
* All consumers receive all messages - decide internally what to handle.
|
|
13
14
|
*/
|
|
14
|
-
handleMessage(message:
|
|
15
|
+
handleMessage(message: RuntimeMessage): Promise<void>;
|
|
15
16
|
}
|
|
16
17
|
|
|
17
18
|
/**
|
|
@@ -41,9 +42,7 @@ interface SandboxContext {
|
|
|
41
42
|
export class RuntimeMessageRouter {
|
|
42
43
|
private sandboxes = new Map<string, SandboxContext>();
|
|
43
44
|
private messageListener: ((e: MessageEvent) => void) | null = null;
|
|
44
|
-
private userScriptMessageListener:
|
|
45
|
-
| ((message: any, sender: any, sendResponse: (response: any) => void) => boolean)
|
|
46
|
-
| null = null;
|
|
45
|
+
private userScriptMessageListener: UserScriptMessageListener | null = null;
|
|
47
46
|
|
|
48
47
|
/**
|
|
49
48
|
* Register a new sandbox with its runtime providers.
|
|
@@ -133,7 +132,7 @@ export class RuntimeMessageRouter {
|
|
|
133
132
|
}
|
|
134
133
|
|
|
135
134
|
// Create respond() function for bidirectional communication
|
|
136
|
-
const respond = (response:
|
|
135
|
+
const respond = (response: RuntimeResponse) => {
|
|
137
136
|
context.iframe?.contentWindow?.postMessage(
|
|
138
137
|
{
|
|
139
138
|
type: "runtime-response",
|
|
@@ -170,14 +169,14 @@ export class RuntimeMessageRouter {
|
|
|
170
169
|
return;
|
|
171
170
|
}
|
|
172
171
|
|
|
173
|
-
this.userScriptMessageListener = (message
|
|
172
|
+
this.userScriptMessageListener = (message, _sender, sendResponse) => {
|
|
174
173
|
const { sandboxId } = message;
|
|
175
174
|
if (!sandboxId) return false;
|
|
176
175
|
|
|
177
176
|
const context = this.sandboxes.get(sandboxId);
|
|
178
177
|
if (!context) return false;
|
|
179
178
|
|
|
180
|
-
const respond = (response:
|
|
179
|
+
const respond = (response: RuntimeResponse) => {
|
|
181
180
|
sendResponse({
|
|
182
181
|
...response,
|
|
183
182
|
sandboxId,
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import type { RuntimeMessage, RuntimeResponse } from "./runtime-messages.js";
|
|
2
|
+
|
|
1
3
|
/**
|
|
2
4
|
* Interface for providing runtime capabilities to sandboxed iframes.
|
|
3
5
|
* Each provider injects data and runtime functions into the sandbox context.
|
|
@@ -7,7 +9,7 @@ export interface SandboxRuntimeProvider {
|
|
|
7
9
|
* Returns data to inject into window scope.
|
|
8
10
|
* Keys become window properties (e.g., { attachments: [...] } -> window.attachments)
|
|
9
11
|
*/
|
|
10
|
-
getData(): Record<string,
|
|
12
|
+
getData(): Record<string, unknown>;
|
|
11
13
|
|
|
12
14
|
/**
|
|
13
15
|
* Returns a runtime function that will be stringified and executed in the sandbox.
|
|
@@ -25,7 +27,7 @@ export interface SandboxRuntimeProvider {
|
|
|
25
27
|
* @param message - The message from the sandbox
|
|
26
28
|
* @param respond - Function to send a response back to the sandbox
|
|
27
29
|
*/
|
|
28
|
-
handleMessage?(message:
|
|
30
|
+
handleMessage?(message: RuntimeMessage, respond: (response: RuntimeResponse) => void): Promise<void>;
|
|
29
31
|
|
|
30
32
|
/**
|
|
31
33
|
* Optional documentation describing what globals/functions this provider injects.
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared types for the sandbox runtime messaging protocol.
|
|
3
|
+
*
|
|
4
|
+
* These describe the message envelopes exchanged between the host page
|
|
5
|
+
* (SandboxedIframe / RuntimeMessageRouter / *RuntimeProvider classes) and the
|
|
6
|
+
* sandboxed execution context (iframe or user script).
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
/** Fields attached to every message routed through the runtime bridge. */
|
|
10
|
+
export interface RuntimeMessageEnvelope {
|
|
11
|
+
sandboxId?: string;
|
|
12
|
+
messageId?: string;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/** Artifact CRUD request issued from sandboxed code. */
|
|
16
|
+
export type ArtifactOperationMessage =
|
|
17
|
+
| { type: "artifact-operation"; action: "list" }
|
|
18
|
+
| { type: "artifact-operation"; action: "get"; filename: string }
|
|
19
|
+
| { type: "artifact-operation"; action: "delete"; filename: string }
|
|
20
|
+
| {
|
|
21
|
+
type: "artifact-operation";
|
|
22
|
+
action: "createOrUpdate";
|
|
23
|
+
filename: string;
|
|
24
|
+
content: string;
|
|
25
|
+
mimeType?: string;
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
/** Captured console output forwarded from sandboxed code. */
|
|
29
|
+
export interface ConsoleRuntimeMessage {
|
|
30
|
+
type: "console";
|
|
31
|
+
method: string;
|
|
32
|
+
text: string;
|
|
33
|
+
args?: unknown[];
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/** A one-time downloadable file returned from sandboxed code. */
|
|
37
|
+
export interface FileReturnedMessage {
|
|
38
|
+
type: "file-returned";
|
|
39
|
+
fileName: string;
|
|
40
|
+
content: string | Uint8Array;
|
|
41
|
+
mimeType: string;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/** Successful execution lifecycle completion. */
|
|
45
|
+
export interface ExecutionCompleteMessage {
|
|
46
|
+
type: "execution-complete";
|
|
47
|
+
returnValue?: unknown;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/** Failed execution lifecycle completion. */
|
|
51
|
+
export interface ExecutionErrorMessage {
|
|
52
|
+
type: "execution-error";
|
|
53
|
+
error: { message: string; stack: string };
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/** Discriminated union of all messages a provider/consumer can receive. */
|
|
57
|
+
export type RuntimeMessage = RuntimeMessageEnvelope &
|
|
58
|
+
(
|
|
59
|
+
| ArtifactOperationMessage
|
|
60
|
+
| ConsoleRuntimeMessage
|
|
61
|
+
| FileReturnedMessage
|
|
62
|
+
| ExecutionCompleteMessage
|
|
63
|
+
| ExecutionErrorMessage
|
|
64
|
+
);
|
|
65
|
+
|
|
66
|
+
/** Response sent back to sandboxed code via the bridge. */
|
|
67
|
+
export interface RuntimeResponse {
|
|
68
|
+
success?: boolean;
|
|
69
|
+
error?: string;
|
|
70
|
+
result?: unknown;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Shape of the resolved value of `window.sendRuntimeMessage` inside the sandbox.
|
|
75
|
+
* Mirrors the `runtime-response` payload posted back by the router.
|
|
76
|
+
*/
|
|
77
|
+
export interface SendRuntimeMessageResponse extends RuntimeResponse, RuntimeMessageEnvelope {
|
|
78
|
+
type?: string;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/** A single attachment record injected into the sandbox via `window.attachments`. */
|
|
82
|
+
export interface SandboxAttachment {
|
|
83
|
+
id: string;
|
|
84
|
+
fileName: string;
|
|
85
|
+
mimeType: string;
|
|
86
|
+
size: number;
|
|
87
|
+
content: string;
|
|
88
|
+
extractedText?: string;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Window surface exposed inside the sandbox by the injected runtime functions.
|
|
93
|
+
*
|
|
94
|
+
* The runtime functions are stringified and injected, so this interface only
|
|
95
|
+
* exists to type-check the function bodies at build time; the assertions are
|
|
96
|
+
* erased from the emitted JavaScript.
|
|
97
|
+
*/
|
|
98
|
+
export interface SandboxRuntimeWindow extends Window {
|
|
99
|
+
sandboxId?: string;
|
|
100
|
+
sendRuntimeMessage?: (message: Record<string, unknown>) => Promise<SendRuntimeMessageResponse>;
|
|
101
|
+
onCompleted?: (callback: (success: boolean) => void | Promise<void>) => void;
|
|
102
|
+
complete?: (error?: { message: string; stack: string } | null, returnValue?: unknown) => Promise<void>;
|
|
103
|
+
__completionCallbacks?: Array<(success: boolean) => void | Promise<void>>;
|
|
104
|
+
__originalConsole?: Record<"log" | "error" | "warn" | "info", (...args: unknown[]) => void>;
|
|
105
|
+
artifacts?: Record<string, string>;
|
|
106
|
+
attachments?: SandboxAttachment[];
|
|
107
|
+
listArtifacts?: () => Promise<string[]>;
|
|
108
|
+
getArtifact?: (filename: string) => Promise<unknown>;
|
|
109
|
+
createOrUpdateArtifact?: (filename: string, content: unknown, mimeType?: string) => Promise<void>;
|
|
110
|
+
deleteArtifact?: (filename: string) => Promise<void>;
|
|
111
|
+
listAttachments?: () => Array<{ id: string; fileName: string; mimeType: string; size: number }>;
|
|
112
|
+
readTextAttachment?: (attachmentId: string) => string;
|
|
113
|
+
readBinaryAttachment?: (attachmentId: string) => Uint8Array;
|
|
114
|
+
returnDownloadableFile?: (fileName: string, content: unknown, mimeType?: string) => Promise<void>;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/** Listener registered with `chrome.runtime.onUserScriptMessage`. */
|
|
118
|
+
export type UserScriptMessageListener = (
|
|
119
|
+
message: RuntimeMessage,
|
|
120
|
+
sender: unknown,
|
|
121
|
+
sendResponse: (response: RuntimeResponse & RuntimeMessageEnvelope) => void,
|
|
122
|
+
) => boolean;
|
|
123
|
+
|
|
124
|
+
/** Minimal subset of the `chrome.runtime.onUserScriptMessage` event API. */
|
|
125
|
+
export interface ChromeUserScriptMessageEvent {
|
|
126
|
+
addListener(listener: UserScriptMessageListener): void;
|
|
127
|
+
removeListener(listener: UserScriptMessageListener): void;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/** Minimal subset of `chrome.runtime` used by the runtime router/bridge. */
|
|
131
|
+
export interface ChromeRuntime {
|
|
132
|
+
onUserScriptMessage?: ChromeUserScriptMessageEvent;
|
|
133
|
+
sendMessage?(message: unknown): Promise<unknown>;
|
|
134
|
+
getURL?(path: string): string;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/** Minimal subset of `chrome.tabs` used to open external URLs. */
|
|
138
|
+
export interface ChromeTabs {
|
|
139
|
+
create(createProperties: { url: string }): void;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/** Minimal subset of the chrome extension API surface used by the sandbox. */
|
|
143
|
+
export interface ChromeApi {
|
|
144
|
+
runtime?: ChromeRuntime;
|
|
145
|
+
tabs?: ChromeTabs;
|
|
146
|
+
}
|