@keeperhub/wallet 0.1.7 → 0.1.9

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.cjs CHANGED
@@ -43,8 +43,10 @@ __export(index_exports, {
43
43
  paymentSigner: () => paymentSigner,
44
44
  readWalletConfig: () => readWalletConfig,
45
45
  registerClaudeCodeHook: () => registerClaudeCodeHook,
46
+ resolveHookCommand: () => resolveHookCommand,
46
47
  runCli: () => runCli,
47
48
  runHookCli: () => runHookCli,
49
+ selectProtocol: () => selectProtocol,
48
50
  tempo: () => tempo,
49
51
  validateAndMerge: () => validateAndMerge,
50
52
  writeWalletConfig: () => writeWalletConfig
@@ -195,15 +197,69 @@ function fund(walletAddress) {
195
197
  }
196
198
 
197
199
  // src/skill-install.ts
200
+ var import_node_child_process = require("child_process");
198
201
  var import_promises = require("fs/promises");
202
+ var import_node_fs2 = require("fs");
199
203
  var import_node_path2 = require("path");
200
204
  var import_node_url = require("url");
201
- var HOOK_COMMAND = "keeperhub-wallet-hook";
202
- var KEEPERHUB_HOOK_MARKER = "keeperhub-wallet-hook";
203
- function buildKeeperhubEntry() {
205
+ var HOOK_BIN = "keeperhub-wallet-hook";
206
+ var HOOK_COMMAND_BARE = HOOK_BIN;
207
+ var PACKAGE_NAME = "@keeperhub/wallet";
208
+ function readPackageVersion() {
209
+ try {
210
+ const here = (0, import_node_path2.dirname)((0, import_node_url.fileURLToPath)(__filename));
211
+ const pkgPath = (0, import_node_path2.join)(here, "..", "package.json");
212
+ const raw = (0, import_node_fs2.readFileSync)(pkgPath, "utf-8");
213
+ const parsed = JSON.parse(raw);
214
+ if (typeof parsed.version === "string" && parsed.version.length > 0) {
215
+ return parsed.version;
216
+ }
217
+ } catch {
218
+ }
219
+ return "latest";
220
+ }
221
+ function buildNpxCommand(version) {
222
+ return `npx -y -p ${PACKAGE_NAME}@${version} ${HOOK_BIN}`;
223
+ }
224
+ var KEEPERHUB_HOOK_MARKER = HOOK_BIN;
225
+ function filterKeeperhubHooksFromEntry(entry) {
226
+ if (typeof entry !== "object" || entry === null) {
227
+ return entry;
228
+ }
229
+ const candidate = entry;
230
+ if (!Array.isArray(candidate.hooks)) {
231
+ return entry;
232
+ }
233
+ const survivors = candidate.hooks.filter((h) => {
234
+ const cmd = h?.command;
235
+ return !(typeof cmd === "string" && cmd.includes(KEEPERHUB_HOOK_MARKER));
236
+ });
237
+ if (survivors.length === candidate.hooks.length) {
238
+ return entry;
239
+ }
240
+ if (survivors.length === 0) {
241
+ return null;
242
+ }
243
+ return { ...candidate, hooks: survivors };
244
+ }
245
+ function resolveHookCommand() {
246
+ const envOverride = process.env.KEEPERHUB_WALLET_HOOK_COMMAND;
247
+ if (envOverride && envOverride.length > 0) {
248
+ return envOverride;
249
+ }
250
+ try {
251
+ (0, import_node_child_process.execFileSync)("/bin/sh", ["-c", `command -v ${HOOK_BIN}`], {
252
+ stdio: "ignore"
253
+ });
254
+ return HOOK_COMMAND_BARE;
255
+ } catch {
256
+ return buildNpxCommand(readPackageVersion());
257
+ }
258
+ }
259
+ function buildKeeperhubEntry(command) {
204
260
  return {
205
261
  matcher: "*",
206
- hooks: [{ type: "command", command: HOOK_COMMAND }]
262
+ hooks: [{ type: "command", command }]
207
263
  };
208
264
  }
209
265
  function resolveDefaultSkillSource() {
@@ -214,7 +270,8 @@ function defaultNotice(msg) {
214
270
  process.stderr.write(`${msg}
215
271
  `);
216
272
  }
217
- async function registerClaudeCodeHook(settingsPath) {
273
+ async function registerClaudeCodeHook(settingsPath, options = {}) {
274
+ const command = options.hookCommand ?? resolveHookCommand();
218
275
  let raw = null;
219
276
  try {
220
277
  raw = await (0, import_promises.readFile)(settingsPath, "utf-8");
@@ -237,12 +294,12 @@ async function registerClaudeCodeHook(settingsPath) {
237
294
  const existingPreToolUse = Array.isArray(hooks.PreToolUse) ? hooks.PreToolUse : [];
238
295
  const filtered = [];
239
296
  for (const entry of existingPreToolUse) {
240
- const serialised = JSON.stringify(entry);
241
- if (!serialised.includes(KEEPERHUB_HOOK_MARKER)) {
242
- filtered.push(entry);
297
+ const survivor = filterKeeperhubHooksFromEntry(entry);
298
+ if (survivor !== null) {
299
+ filtered.push(survivor);
243
300
  }
244
301
  }
245
- filtered.push(buildKeeperhubEntry());
302
+ filtered.push(buildKeeperhubEntry(command));
246
303
  hooks.PreToolUse = filtered;
247
304
  config.hooks = hooks;
248
305
  await (0, import_promises.mkdir)((0, import_node_path2.dirname)(settingsPath), { recursive: true, mode: 448 });
@@ -258,26 +315,27 @@ async function writeSkillToAgent(agent, skillSource) {
258
315
  await (0, import_promises.chmod)(target, 420);
259
316
  return { agent: agent.agent, path: target, status: "written" };
260
317
  }
261
- function buildNoticeMessage(agent) {
262
- 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}`;
318
+ function buildNoticeMessage(agent, command) {
319
+ return `${agent.agent} does not support auto-registered PreToolUse hooks; run \`${command}\` on every tool use via ${agent.agent}'s settings file at ${agent.settingsFile}`;
263
320
  }
264
321
  async function installSkill(options = {}) {
265
322
  const agents = detectAgents(options.homeOverride);
266
323
  const skillSource = options.skillSourcePath ?? resolveDefaultSkillSource();
267
324
  const onNotice = options.onNotice ?? defaultNotice;
325
+ const hookCommand = options.hookCommand ?? resolveHookCommand();
268
326
  const skillWrites = [];
269
327
  const hookRegistrations = [];
270
328
  for (const agent of agents) {
271
329
  const write = await writeSkillToAgent(agent, skillSource);
272
330
  skillWrites.push(write);
273
331
  if (agent.hookSupport === "claude-code") {
274
- await registerClaudeCodeHook(agent.settingsFile);
332
+ await registerClaudeCodeHook(agent.settingsFile, { hookCommand });
275
333
  hookRegistrations.push({
276
334
  agent: agent.agent,
277
335
  status: "registered"
278
336
  });
279
337
  } else {
280
- const message = buildNoticeMessage(agent);
338
+ const message = buildNoticeMessage(agent, hookCommand);
281
339
  hookRegistrations.push({
282
340
  agent: agent.agent,
283
341
  status: "notice",
@@ -685,6 +743,26 @@ var DEFAULT_TOOL_RE = /keeperhub|wallet|sign/i;
685
743
  function defaultToolMatcher(name) {
686
744
  return DEFAULT_TOOL_RE.test(name);
687
745
  }
746
+ function hasPaymentShape(input) {
747
+ const ti = input.tool_input ?? {};
748
+ const challenge = ti.paymentChallenge;
749
+ if (challenge !== void 0 && challenge !== null) {
750
+ return true;
751
+ }
752
+ if (ti.amount !== void 0 && ti.amount !== null) {
753
+ return true;
754
+ }
755
+ if (ti.unit !== void 0 && ti.unit !== null) {
756
+ return true;
757
+ }
758
+ for (const field of ["to", "contract", "assetAddress"]) {
759
+ const v = ti[field];
760
+ if (typeof v === "string" && ADDRESS_RE.test(v)) {
761
+ return true;
762
+ }
763
+ }
764
+ return false;
765
+ }
688
766
  function extractAmountMicroUsdc(input) {
689
767
  const ti = input.tool_input ?? {};
690
768
  const challenge = ti.paymentChallenge ?? {};
@@ -734,6 +812,9 @@ async function createPreToolUseHook(options = {}) {
734
812
  if (!(typeof hookInput.tool_name === "string" && toolMatcher(hookInput.tool_name))) {
735
813
  return { decision: "allow" };
736
814
  }
815
+ if (!hasPaymentShape(hookInput)) {
816
+ return { decision: "allow" };
817
+ }
737
818
  const contractAddr = extractContractAddress(hookInput);
738
819
  const amountMicro = extractAmountMicroUsdc(hookInput);
739
820
  if (contractAddr && !safety.allowlisted_contracts.includes(contractAddr)) {
@@ -866,6 +947,30 @@ var NONCE_BYTES = 32;
866
947
  async function sleep(ms) {
867
948
  await new Promise((resolve) => setTimeout(resolve, ms));
868
949
  }
950
+ function selectProtocol(x402, mpp, hint) {
951
+ const h = hint ?? "auto";
952
+ if (h === "x402") {
953
+ if (!x402) {
954
+ throw new KeeperHubError(
955
+ "X402_NOT_OFFERED",
956
+ "x402 is not offered by this endpoint"
957
+ );
958
+ }
959
+ return "x402";
960
+ }
961
+ if (h === "mpp") {
962
+ if (!mpp) {
963
+ throw new KeeperHubError(
964
+ "MPP_NOT_OFFERED",
965
+ "mpp is not offered by this endpoint"
966
+ );
967
+ }
968
+ return "mpp";
969
+ }
970
+ if (x402) return "x402";
971
+ if (mpp) return "mpp";
972
+ return null;
973
+ }
869
974
  function createPaymentSigner(opts = {}) {
870
975
  const fetchImpl = opts.fetchImpl ?? globalThis.fetch;
871
976
  const walletLoader = opts.walletLoader ?? readWalletConfig;
@@ -1008,12 +1113,13 @@ function createPaymentSigner(opts = {}) {
1008
1113
  return response;
1009
1114
  }
1010
1115
  const wallet = await walletLoader();
1011
- if (mpp) {
1012
- return payViaMpp(response, mpp, wallet, options);
1013
- }
1014
- if (x402) {
1116
+ const protocol = selectProtocol(x402, mpp, options?.paymentHint);
1117
+ if (protocol === "x402") {
1015
1118
  return payViaX402(response, x402, wallet, options);
1016
1119
  }
1120
+ if (protocol === "mpp") {
1121
+ return payViaMpp(response, mpp, wallet, options);
1122
+ }
1017
1123
  return response;
1018
1124
  }
1019
1125
  return {
@@ -1026,7 +1132,8 @@ function createPaymentSigner(opts = {}) {
1026
1132
  return pay(first, {
1027
1133
  body: init?.body ?? void 0,
1028
1134
  headers: init?.headers,
1029
- method: init?.method
1135
+ method: init?.method,
1136
+ paymentHint: init?.paymentHint
1030
1137
  });
1031
1138
  }
1032
1139
  };
@@ -1057,8 +1164,10 @@ var paymentSigner = createPaymentSigner();
1057
1164
  paymentSigner,
1058
1165
  readWalletConfig,
1059
1166
  registerClaudeCodeHook,
1167
+ resolveHookCommand,
1060
1168
  runCli,
1061
1169
  runHookCli,
1170
+ selectProtocol,
1062
1171
  tempo,
1063
1172
  validateAndMerge,
1064
1173
  writeWalletConfig