@agentunion/fastaun-browser 0.4.13 → 0.5.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.
Files changed (175) hide show
  1. package/CHANGELOG.md +480 -386
  2. package/_packed_docs/CHANGELOG.md +480 -386
  3. package/_packed_docs/INDEX.md +111 -53
  4. package/_packed_docs/KITE_DOCS_GUIDE.md +43 -29
  5. package/_packed_docs/agent.md/SCHEMA.md +2 -0
  6. package/_packed_docs/agent.md/examples/codeagent-claudecode.md +2 -0
  7. package/_packed_docs/agent.md/examples/human-developer.md +2 -0
  8. package/_packed_docs/agent.md/examples/openclaw-lobster.md +2 -0
  9. package/_packed_docs/agent.md/examples/signed-openclaw-lobster.md +2 -0
  10. package/_packed_docs/agent.md//350/277/234/347/250/213agent.md/347/274/223/345/255/230/344/270/216etag/351/200/217/344/274/240/346/226/271/346/241/210.md +2 -0
  11. package/_packed_docs/cli/AUN-CLI/350/256/276/350/256/241/346/226/207/346/241/243.md +265 -686
  12. package/_packed_docs/collab-gateway-boundary-test-report.md +164 -0
  13. package/_packed_docs/design/2026-05-22-aun-rpc-trace-enhancement.md +2 -0
  14. package/_packed_docs/design/AUNClient/346/213/206/345/210/206/351/207/215/346/236/204/346/211/247/350/241/214/346/226/271/346/241/210.md +2 -0
  15. package/_packed_docs/design/E2EE_V2/347/256/200/345/214/226/344/270/2721DH/345/212/240Per-AID_Wrap/346/226/271/346/241/210.md +2 -0
  16. package/_packed_docs/design//350/267/250/350/257/255/350/250/200/345/256/271/345/231/250E2E/346/265/213/350/257/225/346/226/271/346/241/210.md +2 -0
  17. package/_packed_docs/protocol/00-/346/200/273/350/247/210/344/270/216/345/210/206/345/261/202.md +2 -0
  18. package/_packed_docs/protocol/00A-/350/256/276/350/256/241/345/216/237/345/210/231-/344/270/272Agent/350/200/214/347/224/237.md +2 -0
  19. package/_packed_docs/protocol/01-/350/272/253/344/273/275/344/270/216/345/207/255/350/257/201/345/215/217/350/256/256-auth.md +2 -0
  20. package/_packed_docs/protocol/02-/350/257/201/344/271/246/344/270/216/344/277/241/344/273/273/344/275/223/347/263/273.md +2 -0
  21. package/_packed_docs/protocol/03-Gateway-/350/277/236/346/216/245/346/250/241/345/274/217.md +2 -0
  22. package/_packed_docs/protocol/04-Peer-/345/255/220/345/215/217/350/256/256.md +2 -0
  23. package/_packed_docs/protocol/05-Relay-/345/255/220/345/215/217/350/256/256.md +2 -0
  24. package/_packed_docs/protocol/07-/351/224/231/350/257/257/347/240/201/344/270/216/347/212/266/346/200/201/346/234/272.md +2 -0
  25. package/_packed_docs/protocol/08-AUN-E2EE-Group.md +2 -0
  26. package/_packed_docs/protocol/08-AUN-E2EE.md +2 -0
  27. package/_packed_docs/protocol/09-/345/256/211/345/205/250/350/200/203/350/231/221.md +2 -0
  28. package/_packed_docs/protocol/10-Group-/345/255/220/345/215/217/350/256/256.md +28 -85
  29. package/_packed_docs/protocol/11-Storage-/345/255/220/345/215/217/350/256/256.md +49 -0
  30. package/_packed_docs/protocol/12-Stream-/345/255/220/345/215/217/350/256/256.md +2 -0
  31. package/_packed_docs/protocol/13-Agent/350/241/214/344/270/272/350/247/204/350/214/203.md +2 -0
  32. package/_packed_docs/protocol/14-/344/272/244/344/272/222/346/234/272/345/210/266-/345/223/215/345/272/224/346/250/241/345/274/217/344/270/216/350/207/252/344/270/273/346/250/241/345/274/217.md +2 -0
  33. package/_packed_docs/protocol/15-/347/246/273/347/272/277/346/216/250/351/200/201/351/200/232/347/237/245/345/215/217/350/256/256.md +2 -0
  34. package/_packed_docs/protocol/README.md +2 -0
  35. package/_packed_docs/protocol/agent.md/SCHEMA.md +2 -0
  36. package/_packed_docs/protocol/agent.md/examples/codeagent-claudecode.md +2 -0
  37. package/_packed_docs/protocol/agent.md/examples/human-developer.md +2 -0
  38. package/_packed_docs/protocol/agent.md/examples/openclaw-lobster.md +2 -0
  39. package/_packed_docs/protocol/aun-docs-guide.md +2 -0
  40. package/_packed_docs/protocol/index.md +3 -1
  41. package/_packed_docs/protocol//350/215/211/346/241/210-agent.md/347/255/276/345/220/215/345/215/217/350/256/256.md +2 -0
  42. package/_packed_docs/protocol//350/215/211/346/241/210-/346/213/222/347/273/235/344/277/241/345/217/267/345/215/217/350/256/256.md +2 -0
  43. package/_packed_docs/protocol//351/231/204/345/275/225A-/346/234/257/350/257/255/350/241/250.md +2 -0
  44. package/_packed_docs/protocol//351/231/204/345/275/225B-/346/211/251/345/261/225/346/200/247/346/214/207/345/215/227.md +2 -0
  45. package/_packed_docs/protocol//351/231/204/345/275/225C-/347/247/201/351/222/245/347/256/241/347/220/206/344/270/216/350/272/253/344/273/275/346/201/242/345/244/215.md +2 -0
  46. package/_packed_docs/protocol//351/231/204/345/275/225D-Root_CA_/346/262/273/347/220/206/346/234/272/345/210/266.md +2 -0
  47. package/_packed_docs/protocol//351/231/204/345/275/225E-Root_CA_/345/207/206/345/205/245/346/265/201/347/250/213.md +2 -0
  48. package/_packed_docs/protocol//351/231/204/345/275/225F-Issuer_CA_/347/224/263/350/257/267/346/265/201/347/250/213.md +2 -0
  49. package/_packed_docs/protocol//351/231/204/345/275/225G-AID_/345/255/244/345/204/277/351/242/204/351/230/262/344/270/216/346/225/221/346/217/264/346/234/272/345/210/266.md +2 -0
  50. package/_packed_docs/protocol//351/231/204/345/275/225H-Identity/346/234/215/345/212/241/345/256/236/347/216/260/346/214/207/345/215/227.md +2 -0
  51. package/_packed_docs/protocol//351/231/204/345/275/225I-/350/267/250/345/237/237/346/266/210/346/201/257/350/267/257/347/224/261/345/256/236/347/216/260/346/214/207/345/215/227.md +2 -0
  52. package/_packed_docs/protocol//351/231/204/345/275/225J-/345/256/242/346/210/267/347/253/257/346/216/245/345/205/245/347/244/272/344/276/213.md +2 -0
  53. package/_packed_docs/protocol//351/231/204/345/275/225K-Agent_Web/345/217/221/347/216/260/345/215/217/350/256/256.md +2 -0
  54. package/_packed_docs/protocol//351/231/204/345/275/225L-E2EE/345/256/236/347/216/260/346/214/207/345/215/227.md +2 -0
  55. package/_packed_docs/protocol//351/231/204/345/275/225M-JWT/350/256/244/350/257/201/345/256/236/347/216/260/346/214/207/345/215/227.md +2 -0
  56. package/_packed_docs/protocol//351/231/204/345/275/225N-/345/210/206/345/270/203/345/274/217Trace/345/215/217/350/256/256.md +2 -0
  57. package/_packed_docs/sdk/01-/345/277/253/351/200/237/345/274/200/345/247/213.md +2 -0
  58. package/_packed_docs/sdk/02-WebSocket/345/215/217/350/256/256.md +2 -0
  59. package/_packed_docs/sdk/03-/346/240/270/345/277/203/346/246/202/345/277/265.md +2 -0
  60. package/_packed_docs/sdk/04-/350/277/236/346/216/245/344/270/216/350/256/244/350/257/201.md +2 -0
  61. package/_packed_docs/sdk/05-E2EE/345/212/240/345/257/206/351/200/232/344/277/241.md +2 -0
  62. package/_packed_docs/sdk/06-API/346/211/213/345/206/214.md +283 -267
  63. package/_packed_docs/sdk/07-/351/224/231/350/257/257/345/244/204/347/220/206.md +4 -1
  64. package/_packed_docs/sdk/08-/346/234/200/344/275/263/345/256/236/350/267/265.md +2 -0
  65. package/_packed_docs/sdk/09-collab-rpc-manual.md +550 -0
  66. package/_packed_docs/sdk/09-custody-api-manual.md +2 -0
  67. package/_packed_docs/sdk/09-group-rpc-manual.md +197 -443
  68. package/_packed_docs/sdk/09-message-rpc-manual.md +2 -0
  69. package/_packed_docs/sdk/09-meta-rpc-manual.md +2 -0
  70. package/_packed_docs/sdk/09-payload-reference.md +2 -0
  71. package/_packed_docs/sdk/09-proxy-rpc-manual.md +2 -0
  72. package/_packed_docs/sdk/09-storage-rpc-manual.md +952 -279
  73. package/_packed_docs/sdk/09-stream-rpc-manual.md +2 -0
  74. package/_packed_docs/sdk/AUN_DOCS_GUIDE.md +33 -20
  75. package/_packed_docs/sdk/E2EE_V2/346/266/210/346/201/257/351/200/232/344/277/241/346/227/266/345/272/217/345/233/276.md +2 -0
  76. package/_packed_docs/sdk/INDEX.md +46 -29
  77. package/_packed_docs/sdk/Notify/351/200/232/347/237/245/346/226/271/346/241/210.md +2 -0
  78. package/_packed_docs/sdk/README.md +17 -15
  79. package/dist/aid-store.d.ts +25 -0
  80. package/dist/aid-store.d.ts.map +1 -1
  81. package/dist/aid-store.js +89 -8
  82. package/dist/aid-store.js.map +1 -1
  83. package/dist/bundle.js +19128 -16784
  84. package/dist/client/delivery.d.ts +2 -0
  85. package/dist/client/delivery.d.ts.map +1 -1
  86. package/dist/client/delivery.js +52 -8
  87. package/dist/client/delivery.js.map +1 -1
  88. package/dist/client/rpc-pipeline.d.ts +1 -0
  89. package/dist/client/rpc-pipeline.d.ts.map +1 -1
  90. package/dist/client/rpc-pipeline.js +70 -33
  91. package/dist/client/rpc-pipeline.js.map +1 -1
  92. package/dist/client.d.ts +31 -0
  93. package/dist/client.d.ts.map +1 -1
  94. package/dist/client.js +282 -20
  95. package/dist/client.js.map +1 -1
  96. package/dist/collab/client.d.ts +39 -0
  97. package/dist/collab/client.d.ts.map +1 -0
  98. package/dist/collab/client.js +124 -0
  99. package/dist/collab/client.js.map +1 -0
  100. package/dist/collab/errors.d.ts +16 -0
  101. package/dist/collab/errors.d.ts.map +1 -0
  102. package/dist/collab/errors.js +60 -0
  103. package/dist/collab/errors.js.map +1 -0
  104. package/dist/collab/index.d.ts +4 -0
  105. package/dist/collab/index.d.ts.map +1 -0
  106. package/dist/collab/index.js +4 -0
  107. package/dist/collab/index.js.map +1 -0
  108. package/dist/collab/types.d.ts +111 -0
  109. package/dist/collab/types.d.ts.map +1 -0
  110. package/dist/collab/types.js +2 -0
  111. package/dist/collab/types.js.map +1 -0
  112. package/dist/crypto.d.ts +6 -0
  113. package/dist/crypto.d.ts.map +1 -1
  114. package/dist/crypto.js +6 -0
  115. package/dist/crypto.js.map +1 -1
  116. package/dist/errors.d.ts.map +1 -1
  117. package/dist/errors.js +16 -1
  118. package/dist/errors.js.map +1 -1
  119. package/dist/facades.d.ts +89 -0
  120. package/dist/facades.d.ts.map +1 -0
  121. package/dist/facades.js +228 -0
  122. package/dist/facades.js.map +1 -0
  123. package/dist/group-fs.d.ts +103 -0
  124. package/dist/group-fs.d.ts.map +1 -0
  125. package/dist/group-fs.js +496 -0
  126. package/dist/group-fs.js.map +1 -0
  127. package/dist/group-resources.d.ts +98 -0
  128. package/dist/group-resources.d.ts.map +1 -0
  129. package/dist/group-resources.js +635 -0
  130. package/dist/group-resources.js.map +1 -0
  131. package/dist/index.d.ts +4 -1
  132. package/dist/index.d.ts.map +1 -1
  133. package/dist/index.js +6 -0
  134. package/dist/index.js.map +1 -1
  135. package/dist/register-flow.d.ts +1 -0
  136. package/dist/register-flow.d.ts.map +1 -1
  137. package/dist/register-flow.js +3 -0
  138. package/dist/register-flow.js.map +1 -1
  139. package/dist/service-proxy.d.ts +1 -0
  140. package/dist/service-proxy.d.ts.map +1 -1
  141. package/dist/service-proxy.js +15 -3
  142. package/dist/service-proxy.js.map +1 -1
  143. package/dist/storage/errors.d.ts +28 -0
  144. package/dist/storage/errors.d.ts.map +1 -0
  145. package/dist/storage/errors.js +65 -0
  146. package/dist/storage/errors.js.map +1 -0
  147. package/dist/storage/index.d.ts +2 -0
  148. package/dist/storage/index.d.ts.map +1 -0
  149. package/dist/storage/index.js +2 -0
  150. package/dist/storage/index.js.map +1 -0
  151. package/dist/storage/lowlevel.d.ts +314 -0
  152. package/dist/storage/lowlevel.d.ts.map +1 -0
  153. package/dist/storage/lowlevel.js +460 -0
  154. package/dist/storage/lowlevel.js.map +1 -0
  155. package/dist/storage/types.d.ts +59 -0
  156. package/dist/storage/types.d.ts.map +1 -0
  157. package/dist/storage/types.js +113 -0
  158. package/dist/storage/types.js.map +1 -0
  159. package/dist/storage/vfs.d.ts +144 -0
  160. package/dist/storage/vfs.d.ts.map +1 -0
  161. package/dist/storage/vfs.js +403 -0
  162. package/dist/storage/vfs.js.map +1 -0
  163. package/dist/tools/cross-sdk-agent.d.ts +3 -0
  164. package/dist/tools/cross-sdk-agent.d.ts.map +1 -0
  165. package/dist/tools/cross-sdk-agent.js +1089 -0
  166. package/dist/tools/cross-sdk-agent.js.map +1 -0
  167. package/dist/transport.d.ts +3 -0
  168. package/dist/transport.d.ts.map +1 -1
  169. package/dist/transport.js +33 -13
  170. package/dist/transport.js.map +1 -1
  171. package/dist/version.d.ts +1 -1
  172. package/dist/version.d.ts.map +1 -1
  173. package/dist/version.js +1 -1
  174. package/dist/version.js.map +1 -1
  175. package/package.json +44 -44
@@ -0,0 +1,1089 @@
1
+ #!/usr/bin/env node
2
+ import 'fake-indexeddb/auto';
3
+ import * as http from 'node:http';
4
+ import * as https from 'node:https';
5
+ import * as crypto from 'node:crypto';
6
+ import * as fs from 'node:fs';
7
+ import { URL } from 'node:url';
8
+ import { AIDStore, AUNClient } from '../index.js';
9
+ import { publicKeyFingerprint } from '../cert-utils.js';
10
+ function installNodeBrowserShims() {
11
+ const root = globalThis;
12
+ if (!root.WebSocket)
13
+ throw new Error('Node.js 22+ global WebSocket is required for the JS cross-sdk agent');
14
+ if (!root.localStorage) {
15
+ const data = new Map();
16
+ root.localStorage = {
17
+ getItem: (key) => data.get(String(key)) ?? null,
18
+ setItem: (key, value) => { data.set(String(key), String(value)); },
19
+ removeItem: (key) => { data.delete(String(key)); },
20
+ clear: () => { data.clear(); },
21
+ key: (index) => [...data.keys()][index] ?? null,
22
+ get length() { return data.size; },
23
+ };
24
+ }
25
+ }
26
+ function envBool(name, fallback = false) {
27
+ const raw = process.env[name];
28
+ if (raw == null)
29
+ return fallback;
30
+ return ['1', 'true', 'yes', 'on'].includes(raw.trim().toLowerCase());
31
+ }
32
+ function textOf(value) {
33
+ return String(value ?? '');
34
+ }
35
+ function jsonObjectOf(value) {
36
+ return value && typeof value === 'object' && !Array.isArray(value) ? value : null;
37
+ }
38
+ function accessTokenFromClient(client) {
39
+ const internals = client;
40
+ const direct = textOf(internals.accessToken ?? internals.access_token).trim();
41
+ if (direct)
42
+ return direct;
43
+ const identityToken = textOf(internals._identity?.access_token).trim();
44
+ if (identityToken)
45
+ return identityToken;
46
+ return textOf(internals._sessionParams?.access_token).trim();
47
+ }
48
+ function jsonSafe(value) {
49
+ try {
50
+ JSON.stringify(value);
51
+ return value;
52
+ }
53
+ catch {
54
+ if (Array.isArray(value))
55
+ return value.map((item) => jsonSafe(item));
56
+ if (value && typeof value === 'object') {
57
+ const out = {};
58
+ for (const [key, item] of Object.entries(value))
59
+ out[key] = jsonSafe(item);
60
+ return out;
61
+ }
62
+ return String(value);
63
+ }
64
+ }
65
+ function stableStringify(value) {
66
+ if (value === null || value === undefined)
67
+ return 'null';
68
+ if (typeof value === 'string')
69
+ return JSON.stringify(value);
70
+ if (typeof value === 'number' || typeof value === 'boolean')
71
+ return JSON.stringify(value);
72
+ if (Array.isArray(value))
73
+ return `[${value.map((item) => stableStringify(item)).join(',')}]`;
74
+ if (typeof value === 'object') {
75
+ const obj = value;
76
+ return `{${Object.keys(obj).sort().map((key) => `${stableStringify(key)}:${stableStringify(obj[key])}`).join(',')}}`;
77
+ }
78
+ return JSON.stringify(String(value));
79
+ }
80
+ function sha256Json(value) {
81
+ return crypto.createHash('sha256').update(stableStringify(jsonSafe(value)), 'utf-8').digest('hex');
82
+ }
83
+ async function readJson(req) {
84
+ const chunks = [];
85
+ for await (const chunk of req) {
86
+ chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
87
+ }
88
+ if (!chunks.length)
89
+ return {};
90
+ try {
91
+ const parsed = JSON.parse(Buffer.concat(chunks).toString('utf-8'));
92
+ return parsed && typeof parsed === 'object' && !Array.isArray(parsed) ? parsed : {};
93
+ }
94
+ catch {
95
+ return {};
96
+ }
97
+ }
98
+ function sendJson(res, status, data) {
99
+ const body = JSON.stringify(jsonSafe(data));
100
+ res.writeHead(status, {
101
+ 'content-type': 'application/json; charset=utf-8',
102
+ 'content-length': Buffer.byteLength(body),
103
+ });
104
+ res.end(body);
105
+ }
106
+ function envelopeMetadata(data) {
107
+ const e2ee = jsonObjectOf(data.e2ee);
108
+ const protectedHeaders = jsonObjectOf(data.protected_headers) ?? jsonObjectOf(e2ee?.protected_headers);
109
+ const payloadType = textOf(data.payload_type ?? protectedHeaders?.payload_type ?? e2ee?.payload_type).trim();
110
+ const out = {};
111
+ if (payloadType)
112
+ out.payload_type = payloadType;
113
+ if (protectedHeaders)
114
+ out.protected_headers = jsonSafe(protectedHeaders);
115
+ return out;
116
+ }
117
+ class CrossSdkJsAgent {
118
+ language = 'js';
119
+ sdkVersion = 'unknown';
120
+ aid = textOf(process.env.AUN_TEST_AID || `cross-js-${crypto.randomUUID().slice(0, 8)}.agentid.pub`).trim();
121
+ issuer = textOf(process.env.AUN_TEST_ISSUER || 'agentid.pub').trim() || 'agentid.pub';
122
+ slotId = textOf(process.env.AUN_TEST_SLOT_ID || `cross-sdk-js-${crypto.randomUUID().slice(0, 8)}`).trim();
123
+ aunPath = textOf(process.env.AUN_TEST_AUN_PATH || process.env.AUN_DATA_ROOT || '/data/aun').trim();
124
+ debug = envBool('AUN_TEST_DEBUG', false);
125
+ client;
126
+ ready = false;
127
+ startupError = '';
128
+ inbox = [];
129
+ groupInbox = [];
130
+ traces = new Map();
131
+ sendResults = new Map();
132
+ constructor() {
133
+ this.client = new AUNClient();
134
+ const internal = this.client;
135
+ if (internal._configModel)
136
+ internal._configModel.requireForwardSecrecy = false;
137
+ }
138
+ async start() {
139
+ this.client.on('message.received', (msg) => {
140
+ void this.storeInboxItem(this.normalizeMessage(msg, true));
141
+ });
142
+ this.client.on('message.undecryptable', (msg) => {
143
+ void this.storeInboxItem(this.normalizeMessage(msg, false, 'undecryptable'));
144
+ });
145
+ this.client.on('group.message_created', (msg) => {
146
+ void this.storeGroupInboxItem(this.normalizeGroupMessage(msg, true));
147
+ });
148
+ this.client.on('group.message_undecryptable', (msg) => {
149
+ void this.storeGroupInboxItem(this.normalizeGroupMessage(msg, false, 'undecryptable'));
150
+ });
151
+ await this.ensureConnected();
152
+ this.ready = true;
153
+ }
154
+ async close() {
155
+ try {
156
+ await this.client.close();
157
+ }
158
+ catch {
159
+ // 退出清理失败不影响容器关闭。
160
+ }
161
+ }
162
+ aidStore() {
163
+ return new AIDStore({
164
+ aunPath: this.aunPath,
165
+ encryptionSeed: '',
166
+ slotId: this.slotId,
167
+ verifySsl: false,
168
+ });
169
+ }
170
+ async ensureConnected() {
171
+ const store = this.aidStore();
172
+ try {
173
+ const registered = await store.register(this.aid);
174
+ if (!registered.ok) {
175
+ const loaded = await store.load(this.aid);
176
+ if (!loaded.ok) {
177
+ throw new Error(`${registered.error.code}: ${registered.error.message}`);
178
+ }
179
+ }
180
+ const loaded = await store.load(this.aid);
181
+ if (!loaded.ok) {
182
+ throw new Error(`load identity failed: ${loaded.error.code}: ${loaded.error.message}`);
183
+ }
184
+ this.client.loadIdentity(loaded.data.aid);
185
+ }
186
+ finally {
187
+ store.close();
188
+ }
189
+ await this.client.connect({ auto_reconnect: false, background_sync: true });
190
+ }
191
+ async identity() {
192
+ const internal = this.client;
193
+ const loaded = await internal._auth?.loadIdentityOrNone?.(this.aid);
194
+ const identity = internal._identity ?? loaded ?? {};
195
+ const cert = textOf(identity.cert ?? identity.cert_pem ?? '');
196
+ return {
197
+ aid: this.aid,
198
+ device_id: textOf(internal._deviceId ?? ''),
199
+ slot_id: textOf(internal._slotId ?? this.slotId),
200
+ issuer: this.issuer,
201
+ public_key_fingerprint: cert ? await publicKeyFingerprint(cert) : '',
202
+ };
203
+ }
204
+ recordTrace(traceId, item) {
205
+ if (!traceId)
206
+ return;
207
+ const current = this.traces.get(traceId) ?? [];
208
+ current.push({
209
+ ts: Date.now(),
210
+ language: this.language,
211
+ aid: this.aid,
212
+ ...item,
213
+ });
214
+ this.traces.set(traceId, current);
215
+ }
216
+ async storeInboxItem(item) {
217
+ this.inbox.push(item);
218
+ if (this.inbox.length > 1000)
219
+ this.inbox = this.inbox.slice(-1000);
220
+ this.recordTrace(textOf(item.trace_id), { stage: 'receive', message: item });
221
+ }
222
+ async storeGroupInboxItem(item) {
223
+ this.groupInbox.push(item);
224
+ if (this.groupInbox.length > 1000)
225
+ this.groupInbox = this.groupInbox.slice(-1000);
226
+ this.recordTrace(textOf(item.trace_id), { stage: 'group_receive', message: item });
227
+ }
228
+ normalizeMessage(msg, decrypted, errorCode = '') {
229
+ const data = jsonObjectOf(msg) ?? { raw: textOf(msg) };
230
+ const payload = jsonObjectOf(data.payload) ?? {};
231
+ const traceId = textOf(payload.trace_id ?? data.trace_id ?? '');
232
+ const text = textOf(payload.text ?? data.text ?? '');
233
+ return {
234
+ trace_id: traceId,
235
+ message_id: textOf(data.message_id ?? data.id ?? ''),
236
+ from: textOf(data.from ?? data.from_aid ?? ''),
237
+ to: textOf(data.to ?? data.to_aid ?? this.aid),
238
+ text,
239
+ decrypted,
240
+ encrypted: Boolean(data.e2ee ?? data.encrypted),
241
+ seq: Number(data.seq ?? data.message_seq ?? 0) || 0,
242
+ ack_seq: Number(data.ack_seq ?? 0) || 0,
243
+ error_code: errorCode,
244
+ raw_sha256: sha256Json(data),
245
+ ...envelopeMetadata(data),
246
+ };
247
+ }
248
+ normalizeGroupMessage(msg, decrypted, errorCode = '') {
249
+ const data = jsonObjectOf(msg) ?? { raw: textOf(msg) };
250
+ const payload = jsonObjectOf(data.payload) ?? {};
251
+ const traceId = textOf(payload.trace_id ?? data.trace_id ?? '');
252
+ const text = textOf(payload.text ?? data.text ?? '');
253
+ return {
254
+ trace_id: traceId,
255
+ group_id: textOf(data.group_id ?? ''),
256
+ message_id: textOf(data.message_id ?? data.id ?? ''),
257
+ from: textOf(data.from ?? data.from_aid ?? data.sender_aid ?? ''),
258
+ text,
259
+ decrypted,
260
+ encrypted: Boolean(data.e2ee ?? data.encrypted),
261
+ seq: Number(data.seq ?? data.message_seq ?? data.msg_seq ?? 0) || 0,
262
+ error_code: errorCode,
263
+ raw_sha256: sha256Json(data),
264
+ ...envelopeMetadata(data),
265
+ };
266
+ }
267
+ async handle(req, res) {
268
+ const url = new URL(req.url ?? '/', `http://${req.headers.host ?? 'localhost'}`);
269
+ try {
270
+ if (req.method === 'GET' && url.pathname === '/health') {
271
+ sendJson(res, this.startupError ? 503 : 200, {
272
+ ok: !this.startupError,
273
+ agent_ready: this.ready && ['ready', 'connected'].includes(textOf(this.client.state)),
274
+ state: textOf(this.client.state),
275
+ aid: this.aid,
276
+ language: this.language,
277
+ sdk_version: this.sdkVersion,
278
+ startup_error: this.startupError,
279
+ });
280
+ return;
281
+ }
282
+ if (req.method === 'POST' && url.pathname === '/reset') {
283
+ const body = await readJson(req);
284
+ const traceId = textOf(body.trace_id ?? '');
285
+ if (traceId) {
286
+ this.inbox = this.inbox.filter((item) => item.trace_id !== traceId);
287
+ this.groupInbox = this.groupInbox.filter((item) => item.trace_id !== traceId);
288
+ this.traces.delete(traceId);
289
+ this.sendResults.delete(traceId);
290
+ }
291
+ else {
292
+ this.inbox = [];
293
+ this.groupInbox = [];
294
+ this.traces.clear();
295
+ this.sendResults.clear();
296
+ }
297
+ sendJson(res, 200, { ok: true });
298
+ return;
299
+ }
300
+ if (req.method === 'GET' && url.pathname === '/identity') {
301
+ sendJson(res, 200, await this.identity());
302
+ return;
303
+ }
304
+ if (req.method === 'POST' && url.pathname === '/send') {
305
+ await this.send(req, res);
306
+ return;
307
+ }
308
+ if (req.method === 'POST' && url.pathname === '/ack') {
309
+ await this.ack(req, res);
310
+ return;
311
+ }
312
+ if (req.method === 'POST' && url.pathname === '/pull') {
313
+ await this.pull(req, res);
314
+ return;
315
+ }
316
+ if (req.method === 'POST' && url.pathname === '/group/create') {
317
+ await this.groupCreate(req, res);
318
+ return;
319
+ }
320
+ if (req.method === 'POST' && url.pathname === '/group/call') {
321
+ await this.groupCall(req, res);
322
+ return;
323
+ }
324
+ if (req.method === 'GET' && url.pathname === '/group/ready') {
325
+ await this.groupReady(url, res);
326
+ return;
327
+ }
328
+ if (req.method === 'POST' && url.pathname === '/group/send') {
329
+ await this.groupSend(req, res);
330
+ return;
331
+ }
332
+ if (req.method === 'POST' && url.pathname === '/group/pull') {
333
+ await this.groupPull(req, res);
334
+ return;
335
+ }
336
+ if (req.method === 'POST' && url.pathname === '/group/ack') {
337
+ await this.groupAck(req, res);
338
+ return;
339
+ }
340
+ if (req.method === 'POST' && url.pathname === '/group/fs/call') {
341
+ await this.groupFsCall(req, res);
342
+ return;
343
+ }
344
+ if (req.method === 'POST' && url.pathname === '/collab/call') {
345
+ await this.collabCall(req, res);
346
+ return;
347
+ }
348
+ if (req.method === 'POST' && url.pathname === '/storage/call') {
349
+ await this.storageCall(req, res);
350
+ return;
351
+ }
352
+ if (req.method === 'GET' && url.pathname === '/inbox') {
353
+ const traceId = textOf(url.searchParams.get('trace_id') ?? '');
354
+ const fromAid = textOf(url.searchParams.get('from') ?? '');
355
+ const limit = Number(url.searchParams.get('limit') ?? 20) || 20;
356
+ let items = [...this.inbox];
357
+ if (traceId)
358
+ items = items.filter((item) => item.trace_id === traceId);
359
+ if (fromAid)
360
+ items = items.filter((item) => item.from === fromAid);
361
+ sendJson(res, 200, { received: items.length > 0, items: items.slice(-limit) });
362
+ return;
363
+ }
364
+ if (req.method === 'GET' && url.pathname === '/group/inbox') {
365
+ const traceId = textOf(url.searchParams.get('trace_id') ?? '');
366
+ const groupId = textOf(url.searchParams.get('group_id') ?? '');
367
+ const fromAid = textOf(url.searchParams.get('from') ?? '');
368
+ const limit = Number(url.searchParams.get('limit') ?? 20) || 20;
369
+ let items = [...this.groupInbox];
370
+ if (traceId)
371
+ items = items.filter((item) => item.trace_id === traceId);
372
+ if (groupId)
373
+ items = items.filter((item) => item.group_id === groupId);
374
+ if (fromAid)
375
+ items = items.filter((item) => item.from === fromAid);
376
+ sendJson(res, 200, { received: items.length > 0, items: items.slice(-limit) });
377
+ return;
378
+ }
379
+ if (req.method === 'GET' && url.pathname.startsWith('/traces/')) {
380
+ const traceId = decodeURIComponent(url.pathname.slice('/traces/'.length));
381
+ sendJson(res, 200, { trace_id: traceId, items: this.traces.get(traceId) ?? [] });
382
+ return;
383
+ }
384
+ if (req.method === 'GET' && url.pathname === '/logs') {
385
+ const logDir = textOf(process.env.AUN_LOG_DIR || '/root/.aun/logs');
386
+ let files = [];
387
+ try {
388
+ files = fs.existsSync(logDir)
389
+ ? fs.readdirSync(logDir, { recursive: true }).map((p) => `${logDir}/${String(p)}`).slice(-20)
390
+ : [];
391
+ }
392
+ catch {
393
+ files = [];
394
+ }
395
+ sendJson(res, 200, { log_files: files, tail: [] });
396
+ return;
397
+ }
398
+ sendJson(res, 404, { ok: false, error_code: 'not_found', error_message: url.pathname });
399
+ }
400
+ catch (err) {
401
+ sendJson(res, 500, {
402
+ ok: false,
403
+ error_code: err instanceof Error ? err.name : 'Error',
404
+ error_message: err instanceof Error ? err.message : String(err),
405
+ });
406
+ }
407
+ }
408
+ async collabCall(req, res) {
409
+ const body = await readJson(req);
410
+ const traceId = textOf(body.trace_id || crypto.randomUUID().replace(/-/g, ''));
411
+ const action = textOf(body.action).trim();
412
+ const params = jsonObjectOf(body.params) ?? {};
413
+ try {
414
+ const result = await this.callCollabAction(action, params);
415
+ const response = { ok: true, trace_id: traceId, action, result: jsonSafe(result) };
416
+ this.recordTrace(traceId, { stage: 'collab_call', action, result: response });
417
+ sendJson(res, 200, response);
418
+ }
419
+ catch (err) {
420
+ const error = {
421
+ ok: false,
422
+ trace_id: traceId,
423
+ action,
424
+ error_code: err instanceof Error ? err.name : 'Error',
425
+ error_message: err instanceof Error ? err.message : String(err),
426
+ };
427
+ this.recordTrace(traceId, { stage: 'collab_call_error', action, error });
428
+ sendJson(res, 500, error);
429
+ }
430
+ }
431
+ async groupCall(req, res) {
432
+ const body = await readJson(req);
433
+ const traceId = textOf(body.trace_id || crypto.randomUUID().replace(/-/g, ''));
434
+ let method = textOf(body.method).trim();
435
+ const action = textOf(body.action).trim();
436
+ if (!method && action)
437
+ method = action.startsWith('group.') ? action : `group.${action}`;
438
+ const params = jsonObjectOf(body.params) ?? {};
439
+ if (!method) {
440
+ sendJson(res, 400, { ok: false, trace_id: traceId, error_code: 'bad_request', error_message: 'method is required' });
441
+ return;
442
+ }
443
+ try {
444
+ const result = await this.client.call(method, params);
445
+ const response = { ok: true, trace_id: traceId, method, result: jsonSafe(result) };
446
+ this.recordTrace(traceId, { stage: 'group_call', method, result: response });
447
+ sendJson(res, 200, response);
448
+ }
449
+ catch (err) {
450
+ const error = {
451
+ ok: false,
452
+ trace_id: traceId,
453
+ method,
454
+ error_code: err instanceof Error ? err.name : 'Error',
455
+ error_message: err instanceof Error ? err.message : String(err),
456
+ };
457
+ this.recordTrace(traceId, { stage: 'group_call_error', method, error });
458
+ sendJson(res, 500, error);
459
+ }
460
+ }
461
+ async storageCall(req, res) {
462
+ const body = await readJson(req);
463
+ const traceId = textOf(body.trace_id || crypto.randomUUID().replace(/-/g, ''));
464
+ const action = textOf(body.action).trim();
465
+ const params = jsonObjectOf(body.params) ?? {};
466
+ try {
467
+ const result = await this.callStorageAction(action, params);
468
+ const response = { ok: true, trace_id: traceId, action, result: jsonSafe(result) };
469
+ this.recordTrace(traceId, { stage: 'storage_call', action, result: response });
470
+ sendJson(res, 200, response);
471
+ }
472
+ catch (err) {
473
+ const error = {
474
+ ok: false,
475
+ trace_id: traceId,
476
+ action,
477
+ error_code: err instanceof Error ? err.name : 'Error',
478
+ error_message: err instanceof Error ? err.message : String(err),
479
+ };
480
+ this.recordTrace(traceId, { stage: 'storage_call_error', action, error });
481
+ sendJson(res, 500, error);
482
+ }
483
+ }
484
+ async groupFsCall(req, res) {
485
+ const body = await readJson(req);
486
+ const traceId = textOf(body.trace_id || crypto.randomUUID().replace(/-/g, ''));
487
+ const action = textOf(body.action).trim();
488
+ const params = jsonObjectOf(body.params) ?? {};
489
+ try {
490
+ const result = await this.callGroupFsAction(action, params);
491
+ const response = { ok: true, trace_id: traceId, action, result: jsonSafe(result) };
492
+ this.recordTrace(traceId, { stage: 'group_fs_call', action, result: response });
493
+ sendJson(res, 200, response);
494
+ }
495
+ catch (err) {
496
+ const error = {
497
+ ok: false,
498
+ trace_id: traceId,
499
+ action,
500
+ error_code: err instanceof Error ? err.name : 'Error',
501
+ error_message: err instanceof Error ? err.message : String(err),
502
+ };
503
+ this.recordTrace(traceId, { stage: 'group_fs_call_error', action, error });
504
+ sendJson(res, 500, error);
505
+ }
506
+ }
507
+ async callCollabAction(action, params) {
508
+ const collabRoot = textOf(params.collab_root ?? params.collabRoot);
509
+ const doc = textOf(params.doc);
510
+ const source = textOf(params.source);
511
+ switch (action) {
512
+ case 'ls':
513
+ case 'ls-files':
514
+ return await this.client.collab.lsFiles(collabRoot);
515
+ case 'create':
516
+ return await this.client.collab.create(collabRoot, doc, source);
517
+ case 'read':
518
+ return await this.client.collab.show(collabRoot, doc);
519
+ case 'show':
520
+ return await this.client.collab.show(collabRoot, doc, params.rev == null ? undefined : Number(params.rev));
521
+ case 'submit':
522
+ return await this.client.collab.commit(collabRoot, doc, source, Number(params.onto ?? params.base_version ?? params.baseVersion ?? 0) || 0, textOf(params.message ?? ''));
523
+ case 'commit':
524
+ return await this.client.collab.commit(collabRoot, doc, source, Number(params.onto ?? params.base_version ?? params.baseVersion ?? 0) || 0, textOf(params.message ?? ''));
525
+ case 'merge':
526
+ return await this.client.collab.merge(collabRoot, doc, source, Number(params.onto ?? params.base_version ?? params.baseVersion ?? 0) || 0);
527
+ case 'history':
528
+ case 'log':
529
+ return await this.client.collab.log(collabRoot, doc);
530
+ case 'get':
531
+ return await this.client.collab.show(collabRoot, doc, Number(params.version ?? 0) || 0);
532
+ case 'diff':
533
+ return await this.client.collab.diff(collabRoot, doc, Number(params.from ?? 0) || 0, Number(params.to ?? 0) || 0);
534
+ case 'export':
535
+ return await this.client.collab.clone(collabRoot, textOf(params.dest), false);
536
+ case 'adopt':
537
+ return await this.client.collab.clone(textOf(params.src), textOf(params.new_root ?? params.newRoot), true);
538
+ case 'clone':
539
+ return await this.client.collab.clone(textOf(params.src), textOf(params.dest), Boolean(params.reroot));
540
+ case 'prune':
541
+ return await this.client.collab.prune(collabRoot, doc);
542
+ case 'ls-remote':
543
+ return await this.client.collab.lsRemote(textOf(params.group_aid ?? params.groupAid));
544
+ case 'unregister':
545
+ return await this.client.collab.unregister(textOf(params.group_aid ?? params.groupAid), collabRoot);
546
+ case 'tag.create':
547
+ return await this.client.collab.tag.create(collabRoot, {
548
+ message: textOf(params.message),
549
+ major: Boolean(params.major),
550
+ });
551
+ case 'tag.list':
552
+ return await this.client.collab.tag.list(collabRoot);
553
+ case 'tag.show':
554
+ return await this.client.collab.tag.show(collabRoot, textOf(params.version));
555
+ case 'tag.diff':
556
+ return await this.client.collab.tag.diff(collabRoot, textOf(params.version_a ?? params.versionA), textOf(params.version_b ?? params.versionB));
557
+ case 'tag.restore':
558
+ return await this.client.collab.tag.restore(collabRoot, textOf(params.version), { message: textOf(params.message) });
559
+ case 'tag.rm':
560
+ return await this.client.collab.tag.rm(collabRoot, textOf(params.version));
561
+ case 'tag.prune':
562
+ return await this.client.collab.tag.prune(collabRoot, {
563
+ before: params.before == null ? null : Number(params.before),
564
+ keep_last: params.keep_last == null && params.keepLast == null ? null : Number(params.keep_last ?? params.keepLast),
565
+ });
566
+ case 'gc':
567
+ return await this.client.collab.gc(collabRoot, params.dry_run == null && params.dryRun == null ? true : Boolean(params.dry_run ?? params.dryRun));
568
+ case 'reflog':
569
+ return await this.client.collab.reflog(collabRoot, doc || undefined, Number(params.limit ?? 100) || 100);
570
+ case 'reset':
571
+ return await this.client.collab.revert(collabRoot, doc, Number(params.version ?? 0) || 0, textOf(params.message) || '');
572
+ case 'revert':
573
+ return await this.client.collab.revert(collabRoot, doc, Number(params.rev ?? params.version ?? 0) || 0, textOf(params.message) || '');
574
+ case 'discover':
575
+ return await this.client.collab.lsRemote(textOf(params.group_aid ?? params.groupAid));
576
+ case 'unregister':
577
+ return await this.client.collab.unregister(textOf(params.group_aid ?? params.groupAid), collabRoot);
578
+ case 'snapshot.create':
579
+ return await this.client.collab.tag.create(collabRoot, {
580
+ message: textOf(params.message),
581
+ major: Boolean(params.major),
582
+ });
583
+ case 'snapshot.list':
584
+ return await this.client.collab.tag.list(collabRoot);
585
+ case 'snapshot.show':
586
+ return await this.client.collab.tag.show(collabRoot, textOf(params.version));
587
+ case 'snapshot.diff':
588
+ return await this.client.collab.tag.diff(collabRoot, textOf(params.version_a ?? params.versionA), textOf(params.version_b ?? params.versionB));
589
+ case 'snapshot.restore':
590
+ return await this.client.collab.tag.restore(collabRoot, textOf(params.version), { message: textOf(params.message) });
591
+ case 'snapshot.rm':
592
+ return await this.client.collab.tag.rm(collabRoot, textOf(params.version));
593
+ case 'snapshot.prune':
594
+ return await this.client.collab.tag.prune(collabRoot, {
595
+ before: params.before == null ? null : Number(params.before),
596
+ keep_last: params.keep_last == null && params.keepLast == null ? null : Number(params.keep_last ?? params.keepLast),
597
+ });
598
+ default:
599
+ throw new Error(`unsupported collab action: ${action}`);
600
+ }
601
+ }
602
+ async callGroupFsAction(action, params) {
603
+ const groupFs = this.client.group.fs;
604
+ const path = textOf(params.path).trim();
605
+ const options = { ...params };
606
+ delete options.path;
607
+ switch (action) {
608
+ case 'ls':
609
+ return await groupFs.ls(path, options);
610
+ case 'find':
611
+ return await groupFs.find(path, options);
612
+ case 'stat':
613
+ return await groupFs.stat(path, options);
614
+ case 'lstat':
615
+ return await groupFs.lstat(path, options);
616
+ case 'mkdir':
617
+ return await groupFs.mkdir(path, { ...options, parents: Boolean(params.parents) });
618
+ case 'rm':
619
+ return await groupFs.rm(path, {
620
+ ...options,
621
+ recursive: Boolean(params.recursive),
622
+ force: Boolean(params.force),
623
+ });
624
+ case 'cp': {
625
+ let src = textOf(params.src);
626
+ const dst = textOf(params.dst);
627
+ const cpOptions = { ...params };
628
+ delete cpOptions.src;
629
+ delete cpOptions.dst;
630
+ delete cpOptions.src_text;
631
+ if (params.src_text != null) {
632
+ src = textOf(params.src_text);
633
+ }
634
+ const result = await groupFs.cp(src, dst || { type: 'bytes' }, cpOptions);
635
+ return this.groupFsCpResponse(result, dst);
636
+ }
637
+ case 'mv':
638
+ {
639
+ const mvOptions = { ...params };
640
+ delete mvOptions.src;
641
+ delete mvOptions.dst;
642
+ return await groupFs.mv(textOf(params.src), textOf(params.dst), mvOptions);
643
+ }
644
+ case 'df':
645
+ return await groupFs.df(path || textOf(params.group_id), options);
646
+ case 'mount':
647
+ return await groupFs.mount(path, options);
648
+ case 'umount':
649
+ return await groupFs.umount(path, options);
650
+ default:
651
+ throw new Error(`unsupported group fs action: ${action}`);
652
+ }
653
+ }
654
+ groupFsCpResponse(result, dst) {
655
+ const response = { raw: jsonSafe(result) };
656
+ const resultObj = jsonObjectOf(result);
657
+ const data = resultObj?.data;
658
+ if (data instanceof ArrayBuffer) {
659
+ const buffer = Buffer.from(new Uint8Array(data));
660
+ response.content = buffer.toString('utf-8');
661
+ response.content_base64 = buffer.toString('base64');
662
+ response.size_bytes = buffer.byteLength;
663
+ }
664
+ else if (data instanceof Uint8Array || ArrayBuffer.isView(data)) {
665
+ const view = data;
666
+ const buffer = Buffer.from(view.buffer, view.byteOffset, view.byteLength);
667
+ response.content = buffer.toString('utf-8');
668
+ response.content_base64 = buffer.toString('base64');
669
+ response.size_bytes = buffer.byteLength;
670
+ }
671
+ const localPath = textOf(resultObj?.localPath ?? resultObj?.local_path ?? dst).trim();
672
+ if (localPath && fs.existsSync(localPath) && fs.statSync(localPath).isFile()) {
673
+ const buffer = fs.readFileSync(localPath);
674
+ response.local_path = localPath;
675
+ response.content = buffer.toString('utf-8');
676
+ response.content_base64 = buffer.toString('base64');
677
+ response.size_bytes = buffer.byteLength;
678
+ }
679
+ return response;
680
+ }
681
+ async callStorageAction(action, params) {
682
+ const storage = this.client.storage;
683
+ const path = textOf(params.path).trim();
684
+ const owner = textOf(params.owner_aid ?? params.ownerAID).trim() || undefined;
685
+ const bucket = textOf(params.bucket).trim() || 'default';
686
+ const token = textOf(params.token).trim() || undefined;
687
+ switch (action) {
688
+ case 'write_bytes':
689
+ return await storage.writeBytes(path, params.content_base64 || params.contentBase64
690
+ ? Buffer.from(textOf(params.content), 'base64')
691
+ : textOf(params.content), {
692
+ owner,
693
+ bucket,
694
+ contentType: textOf(params.content_type ?? params.contentType).trim() || 'text/plain',
695
+ public: Boolean(params.public),
696
+ overwrite: params.overwrite == null ? false : Boolean(params.overwrite),
697
+ });
698
+ case 'read_bytes': {
699
+ const data = await storage.readBytes(path, { owner, bucket, token });
700
+ return {
701
+ content: Buffer.from(data).toString('utf-8'),
702
+ content_base64: Buffer.from(data).toString('base64'),
703
+ size_bytes: data.byteLength,
704
+ };
705
+ }
706
+ case 'create_download_ticket': {
707
+ const objectKey = textOf(params.object_key ?? params.objectKey ?? path).replace(/^\/+/, '');
708
+ return await storage.lowlevel.createDownloadTicket({ owner, bucket, objectKey, token });
709
+ }
710
+ case 'download_text': {
711
+ const url = textOf(params.url ?? params.download_url ?? params.downloadUrl).trim();
712
+ if (!url)
713
+ throw new Error('download_text requires url');
714
+ return { content: await downloadText(url, accessTokenFromClient(this.client)) };
715
+ }
716
+ case 'set_acl':
717
+ return await storage.setAcl(path, {
718
+ owner,
719
+ bucket,
720
+ granteeAid: textOf(params.grantee_aid ?? params.granteeAID).trim(),
721
+ perms: textOf(params.perms).trim(),
722
+ expiresAt: params.expires_at == null && params.expiresAt == null ? undefined : Number(params.expires_at ?? params.expiresAt),
723
+ maxUses: params.max_uses == null && params.maxUses == null ? undefined : Number(params.max_uses ?? params.maxUses),
724
+ });
725
+ case 'remove_acl':
726
+ return await storage.removeAcl(path, {
727
+ owner,
728
+ bucket,
729
+ granteeAid: textOf(params.grantee_aid ?? params.granteeAID).trim(),
730
+ });
731
+ default:
732
+ throw new Error(`unsupported storage action: ${action}`);
733
+ }
734
+ }
735
+ async send(req, res) {
736
+ const body = await readJson(req);
737
+ const traceId = textOf(body.trace_id || crypto.randomUUID().replace(/-/g, ''));
738
+ const messageId = textOf(body.message_id || `${traceId}-${crypto.randomUUID().slice(0, 8)}`);
739
+ const target = textOf(body.to).trim();
740
+ const text = textOf(body.text);
741
+ const e2ee = body.e2ee !== false;
742
+ if (!target) {
743
+ sendJson(res, 400, { ok: false, error_code: 'bad_request', error_message: 'to is required' });
744
+ return;
745
+ }
746
+ try {
747
+ const result = await this.client.call('message.send', {
748
+ to: target,
749
+ payload: { type: 'text', text, trace_id: traceId, case_id: textOf(body.case_id || traceId) },
750
+ encrypt: e2ee,
751
+ message_id: messageId,
752
+ });
753
+ const resultObj = jsonObjectOf(result) ?? {};
754
+ const response = {
755
+ ok: true,
756
+ trace_id: traceId,
757
+ message_id: messageId,
758
+ seq: Number(resultObj.seq ?? resultObj.message_seq ?? 0) || 0,
759
+ encrypted: e2ee,
760
+ result: jsonSafe(result),
761
+ };
762
+ this.sendResults.set(traceId, response);
763
+ this.recordTrace(traceId, { stage: 'send', target, message_id: messageId, result: response });
764
+ sendJson(res, 200, response);
765
+ }
766
+ catch (err) {
767
+ const error = {
768
+ ok: false,
769
+ trace_id: traceId,
770
+ message_id: messageId,
771
+ encrypted: e2ee,
772
+ error_code: err instanceof Error ? err.name : 'Error',
773
+ error_message: err instanceof Error ? err.message : String(err),
774
+ };
775
+ this.sendResults.set(traceId, error);
776
+ this.recordTrace(traceId, { stage: 'send_error', target, message_id: messageId, error });
777
+ sendJson(res, 500, error);
778
+ }
779
+ }
780
+ async ack(req, res) {
781
+ const body = await readJson(req);
782
+ const seq = Number(body.seq ?? body.up_to_seq ?? 0) || 0;
783
+ const params = {};
784
+ if (seq > 0)
785
+ params.seq = seq;
786
+ try {
787
+ const result = await this.client.call('message.ack', params);
788
+ sendJson(res, 200, { ok: true, seq, result: jsonSafe(result) });
789
+ }
790
+ catch (err) {
791
+ sendJson(res, 500, {
792
+ ok: false,
793
+ seq,
794
+ error_code: err instanceof Error ? err.name : 'Error',
795
+ error_message: err instanceof Error ? err.message : String(err),
796
+ });
797
+ }
798
+ }
799
+ async pull(req, res) {
800
+ const body = await readJson(req);
801
+ const afterSeq = Number(body.after_seq ?? 0) || 0;
802
+ const limit = Number(body.limit ?? 50) || 50;
803
+ try {
804
+ const result = await this.client.call('message.pull', { after_seq: afterSeq, limit });
805
+ const resultObj = jsonObjectOf(result) ?? {};
806
+ const messages = Array.isArray(resultObj.messages) ? resultObj.messages : [];
807
+ for (const msg of messages)
808
+ await this.storeInboxItem(this.normalizeMessage(msg, true));
809
+ sendJson(res, 200, { ok: true, result: jsonSafe(result) });
810
+ }
811
+ catch (err) {
812
+ sendJson(res, 500, {
813
+ ok: false,
814
+ error_code: err instanceof Error ? err.name : 'Error',
815
+ error_message: err instanceof Error ? err.message : String(err),
816
+ });
817
+ }
818
+ }
819
+ async groupCreate(req, res) {
820
+ const body = await readJson(req);
821
+ const traceId = textOf(body.trace_id || crypto.randomUUID().replace(/-/g, ''));
822
+ const name = textOf(body.name || `cross-sdk-${traceId.slice(0, 8)}`);
823
+ const members = Array.isArray(body.members) ? body.members.map((item) => textOf(item).trim()).filter(Boolean) : [];
824
+ const params = { name, visibility: textOf(body.visibility || 'private') };
825
+ const groupName = textOf(body.group_name ?? body.groupName ?? '').trim();
826
+ if (groupName)
827
+ params.group_name = groupName;
828
+ const joinMode = textOf(body.join_mode || '').trim();
829
+ if (joinMode)
830
+ params.join_mode = joinMode;
831
+ try {
832
+ const createResult = groupName ? await this.client.createGroup(params) : await this.client.call('group.create', params);
833
+ const groupId = extractGroupId(createResult);
834
+ const groupAid = extractGroupAid(createResult);
835
+ if (!groupId)
836
+ throw new Error(`group.create did not return group_id: ${JSON.stringify(jsonSafe(createResult))}`);
837
+ const addResults = [];
838
+ for (const aid of members) {
839
+ if (!aid || aid === this.aid)
840
+ continue;
841
+ const addResult = await this.client.call('group.add_member', { group_id: groupId, aid, role: 'member' });
842
+ addResults.push(jsonSafe(addResult));
843
+ }
844
+ const response = {
845
+ ok: true,
846
+ trace_id: traceId,
847
+ group_id: groupId,
848
+ group_aid: groupAid,
849
+ create_result: jsonSafe(createResult),
850
+ add_results: addResults,
851
+ };
852
+ this.recordTrace(traceId, { stage: 'group_create', group_id: groupId, result: response });
853
+ sendJson(res, 200, response);
854
+ }
855
+ catch (err) {
856
+ const error = {
857
+ ok: false,
858
+ trace_id: traceId,
859
+ error_code: err instanceof Error ? err.name : 'Error',
860
+ error_message: err instanceof Error ? err.message : String(err),
861
+ };
862
+ this.recordTrace(traceId, { stage: 'group_create_error', error });
863
+ sendJson(res, 500, error);
864
+ }
865
+ }
866
+ async groupReady(url, res) {
867
+ const groupId = textOf(url.searchParams.get('group_id') ?? '').trim();
868
+ const expected = textOf(url.searchParams.get('members') ?? this.aid).split(',').map((item) => item.trim()).filter(Boolean);
869
+ const requireDevices = envBool('CROSS_SDK_GROUP_READY_REQUIRE_DEVICES', true);
870
+ if (!groupId) {
871
+ sendJson(res, 400, { ok: false, ready: false, error_code: 'bad_request', error_message: 'group_id is required' });
872
+ return;
873
+ }
874
+ try {
875
+ const bootstrap = await this.client.call('group.v2.bootstrap', { group_id: groupId });
876
+ const boot = jsonObjectOf(bootstrap) ?? {};
877
+ const committedRaw = Array.isArray(boot.committed_member_aids) ? boot.committed_member_aids : (Array.isArray(boot.member_aids) ? boot.member_aids : []);
878
+ const committed = new Set(committedRaw.map((item) => textOf(item)));
879
+ const devices = Array.isArray(boot.devices) ? boot.devices : [];
880
+ const deviceAids = new Set(devices.map((item) => textOf(jsonObjectOf(item)?.aid)));
881
+ const membershipOk = expected.every((aid) => committed.has(aid));
882
+ const devicesOk = !requireDevices || expected.every((aid) => deviceAids.has(aid));
883
+ sendJson(res, 200, {
884
+ ok: true,
885
+ ready: membershipOk && devicesOk,
886
+ group_id: groupId,
887
+ expected,
888
+ committed_member_aids: [...committed].sort(),
889
+ device_aids: [...deviceAids].sort(),
890
+ pending_adds: Array.isArray(boot.pending_adds) ? boot.pending_adds : [],
891
+ bootstrap: jsonSafe(bootstrap),
892
+ });
893
+ }
894
+ catch (err) {
895
+ sendJson(res, 500, {
896
+ ok: false,
897
+ ready: false,
898
+ error_code: err instanceof Error ? err.name : 'Error',
899
+ error_message: err instanceof Error ? err.message : String(err),
900
+ });
901
+ }
902
+ }
903
+ async groupSend(req, res) {
904
+ const body = await readJson(req);
905
+ const traceId = textOf(body.trace_id || crypto.randomUUID().replace(/-/g, ''));
906
+ const messageId = textOf(body.message_id || `${traceId}-${crypto.randomUUID().slice(0, 8)}`);
907
+ const groupId = textOf(body.group_id).trim();
908
+ const text = textOf(body.text);
909
+ const e2ee = body.e2ee !== false;
910
+ if (!groupId) {
911
+ sendJson(res, 400, { ok: false, error_code: 'bad_request', error_message: 'group_id is required' });
912
+ return;
913
+ }
914
+ try {
915
+ const result = await this.client.call('group.send', {
916
+ group_id: groupId,
917
+ payload: { type: 'text', text, trace_id: traceId, case_id: textOf(body.case_id || traceId) },
918
+ encrypt: e2ee,
919
+ message_id: messageId,
920
+ });
921
+ const resultObj = jsonObjectOf(result) ?? {};
922
+ const response = {
923
+ ok: true,
924
+ trace_id: traceId,
925
+ group_id: groupId,
926
+ message_id: messageId,
927
+ seq: Number(resultObj.seq ?? resultObj.message_seq ?? 0) || 0,
928
+ encrypted: e2ee,
929
+ result: jsonSafe(result),
930
+ };
931
+ this.recordTrace(traceId, { stage: 'group_send', group_id: groupId, message_id: messageId, result: response });
932
+ sendJson(res, 200, response);
933
+ }
934
+ catch (err) {
935
+ const error = {
936
+ ok: false,
937
+ trace_id: traceId,
938
+ group_id: groupId,
939
+ message_id: messageId,
940
+ encrypted: e2ee,
941
+ error_code: err instanceof Error ? err.name : 'Error',
942
+ error_message: err instanceof Error ? err.message : String(err),
943
+ };
944
+ this.recordTrace(traceId, { stage: 'group_send_error', group_id: groupId, message_id: messageId, error });
945
+ sendJson(res, 500, error);
946
+ }
947
+ }
948
+ async groupPull(req, res) {
949
+ const body = await readJson(req);
950
+ const groupId = textOf(body.group_id).trim();
951
+ const afterSeq = Number(body.after_seq ?? 0) || 0;
952
+ const limit = Number(body.limit ?? 50) || 50;
953
+ if (!groupId) {
954
+ sendJson(res, 400, { ok: false, error_code: 'bad_request', error_message: 'group_id is required' });
955
+ return;
956
+ }
957
+ const result = await this.client.call('group.pull', { group_id: groupId, after_seq: afterSeq, limit });
958
+ const messages = Array.isArray(jsonObjectOf(result)?.messages) ? jsonObjectOf(result).messages : [];
959
+ for (const msg of messages)
960
+ await this.storeGroupInboxItem(this.normalizeGroupMessage(msg, true));
961
+ sendJson(res, 200, { ok: true, group_id: groupId, result: jsonSafe(result) });
962
+ }
963
+ async groupAck(req, res) {
964
+ const body = await readJson(req);
965
+ const groupId = textOf(body.group_id).trim();
966
+ const seq = Number(body.seq ?? body.msg_seq ?? body.up_to_seq ?? 0) || 0;
967
+ if (!groupId) {
968
+ sendJson(res, 400, { ok: false, error_code: 'bad_request', error_message: 'group_id is required' });
969
+ return;
970
+ }
971
+ const params = { group_id: groupId };
972
+ if (seq > 0) {
973
+ params.msg_seq = seq;
974
+ params.up_to_seq = seq;
975
+ }
976
+ try {
977
+ const result = await this.client.call('group.ack_messages', params);
978
+ sendJson(res, 200, { ok: true, group_id: groupId, seq, result: jsonSafe(result) });
979
+ }
980
+ catch (err) {
981
+ sendJson(res, 500, {
982
+ ok: false,
983
+ group_id: groupId,
984
+ seq,
985
+ error_code: err instanceof Error ? err.name : 'Error',
986
+ error_message: err instanceof Error ? err.message : String(err),
987
+ });
988
+ }
989
+ }
990
+ }
991
+ function extractGroupId(result) {
992
+ const obj = jsonObjectOf(result);
993
+ if (!obj)
994
+ return '';
995
+ if (obj.group_id)
996
+ return textOf(obj.group_id);
997
+ const group = jsonObjectOf(obj.group);
998
+ if (group?.group_id)
999
+ return textOf(group.group_id);
1000
+ const member = jsonObjectOf(obj.member);
1001
+ if (member?.group_id)
1002
+ return textOf(member.group_id);
1003
+ return '';
1004
+ }
1005
+ function extractGroupAid(result) {
1006
+ const obj = jsonObjectOf(result);
1007
+ if (!obj)
1008
+ return '';
1009
+ const group = jsonObjectOf(obj.group);
1010
+ return textOf(group?.group_aid ?? obj.group_aid).trim();
1011
+ }
1012
+ function shouldForwardBearerOnRedirect(current, next) {
1013
+ if (current.origin === next.origin)
1014
+ return true;
1015
+ const currentHost = current.hostname.toLowerCase();
1016
+ const nextHost = next.hostname.toLowerCase();
1017
+ if (!nextHost.startsWith('storage.'))
1018
+ return false;
1019
+ const issuer = nextHost.slice('storage.'.length);
1020
+ return Boolean(issuer) && (currentHost === issuer || currentHost.endsWith(`.${issuer}`));
1021
+ }
1022
+ function downloadText(url, bearerToken = '', redirects = 0) {
1023
+ return new Promise((resolve, reject) => {
1024
+ const parsed = new URL(url);
1025
+ const client = parsed.protocol === 'https:' ? https : http;
1026
+ const headers = bearerToken ? { Authorization: `Bearer ${bearerToken}` } : undefined;
1027
+ const options = parsed.protocol === 'https:'
1028
+ ? { rejectUnauthorized: false, headers }
1029
+ : { headers };
1030
+ const req = client.get(parsed, options, (resp) => {
1031
+ const status = resp.statusCode ?? 0;
1032
+ const location = typeof resp.headers.location === 'string' ? resp.headers.location : '';
1033
+ if ([301, 302, 303, 307, 308].includes(status) && location) {
1034
+ resp.resume();
1035
+ if (redirects >= 5) {
1036
+ reject(new Error(`download redirect limit exceeded url=${url}`));
1037
+ return;
1038
+ }
1039
+ const nextUrl = new URL(location, parsed).toString();
1040
+ const nextBearer = bearerToken && shouldForwardBearerOnRedirect(parsed, new URL(nextUrl)) ? bearerToken : '';
1041
+ downloadText(nextUrl, nextBearer, redirects + 1).then(resolve, reject);
1042
+ return;
1043
+ }
1044
+ const chunks = [];
1045
+ resp.on('data', (chunk) => chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk)));
1046
+ resp.on('end', () => {
1047
+ if (status < 200 || status >= 300) {
1048
+ reject(new Error(`download failed status=${status} url=${url}`));
1049
+ return;
1050
+ }
1051
+ const content = Buffer.concat(chunks).toString('utf-8');
1052
+ if (!content) {
1053
+ reject(new Error(`download returned empty body status=${status} url=${url}`));
1054
+ return;
1055
+ }
1056
+ resolve(content);
1057
+ });
1058
+ });
1059
+ req.setTimeout(20000, () => {
1060
+ req.destroy(new Error('download timeout'));
1061
+ });
1062
+ req.on('error', reject);
1063
+ });
1064
+ }
1065
+ async function main() {
1066
+ installNodeBrowserShims();
1067
+ const agent = new CrossSdkJsAgent();
1068
+ void agent.start().catch((err) => {
1069
+ agent.startupError = `${err instanceof Error ? err.name : 'Error'}: ${err instanceof Error ? err.message : String(err)}`;
1070
+ console.error(agent.startupError);
1071
+ });
1072
+ const host = textOf(process.env.AUN_CONTROL_HOST || '0.0.0.0');
1073
+ const port = Number(process.env.AUN_CONTROL_PORT || 9001) || 9001;
1074
+ const server = http.createServer((req, res) => {
1075
+ void agent.handle(req, res);
1076
+ });
1077
+ server.listen(port, host, () => {
1078
+ console.log(`cross-sdk js agent listening on ${host}:${port}`);
1079
+ });
1080
+ const shutdown = async () => {
1081
+ server.close();
1082
+ await agent.close();
1083
+ process.exit(0);
1084
+ };
1085
+ process.on('SIGINT', () => { void shutdown(); });
1086
+ process.on('SIGTERM', () => { void shutdown(); });
1087
+ }
1088
+ void main();
1089
+ //# sourceMappingURL=cross-sdk-agent.js.map