@christiandoxa/prodex 0.186.0 → 0.188.0
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/README.md +8 -3
- package/lib/codex-shim.cjs +84 -1
- package/package.json +7 -7
- package/prodex +86 -24
package/README.md
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
Use multiple Codex accounts and supported provider backends from one command line. OpenAI/Codex profiles get quota-aware routing and auto-rotation; provider adapters let `prodex s` launch the Codex front end against Gemini, Anthropic, Copilot, DeepSeek, and local OpenAI-compatible servers.
|
|
6
6
|
|
|
7
|
-

|
|
7
|
+

|
|
8
8
|
|
|
9
9
|
## Contents
|
|
10
10
|
|
|
@@ -567,8 +567,11 @@ prodex run
|
|
|
567
567
|
prodex run --profile main
|
|
568
568
|
prodex run --dry-run
|
|
569
569
|
prodex exec "review this repo"
|
|
570
|
+
prodex delete 019c9e3d-45a0-7ad0-a6ee-b194ac2d44f9
|
|
570
571
|
```
|
|
571
572
|
|
|
573
|
+
Codex-owned TUI commands such as `/usage`, `/goal`, `/import`, and `/delete` stay upstream Codex behavior. Prodex preserves their request metadata through the proxy and does not add a competing command surface. The CLI form `prodex delete <session>` passes through to Codex and, after a successful delete, prunes matching Prodex session affinity metadata.
|
|
574
|
+
|
|
572
575
|
</details>
|
|
573
576
|
|
|
574
577
|
<details>
|
|
@@ -788,7 +791,7 @@ Gemini CLI compatibility helpers accept inline `gemini_memory` / `gemini_policy`
|
|
|
788
791
|
|
|
789
792
|
Before Codex launches, the Gemini provider projects Gemini CLI settings and extension surfaces into the active `CODEX_HOME`: system/global/project `mcpServers` and extension `mcpServers` become generated Codex `[mcp_servers.gemini_*]` entries with settings taking precedence over extension servers of the same Gemini name; system/global/project and extension command hooks are merged into `hooks.json` for Codex `/hooks` review; `~/.gemini/commands`, project `.gemini/commands`, and extension `commands/*.toml` become Codex custom prompts with Gemini command aliases preserved where possible; extension `skills/*/SKILL.md` are copied into generated Codex skill folders under `.agents/skills`; and extension `agents/*.md` become generated Codex custom agents under `agents/*.toml`. Built-in `/prompts:gemini-refresh`, `/prompts:gemini-memory-show`, `/prompts:gemini-memory-refresh`, `/prompts:gemini-memory-inbox`, `/prompts:gemini-remember`, `/prompts:gemini-checkpoint-create`, `/prompts:gemini-checkpoint-restore`, `/prompts:gemini-checkpoint-export`, and `/prompts:gemini-rewind` cover reload/admin, memory, and checkpoint workflows. Generated helper scripts in `CODEX_HOME/bin` include `prodex-gemini-refresh`, `prodex-gemini-checkpoint-create`, and `prodex-gemini-checkpoint-restore`. Set `PRODEX_GEMINI_EXTENSIONS=none` or an allow-list of extension names to control extension loading, `PRODEX_GEMINI_EXTENSION_DIRS` to add extension roots, or `PRODEX_GEMINI_DISABLE_CLI_COMPAT=1` to skip the launch-time Codex surface projection.
|
|
790
793
|
|
|
791
|
-
Gemini Live realtime websocket
|
|
794
|
+
Gemini Live realtime websocket translation remains available for compatible callers and credentialed adapter tests, mapping Codex audio, transcript, text, function-call, function-result, interruption, cancellation, housekeeping, and turn-completion events to and from Gemini `BidiGenerateContent`; one Gemini auth/profile is selected before upgrade and remains fixed for the session. Codex 0.140.0 removed the upstream TUI voice controls, so this bridge should not be treated as a normal Codex TUI voice feature. `PRODEX_GEMINI_LIVE_MODEL` overrides the default Live model, while `PRODEX_GEMINI_LIVE_URL` is available for a custom or test Live endpoint. `prodex doctor --runtime` recognizes provider bridge and Gemini markers such as `local_rewrite_provider_model_fallback`, `local_rewrite_gemini_quota_rotate`, `local_rewrite_gemini_invalid_stream_retry`, and `local_rewrite_gemini_live_error`.
|
|
792
795
|
|
|
793
796
|
Run `npm run test:gemini-schema` after changing Gemini request, response, SSE, semantic compact, exact-output, tool-schema, or Live translation. Run `PRODEX_LIVE_GEMINI=1 npm run test:gemini-live` for a credentialed end-to-end Gemini adapter smoke request; set `PRODEX_BIN` or `PRODEX_LIVE_GEMINI_MODEL` to override the binary or model. Add `PRODEX_LIVE_GEMINI_EXTENDED=1` for command-output-only, file edit, `apply_patch`, reference-repo clone/inspection, optional-tool update discipline, semantic compact, and explicit `exec resume` checks. Add `PRODEX_LIVE_GEMINI_MCP=1` and/or `PRODEX_LIVE_GEMINI_MULTIMODAL=1` when the local environment should also exercise MCP and image-input paths.
|
|
794
797
|
|
|
@@ -964,7 +967,9 @@ On Unix-like systems, this is usually:
|
|
|
964
967
|
~/.codex
|
|
965
968
|
```
|
|
966
969
|
|
|
967
|
-
In practice, profile `history.jsonl`, `sessions`, `archived_sessions`, `config.toml`, `managed_config.toml`, `environments.toml`, plugins, skills, app-server plugin state, memory-extension state, remote-control enrollment, and Codex runtime SQLite files such as `state_*`, `goals_*`, `logs_*`, and `memories_*` link to the same Codex home that direct Codex uses.
|
|
970
|
+
In practice, profile `history.jsonl`, `sessions`, `archived_sessions`, `config.toml`, `managed_config.toml`, `environments.toml`, `.credentials.json`, plugins, skills, app-server plugin state, memory-extension state, remote-control enrollment, and Codex runtime SQLite files such as `state_*`, `goals_*`, `logs_*`, and `memories_*` link to the same Codex home that direct Codex uses.
|
|
971
|
+
|
|
972
|
+
Codex 0.140.0 defaults CLI auth credentials to the file store, so managed Prodex profiles continue to keep `auth.json` isolated per profile, including OpenAI, API-key, and Bedrock API-key auth JSON. MCP OAuth defaults to Codex `auto`; when it falls back to the file store, `.credentials.json` is shared with direct Codex. OS keyring-backed MCP OAuth credentials remain Codex/OS-owned and are not part of Prodex profile export bundles.
|
|
968
973
|
|
|
969
974
|
Codex cloud-managed config bundle caches are identity/account scoped and remain profile-local. System-level Codex requirements and managed config files remain owned by upstream Codex and the operating system.
|
|
970
975
|
|
package/lib/codex-shim.cjs
CHANGED
|
@@ -8,6 +8,45 @@ const { createRequire } = require("node:module");
|
|
|
8
8
|
|
|
9
9
|
const requireFromHere = createRequire(__filename);
|
|
10
10
|
|
|
11
|
+
const PLATFORM_TARGETS = {
|
|
12
|
+
linux: {
|
|
13
|
+
x64: {
|
|
14
|
+
packageName: "@openai/codex-linux-x64",
|
|
15
|
+
targetTriple: "x86_64-unknown-linux-musl",
|
|
16
|
+
binaryFileName: "codex",
|
|
17
|
+
},
|
|
18
|
+
arm64: {
|
|
19
|
+
packageName: "@openai/codex-linux-arm64",
|
|
20
|
+
targetTriple: "aarch64-unknown-linux-musl",
|
|
21
|
+
binaryFileName: "codex",
|
|
22
|
+
},
|
|
23
|
+
},
|
|
24
|
+
darwin: {
|
|
25
|
+
x64: {
|
|
26
|
+
packageName: "@openai/codex-darwin-x64",
|
|
27
|
+
targetTriple: "x86_64-apple-darwin",
|
|
28
|
+
binaryFileName: "codex",
|
|
29
|
+
},
|
|
30
|
+
arm64: {
|
|
31
|
+
packageName: "@openai/codex-darwin-arm64",
|
|
32
|
+
targetTriple: "aarch64-apple-darwin",
|
|
33
|
+
binaryFileName: "codex",
|
|
34
|
+
},
|
|
35
|
+
},
|
|
36
|
+
win32: {
|
|
37
|
+
x64: {
|
|
38
|
+
packageName: "@openai/codex-win32-x64",
|
|
39
|
+
targetTriple: "x86_64-pc-windows-msvc",
|
|
40
|
+
binaryFileName: "codex.exe",
|
|
41
|
+
},
|
|
42
|
+
arm64: {
|
|
43
|
+
packageName: "@openai/codex-win32-arm64",
|
|
44
|
+
targetTriple: "aarch64-pc-windows-msvc",
|
|
45
|
+
binaryFileName: "codex.exe",
|
|
46
|
+
},
|
|
47
|
+
},
|
|
48
|
+
};
|
|
49
|
+
|
|
11
50
|
function resolveCodexBin() {
|
|
12
51
|
let packageJsonPath;
|
|
13
52
|
try {
|
|
@@ -32,14 +71,58 @@ function resolveCodexBin() {
|
|
|
32
71
|
return path.resolve(path.dirname(packageJsonPath), "bin", "codex.js");
|
|
33
72
|
}
|
|
34
73
|
|
|
74
|
+
function explainBundledNativeCodexPermissionIssue() {
|
|
75
|
+
const platformTarget = PLATFORM_TARGETS[process.platform]?.[process.arch];
|
|
76
|
+
if (!platformTarget || process.platform === "win32") {
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
let platformPackageJsonPath;
|
|
81
|
+
try {
|
|
82
|
+
platformPackageJsonPath = requireFromHere.resolve(`${platformTarget.packageName}/package.json`);
|
|
83
|
+
} catch {
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
const nativeBinaryPath = path.join(
|
|
88
|
+
path.dirname(platformPackageJsonPath),
|
|
89
|
+
"vendor",
|
|
90
|
+
platformTarget.targetTriple,
|
|
91
|
+
"bin",
|
|
92
|
+
platformTarget.binaryFileName,
|
|
93
|
+
);
|
|
94
|
+
if (!fs.existsSync(nativeBinaryPath)) {
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
try {
|
|
98
|
+
fs.accessSync(nativeBinaryPath, fs.constants.X_OK);
|
|
99
|
+
} catch {
|
|
100
|
+
process.stderr.write(
|
|
101
|
+
[
|
|
102
|
+
`Bundled Codex native binary is not executable: ${nativeBinaryPath}`,
|
|
103
|
+
"Reinstall @christiandoxa/prodex with optional dependencies enabled, or set PRODEX_CODEX_BIN to an existing Codex CLI.",
|
|
104
|
+
"",
|
|
105
|
+
].join("\n"),
|
|
106
|
+
);
|
|
107
|
+
process.exit(126);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
35
111
|
const codexBin = resolveCodexBin();
|
|
112
|
+
explainBundledNativeCodexPermissionIssue();
|
|
36
113
|
const child = spawn(process.execPath, [codexBin, ...process.argv.slice(2)], {
|
|
37
114
|
env: process.env,
|
|
38
115
|
stdio: "inherit",
|
|
39
116
|
});
|
|
40
117
|
|
|
41
118
|
child.on("error", (error) => {
|
|
42
|
-
|
|
119
|
+
if (error && error.code === "EACCES") {
|
|
120
|
+
process.stderr.write(
|
|
121
|
+
`${error.message}\nSet PRODEX_CODEX_BIN to an existing Codex CLI or reinstall @christiandoxa/prodex.\n`,
|
|
122
|
+
);
|
|
123
|
+
} else {
|
|
124
|
+
process.stderr.write(`${error.message}\n`);
|
|
125
|
+
}
|
|
43
126
|
process.exit(1);
|
|
44
127
|
});
|
|
45
128
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@christiandoxa/prodex",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.188.0",
|
|
4
4
|
"description": "Safe multi-account auto-rotate for Codex CLI with isolated CODEX_HOME profiles",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"bin": {
|
|
@@ -16,12 +16,12 @@
|
|
|
16
16
|
"@openai/codex": "latest"
|
|
17
17
|
},
|
|
18
18
|
"optionalDependencies": {
|
|
19
|
-
"@christiandoxa/prodex-linux-x64": "0.
|
|
20
|
-
"@christiandoxa/prodex-linux-arm64": "0.
|
|
21
|
-
"@christiandoxa/prodex-darwin-x64": "0.
|
|
22
|
-
"@christiandoxa/prodex-darwin-arm64": "0.
|
|
23
|
-
"@christiandoxa/prodex-win32-x64": "0.
|
|
24
|
-
"@christiandoxa/prodex-win32-arm64": "0.
|
|
19
|
+
"@christiandoxa/prodex-linux-x64": "0.188.0",
|
|
20
|
+
"@christiandoxa/prodex-linux-arm64": "0.188.0",
|
|
21
|
+
"@christiandoxa/prodex-darwin-x64": "0.188.0",
|
|
22
|
+
"@christiandoxa/prodex-darwin-arm64": "0.188.0",
|
|
23
|
+
"@christiandoxa/prodex-win32-x64": "0.188.0",
|
|
24
|
+
"@christiandoxa/prodex-win32-arm64": "0.188.0"
|
|
25
25
|
},
|
|
26
26
|
"engines": {
|
|
27
27
|
"node": ">=18"
|
package/prodex
CHANGED
|
@@ -46,6 +46,60 @@ const packageRoot = path.dirname(requireFromHere.resolve("./package.json"));
|
|
|
46
46
|
const codexShimPath = path.join(packageRoot, "lib", "codex-shim.cjs");
|
|
47
47
|
const platformPackage = PLATFORM_PACKAGES[process.platform]?.[process.arch];
|
|
48
48
|
|
|
49
|
+
function candidateCommandNames(command) {
|
|
50
|
+
if (process.platform !== "win32") {
|
|
51
|
+
return [command];
|
|
52
|
+
}
|
|
53
|
+
const extensions = (process.env.PATHEXT || ".COM;.EXE;.BAT;.CMD")
|
|
54
|
+
.split(";")
|
|
55
|
+
.filter(Boolean);
|
|
56
|
+
const lowerCommand = command.toLowerCase();
|
|
57
|
+
if (extensions.some((extension) => lowerCommand.endsWith(extension.toLowerCase()))) {
|
|
58
|
+
return [command];
|
|
59
|
+
}
|
|
60
|
+
return [command, ...extensions.map((extension) => `${command}${extension.toLowerCase()}`)];
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function isExecutableFile(filePath) {
|
|
64
|
+
try {
|
|
65
|
+
const stats = fs.statSync(filePath);
|
|
66
|
+
if (!stats.isFile()) {
|
|
67
|
+
return false;
|
|
68
|
+
}
|
|
69
|
+
fs.accessSync(filePath, fs.constants.X_OK);
|
|
70
|
+
return true;
|
|
71
|
+
} catch {
|
|
72
|
+
return false;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function resolveCommandFromPath(command, pathValue) {
|
|
77
|
+
const hasPathSeparator = command.includes("/") || command.includes("\\");
|
|
78
|
+
if (path.isAbsolute(command) || hasPathSeparator) {
|
|
79
|
+
return isExecutableFile(command) ? command : null;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
for (const entry of (pathValue || "").split(path.delimiter)) {
|
|
83
|
+
if (!entry) {
|
|
84
|
+
continue;
|
|
85
|
+
}
|
|
86
|
+
for (const candidateName of candidateCommandNames(command)) {
|
|
87
|
+
const candidate = path.join(entry, candidateName);
|
|
88
|
+
if (isExecutableFile(candidate)) {
|
|
89
|
+
return candidate;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
return null;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
function resolveExternalCodexBin() {
|
|
97
|
+
if (process.env.PRODEX_CODEX_BIN) {
|
|
98
|
+
return process.env.PRODEX_CODEX_BIN;
|
|
99
|
+
}
|
|
100
|
+
return resolveCommandFromPath("codex", process.env.PATH || "");
|
|
101
|
+
}
|
|
102
|
+
|
|
49
103
|
if (!platformPackage) {
|
|
50
104
|
process.stderr.write(
|
|
51
105
|
`Unsupported platform for @christiandoxa/prodex: ${process.platform} ${process.arch}\n`,
|
|
@@ -75,34 +129,42 @@ if (!fs.existsSync(nativeBinaryPath)) {
|
|
|
75
129
|
process.exit(1);
|
|
76
130
|
}
|
|
77
131
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
fs.writeFileSync(
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
);
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
}
|
|
99
|
-
|
|
132
|
+
let cleanup = () => {};
|
|
133
|
+
let codexBin = resolveExternalCodexBin();
|
|
134
|
+
let childPath = process.env.PATH ?? "";
|
|
135
|
+
|
|
136
|
+
if (!codexBin) {
|
|
137
|
+
const shimDir = fs.mkdtempSync(path.join(os.tmpdir(), "prodex-codex-"));
|
|
138
|
+
const codexCmd = path.join(shimDir, "codex");
|
|
139
|
+
const codexCmdWindows = path.join(shimDir, "codex.cmd");
|
|
140
|
+
|
|
141
|
+
fs.writeFileSync(
|
|
142
|
+
codexCmd,
|
|
143
|
+
`#!/bin/sh\nexec node ${JSON.stringify(codexShimPath)} "$@"\n`,
|
|
144
|
+
{ mode: 0o755 },
|
|
145
|
+
);
|
|
146
|
+
fs.writeFileSync(
|
|
147
|
+
codexCmdWindows,
|
|
148
|
+
`@echo off\r\nnode ${JSON.stringify(codexShimPath)} %*\r\n`,
|
|
149
|
+
{ mode: 0o755 },
|
|
150
|
+
);
|
|
151
|
+
|
|
152
|
+
childPath = `${shimDir}${path.delimiter}${childPath}`;
|
|
153
|
+
codexBin = "codex";
|
|
154
|
+
cleanup = () => {
|
|
155
|
+
try {
|
|
156
|
+
fs.rmSync(shimDir, { recursive: true, force: true });
|
|
157
|
+
} catch {
|
|
158
|
+
// Best-effort cleanup only.
|
|
159
|
+
}
|
|
160
|
+
};
|
|
161
|
+
}
|
|
100
162
|
|
|
101
163
|
const child = spawn(nativeBinaryPath, process.argv.slice(2), {
|
|
102
164
|
env: {
|
|
103
165
|
...process.env,
|
|
104
|
-
PATH:
|
|
105
|
-
PRODEX_CODEX_BIN:
|
|
166
|
+
PATH: childPath,
|
|
167
|
+
PRODEX_CODEX_BIN: codexBin,
|
|
106
168
|
npm_package_name: packageJson.name,
|
|
107
169
|
npm_package_version: packageJson.version,
|
|
108
170
|
},
|