@mariozechner/pi-web-ui 0.5.44 → 0.5.46
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/README.md +178 -99
- package/dist/ChatPanel.d.ts +15 -10
- package/dist/ChatPanel.d.ts.map +1 -1
- package/dist/ChatPanel.js +68 -100
- package/dist/ChatPanel.js.map +1 -1
- package/dist/{state/agent-session.d.ts → agent/agent.d.ts} +23 -19
- package/dist/agent/agent.d.ts.map +1 -0
- package/dist/{state/agent-session.js → agent/agent.js} +50 -32
- package/dist/agent/agent.js.map +1 -0
- package/dist/{state → agent}/transports/AppTransport.d.ts +1 -3
- package/dist/agent/transports/AppTransport.d.ts.map +1 -0
- package/dist/{state → agent}/transports/AppTransport.js +5 -4
- package/dist/{state → agent}/transports/AppTransport.js.map +1 -1
- package/dist/{state → agent}/transports/ProviderTransport.d.ts +1 -3
- package/dist/agent/transports/ProviderTransport.d.ts.map +1 -0
- package/dist/{state → agent}/transports/ProviderTransport.js +6 -7
- package/dist/agent/transports/ProviderTransport.js.map +1 -0
- package/dist/{state → agent}/transports/index.d.ts.map +1 -1
- package/dist/agent/transports/index.js.map +1 -0
- package/dist/{state → agent}/transports/proxy-types.d.ts.map +1 -1
- package/dist/agent/transports/proxy-types.js.map +1 -0
- package/dist/agent/transports/types.d.ts +12 -0
- package/dist/agent/transports/types.d.ts.map +1 -0
- package/dist/{state → agent}/transports/types.js.map +1 -1
- package/dist/{state → agent}/types.d.ts.map +1 -1
- package/dist/{state → agent}/types.js.map +1 -1
- package/dist/app.css +1 -1
- package/dist/components/AgentInterface.d.ts +7 -4
- package/dist/components/AgentInterface.d.ts.map +1 -1
- package/dist/components/AgentInterface.js +29 -17
- package/dist/components/AgentInterface.js.map +1 -1
- package/dist/components/ConsoleBlock.d.ts +1 -0
- package/dist/components/ConsoleBlock.d.ts.map +1 -1
- package/dist/components/ConsoleBlock.js +7 -1
- package/dist/components/ConsoleBlock.js.map +1 -1
- package/dist/components/ExpandableSection.d.ts +15 -0
- package/dist/components/ExpandableSection.d.ts.map +1 -0
- package/dist/components/ExpandableSection.js +63 -0
- package/dist/components/ExpandableSection.js.map +1 -0
- package/dist/components/MessageEditor.d.ts +8 -1
- package/dist/components/MessageEditor.d.ts.map +1 -1
- package/dist/components/MessageEditor.js +149 -6
- package/dist/components/MessageEditor.js.map +1 -1
- package/dist/components/MessageList.d.ts +3 -2
- package/dist/components/MessageList.d.ts.map +1 -1
- package/dist/components/MessageList.js +14 -1
- package/dist/components/MessageList.js.map +1 -1
- package/dist/components/Messages.d.ts +15 -6
- package/dist/components/Messages.d.ts.map +1 -1
- package/dist/components/Messages.js +17 -83
- package/dist/components/Messages.js.map +1 -1
- package/dist/components/ProviderKeyInput.d.ts.map +1 -1
- package/dist/components/ProviderKeyInput.js +6 -5
- package/dist/components/ProviderKeyInput.js.map +1 -1
- package/dist/components/SandboxedIframe.d.ts +29 -7
- package/dist/components/SandboxedIframe.d.ts.map +1 -1
- package/dist/components/SandboxedIframe.js +350 -282
- package/dist/components/SandboxedIframe.js.map +1 -1
- package/dist/components/message-renderer-registry.d.ts +12 -0
- package/dist/components/message-renderer-registry.d.ts.map +1 -0
- package/dist/components/message-renderer-registry.js +12 -0
- package/dist/components/message-renderer-registry.js.map +1 -0
- package/dist/components/sandbox/ArtifactsRuntimeProvider.d.ts +35 -0
- package/dist/components/sandbox/ArtifactsRuntimeProvider.d.ts.map +1 -0
- package/dist/components/sandbox/ArtifactsRuntimeProvider.js +189 -0
- package/dist/components/sandbox/ArtifactsRuntimeProvider.js.map +1 -0
- package/dist/components/sandbox/AttachmentsRuntimeProvider.d.ts +17 -0
- package/dist/components/sandbox/AttachmentsRuntimeProvider.d.ts.map +1 -0
- package/dist/components/sandbox/AttachmentsRuntimeProvider.js +64 -0
- package/dist/components/sandbox/AttachmentsRuntimeProvider.js.map +1 -0
- package/dist/components/sandbox/ConsoleRuntimeProvider.d.ts +42 -0
- package/dist/components/sandbox/ConsoleRuntimeProvider.d.ts.map +1 -0
- package/dist/components/sandbox/ConsoleRuntimeProvider.js +161 -0
- package/dist/components/sandbox/ConsoleRuntimeProvider.js.map +1 -0
- package/dist/components/sandbox/FileDownloadRuntimeProvider.d.ts +30 -0
- package/dist/components/sandbox/FileDownloadRuntimeProvider.d.ts.map +1 -0
- package/dist/components/sandbox/FileDownloadRuntimeProvider.js +97 -0
- package/dist/components/sandbox/FileDownloadRuntimeProvider.js.map +1 -0
- package/dist/components/sandbox/RuntimeMessageBridge.d.ts +19 -0
- package/dist/components/sandbox/RuntimeMessageBridge.d.ts.map +1 -0
- package/dist/components/sandbox/RuntimeMessageBridge.js +74 -0
- package/dist/components/sandbox/RuntimeMessageBridge.js.map +1 -0
- package/dist/components/sandbox/RuntimeMessageRouter.d.ts +65 -0
- package/dist/components/sandbox/RuntimeMessageRouter.d.ts.map +1 -0
- package/dist/components/sandbox/RuntimeMessageRouter.js +168 -0
- package/dist/components/sandbox/RuntimeMessageRouter.js.map +1 -0
- package/dist/components/sandbox/SandboxRuntimeProvider.d.ts +33 -0
- package/dist/components/sandbox/SandboxRuntimeProvider.d.ts.map +1 -0
- package/dist/components/sandbox/SandboxRuntimeProvider.js +2 -0
- package/dist/components/sandbox/SandboxRuntimeProvider.js.map +1 -0
- package/dist/dialogs/ApiKeyPromptDialog.d.ts.map +1 -1
- package/dist/dialogs/ApiKeyPromptDialog.js +2 -5
- package/dist/dialogs/ApiKeyPromptDialog.js.map +1 -1
- package/dist/dialogs/ModelSelector.js.map +1 -1
- package/dist/dialogs/PersistentStorageDialog.d.ts +17 -0
- package/dist/dialogs/PersistentStorageDialog.d.ts.map +1 -0
- package/dist/dialogs/PersistentStorageDialog.js +144 -0
- package/dist/dialogs/PersistentStorageDialog.js.map +1 -0
- package/dist/dialogs/SessionListDialog.d.ts +19 -0
- package/dist/dialogs/SessionListDialog.d.ts.map +1 -0
- package/dist/dialogs/SessionListDialog.js +152 -0
- package/dist/dialogs/SessionListDialog.js.map +1 -0
- package/dist/dialogs/SettingsDialog.d.ts.map +1 -1
- package/dist/dialogs/SettingsDialog.js +1 -0
- package/dist/dialogs/SettingsDialog.js.map +1 -1
- package/dist/index.d.ts +34 -16
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +32 -14
- package/dist/index.js.map +1 -1
- package/dist/prompts/prompts.d.ts +11 -0
- package/dist/prompts/prompts.d.ts.map +1 -0
- package/dist/prompts/prompts.js +272 -0
- package/dist/prompts/prompts.js.map +1 -0
- package/dist/storage/app-storage.d.ts +17 -12
- package/dist/storage/app-storage.d.ts.map +1 -1
- package/dist/storage/app-storage.js +13 -20
- package/dist/storage/app-storage.js.map +1 -1
- package/dist/storage/backends/indexeddb-storage-backend.d.ts +27 -0
- package/dist/storage/backends/indexeddb-storage-backend.d.ts.map +1 -0
- package/dist/storage/backends/indexeddb-storage-backend.js +166 -0
- package/dist/storage/backends/indexeddb-storage-backend.js.map +1 -0
- package/dist/storage/store.d.ts +23 -0
- package/dist/storage/store.d.ts.map +1 -0
- package/dist/storage/store.js +26 -0
- package/dist/storage/store.js.map +1 -0
- package/dist/storage/stores/provider-keys-store.d.ts +14 -0
- package/dist/storage/stores/provider-keys-store.d.ts.map +1 -0
- package/dist/storage/stores/provider-keys-store.js +27 -0
- package/dist/storage/stores/provider-keys-store.js.map +1 -0
- package/dist/storage/stores/sessions-store.d.ts +31 -0
- package/dist/storage/stores/sessions-store.d.ts.map +1 -0
- package/dist/storage/stores/sessions-store.js +113 -0
- package/dist/storage/stores/sessions-store.js.map +1 -0
- package/dist/storage/stores/settings-store.d.ts +14 -0
- package/dist/storage/stores/settings-store.d.ts.map +1 -0
- package/dist/storage/stores/settings-store.js +28 -0
- package/dist/storage/stores/settings-store.js.map +1 -0
- package/dist/storage/types.d.ts +156 -22
- package/dist/storage/types.d.ts.map +1 -1
- package/dist/tools/artifacts/ArtifactElement.d.ts +0 -1
- package/dist/tools/artifacts/ArtifactElement.d.ts.map +1 -1
- package/dist/tools/artifacts/ArtifactElement.js +0 -1
- package/dist/tools/artifacts/ArtifactElement.js.map +1 -1
- package/dist/tools/artifacts/ArtifactPill.d.ts +4 -0
- package/dist/tools/artifacts/ArtifactPill.d.ts.map +1 -0
- package/dist/tools/artifacts/ArtifactPill.js +22 -0
- package/dist/tools/artifacts/ArtifactPill.js.map +1 -0
- package/dist/tools/artifacts/Console.d.ts +18 -0
- package/dist/tools/artifacts/Console.d.ts.map +1 -0
- package/dist/tools/artifacts/Console.js +95 -0
- package/dist/tools/artifacts/Console.js.map +1 -0
- package/dist/tools/artifacts/DocxArtifact.d.ts +22 -0
- package/dist/tools/artifacts/DocxArtifact.d.ts.map +1 -0
- package/dist/tools/artifacts/DocxArtifact.js +208 -0
- package/dist/tools/artifacts/DocxArtifact.js.map +1 -0
- package/dist/tools/artifacts/ExcelArtifact.d.ts +24 -0
- package/dist/tools/artifacts/ExcelArtifact.d.ts.map +1 -0
- package/dist/tools/artifacts/ExcelArtifact.js +216 -0
- package/dist/tools/artifacts/ExcelArtifact.js.map +1 -0
- package/dist/tools/artifacts/GenericArtifact.d.ts +19 -0
- package/dist/tools/artifacts/GenericArtifact.d.ts.map +1 -0
- package/dist/tools/artifacts/GenericArtifact.js +117 -0
- package/dist/tools/artifacts/GenericArtifact.js.map +1 -0
- package/dist/tools/artifacts/HtmlArtifact.d.ts +8 -11
- package/dist/tools/artifacts/HtmlArtifact.d.ts.map +1 -1
- package/dist/tools/artifacts/HtmlArtifact.js +56 -88
- package/dist/tools/artifacts/HtmlArtifact.js.map +1 -1
- package/dist/tools/artifacts/ImageArtifact.d.ts +20 -0
- package/dist/tools/artifacts/ImageArtifact.d.ts.map +1 -0
- package/dist/tools/artifacts/ImageArtifact.js +120 -0
- package/dist/tools/artifacts/ImageArtifact.js.map +1 -0
- package/dist/tools/artifacts/MarkdownArtifact.d.ts +0 -1
- package/dist/tools/artifacts/MarkdownArtifact.d.ts.map +1 -1
- package/dist/tools/artifacts/MarkdownArtifact.js +0 -4
- package/dist/tools/artifacts/MarkdownArtifact.js.map +1 -1
- package/dist/tools/artifacts/PdfArtifact.d.ts +25 -0
- package/dist/tools/artifacts/PdfArtifact.d.ts.map +1 -0
- package/dist/tools/artifacts/PdfArtifact.js +184 -0
- package/dist/tools/artifacts/PdfArtifact.js.map +1 -0
- package/dist/tools/artifacts/SvgArtifact.d.ts +0 -1
- package/dist/tools/artifacts/SvgArtifact.d.ts.map +1 -1
- package/dist/tools/artifacts/SvgArtifact.js +0 -4
- package/dist/tools/artifacts/SvgArtifact.js.map +1 -1
- package/dist/tools/artifacts/TextArtifact.d.ts +0 -1
- package/dist/tools/artifacts/TextArtifact.d.ts.map +1 -1
- package/dist/tools/artifacts/TextArtifact.js +0 -4
- package/dist/tools/artifacts/TextArtifact.js.map +1 -1
- package/dist/tools/artifacts/artifacts-tool-renderer.d.ts +11 -0
- package/dist/tools/artifacts/artifacts-tool-renderer.d.ts.map +1 -0
- package/dist/tools/artifacts/artifacts-tool-renderer.js +262 -0
- package/dist/tools/artifacts/artifacts-tool-renderer.js.map +1 -0
- package/dist/tools/artifacts/artifacts.d.ts +10 -13
- package/dist/tools/artifacts/artifacts.d.ts.map +1 -1
- package/dist/tools/artifacts/artifacts.js +166 -344
- package/dist/tools/artifacts/artifacts.js.map +1 -1
- package/dist/tools/artifacts/index.d.ts +1 -0
- package/dist/tools/artifacts/index.d.ts.map +1 -1
- package/dist/tools/artifacts/index.js +1 -0
- package/dist/tools/artifacts/index.js.map +1 -1
- package/dist/tools/extract-document.d.ts +24 -0
- package/dist/tools/extract-document.d.ts.map +1 -0
- package/dist/tools/extract-document.js +193 -0
- package/dist/tools/extract-document.js.map +1 -0
- package/dist/tools/index.d.ts +9 -7
- package/dist/tools/index.d.ts.map +1 -1
- package/dist/tools/index.js +17 -13
- package/dist/tools/index.js.map +1 -1
- package/dist/tools/javascript-repl.d.ts +16 -15
- package/dist/tools/javascript-repl.d.ts.map +1 -1
- package/dist/tools/javascript-repl.js +101 -133
- package/dist/tools/javascript-repl.js.map +1 -1
- package/dist/tools/renderer-registry.d.ts +12 -0
- package/dist/tools/renderer-registry.d.ts.map +1 -1
- package/dist/tools/renderer-registry.js +78 -0
- package/dist/tools/renderer-registry.js.map +1 -1
- package/dist/tools/renderers/BashRenderer.d.ts +2 -4
- package/dist/tools/renderers/BashRenderer.d.ts.map +1 -1
- package/dist/tools/renderers/BashRenderer.js +30 -26
- package/dist/tools/renderers/BashRenderer.js.map +1 -1
- package/dist/tools/renderers/CalculateRenderer.d.ts +2 -4
- package/dist/tools/renderers/CalculateRenderer.d.ts.map +1 -1
- package/dist/tools/renderers/CalculateRenderer.js +32 -28
- package/dist/tools/renderers/CalculateRenderer.js.map +1 -1
- package/dist/tools/renderers/DefaultRenderer.d.ts +2 -4
- package/dist/tools/renderers/DefaultRenderer.d.ts.map +1 -1
- package/dist/tools/renderers/DefaultRenderer.js +78 -18
- package/dist/tools/renderers/DefaultRenderer.js.map +1 -1
- package/dist/tools/renderers/GetCurrentTimeRenderer.d.ts +2 -4
- package/dist/tools/renderers/GetCurrentTimeRenderer.d.ts.map +1 -1
- package/dist/tools/renderers/GetCurrentTimeRenderer.js +57 -21
- package/dist/tools/renderers/GetCurrentTimeRenderer.js.map +1 -1
- package/dist/tools/types.d.ts +5 -2
- package/dist/tools/types.d.ts.map +1 -1
- package/dist/utils/i18n.d.ts +424 -1
- package/dist/utils/i18n.d.ts.map +1 -1
- package/dist/utils/i18n.js +131 -7
- package/dist/utils/i18n.js.map +1 -1
- package/example/package.json +2 -1
- package/example/src/custom-messages.ts +112 -0
- package/example/src/main.ts +391 -38
- package/package.json +48 -43
- package/scripts/count-prompt-tokens.ts +88 -0
- package/src/ChatPanel.ts +93 -101
- package/src/{state/agent-session.ts → agent/agent.ts} +80 -55
- package/src/{state → agent}/transports/AppTransport.ts +6 -6
- package/src/{state → agent}/transports/ProviderTransport.ts +13 -7
- package/src/{state → agent}/transports/types.ts +8 -2
- package/src/components/AgentInterface.ts +32 -16
- package/src/components/ConsoleBlock.ts +5 -1
- package/src/components/ExpandableSection.ts +46 -0
- package/src/components/MessageEditor.ts +159 -5
- package/src/components/MessageList.ts +18 -3
- package/src/components/Messages.ts +48 -89
- package/src/components/ProviderKeyInput.ts +6 -5
- package/src/components/SandboxedIframe.ts +412 -321
- package/src/components/message-renderer-registry.ts +28 -0
- package/src/components/sandbox/ArtifactsRuntimeProvider.ts +219 -0
- package/src/components/sandbox/AttachmentsRuntimeProvider.ts +66 -0
- package/src/components/sandbox/ConsoleRuntimeProvider.ts +187 -0
- package/src/components/sandbox/FileDownloadRuntimeProvider.ts +110 -0
- package/src/components/sandbox/RuntimeMessageBridge.ts +82 -0
- package/src/components/sandbox/RuntimeMessageRouter.ts +216 -0
- package/src/components/sandbox/SandboxRuntimeProvider.ts +35 -0
- package/src/dialogs/ApiKeyPromptDialog.ts +2 -5
- package/src/dialogs/ModelSelector.ts +2 -2
- package/src/dialogs/PersistentStorageDialog.ts +141 -0
- package/src/dialogs/SessionListDialog.ts +148 -0
- package/src/dialogs/SettingsDialog.ts +1 -0
- package/src/index.ts +61 -20
- package/src/prompts/prompts.ts +282 -0
- package/src/storage/app-storage.ts +27 -24
- package/src/storage/backends/indexeddb-storage-backend.ts +193 -0
- package/src/storage/store.ts +33 -0
- package/src/storage/stores/provider-keys-store.ts +33 -0
- package/src/storage/stores/sessions-store.ts +130 -0
- package/src/storage/stores/settings-store.ts +34 -0
- package/src/storage/types.ts +182 -22
- package/src/tools/artifacts/ArtifactElement.ts +0 -1
- package/src/tools/artifacts/ArtifactPill.ts +25 -0
- package/src/tools/artifacts/Console.ts +93 -0
- package/src/tools/artifacts/DocxArtifact.ts +213 -0
- package/src/tools/artifacts/ExcelArtifact.ts +231 -0
- package/src/tools/artifacts/GenericArtifact.ts +117 -0
- package/src/tools/artifacts/HtmlArtifact.ts +64 -94
- package/src/tools/artifacts/ImageArtifact.ts +116 -0
- package/src/tools/artifacts/MarkdownArtifact.ts +0 -1
- package/src/tools/artifacts/PdfArtifact.ts +201 -0
- package/src/tools/artifacts/SvgArtifact.ts +0 -1
- package/src/tools/artifacts/TextArtifact.ts +0 -1
- package/src/tools/artifacts/artifacts-tool-renderer.ts +298 -0
- package/src/tools/artifacts/artifacts.ts +190 -366
- package/src/tools/artifacts/index.ts +1 -0
- package/src/tools/extract-document.ts +250 -0
- package/src/tools/index.ts +25 -14
- package/src/tools/javascript-repl.ts +138 -160
- package/src/tools/renderer-registry.ts +98 -0
- package/src/tools/renderers/BashRenderer.ts +33 -30
- package/src/tools/renderers/CalculateRenderer.ts +36 -31
- package/src/tools/renderers/DefaultRenderer.ts +84 -21
- package/src/tools/renderers/GetCurrentTimeRenderer.ts +68 -23
- package/src/tools/types.ts +10 -2
- package/src/utils/i18n.ts +203 -8
- package/dist/state/agent-session.d.ts.map +0 -1
- package/dist/state/agent-session.js.map +0 -1
- package/dist/state/transports/AppTransport.d.ts.map +0 -1
- package/dist/state/transports/ProviderTransport.d.ts.map +0 -1
- package/dist/state/transports/ProviderTransport.js.map +0 -1
- package/dist/state/transports/index.js.map +0 -1
- package/dist/state/transports/proxy-types.js.map +0 -1
- package/dist/state/transports/types.d.ts +0 -11
- package/dist/state/transports/types.d.ts.map +0 -1
- package/dist/storage/backends/chrome-storage-backend.d.ts +0 -18
- package/dist/storage/backends/chrome-storage-backend.d.ts.map +0 -1
- package/dist/storage/backends/chrome-storage-backend.js +0 -67
- package/dist/storage/backends/chrome-storage-backend.js.map +0 -1
- package/dist/storage/backends/indexeddb-backend.d.ts +0 -20
- package/dist/storage/backends/indexeddb-backend.d.ts.map +0 -1
- package/dist/storage/backends/indexeddb-backend.js +0 -89
- package/dist/storage/backends/indexeddb-backend.js.map +0 -1
- package/dist/storage/backends/local-storage-backend.d.ts +0 -18
- package/dist/storage/backends/local-storage-backend.d.ts.map +0 -1
- package/dist/storage/backends/local-storage-backend.js +0 -69
- package/dist/storage/backends/local-storage-backend.js.map +0 -1
- package/dist/storage/repositories/provider-keys-repository.d.ts +0 -34
- package/dist/storage/repositories/provider-keys-repository.d.ts.map +0 -1
- package/dist/storage/repositories/provider-keys-repository.js +0 -50
- package/dist/storage/repositories/provider-keys-repository.js.map +0 -1
- package/dist/storage/repositories/settings-repository.d.ts +0 -34
- package/dist/storage/repositories/settings-repository.d.ts.map +0 -1
- package/dist/storage/repositories/settings-repository.js +0 -46
- package/dist/storage/repositories/settings-repository.js.map +0 -1
- package/src/storage/backends/chrome-storage-backend.ts +0 -82
- package/src/storage/backends/indexeddb-backend.ts +0 -107
- package/src/storage/backends/local-storage-backend.ts +0 -74
- package/src/storage/repositories/provider-keys-repository.ts +0 -55
- package/src/storage/repositories/settings-repository.ts +0 -51
- /package/dist/{state → agent}/transports/index.d.ts +0 -0
- /package/dist/{state → agent}/transports/index.js +0 -0
- /package/dist/{state → agent}/transports/proxy-types.d.ts +0 -0
- /package/dist/{state → agent}/transports/proxy-types.js +0 -0
- /package/dist/{state → agent}/transports/types.js +0 -0
- /package/dist/{state → agent}/types.d.ts +0 -0
- /package/dist/{state → agent}/types.js +0 -0
- /package/src/{state → agent}/transports/index.ts +0 -0
- /package/src/{state → agent}/transports/proxy-types.ts +0 -0
- /package/src/{state → agent}/types.ts +0 -0
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { TemplateResult } from "lit";
|
|
2
|
+
import type { AppMessage } from "./Messages.js";
|
|
3
|
+
|
|
4
|
+
// Extract role type from AppMessage union
|
|
5
|
+
export type MessageRole = AppMessage["role"];
|
|
6
|
+
|
|
7
|
+
// Generic message renderer typed to specific message type
|
|
8
|
+
export interface MessageRenderer<TMessage extends AppMessage = AppMessage> {
|
|
9
|
+
render(message: TMessage): TemplateResult;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
// Registry of custom message renderers by role
|
|
13
|
+
const messageRenderers = new Map<MessageRole, MessageRenderer<any>>();
|
|
14
|
+
|
|
15
|
+
export function registerMessageRenderer<TRole extends MessageRole>(
|
|
16
|
+
role: TRole,
|
|
17
|
+
renderer: MessageRenderer<Extract<AppMessage, { role: TRole }>>,
|
|
18
|
+
): void {
|
|
19
|
+
messageRenderers.set(role, renderer);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export function getMessageRenderer(role: MessageRole): MessageRenderer | undefined {
|
|
23
|
+
return messageRenderers.get(role);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export function renderMessage(message: AppMessage): TemplateResult | undefined {
|
|
27
|
+
return messageRenderers.get(message.role)?.render(message);
|
|
28
|
+
}
|
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
import {
|
|
2
|
+
ARTIFACTS_RUNTIME_PROVIDER_DESCRIPTION_RO,
|
|
3
|
+
ARTIFACTS_RUNTIME_PROVIDER_DESCRIPTION_RW,
|
|
4
|
+
} from "../../prompts/prompts.js";
|
|
5
|
+
import type { SandboxRuntimeProvider } from "./SandboxRuntimeProvider.js";
|
|
6
|
+
|
|
7
|
+
// Define minimal interface for ArtifactsPanel to avoid circular dependencies
|
|
8
|
+
interface ArtifactsPanelLike {
|
|
9
|
+
artifacts: Map<string, { content: string }>;
|
|
10
|
+
tool: {
|
|
11
|
+
execute(toolCallId: string, args: { command: string; filename: string; content?: string }): Promise<any>;
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
interface AgentLike {
|
|
16
|
+
appendMessage(message: any): void;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Artifacts Runtime Provider
|
|
21
|
+
*
|
|
22
|
+
* Provides programmatic access to session artifacts from sandboxed code.
|
|
23
|
+
* Allows code to create, read, update, and delete artifacts dynamically.
|
|
24
|
+
* Supports both online (extension) and offline (downloaded HTML) modes.
|
|
25
|
+
*/
|
|
26
|
+
export class ArtifactsRuntimeProvider implements SandboxRuntimeProvider {
|
|
27
|
+
constructor(
|
|
28
|
+
private artifactsPanel: ArtifactsPanelLike,
|
|
29
|
+
private agent?: AgentLike,
|
|
30
|
+
private readWrite: boolean = true,
|
|
31
|
+
) {}
|
|
32
|
+
|
|
33
|
+
getData(): Record<string, any> {
|
|
34
|
+
// Inject artifact snapshot for offline mode
|
|
35
|
+
const snapshot: Record<string, string> = {};
|
|
36
|
+
this.artifactsPanel.artifacts.forEach((artifact, filename) => {
|
|
37
|
+
snapshot[filename] = artifact.content;
|
|
38
|
+
});
|
|
39
|
+
return { artifacts: snapshot };
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
getRuntime(): (sandboxId: string) => void {
|
|
43
|
+
// This function will be stringified, so no external references!
|
|
44
|
+
return (_sandboxId: string) => {
|
|
45
|
+
// Auto-parse/stringify for .json files
|
|
46
|
+
const isJsonFile = (filename: string) => filename.endsWith(".json");
|
|
47
|
+
|
|
48
|
+
(window as any).listArtifacts = async (): Promise<string[]> => {
|
|
49
|
+
// Online: ask extension
|
|
50
|
+
if ((window as any).sendRuntimeMessage) {
|
|
51
|
+
const response = await (window as any).sendRuntimeMessage({
|
|
52
|
+
type: "artifact-operation",
|
|
53
|
+
action: "list",
|
|
54
|
+
});
|
|
55
|
+
if (!response.success) throw new Error(response.error);
|
|
56
|
+
return response.result;
|
|
57
|
+
}
|
|
58
|
+
// Offline: return snapshot keys
|
|
59
|
+
else {
|
|
60
|
+
return Object.keys((window as any).artifacts || {});
|
|
61
|
+
}
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
(window as any).getArtifact = async (filename: string): Promise<any> => {
|
|
65
|
+
let content: string;
|
|
66
|
+
|
|
67
|
+
// Online: ask extension
|
|
68
|
+
if ((window as any).sendRuntimeMessage) {
|
|
69
|
+
const response = await (window as any).sendRuntimeMessage({
|
|
70
|
+
type: "artifact-operation",
|
|
71
|
+
action: "get",
|
|
72
|
+
filename,
|
|
73
|
+
});
|
|
74
|
+
if (!response.success) throw new Error(response.error);
|
|
75
|
+
content = response.result;
|
|
76
|
+
}
|
|
77
|
+
// Offline: read snapshot
|
|
78
|
+
else {
|
|
79
|
+
if (!(window as any).artifacts?.[filename]) {
|
|
80
|
+
throw new Error(`Artifact not found (offline mode): ${filename}`);
|
|
81
|
+
}
|
|
82
|
+
content = (window as any).artifacts[filename];
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// Auto-parse .json files
|
|
86
|
+
if (isJsonFile(filename)) {
|
|
87
|
+
try {
|
|
88
|
+
return JSON.parse(content);
|
|
89
|
+
} catch (e) {
|
|
90
|
+
throw new Error(`Failed to parse JSON from ${filename}: ${e}`);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
return content;
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
(window as any).createOrUpdateArtifact = async (
|
|
97
|
+
filename: string,
|
|
98
|
+
content: any,
|
|
99
|
+
mimeType?: string,
|
|
100
|
+
): Promise<void> => {
|
|
101
|
+
if (!(window as any).sendRuntimeMessage) {
|
|
102
|
+
throw new Error("Cannot create/update artifacts in offline mode (read-only)");
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
let finalContent = content;
|
|
106
|
+
// Auto-stringify .json files
|
|
107
|
+
if (isJsonFile(filename) && typeof content !== "string") {
|
|
108
|
+
finalContent = JSON.stringify(content, null, 2);
|
|
109
|
+
} else if (typeof content !== "string") {
|
|
110
|
+
finalContent = JSON.stringify(content, null, 2);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
const response = await (window as any).sendRuntimeMessage({
|
|
114
|
+
type: "artifact-operation",
|
|
115
|
+
action: "createOrUpdate",
|
|
116
|
+
filename,
|
|
117
|
+
content: finalContent,
|
|
118
|
+
mimeType,
|
|
119
|
+
});
|
|
120
|
+
if (!response.success) throw new Error(response.error);
|
|
121
|
+
};
|
|
122
|
+
|
|
123
|
+
(window as any).deleteArtifact = async (filename: string): Promise<void> => {
|
|
124
|
+
if (!(window as any).sendRuntimeMessage) {
|
|
125
|
+
throw new Error("Cannot delete artifacts in offline mode (read-only)");
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
const response = await (window as any).sendRuntimeMessage({
|
|
129
|
+
type: "artifact-operation",
|
|
130
|
+
action: "delete",
|
|
131
|
+
filename,
|
|
132
|
+
});
|
|
133
|
+
if (!response.success) throw new Error(response.error);
|
|
134
|
+
};
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
async handleMessage(message: any, respond: (response: any) => void): Promise<void> {
|
|
139
|
+
if (message.type !== "artifact-operation") {
|
|
140
|
+
return;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
const { action, filename, content, mimeType } = message;
|
|
144
|
+
|
|
145
|
+
try {
|
|
146
|
+
switch (action) {
|
|
147
|
+
case "list": {
|
|
148
|
+
const filenames = Array.from(this.artifactsPanel.artifacts.keys());
|
|
149
|
+
respond({ success: true, result: filenames });
|
|
150
|
+
break;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
case "get": {
|
|
154
|
+
const artifact = this.artifactsPanel.artifacts.get(filename);
|
|
155
|
+
if (!artifact) {
|
|
156
|
+
respond({ success: false, error: `Artifact not found: ${filename}` });
|
|
157
|
+
} else {
|
|
158
|
+
respond({ success: true, result: artifact.content });
|
|
159
|
+
}
|
|
160
|
+
break;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
case "createOrUpdate": {
|
|
164
|
+
try {
|
|
165
|
+
const exists = this.artifactsPanel.artifacts.has(filename);
|
|
166
|
+
const command = exists ? "rewrite" : "create";
|
|
167
|
+
const action = exists ? "update" : "create";
|
|
168
|
+
|
|
169
|
+
await this.artifactsPanel.tool.execute("", {
|
|
170
|
+
command,
|
|
171
|
+
filename,
|
|
172
|
+
content,
|
|
173
|
+
});
|
|
174
|
+
this.agent?.appendMessage({
|
|
175
|
+
role: "artifact",
|
|
176
|
+
action,
|
|
177
|
+
filename,
|
|
178
|
+
content,
|
|
179
|
+
...(action === "create" && { title: filename }),
|
|
180
|
+
timestamp: new Date().toISOString(),
|
|
181
|
+
});
|
|
182
|
+
respond({ success: true });
|
|
183
|
+
} catch (err: any) {
|
|
184
|
+
respond({ success: false, error: err.message });
|
|
185
|
+
}
|
|
186
|
+
break;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
case "delete": {
|
|
190
|
+
try {
|
|
191
|
+
await this.artifactsPanel.tool.execute("", {
|
|
192
|
+
command: "delete",
|
|
193
|
+
filename,
|
|
194
|
+
});
|
|
195
|
+
this.agent?.appendMessage({
|
|
196
|
+
role: "artifact",
|
|
197
|
+
action: "delete",
|
|
198
|
+
filename,
|
|
199
|
+
timestamp: new Date().toISOString(),
|
|
200
|
+
});
|
|
201
|
+
respond({ success: true });
|
|
202
|
+
} catch (err: any) {
|
|
203
|
+
respond({ success: false, error: err.message });
|
|
204
|
+
}
|
|
205
|
+
break;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
default:
|
|
209
|
+
respond({ success: false, error: `Unknown artifact action: ${action}` });
|
|
210
|
+
}
|
|
211
|
+
} catch (error: any) {
|
|
212
|
+
respond({ success: false, error: error.message });
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
getDescription(): string {
|
|
217
|
+
return this.readWrite ? ARTIFACTS_RUNTIME_PROVIDER_DESCRIPTION_RW : ARTIFACTS_RUNTIME_PROVIDER_DESCRIPTION_RO;
|
|
218
|
+
}
|
|
219
|
+
}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { ATTACHMENTS_RUNTIME_DESCRIPTION } from "../../prompts/prompts.js";
|
|
2
|
+
import type { Attachment } from "../../utils/attachment-utils.js";
|
|
3
|
+
import type { SandboxRuntimeProvider } from "./SandboxRuntimeProvider.js";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Attachments Runtime Provider
|
|
7
|
+
*
|
|
8
|
+
* OPTIONAL provider that provides file access APIs to sandboxed code.
|
|
9
|
+
* Only needed when attachments are present.
|
|
10
|
+
* Attachments are read-only snapshot data - no messaging needed.
|
|
11
|
+
*/
|
|
12
|
+
export class AttachmentsRuntimeProvider implements SandboxRuntimeProvider {
|
|
13
|
+
constructor(private attachments: Attachment[]) {}
|
|
14
|
+
|
|
15
|
+
getData(): Record<string, any> {
|
|
16
|
+
const attachmentsData = this.attachments.map((a) => ({
|
|
17
|
+
id: a.id,
|
|
18
|
+
fileName: a.fileName,
|
|
19
|
+
mimeType: a.mimeType,
|
|
20
|
+
size: a.size,
|
|
21
|
+
content: a.content,
|
|
22
|
+
extractedText: a.extractedText,
|
|
23
|
+
}));
|
|
24
|
+
|
|
25
|
+
return { attachments: attachmentsData };
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
getRuntime(): (sandboxId: string) => void {
|
|
29
|
+
// This function will be stringified, so no external references!
|
|
30
|
+
// These functions read directly from window.attachments
|
|
31
|
+
// Works both online AND offline (no messaging needed!)
|
|
32
|
+
return (_sandboxId: string) => {
|
|
33
|
+
(window as any).listAttachments = () =>
|
|
34
|
+
((window as any).attachments || []).map((a: any) => ({
|
|
35
|
+
id: a.id,
|
|
36
|
+
fileName: a.fileName,
|
|
37
|
+
mimeType: a.mimeType,
|
|
38
|
+
size: a.size,
|
|
39
|
+
}));
|
|
40
|
+
|
|
41
|
+
(window as any).readTextAttachment = (attachmentId: string) => {
|
|
42
|
+
const a = ((window as any).attachments || []).find((x: any) => x.id === attachmentId);
|
|
43
|
+
if (!a) throw new Error("Attachment not found: " + attachmentId);
|
|
44
|
+
if (a.extractedText) return a.extractedText;
|
|
45
|
+
try {
|
|
46
|
+
return atob(a.content);
|
|
47
|
+
} catch {
|
|
48
|
+
throw new Error("Failed to decode text content for: " + attachmentId);
|
|
49
|
+
}
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
(window as any).readBinaryAttachment = (attachmentId: string) => {
|
|
53
|
+
const a = ((window as any).attachments || []).find((x: any) => x.id === attachmentId);
|
|
54
|
+
if (!a) throw new Error("Attachment not found: " + attachmentId);
|
|
55
|
+
const bin = atob(a.content);
|
|
56
|
+
const bytes = new Uint8Array(bin.length);
|
|
57
|
+
for (let i = 0; i < bin.length; i++) bytes[i] = bin.charCodeAt(i);
|
|
58
|
+
return bytes;
|
|
59
|
+
};
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
getDescription(): string {
|
|
64
|
+
return ATTACHMENTS_RUNTIME_DESCRIPTION;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
import type { SandboxRuntimeProvider } from "./SandboxRuntimeProvider.js";
|
|
2
|
+
|
|
3
|
+
export interface ConsoleLog {
|
|
4
|
+
type: "log" | "warn" | "error" | "info";
|
|
5
|
+
text: string;
|
|
6
|
+
args?: unknown[];
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Console Runtime Provider
|
|
11
|
+
*
|
|
12
|
+
* REQUIRED provider that should always be included first.
|
|
13
|
+
* Provides console capture, error handling, and execution lifecycle management.
|
|
14
|
+
* Collects console output for retrieval by caller.
|
|
15
|
+
*/
|
|
16
|
+
export class ConsoleRuntimeProvider implements SandboxRuntimeProvider {
|
|
17
|
+
private logs: ConsoleLog[] = [];
|
|
18
|
+
private completionError: { message: string; stack: string } | null = null;
|
|
19
|
+
private completed = false;
|
|
20
|
+
|
|
21
|
+
getData(): Record<string, any> {
|
|
22
|
+
// No data needed
|
|
23
|
+
return {};
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
getDescription(): string {
|
|
27
|
+
return "";
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
getRuntime(): (sandboxId: string) => void {
|
|
31
|
+
return (_sandboxId: string) => {
|
|
32
|
+
// Store truly original console methods on first wrap only
|
|
33
|
+
// This prevents accumulation of wrapper functions across multiple executions
|
|
34
|
+
if (!(window as any).__originalConsole) {
|
|
35
|
+
(window as any).__originalConsole = {
|
|
36
|
+
log: console.log.bind(console),
|
|
37
|
+
error: console.error.bind(console),
|
|
38
|
+
warn: console.warn.bind(console),
|
|
39
|
+
info: console.info.bind(console),
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// Always use the truly original console, not the current (possibly wrapped) one
|
|
44
|
+
const originalConsole = (window as any).__originalConsole;
|
|
45
|
+
|
|
46
|
+
// Track pending send promises to wait for them in onCompleted
|
|
47
|
+
const pendingSends: Promise<any>[] = [];
|
|
48
|
+
|
|
49
|
+
["log", "error", "warn", "info"].forEach((method) => {
|
|
50
|
+
(console as any)[method] = (...args: any[]) => {
|
|
51
|
+
const text = args
|
|
52
|
+
.map((arg) => {
|
|
53
|
+
try {
|
|
54
|
+
return typeof arg === "object" ? JSON.stringify(arg) : String(arg);
|
|
55
|
+
} catch {
|
|
56
|
+
return String(arg);
|
|
57
|
+
}
|
|
58
|
+
})
|
|
59
|
+
.join(" ");
|
|
60
|
+
|
|
61
|
+
// Always log locally too (using truly original console)
|
|
62
|
+
(originalConsole as any)[method].apply(console, args);
|
|
63
|
+
|
|
64
|
+
// Send immediately and track the promise (only in extension context)
|
|
65
|
+
if ((window as any).sendRuntimeMessage) {
|
|
66
|
+
const sendPromise = (window as any)
|
|
67
|
+
.sendRuntimeMessage({
|
|
68
|
+
type: "console",
|
|
69
|
+
method,
|
|
70
|
+
text,
|
|
71
|
+
args,
|
|
72
|
+
})
|
|
73
|
+
.catch(() => {});
|
|
74
|
+
pendingSends.push(sendPromise);
|
|
75
|
+
}
|
|
76
|
+
};
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
// Register completion callback to wait for all pending sends
|
|
80
|
+
if ((window as any).onCompleted) {
|
|
81
|
+
(window as any).onCompleted(async (_success: boolean) => {
|
|
82
|
+
// Wait for all pending console sends to complete
|
|
83
|
+
if (pendingSends.length > 0) {
|
|
84
|
+
await Promise.all(pendingSends);
|
|
85
|
+
}
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// Track errors for HTML artifacts
|
|
90
|
+
let lastError: { message: string; stack: string } | null = null;
|
|
91
|
+
|
|
92
|
+
// Error handlers - track errors but don't log them
|
|
93
|
+
// (they'll be shown via execution-error message)
|
|
94
|
+
window.addEventListener("error", (e) => {
|
|
95
|
+
const text =
|
|
96
|
+
(e.error?.stack || e.message || String(e)) + " at line " + (e.lineno || "?") + ":" + (e.colno || "?");
|
|
97
|
+
|
|
98
|
+
lastError = {
|
|
99
|
+
message: e.error?.message || e.message || String(e),
|
|
100
|
+
stack: e.error?.stack || text,
|
|
101
|
+
};
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
window.addEventListener("unhandledrejection", (e) => {
|
|
105
|
+
const text = "Unhandled promise rejection: " + (e.reason?.message || e.reason || "Unknown error");
|
|
106
|
+
|
|
107
|
+
lastError = {
|
|
108
|
+
message: e.reason?.message || String(e.reason) || "Unhandled promise rejection",
|
|
109
|
+
stack: e.reason?.stack || text,
|
|
110
|
+
};
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
// Expose complete() method for user code to call
|
|
114
|
+
let completionSent = false;
|
|
115
|
+
(window as any).complete = async (error?: { message: string; stack: string }, returnValue?: any) => {
|
|
116
|
+
if (completionSent) return;
|
|
117
|
+
completionSent = true;
|
|
118
|
+
|
|
119
|
+
const finalError = error || lastError;
|
|
120
|
+
|
|
121
|
+
if ((window as any).sendRuntimeMessage) {
|
|
122
|
+
if (finalError) {
|
|
123
|
+
await (window as any).sendRuntimeMessage({
|
|
124
|
+
type: "execution-error",
|
|
125
|
+
error: finalError,
|
|
126
|
+
});
|
|
127
|
+
} else {
|
|
128
|
+
await (window as any).sendRuntimeMessage({
|
|
129
|
+
type: "execution-complete",
|
|
130
|
+
returnValue,
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
};
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
async handleMessage(message: any, respond: (response: any) => void): Promise<void> {
|
|
139
|
+
if (message.type === "console") {
|
|
140
|
+
// Collect console output
|
|
141
|
+
this.logs.push({
|
|
142
|
+
type:
|
|
143
|
+
message.method === "error"
|
|
144
|
+
? "error"
|
|
145
|
+
: message.method === "warn"
|
|
146
|
+
? "warn"
|
|
147
|
+
: message.method === "info"
|
|
148
|
+
? "info"
|
|
149
|
+
: "log",
|
|
150
|
+
text: message.text,
|
|
151
|
+
args: message.args,
|
|
152
|
+
});
|
|
153
|
+
// Acknowledge receipt
|
|
154
|
+
respond({ success: true });
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* Get collected console logs
|
|
160
|
+
*/
|
|
161
|
+
getLogs(): ConsoleLog[] {
|
|
162
|
+
return this.logs;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* Get completion status
|
|
167
|
+
*/
|
|
168
|
+
isCompleted(): boolean {
|
|
169
|
+
return this.completed;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* Get completion error if any
|
|
174
|
+
*/
|
|
175
|
+
getCompletionError(): { message: string; stack: string } | null {
|
|
176
|
+
return this.completionError;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
/**
|
|
180
|
+
* Reset state for reuse
|
|
181
|
+
*/
|
|
182
|
+
reset(): void {
|
|
183
|
+
this.logs = [];
|
|
184
|
+
this.completionError = null;
|
|
185
|
+
this.completed = false;
|
|
186
|
+
}
|
|
187
|
+
}
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import type { SandboxRuntimeProvider } from "./SandboxRuntimeProvider.js";
|
|
2
|
+
|
|
3
|
+
export interface DownloadableFile {
|
|
4
|
+
fileName: string;
|
|
5
|
+
content: string | Uint8Array;
|
|
6
|
+
mimeType: string;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* File Download Runtime Provider
|
|
11
|
+
*
|
|
12
|
+
* Provides returnDownloadableFile() for creating user downloads.
|
|
13
|
+
* Files returned this way are NOT accessible to the LLM later (one-time download).
|
|
14
|
+
* Works both online (sends to extension) and offline (triggers browser download directly).
|
|
15
|
+
* Collects files for retrieval by caller.
|
|
16
|
+
*/
|
|
17
|
+
export class FileDownloadRuntimeProvider implements SandboxRuntimeProvider {
|
|
18
|
+
private files: DownloadableFile[] = [];
|
|
19
|
+
|
|
20
|
+
getData(): Record<string, any> {
|
|
21
|
+
// No data needed
|
|
22
|
+
return {};
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
getRuntime(): (sandboxId: string) => void {
|
|
26
|
+
return (_sandboxId: string) => {
|
|
27
|
+
(window as any).returnDownloadableFile = async (fileName: string, content: any, mimeType?: string) => {
|
|
28
|
+
let finalContent: any, finalMimeType: string;
|
|
29
|
+
|
|
30
|
+
if (content instanceof Blob) {
|
|
31
|
+
const arrayBuffer = await content.arrayBuffer();
|
|
32
|
+
finalContent = new Uint8Array(arrayBuffer);
|
|
33
|
+
finalMimeType = mimeType || content.type || "application/octet-stream";
|
|
34
|
+
if (!mimeType && !content.type) {
|
|
35
|
+
throw new Error(
|
|
36
|
+
"returnDownloadableFile: MIME type is required for Blob content. Please provide a mimeType parameter (e.g., 'image/png').",
|
|
37
|
+
);
|
|
38
|
+
}
|
|
39
|
+
} else if (content instanceof Uint8Array) {
|
|
40
|
+
finalContent = content;
|
|
41
|
+
if (!mimeType) {
|
|
42
|
+
throw new Error(
|
|
43
|
+
"returnDownloadableFile: MIME type is required for Uint8Array content. Please provide a mimeType parameter (e.g., 'image/png').",
|
|
44
|
+
);
|
|
45
|
+
}
|
|
46
|
+
finalMimeType = mimeType;
|
|
47
|
+
} else if (typeof content === "string") {
|
|
48
|
+
finalContent = content;
|
|
49
|
+
finalMimeType = mimeType || "text/plain";
|
|
50
|
+
} else {
|
|
51
|
+
finalContent = JSON.stringify(content, null, 2);
|
|
52
|
+
finalMimeType = mimeType || "application/json";
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// Send to extension if in extension context (online mode)
|
|
56
|
+
if ((window as any).sendRuntimeMessage) {
|
|
57
|
+
const response = await (window as any).sendRuntimeMessage({
|
|
58
|
+
type: "file-returned",
|
|
59
|
+
fileName,
|
|
60
|
+
content: finalContent,
|
|
61
|
+
mimeType: finalMimeType,
|
|
62
|
+
});
|
|
63
|
+
if (response.error) throw new Error(response.error);
|
|
64
|
+
} else {
|
|
65
|
+
// Offline mode: trigger browser download directly
|
|
66
|
+
const blob = new Blob([finalContent instanceof Uint8Array ? finalContent : finalContent], {
|
|
67
|
+
type: finalMimeType,
|
|
68
|
+
});
|
|
69
|
+
const url = URL.createObjectURL(blob);
|
|
70
|
+
const a = document.createElement("a");
|
|
71
|
+
a.href = url;
|
|
72
|
+
a.download = fileName;
|
|
73
|
+
a.click();
|
|
74
|
+
URL.revokeObjectURL(url);
|
|
75
|
+
}
|
|
76
|
+
};
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
async handleMessage(message: any, respond: (response: any) => void): Promise<void> {
|
|
81
|
+
if (message.type === "file-returned") {
|
|
82
|
+
// Collect file for caller
|
|
83
|
+
this.files.push({
|
|
84
|
+
fileName: message.fileName,
|
|
85
|
+
content: message.content,
|
|
86
|
+
mimeType: message.mimeType,
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
respond({ success: true });
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Get collected files
|
|
95
|
+
*/
|
|
96
|
+
getFiles(): DownloadableFile[] {
|
|
97
|
+
return this.files;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Reset state for reuse
|
|
102
|
+
*/
|
|
103
|
+
reset(): void {
|
|
104
|
+
this.files = [];
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
getDescription(): string {
|
|
108
|
+
return "returnDownloadableFile(filename, content, mimeType?) - Create downloadable file for user (one-time download, not accessible later)";
|
|
109
|
+
}
|
|
110
|
+
}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Generates sendRuntimeMessage() function for injection into execution contexts.
|
|
3
|
+
* Provides unified messaging API that works in both sandbox iframe and user script contexts.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
export type MessageType = "request-response" | "fire-and-forget";
|
|
7
|
+
|
|
8
|
+
export interface RuntimeMessageBridgeOptions {
|
|
9
|
+
context: "sandbox-iframe" | "user-script";
|
|
10
|
+
sandboxId: string;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
// biome-ignore lint/complexity/noStaticOnlyClass: fine
|
|
14
|
+
export class RuntimeMessageBridge {
|
|
15
|
+
/**
|
|
16
|
+
* Generate sendRuntimeMessage() function as injectable string.
|
|
17
|
+
* Returns the function source code to be injected into target context.
|
|
18
|
+
*/
|
|
19
|
+
static generateBridgeCode(options: RuntimeMessageBridgeOptions): string {
|
|
20
|
+
if (options.context === "sandbox-iframe") {
|
|
21
|
+
return RuntimeMessageBridge.generateSandboxBridge(options.sandboxId);
|
|
22
|
+
} else {
|
|
23
|
+
return RuntimeMessageBridge.generateUserScriptBridge(options.sandboxId);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
private static generateSandboxBridge(sandboxId: string): string {
|
|
28
|
+
// Returns stringified function that uses window.parent.postMessage
|
|
29
|
+
return `
|
|
30
|
+
window.__completionCallbacks = [];
|
|
31
|
+
window.sendRuntimeMessage = async (message) => {
|
|
32
|
+
const messageId = 'msg_' + Date.now() + '_' + Math.random().toString(36).substring(2, 9);
|
|
33
|
+
|
|
34
|
+
return new Promise((resolve, reject) => {
|
|
35
|
+
const handler = (e) => {
|
|
36
|
+
if (e.data.type === 'runtime-response' && e.data.messageId === messageId) {
|
|
37
|
+
window.removeEventListener('message', handler);
|
|
38
|
+
if (e.data.success) {
|
|
39
|
+
resolve(e.data);
|
|
40
|
+
} else {
|
|
41
|
+
reject(new Error(e.data.error || 'Operation failed'));
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
window.addEventListener('message', handler);
|
|
47
|
+
|
|
48
|
+
window.parent.postMessage({
|
|
49
|
+
...message,
|
|
50
|
+
sandboxId: ${JSON.stringify(sandboxId)},
|
|
51
|
+
messageId: messageId
|
|
52
|
+
}, '*');
|
|
53
|
+
|
|
54
|
+
// Timeout after 30s
|
|
55
|
+
setTimeout(() => {
|
|
56
|
+
window.removeEventListener('message', handler);
|
|
57
|
+
reject(new Error('Runtime message timeout'));
|
|
58
|
+
}, 30000);
|
|
59
|
+
});
|
|
60
|
+
};
|
|
61
|
+
window.onCompleted = (callback) => {
|
|
62
|
+
window.__completionCallbacks.push(callback);
|
|
63
|
+
};
|
|
64
|
+
`.trim();
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
private static generateUserScriptBridge(sandboxId: string): string {
|
|
68
|
+
// Returns stringified function that uses chrome.runtime.sendMessage
|
|
69
|
+
return `
|
|
70
|
+
window.__completionCallbacks = [];
|
|
71
|
+
window.sendRuntimeMessage = async (message) => {
|
|
72
|
+
return await chrome.runtime.sendMessage({
|
|
73
|
+
...message,
|
|
74
|
+
sandboxId: ${JSON.stringify(sandboxId)}
|
|
75
|
+
});
|
|
76
|
+
};
|
|
77
|
+
window.onCompleted = (callback) => {
|
|
78
|
+
window.__completionCallbacks.push(callback);
|
|
79
|
+
};
|
|
80
|
+
`.trim();
|
|
81
|
+
}
|
|
82
|
+
}
|