cli-wechat-bridge 1.1.2 → 1.1.3

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.
@@ -36,7 +36,7 @@ const DAEMON_TAKEOVER_FORCE_STOP_TIMEOUT_MS = 3_000;
36
36
  const DAEMON_TAKEOVER_STOP_POLL_MS = 250;
37
37
  const VISIBLE_CLIENT_CONNECT_TIMEOUT_MS = 15_000;
38
38
  const VISIBLE_CLIENT_CONNECT_POLL_MS = 250;
39
- const DAEMON_ADAPTERS = ["codex", "claude", "opencode"];
39
+ const DAEMON_ADAPTERS = ["codex", "claude", "opencode", "pi"];
40
40
  function log(message) {
41
41
  process.stderr.write(`[wechat-daemon] ${message}\n`);
42
42
  }
@@ -56,7 +56,7 @@ function computeWechatSendRetryDelayMs(attempt) {
56
56
  return WECHAT_SEND_RETRY_BASE_MS * attempt;
57
57
  }
58
58
  function isDaemonAdapterKind(value) {
59
- return value === "codex" || value === "claude" || value === "opencode";
59
+ return value === "codex" || value === "claude" || value === "opencode" || value === "pi";
60
60
  }
61
61
  function isSameWorkspaceCwd(left, right) {
62
62
  const normalizedLeft = path.resolve(left);
@@ -84,10 +84,10 @@ export function parseDaemonCliArgs(argv) {
84
84
  const next = argv[i + 1];
85
85
  if (arg === "--help" || arg === "-h") {
86
86
  process.stdout.write([
87
- "Usage: wechat-daemon [--cwd <path>] [--adapter <codex|claude|opencode>] [--profile <name-or-path>] [--no-open]",
87
+ "Usage: wechat-daemon [--cwd <path>] [--adapter <codex|claude|opencode|pi>] [--profile <name-or-path>] [--no-open]",
88
88
  "",
89
- "Keeps one WeChat connection alive and switches between Codex, Claude Code, and OpenCode from WeChat.",
90
- "Send /codex, /claude, or /opencode in WeChat to switch the active terminal.",
89
+ "Keeps one WeChat connection alive and switches between Codex, Claude Code, OpenCode, and Pi from WeChat.",
90
+ "Send /codex, /claude, /opencode, or /pi in WeChat to switch the active terminal.",
91
91
  "",
92
92
  ].join("\n"));
93
93
  process.exit(0);
@@ -133,12 +133,26 @@ export function parseDaemonSwitchCommand(text) {
133
133
  return "claude";
134
134
  case "/opencode":
135
135
  return "opencode";
136
+ case "/pi":
137
+ return "pi";
136
138
  default:
137
139
  return null;
138
140
  }
139
141
  }
142
+ export function parseDaemonSwitchDirective(text) {
143
+ const match = text
144
+ .trim()
145
+ .match(/^\/(codex|claude|opencode|pi)(?:\s+([\s\S]+))?$/i);
146
+ if (!match) {
147
+ return null;
148
+ }
149
+ return {
150
+ adapter: match[1].toLowerCase(),
151
+ remainder: match[2]?.trim() ?? "",
152
+ };
153
+ }
140
154
  export function defaultDaemonSessionStartMode(adapter) {
141
- return adapter === "claude" || adapter === "opencode" ? "new" : "restore";
155
+ return adapter === "codex" ? "restore" : "new";
142
156
  }
143
157
  export function resolveDaemonSessionStartMode(params) {
144
158
  if (params.reuseExistingVisible && params.visibleConnected) {
@@ -340,6 +354,12 @@ function isVisibleClientAlive(cwd, adapter) {
340
354
  clearLocalCompanionOccupancy(cwd, endpoint.instanceId, { adapter });
341
355
  return false;
342
356
  }
357
+ export function shouldRestartDeadCodexVisibleRuntime(params) {
358
+ return (params.adapter === "codex" &&
359
+ !params.slotCreated &&
360
+ params.hadVisibleClient &&
361
+ !params.visibleConnected);
362
+ }
343
363
  function cleanupVisibleClientLauncher(launch) {
344
364
  if (!launch.pid || !isPidAlive(launch.pid)) {
345
365
  return false;
@@ -370,6 +390,24 @@ export async function waitForVisibleClientConnection(params, deps = {}) {
370
390
  await sleepFn(Math.min(pollMs, remainingMs));
371
391
  }
372
392
  }
393
+ export async function waitForCodexVisibleThread(params, deps = {}) {
394
+ const timeoutMs = params.timeoutMs ?? VISIBLE_CLIENT_CONNECT_TIMEOUT_MS;
395
+ const pollMs = params.pollMs ?? VISIBLE_CLIENT_CONNECT_POLL_MS;
396
+ const sleepFn = deps.sleep ?? sleep;
397
+ const now = deps.now ?? (() => Date.now());
398
+ const deadline = now() + timeoutMs;
399
+ while (true) {
400
+ const threadId = params.getThreadId();
401
+ if (threadId) {
402
+ return threadId;
403
+ }
404
+ const remainingMs = deadline - now();
405
+ if (remainingMs <= 0) {
406
+ return null;
407
+ }
408
+ await sleepFn(Math.min(pollMs, remainingMs));
409
+ }
410
+ }
373
411
  function formatInboundMessagePreview(message) {
374
412
  if (message.text.trim()) {
375
413
  return message.text;
@@ -384,7 +422,7 @@ function formatInboundMessagePreview(message) {
384
422
  function formatNoActiveAdapterMessage() {
385
423
  return [
386
424
  "No active terminal is selected.",
387
- "Send /codex, /claude, or /opencode to choose one.",
425
+ "Send /codex, /claude, /opencode, or /pi to choose one.",
388
426
  ].join("\n");
389
427
  }
390
428
  export function formatDaemonSwitchResultDetail(result) {
@@ -392,6 +430,9 @@ export function formatDaemonSwitchResultDetail(result) {
392
430
  const previous = result.previousActiveAdapter
393
431
  ? ` Active terminal remains ${result.previousActiveAdapter}.`
394
432
  : " No terminal is active yet.";
433
+ if (result.visibleConnected && result.visibleReady === false) {
434
+ return `The visible Codex CLI connected, but its active thread is not ready yet.${previous} Check ${BRIDGE_LOG_FILE}.`;
435
+ }
395
436
  if (result.openedVisible) {
396
437
  return result.created
397
438
  ? `Started the bridge slot and tried to open the visible CLI, but it has not connected yet.${previous} Check ${BRIDGE_LOG_FILE}.`
@@ -543,7 +584,7 @@ class WechatDaemon {
543
584
  let consecutivePollFailures = 0;
544
585
  log("WeChat daemon is ready.");
545
586
  log(`Working directory: ${this.cwd}`);
546
- log("Switch from WeChat with /codex, /claude, or /opencode.");
587
+ log("Switch from WeChat with /codex, /claude, /opencode, or /pi.");
547
588
  appendDaemonLog(`started: cwd=${this.cwd}`);
548
589
  const activeSlot = this.getActiveSlot();
549
590
  const welcomeText = t("daemon.welcome", {
@@ -697,7 +738,24 @@ class WechatDaemon {
697
738
  created = true;
698
739
  }
699
740
  let openedVisible = false;
741
+ const visibleEndpointBeforeProbe = readLocalCompanionEndpoint(this.cwd, {
742
+ adapter,
743
+ });
744
+ const hadVisibleClient = Boolean(visibleEndpointBeforeProbe?.companionPid ||
745
+ visibleEndpointBeforeProbe?.companionConnectedAt ||
746
+ visibleEndpointBeforeProbe?.sharedThreadId ||
747
+ visibleEndpointBeforeProbe?.sharedSessionId);
700
748
  let visibleConnected = isVisibleClientAlive(this.cwd, adapter);
749
+ if (shouldRestartDeadCodexVisibleRuntime({
750
+ adapter,
751
+ slotCreated: created,
752
+ hadVisibleClient,
753
+ visibleConnected,
754
+ })) {
755
+ await this.startFreshSlotSession(slot);
756
+ appendDaemonLog(`dead_visible_codex_runtime_restarted: cwd=${this.cwd}`);
757
+ }
758
+ const sharedSessionBeforeVisible = getSharedSessionIdFromAdapterState(slot.runtime.getState());
701
759
  const sessionStartMode = resolveDaemonSessionStartMode({
702
760
  adapter,
703
761
  explicitSessionStartMode: options.sessionStartMode,
@@ -709,7 +767,7 @@ class WechatDaemon {
709
767
  if (!created &&
710
768
  options.reuseExistingVisible === false &&
711
769
  sessionStartMode === "new" &&
712
- (adapter === "claude" || adapter === "opencode") &&
770
+ (adapter === "claude" || adapter === "opencode" || adapter === "pi") &&
713
771
  visibleConnected) {
714
772
  await this.startFreshSlotSession(slot);
715
773
  appendDaemonLog(`fresh_session_started: adapter=${adapter} source=start_command`);
@@ -740,16 +798,36 @@ class WechatDaemon {
740
798
  appendDaemonLog(`visible_client_connect_timeout: adapter=${adapter} cwd=${this.cwd} timeout_ms=${VISIBLE_CLIENT_CONNECT_TIMEOUT_MS} cleaned_launcher=${cleanedLauncher}`);
741
799
  }
742
800
  }
743
- const activated = options.openVisible === false || visibleConnected;
801
+ let visibleReady = visibleConnected;
802
+ if (adapter === "codex" && visibleConnected && !sharedSessionBeforeVisible) {
803
+ const visibleThreadId = await waitForCodexVisibleThread({
804
+ getThreadId: () => {
805
+ const state = slot.runtime.getState();
806
+ if (state.lastThreadSwitchSource !== "local") {
807
+ return undefined;
808
+ }
809
+ return state.sharedThreadId ?? state.sharedSessionId;
810
+ },
811
+ });
812
+ visibleReady = Boolean(visibleThreadId);
813
+ if (visibleThreadId) {
814
+ appendDaemonLog(`visible_codex_thread_ready: thread=${visibleThreadId} cwd=${this.cwd}`);
815
+ }
816
+ else {
817
+ appendDaemonLog(`visible_codex_thread_timeout: cwd=${this.cwd} timeout_ms=${VISIBLE_CLIENT_CONNECT_TIMEOUT_MS}`);
818
+ }
819
+ }
820
+ const activated = options.openVisible === false || (visibleConnected && visibleReady);
744
821
  if (activated) {
745
822
  this.activeAdapter = adapter;
746
823
  }
747
- appendDaemonLog(`switch_adapter: adapter=${adapter} created=${created} opened_visible=${openedVisible} visible_connected=${visibleConnected} activated=${activated} previous_active=${previousActiveAdapter ?? "(none)"} session_start_mode=${sessionStartMode}`);
824
+ appendDaemonLog(`switch_adapter: adapter=${adapter} created=${created} opened_visible=${openedVisible} visible_connected=${visibleConnected} visible_ready=${visibleReady} activated=${activated} previous_active=${previousActiveAdapter ?? "(none)"} session_start_mode=${sessionStartMode}`);
748
825
  return {
749
826
  activeAdapter: adapter,
750
827
  created,
751
828
  openedVisible,
752
829
  visibleConnected,
830
+ visibleReady,
753
831
  activated,
754
832
  previousActiveAdapter,
755
833
  };
@@ -792,12 +870,12 @@ class WechatDaemon {
792
870
  slot.pendingConfirmations = [];
793
871
  slot.pendingUserInput = null;
794
872
  slot.activeTask = null;
795
- if (slot.adapter === "claude") {
873
+ if (slot.adapter === "codex" || slot.adapter === "claude") {
796
874
  await slot.runtime.reset();
797
875
  }
798
- else if (slot.adapter === "opencode") {
876
+ else if (slot.adapter === "opencode" || slot.adapter === "pi") {
799
877
  if (!slot.runtime.createSession) {
800
- throw new Error("/new is not available in opencode mode.");
878
+ throw new Error(`/new is not available in ${slot.adapter} mode.`);
801
879
  }
802
880
  await slot.runtime.createSession();
803
881
  }
@@ -975,18 +1053,24 @@ class WechatDaemon {
975
1053
  message = { ...message, text: rewritten };
976
1054
  }
977
1055
  }
978
- const switchAdapter = parseDaemonSwitchCommand(message.text);
979
- if (switchAdapter) {
980
- const result = await this.ensureSlot(switchAdapter, {
1056
+ const switchDirective = parseDaemonSwitchDirective(message.text);
1057
+ if (switchDirective) {
1058
+ const result = await this.ensureSlot(switchDirective.adapter, {
981
1059
  openVisible: true,
982
1060
  reuseExistingVisible: true,
983
1061
  });
984
1062
  const detail = formatDaemonSwitchResultDetail(result);
985
- const heading = result.activated
986
- ? `Active terminal: ${switchAdapter}.`
987
- : `Could not activate terminal: ${switchAdapter}.`;
988
- await this.queueWechatMessage(message.senderId, `${heading}\n${detail}`);
989
- return;
1063
+ if (!result.activated) {
1064
+ await this.queueWechatMessage(message.senderId, `Could not activate terminal: ${switchDirective.adapter}.\n${detail}`);
1065
+ return;
1066
+ }
1067
+ if (switchDirective.remainder) {
1068
+ message = { ...message, text: switchDirective.remainder };
1069
+ }
1070
+ else {
1071
+ await this.queueWechatMessage(message.senderId, `Active terminal: ${switchDirective.adapter}.\n${detail}`);
1072
+ return;
1073
+ }
990
1074
  }
991
1075
  if (message.text.trim().toLowerCase() === "/daemon-stop") {
992
1076
  await this.queueWechatMessage(message.senderId, "Stopping wechat-daemon...");
@@ -1004,7 +1088,7 @@ class WechatDaemon {
1004
1088
  await this.queueWechatMessage(message.senderId, formatBindCommandUsage());
1005
1089
  return;
1006
1090
  }
1007
- const slot = this.getActiveSlot();
1091
+ let slot = this.getActiveSlot();
1008
1092
  if (!slot) {
1009
1093
  await this.queueWechatMessage(message.senderId, formatNoActiveAdapterMessage());
1010
1094
  return;
@@ -1026,6 +1110,15 @@ class WechatDaemon {
1026
1110
  await this.queueWechatMessage(message.senderId, prefixDaemonAdapterMessage(slot.adapter, formatPendingUserInputReminder(slot.pendingUserInput)));
1027
1111
  return;
1028
1112
  }
1113
+ const visibleResult = await this.ensureSlot(slot.adapter, {
1114
+ openVisible: true,
1115
+ reuseExistingVisible: true,
1116
+ });
1117
+ if (!visibleResult.activated) {
1118
+ await this.queueWechatMessage(message.senderId, prefixDaemonAdapterMessage(slot.adapter, formatDaemonSwitchResultDetail(visibleResult)));
1119
+ return;
1120
+ }
1121
+ slot = this.getActiveSlot() ?? slot;
1029
1122
  const adapterState = slot.runtime.getState();
1030
1123
  if (adapterState.status === "busy" || adapterState.status === "awaiting_approval") {
1031
1124
  await this.queueWechatMessage(message.senderId, prefixDaemonAdapterMessage(slot.adapter, `${slot.adapter} is still working. Wait for the current reply or use /stop.`));
@@ -11,8 +11,8 @@ export const messages = {
11
11
  "bridge.fatalError": "Bridge error: {message}",
12
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.",
13
13
  // === Daemon welcome & control ===
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",
15
- "daemon.noActiveAdapter": "No active terminal is selected. Send /codex, /claude, or /opencode to start one.",
14
+ "daemon.welcome": "WeChat Daemon ready.\nCWD: {cwd}\nActive: {adapter}\n\nCommands: /claude, /codex, /opencode, /pi, /stop, /confirm, /deny, /status\n{bindings}\n\nManage: /bind [emoji] cmd, /unbind [emoji], /bindings",
15
+ "daemon.noActiveAdapter": "No active terminal is selected. Send /codex, /claude, /opencode, or /pi to start one.",
16
16
  "daemon.switchResult.new": "Started a new visible CLI.",
17
17
  "daemon.switchResult.reused": "Reused the existing visible CLI.",
18
18
  // === Approval ===
@@ -11,8 +11,8 @@ export const messages = {
11
11
  "bridge.fatalError": "桥接错误: {message}",
12
12
  "bridge.backlogIgnored": "已忽略 {count} 条桥接启动前的积压消息(不会自动处理)。\n如果你是在桥接启动后才发送的消息却看到此提示,说明本机系统时钟可能不准(偏差超过 {graceSeconds} 秒),请校准时间后重启桥接。",
13
13
  // === Daemon 欢迎 & 控制 ===
14
- "daemon.welcome": "微信 Daemon 就绪。\n工作目录: {cwd}\n当前: {adapter}\n\n命令: /claude, /codex, /opencode, /stop, /confirm, /deny, /status\n{bindings}\n\n管理: /bind [表情] 命令, /unbind [表情], /bindings",
15
- "daemon.noActiveAdapter": "未选择活跃终端。发送 /codex、/claude 或 /opencode 启动一个。",
14
+ "daemon.welcome": "微信 Daemon 就绪。\n工作目录: {cwd}\n当前: {adapter}\n\n命令: /claude, /codex, /opencode, /pi, /stop, /confirm, /deny, /status\n{bindings}\n\n管理: /bind [表情] 命令, /unbind [表情], /bindings",
15
+ "daemon.noActiveAdapter": "未选择活跃终端。发送 /codex、/claude、/opencode 或 /pi 启动一个。",
16
16
  "daemon.switchResult.new": "已启动新的可见 CLI。",
17
17
  "daemon.switchResult.reused": "已复用现有的可见 CLI。",
18
18
  // === 审批 ===
@@ -174,6 +174,7 @@ function isBridgeAdapterKind(value) {
174
174
  return (value === "codex" ||
175
175
  value === "claude" ||
176
176
  value === "opencode" ||
177
+ value === "pi" ||
177
178
  value === "shell");
178
179
  }
179
180
  export function parseDoctorCliArgs(argv, mode = "generic", fallbackCwd = process.cwd()) {
@@ -402,7 +403,7 @@ function chooseEndpointAdapters(options, lock) {
402
403
  return [lock.adapter];
403
404
  }
404
405
  return options.mode === "daemon"
405
- ? ["codex", "claude", "opencode"]
406
+ ? ["codex", "claude", "opencode", "pi"]
406
407
  : ["codex"];
407
408
  }
408
409
  function chooseCliChecks(options) {
@@ -414,6 +415,8 @@ function chooseCliChecks(options) {
414
415
  return [{ name: "claude", label: "Claude CLI", optional: false }];
415
416
  case "opencode":
416
417
  return [{ name: "opencode", label: "OpenCode", optional: false }];
418
+ case "pi":
419
+ return [{ name: "pi", label: "Pi", optional: false }];
417
420
  case "shell":
418
421
  return [];
419
422
  default:
@@ -424,6 +427,7 @@ function chooseCliChecks(options) {
424
427
  { name: "codex", label: "Codex CLI", optional: true },
425
428
  { name: "claude", label: "Claude CLI", optional: true },
426
429
  { name: "opencode", label: "OpenCode", optional: true },
430
+ { name: "pi", label: "Pi", optional: true },
427
431
  ];
428
432
  }
429
433
  function appendCliLines(lines, options, deps) {
@@ -169,12 +169,14 @@ function printPostLoginHelp(log) {
169
169
  log(" wechat-codex-start");
170
170
  log(" wechat-claude-start");
171
171
  log(" wechat-opencode-start");
172
+ log(" wechat-pi-start");
172
173
  log(" wechat-bridge-shell");
173
174
  log("");
174
175
  log("Manual two-terminal mode is also available:");
175
176
  log(" wechat-bridge-codex + wechat-codex");
176
177
  log(" wechat-bridge-claude + wechat-claude");
177
178
  log(" wechat-bridge-opencode + wechat-opencode");
179
+ log(" wechat-bridge-pi + wechat-pi");
178
180
  log("");
179
181
  log("Run wechat-setup again any time you need to refresh the login.");
180
182
  }
package/package.json CHANGED
@@ -1,102 +1,120 @@
1
- {
2
- "name": "cli-wechat-bridge",
3
- "version": "1.1.2",
4
- "description": "CLI WeChat Bridge for Codex, Claude Code, OpenCode, and PowerShell over WeChat",
5
- "type": "module",
6
- "files": [
7
- "bin/",
8
- "dist/",
9
- "scripts/ensure-node-pty-permissions.mjs",
10
- "README.md",
11
- "LICENSE.txt"
12
- ],
13
- "keywords": [
14
- "wechat",
15
- "cli",
16
- "bridge",
17
- "codex",
18
- "claude",
19
- "opencode",
20
- "terminal"
21
- ],
22
- "author": "UNLINEARITY",
23
- "license": "AGPL-3.0-or-later",
24
- "repository": {
25
- "type": "git",
26
- "url": "git+https://github.com/UNLINEARITY/CLI-WeChat-Bridge.git"
27
- },
28
- "bugs": {
29
- "url": "https://github.com/UNLINEARITY/CLI-WeChat-Bridge/issues"
30
- },
31
- "homepage": "https://github.com/UNLINEARITY/CLI-WeChat-Bridge#readme",
32
- "publishConfig": {
33
- "access": "public"
34
- },
35
- "bin": {
36
- "wechat-bridge": "bin/wechat-bridge.mjs",
37
- "wechat-bridge-codex": "bin/wechat-bridge-codex.mjs",
38
- "wechat-codex": "bin/wechat-codex.mjs",
39
- "wechat-codex-start": "bin/wechat-codex-start.mjs",
40
- "wechat-claude": "bin/wechat-claude.mjs",
41
- "wechat-claude-start": "bin/wechat-claude-start.mjs",
42
- "wechat-bridge-claude": "bin/wechat-bridge-claude.mjs",
43
- "wechat-bridge-shell": "bin/wechat-bridge-shell.mjs",
44
- "wechat-bridge-opencode": "bin/wechat-bridge-opencode.mjs",
45
- "wechat-opencode": "bin/wechat-opencode.mjs",
46
- "wechat-opencode-start": "bin/wechat-opencode-start.mjs",
47
- "wechat-daemon": "bin/wechat-daemon.mjs",
48
- "wechat-setup": "bin/wechat-setup.mjs",
49
- "wechat-check-update": "bin/wechat-check-update.mjs"
50
- },
51
- "scripts": {
52
- "postinstall": "node scripts/ensure-node-pty-permissions.mjs",
53
- "clean": "node -e \"require('node:fs').rmSync('dist', { recursive: true, force: true })\"",
54
- "build": "npm run clean && tsc -p tsconfig.build.json",
55
- "prepack": "npm run build",
56
- "lint": "eslint bin src test",
57
- "lint:fix": "eslint bin src test --fix",
58
- "typecheck:src": "tsc -p tsconfig.typecheck.src.json --noEmit --pretty false",
59
- "quality": "npm run lint && npm run typecheck:src && bun test test && npm run build",
60
- "smoke:global": "node scripts/smoke-global-install.mjs",
61
- "publish:dual": "node scripts/publish-dual.mjs",
62
- "setup": "node --no-warnings --experimental-strip-types src/wechat/setup.ts",
63
- "start": "node --no-warnings --experimental-strip-types src/wechat/wechat-channel.ts",
64
- "check": "node --no-warnings --experimental-strip-types src/wechat/wechat-channel.ts --check",
65
- "bridge": "node --no-warnings --experimental-strip-types src/bridge/wechat-bridge.ts",
66
- "daemon": "node --no-warnings --experimental-strip-types src/daemon/wechat-daemon.ts",
67
- "bridge:codex": "node --no-warnings --experimental-strip-types src/bridge/wechat-bridge.ts --adapter codex",
68
- "codex:panel": "node --no-warnings --experimental-strip-types src/companion/codex-remote-client.ts",
69
- "codex:start": "node --no-warnings --experimental-strip-types src/companion/local-companion-start.ts",
70
- "claude:start": "node --no-warnings --experimental-strip-types src/companion/local-companion-start.ts --adapter claude",
71
- "claude:companion": "node --no-warnings --experimental-strip-types src/companion/local-companion.ts --adapter claude",
72
- "bridge:claude": "node --no-warnings --experimental-strip-types src/bridge/wechat-bridge.ts --adapter claude",
73
- "bridge:shell": "node --no-warnings --experimental-strip-types src/bridge/wechat-bridge.ts --adapter shell",
74
- "bridge:opencode": "node --no-warnings --experimental-strip-types src/bridge/wechat-bridge.ts --adapter opencode",
75
- "opencode:panel": "node --no-warnings --experimental-strip-types src/companion/local-companion.ts --adapter opencode",
76
- "opencode:start": "node --no-warnings --experimental-strip-types src/companion/local-companion-start.ts --adapter opencode",
77
- "bridge:bun": "bun src/bridge/wechat-bridge.ts",
78
- "test": "bun test test",
79
- "test:bridge": "bun test test/bridge",
80
- "test:companion": "bun test test/companion",
81
- "test:wechat": "bun test test/wechat",
82
- "test:watch": "bun test --watch test"
83
- },
84
- "dependencies": {
85
- "@modelcontextprotocol/sdk": "^1.27.1",
86
- "@opencode-ai/sdk": "^1.3.2",
87
- "node-pty": "^1.1.0",
88
- "qrcode-terminal": "^0.12.0"
89
- },
90
- "devDependencies": {
91
- "@eslint/js": "^10.0.1",
92
- "@types/bun": "latest",
93
- "@types/node": "^24.12.4",
94
- "eslint": "^10.3.0",
95
- "typescript": "^6.0.2",
96
- "typescript-eslint": "^8.59.3"
97
- },
98
- "engines": {
99
- "bun": ">=1.0.0",
100
- "node": ">=24.0.0"
101
- }
102
- }
1
+ {
2
+ "name": "cli-wechat-bridge",
3
+ "version": "1.1.3",
4
+ "description": "CLI WeChat Bridge for Codex, Claude Code, OpenCode, Pi, and PowerShell over WeChat",
5
+ "type": "module",
6
+ "files": [
7
+ "bin/",
8
+ "dist/",
9
+ "scripts/ensure-node-pty-permissions.mjs",
10
+ "README.md",
11
+ "LICENSE.txt"
12
+ ],
13
+ "keywords": [
14
+ "wechat",
15
+ "cli",
16
+ "bridge",
17
+ "codex",
18
+ "claude",
19
+ "opencode",
20
+ "pi",
21
+ "terminal"
22
+ ],
23
+ "author": "UNLINEARITY",
24
+ "license": "AGPL-3.0-or-later",
25
+ "repository": {
26
+ "type": "git",
27
+ "url": "git+https://github.com/UNLINEARITY/CLI-WeChat-Bridge.git"
28
+ },
29
+ "bugs": {
30
+ "url": "https://github.com/UNLINEARITY/CLI-WeChat-Bridge/issues"
31
+ },
32
+ "homepage": "https://github.com/UNLINEARITY/CLI-WeChat-Bridge#readme",
33
+ "publishConfig": {
34
+ "access": "public"
35
+ },
36
+ "bin": {
37
+ "wechat-bridge": "bin/wechat-bridge.mjs",
38
+ "wechat-bridge-codex": "bin/wechat-bridge-codex.mjs",
39
+ "wechat-codex": "bin/wechat-codex.mjs",
40
+ "wechat-codex-start": "bin/wechat-codex-start.mjs",
41
+ "wechat-claude": "bin/wechat-claude.mjs",
42
+ "wechat-claude-start": "bin/wechat-claude-start.mjs",
43
+ "wechat-bridge-claude": "bin/wechat-bridge-claude.mjs",
44
+ "wechat-bridge-shell": "bin/wechat-bridge-shell.mjs",
45
+ "wechat-bridge-opencode": "bin/wechat-bridge-opencode.mjs",
46
+ "wechat-opencode": "bin/wechat-opencode.mjs",
47
+ "wechat-opencode-start": "bin/wechat-opencode-start.mjs",
48
+ "wechat-bridge-pi": "bin/wechat-bridge-pi.mjs",
49
+ "wechat-pi": "bin/wechat-pi.mjs",
50
+ "wechat-pi-start": "bin/wechat-pi-start.mjs",
51
+ "wechat-daemon": "bin/wechat-daemon.mjs",
52
+ "wechat-setup": "bin/wechat-setup.mjs",
53
+ "wechat-check-update": "bin/wechat-check-update.mjs"
54
+ },
55
+ "scripts": {
56
+ "postinstall": "node scripts/ensure-node-pty-permissions.mjs",
57
+ "clean": "node -e \"require('node:fs').rmSync('dist', { recursive: true, force: true })\"",
58
+ "build": "npm run clean && tsc -p tsconfig.build.json",
59
+ "prepack": "npm run build",
60
+ "lint": "eslint bin src test",
61
+ "lint:fix": "eslint bin src test --fix",
62
+ "typecheck:src": "tsc -p tsconfig.typecheck.src.json --noEmit --pretty false",
63
+ "quality": "npm run lint && npm run typecheck:src && bun test test && npm run build",
64
+ "smoke:global": "node scripts/smoke-global-install.mjs",
65
+ "publish:dual": "node scripts/publish-dual.mjs",
66
+ "setup": "node --no-warnings --experimental-strip-types src/wechat/setup.ts",
67
+ "start": "node --no-warnings --experimental-strip-types src/wechat/wechat-channel.ts",
68
+ "check": "node --no-warnings --experimental-strip-types src/wechat/wechat-channel.ts --check",
69
+ "bridge": "node --no-warnings --experimental-strip-types src/bridge/wechat-bridge.ts",
70
+ "daemon": "node --no-warnings --experimental-strip-types src/daemon/wechat-daemon.ts",
71
+ "bridge:codex": "node --no-warnings --experimental-strip-types src/bridge/wechat-bridge.ts --adapter codex",
72
+ "codex:panel": "node --no-warnings --experimental-strip-types src/companion/codex-remote-client.ts",
73
+ "codex:start": "node --no-warnings --experimental-strip-types src/companion/local-companion-start.ts",
74
+ "claude:start": "node --no-warnings --experimental-strip-types src/companion/local-companion-start.ts --adapter claude",
75
+ "claude:companion": "node --no-warnings --experimental-strip-types src/companion/local-companion.ts --adapter claude",
76
+ "bridge:claude": "node --no-warnings --experimental-strip-types src/bridge/wechat-bridge.ts --adapter claude",
77
+ "bridge:shell": "node --no-warnings --experimental-strip-types src/bridge/wechat-bridge.ts --adapter shell",
78
+ "bridge:opencode": "node --no-warnings --experimental-strip-types src/bridge/wechat-bridge.ts --adapter opencode",
79
+ "opencode:panel": "node --no-warnings --experimental-strip-types src/companion/local-companion.ts --adapter opencode",
80
+ "opencode:start": "node --no-warnings --experimental-strip-types src/companion/local-companion-start.ts --adapter opencode",
81
+ "bridge:pi": "node --no-warnings --experimental-strip-types src/bridge/wechat-bridge.ts --adapter pi",
82
+ "pi:companion": "node --no-warnings --experimental-strip-types src/companion/local-companion.ts --adapter pi",
83
+ "pi:start": "node --no-warnings --experimental-strip-types src/companion/local-companion-start.ts --adapter pi",
84
+ "bridge:bun": "bun src/bridge/wechat-bridge.ts",
85
+ "test": "bun test test",
86
+ "test:bridge": "bun test test/bridge",
87
+ "test:companion": "bun test test/companion",
88
+ "test:wechat": "bun test test/wechat",
89
+ "test:watch": "bun test --watch test"
90
+ },
91
+ "dependencies": {
92
+ "@modelcontextprotocol/sdk": "^1.30.0",
93
+ "@opencode-ai/sdk": "^1.18.0",
94
+ "node-pty": "^1.1.0",
95
+ "qrcode-terminal": "^0.12.0"
96
+ },
97
+ "overrides": {
98
+ "@hono/node-server": "2.1.1",
99
+ "body-parser": "2.3.0",
100
+ "brace-expansion": "5.0.9",
101
+ "express-rate-limit": "8.6.2",
102
+ "fast-uri": "3.1.5",
103
+ "hono": "4.13.3",
104
+ "ip-address": "10.5.0",
105
+ "path-to-regexp": "8.4.2",
106
+ "qs": "6.15.3"
107
+ },
108
+ "devDependencies": {
109
+ "@eslint/js": "^10.0.1",
110
+ "@types/bun": "latest",
111
+ "@types/node": "^24.12.4",
112
+ "eslint": "^10.3.0",
113
+ "typescript": "^6.0.2",
114
+ "typescript-eslint": "^8.59.3"
115
+ },
116
+ "engines": {
117
+ "bun": ">=1.0.0",
118
+ "node": ">=24.0.0"
119
+ }
120
+ }