nodpay 0.2.15 → 0.2.17

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/SKILL.md CHANGED
@@ -1,6 +1,17 @@
1
1
  ---
2
2
  name: nodpay
3
3
  description: Propose on-chain payments from a shared wallet. Use when user asks to send crypto, make a payment, or create a shared wallet.
4
+ metadata:
5
+ {
6
+ "openclaw":
7
+ {
8
+ "requires": { "bins": ["npx", "curl"] },
9
+ "install": [{ "id": "node", "kind": "node", "package": "nodpay", "bins": ["npx"], "label": "Install NodPay CLI (npm)" }]
10
+ },
11
+ "credentials": "Agent signing key stored in .nodpay/.env (generated by npx nodpay keygen, never exposed to agent context)",
12
+ "persistence": [".nodpay/.env (agent key, chmod 600)", ".nodpay/wallets/*.json (wallet info, public key material)"],
13
+ "network": ["nodpay.ai (op-store relay + wallet creation UI)", "Public RPC endpoints via --chain"]
14
+ }
4
15
  ---
5
16
 
6
17
  # NodPay — Agent Wallet
@@ -15,9 +26,9 @@ You propose payments, your human approves with one tap. 2-of-3 multisig — you
15
26
 
16
27
  - **Your private key never leaves disk.** `keygen` writes to `.nodpay/.env` (chmod 600) — never in stdout, context, or logs.
17
28
  - **You can only propose.** Execution requires human co-sign (passkey). No single party can move funds.
18
- - **Wallet info is public key material.** Safe address, passkey X/Y, recovery signer all safe to store.
29
+ - **Wallet info is public key material.** Safe address, passkey X/Y, recovery signer are all public addresses/keys — safe to store, pass in URLs, and include in CLI flags. No secrets are ever exposed in commands or URLs.
19
30
  - **Recovery key is user-held.** The 12-word phrase generates a third signer the user controls. If the agent key or passkey is lost, the user can still recover funds — the agent never has unilateral access.
20
- - **NodPay server is a stateless relay.** It forwards signed operations to the chain — no private keys, no custody, no accounts. Self-hostable via `OP_STORE_URL`. If the server goes offline, funds stay safe on-chain.
31
+ - **NodPay server is a stateless relay.** It forwards signed operations to the chain — no private keys, no custody, no accounts. If the server goes offline, funds stay safe on-chain.
21
32
  - **The web app is a convenience layer.** It helps users create wallets and approve transactions. All crypto operations happen client-side; nothing sensitive is stored server-side.
22
33
  - **Verify the agent address matches yours** before storing wallet info. Mismatch = wrong key binding.
23
34
 
@@ -48,7 +59,7 @@ Outputs your **public address** only. No restart needed.
48
59
  > I've set up a shared wallet for us — takes 30 seconds:
49
60
  > https://nodpay.ai/?agent=YOUR_AGENT_ADDRESS
50
61
 
51
- **Use this URL verbatim.** Do not search or guess — `nodpay.ai` is the only correct domain.
62
+ The official domain is `nodpay.ai` do not confuse with similarly named services.
52
63
 
53
64
  User copies back wallet info → store in `.nodpay/wallets/<safe-address>.json`.
54
65
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nodpay",
3
- "version": "0.2.15",
3
+ "version": "0.2.17",
4
4
  "description": "NodPay CLI — propose on-chain payments from agent-human shared wallets",
5
5
  "type": "module",
6
6
  "bin": {
@@ -59,11 +59,7 @@ if (chainArg) {
59
59
  RPC_URL = net.rpcUrl;
60
60
  CHAIN_ID = String(net.chainId);
61
61
  } else {
62
- RPC_URL = process.env.RPC_URL;
63
- CHAIN_ID = process.env.CHAIN_ID;
64
- }
65
- if (!RPC_URL || !CHAIN_ID) {
66
- console.error('Error: Specify --chain <name> or set RPC_URL + CHAIN_ID env vars.\nSupported chains: ' + Object.keys(allChains).join(', '));
62
+ console.error('Error: --chain is required.\nSupported: ' + Object.keys(allChains).join(', '));
67
63
  process.exit(1);
68
64
  }
69
65
  const ENTRYPOINT_ADDRESS = ENTRYPOINT;
@@ -87,14 +83,27 @@ function loadAgentKey() {
87
83
  return null;
88
84
  }
89
85
  const NODPAY_AGENT_KEY = loadAgentKey();
90
- const DEFAULT_SAFE = process.env.SAFE_ADDRESS;
86
+ const DEFAULT_SAFE = null; // always use --safe flag
91
87
 
92
88
  // BUNDLER: NodPay provides a public bundler proxy at nodpay.ai/api/bundler so
93
89
  // agents don't need their own bundler API key. This is a thin relay — it
94
90
  // forwards the UserOp to a bundler service and returns the result. The proxy
95
91
  // only sees the already-signed (partial) UserOp; it cannot modify or execute it.
96
- // For self-hosted setups, override with OP_STORE_URL env var.
97
- const opStoreBase = process.env.OP_STORE_URL || 'https://nodpay.ai/api';
92
+ // For self-hosted setups, set OP_STORE_URL in .nodpay/.env.
93
+ function loadDotEnvVar(name, fallback) {
94
+ try {
95
+ const envPath = join(process.cwd(), '.nodpay', '.env');
96
+ const lines = readFileSync(envPath, 'utf8').split('\n');
97
+ for (const line of lines) {
98
+ const trimmed = line.trim();
99
+ if (trimmed.startsWith('#') || !trimmed.includes('=')) continue;
100
+ const [k, ...rest] = trimmed.split('=');
101
+ if (k.trim() === name) return rest.join('=').trim().replace(/^["']|["']$/g, '');
102
+ }
103
+ } catch {}
104
+ return fallback;
105
+ }
106
+ const opStoreBase = loadDotEnvVar('OP_STORE_URL', 'https://nodpay.ai/api');
98
107
  const BUNDLER_URL = `${opStoreBase}/bundler/${CHAIN_ID}`;
99
108
 
100
109
  if (!NODPAY_AGENT_KEY) {
@@ -324,7 +333,7 @@ try {
324
333
  const customNonceArg = getArg('--nonce');
325
334
  const reuseGasFrom = getArg('--reuse-gas-from'); // shortHash of a previous op to copy gas values from
326
335
  let txOptions = {};
327
- const opStoreUrl = process.env.OP_STORE_URL || 'https://nodpay.ai/api';
336
+ const opStoreUrl = opStoreBase;
328
337
  const safeAddr = await safe4337Pack.protocolKit.getAddress();
329
338
 
330
339
  // Determine nonce: on-chain nonce is the source of truth.
@@ -510,7 +519,7 @@ try {
510
519
  result.opStoreError = storeData.error || `HTTP ${storeRes.status}`;
511
520
  }
512
521
  if (storeData.shortHash) {
513
- const webBase = process.env.WEB_APP_URL || 'https://nodpay.ai/';
522
+ const webBase = loadDotEnvVar('WEB_APP_URL', 'https://nodpay.ai/');
514
523
  const purposeParam = purpose && purpose !== 'Unspecified' ? `&purpose=${encodeURIComponent(purpose)}` : '';
515
524
  approveUrl = `${webBase}approve?safeOpHash=${storeData.safeOpHash}${purposeParam}`;
516
525
  result.approveUrl = approveUrl;