cli-wechat-bridge 1.1.8 → 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 +21 -7
- package/bin/_run-entry.mjs +6 -1
- package/bin/wechat-check-update.mjs +6 -1
- package/bin/wechat-claude.mjs +6 -1
- package/bin/wechat-codex.mjs +6 -1
- package/bin/wechat-daemon.mjs +6 -1
- package/bin/wechat-opencode.mjs +6 -1
- package/bin/wechat-pi.mjs +6 -1
- package/bin/wechat-setup.mjs +6 -1
- package/bin/wecom-check-update.mjs +6 -1
- package/bin/wecom-claude.mjs +6 -1
- package/bin/wecom-codex.mjs +6 -1
- package/bin/wecom-daemon.mjs +6 -1
- package/bin/wecom-opencode.mjs +6 -1
- package/bin/wecom-pi.mjs +6 -1
- package/bin/wecom-setup.mjs +6 -1
- package/dist/bridge/bridge-adapters.claude.js +6 -0
- package/dist/bridge/bridge-adapters.codex.js +95 -8
- package/dist/bridge/bridge-adapters.core.js +6 -0
- package/dist/bridge/bridge-adapters.opencode.js +148 -19
- package/dist/bridge/bridge-adapters.pi.js +6 -0
- package/dist/bridge/bridge-adapters.shared.js +6 -0
- package/dist/bridge/bridge-controller.js +6 -0
- package/dist/bridge/bridge-final-reply.js +6 -0
- package/dist/bridge/bridge-process-reaper.js +6 -0
- package/dist/bridge/bridge-state.js +6 -0
- package/dist/bridge/bridge-utils.js +6 -31
- package/dist/bridge/claude-hook.js +6 -0
- 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 +18 -0
- package/dist/bridge/opencode-v2-compat.js +450 -0
- package/dist/bridge/wechat-bridge.js +148 -241
- package/dist/bridge/wechat-forwarding.js +6 -0
- package/dist/bridge/wechat-outbound-queue.js +6 -0
- package/dist/channels/wechat/bridge-final-reply.js +13 -22
- package/dist/channels/wechat/index.js +6 -0
- package/dist/channels/wechat/wechat-driver.js +191 -0
- package/dist/channels/wechat/wechat-forwarding.js +18 -0
- package/dist/channels/wechat/wechat-outbound-queue.js +39 -0
- package/dist/channels/wecom/bridge-final-reply.js +6 -0
- package/dist/channels/wecom/setup-cli.js +6 -0
- package/dist/channels/wecom/setup.js +6 -0
- package/dist/channels/wecom/wecom-config.js +6 -0
- package/dist/channels/wecom/wecom-driver.js +106 -0
- package/dist/channels/wecom/wecom-media.js +6 -0
- package/dist/channels/wecom/wecom-transport.js +6 -0
- package/dist/commands/check-update.js +6 -0
- package/dist/companion/codex-remote-client.js +6 -0
- package/dist/companion/codex-visible-client-link.js +6 -0
- package/dist/companion/local-companion-link.js +6 -0
- package/dist/companion/local-companion-start.js +6 -0
- package/dist/companion/local-companion.js +6 -0
- package/dist/companion/opencode-tui-bridge-plugin.js +6 -0
- package/dist/companion/opencode-v2-tui-bridge-plugin.js +93 -0
- package/dist/companion/pi-tui-bridge-extension.js +6 -0
- package/dist/core/bridge-control.js +2 -0
- package/dist/core/bridge-defer.js +25 -0
- package/dist/core/channel-driver.js +1 -0
- package/dist/core/conversation-routing.js +11 -1
- package/dist/core/direct-run.js +6 -0
- package/dist/core/inbound-message-claims.js +6 -0
- package/dist/core/outbound-target.js +32 -0
- package/dist/core/text-utils.js +6 -0
- package/dist/core/turn-coordinator.js +85 -0
- package/dist/daemon/daemon-link.js +6 -0
- package/dist/daemon/emoji-bindings.js +6 -0
- package/dist/daemon/wechat-daemon.js +451 -173
- package/dist/i18n/index.js +6 -0
- package/dist/i18n/messages-en.js +7 -1
- package/dist/i18n/messages-zh.js +7 -1
- package/dist/runtime/create-runtime-host.js +6 -0
- package/dist/utils/atomic-file.js +6 -0
- package/dist/utils/doctor.js +6 -0
- package/dist/utils/version-checker.js +6 -0
- package/dist/wechat/channel-config.js +6 -0
- package/dist/wechat/setup.js +6 -0
- package/dist/wechat/wechat-channel.js +6 -0
- package/dist/wechat/wechat-transport.js +73 -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
|
+
};
|
|
@@ -1,3 +1,9 @@
|
|
|
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.
|
|
1
7
|
import fs from "node:fs";
|
|
2
8
|
import net from "node:net";
|
|
3
9
|
import path from "node:path";
|
|
@@ -34,6 +34,8 @@ export function parseBridgeControlCommand(text) {
|
|
|
34
34
|
if (argument.toLowerCase() === "off")
|
|
35
35
|
return { type: "plan", enabled: false };
|
|
36
36
|
return null;
|
|
37
|
+
case "/all":
|
|
38
|
+
return argument ? { type: "broadcast", text: argument } : null;
|
|
37
39
|
default:
|
|
38
40
|
return null;
|
|
39
41
|
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
export function shouldDeferCodexInboundMessage(params) {
|
|
2
|
+
return (params.adapter === "codex" &&
|
|
3
|
+
!params.hasPendingConfirmation &&
|
|
4
|
+
!params.hasSystemCommand &&
|
|
5
|
+
params.activeTurnOrigin === "local" &&
|
|
6
|
+
(params.status === "busy" || params.status === "awaiting_approval"));
|
|
7
|
+
}
|
|
8
|
+
export function canDrainDeferredCodexInboundQueue(params) {
|
|
9
|
+
return (params.adapter === "codex" &&
|
|
10
|
+
params.deferredCount > 0 &&
|
|
11
|
+
!params.hasPendingConfirmation &&
|
|
12
|
+
!params.hasPendingUserInput &&
|
|
13
|
+
!params.hasPendingApproval &&
|
|
14
|
+
!params.hasActiveTask &&
|
|
15
|
+
!params.activeTurnId &&
|
|
16
|
+
params.status !== "busy" &&
|
|
17
|
+
params.status !== "awaiting_approval" &&
|
|
18
|
+
params.status !== "awaiting_input");
|
|
19
|
+
}
|
|
20
|
+
export function formatDeferredCodexInboundQueueMessage(queuePosition) {
|
|
21
|
+
return `Queued for delivery after the current local Codex turn finishes. Queue position: ${queuePosition}.`;
|
|
22
|
+
}
|
|
23
|
+
export function isRetryableDeferredCodexDrainError(errorText) {
|
|
24
|
+
return /still working|approval request is pending|waiting for local terminal input/i.test(errorText);
|
|
25
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -1,3 +1,9 @@
|
|
|
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.
|
|
1
7
|
import { AsyncLocalStorage } from "node:async_hooks";
|
|
2
8
|
/**
|
|
3
9
|
* Keeps the reply target scoped to one inbound async call chain. Different
|
|
@@ -19,6 +25,7 @@ export function tryBeginTurn(state, task, conversation) {
|
|
|
19
25
|
const lease = {
|
|
20
26
|
task,
|
|
21
27
|
previousConversation: state.activeConversation,
|
|
28
|
+
previousLastConversation: state.lastConversation,
|
|
22
29
|
};
|
|
23
30
|
state.activeTask = task;
|
|
24
31
|
if (conversation) {
|
|
@@ -27,12 +34,15 @@ export function tryBeginTurn(state, task, conversation) {
|
|
|
27
34
|
}
|
|
28
35
|
return lease;
|
|
29
36
|
}
|
|
30
|
-
export function rollbackTurn(state, lease) {
|
|
37
|
+
export function rollbackTurn(state, lease, options = {}) {
|
|
31
38
|
if (state.activeTask !== lease.task) {
|
|
32
39
|
return false;
|
|
33
40
|
}
|
|
34
41
|
state.activeTask = null;
|
|
35
42
|
state.activeConversation = lease.previousConversation;
|
|
43
|
+
if (options.restoreLastConversation) {
|
|
44
|
+
state.lastConversation = lease.previousLastConversation;
|
|
45
|
+
}
|
|
36
46
|
return true;
|
|
37
47
|
}
|
|
38
48
|
export function clearTurn(state, expectedTask) {
|
package/dist/core/direct-run.js
CHANGED
|
@@ -1,3 +1,9 @@
|
|
|
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.
|
|
1
7
|
import path from "node:path";
|
|
2
8
|
import { fileURLToPath } from "node:url";
|
|
3
9
|
function normalizeComparablePath(filePath) {
|
|
@@ -1,3 +1,9 @@
|
|
|
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.
|
|
1
7
|
import crypto from "node:crypto";
|
|
2
8
|
import fs from "node:fs";
|
|
3
9
|
import path from "node:path";
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Outbound target priority for standalone bridges:
|
|
3
|
+
* 1. an explicit override captured at enqueue time;
|
|
4
|
+
* 2. for the operator on multi-conversation channels, the pre-resolved turn
|
|
5
|
+
* target (callers pass `TurnCoordinator.resolveTarget(operatorConversation)`);
|
|
6
|
+
* 3. otherwise a direct conversation to the sender.
|
|
7
|
+
*/
|
|
8
|
+
export function resolveOutboundConversationTarget(params) {
|
|
9
|
+
if (params.override) {
|
|
10
|
+
return params.override;
|
|
11
|
+
}
|
|
12
|
+
if (params.multiConversation && params.senderId === params.operatorId) {
|
|
13
|
+
return params.operatorTarget;
|
|
14
|
+
}
|
|
15
|
+
return params.directConversation(params.senderId);
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Outbound target priority for daemon sends. Multi-conversation operators
|
|
19
|
+
* resolve through the current inbound conversation first, then the active
|
|
20
|
+
* slot's remembered target, then the daemon fallback conversation.
|
|
21
|
+
*/
|
|
22
|
+
export function resolveDaemonOutboundTarget(params) {
|
|
23
|
+
if (params.override) {
|
|
24
|
+
return params.override;
|
|
25
|
+
}
|
|
26
|
+
if (params.multiConversation && params.senderId === params.operatorId) {
|
|
27
|
+
return params.inboundConversation
|
|
28
|
+
?? params.activeSlotTarget
|
|
29
|
+
?? params.fallbackConversation;
|
|
30
|
+
}
|
|
31
|
+
return params.directConversation(params.senderId);
|
|
32
|
+
}
|
package/dist/core/text-utils.js
CHANGED
|
@@ -1,3 +1,9 @@
|
|
|
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.
|
|
1
7
|
const ANSI_ESCAPE_RE =
|
|
2
8
|
// eslint-disable-next-line no-control-regex
|
|
3
9
|
/\x1b(?:[@-Z\\-_]|\[[0-?]*[ -/]*[@-~])/g;
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import { clearTurn, resolveConversationTarget, rollbackTurn, tryBeginTurn, } from "./conversation-routing.js";
|
|
2
|
+
/**
|
|
3
|
+
* Owns the active remote turn for one dispatch surface: a standalone bridge
|
|
4
|
+
* or one daemon adapter slot. Wraps the turn ownership state machine (busy
|
|
5
|
+
* rejection, dispatch rollback, conditional completion) so standalone and
|
|
6
|
+
* daemon share identical gating semantics instead of re-implementing them.
|
|
7
|
+
*/
|
|
8
|
+
export class TurnCoordinator {
|
|
9
|
+
state;
|
|
10
|
+
restoreLastConversationOnFailure;
|
|
11
|
+
constructor(options = {}) {
|
|
12
|
+
this.state = {
|
|
13
|
+
activeTask: null,
|
|
14
|
+
lastConversation: options.initialLastConversation ?? null,
|
|
15
|
+
};
|
|
16
|
+
this.restoreLastConversationOnFailure = options.restoreLastConversationOnFailure ?? false;
|
|
17
|
+
}
|
|
18
|
+
get activeTask() {
|
|
19
|
+
return this.state.activeTask;
|
|
20
|
+
}
|
|
21
|
+
get hasActiveTask() {
|
|
22
|
+
return this.state.activeTask !== null;
|
|
23
|
+
}
|
|
24
|
+
get activeConversation() {
|
|
25
|
+
return this.state.activeConversation;
|
|
26
|
+
}
|
|
27
|
+
get lastConversation() {
|
|
28
|
+
return this.state.lastConversation;
|
|
29
|
+
}
|
|
30
|
+
beginTurn(task, conversation) {
|
|
31
|
+
return tryBeginTurn(this.state, task, conversation);
|
|
32
|
+
}
|
|
33
|
+
rollback(lease, options = {}) {
|
|
34
|
+
return rollbackTurn(this.state, lease, options);
|
|
35
|
+
}
|
|
36
|
+
complete(expectedTask) {
|
|
37
|
+
return clearTurn(this.state, expectedTask);
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Replaces the active task unconditionally. Used when a command path
|
|
41
|
+
* (approval confirmation, resume) takes over a continuing turn rather
|
|
42
|
+
* than dispatching fresh input.
|
|
43
|
+
*/
|
|
44
|
+
setActiveTask(task) {
|
|
45
|
+
this.state.activeTask = task;
|
|
46
|
+
}
|
|
47
|
+
/** Binds a conversation as both active and remembered target. */
|
|
48
|
+
bindConversation(conversation) {
|
|
49
|
+
this.state.activeConversation = conversation;
|
|
50
|
+
this.state.lastConversation = conversation;
|
|
51
|
+
}
|
|
52
|
+
/** Records a conversation as the remembered fallback target only. */
|
|
53
|
+
observeConversation(conversation) {
|
|
54
|
+
this.state.lastConversation = conversation;
|
|
55
|
+
}
|
|
56
|
+
resolveTarget(fallback) {
|
|
57
|
+
return resolveConversationTarget({
|
|
58
|
+
active: this.state.activeConversation,
|
|
59
|
+
last: this.state.lastConversation,
|
|
60
|
+
fallback,
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Busy-rejecting dispatch transaction: acquire the turn lease, forward the
|
|
65
|
+
* input, and roll the lease back when forwarding throws. A busy input runs
|
|
66
|
+
* `onBusy` (reminder) and returns without touching ownership.
|
|
67
|
+
*/
|
|
68
|
+
async dispatch(params) {
|
|
69
|
+
const lease = this.beginTurn(params.task, params.conversation);
|
|
70
|
+
if (!lease) {
|
|
71
|
+
await params.onBusy?.();
|
|
72
|
+
return { status: "busy" };
|
|
73
|
+
}
|
|
74
|
+
try {
|
|
75
|
+
await params.forward();
|
|
76
|
+
}
|
|
77
|
+
catch (error) {
|
|
78
|
+
this.rollback(lease, {
|
|
79
|
+
restoreLastConversation: this.restoreLastConversationOnFailure,
|
|
80
|
+
});
|
|
81
|
+
throw error;
|
|
82
|
+
}
|
|
83
|
+
return { status: "dispatched", task: params.task };
|
|
84
|
+
}
|
|
85
|
+
}
|
|
@@ -1,3 +1,9 @@
|
|
|
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.
|
|
1
7
|
import crypto from "node:crypto";
|
|
2
8
|
import fs from "node:fs";
|
|
3
9
|
import net from "node:net";
|
|
@@ -1,3 +1,9 @@
|
|
|
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.
|
|
1
7
|
import fs from "node:fs";
|
|
2
8
|
import { EMOJI_BINDINGS_FILE } from "../wechat/channel-config.js";
|
|
3
9
|
import { t } from "../i18n/index.js";
|