@kashdao/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.
Files changed (85) hide show
  1. package/CHANGELOG.md +78 -0
  2. package/CONTRIBUTING.md +159 -0
  3. package/LICENSE +21 -0
  4. package/README.md +868 -0
  5. package/SECURITY.md +110 -0
  6. package/dist/account-AVFLEM5D.js +90 -0
  7. package/dist/account-AVFLEM5D.js.map +1 -0
  8. package/dist/auth-GUJCVKTD.js +239 -0
  9. package/dist/auth-GUJCVKTD.js.map +1 -0
  10. package/dist/chunk-BN2CUM42.js +9 -0
  11. package/dist/chunk-BN2CUM42.js.map +1 -0
  12. package/dist/chunk-BRK7KJ4O.js +154 -0
  13. package/dist/chunk-BRK7KJ4O.js.map +1 -0
  14. package/dist/chunk-KMBMQIZ7.js +393 -0
  15. package/dist/chunk-KMBMQIZ7.js.map +1 -0
  16. package/dist/chunk-LBIRQHX5.js +133 -0
  17. package/dist/chunk-LBIRQHX5.js.map +1 -0
  18. package/dist/chunk-MIXOZU2S.js +366 -0
  19. package/dist/chunk-MIXOZU2S.js.map +1 -0
  20. package/dist/chunk-QJMF73M5.js +129 -0
  21. package/dist/chunk-QJMF73M5.js.map +1 -0
  22. package/dist/chunk-UZNSYATZ.js +680 -0
  23. package/dist/chunk-UZNSYATZ.js.map +1 -0
  24. package/dist/chunk-VIADBYFY.js +94 -0
  25. package/dist/chunk-VIADBYFY.js.map +1 -0
  26. package/dist/chunk-YHCG2SUC.js +159 -0
  27. package/dist/chunk-YHCG2SUC.js.map +1 -0
  28. package/dist/chunk-YJX3JJ4M.js +174 -0
  29. package/dist/chunk-YJX3JJ4M.js.map +1 -0
  30. package/dist/client-IOM55ZCS.js +14 -0
  31. package/dist/client-IOM55ZCS.js.map +1 -0
  32. package/dist/completion-ZZGX5BQC.js +100 -0
  33. package/dist/completion-ZZGX5BQC.js.map +1 -0
  34. package/dist/config-XJL5TSYK.js +690 -0
  35. package/dist/config-XJL5TSYK.js.map +1 -0
  36. package/dist/config-store-3ZSYGXMQ.js +44 -0
  37. package/dist/config-store-3ZSYGXMQ.js.map +1 -0
  38. package/dist/docs-MDKKSJDC.js +102 -0
  39. package/dist/docs-MDKKSJDC.js.map +1 -0
  40. package/dist/eoa-IQ72EIHR.js +551 -0
  41. package/dist/eoa-IQ72EIHR.js.map +1 -0
  42. package/dist/errors-WMZIEGQI.js +18 -0
  43. package/dist/errors-WMZIEGQI.js.map +1 -0
  44. package/dist/explain-H4EH3KH3.js +165 -0
  45. package/dist/explain-H4EH3KH3.js.map +1 -0
  46. package/dist/global-options-XOLJUPTT.js +18 -0
  47. package/dist/global-options-XOLJUPTT.js.map +1 -0
  48. package/dist/health-VZEIII74.js +98 -0
  49. package/dist/health-VZEIII74.js.map +1 -0
  50. package/dist/help-footer-GTANVDNP.js +43 -0
  51. package/dist/help-footer-GTANVDNP.js.map +1 -0
  52. package/dist/index.d.ts +2 -0
  53. package/dist/index.js +251 -0
  54. package/dist/index.js.map +1 -0
  55. package/dist/intro-PQQTYWR7.js +41 -0
  56. package/dist/intro-PQQTYWR7.js.map +1 -0
  57. package/dist/markets-C37JZDPE.js +295 -0
  58. package/dist/markets-C37JZDPE.js.map +1 -0
  59. package/dist/output-VCBZ3FM7.js +22 -0
  60. package/dist/output-VCBZ3FM7.js.map +1 -0
  61. package/dist/portfolio-2AEPIJIG.js +115 -0
  62. package/dist/portfolio-2AEPIJIG.js.map +1 -0
  63. package/dist/protocol-XEQXF2GW.js +1572 -0
  64. package/dist/protocol-XEQXF2GW.js.map +1 -0
  65. package/dist/quote-I4DVJ7ZB.js +144 -0
  66. package/dist/quote-I4DVJ7ZB.js.map +1 -0
  67. package/dist/schema-PIFQ65TS.js +410 -0
  68. package/dist/schema-PIFQ65TS.js.map +1 -0
  69. package/dist/setup-USZ6IODD.js +260 -0
  70. package/dist/setup-USZ6IODD.js.map +1 -0
  71. package/dist/stdin-YW2CEQXU.js +28 -0
  72. package/dist/stdin-YW2CEQXU.js.map +1 -0
  73. package/dist/trace-IZBYTUFO.js +103 -0
  74. package/dist/trace-IZBYTUFO.js.map +1 -0
  75. package/dist/trade-IIZSEXEI.js +586 -0
  76. package/dist/trade-IIZSEXEI.js.map +1 -0
  77. package/dist/version-JDK3PEP5.js +117 -0
  78. package/dist/version-JDK3PEP5.js.map +1 -0
  79. package/dist/version-check-TDCION37.js +138 -0
  80. package/dist/version-check-TDCION37.js.map +1 -0
  81. package/dist/webhooks-PTVRKICZ.js +680 -0
  82. package/dist/webhooks-PTVRKICZ.js.map +1 -0
  83. package/dist/with-retry-4FIZG3A7.js +223 -0
  84. package/dist/with-retry-4FIZG3A7.js.map +1 -0
  85. package/package.json +99 -0
@@ -0,0 +1,690 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ createTable,
4
+ redact
5
+ } from "./chunk-BRK7KJ4O.js";
6
+ import {
7
+ cliConfigSchema,
8
+ cliFileSchema,
9
+ deleteConfig,
10
+ deleteProfile,
11
+ listProfiles,
12
+ readConfig,
13
+ readWholeFile,
14
+ resolveConfigPathsForOverride,
15
+ setCurrentProfile,
16
+ updateConfig,
17
+ writeWholeFile
18
+ } from "./chunk-KMBMQIZ7.js";
19
+ import "./chunk-YHCG2SUC.js";
20
+ import {
21
+ readGlobals
22
+ } from "./chunk-LBIRQHX5.js";
23
+ import {
24
+ log,
25
+ print,
26
+ printJson,
27
+ style
28
+ } from "./chunk-VIADBYFY.js";
29
+ import "./chunk-MIXOZU2S.js";
30
+ import {
31
+ CliError,
32
+ CliValidationError,
33
+ toCliError
34
+ } from "./chunk-UZNSYATZ.js";
35
+
36
+ // src/commands/config/index.ts
37
+ import { Command as Command9 } from "commander";
38
+
39
+ // src/commands/config/export.ts
40
+ import { writeFile } from "fs/promises";
41
+ import { Command } from "commander";
42
+ var REDACTED = "<redacted>";
43
+ var exportConfigCommand = new Command("export").description("Dump the entire multi-profile config (API keys redacted by default).").option("-o, --out <path>", "write to a file instead of stdout (mode 0600)").option("--include-secrets", "include raw API keys in the export (round-trippable)").addHelpText(
44
+ "after",
45
+ `
46
+ Examples:
47
+ $ kash config export # safe-by-default; redacted JSON to stdout
48
+ $ kash config export --out kash-config.json # write to a file (mode 0600)
49
+ $ kash config export --include-secrets --out kash.json # round-trippable (DO NOT commit)
50
+
51
+ Notes:
52
+ - Without --include-secrets, every \`apiKey\` is replaced with \`${REDACTED}\` \u2014
53
+ safe to commit and share for debugging without leaking credentials.
54
+ - The output is the canonical input for \`kash config import\`. Imports
55
+ refuse to apply a profile whose apiKey is still \`${REDACTED}\`.
56
+ - Use --out to route to a file the CLI can write at mode 0600. Stdout
57
+ inherits whatever umask the parent shell has.
58
+ `
59
+ ).action(async (options, cmd) => {
60
+ const globals = readGlobals(cmd);
61
+ try {
62
+ const file = await readWholeFile({
63
+ ...globals.configPath === void 0 ? {} : { configPath: globals.configPath }
64
+ });
65
+ const sanitized = {
66
+ ...file,
67
+ profiles: Object.fromEntries(
68
+ Object.entries(file.profiles).map(([name, profile]) => [
69
+ name,
70
+ sanitizeProfile(profile, options.includeSecrets ?? false)
71
+ ])
72
+ )
73
+ };
74
+ const payload = JSON.stringify(sanitized, null, 2);
75
+ if (options.out) {
76
+ try {
77
+ await writeFile(options.out, `${payload}
78
+ `, { encoding: "utf8", mode: 384 });
79
+ if (!globals.json) {
80
+ log.success(`Wrote ${options.out} (mode 0600).`);
81
+ if (!options.includeSecrets) {
82
+ log.info(
83
+ `Secrets redacted. Pass --include-secrets to produce a round-trippable export.`
84
+ );
85
+ }
86
+ }
87
+ } catch (cause) {
88
+ throw new CliError(`Failed to write ${options.out}.`, {
89
+ code: "CONFIGURATION",
90
+ recoverable: true,
91
+ suggestion: "Verify the path is writable and the parent directory exists.",
92
+ cause
93
+ });
94
+ }
95
+ return;
96
+ }
97
+ if (globals.json) {
98
+ printJson(sanitized);
99
+ } else {
100
+ if (!options.includeSecrets) {
101
+ log.info(`API keys redacted. Pass --include-secrets for round-trippable export.`);
102
+ }
103
+ print(payload);
104
+ }
105
+ } catch (cause) {
106
+ throw toCliError(cause);
107
+ }
108
+ });
109
+ function sanitizeProfile(profile, includeSecrets) {
110
+ if (includeSecrets) return profile;
111
+ if (profile.apiKey === void 0) return profile;
112
+ return { ...profile, apiKey: REDACTED };
113
+ }
114
+
115
+ // src/commands/config/import.ts
116
+ import { readFile } from "fs/promises";
117
+ import { Command as Command2 } from "commander";
118
+ var importConfigCommand = new Command2("import").description("Merge a `kash config export` bundle into the local config (mode 0600).").argument("[file]", "path to a JSON bundle (omit to read from stdin)").option("--no-overwrite", "skip profiles whose name already exists locally").option("--dry-run", "preview the merge without writing to disk").addHelpText(
119
+ "after",
120
+ `
121
+ Examples:
122
+ $ kash config import kash.json
123
+ $ kash config export --include-secrets | ssh prod-host kash config import
124
+ $ kash config import kash.json --no-overwrite # never replace existing profiles
125
+ $ kash config import kash.json --dry-run --json # preview the merged result
126
+
127
+ Notes:
128
+ - The input format is exactly what \`kash config export\` produces.
129
+ - Profiles whose apiKey is still \`${REDACTED}\` are refused \u2014 re-export
130
+ with --include-secrets to get a round-trippable bundle.
131
+ - --dry-run emits the merged file shape without persisting it. Useful
132
+ for diffing against the on-disk file before committing to a write.
133
+ `
134
+ ).action(async (file, options, cmd) => {
135
+ const globals = readGlobals(cmd);
136
+ try {
137
+ const raw = await readSource(file);
138
+ validateRedactionRaw(raw);
139
+ const parsed = parseAndValidate(raw);
140
+ const existing = await readWholeFile({
141
+ ...globals.configPath === void 0 ? {} : { configPath: globals.configPath }
142
+ });
143
+ const merged = mergeFiles(existing, parsed, options.overwrite === false);
144
+ if (options.dryRun === true) {
145
+ if (globals.json) {
146
+ printJson({ merged, written: false });
147
+ } else {
148
+ log.info("Dry run \u2014 no changes written.");
149
+ print(JSON.stringify(merged, null, 2));
150
+ }
151
+ return;
152
+ }
153
+ await writeWholeFile(merged, {
154
+ ...globals.configPath === void 0 ? {} : { configPath: globals.configPath }
155
+ });
156
+ if (globals.json) {
157
+ printJson({
158
+ merged,
159
+ written: true,
160
+ profiles: Object.keys(merged.profiles).sort(),
161
+ currentProfile: merged.currentProfile ?? null
162
+ });
163
+ return;
164
+ }
165
+ const importedNames = Object.keys(parsed.profiles);
166
+ const merged_n = Object.keys(merged.profiles).length;
167
+ log.success(
168
+ `Imported ${String(importedNames.length)} profile(s); local config now has ${String(merged_n)} profile(s).`
169
+ );
170
+ if (merged.currentProfile) {
171
+ log.info(`Active profile: ${style.bold(merged.currentProfile)}`);
172
+ }
173
+ } catch (cause) {
174
+ throw toCliError(cause);
175
+ }
176
+ });
177
+ async function readSource(file) {
178
+ if (file !== void 0) {
179
+ try {
180
+ return await readFile(file, "utf8");
181
+ } catch (cause) {
182
+ throw new CliError(`Failed to read ${file}.`, {
183
+ code: "CONFIGURATION",
184
+ recoverable: true,
185
+ suggestion: "Verify the path exists and is readable.",
186
+ cause
187
+ });
188
+ }
189
+ }
190
+ const chunks = [];
191
+ for await (const chunk of process.stdin) {
192
+ if (typeof chunk === "string") {
193
+ chunks.push(Buffer.from(chunk));
194
+ } else if (chunk instanceof Buffer) {
195
+ chunks.push(chunk);
196
+ } else {
197
+ chunks.push(Buffer.from(chunk));
198
+ }
199
+ }
200
+ if (chunks.length === 0) {
201
+ throw new CliError("No input received on stdin.", {
202
+ code: "INVALID_INPUT",
203
+ recoverable: true,
204
+ suggestion: "Pass a path argument or pipe a `kash config export` bundle into stdin."
205
+ });
206
+ }
207
+ const { stripBom } = await import("./stdin-YW2CEQXU.js");
208
+ return stripBom(Buffer.concat(chunks).toString("utf8"));
209
+ }
210
+ function parseAndValidate(raw) {
211
+ let parsed;
212
+ try {
213
+ parsed = JSON.parse(raw);
214
+ } catch (cause) {
215
+ throw new CliError("Input is not valid JSON.", {
216
+ code: "INVALID_INPUT",
217
+ recoverable: true,
218
+ suggestion: "The bundle must match the output of `kash config export`. Re-run that command and pipe the result.",
219
+ cause
220
+ });
221
+ }
222
+ const result = cliFileSchema.safeParse(parsed);
223
+ if (!result.success) {
224
+ const first = result.error.issues[0];
225
+ throw new CliError(
226
+ `Input does not match the config file shape${first ? `: ${first.path.join(".")}: ${first.message}` : "."}`,
227
+ {
228
+ code: "INVALID_INPUT",
229
+ recoverable: true,
230
+ suggestion: "Re-export from a known-good machine via `kash config export`."
231
+ }
232
+ );
233
+ }
234
+ return result.data;
235
+ }
236
+ function validateRedactionRaw(raw) {
237
+ if (raw.includes(`"apiKey": "${REDACTED}"`)) {
238
+ throw new CliError("Bundle contains redacted API keys \u2014 cannot import.", {
239
+ code: "INVALID_INPUT",
240
+ recoverable: true,
241
+ suggestion: "Re-run `kash config export --include-secrets` on the source machine to produce a round-trippable bundle."
242
+ });
243
+ }
244
+ }
245
+ function mergeFiles(existing, incoming, noOverwrite) {
246
+ const profiles = { ...existing.profiles };
247
+ for (const [name, profile] of Object.entries(incoming.profiles)) {
248
+ if (noOverwrite && name in profiles) continue;
249
+ profiles[name] = profile;
250
+ }
251
+ let currentProfile = existing.currentProfile;
252
+ if (incoming.currentProfile && incoming.currentProfile in profiles) {
253
+ currentProfile = incoming.currentProfile;
254
+ }
255
+ return {
256
+ version: 1,
257
+ ...currentProfile === void 0 ? {} : { currentProfile },
258
+ profiles
259
+ };
260
+ }
261
+
262
+ // src/commands/config/profiles.ts
263
+ import { Command as Command3 } from "commander";
264
+ var profilesCommand = new Command3("profiles").description("List configured profiles in the config file.").addHelpText(
265
+ "after",
266
+ `
267
+ Examples:
268
+ $ kash config profiles
269
+ $ kash config profiles --json --quiet | jq -r '.profiles[]'
270
+ `
271
+ ).action(async (_opts, cmd) => {
272
+ const globals = readGlobals(cmd);
273
+ let result;
274
+ try {
275
+ result = await listProfiles({
276
+ ...globals.configPath === void 0 ? {} : { configPath: globals.configPath }
277
+ });
278
+ } catch (cause) {
279
+ throw toCliError(cause);
280
+ }
281
+ if (globals.json) {
282
+ printJson(result);
283
+ return;
284
+ }
285
+ if (result.profiles.length === 0) {
286
+ log.info("No profiles configured.");
287
+ log.detail("Hint", "Run 'kash auth set-key <key>' to create the default profile.");
288
+ return;
289
+ }
290
+ const table = createTable(["Active", "Profile"]);
291
+ for (const name of result.profiles) {
292
+ table.push([name === result.current ? style.success("\u2022") : " ", name]);
293
+ }
294
+ print(table.toString());
295
+ });
296
+
297
+ // src/commands/config/remove.ts
298
+ import { Command as Command4 } from "commander";
299
+ var removeProfileCommand = new Command4("remove").description("Delete a named profile from the config file.").argument("<profile>", "profile name to remove").addHelpText(
300
+ "after",
301
+ `
302
+ Examples:
303
+ $ kash config remove staging
304
+ $ kash config remove old-profile --json --quiet
305
+ `
306
+ ).action(async (profile, _opts, cmd) => {
307
+ const globals = readGlobals(cmd);
308
+ let updated;
309
+ try {
310
+ updated = await deleteProfile(profile, {
311
+ ...globals.configPath === void 0 ? {} : { configPath: globals.configPath }
312
+ });
313
+ } catch (cause) {
314
+ throw toCliError(cause);
315
+ }
316
+ if (globals.json) {
317
+ printJson({
318
+ ok: true,
319
+ removed: profile,
320
+ profiles: Object.keys(updated.profiles).sort()
321
+ });
322
+ return;
323
+ }
324
+ log.success(`Profile "${profile}" removed.`);
325
+ });
326
+
327
+ // src/commands/config/reset.ts
328
+ import readline from "readline/promises";
329
+ import { Command as Command5 } from "commander";
330
+ var resetConfigCommand = new Command5("reset").description("Delete ~/.kash/config.json.").option("-y, --yes", "skip the interactive confirmation").addHelpText(
331
+ "after",
332
+ `
333
+ Examples:
334
+ $ kash config reset
335
+ $ kash config reset --yes --json --quiet
336
+ `
337
+ ).action(async (options, cmd) => {
338
+ const globals = readGlobals(cmd);
339
+ const paths = resolveConfigPathsForOverride(globals.configPath);
340
+ const interactive = process.stdin.isTTY && !globals.quiet && !globals.json;
341
+ const confirmed = options.yes || !interactive ? true : await confirm(paths.file);
342
+ if (!confirmed) {
343
+ log.info("Aborted.");
344
+ return;
345
+ }
346
+ try {
347
+ await deleteConfig({
348
+ ...globals.configPath === void 0 ? {} : { configPath: globals.configPath }
349
+ });
350
+ } catch (cause) {
351
+ throw toCliError(cause);
352
+ }
353
+ if (globals.json) {
354
+ printJson({ ok: true, deleted: paths.file });
355
+ return;
356
+ }
357
+ log.success(`Deleted ${paths.file}.`);
358
+ });
359
+ async function confirm(path) {
360
+ const rl = readline.createInterface({ input: process.stdin, output: process.stderr });
361
+ try {
362
+ const answer = await rl.question(`Delete ${path}? [y/N] `);
363
+ return answer.trim().toLowerCase().startsWith("y");
364
+ } finally {
365
+ rl.close();
366
+ }
367
+ }
368
+
369
+ // src/commands/config/set.ts
370
+ import { Command as Command6 } from "commander";
371
+ var TOP_LEVEL_KEYS = [
372
+ // Kash-orchestrated
373
+ "apiKey",
374
+ "baseUrl",
375
+ "defaultChainId",
376
+ // Direct mode
377
+ "rpcUrl",
378
+ "smartAccount",
379
+ "bundlerUrl",
380
+ "bundlerProvider",
381
+ "signerKeyRef"
382
+ ];
383
+ var CUSTOM_CHAIN_LEAVES = [
384
+ "name",
385
+ "factoryAddress",
386
+ "usdcAddress",
387
+ "oracleAddress",
388
+ "vaultAddress",
389
+ "tokens1155Address",
390
+ "paramRegistryAddress"
391
+ ];
392
+ var CUSTOM_CHAIN_SA_LEAVES = [
393
+ "factoryAddress",
394
+ "implementationAddress",
395
+ "entryPointAddress"
396
+ ];
397
+ var ALL_KEYS_FOR_HELP = [
398
+ ...TOP_LEVEL_KEYS,
399
+ ...CUSTOM_CHAIN_LEAVES.map((l) => `customChain.${l}`),
400
+ ...CUSTOM_CHAIN_SA_LEAVES.map((l) => `customChain.smartAccount.${l}`)
401
+ ];
402
+ var setConfigCommand = new Command6("set").description("Set a single config field.").argument(
403
+ "<key>",
404
+ "top-level key OR `customChain.<leaf>` / `customChain.smartAccount.<leaf>` dot-path"
405
+ ).argument("<value>", "value to set").addHelpText(
406
+ "after",
407
+ `
408
+ Examples:
409
+ $ kash config set baseUrl https://api-staging.kash.bot/v1 --profile staging
410
+ $ kash config set defaultChainId 84532 --profile staging
411
+
412
+ Direct-mode (kash protocol \u2026):
413
+ $ kash config set rpcUrl https://base-mainnet.g.alchemy.com/v2/... --profile mm
414
+ $ kash config set smartAccount 0xabc... --profile mm
415
+ $ kash config set signerKeyRef file:~/.kash/keys/mm-owner.key --profile mm
416
+ $ kash config set bundlerProvider flashbots --profile mm
417
+
418
+ Custom chain (local Anvil / forks / sidechains, bypasses static registry):
419
+ $ kash config set defaultChainId 31337 --profile anvil
420
+ $ kash config set rpcUrl http://localhost:8545 --profile anvil
421
+ $ kash config set customChain.name "Anvil local" --profile anvil
422
+ $ kash config set customChain.factoryAddress 0xCf7Ed... --profile anvil
423
+ $ kash config set customChain.usdcAddress 0x5FbDB... --profile anvil
424
+ $ kash config set customChain.smartAccount.factoryAddress 0x3Aa5e... --profile anvil
425
+ `
426
+ ).action(async (key, value, _opts, cmd) => {
427
+ const globals = readGlobals(cmd);
428
+ let patch;
429
+ try {
430
+ patch = await buildPatchForKey(key, value, {
431
+ ...globals.profile === void 0 ? {} : { profile: globals.profile },
432
+ ...globals.configPath === void 0 ? {} : { configPath: globals.configPath }
433
+ });
434
+ } catch (cause) {
435
+ throw toCliError(cause);
436
+ }
437
+ let result;
438
+ try {
439
+ result = await updateConfig(patch, {
440
+ ...globals.profile === void 0 ? {} : { profile: globals.profile },
441
+ ...globals.configPath === void 0 ? {} : { configPath: globals.configPath }
442
+ });
443
+ } catch (cause) {
444
+ throw toCliError(cause);
445
+ }
446
+ if (globals.json) {
447
+ const top = key.split(".", 1)[0];
448
+ printJson({
449
+ ok: true,
450
+ key,
451
+ value: result.stored[top] ?? null,
452
+ profile: result.profile
453
+ });
454
+ return;
455
+ }
456
+ log.success(`Set ${key} on profile "${result.profile}".`);
457
+ });
458
+ function isTopLevelKey(value) {
459
+ return TOP_LEVEL_KEYS.includes(value);
460
+ }
461
+ function isCustomChainLeaf(value) {
462
+ return CUSTOM_CHAIN_LEAVES.includes(value);
463
+ }
464
+ function isCustomChainSaLeaf(value) {
465
+ return CUSTOM_CHAIN_SA_LEAVES.includes(value);
466
+ }
467
+ async function buildPatchForKey(key, value, readOpts) {
468
+ if (isTopLevelKey(key)) {
469
+ return buildTopLevelPatch(key, value);
470
+ }
471
+ if (key.startsWith("customChain.")) {
472
+ const existing = await readConfig(readOpts);
473
+ const existingChain = existing.customChain ?? {};
474
+ const merged = mergeCustomChainLeaf(existingChain, key, value);
475
+ const candidate = { customChain: merged };
476
+ cliConfigSchema.parse(candidate);
477
+ return candidate;
478
+ }
479
+ throw new CliValidationError(
480
+ `Unknown config key "${key}".`,
481
+ `Allowed keys: ${ALL_KEYS_FOR_HELP.join(", ")}.`
482
+ );
483
+ }
484
+ function buildTopLevelPatch(key, value) {
485
+ switch (key) {
486
+ case "apiKey":
487
+ case "baseUrl":
488
+ case "rpcUrl":
489
+ case "smartAccount":
490
+ case "bundlerUrl":
491
+ case "bundlerProvider":
492
+ case "signerKeyRef": {
493
+ const candidate = { [key]: value };
494
+ cliConfigSchema.parse(candidate);
495
+ return candidate;
496
+ }
497
+ case "defaultChainId": {
498
+ const n = Number.parseInt(value, 10);
499
+ if (!Number.isFinite(n) || n <= 0) {
500
+ throw new CliValidationError(
501
+ "defaultChainId must be a positive integer.",
502
+ void 0,
503
+ "defaultChainId"
504
+ );
505
+ }
506
+ const candidate = { defaultChainId: n };
507
+ cliConfigSchema.parse(candidate);
508
+ return candidate;
509
+ }
510
+ }
511
+ }
512
+ function mergeCustomChainLeaf(existing, key, value) {
513
+ const path = key.slice("customChain.".length);
514
+ if (path.startsWith("smartAccount.")) {
515
+ const leaf = path.slice("smartAccount.".length);
516
+ if (!isCustomChainSaLeaf(leaf)) {
517
+ throw new CliValidationError(
518
+ `Unknown customChain.smartAccount leaf "${leaf}".`,
519
+ `Allowed: ${CUSTOM_CHAIN_SA_LEAVES.map((l) => `customChain.smartAccount.${l}`).join(", ")}.`
520
+ );
521
+ }
522
+ const sa = existing.smartAccount ?? {};
523
+ return {
524
+ ...existing,
525
+ smartAccount: { ...sa, [leaf]: value }
526
+ };
527
+ }
528
+ if (!isCustomChainLeaf(path)) {
529
+ throw new CliValidationError(
530
+ `Unknown customChain leaf "${path}".`,
531
+ `Allowed: ${CUSTOM_CHAIN_LEAVES.map((l) => `customChain.${l}`).join(", ")}, customChain.smartAccount.<leaf>.`
532
+ );
533
+ }
534
+ return {
535
+ ...existing,
536
+ [path]: value
537
+ };
538
+ }
539
+
540
+ // src/commands/config/show.ts
541
+ import { Command as Command7 } from "commander";
542
+ var showConfigCommand = new Command7("show").description("Print the resolved CLI configuration.").addHelpText(
543
+ "after",
544
+ `
545
+ Examples:
546
+ $ kash config show
547
+ $ kash config show --profile staging
548
+ $ kash config show --json --quiet | jq -r '.baseUrl'
549
+ `
550
+ ).action(async (_opts, cmd) => {
551
+ const globals = readGlobals(cmd);
552
+ const config = await readConfig({
553
+ ...globals.profile === void 0 ? {} : { profile: globals.profile },
554
+ ...globals.configPath === void 0 ? {} : { configPath: globals.configPath }
555
+ });
556
+ if (globals.json) {
557
+ printJson({
558
+ profile: config.profile,
559
+ authenticated: Boolean(config.apiKey),
560
+ apiKey: config.apiKey ? redact(config.apiKey) : null,
561
+ baseUrl: config.baseUrl,
562
+ defaultChainId: config.defaultChainId,
563
+ rpcUrl: config.rpcUrl ?? null,
564
+ smartAccount: config.smartAccount ?? null,
565
+ bundlerUrl: config.bundlerUrl ?? null,
566
+ bundlerProvider: config.bundlerProvider ?? null,
567
+ signerKeyRef: config.signerKeyRef ?? null,
568
+ customChain: config.customChain ?? null,
569
+ sources: config.sources
570
+ });
571
+ return;
572
+ }
573
+ print("");
574
+ print(` ${style.dim("Profile ")} ${config.profile} (${config.sources.profile})`);
575
+ print(
576
+ ` ${style.dim("API key ")} ${config.apiKey ? redact(config.apiKey) : "(unset)"} (${config.sources.apiKey})`
577
+ );
578
+ print(` ${style.dim("Base URL ")} ${config.baseUrl} (${config.sources.baseUrl})`);
579
+ print(
580
+ ` ${style.dim("Default chain ")} ${String(config.defaultChainId)} (${config.sources.defaultChainId})`
581
+ );
582
+ const hasDirectMode = config.rpcUrl !== void 0 || config.smartAccount !== void 0 || config.bundlerUrl !== void 0 || config.bundlerProvider !== void 0 || config.signerKeyRef !== void 0;
583
+ if (hasDirectMode) {
584
+ print("");
585
+ print(` ${style.dim("Direct mode ")}`);
586
+ print(
587
+ ` ${style.dim(" RPC URL ")} ${config.rpcUrl ?? "(unset)"} (${config.sources.rpcUrl})`
588
+ );
589
+ print(
590
+ ` ${style.dim(" Smart account")} ${config.smartAccount ?? "(unset)"} (${config.sources.smartAccount})`
591
+ );
592
+ print(
593
+ ` ${style.dim(" Bundler URL ")} ${config.bundlerUrl ?? "(unset)"} (${config.sources.bundlerUrl})`
594
+ );
595
+ print(
596
+ ` ${style.dim(" Bundler kind ")} ${config.bundlerProvider ?? "(unset)"} (${config.sources.bundlerProvider})`
597
+ );
598
+ print(
599
+ ` ${style.dim(" Signer ref ")} ${config.signerKeyRef ?? "(unset)"} (${config.sources.signerKeyRef})`
600
+ );
601
+ }
602
+ if (config.customChain !== void 0) {
603
+ const cc = config.customChain;
604
+ print("");
605
+ print(` ${style.dim("Custom chain ")} (${config.sources.customChain})`);
606
+ print(` ${style.dim(" Name ")} ${cc.name}`);
607
+ print(` ${style.dim(" Factory ")} ${cc.factoryAddress}`);
608
+ print(` ${style.dim(" USDC ")} ${cc.usdcAddress}`);
609
+ if (cc.oracleAddress) print(` ${style.dim(" Oracle ")} ${cc.oracleAddress}`);
610
+ if (cc.vaultAddress) print(` ${style.dim(" Vault ")} ${cc.vaultAddress}`);
611
+ if (cc.tokens1155Address) print(` ${style.dim(" Tokens1155 ")} ${cc.tokens1155Address}`);
612
+ if (cc.paramRegistryAddress)
613
+ print(` ${style.dim(" ParamRegistry")} ${cc.paramRegistryAddress}`);
614
+ if (cc.smartAccount) {
615
+ print(` ${style.dim(" Smart account")}`);
616
+ print(` ${style.dim(" Factory ")} ${cc.smartAccount.factoryAddress}`);
617
+ print(` ${style.dim(" Implement. ")} ${cc.smartAccount.implementationAddress}`);
618
+ print(` ${style.dim(" EntryPoint ")} ${cc.smartAccount.entryPointAddress}`);
619
+ }
620
+ }
621
+ });
622
+
623
+ // src/commands/config/use.ts
624
+ import { Command as Command8 } from "commander";
625
+ var useProfileCommand = new Command8("use").description("Switch the active profile (writes currentProfile to the config file).").argument("<profile>", "profile name").option(
626
+ "--allow-new",
627
+ "permit switching to a profile that does not yet exist (you must populate it via `kash auth set-key --profile <name>` afterwards)"
628
+ ).addHelpText(
629
+ "after",
630
+ `
631
+ Examples:
632
+ $ kash config use staging
633
+ $ kash config use prod --json --quiet
634
+ $ kash config use newprofile --allow-new # explicitly create a fresh profile
635
+
636
+ Notes:
637
+ - By default \`kash config use\` refuses unknown profile names and
638
+ lists the known ones \u2014 a typo of an existing profile name would
639
+ otherwise surface later as a confusing AUTH_REQUIRED.
640
+ - Use \`--allow-new\` for the bootstrapping flow when you genuinely
641
+ want to switch to a not-yet-populated profile.
642
+ `
643
+ ).action(async (profile, options, cmd) => {
644
+ const globals = readGlobals(cmd);
645
+ const configPathOpt = globals.configPath === void 0 ? {} : { configPath: globals.configPath };
646
+ let knownProfiles;
647
+ try {
648
+ const file = await readWholeFile(configPathOpt);
649
+ knownProfiles = Object.keys(file.profiles).sort();
650
+ } catch (cause) {
651
+ throw toCliError(cause);
652
+ }
653
+ if (!knownProfiles.includes(profile) && options.allowNew !== true) {
654
+ const known = knownProfiles.length === 0 ? "(none yet)" : knownProfiles.join(", ");
655
+ throw new CliValidationError(
656
+ `Profile "${profile}" does not exist.`,
657
+ `Known profiles: ${known}. Pass --allow-new to create + switch to a fresh profile (you'll then need to populate it with \`kash auth set-key --profile ${profile}\`).`,
658
+ "profile"
659
+ );
660
+ }
661
+ let updated;
662
+ try {
663
+ updated = await setCurrentProfile(profile, configPathOpt);
664
+ } catch (cause) {
665
+ throw toCliError(cause);
666
+ }
667
+ if (globals.json) {
668
+ printJson({
669
+ ok: true,
670
+ currentProfile: updated.currentProfile,
671
+ profiles: Object.keys(updated.profiles).sort(),
672
+ // Surface whether this was a new-profile switch — agents can
673
+ // pipe to `kash auth set-key --profile <p>` automatically when
674
+ // `created` is true.
675
+ created: !knownProfiles.includes(profile)
676
+ });
677
+ return;
678
+ }
679
+ log.success(`Active profile is now "${profile}".`);
680
+ if (!knownProfiles.includes(profile)) {
681
+ log.info(`Profile is empty. Populate it with: kash auth set-key --profile ${profile} <key>`);
682
+ }
683
+ });
684
+
685
+ // src/commands/config/index.ts
686
+ var configCommand = new Command9("config").description("Inspect and edit ~/.kash/config.json (multi-profile).").addCommand(showConfigCommand).addCommand(setConfigCommand).addCommand(profilesCommand).addCommand(useProfileCommand).addCommand(removeProfileCommand).addCommand(resetConfigCommand).addCommand(exportConfigCommand).addCommand(importConfigCommand);
687
+ export {
688
+ configCommand
689
+ };
690
+ //# sourceMappingURL=config-XJL5TSYK.js.map