@keeperhub/wallet 0.1.8 → 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.js CHANGED
@@ -147,15 +147,69 @@ function fund(walletAddress) {
147
147
  }
148
148
 
149
149
  // src/skill-install.ts
150
+ import { execFileSync } from "child_process";
150
151
  import { chmod, copyFile, mkdir, readFile, writeFile } from "fs/promises";
152
+ import { readFileSync } from "fs";
151
153
  import { dirname as dirname2, join as join2 } from "path";
152
154
  import { fileURLToPath } from "url";
153
- var HOOK_COMMAND = "keeperhub-wallet-hook";
154
- var KEEPERHUB_HOOK_MARKER = "keeperhub-wallet-hook";
155
- function buildKeeperhubEntry() {
155
+ var HOOK_BIN = "keeperhub-wallet-hook";
156
+ var HOOK_COMMAND_BARE = HOOK_BIN;
157
+ var PACKAGE_NAME = "@keeperhub/wallet";
158
+ function readPackageVersion() {
159
+ try {
160
+ const here = dirname2(fileURLToPath(import.meta.url));
161
+ const pkgPath = join2(here, "..", "package.json");
162
+ const raw = readFileSync(pkgPath, "utf-8");
163
+ const parsed = JSON.parse(raw);
164
+ if (typeof parsed.version === "string" && parsed.version.length > 0) {
165
+ return parsed.version;
166
+ }
167
+ } catch {
168
+ }
169
+ return "latest";
170
+ }
171
+ function buildNpxCommand(version) {
172
+ return `npx -y -p ${PACKAGE_NAME}@${version} ${HOOK_BIN}`;
173
+ }
174
+ var KEEPERHUB_HOOK_MARKER = HOOK_BIN;
175
+ function filterKeeperhubHooksFromEntry(entry) {
176
+ if (typeof entry !== "object" || entry === null) {
177
+ return entry;
178
+ }
179
+ const candidate = entry;
180
+ if (!Array.isArray(candidate.hooks)) {
181
+ return entry;
182
+ }
183
+ const survivors = candidate.hooks.filter((h) => {
184
+ const cmd = h?.command;
185
+ return !(typeof cmd === "string" && cmd.includes(KEEPERHUB_HOOK_MARKER));
186
+ });
187
+ if (survivors.length === candidate.hooks.length) {
188
+ return entry;
189
+ }
190
+ if (survivors.length === 0) {
191
+ return null;
192
+ }
193
+ return { ...candidate, hooks: survivors };
194
+ }
195
+ function resolveHookCommand() {
196
+ const envOverride = process.env.KEEPERHUB_WALLET_HOOK_COMMAND;
197
+ if (envOverride && envOverride.length > 0) {
198
+ return envOverride;
199
+ }
200
+ try {
201
+ execFileSync("/bin/sh", ["-c", `command -v ${HOOK_BIN}`], {
202
+ stdio: "ignore"
203
+ });
204
+ return HOOK_COMMAND_BARE;
205
+ } catch {
206
+ return buildNpxCommand(readPackageVersion());
207
+ }
208
+ }
209
+ function buildKeeperhubEntry(command) {
156
210
  return {
157
211
  matcher: "*",
158
- hooks: [{ type: "command", command: HOOK_COMMAND }]
212
+ hooks: [{ type: "command", command }]
159
213
  };
160
214
  }
161
215
  function resolveDefaultSkillSource() {
@@ -166,7 +220,8 @@ function defaultNotice(msg) {
166
220
  process.stderr.write(`${msg}
167
221
  `);
168
222
  }
169
- async function registerClaudeCodeHook(settingsPath) {
223
+ async function registerClaudeCodeHook(settingsPath, options = {}) {
224
+ const command = options.hookCommand ?? resolveHookCommand();
170
225
  let raw = null;
171
226
  try {
172
227
  raw = await readFile(settingsPath, "utf-8");
@@ -189,12 +244,12 @@ async function registerClaudeCodeHook(settingsPath) {
189
244
  const existingPreToolUse = Array.isArray(hooks.PreToolUse) ? hooks.PreToolUse : [];
190
245
  const filtered = [];
191
246
  for (const entry of existingPreToolUse) {
192
- const serialised = JSON.stringify(entry);
193
- if (!serialised.includes(KEEPERHUB_HOOK_MARKER)) {
194
- filtered.push(entry);
247
+ const survivor = filterKeeperhubHooksFromEntry(entry);
248
+ if (survivor !== null) {
249
+ filtered.push(survivor);
195
250
  }
196
251
  }
197
- filtered.push(buildKeeperhubEntry());
252
+ filtered.push(buildKeeperhubEntry(command));
198
253
  hooks.PreToolUse = filtered;
199
254
  config.hooks = hooks;
200
255
  await mkdir(dirname2(settingsPath), { recursive: true, mode: 448 });
@@ -210,26 +265,27 @@ async function writeSkillToAgent(agent, skillSource) {
210
265
  await chmod(target, 420);
211
266
  return { agent: agent.agent, path: target, status: "written" };
212
267
  }
213
- function buildNoticeMessage(agent) {
214
- 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}`;
268
+ function buildNoticeMessage(agent, command) {
269
+ 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}`;
215
270
  }
216
271
  async function installSkill(options = {}) {
217
272
  const agents = detectAgents(options.homeOverride);
218
273
  const skillSource = options.skillSourcePath ?? resolveDefaultSkillSource();
219
274
  const onNotice = options.onNotice ?? defaultNotice;
275
+ const hookCommand = options.hookCommand ?? resolveHookCommand();
220
276
  const skillWrites = [];
221
277
  const hookRegistrations = [];
222
278
  for (const agent of agents) {
223
279
  const write = await writeSkillToAgent(agent, skillSource);
224
280
  skillWrites.push(write);
225
281
  if (agent.hookSupport === "claude-code") {
226
- await registerClaudeCodeHook(agent.settingsFile);
282
+ await registerClaudeCodeHook(agent.settingsFile, { hookCommand });
227
283
  hookRegistrations.push({
228
284
  agent: agent.agent,
229
285
  status: "registered"
230
286
  });
231
287
  } else {
232
- const message = buildNoticeMessage(agent);
288
+ const message = buildNoticeMessage(agent, hookCommand);
233
289
  hookRegistrations.push({
234
290
  agent: agent.agent,
235
291
  status: "notice",
@@ -637,6 +693,26 @@ var DEFAULT_TOOL_RE = /keeperhub|wallet|sign/i;
637
693
  function defaultToolMatcher(name) {
638
694
  return DEFAULT_TOOL_RE.test(name);
639
695
  }
696
+ function hasPaymentShape(input) {
697
+ const ti = input.tool_input ?? {};
698
+ const challenge = ti.paymentChallenge;
699
+ if (challenge !== void 0 && challenge !== null) {
700
+ return true;
701
+ }
702
+ if (ti.amount !== void 0 && ti.amount !== null) {
703
+ return true;
704
+ }
705
+ if (ti.unit !== void 0 && ti.unit !== null) {
706
+ return true;
707
+ }
708
+ for (const field of ["to", "contract", "assetAddress"]) {
709
+ const v = ti[field];
710
+ if (typeof v === "string" && ADDRESS_RE.test(v)) {
711
+ return true;
712
+ }
713
+ }
714
+ return false;
715
+ }
640
716
  function extractAmountMicroUsdc(input) {
641
717
  const ti = input.tool_input ?? {};
642
718
  const challenge = ti.paymentChallenge ?? {};
@@ -686,6 +762,9 @@ async function createPreToolUseHook(options = {}) {
686
762
  if (!(typeof hookInput.tool_name === "string" && toolMatcher(hookInput.tool_name))) {
687
763
  return { decision: "allow" };
688
764
  }
765
+ if (!hasPaymentShape(hookInput)) {
766
+ return { decision: "allow" };
767
+ }
689
768
  const contractAddr = extractContractAddress(hookInput);
690
769
  const amountMicro = extractAmountMicroUsdc(hookInput);
691
770
  if (contractAddr && !safety.allowlisted_contracts.includes(contractAddr)) {
@@ -1034,6 +1113,7 @@ export {
1034
1113
  paymentSigner,
1035
1114
  readWalletConfig,
1036
1115
  registerClaudeCodeHook,
1116
+ resolveHookCommand,
1037
1117
  runCli,
1038
1118
  runHookCli,
1039
1119
  selectProtocol,