agentlink-sdk 1.0.2 → 1.0.4
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/dist/index.d.mts +53 -19
- package/dist/index.d.ts +53 -19
- package/dist/index.js +108 -18
- package/dist/index.mjs +108 -18
- package/package.json +1 -1
package/dist/index.d.mts
CHANGED
|
@@ -1,16 +1,3 @@
|
|
|
1
|
-
interface URLData {
|
|
2
|
-
data: any;
|
|
3
|
-
type: string;
|
|
4
|
-
}
|
|
5
|
-
/**
|
|
6
|
-
* 将数据编码为 URL hash
|
|
7
|
-
*/
|
|
8
|
-
declare function encodeDataToUrl(data: any, type: string, baseUrl: string): Promise<string>;
|
|
9
|
-
/**
|
|
10
|
-
* 从 URL 中解码数据
|
|
11
|
-
*/
|
|
12
|
-
declare function decodeDataFromUrl(urlStr: string): Promise<URLData | null>;
|
|
13
|
-
|
|
14
1
|
interface AgentLinkClientOptions {
|
|
15
2
|
/**
|
|
16
3
|
* 服务端验证地址
|
|
@@ -26,13 +13,38 @@ interface WhitelistResponse {
|
|
|
26
13
|
whitelist: WhitelistInfo | WhitelistInfo[] | null;
|
|
27
14
|
origin: string | null;
|
|
28
15
|
}
|
|
16
|
+
interface SenderInfo {
|
|
17
|
+
domain: string;
|
|
18
|
+
description: string | null;
|
|
19
|
+
}
|
|
29
20
|
/**
|
|
30
|
-
* AgentLink 客户端 SDK (URL Hash 方案)
|
|
21
|
+
* AgentLink 客户端 SDK (URL Hash + Token 方案)
|
|
31
22
|
*/
|
|
32
23
|
declare class AgentLinkClient {
|
|
33
24
|
private serverUrl;
|
|
34
25
|
private static DEFAULT_WINDOW_NAME;
|
|
26
|
+
private token;
|
|
27
|
+
private tokenPromise;
|
|
28
|
+
private static tokenCache;
|
|
29
|
+
private static whitelistCache;
|
|
30
|
+
private static CACHE_TTL;
|
|
35
31
|
constructor(options: AgentLinkClientOptions);
|
|
32
|
+
/**
|
|
33
|
+
* 确保有 Token(自动获取)
|
|
34
|
+
*/
|
|
35
|
+
private ensureToken;
|
|
36
|
+
/**
|
|
37
|
+
* 从服务器获取 Token
|
|
38
|
+
*/
|
|
39
|
+
private fetchToken;
|
|
40
|
+
/**
|
|
41
|
+
* 检查域名是否在白名单中
|
|
42
|
+
*/
|
|
43
|
+
private checkWhitelist;
|
|
44
|
+
/**
|
|
45
|
+
* 验证发送端的 Token 和 Origin
|
|
46
|
+
*/
|
|
47
|
+
private verifySender;
|
|
36
48
|
/**
|
|
37
49
|
* 发送数据到目标应用
|
|
38
50
|
* @param targetUrl 目标应用的地址
|
|
@@ -43,13 +55,19 @@ declare class AgentLinkClient {
|
|
|
43
55
|
sendData(targetUrl: string, data: any, type: string, windowName?: string): Promise<void>;
|
|
44
56
|
/**
|
|
45
57
|
* 监听来自 URL 的数据
|
|
46
|
-
* @param callback
|
|
58
|
+
* @param callback 接收到数据时的回调函数,现在包含验证信息
|
|
47
59
|
*/
|
|
48
|
-
receiveData(callback: (data: any, type: string) => void): () => void;
|
|
60
|
+
receiveData(callback: (data: any, type: string, senderInfo?: SenderInfo, verification?: string) => void): () => void;
|
|
49
61
|
/**
|
|
50
|
-
* 从当前 URL
|
|
62
|
+
* 从当前 URL 获取数据并验证发送方
|
|
63
|
+
* 即使验证失败也返回数据,但会标记验证状态
|
|
51
64
|
*/
|
|
52
|
-
getDataFromUrl(url?: string): Promise<
|
|
65
|
+
getDataFromUrl(url?: string): Promise<{
|
|
66
|
+
data: any;
|
|
67
|
+
type: string;
|
|
68
|
+
senderInfo?: SenderInfo;
|
|
69
|
+
verification?: string;
|
|
70
|
+
} | null>;
|
|
53
71
|
/**
|
|
54
72
|
* 获取白名单信息
|
|
55
73
|
*/
|
|
@@ -73,6 +91,22 @@ declare function uint8ArrayToBase64(arr: Uint8Array): string;
|
|
|
73
91
|
*/
|
|
74
92
|
declare function base64ToUint8Array(base64: string): Uint8Array;
|
|
75
93
|
|
|
94
|
+
interface URLData {
|
|
95
|
+
data: any;
|
|
96
|
+
type: string;
|
|
97
|
+
token: string;
|
|
98
|
+
origin: string;
|
|
99
|
+
verification?: string;
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* 将数据编码为 URL hash
|
|
103
|
+
*/
|
|
104
|
+
declare function encodeDataToUrl(data: any, type: string, token: string, origin: string, baseUrl: string, verification?: string): Promise<string>;
|
|
105
|
+
/**
|
|
106
|
+
* 从 URL 中解码数据
|
|
107
|
+
*/
|
|
108
|
+
declare function decodeDataFromUrl(urlStr: string): Promise<URLData | null>;
|
|
109
|
+
|
|
76
110
|
/**
|
|
77
111
|
* 验证域名是否在白名单中
|
|
78
112
|
*/
|
|
@@ -82,4 +116,4 @@ declare function verifyWhitelist(serverUrl: string, origin?: string): Promise<bo
|
|
|
82
116
|
*/
|
|
83
117
|
declare function fetchWhitelistInfo(serverUrl: string, includeAll?: boolean): Promise<any>;
|
|
84
118
|
|
|
85
|
-
export { AgentLinkClient, type AgentLinkClientOptions, type URLData, type WhitelistInfo, type WhitelistResponse, base64ToUint8Array, compress, decodeDataFromUrl, decompress, encodeDataToUrl, fetchWhitelistInfo, uint8ArrayToBase64, verifyWhitelist };
|
|
119
|
+
export { AgentLinkClient, type AgentLinkClientOptions, type SenderInfo, type URLData, type WhitelistInfo, type WhitelistResponse, base64ToUint8Array, compress, decodeDataFromUrl, decompress, encodeDataToUrl, fetchWhitelistInfo, uint8ArrayToBase64, verifyWhitelist };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,16 +1,3 @@
|
|
|
1
|
-
interface URLData {
|
|
2
|
-
data: any;
|
|
3
|
-
type: string;
|
|
4
|
-
}
|
|
5
|
-
/**
|
|
6
|
-
* 将数据编码为 URL hash
|
|
7
|
-
*/
|
|
8
|
-
declare function encodeDataToUrl(data: any, type: string, baseUrl: string): Promise<string>;
|
|
9
|
-
/**
|
|
10
|
-
* 从 URL 中解码数据
|
|
11
|
-
*/
|
|
12
|
-
declare function decodeDataFromUrl(urlStr: string): Promise<URLData | null>;
|
|
13
|
-
|
|
14
1
|
interface AgentLinkClientOptions {
|
|
15
2
|
/**
|
|
16
3
|
* 服务端验证地址
|
|
@@ -26,13 +13,38 @@ interface WhitelistResponse {
|
|
|
26
13
|
whitelist: WhitelistInfo | WhitelistInfo[] | null;
|
|
27
14
|
origin: string | null;
|
|
28
15
|
}
|
|
16
|
+
interface SenderInfo {
|
|
17
|
+
domain: string;
|
|
18
|
+
description: string | null;
|
|
19
|
+
}
|
|
29
20
|
/**
|
|
30
|
-
* AgentLink 客户端 SDK (URL Hash 方案)
|
|
21
|
+
* AgentLink 客户端 SDK (URL Hash + Token 方案)
|
|
31
22
|
*/
|
|
32
23
|
declare class AgentLinkClient {
|
|
33
24
|
private serverUrl;
|
|
34
25
|
private static DEFAULT_WINDOW_NAME;
|
|
26
|
+
private token;
|
|
27
|
+
private tokenPromise;
|
|
28
|
+
private static tokenCache;
|
|
29
|
+
private static whitelistCache;
|
|
30
|
+
private static CACHE_TTL;
|
|
35
31
|
constructor(options: AgentLinkClientOptions);
|
|
32
|
+
/**
|
|
33
|
+
* 确保有 Token(自动获取)
|
|
34
|
+
*/
|
|
35
|
+
private ensureToken;
|
|
36
|
+
/**
|
|
37
|
+
* 从服务器获取 Token
|
|
38
|
+
*/
|
|
39
|
+
private fetchToken;
|
|
40
|
+
/**
|
|
41
|
+
* 检查域名是否在白名单中
|
|
42
|
+
*/
|
|
43
|
+
private checkWhitelist;
|
|
44
|
+
/**
|
|
45
|
+
* 验证发送端的 Token 和 Origin
|
|
46
|
+
*/
|
|
47
|
+
private verifySender;
|
|
36
48
|
/**
|
|
37
49
|
* 发送数据到目标应用
|
|
38
50
|
* @param targetUrl 目标应用的地址
|
|
@@ -43,13 +55,19 @@ declare class AgentLinkClient {
|
|
|
43
55
|
sendData(targetUrl: string, data: any, type: string, windowName?: string): Promise<void>;
|
|
44
56
|
/**
|
|
45
57
|
* 监听来自 URL 的数据
|
|
46
|
-
* @param callback
|
|
58
|
+
* @param callback 接收到数据时的回调函数,现在包含验证信息
|
|
47
59
|
*/
|
|
48
|
-
receiveData(callback: (data: any, type: string) => void): () => void;
|
|
60
|
+
receiveData(callback: (data: any, type: string, senderInfo?: SenderInfo, verification?: string) => void): () => void;
|
|
49
61
|
/**
|
|
50
|
-
* 从当前 URL
|
|
62
|
+
* 从当前 URL 获取数据并验证发送方
|
|
63
|
+
* 即使验证失败也返回数据,但会标记验证状态
|
|
51
64
|
*/
|
|
52
|
-
getDataFromUrl(url?: string): Promise<
|
|
65
|
+
getDataFromUrl(url?: string): Promise<{
|
|
66
|
+
data: any;
|
|
67
|
+
type: string;
|
|
68
|
+
senderInfo?: SenderInfo;
|
|
69
|
+
verification?: string;
|
|
70
|
+
} | null>;
|
|
53
71
|
/**
|
|
54
72
|
* 获取白名单信息
|
|
55
73
|
*/
|
|
@@ -73,6 +91,22 @@ declare function uint8ArrayToBase64(arr: Uint8Array): string;
|
|
|
73
91
|
*/
|
|
74
92
|
declare function base64ToUint8Array(base64: string): Uint8Array;
|
|
75
93
|
|
|
94
|
+
interface URLData {
|
|
95
|
+
data: any;
|
|
96
|
+
type: string;
|
|
97
|
+
token: string;
|
|
98
|
+
origin: string;
|
|
99
|
+
verification?: string;
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* 将数据编码为 URL hash
|
|
103
|
+
*/
|
|
104
|
+
declare function encodeDataToUrl(data: any, type: string, token: string, origin: string, baseUrl: string, verification?: string): Promise<string>;
|
|
105
|
+
/**
|
|
106
|
+
* 从 URL 中解码数据
|
|
107
|
+
*/
|
|
108
|
+
declare function decodeDataFromUrl(urlStr: string): Promise<URLData | null>;
|
|
109
|
+
|
|
76
110
|
/**
|
|
77
111
|
* 验证域名是否在白名单中
|
|
78
112
|
*/
|
|
@@ -82,4 +116,4 @@ declare function verifyWhitelist(serverUrl: string, origin?: string): Promise<bo
|
|
|
82
116
|
*/
|
|
83
117
|
declare function fetchWhitelistInfo(serverUrl: string, includeAll?: boolean): Promise<any>;
|
|
84
118
|
|
|
85
|
-
export { AgentLinkClient, type AgentLinkClientOptions, type URLData, type WhitelistInfo, type WhitelistResponse, base64ToUint8Array, compress, decodeDataFromUrl, decompress, encodeDataToUrl, fetchWhitelistInfo, uint8ArrayToBase64, verifyWhitelist };
|
|
119
|
+
export { AgentLinkClient, type AgentLinkClientOptions, type SenderInfo, type URLData, type WhitelistInfo, type WhitelistResponse, base64ToUint8Array, compress, decodeDataFromUrl, decompress, encodeDataToUrl, fetchWhitelistInfo, uint8ArrayToBase64, verifyWhitelist };
|
package/dist/index.js
CHANGED
|
@@ -66,8 +66,11 @@ function base64ToUint8Array(base64) {
|
|
|
66
66
|
}
|
|
67
67
|
|
|
68
68
|
// src/utils/url.ts
|
|
69
|
-
async function encodeDataToUrl(data, type, baseUrl) {
|
|
70
|
-
const payload = { data, type };
|
|
69
|
+
async function encodeDataToUrl(data, type, token, origin, baseUrl, verification) {
|
|
70
|
+
const payload = { data, type, token, origin };
|
|
71
|
+
if (verification !== void 0) {
|
|
72
|
+
payload.verification = verification;
|
|
73
|
+
}
|
|
71
74
|
const jsonString = JSON.stringify(payload);
|
|
72
75
|
const compressed = await compress(jsonString);
|
|
73
76
|
const base64 = uint8ArrayToBase64(compressed);
|
|
@@ -137,8 +140,79 @@ async function fetchWhitelistInfo(serverUrl, includeAll = false) {
|
|
|
137
140
|
// src/client.ts
|
|
138
141
|
var _AgentLinkClient = class _AgentLinkClient {
|
|
139
142
|
constructor(options) {
|
|
143
|
+
// 发送端 Token 缓存(实例级别)
|
|
144
|
+
this.token = null;
|
|
145
|
+
this.tokenPromise = null;
|
|
140
146
|
this.serverUrl = options.serverUrl.replace(/\/$/, "");
|
|
141
147
|
}
|
|
148
|
+
/**
|
|
149
|
+
* 确保有 Token(自动获取)
|
|
150
|
+
*/
|
|
151
|
+
async ensureToken() {
|
|
152
|
+
if (this.token) return this.token;
|
|
153
|
+
if (!this.tokenPromise) {
|
|
154
|
+
this.tokenPromise = this.fetchToken();
|
|
155
|
+
}
|
|
156
|
+
return this.tokenPromise;
|
|
157
|
+
}
|
|
158
|
+
/**
|
|
159
|
+
* 从服务器获取 Token
|
|
160
|
+
*/
|
|
161
|
+
async fetchToken() {
|
|
162
|
+
const origin = window.location.origin;
|
|
163
|
+
const response = await fetch(`${this.serverUrl}/api/tokens/get?origin=${encodeURIComponent(origin)}`);
|
|
164
|
+
if (!response.ok) {
|
|
165
|
+
const data2 = await response.json();
|
|
166
|
+
throw new Error(`[AgentLink] Failed to get token: ${data2.error || response.statusText}`);
|
|
167
|
+
}
|
|
168
|
+
const data = await response.json();
|
|
169
|
+
if (!data.token) {
|
|
170
|
+
throw new Error("[AgentLink] No token returned from server");
|
|
171
|
+
}
|
|
172
|
+
const token = data.token;
|
|
173
|
+
this.token = token;
|
|
174
|
+
return token;
|
|
175
|
+
}
|
|
176
|
+
/**
|
|
177
|
+
* 检查域名是否在白名单中
|
|
178
|
+
*/
|
|
179
|
+
async checkWhitelist(origin) {
|
|
180
|
+
const cached = _AgentLinkClient.whitelistCache.get(origin);
|
|
181
|
+
if (cached && Date.now() - cached.timestamp < _AgentLinkClient.CACHE_TTL) {
|
|
182
|
+
return cached.verified;
|
|
183
|
+
}
|
|
184
|
+
const verified = await verifyWhitelist(this.serverUrl, origin);
|
|
185
|
+
_AgentLinkClient.whitelistCache.set(origin, {
|
|
186
|
+
verified,
|
|
187
|
+
timestamp: Date.now()
|
|
188
|
+
});
|
|
189
|
+
return verified;
|
|
190
|
+
}
|
|
191
|
+
/**
|
|
192
|
+
* 验证发送端的 Token 和 Origin
|
|
193
|
+
*/
|
|
194
|
+
async verifySender(token, origin) {
|
|
195
|
+
const cached = _AgentLinkClient.tokenCache.get(token);
|
|
196
|
+
if (cached && Date.now() - cached.timestamp < _AgentLinkClient.CACHE_TTL) {
|
|
197
|
+
return { domain: cached.domain, description: cached.description };
|
|
198
|
+
}
|
|
199
|
+
const response = await fetch(
|
|
200
|
+
`${this.serverUrl}/api/tokens/verify?token=${encodeURIComponent(token)}&origin=${encodeURIComponent(origin)}`
|
|
201
|
+
);
|
|
202
|
+
if (!response.ok) {
|
|
203
|
+
return null;
|
|
204
|
+
}
|
|
205
|
+
const data = await response.json();
|
|
206
|
+
if (!data.valid) {
|
|
207
|
+
return null;
|
|
208
|
+
}
|
|
209
|
+
_AgentLinkClient.tokenCache.set(token, {
|
|
210
|
+
domain: data.domain,
|
|
211
|
+
description: data.description,
|
|
212
|
+
timestamp: Date.now()
|
|
213
|
+
});
|
|
214
|
+
return { domain: data.domain, description: data.description };
|
|
215
|
+
}
|
|
142
216
|
/**
|
|
143
217
|
* 发送数据到目标应用
|
|
144
218
|
* @param targetUrl 目标应用的地址
|
|
@@ -147,11 +221,11 @@ var _AgentLinkClient = class _AgentLinkClient {
|
|
|
147
221
|
* @param windowName 窗口名称,用于复用(默认: 'agentlink-window')
|
|
148
222
|
*/
|
|
149
223
|
async sendData(targetUrl, data, type, windowName = _AgentLinkClient.DEFAULT_WINDOW_NAME) {
|
|
150
|
-
const
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
const encodedUrl = await encodeDataToUrl(data, type, targetUrl);
|
|
224
|
+
const token = await this.ensureToken();
|
|
225
|
+
const origin = window.location.origin;
|
|
226
|
+
const isWhitelisted = await this.checkWhitelist(origin);
|
|
227
|
+
const verification = isWhitelisted ? "mixlab launchpad\u52A0\u901F\u8BA1\u5212" : "\u672A\u9A8C\u8BC1";
|
|
228
|
+
const encodedUrl = await encodeDataToUrl(data, type, token, origin, targetUrl, verification);
|
|
155
229
|
const targetWindow = window.open(encodedUrl, windowName);
|
|
156
230
|
if (!targetWindow) {
|
|
157
231
|
throw new Error("[AgentLink] Failed to open target window. It might be blocked by a popup blocker.");
|
|
@@ -160,13 +234,13 @@ var _AgentLinkClient = class _AgentLinkClient {
|
|
|
160
234
|
}
|
|
161
235
|
/**
|
|
162
236
|
* 监听来自 URL 的数据
|
|
163
|
-
* @param callback
|
|
237
|
+
* @param callback 接收到数据时的回调函数,现在包含验证信息
|
|
164
238
|
*/
|
|
165
239
|
receiveData(callback) {
|
|
166
240
|
const handleHashChange = async () => {
|
|
167
241
|
const result = await this.getDataFromUrl();
|
|
168
242
|
if (result) {
|
|
169
|
-
callback(result.data, result.type);
|
|
243
|
+
callback(result.data, result.type, result.senderInfo, result.verification);
|
|
170
244
|
}
|
|
171
245
|
};
|
|
172
246
|
window.addEventListener("hashchange", handleHashChange);
|
|
@@ -176,19 +250,29 @@ var _AgentLinkClient = class _AgentLinkClient {
|
|
|
176
250
|
};
|
|
177
251
|
}
|
|
178
252
|
/**
|
|
179
|
-
* 从当前 URL
|
|
253
|
+
* 从当前 URL 获取数据并验证发送方
|
|
254
|
+
* 即使验证失败也返回数据,但会标记验证状态
|
|
180
255
|
*/
|
|
181
256
|
async getDataFromUrl(url) {
|
|
182
257
|
const targetUrl = url || window.location.href;
|
|
183
|
-
const
|
|
184
|
-
if (
|
|
185
|
-
|
|
186
|
-
if (!isAllowed) {
|
|
187
|
-
console.warn(`[AgentLink] Data received but current origin is not whitelisted: ${window.location.origin}`);
|
|
188
|
-
return null;
|
|
189
|
-
}
|
|
258
|
+
const urlData = await decodeDataFromUrl(targetUrl);
|
|
259
|
+
if (!urlData) {
|
|
260
|
+
return null;
|
|
190
261
|
}
|
|
191
|
-
|
|
262
|
+
const senderInfo = await this.verifySender(urlData.token, urlData.origin);
|
|
263
|
+
let verification = urlData.verification;
|
|
264
|
+
if (!verification) {
|
|
265
|
+
verification = senderInfo ? "mixlab launchpad\u52A0\u901F\u8BA1\u5212" : "\u672A\u9A8C\u8BC1";
|
|
266
|
+
}
|
|
267
|
+
if (!senderInfo) {
|
|
268
|
+
console.warn(`[AgentLink] Data received but sender verification failed. Verification: ${verification}`);
|
|
269
|
+
}
|
|
270
|
+
return {
|
|
271
|
+
data: urlData.data,
|
|
272
|
+
type: urlData.type,
|
|
273
|
+
senderInfo: senderInfo || void 0,
|
|
274
|
+
verification
|
|
275
|
+
};
|
|
192
276
|
}
|
|
193
277
|
/**
|
|
194
278
|
* 获取白名单信息
|
|
@@ -198,5 +282,11 @@ var _AgentLinkClient = class _AgentLinkClient {
|
|
|
198
282
|
}
|
|
199
283
|
};
|
|
200
284
|
_AgentLinkClient.DEFAULT_WINDOW_NAME = "agentlink-window";
|
|
285
|
+
// 接收端验证结果缓存(静态,跨实例共享)
|
|
286
|
+
_AgentLinkClient.tokenCache = /* @__PURE__ */ new Map();
|
|
287
|
+
// 白名单验证缓存(避免重复请求)
|
|
288
|
+
_AgentLinkClient.whitelistCache = /* @__PURE__ */ new Map();
|
|
289
|
+
// 缓存过期时间:1 小时
|
|
290
|
+
_AgentLinkClient.CACHE_TTL = 60 * 60 * 1e3;
|
|
201
291
|
var AgentLinkClient = _AgentLinkClient;
|
|
202
292
|
//# sourceMappingURL=index.js.map
|
package/dist/index.mjs
CHANGED
|
@@ -32,8 +32,11 @@ function base64ToUint8Array(base64) {
|
|
|
32
32
|
}
|
|
33
33
|
|
|
34
34
|
// src/utils/url.ts
|
|
35
|
-
async function encodeDataToUrl(data, type, baseUrl) {
|
|
36
|
-
const payload = { data, type };
|
|
35
|
+
async function encodeDataToUrl(data, type, token, origin, baseUrl, verification) {
|
|
36
|
+
const payload = { data, type, token, origin };
|
|
37
|
+
if (verification !== void 0) {
|
|
38
|
+
payload.verification = verification;
|
|
39
|
+
}
|
|
37
40
|
const jsonString = JSON.stringify(payload);
|
|
38
41
|
const compressed = await compress(jsonString);
|
|
39
42
|
const base64 = uint8ArrayToBase64(compressed);
|
|
@@ -103,8 +106,79 @@ async function fetchWhitelistInfo(serverUrl, includeAll = false) {
|
|
|
103
106
|
// src/client.ts
|
|
104
107
|
var _AgentLinkClient = class _AgentLinkClient {
|
|
105
108
|
constructor(options) {
|
|
109
|
+
// 发送端 Token 缓存(实例级别)
|
|
110
|
+
this.token = null;
|
|
111
|
+
this.tokenPromise = null;
|
|
106
112
|
this.serverUrl = options.serverUrl.replace(/\/$/, "");
|
|
107
113
|
}
|
|
114
|
+
/**
|
|
115
|
+
* 确保有 Token(自动获取)
|
|
116
|
+
*/
|
|
117
|
+
async ensureToken() {
|
|
118
|
+
if (this.token) return this.token;
|
|
119
|
+
if (!this.tokenPromise) {
|
|
120
|
+
this.tokenPromise = this.fetchToken();
|
|
121
|
+
}
|
|
122
|
+
return this.tokenPromise;
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* 从服务器获取 Token
|
|
126
|
+
*/
|
|
127
|
+
async fetchToken() {
|
|
128
|
+
const origin = window.location.origin;
|
|
129
|
+
const response = await fetch(`${this.serverUrl}/api/tokens/get?origin=${encodeURIComponent(origin)}`);
|
|
130
|
+
if (!response.ok) {
|
|
131
|
+
const data2 = await response.json();
|
|
132
|
+
throw new Error(`[AgentLink] Failed to get token: ${data2.error || response.statusText}`);
|
|
133
|
+
}
|
|
134
|
+
const data = await response.json();
|
|
135
|
+
if (!data.token) {
|
|
136
|
+
throw new Error("[AgentLink] No token returned from server");
|
|
137
|
+
}
|
|
138
|
+
const token = data.token;
|
|
139
|
+
this.token = token;
|
|
140
|
+
return token;
|
|
141
|
+
}
|
|
142
|
+
/**
|
|
143
|
+
* 检查域名是否在白名单中
|
|
144
|
+
*/
|
|
145
|
+
async checkWhitelist(origin) {
|
|
146
|
+
const cached = _AgentLinkClient.whitelistCache.get(origin);
|
|
147
|
+
if (cached && Date.now() - cached.timestamp < _AgentLinkClient.CACHE_TTL) {
|
|
148
|
+
return cached.verified;
|
|
149
|
+
}
|
|
150
|
+
const verified = await verifyWhitelist(this.serverUrl, origin);
|
|
151
|
+
_AgentLinkClient.whitelistCache.set(origin, {
|
|
152
|
+
verified,
|
|
153
|
+
timestamp: Date.now()
|
|
154
|
+
});
|
|
155
|
+
return verified;
|
|
156
|
+
}
|
|
157
|
+
/**
|
|
158
|
+
* 验证发送端的 Token 和 Origin
|
|
159
|
+
*/
|
|
160
|
+
async verifySender(token, origin) {
|
|
161
|
+
const cached = _AgentLinkClient.tokenCache.get(token);
|
|
162
|
+
if (cached && Date.now() - cached.timestamp < _AgentLinkClient.CACHE_TTL) {
|
|
163
|
+
return { domain: cached.domain, description: cached.description };
|
|
164
|
+
}
|
|
165
|
+
const response = await fetch(
|
|
166
|
+
`${this.serverUrl}/api/tokens/verify?token=${encodeURIComponent(token)}&origin=${encodeURIComponent(origin)}`
|
|
167
|
+
);
|
|
168
|
+
if (!response.ok) {
|
|
169
|
+
return null;
|
|
170
|
+
}
|
|
171
|
+
const data = await response.json();
|
|
172
|
+
if (!data.valid) {
|
|
173
|
+
return null;
|
|
174
|
+
}
|
|
175
|
+
_AgentLinkClient.tokenCache.set(token, {
|
|
176
|
+
domain: data.domain,
|
|
177
|
+
description: data.description,
|
|
178
|
+
timestamp: Date.now()
|
|
179
|
+
});
|
|
180
|
+
return { domain: data.domain, description: data.description };
|
|
181
|
+
}
|
|
108
182
|
/**
|
|
109
183
|
* 发送数据到目标应用
|
|
110
184
|
* @param targetUrl 目标应用的地址
|
|
@@ -113,11 +187,11 @@ var _AgentLinkClient = class _AgentLinkClient {
|
|
|
113
187
|
* @param windowName 窗口名称,用于复用(默认: 'agentlink-window')
|
|
114
188
|
*/
|
|
115
189
|
async sendData(targetUrl, data, type, windowName = _AgentLinkClient.DEFAULT_WINDOW_NAME) {
|
|
116
|
-
const
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
const encodedUrl = await encodeDataToUrl(data, type, targetUrl);
|
|
190
|
+
const token = await this.ensureToken();
|
|
191
|
+
const origin = window.location.origin;
|
|
192
|
+
const isWhitelisted = await this.checkWhitelist(origin);
|
|
193
|
+
const verification = isWhitelisted ? "mixlab launchpad\u52A0\u901F\u8BA1\u5212" : "\u672A\u9A8C\u8BC1";
|
|
194
|
+
const encodedUrl = await encodeDataToUrl(data, type, token, origin, targetUrl, verification);
|
|
121
195
|
const targetWindow = window.open(encodedUrl, windowName);
|
|
122
196
|
if (!targetWindow) {
|
|
123
197
|
throw new Error("[AgentLink] Failed to open target window. It might be blocked by a popup blocker.");
|
|
@@ -126,13 +200,13 @@ var _AgentLinkClient = class _AgentLinkClient {
|
|
|
126
200
|
}
|
|
127
201
|
/**
|
|
128
202
|
* 监听来自 URL 的数据
|
|
129
|
-
* @param callback
|
|
203
|
+
* @param callback 接收到数据时的回调函数,现在包含验证信息
|
|
130
204
|
*/
|
|
131
205
|
receiveData(callback) {
|
|
132
206
|
const handleHashChange = async () => {
|
|
133
207
|
const result = await this.getDataFromUrl();
|
|
134
208
|
if (result) {
|
|
135
|
-
callback(result.data, result.type);
|
|
209
|
+
callback(result.data, result.type, result.senderInfo, result.verification);
|
|
136
210
|
}
|
|
137
211
|
};
|
|
138
212
|
window.addEventListener("hashchange", handleHashChange);
|
|
@@ -142,19 +216,29 @@ var _AgentLinkClient = class _AgentLinkClient {
|
|
|
142
216
|
};
|
|
143
217
|
}
|
|
144
218
|
/**
|
|
145
|
-
* 从当前 URL
|
|
219
|
+
* 从当前 URL 获取数据并验证发送方
|
|
220
|
+
* 即使验证失败也返回数据,但会标记验证状态
|
|
146
221
|
*/
|
|
147
222
|
async getDataFromUrl(url) {
|
|
148
223
|
const targetUrl = url || window.location.href;
|
|
149
|
-
const
|
|
150
|
-
if (
|
|
151
|
-
|
|
152
|
-
if (!isAllowed) {
|
|
153
|
-
console.warn(`[AgentLink] Data received but current origin is not whitelisted: ${window.location.origin}`);
|
|
154
|
-
return null;
|
|
155
|
-
}
|
|
224
|
+
const urlData = await decodeDataFromUrl(targetUrl);
|
|
225
|
+
if (!urlData) {
|
|
226
|
+
return null;
|
|
156
227
|
}
|
|
157
|
-
|
|
228
|
+
const senderInfo = await this.verifySender(urlData.token, urlData.origin);
|
|
229
|
+
let verification = urlData.verification;
|
|
230
|
+
if (!verification) {
|
|
231
|
+
verification = senderInfo ? "mixlab launchpad\u52A0\u901F\u8BA1\u5212" : "\u672A\u9A8C\u8BC1";
|
|
232
|
+
}
|
|
233
|
+
if (!senderInfo) {
|
|
234
|
+
console.warn(`[AgentLink] Data received but sender verification failed. Verification: ${verification}`);
|
|
235
|
+
}
|
|
236
|
+
return {
|
|
237
|
+
data: urlData.data,
|
|
238
|
+
type: urlData.type,
|
|
239
|
+
senderInfo: senderInfo || void 0,
|
|
240
|
+
verification
|
|
241
|
+
};
|
|
158
242
|
}
|
|
159
243
|
/**
|
|
160
244
|
* 获取白名单信息
|
|
@@ -164,6 +248,12 @@ var _AgentLinkClient = class _AgentLinkClient {
|
|
|
164
248
|
}
|
|
165
249
|
};
|
|
166
250
|
_AgentLinkClient.DEFAULT_WINDOW_NAME = "agentlink-window";
|
|
251
|
+
// 接收端验证结果缓存(静态,跨实例共享)
|
|
252
|
+
_AgentLinkClient.tokenCache = /* @__PURE__ */ new Map();
|
|
253
|
+
// 白名单验证缓存(避免重复请求)
|
|
254
|
+
_AgentLinkClient.whitelistCache = /* @__PURE__ */ new Map();
|
|
255
|
+
// 缓存过期时间:1 小时
|
|
256
|
+
_AgentLinkClient.CACHE_TTL = 60 * 60 * 1e3;
|
|
167
257
|
var AgentLinkClient = _AgentLinkClient;
|
|
168
258
|
export {
|
|
169
259
|
AgentLinkClient,
|