codex-to-im 1.0.58 → 1.0.60
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 +211 -74
- package/dist/daemon.mjs +1584 -656
- package/dist/ui-server.mjs +882 -437
- package/package.json +9 -8
- package/scripts/patch-codex-sdk-windows-hide.js +14 -3
package/dist/cli.mjs
CHANGED
|
@@ -5,14 +5,15 @@ 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 fs4 from "node:fs";
|
|
9
9
|
import os2 from "node:os";
|
|
10
10
|
import path3 from "node:path";
|
|
11
11
|
import { spawn } from "node:child_process";
|
|
12
12
|
import { fileURLToPath } from "node:url";
|
|
13
13
|
|
|
14
14
|
// src/config.ts
|
|
15
|
-
import
|
|
15
|
+
import fs2 from "node:fs";
|
|
16
|
+
import crypto2 from "node:crypto";
|
|
16
17
|
import os from "node:os";
|
|
17
18
|
import path from "node:path";
|
|
18
19
|
|
|
@@ -39,10 +40,101 @@ function normalizeChannelId(value) {
|
|
|
39
40
|
return value.trim().toLowerCase().replace(/[^a-z0-9_-]+/g, "-").replace(/^-+|-+$/g, "") || "channel";
|
|
40
41
|
}
|
|
41
42
|
|
|
43
|
+
// src/file-lock.ts
|
|
44
|
+
import crypto from "node:crypto";
|
|
45
|
+
import fs from "node:fs";
|
|
46
|
+
var DEFAULT_LOCK_TIMEOUT_MS = 5e3;
|
|
47
|
+
var DEFAULT_STALE_LOCK_MS = 3e4;
|
|
48
|
+
var LOCK_RETRY_INTERVAL_MS = 25;
|
|
49
|
+
var WAIT_ARRAY = new Int32Array(new SharedArrayBuffer(4));
|
|
50
|
+
var ACTIVE_LOCKS = /* @__PURE__ */ new Map();
|
|
51
|
+
function sleepSync(ms) {
|
|
52
|
+
Atomics.wait(WAIT_ARRAY, 0, 0, Math.max(1, ms));
|
|
53
|
+
}
|
|
54
|
+
function isProcessAlive(pid) {
|
|
55
|
+
if (!Number.isInteger(pid) || pid <= 0) return false;
|
|
56
|
+
try {
|
|
57
|
+
process.kill(pid, 0);
|
|
58
|
+
return true;
|
|
59
|
+
} catch (error) {
|
|
60
|
+
return error.code === "EPERM";
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
function removeStaleLock(lockPath, staleAfterMs) {
|
|
64
|
+
try {
|
|
65
|
+
const stat = fs.statSync(lockPath);
|
|
66
|
+
if (Date.now() - stat.mtimeMs < staleAfterMs) return false;
|
|
67
|
+
let ownerPid = 0;
|
|
68
|
+
try {
|
|
69
|
+
const parsed = JSON.parse(fs.readFileSync(lockPath, "utf8"));
|
|
70
|
+
ownerPid = typeof parsed.pid === "number" ? parsed.pid : 0;
|
|
71
|
+
} catch {
|
|
72
|
+
}
|
|
73
|
+
if (ownerPid && isProcessAlive(ownerPid)) return false;
|
|
74
|
+
fs.rmSync(lockPath, { force: true });
|
|
75
|
+
return true;
|
|
76
|
+
} catch (error) {
|
|
77
|
+
return error.code === "ENOENT";
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
function withFileLock(targetPath, operation, options) {
|
|
81
|
+
const lockPath = `${targetPath}.lock`;
|
|
82
|
+
const activeDepth = ACTIVE_LOCKS.get(lockPath) || 0;
|
|
83
|
+
if (activeDepth > 0) {
|
|
84
|
+
ACTIVE_LOCKS.set(lockPath, activeDepth + 1);
|
|
85
|
+
try {
|
|
86
|
+
return operation();
|
|
87
|
+
} finally {
|
|
88
|
+
const nextDepth = (ACTIVE_LOCKS.get(lockPath) || 1) - 1;
|
|
89
|
+
if (nextDepth > 0) ACTIVE_LOCKS.set(lockPath, nextDepth);
|
|
90
|
+
else ACTIVE_LOCKS.delete(lockPath);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
const timeoutMs = Math.max(0, options?.timeoutMs ?? DEFAULT_LOCK_TIMEOUT_MS);
|
|
94
|
+
const staleAfterMs = Math.max(timeoutMs, options?.staleAfterMs ?? DEFAULT_STALE_LOCK_MS);
|
|
95
|
+
const deadline = Date.now() + timeoutMs;
|
|
96
|
+
const record = {
|
|
97
|
+
token: crypto.randomUUID(),
|
|
98
|
+
pid: process.pid,
|
|
99
|
+
createdAt: Date.now()
|
|
100
|
+
};
|
|
101
|
+
let handle = null;
|
|
102
|
+
while (handle === null) {
|
|
103
|
+
try {
|
|
104
|
+
handle = fs.openSync(lockPath, "wx", 384);
|
|
105
|
+
fs.writeFileSync(handle, JSON.stringify(record), "utf8");
|
|
106
|
+
} catch (error) {
|
|
107
|
+
const code = error.code;
|
|
108
|
+
if (code !== "EEXIST") throw error;
|
|
109
|
+
if (removeStaleLock(lockPath, staleAfterMs)) continue;
|
|
110
|
+
if (Date.now() >= deadline) {
|
|
111
|
+
throw new Error(`Timed out waiting for file lock: ${lockPath}`);
|
|
112
|
+
}
|
|
113
|
+
sleepSync(Math.min(LOCK_RETRY_INTERVAL_MS, Math.max(1, deadline - Date.now())));
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
try {
|
|
117
|
+
ACTIVE_LOCKS.set(lockPath, 1);
|
|
118
|
+
return operation();
|
|
119
|
+
} finally {
|
|
120
|
+
ACTIVE_LOCKS.delete(lockPath);
|
|
121
|
+
try {
|
|
122
|
+
fs.closeSync(handle);
|
|
123
|
+
} catch {
|
|
124
|
+
}
|
|
125
|
+
try {
|
|
126
|
+
const current = JSON.parse(fs.readFileSync(lockPath, "utf8"));
|
|
127
|
+
if (current.token === record.token) fs.rmSync(lockPath, { force: true });
|
|
128
|
+
} catch {
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
42
133
|
// src/config.ts
|
|
43
134
|
function isSupportedChannelProvider(value) {
|
|
44
135
|
return value === "feishu" || value === "weixin";
|
|
45
136
|
}
|
|
137
|
+
var RAW_CHANNELS = Symbol("rawConfigV2Channels");
|
|
46
138
|
var DEFAULT_CTI_HOME = path.join(os.homedir(), ".codex-to-im");
|
|
47
139
|
var DEFAULT_WORKSPACE_ROOT = path.join(os.homedir(), "cx2im");
|
|
48
140
|
var CTI_HOME = process.env.CTI_HOME || DEFAULT_CTI_HOME;
|
|
@@ -76,7 +168,7 @@ function parseEnvFile(content) {
|
|
|
76
168
|
}
|
|
77
169
|
function loadRawConfigEnv() {
|
|
78
170
|
try {
|
|
79
|
-
return parseEnvFile(
|
|
171
|
+
return parseEnvFile(fs2.readFileSync(CONFIG_PATH, "utf-8"));
|
|
80
172
|
} catch {
|
|
81
173
|
return /* @__PURE__ */ new Map();
|
|
82
174
|
}
|
|
@@ -103,26 +195,66 @@ function nowIso() {
|
|
|
103
195
|
return (/* @__PURE__ */ new Date()).toISOString();
|
|
104
196
|
}
|
|
105
197
|
function ensureConfigDir() {
|
|
106
|
-
|
|
198
|
+
fs2.mkdirSync(CTI_HOME, { recursive: true });
|
|
107
199
|
}
|
|
108
200
|
function readConfigV2File() {
|
|
201
|
+
if (!fs2.existsSync(CONFIG_V2_PATH)) return null;
|
|
202
|
+
let content;
|
|
203
|
+
try {
|
|
204
|
+
content = fs2.readFileSync(CONFIG_V2_PATH, "utf-8");
|
|
205
|
+
} catch (error) {
|
|
206
|
+
throw new Error(`\u65E0\u6CD5\u8BFB\u53D6\u914D\u7F6E\u6587\u4EF6 ${CONFIG_V2_PATH}: ${error instanceof Error ? error.message : String(error)}`);
|
|
207
|
+
}
|
|
208
|
+
let raw;
|
|
109
209
|
try {
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
210
|
+
raw = JSON.parse(content);
|
|
211
|
+
} catch (error) {
|
|
212
|
+
throw new Error(`\u914D\u7F6E\u6587\u4EF6 ${CONFIG_V2_PATH} \u4E0D\u662F\u6709\u6548 JSON\uFF0C\u5DF2\u62D2\u7EDD\u8986\u76D6: ${error instanceof Error ? error.message : String(error)}`);
|
|
213
|
+
}
|
|
214
|
+
if (!raw || typeof raw !== "object" || Array.isArray(raw)) {
|
|
215
|
+
throw new Error(`\u914D\u7F6E\u6587\u4EF6 ${CONFIG_V2_PATH} \u7684\u6839\u8282\u70B9\u5FC5\u987B\u662F\u5BF9\u8C61\uFF0C\u5DF2\u62D2\u7EDD\u8986\u76D6\u3002`);
|
|
216
|
+
}
|
|
217
|
+
if (raw.schemaVersion !== 2) {
|
|
218
|
+
throw new Error(`\u4E0D\u652F\u6301\u914D\u7F6E schemaVersion=${String(raw.schemaVersion)}\uFF0C\u5DF2\u62D2\u7EDD\u7528\u5F53\u524D\u7248\u672C\u8986\u76D6\u3002`);
|
|
219
|
+
}
|
|
220
|
+
if (!raw.runtime || typeof raw.runtime !== "object" || Array.isArray(raw.runtime)) {
|
|
221
|
+
throw new Error(`\u914D\u7F6E\u6587\u4EF6 ${CONFIG_V2_PATH} \u7F3A\u5C11\u6709\u6548\u7684 runtime \u5BF9\u8C61\uFF0C\u5DF2\u62D2\u7EDD\u8986\u76D6\u3002`);
|
|
222
|
+
}
|
|
223
|
+
if (!Array.isArray(raw.channels)) {
|
|
224
|
+
throw new Error(`\u914D\u7F6E\u6587\u4EF6 ${CONFIG_V2_PATH} \u7F3A\u5C11 channels \u6570\u7EC4\uFF0C\u5DF2\u62D2\u7EDD\u8986\u76D6\u3002`);
|
|
225
|
+
}
|
|
226
|
+
const rawChannels = raw.channels;
|
|
227
|
+
const parsed = {
|
|
228
|
+
...raw,
|
|
229
|
+
schemaVersion: 2,
|
|
230
|
+
runtime: {
|
|
231
|
+
...raw.runtime,
|
|
232
|
+
provider: normalizeRuntimeProvider(raw.runtime.provider)
|
|
233
|
+
},
|
|
234
|
+
channels: normalizeChannelInstances(rawChannels)
|
|
235
|
+
};
|
|
236
|
+
Object.defineProperty(parsed, RAW_CHANNELS, { value: rawChannels, enumerable: false });
|
|
237
|
+
return parsed;
|
|
238
|
+
}
|
|
239
|
+
function atomicWriteFile(filePath, content) {
|
|
240
|
+
ensureConfigDir();
|
|
241
|
+
const tmpPath = `${filePath}.${process.pid}.${crypto2.randomUUID()}.tmp`;
|
|
242
|
+
try {
|
|
243
|
+
fs2.writeFileSync(tmpPath, content, { mode: 384 });
|
|
244
|
+
fs2.renameSync(tmpPath, filePath);
|
|
245
|
+
} finally {
|
|
246
|
+
try {
|
|
247
|
+
fs2.rmSync(tmpPath, { force: true });
|
|
248
|
+
} catch {
|
|
115
249
|
}
|
|
116
|
-
return null;
|
|
117
|
-
} catch {
|
|
118
|
-
return null;
|
|
119
250
|
}
|
|
120
251
|
}
|
|
121
252
|
function writeConfigV2File(config) {
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
253
|
+
const serialized = {
|
|
254
|
+
...config,
|
|
255
|
+
channels: config[RAW_CHANNELS] || config.channels
|
|
256
|
+
};
|
|
257
|
+
atomicWriteFile(CONFIG_V2_PATH, JSON.stringify(serialized, null, 2));
|
|
126
258
|
}
|
|
127
259
|
function defaultAliasForProvider(provider) {
|
|
128
260
|
return provider === "feishu" ? "\u98DE\u4E66" : "\u5FAE\u4FE1";
|
|
@@ -143,6 +275,7 @@ function normalizeChannelInstances(value) {
|
|
|
143
275
|
const config = record.config && typeof record.config === "object" ? record.config : {};
|
|
144
276
|
const timestamp = nowIso();
|
|
145
277
|
return [{
|
|
278
|
+
...record,
|
|
146
279
|
id: normalizeChannelId(
|
|
147
280
|
typeof record.id === "string" && record.id.trim() ? record.id : buildDefaultChannelId(provider)
|
|
148
281
|
),
|
|
@@ -242,44 +375,48 @@ function expandConfig(v2) {
|
|
|
242
375
|
function loadConfig() {
|
|
243
376
|
const current = readConfigV2File();
|
|
244
377
|
if (current) return expandConfig(current);
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
378
|
+
return withFileLock(CONFIG_V2_PATH, () => {
|
|
379
|
+
const concurrentlyCreated = readConfigV2File();
|
|
380
|
+
if (concurrentlyCreated) return expandConfig(concurrentlyCreated);
|
|
381
|
+
const legacyEnv = loadRawConfigEnv();
|
|
382
|
+
if (legacyEnv.size > 0) {
|
|
383
|
+
const migrated = migrateLegacyEnvToV2(legacyEnv);
|
|
384
|
+
writeConfigV2File(migrated);
|
|
385
|
+
return expandConfig(migrated);
|
|
386
|
+
}
|
|
387
|
+
const empty = {
|
|
388
|
+
schemaVersion: 2,
|
|
389
|
+
runtime: {
|
|
390
|
+
provider: "codex",
|
|
391
|
+
defaultWorkspaceRoot: DEFAULT_WORKSPACE_ROOT,
|
|
392
|
+
defaultMode: "code",
|
|
393
|
+
historyMessageLimit: 8,
|
|
394
|
+
streamStatusIdleStartSeconds: DEFAULT_STREAM_STATUS_IDLE_START_SECONDS,
|
|
395
|
+
streamStatusCheckIntervalSeconds: DEFAULT_STREAM_STATUS_CHECK_INTERVAL_SECONDS,
|
|
396
|
+
codexSkipGitRepoCheck: true,
|
|
397
|
+
codexSandboxMode: "workspace-write",
|
|
398
|
+
codexReasoningEffort: "medium",
|
|
399
|
+
uiAllowLan: false
|
|
400
|
+
},
|
|
401
|
+
channels: []
|
|
402
|
+
};
|
|
403
|
+
return expandConfig(empty);
|
|
404
|
+
});
|
|
268
405
|
}
|
|
269
406
|
|
|
270
407
|
// src/bridge-instance-lock.ts
|
|
271
|
-
import
|
|
408
|
+
import fs3 from "node:fs";
|
|
272
409
|
import path2 from "node:path";
|
|
273
410
|
var runtimeDir = path2.join(CTI_HOME, "runtime");
|
|
274
411
|
var bridgeInstanceLockFile = path2.join(runtimeDir, "bridge.instance.lock");
|
|
275
412
|
function readJsonFile(filePath, fallback) {
|
|
276
413
|
try {
|
|
277
|
-
return JSON.parse(
|
|
414
|
+
return JSON.parse(fs3.readFileSync(filePath, "utf-8"));
|
|
278
415
|
} catch {
|
|
279
416
|
return fallback;
|
|
280
417
|
}
|
|
281
418
|
}
|
|
282
|
-
function
|
|
419
|
+
function isProcessAlive2(pid) {
|
|
283
420
|
if (!pid) return false;
|
|
284
421
|
try {
|
|
285
422
|
process.kill(pid, 0);
|
|
@@ -295,11 +432,11 @@ function readBridgeInstanceLock(filePath = bridgeInstanceLockFile) {
|
|
|
295
432
|
if (!Number.isFinite(pid) || pid <= 0 || !createdAt) return null;
|
|
296
433
|
return { pid, createdAt };
|
|
297
434
|
}
|
|
298
|
-
function clearStaleBridgeInstanceLock(filePath = bridgeInstanceLockFile, isAlive =
|
|
435
|
+
function clearStaleBridgeInstanceLock(filePath = bridgeInstanceLockFile, isAlive = isProcessAlive2) {
|
|
299
436
|
const existing = readBridgeInstanceLock(filePath);
|
|
300
437
|
if (existing && isAlive(existing.pid)) return;
|
|
301
438
|
try {
|
|
302
|
-
|
|
439
|
+
fs3.unlinkSync(filePath);
|
|
303
440
|
} catch {
|
|
304
441
|
}
|
|
305
442
|
}
|
|
@@ -320,26 +457,26 @@ var npmUninstallLogFile = path3.join(runtimeDir2, "npm-uninstall.log");
|
|
|
320
457
|
var WINDOWS_HIDE = process.platform === "win32" ? { windowsHide: true } : {};
|
|
321
458
|
var BRIDGE_START_LOCK_STALE_MS = 3e4;
|
|
322
459
|
function ensureDirs() {
|
|
323
|
-
|
|
324
|
-
|
|
460
|
+
fs4.mkdirSync(runtimeDir2, { recursive: true });
|
|
461
|
+
fs4.mkdirSync(logsDir, { recursive: true });
|
|
325
462
|
}
|
|
326
463
|
function readJsonFile2(filePath, fallback) {
|
|
327
464
|
try {
|
|
328
|
-
return JSON.parse(
|
|
465
|
+
return JSON.parse(fs4.readFileSync(filePath, "utf-8"));
|
|
329
466
|
} catch {
|
|
330
467
|
return fallback;
|
|
331
468
|
}
|
|
332
469
|
}
|
|
333
470
|
function readPid(filePath) {
|
|
334
471
|
try {
|
|
335
|
-
const raw =
|
|
472
|
+
const raw = fs4.readFileSync(filePath, "utf-8").trim();
|
|
336
473
|
const pid = Number(raw);
|
|
337
474
|
return Number.isFinite(pid) ? pid : void 0;
|
|
338
475
|
} catch {
|
|
339
476
|
return void 0;
|
|
340
477
|
}
|
|
341
478
|
}
|
|
342
|
-
function
|
|
479
|
+
function isProcessAlive3(pid) {
|
|
343
480
|
if (!pid) return false;
|
|
344
481
|
try {
|
|
345
482
|
process.kill(pid, 0);
|
|
@@ -357,7 +494,7 @@ function collectTrackedBridgePids(bridgePid, statusPid, instanceLockPid) {
|
|
|
357
494
|
}
|
|
358
495
|
return [...unique];
|
|
359
496
|
}
|
|
360
|
-
function resolveTrackedBridgePid(bridgePid, statusPid, instanceLockPid, isAlive =
|
|
497
|
+
function resolveTrackedBridgePid(bridgePid, statusPid, instanceLockPid, isAlive = isProcessAlive3) {
|
|
361
498
|
if (isAlive(bridgePid)) return bridgePid;
|
|
362
499
|
if (isAlive(statusPid)) return statusPid;
|
|
363
500
|
if (isAlive(instanceLockPid)) return instanceLockPid;
|
|
@@ -373,7 +510,7 @@ function getTrackedBridgePids(status) {
|
|
|
373
510
|
}
|
|
374
511
|
function clearBridgePidFile() {
|
|
375
512
|
try {
|
|
376
|
-
|
|
513
|
+
fs4.unlinkSync(bridgePidFile);
|
|
377
514
|
} catch {
|
|
378
515
|
}
|
|
379
516
|
}
|
|
@@ -388,7 +525,7 @@ function isBridgeStartLockStale(lock, options = {}) {
|
|
|
388
525
|
if (!lock) return true;
|
|
389
526
|
const nowMs = options.nowMs ?? Date.now();
|
|
390
527
|
const staleMs = options.staleMs ?? BRIDGE_START_LOCK_STALE_MS;
|
|
391
|
-
const isAlive = options.isAlive ??
|
|
528
|
+
const isAlive = options.isAlive ?? isProcessAlive3;
|
|
392
529
|
const createdAtMs = Date.parse(lock.createdAt);
|
|
393
530
|
if (!Number.isFinite(createdAtMs)) return true;
|
|
394
531
|
if (!isAlive(lock.pid)) return true;
|
|
@@ -404,7 +541,7 @@ function tryAcquireBridgeStartLock(options = {}) {
|
|
|
404
541
|
};
|
|
405
542
|
for (let attempt = 0; attempt < 2; attempt += 1) {
|
|
406
543
|
try {
|
|
407
|
-
|
|
544
|
+
fs4.writeFileSync(filePath, JSON.stringify(payload, null, 2), { encoding: "utf-8", flag: "wx" });
|
|
408
545
|
return { acquired: true };
|
|
409
546
|
} catch (error) {
|
|
410
547
|
const code = error.code;
|
|
@@ -418,7 +555,7 @@ function tryAcquireBridgeStartLock(options = {}) {
|
|
|
418
555
|
return { acquired: false, holderPid: existing2?.pid };
|
|
419
556
|
}
|
|
420
557
|
try {
|
|
421
|
-
|
|
558
|
+
fs4.unlinkSync(filePath);
|
|
422
559
|
} catch {
|
|
423
560
|
}
|
|
424
561
|
}
|
|
@@ -430,14 +567,14 @@ function releaseBridgeStartLock(filePath = bridgeStartLockFile, ownerPid = proce
|
|
|
430
567
|
const existing = readBridgeStartLock(filePath);
|
|
431
568
|
if (!existing) {
|
|
432
569
|
try {
|
|
433
|
-
|
|
570
|
+
fs4.unlinkSync(filePath);
|
|
434
571
|
} catch {
|
|
435
572
|
}
|
|
436
573
|
return;
|
|
437
574
|
}
|
|
438
575
|
if (existing.pid !== ownerPid) return;
|
|
439
576
|
try {
|
|
440
|
-
|
|
577
|
+
fs4.unlinkSync(filePath);
|
|
441
578
|
} catch {
|
|
442
579
|
}
|
|
443
580
|
}
|
|
@@ -523,7 +660,7 @@ function ensureBridgeAutostartLauncher() {
|
|
|
523
660
|
"exit $LASTEXITCODE",
|
|
524
661
|
""
|
|
525
662
|
].join("\r\n");
|
|
526
|
-
|
|
663
|
+
fs4.writeFileSync(bridgeAutostartLauncherFile, content, "utf-8");
|
|
527
664
|
return bridgeAutostartLauncherFile;
|
|
528
665
|
}
|
|
529
666
|
function parsePowerShellJson(raw) {
|
|
@@ -567,7 +704,7 @@ function buildDeferredGlobalNpmUninstallLaunch(options = {}) {
|
|
|
567
704
|
async function launchDeferredGlobalNpmUninstall() {
|
|
568
705
|
ensureDirs();
|
|
569
706
|
const launch = buildDeferredGlobalNpmUninstallLaunch();
|
|
570
|
-
|
|
707
|
+
fs4.writeFileSync(
|
|
571
708
|
launch.logPath,
|
|
572
709
|
[
|
|
573
710
|
`[${(/* @__PURE__ */ new Date()).toISOString()}] Scheduling global uninstall.`,
|
|
@@ -606,7 +743,7 @@ function getBridgeStatus() {
|
|
|
606
743
|
status.pid,
|
|
607
744
|
readBridgeInstanceLock()?.pid
|
|
608
745
|
);
|
|
609
|
-
if (!
|
|
746
|
+
if (!isProcessAlive3(pid)) {
|
|
610
747
|
return {
|
|
611
748
|
...status,
|
|
612
749
|
pid,
|
|
@@ -621,7 +758,7 @@ function getBridgeStatus() {
|
|
|
621
758
|
}
|
|
622
759
|
function getUiServerStatus() {
|
|
623
760
|
const status = readJsonFile2(uiStatusFile, { running: false, port: uiPort });
|
|
624
|
-
if (!
|
|
761
|
+
if (!isProcessAlive3(status.pid)) {
|
|
625
762
|
return {
|
|
626
763
|
...status,
|
|
627
764
|
running: false,
|
|
@@ -706,7 +843,7 @@ async function waitForUiServer(timeoutMs = 15e3) {
|
|
|
706
843
|
async function startBridge() {
|
|
707
844
|
ensureDirs();
|
|
708
845
|
const current = getBridgeStatus();
|
|
709
|
-
const extraAlivePids = getTrackedBridgePids(current).filter((pid) => pid !== current.pid &&
|
|
846
|
+
const extraAlivePids = getTrackedBridgePids(current).filter((pid) => pid !== current.pid && isProcessAlive3(pid));
|
|
710
847
|
if (current.running && extraAlivePids.length === 0) return current;
|
|
711
848
|
if (current.running && extraAlivePids.length > 0) {
|
|
712
849
|
await stopBridge();
|
|
@@ -731,17 +868,17 @@ async function startBridge() {
|
|
|
731
868
|
startLockHeld = true;
|
|
732
869
|
try {
|
|
733
870
|
const currentAfterLock = getBridgeStatus();
|
|
734
|
-
const extraAlivePidsAfterLock = getTrackedBridgePids(currentAfterLock).filter((pid) => pid !== currentAfterLock.pid &&
|
|
871
|
+
const extraAlivePidsAfterLock = getTrackedBridgePids(currentAfterLock).filter((pid) => pid !== currentAfterLock.pid && isProcessAlive3(pid));
|
|
735
872
|
if (currentAfterLock.running && extraAlivePidsAfterLock.length === 0) return currentAfterLock;
|
|
736
873
|
if (currentAfterLock.running && extraAlivePidsAfterLock.length > 0) {
|
|
737
874
|
await stopBridge();
|
|
738
875
|
}
|
|
739
876
|
const daemonEntry = path3.join(packageRoot, "dist", "daemon.mjs");
|
|
740
|
-
if (!
|
|
877
|
+
if (!fs4.existsSync(daemonEntry)) {
|
|
741
878
|
throw new Error(`Daemon bundle not found at ${daemonEntry}. Run npm run build first.`);
|
|
742
879
|
}
|
|
743
|
-
const stdoutFd =
|
|
744
|
-
const stderrFd =
|
|
880
|
+
const stdoutFd = fs4.openSync(path3.join(logsDir, "bridge-launcher.out.log"), "a");
|
|
881
|
+
const stderrFd = fs4.openSync(path3.join(logsDir, "bridge-launcher.err.log"), "a");
|
|
745
882
|
const child = spawn(process.execPath, [daemonEntry], {
|
|
746
883
|
cwd: packageRoot,
|
|
747
884
|
detached: true,
|
|
@@ -765,7 +902,7 @@ async function startBridge() {
|
|
|
765
902
|
}
|
|
766
903
|
async function stopBridge() {
|
|
767
904
|
const status = readJsonFile2(bridgeStatusFile, { running: false });
|
|
768
|
-
const pids = getTrackedBridgePids(status).filter((pid) =>
|
|
905
|
+
const pids = getTrackedBridgePids(status).filter((pid) => isProcessAlive3(pid));
|
|
769
906
|
if (pids.length === 0) {
|
|
770
907
|
clearBridgePidFile();
|
|
771
908
|
clearStaleBridgeInstanceLock();
|
|
@@ -790,7 +927,7 @@ async function stopBridge() {
|
|
|
790
927
|
}
|
|
791
928
|
const startedAt = Date.now();
|
|
792
929
|
while (Date.now() - startedAt < 1e4) {
|
|
793
|
-
if (pids.every((pid) => !
|
|
930
|
+
if (pids.every((pid) => !isProcessAlive3(pid))) {
|
|
794
931
|
clearBridgePidFile();
|
|
795
932
|
clearStaleBridgeInstanceLock();
|
|
796
933
|
return getBridgeStatus();
|
|
@@ -887,8 +1024,8 @@ async function uninstallBridgeAutostart() {
|
|
|
887
1024
|
].join("; ");
|
|
888
1025
|
await runPowerShell(script);
|
|
889
1026
|
try {
|
|
890
|
-
if (
|
|
891
|
-
|
|
1027
|
+
if (fs4.existsSync(bridgeAutostartLauncherFile)) {
|
|
1028
|
+
fs4.unlinkSync(bridgeAutostartLauncherFile);
|
|
892
1029
|
}
|
|
893
1030
|
} catch {
|
|
894
1031
|
}
|
|
@@ -920,11 +1057,11 @@ async function ensureUiServerRunning() {
|
|
|
920
1057
|
const current = getUiServerStatus();
|
|
921
1058
|
if (current.running) return current;
|
|
922
1059
|
const serverEntry = path3.join(packageRoot, "dist", "ui-server.mjs");
|
|
923
|
-
if (!
|
|
1060
|
+
if (!fs4.existsSync(serverEntry)) {
|
|
924
1061
|
throw new Error(`UI server bundle not found at ${serverEntry}. Run npm run build first.`);
|
|
925
1062
|
}
|
|
926
|
-
const stdoutFd =
|
|
927
|
-
const stderrFd =
|
|
1063
|
+
const stdoutFd = fs4.openSync(path3.join(logsDir, "ui-server.out.log"), "a");
|
|
1064
|
+
const stderrFd = fs4.openSync(path3.join(logsDir, "ui-server.err.log"), "a");
|
|
928
1065
|
const child = spawn(process.execPath, [serverEntry], {
|
|
929
1066
|
cwd: packageRoot,
|
|
930
1067
|
detached: true,
|
|
@@ -944,7 +1081,7 @@ async function ensureUiServerRunning() {
|
|
|
944
1081
|
}
|
|
945
1082
|
async function stopUiServer() {
|
|
946
1083
|
const status = getUiServerStatus();
|
|
947
|
-
if (!status.pid || !
|
|
1084
|
+
if (!status.pid || !isProcessAlive3(status.pid)) {
|
|
948
1085
|
const next2 = { ...status, running: false };
|
|
949
1086
|
writeUiServerStatus(next2);
|
|
950
1087
|
return next2;
|
|
@@ -981,7 +1118,7 @@ async function stopUiServer() {
|
|
|
981
1118
|
}
|
|
982
1119
|
function writeUiServerStatus(status) {
|
|
983
1120
|
ensureDirs();
|
|
984
|
-
|
|
1121
|
+
fs4.writeFileSync(uiStatusFile, JSON.stringify(status, null, 2), "utf-8");
|
|
985
1122
|
}
|
|
986
1123
|
function openBrowser(url) {
|
|
987
1124
|
if (process.platform === "win32") {
|