@tacoreai/web-sdk 1.23.0 → 1.24.1-beta.1
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/README.md +39 -0
- package/TacoreActionSDK.js +206 -0
- package/database/core/apps/AppServerRuntime.js +76 -46
- package/database/core/apps/AppsClient.AppServer.js +53 -15
- package/database/core/apps/AppsClient.IAM.js +0 -32
- package/database/core/apps/AppsClient.js +1 -8
- package/index.js +1 -0
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -17,6 +17,45 @@ npm install @tacoreai/web-sdk
|
|
|
17
17
|
### AppsClient (Data & AI)
|
|
18
18
|
示例略(保持原有章节)
|
|
19
19
|
|
|
20
|
+
### TacoreActionSDK (Headless Capability)
|
|
21
|
+
|
|
22
|
+
组织通用 Agent、外部 Agent 或 CLI 壳应通过 `TacoreActionSDK` 调用云应用暴露的 `/.tacore/capabilities.json`,而不是依赖应用 UI、DOM、按钮或页面路径。应用 GUI 只是 capability 的一个使用者,必须和 SDK 执行共享同一 Action Registry / service 层。
|
|
23
|
+
|
|
24
|
+
```js
|
|
25
|
+
import { TacoreActionSDK } from '@tacoreai/web-sdk'
|
|
26
|
+
|
|
27
|
+
const actionSdk = new TacoreActionSDK({
|
|
28
|
+
accessToken: 'PLATFORM_ACCESS_TOKEN',
|
|
29
|
+
organizationId: 'org_xxx',
|
|
30
|
+
appId: 'crm_app_xxx',
|
|
31
|
+
})
|
|
32
|
+
|
|
33
|
+
const manifest = await actionSdk.listCapabilities()
|
|
34
|
+
const schema = await actionSdk.getCapabilitySchema('crm.lead.create')
|
|
35
|
+
const draft = await actionSdk.draft('crm.lead.create', {
|
|
36
|
+
name: 'Acme',
|
|
37
|
+
phone: '13800000000',
|
|
38
|
+
})
|
|
39
|
+
|
|
40
|
+
// 写入类能力应先展示 preview/dry-run,再经用户确认后执行。
|
|
41
|
+
await actionSdk.execute('crm.lead.create', {
|
|
42
|
+
draftId: draft.draftId,
|
|
43
|
+
action: {
|
|
44
|
+
name: 'crm.lead.create',
|
|
45
|
+
idempotencyKey: 'lead:13800000000',
|
|
46
|
+
steps: [
|
|
47
|
+
{
|
|
48
|
+
op: 'create',
|
|
49
|
+
modelName: 'lead',
|
|
50
|
+
data: { name: 'Acme', phone: '13800000000' },
|
|
51
|
+
},
|
|
52
|
+
],
|
|
53
|
+
},
|
|
54
|
+
})
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
SDK 封装平台 `/plat/agent/capabilities` 的发现、dry-run 与 execute 请求;核心执行仍落到 manifest 声明的 `actions/run` 事务契约,CLI 只是同一接口的一层命令行包装。
|
|
58
|
+
|
|
20
59
|
### AppsClient (Events)
|
|
21
60
|
|
|
22
61
|
应用自定义埋点应使用事件日志接口,不要把高频事件写入通用数据模型。
|
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
import { getAppsApiBaseUrl, getGlobalOptions, isBrowser } from "./utils/index.js";
|
|
2
|
+
|
|
3
|
+
const PLATFORM_ACCESS_TOKEN_STORAGE_KEY = "tacoreai_access_token";
|
|
4
|
+
|
|
5
|
+
function normalizeString(value) {
|
|
6
|
+
return String(value || "").trim();
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
function normalizeAccessToken(value) {
|
|
10
|
+
const token = normalizeString(value);
|
|
11
|
+
return token.replace(/^Bearer\s+/i, "").trim();
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
function readPlatformAccessToken() {
|
|
15
|
+
if (!isBrowser) {
|
|
16
|
+
return "";
|
|
17
|
+
}
|
|
18
|
+
try {
|
|
19
|
+
return normalizeAccessToken(localStorage.getItem(PLATFORM_ACCESS_TOKEN_STORAGE_KEY));
|
|
20
|
+
} catch (error) {
|
|
21
|
+
console.error("[TacoreActionSDK] Failed to read platform access token:", error);
|
|
22
|
+
return "";
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function buildQuery(params = {}) {
|
|
27
|
+
const searchParams = new URLSearchParams();
|
|
28
|
+
Object.entries(params).forEach(([key, value]) => {
|
|
29
|
+
if (value === undefined || value === null || value === "") {
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
searchParams.append(key, typeof value === "object" ? JSON.stringify(value) : String(value));
|
|
33
|
+
});
|
|
34
|
+
const query = searchParams.toString();
|
|
35
|
+
return query ? `?${query}` : "";
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function resolveDraftCapabilityId(draft) {
|
|
39
|
+
return normalizeString(draft?.capabilityId || draft?.capability?.capabilityId);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export class TacoreActionSDK {
|
|
43
|
+
constructor(config = {}) {
|
|
44
|
+
const globalOptions = getGlobalOptions();
|
|
45
|
+
this.config = {
|
|
46
|
+
...globalOptions.clientDefaultConfig,
|
|
47
|
+
apiBaseUrl: getAppsApiBaseUrl(),
|
|
48
|
+
organizationId: "",
|
|
49
|
+
projectId: "",
|
|
50
|
+
appId: "",
|
|
51
|
+
...config,
|
|
52
|
+
};
|
|
53
|
+
this.accessToken = normalizeAccessToken(config.accessToken || "");
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
updateConfig(nextConfig = {}) {
|
|
57
|
+
if (Object.prototype.hasOwnProperty.call(nextConfig, "accessToken")) {
|
|
58
|
+
this.setAccessToken(nextConfig.accessToken);
|
|
59
|
+
}
|
|
60
|
+
this.config = {
|
|
61
|
+
...this.config,
|
|
62
|
+
...nextConfig,
|
|
63
|
+
accessToken: this.accessToken,
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
setAccessToken(token) {
|
|
68
|
+
this.accessToken = normalizeAccessToken(token);
|
|
69
|
+
this.config.accessToken = this.accessToken;
|
|
70
|
+
return this.accessToken;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
getAccessToken() {
|
|
74
|
+
return this.accessToken || readPlatformAccessToken();
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
getHeaders(options = {}) {
|
|
78
|
+
const headers = {
|
|
79
|
+
"Content-Type": "application/json",
|
|
80
|
+
...(options.headers || {}),
|
|
81
|
+
};
|
|
82
|
+
const organizationId = normalizeString(options.organizationId || this.config.organizationId);
|
|
83
|
+
const projectId = normalizeString(options.projectId || this.config.projectId);
|
|
84
|
+
const appId = normalizeString(options.appId || this.config.appId);
|
|
85
|
+
const token = this.getAccessToken();
|
|
86
|
+
|
|
87
|
+
if (organizationId) {
|
|
88
|
+
headers["x-org-id"] = organizationId;
|
|
89
|
+
}
|
|
90
|
+
if (projectId) {
|
|
91
|
+
headers["x-project-id"] = projectId;
|
|
92
|
+
}
|
|
93
|
+
if (appId) {
|
|
94
|
+
headers["x-app-id"] = appId;
|
|
95
|
+
}
|
|
96
|
+
if (token) {
|
|
97
|
+
headers.Authorization = `Bearer ${token}`;
|
|
98
|
+
}
|
|
99
|
+
return headers;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
async request(endpoint, options = {}) {
|
|
103
|
+
const response = await fetch(`${this.config.apiBaseUrl}${endpoint}`, {
|
|
104
|
+
...options,
|
|
105
|
+
headers: this.getHeaders(options),
|
|
106
|
+
});
|
|
107
|
+
const result = await response.json().catch(() => ({}));
|
|
108
|
+
if (!response.ok || result?.success === false) {
|
|
109
|
+
const error = new Error(result?.message || result?.error || `HTTP ${response.status}: ${response.statusText}`);
|
|
110
|
+
error.status = response.status;
|
|
111
|
+
error.code = result?.code;
|
|
112
|
+
error.details = result?.details;
|
|
113
|
+
error.responseData = result;
|
|
114
|
+
throw error;
|
|
115
|
+
}
|
|
116
|
+
return result?.data ?? result;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
get(endpoint, params = {}, options = {}) {
|
|
120
|
+
return this.request(`${endpoint}${buildQuery(params)}`, {
|
|
121
|
+
...options,
|
|
122
|
+
method: "GET",
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
post(endpoint, body = {}, options = {}) {
|
|
127
|
+
return this.request(endpoint, {
|
|
128
|
+
...options,
|
|
129
|
+
method: "POST",
|
|
130
|
+
body: JSON.stringify(body),
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
listCapabilities(options = {}) {
|
|
135
|
+
const appId = normalizeString(options.appId || this.config.appId);
|
|
136
|
+
return this.get("/plat/agent/capabilities", { appId }, { appId, ...options });
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
getCapabilitySchema(capabilityId, options = {}) {
|
|
140
|
+
const appId = normalizeString(options.appId || this.config.appId);
|
|
141
|
+
const normalizedCapabilityId = encodeURIComponent(normalizeString(capabilityId));
|
|
142
|
+
return this.get(`/plat/agent/capabilities/${normalizedCapabilityId}`, { appId }, { appId, ...options });
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
dryRun(capabilityId, options = {}) {
|
|
146
|
+
const appId = normalizeString(options.appId || this.config.appId);
|
|
147
|
+
return this.post("/plat/agent/capabilities/dry-run", {
|
|
148
|
+
appId,
|
|
149
|
+
capabilityId: normalizeString(capabilityId),
|
|
150
|
+
input: options.input || {},
|
|
151
|
+
action: options.action || null,
|
|
152
|
+
options: options.options || {},
|
|
153
|
+
}, { appId, ...options });
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
draft(capabilityId, inputOrOptions = {}, maybeOptions = {}) {
|
|
157
|
+
const isOptionsObject = inputOrOptions && typeof inputOrOptions === "object" && (
|
|
158
|
+
Object.prototype.hasOwnProperty.call(inputOrOptions, "input") ||
|
|
159
|
+
Object.prototype.hasOwnProperty.call(inputOrOptions, "appId") ||
|
|
160
|
+
Object.prototype.hasOwnProperty.call(inputOrOptions, "options")
|
|
161
|
+
);
|
|
162
|
+
const options = isOptionsObject
|
|
163
|
+
? inputOrOptions
|
|
164
|
+
: {
|
|
165
|
+
...maybeOptions,
|
|
166
|
+
input: inputOrOptions || {},
|
|
167
|
+
};
|
|
168
|
+
return this.dryRun(capabilityId, options);
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
preview(draftOrCapabilityId, options = {}) {
|
|
172
|
+
if (draftOrCapabilityId && typeof draftOrCapabilityId === "object") {
|
|
173
|
+
if (draftOrCapabilityId.dryRun === true) {
|
|
174
|
+
return Promise.resolve(draftOrCapabilityId);
|
|
175
|
+
}
|
|
176
|
+
return this.dryRun(resolveDraftCapabilityId(draftOrCapabilityId), {
|
|
177
|
+
...draftOrCapabilityId,
|
|
178
|
+
...options,
|
|
179
|
+
});
|
|
180
|
+
}
|
|
181
|
+
return this.dryRun(draftOrCapabilityId, options);
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
execute(draftOrCapabilityId, options = {}) {
|
|
185
|
+
const draft = draftOrCapabilityId && typeof draftOrCapabilityId === "object" ? draftOrCapabilityId : null;
|
|
186
|
+
const capabilityId = draft ? resolveDraftCapabilityId(draft) : normalizeString(draftOrCapabilityId);
|
|
187
|
+
const appId = normalizeString(options.appId || draft?.appId || this.config.appId);
|
|
188
|
+
return this.post("/plat/agent/capabilities/execute", {
|
|
189
|
+
appId,
|
|
190
|
+
capabilityId,
|
|
191
|
+
draftId: normalizeString(options.draftId || draft?.draftId),
|
|
192
|
+
input: options.input || draft?.input || {},
|
|
193
|
+
action: options.action || draft?.action || null,
|
|
194
|
+
options: options.options || {},
|
|
195
|
+
}, { appId, ...options });
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
runTransactionAction({ appId, capabilityId, input = {}, action, options = {} } = {}) {
|
|
199
|
+
return this.execute(capabilityId, {
|
|
200
|
+
appId,
|
|
201
|
+
input,
|
|
202
|
+
action,
|
|
203
|
+
options,
|
|
204
|
+
});
|
|
205
|
+
}
|
|
206
|
+
}
|
|
@@ -7,6 +7,7 @@ const DEFAULT_FILE_SIZE_LIMIT = 100 * 1024 * 1024;
|
|
|
7
7
|
const DEFAULT_MAX_FILES = 20;
|
|
8
8
|
|
|
9
9
|
const isPreviewMode = () => process.env.TACORE_APPSERVER_PREVIEW_MODE === "true";
|
|
10
|
+
const isLocalDeploymentMode = () => process.env.TACORE_APPSERVER_LOCAL_DEPLOYMENT_MODE === "true";
|
|
10
11
|
|
|
11
12
|
const isValidInternalToken = (incomingValue, expectedValue) => {
|
|
12
13
|
return Boolean(expectedValue) && Boolean(incomingValue) && incomingValue === expectedValue;
|
|
@@ -78,6 +79,15 @@ const validateExternalApiKey = async (appsClient, appId, apiKey) => {
|
|
|
78
79
|
return Boolean(result?.success && result?.data?.valid);
|
|
79
80
|
};
|
|
80
81
|
|
|
82
|
+
const validateLocalDeploymentAccessToken = async (env, accessToken) => {
|
|
83
|
+
const authClient = createRequestScopedAppsClient(env, {
|
|
84
|
+
accessToken,
|
|
85
|
+
clientAppId: env.appId,
|
|
86
|
+
});
|
|
87
|
+
const result = await authClient.getIamContext({});
|
|
88
|
+
return Boolean(result?.success);
|
|
89
|
+
};
|
|
90
|
+
|
|
81
91
|
const getPreviewGuidePayload = (req) => {
|
|
82
92
|
const baseUrl = `${req.protocol}://${req.get("host")}`;
|
|
83
93
|
const invokeUrl = `${baseUrl}/invokeAppServerAPI?apiName=YOUR_API_NAME`;
|
|
@@ -300,51 +310,71 @@ export const createAppServerRuntime = async (options = {}) => {
|
|
|
300
310
|
|
|
301
311
|
if (!isPlatformProxyRequest && !isSchedulerRequest) {
|
|
302
312
|
if (!requestApiKey) {
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
313
|
+
if (isLocalDeploymentMode() && requestAccessToken) {
|
|
314
|
+
try {
|
|
315
|
+
const isValidAccessToken = await validateLocalDeploymentAccessToken(env, requestAccessToken);
|
|
316
|
+
if (!isValidAccessToken) {
|
|
317
|
+
return res.status(401).json({
|
|
318
|
+
success: false,
|
|
319
|
+
code: "INVALID_ACCESS_TOKEN",
|
|
320
|
+
error: "Invalid Authorization token",
|
|
321
|
+
});
|
|
322
|
+
}
|
|
323
|
+
} catch (error) {
|
|
324
|
+
console.error("[AppServer] Failed to validate local deployment access token:", error);
|
|
325
|
+
return res.status(error.status || 401).json({
|
|
326
|
+
success: false,
|
|
327
|
+
code: "ACCESS_TOKEN_VALIDATION_FAILED",
|
|
328
|
+
error: error.message,
|
|
329
|
+
});
|
|
330
|
+
}
|
|
331
|
+
} else {
|
|
332
|
+
console.warn("[AppServer Auth] Missing X-API-Key. This request is treated as external call.");
|
|
333
|
+
return res.status(401).json({
|
|
334
|
+
success: false,
|
|
335
|
+
code: "MISSING_API_KEY",
|
|
336
|
+
error: "X-API-Key header is required",
|
|
337
|
+
});
|
|
338
|
+
}
|
|
339
|
+
} else {
|
|
340
|
+
if (!isPreviewApiKeyShapeValid(requestApiKey)) {
|
|
341
|
+
return res.status(400).json({
|
|
342
|
+
success: false,
|
|
343
|
+
code: "INVALID_API_KEY_FORMAT",
|
|
344
|
+
error: "X-API-Key format is invalid. Expected 8-128 chars: [A-Za-z0-9._-]",
|
|
345
|
+
});
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
let isValidApiKey = false;
|
|
349
|
+
try {
|
|
350
|
+
const authClient = createRequestScopedAppsClient(env, {
|
|
351
|
+
accessToken: requestAccessToken || null,
|
|
352
|
+
clientAppId: env.appId,
|
|
353
|
+
});
|
|
354
|
+
isValidApiKey = await validateExternalApiKey(authClient, env.appId, requestApiKey);
|
|
355
|
+
} catch (error) {
|
|
356
|
+
console.error("[AppServer] Failed to validate external API key:", error);
|
|
357
|
+
return res.status(500).json({
|
|
358
|
+
success: false,
|
|
359
|
+
code: "API_KEY_VALIDATION_FAILED",
|
|
360
|
+
error: error.message,
|
|
361
|
+
});
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
if (!isValidApiKey) {
|
|
365
|
+
return res.status(401).json({
|
|
366
|
+
success: false,
|
|
367
|
+
code: "INVALID_API_KEY",
|
|
368
|
+
error: "Invalid X-API-Key",
|
|
369
|
+
});
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
isExternalApiKeyRequest = true;
|
|
373
|
+
appServerApiKey = requestApiKey;
|
|
374
|
+
|
|
375
|
+
if (isPreviewMode() && !requestAccessToken) {
|
|
376
|
+
previewAppServerApiKey = requestApiKey;
|
|
377
|
+
}
|
|
348
378
|
}
|
|
349
379
|
}
|
|
350
380
|
|
|
@@ -398,7 +428,7 @@ export const createAppServerRuntime = async (options = {}) => {
|
|
|
398
428
|
|
|
399
429
|
const listen = (listenOptions = {}) => {
|
|
400
430
|
const port = listenOptions.port || process.env.PORT || 9000;
|
|
401
|
-
const host = listenOptions.host;
|
|
431
|
+
const host = listenOptions.host || process.env.HOST || "";
|
|
402
432
|
const callback = typeof listenOptions.onListen === "function"
|
|
403
433
|
? listenOptions.onListen
|
|
404
434
|
: () => {
|
|
@@ -21,6 +21,29 @@ const isBrowserPreviewRuntime = () => {
|
|
|
21
21
|
return pathname.startsWith("/app-run/");
|
|
22
22
|
};
|
|
23
23
|
|
|
24
|
+
const normalizeAppId = value => String(value || "").trim();
|
|
25
|
+
|
|
26
|
+
const getAppServerOwnerAppId = function() {
|
|
27
|
+
const explicitOwnerAppId = normalizeAppId(this.config?.appServerOwnerAppId);
|
|
28
|
+
if (explicitOwnerAppId) {
|
|
29
|
+
return explicitOwnerAppId;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const shellAppId = normalizeAppId(this.config?.appId);
|
|
33
|
+
if (shellAppId) {
|
|
34
|
+
return shellAppId;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
if (typeof this._getChannelAppId === "function") {
|
|
38
|
+
const channelAppId = normalizeAppId(this._getChannelAppId());
|
|
39
|
+
if (channelAppId) {
|
|
40
|
+
return channelAppId;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
return normalizeAppId(this.appId);
|
|
45
|
+
};
|
|
46
|
+
|
|
24
47
|
const hasOwn = (value, key) => Object.prototype.hasOwnProperty.call(value, key);
|
|
25
48
|
|
|
26
49
|
const isPlainObject = (value) => Object.prototype.toString.call(value) === "[object Object]";
|
|
@@ -100,22 +123,23 @@ const clearPreviewSessionToken = (appId) => {
|
|
|
100
123
|
|
|
101
124
|
const getPreviewSessionToken = async function(options = {}) {
|
|
102
125
|
const { forceRefresh = false } = options;
|
|
126
|
+
const appServerOwnerAppId = getAppServerOwnerAppId.call(this);
|
|
103
127
|
if (forceRefresh) {
|
|
104
|
-
clearPreviewSessionToken(
|
|
128
|
+
clearPreviewSessionToken(appServerOwnerAppId);
|
|
105
129
|
}
|
|
106
130
|
|
|
107
|
-
const cachedToken = previewSessionTokenCache.get(
|
|
131
|
+
const cachedToken = previewSessionTokenCache.get(appServerOwnerAppId);
|
|
108
132
|
if (cachedToken) {
|
|
109
133
|
return cachedToken;
|
|
110
134
|
}
|
|
111
135
|
|
|
112
|
-
const pendingRequest = previewSessionPromiseCache.get(
|
|
136
|
+
const pendingRequest = previewSessionPromiseCache.get(appServerOwnerAppId);
|
|
113
137
|
if (pendingRequest) {
|
|
114
138
|
return pendingRequest;
|
|
115
139
|
}
|
|
116
140
|
|
|
117
141
|
const requestPromise = (async () => {
|
|
118
|
-
const endpoint = `/appserver-preview/session?appId=${encodeURIComponent(
|
|
142
|
+
const endpoint = `/appserver-preview/session?appId=${encodeURIComponent(appServerOwnerAppId)}`;
|
|
119
143
|
const response = await fetch(endpoint, {
|
|
120
144
|
method: "GET",
|
|
121
145
|
headers: {
|
|
@@ -138,15 +162,15 @@ const getPreviewSessionToken = async function(options = {}) {
|
|
|
138
162
|
throw new Error("[AppServer-PreviewSession] Missing preview token in session response.");
|
|
139
163
|
}
|
|
140
164
|
|
|
141
|
-
previewSessionTokenCache.set(
|
|
165
|
+
previewSessionTokenCache.set(appServerOwnerAppId, previewToken);
|
|
142
166
|
return previewToken;
|
|
143
167
|
})();
|
|
144
168
|
|
|
145
|
-
previewSessionPromiseCache.set(
|
|
169
|
+
previewSessionPromiseCache.set(appServerOwnerAppId, requestPromise);
|
|
146
170
|
try {
|
|
147
171
|
return await requestPromise;
|
|
148
172
|
} finally {
|
|
149
|
-
previewSessionPromiseCache.delete(
|
|
173
|
+
previewSessionPromiseCache.delete(appServerOwnerAppId);
|
|
150
174
|
}
|
|
151
175
|
};
|
|
152
176
|
|
|
@@ -367,7 +391,8 @@ const buildRemoteRequestInit = function(
|
|
|
367
391
|
};
|
|
368
392
|
|
|
369
393
|
const invokeByPlatformProxy = async function(appServerAPIName, payload, options = {}) {
|
|
370
|
-
const
|
|
394
|
+
const appServerOwnerAppId = getAppServerOwnerAppId.call(this);
|
|
395
|
+
const endpoint = `/apps/invokeAppServerAPI?appId=${encodeURIComponent(appServerOwnerAppId)}&apiName=${encodeURIComponent(appServerAPIName)}`;
|
|
371
396
|
const response = await fetch(
|
|
372
397
|
`${this.config.apiBaseUrl}${endpoint}`,
|
|
373
398
|
buildRemoteRequestInit.call(this, payload, options, {}, {
|
|
@@ -378,7 +403,8 @@ const invokeByPlatformProxy = async function(appServerAPIName, payload, options
|
|
|
378
403
|
};
|
|
379
404
|
|
|
380
405
|
const invokeByPreviewProxyOnce = async function(appServerAPIName, payload, options = {}, previewToken) {
|
|
381
|
-
const
|
|
406
|
+
const appServerOwnerAppId = getAppServerOwnerAppId.call(this);
|
|
407
|
+
const endpoint = `/appserver-preview/invokeAppServerAPI?appId=${encodeURIComponent(appServerOwnerAppId)}&apiName=${encodeURIComponent(appServerAPIName)}`;
|
|
382
408
|
const response = await fetch(
|
|
383
409
|
endpoint,
|
|
384
410
|
buildRemoteRequestInit.call(this, payload, options, {
|
|
@@ -398,7 +424,7 @@ const invokeByPreviewProxy = async function(appServerAPIName, payload, options =
|
|
|
398
424
|
throw error;
|
|
399
425
|
}
|
|
400
426
|
|
|
401
|
-
clearPreviewSessionToken(this
|
|
427
|
+
clearPreviewSessionToken(getAppServerOwnerAppId.call(this));
|
|
402
428
|
previewToken = await getPreviewSessionToken.call(this, { forceRefresh: true });
|
|
403
429
|
return await invokeByPreviewProxyOnce.call(this, appServerAPIName, payload, options, previewToken);
|
|
404
430
|
}
|
|
@@ -406,17 +432,26 @@ const invokeByPreviewProxy = async function(appServerAPIName, payload, options =
|
|
|
406
432
|
|
|
407
433
|
const invokeByCrossAppDirect = async function(appServerAPIName, payload, options = {}) {
|
|
408
434
|
const endpoint = `${this.config.appServerBaseUrl}/invokeAppServerAPI?apiName=${encodeURIComponent(appServerAPIName)}`;
|
|
409
|
-
const
|
|
410
|
-
"X-API-Key": this.config.appServerApiKey
|
|
411
|
-
|
|
435
|
+
const extraHeaders = this.config.appServerApiKey
|
|
436
|
+
? { "X-API-Key": this.config.appServerApiKey }
|
|
437
|
+
: {};
|
|
438
|
+
const requestInit = buildRemoteRequestInit.call(this, payload, options, extraHeaders);
|
|
412
439
|
const response = await fetch(endpoint, requestInit);
|
|
413
440
|
return await unwrapAppServerResponse(response, "[AppServer-CrossApp]");
|
|
414
441
|
};
|
|
415
442
|
|
|
443
|
+
const canUseBackendDirectAppServer = function() {
|
|
444
|
+
return Boolean(this.config.appServerBaseUrl && this.config.appServerApiKey);
|
|
445
|
+
};
|
|
446
|
+
|
|
447
|
+
const canUseProductionBrowserDirectAppServer = function() {
|
|
448
|
+
return Boolean(this.config.appServerBaseUrl);
|
|
449
|
+
};
|
|
450
|
+
|
|
416
451
|
/**
|
|
417
452
|
* 调用一个 App Server API。
|
|
418
453
|
* - 开发态浏览器:预览页走 CLI preview bridge,其他页面走平台转发。
|
|
419
|
-
* -
|
|
454
|
+
* - 生产态浏览器:配置了 appServerBaseUrl 时直连,使用当前用户 Authorization 鉴权。
|
|
420
455
|
* - 后端跨应用:允许使用 appServerBaseUrl + appServerApiKey 直连目标 AppServer。
|
|
421
456
|
* - 后端当前应用:从本地注册表执行。
|
|
422
457
|
*
|
|
@@ -430,7 +465,10 @@ const invokeAppServerAPI = async function(appServerAPIName, payload, options = {
|
|
|
430
465
|
throw new Error("appServerAPIName is required");
|
|
431
466
|
}
|
|
432
467
|
|
|
433
|
-
if (
|
|
468
|
+
if (
|
|
469
|
+
(isBackend && canUseBackendDirectAppServer.call(this)) ||
|
|
470
|
+
(isProductionBrowser && canUseProductionBrowserDirectAppServer.call(this))
|
|
471
|
+
) {
|
|
434
472
|
return await invokeByCrossAppDirect.call(this, appServerAPIName, payload, options);
|
|
435
473
|
}
|
|
436
474
|
|
|
@@ -7,22 +7,6 @@ export const appsClientIamMethods = {
|
|
|
7
7
|
return this._post("/apps/iam/bootstrap", params);
|
|
8
8
|
},
|
|
9
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
10
|
async listIamRoles(params = {}) {
|
|
27
11
|
return this._post("/apps/iam/roles/list", params);
|
|
28
12
|
},
|
|
@@ -39,22 +23,6 @@ export const appsClientIamMethods = {
|
|
|
39
23
|
return this._post("/apps/iam/roles/delete", params);
|
|
40
24
|
},
|
|
41
25
|
|
|
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
26
|
async listModelPolicies(params = {}) {
|
|
59
27
|
return this._post("/apps/data/model-policies/list", params);
|
|
60
28
|
},
|
|
@@ -56,6 +56,7 @@ class AppsClient extends BaseAppsClient {
|
|
|
56
56
|
/**
|
|
57
57
|
* 定义策略适配器映射
|
|
58
58
|
* 将 invoke 的对象参数转换为老方法的位置参数
|
|
59
|
+
* stone手动: 注意!!! 无需前端特殊逻辑的新方法,仅需直接在后端新增 xxx/yyy 这种接口即可,websdk 不需要发版,应用层直接 client.invoke('xxx/yyy', params) 即可调用;需要前端特殊逻辑适配的方法,才需要在这里新增适配器
|
|
59
60
|
*/
|
|
60
61
|
get adapters() {
|
|
61
62
|
return {
|
|
@@ -113,18 +114,10 @@ class AppsClient extends BaseAppsClient {
|
|
|
113
114
|
// === 企业 IAM / 审计 / 文件鉴权 ===
|
|
114
115
|
"iam/me": params => this.getIamContext(params),
|
|
115
116
|
"iam/bootstrap": params => this.bootstrapIam(params),
|
|
116
|
-
"iam/org-units/list": params => this.listIamOrgUnits(params),
|
|
117
|
-
"iam/org-units": params => this.createIamOrgUnit(params),
|
|
118
|
-
"iam/org-units/update": params => this.updateIamOrgUnit(params),
|
|
119
|
-
"iam/org-units/delete": params => this.deleteIamOrgUnit(params),
|
|
120
117
|
"iam/roles/list": params => this.listIamRoles(params),
|
|
121
118
|
"iam/roles": params => this.createIamRole(params),
|
|
122
119
|
"iam/roles/update": params => this.updateIamRole(params),
|
|
123
120
|
"iam/roles/delete": params => this.deleteIamRole(params),
|
|
124
|
-
"iam/staff/list": params => this.listIamStaff(params),
|
|
125
|
-
"iam/staff": params => this.createIamStaff(params),
|
|
126
|
-
"iam/staff/update": params => this.updateIamStaff(params),
|
|
127
|
-
"iam/staff/delete": params => this.deleteIamStaff(params),
|
|
128
121
|
"data/model-policies/list": params => this.listModelPolicies(params),
|
|
129
122
|
"data/model-policies/declare": params => this.declareModelPolicy(params),
|
|
130
123
|
"audit/write": params => this.writeAuditLog(params),
|
package/index.js
CHANGED
|
@@ -4,6 +4,7 @@ import * as utils from "./utils/index.js"
|
|
|
4
4
|
export { AppsClient } from "./database/core/apps/AppsClient.js"
|
|
5
5
|
export { AppsAuthManager } from "./database/core/apps/AppsAuthManager.js"
|
|
6
6
|
export { AppsPublicClient } from "./database/core/apps/AppsPublicClient.js"
|
|
7
|
+
export { TacoreActionSDK } from "./TacoreActionSDK.js"
|
|
7
8
|
|
|
8
9
|
// 导出工具函数
|
|
9
10
|
export { utils }
|
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.
|
|
4
|
+
"version": "1.24.1-beta.1",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"publishConfig": {
|
|
7
7
|
"access": "public",
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
"./app-server-runtime": "./database/core/apps/AppServerRuntime.js"
|
|
14
14
|
},
|
|
15
15
|
"scripts": {
|
|
16
|
-
"release": "npm version minor && git commit -am \"web-sdk update version\" &&
|
|
16
|
+
"release": "npm version minor && git commit -am \"web-sdk update version\" && npm publish && git push",
|
|
17
17
|
"release:beta": "npm version prerelease --preid beta --no-git-tag-version && git commit -am \"web-sdk update beta version\" && npm publish --tag beta"
|
|
18
18
|
}
|
|
19
19
|
}
|