@lark-apaas/client-capability 0.1.5 → 0.1.7-alpha.0

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.cjs CHANGED
@@ -1,7 +1,9 @@
1
1
  "use strict";
2
+ var __create = Object.create;
2
3
  var __defProp = Object.defineProperty;
3
4
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
5
  var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
5
7
  var __hasOwnProp = Object.prototype.hasOwnProperty;
6
8
  var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
7
9
  var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
@@ -17,6 +19,14 @@ var __copyProps = (to, from, except, desc) => {
17
19
  }
18
20
  return to;
19
21
  };
22
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
23
+ // If the importer is in node compatibility mode or this is not an ESM
24
+ // file that has been converted to a CommonJS file using a Babel-
25
+ // compatible transform (i.e. "__esModule" has not been set), then set
26
+ // "default" to the CommonJS "module.exports" for node compatibility.
27
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
28
+ mod
29
+ ));
20
30
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
21
31
  var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
22
32
 
@@ -117,6 +127,7 @@ __name(_RateLimitError, "RateLimitError");
117
127
  var RateLimitError = _RateLimitError;
118
128
 
119
129
  // src/uploader.ts
130
+ var import_internal_slardar = require("@lark-apaas/internal-slardar");
120
131
  var _FileUploader = class _FileUploader {
121
132
  constructor(options) {
122
133
  __publicField(this, "options");
@@ -164,6 +175,11 @@ var _FileUploader = class _FileUploader {
164
175
  })
165
176
  });
166
177
  } catch (error) {
178
+ import_internal_slardar.slardar.captureException(error, {
179
+ source: "client-capability",
180
+ module: "file-upload",
181
+ step: "fetch-upload-url"
182
+ });
167
183
  throw new FileUploadError(`Failed to acquire upload URL: ${error instanceof Error ? error.message : String(error)}`);
168
184
  }
169
185
  if (!response.ok) {
@@ -186,6 +202,11 @@ var _FileUploader = class _FileUploader {
186
202
  body: file
187
203
  });
188
204
  } catch (error) {
205
+ import_internal_slardar.slardar.captureException(error, {
206
+ source: "client-capability",
207
+ module: "file-upload",
208
+ step: "upload-to-tos"
209
+ });
189
210
  throw new FileUploadError(`Failed to upload file to TOS: ${error instanceof Error ? error.message : String(error)}`);
190
211
  }
191
212
  if (!response.ok) {
@@ -211,6 +232,11 @@ var _FileUploader = class _FileUploader {
211
232
  })
212
233
  });
213
234
  } catch (error) {
235
+ import_internal_slardar.slardar.captureException(error, {
236
+ source: "client-capability",
237
+ module: "file-upload",
238
+ step: "fetch-download-url"
239
+ });
214
240
  throw new FileUploadError(`Failed to acquire download URL: ${error instanceof Error ? error.message : String(error)}`);
215
241
  }
216
242
  if (!response.ok) {
@@ -297,6 +323,8 @@ function replaceFilesWithUrls(params, results) {
297
323
  __name(replaceFilesWithUrls, "replaceFilesWithUrls");
298
324
 
299
325
  // src/client.ts
326
+ var import_internal_slardar2 = require("@lark-apaas/internal-slardar");
327
+ var import_virtual_capabilities = __toESM(require("virtual:capabilities"), 1);
300
328
  var LOG_PREFIX = "[CapabilityClient]";
301
329
  var defaultLogger = {
302
330
  debug: console.debug.bind(console),
@@ -310,17 +338,89 @@ var _CapabilityClient = class _CapabilityClient {
310
338
  __publicField(this, "baseURL");
311
339
  __publicField(this, "logger");
312
340
  __publicField(this, "onRateLimitError");
341
+ __publicField(this, "central");
313
342
  this.options = options;
314
343
  this.baseURL = (options?.baseURL ?? "").replace(/\/+$/, "");
315
344
  this.logger = options?.logger ?? defaultLogger;
316
345
  this.onRateLimitError = options?.onRateLimitError;
346
+ if (options?.central?.enabled) {
347
+ this.central = options.central;
348
+ }
349
+ }
350
+ /**
351
+ * central.baseURL → caller 显式给的(去尾斜杠)。
352
+ * 没传:浏览器取 `window.location.origin`(同源调用,SSO cookie 自动带);
353
+ * 非浏览器(SSR / Node.js 测试)抛错。
354
+ * 用法与 fullstack-plugin/packages/client/dataloom/src/service/index.ts 一致。
355
+ */
356
+ resolveCentralBaseURL(central) {
357
+ if (central.baseURL) return central.baseURL.replace(/\/+$/, "");
358
+ if (typeof window !== "undefined" && window.location?.origin) {
359
+ return window.location.origin;
360
+ }
361
+ throw new Error("CapabilityClient: central.baseURL is required when running outside browser (window.location unavailable)");
317
362
  }
318
363
  /**
319
- * 加载能力,返回执行器
320
- * @param capabilityId - 能力 ID
321
- * @returns 能力执行器
364
+ * 预览态 / 运行态自动判定。沿用 nestjs-capability/src/capability.module.ts:18 同款判定:
365
+ * - 沙箱 fullstack-cli scripts/dev.sh → `NODE_ENV=development` → 预览态
366
+ * - 运行态(npm start / build 后部署)→ `NODE_ENV=production` → 运行态
367
+ * Vite 在构建时把 `process.env.NODE_ENV` 替换为字面量字符串。
368
+ */
369
+ resolveMode() {
370
+ const env = typeof process !== "undefined" ? "development" : void 0;
371
+ return env === "development" ? "preview" : "runtime";
372
+ }
373
+ /**
374
+ * 构造单次调用的请求 URL:
375
+ * - central 开启:4 个中心端点之一(mode 自动判定);
376
+ * - central 关闭:保持原 `${baseURL}/api/capability/{id}` 行为。
377
+ */
378
+ buildExecuteUrl(capabilityId, stream) {
379
+ if (!this.central) {
380
+ return `${this.baseURL}/api/capability/${capabilityId}${stream ? "/stream" : ""}`;
381
+ }
382
+ const base = this.resolveCentralBaseURL(this.central);
383
+ const segment = this.resolveMode() === "preview" ? "/preview/execute" : "/execute";
384
+ return `${base}/app/${this.central.appId}/__runtime__/api/v1/plugin_server/capability/${capabilityId}${segment}${stream ? "/stream" : ""}`;
385
+ }
386
+ /**
387
+ * 构造请求体:
388
+ * - central + 预览态:`{ capability, action, params }`,capability 取自
389
+ * `virtual:capabilities`(load() 时已经 fail-fast 校验过,这里直接取)。
390
+ * - central + 运行态:`{ action, params }`(中心服务通过 RPC 查中心存储拿 config)。
391
+ * - 非 central:`{ action, params }`。
392
+ */
393
+ buildRequestBody(capabilityId, action, params) {
394
+ if (this.central && this.resolveMode() === "preview") {
395
+ const capability = import_virtual_capabilities.default[capabilityId];
396
+ if (!capability) {
397
+ throw new CapabilityNotFoundError(capabilityId);
398
+ }
399
+ return JSON.stringify({
400
+ capability,
401
+ action,
402
+ params
403
+ });
404
+ }
405
+ return JSON.stringify({
406
+ action,
407
+ params
408
+ });
409
+ }
410
+ /**
411
+ * 加载能力,返回执行器。
412
+ * 方案文档 § Client SDK 改造伪代码:
413
+ * const config = capabilityMap[id];
414
+ * if (!config) throw new CapabilityNotFoundError(id);
415
+ * central + 预览态时 fail-fast:本地 `virtual:capabilities` map 没这个 id 就直接抛,
416
+ * 不发任何网络请求。运行态 / 非 central 模式跳过此校验。
322
417
  */
323
418
  load(capabilityId) {
419
+ if (this.central && this.resolveMode() === "preview") {
420
+ if (!(capabilityId in import_virtual_capabilities.default)) {
421
+ throw new CapabilityNotFoundError(capabilityId);
422
+ }
423
+ }
324
424
  return this.createExecutor(capabilityId);
325
425
  }
326
426
  createExecutor(capabilityId) {
@@ -334,7 +434,7 @@ var _CapabilityClient = class _CapabilityClient {
334
434
  };
335
435
  }
336
436
  async executeCall(capabilityId, action, params) {
337
- const url = `${this.baseURL}/api/capability/${capabilityId}`;
437
+ const url = this.buildExecuteUrl(capabilityId, false);
338
438
  let requestParams = params ?? {};
339
439
  const extractedFiles = extractFiles(requestParams);
340
440
  if (extractedFiles.length > 0) {
@@ -355,10 +455,7 @@ var _CapabilityClient = class _CapabilityClient {
355
455
  "Content-Type": "application/json",
356
456
  ...this.options.fetchOptions?.headers
357
457
  },
358
- body: JSON.stringify({
359
- action,
360
- params: requestParams
361
- })
458
+ body: this.buildRequestBody(capabilityId, action, requestParams)
362
459
  });
363
460
  const data = await response.json();
364
461
  if (!response.ok || data.status_code !== ErrorCodes.SUCCESS) {
@@ -390,6 +487,12 @@ var _CapabilityClient = class _CapabilityClient {
390
487
  params: requestParams,
391
488
  error
392
489
  });
490
+ import_internal_slardar2.slardar.captureException(error, {
491
+ source: "client-capability",
492
+ module: "execute-call",
493
+ capabilityId,
494
+ action
495
+ });
393
496
  if (error instanceof CapabilityError) {
394
497
  throw error;
395
498
  }
@@ -397,7 +500,7 @@ var _CapabilityClient = class _CapabilityClient {
397
500
  }
398
501
  }
399
502
  async *executeCallStream(capabilityId, action, params) {
400
- const url = `${this.baseURL}/api/capability/${capabilityId}/stream`;
503
+ const url = this.buildExecuteUrl(capabilityId, true);
401
504
  let requestParams = params ?? {};
402
505
  const extractedFiles = extractFiles(requestParams);
403
506
  if (extractedFiles.length > 0) {
@@ -410,6 +513,7 @@ var _CapabilityClient = class _CapabilityClient {
410
513
  this.logger.info(LOG_PREFIX, `callStream start: capabilityId=${capabilityId}, action=${action}`, {
411
514
  params: requestParams
412
515
  });
516
+ const body = this.buildRequestBody(capabilityId, action, requestParams);
413
517
  let response;
414
518
  try {
415
519
  response = await fetch(url, {
@@ -419,16 +523,20 @@ var _CapabilityClient = class _CapabilityClient {
419
523
  "Content-Type": "application/json",
420
524
  ...this.options.fetchOptions?.headers
421
525
  },
422
- body: JSON.stringify({
423
- action,
424
- params: requestParams
425
- })
526
+ body
426
527
  });
427
528
  } catch (error) {
428
529
  this.logger.error(LOG_PREFIX, `callStream failed: capabilityId=${capabilityId}, action=${action}`, {
429
530
  params: requestParams,
430
531
  error
431
532
  });
533
+ import_internal_slardar2.slardar.captureException(error, {
534
+ source: "client-capability",
535
+ module: "execute-call-stream",
536
+ capabilityId,
537
+ action,
538
+ phase: "fetch"
539
+ });
432
540
  throw new NetworkError(error instanceof Error ? error.message : String(error));
433
541
  }
434
542
  if (!response.ok) {
@@ -528,6 +636,13 @@ var _CapabilityClient = class _CapabilityClient {
528
636
  error,
529
637
  chunkCount
530
638
  });
639
+ import_internal_slardar2.slardar.captureException(error, {
640
+ source: "client-capability",
641
+ module: "execute-call-stream",
642
+ capabilityId,
643
+ action,
644
+ phase: "read"
645
+ });
531
646
  if (error instanceof CapabilityError) {
532
647
  throw error;
533
648
  }
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/types.ts","../src/errors.ts","../src/uploader.ts","../src/file-extractor.ts","../src/client.ts"],"sourcesContent":["// 客户端\nexport { createClient, CapabilityClient } from './client';\n\n// 类型\nexport type { CapabilityClientOptions, CapabilityExecutor, Logger, RateLimitErrorHook } from './types';\n\n// 错误类型\nexport {\n CapabilityError,\n CapabilityNotFoundError,\n ActionNotFoundError,\n NetworkError,\n ExecutionError,\n FileUploadError,\n RateLimitError,\n} from './errors';\n","import type { RateLimitError } from './errors';\n\n/**\n * Logger 接口,兼容 console 和 @lark-apaas/toolkit 的 logger\n */\nexport interface Logger {\n debug(message: unknown, ...args: unknown[]): void;\n info(message: unknown, ...args: unknown[]): void;\n warn(message: unknown, ...args: unknown[]): void;\n error(message: unknown, ...args: unknown[]): void;\n}\n\n/**\n * RateLimitError 钩子函数类型\n * 当检测到计费受限错误时,在抛出异常前调用此钩子\n */\nexport type RateLimitErrorHook = (error: RateLimitError) => void | Promise<void>;\n\n/**\n * 客户端配置选项\n */\nexport interface CapabilityClientOptions {\n /** 全局路径前缀,默认 ''。例如线上环境可设置为 '/spark/a' */\n baseURL?: string;\n /** 获取文件上传预签名 URL 的接口地址,由上层注入 */\n acquireUploadUrl: string;\n /** 获取文件临时下载 URL 的接口地址,由上层注入 */\n acquireDownloadUrl: string;\n /** 自定义 fetch 配置 */\n fetchOptions?: RequestInit;\n /** 自定义 logger,默认使用 console */\n logger?: Logger;\n /**\n * 计费受限错误钩子\n * 当检测到 RateLimitError 时,在抛出异常前调用此钩子\n * 可用于上报埋点、展示 toast 等场景\n */\n onRateLimitError?: RateLimitErrorHook;\n}\n\n/**\n * 能力执行器接口\n */\nexport interface CapabilityExecutor {\n /**\n * 调用能力\n * @param action - Action 名称\n * @param params - 输入参数\n * @returns Action 执行结果\n */\n call<T = unknown>(\n action: string,\n params?: Record<string, unknown>\n ): Promise<T>;\n\n /**\n * 流式调用能力\n * @param action - Action 名称\n * @param params - 输入参数\n * @returns AsyncIterable,逐个 yield chunk\n */\n callStream<T = unknown>(\n action: string,\n params?: Record<string, unknown>\n ): AsyncIterable<T>;\n}\n\n// ========== API 响应类型 ==========\n\n/**\n * 成功响应\n */\nexport interface SuccessResponse<T> {\n status_code: '0';\n data: T;\n}\n\n/**\n * 错误响应\n */\nexport interface ErrorResponse {\n status_code: string;\n error_msg: string;\n /** 是否为计费受限错误 */\n is_rate_limit_error?: boolean;\n}\n\n/**\n * API 响应类型\n */\nexport type ApiResponse<T> = SuccessResponse<T> | ErrorResponse;\n\n// ========== 具体响应 data 结构 ==========\n\n/**\n * 执行接口响应 data 结构\n */\nexport interface ExecuteResponseData {\n output: unknown;\n}\n\n// ========== 流式响应结构 ==========\n\n/**\n * 流式内容响应\n */\nexport interface StreamContentResponse {\n status_code: '0';\n data: {\n type: 'content';\n delta: unknown;\n finished?: boolean;\n };\n}\n\n/**\n * 流式错误响应\n */\nexport interface StreamErrorResponse {\n status_code: '0';\n data: {\n type: 'error';\n error: {\n /** 错误码,计费受限时为业务错误码(如 k_st_ec_400002687) */\n code: string;\n message: string;\n /** 是否为计费受限错误 */\n isRateLimitError?: boolean;\n };\n };\n}\n\n/**\n * 流式响应类型\n */\nexport type StreamResponse = StreamContentResponse | StreamErrorResponse;\n\n// ========== 错误码 ==========\n\n/**\n * 后端错误码\n */\nexport const ErrorCodes = {\n SUCCESS: '0',\n CAPABILITY_NOT_FOUND: 'k_ec_cap_001',\n PLUGIN_NOT_FOUND: 'k_ec_cap_002',\n ACTION_NOT_FOUND: 'k_ec_cap_003',\n PARAMS_VALIDATION_ERROR: 'k_ec_cap_004',\n EXECUTION_ERROR: 'k_ec_cap_005',\n RATE_LIMIT_EXCEEDED: 'k_ec_cap_006',\n} as const;\n\n// ========== 文件上传相关类型 ==========\n\n/**\n * 提取的文件信息\n */\nexport interface ExtractedFile {\n /** 在 params 中的路径,如 ['image_list', 0] */\n path: (string | number)[];\n /** 文件对象 */\n file: File | Blob;\n}\n\n/**\n * 上传结果\n */\nexport interface UploadResult {\n /** 在 params 中的路径 */\n path: (string | number)[];\n /** 临时下载 URL */\n downloadUrl: string;\n}\n\n/**\n * 获取上传 URL 响应\n */\nexport interface AcquireUploadUrlResponse {\n status_code: string;\n data: {\n uploadURL: string;\n objectKey: string;\n };\n}\n\n/**\n * 获取下载 URL 响应\n */\nexport interface AcquireDownloadUrlResponse {\n status_code: string;\n data: {\n downloadURL: string;\n };\n}\n","/**\n * 能力调用错误基类\n */\nexport class CapabilityError extends Error {\n /** 错误码 */\n code: string;\n /** HTTP 状态码 */\n statusCode?: number;\n\n constructor(message: string, code: string, statusCode?: number) {\n super(message);\n this.name = 'CapabilityError';\n this.code = code;\n this.statusCode = statusCode;\n }\n}\n\n/**\n * 能力不存在错误\n */\nexport class CapabilityNotFoundError extends CapabilityError {\n constructor(capabilityId: string, statusCode?: number) {\n super(`Capability not found: ${capabilityId}`, 'CAPABILITY_NOT_FOUND', statusCode);\n this.name = 'CapabilityNotFoundError';\n }\n}\n\n/**\n * Action 不存在错误\n */\nexport class ActionNotFoundError extends CapabilityError {\n constructor(message: string, statusCode?: number) {\n super(message, 'ACTION_NOT_FOUND', statusCode);\n this.name = 'ActionNotFoundError';\n }\n}\n\n/**\n * 网络错误\n */\nexport class NetworkError extends CapabilityError {\n constructor(message: string) {\n super(message, 'NETWORK_ERROR');\n this.name = 'NetworkError';\n }\n}\n\n/**\n * 执行错误\n */\nexport class ExecutionError extends CapabilityError {\n constructor(message: string, statusCode?: number) {\n super(message, 'EXECUTION_ERROR', statusCode);\n this.name = 'ExecutionError';\n }\n}\n\n/**\n * 文件上传错误\n */\nexport class FileUploadError extends CapabilityError {\n constructor(message: string, statusCode?: number) {\n super(message, 'FILE_UPLOAD_ERROR', statusCode);\n this.name = 'FileUploadError';\n }\n}\n\n/**\n * 计费受限错误\n */\nexport class RateLimitError extends CapabilityError {\n /** 业务错误码 */\n rateLimitCode: string;\n /** 业务错误消息 */\n rateLimitMessage: string;\n\n constructor(rateLimitCode: string, rateLimitMessage: string, statusCode?: number) {\n super(rateLimitMessage, 'RATE_LIMIT_EXCEEDED', statusCode);\n this.name = 'RateLimitError';\n this.rateLimitCode = rateLimitCode;\n this.rateLimitMessage = rateLimitMessage;\n }\n}\n","import type {\n CapabilityClientOptions,\n ExtractedFile,\n UploadResult,\n AcquireUploadUrlResponse,\n AcquireDownloadUrlResponse,\n} from './types';\nimport { FileUploadError } from './errors';\n\ntype FileUploaderOptions = Pick<CapabilityClientOptions, 'acquireUploadUrl' | 'acquireDownloadUrl' | 'fetchOptions'>;\n\n/**\n * 文件上传器\n */\nexport class FileUploader {\n private readonly options: FileUploaderOptions;\n\n constructor(options: FileUploaderOptions) {\n this.options = options;\n }\n\n /**\n * 上传单个文件,返回下载 URL\n */\n async upload(file: File | Blob): Promise<string> {\n const fileName = file instanceof File ? file.name : `blob-${Date.now()}`;\n\n // 1. 获取上传预签名 URL\n const { uploadURL, objectKey } = await this.fetchUploadUrl(fileName);\n\n // 2. 上传文件到 TOS\n await this.uploadToTos(uploadURL, file);\n\n // 3. 获取下载 URL\n const downloadUrl = await this.fetchDownloadUrl(objectKey);\n\n return downloadUrl;\n }\n\n /**\n * 并发上传多个文件\n */\n async uploadAll(files: ExtractedFile[]): Promise<UploadResult[]> {\n const results = await Promise.all(\n files.map(async ({ path, file }) => {\n const downloadUrl = await this.upload(file);\n return { path, downloadUrl };\n })\n );\n return results;\n }\n\n /**\n * 获取上传预签名 URL\n */\n private async fetchUploadUrl(fileName: string): Promise<{ uploadURL: string; objectKey: string }> {\n const { acquireUploadUrl, fetchOptions } = this.options;\n\n let response: Response;\n try {\n response = await fetch(acquireUploadUrl, {\n method: 'POST',\n ...fetchOptions,\n headers: {\n 'Content-Type': 'application/json',\n ...fetchOptions?.headers,\n },\n body: JSON.stringify({ fileName }),\n });\n } catch (error) {\n throw new FileUploadError(\n `Failed to acquire upload URL: ${error instanceof Error ? error.message : String(error)}`\n );\n }\n\n if (!response.ok) {\n throw new FileUploadError(\n `Failed to acquire upload URL: HTTP ${response.status}`,\n response.status\n );\n }\n\n const data = (await response.json()) as AcquireUploadUrlResponse;\n\n if (data.status_code !== '0') {\n throw new FileUploadError(`Failed to acquire upload URL: ${data.status_code}`);\n }\n\n return data.data;\n }\n\n /**\n * 上传文件到 TOS(预签名 URL)\n */\n private async uploadToTos(uploadURL: string, file: File | Blob): Promise<void> {\n let response: Response;\n try {\n response = await fetch(uploadURL, {\n method: 'PUT',\n body: file,\n });\n } catch (error) {\n throw new FileUploadError(\n `Failed to upload file to TOS: ${error instanceof Error ? error.message : String(error)}`\n );\n }\n\n if (!response.ok) {\n throw new FileUploadError(\n `Failed to upload file to TOS: HTTP ${response.status}`,\n response.status\n );\n }\n }\n\n /**\n * 获取临时下载 URL\n */\n private async fetchDownloadUrl(objectKey: string): Promise<string> {\n const { acquireDownloadUrl, fetchOptions } = this.options;\n\n let response: Response;\n try {\n response = await fetch(acquireDownloadUrl, {\n method: 'POST',\n ...fetchOptions,\n headers: {\n 'Content-Type': 'application/json',\n ...fetchOptions?.headers,\n },\n body: JSON.stringify({ objectKey }),\n });\n } catch (error) {\n throw new FileUploadError(\n `Failed to acquire download URL: ${error instanceof Error ? error.message : String(error)}`\n );\n }\n\n if (!response.ok) {\n throw new FileUploadError(\n `Failed to acquire download URL: HTTP ${response.status}`,\n response.status\n );\n }\n\n const data = (await response.json()) as AcquireDownloadUrlResponse;\n\n if (data.status_code !== '0') {\n throw new FileUploadError(`Failed to acquire download URL: ${data.status_code}`);\n }\n\n return data.data.downloadURL;\n }\n}\n","import type { ExtractedFile, UploadResult } from './types';\n\n/**\n * 检测是否为 File 或 Blob\n */\nexport function isFile(value: unknown): value is File | Blob {\n return value instanceof File || value instanceof Blob;\n}\n\n/**\n * 递归提取 params 中的所有文件\n *\n * @example\n * extractFiles({ image_list: [file1, file2] })\n * // => [\n * // { path: ['image_list', 0], file: file1 },\n * // { path: ['image_list', 1], file: file2 }\n * // ]\n */\nexport function extractFiles(params: Record<string, unknown>): ExtractedFile[] {\n const files: ExtractedFile[] = [];\n\n function traverse(obj: unknown, path: (string | number)[]): void {\n if (isFile(obj)) {\n files.push({ path: [...path], file: obj });\n } else if (Array.isArray(obj)) {\n obj.forEach((item, index) => traverse(item, [...path, index]));\n } else if (obj !== null && typeof obj === 'object') {\n Object.entries(obj).forEach(([key, value]) => {\n traverse(value, [...path, key]);\n });\n }\n }\n\n traverse(params, []);\n return files;\n}\n\n/**\n * 根据上传结果替换 params 中的文件为 URL\n *\n * @example\n * replaceFilesWithUrls(\n * { image_list: [file1, file2] },\n * [\n * { path: ['image_list', 0], downloadUrl: 'https://...' },\n * { path: ['image_list', 1], downloadUrl: 'https://...' }\n * ]\n * )\n * // => { image_list: ['https://...', 'https://...'] }\n */\nexport function replaceFilesWithUrls(\n params: Record<string, unknown>,\n results: UploadResult[]\n): Record<string, unknown> {\n // 创建 path -> url 的映射,用于快速查找\n const urlMap = new Map<string, string>();\n for (const { path, downloadUrl } of results) {\n urlMap.set(JSON.stringify(path), downloadUrl);\n }\n\n // 手动深拷贝,同时将 File/Blob 替换为对应的 URL\n // 注意:不能使用 structuredClone,因为它不支持 File/Blob 对象\n function cloneAndReplace(obj: unknown, currentPath: (string | number)[]): unknown {\n // 检查当前路径是否有对应的 URL\n const pathKey = JSON.stringify(currentPath);\n if (urlMap.has(pathKey)) {\n return urlMap.get(pathKey);\n }\n\n // 如果是 File/Blob 但没有对应的 URL(不应该发生),返回 null\n if (isFile(obj)) {\n return null;\n }\n\n if (Array.isArray(obj)) {\n return obj.map((item, index) => cloneAndReplace(item, [...currentPath, index]));\n }\n\n if (obj !== null && typeof obj === 'object') {\n const result: Record<string, unknown> = {};\n for (const [key, value] of Object.entries(obj)) {\n result[key] = cloneAndReplace(value, [...currentPath, key]);\n }\n return result;\n }\n\n // 原始值直接返回\n return obj;\n }\n\n return cloneAndReplace(params, []) as Record<string, unknown>;\n}\n","import type {\n CapabilityClientOptions,\n CapabilityExecutor,\n ApiResponse,\n ExecuteResponseData,\n StreamResponse,\n Logger,\n RateLimitErrorHook,\n} from './types';\nimport { ErrorCodes } from './types';\nimport {\n CapabilityError,\n CapabilityNotFoundError,\n ActionNotFoundError,\n NetworkError,\n ExecutionError,\n RateLimitError,\n} from './errors';\nimport { FileUploader } from './uploader';\nimport { extractFiles, replaceFilesWithUrls } from './file-extractor';\n\nconst LOG_PREFIX = '[CapabilityClient]';\n\nconst defaultLogger: Logger = {\n debug: console.debug.bind(console),\n info: console.info.bind(console),\n warn: console.warn.bind(console),\n error: console.error.bind(console),\n};\n\n/**\n * 能力客户端\n */\nexport class CapabilityClient {\n private options: CapabilityClientOptions;\n private baseURL: string;\n private logger: Logger;\n private onRateLimitError?: RateLimitErrorHook;\n\n constructor(options: CapabilityClientOptions) {\n this.options = options;\n // 移除末尾的斜杠,确保拼接时格式正确\n this.baseURL = (options?.baseURL ?? '').replace(/\\/+$/, '');\n this.logger = options?.logger ?? defaultLogger;\n this.onRateLimitError = options?.onRateLimitError;\n }\n\n /**\n * 加载能力,返回执行器\n * @param capabilityId - 能力 ID\n * @returns 能力执行器\n */\n load(capabilityId: string): CapabilityExecutor {\n return this.createExecutor(capabilityId);\n }\n\n private createExecutor(capabilityId: string): CapabilityExecutor {\n return {\n call: <T = unknown>(action: string, params?: Record<string, unknown>) => {\n return this.executeCall<T>(capabilityId, action, params);\n },\n callStream: <T = unknown>(action: string, params?: Record<string, unknown>) => {\n return this.executeCallStream<T>(capabilityId, action, params);\n },\n };\n }\n\n private async executeCall<T>(\n capabilityId: string,\n action: string,\n params?: Record<string, unknown>\n ): Promise<T> {\n const url = `${this.baseURL}/api/capability/${capabilityId}`;\n let requestParams = params ?? {};\n\n // 处理文件上传\n const extractedFiles = extractFiles(requestParams);\n if (extractedFiles.length > 0) {\n this.logger.info(LOG_PREFIX, `uploading ${extractedFiles.length} file(s)...`);\n const uploader = new FileUploader(this.options);\n const uploadResults = await uploader.uploadAll(extractedFiles);\n requestParams = replaceFilesWithUrls(requestParams, uploadResults);\n this.logger.info(LOG_PREFIX, `file upload completed`);\n }\n\n this.logger.info(LOG_PREFIX, `call start: capabilityId=${capabilityId}, action=${action}`, { params: requestParams });\n\n try {\n const response = await fetch(url, {\n method: 'POST',\n ...this.options.fetchOptions,\n headers: {\n 'Content-Type': 'application/json',\n ...this.options.fetchOptions?.headers,\n },\n body: JSON.stringify({\n action,\n params: requestParams,\n }),\n });\n\n const data = (await response.json()) as ApiResponse<ExecuteResponseData>;\n\n if (!response.ok || data.status_code !== ErrorCodes.SUCCESS) {\n const errorResponse = data as {\n status_code: string;\n error_msg: string;\n is_rate_limit_error?: boolean;\n };\n const errorCode = errorResponse.status_code;\n\n // 计费受限错误:通过 is_rate_limit_error 字段识别\n if (errorResponse.is_rate_limit_error) {\n const rateLimitError = new RateLimitError(\n errorCode, // status_code 就是业务错误码\n errorResponse.error_msg,\n response.status,\n );\n await this.onRateLimitError?.(rateLimitError);\n throw rateLimitError;\n }\n\n switch (errorCode) {\n case ErrorCodes.CAPABILITY_NOT_FOUND:\n throw new CapabilityNotFoundError(capabilityId, response.status);\n case ErrorCodes.ACTION_NOT_FOUND:\n throw new ActionNotFoundError(errorResponse.error_msg, response.status);\n case ErrorCodes.PLUGIN_NOT_FOUND:\n case ErrorCodes.EXECUTION_ERROR:\n default:\n throw new ExecutionError(errorResponse.error_msg, response.status);\n }\n }\n\n const result = (data as { data: ExecuteResponseData }).data.output as T;\n\n this.logger.info(LOG_PREFIX, `call success: capabilityId=${capabilityId}, action=${action}`, { result });\n\n return result;\n } catch (error) {\n this.logger.error(LOG_PREFIX, `call failed: capabilityId=${capabilityId}, action=${action}`, { params: requestParams, error });\n if (error instanceof CapabilityError) {\n throw error;\n }\n throw new NetworkError(error instanceof Error ? error.message : String(error));\n }\n }\n\n private async *executeCallStream<T>(\n capabilityId: string,\n action: string,\n params?: Record<string, unknown>\n ): AsyncIterable<T> {\n const url = `${this.baseURL}/api/capability/${capabilityId}/stream`;\n let requestParams = params ?? {};\n\n // 处理文件上传\n const extractedFiles = extractFiles(requestParams);\n if (extractedFiles.length > 0) {\n this.logger.info(LOG_PREFIX, `uploading ${extractedFiles.length} file(s)...`);\n const uploader = new FileUploader(this.options);\n const uploadResults = await uploader.uploadAll(extractedFiles);\n requestParams = replaceFilesWithUrls(requestParams, uploadResults);\n this.logger.info(LOG_PREFIX, `file upload completed`);\n }\n\n this.logger.info(LOG_PREFIX, `callStream start: capabilityId=${capabilityId}, action=${action}`, { params: requestParams });\n\n let response: Response;\n try {\n response = await fetch(url, {\n method: 'POST',\n ...this.options.fetchOptions,\n headers: {\n 'Content-Type': 'application/json',\n ...this.options.fetchOptions?.headers,\n },\n body: JSON.stringify({\n action,\n params: requestParams,\n }),\n });\n } catch (error) {\n this.logger.error(LOG_PREFIX, `callStream failed: capabilityId=${capabilityId}, action=${action}`, { params: requestParams, error });\n throw new NetworkError(error instanceof Error ? error.message : String(error));\n }\n\n if (!response.ok) {\n const errMsg = `HTTP ${response.status} ${response.statusText}`;\n this.logger.error(LOG_PREFIX, `callStream failed: capabilityId=${capabilityId}, action=${action}`, { params: requestParams, error: errMsg });\n throw new NetworkError(errMsg);\n }\n\n if (!response.body) {\n const errMsg = 'Response body is null';\n this.logger.error(LOG_PREFIX, `callStream failed: capabilityId=${capabilityId}, action=${action}`, { params: requestParams, error: errMsg });\n throw new NetworkError(errMsg);\n }\n\n const reader = response.body.getReader();\n const decoder = new TextDecoder();\n let buffer = '';\n let chunkCount = 0;\n let aggregatedContent = '';\n let canAggregate = true;\n\n try {\n while (true) {\n const { done, value } = await reader.read();\n if (done) break;\n\n buffer += decoder.decode(value, { stream: true });\n const lines = buffer.split('\\n');\n buffer = lines.pop() ?? '';\n\n for (const line of lines) {\n if (line.startsWith('data: ')) {\n const data = line.slice(6);\n\n try {\n const parsed = JSON.parse(data) as StreamResponse;\n\n if (parsed.status_code === ErrorCodes.SUCCESS && parsed.data) {\n if (parsed.data.type === 'content') {\n chunkCount++;\n const delta = parsed.data.delta as T;\n\n // 尝试聚合 content 字段\n if (canAggregate) {\n const content = (delta as Record<string, unknown>)?.content;\n if (typeof content === 'string') {\n aggregatedContent += content;\n } else {\n canAggregate = false;\n }\n }\n\n yield delta;\n\n if (parsed.data.finished) {\n const resultInfo = canAggregate\n ? { chunkCount, result: aggregatedContent }\n : { chunkCount, resultLength: aggregatedContent.length || chunkCount };\n this.logger.info(LOG_PREFIX, `callStream end: capabilityId=${capabilityId}, action=${action}`, resultInfo);\n return;\n }\n } else if (parsed.data.type === 'error') {\n const err = parsed.data.error;\n if (err.isRateLimitError) {\n const rateLimitError = new RateLimitError(\n err.code, // code 就是业务错误码\n err.message,\n );\n await this.onRateLimitError?.(rateLimitError);\n throw rateLimitError;\n }\n throw new ExecutionError(err.message);\n }\n }\n } catch (parseError) {\n if (parseError instanceof CapabilityError) {\n this.logger.error(LOG_PREFIX, `callStream failed: capabilityId=${capabilityId}, action=${action}`, { params: requestParams, error: parseError, chunkCount });\n throw parseError;\n }\n // 忽略非 JSON 行\n }\n }\n }\n }\n const resultInfo = canAggregate\n ? { chunkCount, result: aggregatedContent }\n : { chunkCount, resultLength: aggregatedContent.length || chunkCount };\n this.logger.info(LOG_PREFIX, `callStream end: capabilityId=${capabilityId}, action=${action}`, resultInfo);\n } catch (error) {\n this.logger.error(LOG_PREFIX, `callStream failed: capabilityId=${capabilityId}, action=${action}`, { params: requestParams, error, chunkCount });\n if (error instanceof CapabilityError) {\n throw error;\n }\n throw new NetworkError(error instanceof Error ? error.message : String(error));\n } finally {\n reader.releaseLock();\n }\n }\n}\n\n/**\n * 创建客户端实例\n */\nexport function createClient(options: CapabilityClientOptions): CapabilityClient {\n return new CapabilityClient(options);\n}\n"],"mappings":"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","names":["ErrorCodes","SUCCESS","CAPABILITY_NOT_FOUND","PLUGIN_NOT_FOUND","ACTION_NOT_FOUND","PARAMS_VALIDATION_ERROR","EXECUTION_ERROR","RATE_LIMIT_EXCEEDED","CapabilityError","Error","message","code","statusCode","name","CapabilityNotFoundError","capabilityId","ActionNotFoundError","NetworkError","ExecutionError","FileUploadError","RateLimitError","rateLimitCode","rateLimitMessage","FileUploader","options","upload","file","fileName","File","name","Date","now","uploadURL","objectKey","fetchUploadUrl","uploadToTos","downloadUrl","fetchDownloadUrl","uploadAll","files","results","Promise","all","map","path","acquireUploadUrl","fetchOptions","response","fetch","method","headers","body","JSON","stringify","error","FileUploadError","Error","message","String","ok","status","data","json","status_code","acquireDownloadUrl","downloadURL","isFile","value","File","Blob","extractFiles","params","files","traverse","obj","path","push","file","Array","isArray","forEach","item","index","Object","entries","key","replaceFilesWithUrls","results","urlMap","Map","downloadUrl","set","JSON","stringify","cloneAndReplace","currentPath","pathKey","has","get","map","result","LOG_PREFIX","defaultLogger","debug","console","bind","info","warn","error","CapabilityClient","options","baseURL","logger","onRateLimitError","replace","load","capabilityId","createExecutor","call","action","params","executeCall","callStream","executeCallStream","url","requestParams","extractedFiles","extractFiles","length","uploader","FileUploader","uploadResults","uploadAll","replaceFilesWithUrls","response","fetch","method","fetchOptions","headers","body","JSON","stringify","data","json","ok","status_code","ErrorCodes","SUCCESS","errorResponse","errorCode","is_rate_limit_error","rateLimitError","RateLimitError","error_msg","status","CAPABILITY_NOT_FOUND","CapabilityNotFoundError","ACTION_NOT_FOUND","ActionNotFoundError","PLUGIN_NOT_FOUND","EXECUTION_ERROR","ExecutionError","result","output","CapabilityError","NetworkError","Error","message","String","errMsg","statusText","reader","getReader","decoder","TextDecoder","buffer","chunkCount","aggregatedContent","canAggregate","done","value","read","decode","stream","lines","split","pop","line","startsWith","slice","parsed","parse","type","delta","content","finished","resultInfo","resultLength","err","isRateLimitError","code","parseError","releaseLock","createClient"]}
1
+ {"version":3,"sources":["../src/index.ts","../src/types.ts","../src/errors.ts","../src/uploader.ts","../src/file-extractor.ts","../src/client.ts"],"sourcesContent":["// 客户端\nexport { createClient, CapabilityClient } from './client';\n\n// 类型\nexport type {\n CapabilityClientOptions,\n CapabilityExecutor,\n Logger,\n RateLimitErrorHook,\n CentralOptions,\n CapabilityConfig,\n} from './types';\n\n// 错误类型\nexport {\n CapabilityError,\n CapabilityNotFoundError,\n ActionNotFoundError,\n NetworkError,\n ExecutionError,\n FileUploadError,\n RateLimitError,\n} from './errors';\n","import type { RateLimitError } from './errors';\n\n/**\n * Logger 接口,兼容 console 和 @lark-apaas/toolkit 的 logger\n */\nexport interface Logger {\n debug(message: unknown, ...args: unknown[]): void;\n info(message: unknown, ...args: unknown[]): void;\n warn(message: unknown, ...args: unknown[]): void;\n error(message: unknown, ...args: unknown[]): void;\n}\n\n/**\n * RateLimitError 钩子函数类型\n * 当检测到计费受限错误时,在抛出异常前调用此钩子\n */\nexport type RateLimitErrorHook = (error: RateLimitError) => void | Promise<void>;\n\n/**\n * 客户端配置选项\n */\nexport interface CapabilityClientOptions {\n /** 全局路径前缀,默认 ''。例如线上环境可设置为 '/spark/a' */\n baseURL?: string;\n /** 获取文件上传预签名 URL 的接口地址,由上层注入 */\n acquireUploadUrl: string;\n /** 获取文件临时下载 URL 的接口地址,由上层注入 */\n acquireDownloadUrl: string;\n /** 自定义 fetch 配置 */\n fetchOptions?: RequestInit;\n /** 自定义 logger,默认使用 console */\n logger?: Logger;\n /**\n * 计费受限错误钩子\n * 当检测到 RateLimitError 时,在抛出异常前调用此钩子\n * 可用于上报埋点、展示 toast 等场景\n */\n onRateLimitError?: RateLimitErrorHook;\n /**\n * 中心化服务分流开关。启用后 capability 调用路由到妙搭 plugin_server 的 4 个中心端点:\n * - 运行态 非流式: POST {baseURL}/app/{appId}/__runtime__/api/v1/plugin_server/capability/{capabilityId}/execute\n * - 运行态 流式: POST {baseURL}/app/{appId}/__runtime__/api/v1/plugin_server/capability/{capabilityId}/execute/stream\n * - 预览态 非流式: POST {baseURL}/app/{appId}/__runtime__/api/v1/plugin_server/capability/{capabilityId}/preview/execute\n * - 预览态 流式: POST {baseURL}/app/{appId}/__runtime__/api/v1/plugin_server/capability/{capabilityId}/preview/execute/stream\n *\n * baseURL 默认取 `window.location.origin`(同源调用,SSO cookie 自动带)。\n * 预览态 / 运行态由 `process.env.NODE_ENV` 自动判定:\n * - 沙箱开发态(fullstack-cli scripts/dev.sh 起的进程,NODE_ENV=development)→ 预览态\n * - 运行态启动(NODE_ENV=production,无论 build 还是 npm start)→ 运行态\n * 业务前端 `createClient` 配置不需要区分两种环境。\n *\n * 预览态 body 必须携带完整 capability config,从 `capabilities` map 查;缺失抛\n * CapabilityNotFoundError。运行态由中心服务通过 capability_id 走 RPC 查中心存储。\n */\n central?: CentralOptions;\n}\n\n/**\n * 中心化服务分流配置。\n */\nexport interface CentralOptions {\n /** 是否启用中心化分流;false / 未配置则维持原行为打用户应用。 */\n enabled: boolean;\n /**\n * 中心服务 base URL(含 scheme)。可选;不传时默认 `window.location.origin`。\n * 非浏览器环境(SSR / Node.js 测试)必须显式传,否则 SDK 抛错。\n */\n baseURL?: string;\n /** 当前应用 ID,拼接在 `/app/{appId}/__runtime__/...` 路径段。 */\n appId: string;\n}\n\n/**\n * Capability 配置,与 @lark-apaas/nestjs-capability / miaoda_plugin_server 的 interfaces/capability-config.ts 对齐。\n * 预览态 body.capability 必须为完整对象。\n */\nexport interface CapabilityConfig {\n id: string;\n pluginKey: string;\n pluginVersion: string;\n name: string;\n description: string;\n paramsSchema: unknown;\n formValue: Record<string, unknown>;\n createdAt: number;\n updatedAt: number;\n}\n\n/**\n * 能力执行器接口\n */\nexport interface CapabilityExecutor {\n /**\n * 调用能力\n * @param action - Action 名称\n * @param params - 输入参数\n * @returns Action 执行结果\n */\n call<T = unknown>(\n action: string,\n params?: Record<string, unknown>\n ): Promise<T>;\n\n /**\n * 流式调用能力\n * @param action - Action 名称\n * @param params - 输入参数\n * @returns AsyncIterable,逐个 yield chunk\n */\n callStream<T = unknown>(\n action: string,\n params?: Record<string, unknown>\n ): AsyncIterable<T>;\n}\n\n// ========== API 响应类型 ==========\n\n/**\n * 成功响应\n */\nexport interface SuccessResponse<T> {\n status_code: '0';\n data: T;\n}\n\n/**\n * 错误响应\n */\nexport interface ErrorResponse {\n status_code: string;\n error_msg: string;\n /** 是否为计费受限错误 */\n is_rate_limit_error?: boolean;\n}\n\n/**\n * API 响应类型\n */\nexport type ApiResponse<T> = SuccessResponse<T> | ErrorResponse;\n\n// ========== 具体响应 data 结构 ==========\n\n/**\n * 执行接口响应 data 结构\n */\nexport interface ExecuteResponseData {\n output: unknown;\n}\n\n// ========== 流式响应结构 ==========\n\n/**\n * 流式内容响应\n */\nexport interface StreamContentResponse {\n status_code: '0';\n data: {\n type: 'content';\n delta: unknown;\n finished?: boolean;\n };\n}\n\n/**\n * 流式错误响应\n */\nexport interface StreamErrorResponse {\n status_code: '0';\n data: {\n type: 'error';\n error: {\n /** 错误码,计费受限时为业务错误码(如 k_st_ec_400002687) */\n code: string;\n message: string;\n /** 是否为计费受限错误 */\n isRateLimitError?: boolean;\n };\n };\n}\n\n/**\n * 流式响应类型\n */\nexport type StreamResponse = StreamContentResponse | StreamErrorResponse;\n\n// ========== 错误码 ==========\n\n/**\n * 后端错误码\n */\nexport const ErrorCodes = {\n SUCCESS: '0',\n CAPABILITY_NOT_FOUND: 'k_ec_cap_001',\n PLUGIN_NOT_FOUND: 'k_ec_cap_002',\n ACTION_NOT_FOUND: 'k_ec_cap_003',\n PARAMS_VALIDATION_ERROR: 'k_ec_cap_004',\n EXECUTION_ERROR: 'k_ec_cap_005',\n RATE_LIMIT_EXCEEDED: 'k_ec_cap_006',\n} as const;\n\n// ========== 文件上传相关类型 ==========\n\n/**\n * 提取的文件信息\n */\nexport interface ExtractedFile {\n /** 在 params 中的路径,如 ['image_list', 0] */\n path: (string | number)[];\n /** 文件对象 */\n file: File | Blob;\n}\n\n/**\n * 上传结果\n */\nexport interface UploadResult {\n /** 在 params 中的路径 */\n path: (string | number)[];\n /** 临时下载 URL */\n downloadUrl: string;\n}\n\n/**\n * 获取上传 URL 响应\n */\nexport interface AcquireUploadUrlResponse {\n status_code: string;\n data: {\n uploadURL: string;\n objectKey: string;\n };\n}\n\n/**\n * 获取下载 URL 响应\n */\nexport interface AcquireDownloadUrlResponse {\n status_code: string;\n data: {\n downloadURL: string;\n };\n}\n","/**\n * 能力调用错误基类\n */\nexport class CapabilityError extends Error {\n /** 错误码 */\n code: string;\n /** HTTP 状态码 */\n statusCode?: number;\n\n constructor(message: string, code: string, statusCode?: number) {\n super(message);\n this.name = 'CapabilityError';\n this.code = code;\n this.statusCode = statusCode;\n }\n}\n\n/**\n * 能力不存在错误\n */\nexport class CapabilityNotFoundError extends CapabilityError {\n constructor(capabilityId: string, statusCode?: number) {\n super(`Capability not found: ${capabilityId}`, 'CAPABILITY_NOT_FOUND', statusCode);\n this.name = 'CapabilityNotFoundError';\n }\n}\n\n/**\n * Action 不存在错误\n */\nexport class ActionNotFoundError extends CapabilityError {\n constructor(message: string, statusCode?: number) {\n super(message, 'ACTION_NOT_FOUND', statusCode);\n this.name = 'ActionNotFoundError';\n }\n}\n\n/**\n * 网络错误\n */\nexport class NetworkError extends CapabilityError {\n constructor(message: string) {\n super(message, 'NETWORK_ERROR');\n this.name = 'NetworkError';\n }\n}\n\n/**\n * 执行错误\n */\nexport class ExecutionError extends CapabilityError {\n constructor(message: string, statusCode?: number) {\n super(message, 'EXECUTION_ERROR', statusCode);\n this.name = 'ExecutionError';\n }\n}\n\n/**\n * 文件上传错误\n */\nexport class FileUploadError extends CapabilityError {\n constructor(message: string, statusCode?: number) {\n super(message, 'FILE_UPLOAD_ERROR', statusCode);\n this.name = 'FileUploadError';\n }\n}\n\n/**\n * 计费受限错误\n */\nexport class RateLimitError extends CapabilityError {\n /** 业务错误码 */\n rateLimitCode: string;\n /** 业务错误消息 */\n rateLimitMessage: string;\n\n constructor(rateLimitCode: string, rateLimitMessage: string, statusCode?: number) {\n super(rateLimitMessage, 'RATE_LIMIT_EXCEEDED', statusCode);\n this.name = 'RateLimitError';\n this.rateLimitCode = rateLimitCode;\n this.rateLimitMessage = rateLimitMessage;\n }\n}\n","import type {\n CapabilityClientOptions,\n ExtractedFile,\n UploadResult,\n AcquireUploadUrlResponse,\n AcquireDownloadUrlResponse,\n} from './types';\nimport { FileUploadError } from './errors';\nimport { slardar } from '@lark-apaas/internal-slardar';\n\ntype FileUploaderOptions = Pick<CapabilityClientOptions, 'acquireUploadUrl' | 'acquireDownloadUrl' | 'fetchOptions'>;\n\n/**\n * 文件上传器\n */\nexport class FileUploader {\n private readonly options: FileUploaderOptions;\n\n constructor(options: FileUploaderOptions) {\n this.options = options;\n }\n\n /**\n * 上传单个文件,返回下载 URL\n */\n async upload(file: File | Blob): Promise<string> {\n const fileName = file instanceof File ? file.name : `blob-${Date.now()}`;\n\n // 1. 获取上传预签名 URL\n const { uploadURL, objectKey } = await this.fetchUploadUrl(fileName);\n\n // 2. 上传文件到 TOS\n await this.uploadToTos(uploadURL, file);\n\n // 3. 获取下载 URL\n const downloadUrl = await this.fetchDownloadUrl(objectKey);\n\n return downloadUrl;\n }\n\n /**\n * 并发上传多个文件\n */\n async uploadAll(files: ExtractedFile[]): Promise<UploadResult[]> {\n const results = await Promise.all(\n files.map(async ({ path, file }) => {\n const downloadUrl = await this.upload(file);\n return { path, downloadUrl };\n })\n );\n return results;\n }\n\n /**\n * 获取上传预签名 URL\n */\n private async fetchUploadUrl(fileName: string): Promise<{ uploadURL: string; objectKey: string }> {\n const { acquireUploadUrl, fetchOptions } = this.options;\n\n let response: Response;\n try {\n response = await fetch(acquireUploadUrl, {\n method: 'POST',\n ...fetchOptions,\n headers: {\n 'Content-Type': 'application/json',\n ...fetchOptions?.headers,\n },\n body: JSON.stringify({ fileName }),\n });\n } catch (error) {\n slardar.captureException(error, { source: 'client-capability', module: 'file-upload', step: 'fetch-upload-url' });\n throw new FileUploadError(\n `Failed to acquire upload URL: ${error instanceof Error ? error.message : String(error)}`\n );\n }\n\n if (!response.ok) {\n throw new FileUploadError(\n `Failed to acquire upload URL: HTTP ${response.status}`,\n response.status\n );\n }\n\n const data = (await response.json()) as AcquireUploadUrlResponse;\n\n if (data.status_code !== '0') {\n throw new FileUploadError(`Failed to acquire upload URL: ${data.status_code}`);\n }\n\n return data.data;\n }\n\n /**\n * 上传文件到 TOS(预签名 URL)\n */\n private async uploadToTos(uploadURL: string, file: File | Blob): Promise<void> {\n let response: Response;\n try {\n response = await fetch(uploadURL, {\n method: 'PUT',\n body: file,\n });\n } catch (error) {\n slardar.captureException(error, { source: 'client-capability', module: 'file-upload', step: 'upload-to-tos' });\n throw new FileUploadError(\n `Failed to upload file to TOS: ${error instanceof Error ? error.message : String(error)}`\n );\n }\n\n if (!response.ok) {\n throw new FileUploadError(\n `Failed to upload file to TOS: HTTP ${response.status}`,\n response.status\n );\n }\n }\n\n /**\n * 获取临时下载 URL\n */\n private async fetchDownloadUrl(objectKey: string): Promise<string> {\n const { acquireDownloadUrl, fetchOptions } = this.options;\n\n let response: Response;\n try {\n response = await fetch(acquireDownloadUrl, {\n method: 'POST',\n ...fetchOptions,\n headers: {\n 'Content-Type': 'application/json',\n ...fetchOptions?.headers,\n },\n body: JSON.stringify({ objectKey }),\n });\n } catch (error) {\n slardar.captureException(error, { source: 'client-capability', module: 'file-upload', step: 'fetch-download-url' });\n throw new FileUploadError(\n `Failed to acquire download URL: ${error instanceof Error ? error.message : String(error)}`\n );\n }\n\n if (!response.ok) {\n throw new FileUploadError(\n `Failed to acquire download URL: HTTP ${response.status}`,\n response.status\n );\n }\n\n const data = (await response.json()) as AcquireDownloadUrlResponse;\n\n if (data.status_code !== '0') {\n throw new FileUploadError(`Failed to acquire download URL: ${data.status_code}`);\n }\n\n return data.data.downloadURL;\n }\n}\n","import type { ExtractedFile, UploadResult } from './types';\n\n/**\n * 检测是否为 File 或 Blob\n */\nexport function isFile(value: unknown): value is File | Blob {\n return value instanceof File || value instanceof Blob;\n}\n\n/**\n * 递归提取 params 中的所有文件\n *\n * @example\n * extractFiles({ image_list: [file1, file2] })\n * // => [\n * // { path: ['image_list', 0], file: file1 },\n * // { path: ['image_list', 1], file: file2 }\n * // ]\n */\nexport function extractFiles(params: Record<string, unknown>): ExtractedFile[] {\n const files: ExtractedFile[] = [];\n\n function traverse(obj: unknown, path: (string | number)[]): void {\n if (isFile(obj)) {\n files.push({ path: [...path], file: obj });\n } else if (Array.isArray(obj)) {\n obj.forEach((item, index) => traverse(item, [...path, index]));\n } else if (obj !== null && typeof obj === 'object') {\n Object.entries(obj).forEach(([key, value]) => {\n traverse(value, [...path, key]);\n });\n }\n }\n\n traverse(params, []);\n return files;\n}\n\n/**\n * 根据上传结果替换 params 中的文件为 URL\n *\n * @example\n * replaceFilesWithUrls(\n * { image_list: [file1, file2] },\n * [\n * { path: ['image_list', 0], downloadUrl: 'https://...' },\n * { path: ['image_list', 1], downloadUrl: 'https://...' }\n * ]\n * )\n * // => { image_list: ['https://...', 'https://...'] }\n */\nexport function replaceFilesWithUrls(\n params: Record<string, unknown>,\n results: UploadResult[]\n): Record<string, unknown> {\n // 创建 path -> url 的映射,用于快速查找\n const urlMap = new Map<string, string>();\n for (const { path, downloadUrl } of results) {\n urlMap.set(JSON.stringify(path), downloadUrl);\n }\n\n // 手动深拷贝,同时将 File/Blob 替换为对应的 URL\n // 注意:不能使用 structuredClone,因为它不支持 File/Blob 对象\n function cloneAndReplace(obj: unknown, currentPath: (string | number)[]): unknown {\n // 检查当前路径是否有对应的 URL\n const pathKey = JSON.stringify(currentPath);\n if (urlMap.has(pathKey)) {\n return urlMap.get(pathKey);\n }\n\n // 如果是 File/Blob 但没有对应的 URL(不应该发生),返回 null\n if (isFile(obj)) {\n return null;\n }\n\n if (Array.isArray(obj)) {\n return obj.map((item, index) => cloneAndReplace(item, [...currentPath, index]));\n }\n\n if (obj !== null && typeof obj === 'object') {\n const result: Record<string, unknown> = {};\n for (const [key, value] of Object.entries(obj)) {\n result[key] = cloneAndReplace(value, [...currentPath, key]);\n }\n return result;\n }\n\n // 原始值直接返回\n return obj;\n }\n\n return cloneAndReplace(params, []) as Record<string, unknown>;\n}\n","import type {\n CapabilityClientOptions,\n CapabilityExecutor,\n ApiResponse,\n ExecuteResponseData,\n StreamResponse,\n Logger,\n RateLimitErrorHook,\n CentralOptions,\n} from './types';\nimport { ErrorCodes } from './types';\nimport {\n CapabilityError,\n CapabilityNotFoundError,\n ActionNotFoundError,\n NetworkError,\n ExecutionError,\n RateLimitError,\n} from './errors';\nimport { FileUploader } from './uploader';\nimport { extractFiles, replaceFilesWithUrls } from './file-extractor';\nimport { slardar } from '@lark-apaas/internal-slardar';\n\n// 方案文档 § Client SDK 改造伪代码原句:\n// `import capabilityMap from 'virtual:capabilities'; // build 时注入`\n// 由 @lark-apaas/fullstack-vite-preset 的 capabilitiesBundlePlugin 在 dev/build 时\n// 扫业务工程 server/capabilities/*.json 注入。类型声明在 ./virtual.d.ts。\nimport virtualCapabilitiesMap from 'virtual:capabilities';\nimport type { CapabilityConfig } from './types';\n\nconst LOG_PREFIX = '[CapabilityClient]';\n\nconst defaultLogger: Logger = {\n debug: console.debug.bind(console),\n info: console.info.bind(console),\n warn: console.warn.bind(console),\n error: console.error.bind(console),\n};\n\n/**\n * 能力客户端\n */\nexport class CapabilityClient {\n private options: CapabilityClientOptions;\n private baseURL: string;\n private logger: Logger;\n private onRateLimitError?: RateLimitErrorHook;\n private central?: CentralOptions;\n\n constructor(options: CapabilityClientOptions) {\n this.options = options;\n // 移除末尾的斜杠,确保拼接时格式正确\n this.baseURL = (options?.baseURL ?? '').replace(/\\/+$/, '');\n this.logger = options?.logger ?? defaultLogger;\n this.onRateLimitError = options?.onRateLimitError;\n if (options?.central?.enabled) {\n this.central = options.central;\n }\n }\n\n /**\n * central.baseURL → caller 显式给的(去尾斜杠)。\n * 没传:浏览器取 `window.location.origin`(同源调用,SSO cookie 自动带);\n * 非浏览器(SSR / Node.js 测试)抛错。\n * 用法与 fullstack-plugin/packages/client/dataloom/src/service/index.ts 一致。\n */\n private resolveCentralBaseURL(central: CentralOptions): string {\n if (central.baseURL) return central.baseURL.replace(/\\/+$/, '');\n if (typeof window !== 'undefined' && window.location?.origin) {\n return window.location.origin;\n }\n throw new Error(\n 'CapabilityClient: central.baseURL is required when running outside browser (window.location unavailable)',\n );\n }\n\n /**\n * 预览态 / 运行态自动判定。沿用 nestjs-capability/src/capability.module.ts:18 同款判定:\n * - 沙箱 fullstack-cli scripts/dev.sh → `NODE_ENV=development` → 预览态\n * - 运行态(npm start / build 后部署)→ `NODE_ENV=production` → 运行态\n * Vite 在构建时把 `process.env.NODE_ENV` 替换为字面量字符串。\n */\n private resolveMode(): 'preview' | 'runtime' {\n const env = typeof process !== 'undefined' ? process.env?.NODE_ENV : undefined;\n return env === 'development' ? 'preview' : 'runtime';\n }\n\n /**\n * 构造单次调用的请求 URL:\n * - central 开启:4 个中心端点之一(mode 自动判定);\n * - central 关闭:保持原 `${baseURL}/api/capability/{id}` 行为。\n */\n private buildExecuteUrl(capabilityId: string, stream: boolean): string {\n if (!this.central) {\n return `${this.baseURL}/api/capability/${capabilityId}${stream ? '/stream' : ''}`;\n }\n const base = this.resolveCentralBaseURL(this.central);\n const segment = this.resolveMode() === 'preview' ? '/preview/execute' : '/execute';\n return `${base}/app/${this.central.appId}/__runtime__/api/v1/plugin_server/capability/${capabilityId}${segment}${stream ? '/stream' : ''}`;\n }\n\n /**\n * 构造请求体:\n * - central + 预览态:`{ capability, action, params }`,capability 取自\n * `virtual:capabilities`(load() 时已经 fail-fast 校验过,这里直接取)。\n * - central + 运行态:`{ action, params }`(中心服务通过 RPC 查中心存储拿 config)。\n * - 非 central:`{ action, params }`。\n */\n private buildRequestBody(\n capabilityId: string,\n action: string,\n params: Record<string, unknown>,\n ): string {\n if (this.central && this.resolveMode() === 'preview') {\n const capability = virtualCapabilitiesMap[capabilityId] as CapabilityConfig | undefined;\n if (!capability) {\n // 理论上 load() 已经 fail-fast 校验,但兜底处理\n throw new CapabilityNotFoundError(capabilityId);\n }\n return JSON.stringify({ capability, action, params });\n }\n return JSON.stringify({ action, params });\n }\n\n /**\n * 加载能力,返回执行器。\n * 方案文档 § Client SDK 改造伪代码:\n * const config = capabilityMap[id];\n * if (!config) throw new CapabilityNotFoundError(id);\n * central + 预览态时 fail-fast:本地 `virtual:capabilities` map 没这个 id 就直接抛,\n * 不发任何网络请求。运行态 / 非 central 模式跳过此校验。\n */\n load(capabilityId: string): CapabilityExecutor {\n if (this.central && this.resolveMode() === 'preview') {\n if (!(capabilityId in virtualCapabilitiesMap)) {\n throw new CapabilityNotFoundError(capabilityId);\n }\n }\n return this.createExecutor(capabilityId);\n }\n\n private createExecutor(capabilityId: string): CapabilityExecutor {\n return {\n call: <T = unknown>(action: string, params?: Record<string, unknown>) => {\n return this.executeCall<T>(capabilityId, action, params);\n },\n callStream: <T = unknown>(action: string, params?: Record<string, unknown>) => {\n return this.executeCallStream<T>(capabilityId, action, params);\n },\n };\n }\n\n private async executeCall<T>(\n capabilityId: string,\n action: string,\n params?: Record<string, unknown>\n ): Promise<T> {\n const url = this.buildExecuteUrl(capabilityId, false);\n let requestParams = params ?? {};\n\n // 处理文件上传\n const extractedFiles = extractFiles(requestParams);\n if (extractedFiles.length > 0) {\n this.logger.info(LOG_PREFIX, `uploading ${extractedFiles.length} file(s)...`);\n const uploader = new FileUploader(this.options);\n const uploadResults = await uploader.uploadAll(extractedFiles);\n requestParams = replaceFilesWithUrls(requestParams, uploadResults);\n this.logger.info(LOG_PREFIX, `file upload completed`);\n }\n\n this.logger.info(LOG_PREFIX, `call start: capabilityId=${capabilityId}, action=${action}`, { params: requestParams });\n\n try {\n const response = await fetch(url, {\n method: 'POST',\n ...this.options.fetchOptions,\n headers: {\n 'Content-Type': 'application/json',\n ...this.options.fetchOptions?.headers,\n },\n body: this.buildRequestBody(capabilityId, action, requestParams),\n });\n\n const data = (await response.json()) as ApiResponse<ExecuteResponseData>;\n\n if (!response.ok || data.status_code !== ErrorCodes.SUCCESS) {\n const errorResponse = data as {\n status_code: string;\n error_msg: string;\n is_rate_limit_error?: boolean;\n };\n const errorCode = errorResponse.status_code;\n\n // 计费受限错误:通过 is_rate_limit_error 字段识别\n if (errorResponse.is_rate_limit_error) {\n const rateLimitError = new RateLimitError(\n errorCode, // status_code 就是业务错误码\n errorResponse.error_msg,\n response.status,\n );\n await this.onRateLimitError?.(rateLimitError);\n throw rateLimitError;\n }\n\n switch (errorCode) {\n case ErrorCodes.CAPABILITY_NOT_FOUND:\n throw new CapabilityNotFoundError(capabilityId, response.status);\n case ErrorCodes.ACTION_NOT_FOUND:\n throw new ActionNotFoundError(errorResponse.error_msg, response.status);\n case ErrorCodes.PLUGIN_NOT_FOUND:\n case ErrorCodes.EXECUTION_ERROR:\n default:\n throw new ExecutionError(errorResponse.error_msg, response.status);\n }\n }\n\n const result = (data as { data: ExecuteResponseData }).data.output as T;\n\n this.logger.info(LOG_PREFIX, `call success: capabilityId=${capabilityId}, action=${action}`, { result });\n\n return result;\n } catch (error) {\n this.logger.error(LOG_PREFIX, `call failed: capabilityId=${capabilityId}, action=${action}`, { params: requestParams, error });\n slardar.captureException(error, { source: 'client-capability', module: 'execute-call', capabilityId, action });\n if (error instanceof CapabilityError) {\n throw error;\n }\n throw new NetworkError(error instanceof Error ? error.message : String(error));\n }\n }\n\n private async *executeCallStream<T>(\n capabilityId: string,\n action: string,\n params?: Record<string, unknown>\n ): AsyncIterable<T> {\n const url = this.buildExecuteUrl(capabilityId, true);\n let requestParams = params ?? {};\n\n // 处理文件上传\n const extractedFiles = extractFiles(requestParams);\n if (extractedFiles.length > 0) {\n this.logger.info(LOG_PREFIX, `uploading ${extractedFiles.length} file(s)...`);\n const uploader = new FileUploader(this.options);\n const uploadResults = await uploader.uploadAll(extractedFiles);\n requestParams = replaceFilesWithUrls(requestParams, uploadResults);\n this.logger.info(LOG_PREFIX, `file upload completed`);\n }\n\n this.logger.info(LOG_PREFIX, `callStream start: capabilityId=${capabilityId}, action=${action}`, { params: requestParams });\n\n // central + preview 模式下 buildRequestBody 可能抛 CapabilityNotFoundError,\n // 先于 fetch 计算好 body,避免被下面的 NetworkError 包装吞掉类型。\n const body = this.buildRequestBody(capabilityId, action, requestParams);\n\n let response: Response;\n try {\n response = await fetch(url, {\n method: 'POST',\n ...this.options.fetchOptions,\n headers: {\n 'Content-Type': 'application/json',\n ...this.options.fetchOptions?.headers,\n },\n body,\n });\n } catch (error) {\n this.logger.error(LOG_PREFIX, `callStream failed: capabilityId=${capabilityId}, action=${action}`, { params: requestParams, error });\n slardar.captureException(error, { source: 'client-capability', module: 'execute-call-stream', capabilityId, action, phase: 'fetch' });\n throw new NetworkError(error instanceof Error ? error.message : String(error));\n }\n\n if (!response.ok) {\n const errMsg = `HTTP ${response.status} ${response.statusText}`;\n this.logger.error(LOG_PREFIX, `callStream failed: capabilityId=${capabilityId}, action=${action}`, { params: requestParams, error: errMsg });\n throw new NetworkError(errMsg);\n }\n\n if (!response.body) {\n const errMsg = 'Response body is null';\n this.logger.error(LOG_PREFIX, `callStream failed: capabilityId=${capabilityId}, action=${action}`, { params: requestParams, error: errMsg });\n throw new NetworkError(errMsg);\n }\n\n const reader = response.body.getReader();\n const decoder = new TextDecoder();\n let buffer = '';\n let chunkCount = 0;\n let aggregatedContent = '';\n let canAggregate = true;\n\n try {\n while (true) {\n const { done, value } = await reader.read();\n if (done) break;\n\n buffer += decoder.decode(value, { stream: true });\n const lines = buffer.split('\\n');\n buffer = lines.pop() ?? '';\n\n for (const line of lines) {\n if (line.startsWith('data: ')) {\n const data = line.slice(6);\n\n try {\n const parsed = JSON.parse(data) as StreamResponse;\n\n if (parsed.status_code === ErrorCodes.SUCCESS && parsed.data) {\n if (parsed.data.type === 'content') {\n chunkCount++;\n const delta = parsed.data.delta as T;\n\n // 尝试聚合 content 字段\n if (canAggregate) {\n const content = (delta as Record<string, unknown>)?.content;\n if (typeof content === 'string') {\n aggregatedContent += content;\n } else {\n canAggregate = false;\n }\n }\n\n yield delta;\n\n if (parsed.data.finished) {\n const resultInfo = canAggregate\n ? { chunkCount, result: aggregatedContent }\n : { chunkCount, resultLength: aggregatedContent.length || chunkCount };\n this.logger.info(LOG_PREFIX, `callStream end: capabilityId=${capabilityId}, action=${action}`, resultInfo);\n return;\n }\n } else if (parsed.data.type === 'error') {\n const err = parsed.data.error;\n if (err.isRateLimitError) {\n const rateLimitError = new RateLimitError(\n err.code, // code 就是业务错误码\n err.message,\n );\n await this.onRateLimitError?.(rateLimitError);\n throw rateLimitError;\n }\n throw new ExecutionError(err.message);\n }\n }\n } catch (parseError) {\n if (parseError instanceof CapabilityError) {\n this.logger.error(LOG_PREFIX, `callStream failed: capabilityId=${capabilityId}, action=${action}`, { params: requestParams, error: parseError, chunkCount });\n throw parseError;\n }\n // 忽略非 JSON 行\n }\n }\n }\n }\n const resultInfo = canAggregate\n ? { chunkCount, result: aggregatedContent }\n : { chunkCount, resultLength: aggregatedContent.length || chunkCount };\n this.logger.info(LOG_PREFIX, `callStream end: capabilityId=${capabilityId}, action=${action}`, resultInfo);\n } catch (error) {\n this.logger.error(LOG_PREFIX, `callStream failed: capabilityId=${capabilityId}, action=${action}`, { params: requestParams, error, chunkCount });\n slardar.captureException(error, { source: 'client-capability', module: 'execute-call-stream', capabilityId, action, phase: 'read' });\n if (error instanceof CapabilityError) {\n throw error;\n }\n throw new NetworkError(error instanceof Error ? error.message : String(error));\n } finally {\n reader.releaseLock();\n }\n }\n}\n\n/**\n * 创建客户端实例\n */\nexport function createClient(options: CapabilityClientOptions): CapabilityClient {\n return new CapabilityClient(options);\n}\n"],"mappings":"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","names":["ErrorCodes","SUCCESS","CAPABILITY_NOT_FOUND","PLUGIN_NOT_FOUND","ACTION_NOT_FOUND","PARAMS_VALIDATION_ERROR","EXECUTION_ERROR","RATE_LIMIT_EXCEEDED","CapabilityError","Error","message","code","statusCode","name","CapabilityNotFoundError","capabilityId","ActionNotFoundError","NetworkError","ExecutionError","FileUploadError","RateLimitError","rateLimitCode","rateLimitMessage","FileUploader","options","upload","file","fileName","File","name","Date","now","uploadURL","objectKey","fetchUploadUrl","uploadToTos","downloadUrl","fetchDownloadUrl","uploadAll","files","results","Promise","all","map","path","acquireUploadUrl","fetchOptions","response","fetch","method","headers","body","JSON","stringify","error","slardar","captureException","source","module","step","FileUploadError","Error","message","String","ok","status","data","json","status_code","acquireDownloadUrl","downloadURL","isFile","value","File","Blob","extractFiles","params","files","traverse","obj","path","push","file","Array","isArray","forEach","item","index","Object","entries","key","replaceFilesWithUrls","results","urlMap","Map","downloadUrl","set","JSON","stringify","cloneAndReplace","currentPath","pathKey","has","get","map","result","import_internal_slardar","LOG_PREFIX","defaultLogger","debug","console","bind","info","warn","error","CapabilityClient","options","baseURL","logger","onRateLimitError","central","replace","enabled","resolveCentralBaseURL","window","location","origin","Error","resolveMode","env","process","undefined","buildExecuteUrl","capabilityId","stream","base","segment","appId","buildRequestBody","action","params","capability","virtualCapabilitiesMap","CapabilityNotFoundError","JSON","stringify","load","createExecutor","call","executeCall","callStream","executeCallStream","url","requestParams","extractedFiles","extractFiles","length","uploader","FileUploader","uploadResults","uploadAll","replaceFilesWithUrls","response","fetch","method","fetchOptions","headers","body","data","json","ok","status_code","ErrorCodes","SUCCESS","errorResponse","errorCode","is_rate_limit_error","rateLimitError","RateLimitError","error_msg","status","CAPABILITY_NOT_FOUND","ACTION_NOT_FOUND","ActionNotFoundError","PLUGIN_NOT_FOUND","EXECUTION_ERROR","ExecutionError","result","output","slardar","captureException","source","module","CapabilityError","NetworkError","message","String","phase","errMsg","statusText","reader","getReader","decoder","TextDecoder","buffer","chunkCount","aggregatedContent","canAggregate","done","value","read","decode","lines","split","pop","line","startsWith","slice","parsed","parse","type","delta","content","finished","resultInfo","resultLength","err","isRateLimitError","code","parseError","releaseLock","createClient"]}
package/dist/index.d.cts CHANGED
@@ -83,6 +83,52 @@ interface CapabilityClientOptions {
83
83
  * 可用于上报埋点、展示 toast 等场景
84
84
  */
85
85
  onRateLimitError?: RateLimitErrorHook;
86
+ /**
87
+ * 中心化服务分流开关。启用后 capability 调用路由到妙搭 plugin_server 的 4 个中心端点:
88
+ * - 运行态 非流式: POST {baseURL}/app/{appId}/__runtime__/api/v1/plugin_server/capability/{capabilityId}/execute
89
+ * - 运行态 流式: POST {baseURL}/app/{appId}/__runtime__/api/v1/plugin_server/capability/{capabilityId}/execute/stream
90
+ * - 预览态 非流式: POST {baseURL}/app/{appId}/__runtime__/api/v1/plugin_server/capability/{capabilityId}/preview/execute
91
+ * - 预览态 流式: POST {baseURL}/app/{appId}/__runtime__/api/v1/plugin_server/capability/{capabilityId}/preview/execute/stream
92
+ *
93
+ * baseURL 默认取 `window.location.origin`(同源调用,SSO cookie 自动带)。
94
+ * 预览态 / 运行态由 `process.env.NODE_ENV` 自动判定:
95
+ * - 沙箱开发态(fullstack-cli scripts/dev.sh 起的进程,NODE_ENV=development)→ 预览态
96
+ * - 运行态启动(NODE_ENV=production,无论 build 还是 npm start)→ 运行态
97
+ * 业务前端 `createClient` 配置不需要区分两种环境。
98
+ *
99
+ * 预览态 body 必须携带完整 capability config,从 `capabilities` map 查;缺失抛
100
+ * CapabilityNotFoundError。运行态由中心服务通过 capability_id 走 RPC 查中心存储。
101
+ */
102
+ central?: CentralOptions;
103
+ }
104
+ /**
105
+ * 中心化服务分流配置。
106
+ */
107
+ interface CentralOptions {
108
+ /** 是否启用中心化分流;false / 未配置则维持原行为打用户应用。 */
109
+ enabled: boolean;
110
+ /**
111
+ * 中心服务 base URL(含 scheme)。可选;不传时默认 `window.location.origin`。
112
+ * 非浏览器环境(SSR / Node.js 测试)必须显式传,否则 SDK 抛错。
113
+ */
114
+ baseURL?: string;
115
+ /** 当前应用 ID,拼接在 `/app/{appId}/__runtime__/...` 路径段。 */
116
+ appId: string;
117
+ }
118
+ /**
119
+ * Capability 配置,与 @lark-apaas/nestjs-capability / miaoda_plugin_server 的 interfaces/capability-config.ts 对齐。
120
+ * 预览态 body.capability 必须为完整对象。
121
+ */
122
+ interface CapabilityConfig {
123
+ id: string;
124
+ pluginKey: string;
125
+ pluginVersion: string;
126
+ name: string;
127
+ description: string;
128
+ paramsSchema: unknown;
129
+ formValue: Record<string, unknown>;
130
+ createdAt: number;
131
+ updatedAt: number;
86
132
  }
87
133
  /**
88
134
  * 能力执行器接口
@@ -112,11 +158,43 @@ declare class CapabilityClient {
112
158
  private baseURL;
113
159
  private logger;
114
160
  private onRateLimitError?;
161
+ private central?;
115
162
  constructor(options: CapabilityClientOptions);
116
163
  /**
117
- * 加载能力,返回执行器
118
- * @param capabilityId - 能力 ID
119
- * @returns 能力执行器
164
+ * central.baseURL → caller 显式给的(去尾斜杠)。
165
+ * 没传:浏览器取 `window.location.origin`(同源调用,SSO cookie 自动带);
166
+ * 非浏览器(SSR / Node.js 测试)抛错。
167
+ * 用法与 fullstack-plugin/packages/client/dataloom/src/service/index.ts 一致。
168
+ */
169
+ private resolveCentralBaseURL;
170
+ /**
171
+ * 预览态 / 运行态自动判定。沿用 nestjs-capability/src/capability.module.ts:18 同款判定:
172
+ * - 沙箱 fullstack-cli scripts/dev.sh → `NODE_ENV=development` → 预览态
173
+ * - 运行态(npm start / build 后部署)→ `NODE_ENV=production` → 运行态
174
+ * Vite 在构建时把 `process.env.NODE_ENV` 替换为字面量字符串。
175
+ */
176
+ private resolveMode;
177
+ /**
178
+ * 构造单次调用的请求 URL:
179
+ * - central 开启:4 个中心端点之一(mode 自动判定);
180
+ * - central 关闭:保持原 `${baseURL}/api/capability/{id}` 行为。
181
+ */
182
+ private buildExecuteUrl;
183
+ /**
184
+ * 构造请求体:
185
+ * - central + 预览态:`{ capability, action, params }`,capability 取自
186
+ * `virtual:capabilities`(load() 时已经 fail-fast 校验过,这里直接取)。
187
+ * - central + 运行态:`{ action, params }`(中心服务通过 RPC 查中心存储拿 config)。
188
+ * - 非 central:`{ action, params }`。
189
+ */
190
+ private buildRequestBody;
191
+ /**
192
+ * 加载能力,返回执行器。
193
+ * 方案文档 § Client SDK 改造伪代码:
194
+ * const config = capabilityMap[id];
195
+ * if (!config) throw new CapabilityNotFoundError(id);
196
+ * central + 预览态时 fail-fast:本地 `virtual:capabilities` map 没这个 id 就直接抛,
197
+ * 不发任何网络请求。运行态 / 非 central 模式跳过此校验。
120
198
  */
121
199
  load(capabilityId: string): CapabilityExecutor;
122
200
  private createExecutor;
@@ -128,4 +206,4 @@ declare class CapabilityClient {
128
206
  */
129
207
  declare function createClient(options: CapabilityClientOptions): CapabilityClient;
130
208
 
131
- export { ActionNotFoundError, CapabilityClient, type CapabilityClientOptions, CapabilityError, type CapabilityExecutor, CapabilityNotFoundError, ExecutionError, FileUploadError, type Logger, NetworkError, RateLimitError, type RateLimitErrorHook, createClient };
209
+ export { ActionNotFoundError, CapabilityClient, type CapabilityClientOptions, type CapabilityConfig, CapabilityError, type CapabilityExecutor, CapabilityNotFoundError, type CentralOptions, ExecutionError, FileUploadError, type Logger, NetworkError, RateLimitError, type RateLimitErrorHook, createClient };
package/dist/index.d.ts CHANGED
@@ -83,6 +83,52 @@ interface CapabilityClientOptions {
83
83
  * 可用于上报埋点、展示 toast 等场景
84
84
  */
85
85
  onRateLimitError?: RateLimitErrorHook;
86
+ /**
87
+ * 中心化服务分流开关。启用后 capability 调用路由到妙搭 plugin_server 的 4 个中心端点:
88
+ * - 运行态 非流式: POST {baseURL}/app/{appId}/__runtime__/api/v1/plugin_server/capability/{capabilityId}/execute
89
+ * - 运行态 流式: POST {baseURL}/app/{appId}/__runtime__/api/v1/plugin_server/capability/{capabilityId}/execute/stream
90
+ * - 预览态 非流式: POST {baseURL}/app/{appId}/__runtime__/api/v1/plugin_server/capability/{capabilityId}/preview/execute
91
+ * - 预览态 流式: POST {baseURL}/app/{appId}/__runtime__/api/v1/plugin_server/capability/{capabilityId}/preview/execute/stream
92
+ *
93
+ * baseURL 默认取 `window.location.origin`(同源调用,SSO cookie 自动带)。
94
+ * 预览态 / 运行态由 `process.env.NODE_ENV` 自动判定:
95
+ * - 沙箱开发态(fullstack-cli scripts/dev.sh 起的进程,NODE_ENV=development)→ 预览态
96
+ * - 运行态启动(NODE_ENV=production,无论 build 还是 npm start)→ 运行态
97
+ * 业务前端 `createClient` 配置不需要区分两种环境。
98
+ *
99
+ * 预览态 body 必须携带完整 capability config,从 `capabilities` map 查;缺失抛
100
+ * CapabilityNotFoundError。运行态由中心服务通过 capability_id 走 RPC 查中心存储。
101
+ */
102
+ central?: CentralOptions;
103
+ }
104
+ /**
105
+ * 中心化服务分流配置。
106
+ */
107
+ interface CentralOptions {
108
+ /** 是否启用中心化分流;false / 未配置则维持原行为打用户应用。 */
109
+ enabled: boolean;
110
+ /**
111
+ * 中心服务 base URL(含 scheme)。可选;不传时默认 `window.location.origin`。
112
+ * 非浏览器环境(SSR / Node.js 测试)必须显式传,否则 SDK 抛错。
113
+ */
114
+ baseURL?: string;
115
+ /** 当前应用 ID,拼接在 `/app/{appId}/__runtime__/...` 路径段。 */
116
+ appId: string;
117
+ }
118
+ /**
119
+ * Capability 配置,与 @lark-apaas/nestjs-capability / miaoda_plugin_server 的 interfaces/capability-config.ts 对齐。
120
+ * 预览态 body.capability 必须为完整对象。
121
+ */
122
+ interface CapabilityConfig {
123
+ id: string;
124
+ pluginKey: string;
125
+ pluginVersion: string;
126
+ name: string;
127
+ description: string;
128
+ paramsSchema: unknown;
129
+ formValue: Record<string, unknown>;
130
+ createdAt: number;
131
+ updatedAt: number;
86
132
  }
87
133
  /**
88
134
  * 能力执行器接口
@@ -112,11 +158,43 @@ declare class CapabilityClient {
112
158
  private baseURL;
113
159
  private logger;
114
160
  private onRateLimitError?;
161
+ private central?;
115
162
  constructor(options: CapabilityClientOptions);
116
163
  /**
117
- * 加载能力,返回执行器
118
- * @param capabilityId - 能力 ID
119
- * @returns 能力执行器
164
+ * central.baseURL → caller 显式给的(去尾斜杠)。
165
+ * 没传:浏览器取 `window.location.origin`(同源调用,SSO cookie 自动带);
166
+ * 非浏览器(SSR / Node.js 测试)抛错。
167
+ * 用法与 fullstack-plugin/packages/client/dataloom/src/service/index.ts 一致。
168
+ */
169
+ private resolveCentralBaseURL;
170
+ /**
171
+ * 预览态 / 运行态自动判定。沿用 nestjs-capability/src/capability.module.ts:18 同款判定:
172
+ * - 沙箱 fullstack-cli scripts/dev.sh → `NODE_ENV=development` → 预览态
173
+ * - 运行态(npm start / build 后部署)→ `NODE_ENV=production` → 运行态
174
+ * Vite 在构建时把 `process.env.NODE_ENV` 替换为字面量字符串。
175
+ */
176
+ private resolveMode;
177
+ /**
178
+ * 构造单次调用的请求 URL:
179
+ * - central 开启:4 个中心端点之一(mode 自动判定);
180
+ * - central 关闭:保持原 `${baseURL}/api/capability/{id}` 行为。
181
+ */
182
+ private buildExecuteUrl;
183
+ /**
184
+ * 构造请求体:
185
+ * - central + 预览态:`{ capability, action, params }`,capability 取自
186
+ * `virtual:capabilities`(load() 时已经 fail-fast 校验过,这里直接取)。
187
+ * - central + 运行态:`{ action, params }`(中心服务通过 RPC 查中心存储拿 config)。
188
+ * - 非 central:`{ action, params }`。
189
+ */
190
+ private buildRequestBody;
191
+ /**
192
+ * 加载能力,返回执行器。
193
+ * 方案文档 § Client SDK 改造伪代码:
194
+ * const config = capabilityMap[id];
195
+ * if (!config) throw new CapabilityNotFoundError(id);
196
+ * central + 预览态时 fail-fast:本地 `virtual:capabilities` map 没这个 id 就直接抛,
197
+ * 不发任何网络请求。运行态 / 非 central 模式跳过此校验。
120
198
  */
121
199
  load(capabilityId: string): CapabilityExecutor;
122
200
  private createExecutor;
@@ -128,4 +206,4 @@ declare class CapabilityClient {
128
206
  */
129
207
  declare function createClient(options: CapabilityClientOptions): CapabilityClient;
130
208
 
131
- export { ActionNotFoundError, CapabilityClient, type CapabilityClientOptions, CapabilityError, type CapabilityExecutor, CapabilityNotFoundError, ExecutionError, FileUploadError, type Logger, NetworkError, RateLimitError, type RateLimitErrorHook, createClient };
209
+ export { ActionNotFoundError, CapabilityClient, type CapabilityClientOptions, type CapabilityConfig, CapabilityError, type CapabilityExecutor, CapabilityNotFoundError, type CentralOptions, ExecutionError, FileUploadError, type Logger, NetworkError, RateLimitError, type RateLimitErrorHook, createClient };
package/dist/index.js CHANGED
@@ -85,6 +85,7 @@ __name(_RateLimitError, "RateLimitError");
85
85
  var RateLimitError = _RateLimitError;
86
86
 
87
87
  // src/uploader.ts
88
+ import { slardar } from "@lark-apaas/internal-slardar";
88
89
  var _FileUploader = class _FileUploader {
89
90
  constructor(options) {
90
91
  __publicField(this, "options");
@@ -132,6 +133,11 @@ var _FileUploader = class _FileUploader {
132
133
  })
133
134
  });
134
135
  } catch (error) {
136
+ slardar.captureException(error, {
137
+ source: "client-capability",
138
+ module: "file-upload",
139
+ step: "fetch-upload-url"
140
+ });
135
141
  throw new FileUploadError(`Failed to acquire upload URL: ${error instanceof Error ? error.message : String(error)}`);
136
142
  }
137
143
  if (!response.ok) {
@@ -154,6 +160,11 @@ var _FileUploader = class _FileUploader {
154
160
  body: file
155
161
  });
156
162
  } catch (error) {
163
+ slardar.captureException(error, {
164
+ source: "client-capability",
165
+ module: "file-upload",
166
+ step: "upload-to-tos"
167
+ });
157
168
  throw new FileUploadError(`Failed to upload file to TOS: ${error instanceof Error ? error.message : String(error)}`);
158
169
  }
159
170
  if (!response.ok) {
@@ -179,6 +190,11 @@ var _FileUploader = class _FileUploader {
179
190
  })
180
191
  });
181
192
  } catch (error) {
193
+ slardar.captureException(error, {
194
+ source: "client-capability",
195
+ module: "file-upload",
196
+ step: "fetch-download-url"
197
+ });
182
198
  throw new FileUploadError(`Failed to acquire download URL: ${error instanceof Error ? error.message : String(error)}`);
183
199
  }
184
200
  if (!response.ok) {
@@ -265,6 +281,8 @@ function replaceFilesWithUrls(params, results) {
265
281
  __name(replaceFilesWithUrls, "replaceFilesWithUrls");
266
282
 
267
283
  // src/client.ts
284
+ import { slardar as slardar2 } from "@lark-apaas/internal-slardar";
285
+ import virtualCapabilitiesMap from "virtual:capabilities";
268
286
  var LOG_PREFIX = "[CapabilityClient]";
269
287
  var defaultLogger = {
270
288
  debug: console.debug.bind(console),
@@ -278,17 +296,89 @@ var _CapabilityClient = class _CapabilityClient {
278
296
  __publicField(this, "baseURL");
279
297
  __publicField(this, "logger");
280
298
  __publicField(this, "onRateLimitError");
299
+ __publicField(this, "central");
281
300
  this.options = options;
282
301
  this.baseURL = (options?.baseURL ?? "").replace(/\/+$/, "");
283
302
  this.logger = options?.logger ?? defaultLogger;
284
303
  this.onRateLimitError = options?.onRateLimitError;
304
+ if (options?.central?.enabled) {
305
+ this.central = options.central;
306
+ }
307
+ }
308
+ /**
309
+ * central.baseURL → caller 显式给的(去尾斜杠)。
310
+ * 没传:浏览器取 `window.location.origin`(同源调用,SSO cookie 自动带);
311
+ * 非浏览器(SSR / Node.js 测试)抛错。
312
+ * 用法与 fullstack-plugin/packages/client/dataloom/src/service/index.ts 一致。
313
+ */
314
+ resolveCentralBaseURL(central) {
315
+ if (central.baseURL) return central.baseURL.replace(/\/+$/, "");
316
+ if (typeof window !== "undefined" && window.location?.origin) {
317
+ return window.location.origin;
318
+ }
319
+ throw new Error("CapabilityClient: central.baseURL is required when running outside browser (window.location unavailable)");
285
320
  }
286
321
  /**
287
- * 加载能力,返回执行器
288
- * @param capabilityId - 能力 ID
289
- * @returns 能力执行器
322
+ * 预览态 / 运行态自动判定。沿用 nestjs-capability/src/capability.module.ts:18 同款判定:
323
+ * - 沙箱 fullstack-cli scripts/dev.sh → `NODE_ENV=development` → 预览态
324
+ * - 运行态(npm start / build 后部署)→ `NODE_ENV=production` → 运行态
325
+ * Vite 在构建时把 `process.env.NODE_ENV` 替换为字面量字符串。
326
+ */
327
+ resolveMode() {
328
+ const env = typeof process !== "undefined" ? "development" : void 0;
329
+ return env === "development" ? "preview" : "runtime";
330
+ }
331
+ /**
332
+ * 构造单次调用的请求 URL:
333
+ * - central 开启:4 个中心端点之一(mode 自动判定);
334
+ * - central 关闭:保持原 `${baseURL}/api/capability/{id}` 行为。
335
+ */
336
+ buildExecuteUrl(capabilityId, stream) {
337
+ if (!this.central) {
338
+ return `${this.baseURL}/api/capability/${capabilityId}${stream ? "/stream" : ""}`;
339
+ }
340
+ const base = this.resolveCentralBaseURL(this.central);
341
+ const segment = this.resolveMode() === "preview" ? "/preview/execute" : "/execute";
342
+ return `${base}/app/${this.central.appId}/__runtime__/api/v1/plugin_server/capability/${capabilityId}${segment}${stream ? "/stream" : ""}`;
343
+ }
344
+ /**
345
+ * 构造请求体:
346
+ * - central + 预览态:`{ capability, action, params }`,capability 取自
347
+ * `virtual:capabilities`(load() 时已经 fail-fast 校验过,这里直接取)。
348
+ * - central + 运行态:`{ action, params }`(中心服务通过 RPC 查中心存储拿 config)。
349
+ * - 非 central:`{ action, params }`。
350
+ */
351
+ buildRequestBody(capabilityId, action, params) {
352
+ if (this.central && this.resolveMode() === "preview") {
353
+ const capability = virtualCapabilitiesMap[capabilityId];
354
+ if (!capability) {
355
+ throw new CapabilityNotFoundError(capabilityId);
356
+ }
357
+ return JSON.stringify({
358
+ capability,
359
+ action,
360
+ params
361
+ });
362
+ }
363
+ return JSON.stringify({
364
+ action,
365
+ params
366
+ });
367
+ }
368
+ /**
369
+ * 加载能力,返回执行器。
370
+ * 方案文档 § Client SDK 改造伪代码:
371
+ * const config = capabilityMap[id];
372
+ * if (!config) throw new CapabilityNotFoundError(id);
373
+ * central + 预览态时 fail-fast:本地 `virtual:capabilities` map 没这个 id 就直接抛,
374
+ * 不发任何网络请求。运行态 / 非 central 模式跳过此校验。
290
375
  */
291
376
  load(capabilityId) {
377
+ if (this.central && this.resolveMode() === "preview") {
378
+ if (!(capabilityId in virtualCapabilitiesMap)) {
379
+ throw new CapabilityNotFoundError(capabilityId);
380
+ }
381
+ }
292
382
  return this.createExecutor(capabilityId);
293
383
  }
294
384
  createExecutor(capabilityId) {
@@ -302,7 +392,7 @@ var _CapabilityClient = class _CapabilityClient {
302
392
  };
303
393
  }
304
394
  async executeCall(capabilityId, action, params) {
305
- const url = `${this.baseURL}/api/capability/${capabilityId}`;
395
+ const url = this.buildExecuteUrl(capabilityId, false);
306
396
  let requestParams = params ?? {};
307
397
  const extractedFiles = extractFiles(requestParams);
308
398
  if (extractedFiles.length > 0) {
@@ -323,10 +413,7 @@ var _CapabilityClient = class _CapabilityClient {
323
413
  "Content-Type": "application/json",
324
414
  ...this.options.fetchOptions?.headers
325
415
  },
326
- body: JSON.stringify({
327
- action,
328
- params: requestParams
329
- })
416
+ body: this.buildRequestBody(capabilityId, action, requestParams)
330
417
  });
331
418
  const data = await response.json();
332
419
  if (!response.ok || data.status_code !== ErrorCodes.SUCCESS) {
@@ -358,6 +445,12 @@ var _CapabilityClient = class _CapabilityClient {
358
445
  params: requestParams,
359
446
  error
360
447
  });
448
+ slardar2.captureException(error, {
449
+ source: "client-capability",
450
+ module: "execute-call",
451
+ capabilityId,
452
+ action
453
+ });
361
454
  if (error instanceof CapabilityError) {
362
455
  throw error;
363
456
  }
@@ -365,7 +458,7 @@ var _CapabilityClient = class _CapabilityClient {
365
458
  }
366
459
  }
367
460
  async *executeCallStream(capabilityId, action, params) {
368
- const url = `${this.baseURL}/api/capability/${capabilityId}/stream`;
461
+ const url = this.buildExecuteUrl(capabilityId, true);
369
462
  let requestParams = params ?? {};
370
463
  const extractedFiles = extractFiles(requestParams);
371
464
  if (extractedFiles.length > 0) {
@@ -378,6 +471,7 @@ var _CapabilityClient = class _CapabilityClient {
378
471
  this.logger.info(LOG_PREFIX, `callStream start: capabilityId=${capabilityId}, action=${action}`, {
379
472
  params: requestParams
380
473
  });
474
+ const body = this.buildRequestBody(capabilityId, action, requestParams);
381
475
  let response;
382
476
  try {
383
477
  response = await fetch(url, {
@@ -387,16 +481,20 @@ var _CapabilityClient = class _CapabilityClient {
387
481
  "Content-Type": "application/json",
388
482
  ...this.options.fetchOptions?.headers
389
483
  },
390
- body: JSON.stringify({
391
- action,
392
- params: requestParams
393
- })
484
+ body
394
485
  });
395
486
  } catch (error) {
396
487
  this.logger.error(LOG_PREFIX, `callStream failed: capabilityId=${capabilityId}, action=${action}`, {
397
488
  params: requestParams,
398
489
  error
399
490
  });
491
+ slardar2.captureException(error, {
492
+ source: "client-capability",
493
+ module: "execute-call-stream",
494
+ capabilityId,
495
+ action,
496
+ phase: "fetch"
497
+ });
400
498
  throw new NetworkError(error instanceof Error ? error.message : String(error));
401
499
  }
402
500
  if (!response.ok) {
@@ -496,6 +594,13 @@ var _CapabilityClient = class _CapabilityClient {
496
594
  error,
497
595
  chunkCount
498
596
  });
597
+ slardar2.captureException(error, {
598
+ source: "client-capability",
599
+ module: "execute-call-stream",
600
+ capabilityId,
601
+ action,
602
+ phase: "read"
603
+ });
499
604
  if (error instanceof CapabilityError) {
500
605
  throw error;
501
606
  }
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/types.ts","../src/errors.ts","../src/uploader.ts","../src/file-extractor.ts","../src/client.ts"],"sourcesContent":["import type { RateLimitError } from './errors';\n\n/**\n * Logger 接口,兼容 console 和 @lark-apaas/toolkit 的 logger\n */\nexport interface Logger {\n debug(message: unknown, ...args: unknown[]): void;\n info(message: unknown, ...args: unknown[]): void;\n warn(message: unknown, ...args: unknown[]): void;\n error(message: unknown, ...args: unknown[]): void;\n}\n\n/**\n * RateLimitError 钩子函数类型\n * 当检测到计费受限错误时,在抛出异常前调用此钩子\n */\nexport type RateLimitErrorHook = (error: RateLimitError) => void | Promise<void>;\n\n/**\n * 客户端配置选项\n */\nexport interface CapabilityClientOptions {\n /** 全局路径前缀,默认 ''。例如线上环境可设置为 '/spark/a' */\n baseURL?: string;\n /** 获取文件上传预签名 URL 的接口地址,由上层注入 */\n acquireUploadUrl: string;\n /** 获取文件临时下载 URL 的接口地址,由上层注入 */\n acquireDownloadUrl: string;\n /** 自定义 fetch 配置 */\n fetchOptions?: RequestInit;\n /** 自定义 logger,默认使用 console */\n logger?: Logger;\n /**\n * 计费受限错误钩子\n * 当检测到 RateLimitError 时,在抛出异常前调用此钩子\n * 可用于上报埋点、展示 toast 等场景\n */\n onRateLimitError?: RateLimitErrorHook;\n}\n\n/**\n * 能力执行器接口\n */\nexport interface CapabilityExecutor {\n /**\n * 调用能力\n * @param action - Action 名称\n * @param params - 输入参数\n * @returns Action 执行结果\n */\n call<T = unknown>(\n action: string,\n params?: Record<string, unknown>\n ): Promise<T>;\n\n /**\n * 流式调用能力\n * @param action - Action 名称\n * @param params - 输入参数\n * @returns AsyncIterable,逐个 yield chunk\n */\n callStream<T = unknown>(\n action: string,\n params?: Record<string, unknown>\n ): AsyncIterable<T>;\n}\n\n// ========== API 响应类型 ==========\n\n/**\n * 成功响应\n */\nexport interface SuccessResponse<T> {\n status_code: '0';\n data: T;\n}\n\n/**\n * 错误响应\n */\nexport interface ErrorResponse {\n status_code: string;\n error_msg: string;\n /** 是否为计费受限错误 */\n is_rate_limit_error?: boolean;\n}\n\n/**\n * API 响应类型\n */\nexport type ApiResponse<T> = SuccessResponse<T> | ErrorResponse;\n\n// ========== 具体响应 data 结构 ==========\n\n/**\n * 执行接口响应 data 结构\n */\nexport interface ExecuteResponseData {\n output: unknown;\n}\n\n// ========== 流式响应结构 ==========\n\n/**\n * 流式内容响应\n */\nexport interface StreamContentResponse {\n status_code: '0';\n data: {\n type: 'content';\n delta: unknown;\n finished?: boolean;\n };\n}\n\n/**\n * 流式错误响应\n */\nexport interface StreamErrorResponse {\n status_code: '0';\n data: {\n type: 'error';\n error: {\n /** 错误码,计费受限时为业务错误码(如 k_st_ec_400002687) */\n code: string;\n message: string;\n /** 是否为计费受限错误 */\n isRateLimitError?: boolean;\n };\n };\n}\n\n/**\n * 流式响应类型\n */\nexport type StreamResponse = StreamContentResponse | StreamErrorResponse;\n\n// ========== 错误码 ==========\n\n/**\n * 后端错误码\n */\nexport const ErrorCodes = {\n SUCCESS: '0',\n CAPABILITY_NOT_FOUND: 'k_ec_cap_001',\n PLUGIN_NOT_FOUND: 'k_ec_cap_002',\n ACTION_NOT_FOUND: 'k_ec_cap_003',\n PARAMS_VALIDATION_ERROR: 'k_ec_cap_004',\n EXECUTION_ERROR: 'k_ec_cap_005',\n RATE_LIMIT_EXCEEDED: 'k_ec_cap_006',\n} as const;\n\n// ========== 文件上传相关类型 ==========\n\n/**\n * 提取的文件信息\n */\nexport interface ExtractedFile {\n /** 在 params 中的路径,如 ['image_list', 0] */\n path: (string | number)[];\n /** 文件对象 */\n file: File | Blob;\n}\n\n/**\n * 上传结果\n */\nexport interface UploadResult {\n /** 在 params 中的路径 */\n path: (string | number)[];\n /** 临时下载 URL */\n downloadUrl: string;\n}\n\n/**\n * 获取上传 URL 响应\n */\nexport interface AcquireUploadUrlResponse {\n status_code: string;\n data: {\n uploadURL: string;\n objectKey: string;\n };\n}\n\n/**\n * 获取下载 URL 响应\n */\nexport interface AcquireDownloadUrlResponse {\n status_code: string;\n data: {\n downloadURL: string;\n };\n}\n","/**\n * 能力调用错误基类\n */\nexport class CapabilityError extends Error {\n /** 错误码 */\n code: string;\n /** HTTP 状态码 */\n statusCode?: number;\n\n constructor(message: string, code: string, statusCode?: number) {\n super(message);\n this.name = 'CapabilityError';\n this.code = code;\n this.statusCode = statusCode;\n }\n}\n\n/**\n * 能力不存在错误\n */\nexport class CapabilityNotFoundError extends CapabilityError {\n constructor(capabilityId: string, statusCode?: number) {\n super(`Capability not found: ${capabilityId}`, 'CAPABILITY_NOT_FOUND', statusCode);\n this.name = 'CapabilityNotFoundError';\n }\n}\n\n/**\n * Action 不存在错误\n */\nexport class ActionNotFoundError extends CapabilityError {\n constructor(message: string, statusCode?: number) {\n super(message, 'ACTION_NOT_FOUND', statusCode);\n this.name = 'ActionNotFoundError';\n }\n}\n\n/**\n * 网络错误\n */\nexport class NetworkError extends CapabilityError {\n constructor(message: string) {\n super(message, 'NETWORK_ERROR');\n this.name = 'NetworkError';\n }\n}\n\n/**\n * 执行错误\n */\nexport class ExecutionError extends CapabilityError {\n constructor(message: string, statusCode?: number) {\n super(message, 'EXECUTION_ERROR', statusCode);\n this.name = 'ExecutionError';\n }\n}\n\n/**\n * 文件上传错误\n */\nexport class FileUploadError extends CapabilityError {\n constructor(message: string, statusCode?: number) {\n super(message, 'FILE_UPLOAD_ERROR', statusCode);\n this.name = 'FileUploadError';\n }\n}\n\n/**\n * 计费受限错误\n */\nexport class RateLimitError extends CapabilityError {\n /** 业务错误码 */\n rateLimitCode: string;\n /** 业务错误消息 */\n rateLimitMessage: string;\n\n constructor(rateLimitCode: string, rateLimitMessage: string, statusCode?: number) {\n super(rateLimitMessage, 'RATE_LIMIT_EXCEEDED', statusCode);\n this.name = 'RateLimitError';\n this.rateLimitCode = rateLimitCode;\n this.rateLimitMessage = rateLimitMessage;\n }\n}\n","import type {\n CapabilityClientOptions,\n ExtractedFile,\n UploadResult,\n AcquireUploadUrlResponse,\n AcquireDownloadUrlResponse,\n} from './types';\nimport { FileUploadError } from './errors';\n\ntype FileUploaderOptions = Pick<CapabilityClientOptions, 'acquireUploadUrl' | 'acquireDownloadUrl' | 'fetchOptions'>;\n\n/**\n * 文件上传器\n */\nexport class FileUploader {\n private readonly options: FileUploaderOptions;\n\n constructor(options: FileUploaderOptions) {\n this.options = options;\n }\n\n /**\n * 上传单个文件,返回下载 URL\n */\n async upload(file: File | Blob): Promise<string> {\n const fileName = file instanceof File ? file.name : `blob-${Date.now()}`;\n\n // 1. 获取上传预签名 URL\n const { uploadURL, objectKey } = await this.fetchUploadUrl(fileName);\n\n // 2. 上传文件到 TOS\n await this.uploadToTos(uploadURL, file);\n\n // 3. 获取下载 URL\n const downloadUrl = await this.fetchDownloadUrl(objectKey);\n\n return downloadUrl;\n }\n\n /**\n * 并发上传多个文件\n */\n async uploadAll(files: ExtractedFile[]): Promise<UploadResult[]> {\n const results = await Promise.all(\n files.map(async ({ path, file }) => {\n const downloadUrl = await this.upload(file);\n return { path, downloadUrl };\n })\n );\n return results;\n }\n\n /**\n * 获取上传预签名 URL\n */\n private async fetchUploadUrl(fileName: string): Promise<{ uploadURL: string; objectKey: string }> {\n const { acquireUploadUrl, fetchOptions } = this.options;\n\n let response: Response;\n try {\n response = await fetch(acquireUploadUrl, {\n method: 'POST',\n ...fetchOptions,\n headers: {\n 'Content-Type': 'application/json',\n ...fetchOptions?.headers,\n },\n body: JSON.stringify({ fileName }),\n });\n } catch (error) {\n throw new FileUploadError(\n `Failed to acquire upload URL: ${error instanceof Error ? error.message : String(error)}`\n );\n }\n\n if (!response.ok) {\n throw new FileUploadError(\n `Failed to acquire upload URL: HTTP ${response.status}`,\n response.status\n );\n }\n\n const data = (await response.json()) as AcquireUploadUrlResponse;\n\n if (data.status_code !== '0') {\n throw new FileUploadError(`Failed to acquire upload URL: ${data.status_code}`);\n }\n\n return data.data;\n }\n\n /**\n * 上传文件到 TOS(预签名 URL)\n */\n private async uploadToTos(uploadURL: string, file: File | Blob): Promise<void> {\n let response: Response;\n try {\n response = await fetch(uploadURL, {\n method: 'PUT',\n body: file,\n });\n } catch (error) {\n throw new FileUploadError(\n `Failed to upload file to TOS: ${error instanceof Error ? error.message : String(error)}`\n );\n }\n\n if (!response.ok) {\n throw new FileUploadError(\n `Failed to upload file to TOS: HTTP ${response.status}`,\n response.status\n );\n }\n }\n\n /**\n * 获取临时下载 URL\n */\n private async fetchDownloadUrl(objectKey: string): Promise<string> {\n const { acquireDownloadUrl, fetchOptions } = this.options;\n\n let response: Response;\n try {\n response = await fetch(acquireDownloadUrl, {\n method: 'POST',\n ...fetchOptions,\n headers: {\n 'Content-Type': 'application/json',\n ...fetchOptions?.headers,\n },\n body: JSON.stringify({ objectKey }),\n });\n } catch (error) {\n throw new FileUploadError(\n `Failed to acquire download URL: ${error instanceof Error ? error.message : String(error)}`\n );\n }\n\n if (!response.ok) {\n throw new FileUploadError(\n `Failed to acquire download URL: HTTP ${response.status}`,\n response.status\n );\n }\n\n const data = (await response.json()) as AcquireDownloadUrlResponse;\n\n if (data.status_code !== '0') {\n throw new FileUploadError(`Failed to acquire download URL: ${data.status_code}`);\n }\n\n return data.data.downloadURL;\n }\n}\n","import type { ExtractedFile, UploadResult } from './types';\n\n/**\n * 检测是否为 File 或 Blob\n */\nexport function isFile(value: unknown): value is File | Blob {\n return value instanceof File || value instanceof Blob;\n}\n\n/**\n * 递归提取 params 中的所有文件\n *\n * @example\n * extractFiles({ image_list: [file1, file2] })\n * // => [\n * // { path: ['image_list', 0], file: file1 },\n * // { path: ['image_list', 1], file: file2 }\n * // ]\n */\nexport function extractFiles(params: Record<string, unknown>): ExtractedFile[] {\n const files: ExtractedFile[] = [];\n\n function traverse(obj: unknown, path: (string | number)[]): void {\n if (isFile(obj)) {\n files.push({ path: [...path], file: obj });\n } else if (Array.isArray(obj)) {\n obj.forEach((item, index) => traverse(item, [...path, index]));\n } else if (obj !== null && typeof obj === 'object') {\n Object.entries(obj).forEach(([key, value]) => {\n traverse(value, [...path, key]);\n });\n }\n }\n\n traverse(params, []);\n return files;\n}\n\n/**\n * 根据上传结果替换 params 中的文件为 URL\n *\n * @example\n * replaceFilesWithUrls(\n * { image_list: [file1, file2] },\n * [\n * { path: ['image_list', 0], downloadUrl: 'https://...' },\n * { path: ['image_list', 1], downloadUrl: 'https://...' }\n * ]\n * )\n * // => { image_list: ['https://...', 'https://...'] }\n */\nexport function replaceFilesWithUrls(\n params: Record<string, unknown>,\n results: UploadResult[]\n): Record<string, unknown> {\n // 创建 path -> url 的映射,用于快速查找\n const urlMap = new Map<string, string>();\n for (const { path, downloadUrl } of results) {\n urlMap.set(JSON.stringify(path), downloadUrl);\n }\n\n // 手动深拷贝,同时将 File/Blob 替换为对应的 URL\n // 注意:不能使用 structuredClone,因为它不支持 File/Blob 对象\n function cloneAndReplace(obj: unknown, currentPath: (string | number)[]): unknown {\n // 检查当前路径是否有对应的 URL\n const pathKey = JSON.stringify(currentPath);\n if (urlMap.has(pathKey)) {\n return urlMap.get(pathKey);\n }\n\n // 如果是 File/Blob 但没有对应的 URL(不应该发生),返回 null\n if (isFile(obj)) {\n return null;\n }\n\n if (Array.isArray(obj)) {\n return obj.map((item, index) => cloneAndReplace(item, [...currentPath, index]));\n }\n\n if (obj !== null && typeof obj === 'object') {\n const result: Record<string, unknown> = {};\n for (const [key, value] of Object.entries(obj)) {\n result[key] = cloneAndReplace(value, [...currentPath, key]);\n }\n return result;\n }\n\n // 原始值直接返回\n return obj;\n }\n\n return cloneAndReplace(params, []) as Record<string, unknown>;\n}\n","import type {\n CapabilityClientOptions,\n CapabilityExecutor,\n ApiResponse,\n ExecuteResponseData,\n StreamResponse,\n Logger,\n RateLimitErrorHook,\n} from './types';\nimport { ErrorCodes } from './types';\nimport {\n CapabilityError,\n CapabilityNotFoundError,\n ActionNotFoundError,\n NetworkError,\n ExecutionError,\n RateLimitError,\n} from './errors';\nimport { FileUploader } from './uploader';\nimport { extractFiles, replaceFilesWithUrls } from './file-extractor';\n\nconst LOG_PREFIX = '[CapabilityClient]';\n\nconst defaultLogger: Logger = {\n debug: console.debug.bind(console),\n info: console.info.bind(console),\n warn: console.warn.bind(console),\n error: console.error.bind(console),\n};\n\n/**\n * 能力客户端\n */\nexport class CapabilityClient {\n private options: CapabilityClientOptions;\n private baseURL: string;\n private logger: Logger;\n private onRateLimitError?: RateLimitErrorHook;\n\n constructor(options: CapabilityClientOptions) {\n this.options = options;\n // 移除末尾的斜杠,确保拼接时格式正确\n this.baseURL = (options?.baseURL ?? '').replace(/\\/+$/, '');\n this.logger = options?.logger ?? defaultLogger;\n this.onRateLimitError = options?.onRateLimitError;\n }\n\n /**\n * 加载能力,返回执行器\n * @param capabilityId - 能力 ID\n * @returns 能力执行器\n */\n load(capabilityId: string): CapabilityExecutor {\n return this.createExecutor(capabilityId);\n }\n\n private createExecutor(capabilityId: string): CapabilityExecutor {\n return {\n call: <T = unknown>(action: string, params?: Record<string, unknown>) => {\n return this.executeCall<T>(capabilityId, action, params);\n },\n callStream: <T = unknown>(action: string, params?: Record<string, unknown>) => {\n return this.executeCallStream<T>(capabilityId, action, params);\n },\n };\n }\n\n private async executeCall<T>(\n capabilityId: string,\n action: string,\n params?: Record<string, unknown>\n ): Promise<T> {\n const url = `${this.baseURL}/api/capability/${capabilityId}`;\n let requestParams = params ?? {};\n\n // 处理文件上传\n const extractedFiles = extractFiles(requestParams);\n if (extractedFiles.length > 0) {\n this.logger.info(LOG_PREFIX, `uploading ${extractedFiles.length} file(s)...`);\n const uploader = new FileUploader(this.options);\n const uploadResults = await uploader.uploadAll(extractedFiles);\n requestParams = replaceFilesWithUrls(requestParams, uploadResults);\n this.logger.info(LOG_PREFIX, `file upload completed`);\n }\n\n this.logger.info(LOG_PREFIX, `call start: capabilityId=${capabilityId}, action=${action}`, { params: requestParams });\n\n try {\n const response = await fetch(url, {\n method: 'POST',\n ...this.options.fetchOptions,\n headers: {\n 'Content-Type': 'application/json',\n ...this.options.fetchOptions?.headers,\n },\n body: JSON.stringify({\n action,\n params: requestParams,\n }),\n });\n\n const data = (await response.json()) as ApiResponse<ExecuteResponseData>;\n\n if (!response.ok || data.status_code !== ErrorCodes.SUCCESS) {\n const errorResponse = data as {\n status_code: string;\n error_msg: string;\n is_rate_limit_error?: boolean;\n };\n const errorCode = errorResponse.status_code;\n\n // 计费受限错误:通过 is_rate_limit_error 字段识别\n if (errorResponse.is_rate_limit_error) {\n const rateLimitError = new RateLimitError(\n errorCode, // status_code 就是业务错误码\n errorResponse.error_msg,\n response.status,\n );\n await this.onRateLimitError?.(rateLimitError);\n throw rateLimitError;\n }\n\n switch (errorCode) {\n case ErrorCodes.CAPABILITY_NOT_FOUND:\n throw new CapabilityNotFoundError(capabilityId, response.status);\n case ErrorCodes.ACTION_NOT_FOUND:\n throw new ActionNotFoundError(errorResponse.error_msg, response.status);\n case ErrorCodes.PLUGIN_NOT_FOUND:\n case ErrorCodes.EXECUTION_ERROR:\n default:\n throw new ExecutionError(errorResponse.error_msg, response.status);\n }\n }\n\n const result = (data as { data: ExecuteResponseData }).data.output as T;\n\n this.logger.info(LOG_PREFIX, `call success: capabilityId=${capabilityId}, action=${action}`, { result });\n\n return result;\n } catch (error) {\n this.logger.error(LOG_PREFIX, `call failed: capabilityId=${capabilityId}, action=${action}`, { params: requestParams, error });\n if (error instanceof CapabilityError) {\n throw error;\n }\n throw new NetworkError(error instanceof Error ? error.message : String(error));\n }\n }\n\n private async *executeCallStream<T>(\n capabilityId: string,\n action: string,\n params?: Record<string, unknown>\n ): AsyncIterable<T> {\n const url = `${this.baseURL}/api/capability/${capabilityId}/stream`;\n let requestParams = params ?? {};\n\n // 处理文件上传\n const extractedFiles = extractFiles(requestParams);\n if (extractedFiles.length > 0) {\n this.logger.info(LOG_PREFIX, `uploading ${extractedFiles.length} file(s)...`);\n const uploader = new FileUploader(this.options);\n const uploadResults = await uploader.uploadAll(extractedFiles);\n requestParams = replaceFilesWithUrls(requestParams, uploadResults);\n this.logger.info(LOG_PREFIX, `file upload completed`);\n }\n\n this.logger.info(LOG_PREFIX, `callStream start: capabilityId=${capabilityId}, action=${action}`, { params: requestParams });\n\n let response: Response;\n try {\n response = await fetch(url, {\n method: 'POST',\n ...this.options.fetchOptions,\n headers: {\n 'Content-Type': 'application/json',\n ...this.options.fetchOptions?.headers,\n },\n body: JSON.stringify({\n action,\n params: requestParams,\n }),\n });\n } catch (error) {\n this.logger.error(LOG_PREFIX, `callStream failed: capabilityId=${capabilityId}, action=${action}`, { params: requestParams, error });\n throw new NetworkError(error instanceof Error ? error.message : String(error));\n }\n\n if (!response.ok) {\n const errMsg = `HTTP ${response.status} ${response.statusText}`;\n this.logger.error(LOG_PREFIX, `callStream failed: capabilityId=${capabilityId}, action=${action}`, { params: requestParams, error: errMsg });\n throw new NetworkError(errMsg);\n }\n\n if (!response.body) {\n const errMsg = 'Response body is null';\n this.logger.error(LOG_PREFIX, `callStream failed: capabilityId=${capabilityId}, action=${action}`, { params: requestParams, error: errMsg });\n throw new NetworkError(errMsg);\n }\n\n const reader = response.body.getReader();\n const decoder = new TextDecoder();\n let buffer = '';\n let chunkCount = 0;\n let aggregatedContent = '';\n let canAggregate = true;\n\n try {\n while (true) {\n const { done, value } = await reader.read();\n if (done) break;\n\n buffer += decoder.decode(value, { stream: true });\n const lines = buffer.split('\\n');\n buffer = lines.pop() ?? '';\n\n for (const line of lines) {\n if (line.startsWith('data: ')) {\n const data = line.slice(6);\n\n try {\n const parsed = JSON.parse(data) as StreamResponse;\n\n if (parsed.status_code === ErrorCodes.SUCCESS && parsed.data) {\n if (parsed.data.type === 'content') {\n chunkCount++;\n const delta = parsed.data.delta as T;\n\n // 尝试聚合 content 字段\n if (canAggregate) {\n const content = (delta as Record<string, unknown>)?.content;\n if (typeof content === 'string') {\n aggregatedContent += content;\n } else {\n canAggregate = false;\n }\n }\n\n yield delta;\n\n if (parsed.data.finished) {\n const resultInfo = canAggregate\n ? { chunkCount, result: aggregatedContent }\n : { chunkCount, resultLength: aggregatedContent.length || chunkCount };\n this.logger.info(LOG_PREFIX, `callStream end: capabilityId=${capabilityId}, action=${action}`, resultInfo);\n return;\n }\n } else if (parsed.data.type === 'error') {\n const err = parsed.data.error;\n if (err.isRateLimitError) {\n const rateLimitError = new RateLimitError(\n err.code, // code 就是业务错误码\n err.message,\n );\n await this.onRateLimitError?.(rateLimitError);\n throw rateLimitError;\n }\n throw new ExecutionError(err.message);\n }\n }\n } catch (parseError) {\n if (parseError instanceof CapabilityError) {\n this.logger.error(LOG_PREFIX, `callStream failed: capabilityId=${capabilityId}, action=${action}`, { params: requestParams, error: parseError, chunkCount });\n throw parseError;\n }\n // 忽略非 JSON 行\n }\n }\n }\n }\n const resultInfo = canAggregate\n ? { chunkCount, result: aggregatedContent }\n : { chunkCount, resultLength: aggregatedContent.length || chunkCount };\n this.logger.info(LOG_PREFIX, `callStream end: capabilityId=${capabilityId}, action=${action}`, resultInfo);\n } catch (error) {\n this.logger.error(LOG_PREFIX, `callStream failed: capabilityId=${capabilityId}, action=${action}`, { params: requestParams, error, chunkCount });\n if (error instanceof CapabilityError) {\n throw error;\n }\n throw new NetworkError(error instanceof Error ? error.message : String(error));\n } finally {\n reader.releaseLock();\n }\n }\n}\n\n/**\n * 创建客户端实例\n */\nexport function createClient(options: CapabilityClientOptions): CapabilityClient {\n return new CapabilityClient(options);\n}\n"],"mappings":"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","names":["ErrorCodes","SUCCESS","CAPABILITY_NOT_FOUND","PLUGIN_NOT_FOUND","ACTION_NOT_FOUND","PARAMS_VALIDATION_ERROR","EXECUTION_ERROR","RATE_LIMIT_EXCEEDED","CapabilityError","Error","message","code","statusCode","name","CapabilityNotFoundError","capabilityId","ActionNotFoundError","NetworkError","ExecutionError","FileUploadError","RateLimitError","rateLimitCode","rateLimitMessage","FileUploader","options","upload","file","fileName","File","name","Date","now","uploadURL","objectKey","fetchUploadUrl","uploadToTos","downloadUrl","fetchDownloadUrl","uploadAll","files","results","Promise","all","map","path","acquireUploadUrl","fetchOptions","response","fetch","method","headers","body","JSON","stringify","error","FileUploadError","Error","message","String","ok","status","data","json","status_code","acquireDownloadUrl","downloadURL","isFile","value","File","Blob","extractFiles","params","files","traverse","obj","path","push","file","Array","isArray","forEach","item","index","Object","entries","key","replaceFilesWithUrls","results","urlMap","Map","downloadUrl","set","JSON","stringify","cloneAndReplace","currentPath","pathKey","has","get","map","result","LOG_PREFIX","defaultLogger","debug","console","bind","info","warn","error","CapabilityClient","options","baseURL","logger","onRateLimitError","replace","load","capabilityId","createExecutor","call","action","params","executeCall","callStream","executeCallStream","url","requestParams","extractedFiles","extractFiles","length","uploader","FileUploader","uploadResults","uploadAll","replaceFilesWithUrls","response","fetch","method","fetchOptions","headers","body","JSON","stringify","data","json","ok","status_code","ErrorCodes","SUCCESS","errorResponse","errorCode","is_rate_limit_error","rateLimitError","RateLimitError","error_msg","status","CAPABILITY_NOT_FOUND","CapabilityNotFoundError","ACTION_NOT_FOUND","ActionNotFoundError","PLUGIN_NOT_FOUND","EXECUTION_ERROR","ExecutionError","result","output","CapabilityError","NetworkError","Error","message","String","errMsg","statusText","reader","getReader","decoder","TextDecoder","buffer","chunkCount","aggregatedContent","canAggregate","done","value","read","decode","stream","lines","split","pop","line","startsWith","slice","parsed","parse","type","delta","content","finished","resultInfo","resultLength","err","isRateLimitError","code","parseError","releaseLock","createClient"]}
1
+ {"version":3,"sources":["../src/types.ts","../src/errors.ts","../src/uploader.ts","../src/file-extractor.ts","../src/client.ts"],"sourcesContent":["import type { RateLimitError } from './errors';\n\n/**\n * Logger 接口,兼容 console 和 @lark-apaas/toolkit 的 logger\n */\nexport interface Logger {\n debug(message: unknown, ...args: unknown[]): void;\n info(message: unknown, ...args: unknown[]): void;\n warn(message: unknown, ...args: unknown[]): void;\n error(message: unknown, ...args: unknown[]): void;\n}\n\n/**\n * RateLimitError 钩子函数类型\n * 当检测到计费受限错误时,在抛出异常前调用此钩子\n */\nexport type RateLimitErrorHook = (error: RateLimitError) => void | Promise<void>;\n\n/**\n * 客户端配置选项\n */\nexport interface CapabilityClientOptions {\n /** 全局路径前缀,默认 ''。例如线上环境可设置为 '/spark/a' */\n baseURL?: string;\n /** 获取文件上传预签名 URL 的接口地址,由上层注入 */\n acquireUploadUrl: string;\n /** 获取文件临时下载 URL 的接口地址,由上层注入 */\n acquireDownloadUrl: string;\n /** 自定义 fetch 配置 */\n fetchOptions?: RequestInit;\n /** 自定义 logger,默认使用 console */\n logger?: Logger;\n /**\n * 计费受限错误钩子\n * 当检测到 RateLimitError 时,在抛出异常前调用此钩子\n * 可用于上报埋点、展示 toast 等场景\n */\n onRateLimitError?: RateLimitErrorHook;\n /**\n * 中心化服务分流开关。启用后 capability 调用路由到妙搭 plugin_server 的 4 个中心端点:\n * - 运行态 非流式: POST {baseURL}/app/{appId}/__runtime__/api/v1/plugin_server/capability/{capabilityId}/execute\n * - 运行态 流式: POST {baseURL}/app/{appId}/__runtime__/api/v1/plugin_server/capability/{capabilityId}/execute/stream\n * - 预览态 非流式: POST {baseURL}/app/{appId}/__runtime__/api/v1/plugin_server/capability/{capabilityId}/preview/execute\n * - 预览态 流式: POST {baseURL}/app/{appId}/__runtime__/api/v1/plugin_server/capability/{capabilityId}/preview/execute/stream\n *\n * baseURL 默认取 `window.location.origin`(同源调用,SSO cookie 自动带)。\n * 预览态 / 运行态由 `process.env.NODE_ENV` 自动判定:\n * - 沙箱开发态(fullstack-cli scripts/dev.sh 起的进程,NODE_ENV=development)→ 预览态\n * - 运行态启动(NODE_ENV=production,无论 build 还是 npm start)→ 运行态\n * 业务前端 `createClient` 配置不需要区分两种环境。\n *\n * 预览态 body 必须携带完整 capability config,从 `capabilities` map 查;缺失抛\n * CapabilityNotFoundError。运行态由中心服务通过 capability_id 走 RPC 查中心存储。\n */\n central?: CentralOptions;\n}\n\n/**\n * 中心化服务分流配置。\n */\nexport interface CentralOptions {\n /** 是否启用中心化分流;false / 未配置则维持原行为打用户应用。 */\n enabled: boolean;\n /**\n * 中心服务 base URL(含 scheme)。可选;不传时默认 `window.location.origin`。\n * 非浏览器环境(SSR / Node.js 测试)必须显式传,否则 SDK 抛错。\n */\n baseURL?: string;\n /** 当前应用 ID,拼接在 `/app/{appId}/__runtime__/...` 路径段。 */\n appId: string;\n}\n\n/**\n * Capability 配置,与 @lark-apaas/nestjs-capability / miaoda_plugin_server 的 interfaces/capability-config.ts 对齐。\n * 预览态 body.capability 必须为完整对象。\n */\nexport interface CapabilityConfig {\n id: string;\n pluginKey: string;\n pluginVersion: string;\n name: string;\n description: string;\n paramsSchema: unknown;\n formValue: Record<string, unknown>;\n createdAt: number;\n updatedAt: number;\n}\n\n/**\n * 能力执行器接口\n */\nexport interface CapabilityExecutor {\n /**\n * 调用能力\n * @param action - Action 名称\n * @param params - 输入参数\n * @returns Action 执行结果\n */\n call<T = unknown>(\n action: string,\n params?: Record<string, unknown>\n ): Promise<T>;\n\n /**\n * 流式调用能力\n * @param action - Action 名称\n * @param params - 输入参数\n * @returns AsyncIterable,逐个 yield chunk\n */\n callStream<T = unknown>(\n action: string,\n params?: Record<string, unknown>\n ): AsyncIterable<T>;\n}\n\n// ========== API 响应类型 ==========\n\n/**\n * 成功响应\n */\nexport interface SuccessResponse<T> {\n status_code: '0';\n data: T;\n}\n\n/**\n * 错误响应\n */\nexport interface ErrorResponse {\n status_code: string;\n error_msg: string;\n /** 是否为计费受限错误 */\n is_rate_limit_error?: boolean;\n}\n\n/**\n * API 响应类型\n */\nexport type ApiResponse<T> = SuccessResponse<T> | ErrorResponse;\n\n// ========== 具体响应 data 结构 ==========\n\n/**\n * 执行接口响应 data 结构\n */\nexport interface ExecuteResponseData {\n output: unknown;\n}\n\n// ========== 流式响应结构 ==========\n\n/**\n * 流式内容响应\n */\nexport interface StreamContentResponse {\n status_code: '0';\n data: {\n type: 'content';\n delta: unknown;\n finished?: boolean;\n };\n}\n\n/**\n * 流式错误响应\n */\nexport interface StreamErrorResponse {\n status_code: '0';\n data: {\n type: 'error';\n error: {\n /** 错误码,计费受限时为业务错误码(如 k_st_ec_400002687) */\n code: string;\n message: string;\n /** 是否为计费受限错误 */\n isRateLimitError?: boolean;\n };\n };\n}\n\n/**\n * 流式响应类型\n */\nexport type StreamResponse = StreamContentResponse | StreamErrorResponse;\n\n// ========== 错误码 ==========\n\n/**\n * 后端错误码\n */\nexport const ErrorCodes = {\n SUCCESS: '0',\n CAPABILITY_NOT_FOUND: 'k_ec_cap_001',\n PLUGIN_NOT_FOUND: 'k_ec_cap_002',\n ACTION_NOT_FOUND: 'k_ec_cap_003',\n PARAMS_VALIDATION_ERROR: 'k_ec_cap_004',\n EXECUTION_ERROR: 'k_ec_cap_005',\n RATE_LIMIT_EXCEEDED: 'k_ec_cap_006',\n} as const;\n\n// ========== 文件上传相关类型 ==========\n\n/**\n * 提取的文件信息\n */\nexport interface ExtractedFile {\n /** 在 params 中的路径,如 ['image_list', 0] */\n path: (string | number)[];\n /** 文件对象 */\n file: File | Blob;\n}\n\n/**\n * 上传结果\n */\nexport interface UploadResult {\n /** 在 params 中的路径 */\n path: (string | number)[];\n /** 临时下载 URL */\n downloadUrl: string;\n}\n\n/**\n * 获取上传 URL 响应\n */\nexport interface AcquireUploadUrlResponse {\n status_code: string;\n data: {\n uploadURL: string;\n objectKey: string;\n };\n}\n\n/**\n * 获取下载 URL 响应\n */\nexport interface AcquireDownloadUrlResponse {\n status_code: string;\n data: {\n downloadURL: string;\n };\n}\n","/**\n * 能力调用错误基类\n */\nexport class CapabilityError extends Error {\n /** 错误码 */\n code: string;\n /** HTTP 状态码 */\n statusCode?: number;\n\n constructor(message: string, code: string, statusCode?: number) {\n super(message);\n this.name = 'CapabilityError';\n this.code = code;\n this.statusCode = statusCode;\n }\n}\n\n/**\n * 能力不存在错误\n */\nexport class CapabilityNotFoundError extends CapabilityError {\n constructor(capabilityId: string, statusCode?: number) {\n super(`Capability not found: ${capabilityId}`, 'CAPABILITY_NOT_FOUND', statusCode);\n this.name = 'CapabilityNotFoundError';\n }\n}\n\n/**\n * Action 不存在错误\n */\nexport class ActionNotFoundError extends CapabilityError {\n constructor(message: string, statusCode?: number) {\n super(message, 'ACTION_NOT_FOUND', statusCode);\n this.name = 'ActionNotFoundError';\n }\n}\n\n/**\n * 网络错误\n */\nexport class NetworkError extends CapabilityError {\n constructor(message: string) {\n super(message, 'NETWORK_ERROR');\n this.name = 'NetworkError';\n }\n}\n\n/**\n * 执行错误\n */\nexport class ExecutionError extends CapabilityError {\n constructor(message: string, statusCode?: number) {\n super(message, 'EXECUTION_ERROR', statusCode);\n this.name = 'ExecutionError';\n }\n}\n\n/**\n * 文件上传错误\n */\nexport class FileUploadError extends CapabilityError {\n constructor(message: string, statusCode?: number) {\n super(message, 'FILE_UPLOAD_ERROR', statusCode);\n this.name = 'FileUploadError';\n }\n}\n\n/**\n * 计费受限错误\n */\nexport class RateLimitError extends CapabilityError {\n /** 业务错误码 */\n rateLimitCode: string;\n /** 业务错误消息 */\n rateLimitMessage: string;\n\n constructor(rateLimitCode: string, rateLimitMessage: string, statusCode?: number) {\n super(rateLimitMessage, 'RATE_LIMIT_EXCEEDED', statusCode);\n this.name = 'RateLimitError';\n this.rateLimitCode = rateLimitCode;\n this.rateLimitMessage = rateLimitMessage;\n }\n}\n","import type {\n CapabilityClientOptions,\n ExtractedFile,\n UploadResult,\n AcquireUploadUrlResponse,\n AcquireDownloadUrlResponse,\n} from './types';\nimport { FileUploadError } from './errors';\nimport { slardar } from '@lark-apaas/internal-slardar';\n\ntype FileUploaderOptions = Pick<CapabilityClientOptions, 'acquireUploadUrl' | 'acquireDownloadUrl' | 'fetchOptions'>;\n\n/**\n * 文件上传器\n */\nexport class FileUploader {\n private readonly options: FileUploaderOptions;\n\n constructor(options: FileUploaderOptions) {\n this.options = options;\n }\n\n /**\n * 上传单个文件,返回下载 URL\n */\n async upload(file: File | Blob): Promise<string> {\n const fileName = file instanceof File ? file.name : `blob-${Date.now()}`;\n\n // 1. 获取上传预签名 URL\n const { uploadURL, objectKey } = await this.fetchUploadUrl(fileName);\n\n // 2. 上传文件到 TOS\n await this.uploadToTos(uploadURL, file);\n\n // 3. 获取下载 URL\n const downloadUrl = await this.fetchDownloadUrl(objectKey);\n\n return downloadUrl;\n }\n\n /**\n * 并发上传多个文件\n */\n async uploadAll(files: ExtractedFile[]): Promise<UploadResult[]> {\n const results = await Promise.all(\n files.map(async ({ path, file }) => {\n const downloadUrl = await this.upload(file);\n return { path, downloadUrl };\n })\n );\n return results;\n }\n\n /**\n * 获取上传预签名 URL\n */\n private async fetchUploadUrl(fileName: string): Promise<{ uploadURL: string; objectKey: string }> {\n const { acquireUploadUrl, fetchOptions } = this.options;\n\n let response: Response;\n try {\n response = await fetch(acquireUploadUrl, {\n method: 'POST',\n ...fetchOptions,\n headers: {\n 'Content-Type': 'application/json',\n ...fetchOptions?.headers,\n },\n body: JSON.stringify({ fileName }),\n });\n } catch (error) {\n slardar.captureException(error, { source: 'client-capability', module: 'file-upload', step: 'fetch-upload-url' });\n throw new FileUploadError(\n `Failed to acquire upload URL: ${error instanceof Error ? error.message : String(error)}`\n );\n }\n\n if (!response.ok) {\n throw new FileUploadError(\n `Failed to acquire upload URL: HTTP ${response.status}`,\n response.status\n );\n }\n\n const data = (await response.json()) as AcquireUploadUrlResponse;\n\n if (data.status_code !== '0') {\n throw new FileUploadError(`Failed to acquire upload URL: ${data.status_code}`);\n }\n\n return data.data;\n }\n\n /**\n * 上传文件到 TOS(预签名 URL)\n */\n private async uploadToTos(uploadURL: string, file: File | Blob): Promise<void> {\n let response: Response;\n try {\n response = await fetch(uploadURL, {\n method: 'PUT',\n body: file,\n });\n } catch (error) {\n slardar.captureException(error, { source: 'client-capability', module: 'file-upload', step: 'upload-to-tos' });\n throw new FileUploadError(\n `Failed to upload file to TOS: ${error instanceof Error ? error.message : String(error)}`\n );\n }\n\n if (!response.ok) {\n throw new FileUploadError(\n `Failed to upload file to TOS: HTTP ${response.status}`,\n response.status\n );\n }\n }\n\n /**\n * 获取临时下载 URL\n */\n private async fetchDownloadUrl(objectKey: string): Promise<string> {\n const { acquireDownloadUrl, fetchOptions } = this.options;\n\n let response: Response;\n try {\n response = await fetch(acquireDownloadUrl, {\n method: 'POST',\n ...fetchOptions,\n headers: {\n 'Content-Type': 'application/json',\n ...fetchOptions?.headers,\n },\n body: JSON.stringify({ objectKey }),\n });\n } catch (error) {\n slardar.captureException(error, { source: 'client-capability', module: 'file-upload', step: 'fetch-download-url' });\n throw new FileUploadError(\n `Failed to acquire download URL: ${error instanceof Error ? error.message : String(error)}`\n );\n }\n\n if (!response.ok) {\n throw new FileUploadError(\n `Failed to acquire download URL: HTTP ${response.status}`,\n response.status\n );\n }\n\n const data = (await response.json()) as AcquireDownloadUrlResponse;\n\n if (data.status_code !== '0') {\n throw new FileUploadError(`Failed to acquire download URL: ${data.status_code}`);\n }\n\n return data.data.downloadURL;\n }\n}\n","import type { ExtractedFile, UploadResult } from './types';\n\n/**\n * 检测是否为 File 或 Blob\n */\nexport function isFile(value: unknown): value is File | Blob {\n return value instanceof File || value instanceof Blob;\n}\n\n/**\n * 递归提取 params 中的所有文件\n *\n * @example\n * extractFiles({ image_list: [file1, file2] })\n * // => [\n * // { path: ['image_list', 0], file: file1 },\n * // { path: ['image_list', 1], file: file2 }\n * // ]\n */\nexport function extractFiles(params: Record<string, unknown>): ExtractedFile[] {\n const files: ExtractedFile[] = [];\n\n function traverse(obj: unknown, path: (string | number)[]): void {\n if (isFile(obj)) {\n files.push({ path: [...path], file: obj });\n } else if (Array.isArray(obj)) {\n obj.forEach((item, index) => traverse(item, [...path, index]));\n } else if (obj !== null && typeof obj === 'object') {\n Object.entries(obj).forEach(([key, value]) => {\n traverse(value, [...path, key]);\n });\n }\n }\n\n traverse(params, []);\n return files;\n}\n\n/**\n * 根据上传结果替换 params 中的文件为 URL\n *\n * @example\n * replaceFilesWithUrls(\n * { image_list: [file1, file2] },\n * [\n * { path: ['image_list', 0], downloadUrl: 'https://...' },\n * { path: ['image_list', 1], downloadUrl: 'https://...' }\n * ]\n * )\n * // => { image_list: ['https://...', 'https://...'] }\n */\nexport function replaceFilesWithUrls(\n params: Record<string, unknown>,\n results: UploadResult[]\n): Record<string, unknown> {\n // 创建 path -> url 的映射,用于快速查找\n const urlMap = new Map<string, string>();\n for (const { path, downloadUrl } of results) {\n urlMap.set(JSON.stringify(path), downloadUrl);\n }\n\n // 手动深拷贝,同时将 File/Blob 替换为对应的 URL\n // 注意:不能使用 structuredClone,因为它不支持 File/Blob 对象\n function cloneAndReplace(obj: unknown, currentPath: (string | number)[]): unknown {\n // 检查当前路径是否有对应的 URL\n const pathKey = JSON.stringify(currentPath);\n if (urlMap.has(pathKey)) {\n return urlMap.get(pathKey);\n }\n\n // 如果是 File/Blob 但没有对应的 URL(不应该发生),返回 null\n if (isFile(obj)) {\n return null;\n }\n\n if (Array.isArray(obj)) {\n return obj.map((item, index) => cloneAndReplace(item, [...currentPath, index]));\n }\n\n if (obj !== null && typeof obj === 'object') {\n const result: Record<string, unknown> = {};\n for (const [key, value] of Object.entries(obj)) {\n result[key] = cloneAndReplace(value, [...currentPath, key]);\n }\n return result;\n }\n\n // 原始值直接返回\n return obj;\n }\n\n return cloneAndReplace(params, []) as Record<string, unknown>;\n}\n","import type {\n CapabilityClientOptions,\n CapabilityExecutor,\n ApiResponse,\n ExecuteResponseData,\n StreamResponse,\n Logger,\n RateLimitErrorHook,\n CentralOptions,\n} from './types';\nimport { ErrorCodes } from './types';\nimport {\n CapabilityError,\n CapabilityNotFoundError,\n ActionNotFoundError,\n NetworkError,\n ExecutionError,\n RateLimitError,\n} from './errors';\nimport { FileUploader } from './uploader';\nimport { extractFiles, replaceFilesWithUrls } from './file-extractor';\nimport { slardar } from '@lark-apaas/internal-slardar';\n\n// 方案文档 § Client SDK 改造伪代码原句:\n// `import capabilityMap from 'virtual:capabilities'; // build 时注入`\n// 由 @lark-apaas/fullstack-vite-preset 的 capabilitiesBundlePlugin 在 dev/build 时\n// 扫业务工程 server/capabilities/*.json 注入。类型声明在 ./virtual.d.ts。\nimport virtualCapabilitiesMap from 'virtual:capabilities';\nimport type { CapabilityConfig } from './types';\n\nconst LOG_PREFIX = '[CapabilityClient]';\n\nconst defaultLogger: Logger = {\n debug: console.debug.bind(console),\n info: console.info.bind(console),\n warn: console.warn.bind(console),\n error: console.error.bind(console),\n};\n\n/**\n * 能力客户端\n */\nexport class CapabilityClient {\n private options: CapabilityClientOptions;\n private baseURL: string;\n private logger: Logger;\n private onRateLimitError?: RateLimitErrorHook;\n private central?: CentralOptions;\n\n constructor(options: CapabilityClientOptions) {\n this.options = options;\n // 移除末尾的斜杠,确保拼接时格式正确\n this.baseURL = (options?.baseURL ?? '').replace(/\\/+$/, '');\n this.logger = options?.logger ?? defaultLogger;\n this.onRateLimitError = options?.onRateLimitError;\n if (options?.central?.enabled) {\n this.central = options.central;\n }\n }\n\n /**\n * central.baseURL → caller 显式给的(去尾斜杠)。\n * 没传:浏览器取 `window.location.origin`(同源调用,SSO cookie 自动带);\n * 非浏览器(SSR / Node.js 测试)抛错。\n * 用法与 fullstack-plugin/packages/client/dataloom/src/service/index.ts 一致。\n */\n private resolveCentralBaseURL(central: CentralOptions): string {\n if (central.baseURL) return central.baseURL.replace(/\\/+$/, '');\n if (typeof window !== 'undefined' && window.location?.origin) {\n return window.location.origin;\n }\n throw new Error(\n 'CapabilityClient: central.baseURL is required when running outside browser (window.location unavailable)',\n );\n }\n\n /**\n * 预览态 / 运行态自动判定。沿用 nestjs-capability/src/capability.module.ts:18 同款判定:\n * - 沙箱 fullstack-cli scripts/dev.sh → `NODE_ENV=development` → 预览态\n * - 运行态(npm start / build 后部署)→ `NODE_ENV=production` → 运行态\n * Vite 在构建时把 `process.env.NODE_ENV` 替换为字面量字符串。\n */\n private resolveMode(): 'preview' | 'runtime' {\n const env = typeof process !== 'undefined' ? process.env?.NODE_ENV : undefined;\n return env === 'development' ? 'preview' : 'runtime';\n }\n\n /**\n * 构造单次调用的请求 URL:\n * - central 开启:4 个中心端点之一(mode 自动判定);\n * - central 关闭:保持原 `${baseURL}/api/capability/{id}` 行为。\n */\n private buildExecuteUrl(capabilityId: string, stream: boolean): string {\n if (!this.central) {\n return `${this.baseURL}/api/capability/${capabilityId}${stream ? '/stream' : ''}`;\n }\n const base = this.resolveCentralBaseURL(this.central);\n const segment = this.resolveMode() === 'preview' ? '/preview/execute' : '/execute';\n return `${base}/app/${this.central.appId}/__runtime__/api/v1/plugin_server/capability/${capabilityId}${segment}${stream ? '/stream' : ''}`;\n }\n\n /**\n * 构造请求体:\n * - central + 预览态:`{ capability, action, params }`,capability 取自\n * `virtual:capabilities`(load() 时已经 fail-fast 校验过,这里直接取)。\n * - central + 运行态:`{ action, params }`(中心服务通过 RPC 查中心存储拿 config)。\n * - 非 central:`{ action, params }`。\n */\n private buildRequestBody(\n capabilityId: string,\n action: string,\n params: Record<string, unknown>,\n ): string {\n if (this.central && this.resolveMode() === 'preview') {\n const capability = virtualCapabilitiesMap[capabilityId] as CapabilityConfig | undefined;\n if (!capability) {\n // 理论上 load() 已经 fail-fast 校验,但兜底处理\n throw new CapabilityNotFoundError(capabilityId);\n }\n return JSON.stringify({ capability, action, params });\n }\n return JSON.stringify({ action, params });\n }\n\n /**\n * 加载能力,返回执行器。\n * 方案文档 § Client SDK 改造伪代码:\n * const config = capabilityMap[id];\n * if (!config) throw new CapabilityNotFoundError(id);\n * central + 预览态时 fail-fast:本地 `virtual:capabilities` map 没这个 id 就直接抛,\n * 不发任何网络请求。运行态 / 非 central 模式跳过此校验。\n */\n load(capabilityId: string): CapabilityExecutor {\n if (this.central && this.resolveMode() === 'preview') {\n if (!(capabilityId in virtualCapabilitiesMap)) {\n throw new CapabilityNotFoundError(capabilityId);\n }\n }\n return this.createExecutor(capabilityId);\n }\n\n private createExecutor(capabilityId: string): CapabilityExecutor {\n return {\n call: <T = unknown>(action: string, params?: Record<string, unknown>) => {\n return this.executeCall<T>(capabilityId, action, params);\n },\n callStream: <T = unknown>(action: string, params?: Record<string, unknown>) => {\n return this.executeCallStream<T>(capabilityId, action, params);\n },\n };\n }\n\n private async executeCall<T>(\n capabilityId: string,\n action: string,\n params?: Record<string, unknown>\n ): Promise<T> {\n const url = this.buildExecuteUrl(capabilityId, false);\n let requestParams = params ?? {};\n\n // 处理文件上传\n const extractedFiles = extractFiles(requestParams);\n if (extractedFiles.length > 0) {\n this.logger.info(LOG_PREFIX, `uploading ${extractedFiles.length} file(s)...`);\n const uploader = new FileUploader(this.options);\n const uploadResults = await uploader.uploadAll(extractedFiles);\n requestParams = replaceFilesWithUrls(requestParams, uploadResults);\n this.logger.info(LOG_PREFIX, `file upload completed`);\n }\n\n this.logger.info(LOG_PREFIX, `call start: capabilityId=${capabilityId}, action=${action}`, { params: requestParams });\n\n try {\n const response = await fetch(url, {\n method: 'POST',\n ...this.options.fetchOptions,\n headers: {\n 'Content-Type': 'application/json',\n ...this.options.fetchOptions?.headers,\n },\n body: this.buildRequestBody(capabilityId, action, requestParams),\n });\n\n const data = (await response.json()) as ApiResponse<ExecuteResponseData>;\n\n if (!response.ok || data.status_code !== ErrorCodes.SUCCESS) {\n const errorResponse = data as {\n status_code: string;\n error_msg: string;\n is_rate_limit_error?: boolean;\n };\n const errorCode = errorResponse.status_code;\n\n // 计费受限错误:通过 is_rate_limit_error 字段识别\n if (errorResponse.is_rate_limit_error) {\n const rateLimitError = new RateLimitError(\n errorCode, // status_code 就是业务错误码\n errorResponse.error_msg,\n response.status,\n );\n await this.onRateLimitError?.(rateLimitError);\n throw rateLimitError;\n }\n\n switch (errorCode) {\n case ErrorCodes.CAPABILITY_NOT_FOUND:\n throw new CapabilityNotFoundError(capabilityId, response.status);\n case ErrorCodes.ACTION_NOT_FOUND:\n throw new ActionNotFoundError(errorResponse.error_msg, response.status);\n case ErrorCodes.PLUGIN_NOT_FOUND:\n case ErrorCodes.EXECUTION_ERROR:\n default:\n throw new ExecutionError(errorResponse.error_msg, response.status);\n }\n }\n\n const result = (data as { data: ExecuteResponseData }).data.output as T;\n\n this.logger.info(LOG_PREFIX, `call success: capabilityId=${capabilityId}, action=${action}`, { result });\n\n return result;\n } catch (error) {\n this.logger.error(LOG_PREFIX, `call failed: capabilityId=${capabilityId}, action=${action}`, { params: requestParams, error });\n slardar.captureException(error, { source: 'client-capability', module: 'execute-call', capabilityId, action });\n if (error instanceof CapabilityError) {\n throw error;\n }\n throw new NetworkError(error instanceof Error ? error.message : String(error));\n }\n }\n\n private async *executeCallStream<T>(\n capabilityId: string,\n action: string,\n params?: Record<string, unknown>\n ): AsyncIterable<T> {\n const url = this.buildExecuteUrl(capabilityId, true);\n let requestParams = params ?? {};\n\n // 处理文件上传\n const extractedFiles = extractFiles(requestParams);\n if (extractedFiles.length > 0) {\n this.logger.info(LOG_PREFIX, `uploading ${extractedFiles.length} file(s)...`);\n const uploader = new FileUploader(this.options);\n const uploadResults = await uploader.uploadAll(extractedFiles);\n requestParams = replaceFilesWithUrls(requestParams, uploadResults);\n this.logger.info(LOG_PREFIX, `file upload completed`);\n }\n\n this.logger.info(LOG_PREFIX, `callStream start: capabilityId=${capabilityId}, action=${action}`, { params: requestParams });\n\n // central + preview 模式下 buildRequestBody 可能抛 CapabilityNotFoundError,\n // 先于 fetch 计算好 body,避免被下面的 NetworkError 包装吞掉类型。\n const body = this.buildRequestBody(capabilityId, action, requestParams);\n\n let response: Response;\n try {\n response = await fetch(url, {\n method: 'POST',\n ...this.options.fetchOptions,\n headers: {\n 'Content-Type': 'application/json',\n ...this.options.fetchOptions?.headers,\n },\n body,\n });\n } catch (error) {\n this.logger.error(LOG_PREFIX, `callStream failed: capabilityId=${capabilityId}, action=${action}`, { params: requestParams, error });\n slardar.captureException(error, { source: 'client-capability', module: 'execute-call-stream', capabilityId, action, phase: 'fetch' });\n throw new NetworkError(error instanceof Error ? error.message : String(error));\n }\n\n if (!response.ok) {\n const errMsg = `HTTP ${response.status} ${response.statusText}`;\n this.logger.error(LOG_PREFIX, `callStream failed: capabilityId=${capabilityId}, action=${action}`, { params: requestParams, error: errMsg });\n throw new NetworkError(errMsg);\n }\n\n if (!response.body) {\n const errMsg = 'Response body is null';\n this.logger.error(LOG_PREFIX, `callStream failed: capabilityId=${capabilityId}, action=${action}`, { params: requestParams, error: errMsg });\n throw new NetworkError(errMsg);\n }\n\n const reader = response.body.getReader();\n const decoder = new TextDecoder();\n let buffer = '';\n let chunkCount = 0;\n let aggregatedContent = '';\n let canAggregate = true;\n\n try {\n while (true) {\n const { done, value } = await reader.read();\n if (done) break;\n\n buffer += decoder.decode(value, { stream: true });\n const lines = buffer.split('\\n');\n buffer = lines.pop() ?? '';\n\n for (const line of lines) {\n if (line.startsWith('data: ')) {\n const data = line.slice(6);\n\n try {\n const parsed = JSON.parse(data) as StreamResponse;\n\n if (parsed.status_code === ErrorCodes.SUCCESS && parsed.data) {\n if (parsed.data.type === 'content') {\n chunkCount++;\n const delta = parsed.data.delta as T;\n\n // 尝试聚合 content 字段\n if (canAggregate) {\n const content = (delta as Record<string, unknown>)?.content;\n if (typeof content === 'string') {\n aggregatedContent += content;\n } else {\n canAggregate = false;\n }\n }\n\n yield delta;\n\n if (parsed.data.finished) {\n const resultInfo = canAggregate\n ? { chunkCount, result: aggregatedContent }\n : { chunkCount, resultLength: aggregatedContent.length || chunkCount };\n this.logger.info(LOG_PREFIX, `callStream end: capabilityId=${capabilityId}, action=${action}`, resultInfo);\n return;\n }\n } else if (parsed.data.type === 'error') {\n const err = parsed.data.error;\n if (err.isRateLimitError) {\n const rateLimitError = new RateLimitError(\n err.code, // code 就是业务错误码\n err.message,\n );\n await this.onRateLimitError?.(rateLimitError);\n throw rateLimitError;\n }\n throw new ExecutionError(err.message);\n }\n }\n } catch (parseError) {\n if (parseError instanceof CapabilityError) {\n this.logger.error(LOG_PREFIX, `callStream failed: capabilityId=${capabilityId}, action=${action}`, { params: requestParams, error: parseError, chunkCount });\n throw parseError;\n }\n // 忽略非 JSON 行\n }\n }\n }\n }\n const resultInfo = canAggregate\n ? { chunkCount, result: aggregatedContent }\n : { chunkCount, resultLength: aggregatedContent.length || chunkCount };\n this.logger.info(LOG_PREFIX, `callStream end: capabilityId=${capabilityId}, action=${action}`, resultInfo);\n } catch (error) {\n this.logger.error(LOG_PREFIX, `callStream failed: capabilityId=${capabilityId}, action=${action}`, { params: requestParams, error, chunkCount });\n slardar.captureException(error, { source: 'client-capability', module: 'execute-call-stream', capabilityId, action, phase: 'read' });\n if (error instanceof CapabilityError) {\n throw error;\n }\n throw new NetworkError(error instanceof Error ? error.message : String(error));\n } finally {\n reader.releaseLock();\n }\n }\n}\n\n/**\n * 创建客户端实例\n */\nexport function createClient(options: CapabilityClientOptions): CapabilityClient {\n return new CapabilityClient(options);\n}\n"],"mappings":"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","names":["ErrorCodes","SUCCESS","CAPABILITY_NOT_FOUND","PLUGIN_NOT_FOUND","ACTION_NOT_FOUND","PARAMS_VALIDATION_ERROR","EXECUTION_ERROR","RATE_LIMIT_EXCEEDED","CapabilityError","Error","message","code","statusCode","name","CapabilityNotFoundError","capabilityId","ActionNotFoundError","NetworkError","ExecutionError","FileUploadError","RateLimitError","rateLimitCode","rateLimitMessage","slardar","FileUploader","options","upload","file","fileName","File","name","Date","now","uploadURL","objectKey","fetchUploadUrl","uploadToTos","downloadUrl","fetchDownloadUrl","uploadAll","files","results","Promise","all","map","path","acquireUploadUrl","fetchOptions","response","fetch","method","headers","body","JSON","stringify","error","slardar","captureException","source","module","step","FileUploadError","Error","message","String","ok","status","data","json","status_code","acquireDownloadUrl","downloadURL","isFile","value","File","Blob","extractFiles","params","files","traverse","obj","path","push","file","Array","isArray","forEach","item","index","Object","entries","key","replaceFilesWithUrls","results","urlMap","Map","downloadUrl","set","JSON","stringify","cloneAndReplace","currentPath","pathKey","has","get","map","result","slardar","virtualCapabilitiesMap","LOG_PREFIX","defaultLogger","debug","console","bind","info","warn","error","CapabilityClient","options","baseURL","logger","onRateLimitError","central","replace","enabled","resolveCentralBaseURL","window","location","origin","Error","resolveMode","env","process","undefined","buildExecuteUrl","capabilityId","stream","base","segment","appId","buildRequestBody","action","params","capability","virtualCapabilitiesMap","CapabilityNotFoundError","JSON","stringify","load","createExecutor","call","executeCall","callStream","executeCallStream","url","requestParams","extractedFiles","extractFiles","length","uploader","FileUploader","uploadResults","uploadAll","replaceFilesWithUrls","response","fetch","method","fetchOptions","headers","body","data","json","ok","status_code","ErrorCodes","SUCCESS","errorResponse","errorCode","is_rate_limit_error","rateLimitError","RateLimitError","error_msg","status","CAPABILITY_NOT_FOUND","ACTION_NOT_FOUND","ActionNotFoundError","PLUGIN_NOT_FOUND","EXECUTION_ERROR","ExecutionError","result","output","slardar","captureException","source","module","CapabilityError","NetworkError","message","String","phase","errMsg","statusText","reader","getReader","decoder","TextDecoder","buffer","chunkCount","aggregatedContent","canAggregate","done","value","read","decode","lines","split","pop","line","startsWith","slice","parsed","parse","type","delta","content","finished","resultInfo","resultLength","err","isRateLimitError","code","parseError","releaseLock","createClient"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lark-apaas/client-capability",
3
- "version": "0.1.5",
3
+ "version": "0.1.7-alpha.0",
4
4
  "description": "Client SDK for calling capabilities",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",
@@ -35,9 +35,12 @@
35
35
  "lint": "echo 'ESLint skipped'",
36
36
  "prepublishOnly": "npm run build"
37
37
  },
38
+ "dependencies": {
39
+ "@lark-apaas/internal-slardar": "^0.0.3"
40
+ },
38
41
  "devDependencies": {
39
42
  "tsup": "^8.0.0",
40
43
  "typescript": "^5.0.0",
41
- "vitest": "^1.6.0"
44
+ "vitest": "^3.2.4"
42
45
  }
43
46
  }