@hpp-io/x402-mcp-bridge 0.0.1 → 0.0.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.
Files changed (78) hide show
  1. package/README.md +125 -77
  2. package/bin/hpp-x402.js +5 -0
  3. package/bin/x402-mcp-bridge.js +0 -0
  4. package/dist/a2a.d.ts +70 -0
  5. package/dist/a2a.js +210 -0
  6. package/dist/a2a.js.map +1 -0
  7. package/dist/autoTopup.d.ts +2 -1
  8. package/dist/autoTopup.js.map +1 -1
  9. package/dist/cli/channel.d.ts +1 -0
  10. package/dist/cli/channel.js +323 -0
  11. package/dist/cli/channel.js.map +1 -0
  12. package/dist/cli/hpp-x402.d.ts +2 -0
  13. package/dist/cli/hpp-x402.js +375 -0
  14. package/dist/cli/hpp-x402.js.map +1 -0
  15. package/dist/cli/install-claude-code.d.ts +24 -0
  16. package/dist/cli/install-claude-code.js +50 -0
  17. package/dist/cli/install-claude-code.js.map +1 -0
  18. package/dist/cli/install-claude.d.ts +2 -20
  19. package/dist/cli/install-claude.js +12 -63
  20. package/dist/cli/install-claude.js.map +1 -1
  21. package/dist/cli/install-cursor.d.ts +7 -0
  22. package/dist/cli/install-cursor.js +26 -0
  23. package/dist/cli/install-cursor.js.map +1 -0
  24. package/dist/cli/install-mcp-host.d.ts +35 -0
  25. package/dist/cli/install-mcp-host.js +82 -0
  26. package/dist/cli/install-mcp-host.js.map +1 -0
  27. package/dist/cli/install-openclaw.d.ts +7 -0
  28. package/dist/cli/install-openclaw.js +28 -0
  29. package/dist/cli/install-openclaw.js.map +1 -0
  30. package/dist/cli/install-windsurf.d.ts +7 -0
  31. package/dist/cli/install-windsurf.js +37 -0
  32. package/dist/cli/install-windsurf.js.map +1 -0
  33. package/dist/cli/policy.d.ts +1 -0
  34. package/dist/cli/policy.js +212 -0
  35. package/dist/cli/policy.js.map +1 -0
  36. package/dist/cli/revoke.d.ts +1 -1
  37. package/dist/cli/revoke.js +1 -5
  38. package/dist/cli/revoke.js.map +1 -1
  39. package/dist/cli/safe.d.ts +4172 -9580
  40. package/dist/cli/setup.d.ts +1 -1
  41. package/dist/cli/setup.js +24 -6
  42. package/dist/cli/setup.js.map +1 -1
  43. package/dist/client.d.ts +25 -10
  44. package/dist/client.js +139 -2
  45. package/dist/client.js.map +1 -1
  46. package/dist/config.d.ts +50 -11
  47. package/dist/config.js +50 -10
  48. package/dist/config.js.map +1 -1
  49. package/dist/discovery.d.ts +43 -0
  50. package/dist/discovery.js +62 -0
  51. package/dist/discovery.js.map +1 -0
  52. package/dist/discoveryTools.d.ts +75 -0
  53. package/dist/discoveryTools.js +101 -0
  54. package/dist/discoveryTools.js.map +1 -0
  55. package/dist/funds/direct-balance.d.ts +27 -0
  56. package/dist/funds/direct-balance.js +58 -0
  57. package/dist/funds/direct-balance.js.map +1 -0
  58. package/dist/funds.d.ts +26 -0
  59. package/dist/funds.js +2 -0
  60. package/dist/funds.js.map +1 -0
  61. package/dist/httpX402.d.ts +45 -0
  62. package/dist/httpX402.js +214 -0
  63. package/dist/httpX402.js.map +1 -0
  64. package/dist/index.js +66 -20
  65. package/dist/index.js.map +1 -1
  66. package/dist/keychain.d.ts +23 -0
  67. package/dist/keychain.js +111 -0
  68. package/dist/keychain.js.map +1 -0
  69. package/dist/policy.d.ts +73 -0
  70. package/dist/policy.js +249 -0
  71. package/dist/policy.js.map +1 -0
  72. package/dist/server.d.ts +18 -1
  73. package/dist/server.js +120 -10
  74. package/dist/server.js.map +1 -1
  75. package/docs/policy.md +101 -0
  76. package/package.json +11 -8
  77. package/bin/hpp-x402-safe-revoke.js +0 -5
  78. package/bin/hpp-x402-safe-setup.js +0 -5
package/dist/server.js CHANGED
@@ -14,25 +14,135 @@
14
14
  import { Server } from "@modelcontextprotocol/sdk/server/index.js";
15
15
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
16
16
  import { CallToolRequestSchema, ListToolsRequestSchema, } from "@modelcontextprotocol/sdk/types.js";
17
+ import { PAY_A2A_TOOL, payA2aAgent } from "./a2a.js";
18
+ import { X402_HTTP_TOOL, x402HttpCall } from "./httpX402.js";
19
+ import { HPP_DISCOVER_TOOL, HPP_CALL_TOOL, hppDiscover, hppCall, } from "./discoveryTools.js";
17
20
  import { log } from "./log.js";
18
21
  export async function startBridgeServer(opts) {
19
22
  const server = new Server({ name: opts.name, version: opts.version }, { capabilities: { tools: {} } });
20
- // ---- listTools — pass through from upstream -------------------------
23
+ // ---- listTools — upstream tools (if any) + local tools --------------
21
24
  server.setRequestHandler(ListToolsRequestSchema, async () => {
22
- const upstreamTools = await opts.upstream.base.listTools();
23
- log.debug("listTools", { count: upstreamTools.tools.length });
24
- return { tools: upstreamTools.tools };
25
+ const upstreamTools = opts.upstream ? await opts.upstream.base.listTools() : { tools: [] };
26
+ const localTools = opts.discovery
27
+ ? [PAY_A2A_TOOL, X402_HTTP_TOOL, HPP_DISCOVER_TOOL, HPP_CALL_TOOL]
28
+ : [PAY_A2A_TOOL, X402_HTTP_TOOL];
29
+ log.debug("listTools", { count: upstreamTools.tools.length + localTools.length });
30
+ return { tools: [...upstreamTools.tools, ...localTools] };
25
31
  });
26
32
  // ---- callTool — forward through x402-aware wrapper ------------------
27
- server.setRequestHandler(CallToolRequestSchema, async (req) => {
33
+ //
34
+ // Long-running tools: the upstream resource-server emits
35
+ // `notifications/progress` every ~25s while it inline-waits for an
36
+ // on-chain X402Delivered event. We forward each one to the host using
37
+ // the host's own progressToken (from extra._meta) so its idle timer
38
+ // resets and the request doesn't get cancelled at 60s. Without this,
39
+ // any compute taking longer than ~55s gets killed at the host's MCP
40
+ // client timeout, regardless of what the server eventually returns.
41
+ //
42
+ // `resetTimeoutOnProgress: true` on the upstream call applies the same
43
+ // protection on the bridge → resource-server hop. `timeout: 300_000`
44
+ // gives the upstream up to 5 minutes (well past anything we'd run
45
+ // inline) before bridge gives up.
46
+ server.setRequestHandler(CallToolRequestSchema, async (req, extra) => {
28
47
  const { name, arguments: args } = req.params;
29
48
  log.info("callTool.start", { name });
49
+ // Local tool: pay an external A2A agent (not an upstream MCP tool).
50
+ if (name === PAY_A2A_TOOL.name) {
51
+ try {
52
+ return await payA2aAgent({
53
+ signer: opts.signer,
54
+ network: opts.network,
55
+ funds: opts.funds,
56
+ rpcTimeoutMs: opts.a2aRpcTimeoutMs,
57
+ }, (args ?? {}));
58
+ }
59
+ catch (err) {
60
+ const msg = err instanceof Error ? err.message : String(err);
61
+ log.error("pay_a2a_agent.failed", { err: msg });
62
+ return { content: [{ type: "text", text: `pay_a2a_agent error: ${msg}` }], isError: true };
63
+ }
64
+ }
65
+ // Local tool: call an x402-protected HTTP endpoint (not an upstream MCP
66
+ // tool). Auth headers + spend limits come from local policy; payment is
67
+ // signed with the bridge's delegate wallet.
68
+ if (name === X402_HTTP_TOOL.name) {
69
+ try {
70
+ return await x402HttpCall({
71
+ signer: opts.signer,
72
+ network: opts.network,
73
+ funds: opts.funds,
74
+ }, (args ?? {}));
75
+ }
76
+ catch (err) {
77
+ const msg = err instanceof Error ? err.message : String(err);
78
+ log.error("x402_http_call.failed", { err: msg });
79
+ return { content: [{ type: "text", text: `x402_http_call error: ${msg}` }], isError: true };
80
+ }
81
+ }
82
+ // Local tool: discover curated x402 services (read-only, no payment).
83
+ if (opts.discovery && name === HPP_DISCOVER_TOOL.name) {
84
+ try {
85
+ return await hppDiscover(opts.discovery, (args ?? {}));
86
+ }
87
+ catch (err) {
88
+ const msg = err instanceof Error ? err.message : String(err);
89
+ log.error("hpp_discover.failed", { err: msg });
90
+ return { content: [{ type: "text", text: `hpp_discover error: ${msg}` }], isError: true };
91
+ }
92
+ }
93
+ // Local tool: call a discovered service by id (payment via our wallet).
94
+ if (opts.discovery && name === HPP_CALL_TOOL.name) {
95
+ try {
96
+ return await hppCall({ signer: opts.signer, network: opts.network, funds: opts.funds }, opts.discovery, (args ?? {}));
97
+ }
98
+ catch (err) {
99
+ const msg = err instanceof Error ? err.message : String(err);
100
+ log.error("hpp_call.failed", { err: msg });
101
+ return { content: [{ type: "text", text: `hpp_call error: ${msg}` }], isError: true };
102
+ }
103
+ }
104
+ // Beyond the local tools above, everything is an upstream resource-server
105
+ // tool. In local-tools-only mode there's no upstream to forward to.
106
+ if (!opts.upstream) {
107
+ return {
108
+ content: [{ type: "text", text: `unknown tool: ${name} (no upstream resource server configured)` }],
109
+ isError: true,
110
+ };
111
+ }
112
+ const up = opts.upstream;
113
+ const hostProgressToken = extra._meta?.progressToken;
30
114
  try {
31
- const result = await opts.upstream.x402.callTool(name, (args ?? {}));
32
- log.info("callTool.done", {
33
- name,
34
- paymentMade: result.paymentMade ?? false,
35
- });
115
+ // x402MCPClient's typed callTool options omit `onprogress`, but at
116
+ // runtime the underlying mcpClient.callTool receives the options
117
+ // object unchanged — so onprogress IS honoured by the SDK. Cast
118
+ // around the type gap; remove once @x402/mcp upstream broadens its
119
+ // option type.
120
+ const upstreamOptions = {
121
+ timeout: 300_000,
122
+ resetTimeoutOnProgress: true,
123
+ onprogress: hostProgressToken !== undefined
124
+ ? (p) => {
125
+ extra
126
+ .sendNotification({
127
+ method: "notifications/progress",
128
+ params: {
129
+ progressToken: hostProgressToken,
130
+ progress: p.progress,
131
+ total: p.total,
132
+ message: p.message,
133
+ },
134
+ })
135
+ .catch((err) => {
136
+ log.debug("progress.forwardFailed", {
137
+ err: err.message,
138
+ });
139
+ });
140
+ }
141
+ : undefined,
142
+ };
143
+ const result = await up.x402.callTool(name, (args ?? {}), upstreamOptions);
144
+ const paymentMade = result.paymentMade ?? false;
145
+ log.info("callTool.done", { name, paymentMade });
36
146
  // Strip bridge-internal fields (paymentMade, paymentResponse) before
37
147
  // returning to host — they're not part of the MCP CallToolResult shape.
38
148
  return {
@@ -1 +1 @@
1
- {"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AACH,OAAO,EAAE,MAAM,EAAE,MAAM,2CAA2C,CAAC;AACnE,OAAO,EAAE,oBAAoB,EAAE,MAAM,2CAA2C,CAAC;AACjF,OAAO,EACL,qBAAqB,EACrB,sBAAsB,GACvB,MAAM,oCAAoC,CAAC;AAG5C,OAAO,EAAE,GAAG,EAAE,MAAM,UAAU,CAAC;AAQ/B,MAAM,CAAC,KAAK,UAAU,iBAAiB,CAAC,IAAyB;IAC/D,MAAM,MAAM,GAAG,IAAI,MAAM,CACvB,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,OAAO,EAAE,IAAI,CAAC,OAAO,EAAE,EAC1C,EAAE,YAAY,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE,EAAE,CAChC,CAAC;IAEF,wEAAwE;IACxE,MAAM,CAAC,iBAAiB,CAAC,sBAAsB,EAAE,KAAK,IAAI,EAAE;QAC1D,MAAM,aAAa,GAAG,MAAM,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,SAAS,EAAE,CAAC;QAC3D,GAAG,CAAC,KAAK,CAAC,WAAW,EAAE,EAAE,KAAK,EAAE,aAAa,CAAC,KAAK,CAAC,MAAM,EAAE,CAAC,CAAC;QAC9D,OAAO,EAAE,KAAK,EAAE,aAAa,CAAC,KAAK,EAAE,CAAC;IACxC,CAAC,CAAC,CAAC;IAEH,wEAAwE;IACxE,MAAM,CAAC,iBAAiB,CAAC,qBAAqB,EAAE,KAAK,EAAE,GAAG,EAAE,EAAE;QAC5D,MAAM,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,GAAG,GAAG,CAAC,MAAM,CAAC;QAC7C,GAAG,CAAC,IAAI,CAAC,gBAAgB,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC;QAErC,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,QAAQ,CAC9C,IAAI,EACJ,CAAC,IAAI,IAAI,EAAE,CAA4B,CACxC,CAAC;YAEF,GAAG,CAAC,IAAI,CAAC,eAAe,EAAE;gBACxB,IAAI;gBACJ,WAAW,EAAG,MAAoC,CAAC,WAAW,IAAI,KAAK;aACxE,CAAC,CAAC;YAEH,qEAAqE;YACrE,wEAAwE;YACxE,OAAO;gBACL,OAAO,EAAE,MAAM,CAAC,OAAO;gBACvB,OAAO,EAAE,MAAM,CAAC,OAAO;aACxB,CAAC;QACJ,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,MAAM,GAAG,GAAG,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;YAC7D,GAAG,CAAC,KAAK,CAAC,iBAAiB,EAAE,EAAE,IAAI,EAAE,GAAG,EAAE,GAAG,EAAE,CAAC,CAAC;YACjD,OAAO;gBACL,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAe,EAAE,IAAI,EAAE,iBAAiB,GAAG,EAAE,EAAE,CAAC;gBAClE,OAAO,EAAE,IAAI;aACd,CAAC;QACJ,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,MAAM,SAAS,GAAG,IAAI,oBAAoB,EAAE,CAAC;IAC7C,MAAM,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;IAChC,GAAG,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC;AAC3B,CAAC"}
1
+ {"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"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"}
package/docs/policy.md ADDED
@@ -0,0 +1,101 @@
1
+ # Host policy (`policy.json`) — 레퍼런스
2
+
3
+ `x402_http_call` 도구가 읽는 **host별 인증 헤더 주입 + 지출 가드레일** 설정. 서비스마다 다른 부분을
4
+ 코드가 아닌 데이터로 두어, 새 서비스 추가가 한 줄이고 어떤 호스트도 하드코딩되지 않습니다.
5
+
6
+ ## 위치
7
+
8
+ - 기본: `~/.hpp-x402/policy.json` (디렉토리는 `$HPP_X402_HOME`로 변경 가능)
9
+ - override: `$HPP_X402_CRED_FILE` (파일 경로 직접 지정)
10
+ - bridge가 **매 도구 호출마다 읽음** → 수정 후 재시작 불필요
11
+ - 정확 경로 확인: `hpp-x402-policy path`
12
+
13
+ ## 스키마
14
+
15
+ ```jsonc
16
+ {
17
+ "_defaults": {
18
+ "allowUnlisted": false, // 미등록 host 허용? (기본 false = 거부 = allowlist)
19
+ "limits": { "requireHttps": true, "maxPerCallAtomic": "1000000" }
20
+ },
21
+ "<host>": { // 정확 일치(포트 포함, 소문자). wildcard 없음
22
+ "headers": { "<HeaderName>": "<value-source>" },
23
+ "limits": {
24
+ "requireHttps": true, // https 강제 (로컬 http면 false)
25
+ "maxPerCallAtomic": "5000000", // 1회 상한 (atomic, USDC.e 6dec → 5000000 = 5)
26
+ "cooldownMs": 300000, // 결제 간 최소 간격(ms) — 멱등(H2)
27
+ "maxPerDayAtomic": "20000000" // (파싱만, 일일 ledger는 후속)
28
+ }
29
+ }
30
+ }
31
+ ```
32
+
33
+ `host`는 `new URL(url).host`(소문자). 정책에 없고 `allowUnlisted=false`면 **결제 전 거부**.
34
+
35
+ ## value-source (헤더 값 출처)
36
+
37
+ 비밀을 파일에 직접 안 쓰고 참조로 둡니다. 호출 시 **로컬에서만** 해석 → 헤더로 주입(LLM/로그 미노출).
38
+ 해석 실패 시 **무인증 전송 없이 에러로 중단**.
39
+
40
+ | 형식 | 의미 |
41
+ |------|------|
42
+ | `file:<path>#<field>` | JSON 파일 필드 (`~` 확장, 중첩 `a.b.c`) |
43
+ | `env:<VAR>` | 환경변수 |
44
+ | `keychain://<svc>/<acct>` | OS 키체인 |
45
+ | `literal:<value>` | 인라인 평문 (**dev 전용**) |
46
+
47
+ ## 가드레일
48
+
49
+ | 키 | 효과 |
50
+ |----|------|
51
+ | `allowUnlisted` (_defaults) | false면 등록된 host만 호출 가능 (deny-by-default) |
52
+ | `requireHttps` | https 아닌 대상 거부 (로컬 http는 host별 false) |
53
+ | `maxPerCallAtomic` | 1회 결제 상한 — **서버가 준 402 금액**에 적용(악성 서버 방어) |
54
+ | `cooldownMs` | 직전 결제 후 이 시간 내 재결제는 직전 결과 반환(재결제 X) |
55
+
56
+ > 온체인 Safe AllowanceModule 일일 한도가 **최종 하드 상한**(정책 파일로 못 넘음).
57
+
58
+ ## CLI — `hpp-x402-policy`
59
+
60
+ 수동 JSON 편집 대신 사용 가능:
61
+
62
+ ```bash
63
+ hpp-x402-policy path # 파일 경로
64
+ hpp-x402-policy show # 전체 JSON
65
+ hpp-x402-policy list # host 요약
66
+
67
+ # host 추가/수정 (--header 반복 가능, --max-per-call은 USDC 단위 → atomic 자동 변환)
68
+ hpp-x402-policy set hub-stage.hpp.io \
69
+ --header X-Api-Key=file:~/.hpphub/config.json#api_key \
70
+ --max-per-call 5 --cooldown-ms 300000
71
+
72
+ hpp-x402-policy set localhost:4000 \
73
+ --header X-Api-Key=file:~/.hpphub/config.json#api_key \
74
+ --max-per-call 5 --https false # 로컬 http 허용
75
+
76
+ hpp-x402-policy unset <host> # host 삭제
77
+ hpp-x402-policy unset <host> --header NAME # 헤더 1개 삭제
78
+
79
+ hpp-x402-policy defaults --allow-unlisted false --max-per-call 1 --https true
80
+ ```
81
+
82
+ ## 예시 (완성형)
83
+
84
+ ```jsonc
85
+ {
86
+ "_defaults": { "allowUnlisted": false, "limits": { "requireHttps": true, "maxPerCallAtomic": "1000000" } },
87
+ "hub-stage.hpp.io": {
88
+ "headers": { "X-Api-Key": "file:~/.hpphub/config.json#api_key" },
89
+ "limits": { "requireHttps": true, "maxPerCallAtomic": "5000000", "cooldownMs": 300000 }
90
+ },
91
+ "localhost:4000": {
92
+ "headers": { "X-Api-Key": "file:~/.hpphub/config.json#api_key" },
93
+ "limits": { "requireHttps": false, "maxPerCallAtomic": "5000000", "cooldownMs": 300000 }
94
+ }
95
+ }
96
+ ```
97
+
98
+ ## 보안 메모
99
+
100
+ - 파일 퍼미션 0600 권장 (CLI는 0600으로 기록). `literal:` 평문은 dev 전용 — 운영은 `file:`/`keychain://`.
101
+ - value-source는 로컬에서만 해석, 결과는 **대상 host 요청 헤더로만** 전송. 로그/도구 args에 노출 안 됨.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hpp-io/x402-mcp-bridge",
3
- "version": "0.0.1",
3
+ "version": "0.0.3",
4
4
  "description": "MCP stdio bridge that lets Claude Desktop / OpenClaw / other MCP hosts make x402 payments on HPP — autoTopup from a Safe via AllowanceModule, signs EIP-3009 with a delegate EOA.",
5
5
  "license": "Apache-2.0",
6
6
  "type": "module",
@@ -8,12 +8,12 @@
8
8
  "types": "./dist/index.d.ts",
9
9
  "bin": {
10
10
  "x402-mcp-bridge": "bin/x402-mcp-bridge.js",
11
- "hpp-x402-safe-setup": "bin/hpp-x402-safe-setup.js",
12
- "hpp-x402-safe-revoke": "bin/hpp-x402-safe-revoke.js"
11
+ "hpp-x402": "bin/hpp-x402.js"
13
12
  },
14
13
  "files": [
15
14
  "dist",
16
15
  "bin",
16
+ "docs/policy.md",
17
17
  "README.md",
18
18
  "LICENSE"
19
19
  ],
@@ -24,22 +24,25 @@
24
24
  "build": "tsc",
25
25
  "dev": "tsx src/index.ts",
26
26
  "typecheck": "tsc --noEmit",
27
+ "postinstall": "patch-package || true",
27
28
  "prepublishOnly": "npm run typecheck && npm run build"
28
29
  },
29
30
  "publishConfig": {
30
- "access": "public",
31
- "provenance": true
31
+ "access": "public"
32
32
  },
33
33
  "dependencies": {
34
34
  "@modelcontextprotocol/sdk": "^1.0.0",
35
- "@x402/core": "^2.10.0",
36
- "@x402/evm": "^2.10.0",
37
- "@x402/mcp": "^2.10.0",
35
+ "@napi-rs/keyring": "^1.3.0",
36
+ "@x402/core": "^2.14.0",
37
+ "@x402/evm": "^2.14.0",
38
+ "@x402/mcp": "^2.14.0",
39
+ "commander": "^12.1.0",
38
40
  "viem": "^2.21.54",
39
41
  "zod": "^3.23.8"
40
42
  },
41
43
  "devDependencies": {
42
44
  "@types/node": "^20.0.0",
45
+ "patch-package": "^8.0.1",
43
46
  "tsx": "^4.0.0",
44
47
  "typescript": "^5.4.0"
45
48
  }
@@ -1,5 +0,0 @@
1
- #!/usr/bin/env node
2
- import("../dist/cli/revoke.js").catch((err) => {
3
- process.stderr.write(`hpp-x402-safe-revoke fatal: ${err?.message ?? err}\n`);
4
- process.exit(1);
5
- });
@@ -1,5 +0,0 @@
1
- #!/usr/bin/env node
2
- import("../dist/cli/setup.js").catch((err) => {
3
- process.stderr.write(`hpp-x402-safe-setup fatal: ${err?.message ?? err}\n`);
4
- process.exit(1);
5
- });