From 25cae2386fb1bafcf7e4c46034519e836b7b8807 Mon Sep 17 00:00:00 2001 From: Makabeez <155258247+Makabeez@users.noreply.github.com> Date: Thu, 6 Aug 2026 16:23:18 +0200 Subject: [PATCH 1/2] fix(wallet): use -p flag so npx resolves the correct binary MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit All kh wallet subcommands that wrap @keeperhub/wallet fail with: could not determine executable to run The package publishes three bins — keeperhub-wallet, keeperhub-wallet-hook, keeperhub-wallet-mcp — none named "wallet". Without an explicit binary name, npx cannot infer which to run and exits with the above error, regardless of whether Node.js is installed. Fix: pass -p @keeperhub/wallet keeperhub-wallet instead of @keeperhub/wallet, which tells npx the package to install and the exact binary to invoke. Found while using kh wallet fund / info during a first-run onboarding session on v0.13.1 (2026-08-06) — not from reading code. --- cmd/wallet/agentic_wrapper.go | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/cmd/wallet/agentic_wrapper.go b/cmd/wallet/agentic_wrapper.go index cc50c2c..7cfb14e 100644 --- a/cmd/wallet/agentic_wrapper.go +++ b/cmd/wallet/agentic_wrapper.go @@ -40,7 +40,12 @@ func runNpxWallet(f *cmdutil.Factory, cmd *cobra.Command, subcmd string, args [] host := cmdutil.ResolveHost(cmd, cfg) baseURL := khhttp.BuildBaseURL(host) - childArgs := append([]string{"@keeperhub/wallet", subcmd}, args...) + // Use -p to name the package explicitly and keeperhub-wallet to name the + // binary. Without -p, npx tries to infer the binary from the package name; + // @keeperhub/wallet exposes keeperhub-wallet / keeperhub-wallet-hook / + // keeperhub-wallet-mcp, so npx cannot pick one and exits "could not + // determine executable to run" regardless of whether Node is installed. + childArgs := append([]string{"-p", "@keeperhub/wallet", "keeperhub-wallet", subcmd}, args...) child := execCommand("npx", childArgs...) child.Stdin = f.IOStreams.In child.Stdout = f.IOStreams.Out From 6c6691558d851ff04a9355d47829b131ec2eb1bd Mon Sep 17 00:00:00 2001 From: Makabeez <155258247+Makabeez@users.noreply.github.com> Date: Fri, 7 Aug 2026 19:09:49 +0200 Subject: [PATCH 2/2] fix(wallet): update Long strings and add test for explicit npx binary - All agentic subcommand Long strings now show the correct invocation: npx -p @keeperhub/wallet keeperhub-wallet (previously npx @keeperhub/wallet , which fails with "could not determine executable to run" when the package exposes multiple bins) - Add agentic_wrapper_internal_test.go (package wallet) that overrides execCommand and lookPath to assert argv[0..2] = [-p, @keeperhub/wallet, keeperhub-wallet] without spawning a real process - Update TestNewAddCmd_Help assertion: "npx @keeperhub/wallet" no longer appears contiguously after the Long string fix; split into two checks for "@keeperhub/wallet" and "keeperhub-wallet" - Regenerate docs/kh_wallet*.md Co-Authored-By: Claude Sonnet 4.6 --- cmd/wallet/add.go | 2 +- cmd/wallet/agentic_wrapper_internal_test.go | 55 +++++++++++++++++++++ cmd/wallet/agentic_wrapper_test.go | 3 +- cmd/wallet/feedback.go | 2 +- cmd/wallet/fund.go | 2 +- cmd/wallet/info.go | 2 +- cmd/wallet/link.go | 2 +- cmd/wallet/wallet.go | 6 +-- docs/kh_wallet.md | 6 +-- docs/kh_wallet_add.md | 2 +- docs/kh_wallet_feedback.md | 2 +- docs/kh_wallet_fund.md | 2 +- docs/kh_wallet_info.md | 2 +- docs/kh_wallet_link.md | 2 +- 14 files changed, 73 insertions(+), 17 deletions(-) create mode 100644 cmd/wallet/agentic_wrapper_internal_test.go diff --git a/cmd/wallet/add.go b/cmd/wallet/add.go index 6deabb9..b03091b 100644 --- a/cmd/wallet/add.go +++ b/cmd/wallet/add.go @@ -13,7 +13,7 @@ func NewAddCmd(f *cmdutil.Factory) *cobra.Command { Short: "Provision a new agentic wallet (no KeeperHub account required)", Long: `Provision a new agentic wallet by calling POST /api/agentic-wallet/provision. -This is a thin wrapper around ` + "`npx @keeperhub/wallet add`" + ` -- the npm package is the +This is a thin wrapper around ` + "`npx -p @keeperhub/wallet keeperhub-wallet add`" + ` -- the npm package is the canonical tool. Writes {subOrgId, walletAddress, hmacSecret} to ~/.keeperhub/wallet.json (chmod 0o600) and prints subOrgId + walletAddress (hmacSecret is NEVER printed).`, Args: cobra.NoArgs, diff --git a/cmd/wallet/agentic_wrapper_internal_test.go b/cmd/wallet/agentic_wrapper_internal_test.go new file mode 100644 index 0000000..ea3e585 --- /dev/null +++ b/cmd/wallet/agentic_wrapper_internal_test.go @@ -0,0 +1,55 @@ +package wallet + +import ( + "os/exec" + "testing" + + "github.com/keeperhub/cli/internal/config" + khhttp "github.com/keeperhub/cli/internal/http" + "github.com/keeperhub/cli/pkg/cmdutil" + "github.com/keeperhub/cli/pkg/iostreams" + "github.com/spf13/cobra" +) + +func TestAgenticWrapperInvokesExplicitBinary(t *testing.T) { + origExec := execCommand + origLook := lookPath + t.Cleanup(func() { + execCommand = origExec + lookPath = origLook + }) + + lookPath = func(string) (string, error) { return "/usr/bin/npx", nil } + + var gotArgs []string + execCommand = func(name string, args ...string) *exec.Cmd { + gotArgs = args + return exec.Command("true") + } + + ios, _, _, _ := iostreams.Test() + f := &cmdutil.Factory{ + AppVersion: "1.0.0", + IOStreams: ios, + HTTPClient: func() (*khhttp.Client, error) { + return khhttp.NewClient(khhttp.ClientOptions{Host: "https://app.keeperhub.com", AppVersion: "1.0.0"}), nil + }, + Config: func() (config.Config, error) { + return config.Config{DefaultHost: "app.keeperhub.com"}, nil + }, + } + + _ = runNpxWallet(f, &cobra.Command{}, "info", nil) + + want := []string{"-p", "@keeperhub/wallet", "keeperhub-wallet"} + for i, w := range want { + if len(gotArgs) <= i || gotArgs[i] != w { + t.Fatalf("argv[%d] = %q, want %q (full argv: %v)", i, func() string { + if len(gotArgs) > i { + return gotArgs[i] + } + return "" + }(), w, gotArgs) + } + } +} diff --git a/cmd/wallet/agentic_wrapper_test.go b/cmd/wallet/agentic_wrapper_test.go index 20da8c2..3ea296f 100644 --- a/cmd/wallet/agentic_wrapper_test.go +++ b/cmd/wallet/agentic_wrapper_test.go @@ -39,7 +39,8 @@ func TestNewAddCmd_Help(t *testing.T) { require.NoError(t, err) out := outBuf.String() assert.Contains(t, out, "agentic wallet", "help should describe agentic wallet, not creator wallet") - assert.Contains(t, out, "npx @keeperhub/wallet", "help should reference the underlying npm package") + assert.Contains(t, out, "@keeperhub/wallet", "help should reference the underlying npm package") + assert.Contains(t, out, "keeperhub-wallet", "help should reference the explicit binary name") } func TestNewInfoCmd_Help(t *testing.T) { diff --git a/cmd/wallet/feedback.go b/cmd/wallet/feedback.go index 52e2da7..030fcd9 100644 --- a/cmd/wallet/feedback.go +++ b/cmd/wallet/feedback.go @@ -29,7 +29,7 @@ wallet paid for. Signs giveFeedback() via Turnkey and broadcasts on Ethereum mainnet via the KeeperHub server proxy. Caller wallet pays gas natively (~$0.05-2 per call at typical mainnet gas). -Thin wrapper around ` + "`npx @keeperhub/wallet feedback`" + `. Defaults to rating +Thin wrapper around ` + "`npx -p @keeperhub/wallet keeperhub-wallet feedback`" + `. Defaults to rating KeeperHub's own ERC-8004 agent (id 31875 on Ethereum); use --agent-id to rate any other agent.`, Example: ` # 5-star rating for an execution this wallet paid for diff --git a/cmd/wallet/fund.go b/cmd/wallet/fund.go index 3356550..6ec2c2b 100644 --- a/cmd/wallet/fund.go +++ b/cmd/wallet/fund.go @@ -13,7 +13,7 @@ func NewFundCmd(f *cmdutil.Factory) *cobra.Command { Short: "Print Coinbase Onramp URL (Base USDC) and Tempo deposit address for the agentic wallet", Long: `Print a Coinbase Onramp URL for Base USDC funding plus the Tempo deposit address. -Thin wrapper around ` + "`npx @keeperhub/wallet fund`" + `. No HTTP calls, no browser launch -- +Thin wrapper around ` + "`npx -p @keeperhub/wallet keeperhub-wallet fund`" + `. No HTTP calls, no browser launch -- prints copy-paste instructions only.`, Args: cobra.NoArgs, RunE: func(cmd *cobra.Command, _ []string) error { diff --git a/cmd/wallet/info.go b/cmd/wallet/info.go index fdbaa59..c0217f1 100644 --- a/cmd/wallet/info.go +++ b/cmd/wallet/info.go @@ -13,7 +13,7 @@ func NewInfoCmd(f *cmdutil.Factory) *cobra.Command { Short: "Print subOrgId and walletAddress from local agentic wallet config", Long: `Print subOrgId and walletAddress from ~/.keeperhub/wallet.json. -Thin wrapper around ` + "`npx @keeperhub/wallet info`" + `. Exits non-zero if the config is missing.`, +Thin wrapper around ` + "`npx -p @keeperhub/wallet keeperhub-wallet info`" + `. Exits non-zero if the config is missing.`, Args: cobra.NoArgs, RunE: func(cmd *cobra.Command, _ []string) error { return runNpxWallet(f, cmd, "info", nil) diff --git a/cmd/wallet/link.go b/cmd/wallet/link.go index 309b86e..7d6a252 100644 --- a/cmd/wallet/link.go +++ b/cmd/wallet/link.go @@ -18,7 +18,7 @@ func NewLinkCmd(f *cmdutil.Factory) *cobra.Command { Short: "Link the agentic wallet to a KeeperHub account (requires KH_SESSION_COOKIE)", Long: `Link the current agentic wallet to your KeeperHub account by calling POST /api/agentic-wallet/link. -Thin wrapper around ` + "`npx @keeperhub/wallet link`" + `. Requires the KH_SESSION_COOKIE env var +Thin wrapper around ` + "`npx -p @keeperhub/wallet keeperhub-wallet link`" + `. Requires the KH_SESSION_COOKIE env var set to a valid kh session cookie (sign in at app.keeperhub.com, copy the session cookie, export it). This command does not launch a browser session handshake; the env-var contract matches the npm CLI.`, diff --git a/cmd/wallet/wallet.go b/cmd/wallet/wallet.go index 6664f5d..922bdef 100644 --- a/cmd/wallet/wallet.go +++ b/cmd/wallet/wallet.go @@ -16,7 +16,7 @@ Creator wallet (REST): kh w balance show creator-wallet on-chain balances via KeeperHub REST API kh w tokens list supported tokens -Agentic wallet (thin wrappers around npx @keeperhub/wallet): +Agentic wallet (thin wrappers around npx -p @keeperhub/wallet keeperhub-wallet): kh w add provision a new agentic wallet (no account required) kh w info print agentic subOrgId + walletAddress kh w fund print Coinbase Onramp URL + Tempo deposit address @@ -28,8 +28,8 @@ Agentic wallet (thin wrappers around npx @keeperhub/wallet): # Provision an agentic wallet (npx wrapper): kh w add - # Check balance on the agentic wallet: - npx @keeperhub/wallet balance`, + # Check balance on the agentic wallet directly: + npx -p @keeperhub/wallet keeperhub-wallet balance`, } cmd.PersistentFlags().Bool("json", false, "Output as JSON") diff --git a/docs/kh_wallet.md b/docs/kh_wallet.md index 61940a8..41f9474 100644 --- a/docs/kh_wallet.md +++ b/docs/kh_wallet.md @@ -10,7 +10,7 @@ Creator wallet (REST): kh w balance show creator-wallet on-chain balances via KeeperHub REST API kh w tokens list supported tokens -Agentic wallet (thin wrappers around npx @keeperhub/wallet): +Agentic wallet (thin wrappers around npx -p @keeperhub/wallet keeperhub-wallet): kh w add provision a new agentic wallet (no account required) kh w info print agentic subOrgId + walletAddress kh w fund print Coinbase Onramp URL + Tempo deposit address @@ -26,8 +26,8 @@ Agentic wallet (thin wrappers around npx @keeperhub/wallet): # Provision an agentic wallet (npx wrapper): kh w add - # Check balance on the agentic wallet: - npx @keeperhub/wallet balance + # Check balance on the agentic wallet directly: + npx -p @keeperhub/wallet keeperhub-wallet balance ``` ### Options diff --git a/docs/kh_wallet_add.md b/docs/kh_wallet_add.md index 2663c21..cecb1a1 100644 --- a/docs/kh_wallet_add.md +++ b/docs/kh_wallet_add.md @@ -6,7 +6,7 @@ Provision a new agentic wallet (no KeeperHub account required) Provision a new agentic wallet by calling POST /api/agentic-wallet/provision. -This is a thin wrapper around `npx @keeperhub/wallet add` -- the npm package is the +This is a thin wrapper around `npx -p @keeperhub/wallet keeperhub-wallet add` -- the npm package is the canonical tool. Writes {subOrgId, walletAddress, hmacSecret} to ~/.keeperhub/wallet.json (chmod 0o600) and prints subOrgId + walletAddress (hmacSecret is NEVER printed). diff --git a/docs/kh_wallet_feedback.md b/docs/kh_wallet_feedback.md index 4fcb6ce..ee34fa8 100644 --- a/docs/kh_wallet_feedback.md +++ b/docs/kh_wallet_feedback.md @@ -9,7 +9,7 @@ wallet paid for. Signs giveFeedback() via Turnkey and broadcasts on Ethereum mainnet via the KeeperHub server proxy. Caller wallet pays gas natively (~$0.05-2 per call at typical mainnet gas). -Thin wrapper around `npx @keeperhub/wallet feedback`. Defaults to rating +Thin wrapper around `npx -p @keeperhub/wallet keeperhub-wallet feedback`. Defaults to rating KeeperHub's own ERC-8004 agent (id 31875 on Ethereum); use --agent-id to rate any other agent. diff --git a/docs/kh_wallet_fund.md b/docs/kh_wallet_fund.md index 4d5fcef..01edf31 100644 --- a/docs/kh_wallet_fund.md +++ b/docs/kh_wallet_fund.md @@ -6,7 +6,7 @@ Print Coinbase Onramp URL (Base USDC) and Tempo deposit address for the agentic Print a Coinbase Onramp URL for Base USDC funding plus the Tempo deposit address. -Thin wrapper around `npx @keeperhub/wallet fund`. No HTTP calls, no browser launch -- +Thin wrapper around `npx -p @keeperhub/wallet keeperhub-wallet fund`. No HTTP calls, no browser launch -- prints copy-paste instructions only. ``` diff --git a/docs/kh_wallet_info.md b/docs/kh_wallet_info.md index 8ab937f..e10433f 100644 --- a/docs/kh_wallet_info.md +++ b/docs/kh_wallet_info.md @@ -6,7 +6,7 @@ Print subOrgId and walletAddress from local agentic wallet config Print subOrgId and walletAddress from ~/.keeperhub/wallet.json. -Thin wrapper around `npx @keeperhub/wallet info`. Exits non-zero if the config is missing. +Thin wrapper around `npx -p @keeperhub/wallet keeperhub-wallet info`. Exits non-zero if the config is missing. ``` kh wallet info [flags] diff --git a/docs/kh_wallet_link.md b/docs/kh_wallet_link.md index 9356a7f..fc24f01 100644 --- a/docs/kh_wallet_link.md +++ b/docs/kh_wallet_link.md @@ -6,7 +6,7 @@ Link the agentic wallet to a KeeperHub account (requires KH_SESSION_COOKIE) Link the current agentic wallet to your KeeperHub account by calling POST /api/agentic-wallet/link. -Thin wrapper around `npx @keeperhub/wallet link`. Requires the KH_SESSION_COOKIE env var +Thin wrapper around `npx -p @keeperhub/wallet keeperhub-wallet link`. Requires the KH_SESSION_COOKIE env var set to a valid kh session cookie (sign in at app.keeperhub.com, copy the session cookie, export it). This command does not launch a browser session handshake; the env-var contract matches the npm CLI.