@elisym/cli 0.5.1 → 0.6.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 +162 -36
- package/dist/index.js.map +1 -1
- package/package.json +4 -2
- package/skills-examples/usdc-summarize/SKILL.md +17 -0
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, ElisymClient, MediaService, jobRequestKind, DEFAULT_KIND_OFFSET, DEFAULTS, makeCensor, DEFAULT_REDACT_PATHS, createSlidingWindowLimiter, getProtocolProgramId, getProtocolConfig, LIMITS, calculateProtocolFee, BoundedSet } from '@elisym/sdk';
|
|
4
|
+
import { SolanaPaymentStrategy, validateAgentName, RELAYS, ElisymIdentity, formatSol, formatAssetAmount, ElisymClient, MediaService, jobRequestKind, DEFAULT_KIND_OFFSET, DEFAULTS, USDC_SOLANA_DEVNET, makeCensor, DEFAULT_REDACT_PATHS, createSlidingWindowLimiter, getProtocolProgramId, getProtocolConfig, LIMITS, calculateProtocolFee, BoundedSet, 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 } from 'nostr-tools';
|
|
@@ -268,6 +268,11 @@ async function promptYaml(inquirer) {
|
|
|
268
268
|
}
|
|
269
269
|
}
|
|
270
270
|
]);
|
|
271
|
+
if (solanaAddress) {
|
|
272
|
+
console.log(
|
|
273
|
+
" The wallet receives every asset on Solana (SOL directly, USDC and other\n SPL tokens via their ATA). Each skill declares its own price and token\n in SKILL.md. Fund with SOL via `solana airdrop` or USDC via https://faucet.circle.com."
|
|
274
|
+
);
|
|
275
|
+
}
|
|
271
276
|
const { llmProvider } = await inquirer.prompt([
|
|
272
277
|
{
|
|
273
278
|
type: "list",
|
|
@@ -815,12 +820,33 @@ async function fetchWithRetry(url, init, signal) {
|
|
|
815
820
|
await sleepWithSignal(delay, signal);
|
|
816
821
|
}
|
|
817
822
|
}
|
|
823
|
+
function isReasoningModel(model) {
|
|
824
|
+
return /^o\d/.test(model) || /^gpt-5(\b|[-.])/.test(model);
|
|
825
|
+
}
|
|
818
826
|
function createLlmClient(config) {
|
|
819
827
|
if (config.provider === "anthropic") {
|
|
820
828
|
return new AnthropicClient(config);
|
|
821
829
|
}
|
|
822
830
|
return new OpenAIClient(config);
|
|
823
831
|
}
|
|
832
|
+
async function verifyLlmApiKey(provider, apiKey, signal) {
|
|
833
|
+
const url = provider === "anthropic" ? "https://api.anthropic.com/v1/models?limit=1" : "https://api.openai.com/v1/models";
|
|
834
|
+
const headers = provider === "anthropic" ? { "x-api-key": apiKey, "anthropic-version": "2023-06-01" } : { Authorization: `Bearer ${apiKey}` };
|
|
835
|
+
try {
|
|
836
|
+
const res = await fetchWithTimeout(url, { method: "GET", headers }, signal);
|
|
837
|
+
if (res.ok) {
|
|
838
|
+
await res.body?.cancel().catch(() => void 0);
|
|
839
|
+
return { ok: true };
|
|
840
|
+
}
|
|
841
|
+
const body = (await res.text().catch(() => "")).slice(0, 500);
|
|
842
|
+
if (res.status === 401 || res.status === 403) {
|
|
843
|
+
return { ok: false, reason: "invalid", status: res.status, body };
|
|
844
|
+
}
|
|
845
|
+
return { ok: false, reason: "unavailable", error: `HTTP ${res.status}: ${body.slice(0, 200)}` };
|
|
846
|
+
} catch (e) {
|
|
847
|
+
return { ok: false, reason: "unavailable", error: e?.message ?? String(e) };
|
|
848
|
+
}
|
|
849
|
+
}
|
|
824
850
|
var AnthropicClient = class {
|
|
825
851
|
constructor(config) {
|
|
826
852
|
this.config = config;
|
|
@@ -945,7 +971,7 @@ var OpenAIClient = class {
|
|
|
945
971
|
}
|
|
946
972
|
}
|
|
947
973
|
async complete(systemPrompt, userInput, signal) {
|
|
948
|
-
const isReasoning =
|
|
974
|
+
const isReasoning = isReasoningModel(this.config.model);
|
|
949
975
|
const res = await fetchWithRetry(
|
|
950
976
|
"https://api.openai.com/v1/chat/completions",
|
|
951
977
|
{
|
|
@@ -987,7 +1013,7 @@ var OpenAIClient = class {
|
|
|
987
1013
|
}
|
|
988
1014
|
}
|
|
989
1015
|
}));
|
|
990
|
-
const isReasoning =
|
|
1016
|
+
const isReasoning = isReasoningModel(this.config.model);
|
|
991
1017
|
const res = await fetchWithRetry(
|
|
992
1018
|
"https://api.openai.com/v1/chat/completions",
|
|
993
1019
|
{
|
|
@@ -1093,7 +1119,11 @@ var LEDGER_RETENTION_MS = 30 * 24 * 60 * 60 * 1e3;
|
|
|
1093
1119
|
var TOTAL_JOB_TIMEOUT_MS = 5 * 60 * 1e3;
|
|
1094
1120
|
function resolveJobPrice(tags, skills) {
|
|
1095
1121
|
const skill = skills.route(tags);
|
|
1096
|
-
return skill?.
|
|
1122
|
+
return skill?.priceSubunits ?? 0;
|
|
1123
|
+
}
|
|
1124
|
+
function resolveJobAsset(tags, skills) {
|
|
1125
|
+
const skill = skills.route(tags);
|
|
1126
|
+
return skill?.asset ?? NATIVE_SOL;
|
|
1097
1127
|
}
|
|
1098
1128
|
var RATE_LIMIT_WINDOW_MS = 10 * 60 * 1e3;
|
|
1099
1129
|
var MAX_JOBS_PER_CUSTOMER = 20;
|
|
@@ -1203,16 +1233,18 @@ var AgentRuntime = class {
|
|
|
1203
1233
|
log("Agent runtime started. Listening for jobs...");
|
|
1204
1234
|
await new Promise((resolve3) => {
|
|
1205
1235
|
this.abortController.signal.addEventListener("abort", () => resolve3(), { once: true });
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1236
|
+
let shuttingDown = false;
|
|
1237
|
+
const onSignal = () => {
|
|
1238
|
+
if (shuttingDown) {
|
|
1239
|
+
return;
|
|
1240
|
+
}
|
|
1241
|
+
shuttingDown = true;
|
|
1212
1242
|
log("Shutting down...");
|
|
1213
1243
|
this.stop();
|
|
1214
1244
|
setTimeout(() => process.exit(0), 3e3).unref();
|
|
1215
|
-
}
|
|
1245
|
+
};
|
|
1246
|
+
process.on("SIGINT", onSignal);
|
|
1247
|
+
process.on("SIGTERM", onSignal);
|
|
1216
1248
|
});
|
|
1217
1249
|
}
|
|
1218
1250
|
/** Drop expired hits from both sliding-window limiters. */
|
|
@@ -1283,6 +1315,7 @@ var AgentRuntime = class {
|
|
|
1283
1315
|
throw new Error(`Input too long: ${job.input.length} chars (max ${LIMITS.MAX_INPUT_LENGTH})`);
|
|
1284
1316
|
}
|
|
1285
1317
|
const jobPrice = resolveJobPrice(job.tags, this.skills);
|
|
1318
|
+
const jobAsset = resolveJobAsset(job.tags, this.skills);
|
|
1286
1319
|
let netAmount;
|
|
1287
1320
|
let paymentRequest;
|
|
1288
1321
|
this.ledger.recordPaid({
|
|
@@ -1297,11 +1330,16 @@ var AgentRuntime = class {
|
|
|
1297
1330
|
created_at: Math.floor(Date.now() / 1e3)
|
|
1298
1331
|
});
|
|
1299
1332
|
if (jobPrice > 0) {
|
|
1300
|
-
const result = await this.collectPayment(job, jobPrice, signal);
|
|
1333
|
+
const result = await this.collectPayment(job, jobPrice, jobAsset, signal);
|
|
1301
1334
|
netAmount = result.netAmount;
|
|
1302
1335
|
paymentRequest = result.paymentRequest;
|
|
1303
1336
|
this.ledger.updatePayment(job.jobId, netAmount, paymentRequest);
|
|
1304
|
-
log(
|
|
1337
|
+
log(
|
|
1338
|
+
`[${job.jobId.slice(0, 8)}] Payment confirmed: ${formatAssetAmount(
|
|
1339
|
+
jobAsset,
|
|
1340
|
+
BigInt(netAmount)
|
|
1341
|
+
)}`
|
|
1342
|
+
);
|
|
1305
1343
|
this.callbacks.onPaymentReceived?.(job.jobId, netAmount);
|
|
1306
1344
|
}
|
|
1307
1345
|
await this.transport.sendFeedback(job, { type: "processing" }).catch(() => {
|
|
@@ -1331,7 +1369,7 @@ var AgentRuntime = class {
|
|
|
1331
1369
|
* Collect payment for a job. Creates payment request, sends PaymentRequired feedback,
|
|
1332
1370
|
* polls for on-chain confirmation. Aborts if signal fires.
|
|
1333
1371
|
*/
|
|
1334
|
-
async collectPayment(job, jobPrice, signal) {
|
|
1372
|
+
async collectPayment(job, jobPrice, jobAsset, signal) {
|
|
1335
1373
|
const log = this.callbacks.onLog ?? console.log;
|
|
1336
1374
|
if (!this.config.solanaAddress) {
|
|
1337
1375
|
throw new Error("Solana address not configured");
|
|
@@ -1341,12 +1379,14 @@ var AgentRuntime = class {
|
|
|
1341
1379
|
this.config.solanaAddress,
|
|
1342
1380
|
jobPrice,
|
|
1343
1381
|
protocolConfig,
|
|
1344
|
-
{ expirySecs: this.config.paymentTimeoutSecs }
|
|
1382
|
+
{ expirySecs: this.config.paymentTimeoutSecs, asset: jobAsset }
|
|
1345
1383
|
);
|
|
1346
1384
|
const requestJson = JSON.stringify(request);
|
|
1347
1385
|
const fee = calculateProtocolFee(jobPrice, protocolConfig.feeBps);
|
|
1348
1386
|
const netAmount = jobPrice - fee;
|
|
1349
|
-
log(
|
|
1387
|
+
log(
|
|
1388
|
+
`[${job.jobId.slice(0, 8)}] Payment required: ${formatAssetAmount(jobAsset, BigInt(jobPrice))} (fee: ${formatAssetAmount(jobAsset, BigInt(fee))})`
|
|
1389
|
+
);
|
|
1350
1390
|
this.ledger.updatePayment(job.jobId, void 0, requestJson);
|
|
1351
1391
|
await this.transport.sendFeedback(job, {
|
|
1352
1392
|
type: "payment-required",
|
|
@@ -1471,11 +1511,11 @@ var AgentRuntime = class {
|
|
|
1471
1511
|
this.ledger.markFailed(entry.job_id);
|
|
1472
1512
|
return;
|
|
1473
1513
|
}
|
|
1474
|
-
if (skill.
|
|
1514
|
+
if (skill.priceSubunits > 0 && !entry.net_amount) {
|
|
1475
1515
|
if (entry.payment_request) {
|
|
1476
1516
|
const verified = await this.reVerifyPayment(
|
|
1477
1517
|
entry,
|
|
1478
|
-
skill.
|
|
1518
|
+
skill.priceSubunits,
|
|
1479
1519
|
log,
|
|
1480
1520
|
recoveryAbort.signal
|
|
1481
1521
|
);
|
|
@@ -1516,7 +1556,7 @@ var AgentRuntime = class {
|
|
|
1516
1556
|
* job will be marked failed. For mainnet: use monitoring, avoid extended downtime, or
|
|
1517
1557
|
* configure an archive RPC via SOLANA_RPC_URL.
|
|
1518
1558
|
*/
|
|
1519
|
-
async reVerifyPayment(entry,
|
|
1559
|
+
async reVerifyPayment(entry, priceSubunits, log, signal) {
|
|
1520
1560
|
try {
|
|
1521
1561
|
const request = JSON.parse(entry.payment_request);
|
|
1522
1562
|
const rpc = createSolanaRpc(getRpcUrl(this.config.network));
|
|
@@ -1550,10 +1590,10 @@ var AgentRuntime = class {
|
|
|
1550
1590
|
result = await payment.verifyPayment(rpc, request, protocolConfig);
|
|
1551
1591
|
}
|
|
1552
1592
|
if (result.verified) {
|
|
1553
|
-
const fee = calculateProtocolFee(
|
|
1554
|
-
const netAmount =
|
|
1593
|
+
const fee = calculateProtocolFee(priceSubunits, protocolConfig.feeBps);
|
|
1594
|
+
const netAmount = priceSubunits - fee;
|
|
1555
1595
|
this.ledger.updatePayment(entry.job_id, netAmount, entry.payment_request);
|
|
1556
|
-
log(`[${entry.job_id.slice(0, 8)}] Recovery: payment re-verified (${netAmount}
|
|
1596
|
+
log(`[${entry.job_id.slice(0, 8)}] Recovery: payment re-verified (${netAmount} subunits)`);
|
|
1557
1597
|
return true;
|
|
1558
1598
|
}
|
|
1559
1599
|
log(`[${entry.job_id.slice(0, 8)}] Recovery: payment not found on-chain, marking failed`);
|
|
@@ -1576,13 +1616,22 @@ var SkillRegistry = class {
|
|
|
1576
1616
|
this.skills.push(skill);
|
|
1577
1617
|
}
|
|
1578
1618
|
route(tags) {
|
|
1579
|
-
for (const
|
|
1580
|
-
|
|
1581
|
-
|
|
1582
|
-
|
|
1583
|
-
}
|
|
1619
|
+
for (const tag of tags) {
|
|
1620
|
+
const byName = this.skills.find((skill) => skill.name === tag);
|
|
1621
|
+
if (byName) {
|
|
1622
|
+
return byName;
|
|
1584
1623
|
}
|
|
1585
1624
|
}
|
|
1625
|
+
for (const tag of tags) {
|
|
1626
|
+
const byCap = this.skills.find((skill) => skill.capabilities.includes(tag));
|
|
1627
|
+
if (byCap) {
|
|
1628
|
+
return byCap;
|
|
1629
|
+
}
|
|
1630
|
+
}
|
|
1631
|
+
const hasSpecificTag = tags.some((tag) => tag && tag !== "elisym");
|
|
1632
|
+
if (hasSpecificTag) {
|
|
1633
|
+
return null;
|
|
1634
|
+
}
|
|
1586
1635
|
return this.defaultIndex !== null ? this.skills[this.defaultIndex] : null;
|
|
1587
1636
|
}
|
|
1588
1637
|
allCapabilities() {
|
|
@@ -1599,22 +1648,49 @@ var ScriptSkill = class {
|
|
|
1599
1648
|
name;
|
|
1600
1649
|
description;
|
|
1601
1650
|
capabilities;
|
|
1602
|
-
|
|
1651
|
+
priceSubunits;
|
|
1652
|
+
asset;
|
|
1603
1653
|
image;
|
|
1604
1654
|
imageFile;
|
|
1605
1655
|
inner;
|
|
1606
|
-
constructor(name, description, capabilities,
|
|
1656
|
+
constructor(name, description, capabilities, priceSubunits, assetOrImage, imageOrImageFile, imageFileOrDir, dirOrPrompt, promptOrTools, toolsOrRounds, rounds) {
|
|
1657
|
+
let asset;
|
|
1658
|
+
let image;
|
|
1659
|
+
let imageFile;
|
|
1660
|
+
let skillDir;
|
|
1661
|
+
let systemPrompt;
|
|
1662
|
+
let tools;
|
|
1663
|
+
let maxToolRounds;
|
|
1664
|
+
if (assetOrImage !== void 0 && typeof assetOrImage === "object" && "token" in assetOrImage) {
|
|
1665
|
+
asset = assetOrImage;
|
|
1666
|
+
image = imageOrImageFile;
|
|
1667
|
+
imageFile = imageFileOrDir;
|
|
1668
|
+
skillDir = dirOrPrompt;
|
|
1669
|
+
systemPrompt = promptOrTools;
|
|
1670
|
+
tools = toolsOrRounds;
|
|
1671
|
+
maxToolRounds = rounds;
|
|
1672
|
+
} else {
|
|
1673
|
+
asset = NATIVE_SOL;
|
|
1674
|
+
image = assetOrImage;
|
|
1675
|
+
imageFile = imageOrImageFile;
|
|
1676
|
+
skillDir = imageFileOrDir;
|
|
1677
|
+
systemPrompt = dirOrPrompt;
|
|
1678
|
+
tools = promptOrTools;
|
|
1679
|
+
maxToolRounds = toolsOrRounds;
|
|
1680
|
+
}
|
|
1607
1681
|
this.name = name;
|
|
1608
1682
|
this.description = description;
|
|
1609
1683
|
this.capabilities = capabilities;
|
|
1610
|
-
this.
|
|
1684
|
+
this.priceSubunits = priceSubunits;
|
|
1685
|
+
this.asset = asset;
|
|
1611
1686
|
this.image = image;
|
|
1612
1687
|
this.imageFile = imageFile;
|
|
1613
1688
|
this.inner = new ScriptSkill$1({
|
|
1614
1689
|
name,
|
|
1615
1690
|
description,
|
|
1616
1691
|
capabilities,
|
|
1617
|
-
|
|
1692
|
+
priceSubunits: BigInt(Math.round(priceSubunits)),
|
|
1693
|
+
asset,
|
|
1618
1694
|
skillDir,
|
|
1619
1695
|
systemPrompt,
|
|
1620
1696
|
tools,
|
|
@@ -1658,7 +1734,8 @@ function loadSkillsFromDir(skillsDir) {
|
|
|
1658
1734
|
parsed.name,
|
|
1659
1735
|
parsed.description,
|
|
1660
1736
|
parsed.capabilities,
|
|
1661
|
-
Number(parsed.
|
|
1737
|
+
Number(parsed.priceSubunits),
|
|
1738
|
+
parsed.asset,
|
|
1662
1739
|
parsed.image,
|
|
1663
1740
|
parsed.imageFile,
|
|
1664
1741
|
entryPath,
|
|
@@ -1923,6 +2000,25 @@ async function cmdStart(nameArg, options = {}) {
|
|
|
1923
2000
|
);
|
|
1924
2001
|
process.exit(1);
|
|
1925
2002
|
}
|
|
2003
|
+
const llmProvider = loaded.yaml.llm.provider;
|
|
2004
|
+
const keyEnvVar = llmProvider === "anthropic" ? "ANTHROPIC_API_KEY" : "OPENAI_API_KEY";
|
|
2005
|
+
process.stdout.write(` Verifying ${llmProvider} API key... `);
|
|
2006
|
+
const verification = await verifyLlmApiKey(llmProvider, loaded.secrets.llm_api_key);
|
|
2007
|
+
if (verification.ok) {
|
|
2008
|
+
console.log("ok");
|
|
2009
|
+
} else if (verification.reason === "invalid") {
|
|
2010
|
+
console.log("INVALID");
|
|
2011
|
+
console.error(` ! ${llmProvider} rejected the API key (HTTP ${verification.status}).`);
|
|
2012
|
+
console.error(` Update it via \`elisym profile\` or set ${keyEnvVar} to a valid key.
|
|
2013
|
+
`);
|
|
2014
|
+
process.exit(1);
|
|
2015
|
+
} else {
|
|
2016
|
+
console.log("unavailable");
|
|
2017
|
+
console.warn(
|
|
2018
|
+
` ! Could not verify ${llmProvider} API key (${verification.error}). Continuing - jobs will fail if the key is invalid.
|
|
2019
|
+
`
|
|
2020
|
+
);
|
|
2021
|
+
}
|
|
1926
2022
|
const paths = agentPaths(loaded.dir);
|
|
1927
2023
|
const skillsDir = paths.skills;
|
|
1928
2024
|
const allSkills = loadSkillsFromDir(skillsDir);
|
|
@@ -1937,11 +2033,11 @@ async function cmdStart(nameArg, options = {}) {
|
|
|
1937
2033
|
const registry = new SkillRegistry();
|
|
1938
2034
|
for (const skill of allSkills) {
|
|
1939
2035
|
registry.register(skill);
|
|
1940
|
-
const price = skill.
|
|
2036
|
+
const price = skill.priceSubunits > 0 ? formatAssetAmount(skill.asset, BigInt(skill.priceSubunits)) : "free";
|
|
1941
2037
|
console.log(` * Skill: ${skill.name} [${skill.capabilities.join(", ")}] - ${price}`);
|
|
1942
2038
|
}
|
|
1943
2039
|
console.log();
|
|
1944
|
-
const hasPaid = allSkills.some((s) => s.
|
|
2040
|
+
const hasPaid = allSkills.some((s) => s.priceSubunits > 0);
|
|
1945
2041
|
if (hasPaid && !solanaAddress) {
|
|
1946
2042
|
console.error(" ! Paid skills require a Solana address. Run `elisym init` to configure.\n");
|
|
1947
2043
|
process.exit(1);
|
|
@@ -2041,7 +2137,11 @@ async function cmdStart(nameArg, options = {}) {
|
|
|
2041
2137
|
chain: "solana",
|
|
2042
2138
|
network: walletNetwork,
|
|
2043
2139
|
address: solanaAddress,
|
|
2044
|
-
job_price: skill.
|
|
2140
|
+
job_price: skill.priceSubunits,
|
|
2141
|
+
token: skill.asset.token,
|
|
2142
|
+
...skill.asset.mint ? { mint: skill.asset.mint } : {},
|
|
2143
|
+
decimals: skill.asset.decimals,
|
|
2144
|
+
symbol: skill.asset.symbol
|
|
2045
2145
|
} : void 0
|
|
2046
2146
|
};
|
|
2047
2147
|
}
|
|
@@ -2298,6 +2398,30 @@ async function loadAgentWithPrompt(name, cwd) {
|
|
|
2298
2398
|
}
|
|
2299
2399
|
throw new Error("Unreachable");
|
|
2300
2400
|
}
|
|
2401
|
+
async function fetchUsdcBalance(rpc, owner) {
|
|
2402
|
+
const mint = USDC_SOLANA_DEVNET.mint;
|
|
2403
|
+
if (!mint) {
|
|
2404
|
+
return 0n;
|
|
2405
|
+
}
|
|
2406
|
+
try {
|
|
2407
|
+
const response = await rpc.getTokenAccountsByOwner(
|
|
2408
|
+
owner,
|
|
2409
|
+
{ mint: address(mint) },
|
|
2410
|
+
{ encoding: "jsonParsed", commitment: "confirmed" }
|
|
2411
|
+
).send();
|
|
2412
|
+
let total = 0n;
|
|
2413
|
+
for (const entry of response.value) {
|
|
2414
|
+
const parsed = entry.account.data;
|
|
2415
|
+
const raw = parsed?.parsed?.info?.tokenAmount?.amount;
|
|
2416
|
+
if (typeof raw === "string") {
|
|
2417
|
+
total += BigInt(raw);
|
|
2418
|
+
}
|
|
2419
|
+
}
|
|
2420
|
+
return total;
|
|
2421
|
+
} catch {
|
|
2422
|
+
return 0n;
|
|
2423
|
+
}
|
|
2424
|
+
}
|
|
2301
2425
|
async function cmdWallet(name) {
|
|
2302
2426
|
const cwd = process.cwd();
|
|
2303
2427
|
if (!name) {
|
|
@@ -2331,11 +2455,13 @@ async function cmdWallet(name) {
|
|
|
2331
2455
|
const rpc = createSolanaRpc(rpcUrl);
|
|
2332
2456
|
const walletAddress = address(solPayment.address);
|
|
2333
2457
|
const { value: balance } = await rpc.getBalance(walletAddress).send();
|
|
2458
|
+
const usdcBalance = await fetchUsdcBalance(rpc, walletAddress);
|
|
2334
2459
|
console.log(`
|
|
2335
2460
|
Agent: ${name}`);
|
|
2336
2461
|
console.log(` Network: ${solPayment.network}`);
|
|
2337
2462
|
console.log(` Address: ${solPayment.address}`);
|
|
2338
|
-
console.log(`
|
|
2463
|
+
console.log(` SOL balance: ${formatSol(Number(balance))} (${balance} lamports)`);
|
|
2464
|
+
console.log(` USDC balance: ${formatAssetAmount(USDC_SOLANA_DEVNET, usdcBalance)}
|
|
2339
2465
|
`);
|
|
2340
2466
|
}
|
|
2341
2467
|
function readPackageVersion() {
|