Extract memind_adm admin server, add local dev tooling, and remove image-generation.
Split platform admin and ops APIs into standalone admin-server.mjs with network guards; simplify billing to RMB token pricing, refactor user auth, and add rsync deploy plus local-test scripts and docs. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
+53
-6
@@ -194,9 +194,20 @@ export function sandboxDeveloperTools(capabilities) {
|
||||
return tools;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tools exposed by the sandbox MCP server (mindspace-sandbox-mcp.mjs) for static_publish users.
|
||||
* read_file is always included because edit_file requires reading first.
|
||||
*/
|
||||
export function sandboxMcpTools(capabilities) {
|
||||
const tools = ['read_file', 'write_file', 'edit_file', 'create_dir'];
|
||||
if (capabilities.shell || capabilities.code_browse) tools.push('list_dir');
|
||||
return tools;
|
||||
}
|
||||
|
||||
export function developerToolsFromPolicy(sessionPolicy) {
|
||||
const developer = sessionPolicy?.extensionOverrides?.find((ext) => ext.name === 'developer');
|
||||
return developer?.available_tools ?? [];
|
||||
const sandboxFs = sessionPolicy?.extensionOverrides?.find((ext) => ext.name === 'sandbox-fs');
|
||||
return (sandboxFs ?? developer)?.available_tools ?? [];
|
||||
}
|
||||
|
||||
function mergeDeveloperTools(capabilities) {
|
||||
@@ -211,10 +222,14 @@ function mergeDeveloperTools(capabilities) {
|
||||
/**
|
||||
* Build goose agent/start extension_overrides from resolved capability flags.
|
||||
* Returns null when the caller should use server defaults (admin / unrestricted).
|
||||
*
|
||||
* sandboxMcp: { serverPath, sandboxRoot, nodeExecPath? }
|
||||
* When provided for a static_publish user, the built-in developer extension is replaced
|
||||
* by a sandboxed stdio MCP that enforces filesystem boundaries at the OS level.
|
||||
*/
|
||||
export function buildAgentExtensionPolicy(
|
||||
capabilities,
|
||||
{ unrestricted = false, policies = null } = {},
|
||||
{ unrestricted = false, policies = null, sandboxMcp = null } = {},
|
||||
) {
|
||||
if (unrestricted) {
|
||||
return { extensionOverrides: null, enableContextMemory: true, gooseMode: 'auto' };
|
||||
@@ -222,9 +237,39 @@ export function buildAgentExtensionPolicy(
|
||||
|
||||
const extensions = [];
|
||||
if (capabilities.static_publish) {
|
||||
const sandboxTools = sandboxDeveloperTools(capabilities);
|
||||
if (sandboxTools.length > 0) {
|
||||
extensions.push(makeExtension('platform', 'developer', sandboxTools));
|
||||
if (sandboxMcp?.serverPath && sandboxMcp?.sandboxRoot) {
|
||||
// Sandboxed stdio MCP: enforces SANDBOX_ROOT at the OS level.
|
||||
// Replaces the built-in developer extension so path traversal is impossible.
|
||||
const mcpTools = sandboxMcpTools(capabilities);
|
||||
if (mcpTools.length > 0) {
|
||||
extensions.push({
|
||||
type: 'stdio',
|
||||
name: 'sandbox-fs',
|
||||
description: '工作区沙箱文件系统(路径限制在用户工作区内)',
|
||||
display_name: 'sandbox-fs',
|
||||
bundled: false,
|
||||
cmd: sandboxMcp.nodeExecPath ?? process.execPath,
|
||||
// sandboxRoot passed as argv[2] so it works even if goosed doesn't forward envs
|
||||
args: [sandboxMcp.serverPath, sandboxMcp.sandboxRoot],
|
||||
// envs (goosed field name) as belt-and-suspenders backup
|
||||
envs: {
|
||||
SANDBOX_ROOT: sandboxMcp.sandboxRoot,
|
||||
ALLOWED_TOOLS: mcpTools.join(','),
|
||||
},
|
||||
available_tools: mcpTools,
|
||||
});
|
||||
}
|
||||
// Keep developer extension only for image reading when applicable.
|
||||
if (capabilities.image_read) {
|
||||
extensions.push(makeExtension('platform', 'developer', ['read_image']));
|
||||
}
|
||||
} else {
|
||||
// Fallback when sandbox MCP is not configured: use built-in developer extension.
|
||||
// This is the legacy path — file operations are NOT boundary-enforced.
|
||||
const sandboxTools = sandboxDeveloperTools(capabilities);
|
||||
if (sandboxTools.length > 0) {
|
||||
extensions.push(makeExtension('platform', 'developer', sandboxTools));
|
||||
}
|
||||
}
|
||||
extensions.push(makeExtension('platform', 'skills', []));
|
||||
extensions.push(makeExtension('platform', 'summon', ['load_skill']));
|
||||
@@ -301,7 +346,9 @@ export function buildPageEditAgentPolicy(basePolicy) {
|
||||
}
|
||||
|
||||
const baseDeveloper = basePolicy?.extensionOverrides?.find((ext) => ext.name === 'developer');
|
||||
const canShell = baseDeveloper?.available_tools?.includes('shell') ?? false;
|
||||
const canShell =
|
||||
baseDeveloper?.available_tools?.includes('shell') ||
|
||||
basePolicy?.capabilities?.shell === true;
|
||||
const extensions = canShell ? [makeExtension('platform', 'developer', ['shell'])] : [];
|
||||
|
||||
return {
|
||||
|
||||
Reference in New Issue
Block a user