@keeperhub/wallet 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,616 @@
1
+ // src/cli.ts
2
+ import { Command } from "commander";
3
+
4
+ // src/balance.ts
5
+ import {
6
+ createPublicClient,
7
+ erc20Abi,
8
+ formatUnits,
9
+ http
10
+ } from "viem";
11
+
12
+ // src/chains.ts
13
+ import { defineChain } from "viem";
14
+ import { base } from "viem/chains";
15
+ var tempo = defineChain({
16
+ id: 4217,
17
+ name: "Tempo",
18
+ nativeCurrency: { decimals: 18, name: "Ether", symbol: "ETH" },
19
+ rpcUrls: {
20
+ default: {
21
+ http: [process.env.TEMPO_RPC_URL ?? "https://rpc.tempo.xyz"]
22
+ }
23
+ },
24
+ blockExplorers: {
25
+ default: { name: "Tempo Explorer", url: "https://explorer.tempo.xyz" }
26
+ }
27
+ });
28
+ var BASE_USDC = "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913";
29
+ var TEMPO_USDC_E = "0x20c000000000000000000000b9537d11c60e8b50";
30
+
31
+ // src/hmac.ts
32
+ import { createHash, createHmac } from "crypto";
33
+ function computeSignature(secret, method, path, subOrgId, body, timestamp) {
34
+ const bodyDigest = createHash("sha256").update(body).digest("hex");
35
+ const signingString = `${method}
36
+ ${path}
37
+ ${subOrgId}
38
+ ${bodyDigest}
39
+ ${timestamp}`;
40
+ return createHmac("sha256", secret).update(signingString).digest("hex");
41
+ }
42
+ function buildHmacHeaders(secret, method, path, subOrgId, body) {
43
+ const timestamp = String(Math.floor(Date.now() / 1e3));
44
+ const signature = computeSignature(
45
+ secret,
46
+ method,
47
+ path,
48
+ subOrgId,
49
+ body,
50
+ timestamp
51
+ );
52
+ return {
53
+ "X-KH-Sub-Org": subOrgId,
54
+ "X-KH-Timestamp": timestamp,
55
+ "X-KH-Signature": signature
56
+ };
57
+ }
58
+
59
+ // src/types.ts
60
+ var KeeperHubError = class extends Error {
61
+ code;
62
+ constructor(code, message) {
63
+ super(message);
64
+ this.name = "KeeperHubError";
65
+ this.code = code;
66
+ }
67
+ };
68
+ var WalletConfigMissingError = class extends Error {
69
+ constructor() {
70
+ super(
71
+ "Wallet config not found at ~/.keeperhub/wallet.json. Run `npx @keeperhub/wallet add` to provision."
72
+ );
73
+ this.name = "WalletConfigMissingError";
74
+ }
75
+ };
76
+
77
+ // src/client.ts
78
+ var TRAILING_SLASH = /\/$/;
79
+ function defaultCodeForStatus(status) {
80
+ if (status === 401) {
81
+ return "HMAC_INVALID";
82
+ }
83
+ if (status === 403) {
84
+ return "POLICY_BLOCKED";
85
+ }
86
+ if (status === 404) {
87
+ return "NOT_FOUND";
88
+ }
89
+ if (status === 502) {
90
+ return "TURNKEY_UPSTREAM";
91
+ }
92
+ return `HTTP_${status}`;
93
+ }
94
+ var KeeperHubClient = class {
95
+ baseUrl;
96
+ fetchImpl;
97
+ wallet;
98
+ constructor(wallet, opts = {}) {
99
+ this.wallet = wallet;
100
+ const envBase = process.env.KEEPERHUB_API_URL;
101
+ this.baseUrl = (opts.baseUrl ?? envBase ?? "https://app.keeperhub.com").replace(TRAILING_SLASH, "");
102
+ this.fetchImpl = opts.fetch ?? globalThis.fetch;
103
+ }
104
+ /**
105
+ * HMAC-signed POST/GET to any /api/agentic-wallet/* route except
106
+ * /provision. Path MUST start with a leading slash. Body is
107
+ * JSON.stringify'd (or the empty string for GET).
108
+ *
109
+ * Error mapping: non-2xx/non-202 surface as `KeeperHubError(code,
110
+ * message)` where `code` is the server-supplied field or the default
111
+ * taxonomy (`HMAC_INVALID`, `POLICY_BLOCKED`, `NOT_FOUND`,
112
+ * `TURNKEY_UPSTREAM`, `HTTP_<status>`). 202 ask-tier surfaces as an
113
+ * AskTierResponse envelope.
114
+ */
115
+ async request(method, path, body) {
116
+ const bodyStr = body === void 0 ? "" : JSON.stringify(body);
117
+ const hmacHeaders = buildHmacHeaders(
118
+ this.wallet.hmacSecret,
119
+ method,
120
+ path,
121
+ this.wallet.subOrgId,
122
+ bodyStr
123
+ );
124
+ const headers = method === "POST" ? { ...hmacHeaders, "content-type": "application/json" } : { ...hmacHeaders };
125
+ const response = await this.fetchImpl(`${this.baseUrl}${path}`, {
126
+ method,
127
+ headers,
128
+ body: method === "POST" ? bodyStr : void 0
129
+ });
130
+ if (response.status === 202) {
131
+ const data = await response.json();
132
+ return { _status: 202, approvalRequestId: data.approvalRequestId };
133
+ }
134
+ if (!response.ok) {
135
+ let code = "UNKNOWN";
136
+ let message = `HTTP ${response.status}`;
137
+ try {
138
+ const data = await response.json();
139
+ code = data.code ?? defaultCodeForStatus(response.status);
140
+ message = data.error ?? message;
141
+ } catch {
142
+ }
143
+ throw new KeeperHubError(code, message);
144
+ }
145
+ return await response.json();
146
+ }
147
+ };
148
+
149
+ // src/balance.ts
150
+ var USDC_DECIMALS = 6;
151
+ async function checkBalance(wallet, opts = {}) {
152
+ const baseClient = opts.baseClient ?? createPublicClient({
153
+ chain: base,
154
+ transport: http()
155
+ });
156
+ const tempoClient = opts.tempoClient ?? createPublicClient({
157
+ chain: tempo,
158
+ transport: http()
159
+ });
160
+ const khClient = opts.khClient ?? new KeeperHubClient(wallet);
161
+ const [baseRaw, tempoRaw, credit] = await Promise.all([
162
+ baseClient.readContract({
163
+ address: BASE_USDC,
164
+ abi: erc20Abi,
165
+ functionName: "balanceOf",
166
+ args: [wallet.walletAddress]
167
+ }),
168
+ tempoClient.readContract({
169
+ address: TEMPO_USDC_E,
170
+ abi: erc20Abi,
171
+ functionName: "balanceOf",
172
+ args: [wallet.walletAddress]
173
+ }),
174
+ khClient.request("GET", "/api/agentic-wallet/credit")
175
+ ]);
176
+ if ("_status" in credit) {
177
+ throw new Error("Unexpected 202 response from /api/agentic-wallet/credit");
178
+ }
179
+ return {
180
+ base: {
181
+ chain: "base",
182
+ token: "USDC",
183
+ amount: formatUnits(baseRaw, USDC_DECIMALS),
184
+ address: wallet.walletAddress
185
+ },
186
+ tempo: {
187
+ chain: "tempo",
188
+ token: "USDC.e",
189
+ amount: formatUnits(tempoRaw, USDC_DECIMALS),
190
+ address: wallet.walletAddress
191
+ },
192
+ offChainCredit: {
193
+ amount: credit.amount,
194
+ currency: "USD"
195
+ }
196
+ };
197
+ }
198
+
199
+ // src/fund.ts
200
+ var EVM_ADDRESS_RE = /^0x[0-9a-fA-F]{40}$/;
201
+ var COINBASE_HOST = "pay.coinbase.com";
202
+ var COINBASE_PATH = "/buy/select-asset";
203
+ function fund(walletAddress) {
204
+ if (!EVM_ADDRESS_RE.test(walletAddress)) {
205
+ throw new Error(`Invalid EVM wallet address: ${walletAddress}`);
206
+ }
207
+ const params = new URLSearchParams({
208
+ defaultNetwork: "base",
209
+ defaultAsset: "USDC",
210
+ addresses: JSON.stringify({ [walletAddress]: ["base"] }),
211
+ presetCryptoAmount: "5"
212
+ });
213
+ const coinbaseOnrampUrl = `https://${COINBASE_HOST}${COINBASE_PATH}?${params.toString()}`;
214
+ const disclaimer = "If the Coinbase page does not pre-fill, paste your address manually. For Tempo USDC.e, transfer from an exchange or another wallet to the address above -- Onramp does not support Tempo directly. Coinbase sessionToken URLs are the 2025+ canonical form; legacy query-param URLs may drop prefill on some accounts.";
215
+ return {
216
+ coinbaseOnrampUrl,
217
+ tempoAddress: walletAddress,
218
+ disclaimer
219
+ };
220
+ }
221
+
222
+ // src/skill-install.ts
223
+ import { chmod, copyFile, mkdir, readFile, writeFile } from "fs/promises";
224
+ import { dirname as dirname2, join as join2 } from "path";
225
+ import { fileURLToPath } from "url";
226
+
227
+ // src/agent-detect.ts
228
+ import { existsSync } from "fs";
229
+ import { homedir } from "os";
230
+ import { dirname, join } from "path";
231
+ var AGENT_SPECS = [
232
+ {
233
+ agent: "claude-code",
234
+ skillsRel: [".claude", "skills"],
235
+ settingsRel: [".claude", "settings.json"],
236
+ hookSupport: "claude-code"
237
+ },
238
+ {
239
+ agent: "cursor",
240
+ skillsRel: [".cursor", "skills"],
241
+ settingsRel: [".cursor", "settings.json"],
242
+ hookSupport: "notice"
243
+ },
244
+ {
245
+ agent: "cline",
246
+ skillsRel: [".cline", "skills"],
247
+ settingsRel: [".cline", "settings.json"],
248
+ hookSupport: "notice"
249
+ },
250
+ {
251
+ agent: "windsurf",
252
+ skillsRel: [".windsurf", "skills"],
253
+ settingsRel: [".windsurf", "settings.json"],
254
+ hookSupport: "notice"
255
+ },
256
+ {
257
+ agent: "opencode",
258
+ skillsRel: [".config", "opencode", "skills"],
259
+ settingsRel: [".config", "opencode", "settings.json"],
260
+ hookSupport: "notice"
261
+ }
262
+ ];
263
+ function detectAgents(homeOverride) {
264
+ const home = homeOverride ?? homedir();
265
+ const results = [];
266
+ for (const spec of AGENT_SPECS) {
267
+ const skillsDir = join(home, ...spec.skillsRel);
268
+ const settingsFile = join(home, ...spec.settingsRel);
269
+ if (existsSync(dirname(skillsDir))) {
270
+ results.push({
271
+ agent: spec.agent,
272
+ skillsDir,
273
+ settingsFile,
274
+ hookSupport: spec.hookSupport
275
+ });
276
+ }
277
+ }
278
+ return results;
279
+ }
280
+
281
+ // src/skill-install.ts
282
+ var HOOK_COMMAND = "keeperhub-wallet-hook";
283
+ var KEEPERHUB_HOOK_MARKER = "keeperhub-wallet-hook";
284
+ function buildKeeperhubEntry() {
285
+ return {
286
+ matcher: "*",
287
+ hooks: [{ type: "command", command: HOOK_COMMAND }]
288
+ };
289
+ }
290
+ function resolveDefaultSkillSource() {
291
+ const here = dirname2(fileURLToPath(import.meta.url));
292
+ return join2(here, "..", "skill", "keeperhub-wallet.skill.md");
293
+ }
294
+ function defaultNotice(msg) {
295
+ process.stderr.write(`${msg}
296
+ `);
297
+ }
298
+ async function registerClaudeCodeHook(settingsPath) {
299
+ let raw = null;
300
+ try {
301
+ raw = await readFile(settingsPath, "utf-8");
302
+ } catch (err) {
303
+ if (err.code !== "ENOENT") {
304
+ throw err;
305
+ }
306
+ }
307
+ let config = {};
308
+ if (raw !== null) {
309
+ try {
310
+ config = JSON.parse(raw);
311
+ } catch {
312
+ throw new Error(
313
+ `settings.json at ${settingsPath} is not valid JSON; aborting hook registration`
314
+ );
315
+ }
316
+ }
317
+ const hooks = typeof config.hooks === "object" && config.hooks !== null ? config.hooks : {};
318
+ const existingPreToolUse = Array.isArray(hooks.PreToolUse) ? hooks.PreToolUse : [];
319
+ const filtered = [];
320
+ for (const entry of existingPreToolUse) {
321
+ const serialised = JSON.stringify(entry);
322
+ if (!serialised.includes(KEEPERHUB_HOOK_MARKER)) {
323
+ filtered.push(entry);
324
+ }
325
+ }
326
+ filtered.push(buildKeeperhubEntry());
327
+ hooks.PreToolUse = filtered;
328
+ config.hooks = hooks;
329
+ await mkdir(dirname2(settingsPath), { recursive: true, mode: 448 });
330
+ const payload = `${JSON.stringify(config, null, 2)}
331
+ `;
332
+ await writeFile(settingsPath, payload, { mode: 384 });
333
+ await chmod(settingsPath, 384);
334
+ }
335
+ async function writeSkillToAgent(agent, skillSource) {
336
+ await mkdir(agent.skillsDir, { recursive: true, mode: 493 });
337
+ const target = join2(agent.skillsDir, "keeperhub-wallet.skill.md");
338
+ await copyFile(skillSource, target);
339
+ await chmod(target, 420);
340
+ return { agent: agent.agent, path: target, status: "written" };
341
+ }
342
+ function buildNoticeMessage(agent) {
343
+ return `${agent.agent} does not support auto-registered PreToolUse hooks; run \`${HOOK_COMMAND}\` on every tool use via ${agent.agent}'s settings file at ${agent.settingsFile}`;
344
+ }
345
+ async function installSkill(options = {}) {
346
+ const agents = detectAgents(options.homeOverride);
347
+ const skillSource = options.skillSourcePath ?? resolveDefaultSkillSource();
348
+ const onNotice = options.onNotice ?? defaultNotice;
349
+ const skillWrites = [];
350
+ const hookRegistrations = [];
351
+ for (const agent of agents) {
352
+ const write = await writeSkillToAgent(agent, skillSource);
353
+ skillWrites.push(write);
354
+ if (agent.hookSupport === "claude-code") {
355
+ await registerClaudeCodeHook(agent.settingsFile);
356
+ hookRegistrations.push({
357
+ agent: agent.agent,
358
+ status: "registered"
359
+ });
360
+ } else {
361
+ const message = buildNoticeMessage(agent);
362
+ hookRegistrations.push({
363
+ agent: agent.agent,
364
+ status: "notice",
365
+ message
366
+ });
367
+ onNotice(message);
368
+ }
369
+ }
370
+ return { skillWrites, hookRegistrations };
371
+ }
372
+
373
+ // src/storage.ts
374
+ import { chmod as chmod2, mkdir as mkdir2, readFile as readFile2, writeFile as writeFile2 } from "fs/promises";
375
+ import { homedir as homedir2 } from "os";
376
+ import { dirname as dirname3, join as join3 } from "path";
377
+ async function readWalletConfig() {
378
+ const walletPath = join3(homedir2(), ".keeperhub", "wallet.json");
379
+ let raw;
380
+ try {
381
+ raw = await readFile2(walletPath, "utf-8");
382
+ } catch (err) {
383
+ if (err.code === "ENOENT") {
384
+ throw new WalletConfigMissingError();
385
+ }
386
+ throw err;
387
+ }
388
+ const parsed = JSON.parse(raw);
389
+ if (!(parsed.subOrgId && parsed.walletAddress && parsed.hmacSecret)) {
390
+ throw new Error(`Malformed wallet.json at ${walletPath}`);
391
+ }
392
+ return parsed;
393
+ }
394
+ async function writeWalletConfig(config) {
395
+ const walletPath = join3(homedir2(), ".keeperhub", "wallet.json");
396
+ await mkdir2(dirname3(walletPath), { recursive: true, mode: 448 });
397
+ await writeFile2(walletPath, JSON.stringify(config, null, 2), { mode: 384 });
398
+ await chmod2(walletPath, 384);
399
+ }
400
+ function getWalletConfigPath() {
401
+ return join3(homedir2(), ".keeperhub", "wallet.json");
402
+ }
403
+
404
+ // src/cli.ts
405
+ var TRAILING_SLASH2 = /\/$/;
406
+ var WALLET_ADDRESS_PATTERN = /^0x[a-fA-F0-9]{40}$/;
407
+ function resolveBaseUrl(override) {
408
+ const candidate = override ?? process.env.KEEPERHUB_API_URL ?? "https://app.keeperhub.com";
409
+ return candidate.replace(TRAILING_SLASH2, "");
410
+ }
411
+ function isNonEmptyString(value) {
412
+ return typeof value === "string" && value.length > 0;
413
+ }
414
+ function provisionInvalidError(message) {
415
+ const err = new Error(message);
416
+ err.code = "PROVISION_RESPONSE_INVALID";
417
+ return err;
418
+ }
419
+ function validateProvisionResponse(data) {
420
+ if (typeof data !== "object" || data === null) {
421
+ throw provisionInvalidError("provision response is not an object");
422
+ }
423
+ const { subOrgId, walletAddress, hmacSecret } = data;
424
+ if (!(isNonEmptyString(subOrgId) && isNonEmptyString(walletAddress) && isNonEmptyString(hmacSecret))) {
425
+ throw provisionInvalidError(
426
+ "provision response missing subOrgId, walletAddress, or hmacSecret"
427
+ );
428
+ }
429
+ if (!WALLET_ADDRESS_PATTERN.test(walletAddress)) {
430
+ throw provisionInvalidError(
431
+ `provision response walletAddress is not a valid 0x-prefixed 40-hex address: ${walletAddress}`
432
+ );
433
+ }
434
+ return {
435
+ subOrgId,
436
+ walletAddress,
437
+ hmacSecret
438
+ };
439
+ }
440
+ async function cmdAdd(opts = {}) {
441
+ const baseUrl = resolveBaseUrl(opts.baseUrl);
442
+ const response = await fetch(`${baseUrl}/api/agentic-wallet/provision`, {
443
+ method: "POST",
444
+ headers: { "content-type": "application/json" },
445
+ body: "{}"
446
+ });
447
+ if (!response.ok) {
448
+ const text = await response.text();
449
+ process.stderr.write(
450
+ `[keeperhub-wallet] provision failed: HTTP ${response.status}: ${text}
451
+ `
452
+ );
453
+ process.exit(1);
454
+ }
455
+ const raw = await response.json();
456
+ const data = validateProvisionResponse(raw);
457
+ await writeWalletConfig({
458
+ subOrgId: data.subOrgId,
459
+ walletAddress: data.walletAddress,
460
+ hmacSecret: data.hmacSecret
461
+ });
462
+ process.stdout.write(`subOrgId: ${data.subOrgId}
463
+ `);
464
+ process.stdout.write(`walletAddress: ${data.walletAddress}
465
+ `);
466
+ process.stdout.write(`config written to ${getWalletConfigPath()}
467
+ `);
468
+ }
469
+ async function cmdLink(opts = {}) {
470
+ const wallet = await readWalletConfig();
471
+ const baseUrl = resolveBaseUrl(opts.baseUrl);
472
+ const sessionCookie = process.env.KH_SESSION_COOKIE;
473
+ if (!sessionCookie) {
474
+ process.stderr.write(
475
+ "[keeperhub-wallet] link requires KH_SESSION_COOKIE env var.\nSign in at app.keeperhub.com, copy the session cookie, and re-run with:\n KH_SESSION_COOKIE='<cookie>' npx @keeperhub/wallet link\n"
476
+ );
477
+ process.exit(1);
478
+ }
479
+ const body = JSON.stringify({ subOrgId: wallet.subOrgId });
480
+ const headers = buildHmacHeaders(
481
+ wallet.hmacSecret,
482
+ "POST",
483
+ "/api/agentic-wallet/link",
484
+ wallet.subOrgId,
485
+ body
486
+ );
487
+ const response = await fetch(`${baseUrl}/api/agentic-wallet/link`, {
488
+ method: "POST",
489
+ headers: {
490
+ ...headers,
491
+ "content-type": "application/json",
492
+ cookie: sessionCookie
493
+ },
494
+ body
495
+ });
496
+ const json = await response.json().catch(() => ({}));
497
+ if (!response.ok) {
498
+ process.stderr.write(
499
+ `[keeperhub-wallet] link failed: ${json.code ?? response.status}: ${json.error ?? ""}
500
+ `
501
+ );
502
+ process.exit(1);
503
+ }
504
+ if (json.already) {
505
+ process.stdout.write("already linked\n");
506
+ return;
507
+ }
508
+ process.stdout.write("linked\n");
509
+ }
510
+ async function cmdFund() {
511
+ const wallet = await readWalletConfig();
512
+ const out = fund(wallet.walletAddress);
513
+ process.stdout.write(`${out.coinbaseOnrampUrl}
514
+ `);
515
+ process.stdout.write(`Tempo address: ${out.tempoAddress}
516
+ `);
517
+ process.stdout.write(`${out.disclaimer}
518
+ `);
519
+ }
520
+ async function cmdBalance() {
521
+ const wallet = await readWalletConfig();
522
+ const snap = await checkBalance(wallet);
523
+ process.stdout.write(`Base USDC: ${snap.base.amount}
524
+ `);
525
+ process.stdout.write(`Tempo USDC.e: ${snap.tempo.amount}
526
+ `);
527
+ process.stdout.write(
528
+ `KeeperHub credit: ${snap.offChainCredit.amount} ${snap.offChainCredit.currency}
529
+ `
530
+ );
531
+ }
532
+ async function cmdInfo() {
533
+ const wallet = await readWalletConfig();
534
+ process.stdout.write(`subOrgId: ${wallet.subOrgId}
535
+ `);
536
+ process.stdout.write(`walletAddress: ${wallet.walletAddress}
537
+ `);
538
+ }
539
+ async function runCli(argv = process.argv) {
540
+ const program = new Command();
541
+ program.name("keeperhub-wallet").description(
542
+ "KeeperHub agentic wallet CLI (auto-pay x402 + MPP 402 responses)"
543
+ ).version("0.1.0");
544
+ program.command("add").description("Provision a new agentic wallet (no account required)").option("--base-url <url>", "KeeperHub API base URL").action(async (opts) => {
545
+ await cmdAdd(opts);
546
+ });
547
+ program.command("link").description(
548
+ "Link the current wallet to your KeeperHub account (requires KH_SESSION_COOKIE env)"
549
+ ).option("--base-url <url>", "KeeperHub API base URL").action(async (opts) => {
550
+ await cmdLink(opts);
551
+ });
552
+ program.command("fund").description(
553
+ "Print Coinbase Onramp URL (Base USDC) and Tempo deposit address"
554
+ ).action(async () => {
555
+ await cmdFund();
556
+ });
557
+ program.command("balance").description(
558
+ "Print unified balance: Base USDC + Tempo USDC.e + off-chain KeeperHub credit"
559
+ ).action(async () => {
560
+ await cmdBalance();
561
+ });
562
+ program.command("info").description("Print subOrgId and walletAddress from local config").action(async () => {
563
+ await cmdInfo();
564
+ });
565
+ program.command("skill").description(
566
+ "Install the KeeperHub skill file into detected agent directories"
567
+ ).addCommand(
568
+ new Command("install").description(
569
+ "Write skill file + register PreToolUse hook in all detected agents"
570
+ ).action(async () => {
571
+ const result = await installSkill();
572
+ for (const write of result.skillWrites) {
573
+ process.stdout.write(
574
+ `skill: ${write.agent} -> ${write.path} (${write.status})
575
+ `
576
+ );
577
+ }
578
+ for (const reg of result.hookRegistrations) {
579
+ if (reg.status === "registered") {
580
+ process.stdout.write(
581
+ `hook: ${reg.agent} -> PreToolUse registered
582
+ `
583
+ );
584
+ } else if (reg.status === "notice") {
585
+ process.stderr.write(
586
+ `notice: ${reg.agent} -> ${reg.message ?? ""}
587
+ `
588
+ );
589
+ }
590
+ }
591
+ if (result.skillWrites.length === 0) {
592
+ process.stderr.write(
593
+ "No supported agent skill directories detected under $HOME. Create ~/.claude/, ~/.cursor/, ~/.cline/, ~/.windsurf/, or ~/.config/opencode/ and re-run.\n"
594
+ );
595
+ }
596
+ })
597
+ );
598
+ try {
599
+ await program.parseAsync(argv);
600
+ } catch (err) {
601
+ if (err instanceof WalletConfigMissingError) {
602
+ process.stderr.write(`[keeperhub-wallet] ${err.message}
603
+ `);
604
+ process.exit(1);
605
+ }
606
+ process.stderr.write(
607
+ `[keeperhub-wallet] ${err.message ?? String(err)}
608
+ `
609
+ );
610
+ process.exit(1);
611
+ }
612
+ }
613
+ export {
614
+ runCli
615
+ };
616
+ //# sourceMappingURL=cli.js.map