@mariozechner/pi-web-ui 0.5.44
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 +252 -0
- package/dist/ChatPanel.d.ts +23 -0
- package/dist/ChatPanel.d.ts.map +1 -0
- package/dist/ChatPanel.js +224 -0
- package/dist/ChatPanel.js.map +1 -0
- package/dist/app.css +2 -0
- package/dist/components/AgentInterface.d.ts +35 -0
- package/dist/components/AgentInterface.d.ts.map +1 -0
- package/dist/components/AgentInterface.js +308 -0
- package/dist/components/AgentInterface.js.map +1 -0
- package/dist/components/AttachmentTile.d.ts +12 -0
- package/dist/components/AttachmentTile.d.ts.map +1 -0
- package/dist/components/AttachmentTile.js +114 -0
- package/dist/components/AttachmentTile.js.map +1 -0
- package/dist/components/ConsoleBlock.d.ts +11 -0
- package/dist/components/ConsoleBlock.d.ts.map +1 -0
- package/dist/components/ConsoleBlock.js +77 -0
- package/dist/components/ConsoleBlock.js.map +1 -0
- package/dist/components/Input.d.ts +26 -0
- package/dist/components/Input.d.ts.map +1 -0
- package/dist/components/Input.js +56 -0
- package/dist/components/Input.js.map +1 -0
- package/dist/components/MessageEditor.d.ts +38 -0
- package/dist/components/MessageEditor.d.ts.map +1 -0
- package/dist/components/MessageEditor.js +296 -0
- package/dist/components/MessageEditor.js.map +1 -0
- package/dist/components/MessageList.d.ts +13 -0
- package/dist/components/MessageList.d.ts.map +1 -0
- package/dist/components/MessageList.js +88 -0
- package/dist/components/MessageList.js.map +1 -0
- package/dist/components/Messages.d.ts +53 -0
- package/dist/components/Messages.d.ts.map +1 -0
- package/dist/components/Messages.js +323 -0
- package/dist/components/Messages.js.map +1 -0
- package/dist/components/ProviderKeyInput.d.ts +16 -0
- package/dist/components/ProviderKeyInput.d.ts.map +1 -0
- package/dist/components/ProviderKeyInput.js +183 -0
- package/dist/components/ProviderKeyInput.js.map +1 -0
- package/dist/components/SandboxedIframe.d.ts +63 -0
- package/dist/components/SandboxedIframe.d.ts.map +1 -0
- package/dist/components/SandboxedIframe.js +435 -0
- package/dist/components/SandboxedIframe.js.map +1 -0
- package/dist/components/StreamingMessageContainer.d.ts +17 -0
- package/dist/components/StreamingMessageContainer.d.ts.map +1 -0
- package/dist/components/StreamingMessageContainer.js +114 -0
- package/dist/components/StreamingMessageContainer.js.map +1 -0
- package/dist/dialogs/ApiKeyPromptDialog.d.ts +15 -0
- package/dist/dialogs/ApiKeyPromptDialog.d.ts.map +1 -0
- package/dist/dialogs/ApiKeyPromptDialog.js +80 -0
- package/dist/dialogs/ApiKeyPromptDialog.js.map +1 -0
- package/dist/dialogs/AttachmentOverlay.d.ts +32 -0
- package/dist/dialogs/AttachmentOverlay.d.ts.map +1 -0
- package/dist/dialogs/AttachmentOverlay.js +575 -0
- package/dist/dialogs/AttachmentOverlay.js.map +1 -0
- package/dist/dialogs/ModelSelector.d.ts +27 -0
- package/dist/dialogs/ModelSelector.d.ts.map +1 -0
- package/dist/dialogs/ModelSelector.js +334 -0
- package/dist/dialogs/ModelSelector.js.map +1 -0
- package/dist/dialogs/SettingsDialog.d.ts +31 -0
- package/dist/dialogs/SettingsDialog.d.ts.map +1 -0
- package/dist/dialogs/SettingsDialog.js +228 -0
- package/dist/dialogs/SettingsDialog.js.map +1 -0
- package/dist/index.d.ts +46 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +51 -0
- package/dist/index.js.map +1 -0
- package/dist/state/agent-session.d.ts +58 -0
- package/dist/state/agent-session.d.ts.map +1 -0
- package/dist/state/agent-session.js +252 -0
- package/dist/state/agent-session.js.map +1 -0
- package/dist/state/transports/AppTransport.d.ts +13 -0
- package/dist/state/transports/AppTransport.d.ts.map +1 -0
- package/dist/state/transports/AppTransport.js +316 -0
- package/dist/state/transports/AppTransport.js.map +1 -0
- package/dist/state/transports/ProviderTransport.d.ts +12 -0
- package/dist/state/transports/ProviderTransport.d.ts.map +1 -0
- package/dist/state/transports/ProviderTransport.js +44 -0
- package/dist/state/transports/ProviderTransport.js.map +1 -0
- package/dist/state/transports/index.d.ts +4 -0
- package/dist/state/transports/index.d.ts.map +1 -0
- package/dist/state/transports/index.js +4 -0
- package/dist/state/transports/index.js.map +1 -0
- package/dist/state/transports/proxy-types.d.ts +48 -0
- package/dist/state/transports/proxy-types.d.ts.map +1 -0
- package/dist/state/transports/proxy-types.js +2 -0
- package/dist/state/transports/proxy-types.js.map +1 -0
- package/dist/state/transports/types.d.ts +11 -0
- package/dist/state/transports/types.d.ts.map +1 -0
- package/dist/state/transports/types.js +2 -0
- package/dist/state/transports/types.js.map +1 -0
- package/dist/state/types.d.ts +15 -0
- package/dist/state/types.d.ts.map +1 -0
- package/dist/state/types.js +2 -0
- package/dist/state/types.js.map +1 -0
- package/dist/storage/app-storage.d.ts +26 -0
- package/dist/storage/app-storage.d.ts.map +1 -0
- package/dist/storage/app-storage.js +44 -0
- package/dist/storage/app-storage.js.map +1 -0
- package/dist/storage/backends/chrome-storage-backend.d.ts +18 -0
- package/dist/storage/backends/chrome-storage-backend.d.ts.map +1 -0
- package/dist/storage/backends/chrome-storage-backend.js +67 -0
- package/dist/storage/backends/chrome-storage-backend.js.map +1 -0
- package/dist/storage/backends/indexeddb-backend.d.ts +20 -0
- package/dist/storage/backends/indexeddb-backend.d.ts.map +1 -0
- package/dist/storage/backends/indexeddb-backend.js +89 -0
- package/dist/storage/backends/indexeddb-backend.js.map +1 -0
- package/dist/storage/backends/local-storage-backend.d.ts +18 -0
- package/dist/storage/backends/local-storage-backend.d.ts.map +1 -0
- package/dist/storage/backends/local-storage-backend.js +69 -0
- package/dist/storage/backends/local-storage-backend.js.map +1 -0
- package/dist/storage/repositories/provider-keys-repository.d.ts +34 -0
- package/dist/storage/repositories/provider-keys-repository.d.ts.map +1 -0
- package/dist/storage/repositories/provider-keys-repository.js +50 -0
- package/dist/storage/repositories/provider-keys-repository.js.map +1 -0
- package/dist/storage/repositories/settings-repository.d.ts +34 -0
- package/dist/storage/repositories/settings-repository.d.ts.map +1 -0
- package/dist/storage/repositories/settings-repository.js +46 -0
- package/dist/storage/repositories/settings-repository.js.map +1 -0
- package/dist/storage/types.d.ts +43 -0
- package/dist/storage/types.d.ts.map +1 -0
- package/dist/storage/types.js +2 -0
- package/dist/storage/types.js.map +1 -0
- package/dist/tools/artifacts/ArtifactElement.d.ts +10 -0
- package/dist/tools/artifacts/ArtifactElement.d.ts.map +1 -0
- package/dist/tools/artifacts/ArtifactElement.js +12 -0
- package/dist/tools/artifacts/ArtifactElement.js.map +1 -0
- package/dist/tools/artifacts/HtmlArtifact.d.ts +30 -0
- package/dist/tools/artifacts/HtmlArtifact.d.ts.map +1 -0
- package/dist/tools/artifacts/HtmlArtifact.js +217 -0
- package/dist/tools/artifacts/HtmlArtifact.js.map +1 -0
- package/dist/tools/artifacts/MarkdownArtifact.d.ts +20 -0
- package/dist/tools/artifacts/MarkdownArtifact.d.ts.map +1 -0
- package/dist/tools/artifacts/MarkdownArtifact.js +84 -0
- package/dist/tools/artifacts/MarkdownArtifact.js.map +1 -0
- package/dist/tools/artifacts/SvgArtifact.d.ts +19 -0
- package/dist/tools/artifacts/SvgArtifact.d.ts.map +1 -0
- package/dist/tools/artifacts/SvgArtifact.js +80 -0
- package/dist/tools/artifacts/SvgArtifact.js.map +1 -0
- package/dist/tools/artifacts/TextArtifact.d.ts +20 -0
- package/dist/tools/artifacts/TextArtifact.d.ts.map +1 -0
- package/dist/tools/artifacts/TextArtifact.js +147 -0
- package/dist/tools/artifacts/TextArtifact.js.map +1 -0
- package/dist/tools/artifacts/artifacts.d.ts +67 -0
- package/dist/tools/artifacts/artifacts.d.ts.map +1 -0
- package/dist/tools/artifacts/artifacts.js +836 -0
- package/dist/tools/artifacts/artifacts.js.map +1 -0
- package/dist/tools/artifacts/index.d.ts +7 -0
- package/dist/tools/artifacts/index.d.ts.map +1 -0
- package/dist/tools/artifacts/index.js +7 -0
- package/dist/tools/artifacts/index.js.map +1 -0
- package/dist/tools/index.d.ts +14 -0
- package/dist/tools/index.d.ts.map +1 -0
- package/dist/tools/index.js +29 -0
- package/dist/tools/index.js.map +1 -0
- package/dist/tools/javascript-repl.d.ts +43 -0
- package/dist/tools/javascript-repl.d.ts.map +1 -0
- package/dist/tools/javascript-repl.js +252 -0
- package/dist/tools/javascript-repl.js.map +1 -0
- package/dist/tools/renderer-registry.d.ts +11 -0
- package/dist/tools/renderer-registry.d.ts.map +1 -0
- package/dist/tools/renderer-registry.js +15 -0
- package/dist/tools/renderer-registry.js.map +1 -0
- package/dist/tools/renderers/BashRenderer.d.ts +12 -0
- package/dist/tools/renderers/BashRenderer.d.ts.map +1 -0
- package/dist/tools/renderers/BashRenderer.js +35 -0
- package/dist/tools/renderers/BashRenderer.js.map +1 -0
- package/dist/tools/renderers/CalculateRenderer.d.ts +12 -0
- package/dist/tools/renderers/CalculateRenderer.d.ts.map +1 -0
- package/dist/tools/renderers/CalculateRenderer.js +38 -0
- package/dist/tools/renderers/CalculateRenderer.js.map +1 -0
- package/dist/tools/renderers/DefaultRenderer.d.ts +8 -0
- package/dist/tools/renderers/DefaultRenderer.d.ts.map +1 -0
- package/dist/tools/renderers/DefaultRenderer.js +31 -0
- package/dist/tools/renderers/DefaultRenderer.js.map +1 -0
- package/dist/tools/renderers/GetCurrentTimeRenderer.d.ts +12 -0
- package/dist/tools/renderers/GetCurrentTimeRenderer.d.ts.map +1 -0
- package/dist/tools/renderers/GetCurrentTimeRenderer.js +30 -0
- package/dist/tools/renderers/GetCurrentTimeRenderer.js.map +1 -0
- package/dist/tools/types.d.ts +7 -0
- package/dist/tools/types.d.ts.map +1 -0
- package/dist/tools/types.js +2 -0
- package/dist/tools/types.js.map +1 -0
- package/dist/utils/attachment-utils.d.ts +19 -0
- package/dist/utils/attachment-utils.d.ts.map +1 -0
- package/dist/utils/attachment-utils.js +415 -0
- package/dist/utils/attachment-utils.js.map +1 -0
- package/dist/utils/auth-token.d.ts +3 -0
- package/dist/utils/auth-token.d.ts.map +1 -0
- package/dist/utils/auth-token.js +19 -0
- package/dist/utils/auth-token.js.map +1 -0
- package/dist/utils/format.d.ts +6 -0
- package/dist/utils/format.d.ts.map +1 -0
- package/dist/utils/format.js +47 -0
- package/dist/utils/format.js.map +1 -0
- package/dist/utils/i18n.d.ts +111 -0
- package/dist/utils/i18n.d.ts.map +1 -0
- package/dist/utils/i18n.js +224 -0
- package/dist/utils/i18n.js.map +1 -0
- package/dist/utils/test-sessions.d.ts +347 -0
- package/dist/utils/test-sessions.d.ts.map +1 -0
- package/dist/utils/test-sessions.js +2215 -0
- package/dist/utils/test-sessions.js.map +1 -0
- package/example/README.md +61 -0
- package/example/index.html +13 -0
- package/example/package-lock.json +1965 -0
- package/example/package.json +22 -0
- package/example/src/app.css +1 -0
- package/example/src/main.ts +57 -0
- package/example/src/test-sessions.ts +104 -0
- package/example/tsconfig.json +15 -0
- package/example/vite.config.ts +6 -0
- package/package.json +45 -0
- package/src/ChatPanel.ts +214 -0
- package/src/app.css +44 -0
- package/src/components/AgentInterface.ts +316 -0
- package/src/components/AttachmentTile.ts +112 -0
- package/src/components/ConsoleBlock.ts +67 -0
- package/src/components/Input.ts +112 -0
- package/src/components/MessageEditor.ts +272 -0
- package/src/components/MessageList.ts +82 -0
- package/src/components/Messages.ts +310 -0
- package/src/components/ProviderKeyInput.ts +170 -0
- package/src/components/SandboxedIframe.ts +525 -0
- package/src/components/StreamingMessageContainer.ts +101 -0
- package/src/dialogs/ApiKeyPromptDialog.ts +76 -0
- package/src/dialogs/AttachmentOverlay.ts +635 -0
- package/src/dialogs/ModelSelector.ts +324 -0
- package/src/dialogs/SettingsDialog.ts +223 -0
- package/src/index.ts +63 -0
- package/src/state/agent-session.ts +311 -0
- package/src/state/transports/AppTransport.ts +363 -0
- package/src/state/transports/ProviderTransport.ts +49 -0
- package/src/state/transports/index.ts +3 -0
- package/src/state/transports/proxy-types.ts +15 -0
- package/src/state/transports/types.ts +16 -0
- package/src/state/types.ts +11 -0
- package/src/storage/app-storage.ts +53 -0
- package/src/storage/backends/chrome-storage-backend.ts +82 -0
- package/src/storage/backends/indexeddb-backend.ts +107 -0
- package/src/storage/backends/local-storage-backend.ts +74 -0
- package/src/storage/repositories/provider-keys-repository.ts +55 -0
- package/src/storage/repositories/settings-repository.ts +51 -0
- package/src/storage/types.ts +48 -0
- package/src/tools/artifacts/ArtifactElement.ts +15 -0
- package/src/tools/artifacts/HtmlArtifact.ts +221 -0
- package/src/tools/artifacts/MarkdownArtifact.ts +81 -0
- package/src/tools/artifacts/SvgArtifact.ts +77 -0
- package/src/tools/artifacts/TextArtifact.ts +148 -0
- package/src/tools/artifacts/artifacts.ts +888 -0
- package/src/tools/artifacts/index.ts +6 -0
- package/src/tools/index.ts +35 -0
- package/src/tools/javascript-repl.ts +309 -0
- package/src/tools/renderer-registry.ts +18 -0
- package/src/tools/renderers/BashRenderer.ts +45 -0
- package/src/tools/renderers/CalculateRenderer.ts +49 -0
- package/src/tools/renderers/DefaultRenderer.ts +36 -0
- package/src/tools/renderers/GetCurrentTimeRenderer.ts +39 -0
- package/src/tools/types.ts +7 -0
- package/src/utils/attachment-utils.ts +472 -0
- package/src/utils/auth-token.ts +22 -0
- package/src/utils/format.ts +42 -0
- package/src/utils/i18n.ts +343 -0
- package/src/utils/test-sessions.ts +2247 -0
- package/tsconfig.build.json +20 -0
- package/tsconfig.json +7 -0
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export { ArtifactElement } from "./ArtifactElement.js";
|
|
2
|
+
export { type Artifact, ArtifactsPanel, type ArtifactsParams } from "./artifacts.js";
|
|
3
|
+
export { HtmlArtifact } from "./HtmlArtifact.js";
|
|
4
|
+
export { MarkdownArtifact } from "./MarkdownArtifact.js";
|
|
5
|
+
export { SvgArtifact } from "./SvgArtifact.js";
|
|
6
|
+
export { TextArtifact } from "./TextArtifact.js";
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import type { TemplateResult } from "@mariozechner/mini-lit";
|
|
2
|
+
import type { ToolResultMessage } from "@mariozechner/pi-ai";
|
|
3
|
+
import { getToolRenderer, registerToolRenderer } from "./renderer-registry.js";
|
|
4
|
+
import { BashRenderer } from "./renderers/BashRenderer.js";
|
|
5
|
+
import { DefaultRenderer } from "./renderers/DefaultRenderer.js";
|
|
6
|
+
import "./javascript-repl.js"; // Auto-registers the renderer
|
|
7
|
+
|
|
8
|
+
// Register all built-in tool renderers
|
|
9
|
+
registerToolRenderer("bash", new BashRenderer());
|
|
10
|
+
|
|
11
|
+
const defaultRenderer = new DefaultRenderer();
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Render tool call parameters
|
|
15
|
+
*/
|
|
16
|
+
export function renderToolParams(toolName: string, params: any, isStreaming?: boolean): TemplateResult {
|
|
17
|
+
const renderer = getToolRenderer(toolName);
|
|
18
|
+
if (renderer) {
|
|
19
|
+
return renderer.renderParams(params, isStreaming);
|
|
20
|
+
}
|
|
21
|
+
return defaultRenderer.renderParams(params, isStreaming);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Render tool result
|
|
26
|
+
*/
|
|
27
|
+
export function renderToolResult(toolName: string, params: any, result: ToolResultMessage): TemplateResult {
|
|
28
|
+
const renderer = getToolRenderer(toolName);
|
|
29
|
+
if (renderer) {
|
|
30
|
+
return renderer.renderResult(params, result);
|
|
31
|
+
}
|
|
32
|
+
return defaultRenderer.renderResult(params, result);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export { registerToolRenderer, getToolRenderer };
|
|
@@ -0,0 +1,309 @@
|
|
|
1
|
+
import { html, i18n, type TemplateResult } from "@mariozechner/mini-lit";
|
|
2
|
+
import type { AgentTool, ToolResultMessage } from "@mariozechner/pi-ai";
|
|
3
|
+
import { type Static, Type } from "@sinclair/typebox";
|
|
4
|
+
import { type SandboxFile, SandboxIframe, type SandboxResult } from "../components/SandboxedIframe.js";
|
|
5
|
+
import type { Attachment } from "../utils/attachment-utils.js";
|
|
6
|
+
|
|
7
|
+
import { registerToolRenderer } from "./renderer-registry.js";
|
|
8
|
+
import type { ToolRenderer } from "./types.js";
|
|
9
|
+
|
|
10
|
+
// Execute JavaScript code with attachments using SandboxedIframe
|
|
11
|
+
export async function executeJavaScript(
|
|
12
|
+
code: string,
|
|
13
|
+
attachments: Attachment[] = [],
|
|
14
|
+
signal?: AbortSignal,
|
|
15
|
+
sandboxUrlProvider?: () => string,
|
|
16
|
+
): Promise<{ output: string; files?: SandboxFile[] }> {
|
|
17
|
+
if (!code) {
|
|
18
|
+
throw new Error("Code parameter is required");
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
// Check for abort before starting
|
|
22
|
+
if (signal?.aborted) {
|
|
23
|
+
throw new Error("Execution aborted");
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// Create a SandboxedIframe instance for execution
|
|
27
|
+
const sandbox = new SandboxIframe();
|
|
28
|
+
if (sandboxUrlProvider) {
|
|
29
|
+
sandbox.sandboxUrlProvider = sandboxUrlProvider;
|
|
30
|
+
}
|
|
31
|
+
sandbox.style.display = "none";
|
|
32
|
+
document.body.appendChild(sandbox);
|
|
33
|
+
|
|
34
|
+
try {
|
|
35
|
+
const sandboxId = `repl-${Date.now()}`;
|
|
36
|
+
const result: SandboxResult = await sandbox.execute(sandboxId, code, attachments, signal);
|
|
37
|
+
|
|
38
|
+
// Remove the sandbox iframe after execution
|
|
39
|
+
sandbox.remove();
|
|
40
|
+
|
|
41
|
+
// Return plain text output
|
|
42
|
+
if (!result.success) {
|
|
43
|
+
// Return error as plain text
|
|
44
|
+
return {
|
|
45
|
+
output: `Error: ${result.error?.message || "Unknown error"}\n${result.error?.stack || ""}`,
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// Build plain text response
|
|
50
|
+
let output = "";
|
|
51
|
+
|
|
52
|
+
// Add console output - result.console contains { type: string, text: string } from sandbox.js
|
|
53
|
+
if (result.console && result.console.length > 0) {
|
|
54
|
+
for (const entry of result.console) {
|
|
55
|
+
const prefix = entry.type === "error" ? "[ERROR]" : "";
|
|
56
|
+
output += (prefix ? `${prefix} ` : "") + entry.text + "\n";
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// Add file notifications
|
|
61
|
+
if (result.files && result.files.length > 0) {
|
|
62
|
+
output += `\n[Files returned: ${result.files.length}]\n`;
|
|
63
|
+
for (const file of result.files) {
|
|
64
|
+
output += ` - ${file.fileName} (${file.mimeType})\n`;
|
|
65
|
+
}
|
|
66
|
+
} else {
|
|
67
|
+
// Explicitly note when no files were returned (helpful for debugging)
|
|
68
|
+
if (code.includes("returnFile")) {
|
|
69
|
+
output += "\n[No files returned - check async operations]";
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
return {
|
|
74
|
+
output: output.trim() || "Code executed successfully (no output)",
|
|
75
|
+
files: result.files,
|
|
76
|
+
};
|
|
77
|
+
} catch (error: unknown) {
|
|
78
|
+
// Clean up on error
|
|
79
|
+
sandbox.remove();
|
|
80
|
+
throw new Error((error as Error).message || "Execution failed");
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
export type JavaScriptReplToolResult = {
|
|
85
|
+
files?:
|
|
86
|
+
| {
|
|
87
|
+
fileName: string;
|
|
88
|
+
contentBase64: string;
|
|
89
|
+
mimeType: string;
|
|
90
|
+
}[]
|
|
91
|
+
| undefined;
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
const javascriptReplSchema = Type.Object({
|
|
95
|
+
code: Type.String({ description: "JavaScript code to execute" }),
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
export function createJavaScriptReplTool(): AgentTool<typeof javascriptReplSchema, JavaScriptReplToolResult> & {
|
|
99
|
+
attachmentsProvider?: () => Attachment[];
|
|
100
|
+
sandboxUrlProvider?: () => string;
|
|
101
|
+
} {
|
|
102
|
+
return {
|
|
103
|
+
label: "JavaScript REPL",
|
|
104
|
+
name: "javascript_repl",
|
|
105
|
+
attachmentsProvider: () => [], // default to empty array
|
|
106
|
+
sandboxUrlProvider: undefined, // optional, for browser extensions
|
|
107
|
+
description: `Execute JavaScript code in a sandboxed browser environment with full modern browser capabilities.
|
|
108
|
+
|
|
109
|
+
Environment: Modern browser with ALL Web APIs available:
|
|
110
|
+
- ES2023+ JavaScript (async/await, optional chaining, nullish coalescing, etc.)
|
|
111
|
+
- DOM APIs (document, window, Canvas, WebGL, etc.)
|
|
112
|
+
- Fetch API for HTTP requests
|
|
113
|
+
|
|
114
|
+
Loading external libraries via dynamic imports (use esm.run):
|
|
115
|
+
- XLSX (Excel files): const XLSX = await import('https://esm.run/xlsx');
|
|
116
|
+
- Papa Parse (CSV): const Papa = (await import('https://esm.run/papaparse')).default;
|
|
117
|
+
- Lodash: const _ = await import('https://esm.run/lodash-es');
|
|
118
|
+
- D3.js: const d3 = await import('https://esm.run/d3');
|
|
119
|
+
- Chart.js: const Chart = (await import('https://esm.run/chart.js/auto')).default;
|
|
120
|
+
- Three.js: const THREE = await import('https://esm.run/three');
|
|
121
|
+
- Any npm package: await import('https://esm.run/package-name')
|
|
122
|
+
|
|
123
|
+
IMPORTANT for graphics/canvas:
|
|
124
|
+
- Use fixed dimensions like 400x400 or 800x600, NOT window.innerWidth/Height
|
|
125
|
+
- For Three.js: renderer.setSize(400, 400) and camera aspect ratio of 1
|
|
126
|
+
- For Chart.js: Set options: { responsive: false, animation: false } to ensure immediate rendering
|
|
127
|
+
- Web Storage (localStorage, sessionStorage, IndexedDB)
|
|
128
|
+
- Web Workers, WebAssembly, WebSockets
|
|
129
|
+
- Media APIs (Audio, Video, WebRTC)
|
|
130
|
+
- File APIs (Blob, FileReader, etc.)
|
|
131
|
+
- Crypto API for cryptography
|
|
132
|
+
- And much more - anything a modern browser supports!
|
|
133
|
+
|
|
134
|
+
Output:
|
|
135
|
+
- console.log() - All output is captured as text
|
|
136
|
+
- await returnFile(filename, content, mimeType?) - Create downloadable files (async function!)
|
|
137
|
+
* Always use await with returnFile
|
|
138
|
+
* REQUIRED: For Blob/Uint8Array binary content, you MUST supply a proper MIME type (e.g., "image/png").
|
|
139
|
+
If omitted, the REPL throws an Error with stack trace pointing to the offending line.
|
|
140
|
+
* Strings without a MIME default to text/plain.
|
|
141
|
+
* Objects are auto-JSON stringified and default to application/json unless a MIME is provided.
|
|
142
|
+
* Canvas images: Use toBlob() with await Promise wrapper
|
|
143
|
+
* Examples:
|
|
144
|
+
- await returnFile('data.txt', 'Hello World', 'text/plain')
|
|
145
|
+
- await returnFile('data.json', {key: 'value'}, 'application/json')
|
|
146
|
+
- await returnFile('data.csv', 'name,age\\nJohn,30', 'text/csv')
|
|
147
|
+
- Chart.js example:
|
|
148
|
+
const Chart = (await import('https://esm.run/chart.js/auto')).default;
|
|
149
|
+
const canvas = document.createElement('canvas');
|
|
150
|
+
canvas.width = 400; canvas.height = 300;
|
|
151
|
+
document.body.appendChild(canvas);
|
|
152
|
+
new Chart(canvas, {
|
|
153
|
+
type: 'line',
|
|
154
|
+
data: {
|
|
155
|
+
labels: ['Jan', 'Feb', 'Mar', 'Apr'],
|
|
156
|
+
datasets: [{ label: 'Sales', data: [10, 20, 15, 25], borderColor: 'blue' }]
|
|
157
|
+
},
|
|
158
|
+
options: { responsive: false, animation: false }
|
|
159
|
+
});
|
|
160
|
+
const blob = await new Promise(resolve => canvas.toBlob(resolve, 'image/png'));
|
|
161
|
+
await returnFile('chart.png', blob, 'image/png');
|
|
162
|
+
|
|
163
|
+
Global variables:
|
|
164
|
+
- attachments[] - Array of attachment objects from user messages
|
|
165
|
+
* Properties:
|
|
166
|
+
- id: string (unique identifier)
|
|
167
|
+
- fileName: string (e.g., "data.xlsx")
|
|
168
|
+
- mimeType: string (e.g., "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet")
|
|
169
|
+
- size: number (bytes)
|
|
170
|
+
* Helper functions:
|
|
171
|
+
- listFiles() - Returns array of {id, fileName, mimeType, size} for all attachments
|
|
172
|
+
- readTextFile(attachmentId) - Returns text content of attachment (for CSV, JSON, text files)
|
|
173
|
+
- readBinaryFile(attachmentId) - Returns Uint8Array of binary data (for images, Excel, etc.)
|
|
174
|
+
* Examples:
|
|
175
|
+
- const files = listFiles();
|
|
176
|
+
- const csvContent = readTextFile(files[0].id); // Read CSV as text
|
|
177
|
+
- const xlsxBytes = readBinaryFile(files[0].id); // Read Excel as binary
|
|
178
|
+
- All standard browser globals (window, document, fetch, etc.)`,
|
|
179
|
+
parameters: javascriptReplSchema,
|
|
180
|
+
execute: async function (_toolCallId: string, args: Static<typeof javascriptReplSchema>, signal?: AbortSignal) {
|
|
181
|
+
const attachments = this.attachmentsProvider?.() || [];
|
|
182
|
+
const result = await executeJavaScript(args.code, attachments, signal, this.sandboxUrlProvider);
|
|
183
|
+
// Convert files to JSON-serializable with base64 payloads
|
|
184
|
+
const files = (result.files || []).map((f) => {
|
|
185
|
+
const toBase64 = (input: string | Uint8Array): { base64: string; size: number } => {
|
|
186
|
+
if (input instanceof Uint8Array) {
|
|
187
|
+
let binary = "";
|
|
188
|
+
const chunk = 0x8000;
|
|
189
|
+
for (let i = 0; i < input.length; i += chunk) {
|
|
190
|
+
binary += String.fromCharCode(...input.subarray(i, i + chunk));
|
|
191
|
+
}
|
|
192
|
+
return { base64: btoa(binary), size: input.length };
|
|
193
|
+
} else if (typeof input === "string") {
|
|
194
|
+
const enc = new TextEncoder();
|
|
195
|
+
const bytes = enc.encode(input);
|
|
196
|
+
let binary = "";
|
|
197
|
+
const chunk = 0x8000;
|
|
198
|
+
for (let i = 0; i < bytes.length; i += chunk) {
|
|
199
|
+
binary += String.fromCharCode(...bytes.subarray(i, i + chunk));
|
|
200
|
+
}
|
|
201
|
+
return { base64: btoa(binary), size: bytes.length };
|
|
202
|
+
} else {
|
|
203
|
+
const s = String(input);
|
|
204
|
+
const enc = new TextEncoder();
|
|
205
|
+
const bytes = enc.encode(s);
|
|
206
|
+
let binary = "";
|
|
207
|
+
const chunk = 0x8000;
|
|
208
|
+
for (let i = 0; i < bytes.length; i += chunk) {
|
|
209
|
+
binary += String.fromCharCode(...bytes.subarray(i, i + chunk));
|
|
210
|
+
}
|
|
211
|
+
return { base64: btoa(binary), size: bytes.length };
|
|
212
|
+
}
|
|
213
|
+
};
|
|
214
|
+
|
|
215
|
+
const { base64, size } = toBase64(f.content);
|
|
216
|
+
return {
|
|
217
|
+
fileName: f.fileName || "file",
|
|
218
|
+
mimeType: f.mimeType || "application/octet-stream",
|
|
219
|
+
size,
|
|
220
|
+
contentBase64: base64,
|
|
221
|
+
};
|
|
222
|
+
});
|
|
223
|
+
return { output: result.output, details: { files } };
|
|
224
|
+
},
|
|
225
|
+
};
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
// Export a default instance for backward compatibility
|
|
229
|
+
export const javascriptReplTool = createJavaScriptReplTool();
|
|
230
|
+
|
|
231
|
+
// JavaScript REPL renderer with streaming support
|
|
232
|
+
|
|
233
|
+
interface JavaScriptReplParams {
|
|
234
|
+
code: string;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
interface JavaScriptReplResult {
|
|
238
|
+
output?: string;
|
|
239
|
+
files?: Array<{
|
|
240
|
+
fileName: string;
|
|
241
|
+
mimeType: string;
|
|
242
|
+
size: number;
|
|
243
|
+
contentBase64: string;
|
|
244
|
+
}>;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
export const javascriptReplRenderer: ToolRenderer<JavaScriptReplParams, JavaScriptReplResult> = {
|
|
248
|
+
renderParams(params: JavaScriptReplParams, isStreaming?: boolean): TemplateResult {
|
|
249
|
+
if (isStreaming && (!params.code || params.code.length === 0)) {
|
|
250
|
+
return html`<div class="text-sm text-muted-foreground">Writing JavaScript code...</div>`;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
return html`
|
|
254
|
+
<div class="text-sm text-muted-foreground mb-2">${i18n("Executing JavaScript")}</div>
|
|
255
|
+
<code-block .code=${params.code || ""} language="javascript"></code-block>
|
|
256
|
+
`;
|
|
257
|
+
},
|
|
258
|
+
|
|
259
|
+
renderResult(_params: JavaScriptReplParams, result: ToolResultMessage<JavaScriptReplResult>): TemplateResult {
|
|
260
|
+
// Console output is in the main output field, files are in details
|
|
261
|
+
const output = result.output || "";
|
|
262
|
+
const files = result.details?.files || [];
|
|
263
|
+
|
|
264
|
+
const attachments: Attachment[] = files.map((f, i) => {
|
|
265
|
+
// Decode base64 content for text files to show in overlay
|
|
266
|
+
let extractedText: string | undefined;
|
|
267
|
+
const isTextBased =
|
|
268
|
+
f.mimeType?.startsWith("text/") ||
|
|
269
|
+
f.mimeType === "application/json" ||
|
|
270
|
+
f.mimeType === "application/javascript" ||
|
|
271
|
+
f.mimeType?.includes("xml");
|
|
272
|
+
|
|
273
|
+
if (isTextBased && f.contentBase64) {
|
|
274
|
+
try {
|
|
275
|
+
extractedText = atob(f.contentBase64);
|
|
276
|
+
} catch (e) {
|
|
277
|
+
console.warn("Failed to decode base64 content for", f.fileName);
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
return {
|
|
282
|
+
id: `repl-${Date.now()}-${i}`,
|
|
283
|
+
type: f.mimeType?.startsWith("image/") ? "image" : "document",
|
|
284
|
+
fileName: f.fileName || `file-${i}`,
|
|
285
|
+
mimeType: f.mimeType || "application/octet-stream",
|
|
286
|
+
size: f.size ?? 0,
|
|
287
|
+
content: f.contentBase64,
|
|
288
|
+
preview: f.mimeType?.startsWith("image/") ? f.contentBase64 : undefined,
|
|
289
|
+
extractedText,
|
|
290
|
+
};
|
|
291
|
+
});
|
|
292
|
+
|
|
293
|
+
return html`
|
|
294
|
+
<div class="flex flex-col gap-3">
|
|
295
|
+
${output ? html`<console-block .content=${output}></console-block>` : ""}
|
|
296
|
+
${
|
|
297
|
+
attachments.length
|
|
298
|
+
? html`<div class="flex flex-wrap gap-2">
|
|
299
|
+
${attachments.map((att) => html`<attachment-tile .attachment=${att}></attachment-tile>`)}
|
|
300
|
+
</div>`
|
|
301
|
+
: ""
|
|
302
|
+
}
|
|
303
|
+
</div>
|
|
304
|
+
`;
|
|
305
|
+
},
|
|
306
|
+
};
|
|
307
|
+
|
|
308
|
+
// Auto-register the renderer
|
|
309
|
+
registerToolRenderer(javascriptReplTool.name, javascriptReplRenderer);
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { ToolRenderer } from "./types.js";
|
|
2
|
+
|
|
3
|
+
// Registry of tool renderers
|
|
4
|
+
export const toolRenderers = new Map<string, ToolRenderer>();
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Register a custom tool renderer
|
|
8
|
+
*/
|
|
9
|
+
export function registerToolRenderer(toolName: string, renderer: ToolRenderer): void {
|
|
10
|
+
toolRenderers.set(toolName, renderer);
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Get a tool renderer by name
|
|
15
|
+
*/
|
|
16
|
+
export function getToolRenderer(toolName: string): ToolRenderer | undefined {
|
|
17
|
+
return toolRenderers.get(toolName);
|
|
18
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { html, type TemplateResult } from "@mariozechner/mini-lit";
|
|
2
|
+
import type { ToolResultMessage } from "@mariozechner/pi-ai";
|
|
3
|
+
import { i18n } from "../../utils/i18n.js";
|
|
4
|
+
import type { ToolRenderer } from "../types.js";
|
|
5
|
+
|
|
6
|
+
interface BashParams {
|
|
7
|
+
command: string;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
// Bash tool has undefined details (only uses output)
|
|
11
|
+
export class BashRenderer implements ToolRenderer<BashParams, undefined> {
|
|
12
|
+
renderParams(params: BashParams, isStreaming?: boolean): TemplateResult {
|
|
13
|
+
if (isStreaming && (!params.command || params.command.length === 0)) {
|
|
14
|
+
return html`<div class="text-sm text-muted-foreground">${i18n("Writing command...")}</div>`;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
return html`
|
|
18
|
+
<div class="text-sm text-muted-foreground">
|
|
19
|
+
<span>${i18n("Running command:")}</span>
|
|
20
|
+
<code class="ml-1 px-1.5 py-0.5 bg-muted rounded text-xs font-mono">${params.command}</code>
|
|
21
|
+
</div>
|
|
22
|
+
`;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
renderResult(_params: BashParams, result: ToolResultMessage<undefined>): TemplateResult {
|
|
26
|
+
const output = result.output || "";
|
|
27
|
+
const isError = result.isError === true;
|
|
28
|
+
|
|
29
|
+
if (isError) {
|
|
30
|
+
return html`
|
|
31
|
+
<div class="text-sm">
|
|
32
|
+
<div class="text-destructive font-medium mb-1">${i18n("Command failed:")}</div>
|
|
33
|
+
<pre class="text-xs font-mono text-destructive bg-destructive/10 p-2 rounded overflow-x-auto">${output}</pre>
|
|
34
|
+
</div>
|
|
35
|
+
`;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// Display the command output
|
|
39
|
+
return html`
|
|
40
|
+
<div class="text-sm">
|
|
41
|
+
<pre class="text-xs font-mono text-foreground bg-muted/50 p-2 rounded overflow-x-auto">${output}</pre>
|
|
42
|
+
</div>
|
|
43
|
+
`;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { html, type TemplateResult } from "@mariozechner/mini-lit";
|
|
2
|
+
import type { ToolResultMessage } from "@mariozechner/pi-ai";
|
|
3
|
+
import { i18n } from "../../utils/i18n.js";
|
|
4
|
+
import type { ToolRenderer } from "../types.js";
|
|
5
|
+
|
|
6
|
+
interface CalculateParams {
|
|
7
|
+
expression: string;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
// Calculate tool has undefined details (only uses output)
|
|
11
|
+
export class CalculateRenderer implements ToolRenderer<CalculateParams, undefined> {
|
|
12
|
+
renderParams(params: CalculateParams, isStreaming?: boolean): TemplateResult {
|
|
13
|
+
if (isStreaming && !params.expression) {
|
|
14
|
+
return html`<div class="text-sm text-muted-foreground">${i18n("Writing expression...")}</div>`;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
return html`
|
|
18
|
+
<div class="text-sm text-muted-foreground">
|
|
19
|
+
<span>${i18n("Calculating")}</span>
|
|
20
|
+
<code class="mx-1 px-1.5 py-0.5 bg-muted rounded text-xs font-mono">${params.expression}</code>
|
|
21
|
+
</div>
|
|
22
|
+
`;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
renderResult(_params: CalculateParams, result: ToolResultMessage<undefined>): TemplateResult {
|
|
26
|
+
// Parse the output to make it look nicer
|
|
27
|
+
const output = result.output || "";
|
|
28
|
+
const isError = result.isError === true;
|
|
29
|
+
|
|
30
|
+
if (isError) {
|
|
31
|
+
return html`<div class="text-sm text-destructive">${output}</div>`;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// Try to split on = to show expression and result separately
|
|
35
|
+
const parts = output.split(" = ");
|
|
36
|
+
if (parts.length === 2) {
|
|
37
|
+
return html`
|
|
38
|
+
<div class="text-sm font-mono">
|
|
39
|
+
<span class="text-muted-foreground">${parts[0]}</span>
|
|
40
|
+
<span class="text-muted-foreground mx-1">=</span>
|
|
41
|
+
<span class="text-foreground font-semibold">${parts[1]}</span>
|
|
42
|
+
</div>
|
|
43
|
+
`;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// Fallback to showing the whole output
|
|
47
|
+
return html`<div class="text-sm font-mono text-foreground">${output}</div>`;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { html, type TemplateResult } from "@mariozechner/mini-lit";
|
|
2
|
+
import type { ToolResultMessage } from "@mariozechner/pi-ai";
|
|
3
|
+
import { i18n } from "../../utils/i18n.js";
|
|
4
|
+
import type { ToolRenderer } from "../types.js";
|
|
5
|
+
|
|
6
|
+
export class DefaultRenderer implements ToolRenderer {
|
|
7
|
+
renderParams(params: any, isStreaming?: boolean): TemplateResult {
|
|
8
|
+
let text: string;
|
|
9
|
+
let isJson = false;
|
|
10
|
+
|
|
11
|
+
try {
|
|
12
|
+
text = JSON.stringify(JSON.parse(params), null, 2);
|
|
13
|
+
isJson = true;
|
|
14
|
+
} catch {
|
|
15
|
+
try {
|
|
16
|
+
text = JSON.stringify(params, null, 2);
|
|
17
|
+
isJson = true;
|
|
18
|
+
} catch {
|
|
19
|
+
text = String(params);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
if (isStreaming && (!text || text === "{}" || text === "null")) {
|
|
24
|
+
return html`<div class="text-sm text-muted-foreground">${i18n("Preparing tool parameters...")}</div>`;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
return html`<console-block .content=${text}></console-block>`;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
renderResult(_params: any, result: ToolResultMessage): TemplateResult {
|
|
31
|
+
// Just show the output field - that's what was sent to the LLM
|
|
32
|
+
const text = result.output || i18n("(no output)");
|
|
33
|
+
|
|
34
|
+
return html`<div class="text-sm text-muted-foreground whitespace-pre-wrap font-mono">${text}</div>`;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { html, type TemplateResult } from "@mariozechner/mini-lit";
|
|
2
|
+
import type { ToolResultMessage } from "@mariozechner/pi-ai";
|
|
3
|
+
import { i18n } from "../../utils/i18n.js";
|
|
4
|
+
import type { ToolRenderer } from "../types.js";
|
|
5
|
+
|
|
6
|
+
interface GetCurrentTimeParams {
|
|
7
|
+
timezone?: string;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
// GetCurrentTime tool has undefined details (only uses output)
|
|
11
|
+
export class GetCurrentTimeRenderer implements ToolRenderer<GetCurrentTimeParams, undefined> {
|
|
12
|
+
renderParams(params: GetCurrentTimeParams, isStreaming?: boolean): TemplateResult {
|
|
13
|
+
if (params.timezone) {
|
|
14
|
+
return html`
|
|
15
|
+
<div class="text-sm text-muted-foreground">
|
|
16
|
+
<span>${i18n("Getting current time in")}</span>
|
|
17
|
+
<code class="mx-1 px-1.5 py-0.5 bg-muted rounded text-xs font-mono">${params.timezone}</code>
|
|
18
|
+
</div>
|
|
19
|
+
`;
|
|
20
|
+
}
|
|
21
|
+
return html`
|
|
22
|
+
<div class="text-sm text-muted-foreground">
|
|
23
|
+
<span>${i18n("Getting current date and time")}${isStreaming ? "..." : ""}</span>
|
|
24
|
+
</div>
|
|
25
|
+
`;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
renderResult(_params: GetCurrentTimeParams, result: ToolResultMessage<undefined>): TemplateResult {
|
|
29
|
+
const output = result.output || "";
|
|
30
|
+
const isError = result.isError === true;
|
|
31
|
+
|
|
32
|
+
if (isError) {
|
|
33
|
+
return html`<div class="text-sm text-destructive">${output}</div>`;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// Display the date/time result
|
|
37
|
+
return html`<div class="text-sm font-mono text-foreground">${output}</div>`;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { ToolResultMessage } from "@mariozechner/pi-ai";
|
|
2
|
+
import type { TemplateResult } from "lit";
|
|
3
|
+
|
|
4
|
+
export interface ToolRenderer<TParams = any, TDetails = any> {
|
|
5
|
+
renderParams(params: TParams, isStreaming?: boolean): TemplateResult;
|
|
6
|
+
renderResult(params: TParams, result: ToolResultMessage<TDetails>): TemplateResult;
|
|
7
|
+
}
|