opencode-copilot-account-switcher 0.14.5 → 0.14.7
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/dist/wechat/bind-flow.js +35 -23
- package/dist/wechat/compat/openclaw-account-helpers.d.ts +29 -0
- package/dist/wechat/compat/openclaw-account-helpers.js +71 -0
- package/dist/wechat/compat/openclaw-guided-smoke.d.ts +1 -0
- package/dist/wechat/compat/openclaw-guided-smoke.js +30 -31
- package/dist/wechat/compat/openclaw-public-entry.d.ts +33 -0
- package/dist/wechat/compat/openclaw-public-entry.js +73 -0
- package/dist/wechat/compat/openclaw-public-helpers.d.ts +31 -72
- package/dist/wechat/compat/openclaw-public-helpers.js +36 -230
- package/dist/wechat/compat/openclaw-qr-gateway.d.ts +15 -0
- package/dist/wechat/compat/openclaw-qr-gateway.js +39 -0
- package/dist/wechat/compat/openclaw-sync-buf.d.ts +24 -0
- package/dist/wechat/compat/openclaw-sync-buf.js +86 -0
- package/dist/wechat/compat/openclaw-updates-send.d.ts +47 -0
- package/dist/wechat/compat/openclaw-updates-send.js +49 -0
- package/dist/wechat/openclaw-account-adapter.js +9 -19
- package/package.json +3 -3
package/dist/wechat/bind-flow.js
CHANGED
|
@@ -18,10 +18,10 @@ function toErrorMessage(error) {
|
|
|
18
18
|
return String(error);
|
|
19
19
|
}
|
|
20
20
|
function pickQrTerminal(value) {
|
|
21
|
-
return pickFirstNonEmptyString(value?.
|
|
21
|
+
return pickFirstNonEmptyString(value?.qrTerminal);
|
|
22
22
|
}
|
|
23
23
|
function pickQrUrl(value) {
|
|
24
|
-
return pickFirstNonEmptyString(value?.qrDataUrl
|
|
24
|
+
return pickFirstNonEmptyString(value?.qrDataUrl);
|
|
25
25
|
}
|
|
26
26
|
function isTimeoutWaitResult(value) {
|
|
27
27
|
return Boolean(value && typeof value === "object" && "status" in value && String(value.status) === "timeout");
|
|
@@ -33,6 +33,12 @@ async function rollbackBinding(action, previousOperatorBinding, persistOperatorR
|
|
|
33
33
|
}
|
|
34
34
|
await clearOperatorBinding().catch(() => { });
|
|
35
35
|
}
|
|
36
|
+
function isSameOperatorBinding(left, right) {
|
|
37
|
+
if (!left || !right) {
|
|
38
|
+
return false;
|
|
39
|
+
}
|
|
40
|
+
return left.wechatAccountId === right.wechatAccountId && left.userId === right.userId && left.boundAt === right.boundAt;
|
|
41
|
+
}
|
|
36
42
|
async function renderQrTerminalDefault(input) {
|
|
37
43
|
return await new Promise((resolve) => {
|
|
38
44
|
qrcodeTerminal.generate(input.value, { small: true }, (output) => {
|
|
@@ -57,7 +63,10 @@ export async function runWechatBindFlow(input) {
|
|
|
57
63
|
const qrTerminal = pickQrTerminal(started);
|
|
58
64
|
const qrUrl = pickQrUrl(started);
|
|
59
65
|
const qrStartMessage = pickFirstNonEmptyString(started?.message, started?.detail, started?.reason);
|
|
60
|
-
const sessionKey = pickFirstNonEmptyString(started?.sessionKey
|
|
66
|
+
const sessionKey = pickFirstNonEmptyString(started?.sessionKey);
|
|
67
|
+
if (!sessionKey) {
|
|
68
|
+
throw new Error("missing sessionKey from qr start");
|
|
69
|
+
}
|
|
61
70
|
if (qrTerminal) {
|
|
62
71
|
await writeLine(qrTerminal);
|
|
63
72
|
}
|
|
@@ -78,49 +87,44 @@ export async function runWechatBindFlow(input) {
|
|
|
78
87
|
if (waited && typeof waited === "object" && "connected" in waited && waited.connected === false) {
|
|
79
88
|
throw new Error("qr login did not complete");
|
|
80
89
|
}
|
|
81
|
-
const accountId = pickFirstNonEmptyString(waited?.accountId
|
|
90
|
+
const accountId = pickFirstNonEmptyString(waited?.accountId);
|
|
82
91
|
if (!accountId) {
|
|
83
92
|
throw new Error("missing accountId after qr login");
|
|
84
93
|
}
|
|
85
94
|
const boundAt = now();
|
|
86
|
-
const userIdFromWait = pickFirstNonEmptyString(waited?.userId, waited?.openid, waited?.uid);
|
|
87
95
|
const previousOperatorBinding = input.action === "wechat-rebind" ? await loadOperatorBinding() : undefined;
|
|
96
|
+
const userIdFromWait = pickFirstNonEmptyString(waited?.userId);
|
|
88
97
|
let menuAccount;
|
|
89
98
|
let boundUserId = "";
|
|
90
99
|
let shouldRollbackBinding = false;
|
|
100
|
+
let attemptedOperatorBinding;
|
|
91
101
|
try {
|
|
92
|
-
const menuAccountState =
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
baseUrl: "https://ilinkai.weixin.qq.com",
|
|
98
|
-
}),
|
|
99
|
-
accountId,
|
|
100
|
-
...(userIdFromWait ? { userId: userIdFromWait } : {}),
|
|
101
|
-
boundAt,
|
|
102
|
-
}
|
|
103
|
-
: helpers.latestAccountState;
|
|
102
|
+
const menuAccountState = {
|
|
103
|
+
accountId,
|
|
104
|
+
token: "",
|
|
105
|
+
baseUrl: "https://ilinkai.weixin.qq.com",
|
|
106
|
+
};
|
|
104
107
|
menuAccount = await buildOpenClawMenuAccount({
|
|
105
108
|
latestAccountState: menuAccountState,
|
|
106
109
|
accountHelpers: helpers.accountHelpers,
|
|
107
110
|
});
|
|
108
|
-
const userId = pickFirstNonEmptyString(
|
|
111
|
+
const userId = pickFirstNonEmptyString(menuAccount?.userId, userIdFromWait);
|
|
109
112
|
if (!userId) {
|
|
110
113
|
throw new Error("missing userId after qr login");
|
|
111
114
|
}
|
|
112
115
|
boundUserId = userId;
|
|
113
|
-
|
|
116
|
+
attemptedOperatorBinding = {
|
|
114
117
|
wechatAccountId: accountId,
|
|
115
118
|
userId,
|
|
116
119
|
boundAt,
|
|
117
120
|
};
|
|
118
|
-
shouldRollbackBinding = true;
|
|
119
121
|
if (input.action === "wechat-rebind") {
|
|
120
|
-
|
|
122
|
+
shouldRollbackBinding = true;
|
|
123
|
+
await persistOperatorRebinding(attemptedOperatorBinding);
|
|
121
124
|
}
|
|
122
125
|
else {
|
|
123
|
-
|
|
126
|
+
shouldRollbackBinding = true;
|
|
127
|
+
await persistOperatorBinding(attemptedOperatorBinding);
|
|
124
128
|
}
|
|
125
129
|
const settings = await input.readCommonSettings();
|
|
126
130
|
const notifications = settings.wechat?.notifications ?? {
|
|
@@ -145,7 +149,15 @@ export async function runWechatBindFlow(input) {
|
|
|
145
149
|
}
|
|
146
150
|
catch (error) {
|
|
147
151
|
if (shouldRollbackBinding) {
|
|
148
|
-
|
|
152
|
+
if (input.action === "wechat-bind") {
|
|
153
|
+
const currentOperatorBinding = await loadOperatorBinding().catch(() => undefined);
|
|
154
|
+
if (isSameOperatorBinding(currentOperatorBinding, attemptedOperatorBinding)) {
|
|
155
|
+
await rollbackBinding(input.action, previousOperatorBinding, persistOperatorRebinding, clearOperatorBinding);
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
else {
|
|
159
|
+
await rollbackBinding(input.action, previousOperatorBinding, persistOperatorRebinding, clearOperatorBinding);
|
|
160
|
+
}
|
|
149
161
|
}
|
|
150
162
|
throw error;
|
|
151
163
|
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
export type WeixinAccountHelpers = {
|
|
2
|
+
listAccountIds: () => Promise<string[]>;
|
|
3
|
+
resolveAccount: (accountId: string) => Promise<{
|
|
4
|
+
accountId: string;
|
|
5
|
+
enabled: boolean;
|
|
6
|
+
configured: boolean;
|
|
7
|
+
name?: string;
|
|
8
|
+
userId?: string;
|
|
9
|
+
}>;
|
|
10
|
+
describeAccount: (accountIdOrInput: string | {
|
|
11
|
+
accountId: string;
|
|
12
|
+
}) => Promise<{
|
|
13
|
+
accountId: string;
|
|
14
|
+
enabled: boolean;
|
|
15
|
+
configured: boolean;
|
|
16
|
+
name?: string;
|
|
17
|
+
userId?: string;
|
|
18
|
+
}>;
|
|
19
|
+
};
|
|
20
|
+
type OpenClawAccountSourceHelpers = {
|
|
21
|
+
listAccountIds: () => string[] | Promise<string[]>;
|
|
22
|
+
loadAccount: (accountId: string) => unknown | Promise<unknown>;
|
|
23
|
+
resolveAccount: (accountId: string) => unknown | Promise<unknown>;
|
|
24
|
+
};
|
|
25
|
+
export declare function createOpenClawAccountHelpers(input: OpenClawAccountSourceHelpers): WeixinAccountHelpers;
|
|
26
|
+
export declare function loadOpenClawAccountHelpers(options?: {
|
|
27
|
+
accountsModulePath?: string;
|
|
28
|
+
}): Promise<WeixinAccountHelpers>;
|
|
29
|
+
export {};
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { createRequire } from "node:module";
|
|
2
|
+
import { createJiti } from "jiti";
|
|
3
|
+
const OPENCLAW_WEIXIN_ACCOUNTS_MODULE = "@tencent-weixin/openclaw-weixin/src/auth/accounts.ts";
|
|
4
|
+
let accountJitiLoader = null;
|
|
5
|
+
function getAccountJiti() {
|
|
6
|
+
if (accountJitiLoader) {
|
|
7
|
+
return accountJitiLoader;
|
|
8
|
+
}
|
|
9
|
+
accountJitiLoader = createJiti(import.meta.url, {
|
|
10
|
+
interopDefault: true,
|
|
11
|
+
extensions: [".ts", ".tsx", ".mts", ".cts", ".js", ".mjs", ".cjs", ".json"],
|
|
12
|
+
});
|
|
13
|
+
return accountJitiLoader;
|
|
14
|
+
}
|
|
15
|
+
function asObject(value) {
|
|
16
|
+
return value && typeof value === "object" ? value : {};
|
|
17
|
+
}
|
|
18
|
+
function deriveEnabled(resolved) {
|
|
19
|
+
return resolved.enabled === true;
|
|
20
|
+
}
|
|
21
|
+
function isConfigured(resolved) {
|
|
22
|
+
return resolved.configured === true;
|
|
23
|
+
}
|
|
24
|
+
function toAccountId(input) {
|
|
25
|
+
return typeof input === "string" ? input : input.accountId;
|
|
26
|
+
}
|
|
27
|
+
export function createOpenClawAccountHelpers(input) {
|
|
28
|
+
const resolveStableAccount = async (accountId) => {
|
|
29
|
+
const resolved = asObject(await input.resolveAccount(accountId));
|
|
30
|
+
const stored = asObject(await input.loadAccount(accountId));
|
|
31
|
+
return {
|
|
32
|
+
accountId,
|
|
33
|
+
enabled: deriveEnabled(resolved),
|
|
34
|
+
configured: isConfigured(resolved),
|
|
35
|
+
name: typeof resolved.name === "string" ? resolved.name : undefined,
|
|
36
|
+
userId: typeof stored.userId === "string" ? stored.userId : undefined,
|
|
37
|
+
};
|
|
38
|
+
};
|
|
39
|
+
return {
|
|
40
|
+
async listAccountIds() {
|
|
41
|
+
const ids = await input.listAccountIds();
|
|
42
|
+
return Array.isArray(ids) ? ids.filter((it) => typeof it === "string" && it.length > 0) : [];
|
|
43
|
+
},
|
|
44
|
+
async resolveAccount(accountId) {
|
|
45
|
+
return resolveStableAccount(accountId);
|
|
46
|
+
},
|
|
47
|
+
async describeAccount(accountIdOrInput) {
|
|
48
|
+
return resolveStableAccount(toAccountId(accountIdOrInput));
|
|
49
|
+
},
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
export async function loadOpenClawAccountHelpers(options = {}) {
|
|
53
|
+
const require = createRequire(import.meta.url);
|
|
54
|
+
const accountsModulePath = require.resolve(options.accountsModulePath ?? OPENCLAW_WEIXIN_ACCOUNTS_MODULE);
|
|
55
|
+
const accountsModule = getAccountJiti()(accountsModulePath);
|
|
56
|
+
if (typeof accountsModule.listIndexedWeixinAccountIds !== "function" || typeof accountsModule.loadWeixinAccount !== "function") {
|
|
57
|
+
throw new Error("[wechat-compat] account source helper unavailable");
|
|
58
|
+
}
|
|
59
|
+
return createOpenClawAccountHelpers({
|
|
60
|
+
listAccountIds: () => accountsModule.listIndexedWeixinAccountIds(),
|
|
61
|
+
loadAccount: (accountId) => accountsModule.loadWeixinAccount(accountId),
|
|
62
|
+
resolveAccount: async (accountId) => {
|
|
63
|
+
const stored = asObject(await accountsModule.loadWeixinAccount(accountId));
|
|
64
|
+
return {
|
|
65
|
+
accountId,
|
|
66
|
+
enabled: stored.enabled === false ? false : true,
|
|
67
|
+
configured: typeof stored.token === "string" && stored.token.trim().length > 0,
|
|
68
|
+
};
|
|
69
|
+
},
|
|
70
|
+
});
|
|
71
|
+
}
|
|
@@ -102,6 +102,7 @@ export type RunGuidedSmokeOptions = {
|
|
|
102
102
|
publicHelpersOptions?: OpenClawWeixinPublicHelpersLoaderOptions;
|
|
103
103
|
slashCaptureWaitTimeoutMs?: number;
|
|
104
104
|
slashCapturePollIntervalMs?: number;
|
|
105
|
+
writeLine?: (line: string) => Promise<void> | void;
|
|
105
106
|
};
|
|
106
107
|
type PreflightData = {
|
|
107
108
|
runId: string;
|
|
@@ -43,8 +43,11 @@ const DEFAULT_KEY_FIELDS_CHECK = {
|
|
|
43
43
|
const DEFAULT_SLASH_CAPTURE_WAIT_TIMEOUT_MS = 180_000;
|
|
44
44
|
const DEFAULT_SLASH_CAPTURE_POLL_INTERVAL_MS = 2_000;
|
|
45
45
|
const DEFAULT_PUBLIC_GET_UPDATES_LONG_POLL_TIMEOUT_MS = 35_000;
|
|
46
|
-
function
|
|
47
|
-
process.stdout.write(`${
|
|
46
|
+
async function writeLineDefault(line) {
|
|
47
|
+
process.stdout.write(`${line}\n`);
|
|
48
|
+
}
|
|
49
|
+
async function printGuidedPrompt(message, writeLine) {
|
|
50
|
+
await writeLine(message);
|
|
48
51
|
}
|
|
49
52
|
function createRunId() {
|
|
50
53
|
return new Date().toISOString().replace(/[:.]/g, "-");
|
|
@@ -390,15 +393,18 @@ async function runQrLoginDefault(input) {
|
|
|
390
393
|
timeoutMs: input.waitTimeoutMs,
|
|
391
394
|
verbose: false,
|
|
392
395
|
}));
|
|
393
|
-
const qrTerminal = pickFirstString(startResult, ["
|
|
394
|
-
const qrUrl = pickFirstString(startResult, ["qrDataUrl"
|
|
395
|
-
const sessionKey = pickFirstString(startResult, ["sessionKey"
|
|
396
|
+
const qrTerminal = pickFirstString(startResult, ["qrTerminal"]);
|
|
397
|
+
const qrUrl = pickFirstString(startResult, ["qrDataUrl"]);
|
|
398
|
+
const sessionKey = pickFirstString(startResult, ["sessionKey"]);
|
|
396
399
|
const qrStartMessage = pickFirstString(startResult, ["message", "detail", "reason"]);
|
|
400
|
+
if (!sessionKey) {
|
|
401
|
+
throw new Error("missing sessionKey from qr start");
|
|
402
|
+
}
|
|
397
403
|
if (qrTerminal) {
|
|
398
|
-
|
|
404
|
+
await input.writeLine(qrTerminal);
|
|
399
405
|
}
|
|
400
406
|
else if (qrUrl) {
|
|
401
|
-
|
|
407
|
+
await input.writeLine(`QR URL fallback: ${qrUrl}`);
|
|
402
408
|
}
|
|
403
409
|
else {
|
|
404
410
|
throw new Error(qrStartMessage || "invalid qr login result: missing qr code or qr url");
|
|
@@ -625,7 +631,7 @@ async function runDefaultNonSlashVerification(input) {
|
|
|
625
631
|
};
|
|
626
632
|
}
|
|
627
633
|
export const runDefaultNonSlashVerificationForTest = runDefaultNonSlashVerification;
|
|
628
|
-
async function runSlashSampling(run, captureSlashInbound) {
|
|
634
|
+
async function runSlashSampling(run, captureSlashInbound, writeLine) {
|
|
629
635
|
const harness = createOpenClawSmokeHarness({ mode: "real-account" });
|
|
630
636
|
for (const step of SLASH_SAMPLE_STEPS) {
|
|
631
637
|
const inbound = await captureSlashInbound(step.command);
|
|
@@ -648,13 +654,13 @@ async function runSlashSampling(run, captureSlashInbound) {
|
|
|
648
654
|
};
|
|
649
655
|
}
|
|
650
656
|
if (step.command === "status") {
|
|
651
|
-
printGuidedPrompt("已收到 `/status`,下一步请发送 `/reply smoke`");
|
|
657
|
+
await printGuidedPrompt("已收到 `/status`,下一步请发送 `/reply smoke`", writeLine);
|
|
652
658
|
}
|
|
653
659
|
else if (step.command === "reply") {
|
|
654
|
-
printGuidedPrompt("已收到 `/reply smoke`,下一步请发送 `/allow once`");
|
|
660
|
+
await printGuidedPrompt("已收到 `/reply smoke`,下一步请发送 `/allow once`", writeLine);
|
|
655
661
|
}
|
|
656
662
|
else {
|
|
657
|
-
printGuidedPrompt("已收到 `/allow once`,下一步开始发送 10 条普通文本");
|
|
663
|
+
await printGuidedPrompt("已收到 `/allow once`,下一步开始发送 10 条普通文本", writeLine);
|
|
658
664
|
}
|
|
659
665
|
}
|
|
660
666
|
return { ok: true };
|
|
@@ -689,23 +695,13 @@ function normalizeQrLoginResult(result) {
|
|
|
689
695
|
return null;
|
|
690
696
|
}
|
|
691
697
|
if (candidate.status === "success") {
|
|
692
|
-
if (
|
|
693
|
-
return
|
|
694
|
-
status: "timeout",
|
|
695
|
-
qrPrinted: candidate.qrPrinted === true,
|
|
696
|
-
qrUrl: typeof candidate.qrDataUrl === "string" && candidate.qrDataUrl.trim().length > 0
|
|
697
|
-
? candidate.qrDataUrl
|
|
698
|
-
: typeof candidate.qrUrl === "string"
|
|
699
|
-
? candidate.qrUrl
|
|
700
|
-
: undefined,
|
|
701
|
-
};
|
|
698
|
+
if (candidate.connected !== true) {
|
|
699
|
+
return null;
|
|
702
700
|
}
|
|
703
701
|
const hasPrintedQr = candidate.qrPrinted === true;
|
|
704
702
|
const qrUrl = typeof candidate.qrDataUrl === "string" && candidate.qrDataUrl.trim().length > 0
|
|
705
703
|
? candidate.qrDataUrl
|
|
706
|
-
:
|
|
707
|
-
? candidate.qrUrl
|
|
708
|
-
: undefined;
|
|
704
|
+
: undefined;
|
|
709
705
|
const hasQrUrl = Boolean(qrUrl);
|
|
710
706
|
if (!hasPrintedQr && !hasQrUrl) {
|
|
711
707
|
return null;
|
|
@@ -713,9 +709,7 @@ function normalizeQrLoginResult(result) {
|
|
|
713
709
|
}
|
|
714
710
|
const normalizedQrUrl = typeof candidate.qrDataUrl === "string" && candidate.qrDataUrl.trim().length > 0
|
|
715
711
|
? candidate.qrDataUrl
|
|
716
|
-
:
|
|
717
|
-
? candidate.qrUrl
|
|
718
|
-
: undefined;
|
|
712
|
+
: undefined;
|
|
719
713
|
return {
|
|
720
714
|
status: candidate.status,
|
|
721
715
|
connected: candidate.connected === true ? true : undefined,
|
|
@@ -819,6 +813,7 @@ async function completeWithNoGoFinalEvidence(run, input, goNoGoDocPath) {
|
|
|
819
813
|
export async function runGuidedSmoke(options = {}) {
|
|
820
814
|
const run = createGuidedSmokeRun(options);
|
|
821
815
|
const waitTimeoutMs = options.qrWaitTimeoutMs ?? DEFAULT_QR_WAIT_TIMEOUT_MS;
|
|
816
|
+
const writeLine = options.writeLine ?? writeLineDefault;
|
|
822
817
|
const slashCaptureState = {};
|
|
823
818
|
const loadOpenClawPublicHelpers = options.loadOpenClawWeixinPublicHelpers ?? loadOpenClawWeixinPublicHelpers;
|
|
824
819
|
let cachedPublicHelpers = null;
|
|
@@ -839,7 +834,11 @@ export async function runGuidedSmoke(options = {}) {
|
|
|
839
834
|
const getDependencyVersions = options.getDependencyVersions ?? resolveDependencyVersionsFromPackageJson;
|
|
840
835
|
const runQrLogin = options.runQrLogin ?? (async (input) => {
|
|
841
836
|
const helpers = await getPublicHelpers();
|
|
842
|
-
return runQrLoginDefault({
|
|
837
|
+
return runQrLoginDefault({
|
|
838
|
+
...input,
|
|
839
|
+
qrGateway: helpers.qrGateway,
|
|
840
|
+
writeLine,
|
|
841
|
+
});
|
|
843
842
|
});
|
|
844
843
|
const captureSlashInbound = options.captureSlashInbound ?? ((command) => captureSlashInboundDefault(command, {
|
|
845
844
|
state: slashCaptureState,
|
|
@@ -957,7 +956,7 @@ export async function runGuidedSmoke(options = {}) {
|
|
|
957
956
|
}, goNoGoDocPath);
|
|
958
957
|
}
|
|
959
958
|
await writeLoginSuccessEvidence(run, "success", waitTimeoutMs);
|
|
960
|
-
printGuidedPrompt("二维码登录成功,下一步请发送 `/status`");
|
|
959
|
+
await printGuidedPrompt("二维码登录成功,下一步请发送 `/status`", writeLine);
|
|
961
960
|
}
|
|
962
961
|
catch (error) {
|
|
963
962
|
const detail = error instanceof Error ? error.message : String(error);
|
|
@@ -971,7 +970,7 @@ export async function runGuidedSmoke(options = {}) {
|
|
|
971
970
|
}
|
|
972
971
|
let slashSampling;
|
|
973
972
|
try {
|
|
974
|
-
slashSampling = await runSlashSampling(run, captureSlashInbound);
|
|
973
|
+
slashSampling = await runSlashSampling(run, captureSlashInbound, writeLine);
|
|
975
974
|
}
|
|
976
975
|
catch (error) {
|
|
977
976
|
return failWithFinalEvidence(run, {
|
|
@@ -1053,7 +1052,7 @@ export async function runGuidedSmoke(options = {}) {
|
|
|
1053
1052
|
keyFields: nonSlashVerification.keyFieldsCheck,
|
|
1054
1053
|
}, goNoGoDocPath);
|
|
1055
1054
|
}
|
|
1056
|
-
printGuidedPrompt(`普通文本验证进度:${index + 1}/${attempts.length}
|
|
1055
|
+
await printGuidedPrompt(`普通文本验证进度:${index + 1}/${attempts.length}`, writeLine);
|
|
1057
1056
|
}
|
|
1058
1057
|
const nonSlashPassed = nonSlashVerification.passed === 10 && nonSlashVerification.total === 10;
|
|
1059
1058
|
if (!nonSlashPassed) {
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
type CompatHostApi = {
|
|
2
|
+
runtime?: {
|
|
3
|
+
channelRuntime?: unknown;
|
|
4
|
+
gateway?: {
|
|
5
|
+
startAccount?: unknown;
|
|
6
|
+
};
|
|
7
|
+
};
|
|
8
|
+
registerChannel?: (input: unknown) => void;
|
|
9
|
+
registerCli?: (handler: unknown, options?: unknown) => void;
|
|
10
|
+
};
|
|
11
|
+
type OpenClawWeixinPlugin = {
|
|
12
|
+
id?: string;
|
|
13
|
+
register(api: CompatHostApi): void;
|
|
14
|
+
};
|
|
15
|
+
export type OpenClawWeixinPublicEntry = {
|
|
16
|
+
packageJsonPath: string;
|
|
17
|
+
packageRoot: string;
|
|
18
|
+
extensions: string[];
|
|
19
|
+
entryRelativePath: string;
|
|
20
|
+
entryAbsolutePath: string;
|
|
21
|
+
};
|
|
22
|
+
export declare function resolveOpenClawWeixinPublicEntry(): Promise<OpenClawWeixinPublicEntry>;
|
|
23
|
+
export declare function loadOpenClawWeixinDefaultExport(): Promise<OpenClawWeixinPlugin>;
|
|
24
|
+
export declare function loadRegisteredWeixinPluginPayloads(): Promise<Array<{
|
|
25
|
+
plugin?: unknown;
|
|
26
|
+
}>>;
|
|
27
|
+
export declare function loadRegisteredWeixinPluginContext(): Promise<{
|
|
28
|
+
pluginId: string;
|
|
29
|
+
payloads: Array<{
|
|
30
|
+
plugin?: unknown;
|
|
31
|
+
}>;
|
|
32
|
+
}>;
|
|
33
|
+
export {};
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { readFile } from "node:fs/promises";
|
|
2
|
+
import { createRequire } from "node:module";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import { createJiti } from "jiti";
|
|
5
|
+
let publicJitiLoader = null;
|
|
6
|
+
function requireField(condition, message) {
|
|
7
|
+
if (!condition) {
|
|
8
|
+
throw new Error(`[wechat-compat] ${message}`);
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
function getPublicJiti() {
|
|
12
|
+
if (publicJitiLoader) {
|
|
13
|
+
return publicJitiLoader;
|
|
14
|
+
}
|
|
15
|
+
publicJitiLoader = createJiti(import.meta.url, {
|
|
16
|
+
interopDefault: true,
|
|
17
|
+
extensions: [".ts", ".tsx", ".mts", ".cts", ".js", ".mjs", ".cjs", ".json"],
|
|
18
|
+
});
|
|
19
|
+
return publicJitiLoader;
|
|
20
|
+
}
|
|
21
|
+
export async function resolveOpenClawWeixinPublicEntry() {
|
|
22
|
+
const require = createRequire(import.meta.url);
|
|
23
|
+
const packageName = "@tencent-weixin/openclaw-weixin";
|
|
24
|
+
const packageJsonPath = require.resolve(`${packageName}/package.json`);
|
|
25
|
+
const packageJsonRaw = await readFile(packageJsonPath, "utf8");
|
|
26
|
+
const packageJson = JSON.parse(packageJsonRaw);
|
|
27
|
+
const extensions = Array.isArray(packageJson.openclaw?.extensions)
|
|
28
|
+
? packageJson.openclaw?.extensions.filter((it) => typeof it === "string")
|
|
29
|
+
: [];
|
|
30
|
+
requireField(extensions.length > 0, `${packageName} openclaw.extensions[0] is required`);
|
|
31
|
+
const entryRelativePath = extensions[0];
|
|
32
|
+
requireField(Boolean(entryRelativePath?.startsWith("./")), `${packageName} openclaw.extensions[0] must start with ./`);
|
|
33
|
+
const packageRoot = path.dirname(packageJsonPath);
|
|
34
|
+
const entryAbsolutePath = path.resolve(packageRoot, entryRelativePath);
|
|
35
|
+
return {
|
|
36
|
+
packageJsonPath,
|
|
37
|
+
packageRoot,
|
|
38
|
+
extensions,
|
|
39
|
+
entryRelativePath,
|
|
40
|
+
entryAbsolutePath,
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
export async function loadOpenClawWeixinDefaultExport() {
|
|
44
|
+
const entry = await resolveOpenClawWeixinPublicEntry();
|
|
45
|
+
const moduleNamespace = getPublicJiti()(entry.entryAbsolutePath);
|
|
46
|
+
const plugin = moduleNamespace.default;
|
|
47
|
+
if (!plugin || typeof plugin !== "object" || typeof plugin.register !== "function") {
|
|
48
|
+
throw new Error("[wechat-compat] @tencent-weixin/openclaw-weixin public entry default export is missing register(api)");
|
|
49
|
+
}
|
|
50
|
+
return plugin;
|
|
51
|
+
}
|
|
52
|
+
export async function loadRegisteredWeixinPluginPayloads() {
|
|
53
|
+
const context = await loadRegisteredWeixinPluginContext();
|
|
54
|
+
return context.payloads;
|
|
55
|
+
}
|
|
56
|
+
export async function loadRegisteredWeixinPluginContext() {
|
|
57
|
+
const payloads = [];
|
|
58
|
+
const plugin = await loadOpenClawWeixinDefaultExport();
|
|
59
|
+
plugin.register({
|
|
60
|
+
runtime: {
|
|
61
|
+
channelRuntime: { mode: "guided-smoke" },
|
|
62
|
+
gateway: { startAccount: { source: "guided-smoke" } },
|
|
63
|
+
},
|
|
64
|
+
registerChannel(payload) {
|
|
65
|
+
payloads.push(payload);
|
|
66
|
+
},
|
|
67
|
+
registerCli() { },
|
|
68
|
+
});
|
|
69
|
+
return {
|
|
70
|
+
pluginId: typeof plugin.id === "string" && plugin.id.trim().length > 0 ? plugin.id : "wechat-openclaw-weixin",
|
|
71
|
+
payloads,
|
|
72
|
+
};
|
|
73
|
+
}
|
|
@@ -1,63 +1,14 @@
|
|
|
1
|
+
import { loadOpenClawAccountHelpers, type WeixinAccountHelpers } from "./openclaw-account-helpers.js";
|
|
2
|
+
import { loadRegisteredWeixinPluginContext, loadRegisteredWeixinPluginPayloads, resolveOpenClawWeixinPublicEntry, type OpenClawWeixinPublicEntry } from "./openclaw-public-entry.js";
|
|
3
|
+
import { type WeixinQrGateway } from "./openclaw-qr-gateway.js";
|
|
4
|
+
import { loadOpenClawUpdatesAndSendHelpers, type PublicWeixinMessage, type PublicWeixinSendMessage } from "./openclaw-updates-send.js";
|
|
5
|
+
import { loadLatestWeixinAccountState, loadOpenClawSyncBufHelper, type PublicWeixinPersistGetUpdatesBuf } from "./openclaw-sync-buf.js";
|
|
1
6
|
export declare const OPENCLAW_WEIXIN_JITI_SRC_HELPER_MODULES: {
|
|
2
7
|
readonly stateDir: "@tencent-weixin/openclaw-weixin/src/storage/state-dir.ts";
|
|
3
8
|
readonly syncBuf: "@tencent-weixin/openclaw-weixin/src/storage/sync-buf.ts";
|
|
4
9
|
readonly getUpdates: "@tencent-weixin/openclaw-weixin/src/api/api.ts";
|
|
5
10
|
readonly sendMessageWeixin: "@tencent-weixin/openclaw-weixin/src/messaging/send.ts";
|
|
6
11
|
};
|
|
7
|
-
type WeixinQrGateway = {
|
|
8
|
-
loginWithQrStart: (input?: unknown) => unknown;
|
|
9
|
-
loginWithQrWait: (input?: unknown) => unknown;
|
|
10
|
-
};
|
|
11
|
-
export type WeixinAccountHelpers = {
|
|
12
|
-
listAccountIds: () => Promise<string[]>;
|
|
13
|
-
resolveAccount: (accountId: string) => Promise<unknown>;
|
|
14
|
-
describeAccount: (accountIdOrInput: string | {
|
|
15
|
-
accountId: string;
|
|
16
|
-
}) => Promise<unknown>;
|
|
17
|
-
};
|
|
18
|
-
export type OpenClawWeixinPublicEntry = {
|
|
19
|
-
packageJsonPath: string;
|
|
20
|
-
packageRoot: string;
|
|
21
|
-
extensions: string[];
|
|
22
|
-
entryRelativePath: string;
|
|
23
|
-
entryAbsolutePath: string;
|
|
24
|
-
};
|
|
25
|
-
declare function resolveOpenClawWeixinPublicEntry(): Promise<OpenClawWeixinPublicEntry>;
|
|
26
|
-
declare function loadPublicWeixinAccountHelpers(): Promise<WeixinAccountHelpers>;
|
|
27
|
-
declare function loadLatestWeixinAccountState(): Promise<{
|
|
28
|
-
accountId: string;
|
|
29
|
-
token: string;
|
|
30
|
-
baseUrl: string;
|
|
31
|
-
getUpdatesBuf?: string;
|
|
32
|
-
} | null>;
|
|
33
|
-
type PublicWeixinMessageItem = {
|
|
34
|
-
type?: number;
|
|
35
|
-
text_item?: {
|
|
36
|
-
text?: string;
|
|
37
|
-
};
|
|
38
|
-
};
|
|
39
|
-
export type PublicWeixinMessage = {
|
|
40
|
-
message_id?: number;
|
|
41
|
-
from_user_id?: string;
|
|
42
|
-
context_token?: string;
|
|
43
|
-
create_time_ms?: number;
|
|
44
|
-
item_list?: PublicWeixinMessageItem[];
|
|
45
|
-
};
|
|
46
|
-
export type PublicWeixinSendMessage = (params: {
|
|
47
|
-
to: string;
|
|
48
|
-
text: string;
|
|
49
|
-
opts: {
|
|
50
|
-
baseUrl: string;
|
|
51
|
-
token: string;
|
|
52
|
-
contextToken?: string;
|
|
53
|
-
};
|
|
54
|
-
}) => Promise<{
|
|
55
|
-
messageId: string;
|
|
56
|
-
}>;
|
|
57
|
-
export type PublicWeixinPersistGetUpdatesBuf = (params: {
|
|
58
|
-
accountId: string;
|
|
59
|
-
getUpdatesBuf: string;
|
|
60
|
-
}) => Promise<void>;
|
|
61
12
|
export type OpenClawWeixinPublicHelpers = {
|
|
62
13
|
entry: OpenClawWeixinPublicEntry;
|
|
63
14
|
pluginId: string;
|
|
@@ -83,29 +34,37 @@ export type OpenClawWeixinPublicHelpers = {
|
|
|
83
34
|
};
|
|
84
35
|
type OpenClawWeixinPublicHelpersLoaders = {
|
|
85
36
|
resolveOpenClawWeixinPublicEntry?: typeof resolveOpenClawWeixinPublicEntry;
|
|
37
|
+
loadRegisteredWeixinPluginContext?: typeof loadRegisteredWeixinPluginContext;
|
|
38
|
+
loadRegisteredWeixinPluginPayloads?: typeof loadRegisteredWeixinPluginPayloads;
|
|
39
|
+
loadOpenClawQrGateway?: (payloads: Array<{
|
|
40
|
+
plugin?: unknown;
|
|
41
|
+
}>) => Promise<{
|
|
42
|
+
gateway: WeixinQrGateway;
|
|
43
|
+
pluginId: string;
|
|
44
|
+
}>;
|
|
86
45
|
loadPublicWeixinQrGateway?: () => Promise<{
|
|
87
46
|
gateway: WeixinQrGateway;
|
|
88
47
|
pluginId?: string;
|
|
89
48
|
}>;
|
|
90
49
|
loadLatestWeixinAccountState?: typeof loadLatestWeixinAccountState;
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
50
|
+
loadOpenClawAccountHelpers?: typeof loadOpenClawAccountHelpers;
|
|
51
|
+
loadOpenClawUpdatesAndSendHelpers?: typeof loadOpenClawUpdatesAndSendHelpers;
|
|
52
|
+
loadOpenClawSyncBufHelper?: typeof loadOpenClawSyncBufHelper;
|
|
53
|
+
loadPublicWeixinHelpers?: () => Promise<{
|
|
54
|
+
getUpdates: (params: {
|
|
55
|
+
baseUrl: string;
|
|
56
|
+
token?: string;
|
|
57
|
+
get_updates_buf?: string;
|
|
58
|
+
timeoutMs?: number;
|
|
59
|
+
}) => Promise<{
|
|
60
|
+
msgs?: PublicWeixinMessage[];
|
|
61
|
+
get_updates_buf?: string;
|
|
62
|
+
}>;
|
|
104
63
|
}>;
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
}
|
|
64
|
+
loadPublicWeixinSendHelper?: () => Promise<{
|
|
65
|
+
sendMessageWeixin: PublicWeixinSendMessage;
|
|
66
|
+
}>;
|
|
67
|
+
};
|
|
109
68
|
export declare function loadOpenClawWeixinPublicHelpers(loaders?: OpenClawWeixinPublicHelpersLoaders): Promise<OpenClawWeixinPublicHelpers>;
|
|
110
69
|
export type OpenClawWeixinPublicHelpersLoaderOptions = OpenClawWeixinPublicHelpersLoaders;
|
|
111
|
-
export {};
|
|
70
|
+
export type { PublicWeixinMessage, PublicWeixinSendMessage, PublicWeixinPersistGetUpdatesBuf };
|
|
@@ -1,244 +1,50 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import
|
|
4
|
-
import {
|
|
1
|
+
import { loadOpenClawAccountHelpers } from "./openclaw-account-helpers.js";
|
|
2
|
+
import { loadRegisteredWeixinPluginContext, resolveOpenClawWeixinPublicEntry, } from "./openclaw-public-entry.js";
|
|
3
|
+
import { loadOpenClawQrGateway } from "./openclaw-qr-gateway.js";
|
|
4
|
+
import { loadOpenClawUpdatesAndSendHelpers, } from "./openclaw-updates-send.js";
|
|
5
|
+
import { loadLatestWeixinAccountState, loadOpenClawSyncBufHelper, } from "./openclaw-sync-buf.js";
|
|
5
6
|
export const OPENCLAW_WEIXIN_JITI_SRC_HELPER_MODULES = {
|
|
6
7
|
stateDir: "@tencent-weixin/openclaw-weixin/src/storage/state-dir.ts",
|
|
7
8
|
syncBuf: "@tencent-weixin/openclaw-weixin/src/storage/sync-buf.ts",
|
|
8
9
|
getUpdates: "@tencent-weixin/openclaw-weixin/src/api/api.ts",
|
|
9
10
|
sendMessageWeixin: "@tencent-weixin/openclaw-weixin/src/messaging/send.ts",
|
|
10
11
|
};
|
|
11
|
-
let publicJitiLoader = null;
|
|
12
|
-
function requireField(condition, message) {
|
|
13
|
-
if (!condition) {
|
|
14
|
-
throw new Error(`[wechat-compat] ${message}`);
|
|
15
|
-
}
|
|
16
|
-
}
|
|
17
|
-
function getPublicJiti() {
|
|
18
|
-
if (publicJitiLoader) {
|
|
19
|
-
return publicJitiLoader;
|
|
20
|
-
}
|
|
21
|
-
publicJitiLoader = createJiti(import.meta.url, {
|
|
22
|
-
interopDefault: true,
|
|
23
|
-
extensions: [".ts", ".tsx", ".mts", ".cts", ".js", ".mjs", ".cjs", ".json"],
|
|
24
|
-
});
|
|
25
|
-
return publicJitiLoader;
|
|
26
|
-
}
|
|
27
|
-
function hasQrLoginMethods(value) {
|
|
28
|
-
if (!value || typeof value !== "object") {
|
|
29
|
-
return false;
|
|
30
|
-
}
|
|
31
|
-
const candidate = value;
|
|
32
|
-
return typeof candidate.loginWithQrStart === "function" && typeof candidate.loginWithQrWait === "function";
|
|
33
|
-
}
|
|
34
|
-
function hasAccountHelpers(value) {
|
|
35
|
-
if (!value || typeof value !== "object") {
|
|
36
|
-
return false;
|
|
37
|
-
}
|
|
38
|
-
const candidate = value;
|
|
39
|
-
return (typeof candidate.listAccountIds === "function" &&
|
|
40
|
-
typeof candidate.resolveAccount === "function" &&
|
|
41
|
-
typeof candidate.describeAccount === "function");
|
|
42
|
-
}
|
|
43
|
-
async function resolveOpenClawWeixinPublicEntry() {
|
|
44
|
-
const require = createRequire(import.meta.url);
|
|
45
|
-
const packageName = "@tencent-weixin/openclaw-weixin";
|
|
46
|
-
const packageJsonPath = require.resolve(`${packageName}/package.json`);
|
|
47
|
-
const packageJsonRaw = await readFile(packageJsonPath, "utf8");
|
|
48
|
-
const packageJson = JSON.parse(packageJsonRaw);
|
|
49
|
-
const extensions = Array.isArray(packageJson.openclaw?.extensions)
|
|
50
|
-
? packageJson.openclaw?.extensions.filter((it) => typeof it === "string")
|
|
51
|
-
: [];
|
|
52
|
-
requireField(extensions.length > 0, `${packageName} openclaw.extensions[0] is required`);
|
|
53
|
-
const entryRelativePath = extensions[0];
|
|
54
|
-
requireField(Boolean(entryRelativePath?.startsWith("./")), `${packageName} openclaw.extensions[0] must start with ./`);
|
|
55
|
-
const packageRoot = path.dirname(packageJsonPath);
|
|
56
|
-
const entryAbsolutePath = path.resolve(packageRoot, entryRelativePath);
|
|
57
|
-
return {
|
|
58
|
-
packageJsonPath,
|
|
59
|
-
packageRoot,
|
|
60
|
-
extensions,
|
|
61
|
-
entryRelativePath,
|
|
62
|
-
entryAbsolutePath,
|
|
63
|
-
};
|
|
64
|
-
}
|
|
65
|
-
async function loadOpenClawWeixinDefaultExport() {
|
|
66
|
-
const entry = await resolveOpenClawWeixinPublicEntry();
|
|
67
|
-
const moduleNamespace = getPublicJiti()(entry.entryAbsolutePath);
|
|
68
|
-
const plugin = moduleNamespace.default;
|
|
69
|
-
if (!plugin || typeof plugin !== "object" || typeof plugin.register !== "function") {
|
|
70
|
-
throw new Error("[wechat-compat] @tencent-weixin/openclaw-weixin public entry default export is missing register(api)");
|
|
71
|
-
}
|
|
72
|
-
return plugin;
|
|
73
|
-
}
|
|
74
|
-
async function loadPublicWeixinQrGateway() {
|
|
75
|
-
const registeredPayloads = [];
|
|
76
|
-
const compatHostApi = {
|
|
77
|
-
runtime: {
|
|
78
|
-
channelRuntime: {
|
|
79
|
-
mode: "guided-smoke",
|
|
80
|
-
},
|
|
81
|
-
gateway: {
|
|
82
|
-
startAccount: {
|
|
83
|
-
source: "guided-smoke",
|
|
84
|
-
},
|
|
85
|
-
},
|
|
86
|
-
},
|
|
87
|
-
registerChannel(payload) {
|
|
88
|
-
registeredPayloads.push(payload);
|
|
89
|
-
},
|
|
90
|
-
registerCli() { },
|
|
91
|
-
};
|
|
92
|
-
const plugin = await loadOpenClawWeixinDefaultExport();
|
|
93
|
-
plugin.register(compatHostApi);
|
|
94
|
-
for (const payload of registeredPayloads) {
|
|
95
|
-
const payloadPlugin = payload?.plugin;
|
|
96
|
-
const gateway = payloadPlugin && typeof payloadPlugin === "object" ? payloadPlugin.gateway : null;
|
|
97
|
-
if (hasQrLoginMethods(gateway)) {
|
|
98
|
-
return { gateway, pluginId: plugin.id ?? "unknown" };
|
|
99
|
-
}
|
|
100
|
-
if (hasQrLoginMethods(payloadPlugin)) {
|
|
101
|
-
return { gateway: payloadPlugin, pluginId: plugin.id ?? "unknown" };
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
|
-
throw new Error("registerChannel did not expose weixin gateway loginWithQrStart/loginWithQrWait");
|
|
105
|
-
}
|
|
106
|
-
async function loadPublicWeixinAccountHelpers() {
|
|
107
|
-
const registeredPayloads = [];
|
|
108
|
-
const compatHostApi = {
|
|
109
|
-
runtime: {
|
|
110
|
-
channelRuntime: {
|
|
111
|
-
mode: "guided-smoke",
|
|
112
|
-
},
|
|
113
|
-
gateway: {
|
|
114
|
-
startAccount: {
|
|
115
|
-
source: "guided-smoke",
|
|
116
|
-
},
|
|
117
|
-
},
|
|
118
|
-
},
|
|
119
|
-
registerChannel(payload) {
|
|
120
|
-
registeredPayloads.push(payload);
|
|
121
|
-
},
|
|
122
|
-
registerCli() { },
|
|
123
|
-
};
|
|
124
|
-
const plugin = await loadOpenClawWeixinDefaultExport();
|
|
125
|
-
plugin.register(compatHostApi);
|
|
126
|
-
for (const payload of registeredPayloads) {
|
|
127
|
-
const payloadPlugin = payload?.plugin;
|
|
128
|
-
const configCandidate = payloadPlugin && typeof payloadPlugin === "object"
|
|
129
|
-
? payloadPlugin.config
|
|
130
|
-
: null;
|
|
131
|
-
if (!hasAccountHelpers(configCandidate)) {
|
|
132
|
-
continue;
|
|
133
|
-
}
|
|
134
|
-
return {
|
|
135
|
-
listAccountIds: async () => {
|
|
136
|
-
const result = await Promise.resolve(configCandidate.listAccountIds());
|
|
137
|
-
return Array.isArray(result) ? result.filter((item) => typeof item === "string") : [];
|
|
138
|
-
},
|
|
139
|
-
resolveAccount: async (accountId) => Promise.resolve(configCandidate.resolveAccount(accountId)),
|
|
140
|
-
describeAccount: async (accountIdOrInput) => Promise.resolve(configCandidate.describeAccount(accountIdOrInput)),
|
|
141
|
-
};
|
|
142
|
-
}
|
|
143
|
-
throw new Error(`${plugin.id ?? "weixin"} registerChannel did not expose config listAccountIds/resolveAccount/describeAccount`);
|
|
144
|
-
}
|
|
145
|
-
async function loadLatestWeixinAccountState() {
|
|
146
|
-
const require = createRequire(import.meta.url);
|
|
147
|
-
const stateDirModulePath = require.resolve(OPENCLAW_WEIXIN_JITI_SRC_HELPER_MODULES.stateDir);
|
|
148
|
-
const stateDirModule = getPublicJiti()(stateDirModulePath);
|
|
149
|
-
const stateDir = stateDirModule.resolveStateDir?.();
|
|
150
|
-
if (!stateDir) {
|
|
151
|
-
return null;
|
|
152
|
-
}
|
|
153
|
-
const accountsIndexPath = path.join(stateDir, "openclaw-weixin", "accounts.json");
|
|
154
|
-
let accountIds = [];
|
|
155
|
-
try {
|
|
156
|
-
const raw = await readFile(accountsIndexPath, "utf8");
|
|
157
|
-
const parsed = JSON.parse(raw);
|
|
158
|
-
if (Array.isArray(parsed)) {
|
|
159
|
-
accountIds = parsed.filter((item) => typeof item === "string" && item.trim().length > 0);
|
|
160
|
-
}
|
|
161
|
-
}
|
|
162
|
-
catch {
|
|
163
|
-
return null;
|
|
164
|
-
}
|
|
165
|
-
const accountId = accountIds.at(-1);
|
|
166
|
-
if (!accountId) {
|
|
167
|
-
return null;
|
|
168
|
-
}
|
|
169
|
-
try {
|
|
170
|
-
const accountFilePath = path.join(stateDir, "openclaw-weixin", "accounts", `${accountId}.json`);
|
|
171
|
-
const accountRaw = await readFile(accountFilePath, "utf8");
|
|
172
|
-
const account = JSON.parse(accountRaw);
|
|
173
|
-
const syncBufModulePath = require.resolve(OPENCLAW_WEIXIN_JITI_SRC_HELPER_MODULES.syncBuf);
|
|
174
|
-
const syncBufModule = getPublicJiti()(syncBufModulePath);
|
|
175
|
-
if (typeof account.token !== "string" || account.token.trim().length === 0) {
|
|
176
|
-
return null;
|
|
177
|
-
}
|
|
178
|
-
const syncBufFilePath = syncBufModule.getSyncBufFilePath?.(accountId);
|
|
179
|
-
const persistedGetUpdatesBuf = syncBufFilePath ? syncBufModule.loadGetUpdatesBuf?.(syncBufFilePath) : undefined;
|
|
180
|
-
return {
|
|
181
|
-
accountId,
|
|
182
|
-
token: account.token,
|
|
183
|
-
baseUrl: typeof account.baseUrl === "string" && account.baseUrl.trim().length > 0 ? account.baseUrl : "https://ilinkai.weixin.qq.com",
|
|
184
|
-
getUpdatesBuf: typeof persistedGetUpdatesBuf === "string" ? persistedGetUpdatesBuf : undefined,
|
|
185
|
-
};
|
|
186
|
-
}
|
|
187
|
-
catch {
|
|
188
|
-
return null;
|
|
189
|
-
}
|
|
190
|
-
}
|
|
191
12
|
function missingHelperError(helperName) {
|
|
192
13
|
return new Error(`[wechat-compat] required helper missing: ${helperName}`);
|
|
193
14
|
}
|
|
194
|
-
async function loadPublicWeixinHelpers() {
|
|
195
|
-
const require = createRequire(import.meta.url);
|
|
196
|
-
const getUpdatesModulePath = require.resolve(OPENCLAW_WEIXIN_JITI_SRC_HELPER_MODULES.getUpdates);
|
|
197
|
-
const getUpdatesModule = getPublicJiti()(getUpdatesModulePath);
|
|
198
|
-
if (typeof getUpdatesModule.getUpdates !== "function") {
|
|
199
|
-
throw new Error("public getUpdates helper unavailable");
|
|
200
|
-
}
|
|
201
|
-
return {
|
|
202
|
-
getUpdates: getUpdatesModule.getUpdates,
|
|
203
|
-
};
|
|
204
|
-
}
|
|
205
|
-
async function loadPublicWeixinSendHelper() {
|
|
206
|
-
const require = createRequire(import.meta.url);
|
|
207
|
-
const sendModulePath = require.resolve(OPENCLAW_WEIXIN_JITI_SRC_HELPER_MODULES.sendMessageWeixin);
|
|
208
|
-
const sendModule = getPublicJiti()(sendModulePath);
|
|
209
|
-
if (typeof sendModule.sendMessageWeixin !== "function") {
|
|
210
|
-
throw new Error("public sendMessageWeixin helper unavailable");
|
|
211
|
-
}
|
|
212
|
-
return {
|
|
213
|
-
sendMessageWeixin: sendModule.sendMessageWeixin,
|
|
214
|
-
};
|
|
215
|
-
}
|
|
216
|
-
async function loadPublicWeixinSyncBufHelpers() {
|
|
217
|
-
const require = createRequire(import.meta.url);
|
|
218
|
-
const syncBufModulePath = require.resolve(OPENCLAW_WEIXIN_JITI_SRC_HELPER_MODULES.syncBuf);
|
|
219
|
-
const syncBufModule = getPublicJiti()(syncBufModulePath);
|
|
220
|
-
if (typeof syncBufModule.getSyncBufFilePath !== "function" || typeof syncBufModule.saveGetUpdatesBuf !== "function") {
|
|
221
|
-
return {};
|
|
222
|
-
}
|
|
223
|
-
return {
|
|
224
|
-
persistGetUpdatesBuf: async ({ accountId, getUpdatesBuf }) => {
|
|
225
|
-
const filePath = syncBufModule.getSyncBufFilePath(accountId);
|
|
226
|
-
syncBufModule.saveGetUpdatesBuf(filePath, getUpdatesBuf);
|
|
227
|
-
},
|
|
228
|
-
};
|
|
229
|
-
}
|
|
230
15
|
export async function loadOpenClawWeixinPublicHelpers(loaders = {}) {
|
|
231
16
|
const entry = await (loaders.resolveOpenClawWeixinPublicEntry ?? resolveOpenClawWeixinPublicEntry)();
|
|
232
|
-
const qrGatewayResult =
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
17
|
+
const qrGatewayResult = loaders.loadPublicWeixinQrGateway
|
|
18
|
+
? await loaders.loadPublicWeixinQrGateway()
|
|
19
|
+
: await (async () => {
|
|
20
|
+
if (loaders.loadRegisteredWeixinPluginContext) {
|
|
21
|
+
const context = await loaders.loadRegisteredWeixinPluginContext();
|
|
22
|
+
return (loaders.loadOpenClawQrGateway ?? loadOpenClawQrGateway)(context.payloads, { pluginId: context.pluginId });
|
|
23
|
+
}
|
|
24
|
+
const context = await loadRegisteredWeixinPluginContext();
|
|
25
|
+
return (loaders.loadOpenClawQrGateway ?? loadOpenClawQrGateway)(context.payloads, { pluginId: context.pluginId });
|
|
26
|
+
})();
|
|
27
|
+
const accountHelpers = await (loaders.loadOpenClawAccountHelpers ?? loadOpenClawAccountHelpers)();
|
|
28
|
+
const latestAccountState = await (loaders.loadLatestWeixinAccountState ?? loadLatestWeixinAccountState)({
|
|
29
|
+
stateDirModulePath: OPENCLAW_WEIXIN_JITI_SRC_HELPER_MODULES.stateDir,
|
|
30
|
+
syncBufModulePath: OPENCLAW_WEIXIN_JITI_SRC_HELPER_MODULES.syncBuf,
|
|
31
|
+
});
|
|
32
|
+
const updatesSend = loaders.loadOpenClawUpdatesAndSendHelpers
|
|
33
|
+
? await loaders.loadOpenClawUpdatesAndSendHelpers()
|
|
34
|
+
: await (async () => {
|
|
35
|
+
const defaults = await loadOpenClawUpdatesAndSendHelpers();
|
|
36
|
+
const maybeUpdates = loaders.loadPublicWeixinHelpers ? await loaders.loadPublicWeixinHelpers() : undefined;
|
|
37
|
+
const maybeSend = loaders.loadPublicWeixinSendHelper ? await loaders.loadPublicWeixinSendHelper() : undefined;
|
|
38
|
+
return {
|
|
39
|
+
getUpdates: loaders.loadPublicWeixinHelpers ? maybeUpdates?.getUpdates : defaults.getUpdates,
|
|
40
|
+
sendMessageWeixin: loaders.loadPublicWeixinSendHelper ? maybeSend?.sendMessageWeixin : defaults.sendMessageWeixin,
|
|
41
|
+
};
|
|
42
|
+
})();
|
|
43
|
+
const syncBufHelpers = await (loaders.loadOpenClawSyncBufHelper ?? loadOpenClawSyncBufHelper)();
|
|
238
44
|
if (typeof qrGatewayResult?.gateway?.loginWithQrStart !== "function" || typeof qrGatewayResult?.gateway?.loginWithQrWait !== "function") {
|
|
239
45
|
throw missingHelperError("qrGateway");
|
|
240
46
|
}
|
|
241
|
-
if (typeof
|
|
47
|
+
if (typeof updatesSend?.getUpdates !== "function") {
|
|
242
48
|
throw missingHelperError("getUpdates");
|
|
243
49
|
}
|
|
244
50
|
if (typeof accountHelpers?.listAccountIds !== "function" ||
|
|
@@ -246,7 +52,7 @@ export async function loadOpenClawWeixinPublicHelpers(loaders = {}) {
|
|
|
246
52
|
typeof accountHelpers?.describeAccount !== "function") {
|
|
247
53
|
throw missingHelperError("accountHelpers");
|
|
248
54
|
}
|
|
249
|
-
if (typeof
|
|
55
|
+
if (typeof updatesSend?.sendMessageWeixin !== "function") {
|
|
250
56
|
throw missingHelperError("sendMessageWeixin");
|
|
251
57
|
}
|
|
252
58
|
return {
|
|
@@ -255,8 +61,8 @@ export async function loadOpenClawWeixinPublicHelpers(loaders = {}) {
|
|
|
255
61
|
qrGateway: qrGatewayResult.gateway,
|
|
256
62
|
accountHelpers,
|
|
257
63
|
latestAccountState,
|
|
258
|
-
getUpdates:
|
|
259
|
-
sendMessageWeixin:
|
|
64
|
+
getUpdates: updatesSend.getUpdates,
|
|
65
|
+
sendMessageWeixin: updatesSend.sendMessageWeixin,
|
|
260
66
|
persistGetUpdatesBuf: syncBufHelpers.persistGetUpdatesBuf,
|
|
261
67
|
};
|
|
262
68
|
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export type WeixinQrGateway = {
|
|
2
|
+
loginWithQrStart: (input?: unknown) => unknown;
|
|
3
|
+
loginWithQrWait: (input?: unknown) => unknown;
|
|
4
|
+
};
|
|
5
|
+
type OpenClawQrGatewayPayload = {
|
|
6
|
+
plugin?: unknown;
|
|
7
|
+
};
|
|
8
|
+
export declare function createOpenClawQrGateway(source: WeixinQrGateway): WeixinQrGateway;
|
|
9
|
+
export declare function loadOpenClawQrGateway(payloads: OpenClawQrGatewayPayload[], options?: {
|
|
10
|
+
pluginId?: string;
|
|
11
|
+
}): Promise<{
|
|
12
|
+
gateway: WeixinQrGateway;
|
|
13
|
+
pluginId: string;
|
|
14
|
+
}>;
|
|
15
|
+
export {};
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
function toObjectInput(input) {
|
|
2
|
+
return input && typeof input === "object" ? input : {};
|
|
3
|
+
}
|
|
4
|
+
function hasQrLoginMethods(value) {
|
|
5
|
+
if (!value || typeof value !== "object") {
|
|
6
|
+
return false;
|
|
7
|
+
}
|
|
8
|
+
const candidate = value;
|
|
9
|
+
return typeof candidate.loginWithQrStart === "function" && typeof candidate.loginWithQrWait === "function";
|
|
10
|
+
}
|
|
11
|
+
export function createOpenClawQrGateway(source) {
|
|
12
|
+
return {
|
|
13
|
+
async loginWithQrStart(input) {
|
|
14
|
+
return source.loginWithQrStart(toObjectInput(input));
|
|
15
|
+
},
|
|
16
|
+
async loginWithQrWait(input) {
|
|
17
|
+
return source.loginWithQrWait(toObjectInput(input));
|
|
18
|
+
},
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
export async function loadOpenClawQrGateway(payloads, options = {}) {
|
|
22
|
+
for (const payload of payloads) {
|
|
23
|
+
const payloadPlugin = payload?.plugin;
|
|
24
|
+
const resolvedPluginId = typeof options.pluginId === "string" && options.pluginId.trim().length > 0
|
|
25
|
+
? options.pluginId
|
|
26
|
+
: typeof payloadPlugin?.id === "string" &&
|
|
27
|
+
String(payloadPlugin.id).trim().length > 0
|
|
28
|
+
? String(payloadPlugin.id)
|
|
29
|
+
: "unknown";
|
|
30
|
+
const gateway = payloadPlugin && typeof payloadPlugin === "object" ? payloadPlugin.gateway : null;
|
|
31
|
+
if (hasQrLoginMethods(gateway)) {
|
|
32
|
+
return {
|
|
33
|
+
gateway: createOpenClawQrGateway(gateway),
|
|
34
|
+
pluginId: resolvedPluginId,
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
throw new Error("registerChannel did not expose weixin gateway loginWithQrStart/loginWithQrWait");
|
|
39
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
export type PublicWeixinPersistGetUpdatesBuf = (params: {
|
|
2
|
+
accountId: string;
|
|
3
|
+
getUpdatesBuf: string;
|
|
4
|
+
}) => Promise<void>;
|
|
5
|
+
export declare function createOpenClawSyncBufHelper(input: {
|
|
6
|
+
getSyncBufFilePath: (accountId: string) => string;
|
|
7
|
+
saveGetUpdatesBuf: (filePath: string, getUpdatesBuf: string) => void;
|
|
8
|
+
}): {
|
|
9
|
+
persistGetUpdatesBuf: PublicWeixinPersistGetUpdatesBuf;
|
|
10
|
+
};
|
|
11
|
+
export declare function loadOpenClawSyncBufHelper(options?: {
|
|
12
|
+
syncBufModulePath?: string;
|
|
13
|
+
}): Promise<{
|
|
14
|
+
persistGetUpdatesBuf: PublicWeixinPersistGetUpdatesBuf;
|
|
15
|
+
}>;
|
|
16
|
+
export declare function loadLatestWeixinAccountState(options?: {
|
|
17
|
+
stateDirModulePath?: string;
|
|
18
|
+
syncBufModulePath?: string;
|
|
19
|
+
}): Promise<{
|
|
20
|
+
accountId: string;
|
|
21
|
+
token: string;
|
|
22
|
+
baseUrl: string;
|
|
23
|
+
getUpdatesBuf?: string;
|
|
24
|
+
} | null>;
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import { readFile } from "node:fs/promises";
|
|
2
|
+
import { createRequire } from "node:module";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import { createJiti } from "jiti";
|
|
5
|
+
const OPENCLAW_SYNC_BUF_MODULE = "@tencent-weixin/openclaw-weixin/src/storage/sync-buf.ts";
|
|
6
|
+
const OPENCLAW_STATE_DIR_MODULE = "@tencent-weixin/openclaw-weixin/src/storage/state-dir.ts";
|
|
7
|
+
let syncBufJitiLoader = null;
|
|
8
|
+
function getSyncBufJiti() {
|
|
9
|
+
if (syncBufJitiLoader) {
|
|
10
|
+
return syncBufJitiLoader;
|
|
11
|
+
}
|
|
12
|
+
syncBufJitiLoader = createJiti(import.meta.url, {
|
|
13
|
+
interopDefault: true,
|
|
14
|
+
extensions: [".ts", ".tsx", ".mts", ".cts", ".js", ".mjs", ".cjs", ".json"],
|
|
15
|
+
});
|
|
16
|
+
return syncBufJitiLoader;
|
|
17
|
+
}
|
|
18
|
+
export function createOpenClawSyncBufHelper(input) {
|
|
19
|
+
return {
|
|
20
|
+
async persistGetUpdatesBuf({ accountId, getUpdatesBuf }) {
|
|
21
|
+
const filePath = input.getSyncBufFilePath(accountId);
|
|
22
|
+
if (typeof filePath !== "string" || filePath.trim().length === 0) {
|
|
23
|
+
throw new Error("[wechat-compat] sync-buf helper returned invalid file path");
|
|
24
|
+
}
|
|
25
|
+
input.saveGetUpdatesBuf(filePath, getUpdatesBuf);
|
|
26
|
+
},
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
export async function loadOpenClawSyncBufHelper(options = {}) {
|
|
30
|
+
const require = createRequire(import.meta.url);
|
|
31
|
+
const syncBufModulePath = require.resolve(options.syncBufModulePath ?? OPENCLAW_SYNC_BUF_MODULE);
|
|
32
|
+
const syncBufModule = getSyncBufJiti()(syncBufModulePath);
|
|
33
|
+
if (typeof syncBufModule.getSyncBufFilePath !== "function" || typeof syncBufModule.saveGetUpdatesBuf !== "function") {
|
|
34
|
+
throw new Error("[wechat-compat] sync-buf source helper unavailable");
|
|
35
|
+
}
|
|
36
|
+
return createOpenClawSyncBufHelper({
|
|
37
|
+
getSyncBufFilePath: syncBufModule.getSyncBufFilePath,
|
|
38
|
+
saveGetUpdatesBuf: syncBufModule.saveGetUpdatesBuf,
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
export async function loadLatestWeixinAccountState(options = {}) {
|
|
42
|
+
const require = createRequire(import.meta.url);
|
|
43
|
+
const stateDirModulePath = require.resolve(options.stateDirModulePath ?? OPENCLAW_STATE_DIR_MODULE);
|
|
44
|
+
const stateDirModule = getSyncBufJiti()(stateDirModulePath);
|
|
45
|
+
const stateDir = stateDirModule.resolveStateDir?.();
|
|
46
|
+
if (!stateDir) {
|
|
47
|
+
return null;
|
|
48
|
+
}
|
|
49
|
+
const accountsIndexPath = path.join(stateDir, "openclaw-weixin", "accounts.json");
|
|
50
|
+
let accountIds = [];
|
|
51
|
+
try {
|
|
52
|
+
const raw = await readFile(accountsIndexPath, "utf8");
|
|
53
|
+
const parsed = JSON.parse(raw);
|
|
54
|
+
if (Array.isArray(parsed)) {
|
|
55
|
+
accountIds = parsed.filter((item) => typeof item === "string" && item.trim().length > 0);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
catch {
|
|
59
|
+
return null;
|
|
60
|
+
}
|
|
61
|
+
const accountId = accountIds.at(-1);
|
|
62
|
+
if (!accountId) {
|
|
63
|
+
return null;
|
|
64
|
+
}
|
|
65
|
+
try {
|
|
66
|
+
const accountFilePath = path.join(stateDir, "openclaw-weixin", "accounts", `${accountId}.json`);
|
|
67
|
+
const accountRaw = await readFile(accountFilePath, "utf8");
|
|
68
|
+
const account = JSON.parse(accountRaw);
|
|
69
|
+
const syncBufModulePath = require.resolve(options.syncBufModulePath ?? OPENCLAW_SYNC_BUF_MODULE);
|
|
70
|
+
const syncBufModule = getSyncBufJiti()(syncBufModulePath);
|
|
71
|
+
if (typeof account.token !== "string" || account.token.trim().length === 0) {
|
|
72
|
+
return null;
|
|
73
|
+
}
|
|
74
|
+
const syncBufFilePath = syncBufModule.getSyncBufFilePath?.(accountId);
|
|
75
|
+
const persistedGetUpdatesBuf = syncBufFilePath ? syncBufModule.loadGetUpdatesBuf?.(syncBufFilePath) : undefined;
|
|
76
|
+
return {
|
|
77
|
+
accountId,
|
|
78
|
+
token: account.token,
|
|
79
|
+
baseUrl: typeof account.baseUrl === "string" && account.baseUrl.trim().length > 0 ? account.baseUrl : "https://ilinkai.weixin.qq.com",
|
|
80
|
+
getUpdatesBuf: typeof persistedGetUpdatesBuf === "string" ? persistedGetUpdatesBuf : undefined,
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
catch {
|
|
84
|
+
return null;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
type PublicWeixinMessageItem = {
|
|
2
|
+
type?: number;
|
|
3
|
+
text_item?: {
|
|
4
|
+
text?: string;
|
|
5
|
+
};
|
|
6
|
+
};
|
|
7
|
+
export type PublicWeixinMessage = {
|
|
8
|
+
message_id?: number;
|
|
9
|
+
from_user_id?: string;
|
|
10
|
+
context_token?: string;
|
|
11
|
+
create_time_ms?: number;
|
|
12
|
+
item_list?: PublicWeixinMessageItem[];
|
|
13
|
+
};
|
|
14
|
+
export type PublicWeixinSendMessage = (params: {
|
|
15
|
+
to: string;
|
|
16
|
+
text: string;
|
|
17
|
+
opts: {
|
|
18
|
+
baseUrl: string;
|
|
19
|
+
token: string;
|
|
20
|
+
contextToken?: string;
|
|
21
|
+
};
|
|
22
|
+
}) => Promise<{
|
|
23
|
+
messageId: string;
|
|
24
|
+
}>;
|
|
25
|
+
type PublicGetUpdates = (params: {
|
|
26
|
+
baseUrl: string;
|
|
27
|
+
token?: string;
|
|
28
|
+
get_updates_buf?: string;
|
|
29
|
+
timeoutMs?: number;
|
|
30
|
+
}) => Promise<{
|
|
31
|
+
msgs?: PublicWeixinMessage[];
|
|
32
|
+
get_updates_buf?: string;
|
|
33
|
+
}>;
|
|
34
|
+
export declare function createOpenClawUpdatesHelper(getUpdates: PublicGetUpdates): {
|
|
35
|
+
getUpdates: PublicGetUpdates;
|
|
36
|
+
};
|
|
37
|
+
export declare function createOpenClawSendHelper(sendMessageWeixin: PublicWeixinSendMessage): {
|
|
38
|
+
sendMessageWeixin: PublicWeixinSendMessage;
|
|
39
|
+
};
|
|
40
|
+
export declare function loadOpenClawUpdatesAndSendHelpers(options?: {
|
|
41
|
+
getUpdatesModulePath?: string;
|
|
42
|
+
sendMessageWeixinModulePath?: string;
|
|
43
|
+
}): Promise<{
|
|
44
|
+
getUpdates: PublicGetUpdates;
|
|
45
|
+
sendMessageWeixin: PublicWeixinSendMessage;
|
|
46
|
+
}>;
|
|
47
|
+
export {};
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { createRequire } from "node:module";
|
|
2
|
+
import { createJiti } from "jiti";
|
|
3
|
+
const OPENCLAW_UPDATES_MODULE = "@tencent-weixin/openclaw-weixin/src/api/api.ts";
|
|
4
|
+
const OPENCLAW_SEND_MODULE = "@tencent-weixin/openclaw-weixin/src/messaging/send.ts";
|
|
5
|
+
let updatesSendJitiLoader = null;
|
|
6
|
+
function getUpdatesSendJiti() {
|
|
7
|
+
if (updatesSendJitiLoader) {
|
|
8
|
+
return updatesSendJitiLoader;
|
|
9
|
+
}
|
|
10
|
+
updatesSendJitiLoader = createJiti(import.meta.url, {
|
|
11
|
+
interopDefault: true,
|
|
12
|
+
extensions: [".ts", ".tsx", ".mts", ".cts", ".js", ".mjs", ".cjs", ".json"],
|
|
13
|
+
});
|
|
14
|
+
return updatesSendJitiLoader;
|
|
15
|
+
}
|
|
16
|
+
function toObjectInput(input) {
|
|
17
|
+
return input && typeof input === "object" ? input : {};
|
|
18
|
+
}
|
|
19
|
+
export function createOpenClawUpdatesHelper(getUpdates) {
|
|
20
|
+
return {
|
|
21
|
+
async getUpdates(input) {
|
|
22
|
+
return getUpdates(toObjectInput(input));
|
|
23
|
+
},
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
export function createOpenClawSendHelper(sendMessageWeixin) {
|
|
27
|
+
return {
|
|
28
|
+
async sendMessageWeixin(input) {
|
|
29
|
+
return sendMessageWeixin(toObjectInput(input));
|
|
30
|
+
},
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
export async function loadOpenClawUpdatesAndSendHelpers(options = {}) {
|
|
34
|
+
const require = createRequire(import.meta.url);
|
|
35
|
+
const getUpdatesModulePath = require.resolve(options.getUpdatesModulePath ?? OPENCLAW_UPDATES_MODULE);
|
|
36
|
+
const sendModulePath = require.resolve(options.sendMessageWeixinModulePath ?? OPENCLAW_SEND_MODULE);
|
|
37
|
+
const getUpdatesModule = getUpdatesSendJiti()(getUpdatesModulePath);
|
|
38
|
+
const sendModule = getUpdatesSendJiti()(sendModulePath);
|
|
39
|
+
if (typeof getUpdatesModule.getUpdates !== "function") {
|
|
40
|
+
throw new Error("public getUpdates helper unavailable");
|
|
41
|
+
}
|
|
42
|
+
if (typeof sendModule.sendMessageWeixin !== "function") {
|
|
43
|
+
throw new Error("public sendMessageWeixin helper unavailable");
|
|
44
|
+
}
|
|
45
|
+
return {
|
|
46
|
+
...createOpenClawUpdatesHelper(getUpdatesModule.getUpdates),
|
|
47
|
+
...createOpenClawSendHelper(sendModule.sendMessageWeixin),
|
|
48
|
+
};
|
|
49
|
+
}
|
|
@@ -13,20 +13,6 @@ function toBoolean(value) {
|
|
|
13
13
|
if (typeof value === "boolean") {
|
|
14
14
|
return value;
|
|
15
15
|
}
|
|
16
|
-
if (value === 1) {
|
|
17
|
-
return true;
|
|
18
|
-
}
|
|
19
|
-
if (value === 0) {
|
|
20
|
-
return false;
|
|
21
|
-
}
|
|
22
|
-
return undefined;
|
|
23
|
-
}
|
|
24
|
-
function firstNumber(...values) {
|
|
25
|
-
for (const value of values) {
|
|
26
|
-
if (typeof value === "number" && Number.isFinite(value)) {
|
|
27
|
-
return value;
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
16
|
return undefined;
|
|
31
17
|
}
|
|
32
18
|
async function tryDescribeAccount(accountHelpers, accountId) {
|
|
@@ -55,13 +41,17 @@ export async function buildOpenClawMenuAccount(input) {
|
|
|
55
41
|
}
|
|
56
42
|
const resolvedRaw = asObject(await input.accountHelpers.resolveAccount(accountId));
|
|
57
43
|
const describedRaw = asObject(await tryDescribeAccount(input.accountHelpers, accountId));
|
|
58
|
-
const enabled = toBoolean(resolvedRaw.enabled) ?? toBoolean(
|
|
59
|
-
const configured = toBoolean(describedRaw.configured) ?? toBoolean(
|
|
60
|
-
const userId = firstNonEmptyString(input.latestAccountState?.userId, resolvedRaw.userId,
|
|
61
|
-
const boundAt =
|
|
44
|
+
const enabled = toBoolean(resolvedRaw.enabled) ?? toBoolean(describedRaw.enabled) ?? false;
|
|
45
|
+
const configured = toBoolean(describedRaw.configured) ?? toBoolean(resolvedRaw.configured) ?? false;
|
|
46
|
+
const userId = firstNonEmptyString(input.latestAccountState?.userId, resolvedRaw.userId, describedRaw.userId);
|
|
47
|
+
const boundAt = [
|
|
48
|
+
input.latestAccountState?.boundAt,
|
|
49
|
+
resolvedRaw.boundAt,
|
|
50
|
+
describedRaw.boundAt,
|
|
51
|
+
].find((value) => typeof value === "number" && Number.isFinite(value));
|
|
62
52
|
return {
|
|
63
53
|
accountId,
|
|
64
|
-
name: firstNonEmptyString(resolvedRaw.name,
|
|
54
|
+
name: firstNonEmptyString(resolvedRaw.name, describedRaw.name),
|
|
65
55
|
enabled,
|
|
66
56
|
configured,
|
|
67
57
|
userId,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "opencode-copilot-account-switcher",
|
|
3
|
-
"version": "0.14.
|
|
3
|
+
"version": "0.14.7",
|
|
4
4
|
"description": "GitHub Copilot account switcher plugin for OpenCode",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"types": "./dist/index.d.ts",
|
|
@@ -63,8 +63,8 @@
|
|
|
63
63
|
"dependencies": {
|
|
64
64
|
"@opencode-ai/plugin": "^1.2.26",
|
|
65
65
|
"@opencode-ai/sdk": "^1.2.26",
|
|
66
|
-
"@tencent-weixin/openclaw-weixin": "
|
|
67
|
-
"openclaw": "2026.3.
|
|
66
|
+
"@tencent-weixin/openclaw-weixin": "2.0.1",
|
|
67
|
+
"openclaw": "2026.3.22",
|
|
68
68
|
"xdg-basedir": "^5.1.0"
|
|
69
69
|
}
|
|
70
70
|
}
|