@aomi-labs/client 0.1.0 → 0.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.js ADDED
@@ -0,0 +1,2416 @@
1
+ #!/usr/bin/env node
2
+ var __defProp = Object.defineProperty;
3
+ var __defProps = Object.defineProperties;
4
+ var __getOwnPropDescs = Object.getOwnPropertyDescriptors;
5
+ var __getOwnPropSymbols = Object.getOwnPropertySymbols;
6
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
7
+ var __propIsEnum = Object.prototype.propertyIsEnumerable;
8
+ var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
9
+ var __spreadValues = (a, b) => {
10
+ for (var prop in b || (b = {}))
11
+ if (__hasOwnProp.call(b, prop))
12
+ __defNormalProp(a, prop, b[prop]);
13
+ if (__getOwnPropSymbols)
14
+ for (var prop of __getOwnPropSymbols(b)) {
15
+ if (__propIsEnum.call(b, prop))
16
+ __defNormalProp(a, prop, b[prop]);
17
+ }
18
+ return a;
19
+ };
20
+ var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b));
21
+
22
+ // src/cli/args.ts
23
+ function parseArgs(argv) {
24
+ const raw = argv.slice(2);
25
+ const command = raw[0] && !raw[0].startsWith("--") ? raw[0] : void 0;
26
+ const rest = command ? raw.slice(1) : raw;
27
+ const positional = [];
28
+ const flags = {};
29
+ for (let i = 0; i < rest.length; i++) {
30
+ const arg = rest[i];
31
+ if (arg.startsWith("--") && arg.includes("=")) {
32
+ const [key, ...val] = arg.slice(2).split("=");
33
+ flags[key] = val.join("=");
34
+ } else if (arg.startsWith("--")) {
35
+ const key = arg.slice(2);
36
+ const next = rest[i + 1];
37
+ if (next && !next.startsWith("-")) {
38
+ flags[key] = next;
39
+ i++;
40
+ } else {
41
+ flags[key] = "true";
42
+ }
43
+ } else if (arg.startsWith("-") && arg.length === 2) {
44
+ flags[arg.slice(1)] = "true";
45
+ } else {
46
+ positional.push(arg);
47
+ }
48
+ }
49
+ return { command, positional, flags };
50
+ }
51
+ function getConfig(parsed) {
52
+ var _a3, _b, _c, _d, _e, _f, _g, _h, _i;
53
+ return {
54
+ baseUrl: (_b = (_a3 = parsed.flags["backend-url"]) != null ? _a3 : process.env.AOMI_BASE_URL) != null ? _b : "https://api.aomi.dev",
55
+ apiKey: (_c = parsed.flags["api-key"]) != null ? _c : process.env.AOMI_API_KEY,
56
+ namespace: (_e = (_d = parsed.flags["namespace"]) != null ? _d : process.env.AOMI_NAMESPACE) != null ? _e : "default",
57
+ model: (_f = parsed.flags["model"]) != null ? _f : process.env.AOMI_MODEL,
58
+ publicKey: (_g = parsed.flags["public-key"]) != null ? _g : process.env.AOMI_PUBLIC_KEY,
59
+ privateKey: (_h = parsed.flags["private-key"]) != null ? _h : process.env.PRIVATE_KEY,
60
+ chainRpcUrl: (_i = parsed.flags["rpc-url"]) != null ? _i : process.env.CHAIN_RPC_URL
61
+ };
62
+ }
63
+ function createRuntime(argv) {
64
+ const parsed = parseArgs(argv);
65
+ return {
66
+ parsed,
67
+ config: getConfig(parsed)
68
+ };
69
+ }
70
+
71
+ // src/cli/state.ts
72
+ import {
73
+ existsSync,
74
+ mkdirSync,
75
+ readFileSync,
76
+ readdirSync,
77
+ rmSync,
78
+ writeFileSync
79
+ } from "fs";
80
+ import { basename, join } from "path";
81
+ import { homedir, tmpdir } from "os";
82
+ var SESSION_FILE_PREFIX = "session-";
83
+ var SESSION_FILE_SUFFIX = ".json";
84
+ var _a;
85
+ var LEGACY_STATE_FILE = join(
86
+ (_a = process.env.XDG_RUNTIME_DIR) != null ? _a : tmpdir(),
87
+ "aomi-session.json"
88
+ );
89
+ var _a2;
90
+ var STATE_ROOT_DIR = (_a2 = process.env.AOMI_STATE_DIR) != null ? _a2 : join(homedir(), ".aomi");
91
+ var SESSIONS_DIR = join(STATE_ROOT_DIR, "sessions");
92
+ var ACTIVE_SESSION_FILE = join(STATE_ROOT_DIR, "active-session.txt");
93
+ function ensureStorageDirs() {
94
+ mkdirSync(SESSIONS_DIR, { recursive: true });
95
+ }
96
+ function parseSessionFileLocalId(filename) {
97
+ const match = filename.match(/^session-(\d+)\.json$/);
98
+ if (!match) return null;
99
+ const localId = parseInt(match[1], 10);
100
+ return Number.isNaN(localId) ? null : localId;
101
+ }
102
+ function toSessionFilePath(localId) {
103
+ return join(SESSIONS_DIR, `${SESSION_FILE_PREFIX}${localId}${SESSION_FILE_SUFFIX}`);
104
+ }
105
+ function toCliSessionState(stored) {
106
+ return {
107
+ sessionId: stored.sessionId,
108
+ baseUrl: stored.baseUrl,
109
+ namespace: stored.namespace,
110
+ model: stored.model,
111
+ apiKey: stored.apiKey,
112
+ publicKey: stored.publicKey,
113
+ pendingTxs: stored.pendingTxs,
114
+ signedTxs: stored.signedTxs
115
+ };
116
+ }
117
+ function readStoredSession(path) {
118
+ var _a3;
119
+ try {
120
+ const raw = readFileSync(path, "utf-8");
121
+ const parsed = JSON.parse(raw);
122
+ if (typeof parsed.sessionId !== "string" || typeof parsed.baseUrl !== "string") {
123
+ return null;
124
+ }
125
+ const fallbackLocalId = (_a3 = parseSessionFileLocalId(basename(path))) != null ? _a3 : 0;
126
+ return {
127
+ sessionId: parsed.sessionId,
128
+ baseUrl: parsed.baseUrl,
129
+ namespace: parsed.namespace,
130
+ model: parsed.model,
131
+ apiKey: parsed.apiKey,
132
+ publicKey: parsed.publicKey,
133
+ pendingTxs: parsed.pendingTxs,
134
+ signedTxs: parsed.signedTxs,
135
+ localId: typeof parsed.localId === "number" && parsed.localId > 0 ? parsed.localId : fallbackLocalId,
136
+ createdAt: typeof parsed.createdAt === "number" && parsed.createdAt > 0 ? parsed.createdAt : Date.now(),
137
+ updatedAt: typeof parsed.updatedAt === "number" && parsed.updatedAt > 0 ? parsed.updatedAt : Date.now()
138
+ };
139
+ } catch (e) {
140
+ return null;
141
+ }
142
+ }
143
+ function readActiveLocalId() {
144
+ try {
145
+ if (!existsSync(ACTIVE_SESSION_FILE)) return null;
146
+ const raw = readFileSync(ACTIVE_SESSION_FILE, "utf-8").trim();
147
+ if (!raw) return null;
148
+ const parsed = parseInt(raw, 10);
149
+ return Number.isNaN(parsed) ? null : parsed;
150
+ } catch (e) {
151
+ return null;
152
+ }
153
+ }
154
+ function writeActiveLocalId(localId) {
155
+ try {
156
+ if (localId === null) {
157
+ if (existsSync(ACTIVE_SESSION_FILE)) {
158
+ rmSync(ACTIVE_SESSION_FILE);
159
+ }
160
+ return;
161
+ }
162
+ ensureStorageDirs();
163
+ writeFileSync(ACTIVE_SESSION_FILE, String(localId));
164
+ } catch (e) {
165
+ }
166
+ }
167
+ function readAllStoredSessions() {
168
+ try {
169
+ ensureStorageDirs();
170
+ const filenames = readdirSync(SESSIONS_DIR).map((name) => ({ name, localId: parseSessionFileLocalId(name) })).filter(
171
+ (entry) => entry.localId !== null
172
+ ).sort((a, b) => a.localId - b.localId);
173
+ const sessions = [];
174
+ for (const entry of filenames) {
175
+ const path = join(SESSIONS_DIR, entry.name);
176
+ const stored = readStoredSession(path);
177
+ if (stored) {
178
+ sessions.push(stored);
179
+ }
180
+ }
181
+ return sessions;
182
+ } catch (e) {
183
+ return [];
184
+ }
185
+ }
186
+ function getNextLocalId(sessions) {
187
+ const maxLocalId = sessions.reduce((max, session) => {
188
+ return session.localId > max ? session.localId : max;
189
+ }, 0);
190
+ return maxLocalId + 1;
191
+ }
192
+ var _migrationDone = false;
193
+ function migrateLegacyStateIfNeeded() {
194
+ if (_migrationDone) return;
195
+ _migrationDone = true;
196
+ if (!existsSync(LEGACY_STATE_FILE)) return;
197
+ const existing = readAllStoredSessions();
198
+ if (existing.length > 0) {
199
+ return;
200
+ }
201
+ try {
202
+ const raw = readFileSync(LEGACY_STATE_FILE, "utf-8");
203
+ const legacy = JSON.parse(raw);
204
+ if (!legacy.sessionId || !legacy.baseUrl) {
205
+ return;
206
+ }
207
+ const now = Date.now();
208
+ const migrated = __spreadProps(__spreadValues({}, legacy), {
209
+ localId: 1,
210
+ createdAt: now,
211
+ updatedAt: now
212
+ });
213
+ ensureStorageDirs();
214
+ writeFileSync(toSessionFilePath(1), JSON.stringify(migrated, null, 2));
215
+ writeActiveLocalId(1);
216
+ rmSync(LEGACY_STATE_FILE);
217
+ } catch (e) {
218
+ }
219
+ }
220
+ function resolveStoredSession(selector, sessions) {
221
+ var _a3, _b;
222
+ const trimmed = selector.trim();
223
+ if (!trimmed) return null;
224
+ const localMatch = trimmed.match(/^(?:session-)?(\d+)$/);
225
+ if (localMatch) {
226
+ const localId = parseInt(localMatch[1], 10);
227
+ if (!Number.isNaN(localId)) {
228
+ return (_a3 = sessions.find((session) => session.localId === localId)) != null ? _a3 : null;
229
+ }
230
+ }
231
+ return (_b = sessions.find((session) => session.sessionId === trimmed)) != null ? _b : null;
232
+ }
233
+ function toStoredSessionRecord(stored) {
234
+ return {
235
+ localId: stored.localId,
236
+ sessionId: stored.sessionId,
237
+ path: toSessionFilePath(stored.localId),
238
+ createdAt: stored.createdAt,
239
+ updatedAt: stored.updatedAt,
240
+ state: toCliSessionState(stored)
241
+ };
242
+ }
243
+ function getActiveStateFilePath() {
244
+ migrateLegacyStateIfNeeded();
245
+ const sessions = readAllStoredSessions();
246
+ const activeLocalId = readActiveLocalId();
247
+ if (activeLocalId === null) return null;
248
+ const active = sessions.find((session) => session.localId === activeLocalId);
249
+ return active ? toSessionFilePath(active.localId) : null;
250
+ }
251
+ function listStoredSessions() {
252
+ migrateLegacyStateIfNeeded();
253
+ return readAllStoredSessions().map(toStoredSessionRecord);
254
+ }
255
+ function setActiveSession(selector) {
256
+ migrateLegacyStateIfNeeded();
257
+ const sessions = readAllStoredSessions();
258
+ const target = resolveStoredSession(selector, sessions);
259
+ if (!target) return null;
260
+ writeActiveLocalId(target.localId);
261
+ return toStoredSessionRecord(target);
262
+ }
263
+ function deleteStoredSession(selector) {
264
+ var _a3, _b;
265
+ migrateLegacyStateIfNeeded();
266
+ const sessions = readAllStoredSessions();
267
+ const target = resolveStoredSession(selector, sessions);
268
+ if (!target) return null;
269
+ const targetPath = toSessionFilePath(target.localId);
270
+ try {
271
+ if (existsSync(targetPath)) {
272
+ rmSync(targetPath);
273
+ }
274
+ } catch (e) {
275
+ return null;
276
+ }
277
+ const activeLocalId = readActiveLocalId();
278
+ if (activeLocalId === target.localId) {
279
+ const remaining = readAllStoredSessions().sort((a, b) => b.updatedAt - a.updatedAt);
280
+ writeActiveLocalId((_b = (_a3 = remaining[0]) == null ? void 0 : _a3.localId) != null ? _b : null);
281
+ }
282
+ return toStoredSessionRecord(target);
283
+ }
284
+ function readState() {
285
+ var _a3;
286
+ migrateLegacyStateIfNeeded();
287
+ const sessions = readAllStoredSessions();
288
+ if (sessions.length === 0) return null;
289
+ const activeLocalId = readActiveLocalId();
290
+ if (activeLocalId === null) {
291
+ return null;
292
+ }
293
+ const active = (_a3 = sessions.find((session) => session.localId === activeLocalId)) != null ? _a3 : null;
294
+ if (!active) {
295
+ writeActiveLocalId(null);
296
+ return null;
297
+ }
298
+ return toCliSessionState(active);
299
+ }
300
+ function writeState(state) {
301
+ var _a3, _b;
302
+ migrateLegacyStateIfNeeded();
303
+ ensureStorageDirs();
304
+ const sessions = readAllStoredSessions();
305
+ const activeLocalId = readActiveLocalId();
306
+ const existingBySessionId = sessions.find(
307
+ (session) => session.sessionId === state.sessionId
308
+ );
309
+ const existingByActive = activeLocalId !== null ? sessions.find((session) => session.localId === activeLocalId) : void 0;
310
+ const existing = existingBySessionId != null ? existingBySessionId : existingByActive;
311
+ const now = Date.now();
312
+ const localId = (_a3 = existing == null ? void 0 : existing.localId) != null ? _a3 : getNextLocalId(sessions);
313
+ const createdAt = (_b = existing == null ? void 0 : existing.createdAt) != null ? _b : now;
314
+ const payload = __spreadProps(__spreadValues({}, state), {
315
+ localId,
316
+ createdAt,
317
+ updatedAt: now
318
+ });
319
+ writeFileSync(toSessionFilePath(localId), JSON.stringify(payload, null, 2));
320
+ writeActiveLocalId(localId);
321
+ }
322
+ function clearState() {
323
+ migrateLegacyStateIfNeeded();
324
+ writeActiveLocalId(null);
325
+ }
326
+ function getNextTxId(state) {
327
+ var _a3, _b;
328
+ const allIds = [...(_a3 = state.pendingTxs) != null ? _a3 : [], ...(_b = state.signedTxs) != null ? _b : []].map((tx) => {
329
+ const match = tx.id.match(/^tx-(\d+)$/);
330
+ return match ? parseInt(match[1], 10) : 0;
331
+ });
332
+ const max = allIds.length > 0 ? Math.max(...allIds) : 0;
333
+ return `tx-${max + 1}`;
334
+ }
335
+ function addPendingTx(state, tx) {
336
+ if (!state.pendingTxs) state.pendingTxs = [];
337
+ const pending = __spreadProps(__spreadValues({}, tx), {
338
+ id: getNextTxId(state)
339
+ });
340
+ state.pendingTxs.push(pending);
341
+ writeState(state);
342
+ return pending;
343
+ }
344
+ function removePendingTx(state, id) {
345
+ if (!state.pendingTxs) return null;
346
+ const idx = state.pendingTxs.findIndex((tx) => tx.id === id);
347
+ if (idx === -1) return null;
348
+ const [removed] = state.pendingTxs.splice(idx, 1);
349
+ writeState(state);
350
+ return removed;
351
+ }
352
+ function addSignedTx(state, tx) {
353
+ if (!state.signedTxs) state.signedTxs = [];
354
+ state.signedTxs.push(tx);
355
+ writeState(state);
356
+ }
357
+
358
+ // src/cli/output.ts
359
+ var DIM = "\x1B[2m";
360
+ var CYAN = "\x1B[36m";
361
+ var YELLOW = "\x1B[33m";
362
+ var GREEN = "\x1B[32m";
363
+ var RESET = "\x1B[0m";
364
+ function printDataFileLocation() {
365
+ const activeFile = getActiveStateFilePath();
366
+ if (activeFile) {
367
+ console.log(`Data stored at ${activeFile} \u{1F4DD}`);
368
+ return;
369
+ }
370
+ console.log(`Data stored under ${STATE_ROOT_DIR} \u{1F4DD}`);
371
+ }
372
+ function printToolUpdate(event) {
373
+ var _a3;
374
+ const name = getToolNameFromEvent(event);
375
+ const status = (_a3 = event.status) != null ? _a3 : "running";
376
+ console.log(`${DIM}\u{1F527} [tool] ${name}: ${status}${RESET}`);
377
+ }
378
+ function printToolComplete(event) {
379
+ const name = getToolNameFromEvent(event);
380
+ const result = getToolResultFromEvent(event);
381
+ const line = formatToolResultLine(name, result);
382
+ console.log(line);
383
+ }
384
+ function printToolResultLine(name, result) {
385
+ console.log(formatToolResultLine(name, result));
386
+ }
387
+ function getToolNameFromEvent(event) {
388
+ var _a3, _b;
389
+ return (_b = (_a3 = event.tool_name) != null ? _a3 : event.name) != null ? _b : "unknown";
390
+ }
391
+ function getToolResultFromEvent(event) {
392
+ var _a3;
393
+ return (_a3 = event.result) != null ? _a3 : event.output;
394
+ }
395
+ function toToolResultKey(name, result) {
396
+ return `${name}
397
+ ${result != null ? result : ""}`;
398
+ }
399
+ function getMessageToolResults(messages, startAt = 0) {
400
+ const results = [];
401
+ for (let i = startAt; i < messages.length; i++) {
402
+ const toolResult = messages[i].tool_result;
403
+ if (!toolResult) {
404
+ continue;
405
+ }
406
+ const [name, result] = toolResult;
407
+ if (!name || typeof result !== "string") {
408
+ continue;
409
+ }
410
+ results.push({ name, result });
411
+ }
412
+ return results;
413
+ }
414
+ function isAlwaysVisibleTool(name) {
415
+ const normalized = name.toLowerCase();
416
+ if (normalized.includes("encode_and_simulate") || normalized.includes("encode-and-simulate") || normalized.includes("encode_and_view") || normalized.includes("encode-and-view")) {
417
+ return true;
418
+ }
419
+ if (normalized.startsWith("simulate ")) {
420
+ return true;
421
+ }
422
+ return false;
423
+ }
424
+ function printNewAgentMessages(messages, lastPrintedCount) {
425
+ const agentMessages = messages.filter(
426
+ (message) => message.sender === "agent" || message.sender === "assistant"
427
+ );
428
+ let handled = lastPrintedCount;
429
+ for (let i = lastPrintedCount; i < agentMessages.length; i++) {
430
+ const message = agentMessages[i];
431
+ if (message.is_streaming) {
432
+ break;
433
+ }
434
+ if (message.content) {
435
+ console.log(`${CYAN}\u{1F916} ${message.content}${RESET}`);
436
+ }
437
+ handled = i + 1;
438
+ }
439
+ return handled;
440
+ }
441
+ function formatLogContent(content) {
442
+ if (!content) return null;
443
+ const trimmed = content.trim();
444
+ return trimmed.length > 0 ? trimmed : null;
445
+ }
446
+ function formatToolResultPreview(result, maxLength = 200) {
447
+ const normalized = result.replace(/\s+/g, " ").trim();
448
+ if (normalized.length <= maxLength) {
449
+ return normalized;
450
+ }
451
+ return `${normalized.slice(0, maxLength)}\u2026`;
452
+ }
453
+ function formatToolResultLine(name, result) {
454
+ if (!result) {
455
+ return `${GREEN}\u2714 [tool] ${name} done${RESET}`;
456
+ }
457
+ return `${GREEN}\u2714 [tool] ${name} \u2192 ${formatToolResultPreview(result, 120)}${RESET}`;
458
+ }
459
+
460
+ // src/sse.ts
461
+ function extractSseData(rawEvent) {
462
+ const dataLines = rawEvent.split("\n").filter((line) => line.startsWith("data:")).map((line) => line.slice(5).trimStart());
463
+ if (!dataLines.length) return null;
464
+ return dataLines.join("\n");
465
+ }
466
+ async function readSseStream(stream, signal, onMessage) {
467
+ const reader = stream.getReader();
468
+ const decoder = new TextDecoder();
469
+ let buffer = "";
470
+ try {
471
+ while (!signal.aborted) {
472
+ const { value, done } = await reader.read();
473
+ if (done) break;
474
+ buffer += decoder.decode(value, { stream: true });
475
+ buffer = buffer.replace(/\r/g, "");
476
+ let separatorIndex = buffer.indexOf("\n\n");
477
+ while (separatorIndex >= 0) {
478
+ const rawEvent = buffer.slice(0, separatorIndex);
479
+ buffer = buffer.slice(separatorIndex + 2);
480
+ const data = extractSseData(rawEvent);
481
+ if (data) {
482
+ onMessage(data);
483
+ }
484
+ separatorIndex = buffer.indexOf("\n\n");
485
+ }
486
+ }
487
+ } finally {
488
+ reader.releaseLock();
489
+ }
490
+ }
491
+ function createSseSubscriber({
492
+ backendUrl,
493
+ getHeaders,
494
+ logger
495
+ }) {
496
+ const subscriptions = /* @__PURE__ */ new Map();
497
+ const subscribe = (sessionId, onUpdate, onError) => {
498
+ const existing = subscriptions.get(sessionId);
499
+ const listener = { onUpdate, onError };
500
+ if (existing) {
501
+ existing.listeners.add(listener);
502
+ logger == null ? void 0 : logger.debug("[aomi][sse] listener added", {
503
+ sessionId,
504
+ listeners: existing.listeners.size
505
+ });
506
+ return () => {
507
+ existing.listeners.delete(listener);
508
+ logger == null ? void 0 : logger.debug("[aomi][sse] listener removed", {
509
+ sessionId,
510
+ listeners: existing.listeners.size
511
+ });
512
+ if (existing.listeners.size === 0) {
513
+ existing.stop("unsubscribe");
514
+ if (subscriptions.get(sessionId) === existing) {
515
+ subscriptions.delete(sessionId);
516
+ }
517
+ }
518
+ };
519
+ }
520
+ const subscription = {
521
+ abortController: null,
522
+ retries: 0,
523
+ retryTimer: null,
524
+ stopped: false,
525
+ listeners: /* @__PURE__ */ new Set([listener]),
526
+ stop: (reason) => {
527
+ var _a3;
528
+ subscription.stopped = true;
529
+ if (subscription.retryTimer) {
530
+ clearTimeout(subscription.retryTimer);
531
+ subscription.retryTimer = null;
532
+ }
533
+ (_a3 = subscription.abortController) == null ? void 0 : _a3.abort();
534
+ subscription.abortController = null;
535
+ logger == null ? void 0 : logger.debug("[aomi][sse] stop", {
536
+ sessionId,
537
+ reason,
538
+ retries: subscription.retries
539
+ });
540
+ }
541
+ };
542
+ const scheduleRetry = () => {
543
+ if (subscription.stopped) return;
544
+ subscription.retries += 1;
545
+ const delayMs = Math.min(500 * 2 ** (subscription.retries - 1), 1e4);
546
+ logger == null ? void 0 : logger.debug("[aomi][sse] retry scheduled", {
547
+ sessionId,
548
+ delayMs,
549
+ retries: subscription.retries
550
+ });
551
+ subscription.retryTimer = setTimeout(() => {
552
+ void open();
553
+ }, delayMs);
554
+ };
555
+ const open = async () => {
556
+ var _a3;
557
+ if (subscription.stopped) return;
558
+ if (subscription.retryTimer) {
559
+ clearTimeout(subscription.retryTimer);
560
+ subscription.retryTimer = null;
561
+ }
562
+ const controller = new AbortController();
563
+ subscription.abortController = controller;
564
+ const openedAt = Date.now();
565
+ try {
566
+ const response = await fetch(`${backendUrl}/api/updates`, {
567
+ headers: getHeaders(sessionId),
568
+ signal: controller.signal
569
+ });
570
+ if (!response.ok) {
571
+ throw new Error(
572
+ `SSE HTTP ${response.status}: ${response.statusText}`
573
+ );
574
+ }
575
+ if (!response.body) {
576
+ throw new Error("SSE response missing body");
577
+ }
578
+ subscription.retries = 0;
579
+ await readSseStream(response.body, controller.signal, (data) => {
580
+ var _a4, _b;
581
+ let parsed;
582
+ try {
583
+ parsed = JSON.parse(data);
584
+ } catch (error) {
585
+ for (const item of subscription.listeners) {
586
+ (_a4 = item.onError) == null ? void 0 : _a4.call(item, error);
587
+ }
588
+ return;
589
+ }
590
+ for (const item of subscription.listeners) {
591
+ try {
592
+ item.onUpdate(parsed);
593
+ } catch (error) {
594
+ (_b = item.onError) == null ? void 0 : _b.call(item, error);
595
+ }
596
+ }
597
+ });
598
+ logger == null ? void 0 : logger.debug("[aomi][sse] stream ended", {
599
+ sessionId,
600
+ aborted: controller.signal.aborted,
601
+ stopped: subscription.stopped,
602
+ durationMs: Date.now() - openedAt
603
+ });
604
+ } catch (error) {
605
+ if (!controller.signal.aborted && !subscription.stopped) {
606
+ for (const item of subscription.listeners) {
607
+ (_a3 = item.onError) == null ? void 0 : _a3.call(item, error);
608
+ }
609
+ }
610
+ }
611
+ if (!subscription.stopped) {
612
+ scheduleRetry();
613
+ }
614
+ };
615
+ subscriptions.set(sessionId, subscription);
616
+ void open();
617
+ return () => {
618
+ subscription.listeners.delete(listener);
619
+ logger == null ? void 0 : logger.debug("[aomi][sse] listener removed", {
620
+ sessionId,
621
+ listeners: subscription.listeners.size
622
+ });
623
+ if (subscription.listeners.size === 0) {
624
+ subscription.stop("unsubscribe");
625
+ if (subscriptions.get(sessionId) === subscription) {
626
+ subscriptions.delete(sessionId);
627
+ }
628
+ }
629
+ };
630
+ };
631
+ return { subscribe };
632
+ }
633
+
634
+ // src/client.ts
635
+ var SESSION_ID_HEADER = "X-Session-Id";
636
+ var API_KEY_HEADER = "X-API-Key";
637
+ function toQueryString(payload) {
638
+ const params = new URLSearchParams();
639
+ for (const [key, value] of Object.entries(payload)) {
640
+ if (value === void 0 || value === null) continue;
641
+ params.set(key, String(value));
642
+ }
643
+ const qs = params.toString();
644
+ return qs ? `?${qs}` : "";
645
+ }
646
+ function withSessionHeader(sessionId, init) {
647
+ const headers = new Headers(init);
648
+ headers.set(SESSION_ID_HEADER, sessionId);
649
+ return headers;
650
+ }
651
+ async function postState(baseUrl, path, payload, sessionId, apiKey) {
652
+ const query = toQueryString(payload);
653
+ const url = `${baseUrl}${path}${query}`;
654
+ const headers = new Headers(withSessionHeader(sessionId));
655
+ if (apiKey) {
656
+ headers.set(API_KEY_HEADER, apiKey);
657
+ }
658
+ const response = await fetch(url, {
659
+ method: "POST",
660
+ headers
661
+ });
662
+ if (!response.ok) {
663
+ throw new Error(`HTTP ${response.status}: ${response.statusText}`);
664
+ }
665
+ return await response.json();
666
+ }
667
+ var AomiClient = class {
668
+ constructor(options) {
669
+ this.baseUrl = options.baseUrl.replace(/\/+$/, "");
670
+ this.apiKey = options.apiKey;
671
+ this.logger = options.logger;
672
+ this.sseSubscriber = createSseSubscriber({
673
+ backendUrl: this.baseUrl,
674
+ getHeaders: (sessionId) => withSessionHeader(sessionId, { Accept: "text/event-stream" }),
675
+ logger: this.logger
676
+ });
677
+ }
678
+ // ===========================================================================
679
+ // Chat & State
680
+ // ===========================================================================
681
+ /**
682
+ * Fetch current session state (messages, processing status, title).
683
+ */
684
+ async fetchState(sessionId, userState) {
685
+ const url = new URL("/api/state", this.baseUrl);
686
+ if (userState) {
687
+ url.searchParams.set("user_state", JSON.stringify(userState));
688
+ }
689
+ const response = await fetch(url.toString(), {
690
+ headers: withSessionHeader(sessionId)
691
+ });
692
+ if (!response.ok) {
693
+ throw new Error(`HTTP ${response.status}: ${response.statusText}`);
694
+ }
695
+ return await response.json();
696
+ }
697
+ /**
698
+ * Send a chat message and return updated session state.
699
+ */
700
+ async sendMessage(sessionId, message, options) {
701
+ var _a3, _b;
702
+ const namespace = (_a3 = options == null ? void 0 : options.namespace) != null ? _a3 : "default";
703
+ const apiKey = (_b = options == null ? void 0 : options.apiKey) != null ? _b : this.apiKey;
704
+ const payload = { message, namespace };
705
+ if (options == null ? void 0 : options.publicKey) {
706
+ payload.public_key = options.publicKey;
707
+ }
708
+ if (options == null ? void 0 : options.userState) {
709
+ payload.user_state = JSON.stringify(options.userState);
710
+ }
711
+ return postState(
712
+ this.baseUrl,
713
+ "/api/chat",
714
+ payload,
715
+ sessionId,
716
+ apiKey
717
+ );
718
+ }
719
+ /**
720
+ * Send a system-level message (e.g. wallet state changes, context switches).
721
+ */
722
+ async sendSystemMessage(sessionId, message) {
723
+ return postState(
724
+ this.baseUrl,
725
+ "/api/system",
726
+ { message },
727
+ sessionId
728
+ );
729
+ }
730
+ /**
731
+ * Interrupt the AI's current response.
732
+ */
733
+ async interrupt(sessionId) {
734
+ return postState(
735
+ this.baseUrl,
736
+ "/api/interrupt",
737
+ {},
738
+ sessionId
739
+ );
740
+ }
741
+ // ===========================================================================
742
+ // SSE (Real-time Updates)
743
+ // ===========================================================================
744
+ /**
745
+ * Subscribe to real-time SSE updates for a session.
746
+ * Automatically reconnects with exponential backoff on disconnects.
747
+ * Returns an unsubscribe function.
748
+ */
749
+ subscribeSSE(sessionId, onUpdate, onError) {
750
+ return this.sseSubscriber.subscribe(sessionId, onUpdate, onError);
751
+ }
752
+ // ===========================================================================
753
+ // Thread / Session Management
754
+ // ===========================================================================
755
+ /**
756
+ * List all threads for a wallet address.
757
+ */
758
+ async listThreads(publicKey) {
759
+ const url = `${this.baseUrl}/api/sessions?public_key=${encodeURIComponent(publicKey)}`;
760
+ const response = await fetch(url);
761
+ if (!response.ok) {
762
+ throw new Error(`Failed to fetch threads: HTTP ${response.status}`);
763
+ }
764
+ return await response.json();
765
+ }
766
+ /**
767
+ * Get a single thread by ID.
768
+ */
769
+ async getThread(sessionId) {
770
+ const url = `${this.baseUrl}/api/sessions/${encodeURIComponent(sessionId)}`;
771
+ const response = await fetch(url, {
772
+ headers: withSessionHeader(sessionId)
773
+ });
774
+ if (!response.ok) {
775
+ throw new Error(`HTTP ${response.status}: ${response.statusText}`);
776
+ }
777
+ return await response.json();
778
+ }
779
+ /**
780
+ * Create a new thread. The client generates the session ID.
781
+ */
782
+ async createThread(threadId, publicKey) {
783
+ const body = {};
784
+ if (publicKey) body.public_key = publicKey;
785
+ const url = `${this.baseUrl}/api/sessions`;
786
+ const response = await fetch(url, {
787
+ method: "POST",
788
+ headers: withSessionHeader(threadId, {
789
+ "Content-Type": "application/json"
790
+ }),
791
+ body: JSON.stringify(body)
792
+ });
793
+ if (!response.ok) {
794
+ throw new Error(`Failed to create thread: HTTP ${response.status}`);
795
+ }
796
+ return await response.json();
797
+ }
798
+ /**
799
+ * Delete a thread by ID.
800
+ */
801
+ async deleteThread(sessionId) {
802
+ const url = `${this.baseUrl}/api/sessions/${encodeURIComponent(sessionId)}`;
803
+ const response = await fetch(url, {
804
+ method: "DELETE",
805
+ headers: withSessionHeader(sessionId)
806
+ });
807
+ if (!response.ok) {
808
+ throw new Error(`Failed to delete thread: HTTP ${response.status}`);
809
+ }
810
+ }
811
+ /**
812
+ * Rename a thread.
813
+ */
814
+ async renameThread(sessionId, newTitle) {
815
+ const url = `${this.baseUrl}/api/sessions/${encodeURIComponent(sessionId)}`;
816
+ const response = await fetch(url, {
817
+ method: "PATCH",
818
+ headers: withSessionHeader(sessionId, {
819
+ "Content-Type": "application/json"
820
+ }),
821
+ body: JSON.stringify({ title: newTitle })
822
+ });
823
+ if (!response.ok) {
824
+ throw new Error(`Failed to rename thread: HTTP ${response.status}`);
825
+ }
826
+ }
827
+ /**
828
+ * Archive a thread.
829
+ */
830
+ async archiveThread(sessionId) {
831
+ const url = `${this.baseUrl}/api/sessions/${encodeURIComponent(sessionId)}/archive`;
832
+ const response = await fetch(url, {
833
+ method: "POST",
834
+ headers: withSessionHeader(sessionId)
835
+ });
836
+ if (!response.ok) {
837
+ throw new Error(`Failed to archive thread: HTTP ${response.status}`);
838
+ }
839
+ }
840
+ /**
841
+ * Unarchive a thread.
842
+ */
843
+ async unarchiveThread(sessionId) {
844
+ const url = `${this.baseUrl}/api/sessions/${encodeURIComponent(sessionId)}/unarchive`;
845
+ const response = await fetch(url, {
846
+ method: "POST",
847
+ headers: withSessionHeader(sessionId)
848
+ });
849
+ if (!response.ok) {
850
+ throw new Error(`Failed to unarchive thread: HTTP ${response.status}`);
851
+ }
852
+ }
853
+ // ===========================================================================
854
+ // System Events
855
+ // ===========================================================================
856
+ /**
857
+ * Get system events for a session.
858
+ */
859
+ async getSystemEvents(sessionId, count) {
860
+ const url = new URL("/api/events", this.baseUrl);
861
+ if (count !== void 0) {
862
+ url.searchParams.set("count", String(count));
863
+ }
864
+ const response = await fetch(url.toString(), {
865
+ headers: withSessionHeader(sessionId)
866
+ });
867
+ if (!response.ok) {
868
+ if (response.status === 404) return [];
869
+ throw new Error(`Failed to get system events: HTTP ${response.status}`);
870
+ }
871
+ return await response.json();
872
+ }
873
+ // ===========================================================================
874
+ // Control API
875
+ // ===========================================================================
876
+ /**
877
+ * Get available namespaces.
878
+ */
879
+ async getNamespaces(sessionId, options) {
880
+ var _a3;
881
+ const url = new URL("/api/control/namespaces", this.baseUrl);
882
+ if (options == null ? void 0 : options.publicKey) {
883
+ url.searchParams.set("public_key", options.publicKey);
884
+ }
885
+ const apiKey = (_a3 = options == null ? void 0 : options.apiKey) != null ? _a3 : this.apiKey;
886
+ const headers = new Headers(withSessionHeader(sessionId));
887
+ if (apiKey) {
888
+ headers.set(API_KEY_HEADER, apiKey);
889
+ }
890
+ const response = await fetch(url.toString(), { headers });
891
+ if (!response.ok) {
892
+ throw new Error(`Failed to get namespaces: HTTP ${response.status}`);
893
+ }
894
+ return await response.json();
895
+ }
896
+ /**
897
+ * Get available models.
898
+ */
899
+ async getModels(sessionId, options) {
900
+ var _a3;
901
+ const url = new URL("/api/control/models", this.baseUrl);
902
+ const apiKey = (_a3 = options == null ? void 0 : options.apiKey) != null ? _a3 : this.apiKey;
903
+ const headers = new Headers(withSessionHeader(sessionId));
904
+ if (apiKey) {
905
+ headers.set(API_KEY_HEADER, apiKey);
906
+ }
907
+ const response = await fetch(url.toString(), {
908
+ headers
909
+ });
910
+ if (!response.ok) {
911
+ throw new Error(`Failed to get models: HTTP ${response.status}`);
912
+ }
913
+ return await response.json();
914
+ }
915
+ /**
916
+ * Set the model for a session.
917
+ */
918
+ async setModel(sessionId, rig, options) {
919
+ var _a3;
920
+ const apiKey = (_a3 = options == null ? void 0 : options.apiKey) != null ? _a3 : this.apiKey;
921
+ const payload = { rig };
922
+ if (options == null ? void 0 : options.namespace) {
923
+ payload.namespace = options.namespace;
924
+ }
925
+ return postState(this.baseUrl, "/api/control/model", payload, sessionId, apiKey);
926
+ }
927
+ };
928
+
929
+ // src/event-emitter.ts
930
+ var TypedEventEmitter = class {
931
+ constructor() {
932
+ this.listeners = /* @__PURE__ */ new Map();
933
+ }
934
+ /**
935
+ * Subscribe to an event type. Returns an unsubscribe function.
936
+ */
937
+ on(type, handler) {
938
+ let set = this.listeners.get(type);
939
+ if (!set) {
940
+ set = /* @__PURE__ */ new Set();
941
+ this.listeners.set(type, set);
942
+ }
943
+ set.add(handler);
944
+ return () => {
945
+ set.delete(handler);
946
+ if (set.size === 0) {
947
+ this.listeners.delete(type);
948
+ }
949
+ };
950
+ }
951
+ /**
952
+ * Subscribe to an event type for a single emission, then auto-unsubscribe.
953
+ */
954
+ once(type, handler) {
955
+ const wrapper = ((payload) => {
956
+ unsub();
957
+ handler(payload);
958
+ });
959
+ const unsub = this.on(type, wrapper);
960
+ return unsub;
961
+ }
962
+ /**
963
+ * Emit an event to all listeners of `type` and wildcard `"*"` listeners.
964
+ */
965
+ emit(type, payload) {
966
+ const typeSet = this.listeners.get(type);
967
+ if (typeSet) {
968
+ for (const handler of typeSet) {
969
+ handler(payload);
970
+ }
971
+ }
972
+ if (type !== "*") {
973
+ const wildcardSet = this.listeners.get("*");
974
+ if (wildcardSet) {
975
+ for (const handler of wildcardSet) {
976
+ handler({ type, payload });
977
+ }
978
+ }
979
+ }
980
+ }
981
+ /**
982
+ * Remove a specific handler from an event type.
983
+ */
984
+ off(type, handler) {
985
+ const set = this.listeners.get(type);
986
+ if (set) {
987
+ set.delete(handler);
988
+ if (set.size === 0) {
989
+ this.listeners.delete(type);
990
+ }
991
+ }
992
+ }
993
+ /**
994
+ * Remove all listeners for all event types.
995
+ */
996
+ removeAllListeners() {
997
+ this.listeners.clear();
998
+ }
999
+ };
1000
+
1001
+ // src/types.ts
1002
+ function isInlineCall(event) {
1003
+ return "InlineCall" in event;
1004
+ }
1005
+ function isSystemNotice(event) {
1006
+ return "SystemNotice" in event;
1007
+ }
1008
+ function isSystemError(event) {
1009
+ return "SystemError" in event;
1010
+ }
1011
+ function isAsyncCallback(event) {
1012
+ return "AsyncCallback" in event;
1013
+ }
1014
+
1015
+ // src/event-unwrap.ts
1016
+ function unwrapSystemEvent(event) {
1017
+ var _a3;
1018
+ if (isInlineCall(event)) {
1019
+ return {
1020
+ type: event.InlineCall.type,
1021
+ payload: (_a3 = event.InlineCall.payload) != null ? _a3 : event.InlineCall
1022
+ };
1023
+ }
1024
+ if (isSystemNotice(event)) {
1025
+ return {
1026
+ type: "system_notice",
1027
+ payload: { message: event.SystemNotice }
1028
+ };
1029
+ }
1030
+ if (isSystemError(event)) {
1031
+ return {
1032
+ type: "system_error",
1033
+ payload: { message: event.SystemError }
1034
+ };
1035
+ }
1036
+ if (isAsyncCallback(event)) {
1037
+ return {
1038
+ type: "async_callback",
1039
+ payload: event.AsyncCallback
1040
+ };
1041
+ }
1042
+ return null;
1043
+ }
1044
+
1045
+ // src/wallet-utils.ts
1046
+ function asRecord(value) {
1047
+ if (!value || typeof value !== "object" || Array.isArray(value))
1048
+ return void 0;
1049
+ return value;
1050
+ }
1051
+ function getToolArgs(payload) {
1052
+ var _a3;
1053
+ const root = asRecord(payload);
1054
+ const nestedArgs = asRecord(root == null ? void 0 : root.args);
1055
+ return (_a3 = nestedArgs != null ? nestedArgs : root) != null ? _a3 : {};
1056
+ }
1057
+ function parseChainId(value) {
1058
+ if (typeof value === "number" && Number.isFinite(value)) return value;
1059
+ if (typeof value !== "string") return void 0;
1060
+ const trimmed = value.trim();
1061
+ if (!trimmed) return void 0;
1062
+ if (trimmed.startsWith("0x")) {
1063
+ const parsedHex = Number.parseInt(trimmed.slice(2), 16);
1064
+ return Number.isFinite(parsedHex) ? parsedHex : void 0;
1065
+ }
1066
+ const parsed = Number.parseInt(trimmed, 10);
1067
+ return Number.isFinite(parsed) ? parsed : void 0;
1068
+ }
1069
+ function normalizeTxPayload(payload) {
1070
+ var _a3, _b, _c;
1071
+ const root = asRecord(payload);
1072
+ const args = getToolArgs(payload);
1073
+ const ctx = asRecord(root == null ? void 0 : root.ctx);
1074
+ const to = typeof args.to === "string" ? args.to : void 0;
1075
+ if (!to) return null;
1076
+ const valueRaw = args.value;
1077
+ const value = typeof valueRaw === "string" ? valueRaw : typeof valueRaw === "number" && Number.isFinite(valueRaw) ? String(Math.trunc(valueRaw)) : void 0;
1078
+ const data = typeof args.data === "string" ? args.data : void 0;
1079
+ const chainId = (_c = (_b = (_a3 = parseChainId(args.chainId)) != null ? _a3 : parseChainId(args.chain_id)) != null ? _b : parseChainId(ctx == null ? void 0 : ctx.user_chain_id)) != null ? _c : parseChainId(ctx == null ? void 0 : ctx.userChainId);
1080
+ return { to, value, data, chainId };
1081
+ }
1082
+ function normalizeEip712Payload(payload) {
1083
+ var _a3;
1084
+ const args = getToolArgs(payload);
1085
+ const typedDataRaw = (_a3 = args.typed_data) != null ? _a3 : args.typedData;
1086
+ let typedData;
1087
+ if (typeof typedDataRaw === "string") {
1088
+ try {
1089
+ const parsed = JSON.parse(typedDataRaw);
1090
+ if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
1091
+ typedData = parsed;
1092
+ }
1093
+ } catch (e) {
1094
+ typedData = void 0;
1095
+ }
1096
+ } else if (typedDataRaw && typeof typedDataRaw === "object" && !Array.isArray(typedDataRaw)) {
1097
+ typedData = typedDataRaw;
1098
+ }
1099
+ const description = typeof args.description === "string" ? args.description : void 0;
1100
+ return { typed_data: typedData, description };
1101
+ }
1102
+
1103
+ // src/session.ts
1104
+ var Session = class extends TypedEventEmitter {
1105
+ constructor(clientOrOptions, sessionOptions) {
1106
+ var _a3, _b, _c;
1107
+ super();
1108
+ // Internal state
1109
+ this.pollTimer = null;
1110
+ this.unsubscribeSSE = null;
1111
+ this._isProcessing = false;
1112
+ this.walletRequests = [];
1113
+ this.walletRequestNextId = 1;
1114
+ this._messages = [];
1115
+ this.closed = false;
1116
+ // For send() blocking behavior
1117
+ this.pendingResolve = null;
1118
+ this.client = clientOrOptions instanceof AomiClient ? clientOrOptions : new AomiClient(clientOrOptions);
1119
+ this.sessionId = (_a3 = sessionOptions == null ? void 0 : sessionOptions.sessionId) != null ? _a3 : crypto.randomUUID();
1120
+ this.namespace = (_b = sessionOptions == null ? void 0 : sessionOptions.namespace) != null ? _b : "default";
1121
+ this.publicKey = sessionOptions == null ? void 0 : sessionOptions.publicKey;
1122
+ this.apiKey = sessionOptions == null ? void 0 : sessionOptions.apiKey;
1123
+ this.userState = sessionOptions == null ? void 0 : sessionOptions.userState;
1124
+ this.pollIntervalMs = (_c = sessionOptions == null ? void 0 : sessionOptions.pollIntervalMs) != null ? _c : 500;
1125
+ this.logger = sessionOptions == null ? void 0 : sessionOptions.logger;
1126
+ this.unsubscribeSSE = this.client.subscribeSSE(
1127
+ this.sessionId,
1128
+ (event) => this.handleSSEEvent(event),
1129
+ (error) => this.emit("error", { error })
1130
+ );
1131
+ }
1132
+ // ===========================================================================
1133
+ // Public API — Chat
1134
+ // ===========================================================================
1135
+ /**
1136
+ * Send a message and wait for the AI to finish processing.
1137
+ *
1138
+ * The returned promise resolves when `is_processing` becomes `false` AND
1139
+ * there are no pending wallet requests. If a wallet request arrives
1140
+ * mid-processing, polling continues but the promise pauses until the
1141
+ * request is resolved or rejected via `resolve()` / `reject()`.
1142
+ */
1143
+ async send(message) {
1144
+ this.assertOpen();
1145
+ const response = await this.client.sendMessage(this.sessionId, message, {
1146
+ namespace: this.namespace,
1147
+ publicKey: this.publicKey,
1148
+ apiKey: this.apiKey,
1149
+ userState: this.userState
1150
+ });
1151
+ this.applyState(response);
1152
+ if (!response.is_processing && this.walletRequests.length === 0) {
1153
+ return { messages: this._messages, title: this._title };
1154
+ }
1155
+ this._isProcessing = true;
1156
+ this.emit("processing_start", void 0);
1157
+ return new Promise((resolve) => {
1158
+ this.pendingResolve = resolve;
1159
+ this.startPolling();
1160
+ });
1161
+ }
1162
+ /**
1163
+ * Send a message without waiting for completion.
1164
+ * Polling starts in the background; listen to events for updates.
1165
+ */
1166
+ async sendAsync(message) {
1167
+ this.assertOpen();
1168
+ const response = await this.client.sendMessage(this.sessionId, message, {
1169
+ namespace: this.namespace,
1170
+ publicKey: this.publicKey,
1171
+ apiKey: this.apiKey,
1172
+ userState: this.userState
1173
+ });
1174
+ this.applyState(response);
1175
+ if (response.is_processing) {
1176
+ this._isProcessing = true;
1177
+ this.emit("processing_start", void 0);
1178
+ this.startPolling();
1179
+ }
1180
+ return response;
1181
+ }
1182
+ // ===========================================================================
1183
+ // Public API — Wallet Request Resolution
1184
+ // ===========================================================================
1185
+ /**
1186
+ * Resolve a pending wallet request (transaction or EIP-712 signing).
1187
+ * Sends the result to the backend and resumes polling.
1188
+ */
1189
+ async resolve(requestId, result) {
1190
+ var _a3;
1191
+ const req = this.removeWalletRequest(requestId);
1192
+ if (!req) {
1193
+ throw new Error(`No pending wallet request with id "${requestId}"`);
1194
+ }
1195
+ if (req.kind === "transaction") {
1196
+ await this.sendSystemEvent("wallet:tx_complete", {
1197
+ txHash: (_a3 = result.txHash) != null ? _a3 : "",
1198
+ status: "success",
1199
+ amount: result.amount
1200
+ });
1201
+ } else {
1202
+ const eip712Payload = req.payload;
1203
+ await this.sendSystemEvent("wallet_eip712_response", {
1204
+ status: "success",
1205
+ signature: result.signature,
1206
+ description: eip712Payload.description
1207
+ });
1208
+ }
1209
+ if (this._isProcessing) {
1210
+ this.startPolling();
1211
+ }
1212
+ }
1213
+ /**
1214
+ * Reject a pending wallet request.
1215
+ * Sends an error to the backend and resumes polling.
1216
+ */
1217
+ async reject(requestId, reason) {
1218
+ const req = this.removeWalletRequest(requestId);
1219
+ if (!req) {
1220
+ throw new Error(`No pending wallet request with id "${requestId}"`);
1221
+ }
1222
+ if (req.kind === "transaction") {
1223
+ await this.sendSystemEvent("wallet:tx_complete", {
1224
+ txHash: "",
1225
+ status: "failed"
1226
+ });
1227
+ } else {
1228
+ const eip712Payload = req.payload;
1229
+ await this.sendSystemEvent("wallet_eip712_response", {
1230
+ status: "failed",
1231
+ error: reason != null ? reason : "Request rejected",
1232
+ description: eip712Payload.description
1233
+ });
1234
+ }
1235
+ if (this._isProcessing) {
1236
+ this.startPolling();
1237
+ }
1238
+ }
1239
+ // ===========================================================================
1240
+ // Public API — Control
1241
+ // ===========================================================================
1242
+ /**
1243
+ * Cancel the AI's current response.
1244
+ */
1245
+ async interrupt() {
1246
+ this.stopPolling();
1247
+ const response = await this.client.interrupt(this.sessionId);
1248
+ this.applyState(response);
1249
+ this._isProcessing = false;
1250
+ this.emit("processing_end", void 0);
1251
+ this.resolvePending();
1252
+ }
1253
+ /**
1254
+ * Close the session. Stops polling, unsubscribes SSE, removes all listeners.
1255
+ * The session cannot be used after closing.
1256
+ */
1257
+ close() {
1258
+ var _a3;
1259
+ if (this.closed) return;
1260
+ this.closed = true;
1261
+ this.stopPolling();
1262
+ (_a3 = this.unsubscribeSSE) == null ? void 0 : _a3.call(this);
1263
+ this.unsubscribeSSE = null;
1264
+ this.resolvePending();
1265
+ this.removeAllListeners();
1266
+ }
1267
+ // ===========================================================================
1268
+ // Public API — Accessors
1269
+ // ===========================================================================
1270
+ /** Current messages in the session. */
1271
+ getMessages() {
1272
+ return this._messages;
1273
+ }
1274
+ /** Current session title. */
1275
+ getTitle() {
1276
+ return this._title;
1277
+ }
1278
+ /** Pending wallet requests waiting for resolve/reject. */
1279
+ getPendingRequests() {
1280
+ return [...this.walletRequests];
1281
+ }
1282
+ /** Whether the AI is currently processing. */
1283
+ getIsProcessing() {
1284
+ return this._isProcessing;
1285
+ }
1286
+ // ===========================================================================
1287
+ // Internal — Polling (ported from PollingController)
1288
+ // ===========================================================================
1289
+ startPolling() {
1290
+ var _a3;
1291
+ if (this.pollTimer || this.closed) return;
1292
+ (_a3 = this.logger) == null ? void 0 : _a3.debug("[session] polling started", this.sessionId);
1293
+ this.pollTimer = setInterval(() => {
1294
+ void this.pollTick();
1295
+ }, this.pollIntervalMs);
1296
+ }
1297
+ stopPolling() {
1298
+ var _a3;
1299
+ if (this.pollTimer) {
1300
+ clearInterval(this.pollTimer);
1301
+ this.pollTimer = null;
1302
+ (_a3 = this.logger) == null ? void 0 : _a3.debug("[session] polling stopped", this.sessionId);
1303
+ }
1304
+ }
1305
+ async pollTick() {
1306
+ var _a3;
1307
+ if (!this.pollTimer) return;
1308
+ try {
1309
+ const state = await this.client.fetchState(
1310
+ this.sessionId,
1311
+ this.userState
1312
+ );
1313
+ if (!this.pollTimer) return;
1314
+ this.applyState(state);
1315
+ if (!state.is_processing && this.walletRequests.length === 0) {
1316
+ this.stopPolling();
1317
+ this._isProcessing = false;
1318
+ this.emit("processing_end", void 0);
1319
+ this.resolvePending();
1320
+ }
1321
+ } catch (error) {
1322
+ (_a3 = this.logger) == null ? void 0 : _a3.debug("[session] poll error", error);
1323
+ this.emit("error", { error });
1324
+ }
1325
+ }
1326
+ // ===========================================================================
1327
+ // Internal — State Application
1328
+ // ===========================================================================
1329
+ applyState(state) {
1330
+ var _a3;
1331
+ if (state.messages) {
1332
+ this._messages = state.messages;
1333
+ this.emit("messages", this._messages);
1334
+ }
1335
+ if (state.title) {
1336
+ this._title = state.title;
1337
+ }
1338
+ if ((_a3 = state.system_events) == null ? void 0 : _a3.length) {
1339
+ this.dispatchSystemEvents(state.system_events);
1340
+ }
1341
+ }
1342
+ dispatchSystemEvents(events) {
1343
+ var _a3;
1344
+ for (const event of events) {
1345
+ const unwrapped = unwrapSystemEvent(event);
1346
+ if (!unwrapped) continue;
1347
+ if (unwrapped.type === "wallet_tx_request") {
1348
+ const payload = normalizeTxPayload(unwrapped.payload);
1349
+ if (payload) {
1350
+ const req = this.enqueueWalletRequest("transaction", payload);
1351
+ this.emit("wallet_tx_request", req);
1352
+ }
1353
+ } else if (unwrapped.type === "wallet_eip712_request") {
1354
+ const payload = normalizeEip712Payload((_a3 = unwrapped.payload) != null ? _a3 : {});
1355
+ const req = this.enqueueWalletRequest("eip712_sign", payload);
1356
+ this.emit("wallet_eip712_request", req);
1357
+ } else if (unwrapped.type === "system_notice" || unwrapped.type === "system_error" || unwrapped.type === "async_callback") {
1358
+ this.emit(
1359
+ unwrapped.type,
1360
+ unwrapped.payload
1361
+ );
1362
+ }
1363
+ }
1364
+ }
1365
+ // ===========================================================================
1366
+ // Internal — SSE Handling
1367
+ // ===========================================================================
1368
+ handleSSEEvent(event) {
1369
+ if (event.type === "title_changed" && event.new_title) {
1370
+ this._title = event.new_title;
1371
+ this.emit("title_changed", { title: event.new_title });
1372
+ } else if (event.type === "tool_update") {
1373
+ this.emit("tool_update", event);
1374
+ } else if (event.type === "tool_complete") {
1375
+ this.emit("tool_complete", event);
1376
+ }
1377
+ }
1378
+ // ===========================================================================
1379
+ // Internal — Wallet Request Queue
1380
+ // ===========================================================================
1381
+ enqueueWalletRequest(kind, payload) {
1382
+ const req = {
1383
+ id: `wreq-${this.walletRequestNextId++}`,
1384
+ kind,
1385
+ payload,
1386
+ timestamp: Date.now()
1387
+ };
1388
+ this.walletRequests.push(req);
1389
+ return req;
1390
+ }
1391
+ removeWalletRequest(id) {
1392
+ const idx = this.walletRequests.findIndex((r) => r.id === id);
1393
+ if (idx === -1) return null;
1394
+ return this.walletRequests.splice(idx, 1)[0];
1395
+ }
1396
+ // ===========================================================================
1397
+ // Internal — Helpers
1398
+ // ===========================================================================
1399
+ async sendSystemEvent(type, payload) {
1400
+ const message = JSON.stringify({ type, payload });
1401
+ await this.client.sendSystemMessage(this.sessionId, message);
1402
+ }
1403
+ resolvePending() {
1404
+ if (this.pendingResolve) {
1405
+ const resolve = this.pendingResolve;
1406
+ this.pendingResolve = null;
1407
+ resolve({ messages: this._messages, title: this._title });
1408
+ }
1409
+ }
1410
+ assertOpen() {
1411
+ if (this.closed) {
1412
+ throw new Error("Session is closed");
1413
+ }
1414
+ }
1415
+ };
1416
+
1417
+ // src/cli/context.ts
1418
+ function getOrCreateSession(runtime) {
1419
+ const { config } = runtime;
1420
+ let state = readState();
1421
+ if (!state) {
1422
+ state = {
1423
+ sessionId: crypto.randomUUID(),
1424
+ baseUrl: config.baseUrl,
1425
+ namespace: config.namespace,
1426
+ apiKey: config.apiKey,
1427
+ publicKey: config.publicKey
1428
+ };
1429
+ writeState(state);
1430
+ } else {
1431
+ let changed = false;
1432
+ if (config.baseUrl !== state.baseUrl) {
1433
+ state.baseUrl = config.baseUrl;
1434
+ changed = true;
1435
+ }
1436
+ if (config.namespace !== state.namespace) {
1437
+ state.namespace = config.namespace;
1438
+ changed = true;
1439
+ }
1440
+ if (config.apiKey !== void 0 && config.apiKey !== state.apiKey) {
1441
+ state.apiKey = config.apiKey;
1442
+ changed = true;
1443
+ }
1444
+ if (config.publicKey !== void 0 && config.publicKey !== state.publicKey) {
1445
+ state.publicKey = config.publicKey;
1446
+ changed = true;
1447
+ }
1448
+ if (changed) writeState(state);
1449
+ }
1450
+ const session = new Session(
1451
+ { baseUrl: state.baseUrl, apiKey: state.apiKey },
1452
+ {
1453
+ sessionId: state.sessionId,
1454
+ namespace: state.namespace,
1455
+ apiKey: state.apiKey,
1456
+ publicKey: state.publicKey,
1457
+ userState: state.publicKey ? {
1458
+ address: state.publicKey,
1459
+ chainId: 1,
1460
+ isConnected: true
1461
+ } : void 0
1462
+ }
1463
+ );
1464
+ return { session, state };
1465
+ }
1466
+ function createControlClient(runtime) {
1467
+ return new AomiClient({
1468
+ baseUrl: runtime.config.baseUrl,
1469
+ apiKey: runtime.config.apiKey
1470
+ });
1471
+ }
1472
+ async function applyModelSelection(session, state, model) {
1473
+ await session.client.setModel(state.sessionId, model, {
1474
+ namespace: state.namespace,
1475
+ apiKey: state.apiKey
1476
+ });
1477
+ state.model = model;
1478
+ writeState(state);
1479
+ }
1480
+ async function applyRequestedModelIfPresent(runtime, session, state) {
1481
+ const requestedModel = runtime.config.model;
1482
+ if (!requestedModel || requestedModel === state.model) {
1483
+ return;
1484
+ }
1485
+ await applyModelSelection(session, state, requestedModel);
1486
+ }
1487
+
1488
+ // src/cli/errors.ts
1489
+ var CliExit = class extends Error {
1490
+ constructor(code) {
1491
+ super();
1492
+ this.code = code;
1493
+ }
1494
+ };
1495
+ function fatal(message) {
1496
+ const RED = "\x1B[31m";
1497
+ const DIM3 = "\x1B[2m";
1498
+ const RESET3 = "\x1B[0m";
1499
+ const lines = message.split("\n");
1500
+ const [headline, ...details] = lines;
1501
+ console.error(`${RED}\u274C ${headline}${RESET3}`);
1502
+ for (const detail of details) {
1503
+ if (!detail.trim()) {
1504
+ console.error("");
1505
+ continue;
1506
+ }
1507
+ console.error(`${DIM3}${detail}${RESET3}`);
1508
+ }
1509
+ throw new CliExit(1);
1510
+ }
1511
+
1512
+ // src/cli/transactions.ts
1513
+ function walletRequestToPendingTx(request) {
1514
+ if (request.kind === "transaction") {
1515
+ const payload2 = request.payload;
1516
+ return {
1517
+ kind: "transaction",
1518
+ to: payload2.to,
1519
+ value: payload2.value,
1520
+ data: payload2.data,
1521
+ chainId: payload2.chainId,
1522
+ timestamp: request.timestamp,
1523
+ payload: request.payload
1524
+ };
1525
+ }
1526
+ const payload = request.payload;
1527
+ return {
1528
+ kind: "eip712_sign",
1529
+ description: payload.description,
1530
+ timestamp: request.timestamp,
1531
+ payload: request.payload
1532
+ };
1533
+ }
1534
+ function formatTxLine(tx, prefix) {
1535
+ var _a3;
1536
+ const parts = [`${prefix} ${tx.id}`];
1537
+ if (tx.kind === "transaction") {
1538
+ parts.push(`to: ${(_a3 = tx.to) != null ? _a3 : "?"}`);
1539
+ if (tx.value) parts.push(`value: ${tx.value}`);
1540
+ if (tx.chainId) parts.push(`chain: ${tx.chainId}`);
1541
+ if (tx.data) parts.push(`data: ${tx.data.slice(0, 20)}...`);
1542
+ } else {
1543
+ parts.push("eip712");
1544
+ if (tx.description) parts.push(tx.description);
1545
+ }
1546
+ parts.push(`(${new Date(tx.timestamp).toLocaleTimeString()})`);
1547
+ return parts.join(" ");
1548
+ }
1549
+
1550
+ // src/cli/commands/chat.ts
1551
+ async function chatCommand(runtime) {
1552
+ const message = runtime.parsed.positional.join(" ");
1553
+ if (!message) {
1554
+ fatal("Usage: aomi chat <message>");
1555
+ }
1556
+ const verbose = runtime.parsed.flags["verbose"] === "true" || runtime.parsed.flags["v"] === "true";
1557
+ const { session, state } = getOrCreateSession(runtime);
1558
+ try {
1559
+ await applyRequestedModelIfPresent(runtime, session, state);
1560
+ if (state.publicKey) {
1561
+ await session.client.sendSystemMessage(
1562
+ session.sessionId,
1563
+ JSON.stringify({
1564
+ type: "wallet:state_changed",
1565
+ payload: {
1566
+ address: state.publicKey,
1567
+ chainId: 1,
1568
+ isConnected: true
1569
+ }
1570
+ })
1571
+ );
1572
+ }
1573
+ const capturedRequests = [];
1574
+ let printedAgentCount = 0;
1575
+ const seenToolResults = /* @__PURE__ */ new Set();
1576
+ session.on("wallet_tx_request", (request) => capturedRequests.push(request));
1577
+ session.on(
1578
+ "wallet_eip712_request",
1579
+ (request) => capturedRequests.push(request)
1580
+ );
1581
+ session.on("tool_complete", (event) => {
1582
+ const name = getToolNameFromEvent(event);
1583
+ const result = getToolResultFromEvent(event);
1584
+ const key = toToolResultKey(name, result);
1585
+ seenToolResults.add(key);
1586
+ if (verbose || isAlwaysVisibleTool(name)) {
1587
+ printToolComplete(event);
1588
+ }
1589
+ });
1590
+ session.on("tool_update", (event) => {
1591
+ if (verbose) {
1592
+ printToolUpdate(event);
1593
+ }
1594
+ });
1595
+ if (verbose) {
1596
+ session.on("processing_start", () => {
1597
+ console.log(`${DIM}\u23F3 Processing\u2026${RESET}`);
1598
+ });
1599
+ session.on("system_notice", ({ message: msg }) => {
1600
+ console.log(`${YELLOW}\u{1F4E2} ${msg}${RESET}`);
1601
+ });
1602
+ session.on("system_error", ({ message: msg }) => {
1603
+ console.log(`\x1B[31m\u274C ${msg}${RESET}`);
1604
+ });
1605
+ }
1606
+ await session.sendAsync(message);
1607
+ const allMessages = session.getMessages();
1608
+ let seedIdx = allMessages.length;
1609
+ for (let i = allMessages.length - 1; i >= 0; i--) {
1610
+ if (allMessages[i].sender === "user") {
1611
+ seedIdx = i;
1612
+ break;
1613
+ }
1614
+ }
1615
+ printedAgentCount = allMessages.slice(0, seedIdx).filter(
1616
+ (entry) => entry.sender === "agent" || entry.sender === "assistant"
1617
+ ).length;
1618
+ if (verbose) {
1619
+ printedAgentCount = printNewAgentMessages(
1620
+ allMessages,
1621
+ printedAgentCount
1622
+ );
1623
+ session.on("messages", (messages) => {
1624
+ printedAgentCount = printNewAgentMessages(messages, printedAgentCount);
1625
+ });
1626
+ }
1627
+ if (session.getIsProcessing()) {
1628
+ await new Promise((resolve) => {
1629
+ const checkWallet = () => {
1630
+ if (capturedRequests.length > 0) resolve();
1631
+ };
1632
+ session.on("wallet_tx_request", checkWallet);
1633
+ session.on("wallet_eip712_request", checkWallet);
1634
+ session.on("processing_end", () => resolve());
1635
+ });
1636
+ }
1637
+ const messageToolResults = getMessageToolResults(
1638
+ session.getMessages(),
1639
+ seedIdx + 1
1640
+ );
1641
+ if (verbose) {
1642
+ for (const tool of messageToolResults) {
1643
+ const key = toToolResultKey(tool.name, tool.result);
1644
+ if (seenToolResults.has(key)) {
1645
+ continue;
1646
+ }
1647
+ printToolResultLine(tool.name, tool.result);
1648
+ }
1649
+ } else {
1650
+ for (const tool of messageToolResults) {
1651
+ const key = toToolResultKey(tool.name, tool.result);
1652
+ if (seenToolResults.has(key)) {
1653
+ continue;
1654
+ }
1655
+ if (isAlwaysVisibleTool(tool.name)) {
1656
+ printToolResultLine(tool.name, tool.result);
1657
+ }
1658
+ }
1659
+ }
1660
+ if (verbose) {
1661
+ printedAgentCount = printNewAgentMessages(
1662
+ session.getMessages(),
1663
+ printedAgentCount
1664
+ );
1665
+ console.log(`${DIM}\u2705 Done${RESET}`);
1666
+ }
1667
+ for (const request of capturedRequests) {
1668
+ const pending = addPendingTx(state, walletRequestToPendingTx(request));
1669
+ console.log(`\u26A1 Wallet request queued: ${pending.id}`);
1670
+ if (request.kind === "transaction") {
1671
+ const payload = request.payload;
1672
+ console.log(` to: ${payload.to}`);
1673
+ if (payload.value) console.log(` value: ${payload.value}`);
1674
+ if (payload.chainId) console.log(` chain: ${payload.chainId}`);
1675
+ } else {
1676
+ const payload = request.payload;
1677
+ if (payload.description) {
1678
+ console.log(` desc: ${payload.description}`);
1679
+ }
1680
+ }
1681
+ }
1682
+ if (!verbose) {
1683
+ const agentMessages = session.getMessages().filter(
1684
+ (entry) => entry.sender === "agent" || entry.sender === "assistant"
1685
+ );
1686
+ const last = agentMessages[agentMessages.length - 1];
1687
+ if (last == null ? void 0 : last.content) {
1688
+ console.log(last.content);
1689
+ } else if (capturedRequests.length === 0) {
1690
+ console.log("(no response)");
1691
+ }
1692
+ }
1693
+ if (capturedRequests.length > 0) {
1694
+ console.log(
1695
+ "\nRun `aomi tx` to see pending transactions, `aomi sign <id>` to sign."
1696
+ );
1697
+ }
1698
+ } finally {
1699
+ session.close();
1700
+ }
1701
+ }
1702
+
1703
+ // src/cli/commands/control.ts
1704
+ async function statusCommand(runtime) {
1705
+ var _a3, _b, _c, _d, _e, _f, _g, _h, _i;
1706
+ if (!readState()) {
1707
+ console.log("No active session");
1708
+ printDataFileLocation();
1709
+ return;
1710
+ }
1711
+ const { session, state } = getOrCreateSession(runtime);
1712
+ try {
1713
+ const apiState = await session.client.fetchState(state.sessionId);
1714
+ console.log(
1715
+ JSON.stringify(
1716
+ {
1717
+ sessionId: state.sessionId,
1718
+ baseUrl: state.baseUrl,
1719
+ namespace: state.namespace,
1720
+ model: (_a3 = state.model) != null ? _a3 : null,
1721
+ isProcessing: (_b = apiState.is_processing) != null ? _b : false,
1722
+ messageCount: (_d = (_c = apiState.messages) == null ? void 0 : _c.length) != null ? _d : 0,
1723
+ title: (_e = apiState.title) != null ? _e : null,
1724
+ pendingTxs: (_g = (_f = state.pendingTxs) == null ? void 0 : _f.length) != null ? _g : 0,
1725
+ signedTxs: (_i = (_h = state.signedTxs) == null ? void 0 : _h.length) != null ? _i : 0
1726
+ },
1727
+ null,
1728
+ 2
1729
+ )
1730
+ );
1731
+ printDataFileLocation();
1732
+ } finally {
1733
+ session.close();
1734
+ }
1735
+ }
1736
+ async function eventsCommand(runtime) {
1737
+ if (!readState()) {
1738
+ console.log("No active session");
1739
+ return;
1740
+ }
1741
+ const { session, state } = getOrCreateSession(runtime);
1742
+ try {
1743
+ const events = await session.client.getSystemEvents(state.sessionId);
1744
+ console.log(JSON.stringify(events, null, 2));
1745
+ } finally {
1746
+ session.close();
1747
+ }
1748
+ }
1749
+ async function modelsCommand(runtime) {
1750
+ var _a3, _b;
1751
+ const client = createControlClient(runtime);
1752
+ const state = readState();
1753
+ const sessionId = (_a3 = state == null ? void 0 : state.sessionId) != null ? _a3 : crypto.randomUUID();
1754
+ const models = await client.getModels(sessionId, {
1755
+ apiKey: (_b = runtime.config.apiKey) != null ? _b : state == null ? void 0 : state.apiKey
1756
+ });
1757
+ if (models.length === 0) {
1758
+ console.log("No models available.");
1759
+ return;
1760
+ }
1761
+ for (const model of models) {
1762
+ const marker = (state == null ? void 0 : state.model) === model ? " (current)" : "";
1763
+ console.log(`${model}${marker}`);
1764
+ }
1765
+ }
1766
+ async function modelCommand(runtime) {
1767
+ var _a3;
1768
+ const subcommand = runtime.parsed.positional[0];
1769
+ if (!subcommand || subcommand === "current") {
1770
+ const state2 = readState();
1771
+ if (!state2) {
1772
+ console.log("No active session");
1773
+ printDataFileLocation();
1774
+ return;
1775
+ }
1776
+ console.log((_a3 = state2.model) != null ? _a3 : "(default backend model)");
1777
+ printDataFileLocation();
1778
+ return;
1779
+ }
1780
+ if (subcommand !== "set") {
1781
+ fatal("Usage: aomi model set <rig>\n aomi model current");
1782
+ }
1783
+ const model = runtime.parsed.positional.slice(1).join(" ").trim();
1784
+ if (!model) {
1785
+ fatal("Usage: aomi model set <rig>");
1786
+ }
1787
+ const { session, state } = getOrCreateSession(runtime);
1788
+ try {
1789
+ await applyModelSelection(session, state, model);
1790
+ console.log(`Model set to ${model}`);
1791
+ printDataFileLocation();
1792
+ } finally {
1793
+ session.close();
1794
+ }
1795
+ }
1796
+
1797
+ // src/cli/tables.ts
1798
+ var MAX_TABLE_VALUE_WIDTH = 72;
1799
+ var MAX_TX_JSON_WIDTH = 96;
1800
+ var MAX_TX_ROWS = 8;
1801
+ function truncateCell(value, maxWidth) {
1802
+ if (value.length <= maxWidth) return value;
1803
+ return `${value.slice(0, maxWidth - 1)}\u2026`;
1804
+ }
1805
+ function padRight(value, width) {
1806
+ return value.padEnd(width, " ");
1807
+ }
1808
+ function estimateTokenCount(messages) {
1809
+ var _a3;
1810
+ let totalChars = 0;
1811
+ for (const message of messages) {
1812
+ const content = formatLogContent(message.content);
1813
+ if (content) {
1814
+ totalChars += content.length + 1;
1815
+ }
1816
+ if ((_a3 = message.tool_result) == null ? void 0 : _a3[1]) {
1817
+ totalChars += message.tool_result[1].length;
1818
+ }
1819
+ }
1820
+ return Math.round(totalChars / 4);
1821
+ }
1822
+ function toPendingTxMetadata(tx) {
1823
+ var _a3, _b, _c, _d;
1824
+ return {
1825
+ id: tx.id,
1826
+ kind: tx.kind,
1827
+ to: (_a3 = tx.to) != null ? _a3 : null,
1828
+ value: (_b = tx.value) != null ? _b : null,
1829
+ chainId: (_c = tx.chainId) != null ? _c : null,
1830
+ description: (_d = tx.description) != null ? _d : null,
1831
+ timestamp: new Date(tx.timestamp).toISOString()
1832
+ };
1833
+ }
1834
+ function toSignedTxMetadata(tx) {
1835
+ var _a3, _b, _c, _d, _e, _f, _g;
1836
+ return {
1837
+ id: tx.id,
1838
+ kind: tx.kind,
1839
+ txHash: (_a3 = tx.txHash) != null ? _a3 : null,
1840
+ signature: (_b = tx.signature) != null ? _b : null,
1841
+ from: (_c = tx.from) != null ? _c : null,
1842
+ to: (_d = tx.to) != null ? _d : null,
1843
+ value: (_e = tx.value) != null ? _e : null,
1844
+ chainId: (_f = tx.chainId) != null ? _f : null,
1845
+ description: (_g = tx.description) != null ? _g : null,
1846
+ timestamp: new Date(tx.timestamp).toISOString()
1847
+ };
1848
+ }
1849
+ function printKeyValueTable(rows, color = CYAN) {
1850
+ const labels = rows.map(([label]) => label);
1851
+ const values = rows.map(
1852
+ ([, value]) => truncateCell(value, MAX_TABLE_VALUE_WIDTH)
1853
+ );
1854
+ const keyWidth = Math.max("field".length, ...labels.map((label) => label.length));
1855
+ const valueWidth = Math.max("value".length, ...values.map((value) => value.length));
1856
+ const border = `+${"-".repeat(keyWidth + 2)}+${"-".repeat(valueWidth + 2)}+`;
1857
+ console.log(`${color}${border}${RESET}`);
1858
+ console.log(
1859
+ `${color}| ${padRight("field", keyWidth)} | ${padRight("value", valueWidth)} |${RESET}`
1860
+ );
1861
+ console.log(`${color}${border}${RESET}`);
1862
+ for (let i = 0; i < rows.length; i++) {
1863
+ console.log(
1864
+ `${color}| ${padRight(labels[i], keyWidth)} | ${padRight(values[i], valueWidth)} |${RESET}`
1865
+ );
1866
+ console.log(`${color}${border}${RESET}`);
1867
+ }
1868
+ }
1869
+ function printTransactionTable(pendingTxs, signedTxs, color = GREEN) {
1870
+ const rows = [
1871
+ ...pendingTxs.map((tx) => ({
1872
+ status: "pending",
1873
+ metadata: toPendingTxMetadata(tx)
1874
+ })),
1875
+ ...signedTxs.map((tx) => ({
1876
+ status: "signed",
1877
+ metadata: toSignedTxMetadata(tx)
1878
+ }))
1879
+ ];
1880
+ if (rows.length === 0) {
1881
+ console.log(`${YELLOW}No transactions in local CLI state.${RESET}`);
1882
+ return;
1883
+ }
1884
+ const visibleRows = rows.slice(0, MAX_TX_ROWS);
1885
+ const statusWidth = Math.max(
1886
+ "status".length,
1887
+ ...visibleRows.map((row) => row.status.length)
1888
+ );
1889
+ const jsonCells = visibleRows.map(
1890
+ (row) => truncateCell(JSON.stringify(row.metadata), MAX_TX_JSON_WIDTH)
1891
+ );
1892
+ const jsonWidth = Math.max("metadata_json".length, ...jsonCells.map((v) => v.length));
1893
+ const border = `+${"-".repeat(statusWidth + 2)}+${"-".repeat(jsonWidth + 2)}+`;
1894
+ console.log(`${color}${border}${RESET}`);
1895
+ console.log(
1896
+ `${color}| ${padRight("status", statusWidth)} | ${padRight("metadata_json", jsonWidth)} |${RESET}`
1897
+ );
1898
+ console.log(`${color}${border}${RESET}`);
1899
+ for (let i = 0; i < visibleRows.length; i++) {
1900
+ console.log(
1901
+ `${color}| ${padRight(visibleRows[i].status, statusWidth)} | ${padRight(jsonCells[i], jsonWidth)} |${RESET}`
1902
+ );
1903
+ console.log(`${color}${border}${RESET}`);
1904
+ }
1905
+ if (rows.length > MAX_TX_ROWS) {
1906
+ const omitted = rows.length - MAX_TX_ROWS;
1907
+ console.log(`${DIM}${omitted} transaction rows omitted${RESET}`);
1908
+ }
1909
+ }
1910
+
1911
+ // src/cli/commands/history.ts
1912
+ async function logCommand(runtime) {
1913
+ var _a3, _b, _c, _d, _e;
1914
+ if (!readState()) {
1915
+ console.log("No active session");
1916
+ printDataFileLocation();
1917
+ return;
1918
+ }
1919
+ const { session, state } = getOrCreateSession(runtime);
1920
+ try {
1921
+ const apiState = await session.client.fetchState(state.sessionId);
1922
+ const messages = (_a3 = apiState.messages) != null ? _a3 : [];
1923
+ const pendingTxs = (_b = state.pendingTxs) != null ? _b : [];
1924
+ const signedTxs = (_c = state.signedTxs) != null ? _c : [];
1925
+ const toolCalls = messages.filter((msg) => Boolean(msg.tool_result)).length;
1926
+ const tokenCountEstimate = estimateTokenCount(messages);
1927
+ const topic = (_d = apiState.title) != null ? _d : "Untitled Session";
1928
+ if (messages.length === 0) {
1929
+ console.log("No messages in this session.");
1930
+ printDataFileLocation();
1931
+ return;
1932
+ }
1933
+ console.log(`------ Session id: ${state.sessionId} ------`);
1934
+ printKeyValueTable([
1935
+ ["topic", topic],
1936
+ ["msg count", String(messages.length)],
1937
+ ["token count", `${tokenCountEstimate} (estimated)`],
1938
+ ["tool calls", String(toolCalls)],
1939
+ [
1940
+ "transactions",
1941
+ `${pendingTxs.length + signedTxs.length} (${pendingTxs.length} pending, ${signedTxs.length} signed)`
1942
+ ]
1943
+ ]);
1944
+ console.log("Transactions metadata (JSON):");
1945
+ printTransactionTable(pendingTxs, signedTxs);
1946
+ console.log("-------------------- Messages --------------------");
1947
+ for (const msg of messages) {
1948
+ const content = formatLogContent(msg.content);
1949
+ let time = "";
1950
+ if (msg.timestamp) {
1951
+ const raw = msg.timestamp;
1952
+ const numeric = /^\d+$/.test(raw) ? parseInt(raw, 10) : NaN;
1953
+ const date = !Number.isNaN(numeric) ? new Date(numeric < 1e12 ? numeric * 1e3 : numeric) : new Date(raw);
1954
+ time = Number.isNaN(date.getTime()) ? "" : `${DIM}${date.toLocaleTimeString()}${RESET} `;
1955
+ }
1956
+ const sender = (_e = msg.sender) != null ? _e : "unknown";
1957
+ if (sender === "user") {
1958
+ if (content) {
1959
+ console.log(`${time}${CYAN}\u{1F464} You:${RESET} ${content}`);
1960
+ }
1961
+ } else if (sender === "agent" || sender === "assistant") {
1962
+ if (msg.tool_result) {
1963
+ const [toolName, result] = msg.tool_result;
1964
+ console.log(
1965
+ `${time}${GREEN}\u{1F527} [${toolName}]${RESET} ${formatToolResultPreview(result)}`
1966
+ );
1967
+ }
1968
+ if (content) {
1969
+ console.log(`${time}${CYAN}\u{1F916} Agent:${RESET} ${content}`);
1970
+ }
1971
+ } else if (sender === "system") {
1972
+ if (content && !content.startsWith("Response of system endpoint:")) {
1973
+ console.log(`${time}${YELLOW}\u2699\uFE0F System:${RESET} ${content}`);
1974
+ }
1975
+ } else {
1976
+ if (content) {
1977
+ console.log(`${time}${DIM}[${sender}]${RESET} ${content}`);
1978
+ }
1979
+ }
1980
+ }
1981
+ console.log(`
1982
+ ${DIM}\u2014 ${messages.length} messages \u2014${RESET}`);
1983
+ printDataFileLocation();
1984
+ } finally {
1985
+ session.close();
1986
+ }
1987
+ }
1988
+ function closeCommand(runtime) {
1989
+ if (readState()) {
1990
+ const { session } = getOrCreateSession(runtime);
1991
+ session.close();
1992
+ }
1993
+ clearState();
1994
+ console.log("Session closed");
1995
+ }
1996
+
1997
+ // src/cli/commands/sessions.ts
1998
+ async function fetchRemoteSessionStats(record) {
1999
+ var _a3, _b;
2000
+ const client = new AomiClient({
2001
+ baseUrl: record.state.baseUrl,
2002
+ apiKey: record.state.apiKey
2003
+ });
2004
+ try {
2005
+ const apiState = await client.fetchState(record.sessionId);
2006
+ const messages = (_a3 = apiState.messages) != null ? _a3 : [];
2007
+ return {
2008
+ topic: (_b = apiState.title) != null ? _b : "Untitled Session",
2009
+ messageCount: messages.length,
2010
+ tokenCountEstimate: estimateTokenCount(messages),
2011
+ toolCalls: messages.filter((msg) => Boolean(msg.tool_result)).length
2012
+ };
2013
+ } catch (e) {
2014
+ return null;
2015
+ }
2016
+ }
2017
+ function printSessionSummary(record, stats, isActive) {
2018
+ var _a3, _b, _c;
2019
+ const pendingTxs = (_a3 = record.state.pendingTxs) != null ? _a3 : [];
2020
+ const signedTxs = (_b = record.state.signedTxs) != null ? _b : [];
2021
+ const header = isActive ? `\u{1F9F5} Session id: ${record.sessionId} (session-${record.localId}, active)` : `\u{1F9F5} Session id: ${record.sessionId} (session-${record.localId})`;
2022
+ console.log(`${YELLOW}------ ${header} ------${RESET}`);
2023
+ printKeyValueTable([
2024
+ ["\u{1F9E0} topic", (_c = stats == null ? void 0 : stats.topic) != null ? _c : "Unavailable (fetch failed)"],
2025
+ ["\u{1F4AC} msg count", stats ? String(stats.messageCount) : "n/a"],
2026
+ [
2027
+ "\u{1F9EE} token count",
2028
+ stats ? `${stats.tokenCountEstimate} (estimated)` : "n/a"
2029
+ ],
2030
+ ["\u{1F6E0} tool calls", stats ? String(stats.toolCalls) : "n/a"],
2031
+ [
2032
+ "\u{1F4B8} transactions",
2033
+ `${pendingTxs.length + signedTxs.length} (${pendingTxs.length} pending, ${signedTxs.length} signed)`
2034
+ ]
2035
+ ]);
2036
+ console.log();
2037
+ console.log(`${YELLOW}\u{1F4BE} Transactions metadata (JSON):${RESET}`);
2038
+ printTransactionTable(pendingTxs, signedTxs);
2039
+ }
2040
+ async function sessionsCommand(_runtime) {
2041
+ var _a3;
2042
+ const sessions = listStoredSessions().sort((a, b) => b.updatedAt - a.updatedAt);
2043
+ if (sessions.length === 0) {
2044
+ console.log("No local sessions.");
2045
+ printDataFileLocation();
2046
+ return;
2047
+ }
2048
+ const activeSessionId = (_a3 = readState()) == null ? void 0 : _a3.sessionId;
2049
+ const statsResults = await Promise.all(
2050
+ sessions.map((record) => fetchRemoteSessionStats(record))
2051
+ );
2052
+ for (let i = 0; i < sessions.length; i++) {
2053
+ printSessionSummary(
2054
+ sessions[i],
2055
+ statsResults[i],
2056
+ sessions[i].sessionId === activeSessionId
2057
+ );
2058
+ if (i < sessions.length - 1) {
2059
+ console.log();
2060
+ }
2061
+ }
2062
+ printDataFileLocation();
2063
+ }
2064
+ function sessionCommand(runtime) {
2065
+ const subcommand = runtime.parsed.positional[0];
2066
+ const selector = runtime.parsed.positional[1];
2067
+ if (subcommand === "resume") {
2068
+ if (!selector) {
2069
+ fatal("Usage: aomi session resume <session-id|session-N|N>");
2070
+ }
2071
+ const resumed = setActiveSession(selector);
2072
+ if (!resumed) {
2073
+ fatal(`No local session found for selector "${selector}".`);
2074
+ }
2075
+ console.log(`Active session set to ${resumed.sessionId} (session-${resumed.localId}).`);
2076
+ printDataFileLocation();
2077
+ return;
2078
+ }
2079
+ if (subcommand === "delete") {
2080
+ if (!selector) {
2081
+ fatal("Usage: aomi session delete <session-id|session-N|N>");
2082
+ }
2083
+ const deleted = deleteStoredSession(selector);
2084
+ if (!deleted) {
2085
+ fatal(`No local session found for selector "${selector}".`);
2086
+ }
2087
+ console.log(`Deleted local session ${deleted.sessionId} (session-${deleted.localId}).`);
2088
+ const active = readState();
2089
+ if (active) {
2090
+ console.log(`Active session: ${active.sessionId}`);
2091
+ } else {
2092
+ console.log("No active session");
2093
+ }
2094
+ printDataFileLocation();
2095
+ return;
2096
+ }
2097
+ fatal(
2098
+ "Usage: aomi session resume <session-id|session-N|N>\n aomi session delete <session-id|session-N|N>"
2099
+ );
2100
+ }
2101
+
2102
+ // src/cli/commands/wallet.ts
2103
+ import { createWalletClient, http } from "viem";
2104
+ import { privateKeyToAccount } from "viem/accounts";
2105
+ import * as viemChains from "viem/chains";
2106
+ function txCommand() {
2107
+ var _a3, _b, _c;
2108
+ const state = readState();
2109
+ if (!state) {
2110
+ console.log("No active session");
2111
+ printDataFileLocation();
2112
+ return;
2113
+ }
2114
+ const pending = (_a3 = state.pendingTxs) != null ? _a3 : [];
2115
+ const signed = (_b = state.signedTxs) != null ? _b : [];
2116
+ if (pending.length === 0 && signed.length === 0) {
2117
+ console.log("No transactions.");
2118
+ printDataFileLocation();
2119
+ return;
2120
+ }
2121
+ if (pending.length > 0) {
2122
+ console.log(`Pending (${pending.length}):`);
2123
+ for (const tx of pending) {
2124
+ console.log(formatTxLine(tx, " \u23F3"));
2125
+ }
2126
+ }
2127
+ if (signed.length > 0) {
2128
+ if (pending.length > 0) console.log();
2129
+ console.log(`Signed (${signed.length}):`);
2130
+ for (const tx of signed) {
2131
+ const parts = [` \u2705 ${tx.id}`];
2132
+ if (tx.kind === "eip712_sign") {
2133
+ parts.push(`sig: ${(_c = tx.signature) == null ? void 0 : _c.slice(0, 20)}...`);
2134
+ if (tx.description) parts.push(tx.description);
2135
+ } else {
2136
+ parts.push(`hash: ${tx.txHash}`);
2137
+ if (tx.to) parts.push(`to: ${tx.to}`);
2138
+ if (tx.value) parts.push(`value: ${tx.value}`);
2139
+ }
2140
+ parts.push(`(${new Date(tx.timestamp).toLocaleTimeString()})`);
2141
+ console.log(parts.join(" "));
2142
+ }
2143
+ }
2144
+ printDataFileLocation();
2145
+ }
2146
+ function requirePendingTx(state, txId) {
2147
+ var _a3;
2148
+ const pendingTx = ((_a3 = state.pendingTxs) != null ? _a3 : []).find((tx) => tx.id === txId);
2149
+ if (!pendingTx) {
2150
+ fatal(
2151
+ `No pending transaction with id "${txId}".
2152
+ Run \`aomi tx\` to see available IDs.`
2153
+ );
2154
+ }
2155
+ return pendingTx;
2156
+ }
2157
+ async function signCommand(runtime) {
2158
+ var _a3, _b, _c;
2159
+ const txId = runtime.parsed.positional[0];
2160
+ if (!txId) {
2161
+ fatal(
2162
+ "Usage: aomi sign <tx-id>\nRun `aomi tx` to see pending transaction IDs."
2163
+ );
2164
+ }
2165
+ const privateKey = runtime.config.privateKey;
2166
+ if (!privateKey) {
2167
+ fatal(
2168
+ [
2169
+ "Private key required for `aomi sign`.",
2170
+ "Pass one of:",
2171
+ " --private-key <hex-key>",
2172
+ " PRIVATE_KEY=<hex-key> aomi sign <tx-id>"
2173
+ ].join("\n")
2174
+ );
2175
+ }
2176
+ const state = readState();
2177
+ if (!state) {
2178
+ fatal("No active session. Run `aomi chat` first.");
2179
+ }
2180
+ const pendingTx = requirePendingTx(state, txId);
2181
+ const { session } = getOrCreateSession(runtime);
2182
+ try {
2183
+ const account = privateKeyToAccount(privateKey);
2184
+ const rpcUrl = runtime.config.chainRpcUrl;
2185
+ const targetChainId = (_a3 = pendingTx.chainId) != null ? _a3 : 1;
2186
+ const chain = (_b = Object.values(viemChains).find(
2187
+ (candidate) => typeof candidate === "object" && candidate !== null && "id" in candidate && candidate.id === targetChainId
2188
+ )) != null ? _b : {
2189
+ id: targetChainId,
2190
+ name: `Chain ${targetChainId}`,
2191
+ nativeCurrency: {
2192
+ name: "ETH",
2193
+ symbol: "ETH",
2194
+ decimals: 18
2195
+ },
2196
+ rpcUrls: {
2197
+ default: {
2198
+ http: [rpcUrl != null ? rpcUrl : ""]
2199
+ }
2200
+ }
2201
+ };
2202
+ const walletClient = createWalletClient({
2203
+ account,
2204
+ chain,
2205
+ transport: http(rpcUrl)
2206
+ });
2207
+ console.log(`Signer: ${account.address}`);
2208
+ console.log(`ID: ${pendingTx.id}`);
2209
+ console.log(`Kind: ${pendingTx.kind}`);
2210
+ if (pendingTx.kind === "transaction") {
2211
+ console.log(`To: ${pendingTx.to}`);
2212
+ if (pendingTx.value) console.log(`Value: ${pendingTx.value}`);
2213
+ if (pendingTx.chainId) console.log(`Chain: ${pendingTx.chainId}`);
2214
+ if (pendingTx.data) {
2215
+ console.log(`Data: ${pendingTx.data.slice(0, 40)}...`);
2216
+ }
2217
+ console.log();
2218
+ const hash = await walletClient.sendTransaction({
2219
+ to: pendingTx.to,
2220
+ value: pendingTx.value ? BigInt(pendingTx.value) : /* @__PURE__ */ BigInt("0"),
2221
+ data: (_c = pendingTx.data) != null ? _c : void 0
2222
+ });
2223
+ console.log(`\u2705 Sent! Hash: ${hash}`);
2224
+ removePendingTx(state, txId);
2225
+ const freshState = readState();
2226
+ addSignedTx(freshState, {
2227
+ id: txId,
2228
+ kind: "transaction",
2229
+ txHash: hash,
2230
+ from: account.address,
2231
+ to: pendingTx.to,
2232
+ value: pendingTx.value,
2233
+ chainId: pendingTx.chainId,
2234
+ timestamp: Date.now()
2235
+ });
2236
+ await session.client.sendSystemMessage(
2237
+ state.sessionId,
2238
+ JSON.stringify({
2239
+ type: "wallet:tx_complete",
2240
+ payload: { txHash: hash, status: "success" }
2241
+ })
2242
+ );
2243
+ } else {
2244
+ const typedData = pendingTx.payload.typed_data;
2245
+ if (!typedData) {
2246
+ fatal("EIP-712 request is missing typed_data payload.");
2247
+ }
2248
+ if (pendingTx.description) {
2249
+ console.log(`Desc: ${pendingTx.description}`);
2250
+ }
2251
+ if (typedData.primaryType) {
2252
+ console.log(`Type: ${typedData.primaryType}`);
2253
+ }
2254
+ console.log();
2255
+ const { domain, types, primaryType, message } = typedData;
2256
+ const sigTypes = __spreadValues({}, types);
2257
+ delete sigTypes["EIP712Domain"];
2258
+ const signature = await walletClient.signTypedData({
2259
+ domain,
2260
+ types: sigTypes,
2261
+ primaryType,
2262
+ message
2263
+ });
2264
+ console.log(`\u2705 Signed! Signature: ${signature.slice(0, 20)}...`);
2265
+ removePendingTx(state, txId);
2266
+ const freshState = readState();
2267
+ addSignedTx(freshState, {
2268
+ id: txId,
2269
+ kind: "eip712_sign",
2270
+ signature,
2271
+ from: account.address,
2272
+ description: pendingTx.description,
2273
+ timestamp: Date.now()
2274
+ });
2275
+ await session.client.sendSystemMessage(
2276
+ state.sessionId,
2277
+ JSON.stringify({
2278
+ type: "wallet_eip712_response",
2279
+ payload: {
2280
+ status: "success",
2281
+ signature,
2282
+ description: pendingTx.description
2283
+ }
2284
+ })
2285
+ );
2286
+ }
2287
+ console.log("Backend notified.");
2288
+ } catch (err) {
2289
+ if (err instanceof CliExit) throw err;
2290
+ const errMsg = err instanceof Error ? err.message : String(err);
2291
+ fatal(`\u274C Signing failed: ${errMsg}`);
2292
+ } finally {
2293
+ session.close();
2294
+ }
2295
+ }
2296
+
2297
+ // src/cli/main.ts
2298
+ function printUsage() {
2299
+ console.log(`
2300
+ aomi \u2014 CLI client for Aomi on-chain agent
2301
+
2302
+ Usage:
2303
+ aomi chat <message> Send a message and print the response
2304
+ aomi chat --model <rig>
2305
+ Set the session model before sending the message
2306
+ aomi chat --verbose Stream agent responses, tool calls, and events live
2307
+ aomi models List models available to the current backend
2308
+ aomi model set <rig> Set the active model for the current session
2309
+ aomi sessions List local sessions with metadata tables
2310
+ aomi session resume <id>
2311
+ Resume a local session (session-id or session-N)
2312
+ aomi session delete <id>
2313
+ Delete a local session file (session-id or session-N)
2314
+ aomi log Show full conversation history with tool results
2315
+ aomi tx List pending and signed transactions
2316
+ aomi sign <tx-id> Sign and submit a pending transaction
2317
+ aomi status Show current session state
2318
+ aomi events List system events
2319
+ aomi close Close the current session
2320
+
2321
+ Options:
2322
+ --backend-url <url> Backend URL (default: https://api.aomi.dev)
2323
+ --api-key <key> API key for non-default namespaces
2324
+ --namespace <ns> Namespace (default: "default")
2325
+ --model <rig> Set the active model for this session
2326
+ --public-key <addr> Wallet address (so the agent knows your wallet)
2327
+ --private-key <key> Hex private key for signing
2328
+ --rpc-url <url> RPC URL for transaction submission
2329
+ --verbose, -v Show tool calls and streaming output (for chat)
2330
+
2331
+ Environment (overridden by flags):
2332
+ AOMI_BASE_URL Backend URL
2333
+ AOMI_API_KEY API key
2334
+ AOMI_NAMESPACE Namespace
2335
+ AOMI_MODEL Model rig
2336
+ AOMI_PUBLIC_KEY Wallet address
2337
+ PRIVATE_KEY Hex private key for signing
2338
+ CHAIN_RPC_URL RPC URL for transaction submission
2339
+ `.trim());
2340
+ }
2341
+ async function main(runtime) {
2342
+ var _a3;
2343
+ const command = (_a3 = runtime.parsed.command) != null ? _a3 : runtime.parsed.flags["help"] || runtime.parsed.flags["h"] ? "help" : void 0;
2344
+ switch (command) {
2345
+ case "chat":
2346
+ await chatCommand(runtime);
2347
+ break;
2348
+ case "log":
2349
+ await logCommand(runtime);
2350
+ break;
2351
+ case "models":
2352
+ await modelsCommand(runtime);
2353
+ break;
2354
+ case "model":
2355
+ await modelCommand(runtime);
2356
+ break;
2357
+ case "sessions":
2358
+ await sessionsCommand(runtime);
2359
+ break;
2360
+ case "session":
2361
+ sessionCommand(runtime);
2362
+ break;
2363
+ case "tx":
2364
+ txCommand();
2365
+ break;
2366
+ case "sign":
2367
+ await signCommand(runtime);
2368
+ break;
2369
+ case "status":
2370
+ await statusCommand(runtime);
2371
+ break;
2372
+ case "events":
2373
+ await eventsCommand(runtime);
2374
+ break;
2375
+ case "close":
2376
+ closeCommand(runtime);
2377
+ break;
2378
+ case "help":
2379
+ printUsage();
2380
+ break;
2381
+ default:
2382
+ printUsage();
2383
+ if (command) {
2384
+ throw new CliExit(1);
2385
+ }
2386
+ }
2387
+ }
2388
+ function isPnpmExecWrapper() {
2389
+ var _a3, _b;
2390
+ const npmCommand = (_a3 = process.env.npm_command) != null ? _a3 : "";
2391
+ const userAgent = (_b = process.env.npm_config_user_agent) != null ? _b : "";
2392
+ return npmCommand === "exec" && userAgent.includes("pnpm/");
2393
+ }
2394
+ async function runCli(argv = process.argv) {
2395
+ const runtime = createRuntime(argv);
2396
+ const RED = "\x1B[31m";
2397
+ const RESET3 = "\x1B[0m";
2398
+ const strictExit = process.env.AOMI_CLI_STRICT_EXIT === "1";
2399
+ try {
2400
+ await main(runtime);
2401
+ } catch (err) {
2402
+ if (err instanceof CliExit) {
2403
+ if (!strictExit && isPnpmExecWrapper()) {
2404
+ return;
2405
+ }
2406
+ process.exit(err.code);
2407
+ return;
2408
+ }
2409
+ const message = err instanceof Error ? err.message : String(err);
2410
+ console.error(`${RED}\u274C ${message}${RESET3}`);
2411
+ process.exit(1);
2412
+ }
2413
+ }
2414
+
2415
+ // src/cli.ts
2416
+ void runCli();