codex-to-im 1.0.40 → 1.0.42

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/cli.mjs CHANGED
@@ -5,9 +5,9 @@ import { createRequire } from 'module'; const require = createRequire(import.met
5
5
  import { stdin as input, stdout as output } from "node:process";
6
6
 
7
7
  // src/service-manager.ts
8
- import fs2 from "node:fs";
8
+ import fs3 from "node:fs";
9
9
  import os2 from "node:os";
10
- import path2 from "node:path";
10
+ import path3 from "node:path";
11
11
  import { spawn } from "node:child_process";
12
12
  import { fileURLToPath } from "node:url";
13
13
 
@@ -233,42 +233,79 @@ function loadConfig() {
233
233
  return expandConfig(empty);
234
234
  }
235
235
 
236
- // src/service-manager.ts
237
- var moduleDir = path2.dirname(fileURLToPath(import.meta.url));
238
- var packageRoot = path2.resolve(moduleDir, "..");
236
+ // src/bridge-instance-lock.ts
237
+ import fs2 from "node:fs";
238
+ import path2 from "node:path";
239
239
  var runtimeDir = path2.join(CTI_HOME, "runtime");
240
- var logsDir = path2.join(CTI_HOME, "logs");
241
- var bridgePidFile = path2.join(runtimeDir, "bridge.pid");
242
- var bridgeStatusFile = path2.join(runtimeDir, "status.json");
243
- var bridgeStartLockFile = path2.join(runtimeDir, "bridge.start.lock");
244
- var uiStatusFile = path2.join(runtimeDir, "ui-server.json");
240
+ var bridgeInstanceLockFile = path2.join(runtimeDir, "bridge.instance.lock");
241
+ function readJsonFile(filePath, fallback) {
242
+ try {
243
+ return JSON.parse(fs2.readFileSync(filePath, "utf-8"));
244
+ } catch {
245
+ return fallback;
246
+ }
247
+ }
248
+ function isProcessAlive(pid) {
249
+ if (!pid) return false;
250
+ try {
251
+ process.kill(pid, 0);
252
+ return true;
253
+ } catch {
254
+ return false;
255
+ }
256
+ }
257
+ function readBridgeInstanceLock(filePath = bridgeInstanceLockFile) {
258
+ const parsed = readJsonFile(filePath, null);
259
+ const pid = Number(parsed?.pid);
260
+ const createdAt = typeof parsed?.createdAt === "string" ? parsed.createdAt : "";
261
+ if (!Number.isFinite(pid) || pid <= 0 || !createdAt) return null;
262
+ return { pid, createdAt };
263
+ }
264
+ function clearStaleBridgeInstanceLock(filePath = bridgeInstanceLockFile, isAlive = isProcessAlive) {
265
+ const existing = readBridgeInstanceLock(filePath);
266
+ if (existing && isAlive(existing.pid)) return;
267
+ try {
268
+ fs2.unlinkSync(filePath);
269
+ } catch {
270
+ }
271
+ }
272
+
273
+ // src/service-manager.ts
274
+ var moduleDir = path3.dirname(fileURLToPath(import.meta.url));
275
+ var packageRoot = path3.resolve(moduleDir, "..");
276
+ var runtimeDir2 = path3.join(CTI_HOME, "runtime");
277
+ var logsDir = path3.join(CTI_HOME, "logs");
278
+ var bridgePidFile = path3.join(runtimeDir2, "bridge.pid");
279
+ var bridgeStatusFile = path3.join(runtimeDir2, "status.json");
280
+ var bridgeStartLockFile = path3.join(runtimeDir2, "bridge.start.lock");
281
+ var uiStatusFile = path3.join(runtimeDir2, "ui-server.json");
245
282
  var uiPort = 4781;
246
283
  var bridgeAutostartTaskName = "CodexToIMBridge";
247
- var bridgeAutostartLauncherFile = path2.join(runtimeDir, "bridge-autostart.ps1");
248
- var npmUninstallLogFile = path2.join(runtimeDir, "npm-uninstall.log");
284
+ var bridgeAutostartLauncherFile = path3.join(runtimeDir2, "bridge-autostart.ps1");
285
+ var npmUninstallLogFile = path3.join(runtimeDir2, "npm-uninstall.log");
249
286
  var WINDOWS_HIDE = process.platform === "win32" ? { windowsHide: true } : {};
250
287
  var BRIDGE_START_LOCK_STALE_MS = 3e4;
251
288
  function ensureDirs() {
252
- fs2.mkdirSync(runtimeDir, { recursive: true });
253
- fs2.mkdirSync(logsDir, { recursive: true });
289
+ fs3.mkdirSync(runtimeDir2, { recursive: true });
290
+ fs3.mkdirSync(logsDir, { recursive: true });
254
291
  }
255
- function readJsonFile(filePath, fallback) {
292
+ function readJsonFile2(filePath, fallback) {
256
293
  try {
257
- return JSON.parse(fs2.readFileSync(filePath, "utf-8"));
294
+ return JSON.parse(fs3.readFileSync(filePath, "utf-8"));
258
295
  } catch {
259
296
  return fallback;
260
297
  }
261
298
  }
262
299
  function readPid(filePath) {
263
300
  try {
264
- const raw = fs2.readFileSync(filePath, "utf-8").trim();
301
+ const raw = fs3.readFileSync(filePath, "utf-8").trim();
265
302
  const pid = Number(raw);
266
303
  return Number.isFinite(pid) ? pid : void 0;
267
304
  } catch {
268
305
  return void 0;
269
306
  }
270
307
  }
271
- function isProcessAlive(pid) {
308
+ function isProcessAlive2(pid) {
272
309
  if (!pid) return false;
273
310
  try {
274
311
  process.kill(pid, 0);
@@ -277,32 +314,37 @@ function isProcessAlive(pid) {
277
314
  return false;
278
315
  }
279
316
  }
280
- function collectTrackedBridgePids(bridgePid, statusPid) {
317
+ function collectTrackedBridgePids(bridgePid, statusPid, instanceLockPid) {
281
318
  const unique = /* @__PURE__ */ new Set();
282
- for (const pid of [bridgePid, statusPid]) {
319
+ for (const pid of [bridgePid, statusPid, instanceLockPid]) {
283
320
  if (Number.isFinite(pid) && pid > 0) {
284
321
  unique.add(pid);
285
322
  }
286
323
  }
287
324
  return [...unique];
288
325
  }
289
- function resolveTrackedBridgePid(bridgePid, statusPid, isAlive = isProcessAlive) {
326
+ function resolveTrackedBridgePid(bridgePid, statusPid, instanceLockPid, isAlive = isProcessAlive2) {
290
327
  if (isAlive(bridgePid)) return bridgePid;
291
328
  if (isAlive(statusPid)) return statusPid;
292
- return bridgePid ?? statusPid;
329
+ if (isAlive(instanceLockPid)) return instanceLockPid;
330
+ return bridgePid ?? statusPid ?? instanceLockPid;
293
331
  }
294
332
  function getTrackedBridgePids(status) {
295
- const resolvedStatus = status ?? readJsonFile(bridgeStatusFile, { running: false });
296
- return collectTrackedBridgePids(readPid(bridgePidFile), resolvedStatus.pid);
333
+ const resolvedStatus = status ?? readJsonFile2(bridgeStatusFile, { running: false });
334
+ return collectTrackedBridgePids(
335
+ readPid(bridgePidFile),
336
+ resolvedStatus.pid,
337
+ readBridgeInstanceLock()?.pid
338
+ );
297
339
  }
298
340
  function clearBridgePidFile() {
299
341
  try {
300
- fs2.unlinkSync(bridgePidFile);
342
+ fs3.unlinkSync(bridgePidFile);
301
343
  } catch {
302
344
  }
303
345
  }
304
346
  function readBridgeStartLock(filePath = bridgeStartLockFile) {
305
- const parsed = readJsonFile(filePath, null);
347
+ const parsed = readJsonFile2(filePath, null);
306
348
  const pid = Number(parsed?.pid);
307
349
  const createdAt = typeof parsed?.createdAt === "string" ? parsed.createdAt : "";
308
350
  if (!Number.isFinite(pid) || pid <= 0 || !createdAt) return null;
@@ -312,7 +354,7 @@ function isBridgeStartLockStale(lock, options = {}) {
312
354
  if (!lock) return true;
313
355
  const nowMs = options.nowMs ?? Date.now();
314
356
  const staleMs = options.staleMs ?? BRIDGE_START_LOCK_STALE_MS;
315
- const isAlive = options.isAlive ?? isProcessAlive;
357
+ const isAlive = options.isAlive ?? isProcessAlive2;
316
358
  const createdAtMs = Date.parse(lock.createdAt);
317
359
  if (!Number.isFinite(createdAtMs)) return true;
318
360
  if (!isAlive(lock.pid)) return true;
@@ -328,7 +370,7 @@ function tryAcquireBridgeStartLock(options = {}) {
328
370
  };
329
371
  for (let attempt = 0; attempt < 2; attempt += 1) {
330
372
  try {
331
- fs2.writeFileSync(filePath, JSON.stringify(payload, null, 2), { encoding: "utf-8", flag: "wx" });
373
+ fs3.writeFileSync(filePath, JSON.stringify(payload, null, 2), { encoding: "utf-8", flag: "wx" });
332
374
  return { acquired: true };
333
375
  } catch (error) {
334
376
  const code = error.code;
@@ -342,7 +384,7 @@ function tryAcquireBridgeStartLock(options = {}) {
342
384
  return { acquired: false, holderPid: existing2?.pid };
343
385
  }
344
386
  try {
345
- fs2.unlinkSync(filePath);
387
+ fs3.unlinkSync(filePath);
346
388
  } catch {
347
389
  }
348
390
  }
@@ -354,14 +396,14 @@ function releaseBridgeStartLock(filePath = bridgeStartLockFile, ownerPid = proce
354
396
  const existing = readBridgeStartLock(filePath);
355
397
  if (!existing) {
356
398
  try {
357
- fs2.unlinkSync(filePath);
399
+ fs3.unlinkSync(filePath);
358
400
  } catch {
359
401
  }
360
402
  return;
361
403
  }
362
404
  if (existing.pid !== ownerPid) return;
363
405
  try {
364
- fs2.unlinkSync(filePath);
406
+ fs3.unlinkSync(filePath);
365
407
  } catch {
366
408
  }
367
409
  }
@@ -443,11 +485,11 @@ function ensureBridgeAutostartLauncher() {
443
485
  " }",
444
486
  " } catch { }",
445
487
  "}",
446
- `& $node '${escapePowerShellSingleQuoted(path2.join(packageRoot, "dist", "cli.mjs"))}' start`,
488
+ `& $node '${escapePowerShellSingleQuoted(path3.join(packageRoot, "dist", "cli.mjs"))}' start`,
447
489
  "exit $LASTEXITCODE",
448
490
  ""
449
491
  ].join("\r\n");
450
- fs2.writeFileSync(bridgeAutostartLauncherFile, content, "utf-8");
492
+ fs3.writeFileSync(bridgeAutostartLauncherFile, content, "utf-8");
451
493
  return bridgeAutostartLauncherFile;
452
494
  }
453
495
  function parsePowerShellJson(raw) {
@@ -491,7 +533,7 @@ function buildDeferredGlobalNpmUninstallLaunch(options = {}) {
491
533
  async function launchDeferredGlobalNpmUninstall() {
492
534
  ensureDirs();
493
535
  const launch = buildDeferredGlobalNpmUninstallLaunch();
494
- fs2.writeFileSync(
536
+ fs3.writeFileSync(
495
537
  launch.logPath,
496
538
  [
497
539
  `[${(/* @__PURE__ */ new Date()).toISOString()}] Scheduling global uninstall.`,
@@ -519,14 +561,18 @@ function getUiServerUrl(port = uiPort) {
519
561
  return `http://127.0.0.1:${port}`;
520
562
  }
521
563
  function getCurrentUiServerUrl() {
522
- const status = readJsonFile(uiStatusFile, null);
564
+ const status = readJsonFile2(uiStatusFile, null);
523
565
  if (!status?.port) return void 0;
524
566
  return getUiServerUrl(status.port);
525
567
  }
526
568
  function getBridgeStatus() {
527
- const status = readJsonFile(bridgeStatusFile, { running: false });
528
- const pid = resolveTrackedBridgePid(readPid(bridgePidFile), status.pid);
529
- if (!isProcessAlive(pid)) {
569
+ const status = readJsonFile2(bridgeStatusFile, { running: false });
570
+ const pid = resolveTrackedBridgePid(
571
+ readPid(bridgePidFile),
572
+ status.pid,
573
+ readBridgeInstanceLock()?.pid
574
+ );
575
+ if (!isProcessAlive2(pid)) {
530
576
  return {
531
577
  ...status,
532
578
  pid,
@@ -540,8 +586,8 @@ function getBridgeStatus() {
540
586
  };
541
587
  }
542
588
  function getUiServerStatus() {
543
- const status = readJsonFile(uiStatusFile, { running: false, port: uiPort });
544
- if (!isProcessAlive(status.pid)) {
589
+ const status = readJsonFile2(uiStatusFile, { running: false, port: uiPort });
590
+ if (!isProcessAlive2(status.pid)) {
545
591
  return {
546
592
  ...status,
547
593
  running: false,
@@ -626,7 +672,7 @@ async function waitForUiServer(timeoutMs = 15e3) {
626
672
  async function startBridge() {
627
673
  ensureDirs();
628
674
  const current = getBridgeStatus();
629
- const extraAlivePids = getTrackedBridgePids(current).filter((pid) => pid !== current.pid && isProcessAlive(pid));
675
+ const extraAlivePids = getTrackedBridgePids(current).filter((pid) => pid !== current.pid && isProcessAlive2(pid));
630
676
  if (current.running && extraAlivePids.length === 0) return current;
631
677
  if (current.running && extraAlivePids.length > 0) {
632
678
  await stopBridge();
@@ -651,17 +697,17 @@ async function startBridge() {
651
697
  startLockHeld = true;
652
698
  try {
653
699
  const currentAfterLock = getBridgeStatus();
654
- const extraAlivePidsAfterLock = getTrackedBridgePids(currentAfterLock).filter((pid) => pid !== currentAfterLock.pid && isProcessAlive(pid));
700
+ const extraAlivePidsAfterLock = getTrackedBridgePids(currentAfterLock).filter((pid) => pid !== currentAfterLock.pid && isProcessAlive2(pid));
655
701
  if (currentAfterLock.running && extraAlivePidsAfterLock.length === 0) return currentAfterLock;
656
702
  if (currentAfterLock.running && extraAlivePidsAfterLock.length > 0) {
657
703
  await stopBridge();
658
704
  }
659
- const daemonEntry = path2.join(packageRoot, "dist", "daemon.mjs");
660
- if (!fs2.existsSync(daemonEntry)) {
705
+ const daemonEntry = path3.join(packageRoot, "dist", "daemon.mjs");
706
+ if (!fs3.existsSync(daemonEntry)) {
661
707
  throw new Error(`Daemon bundle not found at ${daemonEntry}. Run npm run build first.`);
662
708
  }
663
- const stdoutFd = fs2.openSync(path2.join(logsDir, "bridge-launcher.out.log"), "a");
664
- const stderrFd = fs2.openSync(path2.join(logsDir, "bridge-launcher.err.log"), "a");
709
+ const stdoutFd = fs3.openSync(path3.join(logsDir, "bridge-launcher.out.log"), "a");
710
+ const stderrFd = fs3.openSync(path3.join(logsDir, "bridge-launcher.err.log"), "a");
665
711
  const child = spawn(process.execPath, [daemonEntry], {
666
712
  cwd: packageRoot,
667
713
  detached: true,
@@ -684,10 +730,11 @@ async function startBridge() {
684
730
  }
685
731
  }
686
732
  async function stopBridge() {
687
- const status = readJsonFile(bridgeStatusFile, { running: false });
688
- const pids = getTrackedBridgePids(status).filter((pid) => isProcessAlive(pid));
733
+ const status = readJsonFile2(bridgeStatusFile, { running: false });
734
+ const pids = getTrackedBridgePids(status).filter((pid) => isProcessAlive2(pid));
689
735
  if (pids.length === 0) {
690
736
  clearBridgePidFile();
737
+ clearStaleBridgeInstanceLock();
691
738
  return { ...getBridgeStatus(), running: false };
692
739
  }
693
740
  for (const pid of pids) {
@@ -709,13 +756,15 @@ async function stopBridge() {
709
756
  }
710
757
  const startedAt = Date.now();
711
758
  while (Date.now() - startedAt < 1e4) {
712
- if (pids.every((pid) => !isProcessAlive(pid))) {
759
+ if (pids.every((pid) => !isProcessAlive2(pid))) {
713
760
  clearBridgePidFile();
761
+ clearStaleBridgeInstanceLock();
714
762
  return getBridgeStatus();
715
763
  }
716
764
  await sleep(300);
717
765
  }
718
766
  clearBridgePidFile();
767
+ clearStaleBridgeInstanceLock();
719
768
  return getBridgeStatus();
720
769
  }
721
770
  async function getBridgeAutostartStatus() {
@@ -804,8 +853,8 @@ async function uninstallBridgeAutostart() {
804
853
  ].join("; ");
805
854
  await runPowerShell(script);
806
855
  try {
807
- if (fs2.existsSync(bridgeAutostartLauncherFile)) {
808
- fs2.unlinkSync(bridgeAutostartLauncherFile);
856
+ if (fs3.existsSync(bridgeAutostartLauncherFile)) {
857
+ fs3.unlinkSync(bridgeAutostartLauncherFile);
809
858
  }
810
859
  } catch {
811
860
  }
@@ -836,12 +885,12 @@ async function ensureUiServerRunning() {
836
885
  ensureDirs();
837
886
  const current = getUiServerStatus();
838
887
  if (current.running) return current;
839
- const serverEntry = path2.join(packageRoot, "dist", "ui-server.mjs");
840
- if (!fs2.existsSync(serverEntry)) {
888
+ const serverEntry = path3.join(packageRoot, "dist", "ui-server.mjs");
889
+ if (!fs3.existsSync(serverEntry)) {
841
890
  throw new Error(`UI server bundle not found at ${serverEntry}. Run npm run build first.`);
842
891
  }
843
- const stdoutFd = fs2.openSync(path2.join(logsDir, "ui-server.out.log"), "a");
844
- const stderrFd = fs2.openSync(path2.join(logsDir, "ui-server.err.log"), "a");
892
+ const stdoutFd = fs3.openSync(path3.join(logsDir, "ui-server.out.log"), "a");
893
+ const stderrFd = fs3.openSync(path3.join(logsDir, "ui-server.err.log"), "a");
845
894
  const child = spawn(process.execPath, [serverEntry], {
846
895
  cwd: packageRoot,
847
896
  detached: true,
@@ -861,7 +910,7 @@ async function ensureUiServerRunning() {
861
910
  }
862
911
  async function stopUiServer() {
863
912
  const status = getUiServerStatus();
864
- if (!status.pid || !isProcessAlive(status.pid)) {
913
+ if (!status.pid || !isProcessAlive2(status.pid)) {
865
914
  const next2 = { ...status, running: false };
866
915
  writeUiServerStatus(next2);
867
916
  return next2;
@@ -898,7 +947,7 @@ async function stopUiServer() {
898
947
  }
899
948
  function writeUiServerStatus(status) {
900
949
  ensureDirs();
901
- fs2.writeFileSync(uiStatusFile, JSON.stringify(status, null, 2), "utf-8");
950
+ fs3.writeFileSync(uiStatusFile, JSON.stringify(status, null, 2), "utf-8");
902
951
  }
903
952
  function openBrowser(url) {
904
953
  if (process.platform === "win32") {