@tacoreai/web-sdk 1.22.0 → 1.22.1-beta.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.
package/README.md
CHANGED
|
@@ -17,52 +17,63 @@ npm install @tacoreai/web-sdk
|
|
|
17
17
|
### AppsClient (Data & AI)
|
|
18
18
|
示例略(保持原有章节)
|
|
19
19
|
|
|
20
|
+
### AppsClient (Events)
|
|
21
|
+
|
|
22
|
+
应用自定义埋点应使用事件日志接口,不要把高频事件写入通用数据模型。
|
|
23
|
+
应用代码不要直接请求 `/plat` 平台接口,也不要读取平台登录 token;写入和读取都应通过 `AppsClient.invoke(...)` 走 `/apps/events/*`。事件日志不新增 WebSDK 专用方法,apiName 直接和后端 path 对齐。
|
|
24
|
+
|
|
25
|
+
```js
|
|
26
|
+
const tracked = await client.invoke('events/track', {
|
|
27
|
+
eventName: 'signup_click',
|
|
28
|
+
properties: { plan: 'pro' },
|
|
29
|
+
visitorId: localStorage.getItem('visitor_id'),
|
|
30
|
+
sessionId: sessionStorage.getItem('session_id'),
|
|
31
|
+
path: window.location.pathname,
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
// 以下读取接口仅适用于已登录且具备 app owner/admin 角色的应用管理界面。
|
|
35
|
+
const event = await client.invoke('events/read', {
|
|
36
|
+
eventId: tracked?.data?.eventId,
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
const page = await client.invoke('events/query', {
|
|
40
|
+
day: '2026-05-14',
|
|
41
|
+
eventName: 'signup_click',
|
|
42
|
+
limit: 50,
|
|
43
|
+
});
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
可用接口包括 `events/track`、`events/batch-track`、`events/read`、`events/batch-read`、`events/query`。写入接口由后端校验应用部署域名或自定义域名来源;所有读取接口都要求 Apps 登录态且当前用户为应用 `owner/admin`。平台通用查看/搜索由平台工作视图承接。
|
|
47
|
+
|
|
20
48
|
---
|
|
21
49
|
|
|
22
|
-
## 文件存储 API(
|
|
50
|
+
## 文件存储 API(R2 / GCS)
|
|
23
51
|
|
|
24
52
|
先确保已通过 AppsAuthManager 登录,并使用 AppsClient 初始化时提供 appId。以下接口均作用于 /apps 路由,需携带 Authorization。
|
|
25
53
|
|
|
26
|
-
1)
|
|
54
|
+
1) 上传文件到 R2(公开访问 / 前端展示)
|
|
27
55
|
|
|
28
56
|
import { AppsClient } from '@tacoreai/web-sdk'
|
|
29
57
|
const client = new AppsClient('YOUR_APP_ID')
|
|
30
58
|
|
|
31
59
|
const input = document.querySelector('#fileInput')
|
|
32
60
|
const file = input.files[0]
|
|
33
|
-
const result = await client.uploadFile(file
|
|
34
|
-
// result
|
|
61
|
+
const result = await client.uploadFile(file)
|
|
62
|
+
// result => { success: true, url, key }
|
|
35
63
|
|
|
36
|
-
2)
|
|
64
|
+
2) 下载 R2 文件
|
|
37
65
|
|
|
38
|
-
const
|
|
39
|
-
|
|
66
|
+
const response = await client.downloadFromR2('<key>')
|
|
67
|
+
const blob = await response.blob()
|
|
40
68
|
|
|
41
|
-
3)
|
|
69
|
+
3) 删除 R2 文件
|
|
42
70
|
|
|
43
|
-
await client.
|
|
71
|
+
await client.deleteFromR2('<key>')
|
|
44
72
|
|
|
45
|
-
4)
|
|
73
|
+
4) 上传文件到 GCS(Gemini / 多模态分析输入)
|
|
46
74
|
|
|
47
|
-
const
|
|
48
|
-
//
|
|
49
|
-
const a = document.createElement('a')
|
|
50
|
-
a.href = URL.createObjectURL(blob)
|
|
51
|
-
a.download = 'download.bin'
|
|
52
|
-
a.click()
|
|
53
|
-
URL.revokeObjectURL(a.href)
|
|
54
|
-
|
|
55
|
-
---
|
|
56
|
-
|
|
57
|
-
## [新增] 文件上传与分析
|
|
58
|
-
|
|
59
|
-
此接口用于上传文件(如 PDF、图片)以供 AI 进行深度分析或作为图片编辑的输入。
|
|
60
|
-
|
|
61
|
-
### `uploadFileForAnalysis(file)`
|
|
62
|
-
- **功能**: 将文件上传至 Google 的专用存储,并返回一个可供 AI 模型引用的 `fileUri`。
|
|
63
|
-
- **参数**:
|
|
64
|
-
- `file` (File): 从文件输入框获取的 File 对象。
|
|
65
|
-
- **返回值**: `Promise<{fileUri: string, mimeType: string, ...}>`
|
|
75
|
+
const gcsResult = await client.uploadToGCS(file)
|
|
76
|
+
// gcsResult.fileUri => gs://... ,供 Gemini 等模型使用
|
|
66
77
|
|
|
67
78
|
---
|
|
68
79
|
|
|
@@ -111,12 +122,12 @@ const imageUri = result.data.uri;
|
|
|
111
122
|
- **返回**: `Promise<Object>` (后端响应对象,包含 `data.uri` 或 `data.images`)
|
|
112
123
|
|
|
113
124
|
### 核心流程
|
|
114
|
-
1. **上传图片**:
|
|
125
|
+
1. **上传图片**: Google / Gemini 链路下,所有源图片(包括底图、叠加图、掩膜图)都应先通过 `uploadToGCS(file)` 获取 `gs://` 形式的 `fileUri`。
|
|
115
126
|
2. **调用编辑**: 使用获取到的 `fileUri` 和 `mimeType`,通过 `editImage` 方法并采用**简化的 `prompt + inputs` 模式**进行调用。
|
|
116
127
|
|
|
117
128
|
### 前置要求
|
|
118
129
|
- **必须先登录** (AppsAuthManager)
|
|
119
|
-
- `
|
|
130
|
+
- `uploadToGCS`
|
|
120
131
|
|
|
121
132
|
### 1. 简化调用 (prompt + inputs 模式,推荐)
|
|
122
133
|
SDK 会自动将 `prompt` 和 `inputs` 数组组合成标准的多模态 `messages`。
|
|
@@ -125,13 +136,13 @@ import { AppsClient } from '@tacoreai/web-sdk'
|
|
|
125
136
|
const client = new AppsClient('YOUR_APP_ID')
|
|
126
137
|
|
|
127
138
|
// 1. 上传源文件
|
|
128
|
-
const baseFile = await client.
|
|
139
|
+
const baseFile = await client.uploadToGCS(file);
|
|
129
140
|
|
|
130
141
|
// 2. 调用编辑接口
|
|
131
142
|
const result = await client.editImage({
|
|
132
143
|
prompt: '仅移除图中叠加的文字,保持其他区域不变,输出高清。',
|
|
133
144
|
inputs: [
|
|
134
|
-
{ type: 'uploaded_file', fileUri: baseFile.fileUri, mimeType:
|
|
145
|
+
{ type: 'uploaded_file', fileUri: baseFile.fileUri, mimeType: file.type }
|
|
135
146
|
]
|
|
136
147
|
});
|
|
137
148
|
// 获取图片 URI (兼容不同后端返回结构)
|
|
@@ -142,41 +153,41 @@ const editedBase64 = result.data.uri || (result.data.images && result.data.image
|
|
|
142
153
|
|
|
143
154
|
- **场景 A:单图编辑(去字/去水印/局部替换)**
|
|
144
155
|
|
|
145
|
-
const base = await client.
|
|
156
|
+
const base = await client.uploadToGCS(file);
|
|
146
157
|
const result = await client.editImage({
|
|
147
158
|
prompt: '移除叠加文字,其他区域完全保持不变。',
|
|
148
159
|
inputs: [
|
|
149
|
-
{ type: 'uploaded_file', fileUri: base.fileUri, mimeType:
|
|
160
|
+
{ type: 'uploaded_file', fileUri: base.fileUri, mimeType: file.type }
|
|
150
161
|
]
|
|
151
162
|
});
|
|
152
163
|
const base64 = result.data.uri || result.data.images?.[0]?.uri;
|
|
153
164
|
|
|
154
165
|
- **场景 B:双图融合(将服装穿到模特身上)**
|
|
155
166
|
|
|
156
|
-
const garment = await client.
|
|
157
|
-
const modelPhoto = await client.
|
|
167
|
+
const garment = await client.uploadToGCS(garmentFile);
|
|
168
|
+
const modelPhoto = await client.uploadToGCS(modelFile);
|
|
158
169
|
|
|
159
170
|
const result = await client.editImage({
|
|
160
171
|
prompt: '将服装自然替换到模特身上,保持姿态与光照;去除服装图背景;保持背景不变;不改变面部和发型。',
|
|
161
172
|
inputs: [
|
|
162
173
|
// 建议顺序:底图(模特)放前,叠加/融合图(服装)放后
|
|
163
|
-
{ type: 'uploaded_file', fileUri: modelPhoto.fileUri, mimeType:
|
|
164
|
-
{ type: 'uploaded_file', fileUri: garment.fileUri, mimeType:
|
|
174
|
+
{ type: 'uploaded_file', fileUri: modelPhoto.fileUri, mimeType: modelFile.type },
|
|
175
|
+
{ type: 'uploaded_file', fileUri: garment.fileUri, mimeType: garmentFile.type }
|
|
165
176
|
]
|
|
166
177
|
});
|
|
167
178
|
const base64 = result.data.uri || result.data.images?.[0]?.uri;
|
|
168
179
|
|
|
169
180
|
- **场景 C:掩膜修复/扩展(Inpainting / Outpainting)**
|
|
170
181
|
|
|
171
|
-
const base = await client.
|
|
172
|
-
const mask = await client.
|
|
182
|
+
const base = await client.uploadToGCS(baseFile);
|
|
183
|
+
const mask = await client.uploadToGCS(maskFile); // 掩膜语义按后端约定
|
|
173
184
|
|
|
174
185
|
const result = await client.editImage({
|
|
175
186
|
prompt: '在掩膜区域进行修复,移除水印并保持纹理一致;不要改变未掩膜区域。',
|
|
176
187
|
inputs: [
|
|
177
188
|
// 建议顺序:底图 -> 掩膜
|
|
178
|
-
{ type: 'uploaded_file', fileUri: base.fileUri, mimeType:
|
|
179
|
-
{ type: 'uploaded_file', fileUri: mask.fileUri, mimeType:
|
|
189
|
+
{ type: 'uploaded_file', fileUri: base.fileUri, mimeType: baseFile.type },
|
|
190
|
+
{ type: 'uploaded_file', fileUri: mask.fileUri, mimeType: maskFile.type }
|
|
180
191
|
]
|
|
181
192
|
});
|
|
182
193
|
const base64 = result.data.uri || result.data.images?.[0]?.uri;
|
|
@@ -184,7 +195,7 @@ const base64 = result.data.uri || result.data.images?.[0]?.uri;
|
|
|
184
195
|
### 3. 高级模式 (messages 透传)
|
|
185
196
|
用于完全控制请求结构,不推荐常规使用。
|
|
186
197
|
|
|
187
|
-
const base = await client.
|
|
198
|
+
const base = await client.uploadToGCS(file)
|
|
188
199
|
|
|
189
200
|
const result = await client.editImage({
|
|
190
201
|
model: 'gemini-2.5-flash-image-preview',
|
|
@@ -193,7 +204,7 @@ const result = await client.editImage({
|
|
|
193
204
|
role: 'user',
|
|
194
205
|
content: [
|
|
195
206
|
{ type: 'text', text: '移除图中叠加的文字...' },
|
|
196
|
-
{ type: 'uploaded_file', uploaded_file: { fileUri: base.fileUri, mimeType:
|
|
207
|
+
{ type: 'uploaded_file', uploaded_file: { fileUri: base.fileUri, mimeType: file.type } }
|
|
197
208
|
]
|
|
198
209
|
}
|
|
199
210
|
]
|
|
@@ -31,12 +31,25 @@ const getRequestSource = (isPlatformProxyRequest, isSchedulerRequest) => {
|
|
|
31
31
|
};
|
|
32
32
|
|
|
33
33
|
const createRequestScopedAppsClient = (env, options = {}) => {
|
|
34
|
-
const {
|
|
34
|
+
const {
|
|
35
|
+
accessToken = null,
|
|
36
|
+
previewAppServerApiKey = null,
|
|
37
|
+
enableAppServerServiceAuth = false,
|
|
38
|
+
} = options;
|
|
35
39
|
const config = {
|
|
36
40
|
...env,
|
|
37
41
|
accessToken: accessToken || null,
|
|
42
|
+
enableAppServerServiceAuth,
|
|
38
43
|
};
|
|
39
44
|
|
|
45
|
+
if (enableAppServerServiceAuth) {
|
|
46
|
+
config.appServerOwnerAppId = env.appId;
|
|
47
|
+
config.tacoreAppServerServiceToken =
|
|
48
|
+
env.tacoreAppServerServiceToken ||
|
|
49
|
+
process.env.TACORE_APP_SERVER_SERVICE_TOKEN ||
|
|
50
|
+
null;
|
|
51
|
+
}
|
|
52
|
+
|
|
40
53
|
if (previewAppServerApiKey) {
|
|
41
54
|
config.previewAppServerApiKey = previewAppServerApiKey;
|
|
42
55
|
}
|
|
@@ -274,6 +287,7 @@ export const createAppServerRuntime = async (options = {}) => {
|
|
|
274
287
|
);
|
|
275
288
|
const requestSource = getRequestSource(isPlatformProxyRequest, isSchedulerRequest);
|
|
276
289
|
let previewAppServerApiKey = null;
|
|
290
|
+
let isExternalApiKeyRequest = false;
|
|
277
291
|
|
|
278
292
|
console.log(`[AppServer] invoke api="${apiName}" source="${requestSource}"`);
|
|
279
293
|
|
|
@@ -318,6 +332,8 @@ export const createAppServerRuntime = async (options = {}) => {
|
|
|
318
332
|
});
|
|
319
333
|
}
|
|
320
334
|
|
|
335
|
+
isExternalApiKeyRequest = true;
|
|
336
|
+
|
|
321
337
|
if (isPreviewMode() && !requestAccessToken) {
|
|
322
338
|
previewAppServerApiKey = requestApiKey;
|
|
323
339
|
}
|
|
@@ -330,17 +346,21 @@ export const createAppServerRuntime = async (options = {}) => {
|
|
|
330
346
|
hasRequestApiKey: Boolean(requestApiKey),
|
|
331
347
|
hasPlatformInteropHeader: Boolean(platformInteropHeader),
|
|
332
348
|
hasSchedulerInteropHeader: Boolean(schedulerInteropHeader),
|
|
349
|
+
isExternalApiKeyRequest,
|
|
333
350
|
willForwardPreviewAppServerApiKey: Boolean(previewAppServerApiKey),
|
|
334
351
|
});
|
|
335
352
|
|
|
336
353
|
const appsClient = createRequestScopedAppsClient(env, {
|
|
337
354
|
accessToken: requestAccessToken || null,
|
|
338
355
|
previewAppServerApiKey,
|
|
356
|
+
enableAppServerServiceAuth: Boolean((isSchedulerRequest || isExternalApiKeyRequest) && !requestAccessToken),
|
|
339
357
|
});
|
|
340
358
|
console.log(`[AppServer Debug] scoped AppsClient api="${apiName}"`, {
|
|
341
359
|
hasAccessToken: Boolean(appsClient.getAccessToken()),
|
|
342
360
|
hasPreviewAppServerApiKey: Boolean(appsClient.config.previewAppServerApiKey),
|
|
343
361
|
hasInteropKey: Boolean(appsClient.config.tacoreServerInteropAppServerApiKey),
|
|
362
|
+
serviceAuthEnabled: appsClient.config.enableAppServerServiceAuth === true,
|
|
363
|
+
hasServiceToken: Boolean(appsClient.config.tacoreAppServerServiceToken),
|
|
344
364
|
});
|
|
345
365
|
const data = await appsClient.invokeAppServerAPI(apiName, payload, {
|
|
346
366
|
appsClient,
|
|
@@ -2,6 +2,8 @@ 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 APP_SERVER_SERVICE_TOKEN_HEADER = "x-tacore-app-server-service-token";
|
|
6
|
+
const APP_SERVER_OWNER_APP_ID_HEADER = "x-tacore-app-server-owner-app-id";
|
|
5
7
|
const INTEROP_APP_SERVER_API_KEY_REQUIRED_ERROR =
|
|
6
8
|
"tacoreServerInteropAppServerApiKey is required for backend environment. Provide it in config or via TACORE_SERVER_INTEROP_APP_SERVER_API_KEY env var.";
|
|
7
9
|
|
|
@@ -85,6 +87,14 @@ export class BaseAppsClient {
|
|
|
85
87
|
if (isBackend && !isSSR && !this.config.tacoreServerInteropAppServerApiKey) {
|
|
86
88
|
this.config.tacoreServerInteropAppServerApiKey = process.env.TACORE_SERVER_INTEROP_APP_SERVER_API_KEY;
|
|
87
89
|
}
|
|
90
|
+
if (
|
|
91
|
+
isBackend &&
|
|
92
|
+
!isSSR &&
|
|
93
|
+
this.config.enableAppServerServiceAuth === true &&
|
|
94
|
+
!this.config.tacoreAppServerServiceToken
|
|
95
|
+
) {
|
|
96
|
+
this.config.tacoreAppServerServiceToken = process.env.TACORE_APP_SERVER_SERVICE_TOKEN;
|
|
97
|
+
}
|
|
88
98
|
|
|
89
99
|
if (shouldRequireInteropAppServerApiKey() && !this.config.tacoreServerInteropAppServerApiKey) {
|
|
90
100
|
throw new Error(INTEROP_APP_SERVER_API_KEY_REQUIRED_ERROR);
|
|
@@ -186,8 +196,17 @@ export class BaseAppsClient {
|
|
|
186
196
|
headers["Authorization"] = `Bearer ${token}`;
|
|
187
197
|
}
|
|
188
198
|
|
|
189
|
-
if (
|
|
199
|
+
if (
|
|
200
|
+
isBackend &&
|
|
201
|
+
!isSSR &&
|
|
202
|
+
!token &&
|
|
203
|
+
this.config.enableAppServerServiceAuth === true &&
|
|
204
|
+
this.config.tacoreServerInteropAppServerApiKey &&
|
|
205
|
+
this.config.tacoreAppServerServiceToken
|
|
206
|
+
) {
|
|
190
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;
|
|
191
210
|
} else if (
|
|
192
211
|
isBackendPreviewMode() &&
|
|
193
212
|
!token &&
|
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.0",
|
|
4
|
+
"version": "1.22.1-beta.0",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"publishConfig": {
|
|
7
7
|
"access": "public",
|
|
@@ -13,6 +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\" && npm publish"
|
|
16
|
+
"release": "npm version minor && git commit -am \"web-sdk update version\" && git push && npm publish",
|
|
17
|
+
"release:beta": "npm version prerelease --preid beta --no-git-tag-version && git commit -am \"web-sdk update beta version\" && npm publish --tag beta"
|
|
17
18
|
}
|
|
18
19
|
}
|