ai-world-sdk 1.2.0 → 1.2.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,390 @@
1
+ "use strict";
2
+ /**
3
+ * Resource Client
4
+ * 资源管理客户端
5
+ * 通过后端 /api/resources 代理操作资源管理系统
6
+ * 支持 private / specific_users / public 三级访问控制,默认按 plugin_id 隔离
7
+ */
8
+ Object.defineProperty(exports, "__esModule", { value: true });
9
+ exports.ResourceClient = void 0;
10
+ const config_1 = require("./config");
11
+ const log_1 = require("./log");
12
+ // ==================== ResourceClient ====================
13
+ /**
14
+ * Resource Client
15
+ * 资源管理客户端类
16
+ *
17
+ * @example
18
+ * ```typescript
19
+ * import { ResourceClient, sdkConfig } from 'ai-world-sdk';
20
+ *
21
+ * sdkConfig.setPluginId('my-plugin');
22
+ * const resources = new ResourceClient();
23
+ *
24
+ * // 上传并设为公开
25
+ * const file = new File(['hello'], 'hello.txt', { type: 'text/plain' });
26
+ * const res = await resources.upload('docs/hello.txt', file, { accessLevel: 'public' });
27
+ *
28
+ * // 列出我的资源
29
+ * const myFiles = await resources.listMy();
30
+ *
31
+ * // 查看某用户的公开资源
32
+ * const userFiles = await resources.listByUser(42);
33
+ *
34
+ * // 分享给指定用户
35
+ * await resources.addShare(res.id, 99);
36
+ *
37
+ * // 通过 userId + path 下载
38
+ * const blob = await resources.downloadByUser(42, 'docs/hello.txt');
39
+ * ```
40
+ */
41
+ class ResourceClient {
42
+ constructor(config = {}) {
43
+ this.baseUrl =
44
+ config.baseUrl ||
45
+ config_1.sdkConfig.getServerUrl() ||
46
+ (typeof window !== "undefined" ? window.location.origin : "");
47
+ const globalHeaders = config_1.sdkConfig.getHeaders();
48
+ const globalToken = config.token || config_1.sdkConfig.getToken();
49
+ this.headers = {
50
+ ...globalHeaders,
51
+ ...config.headers,
52
+ };
53
+ const pluginId = config.pluginId || config_1.sdkConfig.getPluginId();
54
+ if (pluginId) {
55
+ this.headers["X-Plugin-Id"] = pluginId;
56
+ }
57
+ if (globalToken) {
58
+ this.headers["Authorization"] = `Bearer ${globalToken}`;
59
+ }
60
+ }
61
+ ensurePluginId() {
62
+ if (!this.headers["X-Plugin-Id"]) {
63
+ const pluginId = config_1.sdkConfig.getPluginId();
64
+ if (pluginId) {
65
+ this.headers["X-Plugin-Id"] = pluginId;
66
+ }
67
+ else {
68
+ throw new Error("X-Plugin-Id is required. Set it via sdkConfig.setPluginId() or pass pluginId in ResourceClientConfig.");
69
+ }
70
+ }
71
+ }
72
+ async handleErrorResponse(response) {
73
+ let errorMessage = `Request failed: ${response.status} ${response.statusText}`;
74
+ try {
75
+ const errorText = await response.text();
76
+ const errorJson = JSON.parse(errorText);
77
+ errorMessage = errorJson.detail || errorMessage;
78
+ }
79
+ catch {
80
+ // ignore parse error
81
+ }
82
+ throw new Error(errorMessage);
83
+ }
84
+ /**
85
+ * 上传文件并创建资源记录
86
+ */
87
+ async upload(path, file, options = {}) {
88
+ config_1.sdkConfig.ensureVersionCompatible();
89
+ this.ensurePluginId();
90
+ const params = new URLSearchParams();
91
+ params.append("path", path);
92
+ if (options.accessLevel) {
93
+ params.append("access_level", options.accessLevel);
94
+ }
95
+ if (options.description) {
96
+ params.append("description", options.description);
97
+ }
98
+ const apiUrl = `${this.baseUrl}/api/resources/upload?${params.toString()}`;
99
+ const formData = new FormData();
100
+ const filename = file instanceof File ? file.name : path.split("/").pop() || "file";
101
+ formData.append("file", file, filename);
102
+ const headers = { ...this.headers };
103
+ delete headers["Content-Type"];
104
+ (0, log_1.debugLog)("Resource upload request:", { path, options });
105
+ (0, log_1.logRequest)("POST", apiUrl, headers);
106
+ const response = await fetch(apiUrl, {
107
+ method: "POST",
108
+ headers,
109
+ body: formData,
110
+ });
111
+ if (!response.ok) {
112
+ await this.handleErrorResponse(response);
113
+ }
114
+ const result = (await response.json());
115
+ (0, log_1.debugLog)("Resource upload response:", result);
116
+ (0, log_1.logResponse)(response.status, response.statusText, response.headers, result);
117
+ return result;
118
+ }
119
+ /**
120
+ * 列出当前用户的资源
121
+ */
122
+ async listMy(options = {}) {
123
+ config_1.sdkConfig.ensureVersionCompatible();
124
+ this.ensurePluginId();
125
+ const params = new URLSearchParams();
126
+ if (options.crossPlugin)
127
+ params.append("cross_plugin", "true");
128
+ const apiUrl = `${this.baseUrl}/api/resources/my?${params.toString()}`;
129
+ (0, log_1.debugLog)("Resource listMy request:", options);
130
+ (0, log_1.logRequest)("GET", apiUrl, this.headers);
131
+ const response = await fetch(apiUrl, {
132
+ method: "GET",
133
+ headers: this.headers,
134
+ });
135
+ if (!response.ok)
136
+ await this.handleErrorResponse(response);
137
+ const result = (await response.json());
138
+ (0, log_1.debugLog)("Resource listMy response:", { count: result.length });
139
+ (0, log_1.logResponse)(response.status, response.statusText, response.headers, result);
140
+ return result;
141
+ }
142
+ /**
143
+ * 获取资源详情
144
+ */
145
+ async getInfo(resourceId) {
146
+ config_1.sdkConfig.ensureVersionCompatible();
147
+ this.ensurePluginId();
148
+ const apiUrl = `${this.baseUrl}/api/resources/${resourceId}`;
149
+ (0, log_1.debugLog)("Resource getInfo request:", { resourceId });
150
+ (0, log_1.logRequest)("GET", apiUrl, this.headers);
151
+ const response = await fetch(apiUrl, {
152
+ method: "GET",
153
+ headers: this.headers,
154
+ });
155
+ if (!response.ok)
156
+ await this.handleErrorResponse(response);
157
+ const result = (await response.json());
158
+ (0, log_1.debugLog)("Resource getInfo response:", result);
159
+ (0, log_1.logResponse)(response.status, response.statusText, response.headers, result);
160
+ return result;
161
+ }
162
+ /**
163
+ * 修改资源访问级别
164
+ */
165
+ async updateAccess(resourceId, accessLevel) {
166
+ config_1.sdkConfig.ensureVersionCompatible();
167
+ this.ensurePluginId();
168
+ const apiUrl = `${this.baseUrl}/api/resources/${resourceId}/access`;
169
+ (0, log_1.debugLog)("Resource updateAccess request:", { resourceId, accessLevel });
170
+ (0, log_1.logRequest)("PATCH", apiUrl, this.headers);
171
+ const response = await fetch(apiUrl, {
172
+ method: "PATCH",
173
+ headers: {
174
+ ...this.headers,
175
+ "Content-Type": "application/json",
176
+ },
177
+ body: JSON.stringify({ access_level: accessLevel }),
178
+ });
179
+ if (!response.ok)
180
+ await this.handleErrorResponse(response);
181
+ const result = (await response.json());
182
+ (0, log_1.debugLog)("Resource updateAccess response:", result);
183
+ (0, log_1.logResponse)(response.status, response.statusText, response.headers, result);
184
+ return result;
185
+ }
186
+ /**
187
+ * 批量添加用户的访问授权
188
+ *
189
+ * @param resourceId - 资源 ID
190
+ * @param userIds - 被授权用户 ID 列表(支持单个或多个)
191
+ */
192
+ async addShare(resourceId, userIds) {
193
+ config_1.sdkConfig.ensureVersionCompatible();
194
+ this.ensurePluginId();
195
+ const ids = Array.isArray(userIds) ? userIds : [userIds];
196
+ const apiUrl = `${this.baseUrl}/api/resources/${resourceId}/share`;
197
+ (0, log_1.debugLog)("Resource addShare request:", { resourceId, userIds: ids });
198
+ (0, log_1.logRequest)("POST", apiUrl, this.headers);
199
+ const response = await fetch(apiUrl, {
200
+ method: "POST",
201
+ headers: {
202
+ ...this.headers,
203
+ "Content-Type": "application/json",
204
+ },
205
+ body: JSON.stringify({ user_ids: ids }),
206
+ });
207
+ if (!response.ok)
208
+ await this.handleErrorResponse(response);
209
+ const result = await response.json();
210
+ (0, log_1.debugLog)("Resource addShare completed:", result);
211
+ return result;
212
+ }
213
+ /**
214
+ * 批量移除用户的访问授权
215
+ *
216
+ * @param resourceId - 资源 ID
217
+ * @param userIds - 要移除授权的用户 ID 列表(支持单个或多个)
218
+ */
219
+ async removeShare(resourceId, userIds) {
220
+ config_1.sdkConfig.ensureVersionCompatible();
221
+ this.ensurePluginId();
222
+ const ids = Array.isArray(userIds) ? userIds : [userIds];
223
+ const apiUrl = `${this.baseUrl}/api/resources/${resourceId}/unshare`;
224
+ (0, log_1.debugLog)("Resource removeShare request:", { resourceId, userIds: ids });
225
+ (0, log_1.logRequest)("POST", apiUrl, this.headers);
226
+ const response = await fetch(apiUrl, {
227
+ method: "POST",
228
+ headers: {
229
+ ...this.headers,
230
+ "Content-Type": "application/json",
231
+ },
232
+ body: JSON.stringify({ user_ids: ids }),
233
+ });
234
+ if (!response.ok)
235
+ await this.handleErrorResponse(response);
236
+ const result = await response.json();
237
+ (0, log_1.debugLog)("Resource removeShare completed:", result);
238
+ return result;
239
+ }
240
+ /**
241
+ * 通过资源 ID 下载
242
+ */
243
+ async download(resourceId) {
244
+ config_1.sdkConfig.ensureVersionCompatible();
245
+ this.ensurePluginId();
246
+ const apiUrl = `${this.baseUrl}/api/resources/${resourceId}/download`;
247
+ (0, log_1.debugLog)("Resource download request:", { resourceId });
248
+ (0, log_1.logRequest)("GET", apiUrl, this.headers);
249
+ const response = await fetch(apiUrl, {
250
+ method: "GET",
251
+ headers: this.headers,
252
+ });
253
+ if (!response.ok)
254
+ await this.handleErrorResponse(response);
255
+ const blob = await response.blob();
256
+ (0, log_1.debugLog)("Resource download completed:", {
257
+ size: blob.size,
258
+ type: blob.type,
259
+ });
260
+ return blob;
261
+ }
262
+ /**
263
+ * 删除资源
264
+ */
265
+ async delete(resourceId) {
266
+ config_1.sdkConfig.ensureVersionCompatible();
267
+ this.ensurePluginId();
268
+ const apiUrl = `${this.baseUrl}/api/resources/${resourceId}`;
269
+ (0, log_1.debugLog)("Resource delete request:", { resourceId });
270
+ (0, log_1.logRequest)("DELETE", apiUrl, this.headers);
271
+ const response = await fetch(apiUrl, {
272
+ method: "DELETE",
273
+ headers: this.headers,
274
+ });
275
+ if (!response.ok)
276
+ await this.handleErrorResponse(response);
277
+ (0, log_1.debugLog)("Resource delete completed:", { resourceId });
278
+ }
279
+ /**
280
+ * 列出指定用户的可访问资源
281
+ */
282
+ async listByUser(userId, options = {}) {
283
+ config_1.sdkConfig.ensureVersionCompatible();
284
+ this.ensurePluginId();
285
+ const params = new URLSearchParams();
286
+ if (options.crossPlugin)
287
+ params.append("cross_plugin", "true");
288
+ const apiUrl = `${this.baseUrl}/api/resources/user/${userId}?${params.toString()}`;
289
+ (0, log_1.debugLog)("Resource listByUser request:", { userId, options });
290
+ (0, log_1.logRequest)("GET", apiUrl, this.headers);
291
+ const response = await fetch(apiUrl, {
292
+ method: "GET",
293
+ headers: this.headers,
294
+ });
295
+ if (!response.ok)
296
+ await this.handleErrorResponse(response);
297
+ const result = (await response.json());
298
+ (0, log_1.debugLog)("Resource listByUser response:", { count: result.length });
299
+ (0, log_1.logResponse)(response.status, response.statusText, response.headers, result);
300
+ return result;
301
+ }
302
+ /**
303
+ * 通过 userId + path 下载资源
304
+ */
305
+ async downloadByUser(userId, path, pluginId) {
306
+ config_1.sdkConfig.ensureVersionCompatible();
307
+ this.ensurePluginId();
308
+ const params = new URLSearchParams();
309
+ if (pluginId)
310
+ params.append("plugin_id", pluginId);
311
+ const queryStr = params.toString();
312
+ const apiUrl = `${this.baseUrl}/api/resources/user/${userId}/download/${encodeURIComponent(path)}${queryStr ? `?${queryStr}` : ""}`;
313
+ (0, log_1.debugLog)("Resource downloadByUser request:", { userId, path, pluginId });
314
+ (0, log_1.logRequest)("GET", apiUrl, this.headers);
315
+ const response = await fetch(apiUrl, {
316
+ method: "GET",
317
+ headers: this.headers,
318
+ });
319
+ if (!response.ok)
320
+ await this.handleErrorResponse(response);
321
+ const blob = await response.blob();
322
+ (0, log_1.debugLog)("Resource downloadByUser completed:", {
323
+ size: blob.size,
324
+ type: blob.type,
325
+ });
326
+ return blob;
327
+ }
328
+ /**
329
+ * 列出被分享给当前用户的资源
330
+ */
331
+ async listSharedWithMe(options = {}) {
332
+ config_1.sdkConfig.ensureVersionCompatible();
333
+ this.ensurePluginId();
334
+ const params = new URLSearchParams();
335
+ if (options.crossPlugin)
336
+ params.append("cross_plugin", "true");
337
+ const apiUrl = `${this.baseUrl}/api/resources/shared-with-me?${params.toString()}`;
338
+ (0, log_1.debugLog)("Resource listSharedWithMe request:", options);
339
+ (0, log_1.logRequest)("GET", apiUrl, this.headers);
340
+ const response = await fetch(apiUrl, {
341
+ method: "GET",
342
+ headers: this.headers,
343
+ });
344
+ if (!response.ok)
345
+ await this.handleErrorResponse(response);
346
+ const result = (await response.json());
347
+ (0, log_1.debugLog)("Resource listSharedWithMe response:", { count: result.length });
348
+ (0, log_1.logResponse)(response.status, response.statusText, response.headers, result);
349
+ return result;
350
+ }
351
+ /**
352
+ * 列出所有 public 资源
353
+ */
354
+ async listPublic(options = {}) {
355
+ config_1.sdkConfig.ensureVersionCompatible();
356
+ this.ensurePluginId();
357
+ const params = new URLSearchParams();
358
+ if (options.crossPlugin)
359
+ params.append("cross_plugin", "true");
360
+ const apiUrl = `${this.baseUrl}/api/resources/public?${params.toString()}`;
361
+ (0, log_1.debugLog)("Resource listPublic request:", options);
362
+ (0, log_1.logRequest)("GET", apiUrl, this.headers);
363
+ const response = await fetch(apiUrl, {
364
+ method: "GET",
365
+ headers: this.headers,
366
+ });
367
+ if (!response.ok)
368
+ await this.handleErrorResponse(response);
369
+ const result = (await response.json());
370
+ (0, log_1.debugLog)("Resource listPublic response:", { count: result.length });
371
+ (0, log_1.logResponse)(response.status, response.statusText, response.headers, result);
372
+ return result;
373
+ }
374
+ /**
375
+ * 下载资源并触发浏览器下载
376
+ */
377
+ async downloadAsFile(resourceId, filename) {
378
+ const blob = await this.download(resourceId);
379
+ const downloadFilename = filename || "download";
380
+ const url = URL.createObjectURL(blob);
381
+ const a = document.createElement("a");
382
+ a.href = url;
383
+ a.download = downloadFilename;
384
+ document.body.appendChild(a);
385
+ a.click();
386
+ document.body.removeChild(a);
387
+ setTimeout(() => URL.revokeObjectURL(url), 100);
388
+ }
389
+ }
390
+ exports.ResourceClient = ResourceClient;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ai-world-sdk",
3
- "version": "1.2.0",
3
+ "version": "1.2.2",
4
4
  "description": "TypeScript SDK for AI World Platform - Chat Models, Image Generation, and Video Generation",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -25,7 +25,9 @@
25
25
  "test:image-generation": "jest -t 'ImageGenerationClient - 基础图像生成'",
26
26
  "test:video-generation": "jest -t 'VideoGenerationClient - 轮询视频生成任务'",
27
27
  "test:llm": "jest --testPathPattern=llm.test.ts",
28
- "test:llm-real": "jest --testPathPattern=llm.real.test.ts"
28
+ "test:llm-real": "jest --testPathPattern=llm.real.test.ts",
29
+ "test:minio": "jest --testPathPattern=minio.test.ts",
30
+ "test:minio-real": "jest -t 'MinIO Storage Tests'"
29
31
  },
30
32
  "keywords": [
31
33
  "ai-world",