@aomi-labs/client 0.1.1 → 0.1.5

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