codex-to-im 1.0.28 → 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 +219 -39
- package/dist/ui-server.mjs +55 -20
- 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) {
|
|
@@ -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
|
}
|
|
@@ -18065,7 +18101,9 @@ function scheduleMirrorWake(delayMs = MIRROR_WATCH_DEBOUNCE_MS) {
|
|
|
18065
18101
|
if (state.mirrorWakeTimer) return;
|
|
18066
18102
|
state.mirrorWakeTimer = setTimeout(() => {
|
|
18067
18103
|
state.mirrorWakeTimer = null;
|
|
18068
|
-
void reconcileMirrorSubscriptions()
|
|
18104
|
+
void reconcileMirrorSubscriptions().catch((err) => {
|
|
18105
|
+
console.error("[bridge-manager] Mirror wake reconcile failed:", describeUnknownError(err));
|
|
18106
|
+
});
|
|
18069
18107
|
}, delayMs);
|
|
18070
18108
|
}
|
|
18071
18109
|
function watchMirrorFile(subscription, filePath) {
|
|
@@ -18103,6 +18141,16 @@ function syncMirrorSessionState(sessionId) {
|
|
|
18103
18141
|
mirror_last_event_at: deliveredAt
|
|
18104
18142
|
});
|
|
18105
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
|
+
}
|
|
18106
18154
|
function getMirrorAssistantRuntimeLabel() {
|
|
18107
18155
|
const { store } = getBridgeContext();
|
|
18108
18156
|
const runtime = (store.getSetting("bridge_runtime") || "codex").trim().toLowerCase();
|
|
@@ -18467,7 +18515,17 @@ function removeMirrorSubscription(bindingId) {
|
|
|
18467
18515
|
stopMirrorStreaming(existing);
|
|
18468
18516
|
closeMirrorWatcher(existing);
|
|
18469
18517
|
state.mirrorSubscriptions.delete(bindingId);
|
|
18470
|
-
|
|
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);
|
|
18471
18529
|
}
|
|
18472
18530
|
function upsertMirrorSubscription(binding) {
|
|
18473
18531
|
const { store } = getBridgeContext();
|
|
@@ -18482,7 +18540,7 @@ function upsertMirrorSubscription(binding) {
|
|
|
18482
18540
|
removeMirrorSubscription(binding.id);
|
|
18483
18541
|
return;
|
|
18484
18542
|
}
|
|
18485
|
-
const desktopSession =
|
|
18543
|
+
const desktopSession = getDesktopSessionByThreadIdSafe(threadId, "mirror subscription sync");
|
|
18486
18544
|
const filePath = desktopSession?.filePath || null;
|
|
18487
18545
|
const existing = state.mirrorSubscriptions.get(binding.id);
|
|
18488
18546
|
if (!existing) {
|
|
@@ -18507,11 +18565,14 @@ function upsertMirrorSubscription(binding) {
|
|
|
18507
18565
|
trailingText: "",
|
|
18508
18566
|
activeMirrorTurnId: null,
|
|
18509
18567
|
bufferedRecords: [],
|
|
18510
|
-
pendingTurn: null
|
|
18568
|
+
pendingTurn: null,
|
|
18569
|
+
missingThreadPolls: 0,
|
|
18570
|
+
consecutiveFailures: 0,
|
|
18571
|
+
suspendedUntil: null
|
|
18511
18572
|
};
|
|
18512
18573
|
watchMirrorFile(created, filePath);
|
|
18513
18574
|
state.mirrorSubscriptions.set(binding.id, created);
|
|
18514
|
-
|
|
18575
|
+
syncMirrorSessionStateSafe(binding.codepilotSessionId, "mirror subscription create");
|
|
18515
18576
|
return;
|
|
18516
18577
|
}
|
|
18517
18578
|
const previousSessionId = existing.sessionId;
|
|
@@ -18529,18 +18590,23 @@ function upsertMirrorSubscription(binding) {
|
|
|
18529
18590
|
existing.lastDeliveredAt = session.mirror_last_event_at || null;
|
|
18530
18591
|
existing.dirty = true;
|
|
18531
18592
|
existing.pendingTurn = null;
|
|
18593
|
+
existing.missingThreadPolls = 0;
|
|
18594
|
+
existing.consecutiveFailures = 0;
|
|
18595
|
+
existing.suspendedUntil = null;
|
|
18532
18596
|
resetMirrorReadState(existing);
|
|
18533
18597
|
} else if (filePathChanged) {
|
|
18534
18598
|
stopMirrorStreaming(existing);
|
|
18535
18599
|
existing.dirty = true;
|
|
18536
18600
|
existing.pendingTurn = null;
|
|
18601
|
+
existing.consecutiveFailures = 0;
|
|
18602
|
+
existing.suspendedUntil = null;
|
|
18537
18603
|
resetMirrorReadState(existing);
|
|
18538
18604
|
}
|
|
18539
18605
|
watchMirrorFile(existing, filePath);
|
|
18540
18606
|
if (previousSessionId !== binding.codepilotSessionId) {
|
|
18541
|
-
|
|
18607
|
+
syncMirrorSessionStateSafe(previousSessionId, "mirror subscription rebind previous session");
|
|
18542
18608
|
}
|
|
18543
|
-
|
|
18609
|
+
syncMirrorSessionStateSafe(binding.codepilotSessionId, "mirror subscription upsert");
|
|
18544
18610
|
}
|
|
18545
18611
|
function syncMirrorSubscriptionSet() {
|
|
18546
18612
|
const { store } = getBridgeContext();
|
|
@@ -18554,7 +18620,14 @@ function syncMirrorSubscriptionSet() {
|
|
|
18554
18620
|
const desiredIds = /* @__PURE__ */ new Set();
|
|
18555
18621
|
for (const binding of desiredBindings) {
|
|
18556
18622
|
desiredIds.add(binding.id);
|
|
18557
|
-
|
|
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
|
+
}
|
|
18558
18631
|
}
|
|
18559
18632
|
for (const bindingId of Array.from(state.mirrorSubscriptions.keys())) {
|
|
18560
18633
|
if (!desiredIds.has(bindingId)) {
|
|
@@ -18569,7 +18642,29 @@ async function reconcileMirrorSubscription(subscription) {
|
|
|
18569
18642
|
removeMirrorSubscription(subscription.bindingId);
|
|
18570
18643
|
return;
|
|
18571
18644
|
}
|
|
18572
|
-
|
|
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
|
+
}
|
|
18573
18668
|
const filePathChanged = subscription.filePath !== (desktopSession?.filePath || null);
|
|
18574
18669
|
subscription.filePath = desktopSession?.filePath || null;
|
|
18575
18670
|
subscription.status = subscription.filePath ? "watching" : "stale";
|
|
@@ -18580,7 +18675,7 @@ async function reconcileMirrorSubscription(subscription) {
|
|
|
18580
18675
|
watchMirrorFile(subscription, subscription.filePath);
|
|
18581
18676
|
subscription.lastReconciledAt = nowIso2();
|
|
18582
18677
|
if (!subscription.filePath) {
|
|
18583
|
-
|
|
18678
|
+
syncMirrorSessionStateSafe(subscription.sessionId, "mirror reconcile without file");
|
|
18584
18679
|
return;
|
|
18585
18680
|
}
|
|
18586
18681
|
const snapshot = statMirrorFile(subscription.filePath);
|
|
@@ -18588,12 +18683,12 @@ async function reconcileMirrorSubscription(subscription) {
|
|
|
18588
18683
|
subscription.status = "stale";
|
|
18589
18684
|
subscription.dirty = true;
|
|
18590
18685
|
resetMirrorReadState(subscription);
|
|
18591
|
-
|
|
18686
|
+
syncMirrorSessionStateSafe(subscription.sessionId, "mirror reconcile missing snapshot");
|
|
18592
18687
|
return;
|
|
18593
18688
|
}
|
|
18594
18689
|
const unchanged = !subscription.dirty && subscription.fileIdentity === snapshot.identity && subscription.fileSize === snapshot.size && subscription.fileMtimeMs === snapshot.mtimeMs;
|
|
18595
18690
|
if (unchanged && !hasPendingMirrorWork(subscription)) {
|
|
18596
|
-
|
|
18691
|
+
syncMirrorSessionStateSafe(subscription.sessionId, "mirror reconcile unchanged snapshot");
|
|
18597
18692
|
return;
|
|
18598
18693
|
}
|
|
18599
18694
|
let deliverableRecords = [];
|
|
@@ -18648,13 +18743,13 @@ async function reconcileMirrorSubscription(subscription) {
|
|
|
18648
18743
|
console.warn("[bridge-manager] Mirror delivery failed:", error instanceof Error ? error.message : error);
|
|
18649
18744
|
}
|
|
18650
18745
|
}
|
|
18651
|
-
|
|
18746
|
+
syncMirrorSessionStateSafe(subscription.sessionId, "mirror reconcile active task");
|
|
18652
18747
|
return;
|
|
18653
18748
|
}
|
|
18654
18749
|
const finalizedTurns = timedOutTurn ? [timedOutTurn] : [];
|
|
18655
18750
|
finalizedTurns.push(...consumeBufferedMirrorTurns(subscription));
|
|
18656
18751
|
if (finalizedTurns.length === 0) {
|
|
18657
|
-
|
|
18752
|
+
syncMirrorSessionStateSafe(subscription.sessionId, "mirror reconcile no finalized turns");
|
|
18658
18753
|
return;
|
|
18659
18754
|
}
|
|
18660
18755
|
try {
|
|
@@ -18663,29 +18758,66 @@ async function reconcileMirrorSubscription(subscription) {
|
|
|
18663
18758
|
subscription.dirty = true;
|
|
18664
18759
|
console.warn("[bridge-manager] Mirror delivery failed:", error instanceof Error ? error.message : error);
|
|
18665
18760
|
}
|
|
18666
|
-
|
|
18761
|
+
syncMirrorSessionStateSafe(subscription.sessionId, "mirror reconcile delivered turns");
|
|
18667
18762
|
}
|
|
18668
18763
|
async function reconcileMirrorSubscriptions() {
|
|
18669
18764
|
const state = getState();
|
|
18670
18765
|
if (!state.running || state.mirrorSyncInFlight) return;
|
|
18671
18766
|
state.mirrorSyncInFlight = true;
|
|
18767
|
+
let stage = "sync-start";
|
|
18672
18768
|
try {
|
|
18673
|
-
|
|
18674
|
-
|
|
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}`;
|
|
18675
18780
|
try {
|
|
18676
18781
|
await reconcileMirrorSubscription(subscription);
|
|
18782
|
+
subscription.consecutiveFailures = 0;
|
|
18783
|
+
subscription.suspendedUntil = null;
|
|
18677
18784
|
} catch (error) {
|
|
18678
|
-
|
|
18679
|
-
|
|
18680
|
-
|
|
18681
|
-
|
|
18682
|
-
|
|
18683
|
-
|
|
18684
|
-
|
|
18685
|
-
|
|
18686
|
-
|
|
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
|
+
}
|
|
18687
18813
|
}
|
|
18688
18814
|
}
|
|
18815
|
+
stage = "sync-complete";
|
|
18816
|
+
} catch (error) {
|
|
18817
|
+
console.error(
|
|
18818
|
+
`[bridge-manager] Mirror reconcile failed during ${stage}:`,
|
|
18819
|
+
describeUnknownError(error)
|
|
18820
|
+
);
|
|
18689
18821
|
} finally {
|
|
18690
18822
|
state.mirrorSyncInFlight = false;
|
|
18691
18823
|
}
|
|
@@ -18774,6 +18906,8 @@ function listEnabledAdapterInstances() {
|
|
|
18774
18906
|
async function stopAdapterInstance(channelType) {
|
|
18775
18907
|
const state = getState();
|
|
18776
18908
|
const adapter = state.adapters.get(channelType);
|
|
18909
|
+
state.invalidAdapters.delete(channelType);
|
|
18910
|
+
INVALID_ADAPTER_WARNING_CACHE.delete(channelType);
|
|
18777
18911
|
if (!adapter) return;
|
|
18778
18912
|
state.loopAborts.get(channelType)?.abort();
|
|
18779
18913
|
state.loopAborts.delete(channelType);
|
|
@@ -18799,6 +18933,14 @@ async function syncConfiguredAdapters(options) {
|
|
|
18799
18933
|
await stopAdapterInstance(existingKey);
|
|
18800
18934
|
changed = true;
|
|
18801
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
|
+
}
|
|
18802
18944
|
for (const instance of desiredInstances) {
|
|
18803
18945
|
const existing = state.adapters.get(instance.id);
|
|
18804
18946
|
const desiredFingerprint = desiredFingerprints.get(instance.id) || "";
|
|
@@ -18812,9 +18954,16 @@ async function syncConfiguredAdapters(options) {
|
|
|
18812
18954
|
if (!adapter) continue;
|
|
18813
18955
|
const configError = adapter.validateConfig();
|
|
18814
18956
|
if (configError) {
|
|
18815
|
-
|
|
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
|
+
}
|
|
18816
18963
|
continue;
|
|
18817
18964
|
}
|
|
18965
|
+
state.invalidAdapters.delete(instance.id);
|
|
18966
|
+
INVALID_ADAPTER_WARNING_CACHE.delete(instance.id);
|
|
18818
18967
|
try {
|
|
18819
18968
|
state.adapters.set(instance.id, adapter);
|
|
18820
18969
|
state.adapterMeta.set(instance.id, {
|
|
@@ -18874,11 +19023,11 @@ async function start() {
|
|
|
18874
19023
|
}, 5e3);
|
|
18875
19024
|
state.mirrorPollTimer = setInterval(() => {
|
|
18876
19025
|
void reconcileMirrorSubscriptions().catch((err) => {
|
|
18877
|
-
console.error("[bridge-manager] Mirror reconcile failed:", err);
|
|
19026
|
+
console.error("[bridge-manager] Mirror reconcile failed:", describeUnknownError(err));
|
|
18878
19027
|
});
|
|
18879
19028
|
}, MIRROR_POLL_INTERVAL_MS);
|
|
18880
19029
|
void reconcileMirrorSubscriptions().catch((err) => {
|
|
18881
|
-
console.error("[bridge-manager] Initial mirror reconcile failed:", err);
|
|
19030
|
+
console.error("[bridge-manager] Initial mirror reconcile failed:", describeUnknownError(err));
|
|
18882
19031
|
});
|
|
18883
19032
|
console.log(`[bridge-manager] Bridge started with ${startedCount} adapter(s)`);
|
|
18884
19033
|
}
|
|
@@ -18911,6 +19060,8 @@ async function stop() {
|
|
|
18911
19060
|
state.mirrorSuppressUntil.clear();
|
|
18912
19061
|
state.mirrorIgnoredTurnIds.clear();
|
|
18913
19062
|
state.queuedCounts.clear();
|
|
19063
|
+
state.invalidAdapters.clear();
|
|
19064
|
+
INVALID_ADAPTER_WARNING_CACHE.clear();
|
|
18914
19065
|
for (const sessionId of activeSessionIds) {
|
|
18915
19066
|
syncSessionRuntimeState(sessionId);
|
|
18916
19067
|
}
|
|
@@ -19403,6 +19554,10 @@ async function handleCommand(adapter, msg, text2) {
|
|
|
19403
19554
|
}
|
|
19404
19555
|
if (args === "all") {
|
|
19405
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
|
+
}
|
|
19406
19561
|
if (desktopSessions.length === 0) {
|
|
19407
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";
|
|
19408
19563
|
break;
|
|
@@ -19415,6 +19570,10 @@ async function handleCommand(adapter, msg, text2) {
|
|
|
19415
19570
|
break;
|
|
19416
19571
|
}
|
|
19417
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
|
+
}
|
|
19418
19577
|
const threadPick = resolveByIndexOrPrefix(args, displayedThreads, (session) => session.threadId);
|
|
19419
19578
|
if (threadPick.ambiguous) {
|
|
19420
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";
|
|
@@ -19422,7 +19581,7 @@ async function handleCommand(adapter, msg, text2) {
|
|
|
19422
19581
|
}
|
|
19423
19582
|
if (!threadPick.match) {
|
|
19424
19583
|
if (validateSessionId(args)) {
|
|
19425
|
-
const desktop =
|
|
19584
|
+
const desktop = getDesktopSessionByThreadIdSafe(args, "thread switch");
|
|
19426
19585
|
let binding2;
|
|
19427
19586
|
try {
|
|
19428
19587
|
binding2 = bindToSdkSession(msg.address, args, desktop ? {
|
|
@@ -19481,6 +19640,10 @@ async function handleCommand(adapter, msg, text2) {
|
|
|
19481
19640
|
}
|
|
19482
19641
|
const { showAll, limit } = listArgs;
|
|
19483
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
|
+
}
|
|
19484
19647
|
if (desktopSessions.length === 0) {
|
|
19485
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";
|
|
19486
19649
|
break;
|
|
@@ -20887,6 +21050,7 @@ var PendingPermissions = class {
|
|
|
20887
21050
|
// src/logger.ts
|
|
20888
21051
|
import fs11 from "node:fs";
|
|
20889
21052
|
import path13 from "node:path";
|
|
21053
|
+
import { inspect as inspect2 } from "node:util";
|
|
20890
21054
|
var MASK_PATTERNS = [
|
|
20891
21055
|
/(?:token|secret|password|api_key)["']?\s*[:=]\s*["']?([^\s"',]+)/gi,
|
|
20892
21056
|
/bot\d+:[A-Za-z0-9_-]{35}/g,
|
|
@@ -20908,6 +21072,22 @@ var LOG_PATH = path13.join(LOG_DIR, "bridge.log");
|
|
|
20908
21072
|
var MAX_LOG_SIZE = 10 * 1024 * 1024;
|
|
20909
21073
|
var MAX_ROTATED = 3;
|
|
20910
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
|
+
}
|
|
20911
21091
|
function openLogStream() {
|
|
20912
21092
|
return fs11.createWriteStream(LOG_PATH, { flags: "a" });
|
|
20913
21093
|
}
|
|
@@ -20937,7 +21117,7 @@ function setupLogger() {
|
|
|
20937
21117
|
logStream = openLogStream();
|
|
20938
21118
|
const write = (level, args) => {
|
|
20939
21119
|
const timestamp = (/* @__PURE__ */ new Date()).toISOString();
|
|
20940
|
-
const message = args.map((a) =>
|
|
21120
|
+
const message = args.map((a) => formatLogArg(a)).join(" ");
|
|
20941
21121
|
const formatted = `[${timestamp}] [${level}] ${message}`;
|
|
20942
21122
|
const masked = maskSecrets(formatted);
|
|
20943
21123
|
rotateIfNeeded();
|
package/dist/ui-server.mjs
CHANGED
|
@@ -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