cli-wechat-bridge 1.1.0 → 1.1.2
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 +33 -2
- package/bin/_run-entry.mjs +24 -0
- package/dist/bridge/bridge-adapters.claude.js +18 -0
- package/dist/bridge/bridge-adapters.codex.js +25 -0
- package/dist/bridge/bridge-adapters.opencode.js +17 -0
- package/dist/bridge/bridge-adapters.shared.js +38 -44
- package/dist/bridge/bridge-final-reply.js +8 -4
- package/dist/bridge/bridge-process-reaper.js +58 -3
- package/dist/bridge/bridge-state.js +14 -6
- package/dist/bridge/bridge-utils.js +62 -7
- package/dist/bridge/claude-hook.js +7 -0
- package/dist/bridge/claude-hooks.js +10 -2
- package/dist/bridge/wechat-bridge.js +17 -1
- package/dist/commands/check-update.js +15 -4
- package/dist/companion/local-companion-start.js +5 -0
- package/dist/daemon/wechat-daemon.js +15 -2
- package/dist/i18n/messages-en.js +89 -2
- package/dist/i18n/messages-zh.js +89 -2
- package/dist/utils/doctor.js +619 -0
- package/dist/utils/version-checker.js +77 -44
- package/dist/wechat/channel-config.js +28 -0
- package/dist/wechat/wechat-transport.js +49 -13
- package/package.json +3 -1
- package/scripts/ensure-node-pty-permissions.mjs +73 -0
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { WECHAT_OUTBOUND_ATTACHMENT_DENY_MESSAGE, containsWechatOutboundAttachmentPath, isHighRiskShellCommand, isWechatOutboundAttachmentWriteCommand, normalizeOutput, truncatePreview, } from "./bridge-utils.js";
|
|
1
|
+
import { WECHAT_OUTBOUND_ATTACHMENT_DENY_MESSAGE, containsWechatOutboundAttachmentPath, isHighRiskShellCommand, isStrictApprovalModeEnabled, isWechatOutboundAttachmentWriteCommand, normalizeOutput, truncatePreview, } from "./bridge-utils.js";
|
|
2
2
|
export const CLAUDE_WECHAT_OUTBOUND_ATTACHMENT_DENY_MESSAGE = WECHAT_OUTBOUND_ATTACHMENT_DENY_MESSAGE;
|
|
3
3
|
const FILE_MUTATION_TOOL_NAMES = new Set([
|
|
4
4
|
"Write",
|
|
@@ -88,6 +88,11 @@ export function buildClaudeHookScript(params) {
|
|
|
88
88
|
return [
|
|
89
89
|
"@echo off",
|
|
90
90
|
"setlocal",
|
|
91
|
+
// The script is written as UTF-8, but cmd.exe decodes batch lines with
|
|
92
|
+
// the active OEM code page (GBK on zh-CN systems). Paths below may
|
|
93
|
+
// contain non-ASCII characters (e.g. a Chinese Windows user name), so
|
|
94
|
+
// switch to UTF-8 before any line that embeds a path.
|
|
95
|
+
"chcp 65001>nul",
|
|
91
96
|
`set "CLAUDE_WECHAT_HOOK_PORT=${params.hookPort}"`,
|
|
92
97
|
`set "CLAUDE_WECHAT_HOOK_TOKEN=${params.hookToken}"`,
|
|
93
98
|
`${command} ${stderrRedirect}`,
|
|
@@ -205,7 +210,10 @@ export function getClaudeWechatOutboundAttachmentDenyMessage(payload) {
|
|
|
205
210
|
}
|
|
206
211
|
return null;
|
|
207
212
|
}
|
|
208
|
-
export function getClaudePermissionAutoResponse(payload) {
|
|
213
|
+
export function getClaudePermissionAutoResponse(payload, env = process.env) {
|
|
214
|
+
if (isStrictApprovalModeEnabled(env)) {
|
|
215
|
+
return null;
|
|
216
|
+
}
|
|
209
217
|
const toolName = typeof payload.tool_name === "string" ? payload.tool_name.trim() : "";
|
|
210
218
|
if (!toolName) {
|
|
211
219
|
return null;
|
|
@@ -242,6 +242,11 @@ function printUsageAndExit() {
|
|
|
242
242
|
process.exit(0);
|
|
243
243
|
}
|
|
244
244
|
async function main() {
|
|
245
|
+
if (process.argv.includes("--doctor")) {
|
|
246
|
+
const { runDoctorCheck } = await import("../utils/doctor.js");
|
|
247
|
+
await runDoctorCheck(process.argv.slice(2), { mode: "bridge" });
|
|
248
|
+
process.exit(0);
|
|
249
|
+
}
|
|
245
250
|
const options = parseCliArgs(process.argv.slice(2));
|
|
246
251
|
const daemonEndpoint = readDaemonEndpoint();
|
|
247
252
|
if (daemonEndpoint && await isDaemonEndpointAlive(daemonEndpoint, { timeoutMs: 500 })) {
|
|
@@ -261,7 +266,9 @@ async function main() {
|
|
|
261
266
|
}
|
|
262
267
|
const transport = new WeChatTransport({ log, logError });
|
|
263
268
|
// 非阻塞地检查更新(不影响启动速度,也避免首次登录时打断二维码输出)
|
|
264
|
-
|
|
269
|
+
// unref:不能让这个延迟检查把 event loop 挂活(如 --doctor 或快速退出场景),
|
|
270
|
+
// 否则会与强制退出 teardown 竞态。
|
|
271
|
+
const updateCheckTimer = setTimeout(async () => {
|
|
265
272
|
try {
|
|
266
273
|
const versionInfo = await checkForUpdate();
|
|
267
274
|
if (versionInfo?.hasUpdate) {
|
|
@@ -272,6 +279,7 @@ async function main() {
|
|
|
272
279
|
// 静默失败,不影响正常使用
|
|
273
280
|
}
|
|
274
281
|
}, 3000); // 延迟3秒,确保不影响启动
|
|
282
|
+
updateCheckTimer.unref?.();
|
|
275
283
|
const stateStore = new BridgeStateStore({
|
|
276
284
|
...options,
|
|
277
285
|
authorizedUserId: credentials.userId,
|
|
@@ -333,6 +341,7 @@ async function main() {
|
|
|
333
341
|
let lastOutputAt = 0;
|
|
334
342
|
let lastHeartbeatAt = 0;
|
|
335
343
|
let consecutivePollFailures = 0;
|
|
344
|
+
let backlogNoticeSent = false;
|
|
336
345
|
const queueWechatTextAction = (action) => {
|
|
337
346
|
const run = textSendChain.then(action);
|
|
338
347
|
textSendChain = run.then(() => undefined, () => undefined);
|
|
@@ -656,6 +665,13 @@ async function main() {
|
|
|
656
665
|
if (pollResult.ignoredBacklogCount > 0) {
|
|
657
666
|
stateStore.incrementIgnoredBacklog(pollResult.ignoredBacklogCount);
|
|
658
667
|
stateStore.appendLog(`ignored_startup_backlog: count=${pollResult.ignoredBacklogCount}`);
|
|
668
|
+
if (!backlogNoticeSent) {
|
|
669
|
+
backlogNoticeSent = true;
|
|
670
|
+
await queueWechatMessage(stateStore.getState().authorizedUserId, t("bridge.backlogIgnored", {
|
|
671
|
+
count: pollResult.ignoredBacklogCount,
|
|
672
|
+
graceSeconds: Math.round(MESSAGE_START_GRACE_MS / 1000),
|
|
673
|
+
}), "notice");
|
|
674
|
+
}
|
|
659
675
|
}
|
|
660
676
|
for (const message of pollResult.messages) {
|
|
661
677
|
if (!ensureRuntimeOwnership()) {
|
|
@@ -7,12 +7,23 @@ async function main() {
|
|
|
7
7
|
console.log(`Checking for updates...`);
|
|
8
8
|
const versionInfo = await checkForUpdate(true); // 强制检查
|
|
9
9
|
if (!versionInfo) {
|
|
10
|
-
console.
|
|
11
|
-
|
|
10
|
+
console.error(`ERROR: Unable to check for updates.`);
|
|
11
|
+
console.error(`Could not read the latest version from the npm registry or GitHub.`);
|
|
12
|
+
console.error(`Possible causes:`);
|
|
13
|
+
console.error(` - No network connection`);
|
|
14
|
+
console.error(` - A proxy is required but not configured (set HTTP_PROXY/HTTPS_PROXY and NODE_USE_ENV_PROXY=1)`);
|
|
15
|
+
console.error(` - The npm registry or GitHub API are temporarily unavailable`);
|
|
16
|
+
console.error(`\nCheck the latest release manually:`);
|
|
17
|
+
console.error(` https://github.com/UNLINEARITY/CLI-WeChat-Bridge/releases`);
|
|
18
|
+
// 不使用 process.exit():强制退出会打断 fetch 底层 handle 的关闭流程,
|
|
19
|
+
// 在 Windows + Node 24 上触发 libuv 的 UV_HANDLE_CLOSING 断言崩溃。
|
|
20
|
+
// 设置 exitCode 后让事件循环自然退出。
|
|
21
|
+
process.exitCode = 1;
|
|
22
|
+
return;
|
|
12
23
|
}
|
|
13
24
|
if (!versionInfo.hasUpdate) {
|
|
14
25
|
console.log(`OK: Already up to date (v${versionInfo.latest})`);
|
|
15
|
-
|
|
26
|
+
return;
|
|
16
27
|
}
|
|
17
28
|
console.log(`[New Version Available] v${versionInfo.latest}`);
|
|
18
29
|
console.log(`Current version: v${versionInfo.current}\n`);
|
|
@@ -26,5 +37,5 @@ async function main() {
|
|
|
26
37
|
}
|
|
27
38
|
main().catch((error) => {
|
|
28
39
|
console.error(`Error checking for updates: ${error.message}`);
|
|
29
|
-
process.
|
|
40
|
+
process.exitCode = 1;
|
|
30
41
|
});
|
|
@@ -283,6 +283,11 @@ function startBridgeInBackground(options) {
|
|
|
283
283
|
stdio: "ignore",
|
|
284
284
|
windowsHide: true,
|
|
285
285
|
});
|
|
286
|
+
// A detached background spawn must still handle its own 'error' event
|
|
287
|
+
// (e.g. ENOENT), otherwise it surfaces as an unhandled exception.
|
|
288
|
+
child.on("error", () => {
|
|
289
|
+
/* best effort: background bridge failed to detach */
|
|
290
|
+
});
|
|
286
291
|
child.unref();
|
|
287
292
|
}
|
|
288
293
|
async function waitForEndpoint(cwd, adapter, timeoutMs) {
|
|
@@ -13,7 +13,7 @@ import { readBridgeLockFile, } from "../bridge/bridge-state.js";
|
|
|
13
13
|
import { getProcessRecordByPid, isWechatDaemonCommandLine, killProcessTreeSync, listWechatDaemonProcesses, reapOrphanedOpencodeProcesses, reapPeerBridgeProcesses, } from "../bridge/bridge-process-reaper.js";
|
|
14
14
|
import { buildOneTimeCode, buildWechatInboundPrompt, formatApprovalMessage, formatDuration, formatMirroredUserInputMessage, formatPendingApprovalReminder, formatPendingUserInputReminder, formatSessionSwitchMessage, formatTaskFailedMessage, formatUserInputRequestMessage, MESSAGE_START_GRACE_MS, nowIso, OutputBatcher, parsePendingUserInputAnswerCommand, parseWechatControlCommand, truncatePreview, } from "../bridge/bridge-utils.js";
|
|
15
15
|
import { formatUserFacingBridgeFatalError, formatUserFacingInboundError, formatWechatContextTokenStaleLogEntry, formatWechatSendFailureLogEntry, isRetryableWechatSendError, shouldForwardBridgeEventToWechat, } from "../bridge/wechat-bridge.js";
|
|
16
|
-
import { BRIDGE_LOCK_FILE, BRIDGE_LOG_FILE, ensureChannelDataDir, migrateLegacyChannelFiles, } from "../wechat/channel-config.js";
|
|
16
|
+
import { BRIDGE_LOCK_FILE, BRIDGE_LOG_FILE, appendBoundedLog, ensureChannelDataDir, migrateLegacyChannelFiles, } from "../wechat/channel-config.js";
|
|
17
17
|
import { ensureWechatCredentials } from "../wechat/setup.js";
|
|
18
18
|
import { formatBindCommandUsage, formatBindingsListMessage, isBindCommandPrefix, listBindings, loadEmojiBindings, parseEmojiBindingsCommand, removeBinding, resolveEmojiCommand, setBinding, } from "./emoji-bindings.js";
|
|
19
19
|
import { classifyWechatTransportError, DEFAULT_LONG_POLL_TIMEOUT_MS, describeWechatTransportError, isWechatContextTokenStaleError, WeChatTransport, } from "../wechat/wechat-transport.js";
|
|
@@ -45,7 +45,7 @@ function logError(message) {
|
|
|
45
45
|
}
|
|
46
46
|
function appendDaemonLog(message) {
|
|
47
47
|
ensureChannelDataDir();
|
|
48
|
-
|
|
48
|
+
appendBoundedLog(BRIDGE_LOG_FILE, `[${new Date().toISOString()}] daemon: ${message}\n`);
|
|
49
49
|
}
|
|
50
50
|
function computePollRetryDelayMs(consecutiveFailures) {
|
|
51
51
|
const normalizedFailures = Math.max(1, consecutiveFailures);
|
|
@@ -444,6 +444,7 @@ class WechatDaemon {
|
|
|
444
444
|
slots = new Map();
|
|
445
445
|
startedAt = new Date().toISOString();
|
|
446
446
|
bridgeStartedAtMs = Date.now();
|
|
447
|
+
backlogNoticeSent = false;
|
|
447
448
|
activeAdapter = null;
|
|
448
449
|
takenOverAdapter;
|
|
449
450
|
textSendChain = Promise.resolve();
|
|
@@ -582,6 +583,13 @@ class WechatDaemon {
|
|
|
582
583
|
}
|
|
583
584
|
if (pollResult.ignoredBacklogCount > 0) {
|
|
584
585
|
appendDaemonLog(`ignored_startup_backlog: count=${pollResult.ignoredBacklogCount}`);
|
|
586
|
+
if (!this.backlogNoticeSent) {
|
|
587
|
+
this.backlogNoticeSent = true;
|
|
588
|
+
await this.queueWechatMessage(this.authorizedUserId, t("bridge.backlogIgnored", {
|
|
589
|
+
count: pollResult.ignoredBacklogCount,
|
|
590
|
+
graceSeconds: Math.round(MESSAGE_START_GRACE_MS / 1000),
|
|
591
|
+
}), "notice");
|
|
592
|
+
}
|
|
585
593
|
}
|
|
586
594
|
for (const message of pollResult.messages) {
|
|
587
595
|
try {
|
|
@@ -1580,6 +1588,11 @@ export async function runDaemon(options) {
|
|
|
1580
1588
|
await daemon.runPollLoop();
|
|
1581
1589
|
}
|
|
1582
1590
|
export async function main(argv = process.argv.slice(2)) {
|
|
1591
|
+
if (argv.includes("--doctor")) {
|
|
1592
|
+
const { runDoctorCheck } = await import("../utils/doctor.js");
|
|
1593
|
+
await runDoctorCheck(argv, { mode: "daemon" });
|
|
1594
|
+
process.exit(0);
|
|
1595
|
+
}
|
|
1583
1596
|
try {
|
|
1584
1597
|
await runDaemon(parseDaemonCliArgs(argv));
|
|
1585
1598
|
}
|
package/dist/i18n/messages-en.js
CHANGED
|
@@ -9,6 +9,7 @@ export const messages = {
|
|
|
9
9
|
"bridge.status.busy": "{adapter} is still working on the previous request. Wait for it to finish or send /interrupt.",
|
|
10
10
|
"bridge.newSession.done": "New session started.",
|
|
11
11
|
"bridge.fatalError": "Bridge error: {message}",
|
|
12
|
+
"bridge.backlogIgnored": "Ignored {count} backlog message(s) sent before the bridge started (they will not be processed).\nIf you actually sent them after the bridge started, your system clock is likely skewed by more than {graceSeconds}s — sync the clock and restart the bridge.",
|
|
12
13
|
// === Daemon welcome & control ===
|
|
13
14
|
"daemon.welcome": "WeChat Daemon ready.\nCWD: {cwd}\nActive: {adapter}\n\nCommands: /claude, /codex, /opencode, /stop, /confirm, /deny, /status\n{bindings}\n\nManage: /bind [emoji] cmd, /unbind [emoji], /bindings",
|
|
14
15
|
"daemon.noActiveAdapter": "No active terminal is selected. Send /codex, /claude, or /opencode to start one.",
|
|
@@ -26,16 +27,17 @@ export const messages = {
|
|
|
26
27
|
"hook.healthCheck.logHint": "\nCheck: {logPath}",
|
|
27
28
|
"hook.healthCheck.fixes": "\nCommon fixes:\n- Ensure Node.js >= 22.6.0: node --version\n- Reinstall: npm install -g cli-wechat-bridge@latest\n- Check firewall: allow localhost TCP connections",
|
|
28
29
|
// === PTY fallback ===
|
|
29
|
-
"pty.fallback.warning": "[Warning] PTY unavailable
|
|
30
|
+
"pty.fallback.warning": "[Warning] PTY unavailable — using fallback mode (TERM=dumb).\nCore features (messaging, approvals, session management) work normally; terminal rendering (colors, progress bars) may be degraded.\nFix: Linux install build-essential python3 | Windows run npm rebuild node-pty | macOS run xcode-select --install\nRun --doctor for detailed environment info.\n",
|
|
30
31
|
// === Spawn diagnostic ===
|
|
31
32
|
"spawn.diagnostic.title": "Failed to start CLI process: {target}\nError: {error}",
|
|
32
33
|
"spawn.diagnostic.fixesHeader": "\nPossible fixes:",
|
|
33
34
|
"spawn.diagnostic.nodePty": "- The node-pty native module is incompatible with your Node.js version.\n- Run: npm rebuild node-pty\n- Or reinstall: npm install -g cli-wechat-bridge@latest",
|
|
34
35
|
"spawn.diagnostic.xcode": "- Ensure Xcode CLI tools are installed: xcode-select --install",
|
|
36
|
+
"spawn.diagnostic.linuxBuildTools": "- Linux requires build tools for node-pty: sudo apt install build-essential python3\n- Then reinstall: npm install -g cli-wechat-bridge@latest",
|
|
35
37
|
"spawn.diagnostic.notFound": "- The command \"{target}\" was not found on PATH.\n- Verify it is installed and accessible from your terminal.",
|
|
36
38
|
"spawn.diagnostic.generic": "- Reinstall: npm install -g cli-wechat-bridge@latest",
|
|
37
39
|
"spawn.diagnostic.nodeVersion": "- Ensure Node.js >= 22.6.0: node --version",
|
|
38
|
-
"spawn.diagnostic.
|
|
40
|
+
"spawn.diagnostic.winFull": "- Ensure Windows 10 build 18309+ (run 'winver' to check)\n- Install Visual C++ Redistributable: https://aka.ms/vs/17/release/vc_redist.x64.exe\n- Run: npm rebuild node-pty\n- Try running as Administrator",
|
|
39
41
|
// === Emoji bindings ===
|
|
40
42
|
"binding.usage": "Invalid format.\nUsage: /bind [emoji] command\nExample: /bind 🚀 deploy --prod",
|
|
41
43
|
"binding.bound": "Bound {emoji} → {command}",
|
|
@@ -45,4 +47,89 @@ export const messages = {
|
|
|
45
47
|
"binding.listHeader": "Emoji bindings:",
|
|
46
48
|
// === Version checker ===
|
|
47
49
|
"update.available": "[Update Available] Version {latest} is available (current: {current}).\nRun: npm install -g cli-wechat-bridge@latest",
|
|
50
|
+
// === Doctor ===
|
|
51
|
+
"doctor.title": "CLI WeChat Bridge Doctor",
|
|
52
|
+
"doctor.section.environment": "Environment",
|
|
53
|
+
"doctor.section.adapterCli": "Adapter commands",
|
|
54
|
+
"doctor.section.data": "Data",
|
|
55
|
+
"doctor.section.runtime": "Runtime",
|
|
56
|
+
"doctor.label.node": "Node.js",
|
|
57
|
+
"doctor.label.platform": "Platform",
|
|
58
|
+
"doctor.label.winBuild": "Windows build",
|
|
59
|
+
"doctor.label.codePage": "Console code page",
|
|
60
|
+
"doctor.codePage.nonAsciiWarning": "{codePage} (not UTF-8 and the data directory path contains non-ASCII characters; hook scripts from older versions break in this environment — upgrade to the latest release)",
|
|
61
|
+
"doctor.label.connectivity": "WeChat connectivity",
|
|
62
|
+
"doctor.connectivity.ok": "reachable ({baseUrl})",
|
|
63
|
+
"doctor.connectivity.unreachable": "cannot reach {baseUrl} (check network and firewall)",
|
|
64
|
+
"doctor.connectivity.proxyHint": "cannot reach {baseUrl}. Proxy {proxy} detected, but Node fetch ignores proxy environment variables by default; if your network requires the proxy, set NODE_USE_ENV_PROXY=1 and retry.",
|
|
65
|
+
"doctor.label.clock": "System clock",
|
|
66
|
+
"doctor.clock.ok": "~{skewSeconds}s skew vs server time",
|
|
67
|
+
"doctor.clock.skewWarning": "~{skewSeconds}s skew vs server time (large skew makes the bridge treat fresh messages as pre-start backlog — sync the system clock)",
|
|
68
|
+
"doctor.label.nodePty": "node-pty",
|
|
69
|
+
"doctor.label.shell": "shell",
|
|
70
|
+
"doctor.label.dataDir": "Data dir",
|
|
71
|
+
"doctor.label.credentials": "Credentials",
|
|
72
|
+
"doctor.label.mode": "Mode",
|
|
73
|
+
"doctor.label.cwd": "Cwd",
|
|
74
|
+
"doctor.label.adapter": "Adapter",
|
|
75
|
+
"doctor.label.daemon": "Daemon",
|
|
76
|
+
"doctor.label.bridgeLock": "Bridge lock",
|
|
77
|
+
"doctor.label.workspaceEndpoint": "Workspace endpoint ({adapter})",
|
|
78
|
+
"doctor.label.legacyEndpoint": "Legacy endpoint",
|
|
79
|
+
"doctor.winBuildConpty": "(ConPTY requires build 18309+)",
|
|
80
|
+
"doctor.nodePty.loaded": "loaded",
|
|
81
|
+
"doctor.nodePty.fix": "Fix: {fix}",
|
|
82
|
+
"doctor.adapterCli.shellNative": "uses the local shell; no other adapter CLI is checked.",
|
|
83
|
+
"doctor.adapterCli.notFound": "not found{optional}",
|
|
84
|
+
"doctor.dataDirMissing": "(will be created on first run)",
|
|
85
|
+
"doctor.credentialsFound": "found",
|
|
86
|
+
"doctor.credentialsMissing": "not found (run wechat-setup first)",
|
|
87
|
+
"doctor.mode.bridge": "standalone bridge",
|
|
88
|
+
"doctor.mode.generic": "generic",
|
|
89
|
+
"doctor.notSpecified": "(not specified)",
|
|
90
|
+
"doctor.none": "none",
|
|
91
|
+
"doctor.unknown": "unknown",
|
|
92
|
+
"doctor.yes": "yes",
|
|
93
|
+
"doctor.no": "no",
|
|
94
|
+
"doctor.optional": "(optional)",
|
|
95
|
+
"doctor.invalidShape": "unexpected file shape",
|
|
96
|
+
"doctor.file": "File: {file}",
|
|
97
|
+
"doctor.issue": "Issue: {issue}.",
|
|
98
|
+
"doctor.daemon.invalid": "invalid endpoint file ({error})",
|
|
99
|
+
"doctor.daemon.stale": "stale endpoint pid={pid} cwd={cwd} port={port}",
|
|
100
|
+
"doctor.daemon.staleStartup": "Startup: bridge and daemon startup paths will clear stale daemon endpoint state before continuing.",
|
|
101
|
+
"doctor.daemon.live": "live pid={pid} cwd={cwd} port={port}",
|
|
102
|
+
"doctor.daemon.liveBridgeStartup": "Startup: standalone bridge will refuse to start while this daemon is live.",
|
|
103
|
+
"doctor.daemon.liveDaemonStartup": "Startup: daemon startup will request shutdown of this daemon before binding.",
|
|
104
|
+
"doctor.lock.invalid": "invalid lock file ({error})",
|
|
105
|
+
"doctor.lock.stale": "stale",
|
|
106
|
+
"doctor.lock.staleDaemonStartup": "daemon startup will clear this stale single-bridge lock.",
|
|
107
|
+
"doctor.lock.staleBridgeStartup": "the next standalone bridge start will replace this stale lock.",
|
|
108
|
+
"doctor.lock.reclaimable": "reclaimable",
|
|
109
|
+
"doctor.lock.parent": "pid={pid} alive={alive}",
|
|
110
|
+
"doctor.lock.detail.pid": "pid",
|
|
111
|
+
"doctor.lock.detail.adapter": "adapter",
|
|
112
|
+
"doctor.lock.detail.cwd": "cwd",
|
|
113
|
+
"doctor.lock.detail.lifecycle": "lifecycle",
|
|
114
|
+
"doctor.lock.detail.startedAt": "started at",
|
|
115
|
+
"doctor.lock.detail.parent": "parent",
|
|
116
|
+
"doctor.lock.detail.startup": "startup",
|
|
117
|
+
"doctor.lock.detail.note": "note",
|
|
118
|
+
"doctor.lock.reclaimableDaemonStartup": "daemon startup will stop or clear this orphaned single bridge before continuing.",
|
|
119
|
+
"doctor.lock.reclaimableBridgeStartup": "standalone bridge startup can reclaim this companion-bound or legacy lock because its parent is gone.",
|
|
120
|
+
"doctor.lock.live": "live",
|
|
121
|
+
"doctor.lock.liveDaemonStartup": "daemon startup will stop this single bridge before continuing.",
|
|
122
|
+
"doctor.lock.liveBridgeStartup": "standalone bridge will fail with a lock conflict until this process exits.",
|
|
123
|
+
"doctor.lock.cwdDifferent": "lock cwd differs from requested cwd ({cwd}); single-bridge lock is global.",
|
|
124
|
+
"doctor.lock.adapterDifferent": "lock adapter={lockAdapter} differs from requested adapter={requestedAdapter}.",
|
|
125
|
+
"doctor.endpoint.legacySame": "same as adapter endpoint",
|
|
126
|
+
"doctor.endpoint.summary": "instance={instanceId} kind={kind} port={port} reachable={reachable}",
|
|
127
|
+
"doctor.endpoint.owner": "ownerPid={ownerPid} ownerAlive={ownerAlive} companionPid={companionPid} companionAlive={companionAlive} status={status}",
|
|
128
|
+
"doctor.endpoint.issue.kind": "endpoint kind={kind} differs from requested adapter={adapter}",
|
|
129
|
+
"doctor.endpoint.issue.cwd": "endpoint cwd differs from requested cwd ({cwd})",
|
|
130
|
+
"doctor.endpoint.issue.port": "endpoint port {port} is not reachable",
|
|
131
|
+
"doctor.endpoint.issue.ownerDead": "bridge owner pid={pid} is dead",
|
|
132
|
+
"doctor.endpoint.issue.companionDead": "companion pid={pid} is dead",
|
|
133
|
+
"doctor.endpoint.issue.workerStatus": "visible worker status={status}",
|
|
134
|
+
"doctor.endpoint.issue.instanceMismatch": "endpoint instanceId does not match bridge lock instanceId",
|
|
48
135
|
};
|
package/dist/i18n/messages-zh.js
CHANGED
|
@@ -9,6 +9,7 @@ export const messages = {
|
|
|
9
9
|
"bridge.status.busy": "{adapter} 仍在处理上一个请求。请等待完成或发送 /interrupt。",
|
|
10
10
|
"bridge.newSession.done": "已开始新会话。",
|
|
11
11
|
"bridge.fatalError": "桥接错误: {message}",
|
|
12
|
+
"bridge.backlogIgnored": "已忽略 {count} 条桥接启动前的积压消息(不会自动处理)。\n如果你是在桥接启动后才发送的消息却看到此提示,说明本机系统时钟可能不准(偏差超过 {graceSeconds} 秒),请校准时间后重启桥接。",
|
|
12
13
|
// === Daemon 欢迎 & 控制 ===
|
|
13
14
|
"daemon.welcome": "微信 Daemon 就绪。\n工作目录: {cwd}\n当前: {adapter}\n\n命令: /claude, /codex, /opencode, /stop, /confirm, /deny, /status\n{bindings}\n\n管理: /bind [表情] 命令, /unbind [表情], /bindings",
|
|
14
15
|
"daemon.noActiveAdapter": "未选择活跃终端。发送 /codex、/claude 或 /opencode 启动一个。",
|
|
@@ -26,16 +27,17 @@ export const messages = {
|
|
|
26
27
|
"hook.healthCheck.logHint": "\n查看日志: {logPath}",
|
|
27
28
|
"hook.healthCheck.fixes": "\n常见修复方法:\n- 确保 Node.js >= 22.6.0: node --version\n- 重新安装: npm install -g cli-wechat-bridge@latest\n- 检查防火墙: 允许 localhost TCP 连接",
|
|
28
29
|
// === PTY 回退 ===
|
|
29
|
-
"pty.fallback.warning": "[警告] PTY
|
|
30
|
+
"pty.fallback.warning": "[警告] PTY 不可用 — 已切换到回退模式(TERM=dumb)。\n核心功能(消息收发、审批、会话管理)正常工作;终端渲染(颜色、进度条)可能受限。\n修复方法: Linux 安装 build-essential python3 | Windows 运行 npm rebuild node-pty | macOS 运行 xcode-select --install\n运行 --doctor 查看详细环境信息。\n",
|
|
30
31
|
// === 启动诊断 ===
|
|
31
32
|
"spawn.diagnostic.title": "无法启动 CLI 进程: {target}\n错误: {error}",
|
|
32
33
|
"spawn.diagnostic.fixesHeader": "\n可能的修复方法:",
|
|
33
34
|
"spawn.diagnostic.nodePty": "- node-pty 原生模块与当前 Node.js 版本不兼容。\n- 运行: npm rebuild node-pty\n- 或重新安装: npm install -g cli-wechat-bridge@latest",
|
|
34
35
|
"spawn.diagnostic.xcode": "- 确保已安装 Xcode 命令行工具: xcode-select --install",
|
|
36
|
+
"spawn.diagnostic.linuxBuildTools": "- Linux 需要编译工具来构建 node-pty: sudo apt install build-essential python3\n- 然后重新安装: npm install -g cli-wechat-bridge@latest",
|
|
35
37
|
"spawn.diagnostic.notFound": "- 命令 \"{target}\" 未在 PATH 中找到。\n- 请确认已安装并可从终端访问。",
|
|
36
38
|
"spawn.diagnostic.generic": "- 重新安装: npm install -g cli-wechat-bridge@latest",
|
|
37
39
|
"spawn.diagnostic.nodeVersion": "- 确保 Node.js >= 22.6.0: node --version",
|
|
38
|
-
"spawn.diagnostic.
|
|
40
|
+
"spawn.diagnostic.winFull": "- 确保 Windows 10 build 18309+(运行 winver 查看)\n- 安装 Visual C++ Redistributable: https://aka.ms/vs/17/release/vc_redist.x64.exe\n- 运行: npm rebuild node-pty\n- 尝试以管理员身份运行",
|
|
39
41
|
// === Emoji 绑定 ===
|
|
40
42
|
"binding.usage": "格式错误。\n用法: /bind [表情] 命令\n示例: /bind 🚀 deploy --prod",
|
|
41
43
|
"binding.bound": "已绑定 {emoji} → {command}",
|
|
@@ -45,4 +47,89 @@ export const messages = {
|
|
|
45
47
|
"binding.listHeader": "Emoji 绑定列表:",
|
|
46
48
|
// === 版本检查 ===
|
|
47
49
|
"update.available": "[有新版本] 版本 {latest} 可用(当前: {current})。\n运行: npm install -g cli-wechat-bridge@latest",
|
|
50
|
+
// === Doctor ===
|
|
51
|
+
"doctor.title": "CLI WeChat Bridge 诊断",
|
|
52
|
+
"doctor.section.environment": "环境",
|
|
53
|
+
"doctor.section.adapterCli": "适配器命令",
|
|
54
|
+
"doctor.section.data": "数据",
|
|
55
|
+
"doctor.section.runtime": "运行时",
|
|
56
|
+
"doctor.label.node": "Node.js",
|
|
57
|
+
"doctor.label.platform": "平台",
|
|
58
|
+
"doctor.label.winBuild": "Windows build",
|
|
59
|
+
"doctor.label.codePage": "控制台代码页",
|
|
60
|
+
"doctor.codePage.nonAsciiWarning": "{codePage}(非 UTF-8,且数据目录路径含非 ASCII 字符;旧版本的 hook 脚本在此环境下会乱码失效,请升级到最新版本)",
|
|
61
|
+
"doctor.label.connectivity": "微信服务可达性",
|
|
62
|
+
"doctor.connectivity.ok": "可达 ({baseUrl})",
|
|
63
|
+
"doctor.connectivity.unreachable": "无法访问 {baseUrl}(检查网络与防火墙)",
|
|
64
|
+
"doctor.connectivity.proxyHint": "无法访问 {baseUrl}。检测到代理 {proxy},但 Node fetch 默认不使用代理环境变量;若必须经代理出网,请设置 NODE_USE_ENV_PROXY=1 后重试。",
|
|
65
|
+
"doctor.label.clock": "系统时钟",
|
|
66
|
+
"doctor.clock.ok": "与服务器时间偏差约 {skewSeconds}s",
|
|
67
|
+
"doctor.clock.skewWarning": "与服务器时间偏差约 {skewSeconds}s(偏差过大会导致桥接启动后的消息被当作积压忽略,请校准系统时间)",
|
|
68
|
+
"doctor.label.nodePty": "node-pty",
|
|
69
|
+
"doctor.label.shell": "shell",
|
|
70
|
+
"doctor.label.dataDir": "数据目录",
|
|
71
|
+
"doctor.label.credentials": "凭据",
|
|
72
|
+
"doctor.label.mode": "模式",
|
|
73
|
+
"doctor.label.cwd": "目录",
|
|
74
|
+
"doctor.label.adapter": "适配器",
|
|
75
|
+
"doctor.label.daemon": "守护进程",
|
|
76
|
+
"doctor.label.bridgeLock": "桥接锁",
|
|
77
|
+
"doctor.label.workspaceEndpoint": "工作区端点 ({adapter})",
|
|
78
|
+
"doctor.label.legacyEndpoint": "旧端点",
|
|
79
|
+
"doctor.winBuildConpty": "(ConPTY 需要 build 18309+)",
|
|
80
|
+
"doctor.nodePty.loaded": "已加载",
|
|
81
|
+
"doctor.nodePty.fix": "修复: {fix}",
|
|
82
|
+
"doctor.adapterCli.shellNative": "使用本机 shell,无需检查其它适配器 CLI。",
|
|
83
|
+
"doctor.adapterCli.notFound": "未找到{optional}",
|
|
84
|
+
"doctor.dataDirMissing": "(首次运行会创建)",
|
|
85
|
+
"doctor.credentialsFound": "已找到",
|
|
86
|
+
"doctor.credentialsMissing": "未找到 (先运行 wechat-setup)",
|
|
87
|
+
"doctor.mode.bridge": "独立桥接",
|
|
88
|
+
"doctor.mode.generic": "通用",
|
|
89
|
+
"doctor.notSpecified": "(未指定)",
|
|
90
|
+
"doctor.none": "无",
|
|
91
|
+
"doctor.unknown": "未知",
|
|
92
|
+
"doctor.yes": "是",
|
|
93
|
+
"doctor.no": "否",
|
|
94
|
+
"doctor.optional": "(可选)",
|
|
95
|
+
"doctor.invalidShape": "文件结构不符合预期",
|
|
96
|
+
"doctor.file": "文件: {file}",
|
|
97
|
+
"doctor.issue": "问题: {issue}。",
|
|
98
|
+
"doctor.daemon.invalid": "端点文件无效 ({error})",
|
|
99
|
+
"doctor.daemon.stale": "过期端点 pid={pid} cwd={cwd} port={port}",
|
|
100
|
+
"doctor.daemon.staleStartup": "启动行为: 桥接和守护进程启动路径会先清理过期守护进程端点。",
|
|
101
|
+
"doctor.daemon.live": "运行中 pid={pid} cwd={cwd} port={port}",
|
|
102
|
+
"doctor.daemon.liveBridgeStartup": "启动行为: 守护进程运行中时,独立桥接会拒绝启动。",
|
|
103
|
+
"doctor.daemon.liveDaemonStartup": "启动行为: 守护进程启动会先请求关闭这个旧守护进程再绑定。",
|
|
104
|
+
"doctor.lock.invalid": "锁文件无效 ({error})",
|
|
105
|
+
"doctor.lock.stale": "过期",
|
|
106
|
+
"doctor.lock.staleDaemonStartup": "守护进程启动会清理这个过期的单桥接锁。",
|
|
107
|
+
"doctor.lock.staleBridgeStartup": "下次独立桥接启动会替换这个过期锁。",
|
|
108
|
+
"doctor.lock.reclaimable": "可回收",
|
|
109
|
+
"doctor.lock.parent": "pid={pid} alive={alive}",
|
|
110
|
+
"doctor.lock.detail.pid": "pid",
|
|
111
|
+
"doctor.lock.detail.adapter": "适配器",
|
|
112
|
+
"doctor.lock.detail.cwd": "目录",
|
|
113
|
+
"doctor.lock.detail.lifecycle": "生命周期",
|
|
114
|
+
"doctor.lock.detail.startedAt": "启动时间",
|
|
115
|
+
"doctor.lock.detail.parent": "父进程",
|
|
116
|
+
"doctor.lock.detail.startup": "启动行为",
|
|
117
|
+
"doctor.lock.detail.note": "注意",
|
|
118
|
+
"doctor.lock.reclaimableDaemonStartup": "守护进程启动会先停止或清理这个孤儿单桥接。",
|
|
119
|
+
"doctor.lock.reclaimableBridgeStartup": "独立桥接可回收这个父进程已退出的锁。",
|
|
120
|
+
"doctor.lock.live": "运行中",
|
|
121
|
+
"doctor.lock.liveDaemonStartup": "守护进程启动会先停止这个单桥接。",
|
|
122
|
+
"doctor.lock.liveBridgeStartup": "该进程退出前,独立桥接会因锁冲突启动失败。",
|
|
123
|
+
"doctor.lock.cwdDifferent": "锁 cwd 与请求 cwd 不同 ({cwd});单桥接锁是全局的。",
|
|
124
|
+
"doctor.lock.adapterDifferent": "锁 adapter={lockAdapter} 与请求 adapter={requestedAdapter} 不同。",
|
|
125
|
+
"doctor.endpoint.legacySame": "与适配器端点相同",
|
|
126
|
+
"doctor.endpoint.summary": "实例={instanceId} 类型={kind} 端口={port} 可达={reachable}",
|
|
127
|
+
"doctor.endpoint.owner": "ownerPid={ownerPid} ownerAlive={ownerAlive} companionPid={companionPid} companionAlive={companionAlive} 状态={status}",
|
|
128
|
+
"doctor.endpoint.issue.kind": "端点类型 {kind} 与请求适配器 {adapter} 不同",
|
|
129
|
+
"doctor.endpoint.issue.cwd": "端点 cwd 与请求 cwd 不同 ({cwd})",
|
|
130
|
+
"doctor.endpoint.issue.port": "端点端口 {port} 不可达",
|
|
131
|
+
"doctor.endpoint.issue.ownerDead": "桥接 owner pid={pid} 已退出",
|
|
132
|
+
"doctor.endpoint.issue.companionDead": "companion pid={pid} 已退出",
|
|
133
|
+
"doctor.endpoint.issue.workerStatus": "可见端 worker 状态={status}",
|
|
134
|
+
"doctor.endpoint.issue.instanceMismatch": "端点 instanceId 与桥接锁 instanceId 不一致",
|
|
48
135
|
};
|