@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/README.md +299 -0
- package/dist/cli.js +2416 -0
- package/dist/index.cjs +494 -4
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +258 -27
- package/dist/index.d.ts +258 -27
- package/dist/index.js +488 -3
- package/dist/index.js.map +1 -1
- package/package.json +10 -2
- package/skills/aomi-transact/SKILL.md +164 -0
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();
|