@lark-apaas/client-capability 0.1.3 → 0.1.4

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
@@ -30,6 +30,7 @@ __export(index_exports, {
30
30
  ExecutionError: () => ExecutionError,
31
31
  FileUploadError: () => FileUploadError,
32
32
  NetworkError: () => NetworkError,
33
+ RateLimitError: () => RateLimitError,
33
34
  createClient: () => createClient
34
35
  });
35
36
  module.exports = __toCommonJS(index_exports);
@@ -41,7 +42,8 @@ var ErrorCodes = {
41
42
  PLUGIN_NOT_FOUND: "k_ec_cap_002",
42
43
  ACTION_NOT_FOUND: "k_ec_cap_003",
43
44
  PARAMS_VALIDATION_ERROR: "k_ec_cap_004",
44
- EXECUTION_ERROR: "k_ec_cap_005"
45
+ EXECUTION_ERROR: "k_ec_cap_005",
46
+ RATE_LIMIT_EXCEEDED: "k_ec_cap_006"
45
47
  };
46
48
  var UPLOAD_API_PATH = "/af/api/v1/studio/plugins/tmp_files";
47
49
 
@@ -100,6 +102,20 @@ var _FileUploadError = class _FileUploadError extends CapabilityError {
100
102
  };
101
103
  __name(_FileUploadError, "FileUploadError");
102
104
  var FileUploadError = _FileUploadError;
105
+ var _RateLimitError = class _RateLimitError extends CapabilityError {
106
+ constructor(rateLimitCode, rateLimitMessage, statusCode) {
107
+ super(rateLimitMessage, "RATE_LIMIT_EXCEEDED", statusCode);
108
+ /** 业务错误码 */
109
+ __publicField(this, "rateLimitCode");
110
+ /** 业务错误消息 */
111
+ __publicField(this, "rateLimitMessage");
112
+ this.name = "RateLimitError";
113
+ this.rateLimitCode = rateLimitCode;
114
+ this.rateLimitMessage = rateLimitMessage;
115
+ }
116
+ };
117
+ __name(_RateLimitError, "RateLimitError");
118
+ var RateLimitError = _RateLimitError;
103
119
 
104
120
  // src/uploader.ts
105
121
  var _FileUploader = class _FileUploader {
@@ -294,9 +310,11 @@ var _CapabilityClient = class _CapabilityClient {
294
310
  __publicField(this, "options");
295
311
  __publicField(this, "baseURL");
296
312
  __publicField(this, "logger");
313
+ __publicField(this, "onRateLimitError");
297
314
  this.options = options ?? {};
298
315
  this.baseURL = (options?.baseURL ?? "").replace(/\/+$/, "");
299
316
  this.logger = options?.logger ?? defaultLogger;
317
+ this.onRateLimitError = options?.onRateLimitError;
300
318
  }
301
319
  /**
302
320
  * 加载能力,返回执行器
@@ -347,6 +365,11 @@ var _CapabilityClient = class _CapabilityClient {
347
365
  if (!response.ok || data.status_code !== ErrorCodes.SUCCESS) {
348
366
  const errorResponse = data;
349
367
  const errorCode = errorResponse.status_code;
368
+ if (errorResponse.is_rate_limit_error) {
369
+ const rateLimitError = new RateLimitError(errorCode, errorResponse.error_msg, response.status);
370
+ await this.onRateLimitError?.(rateLimitError);
371
+ throw rateLimitError;
372
+ }
350
373
  switch (errorCode) {
351
374
  case ErrorCodes.CAPABILITY_NOT_FOUND:
352
375
  throw new CapabilityNotFoundError(capabilityId, response.status);
@@ -470,7 +493,13 @@ var _CapabilityClient = class _CapabilityClient {
470
493
  return;
471
494
  }
472
495
  } else if (parsed.data.type === "error") {
473
- throw new ExecutionError(parsed.data.error.message);
496
+ const err = parsed.data.error;
497
+ if (err.isRateLimitError) {
498
+ const rateLimitError = new RateLimitError(err.code, err.message);
499
+ await this.onRateLimitError?.(rateLimitError);
500
+ throw rateLimitError;
501
+ }
502
+ throw new ExecutionError(err.message);
474
503
  }
475
504
  }
476
505
  } catch (parseError) {
@@ -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 } from './types';\n\n// 错误类型\nexport {\n CapabilityError,\n CapabilityNotFoundError,\n ActionNotFoundError,\n NetworkError,\n ExecutionError,\n FileUploadError,\n} from './errors';\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 * 客户端配置选项\n */\nexport interface CapabilityClientOptions {\n /** 全局路径前缀,默认 ''。例如线上环境可设置为 '/spark/a' */\n baseURL?: string;\n /** 自定义 fetch 配置 */\n fetchOptions?: RequestInit;\n /** 自定义 logger,默认使用 console */\n logger?: Logger;\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\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 code: number;\n message: string;\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} as const;\n\n// ========== 文件上传相关类型 ==========\n\n/**\n * 文件上传接口 BasePath\n */\nexport const UPLOAD_API_PATH = '/af/api/v1/studio/plugins/tmp_files';\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","import type {\n ExtractedFile,\n UploadResult,\n AcquireUploadUrlResponse,\n AcquireDownloadUrlResponse,\n} from './types';\nimport { UPLOAD_API_PATH } from './types';\nimport { FileUploadError } from './errors';\n\n/**\n * 文件上传器\n */\nexport class FileUploader {\n private readonly fetchOptions?: RequestInit;\n\n constructor(fetchOptions?: RequestInit) {\n this.fetchOptions = fetchOptions;\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.acquireUploadUrl(fileName);\n\n // 2. 上传文件到 TOS\n await this.uploadToTos(uploadURL, file);\n\n // 3. 获取下载 URL\n const downloadUrl = await this.acquireDownloadUrl(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 acquireUploadUrl(fileName: string): Promise<{ uploadURL: string; objectKey: string }> {\n const url = `${UPLOAD_API_PATH}/acquire_upload_url`;\n\n let response: Response;\n try {\n response = await fetch(url, {\n method: 'POST',\n ...this.fetchOptions,\n headers: {\n 'Content-Type': 'application/json',\n ...this.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 acquireDownloadUrl(objectKey: string): Promise<string> {\n const url = `${UPLOAD_API_PATH}/acquire_download_url`;\n\n let response: Response;\n try {\n response = await fetch(url, {\n method: 'POST',\n ...this.fetchOptions,\n headers: {\n 'Content-Type': 'application/json',\n ...this.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} from './types';\nimport { ErrorCodes } from './types';\nimport {\n CapabilityError,\n CapabilityNotFoundError,\n ActionNotFoundError,\n NetworkError,\n ExecutionError,\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\n constructor(options?: CapabilityClientOptions) {\n this.options = options ?? {};\n // 移除末尾的斜杠,确保拼接时格式正确\n this.baseURL = (options?.baseURL ?? '').replace(/\\/+$/, '');\n this.logger = options?.logger ?? defaultLogger;\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.fetchOptions);\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 { status_code: string; error_msg: string };\n const errorCode = errorResponse.status_code;\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.fetchOptions);\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 throw new ExecutionError(parsed.data.error.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","UPLOAD_API_PATH","CapabilityError","Error","message","code","statusCode","name","CapabilityNotFoundError","capabilityId","ActionNotFoundError","NetworkError","ExecutionError","FileUploadError","FileUploader","fetchOptions","upload","file","fileName","File","name","Date","now","uploadURL","objectKey","acquireUploadUrl","uploadToTos","downloadUrl","acquireDownloadUrl","uploadAll","files","results","Promise","all","map","path","url","UPLOAD_API_PATH","response","fetch","method","headers","body","JSON","stringify","error","FileUploadError","Error","message","String","ok","status","data","json","status_code","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","replace","load","capabilityId","createExecutor","call","action","params","executeCall","callStream","executeCallStream","url","requestParams","extractedFiles","extractFiles","length","uploader","FileUploader","fetchOptions","uploadResults","uploadAll","replaceFilesWithUrls","response","fetch","method","headers","body","JSON","stringify","data","json","ok","status_code","ErrorCodes","SUCCESS","errorResponse","errorCode","CAPABILITY_NOT_FOUND","CapabilityNotFoundError","status","ACTION_NOT_FOUND","ActionNotFoundError","error_msg","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","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 { 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 /** 自定义 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 * 文件上传接口 BasePath\n */\nexport const UPLOAD_API_PATH = '/af/api/v1/studio/plugins/tmp_files';\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 ExtractedFile,\n UploadResult,\n AcquireUploadUrlResponse,\n AcquireDownloadUrlResponse,\n} from './types';\nimport { UPLOAD_API_PATH } from './types';\nimport { FileUploadError } from './errors';\n\n/**\n * 文件上传器\n */\nexport class FileUploader {\n private readonly fetchOptions?: RequestInit;\n\n constructor(fetchOptions?: RequestInit) {\n this.fetchOptions = fetchOptions;\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.acquireUploadUrl(fileName);\n\n // 2. 上传文件到 TOS\n await this.uploadToTos(uploadURL, file);\n\n // 3. 获取下载 URL\n const downloadUrl = await this.acquireDownloadUrl(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 acquireUploadUrl(fileName: string): Promise<{ uploadURL: string; objectKey: string }> {\n const url = `${UPLOAD_API_PATH}/acquire_upload_url`;\n\n let response: Response;\n try {\n response = await fetch(url, {\n method: 'POST',\n ...this.fetchOptions,\n headers: {\n 'Content-Type': 'application/json',\n ...this.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 acquireDownloadUrl(objectKey: string): Promise<string> {\n const url = `${UPLOAD_API_PATH}/acquire_download_url`;\n\n let response: Response;\n try {\n response = await fetch(url, {\n method: 'POST',\n ...this.fetchOptions,\n headers: {\n 'Content-Type': 'application/json',\n ...this.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.fetchOptions);\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.fetchOptions);\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","UPLOAD_API_PATH","CapabilityError","Error","message","code","statusCode","name","CapabilityNotFoundError","capabilityId","ActionNotFoundError","NetworkError","ExecutionError","FileUploadError","RateLimitError","rateLimitCode","rateLimitMessage","FileUploader","fetchOptions","upload","file","fileName","File","name","Date","now","uploadURL","objectKey","acquireUploadUrl","uploadToTos","downloadUrl","acquireDownloadUrl","uploadAll","files","results","Promise","all","map","path","url","UPLOAD_API_PATH","response","fetch","method","headers","body","JSON","stringify","error","FileUploadError","Error","message","String","ok","status","data","json","status_code","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","fetchOptions","uploadResults","uploadAll","replaceFilesWithUrls","response","fetch","method","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"]}
package/dist/index.d.cts CHANGED
@@ -1,3 +1,54 @@
1
+ /**
2
+ * 能力调用错误基类
3
+ */
4
+ declare class CapabilityError extends Error {
5
+ /** 错误码 */
6
+ code: string;
7
+ /** HTTP 状态码 */
8
+ statusCode?: number;
9
+ constructor(message: string, code: string, statusCode?: number);
10
+ }
11
+ /**
12
+ * 能力不存在错误
13
+ */
14
+ declare class CapabilityNotFoundError extends CapabilityError {
15
+ constructor(capabilityId: string, statusCode?: number);
16
+ }
17
+ /**
18
+ * Action 不存在错误
19
+ */
20
+ declare class ActionNotFoundError extends CapabilityError {
21
+ constructor(message: string, statusCode?: number);
22
+ }
23
+ /**
24
+ * 网络错误
25
+ */
26
+ declare class NetworkError extends CapabilityError {
27
+ constructor(message: string);
28
+ }
29
+ /**
30
+ * 执行错误
31
+ */
32
+ declare class ExecutionError extends CapabilityError {
33
+ constructor(message: string, statusCode?: number);
34
+ }
35
+ /**
36
+ * 文件上传错误
37
+ */
38
+ declare class FileUploadError extends CapabilityError {
39
+ constructor(message: string, statusCode?: number);
40
+ }
41
+ /**
42
+ * 计费受限错误
43
+ */
44
+ declare class RateLimitError extends CapabilityError {
45
+ /** 业务错误码 */
46
+ rateLimitCode: string;
47
+ /** 业务错误消息 */
48
+ rateLimitMessage: string;
49
+ constructor(rateLimitCode: string, rateLimitMessage: string, statusCode?: number);
50
+ }
51
+
1
52
  /**
2
53
  * Logger 接口,兼容 console 和 @lark-apaas/toolkit 的 logger
3
54
  */
@@ -7,6 +58,11 @@ interface Logger {
7
58
  warn(message: unknown, ...args: unknown[]): void;
8
59
  error(message: unknown, ...args: unknown[]): void;
9
60
  }
61
+ /**
62
+ * RateLimitError 钩子函数类型
63
+ * 当检测到计费受限错误时,在抛出异常前调用此钩子
64
+ */
65
+ type RateLimitErrorHook = (error: RateLimitError) => void | Promise<void>;
10
66
  /**
11
67
  * 客户端配置选项
12
68
  */
@@ -17,6 +73,12 @@ interface CapabilityClientOptions {
17
73
  fetchOptions?: RequestInit;
18
74
  /** 自定义 logger,默认使用 console */
19
75
  logger?: Logger;
76
+ /**
77
+ * 计费受限错误钩子
78
+ * 当检测到 RateLimitError 时,在抛出异常前调用此钩子
79
+ * 可用于上报埋点、展示 toast 等场景
80
+ */
81
+ onRateLimitError?: RateLimitErrorHook;
20
82
  }
21
83
  /**
22
84
  * 能力执行器接口
@@ -45,6 +107,7 @@ declare class CapabilityClient {
45
107
  private options;
46
108
  private baseURL;
47
109
  private logger;
110
+ private onRateLimitError?;
48
111
  constructor(options?: CapabilityClientOptions);
49
112
  /**
50
113
  * 加载能力,返回执行器
@@ -61,45 +124,4 @@ declare class CapabilityClient {
61
124
  */
62
125
  declare function createClient(options?: CapabilityClientOptions): CapabilityClient;
63
126
 
64
- /**
65
- * 能力调用错误基类
66
- */
67
- declare class CapabilityError extends Error {
68
- /** 错误码 */
69
- code: string;
70
- /** HTTP 状态码 */
71
- statusCode?: number;
72
- constructor(message: string, code: string, statusCode?: number);
73
- }
74
- /**
75
- * 能力不存在错误
76
- */
77
- declare class CapabilityNotFoundError extends CapabilityError {
78
- constructor(capabilityId: string, statusCode?: number);
79
- }
80
- /**
81
- * Action 不存在错误
82
- */
83
- declare class ActionNotFoundError extends CapabilityError {
84
- constructor(message: string, statusCode?: number);
85
- }
86
- /**
87
- * 网络错误
88
- */
89
- declare class NetworkError extends CapabilityError {
90
- constructor(message: string);
91
- }
92
- /**
93
- * 执行错误
94
- */
95
- declare class ExecutionError extends CapabilityError {
96
- constructor(message: string, statusCode?: number);
97
- }
98
- /**
99
- * 文件上传错误
100
- */
101
- declare class FileUploadError extends CapabilityError {
102
- constructor(message: string, statusCode?: number);
103
- }
104
-
105
- export { ActionNotFoundError, CapabilityClient, type CapabilityClientOptions, CapabilityError, type CapabilityExecutor, CapabilityNotFoundError, ExecutionError, FileUploadError, type Logger, NetworkError, createClient };
127
+ export { ActionNotFoundError, CapabilityClient, type CapabilityClientOptions, CapabilityError, type CapabilityExecutor, CapabilityNotFoundError, ExecutionError, FileUploadError, type Logger, NetworkError, RateLimitError, type RateLimitErrorHook, createClient };
package/dist/index.d.ts CHANGED
@@ -1,3 +1,54 @@
1
+ /**
2
+ * 能力调用错误基类
3
+ */
4
+ declare class CapabilityError extends Error {
5
+ /** 错误码 */
6
+ code: string;
7
+ /** HTTP 状态码 */
8
+ statusCode?: number;
9
+ constructor(message: string, code: string, statusCode?: number);
10
+ }
11
+ /**
12
+ * 能力不存在错误
13
+ */
14
+ declare class CapabilityNotFoundError extends CapabilityError {
15
+ constructor(capabilityId: string, statusCode?: number);
16
+ }
17
+ /**
18
+ * Action 不存在错误
19
+ */
20
+ declare class ActionNotFoundError extends CapabilityError {
21
+ constructor(message: string, statusCode?: number);
22
+ }
23
+ /**
24
+ * 网络错误
25
+ */
26
+ declare class NetworkError extends CapabilityError {
27
+ constructor(message: string);
28
+ }
29
+ /**
30
+ * 执行错误
31
+ */
32
+ declare class ExecutionError extends CapabilityError {
33
+ constructor(message: string, statusCode?: number);
34
+ }
35
+ /**
36
+ * 文件上传错误
37
+ */
38
+ declare class FileUploadError extends CapabilityError {
39
+ constructor(message: string, statusCode?: number);
40
+ }
41
+ /**
42
+ * 计费受限错误
43
+ */
44
+ declare class RateLimitError extends CapabilityError {
45
+ /** 业务错误码 */
46
+ rateLimitCode: string;
47
+ /** 业务错误消息 */
48
+ rateLimitMessage: string;
49
+ constructor(rateLimitCode: string, rateLimitMessage: string, statusCode?: number);
50
+ }
51
+
1
52
  /**
2
53
  * Logger 接口,兼容 console 和 @lark-apaas/toolkit 的 logger
3
54
  */
@@ -7,6 +58,11 @@ interface Logger {
7
58
  warn(message: unknown, ...args: unknown[]): void;
8
59
  error(message: unknown, ...args: unknown[]): void;
9
60
  }
61
+ /**
62
+ * RateLimitError 钩子函数类型
63
+ * 当检测到计费受限错误时,在抛出异常前调用此钩子
64
+ */
65
+ type RateLimitErrorHook = (error: RateLimitError) => void | Promise<void>;
10
66
  /**
11
67
  * 客户端配置选项
12
68
  */
@@ -17,6 +73,12 @@ interface CapabilityClientOptions {
17
73
  fetchOptions?: RequestInit;
18
74
  /** 自定义 logger,默认使用 console */
19
75
  logger?: Logger;
76
+ /**
77
+ * 计费受限错误钩子
78
+ * 当检测到 RateLimitError 时,在抛出异常前调用此钩子
79
+ * 可用于上报埋点、展示 toast 等场景
80
+ */
81
+ onRateLimitError?: RateLimitErrorHook;
20
82
  }
21
83
  /**
22
84
  * 能力执行器接口
@@ -45,6 +107,7 @@ declare class CapabilityClient {
45
107
  private options;
46
108
  private baseURL;
47
109
  private logger;
110
+ private onRateLimitError?;
48
111
  constructor(options?: CapabilityClientOptions);
49
112
  /**
50
113
  * 加载能力,返回执行器
@@ -61,45 +124,4 @@ declare class CapabilityClient {
61
124
  */
62
125
  declare function createClient(options?: CapabilityClientOptions): CapabilityClient;
63
126
 
64
- /**
65
- * 能力调用错误基类
66
- */
67
- declare class CapabilityError extends Error {
68
- /** 错误码 */
69
- code: string;
70
- /** HTTP 状态码 */
71
- statusCode?: number;
72
- constructor(message: string, code: string, statusCode?: number);
73
- }
74
- /**
75
- * 能力不存在错误
76
- */
77
- declare class CapabilityNotFoundError extends CapabilityError {
78
- constructor(capabilityId: string, statusCode?: number);
79
- }
80
- /**
81
- * Action 不存在错误
82
- */
83
- declare class ActionNotFoundError extends CapabilityError {
84
- constructor(message: string, statusCode?: number);
85
- }
86
- /**
87
- * 网络错误
88
- */
89
- declare class NetworkError extends CapabilityError {
90
- constructor(message: string);
91
- }
92
- /**
93
- * 执行错误
94
- */
95
- declare class ExecutionError extends CapabilityError {
96
- constructor(message: string, statusCode?: number);
97
- }
98
- /**
99
- * 文件上传错误
100
- */
101
- declare class FileUploadError extends CapabilityError {
102
- constructor(message: string, statusCode?: number);
103
- }
104
-
105
- export { ActionNotFoundError, CapabilityClient, type CapabilityClientOptions, CapabilityError, type CapabilityExecutor, CapabilityNotFoundError, ExecutionError, FileUploadError, type Logger, NetworkError, createClient };
127
+ export { ActionNotFoundError, CapabilityClient, type CapabilityClientOptions, CapabilityError, type CapabilityExecutor, CapabilityNotFoundError, ExecutionError, FileUploadError, type Logger, NetworkError, RateLimitError, type RateLimitErrorHook, createClient };
package/dist/index.js CHANGED
@@ -10,7 +10,8 @@ var ErrorCodes = {
10
10
  PLUGIN_NOT_FOUND: "k_ec_cap_002",
11
11
  ACTION_NOT_FOUND: "k_ec_cap_003",
12
12
  PARAMS_VALIDATION_ERROR: "k_ec_cap_004",
13
- EXECUTION_ERROR: "k_ec_cap_005"
13
+ EXECUTION_ERROR: "k_ec_cap_005",
14
+ RATE_LIMIT_EXCEEDED: "k_ec_cap_006"
14
15
  };
15
16
  var UPLOAD_API_PATH = "/af/api/v1/studio/plugins/tmp_files";
16
17
 
@@ -69,6 +70,20 @@ var _FileUploadError = class _FileUploadError extends CapabilityError {
69
70
  };
70
71
  __name(_FileUploadError, "FileUploadError");
71
72
  var FileUploadError = _FileUploadError;
73
+ var _RateLimitError = class _RateLimitError extends CapabilityError {
74
+ constructor(rateLimitCode, rateLimitMessage, statusCode) {
75
+ super(rateLimitMessage, "RATE_LIMIT_EXCEEDED", statusCode);
76
+ /** 业务错误码 */
77
+ __publicField(this, "rateLimitCode");
78
+ /** 业务错误消息 */
79
+ __publicField(this, "rateLimitMessage");
80
+ this.name = "RateLimitError";
81
+ this.rateLimitCode = rateLimitCode;
82
+ this.rateLimitMessage = rateLimitMessage;
83
+ }
84
+ };
85
+ __name(_RateLimitError, "RateLimitError");
86
+ var RateLimitError = _RateLimitError;
72
87
 
73
88
  // src/uploader.ts
74
89
  var _FileUploader = class _FileUploader {
@@ -263,9 +278,11 @@ var _CapabilityClient = class _CapabilityClient {
263
278
  __publicField(this, "options");
264
279
  __publicField(this, "baseURL");
265
280
  __publicField(this, "logger");
281
+ __publicField(this, "onRateLimitError");
266
282
  this.options = options ?? {};
267
283
  this.baseURL = (options?.baseURL ?? "").replace(/\/+$/, "");
268
284
  this.logger = options?.logger ?? defaultLogger;
285
+ this.onRateLimitError = options?.onRateLimitError;
269
286
  }
270
287
  /**
271
288
  * 加载能力,返回执行器
@@ -316,6 +333,11 @@ var _CapabilityClient = class _CapabilityClient {
316
333
  if (!response.ok || data.status_code !== ErrorCodes.SUCCESS) {
317
334
  const errorResponse = data;
318
335
  const errorCode = errorResponse.status_code;
336
+ if (errorResponse.is_rate_limit_error) {
337
+ const rateLimitError = new RateLimitError(errorCode, errorResponse.error_msg, response.status);
338
+ await this.onRateLimitError?.(rateLimitError);
339
+ throw rateLimitError;
340
+ }
319
341
  switch (errorCode) {
320
342
  case ErrorCodes.CAPABILITY_NOT_FOUND:
321
343
  throw new CapabilityNotFoundError(capabilityId, response.status);
@@ -439,7 +461,13 @@ var _CapabilityClient = class _CapabilityClient {
439
461
  return;
440
462
  }
441
463
  } else if (parsed.data.type === "error") {
442
- throw new ExecutionError(parsed.data.error.message);
464
+ const err = parsed.data.error;
465
+ if (err.isRateLimitError) {
466
+ const rateLimitError = new RateLimitError(err.code, err.message);
467
+ await this.onRateLimitError?.(rateLimitError);
468
+ throw rateLimitError;
469
+ }
470
+ throw new ExecutionError(err.message);
443
471
  }
444
472
  }
445
473
  } catch (parseError) {
@@ -492,6 +520,7 @@ export {
492
520
  ExecutionError,
493
521
  FileUploadError,
494
522
  NetworkError,
523
+ RateLimitError,
495
524
  createClient
496
525
  };
497
526
  //# sourceMappingURL=index.js.map
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":["/**\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 * 客户端配置选项\n */\nexport interface CapabilityClientOptions {\n /** 全局路径前缀,默认 ''。例如线上环境可设置为 '/spark/a' */\n baseURL?: string;\n /** 自定义 fetch 配置 */\n fetchOptions?: RequestInit;\n /** 自定义 logger,默认使用 console */\n logger?: Logger;\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\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 code: number;\n message: string;\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} as const;\n\n// ========== 文件上传相关类型 ==========\n\n/**\n * 文件上传接口 BasePath\n */\nexport const UPLOAD_API_PATH = '/af/api/v1/studio/plugins/tmp_files';\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","import type {\n ExtractedFile,\n UploadResult,\n AcquireUploadUrlResponse,\n AcquireDownloadUrlResponse,\n} from './types';\nimport { UPLOAD_API_PATH } from './types';\nimport { FileUploadError } from './errors';\n\n/**\n * 文件上传器\n */\nexport class FileUploader {\n private readonly fetchOptions?: RequestInit;\n\n constructor(fetchOptions?: RequestInit) {\n this.fetchOptions = fetchOptions;\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.acquireUploadUrl(fileName);\n\n // 2. 上传文件到 TOS\n await this.uploadToTos(uploadURL, file);\n\n // 3. 获取下载 URL\n const downloadUrl = await this.acquireDownloadUrl(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 acquireUploadUrl(fileName: string): Promise<{ uploadURL: string; objectKey: string }> {\n const url = `${UPLOAD_API_PATH}/acquire_upload_url`;\n\n let response: Response;\n try {\n response = await fetch(url, {\n method: 'POST',\n ...this.fetchOptions,\n headers: {\n 'Content-Type': 'application/json',\n ...this.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 acquireDownloadUrl(objectKey: string): Promise<string> {\n const url = `${UPLOAD_API_PATH}/acquire_download_url`;\n\n let response: Response;\n try {\n response = await fetch(url, {\n method: 'POST',\n ...this.fetchOptions,\n headers: {\n 'Content-Type': 'application/json',\n ...this.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} from './types';\nimport { ErrorCodes } from './types';\nimport {\n CapabilityError,\n CapabilityNotFoundError,\n ActionNotFoundError,\n NetworkError,\n ExecutionError,\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\n constructor(options?: CapabilityClientOptions) {\n this.options = options ?? {};\n // 移除末尾的斜杠,确保拼接时格式正确\n this.baseURL = (options?.baseURL ?? '').replace(/\\/+$/, '');\n this.logger = options?.logger ?? defaultLogger;\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.fetchOptions);\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 { status_code: string; error_msg: string };\n const errorCode = errorResponse.status_code;\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.fetchOptions);\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 throw new ExecutionError(parsed.data.error.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","UPLOAD_API_PATH","CapabilityError","Error","message","code","statusCode","name","CapabilityNotFoundError","capabilityId","ActionNotFoundError","NetworkError","ExecutionError","FileUploadError","FileUploader","fetchOptions","upload","file","fileName","File","name","Date","now","uploadURL","objectKey","acquireUploadUrl","uploadToTos","downloadUrl","acquireDownloadUrl","uploadAll","files","results","Promise","all","map","path","url","UPLOAD_API_PATH","response","fetch","method","headers","body","JSON","stringify","error","FileUploadError","Error","message","String","ok","status","data","json","status_code","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","replace","load","capabilityId","createExecutor","call","action","params","executeCall","callStream","executeCallStream","url","requestParams","extractedFiles","extractFiles","length","uploader","FileUploader","fetchOptions","uploadResults","uploadAll","replaceFilesWithUrls","response","fetch","method","headers","body","JSON","stringify","data","json","ok","status_code","ErrorCodes","SUCCESS","errorResponse","errorCode","CAPABILITY_NOT_FOUND","CapabilityNotFoundError","status","ACTION_NOT_FOUND","ActionNotFoundError","error_msg","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","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 /** 自定义 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 * 文件上传接口 BasePath\n */\nexport const UPLOAD_API_PATH = '/af/api/v1/studio/plugins/tmp_files';\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 ExtractedFile,\n UploadResult,\n AcquireUploadUrlResponse,\n AcquireDownloadUrlResponse,\n} from './types';\nimport { UPLOAD_API_PATH } from './types';\nimport { FileUploadError } from './errors';\n\n/**\n * 文件上传器\n */\nexport class FileUploader {\n private readonly fetchOptions?: RequestInit;\n\n constructor(fetchOptions?: RequestInit) {\n this.fetchOptions = fetchOptions;\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.acquireUploadUrl(fileName);\n\n // 2. 上传文件到 TOS\n await this.uploadToTos(uploadURL, file);\n\n // 3. 获取下载 URL\n const downloadUrl = await this.acquireDownloadUrl(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 acquireUploadUrl(fileName: string): Promise<{ uploadURL: string; objectKey: string }> {\n const url = `${UPLOAD_API_PATH}/acquire_upload_url`;\n\n let response: Response;\n try {\n response = await fetch(url, {\n method: 'POST',\n ...this.fetchOptions,\n headers: {\n 'Content-Type': 'application/json',\n ...this.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 acquireDownloadUrl(objectKey: string): Promise<string> {\n const url = `${UPLOAD_API_PATH}/acquire_download_url`;\n\n let response: Response;\n try {\n response = await fetch(url, {\n method: 'POST',\n ...this.fetchOptions,\n headers: {\n 'Content-Type': 'application/json',\n ...this.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.fetchOptions);\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.fetchOptions);\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","UPLOAD_API_PATH","CapabilityError","Error","message","code","statusCode","name","CapabilityNotFoundError","capabilityId","ActionNotFoundError","NetworkError","ExecutionError","FileUploadError","RateLimitError","rateLimitCode","rateLimitMessage","FileUploader","fetchOptions","upload","file","fileName","File","name","Date","now","uploadURL","objectKey","acquireUploadUrl","uploadToTos","downloadUrl","acquireDownloadUrl","uploadAll","files","results","Promise","all","map","path","url","UPLOAD_API_PATH","response","fetch","method","headers","body","JSON","stringify","error","FileUploadError","Error","message","String","ok","status","data","json","status_code","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","fetchOptions","uploadResults","uploadAll","replaceFilesWithUrls","response","fetch","method","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"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lark-apaas/client-capability",
3
- "version": "0.1.3",
3
+ "version": "0.1.4",
4
4
  "description": "Client SDK for calling capabilities",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",