@agentunion/fastaun-browser 0.4.0 → 0.4.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/CHANGELOG.md +24 -0
- package/_packed_docs/AUN_SDK_/351/207/215/346/236/204/350/256/276/350/256/241/346/226/271/346/241/210_v3.md +158 -94
- package/_packed_docs/CHANGELOG.md +24 -0
- package/_packed_docs/sdk/06-API/346/211/213/345/206/214.md +5 -0
- package/dist/aid-store.d.ts +77 -16
- package/dist/aid-store.d.ts.map +1 -1
- package/dist/aid-store.js +18 -32
- package/dist/aid-store.js.map +1 -1
- package/dist/aid.d.ts +6 -0
- package/dist/aid.d.ts.map +1 -1
- package/dist/aid.js +6 -0
- package/dist/aid.js.map +1 -1
- package/dist/bundle.js +68 -80
- package/dist/client.d.ts +11 -11
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +42 -38
- package/dist/client.js.map +1 -1
- package/dist/config.d.ts +0 -2
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +0 -2
- package/dist/config.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +2 -1
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# AUN SDK 重构设计方案 v4.
|
|
1
|
+
# AUN SDK 重构设计方案 v4.2
|
|
2
2
|
|
|
3
3
|
## 一、设计原则
|
|
4
4
|
|
|
@@ -40,6 +40,9 @@ class AIDStore {
|
|
|
40
40
|
encryptionSeed: string; // 必传:加密种子(可为空字符串 '')
|
|
41
41
|
deviceId?: string; // 默认 getDeviceId(),同一 AID 最多 10 个设备在线
|
|
42
42
|
slotId?: string; // 默认 'default',同设备最多 10 个 slot 在线
|
|
43
|
+
verifySsl?: boolean; // 默认 true,测试环境可设为 false
|
|
44
|
+
rootCaPath?: string; // 自定义根证书路径,私有部署使用
|
|
45
|
+
debug?: boolean; // 开启调试日志,默认 false
|
|
43
46
|
});
|
|
44
47
|
}
|
|
45
48
|
```
|
|
@@ -49,6 +52,9 @@ class AIDStore {
|
|
|
49
52
|
- `encryptionSeed` 可以是空字符串 `''`,表示不加密
|
|
50
53
|
- 应用层统一管理加密种子,SDK 不持久化
|
|
51
54
|
- `deviceId` + `slotId` 构成消费通道,影响 V2 session 密钥存储和消息序号命名空间
|
|
55
|
+
- `verifySsl: false` 仅用于测试环境,生产环境不应关闭
|
|
56
|
+
- `rootCaPath` 用于私有部署时指定自定义根证书
|
|
57
|
+
- `debug: true` 开启详细调试日志输出
|
|
52
58
|
- 同一进程内可创建多个 AIDStore 实例(指向不同 keystore 或不同 slot)
|
|
53
59
|
|
|
54
60
|
**示例**:
|
|
@@ -72,10 +78,12 @@ const store = new AIDStore({
|
|
|
72
78
|
|
|
73
79
|
### 2.2 AIDStore 加载与注册
|
|
74
80
|
|
|
75
|
-
#### `load(aid: string):
|
|
81
|
+
#### `load(aid: string): Result<{ aid: AID }>`
|
|
76
82
|
|
|
77
83
|
从本地 keystore 加载 AID(证书 + 私钥若有)。
|
|
78
84
|
|
|
85
|
+
> **注意**:JS SDK 因底层使用 IndexedDB(浏览器异步 API),保持 `Promise<Result<{ aid: AID }>>` 为平台例外。
|
|
86
|
+
|
|
79
87
|
**流程**:
|
|
80
88
|
1. 从 `{aunPath}/AIDs/{aid}/public/certs/` 读证书
|
|
81
89
|
2. 链验证 + 有效期检查
|
|
@@ -92,7 +100,7 @@ const store = new AIDStore({
|
|
|
92
100
|
**示例**:
|
|
93
101
|
```typescript
|
|
94
102
|
const store = new AIDStore({ aunPath: '...', encryptionSeed: '...' });
|
|
95
|
-
const result =
|
|
103
|
+
const result = store.load('alice.aid.pub');
|
|
96
104
|
|
|
97
105
|
if (result.ok) {
|
|
98
106
|
const me = result.data.aid;
|
|
@@ -131,7 +139,7 @@ const result = await store.register('alice.aid.pub');
|
|
|
131
139
|
|
|
132
140
|
if (result.ok) {
|
|
133
141
|
// 注册成功,加载身份
|
|
134
|
-
const loadResult =
|
|
142
|
+
const loadResult = store.load('alice.aid.pub');
|
|
135
143
|
const me = loadResult.data!.aid;
|
|
136
144
|
} else {
|
|
137
145
|
console.log('注册失败:', result.error.code);
|
|
@@ -140,7 +148,7 @@ if (result.ok) {
|
|
|
140
148
|
|
|
141
149
|
---
|
|
142
150
|
|
|
143
|
-
#### `list():
|
|
151
|
+
#### `list(): Result<{ identities: AIDInfo[] }>`
|
|
144
152
|
|
|
145
153
|
列出本地所有有私钥的 AID 元信息。
|
|
146
154
|
|
|
@@ -155,6 +163,7 @@ type AIDInfo = {
|
|
|
155
163
|
aid: string;
|
|
156
164
|
certNotAfter: Date;
|
|
157
165
|
certIssuer: string;
|
|
166
|
+
certFingerprint: string;
|
|
158
167
|
};
|
|
159
168
|
|
|
160
169
|
// 成功
|
|
@@ -164,7 +173,7 @@ type AIDInfo = {
|
|
|
164
173
|
**示例**:
|
|
165
174
|
```typescript
|
|
166
175
|
const store = new AIDStore({ aunPath: '...', encryptionSeed: '...' });
|
|
167
|
-
const result =
|
|
176
|
+
const result = store.list();
|
|
168
177
|
|
|
169
178
|
if (result.ok) {
|
|
170
179
|
console.log('本地身份:', result.data.identities.map(i => i.aid));
|
|
@@ -214,7 +223,7 @@ if (result.ok) {
|
|
|
214
223
|
|
|
215
224
|
### 2.3 AIDStore 解析对端
|
|
216
225
|
|
|
217
|
-
#### `resolve(aid: string, opts?: ResolveOpts): Promise<Result<
|
|
226
|
+
#### `resolve(aid: string, opts?: ResolveOpts): Promise<Result<ResolveResult>>`
|
|
218
227
|
|
|
219
228
|
**一站式解析对端 AID**:下载证书 → 验签证书 → 缓存到本地 → 下载 agent.md → 验签 agent.md。
|
|
220
229
|
|
|
@@ -236,23 +245,24 @@ type ResolveOpts = {
|
|
|
236
245
|
|
|
237
246
|
**返回**:
|
|
238
247
|
```typescript
|
|
239
|
-
type
|
|
248
|
+
type ResolveResult = {
|
|
240
249
|
aid: AID; // PeerOnly AID 对象
|
|
241
|
-
|
|
250
|
+
agent_md?: {
|
|
242
251
|
content: string;
|
|
243
252
|
verification: {
|
|
244
|
-
status:
|
|
253
|
+
status: string;
|
|
245
254
|
reason?: string;
|
|
246
255
|
};
|
|
256
|
+
cert_pem: string;
|
|
247
257
|
};
|
|
248
258
|
source: {
|
|
249
|
-
|
|
250
|
-
|
|
259
|
+
cert_from_cache: boolean; // 证书来自本地缓存
|
|
260
|
+
agent_md_fetched: boolean; // agent.md 已下载
|
|
251
261
|
};
|
|
252
262
|
};
|
|
253
263
|
|
|
254
264
|
// 成功
|
|
255
|
-
{ ok: true, data:
|
|
265
|
+
{ ok: true, data: ResolveResult }
|
|
256
266
|
// 失败
|
|
257
267
|
{ ok: false, error: { code, message } }
|
|
258
268
|
```
|
|
@@ -269,7 +279,7 @@ type ResolveData = {
|
|
|
269
279
|
|
|
270
280
|
**关键设计原则**:
|
|
271
281
|
- 网络/资源不存在 → 返回 `error`(应用层无法继续)
|
|
272
|
-
- 内容验证失败(签名 invalid / unsigned)→ 仍 `ok: true`,通过 `data.
|
|
282
|
+
- 内容验证失败(签名 invalid / unsigned)→ 仍 `ok: true`,通过 `data.agent_md.verification.status` 标记,让应用层决定
|
|
273
283
|
|
|
274
284
|
**示例**:
|
|
275
285
|
```typescript
|
|
@@ -277,13 +287,13 @@ const store = new AIDStore({ aunPath: '...', encryptionSeed: '' });
|
|
|
277
287
|
const result = await store.resolve('bob.aid.pub');
|
|
278
288
|
|
|
279
289
|
if (result.ok) {
|
|
280
|
-
const { aid: peer,
|
|
281
|
-
console.log('证书来自:', source.
|
|
290
|
+
const { aid: peer, agent_md, source } = result.data;
|
|
291
|
+
console.log('证书来自:', source.cert_from_cache ? '本地缓存' : '网络下载');
|
|
282
292
|
|
|
283
|
-
if (
|
|
284
|
-
console.log('名片有效:',
|
|
285
|
-
} else if (
|
|
286
|
-
console.log('警告:名片签名无效,原因:',
|
|
293
|
+
if (agent_md?.verification.status === 'verified') {
|
|
294
|
+
console.log('名片有效:', agent_md.content);
|
|
295
|
+
} else if (agent_md?.verification.status === 'invalid') {
|
|
296
|
+
console.log('警告:名片签名无效,原因:', agent_md.verification.reason);
|
|
287
297
|
} else {
|
|
288
298
|
console.log('名片未签名');
|
|
289
299
|
}
|
|
@@ -303,7 +313,7 @@ if (result.ok) {
|
|
|
303
313
|
|
|
304
314
|
---
|
|
305
315
|
|
|
306
|
-
#### `fetchAgentMd(aid: string): Promise<Result<
|
|
316
|
+
#### `fetchAgentMd(aid: string): Promise<Result<FetchAgentMdResult>>`
|
|
307
317
|
|
|
308
318
|
下载 agent.md + 自动拉证书 + 验签。比 `resolve` 更轻量,适用于"只想拿名片"的场景。
|
|
309
319
|
|
|
@@ -315,13 +325,16 @@ if (result.ok) {
|
|
|
315
325
|
|
|
316
326
|
**返回**:
|
|
317
327
|
```typescript
|
|
318
|
-
type
|
|
328
|
+
type FetchAgentMdResult = {
|
|
329
|
+
aid: string;
|
|
319
330
|
content: string;
|
|
320
331
|
verification: {
|
|
321
|
-
status:
|
|
332
|
+
status: string;
|
|
322
333
|
reason?: string;
|
|
323
334
|
};
|
|
324
|
-
|
|
335
|
+
cert_pem: string;
|
|
336
|
+
etag: string;
|
|
337
|
+
last_modified: string;
|
|
325
338
|
};
|
|
326
339
|
```
|
|
327
340
|
|
|
@@ -329,32 +342,37 @@ type AgentMdFetchData = {
|
|
|
329
342
|
|
|
330
343
|
---
|
|
331
344
|
|
|
332
|
-
#### `checkAgentMd(aid: string, ttlDays?: number): Promise<Result<
|
|
345
|
+
#### `checkAgentMd(aid: string, ttlDays?: number): Promise<Result<CheckAgentMdResult>>`
|
|
333
346
|
|
|
334
347
|
比对本地缓存与远端 etag,决定是否需要重新拉取。
|
|
335
348
|
|
|
336
349
|
**返回**:
|
|
337
350
|
```typescript
|
|
338
|
-
type
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
351
|
+
type CheckAgentMdResult = {
|
|
352
|
+
aid: string;
|
|
353
|
+
local_found: boolean;
|
|
354
|
+
remote_found: boolean;
|
|
355
|
+
local_etag: string;
|
|
356
|
+
remote_etag: string;
|
|
357
|
+
needs_update: boolean;
|
|
358
|
+
ttl_days: number;
|
|
343
359
|
};
|
|
344
360
|
```
|
|
345
361
|
|
|
346
362
|
---
|
|
347
363
|
|
|
348
|
-
#### `headAgentMd(aid: string): Promise<Result<
|
|
364
|
+
#### `headAgentMd(aid: string): Promise<Result<HeadAgentMdResult>>`
|
|
349
365
|
|
|
350
366
|
HEAD 请求拿 agent.md 元数据,**判断对端是否发布了名片**。
|
|
351
367
|
|
|
352
368
|
**返回**:
|
|
353
369
|
```typescript
|
|
354
|
-
type
|
|
370
|
+
type HeadAgentMdResult = {
|
|
371
|
+
aid: string;
|
|
372
|
+
found: boolean;
|
|
355
373
|
etag: string;
|
|
356
|
-
|
|
357
|
-
|
|
374
|
+
last_modified: string;
|
|
375
|
+
content_length: number;
|
|
358
376
|
};
|
|
359
377
|
```
|
|
360
378
|
|
|
@@ -371,14 +389,17 @@ type AgentMdHeadData = {
|
|
|
371
389
|
| `changeSeed(oldSeed, newSeed)` | 否 | 更换加密种子:用旧种子解密所有私钥 → 用新种子重新加密 → 落盘 |
|
|
372
390
|
| `diagnose(aid)` | 是 | 本地状态 + 远端注册状态对比 |
|
|
373
391
|
|
|
374
|
-
|
|
392
|
+
`renewCert` / `rekey` / `diagnose` 返回 `Promise<Result<T>>`,`changeSeed` 为同步方法返回 `Result<T>`:
|
|
375
393
|
|
|
376
394
|
```typescript
|
|
395
|
+
// changeSeed 签名(同步)
|
|
396
|
+
changeSeed(oldSeed: string, newSeed: string): Result<{ changed: true; count: number }>
|
|
397
|
+
|
|
377
398
|
// renewCert 成功
|
|
378
|
-
{ ok: true, data: { renewed: true,
|
|
399
|
+
{ ok: true, data: { renewed: true, new_cert_not_after: Date, new_fingerprint: string } }
|
|
379
400
|
|
|
380
401
|
// rekey 成功
|
|
381
|
-
{ ok: true, data: { rekeyed: true,
|
|
402
|
+
{ ok: true, data: { rekeyed: true, new_cert_not_after: Date, new_fingerprint: string } }
|
|
382
403
|
|
|
383
404
|
// changeSeed 成功
|
|
384
405
|
{ ok: true, data: { changed: true, count: number } } // 重新加密的私钥数量
|
|
@@ -387,14 +408,42 @@ type AgentMdHeadData = {
|
|
|
387
408
|
{
|
|
388
409
|
ok: true,
|
|
389
410
|
data: {
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
411
|
+
aid: string;
|
|
412
|
+
status: string;
|
|
413
|
+
local_valid: boolean;
|
|
414
|
+
remote_registered: boolean;
|
|
393
415
|
suggestions: string[];
|
|
416
|
+
local: Record<string, unknown>;
|
|
417
|
+
remote: Record<string, unknown>;
|
|
394
418
|
}
|
|
395
419
|
}
|
|
396
420
|
```
|
|
397
421
|
|
|
422
|
+
**返回类型定义**:
|
|
423
|
+
```typescript
|
|
424
|
+
type RenewCertResult = {
|
|
425
|
+
renewed: true;
|
|
426
|
+
new_cert_not_after: Date;
|
|
427
|
+
new_fingerprint: string;
|
|
428
|
+
};
|
|
429
|
+
|
|
430
|
+
type RekeyResult = {
|
|
431
|
+
rekeyed: true;
|
|
432
|
+
new_cert_not_after: Date;
|
|
433
|
+
new_fingerprint: string;
|
|
434
|
+
};
|
|
435
|
+
|
|
436
|
+
type DiagnoseResult = {
|
|
437
|
+
aid: string;
|
|
438
|
+
status: string;
|
|
439
|
+
local_valid: boolean;
|
|
440
|
+
remote_registered: boolean;
|
|
441
|
+
suggestions: string[];
|
|
442
|
+
local: Record<string, unknown>;
|
|
443
|
+
remote: Record<string, unknown>;
|
|
444
|
+
};
|
|
445
|
+
```
|
|
446
|
+
|
|
398
447
|
**错误码**:
|
|
399
448
|
- `renewCert`: `CERT_RENEWAL_FAILED` | `PRIVATE_KEY_REQUIRED` | `NETWORK_ERROR`
|
|
400
449
|
- `rekey`: `REKEY_FAILED` | `PRIVATE_KEY_REQUIRED` | `NETWORK_ERROR`
|
|
@@ -408,12 +457,12 @@ const store = new AIDStore({ aunPath, encryptionSeed: 'old-seed' });
|
|
|
408
457
|
const renewResult = await store.renewCert('alice.aid.pub');
|
|
409
458
|
if (renewResult.ok) {
|
|
410
459
|
// 重新加载拿新证书
|
|
411
|
-
const me =
|
|
460
|
+
const me = store.load('alice.aid.pub').data!.aid;
|
|
412
461
|
console.log('新证书有效期至:', me.certNotAfter);
|
|
413
462
|
}
|
|
414
463
|
|
|
415
|
-
//
|
|
416
|
-
const seedResult =
|
|
464
|
+
// 换加密种子(同步)
|
|
465
|
+
const seedResult = store.changeSeed('old-seed', 'new-seed');
|
|
417
466
|
if (seedResult.ok) {
|
|
418
467
|
console.log(`已重新加密 ${seedResult.data.count} 个私钥`);
|
|
419
468
|
}
|
|
@@ -431,6 +480,11 @@ AID 是不可变的身份值对象,由 AIDStore 创建,外部不直接 `new
|
|
|
431
480
|
|------|------|------|
|
|
432
481
|
| `aid` | string | AID 标识符(如 `'alice.aid.pub'`) |
|
|
433
482
|
| `aunPath` | string | keystore 根目录(来自创建它的 store) |
|
|
483
|
+
| `deviceId` | string | 来自创建它的 AIDStore 的 deviceId |
|
|
484
|
+
| `slotId` | string | 来自创建它的 AIDStore 的 slotId |
|
|
485
|
+
| `verifySsl` | boolean | 来自创建它的 AIDStore 的 verifySsl |
|
|
486
|
+
| `rootCaPath` | string \| null | 来自创建它的 AIDStore 的 rootCaPath |
|
|
487
|
+
| `debug` | boolean | 来自创建它的 AIDStore 的 debug |
|
|
434
488
|
| `certPem` | string | PEM 格式证书 |
|
|
435
489
|
| `publicKey` | string | DER base64 公钥 |
|
|
436
490
|
| `certSubject` | string | 证书 subject |
|
|
@@ -486,7 +540,7 @@ type VerifyResult = {
|
|
|
486
540
|
**示例**:
|
|
487
541
|
```typescript
|
|
488
542
|
// 签名
|
|
489
|
-
const me =
|
|
543
|
+
const me = store.load('alice.aid.pub').data!.aid;
|
|
490
544
|
const signResult = me.signAgentMd(content);
|
|
491
545
|
if (signResult.ok) {
|
|
492
546
|
const signed = signResult.data.signed;
|
|
@@ -520,7 +574,7 @@ type Result<T> =
|
|
|
520
574
|
|
|
521
575
|
**TypeScript 使用模式**:
|
|
522
576
|
```typescript
|
|
523
|
-
const result =
|
|
577
|
+
const result = store.load('alice.aid.pub');
|
|
524
578
|
if (!result.ok) {
|
|
525
579
|
// 处理错误
|
|
526
580
|
console.log(result.error.code, result.error.message);
|
|
@@ -537,10 +591,10 @@ const me = result.data.aid;
|
|
|
537
591
|
| 场景 | 推荐方法 | 一行代码示例 |
|
|
538
592
|
|------|---------|--------------|
|
|
539
593
|
| 检查 AID 名字是否可注册 | `store.exists(aid)` | `(await store.exists('alice.aid.pub')).data?.exists === false` |
|
|
540
|
-
| 注册新身份 | `store.register` + `store.load` | `await store.register('alice.aid.pub');
|
|
541
|
-
| 加载本地身份 | `store.load(aid)` | `
|
|
542
|
-
| 列出本地所有身份 | `store.list()` | `
|
|
543
|
-
| **一站式解析对端**(推荐)| `store.resolve(aid)` | `const { aid: peer,
|
|
594
|
+
| 注册新身份 | `store.register` + `store.load` | `await store.register('alice.aid.pub'); store.load('alice.aid.pub')` |
|
|
595
|
+
| 加载本地身份 | `store.load(aid)` | `store.load('alice.aid.pub').data!.aid` |
|
|
596
|
+
| 列出本地所有身份 | `store.list()` | `store.list().data!.identities` |
|
|
597
|
+
| **一站式解析对端**(推荐)| `store.resolve(aid)` | `const { aid: peer, agent_md } = (await store.resolve('bob')).data!` |
|
|
544
598
|
| 只想拿对端 agent.md | `store.fetchAgentMd(aid)` | `(await store.fetchAgentMd('bob.aid.pub')).data?.content` |
|
|
545
599
|
| 离线签名 agent.md | `load` → `signAgentMd` | `me.signAgentMd(content).data?.signed` |
|
|
546
600
|
| 离线验签 agent.md | `load`/`resolve` → `verifyAgentMd` | `peer.verifyAgentMd(signed).data?.status` |
|
|
@@ -559,8 +613,8 @@ const me = result.data.aid;
|
|
|
559
613
|
const store = new AIDStore({ aunPath, encryptionSeed: '' });
|
|
560
614
|
const result = await store.resolve('bob.aid.pub');
|
|
561
615
|
|
|
562
|
-
if (result.ok && result.data.
|
|
563
|
-
console.log('对端可信:', result.data.
|
|
616
|
+
if (result.ok && result.data.agent_md?.verification.status === 'verified') {
|
|
617
|
+
console.log('对端可信:', result.data.agent_md.content);
|
|
564
618
|
}
|
|
565
619
|
```
|
|
566
620
|
|
|
@@ -568,7 +622,7 @@ if (result.ok && result.data.agentMd?.verification.status === 'verified') {
|
|
|
568
622
|
|
|
569
623
|
```typescript
|
|
570
624
|
const store = new AIDStore({ aunPath, encryptionSeed: '' });
|
|
571
|
-
const peer =
|
|
625
|
+
const peer = store.load('bob.aid.pub').data?.aid;
|
|
572
626
|
|
|
573
627
|
if (peer?.isCertValid()) {
|
|
574
628
|
const r = peer.verifyAgentMd(content);
|
|
@@ -597,7 +651,7 @@ if (!reg.ok) {
|
|
|
597
651
|
}
|
|
598
652
|
|
|
599
653
|
// Step 3: 加载身份用于后续操作
|
|
600
|
-
const load =
|
|
654
|
+
const load = store.load('alice.aid.pub');
|
|
601
655
|
const me = load.data!.aid;
|
|
602
656
|
```
|
|
603
657
|
|
|
@@ -605,13 +659,11 @@ const me = load.data!.aid;
|
|
|
605
659
|
|
|
606
660
|
```typescript
|
|
607
661
|
const store = new AIDStore({ aunPath, encryptionSeed: '' });
|
|
608
|
-
const list =
|
|
662
|
+
const list = store.list();
|
|
609
663
|
if (!list.ok) return;
|
|
610
664
|
|
|
611
|
-
// 并发加载多个 AID
|
|
612
|
-
const aids =
|
|
613
|
-
list.data.identities.map(i => store.load(i.aid).then(r => r.data!.aid))
|
|
614
|
-
);
|
|
665
|
+
// 并发加载多个 AID 实例(load 为同步,直接 map)
|
|
666
|
+
const aids = list.data.identities.map(i => store.load(i.aid).data!.aid);
|
|
615
667
|
|
|
616
668
|
// 并发签名
|
|
617
669
|
const signatures = aids.map(me => me.signAgentMd(content));
|
|
@@ -723,23 +775,36 @@ store.load(aid)
|
|
|
723
775
|
class AUNClient {
|
|
724
776
|
constructor(aid?: AID);
|
|
725
777
|
loadIdentity(aid: AID): void; // 加载/重载身份,aid 必须 isPrivateKeyValid(),只在 NoIdentity 或 Closed 状态可调用
|
|
778
|
+
connect(opts?: ConnectionOptions): Promise<void>;
|
|
726
779
|
setProtectedHeaders(headers: Record<string, string> | null): void; // 设置/清除实例级 protected_headers,随时可调
|
|
727
780
|
}
|
|
781
|
+
|
|
782
|
+
type ConnectionOptions = {
|
|
783
|
+
auto_reconnect?: boolean; // 是否自动重连,默认 true
|
|
784
|
+
connect_timeout?: number; // 连接超时(秒),默认 5
|
|
785
|
+
retry_initial_delay?: number; // 最小退避间隔(秒),默认 1
|
|
786
|
+
retry_max_delay?: number; // 最大退避间隔(秒),默认 64
|
|
787
|
+
retry_max_attempts?: number; // 最大重试次数,0=无限,默认 0
|
|
788
|
+
heartbeat_interval?: number; // 心跳间隔(秒),默认 30
|
|
789
|
+
call_timeout?: number; // RPC 调用超时(秒),默认 35
|
|
790
|
+
};
|
|
728
791
|
```
|
|
729
792
|
|
|
730
793
|
**说明**:
|
|
731
794
|
- 构造时可选传入 AID 对象
|
|
732
795
|
- 传入有效本地 AID(`isPrivateKeyValid() === true`)→ 直接进入 Standby 状态
|
|
733
796
|
- 传入无效 AID 或不传 → 进入 NoIdentity 状态
|
|
797
|
+
- 所有配置(aunPath、verifySsl、rootCaPath、debug 等)通过 AID 对象传递,AUNClient 不单独接受配置参数,必须通过 AIDStore 创建的 AID 使用
|
|
734
798
|
- `loadIdentity(aid)` 只在 NoIdentity 或 Closed 状态可调用
|
|
735
799
|
- `loadIdentity` 传入的 AID 必须 `isPrivateKeyValid() === true`,否则抛 `InvalidIdentityError`
|
|
736
800
|
- `deviceId` + `slotId` 由 AIDStore 管理,AUNClient 通过 AID 实例间接获取
|
|
801
|
+
- `connect(opts?)` 接受可选的 `ConnectionOptions`,用于控制连接行为(超时、重连退避等);gateway URL 和 token 来自 authenticate 缓存,不在 opts 中传入
|
|
737
802
|
- `setProtectedHeaders(headers)` 随时可调,传 `null` 清除;设置后自动附加到所有 `call()`、`sendV2()`、`sendGroupV2()` 调用,无需在每次调用时传入
|
|
738
803
|
|
|
739
804
|
**示例**:
|
|
740
805
|
```typescript
|
|
741
806
|
const store = new AIDStore({ aunPath: '...', encryptionSeed: '...' });
|
|
742
|
-
const me =
|
|
807
|
+
const me = store.load('alice.aid.pub').data!.aid;
|
|
743
808
|
const client = new AUNClient(me);
|
|
744
809
|
|
|
745
810
|
// 设置实例级 protected_headers
|
|
@@ -852,7 +917,7 @@ new AUNClient() new AUNClient(validAid)
|
|
|
852
917
|
|
|
853
918
|
**关键说明**:
|
|
854
919
|
|
|
855
|
-
- **Authenticated**:有 token,可调 `publishAgentMd()
|
|
920
|
+
- **Authenticated**:有 token,可调 `publishAgentMd()`,不需要长连接
|
|
856
921
|
- **Connecting**:`connect()` 在 Standby 时自动先 authenticate(内部完成),在 Authenticated 时直接建连接
|
|
857
922
|
- **RetryBackoff**:`isOnline === true`,SDK 仍认为自己应该在线,只是暂时等待。可读 `nextRetryAt`
|
|
858
923
|
- **ConnectionFailed**:保留身份,可调 `connect()` 重新尝试
|
|
@@ -867,10 +932,10 @@ new AUNClient() new AUNClient(validAid)
|
|
|
867
932
|
| **NoIdentity** | `loadIdentity(aid)` | Standby | aid 必须 `isPrivateKeyValid()` |
|
|
868
933
|
| | `close()` | Closed | 幂等 |
|
|
869
934
|
| **Standby** | `authenticate()` | Authenticated | 拿 token,不建长连接 |
|
|
870
|
-
| | `connect(
|
|
935
|
+
| | `connect(opts?)` | Connecting | 自动先 authenticate 再建连接 |
|
|
871
936
|
| | `loadIdentity(aid)` | ❌ 抛 StateError | 仅 NoIdentity / Closed 可重载 |
|
|
872
937
|
| | `close()` | Closed | 清除身份 |
|
|
873
|
-
| **Authenticated** | `connect(
|
|
938
|
+
| **Authenticated** | `connect(opts?)` | Connecting | 直接建连接(已有 token) |
|
|
874
939
|
| | `disconnect()` | Standby | 丢弃 token |
|
|
875
940
|
| | `close()` | Closed | 清除身份 |
|
|
876
941
|
| **Connecting** | 成功 | Ready | 自动推进 |
|
|
@@ -881,7 +946,7 @@ new AUNClient() new AUNClient(validAid)
|
|
|
881
946
|
| | 网络断开 | RetryBackoff | 自动推进,启动退避 |
|
|
882
947
|
| | `close()` | Closed | 清除身份 |
|
|
883
948
|
| **RetryBackoff** | 退避到期 | Reconnecting | 自动推进 |
|
|
884
|
-
| | `connect()` | Reconnecting | 跳过退避,立即重连 |
|
|
949
|
+
| | `connect(opts?)` | Reconnecting | 跳过退避,立即重连 |
|
|
885
950
|
| | `disconnect()` | Standby | 取消重连 |
|
|
886
951
|
| | `close()` | Closed | 清除身份 |
|
|
887
952
|
| **Reconnecting** | 成功 | Ready | 自动推进 |
|
|
@@ -889,7 +954,7 @@ new AUNClient() new AUNClient(validAid)
|
|
|
889
954
|
| | 失败(重连耗尽) | ConnectionFailed | 自动推进,记录 lastError |
|
|
890
955
|
| | `disconnect()` | Standby | 取消重连 |
|
|
891
956
|
| | `close()` | Closed | 清除身份 |
|
|
892
|
-
| **ConnectionFailed** | `connect()` | Connecting | 重新尝试 |
|
|
957
|
+
| **ConnectionFailed** | `connect(opts?)` | Connecting | 重新尝试 |
|
|
893
958
|
| | `disconnect()` | Standby | 放弃重试 |
|
|
894
959
|
| | `close()` | Closed | 清除身份 |
|
|
895
960
|
| **Closed** | `loadIdentity(aid)` | Standby | 重新激活 |
|
|
@@ -903,7 +968,7 @@ new AUNClient() new AUNClient(validAid)
|
|
|
903
968
|
| **状态推进** |
|
|
904
969
|
| `loadIdentity(aid)` | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ✅ |
|
|
905
970
|
| `authenticate()` | ❌ | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ |
|
|
906
|
-
| `connect(
|
|
971
|
+
| `connect()` | ❌ | ✅ | ✅ | ❌ | ❌ | ✅ | ❌ | ✅ | ❌ |
|
|
907
972
|
| `disconnect()` | ❌ | ❌ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ❌ |
|
|
908
973
|
| `close()` | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
|
|
909
974
|
| **状态查询** |
|
|
@@ -933,14 +998,13 @@ new AUNClient() new AUNClient(validAid)
|
|
|
933
998
|
| `off()` | ❌ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ❌ |
|
|
934
999
|
| **agent.md 上传**(需 token) |
|
|
935
1000
|
| `publishAgentMd()` | ❌ | ❌ | ✅ | ✅ | ✅ | ❌ | ❌ | ❌ | ❌ |
|
|
936
|
-
| `uploadAgentMd()` | ❌ | ❌ | ✅ | ✅ | ✅ | ❌ | ❌ | ❌ | ❌ |
|
|
937
1001
|
|
|
938
1002
|
**图例**:
|
|
939
1003
|
- ✅ 可调用
|
|
940
1004
|
- ❌ 不可调用(抛 StateError)
|
|
941
1005
|
|
|
942
1006
|
**说明**:
|
|
943
|
-
- `publishAgentMd`
|
|
1007
|
+
- `publishAgentMd` 要求至少 Authenticated(有 token)。Standby 状态下先调 `authenticate()`
|
|
944
1008
|
- `disconnect()` 在 ConnectionFailed 状态也可调,从"放弃重试"语义回到 Standby
|
|
945
1009
|
- 签名/验签操作直接用 `AID` 实例(`me.signAgentMd()`、`peer.verifyAgentMd()`),不在 AUNClient 上
|
|
946
1010
|
|
|
@@ -952,8 +1016,8 @@ new AUNClient() new AUNClient(validAid)
|
|
|
952
1016
|
class AUNClient {
|
|
953
1017
|
// ─── 基础状态 ─────────────────────────
|
|
954
1018
|
get state(): ConnectionState;
|
|
955
|
-
// '
|
|
956
|
-
// | '
|
|
1019
|
+
// 'no_identity' | 'standby' | 'authenticated' | 'connecting' | 'ready'
|
|
1020
|
+
// | 'retry_backoff' | 'reconnecting' | 'connection_failed' | 'closed'
|
|
957
1021
|
|
|
958
1022
|
get currentAid(): AID | null;
|
|
959
1023
|
get aunPath(): string | null;
|
|
@@ -972,12 +1036,12 @@ class AUNClient {
|
|
|
972
1036
|
get gatewayHealth(): boolean | null;
|
|
973
1037
|
|
|
974
1038
|
// ─── Capability Getters ─────────────────
|
|
975
|
-
get hasIdentity(): boolean; // state !== '
|
|
1039
|
+
get hasIdentity(): boolean; // state !== 'no_identity' && state !== 'closed'
|
|
976
1040
|
get canSign(): boolean; // hasIdentity && currentAid.isPrivateKeyValid()
|
|
977
1041
|
get canConnect(): boolean; // hasIdentity && state !== 'closed'
|
|
978
1042
|
get canSend(): boolean; // state === 'ready'
|
|
979
1043
|
get isReady(): boolean; // 同 canSend
|
|
980
|
-
get isOnline(): boolean; // ready |
|
|
1044
|
+
get isOnline(): boolean; // ready | retry_backoff | reconnecting
|
|
981
1045
|
get isClosed(): boolean; // state === 'closed'
|
|
982
1046
|
}
|
|
983
1047
|
```
|
|
@@ -1005,9 +1069,9 @@ class AUNClient {
|
|
|
1005
1069
|
│ ├─ 是 → call()
|
|
1006
1070
|
│ └─ 否 → 看当前状态
|
|
1007
1071
|
│ ├─ 'standby' / 'authenticated' → connect()
|
|
1008
|
-
│ ├─ '
|
|
1009
|
-
│ ├─ '
|
|
1010
|
-
│ └─ '
|
|
1072
|
+
│ ├─ 'retry_backoff' → connect()(跳过退避立即重连)
|
|
1073
|
+
│ ├─ 'connection_failed' → connect()(重新尝试)
|
|
1074
|
+
│ └─ 'no_identity' / 'closed' → loadIdentity() 先
|
|
1011
1075
|
│
|
|
1012
1076
|
├─ 主动断开 → disconnect()(任意连接相关状态都行)
|
|
1013
1077
|
│
|
|
@@ -1022,7 +1086,7 @@ class AUNClient {
|
|
|
1022
1086
|
|
|
1023
1087
|
```typescript
|
|
1024
1088
|
const store = new AIDStore({ aunPath: '...', encryptionSeed: '...' });
|
|
1025
|
-
const me =
|
|
1089
|
+
const me = store.load('alice.aid.pub').data!.aid;
|
|
1026
1090
|
const client = new AUNClient(me); // → Standby
|
|
1027
1091
|
await client.connect(); // Standby → Authenticated → Connecting → Ready
|
|
1028
1092
|
// connect() 内部自动完成 authenticate(如果还没有 token)
|
|
@@ -1032,7 +1096,7 @@ await client.connect(); // Standby → Authenticated → Connecting
|
|
|
1032
1096
|
|
|
1033
1097
|
```typescript
|
|
1034
1098
|
const store = new AIDStore({ aunPath: '...', encryptionSeed: '...' });
|
|
1035
|
-
const me =
|
|
1099
|
+
const me = store.load('alice.aid.pub').data!.aid;
|
|
1036
1100
|
const client = new AUNClient(me); // → Standby
|
|
1037
1101
|
|
|
1038
1102
|
await client.authenticate(); // Standby → Authenticated
|
|
@@ -1057,15 +1121,15 @@ console.log('下次重连:', client.nextRetryInSeconds, '秒后');
|
|
|
1057
1121
|
**场景 4:重连等待中应用想立即发消息**
|
|
1058
1122
|
|
|
1059
1123
|
```typescript
|
|
1060
|
-
// 当前 state === '
|
|
1124
|
+
// 当前 state === 'retry_backoff'
|
|
1061
1125
|
// 应用想立即发消息,不想等退避
|
|
1062
1126
|
|
|
1063
1127
|
if (!client.canSend) {
|
|
1064
1128
|
await client.connect(); // 跳过退避,立即进入 Reconnecting
|
|
1065
1129
|
}
|
|
1066
1130
|
|
|
1067
|
-
// 等待 Reconnecting → Ready,或监听 '
|
|
1068
|
-
client.on('
|
|
1131
|
+
// 等待 Reconnecting → Ready,或监听 'state_change' 事件
|
|
1132
|
+
client.on('state_change', ({ to }) => {
|
|
1069
1133
|
if (to === 'ready') {
|
|
1070
1134
|
client.call('message.send', {...});
|
|
1071
1135
|
}
|
|
@@ -1075,7 +1139,7 @@ client.on('state-change', ({ to }) => {
|
|
|
1075
1139
|
**场景 5:重连耗尽后手动重试**
|
|
1076
1140
|
|
|
1077
1141
|
```typescript
|
|
1078
|
-
// state === '
|
|
1142
|
+
// state === 'connection_failed'
|
|
1079
1143
|
console.log('重连失败:', client.lastError, client.lastErrorCode);
|
|
1080
1144
|
|
|
1081
1145
|
// 应用决定再试一次(身份还在)
|
|
@@ -1096,7 +1160,7 @@ await client.connect(); // Standby → Authenticated → Connecting → Read
|
|
|
1096
1160
|
|
|
1097
1161
|
```typescript
|
|
1098
1162
|
await client.close(); // → Closed(身份清除)
|
|
1099
|
-
const newMe =
|
|
1163
|
+
const newMe = store.load('bob.aid.pub').data!.aid;
|
|
1100
1164
|
client.loadIdentity(newMe); // Closed → Standby(新身份)
|
|
1101
1165
|
await client.connect(); // → Authenticated → Connecting → Ready
|
|
1102
1166
|
```
|
|
@@ -1106,7 +1170,7 @@ await client.connect(); // → Authenticated → Connecting →
|
|
|
1106
1170
|
```typescript
|
|
1107
1171
|
// 应用启动时,预先认证以减少后续连接延迟
|
|
1108
1172
|
const store = new AIDStore({ aunPath: '...', encryptionSeed: '...' });
|
|
1109
|
-
const me =
|
|
1173
|
+
const me = store.load('alice.aid.pub').data!.aid;
|
|
1110
1174
|
const client = new AUNClient(me);
|
|
1111
1175
|
await client.authenticate(); // → Authenticated(提前拿好 token)
|
|
1112
1176
|
|
|
@@ -1134,14 +1198,14 @@ if (client.isOnline) {
|
|
|
1134
1198
|
}
|
|
1135
1199
|
|
|
1136
1200
|
if (!client.hasIdentity) {
|
|
1137
|
-
client.loadIdentity(
|
|
1201
|
+
client.loadIdentity(store.load('alice.aid.pub').data!.aid);
|
|
1138
1202
|
}
|
|
1139
1203
|
```
|
|
1140
1204
|
|
|
1141
1205
|
**RetryBackoff 状态下的常见模式**:
|
|
1142
1206
|
|
|
1143
1207
|
```typescript
|
|
1144
|
-
if (client.state === '
|
|
1208
|
+
if (client.state === 'retry_backoff') {
|
|
1145
1209
|
console.log(`将在 ${client.nextRetryInSeconds} 秒后自动重连`);
|
|
1146
1210
|
console.log(`已尝试 ${client.retryAttempt}/${client.retryMaxAttempts} 次`);
|
|
1147
1211
|
|
|
@@ -1275,6 +1339,7 @@ client.on('group.message_created', (msg) => {
|
|
|
1275
1339
|
| | `rekey(aid)` | 是 | 密钥轮换并落盘 |
|
|
1276
1340
|
| | `changeSeed(oldSeed, newSeed)` | 否 | 更换加密种子 |
|
|
1277
1341
|
| | `diagnose(aid)` | 是 | 本地 + 远端状态对比 |
|
|
1342
|
+
| **资源管理** | `close()` | 否 | 释放内部 HTTP client 等资源 |
|
|
1278
1343
|
|
|
1279
1344
|
### 4.2 AID 操作表
|
|
1280
1345
|
|
|
@@ -1295,7 +1360,7 @@ client.on('group.message_created', (msg) => {
|
|
|
1295
1360
|
| **构造** | `new AUNClient()` | 否 | — | → NoIdentity | 不传身份 |
|
|
1296
1361
|
| | `new AUNClient(aid)` | 否 | — | → Standby | aid 必须 `isPrivateKeyValid()` |
|
|
1297
1362
|
| **状态推进** | `loadIdentity(aid)` | 否 | NoIdentity \| Closed | → Standby | 加载/重载身份 |
|
|
1298
|
-
| | `connect(
|
|
1363
|
+
| | `connect(opts?)` | 是 | Standby \| Authenticated \| RetryBackoff \| ConnectionFailed | → Connecting / Reconnecting | Standby 时自动先 authenticate;可选 ConnectionOptions 控制连接行为;gateway URL 和 token 来自 authenticate 缓存,不在 opts 中传入 |
|
|
1299
1364
|
| | `authenticate()` | 是 | Standby | → Authenticated | 拿 token,不建长连接 |
|
|
1300
1365
|
| | `disconnect()` | 是 | Authenticated \| Connecting \| Ready \| RetryBackoff \| Reconnecting \| ConnectionFailed | → Standby | 主动断开 |
|
|
1301
1366
|
| | `close()` | 否 | * | → Closed | 清除身份 + 资源 |
|
|
@@ -1313,7 +1378,7 @@ client.on('group.message_created', (msg) => {
|
|
|
1313
1378
|
| | `canConnect` (getter) | 否 | — | — | hasIdentity 且非 Closed |
|
|
1314
1379
|
| | `canSend` (getter) | 否 | — | — | state === 'ready' |
|
|
1315
1380
|
| | `isReady` (getter) | 否 | — | — | 同 canSend |
|
|
1316
|
-
| | `isOnline` (getter) | 否 | — | — | ready \|
|
|
1381
|
+
| | `isOnline` (getter) | 否 | — | — | ready \| retry_backoff \| reconnecting |
|
|
1317
1382
|
| | `isClosed` (getter) | 否 | — | — | state === 'closed' |
|
|
1318
1383
|
| **对端管理** | `lookupPeer(aid)` | 视缓存 | hasIdentity | — | 查缓存 → 无则解析 |
|
|
1319
1384
|
| | `getPeer(aid)` | 否 | hasIdentity | — | 仅查缓存 |
|
|
@@ -1323,7 +1388,6 @@ client.on('group.message_created', (msg) => {
|
|
|
1323
1388
|
| | `on(event, handler)` | 否 | hasIdentity | — | 事件订阅 |
|
|
1324
1389
|
| | `off(event, handler)` | 否 | hasIdentity | — | 取消订阅 |
|
|
1325
1390
|
| **agent.md 上传** | `publishAgentMd(content?)` | 是 | Authenticated \| Connecting \| Ready | — | 签名 + 上传 |
|
|
1326
|
-
| | `uploadAgentMd(content)` | 是 | Authenticated \| Connecting \| Ready | — | 直接上传已签名内容 |
|
|
1327
1391
|
| **配置** | `setProtectedHeaders(headers)` | 否 | * | — | 设置实例级 protected_headers,传 null 清除,随时可调 |
|
|
1328
1392
|
| | `getProtectedHeaders()` | 否 | * | — | 读取当前实例级 protected_headers |
|
|
1329
1393
|
|
|
@@ -1331,7 +1395,7 @@ client.on('group.message_created', (msg) => {
|
|
|
1331
1395
|
|
|
1332
1396
|
| 事件 | 触发时机 | 数据 |
|
|
1333
1397
|
|------|---------|------|
|
|
1334
|
-
| `
|
|
1398
|
+
| `state_change` | 状态变化 | `{ from, to }` |
|
|
1335
1399
|
| `message.received` | 收到 P2P 消息 | `{ from, payload, protected_headers?, context?, ... }` |
|
|
1336
1400
|
| `group.message_created` | 收到群消息 | `{ from, group_id, payload, protected_headers?, context?, ... }` |
|
|
1337
1401
|
| `message.recalled` | 消息被撤回 | `{ message_id, from, ... }` |
|
|
@@ -1407,7 +1471,7 @@ const store = new AIDStore({
|
|
|
1407
1471
|
encryptionSeed: process.env.ENCRYPTION_SEED || ''
|
|
1408
1472
|
});
|
|
1409
1473
|
|
|
1410
|
-
const loadResult =
|
|
1474
|
+
const loadResult = store.load('alice.aid.pub');
|
|
1411
1475
|
if (!loadResult.ok) {
|
|
1412
1476
|
console.log('加载失败:', loadResult.error.code);
|
|
1413
1477
|
return;
|
|
@@ -1435,7 +1499,7 @@ const store = new AIDStore({
|
|
|
1435
1499
|
encryptionSeed: process.env.ENCRYPTION_SEED || ''
|
|
1436
1500
|
});
|
|
1437
1501
|
|
|
1438
|
-
const me =
|
|
1502
|
+
const me = store.load('alice.aid.pub').data!.aid;
|
|
1439
1503
|
const client = new AUNClient(me);
|
|
1440
1504
|
|
|
1441
1505
|
await client.connect(); // 自动完成认证 + 建立连接
|
|
@@ -1503,7 +1567,7 @@ if (verifyResult.ok && verifyResult.data.status === 'verified') {
|
|
|
1503
1567
|
| `auth.fetchPeerCert({ aid })` | `AIDStore.resolve()` 内部自动完成 | ✅ |
|
|
1504
1568
|
| `auth.signAgentMd(content, { aid })` | `aid.signAgentMd(content)` | ✅ |
|
|
1505
1569
|
| `auth.verifyAgentMd(content, { aid, certPem })` | `aid.verifyAgentMd(content)` | ✅ |
|
|
1506
|
-
| `auth.uploadAgentMd(content)` | `AUNClient.
|
|
1570
|
+
| `auth.uploadAgentMd(content)` | `AUNClient._uploadAgentMd(content)`(内部私有方法) | ✅ |
|
|
1507
1571
|
| `auth.downloadAgentMd(aid)` | `AIDStore.fetchAgentMd(aid)` | ✅ |
|
|
1508
1572
|
| `auth.headAgentMd(aid)` | `AIDStore.headAgentMd(aid)` | ✅ |
|
|
1509
1573
|
| `auth.checkAid({ aid })` | `AIDStore.diagnose(aid)` | ✅ |
|
|
@@ -1517,7 +1581,7 @@ if (verifyResult.ok && verifyResult.data.status === 'verified') {
|
|
|
1517
1581
|
|
|
1518
1582
|
| 当前方法 | 新归宿 | 迁移状态 |
|
|
1519
1583
|
|---------|--------|:--------:|
|
|
1520
|
-
| `client.connect(auth, opts)` | `AUNClient.connect(
|
|
1584
|
+
| `client.connect(auth, opts)` | `AUNClient.connect()` | ✅ |
|
|
1521
1585
|
| `client.disconnect()` | `AUNClient.disconnect()` | ✅ |
|
|
1522
1586
|
| `client.close()` | `AUNClient.close()` | ✅ |
|
|
1523
1587
|
| `client.call(method, params)` | `AUNClient.call(method, params)` | ✅ |
|
|
@@ -1628,6 +1692,6 @@ if (verifyResult.ok && verifyResult.data.status === 'verified') {
|
|
|
1628
1692
|
|
|
1629
1693
|
---
|
|
1630
1694
|
|
|
1631
|
-
**文档版本**:v4.
|
|
1632
|
-
**最后更新**:2026-05-
|
|
1695
|
+
**文档版本**:v4.2
|
|
1696
|
+
**最后更新**:2026-05-30
|
|
1633
1697
|
|