codex-to-im 1.0.28 → 1.0.30
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 +261 -21
- package/dist/daemon.mjs +247 -57
- package/dist/ui-server.mjs +763 -105
- package/package.json +1 -1
- package/references/setup-guides.md +1 -1
- package/scripts/doctor.sh +9 -9
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";
|
|
@@ -7817,11 +7818,8 @@ function readJson(filePath, fallback) {
|
|
|
7817
7818
|
function getAccountRecency(account) {
|
|
7818
7819
|
return account.lastLoginAt ?? account.updatedAt ?? account.createdAt;
|
|
7819
7820
|
}
|
|
7820
|
-
function
|
|
7821
|
-
|
|
7822
|
-
return { accounts, removedAccountIds: [] };
|
|
7823
|
-
}
|
|
7824
|
-
const sorted = [...accounts].sort((a, b) => {
|
|
7821
|
+
function sortAccountsByRecency(accounts) {
|
|
7822
|
+
return [...accounts].sort((a, b) => {
|
|
7825
7823
|
const recencyDiff = getAccountRecency(b).localeCompare(getAccountRecency(a));
|
|
7826
7824
|
if (recencyDiff !== 0) return recencyDiff;
|
|
7827
7825
|
const updatedDiff = b.updatedAt.localeCompare(a.updatedAt);
|
|
@@ -7830,16 +7828,14 @@ function normalizeAccounts(accounts) {
|
|
|
7830
7828
|
if (createdDiff !== 0) return createdDiff;
|
|
7831
7829
|
return 0;
|
|
7832
7830
|
});
|
|
7833
|
-
|
|
7834
|
-
|
|
7835
|
-
|
|
7836
|
-
|
|
7837
|
-
)
|
|
7838
|
-
|
|
7839
|
-
|
|
7840
|
-
|
|
7841
|
-
removedAccountIds
|
|
7842
|
-
};
|
|
7831
|
+
}
|
|
7832
|
+
function normalizeAccounts(accounts) {
|
|
7833
|
+
const deduped = /* @__PURE__ */ new Map();
|
|
7834
|
+
for (const account of sortAccountsByRecency(accounts)) {
|
|
7835
|
+
if (!account?.accountId || deduped.has(account.accountId)) continue;
|
|
7836
|
+
deduped.set(account.accountId, account);
|
|
7837
|
+
}
|
|
7838
|
+
return Array.from(deduped.values());
|
|
7843
7839
|
}
|
|
7844
7840
|
function readStoredAccounts() {
|
|
7845
7841
|
ensureDir(DATA_DIR);
|
|
@@ -7847,7 +7843,7 @@ function readStoredAccounts() {
|
|
|
7847
7843
|
return Array.isArray(raw) ? raw : [];
|
|
7848
7844
|
}
|
|
7849
7845
|
function readAccounts() {
|
|
7850
|
-
return normalizeAccounts(readStoredAccounts())
|
|
7846
|
+
return normalizeAccounts(readStoredAccounts());
|
|
7851
7847
|
}
|
|
7852
7848
|
function readContextTokens() {
|
|
7853
7849
|
ensureDir(DATA_DIR);
|
|
@@ -7861,7 +7857,7 @@ function contextKey(accountId, peerUserId) {
|
|
|
7861
7857
|
return `${accountId}::${peerUserId}`;
|
|
7862
7858
|
}
|
|
7863
7859
|
function listWeixinAccounts() {
|
|
7864
|
-
return readAccounts()
|
|
7860
|
+
return sortAccountsByRecency(readAccounts());
|
|
7865
7861
|
}
|
|
7866
7862
|
function getWeixinAccount(accountId) {
|
|
7867
7863
|
return readAccounts().find((account) => account.accountId === accountId);
|
|
@@ -14233,11 +14229,21 @@ var WeixinAdapter = class extends BaseChannelAdapter {
|
|
|
14233
14229
|
}
|
|
14234
14230
|
}
|
|
14235
14231
|
validateConfig() {
|
|
14236
|
-
const
|
|
14237
|
-
|
|
14238
|
-
);
|
|
14239
|
-
if (
|
|
14240
|
-
|
|
14232
|
+
const configuredAccountId = this.configuredAccountId;
|
|
14233
|
+
const accounts = listWeixinAccounts();
|
|
14234
|
+
const enabledAccounts = accounts.filter((account) => account.enabled && account.token);
|
|
14235
|
+
if (configuredAccountId) {
|
|
14236
|
+
const configured = getWeixinAccount(configuredAccountId);
|
|
14237
|
+
if (!configured) {
|
|
14238
|
+
return `Linked WeChat account ${configuredAccountId} not found`;
|
|
14239
|
+
}
|
|
14240
|
+
if (!configured.enabled || !configured.token) {
|
|
14241
|
+
return `Linked WeChat account ${configuredAccountId} is disabled or missing token`;
|
|
14242
|
+
}
|
|
14243
|
+
return null;
|
|
14244
|
+
}
|
|
14245
|
+
if (enabledAccounts.length > 1) {
|
|
14246
|
+
return "Multiple linked WeChat accounts detected. Please select a WeChat account for this channel.";
|
|
14241
14247
|
}
|
|
14242
14248
|
return null;
|
|
14243
14249
|
}
|
|
@@ -15375,7 +15381,7 @@ function bindStoreToSdkSession(store, channelType, chatId, sdkSessionId, opts) {
|
|
|
15375
15381
|
import fs5 from "node:fs";
|
|
15376
15382
|
import path6 from "node:path";
|
|
15377
15383
|
var DRAFT_TTL_MS = 24 * 60 * 60 * 1e3;
|
|
15378
|
-
var MAX_HIDDEN_DRAFT_SESSIONS =
|
|
15384
|
+
var MAX_HIDDEN_DRAFT_SESSIONS = 64;
|
|
15379
15385
|
var INTERNAL_SESSION_ROOT = path6.join(CTI_HOME, "runtime", "internal-sessions");
|
|
15380
15386
|
var DRAFT_SESSION_PREFIX = "Draft";
|
|
15381
15387
|
var HISTORY_SESSION_PREFIX = "History Summary";
|
|
@@ -17018,6 +17024,9 @@ var MODE_OPTIONS_TEXT = "\u53EF\u9009\uFF1A`code`\uFF08\u76F4\u63A5\u6267\u884C\
|
|
|
17018
17024
|
var REASONING_OPTIONS_TEXT = "\u53EF\u9009\uFF1A`1=minimal` `2=low` `3=medium` `4=high` `5=xhigh`";
|
|
17019
17025
|
var DEFAULT_DESKTOP_THREAD_LIST_LIMIT = 10;
|
|
17020
17026
|
var MAX_DESKTOP_THREAD_LIST_LIMIT = 200;
|
|
17027
|
+
var DANGLING_MIRROR_THREAD_RETRY_LIMIT = 3;
|
|
17028
|
+
var MIRROR_FAILURE_SUSPEND_MS = 6e4;
|
|
17029
|
+
var MIRROR_FAILURE_SUSPEND_THRESHOLD = 3;
|
|
17021
17030
|
var MIRROR_POLL_INTERVAL_MS = 2500;
|
|
17022
17031
|
var MIRROR_WATCH_DEBOUNCE_MS = 350;
|
|
17023
17032
|
var MIRROR_EVENT_BATCH_LIMIT = 8;
|
|
@@ -17027,6 +17036,7 @@ var INTERACTIVE_IDLE_REMINDER_MS = 6e5;
|
|
|
17027
17036
|
var MIRROR_IDLE_TIMEOUT_MS = 6e5;
|
|
17028
17037
|
var AVAILABLE_CODEX_MODELS = listSelectableCodexModels();
|
|
17029
17038
|
var AVAILABLE_CODEX_MODEL_MAP = new Map(AVAILABLE_CODEX_MODELS.map((model) => [model.slug, model]));
|
|
17039
|
+
var INVALID_ADAPTER_WARNING_CACHE = /* @__PURE__ */ new Map();
|
|
17030
17040
|
function generateDraftId() {
|
|
17031
17041
|
return Math.floor(Math.random() * 2147483646) + 1;
|
|
17032
17042
|
}
|
|
@@ -17109,8 +17119,41 @@ function resolveByIndexOrPrefix(raw, items, getId) {
|
|
|
17109
17119
|
}
|
|
17110
17120
|
return { match: null, ambiguous: false };
|
|
17111
17121
|
}
|
|
17122
|
+
function describeUnknownError(error) {
|
|
17123
|
+
if (error instanceof Error) {
|
|
17124
|
+
return error.stack || `${error.name}: ${error.message}`;
|
|
17125
|
+
}
|
|
17126
|
+
if (error === null) return "null";
|
|
17127
|
+
if (typeof error === "undefined") return "undefined";
|
|
17128
|
+
if (typeof error === "object") {
|
|
17129
|
+
const ctor = error?.constructor?.name;
|
|
17130
|
+
const rendered = inspect(error, {
|
|
17131
|
+
depth: 4,
|
|
17132
|
+
breakLength: Infinity,
|
|
17133
|
+
compact: true
|
|
17134
|
+
});
|
|
17135
|
+
return ctor && ctor !== "Object" ? `${ctor} ${rendered}` : rendered;
|
|
17136
|
+
}
|
|
17137
|
+
return String(error);
|
|
17138
|
+
}
|
|
17112
17139
|
function getDisplayedDesktopThreads(limit = DEFAULT_DESKTOP_THREAD_LIST_LIMIT) {
|
|
17113
|
-
|
|
17140
|
+
try {
|
|
17141
|
+
return listDesktopSessions(limit);
|
|
17142
|
+
} catch (error) {
|
|
17143
|
+
console.error("[bridge-manager] Failed to list desktop sessions:", error);
|
|
17144
|
+
return null;
|
|
17145
|
+
}
|
|
17146
|
+
}
|
|
17147
|
+
function getDesktopSessionByThreadIdSafe(threadId, context) {
|
|
17148
|
+
try {
|
|
17149
|
+
return getDesktopSessionByThreadId(threadId);
|
|
17150
|
+
} catch (error) {
|
|
17151
|
+
console.error(
|
|
17152
|
+
`[bridge-manager] Failed to load desktop thread ${threadId} during ${context}:`,
|
|
17153
|
+
error
|
|
17154
|
+
);
|
|
17155
|
+
return null;
|
|
17156
|
+
}
|
|
17114
17157
|
}
|
|
17115
17158
|
function parseDesktopThreadListArgs(args) {
|
|
17116
17159
|
const trimmed = args.trim().toLowerCase();
|
|
@@ -17522,7 +17565,7 @@ async function summarizeHistory(currentBinding) {
|
|
|
17522
17565
|
}
|
|
17523
17566
|
function getDesktopThreadTitle(threadId) {
|
|
17524
17567
|
if (!threadId) return null;
|
|
17525
|
-
return
|
|
17568
|
+
return getDesktopSessionByThreadIdSafe(threadId, "status lookup")?.title || null;
|
|
17526
17569
|
}
|
|
17527
17570
|
function formatCommandPath(cwd) {
|
|
17528
17571
|
return cwd?.trim() || "~";
|
|
@@ -17684,6 +17727,7 @@ function getState() {
|
|
|
17684
17727
|
g[GLOBAL_KEY] = {
|
|
17685
17728
|
adapters: /* @__PURE__ */ new Map(),
|
|
17686
17729
|
adapterMeta: /* @__PURE__ */ new Map(),
|
|
17730
|
+
invalidAdapters: /* @__PURE__ */ new Map(),
|
|
17687
17731
|
running: false,
|
|
17688
17732
|
startedAt: null,
|
|
17689
17733
|
loopAborts: /* @__PURE__ */ new Map(),
|
|
@@ -17706,6 +17750,9 @@ function getState() {
|
|
|
17706
17750
|
if (!g[GLOBAL_KEY].mirrorSubscriptions) {
|
|
17707
17751
|
g[GLOBAL_KEY].mirrorSubscriptions = /* @__PURE__ */ new Map();
|
|
17708
17752
|
}
|
|
17753
|
+
if (!g[GLOBAL_KEY].invalidAdapters) {
|
|
17754
|
+
g[GLOBAL_KEY].invalidAdapters = /* @__PURE__ */ new Map();
|
|
17755
|
+
}
|
|
17709
17756
|
if (!g[GLOBAL_KEY].queuedCounts) {
|
|
17710
17757
|
g[GLOBAL_KEY].queuedCounts = /* @__PURE__ */ new Map();
|
|
17711
17758
|
}
|
|
@@ -18065,7 +18112,9 @@ function scheduleMirrorWake(delayMs = MIRROR_WATCH_DEBOUNCE_MS) {
|
|
|
18065
18112
|
if (state.mirrorWakeTimer) return;
|
|
18066
18113
|
state.mirrorWakeTimer = setTimeout(() => {
|
|
18067
18114
|
state.mirrorWakeTimer = null;
|
|
18068
|
-
void reconcileMirrorSubscriptions()
|
|
18115
|
+
void reconcileMirrorSubscriptions().catch((err) => {
|
|
18116
|
+
console.error("[bridge-manager] Mirror wake reconcile failed:", describeUnknownError(err));
|
|
18117
|
+
});
|
|
18069
18118
|
}, delayMs);
|
|
18070
18119
|
}
|
|
18071
18120
|
function watchMirrorFile(subscription, filePath) {
|
|
@@ -18103,6 +18152,16 @@ function syncMirrorSessionState(sessionId) {
|
|
|
18103
18152
|
mirror_last_event_at: deliveredAt
|
|
18104
18153
|
});
|
|
18105
18154
|
}
|
|
18155
|
+
function syncMirrorSessionStateSafe(sessionId, context) {
|
|
18156
|
+
try {
|
|
18157
|
+
syncMirrorSessionState(sessionId);
|
|
18158
|
+
} catch (error) {
|
|
18159
|
+
console.error(
|
|
18160
|
+
`[bridge-manager] Failed to sync mirror session state for ${sessionId} during ${context}:`,
|
|
18161
|
+
describeUnknownError(error)
|
|
18162
|
+
);
|
|
18163
|
+
}
|
|
18164
|
+
}
|
|
18106
18165
|
function getMirrorAssistantRuntimeLabel() {
|
|
18107
18166
|
const { store } = getBridgeContext();
|
|
18108
18167
|
const runtime = (store.getSetting("bridge_runtime") || "codex").trim().toLowerCase();
|
|
@@ -18368,7 +18427,6 @@ function consumeMirrorRecords(subscription, records) {
|
|
|
18368
18427
|
subscription.pendingTurn.lastActivityAt = record.timestamp;
|
|
18369
18428
|
}
|
|
18370
18429
|
}
|
|
18371
|
-
startMirrorStreaming(subscription, subscription.pendingTurn);
|
|
18372
18430
|
continue;
|
|
18373
18431
|
}
|
|
18374
18432
|
if (record.type === "task_complete") {
|
|
@@ -18467,7 +18525,17 @@ function removeMirrorSubscription(bindingId) {
|
|
|
18467
18525
|
stopMirrorStreaming(existing);
|
|
18468
18526
|
closeMirrorWatcher(existing);
|
|
18469
18527
|
state.mirrorSubscriptions.delete(bindingId);
|
|
18470
|
-
|
|
18528
|
+
syncMirrorSessionStateSafe(existing.sessionId, "mirror subscription removal");
|
|
18529
|
+
}
|
|
18530
|
+
function clearDanglingMirrorThread(subscription, reason) {
|
|
18531
|
+
const { store } = getBridgeContext();
|
|
18532
|
+
const session = store.getSession(subscription.sessionId);
|
|
18533
|
+
const currentThreadId = session?.sdk_session_id || subscription.threadId;
|
|
18534
|
+
console.warn(
|
|
18535
|
+
`[bridge-manager] Clearing dangling desktop thread ${currentThreadId} for session ${subscription.sessionId}: ${reason}`
|
|
18536
|
+
);
|
|
18537
|
+
store.updateSdkSessionId(subscription.sessionId, "");
|
|
18538
|
+
removeMirrorSubscription(subscription.bindingId);
|
|
18471
18539
|
}
|
|
18472
18540
|
function upsertMirrorSubscription(binding) {
|
|
18473
18541
|
const { store } = getBridgeContext();
|
|
@@ -18482,7 +18550,7 @@ function upsertMirrorSubscription(binding) {
|
|
|
18482
18550
|
removeMirrorSubscription(binding.id);
|
|
18483
18551
|
return;
|
|
18484
18552
|
}
|
|
18485
|
-
const desktopSession =
|
|
18553
|
+
const desktopSession = getDesktopSessionByThreadIdSafe(threadId, "mirror subscription sync");
|
|
18486
18554
|
const filePath = desktopSession?.filePath || null;
|
|
18487
18555
|
const existing = state.mirrorSubscriptions.get(binding.id);
|
|
18488
18556
|
if (!existing) {
|
|
@@ -18507,11 +18575,14 @@ function upsertMirrorSubscription(binding) {
|
|
|
18507
18575
|
trailingText: "",
|
|
18508
18576
|
activeMirrorTurnId: null,
|
|
18509
18577
|
bufferedRecords: [],
|
|
18510
|
-
pendingTurn: null
|
|
18578
|
+
pendingTurn: null,
|
|
18579
|
+
missingThreadPolls: 0,
|
|
18580
|
+
consecutiveFailures: 0,
|
|
18581
|
+
suspendedUntil: null
|
|
18511
18582
|
};
|
|
18512
18583
|
watchMirrorFile(created, filePath);
|
|
18513
18584
|
state.mirrorSubscriptions.set(binding.id, created);
|
|
18514
|
-
|
|
18585
|
+
syncMirrorSessionStateSafe(binding.codepilotSessionId, "mirror subscription create");
|
|
18515
18586
|
return;
|
|
18516
18587
|
}
|
|
18517
18588
|
const previousSessionId = existing.sessionId;
|
|
@@ -18529,18 +18600,23 @@ function upsertMirrorSubscription(binding) {
|
|
|
18529
18600
|
existing.lastDeliveredAt = session.mirror_last_event_at || null;
|
|
18530
18601
|
existing.dirty = true;
|
|
18531
18602
|
existing.pendingTurn = null;
|
|
18603
|
+
existing.missingThreadPolls = 0;
|
|
18604
|
+
existing.consecutiveFailures = 0;
|
|
18605
|
+
existing.suspendedUntil = null;
|
|
18532
18606
|
resetMirrorReadState(existing);
|
|
18533
18607
|
} else if (filePathChanged) {
|
|
18534
18608
|
stopMirrorStreaming(existing);
|
|
18535
18609
|
existing.dirty = true;
|
|
18536
18610
|
existing.pendingTurn = null;
|
|
18611
|
+
existing.consecutiveFailures = 0;
|
|
18612
|
+
existing.suspendedUntil = null;
|
|
18537
18613
|
resetMirrorReadState(existing);
|
|
18538
18614
|
}
|
|
18539
18615
|
watchMirrorFile(existing, filePath);
|
|
18540
18616
|
if (previousSessionId !== binding.codepilotSessionId) {
|
|
18541
|
-
|
|
18617
|
+
syncMirrorSessionStateSafe(previousSessionId, "mirror subscription rebind previous session");
|
|
18542
18618
|
}
|
|
18543
|
-
|
|
18619
|
+
syncMirrorSessionStateSafe(binding.codepilotSessionId, "mirror subscription upsert");
|
|
18544
18620
|
}
|
|
18545
18621
|
function syncMirrorSubscriptionSet() {
|
|
18546
18622
|
const { store } = getBridgeContext();
|
|
@@ -18554,7 +18630,14 @@ function syncMirrorSubscriptionSet() {
|
|
|
18554
18630
|
const desiredIds = /* @__PURE__ */ new Set();
|
|
18555
18631
|
for (const binding of desiredBindings) {
|
|
18556
18632
|
desiredIds.add(binding.id);
|
|
18557
|
-
|
|
18633
|
+
try {
|
|
18634
|
+
upsertMirrorSubscription(binding);
|
|
18635
|
+
} catch (error) {
|
|
18636
|
+
console.error(
|
|
18637
|
+
`[bridge-manager] Failed to sync mirror subscription for binding ${binding.id}:`,
|
|
18638
|
+
error
|
|
18639
|
+
);
|
|
18640
|
+
}
|
|
18558
18641
|
}
|
|
18559
18642
|
for (const bindingId of Array.from(state.mirrorSubscriptions.keys())) {
|
|
18560
18643
|
if (!desiredIds.has(bindingId)) {
|
|
@@ -18569,7 +18652,29 @@ async function reconcileMirrorSubscription(subscription) {
|
|
|
18569
18652
|
removeMirrorSubscription(subscription.bindingId);
|
|
18570
18653
|
return;
|
|
18571
18654
|
}
|
|
18572
|
-
|
|
18655
|
+
if (subscription.suspendedUntil && Date.now() < subscription.suspendedUntil) {
|
|
18656
|
+
syncMirrorSessionStateSafe(subscription.sessionId, "mirror suspension");
|
|
18657
|
+
return;
|
|
18658
|
+
}
|
|
18659
|
+
if (subscription.suspendedUntil) {
|
|
18660
|
+
subscription.suspendedUntil = null;
|
|
18661
|
+
}
|
|
18662
|
+
const desktopSession = getDesktopSessionByThreadIdSafe(
|
|
18663
|
+
subscription.threadId,
|
|
18664
|
+
"mirror reconcile"
|
|
18665
|
+
);
|
|
18666
|
+
if (!desktopSession) {
|
|
18667
|
+
subscription.missingThreadPolls += 1;
|
|
18668
|
+
if (subscription.missingThreadPolls >= DANGLING_MIRROR_THREAD_RETRY_LIMIT) {
|
|
18669
|
+
clearDanglingMirrorThread(
|
|
18670
|
+
subscription,
|
|
18671
|
+
"desktop thread no longer exists locally"
|
|
18672
|
+
);
|
|
18673
|
+
return;
|
|
18674
|
+
}
|
|
18675
|
+
} else {
|
|
18676
|
+
subscription.missingThreadPolls = 0;
|
|
18677
|
+
}
|
|
18573
18678
|
const filePathChanged = subscription.filePath !== (desktopSession?.filePath || null);
|
|
18574
18679
|
subscription.filePath = desktopSession?.filePath || null;
|
|
18575
18680
|
subscription.status = subscription.filePath ? "watching" : "stale";
|
|
@@ -18580,7 +18685,7 @@ async function reconcileMirrorSubscription(subscription) {
|
|
|
18580
18685
|
watchMirrorFile(subscription, subscription.filePath);
|
|
18581
18686
|
subscription.lastReconciledAt = nowIso2();
|
|
18582
18687
|
if (!subscription.filePath) {
|
|
18583
|
-
|
|
18688
|
+
syncMirrorSessionStateSafe(subscription.sessionId, "mirror reconcile without file");
|
|
18584
18689
|
return;
|
|
18585
18690
|
}
|
|
18586
18691
|
const snapshot = statMirrorFile(subscription.filePath);
|
|
@@ -18588,12 +18693,12 @@ async function reconcileMirrorSubscription(subscription) {
|
|
|
18588
18693
|
subscription.status = "stale";
|
|
18589
18694
|
subscription.dirty = true;
|
|
18590
18695
|
resetMirrorReadState(subscription);
|
|
18591
|
-
|
|
18696
|
+
syncMirrorSessionStateSafe(subscription.sessionId, "mirror reconcile missing snapshot");
|
|
18592
18697
|
return;
|
|
18593
18698
|
}
|
|
18594
18699
|
const unchanged = !subscription.dirty && subscription.fileIdentity === snapshot.identity && subscription.fileSize === snapshot.size && subscription.fileMtimeMs === snapshot.mtimeMs;
|
|
18595
18700
|
if (unchanged && !hasPendingMirrorWork(subscription)) {
|
|
18596
|
-
|
|
18701
|
+
syncMirrorSessionStateSafe(subscription.sessionId, "mirror reconcile unchanged snapshot");
|
|
18597
18702
|
return;
|
|
18598
18703
|
}
|
|
18599
18704
|
let deliverableRecords = [];
|
|
@@ -18648,13 +18753,13 @@ async function reconcileMirrorSubscription(subscription) {
|
|
|
18648
18753
|
console.warn("[bridge-manager] Mirror delivery failed:", error instanceof Error ? error.message : error);
|
|
18649
18754
|
}
|
|
18650
18755
|
}
|
|
18651
|
-
|
|
18756
|
+
syncMirrorSessionStateSafe(subscription.sessionId, "mirror reconcile active task");
|
|
18652
18757
|
return;
|
|
18653
18758
|
}
|
|
18654
18759
|
const finalizedTurns = timedOutTurn ? [timedOutTurn] : [];
|
|
18655
18760
|
finalizedTurns.push(...consumeBufferedMirrorTurns(subscription));
|
|
18656
18761
|
if (finalizedTurns.length === 0) {
|
|
18657
|
-
|
|
18762
|
+
syncMirrorSessionStateSafe(subscription.sessionId, "mirror reconcile no finalized turns");
|
|
18658
18763
|
return;
|
|
18659
18764
|
}
|
|
18660
18765
|
try {
|
|
@@ -18663,29 +18768,66 @@ async function reconcileMirrorSubscription(subscription) {
|
|
|
18663
18768
|
subscription.dirty = true;
|
|
18664
18769
|
console.warn("[bridge-manager] Mirror delivery failed:", error instanceof Error ? error.message : error);
|
|
18665
18770
|
}
|
|
18666
|
-
|
|
18771
|
+
syncMirrorSessionStateSafe(subscription.sessionId, "mirror reconcile delivered turns");
|
|
18667
18772
|
}
|
|
18668
18773
|
async function reconcileMirrorSubscriptions() {
|
|
18669
18774
|
const state = getState();
|
|
18670
18775
|
if (!state.running || state.mirrorSyncInFlight) return;
|
|
18671
18776
|
state.mirrorSyncInFlight = true;
|
|
18777
|
+
let stage = "sync-start";
|
|
18672
18778
|
try {
|
|
18673
|
-
|
|
18674
|
-
|
|
18779
|
+
try {
|
|
18780
|
+
stage = "sync-subscription-set";
|
|
18781
|
+
syncMirrorSubscriptionSet();
|
|
18782
|
+
} catch (error) {
|
|
18783
|
+
console.error("[bridge-manager] Mirror subscription set reconcile failed:", error);
|
|
18784
|
+
return;
|
|
18785
|
+
}
|
|
18786
|
+
stage = "snapshot-subscriptions";
|
|
18787
|
+
const subscriptions = Array.from(state.mirrorSubscriptions.values());
|
|
18788
|
+
for (const subscription of subscriptions) {
|
|
18789
|
+
stage = `subscription:${subscription.bindingId}`;
|
|
18675
18790
|
try {
|
|
18676
18791
|
await reconcileMirrorSubscription(subscription);
|
|
18792
|
+
subscription.consecutiveFailures = 0;
|
|
18793
|
+
subscription.suspendedUntil = null;
|
|
18677
18794
|
} catch (error) {
|
|
18678
|
-
|
|
18679
|
-
|
|
18680
|
-
|
|
18681
|
-
|
|
18682
|
-
|
|
18683
|
-
|
|
18684
|
-
|
|
18685
|
-
|
|
18686
|
-
|
|
18795
|
+
try {
|
|
18796
|
+
stopMirrorStreaming(subscription, "interrupted");
|
|
18797
|
+
subscription.pendingTurn = null;
|
|
18798
|
+
subscription.bufferedRecords = [];
|
|
18799
|
+
subscription.status = "stale";
|
|
18800
|
+
subscription.dirty = false;
|
|
18801
|
+
subscription.consecutiveFailures += 1;
|
|
18802
|
+
if (subscription.consecutiveFailures >= MIRROR_FAILURE_SUSPEND_THRESHOLD) {
|
|
18803
|
+
subscription.suspendedUntil = Date.now() + MIRROR_FAILURE_SUSPEND_MS;
|
|
18804
|
+
console.warn(
|
|
18805
|
+
`[bridge-manager] Mirror subscription for thread ${subscription.threadId} is suspended for ${Math.round(MIRROR_FAILURE_SUSPEND_MS / 1e3)}s after ${subscription.consecutiveFailures} consecutive failures`
|
|
18806
|
+
);
|
|
18807
|
+
}
|
|
18808
|
+
console.error(
|
|
18809
|
+
`[bridge-manager] Mirror reconcile failed for thread ${subscription.threadId}:`,
|
|
18810
|
+
describeUnknownError(error)
|
|
18811
|
+
);
|
|
18812
|
+
syncMirrorSessionStateSafe(subscription.sessionId, "mirror reconcile failure");
|
|
18813
|
+
} catch (recoveryError) {
|
|
18814
|
+
console.error(
|
|
18815
|
+
`[bridge-manager] Mirror reconcile recovery failed for thread ${subscription.threadId}:`,
|
|
18816
|
+
describeUnknownError(recoveryError)
|
|
18817
|
+
);
|
|
18818
|
+
console.error(
|
|
18819
|
+
`[bridge-manager] Original mirror reconcile error for thread ${subscription.threadId}:`,
|
|
18820
|
+
describeUnknownError(error)
|
|
18821
|
+
);
|
|
18822
|
+
}
|
|
18687
18823
|
}
|
|
18688
18824
|
}
|
|
18825
|
+
stage = "sync-complete";
|
|
18826
|
+
} catch (error) {
|
|
18827
|
+
console.error(
|
|
18828
|
+
`[bridge-manager] Mirror reconcile failed during ${stage}:`,
|
|
18829
|
+
describeUnknownError(error)
|
|
18830
|
+
);
|
|
18689
18831
|
} finally {
|
|
18690
18832
|
state.mirrorSyncInFlight = false;
|
|
18691
18833
|
}
|
|
@@ -18774,6 +18916,8 @@ function listEnabledAdapterInstances() {
|
|
|
18774
18916
|
async function stopAdapterInstance(channelType) {
|
|
18775
18917
|
const state = getState();
|
|
18776
18918
|
const adapter = state.adapters.get(channelType);
|
|
18919
|
+
state.invalidAdapters.delete(channelType);
|
|
18920
|
+
INVALID_ADAPTER_WARNING_CACHE.delete(channelType);
|
|
18777
18921
|
if (!adapter) return;
|
|
18778
18922
|
state.loopAborts.get(channelType)?.abort();
|
|
18779
18923
|
state.loopAborts.delete(channelType);
|
|
@@ -18799,6 +18943,14 @@ async function syncConfiguredAdapters(options) {
|
|
|
18799
18943
|
await stopAdapterInstance(existingKey);
|
|
18800
18944
|
changed = true;
|
|
18801
18945
|
}
|
|
18946
|
+
for (const invalidKey of Array.from(state.invalidAdapters.keys())) {
|
|
18947
|
+
if (desiredKeys.has(invalidKey)) continue;
|
|
18948
|
+
state.invalidAdapters.delete(invalidKey);
|
|
18949
|
+
}
|
|
18950
|
+
for (const invalidKey of Array.from(INVALID_ADAPTER_WARNING_CACHE.keys())) {
|
|
18951
|
+
if (desiredKeys.has(invalidKey)) continue;
|
|
18952
|
+
INVALID_ADAPTER_WARNING_CACHE.delete(invalidKey);
|
|
18953
|
+
}
|
|
18802
18954
|
for (const instance of desiredInstances) {
|
|
18803
18955
|
const existing = state.adapters.get(instance.id);
|
|
18804
18956
|
const desiredFingerprint = desiredFingerprints.get(instance.id) || "";
|
|
@@ -18812,9 +18964,16 @@ async function syncConfiguredAdapters(options) {
|
|
|
18812
18964
|
if (!adapter) continue;
|
|
18813
18965
|
const configError = adapter.validateConfig();
|
|
18814
18966
|
if (configError) {
|
|
18815
|
-
|
|
18967
|
+
const invalidSignature = `${desiredFingerprint}:${configError}`;
|
|
18968
|
+
if (INVALID_ADAPTER_WARNING_CACHE.get(instance.id) !== invalidSignature) {
|
|
18969
|
+
console.warn(`[bridge-manager] ${instance.id} adapter not valid:`, configError);
|
|
18970
|
+
INVALID_ADAPTER_WARNING_CACHE.set(instance.id, invalidSignature);
|
|
18971
|
+
state.invalidAdapters.set(instance.id, invalidSignature);
|
|
18972
|
+
}
|
|
18816
18973
|
continue;
|
|
18817
18974
|
}
|
|
18975
|
+
state.invalidAdapters.delete(instance.id);
|
|
18976
|
+
INVALID_ADAPTER_WARNING_CACHE.delete(instance.id);
|
|
18818
18977
|
try {
|
|
18819
18978
|
state.adapters.set(instance.id, adapter);
|
|
18820
18979
|
state.adapterMeta.set(instance.id, {
|
|
@@ -18874,11 +19033,11 @@ async function start() {
|
|
|
18874
19033
|
}, 5e3);
|
|
18875
19034
|
state.mirrorPollTimer = setInterval(() => {
|
|
18876
19035
|
void reconcileMirrorSubscriptions().catch((err) => {
|
|
18877
|
-
console.error("[bridge-manager] Mirror reconcile failed:", err);
|
|
19036
|
+
console.error("[bridge-manager] Mirror reconcile failed:", describeUnknownError(err));
|
|
18878
19037
|
});
|
|
18879
19038
|
}, MIRROR_POLL_INTERVAL_MS);
|
|
18880
19039
|
void reconcileMirrorSubscriptions().catch((err) => {
|
|
18881
|
-
console.error("[bridge-manager] Initial mirror reconcile failed:", err);
|
|
19040
|
+
console.error("[bridge-manager] Initial mirror reconcile failed:", describeUnknownError(err));
|
|
18882
19041
|
});
|
|
18883
19042
|
console.log(`[bridge-manager] Bridge started with ${startedCount} adapter(s)`);
|
|
18884
19043
|
}
|
|
@@ -18911,6 +19070,8 @@ async function stop() {
|
|
|
18911
19070
|
state.mirrorSuppressUntil.clear();
|
|
18912
19071
|
state.mirrorIgnoredTurnIds.clear();
|
|
18913
19072
|
state.queuedCounts.clear();
|
|
19073
|
+
state.invalidAdapters.clear();
|
|
19074
|
+
INVALID_ADAPTER_WARNING_CACHE.clear();
|
|
18914
19075
|
for (const sessionId of activeSessionIds) {
|
|
18915
19076
|
syncSessionRuntimeState(sessionId);
|
|
18916
19077
|
}
|
|
@@ -19403,6 +19564,10 @@ async function handleCommand(adapter, msg, text2) {
|
|
|
19403
19564
|
}
|
|
19404
19565
|
if (args === "all") {
|
|
19405
19566
|
const desktopSessions = getDisplayedDesktopThreads(MAX_DESKTOP_THREAD_LIST_LIMIT);
|
|
19567
|
+
if (!desktopSessions) {
|
|
19568
|
+
response = "\u8BFB\u53D6\u684C\u9762\u4F1A\u8BDD\u5217\u8868\u5931\u8D25\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5\u3002";
|
|
19569
|
+
break;
|
|
19570
|
+
}
|
|
19406
19571
|
if (desktopSessions.length === 0) {
|
|
19407
19572
|
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
19573
|
break;
|
|
@@ -19415,6 +19580,10 @@ async function handleCommand(adapter, msg, text2) {
|
|
|
19415
19580
|
break;
|
|
19416
19581
|
}
|
|
19417
19582
|
const displayedThreads = getDisplayedDesktopThreads(MAX_DESKTOP_THREAD_LIST_LIMIT);
|
|
19583
|
+
if (!displayedThreads) {
|
|
19584
|
+
response = "\u8BFB\u53D6\u684C\u9762\u4F1A\u8BDD\u5217\u8868\u5931\u8D25\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5\u3002";
|
|
19585
|
+
break;
|
|
19586
|
+
}
|
|
19418
19587
|
const threadPick = resolveByIndexOrPrefix(args, displayedThreads, (session) => session.threadId);
|
|
19419
19588
|
if (threadPick.ambiguous) {
|
|
19420
19589
|
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 +19591,7 @@ async function handleCommand(adapter, msg, text2) {
|
|
|
19422
19591
|
}
|
|
19423
19592
|
if (!threadPick.match) {
|
|
19424
19593
|
if (validateSessionId(args)) {
|
|
19425
|
-
const desktop =
|
|
19594
|
+
const desktop = getDesktopSessionByThreadIdSafe(args, "thread switch");
|
|
19426
19595
|
let binding2;
|
|
19427
19596
|
try {
|
|
19428
19597
|
binding2 = bindToSdkSession(msg.address, args, desktop ? {
|
|
@@ -19481,6 +19650,10 @@ async function handleCommand(adapter, msg, text2) {
|
|
|
19481
19650
|
}
|
|
19482
19651
|
const { showAll, limit } = listArgs;
|
|
19483
19652
|
const desktopSessions = getDisplayedDesktopThreads(limit);
|
|
19653
|
+
if (!desktopSessions) {
|
|
19654
|
+
response = "\u8BFB\u53D6\u684C\u9762\u4F1A\u8BDD\u5217\u8868\u5931\u8D25\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5\u3002";
|
|
19655
|
+
break;
|
|
19656
|
+
}
|
|
19484
19657
|
if (desktopSessions.length === 0) {
|
|
19485
19658
|
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
19659
|
break;
|
|
@@ -20887,6 +21060,7 @@ var PendingPermissions = class {
|
|
|
20887
21060
|
// src/logger.ts
|
|
20888
21061
|
import fs11 from "node:fs";
|
|
20889
21062
|
import path13 from "node:path";
|
|
21063
|
+
import { inspect as inspect2 } from "node:util";
|
|
20890
21064
|
var MASK_PATTERNS = [
|
|
20891
21065
|
/(?:token|secret|password|api_key)["']?\s*[:=]\s*["']?([^\s"',]+)/gi,
|
|
20892
21066
|
/bot\d+:[A-Za-z0-9_-]{35}/g,
|
|
@@ -20908,6 +21082,22 @@ var LOG_PATH = path13.join(LOG_DIR, "bridge.log");
|
|
|
20908
21082
|
var MAX_LOG_SIZE = 10 * 1024 * 1024;
|
|
20909
21083
|
var MAX_ROTATED = 3;
|
|
20910
21084
|
var logStream = null;
|
|
21085
|
+
function formatLogArg(value) {
|
|
21086
|
+
if (typeof value === "string") return value;
|
|
21087
|
+
if (value instanceof Error) {
|
|
21088
|
+
return value.stack || `${value.name}: ${value.message}`;
|
|
21089
|
+
}
|
|
21090
|
+
if (value === null) return "null";
|
|
21091
|
+
if (typeof value === "undefined") return "undefined";
|
|
21092
|
+
if (typeof value === "object") {
|
|
21093
|
+
return inspect2(value, {
|
|
21094
|
+
depth: 4,
|
|
21095
|
+
breakLength: Infinity,
|
|
21096
|
+
compact: true
|
|
21097
|
+
});
|
|
21098
|
+
}
|
|
21099
|
+
return String(value);
|
|
21100
|
+
}
|
|
20911
21101
|
function openLogStream() {
|
|
20912
21102
|
return fs11.createWriteStream(LOG_PATH, { flags: "a" });
|
|
20913
21103
|
}
|
|
@@ -20937,7 +21127,7 @@ function setupLogger() {
|
|
|
20937
21127
|
logStream = openLogStream();
|
|
20938
21128
|
const write = (level, args) => {
|
|
20939
21129
|
const timestamp = (/* @__PURE__ */ new Date()).toISOString();
|
|
20940
|
-
const message = args.map((a) =>
|
|
21130
|
+
const message = args.map((a) => formatLogArg(a)).join(" ");
|
|
20941
21131
|
const formatted = `[${timestamp}] [${level}] ${message}`;
|
|
20942
21132
|
const masked = maskSecrets(formatted);
|
|
20943
21133
|
rotateIfNeeded();
|