claude-codex-wechat 0.1.8 → 0.1.12
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 +34 -15
- package/dist/server/cli.js +134 -80
- package/dist/web/assets/index-BW-_vdVB.js +10 -0
- package/dist/web/index.html +1 -1
- package/package.json +5 -1
- package/dist/web/assets/index-Dxzp2xaC.js +0 -10
package/README.md
CHANGED
|
@@ -52,22 +52,47 @@ npm install -g claude-codex-wechat
|
|
|
52
52
|
pnpm add -g claude-codex-wechat
|
|
53
53
|
```
|
|
54
54
|
|
|
55
|
-
|
|
55
|
+
说明:
|
|
56
|
+
|
|
57
|
+
- 该包依赖 `better-sqlite3`
|
|
58
|
+
- 大多数常见平台会直接下载预编译二进制
|
|
59
|
+
- 少数环境可能需要本地编译工具链
|
|
60
|
+
- Node 版本要求:`>=20`
|
|
61
|
+
|
|
62
|
+
## 快速开始
|
|
63
|
+
|
|
64
|
+
安装后按以下顺序跑通:
|
|
56
65
|
|
|
57
66
|
```bash
|
|
58
|
-
|
|
67
|
+
# 1. 生成默认配置
|
|
59
68
|
claude-codex-wechat init
|
|
69
|
+
|
|
70
|
+
# 2. 编辑 ~/.claude-codex-wechat/config.json,填入微信 token / accountId
|
|
71
|
+
# (配置说明见下方「配置」一节)
|
|
72
|
+
|
|
73
|
+
# 3. 检查环境(配置、前端产物、claude/codex 是否就绪)
|
|
60
74
|
claude-codex-wechat doctor
|
|
75
|
+
|
|
76
|
+
# 4. 后台启动(首次会自动注册为系统服务并守护运行)
|
|
61
77
|
claude-codex-wechat start
|
|
62
|
-
|
|
78
|
+
|
|
79
|
+
# 5. 查看状态 / 日志
|
|
80
|
+
claude-codex-wechat status
|
|
81
|
+
claude-codex-wechat logs
|
|
63
82
|
```
|
|
64
83
|
|
|
65
|
-
|
|
84
|
+
启动后:
|
|
66
85
|
|
|
67
|
-
-
|
|
68
|
-
-
|
|
69
|
-
|
|
70
|
-
|
|
86
|
+
- 管理页:`http://127.0.0.1:8787`
|
|
87
|
+
- 微信侧即可直接给机器人发消息,驱动本机 `Claude Code` / `Codex CLI` 会话
|
|
88
|
+
|
|
89
|
+
停止 / 重启 / 卸载:
|
|
90
|
+
|
|
91
|
+
```bash
|
|
92
|
+
claude-codex-wechat restart
|
|
93
|
+
claude-codex-wechat stop
|
|
94
|
+
claude-codex-wechat uninstall
|
|
95
|
+
```
|
|
71
96
|
|
|
72
97
|
### 从源码安装
|
|
73
98
|
|
|
@@ -188,13 +213,7 @@ pnpm build
|
|
|
188
213
|
|
|
189
214
|
## 生产运行
|
|
190
215
|
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
```bash
|
|
194
|
-
claude-codex-wechat init # 首次:写配置,填好 token / accountId
|
|
195
|
-
claude-codex-wechat start # 后台启动(自动安装服务)
|
|
196
|
-
claude-codex-wechat status # 查看状态
|
|
197
|
-
```
|
|
216
|
+
首次跑通流程见上方「快速开始」。`start` 会把当前 CLI 注册成操作系统服务并守护拉起。
|
|
198
217
|
|
|
199
218
|
常用管理命令:
|
|
200
219
|
|
package/dist/server/cli.js
CHANGED
|
@@ -3,7 +3,7 @@ import { createRequire as __cjsCreateRequire } from 'node:module'; const require
|
|
|
3
3
|
|
|
4
4
|
// src/cli.ts
|
|
5
5
|
import { existsSync as existsSync9, mkdirSync as mkdirSync6, readFileSync as readFileSync7, writeFileSync as writeFileSync6 } from "node:fs";
|
|
6
|
-
import { dirname as
|
|
6
|
+
import { dirname as dirname12, join as join12 } from "node:path";
|
|
7
7
|
import { fileURLToPath } from "node:url";
|
|
8
8
|
|
|
9
9
|
// src/daemon/bootstrap.ts
|
|
@@ -12,7 +12,7 @@ import { networkInterfaces } from "node:os";
|
|
|
12
12
|
// src/daemon/server.ts
|
|
13
13
|
import { mkdtempSync } from "node:fs";
|
|
14
14
|
import { tmpdir as tmpdir2 } from "node:os";
|
|
15
|
-
import { dirname as
|
|
15
|
+
import { dirname as dirname10, join as join9 } from "node:path";
|
|
16
16
|
import Fastify from "fastify";
|
|
17
17
|
|
|
18
18
|
// src/channels/platforms.ts
|
|
@@ -372,7 +372,7 @@ async function listRecoverableClaudeSessions(env = process.env) {
|
|
|
372
372
|
id: sessionId,
|
|
373
373
|
providerId: "claude-code",
|
|
374
374
|
...cwd ? { cwd } : {},
|
|
375
|
-
title: parsedMeta?.aiTitle ?? parsedMeta?.lastPrompt
|
|
375
|
+
...parsedMeta?.aiTitle ?? parsedMeta?.lastPrompt ? { title: parsedMeta?.aiTitle ?? parsedMeta?.lastPrompt } : {},
|
|
376
376
|
...parsedMeta?.sessionName ? { resumeTitle: parsedMeta.sessionName } : historyMeta?.display ? { resumeTitle: historyMeta.display } : {},
|
|
377
377
|
...historyMeta?.timestamp ? { lastActivityAt: historyMeta.timestamp } : metadata ? { lastActivityAt: Math.trunc(metadata.mtimeMs) } : {},
|
|
378
378
|
...bridgeTag ? { bridgeBindingSource: "bridge_tag", bridgeTag } : {}
|
|
@@ -880,7 +880,7 @@ async function syncCodexRolloutSessionMeta(rolloutPath) {
|
|
|
880
880
|
}
|
|
881
881
|
}
|
|
882
882
|
async function delay(ms) {
|
|
883
|
-
await new Promise((
|
|
883
|
+
await new Promise((resolve2) => setTimeout(resolve2, ms));
|
|
884
884
|
}
|
|
885
885
|
|
|
886
886
|
// src/session/providerAutoAttach.ts
|
|
@@ -1061,7 +1061,7 @@ function registerChannelAdminRoutes(input) {
|
|
|
1061
1061
|
|
|
1062
1062
|
`);
|
|
1063
1063
|
while (true) {
|
|
1064
|
-
await new Promise((
|
|
1064
|
+
await new Promise((resolve2) => setTimeout(resolve2, client.pollIntervalMs));
|
|
1065
1065
|
const status = await client.pollQrCodeStatus(qr.ticket);
|
|
1066
1066
|
if (status.status === "waiting") continue;
|
|
1067
1067
|
if (status.status === "scanned") {
|
|
@@ -1431,6 +1431,48 @@ function normalizeSettings(input, defaultWorkspace) {
|
|
|
1431
1431
|
};
|
|
1432
1432
|
}
|
|
1433
1433
|
|
|
1434
|
+
// src/admin/fsBrowseRoutes.ts
|
|
1435
|
+
import { readdir as readdir3 } from "node:fs/promises";
|
|
1436
|
+
import { homedir as homedir7 } from "node:os";
|
|
1437
|
+
import { dirname as dirname5, join as join6, resolve } from "node:path";
|
|
1438
|
+
|
|
1439
|
+
// src/shared/expandTilde.ts
|
|
1440
|
+
import { homedir as homedir6 } from "node:os";
|
|
1441
|
+
function expandTilde(target) {
|
|
1442
|
+
if (!target) return target;
|
|
1443
|
+
if (target === "~") return homedir6();
|
|
1444
|
+
if (target.startsWith("~/")) return homedir6() + target.slice(1);
|
|
1445
|
+
return target;
|
|
1446
|
+
}
|
|
1447
|
+
|
|
1448
|
+
// src/admin/fsBrowseRoutes.ts
|
|
1449
|
+
function registerFsBrowseRoutes(input) {
|
|
1450
|
+
input.app.get("/api/fs/list", async (request, reply) => {
|
|
1451
|
+
const raw = request.query.path?.trim();
|
|
1452
|
+
const target = resolve(expandTilde(raw && raw.length > 0 ? raw : homedir7()) ?? homedir7());
|
|
1453
|
+
const keepRaw = request.query.keep?.trim();
|
|
1454
|
+
const keep = keepRaw && keepRaw.length > 0 ? resolve(expandTilde(keepRaw) ?? keepRaw) : null;
|
|
1455
|
+
const isOnKeepChain = (path) => keep !== null && (keep === path || keep.startsWith(path + "/"));
|
|
1456
|
+
let dirents;
|
|
1457
|
+
try {
|
|
1458
|
+
dirents = await readdir3(target, { withFileTypes: true });
|
|
1459
|
+
} catch {
|
|
1460
|
+
reply.code(400);
|
|
1461
|
+
return { error: "cannot_read_directory", path: target };
|
|
1462
|
+
}
|
|
1463
|
+
const entries = dirents.filter((dirent) => dirent.isDirectory()).map((dirent) => ({ name: dirent.name, path: join6(target, dirent.name), isDirectory: true })).filter((entry) => !entry.name.startsWith(".") || isOnKeepChain(entry.path)).sort((a, b) => a.name.localeCompare(b.name));
|
|
1464
|
+
const parent = dirname5(target);
|
|
1465
|
+
const isRoot = parent === target;
|
|
1466
|
+
const listing = {
|
|
1467
|
+
path: target,
|
|
1468
|
+
parent: isRoot ? null : parent,
|
|
1469
|
+
isRoot,
|
|
1470
|
+
entries
|
|
1471
|
+
};
|
|
1472
|
+
return listing;
|
|
1473
|
+
});
|
|
1474
|
+
}
|
|
1475
|
+
|
|
1434
1476
|
// src/channels/weixin-direct/apiClient.ts
|
|
1435
1477
|
import { randomUUID } from "node:crypto";
|
|
1436
1478
|
function collectInboundItems(itemList) {
|
|
@@ -1600,7 +1642,7 @@ var WeixinDirectApiClient = class {
|
|
|
1600
1642
|
|
|
1601
1643
|
// src/channels/weixin-direct/adapter.ts
|
|
1602
1644
|
import { setTimeout as delay2 } from "node:timers/promises";
|
|
1603
|
-
import { join as
|
|
1645
|
+
import { join as join7 } from "node:path";
|
|
1604
1646
|
|
|
1605
1647
|
// src/channels/weixin-direct/typingController.ts
|
|
1606
1648
|
var CONFIG_CACHE_TTL_MS = 24 * 60 * 60 * 1e3;
|
|
@@ -1865,7 +1907,7 @@ var WeixinDirectAdapter = class _WeixinDirectAdapter {
|
|
|
1865
1907
|
continue;
|
|
1866
1908
|
}
|
|
1867
1909
|
const { ext, mimeType } = mediaExtAndMime(meta);
|
|
1868
|
-
const destPath =
|
|
1910
|
+
const destPath = join7(mediaDir, `${idPrefix}_${i}${ext}`);
|
|
1869
1911
|
const maxBytes = meta.kind === "video" ? VIDEO_MAX_BYTES : void 0;
|
|
1870
1912
|
const result = await downloader.download(meta.media ?? {}, { destPath, aeskeyOverride: meta.aeskey, maxBytes });
|
|
1871
1913
|
if (result.ok) {
|
|
@@ -1926,7 +1968,7 @@ function chunkText(text, limit) {
|
|
|
1926
1968
|
|
|
1927
1969
|
// src/channels/weixin-direct/mediaDownloader.ts
|
|
1928
1970
|
import { mkdirSync as mkdirSync2, writeFileSync as writeFileSync2 } from "node:fs";
|
|
1929
|
-
import { dirname as
|
|
1971
|
+
import { dirname as dirname6 } from "node:path";
|
|
1930
1972
|
|
|
1931
1973
|
// src/channels/weixin-direct/mediaCrypto.ts
|
|
1932
1974
|
import { createDecipheriv } from "node:crypto";
|
|
@@ -1989,7 +2031,7 @@ var WeixinMediaDownloader = class {
|
|
|
1989
2031
|
} catch {
|
|
1990
2032
|
return { ok: false, reason: "decrypt_failed" };
|
|
1991
2033
|
}
|
|
1992
|
-
mkdirSync2(
|
|
2034
|
+
mkdirSync2(dirname6(input.destPath), { recursive: true });
|
|
1993
2035
|
writeFileSync2(input.destPath, plaintext);
|
|
1994
2036
|
return { ok: true, localPath: input.destPath, bytes: plaintext.length };
|
|
1995
2037
|
}
|
|
@@ -2174,18 +2216,9 @@ var WeixinOutboundGate = class _WeixinOutboundGate {
|
|
|
2174
2216
|
import { spawn as spawn2 } from "node:child_process";
|
|
2175
2217
|
import { randomUUID as randomUUID2 } from "node:crypto";
|
|
2176
2218
|
|
|
2177
|
-
// src/shared/expandTilde.ts
|
|
2178
|
-
import { homedir as homedir6 } from "node:os";
|
|
2179
|
-
function expandTilde(target) {
|
|
2180
|
-
if (!target) return target;
|
|
2181
|
-
if (target === "~") return homedir6();
|
|
2182
|
-
if (target.startsWith("~/")) return homedir6() + target.slice(1);
|
|
2183
|
-
return target;
|
|
2184
|
-
}
|
|
2185
|
-
|
|
2186
2219
|
// src/shared/platform.ts
|
|
2187
2220
|
import { access, constants } from "node:fs/promises";
|
|
2188
|
-
import { delimiter, join as
|
|
2221
|
+
import { delimiter, join as join8 } from "node:path";
|
|
2189
2222
|
import { tmpdir } from "node:os";
|
|
2190
2223
|
import { spawn } from "node:child_process";
|
|
2191
2224
|
var isWindows = process.platform === "win32";
|
|
@@ -2213,7 +2246,7 @@ async function findExecutable(command) {
|
|
|
2213
2246
|
const dirs = (process.env.PATH ?? "").split(delimiter).filter(Boolean);
|
|
2214
2247
|
for (const dir of dirs) {
|
|
2215
2248
|
for (const ext of extensions) {
|
|
2216
|
-
const candidate =
|
|
2249
|
+
const candidate = join8(dir, command + ext);
|
|
2217
2250
|
if (await isExecutableFile(candidate)) return candidate;
|
|
2218
2251
|
}
|
|
2219
2252
|
}
|
|
@@ -2236,10 +2269,10 @@ function terminateChild(child, signal = "SIGTERM") {
|
|
|
2236
2269
|
}
|
|
2237
2270
|
}
|
|
2238
2271
|
function defaultWorkspaceDir() {
|
|
2239
|
-
return
|
|
2272
|
+
return join8(tmpdir(), "project");
|
|
2240
2273
|
}
|
|
2241
2274
|
function statePath(filename) {
|
|
2242
|
-
return
|
|
2275
|
+
return join8(tmpdir(), filename);
|
|
2243
2276
|
}
|
|
2244
2277
|
|
|
2245
2278
|
// src/providers/claude-code/claudeStreamingRunner.ts
|
|
@@ -2418,10 +2451,10 @@ function defaultClaudeStreamSpawner(call) {
|
|
|
2418
2451
|
const queue = [];
|
|
2419
2452
|
let resolveNext = null;
|
|
2420
2453
|
const push = (chunk) => {
|
|
2421
|
-
const
|
|
2422
|
-
if (
|
|
2454
|
+
const resolve2 = resolveNext;
|
|
2455
|
+
if (resolve2) {
|
|
2423
2456
|
resolveNext = null;
|
|
2424
|
-
|
|
2457
|
+
resolve2(chunk);
|
|
2425
2458
|
} else {
|
|
2426
2459
|
queue.push(chunk);
|
|
2427
2460
|
}
|
|
@@ -2451,8 +2484,8 @@ function defaultClaudeStreamSpawner(call) {
|
|
|
2451
2484
|
read() {
|
|
2452
2485
|
const next = queue.shift();
|
|
2453
2486
|
if (next) return Promise.resolve(next);
|
|
2454
|
-
return new Promise((
|
|
2455
|
-
resolveNext =
|
|
2487
|
+
return new Promise((resolve2) => {
|
|
2488
|
+
resolveNext = resolve2;
|
|
2456
2489
|
});
|
|
2457
2490
|
},
|
|
2458
2491
|
close() {
|
|
@@ -2551,8 +2584,8 @@ var CodexAppServerClient = class {
|
|
|
2551
2584
|
const id = this.nextId++;
|
|
2552
2585
|
const key = String(id);
|
|
2553
2586
|
const payload = { id, method, ...params !== void 0 ? { params } : {} };
|
|
2554
|
-
const response = new Promise((
|
|
2555
|
-
this.pending.set(key, { resolve, reject });
|
|
2587
|
+
const response = new Promise((resolve2, reject) => {
|
|
2588
|
+
this.pending.set(key, { resolve: resolve2, reject });
|
|
2556
2589
|
});
|
|
2557
2590
|
this.child.stdin.write(`${JSON.stringify(payload)}
|
|
2558
2591
|
`);
|
|
@@ -2653,6 +2686,18 @@ var CodexAppServerClient = class {
|
|
|
2653
2686
|
};
|
|
2654
2687
|
|
|
2655
2688
|
// src/providers/codex/codexInteractiveRunner.ts
|
|
2689
|
+
function readAgentMessageItemId(value) {
|
|
2690
|
+
if (!value || typeof value !== "object") return void 0;
|
|
2691
|
+
const record = value;
|
|
2692
|
+
if (typeof record.itemId === "string" && record.itemId) return record.itemId;
|
|
2693
|
+
if (typeof record.id === "string" && record.id) return record.id;
|
|
2694
|
+
if (record.item && typeof record.item === "object") {
|
|
2695
|
+
const item = record.item;
|
|
2696
|
+
if (typeof item.id === "string" && item.id) return item.id;
|
|
2697
|
+
if (typeof item.itemId === "string" && item.itemId) return item.itemId;
|
|
2698
|
+
}
|
|
2699
|
+
return void 0;
|
|
2700
|
+
}
|
|
2656
2701
|
function readThreadId(value) {
|
|
2657
2702
|
if (!value || typeof value !== "object") return void 0;
|
|
2658
2703
|
const record = value;
|
|
@@ -2693,7 +2738,7 @@ var CodexInteractiveRunner = class {
|
|
|
2693
2738
|
sessionName: input.options?.sessionName,
|
|
2694
2739
|
cwd: input.cwd,
|
|
2695
2740
|
status: "idle",
|
|
2696
|
-
|
|
2741
|
+
pendingMessages: []
|
|
2697
2742
|
};
|
|
2698
2743
|
this.sessions.set(input.bridgeSessionId, session);
|
|
2699
2744
|
return session;
|
|
@@ -2702,10 +2747,10 @@ var CodexInteractiveRunner = class {
|
|
|
2702
2747
|
const session = this.sessions.get(input.bridgeSessionId);
|
|
2703
2748
|
if (!session) throw new Error(`codex_session_not_found:${input.bridgeSessionId}`);
|
|
2704
2749
|
const client = await this.ensureClient(session);
|
|
2705
|
-
session.
|
|
2750
|
+
session.pendingMessages = [];
|
|
2706
2751
|
session.activeTurnId = void 0;
|
|
2707
|
-
session.turnCompletedPromise = new Promise((
|
|
2708
|
-
session.turnCompletedResolver =
|
|
2752
|
+
session.turnCompletedPromise = new Promise((resolve2) => {
|
|
2753
|
+
session.turnCompletedResolver = resolve2;
|
|
2709
2754
|
});
|
|
2710
2755
|
if (session.threadId) {
|
|
2711
2756
|
await client.request("thread/resume", {
|
|
@@ -2751,8 +2796,10 @@ var CodexInteractiveRunner = class {
|
|
|
2751
2796
|
cwd: session.cwd
|
|
2752
2797
|
});
|
|
2753
2798
|
}
|
|
2754
|
-
for (const
|
|
2755
|
-
|
|
2799
|
+
for (const message of session.pendingMessages) {
|
|
2800
|
+
if (!message.text) continue;
|
|
2801
|
+
yield { type: "text_delta", text: message.text };
|
|
2802
|
+
yield { type: "message_done" };
|
|
2756
2803
|
}
|
|
2757
2804
|
yield {
|
|
2758
2805
|
type: "session_state",
|
|
@@ -2764,7 +2811,6 @@ var CodexInteractiveRunner = class {
|
|
|
2764
2811
|
status: "idle"
|
|
2765
2812
|
}
|
|
2766
2813
|
};
|
|
2767
|
-
yield { type: "message_done" };
|
|
2768
2814
|
}
|
|
2769
2815
|
async stopSession(bridgeSessionId) {
|
|
2770
2816
|
const session = this.sessions.get(bridgeSessionId);
|
|
@@ -2801,7 +2847,14 @@ var CodexInteractiveRunner = class {
|
|
|
2801
2847
|
await client.initialize();
|
|
2802
2848
|
client.onNotification("item/agentMessage/delta", (params) => {
|
|
2803
2849
|
const record = params;
|
|
2804
|
-
if (typeof record.delta
|
|
2850
|
+
if (typeof record.delta !== "string" || !record.delta) return;
|
|
2851
|
+
const itemId = readAgentMessageItemId(record) ?? `fallback-${session.pendingMessages.length}`;
|
|
2852
|
+
const last = session.pendingMessages.at(-1);
|
|
2853
|
+
if (last?.itemId === itemId) {
|
|
2854
|
+
last.text += record.delta;
|
|
2855
|
+
return;
|
|
2856
|
+
}
|
|
2857
|
+
session.pendingMessages.push({ itemId, text: record.delta });
|
|
2805
2858
|
});
|
|
2806
2859
|
client.onNotification("turn/started", (params) => {
|
|
2807
2860
|
const turnId = readTurnId(params);
|
|
@@ -2878,7 +2931,7 @@ function createDefaultProviders(input = {}) {
|
|
|
2878
2931
|
// src/providers/claude-code/claudeDetection.ts
|
|
2879
2932
|
import { spawn as spawn4 } from "node:child_process";
|
|
2880
2933
|
async function defaultCommandRunner(command, args) {
|
|
2881
|
-
return await new Promise((
|
|
2934
|
+
return await new Promise((resolve2) => {
|
|
2882
2935
|
const child = spawn4(command, args, { stdio: ["ignore", "pipe", "pipe"] });
|
|
2883
2936
|
let stdout = "";
|
|
2884
2937
|
let stderr = "";
|
|
@@ -2889,11 +2942,11 @@ async function defaultCommandRunner(command, args) {
|
|
|
2889
2942
|
stderr += String(chunk);
|
|
2890
2943
|
});
|
|
2891
2944
|
child.on("error", (error) => {
|
|
2892
|
-
|
|
2945
|
+
resolve2({ ok: false, code: error.code ?? "ERROR", stdout, stderr: stderr || error.message });
|
|
2893
2946
|
});
|
|
2894
2947
|
child.on("close", (code) => {
|
|
2895
|
-
if (code === 0)
|
|
2896
|
-
else
|
|
2948
|
+
if (code === 0) resolve2({ ok: true, stdout, stderr });
|
|
2949
|
+
else resolve2({ ok: false, code: code ?? "SIGNAL", stdout, stderr });
|
|
2897
2950
|
});
|
|
2898
2951
|
});
|
|
2899
2952
|
}
|
|
@@ -2914,7 +2967,7 @@ ${result.stderr}`) };
|
|
|
2914
2967
|
// src/providers/codex/codexDetection.ts
|
|
2915
2968
|
import { spawn as spawn5 } from "node:child_process";
|
|
2916
2969
|
async function defaultCodexCommandRunner(command, args) {
|
|
2917
|
-
return await new Promise((
|
|
2970
|
+
return await new Promise((resolve2) => {
|
|
2918
2971
|
const child = spawn5(command, args, { stdio: ["ignore", "pipe", "pipe"] });
|
|
2919
2972
|
let stdout = "";
|
|
2920
2973
|
let stderr = "";
|
|
@@ -2925,11 +2978,11 @@ async function defaultCodexCommandRunner(command, args) {
|
|
|
2925
2978
|
stderr += String(chunk);
|
|
2926
2979
|
});
|
|
2927
2980
|
child.on("error", (error) => {
|
|
2928
|
-
|
|
2981
|
+
resolve2({ ok: false, code: error.code ?? "ERROR", stdout, stderr: stderr || error.message });
|
|
2929
2982
|
});
|
|
2930
2983
|
child.on("close", (code) => {
|
|
2931
|
-
if (code === 0)
|
|
2932
|
-
else
|
|
2984
|
+
if (code === 0) resolve2({ ok: true, stdout, stderr });
|
|
2985
|
+
else resolve2({ ok: false, code: code ?? "SIGNAL", stdout, stderr });
|
|
2933
2986
|
});
|
|
2934
2987
|
});
|
|
2935
2988
|
}
|
|
@@ -3060,7 +3113,7 @@ function buildBridgeCommandHelpMarkdown() {
|
|
|
3060
3113
|
|
|
3061
3114
|
// src/session/currentConversationStore.ts
|
|
3062
3115
|
import { existsSync as existsSync5, mkdirSync as mkdirSync3, readFileSync as readFileSync3, writeFileSync as writeFileSync3 } from "node:fs";
|
|
3063
|
-
import { dirname as
|
|
3116
|
+
import { dirname as dirname7 } from "node:path";
|
|
3064
3117
|
import { nanoid as nanoid2 } from "nanoid";
|
|
3065
3118
|
var CurrentConversationStore = class {
|
|
3066
3119
|
constructor(configPath, defaults) {
|
|
@@ -3142,7 +3195,7 @@ var CurrentConversationStore = class {
|
|
|
3142
3195
|
this.writeState(state);
|
|
3143
3196
|
}
|
|
3144
3197
|
writeState(state) {
|
|
3145
|
-
mkdirSync3(
|
|
3198
|
+
mkdirSync3(dirname7(this.configPath), { recursive: true });
|
|
3146
3199
|
writeFileSync3(this.configPath, `${JSON.stringify(state, null, 2)}
|
|
3147
3200
|
`, "utf8");
|
|
3148
3201
|
}
|
|
@@ -3350,8 +3403,8 @@ var MessageRouter = class _MessageRouter {
|
|
|
3350
3403
|
}
|
|
3351
3404
|
const genId = ++this.generationSeq;
|
|
3352
3405
|
let abortGeneration;
|
|
3353
|
-
const aborted = new Promise((
|
|
3354
|
-
abortGeneration = () =>
|
|
3406
|
+
const aborted = new Promise((resolve2) => {
|
|
3407
|
+
abortGeneration = () => resolve2("aborted");
|
|
3355
3408
|
});
|
|
3356
3409
|
this.activeGenerations.set(message.chatId, {
|
|
3357
3410
|
genId,
|
|
@@ -3576,7 +3629,7 @@ var MessageRouter = class _MessageRouter {
|
|
|
3576
3629
|
}
|
|
3577
3630
|
}
|
|
3578
3631
|
formatSessionLine(candidate) {
|
|
3579
|
-
const title = candidate.resumeTitle ?? candidate.title ?? candidate.id
|
|
3632
|
+
const title = candidate.resumeTitle ?? candidate.title ?? `\u672A\u547D\u540D\u4F1A\u8BDD \xB7 ${candidate.id.slice(0, 8)}`;
|
|
3580
3633
|
const cwd = candidate.cwd ? ` \xB7 ${candidate.cwd}` : "";
|
|
3581
3634
|
const when = candidate.lastActivityAt ? ` \xB7 ${formatRelativeTime(candidate.lastActivityAt)}` : "";
|
|
3582
3635
|
return `${title}${cwd}${when}`;
|
|
@@ -3649,7 +3702,7 @@ function composeInboundText(content) {
|
|
|
3649
3702
|
|
|
3650
3703
|
// src/storage/lastProviderSessionStore.ts
|
|
3651
3704
|
import { existsSync as existsSync6, mkdirSync as mkdirSync4, readFileSync as readFileSync4, writeFileSync as writeFileSync4 } from "node:fs";
|
|
3652
|
-
import { dirname as
|
|
3705
|
+
import { dirname as dirname8 } from "node:path";
|
|
3653
3706
|
var LastProviderSessionStore = class {
|
|
3654
3707
|
constructor(configPath) {
|
|
3655
3708
|
this.configPath = configPath;
|
|
@@ -3680,7 +3733,7 @@ var LastProviderSessionStore = class {
|
|
|
3680
3733
|
return raw && typeof raw === "object" ? raw : {};
|
|
3681
3734
|
}
|
|
3682
3735
|
writeState(state) {
|
|
3683
|
-
mkdirSync4(
|
|
3736
|
+
mkdirSync4(dirname8(this.configPath), { recursive: true });
|
|
3684
3737
|
writeFileSync4(this.configPath, `${JSON.stringify(state, null, 2)}
|
|
3685
3738
|
`, "utf8");
|
|
3686
3739
|
}
|
|
@@ -3688,7 +3741,7 @@ var LastProviderSessionStore = class {
|
|
|
3688
3741
|
|
|
3689
3742
|
// src/storage/runtimeUserStore.ts
|
|
3690
3743
|
import { mkdirSync as mkdirSync5, readFileSync as readFileSync5, writeFileSync as writeFileSync5, existsSync as existsSync7 } from "node:fs";
|
|
3691
|
-
import { dirname as
|
|
3744
|
+
import { dirname as dirname9 } from "node:path";
|
|
3692
3745
|
import { nanoid as nanoid3 } from "nanoid";
|
|
3693
3746
|
var RuntimeUserStore = class {
|
|
3694
3747
|
constructor(configPath) {
|
|
@@ -3753,7 +3806,7 @@ var RuntimeUserStore = class {
|
|
|
3753
3806
|
return raw && typeof raw === "object" ? raw : {};
|
|
3754
3807
|
}
|
|
3755
3808
|
writeState(state) {
|
|
3756
|
-
mkdirSync5(
|
|
3809
|
+
mkdirSync5(dirname9(this.configPath), { recursive: true });
|
|
3757
3810
|
writeFileSync5(this.configPath, `${JSON.stringify(state, null, 2)}
|
|
3758
3811
|
`, "utf8");
|
|
3759
3812
|
}
|
|
@@ -3784,7 +3837,7 @@ function createDaemonServer(options = {}) {
|
|
|
3784
3837
|
defaultProvider: options.bridgeDefaults?.defaultProvider ?? "claude-code",
|
|
3785
3838
|
defaultWorkspace: options.bridgeDefaults?.defaultWorkspace ?? process.cwd()
|
|
3786
3839
|
};
|
|
3787
|
-
const configPath = options.configPath ?? process.env.BRIDGE_CONFIG ??
|
|
3840
|
+
const configPath = options.configPath ?? process.env.BRIDGE_CONFIG ?? join9(mkdtempSync(join9(tmpdir2(), "claude-codex-wechat-")), "config.json");
|
|
3788
3841
|
const conversation = new CurrentConversationStore(configPath, {
|
|
3789
3842
|
defaultCwd: bridgeDefaults.defaultWorkspace,
|
|
3790
3843
|
defaultProviderId: bridgeDefaults.defaultProvider
|
|
@@ -3817,7 +3870,7 @@ function createDaemonServer(options = {}) {
|
|
|
3817
3870
|
});
|
|
3818
3871
|
}
|
|
3819
3872
|
const weixinStateStore = configPath ? new FileWeixinStateStore(configPath) : void 0;
|
|
3820
|
-
const weixinMediaDir = configPath ?
|
|
3873
|
+
const weixinMediaDir = configPath ? join9(dirname10(configPath), "media") : void 0;
|
|
3821
3874
|
const managedWechatChannel = options.channel ? null : new ManagedWeixinDirectAdapter(options.wechat, weixinStateStore, weixinMediaDir);
|
|
3822
3875
|
const channel = options.channel ?? managedWechatChannel;
|
|
3823
3876
|
const weixinOutboundGate = managedWechatChannel && weixinStateStore ? new WeixinOutboundGate({
|
|
@@ -3907,6 +3960,7 @@ function createDaemonServer(options = {}) {
|
|
|
3907
3960
|
defaults: bridgeDefaults,
|
|
3908
3961
|
configPath
|
|
3909
3962
|
});
|
|
3963
|
+
registerFsBrowseRoutes({ app });
|
|
3910
3964
|
app.get("/api/status", async () => ({
|
|
3911
3965
|
ok: true,
|
|
3912
3966
|
sessions: conversation.getCurrent() ? [conversation.getCurrent()] : []
|
|
@@ -3991,7 +4045,7 @@ async function resolveProviderCommands(providers) {
|
|
|
3991
4045
|
|
|
3992
4046
|
// src/daemon/staticFrontend.ts
|
|
3993
4047
|
import { readFileSync as readFileSync6 } from "node:fs";
|
|
3994
|
-
import { join as
|
|
4048
|
+
import { join as join10 } from "node:path";
|
|
3995
4049
|
import fastifyStatic from "@fastify/static";
|
|
3996
4050
|
function attachStaticFrontend(webRoot2) {
|
|
3997
4051
|
return async (app) => {
|
|
@@ -3999,7 +4053,7 @@ function attachStaticFrontend(webRoot2) {
|
|
|
3999
4053
|
root: webRoot2,
|
|
4000
4054
|
wildcard: false
|
|
4001
4055
|
});
|
|
4002
|
-
const indexHtml = readFileSync6(
|
|
4056
|
+
const indexHtml = readFileSync6(join10(webRoot2, "index.html"), "utf8");
|
|
4003
4057
|
app.setNotFoundHandler((request, reply) => {
|
|
4004
4058
|
if (request.url.startsWith("/api")) {
|
|
4005
4059
|
reply.code(404).send({ error: "not_found" });
|
|
@@ -4013,8 +4067,8 @@ function attachStaticFrontend(webRoot2) {
|
|
|
4013
4067
|
// src/daemon/service.ts
|
|
4014
4068
|
import { existsSync as existsSync8, statSync } from "node:fs";
|
|
4015
4069
|
import { mkdir as mkdir4, readFile as readFile6, rm as rm2, writeFile as writeFile5 } from "node:fs/promises";
|
|
4016
|
-
import { homedir as
|
|
4017
|
-
import { dirname as
|
|
4070
|
+
import { homedir as homedir8 } from "node:os";
|
|
4071
|
+
import { dirname as dirname11, join as join11 } from "node:path";
|
|
4018
4072
|
import { createReadStream } from "node:fs";
|
|
4019
4073
|
import { execFile, spawn as spawn6 } from "node:child_process";
|
|
4020
4074
|
import { createInterface } from "node:readline";
|
|
@@ -4023,8 +4077,8 @@ var execFileAsync = promisify(execFile);
|
|
|
4023
4077
|
async function installService(context) {
|
|
4024
4078
|
if (process.platform === "darwin") {
|
|
4025
4079
|
const spec = buildLaunchdSpec(context);
|
|
4026
|
-
await mkdir4(
|
|
4027
|
-
await mkdir4(
|
|
4080
|
+
await mkdir4(dirname11(spec.plistPath), { recursive: true });
|
|
4081
|
+
await mkdir4(dirname11(spec.stdoutPath), { recursive: true });
|
|
4028
4082
|
await writeFile5(spec.plistPath, renderLaunchdPlist(spec), "utf8");
|
|
4029
4083
|
await runLaunchctl(["unload", spec.plistPath]).catch(() => void 0);
|
|
4030
4084
|
await runLaunchctl(["load", spec.plistPath]);
|
|
@@ -4033,8 +4087,8 @@ async function installService(context) {
|
|
|
4033
4087
|
}
|
|
4034
4088
|
if (process.platform === "linux") {
|
|
4035
4089
|
const spec = buildSystemdUserSpec(context);
|
|
4036
|
-
await mkdir4(
|
|
4037
|
-
await mkdir4(
|
|
4090
|
+
await mkdir4(dirname11(spec.unitPath), { recursive: true });
|
|
4091
|
+
await mkdir4(dirname11(spec.stdoutPath), { recursive: true });
|
|
4038
4092
|
await writeFile5(spec.unitPath, renderSystemdUnit(spec), "utf8");
|
|
4039
4093
|
await runSystemctl(["--user", "daemon-reload"]);
|
|
4040
4094
|
await runSystemctl(["--user", "enable", "--now", spec.unitName]);
|
|
@@ -4156,33 +4210,33 @@ async function readServiceStatus(context) {
|
|
|
4156
4210
|
throw new Error("service_status_not_supported_on_this_platform");
|
|
4157
4211
|
}
|
|
4158
4212
|
function buildLaunchdSpec(context) {
|
|
4159
|
-
const stateDir =
|
|
4213
|
+
const stateDir = join11(homedir8(), ".claude-codex-wechat");
|
|
4160
4214
|
return {
|
|
4161
4215
|
label: "com.claude-codex-wechat",
|
|
4162
|
-
plistPath:
|
|
4216
|
+
plistPath: join11(homedir8(), "Library", "LaunchAgents", "com.claude-codex-wechat.plist"),
|
|
4163
4217
|
programArgs: [context.nodePath ?? process.execPath, context.cliEntrypointPath, "__daemon"],
|
|
4164
|
-
workingDirectory:
|
|
4165
|
-
stdoutPath:
|
|
4166
|
-
stderrPath:
|
|
4218
|
+
workingDirectory: homedir8(),
|
|
4219
|
+
stdoutPath: join11(stateDir, "logs", "service.stdout.log"),
|
|
4220
|
+
stderrPath: join11(stateDir, "logs", "service.stderr.log"),
|
|
4167
4221
|
environment: buildServiceEnvironment(context)
|
|
4168
4222
|
};
|
|
4169
4223
|
}
|
|
4170
4224
|
function buildSystemdUserSpec(context) {
|
|
4171
|
-
const stateDir =
|
|
4225
|
+
const stateDir = join11(homedir8(), ".claude-codex-wechat");
|
|
4172
4226
|
return {
|
|
4173
4227
|
unitName: "claude-codex-wechat.service",
|
|
4174
|
-
unitPath:
|
|
4228
|
+
unitPath: join11(homedir8(), ".config", "systemd", "user", "claude-codex-wechat.service"),
|
|
4175
4229
|
execStart: [context.nodePath ?? process.execPath, context.cliEntrypointPath, "__daemon"],
|
|
4176
|
-
workingDirectory:
|
|
4177
|
-
stdoutPath:
|
|
4178
|
-
stderrPath:
|
|
4230
|
+
workingDirectory: homedir8(),
|
|
4231
|
+
stdoutPath: join11(stateDir, "logs", "service.stdout.log"),
|
|
4232
|
+
stderrPath: join11(stateDir, "logs", "service.stderr.log"),
|
|
4179
4233
|
environment: buildServiceEnvironment(context)
|
|
4180
4234
|
};
|
|
4181
4235
|
}
|
|
4182
4236
|
function buildServiceEnvironment(context) {
|
|
4183
4237
|
const env = {
|
|
4184
4238
|
PATH: process.env.PATH ?? "",
|
|
4185
|
-
HOME: process.env.HOME ??
|
|
4239
|
+
HOME: process.env.HOME ?? homedir8()
|
|
4186
4240
|
};
|
|
4187
4241
|
const configPath = context.configPath ?? process.env.BRIDGE_CONFIG;
|
|
4188
4242
|
const port = context.port ?? Number(process.env.BRIDGE_PORT ?? 8787);
|
|
@@ -4304,8 +4358,8 @@ async function tailFiles(paths) {
|
|
|
4304
4358
|
child.stdout.on("data", (chunk) => {
|
|
4305
4359
|
process.stdout.write(String(chunk));
|
|
4306
4360
|
});
|
|
4307
|
-
await new Promise((
|
|
4308
|
-
child.on("exit", () =>
|
|
4361
|
+
await new Promise((resolve2, reject) => {
|
|
4362
|
+
child.on("exit", () => resolve2());
|
|
4309
4363
|
child.on("error", reject);
|
|
4310
4364
|
}).finally(() => {
|
|
4311
4365
|
for (const reader of readers) reader.close();
|
|
@@ -4320,8 +4374,8 @@ function fileSizeHint(path) {
|
|
|
4320
4374
|
}
|
|
4321
4375
|
|
|
4322
4376
|
// src/cli.ts
|
|
4323
|
-
var here =
|
|
4324
|
-
var webRoot =
|
|
4377
|
+
var here = dirname12(fileURLToPath(import.meta.url));
|
|
4378
|
+
var webRoot = join12(here, "..", "web");
|
|
4325
4379
|
async function main() {
|
|
4326
4380
|
const command = process.argv[2];
|
|
4327
4381
|
switch (command) {
|
|
@@ -4414,7 +4468,7 @@ async function cmdDaemon() {
|
|
|
4414
4468
|
}
|
|
4415
4469
|
function cmdInit() {
|
|
4416
4470
|
const configPath = process.env.BRIDGE_CONFIG ?? defaultConfigPath();
|
|
4417
|
-
const dir =
|
|
4471
|
+
const dir = dirname12(configPath);
|
|
4418
4472
|
mkdirSync6(dir, { recursive: true });
|
|
4419
4473
|
if (existsSync9(configPath)) {
|
|
4420
4474
|
console.log(`\u914D\u7F6E\u5DF2\u5B58\u5728\uFF0C\u672A\u8986\u76D6: ${configPath}`);
|