@tacoreai/web-sdk 1.22.1-beta.0 → 1.22.1-beta.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.
@@ -33,6 +33,8 @@ const getRequestSource = (isPlatformProxyRequest, isSchedulerRequest) => {
33
33
  const createRequestScopedAppsClient = (env, options = {}) => {
34
34
  const {
35
35
  accessToken = null,
36
+ appServerApiKey = null,
37
+ clientAppId = env.dataSourceId || env.appId,
36
38
  previewAppServerApiKey = null,
37
39
  enableAppServerServiceAuth = false,
38
40
  } = options;
@@ -50,11 +52,15 @@ const createRequestScopedAppsClient = (env, options = {}) => {
50
52
  null;
51
53
  }
52
54
 
55
+ if (appServerApiKey) {
56
+ config.appServerApiKey = appServerApiKey;
57
+ }
58
+
53
59
  if (previewAppServerApiKey) {
54
60
  config.previewAppServerApiKey = previewAppServerApiKey;
55
61
  }
56
62
 
57
- return new AppsClient(env.dataSourceId || env.appId, config);
63
+ return new AppsClient(clientAppId, config);
58
64
  };
59
65
 
60
66
  const isPreviewApiKeyShapeValid = (apiKey) => {
@@ -286,6 +292,7 @@ export const createAppServerRuntime = async (options = {}) => {
286
292
  process.env.CLOUDFLARE_WORKER_INTEROP_APP_SERVER_TOKEN
287
293
  );
288
294
  const requestSource = getRequestSource(isPlatformProxyRequest, isSchedulerRequest);
295
+ let appServerApiKey = null;
289
296
  let previewAppServerApiKey = null;
290
297
  let isExternalApiKeyRequest = false;
291
298
 
@@ -313,6 +320,7 @@ export const createAppServerRuntime = async (options = {}) => {
313
320
  try {
314
321
  const authClient = createRequestScopedAppsClient(env, {
315
322
  accessToken: requestAccessToken || null,
323
+ clientAppId: env.appId,
316
324
  });
317
325
  isValidApiKey = await validateExternalApiKey(authClient, env.appId, requestApiKey);
318
326
  } catch (error) {
@@ -333,6 +341,7 @@ export const createAppServerRuntime = async (options = {}) => {
333
341
  }
334
342
 
335
343
  isExternalApiKeyRequest = true;
344
+ appServerApiKey = requestApiKey;
336
345
 
337
346
  if (isPreviewMode() && !requestAccessToken) {
338
347
  previewAppServerApiKey = requestApiKey;
@@ -347,16 +356,19 @@ export const createAppServerRuntime = async (options = {}) => {
347
356
  hasPlatformInteropHeader: Boolean(platformInteropHeader),
348
357
  hasSchedulerInteropHeader: Boolean(schedulerInteropHeader),
349
358
  isExternalApiKeyRequest,
359
+ willForwardAppServerApiKey: Boolean(appServerApiKey && !requestAccessToken),
350
360
  willForwardPreviewAppServerApiKey: Boolean(previewAppServerApiKey),
351
361
  });
352
362
 
353
363
  const appsClient = createRequestScopedAppsClient(env, {
354
364
  accessToken: requestAccessToken || null,
365
+ appServerApiKey: requestAccessToken ? null : appServerApiKey,
355
366
  previewAppServerApiKey,
356
367
  enableAppServerServiceAuth: Boolean((isSchedulerRequest || isExternalApiKeyRequest) && !requestAccessToken),
357
368
  });
358
369
  console.log(`[AppServer Debug] scoped AppsClient api="${apiName}"`, {
359
370
  hasAccessToken: Boolean(appsClient.getAccessToken()),
371
+ hasAppServerApiKey: Boolean(appsClient.config.appServerApiKey),
360
372
  hasPreviewAppServerApiKey: Boolean(appsClient.config.previewAppServerApiKey),
361
373
  hasInteropKey: Boolean(appsClient.config.tacoreServerInteropAppServerApiKey),
362
374
  serviceAuthEnabled: appsClient.config.enableAppServerServiceAuth === true,
@@ -80,22 +80,11 @@ export const appsClientDataMethods = {
80
80
  modelName: modelName,
81
81
  };
82
82
 
83
- if (typeof query === "string") {
84
- params.wyID = query;
85
- } else if (query && typeof query === "object") {
86
- const { page = 1, pageSize = 20, fields, filter } = query;
87
- params.page = Math.max(1, parseInt(page));
88
- params.pageSize = parseInt(pageSize) || 1000;
89
- console.log(params);
90
- if (fields && Array.isArray(fields)) {
91
- params.fields = fields;
92
- }
93
-
94
- if (filter && typeof filter === "object") {
95
- params.filter = filter;
96
- }
83
+ if (typeof query !== "string" || !query) {
84
+ throw new Error("readData only supports single-record reads by wyID. Use invoke('data/list', ...) or invoke('data/query', ...) for list/query reads.");
97
85
  }
98
86
 
87
+ params.wyID = query;
99
88
  const result = await this._get("/apps/data/read", params);
100
89
 
101
90
  if (result.data === null && params.wyID) {
@@ -152,7 +141,13 @@ export const appsClientDataMethods = {
152
141
  async hasData(modelName) {
153
142
  try {
154
143
  if (!modelName) throw new Error("modelName is required for hasData");
155
- const result = await this.readData(modelName, { page: 1, pageSize: 1 });
144
+ const result = await this.invoke("data/list", {
145
+ modelName,
146
+ query: {
147
+ page: 1,
148
+ pageSize: 1,
149
+ },
150
+ });
156
151
  return result && result.data && result.data.length > 0;
157
152
  } catch (error) {
158
153
  console.error(`检查应用数据失败 (model: ${modelName}):`, error);
@@ -0,0 +1,77 @@
1
+ export const appsClientIamMethods = {
2
+ async getIamContext(params = {}) {
3
+ return this._post("/apps/iam/me", params);
4
+ },
5
+
6
+ async bootstrapIam(params = {}) {
7
+ return this._post("/apps/iam/bootstrap", params);
8
+ },
9
+
10
+ async listIamOrgUnits(params = {}) {
11
+ return this._post("/apps/iam/org-units/list", params);
12
+ },
13
+
14
+ async createIamOrgUnit(params = {}) {
15
+ return this._post("/apps/iam/org-units", params);
16
+ },
17
+
18
+ async updateIamOrgUnit(params = {}) {
19
+ return this._post("/apps/iam/org-units/update", params);
20
+ },
21
+
22
+ async deleteIamOrgUnit(params = {}) {
23
+ return this._post("/apps/iam/org-units/delete", params);
24
+ },
25
+
26
+ async listIamRoles(params = {}) {
27
+ return this._post("/apps/iam/roles/list", params);
28
+ },
29
+
30
+ async createIamRole(params = {}) {
31
+ return this._post("/apps/iam/roles", params);
32
+ },
33
+
34
+ async updateIamRole(params = {}) {
35
+ return this._post("/apps/iam/roles/update", params);
36
+ },
37
+
38
+ async deleteIamRole(params = {}) {
39
+ return this._post("/apps/iam/roles/delete", params);
40
+ },
41
+
42
+ async listIamStaff(params = {}) {
43
+ return this._post("/apps/iam/staff/list", params);
44
+ },
45
+
46
+ async createIamStaff(params = {}) {
47
+ return this._post("/apps/iam/staff", params);
48
+ },
49
+
50
+ async updateIamStaff(params = {}) {
51
+ return this._post("/apps/iam/staff/update", params);
52
+ },
53
+
54
+ async deleteIamStaff(params = {}) {
55
+ return this._post("/apps/iam/staff/delete", params);
56
+ },
57
+
58
+ async listModelPolicies(params = {}) {
59
+ return this._post("/apps/data/model-policies/list", params);
60
+ },
61
+
62
+ async declareModelPolicy(params = {}) {
63
+ return this._post("/apps/data/model-policies/declare", params);
64
+ },
65
+
66
+ async writeAuditLog(params = {}) {
67
+ return this._post("/apps/audit/write", params);
68
+ },
69
+
70
+ async queryAuditLogs(params = {}) {
71
+ return this._post("/apps/audit/query", params);
72
+ },
73
+
74
+ async authorizeFileDownload(params = {}) {
75
+ return this._post("/apps/files/authorize-download", params);
76
+ },
77
+ };
@@ -62,4 +62,34 @@ export const appsClientUserMethods = {
62
62
  password,
63
63
  });
64
64
  },
65
+
66
+ /**
67
+ * 从当前应用移除用户访问权限,保留 Auth 账号。
68
+ * @param {Object} params { userId: string }
69
+ * @returns {Promise<{success: boolean, data: {userId: string, appId: string}}>}
70
+ */
71
+ async removeAppUser({ userId }) {
72
+ if (!userId) {
73
+ throw new Error("userId is required.");
74
+ }
75
+ return this._post("/apps/users/remove", {
76
+ appId: this.appId,
77
+ targetUserId: userId,
78
+ });
79
+ },
80
+
81
+ /**
82
+ * 彻底删除当前应用中的用户账号,使该邮箱可重新注册。
83
+ * @param {Object} params { userId: string }
84
+ * @returns {Promise<{success: boolean, data: {userId: string, appId: string, deletionMode: string, authDeleted: boolean}}>}
85
+ */
86
+ async deleteAppUser({ userId }) {
87
+ if (!userId) {
88
+ throw new Error("userId is required.");
89
+ }
90
+ return this._post("/apps/users/delete", {
91
+ appId: this.appId,
92
+ targetUserId: userId,
93
+ });
94
+ },
65
95
  };
@@ -18,6 +18,7 @@ import { appsClientToolsMethods } from "./AppsClient.Tools.js";
18
18
  import { appsClientDebugMethods } from "./AppsClient.Debug.js";
19
19
  import { appsClientCrawlerMethods } from "./AppsClient.Crawler.js";
20
20
  import { appsClientWechatMethods } from "./AppsClient.Wechat.js";
21
+ import { appsClientIamMethods } from "./AppsClient.IAM.js";
21
22
 
22
23
  const instanceMap = new Map();
23
24
  /**
@@ -79,8 +80,7 @@ class AppsClient extends BaseAppsClient {
79
80
  return this.readData(modelName, rest.wyID);
80
81
  }
81
82
 
82
- // 兼容旧版平铺参数
83
- return this.readData(modelName, rest);
83
+ throw new Error("readData only supports single-record reads by wyID. Use invoke('data/list', ...) or invoke('data/query', ...) for list/query reads.");
84
84
  },
85
85
 
86
86
  "deleteData": ({ modelName, wyID, options }) =>
@@ -103,8 +103,31 @@ class AppsClient extends BaseAppsClient {
103
103
  "createRole": params => this.createRole(params),
104
104
  "updateUserRole": params => this.updateUserRole(params),
105
105
  "users/update-password": params => this.updateUserPassword(params),
106
+ "users/remove": params => this.removeAppUser(params),
107
+ "users/delete": params => this.deleteAppUser(params),
106
108
  "listAppUsers": params => this.listAppUsers(params),
107
109
 
110
+ // === 企业 IAM / 审计 / 文件鉴权 ===
111
+ "iam/me": params => this.getIamContext(params),
112
+ "iam/bootstrap": params => this.bootstrapIam(params),
113
+ "iam/org-units/list": params => this.listIamOrgUnits(params),
114
+ "iam/org-units": params => this.createIamOrgUnit(params),
115
+ "iam/org-units/update": params => this.updateIamOrgUnit(params),
116
+ "iam/org-units/delete": params => this.deleteIamOrgUnit(params),
117
+ "iam/roles/list": params => this.listIamRoles(params),
118
+ "iam/roles": params => this.createIamRole(params),
119
+ "iam/roles/update": params => this.updateIamRole(params),
120
+ "iam/roles/delete": params => this.deleteIamRole(params),
121
+ "iam/staff/list": params => this.listIamStaff(params),
122
+ "iam/staff": params => this.createIamStaff(params),
123
+ "iam/staff/update": params => this.updateIamStaff(params),
124
+ "iam/staff/delete": params => this.deleteIamStaff(params),
125
+ "data/model-policies/list": params => this.listModelPolicies(params),
126
+ "data/model-policies/declare": params => this.declareModelPolicy(params),
127
+ "audit/write": params => this.writeAuditLog(params),
128
+ "audit/query": params => this.queryAuditLogs(params),
129
+ "files/authorize-download": params => this.authorizeFileDownload(params),
130
+
108
131
  // === 文件存储 ===
109
132
  "uploadFile": ({ file }) => this.uploadFile(file),
110
133
  "uploadToR2": ({ file }) => this.uploadToR2(file),
@@ -213,7 +236,8 @@ Object.assign(
213
236
  appsClientToolsMethods,
214
237
  appsClientDebugMethods,
215
238
  appsClientCrawlerMethods,
216
- appsClientWechatMethods
239
+ appsClientWechatMethods,
240
+ appsClientIamMethods
217
241
  );
218
242
 
219
243
  export { AppsClient };
@@ -4,8 +4,10 @@ import { BaseAppsClient } from "./BaseAppsClient.js";
4
4
  * AI应用公共数据客户端 - 无需认证
5
5
  *
6
6
  * 核心理念:
7
- * - 用于匿名读取被 `writeData` 设为公共的数据。
7
+ * - 通过继承的通用 invoke(path, params) 调用匿名公开接口。
8
+ * - 用于匿名读取模型策略 `publicRead` 明确开放的 CMS 数据。
8
9
  * - 用于匿名提交数据到指定的、允许公开写入的模型。
10
+ * - 保留 `readConfiguredPublicData` 兼容旧 publicConfig 公开读。
9
11
  * - 此客户端的所有操作都无需用户登录。
10
12
  */
11
13
  export class AppsPublicClient extends BaseAppsClient {
@@ -2,10 +2,11 @@ import { getAppsApiBaseUrl, isBrowser, isBackend, isSSR, getGlobalOptions } from
2
2
 
3
3
  const ACCESS_TOKEN_STORAGE_KEY = "tacoreai_apps_access_token";
4
4
  const PREVIEW_APPSERVER_API_KEY_HEADER = "x-tacore-preview-app-server-api-key";
5
+ const FORWARDED_APP_SERVER_API_KEY_HEADER = "x-tacore-forwarded-app-server-api-key";
5
6
  const APP_SERVER_SERVICE_TOKEN_HEADER = "x-tacore-app-server-service-token";
6
7
  const APP_SERVER_OWNER_APP_ID_HEADER = "x-tacore-app-server-owner-app-id";
7
- const INTEROP_APP_SERVER_API_KEY_REQUIRED_ERROR =
8
- "tacoreServerInteropAppServerApiKey is required for backend environment. Provide it in config or via TACORE_SERVER_INTEROP_APP_SERVER_API_KEY env var.";
8
+ const APP_SERVER_SERVICE_CREDENTIAL_REQUIRED_ERROR =
9
+ "AppServer service credential is required for backend service auth. Provide TACORE_SERVER_INTEROP_APP_SERVER_API_KEY + TACORE_APP_SERVER_SERVICE_TOKEN, or forward a validated appServerApiKey.";
9
10
 
10
11
  const normalizeAccessToken = (value) => {
11
12
  if (typeof value !== "string") {
@@ -50,7 +51,17 @@ const writePersistedAccessToken = (token) => {
50
51
  };
51
52
 
52
53
  const isBackendPreviewMode = () => isBackend && process.env.TACORE_APPSERVER_PREVIEW_MODE === "true";
53
- const shouldRequireInteropAppServerApiKey = () => isBackend && !isSSR && !isBackendPreviewMode();
54
+ const hasAppServerServiceCredential = (config = {}) => {
55
+ if (config.tacoreServerInteropAppServerApiKey && config.tacoreAppServerServiceToken) {
56
+ return true;
57
+ }
58
+ if (config.appServerApiKey) {
59
+ return true;
60
+ }
61
+ return isBackendPreviewMode() && Boolean(config.previewAppServerApiKey);
62
+ };
63
+ const shouldRequireAppServerServiceCredential = (config = {}) =>
64
+ isBackend && !isSSR && config.enableAppServerServiceAuth === true;
54
65
 
55
66
  /**
56
67
  * Apps SDK 基类
@@ -96,8 +107,8 @@ export class BaseAppsClient {
96
107
  this.config.tacoreAppServerServiceToken = process.env.TACORE_APP_SERVER_SERVICE_TOKEN;
97
108
  }
98
109
 
99
- if (shouldRequireInteropAppServerApiKey() && !this.config.tacoreServerInteropAppServerApiKey) {
100
- throw new Error(INTEROP_APP_SERVER_API_KEY_REQUIRED_ERROR);
110
+ if (shouldRequireAppServerServiceCredential(this.config) && !hasAppServerServiceCredential(this.config)) {
111
+ throw new Error(APP_SERVER_SERVICE_CREDENTIAL_REQUIRED_ERROR);
101
112
  }
102
113
  }
103
114
 
@@ -167,10 +178,6 @@ export class BaseAppsClient {
167
178
  ...additionalHeaders,
168
179
  };
169
180
 
170
- if (this.config.datasource) {
171
- headers["x-datasource"] = this.config.datasource;
172
- }
173
-
174
181
  if (this.config.supabaseBaseUrl) {
175
182
  headers["x-supabase-base-url"] = this.config.supabaseBaseUrl;
176
183
  }
@@ -196,17 +203,20 @@ export class BaseAppsClient {
196
203
  headers["Authorization"] = `Bearer ${token}`;
197
204
  }
198
205
 
199
- if (
200
- isBackend &&
201
- !isSSR &&
202
- !token &&
203
- this.config.enableAppServerServiceAuth === true &&
204
- this.config.tacoreServerInteropAppServerApiKey &&
205
- this.config.tacoreAppServerServiceToken
206
- ) {
207
- headers["x-tacore-server-interop-app-server-api-key"] = this.config.tacoreServerInteropAppServerApiKey;
208
- headers[APP_SERVER_SERVICE_TOKEN_HEADER] = this.config.tacoreAppServerServiceToken;
209
- headers[APP_SERVER_OWNER_APP_ID_HEADER] = this.config.appServerOwnerAppId || this.config.appId || process.env.APP_ID || this.appId;
206
+ if (isBackend && !isSSR && !token && this.config.enableAppServerServiceAuth === true) {
207
+ if (this.config.tacoreServerInteropAppServerApiKey && this.config.tacoreAppServerServiceToken) {
208
+ headers["x-tacore-server-interop-app-server-api-key"] = this.config.tacoreServerInteropAppServerApiKey;
209
+ headers[APP_SERVER_SERVICE_TOKEN_HEADER] = this.config.tacoreAppServerServiceToken;
210
+ headers[APP_SERVER_OWNER_APP_ID_HEADER] = this.config.appServerOwnerAppId || this.config.appId || process.env.APP_ID || this.appId;
211
+ } else if (this.config.appServerApiKey && !headers[FORWARDED_APP_SERVER_API_KEY_HEADER]) {
212
+ headers[FORWARDED_APP_SERVER_API_KEY_HEADER] = this.config.appServerApiKey;
213
+ } else if (
214
+ isBackendPreviewMode() &&
215
+ this.config.previewAppServerApiKey &&
216
+ !headers[PREVIEW_APPSERVER_API_KEY_HEADER]
217
+ ) {
218
+ headers[PREVIEW_APPSERVER_API_KEY_HEADER] = this.config.previewAppServerApiKey;
219
+ }
210
220
  } else if (
211
221
  isBackendPreviewMode() &&
212
222
  !token &&
@@ -323,6 +333,7 @@ export class BaseAppsClient {
323
333
  getConfig() {
324
334
  const config = { ...this.config };
325
335
  delete config.accessToken;
336
+ delete config.appServerApiKey;
326
337
  delete config.previewAppServerApiKey;
327
338
  return config;
328
339
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@tacoreai/web-sdk",
3
3
  "description": "This file is for app server package, not the real npm package",
4
- "version": "1.22.1-beta.0",
4
+ "version": "1.22.1-beta.2",
5
5
  "type": "module",
6
6
  "publishConfig": {
7
7
  "access": "public",
package/utils/index.js CHANGED
@@ -54,11 +54,8 @@ export function getAppsApiBaseUrl() {
54
54
  }
55
55
  // 后端环境逻辑
56
56
  if (isBackend) {
57
- // stone 手动: tacore server 内网地址
58
- const baseUrl = process.env.TACORE_SERVER_INTRANET_BASE_URL;
59
- if (!baseUrl) {
60
- throw new Error("TACORE_SERVER_INTRANET_BASE_URL environment variable is not set in the backend environment.");
61
- }
57
+ // stone 手动:tacore server 地址,优先使用内网地址,兜底走公网(比如平台外本地部署)
58
+ const baseUrl = process.env.TACORE_SERVER_INTRANET_BASE_URL || 'https://api.tacore.chat';
62
59
  return baseUrl;
63
60
  }
64
61