feat(deno): Add Deno tool and smartdeno integration; export and register DenoTool; update docs and tests
This commit is contained in:
@@ -3,6 +3,6 @@
|
||||
*/
|
||||
export const commitinfo = {
|
||||
name: '@push.rocks/smartagent',
|
||||
version: '1.0.2',
|
||||
version: '1.1.0',
|
||||
description: 'an agentic framework built on top of @push.rocks/smartai'
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@ export { FilesystemTool } from './smartagent.tools.filesystem.js';
|
||||
export { HttpTool } from './smartagent.tools.http.js';
|
||||
export { ShellTool } from './smartagent.tools.shell.js';
|
||||
export { BrowserTool } from './smartagent.tools.browser.js';
|
||||
export { DenoTool, type TDenoPermission } from './smartagent.tools.deno.js';
|
||||
|
||||
// Export all interfaces
|
||||
export * from './smartagent.interfaces.js';
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
// @push.rocks scope
|
||||
import * as smartai from '@push.rocks/smartai';
|
||||
import * as smartdeno from '@push.rocks/smartdeno';
|
||||
import * as smartfs from '@push.rocks/smartfs';
|
||||
import * as smartrequest from '@push.rocks/smartrequest';
|
||||
import * as smartbrowser from '@push.rocks/smartbrowser';
|
||||
@@ -7,6 +8,7 @@ import * as smartshell from '@push.rocks/smartshell';
|
||||
|
||||
export {
|
||||
smartai,
|
||||
smartdeno,
|
||||
smartfs,
|
||||
smartrequest,
|
||||
smartbrowser,
|
||||
|
||||
@@ -7,6 +7,7 @@ import { FilesystemTool } from './smartagent.tools.filesystem.js';
|
||||
import { HttpTool } from './smartagent.tools.http.js';
|
||||
import { ShellTool } from './smartagent.tools.shell.js';
|
||||
import { BrowserTool } from './smartagent.tools.browser.js';
|
||||
import { DenoTool } from './smartagent.tools.deno.js';
|
||||
|
||||
/**
|
||||
* DualAgentOrchestrator - Coordinates Driver and Guardian agents
|
||||
@@ -87,6 +88,7 @@ export class DualAgentOrchestrator {
|
||||
new HttpTool(),
|
||||
new ShellTool(),
|
||||
new BrowserTool(),
|
||||
new DenoTool(),
|
||||
];
|
||||
|
||||
for (const tool of standardTools) {
|
||||
|
||||
191
ts/smartagent.tools.deno.ts
Normal file
191
ts/smartagent.tools.deno.ts
Normal file
@@ -0,0 +1,191 @@
|
||||
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<void> {
|
||||
this.smartdeno = new plugins.smartdeno.SmartDeno();
|
||||
await this.smartdeno.start();
|
||||
this.isInitialized = true;
|
||||
}
|
||||
|
||||
public async cleanup(): Promise<void> {
|
||||
if (this.smartdeno) {
|
||||
await this.smartdeno.stop();
|
||||
}
|
||||
this.isInitialized = false;
|
||||
}
|
||||
|
||||
public async execute(
|
||||
action: string,
|
||||
params: Record<string, unknown>
|
||||
): Promise<interfaces.IToolExecutionResult> {
|
||||
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, unknown>): 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}`;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user