diff --git a/cli.js b/cli.js index 57ede72..cbae79f 100644 --- a/cli.js +++ b/cli.js @@ -48,8 +48,7 @@ async function loadTools() { return Object.fromEntries(toolEntries); } -streamOnce(new OpenAI({ apiKey: 'csk-8jftdte6r6vf8fdvp9xkyek5t3jnc6jfhh93d3ewfcwxxvh9', baseURL: "https://api.cerebras.ai/v1"}), 'Erstelle eine React Project für eine Abovverwaltung. Mui, Sqllite, Express, Nodejs, KEIN Typescript, Aber ESM import. webpack, kein vite. HRM, nodemon'); -//streamOnce(new OpenAI({ apiKey: process.env.OPENAI_API_KEY }), 'Erstelle eine React Project für eine Abovverwaltung. Mui, Sqllite, Express, Nodejs, KEIN Typescript, Aber ESM import. webpack, kein vite. HRM, nodemon'); +streamOnce(new OpenAI({ apiKey: process.env.OPENAI_API_KEY }), 'Erstelle eine React Project für eine Abovverwaltung. Mui, Sqllite, Express, Nodejs, KEIN Typescript, Aber ESM import. webpack, kein vite. HRM, nodemon'); let counter = 0; @@ -65,7 +64,7 @@ async function streamOnce(openai, userText) { while(input.length > 0){ const call = { - model: 'gpt-oss-120b', + model: 'gpt-5-mini', input: input, text: { format: { type: 'text' }, verbosity: 'high' }, reasoning: { effort: 'medium', summary: 'detailed' }, @@ -83,7 +82,7 @@ async function streamOnce(openai, userText) { previousResponseId = event.response.id; }); stream.on('response.reasoning_summary_text.delta', (event) => { - ////process.stdout.write(event.delta); + process.stdout.write('o') }); stream.on('response.reasoning_summary_text.done', () => { process.stdout.write('\n'); @@ -91,7 +90,7 @@ async function streamOnce(openai, userText) { }); stream.on('response.output_text.delta', (event) => { - ////process.stdout.write(event.delta); + process.stdout.write('.') }); @@ -101,7 +100,7 @@ async function streamOnce(openai, userText) { } }); stream.on('response.function_call_arguments.delta', (event) => { - ////process.stdout.write(event.delta); + process.stdout.write('x'); }); const functionCalls = []; diff --git a/tools/read_file.js b/tools/read_file.js index 6f00425..5fbb7f1 100644 --- a/tools/read_file.js +++ b/tools/read_file.js @@ -1,14 +1,58 @@ +import { createReadStream } from "node:fs"; +import { createInterface } from "node:readline"; + +import path from "node:path"; + const virtual_chroot = '/home/seb/src/aiTools/tmp'; +// Ensures reads are confined to `virtual_chroot`. + export default { type: "function", name: "read_file", description: "read a file", strict: true, parameters: { - type: "object", required: ["path","linesToSkip","linesToRead"],additionalProperties: false, properties: { - path: { type: "string", description: "The path to the file to read.",}, - linesToSkip: { type: "number", description: "The number of lines to skip. Use 0 to read from the beginning.",}, - linesToRead: { type: "number",description: "1-400 The number of lines to read. Use 0 or more than 400 to read 400 lines.",} -},},}; + type: "object", required: ["path","linesToSkip","linesToRead"], additionalProperties: false, properties: { + path: { type: "string", description: "The path to the file to read.", }, + linesToSkip: { type: "integer", description: "The number of lines to skip. Use 0 to read from the beginning.", minimum: 0 }, + linesToRead: { type: "integer", description: "1-400 The number of lines to read. Use 0 or more than 400 to read 400 lines.", minimum: 0 } + } + } +}; -export async function run() { - return `read_file error (not implemented)` -} +export async function run(args) { + const { path: filePath, linesToSkip, linesToRead } = args; + + // Validate path is within virtual_chroot + const fullPath = path.resolve(virtual_chroot, filePath.replace(/^\//, '')); + if (!fullPath.startsWith(virtual_chroot)) { + return `read_file error: Path outside of allowed directory`; + } + + // Normalize linesToRead (1-400, with 0 or >400 meaning 400) + const maxLines = (linesToRead <= 0 || linesToRead > 400) ? 400 : linesToRead; + + try { + + const rl = createInterface({ + input: createReadStream(fullPath), + crlfDelay: Infinity + }); + + let lines = []; + let skipped = 0; + + for await (const line of rl) { + if (skipped < linesToSkip) { + skipped++; + continue; + } + lines.push(line); + if (lines.length >= maxLines) { + break; + } + } + + return lines.join('\n'); + } catch (error) { + return `read_file error: ${error.message}`; + } +} \ No newline at end of file diff --git a/tools/ripgrep.js b/tools/ripgrep.js index 62e2869..cfa107e 100644 --- a/tools/ripgrep.js +++ b/tools/ripgrep.js @@ -9,8 +9,53 @@ export default { filePattern: { type: "string", description: "'*.js' for only js files, '!*.log' for all files except log files , '' for all files"}, n_flag: { type: "boolean",description: "show line numbers."}, i_flag: { type: "boolean",description: "case insensitive search."} -},},}; + } + } +}; export async function run(args) { - return `ripgrep error (not implemented)` + const { pattern, filePattern, n_flag, i_flag } = args; + + // Build the args array + let rgArgs = []; + + if (n_flag) rgArgs.push('-n'); + if (i_flag) rgArgs.push('-i'); + if (filePattern) { + rgArgs.push('-g', filePattern); + } + + // Add separator and pattern + rgArgs.push('--', pattern); + + try { + const { spawnSync } = require('child_process'); + const proc = spawnSync('rg', rgArgs, { + cwd: virtual_chroot, + encoding: 'utf8', + maxBuffer: 1024 * 1024 * 10 // 10MB buffer + }); + + if (proc.error) { + return `ripgrep error: ${proc.error.message}`; + } + + let output = proc.stdout; + + if (proc.status !== 0) { + if (proc.status === 1) { + // No matches found + return output || ''; + } + return `ripgrep error: exit ${proc.status}, ${proc.stderr}`; + } + + // Limit to 200 lines + const lines = output.split('\n'); + const limitedOutput = lines.slice(0, 200).join('\n'); + + return limitedOutput; + } catch (error) { + return `ripgrep error: ${error.message}`; + } } \ No newline at end of file