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.
- package/README.md +57 -41
- package/bin/wechat-bridge-pi.mjs +5 -0
- package/bin/wechat-pi-start.mjs +5 -0
- package/bin/wechat-pi.mjs +5 -0
- package/dist/bridge/bridge-adapter-common.js +2 -0
- package/dist/bridge/bridge-adapters.codex.js +58 -2
- package/dist/bridge/bridge-adapters.core.js +36 -2
- package/dist/bridge/bridge-adapters.js +5 -0
- package/dist/bridge/bridge-adapters.opencode.js +261 -32
- package/dist/bridge/bridge-adapters.pi.js +697 -0
- package/dist/bridge/bridge-adapters.shared.js +10 -3
- package/dist/bridge/bridge-state.js +1 -0
- package/dist/bridge/bridge-utils.js +67 -23
- package/dist/bridge/wechat-bridge.js +8 -7
- package/dist/companion/local-companion-link.js +6 -1
- package/dist/companion/local-companion-start.js +24 -8
- package/dist/companion/local-companion.js +3 -3
- package/dist/companion/pi-tui-bridge-extension.js +256 -0
- package/dist/daemon/wechat-daemon.js +116 -23
- package/dist/i18n/messages-en.js +2 -2
- package/dist/i18n/messages-zh.js +2 -2
- package/dist/utils/doctor.js +5 -1
- package/dist/wechat/setup.js +2 -0
- package/package.json +120 -102
|
@@ -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
|
|
90
|
-
"Send /codex, /claude, or /
|
|
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 === "
|
|
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 /
|
|
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 /
|
|
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
|
-
|
|
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(
|
|
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
|
|
979
|
-
if (
|
|
980
|
-
const result = await this.ensureSlot(
|
|
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
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
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
|
-
|
|
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.`));
|
package/dist/i18n/messages-en.js
CHANGED
|
@@ -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 /
|
|
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 ===
|
package/dist/i18n/messages-zh.js
CHANGED
|
@@ -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 或 /
|
|
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
|
// === 审批 ===
|
package/dist/utils/doctor.js
CHANGED
|
@@ -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) {
|
package/dist/wechat/setup.js
CHANGED
|
@@ -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.
|
|
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
|
-
"
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
"
|
|
24
|
-
"
|
|
25
|
-
|
|
26
|
-
"
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
"
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
"wechat-bridge
|
|
38
|
-
"wechat-codex": "bin/wechat-codex.mjs",
|
|
39
|
-
"wechat-codex
|
|
40
|
-
"wechat-
|
|
41
|
-
"wechat-claude
|
|
42
|
-
"wechat-
|
|
43
|
-
"wechat-bridge-
|
|
44
|
-
"wechat-bridge-
|
|
45
|
-
"wechat-opencode": "bin/wechat-opencode.mjs",
|
|
46
|
-
"wechat-opencode
|
|
47
|
-
"wechat-
|
|
48
|
-
"wechat-
|
|
49
|
-
"wechat-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
"
|
|
53
|
-
"
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
"
|
|
57
|
-
"
|
|
58
|
-
"
|
|
59
|
-
"
|
|
60
|
-
"
|
|
61
|
-
"
|
|
62
|
-
"
|
|
63
|
-
"
|
|
64
|
-
"
|
|
65
|
-
"
|
|
66
|
-
"
|
|
67
|
-
"
|
|
68
|
-
"
|
|
69
|
-
"
|
|
70
|
-
"
|
|
71
|
-
"
|
|
72
|
-
"
|
|
73
|
-
"
|
|
74
|
-
"
|
|
75
|
-
"
|
|
76
|
-
"
|
|
77
|
-
"bridge:
|
|
78
|
-
"
|
|
79
|
-
"
|
|
80
|
-
"
|
|
81
|
-
"
|
|
82
|
-
"
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
"
|
|
86
|
-
"
|
|
87
|
-
"
|
|
88
|
-
"
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
"@
|
|
93
|
-
"@
|
|
94
|
-
"
|
|
95
|
-
"
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
"
|
|
100
|
-
"
|
|
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
|
+
}
|