@unlinearity/cli-wechat-bridge 1.0.5 → 1.0.6
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 +17 -12
- package/dist/bridge/bridge-adapters.claude.js +33 -3
- package/dist/bridge/bridge-adapters.codex.js +216 -6
- package/dist/bridge/bridge-adapters.opencode.js +79 -13
- package/dist/bridge/bridge-adapters.shared.js +215 -1
- package/dist/bridge/bridge-utils.js +49 -2
- package/dist/bridge/claude-hooks.js +64 -3
- package/dist/bridge/wechat-bridge.js +58 -2
- package/dist/daemon/wechat-daemon.js +1 -1
- package/package.json +62 -100
|
@@ -8,7 +8,7 @@ import { ensureWechatCredentials } from "../wechat/setup.js";
|
|
|
8
8
|
import { BridgeStateStore } from "./bridge-state.js";
|
|
9
9
|
import { reapOrphanedOpencodeProcesses, reapPeerBridgeProcesses } from "./bridge-process-reaper.js";
|
|
10
10
|
import { createRuntimeHost } from "../runtime/create-runtime-host.js";
|
|
11
|
-
import { buildWechatInboundPrompt, buildOneTimeCode, formatApprovalMessage, formatPendingApprovalReminder, formatDuration, formatMirroredUserInputMessage, formatSessionSwitchMessage, formatStatusReport, formatTaskFailedMessage, MESSAGE_START_GRACE_MS, nowIso, OutputBatcher, parseWechatControlCommand, truncatePreview, } from "./bridge-utils.js";
|
|
11
|
+
import { buildWechatInboundPrompt, buildOneTimeCode, formatApprovalMessage, formatPendingApprovalReminder, formatPendingUserInputReminder, formatDuration, formatMirroredUserInputMessage, formatSessionSwitchMessage, formatStatusReport, formatTaskFailedMessage, formatUserInputRequestMessage, MESSAGE_START_GRACE_MS, nowIso, OutputBatcher, parsePendingUserInputAnswerCommand, parseWechatControlCommand, truncatePreview, } from "./bridge-utils.js";
|
|
12
12
|
import { classifyWechatTransportError, DEFAULT_LONG_POLL_TIMEOUT_MS, WeChatTransport, describeWechatTransportError, isWechatContextTokenStaleError, } from "../wechat/wechat-transport.js";
|
|
13
13
|
import { checkForUpdate, formatUpdateMessage, } from "../utils/version-checker.js";
|
|
14
14
|
import { clearDaemonEndpoint, isDaemonEndpointAlive, readDaemonEndpoint, } from "../daemon/daemon-link.js";
|
|
@@ -111,6 +111,15 @@ function toPendingApproval(request) {
|
|
|
111
111
|
createdAt: nowIso(),
|
|
112
112
|
};
|
|
113
113
|
}
|
|
114
|
+
function toPendingUserInput(request) {
|
|
115
|
+
if (typeof request.createdAt === "string") {
|
|
116
|
+
return request;
|
|
117
|
+
}
|
|
118
|
+
return {
|
|
119
|
+
...request,
|
|
120
|
+
createdAt: nowIso(),
|
|
121
|
+
};
|
|
122
|
+
}
|
|
114
123
|
export function shouldDeferCodexInboundMessage(params) {
|
|
115
124
|
return (params.adapter === "codex" &&
|
|
116
125
|
!params.hasPendingConfirmation &&
|
|
@@ -122,11 +131,13 @@ export function canDrainDeferredCodexInboundQueue(params) {
|
|
|
122
131
|
return (params.adapter === "codex" &&
|
|
123
132
|
params.deferredCount > 0 &&
|
|
124
133
|
!params.hasPendingConfirmation &&
|
|
134
|
+
!params.hasPendingUserInput &&
|
|
125
135
|
!params.hasPendingApproval &&
|
|
126
136
|
!params.hasActiveTask &&
|
|
127
137
|
!params.activeTurnId &&
|
|
128
138
|
params.status !== "busy" &&
|
|
129
|
-
params.status !== "awaiting_approval"
|
|
139
|
+
params.status !== "awaiting_approval" &&
|
|
140
|
+
params.status !== "awaiting_input");
|
|
130
141
|
}
|
|
131
142
|
export function formatDeferredCodexInboundQueueMessage(queuePosition) {
|
|
132
143
|
return `Queued for delivery after the current local Codex turn finishes. Queue position: ${queuePosition}.`;
|
|
@@ -394,6 +405,7 @@ async function main() {
|
|
|
394
405
|
status: adapterState.status,
|
|
395
406
|
activeTurnId: adapterState.activeTurnId,
|
|
396
407
|
hasPendingConfirmation: Boolean(stateStore.getState().pendingConfirmation),
|
|
408
|
+
hasPendingUserInput: Boolean(stateStore.getState().pendingUserInput),
|
|
397
409
|
hasPendingApproval: Boolean(adapterState.pendingApproval),
|
|
398
410
|
hasActiveTask: Boolean(activeTask),
|
|
399
411
|
})) {
|
|
@@ -717,6 +729,9 @@ function wireAdapterEvents(params) {
|
|
|
717
729
|
if (bridgeState.pendingConfirmation && !adapterState.pendingApproval) {
|
|
718
730
|
stateStore.clearPendingConfirmation();
|
|
719
731
|
}
|
|
732
|
+
if (bridgeState.pendingUserInput && !adapterState.pendingUserInput) {
|
|
733
|
+
stateStore.clearPendingUserInput();
|
|
734
|
+
}
|
|
720
735
|
const authorizedUserId = stateStore.getState().authorizedUserId;
|
|
721
736
|
switch (event.type) {
|
|
722
737
|
case "stdout":
|
|
@@ -775,6 +790,14 @@ function wireAdapterEvents(params) {
|
|
|
775
790
|
await queueWechatMessage(authorizedUserId, formatApprovalMessage(pending, adapterState), "approval_required");
|
|
776
791
|
}));
|
|
777
792
|
break;
|
|
793
|
+
case "user_input_required":
|
|
794
|
+
trackWechatForwardTask(outputBatcher.flushNow().then(async () => {
|
|
795
|
+
const pending = toPendingUserInput(event.request);
|
|
796
|
+
stateStore.setPendingUserInput(pending);
|
|
797
|
+
stateStore.appendLog(`User input requested: questions=${pending.questions.length}`);
|
|
798
|
+
await queueWechatMessage(authorizedUserId, formatUserInputRequestMessage(pending, adapterState), "user_input_required");
|
|
799
|
+
}));
|
|
800
|
+
break;
|
|
778
801
|
case "mirrored_user_input":
|
|
779
802
|
stateStore.appendLog(`mirrored_local_input: ${truncatePreview(event.text)}`);
|
|
780
803
|
if (shouldForwardBridgeEventToWechat(options.adapter, event.type, { text: event.text })) {
|
|
@@ -813,6 +836,7 @@ function wireAdapterEvents(params) {
|
|
|
813
836
|
case "task_complete":
|
|
814
837
|
trackWechatForwardTask(outputBatcher.flushNow().then(async () => {
|
|
815
838
|
stateStore.clearPendingConfirmation();
|
|
839
|
+
stateStore.clearPendingUserInput();
|
|
816
840
|
if (options.adapter === "shell") {
|
|
817
841
|
const summary = buildCompletionSummary({
|
|
818
842
|
adapter: options.adapter,
|
|
@@ -829,6 +853,7 @@ function wireAdapterEvents(params) {
|
|
|
829
853
|
case "task_failed":
|
|
830
854
|
trackWechatForwardTask(outputBatcher.flushNow().then(async () => {
|
|
831
855
|
stateStore.clearPendingConfirmation();
|
|
856
|
+
stateStore.clearPendingUserInput();
|
|
832
857
|
clearActiveTask();
|
|
833
858
|
await queueWechatMessage(authorizedUserId, formatTaskFailedMessage(options.adapter, event.message), "task_failed");
|
|
834
859
|
await maybeDrainDeferredInboundMessages();
|
|
@@ -838,6 +863,7 @@ function wireAdapterEvents(params) {
|
|
|
838
863
|
logError(event.message);
|
|
839
864
|
stateStore.appendLog(`fatal_error: ${event.message}`);
|
|
840
865
|
stateStore.clearPendingConfirmation();
|
|
866
|
+
stateStore.clearPendingUserInput();
|
|
841
867
|
clearActiveTask();
|
|
842
868
|
trackWechatForwardTask(outputBatcher.flushNow().then(async () => {
|
|
843
869
|
await queueWechatMessage(authorizedUserId, formatUserFacingBridgeFatalError(event.message), "fatal_error");
|
|
@@ -914,6 +940,7 @@ async function handleInboundMessage(params) {
|
|
|
914
940
|
await outputBatcher.flushNow();
|
|
915
941
|
outputBatcher.clear();
|
|
916
942
|
stateStore.clearPendingConfirmation();
|
|
943
|
+
stateStore.clearPendingUserInput();
|
|
917
944
|
stateStore.clearSharedSessionId();
|
|
918
945
|
await adapter.createSession();
|
|
919
946
|
stateStore.appendLog(`New ${options.adapter} session requested by owner.`);
|
|
@@ -930,6 +957,7 @@ async function handleInboundMessage(params) {
|
|
|
930
957
|
await outputBatcher.flushNow();
|
|
931
958
|
outputBatcher.clear();
|
|
932
959
|
stateStore.clearPendingConfirmation();
|
|
960
|
+
stateStore.clearPendingUserInput();
|
|
933
961
|
stateStore.clearSharedSessionId();
|
|
934
962
|
await adapter.reset();
|
|
935
963
|
stateStore.appendLog("Worker reset by owner.");
|
|
@@ -974,11 +1002,39 @@ async function handleInboundMessage(params) {
|
|
|
974
1002
|
await queueWechatMessage(message.senderId, "Approval denied.");
|
|
975
1003
|
return null;
|
|
976
1004
|
}
|
|
1005
|
+
case "answer": {
|
|
1006
|
+
const pending = state.pendingUserInput;
|
|
1007
|
+
if (!pending) {
|
|
1008
|
+
await queueWechatMessage(message.senderId, "No pending user input request.");
|
|
1009
|
+
return null;
|
|
1010
|
+
}
|
|
1011
|
+
const parsed = parsePendingUserInputAnswerCommand(systemCommand.raw, pending);
|
|
1012
|
+
if ("error" in parsed) {
|
|
1013
|
+
await queueWechatMessage(message.senderId, parsed.error);
|
|
1014
|
+
return null;
|
|
1015
|
+
}
|
|
1016
|
+
const submitted = await adapter.submitUserInput(parsed.answers);
|
|
1017
|
+
if (!submitted) {
|
|
1018
|
+
await queueWechatMessage(message.senderId, "The worker could not apply this answer.");
|
|
1019
|
+
return null;
|
|
1020
|
+
}
|
|
1021
|
+
stateStore.clearPendingUserInput();
|
|
1022
|
+
stateStore.appendLog(`User input answered: ${parsed.preview}`);
|
|
1023
|
+
await queueWechatMessage(message.senderId, "Answer submitted. Continuing...");
|
|
1024
|
+
return {
|
|
1025
|
+
startedAt: Date.now(),
|
|
1026
|
+
inputPreview: parsed.preview,
|
|
1027
|
+
};
|
|
1028
|
+
}
|
|
977
1029
|
}
|
|
978
1030
|
if (state.pendingConfirmation) {
|
|
979
1031
|
await queueWechatMessage(message.senderId, formatPendingApprovalReminder(state.pendingConfirmation, adapter.getState()));
|
|
980
1032
|
return null;
|
|
981
1033
|
}
|
|
1034
|
+
if (state.pendingUserInput) {
|
|
1035
|
+
await queueWechatMessage(message.senderId, formatPendingUserInputReminder(state.pendingUserInput));
|
|
1036
|
+
return null;
|
|
1037
|
+
}
|
|
982
1038
|
const adapterState = adapter.getState();
|
|
983
1039
|
if (shouldDeferCodexInboundMessage({
|
|
984
1040
|
adapter: options.adapter,
|
|
@@ -703,7 +703,7 @@ class WechatDaemon {
|
|
|
703
703
|
const pending = toPendingUserInput(event.request);
|
|
704
704
|
slot.pendingUserInput = pending;
|
|
705
705
|
appendDaemonLog(`user_input_required: adapter=${slot.adapter} questions=${pending.questions.length}`);
|
|
706
|
-
await this.queueWechatMessage(this.authorizedUserId, prefixDaemonAdapterMessage(slot.adapter, formatUserInputRequestMessage(pending, adapterState)), "
|
|
706
|
+
await this.queueWechatMessage(this.authorizedUserId, prefixDaemonAdapterMessage(slot.adapter, formatUserInputRequestMessage(pending, adapterState)), "user_input_required");
|
|
707
707
|
}));
|
|
708
708
|
break;
|
|
709
709
|
case "mirrored_user_input":
|
package/package.json
CHANGED
|
@@ -1,100 +1,62 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "@unlinearity/cli-wechat-bridge",
|
|
3
|
-
"version": "1.0.
|
|
4
|
-
"description": "CLI WeChat Bridge for Codex, Claude Code, OpenCode, and PowerShell over WeChat",
|
|
5
|
-
"type": "module",
|
|
6
|
-
"files": [
|
|
7
|
-
"bin/",
|
|
8
|
-
"dist/",
|
|
9
|
-
"README.md",
|
|
10
|
-
"LICENSE.txt"
|
|
11
|
-
],
|
|
12
|
-
"keywords": [
|
|
13
|
-
"wechat",
|
|
14
|
-
"cli",
|
|
15
|
-
"bridge",
|
|
16
|
-
"codex",
|
|
17
|
-
"claude",
|
|
18
|
-
"opencode",
|
|
19
|
-
"terminal"
|
|
20
|
-
],
|
|
21
|
-
"author": "UNLINEARITY",
|
|
22
|
-
"license": "MIT",
|
|
23
|
-
"repository": {
|
|
24
|
-
"type": "git",
|
|
25
|
-
"url": "git+https://github.com/UNLINEARITY/CLI-WeChat-Bridge.git"
|
|
26
|
-
},
|
|
27
|
-
"bugs": {
|
|
28
|
-
"url": "https://github.com/UNLINEARITY/CLI-WeChat-Bridge/issues"
|
|
29
|
-
},
|
|
30
|
-
"homepage": "https://github.com/UNLINEARITY/CLI-WeChat-Bridge#readme",
|
|
31
|
-
"publishConfig": {
|
|
32
|
-
"access": "public"
|
|
33
|
-
},
|
|
34
|
-
"bin": {
|
|
35
|
-
"wechat-bridge": "bin/wechat-bridge.mjs",
|
|
36
|
-
"wechat-bridge-codex": "bin/wechat-bridge-codex.mjs",
|
|
37
|
-
"wechat-codex": "bin/wechat-codex.mjs",
|
|
38
|
-
"wechat-codex-start": "bin/wechat-codex-start.mjs",
|
|
39
|
-
"wechat-claude": "bin/wechat-claude.mjs",
|
|
40
|
-
"wechat-claude-start": "bin/wechat-claude-start.mjs",
|
|
41
|
-
"wechat-bridge-claude": "bin/wechat-bridge-claude.mjs",
|
|
42
|
-
"wechat-bridge-shell": "bin/wechat-bridge-shell.mjs",
|
|
43
|
-
"wechat-bridge-opencode": "bin/wechat-bridge-opencode.mjs",
|
|
44
|
-
"wechat-opencode": "bin/wechat-opencode.mjs",
|
|
45
|
-
"wechat-opencode-start": "bin/wechat-opencode-start.mjs",
|
|
46
|
-
"wechat-daemon": "bin/wechat-daemon.mjs",
|
|
47
|
-
"wechat-setup": "bin/wechat-setup.mjs",
|
|
48
|
-
"wechat-check-update": "bin/wechat-check-update.mjs"
|
|
49
|
-
},
|
|
50
|
-
"
|
|
51
|
-
"
|
|
52
|
-
"
|
|
53
|
-
"
|
|
54
|
-
"
|
|
55
|
-
"
|
|
56
|
-
"
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
"
|
|
60
|
-
"
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
"daemon": "node --no-warnings --experimental-strip-types src/daemon/wechat-daemon.ts",
|
|
64
|
-
"bridge:codex": "node --no-warnings --experimental-strip-types src/bridge/wechat-bridge.ts --adapter codex",
|
|
65
|
-
"codex:panel": "node --no-warnings --experimental-strip-types src/companion/codex-remote-client.ts",
|
|
66
|
-
"codex:start": "node --no-warnings --experimental-strip-types src/companion/local-companion-start.ts",
|
|
67
|
-
"claude:start": "node --no-warnings --experimental-strip-types src/companion/local-companion-start.ts --adapter claude",
|
|
68
|
-
"claude:companion": "node --no-warnings --experimental-strip-types src/companion/local-companion.ts --adapter claude",
|
|
69
|
-
"bridge:claude": "node --no-warnings --experimental-strip-types src/bridge/wechat-bridge.ts --adapter claude",
|
|
70
|
-
"bridge:shell": "node --no-warnings --experimental-strip-types src/bridge/wechat-bridge.ts --adapter shell",
|
|
71
|
-
"bridge:opencode": "node --no-warnings --experimental-strip-types src/bridge/wechat-bridge.ts --adapter opencode",
|
|
72
|
-
"opencode:panel": "node --no-warnings --experimental-strip-types src/companion/local-companion.ts --adapter opencode",
|
|
73
|
-
"opencode:start": "node --no-warnings --experimental-strip-types src/companion/local-companion-start.ts --adapter opencode",
|
|
74
|
-
"bridge:bun": "bun src/bridge/wechat-bridge.ts",
|
|
75
|
-
"test": "bun test test",
|
|
76
|
-
"test:bridge": "bun test test/bridge",
|
|
77
|
-
"test:companion": "bun test test/companion",
|
|
78
|
-
"test:wechat": "bun test test/wechat",
|
|
79
|
-
"test:watch": "bun test --watch test"
|
|
80
|
-
},
|
|
81
|
-
"dependencies": {
|
|
82
|
-
"@anthropic-ai/sdk": "^0.80.0",
|
|
83
|
-
"@modelcontextprotocol/sdk": "^1.27.1",
|
|
84
|
-
"@opencode-ai/sdk": "^1.3.2",
|
|
85
|
-
"ajv-formats": "^3.0.1",
|
|
86
|
-
"node-pty": "^1.1.0",
|
|
87
|
-
"qrcode-terminal": "^0.12.0"
|
|
88
|
-
},
|
|
89
|
-
"devDependencies": {
|
|
90
|
-
"@eslint/js": "^10.0.1",
|
|
91
|
-
"@types/bun": "latest",
|
|
92
|
-
"eslint": "^10.3.0",
|
|
93
|
-
"typescript": "^6.0.2",
|
|
94
|
-
"typescript-eslint": "^8.59.3"
|
|
95
|
-
},
|
|
96
|
-
"engines": {
|
|
97
|
-
"bun": ">=1.0.0",
|
|
98
|
-
"node": ">=24.0.0"
|
|
99
|
-
}
|
|
100
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "@unlinearity/cli-wechat-bridge",
|
|
3
|
+
"version": "1.0.6",
|
|
4
|
+
"description": "CLI WeChat Bridge for Codex, Claude Code, OpenCode, and PowerShell over WeChat",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"files": [
|
|
7
|
+
"bin/",
|
|
8
|
+
"dist/",
|
|
9
|
+
"README.md",
|
|
10
|
+
"LICENSE.txt"
|
|
11
|
+
],
|
|
12
|
+
"keywords": [
|
|
13
|
+
"wechat",
|
|
14
|
+
"cli",
|
|
15
|
+
"bridge",
|
|
16
|
+
"codex",
|
|
17
|
+
"claude",
|
|
18
|
+
"opencode",
|
|
19
|
+
"terminal"
|
|
20
|
+
],
|
|
21
|
+
"author": "UNLINEARITY",
|
|
22
|
+
"license": "MIT",
|
|
23
|
+
"repository": {
|
|
24
|
+
"type": "git",
|
|
25
|
+
"url": "git+https://github.com/UNLINEARITY/CLI-WeChat-Bridge.git"
|
|
26
|
+
},
|
|
27
|
+
"bugs": {
|
|
28
|
+
"url": "https://github.com/UNLINEARITY/CLI-WeChat-Bridge/issues"
|
|
29
|
+
},
|
|
30
|
+
"homepage": "https://github.com/UNLINEARITY/CLI-WeChat-Bridge#readme",
|
|
31
|
+
"publishConfig": {
|
|
32
|
+
"access": "public"
|
|
33
|
+
},
|
|
34
|
+
"bin": {
|
|
35
|
+
"wechat-bridge": "bin/wechat-bridge.mjs",
|
|
36
|
+
"wechat-bridge-codex": "bin/wechat-bridge-codex.mjs",
|
|
37
|
+
"wechat-codex": "bin/wechat-codex.mjs",
|
|
38
|
+
"wechat-codex-start": "bin/wechat-codex-start.mjs",
|
|
39
|
+
"wechat-claude": "bin/wechat-claude.mjs",
|
|
40
|
+
"wechat-claude-start": "bin/wechat-claude-start.mjs",
|
|
41
|
+
"wechat-bridge-claude": "bin/wechat-bridge-claude.mjs",
|
|
42
|
+
"wechat-bridge-shell": "bin/wechat-bridge-shell.mjs",
|
|
43
|
+
"wechat-bridge-opencode": "bin/wechat-bridge-opencode.mjs",
|
|
44
|
+
"wechat-opencode": "bin/wechat-opencode.mjs",
|
|
45
|
+
"wechat-opencode-start": "bin/wechat-opencode-start.mjs",
|
|
46
|
+
"wechat-daemon": "bin/wechat-daemon.mjs",
|
|
47
|
+
"wechat-setup": "bin/wechat-setup.mjs",
|
|
48
|
+
"wechat-check-update": "bin/wechat-check-update.mjs"
|
|
49
|
+
},
|
|
50
|
+
"dependencies": {
|
|
51
|
+
"@anthropic-ai/sdk": "^0.80.0",
|
|
52
|
+
"@modelcontextprotocol/sdk": "^1.27.1",
|
|
53
|
+
"@opencode-ai/sdk": "^1.3.2",
|
|
54
|
+
"ajv-formats": "^3.0.1",
|
|
55
|
+
"node-pty": "^1.1.0",
|
|
56
|
+
"qrcode-terminal": "^0.12.0"
|
|
57
|
+
},
|
|
58
|
+
"engines": {
|
|
59
|
+
"bun": ">=1.0.0",
|
|
60
|
+
"node": ">=24.0.0"
|
|
61
|
+
}
|
|
62
|
+
}
|