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 +106 -57
- package/dist/daemon.mjs +1051 -161
- package/dist/ui-server.mjs +181 -130
- package/package.json +2 -2
- package/scripts/patch-codex-sdk-windows-hide.js +1 -1
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
|
|
8
|
+
import fs3 from "node:fs";
|
|
9
9
|
import os2 from "node:os";
|
|
10
|
-
import
|
|
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/
|
|
237
|
-
|
|
238
|
-
|
|
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
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
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 =
|
|
248
|
-
var npmUninstallLogFile =
|
|
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
|
-
|
|
253
|
-
|
|
289
|
+
fs3.mkdirSync(runtimeDir2, { recursive: true });
|
|
290
|
+
fs3.mkdirSync(logsDir, { recursive: true });
|
|
254
291
|
}
|
|
255
|
-
function
|
|
292
|
+
function readJsonFile2(filePath, fallback) {
|
|
256
293
|
try {
|
|
257
|
-
return JSON.parse(
|
|
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 =
|
|
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
|
|
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 =
|
|
326
|
+
function resolveTrackedBridgePid(bridgePid, statusPid, instanceLockPid, isAlive = isProcessAlive2) {
|
|
290
327
|
if (isAlive(bridgePid)) return bridgePid;
|
|
291
328
|
if (isAlive(statusPid)) return statusPid;
|
|
292
|
-
|
|
329
|
+
if (isAlive(instanceLockPid)) return instanceLockPid;
|
|
330
|
+
return bridgePid ?? statusPid ?? instanceLockPid;
|
|
293
331
|
}
|
|
294
332
|
function getTrackedBridgePids(status) {
|
|
295
|
-
const resolvedStatus = status ??
|
|
296
|
-
return collectTrackedBridgePids(
|
|
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
|
-
|
|
342
|
+
fs3.unlinkSync(bridgePidFile);
|
|
301
343
|
} catch {
|
|
302
344
|
}
|
|
303
345
|
}
|
|
304
346
|
function readBridgeStartLock(filePath = bridgeStartLockFile) {
|
|
305
|
-
const parsed =
|
|
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 ??
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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(
|
|
488
|
+
`& $node '${escapePowerShellSingleQuoted(path3.join(packageRoot, "dist", "cli.mjs"))}' start`,
|
|
447
489
|
"exit $LASTEXITCODE",
|
|
448
490
|
""
|
|
449
491
|
].join("\r\n");
|
|
450
|
-
|
|
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
|
-
|
|
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 =
|
|
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 =
|
|
528
|
-
const pid = resolveTrackedBridgePid(
|
|
529
|
-
|
|
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 =
|
|
544
|
-
if (!
|
|
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 &&
|
|
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 &&
|
|
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 =
|
|
660
|
-
if (!
|
|
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 =
|
|
664
|
-
const stderrFd =
|
|
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 =
|
|
688
|
-
const pids = getTrackedBridgePids(status).filter((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) => !
|
|
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 (
|
|
808
|
-
|
|
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 =
|
|
840
|
-
if (!
|
|
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 =
|
|
844
|
-
const stderrFd =
|
|
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 || !
|
|
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
|
-
|
|
950
|
+
fs3.writeFileSync(uiStatusFile, JSON.stringify(status, null, 2), "utf-8");
|
|
902
951
|
}
|
|
903
952
|
function openBrowser(url) {
|
|
904
953
|
if (process.platform === "win32") {
|