codex-to-im 1.0.27 → 1.0.29
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 +55 -20
- package/dist/daemon.mjs +239 -44
- package/dist/ui-server.mjs +60 -25
- package/package.json +1 -1
package/dist/cli.mjs
CHANGED
|
@@ -86,6 +86,30 @@ function isProcessAlive(pid) {
|
|
|
86
86
|
return false;
|
|
87
87
|
}
|
|
88
88
|
}
|
|
89
|
+
function collectTrackedBridgePids(bridgePid, statusPid) {
|
|
90
|
+
const unique = /* @__PURE__ */ new Set();
|
|
91
|
+
for (const pid of [bridgePid, statusPid]) {
|
|
92
|
+
if (Number.isFinite(pid) && pid > 0) {
|
|
93
|
+
unique.add(pid);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
return [...unique];
|
|
97
|
+
}
|
|
98
|
+
function resolveTrackedBridgePid(bridgePid, statusPid, isAlive = isProcessAlive) {
|
|
99
|
+
if (isAlive(bridgePid)) return bridgePid;
|
|
100
|
+
if (isAlive(statusPid)) return statusPid;
|
|
101
|
+
return bridgePid ?? statusPid;
|
|
102
|
+
}
|
|
103
|
+
function getTrackedBridgePids(status) {
|
|
104
|
+
const resolvedStatus = status ?? readJsonFile(bridgeStatusFile, { running: false });
|
|
105
|
+
return collectTrackedBridgePids(readPid(bridgePidFile), resolvedStatus.pid);
|
|
106
|
+
}
|
|
107
|
+
function clearBridgePidFile() {
|
|
108
|
+
try {
|
|
109
|
+
fs2.unlinkSync(bridgePidFile);
|
|
110
|
+
} catch {
|
|
111
|
+
}
|
|
112
|
+
}
|
|
89
113
|
function sleep(ms) {
|
|
90
114
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
91
115
|
}
|
|
@@ -246,7 +270,7 @@ function getCurrentUiServerUrl() {
|
|
|
246
270
|
}
|
|
247
271
|
function getBridgeStatus() {
|
|
248
272
|
const status = readJsonFile(bridgeStatusFile, { running: false });
|
|
249
|
-
const pid = readPid(bridgePidFile)
|
|
273
|
+
const pid = resolveTrackedBridgePid(readPid(bridgePidFile), status.pid);
|
|
250
274
|
if (!isProcessAlive(pid)) {
|
|
251
275
|
return {
|
|
252
276
|
...status,
|
|
@@ -311,7 +335,11 @@ async function waitForUiServer(timeoutMs = 15e3) {
|
|
|
311
335
|
async function startBridge() {
|
|
312
336
|
ensureDirs();
|
|
313
337
|
const current = getBridgeStatus();
|
|
314
|
-
|
|
338
|
+
const extraAlivePids = getTrackedBridgePids(current).filter((pid) => pid !== current.pid && isProcessAlive(pid));
|
|
339
|
+
if (current.running && extraAlivePids.length === 0) return current;
|
|
340
|
+
if (current.running && extraAlivePids.length > 0) {
|
|
341
|
+
await stopBridge();
|
|
342
|
+
}
|
|
315
343
|
const daemonEntry = path2.join(packageRoot, "dist", "daemon.mjs");
|
|
316
344
|
if (!fs2.existsSync(daemonEntry)) {
|
|
317
345
|
throw new Error(`Daemon bundle not found at ${daemonEntry}. Run npm run build first.`);
|
|
@@ -333,31 +361,38 @@ async function startBridge() {
|
|
|
333
361
|
return status;
|
|
334
362
|
}
|
|
335
363
|
async function stopBridge() {
|
|
336
|
-
const status =
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
364
|
+
const status = readJsonFile(bridgeStatusFile, { running: false });
|
|
365
|
+
const pids = getTrackedBridgePids(status).filter((pid) => isProcessAlive(pid));
|
|
366
|
+
if (pids.length === 0) {
|
|
367
|
+
clearBridgePidFile();
|
|
368
|
+
return { ...getBridgeStatus(), running: false };
|
|
369
|
+
}
|
|
370
|
+
for (const pid of pids) {
|
|
371
|
+
if (process.platform === "win32") {
|
|
372
|
+
await new Promise((resolve) => {
|
|
373
|
+
const killer = spawn("cmd", ["/c", "taskkill", "/PID", String(pid), "/T", "/F"], {
|
|
374
|
+
stdio: "ignore",
|
|
375
|
+
...WINDOWS_HIDE
|
|
376
|
+
});
|
|
377
|
+
killer.on("exit", () => resolve());
|
|
378
|
+
killer.on("error", () => resolve());
|
|
345
379
|
});
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
process.kill(status.pid, "SIGTERM");
|
|
352
|
-
} catch {
|
|
380
|
+
} else {
|
|
381
|
+
try {
|
|
382
|
+
process.kill(pid, "SIGTERM");
|
|
383
|
+
} catch {
|
|
384
|
+
}
|
|
353
385
|
}
|
|
354
386
|
}
|
|
355
387
|
const startedAt = Date.now();
|
|
356
388
|
while (Date.now() - startedAt < 1e4) {
|
|
357
|
-
|
|
358
|
-
|
|
389
|
+
if (pids.every((pid) => !isProcessAlive(pid))) {
|
|
390
|
+
clearBridgePidFile();
|
|
391
|
+
return getBridgeStatus();
|
|
392
|
+
}
|
|
359
393
|
await sleep(300);
|
|
360
394
|
}
|
|
395
|
+
clearBridgePidFile();
|
|
361
396
|
return getBridgeStatus();
|
|
362
397
|
}
|
|
363
398
|
async function getBridgeAutostartStatus() {
|
package/dist/daemon.mjs
CHANGED
|
@@ -4255,6 +4255,7 @@ function getRegisteredTypes() {
|
|
|
4255
4255
|
// src/lib/bridge/bridge-manager.ts
|
|
4256
4256
|
import fs8 from "node:fs";
|
|
4257
4257
|
import path11 from "node:path";
|
|
4258
|
+
import { inspect } from "node:util";
|
|
4258
4259
|
|
|
4259
4260
|
// src/lib/bridge/adapters/telegram-adapter.ts
|
|
4260
4261
|
import crypto from "crypto";
|
|
@@ -14233,12 +14234,6 @@ var WeixinAdapter = class extends BaseChannelAdapter {
|
|
|
14233
14234
|
}
|
|
14234
14235
|
}
|
|
14235
14236
|
validateConfig() {
|
|
14236
|
-
const linkedAccounts = this.filterConfiguredAccounts(
|
|
14237
|
-
listWeixinAccounts().filter((account) => account.enabled && account.token)
|
|
14238
|
-
);
|
|
14239
|
-
if (linkedAccounts.length === 0) {
|
|
14240
|
-
return "No linked WeChat account. Run the WeChat QR login helper first.";
|
|
14241
|
-
}
|
|
14242
14237
|
return null;
|
|
14243
14238
|
}
|
|
14244
14239
|
isAuthorized(_userId, _chatId) {
|
|
@@ -14729,8 +14724,8 @@ function walkSessionFiles(dirPath, target) {
|
|
|
14729
14724
|
}
|
|
14730
14725
|
}
|
|
14731
14726
|
function isDesktopLike(meta) {
|
|
14732
|
-
const originator = meta?.originator
|
|
14733
|
-
const source = meta?.source
|
|
14727
|
+
const originator = typeof meta?.originator === "string" ? meta.originator.toLowerCase() : "";
|
|
14728
|
+
const source = typeof meta?.source === "string" ? meta.source.toLowerCase() : "";
|
|
14734
14729
|
if (source === "exec") return false;
|
|
14735
14730
|
return originator.includes("desktop") || source === "vscode" || source === "desktop";
|
|
14736
14731
|
}
|
|
@@ -14862,9 +14857,9 @@ function parseDesktopSession(filePath, threadIndexEntries, archivedThreadIds) {
|
|
|
14862
14857
|
threadId,
|
|
14863
14858
|
filePath,
|
|
14864
14859
|
cwd,
|
|
14865
|
-
originator: parsed.payload.originator
|
|
14866
|
-
source: parsed.payload.source
|
|
14867
|
-
cliVersion: parsed.payload.cli_version
|
|
14860
|
+
originator: typeof parsed.payload.originator === "string" ? parsed.payload.originator : "Codex Desktop",
|
|
14861
|
+
source: typeof parsed.payload.source === "string" ? parsed.payload.source : void 0,
|
|
14862
|
+
cliVersion: typeof parsed.payload.cli_version === "string" ? parsed.payload.cli_version : void 0,
|
|
14868
14863
|
firstSeenAt,
|
|
14869
14864
|
lastEventAt,
|
|
14870
14865
|
title,
|
|
@@ -17018,6 +17013,9 @@ var MODE_OPTIONS_TEXT = "\u53EF\u9009\uFF1A`code`\uFF08\u76F4\u63A5\u6267\u884C\
|
|
|
17018
17013
|
var REASONING_OPTIONS_TEXT = "\u53EF\u9009\uFF1A`1=minimal` `2=low` `3=medium` `4=high` `5=xhigh`";
|
|
17019
17014
|
var DEFAULT_DESKTOP_THREAD_LIST_LIMIT = 10;
|
|
17020
17015
|
var MAX_DESKTOP_THREAD_LIST_LIMIT = 200;
|
|
17016
|
+
var DANGLING_MIRROR_THREAD_RETRY_LIMIT = 3;
|
|
17017
|
+
var MIRROR_FAILURE_SUSPEND_MS = 6e4;
|
|
17018
|
+
var MIRROR_FAILURE_SUSPEND_THRESHOLD = 3;
|
|
17021
17019
|
var MIRROR_POLL_INTERVAL_MS = 2500;
|
|
17022
17020
|
var MIRROR_WATCH_DEBOUNCE_MS = 350;
|
|
17023
17021
|
var MIRROR_EVENT_BATCH_LIMIT = 8;
|
|
@@ -17027,6 +17025,7 @@ var INTERACTIVE_IDLE_REMINDER_MS = 6e5;
|
|
|
17027
17025
|
var MIRROR_IDLE_TIMEOUT_MS = 6e5;
|
|
17028
17026
|
var AVAILABLE_CODEX_MODELS = listSelectableCodexModels();
|
|
17029
17027
|
var AVAILABLE_CODEX_MODEL_MAP = new Map(AVAILABLE_CODEX_MODELS.map((model) => [model.slug, model]));
|
|
17028
|
+
var INVALID_ADAPTER_WARNING_CACHE = /* @__PURE__ */ new Map();
|
|
17030
17029
|
function generateDraftId() {
|
|
17031
17030
|
return Math.floor(Math.random() * 2147483646) + 1;
|
|
17032
17031
|
}
|
|
@@ -17109,8 +17108,41 @@ function resolveByIndexOrPrefix(raw, items, getId) {
|
|
|
17109
17108
|
}
|
|
17110
17109
|
return { match: null, ambiguous: false };
|
|
17111
17110
|
}
|
|
17111
|
+
function describeUnknownError(error) {
|
|
17112
|
+
if (error instanceof Error) {
|
|
17113
|
+
return error.stack || `${error.name}: ${error.message}`;
|
|
17114
|
+
}
|
|
17115
|
+
if (error === null) return "null";
|
|
17116
|
+
if (typeof error === "undefined") return "undefined";
|
|
17117
|
+
if (typeof error === "object") {
|
|
17118
|
+
const ctor = error?.constructor?.name;
|
|
17119
|
+
const rendered = inspect(error, {
|
|
17120
|
+
depth: 4,
|
|
17121
|
+
breakLength: Infinity,
|
|
17122
|
+
compact: true
|
|
17123
|
+
});
|
|
17124
|
+
return ctor && ctor !== "Object" ? `${ctor} ${rendered}` : rendered;
|
|
17125
|
+
}
|
|
17126
|
+
return String(error);
|
|
17127
|
+
}
|
|
17112
17128
|
function getDisplayedDesktopThreads(limit = DEFAULT_DESKTOP_THREAD_LIST_LIMIT) {
|
|
17113
|
-
|
|
17129
|
+
try {
|
|
17130
|
+
return listDesktopSessions(limit);
|
|
17131
|
+
} catch (error) {
|
|
17132
|
+
console.error("[bridge-manager] Failed to list desktop sessions:", error);
|
|
17133
|
+
return null;
|
|
17134
|
+
}
|
|
17135
|
+
}
|
|
17136
|
+
function getDesktopSessionByThreadIdSafe(threadId, context) {
|
|
17137
|
+
try {
|
|
17138
|
+
return getDesktopSessionByThreadId(threadId);
|
|
17139
|
+
} catch (error) {
|
|
17140
|
+
console.error(
|
|
17141
|
+
`[bridge-manager] Failed to load desktop thread ${threadId} during ${context}:`,
|
|
17142
|
+
error
|
|
17143
|
+
);
|
|
17144
|
+
return null;
|
|
17145
|
+
}
|
|
17114
17146
|
}
|
|
17115
17147
|
function parseDesktopThreadListArgs(args) {
|
|
17116
17148
|
const trimmed = args.trim().toLowerCase();
|
|
@@ -17522,7 +17554,7 @@ async function summarizeHistory(currentBinding) {
|
|
|
17522
17554
|
}
|
|
17523
17555
|
function getDesktopThreadTitle(threadId) {
|
|
17524
17556
|
if (!threadId) return null;
|
|
17525
|
-
return
|
|
17557
|
+
return getDesktopSessionByThreadIdSafe(threadId, "status lookup")?.title || null;
|
|
17526
17558
|
}
|
|
17527
17559
|
function formatCommandPath(cwd) {
|
|
17528
17560
|
return cwd?.trim() || "~";
|
|
@@ -17684,6 +17716,7 @@ function getState() {
|
|
|
17684
17716
|
g[GLOBAL_KEY] = {
|
|
17685
17717
|
adapters: /* @__PURE__ */ new Map(),
|
|
17686
17718
|
adapterMeta: /* @__PURE__ */ new Map(),
|
|
17719
|
+
invalidAdapters: /* @__PURE__ */ new Map(),
|
|
17687
17720
|
running: false,
|
|
17688
17721
|
startedAt: null,
|
|
17689
17722
|
loopAborts: /* @__PURE__ */ new Map(),
|
|
@@ -17706,6 +17739,9 @@ function getState() {
|
|
|
17706
17739
|
if (!g[GLOBAL_KEY].mirrorSubscriptions) {
|
|
17707
17740
|
g[GLOBAL_KEY].mirrorSubscriptions = /* @__PURE__ */ new Map();
|
|
17708
17741
|
}
|
|
17742
|
+
if (!g[GLOBAL_KEY].invalidAdapters) {
|
|
17743
|
+
g[GLOBAL_KEY].invalidAdapters = /* @__PURE__ */ new Map();
|
|
17744
|
+
}
|
|
17709
17745
|
if (!g[GLOBAL_KEY].queuedCounts) {
|
|
17710
17746
|
g[GLOBAL_KEY].queuedCounts = /* @__PURE__ */ new Map();
|
|
17711
17747
|
}
|
|
@@ -17802,6 +17838,20 @@ function decrementQueuedCount(sessionId) {
|
|
|
17802
17838
|
}
|
|
17803
17839
|
syncSessionRuntimeState(sessionId);
|
|
17804
17840
|
}
|
|
17841
|
+
function resetPersistedInteractiveRuntimeState() {
|
|
17842
|
+
const { store } = getBridgeContext();
|
|
17843
|
+
for (const session of store.listSessions()) {
|
|
17844
|
+
const queuedCount = session.queued_count && session.queued_count > 0 ? session.queued_count : 0;
|
|
17845
|
+
if (queuedCount === 0 && session.runtime_status !== "running" && session.runtime_status !== "queued") {
|
|
17846
|
+
continue;
|
|
17847
|
+
}
|
|
17848
|
+
store.updateSession(session.id, {
|
|
17849
|
+
queued_count: 0,
|
|
17850
|
+
runtime_status: "idle",
|
|
17851
|
+
last_runtime_update_at: nowIso2()
|
|
17852
|
+
});
|
|
17853
|
+
}
|
|
17854
|
+
}
|
|
17805
17855
|
function formatRuntimeStatus(session) {
|
|
17806
17856
|
const status = session?.runtime_status || "idle";
|
|
17807
17857
|
const queuedCount = session?.queued_count && session.queued_count > 0 ? session.queued_count : 0;
|
|
@@ -18051,7 +18101,9 @@ function scheduleMirrorWake(delayMs = MIRROR_WATCH_DEBOUNCE_MS) {
|
|
|
18051
18101
|
if (state.mirrorWakeTimer) return;
|
|
18052
18102
|
state.mirrorWakeTimer = setTimeout(() => {
|
|
18053
18103
|
state.mirrorWakeTimer = null;
|
|
18054
|
-
void reconcileMirrorSubscriptions()
|
|
18104
|
+
void reconcileMirrorSubscriptions().catch((err) => {
|
|
18105
|
+
console.error("[bridge-manager] Mirror wake reconcile failed:", describeUnknownError(err));
|
|
18106
|
+
});
|
|
18055
18107
|
}, delayMs);
|
|
18056
18108
|
}
|
|
18057
18109
|
function watchMirrorFile(subscription, filePath) {
|
|
@@ -18089,6 +18141,16 @@ function syncMirrorSessionState(sessionId) {
|
|
|
18089
18141
|
mirror_last_event_at: deliveredAt
|
|
18090
18142
|
});
|
|
18091
18143
|
}
|
|
18144
|
+
function syncMirrorSessionStateSafe(sessionId, context) {
|
|
18145
|
+
try {
|
|
18146
|
+
syncMirrorSessionState(sessionId);
|
|
18147
|
+
} catch (error) {
|
|
18148
|
+
console.error(
|
|
18149
|
+
`[bridge-manager] Failed to sync mirror session state for ${sessionId} during ${context}:`,
|
|
18150
|
+
describeUnknownError(error)
|
|
18151
|
+
);
|
|
18152
|
+
}
|
|
18153
|
+
}
|
|
18092
18154
|
function getMirrorAssistantRuntimeLabel() {
|
|
18093
18155
|
const { store } = getBridgeContext();
|
|
18094
18156
|
const runtime = (store.getSetting("bridge_runtime") || "codex").trim().toLowerCase();
|
|
@@ -18453,7 +18515,17 @@ function removeMirrorSubscription(bindingId) {
|
|
|
18453
18515
|
stopMirrorStreaming(existing);
|
|
18454
18516
|
closeMirrorWatcher(existing);
|
|
18455
18517
|
state.mirrorSubscriptions.delete(bindingId);
|
|
18456
|
-
|
|
18518
|
+
syncMirrorSessionStateSafe(existing.sessionId, "mirror subscription removal");
|
|
18519
|
+
}
|
|
18520
|
+
function clearDanglingMirrorThread(subscription, reason) {
|
|
18521
|
+
const { store } = getBridgeContext();
|
|
18522
|
+
const session = store.getSession(subscription.sessionId);
|
|
18523
|
+
const currentThreadId = session?.sdk_session_id || subscription.threadId;
|
|
18524
|
+
console.warn(
|
|
18525
|
+
`[bridge-manager] Clearing dangling desktop thread ${currentThreadId} for session ${subscription.sessionId}: ${reason}`
|
|
18526
|
+
);
|
|
18527
|
+
store.updateSdkSessionId(subscription.sessionId, "");
|
|
18528
|
+
removeMirrorSubscription(subscription.bindingId);
|
|
18457
18529
|
}
|
|
18458
18530
|
function upsertMirrorSubscription(binding) {
|
|
18459
18531
|
const { store } = getBridgeContext();
|
|
@@ -18468,7 +18540,7 @@ function upsertMirrorSubscription(binding) {
|
|
|
18468
18540
|
removeMirrorSubscription(binding.id);
|
|
18469
18541
|
return;
|
|
18470
18542
|
}
|
|
18471
|
-
const desktopSession =
|
|
18543
|
+
const desktopSession = getDesktopSessionByThreadIdSafe(threadId, "mirror subscription sync");
|
|
18472
18544
|
const filePath = desktopSession?.filePath || null;
|
|
18473
18545
|
const existing = state.mirrorSubscriptions.get(binding.id);
|
|
18474
18546
|
if (!existing) {
|
|
@@ -18493,11 +18565,14 @@ function upsertMirrorSubscription(binding) {
|
|
|
18493
18565
|
trailingText: "",
|
|
18494
18566
|
activeMirrorTurnId: null,
|
|
18495
18567
|
bufferedRecords: [],
|
|
18496
|
-
pendingTurn: null
|
|
18568
|
+
pendingTurn: null,
|
|
18569
|
+
missingThreadPolls: 0,
|
|
18570
|
+
consecutiveFailures: 0,
|
|
18571
|
+
suspendedUntil: null
|
|
18497
18572
|
};
|
|
18498
18573
|
watchMirrorFile(created, filePath);
|
|
18499
18574
|
state.mirrorSubscriptions.set(binding.id, created);
|
|
18500
|
-
|
|
18575
|
+
syncMirrorSessionStateSafe(binding.codepilotSessionId, "mirror subscription create");
|
|
18501
18576
|
return;
|
|
18502
18577
|
}
|
|
18503
18578
|
const previousSessionId = existing.sessionId;
|
|
@@ -18515,18 +18590,23 @@ function upsertMirrorSubscription(binding) {
|
|
|
18515
18590
|
existing.lastDeliveredAt = session.mirror_last_event_at || null;
|
|
18516
18591
|
existing.dirty = true;
|
|
18517
18592
|
existing.pendingTurn = null;
|
|
18593
|
+
existing.missingThreadPolls = 0;
|
|
18594
|
+
existing.consecutiveFailures = 0;
|
|
18595
|
+
existing.suspendedUntil = null;
|
|
18518
18596
|
resetMirrorReadState(existing);
|
|
18519
18597
|
} else if (filePathChanged) {
|
|
18520
18598
|
stopMirrorStreaming(existing);
|
|
18521
18599
|
existing.dirty = true;
|
|
18522
18600
|
existing.pendingTurn = null;
|
|
18601
|
+
existing.consecutiveFailures = 0;
|
|
18602
|
+
existing.suspendedUntil = null;
|
|
18523
18603
|
resetMirrorReadState(existing);
|
|
18524
18604
|
}
|
|
18525
18605
|
watchMirrorFile(existing, filePath);
|
|
18526
18606
|
if (previousSessionId !== binding.codepilotSessionId) {
|
|
18527
|
-
|
|
18607
|
+
syncMirrorSessionStateSafe(previousSessionId, "mirror subscription rebind previous session");
|
|
18528
18608
|
}
|
|
18529
|
-
|
|
18609
|
+
syncMirrorSessionStateSafe(binding.codepilotSessionId, "mirror subscription upsert");
|
|
18530
18610
|
}
|
|
18531
18611
|
function syncMirrorSubscriptionSet() {
|
|
18532
18612
|
const { store } = getBridgeContext();
|
|
@@ -18540,7 +18620,14 @@ function syncMirrorSubscriptionSet() {
|
|
|
18540
18620
|
const desiredIds = /* @__PURE__ */ new Set();
|
|
18541
18621
|
for (const binding of desiredBindings) {
|
|
18542
18622
|
desiredIds.add(binding.id);
|
|
18543
|
-
|
|
18623
|
+
try {
|
|
18624
|
+
upsertMirrorSubscription(binding);
|
|
18625
|
+
} catch (error) {
|
|
18626
|
+
console.error(
|
|
18627
|
+
`[bridge-manager] Failed to sync mirror subscription for binding ${binding.id}:`,
|
|
18628
|
+
error
|
|
18629
|
+
);
|
|
18630
|
+
}
|
|
18544
18631
|
}
|
|
18545
18632
|
for (const bindingId of Array.from(state.mirrorSubscriptions.keys())) {
|
|
18546
18633
|
if (!desiredIds.has(bindingId)) {
|
|
@@ -18555,7 +18642,29 @@ async function reconcileMirrorSubscription(subscription) {
|
|
|
18555
18642
|
removeMirrorSubscription(subscription.bindingId);
|
|
18556
18643
|
return;
|
|
18557
18644
|
}
|
|
18558
|
-
|
|
18645
|
+
if (subscription.suspendedUntil && Date.now() < subscription.suspendedUntil) {
|
|
18646
|
+
syncMirrorSessionStateSafe(subscription.sessionId, "mirror suspension");
|
|
18647
|
+
return;
|
|
18648
|
+
}
|
|
18649
|
+
if (subscription.suspendedUntil) {
|
|
18650
|
+
subscription.suspendedUntil = null;
|
|
18651
|
+
}
|
|
18652
|
+
const desktopSession = getDesktopSessionByThreadIdSafe(
|
|
18653
|
+
subscription.threadId,
|
|
18654
|
+
"mirror reconcile"
|
|
18655
|
+
);
|
|
18656
|
+
if (!desktopSession) {
|
|
18657
|
+
subscription.missingThreadPolls += 1;
|
|
18658
|
+
if (subscription.missingThreadPolls >= DANGLING_MIRROR_THREAD_RETRY_LIMIT) {
|
|
18659
|
+
clearDanglingMirrorThread(
|
|
18660
|
+
subscription,
|
|
18661
|
+
"desktop thread no longer exists locally"
|
|
18662
|
+
);
|
|
18663
|
+
return;
|
|
18664
|
+
}
|
|
18665
|
+
} else {
|
|
18666
|
+
subscription.missingThreadPolls = 0;
|
|
18667
|
+
}
|
|
18559
18668
|
const filePathChanged = subscription.filePath !== (desktopSession?.filePath || null);
|
|
18560
18669
|
subscription.filePath = desktopSession?.filePath || null;
|
|
18561
18670
|
subscription.status = subscription.filePath ? "watching" : "stale";
|
|
@@ -18566,7 +18675,7 @@ async function reconcileMirrorSubscription(subscription) {
|
|
|
18566
18675
|
watchMirrorFile(subscription, subscription.filePath);
|
|
18567
18676
|
subscription.lastReconciledAt = nowIso2();
|
|
18568
18677
|
if (!subscription.filePath) {
|
|
18569
|
-
|
|
18678
|
+
syncMirrorSessionStateSafe(subscription.sessionId, "mirror reconcile without file");
|
|
18570
18679
|
return;
|
|
18571
18680
|
}
|
|
18572
18681
|
const snapshot = statMirrorFile(subscription.filePath);
|
|
@@ -18574,12 +18683,12 @@ async function reconcileMirrorSubscription(subscription) {
|
|
|
18574
18683
|
subscription.status = "stale";
|
|
18575
18684
|
subscription.dirty = true;
|
|
18576
18685
|
resetMirrorReadState(subscription);
|
|
18577
|
-
|
|
18686
|
+
syncMirrorSessionStateSafe(subscription.sessionId, "mirror reconcile missing snapshot");
|
|
18578
18687
|
return;
|
|
18579
18688
|
}
|
|
18580
18689
|
const unchanged = !subscription.dirty && subscription.fileIdentity === snapshot.identity && subscription.fileSize === snapshot.size && subscription.fileMtimeMs === snapshot.mtimeMs;
|
|
18581
18690
|
if (unchanged && !hasPendingMirrorWork(subscription)) {
|
|
18582
|
-
|
|
18691
|
+
syncMirrorSessionStateSafe(subscription.sessionId, "mirror reconcile unchanged snapshot");
|
|
18583
18692
|
return;
|
|
18584
18693
|
}
|
|
18585
18694
|
let deliverableRecords = [];
|
|
@@ -18634,13 +18743,13 @@ async function reconcileMirrorSubscription(subscription) {
|
|
|
18634
18743
|
console.warn("[bridge-manager] Mirror delivery failed:", error instanceof Error ? error.message : error);
|
|
18635
18744
|
}
|
|
18636
18745
|
}
|
|
18637
|
-
|
|
18746
|
+
syncMirrorSessionStateSafe(subscription.sessionId, "mirror reconcile active task");
|
|
18638
18747
|
return;
|
|
18639
18748
|
}
|
|
18640
18749
|
const finalizedTurns = timedOutTurn ? [timedOutTurn] : [];
|
|
18641
18750
|
finalizedTurns.push(...consumeBufferedMirrorTurns(subscription));
|
|
18642
18751
|
if (finalizedTurns.length === 0) {
|
|
18643
|
-
|
|
18752
|
+
syncMirrorSessionStateSafe(subscription.sessionId, "mirror reconcile no finalized turns");
|
|
18644
18753
|
return;
|
|
18645
18754
|
}
|
|
18646
18755
|
try {
|
|
@@ -18649,29 +18758,66 @@ async function reconcileMirrorSubscription(subscription) {
|
|
|
18649
18758
|
subscription.dirty = true;
|
|
18650
18759
|
console.warn("[bridge-manager] Mirror delivery failed:", error instanceof Error ? error.message : error);
|
|
18651
18760
|
}
|
|
18652
|
-
|
|
18761
|
+
syncMirrorSessionStateSafe(subscription.sessionId, "mirror reconcile delivered turns");
|
|
18653
18762
|
}
|
|
18654
18763
|
async function reconcileMirrorSubscriptions() {
|
|
18655
18764
|
const state = getState();
|
|
18656
18765
|
if (!state.running || state.mirrorSyncInFlight) return;
|
|
18657
18766
|
state.mirrorSyncInFlight = true;
|
|
18767
|
+
let stage = "sync-start";
|
|
18658
18768
|
try {
|
|
18659
|
-
|
|
18660
|
-
|
|
18769
|
+
try {
|
|
18770
|
+
stage = "sync-subscription-set";
|
|
18771
|
+
syncMirrorSubscriptionSet();
|
|
18772
|
+
} catch (error) {
|
|
18773
|
+
console.error("[bridge-manager] Mirror subscription set reconcile failed:", error);
|
|
18774
|
+
return;
|
|
18775
|
+
}
|
|
18776
|
+
stage = "snapshot-subscriptions";
|
|
18777
|
+
const subscriptions = Array.from(state.mirrorSubscriptions.values());
|
|
18778
|
+
for (const subscription of subscriptions) {
|
|
18779
|
+
stage = `subscription:${subscription.bindingId}`;
|
|
18661
18780
|
try {
|
|
18662
18781
|
await reconcileMirrorSubscription(subscription);
|
|
18782
|
+
subscription.consecutiveFailures = 0;
|
|
18783
|
+
subscription.suspendedUntil = null;
|
|
18663
18784
|
} catch (error) {
|
|
18664
|
-
|
|
18665
|
-
|
|
18666
|
-
|
|
18667
|
-
|
|
18668
|
-
|
|
18669
|
-
|
|
18670
|
-
|
|
18671
|
-
|
|
18672
|
-
|
|
18785
|
+
try {
|
|
18786
|
+
stopMirrorStreaming(subscription, "interrupted");
|
|
18787
|
+
subscription.pendingTurn = null;
|
|
18788
|
+
subscription.bufferedRecords = [];
|
|
18789
|
+
subscription.status = "stale";
|
|
18790
|
+
subscription.dirty = false;
|
|
18791
|
+
subscription.consecutiveFailures += 1;
|
|
18792
|
+
if (subscription.consecutiveFailures >= MIRROR_FAILURE_SUSPEND_THRESHOLD) {
|
|
18793
|
+
subscription.suspendedUntil = Date.now() + MIRROR_FAILURE_SUSPEND_MS;
|
|
18794
|
+
console.warn(
|
|
18795
|
+
`[bridge-manager] Mirror subscription for thread ${subscription.threadId} is suspended for ${Math.round(MIRROR_FAILURE_SUSPEND_MS / 1e3)}s after ${subscription.consecutiveFailures} consecutive failures`
|
|
18796
|
+
);
|
|
18797
|
+
}
|
|
18798
|
+
console.error(
|
|
18799
|
+
`[bridge-manager] Mirror reconcile failed for thread ${subscription.threadId}:`,
|
|
18800
|
+
describeUnknownError(error)
|
|
18801
|
+
);
|
|
18802
|
+
syncMirrorSessionStateSafe(subscription.sessionId, "mirror reconcile failure");
|
|
18803
|
+
} catch (recoveryError) {
|
|
18804
|
+
console.error(
|
|
18805
|
+
`[bridge-manager] Mirror reconcile recovery failed for thread ${subscription.threadId}:`,
|
|
18806
|
+
describeUnknownError(recoveryError)
|
|
18807
|
+
);
|
|
18808
|
+
console.error(
|
|
18809
|
+
`[bridge-manager] Original mirror reconcile error for thread ${subscription.threadId}:`,
|
|
18810
|
+
describeUnknownError(error)
|
|
18811
|
+
);
|
|
18812
|
+
}
|
|
18673
18813
|
}
|
|
18674
18814
|
}
|
|
18815
|
+
stage = "sync-complete";
|
|
18816
|
+
} catch (error) {
|
|
18817
|
+
console.error(
|
|
18818
|
+
`[bridge-manager] Mirror reconcile failed during ${stage}:`,
|
|
18819
|
+
describeUnknownError(error)
|
|
18820
|
+
);
|
|
18675
18821
|
} finally {
|
|
18676
18822
|
state.mirrorSyncInFlight = false;
|
|
18677
18823
|
}
|
|
@@ -18760,6 +18906,8 @@ function listEnabledAdapterInstances() {
|
|
|
18760
18906
|
async function stopAdapterInstance(channelType) {
|
|
18761
18907
|
const state = getState();
|
|
18762
18908
|
const adapter = state.adapters.get(channelType);
|
|
18909
|
+
state.invalidAdapters.delete(channelType);
|
|
18910
|
+
INVALID_ADAPTER_WARNING_CACHE.delete(channelType);
|
|
18763
18911
|
if (!adapter) return;
|
|
18764
18912
|
state.loopAborts.get(channelType)?.abort();
|
|
18765
18913
|
state.loopAborts.delete(channelType);
|
|
@@ -18785,6 +18933,14 @@ async function syncConfiguredAdapters(options) {
|
|
|
18785
18933
|
await stopAdapterInstance(existingKey);
|
|
18786
18934
|
changed = true;
|
|
18787
18935
|
}
|
|
18936
|
+
for (const invalidKey of Array.from(state.invalidAdapters.keys())) {
|
|
18937
|
+
if (desiredKeys.has(invalidKey)) continue;
|
|
18938
|
+
state.invalidAdapters.delete(invalidKey);
|
|
18939
|
+
}
|
|
18940
|
+
for (const invalidKey of Array.from(INVALID_ADAPTER_WARNING_CACHE.keys())) {
|
|
18941
|
+
if (desiredKeys.has(invalidKey)) continue;
|
|
18942
|
+
INVALID_ADAPTER_WARNING_CACHE.delete(invalidKey);
|
|
18943
|
+
}
|
|
18788
18944
|
for (const instance of desiredInstances) {
|
|
18789
18945
|
const existing = state.adapters.get(instance.id);
|
|
18790
18946
|
const desiredFingerprint = desiredFingerprints.get(instance.id) || "";
|
|
@@ -18798,9 +18954,16 @@ async function syncConfiguredAdapters(options) {
|
|
|
18798
18954
|
if (!adapter) continue;
|
|
18799
18955
|
const configError = adapter.validateConfig();
|
|
18800
18956
|
if (configError) {
|
|
18801
|
-
|
|
18957
|
+
const invalidSignature = `${desiredFingerprint}:${configError}`;
|
|
18958
|
+
if (INVALID_ADAPTER_WARNING_CACHE.get(instance.id) !== invalidSignature) {
|
|
18959
|
+
console.warn(`[bridge-manager] ${instance.id} adapter not valid:`, configError);
|
|
18960
|
+
INVALID_ADAPTER_WARNING_CACHE.set(instance.id, invalidSignature);
|
|
18961
|
+
state.invalidAdapters.set(instance.id, invalidSignature);
|
|
18962
|
+
}
|
|
18802
18963
|
continue;
|
|
18803
18964
|
}
|
|
18965
|
+
state.invalidAdapters.delete(instance.id);
|
|
18966
|
+
INVALID_ADAPTER_WARNING_CACHE.delete(instance.id);
|
|
18804
18967
|
try {
|
|
18805
18968
|
state.adapters.set(instance.id, adapter);
|
|
18806
18969
|
state.adapterMeta.set(instance.id, {
|
|
@@ -18833,6 +18996,7 @@ async function start() {
|
|
|
18833
18996
|
console.log("[bridge-manager] Bridge not enabled (remote_bridge_enabled != true)");
|
|
18834
18997
|
return;
|
|
18835
18998
|
}
|
|
18999
|
+
resetPersistedInteractiveRuntimeState();
|
|
18836
19000
|
await syncConfiguredAdapters({ startLoops: false });
|
|
18837
19001
|
const startedCount = state.adapters.size;
|
|
18838
19002
|
if (startedCount === 0) {
|
|
@@ -18859,11 +19023,11 @@ async function start() {
|
|
|
18859
19023
|
}, 5e3);
|
|
18860
19024
|
state.mirrorPollTimer = setInterval(() => {
|
|
18861
19025
|
void reconcileMirrorSubscriptions().catch((err) => {
|
|
18862
|
-
console.error("[bridge-manager] Mirror reconcile failed:", err);
|
|
19026
|
+
console.error("[bridge-manager] Mirror reconcile failed:", describeUnknownError(err));
|
|
18863
19027
|
});
|
|
18864
19028
|
}, MIRROR_POLL_INTERVAL_MS);
|
|
18865
19029
|
void reconcileMirrorSubscriptions().catch((err) => {
|
|
18866
|
-
console.error("[bridge-manager] Initial mirror reconcile failed:", err);
|
|
19030
|
+
console.error("[bridge-manager] Initial mirror reconcile failed:", describeUnknownError(err));
|
|
18867
19031
|
});
|
|
18868
19032
|
console.log(`[bridge-manager] Bridge started with ${startedCount} adapter(s)`);
|
|
18869
19033
|
}
|
|
@@ -18896,6 +19060,8 @@ async function stop() {
|
|
|
18896
19060
|
state.mirrorSuppressUntil.clear();
|
|
18897
19061
|
state.mirrorIgnoredTurnIds.clear();
|
|
18898
19062
|
state.queuedCounts.clear();
|
|
19063
|
+
state.invalidAdapters.clear();
|
|
19064
|
+
INVALID_ADAPTER_WARNING_CACHE.clear();
|
|
18899
19065
|
for (const sessionId of activeSessionIds) {
|
|
18900
19066
|
syncSessionRuntimeState(sessionId);
|
|
18901
19067
|
}
|
|
@@ -19388,6 +19554,10 @@ async function handleCommand(adapter, msg, text2) {
|
|
|
19388
19554
|
}
|
|
19389
19555
|
if (args === "all") {
|
|
19390
19556
|
const desktopSessions = getDisplayedDesktopThreads(MAX_DESKTOP_THREAD_LIST_LIMIT);
|
|
19557
|
+
if (!desktopSessions) {
|
|
19558
|
+
response = "\u8BFB\u53D6\u684C\u9762\u4F1A\u8BDD\u5217\u8868\u5931\u8D25\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5\u3002";
|
|
19559
|
+
break;
|
|
19560
|
+
}
|
|
19391
19561
|
if (desktopSessions.length === 0) {
|
|
19392
19562
|
response = "\u6CA1\u6709\u627E\u5230\u684C\u9762\u4F1A\u8BDD\u3002\u5148\u5728 Codex Desktop App \u4E2D\u6253\u5F00\u4E00\u4E2A\u4F1A\u8BDD\uFF0C\u518D\u56DE\u6765\u8BD5\u4E00\u6B21\u3002";
|
|
19393
19563
|
break;
|
|
@@ -19400,6 +19570,10 @@ async function handleCommand(adapter, msg, text2) {
|
|
|
19400
19570
|
break;
|
|
19401
19571
|
}
|
|
19402
19572
|
const displayedThreads = getDisplayedDesktopThreads(MAX_DESKTOP_THREAD_LIST_LIMIT);
|
|
19573
|
+
if (!displayedThreads) {
|
|
19574
|
+
response = "\u8BFB\u53D6\u684C\u9762\u4F1A\u8BDD\u5217\u8868\u5931\u8D25\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5\u3002";
|
|
19575
|
+
break;
|
|
19576
|
+
}
|
|
19403
19577
|
const threadPick = resolveByIndexOrPrefix(args, displayedThreads, (session) => session.threadId);
|
|
19404
19578
|
if (threadPick.ambiguous) {
|
|
19405
19579
|
response = "\u5339\u914D\u5230\u591A\u4E2A\u684C\u9762\u4F1A\u8BDD\uFF0C\u8BF7\u5148\u53D1\u9001 `/t` \u67E5\u770B\u5217\u8868\uFF0C\u518D\u7528 `/t 1` \u8FD9\u79CD\u5E8F\u53F7\u5207\u6362\u3002";
|
|
@@ -19407,7 +19581,7 @@ async function handleCommand(adapter, msg, text2) {
|
|
|
19407
19581
|
}
|
|
19408
19582
|
if (!threadPick.match) {
|
|
19409
19583
|
if (validateSessionId(args)) {
|
|
19410
|
-
const desktop =
|
|
19584
|
+
const desktop = getDesktopSessionByThreadIdSafe(args, "thread switch");
|
|
19411
19585
|
let binding2;
|
|
19412
19586
|
try {
|
|
19413
19587
|
binding2 = bindToSdkSession(msg.address, args, desktop ? {
|
|
@@ -19466,6 +19640,10 @@ async function handleCommand(adapter, msg, text2) {
|
|
|
19466
19640
|
}
|
|
19467
19641
|
const { showAll, limit } = listArgs;
|
|
19468
19642
|
const desktopSessions = getDisplayedDesktopThreads(limit);
|
|
19643
|
+
if (!desktopSessions) {
|
|
19644
|
+
response = "\u8BFB\u53D6\u684C\u9762\u4F1A\u8BDD\u5217\u8868\u5931\u8D25\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5\u3002";
|
|
19645
|
+
break;
|
|
19646
|
+
}
|
|
19469
19647
|
if (desktopSessions.length === 0) {
|
|
19470
19648
|
response = showAll ? "\u6CA1\u6709\u627E\u5230\u684C\u9762\u4F1A\u8BDD\u3002\u5148\u5728 Codex Desktop App \u4E2D\u6253\u5F00\u4E00\u4E2A\u4F1A\u8BDD\uFF0C\u518D\u56DE\u6765\u8BD5\u4E00\u6B21\u3002" : "\u6CA1\u6709\u627E\u5230\u6700\u8FD1\u684C\u9762\u4F1A\u8BDD\u3002\u5148\u5728 Codex Desktop App \u4E2D\u6253\u5F00\u4E00\u4E2A\u4F1A\u8BDD\uFF0C\u518D\u56DE\u6765\u8BD5\u4E00\u6B21\u3002";
|
|
19471
19649
|
break;
|
|
@@ -20872,6 +21050,7 @@ var PendingPermissions = class {
|
|
|
20872
21050
|
// src/logger.ts
|
|
20873
21051
|
import fs11 from "node:fs";
|
|
20874
21052
|
import path13 from "node:path";
|
|
21053
|
+
import { inspect as inspect2 } from "node:util";
|
|
20875
21054
|
var MASK_PATTERNS = [
|
|
20876
21055
|
/(?:token|secret|password|api_key)["']?\s*[:=]\s*["']?([^\s"',]+)/gi,
|
|
20877
21056
|
/bot\d+:[A-Za-z0-9_-]{35}/g,
|
|
@@ -20893,6 +21072,22 @@ var LOG_PATH = path13.join(LOG_DIR, "bridge.log");
|
|
|
20893
21072
|
var MAX_LOG_SIZE = 10 * 1024 * 1024;
|
|
20894
21073
|
var MAX_ROTATED = 3;
|
|
20895
21074
|
var logStream = null;
|
|
21075
|
+
function formatLogArg(value) {
|
|
21076
|
+
if (typeof value === "string") return value;
|
|
21077
|
+
if (value instanceof Error) {
|
|
21078
|
+
return value.stack || `${value.name}: ${value.message}`;
|
|
21079
|
+
}
|
|
21080
|
+
if (value === null) return "null";
|
|
21081
|
+
if (typeof value === "undefined") return "undefined";
|
|
21082
|
+
if (typeof value === "object") {
|
|
21083
|
+
return inspect2(value, {
|
|
21084
|
+
depth: 4,
|
|
21085
|
+
breakLength: Infinity,
|
|
21086
|
+
compact: true
|
|
21087
|
+
});
|
|
21088
|
+
}
|
|
21089
|
+
return String(value);
|
|
21090
|
+
}
|
|
20896
21091
|
function openLogStream() {
|
|
20897
21092
|
return fs11.createWriteStream(LOG_PATH, { flags: "a" });
|
|
20898
21093
|
}
|
|
@@ -20922,7 +21117,7 @@ function setupLogger() {
|
|
|
20922
21117
|
logStream = openLogStream();
|
|
20923
21118
|
const write = (level, args) => {
|
|
20924
21119
|
const timestamp = (/* @__PURE__ */ new Date()).toISOString();
|
|
20925
|
-
const message = args.map((a) =>
|
|
21120
|
+
const message = args.map((a) => formatLogArg(a)).join(" ");
|
|
20926
21121
|
const formatted = `[${timestamp}] [${level}] ${message}`;
|
|
20927
21122
|
const masked = maskSecrets(formatted);
|
|
20928
21123
|
rotateIfNeeded();
|
package/dist/ui-server.mjs
CHANGED
|
@@ -5142,8 +5142,8 @@ function walkSessionFiles(dirPath, target) {
|
|
|
5142
5142
|
}
|
|
5143
5143
|
}
|
|
5144
5144
|
function isDesktopLike(meta) {
|
|
5145
|
-
const originator = meta?.originator
|
|
5146
|
-
const source = meta?.source
|
|
5145
|
+
const originator = typeof meta?.originator === "string" ? meta.originator.toLowerCase() : "";
|
|
5146
|
+
const source = typeof meta?.source === "string" ? meta.source.toLowerCase() : "";
|
|
5147
5147
|
if (source === "exec") return false;
|
|
5148
5148
|
return originator.includes("desktop") || source === "vscode" || source === "desktop";
|
|
5149
5149
|
}
|
|
@@ -5275,9 +5275,9 @@ function parseDesktopSession(filePath, threadIndexEntries, archivedThreadIds) {
|
|
|
5275
5275
|
threadId,
|
|
5276
5276
|
filePath,
|
|
5277
5277
|
cwd,
|
|
5278
|
-
originator: parsed.payload.originator
|
|
5279
|
-
source: parsed.payload.source
|
|
5280
|
-
cliVersion: parsed.payload.cli_version
|
|
5278
|
+
originator: typeof parsed.payload.originator === "string" ? parsed.payload.originator : "Codex Desktop",
|
|
5279
|
+
source: typeof parsed.payload.source === "string" ? parsed.payload.source : void 0,
|
|
5280
|
+
cliVersion: typeof parsed.payload.cli_version === "string" ? parsed.payload.cli_version : void 0,
|
|
5281
5281
|
firstSeenAt,
|
|
5282
5282
|
lastEventAt,
|
|
5283
5283
|
title,
|
|
@@ -5646,6 +5646,30 @@ function isProcessAlive(pid) {
|
|
|
5646
5646
|
return false;
|
|
5647
5647
|
}
|
|
5648
5648
|
}
|
|
5649
|
+
function collectTrackedBridgePids(bridgePid, statusPid) {
|
|
5650
|
+
const unique = /* @__PURE__ */ new Set();
|
|
5651
|
+
for (const pid of [bridgePid, statusPid]) {
|
|
5652
|
+
if (Number.isFinite(pid) && pid > 0) {
|
|
5653
|
+
unique.add(pid);
|
|
5654
|
+
}
|
|
5655
|
+
}
|
|
5656
|
+
return [...unique];
|
|
5657
|
+
}
|
|
5658
|
+
function resolveTrackedBridgePid(bridgePid, statusPid, isAlive = isProcessAlive) {
|
|
5659
|
+
if (isAlive(bridgePid)) return bridgePid;
|
|
5660
|
+
if (isAlive(statusPid)) return statusPid;
|
|
5661
|
+
return bridgePid ?? statusPid;
|
|
5662
|
+
}
|
|
5663
|
+
function getTrackedBridgePids(status) {
|
|
5664
|
+
const resolvedStatus = status ?? readJsonFile(bridgeStatusFile, { running: false });
|
|
5665
|
+
return collectTrackedBridgePids(readPid(bridgePidFile), resolvedStatus.pid);
|
|
5666
|
+
}
|
|
5667
|
+
function clearBridgePidFile() {
|
|
5668
|
+
try {
|
|
5669
|
+
fs3.unlinkSync(bridgePidFile);
|
|
5670
|
+
} catch {
|
|
5671
|
+
}
|
|
5672
|
+
}
|
|
5649
5673
|
function sleep(ms) {
|
|
5650
5674
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
5651
5675
|
}
|
|
@@ -5700,7 +5724,7 @@ function getUiServerUrl(port2 = uiPort) {
|
|
|
5700
5724
|
}
|
|
5701
5725
|
function getBridgeStatus() {
|
|
5702
5726
|
const status = readJsonFile(bridgeStatusFile, { running: false });
|
|
5703
|
-
const pid = readPid(bridgePidFile)
|
|
5727
|
+
const pid = resolveTrackedBridgePid(readPid(bridgePidFile), status.pid);
|
|
5704
5728
|
if (!isProcessAlive(pid)) {
|
|
5705
5729
|
return {
|
|
5706
5730
|
...status,
|
|
@@ -5750,7 +5774,11 @@ async function waitForBridgeRunning(timeoutMs = 2e4) {
|
|
|
5750
5774
|
async function startBridge() {
|
|
5751
5775
|
ensureDirs();
|
|
5752
5776
|
const current = getBridgeStatus();
|
|
5753
|
-
|
|
5777
|
+
const extraAlivePids = getTrackedBridgePids(current).filter((pid) => pid !== current.pid && isProcessAlive(pid));
|
|
5778
|
+
if (current.running && extraAlivePids.length === 0) return current;
|
|
5779
|
+
if (current.running && extraAlivePids.length > 0) {
|
|
5780
|
+
await stopBridge();
|
|
5781
|
+
}
|
|
5754
5782
|
const daemonEntry = path4.join(packageRoot, "dist", "daemon.mjs");
|
|
5755
5783
|
if (!fs3.existsSync(daemonEntry)) {
|
|
5756
5784
|
throw new Error(`Daemon bundle not found at ${daemonEntry}. Run npm run build first.`);
|
|
@@ -5772,31 +5800,38 @@ async function startBridge() {
|
|
|
5772
5800
|
return status;
|
|
5773
5801
|
}
|
|
5774
5802
|
async function stopBridge() {
|
|
5775
|
-
const status =
|
|
5776
|
-
|
|
5777
|
-
|
|
5778
|
-
|
|
5779
|
-
|
|
5780
|
-
|
|
5781
|
-
|
|
5782
|
-
|
|
5783
|
-
|
|
5803
|
+
const status = readJsonFile(bridgeStatusFile, { running: false });
|
|
5804
|
+
const pids = getTrackedBridgePids(status).filter((pid) => isProcessAlive(pid));
|
|
5805
|
+
if (pids.length === 0) {
|
|
5806
|
+
clearBridgePidFile();
|
|
5807
|
+
return { ...getBridgeStatus(), running: false };
|
|
5808
|
+
}
|
|
5809
|
+
for (const pid of pids) {
|
|
5810
|
+
if (process.platform === "win32") {
|
|
5811
|
+
await new Promise((resolve) => {
|
|
5812
|
+
const killer = spawn("cmd", ["/c", "taskkill", "/PID", String(pid), "/T", "/F"], {
|
|
5813
|
+
stdio: "ignore",
|
|
5814
|
+
...WINDOWS_HIDE
|
|
5815
|
+
});
|
|
5816
|
+
killer.on("exit", () => resolve());
|
|
5817
|
+
killer.on("error", () => resolve());
|
|
5784
5818
|
});
|
|
5785
|
-
|
|
5786
|
-
|
|
5787
|
-
|
|
5788
|
-
|
|
5789
|
-
|
|
5790
|
-
process.kill(status.pid, "SIGTERM");
|
|
5791
|
-
} catch {
|
|
5819
|
+
} else {
|
|
5820
|
+
try {
|
|
5821
|
+
process.kill(pid, "SIGTERM");
|
|
5822
|
+
} catch {
|
|
5823
|
+
}
|
|
5792
5824
|
}
|
|
5793
5825
|
}
|
|
5794
5826
|
const startedAt = Date.now();
|
|
5795
5827
|
while (Date.now() - startedAt < 1e4) {
|
|
5796
|
-
|
|
5797
|
-
|
|
5828
|
+
if (pids.every((pid) => !isProcessAlive(pid))) {
|
|
5829
|
+
clearBridgePidFile();
|
|
5830
|
+
return getBridgeStatus();
|
|
5831
|
+
}
|
|
5798
5832
|
await sleep(300);
|
|
5799
5833
|
}
|
|
5834
|
+
clearBridgePidFile();
|
|
5800
5835
|
return getBridgeStatus();
|
|
5801
5836
|
}
|
|
5802
5837
|
async function restartBridge() {
|
package/package.json
CHANGED