nodpay 0.2.2 → 0.2.4

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/README.md CHANGED
@@ -28,11 +28,29 @@ NODPAY_AGENT_KEY=0x... npx nodpay propose \
28
28
  3. Agent proposes transactions with `npx nodpay propose`
29
29
  4. User approves/rejects on their phone
30
30
 
31
+ ## Key generation
32
+
33
+ ```bash
34
+ npx nodpay keygen --env-file .env
35
+ ```
36
+
37
+ Outputs the agent's **public address only**. The private key is written directly to `.env` — it never appears in stdout, logs, or the agent's context window.
38
+
39
+ If a key already exists, it reuses it and prints the address.
40
+
41
+ ### Security design
42
+
43
+ The agent (LLM) **never sees the private key**. `keygen` writes the secret directly to disk; the `propose` command reads it from the environment at runtime. This means:
44
+
45
+ - No private key in conversation history or context window
46
+ - No risk of leaking the key through prompt injection
47
+ - The agent only needs the public address (for wallet links)
48
+
31
49
  ## Env
32
50
 
33
51
  | Variable | Required | Description |
34
52
  |----------|----------|-------------|
35
- | `NODPAY_AGENT_KEY` | ✅ | Agent's private key (hex) |
53
+ | `NODPAY_AGENT_KEY` | ✅ | Agent's private key use `npx nodpay keygen` to generate securely |
36
54
 
37
55
  ## Supported chains
38
56
 
package/SKILL.md CHANGED
@@ -39,12 +39,19 @@ The wallet is a **Safe 2-of-3 multisig**. You hold 1 key, the user holds 2 (pass
39
39
  ### 1. Generate an agent key
40
40
 
41
41
  ```bash
42
- node -e "const w=require('ethers').Wallet.createRandom();console.log('Address:',w.address,'\nKey:',w.privateKey)"
42
+ npx nodpay keygen --env-file .env
43
43
  ```
44
44
 
45
- Store the private key as `NODPAY_AGENT_KEY` in your environment. The address is your public agent identity.
45
+ This outputs your agent's **public address** (stdout). The private key is written directly to `.env` it never appears in your output or context.
46
46
 
47
- **⚠️ Storing the env var may trigger a restart.** Complete all config/env changes and wait for the restart to finish BEFORE sending any message to the user. Do not send messages and update config in parallel — the restart will truncate your message.
47
+ If `NODPAY_AGENT_KEY` already exists in the file, it reuses the existing key and prints the address.
48
+
49
+ **After keygen**, trigger a gateway restart so the env var takes effect. Send your wallet link message to the user BEFORE restarting — the restart will interrupt your session.
50
+
51
+ Recommended sequence:
52
+ 1. Run `npx nodpay keygen --env-file .env` → note the address
53
+ 2. Send the wallet link to your user (step 2 below)
54
+ 3. Trigger gateway restart
48
55
 
49
56
  ### 2. Send the user a wallet link
50
57
 
@@ -108,6 +115,7 @@ One agent key serves all wallets — multi-wallet is handled user-side (differen
108
115
  ```bash
109
116
  NODPAY_AGENT_KEY=0x... \
110
117
  npx nodpay propose \
118
+ --chain <CHAIN_NAME> \
111
119
  --safe <WALLET_ADDRESS> \
112
120
  --to <RECIPIENT> \
113
121
  --value-eth <AMOUNT> \
@@ -152,6 +160,7 @@ Always check before proposing — this tells you the current nonce, pending ops,
152
160
 
153
161
  | Flag | Required | Description |
154
162
  |------|----------|-------------|
163
+ | `--chain` | ✅ | Chain name (e.g. `ethereum`, `base`, `sepolia`) |
155
164
  | `--safe` | ✅ | Wallet (Safe) address |
156
165
  | `--to` | ✅ | Recipient address |
157
166
  | `--value-eth` | ✅ | Amount in ETH |
@@ -171,7 +180,7 @@ Only one env var is required:
171
180
  |-----|-------------|
172
181
  | `NODPAY_AGENT_KEY` | Agent signing key (required) |
173
182
 
174
- Chain config (RPC, bundler, explorer) is auto-resolved from `references/networks.json`. No need to set `RPC_URL`, `CHAIN_ID`, or bundler keys.
183
+ Chain config (RPC, bundler) is auto-resolved via `--chain` from [`@nodpay/core/networks`](https://www.npmjs.com/package/@nodpay/core). You can override with `RPC_URL`/`CHAIN_ID` env vars if needed, but `--chain` is the recommended way.
175
184
 
176
185
  ### Supported Chains
177
186
 
package/bin/nodpay.mjs CHANGED
@@ -7,6 +7,10 @@ if (command === 'propose') {
7
7
  const scriptPath = new URL('../scripts/propose.mjs', import.meta.url).pathname;
8
8
  process.argv = [process.argv[0], scriptPath, ...process.argv.slice(3)];
9
9
  await import(scriptPath);
10
+ } else if (command === 'keygen') {
11
+ const scriptPath = new URL('../scripts/keygen.mjs', import.meta.url).pathname;
12
+ process.argv = [process.argv[0], scriptPath, ...process.argv.slice(3)];
13
+ await import(scriptPath);
10
14
  } else if (command === 'version' || command === '--version' || command === '-v') {
11
15
  const { readFileSync } = await import('fs');
12
16
  const pkg = JSON.parse(readFileSync(new URL('../package.json', import.meta.url), 'utf8'));
@@ -15,9 +19,11 @@ if (command === 'propose') {
15
19
  console.log(`Usage: nodpay <command>
16
20
 
17
21
  Commands:
22
+ keygen Generate (or reuse) agent keypair
18
23
  propose Propose a transaction for human approval
19
24
 
20
- Example:
25
+ Examples:
26
+ nodpay keygen --env-file .env
21
27
  nodpay propose --safe 0x... --to 0x... --value-eth 0.01 --signer-type passkey
22
28
 
23
29
  Docs: https://nodpay.ai/skill.md`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nodpay",
3
- "version": "0.2.2",
3
+ "version": "0.2.4",
4
4
  "description": "NodPay CLI — propose on-chain payments from agent-human shared wallets",
5
5
  "type": "module",
6
6
  "bin": {
@@ -0,0 +1,61 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Generate (or reuse) an agent keypair.
4
+ *
5
+ * - If NODPAY_AGENT_KEY already exists in --env-file, derives and prints the address.
6
+ * - Otherwise generates a new keypair, appends to --env-file, prints the address.
7
+ *
8
+ * The private key NEVER appears in stdout — only the public address.
9
+ *
10
+ * Usage:
11
+ * npx nodpay keygen --env-file .env
12
+ */
13
+
14
+ import { Wallet } from 'ethers';
15
+ import { readFileSync, appendFileSync, existsSync } from 'fs';
16
+ import { resolve } from 'path';
17
+
18
+ const args = process.argv.slice(2);
19
+ const envFileIdx = args.indexOf('--env-file');
20
+ const envFile = envFileIdx !== -1 ? resolve(args[envFileIdx + 1]) : null;
21
+
22
+ if (!envFile) {
23
+ console.error('Usage: npx nodpay keygen --env-file <path>');
24
+ process.exit(1);
25
+ }
26
+
27
+ const ENV_VAR = 'NODPAY_AGENT_KEY';
28
+
29
+ // Check if key already exists in the env file
30
+ function findExistingKey() {
31
+ if (!existsSync(envFile)) return null;
32
+ const lines = readFileSync(envFile, 'utf8').split('\n');
33
+ for (const line of lines) {
34
+ const trimmed = line.trim();
35
+ if (trimmed.startsWith('#') || !trimmed.includes('=')) continue;
36
+ const [name, ...rest] = trimmed.split('=');
37
+ if (name.trim() === ENV_VAR) {
38
+ const value = rest.join('=').trim().replace(/^["']|["']$/g, '');
39
+ if (value) return value;
40
+ }
41
+ }
42
+ return null;
43
+ }
44
+
45
+ const existing = findExistingKey();
46
+
47
+ if (existing) {
48
+ try {
49
+ const wallet = new Wallet(existing);
50
+ console.log(wallet.address);
51
+ console.error(`${ENV_VAR} already configured in ${envFile}`);
52
+ } catch {
53
+ console.error(`${ENV_VAR} exists in ${envFile} but is invalid. Remove it and re-run.`);
54
+ process.exit(1);
55
+ }
56
+ } else {
57
+ const wallet = Wallet.createRandom();
58
+ appendFileSync(envFile, `\n${ENV_VAR}=${wallet.privateKey}\n`);
59
+ console.log(wallet.address);
60
+ console.error(`Generated new agent key → ${envFile}`);
61
+ }
@@ -37,10 +37,31 @@ const __dirname = dirname(fileURLToPath(import.meta.url));
37
37
  const PENDING_DIR = join(__dirname, '..', '.pending-txs');
38
38
  mkdirSync(PENDING_DIR, { recursive: true });
39
39
 
40
- const RPC_URL = process.env.RPC_URL;
41
- const CHAIN_ID = process.env.CHAIN_ID;
40
+ // Resolve chain config: --chain flag auto-resolves from networks.json, env vars as fallback
41
+ import { createRequire } from 'module';
42
+ const require = createRequire(import.meta.url);
43
+ const NETWORKS = require('@nodpay/core/networks');
44
+ const allChains = { ...NETWORKS.mainnet, ...NETWORKS.testnet };
45
+
46
+ const chainArg = process.argv.includes('--chain')
47
+ ? process.argv[process.argv.indexOf('--chain') + 1]
48
+ : null;
49
+
50
+ let RPC_URL, CHAIN_ID;
51
+ if (chainArg) {
52
+ const net = allChains[chainArg];
53
+ if (!net) {
54
+ console.error(`Error: Unknown chain "${chainArg}". Supported: ${Object.keys(allChains).join(', ')}`);
55
+ process.exit(1);
56
+ }
57
+ RPC_URL = process.env.RPC_URL || net.rpcUrl;
58
+ CHAIN_ID = String(net.chainId);
59
+ } else {
60
+ RPC_URL = process.env.RPC_URL;
61
+ CHAIN_ID = process.env.CHAIN_ID;
62
+ }
42
63
  if (!RPC_URL || !CHAIN_ID) {
43
- console.error('Error: RPC_URL and CHAIN_ID environment variables are required.\nSet them for your target chain. See references/networks.json for supported chains.');
64
+ console.error('Error: Specify --chain <name> or set RPC_URL + CHAIN_ID env vars.\nSupported chains: ' + Object.keys(allChains).join(', '));
44
65
  process.exit(1);
45
66
  }
46
67
  const ENTRYPOINT_ADDRESS = ENTRYPOINT;