Files
clawdbot/apps/macos/Sources/Clawdis/Resources/WebChat/components/sandbox/ArtifactsRuntimeProvider.js
2025-12-06 05:01:28 +01:00

189 lines
7.7 KiB
JavaScript

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