@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.
@@ -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)), "approval_required");
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.5",
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
- "scripts": {
51
- "clean": "node -e \"require('node:fs').rmSync('dist', { recursive: true, force: true })\"",
52
- "build": "npm run clean && tsc -p tsconfig.build.json",
53
- "prepack": "npm run build",
54
- "lint": "eslint bin src test",
55
- "lint:fix": "eslint bin src test --fix",
56
- "typecheck:src": "tsc -p tsconfig.typecheck.src.json --noEmit --pretty false",
57
- "quality": "npm run lint && npm run typecheck:src && bun test test && npm run build",
58
- "smoke:global": "node scripts/smoke-global-install.mjs",
59
- "setup": "node --no-warnings --experimental-strip-types src/wechat/setup.ts",
60
- "start": "node --no-warnings --experimental-strip-types src/wechat/wechat-channel.ts",
61
- "check": "node --no-warnings --experimental-strip-types src/wechat/wechat-channel.ts --check",
62
- "bridge": "node --no-warnings --experimental-strip-types src/bridge/wechat-bridge.ts",
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
+ }