@tacoreai/web-sdk 1.0.12 → 1.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,215 @@
1
+ import { isBrowser, isBackend } from "../../../utils/index.js";
2
+ import { BaseAppsClient } from "./BaseAppsClient.js";
3
+ import { VolcengineImpl } from "./AppsClient.Volcengine.js";
4
+ import { appsClientAppServerMethods } from "./AppsClient.AppServer.js";
5
+ import { appsClientDataMethods } from "./AppsClient.Data.js";
6
+ import { appsClientEcommerceMethods } from "./AppsClient.Ecommerce.js";
7
+ import { appsClientUserMethods } from "./AppsClient.Users.js";
8
+ import { appsClientRealtimeMethods } from "./AppsClient.Realtime.js";
9
+ import { appsClientMessageMethods } from "./AppsClient.Message.js";
10
+ import { appsClientKnowledgeBaseMethods } from "./AppsClient.KnowledgeBase.js";
11
+ import { appsClientDeployMethods } from "./AppsClient.Deploy.js";
12
+ import { appsClientAiMethods } from "./AppsClient.AI.js";
13
+ import { appsClientVoiceVideoMethods } from "./AppsClient.VoiceVideo.js";
14
+ import { appsClientStorageMethods } from "./AppsClient.Storage.js";
15
+ import { appsClientKvMethods } from "./AppsClient.KV.js";
16
+ import { appsClientTextInMethods } from "./AppsClient.TextIn.js";
17
+ import { appsClientToolsMethods } from "./AppsClient.Tools.js";
18
+ import { appsClientDebugMethods } from "./AppsClient.Debug.js";
19
+ import { appsClientCrawlerMethods } from "./AppsClient.Crawler.js";
20
+
21
+ const instanceMap = new Map();
22
+ /**
23
+ * AI应用数据库客户端 - 多模型版
24
+ *
25
+ * 核心理念:
26
+ * - 后端只负责数据持久化(按 Model 存储和读取)
27
+ * - 前端 Store 负责业务逻辑处理(查询、筛选、排序、分页等)
28
+ * - 每个 Store 独立管理自己对应的 Model 数据
29
+ */
30
+ class AppsClient extends BaseAppsClient {
31
+ static getInstance(tagetAppId, config = {}) {
32
+ const instance = instanceMap.get(tagetAppId);
33
+ if (instance) {
34
+ return instance;
35
+ }
36
+ return new AppsClient(tagetAppId, config);
37
+ }
38
+
39
+ constructor(appId, config = {}) {
40
+ super(appId, config);
41
+
42
+ if (isBackend && !this.config.tacoreServerInteropAppServerApiKey) {
43
+ throw new Error(
44
+ "tacoreServerInteropAppServerApiKey is required for backend environment. Provide it in config or via TACORE_SERVER_INTEROP_APP_SERVER_API_KEY env var."
45
+ );
46
+ }
47
+
48
+ instanceMap.set(appId, this);
49
+
50
+ // 用于本地实时数据模式的订阅者管理
51
+ this.subscribers = new Map();
52
+
53
+ // 初始化火山引擎实时语音客户端
54
+ // 传入 this (AppsClient 实例) 以便 VolcengineImpl 复用请求逻辑
55
+ this.volcengine = new VolcengineImpl(this);
56
+ }
57
+
58
+ /**
59
+ * 定义策略适配器映射
60
+ * 将 invoke 的对象参数转换为老方法的位置参数
61
+ */
62
+ get adapters() {
63
+ return {
64
+ // === 数据 CRUD ===
65
+ "createData": ({ modelName, data, permissions, publicConfig }) =>
66
+ this.createData(modelName, data, permissions, publicConfig),
67
+
68
+ "updateData": ({ modelName, wyID, data, permissions, publicConfig }) =>
69
+ this.updateData(modelName, wyID, data, permissions, publicConfig),
70
+
71
+ "readData": params => {
72
+ // readData(modelName, query)
73
+ // 适配逻辑:如果 params 中除了 modelName 只有一个 wyID 字段,则视为单条查询
74
+ const { modelName, ...rest } = params;
75
+ if (rest.wyID && Object.keys(rest).length === 1) {
76
+ return this.readData(modelName, rest.wyID);
77
+ }
78
+ return this.readData(modelName, rest);
79
+ },
80
+
81
+ "deleteData": ({ modelName, wyID, options }) =>
82
+ this.deleteData(modelName, wyID, options),
83
+
84
+ // === 电商模块 ===
85
+ "createProduct": params => this.createProduct(params),
86
+ "updateProduct": ({ productId, data }) => this.updateProduct(productId, data),
87
+ "deleteProduct": ({ productId }) => this.deleteProduct(productId),
88
+ "getProduct": ({ productId }) => this.getProduct(productId),
89
+ "listProducts": options => this.listProducts(options),
90
+
91
+ "createOrder": params => this.createOrder(params),
92
+ "updateOrder": ({ orderId, data }) => this.updateOrder(orderId, data),
93
+ "deleteOrder": ({ orderId }) => this.deleteOrder(orderId),
94
+ "getOrder": ({ orderId }) => this.getOrder(orderId),
95
+ "listOrders": options => this.listOrders(options),
96
+
97
+ // === 用户与角色 ===
98
+ "createRole": params => this.createRole(params),
99
+ "updateUserRole": params => this.updateUserRole(params),
100
+ "listAppUsers": params => this.listAppUsers(params),
101
+
102
+ // === 文件存储 ===
103
+ "uploadFile": ({ file }) => this.uploadFile(file),
104
+ "uploadToR2": ({ file }) => this.uploadToR2(file),
105
+ "uploadToOSS": ({ file }) => this.uploadToOSS(file),
106
+ "uploadToGCS": ({ file }) => this.uploadToGCS(file),
107
+ "downloadFromR2": ({ key }) => this.downloadFromR2(key),
108
+ "deleteFromR2": ({ key }) => this.deleteFromR2(key),
109
+ "getR2PublicUrl": ({ key }) => this.getR2PublicUrl(key),
110
+ "getR2UploadUrl": ({ fileName, contentType }) => this.getR2UploadUrl(fileName, contentType),
111
+
112
+ // === KV 存储 ===
113
+ "kvGet": ({ key, params }) => this.kvGet(key, params),
114
+ "kvPut": ({ key, value, options }) => this.kvPut(key, value, options),
115
+ "kvDelete": ({ key, params }) => this.kvDelete(key, params),
116
+
117
+ // === TextIn / OCR ===
118
+ "recognizeText": ({ input, options }) => this.recognizeText(input, options),
119
+ "wordToPdf": ({ input }) => this.wordToPdf(input),
120
+ "pdfToWord": ({ input }) => this.pdfToWord(input),
121
+ "excelToPdf": ({ input }) => this.excelToPdf(input),
122
+ "pdfToExcel": ({ input }) => this.pdfToExcel(input),
123
+ "fileToMarkdown": ({ input, options }) => this.fileToMarkdown(input, options),
124
+
125
+ // === 工具 ===
126
+ "addWatermark": options => this.addWatermark(options),
127
+ "detectSensitiveInfo": ({ content }) => this.detectSensitiveInfo(content),
128
+ "sendMessage": params => this.sendMessage(params),
129
+
130
+ // === 爬虫 ===
131
+ "scrapeUrl": options => this.scrapeUrl(options),
132
+ "scrapeMultipleUrls": options => this.scrapeMultipleUrls(options),
133
+ "firecrawlScrape": options => this.firecrawlScrape(options),
134
+ "firecrawlMap": options => this.firecrawlMap(options),
135
+
136
+ // === 视频 ===
137
+ "createVideoJob": options => this.createVideoJob(options),
138
+ "remixVideoJob": ({ jobId, options }) => this.remixVideoJob(jobId, options),
139
+ "getVideoJob": ({ jobId }) => this.getVideoJob(jobId),
140
+ "listVideoJobs": options => this.listVideoJobs(options),
141
+
142
+ // === 语音 ===
143
+ "generateVoice": options => this.generateVoice(options),
144
+ "streamVoice": options => this.streamVoice(options),
145
+
146
+ // === 图像 ===
147
+ "generateImage": options => this.generateImage(options),
148
+ "editImage": options => this.editImage(options),
149
+
150
+ // === AI ===
151
+ "streamCompletion": options => this.streamCompletion(options),
152
+
153
+ // === Knowledge Base ===
154
+ "createKnowledgeBaseDataStore": params => this.createKnowledgeBaseDataStore(params),
155
+ "deleteKnowledgeBaseDataStore": ({ storeId, provider }) => this.deleteKnowledgeBaseDataStore(storeId, provider),
156
+ "getKnowledgeBaseDataStore": ({ storeId, provider }) => this.getKnowledgeBaseDataStore(storeId, provider),
157
+ "listKnowledgeBaseDataStores": ({ provider }) => this.listKnowledgeBaseDataStores(provider),
158
+ "importKnowledgeBaseDocuments": params => this.importKnowledgeBaseDocuments(params),
159
+ "listKnowledgeBaseDocuments": ({ storeId, pageToken, pageSize, provider }) =>
160
+ this.listKnowledgeBaseDocuments(storeId, pageToken, pageSize, provider),
161
+ "deleteKnowledgeBaseDocument": ({ storeId, documentId, provider }) =>
162
+ this.deleteKnowledgeBaseDocument(storeId, documentId, provider),
163
+
164
+ // === [新增] 部署与环境管理 ===
165
+ "deployCustomer": params => this.deployCustomer(params),
166
+ "addCustomDomain": params => this.addCustomDomain(params),
167
+ "deleteCustomDomain": params => this.deleteCustomDomain(params),
168
+ "createEnvironment": params => this.createEnvironment(params),
169
+ "updateEnvironment": params => this.updateEnvironment(params),
170
+ "deleteEnvironment": params => this.deleteEnvironment(params),
171
+ };
172
+ }
173
+
174
+ /**
175
+ * 重写基类方法,提供 Token
176
+ */
177
+ _getAccessToken() {
178
+ if (isBrowser) {
179
+ return localStorage.getItem("tacoreai_apps_access_token");
180
+ }
181
+ return null;
182
+ }
183
+
184
+ /**
185
+ * 重写基类方法,执行业务错误检查
186
+ */
187
+ _validateResponse(result) {
188
+ // 统一的业务层面错误检查
189
+ if (result.code && result.code !== 200 && result.code !== 201 && !result.success) {
190
+ throw new Error(result.message || result.error || `Business error: ${result.code}`);
191
+ }
192
+ }
193
+ }
194
+
195
+ Object.assign(
196
+ AppsClient.prototype,
197
+ appsClientAppServerMethods,
198
+ appsClientDataMethods,
199
+ appsClientEcommerceMethods,
200
+ appsClientUserMethods,
201
+ appsClientRealtimeMethods,
202
+ appsClientMessageMethods,
203
+ appsClientKnowledgeBaseMethods,
204
+ appsClientDeployMethods,
205
+ appsClientAiMethods,
206
+ appsClientVoiceVideoMethods,
207
+ appsClientStorageMethods,
208
+ appsClientKvMethods,
209
+ appsClientTextInMethods,
210
+ appsClientToolsMethods,
211
+ appsClientDebugMethods,
212
+ appsClientCrawlerMethods
213
+ );
214
+
215
+ export { AppsClient };
@@ -0,0 +1,107 @@
1
+ import { BaseAppsClient } from "./BaseAppsClient.js";
2
+
3
+ /**
4
+ * AI应用公共数据客户端 - 无需认证
5
+ *
6
+ * 核心理念:
7
+ * - 用于匿名读取被 `writeData` 设为公共的数据。
8
+ * - 用于匿名提交数据到指定的、允许公开写入的模型。
9
+ * - 此客户端的所有操作都无需用户登录。
10
+ */
11
+ export class AppsPublicClient extends BaseAppsClient {
12
+ constructor(appId, config = {}) {
13
+ super(appId, config);
14
+ }
15
+
16
+ /**
17
+ * 定义策略适配器映射
18
+ * 将 invoke 的对象参数转换为老方法的位置参数
19
+ */
20
+ get adapters() {
21
+ return {
22
+ 'submitPublicData': ({ modelName, data }) => this.submitPublicData(modelName, data),
23
+ 'readConfiguredPublicData': (params) => {
24
+ const { modelName, ...rest } = params;
25
+ if (rest.wyID && Object.keys(rest).length === 1) {
26
+ return this.readConfiguredPublicData(modelName, rest.wyID);
27
+ }
28
+ return this.readConfiguredPublicData(modelName, rest);
29
+ }
30
+ };
31
+ }
32
+
33
+ /**
34
+ * 提交公共数据 (例如 waitlist, contact form)
35
+ * @param {string} modelName - 目标模型名称 (后端会校验白名单)
36
+ * @param {Object} jsonData - 要提交的数据
37
+ * @returns {Promise<Object>} 提交成功后的响应,包含 wyID
38
+ */
39
+ async submitPublicData(modelName, jsonData) {
40
+ try {
41
+ if (!modelName) {
42
+ throw new Error("modelName is required");
43
+ }
44
+ if (!jsonData || typeof jsonData !== "object") {
45
+ throw new Error("jsonData must be a valid object");
46
+ }
47
+
48
+ const result = await this._post("/apps/public/data/submit", {
49
+ appId: this.appId,
50
+ modelName: modelName,
51
+ data: jsonData,
52
+ });
53
+
54
+ console.log(
55
+ `✅ [AppsPublicClient] Public data submitted successfully for model: ${modelName} in app: ${this.appId}`
56
+ );
57
+ return result;
58
+ } catch (error) {
59
+ console.error(`提交公共数据失败 (model: ${modelName}):`, error);
60
+ throw error;
61
+ }
62
+ }
63
+
64
+ /**
65
+ * 读取由 AppsClient.writeData 设为公共的数据 (通过 publicConfig)
66
+ * @param {string} modelName - 模型名称
67
+ * @param {string|Object} [query] - 查询参数。可以是单个 wyID 字符串,或包含分页/过滤的对象。
68
+ * @returns {Promise<Object|null>} 查询结果,结构与 AppsClient.readData 一致,但只包含公共字段。
69
+ */
70
+ async readConfiguredPublicData(modelName, query) {
71
+ try {
72
+ if (!modelName) {
73
+ throw new Error("modelName is required");
74
+ }
75
+
76
+ const params = {
77
+ appId: this.appId,
78
+ modelName: modelName,
79
+ };
80
+
81
+ if (typeof query === "string") {
82
+ // 支持按 wyID 查询单条
83
+ params.wyID = query;
84
+ } else if (query && typeof query === "object") {
85
+ // 支持列表查询、过滤和分页
86
+ Object.assign(params, query);
87
+ }
88
+
89
+ // 注意:此接口用于读取通过 publicConfig 配置的数据
90
+ const result = await this._get("/apps/data/public/read", params);
91
+
92
+ if (result.data === null || (Array.isArray(result.data) && result.data.length === 0)) {
93
+ console.log(
94
+ `📋 [AppsPublicClient] No configured public data found for model: ${modelName} in app: ${this.appId}`
95
+ );
96
+ } else {
97
+ console.log(
98
+ `✅ [AppsPublicClient] Configured public data read successfully for model: ${modelName} in app: ${this.appId}`
99
+ );
100
+ }
101
+ return result;
102
+ } catch (error) {
103
+ console.error(`读取配置的公共数据失败 (model: ${modelName}):`, error);
104
+ throw error;
105
+ }
106
+ }
107
+ }
@@ -0,0 +1,216 @@
1
+ import { getAppsApiBaseUrl, isBrowser, isBackend, getGlobalOptions } from "../../../utils/index.js";
2
+
3
+ /**
4
+ * Apps SDK 基类
5
+ * 封装通用的配置管理、Header构建、HTTP请求逻辑
6
+ */
7
+ export class BaseAppsClient {
8
+ constructor(appId, config = {}) {
9
+ if (!appId) {
10
+ throw new Error("appId is required during initialization.");
11
+ }
12
+ this.appId = appId;
13
+
14
+ const globalOptions = getGlobalOptions();
15
+ const defaultConfig = {
16
+ // 默认统一走平台接口;如需特殊链路可显式传入 apiBaseUrl 覆盖。
17
+ apiBaseUrl: getAppsApiBaseUrl(),
18
+ };
19
+
20
+ this.config = {
21
+ ...globalOptions.clientDefaultConfig,
22
+ ...defaultConfig,
23
+ ...config,
24
+ };
25
+
26
+ // 后端环境通用逻辑:尝试从环境变量加载 API Key
27
+ if (isBackend && !this.config.tacoreServerInteropAppServerApiKey) {
28
+ this.config.tacoreServerInteropAppServerApiKey = process.env.TACORE_SERVER_INTEROP_APP_SERVER_API_KEY;
29
+ }
30
+ }
31
+
32
+ /**
33
+ * 获取当前实例定义的策略适配器映射
34
+ * 子类应该重写此 getter 以注册需要参数适配的老方法
35
+ * @returns {Object} { 'apiName': (params) => this.method(...) }
36
+ */
37
+ get adapters() {
38
+ return {};
39
+ }
40
+
41
+ /**
42
+ * 统一调用入口 (策略模式)
43
+ * @param {string} apiName - API 名称 或 URL 路径 (如 'createData', 'agent/completions')
44
+ * @param {Object} params - 参数对象
45
+ * @returns {Promise<any>}
46
+ */
47
+ async invoke(apiName, params = {}) {
48
+ // 1. 优先查找适配器 (处理老方法的位置参数问题)
49
+ const adapter = this.adapters[apiName];
50
+ if (adapter) {
51
+ // 绑定 this 并传入 params
52
+ return adapter.call(this, params);
53
+ }
54
+
55
+ // 2. 查找原型方法 (前提:该方法必须支持单对象参数,或者 params 就是该方法需要的唯一参数)
56
+ // 这允许调用那些本身就设计为接收一个对象参数的方法 (如 generateImage) 而无需显式注册适配器
57
+ if (typeof this[apiName] === 'function') {
58
+ return this[apiName](params);
59
+ }
60
+
61
+ // 3. 默认回退机制 (通用后端接口调用)
62
+ // 支持两级路径,如 'agent/completions' -> '/apps/agent/completions'
63
+ // 如果 apiName 以 / 开头,则直接使用
64
+ const endpoint = apiName.startsWith('/') ? apiName : `/apps/${apiName}`;
65
+
66
+ // console.log(`[SDK] Invoke generic API: ${endpoint}`);
67
+ return this._post(endpoint, params);
68
+ }
69
+
70
+ /**
71
+ * 获取 Access Token (钩子方法)
72
+ * 子类需重写此方法以提供认证 Token
73
+ * @returns {string|null}
74
+ */
75
+ _getAccessToken() {
76
+ return null;
77
+ }
78
+
79
+ /**
80
+ * 验证响应数据 (钩子方法)
81
+ * 子类可重写此方法以抛出业务特定的错误
82
+ * @param {Object} result - 解析后的 JSON 响应
83
+ */
84
+ _validateResponse(result) {
85
+ // 默认不进行额外的业务错误检查
86
+ }
87
+
88
+ /**
89
+ * 构建请求头
90
+ * @param {Object} additionalHeaders - 额外的 Header
91
+ */
92
+ _getRequestHeaders(additionalHeaders = {}) {
93
+ const headers = {
94
+ "Content-Type": "application/json",
95
+ ...additionalHeaders,
96
+ };
97
+
98
+ if (this.config.datasource) {
99
+ headers["x-datasource"] = this.config.datasource;
100
+ }
101
+
102
+ if (this.config.supabaseBaseUrl) {
103
+ headers["x-supabase-base-url"] = this.config.supabaseBaseUrl;
104
+ }
105
+ if (this.config.supabaseAnonKey) {
106
+ headers["x-supabase-anon-key"] = this.config.supabaseAnonKey;
107
+ }
108
+ if (this.config.supabaseRoleKey) {
109
+ headers["x-supabase-role-key"] = this.config.supabaseRoleKey;
110
+ }
111
+
112
+ if (this.config.organizationId) {
113
+ headers["x-org-id"] = this.config.organizationId;
114
+ }
115
+ if (this.config.projectId) {
116
+ headers["x-project-id"] = this.config.projectId;
117
+ }
118
+ if (this.appId) {
119
+ headers["x-app-id"] = this.appId;
120
+ }
121
+
122
+ if (isBrowser) {
123
+ const token = this._getAccessToken();
124
+ if (token) {
125
+ headers["Authorization"] = `Bearer ${token}`;
126
+ }
127
+ } else { // isBackend
128
+ if (this.config.tacoreServerInteropAppServerApiKey) {
129
+ headers["x-tacore-server-interop-app-server-api-key"] = this.config.tacoreServerInteropAppServerApiKey;
130
+ }
131
+ }
132
+
133
+ return headers;
134
+ }
135
+
136
+ /**
137
+ * 发送 API 请求
138
+ */
139
+ async _apiRequest(endpoint, options = {}) {
140
+ const url = `${this.config.apiBaseUrl}${endpoint}`;
141
+ const headers = this._getRequestHeaders(options.headers);
142
+
143
+ const response = await fetch(url, {
144
+ ...options,
145
+ headers,
146
+ });
147
+
148
+ if (!response.ok) {
149
+ const errorData = await response.json().catch(() => ({ error: "Network error" }));
150
+ console.error(`[SDK][${this.appId}] API request failed: ${response.status} ${response.statusText}`, errorData);
151
+ throw new Error(errorData.error || `HTTP ${response.status}: ${response.statusText}`);
152
+ }
153
+
154
+ const result = await response.json();
155
+
156
+ // 调用子类的验证钩子
157
+ this._validateResponse(result);
158
+
159
+ return result;
160
+ }
161
+
162
+ /**
163
+ * GET 请求辅助方法
164
+ */
165
+ async _get(endpoint, params = {}) {
166
+ const searchParams = new URLSearchParams();
167
+ Object.keys(params).forEach((key) => {
168
+ if (params[key] !== undefined && params[key] !== null) {
169
+ if (typeof params[key] === "object") {
170
+ searchParams.append(key, JSON.stringify(params[key]));
171
+ } else {
172
+ searchParams.append(key, params[key]);
173
+ }
174
+ }
175
+ });
176
+
177
+ const queryString = searchParams.toString();
178
+ const url = queryString ? `${endpoint}?${queryString}` : endpoint;
179
+
180
+ return this._apiRequest(url, { method: "GET" });
181
+ }
182
+
183
+ /**
184
+ * POST 请求辅助方法
185
+ */
186
+ async _post(endpoint, data = {}) {
187
+ return this._apiRequest(endpoint, {
188
+ method: "POST",
189
+ body: JSON.stringify(data),
190
+ });
191
+ }
192
+
193
+ /**
194
+ * DELETE 请求辅助方法
195
+ */
196
+ async _delete(endpoint, data = {}) {
197
+ return this._apiRequest(endpoint, {
198
+ method: "DELETE",
199
+ body: JSON.stringify(data),
200
+ });
201
+ }
202
+
203
+ /**
204
+ * 更新配置
205
+ */
206
+ updateConfig(newConfig) {
207
+ this.config = { ...this.config, ...newConfig };
208
+ }
209
+
210
+ /**
211
+ * 获取当前配置
212
+ */
213
+ getConfig() {
214
+ return { ...this.config };
215
+ }
216
+ }
package/index.js ADDED
@@ -0,0 +1,9 @@
1
+ import * as utils from "./utils/index.js"
2
+
3
+ // 导出AI应用专用客户端
4
+ export { AppsClient } from "./database/core/apps/AppsClient.js"
5
+ export { AppsAuthManager } from "./database/core/apps/AppsAuthManager.js"
6
+ export { AppsPublicClient } from "./database/core/apps/AppsPublicClient.js"
7
+
8
+ // 导出工具函数
9
+ export { utils }
package/package.json CHANGED
@@ -1,29 +1,17 @@
1
1
  {
2
2
  "name": "@tacoreai/web-sdk",
3
- "version": "1.0.12",
4
- "description": "TacoreAI Web SDK for AI generated applications",
5
- "main": "dist/index.umd.js",
6
- "unpkg": "dist/index.umd.js",
7
- "files": [
8
- "dist"
9
- ],
10
- "scripts": {
11
- "build": "vite build"
12
- },
13
- "keywords": [
14
- "tacoreai",
15
- "web-sdk",
16
- "ai",
17
- "database",
18
- "utils"
19
- ],
20
- "author": "TacoreAI",
21
- "devDependencies": {
22
- "vite": "^5.0.0"
23
- },
24
- "peerDependencies": {},
3
+ "description": "This file is for app server package, not the real npm package",
4
+ "version": "1.3.0",
5
+ "type": "module",
25
6
  "publishConfig": {
26
7
  "access": "public",
27
- "registry": "https://registry.npmjs.org/"
8
+ "registry": "https://registry.npmjs.com"
9
+ },
10
+ "exports": {
11
+ ".": "./index.js",
12
+ "./utils": "./utils/index.js"
13
+ },
14
+ "scripts": {
15
+ "release": "npm version minor && git commit -am \"web-sdk update version\" && npm publish"
28
16
  }
29
17
  }
@@ -0,0 +1,28 @@
1
+ /**
2
+ * 记录运行时业务逻辑错误,用于 AI 自动修复闭环。
3
+ * @param {string} appId - 当前应用的 ID。
4
+ * @param {object} errorDetails - 包含完整上下文的错误对象。
5
+ * @param {string} errorDetails.type - 错误类型 (e.g., 'api_error', 'data_validation_error')。
6
+ * @param {string} errorDetails.message - 具体的错误信息。
7
+ * @param {object} errorDetails.context - 错误的上下文,必须包含 params 和 response。
8
+ */
9
+ export const logRuntimeError = (appId, errorDetails) => {
10
+ if (!appId) return
11
+
12
+ const errorKey = `TACORE_RUNTIME_ERRORS_${appId}`
13
+
14
+ try {
15
+ const existingErrors = JSON.parse(localStorage.getItem(errorKey) || "[]")
16
+ const newError = {
17
+ ...errorDetails,
18
+ timestamp: new Date().toISOString(),
19
+ }
20
+
21
+ // 为避免 localStorage 溢出,只保留最近的 20 条错误记录
22
+ const updatedErrors = [newError, ...existingErrors].slice(0, 20)
23
+
24
+ localStorage.setItem(errorKey, JSON.stringify(updatedErrors))
25
+ } catch (e) {
26
+ console.error("Failed to log runtime error:", e)
27
+ }
28
+ }