mirror of
https://github.com/nexu-io/open-design.git
synced 2026-06-01 03:14:35 +07:00
fix(daemon): allow codex sandbox override
This commit is contained in:
parent
fe58db2ba1
commit
cbb3d392c0
5 changed files with 132 additions and 53 deletions
|
|
@ -48,6 +48,10 @@ export function codexNeedsDangerFullAccessSandbox(
|
|||
platform: NodeJS.Platform = process.platform,
|
||||
env: NodeJS.ProcessEnv = process.env,
|
||||
): boolean {
|
||||
// Operator override for deployments where Codex cannot create its
|
||||
// workspace-write sandbox, for example unprivileged Linux containers.
|
||||
// Only danger-full-access is accepted; unknown values keep the default path.
|
||||
if (env.OD_CODEX_SANDBOX?.trim() === 'danger-full-access') return true;
|
||||
if (platform === 'win32') return true;
|
||||
// WSL reports `linux` but Codex still hits the Windows read-only
|
||||
// workspace-write sandbox path when launched from there (#2834).
|
||||
|
|
|
|||
|
|
@ -103,33 +103,37 @@ test('local agent profiles skip explicit unknown baseAgent without falling back'
|
|||
});
|
||||
|
||||
test('codex args disable plugins when OD_CODEX_DISABLE_PLUGINS is 1', () => {
|
||||
process.env.OD_CODEX_DISABLE_PLUGINS = '1';
|
||||
withEnvSnapshot(['OD_CODEX_DISABLE_PLUGINS', 'OD_CODEX_SANDBOX'], () => {
|
||||
process.env.OD_CODEX_DISABLE_PLUGINS = '1';
|
||||
delete process.env.OD_CODEX_SANDBOX;
|
||||
|
||||
withPlatform('darwin', () => {
|
||||
const args = codex.buildArgs('', [], [], {}, { cwd: '/tmp/od-project' });
|
||||
withPlatform('darwin', () => {
|
||||
const args = codex.buildArgs('', [], [], {}, { cwd: '/tmp/od-project' });
|
||||
|
||||
assert.deepEqual(args.slice(0, 11), [
|
||||
'exec',
|
||||
'--json',
|
||||
'--skip-git-repo-check',
|
||||
'--sandbox',
|
||||
'workspace-write',
|
||||
'-c',
|
||||
'sandbox_workspace_write.network_access=true',
|
||||
'-c',
|
||||
'default_permissions=":workspace"',
|
||||
'--disable',
|
||||
'plugins',
|
||||
]);
|
||||
assert.deepEqual(args.slice(0, 11), [
|
||||
'exec',
|
||||
'--json',
|
||||
'--skip-git-repo-check',
|
||||
'--sandbox',
|
||||
'workspace-write',
|
||||
'-c',
|
||||
'sandbox_workspace_write.network_access=true',
|
||||
'-c',
|
||||
'default_permissions=":workspace"',
|
||||
'--disable',
|
||||
'plugins',
|
||||
]);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
test('codex args use workspace-write sandbox on macOS and Linux', () => {
|
||||
delete process.env.OD_CODEX_DISABLE_PLUGINS;
|
||||
withEnvSnapshot(['OD_CODEX_DISABLE_PLUGINS', 'OD_CODEX_SANDBOX', 'WSL_DISTRO_NAME'], () => {
|
||||
delete process.env.OD_CODEX_DISABLE_PLUGINS;
|
||||
delete process.env.OD_CODEX_SANDBOX;
|
||||
|
||||
for (const platform of ['darwin', 'linux'] as const) {
|
||||
withPlatform(platform, () => {
|
||||
withEnvSnapshot(['WSL_DISTRO_NAME'], () => {
|
||||
for (const platform of ['darwin', 'linux'] as const) {
|
||||
withPlatform(platform, () => {
|
||||
delete process.env.WSL_DISTRO_NAME;
|
||||
const args = codex.buildArgs('', [], [], {}, { cwd: '/tmp/od-project' });
|
||||
assert.equal(args.includes('--full-auto'), false);
|
||||
|
|
@ -149,15 +153,15 @@ test('codex args use workspace-write sandbox on macOS and Linux', () => {
|
|||
true,
|
||||
);
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
test('codex args use danger-full-access sandbox on WSL because workspace-write stays read-only', () => {
|
||||
delete process.env.OD_CODEX_DISABLE_PLUGINS;
|
||||
|
||||
withPlatform('linux', () => {
|
||||
withEnvSnapshot(['WSL_DISTRO_NAME'], () => {
|
||||
withEnvSnapshot(['OD_CODEX_DISABLE_PLUGINS', 'OD_CODEX_SANDBOX', 'WSL_DISTRO_NAME'], () => {
|
||||
delete process.env.OD_CODEX_DISABLE_PLUGINS;
|
||||
delete process.env.OD_CODEX_SANDBOX;
|
||||
process.env.WSL_DISTRO_NAME = 'Ubuntu';
|
||||
assert.equal(codexNeedsDangerFullAccessSandbox('linux', process.env), true);
|
||||
const args = codex.buildArgs('', [], [], {}, { cwd: '/tmp/od-project' });
|
||||
|
|
@ -173,6 +177,50 @@ test('codex args use danger-full-access sandbox on WSL because workspace-write s
|
|||
});
|
||||
});
|
||||
|
||||
test('codex args allow OD_CODEX_SANDBOX danger-full-access override on Linux', () => {
|
||||
withPlatform('linux', () => {
|
||||
withEnvSnapshot(['OD_CODEX_DISABLE_PLUGINS', 'OD_CODEX_SANDBOX', 'WSL_DISTRO_NAME'], () => {
|
||||
delete process.env.OD_CODEX_DISABLE_PLUGINS;
|
||||
process.env.OD_CODEX_SANDBOX = 'danger-full-access';
|
||||
delete process.env.WSL_DISTRO_NAME;
|
||||
|
||||
assert.equal(codexNeedsDangerFullAccessSandbox('linux', process.env), true);
|
||||
const args = codex.buildArgs('', [], [], {}, { cwd: '/tmp/od-project' });
|
||||
assert.deepEqual(args.slice(0, 5), [
|
||||
'exec',
|
||||
'--json',
|
||||
'--skip-git-repo-check',
|
||||
'--sandbox',
|
||||
'danger-full-access',
|
||||
]);
|
||||
assert.equal(
|
||||
args.includes('sandbox_workspace_write.network_access=true'),
|
||||
false,
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
test('codex args ignore unknown OD_CODEX_SANDBOX values', () => {
|
||||
withPlatform('linux', () => {
|
||||
withEnvSnapshot(['OD_CODEX_DISABLE_PLUGINS', 'OD_CODEX_SANDBOX', 'WSL_DISTRO_NAME'], () => {
|
||||
delete process.env.OD_CODEX_DISABLE_PLUGINS;
|
||||
process.env.OD_CODEX_SANDBOX = 'workspace-write';
|
||||
delete process.env.WSL_DISTRO_NAME;
|
||||
|
||||
assert.equal(codexNeedsDangerFullAccessSandbox('linux', process.env), false);
|
||||
const args = codex.buildArgs('', [], [], {}, { cwd: '/tmp/od-project' });
|
||||
assert.deepEqual(args.slice(0, 5), [
|
||||
'exec',
|
||||
'--json',
|
||||
'--skip-git-repo-check',
|
||||
'--sandbox',
|
||||
'workspace-write',
|
||||
]);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
test('codex args use danger-full-access sandbox on Windows because workspace-write blocks PowerShell', () => {
|
||||
// Codex CLI's workspace-write sandbox mode on Windows lacks a working
|
||||
// OS-level sandbox and falls back to a policy that rejects shell
|
||||
|
|
@ -180,48 +228,57 @@ test('codex args use danger-full-access sandbox on Windows because workspace-wri
|
|||
// The agent cannot list files or run any shell-backed tool under that
|
||||
// policy. danger-full-access is Codex CLI's documented Windows-compatible
|
||||
// mode (issue #1721).
|
||||
delete process.env.OD_CODEX_DISABLE_PLUGINS;
|
||||
withEnvSnapshot(['OD_CODEX_DISABLE_PLUGINS', 'OD_CODEX_SANDBOX'], () => {
|
||||
delete process.env.OD_CODEX_DISABLE_PLUGINS;
|
||||
delete process.env.OD_CODEX_SANDBOX;
|
||||
|
||||
withPlatform('win32', () => {
|
||||
const args = codex.buildArgs('', [], [], {}, { cwd: '/tmp/od-project' });
|
||||
withPlatform('win32', () => {
|
||||
const args = codex.buildArgs('', [], [], {}, { cwd: '/tmp/od-project' });
|
||||
|
||||
assert.deepEqual(args.slice(0, 5), [
|
||||
'exec',
|
||||
'--json',
|
||||
'--skip-git-repo-check',
|
||||
'--sandbox',
|
||||
'danger-full-access',
|
||||
]);
|
||||
// The workspace-write-scoped network override is meaningless under
|
||||
// danger-full-access and must not appear on Windows.
|
||||
assert.equal(args.includes('workspace-write'), false);
|
||||
assert.equal(
|
||||
args.includes('sandbox_workspace_write.network_access=true'),
|
||||
false,
|
||||
);
|
||||
assert.equal(args.includes('default_permissions=":workspace"'), true);
|
||||
assert.deepEqual(args.slice(0, 5), [
|
||||
'exec',
|
||||
'--json',
|
||||
'--skip-git-repo-check',
|
||||
'--sandbox',
|
||||
'danger-full-access',
|
||||
]);
|
||||
// The workspace-write-scoped network override is meaningless under
|
||||
// danger-full-access and must not appear on Windows.
|
||||
assert.equal(args.includes('workspace-write'), false);
|
||||
assert.equal(
|
||||
args.includes('sandbox_workspace_write.network_access=true'),
|
||||
false,
|
||||
);
|
||||
assert.equal(args.includes('default_permissions=":workspace"'), true);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
test('codex args keep plugins enabled when OD_CODEX_DISABLE_PLUGINS is unset', () => {
|
||||
delete process.env.OD_CODEX_DISABLE_PLUGINS;
|
||||
withEnvSnapshot(['OD_CODEX_DISABLE_PLUGINS', 'OD_CODEX_SANDBOX'], () => {
|
||||
delete process.env.OD_CODEX_DISABLE_PLUGINS;
|
||||
delete process.env.OD_CODEX_SANDBOX;
|
||||
|
||||
withPlatform('darwin', () => {
|
||||
const args = codex.buildArgs('', [], [], {}, { cwd: '/tmp/od-project' });
|
||||
withPlatform('darwin', () => {
|
||||
const args = codex.buildArgs('', [], [], {}, { cwd: '/tmp/od-project' });
|
||||
|
||||
assert.equal(args.includes('--disable'), false);
|
||||
assert.equal(args.includes('plugins'), false);
|
||||
assert.equal(args.includes('--disable'), false);
|
||||
assert.equal(args.includes('plugins'), false);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
test('codex args keep plugins enabled when OD_CODEX_DISABLE_PLUGINS is not 1', () => {
|
||||
process.env.OD_CODEX_DISABLE_PLUGINS = 'true';
|
||||
withEnvSnapshot(['OD_CODEX_DISABLE_PLUGINS', 'OD_CODEX_SANDBOX'], () => {
|
||||
process.env.OD_CODEX_DISABLE_PLUGINS = 'true';
|
||||
delete process.env.OD_CODEX_SANDBOX;
|
||||
|
||||
withPlatform('darwin', () => {
|
||||
const args = codex.buildArgs('', [], [], {}, { cwd: '/tmp/od-project' });
|
||||
withPlatform('darwin', () => {
|
||||
const args = codex.buildArgs('', [], [], {}, { cwd: '/tmp/od-project' });
|
||||
|
||||
assert.equal(args.includes('--disable'), false);
|
||||
assert.equal(args.includes('plugins'), false);
|
||||
assert.equal(args.includes('--disable'), false);
|
||||
assert.equal(args.includes('plugins'), false);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -22,3 +22,6 @@ OPEN_DESIGN_MEM_LIMIT=384m
|
|||
# Node.js heap cap inside the container.
|
||||
NODE_OPTIONS=--max-old-space-size=192
|
||||
|
||||
# Optional Codex CLI sandbox override. Set to danger-full-access only when
|
||||
# Codex fails with workspace-write sandbox setup errors inside the container.
|
||||
OD_CODEX_SANDBOX=
|
||||
|
|
|
|||
|
|
@ -58,6 +58,20 @@ The image intentionally does not bundle Claude/Codex/Gemini CLI binaries. Keep
|
|||
those outside the image, or build a separate private runtime layer if a server
|
||||
deployment needs local code-agent CLIs installed in the container.
|
||||
|
||||
If you install Codex inside an unprivileged Linux container and it fails while
|
||||
creating its `workspace-write` sandbox, opt into Codex's full-access mode for
|
||||
all Codex runs in that deployment:
|
||||
|
||||
```bash
|
||||
OD_CODEX_SANDBOX=danger-full-access docker compose up -d --no-build
|
||||
```
|
||||
|
||||
Only the exact value `danger-full-access` is supported; unknown values are
|
||||
ignored. Use this only for trusted, single-user deployments. It lets Codex run
|
||||
without the workspace-write sandbox, which is useful when the container host
|
||||
blocks unprivileged user namespaces, but it gives the Codex process broader
|
||||
filesystem access inside the container.
|
||||
|
||||
## Publish to Docker Hub
|
||||
|
||||
```bash
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ services:
|
|||
OD_PORT: 7456
|
||||
OD_WEB_PORT: ${OPEN_DESIGN_PORT:-7456}
|
||||
OD_API_TOKEN: ${OD_API_TOKEN:?Please run 'openssl rand -hex 32' to generate one, and set it in your .env file.}
|
||||
OD_CODEX_SANDBOX: ${OD_CODEX_SANDBOX:-}
|
||||
ports:
|
||||
- "127.0.0.1:${OPEN_DESIGN_PORT:-7456}:7456"
|
||||
volumes:
|
||||
|
|
|
|||
Loading…
Reference in a new issue