@parall/codex-agent 1.18.1 → 1.18.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/config.d.ts +28 -6
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +38 -21
- package/dist/dispatch.d.ts.map +1 -1
- package/dist/dispatch.js +13 -4
- package/dist/index.js +2 -1
- package/package.json +1 -1
- package/src/config.ts +55 -22
- package/src/dispatch.ts +13 -4
- package/src/index.ts +2 -1
package/dist/config.d.ts
CHANGED
|
@@ -10,20 +10,42 @@ export type CodexAgentConfig = {
|
|
|
10
10
|
workspaceDir: string;
|
|
11
11
|
model?: string;
|
|
12
12
|
reasoningEffort?: string;
|
|
13
|
-
|
|
13
|
+
/**
|
|
14
|
+
* Only set when the user explicitly specified `PRLL_CODEX_SANDBOX`.
|
|
15
|
+
* When undefined, the bridge deliberately does NOT pass `sandbox` on
|
|
16
|
+
* `thread/{start,resume}` so codex reads whatever the user configured
|
|
17
|
+
* in `~/.codex/config.toml` (`sandbox_mode`). This matches the self-
|
|
18
|
+
* hosted philosophy: user's codex setup is authoritative; bridge only
|
|
19
|
+
* overrides when the user asks it to.
|
|
20
|
+
*/
|
|
21
|
+
sandbox?: string;
|
|
22
|
+
/**
|
|
23
|
+
* Approval policy is forced to `"never"` by default (not configurable
|
|
24
|
+
* via config.toml override): Parall's HITL contract routes approvals
|
|
25
|
+
* through the app, not codex's own stdin-prompt mechanism, which the
|
|
26
|
+
* long-running app-server subprocess has no way to answer.
|
|
27
|
+
*/
|
|
14
28
|
approvalPolicy: string;
|
|
15
29
|
runtimeKey?: string;
|
|
16
30
|
};
|
|
17
31
|
export declare function resolveCodexAgentConfig(env?: NodeJS.ProcessEnv): CodexAgentConfig;
|
|
18
32
|
/**
|
|
19
|
-
* Normalise a sandbox value
|
|
20
|
-
*
|
|
21
|
-
*
|
|
33
|
+
* Normalise a sandbox value to the kebab-case form the app-server JSON-RPC
|
|
34
|
+
* enum expects (`read-only` / `workspace-write` / `danger-full-access`).
|
|
35
|
+
* The CLI-facing PRLL_CODEX_SANDBOX env var may come in as either kebab-case
|
|
36
|
+
* or camelCase (from docs that cite the Rust field names); accept both.
|
|
37
|
+
*
|
|
38
|
+
* Empirically verified: the app-server rejects `workspaceWrite` with
|
|
39
|
+
* "unknown variant workspaceWrite, expected one of read-only,
|
|
40
|
+
* workspace-write, danger-full-access"
|
|
41
|
+
* which is the Rust enum's serde kebab-case form.
|
|
22
42
|
*/
|
|
23
43
|
export declare function normalizeSandbox(value: string): string;
|
|
24
44
|
/**
|
|
25
|
-
* Normalise an approval policy string to the
|
|
26
|
-
* JSON-RPC
|
|
45
|
+
* Normalise an approval policy string to the kebab-case form the app-server
|
|
46
|
+
* JSON-RPC enum expects (`never` / `on-request` / `on-failure` /
|
|
47
|
+
* `unless-trusted`). Mirrors the sandbox enum — serde rename_all kebab-case
|
|
48
|
+
* applies across app-server protocol enums.
|
|
27
49
|
*/
|
|
28
50
|
export declare function normalizeApprovalPolicy(value: string): string;
|
|
29
51
|
export declare function resolveWsUrl(apiUrl: string, explicitWsUrl?: string, swimlaneName?: string): string;
|
package/dist/config.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAGA,MAAM,MAAM,gBAAgB,GAAG;IAC7B,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,EAAE,MAAM,CAAC;IACrB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,OAAO,EAAE,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAGA,MAAM,MAAM,gBAAgB,GAAG;IAC7B,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,EAAE,MAAM,CAAC;IACrB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB;;;;;;;OAOG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB;;;;;OAKG;IACH,cAAc,EAAE,MAAM,CAAC;IACvB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB,CAAC;AAcF,wBAAgB,uBAAuB,CAAC,GAAG,GAAE,MAAM,CAAC,UAAwB,GAAG,gBAAgB,CAwC9F;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAUtD;AAED;;;;;GAKG;AACH,wBAAgB,uBAAuB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAW7D;AAED,wBAAgB,YAAY,CAAC,MAAM,EAAE,MAAM,EAAE,aAAa,CAAC,EAAE,MAAM,EAAE,YAAY,CAAC,EAAE,MAAM,GAAG,MAAM,CAMlG;AAED,wBAAgB,oBAAoB,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,CAEhE;AAED,wBAAgB,8BAA8B,CAAC,QAAQ,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,MAAM,CAG3F;AAED,wBAAgB,wBAAwB,CAAC,QAAQ,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,MAAM,CAGrF"}
|
package/dist/config.js
CHANGED
|
@@ -14,11 +14,20 @@ export function resolveCodexAgentConfig(env = process.env) {
|
|
|
14
14
|
const apiUrl = requireEnv(env, "PRLL_API_URL");
|
|
15
15
|
const apiKey = requireEnv(env, "PRLL_API_KEY");
|
|
16
16
|
const orgId = requireEnv(env, "PRLL_ORG_ID");
|
|
17
|
+
// CODEX_HOME is where codex looks for `auth.json` / `config.toml`. Its
|
|
18
|
+
// own default is `$HOME/.codex`; falling back to `$HOME` here was wrong
|
|
19
|
+
// — it made the spawned app-server subprocess look for
|
|
20
|
+
// `$HOME/auth.json`, which silently doesn't exist, so every
|
|
21
|
+
// `thread/start` failed with `401 Unauthorized` against the OpenAI
|
|
22
|
+
// WebSocket endpoint. Default to `$HOME/.codex` so `codex login`'s
|
|
23
|
+
// credentials are picked up transparently.
|
|
17
24
|
const codexHome = resolvePath(env.PRLL_CODEX_HOME?.trim()
|
|
18
25
|
|| env.CODEX_HOME?.trim()
|
|
19
|
-
|| env.HOME
|
|
20
|
-
|
|
21
|
-
|
|
26
|
+
|| path.join(env.HOME || os.homedir(), ".codex"));
|
|
27
|
+
// Bridge state lives outside CODEX_HOME to avoid polluting codex's own
|
|
28
|
+
// directory with non-codex files.
|
|
29
|
+
const stateDir = resolvePath(env.PRLL_CODEX_STATE_DIR?.trim()
|
|
30
|
+
|| path.join(env.HOME || os.homedir(), ".parall-agent"));
|
|
22
31
|
const workspaceDir = resolvePath(env.PRLL_CODEX_WORKSPACE_DIR?.trim() || path.join(stateDir, "workspace"));
|
|
23
32
|
return {
|
|
24
33
|
apiUrl,
|
|
@@ -32,40 +41,48 @@ export function resolveCodexAgentConfig(env = process.env) {
|
|
|
32
41
|
workspaceDir,
|
|
33
42
|
model: env.PRLL_CODEX_MODEL?.trim() || undefined,
|
|
34
43
|
reasoningEffort: env.PRLL_CODEX_REASONING_EFFORT?.trim() || undefined,
|
|
35
|
-
sandbox: env.PRLL_CODEX_SANDBOX?.trim() ||
|
|
44
|
+
sandbox: env.PRLL_CODEX_SANDBOX?.trim() || undefined,
|
|
36
45
|
approvalPolicy: env.PRLL_CODEX_APPROVAL?.trim() || "never",
|
|
37
46
|
runtimeKey: env.PRLL_CODEX_RUNTIME_KEY?.trim() || undefined,
|
|
38
47
|
};
|
|
39
48
|
}
|
|
40
49
|
/**
|
|
41
|
-
* Normalise a sandbox value
|
|
42
|
-
*
|
|
43
|
-
*
|
|
50
|
+
* Normalise a sandbox value to the kebab-case form the app-server JSON-RPC
|
|
51
|
+
* enum expects (`read-only` / `workspace-write` / `danger-full-access`).
|
|
52
|
+
* The CLI-facing PRLL_CODEX_SANDBOX env var may come in as either kebab-case
|
|
53
|
+
* or camelCase (from docs that cite the Rust field names); accept both.
|
|
54
|
+
*
|
|
55
|
+
* Empirically verified: the app-server rejects `workspaceWrite` with
|
|
56
|
+
* "unknown variant workspaceWrite, expected one of read-only,
|
|
57
|
+
* workspace-write, danger-full-access"
|
|
58
|
+
* which is the Rust enum's serde kebab-case form.
|
|
44
59
|
*/
|
|
45
60
|
export function normalizeSandbox(value) {
|
|
46
61
|
const map = {
|
|
47
|
-
"workspace-write": "
|
|
48
|
-
"workspacewrite": "
|
|
49
|
-
"danger-full-access": "
|
|
50
|
-
"dangerfullaccess": "
|
|
51
|
-
"read-only": "
|
|
52
|
-
"readonly": "
|
|
62
|
+
"workspace-write": "workspace-write",
|
|
63
|
+
"workspacewrite": "workspace-write",
|
|
64
|
+
"danger-full-access": "danger-full-access",
|
|
65
|
+
"dangerfullaccess": "danger-full-access",
|
|
66
|
+
"read-only": "read-only",
|
|
67
|
+
"readonly": "read-only",
|
|
53
68
|
};
|
|
54
69
|
return map[value.toLowerCase()] ?? value;
|
|
55
70
|
}
|
|
56
71
|
/**
|
|
57
|
-
* Normalise an approval policy string to the
|
|
58
|
-
* JSON-RPC
|
|
72
|
+
* Normalise an approval policy string to the kebab-case form the app-server
|
|
73
|
+
* JSON-RPC enum expects (`never` / `on-request` / `on-failure` /
|
|
74
|
+
* `unless-trusted`). Mirrors the sandbox enum — serde rename_all kebab-case
|
|
75
|
+
* applies across app-server protocol enums.
|
|
59
76
|
*/
|
|
60
77
|
export function normalizeApprovalPolicy(value) {
|
|
61
78
|
const map = {
|
|
62
79
|
"never": "never",
|
|
63
|
-
"on-request": "
|
|
64
|
-
"onrequest": "
|
|
65
|
-
"unless-trusted": "
|
|
66
|
-
"unlesstrusted": "
|
|
67
|
-
"on-failure": "
|
|
68
|
-
"onfailure": "
|
|
80
|
+
"on-request": "on-request",
|
|
81
|
+
"onrequest": "on-request",
|
|
82
|
+
"unless-trusted": "unless-trusted",
|
|
83
|
+
"unlesstrusted": "unless-trusted",
|
|
84
|
+
"on-failure": "on-failure",
|
|
85
|
+
"onfailure": "on-failure",
|
|
69
86
|
};
|
|
70
87
|
return map[value.toLowerCase()] ?? value;
|
|
71
88
|
}
|
package/dist/dispatch.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dispatch.d.ts","sourceRoot":"","sources":["../src/dispatch.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EACV,eAAe,EACf,eAAe,EACf,YAAY,EACZ,QAAQ,EACR,iBAAiB,EACjB,aAAa,EACb,YAAY,EACb,MAAM,oBAAoB,CAAC;AAC5B,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAIpD,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,sBAAsB,CAAC;AAEhE,KAAK,4BAA4B,GAAG,IAAI,CACtC,gBAAgB,EACd,gBAAgB,GAChB,UAAU,GACV,WAAW,GACX,OAAO,GACP,iBAAiB,GACjB,SAAS,GACT,cAAc,CACjB,GAAG;IACF,cAAc,EAAE,mBAAmB,CAAC;IACpC,GAAG,CAAC,EAAE,aAAa,CAAC;CACrB,CAAC;AAOF;;;;;;;;;;;;;GAaG;AACH,qBAAa,qBAAsB,YAAW,eAAe;IA6B/C,OAAO,CAAC,QAAQ,CAAC,IAAI;IA5BjC,OAAO,CAAC,MAAM,CAAmC;IACjD,OAAO,CAAC,IAAI,CAA+C;IAC3D,OAAO,CAAC,WAAW,CAAS;IAC5B,OAAO,CAAC,YAAY,CAA8B;IAClD,OAAO,CAAC,QAAQ,CAAC,WAAW,CAA+B;IAC3D,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAqB;IACtD,OAAO,CAAC,QAAQ,CAAS;IAEzB;;;;;;;OAOG;IACH,OAAO,CAAC,aAAa;gBAYQ,IAAI,EAAE,4BAA4B;IAExD,QAAQ,CAAC,EAAE,YAAY,EAAE,UAAU,EAAE,OAAO,EAAE,EAAE,YAAY,GAAG,aAAa,CAAC,YAAY,CAAC;IA0J3F,WAAW,CAAC,EAAE,UAAU,EAAE,gBAAgB,EAAE,EAAE,QAAQ,GAAG,OAAO,CAAC,iBAAiB,GAAG,IAAI,CAAC;IAuBhG,WAAW,CAAC,EAAE,IAAI,EAAE,EAAE,eAAe;IAIrC,OAAO,CAAC,cAAc;IAKhB,IAAI;YAaI,aAAa;YAcb,OAAO;IA2DrB,OAAO,CAAC,qBAAqB;YAiCf,UAAU;
|
|
1
|
+
{"version":3,"file":"dispatch.d.ts","sourceRoot":"","sources":["../src/dispatch.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EACV,eAAe,EACf,eAAe,EACf,YAAY,EACZ,QAAQ,EACR,iBAAiB,EACjB,aAAa,EACb,YAAY,EACb,MAAM,oBAAoB,CAAC;AAC5B,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAIpD,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,sBAAsB,CAAC;AAEhE,KAAK,4BAA4B,GAAG,IAAI,CACtC,gBAAgB,EACd,gBAAgB,GAChB,UAAU,GACV,WAAW,GACX,OAAO,GACP,iBAAiB,GACjB,SAAS,GACT,cAAc,CACjB,GAAG;IACF,cAAc,EAAE,mBAAmB,CAAC;IACpC,GAAG,CAAC,EAAE,aAAa,CAAC;CACrB,CAAC;AAOF;;;;;;;;;;;;;GAaG;AACH,qBAAa,qBAAsB,YAAW,eAAe;IA6B/C,OAAO,CAAC,QAAQ,CAAC,IAAI;IA5BjC,OAAO,CAAC,MAAM,CAAmC;IACjD,OAAO,CAAC,IAAI,CAA+C;IAC3D,OAAO,CAAC,WAAW,CAAS;IAC5B,OAAO,CAAC,YAAY,CAA8B;IAClD,OAAO,CAAC,QAAQ,CAAC,WAAW,CAA+B;IAC3D,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAqB;IACtD,OAAO,CAAC,QAAQ,CAAS;IAEzB;;;;;;;OAOG;IACH,OAAO,CAAC,aAAa;gBAYQ,IAAI,EAAE,4BAA4B;IAExD,QAAQ,CAAC,EAAE,YAAY,EAAE,UAAU,EAAE,OAAO,EAAE,EAAE,YAAY,GAAG,aAAa,CAAC,YAAY,CAAC;IA0J3F,WAAW,CAAC,EAAE,UAAU,EAAE,gBAAgB,EAAE,EAAE,QAAQ,GAAG,OAAO,CAAC,iBAAiB,GAAG,IAAI,CAAC;IAuBhG,WAAW,CAAC,EAAE,IAAI,EAAE,EAAE,eAAe;IAIrC,OAAO,CAAC,cAAc;IAKhB,IAAI;YAaI,aAAa;YAcb,OAAO;IA2DrB,OAAO,CAAC,qBAAqB;YAiCf,UAAU;IAgDxB,OAAO,CAAC,iBAAiB;CA2B1B"}
|
package/dist/dispatch.js
CHANGED
|
@@ -353,13 +353,22 @@ export class CodexAppServerAdapter {
|
|
|
353
353
|
// model, reasoningEffort). Passing them on resume lets the user change
|
|
354
354
|
// PRLL_CODEX_* env vars and have the bridge pick them up on the next
|
|
355
355
|
// restart instead of being stuck on the values baked into the persisted
|
|
356
|
-
// thread. Sandbox / approval values are normalised to the
|
|
357
|
-
// the protocol expects (`
|
|
358
|
-
//
|
|
356
|
+
// thread. Sandbox / approval values are normalised to the kebab-case
|
|
357
|
+
// enum form the protocol expects (`workspace-write`, `on-request`, etc.
|
|
358
|
+
// — serde rename_all kebab-case on the Rust enum); users can supply
|
|
359
|
+
// either kebab or camel and we always emit kebab.
|
|
359
360
|
const commonParams = {
|
|
360
361
|
approvalPolicy: normalizeApprovalPolicy(this.opts.approvalPolicy),
|
|
361
|
-
sandbox: normalizeSandbox(this.opts.sandbox),
|
|
362
362
|
};
|
|
363
|
+
// Only override sandbox when the user explicitly set PRLL_CODEX_SANDBOX.
|
|
364
|
+
// Otherwise let codex read `sandbox_mode` from ~/.codex/config.toml — the
|
|
365
|
+
// user's own configuration is authoritative for a self-hosted bridge,
|
|
366
|
+
// and codex 0.120.0's workspace-write mode has a write_stdin bug that
|
|
367
|
+
// our previous `workspace-write` default tripped for users whose config
|
|
368
|
+
// had danger-full-access.
|
|
369
|
+
if (this.opts.sandbox) {
|
|
370
|
+
commonParams.sandbox = normalizeSandbox(this.opts.sandbox);
|
|
371
|
+
}
|
|
363
372
|
if (this.opts.model)
|
|
364
373
|
commonParams.model = this.opts.model;
|
|
365
374
|
if (this.opts.reasoningEffort) {
|
package/dist/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import * as os from "node:os";
|
|
3
|
-
import { ParallAgentGateway } from "@parall/agent-core";
|
|
3
|
+
import { ParallAgentGateway, parseShutdownDeadlineMs } from "@parall/agent-core";
|
|
4
4
|
import { ParallClient, ParallWs } from "@parall/sdk";
|
|
5
5
|
import { buildCodexRuntimeKey, resolveCodexAgentConfig, resolveWsUrl, sessionStateFilePathForRuntime, stepIdFilePathForSession, } from "./config.js";
|
|
6
6
|
import { CodexAppServerAdapter } from "./dispatch.js";
|
|
@@ -65,6 +65,7 @@ async function main() {
|
|
|
65
65
|
},
|
|
66
66
|
dispatchAdapter: adapter,
|
|
67
67
|
log,
|
|
68
|
+
shutdownDeadlineMs: parseShutdownDeadlineMs(process.env.PRLL_SHUTDOWN_DEADLINE_MS),
|
|
68
69
|
stepIdFilePathForSession: (sessionKey) => stepIdFilePathForSession(config.stateDir, sessionKey),
|
|
69
70
|
});
|
|
70
71
|
const abortController = new AbortController();
|
package/package.json
CHANGED
package/src/config.ts
CHANGED
|
@@ -13,7 +13,21 @@ export type CodexAgentConfig = {
|
|
|
13
13
|
workspaceDir: string;
|
|
14
14
|
model?: string;
|
|
15
15
|
reasoningEffort?: string;
|
|
16
|
-
|
|
16
|
+
/**
|
|
17
|
+
* Only set when the user explicitly specified `PRLL_CODEX_SANDBOX`.
|
|
18
|
+
* When undefined, the bridge deliberately does NOT pass `sandbox` on
|
|
19
|
+
* `thread/{start,resume}` so codex reads whatever the user configured
|
|
20
|
+
* in `~/.codex/config.toml` (`sandbox_mode`). This matches the self-
|
|
21
|
+
* hosted philosophy: user's codex setup is authoritative; bridge only
|
|
22
|
+
* overrides when the user asks it to.
|
|
23
|
+
*/
|
|
24
|
+
sandbox?: string;
|
|
25
|
+
/**
|
|
26
|
+
* Approval policy is forced to `"never"` by default (not configurable
|
|
27
|
+
* via config.toml override): Parall's HITL contract routes approvals
|
|
28
|
+
* through the app, not codex's own stdin-prompt mechanism, which the
|
|
29
|
+
* long-running app-server subprocess has no way to answer.
|
|
30
|
+
*/
|
|
17
31
|
approvalPolicy: string;
|
|
18
32
|
runtimeKey?: string;
|
|
19
33
|
};
|
|
@@ -34,13 +48,24 @@ export function resolveCodexAgentConfig(env: NodeJS.ProcessEnv = process.env): C
|
|
|
34
48
|
const apiUrl = requireEnv(env, "PRLL_API_URL");
|
|
35
49
|
const apiKey = requireEnv(env, "PRLL_API_KEY");
|
|
36
50
|
const orgId = requireEnv(env, "PRLL_ORG_ID");
|
|
51
|
+
// CODEX_HOME is where codex looks for `auth.json` / `config.toml`. Its
|
|
52
|
+
// own default is `$HOME/.codex`; falling back to `$HOME` here was wrong
|
|
53
|
+
// — it made the spawned app-server subprocess look for
|
|
54
|
+
// `$HOME/auth.json`, which silently doesn't exist, so every
|
|
55
|
+
// `thread/start` failed with `401 Unauthorized` against the OpenAI
|
|
56
|
+
// WebSocket endpoint. Default to `$HOME/.codex` so `codex login`'s
|
|
57
|
+
// credentials are picked up transparently.
|
|
37
58
|
const codexHome = resolvePath(
|
|
38
59
|
env.PRLL_CODEX_HOME?.trim()
|
|
39
60
|
|| env.CODEX_HOME?.trim()
|
|
40
|
-
|| env.HOME
|
|
41
|
-
|
|
61
|
+
|| path.join(env.HOME || os.homedir(), ".codex"),
|
|
62
|
+
);
|
|
63
|
+
// Bridge state lives outside CODEX_HOME to avoid polluting codex's own
|
|
64
|
+
// directory with non-codex files.
|
|
65
|
+
const stateDir = resolvePath(
|
|
66
|
+
env.PRLL_CODEX_STATE_DIR?.trim()
|
|
67
|
+
|| path.join(env.HOME || os.homedir(), ".parall-agent"),
|
|
42
68
|
);
|
|
43
|
-
const stateDir = resolvePath(env.PRLL_CODEX_STATE_DIR?.trim() || path.join(codexHome, ".parall-agent"));
|
|
44
69
|
const workspaceDir = resolvePath(env.PRLL_CODEX_WORKSPACE_DIR?.trim() || path.join(stateDir, "workspace"));
|
|
45
70
|
|
|
46
71
|
return {
|
|
@@ -55,42 +80,50 @@ export function resolveCodexAgentConfig(env: NodeJS.ProcessEnv = process.env): C
|
|
|
55
80
|
workspaceDir,
|
|
56
81
|
model: env.PRLL_CODEX_MODEL?.trim() || undefined,
|
|
57
82
|
reasoningEffort: env.PRLL_CODEX_REASONING_EFFORT?.trim() || undefined,
|
|
58
|
-
sandbox: env.PRLL_CODEX_SANDBOX?.trim() ||
|
|
83
|
+
sandbox: env.PRLL_CODEX_SANDBOX?.trim() || undefined,
|
|
59
84
|
approvalPolicy: env.PRLL_CODEX_APPROVAL?.trim() || "never",
|
|
60
85
|
runtimeKey: env.PRLL_CODEX_RUNTIME_KEY?.trim() || undefined,
|
|
61
86
|
};
|
|
62
87
|
}
|
|
63
88
|
|
|
64
89
|
/**
|
|
65
|
-
* Normalise a sandbox value
|
|
66
|
-
*
|
|
67
|
-
*
|
|
90
|
+
* Normalise a sandbox value to the kebab-case form the app-server JSON-RPC
|
|
91
|
+
* enum expects (`read-only` / `workspace-write` / `danger-full-access`).
|
|
92
|
+
* The CLI-facing PRLL_CODEX_SANDBOX env var may come in as either kebab-case
|
|
93
|
+
* or camelCase (from docs that cite the Rust field names); accept both.
|
|
94
|
+
*
|
|
95
|
+
* Empirically verified: the app-server rejects `workspaceWrite` with
|
|
96
|
+
* "unknown variant workspaceWrite, expected one of read-only,
|
|
97
|
+
* workspace-write, danger-full-access"
|
|
98
|
+
* which is the Rust enum's serde kebab-case form.
|
|
68
99
|
*/
|
|
69
100
|
export function normalizeSandbox(value: string): string {
|
|
70
101
|
const map: Record<string, string> = {
|
|
71
|
-
"workspace-write": "
|
|
72
|
-
"workspacewrite": "
|
|
73
|
-
"danger-full-access": "
|
|
74
|
-
"dangerfullaccess": "
|
|
75
|
-
"read-only": "
|
|
76
|
-
"readonly": "
|
|
102
|
+
"workspace-write": "workspace-write",
|
|
103
|
+
"workspacewrite": "workspace-write",
|
|
104
|
+
"danger-full-access": "danger-full-access",
|
|
105
|
+
"dangerfullaccess": "danger-full-access",
|
|
106
|
+
"read-only": "read-only",
|
|
107
|
+
"readonly": "read-only",
|
|
77
108
|
};
|
|
78
109
|
return map[value.toLowerCase()] ?? value;
|
|
79
110
|
}
|
|
80
111
|
|
|
81
112
|
/**
|
|
82
|
-
* Normalise an approval policy string to the
|
|
83
|
-
* JSON-RPC
|
|
113
|
+
* Normalise an approval policy string to the kebab-case form the app-server
|
|
114
|
+
* JSON-RPC enum expects (`never` / `on-request` / `on-failure` /
|
|
115
|
+
* `unless-trusted`). Mirrors the sandbox enum — serde rename_all kebab-case
|
|
116
|
+
* applies across app-server protocol enums.
|
|
84
117
|
*/
|
|
85
118
|
export function normalizeApprovalPolicy(value: string): string {
|
|
86
119
|
const map: Record<string, string> = {
|
|
87
120
|
"never": "never",
|
|
88
|
-
"on-request": "
|
|
89
|
-
"onrequest": "
|
|
90
|
-
"unless-trusted": "
|
|
91
|
-
"unlesstrusted": "
|
|
92
|
-
"on-failure": "
|
|
93
|
-
"onfailure": "
|
|
121
|
+
"on-request": "on-request",
|
|
122
|
+
"onrequest": "on-request",
|
|
123
|
+
"unless-trusted": "unless-trusted",
|
|
124
|
+
"unlesstrusted": "unless-trusted",
|
|
125
|
+
"on-failure": "on-failure",
|
|
126
|
+
"onfailure": "on-failure",
|
|
94
127
|
};
|
|
95
128
|
return map[value.toLowerCase()] ?? value;
|
|
96
129
|
}
|
package/src/dispatch.ts
CHANGED
|
@@ -396,13 +396,22 @@ export class CodexAppServerAdapter implements DispatchAdapter {
|
|
|
396
396
|
// model, reasoningEffort). Passing them on resume lets the user change
|
|
397
397
|
// PRLL_CODEX_* env vars and have the bridge pick them up on the next
|
|
398
398
|
// restart instead of being stuck on the values baked into the persisted
|
|
399
|
-
// thread. Sandbox / approval values are normalised to the
|
|
400
|
-
// the protocol expects (`
|
|
401
|
-
//
|
|
399
|
+
// thread. Sandbox / approval values are normalised to the kebab-case
|
|
400
|
+
// enum form the protocol expects (`workspace-write`, `on-request`, etc.
|
|
401
|
+
// — serde rename_all kebab-case on the Rust enum); users can supply
|
|
402
|
+
// either kebab or camel and we always emit kebab.
|
|
402
403
|
const commonParams: Record<string, unknown> = {
|
|
403
404
|
approvalPolicy: normalizeApprovalPolicy(this.opts.approvalPolicy),
|
|
404
|
-
sandbox: normalizeSandbox(this.opts.sandbox),
|
|
405
405
|
};
|
|
406
|
+
// Only override sandbox when the user explicitly set PRLL_CODEX_SANDBOX.
|
|
407
|
+
// Otherwise let codex read `sandbox_mode` from ~/.codex/config.toml — the
|
|
408
|
+
// user's own configuration is authoritative for a self-hosted bridge,
|
|
409
|
+
// and codex 0.120.0's workspace-write mode has a write_stdin bug that
|
|
410
|
+
// our previous `workspace-write` default tripped for users whose config
|
|
411
|
+
// had danger-full-access.
|
|
412
|
+
if (this.opts.sandbox) {
|
|
413
|
+
commonParams.sandbox = normalizeSandbox(this.opts.sandbox);
|
|
414
|
+
}
|
|
406
415
|
if (this.opts.model) commonParams.model = this.opts.model;
|
|
407
416
|
if (this.opts.reasoningEffort) {
|
|
408
417
|
// The app-server JSON-RPC surface uses camelCase for overrides —
|
package/src/index.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
import * as os from "node:os";
|
|
4
|
-
import { ParallAgentGateway } from "@parall/agent-core";
|
|
4
|
+
import { ParallAgentGateway, parseShutdownDeadlineMs } from "@parall/agent-core";
|
|
5
5
|
import { ParallClient, ParallWs } from "@parall/sdk";
|
|
6
6
|
import {
|
|
7
7
|
buildCodexRuntimeKey,
|
|
@@ -83,6 +83,7 @@ async function main() {
|
|
|
83
83
|
},
|
|
84
84
|
dispatchAdapter: adapter,
|
|
85
85
|
log,
|
|
86
|
+
shutdownDeadlineMs: parseShutdownDeadlineMs(process.env.PRLL_SHUTDOWN_DEADLINE_MS),
|
|
86
87
|
stepIdFilePathForSession: (sessionKey) => stepIdFilePathForSession(config.stateDir, sessionKey),
|
|
87
88
|
});
|
|
88
89
|
|