cli-wechat-bridge 1.2.0 → 1.2.1
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 +4 -3
- package/bin/wechat-check-update.mjs +0 -0
- package/bin/wechat-claude.mjs +0 -0
- package/bin/wechat-codex.mjs +0 -0
- package/bin/wechat-daemon.mjs +0 -0
- package/bin/wechat-opencode.mjs +0 -0
- package/bin/wechat-pi.mjs +0 -0
- package/bin/wechat-setup.mjs +0 -0
- package/bin/wecom-check-update.mjs +0 -0
- package/bin/wecom-claude.mjs +0 -0
- package/bin/wecom-codex.mjs +0 -0
- package/bin/wecom-daemon.mjs +0 -0
- package/bin/wecom-opencode.mjs +0 -0
- package/bin/wecom-pi.mjs +0 -0
- package/bin/wecom-setup.mjs +0 -0
- package/dist/bridge/bridge-adapters.codex.js +89 -8
- package/dist/bridge/bridge-adapters.opencode.js +142 -19
- package/dist/bridge/claude-native-controls.js +14 -2
- package/dist/bridge/codex-visible-thread-proxy.js +140 -0
- package/dist/bridge/native-terminal-control.js +12 -0
- package/dist/bridge/opencode-v2-compat.js +450 -0
- package/dist/companion/opencode-v2-tui-bridge-plugin.js +93 -0
- package/package.json +5 -2
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
// SPDX-License-Identifier: AGPL-3.0-or-later
|
|
2
|
+
// Copyright (c) 2026 UNLINEARITY <unlinearity@gmail.com>
|
|
3
|
+
// Source: https://github.com/UNLINEARITY/CLI-WeChat-Bridge
|
|
4
|
+
// This file is part of CLI-WeChat-Bridge. Modifications and derivative works
|
|
5
|
+
// must be released under AGPL-3.0-or-later with full source code; see
|
|
6
|
+
// LICENSE.txt. Network services built on it must offer source to users.
|
|
7
|
+
import net from "node:net";
|
|
8
|
+
const ROUTE_POLL_INTERVAL_MS = 100;
|
|
9
|
+
const MAX_BUFFER_SIZE = 1024 * 1024;
|
|
10
|
+
export default {
|
|
11
|
+
id: "cli-wechat-bridge.session-route-v2",
|
|
12
|
+
setup(context) {
|
|
13
|
+
const port = Number(context.options.port);
|
|
14
|
+
const token = typeof context.options.token === "string" ? context.options.token : "";
|
|
15
|
+
if (!Number.isInteger(port) || port <= 0 || !token) {
|
|
16
|
+
return () => undefined;
|
|
17
|
+
}
|
|
18
|
+
const socket = net.connect({ host: "127.0.0.1", port });
|
|
19
|
+
let connected = false;
|
|
20
|
+
let inputBuffer = "";
|
|
21
|
+
let lastSessionId;
|
|
22
|
+
let queuedFrame = null;
|
|
23
|
+
const writeFrame = (frame) => {
|
|
24
|
+
if (socket.destroyed)
|
|
25
|
+
return;
|
|
26
|
+
if (!connected) {
|
|
27
|
+
queuedFrame = frame;
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
socket.write(`${JSON.stringify(frame)}\n`);
|
|
31
|
+
};
|
|
32
|
+
const publishRoute = () => {
|
|
33
|
+
const route = context.ui.router.current();
|
|
34
|
+
const sessionId = route.type === "session" && typeof route.sessionID === "string"
|
|
35
|
+
? route.sessionID
|
|
36
|
+
: null;
|
|
37
|
+
if (sessionId === lastSessionId)
|
|
38
|
+
return;
|
|
39
|
+
lastSessionId = sessionId;
|
|
40
|
+
writeFrame({
|
|
41
|
+
type: "route_state",
|
|
42
|
+
sessionId,
|
|
43
|
+
observedAt: new Date().toISOString(),
|
|
44
|
+
});
|
|
45
|
+
};
|
|
46
|
+
socket.setEncoding("utf8");
|
|
47
|
+
socket.on("connect", () => {
|
|
48
|
+
socket.setNoDelay(true);
|
|
49
|
+
socket.write(`${JSON.stringify({ type: "hello", token })}\n`);
|
|
50
|
+
connected = true;
|
|
51
|
+
publishRoute();
|
|
52
|
+
if (queuedFrame) {
|
|
53
|
+
socket.write(`${JSON.stringify(queuedFrame)}\n`);
|
|
54
|
+
queuedFrame = null;
|
|
55
|
+
}
|
|
56
|
+
});
|
|
57
|
+
socket.on("data", (chunk) => {
|
|
58
|
+
inputBuffer += chunk;
|
|
59
|
+
if (inputBuffer.length > MAX_BUFFER_SIZE) {
|
|
60
|
+
socket.destroy();
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
for (;;) {
|
|
64
|
+
const newline = inputBuffer.indexOf("\n");
|
|
65
|
+
if (newline < 0)
|
|
66
|
+
break;
|
|
67
|
+
const line = inputBuffer.slice(0, newline).trim();
|
|
68
|
+
inputBuffer = inputBuffer.slice(newline + 1);
|
|
69
|
+
if (!line)
|
|
70
|
+
continue;
|
|
71
|
+
try {
|
|
72
|
+
const frame = JSON.parse(line);
|
|
73
|
+
if (frame.type === "select_session" && typeof frame.sessionId === "string") {
|
|
74
|
+
context.ui.router.navigate({ type: "session", sessionID: frame.sessionId });
|
|
75
|
+
publishRoute();
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
catch {
|
|
79
|
+
// Ignore malformed bridge frames and keep the visible TUI alive.
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
});
|
|
83
|
+
socket.on("error", () => {
|
|
84
|
+
// Keep the native TUI usable if the bridge-side observer disappears.
|
|
85
|
+
});
|
|
86
|
+
const timer = setInterval(publishRoute, ROUTE_POLL_INTERVAL_MS);
|
|
87
|
+
timer.unref?.();
|
|
88
|
+
return () => {
|
|
89
|
+
clearInterval(timer);
|
|
90
|
+
socket.destroy();
|
|
91
|
+
};
|
|
92
|
+
},
|
|
93
|
+
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cli-wechat-bridge",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.1",
|
|
4
4
|
"description": "CLI bridge for Codex, Claude Code, OpenCode, and Pi over WeChat and WeCom",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"files": [
|
|
@@ -90,10 +90,12 @@
|
|
|
90
90
|
"dependencies": {
|
|
91
91
|
"@modelcontextprotocol/sdk": "^1.30.0",
|
|
92
92
|
"@opencode-ai/sdk": "^1.18.0",
|
|
93
|
+
"@opencode/client": "^2.0.18",
|
|
93
94
|
"@wecom/aibot-node-sdk": "1.0.7",
|
|
94
95
|
"@xterm/headless": "^6.0.0",
|
|
95
96
|
"node-pty": "^1.1.0",
|
|
96
|
-
"qrcode-terminal": "^0.12.0"
|
|
97
|
+
"qrcode-terminal": "^0.12.0",
|
|
98
|
+
"ws": "^8.22.0"
|
|
97
99
|
},
|
|
98
100
|
"overrides": {
|
|
99
101
|
"@hono/node-server": "2.1.1",
|
|
@@ -110,6 +112,7 @@
|
|
|
110
112
|
"@eslint/js": "^10.0.1",
|
|
111
113
|
"@types/bun": "latest",
|
|
112
114
|
"@types/node": "^24.12.4",
|
|
115
|
+
"@types/ws": "^8.18.1",
|
|
113
116
|
"eslint": "^10.3.0",
|
|
114
117
|
"typescript": "^6.0.2",
|
|
115
118
|
"typescript-eslint": "^8.59.3"
|