js tool path err fix

This commit is contained in:
aaguirre-rdit 2026-10-08 15:34:28 +01:00
parent 3909546fef
commit 837a816def
No known key found for this signature in database
2 changed files with 32 additions and 15 deletions

View File

@ -51,24 +51,19 @@ function makeFakeSandbox(result: Partial<CommandResult> = {}): WorkspaceSandbox
}
describe('runJavascriptInputSchema', () => {
it('accepts exactly one of path or code', () => {
it('accepts path, code, or both', () => {
expect(runJavascriptInputSchema.safeParse({ path: 'plot.js' }).success).toBe(true);
expect(runJavascriptInputSchema.safeParse({ code: 'console.log(1)' }).success).toBe(true);
});
it('rejects both path and code', () => {
const parsed = runJavascriptInputSchema.safeParse({ path: 'plot.js', code: 'console.log(1)' });
expect(parsed.success).toBe(false);
if (!parsed.success) {
expect(parsed.error.message).toContain('Provide exactly one of path or code');
}
expect(
runJavascriptInputSchema.safeParse({ path: 'tmp/unused', code: 'console.log(1)' }).success,
).toBe(true);
});
it('rejects neither path nor code', () => {
const parsed = runJavascriptInputSchema.safeParse({});
expect(parsed.success).toBe(false);
if (!parsed.success) {
expect(parsed.error.message).toContain('Provide exactly one of path or code');
expect(parsed.error.message).toContain('Provide path or code');
}
});
});
@ -104,6 +99,22 @@ describe('createRunJavascriptTool', () => {
});
});
it('uses code when both path and code are set', async () => {
const filesystem = makeFakeFilesystem();
const sandbox = makeFakeSandbox();
const tool = createRunJavascriptTool(sandbox, filesystem);
await tool.handler!({ path: 'tmp/unused', code: 'console.log(1)' }, {} as never);
expect(filesystem.writeFile).toHaveBeenCalledTimes(1);
expect(vi.mocked(filesystem.writeFile).mock.calls[0]?.[1]).toBe('console.log(1)');
const scriptPath = vi.mocked(filesystem.writeFile).mock.calls[0]?.[0];
expect(sandbox.executeCommand).toHaveBeenCalledWith('node', [scriptPath], {
timeout: 60_000,
abortSignal: undefined,
});
});
it('runs an existing path with node and does not write a snippet', async () => {
const filesystem = makeFakeFilesystem();
const sandbox = makeFakeSandbox();

View File

@ -18,10 +18,16 @@ export const runJavascriptInputSchema = z
.describe(
'Path of the JavaScript file, relative to the workspace root (absolute paths must be under it)',
),
code: z.string().min(1).optional().describe('JavaScript source to run as a Scratch File'),
code: z
.string()
.min(1)
.optional()
.describe(
'JavaScript source to run as a Scratch File. If both path and code are set, code is used.',
),
})
.refine((value) => Boolean(value.path) !== Boolean(value.code), {
message: 'Provide exactly one of path or code',
.refine((value) => Boolean(value.path) || Boolean(value.code), {
message: 'Provide path or code',
});
const runJavascriptOutputSchema = z.object({
@ -41,7 +47,7 @@ export function createRunJavascriptTool(
): BuiltTool {
return new Tool('workspace_run_javascript')
.description(
'Run a JavaScript file in the sandbox with node. Provide exactly one of path or code. Read Attachments from `$N8N_UPLOADS_DIR` or `$N8N_UPLOADS_MANIFEST`. Write files the user should retrieve only under `$N8N_OUTPUTS_DIR`. This tool has a 60 second limit. Use `workspace_execute_command` for longer jobs.',
'Run a JavaScript file in the sandbox with node. Provide path or code. If both are set, code is used. Read Attachments from `$N8N_UPLOADS_DIR` or `$N8N_UPLOADS_MANIFEST`. Write files the user should retrieve only under `$N8N_OUTPUTS_DIR`. This tool has a 60 second limit. Use `workspace_execute_command` for longer jobs.',
)
.input(runJavascriptInputSchema)
.output(runJavascriptOutputSchema)
@ -64,7 +70,7 @@ export function createRunJavascriptTool(
} else if (input.path) {
scriptPath = input.path;
} else {
throw new Error('Provide exactly one of path or code');
throw new Error('Provide path or code');
}
const result = await sandbox.executeCommand('node', [scriptPath], {