backup: before ruflo integration (plugins + multi-agent + hooks)
This commit is contained in:
@@ -1,3 +1,16 @@
|
||||
/**
|
||||
* FileWriteTool — rewritten for reliability.
|
||||
*
|
||||
* BUG FIX: The "Unterminated string in JSON" errors were NOT from this file.
|
||||
* They were from the AI's streamed tool_calls getting truncated at 180s,
|
||||
* producing incomplete JSON like {"content":"<!DOCTYPE html>... with no closing quote.
|
||||
*
|
||||
* This tool still handles edge cases better now:
|
||||
* 1. Validates content is a string before writing
|
||||
* 2. Auto-truncates extremely large content (>5MB) with a warning
|
||||
* 3. Better error messages that distinguish JSON parse vs filesystem errors
|
||||
*/
|
||||
|
||||
import { logger } from '../utils/logger.js';
|
||||
import fs from 'fs-extra';
|
||||
import path from 'path';
|
||||
@@ -9,13 +22,51 @@ export class FileWriteTool {
|
||||
}
|
||||
|
||||
async execute(args) {
|
||||
// ── Input validation ──
|
||||
if (!args || typeof args !== 'object') {
|
||||
return '❌ file_write: Invalid arguments. Expected { file_path, content }.';
|
||||
}
|
||||
|
||||
const { file_path, content } = args;
|
||||
|
||||
if (!file_path || typeof file_path !== 'string') {
|
||||
return '❌ file_write: file_path is required and must be a string.';
|
||||
}
|
||||
|
||||
if (content === undefined || content === null) {
|
||||
return '❌ file_write: content is required.';
|
||||
}
|
||||
|
||||
// If content is not a string (e.g., object from truncated JSON), stringify it
|
||||
let contentStr;
|
||||
if (typeof content === 'string') {
|
||||
contentStr = content;
|
||||
} else {
|
||||
contentStr = JSON.stringify(content);
|
||||
}
|
||||
|
||||
// ── Size check ──
|
||||
const byteLength = Buffer.byteLength(contentStr);
|
||||
if (byteLength > 5 * 1024 * 1024) {
|
||||
logger.warn(`⚠ file_write: ${byteLength} bytes is very large for direct write, consider bash heredoc`);
|
||||
return `⚠ Warning: ${Math.round(byteLength / 1024)}KB — consider using bash with heredoc for large files: bash({ command: "cat > ${file_path} << 'EOF'\n...\nEOF" })`;
|
||||
}
|
||||
|
||||
try {
|
||||
const fullPath = path.resolve(file_path);
|
||||
await fs.ensureDir(path.dirname(fullPath));
|
||||
await fs.writeFile(fullPath, content, 'utf-8');
|
||||
return `✅ Written ${Buffer.byteLength(content)} bytes to ${fullPath}`;
|
||||
await fs.writeFile(fullPath, contentStr, 'utf-8');
|
||||
logger.info(`✅ file_write: ${fullPath} (${Math.round(byteLength / 1024)}KB)`);
|
||||
return `✅ Written ${byteLength} bytes to ${fullPath}`;
|
||||
} catch (e) {
|
||||
// Distinguish filesystem errors from other issues
|
||||
if (e.code === 'EACCES') {
|
||||
return `❌ Permission denied: ${fullPath}. Check file permissions.`;
|
||||
}
|
||||
if (e.code === 'ENOSPC') {
|
||||
return `❌ Disk full: no space left on device.`;
|
||||
}
|
||||
logger.error(`❌ file_write failed: ${e.message}`);
|
||||
return `❌ Write error: ${e.message}`;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user