@keeperhub/wallet 0.1.2 → 0.1.3

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.d.ts CHANGED
@@ -37,49 +37,6 @@ declare class WalletConfigMissingError extends Error {
37
37
  constructor();
38
38
  }
39
39
 
40
- type ClientOptions = {
41
- /** Defaults to process.env.KEEPERHUB_API_URL ?? "https://app.keeperhub.com" */
42
- baseUrl?: string;
43
- /** Injected for tests; defaults to global fetch */
44
- fetch?: typeof fetch;
45
- };
46
- /**
47
- * 202 ask-tier envelope returned by /sign and /approval-request when the
48
- * risk classifier routes a request to the ask queue. Callers poll
49
- * `/api/agentic-wallet/approval-request/:id` until status !== "pending".
50
- */
51
- type AskTierResponse = {
52
- _status: 202;
53
- approvalRequestId: string;
54
- };
55
- /**
56
- * HMAC-signed HTTP client for the KeeperHub agentic-wallet API surface.
57
- * Every request to /api/agentic-wallet/* (except /provision, which uses
58
- * the session cookie) flows through this class.
59
- *
60
- * @security No logging of headers, body, or response bodies. Any stdout
61
- * emitter (the global console object or util.inspect) added to this
62
- * file is a T-34-08 violation (grep-enforced in CI).
63
- */
64
- declare class KeeperHubClient {
65
- private readonly baseUrl;
66
- private readonly fetchImpl;
67
- private readonly wallet;
68
- constructor(wallet: WalletConfig, opts?: ClientOptions);
69
- /**
70
- * HMAC-signed POST/GET to any /api/agentic-wallet/* route except
71
- * /provision. Path MUST start with a leading slash. Body is
72
- * JSON.stringify'd (or the empty string for GET).
73
- *
74
- * Error mapping: non-2xx/non-202 surface as `KeeperHubError(code,
75
- * message)` where `code` is the server-supplied field or the default
76
- * taxonomy (`HMAC_INVALID`, `POLICY_BLOCKED`, `NOT_FOUND`,
77
- * `TURNKEY_UPSTREAM`, `HTTP_<status>`). 202 ask-tier surfaces as an
78
- * AskTierResponse envelope.
79
- */
80
- request<T>(method: "GET" | "POST", path: string, body?: unknown): Promise<T | AskTierResponse>;
81
- }
82
-
83
40
  type BalanceSnapshot = {
84
41
  base: {
85
42
  chain: "base";
@@ -93,23 +50,16 @@ type BalanceSnapshot = {
93
50
  amount: string;
94
51
  address: `0x${string}`;
95
52
  };
96
- offChainCredit: {
97
- amount: string;
98
- currency: "USD";
99
- };
100
53
  };
101
54
  type CheckBalanceOptions = {
102
55
  /** Injectable viem client for Base (tests mock readContract). */
103
56
  baseClient?: PublicClient;
104
57
  /** Injectable viem client for Tempo (tests mock readContract). */
105
58
  tempoClient?: PublicClient;
106
- /** Injectable KeeperHubClient (tests inject a mocked fetch). */
107
- khClient?: KeeperHubClient;
108
59
  };
109
60
  /**
110
- * Read the wallet's balance across Base + Tempo + off-chain KeeperHub credit
111
- * in parallel. All three legs must resolve; any single failure rejects the
112
- * Promise.
61
+ * Read the wallet's on-chain balance across Base + Tempo in parallel. Both
62
+ * legs must resolve; any single failure rejects the Promise.
113
63
  *
114
64
  * Amounts are formatted as decimal strings (6-decimal USDC precision) so the
115
65
  * caller can render them without BigInt math.
@@ -168,6 +118,49 @@ declare const BASE_USDC: "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913";
168
118
  /** Bridged USDC (USDC.e) on Tempo mainnet. NOT the same contract as BASE_USDC. */
169
119
  declare const TEMPO_USDC_E: "0x20c000000000000000000000b9537d11c60e8b50";
170
120
 
121
+ type ClientOptions = {
122
+ /** Defaults to process.env.KEEPERHUB_API_URL ?? "https://app.keeperhub.com" */
123
+ baseUrl?: string;
124
+ /** Injected for tests; defaults to global fetch */
125
+ fetch?: typeof fetch;
126
+ };
127
+ /**
128
+ * 202 ask-tier envelope returned by /sign and /approval-request when the
129
+ * risk classifier routes a request to the ask queue. Callers poll
130
+ * `/api/agentic-wallet/approval-request/:id` until status !== "pending".
131
+ */
132
+ type AskTierResponse = {
133
+ _status: 202;
134
+ approvalRequestId: string;
135
+ };
136
+ /**
137
+ * HMAC-signed HTTP client for the KeeperHub agentic-wallet API surface.
138
+ * Every request to /api/agentic-wallet/* (except /provision, which uses
139
+ * the session cookie) flows through this class.
140
+ *
141
+ * @security No logging of headers, body, or response bodies. Any stdout
142
+ * emitter (the global console object or util.inspect) added to this
143
+ * file is a T-34-08 violation (grep-enforced in CI).
144
+ */
145
+ declare class KeeperHubClient {
146
+ private readonly baseUrl;
147
+ private readonly fetchImpl;
148
+ private readonly wallet;
149
+ constructor(wallet: WalletConfig, opts?: ClientOptions);
150
+ /**
151
+ * HMAC-signed POST/GET to any /api/agentic-wallet/* route except
152
+ * /provision. Path MUST start with a leading slash. Body is
153
+ * JSON.stringify'd (or the empty string for GET).
154
+ *
155
+ * Error mapping: non-2xx/non-202 surface as `KeeperHubError(code,
156
+ * message)` where `code` is the server-supplied field or the default
157
+ * taxonomy (`HMAC_INVALID`, `POLICY_BLOCKED`, `NOT_FOUND`,
158
+ * `TURNKEY_UPSTREAM`, `HTTP_<status>`). 202 ask-tier surfaces as an
159
+ * AskTierResponse envelope.
160
+ */
161
+ request<T>(method: "GET" | "POST", path: string, body?: unknown): Promise<T | AskTierResponse>;
162
+ }
163
+
171
164
  type FundInstructions = {
172
165
  /** Coinbase Onramp deeplink (legacy query-param form). */
173
166
  coinbaseOnrampUrl: string;
package/dist/index.js CHANGED
@@ -79,124 +79,6 @@ var tempo = defineChain({
79
79
  var BASE_USDC = "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913";
80
80
  var TEMPO_USDC_E = "0x20c000000000000000000000b9537d11c60e8b50";
81
81
 
82
- // src/hmac.ts
83
- import { createHash, createHmac } from "crypto";
84
- function computeSignature(secret, method, path, subOrgId, body, timestamp) {
85
- const bodyDigest = createHash("sha256").update(body).digest("hex");
86
- const signingString = `${method}
87
- ${path}
88
- ${subOrgId}
89
- ${bodyDigest}
90
- ${timestamp}`;
91
- return createHmac("sha256", secret).update(signingString).digest("hex");
92
- }
93
- function buildHmacHeaders(secret, method, path, subOrgId, body) {
94
- const timestamp = String(Math.floor(Date.now() / 1e3));
95
- const signature = computeSignature(
96
- secret,
97
- method,
98
- path,
99
- subOrgId,
100
- body,
101
- timestamp
102
- );
103
- return {
104
- "X-KH-Sub-Org": subOrgId,
105
- "X-KH-Timestamp": timestamp,
106
- "X-KH-Signature": signature
107
- };
108
- }
109
-
110
- // src/types.ts
111
- var KeeperHubError = class extends Error {
112
- code;
113
- constructor(code, message) {
114
- super(message);
115
- this.name = "KeeperHubError";
116
- this.code = code;
117
- }
118
- };
119
- var WalletConfigMissingError = class extends Error {
120
- constructor() {
121
- super(
122
- "Wallet config not found at ~/.keeperhub/wallet.json. Run `npx @keeperhub/wallet add` to provision."
123
- );
124
- this.name = "WalletConfigMissingError";
125
- }
126
- };
127
-
128
- // src/client.ts
129
- var TRAILING_SLASH = /\/$/;
130
- function defaultCodeForStatus(status) {
131
- if (status === 401) {
132
- return "HMAC_INVALID";
133
- }
134
- if (status === 403) {
135
- return "POLICY_BLOCKED";
136
- }
137
- if (status === 404) {
138
- return "NOT_FOUND";
139
- }
140
- if (status === 502) {
141
- return "TURNKEY_UPSTREAM";
142
- }
143
- return `HTTP_${status}`;
144
- }
145
- var KeeperHubClient = class {
146
- baseUrl;
147
- fetchImpl;
148
- wallet;
149
- constructor(wallet, opts = {}) {
150
- this.wallet = wallet;
151
- const envBase = process.env.KEEPERHUB_API_URL;
152
- this.baseUrl = (opts.baseUrl ?? envBase ?? "https://app.keeperhub.com").replace(TRAILING_SLASH, "");
153
- this.fetchImpl = opts.fetch ?? globalThis.fetch;
154
- }
155
- /**
156
- * HMAC-signed POST/GET to any /api/agentic-wallet/* route except
157
- * /provision. Path MUST start with a leading slash. Body is
158
- * JSON.stringify'd (or the empty string for GET).
159
- *
160
- * Error mapping: non-2xx/non-202 surface as `KeeperHubError(code,
161
- * message)` where `code` is the server-supplied field or the default
162
- * taxonomy (`HMAC_INVALID`, `POLICY_BLOCKED`, `NOT_FOUND`,
163
- * `TURNKEY_UPSTREAM`, `HTTP_<status>`). 202 ask-tier surfaces as an
164
- * AskTierResponse envelope.
165
- */
166
- async request(method, path, body) {
167
- const bodyStr = body === void 0 ? "" : JSON.stringify(body);
168
- const hmacHeaders = buildHmacHeaders(
169
- this.wallet.hmacSecret,
170
- method,
171
- path,
172
- this.wallet.subOrgId,
173
- bodyStr
174
- );
175
- const headers = method === "POST" ? { ...hmacHeaders, "content-type": "application/json" } : { ...hmacHeaders };
176
- const response = await this.fetchImpl(`${this.baseUrl}${path}`, {
177
- method,
178
- headers,
179
- body: method === "POST" ? bodyStr : void 0
180
- });
181
- if (response.status === 202) {
182
- const data = await response.json();
183
- return { _status: 202, approvalRequestId: data.approvalRequestId };
184
- }
185
- if (!response.ok) {
186
- let code = "UNKNOWN";
187
- let message = `HTTP ${response.status}`;
188
- try {
189
- const data = await response.json();
190
- code = data.code ?? defaultCodeForStatus(response.status);
191
- message = data.error ?? message;
192
- } catch {
193
- }
194
- throw new KeeperHubError(code, message);
195
- }
196
- return await response.json();
197
- }
198
- };
199
-
200
82
  // src/balance.ts
201
83
  var USDC_DECIMALS = 6;
202
84
  async function checkBalance(wallet, opts = {}) {
@@ -208,8 +90,7 @@ async function checkBalance(wallet, opts = {}) {
208
90
  chain: tempo,
209
91
  transport: http()
210
92
  });
211
- const khClient = opts.khClient ?? new KeeperHubClient(wallet);
212
- const [baseRaw, tempoRaw, credit] = await Promise.all([
93
+ const [baseRaw, tempoRaw] = await Promise.all([
213
94
  baseClient.readContract({
214
95
  address: BASE_USDC,
215
96
  abi: erc20Abi,
@@ -221,12 +102,8 @@ async function checkBalance(wallet, opts = {}) {
221
102
  abi: erc20Abi,
222
103
  functionName: "balanceOf",
223
104
  args: [wallet.walletAddress]
224
- }),
225
- khClient.request("GET", "/api/agentic-wallet/credit")
105
+ })
226
106
  ]);
227
- if ("_status" in credit) {
228
- throw new Error("Unexpected 202 response from /api/agentic-wallet/credit");
229
- }
230
107
  return {
231
108
  base: {
232
109
  chain: "base",
@@ -239,10 +116,6 @@ async function checkBalance(wallet, opts = {}) {
239
116
  token: "USDC.e",
240
117
  amount: formatUnits(tempoRaw, USDC_DECIMALS),
241
118
  address: wallet.walletAddress
242
- },
243
- offChainCredit: {
244
- amount: credit.amount,
245
- currency: "USD"
246
119
  }
247
120
  };
248
121
  }
@@ -273,6 +146,34 @@ function fund(walletAddress) {
273
146
  };
274
147
  }
275
148
 
149
+ // src/hmac.ts
150
+ import { createHash, createHmac } from "crypto";
151
+ function computeSignature(secret, method, path, subOrgId, body, timestamp) {
152
+ const bodyDigest = createHash("sha256").update(body).digest("hex");
153
+ const signingString = `${method}
154
+ ${path}
155
+ ${subOrgId}
156
+ ${bodyDigest}
157
+ ${timestamp}`;
158
+ return createHmac("sha256", secret).update(signingString).digest("hex");
159
+ }
160
+ function buildHmacHeaders(secret, method, path, subOrgId, body) {
161
+ const timestamp = String(Math.floor(Date.now() / 1e3));
162
+ const signature = computeSignature(
163
+ secret,
164
+ method,
165
+ path,
166
+ subOrgId,
167
+ body,
168
+ timestamp
169
+ );
170
+ return {
171
+ "X-KH-Sub-Org": subOrgId,
172
+ "X-KH-Timestamp": timestamp,
173
+ "X-KH-Signature": signature
174
+ };
175
+ }
176
+
276
177
  // src/skill-install.ts
277
178
  import { chmod, copyFile, mkdir, readFile, writeFile } from "fs/promises";
278
179
  import { dirname as dirname2, join as join2 } from "path";
@@ -372,6 +273,26 @@ async function installSkill(options = {}) {
372
273
  import { chmod as chmod2, mkdir as mkdir2, readFile as readFile2, writeFile as writeFile2 } from "fs/promises";
373
274
  import { homedir as homedir2 } from "os";
374
275
  import { dirname as dirname3, join as join3 } from "path";
276
+
277
+ // src/types.ts
278
+ var KeeperHubError = class extends Error {
279
+ code;
280
+ constructor(code, message) {
281
+ super(message);
282
+ this.name = "KeeperHubError";
283
+ this.code = code;
284
+ }
285
+ };
286
+ var WalletConfigMissingError = class extends Error {
287
+ constructor() {
288
+ super(
289
+ "Wallet config not found at ~/.keeperhub/wallet.json. Run `npx @keeperhub/wallet add` to provision."
290
+ );
291
+ this.name = "WalletConfigMissingError";
292
+ }
293
+ };
294
+
295
+ // src/storage.ts
375
296
  async function readWalletConfig() {
376
297
  const walletPath = join3(homedir2(), ".keeperhub", "wallet.json");
377
298
  let raw;
@@ -400,11 +321,11 @@ function getWalletConfigPath() {
400
321
  }
401
322
 
402
323
  // src/cli.ts
403
- var TRAILING_SLASH2 = /\/$/;
324
+ var TRAILING_SLASH = /\/$/;
404
325
  var WALLET_ADDRESS_PATTERN = /^0x[a-fA-F0-9]{40}$/;
405
326
  function resolveBaseUrl(override) {
406
327
  const candidate = override ?? process.env.KEEPERHUB_API_URL ?? "https://app.keeperhub.com";
407
- return candidate.replace(TRAILING_SLASH2, "");
328
+ return candidate.replace(TRAILING_SLASH, "");
408
329
  }
409
330
  function isNonEmptyString(value) {
410
331
  return typeof value === "string" && value.length > 0;
@@ -518,14 +439,10 @@ async function cmdFund() {
518
439
  async function cmdBalance() {
519
440
  const wallet = await readWalletConfig();
520
441
  const snap = await checkBalance(wallet);
521
- process.stdout.write(`Base USDC: ${snap.base.amount}
442
+ process.stdout.write(`Base USDC: ${snap.base.amount}
522
443
  `);
523
- process.stdout.write(`Tempo USDC.e: ${snap.tempo.amount}
444
+ process.stdout.write(`Tempo USDC.e: ${snap.tempo.amount}
524
445
  `);
525
- process.stdout.write(
526
- `KeeperHub credit: ${snap.offChainCredit.amount} ${snap.offChainCredit.currency}
527
- `
528
- );
529
446
  }
530
447
  async function cmdInfo() {
531
448
  const wallet = await readWalletConfig();
@@ -538,7 +455,7 @@ async function runCli(argv = process.argv) {
538
455
  const program = new Command();
539
456
  program.name("keeperhub-wallet").description(
540
457
  "KeeperHub agentic wallet CLI (auto-pay x402 + MPP 402 responses)"
541
- ).version("0.1.0");
458
+ ).version("0.1.3");
542
459
  program.command("add").description("Provision a new agentic wallet (no account required)").option("--base-url <url>", "KeeperHub API base URL").action(async (opts) => {
543
460
  await cmdAdd(opts);
544
461
  });
@@ -552,9 +469,7 @@ async function runCli(argv = process.argv) {
552
469
  ).action(async () => {
553
470
  await cmdFund();
554
471
  });
555
- program.command("balance").description(
556
- "Print unified balance: Base USDC + Tempo USDC.e + off-chain KeeperHub credit"
557
- ).action(async () => {
472
+ program.command("balance").description("Print on-chain balance: Base USDC + Tempo USDC.e").action(async () => {
558
473
  await cmdBalance();
559
474
  });
560
475
  program.command("info").description("Print subOrgId and walletAddress from local config").action(async () => {
@@ -609,6 +524,78 @@ async function runCli(argv = process.argv) {
609
524
  }
610
525
  }
611
526
 
527
+ // src/client.ts
528
+ var TRAILING_SLASH2 = /\/$/;
529
+ function defaultCodeForStatus(status) {
530
+ if (status === 401) {
531
+ return "HMAC_INVALID";
532
+ }
533
+ if (status === 403) {
534
+ return "POLICY_BLOCKED";
535
+ }
536
+ if (status === 404) {
537
+ return "NOT_FOUND";
538
+ }
539
+ if (status === 502) {
540
+ return "TURNKEY_UPSTREAM";
541
+ }
542
+ return `HTTP_${status}`;
543
+ }
544
+ var KeeperHubClient = class {
545
+ baseUrl;
546
+ fetchImpl;
547
+ wallet;
548
+ constructor(wallet, opts = {}) {
549
+ this.wallet = wallet;
550
+ const envBase = process.env.KEEPERHUB_API_URL;
551
+ this.baseUrl = (opts.baseUrl ?? envBase ?? "https://app.keeperhub.com").replace(TRAILING_SLASH2, "");
552
+ this.fetchImpl = opts.fetch ?? globalThis.fetch;
553
+ }
554
+ /**
555
+ * HMAC-signed POST/GET to any /api/agentic-wallet/* route except
556
+ * /provision. Path MUST start with a leading slash. Body is
557
+ * JSON.stringify'd (or the empty string for GET).
558
+ *
559
+ * Error mapping: non-2xx/non-202 surface as `KeeperHubError(code,
560
+ * message)` where `code` is the server-supplied field or the default
561
+ * taxonomy (`HMAC_INVALID`, `POLICY_BLOCKED`, `NOT_FOUND`,
562
+ * `TURNKEY_UPSTREAM`, `HTTP_<status>`). 202 ask-tier surfaces as an
563
+ * AskTierResponse envelope.
564
+ */
565
+ async request(method, path, body) {
566
+ const bodyStr = body === void 0 ? "" : JSON.stringify(body);
567
+ const hmacHeaders = buildHmacHeaders(
568
+ this.wallet.hmacSecret,
569
+ method,
570
+ path,
571
+ this.wallet.subOrgId,
572
+ bodyStr
573
+ );
574
+ const headers = method === "POST" ? { ...hmacHeaders, "content-type": "application/json" } : { ...hmacHeaders };
575
+ const response = await this.fetchImpl(`${this.baseUrl}${path}`, {
576
+ method,
577
+ headers,
578
+ body: method === "POST" ? bodyStr : void 0
579
+ });
580
+ if (response.status === 202) {
581
+ const data = await response.json();
582
+ return { _status: 202, approvalRequestId: data.approvalRequestId };
583
+ }
584
+ if (!response.ok) {
585
+ let code = "UNKNOWN";
586
+ let message = `HTTP ${response.status}`;
587
+ try {
588
+ const data = await response.json();
589
+ code = data.code ?? defaultCodeForStatus(response.status);
590
+ message = data.error ?? message;
591
+ } catch {
592
+ }
593
+ throw new KeeperHubError(code, message);
594
+ }
595
+ return await response.json();
596
+ }
597
+ };
598
+
612
599
  // src/safety-config.ts
613
600
  import { chmod as chmod3, mkdir as mkdir3, readFile as readFile3, writeFile as writeFile3 } from "fs/promises";
614
601
  import { homedir as homedir3 } from "os";