1274943f33
Verify write_file/edit_file land public HTML via call_tool, and Portal can start/resume v1.49 sessions without 404 on the resume path. Co-authored-by: Cursor <cursoragent@cursor.com>
111 lines
3.5 KiB
JavaScript
111 lines
3.5 KiB
JavaScript
#!/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 = `<!DOCTYPE html>
|
|
<html lang="zh-CN">
|
|
<head><meta charset="UTF-8"><title>Goose v1.49 Sandbox</title></head>
|
|
<body><h1>苏州一日游</h1><p>初始段落</p></body>
|
|
</html>`;
|
|
|
|
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);
|
|
});
|