claude-codex-wechat 0.1.15 → 0.1.17

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/README.md CHANGED
@@ -47,7 +47,7 @@
47
47
  ### 作为 npm 包安装
48
48
 
49
49
  ```bash
50
- npm install -g claude-codex-wechat
50
+ npm install -g claude-codex-wechat --registry=https://registry.npmmirror.com/
51
51
  # 或
52
52
  pnpm add -g claude-codex-wechat
53
53
  ```
@@ -106,16 +106,13 @@ function normalizeBridgeDefaultsConfig(raw) {
106
106
  }
107
107
  function normalizeTunnelConfig(raw) {
108
108
  const record = raw && typeof raw === "object" ? raw : {};
109
- const enabled = record.enabled === true;
110
109
  const relayRecord = record.relay && typeof record.relay === "object" ? record.relay : void 0;
111
- const relay = relayRecord ? {
112
- ...typeof relayRecord.serverUrl === "string" && relayRecord.serverUrl.trim() ? { serverUrl: relayRecord.serverUrl } : {},
113
- ...typeof relayRecord.authToken === "string" && relayRecord.authToken.trim() ? { authToken: relayRecord.authToken } : {}
114
- } : void 0;
115
- if (!enabled && !relay) return void 0;
110
+ const relay = {
111
+ serverUrl: typeof relayRecord?.serverUrl === "string" && relayRecord.serverUrl.trim() ? relayRecord.serverUrl : "wss://wechat.style520.com/agent",
112
+ ...typeof relayRecord?.authToken === "string" && relayRecord.authToken.trim() ? { authToken: relayRecord.authToken } : {}
113
+ };
116
114
  return {
117
- enabled,
118
- ...relay ? { relay } : {}
115
+ relay
119
116
  };
120
117
  }
121
118
  function normalizeProviderCommand(raw, envFallback) {
@@ -179,7 +176,6 @@ async function persistBridgeDefaultsToConfigFile(input) {
179
176
  },
180
177
  ...input.tunnel ? {
181
178
  tunnel: {
182
- enabled: input.tunnel.enabled === true,
183
179
  ...input.tunnel.relay ? {
184
180
  relay: {
185
181
  ...input.tunnel.relay.serverUrl ? { serverUrl: input.tunnel.relay.serverUrl } : {},
@@ -1472,7 +1468,6 @@ function registerTunnelRoutes(input) {
1472
1468
  const getStatus = async () => await input.tunnelManager.getStatus();
1473
1469
  input.app.get("/api/tunnel/status", getStatus);
1474
1470
  const start = async () => {
1475
- input.defaults.tunnel.enabled = true;
1476
1471
  await persistBridgeDefaultsToConfigFile({
1477
1472
  configPath: input.configPath,
1478
1473
  tunnel: input.defaults.tunnel
@@ -1481,24 +1476,9 @@ function registerTunnelRoutes(input) {
1481
1476
  };
1482
1477
  input.app.post("/api/tunnel/start", start);
1483
1478
  const stop = async () => {
1484
- input.defaults.tunnel.enabled = false;
1485
- await persistBridgeDefaultsToConfigFile({
1486
- configPath: input.configPath,
1487
- tunnel: input.defaults.tunnel
1488
- });
1489
1479
  return await input.tunnelManager.stop();
1490
1480
  };
1491
1481
  input.app.post("/api/tunnel/stop", stop);
1492
- const setEnabled = async (request) => {
1493
- const enabled = request.body?.enabled === true;
1494
- input.defaults.tunnel.enabled = enabled;
1495
- await persistBridgeDefaultsToConfigFile({
1496
- configPath: input.configPath,
1497
- tunnel: input.defaults.tunnel
1498
- });
1499
- return await input.tunnelManager.setEnabled(enabled);
1500
- };
1501
- input.app.post("/api/tunnel/settings", setEnabled);
1502
1482
  }
1503
1483
 
1504
1484
  // src/admin/settingsRoutes.ts
@@ -1547,10 +1527,9 @@ function normalizeSettings(input, defaultWorkspace) {
1547
1527
  defaultProvider: input.defaultProvider === "codex" ? "codex" : "claude-code",
1548
1528
  defaultWorkspace: typeof input.defaultWorkspace === "string" && input.defaultWorkspace.trim() ? input.defaultWorkspace : defaultWorkspace,
1549
1529
  tunnel: {
1550
- enabled: tunnel.enabled === true,
1551
- ...typeof relay.serverUrl === "string" && relay.serverUrl.trim() || typeof relay.authToken === "string" && relay.authToken.trim() ? {
1530
+ ...typeof relay.serverUrl === "string" && relay.serverUrl.trim() || typeof relay.authToken === "string" && relay.authToken.trim() || !("relay" in tunnel) ? {
1552
1531
  relay: {
1553
- ...typeof relay.serverUrl === "string" && relay.serverUrl.trim() ? { serverUrl: relay.serverUrl } : {},
1532
+ serverUrl: typeof relay.serverUrl === "string" && relay.serverUrl.trim() ? relay.serverUrl : "wss://wechat.style520.com/agent",
1554
1533
  ...typeof relay.authToken === "string" && relay.authToken.trim() ? { authToken: relay.authToken } : {}
1555
1534
  }
1556
1535
  } : {}
@@ -2398,6 +2377,9 @@ function terminateChild(child, signal = "SIGTERM") {
2398
2377
  function defaultWorkspaceDir() {
2399
2378
  return join8(tmpdir(), "project");
2400
2379
  }
2380
+ function useShellForCli() {
2381
+ return isWindows;
2382
+ }
2401
2383
  function statePath(filename) {
2402
2384
  return join8(tmpdir(), filename);
2403
2385
  }
@@ -2572,7 +2554,7 @@ function parseJsonLine(line) {
2572
2554
  }
2573
2555
  }
2574
2556
  function defaultClaudeStreamSpawner(call) {
2575
- const child = spawn2(call.command, call.args, { cwd: expandTilde(call.cwd) ?? call.cwd, stdio: ["pipe", "pipe", "pipe"] });
2557
+ const child = spawn2(call.command, call.args, { cwd: expandTilde(call.cwd) ?? call.cwd, stdio: ["pipe", "pipe", "pipe"], shell: useShellForCli() });
2576
2558
  let stderr = "";
2577
2559
  let buffer = "";
2578
2560
  const queue = [];
@@ -2684,7 +2666,8 @@ var CodexAppServerClient = class {
2684
2666
  const command = input.command ?? "codex";
2685
2667
  this.child = spawn3(command, ["app-server", "--listen", "stdio://"], {
2686
2668
  cwd: input.cwd,
2687
- stdio: ["pipe", "pipe", "pipe"]
2669
+ stdio: ["pipe", "pipe", "pipe"],
2670
+ shell: useShellForCli()
2688
2671
  });
2689
2672
  this.child.stdout.on("data", (chunk) => this.onStdout(String(chunk)));
2690
2673
  this.child.stderr.on("data", () => {
@@ -3091,7 +3074,7 @@ function createDefaultProviders(input = {}) {
3091
3074
  import { spawn as spawn4 } from "node:child_process";
3092
3075
  async function defaultCommandRunner(command, args) {
3093
3076
  return await new Promise((resolve2) => {
3094
- const child = spawn4(command, args, { stdio: ["ignore", "pipe", "pipe"] });
3077
+ const child = spawn4(command, args, { stdio: ["ignore", "pipe", "pipe"], shell: useShellForCli() });
3095
3078
  let stdout = "";
3096
3079
  let stderr = "";
3097
3080
  child.stdout.on("data", (chunk) => {
@@ -3127,7 +3110,7 @@ ${result.stderr}`) };
3127
3110
  import { spawn as spawn5 } from "node:child_process";
3128
3111
  async function defaultCodexCommandRunner(command, args) {
3129
3112
  return await new Promise((resolve2) => {
3130
- const child = spawn5(command, args, { stdio: ["ignore", "pipe", "pipe"] });
3113
+ const child = spawn5(command, args, { stdio: ["ignore", "pipe", "pipe"], shell: useShellForCli() });
3131
3114
  let stdout = "";
3132
3115
  let stderr = "";
3133
3116
  child.stdout.on("data", (chunk) => {
@@ -3241,9 +3224,9 @@ var BRIDGE_COMMAND_HELP_GROUPS = [
3241
3224
  entries: [
3242
3225
  { command: "/help", desc: "\u663E\u793A\u672C\u5E2E\u52A9" },
3243
3226
  { command: "/status", desc: "\u67E5\u770B\u5F53\u524D\u4F1A\u8BDD\uFF08provider\u3001\u5DE5\u4F5C\u76EE\u5F55\u3001\u72B6\u6001\uFF09" },
3244
- { command: "/new", desc: "\u65B0\u5EFA\u9ED8\u8BA4 provider \u4F1A\u8BDD" },
3227
+ { command: "/new", desc: "\u6309\u5F53\u524D provider \u65B0\u5EFA\u4F1A\u8BDD" },
3245
3228
  { command: "/new claude|codex", desc: "\u65B0\u5EFA\u6307\u5B9A provider \u4F1A\u8BDD" },
3246
- { command: "/new <\u76EE\u5F55>", desc: "\u5728\u6307\u5B9A\u76EE\u5F55\u65B0\u5EFA\u9ED8\u8BA4 provider \u4F1A\u8BDD" },
3229
+ { command: "/new <\u76EE\u5F55>", desc: "\u5728\u6307\u5B9A\u76EE\u5F55\u6309\u5F53\u524D provider \u65B0\u5EFA\u4F1A\u8BDD" },
3247
3230
  { command: "/new claude:<\u76EE\u5F55>", desc: "\u65B0\u5EFA\u6307\u5B9A provider + \u6307\u5B9A\u76EE\u5F55\u4F1A\u8BDD" },
3248
3231
  { command: "/stop", desc: "\u4E2D\u65AD\u5F53\u524D\u6B63\u5728\u751F\u6210\u7684\u56DE\u590D\uFF08\u4F1A\u8BDD\u4FDD\u7559\uFF09" }
3249
3232
  ]
@@ -3673,7 +3656,7 @@ var MessageRouter = class _MessageRouter {
3673
3656
  }
3674
3657
  if (command.kind === "new_session") {
3675
3658
  await this.preemptActiveGeneration(chatId);
3676
- const providerId = command.providerId ?? this.options.defaults?.defaultProvider ?? "claude-code";
3659
+ const providerId = command.providerId ?? this.conversation.getCurrent()?.providerId ?? this.options.defaults?.defaultProvider ?? "claude-code";
3677
3660
  const cwd = command.cwd ?? this.conversation.getCurrent()?.cwd ?? this.options.defaults?.defaultWorkspace ?? defaultWorkspaceDir();
3678
3661
  const session = this.conversation.create({
3679
3662
  chatId,
@@ -4008,7 +3991,6 @@ var RelayTunnelProvider = class {
4008
3991
  socket = null;
4009
3992
  status = {
4010
3993
  installed: true,
4011
- enabled: false,
4012
3994
  running: false,
4013
3995
  status: "stopped"
4014
3996
  };
@@ -4035,7 +4017,6 @@ var RelayTunnelProvider = class {
4035
4017
  async start() {
4036
4018
  this.status = {
4037
4019
  ...this.status,
4038
- enabled: true,
4039
4020
  status: "starting"
4040
4021
  };
4041
4022
  const socket = this.options.createSocket(this.options.serverUrl);
@@ -4074,10 +4055,9 @@ var RelayTunnelProvider = class {
4074
4055
  return;
4075
4056
  }
4076
4057
  if (payload?.type === "registered") {
4077
- const publicUrl = typeof payload?.token === "string" ? `${derivePublicBaseUrl(this.options.serverUrl)}/${payload.token}` : void 0;
4058
+ const publicUrl = typeof payload?.publicUrl === "string" && payload.publicUrl ? payload.publicUrl : typeof payload?.token === "string" ? `${derivePublicBaseUrl(this.options.serverUrl)}/${payload.token}` : void 0;
4078
4059
  this.status = {
4079
4060
  installed: true,
4080
- enabled: true,
4081
4061
  running: true,
4082
4062
  status: "running",
4083
4063
  ...publicUrl ? { publicUrl } : {}
@@ -4115,15 +4095,11 @@ var RelayTunnelProvider = class {
4115
4095
  this.socket = null;
4116
4096
  this.status = {
4117
4097
  installed: true,
4118
- enabled: false,
4119
4098
  running: false,
4120
4099
  status: "stopped"
4121
4100
  };
4122
4101
  return { ...this.status };
4123
4102
  }
4124
- async setEnabled(enabled) {
4125
- return enabled ? await this.start() : await this.stop();
4126
- }
4127
4103
  async handleRequest(payload) {
4128
4104
  const fetchImpl = this.options.fetchImpl ?? fetch;
4129
4105
  const response = await fetchImpl(`${this.options.targetBaseUrl}${payload.path}`, {
@@ -4168,11 +4144,6 @@ var RelayTunnelRouter = class {
4168
4144
  if (!provider) return this.notConfiguredStatus();
4169
4145
  return await provider.stop();
4170
4146
  }
4171
- async setEnabled(enabled) {
4172
- const provider = this.getOrCreateRelayProvider();
4173
- if (!provider) return this.notConfiguredStatus();
4174
- return await provider.setEnabled(enabled);
4175
- }
4176
4147
  getOrCreateRelayProvider() {
4177
4148
  const relay = this.options.defaults.tunnel.relay;
4178
4149
  if (!relay?.serverUrl || !relay.authToken) return null;
@@ -4190,7 +4161,6 @@ var RelayTunnelRouter = class {
4190
4161
  notConfiguredStatus() {
4191
4162
  return {
4192
4163
  installed: false,
4193
- enabled: false,
4194
4164
  running: false,
4195
4165
  status: "error",
4196
4166
  error: "relay_not_configured"
@@ -4208,8 +4178,10 @@ function createDaemonServer(options = {}) {
4208
4178
  defaultProvider: options.bridgeDefaults?.defaultProvider ?? persistedConfig.bridge?.defaultProvider ?? "claude-code",
4209
4179
  defaultWorkspace: options.bridgeDefaults?.defaultWorkspace ?? persistedConfig.bridge?.defaultWorkspace ?? process.cwd(),
4210
4180
  tunnel: options.bridgeDefaults?.tunnel ?? {
4211
- enabled: persistedConfig.tunnel?.enabled === true,
4212
- ...persistedConfig.tunnel?.relay ? { relay: persistedConfig.tunnel.relay } : {}
4181
+ relay: {
4182
+ serverUrl: persistedConfig.tunnel?.relay?.serverUrl ?? "wss://wechat.style520.com/agent",
4183
+ ...persistedConfig.tunnel?.relay?.authToken ? { authToken: persistedConfig.tunnel.relay.authToken } : {}
4184
+ }
4213
4185
  }
4214
4186
  };
4215
4187
  const existingRelayAuthToken = bridgeDefaults.tunnel.relay?.authToken?.trim();
@@ -4427,7 +4399,7 @@ async function startDaemon(options) {
4427
4399
  defaultProvider: config.bridge?.defaultProvider ?? "claude-code",
4428
4400
  defaultWorkspace: config.bridge?.defaultWorkspace ?? process.cwd(),
4429
4401
  tunnel: {
4430
- enabled: config.tunnel?.enabled === true,
4402
+ enabled: true,
4431
4403
  ...config.tunnel?.relay ? { relay: config.tunnel.relay } : {}
4432
4404
  }
4433
4405
  };
@@ -4442,7 +4414,7 @@ async function startDaemon(options) {
4442
4414
  await app.listen({ host, port });
4443
4415
  const address = app.server.address();
4444
4416
  const actualPort = typeof address === "object" && address ? address.port : port;
4445
- if (config.tunnel?.enabled === true) {
4417
+ if (config.tunnel?.relay?.serverUrl && config.tunnel?.relay?.authToken) {
4446
4418
  await resolvedTunnelProvider?.start().catch(() => void 0);
4447
4419
  }
4448
4420
  console.log("claude-codex-wechat listening:");
@@ -4488,7 +4460,7 @@ function attachStaticFrontend(webRoot2) {
4488
4460
  }
4489
4461
 
4490
4462
  // src/daemon/service.ts
4491
- import { existsSync as existsSync9, statSync } from "node:fs";
4463
+ import { existsSync as existsSync9, statSync, openSync, closeSync, watchFile } from "node:fs";
4492
4464
  import { mkdir as mkdir4, readFile as readFile6, rm as rm2, writeFile as writeFile5 } from "node:fs/promises";
4493
4465
  import { homedir as homedir8 } from "node:os";
4494
4466
  import { dirname as dirname11, join as join11 } from "node:path";
@@ -4517,6 +4489,9 @@ async function installService(context) {
4517
4489
  await runSystemctl(["--user", "enable", "--now", spec.unitName]);
4518
4490
  return await readServiceStatus(context);
4519
4491
  }
4492
+ if (process.platform === "win32") {
4493
+ return await startService(context);
4494
+ }
4520
4495
  throw new Error("service_install_not_supported_on_this_platform");
4521
4496
  }
4522
4497
  async function startService(context) {
@@ -4533,6 +4508,15 @@ async function startService(context) {
4533
4508
  await runSystemctl(["--user", "start", spec.unitName]);
4534
4509
  return await readServiceStatus(context);
4535
4510
  }
4511
+ if (process.platform === "win32") {
4512
+ const spec = buildProcessSpec(context);
4513
+ const existingPid = await readPidFile(spec.pidPath);
4514
+ if (existingPid !== null && isProcessAlive(existingPid)) {
4515
+ return await readServiceStatus(context);
4516
+ }
4517
+ await spawnDetachedDaemon(spec);
4518
+ return await readServiceStatus(context);
4519
+ }
4536
4520
  throw new Error("service_start_not_supported_on_this_platform");
4537
4521
  }
4538
4522
  async function stopService(context) {
@@ -4548,6 +4532,15 @@ async function stopService(context) {
4548
4532
  await runSystemctl(["--user", "stop", spec.unitName]).catch(() => void 0);
4549
4533
  return await readServiceStatus(context);
4550
4534
  }
4535
+ if (process.platform === "win32") {
4536
+ const spec = buildProcessSpec(context);
4537
+ const pid = await readPidFile(spec.pidPath);
4538
+ if (pid !== null && isProcessAlive(pid)) {
4539
+ await killPid(pid).catch(() => void 0);
4540
+ }
4541
+ await removePidFile(spec.pidPath);
4542
+ return await readServiceStatus(context);
4543
+ }
4551
4544
  throw new Error("service_stop_not_supported_on_this_platform");
4552
4545
  }
4553
4546
  async function uninstallService(context) {
@@ -4568,6 +4561,9 @@ async function uninstallService(context) {
4568
4561
  }
4569
4562
  return await readServiceStatus(context);
4570
4563
  }
4564
+ if (process.platform === "win32") {
4565
+ return await stopService(context);
4566
+ }
4571
4567
  throw new Error("service_uninstall_not_supported_on_this_platform");
4572
4568
  }
4573
4569
  async function restartService(context) {
@@ -4583,6 +4579,10 @@ async function restartService(context) {
4583
4579
  await runSystemctl(["--user", "restart", spec.unitName]);
4584
4580
  return await readServiceStatus(context);
4585
4581
  }
4582
+ if (process.platform === "win32") {
4583
+ await stopService(context);
4584
+ return await startService(context);
4585
+ }
4586
4586
  throw new Error("service_restart_not_supported_on_this_platform");
4587
4587
  }
4588
4588
  async function readServiceLogs(context, lines = 120) {
@@ -4630,6 +4630,21 @@ async function readServiceStatus(context) {
4630
4630
  stderrPath: spec.stderrPath
4631
4631
  };
4632
4632
  }
4633
+ if (process.platform === "win32") {
4634
+ const spec = buildProcessSpec(context);
4635
+ const pid = await readPidFile(spec.pidPath);
4636
+ const installed = pid !== null;
4637
+ const running = pid !== null && isProcessAlive(pid);
4638
+ return {
4639
+ manager: "process",
4640
+ installed,
4641
+ running,
4642
+ serviceFilePath: spec.pidPath,
4643
+ label: "claude-codex-wechat",
4644
+ stdoutPath: spec.stdoutPath,
4645
+ stderrPath: spec.stderrPath
4646
+ };
4647
+ }
4633
4648
  throw new Error("service_status_not_supported_on_this_platform");
4634
4649
  }
4635
4650
  function buildLaunchdSpec(context) {
@@ -4656,6 +4671,17 @@ function buildSystemdUserSpec(context) {
4656
4671
  environment: buildServiceEnvironment(context)
4657
4672
  };
4658
4673
  }
4674
+ function buildProcessSpec(context) {
4675
+ const stateDir = join11(homedir8(), ".claude-codex-wechat");
4676
+ return {
4677
+ pidPath: join11(stateDir, "service.pid"),
4678
+ command: [context.nodePath ?? process.execPath, context.cliEntrypointPath, "__daemon"],
4679
+ workingDirectory: homedir8(),
4680
+ stdoutPath: join11(stateDir, "logs", "service.stdout.log"),
4681
+ stderrPath: join11(stateDir, "logs", "service.stderr.log"),
4682
+ environment: buildServiceEnvironment(context)
4683
+ };
4684
+ }
4659
4685
  function buildServiceEnvironment(context) {
4660
4686
  const env = {
4661
4687
  PATH: process.env.PATH ?? "",
@@ -4753,6 +4779,66 @@ async function runLaunchctl(args) {
4753
4779
  async function runSystemctl(args) {
4754
4780
  await execFileAsync("systemctl", args);
4755
4781
  }
4782
+ function isProcessAlive(pid) {
4783
+ if (!Number.isInteger(pid) || pid <= 0) return false;
4784
+ try {
4785
+ process.kill(pid, 0);
4786
+ return true;
4787
+ } catch (error) {
4788
+ return error.code === "EPERM";
4789
+ }
4790
+ }
4791
+ async function readPidFile(pidPath) {
4792
+ if (!existsSync9(pidPath)) return null;
4793
+ try {
4794
+ const raw = (await readFile6(pidPath, "utf8")).trim();
4795
+ const pid = Number.parseInt(raw, 10);
4796
+ return Number.isInteger(pid) && pid > 0 ? pid : null;
4797
+ } catch {
4798
+ return null;
4799
+ }
4800
+ }
4801
+ async function writePidFile(pidPath, pid) {
4802
+ await mkdir4(dirname11(pidPath), { recursive: true });
4803
+ await writeFile5(pidPath, `${pid}
4804
+ `, "utf8");
4805
+ }
4806
+ async function removePidFile(pidPath) {
4807
+ await rm2(pidPath, { force: true });
4808
+ }
4809
+ async function killPid(pid) {
4810
+ if (process.platform === "win32") {
4811
+ await execFileAsync("taskkill", ["/PID", String(pid), "/T", "/F"]);
4812
+ return;
4813
+ }
4814
+ process.kill(pid, "SIGTERM");
4815
+ }
4816
+ async function spawnDetachedDaemon(spec) {
4817
+ await mkdir4(dirname11(spec.stdoutPath), { recursive: true });
4818
+ const outFd = openSync(spec.stdoutPath, "a");
4819
+ const errFd = openSync(spec.stderrPath, "a");
4820
+ try {
4821
+ const [command, ...args] = spec.command;
4822
+ const child = spawn6(command, args, {
4823
+ cwd: spec.workingDirectory,
4824
+ // Windows 分离进程需继承完整 process.env(SystemRoot/APPDATA/PATHEXT 等),
4825
+ // 否则子进程及其拉起的 claude/codex 会异常;spec.environment 仅作 BRIDGE_* 等覆盖层。
4826
+ env: { ...process.env, ...spec.environment },
4827
+ detached: true,
4828
+ windowsHide: true,
4829
+ stdio: ["ignore", outFd, errFd]
4830
+ });
4831
+ if (typeof child.pid !== "number") {
4832
+ throw new Error("service_spawn_failed");
4833
+ }
4834
+ await writePidFile(spec.pidPath, child.pid);
4835
+ child.unref();
4836
+ return child.pid;
4837
+ } finally {
4838
+ closeSync(outFd);
4839
+ closeSync(errFd);
4840
+ }
4841
+ }
4756
4842
  async function ensureFileExists(path, errorCode) {
4757
4843
  if (!existsSync9(path)) throw new Error(errorCode);
4758
4844
  await readFile6(path, "utf8");
@@ -4763,29 +4849,46 @@ async function tailFile(path, lines) {
4763
4849
  return content.split(/\r?\n/).slice(-lines).join("\n").trim();
4764
4850
  }
4765
4851
  async function tailFiles(paths) {
4766
- const readers = paths.filter((path) => existsSync9(path)).map((path) => {
4767
- const rl = createInterface({
4768
- input: createReadStream(path, { encoding: "utf8", start: Math.max(0, fileSizeHint(path) - 8192) }),
4769
- crlfDelay: Infinity
4770
- });
4771
- rl.on("line", (line) => {
4772
- process.stdout.write(`[${path}] ${line}
4852
+ const existing = paths.filter((path) => existsSync9(path));
4853
+ for (const path of existing) {
4854
+ const tail = await tailFile(path, 200);
4855
+ if (tail) process.stdout.write(`[${path}]
4856
+ ${tail}
4773
4857
  `);
4858
+ }
4859
+ if (process.platform === "linux" || process.platform === "darwin") {
4860
+ const child = spawn6("tail", ["-f", ...existing], { stdio: ["ignore", "pipe", "inherit"] });
4861
+ child.stdout.on("data", (chunk) => {
4862
+ process.stdout.write(String(chunk));
4863
+ });
4864
+ await new Promise((resolve2, reject) => {
4865
+ child.on("exit", () => resolve2());
4866
+ child.on("error", reject);
4774
4867
  });
4775
- return rl;
4776
- });
4777
- if (process.platform !== "linux" && process.platform !== "darwin") {
4778
4868
  return;
4779
4869
  }
4780
- const child = spawn6("tail", ["-f", ...paths.filter((path) => existsSync9(path))], { stdio: ["ignore", "pipe", "inherit"] });
4781
- child.stdout.on("data", (chunk) => {
4782
- process.stdout.write(String(chunk));
4783
- });
4784
- await new Promise((resolve2, reject) => {
4785
- child.on("exit", () => resolve2());
4786
- child.on("error", reject);
4787
- }).finally(() => {
4788
- for (const reader of readers) reader.close();
4870
+ const offsets = new Map(existing.map((path) => [path, fileSizeHint(path)]));
4871
+ for (const path of existing) {
4872
+ watchFile(path, { interval: 1e3 }, (curr) => {
4873
+ const prevSize = offsets.get(path) ?? 0;
4874
+ if (curr.size < prevSize) {
4875
+ offsets.set(path, 0);
4876
+ return;
4877
+ }
4878
+ if (curr.size === prevSize) return;
4879
+ const start = prevSize;
4880
+ offsets.set(path, curr.size);
4881
+ const rl = createInterface({
4882
+ input: createReadStream(path, { encoding: "utf8", start, end: curr.size - 1 }),
4883
+ crlfDelay: Infinity
4884
+ });
4885
+ rl.on("line", (line) => {
4886
+ process.stdout.write(`[${path}] ${line}
4887
+ `);
4888
+ });
4889
+ });
4890
+ }
4891
+ await new Promise(() => {
4789
4892
  });
4790
4893
  }
4791
4894
  function fileSizeHint(path) {
@@ -4914,6 +5017,7 @@ function cmdInit() {
4914
5017
  `, "utf8");
4915
5018
  console.log(`\u5DF2\u521B\u5EFA\u914D\u7F6E: ${configPath}`);
4916
5019
  console.log("\u8BF7\u586B\u5165\u5FAE\u4FE1 token / accountId \u540E\u8FD0\u884C: claude-codex-wechat start");
5020
+ console.log("\u9996\u6B21\u542F\u52A8\u65F6\u5C06\u81EA\u52A8\u751F\u6210 relay \u63A5\u5165\u51ED\u636E\uFF08tunnel.relay.authToken\uFF09\u5E76\u5199\u56DE\u914D\u7F6E\uFF0C\u65E0\u9700\u624B\u52A8\u586B\u5199\u3002");
4917
5021
  }
4918
5022
  async function cmdDoctor() {
4919
5023
  const configPath = process.env.BRIDGE_CONFIG ?? defaultConfigPath();
@@ -4988,6 +5092,11 @@ function printUsage() {
4988
5092
 
4989
5093
  \u73AF\u5883\u53D8\u91CF:
4990
5094
  BRIDGE_PORT \u76D1\u542C\u7AEF\u53E3\uFF08\u9ED8\u8BA4 8787\uFF09
4991
- BRIDGE_CONFIG \u914D\u7F6E\u6587\u4EF6\u8DEF\u5F84\uFF08\u9ED8\u8BA4 ~/.claude-codex-wechat/config.json\uFF09`);
5095
+ BRIDGE_CONFIG \u914D\u7F6E\u6587\u4EF6\u8DEF\u5F84\uFF08\u9ED8\u8BA4 ~/.claude-codex-wechat/config.json\uFF09
5096
+
5097
+ \u5E73\u53F0\u8BF4\u660E:
5098
+ macOS \u7528 launchd\u3001Linux \u7528 systemd --user \u6258\u7BA1\uFF0C\u767B\u5F55\u81EA\u542F\u4E14\u5D29\u6E83\u81EA\u52A8\u91CD\u542F\u3002
5099
+ Windows \u91C7\u7528\u540E\u53F0\u8FDB\u7A0B + PID \u6587\u4EF6\u6258\u7BA1\uFF08\u514D\u7BA1\u7406\u5458\uFF09\uFF0C\u4E0D\u968F\u5F00\u673A\u81EA\u542F\uFF0C
5100
+ \u91CD\u542F\u7535\u8111\u540E\u9700\u91CD\u65B0\u8FD0\u884C start\u3002`);
4992
5101
  }
4993
5102
  await main();