#!/usr/bin/env node /** * Verify sandbox-fs write_file + edit_file materialize HTML via /agent/call_tool. */ import fs from 'node:fs'; import os from 'node:os'; import path from 'node:path'; import { fileURLToPath } from 'node:url'; import { createV149Client } from './goose-v149-sse.mjs'; const memindRoot = path.join(path.dirname(fileURLToPath(import.meta.url)), '..'); const nodeExec = process.env.GOOSED_MCP_NODE_PATH || process.execPath; const serverPath = process.env.GOOSED_MCP_SERVER_PATH || path.join(memindRoot, 'mindspace-sandbox-mcp.mjs'); const pollMs = Number(process.env.GOOSE_V149_SANDBOX_POLL_MS || 500); const timeoutMs = Number(process.env.GOOSE_V149_SANDBOX_TIMEOUT_MS || 45_000); const pageRel = 'public/goose-v149-sandbox-page.html'; const client = createV149Client(); const sandboxRoot = fs.mkdtempSync(path.join(os.tmpdir(), 'goose-v149-page-')); fs.mkdirSync(path.join(sandboxRoot, 'public'), { recursive: true }); async function waitForWriteTool(sessionId) { const deadline = Date.now() + timeoutMs; while (Date.now() < deadline) { const response = await client.apiFetch( `/agent/tools?session_id=${encodeURIComponent(sessionId)}&extension_name=sandbox-fs`, { method: 'GET' }, ); const tools = await response.json(); if (tools.some((tool) => tool.name === 'sandbox-fs__write_file')) return; await new Promise((resolve) => setTimeout(resolve, pollMs)); } throw new Error(`sandbox-fs write tool not ready within ${timeoutMs}ms`); } async function callTool(sessionId, name, arguments_) { return client.apiJson('/agent/call_tool', { session_id: sessionId, name, arguments: arguments_, }); } async function main() { if (!fs.existsSync(serverPath)) { throw new Error(`MCP entry missing: ${serverPath}`); } const session = await client.apiJson('/agent/start', { working_dir: memindRoot, extension_overrides: [ { type: 'stdio', name: 'sandbox-fs', description: 'MindSpace sandbox page smoke', cmd: nodeExec, args: [serverPath, sandboxRoot], envs: { ALLOWED_TOOLS: 'write_file,edit_file,read_file,create_dir,list_dir', SANDBOX_ROOT: sandboxRoot, }, available_tools: ['write_file', 'read_file', 'edit_file', 'create_dir', 'list_dir'], }, ], }); if (!session?.id) throw new Error('missing session id'); await waitForWriteTool(session.id); const initialHtml = `
初始段落
`; await callTool(session.id, 'sandbox-fs__write_file', { path: pageRel, content: initialHtml, }); const absPath = path.join(sandboxRoot, pageRel); if (!fs.existsSync(absPath)) { throw new Error(`write_file did not create ${pageRel}`); } await callTool(session.id, 'sandbox-fs__edit_file', { path: pageRel, old_str: '初始段落', new_str: '已编辑段落 · v1.49', }); const updated = fs.readFileSync(absPath, 'utf8'); if (!updated.includes('已编辑段落 · v1.49')) { throw new Error('edit_file did not apply patch'); } if (!updated.includes('苏州一日游')) { throw new Error('edit_file removed expected page content'); } console.log( `GOOSE_V149_SANDBOX_PAGE_OK: session=${session.id} file=${pageRel} sandbox=${sandboxRoot}`, ); } main().catch((error) => { console.error(`GOOSE_V149_SANDBOX_PAGE_FAIL: ${error.message}`); process.exit(1); });