@searchfe/openclaw-baiduapp 0.1.9-beta.1 → 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/dist/index.js CHANGED
@@ -2,7 +2,7 @@ import { spawnSync } from 'child_process';
2
2
  import readline from 'readline';
3
3
  import path2 from 'path';
4
4
  import { fileURLToPath } from 'url';
5
- import crypto2 from 'crypto';
5
+ import crypto from 'crypto';
6
6
  import { createRequire } from 'module';
7
7
  import { lookup } from 'dns/promises';
8
8
  import fs2 from 'fs/promises';
@@ -4334,7 +4334,7 @@ function createLogger(prefix, opts) {
4334
4334
  };
4335
4335
  }
4336
4336
  function sha1Hex(input) {
4337
- return crypto2.createHash("sha1").update(input).digest("hex");
4337
+ return crypto.createHash("sha1").update(input).digest("hex");
4338
4338
  }
4339
4339
  function computeAKSKBearerToken(params) {
4340
4340
  return sha1Hex(params.ak + params.sk + params.timestamp + params.nonce);
@@ -4349,19 +4349,22 @@ function verifyAKSKBearerToken(params) {
4349
4349
  return expected === params.token;
4350
4350
  }
4351
4351
  function generateNonce() {
4352
- return crypto2.randomBytes(8).toString("hex");
4352
+ return crypto.randomBytes(8).toString("hex");
4353
4353
  }
4354
4354
  function buildAuthorizationHeader(params) {
4355
4355
  const token = computeAKSKBearerToken(params);
4356
4356
  return `Bearer ${token}`;
4357
4357
  }
4358
+
4359
+ // src/types.ts
4360
+ var DEFAULT_BAIDU_APP_SESSION_ID = "agent:main:main";
4358
4361
  var require2 = createRequire(import.meta.url);
4359
4362
  var pkg = require2("../package.json");
4360
4363
  var PLUGIN_VERSION = pkg.version;
4361
4364
 
4362
4365
  // src/api.ts
4363
4366
  var logger = createLogger("openclaw-baiduapp");
4364
- var DEFAULT_sessionId = "agent:main:main";
4367
+ var DEFAULT_sessionId = DEFAULT_BAIDU_APP_SESSION_ID;
4365
4368
  async function sendBaiduAppMessage(account, message, options) {
4366
4369
  if (!account.canSendActive) {
4367
4370
  logger.error("Account not configured for active sending (missing appKey or appSecret)");
@@ -4424,6 +4427,15 @@ async function sendBaiduAppMessage(account, message, options) {
4424
4427
  logger.error(`request failed: ${errmsg}`);
4425
4428
  return { ok: false, errcode: resp.status, errmsg };
4426
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
+ }
4427
4439
  const result = {
4428
4440
  ok: true
4429
4441
  };
@@ -4769,92 +4781,122 @@ var require3 = createRequire(import.meta.url);
4769
4781
  var BaiduCloudSdk = require3("@baiducloud/sdk");
4770
4782
  var DefaultBosClient = BaiduCloudSdk.BosClient;
4771
4783
  var MAX_DOWNLOAD_REDIRECTS = 5;
4772
- var EMPTY_QUERY_MD5 = crypto2.createHash("md5").update("").digest("hex");
4773
- var SKS_BASE_TOKEN_POSITIONS = [12, 37, 5, 23, 48, 15, 62, 33];
4774
- async function fetchSksCredentials(account, deps = {}) {
4775
- if (!account.appKey?.trim()) {
4776
- throw new Error("Cannot fetch SKS credentials without account.appKey");
4777
- }
4778
- if (!account.appSecret?.trim()) {
4779
- throw new Error("Cannot fetch SKS credentials without account.appSecret");
4780
- }
4781
- const fetchImpl = deps.fetchImpl ?? fetch;
4782
- const apiBase = account.apiBase.replace(/\/+$/, "");
4783
- const pageLid = deps.createPageLid?.() ?? generateSksPageLid();
4784
- const timestamp = String((deps.now?.() ?? /* @__PURE__ */ new Date()).getTime());
4785
- const token = createSksRequestToken({ pageLid, timestamp });
4786
- const url = `${apiBase}/channel/file/sts?ak=${encodeURIComponent(account.appKey)}&sk=${encodeURIComponent(account.appSecret)}&tk=${encodeURIComponent(token)}`;
4787
- const response = await fetchImpl(url, { method: "POST" });
4788
- if (!response.ok) {
4789
- throw new Error(`SKS request failed with HTTP ${response.status}`);
4790
- }
4791
- let payload;
4792
- try {
4793
- payload = await response.json();
4794
- } catch (error) {
4795
- throw new Error(`SKS response is not valid JSON: ${formatError(error)}`, { cause: error });
4796
- }
4797
- return parseSksCredentialsFromPayload(payload);
4798
- }
4799
- function parseSksCredentialsFromPayload(payload) {
4800
- const parsed = parseSksResponse(payload);
4801
- if (parsed.status !== 0) {
4802
- 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");
4803
4802
  }
4804
- if (!parsed.data) {
4805
- throw new Error("SKS response missing data");
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 };
4806
4807
  }
4807
4808
  return {
4808
- ak: requireNonEmptyString(parsed.data.ak, "SKS data.ak"),
4809
- sk: requireNonEmptyString(parsed.data.sk, "SKS data.sk"),
4810
- sessionToken: requireNonEmptyString(parsed.data.token, "SKS data.token"),
4811
- bucketName: requireNonEmptyString(parsed.data.bucketName, "SKS data.bucketName"),
4812
- prefixPath: sanitizeObjectKeyPrefix(requireNonEmptyString(parsed.data.preFixPath, "SKS data.preFixPath")),
4813
- endpoint: requireNonEmptyString(parsed.data.bceUrl, "SKS data.bceUrl")
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")
4814
4815
  };
4815
4816
  }
4816
- function createSksRequestToken(params) {
4817
- const pageLid = requireNonEmptyString(params.pageLid, "SKS pageLid");
4818
- const timestamp = requireNonEmptyString(params.timestamp, "SKS timestamp");
4819
- const baseToken = createSksBaseToken(pageLid);
4820
- const payload = `${baseToken}|${EMPTY_QUERY_MD5}|${timestamp}|${pageLid}`;
4821
- return `${Buffer.from(payload, "utf8").toString("base64")}-${pageLid}-3`;
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 };
4822
4843
  }
4823
- function createBosClient(credentials, deps = {}) {
4844
+ function createBosClient(fileSts, deps = {}) {
4845
+ if (!fileSts.sts || !fileSts.bceUrl) {
4846
+ throw new Error("Cannot create BOS client without STS credentials");
4847
+ }
4824
4848
  const BosClientCtor = deps.bosClientCtor ?? DefaultBosClient;
4825
4849
  return new BosClientCtor({
4826
- endpoint: credentials.endpoint,
4827
- sessionToken: credentials.sessionToken,
4850
+ endpoint: fileSts.bceUrl,
4851
+ sessionToken: fileSts.sts.token,
4828
4852
  credentials: {
4829
- ak: credentials.ak,
4830
- sk: credentials.sk
4853
+ ak: fileSts.sts.ak,
4854
+ sk: fileSts.sts.sk
4831
4855
  }
4832
4856
  });
4833
4857
  }
4834
4858
  async function uploadLocalFileToBos(options, deps = {}) {
4835
4859
  const localFile = await assertUploadableLocalFile(options.filePath);
4836
- const credentials = await fetchSksCredentials(options.account, deps);
4837
- const client = createBosClient(credentials, deps);
4838
4860
  const sourceName = options.fileName ?? path2.basename(localFile);
4839
- const sanitizedFileName = sanitizeFileName(sourceName);
4840
- const key = buildBosObjectKey({
4841
- prefixPath: credentials.prefixPath,
4842
- fileName: sanitizedFileName,
4843
- now: deps.now
4844
- });
4845
- const uploadOptions = options.contentType?.trim() ? {
4846
- "Content-Type": options.contentType.trim()
4847
- } : void 0;
4848
- const [, fileStat] = await Promise.all([
4849
- client.putObjectFromFile(credentials.bucketName, key, localFile, uploadOptions),
4850
- fs2.stat(localFile)
4851
- ]);
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
+ );
4852
4893
  return {
4853
- bucketName: credentials.bucketName,
4854
- key,
4855
- url: client.generateUrl(credentials.bucketName, key),
4856
- fileName: sanitizedFileName,
4857
- 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
4858
4900
  };
4859
4901
  }
4860
4902
  async function downloadInboundFileToTemp(options, deps = {}) {
@@ -4952,38 +4994,93 @@ async function pruneExpiredTempFiles(deps = {}) {
4952
4994
  await walk(tempDir);
4953
4995
  return { deletedFiles };
4954
4996
  }
4955
- function parseSksResponse(payload) {
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) {
4956
5030
  if (!payload || typeof payload !== "object") {
4957
- throw new Error("SKS response must be an object");
5031
+ throw new Error(`${label} response must be an object`);
4958
5032
  }
4959
5033
  const record = payload;
4960
- const status = typeof record.code === "number" ? record.code : typeof record.status === "number" ? record.status : void 0;
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
+ }
4961
5042
  const data = record.data;
4962
5043
  return {
4963
- status,
4964
- data: data && typeof data === "object" ? {
4965
- ak: data.ak,
4966
- sk: data.sk,
4967
- token: data.token,
4968
- bucketName: data.bucketName,
4969
- preFixPath: data.preFixPath,
4970
- bceUrl: data.bceUrl
4971
- } : void 0
5044
+ code,
5045
+ msg: typeof record.msg === "string" ? record.msg : void 0,
5046
+ data: data && typeof data === "object" ? data : void 0
4972
5047
  };
4973
5048
  }
4974
- function generateSksPageLid() {
4975
- return crypto2.randomBytes(16).toString("hex");
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
+ };
4976
5060
  }
4977
- function createSksBaseToken(pageLid) {
4978
- const pageLidHash = crypto2.createHash("sha256").update(pageLid).digest("hex");
4979
- const characters = SKS_BASE_TOKEN_POSITIONS.map((position) => {
4980
- const character = pageLidHash[position];
4981
- if (!character) {
4982
- throw new Error(`SKS base token position is out of range: ${position}`);
4983
- }
4984
- return character;
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
+ };
4985
5075
  });
4986
- return characters.join("");
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
+ }
4987
5084
  }
4988
5085
  function requireNonEmptyString(value, label) {
4989
5086
  if (typeof value !== "string" || !value.trim()) {
@@ -4991,36 +5088,19 @@ function requireNonEmptyString(value, label) {
4991
5088
  }
4992
5089
  return value.trim();
4993
5090
  }
4994
- function sanitizeObjectKeyPrefix(prefixPath) {
4995
- const normalized = prefixPath.replace(/\\/g, "/").trim();
4996
- const segments = normalized.split("/").map((segment) => segment.trim()).filter(Boolean);
4997
- if (segments.length === 0) {
4998
- 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`);
4999
5094
  }
5000
- const safeSegments = segments.map((segment) => {
5001
- if (segment === "." || segment === "..") {
5002
- throw new Error("SKS data.preFixPath contains path traversal");
5003
- }
5004
- return sanitizeObjectKeySegment(segment);
5005
- });
5006
- return safeSegments.join("/");
5095
+ return value;
5007
5096
  }
5008
- function sanitizeObjectKeySegment(segment) {
5009
- const sanitized = segment.replace(/[^a-zA-Z0-9._-]+/g, "-").replace(/-+/g, "-").replace(/^-|-$/g, "");
5010
- if (!sanitized) {
5011
- 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`);
5012
5100
  }
5013
- return sanitized.slice(0, 80);
5101
+ return value;
5014
5102
  }
5015
- function sanitizeFileName(fileName) {
5016
- assertSafeUntrustedFileName(fileName);
5017
- const parsed = path2.parse(fileName.trim());
5018
- const safeBase = parsed.name.replace(/[^a-zA-Z0-9._-]+/g, "-").replace(/-+/g, "-").replace(/^-|-$/g, "");
5019
- const safeExt = parsed.ext.replace(/[^a-zA-Z0-9.]+/g, "").slice(0, 16);
5020
- const baseName = safeBase || "file";
5021
- return `${baseName.slice(0, 80)}${safeExt}`;
5022
- }
5023
- function assertSafeUntrustedFileName(fileName) {
5103
+ function requireSafeDisplayFileName(fileName) {
5024
5104
  const trimmed = fileName.trim();
5025
5105
  if (!trimmed) {
5026
5106
  throw new Error("Filename cannot be empty");
@@ -5031,14 +5111,18 @@ function assertSafeUntrustedFileName(fileName) {
5031
5111
  if (trimmed === "." || trimmed === "..") {
5032
5112
  throw new Error(`Filename is not safe: ${fileName}`);
5033
5113
  }
5114
+ return trimmed;
5034
5115
  }
5035
- function buildBosObjectKey(params) {
5036
- const timestamp = formatUtcDate(params.now?.() ?? /* @__PURE__ */ new Date());
5037
- const uniqueId = crypto2.randomUUID();
5038
- return `${params.prefixPath}/${timestamp}/${uniqueId}-${params.fileName}`;
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;
5039
5122
  }
5040
- function formatUtcDate(date) {
5041
- return date.toISOString().slice(0, 10);
5123
+ function inferOutboundFileType(fileName) {
5124
+ const extension = path2.extname(fileName).replace(/^\./, "").toLowerCase();
5125
+ return extension || "bin";
5042
5126
  }
5043
5127
  async function assertUploadableLocalFile(filePath) {
5044
5128
  const resolvedPath = path2.resolve(filePath);
@@ -5163,11 +5247,11 @@ function parseContentDispositionFileName(contentDisposition) {
5163
5247
  }
5164
5248
  function resolveDownloadFileName(params) {
5165
5249
  const candidate = params.explicitFileName ?? params.headerFileName ?? params.urlFileName ?? "file.bin";
5166
- return sanitizeFileName(candidate);
5250
+ return requireSafeDisplayFileName(candidate);
5167
5251
  }
5168
5252
  function buildTempFilePath(params) {
5169
5253
  const timestamp = (params.now?.() ?? /* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
5170
- return path2.join(params.tempDir, `${timestamp}-${crypto2.randomUUID()}-${params.fileName}`);
5254
+ return path2.join(params.tempDir, `${timestamp}-${crypto.randomUUID()}-${params.fileName}`);
5171
5255
  }
5172
5256
  async function ensurePluginTempDir(resolveTmpDir) {
5173
5257
  const baseTmpDir = resolveTmpDir?.() ?? resolvePreferredOpenClawTmpDir();
@@ -5614,7 +5698,7 @@ function parseTextAndFilesFromList(list) {
5614
5698
  const fileItems = list.filter((entry) => entry.type === "file");
5615
5699
  const content = textItem?.type === "text" ? textItem.data.text.content : void 0;
5616
5700
  const files = fileItems.map((file) => {
5617
- const fileId = file.data.file_id;
5701
+ const fileId = file.data.file.fileId;
5618
5702
  return typeof fileId === "string" && fileId.trim() ? { url: fileId, fileType: "file" } : null;
5619
5703
  }).filter((file) => file != null);
5620
5704
  return { content, files };
@@ -5715,7 +5799,7 @@ async function pollBaiduAppChatlistOnce(account, loggerOptions, requestOptions)
5715
5799
  version: PLUGIN_VERSION
5716
5800
  };
5717
5801
  const body = JSON.stringify(payload);
5718
- console.log("start poll");
5802
+ console.log("start poll", url);
5719
5803
  const requestSignal = createAbortSignalController({
5720
5804
  signal: requestOptions?.signal,
5721
5805
  timeoutMs: requestOptions?.timeoutMs
@@ -5904,12 +5988,23 @@ function resolveOutboundLocalMediaPath(mediaUrl) {
5904
5988
  }
5905
5989
  return trimmed;
5906
5990
  }
5907
- function inferBaiduOutboundFileType(params) {
5908
- const ext = path2.extname(params.mediaPath).toLowerCase();
5909
- return ext.startsWith(".") ? ext.slice(1) : ext || "bin";
5910
- }
5911
5991
  function buildOutboundMediaPayload(params) {
5912
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
+ };
5913
6008
  return {
5914
6009
  list: [
5915
6010
  ...caption ? [{
@@ -5918,12 +6013,7 @@ function buildOutboundMediaPayload(params) {
5918
6013
  text: { content: caption }
5919
6014
  }
5920
6015
  }] : [],
5921
- {
5922
- type: "file",
5923
- data: {
5924
- file_id: params.uploadedUrl
5925
- }
5926
- }
6016
+ mediaItem
5927
6017
  ]
5928
6018
  };
5929
6019
  }
@@ -6207,15 +6297,15 @@ var baiduAppPlugin = {
6207
6297
  account,
6208
6298
  buildOutboundMediaPayload({
6209
6299
  caption: params.text,
6210
- uploadedUrl: remoteUrl,
6211
- fileType: inferBaiduOutboundFileType({ mediaPath: remoteUrl })
6300
+ externalUrl: remoteUrl
6212
6301
  }),
6213
6302
  { agentid: account.accountId, isActive: true }
6214
6303
  );
6215
6304
  return {
6216
6305
  channel: "openclaw-baiduapp",
6217
- ok: true,
6218
- messageId: result.msgid ?? ""
6306
+ ok: result.ok,
6307
+ messageId: result.msgid ?? "",
6308
+ error: result.ok ? void 0 : new Error(result.errmsg ?? "send failed")
6219
6309
  };
6220
6310
  } catch (err) {
6221
6311
  return {
@@ -6248,10 +6338,7 @@ var baiduAppPlugin = {
6248
6338
  account,
6249
6339
  buildOutboundMediaPayload({
6250
6340
  caption: params.text,
6251
- uploadedUrl: uploaded.url,
6252
- fileType: inferBaiduOutboundFileType({
6253
- mediaPath: uploaded.fileName || localMediaPath
6254
- })
6341
+ fileId: uploaded.fileId
6255
6342
  }),
6256
6343
  { agentid: account.accountId, isActive: true }
6257
6344
  );