opencode-copilot-account-switcher 0.14.6 → 0.14.8
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 +33 -22
- package/dist/wechat/compat/openclaw-account-helpers.js +14 -29
- package/dist/wechat/compat/openclaw-guided-smoke.js +5 -17
- package/dist/wechat/compat/openclaw-qr-gateway.js +0 -6
- package/dist/wechat/compat/openclaw-sync-buf.d.ts +1 -1
- package/dist/wechat/compat/openclaw-sync-buf.js +4 -1
- package/dist/wechat/openclaw-account-adapter.js +9 -19
- package/package.json +1 -1
package/dist/wechat/bind-flow.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { bindOperator, readOperatorBinding, rebindOperator, resetOperatorBinding } from "./operator-store.js";
|
|
2
2
|
import { loadOpenClawWeixinPublicHelpers } from "./compat/openclaw-public-helpers.js";
|
|
3
3
|
import { buildOpenClawMenuAccount } from "./openclaw-account-adapter.js";
|
|
4
|
+
import { normalizeAccountId } from "openclaw/plugin-sdk/account-id";
|
|
4
5
|
import qrcodeTerminal from "qrcode-terminal";
|
|
5
6
|
const DEFAULT_QR_WAIT_TIMEOUT_MS = 480000;
|
|
6
7
|
function pickFirstNonEmptyString(...values) {
|
|
@@ -21,7 +22,7 @@ function pickQrTerminal(value) {
|
|
|
21
22
|
return pickFirstNonEmptyString(value?.qrTerminal);
|
|
22
23
|
}
|
|
23
24
|
function pickQrUrl(value) {
|
|
24
|
-
return pickFirstNonEmptyString(value?.qrDataUrl
|
|
25
|
+
return pickFirstNonEmptyString(value?.qrDataUrl);
|
|
25
26
|
}
|
|
26
27
|
function isTimeoutWaitResult(value) {
|
|
27
28
|
return Boolean(value && typeof value === "object" && "status" in value && String(value.status) === "timeout");
|
|
@@ -33,6 +34,12 @@ async function rollbackBinding(action, previousOperatorBinding, persistOperatorR
|
|
|
33
34
|
}
|
|
34
35
|
await clearOperatorBinding().catch(() => { });
|
|
35
36
|
}
|
|
37
|
+
function isSameOperatorBinding(left, right) {
|
|
38
|
+
if (!left || !right) {
|
|
39
|
+
return false;
|
|
40
|
+
}
|
|
41
|
+
return left.wechatAccountId === right.wechatAccountId && left.userId === right.userId && left.boundAt === right.boundAt;
|
|
42
|
+
}
|
|
36
43
|
async function renderQrTerminalDefault(input) {
|
|
37
44
|
return await new Promise((resolve) => {
|
|
38
45
|
qrcodeTerminal.generate(input.value, { small: true }, (output) => {
|
|
@@ -81,49 +88,45 @@ export async function runWechatBindFlow(input) {
|
|
|
81
88
|
if (waited && typeof waited === "object" && "connected" in waited && waited.connected === false) {
|
|
82
89
|
throw new Error("qr login did not complete");
|
|
83
90
|
}
|
|
84
|
-
const
|
|
85
|
-
if (!
|
|
91
|
+
const rawAccountId = pickFirstNonEmptyString(waited?.accountId);
|
|
92
|
+
if (!rawAccountId) {
|
|
86
93
|
throw new Error("missing accountId after qr login");
|
|
87
94
|
}
|
|
95
|
+
const accountId = normalizeAccountId(rawAccountId);
|
|
88
96
|
const boundAt = now();
|
|
89
|
-
const userIdFromWait = pickFirstNonEmptyString(waited?.userId);
|
|
90
97
|
const previousOperatorBinding = input.action === "wechat-rebind" ? await loadOperatorBinding() : undefined;
|
|
98
|
+
const userIdFromWait = pickFirstNonEmptyString(waited?.userId);
|
|
91
99
|
let menuAccount;
|
|
92
100
|
let boundUserId = "";
|
|
93
101
|
let shouldRollbackBinding = false;
|
|
102
|
+
let attemptedOperatorBinding;
|
|
94
103
|
try {
|
|
95
|
-
const menuAccountState =
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
baseUrl: "https://ilinkai.weixin.qq.com",
|
|
101
|
-
}),
|
|
102
|
-
accountId,
|
|
103
|
-
...(userIdFromWait ? { userId: userIdFromWait } : {}),
|
|
104
|
-
boundAt,
|
|
105
|
-
}
|
|
106
|
-
: helpers.latestAccountState;
|
|
104
|
+
const menuAccountState = {
|
|
105
|
+
accountId,
|
|
106
|
+
token: "",
|
|
107
|
+
baseUrl: "https://ilinkai.weixin.qq.com",
|
|
108
|
+
};
|
|
107
109
|
menuAccount = await buildOpenClawMenuAccount({
|
|
108
110
|
latestAccountState: menuAccountState,
|
|
109
111
|
accountHelpers: helpers.accountHelpers,
|
|
110
112
|
});
|
|
111
|
-
const userId = pickFirstNonEmptyString(
|
|
113
|
+
const userId = pickFirstNonEmptyString(menuAccount?.userId, userIdFromWait);
|
|
112
114
|
if (!userId) {
|
|
113
115
|
throw new Error("missing userId after qr login");
|
|
114
116
|
}
|
|
115
117
|
boundUserId = userId;
|
|
116
|
-
|
|
118
|
+
attemptedOperatorBinding = {
|
|
117
119
|
wechatAccountId: accountId,
|
|
118
120
|
userId,
|
|
119
121
|
boundAt,
|
|
120
122
|
};
|
|
121
|
-
shouldRollbackBinding = true;
|
|
122
123
|
if (input.action === "wechat-rebind") {
|
|
123
|
-
|
|
124
|
+
shouldRollbackBinding = true;
|
|
125
|
+
await persistOperatorRebinding(attemptedOperatorBinding);
|
|
124
126
|
}
|
|
125
127
|
else {
|
|
126
|
-
|
|
128
|
+
shouldRollbackBinding = true;
|
|
129
|
+
await persistOperatorBinding(attemptedOperatorBinding);
|
|
127
130
|
}
|
|
128
131
|
const settings = await input.readCommonSettings();
|
|
129
132
|
const notifications = settings.wechat?.notifications ?? {
|
|
@@ -148,7 +151,15 @@ export async function runWechatBindFlow(input) {
|
|
|
148
151
|
}
|
|
149
152
|
catch (error) {
|
|
150
153
|
if (shouldRollbackBinding) {
|
|
151
|
-
|
|
154
|
+
if (input.action === "wechat-bind") {
|
|
155
|
+
const currentOperatorBinding = await loadOperatorBinding().catch(() => undefined);
|
|
156
|
+
if (isSameOperatorBinding(currentOperatorBinding, attemptedOperatorBinding)) {
|
|
157
|
+
await rollbackBinding(input.action, previousOperatorBinding, persistOperatorRebinding, clearOperatorBinding);
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
else {
|
|
161
|
+
await rollbackBinding(input.action, previousOperatorBinding, persistOperatorRebinding, clearOperatorBinding);
|
|
162
|
+
}
|
|
152
163
|
}
|
|
153
164
|
throw error;
|
|
154
165
|
}
|
|
@@ -15,33 +15,11 @@ function getAccountJiti() {
|
|
|
15
15
|
function asObject(value) {
|
|
16
16
|
return value && typeof value === "object" ? value : {};
|
|
17
17
|
}
|
|
18
|
-
function
|
|
19
|
-
|
|
20
|
-
return value;
|
|
21
|
-
}
|
|
22
|
-
if (value === 1) {
|
|
23
|
-
return true;
|
|
24
|
-
}
|
|
25
|
-
if (value === 0) {
|
|
26
|
-
return false;
|
|
27
|
-
}
|
|
28
|
-
return undefined;
|
|
29
|
-
}
|
|
30
|
-
function deriveEnabled(resolved, stored) {
|
|
31
|
-
const explicitEnabled = toBoolean(resolved.enabled) ?? toBoolean(resolved.isEnabled) ?? toBoolean(stored.enabled) ?? toBoolean(stored.isEnabled);
|
|
32
|
-
if (explicitEnabled !== undefined) {
|
|
33
|
-
return explicitEnabled;
|
|
34
|
-
}
|
|
35
|
-
if (resolved.disabled === true || stored.disabled === true) {
|
|
36
|
-
return false;
|
|
37
|
-
}
|
|
38
|
-
return true;
|
|
18
|
+
function deriveEnabled(resolved) {
|
|
19
|
+
return resolved.enabled === true;
|
|
39
20
|
}
|
|
40
|
-
function isConfigured(resolved
|
|
41
|
-
|
|
42
|
-
return resolved.configured;
|
|
43
|
-
}
|
|
44
|
-
return typeof stored.token === "string" && stored.token.trim().length > 0;
|
|
21
|
+
function isConfigured(resolved) {
|
|
22
|
+
return resolved.configured === true;
|
|
45
23
|
}
|
|
46
24
|
function toAccountId(input) {
|
|
47
25
|
return typeof input === "string" ? input : input.accountId;
|
|
@@ -52,8 +30,8 @@ export function createOpenClawAccountHelpers(input) {
|
|
|
52
30
|
const stored = asObject(await input.loadAccount(accountId));
|
|
53
31
|
return {
|
|
54
32
|
accountId,
|
|
55
|
-
enabled: deriveEnabled(resolved
|
|
56
|
-
configured: isConfigured(resolved
|
|
33
|
+
enabled: deriveEnabled(resolved),
|
|
34
|
+
configured: isConfigured(resolved),
|
|
57
35
|
name: typeof resolved.name === "string" ? resolved.name : undefined,
|
|
58
36
|
userId: typeof stored.userId === "string" ? stored.userId : undefined,
|
|
59
37
|
};
|
|
@@ -81,6 +59,13 @@ export async function loadOpenClawAccountHelpers(options = {}) {
|
|
|
81
59
|
return createOpenClawAccountHelpers({
|
|
82
60
|
listAccountIds: () => accountsModule.listIndexedWeixinAccountIds(),
|
|
83
61
|
loadAccount: (accountId) => accountsModule.loadWeixinAccount(accountId),
|
|
84
|
-
resolveAccount: (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
|
+
},
|
|
85
70
|
});
|
|
86
71
|
}
|
|
@@ -394,7 +394,7 @@ async function runQrLoginDefault(input) {
|
|
|
394
394
|
verbose: false,
|
|
395
395
|
}));
|
|
396
396
|
const qrTerminal = pickFirstString(startResult, ["qrTerminal"]);
|
|
397
|
-
const qrUrl = pickFirstString(startResult, ["qrDataUrl"
|
|
397
|
+
const qrUrl = pickFirstString(startResult, ["qrDataUrl"]);
|
|
398
398
|
const sessionKey = pickFirstString(startResult, ["sessionKey"]);
|
|
399
399
|
const qrStartMessage = pickFirstString(startResult, ["message", "detail", "reason"]);
|
|
400
400
|
if (!sessionKey) {
|
|
@@ -695,23 +695,13 @@ function normalizeQrLoginResult(result) {
|
|
|
695
695
|
return null;
|
|
696
696
|
}
|
|
697
697
|
if (candidate.status === "success") {
|
|
698
|
-
if (
|
|
699
|
-
return
|
|
700
|
-
status: "timeout",
|
|
701
|
-
qrPrinted: candidate.qrPrinted === true,
|
|
702
|
-
qrUrl: typeof candidate.qrDataUrl === "string" && candidate.qrDataUrl.trim().length > 0
|
|
703
|
-
? candidate.qrDataUrl
|
|
704
|
-
: typeof candidate.qrUrl === "string"
|
|
705
|
-
? candidate.qrUrl
|
|
706
|
-
: undefined,
|
|
707
|
-
};
|
|
698
|
+
if (candidate.connected !== true) {
|
|
699
|
+
return null;
|
|
708
700
|
}
|
|
709
701
|
const hasPrintedQr = candidate.qrPrinted === true;
|
|
710
702
|
const qrUrl = typeof candidate.qrDataUrl === "string" && candidate.qrDataUrl.trim().length > 0
|
|
711
703
|
? candidate.qrDataUrl
|
|
712
|
-
:
|
|
713
|
-
? candidate.qrUrl
|
|
714
|
-
: undefined;
|
|
704
|
+
: undefined;
|
|
715
705
|
const hasQrUrl = Boolean(qrUrl);
|
|
716
706
|
if (!hasPrintedQr && !hasQrUrl) {
|
|
717
707
|
return null;
|
|
@@ -719,9 +709,7 @@ function normalizeQrLoginResult(result) {
|
|
|
719
709
|
}
|
|
720
710
|
const normalizedQrUrl = typeof candidate.qrDataUrl === "string" && candidate.qrDataUrl.trim().length > 0
|
|
721
711
|
? candidate.qrDataUrl
|
|
722
|
-
:
|
|
723
|
-
? candidate.qrUrl
|
|
724
|
-
: undefined;
|
|
712
|
+
: undefined;
|
|
725
713
|
return {
|
|
726
714
|
status: candidate.status,
|
|
727
715
|
connected: candidate.connected === true ? true : undefined,
|
|
@@ -34,12 +34,6 @@ export async function loadOpenClawQrGateway(payloads, options = {}) {
|
|
|
34
34
|
pluginId: resolvedPluginId,
|
|
35
35
|
};
|
|
36
36
|
}
|
|
37
|
-
if (hasQrLoginMethods(payloadPlugin)) {
|
|
38
|
-
return {
|
|
39
|
-
gateway: createOpenClawQrGateway(payloadPlugin),
|
|
40
|
-
pluginId: resolvedPluginId,
|
|
41
|
-
};
|
|
42
|
-
}
|
|
43
37
|
}
|
|
44
38
|
throw new Error("registerChannel did not expose weixin gateway loginWithQrStart/loginWithQrWait");
|
|
45
39
|
}
|
|
@@ -11,7 +11,7 @@ export declare function createOpenClawSyncBufHelper(input: {
|
|
|
11
11
|
export declare function loadOpenClawSyncBufHelper(options?: {
|
|
12
12
|
syncBufModulePath?: string;
|
|
13
13
|
}): Promise<{
|
|
14
|
-
persistGetUpdatesBuf
|
|
14
|
+
persistGetUpdatesBuf: PublicWeixinPersistGetUpdatesBuf;
|
|
15
15
|
}>;
|
|
16
16
|
export declare function loadLatestWeixinAccountState(options?: {
|
|
17
17
|
stateDirModulePath?: string;
|
|
@@ -19,6 +19,9 @@ export function createOpenClawSyncBufHelper(input) {
|
|
|
19
19
|
return {
|
|
20
20
|
async persistGetUpdatesBuf({ accountId, getUpdatesBuf }) {
|
|
21
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
|
+
}
|
|
22
25
|
input.saveGetUpdatesBuf(filePath, getUpdatesBuf);
|
|
23
26
|
},
|
|
24
27
|
};
|
|
@@ -28,7 +31,7 @@ export async function loadOpenClawSyncBufHelper(options = {}) {
|
|
|
28
31
|
const syncBufModulePath = require.resolve(options.syncBufModulePath ?? OPENCLAW_SYNC_BUF_MODULE);
|
|
29
32
|
const syncBufModule = getSyncBufJiti()(syncBufModulePath);
|
|
30
33
|
if (typeof syncBufModule.getSyncBufFilePath !== "function" || typeof syncBufModule.saveGetUpdatesBuf !== "function") {
|
|
31
|
-
|
|
34
|
+
throw new Error("[wechat-compat] sync-buf source helper unavailable");
|
|
32
35
|
}
|
|
33
36
|
return createOpenClawSyncBufHelper({
|
|
34
37
|
getSyncBufFilePath: syncBufModule.getSyncBufFilePath,
|
|
@@ -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,
|