claude-codex-wechat 0.1.16 → 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
  ```
@@ -2377,6 +2377,9 @@ function terminateChild(child, signal = "SIGTERM") {
2377
2377
  function defaultWorkspaceDir() {
2378
2378
  return join8(tmpdir(), "project");
2379
2379
  }
2380
+ function useShellForCli() {
2381
+ return isWindows;
2382
+ }
2380
2383
  function statePath(filename) {
2381
2384
  return join8(tmpdir(), filename);
2382
2385
  }
@@ -2551,7 +2554,7 @@ function parseJsonLine(line) {
2551
2554
  }
2552
2555
  }
2553
2556
  function defaultClaudeStreamSpawner(call) {
2554
- 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() });
2555
2558
  let stderr = "";
2556
2559
  let buffer = "";
2557
2560
  const queue = [];
@@ -2663,7 +2666,8 @@ var CodexAppServerClient = class {
2663
2666
  const command = input.command ?? "codex";
2664
2667
  this.child = spawn3(command, ["app-server", "--listen", "stdio://"], {
2665
2668
  cwd: input.cwd,
2666
- stdio: ["pipe", "pipe", "pipe"]
2669
+ stdio: ["pipe", "pipe", "pipe"],
2670
+ shell: useShellForCli()
2667
2671
  });
2668
2672
  this.child.stdout.on("data", (chunk) => this.onStdout(String(chunk)));
2669
2673
  this.child.stderr.on("data", () => {
@@ -3070,7 +3074,7 @@ function createDefaultProviders(input = {}) {
3070
3074
  import { spawn as spawn4 } from "node:child_process";
3071
3075
  async function defaultCommandRunner(command, args) {
3072
3076
  return await new Promise((resolve2) => {
3073
- const child = spawn4(command, args, { stdio: ["ignore", "pipe", "pipe"] });
3077
+ const child = spawn4(command, args, { stdio: ["ignore", "pipe", "pipe"], shell: useShellForCli() });
3074
3078
  let stdout = "";
3075
3079
  let stderr = "";
3076
3080
  child.stdout.on("data", (chunk) => {
@@ -3106,7 +3110,7 @@ ${result.stderr}`) };
3106
3110
  import { spawn as spawn5 } from "node:child_process";
3107
3111
  async function defaultCodexCommandRunner(command, args) {
3108
3112
  return await new Promise((resolve2) => {
3109
- const child = spawn5(command, args, { stdio: ["ignore", "pipe", "pipe"] });
3113
+ const child = spawn5(command, args, { stdio: ["ignore", "pipe", "pipe"], shell: useShellForCli() });
3110
3114
  let stdout = "";
3111
3115
  let stderr = "";
3112
3116
  child.stdout.on("data", (chunk) => {
@@ -4456,7 +4460,7 @@ function attachStaticFrontend(webRoot2) {
4456
4460
  }
4457
4461
 
4458
4462
  // src/daemon/service.ts
4459
- import { existsSync as existsSync9, statSync } from "node:fs";
4463
+ import { existsSync as existsSync9, statSync, openSync, closeSync, watchFile } from "node:fs";
4460
4464
  import { mkdir as mkdir4, readFile as readFile6, rm as rm2, writeFile as writeFile5 } from "node:fs/promises";
4461
4465
  import { homedir as homedir8 } from "node:os";
4462
4466
  import { dirname as dirname11, join as join11 } from "node:path";
@@ -4485,6 +4489,9 @@ async function installService(context) {
4485
4489
  await runSystemctl(["--user", "enable", "--now", spec.unitName]);
4486
4490
  return await readServiceStatus(context);
4487
4491
  }
4492
+ if (process.platform === "win32") {
4493
+ return await startService(context);
4494
+ }
4488
4495
  throw new Error("service_install_not_supported_on_this_platform");
4489
4496
  }
4490
4497
  async function startService(context) {
@@ -4501,6 +4508,15 @@ async function startService(context) {
4501
4508
  await runSystemctl(["--user", "start", spec.unitName]);
4502
4509
  return await readServiceStatus(context);
4503
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
+ }
4504
4520
  throw new Error("service_start_not_supported_on_this_platform");
4505
4521
  }
4506
4522
  async function stopService(context) {
@@ -4516,6 +4532,15 @@ async function stopService(context) {
4516
4532
  await runSystemctl(["--user", "stop", spec.unitName]).catch(() => void 0);
4517
4533
  return await readServiceStatus(context);
4518
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
+ }
4519
4544
  throw new Error("service_stop_not_supported_on_this_platform");
4520
4545
  }
4521
4546
  async function uninstallService(context) {
@@ -4536,6 +4561,9 @@ async function uninstallService(context) {
4536
4561
  }
4537
4562
  return await readServiceStatus(context);
4538
4563
  }
4564
+ if (process.platform === "win32") {
4565
+ return await stopService(context);
4566
+ }
4539
4567
  throw new Error("service_uninstall_not_supported_on_this_platform");
4540
4568
  }
4541
4569
  async function restartService(context) {
@@ -4551,6 +4579,10 @@ async function restartService(context) {
4551
4579
  await runSystemctl(["--user", "restart", spec.unitName]);
4552
4580
  return await readServiceStatus(context);
4553
4581
  }
4582
+ if (process.platform === "win32") {
4583
+ await stopService(context);
4584
+ return await startService(context);
4585
+ }
4554
4586
  throw new Error("service_restart_not_supported_on_this_platform");
4555
4587
  }
4556
4588
  async function readServiceLogs(context, lines = 120) {
@@ -4598,6 +4630,21 @@ async function readServiceStatus(context) {
4598
4630
  stderrPath: spec.stderrPath
4599
4631
  };
4600
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
+ }
4601
4648
  throw new Error("service_status_not_supported_on_this_platform");
4602
4649
  }
4603
4650
  function buildLaunchdSpec(context) {
@@ -4624,6 +4671,17 @@ function buildSystemdUserSpec(context) {
4624
4671
  environment: buildServiceEnvironment(context)
4625
4672
  };
4626
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
+ }
4627
4685
  function buildServiceEnvironment(context) {
4628
4686
  const env = {
4629
4687
  PATH: process.env.PATH ?? "",
@@ -4721,6 +4779,66 @@ async function runLaunchctl(args) {
4721
4779
  async function runSystemctl(args) {
4722
4780
  await execFileAsync("systemctl", args);
4723
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
+ }
4724
4842
  async function ensureFileExists(path, errorCode) {
4725
4843
  if (!existsSync9(path)) throw new Error(errorCode);
4726
4844
  await readFile6(path, "utf8");
@@ -4731,29 +4849,46 @@ async function tailFile(path, lines) {
4731
4849
  return content.split(/\r?\n/).slice(-lines).join("\n").trim();
4732
4850
  }
4733
4851
  async function tailFiles(paths) {
4734
- const readers = paths.filter((path) => existsSync9(path)).map((path) => {
4735
- const rl = createInterface({
4736
- input: createReadStream(path, { encoding: "utf8", start: Math.max(0, fileSizeHint(path) - 8192) }),
4737
- crlfDelay: Infinity
4738
- });
4739
- rl.on("line", (line) => {
4740
- 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}
4741
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);
4742
4867
  });
4743
- return rl;
4744
- });
4745
- if (process.platform !== "linux" && process.platform !== "darwin") {
4746
4868
  return;
4747
4869
  }
4748
- const child = spawn6("tail", ["-f", ...paths.filter((path) => existsSync9(path))], { stdio: ["ignore", "pipe", "inherit"] });
4749
- child.stdout.on("data", (chunk) => {
4750
- process.stdout.write(String(chunk));
4751
- });
4752
- await new Promise((resolve2, reject) => {
4753
- child.on("exit", () => resolve2());
4754
- child.on("error", reject);
4755
- }).finally(() => {
4756
- 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(() => {
4757
4892
  });
4758
4893
  }
4759
4894
  function fileSizeHint(path) {
@@ -4882,6 +5017,7 @@ function cmdInit() {
4882
5017
  `, "utf8");
4883
5018
  console.log(`\u5DF2\u521B\u5EFA\u914D\u7F6E: ${configPath}`);
4884
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");
4885
5021
  }
4886
5022
  async function cmdDoctor() {
4887
5023
  const configPath = process.env.BRIDGE_CONFIG ?? defaultConfigPath();
@@ -4956,6 +5092,11 @@ function printUsage() {
4956
5092
 
4957
5093
  \u73AF\u5883\u53D8\u91CF:
4958
5094
  BRIDGE_PORT \u76D1\u542C\u7AEF\u53E3\uFF08\u9ED8\u8BA4 8787\uFF09
4959
- 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`);
4960
5101
  }
4961
5102
  await main();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-codex-wechat",
3
- "version": "0.1.16",
3
+ "version": "0.1.17",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "bin": {