@agentkv/cli 0.1.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/cli.js ADDED
@@ -0,0 +1,679 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ VERSION,
4
+ accountPath,
5
+ agentkvDir,
6
+ clientFromConfig,
7
+ createStoredAccount,
8
+ getOnrampProvider,
9
+ getOrCreateStoredWallet,
10
+ peekStoredAccount,
11
+ peekStoredWallet,
12
+ readConfigFile,
13
+ readEnvSecret,
14
+ readFileSecret,
15
+ resolveConfig,
16
+ writeSecretFile
17
+ } from "./chunk-34AG6FLC.js";
18
+
19
+ // src/cli.ts
20
+ import { mkdirSync, realpathSync, writeFileSync } from "fs";
21
+ import { join } from "path";
22
+ import { fileURLToPath } from "url";
23
+ import { AgentKVError, SpendCapError } from "@agentkv/client";
24
+
25
+ // src/args.ts
26
+ var UsageError = class extends Error {
27
+ constructor(message) {
28
+ super(message);
29
+ this.name = "UsageError";
30
+ }
31
+ };
32
+ var KNOWN_FLAGS = /* @__PURE__ */ new Set([
33
+ "endpoint",
34
+ "network",
35
+ "max-spend-usd",
36
+ "ttl-days",
37
+ "strict-ttl",
38
+ "pretty",
39
+ "json",
40
+ "reveal",
41
+ "out",
42
+ "file",
43
+ "from-key",
44
+ "from-env",
45
+ "cursor",
46
+ "limit",
47
+ "idempotency-key",
48
+ "onramp-app-id",
49
+ "onramp-provider"
50
+ ]);
51
+ function parseFlags(args) {
52
+ const flags = {};
53
+ const positionals = [];
54
+ for (let i = 0; i < args.length; i++) {
55
+ const a = args[i];
56
+ if (a.startsWith("--")) {
57
+ const key = a.slice(2);
58
+ if (!KNOWN_FLAGS.has(key)) {
59
+ throw new UsageError(`unknown flag --${key}`);
60
+ }
61
+ const boolish = ["strict-ttl", "pretty", "json", "reveal"].includes(key);
62
+ const val = boolish ? true : args[++i];
63
+ if (!boolish && (val === void 0 || val === "" || val.startsWith("--"))) {
64
+ throw new UsageError(`flag --${key} requires a value`);
65
+ }
66
+ if (key.endsWith("usd") || key === "ttl-days") {
67
+ const n = Number(val);
68
+ if (!Number.isFinite(n) || n < 0) {
69
+ throw new UsageError(
70
+ `flag --${key} must be a non-negative number (got ${JSON.stringify(val)})`
71
+ );
72
+ }
73
+ flags[camel(key)] = n;
74
+ } else {
75
+ flags[camel(key)] = val;
76
+ }
77
+ } else positionals.push(a);
78
+ }
79
+ return { flags, positionals };
80
+ }
81
+ var camel = (s) => s.replace(/-([a-z])/g, (_, c) => c.toUpperCase());
82
+
83
+ // src/commands/account.ts
84
+ import { AgentKV, isAccountKeyFormat } from "@agentkv/client";
85
+ import { privateKeyToAccount } from "viem/accounts";
86
+
87
+ // src/output.ts
88
+ var EXIT = { OK: 0, GENERIC: 1, USAGE: 2, PAYMENT: 3, NOT_FOUND: 4 };
89
+ function printJson(w, value) {
90
+ w(`${JSON.stringify(value, null, 2)}
91
+ `);
92
+ }
93
+ function printError(w, code, message, hint) {
94
+ w(`${JSON.stringify({ error: message, code, ...hint ? { hint } : {} })}
95
+ `);
96
+ }
97
+
98
+ // src/commands/account.ts
99
+ var PAYER_KEY_RE = /^0x[0-9a-fA-F]{64}$/;
100
+ function resolvePayerKey(flags, env) {
101
+ const explicit = (typeof flags.fromKey === "string" ? flags.fromKey.trim() : "") || env.AGENTKV_PAYER_KEY?.trim() || env.AGENTKV_PRIVATE_KEY?.trim() || "";
102
+ if (explicit) {
103
+ if (!PAYER_KEY_RE.test(explicit)) {
104
+ throw new Error(
105
+ "payer key must be a 0x-prefixed 32-byte hex private key (from --from-key / AGENTKV_PAYER_KEY / AGENTKV_PRIVATE_KEY)"
106
+ );
107
+ }
108
+ return explicit;
109
+ }
110
+ if (peekStoredWallet(env)) return getOrCreateStoredWallet(env).privateKey;
111
+ return void 0;
112
+ }
113
+ async function runAccount(args, io) {
114
+ const sub = args[0];
115
+ const env = io.env ?? process.env;
116
+ const { flags, positionals } = parseFlags(args);
117
+ const cfg = resolveConfig(flags, env, () => readConfigFile(env));
118
+ if (sub === "new") {
119
+ let acct;
120
+ try {
121
+ acct = createStoredAccount(env);
122
+ } catch (e) {
123
+ if (e?.code === "EEXIST") {
124
+ printError(
125
+ io.stderr,
126
+ "account_exists",
127
+ // accountPath (not peekStoredAccount) so a present-but-CORRUPT file — which now
128
+ // throws from peek — still yields a clean "already exists" message, not a crash.
129
+ `an account already exists at ${accountPath(env)}; delete it first to replace it`
130
+ );
131
+ return EXIT.GENERIC;
132
+ }
133
+ throw e;
134
+ }
135
+ printJson(io.stdout, {
136
+ accountKey: acct.accountKey,
137
+ encryptionKey: acct.encryptionKey,
138
+ path: acct.path,
139
+ note: `Back up these \u2014 they are UNRECOVERABLE (the server stores only a hash of the account key). Fund this account by depositing >=$1 to ${cfg.endpoint}/account/deposit from a wallet that can sign (e.g. awal). To buy USDC with a card for such a signing wallet, run \`agentkv fund\`.`
140
+ });
141
+ return EXIT.OK;
142
+ }
143
+ if (sub === "show") {
144
+ const reveal = args.includes("--reveal");
145
+ const envAccountKey = cfg.accountKey;
146
+ if (envAccountKey !== void 0 && !isAccountKeyFormat(envAccountKey)) {
147
+ printError(
148
+ io.stderr,
149
+ "invalid_config",
150
+ "AGENTKV_ACCOUNT_KEY must be of the form ak_<64 lowercase hex> (run 'agentkv account new' to mint one)"
151
+ );
152
+ return EXIT.GENERIC;
153
+ }
154
+ const envAccount = envAccountKey ? { accountKey: envAccountKey, encryptionKey: cfg.encryptionKey } : null;
155
+ const stored = envAccount ? null : peekStoredAccount(env);
156
+ if (!envAccount && !stored) {
157
+ printJson(io.stdout, {
158
+ configured: false,
159
+ note: "No account yet \u2014 run 'agentkv account new' to mint one (account-key mode is opt-in)."
160
+ });
161
+ return EXIT.OK;
162
+ }
163
+ if (reveal) {
164
+ if (envAccount) {
165
+ printJson(io.stdout, {
166
+ source: "AGENTKV_ACCOUNT_KEY env",
167
+ accountKey: envAccount.accountKey,
168
+ encryptionKey: envAccount.encryptionKey ?? null,
169
+ note: "These come from the environment (AGENTKV_ACCOUNT_KEY / AGENTKV_ENCRYPTION_KEY)."
170
+ });
171
+ return EXIT.OK;
172
+ }
173
+ printJson(io.stdout, {
174
+ source: "local account file",
175
+ path: stored.path,
176
+ accountKey: stored.accountKey,
177
+ encryptionKey: stored.encryptionKey,
178
+ note: "These are unrecoverable secrets \u2014 store them somewhere safe."
179
+ });
180
+ return EXIT.OK;
181
+ }
182
+ const acctKey = envAccount?.accountKey ?? stored?.accountKey;
183
+ const encKey = envAccount?.encryptionKey ?? stored?.encryptionKey;
184
+ let balance;
185
+ let balanceError;
186
+ try {
187
+ const balanceClient = io.client ?? (acctKey && encKey ? new AgentKV({
188
+ accountKey: acctKey,
189
+ encryptionKey: encKey,
190
+ endpoint: cfg.endpoint,
191
+ network: cfg.network
192
+ }) : void 0);
193
+ if (balanceClient) {
194
+ balance = await balanceClient.balance();
195
+ }
196
+ } catch (e) {
197
+ balanceError = e instanceof Error ? e.message : String(e);
198
+ }
199
+ if (envAccount) {
200
+ printJson(io.stdout, {
201
+ configured: true,
202
+ source: "AGENTKV_ACCOUNT_KEY env",
203
+ note: "Account configured from the environment (AGENTKV_ACCOUNT_KEY). Its account key + encryption key are unrecoverable \u2014 back them up. Use --reveal to print them.",
204
+ ...balance !== void 0 ? { balance } : {},
205
+ ...balanceError ? { balanceError } : {}
206
+ });
207
+ return EXIT.OK;
208
+ }
209
+ printJson(io.stdout, {
210
+ source: "local account file",
211
+ path: stored.path,
212
+ note: "Back up this file \u2014 its account key + encryption key are unrecoverable. Use --reveal to print them.",
213
+ ...balance !== void 0 ? { balance } : {},
214
+ ...balanceError ? { balanceError } : {}
215
+ });
216
+ return EXIT.OK;
217
+ }
218
+ if (sub === "fund") {
219
+ const usd = Number(positionals[1]);
220
+ if (!Number.isInteger(usd) || usd < 1) {
221
+ printError(
222
+ io.stderr,
223
+ "usage",
224
+ "account fund requires <usd> as a whole number of US dollars >= 1"
225
+ );
226
+ return EXIT.USAGE;
227
+ }
228
+ const envAccountKey = cfg.accountKey;
229
+ if (envAccountKey !== void 0 && !isAccountKeyFormat(envAccountKey)) {
230
+ printError(
231
+ io.stderr,
232
+ "invalid_config",
233
+ "AGENTKV_ACCOUNT_KEY must be of the form ak_<64 lowercase hex> (run 'agentkv account new' to mint one)"
234
+ );
235
+ return EXIT.GENERIC;
236
+ }
237
+ const envAccount = envAccountKey ? { accountKey: envAccountKey, encryptionKey: cfg.encryptionKey } : null;
238
+ const stored = envAccount ? null : peekStoredAccount(env);
239
+ const acctKey = envAccount?.accountKey ?? stored?.accountKey;
240
+ const encKey = envAccount?.encryptionKey ?? stored?.encryptionKey;
241
+ if (!acctKey) {
242
+ printError(
243
+ io.stderr,
244
+ "no_account",
245
+ "no account configured; run 'agentkv account new' or set AGENTKV_ACCOUNT_KEY (+ AGENTKV_ENCRYPTION_KEY)"
246
+ );
247
+ return EXIT.GENERIC;
248
+ }
249
+ let payerKey;
250
+ try {
251
+ payerKey = resolvePayerKey(flags, env);
252
+ } catch (e) {
253
+ printError(io.stderr, "invalid_payer", e instanceof Error ? e.message : String(e));
254
+ return EXIT.USAGE;
255
+ }
256
+ if (!payerKey) {
257
+ printError(
258
+ io.stderr,
259
+ "no_payer",
260
+ "no payer wallet; pass --from-key <0xhex> or set AGENTKV_PAYER_KEY (or AGENTKV_PRIVATE_KEY)"
261
+ );
262
+ return EXIT.GENERIC;
263
+ }
264
+ const payer = privateKeyToAccount(payerKey);
265
+ let client = io.client;
266
+ if (!io.client) {
267
+ if (!encKey) {
268
+ printError(
269
+ io.stderr,
270
+ "invalid_config",
271
+ envAccount ? "account-key mode (AGENTKV_ACCOUNT_KEY) requires AGENTKV_ENCRYPTION_KEY (the local 32-byte AES key)" : "account.json is missing or has a malformed encryptionKey (the local 32-byte AES key)"
272
+ );
273
+ return EXIT.GENERIC;
274
+ }
275
+ client = new AgentKV({
276
+ accountKey: acctKey,
277
+ encryptionKey: encKey,
278
+ endpoint: cfg.endpoint,
279
+ network: cfg.network
280
+ });
281
+ }
282
+ const result = await client.fundAccount(payer, usd);
283
+ printJson(io.stdout, result);
284
+ return EXIT.OK;
285
+ }
286
+ printError(
287
+ io.stderr,
288
+ "usage",
289
+ "account new | account show [--reveal] | account fund <usd> [--from-key <0xhex>]"
290
+ );
291
+ return EXIT.USAGE;
292
+ }
293
+
294
+ // src/commands/credits.ts
295
+ async function runCredits(cmd, args, io) {
296
+ if (cmd === "balance") {
297
+ printJson(io.stdout, { balance: await io.client.balance() });
298
+ return EXIT.OK;
299
+ }
300
+ const { positionals } = parseFlags(args);
301
+ const usd = Number(positionals[0]);
302
+ if (!Number.isFinite(usd) || usd < 1 || usd * 1e6 !== Math.round(usd * 1e6)) {
303
+ printError(
304
+ io.stderr,
305
+ "usage",
306
+ "deposit requires <usd> >= 1 (a whole number of atomic USDC units)"
307
+ );
308
+ return EXIT.USAGE;
309
+ }
310
+ printJson(io.stdout, await io.client.deposit(usd));
311
+ return EXIT.OK;
312
+ }
313
+
314
+ // src/commands/fund.ts
315
+ import { privateKeyToAccount as privateKeyToAccount2 } from "viem/accounts";
316
+ var KEY_RE = /^0x[0-9a-fA-F]{64}$/;
317
+ function runFund(args, io) {
318
+ const env = io.env ?? process.env;
319
+ const { flags, positionals } = parseFlags(args);
320
+ let amountUsd;
321
+ if (positionals.length > 0) {
322
+ const n = Number(positionals[0]);
323
+ if (!Number.isFinite(n) || n <= 0) {
324
+ printError(
325
+ io.stderr,
326
+ "usage",
327
+ `amount must be a positive number (got ${JSON.stringify(positionals[0])})`
328
+ );
329
+ return EXIT.USAGE;
330
+ }
331
+ amountUsd = n;
332
+ }
333
+ const cfg = resolveConfig(flags, env, () => readConfigFile(env));
334
+ const providerId = cfg.onrampProvider ?? "coinbase";
335
+ const onrampConfig = cfg.onrampConfig ?? {};
336
+ let address;
337
+ const envKey = env.AGENTKV_PRIVATE_KEY?.trim();
338
+ if (envKey) {
339
+ if (!KEY_RE.test(envKey)) {
340
+ printError(
341
+ io.stderr,
342
+ "invalid_config",
343
+ "AGENTKV_PRIVATE_KEY is set but malformed (expected 0x followed by 64 hex chars)"
344
+ );
345
+ return EXIT.GENERIC;
346
+ }
347
+ address = privateKeyToAccount2(envKey).address;
348
+ } else {
349
+ address = peekStoredWallet(env)?.address;
350
+ }
351
+ if (!address) {
352
+ const account = peekStoredAccount(env);
353
+ const inAccountMode = !!(env.AGENTKV_ACCOUNT_KEY?.trim() || account);
354
+ if (inAccountMode) {
355
+ printJson(io.stdout, {
356
+ provider: providerId,
357
+ url: null,
358
+ address: null,
359
+ note: `Account-key mode has no single wallet to onramp into. Account credits are funded by depositing USDC to ${cfg.endpoint}/account/deposit from a wallet that can sign (e.g. awal). An onramp can fund such a signing wallet: fund a wallet first (e.g. send USDC to it, or set AGENTKV_PRIVATE_KEY and re-run \`agentkv fund\`), then deposit to the account.`
360
+ });
361
+ return EXIT.OK;
362
+ }
363
+ printJson(io.stdout, {
364
+ provider: providerId,
365
+ url: null,
366
+ address: null,
367
+ note: "No wallet yet \u2014 one is auto-provisioned on the first paid op (e.g. `agentkv balance`), or set AGENTKV_PRIVATE_KEY to use your own. Provision/configure a wallet, then re-run `agentkv fund` for a funding URL."
368
+ });
369
+ return EXIT.OK;
370
+ }
371
+ let provider;
372
+ try {
373
+ provider = getOnrampProvider(providerId);
374
+ } catch (e) {
375
+ printError(io.stderr, "unknown_provider", e instanceof Error ? e.message : String(e));
376
+ return EXIT.USAGE;
377
+ }
378
+ let url;
379
+ try {
380
+ url = provider.buildUrl({ address, network: cfg.network, amountUsd, config: onrampConfig });
381
+ } catch (e) {
382
+ printError(io.stderr, "onramp_config", e instanceof Error ? e.message : String(e));
383
+ return EXIT.GENERIC;
384
+ }
385
+ printJson(io.stdout, {
386
+ provider: provider.id,
387
+ url,
388
+ address,
389
+ ...amountUsd !== void 0 ? { amountUsd } : {},
390
+ note: `Open this URL to buy USDC with a card and deliver it to ${address} on Base via ${provider.name}.`
391
+ });
392
+ return EXIT.OK;
393
+ }
394
+
395
+ // src/commands/kv.ts
396
+ import { readFileSync } from "fs";
397
+ async function runKv(cmd, args, io) {
398
+ const { flags, positionals } = parseFlags(args);
399
+ const key = positionals[0];
400
+ if (!key) {
401
+ printError(io.stderr, "usage", `${cmd} requires <key>`);
402
+ return EXIT.USAGE;
403
+ }
404
+ if (cmd === "delete") {
405
+ const bad = ["out", "fromEnv", "file"].find((f) => f in flags);
406
+ if (bad) {
407
+ const flag = bad === "fromEnv" ? "from-env" : bad;
408
+ printError(io.stderr, "usage", `--${flag} is not valid for delete`);
409
+ return EXIT.USAGE;
410
+ }
411
+ printJson(io.stdout, await io.client.delete(key));
412
+ return EXIT.OK;
413
+ }
414
+ if (cmd === "get") {
415
+ const v = await io.client.get(
416
+ key,
417
+ flags.idempotencyKey ? { idempotencyKey: flags.idempotencyKey } : {}
418
+ );
419
+ if (v === null) {
420
+ printJson(io.stdout, flags.out ? { found: false } : null);
421
+ return EXIT.NOT_FOUND;
422
+ }
423
+ if (flags.out) {
424
+ const text = typeof v === "string" ? v : JSON.stringify(v);
425
+ let written;
426
+ try {
427
+ written = writeSecretFile(text, flags.out);
428
+ } catch {
429
+ printError(
430
+ io.stderr,
431
+ "write_failed",
432
+ "could not write --out file (choose a fresh path that does not already exist)"
433
+ );
434
+ return EXIT.USAGE;
435
+ }
436
+ printJson(io.stdout, { found: true, path: written.path, bytes: written.bytes });
437
+ return EXIT.OK;
438
+ }
439
+ printJson(io.stdout, v);
440
+ return EXIT.OK;
441
+ }
442
+ let value;
443
+ if (flags.fromEnv) {
444
+ const r = readEnvSecret(flags.fromEnv);
445
+ if (!r.ok) {
446
+ printError(io.stderr, r.code, r.error);
447
+ return EXIT.USAGE;
448
+ }
449
+ value = r.value;
450
+ } else {
451
+ let raw;
452
+ if (positionals[1] !== void 0) {
453
+ raw = positionals[1];
454
+ } else if (flags.file) {
455
+ const r = readFileSecret(flags.file, { trim: false });
456
+ if (!r.ok) {
457
+ printError(io.stderr, r.code, r.error);
458
+ return EXIT.USAGE;
459
+ }
460
+ raw = r.value;
461
+ } else {
462
+ raw = readFileSync(0, "utf8");
463
+ }
464
+ try {
465
+ value = JSON.parse(raw);
466
+ } catch {
467
+ printError(
468
+ io.stderr,
469
+ "invalid_value",
470
+ "value must be valid JSON",
471
+ `examples: '"a string"' 42 '{"k":"v"}'`
472
+ );
473
+ return EXIT.USAGE;
474
+ }
475
+ }
476
+ const opts = {};
477
+ if (flags.ttlDays !== void 0) opts.ttlDays = flags.ttlDays;
478
+ if (flags.strictTtl) opts.strictTtl = true;
479
+ if (flags.idempotencyKey) opts.idempotencyKey = flags.idempotencyKey;
480
+ printJson(io.stdout, await io.client.set(key, value, opts));
481
+ return EXIT.OK;
482
+ }
483
+ async function runListKeys(args, io) {
484
+ const { flags } = parseFlags(args);
485
+ const limit = flags.limit !== void 0 ? Number(flags.limit) : void 0;
486
+ const onePage = flags.cursor !== void 0;
487
+ const keys = [];
488
+ let cursor = flags.cursor ?? null;
489
+ do {
490
+ const res = await io.client.listKeys(cursor ? { cursor, limit } : { limit });
491
+ keys.push(...res.keys);
492
+ cursor = res.cursor;
493
+ } while (cursor && !onePage);
494
+ printJson(io.stdout, { keys: keys.sort(), count: keys.length, cursor: onePage ? cursor : null });
495
+ return EXIT.OK;
496
+ }
497
+
498
+ // src/commands/wallet.ts
499
+ import { generatePrivateKey, privateKeyToAccount as privateKeyToAccount3 } from "viem/accounts";
500
+ var KEY_RE2 = /^0x[0-9a-fA-F]{64}$/;
501
+ function runWallet(args, io) {
502
+ const sub = args[0];
503
+ if (sub === "new") {
504
+ const privateKey = generatePrivateKey();
505
+ printJson(io.stdout, {
506
+ address: privateKeyToAccount3(privateKey).address,
507
+ privateKey,
508
+ note: "Optional: set AGENTKV_PRIVATE_KEY to this to manage your own key. Otherwise AgentKV mints + manages a local wallet on first use."
509
+ });
510
+ return EXIT.OK;
511
+ }
512
+ if (sub === "show") {
513
+ const env = io.env ?? process.env;
514
+ const envKey = env.AGENTKV_PRIVATE_KEY?.trim();
515
+ if (envKey) {
516
+ if (!KEY_RE2.test(envKey)) {
517
+ printError(
518
+ io.stderr,
519
+ "invalid_config",
520
+ "AGENTKV_PRIVATE_KEY is set but malformed (expected 0x followed by 64 hex chars)"
521
+ );
522
+ return EXIT.GENERIC;
523
+ }
524
+ printJson(io.stdout, {
525
+ address: privateKeyToAccount3(envKey).address,
526
+ source: "env (AGENTKV_PRIVATE_KEY)"
527
+ });
528
+ return EXIT.OK;
529
+ }
530
+ const stored = peekStoredWallet(env);
531
+ if (stored) {
532
+ printJson(io.stdout, {
533
+ address: stored.address,
534
+ source: "local keystore",
535
+ path: stored.path,
536
+ note: "Back up this file \u2014 it is your namespace and holds your funds."
537
+ });
538
+ return EXIT.OK;
539
+ }
540
+ printJson(io.stdout, {
541
+ address: null,
542
+ note: "No wallet yet \u2014 one is created automatically on first use (set / get / deposit / \u2026)."
543
+ });
544
+ return EXIT.OK;
545
+ }
546
+ printError(io.stderr, "usage", "wallet new | wallet show");
547
+ return EXIT.USAGE;
548
+ }
549
+
550
+ // src/cli.ts
551
+ var HELP = `agentkv \u2014 encrypted, x402-paid key-value store (v${VERSION})
552
+
553
+ Usage: agentkv <command> [options]
554
+
555
+ Commands:
556
+ set <key> <json> Encrypt and store a value
557
+ get <key> Fetch and decrypt a value
558
+ delete <key> Delete a value
559
+ list-keys List stored key names
560
+ balance Show prepaid credit balance
561
+ deposit <usd> Buy credits (>= $1) with USDC via x402
562
+ wallet new|show Manage the local signing wallet
563
+ account new|show|fund Manage an account-key (ak_) namespace
564
+ fund Print a card->USDC onramp URL
565
+ config Read/write local defaults
566
+ mcp Run the MCP server (stdio)
567
+
568
+ Global options:
569
+ --endpoint <url> API endpoint (default https://api.agentx402.ai)
570
+ --network <caip2> eip155:8453 (default) or eip155:84532
571
+ --max-spend-usd <n> Per-operation spend cap (USD)
572
+ --json | --pretty Output format
573
+ -h, --help Show this help
574
+ -V, --version Show the CLI version
575
+
576
+ Docs: https://github.com/agentx402-ai/agentkv
577
+ `;
578
+ async function runCli(argv, deps = {}) {
579
+ const stdout = deps.stdout ?? ((s) => process.stdout.write(s));
580
+ const stderr = deps.stderr ?? ((s) => process.stderr.write(s));
581
+ const [cmd, ...rest] = argv;
582
+ if (cmd === void 0 || cmd === "-h" || cmd === "--help" || cmd === "help") {
583
+ stdout(HELP);
584
+ return EXIT.OK;
585
+ }
586
+ if (cmd === "-V" || cmd === "--version" || cmd === "version") {
587
+ stdout(`${VERSION}
588
+ `);
589
+ return EXIT.OK;
590
+ }
591
+ try {
592
+ const env = deps.env ?? process.env;
593
+ if (cmd === "wallet") return runWallet(rest, { stdout, stderr, env });
594
+ if (cmd === "fund") return runFund(rest, { stdout, stderr, env });
595
+ if (cmd === "config") return runConfig(rest, { stdout, stderr, env });
596
+ if (cmd === "account") {
597
+ return await runAccount(rest, { client: deps.client, stdout, stderr, env });
598
+ }
599
+ if (cmd === "mcp") {
600
+ const { startMcp } = await import("./mcp-MINXA6WX.js");
601
+ await startMcp({ env: deps.env, client: deps.client });
602
+ return EXIT.OK;
603
+ }
604
+ const KV_COMMANDS = /* @__PURE__ */ new Set(["set", "get", "delete", "list-keys", "balance", "deposit"]);
605
+ if (!KV_COMMANDS.has(cmd)) {
606
+ printError(
607
+ stderr,
608
+ "usage",
609
+ `unknown command: ${cmd ?? "(none)"}`,
610
+ "commands: set get delete list-keys deposit balance wallet account fund config mcp (run `agentkv --help`)"
611
+ );
612
+ return EXIT.USAGE;
613
+ }
614
+ const client = deps.client ?? clientFromConfig(
615
+ resolveConfig(parseFlags(rest).flags, env, () => readConfigFile(env)),
616
+ {
617
+ env,
618
+ // Auto-provision notice -> stderr, so it never pollutes JSON stdout.
619
+ notify: (m) => stderr(`agentkv: ${m}
620
+ `)
621
+ }
622
+ );
623
+ if (cmd === "set" || cmd === "get" || cmd === "delete")
624
+ return await runKv(cmd, rest, { client, stdout, stderr });
625
+ if (cmd === "list-keys") return await runListKeys(rest, { client, stdout, stderr });
626
+ return await runCredits(cmd, rest, { client, stdout, stderr });
627
+ } catch (e) {
628
+ return mapError(e, stderr);
629
+ }
630
+ }
631
+ function mapError(e, stderr) {
632
+ if (e instanceof SpendCapError) {
633
+ printError(stderr, e.code, e.message);
634
+ return EXIT.PAYMENT;
635
+ }
636
+ if (e instanceof AgentKVError) {
637
+ printError(stderr, e.code, e.message);
638
+ if (e.status === 404) return EXIT.NOT_FOUND;
639
+ if (e.status === 402) return EXIT.PAYMENT;
640
+ return EXIT.GENERIC;
641
+ }
642
+ if (e instanceof UsageError) {
643
+ printError(stderr, "usage", e.message);
644
+ return EXIT.USAGE;
645
+ }
646
+ printError(stderr, "error", e instanceof Error ? e.message : String(e));
647
+ return EXIT.GENERIC;
648
+ }
649
+ function runConfig(args, io) {
650
+ const env = io.env ?? process.env;
651
+ const { flags } = parseFlags(args);
652
+ const dir = agentkvDir(env);
653
+ mkdirSync(dir, { recursive: true });
654
+ const existing = readConfigFile(env) ?? {};
655
+ const merged = { ...existing };
656
+ if (flags.endpoint) merged.endpoint = flags.endpoint;
657
+ if (flags.network) merged.network = flags.network;
658
+ if (flags.maxSpendUsd !== void 0) merged.maxSpendUsd = flags.maxSpendUsd;
659
+ const path = join(dir, "config.json");
660
+ writeFileSync(path, JSON.stringify(merged, null, 2));
661
+ printJson(io.stdout, { ok: true, path, ...merged });
662
+ return EXIT.OK;
663
+ }
664
+ function isMainModule() {
665
+ const argv1 = process.argv[1];
666
+ if (!argv1) return false;
667
+ try {
668
+ return realpathSync(argv1) === fileURLToPath(import.meta.url);
669
+ } catch {
670
+ return false;
671
+ }
672
+ }
673
+ if (isMainModule()) {
674
+ runCli(process.argv.slice(2)).then((code) => process.exit(code));
675
+ }
676
+ export {
677
+ parseFlags,
678
+ runCli
679
+ };