@searchfe/openclaw-baiduapp 0.1.8 → 0.1.9-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/README.md +19 -30
- package/dist/index.js +625 -459
- package/dist/index.js.map +1 -1
- package/openclaw.plugin.json +1 -7
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
|
+
import { spawnSync } from 'child_process';
|
|
2
|
+
import readline from 'readline';
|
|
1
3
|
import path2 from 'path';
|
|
2
4
|
import { fileURLToPath } from 'url';
|
|
3
|
-
import
|
|
5
|
+
import crypto from 'crypto';
|
|
4
6
|
import { createRequire } from 'module';
|
|
5
7
|
import { lookup } from 'dns/promises';
|
|
6
8
|
import fs2 from 'fs/promises';
|
|
@@ -9,11 +11,120 @@ import fs from 'fs';
|
|
|
9
11
|
import { tmpdir } from 'os';
|
|
10
12
|
|
|
11
13
|
var __defProp = Object.defineProperty;
|
|
14
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
15
|
+
var __esm = (fn, res) => function __init() {
|
|
16
|
+
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
|
|
17
|
+
};
|
|
12
18
|
var __export = (target, all) => {
|
|
13
19
|
for (var name in all)
|
|
14
20
|
__defProp(target, name, { get: all[name], enumerable: true });
|
|
15
21
|
};
|
|
16
22
|
|
|
23
|
+
// src/auth/login.ts
|
|
24
|
+
var login_exports = {};
|
|
25
|
+
__export(login_exports, {
|
|
26
|
+
loginBaiduApp: () => loginBaiduApp
|
|
27
|
+
});
|
|
28
|
+
function createTerminalPrompter() {
|
|
29
|
+
const rl = readline.createInterface({
|
|
30
|
+
input: process.stdin,
|
|
31
|
+
output: process.stdout
|
|
32
|
+
});
|
|
33
|
+
const question = (message) => new Promise((resolve) => {
|
|
34
|
+
rl.question(message, (answer) => resolve(answer));
|
|
35
|
+
});
|
|
36
|
+
return {
|
|
37
|
+
select: async (message, choices) => {
|
|
38
|
+
while (true) {
|
|
39
|
+
process.stdout.write(`${message}
|
|
40
|
+
`);
|
|
41
|
+
for (const [index, choice] of choices.entries()) {
|
|
42
|
+
process.stdout.write(`${index + 1}. ${choice}
|
|
43
|
+
`);
|
|
44
|
+
}
|
|
45
|
+
const answer = (await question("\u8BF7\u9009\u62E9\u5E8F\u53F7: ")).trim();
|
|
46
|
+
const choiceIndex = Number(answer);
|
|
47
|
+
if (Number.isInteger(choiceIndex) && choiceIndex >= 1 && choiceIndex <= choices.length) {
|
|
48
|
+
return choices[choiceIndex - 1];
|
|
49
|
+
}
|
|
50
|
+
process.stdout.write(`\u8BF7\u8F93\u5165 1-${choices.length} \u4E4B\u95F4\u7684\u5E8F\u53F7\u3002
|
|
51
|
+
`);
|
|
52
|
+
}
|
|
53
|
+
},
|
|
54
|
+
text: async (message, opts) => {
|
|
55
|
+
while (true) {
|
|
56
|
+
const answer = (await question(`${message}: `)).trim();
|
|
57
|
+
if (!opts?.required || answer) {
|
|
58
|
+
return answer;
|
|
59
|
+
}
|
|
60
|
+
process.stdout.write("\u8BE5\u9879\u4E3A\u5FC5\u586B\uFF0C\u8BF7\u91CD\u65B0\u8F93\u5165\u3002\n");
|
|
61
|
+
}
|
|
62
|
+
},
|
|
63
|
+
close: () => {
|
|
64
|
+
rl.close();
|
|
65
|
+
}
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
function redactValue(value, opts) {
|
|
69
|
+
if (!value) {
|
|
70
|
+
return "(empty)";
|
|
71
|
+
}
|
|
72
|
+
const keepStart = opts?.keepStart ?? 2;
|
|
73
|
+
const keepEnd = opts?.keepEnd ?? 2;
|
|
74
|
+
if (value.length <= keepStart + keepEnd) {
|
|
75
|
+
return "*".repeat(value.length);
|
|
76
|
+
}
|
|
77
|
+
return `${value.slice(0, keepStart)}***${value.slice(-keepEnd)}`;
|
|
78
|
+
}
|
|
79
|
+
function setConfigValue(key, value) {
|
|
80
|
+
const args = ["config", "set", key, String(value)];
|
|
81
|
+
const result = spawnSync("openclaw", args, {
|
|
82
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
83
|
+
encoding: "utf8",
|
|
84
|
+
timeout: 15e3
|
|
85
|
+
});
|
|
86
|
+
if (result.status !== 0) {
|
|
87
|
+
const stderr = (result.stderr ?? "").trim();
|
|
88
|
+
throw new Error(`openclaw config set failed: ${stderr || `exit code ${result.status}`}`);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
async function loginBaiduApp(params) {
|
|
92
|
+
void params.cfg;
|
|
93
|
+
void params.verbose;
|
|
94
|
+
const agentId = params.accountId?.trim() || "main";
|
|
95
|
+
const prompter = createTerminalPrompter();
|
|
96
|
+
try {
|
|
97
|
+
const mode = await prompter.select("\u8BF7\u9009\u62E9\u767E\u5EA6 App \u914D\u7F6E\u65B9\u5F0F", [
|
|
98
|
+
"\u624B\u52A8\u586B\u5199",
|
|
99
|
+
"\u626B\u7801\u914D\u7F6E\uFF08\u5373\u5C06\u652F\u6301\uFF09"
|
|
100
|
+
]);
|
|
101
|
+
if (mode === "\u626B\u7801\u914D\u7F6E\uFF08\u5373\u5C06\u652F\u6301\uFF09") {
|
|
102
|
+
params.runtime.log("\u23F3 \u626B\u7801\u914D\u7F6E\u529F\u80FD\u6B63\u5728\u5F00\u53D1\u4E2D\uFF0C\u656C\u8BF7\u671F\u5F85...");
|
|
103
|
+
params.runtime.log("\u8BF7\u5148\u4F7F\u7528\u624B\u52A8\u586B\u5199\u65B9\u5F0F\u5B8C\u6210\u914D\u7F6E\u3002");
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
106
|
+
const appKey = await prompter.text("App Key", { required: true });
|
|
107
|
+
const appSecret = await prompter.text("App Secret", { required: true });
|
|
108
|
+
const accountKeyPrefix = `channels.openclaw-baiduapp.accounts.${agentId}`;
|
|
109
|
+
setConfigValue(`${accountKeyPrefix}.appKey`, appKey);
|
|
110
|
+
setConfigValue(`${accountKeyPrefix}.appSecret`, appSecret);
|
|
111
|
+
setConfigValue(`${accountKeyPrefix}.enabled`, true);
|
|
112
|
+
params.runtime.log(`\u2705 openclaw-baiduapp \u5DF2\u5B8C\u6210\u914D\u7F6E\uFF08account: ${agentId}\uFF09`);
|
|
113
|
+
params.runtime.log(`- appKey: ${redactValue(appKey, { keepStart: 3, keepEnd: 3 })}`);
|
|
114
|
+
params.runtime.log(`- appSecret: ${redactValue(appSecret, { keepStart: 3, keepEnd: 3 })}`);
|
|
115
|
+
} catch (error) {
|
|
116
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
117
|
+
params.runtime.error(`openclaw-baiduapp \u767B\u5F55\u914D\u7F6E\u5931\u8D25\uFF1A${message}`);
|
|
118
|
+
throw error;
|
|
119
|
+
} finally {
|
|
120
|
+
prompter.close();
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
var init_login = __esm({
|
|
124
|
+
"src/auth/login.ts"() {
|
|
125
|
+
}
|
|
126
|
+
});
|
|
127
|
+
|
|
17
128
|
// node_modules/.pnpm/zod@3.25.76/node_modules/zod/v3/external.js
|
|
18
129
|
var external_exports = {};
|
|
19
130
|
__export(external_exports, {
|
|
@@ -4056,14 +4167,13 @@ var NEVER = INVALID;
|
|
|
4056
4167
|
|
|
4057
4168
|
// src/config.ts
|
|
4058
4169
|
var DEFAULT_ACCOUNT_ID = "default";
|
|
4170
|
+
var DEFAULT_AGENT_ID = "main";
|
|
4059
4171
|
var DEFAULT_API_BASE = "https://claw.baidu.com";
|
|
4060
4172
|
var BaiduAppAccountSchema = external_exports.object({
|
|
4061
4173
|
name: external_exports.string().optional(),
|
|
4062
4174
|
enabled: external_exports.boolean().optional(),
|
|
4063
4175
|
pollingEnabled: external_exports.boolean().optional(),
|
|
4064
4176
|
webhookPath: external_exports.string().optional(),
|
|
4065
|
-
token: external_exports.string().optional(),
|
|
4066
|
-
encodingAESKey: external_exports.string().optional(),
|
|
4067
4177
|
appKey: external_exports.string().optional(),
|
|
4068
4178
|
appSecret: external_exports.string().optional(),
|
|
4069
4179
|
apiBase: external_exports.string().optional(),
|
|
@@ -4082,8 +4192,6 @@ var BaiduAppConfigJsonSchema = {
|
|
|
4082
4192
|
enabled: { type: "boolean" },
|
|
4083
4193
|
pollingEnabled: { type: "boolean" },
|
|
4084
4194
|
webhookPath: { type: "string" },
|
|
4085
|
-
token: { type: "string" },
|
|
4086
|
-
encodingAESKey: { type: "string" },
|
|
4087
4195
|
appKey: { type: "string" },
|
|
4088
4196
|
appSecret: { type: "string" },
|
|
4089
4197
|
apiBase: { type: "string" },
|
|
@@ -4099,8 +4207,6 @@ var BaiduAppConfigJsonSchema = {
|
|
|
4099
4207
|
enabled: { type: "boolean" },
|
|
4100
4208
|
pollingEnabled: { type: "boolean" },
|
|
4101
4209
|
webhookPath: { type: "string" },
|
|
4102
|
-
token: { type: "string" },
|
|
4103
|
-
encodingAESKey: { type: "string" },
|
|
4104
4210
|
appKey: { type: "string" },
|
|
4105
4211
|
appSecret: { type: "string" },
|
|
4106
4212
|
apiBase: { type: "string" },
|
|
@@ -4115,6 +4221,10 @@ function normalizeAccountId(raw) {
|
|
|
4115
4221
|
const trimmed = String(raw ?? "").trim();
|
|
4116
4222
|
return trimmed || DEFAULT_ACCOUNT_ID;
|
|
4117
4223
|
}
|
|
4224
|
+
function extractAgentId(msg) {
|
|
4225
|
+
const raw = msg.agentid ?? "";
|
|
4226
|
+
return raw.trim() || DEFAULT_AGENT_ID;
|
|
4227
|
+
}
|
|
4118
4228
|
function listConfiguredAccountIds(cfg) {
|
|
4119
4229
|
const accounts = cfg.channels?.["openclaw-baiduapp"]?.accounts;
|
|
4120
4230
|
if (!accounts || typeof accounts !== "object") {
|
|
@@ -4162,12 +4272,10 @@ function resolveBaiduAppAccount(params) {
|
|
|
4162
4272
|
const merged = mergeBaiduAppAccountConfig(params.cfg, accountId);
|
|
4163
4273
|
const enabled = baseEnabled && merged.enabled !== false;
|
|
4164
4274
|
const isDefaultAccount = accountId === DEFAULT_ACCOUNT_ID;
|
|
4165
|
-
const token = merged.token?.trim() || (isDefaultAccount ? process.env.BAIDU_APP_TOKEN?.trim() : void 0) || void 0;
|
|
4166
|
-
const encodingAESKey = merged.encodingAESKey?.trim() || (isDefaultAccount ? process.env.BAIDU_APP_ENCODING_AES_KEY?.trim() : void 0) || void 0;
|
|
4167
4275
|
const appKey = merged.appKey?.trim() || (isDefaultAccount ? process.env.BAIDU_APP_KEY?.trim() : void 0) || void 0;
|
|
4168
4276
|
const appSecret = merged.appSecret?.trim() || (isDefaultAccount ? process.env.BAIDU_APP_SECRET?.trim() : void 0) || void 0;
|
|
4169
|
-
const configured = Boolean(
|
|
4170
|
-
const canSendActive = Boolean(appKey && appSecret
|
|
4277
|
+
const configured = Boolean(appKey && appSecret);
|
|
4278
|
+
const canSendActive = Boolean(appKey && appSecret);
|
|
4171
4279
|
const rawApiBase = merged.apiBase?.trim() || (isDefaultAccount ? process.env.BAIDU_API_BASE?.trim() : void 0) || void 0;
|
|
4172
4280
|
const apiBase = (rawApiBase || DEFAULT_API_BASE).replace(/\/+$/, "");
|
|
4173
4281
|
return {
|
|
@@ -4175,8 +4283,6 @@ function resolveBaiduAppAccount(params) {
|
|
|
4175
4283
|
name: merged.name?.trim() || void 0,
|
|
4176
4284
|
enabled,
|
|
4177
4285
|
configured,
|
|
4178
|
-
token,
|
|
4179
|
-
encodingAESKey,
|
|
4180
4286
|
appKey,
|
|
4181
4287
|
appSecret,
|
|
4182
4288
|
apiBase,
|
|
@@ -4184,6 +4290,37 @@ function resolveBaiduAppAccount(params) {
|
|
|
4184
4290
|
config: merged
|
|
4185
4291
|
};
|
|
4186
4292
|
}
|
|
4293
|
+
function resolveAgentField(agentCfg, mainCfg, baseAccount, field) {
|
|
4294
|
+
return agentCfg?.[field]?.trim() || mainCfg?.[field]?.trim() || baseAccount[field];
|
|
4295
|
+
}
|
|
4296
|
+
function resolveAgentAccount(params) {
|
|
4297
|
+
const { baseAccount, cfg, agentId } = params;
|
|
4298
|
+
if (agentId === baseAccount.accountId) {
|
|
4299
|
+
return baseAccount;
|
|
4300
|
+
}
|
|
4301
|
+
const accounts = cfg.channels?.["openclaw-baiduapp"]?.accounts;
|
|
4302
|
+
const agentCfg = accounts?.[agentId] && typeof accounts[agentId] === "object" ? accounts[agentId] : void 0;
|
|
4303
|
+
const mainCfg = agentId !== DEFAULT_AGENT_ID && accounts?.[DEFAULT_AGENT_ID] && typeof accounts[DEFAULT_AGENT_ID] === "object" ? accounts[DEFAULT_AGENT_ID] : void 0;
|
|
4304
|
+
const appKey = resolveAgentField(agentCfg, mainCfg, baseAccount, "appKey");
|
|
4305
|
+
const appSecret = resolveAgentField(agentCfg, mainCfg, baseAccount, "appSecret");
|
|
4306
|
+
const apiBase = baseAccount.apiBase;
|
|
4307
|
+
const name = resolveAgentField(agentCfg, mainCfg, baseAccount, "name");
|
|
4308
|
+
const configured = Boolean(appKey && appSecret);
|
|
4309
|
+
const canSendActive = Boolean(appKey && appSecret);
|
|
4310
|
+
const agentEnabled = agentCfg?.enabled !== false;
|
|
4311
|
+
return {
|
|
4312
|
+
...baseAccount,
|
|
4313
|
+
accountId: agentId,
|
|
4314
|
+
name: name || baseAccount.name,
|
|
4315
|
+
enabled: agentEnabled && baseAccount.enabled,
|
|
4316
|
+
configured,
|
|
4317
|
+
appKey,
|
|
4318
|
+
appSecret,
|
|
4319
|
+
apiBase,
|
|
4320
|
+
canSendActive,
|
|
4321
|
+
config: { ...baseAccount.config, ...mainCfg ?? {}, ...agentCfg ?? {} }
|
|
4322
|
+
};
|
|
4323
|
+
}
|
|
4187
4324
|
|
|
4188
4325
|
// src/shared/logger.ts
|
|
4189
4326
|
function createLogger(prefix, opts) {
|
|
@@ -4196,135 +4333,85 @@ function createLogger(prefix, opts) {
|
|
|
4196
4333
|
error: (msg) => errorFn(`[${prefix}] [ERROR] ${msg}`)
|
|
4197
4334
|
};
|
|
4198
4335
|
}
|
|
4199
|
-
function decodeEncodingAESKey(encodingAESKey) {
|
|
4200
|
-
const trimmed = encodingAESKey.trim();
|
|
4201
|
-
if (!trimmed) {
|
|
4202
|
-
throw new Error("encodingAESKey missing");
|
|
4203
|
-
}
|
|
4204
|
-
const withPadding = trimmed.endsWith("=") ? trimmed : `${trimmed}=`;
|
|
4205
|
-
const key = Buffer.from(withPadding, "base64");
|
|
4206
|
-
if (key.length !== 32) {
|
|
4207
|
-
throw new Error(`invalid encodingAESKey (expected 32 bytes after base64 decode, got ${key.length})`);
|
|
4208
|
-
}
|
|
4209
|
-
return key;
|
|
4210
|
-
}
|
|
4211
|
-
var PKCS7_BLOCK_SIZE = 32;
|
|
4212
|
-
function pkcs7Pad(buf, blockSize) {
|
|
4213
|
-
const mod = buf.length % blockSize;
|
|
4214
|
-
const pad = mod === 0 ? blockSize : blockSize - mod;
|
|
4215
|
-
return Buffer.concat([buf, Buffer.alloc(pad, pad)]);
|
|
4216
|
-
}
|
|
4217
|
-
function pkcs7Unpad(buf, blockSize) {
|
|
4218
|
-
if (buf.length === 0) {
|
|
4219
|
-
throw new Error("invalid pkcs7 payload");
|
|
4220
|
-
}
|
|
4221
|
-
const pad = buf[buf.length - 1];
|
|
4222
|
-
if (!pad || pad < 1 || pad > blockSize || pad > buf.length) {
|
|
4223
|
-
throw new Error("invalid pkcs7 padding");
|
|
4224
|
-
}
|
|
4225
|
-
for (let i = 1; i <= pad; i += 1) {
|
|
4226
|
-
if (buf[buf.length - i] !== pad) {
|
|
4227
|
-
throw new Error("invalid pkcs7 padding");
|
|
4228
|
-
}
|
|
4229
|
-
}
|
|
4230
|
-
return buf.subarray(0, buf.length - pad);
|
|
4231
|
-
}
|
|
4232
4336
|
function sha1Hex(input) {
|
|
4233
|
-
return
|
|
4337
|
+
return crypto.createHash("sha1").update(input).digest("hex");
|
|
4234
4338
|
}
|
|
4235
|
-
function
|
|
4236
|
-
|
|
4237
|
-
return sha1Hex(parts.join(""));
|
|
4339
|
+
function computeAKSKBearerToken(params) {
|
|
4340
|
+
return sha1Hex(params.ak + params.sk + params.timestamp + params.nonce);
|
|
4238
4341
|
}
|
|
4239
|
-
function
|
|
4240
|
-
const expected =
|
|
4241
|
-
|
|
4342
|
+
function verifyAKSKBearerToken(params) {
|
|
4343
|
+
const expected = computeAKSKBearerToken({
|
|
4344
|
+
ak: params.ak,
|
|
4345
|
+
sk: params.sk,
|
|
4242
4346
|
timestamp: params.timestamp,
|
|
4243
|
-
nonce: params.nonce
|
|
4244
|
-
encrypt: params.encrypt
|
|
4347
|
+
nonce: params.nonce
|
|
4245
4348
|
});
|
|
4246
|
-
return expected === params.
|
|
4349
|
+
return expected === params.token;
|
|
4247
4350
|
}
|
|
4248
|
-
function
|
|
4249
|
-
|
|
4250
|
-
const iv = aesKey.subarray(0, 16);
|
|
4251
|
-
const decipher = crypto3.createDecipheriv("aes-256-cbc", aesKey, iv);
|
|
4252
|
-
decipher.setAutoPadding(false);
|
|
4253
|
-
const decryptedPadded = Buffer.concat([decipher.update(Buffer.from(params.encrypt, "base64")), decipher.final()]);
|
|
4254
|
-
const decrypted = pkcs7Unpad(decryptedPadded, PKCS7_BLOCK_SIZE);
|
|
4255
|
-
if (decrypted.length < 20) {
|
|
4256
|
-
throw new Error(`invalid decrypted payload (expected at least 20 bytes, got ${decrypted.length})`);
|
|
4257
|
-
}
|
|
4258
|
-
const msgLen = decrypted.readUInt32BE(16);
|
|
4259
|
-
const msgStart = 20;
|
|
4260
|
-
const msgEnd = msgStart + msgLen;
|
|
4261
|
-
if (msgEnd > decrypted.length) {
|
|
4262
|
-
throw new Error(`invalid decrypted msg length (msgEnd=${msgEnd}, payloadLength=${decrypted.length})`);
|
|
4263
|
-
}
|
|
4264
|
-
return decrypted.subarray(msgStart, msgEnd).toString("utf8");
|
|
4351
|
+
function generateNonce() {
|
|
4352
|
+
return crypto.randomBytes(8).toString("hex");
|
|
4265
4353
|
}
|
|
4266
|
-
function
|
|
4267
|
-
const
|
|
4268
|
-
|
|
4269
|
-
const random16 = crypto3.randomBytes(16);
|
|
4270
|
-
const msg = Buffer.from(params.plaintext ?? "", "utf8");
|
|
4271
|
-
const msgLen = Buffer.alloc(4);
|
|
4272
|
-
msgLen.writeUInt32BE(msg.length, 0);
|
|
4273
|
-
const raw = Buffer.concat([random16, msgLen, msg]);
|
|
4274
|
-
const padded = pkcs7Pad(raw, PKCS7_BLOCK_SIZE);
|
|
4275
|
-
const cipher = crypto3.createCipheriv("aes-256-cbc", aesKey, iv);
|
|
4276
|
-
cipher.setAutoPadding(false);
|
|
4277
|
-
const encrypted = Buffer.concat([cipher.update(padded), cipher.final()]);
|
|
4278
|
-
return encrypted.toString("base64");
|
|
4354
|
+
function buildAuthorizationHeader(params) {
|
|
4355
|
+
const token = computeAKSKBearerToken(params);
|
|
4356
|
+
return `Bearer ${token}`;
|
|
4279
4357
|
}
|
|
4358
|
+
|
|
4359
|
+
// src/types.ts
|
|
4360
|
+
var DEFAULT_BAIDU_APP_SESSION_ID = "agent:main:main";
|
|
4280
4361
|
var require2 = createRequire(import.meta.url);
|
|
4281
4362
|
var pkg = require2("../package.json");
|
|
4282
4363
|
var PLUGIN_VERSION = pkg.version;
|
|
4283
4364
|
|
|
4284
4365
|
// src/api.ts
|
|
4285
4366
|
var logger = createLogger("openclaw-baiduapp");
|
|
4367
|
+
var DEFAULT_sessionId = DEFAULT_BAIDU_APP_SESSION_ID;
|
|
4286
4368
|
async function sendBaiduAppMessage(account, message, options) {
|
|
4287
4369
|
if (!account.canSendActive) {
|
|
4288
|
-
logger.error("Account not configured for active sending (missing appKey
|
|
4370
|
+
logger.error("Account not configured for active sending (missing appKey or appSecret)");
|
|
4289
4371
|
return {
|
|
4290
4372
|
ok: false,
|
|
4291
4373
|
errcode: -1,
|
|
4292
|
-
errmsg: "Account not configured for active sending (missing appKey
|
|
4374
|
+
errmsg: "Account not configured for active sending (missing appKey or appSecret)"
|
|
4293
4375
|
};
|
|
4294
4376
|
}
|
|
4295
4377
|
const normalizedPayload = typeof message === "string" ? {
|
|
4296
|
-
|
|
4297
|
-
|
|
4378
|
+
list: [{
|
|
4379
|
+
type: "text",
|
|
4380
|
+
data: {
|
|
4381
|
+
text: { content: message }
|
|
4382
|
+
}
|
|
4383
|
+
}]
|
|
4298
4384
|
} : message;
|
|
4299
|
-
const
|
|
4300
|
-
|
|
4385
|
+
const sessionId = options?.sessionId ?? DEFAULT_sessionId;
|
|
4386
|
+
const callbackBody = {
|
|
4387
|
+
sessionId,
|
|
4388
|
+
list: normalizedPayload.list,
|
|
4301
4389
|
version: PLUGIN_VERSION,
|
|
4302
|
-
|
|
4303
|
-
...options?.
|
|
4304
|
-
...options?.chunkKey != null ? { chunkKey: options.chunkKey } : {}
|
|
4390
|
+
isActive: options?.isActive || false,
|
|
4391
|
+
...options?.agentid ? { agentid: options.agentid } : {},
|
|
4392
|
+
...options?.chunkKey != null ? { chunkKey: options.chunkKey } : {},
|
|
4393
|
+
...options?.replyToMsgId ? { replyToMsgId: options.replyToMsgId } : {}
|
|
4305
4394
|
};
|
|
4306
|
-
const plaintext = JSON.stringify(payload);
|
|
4307
|
-
const encrypt = encryptBaiduAppPlaintext({
|
|
4308
|
-
encodingAESKey: account.encodingAESKey ?? "",
|
|
4309
|
-
plaintext
|
|
4310
|
-
});
|
|
4311
4395
|
const timestamp = String(Math.floor(Date.now() / 1e3));
|
|
4312
|
-
const nonce =
|
|
4313
|
-
const
|
|
4314
|
-
|
|
4396
|
+
const nonce = generateNonce();
|
|
4397
|
+
const authorization = buildAuthorizationHeader({
|
|
4398
|
+
ak: account.appKey ?? "",
|
|
4399
|
+
sk: account.appSecret ?? "",
|
|
4315
4400
|
timestamp,
|
|
4316
|
-
nonce
|
|
4317
|
-
encrypt
|
|
4401
|
+
nonce
|
|
4318
4402
|
});
|
|
4319
|
-
const sendMessageUrl = `${account.apiBase}/
|
|
4320
|
-
const url = `${sendMessageUrl}?timestamp=${encodeURIComponent(timestamp)}&ak=${encodeURIComponent(account.appKey ?? "")}&nonce=${encodeURIComponent(nonce)}
|
|
4321
|
-
const body = JSON.stringify(
|
|
4403
|
+
const sendMessageUrl = `${account.apiBase}/channel/msg/callback`;
|
|
4404
|
+
const url = `${sendMessageUrl}?timestamp=${encodeURIComponent(timestamp)}&ak=${encodeURIComponent(account.appKey ?? "")}&nonce=${encodeURIComponent(nonce)}`;
|
|
4405
|
+
const body = JSON.stringify(callbackBody);
|
|
4322
4406
|
logger.info(`POST ${url}`);
|
|
4323
4407
|
logger.debug(`request body: ${body}`);
|
|
4324
4408
|
const resp = await fetch(url, {
|
|
4325
4409
|
method: "POST",
|
|
4326
4410
|
body,
|
|
4327
|
-
headers: {
|
|
4411
|
+
headers: {
|
|
4412
|
+
"Authorization": authorization,
|
|
4413
|
+
"Content-Type": "application/json"
|
|
4414
|
+
}
|
|
4328
4415
|
});
|
|
4329
4416
|
const text = await resp.text();
|
|
4330
4417
|
if (!text) {
|
|
@@ -4340,6 +4427,15 @@ async function sendBaiduAppMessage(account, message, options) {
|
|
|
4340
4427
|
logger.error(`request failed: ${errmsg}`);
|
|
4341
4428
|
return { ok: false, errcode: resp.status, errmsg };
|
|
4342
4429
|
}
|
|
4430
|
+
if (data.code !== 0) {
|
|
4431
|
+
const errmsg = data.msg ?? `channel callback failed with code ${String(data.code)}`;
|
|
4432
|
+
logger.error(`request failed: ${errmsg}`);
|
|
4433
|
+
return {
|
|
4434
|
+
ok: false,
|
|
4435
|
+
errcode: data.code,
|
|
4436
|
+
errmsg
|
|
4437
|
+
};
|
|
4438
|
+
}
|
|
4343
4439
|
const result = {
|
|
4344
4440
|
ok: true
|
|
4345
4441
|
};
|
|
@@ -4365,21 +4461,21 @@ function buildMediaPayload(mediaList, opts) {
|
|
|
4365
4461
|
|
|
4366
4462
|
// src/bot.ts
|
|
4367
4463
|
function extractBaiduAppTextContent(msg) {
|
|
4368
|
-
const msgtype = String(msg.msgtype ??
|
|
4464
|
+
const msgtype = String(msg.msgtype ?? "").toLowerCase();
|
|
4369
4465
|
if (msgtype === "text") {
|
|
4370
|
-
const content = msg.text?.content
|
|
4466
|
+
const content = msg.text?.content;
|
|
4371
4467
|
return typeof content === "string" ? content : "";
|
|
4372
4468
|
}
|
|
4373
4469
|
if (msgtype === "event") {
|
|
4374
4470
|
const eventtype = String(
|
|
4375
|
-
msg.event?.eventtype ??
|
|
4471
|
+
msg.event?.eventtype ?? ""
|
|
4376
4472
|
).trim();
|
|
4377
4473
|
return eventtype ? `[event] ${eventtype}` : "[event]";
|
|
4378
4474
|
}
|
|
4379
4475
|
return msgtype ? `[${msgtype}]` : "";
|
|
4380
4476
|
}
|
|
4381
4477
|
function canDispatchBaiduAppInboundMessage(msg) {
|
|
4382
|
-
const msgtype = String(msg.msgtype ??
|
|
4478
|
+
const msgtype = String(msg.msgtype ?? "").toLowerCase();
|
|
4383
4479
|
if (msgtype === "text") {
|
|
4384
4480
|
if (extractBaiduAppTextContent(msg)) {
|
|
4385
4481
|
return true;
|
|
@@ -4449,7 +4545,7 @@ async function dispatchBaiduAppMessage(params) {
|
|
|
4449
4545
|
cfg: safeCfg,
|
|
4450
4546
|
channel: "openclaw-baiduapp",
|
|
4451
4547
|
accountId: account.accountId,
|
|
4452
|
-
peer: { kind: "dm", id:
|
|
4548
|
+
peer: { kind: "dm", id: account.accountId || DEFAULT_AGENT_ID }
|
|
4453
4549
|
});
|
|
4454
4550
|
logger3.info(`SessionKey: ${route.sessionKey}`);
|
|
4455
4551
|
logger3.info(
|
|
@@ -4479,7 +4575,7 @@ async function dispatchBaiduAppMessage(params) {
|
|
|
4479
4575
|
envelope: envelopeOptions,
|
|
4480
4576
|
body: rawBody
|
|
4481
4577
|
}) : rawBody;
|
|
4482
|
-
const msgid = msg.msgid ??
|
|
4578
|
+
const msgid = msg.msgid ?? void 0;
|
|
4483
4579
|
const ctxPayload = channel.reply?.finalizeInboundContext ? channel.reply.finalizeInboundContext({
|
|
4484
4580
|
Body: body,
|
|
4485
4581
|
RawBody: rawBody,
|
|
@@ -4685,92 +4781,122 @@ var require3 = createRequire(import.meta.url);
|
|
|
4685
4781
|
var BaiduCloudSdk = require3("@baiducloud/sdk");
|
|
4686
4782
|
var DefaultBosClient = BaiduCloudSdk.BosClient;
|
|
4687
4783
|
var MAX_DOWNLOAD_REDIRECTS = 5;
|
|
4688
|
-
|
|
4689
|
-
|
|
4690
|
-
|
|
4691
|
-
|
|
4692
|
-
|
|
4693
|
-
|
|
4694
|
-
|
|
4695
|
-
|
|
4696
|
-
|
|
4697
|
-
|
|
4698
|
-
|
|
4699
|
-
|
|
4700
|
-
|
|
4701
|
-
|
|
4702
|
-
const
|
|
4703
|
-
const
|
|
4704
|
-
if (!
|
|
4705
|
-
throw new Error(
|
|
4706
|
-
}
|
|
4707
|
-
let payload;
|
|
4708
|
-
try {
|
|
4709
|
-
payload = await response.json();
|
|
4710
|
-
} catch (error) {
|
|
4711
|
-
throw new Error(`SKS response is not valid JSON: ${formatError(error)}`, { cause: error });
|
|
4712
|
-
}
|
|
4713
|
-
return parseSksCredentialsFromPayload(payload);
|
|
4714
|
-
}
|
|
4715
|
-
function parseSksCredentialsFromPayload(payload) {
|
|
4716
|
-
const parsed = parseSksResponse(payload);
|
|
4717
|
-
if (parsed.status !== 0) {
|
|
4718
|
-
throw new Error(`SKS request failed with status ${parsed.status}`);
|
|
4784
|
+
async function fetchFileUploadSts(options, deps = {}) {
|
|
4785
|
+
assertAccountCanUseChannelApi(options.account);
|
|
4786
|
+
const responsePayload = await postAuthenticatedChannelJson(
|
|
4787
|
+
options.account,
|
|
4788
|
+
"/channel/file/sts",
|
|
4789
|
+
{
|
|
4790
|
+
sessionId: options.sessionId ?? DEFAULT_BAIDU_APP_SESSION_ID,
|
|
4791
|
+
filename: requireSafeDisplayFileName(options.filename),
|
|
4792
|
+
fileSize: options.fileSize,
|
|
4793
|
+
fileType: normalizeOutboundFileType(options.fileType),
|
|
4794
|
+
...options.md5 ? { md5: options.md5 } : {}
|
|
4795
|
+
},
|
|
4796
|
+
deps
|
|
4797
|
+
);
|
|
4798
|
+
const envelope = parseChannelEnvelope(responsePayload, "file STS");
|
|
4799
|
+
const data = envelope.data;
|
|
4800
|
+
if (!data) {
|
|
4801
|
+
throw new Error("file STS response missing data");
|
|
4719
4802
|
}
|
|
4720
|
-
|
|
4721
|
-
|
|
4803
|
+
const fileId = requireNonEmptyString(data.fileId, "file STS data.fileId");
|
|
4804
|
+
const reuse = requireBoolean(data.reuse, "file STS data.reuse");
|
|
4805
|
+
if (reuse) {
|
|
4806
|
+
return { fileId, reuse };
|
|
4722
4807
|
}
|
|
4723
4808
|
return {
|
|
4724
|
-
|
|
4725
|
-
|
|
4726
|
-
|
|
4727
|
-
|
|
4728
|
-
|
|
4729
|
-
|
|
4809
|
+
fileId,
|
|
4810
|
+
reuse,
|
|
4811
|
+
sts: parseFileUploadStsCredentials(data.sts),
|
|
4812
|
+
bceUrl: requireNonEmptyString(data.bceUrl, "file STS data.bceUrl"),
|
|
4813
|
+
bucket: requireNonEmptyString(data.bucket, "file STS data.bucket"),
|
|
4814
|
+
bosName: requireNonEmptyString(data.bosName, "file STS data.bosName")
|
|
4730
4815
|
};
|
|
4731
4816
|
}
|
|
4732
|
-
function
|
|
4733
|
-
|
|
4734
|
-
|
|
4735
|
-
|
|
4736
|
-
|
|
4737
|
-
|
|
4817
|
+
async function notifyFileUploadComplete(options, deps = {}) {
|
|
4818
|
+
assertAccountCanUseChannelApi(options.account);
|
|
4819
|
+
if (options.fileIds.length === 0) {
|
|
4820
|
+
throw new Error("file complete requires at least one fileId");
|
|
4821
|
+
}
|
|
4822
|
+
const responsePayload = await postAuthenticatedChannelJson(
|
|
4823
|
+
options.account,
|
|
4824
|
+
"/channel/file/complete",
|
|
4825
|
+
{
|
|
4826
|
+
sessionId: options.sessionId ?? DEFAULT_BAIDU_APP_SESSION_ID,
|
|
4827
|
+
fileIds: options.fileIds
|
|
4828
|
+
},
|
|
4829
|
+
deps
|
|
4830
|
+
);
|
|
4831
|
+
const envelope = parseChannelEnvelope(responsePayload, "file complete");
|
|
4832
|
+
const files = extractCompleteFiles(envelope.data);
|
|
4833
|
+
const fileIds = new Set(options.fileIds);
|
|
4834
|
+
for (const file of files) {
|
|
4835
|
+
if (!fileIds.has(file.fileId)) {
|
|
4836
|
+
continue;
|
|
4837
|
+
}
|
|
4838
|
+
if (file.status === -1) {
|
|
4839
|
+
throw new Error(`file complete failed for fileId ${file.fileId}`);
|
|
4840
|
+
}
|
|
4841
|
+
}
|
|
4842
|
+
return { files };
|
|
4738
4843
|
}
|
|
4739
|
-
function createBosClient(
|
|
4844
|
+
function createBosClient(fileSts, deps = {}) {
|
|
4845
|
+
if (!fileSts.sts || !fileSts.bceUrl) {
|
|
4846
|
+
throw new Error("Cannot create BOS client without STS credentials");
|
|
4847
|
+
}
|
|
4740
4848
|
const BosClientCtor = deps.bosClientCtor ?? DefaultBosClient;
|
|
4741
4849
|
return new BosClientCtor({
|
|
4742
|
-
endpoint:
|
|
4743
|
-
sessionToken:
|
|
4850
|
+
endpoint: fileSts.bceUrl,
|
|
4851
|
+
sessionToken: fileSts.sts.token,
|
|
4744
4852
|
credentials: {
|
|
4745
|
-
ak:
|
|
4746
|
-
sk:
|
|
4853
|
+
ak: fileSts.sts.ak,
|
|
4854
|
+
sk: fileSts.sts.sk
|
|
4747
4855
|
}
|
|
4748
4856
|
});
|
|
4749
4857
|
}
|
|
4750
4858
|
async function uploadLocalFileToBos(options, deps = {}) {
|
|
4751
4859
|
const localFile = await assertUploadableLocalFile(options.filePath);
|
|
4752
|
-
const credentials = await fetchSksCredentials(options.account, deps);
|
|
4753
|
-
const client = createBosClient(credentials, deps);
|
|
4754
4860
|
const sourceName = options.fileName ?? path2.basename(localFile);
|
|
4755
|
-
const
|
|
4756
|
-
const
|
|
4757
|
-
|
|
4758
|
-
|
|
4759
|
-
|
|
4760
|
-
|
|
4761
|
-
|
|
4762
|
-
|
|
4763
|
-
|
|
4764
|
-
|
|
4765
|
-
|
|
4766
|
-
|
|
4767
|
-
|
|
4861
|
+
const fileName = requireSafeDisplayFileName(sourceName);
|
|
4862
|
+
const [fileBuffer, fileStat] = await Promise.all([fs2.readFile(localFile), fs2.stat(localFile)]);
|
|
4863
|
+
const md5 = crypto.createHash("md5").update(fileBuffer).digest("hex");
|
|
4864
|
+
const fileType = inferOutboundFileType(fileName);
|
|
4865
|
+
const fileSts = await fetchFileUploadSts(
|
|
4866
|
+
{
|
|
4867
|
+
account: options.account,
|
|
4868
|
+
sessionId: options.sessionId,
|
|
4869
|
+
filename: fileName,
|
|
4870
|
+
fileSize: fileStat.size,
|
|
4871
|
+
fileType,
|
|
4872
|
+
md5
|
|
4873
|
+
},
|
|
4874
|
+
deps
|
|
4875
|
+
);
|
|
4876
|
+
if (!fileSts.reuse) {
|
|
4877
|
+
const bucketName = requireNonEmptyString(fileSts.bucket, "file STS bucket");
|
|
4878
|
+
const key = requireNonEmptyString(fileSts.bosName, "file STS bosName");
|
|
4879
|
+
const client = createBosClient(fileSts, deps);
|
|
4880
|
+
const uploadOptions = options.contentType?.trim() ? {
|
|
4881
|
+
"Content-Type": options.contentType.trim()
|
|
4882
|
+
} : void 0;
|
|
4883
|
+
await client.putObjectFromFile(bucketName, key, localFile, uploadOptions);
|
|
4884
|
+
}
|
|
4885
|
+
await notifyFileUploadComplete(
|
|
4886
|
+
{
|
|
4887
|
+
account: options.account,
|
|
4888
|
+
sessionId: options.sessionId,
|
|
4889
|
+
fileIds: [fileSts.fileId]
|
|
4890
|
+
},
|
|
4891
|
+
deps
|
|
4892
|
+
);
|
|
4768
4893
|
return {
|
|
4769
|
-
|
|
4770
|
-
|
|
4771
|
-
|
|
4772
|
-
fileName
|
|
4773
|
-
fileSize: fileStat.size
|
|
4894
|
+
fileId: fileSts.fileId,
|
|
4895
|
+
bucketName: fileSts.bucket,
|
|
4896
|
+
key: fileSts.bosName,
|
|
4897
|
+
fileName,
|
|
4898
|
+
fileSize: fileStat.size,
|
|
4899
|
+
reuse: fileSts.reuse
|
|
4774
4900
|
};
|
|
4775
4901
|
}
|
|
4776
4902
|
async function downloadInboundFileToTemp(options, deps = {}) {
|
|
@@ -4868,38 +4994,93 @@ async function pruneExpiredTempFiles(deps = {}) {
|
|
|
4868
4994
|
await walk(tempDir);
|
|
4869
4995
|
return { deletedFiles };
|
|
4870
4996
|
}
|
|
4871
|
-
function
|
|
4997
|
+
async function postAuthenticatedChannelJson(account, pathname, bodyPayload, deps) {
|
|
4998
|
+
const fetchImpl = deps.fetchImpl ?? fetch;
|
|
4999
|
+
const now = deps.now?.() ?? /* @__PURE__ */ new Date();
|
|
5000
|
+
const timestamp = String(Math.floor(now.getTime() / 1e3));
|
|
5001
|
+
const nonce = (deps.generateNonceImpl ?? generateNonce)();
|
|
5002
|
+
const authorization = buildAuthorizationHeader({
|
|
5003
|
+
ak: account.appKey ?? "",
|
|
5004
|
+
sk: account.appSecret ?? "",
|
|
5005
|
+
timestamp,
|
|
5006
|
+
nonce
|
|
5007
|
+
});
|
|
5008
|
+
const url = new URL(`${account.apiBase.replace(/\/+$/, "")}${pathname}`);
|
|
5009
|
+
url.searchParams.set("ak", account.appKey ?? "");
|
|
5010
|
+
url.searchParams.set("timestamp", timestamp);
|
|
5011
|
+
url.searchParams.set("nonce", nonce);
|
|
5012
|
+
const response = await fetchImpl(url.toString(), {
|
|
5013
|
+
method: "POST",
|
|
5014
|
+
headers: {
|
|
5015
|
+
"Authorization": authorization,
|
|
5016
|
+
"Content-Type": "application/json"
|
|
5017
|
+
},
|
|
5018
|
+
body: JSON.stringify(bodyPayload)
|
|
5019
|
+
});
|
|
5020
|
+
if (!response.ok) {
|
|
5021
|
+
throw new Error(`channel request failed with HTTP ${response.status}`);
|
|
5022
|
+
}
|
|
5023
|
+
try {
|
|
5024
|
+
return await response.json();
|
|
5025
|
+
} catch (error) {
|
|
5026
|
+
throw new Error(`channel response is not valid JSON: ${formatError(error)}`, { cause: error });
|
|
5027
|
+
}
|
|
5028
|
+
}
|
|
5029
|
+
function parseChannelEnvelope(payload, label) {
|
|
4872
5030
|
if (!payload || typeof payload !== "object") {
|
|
4873
|
-
throw new Error(
|
|
5031
|
+
throw new Error(`${label} response must be an object`);
|
|
4874
5032
|
}
|
|
4875
5033
|
const record = payload;
|
|
4876
|
-
const
|
|
5034
|
+
const code = record.code;
|
|
5035
|
+
if (typeof code !== "number") {
|
|
5036
|
+
throw new Error(`${label} response missing numeric code`);
|
|
5037
|
+
}
|
|
5038
|
+
if (code !== 0) {
|
|
5039
|
+
const msg = typeof record.msg === "string" ? record.msg : "unknown error";
|
|
5040
|
+
throw new Error(`${label} request failed with code ${code}: ${msg}`);
|
|
5041
|
+
}
|
|
4877
5042
|
const data = record.data;
|
|
4878
5043
|
return {
|
|
4879
|
-
|
|
4880
|
-
|
|
4881
|
-
|
|
4882
|
-
sk: data.sk,
|
|
4883
|
-
token: data.token,
|
|
4884
|
-
bucketName: data.bucketName,
|
|
4885
|
-
preFixPath: data.preFixPath,
|
|
4886
|
-
bceUrl: data.bceUrl
|
|
4887
|
-
} : void 0
|
|
5044
|
+
code,
|
|
5045
|
+
msg: typeof record.msg === "string" ? record.msg : void 0,
|
|
5046
|
+
data: data && typeof data === "object" ? data : void 0
|
|
4888
5047
|
};
|
|
4889
5048
|
}
|
|
4890
|
-
function
|
|
4891
|
-
|
|
5049
|
+
function parseFileUploadStsCredentials(payload) {
|
|
5050
|
+
if (!payload || typeof payload !== "object") {
|
|
5051
|
+
throw new Error("file STS data.sts is required");
|
|
5052
|
+
}
|
|
5053
|
+
const record = payload;
|
|
5054
|
+
return {
|
|
5055
|
+
ak: requireNonEmptyString(record.ak, "file STS data.sts.ak"),
|
|
5056
|
+
sk: requireNonEmptyString(record.sk, "file STS data.sts.sk"),
|
|
5057
|
+
token: requireNonEmptyString(record.token, "file STS data.sts.token"),
|
|
5058
|
+
expireAt: requireNumber(record.expireAt, "file STS data.sts.expireAt")
|
|
5059
|
+
};
|
|
4892
5060
|
}
|
|
4893
|
-
function
|
|
4894
|
-
const
|
|
4895
|
-
|
|
4896
|
-
|
|
4897
|
-
|
|
4898
|
-
|
|
4899
|
-
|
|
4900
|
-
|
|
5061
|
+
function extractCompleteFiles(payload) {
|
|
5062
|
+
const files = payload?.files;
|
|
5063
|
+
if (!Array.isArray(files)) {
|
|
5064
|
+
return [];
|
|
5065
|
+
}
|
|
5066
|
+
return files.map((file, index) => {
|
|
5067
|
+
if (!file || typeof file !== "object") {
|
|
5068
|
+
throw new Error(`file complete data.files[${index}] must be an object`);
|
|
5069
|
+
}
|
|
5070
|
+
const record = file;
|
|
5071
|
+
return {
|
|
5072
|
+
fileId: requireNonEmptyString(record.fileId, `file complete data.files[${index}].fileId`),
|
|
5073
|
+
status: requireNumber(record.status, `file complete data.files[${index}].status`)
|
|
5074
|
+
};
|
|
4901
5075
|
});
|
|
4902
|
-
|
|
5076
|
+
}
|
|
5077
|
+
function assertAccountCanUseChannelApi(account) {
|
|
5078
|
+
if (!account.appKey?.trim()) {
|
|
5079
|
+
throw new Error("Cannot call channel API without account.appKey");
|
|
5080
|
+
}
|
|
5081
|
+
if (!account.appSecret?.trim()) {
|
|
5082
|
+
throw new Error("Cannot call channel API without account.appSecret");
|
|
5083
|
+
}
|
|
4903
5084
|
}
|
|
4904
5085
|
function requireNonEmptyString(value, label) {
|
|
4905
5086
|
if (typeof value !== "string" || !value.trim()) {
|
|
@@ -4907,36 +5088,19 @@ function requireNonEmptyString(value, label) {
|
|
|
4907
5088
|
}
|
|
4908
5089
|
return value.trim();
|
|
4909
5090
|
}
|
|
4910
|
-
function
|
|
4911
|
-
|
|
4912
|
-
|
|
4913
|
-
if (segments.length === 0) {
|
|
4914
|
-
throw new Error("SKS data.preFixPath must contain at least one safe path segment");
|
|
5091
|
+
function requireBoolean(value, label) {
|
|
5092
|
+
if (typeof value !== "boolean") {
|
|
5093
|
+
throw new Error(`${label} must be a boolean`);
|
|
4915
5094
|
}
|
|
4916
|
-
|
|
4917
|
-
if (segment === "." || segment === "..") {
|
|
4918
|
-
throw new Error("SKS data.preFixPath contains path traversal");
|
|
4919
|
-
}
|
|
4920
|
-
return sanitizeObjectKeySegment(segment);
|
|
4921
|
-
});
|
|
4922
|
-
return safeSegments.join("/");
|
|
5095
|
+
return value;
|
|
4923
5096
|
}
|
|
4924
|
-
function
|
|
4925
|
-
|
|
4926
|
-
|
|
4927
|
-
throw new Error(`Object key segment is not safe: ${segment}`);
|
|
5097
|
+
function requireNumber(value, label) {
|
|
5098
|
+
if (typeof value !== "number" || !Number.isFinite(value)) {
|
|
5099
|
+
throw new Error(`${label} must be a finite number`);
|
|
4928
5100
|
}
|
|
4929
|
-
return
|
|
4930
|
-
}
|
|
4931
|
-
function sanitizeFileName(fileName) {
|
|
4932
|
-
assertSafeUntrustedFileName(fileName);
|
|
4933
|
-
const parsed = path2.parse(fileName.trim());
|
|
4934
|
-
const safeBase = parsed.name.replace(/[^a-zA-Z0-9._-]+/g, "-").replace(/-+/g, "-").replace(/^-|-$/g, "");
|
|
4935
|
-
const safeExt = parsed.ext.replace(/[^a-zA-Z0-9.]+/g, "").slice(0, 16);
|
|
4936
|
-
const baseName = safeBase || "file";
|
|
4937
|
-
return `${baseName.slice(0, 80)}${safeExt}`;
|
|
5101
|
+
return value;
|
|
4938
5102
|
}
|
|
4939
|
-
function
|
|
5103
|
+
function requireSafeDisplayFileName(fileName) {
|
|
4940
5104
|
const trimmed = fileName.trim();
|
|
4941
5105
|
if (!trimmed) {
|
|
4942
5106
|
throw new Error("Filename cannot be empty");
|
|
@@ -4947,14 +5111,18 @@ function assertSafeUntrustedFileName(fileName) {
|
|
|
4947
5111
|
if (trimmed === "." || trimmed === "..") {
|
|
4948
5112
|
throw new Error(`Filename is not safe: ${fileName}`);
|
|
4949
5113
|
}
|
|
5114
|
+
return trimmed;
|
|
4950
5115
|
}
|
|
4951
|
-
function
|
|
4952
|
-
const
|
|
4953
|
-
|
|
4954
|
-
|
|
5116
|
+
function normalizeOutboundFileType(fileType) {
|
|
5117
|
+
const normalized = fileType.trim().replace(/^\.+/, "").toLowerCase();
|
|
5118
|
+
if (!normalized) {
|
|
5119
|
+
throw new Error("fileType is required");
|
|
5120
|
+
}
|
|
5121
|
+
return normalized;
|
|
4955
5122
|
}
|
|
4956
|
-
function
|
|
4957
|
-
|
|
5123
|
+
function inferOutboundFileType(fileName) {
|
|
5124
|
+
const extension = path2.extname(fileName).replace(/^\./, "").toLowerCase();
|
|
5125
|
+
return extension || "bin";
|
|
4958
5126
|
}
|
|
4959
5127
|
async function assertUploadableLocalFile(filePath) {
|
|
4960
5128
|
const resolvedPath = path2.resolve(filePath);
|
|
@@ -5079,11 +5247,11 @@ function parseContentDispositionFileName(contentDisposition) {
|
|
|
5079
5247
|
}
|
|
5080
5248
|
function resolveDownloadFileName(params) {
|
|
5081
5249
|
const candidate = params.explicitFileName ?? params.headerFileName ?? params.urlFileName ?? "file.bin";
|
|
5082
|
-
return
|
|
5250
|
+
return requireSafeDisplayFileName(candidate);
|
|
5083
5251
|
}
|
|
5084
5252
|
function buildTempFilePath(params) {
|
|
5085
5253
|
const timestamp = (params.now?.() ?? /* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
5086
|
-
return path2.join(params.tempDir, `${timestamp}-${
|
|
5254
|
+
return path2.join(params.tempDir, `${timestamp}-${crypto.randomUUID()}-${params.fileName}`);
|
|
5087
5255
|
}
|
|
5088
5256
|
async function ensurePluginTempDir(resolveTmpDir) {
|
|
5089
5257
|
const baseTmpDir = resolveTmpDir?.() ?? resolvePreferredOpenClawTmpDir();
|
|
@@ -5144,12 +5312,12 @@ function normalizeWebhookPath(raw) {
|
|
|
5144
5312
|
function jsonOk(res, body) {
|
|
5145
5313
|
res.statusCode = 200;
|
|
5146
5314
|
res.setHeader("Content-Type", "application/json; charset=utf-8");
|
|
5147
|
-
res.end(JSON.stringify({
|
|
5315
|
+
res.end(JSON.stringify({ code: 0, msg: "success", data: body }));
|
|
5148
5316
|
}
|
|
5149
5317
|
function jsonError(res, message, statusCode = 200) {
|
|
5150
5318
|
res.statusCode = statusCode;
|
|
5151
5319
|
res.setHeader("Content-Type", "application/json; charset=utf-8");
|
|
5152
|
-
res.end(JSON.stringify({
|
|
5320
|
+
res.end(JSON.stringify({ code: -1, msg: message, data: {} }));
|
|
5153
5321
|
}
|
|
5154
5322
|
async function readRawBody(req, maxBytes) {
|
|
5155
5323
|
const chunks = [];
|
|
@@ -5181,47 +5349,6 @@ async function readRawBody(req, maxBytes) {
|
|
|
5181
5349
|
});
|
|
5182
5350
|
});
|
|
5183
5351
|
}
|
|
5184
|
-
function parseXmlBody(xml) {
|
|
5185
|
-
const result = {};
|
|
5186
|
-
const cdataRegex = /<(\w+)><!\[CDATA\[([\s\S]*?)\]\]><\/\1>/g;
|
|
5187
|
-
let match;
|
|
5188
|
-
while ((match = cdataRegex.exec(xml)) !== null) {
|
|
5189
|
-
const [, key = "", value = ""] = match;
|
|
5190
|
-
result[key] = value;
|
|
5191
|
-
}
|
|
5192
|
-
const simpleRegex = /<(\w+)>([^<]*)<\/\1>/g;
|
|
5193
|
-
while ((match = simpleRegex.exec(xml)) !== null) {
|
|
5194
|
-
const [, key = "", value = ""] = match;
|
|
5195
|
-
if (!result[key]) {
|
|
5196
|
-
result[key] = value;
|
|
5197
|
-
}
|
|
5198
|
-
}
|
|
5199
|
-
return result;
|
|
5200
|
-
}
|
|
5201
|
-
function isXmlFormat(raw) {
|
|
5202
|
-
const trimmed = raw.trim();
|
|
5203
|
-
return trimmed.startsWith("<") && trimmed.endsWith(">");
|
|
5204
|
-
}
|
|
5205
|
-
function buildEncryptedJsonReply(params) {
|
|
5206
|
-
const base = params.plaintextJson != null && typeof params.plaintextJson === "object" ? params.plaintextJson : {};
|
|
5207
|
-
const plaintext = JSON.stringify({ ...base, version: PLUGIN_VERSION });
|
|
5208
|
-
const encrypt = encryptBaiduAppPlaintext({
|
|
5209
|
-
encodingAESKey: params.account.encodingAESKey ?? "",
|
|
5210
|
-
plaintext
|
|
5211
|
-
});
|
|
5212
|
-
const msgsignature = computeBaiduAppMsgSignature({
|
|
5213
|
-
token: params.account.token ?? "",
|
|
5214
|
-
timestamp: params.timestamp,
|
|
5215
|
-
nonce: params.nonce,
|
|
5216
|
-
encrypt
|
|
5217
|
-
});
|
|
5218
|
-
return {
|
|
5219
|
-
encrypt,
|
|
5220
|
-
msgsignature,
|
|
5221
|
-
timestamp: params.timestamp,
|
|
5222
|
-
nonce: params.nonce
|
|
5223
|
-
};
|
|
5224
|
-
}
|
|
5225
5352
|
function resolveQueryParams(req) {
|
|
5226
5353
|
const url = new URL(req.url ?? "/", "http://localhost");
|
|
5227
5354
|
return url.searchParams;
|
|
@@ -5230,8 +5357,16 @@ function resolvePath(req) {
|
|
|
5230
5357
|
const url = new URL(req.url ?? "/", "http://localhost");
|
|
5231
5358
|
return normalizeWebhookPath(url.pathname || "/");
|
|
5232
5359
|
}
|
|
5233
|
-
function
|
|
5234
|
-
|
|
5360
|
+
function resolveBearerToken(req) {
|
|
5361
|
+
const authorization = req.headers.authorization ?? req.headers.Authorization;
|
|
5362
|
+
if (Array.isArray(authorization)) {
|
|
5363
|
+
return "";
|
|
5364
|
+
}
|
|
5365
|
+
const value = typeof authorization === "string" ? authorization.trim() : "";
|
|
5366
|
+
if (!value.toLowerCase().startsWith("bearer ")) {
|
|
5367
|
+
return "";
|
|
5368
|
+
}
|
|
5369
|
+
return value.slice(7).trim();
|
|
5235
5370
|
}
|
|
5236
5371
|
function buildLogger(target) {
|
|
5237
5372
|
return createLogger("openclaw-baiduapp", {
|
|
@@ -5299,50 +5434,49 @@ async function downloadInboundFiles(params) {
|
|
|
5299
5434
|
}
|
|
5300
5435
|
};
|
|
5301
5436
|
}
|
|
5302
|
-
function
|
|
5303
|
-
const
|
|
5304
|
-
|
|
5305
|
-
|
|
5306
|
-
|
|
5307
|
-
|
|
5308
|
-
|
|
5309
|
-
|
|
5310
|
-
|
|
5311
|
-
|
|
5312
|
-
|
|
5313
|
-
|
|
5314
|
-
|
|
5315
|
-
|
|
5316
|
-
|
|
5317
|
-
|
|
5318
|
-
|
|
5319
|
-
|
|
5437
|
+
function parseV2MsgToInbound(inner, outer) {
|
|
5438
|
+
const list = Array.isArray(inner.list) ? inner.list : [];
|
|
5439
|
+
const textItem = list.find(
|
|
5440
|
+
(item) => item && typeof item === "object" && item.type === "text"
|
|
5441
|
+
);
|
|
5442
|
+
const fileItems = list.filter(
|
|
5443
|
+
(item) => item && typeof item === "object" && item.type === "file"
|
|
5444
|
+
);
|
|
5445
|
+
const textContent = textItem?.data?.text?.content ?? "";
|
|
5446
|
+
const files = fileItems.map((f) => ({ url: f.data.file_id, fileType: "file" }));
|
|
5447
|
+
return {
|
|
5448
|
+
msgtype: "text",
|
|
5449
|
+
msgid: typeof outer.msgid === "string" ? outer.msgid : void 0,
|
|
5450
|
+
agentid: typeof outer.agentid === "string" ? outer.agentid : void 0,
|
|
5451
|
+
text: textContent ? { content: textContent } : void 0,
|
|
5452
|
+
...files.length > 0 ? { files } : {}
|
|
5453
|
+
};
|
|
5454
|
+
}
|
|
5455
|
+
function parseBaiduAppCandidates(params) {
|
|
5456
|
+
const results = [];
|
|
5457
|
+
let outer = {};
|
|
5320
5458
|
try {
|
|
5321
|
-
const parsed = JSON.parse(
|
|
5322
|
-
if (
|
|
5323
|
-
|
|
5459
|
+
const parsed = JSON.parse(params.raw);
|
|
5460
|
+
if (parsed && typeof parsed === "object") {
|
|
5461
|
+
outer = parsed;
|
|
5324
5462
|
}
|
|
5325
|
-
return parsed;
|
|
5326
5463
|
} catch {
|
|
5327
|
-
|
|
5464
|
+
outer = {};
|
|
5328
5465
|
}
|
|
5329
|
-
}
|
|
5330
|
-
|
|
5331
|
-
const results = [];
|
|
5332
|
-
for (const candidate of params.candidates) {
|
|
5333
|
-
if (!candidate.account.encodingAESKey) {
|
|
5334
|
-
continue;
|
|
5335
|
-
}
|
|
5466
|
+
let inner = {};
|
|
5467
|
+
if (typeof outer.msg === "string") {
|
|
5336
5468
|
try {
|
|
5337
|
-
const
|
|
5338
|
-
|
|
5339
|
-
|
|
5340
|
-
}
|
|
5341
|
-
const msg = parseBaiduAppPlainMessage(plaintext);
|
|
5342
|
-
results.push({ target: candidate, plaintext, msg });
|
|
5469
|
+
const parsedInner = JSON.parse(outer.msg);
|
|
5470
|
+
if (parsedInner && typeof parsedInner === "object") {
|
|
5471
|
+
inner = parsedInner;
|
|
5472
|
+
}
|
|
5343
5473
|
} catch {
|
|
5474
|
+
inner = {};
|
|
5344
5475
|
}
|
|
5345
5476
|
}
|
|
5477
|
+
for (const candidate of params.candidates) {
|
|
5478
|
+
results.push({ target: candidate, msg: parseV2MsgToInbound(inner, outer) });
|
|
5479
|
+
}
|
|
5346
5480
|
return results;
|
|
5347
5481
|
}
|
|
5348
5482
|
function selectDecryptedTarget(params) {
|
|
@@ -5359,8 +5493,9 @@ async function processBaiduAppInboundMessage(params) {
|
|
|
5359
5493
|
const { target, msg } = params;
|
|
5360
5494
|
const logger3 = buildLogger(target);
|
|
5361
5495
|
target.statusSink?.({ lastInboundAt: Date.now() });
|
|
5362
|
-
const msgtype = String(msg.msgtype ??
|
|
5363
|
-
const msgid = msg.msgid
|
|
5496
|
+
const msgtype = String(msg.msgtype ?? "").toLowerCase();
|
|
5497
|
+
const msgid = msg.msgid ? String(msg.msgid) : void 0;
|
|
5498
|
+
const agentId = extractAgentId(msg);
|
|
5364
5499
|
logger3.info(`inbound: type=${msgtype || "unknown"} msgid=${msgid ?? "none"} account=${target.account.accountId}`);
|
|
5365
5500
|
if (!canDispatchBaiduAppInboundMessage(msg)) {
|
|
5366
5501
|
logger3.warn(`inbound message skipped: type=${msgtype || "unknown"} reason=no-dispatchable-content`);
|
|
@@ -5368,6 +5503,11 @@ async function processBaiduAppInboundMessage(params) {
|
|
|
5368
5503
|
}
|
|
5369
5504
|
const inboundFileResult = msgtype === "text" ? await downloadInboundFiles({ msg, logger: logger3, runtime: target.runtime }) : { localFiles: [], diagnostic: {} };
|
|
5370
5505
|
const inboundMediaFiles = inboundFileResult.localFiles;
|
|
5506
|
+
const agentAccount = resolveAgentAccount({
|
|
5507
|
+
baseAccount: target.account,
|
|
5508
|
+
cfg: target.config,
|
|
5509
|
+
agentId
|
|
5510
|
+
});
|
|
5371
5511
|
const core = tryGetBaiduAppRuntime();
|
|
5372
5512
|
if (core) {
|
|
5373
5513
|
logger3.info(`agent dispatch started: canSendActive=${target.account.canSendActive}`);
|
|
@@ -5380,8 +5520,9 @@ async function processBaiduAppInboundMessage(params) {
|
|
|
5380
5520
|
);
|
|
5381
5521
|
target.statusSink?.({ lastOutboundAt: Date.now() });
|
|
5382
5522
|
if (target.account.canSendActive) {
|
|
5383
|
-
sendBaiduAppMessage(
|
|
5384
|
-
msgid,
|
|
5523
|
+
sendBaiduAppMessage(agentAccount, text, {
|
|
5524
|
+
replyToMsgId: msgid,
|
|
5525
|
+
agentid: agentId,
|
|
5385
5526
|
chunkKey: currentChunkKey
|
|
5386
5527
|
}).then((result) => {
|
|
5387
5528
|
if (!result.ok) {
|
|
@@ -5404,7 +5545,7 @@ async function processBaiduAppInboundMessage(params) {
|
|
|
5404
5545
|
};
|
|
5405
5546
|
dispatchBaiduAppMessage({
|
|
5406
5547
|
cfg: target.config,
|
|
5407
|
-
account:
|
|
5548
|
+
account: agentAccount,
|
|
5408
5549
|
msg,
|
|
5409
5550
|
core,
|
|
5410
5551
|
hooks,
|
|
@@ -5446,53 +5587,43 @@ async function handleBaiduAppWebhookRequest(req, res) {
|
|
|
5446
5587
|
const query = resolveQueryParams(req);
|
|
5447
5588
|
const timestamp = query.get("timestamp") ?? "";
|
|
5448
5589
|
const nonce = query.get("nonce") ?? "";
|
|
5449
|
-
const
|
|
5590
|
+
const ak = query.get("ak") ?? "";
|
|
5591
|
+
const token = resolveBearerToken(req);
|
|
5450
5592
|
const primary = targets[0];
|
|
5451
5593
|
const logger3 = buildLogger(primary);
|
|
5452
5594
|
if (req.method === "GET") {
|
|
5453
5595
|
const echostr = query.get("echostr") ?? "";
|
|
5454
|
-
if (!timestamp || !nonce || !
|
|
5596
|
+
if (!ak || !timestamp || !nonce || !token || !echostr) {
|
|
5455
5597
|
jsonError(res, "missing query params", 400);
|
|
5456
5598
|
return true;
|
|
5457
5599
|
}
|
|
5458
|
-
const
|
|
5459
|
-
if (!candidate.account.
|
|
5600
|
+
const tokenMatched2 = targets.filter((candidate) => {
|
|
5601
|
+
if (!candidate.account.appKey || !candidate.account.appSecret) {
|
|
5460
5602
|
return false;
|
|
5461
5603
|
}
|
|
5462
|
-
|
|
5463
|
-
|
|
5604
|
+
if (candidate.account.appKey !== ak) {
|
|
5605
|
+
return false;
|
|
5606
|
+
}
|
|
5607
|
+
return verifyAKSKBearerToken({
|
|
5608
|
+
ak: candidate.account.appKey,
|
|
5609
|
+
sk: candidate.account.appSecret,
|
|
5464
5610
|
timestamp,
|
|
5465
5611
|
nonce,
|
|
5466
|
-
|
|
5467
|
-
signature
|
|
5612
|
+
token
|
|
5468
5613
|
});
|
|
5469
5614
|
});
|
|
5470
|
-
if (
|
|
5615
|
+
if (tokenMatched2.length === 0) {
|
|
5471
5616
|
jsonError(res, "unauthorized");
|
|
5472
5617
|
return true;
|
|
5473
5618
|
}
|
|
5474
|
-
|
|
5475
|
-
if (decryptable2.length === 0) {
|
|
5476
|
-
jsonError(res, "unauthorized");
|
|
5477
|
-
return true;
|
|
5478
|
-
}
|
|
5479
|
-
const decryptedCandidates2 = decryptBaiduAppCandidates({
|
|
5480
|
-
candidates: decryptable2,
|
|
5481
|
-
encrypt: echostr
|
|
5482
|
-
});
|
|
5483
|
-
if (decryptedCandidates2.length === 0) {
|
|
5484
|
-
jsonError(res, "decrypt failed");
|
|
5485
|
-
return true;
|
|
5486
|
-
}
|
|
5487
|
-
const selected2 = selectDecryptedTarget({ candidates: decryptedCandidates2, logger: logger3 });
|
|
5488
|
-
jsonOk(res, selected2.plaintext);
|
|
5619
|
+
jsonOk(res, echostr);
|
|
5489
5620
|
return true;
|
|
5490
5621
|
}
|
|
5491
5622
|
if (req.method !== "POST") {
|
|
5492
5623
|
jsonError(res, "Method Not Allowed", 405);
|
|
5493
5624
|
return true;
|
|
5494
5625
|
}
|
|
5495
|
-
if (!timestamp || !nonce || !
|
|
5626
|
+
if (!ak || !timestamp || !nonce || !token) {
|
|
5496
5627
|
jsonError(res, "missing query params");
|
|
5497
5628
|
return true;
|
|
5498
5629
|
}
|
|
@@ -5502,90 +5633,56 @@ async function handleBaiduAppWebhookRequest(req, res) {
|
|
|
5502
5633
|
return true;
|
|
5503
5634
|
}
|
|
5504
5635
|
const rawBody = body.raw;
|
|
5505
|
-
|
|
5506
|
-
|
|
5507
|
-
|
|
5508
|
-
|
|
5509
|
-
|
|
5510
|
-
|
|
5511
|
-
encrypt = xmlData.Encrypt ?? "";
|
|
5512
|
-
msgSignature = xmlData.MsgSignature ?? signature;
|
|
5513
|
-
msgTimestamp = xmlData.TimeStamp ?? timestamp;
|
|
5514
|
-
msgNonce = xmlData.Nonce ?? nonce;
|
|
5515
|
-
logger3.info(`inbound xml parsed: hasEncrypt=${Boolean(encrypt)}, msg_signature=${msgSignature ? "yes" : "no"}`);
|
|
5516
|
-
} else {
|
|
5517
|
-
try {
|
|
5518
|
-
const record = JSON.parse(rawBody);
|
|
5519
|
-
encrypt = String(record.encrypt ?? record.Encrypt ?? "");
|
|
5520
|
-
logger3.info(`inbound json parsed: hasEncrypt=${Boolean(encrypt)}`);
|
|
5521
|
-
} catch {
|
|
5522
|
-
logger3.warn(`inbound payload parse failed: not valid xml or json`);
|
|
5523
|
-
jsonError(res, "invalid payload format");
|
|
5524
|
-
return true;
|
|
5525
|
-
}
|
|
5526
|
-
}
|
|
5527
|
-
if (!encrypt) {
|
|
5528
|
-
jsonError(res, "missing encrypt");
|
|
5636
|
+
try {
|
|
5637
|
+
JSON.parse(rawBody);
|
|
5638
|
+
logger3.info("inbound json parsed");
|
|
5639
|
+
} catch {
|
|
5640
|
+
logger3.warn("inbound payload parse failed: not valid json");
|
|
5641
|
+
jsonError(res, "invalid payload format");
|
|
5529
5642
|
return true;
|
|
5530
5643
|
}
|
|
5531
|
-
const
|
|
5532
|
-
if (!candidate.account.
|
|
5644
|
+
const tokenMatched = targets.filter((candidate) => {
|
|
5645
|
+
if (!candidate.account.appKey || !candidate.account.appSecret) {
|
|
5646
|
+
return false;
|
|
5647
|
+
}
|
|
5648
|
+
if (candidate.account.appKey !== ak) {
|
|
5533
5649
|
return false;
|
|
5534
5650
|
}
|
|
5535
|
-
return
|
|
5536
|
-
|
|
5537
|
-
|
|
5538
|
-
|
|
5539
|
-
|
|
5540
|
-
|
|
5651
|
+
return verifyAKSKBearerToken({
|
|
5652
|
+
ak: candidate.account.appKey,
|
|
5653
|
+
sk: candidate.account.appSecret,
|
|
5654
|
+
timestamp,
|
|
5655
|
+
nonce,
|
|
5656
|
+
token
|
|
5541
5657
|
});
|
|
5542
5658
|
});
|
|
5543
|
-
if (
|
|
5544
|
-
logger3.warn(`
|
|
5659
|
+
if (tokenMatched.length === 0) {
|
|
5660
|
+
logger3.warn(`bearer token verification failed: checked ${targets.length} account(s), none matched`);
|
|
5545
5661
|
jsonError(res, "unauthorized");
|
|
5546
5662
|
return true;
|
|
5547
5663
|
}
|
|
5548
|
-
logger3.debug(`
|
|
5549
|
-
const
|
|
5550
|
-
|
|
5551
|
-
|
|
5552
|
-
jsonError(res, "openclaw-baiduapp not configured");
|
|
5553
|
-
return true;
|
|
5554
|
-
}
|
|
5555
|
-
const decryptedCandidates = decryptBaiduAppCandidates({
|
|
5556
|
-
candidates: decryptable,
|
|
5557
|
-
encrypt
|
|
5664
|
+
logger3.debug(`bearer token verified: ${tokenMatched.length} account(s) matched`);
|
|
5665
|
+
const decryptedCandidates = parseBaiduAppCandidates({
|
|
5666
|
+
candidates: tokenMatched,
|
|
5667
|
+
raw: rawBody
|
|
5558
5668
|
});
|
|
5559
|
-
if (decryptedCandidates.length === 0) {
|
|
5560
|
-
logger3.warn(`decrypt failed for all ${decryptable.length} candidate account(s)`);
|
|
5561
|
-
jsonError(res, "decrypt failed");
|
|
5562
|
-
return true;
|
|
5563
|
-
}
|
|
5564
5669
|
const selected = selectDecryptedTarget({ candidates: decryptedCandidates, logger: logger3 });
|
|
5565
5670
|
const target = selected.target;
|
|
5566
|
-
if (!target.account.configured || !target.account.
|
|
5671
|
+
if (!target.account.configured || !target.account.appKey || !target.account.appSecret) {
|
|
5567
5672
|
logger3.warn(`selected account ${target.account.accountId} not fully configured`);
|
|
5568
5673
|
jsonError(res, "openclaw-baiduapp not configured");
|
|
5569
5674
|
return true;
|
|
5570
5675
|
}
|
|
5571
|
-
|
|
5676
|
+
await processBaiduAppInboundMessage({
|
|
5572
5677
|
target,
|
|
5573
5678
|
msg: selected.msg
|
|
5574
5679
|
});
|
|
5575
|
-
jsonOk(
|
|
5576
|
-
res,
|
|
5577
|
-
buildEncryptedJsonReply({
|
|
5578
|
-
account: target.account,
|
|
5579
|
-
plaintextJson: reply,
|
|
5580
|
-
nonce: msgNonce,
|
|
5581
|
-
timestamp: msgTimestamp
|
|
5582
|
-
})
|
|
5583
|
-
);
|
|
5680
|
+
jsonOk(res, {});
|
|
5584
5681
|
return true;
|
|
5585
5682
|
}
|
|
5586
5683
|
|
|
5587
5684
|
// src/poller.ts
|
|
5588
|
-
var DEFAULT_POLL_INTERVAL_MS =
|
|
5685
|
+
var DEFAULT_POLL_INTERVAL_MS = 3e3;
|
|
5589
5686
|
var DEFAULT_POLL_REQUEST_TIMEOUT_MS = 1e4;
|
|
5590
5687
|
var accountPollers = /* @__PURE__ */ new Map();
|
|
5591
5688
|
function buildPollingTextInboundMessage(content) {
|
|
@@ -5596,21 +5693,36 @@ function buildPollingTextInboundMessage(content) {
|
|
|
5596
5693
|
}
|
|
5597
5694
|
};
|
|
5598
5695
|
}
|
|
5696
|
+
function parseTextAndFilesFromList(list) {
|
|
5697
|
+
const textItem = list.find((entry) => entry.type === "text");
|
|
5698
|
+
const fileItems = list.filter((entry) => entry.type === "file");
|
|
5699
|
+
const content = textItem?.type === "text" ? textItem.data.text.content : void 0;
|
|
5700
|
+
const files = fileItems.map((file) => {
|
|
5701
|
+
const fileId = file.data.file.fileId;
|
|
5702
|
+
return typeof fileId === "string" && fileId.trim() ? { url: fileId, fileType: "file" } : null;
|
|
5703
|
+
}).filter((file) => file != null);
|
|
5704
|
+
return { content, files };
|
|
5705
|
+
}
|
|
5599
5706
|
async function dispatchPendingPollingMessages(data, target) {
|
|
5707
|
+
console.log(data);
|
|
5600
5708
|
if (!target || data.length === 0) {
|
|
5601
5709
|
return;
|
|
5602
5710
|
}
|
|
5603
5711
|
for (const item of data) {
|
|
5604
|
-
|
|
5605
|
-
|
|
5606
|
-
}
|
|
5607
|
-
|
|
5608
|
-
if (typeof content !== "string" || content.length === 0) {
|
|
5712
|
+
const list = Array.isArray(item.list) ? item.list : [];
|
|
5713
|
+
const { content, files } = parseTextAndFilesFromList(list);
|
|
5714
|
+
console.log({ content, files });
|
|
5715
|
+
if (typeof content !== "string" && files.length === 0) {
|
|
5609
5716
|
continue;
|
|
5610
5717
|
}
|
|
5611
5718
|
await processBaiduAppInboundMessage({
|
|
5612
5719
|
target,
|
|
5613
|
-
msg:
|
|
5720
|
+
msg: {
|
|
5721
|
+
...buildPollingTextInboundMessage(typeof content === "string" ? content : ""),
|
|
5722
|
+
msgid: typeof item.msgId === "string" ? item.msgId : void 0,
|
|
5723
|
+
agentid: typeof item.agentId === "string" ? item.agentId : void 0,
|
|
5724
|
+
...files.length > 0 ? { files } : {}
|
|
5725
|
+
}
|
|
5614
5726
|
});
|
|
5615
5727
|
}
|
|
5616
5728
|
}
|
|
@@ -5665,12 +5777,29 @@ function createAbortSignalController(params) {
|
|
|
5665
5777
|
};
|
|
5666
5778
|
}
|
|
5667
5779
|
async function pollBaiduAppChatlistOnce(account, loggerOptions, requestOptions) {
|
|
5668
|
-
const endpoint = `${account.apiBase}/
|
|
5780
|
+
const endpoint = `${account.apiBase}/channel/msg/poll`;
|
|
5781
|
+
const logger3 = createLogger("openclaw-baiduapp:poller", loggerOptions);
|
|
5782
|
+
const timestamp = String(Math.floor(Date.now() / 1e3));
|
|
5783
|
+
const nonce = generateNonce();
|
|
5784
|
+
const authorization = buildAuthorizationHeader({
|
|
5785
|
+
ak: account.appKey ?? "",
|
|
5786
|
+
sk: account.appSecret ?? "",
|
|
5787
|
+
timestamp,
|
|
5788
|
+
nonce
|
|
5789
|
+
});
|
|
5669
5790
|
const query = new URLSearchParams({
|
|
5670
5791
|
ak: account.appKey ?? "",
|
|
5671
|
-
|
|
5792
|
+
timestamp,
|
|
5793
|
+
nonce
|
|
5672
5794
|
});
|
|
5673
5795
|
const url = `${endpoint}?${query.toString()}`;
|
|
5796
|
+
const payload = {
|
|
5797
|
+
sessionId: "agent:main:main",
|
|
5798
|
+
num: 5,
|
|
5799
|
+
version: PLUGIN_VERSION
|
|
5800
|
+
};
|
|
5801
|
+
const body = JSON.stringify(payload);
|
|
5802
|
+
console.log("start poll", url);
|
|
5674
5803
|
const requestSignal = createAbortSignalController({
|
|
5675
5804
|
signal: requestOptions?.signal,
|
|
5676
5805
|
timeoutMs: requestOptions?.timeoutMs
|
|
@@ -5678,8 +5807,13 @@ async function pollBaiduAppChatlistOnce(account, loggerOptions, requestOptions)
|
|
|
5678
5807
|
let response;
|
|
5679
5808
|
try {
|
|
5680
5809
|
response = await (requestOptions?.fetchImpl ?? fetch)(url, {
|
|
5681
|
-
method: "
|
|
5682
|
-
|
|
5810
|
+
method: "POST",
|
|
5811
|
+
body,
|
|
5812
|
+
signal: requestSignal.signal,
|
|
5813
|
+
headers: {
|
|
5814
|
+
"Authorization": authorization,
|
|
5815
|
+
"Content-Type": "application/json"
|
|
5816
|
+
}
|
|
5683
5817
|
});
|
|
5684
5818
|
} catch (error) {
|
|
5685
5819
|
requestSignal.cleanup();
|
|
@@ -5732,10 +5866,13 @@ async function pollBaiduAppChatlistOnce(account, loggerOptions, requestOptions)
|
|
|
5732
5866
|
}
|
|
5733
5867
|
});
|
|
5734
5868
|
}
|
|
5735
|
-
|
|
5869
|
+
if (parsed.code !== 0) {
|
|
5870
|
+
logger3.debug(`Baidu poll returned non-zero code=${String(parsed.code)}, message=${parsed.message}`);
|
|
5871
|
+
}
|
|
5872
|
+
const msgList = Array.isArray(parsed.data?.msgList) ? parsed.data.msgList : [];
|
|
5736
5873
|
return buildPollResult({
|
|
5737
5874
|
ok: true,
|
|
5738
|
-
data
|
|
5875
|
+
data: msgList
|
|
5739
5876
|
});
|
|
5740
5877
|
}
|
|
5741
5878
|
function scheduleNextPoll(account, state) {
|
|
@@ -5851,16 +5988,33 @@ function resolveOutboundLocalMediaPath(mediaUrl) {
|
|
|
5851
5988
|
}
|
|
5852
5989
|
return trimmed;
|
|
5853
5990
|
}
|
|
5854
|
-
function inferBaiduOutboundFileType(params) {
|
|
5855
|
-
const ext = path2.extname(params.mediaPath).toLowerCase();
|
|
5856
|
-
return ext.startsWith(".") ? ext.slice(1) : ext || "bin";
|
|
5857
|
-
}
|
|
5858
5991
|
function buildOutboundMediaPayload(params) {
|
|
5859
5992
|
const caption = params.caption?.trim();
|
|
5993
|
+
const mediaItem = params.externalUrl ? {
|
|
5994
|
+
type: "external_url",
|
|
5995
|
+
data: {
|
|
5996
|
+
externalUrl: {
|
|
5997
|
+
url: params.externalUrl
|
|
5998
|
+
}
|
|
5999
|
+
}
|
|
6000
|
+
} : {
|
|
6001
|
+
type: "file",
|
|
6002
|
+
data: {
|
|
6003
|
+
file: {
|
|
6004
|
+
fileId: params.fileId ?? ""
|
|
6005
|
+
}
|
|
6006
|
+
}
|
|
6007
|
+
};
|
|
5860
6008
|
return {
|
|
5861
|
-
|
|
5862
|
-
|
|
5863
|
-
|
|
6009
|
+
list: [
|
|
6010
|
+
...caption ? [{
|
|
6011
|
+
type: "text",
|
|
6012
|
+
data: {
|
|
6013
|
+
text: { content: caption }
|
|
6014
|
+
}
|
|
6015
|
+
}] : [],
|
|
6016
|
+
mediaItem
|
|
6017
|
+
]
|
|
5864
6018
|
};
|
|
5865
6019
|
}
|
|
5866
6020
|
function resolveDirectOutboundFiles(files) {
|
|
@@ -6097,7 +6251,10 @@ var baiduAppPlugin = {
|
|
|
6097
6251
|
};
|
|
6098
6252
|
}
|
|
6099
6253
|
try {
|
|
6100
|
-
const result = await sendBaiduAppMessage(account, params.text
|
|
6254
|
+
const result = await sendBaiduAppMessage(account, params.text, {
|
|
6255
|
+
agentid: account.accountId,
|
|
6256
|
+
isActive: true
|
|
6257
|
+
});
|
|
6101
6258
|
return {
|
|
6102
6259
|
channel: "openclaw-baiduapp",
|
|
6103
6260
|
ok: result.ok,
|
|
@@ -6140,14 +6297,15 @@ var baiduAppPlugin = {
|
|
|
6140
6297
|
account,
|
|
6141
6298
|
buildOutboundMediaPayload({
|
|
6142
6299
|
caption: params.text,
|
|
6143
|
-
|
|
6144
|
-
|
|
6145
|
-
}
|
|
6300
|
+
externalUrl: remoteUrl
|
|
6301
|
+
}),
|
|
6302
|
+
{ agentid: account.accountId, isActive: true }
|
|
6146
6303
|
);
|
|
6147
6304
|
return {
|
|
6148
6305
|
channel: "openclaw-baiduapp",
|
|
6149
|
-
ok:
|
|
6150
|
-
messageId: result.msgid ?? ""
|
|
6306
|
+
ok: result.ok,
|
|
6307
|
+
messageId: result.msgid ?? "",
|
|
6308
|
+
error: result.ok ? void 0 : new Error(result.errmsg ?? "send failed")
|
|
6151
6309
|
};
|
|
6152
6310
|
} catch (err) {
|
|
6153
6311
|
return {
|
|
@@ -6180,11 +6338,9 @@ var baiduAppPlugin = {
|
|
|
6180
6338
|
account,
|
|
6181
6339
|
buildOutboundMediaPayload({
|
|
6182
6340
|
caption: params.text,
|
|
6183
|
-
|
|
6184
|
-
|
|
6185
|
-
|
|
6186
|
-
})
|
|
6187
|
-
})
|
|
6341
|
+
fileId: uploaded.fileId
|
|
6342
|
+
}),
|
|
6343
|
+
{ agentid: account.accountId, isActive: true }
|
|
6188
6344
|
);
|
|
6189
6345
|
return {
|
|
6190
6346
|
channel: "openclaw-baiduapp",
|
|
@@ -6206,6 +6362,18 @@ var baiduAppPlugin = {
|
|
|
6206
6362
|
}
|
|
6207
6363
|
}
|
|
6208
6364
|
},
|
|
6365
|
+
auth: {
|
|
6366
|
+
login: async (params) => {
|
|
6367
|
+
void params.channelInput;
|
|
6368
|
+
const { loginBaiduApp: loginBaiduApp2 } = await Promise.resolve().then(() => (init_login(), login_exports));
|
|
6369
|
+
await loginBaiduApp2({
|
|
6370
|
+
cfg: params.cfg,
|
|
6371
|
+
accountId: params.accountId,
|
|
6372
|
+
runtime: params.runtime,
|
|
6373
|
+
verbose: params.verbose
|
|
6374
|
+
});
|
|
6375
|
+
}
|
|
6376
|
+
},
|
|
6209
6377
|
gateway: {
|
|
6210
6378
|
startAccount: async (ctx) => {
|
|
6211
6379
|
ctx.setStatus?.({ accountId: ctx.accountId });
|
|
@@ -6243,21 +6411,19 @@ var baiduAppPlugin = {
|
|
|
6243
6411
|
existing();
|
|
6244
6412
|
}
|
|
6245
6413
|
unregisterHooks.set(ctx.accountId, unregister);
|
|
6246
|
-
|
|
6247
|
-
|
|
6414
|
+
startAccountPolling({
|
|
6415
|
+
account,
|
|
6416
|
+
dispatchTarget: {
|
|
6248
6417
|
account,
|
|
6249
|
-
|
|
6250
|
-
|
|
6251
|
-
|
|
6252
|
-
|
|
6253
|
-
|
|
6254
|
-
|
|
6255
|
-
|
|
6256
|
-
|
|
6257
|
-
|
|
6258
|
-
}
|
|
6259
|
-
});
|
|
6260
|
-
}
|
|
6418
|
+
config: ctx.cfg ?? {},
|
|
6419
|
+
runtime: runtime2,
|
|
6420
|
+
statusSink: (patch) => ctx.setStatus?.({ accountId: ctx.accountId, ...patch })
|
|
6421
|
+
},
|
|
6422
|
+
onError: (error) => {
|
|
6423
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
6424
|
+
ctx.log?.error(`[openclaw-baiduapp] polling failed for account ${ctx.accountId}: ${message}`);
|
|
6425
|
+
}
|
|
6426
|
+
});
|
|
6261
6427
|
ctx.log?.info(
|
|
6262
6428
|
`[openclaw-baiduapp] webhook registered at ${path4} for account ${ctx.accountId} (canSendActive=${account.canSendActive})`
|
|
6263
6429
|
);
|
|
@@ -6309,7 +6475,7 @@ async function sendMessage(account, options) {
|
|
|
6309
6475
|
};
|
|
6310
6476
|
}
|
|
6311
6477
|
try {
|
|
6312
|
-
const textResult = await sendBaiduAppMessage(account, options.text);
|
|
6478
|
+
const textResult = await sendBaiduAppMessage(account, options.text, { isActive: true });
|
|
6313
6479
|
return {
|
|
6314
6480
|
ok: textResult.ok,
|
|
6315
6481
|
msgid: textResult.msgid,
|