claude-codex-wechat 0.1.6 → 0.1.8

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
@@ -83,11 +83,21 @@ pnpm install
83
83
  可用命令:
84
84
 
85
85
  - `claude-codex-wechat start`
86
- - 前台启动 daemon
87
- - `claude-codex-wechat init`
88
- - 写默认配置到 `~/.claude-codex-wechat/config.json`
86
+ - 后台启动服务(未安装则自动注册系统服务),默认命令
87
+ - `claude-codex-wechat stop`
88
+ - 停止后台服务
89
+ - `claude-codex-wechat restart`
90
+ - 重启后台服务
91
+ - `claude-codex-wechat status`
92
+ - 查看运行状态
93
+ - `claude-codex-wechat logs` / `tail`
94
+ - 打印 / 实时跟随日志
89
95
  - `claude-codex-wechat doctor`
90
96
  - 检查配置、前端产物、`claude`/`codex` 可执行文件
97
+ - `claude-codex-wechat init`
98
+ - 写默认配置到 `~/.claude-codex-wechat/config.json`
99
+ - `claude-codex-wechat uninstall`
100
+ - 卸载后台服务
91
101
  - `claude-codex-wechat print-config`
92
102
  - 打印当前配置文件
93
103
  - `claude-codex-wechat help`
@@ -178,35 +188,24 @@ pnpm build
178
188
 
179
189
  ## 生产运行
180
190
 
181
- 生产态通常按下面方式运行:
191
+ 安装后直接后台启动即可,`start` 会把当前 CLI 注册成操作系统服务并拉起:
182
192
 
183
193
  ```bash
184
- claude-codex-wechat start
194
+ claude-codex-wechat init # 首次:写配置,填好 token / accountId
195
+ claude-codex-wechat start # 后台启动(自动安装服务)
196
+ claude-codex-wechat status # 查看状态
185
197
  ```
186
198
 
187
- 注意:
188
-
189
- - 这是前台进程
190
- - npm 本身不负责守护
191
- - 长期常驻建议交给进程管理器
192
- - 如果默认端口已被本程序的后台服务占用,`start` 会先停掉后台服务,再以前台模式启动
193
- - 如果端口被别的进程占用,`start` 会直接报出占用 PID 和命令,不会强杀陌生进程
194
-
195
- ### 安装后后台运行
196
-
197
- 参考 `happier` 的方式,这个仓库现在提供 `service` 命令,把当前 CLI 注册成操作系统服务,而不是在 Node 进程里自己 daemonize。
198
-
199
- 常用命令:
199
+ 常用管理命令:
200
200
 
201
201
  ```bash
202
- claude-codex-wechat service install
203
- claude-codex-wechat service start
204
- claude-codex-wechat service restart
205
- claude-codex-wechat service status
206
- claude-codex-wechat service logs
207
- claude-codex-wechat service tail
208
- claude-codex-wechat service stop
209
- claude-codex-wechat service uninstall
202
+ claude-codex-wechat start # 后台启动 / 启动已安装服务
203
+ claude-codex-wechat restart # 重启
204
+ claude-codex-wechat status # 状态
205
+ claude-codex-wechat logs # 最近日志
206
+ claude-codex-wechat tail # 实时日志
207
+ claude-codex-wechat stop # 停止
208
+ claude-codex-wechat uninstall # 卸载服务
210
209
  ```
211
210
 
212
211
  当前支持:
@@ -214,73 +213,7 @@ claude-codex-wechat service uninstall
214
213
  - macOS:`launchd`(`~/Library/LaunchAgents/`)
215
214
  - Linux:`systemd --user`(`~/.config/systemd/user/`)
216
215
 
217
- 推荐安装后直接这样:
218
-
219
- ```bash
220
- claude-codex-wechat init
221
- claude-codex-wechat service install
222
- claude-codex-wechat service status
223
- ```
224
-
225
- ### pm2
226
-
227
- ```bash
228
- npm install -g pm2
229
- pm2 start claude-codex-wechat --name ccwx -- start
230
- pm2 logs ccwx
231
- pm2 restart ccwx
232
- pm2 startup && pm2 save
233
- ```
234
-
235
- ### systemd
236
-
237
- 示例:
238
-
239
- ```ini
240
- [Unit]
241
- Description=claude-codex-wechat bridge daemon
242
- After=network.target
243
-
244
- [Service]
245
- Type=simple
246
- User=youruser
247
- Environment=BRIDGE_PORT=8787
248
- ExecStart=/usr/bin/claude-codex-wechat start
249
- Restart=on-failure
250
- RestartSec=5
251
-
252
- [Install]
253
- WantedBy=multi-user.target
254
- ```
255
-
256
- ### launchd
257
-
258
- 示例:
259
-
260
- ```xml
261
- <?xml version="1.0" encoding="UTF-8"?>
262
- <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
263
- <plist version="1.0">
264
- <dict>
265
- <key>Label</key>
266
- <string>com.claude-codex-wechat</string>
267
- <key>ProgramArguments</key>
268
- <array>
269
- <string>/opt/homebrew/bin/claude-codex-wechat</string>
270
- <string>start</string>
271
- </array>
272
- <key>EnvironmentVariables</key>
273
- <dict>
274
- <key>BRIDGE_PORT</key>
275
- <string>8787</string>
276
- </dict>
277
- <key>RunAtLoad</key>
278
- <true/>
279
- <key>KeepAlive</key>
280
- <true/>
281
- </dict>
282
- </plist>
283
- ```
216
+ 服务会以 `KeepAlive` / `Restart=on-failure` 守护,崩溃后自动拉起,无需额外进程管理器。
284
217
 
285
218
  ## 发布 npm 包
286
219
 
@@ -347,7 +280,6 @@ HTTPS_PROXY=http://127.0.0.1:7890 HTTP_PROXY=http://127.0.0.1:7890 ./release.sh
347
280
 
348
281
  这个脚本会:
349
282
 
350
- - 先检查 `npm whoami`
351
283
  - 先执行 `pnpm typecheck`
352
284
  - 再执行 `pnpm build`
353
285
  - 提交当前改动
@@ -3989,27 +3989,6 @@ async function resolveProviderCommands(providers) {
3989
3989
  };
3990
3990
  }
3991
3991
 
3992
- // src/daemon/portGuard.ts
3993
- import { execFile } from "node:child_process";
3994
- import { promisify } from "node:util";
3995
- var execFileAsync = promisify(execFile);
3996
- async function findListeningProcess(port) {
3997
- if (process.platform === "win32") return null;
3998
- try {
3999
- const { stdout } = await execFileAsync("lsof", ["-n", "-P", `-iTCP:${port}`, "-sTCP:LISTEN"]);
4000
- const lines = stdout.trim().split(/\r?\n/).filter(Boolean);
4001
- const record = lines.slice(1)[0];
4002
- if (!record) return null;
4003
- const parts = record.trim().split(/\s+/);
4004
- const command = parts[0] ?? "";
4005
- const pid = Number(parts[1] ?? "");
4006
- if (!command || !Number.isFinite(pid)) return null;
4007
- return { pid, command };
4008
- } catch {
4009
- return null;
4010
- }
4011
- }
4012
-
4013
3992
  // src/daemon/staticFrontend.ts
4014
3993
  import { readFileSync as readFileSync6 } from "node:fs";
4015
3994
  import { join as join9 } from "node:path";
@@ -4037,10 +4016,10 @@ import { mkdir as mkdir4, readFile as readFile6, rm as rm2, writeFile as writeFi
4037
4016
  import { homedir as homedir7 } from "node:os";
4038
4017
  import { dirname as dirname10, join as join10 } from "node:path";
4039
4018
  import { createReadStream } from "node:fs";
4040
- import { execFile as execFile2, spawn as spawn6 } from "node:child_process";
4019
+ import { execFile, spawn as spawn6 } from "node:child_process";
4041
4020
  import { createInterface } from "node:readline";
4042
- import { promisify as promisify2 } from "node:util";
4043
- var execFileAsync2 = promisify2(execFile2);
4021
+ import { promisify } from "node:util";
4022
+ var execFileAsync = promisify(execFile);
4044
4023
  async function installService(context) {
4045
4024
  if (process.platform === "darwin") {
4046
4025
  const spec = buildLaunchdSpec(context);
@@ -4181,7 +4160,7 @@ function buildLaunchdSpec(context) {
4181
4160
  return {
4182
4161
  label: "com.claude-codex-wechat",
4183
4162
  plistPath: join10(homedir7(), "Library", "LaunchAgents", "com.claude-codex-wechat.plist"),
4184
- programArgs: [context.nodePath ?? process.execPath, context.cliEntrypointPath, "start"],
4163
+ programArgs: [context.nodePath ?? process.execPath, context.cliEntrypointPath, "__daemon"],
4185
4164
  workingDirectory: homedir7(),
4186
4165
  stdoutPath: join10(stateDir, "logs", "service.stdout.log"),
4187
4166
  stderrPath: join10(stateDir, "logs", "service.stderr.log"),
@@ -4193,7 +4172,7 @@ function buildSystemdUserSpec(context) {
4193
4172
  return {
4194
4173
  unitName: "claude-codex-wechat.service",
4195
4174
  unitPath: join10(homedir7(), ".config", "systemd", "user", "claude-codex-wechat.service"),
4196
- execStart: [context.nodePath ?? process.execPath, context.cliEntrypointPath, "start"],
4175
+ execStart: [context.nodePath ?? process.execPath, context.cliEntrypointPath, "__daemon"],
4197
4176
  workingDirectory: homedir7(),
4198
4177
  stdoutPath: join10(stateDir, "logs", "service.stdout.log"),
4199
4178
  stderrPath: join10(stateDir, "logs", "service.stderr.log"),
@@ -4292,10 +4271,10 @@ function quoteSystemdEnv(value) {
4292
4271
  return `"${value.replaceAll("\\", "\\\\").replaceAll('"', '\\"')}"`;
4293
4272
  }
4294
4273
  async function runLaunchctl(args) {
4295
- await execFileAsync2("launchctl", args);
4274
+ await execFileAsync("launchctl", args);
4296
4275
  }
4297
4276
  async function runSystemctl(args) {
4298
- await execFileAsync2("systemctl", args);
4277
+ await execFileAsync("systemctl", args);
4299
4278
  }
4300
4279
  async function ensureFileExists(path, errorCode) {
4301
4280
  if (!existsSync8(path)) throw new Error(errorCode);
@@ -4350,18 +4329,37 @@ async function main() {
4350
4329
  case "start":
4351
4330
  await cmdStart();
4352
4331
  return;
4332
+ case "stop":
4333
+ await cmdStop();
4334
+ return;
4335
+ case "restart":
4336
+ await cmdRestart();
4337
+ return;
4338
+ case "status":
4339
+ await cmdStatus();
4340
+ return;
4341
+ case "logs":
4342
+ await cmdLogs();
4343
+ return;
4344
+ case "tail":
4345
+ await cmdTail();
4346
+ return;
4353
4347
  case "init":
4354
4348
  cmdInit();
4355
4349
  return;
4356
4350
  case "doctor":
4357
4351
  await cmdDoctor();
4358
4352
  return;
4359
- case "service":
4360
- await cmdService(process.argv.slice(3));
4353
+ case "uninstall":
4354
+ await cmdUninstall();
4361
4355
  return;
4362
4356
  case "print-config":
4363
4357
  cmdPrintConfig();
4364
4358
  return;
4359
+ // 内部命令:service 文件实际拉起的前台 daemon 进程,不对外暴露。
4360
+ case "__daemon":
4361
+ await cmdDaemon();
4362
+ return;
4365
4363
  case "help":
4366
4364
  case "--help":
4367
4365
  case "-h":
@@ -4377,24 +4375,38 @@ async function main() {
4377
4375
  async function cmdStart() {
4378
4376
  if (!existsSync9(webRoot)) {
4379
4377
  console.error(`\u627E\u4E0D\u5230\u524D\u7AEF\u6784\u5EFA\u4EA7\u7269: ${webRoot}`);
4380
- console.error("\u8BF7\u5148\u8FD0\u884C\u6784\u5EFA (pnpm build) \u540E\u518D\u542F\u52A8\uFF0C\u6216\u91CD\u65B0\u5B89\u88C5\u5B8C\u6574\u7684 npm \u5305\u3002");
4378
+ console.error("\u8BF7\u91CD\u65B0\u5B89\u88C5\u5B8C\u6574\u7684 npm \u5305\u3002");
4381
4379
  process.exitCode = 1;
4382
4380
  return;
4383
4381
  }
4384
4382
  const context = createServiceContext();
4385
- const port = context.port ?? 8787;
4386
- const occupiedBy = await findListeningProcess(port);
4387
- if (occupiedBy) {
4388
- const serviceStatus = await readServiceStatus(context).catch(() => null);
4389
- if (serviceStatus?.installed && serviceStatus.running) {
4390
- console.log(`\u7AEF\u53E3 ${port} \u5DF2\u88AB\u540E\u53F0\u670D\u52A1\u5360\u7528\uFF0C\u5148\u505C\u6B62\u670D\u52A1\u518D\u4EE5\u524D\u53F0\u6A21\u5F0F\u542F\u52A8\u3002`);
4391
- await stopService(context);
4392
- } else {
4393
- console.error(`\u7AEF\u53E3 ${port} \u5DF2\u88AB\u5360\u7528: PID=${occupiedBy.pid} COMMAND=${occupiedBy.command}`);
4394
- console.error("\u8BF7\u5148\u505C\u6B62\u5360\u7528\u8FDB\u7A0B\uFF0C\u6216\u6539\u7528 `claude-codex-wechat service stop` \u505C\u6389\u540E\u53F0\u670D\u52A1\u3002");
4395
- process.exitCode = 1;
4396
- return;
4397
- }
4383
+ const status = await readServiceStatus(context).catch(() => null);
4384
+ const result = status?.installed ? await startService(context) : await installService(context);
4385
+ printServiceStatus("service started", result);
4386
+ }
4387
+ async function cmdStop() {
4388
+ const status = await stopService(createServiceContext());
4389
+ printServiceStatus("service stopped", status);
4390
+ }
4391
+ async function cmdRestart() {
4392
+ const status = await restartService(createServiceContext());
4393
+ printServiceStatus("service restarted", status);
4394
+ }
4395
+ async function cmdStatus() {
4396
+ const status = await readServiceStatus(createServiceContext());
4397
+ printServiceStatus("service status", status);
4398
+ }
4399
+ async function cmdLogs() {
4400
+ console.log(await readServiceLogs(createServiceContext()));
4401
+ }
4402
+ async function cmdTail() {
4403
+ await tailServiceLogs(createServiceContext());
4404
+ }
4405
+ async function cmdDaemon() {
4406
+ if (!existsSync9(webRoot)) {
4407
+ console.error(`\u627E\u4E0D\u5230\u524D\u7AEF\u6784\u5EFA\u4EA7\u7269: ${webRoot}`);
4408
+ process.exitCode = 1;
4409
+ return;
4398
4410
  }
4399
4411
  await startDaemon({
4400
4412
  attachFrontend: attachStaticFrontend(webRoot)
@@ -4452,54 +4464,9 @@ function cmdPrintConfig() {
4452
4464
  }
4453
4465
  console.log(readFileSync7(configPath, "utf8"));
4454
4466
  }
4455
- async function cmdService(args) {
4456
- const action = args[0] ?? "status";
4457
- const context = createServiceContext();
4458
- switch (action) {
4459
- case "install": {
4460
- const status = await installService(context);
4461
- printServiceStatus("service installed", status);
4462
- return;
4463
- }
4464
- case "start": {
4465
- const status = await startService(context);
4466
- printServiceStatus("service started", status);
4467
- return;
4468
- }
4469
- case "stop": {
4470
- const status = await stopService(context);
4471
- printServiceStatus("service stopped", status);
4472
- return;
4473
- }
4474
- case "restart": {
4475
- const status = await restartService(context);
4476
- printServiceStatus("service restarted", status);
4477
- return;
4478
- }
4479
- case "logs": {
4480
- console.log(await readServiceLogs(context));
4481
- return;
4482
- }
4483
- case "tail": {
4484
- await tailServiceLogs(context);
4485
- return;
4486
- }
4487
- case "uninstall": {
4488
- const status = await uninstallService(context);
4489
- printServiceStatus("service uninstalled", status);
4490
- return;
4491
- }
4492
- case "status": {
4493
- const status = await readServiceStatus(context);
4494
- printServiceStatus("service status", status);
4495
- return;
4496
- }
4497
- default:
4498
- console.error(`\u672A\u77E5 service \u5B50\u547D\u4EE4: ${action}
4499
- `);
4500
- printUsage();
4501
- process.exitCode = 1;
4502
- }
4467
+ async function cmdUninstall() {
4468
+ const status = await uninstallService(createServiceContext());
4469
+ printServiceStatus("service uninstalled", status);
4503
4470
  }
4504
4471
  function createServiceContext() {
4505
4472
  return {
@@ -4530,18 +4497,20 @@ function printUsage() {
4530
4497
  claude-codex-wechat <command>
4531
4498
 
4532
4499
  \u547D\u4EE4:
4533
- start \u542F\u52A8 daemon\uFF08\u9ED8\u8BA4\u547D\u4EE4\uFF0C\u524D\u53F0\u8FD0\u884C\uFF09
4534
- init \u5728 ~/.claude-codex-wechat/ \u521B\u5EFA\u9ED8\u8BA4\u914D\u7F6E
4500
+ start \u540E\u53F0\u542F\u52A8\u670D\u52A1\uFF08\u672A\u5B89\u88C5\u5219\u81EA\u52A8\u5B89\u88C5\uFF0C\u9ED8\u8BA4\u547D\u4EE4\uFF09
4501
+ stop \u505C\u6B62\u540E\u53F0\u670D\u52A1
4502
+ restart \u91CD\u542F\u540E\u53F0\u670D\u52A1
4503
+ status \u67E5\u770B\u8FD0\u884C\u72B6\u6001
4504
+ logs \u6253\u5370\u6700\u8FD1\u65E5\u5FD7
4505
+ tail \u5B9E\u65F6\u8DDF\u968F\u65E5\u5FD7
4535
4506
  doctor \u68C0\u67E5\u914D\u7F6E\u3001\u524D\u7AEF\u4EA7\u7269\u4E0E claude/codex \u53EF\u6267\u884C\u6587\u4EF6
4536
- service \u7BA1\u7406\u540E\u53F0\u670D\u52A1\uFF08install/start/stop/restart/status/logs/tail/uninstall\uFF09
4507
+ init \u5728 ~/.claude-codex-wechat/ \u521B\u5EFA\u9ED8\u8BA4\u914D\u7F6E
4508
+ uninstall \u5378\u8F7D\u540E\u53F0\u670D\u52A1
4537
4509
  print-config \u6253\u5370\u5F53\u524D\u914D\u7F6E\u6587\u4EF6\u5185\u5BB9
4538
4510
  help \u663E\u793A\u672C\u5E2E\u52A9
4539
4511
 
4540
4512
  \u73AF\u5883\u53D8\u91CF:
4541
4513
  BRIDGE_PORT \u76D1\u542C\u7AEF\u53E3\uFF08\u9ED8\u8BA4 8787\uFF09
4542
- BRIDGE_CONFIG \u914D\u7F6E\u6587\u4EF6\u8DEF\u5F84\uFF08\u9ED8\u8BA4 ~/.claude-codex-wechat/config.json\uFF09
4543
-
4544
- \u5E38\u9A7B\u8FD0\u884C\u8BF7\u7528\u8FDB\u7A0B\u7BA1\u7406\u5668\u6258\u7BA1\uFF0C\u4F8B\u5982:
4545
- pm2 start claude-codex-wechat -- start`);
4514
+ BRIDGE_CONFIG \u914D\u7F6E\u6587\u4EF6\u8DEF\u5F84\uFF08\u9ED8\u8BA4 ~/.claude-codex-wechat/config.json\uFF09`);
4546
4515
  }
4547
4516
  await main();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-codex-wechat",
3
- "version": "0.1.6",
3
+ "version": "0.1.8",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "bin": {