@unlinearity/cli-wechat-bridge 1.1.5 → 1.2.0

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.
Files changed (89) hide show
  1. package/LICENSE.txt +661 -661
  2. package/README.md +434 -427
  3. package/bin/_run-entry.mjs +47 -9
  4. package/bin/wechat-check-update.mjs +6 -1
  5. package/bin/wechat-claude.mjs +6 -1
  6. package/bin/wechat-codex.mjs +6 -1
  7. package/bin/wechat-daemon.mjs +6 -1
  8. package/bin/wechat-opencode.mjs +6 -1
  9. package/bin/wechat-pi.mjs +6 -1
  10. package/bin/wechat-setup.mjs +6 -1
  11. package/bin/wecom-check-update.mjs +10 -0
  12. package/bin/wecom-claude.mjs +10 -0
  13. package/bin/wecom-codex.mjs +10 -0
  14. package/bin/wecom-daemon.mjs +10 -0
  15. package/bin/wecom-opencode.mjs +10 -0
  16. package/bin/wecom-pi.mjs +10 -0
  17. package/bin/wecom-setup.mjs +10 -0
  18. package/dist/bridge/adapter-control.js +58 -0
  19. package/dist/bridge/bridge-adapters.claude.js +163 -5
  20. package/dist/bridge/bridge-adapters.codex.js +331 -29
  21. package/dist/bridge/bridge-adapters.core.js +27 -2
  22. package/dist/bridge/bridge-adapters.opencode.js +156 -1
  23. package/dist/bridge/bridge-adapters.pi.js +46 -1
  24. package/dist/bridge/bridge-adapters.shared.js +8 -1
  25. package/dist/bridge/bridge-controller.js +6 -0
  26. package/dist/bridge/bridge-final-reply.js +8 -65
  27. package/dist/bridge/bridge-process-reaper.js +41 -2
  28. package/dist/bridge/bridge-state.js +17 -4
  29. package/dist/bridge/bridge-utils.js +10 -127
  30. package/dist/bridge/claude-hook.js +6 -0
  31. package/dist/bridge/claude-native-controls.js +133 -0
  32. package/dist/bridge/native-terminal-control.js +259 -0
  33. package/dist/bridge/wechat-bridge.js +520 -274
  34. package/dist/bridge/wechat-forwarding.js +8 -58
  35. package/dist/bridge/wechat-outbound-queue.js +8 -0
  36. package/dist/channels/wechat/bridge-final-reply.js +56 -0
  37. package/dist/channels/wechat/channel-message.js +24 -0
  38. package/dist/channels/wechat/index.js +11 -0
  39. package/dist/channels/wechat/wechat-channel-port.js +67 -0
  40. package/dist/channels/wechat/wechat-driver.js +191 -0
  41. package/dist/channels/wechat/wechat-forwarding.js +97 -0
  42. package/dist/channels/wechat/wechat-outbound-queue.js +118 -0
  43. package/dist/channels/wecom/bridge-final-reply.js +32 -0
  44. package/dist/channels/wecom/setup-cli.js +12 -0
  45. package/dist/channels/wecom/setup.js +182 -0
  46. package/dist/channels/wecom/wecom-channel-port.js +45 -0
  47. package/dist/channels/wecom/wecom-config.js +72 -0
  48. package/dist/channels/wecom/wecom-driver.js +106 -0
  49. package/dist/channels/wecom/wecom-media.js +147 -0
  50. package/dist/channels/wecom/wecom-message.js +149 -0
  51. package/dist/channels/wecom/wecom-transport.js +314 -0
  52. package/dist/commands/check-update.js +6 -0
  53. package/dist/companion/codex-remote-client.js +30 -3
  54. package/dist/companion/codex-visible-client-link.js +107 -0
  55. package/dist/companion/local-companion-link.js +6 -0
  56. package/dist/companion/local-companion-start.js +62 -11
  57. package/dist/companion/local-companion.js +25 -1
  58. package/dist/companion/opencode-tui-bridge-plugin.js +6 -0
  59. package/dist/companion/pi-tui-bridge-extension.js +62 -3
  60. package/dist/core/bridge-control.js +42 -0
  61. package/dist/core/bridge-defer.js +25 -0
  62. package/dist/core/bridge-event-forwarder.js +50 -0
  63. package/dist/core/bridge-message-router.js +32 -0
  64. package/dist/core/channel-driver.js +1 -0
  65. package/dist/core/channel-types.js +3 -0
  66. package/dist/core/conversation-routing.js +58 -0
  67. package/dist/core/direct-run.js +23 -0
  68. package/dist/core/inbound-message-claims.js +67 -0
  69. package/dist/core/outbound-target.js +32 -0
  70. package/dist/core/text-utils.js +69 -0
  71. package/dist/core/turn-coordinator.js +85 -0
  72. package/dist/daemon/daemon-link.js +9 -0
  73. package/dist/daemon/emoji-bindings.js +6 -0
  74. package/dist/daemon/wechat-daemon.js +827 -209
  75. package/dist/i18n/index.js +6 -0
  76. package/dist/i18n/messages-en.js +11 -3
  77. package/dist/i18n/messages-zh.js +11 -3
  78. package/dist/runtime/create-runtime-host.js +6 -0
  79. package/dist/runtime/legacy-adapter-runtime.js +15 -0
  80. package/dist/runtime/runtime-types.js +3 -0
  81. package/dist/utils/atomic-file.js +6 -0
  82. package/dist/utils/doctor.js +36 -10
  83. package/dist/utils/version-checker.js +17 -11
  84. package/dist/wechat/channel-config.js +6 -0
  85. package/dist/wechat/setup.js +119 -13
  86. package/dist/wechat/wechat-channel.js +6 -0
  87. package/dist/wechat/wechat-transport.js +169 -58
  88. package/package.json +14 -4
  89. package/scripts/ensure-node-pty-permissions.mjs +73 -73
@@ -1,27 +1,51 @@
1
1
  #!/usr/bin/env node
2
-
2
+ // SPDX-License-Identifier: AGPL-3.0-or-later
3
+ // Copyright (c) 2026 UNLINEARITY <unlinearity@gmail.com>
4
+ // Source: https://github.com/UNLINEARITY/CLI-WeChat-Bridge
5
+ // This file is part of CLI-WeChat-Bridge. Modifications and derivative works
6
+ // must be released under AGPL-3.0-or-later with full source code; see
7
+ // LICENSE.txt. Network services built on it must offer source to users.
3
8
  import { spawn } from "node:child_process";
4
9
  import path from "node:path";
5
10
  import { fileURLToPath } from "node:url";
6
11
 
7
12
  const BIN_DIR = path.dirname(fileURLToPath(import.meta.url));
8
13
  const PROJECT_DIR = path.resolve(BIN_DIR, "..");
9
- const MIN_NODE_MAJOR = 24;
14
+ export const MIN_NODE_VERSION = Object.freeze([22, 13, 0]);
15
+ export const MIN_NODE_VERSION_TEXT = MIN_NODE_VERSION.join(".");
16
+ export const MIN_PI_NODE_VERSION = Object.freeze([22, 19, 0]);
17
+ export const MIN_PI_NODE_VERSION_TEXT = MIN_PI_NODE_VERSION.join(".");
18
+
19
+ export function isSupportedNodeVersion(version) {
20
+ const match = /^v?(\d+)(?:\.(\d+))?(?:\.(\d+))?/.exec(version);
21
+ if (!match) {
22
+ return false;
23
+ }
24
+
25
+ const current = [Number(match[1]), Number(match[2] ?? 0), Number(match[3] ?? 0)];
26
+ for (let index = 0; index < MIN_NODE_VERSION.length; index += 1) {
27
+ if (current[index] !== MIN_NODE_VERSION[index]) {
28
+ return current[index] > MIN_NODE_VERSION[index];
29
+ }
30
+ }
31
+ return true;
32
+ }
10
33
 
11
- function ensureSupportedNodeVersion() {
34
+ function ensureSupportedNodeVersion(extraArgs = []) {
12
35
  if (process.env.CLI_BRIDGE_SKIP_NODE_CHECK === "1") {
13
36
  return;
14
37
  }
15
38
 
16
- const major = Number(process.versions.node.split(".")[0]);
17
- if (Number.isFinite(major) && major >= MIN_NODE_MAJOR) {
18
- return;
39
+ const piRequested = extraArgs.some((arg, index) => arg === "pi" && extraArgs[index - 1] === "--adapter");
40
+ if (isSupportedNodeVersion(process.versions.node)) {
41
+ if (!piRequested || compareNodeVersion(process.versions.node, MIN_PI_NODE_VERSION) >= 0) return;
19
42
  }
20
43
 
44
+ const minimumText = piRequested ? MIN_PI_NODE_VERSION_TEXT : MIN_NODE_VERSION_TEXT;
21
45
  process.stderr.write(
22
46
  [
23
- `[cli-wechat-bridge] Node.js >= ${MIN_NODE_MAJOR} is required, but you are running ${process.version}.`,
24
- `[cli-wechat-bridge] 需要 Node.js >= ${MIN_NODE_MAJOR},当前版本为 ${process.version}。`,
47
+ `[cli-wechat-bridge] Node.js >= ${minimumText} is required, but you are running ${process.version}.`,
48
+ `[cli-wechat-bridge] 需要 Node.js >= ${minimumText},当前版本为 ${process.version}。`,
25
49
  "Install the latest LTS from https://nodejs.org/ (or via nvm), then retry.",
26
50
  "Set CLI_BRIDGE_SKIP_NODE_CHECK=1 to bypass this check at your own risk.",
27
51
  "",
@@ -30,8 +54,22 @@ function ensureSupportedNodeVersion() {
30
54
  process.exit(1);
31
55
  }
32
56
 
57
+ function compareNodeVersion(version, minimum) {
58
+ const match = /^v?(\d+)(?:\.(\d+))?(?:\.(\d+))?/.exec(version);
59
+ if (!match) return -1;
60
+ const current = [Number(match[1]), Number(match[2] ?? 0), Number(match[3] ?? 0)];
61
+ for (let index = 0; index < minimum.length; index += 1) {
62
+ if (current[index] !== minimum[index]) return current[index] > minimum[index] ? 1 : -1;
63
+ }
64
+ return 0;
65
+ }
66
+
67
+ export function isSupportedPiNodeVersion(version) {
68
+ return compareNodeVersion(version, MIN_PI_NODE_VERSION) >= 0;
69
+ }
70
+
33
71
  export function runJsEntry(relativeEntryPath, extraArgs = []) {
34
- ensureSupportedNodeVersion();
72
+ ensureSupportedNodeVersion([...extraArgs, ...process.argv.slice(2)]);
35
73
  const entryPath = path.join(PROJECT_DIR, relativeEntryPath);
36
74
  const child = spawn(
37
75
  process.execPath,
@@ -1,5 +1,10 @@
1
1
  #!/usr/bin/env node
2
-
2
+ // SPDX-License-Identifier: AGPL-3.0-or-later
3
+ // Copyright (c) 2026 UNLINEARITY <unlinearity@gmail.com>
4
+ // Source: https://github.com/UNLINEARITY/CLI-WeChat-Bridge
5
+ // This file is part of CLI-WeChat-Bridge. Modifications and derivative works
6
+ // must be released under AGPL-3.0-or-later with full source code; see
7
+ // LICENSE.txt. Network services built on it must offer source to users.
3
8
  import { runJsEntry } from "./_run-entry.mjs";
4
9
 
5
10
  runJsEntry("dist/commands/check-update.js", []);
@@ -1,5 +1,10 @@
1
1
  #!/usr/bin/env node
2
-
2
+ // SPDX-License-Identifier: AGPL-3.0-or-later
3
+ // Copyright (c) 2026 UNLINEARITY <unlinearity@gmail.com>
4
+ // Source: https://github.com/UNLINEARITY/CLI-WeChat-Bridge
5
+ // This file is part of CLI-WeChat-Bridge. Modifications and derivative works
6
+ // must be released under AGPL-3.0-or-later with full source code; see
7
+ // LICENSE.txt. Network services built on it must offer source to users.
3
8
  import { runJsEntry } from "./_run-entry.mjs";
4
9
 
5
10
  runJsEntry("dist/companion/local-companion-start.js", ["--adapter", "claude"]);
@@ -1,5 +1,10 @@
1
1
  #!/usr/bin/env node
2
-
2
+ // SPDX-License-Identifier: AGPL-3.0-or-later
3
+ // Copyright (c) 2026 UNLINEARITY <unlinearity@gmail.com>
4
+ // Source: https://github.com/UNLINEARITY/CLI-WeChat-Bridge
5
+ // This file is part of CLI-WeChat-Bridge. Modifications and derivative works
6
+ // must be released under AGPL-3.0-or-later with full source code; see
7
+ // LICENSE.txt. Network services built on it must offer source to users.
3
8
  import { runJsEntry } from "./_run-entry.mjs";
4
9
 
5
10
  runJsEntry("dist/companion/local-companion-start.js", ["--adapter", "codex"]);
@@ -1,5 +1,10 @@
1
1
  #!/usr/bin/env node
2
-
2
+ // SPDX-License-Identifier: AGPL-3.0-or-later
3
+ // Copyright (c) 2026 UNLINEARITY <unlinearity@gmail.com>
4
+ // Source: https://github.com/UNLINEARITY/CLI-WeChat-Bridge
5
+ // This file is part of CLI-WeChat-Bridge. Modifications and derivative works
6
+ // must be released under AGPL-3.0-or-later with full source code; see
7
+ // LICENSE.txt. Network services built on it must offer source to users.
3
8
  import { runJsEntry } from "./_run-entry.mjs";
4
9
 
5
10
  runJsEntry("dist/daemon/wechat-daemon.js");
@@ -1,5 +1,10 @@
1
1
  #!/usr/bin/env node
2
-
2
+ // SPDX-License-Identifier: AGPL-3.0-or-later
3
+ // Copyright (c) 2026 UNLINEARITY <unlinearity@gmail.com>
4
+ // Source: https://github.com/UNLINEARITY/CLI-WeChat-Bridge
5
+ // This file is part of CLI-WeChat-Bridge. Modifications and derivative works
6
+ // must be released under AGPL-3.0-or-later with full source code; see
7
+ // LICENSE.txt. Network services built on it must offer source to users.
3
8
  import { runJsEntry } from "./_run-entry.mjs";
4
9
 
5
10
  runJsEntry("dist/companion/local-companion-start.js", ["--adapter", "opencode"]);
package/bin/wechat-pi.mjs CHANGED
@@ -1,5 +1,10 @@
1
1
  #!/usr/bin/env node
2
-
2
+ // SPDX-License-Identifier: AGPL-3.0-or-later
3
+ // Copyright (c) 2026 UNLINEARITY <unlinearity@gmail.com>
4
+ // Source: https://github.com/UNLINEARITY/CLI-WeChat-Bridge
5
+ // This file is part of CLI-WeChat-Bridge. Modifications and derivative works
6
+ // must be released under AGPL-3.0-or-later with full source code; see
7
+ // LICENSE.txt. Network services built on it must offer source to users.
3
8
  import { runJsEntry } from "./_run-entry.mjs";
4
9
 
5
10
  runJsEntry("dist/companion/local-companion-start.js", ["--adapter", "pi"]);
@@ -1,5 +1,10 @@
1
1
  #!/usr/bin/env node
2
-
2
+ // SPDX-License-Identifier: AGPL-3.0-or-later
3
+ // Copyright (c) 2026 UNLINEARITY <unlinearity@gmail.com>
4
+ // Source: https://github.com/UNLINEARITY/CLI-WeChat-Bridge
5
+ // This file is part of CLI-WeChat-Bridge. Modifications and derivative works
6
+ // must be released under AGPL-3.0-or-later with full source code; see
7
+ // LICENSE.txt. Network services built on it must offer source to users.
3
8
  import { runJsEntry } from "./_run-entry.mjs";
4
9
 
5
10
  runJsEntry("dist/wechat/setup.js");
@@ -0,0 +1,10 @@
1
+ #!/usr/bin/env node
2
+ // SPDX-License-Identifier: AGPL-3.0-or-later
3
+ // Copyright (c) 2026 UNLINEARITY <unlinearity@gmail.com>
4
+ // Source: https://github.com/UNLINEARITY/CLI-WeChat-Bridge
5
+ // This file is part of CLI-WeChat-Bridge. Modifications and derivative works
6
+ // must be released under AGPL-3.0-or-later with full source code; see
7
+ // LICENSE.txt. Network services built on it must offer source to users.
8
+ import { runJsEntry } from "./_run-entry.mjs";
9
+
10
+ runJsEntry("dist/commands/check-update.js");
@@ -0,0 +1,10 @@
1
+ #!/usr/bin/env node
2
+ // SPDX-License-Identifier: AGPL-3.0-or-later
3
+ // Copyright (c) 2026 UNLINEARITY <unlinearity@gmail.com>
4
+ // Source: https://github.com/UNLINEARITY/CLI-WeChat-Bridge
5
+ // This file is part of CLI-WeChat-Bridge. Modifications and derivative works
6
+ // must be released under AGPL-3.0-or-later with full source code; see
7
+ // LICENSE.txt. Network services built on it must offer source to users.
8
+ import { runJsEntry } from "./_run-entry.mjs";
9
+
10
+ runJsEntry("dist/companion/local-companion-start.js", ["--adapter", "claude", "--channel", "wecom"]);
@@ -0,0 +1,10 @@
1
+ #!/usr/bin/env node
2
+ // SPDX-License-Identifier: AGPL-3.0-or-later
3
+ // Copyright (c) 2026 UNLINEARITY <unlinearity@gmail.com>
4
+ // Source: https://github.com/UNLINEARITY/CLI-WeChat-Bridge
5
+ // This file is part of CLI-WeChat-Bridge. Modifications and derivative works
6
+ // must be released under AGPL-3.0-or-later with full source code; see
7
+ // LICENSE.txt. Network services built on it must offer source to users.
8
+ import { runJsEntry } from "./_run-entry.mjs";
9
+
10
+ runJsEntry("dist/companion/local-companion-start.js", ["--adapter", "codex", "--channel", "wecom"]);
@@ -0,0 +1,10 @@
1
+ #!/usr/bin/env node
2
+ // SPDX-License-Identifier: AGPL-3.0-or-later
3
+ // Copyright (c) 2026 UNLINEARITY <unlinearity@gmail.com>
4
+ // Source: https://github.com/UNLINEARITY/CLI-WeChat-Bridge
5
+ // This file is part of CLI-WeChat-Bridge. Modifications and derivative works
6
+ // must be released under AGPL-3.0-or-later with full source code; see
7
+ // LICENSE.txt. Network services built on it must offer source to users.
8
+ import { runJsEntry } from "./_run-entry.mjs";
9
+
10
+ runJsEntry("dist/daemon/wechat-daemon.js", ["--channel", "wecom"]);
@@ -0,0 +1,10 @@
1
+ #!/usr/bin/env node
2
+ // SPDX-License-Identifier: AGPL-3.0-or-later
3
+ // Copyright (c) 2026 UNLINEARITY <unlinearity@gmail.com>
4
+ // Source: https://github.com/UNLINEARITY/CLI-WeChat-Bridge
5
+ // This file is part of CLI-WeChat-Bridge. Modifications and derivative works
6
+ // must be released under AGPL-3.0-or-later with full source code; see
7
+ // LICENSE.txt. Network services built on it must offer source to users.
8
+ import { runJsEntry } from "./_run-entry.mjs";
9
+
10
+ runJsEntry("dist/companion/local-companion-start.js", ["--adapter", "opencode", "--channel", "wecom"]);
@@ -0,0 +1,10 @@
1
+ #!/usr/bin/env node
2
+ // SPDX-License-Identifier: AGPL-3.0-or-later
3
+ // Copyright (c) 2026 UNLINEARITY <unlinearity@gmail.com>
4
+ // Source: https://github.com/UNLINEARITY/CLI-WeChat-Bridge
5
+ // This file is part of CLI-WeChat-Bridge. Modifications and derivative works
6
+ // must be released under AGPL-3.0-or-later with full source code; see
7
+ // LICENSE.txt. Network services built on it must offer source to users.
8
+ import { runJsEntry } from "./_run-entry.mjs";
9
+
10
+ runJsEntry("dist/companion/local-companion-start.js", ["--adapter", "pi", "--channel", "wecom"]);
@@ -0,0 +1,10 @@
1
+ #!/usr/bin/env node
2
+ // SPDX-License-Identifier: AGPL-3.0-or-later
3
+ // Copyright (c) 2026 UNLINEARITY <unlinearity@gmail.com>
4
+ // Source: https://github.com/UNLINEARITY/CLI-WeChat-Bridge
5
+ // This file is part of CLI-WeChat-Bridge. Modifications and derivative works
6
+ // must be released under AGPL-3.0-or-later with full source code; see
7
+ // LICENSE.txt. Network services built on it must offer source to users.
8
+ import { runJsEntry } from "./_run-entry.mjs";
9
+
10
+ runJsEntry("dist/channels/wecom/setup-cli.js");
@@ -0,0 +1,58 @@
1
+ const snapshots = new WeakMap();
2
+ export const MODEL_SNAPSHOT_TTL_MS = 5 * 60_000;
3
+ export function invalidateModelSnapshot(adapter) {
4
+ snapshots.delete(adapter);
5
+ }
6
+ function snapshotOwner(adapter, senderId) {
7
+ const state = adapter.getState();
8
+ return JSON.stringify([
9
+ senderId, state.kind, state.cwd, state.pid, state.startedAt,
10
+ state.resumeConversationId ?? state.sharedSessionId ?? state.sharedThreadId,
11
+ state.lastSessionSwitchAt ?? state.lastThreadSwitchAt,
12
+ ]);
13
+ }
14
+ export async function handleAdapterControl(adapter, senderId, command) {
15
+ const kind = adapter.getState().kind;
16
+ const name = kind === "claude" ? "Claude Code" : kind === "opencode" ? "OpenCode" : kind === "codex" ? "Codex" : "Pi";
17
+ try {
18
+ if (command.type === "plan") {
19
+ if (!adapter.setPlanMode)
20
+ throw new Error(`Plan mode is not available for ${name}.`);
21
+ const enabled = await adapter.setPlanMode(command.enabled);
22
+ return enabled
23
+ ? `${name} plan mode enabled. Send /plan off to restore the previous mode.`
24
+ : `${name} plan mode disabled.`;
25
+ }
26
+ if (!adapter.listModels || !adapter.selectModel)
27
+ throw new Error(`Model selection is not available for ${name}.`);
28
+ const owner = snapshotOwner(adapter, senderId);
29
+ if (!command.target) {
30
+ snapshots.delete(adapter);
31
+ const models = await adapter.listModels();
32
+ if (snapshotOwner(adapter, senderId) !== owner)
33
+ throw new Error("The active session changed. Send /model again.");
34
+ if (!models.length)
35
+ return `${name} did not return any available models.`;
36
+ snapshots.set(adapter, { owner, createdAtMs: Date.now(), models });
37
+ return [
38
+ `Available ${name} models:`, "",
39
+ ...models.map((model, index) => `${index + 1}. ${model.displayName}${model.isCurrent ? " (current)" : ""}`),
40
+ "", "Reply with /model <number> to switch. The list is valid for 5 minutes.",
41
+ ].join("\n");
42
+ }
43
+ const snapshot = snapshots.get(adapter);
44
+ if (!snapshot || snapshot.owner !== owner || Date.now() - snapshot.createdAtMs > MODEL_SNAPSHOT_TTL_MS) {
45
+ throw new Error("The model list has expired or belongs to another session. Send /model again.");
46
+ }
47
+ const index = Number(command.target);
48
+ if (!/^\d+$/.test(command.target) || !Number.isSafeInteger(index) || index < 1 || index > snapshot.models.length) {
49
+ throw new Error("Model selection is outside the displayed range. Send /model again to refresh the list.");
50
+ }
51
+ const model = await adapter.selectModel(snapshot.models[index - 1].id);
52
+ snapshots.delete(adapter);
53
+ return `${name} model switched to ${model.displayName}.`;
54
+ }
55
+ catch (error) {
56
+ return error instanceof Error ? error.message : String(error);
57
+ }
58
+ }