claude-codex-wechat 0.1.18 → 0.1.20

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.
@@ -2,9 +2,9 @@
2
2
  import { createRequire as __cjsCreateRequire } from 'node:module'; const require = __cjsCreateRequire(import.meta.url);
3
3
 
4
4
  // src/cli.ts
5
- import { existsSync as existsSync10, readFileSync as readFileSync8 } from "node:fs";
6
- import { dirname as dirname12, join as join12 } from "node:path";
7
- import { fileURLToPath } from "node:url";
5
+ import { existsSync as existsSync10, readFileSync as readFileSync9 } from "node:fs";
6
+ import { dirname as dirname13, join as join13 } from "node:path";
7
+ import { fileURLToPath as fileURLToPath2 } from "node:url";
8
8
 
9
9
  // src/daemon/bootstrap.ts
10
10
  import { networkInterfaces } from "node:os";
@@ -81,9 +81,24 @@ function normalizeBridgeConfig(raw, env, path) {
81
81
  wechat: normalizeWechatConfig(record.wechat, env),
82
82
  bridge: normalizeBridgeDefaultsConfig(record.bridge),
83
83
  tunnel: normalizeTunnelConfig(record.tunnel),
84
+ update: normalizeUpdateStatusConfig(record.update),
84
85
  providers: normalizeProvidersConfig(record.providers, env)
85
86
  };
86
87
  }
88
+ function normalizeUpdateStatusConfig(raw) {
89
+ const record = raw && typeof raw === "object" ? raw : {};
90
+ const currentVersion = typeof record.currentVersion === "string" && record.currentVersion ? record.currentVersion : void 0;
91
+ const latestVersion = typeof record.latestVersion === "string" && record.latestVersion ? record.latestVersion : void 0;
92
+ const lastCheckedAt = typeof record.lastCheckedAt === "number" && Number.isFinite(record.lastCheckedAt) ? record.lastCheckedAt : void 0;
93
+ const updateAvailable = record.updateAvailable === true;
94
+ if (!currentVersion && !latestVersion && lastCheckedAt === void 0 && !updateAvailable) return void 0;
95
+ return {
96
+ ...currentVersion ? { currentVersion } : {},
97
+ ...latestVersion ? { latestVersion } : {},
98
+ updateAvailable,
99
+ ...lastCheckedAt !== void 0 ? { lastCheckedAt } : {}
100
+ };
101
+ }
87
102
  function normalizeProvidersConfig(raw, env) {
88
103
  const record = raw && typeof raw === "object" ? raw : {};
89
104
  const claude = normalizeProviderCommand(record.claude, env.BRIDGE_CLAUDE_COMMAND);
@@ -164,6 +179,21 @@ async function deleteConfigFile(configPath) {
164
179
  throw error;
165
180
  }
166
181
  }
182
+ function persistUpdateStatusToConfigFile(input) {
183
+ const currentConfig = readConfigFileSync(input.configPath);
184
+ const nextConfig = {
185
+ ...currentConfig,
186
+ update: {
187
+ ...input.status.currentVersion ? { currentVersion: input.status.currentVersion } : {},
188
+ ...input.status.latestVersion ? { latestVersion: input.status.latestVersion } : {},
189
+ updateAvailable: input.status.updateAvailable,
190
+ ...input.status.lastCheckedAt !== void 0 ? { lastCheckedAt: input.status.lastCheckedAt } : {}
191
+ }
192
+ };
193
+ mkdirSync(dirname(input.configPath), { recursive: true });
194
+ writeFileSync(input.configPath, `${JSON.stringify(nextConfig, null, 2)}
195
+ `, "utf8");
196
+ }
167
197
  async function persistBridgeDefaultsToConfigFile(input) {
168
198
  const currentConfig = await readConfigFile(input.configPath);
169
199
  const currentBridge = isRecord(currentConfig.bridge) ? currentConfig.bridge : void 0;
@@ -2319,7 +2349,7 @@ var WeixinOutboundGate = class _WeixinOutboundGate {
2319
2349
  };
2320
2350
 
2321
2351
  // src/providers/claude-code/claudeStreamingRunner.ts
2322
- import { spawn as spawn2 } from "node:child_process";
2352
+ import { spawn as spawn3 } from "node:child_process";
2323
2353
  import { randomUUID as randomUUID2 } from "node:crypto";
2324
2354
 
2325
2355
  // src/shared/platform.ts
@@ -2384,14 +2414,129 @@ function statePath(filename) {
2384
2414
  return join8(tmpdir(), filename);
2385
2415
  }
2386
2416
 
2417
+ // src/providers/claude-code/assistantContent.ts
2418
+ function extractAssistantBlocks(message) {
2419
+ if (!message || typeof message !== "object") return [];
2420
+ const content = message.content;
2421
+ if (!Array.isArray(content)) return [];
2422
+ return content.flatMap((item) => {
2423
+ if (!item || typeof item !== "object") return [];
2424
+ const record = item;
2425
+ if (record.type === "text" && typeof record.text === "string") {
2426
+ return [{ type: "text", text: record.text }];
2427
+ }
2428
+ if (record.type === "tool_use" && record.name === "AskUserQuestion") {
2429
+ const rendered = renderAskUserQuestion(record.input);
2430
+ return rendered ? [{ type: "choice", ...rendered }] : [];
2431
+ }
2432
+ return [];
2433
+ });
2434
+ }
2435
+ function renderAskUserQuestion(input) {
2436
+ if (!input || typeof input !== "object") return null;
2437
+ const questions = input.questions;
2438
+ if (!Array.isArray(questions) || questions.length === 0) return null;
2439
+ const singleQuestion = questions.length === 1;
2440
+ const blocks = [];
2441
+ let mappedLabels = [];
2442
+ let mappedMultiSelect = false;
2443
+ for (const raw of questions) {
2444
+ if (!raw || typeof raw !== "object") continue;
2445
+ const q = raw;
2446
+ const question = typeof q.question === "string" ? q.question.trim() : "";
2447
+ if (!question) continue;
2448
+ const multiSelect = q.multiSelect === true;
2449
+ const lines = [`\u2753 ${question}`];
2450
+ const labels = [];
2451
+ const options = Array.isArray(q.options) ? q.options : [];
2452
+ for (const rawOption of options) {
2453
+ if (!rawOption || typeof rawOption !== "object") continue;
2454
+ const option = rawOption;
2455
+ const label = typeof option.label === "string" ? option.label.trim() : "";
2456
+ if (!label) continue;
2457
+ const description = typeof option.description === "string" ? option.description.trim() : "";
2458
+ labels.push(label);
2459
+ const bullet = singleQuestion ? `${labels.length}.` : "\u30FB";
2460
+ lines.push(description ? `${bullet} ${label} \u2014\u2014 ${description}` : `${bullet} ${label}`);
2461
+ }
2462
+ if (!singleQuestion) {
2463
+ lines.push("\uFF08\u56DE\u590D\u5BF9\u5E94\u9009\u9879\u7684\u6587\u5B57\u5373\u53EF\uFF09");
2464
+ } else {
2465
+ lines.push(multiSelect ? "\uFF08\u53EF\u591A\u9009\uFF0C\u56DE\u590D\u5E8F\u53F7\u6216\u9009\u9879\u6587\u5B57\uFF0C\u591A\u4E2A\u7528\u9017\u53F7\u5206\u9694\uFF0C\u5982 1,2\uFF09" : "\uFF08\u56DE\u590D\u5E8F\u53F7\u6216\u9009\u9879\u6587\u5B57\u5373\u53EF\uFF0C\u5982 1\uFF09");
2466
+ }
2467
+ blocks.push(lines.join("\n"));
2468
+ if (singleQuestion) {
2469
+ mappedLabels = labels;
2470
+ mappedMultiSelect = multiSelect;
2471
+ }
2472
+ }
2473
+ if (blocks.length === 0) return null;
2474
+ return { text: blocks.join("\n\n"), labels: mappedLabels, multiSelect: mappedMultiSelect };
2475
+ }
2476
+ function mapChoiceReply(text, labels, multiSelect) {
2477
+ if (labels.length === 0) return null;
2478
+ const normalized = text.trim().replace(/[0-9]/g, (d) => String.fromCharCode(d.charCodeAt(0) - 65296 + 48));
2479
+ if (!normalized) return null;
2480
+ const tokens = normalized.split(/[\s,,、]+/).filter(Boolean);
2481
+ if (tokens.length === 0) return null;
2482
+ if (!multiSelect && tokens.length > 1) return null;
2483
+ const picked = [];
2484
+ for (const token of tokens) {
2485
+ const digits = token.replace(/[.。))::]+$/, "");
2486
+ if (!/^\d+$/.test(digits)) return null;
2487
+ const index = Number(digits);
2488
+ if (index < 1 || index > labels.length) return null;
2489
+ const label = labels[index - 1];
2490
+ if (!picked.includes(label)) picked.push(label);
2491
+ }
2492
+ return picked.length > 0 ? picked.join(", ") : null;
2493
+ }
2494
+
2495
+ // src/providers/claude-code/bridgeSystemPrompt.ts
2496
+ var BRIDGE_APPEND_SYSTEM_PROMPT = [
2497
+ "\u4F60\u6B63\u8FD0\u884C\u5728\u4E00\u4E2A\u5FAE\u4FE1\u7EAF\u6587\u672C\u6865\u63A5\u73AF\u5883\u4E2D,\u7528\u6237\u53EA\u80FD\u6536\u53D1\u6587\u672C\u6D88\u606F,\u6CA1\u6709\u4EFB\u4F55\u4EA4\u4E92\u5F0F\u5F39\u7A97\u6216\u6309\u94AE\u3002",
2498
+ "\u5F53\u4F60\u9700\u8981\u7528\u6237\u505A\u9009\u62E9\u65F6,\u8BF7\u76F4\u63A5\u5728\u56DE\u590D\u91CC\u7528\u5E26\u5E8F\u53F7\u7684\u5217\u8868\u5217\u51FA\u9009\u9879(\u4F8B\u5982\u300C1. \u9009\u9879A\u300D\u300C2. \u9009\u9879B\u300D),",
2499
+ "\u5E76\u544A\u8BC9\u7528\u6237\u56DE\u590D\u5E8F\u53F7\u6216\u9009\u9879\u6587\u5B57\u5373\u53EF;\u4E0D\u8981\u8C03\u7528 AskUserQuestion \u5DE5\u5177(\u5B83\u5728\u6B64\u73AF\u5883\u4E2D\u65E0\u6CD5\u4EA4\u4E92)\u3002"
2500
+ ].join("");
2501
+
2502
+ // src/providers/claude-code/claudeCapabilities.ts
2503
+ import { spawn as spawn2 } from "node:child_process";
2504
+ var cache = /* @__PURE__ */ new Map();
2505
+ var probeAppendSystemPromptSupport = (command) => {
2506
+ const cached = cache.get(command);
2507
+ if (cached) return cached;
2508
+ const result = runHelpProbe(command);
2509
+ cache.set(command, result);
2510
+ return result;
2511
+ };
2512
+ function runHelpProbe(command) {
2513
+ return new Promise((resolve2) => {
2514
+ try {
2515
+ const child = spawn2(command, ["--help"], { stdio: ["ignore", "pipe", "pipe"], shell: useShellForCli() });
2516
+ let output = "";
2517
+ const collect = (data) => {
2518
+ output += String(data);
2519
+ };
2520
+ child.stdout?.on("data", collect);
2521
+ child.stderr?.on("data", collect);
2522
+ child.on("error", () => resolve2(false));
2523
+ child.on("close", () => resolve2(output.includes("--append-system-prompt")));
2524
+ } catch {
2525
+ resolve2(false);
2526
+ }
2527
+ });
2528
+ }
2529
+
2387
2530
  // src/providers/claude-code/claudeStreamingRunner.ts
2388
2531
  var ClaudeStreamingRunner = class {
2389
2532
  sessions = /* @__PURE__ */ new Map();
2390
2533
  command;
2391
2534
  spawner;
2535
+ capabilityProbe;
2392
2536
  constructor(input = {}) {
2393
2537
  this.command = input.command ?? "claude";
2394
2538
  this.spawner = input.spawner ?? defaultClaudeStreamSpawner;
2539
+ this.capabilityProbe = input.capabilityProbe ?? probeAppendSystemPromptSupport;
2395
2540
  }
2396
2541
  async startSession(input) {
2397
2542
  const resumeId = input.options?.providerSessionId;
@@ -2412,6 +2557,9 @@ var ClaudeStreamingRunner = class {
2412
2557
  async *sendMessage(input) {
2413
2558
  const session = this.sessions.get(input.bridgeSessionId);
2414
2559
  if (!session) throw new Error(`claude_session_not_found:${input.bridgeSessionId}`);
2560
+ if (session.supportsAppendSystemPrompt === void 0) {
2561
+ session.supportsAppendSystemPrompt = await this.capabilityProbe(this.command);
2562
+ }
2415
2563
  const handle = this.ensureHandle(session);
2416
2564
  session.pendingFollowUps = 0;
2417
2565
  handle.write(userEnvelope(input.text));
@@ -2448,8 +2596,11 @@ var ClaudeStreamingRunner = class {
2448
2596
  }
2449
2597
  if (type === "assistant") {
2450
2598
  let emitted = false;
2451
- for (const text of extractAssistantText(record.message)) {
2452
- yield { type: "text_delta", text };
2599
+ for (const block of extractAssistantBlocks(record.message)) {
2600
+ yield { type: "text_delta", text: block.text };
2601
+ if (block.type === "choice" && block.labels.length > 0) {
2602
+ yield { type: "choice_prompt", labels: block.labels, multiSelect: block.multiSelect };
2603
+ }
2453
2604
  emitted = true;
2454
2605
  }
2455
2606
  if (emitted) yield { type: "message_done" };
@@ -2510,6 +2661,7 @@ function buildStreamingArgs(session) {
2510
2661
  "--include-partial-messages",
2511
2662
  "--verbose",
2512
2663
  "--dangerously-skip-permissions",
2664
+ ...session.supportsAppendSystemPrompt ? ["--append-system-prompt", BRIDGE_APPEND_SYSTEM_PROMPT] : [],
2513
2665
  "--replay-user-messages"
2514
2666
  ];
2515
2667
  if (session.resumeId) {
@@ -2528,16 +2680,6 @@ function userEnvelope(text) {
2528
2680
  return `${JSON.stringify({ type: "user", message: { role: "user", content: text }, parent_tool_use_id: null })}
2529
2681
  `;
2530
2682
  }
2531
- function extractAssistantText(message) {
2532
- if (!message || typeof message !== "object") return [];
2533
- const content = message.content;
2534
- if (!Array.isArray(content)) return [];
2535
- return content.flatMap((item) => {
2536
- if (!item || typeof item !== "object") return [];
2537
- const record = item;
2538
- return record.type === "text" && typeof record.text === "string" ? [record.text] : [];
2539
- });
2540
- }
2541
2683
  function extractError(record) {
2542
2684
  if (typeof record.message === "string") return record.message;
2543
2685
  if (record.error && typeof record.error === "object") {
@@ -2554,7 +2696,7 @@ function parseJsonLine(line) {
2554
2696
  }
2555
2697
  }
2556
2698
  function defaultClaudeStreamSpawner(call) {
2557
- const child = spawn2(call.command, call.args, { cwd: expandTilde(call.cwd) ?? call.cwd, stdio: ["pipe", "pipe", "pipe"], shell: useShellForCli() });
2699
+ const child = spawn3(call.command, call.args, { cwd: expandTilde(call.cwd) ?? call.cwd, stdio: ["pipe", "pipe", "pipe"], shell: useShellForCli() });
2558
2700
  let stderr = "";
2559
2701
  let buffer = "";
2560
2702
  const queue = [];
@@ -2653,7 +2795,7 @@ var ClaudeCodeProvider = class {
2653
2795
  };
2654
2796
 
2655
2797
  // src/providers/codex/codexAppServerClient.ts
2656
- import { spawn as spawn3 } from "node:child_process";
2798
+ import { spawn as spawn4 } from "node:child_process";
2657
2799
  var CodexAppServerClient = class {
2658
2800
  child;
2659
2801
  pending = /* @__PURE__ */ new Map();
@@ -2664,7 +2806,7 @@ var CodexAppServerClient = class {
2664
2806
  buffer = "";
2665
2807
  constructor(input) {
2666
2808
  const command = input.command ?? "codex";
2667
- this.child = spawn3(command, ["app-server", "--listen", "stdio://"], {
2809
+ this.child = spawn4(command, ["app-server", "--listen", "stdio://"], {
2668
2810
  cwd: input.cwd,
2669
2811
  stdio: ["pipe", "pipe", "pipe"],
2670
2812
  shell: useShellForCli()
@@ -3071,10 +3213,10 @@ function createDefaultProviders(input = {}) {
3071
3213
  }
3072
3214
 
3073
3215
  // src/providers/claude-code/claudeDetection.ts
3074
- import { spawn as spawn4 } from "node:child_process";
3216
+ import { spawn as spawn5 } from "node:child_process";
3075
3217
  async function defaultCommandRunner(command, args) {
3076
3218
  return await new Promise((resolve2) => {
3077
- const child = spawn4(command, args, { stdio: ["ignore", "pipe", "pipe"], shell: useShellForCli() });
3219
+ const child = spawn5(command, args, { stdio: ["ignore", "pipe", "pipe"], shell: useShellForCli() });
3078
3220
  let stdout = "";
3079
3221
  let stderr = "";
3080
3222
  child.stdout.on("data", (chunk) => {
@@ -3107,10 +3249,10 @@ ${result.stderr}`) };
3107
3249
  }
3108
3250
 
3109
3251
  // src/providers/codex/codexDetection.ts
3110
- import { spawn as spawn5 } from "node:child_process";
3252
+ import { spawn as spawn6 } from "node:child_process";
3111
3253
  async function defaultCodexCommandRunner(command, args) {
3112
3254
  return await new Promise((resolve2) => {
3113
- const child = spawn5(command, args, { stdio: ["ignore", "pipe", "pipe"], shell: useShellForCli() });
3255
+ const child = spawn6(command, args, { stdio: ["ignore", "pipe", "pipe"], shell: useShellForCli() });
3114
3256
  let stdout = "";
3115
3257
  let stderr = "";
3116
3258
  child.stdout.on("data", (chunk) => {
@@ -3377,6 +3519,10 @@ var MessageRouter = class _MessageRouter {
3377
3519
  opSeq = 0;
3378
3520
  latestMutatingSeq = /* @__PURE__ */ new Map();
3379
3521
  activeGenerations = /* @__PURE__ */ new Map();
3522
+ // 最近一次 AskUserQuestion 的选项,按 chatId 暂存,并记住是哪个 bridge 会话发出的。
3523
+ // 下一条入站消息若是纯序号,用它翻译成选项文字再转发(消费一次即清除);不是序号、
3524
+ // 或当前会话已变(sessionId 不匹配)则原样透传,避免旧选项串到别的会话。
3525
+ pendingChoices = /* @__PURE__ */ new Map();
3380
3526
  get conversation() {
3381
3527
  return this.options.conversation;
3382
3528
  }
@@ -3452,12 +3598,24 @@ var MessageRouter = class _MessageRouter {
3452
3598
  if ((this.latestMutatingSeq.get(message.chatId) ?? 0) > seq) {
3453
3599
  return { status: "accepted" };
3454
3600
  }
3455
- if (await this.maybeSteer(message.chatId, command.text)) {
3601
+ const chatText = this.resolveChoiceReply(message.chatId, command.text);
3602
+ if (await this.maybeSteer(message.chatId, chatText)) {
3456
3603
  return { status: "accepted" };
3457
3604
  }
3458
- await this.runExclusive(() => this.runChatGeneration(message, user, command.text, seq));
3605
+ await this.runExclusive(() => this.runChatGeneration(message, user, chatText, seq));
3459
3606
  return { status: "accepted" };
3460
3607
  }
3608
+ // 消费一次上一轮暂存的 AskUserQuestion 选项:纯序号回复翻译成对应选项文字,
3609
+ // 其它情况原样透传。取用后即清除,避免过期映射误伤后续消息。若暂存所属会话
3610
+ // 与当前会话不一致(用户已 /new、/resume 或切换 provider),也一律不翻译,
3611
+ // 防止旧会话的选项串到新会话。
3612
+ resolveChoiceReply(chatId, text) {
3613
+ const pending = this.pendingChoices.get(chatId);
3614
+ if (!pending) return text;
3615
+ this.pendingChoices.delete(chatId);
3616
+ if (pending.sessionId !== this.conversation.getCurrent()?.id) return text;
3617
+ return mapChoiceReply(text, pending.labels, pending.multiSelect) ?? text;
3618
+ }
3461
3619
  // A chat message arriving while a turn is in flight: if the provider supports
3462
3620
  // native steer (Codex turn/steer), inject it into the running turn instead of
3463
3621
  // queueing a new turn. Otherwise fall back to the serialized lock (queue).
@@ -3584,6 +3742,9 @@ var MessageRouter = class _MessageRouter {
3584
3742
  await this.sendToChat({ chatId: message.chatId, kind: "text", text: bufferedText });
3585
3743
  bufferedText = "";
3586
3744
  }
3745
+ if (event.type === "choice_prompt" && event.labels.length > 0) {
3746
+ this.pendingChoices.set(message.chatId, { sessionId: session.id, labels: event.labels, multiSelect: event.multiSelect });
3747
+ }
3587
3748
  if (event.type === "session_state") {
3588
3749
  const updated = this.conversation.update({
3589
3750
  providerSessionId: event.state.providerSessionId,
@@ -3655,6 +3816,7 @@ var MessageRouter = class _MessageRouter {
3655
3816
  return;
3656
3817
  }
3657
3818
  if (command.kind === "new_session") {
3819
+ this.pendingChoices.delete(chatId);
3658
3820
  await this.preemptActiveGeneration(chatId);
3659
3821
  const providerId = command.providerId ?? this.conversation.getCurrent()?.providerId ?? this.options.defaults?.defaultProvider ?? "claude-code";
3660
3822
  const cwd = command.cwd ?? this.conversation.getCurrent()?.cwd ?? this.options.defaults?.defaultWorkspace ?? defaultWorkspaceDir();
@@ -3720,6 +3882,7 @@ var MessageRouter = class _MessageRouter {
3720
3882
  return;
3721
3883
  }
3722
3884
  if (command.kind === "resume_session") {
3885
+ this.pendingChoices.delete(chatId);
3723
3886
  const ref = command.ref.trim();
3724
3887
  if (!ref) {
3725
3888
  await this.sendToChat({ chatId, kind: "status", text: "\u7528\u6CD5\uFF1A/resume <\u7F16\u53F7>" });
@@ -3983,6 +4146,7 @@ var BridgeEventHub = class {
3983
4146
  };
3984
4147
 
3985
4148
  // src/runtime/relayTunnelProvider.ts
4149
+ var WS_OPEN = 1;
3986
4150
  var RelayTunnelProvider = class {
3987
4151
  constructor(options) {
3988
4152
  this.options = options;
@@ -3997,6 +4161,9 @@ var RelayTunnelProvider = class {
3997
4161
  stopped = true;
3998
4162
  reconnectAttempts = 0;
3999
4163
  reconnectTimer = null;
4164
+ // 能力协商结果:仅当服务器在 registered 回包里声明 streaming:true 时才走流式,
4165
+ // 否则退回缓冲(兼容老服务器)。
4166
+ serverSupportsStreaming = false;
4000
4167
  static defaultCreateSocket(url) {
4001
4168
  const socket = new WebSocket(url);
4002
4169
  return {
@@ -4062,7 +4229,10 @@ var RelayTunnelProvider = class {
4062
4229
  type: "register",
4063
4230
  clientVersion: "0.1.0",
4064
4231
  targetBaseUrl: this.options.targetBaseUrl,
4065
- authToken: this.options.authToken
4232
+ authToken: this.options.authToken,
4233
+ // 声明支持流式转发。新服务器会在 registered 回包里回 streaming:true;
4234
+ // 老服务器忽略此字段,客户端据此自动退回缓冲。
4235
+ supportsStreaming: true
4066
4236
  }));
4067
4237
  });
4068
4238
  socket.on("message", (raw) => {
@@ -4081,6 +4251,7 @@ var RelayTunnelProvider = class {
4081
4251
  }
4082
4252
  if (payload?.type === "registered") {
4083
4253
  this.reconnectAttempts = 0;
4254
+ this.serverSupportsStreaming = payload?.streaming === true;
4084
4255
  const publicUrl = typeof payload?.publicUrl === "string" && payload.publicUrl ? payload.publicUrl : typeof payload?.token === "string" ? `${derivePublicBaseUrl(this.options.serverUrl)}/${payload.token}` : void 0;
4085
4256
  this.status = {
4086
4257
  installed: true,
@@ -4092,7 +4263,10 @@ var RelayTunnelProvider = class {
4092
4263
  return;
4093
4264
  }
4094
4265
  if (payload?.type === "request") {
4095
- void this.handleRequest(payload);
4266
+ const requestId = typeof payload?.requestId === "string" ? payload.requestId : "unknown";
4267
+ void this.handleRequest(payload).catch((error) => {
4268
+ console.warn(`[relay] \u5904\u7406\u8BF7\u6C42 ${requestId} \u5931\u8D25\uFF1A`, error instanceof Error ? error.message : String(error));
4269
+ });
4096
4270
  }
4097
4271
  });
4098
4272
  socket.on("close", () => {
@@ -4139,22 +4313,91 @@ var RelayTunnelProvider = class {
4139
4313
  }, delay3);
4140
4314
  this.reconnectTimer.unref?.();
4141
4315
  }
4142
- async handleRequest(payload) {
4143
- const fetchImpl = this.options.fetchImpl ?? fetch;
4144
- const response = await fetchImpl(`${this.options.targetBaseUrl}${payload.path}`, {
4145
- method: payload.method,
4146
- headers: payload.headers,
4147
- body: payload.bodyBase64 ? Buffer.from(payload.bodyBase64, "base64") : void 0
4148
- });
4149
- const bodyBuffer = Buffer.from(await response.arrayBuffer());
4150
- this.socket?.send(JSON.stringify({
4316
+ // 只在连接就绪时发送;未就绪(重连中/尚未 OPEN)时静默跳过(属正常态,不记日志、
4317
+ // 不关连接,以免误伤正在进行的重连)。底层 send 真抛错说明 socket 已坏:记一条 warn,
4318
+ // 并主动 close() 让 close 事件驱动重连、由服务端 WS close 清理它那边的挂起请求。
4319
+ safeSend(payload) {
4320
+ const socket = this.socket;
4321
+ if (!socket) return false;
4322
+ if (socket.readyState !== void 0 && socket.readyState !== WS_OPEN) return false;
4323
+ try {
4324
+ socket.send(payload);
4325
+ return true;
4326
+ } catch (error) {
4327
+ console.warn("[relay] \u56DE\u4F20\u53D1\u9001\u5931\u8D25\uFF0C\u56DE\u6536\u8FDE\u63A5\u4EE5\u91CD\u8FDE\uFF1A", error instanceof Error ? error.message : String(error));
4328
+ try {
4329
+ socket.close();
4330
+ } catch {
4331
+ }
4332
+ return false;
4333
+ }
4334
+ }
4335
+ // 尽力给服务端回一个错误响应,让它立即结束这单挂起请求(避免公网调用方干等到 30s 超时)。
4336
+ // 隧道已断时 safeSend 会静默跳过——此时服务端会因 WS close 自行清理,无需在此处理。
4337
+ sendErrorReply(requestId) {
4338
+ this.safeSend(JSON.stringify({
4151
4339
  type: "response",
4152
- requestId: payload.requestId,
4153
- status: response.status,
4154
- headers: Object.fromEntries(response.headers.entries()),
4155
- bodyBase64: bodyBuffer.toString("base64")
4340
+ requestId,
4341
+ status: 502,
4342
+ headers: { "content-type": "text/plain; charset=utf-8" },
4343
+ bodyBase64: Buffer.from("agent_local_error").toString("base64")
4156
4344
  }));
4157
4345
  }
4346
+ async handleRequest(payload) {
4347
+ const fetchImpl = this.options.fetchImpl ?? fetch;
4348
+ let response;
4349
+ try {
4350
+ response = await fetchImpl(`${this.options.targetBaseUrl}${payload.path}`, {
4351
+ method: payload.method,
4352
+ headers: payload.headers,
4353
+ body: payload.bodyBase64 ? Buffer.from(payload.bodyBase64, "base64") : void 0
4354
+ });
4355
+ } catch (error) {
4356
+ console.warn(`[relay] \u672C\u5730\u8BF7\u6C42 ${payload.requestId} \u5931\u8D25\uFF1A`, error instanceof Error ? error.message : String(error));
4357
+ this.sendErrorReply(payload.requestId);
4358
+ return;
4359
+ }
4360
+ if (this.serverSupportsStreaming) {
4361
+ await this.streamResponse(payload.requestId, response);
4362
+ return;
4363
+ }
4364
+ try {
4365
+ const bodyBuffer = Buffer.from(await response.arrayBuffer());
4366
+ this.safeSend(JSON.stringify({
4367
+ type: "response",
4368
+ requestId: payload.requestId,
4369
+ status: response.status,
4370
+ headers: Object.fromEntries(response.headers.entries()),
4371
+ bodyBase64: bodyBuffer.toString("base64")
4372
+ }));
4373
+ } catch (error) {
4374
+ console.warn(`[relay] \u8BFB\u53D6\u54CD\u5E94\u4F53 ${payload.requestId} \u5931\u8D25\uFF1A`, error instanceof Error ? error.message : String(error));
4375
+ this.sendErrorReply(payload.requestId);
4376
+ }
4377
+ }
4378
+ // 流式回传:把响应拆成 response-start → response-chunk* → response-end 三段边收边发。
4379
+ // 不区分 content-type——服务器按 content-type 决定透传还是收齐重写(html/js/css)。
4380
+ // 对无限流(SSE)这是关键:响应体永不结束,绝不能 await 读完再发。
4381
+ async streamResponse(requestId, response) {
4382
+ const status = response.status;
4383
+ const headers = Object.fromEntries(response.headers.entries());
4384
+ if (!this.safeSend(JSON.stringify({ type: "response-start", requestId, status, headers }))) return;
4385
+ try {
4386
+ const body = response.body;
4387
+ if (body) {
4388
+ for await (const chunk of body) {
4389
+ const ok = this.safeSend(JSON.stringify({
4390
+ type: "response-chunk",
4391
+ requestId,
4392
+ chunkBase64: Buffer.from(chunk).toString("base64")
4393
+ }));
4394
+ if (!ok) return;
4395
+ }
4396
+ }
4397
+ } finally {
4398
+ this.safeSend(JSON.stringify({ type: "response-end", requestId }));
4399
+ }
4400
+ }
4158
4401
  };
4159
4402
  function derivePublicBaseUrl(serverUrl) {
4160
4403
  return serverUrl.replace(/^ws:\/\//, "http://").replace(/^wss:\/\//, "https://").replace(/\/agent\/?$/, "").replace(/\/+$/, "");
@@ -4208,6 +4451,14 @@ var RelayTunnelRouter = class {
4208
4451
  };
4209
4452
 
4210
4453
  // src/daemon/server.ts
4454
+ function readUpdateStatusBestEffort(configPath) {
4455
+ if (!configPath) return { ok: false };
4456
+ try {
4457
+ return { ok: true, update: loadBridgeConfig(configPath).update };
4458
+ } catch {
4459
+ return { ok: false };
4460
+ }
4461
+ }
4211
4462
  function createDaemonServer(options = {}) {
4212
4463
  const app = Fastify({ logger: true });
4213
4464
  const events = new BridgeEventHub();
@@ -4378,16 +4629,21 @@ function createDaemonServer(options = {}) {
4378
4629
  });
4379
4630
  }
4380
4631
  registerFsBrowseRoutes({ app });
4381
- app.get("/api/status", async () => ({
4382
- ok: true,
4383
- sessions: conversation.getCurrent() ? [conversation.getCurrent()] : [],
4384
- preferredLocalUrl: (() => {
4385
- const lan = listLanIpv4Addresses()[0];
4386
- const port = process.env.BRIDGE_PORT ?? 8787;
4387
- if (lan) return `http://${lan}:${port}`;
4388
- return `http://127.0.0.1:${port}`;
4389
- })()
4390
- }));
4632
+ app.get("/api/status", async () => {
4633
+ const read = readUpdateStatusBestEffort(configPath);
4634
+ const update = read.ok ? read.update ?? null : persistedConfig.update ?? null;
4635
+ return {
4636
+ ok: true,
4637
+ sessions: conversation.getCurrent() ? [conversation.getCurrent()] : [],
4638
+ update,
4639
+ preferredLocalUrl: (() => {
4640
+ const lan = listLanIpv4Addresses()[0];
4641
+ const port = process.env.BRIDGE_PORT ?? 8787;
4642
+ if (lan) return `http://${lan}:${port}`;
4643
+ return `http://127.0.0.1:${port}`;
4644
+ })()
4645
+ };
4646
+ });
4391
4647
  app.get("/api/providers/status", async () => providers.getStatus());
4392
4648
  app.post("/api/bridge-events", async (request, reply) => {
4393
4649
  reply.raw.writeHead(200, {
@@ -4423,6 +4679,91 @@ function createDaemonServer(options = {}) {
4423
4679
  return { app, conversation, sessions: conversation, events, activeUserStore, tunnelProvider };
4424
4680
  }
4425
4681
 
4682
+ // src/daemon/updateChecker.ts
4683
+ var DEFAULT_REGISTRY_URL = "https://registry.npmmirror.com/claude-codex-wechat/latest";
4684
+ var DEFAULT_INTERVAL_MS = 60 * 60 * 1e3;
4685
+ var FETCH_TIMEOUT_MS = 5e3;
4686
+ function parseCore(version) {
4687
+ const core = version.trim().replace(/^v/, "").split(/[-+]/, 1)[0];
4688
+ const parts = core.split(".");
4689
+ if (parts.length !== 3) return null;
4690
+ const nums = parts.map((p) => Number(p));
4691
+ if (nums.some((n) => !Number.isInteger(n) || n < 0)) return null;
4692
+ return [nums[0], nums[1], nums[2]];
4693
+ }
4694
+ function compareSemver(a, b) {
4695
+ const ca = parseCore(a);
4696
+ const cb = parseCore(b);
4697
+ if (!ca || !cb) return null;
4698
+ for (let i = 0; i < 3; i += 1) {
4699
+ if (ca[i] > cb[i]) return 1;
4700
+ if (ca[i] < cb[i]) return -1;
4701
+ }
4702
+ return 0;
4703
+ }
4704
+ function isNewerVersion(latest, current) {
4705
+ if (/[-]/.test(latest.trim().replace(/^v/, ""))) return false;
4706
+ return compareSemver(latest, current) === 1;
4707
+ }
4708
+ function createUpdateChecker(opts) {
4709
+ const registryUrl = opts.registryUrl ?? DEFAULT_REGISTRY_URL;
4710
+ const intervalMs = opts.intervalMs ?? DEFAULT_INTERVAL_MS;
4711
+ const fetchImpl = opts.fetchImpl ?? fetch;
4712
+ const persist = opts.persist ?? persistUpdateStatusToConfigFile;
4713
+ const now = opts.now ?? (() => Date.now());
4714
+ let timer = null;
4715
+ async function checkOnce() {
4716
+ try {
4717
+ const response = await fetchImpl(registryUrl, { signal: AbortSignal.timeout(FETCH_TIMEOUT_MS) });
4718
+ if (!response.ok) return;
4719
+ const data = await response.json();
4720
+ const latest = typeof data?.version === "string" && data.version.trim() ? data.version.trim() : void 0;
4721
+ if (!latest) return;
4722
+ if (compareSemver(latest, opts.currentVersion) === null) return;
4723
+ await Promise.resolve(persist({
4724
+ configPath: opts.configPath,
4725
+ status: {
4726
+ currentVersion: opts.currentVersion,
4727
+ latestVersion: latest,
4728
+ updateAvailable: isNewerVersion(latest, opts.currentVersion),
4729
+ lastCheckedAt: now()
4730
+ }
4731
+ }));
4732
+ } catch {
4733
+ }
4734
+ }
4735
+ return {
4736
+ async start() {
4737
+ await checkOnce();
4738
+ timer = setInterval(() => {
4739
+ void checkOnce();
4740
+ }, intervalMs);
4741
+ timer.unref?.();
4742
+ },
4743
+ stop() {
4744
+ if (timer) {
4745
+ clearInterval(timer);
4746
+ timer = null;
4747
+ }
4748
+ },
4749
+ checkOnce
4750
+ };
4751
+ }
4752
+
4753
+ // src/shared/version.ts
4754
+ import { readFileSync as readFileSync7 } from "node:fs";
4755
+ import { dirname as dirname11, join as join10 } from "node:path";
4756
+ import { fileURLToPath } from "node:url";
4757
+ function readClientVersion() {
4758
+ try {
4759
+ const here2 = dirname11(fileURLToPath(import.meta.url));
4760
+ const pkg = JSON.parse(readFileSync7(join10(here2, "..", "..", "package.json"), "utf8"));
4761
+ return typeof pkg.version === "string" && pkg.version ? pkg.version : "0.0.0";
4762
+ } catch {
4763
+ return "0.0.0";
4764
+ }
4765
+ }
4766
+
4426
4767
  // src/daemon/bootstrap.ts
4427
4768
  async function startDaemon(options) {
4428
4769
  const port = options.port ?? Number(process.env.BRIDGE_PORT ?? 8787);
@@ -4456,6 +4797,9 @@ async function startDaemon(options) {
4456
4797
  if (config.tunnel?.relay?.serverUrl && config.tunnel?.relay?.authToken) {
4457
4798
  await resolvedTunnelProvider?.start().catch(() => void 0);
4458
4799
  }
4800
+ if (options.enableUpdateCheck) {
4801
+ createUpdateChecker({ currentVersion: readClientVersion(), configPath }).start().catch(() => void 0);
4802
+ }
4459
4803
  console.log("claude-codex-wechat listening:");
4460
4804
  console.log(` Local: http://127.0.0.1:${actualPort}`);
4461
4805
  for (const ip of listLanIpv4Addresses()) {
@@ -4478,8 +4822,8 @@ async function resolveProviderCommands(providers) {
4478
4822
  }
4479
4823
 
4480
4824
  // src/daemon/staticFrontend.ts
4481
- import { readFileSync as readFileSync7 } from "node:fs";
4482
- import { join as join10 } from "node:path";
4825
+ import { readFileSync as readFileSync8 } from "node:fs";
4826
+ import { join as join11 } from "node:path";
4483
4827
  import fastifyStatic from "@fastify/static";
4484
4828
  function attachStaticFrontend(webRoot2) {
4485
4829
  return async (app) => {
@@ -4487,7 +4831,7 @@ function attachStaticFrontend(webRoot2) {
4487
4831
  root: webRoot2,
4488
4832
  wildcard: false
4489
4833
  });
4490
- const indexHtml = readFileSync7(join10(webRoot2, "index.html"), "utf8");
4834
+ const indexHtml = readFileSync8(join11(webRoot2, "index.html"), "utf8");
4491
4835
  app.setNotFoundHandler((request, reply) => {
4492
4836
  if (request.url.startsWith("/api")) {
4493
4837
  reply.code(404).send({ error: "not_found" });
@@ -4502,17 +4846,17 @@ function attachStaticFrontend(webRoot2) {
4502
4846
  import { existsSync as existsSync9, statSync, openSync, closeSync, watchFile } from "node:fs";
4503
4847
  import { mkdir as mkdir4, readFile as readFile6, rm as rm2, writeFile as writeFile5 } from "node:fs/promises";
4504
4848
  import { homedir as homedir8 } from "node:os";
4505
- import { dirname as dirname11, join as join11 } from "node:path";
4849
+ import { dirname as dirname12, join as join12 } from "node:path";
4506
4850
  import { createReadStream } from "node:fs";
4507
- import { execFile, spawn as spawn6 } from "node:child_process";
4851
+ import { execFile, spawn as spawn7 } from "node:child_process";
4508
4852
  import { createInterface } from "node:readline";
4509
4853
  import { promisify } from "node:util";
4510
4854
  var execFileAsync = promisify(execFile);
4511
4855
  async function installService(context) {
4512
4856
  if (process.platform === "darwin") {
4513
4857
  const spec = buildLaunchdSpec(context);
4514
- await mkdir4(dirname11(spec.plistPath), { recursive: true });
4515
- await mkdir4(dirname11(spec.stdoutPath), { recursive: true });
4858
+ await mkdir4(dirname12(spec.plistPath), { recursive: true });
4859
+ await mkdir4(dirname12(spec.stdoutPath), { recursive: true });
4516
4860
  await writeFile5(spec.plistPath, renderLaunchdPlist(spec), "utf8");
4517
4861
  await runLaunchctl(["unload", spec.plistPath]).catch(() => void 0);
4518
4862
  await runLaunchctl(["load", spec.plistPath]);
@@ -4521,8 +4865,8 @@ async function installService(context) {
4521
4865
  }
4522
4866
  if (process.platform === "linux") {
4523
4867
  const spec = buildSystemdUserSpec(context);
4524
- await mkdir4(dirname11(spec.unitPath), { recursive: true });
4525
- await mkdir4(dirname11(spec.stdoutPath), { recursive: true });
4868
+ await mkdir4(dirname12(spec.unitPath), { recursive: true });
4869
+ await mkdir4(dirname12(spec.stdoutPath), { recursive: true });
4526
4870
  await writeFile5(spec.unitPath, renderSystemdUnit(spec), "utf8");
4527
4871
  await runSystemctl(["--user", "daemon-reload"]);
4528
4872
  await runSystemctl(["--user", "enable", "--now", spec.unitName]);
@@ -4687,37 +5031,37 @@ async function readServiceStatus(context) {
4687
5031
  throw new Error("service_status_not_supported_on_this_platform");
4688
5032
  }
4689
5033
  function buildLaunchdSpec(context) {
4690
- const stateDir = join11(homedir8(), ".claude-codex-wechat");
5034
+ const stateDir = join12(homedir8(), ".claude-codex-wechat");
4691
5035
  return {
4692
5036
  label: "com.claude-codex-wechat",
4693
- plistPath: join11(homedir8(), "Library", "LaunchAgents", "com.claude-codex-wechat.plist"),
5037
+ plistPath: join12(homedir8(), "Library", "LaunchAgents", "com.claude-codex-wechat.plist"),
4694
5038
  programArgs: [context.nodePath ?? process.execPath, context.cliEntrypointPath, "__daemon"],
4695
5039
  workingDirectory: homedir8(),
4696
- stdoutPath: join11(stateDir, "logs", "service.stdout.log"),
4697
- stderrPath: join11(stateDir, "logs", "service.stderr.log"),
5040
+ stdoutPath: join12(stateDir, "logs", "service.stdout.log"),
5041
+ stderrPath: join12(stateDir, "logs", "service.stderr.log"),
4698
5042
  environment: buildServiceEnvironment(context)
4699
5043
  };
4700
5044
  }
4701
5045
  function buildSystemdUserSpec(context) {
4702
- const stateDir = join11(homedir8(), ".claude-codex-wechat");
5046
+ const stateDir = join12(homedir8(), ".claude-codex-wechat");
4703
5047
  return {
4704
5048
  unitName: "claude-codex-wechat.service",
4705
- unitPath: join11(homedir8(), ".config", "systemd", "user", "claude-codex-wechat.service"),
5049
+ unitPath: join12(homedir8(), ".config", "systemd", "user", "claude-codex-wechat.service"),
4706
5050
  execStart: [context.nodePath ?? process.execPath, context.cliEntrypointPath, "__daemon"],
4707
5051
  workingDirectory: homedir8(),
4708
- stdoutPath: join11(stateDir, "logs", "service.stdout.log"),
4709
- stderrPath: join11(stateDir, "logs", "service.stderr.log"),
5052
+ stdoutPath: join12(stateDir, "logs", "service.stdout.log"),
5053
+ stderrPath: join12(stateDir, "logs", "service.stderr.log"),
4710
5054
  environment: buildServiceEnvironment(context)
4711
5055
  };
4712
5056
  }
4713
5057
  function buildProcessSpec(context) {
4714
- const stateDir = join11(homedir8(), ".claude-codex-wechat");
5058
+ const stateDir = join12(homedir8(), ".claude-codex-wechat");
4715
5059
  return {
4716
- pidPath: join11(stateDir, "service.pid"),
5060
+ pidPath: join12(stateDir, "service.pid"),
4717
5061
  command: [context.nodePath ?? process.execPath, context.cliEntrypointPath, "__daemon"],
4718
5062
  workingDirectory: homedir8(),
4719
- stdoutPath: join11(stateDir, "logs", "service.stdout.log"),
4720
- stderrPath: join11(stateDir, "logs", "service.stderr.log"),
5063
+ stdoutPath: join12(stateDir, "logs", "service.stdout.log"),
5064
+ stderrPath: join12(stateDir, "logs", "service.stderr.log"),
4721
5065
  environment: buildServiceEnvironment(context)
4722
5066
  };
4723
5067
  }
@@ -4838,7 +5182,7 @@ async function readPidFile(pidPath) {
4838
5182
  }
4839
5183
  }
4840
5184
  async function writePidFile(pidPath, pid) {
4841
- await mkdir4(dirname11(pidPath), { recursive: true });
5185
+ await mkdir4(dirname12(pidPath), { recursive: true });
4842
5186
  await writeFile5(pidPath, `${pid}
4843
5187
  `, "utf8");
4844
5188
  }
@@ -4853,12 +5197,12 @@ async function killPid(pid) {
4853
5197
  process.kill(pid, "SIGTERM");
4854
5198
  }
4855
5199
  async function spawnDetachedDaemon(spec) {
4856
- await mkdir4(dirname11(spec.stdoutPath), { recursive: true });
5200
+ await mkdir4(dirname12(spec.stdoutPath), { recursive: true });
4857
5201
  const outFd = openSync(spec.stdoutPath, "a");
4858
5202
  const errFd = openSync(spec.stderrPath, "a");
4859
5203
  try {
4860
5204
  const [command, ...args] = spec.command;
4861
- const child = spawn6(command, args, {
5205
+ const child = spawn7(command, args, {
4862
5206
  cwd: spec.workingDirectory,
4863
5207
  // Windows 分离进程需继承完整 process.env(SystemRoot/APPDATA/PATHEXT 等),
4864
5208
  // 否则子进程及其拉起的 claude/codex 会异常;spec.environment 仅作 BRIDGE_* 等覆盖层。
@@ -4896,7 +5240,7 @@ ${tail}
4896
5240
  `);
4897
5241
  }
4898
5242
  if (process.platform === "linux" || process.platform === "darwin") {
4899
- const child = spawn6("tail", ["-f", ...existing], { stdio: ["ignore", "pipe", "inherit"] });
5243
+ const child = spawn7("tail", ["-f", ...existing], { stdio: ["ignore", "pipe", "inherit"] });
4900
5244
  child.stdout.on("data", (chunk) => {
4901
5245
  process.stdout.write(String(chunk));
4902
5246
  });
@@ -4939,8 +5283,8 @@ function fileSizeHint(path) {
4939
5283
  }
4940
5284
 
4941
5285
  // src/cli.ts
4942
- var here = dirname12(fileURLToPath(import.meta.url));
4943
- var webRoot = join12(here, "..", "web");
5286
+ var here = dirname13(fileURLToPath2(import.meta.url));
5287
+ var webRoot = join13(here, "..", "web");
4944
5288
  async function main() {
4945
5289
  const command = process.argv[2];
4946
5290
  switch (command) {
@@ -5011,6 +5355,7 @@ async function cmdRestart() {
5011
5355
  async function cmdStatus() {
5012
5356
  const status = await readServiceStatus(createServiceContext());
5013
5357
  printServiceStatus("service status", status);
5358
+ printUpdateHint();
5014
5359
  }
5015
5360
  async function cmdLogs() {
5016
5361
  console.log(await readServiceLogs(createServiceContext()));
@@ -5025,7 +5370,8 @@ async function cmdDaemon() {
5025
5370
  return;
5026
5371
  }
5027
5372
  await startDaemon({
5028
- attachFrontend: attachStaticFrontend(webRoot)
5373
+ attachFrontend: attachStaticFrontend(webRoot),
5374
+ enableUpdateCheck: true
5029
5375
  });
5030
5376
  }
5031
5377
  async function cmdDoctor() {
@@ -5044,6 +5390,23 @@ async function cmdDoctor() {
5044
5390
  report("\u5FAE\u4FE1 token", wechat?.token ? "\u5DF2\u914D\u7F6E" : "\u672A\u914D\u7F6E");
5045
5391
  report("\u5FAE\u4FE1 accountId", wechat?.accountId ? "\u5DF2\u914D\u7F6E" : "\u672A\u914D\u7F6E");
5046
5392
  }
5393
+ printUpdateHint();
5394
+ }
5395
+ function printUpdateHint() {
5396
+ const configPath = process.env.BRIDGE_CONFIG ?? defaultConfigPath();
5397
+ if (!existsSync10(configPath)) return;
5398
+ let update;
5399
+ try {
5400
+ update = loadBridgeConfig(configPath).update;
5401
+ } catch {
5402
+ return;
5403
+ }
5404
+ if (!update?.updateAvailable) return;
5405
+ const current = update.currentVersion ?? readClientVersion();
5406
+ console.log(`
5407
+ \u53D1\u73B0\u65B0\u7248 v${update.latestVersion}\uFF08\u5F53\u524D v${current}\uFF09\u3002\u66F4\u65B0\uFF1A`);
5408
+ console.log(" npm install -g claude-codex-wechat@latest --registry=https://registry.npmmirror.com/");
5409
+ console.log(" claude-codex-wechat restart");
5047
5410
  }
5048
5411
  function cmdPrintConfig() {
5049
5412
  const configPath = process.env.BRIDGE_CONFIG ?? defaultConfigPath();
@@ -5052,7 +5415,7 @@ function cmdPrintConfig() {
5052
5415
  process.exitCode = 1;
5053
5416
  return;
5054
5417
  }
5055
- console.log(readFileSync8(configPath, "utf8"));
5418
+ console.log(readFileSync9(configPath, "utf8"));
5056
5419
  }
5057
5420
  async function cmdUninstall() {
5058
5421
  const status = await uninstallService(createServiceContext());
@@ -5060,7 +5423,7 @@ async function cmdUninstall() {
5060
5423
  }
5061
5424
  function createServiceContext() {
5062
5425
  return {
5063
- cliEntrypointPath: fileURLToPath(import.meta.url),
5426
+ cliEntrypointPath: fileURLToPath2(import.meta.url),
5064
5427
  nodePath: process.execPath,
5065
5428
  configPath: process.env.BRIDGE_CONFIG ?? defaultConfigPath(),
5066
5429
  port: Number(process.env.BRIDGE_PORT ?? 8787)