@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/LICENSE +19 -0
- package/README.md +23 -0
- package/bin/keeperhub-wallet-hook.js +9 -0
- package/bin/keeperhub-wallet.js +7 -0
- package/dist/cli.cjs +636 -0
- package/dist/cli.cjs.map +1 -0
- package/dist/cli.d.cts +3 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.js +616 -0
- package/dist/cli.js.map +1 -0
- package/dist/hook-entrypoint.cjs +390 -0
- package/dist/hook-entrypoint.cjs.map +1 -0
- package/dist/hook-entrypoint.d.cts +17 -0
- package/dist/hook-entrypoint.d.ts +17 -0
- package/dist/hook-entrypoint.js +363 -0
- package/dist/hook-entrypoint.js.map +1 -0
- package/dist/index.cjs +1114 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +328 -0
- package/dist/index.d.ts +328 -0
- package/dist/index.js +1065 -0
- package/dist/index.js.map +1 -0
- package/package.json +56 -0
- package/skill/keeperhub-wallet.skill.md +62 -0
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,328 @@
|
|
|
1
|
+
import * as viem from 'viem';
|
|
2
|
+
import { PublicClient } from 'viem';
|
|
3
|
+
export { runCli } from './cli.js';
|
|
4
|
+
export { runHookCli } from './hook-entrypoint.js';
|
|
5
|
+
export { base } from 'viem/chains';
|
|
6
|
+
|
|
7
|
+
type AgentTarget = {
|
|
8
|
+
agent: "claude-code" | "cursor" | "cline" | "windsurf" | "opencode";
|
|
9
|
+
skillsDir: string;
|
|
10
|
+
settingsFile: string;
|
|
11
|
+
hookSupport: "claude-code" | "notice";
|
|
12
|
+
};
|
|
13
|
+
declare function detectAgents(homeOverride?: string): AgentTarget[];
|
|
14
|
+
|
|
15
|
+
type WalletConfig = {
|
|
16
|
+
/** Turnkey sub-org ID returned by POST /api/agentic-wallet/provision */
|
|
17
|
+
subOrgId: string;
|
|
18
|
+
/** EVM-shared wallet address (same for Base chainId 8453 and Tempo chainId 4217) */
|
|
19
|
+
walletAddress: `0x${string}`;
|
|
20
|
+
/** 64-char lowercase hex HMAC secret, minted server-side at provision; never logged */
|
|
21
|
+
hmacSecret: string;
|
|
22
|
+
};
|
|
23
|
+
type HmacHeaders = {
|
|
24
|
+
"X-KH-Sub-Org": string;
|
|
25
|
+
"X-KH-Timestamp": string;
|
|
26
|
+
"X-KH-Signature": string;
|
|
27
|
+
};
|
|
28
|
+
type HookDecision = {
|
|
29
|
+
decision: "allow" | "deny" | "ask";
|
|
30
|
+
reason?: string;
|
|
31
|
+
};
|
|
32
|
+
declare class KeeperHubError extends Error {
|
|
33
|
+
readonly code: string;
|
|
34
|
+
constructor(code: string, message: string);
|
|
35
|
+
}
|
|
36
|
+
declare class WalletConfigMissingError extends Error {
|
|
37
|
+
constructor();
|
|
38
|
+
}
|
|
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
|
+
type BalanceSnapshot = {
|
|
84
|
+
base: {
|
|
85
|
+
chain: "base";
|
|
86
|
+
token: "USDC";
|
|
87
|
+
amount: string;
|
|
88
|
+
address: `0x${string}`;
|
|
89
|
+
};
|
|
90
|
+
tempo: {
|
|
91
|
+
chain: "tempo";
|
|
92
|
+
token: "USDC.e";
|
|
93
|
+
amount: string;
|
|
94
|
+
address: `0x${string}`;
|
|
95
|
+
};
|
|
96
|
+
offChainCredit: {
|
|
97
|
+
amount: string;
|
|
98
|
+
currency: "USD";
|
|
99
|
+
};
|
|
100
|
+
};
|
|
101
|
+
type CheckBalanceOptions = {
|
|
102
|
+
/** Injectable viem client for Base (tests mock readContract). */
|
|
103
|
+
baseClient?: PublicClient;
|
|
104
|
+
/** Injectable viem client for Tempo (tests mock readContract). */
|
|
105
|
+
tempoClient?: PublicClient;
|
|
106
|
+
/** Injectable KeeperHubClient (tests inject a mocked fetch). */
|
|
107
|
+
khClient?: KeeperHubClient;
|
|
108
|
+
};
|
|
109
|
+
/**
|
|
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.
|
|
113
|
+
*
|
|
114
|
+
* Amounts are formatted as decimal strings (6-decimal USDC precision) so the
|
|
115
|
+
* caller can render them without BigInt math.
|
|
116
|
+
*/
|
|
117
|
+
declare function checkBalance(wallet: WalletConfig, opts?: CheckBalanceOptions): Promise<BalanceSnapshot>;
|
|
118
|
+
|
|
119
|
+
declare const tempo: {
|
|
120
|
+
blockExplorers: {
|
|
121
|
+
readonly default: {
|
|
122
|
+
readonly name: "Tempo Explorer";
|
|
123
|
+
readonly url: "https://explorer.tempo.xyz";
|
|
124
|
+
};
|
|
125
|
+
};
|
|
126
|
+
blockTime?: number | undefined | undefined;
|
|
127
|
+
contracts?: {
|
|
128
|
+
[x: string]: viem.ChainContract | {
|
|
129
|
+
[sourceId: number]: viem.ChainContract | undefined;
|
|
130
|
+
} | undefined;
|
|
131
|
+
ensRegistry?: viem.ChainContract | undefined;
|
|
132
|
+
ensUniversalResolver?: viem.ChainContract | undefined;
|
|
133
|
+
multicall3?: viem.ChainContract | undefined;
|
|
134
|
+
erc6492Verifier?: viem.ChainContract | undefined;
|
|
135
|
+
} | undefined;
|
|
136
|
+
ensTlds?: readonly string[] | undefined;
|
|
137
|
+
id: 4217;
|
|
138
|
+
name: "Tempo";
|
|
139
|
+
nativeCurrency: {
|
|
140
|
+
readonly decimals: 18;
|
|
141
|
+
readonly name: "Ether";
|
|
142
|
+
readonly symbol: "ETH";
|
|
143
|
+
};
|
|
144
|
+
experimental_preconfirmationTime?: number | undefined | undefined;
|
|
145
|
+
rpcUrls: {
|
|
146
|
+
readonly default: {
|
|
147
|
+
readonly http: readonly [string];
|
|
148
|
+
};
|
|
149
|
+
};
|
|
150
|
+
sourceId?: number | undefined | undefined;
|
|
151
|
+
testnet?: boolean | undefined | undefined;
|
|
152
|
+
custom?: Record<string, unknown> | undefined;
|
|
153
|
+
extendSchema?: Record<string, unknown> | undefined;
|
|
154
|
+
fees?: viem.ChainFees<undefined> | undefined;
|
|
155
|
+
formatters?: undefined;
|
|
156
|
+
prepareTransactionRequest?: ((args: viem.PrepareTransactionRequestParameters, options: {
|
|
157
|
+
phase: "beforeFillTransaction" | "beforeFillParameters" | "afterFillParameters";
|
|
158
|
+
}) => Promise<viem.PrepareTransactionRequestParameters>) | [fn: ((args: viem.PrepareTransactionRequestParameters, options: {
|
|
159
|
+
phase: "beforeFillTransaction" | "beforeFillParameters" | "afterFillParameters";
|
|
160
|
+
}) => Promise<viem.PrepareTransactionRequestParameters>) | undefined, options: {
|
|
161
|
+
runAt: readonly ("beforeFillTransaction" | "beforeFillParameters" | "afterFillParameters")[];
|
|
162
|
+
}] | undefined;
|
|
163
|
+
serializers?: viem.ChainSerializers<undefined, viem.TransactionSerializable> | undefined;
|
|
164
|
+
verifyHash?: ((client: viem.Client, parameters: viem.VerifyHashActionParameters) => Promise<viem.VerifyHashActionReturnType>) | undefined;
|
|
165
|
+
};
|
|
166
|
+
/** Circle-issued USDC on Base mainnet. */
|
|
167
|
+
declare const BASE_USDC: "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913";
|
|
168
|
+
/** Bridged USDC (USDC.e) on Tempo mainnet. NOT the same contract as BASE_USDC. */
|
|
169
|
+
declare const TEMPO_USDC_E: "0x20c000000000000000000000b9537d11c60e8b50";
|
|
170
|
+
|
|
171
|
+
type FundInstructions = {
|
|
172
|
+
/** Coinbase Onramp deeplink (legacy query-param form). */
|
|
173
|
+
coinbaseOnrampUrl: string;
|
|
174
|
+
/** Tempo deposit address — same as the input wallet (EVM address shared). */
|
|
175
|
+
tempoAddress: `0x${string}`;
|
|
176
|
+
/** Plain-ASCII guidance string; no emojis (CLAUDE.md rule). */
|
|
177
|
+
disclaimer: string;
|
|
178
|
+
};
|
|
179
|
+
/**
|
|
180
|
+
* Build Coinbase Onramp URL + Tempo deposit address for the given wallet.
|
|
181
|
+
*
|
|
182
|
+
* No HTTP calls are performed. The caller is expected to either print the
|
|
183
|
+
* resulting URL (CLI) or render it in a chat bubble (skill). The returned
|
|
184
|
+
* `disclaimer` explains the Onramp deprecation + the Tempo external-transfer
|
|
185
|
+
* fallback in plain ASCII so terminal clients with ASCII-only fonts render
|
|
186
|
+
* identically to emoji-capable clients.
|
|
187
|
+
*
|
|
188
|
+
* @throws if `walletAddress` does not match /^0x[0-9a-fA-F]{40}$/.
|
|
189
|
+
*/
|
|
190
|
+
declare function fund(walletAddress: string): FundInstructions;
|
|
191
|
+
|
|
192
|
+
/**
|
|
193
|
+
* Mirror of lib/agentic-wallet/hmac.ts::computeSignature.
|
|
194
|
+
* Format (byte-for-byte identical to server):
|
|
195
|
+
* `${method}\n${path}\n${subOrgId}\n${sha256_hex(body)}\n${timestamp}`
|
|
196
|
+
* Post-HI-05: subOrgId is a signed field.
|
|
197
|
+
*
|
|
198
|
+
* @security Do NOT log the secret or the returned signature. Any stdout
|
|
199
|
+
* emitter (the global console object or util.inspect) added to this
|
|
200
|
+
* file is a T-34-08 violation (grep-enforced).
|
|
201
|
+
*/
|
|
202
|
+
declare function computeSignature(secret: string, method: string, path: string, subOrgId: string, body: string, timestamp: string): string;
|
|
203
|
+
/**
|
|
204
|
+
* Build the three X-KH-* headers that authenticate every request to
|
|
205
|
+
* /api/agentic-wallet/* (except /provision, which uses the session cookie).
|
|
206
|
+
*
|
|
207
|
+
* Timestamp is unix seconds (Math.floor(Date.now() / 1000)); the server
|
|
208
|
+
* enforces a symmetric 300-second replay window.
|
|
209
|
+
*/
|
|
210
|
+
declare function buildHmacHeaders(secret: string, method: string, path: string, subOrgId: string, body: string): HmacHeaders;
|
|
211
|
+
|
|
212
|
+
/**
|
|
213
|
+
* User-owned safety config at ~/.keeperhub/safety.json. File mode 0o644 so the
|
|
214
|
+
* user can freely edit thresholds and the allowlist; server-side Turnkey policy
|
|
215
|
+
* remains the authoritative hard cap (GUARD-06).
|
|
216
|
+
*/
|
|
217
|
+
type SafetyConfig = {
|
|
218
|
+
auto_approve_max_usd: number;
|
|
219
|
+
ask_threshold_usd: number;
|
|
220
|
+
block_threshold_usd: number;
|
|
221
|
+
allowlisted_contracts: string[];
|
|
222
|
+
};
|
|
223
|
+
/**
|
|
224
|
+
* Defaults per 34-CONTEXT lines 61-68. Thresholds bracket the Turnkey policy
|
|
225
|
+
* hard cap (100 USDC). Allowlisted contracts mirror the server Turnkey policy
|
|
226
|
+
* allowlist (lib/agentic-wallet/policy.ts FACILITATOR_ALLOWLIST) -- lowercased
|
|
227
|
+
* for case-insensitive match against tool_input.to / paymentChallenge.payTo.
|
|
228
|
+
*/
|
|
229
|
+
declare const DEFAULT_SAFETY_CONFIG: SafetyConfig;
|
|
230
|
+
declare function loadSafetyConfig(): Promise<SafetyConfig>;
|
|
231
|
+
declare function validateAndMerge(partial: Partial<SafetyConfig>): SafetyConfig;
|
|
232
|
+
declare function getSafetyConfigPath(): string;
|
|
233
|
+
|
|
234
|
+
type CreateHookOptions = {
|
|
235
|
+
/** Match against tool_name. Default: /keeperhub|wallet|sign/i */
|
|
236
|
+
toolNameMatcher?: (name: string) => boolean;
|
|
237
|
+
/** Injected for tests */
|
|
238
|
+
walletLoader?: () => Promise<WalletConfig>;
|
|
239
|
+
/** Injected for tests */
|
|
240
|
+
configLoader?: () => Promise<SafetyConfig>;
|
|
241
|
+
/** Injected for tests */
|
|
242
|
+
clientFactory?: (w: WalletConfig) => KeeperHubClient;
|
|
243
|
+
/**
|
|
244
|
+
* Called when the ask tier opens an approval URL. Default: write to stderr
|
|
245
|
+
* (stdout is reserved for the Claude Code hook JSON output).
|
|
246
|
+
*/
|
|
247
|
+
onAskOpen?: (url: string) => void;
|
|
248
|
+
/** Polling config for the ask tier */
|
|
249
|
+
poll?: {
|
|
250
|
+
intervalMs: number;
|
|
251
|
+
maxAttempts: number;
|
|
252
|
+
};
|
|
253
|
+
};
|
|
254
|
+
/**
|
|
255
|
+
* Factory returning the PreToolUse hook function. The hook enforces the three
|
|
256
|
+
* client-side safety tiers (auto / ask / block) sourced EXCLUSIVELY from
|
|
257
|
+
* ~/.keeperhub/safety.json -- never from the tool payload (GUARD-05).
|
|
258
|
+
*/
|
|
259
|
+
declare function createPreToolUseHook(options?: CreateHookOptions): Promise<(input: unknown) => Promise<HookDecision>>;
|
|
260
|
+
|
|
261
|
+
type MppChallenge = {
|
|
262
|
+
serialized: string;
|
|
263
|
+
};
|
|
264
|
+
declare function parseMppChallenge(response: Response): MppChallenge | null;
|
|
265
|
+
|
|
266
|
+
type PaySignerOptions = {
|
|
267
|
+
/** Override wallet loader (primarily for tests). */
|
|
268
|
+
walletLoader?: () => Promise<WalletConfig>;
|
|
269
|
+
/** Override KeeperHubClient factory (tests inject a mocked fetch). */
|
|
270
|
+
clientFactory?: (wallet: WalletConfig) => KeeperHubClient;
|
|
271
|
+
/** Replayed fetch (tests intercept the retry). */
|
|
272
|
+
fetchImpl?: typeof fetch;
|
|
273
|
+
/** Approval polling override: interval + max attempts. */
|
|
274
|
+
approval?: {
|
|
275
|
+
intervalMs: number;
|
|
276
|
+
maxAttempts: number;
|
|
277
|
+
};
|
|
278
|
+
};
|
|
279
|
+
type PaymentSigner = {
|
|
280
|
+
pay: (response: Response) => Promise<Response>;
|
|
281
|
+
};
|
|
282
|
+
declare function createPaymentSigner(opts?: PaySignerOptions): PaymentSigner;
|
|
283
|
+
declare const paymentSigner: PaymentSigner;
|
|
284
|
+
|
|
285
|
+
type InstallResult = {
|
|
286
|
+
skillWrites: Array<{
|
|
287
|
+
agent: string;
|
|
288
|
+
path: string;
|
|
289
|
+
status: "written" | "skipped";
|
|
290
|
+
}>;
|
|
291
|
+
hookRegistrations: Array<{
|
|
292
|
+
agent: string;
|
|
293
|
+
status: "registered" | "notice" | "skipped";
|
|
294
|
+
message?: string;
|
|
295
|
+
}>;
|
|
296
|
+
};
|
|
297
|
+
type InstallOptions = {
|
|
298
|
+
homeOverride?: string;
|
|
299
|
+
skillSourcePath?: string;
|
|
300
|
+
onNotice?: (msg: string) => void;
|
|
301
|
+
};
|
|
302
|
+
declare function registerClaudeCodeHook(settingsPath: string): Promise<void>;
|
|
303
|
+
declare function installSkill(options?: InstallOptions): Promise<InstallResult>;
|
|
304
|
+
|
|
305
|
+
declare function readWalletConfig(): Promise<WalletConfig>;
|
|
306
|
+
declare function writeWalletConfig(config: WalletConfig): Promise<void>;
|
|
307
|
+
declare function getWalletConfigPath(): string;
|
|
308
|
+
|
|
309
|
+
type X402Challenge = {
|
|
310
|
+
x402Version: 2;
|
|
311
|
+
accepts: Array<{
|
|
312
|
+
scheme: "exact";
|
|
313
|
+
network: string;
|
|
314
|
+
asset: string;
|
|
315
|
+
amount: string;
|
|
316
|
+
payTo: string;
|
|
317
|
+
maxTimeoutSeconds: number;
|
|
318
|
+
extra: Record<string, unknown>;
|
|
319
|
+
}>;
|
|
320
|
+
resource: {
|
|
321
|
+
url: string;
|
|
322
|
+
description: string;
|
|
323
|
+
mimeType: string;
|
|
324
|
+
};
|
|
325
|
+
};
|
|
326
|
+
declare function parseX402Challenge(response: Response): Promise<X402Challenge | null>;
|
|
327
|
+
|
|
328
|
+
export { type AgentTarget, type AskTierResponse, BASE_USDC, type BalanceSnapshot, type CheckBalanceOptions, type ClientOptions, type CreateHookOptions, DEFAULT_SAFETY_CONFIG, type FundInstructions, type HmacHeaders, type HookDecision, type InstallOptions, type InstallResult, KeeperHubClient, KeeperHubError, type MppChallenge, type PaymentSigner, type SafetyConfig, TEMPO_USDC_E, type WalletConfig, WalletConfigMissingError, type X402Challenge, buildHmacHeaders, checkBalance, computeSignature, createPaymentSigner, createPreToolUseHook, detectAgents, fund, getSafetyConfigPath, getWalletConfigPath, installSkill, loadSafetyConfig, parseMppChallenge, parseX402Challenge, paymentSigner, readWalletConfig, registerClaudeCodeHook, tempo, validateAndMerge, writeWalletConfig };
|