@unlinearity/cli-wechat-bridge 1.0.2 → 1.0.4
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 +221 -223
- package/dist/bridge/bridge-adapter-common.js +95 -0
- package/dist/bridge/bridge-adapters.claude.js +1 -1
- package/dist/bridge/bridge-adapters.codex.js +26 -4
- package/dist/bridge/bridge-adapters.core.js +10 -1
- package/dist/bridge/bridge-adapters.opencode.js +395 -43
- package/dist/bridge/bridge-adapters.shared.js +4 -97
- package/dist/bridge/bridge-final-reply.js +18 -3
- package/dist/bridge/bridge-state.js +9 -3
- package/dist/bridge/bridge-utils.js +62 -8
- package/dist/bridge/claude-hooks.js +2 -2
- package/dist/bridge/wechat-bridge.js +111 -27
- package/dist/companion/codex-remote-client.js +3 -0
- package/dist/companion/local-companion-start.js +6 -1
- package/dist/companion/local-companion.js +11 -0
- package/dist/runtime/legacy-adapter-runtime.js +6 -0
- package/dist/utils/version-checker.js +1 -1
- package/dist/wechat/wechat-transport.js +46 -2
- package/package.json +10 -2
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
export function isRecord(value) {
|
|
2
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
3
|
+
}
|
|
4
|
+
export function getCodexRpcRequestId(value) {
|
|
5
|
+
return typeof value === "string" || typeof value === "number" ? value : null;
|
|
6
|
+
}
|
|
7
|
+
export function getNotificationThreadId(params) {
|
|
8
|
+
if (!isRecord(params)) {
|
|
9
|
+
return null;
|
|
10
|
+
}
|
|
11
|
+
if (typeof params.threadId === "string") {
|
|
12
|
+
return params.threadId;
|
|
13
|
+
}
|
|
14
|
+
if (isRecord(params.thread) && typeof params.thread.id === "string") {
|
|
15
|
+
return params.thread.id;
|
|
16
|
+
}
|
|
17
|
+
return null;
|
|
18
|
+
}
|
|
19
|
+
export function getNotificationTurnId(params) {
|
|
20
|
+
if (!isRecord(params)) {
|
|
21
|
+
return null;
|
|
22
|
+
}
|
|
23
|
+
if (typeof params.turnId === "string") {
|
|
24
|
+
return params.turnId;
|
|
25
|
+
}
|
|
26
|
+
if (isRecord(params.turn) && typeof params.turn.id === "string") {
|
|
27
|
+
return params.turn.id;
|
|
28
|
+
}
|
|
29
|
+
return null;
|
|
30
|
+
}
|
|
31
|
+
export function describeUnknownError(error) {
|
|
32
|
+
if (error instanceof Error && error.message) {
|
|
33
|
+
return error.message;
|
|
34
|
+
}
|
|
35
|
+
return String(error);
|
|
36
|
+
}
|
|
37
|
+
export function normalizeCodexRpcError(error) {
|
|
38
|
+
if (isRecord(error)) {
|
|
39
|
+
const message = typeof error.message === "string"
|
|
40
|
+
? error.message
|
|
41
|
+
: typeof error.code === "number"
|
|
42
|
+
? `RPC error ${error.code}`
|
|
43
|
+
: "";
|
|
44
|
+
const data = typeof error.data === "string"
|
|
45
|
+
? error.data
|
|
46
|
+
: typeof error.details === "string"
|
|
47
|
+
? error.details
|
|
48
|
+
: "";
|
|
49
|
+
const combined = [message, data].filter(Boolean).join(": ");
|
|
50
|
+
if (combined) {
|
|
51
|
+
return combined;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
return describeUnknownError(error);
|
|
55
|
+
}
|
|
56
|
+
export function getLocalCompanionCommandName(kind) {
|
|
57
|
+
switch (kind) {
|
|
58
|
+
case "codex":
|
|
59
|
+
return "wechat-codex";
|
|
60
|
+
case "claude":
|
|
61
|
+
return "wechat-claude";
|
|
62
|
+
case "opencode":
|
|
63
|
+
return "wechat-opencode";
|
|
64
|
+
default:
|
|
65
|
+
return "local companion";
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
export function getSharedSessionIdFromAdapterState(state) {
|
|
69
|
+
return state.sharedSessionId ?? state.sharedThreadId;
|
|
70
|
+
}
|
|
71
|
+
export function quoteWindowsCommandArg(value) {
|
|
72
|
+
return `"${value.replace(/"/g, '""')}"`;
|
|
73
|
+
}
|
|
74
|
+
export function quotePosixCommandArg(value) {
|
|
75
|
+
return `'${value.replace(/'/g, `'\\''`)}'`;
|
|
76
|
+
}
|
|
77
|
+
export function isRecentIsoTimestamp(timestamp, maxAgeMs) {
|
|
78
|
+
const parsedMs = Date.parse(timestamp);
|
|
79
|
+
if (!Number.isFinite(parsedMs)) {
|
|
80
|
+
return false;
|
|
81
|
+
}
|
|
82
|
+
return parsedMs >= Date.now() - maxAgeMs;
|
|
83
|
+
}
|
|
84
|
+
export function coerceWebSocketMessageData(data) {
|
|
85
|
+
if (typeof data === "string") {
|
|
86
|
+
return data;
|
|
87
|
+
}
|
|
88
|
+
if (data instanceof ArrayBuffer) {
|
|
89
|
+
return Buffer.from(data).toString("utf8");
|
|
90
|
+
}
|
|
91
|
+
if (ArrayBuffer.isView(data)) {
|
|
92
|
+
return Buffer.from(data.buffer, data.byteOffset, data.byteLength).toString("utf8");
|
|
93
|
+
}
|
|
94
|
+
return null;
|
|
95
|
+
}
|
|
@@ -7,7 +7,7 @@ import { buildClaudeFailureMessage, buildClaudeHookScript, buildClaudeHookSettin
|
|
|
7
7
|
import { detectCliApproval, normalizeOutput, nowIso, truncatePreview, } from "./bridge-utils.js";
|
|
8
8
|
import { AbstractPtyAdapter } from "./bridge-adapters.core.js";
|
|
9
9
|
import * as shared from "./bridge-adapters.shared.js";
|
|
10
|
-
const { CLAUDE_HOOK_LISTEN_HOST, CLAUDE_WECHAT_WORKING_NOTICE_DELAY_MS, MODULE_DIR, buildClaudeCliArgs, isClaudeInvalidResumeError, quotePosixCommandArg, quoteWindowsCommandArg, shouldIncludeClaudeNoAltScreen, } = shared;
|
|
10
|
+
const { CLAUDE_HOOK_LISTEN_HOST, CLAUDE_WECHAT_WORKING_NOTICE_DELAY_MS, DEFAULT_COLS, DEFAULT_ROWS, MODULE_DIR, buildClaudeCliArgs, isClaudeInvalidResumeError, quotePosixCommandArg, quoteWindowsCommandArg, shouldIncludeClaudeNoAltScreen, } = shared;
|
|
11
11
|
const CLAUDE_COMPACT_OUTPUT_LINE_RE = /^Compacted(?:\s*\(.*full summary.*\))?$/i;
|
|
12
12
|
const CLAUDE_COMPACT_FAILURE_RE = /Error:\s*Error during compaction:|(?:^|\b)API Error:|\b(?:compact|compaction)\s+failed\b|^Error:/i;
|
|
13
13
|
const CLAUDE_COMPACT_DEDUP_MS = 2_000;
|
|
@@ -314,6 +314,13 @@ export class CodexPtyAdapter extends AbstractPtyAdapter {
|
|
|
314
314
|
}
|
|
315
315
|
return this.isNativePanelMode() ? Boolean(this.nativeProcess) : Boolean(this.pty);
|
|
316
316
|
}
|
|
317
|
+
shouldPollSessionLog() {
|
|
318
|
+
return (this.isCodexClientRunning() ||
|
|
319
|
+
this.pendingTurnStart ||
|
|
320
|
+
Boolean(this.activeTurn) ||
|
|
321
|
+
Boolean(this.state.activeTurnId) ||
|
|
322
|
+
Boolean(this.sessionFilePath));
|
|
323
|
+
}
|
|
317
324
|
async startNativeClient() {
|
|
318
325
|
this.setStatus("starting", `Starting ${this.options.kind} adapter...`);
|
|
319
326
|
let spawnTarget = null;
|
|
@@ -450,7 +457,7 @@ export class CodexPtyAdapter extends AbstractPtyAdapter {
|
|
|
450
457
|
this.nextSessionFallbackScanAtMs = 0;
|
|
451
458
|
}
|
|
452
459
|
async pollSessionLog() {
|
|
453
|
-
if (!this.
|
|
460
|
+
if (!this.shouldPollSessionLog()) {
|
|
454
461
|
return;
|
|
455
462
|
}
|
|
456
463
|
this.maybeApplyRecentSessionFallback();
|
|
@@ -462,6 +469,7 @@ export class CodexPtyAdapter extends AbstractPtyAdapter {
|
|
|
462
469
|
}
|
|
463
470
|
this.sessionReadOffset = 0;
|
|
464
471
|
this.sessionPartialLine = "";
|
|
472
|
+
this.seedSessionReplayCutoff(startedAtMs);
|
|
465
473
|
}
|
|
466
474
|
let content;
|
|
467
475
|
try {
|
|
@@ -488,6 +496,17 @@ export class CodexPtyAdapter extends AbstractPtyAdapter {
|
|
|
488
496
|
this.handleSessionLogLine(line);
|
|
489
497
|
}
|
|
490
498
|
}
|
|
499
|
+
seedSessionReplayCutoff(startedAtMs) {
|
|
500
|
+
if (this.sessionIgnoreBeforeMs !== null ||
|
|
501
|
+
this.pendingTurnStart ||
|
|
502
|
+
this.activeTurn ||
|
|
503
|
+
this.state.activeTurnId) {
|
|
504
|
+
return;
|
|
505
|
+
}
|
|
506
|
+
if (Number.isFinite(startedAtMs)) {
|
|
507
|
+
this.sessionIgnoreBeforeMs = startedAtMs;
|
|
508
|
+
}
|
|
509
|
+
}
|
|
491
510
|
maybeApplyRecentSessionFallback() {
|
|
492
511
|
if (!this.isNativePanelMode()) {
|
|
493
512
|
return;
|
|
@@ -531,6 +550,7 @@ export class CodexPtyAdapter extends AbstractPtyAdapter {
|
|
|
531
550
|
this.sessionReadOffset = 0;
|
|
532
551
|
this.sessionPartialLine = "";
|
|
533
552
|
this.sessionFinalText = null;
|
|
553
|
+
this.seedSessionReplayCutoff(startedAtMs);
|
|
534
554
|
}
|
|
535
555
|
}
|
|
536
556
|
handleSessionLogLine(line) {
|
|
@@ -783,7 +803,7 @@ export class CodexPtyAdapter extends AbstractPtyAdapter {
|
|
|
783
803
|
this.pendingTurnThreadId = null;
|
|
784
804
|
this.interruptPendingTurnStart = false;
|
|
785
805
|
this.state.activeTurnOrigin = undefined;
|
|
786
|
-
if (!this.activeTurn && this.
|
|
806
|
+
if (!this.activeTurn && this.getState().status === "busy") {
|
|
787
807
|
this.setStatus("idle");
|
|
788
808
|
}
|
|
789
809
|
throw error;
|
|
@@ -879,7 +899,9 @@ export class CodexPtyAdapter extends AbstractPtyAdapter {
|
|
|
879
899
|
catch (err) {
|
|
880
900
|
await this.stopAppServer();
|
|
881
901
|
const details = this.describeAppServerLog();
|
|
882
|
-
throw new Error(`Failed to start Codex app-server: ${String(err)}${details}
|
|
902
|
+
throw new Error(`Failed to start Codex app-server: ${String(err)}${details}`, {
|
|
903
|
+
cause: err,
|
|
904
|
+
});
|
|
883
905
|
}
|
|
884
906
|
}
|
|
885
907
|
async connectRpcClient() {
|
|
@@ -1975,7 +1997,7 @@ export class CodexPtyAdapter extends AbstractPtyAdapter {
|
|
|
1975
1997
|
if (deltaFallback.length === 0) {
|
|
1976
1998
|
return null;
|
|
1977
1999
|
}
|
|
1978
|
-
return deltaFallback[deltaFallback.length - 1];
|
|
2000
|
+
return deltaFallback[deltaFallback.length - 1] ?? null;
|
|
1979
2001
|
}
|
|
1980
2002
|
recordTurnActivity(turnId, timestamp = Date.now()) {
|
|
1981
2003
|
const timestampMs = typeof timestamp === "number" ? timestamp : Date.parse(timestamp);
|
|
@@ -3,6 +3,7 @@ import { spawn as spawnPty } from "node-pty";
|
|
|
3
3
|
import { attachLocalCompanionMessageListener, buildLocalCompanionToken, clearLocalCompanionEndpoint, clearLocalCompanionOccupancy, sendLocalCompanionMessage, updateLocalCompanionOccupancy, writeLocalCompanionEndpoint, } from "../companion/local-companion-link.js";
|
|
4
4
|
import { detectCliApproval, normalizeOutput, nowIso, truncatePreview, } from "./bridge-utils.js";
|
|
5
5
|
import * as shared from "./bridge-adapters.shared.js";
|
|
6
|
+
import { LOCAL_CLIENT_PROTOCOL_VERSION } from "../runtime/runtime-types.js";
|
|
6
7
|
const { CODEX_APP_SERVER_HOST, INTERRUPT_SETTLE_DELAY_MS, buildCliEnvironment, buildPtySpawnOptions, getLocalCompanionCommandName, getSharedSessionIdFromAdapterState, LOCAL_COMPANION_RECONNECT_GRACE_MS, resolveSpawnTarget, } = shared;
|
|
7
8
|
export class LocalCompanionProxyAdapter {
|
|
8
9
|
options;
|
|
@@ -62,6 +63,8 @@ export class LocalCompanionProxyAdapter {
|
|
|
62
63
|
return;
|
|
63
64
|
}
|
|
64
65
|
this.endpoint = {
|
|
66
|
+
protocolVersion: LOCAL_CLIENT_PROTOCOL_VERSION,
|
|
67
|
+
runtimeKind: "legacy_adapter",
|
|
65
68
|
instanceId: `${process.pid}-${Date.now().toString(36)}`,
|
|
66
69
|
kind: this.options.kind,
|
|
67
70
|
port: address.port,
|
|
@@ -74,7 +77,8 @@ export class LocalCompanionProxyAdapter {
|
|
|
74
77
|
transcriptPath: this.state.transcriptPath,
|
|
75
78
|
startedAt: nowIso(),
|
|
76
79
|
};
|
|
77
|
-
|
|
80
|
+
const endpoint = this.endpoint;
|
|
81
|
+
writeLocalCompanionEndpoint(endpoint);
|
|
78
82
|
resolve();
|
|
79
83
|
});
|
|
80
84
|
});
|
|
@@ -98,6 +102,11 @@ export class LocalCompanionProxyAdapter {
|
|
|
98
102
|
sessionId,
|
|
99
103
|
});
|
|
100
104
|
}
|
|
105
|
+
async createSession() {
|
|
106
|
+
await this.sendRequest({
|
|
107
|
+
command: "create_session",
|
|
108
|
+
});
|
|
109
|
+
}
|
|
101
110
|
async interrupt() {
|
|
102
111
|
const result = await this.sendRequest({
|
|
103
112
|
command: "interrupt",
|