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
package/dist/server/cli.js
CHANGED
|
@@ -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 =
|
|
112
|
-
|
|
113
|
-
...typeof relayRecord
|
|
114
|
-
}
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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: "\
|
|
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\
|
|
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
|
-
|
|
4212
|
-
|
|
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:
|
|
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?.
|
|
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
|
|
4767
|
-
|
|
4768
|
-
|
|
4769
|
-
|
|
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
|
|
4781
|
-
|
|
4782
|
-
|
|
4783
|
-
|
|
4784
|
-
|
|
4785
|
-
|
|
4786
|
-
|
|
4787
|
-
|
|
4788
|
-
|
|
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();
|