@elisym/cli 0.5.2 → 0.6.1
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 +179 -69
- 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';
|
|
@@ -83,6 +83,11 @@ async function cmdInit(nameArg, options = {}) {
|
|
|
83
83
|
const target = pickTarget(options);
|
|
84
84
|
const sameLocation = target === "home" ? resolveInHome(agentName) : resolveInProject(agentName, cwd);
|
|
85
85
|
if (sameLocation) {
|
|
86
|
+
if (options.yes) {
|
|
87
|
+
throw new Error(
|
|
88
|
+
`Agent "${agentName}" already exists at ${sameLocation}. Refusing to overwrite secrets under --yes. Remove the directory first or choose a different name.`
|
|
89
|
+
);
|
|
90
|
+
}
|
|
86
91
|
const { overwrite } = await inquirer.prompt([
|
|
87
92
|
{
|
|
88
93
|
type: "confirm",
|
|
@@ -96,30 +101,34 @@ async function cmdInit(nameArg, options = {}) {
|
|
|
96
101
|
return;
|
|
97
102
|
}
|
|
98
103
|
} else if (target === "project" && resolveInHome(agentName)) {
|
|
99
|
-
|
|
100
|
-
{
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
104
|
+
if (!options.yes) {
|
|
105
|
+
const { shadow } = await inquirer.prompt([
|
|
106
|
+
{
|
|
107
|
+
type: "confirm",
|
|
108
|
+
name: "shadow",
|
|
109
|
+
message: `A global agent "${agentName}" exists in ~/.elisym/${agentName}/. Create a project-local shadow?`,
|
|
110
|
+
default: true
|
|
111
|
+
}
|
|
112
|
+
]);
|
|
113
|
+
if (!shadow) {
|
|
114
|
+
console.log("Aborted.");
|
|
115
|
+
return;
|
|
105
116
|
}
|
|
106
|
-
]);
|
|
107
|
-
if (!shadow) {
|
|
108
|
-
console.log("Aborted.");
|
|
109
|
-
return;
|
|
110
117
|
}
|
|
111
118
|
} else if (target === "home" && resolveInProject(agentName, cwd)) {
|
|
112
|
-
|
|
113
|
-
{
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
119
|
+
if (!options.yes) {
|
|
120
|
+
const { proceed } = await inquirer.prompt([
|
|
121
|
+
{
|
|
122
|
+
type: "confirm",
|
|
123
|
+
name: "proceed",
|
|
124
|
+
message: `A project-local agent "${agentName}" exists. Create a global agent with the same name?`,
|
|
125
|
+
default: true
|
|
126
|
+
}
|
|
127
|
+
]);
|
|
128
|
+
if (!proceed) {
|
|
129
|
+
console.log("Aborted.");
|
|
130
|
+
return;
|
|
118
131
|
}
|
|
119
|
-
]);
|
|
120
|
-
if (!proceed) {
|
|
121
|
-
console.log("Aborted.");
|
|
122
|
-
return;
|
|
123
132
|
}
|
|
124
133
|
}
|
|
125
134
|
let yaml;
|
|
@@ -153,24 +162,33 @@ async function cmdInit(nameArg, options = {}) {
|
|
|
153
162
|
llmApiKey = apiKey || void 0;
|
|
154
163
|
}
|
|
155
164
|
}
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
if (passphrase) {
|
|
165
|
-
const { confirmPassphrase } = await inquirer.prompt([
|
|
165
|
+
let passphrase = "";
|
|
166
|
+
const envPassphrase = process.env.ELISYM_PASSPHRASE;
|
|
167
|
+
if (options.passphrase !== void 0) {
|
|
168
|
+
passphrase = options.passphrase;
|
|
169
|
+
} else if (envPassphrase !== void 0) {
|
|
170
|
+
passphrase = envPassphrase;
|
|
171
|
+
} else {
|
|
172
|
+
const answer = await inquirer.prompt([
|
|
166
173
|
{
|
|
167
174
|
type: "password",
|
|
168
|
-
name: "
|
|
169
|
-
message: "
|
|
170
|
-
mask: "*"
|
|
171
|
-
validate: (value) => value === passphrase || "Passphrases do not match"
|
|
175
|
+
name: "passphrase",
|
|
176
|
+
message: "Passphrase to encrypt secrets (leave empty to skip):",
|
|
177
|
+
mask: "*"
|
|
172
178
|
}
|
|
173
179
|
]);
|
|
180
|
+
passphrase = answer.passphrase ?? "";
|
|
181
|
+
if (passphrase) {
|
|
182
|
+
const { confirmPassphrase } = await inquirer.prompt([
|
|
183
|
+
{
|
|
184
|
+
type: "password",
|
|
185
|
+
name: "confirmPassphrase",
|
|
186
|
+
message: "Confirm passphrase:",
|
|
187
|
+
mask: "*",
|
|
188
|
+
validate: (value) => value === passphrase || "Passphrases do not match"
|
|
189
|
+
}
|
|
190
|
+
]);
|
|
191
|
+
}
|
|
174
192
|
}
|
|
175
193
|
const nostrSecretBytes = generateSecretKey();
|
|
176
194
|
const nostrPubkey = getPublicKey(nostrSecretBytes);
|
|
@@ -268,6 +286,11 @@ async function promptYaml(inquirer) {
|
|
|
268
286
|
}
|
|
269
287
|
}
|
|
270
288
|
]);
|
|
289
|
+
if (solanaAddress) {
|
|
290
|
+
console.log(
|
|
291
|
+
" 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."
|
|
292
|
+
);
|
|
293
|
+
}
|
|
271
294
|
const { llmProvider } = await inquirer.prompt([
|
|
272
295
|
{
|
|
273
296
|
type: "list",
|
|
@@ -1114,7 +1137,11 @@ var LEDGER_RETENTION_MS = 30 * 24 * 60 * 60 * 1e3;
|
|
|
1114
1137
|
var TOTAL_JOB_TIMEOUT_MS = 5 * 60 * 1e3;
|
|
1115
1138
|
function resolveJobPrice(tags, skills) {
|
|
1116
1139
|
const skill = skills.route(tags);
|
|
1117
|
-
return skill?.
|
|
1140
|
+
return skill?.priceSubunits ?? 0;
|
|
1141
|
+
}
|
|
1142
|
+
function resolveJobAsset(tags, skills) {
|
|
1143
|
+
const skill = skills.route(tags);
|
|
1144
|
+
return skill?.asset ?? NATIVE_SOL;
|
|
1118
1145
|
}
|
|
1119
1146
|
var RATE_LIMIT_WINDOW_MS = 10 * 60 * 1e3;
|
|
1120
1147
|
var MAX_JOBS_PER_CUSTOMER = 20;
|
|
@@ -1224,16 +1251,18 @@ var AgentRuntime = class {
|
|
|
1224
1251
|
log("Agent runtime started. Listening for jobs...");
|
|
1225
1252
|
await new Promise((resolve3) => {
|
|
1226
1253
|
this.abortController.signal.addEventListener("abort", () => resolve3(), { once: true });
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1254
|
+
let shuttingDown = false;
|
|
1255
|
+
const onSignal = () => {
|
|
1256
|
+
if (shuttingDown) {
|
|
1257
|
+
return;
|
|
1258
|
+
}
|
|
1259
|
+
shuttingDown = true;
|
|
1233
1260
|
log("Shutting down...");
|
|
1234
1261
|
this.stop();
|
|
1235
1262
|
setTimeout(() => process.exit(0), 3e3).unref();
|
|
1236
|
-
}
|
|
1263
|
+
};
|
|
1264
|
+
process.on("SIGINT", onSignal);
|
|
1265
|
+
process.on("SIGTERM", onSignal);
|
|
1237
1266
|
});
|
|
1238
1267
|
}
|
|
1239
1268
|
/** Drop expired hits from both sliding-window limiters. */
|
|
@@ -1304,6 +1333,7 @@ var AgentRuntime = class {
|
|
|
1304
1333
|
throw new Error(`Input too long: ${job.input.length} chars (max ${LIMITS.MAX_INPUT_LENGTH})`);
|
|
1305
1334
|
}
|
|
1306
1335
|
const jobPrice = resolveJobPrice(job.tags, this.skills);
|
|
1336
|
+
const jobAsset = resolveJobAsset(job.tags, this.skills);
|
|
1307
1337
|
let netAmount;
|
|
1308
1338
|
let paymentRequest;
|
|
1309
1339
|
this.ledger.recordPaid({
|
|
@@ -1318,11 +1348,16 @@ var AgentRuntime = class {
|
|
|
1318
1348
|
created_at: Math.floor(Date.now() / 1e3)
|
|
1319
1349
|
});
|
|
1320
1350
|
if (jobPrice > 0) {
|
|
1321
|
-
const result = await this.collectPayment(job, jobPrice, signal);
|
|
1351
|
+
const result = await this.collectPayment(job, jobPrice, jobAsset, signal);
|
|
1322
1352
|
netAmount = result.netAmount;
|
|
1323
1353
|
paymentRequest = result.paymentRequest;
|
|
1324
1354
|
this.ledger.updatePayment(job.jobId, netAmount, paymentRequest);
|
|
1325
|
-
log(
|
|
1355
|
+
log(
|
|
1356
|
+
`[${job.jobId.slice(0, 8)}] Payment confirmed: ${formatAssetAmount(
|
|
1357
|
+
jobAsset,
|
|
1358
|
+
BigInt(netAmount)
|
|
1359
|
+
)}`
|
|
1360
|
+
);
|
|
1326
1361
|
this.callbacks.onPaymentReceived?.(job.jobId, netAmount);
|
|
1327
1362
|
}
|
|
1328
1363
|
await this.transport.sendFeedback(job, { type: "processing" }).catch(() => {
|
|
@@ -1352,7 +1387,7 @@ var AgentRuntime = class {
|
|
|
1352
1387
|
* Collect payment for a job. Creates payment request, sends PaymentRequired feedback,
|
|
1353
1388
|
* polls for on-chain confirmation. Aborts if signal fires.
|
|
1354
1389
|
*/
|
|
1355
|
-
async collectPayment(job, jobPrice, signal) {
|
|
1390
|
+
async collectPayment(job, jobPrice, jobAsset, signal) {
|
|
1356
1391
|
const log = this.callbacks.onLog ?? console.log;
|
|
1357
1392
|
if (!this.config.solanaAddress) {
|
|
1358
1393
|
throw new Error("Solana address not configured");
|
|
@@ -1362,12 +1397,14 @@ var AgentRuntime = class {
|
|
|
1362
1397
|
this.config.solanaAddress,
|
|
1363
1398
|
jobPrice,
|
|
1364
1399
|
protocolConfig,
|
|
1365
|
-
{ expirySecs: this.config.paymentTimeoutSecs }
|
|
1400
|
+
{ expirySecs: this.config.paymentTimeoutSecs, asset: jobAsset }
|
|
1366
1401
|
);
|
|
1367
1402
|
const requestJson = JSON.stringify(request);
|
|
1368
1403
|
const fee = calculateProtocolFee(jobPrice, protocolConfig.feeBps);
|
|
1369
1404
|
const netAmount = jobPrice - fee;
|
|
1370
|
-
log(
|
|
1405
|
+
log(
|
|
1406
|
+
`[${job.jobId.slice(0, 8)}] Payment required: ${formatAssetAmount(jobAsset, BigInt(jobPrice))} (fee: ${formatAssetAmount(jobAsset, BigInt(fee))})`
|
|
1407
|
+
);
|
|
1371
1408
|
this.ledger.updatePayment(job.jobId, void 0, requestJson);
|
|
1372
1409
|
await this.transport.sendFeedback(job, {
|
|
1373
1410
|
type: "payment-required",
|
|
@@ -1492,11 +1529,11 @@ var AgentRuntime = class {
|
|
|
1492
1529
|
this.ledger.markFailed(entry.job_id);
|
|
1493
1530
|
return;
|
|
1494
1531
|
}
|
|
1495
|
-
if (skill.
|
|
1532
|
+
if (skill.priceSubunits > 0 && !entry.net_amount) {
|
|
1496
1533
|
if (entry.payment_request) {
|
|
1497
1534
|
const verified = await this.reVerifyPayment(
|
|
1498
1535
|
entry,
|
|
1499
|
-
skill.
|
|
1536
|
+
skill.priceSubunits,
|
|
1500
1537
|
log,
|
|
1501
1538
|
recoveryAbort.signal
|
|
1502
1539
|
);
|
|
@@ -1537,7 +1574,7 @@ var AgentRuntime = class {
|
|
|
1537
1574
|
* job will be marked failed. For mainnet: use monitoring, avoid extended downtime, or
|
|
1538
1575
|
* configure an archive RPC via SOLANA_RPC_URL.
|
|
1539
1576
|
*/
|
|
1540
|
-
async reVerifyPayment(entry,
|
|
1577
|
+
async reVerifyPayment(entry, priceSubunits, log, signal) {
|
|
1541
1578
|
try {
|
|
1542
1579
|
const request = JSON.parse(entry.payment_request);
|
|
1543
1580
|
const rpc = createSolanaRpc(getRpcUrl(this.config.network));
|
|
@@ -1571,10 +1608,10 @@ var AgentRuntime = class {
|
|
|
1571
1608
|
result = await payment.verifyPayment(rpc, request, protocolConfig);
|
|
1572
1609
|
}
|
|
1573
1610
|
if (result.verified) {
|
|
1574
|
-
const fee = calculateProtocolFee(
|
|
1575
|
-
const netAmount =
|
|
1611
|
+
const fee = calculateProtocolFee(priceSubunits, protocolConfig.feeBps);
|
|
1612
|
+
const netAmount = priceSubunits - fee;
|
|
1576
1613
|
this.ledger.updatePayment(entry.job_id, netAmount, entry.payment_request);
|
|
1577
|
-
log(`[${entry.job_id.slice(0, 8)}] Recovery: payment re-verified (${netAmount}
|
|
1614
|
+
log(`[${entry.job_id.slice(0, 8)}] Recovery: payment re-verified (${netAmount} subunits)`);
|
|
1578
1615
|
return true;
|
|
1579
1616
|
}
|
|
1580
1617
|
log(`[${entry.job_id.slice(0, 8)}] Recovery: payment not found on-chain, marking failed`);
|
|
@@ -1597,13 +1634,22 @@ var SkillRegistry = class {
|
|
|
1597
1634
|
this.skills.push(skill);
|
|
1598
1635
|
}
|
|
1599
1636
|
route(tags) {
|
|
1600
|
-
for (const
|
|
1601
|
-
|
|
1602
|
-
|
|
1603
|
-
|
|
1604
|
-
|
|
1637
|
+
for (const tag of tags) {
|
|
1638
|
+
const byName = this.skills.find((skill) => skill.name === tag);
|
|
1639
|
+
if (byName) {
|
|
1640
|
+
return byName;
|
|
1641
|
+
}
|
|
1642
|
+
}
|
|
1643
|
+
for (const tag of tags) {
|
|
1644
|
+
const byCap = this.skills.find((skill) => skill.capabilities.includes(tag));
|
|
1645
|
+
if (byCap) {
|
|
1646
|
+
return byCap;
|
|
1605
1647
|
}
|
|
1606
1648
|
}
|
|
1649
|
+
const hasSpecificTag = tags.some((tag) => tag && tag !== "elisym");
|
|
1650
|
+
if (hasSpecificTag) {
|
|
1651
|
+
return null;
|
|
1652
|
+
}
|
|
1607
1653
|
return this.defaultIndex !== null ? this.skills[this.defaultIndex] : null;
|
|
1608
1654
|
}
|
|
1609
1655
|
allCapabilities() {
|
|
@@ -1620,22 +1666,49 @@ var ScriptSkill = class {
|
|
|
1620
1666
|
name;
|
|
1621
1667
|
description;
|
|
1622
1668
|
capabilities;
|
|
1623
|
-
|
|
1669
|
+
priceSubunits;
|
|
1670
|
+
asset;
|
|
1624
1671
|
image;
|
|
1625
1672
|
imageFile;
|
|
1626
1673
|
inner;
|
|
1627
|
-
constructor(name, description, capabilities,
|
|
1674
|
+
constructor(name, description, capabilities, priceSubunits, assetOrImage, imageOrImageFile, imageFileOrDir, dirOrPrompt, promptOrTools, toolsOrRounds, rounds) {
|
|
1675
|
+
let asset;
|
|
1676
|
+
let image;
|
|
1677
|
+
let imageFile;
|
|
1678
|
+
let skillDir;
|
|
1679
|
+
let systemPrompt;
|
|
1680
|
+
let tools;
|
|
1681
|
+
let maxToolRounds;
|
|
1682
|
+
if (assetOrImage !== void 0 && typeof assetOrImage === "object" && "token" in assetOrImage) {
|
|
1683
|
+
asset = assetOrImage;
|
|
1684
|
+
image = imageOrImageFile;
|
|
1685
|
+
imageFile = imageFileOrDir;
|
|
1686
|
+
skillDir = dirOrPrompt;
|
|
1687
|
+
systemPrompt = promptOrTools;
|
|
1688
|
+
tools = toolsOrRounds;
|
|
1689
|
+
maxToolRounds = rounds;
|
|
1690
|
+
} else {
|
|
1691
|
+
asset = NATIVE_SOL;
|
|
1692
|
+
image = assetOrImage;
|
|
1693
|
+
imageFile = imageOrImageFile;
|
|
1694
|
+
skillDir = imageFileOrDir;
|
|
1695
|
+
systemPrompt = dirOrPrompt;
|
|
1696
|
+
tools = promptOrTools;
|
|
1697
|
+
maxToolRounds = toolsOrRounds;
|
|
1698
|
+
}
|
|
1628
1699
|
this.name = name;
|
|
1629
1700
|
this.description = description;
|
|
1630
1701
|
this.capabilities = capabilities;
|
|
1631
|
-
this.
|
|
1702
|
+
this.priceSubunits = priceSubunits;
|
|
1703
|
+
this.asset = asset;
|
|
1632
1704
|
this.image = image;
|
|
1633
1705
|
this.imageFile = imageFile;
|
|
1634
1706
|
this.inner = new ScriptSkill$1({
|
|
1635
1707
|
name,
|
|
1636
1708
|
description,
|
|
1637
1709
|
capabilities,
|
|
1638
|
-
|
|
1710
|
+
priceSubunits: BigInt(Math.round(priceSubunits)),
|
|
1711
|
+
asset,
|
|
1639
1712
|
skillDir,
|
|
1640
1713
|
systemPrompt,
|
|
1641
1714
|
tools,
|
|
@@ -1679,7 +1752,8 @@ function loadSkillsFromDir(skillsDir) {
|
|
|
1679
1752
|
parsed.name,
|
|
1680
1753
|
parsed.description,
|
|
1681
1754
|
parsed.capabilities,
|
|
1682
|
-
Number(parsed.
|
|
1755
|
+
Number(parsed.priceSubunits),
|
|
1756
|
+
parsed.asset,
|
|
1683
1757
|
parsed.image,
|
|
1684
1758
|
parsed.imageFile,
|
|
1685
1759
|
entryPath,
|
|
@@ -1977,11 +2051,11 @@ async function cmdStart(nameArg, options = {}) {
|
|
|
1977
2051
|
const registry = new SkillRegistry();
|
|
1978
2052
|
for (const skill of allSkills) {
|
|
1979
2053
|
registry.register(skill);
|
|
1980
|
-
const price = skill.
|
|
2054
|
+
const price = skill.priceSubunits > 0 ? formatAssetAmount(skill.asset, BigInt(skill.priceSubunits)) : "free";
|
|
1981
2055
|
console.log(` * Skill: ${skill.name} [${skill.capabilities.join(", ")}] - ${price}`);
|
|
1982
2056
|
}
|
|
1983
2057
|
console.log();
|
|
1984
|
-
const hasPaid = allSkills.some((s) => s.
|
|
2058
|
+
const hasPaid = allSkills.some((s) => s.priceSubunits > 0);
|
|
1985
2059
|
if (hasPaid && !solanaAddress) {
|
|
1986
2060
|
console.error(" ! Paid skills require a Solana address. Run `elisym init` to configure.\n");
|
|
1987
2061
|
process.exit(1);
|
|
@@ -2081,7 +2155,11 @@ async function cmdStart(nameArg, options = {}) {
|
|
|
2081
2155
|
chain: "solana",
|
|
2082
2156
|
network: walletNetwork,
|
|
2083
2157
|
address: solanaAddress,
|
|
2084
|
-
job_price: skill.
|
|
2158
|
+
job_price: skill.priceSubunits,
|
|
2159
|
+
token: skill.asset.token,
|
|
2160
|
+
...skill.asset.mint ? { mint: skill.asset.mint } : {},
|
|
2161
|
+
decimals: skill.asset.decimals,
|
|
2162
|
+
symbol: skill.asset.symbol
|
|
2085
2163
|
} : void 0
|
|
2086
2164
|
};
|
|
2087
2165
|
}
|
|
@@ -2338,6 +2416,30 @@ async function loadAgentWithPrompt(name, cwd) {
|
|
|
2338
2416
|
}
|
|
2339
2417
|
throw new Error("Unreachable");
|
|
2340
2418
|
}
|
|
2419
|
+
async function fetchUsdcBalance(rpc, owner) {
|
|
2420
|
+
const mint = USDC_SOLANA_DEVNET.mint;
|
|
2421
|
+
if (!mint) {
|
|
2422
|
+
return 0n;
|
|
2423
|
+
}
|
|
2424
|
+
try {
|
|
2425
|
+
const response = await rpc.getTokenAccountsByOwner(
|
|
2426
|
+
owner,
|
|
2427
|
+
{ mint: address(mint) },
|
|
2428
|
+
{ encoding: "jsonParsed", commitment: "confirmed" }
|
|
2429
|
+
).send();
|
|
2430
|
+
let total = 0n;
|
|
2431
|
+
for (const entry of response.value) {
|
|
2432
|
+
const parsed = entry.account.data;
|
|
2433
|
+
const raw = parsed?.parsed?.info?.tokenAmount?.amount;
|
|
2434
|
+
if (typeof raw === "string") {
|
|
2435
|
+
total += BigInt(raw);
|
|
2436
|
+
}
|
|
2437
|
+
}
|
|
2438
|
+
return total;
|
|
2439
|
+
} catch {
|
|
2440
|
+
return 0n;
|
|
2441
|
+
}
|
|
2442
|
+
}
|
|
2341
2443
|
async function cmdWallet(name) {
|
|
2342
2444
|
const cwd = process.cwd();
|
|
2343
2445
|
if (!name) {
|
|
@@ -2371,11 +2473,13 @@ async function cmdWallet(name) {
|
|
|
2371
2473
|
const rpc = createSolanaRpc(rpcUrl);
|
|
2372
2474
|
const walletAddress = address(solPayment.address);
|
|
2373
2475
|
const { value: balance } = await rpc.getBalance(walletAddress).send();
|
|
2476
|
+
const usdcBalance = await fetchUsdcBalance(rpc, walletAddress);
|
|
2374
2477
|
console.log(`
|
|
2375
2478
|
Agent: ${name}`);
|
|
2376
2479
|
console.log(` Network: ${solPayment.network}`);
|
|
2377
2480
|
console.log(` Address: ${solPayment.address}`);
|
|
2378
|
-
console.log(`
|
|
2481
|
+
console.log(` SOL balance: ${formatSol(Number(balance))} (${balance} lamports)`);
|
|
2482
|
+
console.log(` USDC balance: ${formatAssetAmount(USDC_SOLANA_DEVNET, usdcBalance)}
|
|
2379
2483
|
`);
|
|
2380
2484
|
}
|
|
2381
2485
|
function readPackageVersion() {
|
|
@@ -2402,7 +2506,13 @@ function safe(fn) {
|
|
|
2402
2506
|
};
|
|
2403
2507
|
}
|
|
2404
2508
|
var program = new Command().name("elisym").description("CLI agent runner for the elisym network").version(PACKAGE_VERSION);
|
|
2405
|
-
program.command("init [name]").description("Create a new agent").option("-c, --config <path>", "Load fields from an elisym.yaml template (non-interactive)").option("--local", "Create in project <project>/.elisym/<name>/ (default: ~/.elisym/<name>/)").
|
|
2509
|
+
program.command("init [name]").description("Create a new agent").option("-c, --config <path>", "Load fields from an elisym.yaml template (non-interactive)").option("--local", "Create in project <project>/.elisym/<name>/ (default: ~/.elisym/<name>/)").option(
|
|
2510
|
+
"--passphrase <value>",
|
|
2511
|
+
'Passphrase to encrypt secrets at rest. Empty string ("") skips encryption. Also reads ELISYM_PASSPHRASE env var. When neither is provided, prompts interactively.'
|
|
2512
|
+
).option(
|
|
2513
|
+
"--yes",
|
|
2514
|
+
"Skip confirmation prompts (shadow/sibling-location). Fails closed on an existing agent at the same location - never overwrites secrets silently."
|
|
2515
|
+
).action(
|
|
2406
2516
|
safe(async (name, options) => {
|
|
2407
2517
|
await cmdInit(name, options);
|
|
2408
2518
|
})
|