@openclaw/qqbot 2026.5.12 → 2026.5.14-beta.2
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/api.js +6 -6
- package/dist/channel-nG7HntSR.js +1102 -0
- package/dist/channel-plugin-api.js +1 -1
- package/dist/{channel.setup-D_GS95Pk.js → channel.setup-D_8Ryfln.js} +1 -2
- package/dist/config-schema-BE5YP0NG.js +678 -0
- package/dist/{gateway-Bbsd8-ne.js → gateway-Bz_nu9BP.js} +98 -33
- package/dist/{handler-runtime-ZgvjnyoH.js → handler-runtime-DMMHU4NC.js} +3 -5
- package/dist/{outbound-BLWish89.js → outbound-DTQUASKu.js} +11 -3
- package/dist/{request-context-BwSBFANU.js → request-context-DiWgzdpn.js} +2 -2
- package/dist/{sender-BrpzCcXA.js → runtime-BFcYWYuk.js} +27 -6
- package/dist/runtime-api.js +1 -1
- package/dist/setup-plugin-api.js +1 -1
- package/package.json +5 -5
- package/dist/approval-cg0SVahb.js +0 -94
- package/dist/channel-CmK-n3Oz.js +0 -621
- package/dist/config-BT8KP_H1.js +0 -94
- package/dist/config-schema-BCxobX3l.js +0 -309
- package/dist/exec-approvals-W4oL9R6C.js +0 -138
- package/dist/narrowing-BoieBTIU.js +0 -25
- package/dist/resolve-D3lCbUze.js +0 -284
- package/dist/runtime-CZyFkpnB.js +0 -18
- package/dist/target-parser-C8HaD-ik.js +0 -245
package/dist/config-BT8KP_H1.js
DELETED
|
@@ -1,94 +0,0 @@
|
|
|
1
|
-
import { c as getPlatformAdapter } from "./string-normalize-C46CS-F1.js";
|
|
2
|
-
import { c as resolveDefaultAccountId, n as applyAccountConfig, o as listAccountIds, s as resolveAccountBase, t as DEFAULT_ACCOUNT_ID$1 } from "./resolve-D3lCbUze.js";
|
|
3
|
-
import { coerceSecretRef, normalizeSecretInputString } from "openclaw/plugin-sdk/secret-input";
|
|
4
|
-
import fs from "node:fs";
|
|
5
|
-
import { resolveDefaultSecretProviderAlias } from "openclaw/plugin-sdk/provider-auth";
|
|
6
|
-
//#region extensions/qqbot/src/bridge/config.ts
|
|
7
|
-
const DEFAULT_ACCOUNT_ID = DEFAULT_ACCOUNT_ID$1;
|
|
8
|
-
function assertNotLegacySecretRefMarker(value, path) {
|
|
9
|
-
const normalized = normalizeSecretInputString(value);
|
|
10
|
-
if (!normalized || !/^secretref(?:-env)?:/i.test(normalized)) return;
|
|
11
|
-
throw new Error(`${path}: legacy SecretRef marker strings are not valid QQ Bot clientSecret values; use a structured SecretRef object instead.`);
|
|
12
|
-
}
|
|
13
|
-
function resolveEnvSecretRefValue(params) {
|
|
14
|
-
const ref = coerceSecretRef(params.value, params.cfg.secrets?.defaults);
|
|
15
|
-
if (!ref || ref.source !== "env") return;
|
|
16
|
-
const providerConfig = params.cfg.secrets?.providers?.[ref.provider];
|
|
17
|
-
if (providerConfig) {
|
|
18
|
-
if (providerConfig.source !== "env") throw new Error(`Secret provider "${ref.provider}" has source "${providerConfig.source}" but ref requests "env".`);
|
|
19
|
-
if (providerConfig.allowlist && !providerConfig.allowlist.includes(ref.id)) throw new Error(`Environment variable "${ref.id}" is not allowlisted in secrets.providers.${ref.provider}.allowlist.`);
|
|
20
|
-
} else if (ref.provider !== resolveDefaultSecretProviderAlias(params.cfg, "env")) throw new Error(`Secret provider "${ref.provider}" is not configured (ref: env:${ref.provider}:${ref.id}).`);
|
|
21
|
-
return normalizeSecretInputString((params.env ?? process.env)[ref.id]);
|
|
22
|
-
}
|
|
23
|
-
function resolveQQBotClientSecretInput(params) {
|
|
24
|
-
assertNotLegacySecretRefMarker(params.value, params.path);
|
|
25
|
-
const envSecret = resolveEnvSecretRefValue({
|
|
26
|
-
cfg: params.cfg,
|
|
27
|
-
value: params.value
|
|
28
|
-
});
|
|
29
|
-
if (envSecret) return envSecret;
|
|
30
|
-
return getPlatformAdapter().resolveSecretInputString({
|
|
31
|
-
value: params.value,
|
|
32
|
-
path: params.path
|
|
33
|
-
});
|
|
34
|
-
}
|
|
35
|
-
/** List all configured QQBot account IDs. */
|
|
36
|
-
function listQQBotAccountIds(cfg) {
|
|
37
|
-
return listAccountIds(cfg);
|
|
38
|
-
}
|
|
39
|
-
/** Resolve the default QQBot account ID. */
|
|
40
|
-
function resolveDefaultQQBotAccountId(cfg) {
|
|
41
|
-
return resolveDefaultAccountId(cfg);
|
|
42
|
-
}
|
|
43
|
-
/** Resolve QQBot account config for runtime or setup flows. */
|
|
44
|
-
function resolveQQBotAccount(cfg, accountId, opts) {
|
|
45
|
-
const base = resolveAccountBase(cfg, accountId);
|
|
46
|
-
const qqbot = cfg.channels?.qqbot;
|
|
47
|
-
/**
|
|
48
|
-
* Legacy top-level account uses `channels.qqbot` as the base, but per-account
|
|
49
|
-
* fields (allowFrom, streaming, …) often live under `accounts.default`.
|
|
50
|
-
* Merge that slice so runtime sees `config.streaming` etc.
|
|
51
|
-
*/
|
|
52
|
-
const accountConfig = base.accountId === DEFAULT_ACCOUNT_ID ? {
|
|
53
|
-
...qqbot,
|
|
54
|
-
...qqbot?.accounts?.[DEFAULT_ACCOUNT_ID]
|
|
55
|
-
} : qqbot?.accounts?.[base.accountId] ?? {};
|
|
56
|
-
let clientSecret = "";
|
|
57
|
-
let secretSource = "none";
|
|
58
|
-
const clientSecretPath = base.accountId === DEFAULT_ACCOUNT_ID ? "channels.qqbot.clientSecret" : `channels.qqbot.accounts.${base.accountId}.clientSecret`;
|
|
59
|
-
const adapter = getPlatformAdapter();
|
|
60
|
-
if (adapter.hasConfiguredSecret(accountConfig.clientSecret)) {
|
|
61
|
-
clientSecret = opts?.allowUnresolvedSecretRef ? adapter.normalizeSecretInputString(accountConfig.clientSecret) ?? "" : resolveQQBotClientSecretInput({
|
|
62
|
-
cfg,
|
|
63
|
-
value: accountConfig.clientSecret,
|
|
64
|
-
path: clientSecretPath
|
|
65
|
-
}) ?? "";
|
|
66
|
-
secretSource = "config";
|
|
67
|
-
} else if (accountConfig.clientSecretFile) try {
|
|
68
|
-
clientSecret = fs.readFileSync(accountConfig.clientSecretFile, "utf8").trim();
|
|
69
|
-
secretSource = "file";
|
|
70
|
-
} catch {
|
|
71
|
-
secretSource = "none";
|
|
72
|
-
}
|
|
73
|
-
else if (process.env.QQBOT_CLIENT_SECRET && base.accountId === DEFAULT_ACCOUNT_ID) {
|
|
74
|
-
clientSecret = process.env.QQBOT_CLIENT_SECRET;
|
|
75
|
-
secretSource = "env";
|
|
76
|
-
}
|
|
77
|
-
return {
|
|
78
|
-
accountId: base.accountId,
|
|
79
|
-
name: accountConfig.name,
|
|
80
|
-
enabled: base.enabled,
|
|
81
|
-
appId: base.appId,
|
|
82
|
-
clientSecret,
|
|
83
|
-
secretSource,
|
|
84
|
-
systemPrompt: base.systemPrompt,
|
|
85
|
-
markdownSupport: base.markdownSupport,
|
|
86
|
-
config: accountConfig
|
|
87
|
-
};
|
|
88
|
-
}
|
|
89
|
-
/** Apply account config updates back into the OpenClaw config object. */
|
|
90
|
-
function applyQQBotAccountConfig(cfg, accountId, input) {
|
|
91
|
-
return applyAccountConfig(cfg, accountId, input);
|
|
92
|
-
}
|
|
93
|
-
//#endregion
|
|
94
|
-
export { resolveQQBotAccount as a, resolveDefaultQQBotAccountId as i, applyQQBotAccountConfig as n, listQQBotAccountIds as r, DEFAULT_ACCOUNT_ID as t };
|
|
@@ -1,309 +0,0 @@
|
|
|
1
|
-
import { n as normalizeLowercaseStringOrEmpty } from "./string-normalize-C46CS-F1.js";
|
|
2
|
-
import { a as isAccountConfigured, i as formatAllowFrom, n as applyAccountConfig, r as describeAccount } from "./resolve-D3lCbUze.js";
|
|
3
|
-
import { a as resolveQQBotAccount, i as resolveDefaultQQBotAccountId, n as applyQQBotAccountConfig, r as listQQBotAccountIds } from "./config-BT8KP_H1.js";
|
|
4
|
-
import { buildSecretInputSchema } from "openclaw/plugin-sdk/secret-input";
|
|
5
|
-
import { applyAccountNameToChannelSection, deleteAccountFromConfigSection, setAccountEnabledInConfigSection } from "openclaw/plugin-sdk/core";
|
|
6
|
-
import { DEFAULT_ACCOUNT_ID, createStandardChannelSetupStatus, setSetupChannelEnabled } from "openclaw/plugin-sdk/setup";
|
|
7
|
-
import { formatDocsLink } from "openclaw/plugin-sdk/setup-tools";
|
|
8
|
-
import { AllowFromListSchema, buildChannelConfigSchema } from "openclaw/plugin-sdk/channel-config-schema";
|
|
9
|
-
import { z } from "zod";
|
|
10
|
-
//#region extensions/qqbot/src/engine/config/setup-logic.ts
|
|
11
|
-
/**
|
|
12
|
-
* QQBot setup business logic (pure layer).
|
|
13
|
-
* QQBot setup 相关纯业务逻辑。
|
|
14
|
-
*
|
|
15
|
-
* Token parsing, input validation, and setup config application.
|
|
16
|
-
* All functions are framework-agnostic and operate on plain objects.
|
|
17
|
-
*/
|
|
18
|
-
/** Parse an inline "appId:clientSecret" token string. */
|
|
19
|
-
function parseInlineToken(token) {
|
|
20
|
-
const colonIdx = token.indexOf(":");
|
|
21
|
-
if (colonIdx <= 0 || colonIdx === token.length - 1) return null;
|
|
22
|
-
const appId = token.slice(0, colonIdx).trim();
|
|
23
|
-
const clientSecret = token.slice(colonIdx + 1).trim();
|
|
24
|
-
if (!appId || !clientSecret) return null;
|
|
25
|
-
return {
|
|
26
|
-
appId,
|
|
27
|
-
clientSecret
|
|
28
|
-
};
|
|
29
|
-
}
|
|
30
|
-
/** Validate setup input for a QQBot account. Returns an error string or null. */
|
|
31
|
-
function validateSetupInput(accountId, input) {
|
|
32
|
-
if (!input.token && !input.tokenFile && !input.useEnv) return "QQBot requires --token (format: appId:clientSecret) or --use-env";
|
|
33
|
-
if (input.useEnv && accountId !== "default") return "QQBot --use-env only supports the default account";
|
|
34
|
-
if (input.token && !parseInlineToken(input.token)) return "QQBot --token must be in appId:clientSecret format";
|
|
35
|
-
return null;
|
|
36
|
-
}
|
|
37
|
-
/** Apply setup input to account config. Returns updated config. */
|
|
38
|
-
function applySetupAccountConfig(cfg, accountId, input) {
|
|
39
|
-
if (input.useEnv && accountId !== "default") return cfg;
|
|
40
|
-
let appId = "";
|
|
41
|
-
let clientSecret = "";
|
|
42
|
-
if (input.token) {
|
|
43
|
-
const parsed = parseInlineToken(input.token);
|
|
44
|
-
if (!parsed) return cfg;
|
|
45
|
-
appId = parsed.appId;
|
|
46
|
-
clientSecret = parsed.clientSecret;
|
|
47
|
-
}
|
|
48
|
-
if (!appId && !input.tokenFile && !input.useEnv) return cfg;
|
|
49
|
-
return applyAccountConfig(cfg, accountId, {
|
|
50
|
-
appId,
|
|
51
|
-
clientSecret,
|
|
52
|
-
clientSecretFile: input.tokenFile,
|
|
53
|
-
name: input.name
|
|
54
|
-
});
|
|
55
|
-
}
|
|
56
|
-
//#endregion
|
|
57
|
-
//#region extensions/qqbot/src/bridge/config-shared.ts
|
|
58
|
-
const qqbotMeta = {
|
|
59
|
-
id: "qqbot",
|
|
60
|
-
label: "QQ Bot",
|
|
61
|
-
selectionLabel: "QQ Bot (Bot API)",
|
|
62
|
-
docsPath: "/channels/qqbot",
|
|
63
|
-
blurb: "Connect to QQ via official QQ Bot API",
|
|
64
|
-
order: 50
|
|
65
|
-
};
|
|
66
|
-
function validateQQBotSetupInput(params) {
|
|
67
|
-
return validateSetupInput(params.accountId, params.input);
|
|
68
|
-
}
|
|
69
|
-
function applyQQBotSetupAccountConfig(params) {
|
|
70
|
-
return applySetupAccountConfig(params.cfg, params.accountId, params.input);
|
|
71
|
-
}
|
|
72
|
-
function isQQBotConfigured(account) {
|
|
73
|
-
return isAccountConfigured(account);
|
|
74
|
-
}
|
|
75
|
-
function describeQQBotAccount(account) {
|
|
76
|
-
return describeAccount(account);
|
|
77
|
-
}
|
|
78
|
-
function formatQQBotAllowFrom(params) {
|
|
79
|
-
return formatAllowFrom(params.allowFrom);
|
|
80
|
-
}
|
|
81
|
-
const qqbotConfigAdapter = {
|
|
82
|
-
listAccountIds: (cfg) => listQQBotAccountIds(cfg),
|
|
83
|
-
resolveAccount: (cfg, accountId) => resolveQQBotAccount(cfg, accountId, { allowUnresolvedSecretRef: true }),
|
|
84
|
-
defaultAccountId: (cfg) => resolveDefaultQQBotAccountId(cfg),
|
|
85
|
-
setAccountEnabled: ({ cfg, accountId, enabled }) => setAccountEnabledInConfigSection({
|
|
86
|
-
cfg,
|
|
87
|
-
sectionKey: "qqbot",
|
|
88
|
-
accountId,
|
|
89
|
-
enabled,
|
|
90
|
-
allowTopLevel: true
|
|
91
|
-
}),
|
|
92
|
-
deleteAccount: ({ cfg, accountId }) => deleteAccountFromConfigSection({
|
|
93
|
-
cfg,
|
|
94
|
-
sectionKey: "qqbot",
|
|
95
|
-
accountId,
|
|
96
|
-
clearBaseFields: [
|
|
97
|
-
"appId",
|
|
98
|
-
"clientSecret",
|
|
99
|
-
"clientSecretFile",
|
|
100
|
-
"name"
|
|
101
|
-
]
|
|
102
|
-
}),
|
|
103
|
-
isConfigured: isQQBotConfigured,
|
|
104
|
-
describeAccount: describeQQBotAccount,
|
|
105
|
-
resolveAllowFrom: ({ cfg, accountId }) => resolveQQBotAccount(cfg, accountId, { allowUnresolvedSecretRef: true }).config?.allowFrom,
|
|
106
|
-
formatAllowFrom: ({ allowFrom }) => formatQQBotAllowFrom({ allowFrom })
|
|
107
|
-
};
|
|
108
|
-
const qqbotSetupAdapterShared = {
|
|
109
|
-
resolveAccountId: ({ cfg, accountId }) => normalizeLowercaseStringOrEmpty(accountId) || resolveDefaultQQBotAccountId(cfg),
|
|
110
|
-
applyAccountName: ({ cfg, accountId, name }) => applyAccountNameToChannelSection({
|
|
111
|
-
cfg,
|
|
112
|
-
channelKey: "qqbot",
|
|
113
|
-
accountId,
|
|
114
|
-
name
|
|
115
|
-
}),
|
|
116
|
-
validateInput: ({ accountId, input }) => validateQQBotSetupInput({
|
|
117
|
-
accountId,
|
|
118
|
-
input
|
|
119
|
-
}),
|
|
120
|
-
applyAccountConfig: ({ cfg, accountId, input }) => applyQQBotSetupAccountConfig({
|
|
121
|
-
cfg,
|
|
122
|
-
accountId,
|
|
123
|
-
input
|
|
124
|
-
})
|
|
125
|
-
};
|
|
126
|
-
//#endregion
|
|
127
|
-
//#region extensions/qqbot/src/bridge/setup/finalize.ts
|
|
128
|
-
function isQQBotAccountConfigured(cfg, accountId) {
|
|
129
|
-
const account = resolveQQBotAccount(cfg, accountId, { allowUnresolvedSecretRef: true });
|
|
130
|
-
return Boolean(account.appId && account.clientSecret);
|
|
131
|
-
}
|
|
132
|
-
async function linkViaQrCode(params) {
|
|
133
|
-
try {
|
|
134
|
-
const { qrConnect } = await import("@tencent-connect/qqbot-connector");
|
|
135
|
-
const accounts = await qrConnect({ source: "openclaw" });
|
|
136
|
-
if (accounts.length === 0) {
|
|
137
|
-
await params.prompter.note("未获取到任何 QQ Bot 账号信息。", "QQ Bot");
|
|
138
|
-
return params.cfg;
|
|
139
|
-
}
|
|
140
|
-
let next = params.cfg;
|
|
141
|
-
for (let i = 0; i < accounts.length; i++) {
|
|
142
|
-
const { appId, appSecret } = accounts[i];
|
|
143
|
-
const targetAccountId = i === 0 ? params.accountId : appId;
|
|
144
|
-
next = applyQQBotAccountConfig(next, targetAccountId, {
|
|
145
|
-
appId,
|
|
146
|
-
clientSecret: appSecret
|
|
147
|
-
});
|
|
148
|
-
}
|
|
149
|
-
if (accounts.length === 1) params.runtime.log(`✔ QQ Bot 绑定成功!(AppID: ${accounts[0].appId})`);
|
|
150
|
-
else {
|
|
151
|
-
const idList = accounts.map((a) => a.appId).join(", ");
|
|
152
|
-
params.runtime.log(`✔ ${accounts.length} 个 QQ Bot 绑定成功!(AppID: ${idList})`);
|
|
153
|
-
}
|
|
154
|
-
return next;
|
|
155
|
-
} catch (error) {
|
|
156
|
-
params.runtime.error(`QQ Bot 绑定失败: ${String(error)}`);
|
|
157
|
-
await params.prompter.note(["绑定失败,您可以稍后手动配置。", `文档: ${formatDocsLink("/channels/qqbot", "qqbot")}`].join("\n"), "QQ Bot");
|
|
158
|
-
return params.cfg;
|
|
159
|
-
}
|
|
160
|
-
}
|
|
161
|
-
async function linkViaManualInput(params) {
|
|
162
|
-
const appId = await params.prompter.text({
|
|
163
|
-
message: "请输入 QQ Bot AppID",
|
|
164
|
-
validate: (value) => value.trim() ? void 0 : "AppID 不能为空"
|
|
165
|
-
});
|
|
166
|
-
const appSecret = await params.prompter.text({
|
|
167
|
-
message: "请输入 QQ Bot AppSecret",
|
|
168
|
-
validate: (value) => value.trim() ? void 0 : "AppSecret 不能为空"
|
|
169
|
-
});
|
|
170
|
-
const next = applyQQBotAccountConfig(params.cfg, params.accountId, {
|
|
171
|
-
appId: appId.trim(),
|
|
172
|
-
clientSecret: appSecret.trim()
|
|
173
|
-
});
|
|
174
|
-
await params.prompter.note("✔ QQ Bot 配置完成!", "QQ Bot");
|
|
175
|
-
return next;
|
|
176
|
-
}
|
|
177
|
-
async function finalizeQQBotSetup(params) {
|
|
178
|
-
const accountId = params.accountId.trim() || DEFAULT_ACCOUNT_ID;
|
|
179
|
-
let next = params.cfg;
|
|
180
|
-
const configured = isQQBotAccountConfigured(next, accountId);
|
|
181
|
-
const mode = await params.prompter.select({
|
|
182
|
-
message: configured ? "QQ 已绑定,选择操作" : "选择 QQ 绑定方式",
|
|
183
|
-
options: [
|
|
184
|
-
{
|
|
185
|
-
value: "qr",
|
|
186
|
-
label: "扫码绑定(推荐)",
|
|
187
|
-
hint: "使用 QQ 扫描二维码自动完成绑定"
|
|
188
|
-
},
|
|
189
|
-
{
|
|
190
|
-
value: "manual",
|
|
191
|
-
label: "手动输入 QQ Bot AppID 和 AppSecret",
|
|
192
|
-
hint: "需到 QQ 开放平台 q.qq.com 查看"
|
|
193
|
-
},
|
|
194
|
-
{
|
|
195
|
-
value: "skip",
|
|
196
|
-
label: configured ? "保持当前配置" : "稍后配置"
|
|
197
|
-
}
|
|
198
|
-
]
|
|
199
|
-
});
|
|
200
|
-
if (mode === "qr") next = await linkViaQrCode({
|
|
201
|
-
cfg: next,
|
|
202
|
-
accountId,
|
|
203
|
-
prompter: params.prompter,
|
|
204
|
-
runtime: params.runtime
|
|
205
|
-
});
|
|
206
|
-
else if (mode === "manual") next = await linkViaManualInput({
|
|
207
|
-
cfg: next,
|
|
208
|
-
accountId,
|
|
209
|
-
prompter: params.prompter
|
|
210
|
-
});
|
|
211
|
-
else if (!configured) await params.prompter.note(["您可以稍后运行以下命令重新选择 QQ Bot 进行配置:", " openclaw channels add"].join("\n"), "QQ Bot");
|
|
212
|
-
return { cfg: next };
|
|
213
|
-
}
|
|
214
|
-
//#endregion
|
|
215
|
-
//#region extensions/qqbot/src/bridge/setup/surface.ts
|
|
216
|
-
const channel = "qqbot";
|
|
217
|
-
const qqbotSetupWizard = {
|
|
218
|
-
channel,
|
|
219
|
-
status: createStandardChannelSetupStatus({
|
|
220
|
-
channelLabel: "QQ Bot",
|
|
221
|
-
configuredLabel: "configured",
|
|
222
|
-
unconfiguredLabel: "needs AppID + AppSercet",
|
|
223
|
-
configuredHint: "configured",
|
|
224
|
-
unconfiguredHint: "needs AppID + AppSercet",
|
|
225
|
-
configuredScore: 1,
|
|
226
|
-
unconfiguredScore: 6,
|
|
227
|
-
resolveConfigured: ({ cfg, accountId }) => (accountId ? [accountId] : listQQBotAccountIds(cfg)).some((resolvedAccountId) => {
|
|
228
|
-
return isAccountConfigured(resolveQQBotAccount(cfg, resolvedAccountId, { allowUnresolvedSecretRef: true }));
|
|
229
|
-
})
|
|
230
|
-
}),
|
|
231
|
-
credentials: [],
|
|
232
|
-
finalize: async ({ cfg, accountId, forceAllowFrom, prompter, runtime }) => await finalizeQQBotSetup({
|
|
233
|
-
cfg,
|
|
234
|
-
accountId,
|
|
235
|
-
forceAllowFrom,
|
|
236
|
-
prompter,
|
|
237
|
-
runtime
|
|
238
|
-
}),
|
|
239
|
-
disable: (cfg) => setSetupChannelEnabled(cfg, channel, false)
|
|
240
|
-
};
|
|
241
|
-
//#endregion
|
|
242
|
-
//#region extensions/qqbot/src/config-schema.ts
|
|
243
|
-
const AudioFormatPolicySchema = z.object({
|
|
244
|
-
sttDirectFormats: z.array(z.string()).optional(),
|
|
245
|
-
uploadDirectFormats: z.array(z.string()).optional(),
|
|
246
|
-
transcodeEnabled: z.boolean().optional()
|
|
247
|
-
}).optional();
|
|
248
|
-
const QQBotSttSchema = z.object({
|
|
249
|
-
enabled: z.boolean().optional(),
|
|
250
|
-
provider: z.string().optional(),
|
|
251
|
-
baseUrl: z.string().optional(),
|
|
252
|
-
apiKey: z.string().optional(),
|
|
253
|
-
model: z.string().optional()
|
|
254
|
-
}).strict().optional();
|
|
255
|
-
/** When `true`, same as `mode: "partial"` and `c2cStreamApi: true` for C2C. Object form kept for legacy configs. */
|
|
256
|
-
const QQBotStreamingSchema = z.union([z.boolean(), z.object({
|
|
257
|
-
/** "partial" (default) enables block streaming; "off" disables it. */
|
|
258
|
-
mode: z.enum(["off", "partial"]).default("partial"),
|
|
259
|
-
/** @deprecated Prefer `streaming: true`. */
|
|
260
|
-
c2cStreamApi: z.boolean().optional()
|
|
261
|
-
}).passthrough()]).optional();
|
|
262
|
-
const QQBotExecApprovalsSchema = z.object({
|
|
263
|
-
enabled: z.union([z.boolean(), z.literal("auto")]).optional(),
|
|
264
|
-
approvers: z.array(z.string()).optional(),
|
|
265
|
-
agentFilter: z.array(z.string()).optional(),
|
|
266
|
-
sessionFilter: z.array(z.string()).optional(),
|
|
267
|
-
target: z.enum([
|
|
268
|
-
"dm",
|
|
269
|
-
"channel",
|
|
270
|
-
"both"
|
|
271
|
-
]).optional()
|
|
272
|
-
}).strict().optional();
|
|
273
|
-
const QQBotDmPolicySchema = z.enum([
|
|
274
|
-
"open",
|
|
275
|
-
"allowlist",
|
|
276
|
-
"disabled"
|
|
277
|
-
]).optional();
|
|
278
|
-
const QQBotGroupPolicySchema = z.enum([
|
|
279
|
-
"open",
|
|
280
|
-
"allowlist",
|
|
281
|
-
"disabled"
|
|
282
|
-
]).optional();
|
|
283
|
-
const QQBotAccountSchema = z.object({
|
|
284
|
-
enabled: z.boolean().optional(),
|
|
285
|
-
name: z.string().optional(),
|
|
286
|
-
appId: z.string().optional(),
|
|
287
|
-
clientSecret: buildSecretInputSchema().optional(),
|
|
288
|
-
clientSecretFile: z.string().optional(),
|
|
289
|
-
allowFrom: AllowFromListSchema,
|
|
290
|
-
groupAllowFrom: AllowFromListSchema,
|
|
291
|
-
dmPolicy: QQBotDmPolicySchema,
|
|
292
|
-
groupPolicy: QQBotGroupPolicySchema,
|
|
293
|
-
systemPrompt: z.string().optional(),
|
|
294
|
-
markdownSupport: z.boolean().optional(),
|
|
295
|
-
voiceDirectUploadFormats: z.array(z.string()).optional(),
|
|
296
|
-
audioFormatPolicy: AudioFormatPolicySchema,
|
|
297
|
-
urlDirectUpload: z.boolean().optional(),
|
|
298
|
-
upgradeUrl: z.string().optional(),
|
|
299
|
-
upgradeMode: z.enum(["doc", "hot-reload"]).optional(),
|
|
300
|
-
streaming: QQBotStreamingSchema,
|
|
301
|
-
execApprovals: QQBotExecApprovalsSchema
|
|
302
|
-
}).passthrough();
|
|
303
|
-
const qqbotChannelConfigSchema = buildChannelConfigSchema(QQBotAccountSchema.extend({
|
|
304
|
-
stt: QQBotSttSchema,
|
|
305
|
-
accounts: z.object({}).catchall(QQBotAccountSchema.passthrough()).optional(),
|
|
306
|
-
defaultAccount: z.string().optional()
|
|
307
|
-
}).passthrough());
|
|
308
|
-
//#endregion
|
|
309
|
-
export { qqbotSetupAdapterShared as a, qqbotMeta as i, qqbotSetupWizard as n, qqbotConfigAdapter as r, qqbotChannelConfigSchema as t };
|
|
@@ -1,138 +0,0 @@
|
|
|
1
|
-
import { a as resolveQQBotAccount, r as listQQBotAccountIds } from "./config-BT8KP_H1.js";
|
|
2
|
-
import { resolveApprovalRequestChannelAccountId } from "openclaw/plugin-sdk/approval-native-runtime";
|
|
3
|
-
import { normalizeLowercaseStringOrEmpty, normalizeOptionalString } from "openclaw/plugin-sdk/string-coerce-runtime";
|
|
4
|
-
import { resolveApprovalApprovers } from "openclaw/plugin-sdk/approval-auth-runtime";
|
|
5
|
-
import { createChannelExecApprovalProfile, isChannelExecApprovalClientEnabledFromConfig, matchesApprovalRequestFilters } from "openclaw/plugin-sdk/approval-client-runtime";
|
|
6
|
-
import { normalizeAccountId } from "openclaw/plugin-sdk/routing";
|
|
7
|
-
//#region extensions/qqbot/src/exec-approvals.ts
|
|
8
|
-
function normalizeApproverId(value) {
|
|
9
|
-
return normalizeOptionalString(String(value)) || void 0;
|
|
10
|
-
}
|
|
11
|
-
function resolveQQBotExecApprovalConfig(params) {
|
|
12
|
-
const account = resolveQQBotAccount(params.cfg, params.accountId);
|
|
13
|
-
const config = account.config.execApprovals;
|
|
14
|
-
if (!config) return;
|
|
15
|
-
return {
|
|
16
|
-
...config,
|
|
17
|
-
enabled: account.enabled && account.secretSource !== "none" ? config.enabled : false
|
|
18
|
-
};
|
|
19
|
-
}
|
|
20
|
-
function getQQBotExecApprovalApprovers(params) {
|
|
21
|
-
const accountConfig = resolveQQBotAccount(params.cfg, params.accountId).config;
|
|
22
|
-
return resolveApprovalApprovers({
|
|
23
|
-
explicit: resolveQQBotExecApprovalConfig(params)?.approvers,
|
|
24
|
-
allowFrom: accountConfig.allowFrom,
|
|
25
|
-
normalizeApprover: normalizeApproverId
|
|
26
|
-
});
|
|
27
|
-
}
|
|
28
|
-
function countQQBotExecApprovalEligibleAccounts(params) {
|
|
29
|
-
return listQQBotAccountIds(params.cfg).filter((accountId) => {
|
|
30
|
-
const account = resolveQQBotAccount(params.cfg, accountId);
|
|
31
|
-
if (!account.enabled || account.secretSource === "none") return false;
|
|
32
|
-
const config = resolveQQBotExecApprovalConfig({
|
|
33
|
-
cfg: params.cfg,
|
|
34
|
-
accountId
|
|
35
|
-
});
|
|
36
|
-
return isChannelExecApprovalClientEnabledFromConfig({
|
|
37
|
-
enabled: config?.enabled,
|
|
38
|
-
approverCount: getQQBotExecApprovalApprovers({
|
|
39
|
-
cfg: params.cfg,
|
|
40
|
-
accountId
|
|
41
|
-
}).length
|
|
42
|
-
}) && matchesApprovalRequestFilters({
|
|
43
|
-
request: params.request.request,
|
|
44
|
-
agentFilter: config?.agentFilter,
|
|
45
|
-
sessionFilter: config?.sessionFilter,
|
|
46
|
-
fallbackAgentIdFromSessionKey: true
|
|
47
|
-
});
|
|
48
|
-
}).length;
|
|
49
|
-
}
|
|
50
|
-
function matchesQQBotRequestAccount(params) {
|
|
51
|
-
const turnSourceChannel = normalizeLowercaseStringOrEmpty(params.request.request.turnSourceChannel);
|
|
52
|
-
const boundAccountId = resolveApprovalRequestChannelAccountId({
|
|
53
|
-
cfg: params.cfg,
|
|
54
|
-
request: params.request,
|
|
55
|
-
channel: "qqbot"
|
|
56
|
-
});
|
|
57
|
-
if (turnSourceChannel && turnSourceChannel !== "qqbot" && !boundAccountId) return countQQBotExecApprovalEligibleAccounts({
|
|
58
|
-
cfg: params.cfg,
|
|
59
|
-
request: params.request
|
|
60
|
-
}) <= 1;
|
|
61
|
-
return !boundAccountId || !params.accountId || normalizeAccountId(boundAccountId) === normalizeAccountId(params.accountId);
|
|
62
|
-
}
|
|
63
|
-
/**
|
|
64
|
-
* Count QQBot accounts that could actually deliver a native approval
|
|
65
|
-
* message — i.e. accounts that are enabled and have resolvable secrets.
|
|
66
|
-
* Disabled or unconfigured accounts never spawn a handler, so they
|
|
67
|
-
* must not contribute to the single-account shortcut in the fallback
|
|
68
|
-
* ownership check below.
|
|
69
|
-
*/
|
|
70
|
-
function countQQBotFallbackEligibleAccounts(cfg) {
|
|
71
|
-
return listQQBotAccountIds(cfg).filter((accountId) => {
|
|
72
|
-
const account = resolveQQBotAccount(cfg, accountId);
|
|
73
|
-
return account.enabled && account.secretSource !== "none";
|
|
74
|
-
}).length;
|
|
75
|
-
}
|
|
76
|
-
/**
|
|
77
|
-
* Fallback account-ownership check — applied when `execApprovals` is NOT
|
|
78
|
-
* configured for any QQBot account. In this mode every enabled account
|
|
79
|
-
* handler would otherwise race to deliver the same approval to its own
|
|
80
|
-
* openid namespace, so we must enforce per-account isolation.
|
|
81
|
-
*
|
|
82
|
-
* Rules:
|
|
83
|
-
* - If the request carries a bound account (via `turnSourceAccountId`
|
|
84
|
-
* or session binding), only the handler whose `accountId` matches it
|
|
85
|
-
* delivers the approval. This is strict: a handler with an unknown
|
|
86
|
-
* `accountId` (null/undefined) must not claim a bound request.
|
|
87
|
-
* - If no account is bound, only deliver when there is a single
|
|
88
|
-
* *eligible* QQBot account (enabled + secret resolved). Disabled or
|
|
89
|
-
* unconfigured accounts never deliver anyway, so they shouldn't
|
|
90
|
-
* block the remaining single account from handling the approval.
|
|
91
|
-
* Multiple eligible accounts cannot safely race because openids are
|
|
92
|
-
* account-scoped — cross-account delivery hits the QQ Bot API with
|
|
93
|
-
* a mismatched token and fails.
|
|
94
|
-
*/
|
|
95
|
-
function matchesQQBotFallbackRequestAccount(params) {
|
|
96
|
-
const boundAccountId = resolveApprovalRequestChannelAccountId({
|
|
97
|
-
cfg: params.cfg,
|
|
98
|
-
request: params.request,
|
|
99
|
-
channel: "qqbot"
|
|
100
|
-
});
|
|
101
|
-
if (boundAccountId) {
|
|
102
|
-
if (!params.accountId) return false;
|
|
103
|
-
return normalizeAccountId(boundAccountId) === normalizeAccountId(params.accountId);
|
|
104
|
-
}
|
|
105
|
-
return countQQBotFallbackEligibleAccounts(params.cfg) <= 1;
|
|
106
|
-
}
|
|
107
|
-
/**
|
|
108
|
-
* Unified per-account ownership check used by both the profile and
|
|
109
|
-
* fallback approval paths. Dispatches to the profile rules when the
|
|
110
|
-
* current account has `execApprovals` configured, otherwise uses the
|
|
111
|
-
* fallback rules.
|
|
112
|
-
*
|
|
113
|
-
* This is the single source of truth for "does this QQBot handler own
|
|
114
|
-
* this approval request?" and is consumed by both the capability
|
|
115
|
-
* gate (shouldHandle) and the lazy native runtime adapter.
|
|
116
|
-
*/
|
|
117
|
-
function matchesQQBotApprovalAccount(params) {
|
|
118
|
-
const normalized = {
|
|
119
|
-
cfg: params.cfg,
|
|
120
|
-
accountId: params.accountId,
|
|
121
|
-
request: params.request
|
|
122
|
-
};
|
|
123
|
-
if (resolveQQBotExecApprovalConfig(normalized) !== void 0) return matchesQQBotRequestAccount(normalized);
|
|
124
|
-
return matchesQQBotFallbackRequestAccount(normalized);
|
|
125
|
-
}
|
|
126
|
-
const qqbotExecApprovalProfile = createChannelExecApprovalProfile({
|
|
127
|
-
resolveConfig: resolveQQBotExecApprovalConfig,
|
|
128
|
-
resolveApprovers: getQQBotExecApprovalApprovers,
|
|
129
|
-
matchesRequestAccount: matchesQQBotRequestAccount,
|
|
130
|
-
fallbackAgentIdFromSessionKey: true,
|
|
131
|
-
requireClientEnabledForLocalPromptSuppression: false
|
|
132
|
-
});
|
|
133
|
-
const isQQBotExecApprovalClientEnabled = qqbotExecApprovalProfile.isClientEnabled;
|
|
134
|
-
const isQQBotExecApprovalApprover = qqbotExecApprovalProfile.isApprover;
|
|
135
|
-
const isQQBotExecApprovalAuthorizedSender = qqbotExecApprovalProfile.isAuthorizedSender;
|
|
136
|
-
const shouldHandleQQBotExecApprovalRequest = qqbotExecApprovalProfile.shouldHandleRequest;
|
|
137
|
-
//#endregion
|
|
138
|
-
export { resolveQQBotExecApprovalConfig as a, matchesQQBotApprovalAccount as i, isQQBotExecApprovalAuthorizedSender as n, shouldHandleQQBotExecApprovalRequest as o, isQQBotExecApprovalClientEnabled as r, isQQBotExecApprovalApprover as t };
|
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
//#region extensions/qqbot/src/bridge/narrowing.ts
|
|
2
|
-
/**
|
|
3
|
-
* Map resolved plugin account to the engine gateway account shape (single assertion on nested config).
|
|
4
|
-
*/
|
|
5
|
-
function toGatewayAccount(account) {
|
|
6
|
-
return {
|
|
7
|
-
accountId: account.accountId,
|
|
8
|
-
appId: account.appId,
|
|
9
|
-
clientSecret: account.clientSecret,
|
|
10
|
-
markdownSupport: account.markdownSupport,
|
|
11
|
-
systemPrompt: account.systemPrompt,
|
|
12
|
-
config: account.config
|
|
13
|
-
};
|
|
14
|
-
}
|
|
15
|
-
/**
|
|
16
|
-
* Persist OpenClaw config through the injected plugin runtime (typed entry point).
|
|
17
|
-
*/
|
|
18
|
-
async function writeOpenClawConfigThroughRuntime(runtime, cfg) {
|
|
19
|
-
await runtime.config.replaceConfigFile({
|
|
20
|
-
nextConfig: cfg,
|
|
21
|
-
afterWrite: { mode: "auto" }
|
|
22
|
-
});
|
|
23
|
-
}
|
|
24
|
-
//#endregion
|
|
25
|
-
export { writeOpenClawConfigThroughRuntime as n, toGatewayAccount as t };
|