cli-wechat-bridge 1.1.3 → 1.1.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 +98 -135
- package/bin/_run-entry.mjs +12 -0
- package/bin/wechat-claude-start.mjs +7 -2
- package/bin/wechat-claude.mjs +1 -1
- package/bin/wechat-codex-start.mjs +7 -2
- package/bin/wechat-codex.mjs +1 -1
- package/bin/wechat-opencode-start.mjs +7 -2
- package/bin/wechat-opencode.mjs +1 -1
- package/bin/wechat-pi-start.mjs +7 -2
- package/bin/wechat-pi.mjs +1 -1
- package/dist/bridge/bridge-adapters.claude.js +21 -1
- package/dist/bridge/bridge-adapters.codex.js +27 -4
- package/dist/bridge/bridge-adapters.core.js +76 -4
- package/dist/bridge/bridge-adapters.js +0 -3
- package/dist/bridge/bridge-adapters.opencode.js +130 -21
- package/dist/bridge/bridge-adapters.pi.js +12 -2
- package/dist/bridge/bridge-adapters.shared.js +13 -100
- package/dist/bridge/bridge-final-reply.js +17 -2
- package/dist/bridge/bridge-process-reaper.js +14 -4
- package/dist/bridge/bridge-state.js +41 -6
- package/dist/bridge/bridge-utils.js +37 -224
- package/dist/bridge/wechat-bridge.js +75 -130
- package/dist/bridge/wechat-forwarding.js +58 -0
- package/dist/companion/codex-remote-client.js +20 -6
- package/dist/companion/local-companion-link.js +7 -2
- package/dist/companion/local-companion-start.js +45 -13
- package/dist/companion/local-companion.js +6 -3
- package/dist/daemon/daemon-link.js +25 -3
- package/dist/daemon/wechat-daemon.js +148 -54
- package/dist/i18n/index.js +0 -3
- package/dist/i18n/messages-en.js +2 -3
- package/dist/i18n/messages-zh.js +2 -3
- package/dist/utils/atomic-file.js +15 -0
- package/dist/utils/doctor.js +23 -11
- package/dist/wechat/channel-config.js +0 -2
- package/dist/wechat/setup.js +5 -11
- package/dist/wechat/wechat-transport.js +14 -8
- package/package.json +2 -9
- package/bin/wechat-bridge-claude.mjs +0 -5
- package/bin/wechat-bridge-codex.mjs +0 -5
- package/bin/wechat-bridge-opencode.mjs +0 -5
- package/bin/wechat-bridge-pi.mjs +0 -5
- package/bin/wechat-bridge-shell.mjs +0 -5
- package/bin/wechat-bridge.mjs +0 -5
- package/dist/bridge/bridge-adapters.shell.js +0 -363
|
@@ -61,45 +61,40 @@ const INLINE_VOICE_EXTENSIONS = new Set([
|
|
|
61
61
|
".ogg",
|
|
62
62
|
".aac",
|
|
63
63
|
]);
|
|
64
|
+
// Directly executable / installer file types are never auto-uploaded from a
|
|
65
|
+
// path mentioned in a reply: handing these to WeChat contacts by accident is
|
|
66
|
+
// a real hazard. Everything else — source code, documents, archives, data
|
|
67
|
+
// files — stays auto-sendable so common workflows keep working.
|
|
64
68
|
const INLINE_REFERENCE_ONLY_FILE_EXTENSIONS = new Set([
|
|
69
|
+
// Windows binaries and installers
|
|
70
|
+
".exe",
|
|
71
|
+
".dll",
|
|
72
|
+
".sys",
|
|
73
|
+
".msi",
|
|
74
|
+
".msp",
|
|
75
|
+
".scr",
|
|
76
|
+
".com",
|
|
77
|
+
".pif",
|
|
78
|
+
".cpl",
|
|
79
|
+
".ocx",
|
|
80
|
+
".hta",
|
|
81
|
+
".jar",
|
|
82
|
+
// Windows one-click script launchers
|
|
65
83
|
".bat",
|
|
66
|
-
".c",
|
|
67
|
-
".cc",
|
|
68
|
-
".cjs",
|
|
69
84
|
".cmd",
|
|
70
|
-
".cpp",
|
|
71
|
-
".cs",
|
|
72
|
-
".cts",
|
|
73
|
-
".cxx",
|
|
74
|
-
".go",
|
|
75
|
-
".h",
|
|
76
|
-
".hh",
|
|
77
|
-
".hpp",
|
|
78
|
-
".java",
|
|
79
|
-
".js",
|
|
80
|
-
".jsx",
|
|
81
|
-
".kt",
|
|
82
|
-
".kts",
|
|
83
|
-
".lua",
|
|
84
|
-
".m",
|
|
85
|
-
".mjs",
|
|
86
|
-
".mm",
|
|
87
|
-
".mts",
|
|
88
|
-
".php",
|
|
89
|
-
".pl",
|
|
90
85
|
".ps1",
|
|
91
|
-
".
|
|
92
|
-
".
|
|
93
|
-
".
|
|
94
|
-
".
|
|
95
|
-
".
|
|
96
|
-
|
|
86
|
+
".vbs",
|
|
87
|
+
".vbe",
|
|
88
|
+
".jse",
|
|
89
|
+
".wsf",
|
|
90
|
+
".wsh",
|
|
91
|
+
// Unix one-click script launchers and installable packages
|
|
97
92
|
".sh",
|
|
98
|
-
".
|
|
99
|
-
".
|
|
100
|
-
".
|
|
101
|
-
".
|
|
102
|
-
".
|
|
93
|
+
".bash",
|
|
94
|
+
".deb",
|
|
95
|
+
".rpm",
|
|
96
|
+
".apk",
|
|
97
|
+
".dmg",
|
|
103
98
|
]);
|
|
104
99
|
const INLINE_MAAS_URL_RE = /https?:\/\/[^\s]*?\/([A-Za-z]:\\.+?(?:\.\s*[A-Za-z0-9]{2,8})+)(?:\?[^\n]*)?/g;
|
|
105
100
|
const INLINE_WINDOWS_PATH_RE = /(^|[^\w])`?([A-Za-z]:\\(?:[^\\/:*?"<>|\r\n`]+\\)*[^\\/:*?"<>|\r\n`]+?(?:\.\s*[A-Za-z0-9]{2,8})+)`?(?=$|[^\w])/gm;
|
|
@@ -451,176 +446,6 @@ export function isHighRiskShellCommand(command) {
|
|
|
451
446
|
}
|
|
452
447
|
return HIGH_RISK_PATTERNS.some((pattern) => pattern.test(normalized));
|
|
453
448
|
}
|
|
454
|
-
const ALWAYS_INTERACTIVE_SHELL_COMMANDS = new Set([
|
|
455
|
-
"ftp",
|
|
456
|
-
"htop",
|
|
457
|
-
"irb",
|
|
458
|
-
"less",
|
|
459
|
-
"more",
|
|
460
|
-
"mongosh",
|
|
461
|
-
"mysql",
|
|
462
|
-
"nano",
|
|
463
|
-
"nvim",
|
|
464
|
-
"psql",
|
|
465
|
-
"redis-cli",
|
|
466
|
-
"screen",
|
|
467
|
-
"sftp",
|
|
468
|
-
"sqlite3",
|
|
469
|
-
"ssh",
|
|
470
|
-
"telnet",
|
|
471
|
-
"tmux",
|
|
472
|
-
"top",
|
|
473
|
-
"vi",
|
|
474
|
-
"vim",
|
|
475
|
-
"watch",
|
|
476
|
-
]);
|
|
477
|
-
function tokenizeShellCommand(command, maxTokens = 16) {
|
|
478
|
-
const tokens = [];
|
|
479
|
-
let current = "";
|
|
480
|
-
let quote = null;
|
|
481
|
-
for (const char of command.trim()) {
|
|
482
|
-
if (quote) {
|
|
483
|
-
if (char === quote) {
|
|
484
|
-
quote = null;
|
|
485
|
-
}
|
|
486
|
-
else {
|
|
487
|
-
current += char;
|
|
488
|
-
}
|
|
489
|
-
continue;
|
|
490
|
-
}
|
|
491
|
-
if (char === '"' || char === "'") {
|
|
492
|
-
quote = char;
|
|
493
|
-
continue;
|
|
494
|
-
}
|
|
495
|
-
if (/\s/u.test(char)) {
|
|
496
|
-
if (current) {
|
|
497
|
-
tokens.push(current);
|
|
498
|
-
current = "";
|
|
499
|
-
if (tokens.length >= maxTokens) {
|
|
500
|
-
return tokens;
|
|
501
|
-
}
|
|
502
|
-
}
|
|
503
|
-
continue;
|
|
504
|
-
}
|
|
505
|
-
current += char;
|
|
506
|
-
}
|
|
507
|
-
if (current) {
|
|
508
|
-
tokens.push(current);
|
|
509
|
-
}
|
|
510
|
-
return tokens;
|
|
511
|
-
}
|
|
512
|
-
function normalizeShellExecutableToken(token) {
|
|
513
|
-
const trimmed = token.trim().replace(/^["']|["']$/g, "");
|
|
514
|
-
if (!trimmed) {
|
|
515
|
-
return "";
|
|
516
|
-
}
|
|
517
|
-
return path.parse(trimmed).name.toLowerCase();
|
|
518
|
-
}
|
|
519
|
-
function findCommandFlagIndex(args, supportedFlags) {
|
|
520
|
-
return args.findIndex((arg) => supportedFlags.includes(arg.toLowerCase()));
|
|
521
|
-
}
|
|
522
|
-
function hasScriptLikeArg(args) {
|
|
523
|
-
return args.some((arg) => Boolean(arg) && !arg.startsWith("-") && !arg.startsWith("/"));
|
|
524
|
-
}
|
|
525
|
-
function hasAnyCommandFlag(args, supportedFlags) {
|
|
526
|
-
return findCommandFlagIndex(args, supportedFlags) >= 0;
|
|
527
|
-
}
|
|
528
|
-
function buildInteractiveShellCommandMessage(executable, suggestion) {
|
|
529
|
-
return `Interactive command "${executable}" is not supported in shell mode yet. This shell bridge currently only supports non-interactive commands and scripts. ${suggestion}`;
|
|
530
|
-
}
|
|
531
|
-
export function getInteractiveShellCommandRejectionMessage(command) {
|
|
532
|
-
const tokens = tokenizeShellCommand(command);
|
|
533
|
-
if (!tokens.length) {
|
|
534
|
-
return null;
|
|
535
|
-
}
|
|
536
|
-
const firstToken = tokens[0];
|
|
537
|
-
if (!firstToken) {
|
|
538
|
-
return null;
|
|
539
|
-
}
|
|
540
|
-
const executable = normalizeShellExecutableToken(firstToken);
|
|
541
|
-
const args = tokens.slice(1);
|
|
542
|
-
const lowerArgs = args.map((arg) => arg.toLowerCase());
|
|
543
|
-
if (!executable) {
|
|
544
|
-
return null;
|
|
545
|
-
}
|
|
546
|
-
if (ALWAYS_INTERACTIVE_SHELL_COMMANDS.has(executable)) {
|
|
547
|
-
return buildInteractiveShellCommandMessage(executable, "Run a non-interactive command or script instead.");
|
|
548
|
-
}
|
|
549
|
-
switch (executable) {
|
|
550
|
-
case "python":
|
|
551
|
-
case "python3":
|
|
552
|
-
case "py": {
|
|
553
|
-
if (!args.length) {
|
|
554
|
-
return buildInteractiveShellCommandMessage(executable, 'Try "python script.py" or "python -c \\"...\\"" instead.');
|
|
555
|
-
}
|
|
556
|
-
if (lowerArgs.includes("-i") || lowerArgs.includes("--interactive")) {
|
|
557
|
-
return buildInteractiveShellCommandMessage(executable, 'Try "python script.py" or "python -c \\"...\\"" instead.');
|
|
558
|
-
}
|
|
559
|
-
if (hasAnyCommandFlag(lowerArgs, ["-c", "-h", "--help", "-v", "--version"])) {
|
|
560
|
-
return null;
|
|
561
|
-
}
|
|
562
|
-
const moduleFlagIndex = findCommandFlagIndex(lowerArgs, ["-m"]);
|
|
563
|
-
if (moduleFlagIndex >= 0) {
|
|
564
|
-
return moduleFlagIndex < args.length - 1
|
|
565
|
-
? null
|
|
566
|
-
: buildInteractiveShellCommandMessage(executable, 'Try "python script.py" or "python -m module_name" instead.');
|
|
567
|
-
}
|
|
568
|
-
return hasScriptLikeArg(args)
|
|
569
|
-
? null
|
|
570
|
-
: buildInteractiveShellCommandMessage(executable, 'Try "python script.py" or "python -c \\"...\\"" instead.');
|
|
571
|
-
}
|
|
572
|
-
case "node":
|
|
573
|
-
if (!args.length || lowerArgs.includes("-i") || lowerArgs.includes("--interactive")) {
|
|
574
|
-
return buildInteractiveShellCommandMessage(executable, 'Try "node script.js" or "node -e \\"...\\"" instead.');
|
|
575
|
-
}
|
|
576
|
-
if (hasAnyCommandFlag(lowerArgs, ["-e", "--eval", "-p", "--print", "-h", "--help", "-v", "--version"])) {
|
|
577
|
-
return null;
|
|
578
|
-
}
|
|
579
|
-
return hasScriptLikeArg(args)
|
|
580
|
-
? null
|
|
581
|
-
: buildInteractiveShellCommandMessage(executable, 'Try "node script.js" or "node -e \\"...\\"" instead.');
|
|
582
|
-
case "cmd":
|
|
583
|
-
if (lowerArgs.includes("/?")) {
|
|
584
|
-
return null;
|
|
585
|
-
}
|
|
586
|
-
if (lowerArgs.includes("/k") || !lowerArgs.includes("/c")) {
|
|
587
|
-
return buildInteractiveShellCommandMessage(executable, 'Try "cmd /c <command>" or run the command directly instead.');
|
|
588
|
-
}
|
|
589
|
-
return null;
|
|
590
|
-
case "powershell":
|
|
591
|
-
case "pwsh":
|
|
592
|
-
if (!args.length ||
|
|
593
|
-
lowerArgs.includes("-noexit") ||
|
|
594
|
-
lowerArgs.includes("-nologo") && args.length === 1) {
|
|
595
|
-
return buildInteractiveShellCommandMessage(executable, `Try "${executable} -Command \\"...\\"" or "${executable} -File script.ps1" instead.`);
|
|
596
|
-
}
|
|
597
|
-
if (hasAnyCommandFlag(lowerArgs, ["-c", "-command", "-enc", "-encodedcommand", "-f", "-file", "-h", "-help", "-v", "-version", "-?"])) {
|
|
598
|
-
return null;
|
|
599
|
-
}
|
|
600
|
-
return hasScriptLikeArg(args)
|
|
601
|
-
? null
|
|
602
|
-
: buildInteractiveShellCommandMessage(executable, `Try "${executable} -Command \\"...\\"" or "${executable} -File script.ps1" instead.`);
|
|
603
|
-
case "bash":
|
|
604
|
-
case "dash":
|
|
605
|
-
case "ksh":
|
|
606
|
-
case "sh":
|
|
607
|
-
case "zsh":
|
|
608
|
-
if (!args.length || lowerArgs.includes("-i")) {
|
|
609
|
-
return buildInteractiveShellCommandMessage(executable, `Try "${executable} -c '...'" or "${executable} script.sh" instead.`);
|
|
610
|
-
}
|
|
611
|
-
if (findCommandFlagIndex(lowerArgs, ["-c", "-lc"]) >= 0) {
|
|
612
|
-
return null;
|
|
613
|
-
}
|
|
614
|
-
if (hasAnyCommandFlag(lowerArgs, ["-h", "--help", "--version"])) {
|
|
615
|
-
return null;
|
|
616
|
-
}
|
|
617
|
-
return hasScriptLikeArg(args)
|
|
618
|
-
? null
|
|
619
|
-
: buildInteractiveShellCommandMessage(executable, `Try "${executable} -c '...'" or "${executable} script.sh" instead.`);
|
|
620
|
-
default:
|
|
621
|
-
return null;
|
|
622
|
-
}
|
|
623
|
-
}
|
|
624
449
|
export function detectCliApproval(text) {
|
|
625
450
|
const normalized = normalizeOutput(text);
|
|
626
451
|
const compact = normalized.replace(/\s+/g, " ").trim();
|
|
@@ -807,27 +632,14 @@ export function formatMirroredUserInputMessage(adapter, text) {
|
|
|
807
632
|
: "Local input";
|
|
808
633
|
return `${label}:\n${truncatePreview(text, 500)}`;
|
|
809
634
|
}
|
|
810
|
-
export function formatFinalReplyMessage(
|
|
811
|
-
|
|
812
|
-
return text;
|
|
813
|
-
}
|
|
814
|
-
// After the early return above, only "shell" remains.
|
|
815
|
-
const label = adapter === "codex"
|
|
816
|
-
? "Codex"
|
|
817
|
-
: adapter === "claude"
|
|
818
|
-
? "Claude"
|
|
819
|
-
: adapter === "opencode"
|
|
820
|
-
? "OpenCode"
|
|
821
|
-
: adapter === "pi"
|
|
822
|
-
? "Pi"
|
|
823
|
-
: adapter;
|
|
824
|
-
return `${label} final reply:\n${text}`;
|
|
635
|
+
export function formatFinalReplyMessage(_adapter, text) {
|
|
636
|
+
return text;
|
|
825
637
|
}
|
|
826
638
|
const OPENCODE_WORKING_NOTICE_RE = /^OpenCode is still working on:\s*$/i;
|
|
827
639
|
const OPENCODE_TRANSIENT_NOTICE_RES = [
|
|
828
640
|
/^Bridge error: opencode companion is not connected\./i,
|
|
829
641
|
/^OpenCode companion is not connected(?: for bridge workspace)?:?$/i,
|
|
830
|
-
/^Run "wechat-
|
|
642
|
+
/^Run "wechat-opencode".*$/i,
|
|
831
643
|
/^OpenCode session switched to \S+ from the local terminal\.$/i,
|
|
832
644
|
/^Local OpenCode input:\s*$/i,
|
|
833
645
|
];
|
|
@@ -922,7 +734,7 @@ export function sanitizeWechatFinalReplyText(adapter, text) {
|
|
|
922
734
|
const resolved = tail || cleaned;
|
|
923
735
|
return isOpenCodeReasoningResidue(resolved) ? "" : resolved;
|
|
924
736
|
}
|
|
925
|
-
function extractInlineWechatAttachments(text) {
|
|
737
|
+
export function extractInlineWechatAttachments(text) {
|
|
926
738
|
const sanitized = text
|
|
927
739
|
.replace(/\\\n\s*/g, "\\")
|
|
928
740
|
.replace(/\.\s*\n?\s*([A-Za-z0-9]{2,8})(?=\?)/g, ".$1")
|
|
@@ -1010,8 +822,9 @@ function inferInlineWechatAttachmentKind(filePath) {
|
|
|
1010
822
|
if (INLINE_VOICE_EXTENSIONS.has(extension)) {
|
|
1011
823
|
return "voice";
|
|
1012
824
|
}
|
|
1013
|
-
//
|
|
1014
|
-
//
|
|
825
|
+
// Ordinary local files stay auto-sendable; only directly executable /
|
|
826
|
+
// installer types are excluded so a path mentioned in prose cannot
|
|
827
|
+
// accidentally ship a runnable binary to a WeChat contact.
|
|
1015
828
|
if (!extension || INLINE_REFERENCE_ONLY_FILE_EXTENSIONS.has(extension)) {
|
|
1016
829
|
return null;
|
|
1017
830
|
}
|
|
@@ -1053,7 +866,7 @@ export function formatApprovalMessage(pending, adapterState) {
|
|
|
1053
866
|
].join("\n");
|
|
1054
867
|
}
|
|
1055
868
|
return [
|
|
1056
|
-
|
|
869
|
+
"CLI approval is required.",
|
|
1057
870
|
`adapter: ${adapterState.kind}`,
|
|
1058
871
|
`summary: ${pending.summary}`,
|
|
1059
872
|
`target: ${pending.commandPreview}`,
|