@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,130 @@
1
+ export const appsClientKnowledgeBaseMethods = {
2
+ // ==================== Knowledge Base (RAG) ====================
3
+
4
+ /**
5
+ * 创建 Knowledge Base Data Store
6
+ * @param {Object} params
7
+ * @param {string} params.storeId - Data Store ID
8
+ * @param {string} params.displayName - 显示名称
9
+ * @param {string} [params.provider='google'] - 提供商 (google, aliyun)
10
+ * @returns {Promise<Object>} 创建结果
11
+ */
12
+ async createKnowledgeBaseDataStore({ storeId, displayName, provider = "google" }) {
13
+ if (!storeId || !displayName) {
14
+ throw new Error("storeId and displayName are required.");
15
+ }
16
+ return this._post("/apps/knowledge-base/dataStore", { storeId, displayName, provider });
17
+ },
18
+
19
+ /**
20
+ * 删除 Knowledge Base Data Store
21
+ * @param {string} storeId
22
+ * @param {string} [provider='google']
23
+ * @returns {Promise<Object>} 删除结果
24
+ */
25
+ async deleteKnowledgeBaseDataStore(storeId, provider = "google") {
26
+ if (!storeId) {
27
+ throw new Error("storeId is required.");
28
+ }
29
+ return this._delete(`/apps/knowledge-base/dataStore/${storeId}`, { provider });
30
+ },
31
+
32
+ /**
33
+ * 获取 Knowledge Base Data Store 详情
34
+ * @param {string} storeId
35
+ * @param {string} [provider='google']
36
+ * @returns {Promise<Object>} Data Store 详情
37
+ */
38
+ async getKnowledgeBaseDataStore(storeId, provider = "google") {
39
+ if (!storeId) {
40
+ throw new Error("storeId is required.");
41
+ }
42
+ return this._get(`/apps/knowledge-base/dataStore/${storeId}`, { provider });
43
+ },
44
+
45
+ /**
46
+ * 列出所有 Knowledge Base Data Stores
47
+ * @param {string} [provider='google']
48
+ * @returns {Promise<Object>} Data Store 列表
49
+ */
50
+ async listKnowledgeBaseDataStores(provider = "google") {
51
+ return this._get("/apps/knowledge-base/dataStore", { provider });
52
+ },
53
+
54
+ /**
55
+ * 导入文档到 Knowledge Base Data Store
56
+ * @param {Object} params
57
+ * @param {string} params.storeId - 目标 Data Store ID
58
+ * @param {string} params.sourceUri - 源 URI
59
+ * @param {string} [params.provider='google']
60
+ * @returns {Promise<Object>} 导入操作信息
61
+ */
62
+ async importKnowledgeBaseDocuments({ storeId, sourceUri, provider = "google" }) {
63
+ if (!storeId || !sourceUri) {
64
+ throw new Error("storeId and sourceUri are required.");
65
+ }
66
+ return this._post("/apps/knowledge-base/dataStore/import", { storeId, sourceUri, provider });
67
+ },
68
+
69
+ /**
70
+ * 获取 Knowledge Base 文档列表
71
+ * @param {string} storeId
72
+ * @param {string} pageToken
73
+ * @param {number} pageSize
74
+ * @param {string} provider
75
+ */
76
+ async listKnowledgeBaseDocuments(storeId, pageToken = "", pageSize = 10, provider = "google") {
77
+ return this._get(`/apps/knowledge-base/dataStore/${storeId}/documents`, {
78
+ pageToken,
79
+ pageSize,
80
+ provider,
81
+ });
82
+ },
83
+
84
+ /**
85
+ * 删除 Knowledge Base 文档
86
+ * @param {string} storeId
87
+ * @param {string} documentId
88
+ * @param {string} provider
89
+ */
90
+ async deleteKnowledgeBaseDocument(storeId, documentId, provider = "google") {
91
+ return this._delete(`/apps/knowledge-base/dataStore/${storeId}/documents/${documentId}`, { provider });
92
+ },
93
+
94
+ // ==================== Deprecated Vertex AI Aliases ====================
95
+
96
+ /**
97
+ * @deprecated Use createKnowledgeBaseDataStore
98
+ */
99
+ async createVertexDataStore({ dataStoreId, displayName }) {
100
+ return this.createKnowledgeBaseDataStore({ storeId: dataStoreId, displayName, provider: "google" });
101
+ },
102
+
103
+ /**
104
+ * @deprecated Use deleteKnowledgeBaseDataStore
105
+ */
106
+ async deleteVertexDataStore(dataStoreId) {
107
+ return this.deleteKnowledgeBaseDataStore(dataStoreId, "google");
108
+ },
109
+
110
+ /**
111
+ * @deprecated Use getKnowledgeBaseDataStore
112
+ */
113
+ async getVertexDataStore(dataStoreId) {
114
+ return this.getKnowledgeBaseDataStore(dataStoreId, "google");
115
+ },
116
+
117
+ /**
118
+ * @deprecated Use listKnowledgeBaseDataStores
119
+ */
120
+ async listVertexDataStores() {
121
+ return this.listKnowledgeBaseDataStores("google");
122
+ },
123
+
124
+ /**
125
+ * @deprecated Use importKnowledgeBaseDocuments
126
+ */
127
+ async importVertexDocuments({ dataStoreId, gcsUri }) {
128
+ return this.importKnowledgeBaseDocuments({ storeId: dataStoreId, sourceUri: gcsUri, provider: "google" });
129
+ },
130
+ };
@@ -0,0 +1,27 @@
1
+ export const appsClientMessageMethods = {
2
+ // ==================== [新增] 统一消息服务 ====================
3
+
4
+ /**
5
+ * 发送消息(统一接口)
6
+ * 支持多种 Provider (email, sms, wechat 等)
7
+ *
8
+ * @param {object} options
9
+ * @param {string} options.provider - 渠道标识: 'email' | 'sms' | 'wechat' ...
10
+ * @param {object} options.data - 消息数据,根据 provider 不同而不同
11
+ * @returns {Promise<{success: boolean, data: any}>}
12
+ */
13
+ async sendMessage({ provider, data }) {
14
+ if (!provider) {
15
+ throw new Error("provider is required for sendMessage.");
16
+ }
17
+ if (!data || typeof data !== "object") {
18
+ throw new Error("data object is required for sendMessage.");
19
+ }
20
+
21
+ return this._post("/apps/message/send", {
22
+ appId: this.appId,
23
+ provider,
24
+ data,
25
+ });
26
+ },
27
+ };
@@ -0,0 +1,46 @@
1
+ export const appsClientRealtimeMethods = {
2
+ // ==================== 实时数据接口 (高频/本地) ====================
3
+
4
+ /**
5
+ * 发布(写入)一条新的实时数据。
6
+ * 强制规则:此方法仅在客户端本地通过内部事件总线工作,永远不会将数据发送到云端。
7
+ * 这是为了保护云端数据库免受高频写入的影响。
8
+ * @param {string} modelName - 数据模型的名称。
9
+ * @param {Object} data - 要发布的数据。
10
+ */
11
+ publishRealtimeData(modelName, data) {
12
+ const listeners = this.subscribers.get(modelName);
13
+ if (listeners) {
14
+ listeners.forEach(callback => {
15
+ try {
16
+ callback(data);
17
+ } catch (e) {
18
+ console.error("Error in realtime data subscriber:", e);
19
+ }
20
+ });
21
+ }
22
+ },
23
+
24
+ /**
25
+ * 订阅指定模型的数据流。
26
+ * 强制规则:此方法仅订阅来自客户端本地的数据流。
27
+ * @param {string} modelName - 要订阅的数据模型的名称。
28
+ * @param {Function} callback - 接收新数据的回调函数。
29
+ * @returns {Function} 一个 unsubscribe 函数,用于取消订阅。
30
+ */
31
+ subscribeToRealtimeData(modelName, callback) {
32
+ if (!this.subscribers.has(modelName)) {
33
+ this.subscribers.set(modelName, []);
34
+ }
35
+ const listeners = this.subscribers.get(modelName);
36
+ listeners.push(callback);
37
+
38
+ // 返回取消订阅函数
39
+ return () => {
40
+ const index = listeners.indexOf(callback);
41
+ if (index > -1) {
42
+ listeners.splice(index, 1);
43
+ }
44
+ };
45
+ },
46
+ };
@@ -0,0 +1,275 @@
1
+ import { isBrowser } from "../../../utils/index.js";
2
+
3
+ export const appsClientStorageMethods = {
4
+ // ==================== 文件存储 API (R2 & GCS & OSS) ====================
5
+
6
+ /**
7
+ * 上传文件到 Cloudflare R2 (用于前端展示)
8
+ * @param {File | {data: string, encoding: 'base64', name: string}} file - 文件对象
9
+ * @returns {Promise<{success: boolean, data: {url: string, key: string}}>}
10
+ */
11
+ async uploadFile(file) {
12
+ // 统一使用 R2 作为默认存储
13
+ return this.uploadToR2(file);
14
+ },
15
+
16
+ /**
17
+ * [原子接口] 获取 R2 上传预签名 URL
18
+ * @param {string} fileName - 文件名 (包含扩展名)
19
+ * @param {string} contentType - 文件 MIME 类型
20
+ * @returns {Promise<{uploadUrl: string, url: string, key: string, headers: object}>}
21
+ */
22
+ async getR2UploadUrl(fileName, contentType) {
23
+ if (!fileName) throw new Error("fileName is required");
24
+
25
+ const result = await this._post("/apps/upload-r2-presigned", {
26
+ appId: this.appId,
27
+ fileName,
28
+ contentType: contentType || "application/octet-stream",
29
+ });
30
+
31
+ if (!result.success || !result.data?.uploadUrl) {
32
+ throw new Error(result.message || "Failed to get upload URL");
33
+ }
34
+
35
+ return result.data;
36
+ },
37
+
38
+ /**
39
+ * 上传文件到 Cloudflare R2 (使用 Presigned URL 直传,支持代理转发以解决 CORS)
40
+ * @param {File | {data: string, encoding: 'base64', name: string}} file - 文件对象
41
+ * @returns {Promise<{success: boolean, data: {url: string, key: string}}>}
42
+ */
43
+ async uploadToR2(file) {
44
+ if (!file) throw new Error("file is required");
45
+
46
+ let fileName;
47
+ let contentType;
48
+ let fileBody;
49
+
50
+ if (isBrowser) {
51
+ if (!(file instanceof File)) {
52
+ throw new Error("Invalid file input in browser. Provide a File object.");
53
+ }
54
+ fileName = file.name;
55
+ contentType = file.type || "application/octet-stream";
56
+ fileBody = file;
57
+ } else {
58
+ // isBackend
59
+ if (typeof file === "object" && file.data && file.name && file.encoding === "base64") {
60
+ fileName = file.name;
61
+ contentType = file.type || "application/octet-stream"; // 默认类型
62
+ fileBody = Buffer.from(file.data, "base64");
63
+ } else {
64
+ throw new Error(
65
+ "Invalid file input in Node.js. Provide { data: base64_string, encoding: 'base64', name: string }."
66
+ );
67
+ }
68
+ }
69
+
70
+ // 1. 获取上传地址 (使用原子接口)
71
+ const { uploadUrl, url: publicUrl, key, headers: uploadHeaders } = await this.getR2UploadUrl(fileName, contentType);
72
+
73
+ // 2. 执行上传 (直接使用后端返回的代理 URL 和 headers)
74
+ const uploadResponse = await fetch(uploadUrl, {
75
+ method: "PUT",
76
+ headers: uploadHeaders || { "Content-Type": contentType },
77
+ body: fileBody,
78
+ });
79
+
80
+ if (!uploadResponse.ok) {
81
+ throw new Error(`Failed to upload to R2: ${uploadResponse.statusText}`);
82
+ }
83
+
84
+ return {
85
+ success: true,
86
+ url: publicUrl,
87
+ key: key,
88
+ };
89
+ },
90
+
91
+ /**
92
+ * 下载 R2 文件 (流式/Blob)
93
+ * @param {string} key - 文件 Key
94
+ * @returns {Promise<Response>}
95
+ */
96
+ async downloadFromR2(key) {
97
+ if (!key) throw new Error("key is required");
98
+ const url = `${this.config.apiBaseUrl}/apps/download-r2?key=${encodeURIComponent(key)}`;
99
+ const headers = this._getRequestHeaders();
100
+
101
+ const response = await fetch(url, {
102
+ method: "GET",
103
+ headers,
104
+ });
105
+
106
+ if (!response.ok) {
107
+ const errorData = await response.json().catch(() => ({ error: "Network error" }));
108
+ throw new Error(errorData.message || `HTTP ${response.status}: ${response.statusText}`);
109
+ }
110
+
111
+ return response;
112
+ },
113
+
114
+ /**
115
+ * 获取 R2 文件的公开访问链接
116
+ * @param {string} key - 文件 Key
117
+ * @returns {string}
118
+ */
119
+ getR2PublicUrl(key) {
120
+ if (!key) throw new Error("key is required");
121
+ return `https://storage-r2.tacore.ai/${key}`;
122
+ },
123
+
124
+ /**
125
+ * 删除 R2 文件
126
+ * @param {string} key - 文件 Key
127
+ * @returns {Promise<{success: boolean, message: string}>}
128
+ */
129
+ async deleteFromR2(key) {
130
+ if (!key) throw new Error("key is required");
131
+ const url = `${this.config.apiBaseUrl}/apps/delete-r2`;
132
+ const headers = this._getRequestHeaders();
133
+ headers["Content-Type"] = "application/json";
134
+
135
+ const response = await fetch(url, {
136
+ method: "DELETE",
137
+ headers,
138
+ body: JSON.stringify({ key }),
139
+ });
140
+
141
+ if (!response.ok) {
142
+ const errorData = await response.json().catch(() => ({ error: "Network error" }));
143
+ throw new Error(errorData.message || `HTTP ${response.status}: ${response.statusText}`);
144
+ }
145
+
146
+ return response.json();
147
+ },
148
+
149
+ /**
150
+ * 上传文件到 Aliyun OSS
151
+ * @param {File | {data: string, encoding: 'base64', name: string}} file - 文件对象
152
+ * @returns {Promise<{success: boolean, data: {url: string, name: string}}>}
153
+ */
154
+ async uploadToOSS(file) {
155
+ if (!file) throw new Error("file is required");
156
+
157
+ const url = `${this.config.apiBaseUrl}/apps/oss/upload`;
158
+ const form = new FormData();
159
+ form.append("appId", this.appId);
160
+
161
+ if (isBrowser) {
162
+ if (!(file instanceof File)) {
163
+ throw new Error("Invalid file input in browser. Provide a File object.");
164
+ }
165
+ form.append("file", file, file.name);
166
+ } else {
167
+ // isBackend
168
+ if (typeof file === "object" && file.data && file.name && file.encoding === "base64") {
169
+ const buffer = Buffer.from(file.data, "base64");
170
+ const fileBlob = new Blob([buffer]);
171
+ form.append("file", fileBlob, file.name);
172
+ } else {
173
+ throw new Error(
174
+ "Invalid file input in Node.js. Provide { data: base64_string, encoding: 'base64', name: string }."
175
+ );
176
+ }
177
+ }
178
+
179
+ const headers = this._getRequestHeaders();
180
+ delete headers["Content-Type"]; // Let browser set boundary
181
+
182
+ const resp = await fetch(url, {
183
+ method: "POST",
184
+ headers,
185
+ body: form,
186
+ });
187
+
188
+ if (!resp.ok) {
189
+ const err = await resp.json().catch(() => ({}));
190
+ throw new Error(err.message || `HTTP ${resp.status}: ${resp.statusText}`);
191
+ }
192
+ return resp.json();
193
+ },
194
+
195
+ /**
196
+ * 上传文件到 Google Cloud Storage (GCS) - 用于 AI 多模态理解
197
+ * @param {File | {data: string, encoding: 'base64', name: string, type: string}} file - 文件对象
198
+ * @returns {Promise<{signedUrl: string, fileUrl: string, fileUri: string, accessUrl: string}>}
199
+ */
200
+ async uploadToGCS(file) {
201
+ if (!file) throw new Error("file is required");
202
+
203
+ let fileName;
204
+ let contentType;
205
+ let fileBody;
206
+
207
+ if (isBrowser) {
208
+ if (!(file instanceof File)) {
209
+ throw new Error("Invalid file input in browser. Provide a File object.");
210
+ }
211
+ fileName = file.name;
212
+ contentType = file.type || "application/octet-stream";
213
+ fileBody = file;
214
+ } else {
215
+ // isBackend
216
+ if (typeof file === "object" && file.data && file.name && file.encoding === "base64") {
217
+ fileName = file.name;
218
+ contentType = file.type || "application/octet-stream";
219
+ fileBody = Buffer.from(file.data, "base64");
220
+ } else {
221
+ throw new Error(
222
+ "Invalid file input in Node.js. Provide { data: base64_string, encoding: 'base64', name: string, type: string }."
223
+ );
224
+ }
225
+ }
226
+
227
+ // [DEBUG] 打印上传参数
228
+ console.log("[AppsClient] uploadToGCS - Requesting signed URL for:", { fileName, contentType });
229
+
230
+ // 1. 获取签名 URL
231
+ const signResult = await this._post("/apps/storage/gcs-signed-url/generate", {
232
+ appId: this.appId,
233
+ fileName,
234
+ contentType,
235
+ });
236
+
237
+ if (!signResult.success || !signResult.data?.signedUrl) {
238
+ throw new Error(signResult.error || "Failed to get GCS signed URL");
239
+ }
240
+
241
+ const { signedUrl, fileUrl, fileUri, accessUrl } = signResult.data;
242
+
243
+ // [DEBUG] 打印签名 URL 和上传头
244
+ console.log("[AppsClient] uploadToGCS - Got signed URL:", signedUrl);
245
+ console.log("[AppsClient] uploadToGCS - Uploading with headers:", {
246
+ "Content-Type": contentType,
247
+ "x-target-hostname": "storage.googleapis.com",
248
+ });
249
+
250
+ // 2. 使用签名 URL 上传文件
251
+ // 注意:这里直接使用 fetch 上传到 GCS (通过代理),不经过 tacore-server
252
+ // 后端已经将 signedUrl 替换为代理域名,直接使用即可
253
+ try {
254
+ const uploadResponse = await fetch(signedUrl, {
255
+ method: "PUT",
256
+ headers: {
257
+ "Content-Type": contentType,
258
+ "x-target-hostname": "storage.googleapis.com",
259
+ },
260
+ body: fileBody,
261
+ });
262
+
263
+ if (!uploadResponse.ok) {
264
+ const errorText = await uploadResponse.text().catch(() => "No error details");
265
+ console.error("[AppsClient] uploadToGCS - Upload failed:", uploadResponse.status, errorText);
266
+ throw new Error(`Failed to upload to GCS: ${uploadResponse.statusText} (${uploadResponse.status})`);
267
+ }
268
+ } catch (error) {
269
+ console.error("[AppsClient] uploadToGCS - Network error:", error);
270
+ throw error;
271
+ }
272
+
273
+ return { signedUrl, fileUrl, fileUri, accessUrl };
274
+ },
275
+ };