@childclm/codexx 0.1.2 → 0.1.4

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/bin/codexx.js CHANGED
File without changes
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@childclm/codexx",
3
- "version": "0.1.2",
3
+ "version": "0.1.4",
4
4
  "description": "Cross-platform launcher for Codex CLI with project-scoped session trees and provider switching.",
5
5
  "main": "src/index.js",
6
6
  "bin": {
package/src/index.js CHANGED
@@ -144,50 +144,46 @@ function resolveWindowsCodexCandidates() {
144
144
  const candidates = [];
145
145
  const appData = process.env.APPDATA || path.join(HOME, "AppData", "Roaming");
146
146
  const npmRoot = path.join(appData, "npm");
147
- const localShims = [
148
- path.join(npmRoot, "codex.exe"),
149
- path.join(npmRoot, "codex.cmd"),
150
- path.join(npmRoot, "codex"),
151
- ];
152
- candidates.push(...localShims);
153
147
 
154
- const finder = spawnSync("where", ["codex.exe"], { encoding: "utf8" });
155
- const foundExe = (finder.stdout || "")
156
- .trim()
157
- .split(/\r?\n/)
158
- .filter(Boolean);
159
- candidates.push(...foundExe);
160
-
161
- const foundCmd = spawnSync("where", ["codex.cmd"], { encoding: "utf8" });
162
- const cmdHits = (foundCmd.stdout || "")
163
- .trim()
164
- .split(/\r?\n/)
165
- .filter(Boolean);
166
- candidates.push(...cmdHits);
167
-
168
- const directVendorParent = path.join(
148
+ const nativePackageScope = path.join(
169
149
  npmRoot,
170
150
  "node_modules",
171
151
  "@openai",
172
152
  "codex",
173
153
  "node_modules",
154
+ "@openai",
174
155
  );
175
- if (fileExists(directVendorParent)) {
156
+ if (fileExists(nativePackageScope)) {
176
157
  try {
177
- const dirs = fs.readdirSync(directVendorParent);
158
+ const dirs = fs.readdirSync(nativePackageScope);
178
159
  for (const dir of dirs) {
179
- if (!dir.includes("codex-win32-")) continue;
180
- const vendorRoot = path.join(directVendorParent, dir, "vendor");
160
+ if (!dir.startsWith("codex-win32-")) continue;
161
+ const vendorRoot = path.join(nativePackageScope, dir, "vendor");
181
162
  if (!fileExists(vendorRoot)) continue;
182
163
  const vendorDirs = fs.readdirSync(vendorRoot);
183
164
  for (const vendorDir of vendorDirs) {
184
- const exePath = path.join(vendorRoot, vendorDir, "bin", "codex.exe");
185
- candidates.push(exePath);
165
+ candidates.push(path.join(vendorRoot, vendorDir, "bin", "codex.exe"));
186
166
  }
187
167
  }
188
168
  } catch {}
189
169
  }
190
170
 
171
+ candidates.push(path.join(npmRoot, "codex.cmd"), path.join(npmRoot, "codex"));
172
+
173
+ const finder = spawnSync("where", ["codex.exe"], { encoding: "utf8" });
174
+ const foundExe = (finder.stdout || "")
175
+ .trim()
176
+ .split(/\r?\n/)
177
+ .filter(Boolean);
178
+ candidates.push(...foundExe);
179
+
180
+ const foundCmd = spawnSync("where", ["codex.cmd"], { encoding: "utf8" });
181
+ const cmdHits = (foundCmd.stdout || "")
182
+ .trim()
183
+ .split(/\r?\n/)
184
+ .filter(Boolean);
185
+ candidates.push(...cmdHits);
186
+
191
187
  return [...new Set(candidates)].filter(Boolean);
192
188
  }
193
189
 
@@ -268,6 +264,30 @@ function clearScreen() {
268
264
  process.stdout.write("\x1b[2J\x1b[0f");
269
265
  }
270
266
 
267
+ function resetTerminalForChild() {
268
+ process.stdout.write("\x1b[0m\x1b[2J\x1b[3J\x1b[H");
269
+ }
270
+
271
+ function suspendTerminalForChild(rl) {
272
+ setRawMode(false);
273
+ if (rl && typeof rl.pause === "function") {
274
+ rl.pause();
275
+ }
276
+ if (typeof process.stdin.pause === "function") {
277
+ process.stdin.pause();
278
+ }
279
+ }
280
+
281
+ function resumeTerminalAfterChild(rl) {
282
+ if (typeof process.stdin.resume === "function") {
283
+ process.stdin.resume();
284
+ }
285
+ if (rl && typeof rl.resume === "function") {
286
+ rl.resume();
287
+ }
288
+ setRawMode(true);
289
+ }
290
+
271
291
  function prompt(rl, text) {
272
292
  return new Promise((resolve) => rl.question(text, resolve));
273
293
  }
@@ -472,23 +492,63 @@ function hasOfficialSession(sessionId, projectRoot) {
472
492
 
473
493
  function buildCodexEnv(provider) {
474
494
  return {
495
+ CODEXX_PROVIDER_API_KEY: provider.apiKey,
475
496
  OPENAI_API_KEY: provider.apiKey,
476
497
  OPENAI_BASE_URL: provider.baseUrl || process.env.OPENAI_BASE_URL,
477
498
  };
478
499
  }
479
500
 
480
- function runCodex(args, env, cwd = process.cwd()) {
501
+ function tomlString(value) {
502
+ return JSON.stringify(String(value));
503
+ }
504
+
505
+ function buildCodexProviderArgs(provider) {
506
+ if (!provider || !provider.baseUrl) return [];
507
+
508
+ const providerKey = "codexx";
509
+ const providerDisplayName = provider.modelProvider || "OpenAI";
510
+ return [
511
+ "-c",
512
+ `model_provider=${tomlString(providerKey)}`,
513
+ "-c",
514
+ `model_providers.${providerKey}.name=${tomlString(providerDisplayName)}`,
515
+ "-c",
516
+ `model_providers.${providerKey}.base_url=${tomlString(provider.baseUrl)}`,
517
+ "-c",
518
+ `model_providers.${providerKey}.wire_api=${tomlString("responses")}`,
519
+ "-c",
520
+ `model_providers.${providerKey}.requires_openai_auth=false`,
521
+ "-c",
522
+ `model_providers.${providerKey}.env_key=${tomlString("CODEXX_PROVIDER_API_KEY")}`,
523
+ ];
524
+ }
525
+
526
+ function runCodex(args, env, cwd = process.cwd(), rl = null, provider = null) {
481
527
  return new Promise((resolve, reject) => {
482
528
  const cmd = resolveCodexCommand();
483
- const launch = buildCodexLaunch(cmd, args);
484
- const child = spawn(launch.command, launch.args, {
485
- cwd,
486
- stdio: "inherit",
487
- env: { ...process.env, ...env },
488
- shell: launch.shell,
489
- });
490
- child.on("error", reject);
491
- child.on("exit", (code) => resolve(code ?? 0));
529
+ const launch = buildCodexLaunch(
530
+ cmd,
531
+ [...buildCodexProviderArgs(provider), ...args],
532
+ );
533
+ const settle = (done, value) => {
534
+ resumeTerminalAfterChild(rl);
535
+ done(value);
536
+ };
537
+
538
+ try {
539
+ suspendTerminalForChild(rl);
540
+ resetTerminalForChild();
541
+ const child = spawn(launch.command, launch.args, {
542
+ cwd,
543
+ stdio: "inherit",
544
+ env: { ...process.env, ...env },
545
+ shell: launch.shell,
546
+ });
547
+ child.on("error", (error) => settle(reject, error));
548
+ child.on("exit", (code) => settle(resolve, code ?? 0));
549
+ } catch (error) {
550
+ settle(reject, error);
551
+ }
492
552
  });
493
553
  }
494
554
 
@@ -848,10 +908,8 @@ async function launchNewSession(rl, cwd = process.cwd()) {
848
908
  console.log(`准备启动新主线会话 · provider=${provider.name}`);
849
909
  await pause(rl, "按回车进入 Codex...");
850
910
 
851
- setRawMode(false);
852
911
  const startedAtMs = Date.now();
853
- await runCodex([], buildCodexEnv(provider), projectState.root);
854
- setRawMode(true);
912
+ await runCodex([], buildCodexEnv(provider), projectState.root, rl, provider);
855
913
 
856
914
  const linkedSessionId = await maybeLinkLatestSession(
857
915
  projectState.root,
@@ -939,9 +997,13 @@ async function resumeSpecificSession(rl, sessionId, cwd = process.cwd()) {
939
997
  console.log(`provider=${provider.name}`);
940
998
  await pause(rl, "按回车进入 Codex...");
941
999
 
942
- setRawMode(false);
943
- await runCodex(["resume", sessionId], buildCodexEnv(provider), projectState.root);
944
- setRawMode(true);
1000
+ await runCodex(
1001
+ ["resume", sessionId],
1002
+ buildCodexEnv(provider),
1003
+ projectState.root,
1004
+ rl,
1005
+ provider,
1006
+ );
945
1007
  }
946
1008
 
947
1009
  async function pickSessionAndResume(rl, cwd = process.cwd()) {
@@ -1087,10 +1149,14 @@ async function forkFromSession(rl, source, cwd = process.cwd()) {
1087
1149
  console.log(`provider=${provider.name}`);
1088
1150
  await pause(rl, "按回车进入 Codex...");
1089
1151
 
1090
- setRawMode(false);
1091
1152
  const startedAtMs = Date.now();
1092
- await runCodex(["fork", source.sessionId], buildCodexEnv(provider), projectState.root);
1093
- setRawMode(true);
1153
+ await runCodex(
1154
+ ["fork", source.sessionId],
1155
+ buildCodexEnv(provider),
1156
+ projectState.root,
1157
+ rl,
1158
+ provider,
1159
+ );
1094
1160
 
1095
1161
  const linkedSessionId = await maybeLinkLatestSession(
1096
1162
  projectState.root,
@@ -1143,13 +1209,12 @@ async function deleteSpecificSession(rl, target, cwd = process.cwd()) {
1143
1209
  };
1144
1210
  }
1145
1211
 
1146
- setRawMode(false);
1147
1212
  const exitCode = await runCodex(
1148
1213
  ["delete", "--force", target.sessionId],
1149
1214
  {},
1150
1215
  projectState.root,
1216
+ rl,
1151
1217
  );
1152
- setRawMode(true);
1153
1218
 
1154
1219
  const stillExists = hasOfficialSession(target.sessionId, projectState.root);
1155
1220
  if (exitCode !== 0 || stillExists) {