fix sandbox tool/extension/skill discovery for members

Sandbox now mounts global content at short /officer/* paths to avoid
bwrap intermediate directory traversal issues. Pi uses NODE_PATH for
extension dependency resolution.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-08 07:13:19 +00:00
co-authored by Claude Opus 4.6
parent 8264e7b995
commit 8377da8a77
2 changed files with 96 additions and 30 deletions
+20 -2
View File
@@ -17,15 +17,20 @@ const OS_USERNAME = (() => {
return result.stdout.toString().trim() || 'pastilhas';
})();
// Sandbox mount point for user data (short path avoids intermediate dir traversal issues)
// Sandbox mount points
export const SANDBOX_DATA = '/data';
export const SANDBOX_HOME = `${SANDBOX_DATA}/home`;
export const SANDBOX_GLOBAL_ROOT = '/officer';
export const SANDBOX_GLOBAL_SKILLS = `${SANDBOX_GLOBAL_ROOT}/skills`;
export const SANDBOX_GLOBAL_EXTENSIONS = `${SANDBOX_GLOBAL_ROOT}/extensions`;
export const SANDBOX_GLOBAL_TOOLS = `${SANDBOX_GLOBAL_ROOT}/tools`;
// Build bwrap sandbox prefix for a given user email.
// Returns args up to (but not including) the `-- runuser` suffix.
// Callers can append extra `--setenv` args before calling `buildRunuserSuffix()`.
export function buildSandboxPrefix(email: string): string[] {
const userDataDir = join(DATA_PATH, email);
const globalSkillsDir = join(DATA_PATH, 'skills');
const globalToolsDir = join(DATA_PATH, 'tools');
const globalExtensionsDir = join(DATA_PATH, 'extensions');
@@ -83,10 +88,23 @@ export function buildSandboxPrefix(email: string): string[] {
// Project source (for MCP server)
args.push('--ro-bind', PROJECT_ROOT, PROJECT_ROOT);
// Global tools/extensions (read-only, mounted at original paths for MCP config references)
// Ensure DATA_PATH intermediate dirs are traversable (same issue as HOME)
args.push('--perms', '0755', '--dir', DATA_PATH);
// Global content mounted at original paths for existing host-path references
if (existsSync(globalSkillsDir)) args.push('--ro-bind', globalSkillsDir, globalSkillsDir);
if (existsSync(globalToolsDir)) args.push('--ro-bind', globalToolsDir, globalToolsDir);
if (existsSync(globalExtensionsDir)) args.push('--ro-bind', globalExtensionsDir, globalExtensionsDir);
// Ensure sandbox-local global root is traversable before mounting nested paths under it.
args.push('--perms', '0755', '--dir', SANDBOX_GLOBAL_ROOT);
// Global content also mounted at short sandbox-local paths so nested imports do not
// depend on traversing host-specific parent directories created by bwrap.
if (existsSync(globalSkillsDir)) args.push('--ro-bind', globalSkillsDir, SANDBOX_GLOBAL_SKILLS);
if (existsSync(globalToolsDir)) args.push('--ro-bind', globalToolsDir, SANDBOX_GLOBAL_TOOLS);
if (existsSync(globalExtensionsDir)) args.push('--ro-bind', globalExtensionsDir, SANDBOX_GLOBAL_EXTENSIONS);
// User data (read-write, mounted at /data to avoid intermediate dir permission issues)
args.push('--bind', userDataDir, SANDBOX_DATA);