evolclaw 3.1.2 → 3.1.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.
Files changed (48) hide show
  1. package/CHANGELOG.md +38 -0
  2. package/README.md +2 -6
  3. package/assets/.env.template +4 -0
  4. package/assets/config.json.template +6 -0
  5. package/assets/wechat-group-qr.jpeg +0 -0
  6. package/dist/agents/claude-runner.js +1 -1
  7. package/dist/agents/codex-runner.js +75 -19
  8. package/dist/agents/gemini-runner.js +0 -2
  9. package/dist/agents/kit-renderer.js +85 -22
  10. package/dist/aun/aid/agentmd.js +67 -74
  11. package/dist/aun/aid/client.js +22 -7
  12. package/dist/aun/aid/identity.js +314 -28
  13. package/dist/aun/aid/index.js +2 -2
  14. package/dist/aun/rpc/connection.js +8 -10
  15. package/dist/channels/aun.js +53 -41
  16. package/dist/cli/agent.js +28 -28
  17. package/dist/cli/bench.js +8 -14
  18. package/dist/cli/help.js +23 -0
  19. package/dist/cli/index.js +398 -73
  20. package/dist/cli/init-channel.js +2 -3
  21. package/dist/cli/init.js +13 -6
  22. package/dist/cli/link-rules.js +2 -1
  23. package/dist/cli/net-check.js +10 -11
  24. package/dist/core/command-handler.js +621 -541
  25. package/dist/core/evolagent.js +31 -0
  26. package/dist/core/message/im-renderer.js +10 -0
  27. package/dist/core/message/message-bridge.js +123 -24
  28. package/dist/core/message/message-processor.js +61 -31
  29. package/dist/core/relation/peer-identity.js +64 -21
  30. package/dist/core/session/session-manager.js +191 -44
  31. package/dist/core/trigger/manager.js +37 -0
  32. package/dist/index.js +4 -1
  33. package/dist/paths.js +87 -16
  34. package/dist/utils/npm-ops.js +18 -11
  35. package/kits/eck_manifest.json +9 -9
  36. package/kits/rules/02-navigation.md +1 -0
  37. package/kits/rules/05-venue.md +2 -2
  38. package/kits/rules/06-channel.md +2 -18
  39. package/kits/templates/system-fragments/baseagent.md +8 -2
  40. package/kits/templates/system-fragments/channel.md +20 -8
  41. package/kits/templates/system-fragments/identity.md +5 -6
  42. package/kits/templates/system-fragments/relation.md +10 -5
  43. package/kits/templates/system-fragments/session.md +20 -0
  44. package/kits/templates/system-fragments/venue.md +5 -3
  45. package/package.json +4 -2
  46. package/dist/net-check.js +0 -640
  47. package/dist/watch-msg.js +0 -544
  48. package/kits/templates/system-fragments/runtime.md +0 -19
@@ -1,56 +1,193 @@
1
1
  import fs from 'fs';
2
2
  import path from 'path';
3
- import os from 'os';
4
3
  import crypto from 'crypto';
5
- import { getAunClient, downloadCaRoot } from './client.js';
6
- import { resolvePaths, aidsDir as evolclawAidsDir, agentMdPath } from '../../paths.js';
4
+ import { getAunClient, createAunClient, downloadCaRoot } from './client.js';
5
+ import { resolvePaths, agentMdPath, aunPath as defaultAunPath } from '../../paths.js';
7
6
  // ==================== Validation ====================
8
7
  export function isValidAid(name) {
9
8
  const labels = name.split('.');
10
9
  return labels.length >= 3 && labels.every(l => /^[a-zA-Z0-9]([a-zA-Z0-9-]*[a-zA-Z0-9])?$/.test(l));
11
10
  }
11
+ /**
12
+ * 根据扫描得到的状态推断 AID 分类。
13
+ * - hasPrivateKey + signVerified=true → mine
14
+ * - hasPrivateKey + (signVerified=false 或未实测) → broken
15
+ * - !hasPrivateKey + hasCert → peer-cert
16
+ * - !hasPrivateKey + !hasCert → no-cert
17
+ */
18
+ function categorizeAid(info) {
19
+ if (info.hasPrivateKey) {
20
+ if (info.signVerified === true)
21
+ return 'mine';
22
+ return 'broken';
23
+ }
24
+ return info.hasCert ? 'peer-cert' : 'no-cert';
25
+ }
12
26
  // ==================== AID Operations ====================
13
27
  export function aidList(aunPath) {
14
- const aunAidsDir = path.join(aunPath ?? path.join(os.homedir(), '.aun'), 'AIDs');
15
- const ecAidsDir = evolclawAidsDir();
28
+ const root = aunPath ?? defaultAunPath();
29
+ const aunAidsDir = path.join(root, 'AIDs');
16
30
  const seen = new Map();
17
- // Scan ~/.aun/AIDs (private keys live here)
18
31
  if (fs.existsSync(aunAidsDir)) {
19
32
  for (const e of fs.readdirSync(aunAidsDir, { withFileTypes: true })) {
20
33
  if (!e.isDirectory())
21
34
  continue;
35
+ const aidDir = path.join(aunAidsDir, e.name);
36
+ const keyJsonPath = path.join(aidDir, 'private', 'key.json');
37
+ const hasPrivateKey = fs.existsSync(path.join(aidDir, 'private'));
38
+ const certPath = path.join(aidDir, 'public', 'cert.pem');
39
+ let hasCert = false;
40
+ let certExpired = false;
41
+ let keyMatchesCert = null;
42
+ if (fs.existsSync(certPath)) {
43
+ hasCert = true;
44
+ try {
45
+ const certPem = fs.readFileSync(certPath, 'utf-8');
46
+ const x509 = new crypto.X509Certificate(certPem);
47
+ certExpired = new Date(x509.validTo) < new Date();
48
+ if (hasPrivateKey && fs.existsSync(keyJsonPath)) {
49
+ try {
50
+ const kp = JSON.parse(fs.readFileSync(keyJsonPath, 'utf-8'));
51
+ const localPubB64 = typeof kp?.public_key_der_b64 === 'string' ? kp.public_key_der_b64 : '';
52
+ if (localPubB64) {
53
+ const certPubDer = x509.publicKey.export({ type: 'spki', format: 'der' });
54
+ const localPubDer = Buffer.from(localPubB64, 'base64');
55
+ keyMatchesCert = certPubDer.equals(localPubDer);
56
+ }
57
+ }
58
+ catch { /* key.json 不可解析视作不匹配 */
59
+ keyMatchesCert = false;
60
+ }
61
+ }
62
+ }
63
+ catch {
64
+ // 证书无法解析视为过期 / 不可用
65
+ certExpired = true;
66
+ }
67
+ }
22
68
  seen.set(e.name, {
23
69
  aid: e.name,
24
- hasPrivateKey: fs.existsSync(path.join(aunAidsDir, e.name, 'private')),
70
+ category: categorizeAid({
71
+ hasPrivateKey,
72
+ hasCert,
73
+ // 静态扫描没跑实测,用 canSign 作为 mine/broken 的近似判定
74
+ signVerified: hasPrivateKey ? (hasCert && !certExpired && keyMatchesCert === true) : null,
75
+ canSign: hasPrivateKey && hasCert && !certExpired && keyMatchesCert === true,
76
+ }),
77
+ hasPrivateKey,
25
78
  hasAgentMd: fs.existsSync(agentMdPath(e.name)),
79
+ hasCert,
80
+ certExpired,
81
+ keyMatchesCert,
82
+ canSign: hasPrivateKey && hasCert && !certExpired && keyMatchesCert === true,
83
+ signVerified: null,
26
84
  });
27
85
  }
28
86
  }
29
- // Scan $EVOLCLAW_HOME/AIDs (agent.md lives here)
30
- if (fs.existsSync(ecAidsDir) && ecAidsDir !== aunAidsDir) {
31
- for (const e of fs.readdirSync(ecAidsDir, { withFileTypes: true })) {
32
- if (!e.isDirectory())
33
- continue;
34
- if (seen.has(e.name))
87
+ return [...seen.values()];
88
+ }
89
+ // ==================== Sign Self-Test ====================
90
+ /**
91
+ * 实跑一次本地 sign + verify,验证 AID 是否真能签名/验签。
92
+ * 全本地(不联网):用 SDK 解密私钥 → ECDSA 签 payload → 用本地 cert 公钥验。
93
+ * 任一环节失败都视为不可签——包括私钥 passphrase 解不开、cert 被 SDK 主动 discard 等。
94
+ */
95
+ export async function verifySignAbility(aid, opts) {
96
+ const aunPath = opts?.aunPath ?? defaultAunPath();
97
+ let ownClient = null;
98
+ try {
99
+ let client = opts?.client;
100
+ if (!client) {
101
+ client = await createAunClient({ aunPath });
102
+ ownClient = client;
103
+ }
104
+ const probe = `# probe\naid: "${aid}"\n`;
105
+ let signed;
106
+ try {
107
+ signed = await client.auth.signAgentMd(probe, { aid });
108
+ }
109
+ catch (e) {
110
+ return { ok: false, reason: `sign failed: ${String(e?.message || e).slice(0, 120)}` };
111
+ }
112
+ const certPath = path.join(aunPath, 'AIDs', aid, 'public', 'cert.pem');
113
+ const certPem = fs.existsSync(certPath) ? fs.readFileSync(certPath, 'utf-8') : '';
114
+ if (!certPem)
115
+ return { ok: false, reason: 'cert.pem missing' };
116
+ let result;
117
+ try {
118
+ result = await client.auth.verifyAgentMd(signed, { aid, certPem });
119
+ }
120
+ catch (e) {
121
+ return { ok: false, reason: `verify threw: ${String(e?.message || e).slice(0, 120)}` };
122
+ }
123
+ const verified = result?.status === 'verified' || result?.verified === true;
124
+ if (verified)
125
+ return { ok: true };
126
+ return { ok: false, reason: `verify failed: ${result?.status ?? 'unknown'}${result?.reason ? ' — ' + result.reason : ''}` };
127
+ }
128
+ finally {
129
+ if (ownClient) {
130
+ try {
131
+ await ownClient.close();
132
+ }
133
+ catch { /* ignore */ }
134
+ }
135
+ }
136
+ }
137
+ /**
138
+ * aidList 的"实测版":先做静态扫描,再对每个 AID 跑一次本地 sign+verify。
139
+ * 共用同一个 AUNClient 实例,避免重复初始化 secret-store / sqlite。
140
+ */
141
+ export async function aidListVerified(aunPath) {
142
+ const list = aidList(aunPath);
143
+ const root = aunPath ?? defaultAunPath();
144
+ const client = await createAunClient({ aunPath: root });
145
+ try {
146
+ for (const a of list) {
147
+ // canSign=false 的 AID 不必跑实测,结论已经明确
148
+ if (!a.canSign) {
149
+ a.signVerified = false;
150
+ if (!a.hasPrivateKey)
151
+ a.signError = 'no private key';
152
+ else if (!a.hasCert)
153
+ a.signError = 'no cert';
154
+ else if (a.certExpired)
155
+ a.signError = 'cert expired';
156
+ else if (a.keyMatchesCert === false)
157
+ a.signError = 'key/cert public-key mismatch';
158
+ a.category = categorizeAid({
159
+ hasPrivateKey: a.hasPrivateKey, hasCert: a.hasCert,
160
+ signVerified: a.signVerified, canSign: a.canSign,
161
+ });
35
162
  continue;
36
- seen.set(e.name, {
37
- aid: e.name,
38
- hasPrivateKey: fs.existsSync(path.join(aunAidsDir, e.name, 'private')),
39
- hasAgentMd: fs.existsSync(agentMdPath(e.name)),
163
+ }
164
+ const r = await verifySignAbility(a.aid, { aunPath: root, client });
165
+ a.signVerified = r.ok;
166
+ if (!r.ok)
167
+ a.signError = r.reason;
168
+ a.category = categorizeAid({
169
+ hasPrivateKey: a.hasPrivateKey, hasCert: a.hasCert,
170
+ signVerified: a.signVerified, canSign: a.canSign,
40
171
  });
41
172
  }
42
173
  }
43
- return [...seen.values()];
174
+ finally {
175
+ try {
176
+ await client.close();
177
+ }
178
+ catch { /* ignore */ }
179
+ }
180
+ return list;
44
181
  }
45
182
  export async function aidCreate(aid, opts) {
46
- const aunPath = opts?.aunPath ?? path.join(os.homedir(), '.aun');
183
+ const aunPath = opts?.aunPath ?? defaultAunPath();
47
184
  const aidDir = path.join(aunPath, 'AIDs', aid);
48
185
  if (fs.existsSync(aidDir) && fs.existsSync(path.join(aidDir, 'private'))) {
49
186
  const client = await getAunClient(aid, { aunPath });
50
187
  return { aid, alreadyExisted: true, gateway: '', client };
51
188
  }
52
- const { AUNClient, GatewayDiscovery } = await import('@agentunion/fastaun');
53
- let client = new AUNClient({ aun_path: aunPath });
189
+ const { GatewayDiscovery } = await import('@agentunion/fastaun');
190
+ let client = await createAunClient({ aunPath });
54
191
  try {
55
192
  const result = await client.auth.createAid({ aid });
56
193
  const gateway = result.gateway || '';
@@ -61,7 +198,7 @@ export async function aidCreate(aid, opts) {
61
198
  await client.close();
62
199
  }
63
200
  catch { /* ignore */ }
64
- client = new AUNClient({ aun_path: aunPath, root_ca_path: caCertPath });
201
+ client = await createAunClient({ aunPath });
65
202
  await client.auth.createAid({ aid });
66
203
  }
67
204
  let gatewayUrl = gateway;
@@ -86,34 +223,183 @@ export async function aidCreate(aid, opts) {
86
223
  }
87
224
  }
88
225
  // ==================== Show ====================
89
- export function aidShow(aid, opts) {
90
- const aunPath = opts?.aunPath ?? path.join(os.homedir(), '.aun');
226
+ export async function aidShow(aid, opts) {
227
+ const aunPath = opts?.aunPath ?? defaultAunPath();
91
228
  const aidDir = path.join(aunPath, 'AIDs', aid);
92
229
  const hasPrivateKey = fs.existsSync(path.join(aidDir, 'private'));
93
230
  const hasAgentMd = fs.existsSync(agentMdPath(aid));
94
231
  let certExpiresAt = null;
95
232
  let certSubject = null;
233
+ let certExpired = false;
234
+ let certPem = null;
235
+ let keyMatchesCert = null;
96
236
  const certPath = path.join(aidDir, 'public', 'cert.pem');
97
237
  if (fs.existsSync(certPath)) {
98
238
  try {
99
- const pem = fs.readFileSync(certPath, 'utf-8');
100
- const x509 = new crypto.X509Certificate(pem);
239
+ certPem = fs.readFileSync(certPath, 'utf-8');
240
+ const x509 = new crypto.X509Certificate(certPem);
101
241
  certExpiresAt = x509.validTo;
102
242
  certSubject = x509.subject;
243
+ certExpired = new Date(x509.validTo) < new Date();
244
+ const keyJsonPath = path.join(aidDir, 'private', 'key.json');
245
+ if (hasPrivateKey && fs.existsSync(keyJsonPath)) {
246
+ try {
247
+ const kp = JSON.parse(fs.readFileSync(keyJsonPath, 'utf-8'));
248
+ const localPubB64 = typeof kp?.public_key_der_b64 === 'string' ? kp.public_key_der_b64 : '';
249
+ if (localPubB64) {
250
+ const certPubDer = x509.publicKey.export({ type: 'spki', format: 'der' });
251
+ const localPubDer = Buffer.from(localPubB64, 'base64');
252
+ keyMatchesCert = certPubDer.equals(localPubDer);
253
+ }
254
+ }
255
+ catch {
256
+ keyMatchesCert = false;
257
+ }
258
+ }
103
259
  }
104
260
  catch { /* ignore parse errors */ }
105
261
  }
106
- return { aid, hasPrivateKey, hasAgentMd, certExpiresAt, certSubject };
262
+ let agentMdSignature = 'unknown';
263
+ let agentMdSignatureReason;
264
+ let signVerified = null;
265
+ let signError;
266
+ // 先做一次签名自检(共享 client,避免重复起 SDK)
267
+ const client = await createAunClient({ aunPath });
268
+ try {
269
+ if (hasPrivateKey && certPem && !certExpired && keyMatchesCert !== false) {
270
+ const r = await verifySignAbility(aid, { aunPath, client });
271
+ signVerified = r.ok;
272
+ if (!r.ok)
273
+ signError = r.reason;
274
+ }
275
+ else {
276
+ signVerified = false;
277
+ if (!hasPrivateKey)
278
+ signError = 'no private key';
279
+ else if (!certPem)
280
+ signError = 'no cert';
281
+ else if (certExpired)
282
+ signError = 'cert expired';
283
+ else if (keyMatchesCert === false)
284
+ signError = 'key/cert public-key mismatch';
285
+ }
286
+ if (hasAgentMd) {
287
+ try {
288
+ const content = fs.readFileSync(agentMdPath(aid), 'utf-8');
289
+ if (!content.includes('AUN-SIGNATURE')) {
290
+ agentMdSignature = 'unsigned';
291
+ }
292
+ else {
293
+ const result = await client.auth.verifyAgentMd(content, { aid, ...(certPem ? { certPem } : {}) });
294
+ if (result.status === 'verified' || result.verified) {
295
+ agentMdSignature = 'verified';
296
+ }
297
+ else if (result.status === 'unsigned') {
298
+ agentMdSignature = 'unsigned';
299
+ }
300
+ else {
301
+ agentMdSignature = 'invalid';
302
+ agentMdSignatureReason = result.reason;
303
+ }
304
+ }
305
+ }
306
+ catch (e) {
307
+ agentMdSignature = 'unknown';
308
+ agentMdSignatureReason = String(e?.message || e).slice(0, 100);
309
+ }
310
+ }
311
+ }
312
+ finally {
313
+ try {
314
+ await client.close();
315
+ }
316
+ catch { }
317
+ }
318
+ return { aid, hasPrivateKey, hasAgentMd, certExpiresAt, certSubject, certExpired, keyMatchesCert, signVerified, signError, agentMdSignature, agentMdSignatureReason };
107
319
  }
108
320
  // ==================== Delete ====================
109
321
  export function aidDelete(aid, opts) {
110
- const aunPath = opts?.aunPath ?? path.join(os.homedir(), '.aun');
322
+ const aunPath = opts?.aunPath ?? defaultAunPath();
111
323
  const aidDir = path.join(aunPath, 'AIDs', aid);
112
324
  if (!fs.existsSync(aidDir))
113
325
  return false;
114
326
  fs.rmSync(aidDir, { recursive: true, force: true });
115
327
  return true;
116
328
  }
329
+ export async function probePkiRecoverability(aid, opts) {
330
+ const aunPath = opts?.aunPath ?? defaultAunPath();
331
+ const timeoutMs = opts?.timeoutMs ?? 8000;
332
+ const keyJsonPath = path.join(aunPath, 'AIDs', aid, 'private', 'key.json');
333
+ if (!fs.existsSync(keyJsonPath))
334
+ return { kind: 'no-key' };
335
+ let localPubB64 = '';
336
+ try {
337
+ const kp = JSON.parse(fs.readFileSync(keyJsonPath, 'utf-8'));
338
+ if (typeof kp?.public_key_der_b64 !== 'string' || !kp.public_key_der_b64) {
339
+ return { kind: 'unknown', reason: 'key.json missing public_key_der_b64' };
340
+ }
341
+ localPubB64 = kp.public_key_der_b64;
342
+ }
343
+ catch (e) {
344
+ return { kind: 'unknown', reason: `key.json parse failed: ${String(e?.message || e).slice(0, 80)}` };
345
+ }
346
+ // 1. 发现网关
347
+ let gateway = '';
348
+ try {
349
+ const ctl = AbortSignal.timeout(timeoutMs);
350
+ const gwResp = await fetch(`https://${aid}/.well-known/aun-gateway`, { redirect: 'follow', signal: ctl });
351
+ if (gwResp.ok) {
352
+ const text = (await gwResp.text()).trim();
353
+ try {
354
+ const parsed = JSON.parse(text);
355
+ gateway = parsed?.gateways?.[0]?.url || text;
356
+ }
357
+ catch {
358
+ gateway = text;
359
+ }
360
+ }
361
+ }
362
+ catch (e) {
363
+ return { kind: 'no-server-record', reason: `gateway discovery failed: ${String(e?.message || e).slice(0, 80)}` };
364
+ }
365
+ if (!gateway)
366
+ return { kind: 'no-server-record', reason: 'no gateway for AID' };
367
+ // 2. 拉云端 cert
368
+ let certPem = '';
369
+ try {
370
+ const parsed = new URL(gateway);
371
+ const scheme = parsed.protocol === 'wss:' ? 'https:' : 'http:';
372
+ const certUrl = `${scheme}//${parsed.host}/pki/cert/${encodeURIComponent(aid)}`;
373
+ const ctl = AbortSignal.timeout(timeoutMs);
374
+ const resp = await fetch(certUrl, { redirect: 'follow', signal: ctl });
375
+ if (!resp.ok) {
376
+ return { kind: 'no-server-record', reason: `pki/cert HTTP ${resp.status}` };
377
+ }
378
+ certPem = (await resp.text()).trim();
379
+ if (!certPem.includes('BEGIN CERTIFICATE')) {
380
+ return { kind: 'no-server-record', reason: 'pki/cert returned non-cert content' };
381
+ }
382
+ }
383
+ catch (e) {
384
+ return { kind: 'no-server-record', reason: `pki/cert fetch failed: ${String(e?.message || e).slice(0, 80)}` };
385
+ }
386
+ // 3. 比对公钥
387
+ try {
388
+ const x509 = new crypto.X509Certificate(certPem);
389
+ const certPubDer = x509.publicKey.export({ type: 'spki', format: 'der' });
390
+ const localPubDer = Buffer.from(localPubB64, 'base64');
391
+ if (certPubDer.equals(localPubDer)) {
392
+ return { kind: 'recoverable', serverCertPubB64: certPubDer.toString('base64') };
393
+ }
394
+ return {
395
+ kind: 'unrecoverable',
396
+ reason: 'server has different public key registered, current local private key cannot be used',
397
+ };
398
+ }
399
+ catch (e) {
400
+ return { kind: 'unknown', reason: `cert parse failed: ${String(e?.message || e).slice(0, 80)}` };
401
+ }
402
+ }
117
403
  // ==================== Lookup ====================
118
404
  export async function aidLookup(aid) {
119
405
  let gateway = '';
@@ -1,3 +1,3 @@
1
- export { isValidAid, aidList, aidCreate, aidShow, aidDelete, aidLookup, appendAidLifecycle, readAidLifecycle } from './identity.js';
2
- export { buildInitialAgentMd, agentmdGet, agentmdPut } from './agentmd.js';
1
+ export { isValidAid, aidList, aidListVerified, aidCreate, aidShow, aidDelete, aidLookup, verifySignAbility, probePkiRecoverability, appendAidLifecycle, readAidLifecycle } from './identity.js';
2
+ export { buildInitialAgentMd, agentmdGet, agentmdPut, agentmdSync } from './agentmd.js';
3
3
  export { MIN_AUN_CORE_SDK, AUN_CORE_SDK_PKG, isAunSdkVersionOk, resolveAunCoreSdkPkg, ensureAunSdk, isAunSdkReady, downloadCaRoot, getAunClient, suppressSdkLogs, } from './client.js';
@@ -1,15 +1,13 @@
1
- import fs from 'fs';
2
- import path from 'path';
3
- import os from 'os';
1
+ import { aunPath as defaultAunPath } from '../../paths.js';
2
+ import { createAunClient } from '../aid/client.js';
3
+ import { loadProcessConfig } from '../../config-store.js';
4
4
  export async function createShortConnection(aid, opts) {
5
- const aunPath = opts?.aunPath ?? path.join(os.homedir(), '.aun');
5
+ const aunPath = opts?.aunPath ?? defaultAunPath();
6
6
  const slotId = opts?.slotId ?? '';
7
- const caCertPath = path.join(aunPath, 'CA', 'root', 'root.crt');
8
- const { AUNClient } = await import('@agentunion/fastaun');
9
- const clientOpts = { aun_path: aunPath, debug: false };
10
- if (fs.existsSync(caCertPath))
11
- clientOpts.root_ca_path = caCertPath;
12
- const client = new AUNClient(clientOpts);
7
+ const encryptionSeed = loadProcessConfig().aun?.encryptionSeed
8
+ || process.env.AUN_ENCRYPTION_SEED
9
+ || 'evol';
10
+ const client = await createAunClient({ aunPath, encryptionSeed });
13
11
  await client.auth.createAid({ aid });
14
12
  const authResult = await client.auth.authenticate({ aid });
15
13
  const accessToken = authResult?.access_token ?? client._access_token;