@elisym/cli 0.9.0 → 0.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +684 -464
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env -S node --no-deprecation
|
|
2
2
|
import { readFileSync, readdirSync, statSync, renameSync, mkdirSync, writeFileSync } from 'node:fs';
|
|
3
3
|
import { dirname, join, resolve, basename, relative, sep } from 'node:path';
|
|
4
|
-
import { SolanaPaymentStrategy, validateAgentName, RELAYS, ElisymIdentity, formatSol, formatAssetAmount, ElisymClient, MediaService, jobRequestKind, DEFAULT_KIND_OFFSET, toDTag, DEFAULTS,
|
|
4
|
+
import { SolanaPaymentStrategy, validateAgentName, RELAYS, ElisymIdentity, formatSol, formatAssetAmount, USDC_SOLANA_DEVNET, ElisymClient, MediaService, jobRequestKind, DEFAULT_KIND_OFFSET, toDTag, DEFAULTS, makeCensor, DEFAULT_REDACT_PATHS, createSlidingWindowLimiter, getProtocolProgramId, getProtocolConfig, LIMITS, calculateProtocolFee, BoundedSet, KIND_JOB_FEEDBACK, NATIVE_SOL } from '@elisym/sdk';
|
|
5
5
|
import { ElisymYamlSchema, resolveInHome, resolveInProject, createAgentDir, writeYaml, writeSecrets, listAgents, loadAgent, agentPaths, readMediaCache, lookupCachedUrl, newCacheEntry, writeMediaCache } from '@elisym/sdk/agent-store';
|
|
6
6
|
import { isAddress, createSolanaRpc, address } from '@solana/kit';
|
|
7
7
|
import { generateSecretKey, getPublicKey, nip19, verifyEvent } from 'nostr-tools';
|
|
@@ -25,42 +25,542 @@ var __export = (target, all) => {
|
|
|
25
25
|
__defProp(target, name, { get: all[name], enumerable: true });
|
|
26
26
|
};
|
|
27
27
|
|
|
28
|
-
// src/
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
28
|
+
// src/llm/providers/http.ts
|
|
29
|
+
function createAbortError() {
|
|
30
|
+
const err = new Error("The operation was aborted");
|
|
31
|
+
err.name = "AbortError";
|
|
32
|
+
return err;
|
|
33
|
+
}
|
|
34
|
+
function sleepWithSignal(ms, signal) {
|
|
35
|
+
if (signal?.aborted) {
|
|
36
|
+
return Promise.reject(createAbortError());
|
|
37
|
+
}
|
|
38
|
+
if (!signal) {
|
|
39
|
+
return new Promise((resolve3) => setTimeout(resolve3, ms));
|
|
40
|
+
}
|
|
41
|
+
return new Promise((resolve3, reject) => {
|
|
42
|
+
const cleanup = () => {
|
|
43
|
+
clearTimeout(timer);
|
|
44
|
+
signal.removeEventListener("abort", onAbort);
|
|
45
|
+
};
|
|
46
|
+
const onAbort = () => {
|
|
47
|
+
cleanup();
|
|
48
|
+
reject(createAbortError());
|
|
49
|
+
};
|
|
50
|
+
const timer = setTimeout(() => {
|
|
51
|
+
cleanup();
|
|
52
|
+
resolve3();
|
|
53
|
+
}, ms);
|
|
54
|
+
signal.addEventListener("abort", onAbort, { once: true });
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
async function fetchWithTimeout(url, init, signal) {
|
|
58
|
+
if (signal?.aborted) {
|
|
59
|
+
throw createAbortError();
|
|
60
|
+
}
|
|
61
|
+
const controller = new AbortController();
|
|
62
|
+
const timer = setTimeout(() => controller.abort(), LLM_TIMEOUT_MS);
|
|
63
|
+
const onAbort = () => controller.abort();
|
|
64
|
+
signal?.addEventListener("abort", onAbort, { once: true });
|
|
65
|
+
try {
|
|
66
|
+
return await fetch(url, { ...init, signal: controller.signal });
|
|
67
|
+
} finally {
|
|
68
|
+
clearTimeout(timer);
|
|
69
|
+
signal?.removeEventListener("abort", onAbort);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
async function fetchWithRetry(url, init, signal) {
|
|
73
|
+
for (let attempt = 0; ; attempt++) {
|
|
74
|
+
let response;
|
|
75
|
+
try {
|
|
76
|
+
response = await fetchWithTimeout(url, init, signal);
|
|
77
|
+
} catch (error) {
|
|
78
|
+
const name = error instanceof Error ? error.name : "";
|
|
79
|
+
if (attempt >= MAX_RETRIES || name === "AbortError") {
|
|
80
|
+
throw error;
|
|
81
|
+
}
|
|
82
|
+
await sleepWithSignal(Math.min(1e3 * 2 ** attempt, 8e3), signal);
|
|
83
|
+
continue;
|
|
84
|
+
}
|
|
85
|
+
if (response.ok || attempt >= MAX_RETRIES || !RETRYABLE_STATUSES.has(response.status)) {
|
|
86
|
+
return response;
|
|
87
|
+
}
|
|
88
|
+
const retryAfter = response.headers.get("retry-after");
|
|
89
|
+
const delay = retryAfter ? Math.min(parseInt(retryAfter, 10) * 1e3 || 1e3 * 2 ** attempt, 3e4) : Math.min(1e3 * 2 ** attempt, 8e3);
|
|
90
|
+
await response.body?.cancel().catch(() => void 0);
|
|
91
|
+
await sleepWithSignal(delay, signal);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
var LLM_TIMEOUT_MS, MAX_RETRIES, RETRYABLE_STATUSES;
|
|
95
|
+
var init_http = __esm({
|
|
96
|
+
"src/llm/providers/http.ts"() {
|
|
97
|
+
LLM_TIMEOUT_MS = 12e4;
|
|
98
|
+
MAX_RETRIES = 2;
|
|
99
|
+
RETRYABLE_STATUSES = /* @__PURE__ */ new Set([429, 500, 502, 503, 504]);
|
|
100
|
+
}
|
|
33
101
|
});
|
|
34
|
-
|
|
102
|
+
|
|
103
|
+
// src/llm/providers/anthropic.ts
|
|
104
|
+
async function fetchModels(apiKey, signal) {
|
|
35
105
|
try {
|
|
36
|
-
|
|
37
|
-
|
|
106
|
+
const response = await fetchWithTimeout(
|
|
107
|
+
"https://api.anthropic.com/v1/models?limit=1000",
|
|
108
|
+
{
|
|
109
|
+
method: "GET",
|
|
38
110
|
headers: { "x-api-key": apiKey, "anthropic-version": "2023-06-01" }
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
const models = (data.data ?? []).map((m) => m.id).sort();
|
|
45
|
-
return models.length > 0 ? models : FALLBACK_MODELS.anthropic;
|
|
111
|
+
},
|
|
112
|
+
signal
|
|
113
|
+
);
|
|
114
|
+
if (!response.ok) {
|
|
115
|
+
return FALLBACK_MODELS;
|
|
46
116
|
}
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
117
|
+
const data = await response.json();
|
|
118
|
+
const models = (data.data ?? []).map((entry) => entry.id).sort();
|
|
119
|
+
return models.length > 0 ? models : FALLBACK_MODELS;
|
|
120
|
+
} catch {
|
|
121
|
+
return FALLBACK_MODELS;
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
async function verifyKey(apiKey, signal) {
|
|
125
|
+
try {
|
|
126
|
+
const response = await fetchWithTimeout(
|
|
127
|
+
"https://api.anthropic.com/v1/models?limit=1",
|
|
128
|
+
{
|
|
129
|
+
method: "GET",
|
|
130
|
+
headers: { "x-api-key": apiKey, "anthropic-version": "2023-06-01" }
|
|
131
|
+
},
|
|
132
|
+
signal
|
|
133
|
+
);
|
|
134
|
+
if (response.ok) {
|
|
135
|
+
await response.body?.cancel().catch(() => void 0);
|
|
136
|
+
return { ok: true };
|
|
137
|
+
}
|
|
138
|
+
const body = (await response.text().catch(() => "")).slice(0, 500);
|
|
139
|
+
if (response.status === 401 || response.status === 403) {
|
|
140
|
+
return { ok: false, reason: "invalid", status: response.status, body };
|
|
141
|
+
}
|
|
142
|
+
return {
|
|
143
|
+
ok: false,
|
|
144
|
+
reason: "unavailable",
|
|
145
|
+
error: `HTTP ${response.status}: ${body.slice(0, 200)}`
|
|
146
|
+
};
|
|
147
|
+
} catch (error) {
|
|
148
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
149
|
+
return { ok: false, reason: "unavailable", error: message };
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
var DEFAULT_MODEL, DEFAULT_MAX_TOKENS, FALLBACK_MODELS, AnthropicClient, ANTHROPIC_PROVIDER;
|
|
153
|
+
var init_anthropic = __esm({
|
|
154
|
+
"src/llm/providers/anthropic.ts"() {
|
|
155
|
+
init_http();
|
|
156
|
+
DEFAULT_MODEL = "claude-haiku-4-5-20251001";
|
|
157
|
+
DEFAULT_MAX_TOKENS = 4096;
|
|
158
|
+
FALLBACK_MODELS = ["claude-sonnet-4-6", "claude-haiku-4-5-20251001", "claude-opus-4-6"];
|
|
159
|
+
AnthropicClient = class {
|
|
160
|
+
constructor(config) {
|
|
161
|
+
this.config = config;
|
|
162
|
+
}
|
|
163
|
+
totalIn = 0;
|
|
164
|
+
totalOut = 0;
|
|
165
|
+
logTokens(usage) {
|
|
166
|
+
if (!this.config.logUsage || !usage) {
|
|
167
|
+
return;
|
|
168
|
+
}
|
|
169
|
+
const inputTokens = usage.input_tokens ?? 0;
|
|
170
|
+
const outputTokens = usage.output_tokens ?? 0;
|
|
171
|
+
this.totalIn += inputTokens;
|
|
172
|
+
this.totalOut += outputTokens;
|
|
173
|
+
console.log(
|
|
174
|
+
` [LLM] ${this.config.model} tokens: in=${inputTokens} out=${outputTokens} (total: in=${this.totalIn} out=${this.totalOut})`
|
|
175
|
+
);
|
|
176
|
+
}
|
|
177
|
+
async complete(systemPrompt, userInput, signal) {
|
|
178
|
+
const response = await fetchWithRetry(
|
|
179
|
+
"https://api.anthropic.com/v1/messages",
|
|
180
|
+
{
|
|
181
|
+
method: "POST",
|
|
182
|
+
headers: {
|
|
183
|
+
"Content-Type": "application/json",
|
|
184
|
+
"x-api-key": this.config.apiKey,
|
|
185
|
+
"anthropic-version": "2023-06-01"
|
|
186
|
+
},
|
|
187
|
+
body: JSON.stringify({
|
|
188
|
+
model: this.config.model,
|
|
189
|
+
max_tokens: this.config.maxTokens,
|
|
190
|
+
system: systemPrompt,
|
|
191
|
+
messages: [{ role: "user", content: userInput }]
|
|
192
|
+
})
|
|
193
|
+
},
|
|
194
|
+
signal
|
|
195
|
+
);
|
|
196
|
+
if (!response.ok) {
|
|
197
|
+
throw new Error(`Anthropic API error: ${response.status} ${await response.text()}`);
|
|
198
|
+
}
|
|
199
|
+
const data = await response.json();
|
|
200
|
+
this.logTokens(data.usage);
|
|
201
|
+
const textBlock = data.content?.find((block) => block.type === "text");
|
|
202
|
+
return textBlock?.text ?? "";
|
|
203
|
+
}
|
|
204
|
+
async completeWithTools(systemPrompt, messages, tools, signal) {
|
|
205
|
+
const anthropicTools = tools.map((tool) => ({
|
|
206
|
+
name: tool.name,
|
|
207
|
+
description: tool.description,
|
|
208
|
+
input_schema: {
|
|
209
|
+
type: "object",
|
|
210
|
+
properties: Object.fromEntries(
|
|
211
|
+
tool.parameters.map((param) => [
|
|
212
|
+
param.name,
|
|
213
|
+
{ type: "string", description: param.description }
|
|
214
|
+
])
|
|
215
|
+
),
|
|
216
|
+
required: tool.parameters.filter((param) => param.required).map((param) => param.name)
|
|
217
|
+
}
|
|
218
|
+
}));
|
|
219
|
+
const response = await fetchWithRetry(
|
|
220
|
+
"https://api.anthropic.com/v1/messages",
|
|
221
|
+
{
|
|
222
|
+
method: "POST",
|
|
223
|
+
headers: {
|
|
224
|
+
"Content-Type": "application/json",
|
|
225
|
+
"x-api-key": this.config.apiKey,
|
|
226
|
+
"anthropic-version": "2023-06-01"
|
|
227
|
+
},
|
|
228
|
+
body: JSON.stringify({
|
|
229
|
+
model: this.config.model,
|
|
230
|
+
max_tokens: this.config.maxTokens,
|
|
231
|
+
system: systemPrompt,
|
|
232
|
+
messages,
|
|
233
|
+
tools: anthropicTools
|
|
234
|
+
})
|
|
235
|
+
},
|
|
236
|
+
signal
|
|
237
|
+
);
|
|
238
|
+
if (!response.ok) {
|
|
239
|
+
throw new Error(`Anthropic API error: ${response.status} ${await response.text()}`);
|
|
240
|
+
}
|
|
241
|
+
const data = await response.json();
|
|
242
|
+
this.logTokens(data.usage);
|
|
243
|
+
const content = data.content ?? [];
|
|
244
|
+
const toolUses = content.filter((block) => block.type === "tool_use");
|
|
245
|
+
if (toolUses.length > 0) {
|
|
246
|
+
const calls = toolUses.map((block) => ({
|
|
247
|
+
id: block.id ?? "",
|
|
248
|
+
name: block.name ?? "",
|
|
249
|
+
arguments: block.input ?? {}
|
|
250
|
+
}));
|
|
251
|
+
return {
|
|
252
|
+
type: "tool_use",
|
|
253
|
+
calls,
|
|
254
|
+
assistantMessage: { role: "assistant", content }
|
|
255
|
+
};
|
|
256
|
+
}
|
|
257
|
+
const textBlock = content.find((block) => block.type === "text");
|
|
258
|
+
return { type: "text", text: textBlock?.text ?? "" };
|
|
259
|
+
}
|
|
260
|
+
formatToolResultMessages(results) {
|
|
261
|
+
return [
|
|
262
|
+
{
|
|
263
|
+
role: "user",
|
|
264
|
+
content: results.map((result) => ({
|
|
265
|
+
type: "tool_result",
|
|
266
|
+
tool_use_id: result.callId,
|
|
267
|
+
content: result.content
|
|
268
|
+
}))
|
|
269
|
+
}
|
|
270
|
+
];
|
|
53
271
|
}
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
272
|
+
};
|
|
273
|
+
ANTHROPIC_PROVIDER = {
|
|
274
|
+
id: "anthropic",
|
|
275
|
+
displayName: "Anthropic (Claude)",
|
|
276
|
+
envVar: "ANTHROPIC_API_KEY",
|
|
277
|
+
defaultModel: DEFAULT_MODEL,
|
|
278
|
+
fallbackModels: FALLBACK_MODELS,
|
|
279
|
+
fetchModels,
|
|
280
|
+
verifyKey,
|
|
281
|
+
createClient: (config) => new AnthropicClient({
|
|
282
|
+
apiKey: config.apiKey,
|
|
283
|
+
model: config.model ?? DEFAULT_MODEL,
|
|
284
|
+
maxTokens: config.maxTokens ?? DEFAULT_MAX_TOKENS,
|
|
285
|
+
logUsage: config.logUsage
|
|
286
|
+
})
|
|
287
|
+
};
|
|
288
|
+
}
|
|
289
|
+
});
|
|
290
|
+
|
|
291
|
+
// src/llm/providers/openai.ts
|
|
292
|
+
function isOpenAIReasoningModel(model) {
|
|
293
|
+
return /^o\d/.test(model) || /^gpt-5(\b|[-.])/.test(model);
|
|
294
|
+
}
|
|
295
|
+
async function fetchModels2(apiKey, signal) {
|
|
296
|
+
try {
|
|
297
|
+
const response = await fetchWithTimeout(
|
|
298
|
+
"https://api.openai.com/v1/models",
|
|
299
|
+
{
|
|
300
|
+
method: "GET",
|
|
301
|
+
headers: { Authorization: `Bearer ${apiKey}` }
|
|
302
|
+
},
|
|
303
|
+
signal
|
|
304
|
+
);
|
|
305
|
+
if (!response.ok) {
|
|
306
|
+
return FALLBACK_MODELS2;
|
|
307
|
+
}
|
|
308
|
+
const data = await response.json();
|
|
309
|
+
const models = (data.data ?? []).map((entry) => entry.id).filter(
|
|
310
|
+
(id) => (id.startsWith("gpt-") || id.startsWith("o1") || id.startsWith("o3") || id.startsWith("o4") || id.startsWith("chatgpt-")) && !id.includes("instruct") && !id.includes("realtime") && !id.includes("audio") && !id.includes("tts") && !id.includes("whisper")
|
|
311
|
+
).sort();
|
|
312
|
+
return models.length > 0 ? models : FALLBACK_MODELS2;
|
|
313
|
+
} catch {
|
|
314
|
+
return FALLBACK_MODELS2;
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
async function verifyKey2(apiKey, signal) {
|
|
318
|
+
try {
|
|
319
|
+
const response = await fetchWithTimeout(
|
|
320
|
+
"https://api.openai.com/v1/models",
|
|
321
|
+
{
|
|
322
|
+
method: "GET",
|
|
323
|
+
headers: { Authorization: `Bearer ${apiKey}` }
|
|
324
|
+
},
|
|
325
|
+
signal
|
|
326
|
+
);
|
|
327
|
+
if (response.ok) {
|
|
328
|
+
await response.body?.cancel().catch(() => void 0);
|
|
329
|
+
return { ok: true };
|
|
59
330
|
}
|
|
60
|
-
|
|
331
|
+
const body = (await response.text().catch(() => "")).slice(0, 500);
|
|
332
|
+
if (response.status === 401 || response.status === 403) {
|
|
333
|
+
return { ok: false, reason: "invalid", status: response.status, body };
|
|
334
|
+
}
|
|
335
|
+
return {
|
|
336
|
+
ok: false,
|
|
337
|
+
reason: "unavailable",
|
|
338
|
+
error: `HTTP ${response.status}: ${body.slice(0, 200)}`
|
|
339
|
+
};
|
|
340
|
+
} catch (error) {
|
|
341
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
342
|
+
return { ok: false, reason: "unavailable", error: message };
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
var DEFAULT_MODEL2, DEFAULT_MAX_TOKENS2, FALLBACK_MODELS2, OpenAIClient, OPENAI_PROVIDER;
|
|
346
|
+
var init_openai = __esm({
|
|
347
|
+
"src/llm/providers/openai.ts"() {
|
|
348
|
+
init_http();
|
|
349
|
+
DEFAULT_MODEL2 = "gpt-4o-mini";
|
|
350
|
+
DEFAULT_MAX_TOKENS2 = 4096;
|
|
351
|
+
FALLBACK_MODELS2 = ["gpt-4o", "gpt-4o-mini", "o3-mini"];
|
|
352
|
+
OpenAIClient = class {
|
|
353
|
+
constructor(config) {
|
|
354
|
+
this.config = config;
|
|
355
|
+
}
|
|
356
|
+
totalIn = 0;
|
|
357
|
+
totalOut = 0;
|
|
358
|
+
isReasoningModel() {
|
|
359
|
+
return isOpenAIReasoningModel(this.config.model);
|
|
360
|
+
}
|
|
361
|
+
logTokens(usage) {
|
|
362
|
+
if (!this.config.logUsage || !usage) {
|
|
363
|
+
return;
|
|
364
|
+
}
|
|
365
|
+
const inputTokens = usage.prompt_tokens ?? 0;
|
|
366
|
+
const outputTokens = usage.completion_tokens ?? 0;
|
|
367
|
+
this.totalIn += inputTokens;
|
|
368
|
+
this.totalOut += outputTokens;
|
|
369
|
+
console.log(
|
|
370
|
+
` [LLM] ${this.config.model} tokens: in=${inputTokens} out=${outputTokens} (total: in=${this.totalIn} out=${this.totalOut})`
|
|
371
|
+
);
|
|
372
|
+
}
|
|
373
|
+
async complete(systemPrompt, userInput, signal) {
|
|
374
|
+
const reasoning = this.isReasoningModel();
|
|
375
|
+
const response = await fetchWithRetry(
|
|
376
|
+
"https://api.openai.com/v1/chat/completions",
|
|
377
|
+
{
|
|
378
|
+
method: "POST",
|
|
379
|
+
headers: {
|
|
380
|
+
"Content-Type": "application/json",
|
|
381
|
+
Authorization: `Bearer ${this.config.apiKey}`
|
|
382
|
+
},
|
|
383
|
+
body: JSON.stringify({
|
|
384
|
+
model: this.config.model,
|
|
385
|
+
...reasoning ? { max_completion_tokens: this.config.maxTokens } : { max_tokens: this.config.maxTokens },
|
|
386
|
+
messages: [
|
|
387
|
+
{ role: reasoning ? "developer" : "system", content: systemPrompt },
|
|
388
|
+
{ role: "user", content: userInput }
|
|
389
|
+
]
|
|
390
|
+
})
|
|
391
|
+
},
|
|
392
|
+
signal
|
|
393
|
+
);
|
|
394
|
+
if (!response.ok) {
|
|
395
|
+
throw new Error(`OpenAI API error: ${response.status} ${await response.text()}`);
|
|
396
|
+
}
|
|
397
|
+
const data = await response.json();
|
|
398
|
+
this.logTokens(data.usage);
|
|
399
|
+
return data.choices?.[0]?.message?.content ?? "";
|
|
400
|
+
}
|
|
401
|
+
async completeWithTools(systemPrompt, messages, tools, signal) {
|
|
402
|
+
const openaiTools = tools.map((tool) => ({
|
|
403
|
+
type: "function",
|
|
404
|
+
function: {
|
|
405
|
+
name: tool.name,
|
|
406
|
+
description: tool.description,
|
|
407
|
+
parameters: {
|
|
408
|
+
type: "object",
|
|
409
|
+
properties: Object.fromEntries(
|
|
410
|
+
tool.parameters.map((param) => [
|
|
411
|
+
param.name,
|
|
412
|
+
{ type: "string", description: param.description }
|
|
413
|
+
])
|
|
414
|
+
),
|
|
415
|
+
required: tool.parameters.filter((param) => param.required).map((param) => param.name)
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
}));
|
|
419
|
+
const reasoning = this.isReasoningModel();
|
|
420
|
+
const response = await fetchWithRetry(
|
|
421
|
+
"https://api.openai.com/v1/chat/completions",
|
|
422
|
+
{
|
|
423
|
+
method: "POST",
|
|
424
|
+
headers: {
|
|
425
|
+
"Content-Type": "application/json",
|
|
426
|
+
Authorization: `Bearer ${this.config.apiKey}`
|
|
427
|
+
},
|
|
428
|
+
body: JSON.stringify({
|
|
429
|
+
model: this.config.model,
|
|
430
|
+
...reasoning ? { max_completion_tokens: this.config.maxTokens } : { max_tokens: this.config.maxTokens },
|
|
431
|
+
messages: [
|
|
432
|
+
{ role: reasoning ? "developer" : "system", content: systemPrompt },
|
|
433
|
+
...messages
|
|
434
|
+
],
|
|
435
|
+
tools: openaiTools
|
|
436
|
+
})
|
|
437
|
+
},
|
|
438
|
+
signal
|
|
439
|
+
);
|
|
440
|
+
if (!response.ok) {
|
|
441
|
+
throw new Error(`OpenAI API error: ${response.status} ${await response.text()}`);
|
|
442
|
+
}
|
|
443
|
+
const data = await response.json();
|
|
444
|
+
this.logTokens(data.usage);
|
|
445
|
+
const message = data.choices?.[0]?.message;
|
|
446
|
+
const toolCalls = message?.tool_calls ?? [];
|
|
447
|
+
if (toolCalls.length > 0) {
|
|
448
|
+
const calls = toolCalls.map((call) => {
|
|
449
|
+
let args;
|
|
450
|
+
try {
|
|
451
|
+
args = JSON.parse(call.function?.arguments ?? "{}");
|
|
452
|
+
} catch {
|
|
453
|
+
args = {};
|
|
454
|
+
}
|
|
455
|
+
return { id: call.id ?? "", name: call.function?.name ?? "", arguments: args };
|
|
456
|
+
});
|
|
457
|
+
return { type: "tool_use", calls, assistantMessage: message };
|
|
458
|
+
}
|
|
459
|
+
return { type: "text", text: message?.content ?? "" };
|
|
460
|
+
}
|
|
461
|
+
formatToolResultMessages(results) {
|
|
462
|
+
return results.map((result) => ({
|
|
463
|
+
role: "tool",
|
|
464
|
+
tool_call_id: result.callId,
|
|
465
|
+
content: result.content
|
|
466
|
+
}));
|
|
467
|
+
}
|
|
468
|
+
};
|
|
469
|
+
OPENAI_PROVIDER = {
|
|
470
|
+
id: "openai",
|
|
471
|
+
displayName: "OpenAI (GPT)",
|
|
472
|
+
envVar: "OPENAI_API_KEY",
|
|
473
|
+
defaultModel: DEFAULT_MODEL2,
|
|
474
|
+
fallbackModels: FALLBACK_MODELS2,
|
|
475
|
+
fetchModels: fetchModels2,
|
|
476
|
+
verifyKey: verifyKey2,
|
|
477
|
+
createClient: (config) => new OpenAIClient({
|
|
478
|
+
apiKey: config.apiKey,
|
|
479
|
+
model: config.model ?? DEFAULT_MODEL2,
|
|
480
|
+
maxTokens: config.maxTokens ?? DEFAULT_MAX_TOKENS2,
|
|
481
|
+
logUsage: config.logUsage
|
|
482
|
+
}),
|
|
483
|
+
isReasoningModel: isOpenAIReasoningModel
|
|
484
|
+
};
|
|
485
|
+
}
|
|
486
|
+
});
|
|
487
|
+
|
|
488
|
+
// src/llm/registry.ts
|
|
489
|
+
function registerLlmProvider(descriptor) {
|
|
490
|
+
REGISTRY.set(descriptor.id, descriptor);
|
|
491
|
+
}
|
|
492
|
+
function getLlmProvider(id) {
|
|
493
|
+
return REGISTRY.get(id);
|
|
494
|
+
}
|
|
495
|
+
function listLlmProviders() {
|
|
496
|
+
return Array.from(REGISTRY.values());
|
|
497
|
+
}
|
|
498
|
+
function getRegisteredProviderIds() {
|
|
499
|
+
return Array.from(REGISTRY.keys());
|
|
500
|
+
}
|
|
501
|
+
var REGISTRY;
|
|
502
|
+
var init_registry = __esm({
|
|
503
|
+
"src/llm/registry.ts"() {
|
|
504
|
+
init_anthropic();
|
|
505
|
+
init_openai();
|
|
506
|
+
REGISTRY = /* @__PURE__ */ new Map();
|
|
507
|
+
registerLlmProvider(ANTHROPIC_PROVIDER);
|
|
508
|
+
registerLlmProvider(OPENAI_PROVIDER);
|
|
509
|
+
}
|
|
510
|
+
});
|
|
511
|
+
|
|
512
|
+
// src/llm/index.ts
|
|
513
|
+
function createLlmClient(config) {
|
|
514
|
+
const descriptor = getLlmProvider(config.provider);
|
|
515
|
+
if (!descriptor) {
|
|
516
|
+
const known = getRegisteredProviderIds().join(", ") || "<none>";
|
|
517
|
+
throw new Error(`Unknown LLM provider "${config.provider}". Registered: ${known}.`);
|
|
518
|
+
}
|
|
519
|
+
if (!config.apiKey) {
|
|
520
|
+
throw new Error(`${descriptor.envVar} is required for skill runtime`);
|
|
521
|
+
}
|
|
522
|
+
return descriptor.createClient({
|
|
523
|
+
apiKey: config.apiKey,
|
|
524
|
+
model: config.model,
|
|
525
|
+
maxTokens: config.maxTokens,
|
|
526
|
+
logUsage: config.logUsage
|
|
527
|
+
});
|
|
528
|
+
}
|
|
529
|
+
async function verifyLlmApiKey(provider, apiKey, signal) {
|
|
530
|
+
const descriptor = getLlmProvider(provider);
|
|
531
|
+
if (!descriptor) {
|
|
532
|
+
return {
|
|
533
|
+
ok: false,
|
|
534
|
+
reason: "unavailable",
|
|
535
|
+
error: `Unknown LLM provider "${provider}"`
|
|
536
|
+
};
|
|
537
|
+
}
|
|
538
|
+
return descriptor.verifyKey(apiKey, signal);
|
|
539
|
+
}
|
|
540
|
+
var init_llm = __esm({
|
|
541
|
+
"src/llm/index.ts"() {
|
|
542
|
+
init_registry();
|
|
543
|
+
init_registry();
|
|
544
|
+
}
|
|
545
|
+
});
|
|
546
|
+
|
|
547
|
+
// src/commands/init.ts
|
|
548
|
+
var init_exports = {};
|
|
549
|
+
__export(init_exports, {
|
|
550
|
+
cmdInit: () => cmdInit,
|
|
551
|
+
fetchModels: () => fetchModels3
|
|
552
|
+
});
|
|
553
|
+
async function fetchModels3(provider, apiKey) {
|
|
554
|
+
const descriptor = getLlmProvider(provider);
|
|
555
|
+
if (!descriptor) {
|
|
556
|
+
console.warn(` ! Unknown provider "${provider}". No models available.`);
|
|
557
|
+
return [];
|
|
558
|
+
}
|
|
559
|
+
try {
|
|
560
|
+
return await descriptor.fetchModels(apiKey);
|
|
61
561
|
} catch (e) {
|
|
62
562
|
console.warn(` ! Could not fetch models: ${e.message}. Using defaults.`);
|
|
63
|
-
return
|
|
563
|
+
return descriptor.fallbackModels;
|
|
64
564
|
}
|
|
65
565
|
}
|
|
66
566
|
function pickTarget(options) {
|
|
@@ -140,55 +640,61 @@ async function cmdInit(nameArg, options = {}) {
|
|
|
140
640
|
promptedApiKey = result.apiKey;
|
|
141
641
|
}
|
|
142
642
|
let defaultProviderKey;
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
643
|
+
const defaultProviderId = yaml.llm?.provider;
|
|
644
|
+
if (yaml.llm && defaultProviderId) {
|
|
645
|
+
const descriptor = getLlmProvider(defaultProviderId);
|
|
646
|
+
const envKey = descriptor ? process.env[descriptor.envVar] : void 0;
|
|
647
|
+
if (envKey && descriptor) {
|
|
146
648
|
defaultProviderKey = envKey;
|
|
147
|
-
console.log(
|
|
148
|
-
` Using ${yaml.llm.provider === "anthropic" ? "ANTHROPIC" : "OPENAI"}_API_KEY from environment.`
|
|
149
|
-
);
|
|
649
|
+
console.log(` Using ${descriptor.envVar} from environment.`);
|
|
150
650
|
} else if (promptedApiKey) {
|
|
151
651
|
defaultProviderKey = promptedApiKey;
|
|
152
652
|
} else {
|
|
653
|
+
const label = descriptor?.displayName ?? defaultProviderId;
|
|
153
654
|
const { apiKey } = await inquirer.prompt([
|
|
154
655
|
{
|
|
155
656
|
type: "password",
|
|
156
657
|
name: "apiKey",
|
|
157
|
-
message: `${
|
|
658
|
+
message: `${label} API key:`,
|
|
158
659
|
mask: "*"
|
|
159
660
|
}
|
|
160
661
|
]);
|
|
161
662
|
defaultProviderKey = apiKey || void 0;
|
|
162
663
|
}
|
|
163
664
|
}
|
|
164
|
-
|
|
165
|
-
if (yaml.llm && !template) {
|
|
166
|
-
const
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
const
|
|
170
|
-
|
|
171
|
-
{
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
665
|
+
const otherProviderKeys = /* @__PURE__ */ new Map();
|
|
666
|
+
if (yaml.llm && !template && defaultProviderId) {
|
|
667
|
+
const otherDescriptors = listLlmProviders().filter(
|
|
668
|
+
(descriptor) => descriptor.id !== defaultProviderId
|
|
669
|
+
);
|
|
670
|
+
for (const descriptor of otherDescriptors) {
|
|
671
|
+
const otherEnvKey = process.env[descriptor.envVar];
|
|
672
|
+
const { configureOther } = await inquirer.prompt([
|
|
673
|
+
{
|
|
674
|
+
type: "confirm",
|
|
675
|
+
name: "configureOther",
|
|
676
|
+
message: `Configure ${descriptor.displayName} API key too (for skills that override the default provider)?`,
|
|
677
|
+
default: Boolean(otherEnvKey)
|
|
678
|
+
}
|
|
679
|
+
]);
|
|
680
|
+
if (!configureOther) {
|
|
681
|
+
continue;
|
|
176
682
|
}
|
|
177
|
-
]);
|
|
178
|
-
if (configureOther) {
|
|
179
683
|
if (otherEnvKey) {
|
|
180
|
-
|
|
181
|
-
console.log(` Using ${
|
|
684
|
+
otherProviderKeys.set(descriptor.id, otherEnvKey);
|
|
685
|
+
console.log(` Using ${descriptor.envVar} from environment.`);
|
|
182
686
|
} else {
|
|
183
687
|
const { promptedOther } = await inquirer.prompt([
|
|
184
688
|
{
|
|
185
689
|
type: "password",
|
|
186
690
|
name: "promptedOther",
|
|
187
|
-
message: `${
|
|
691
|
+
message: `${descriptor.displayName} API key:`,
|
|
188
692
|
mask: "*"
|
|
189
693
|
}
|
|
190
694
|
]);
|
|
191
|
-
|
|
695
|
+
if (promptedOther) {
|
|
696
|
+
otherProviderKeys.set(descriptor.id, promptedOther);
|
|
697
|
+
}
|
|
192
698
|
}
|
|
193
699
|
}
|
|
194
700
|
}
|
|
@@ -224,15 +730,17 @@ async function cmdInit(nameArg, options = {}) {
|
|
|
224
730
|
const nostrPubkey = getPublicKey(nostrSecretBytes);
|
|
225
731
|
const nostrSecretHex = Buffer.from(nostrSecretBytes).toString("hex");
|
|
226
732
|
const created = await createAgentDir({ target, name: agentName, cwd });
|
|
227
|
-
const
|
|
228
|
-
|
|
733
|
+
const collectedKeys = new Map(otherProviderKeys);
|
|
734
|
+
if (yaml.llm && defaultProviderKey) {
|
|
735
|
+
collectedKeys.set(yaml.llm.provider, defaultProviderKey);
|
|
736
|
+
}
|
|
737
|
+
const llmApiKeys = Object.fromEntries(collectedKeys);
|
|
229
738
|
await writeYaml(created.dir, yaml);
|
|
230
739
|
await writeSecrets(
|
|
231
740
|
created.dir,
|
|
232
741
|
{
|
|
233
742
|
nostr_secret_key: nostrSecretHex,
|
|
234
|
-
|
|
235
|
-
openai_api_key: openaiKey
|
|
743
|
+
llm_api_keys: Object.keys(llmApiKeys).length > 0 ? llmApiKeys : void 0
|
|
236
744
|
},
|
|
237
745
|
passphrase || void 0
|
|
238
746
|
);
|
|
@@ -334,8 +842,10 @@ async function promptYaml(inquirer) {
|
|
|
334
842
|
name: "None (non-LLM agent - static-file / static-script / dynamic-script only)",
|
|
335
843
|
value: "none"
|
|
336
844
|
},
|
|
337
|
-
|
|
338
|
-
|
|
845
|
+
...listLlmProviders().map((descriptor2) => ({
|
|
846
|
+
name: descriptor2.displayName,
|
|
847
|
+
value: descriptor2.id
|
|
848
|
+
}))
|
|
339
849
|
]
|
|
340
850
|
}
|
|
341
851
|
]);
|
|
@@ -352,21 +862,25 @@ async function promptYaml(inquirer) {
|
|
|
352
862
|
const yaml2 = ElisymYamlSchema.parse(baseYaml);
|
|
353
863
|
return { yaml: yaml2 };
|
|
354
864
|
}
|
|
355
|
-
const
|
|
865
|
+
const descriptor = getLlmProvider(llmProvider);
|
|
866
|
+
if (!descriptor) {
|
|
867
|
+
throw new Error(`Internal: provider "${llmProvider}" not registered.`);
|
|
868
|
+
}
|
|
869
|
+
const envKey = process.env[descriptor.envVar];
|
|
356
870
|
let apiKey = envKey;
|
|
357
871
|
if (!apiKey) {
|
|
358
872
|
const { promptedKey } = await inquirer.prompt([
|
|
359
873
|
{
|
|
360
874
|
type: "password",
|
|
361
875
|
name: "promptedKey",
|
|
362
|
-
message: `${
|
|
876
|
+
message: `${descriptor.displayName} API key:`,
|
|
363
877
|
mask: "*"
|
|
364
878
|
}
|
|
365
879
|
]);
|
|
366
880
|
apiKey = promptedKey || void 0;
|
|
367
881
|
}
|
|
368
882
|
console.log(" Fetching available models...");
|
|
369
|
-
const models = await
|
|
883
|
+
const models = await fetchModels3(llmProvider, apiKey ?? "");
|
|
370
884
|
const { model } = await inquirer.prompt([
|
|
371
885
|
{
|
|
372
886
|
type: "list",
|
|
@@ -389,18 +903,17 @@ async function promptYaml(inquirer) {
|
|
|
389
903
|
});
|
|
390
904
|
return { yaml, apiKey: envKey ? void 0 : apiKey };
|
|
391
905
|
}
|
|
392
|
-
var FALLBACK_MODELS;
|
|
393
906
|
var init_init = __esm({
|
|
394
907
|
"src/commands/init.ts"() {
|
|
395
|
-
|
|
396
|
-
anthropic: ["claude-sonnet-4-6", "claude-haiku-4-5-20251001", "claude-opus-4-6"],
|
|
397
|
-
openai: ["gpt-4o", "gpt-4o-mini", "o3-mini"]
|
|
398
|
-
};
|
|
908
|
+
init_llm();
|
|
399
909
|
}
|
|
400
910
|
});
|
|
401
911
|
|
|
402
912
|
// src/index.ts
|
|
403
913
|
init_init();
|
|
914
|
+
|
|
915
|
+
// src/commands/profile.ts
|
|
916
|
+
init_llm();
|
|
404
917
|
async function cmdProfile(name) {
|
|
405
918
|
const { default: inquirer } = await import('inquirer');
|
|
406
919
|
const cwd = process.cwd();
|
|
@@ -519,32 +1032,45 @@ async function cmdProfile(name) {
|
|
|
519
1032
|
console.log(" Wallet updated.\n");
|
|
520
1033
|
}
|
|
521
1034
|
if (section === "llm") {
|
|
1035
|
+
const providerChoices = listLlmProviders().map((descriptor) => ({
|
|
1036
|
+
name: descriptor.displayName,
|
|
1037
|
+
value: descriptor.id
|
|
1038
|
+
}));
|
|
1039
|
+
if (providerChoices.length === 0) {
|
|
1040
|
+
console.error(" ! No LLM providers registered.");
|
|
1041
|
+
continue;
|
|
1042
|
+
}
|
|
1043
|
+
const firstChoice = providerChoices[0];
|
|
1044
|
+
if (!firstChoice) {
|
|
1045
|
+
console.error(" ! No LLM providers registered.");
|
|
1046
|
+
continue;
|
|
1047
|
+
}
|
|
522
1048
|
const { llmProvider } = await inquirer.prompt([
|
|
523
1049
|
{
|
|
524
1050
|
type: "list",
|
|
525
1051
|
name: "llmProvider",
|
|
526
1052
|
message: "Default LLM provider (used by skills without a `provider:` override):",
|
|
527
|
-
choices:
|
|
528
|
-
|
|
529
|
-
{ name: "OpenAI (GPT)", value: "openai" }
|
|
530
|
-
],
|
|
531
|
-
default: loaded.yaml.llm?.provider ?? "anthropic"
|
|
1053
|
+
choices: providerChoices,
|
|
1054
|
+
default: loaded.yaml.llm?.provider ?? firstChoice.value
|
|
532
1055
|
}
|
|
533
1056
|
]);
|
|
534
|
-
const
|
|
1057
|
+
const defaultDescriptor = getLlmProvider(llmProvider);
|
|
1058
|
+
if (!defaultDescriptor) {
|
|
1059
|
+
throw new Error(`Internal: provider "${llmProvider}" not registered.`);
|
|
1060
|
+
}
|
|
535
1061
|
const defaultKeyStatus = describeKeyStatus(loaded.secrets, llmProvider);
|
|
536
1062
|
const { apiKey } = await inquirer.prompt([
|
|
537
1063
|
{
|
|
538
1064
|
type: "password",
|
|
539
1065
|
name: "apiKey",
|
|
540
|
-
message: `${
|
|
1066
|
+
message: `${defaultDescriptor.displayName} API key [${defaultKeyStatus}] (leave empty to keep current):`,
|
|
541
1067
|
mask: "*"
|
|
542
1068
|
}
|
|
543
1069
|
]);
|
|
544
1070
|
const probeKey = apiKey || pickPlainKey(loaded.secrets, llmProvider);
|
|
545
1071
|
console.log(" Fetching available models...");
|
|
546
|
-
const { fetchModels:
|
|
547
|
-
const models = await
|
|
1072
|
+
const { fetchModels: fetchModels4 } = await Promise.resolve().then(() => (init_init(), init_exports));
|
|
1073
|
+
const models = await fetchModels4(llmProvider, probeKey);
|
|
548
1074
|
const { model } = await inquirer.prompt([
|
|
549
1075
|
{
|
|
550
1076
|
type: "list",
|
|
@@ -562,35 +1088,42 @@ async function cmdProfile(name) {
|
|
|
562
1088
|
default: loaded.yaml.llm?.max_tokens ?? 4096
|
|
563
1089
|
}
|
|
564
1090
|
]);
|
|
565
|
-
const
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
1091
|
+
const otherDescriptors = listLlmProviders().filter(
|
|
1092
|
+
(descriptor) => descriptor.id !== llmProvider
|
|
1093
|
+
);
|
|
1094
|
+
const otherKeys = /* @__PURE__ */ new Map();
|
|
1095
|
+
for (const descriptor of otherDescriptors) {
|
|
1096
|
+
const status = describeKeyStatus(loaded.secrets, descriptor.id);
|
|
1097
|
+
const { otherApiKey } = await inquirer.prompt([
|
|
1098
|
+
{
|
|
1099
|
+
type: "password",
|
|
1100
|
+
name: "otherApiKey",
|
|
1101
|
+
message: `${descriptor.displayName} API key for skill overrides [${status}] (leave empty to keep current):`,
|
|
1102
|
+
mask: "*"
|
|
1103
|
+
}
|
|
1104
|
+
]);
|
|
1105
|
+
if (otherApiKey) {
|
|
1106
|
+
otherKeys.set(descriptor.id, otherApiKey);
|
|
572
1107
|
}
|
|
573
|
-
|
|
1108
|
+
}
|
|
574
1109
|
const nextLlm = { provider: llmProvider, model, max_tokens: maxTokens };
|
|
575
1110
|
const nextYaml = { ...loaded.yaml, llm: nextLlm };
|
|
576
1111
|
await writeYaml(loaded.dir, nextYaml);
|
|
577
1112
|
loaded.yaml = nextYaml;
|
|
578
|
-
if (apiKey ||
|
|
579
|
-
const
|
|
1113
|
+
if (apiKey || otherKeys.size > 0) {
|
|
1114
|
+
const nextLlmApiKeys = {
|
|
1115
|
+
...loaded.secrets.llm_api_keys ?? {}
|
|
1116
|
+
};
|
|
580
1117
|
if (apiKey) {
|
|
581
|
-
|
|
582
|
-
nextSecrets.anthropic_api_key = apiKey;
|
|
583
|
-
} else {
|
|
584
|
-
nextSecrets.openai_api_key = apiKey;
|
|
585
|
-
}
|
|
1118
|
+
nextLlmApiKeys[llmProvider] = apiKey;
|
|
586
1119
|
}
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
nextSecrets.anthropic_api_key = otherApiKey;
|
|
590
|
-
} else {
|
|
591
|
-
nextSecrets.openai_api_key = otherApiKey;
|
|
592
|
-
}
|
|
1120
|
+
for (const [providerId, key] of otherKeys) {
|
|
1121
|
+
nextLlmApiKeys[providerId] = key;
|
|
593
1122
|
}
|
|
1123
|
+
const nextSecrets = {
|
|
1124
|
+
...loaded.secrets,
|
|
1125
|
+
llm_api_keys: nextLlmApiKeys
|
|
1126
|
+
};
|
|
594
1127
|
await writeSecrets(loaded.dir, nextSecrets, passphrase);
|
|
595
1128
|
loaded.secrets = nextSecrets;
|
|
596
1129
|
}
|
|
@@ -606,16 +1139,12 @@ function truncate(value, max = 40) {
|
|
|
606
1139
|
}
|
|
607
1140
|
return value.slice(0, max - 1) + "\u2026";
|
|
608
1141
|
}
|
|
609
|
-
function
|
|
610
|
-
return
|
|
611
|
-
}
|
|
612
|
-
function describeKeyStatus(secrets, provider) {
|
|
613
|
-
const perProviderField = provider === "anthropic" ? "anthropic_api_key" : "openai_api_key";
|
|
614
|
-
return secrets[perProviderField] ? "set" : "not set";
|
|
1142
|
+
function describeKeyStatus(secrets, providerId) {
|
|
1143
|
+
return secrets.llm_api_keys?.[providerId] ? "set" : "not set";
|
|
615
1144
|
}
|
|
616
|
-
function pickPlainKey(secrets,
|
|
617
|
-
const
|
|
618
|
-
return
|
|
1145
|
+
function pickPlainKey(secrets, providerId) {
|
|
1146
|
+
const value = secrets.llm_api_keys?.[providerId];
|
|
1147
|
+
return typeof value === "string" ? value : "";
|
|
619
1148
|
}
|
|
620
1149
|
var TCP_PROBE_TIMEOUT_MS = 3e3;
|
|
621
1150
|
function parseRelayUrl(url) {
|
|
@@ -712,6 +1241,30 @@ function getRpcUrl(_network) {
|
|
|
712
1241
|
}
|
|
713
1242
|
return "https://api.devnet.solana.com";
|
|
714
1243
|
}
|
|
1244
|
+
async function fetchUsdcBalance(rpc, owner) {
|
|
1245
|
+
const mint = USDC_SOLANA_DEVNET.mint;
|
|
1246
|
+
if (!mint) {
|
|
1247
|
+
return 0n;
|
|
1248
|
+
}
|
|
1249
|
+
try {
|
|
1250
|
+
const response = await rpc.getTokenAccountsByOwner(
|
|
1251
|
+
owner,
|
|
1252
|
+
{ mint: address(mint) },
|
|
1253
|
+
{ encoding: "jsonParsed", commitment: "confirmed" }
|
|
1254
|
+
).send();
|
|
1255
|
+
let total = 0n;
|
|
1256
|
+
for (const entry of response.value) {
|
|
1257
|
+
const parsed = entry.account.data;
|
|
1258
|
+
const raw = parsed?.parsed?.info?.tokenAmount?.amount;
|
|
1259
|
+
if (typeof raw === "string") {
|
|
1260
|
+
total += BigInt(raw);
|
|
1261
|
+
}
|
|
1262
|
+
}
|
|
1263
|
+
return total;
|
|
1264
|
+
} catch {
|
|
1265
|
+
return 0n;
|
|
1266
|
+
}
|
|
1267
|
+
}
|
|
715
1268
|
var VALID_TRANSITIONS = {
|
|
716
1269
|
paid: ["executed", "failed"],
|
|
717
1270
|
executed: ["delivered", "failed"],
|
|
@@ -851,322 +1404,11 @@ var JobLedger = class {
|
|
|
851
1404
|
}
|
|
852
1405
|
};
|
|
853
1406
|
|
|
854
|
-
// src/
|
|
855
|
-
|
|
856
|
-
var MAX_RETRIES = 2;
|
|
857
|
-
var RETRYABLE_STATUSES = /* @__PURE__ */ new Set([429, 500, 502, 503, 504]);
|
|
858
|
-
function createAbortError() {
|
|
859
|
-
const err = new Error("The operation was aborted");
|
|
860
|
-
err.name = "AbortError";
|
|
861
|
-
return err;
|
|
862
|
-
}
|
|
863
|
-
function sleepWithSignal(ms, signal) {
|
|
864
|
-
if (signal?.aborted) {
|
|
865
|
-
throw createAbortError();
|
|
866
|
-
}
|
|
867
|
-
if (!signal) {
|
|
868
|
-
return new Promise((r) => setTimeout(r, ms));
|
|
869
|
-
}
|
|
870
|
-
return new Promise((resolve3, reject) => {
|
|
871
|
-
const cleanup = () => {
|
|
872
|
-
clearTimeout(timer);
|
|
873
|
-
signal.removeEventListener("abort", onAbort);
|
|
874
|
-
};
|
|
875
|
-
const onAbort = () => {
|
|
876
|
-
cleanup();
|
|
877
|
-
reject(createAbortError());
|
|
878
|
-
};
|
|
879
|
-
const timer = setTimeout(() => {
|
|
880
|
-
cleanup();
|
|
881
|
-
resolve3();
|
|
882
|
-
}, ms);
|
|
883
|
-
signal.addEventListener("abort", onAbort, { once: true });
|
|
884
|
-
});
|
|
885
|
-
}
|
|
886
|
-
async function fetchWithTimeout(url, init, signal) {
|
|
887
|
-
if (signal?.aborted) {
|
|
888
|
-
throw createAbortError();
|
|
889
|
-
}
|
|
890
|
-
const controller = new AbortController();
|
|
891
|
-
const timer = setTimeout(() => controller.abort(), LLM_TIMEOUT_MS);
|
|
892
|
-
const onAbort = () => controller.abort();
|
|
893
|
-
signal?.addEventListener("abort", onAbort, { once: true });
|
|
894
|
-
try {
|
|
895
|
-
return await fetch(url, { ...init, signal: controller.signal });
|
|
896
|
-
} finally {
|
|
897
|
-
clearTimeout(timer);
|
|
898
|
-
signal?.removeEventListener("abort", onAbort);
|
|
899
|
-
}
|
|
900
|
-
}
|
|
901
|
-
async function fetchWithRetry(url, init, signal) {
|
|
902
|
-
for (let attempt = 0; ; attempt++) {
|
|
903
|
-
let res;
|
|
904
|
-
try {
|
|
905
|
-
res = await fetchWithTimeout(url, init, signal);
|
|
906
|
-
} catch (e) {
|
|
907
|
-
if (attempt >= MAX_RETRIES || e.name === "AbortError") {
|
|
908
|
-
throw e;
|
|
909
|
-
}
|
|
910
|
-
await sleepWithSignal(Math.min(1e3 * 2 ** attempt, 8e3), signal);
|
|
911
|
-
continue;
|
|
912
|
-
}
|
|
913
|
-
if (res.ok || attempt >= MAX_RETRIES || !RETRYABLE_STATUSES.has(res.status)) {
|
|
914
|
-
return res;
|
|
915
|
-
}
|
|
916
|
-
const retryAfter = res.headers.get("retry-after");
|
|
917
|
-
const delay = retryAfter ? Math.min(parseInt(retryAfter, 10) * 1e3 || 1e3 * 2 ** attempt, 3e4) : Math.min(1e3 * 2 ** attempt, 8e3);
|
|
918
|
-
await sleepWithSignal(delay, signal);
|
|
919
|
-
}
|
|
920
|
-
}
|
|
921
|
-
function isReasoningModel(model) {
|
|
922
|
-
return /^o\d/.test(model) || /^gpt-5(\b|[-.])/.test(model);
|
|
923
|
-
}
|
|
924
|
-
function createLlmClient(config) {
|
|
925
|
-
if (config.provider === "anthropic") {
|
|
926
|
-
return new AnthropicClient(config);
|
|
927
|
-
}
|
|
928
|
-
return new OpenAIClient(config);
|
|
929
|
-
}
|
|
930
|
-
async function verifyLlmApiKey(provider, apiKey, signal) {
|
|
931
|
-
const url = provider === "anthropic" ? "https://api.anthropic.com/v1/models?limit=1" : "https://api.openai.com/v1/models";
|
|
932
|
-
const headers = provider === "anthropic" ? { "x-api-key": apiKey, "anthropic-version": "2023-06-01" } : { Authorization: `Bearer ${apiKey}` };
|
|
933
|
-
try {
|
|
934
|
-
const res = await fetchWithTimeout(url, { method: "GET", headers }, signal);
|
|
935
|
-
if (res.ok) {
|
|
936
|
-
await res.body?.cancel().catch(() => void 0);
|
|
937
|
-
return { ok: true };
|
|
938
|
-
}
|
|
939
|
-
const body = (await res.text().catch(() => "")).slice(0, 500);
|
|
940
|
-
if (res.status === 401 || res.status === 403) {
|
|
941
|
-
return { ok: false, reason: "invalid", status: res.status, body };
|
|
942
|
-
}
|
|
943
|
-
return { ok: false, reason: "unavailable", error: `HTTP ${res.status}: ${body.slice(0, 200)}` };
|
|
944
|
-
} catch (e) {
|
|
945
|
-
return { ok: false, reason: "unavailable", error: e?.message ?? String(e) };
|
|
946
|
-
}
|
|
947
|
-
}
|
|
948
|
-
var AnthropicClient = class {
|
|
949
|
-
constructor(config) {
|
|
950
|
-
this.config = config;
|
|
951
|
-
}
|
|
952
|
-
totalIn = 0;
|
|
953
|
-
totalOut = 0;
|
|
954
|
-
logTokens(usage) {
|
|
955
|
-
if (this.config.logUsage && usage) {
|
|
956
|
-
this.totalIn += usage.input_tokens ?? 0;
|
|
957
|
-
this.totalOut += usage.output_tokens ?? 0;
|
|
958
|
-
console.log(
|
|
959
|
-
` [LLM] ${this.config.model} tokens: in=${usage.input_tokens ?? 0} out=${usage.output_tokens ?? 0} (total: in=${this.totalIn} out=${this.totalOut})`
|
|
960
|
-
);
|
|
961
|
-
}
|
|
962
|
-
}
|
|
963
|
-
async complete(systemPrompt, userInput, signal) {
|
|
964
|
-
const res = await fetchWithRetry(
|
|
965
|
-
"https://api.anthropic.com/v1/messages",
|
|
966
|
-
{
|
|
967
|
-
method: "POST",
|
|
968
|
-
headers: {
|
|
969
|
-
"Content-Type": "application/json",
|
|
970
|
-
"x-api-key": this.config.apiKey,
|
|
971
|
-
"anthropic-version": "2023-06-01"
|
|
972
|
-
},
|
|
973
|
-
body: JSON.stringify({
|
|
974
|
-
model: this.config.model,
|
|
975
|
-
max_tokens: this.config.maxTokens,
|
|
976
|
-
system: systemPrompt,
|
|
977
|
-
messages: [{ role: "user", content: userInput }]
|
|
978
|
-
})
|
|
979
|
-
},
|
|
980
|
-
signal
|
|
981
|
-
);
|
|
982
|
-
if (!res.ok) {
|
|
983
|
-
throw new Error(`Anthropic API error: ${res.status} ${await res.text()}`);
|
|
984
|
-
}
|
|
985
|
-
const data = await res.json();
|
|
986
|
-
this.logTokens(data.usage);
|
|
987
|
-
const textBlock = data.content?.find((b) => b.type === "text");
|
|
988
|
-
return textBlock?.text ?? "";
|
|
989
|
-
}
|
|
990
|
-
async completeWithTools(systemPrompt, messages, tools, signal) {
|
|
991
|
-
const anthropicTools = tools.map((t) => ({
|
|
992
|
-
name: t.name,
|
|
993
|
-
description: t.description,
|
|
994
|
-
input_schema: {
|
|
995
|
-
type: "object",
|
|
996
|
-
properties: Object.fromEntries(
|
|
997
|
-
t.parameters.map((p) => [p.name, { type: "string", description: p.description }])
|
|
998
|
-
),
|
|
999
|
-
required: t.parameters.filter((p) => p.required).map((p) => p.name)
|
|
1000
|
-
}
|
|
1001
|
-
}));
|
|
1002
|
-
const res = await fetchWithRetry(
|
|
1003
|
-
"https://api.anthropic.com/v1/messages",
|
|
1004
|
-
{
|
|
1005
|
-
method: "POST",
|
|
1006
|
-
headers: {
|
|
1007
|
-
"Content-Type": "application/json",
|
|
1008
|
-
"x-api-key": this.config.apiKey,
|
|
1009
|
-
"anthropic-version": "2023-06-01"
|
|
1010
|
-
},
|
|
1011
|
-
body: JSON.stringify({
|
|
1012
|
-
model: this.config.model,
|
|
1013
|
-
max_tokens: this.config.maxTokens,
|
|
1014
|
-
system: systemPrompt,
|
|
1015
|
-
messages,
|
|
1016
|
-
tools: anthropicTools
|
|
1017
|
-
})
|
|
1018
|
-
},
|
|
1019
|
-
signal
|
|
1020
|
-
);
|
|
1021
|
-
if (!res.ok) {
|
|
1022
|
-
throw new Error(`Anthropic API error: ${res.status} ${await res.text()}`);
|
|
1023
|
-
}
|
|
1024
|
-
const data = await res.json();
|
|
1025
|
-
this.logTokens(data.usage);
|
|
1026
|
-
const content = data.content ?? [];
|
|
1027
|
-
const toolUses = content.filter((b) => b.type === "tool_use");
|
|
1028
|
-
if (toolUses.length > 0) {
|
|
1029
|
-
const calls = toolUses.map((t) => ({
|
|
1030
|
-
id: t.id,
|
|
1031
|
-
name: t.name,
|
|
1032
|
-
arguments: t.input ?? {}
|
|
1033
|
-
}));
|
|
1034
|
-
return {
|
|
1035
|
-
type: "tool_use",
|
|
1036
|
-
calls,
|
|
1037
|
-
assistantMessage: { role: "assistant", content }
|
|
1038
|
-
};
|
|
1039
|
-
}
|
|
1040
|
-
const textBlock = content.find((b) => b.type === "text");
|
|
1041
|
-
return { type: "text", text: textBlock?.text ?? "" };
|
|
1042
|
-
}
|
|
1043
|
-
formatToolResultMessages(results) {
|
|
1044
|
-
return [
|
|
1045
|
-
{
|
|
1046
|
-
role: "user",
|
|
1047
|
-
content: results.map((r) => ({
|
|
1048
|
-
type: "tool_result",
|
|
1049
|
-
tool_use_id: r.callId,
|
|
1050
|
-
content: r.content
|
|
1051
|
-
}))
|
|
1052
|
-
}
|
|
1053
|
-
];
|
|
1054
|
-
}
|
|
1055
|
-
};
|
|
1056
|
-
var OpenAIClient = class {
|
|
1057
|
-
constructor(config) {
|
|
1058
|
-
this.config = config;
|
|
1059
|
-
}
|
|
1060
|
-
totalIn = 0;
|
|
1061
|
-
totalOut = 0;
|
|
1062
|
-
logTokens(usage) {
|
|
1063
|
-
if (this.config.logUsage && usage) {
|
|
1064
|
-
this.totalIn += usage.prompt_tokens ?? 0;
|
|
1065
|
-
this.totalOut += usage.completion_tokens ?? 0;
|
|
1066
|
-
console.log(
|
|
1067
|
-
` [LLM] ${this.config.model} tokens: in=${usage.prompt_tokens ?? 0} out=${usage.completion_tokens ?? 0} (total: in=${this.totalIn} out=${this.totalOut})`
|
|
1068
|
-
);
|
|
1069
|
-
}
|
|
1070
|
-
}
|
|
1071
|
-
async complete(systemPrompt, userInput, signal) {
|
|
1072
|
-
const isReasoning = isReasoningModel(this.config.model);
|
|
1073
|
-
const res = await fetchWithRetry(
|
|
1074
|
-
"https://api.openai.com/v1/chat/completions",
|
|
1075
|
-
{
|
|
1076
|
-
method: "POST",
|
|
1077
|
-
headers: {
|
|
1078
|
-
"Content-Type": "application/json",
|
|
1079
|
-
Authorization: `Bearer ${this.config.apiKey}`
|
|
1080
|
-
},
|
|
1081
|
-
body: JSON.stringify({
|
|
1082
|
-
model: this.config.model,
|
|
1083
|
-
...isReasoning ? { max_completion_tokens: this.config.maxTokens } : { max_tokens: this.config.maxTokens },
|
|
1084
|
-
messages: [
|
|
1085
|
-
{ role: isReasoning ? "developer" : "system", content: systemPrompt },
|
|
1086
|
-
{ role: "user", content: userInput }
|
|
1087
|
-
]
|
|
1088
|
-
})
|
|
1089
|
-
},
|
|
1090
|
-
signal
|
|
1091
|
-
);
|
|
1092
|
-
if (!res.ok) {
|
|
1093
|
-
throw new Error(`OpenAI API error: ${res.status} ${await res.text()}`);
|
|
1094
|
-
}
|
|
1095
|
-
const data = await res.json();
|
|
1096
|
-
this.logTokens(data.usage);
|
|
1097
|
-
return data.choices?.[0]?.message?.content ?? "";
|
|
1098
|
-
}
|
|
1099
|
-
async completeWithTools(systemPrompt, messages, tools, signal) {
|
|
1100
|
-
const openaiTools = tools.map((t) => ({
|
|
1101
|
-
type: "function",
|
|
1102
|
-
function: {
|
|
1103
|
-
name: t.name,
|
|
1104
|
-
description: t.description,
|
|
1105
|
-
parameters: {
|
|
1106
|
-
type: "object",
|
|
1107
|
-
properties: Object.fromEntries(
|
|
1108
|
-
t.parameters.map((p) => [p.name, { type: "string", description: p.description }])
|
|
1109
|
-
),
|
|
1110
|
-
required: t.parameters.filter((p) => p.required).map((p) => p.name)
|
|
1111
|
-
}
|
|
1112
|
-
}
|
|
1113
|
-
}));
|
|
1114
|
-
const isReasoning = isReasoningModel(this.config.model);
|
|
1115
|
-
const res = await fetchWithRetry(
|
|
1116
|
-
"https://api.openai.com/v1/chat/completions",
|
|
1117
|
-
{
|
|
1118
|
-
method: "POST",
|
|
1119
|
-
headers: {
|
|
1120
|
-
"Content-Type": "application/json",
|
|
1121
|
-
Authorization: `Bearer ${this.config.apiKey}`
|
|
1122
|
-
},
|
|
1123
|
-
body: JSON.stringify({
|
|
1124
|
-
model: this.config.model,
|
|
1125
|
-
...isReasoning ? { max_completion_tokens: this.config.maxTokens } : { max_tokens: this.config.maxTokens },
|
|
1126
|
-
messages: [
|
|
1127
|
-
{ role: isReasoning ? "developer" : "system", content: systemPrompt },
|
|
1128
|
-
...messages
|
|
1129
|
-
],
|
|
1130
|
-
tools: openaiTools
|
|
1131
|
-
})
|
|
1132
|
-
},
|
|
1133
|
-
signal
|
|
1134
|
-
);
|
|
1135
|
-
if (!res.ok) {
|
|
1136
|
-
throw new Error(`OpenAI API error: ${res.status} ${await res.text()}`);
|
|
1137
|
-
}
|
|
1138
|
-
const data = await res.json();
|
|
1139
|
-
this.logTokens(data.usage);
|
|
1140
|
-
const message = data.choices?.[0]?.message;
|
|
1141
|
-
if (message?.tool_calls?.length > 0) {
|
|
1142
|
-
const calls = message.tool_calls.map((tc) => {
|
|
1143
|
-
let args;
|
|
1144
|
-
try {
|
|
1145
|
-
args = JSON.parse(tc.function.arguments ?? "{}");
|
|
1146
|
-
} catch {
|
|
1147
|
-
args = {};
|
|
1148
|
-
}
|
|
1149
|
-
return { id: tc.id, name: tc.function.name, arguments: args };
|
|
1150
|
-
});
|
|
1151
|
-
return {
|
|
1152
|
-
type: "tool_use",
|
|
1153
|
-
calls,
|
|
1154
|
-
assistantMessage: message
|
|
1155
|
-
};
|
|
1156
|
-
}
|
|
1157
|
-
return { type: "text", text: message?.content ?? "" };
|
|
1158
|
-
}
|
|
1159
|
-
formatToolResultMessages(results) {
|
|
1160
|
-
return results.map((r) => ({
|
|
1161
|
-
role: "tool",
|
|
1162
|
-
tool_call_id: r.callId,
|
|
1163
|
-
content: r.content
|
|
1164
|
-
}));
|
|
1165
|
-
}
|
|
1166
|
-
};
|
|
1407
|
+
// src/commands/start.ts
|
|
1408
|
+
init_llm();
|
|
1167
1409
|
|
|
1168
1410
|
// src/llm/resolve.ts
|
|
1169
|
-
var
|
|
1411
|
+
var DEFAULT_MAX_TOKENS3 = 4096;
|
|
1170
1412
|
function resolveSkillLlm(input, agentDefault) {
|
|
1171
1413
|
const override = input.llmOverride;
|
|
1172
1414
|
const overridePairSet = override?.provider !== void 0 && override.model !== void 0;
|
|
@@ -1184,7 +1426,7 @@ function resolveSkillLlm(input, agentDefault) {
|
|
|
1184
1426
|
error: `Skill "${input.skillName}" at ${input.skillMdPath}: LLM model is required - declare "provider" + "model" in the SKILL.md frontmatter or set agent-level llm via 'npx @elisym/cli profile <agent>'.`
|
|
1185
1427
|
};
|
|
1186
1428
|
}
|
|
1187
|
-
const maxTokens = override?.maxTokens ?? agentDefault?.max_tokens ??
|
|
1429
|
+
const maxTokens = override?.maxTokens ?? agentDefault?.max_tokens ?? DEFAULT_MAX_TOKENS3;
|
|
1188
1430
|
return { provider, model, maxTokens };
|
|
1189
1431
|
}
|
|
1190
1432
|
|
|
@@ -1210,34 +1452,32 @@ function resolveTripleForOverride(override, agentDefault) {
|
|
|
1210
1452
|
if (!provider || !model) {
|
|
1211
1453
|
return void 0;
|
|
1212
1454
|
}
|
|
1213
|
-
const maxTokens = override?.maxTokens ?? agentDefault?.max_tokens ??
|
|
1455
|
+
const maxTokens = override?.maxTokens ?? agentDefault?.max_tokens ?? DEFAULT_MAX_TOKENS3;
|
|
1214
1456
|
return { provider, model, maxTokens };
|
|
1215
1457
|
}
|
|
1216
1458
|
|
|
1217
1459
|
// src/llm/keys.ts
|
|
1218
|
-
|
|
1219
|
-
anthropic: "ANTHROPIC_API_KEY",
|
|
1220
|
-
openai: "OPENAI_API_KEY"
|
|
1221
|
-
};
|
|
1222
|
-
var SECRET_FIELD_FOR_PROVIDER = {
|
|
1223
|
-
anthropic: "anthropic_api_key",
|
|
1224
|
-
openai: "openai_api_key"
|
|
1225
|
-
};
|
|
1460
|
+
init_registry();
|
|
1226
1461
|
function resolveProviderApiKey(input) {
|
|
1227
1462
|
const { provider, secrets, dependentSkills } = input;
|
|
1228
|
-
const
|
|
1229
|
-
|
|
1230
|
-
|
|
1463
|
+
const descriptor = getLlmProvider(provider);
|
|
1464
|
+
if (!descriptor) {
|
|
1465
|
+
const skillList2 = dependentSkills.length > 0 ? dependentSkills.join(", ") : "<none>";
|
|
1466
|
+
return {
|
|
1467
|
+
error: `Provider "${provider}" is not registered (required by skill(s): ${skillList2}).`
|
|
1468
|
+
};
|
|
1469
|
+
}
|
|
1470
|
+
const perProviderValue = secrets.llm_api_keys?.[provider];
|
|
1471
|
+
if (typeof perProviderValue === "string" && perProviderValue.length > 0) {
|
|
1231
1472
|
return { apiKey: perProviderValue, origin: "secrets-per-provider" };
|
|
1232
1473
|
}
|
|
1233
|
-
const
|
|
1234
|
-
const envValue = process.env[envVar];
|
|
1474
|
+
const envValue = process.env[descriptor.envVar];
|
|
1235
1475
|
if (envValue) {
|
|
1236
1476
|
return { apiKey: envValue, origin: "env" };
|
|
1237
1477
|
}
|
|
1238
1478
|
const skillList = dependentSkills.length > 0 ? dependentSkills.join(", ") : "<none>";
|
|
1239
1479
|
return {
|
|
1240
|
-
error: `Provider "${provider}" needs an API key (required by skill(s): ${skillList}). Set secrets.${
|
|
1480
|
+
error: `Provider "${provider}" needs an API key (required by skill(s): ${skillList}). Set secrets.llm_api_keys.${provider} via 'npx @elisym/cli profile <agent>' or export ${descriptor.envVar}.`
|
|
1241
1481
|
};
|
|
1242
1482
|
}
|
|
1243
1483
|
function resolveLevel(options) {
|
|
@@ -2505,7 +2745,10 @@ async function cmdStart(nameArg, options = {}) {
|
|
|
2505
2745
|
const rpcUrl = getRpcUrl(walletNetwork);
|
|
2506
2746
|
const rpc = createSolanaRpc(rpcUrl);
|
|
2507
2747
|
const walletAddress = address(solanaAddress);
|
|
2508
|
-
const { value: balanceLamports } = await
|
|
2748
|
+
const [{ value: balanceLamports }, usdcBalance] = await Promise.all([
|
|
2749
|
+
rpc.getBalance(walletAddress).send(),
|
|
2750
|
+
fetchUsdcBalance(rpc, walletAddress)
|
|
2751
|
+
]);
|
|
2509
2752
|
const balance = Number(balanceLamports);
|
|
2510
2753
|
console.log(" Wallet");
|
|
2511
2754
|
console.log(` Network ${walletNetwork}`);
|
|
@@ -2513,7 +2756,8 @@ async function cmdStart(nameArg, options = {}) {
|
|
|
2513
2756
|
if (process.env.SOLANA_RPC_URL) {
|
|
2514
2757
|
console.log(` RPC ${process.env.SOLANA_RPC_URL} (custom)`);
|
|
2515
2758
|
}
|
|
2516
|
-
console.log(`
|
|
2759
|
+
console.log(` SOL ${formatSol(balance)} (${balance} lamports)`);
|
|
2760
|
+
console.log(` USDC ${formatAssetAmount(USDC_SOLANA_DEVNET, usdcBalance)}`);
|
|
2517
2761
|
if (balance === 0) {
|
|
2518
2762
|
console.log(" ! Wallet is empty. Get devnet SOL: https://faucet.solana.com");
|
|
2519
2763
|
}
|
|
@@ -2615,7 +2859,7 @@ async function cmdStart(nameArg, options = {}) {
|
|
|
2615
2859
|
if (!apiKey) {
|
|
2616
2860
|
continue;
|
|
2617
2861
|
}
|
|
2618
|
-
const envVar = provider
|
|
2862
|
+
const envVar = getLlmProvider(provider)?.envVar ?? `${provider.toUpperCase()}_API_KEY`;
|
|
2619
2863
|
process.stdout.write(` Verifying ${provider} API key... `);
|
|
2620
2864
|
const verification = await verifyLlmApiKey(provider, apiKey);
|
|
2621
2865
|
if (verification.ok) {
|
|
@@ -3000,30 +3244,6 @@ async function loadAgentWithPrompt(name, cwd) {
|
|
|
3000
3244
|
}
|
|
3001
3245
|
throw new Error("Unreachable");
|
|
3002
3246
|
}
|
|
3003
|
-
async function fetchUsdcBalance(rpc, owner) {
|
|
3004
|
-
const mint = USDC_SOLANA_DEVNET.mint;
|
|
3005
|
-
if (!mint) {
|
|
3006
|
-
return 0n;
|
|
3007
|
-
}
|
|
3008
|
-
try {
|
|
3009
|
-
const response = await rpc.getTokenAccountsByOwner(
|
|
3010
|
-
owner,
|
|
3011
|
-
{ mint: address(mint) },
|
|
3012
|
-
{ encoding: "jsonParsed", commitment: "confirmed" }
|
|
3013
|
-
).send();
|
|
3014
|
-
let total = 0n;
|
|
3015
|
-
for (const entry of response.value) {
|
|
3016
|
-
const parsed = entry.account.data;
|
|
3017
|
-
const raw = parsed?.parsed?.info?.tokenAmount?.amount;
|
|
3018
|
-
if (typeof raw === "string") {
|
|
3019
|
-
total += BigInt(raw);
|
|
3020
|
-
}
|
|
3021
|
-
}
|
|
3022
|
-
return total;
|
|
3023
|
-
} catch {
|
|
3024
|
-
return 0n;
|
|
3025
|
-
}
|
|
3026
|
-
}
|
|
3027
3247
|
async function cmdWallet(name) {
|
|
3028
3248
|
const cwd = process.cwd();
|
|
3029
3249
|
if (!name) {
|