Fix sandbox MCP path resolution for session policy sync.

Resolve mindspace-sandbox-mcp.mjs from the code module location instead of h5Root, and copy it into the portal runtime artifact so goosed can spawn sandbox-fs on production.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
john
2026-06-26 13:36:05 +08:00
parent d51df2fb0a
commit 9ed4fd48d7
4 changed files with 761 additions and 57 deletions
+67 -1
View File
@@ -1,5 +1,6 @@
import test from 'node:test';
import assert from 'node:assert/strict';
import fs from 'node:fs';
import {
buildAgentExtensionPolicy,
buildPageEditAgentPolicy,
@@ -7,20 +8,30 @@ import {
clampUserCapabilities,
DEFAULT_USER_CAPABILITIES,
normalizeCapabilityPatch,
resolveSandboxMcpServerPath,
sandboxDeveloperTools,
sandboxMcpTools,
} from './capabilities.mjs';
import { applyPoliciesToCapabilities } from './policies.mjs';
test('resolveSandboxMcpServerPath resolves to an existing MCP entry file', () => {
const serverPath = resolveSandboxMcpServerPath();
assert.match(serverPath, /mindspace-sandbox-mcp\.mjs$/);
assert.ok(fs.existsSync(serverPath));
});
test('default user policy blocks dangerous capabilities', () => {
assert.equal(DEFAULT_USER_CAPABILITIES.shell, false);
assert.equal(DEFAULT_USER_CAPABILITIES.filesystem, false);
assert.equal(DEFAULT_USER_CAPABILITIES.extension_admin, false);
assert.equal(DEFAULT_USER_CAPABILITIES.static_publish, false);
assert.equal(DEFAULT_USER_CAPABILITIES.private_data_space, true);
assert.equal(DEFAULT_USER_CAPABILITIES.code_browse, false);
assert.equal(DEFAULT_USER_CAPABILITIES.memory_store, true);
assert.equal(DEFAULT_USER_CAPABILITIES.skills, true);
assert.equal(DEFAULT_USER_CAPABILITIES.chat_recall, true);
assert.equal(DEFAULT_USER_CAPABILITIES.aider, false);
assert.equal(DEFAULT_USER_CAPABILITIES.openhands, false);
});
test('buildAgentExtensionPolicy returns null overrides and auto mode for unrestricted users', () => {
@@ -73,6 +84,16 @@ test('buildAgentExtensionPolicy filters developer tools', () => {
);
});
test('buildAgentExtensionPolicy includes aider and openhands when granted', () => {
const policy = buildAgentExtensionPolicy({
...DEFAULT_USER_CAPABILITIES,
aider: true,
openhands: true,
});
assert.ok(policy.extensionOverrides.some((ext) => ext.name === 'aider'));
assert.ok(policy.extensionOverrides.some((ext) => ext.name === 'openhands'));
});
test('normalizeCapabilityPatch ignores unknown keys', () => {
assert.deepEqual(normalizeCapabilityPatch({ shell: true, unknown: true }), { shell: true });
});
@@ -139,7 +160,19 @@ test('buildPageEditAgentPolicy grants shell for static_publish sandbox users wit
test('sandboxMcpTools returns correct tool list based on capabilities', () => {
const base = { ...DEFAULT_USER_CAPABILITIES, static_publish: true };
assert.deepEqual(sandboxMcpTools(base), ['read_file', 'write_file', 'edit_file', 'create_dir']);
assert.deepEqual(sandboxMcpTools(base), [
'read_file',
'write_file',
'edit_file',
'create_dir',
'private_data_info',
'private_data_schema',
'private_data_query',
'private_data_execute',
'schedule_create_item',
'schedule_create_reminder',
'schedule_list_items',
]);
const withBrowse = { ...base, code_browse: true };
assert.ok(sandboxMcpTools(withBrowse).includes('list_dir'));
@@ -148,6 +181,39 @@ test('sandboxMcpTools returns correct tool list based on capabilities', () => {
assert.ok(sandboxMcpTools(withShell).includes('list_dir'));
});
test('private_data_space alone exposes private data tools through sandbox MCP', () => {
const caps = { ...DEFAULT_USER_CAPABILITIES, static_publish: false, private_data_space: true };
assert.deepEqual(sandboxMcpTools(caps), [
'private_data_info',
'private_data_schema',
'private_data_query',
'private_data_execute',
'schedule_create_item',
'schedule_create_reminder',
'schedule_list_items',
]);
const policy = buildAgentExtensionPolicy(caps, {
sandboxMcp: {
serverPath: '/opt/h5/mindspace-sandbox-mcp.mjs',
sandboxRoot: '/opt/h5/MindSpace/user-1',
userId: 'user-1',
},
});
const sandboxExt = policy.extensionOverrides.find((ext) => ext.name === 'sandbox-fs');
assert.ok(sandboxExt);
assert.equal(sandboxExt.envs.PRIVATE_DATA_USER_ID, 'user-1');
assert.deepEqual(sandboxExt.available_tools, [
'private_data_info',
'private_data_schema',
'private_data_query',
'private_data_execute',
'schedule_create_item',
'schedule_create_reminder',
'schedule_list_items',
]);
});
test('static_publish with sandboxMcp uses stdio sandbox-fs extension instead of developer', () => {
const caps = { ...DEFAULT_USER_CAPABILITIES, static_publish: true };
const sandboxMcp = { serverPath: '/opt/h5/mindspace-sandbox-mcp.mjs', sandboxRoot: '/opt/h5/MindSpace/abc123' };