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:
Your Name
2026-06-17 16:39:39 -07:00
parent ab0718938e
commit b0f5d6a51c
98 changed files with 5394 additions and 3010 deletions
+53 -6
View File
@@ -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 {