import * as plugins from './plugins.js'; import * as interfaces from './smartagent.interfaces.js'; import { BaseToolWrapper } from './smartagent.tools.base.js'; /** * Deno permission types for sandboxed code execution */ export type TDenoPermission = | 'all' | 'env' | 'ffi' | 'hrtime' | 'net' | 'read' | 'run' | 'sys' | 'write'; /** * Deno tool for executing TypeScript/JavaScript code in a sandboxed environment * Wraps @push.rocks/smartdeno */ export class DenoTool extends BaseToolWrapper { public name = 'deno'; public description = 'Execute TypeScript/JavaScript code in a sandboxed Deno environment with fine-grained permission control'; public actions: interfaces.IToolAction[] = [ { name: 'execute', description: 'Execute TypeScript/JavaScript code and return stdout/stderr. Code runs in Deno sandbox with specified permissions.', parameters: { type: 'object', properties: { code: { type: 'string', description: 'TypeScript/JavaScript code to execute', }, permissions: { type: 'array', items: { type: 'string', enum: ['all', 'env', 'ffi', 'hrtime', 'net', 'read', 'run', 'sys', 'write'], }, description: 'Deno permissions to grant. Default: none (fully sandboxed). Options: all, env, net, read, write, run, sys, ffi, hrtime', }, }, required: ['code'], }, }, { name: 'executeWithResult', description: 'Execute code that outputs JSON on the last line of stdout. The JSON is parsed and returned as the result.', parameters: { type: 'object', properties: { code: { type: 'string', description: 'Code that console.logs a JSON value on the final line. This JSON will be parsed and returned.', }, permissions: { type: 'array', items: { type: 'string', enum: ['all', 'env', 'ffi', 'hrtime', 'net', 'read', 'run', 'sys', 'write'], }, description: 'Deno permissions to grant', }, }, required: ['code'], }, }, ]; private smartdeno!: plugins.smartdeno.SmartDeno; public async initialize(): Promise { this.smartdeno = new plugins.smartdeno.SmartDeno(); await this.smartdeno.start(); this.isInitialized = true; } public async cleanup(): Promise { if (this.smartdeno) { await this.smartdeno.stop(); } this.isInitialized = false; } public async execute( action: string, params: Record ): Promise { this.validateAction(action); this.ensureInitialized(); try { const code = params.code as string; const permissions = (params.permissions as TDenoPermission[]) || []; // Execute the script const result = await this.smartdeno.executeScript(code, { permissions, }); switch (action) { case 'execute': { return { success: result.exitCode === 0, result: { exitCode: result.exitCode, stdout: result.stdout, stderr: result.stderr, permissions, }, }; } case 'executeWithResult': { if (result.exitCode !== 0) { return { success: false, error: `Script failed with exit code ${result.exitCode}: ${result.stderr}`, }; } // Parse the last line of stdout as JSON const lines = result.stdout.trim().split('\n'); const lastLine = lines[lines.length - 1]; try { const parsedResult = JSON.parse(lastLine); return { success: true, result: { parsed: parsedResult, stdout: result.stdout, stderr: result.stderr, }, }; } catch (parseError) { return { success: false, error: `Failed to parse JSON from last line of output: ${lastLine}`, }; } } default: return { success: false, error: `Unknown action: ${action}`, }; } } catch (error) { return { success: false, error: error instanceof Error ? error.message : String(error), }; } } public getCallSummary(action: string, params: Record): string { const code = params.code as string; const permissions = (params.permissions as string[]) || []; // Create a preview of the code (first 100 chars) const codePreview = code.length > 100 ? code.substring(0, 100) + '...' : code; // Escape newlines for single-line display const cleanPreview = codePreview.replace(/\n/g, '\\n'); const permissionInfo = permissions.length > 0 ? ` [permissions: ${permissions.join(', ')}]` : ' [sandboxed - no permissions]'; switch (action) { case 'execute': return `Execute Deno code${permissionInfo}: "${cleanPreview}"`; case 'executeWithResult': return `Execute Deno code and parse JSON result${permissionInfo}: "${cleanPreview}"`; default: return `Unknown action: ${action}`; } } }