diff --git a/scripts/reasonix-acp-wrapper.mjs b/scripts/reasonix-acp-wrapper.mjs index ec4389c0..34aa6105 100644 --- a/scripts/reasonix-acp-wrapper.mjs +++ b/scripts/reasonix-acp-wrapper.mjs @@ -23,17 +23,12 @@ import { createInterface } from 'node:readline'; import { stdin, stdout } from 'node:process'; import { randomUUID } from 'node:crypto'; import { AsyncLocalStorage } from 'node:async_hooks'; -import { readFileSync, existsSync, writeFileSync } from 'node:fs'; +import { readFileSync, existsSync } from 'node:fs'; import { homedir } from 'node:os'; -import { join, resolve } from 'node:path'; +import { join } from 'node:path'; const MNOTE_WEB_URL = process.env.MNOTE_WEB_URL || 'http://127.0.0.1:3000'; const DEBUG = process.env.MNOTE_REASONIX_ACP_DEBUG === '1'; -const REASONIX_ALLOWED_FS_ROOTS = (process.env.REASONIX_ALLOWED_FS_ROOTS || '') - .split(':') - .map((value) => value.trim()) - .filter(Boolean) - .map((value) => resolve(value)); function debugLog(message) { if (DEBUG) process.stderr.write(`${message}\n`); @@ -53,64 +48,6 @@ function stableIdPart(value, fallback) { return text || String(fallback || 'unknown'); } -function isPathWithinRoot(targetPath, rootPath) { - const normalizedRoot = rootPath.endsWith('/') ? rootPath : `${rootPath}/`; - const normalizedTarget = targetPath.endsWith('/') ? targetPath : targetPath; - return normalizedTarget === rootPath || normalizedTarget.startsWith(normalizedRoot); -} - -function assertAllowedFsPath(targetPath) { - const resolved = resolve(targetPath); - if (!REASONIX_ALLOWED_FS_ROOTS.length) { - throw new Error( - 'reasonix local fs tools are disabled; set REASONIX_ALLOWED_FS_ROOTS to enable them', - ); - } - const matchedRoot = REASONIX_ALLOWED_FS_ROOTS.find((rootPath) => isPathWithinRoot(resolved, rootPath)); - if (!matchedRoot) { - throw new Error( - `path not allowed: ${resolved}. allowed roots: ${REASONIX_ALLOWED_FS_ROOTS.join(', ')}`, - ); - } - return resolved; -} - -function readAllowedFsFile(targetPath) { - const resolved = assertAllowedFsPath(targetPath); - return { - path: resolved, - content: readFileSync(resolved, 'utf-8'), - }; -} - -function writeAllowedFsFile(targetPath, content) { - const resolved = assertAllowedFsPath(targetPath); - writeFileSync(resolved, String(content), 'utf-8'); - return { - path: resolved, - written: true, - }; -} - -function replaceAllowedFsFile(targetPath, search, replace) { - const resolved = assertAllowedFsPath(targetPath); - const current = readFileSync(resolved, 'utf-8'); - const next = String(current).split(String(search)).join(String(replace)); - if (next === current) { - return { - path: resolved, - written: false, - replaced: false, - }; - } - writeFileSync(resolved, next, 'utf-8'); - return { - path: resolved, - written: true, - replaced: true, - }; -} - function buildMnoteToolPayload(toolName, rawArgs = {}, context = {}) { const { actorId, @@ -446,54 +383,6 @@ tools.register({ parallelSafe: false, }); -if (REASONIX_ALLOWED_FS_ROOTS.length) { - tools.register({ - name: 'reasonix_file_read', - description: '读取允许根目录中的本地 UTF-8 文件。', - parameters: { - type: 'object', - properties: { - path: { type: 'string', description: '要读取的绝对路径文件' }, - }, - required: ['path'], - }, - readOnly: true, - fn: async (args) => readAllowedFsFile(args.path), - parallelSafe: true, - }); - - tools.register({ - name: 'reasonix_file_write', - description: '写入允许根目录中的本地 UTF-8 文件。', - parameters: { - type: 'object', - properties: { - path: { type: 'string', description: '要写入的绝对路径文件' }, - content: { type: 'string', description: '完整文件内容' }, - }, - required: ['path', 'content'], - }, - fn: async (args) => writeAllowedFsFile(args.path, args.content), - parallelSafe: false, - }); - - tools.register({ - name: 'reasonix_file_replace', - description: '对允许根目录中的本地 UTF-8 文件做字符串替换。', - parameters: { - type: 'object', - properties: { - path: { type: 'string', description: '要修改的绝对路径文件' }, - search: { type: 'string', description: '要查找的旧文本' }, - replace: { type: 'string', description: '要替换成的新文本' }, - }, - required: ['path', 'search', 'replace'], - }, - fn: async (args) => replaceAllowedFsFile(args.path, args.search, args.replace), - parallelSafe: false, - }); -} - // ── Session Store ──────────────────────────────────── const sessions = new Map(); @@ -525,9 +414,6 @@ onRequest('session/new', async (params) => { 'Use mnote_doc_fetch to read the current document.', 'Use mnote_doc_markdown_edit to apply precise search/replace edits.', 'Always use mnote_doc_fetch first to understand the document content before editing.', - REASONIX_ALLOWED_FS_ROOTS.length - ? `Local filesystem tools are enabled for these roots: ${REASONIX_ALLOWED_FS_ROOTS.join(', ')}. Use reasonix_file_read, reasonix_file_replace, or reasonix_file_write for files inside those roots.` - : 'Local filesystem tools are disabled.', ].join('\n'); const loop = new CacheFirstLoop({