@childclm/codexx 0.1.1 → 0.1.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.
Files changed (3) hide show
  1. package/bin/codexx.js +0 -0
  2. package/package.json +1 -1
  3. package/src/index.js +104 -121
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.1",
3
+ "version": "0.1.3",
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
@@ -137,38 +137,39 @@ function saveProjectSessionsMeta(data, cwd = process.cwd()) {
137
137
 
138
138
  function detectCodex() {
139
139
  const settings = loadSettings();
140
- return settings.codexCommand || "codex";
140
+ return String(settings.codexCommand || "codex").trim().replace(/^"(.*)"$/, "$1");
141
141
  }
142
142
 
143
143
  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
-
154
- const openaiCodexRoot = path.join(
147
+
148
+ const nativePackageScope = path.join(
155
149
  npmRoot,
156
150
  "node_modules",
157
151
  "@openai",
158
152
  "codex",
159
153
  "node_modules",
154
+ "@openai",
160
155
  );
161
- if (fileExists(openaiCodexRoot)) {
156
+ if (fileExists(nativePackageScope)) {
162
157
  try {
163
- const children = fs.readdirSync(openaiCodexRoot);
164
- for (const child of children) {
165
- if (!child.startsWith("@openai\\codex-win32-") && !child.startsWith("@openai/codex-win32-")) {
166
- continue;
158
+ const dirs = fs.readdirSync(nativePackageScope);
159
+ for (const dir of dirs) {
160
+ if (!dir.startsWith("codex-win32-")) continue;
161
+ const vendorRoot = path.join(nativePackageScope, dir, "vendor");
162
+ if (!fileExists(vendorRoot)) continue;
163
+ const vendorDirs = fs.readdirSync(vendorRoot);
164
+ for (const vendorDir of vendorDirs) {
165
+ candidates.push(path.join(vendorRoot, vendorDir, "bin", "codex.exe"));
167
166
  }
168
167
  }
169
168
  } catch {}
170
169
  }
171
170
 
171
+ candidates.push(path.join(npmRoot, "codex.cmd"), path.join(npmRoot, "codex"));
172
+
172
173
  const finder = spawnSync("where", ["codex.exe"], { encoding: "utf8" });
173
174
  const foundExe = (finder.stdout || "")
174
175
  .trim()
@@ -183,93 +184,6 @@ function resolveWindowsCodexCandidates() {
183
184
  .filter(Boolean);
184
185
  candidates.push(...cmdHits);
185
186
 
186
- const vendorGlobRoot = path.join(
187
- npmRoot,
188
- "node_modules",
189
- "@openai",
190
- "codex",
191
- "node_modules",
192
- );
193
- if (fileExists(vendorGlobRoot)) {
194
- try {
195
- const dirs = fs.readdirSync(vendorGlobRoot);
196
- for (const dir of dirs) {
197
- if (!dir.startsWith("@openai")) continue;
198
- }
199
- } catch {}
200
- }
201
-
202
- const packageRoot = path.join(
203
- npmRoot,
204
- "node_modules",
205
- "@openai",
206
- "codex",
207
- "node_modules",
208
- );
209
- if (fileExists(packageRoot)) {
210
- try {
211
- const entries = fs.readdirSync(packageRoot);
212
- for (const entry of entries) {
213
- if (!entry.startsWith("@openai")) continue;
214
- }
215
- } catch {}
216
- }
217
-
218
- const openaiNodeModules = path.join(
219
- npmRoot,
220
- "node_modules",
221
- "@openai",
222
- "codex",
223
- "node_modules",
224
- );
225
- if (fileExists(openaiNodeModules)) {
226
- try {
227
- const entries = fs.readdirSync(openaiNodeModules);
228
- for (const entry of entries) {
229
- if (!entry.startsWith("@openai")) continue;
230
- }
231
- } catch {}
232
- }
233
-
234
- const codexNodeModules = path.join(
235
- npmRoot,
236
- "node_modules",
237
- "@openai",
238
- "codex",
239
- "node_modules",
240
- );
241
- if (fileExists(codexNodeModules)) {
242
- try {
243
- const entries = fs.readdirSync(codexNodeModules);
244
- for (const entry of entries) {
245
- if (!entry.startsWith("@openai")) continue;
246
- }
247
- } catch {}
248
- }
249
-
250
- const directVendorParent = path.join(
251
- npmRoot,
252
- "node_modules",
253
- "@openai",
254
- "codex",
255
- "node_modules",
256
- );
257
- if (fileExists(directVendorParent)) {
258
- try {
259
- const dirs = fs.readdirSync(directVendorParent);
260
- for (const dir of dirs) {
261
- if (!dir.includes("codex-win32-")) continue;
262
- const vendorRoot = path.join(directVendorParent, dir, "vendor");
263
- if (!fileExists(vendorRoot)) continue;
264
- const vendorDirs = fs.readdirSync(vendorRoot);
265
- for (const vendorDir of vendorDirs) {
266
- const exePath = path.join(vendorRoot, vendorDir, "bin", "codex.exe");
267
- candidates.push(exePath);
268
- }
269
- }
270
- } catch {}
271
- }
272
-
273
187
  return [...new Set(candidates)].filter(Boolean);
274
188
  }
275
189
 
@@ -306,10 +220,74 @@ function codexPath() {
306
220
  return resolveCodexCommand();
307
221
  }
308
222
 
223
+ function buildCodexLaunch(cmd, args) {
224
+ const ext = path.extname(cmd).toLowerCase();
225
+ if (process.platform !== "win32") {
226
+ return {
227
+ command: cmd,
228
+ args,
229
+ shell: false,
230
+ };
231
+ }
232
+
233
+ if (ext === ".ps1") {
234
+ return {
235
+ command: "powershell.exe",
236
+ args: [
237
+ "-NoProfile",
238
+ "-ExecutionPolicy",
239
+ "Bypass",
240
+ "-File",
241
+ cmd,
242
+ ...args,
243
+ ],
244
+ shell: false,
245
+ };
246
+ }
247
+
248
+ if (ext === ".cmd" || ext === ".bat") {
249
+ return {
250
+ command: "cmd.exe",
251
+ args: ["/d", "/s", "/c", cmd, ...args],
252
+ shell: false,
253
+ };
254
+ }
255
+
256
+ return {
257
+ command: cmd,
258
+ args,
259
+ shell: false,
260
+ };
261
+ }
262
+
309
263
  function clearScreen() {
310
264
  process.stdout.write("\x1b[2J\x1b[0f");
311
265
  }
312
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
+
313
291
  function prompt(rl, text) {
314
292
  return new Promise((resolve) => rl.question(text, resolve));
315
293
  }
@@ -519,17 +497,29 @@ function buildCodexEnv(provider) {
519
497
  };
520
498
  }
521
499
 
522
- function runCodex(args, env, cwd = process.cwd()) {
500
+ function runCodex(args, env, cwd = process.cwd(), rl = null) {
523
501
  return new Promise((resolve, reject) => {
524
502
  const cmd = resolveCodexCommand();
525
- const child = spawn(cmd, args, {
526
- cwd,
527
- stdio: "inherit",
528
- env: { ...process.env, ...env },
529
- shell: false,
530
- });
531
- child.on("error", reject);
532
- child.on("exit", (code) => resolve(code ?? 0));
503
+ const launch = buildCodexLaunch(cmd, args);
504
+ const settle = (done, value) => {
505
+ resumeTerminalAfterChild(rl);
506
+ done(value);
507
+ };
508
+
509
+ try {
510
+ suspendTerminalForChild(rl);
511
+ resetTerminalForChild();
512
+ const child = spawn(launch.command, launch.args, {
513
+ cwd,
514
+ stdio: "inherit",
515
+ env: { ...process.env, ...env },
516
+ shell: launch.shell,
517
+ });
518
+ child.on("error", (error) => settle(reject, error));
519
+ child.on("exit", (code) => settle(resolve, code ?? 0));
520
+ } catch (error) {
521
+ settle(reject, error);
522
+ }
533
523
  });
534
524
  }
535
525
 
@@ -889,10 +879,8 @@ async function launchNewSession(rl, cwd = process.cwd()) {
889
879
  console.log(`准备启动新主线会话 · provider=${provider.name}`);
890
880
  await pause(rl, "按回车进入 Codex...");
891
881
 
892
- setRawMode(false);
893
882
  const startedAtMs = Date.now();
894
- await runCodex([], buildCodexEnv(provider), projectState.root);
895
- setRawMode(true);
883
+ await runCodex([], buildCodexEnv(provider), projectState.root, rl);
896
884
 
897
885
  const linkedSessionId = await maybeLinkLatestSession(
898
886
  projectState.root,
@@ -980,9 +968,7 @@ async function resumeSpecificSession(rl, sessionId, cwd = process.cwd()) {
980
968
  console.log(`provider=${provider.name}`);
981
969
  await pause(rl, "按回车进入 Codex...");
982
970
 
983
- setRawMode(false);
984
- await runCodex(["resume", sessionId], buildCodexEnv(provider), projectState.root);
985
- setRawMode(true);
971
+ await runCodex(["resume", sessionId], buildCodexEnv(provider), projectState.root, rl);
986
972
  }
987
973
 
988
974
  async function pickSessionAndResume(rl, cwd = process.cwd()) {
@@ -1128,10 +1114,8 @@ async function forkFromSession(rl, source, cwd = process.cwd()) {
1128
1114
  console.log(`provider=${provider.name}`);
1129
1115
  await pause(rl, "按回车进入 Codex...");
1130
1116
 
1131
- setRawMode(false);
1132
1117
  const startedAtMs = Date.now();
1133
- await runCodex(["fork", source.sessionId], buildCodexEnv(provider), projectState.root);
1134
- setRawMode(true);
1118
+ await runCodex(["fork", source.sessionId], buildCodexEnv(provider), projectState.root, rl);
1135
1119
 
1136
1120
  const linkedSessionId = await maybeLinkLatestSession(
1137
1121
  projectState.root,
@@ -1184,13 +1168,12 @@ async function deleteSpecificSession(rl, target, cwd = process.cwd()) {
1184
1168
  };
1185
1169
  }
1186
1170
 
1187
- setRawMode(false);
1188
1171
  const exitCode = await runCodex(
1189
1172
  ["delete", "--force", target.sessionId],
1190
1173
  {},
1191
1174
  projectState.root,
1175
+ rl,
1192
1176
  );
1193
- setRawMode(true);
1194
1177
 
1195
1178
  const stillExists = hasOfficialSession(target.sessionId, projectState.root);
1196
1179
  if (exitCode !== 0 || stillExists) {