@getalby/cli 0.0.0 → 0.1.0
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 +80 -16
- package/build/commands/{request-invoice.js → request-invoice-from-lightning-address.js} +5 -5
- package/build/index.js +4 -4
- package/build/test/connection-secret.test.js +57 -0
- package/build/test/lightning-tools.test.js +2 -2
- package/build/tools/lightning/{request_invoice.js → request_invoice_from_lightning_address.js} +1 -1
- package/build/utils.js +37 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -5,10 +5,14 @@ CLI for Nostr Wallet Connect (NIP-47) with lightning tools.
|
|
|
5
5
|
## Usage
|
|
6
6
|
|
|
7
7
|
```bash
|
|
8
|
-
|
|
8
|
+
# Pass a file path to a connection secret (preferred)
|
|
9
|
+
npx @getalby/cli -c /path/to/secret.txt <command> [options]
|
|
10
|
+
|
|
11
|
+
# Or pass connection string directly
|
|
12
|
+
npx @getalby/cli -c "nostr+walletconnect://..." <command> [options]
|
|
9
13
|
```
|
|
10
14
|
|
|
11
|
-
The
|
|
15
|
+
The `-c` option auto-detects whether you're passing a connection string or a file path. You can get a connection string from your NWC-compatible wallet (e.g., [Alby](https://getalby.com)).
|
|
12
16
|
|
|
13
17
|
## Testing Wallet
|
|
14
18
|
|
|
@@ -48,14 +52,41 @@ npx @getalby/cli -c "nostr+walletconnect://..." make-invoice --amount 1000 --des
|
|
|
48
52
|
# Pay an invoice
|
|
49
53
|
npx @getalby/cli -c "nostr+walletconnect://..." pay-invoice --invoice "lnbc..."
|
|
50
54
|
|
|
55
|
+
# Send a keysend payment
|
|
56
|
+
npx @getalby/cli -c "nostr+walletconnect://..." pay-keysend --pubkey "02abc..." --amount 100
|
|
57
|
+
|
|
51
58
|
# Look up an invoice by payment hash
|
|
52
59
|
npx @getalby/cli -c "nostr+walletconnect://..." lookup-invoice --payment-hash "abc123..."
|
|
53
60
|
|
|
54
61
|
# List transactions
|
|
55
62
|
npx @getalby/cli -c "nostr+walletconnect://..." list-transactions --limit 10
|
|
56
63
|
|
|
64
|
+
# Get wallet budget
|
|
65
|
+
npx @getalby/cli -c "nostr+walletconnect://..." get-budget
|
|
66
|
+
|
|
67
|
+
# Sign a message
|
|
68
|
+
npx @getalby/cli -c "nostr+walletconnect://..." sign-message --message "Hello, World!"
|
|
69
|
+
|
|
57
70
|
# Fetch L402-protected resource
|
|
58
71
|
npx @getalby/cli -c "nostr+walletconnect://..." fetch-l402 --url "https://example.com/api"
|
|
72
|
+
|
|
73
|
+
# Wait for a payment notification
|
|
74
|
+
npx @getalby/cli -c "nostr+walletconnect://..." wait-for-payment --payment-hash "abc123..."
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
### HOLD Invoices
|
|
78
|
+
|
|
79
|
+
HOLD invoices allow you to accept payments conditionally - the payment is held until you settle or cancel it.
|
|
80
|
+
|
|
81
|
+
```bash
|
|
82
|
+
# Create a HOLD invoice (you provide the payment hash)
|
|
83
|
+
npx @getalby/cli -c "nostr+walletconnect://..." make-hold-invoice --amount 1000 --payment-hash "abc123..."
|
|
84
|
+
|
|
85
|
+
# Settle a HOLD invoice (claim the payment)
|
|
86
|
+
npx @getalby/cli -c "nostr+walletconnect://..." settle-hold-invoice --preimage "def456..."
|
|
87
|
+
|
|
88
|
+
# Cancel a HOLD invoice (reject the payment)
|
|
89
|
+
npx @getalby/cli -c "nostr+walletconnect://..." cancel-hold-invoice --payment-hash "abc123..."
|
|
59
90
|
```
|
|
60
91
|
|
|
61
92
|
### Lightning Tools
|
|
@@ -66,28 +97,61 @@ These commands don't require a wallet connection:
|
|
|
66
97
|
# Convert USD to sats
|
|
67
98
|
npx @getalby/cli fiat-to-sats --currency USD --amount 10
|
|
68
99
|
|
|
100
|
+
# Convert sats to USD
|
|
101
|
+
npx @getalby/cli sats-to-fiat --amount 1000 --currency USD
|
|
102
|
+
|
|
69
103
|
# Parse a BOLT-11 invoice
|
|
70
104
|
npx @getalby/cli parse-invoice --invoice "lnbc..."
|
|
71
105
|
|
|
106
|
+
# Verify a preimage against an invoice
|
|
107
|
+
npx @getalby/cli verify-preimage --invoice "lnbc..." --preimage "abc123..."
|
|
108
|
+
|
|
72
109
|
# Request invoice from lightning address
|
|
73
|
-
npx @getalby/cli request-invoice --address "hello@getalby.com" --amount 1000
|
|
110
|
+
npx @getalby/cli request-invoice-from-lightning-address --address "hello@getalby.com" --amount 1000
|
|
74
111
|
```
|
|
75
112
|
|
|
76
113
|
## Command Reference
|
|
77
114
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
|
83
|
-
|
|
|
84
|
-
| `
|
|
85
|
-
| `
|
|
86
|
-
| `
|
|
87
|
-
| `
|
|
88
|
-
| `
|
|
89
|
-
| `
|
|
90
|
-
| `
|
|
115
|
+
### Wallet Commands
|
|
116
|
+
|
|
117
|
+
These require `-c` or `--connection-secret`:
|
|
118
|
+
|
|
119
|
+
| Command | Description | Required Options |
|
|
120
|
+
| ------------------------- | ------------------------------ | ------------------------------- |
|
|
121
|
+
| `get-balance` | Get wallet balance | - |
|
|
122
|
+
| `get-info` | Get wallet info | - |
|
|
123
|
+
| `get-wallet-service-info` | Get wallet capabilities | - |
|
|
124
|
+
| `get-budget` | Get wallet budget | - |
|
|
125
|
+
| `make-invoice` | Create a lightning invoice | `--amount` |
|
|
126
|
+
| `pay-invoice` | Pay a lightning invoice | `--invoice` |
|
|
127
|
+
| `pay-keysend` | Send a keysend payment | `--pubkey`, `--amount` |
|
|
128
|
+
| `lookup-invoice` | Look up an invoice | `--payment-hash` or `--invoice` |
|
|
129
|
+
| `list-transactions` | List transactions | - |
|
|
130
|
+
| `sign-message` | Sign a message with wallet key | `--message` |
|
|
131
|
+
| `wait-for-payment` | Wait for payment notification | `--payment-hash` |
|
|
132
|
+
| `fetch-l402` | Fetch L402-protected resource | `--url` |
|
|
133
|
+
|
|
134
|
+
### HOLD Invoice Commands
|
|
135
|
+
|
|
136
|
+
These require `-c` or `--connection-secret`:
|
|
137
|
+
|
|
138
|
+
| Command | Description | Required Options |
|
|
139
|
+
| --------------------- | --------------------- | ---------------------------- |
|
|
140
|
+
| `make-hold-invoice` | Create a HOLD invoice | `--amount`, `--payment-hash` |
|
|
141
|
+
| `settle-hold-invoice` | Settle a HOLD invoice | `--preimage` |
|
|
142
|
+
| `cancel-hold-invoice` | Cancel a HOLD invoice | `--payment-hash` |
|
|
143
|
+
|
|
144
|
+
### Lightning Tools
|
|
145
|
+
|
|
146
|
+
These don't require a wallet connection:
|
|
147
|
+
|
|
148
|
+
| Command | Description | Required Options |
|
|
149
|
+
| ---------------------------------------- | -------------------------------------- | ------------------------- |
|
|
150
|
+
| `fiat-to-sats` | Convert fiat to sats | `--currency`, `--amount` |
|
|
151
|
+
| `sats-to-fiat` | Convert sats to fiat | `--amount`, `--currency` |
|
|
152
|
+
| `parse-invoice` | Parse a BOLT-11 invoice | `--invoice` |
|
|
153
|
+
| `verify-preimage` | Verify preimage against invoice | `--invoice`, `--preimage` |
|
|
154
|
+
| `request-invoice-from-lightning-address` | Request invoice from lightning address | `--address`, `--amount` |
|
|
91
155
|
|
|
92
156
|
## Output
|
|
93
157
|
|
|
@@ -1,15 +1,15 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { requestInvoiceFromLightningAddress } from "../tools/lightning/request_invoice_from_lightning_address.js";
|
|
2
2
|
import { handleError, output } from "../utils.js";
|
|
3
|
-
export function
|
|
3
|
+
export function registerRequestInvoiceFromLightningAddressCommand(program) {
|
|
4
4
|
program
|
|
5
|
-
.command("request-invoice")
|
|
6
|
-
.description("Request invoice from lightning address")
|
|
5
|
+
.command("request-invoice-from-lightning-address")
|
|
6
|
+
.description("Request an invoice from a lightning address")
|
|
7
7
|
.requiredOption("-a, --address <ln-address>", "Lightning address")
|
|
8
8
|
.requiredOption("-s, --amount <sats>", "Amount in sats", parseInt)
|
|
9
9
|
.option("--comment <text>", "Optional comment")
|
|
10
10
|
.action(async (options) => {
|
|
11
11
|
await handleError(async () => {
|
|
12
|
-
const result = await
|
|
12
|
+
const result = await requestInvoiceFromLightningAddress({
|
|
13
13
|
lightning_address: options.address,
|
|
14
14
|
amount_in_sats: options.amount,
|
|
15
15
|
comment: options.comment,
|
package/build/index.js
CHANGED
|
@@ -18,14 +18,14 @@ import { registerFiatToSatsCommand } from "./commands/fiat-to-sats.js";
|
|
|
18
18
|
import { registerSatsToFiatCommand } from "./commands/sats-to-fiat.js";
|
|
19
19
|
import { registerParseInvoiceCommand } from "./commands/parse-invoice.js";
|
|
20
20
|
import { registerVerifyPreimageCommand } from "./commands/verify-preimage.js";
|
|
21
|
-
import {
|
|
21
|
+
import { registerRequestInvoiceFromLightningAddressCommand } from "./commands/request-invoice-from-lightning-address.js";
|
|
22
22
|
import { registerFetchL402Command } from "./commands/fetch-l402.js";
|
|
23
23
|
const program = new Command();
|
|
24
24
|
program
|
|
25
25
|
.name("alby-cli")
|
|
26
26
|
.description("CLI for Nostr Wallet Connect (NIP-47) with lightning tools")
|
|
27
|
-
.version("0.
|
|
28
|
-
.option("-c, --connection-secret <string>", "NWC connection secret (nostr+walletconnect://...)");
|
|
27
|
+
.version("0.1.0")
|
|
28
|
+
.option("-c, --connection-secret <string>", "NWC connection secret (nostr+walletconnect://...) or path to file containing it (preferred)");
|
|
29
29
|
// Register all commands
|
|
30
30
|
registerGetBalanceCommand(program);
|
|
31
31
|
registerGetBudgetCommand(program);
|
|
@@ -45,6 +45,6 @@ registerFiatToSatsCommand(program);
|
|
|
45
45
|
registerSatsToFiatCommand(program);
|
|
46
46
|
registerParseInvoiceCommand(program);
|
|
47
47
|
registerVerifyPreimageCommand(program);
|
|
48
|
-
|
|
48
|
+
registerRequestInvoiceFromLightningAddressCommand(program);
|
|
49
49
|
registerFetchL402Command(program);
|
|
50
50
|
program.parse();
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { describe, test, expect, beforeAll, afterAll } from "vitest";
|
|
2
|
+
import { createTestWallet, runCli } from "./helpers.js";
|
|
3
|
+
import { writeFileSync, unlinkSync } from "node:fs";
|
|
4
|
+
import { tmpdir } from "node:os";
|
|
5
|
+
import { join } from "node:path";
|
|
6
|
+
describe("Connection Secret Handling", () => {
|
|
7
|
+
let wallet;
|
|
8
|
+
let secretFilePath;
|
|
9
|
+
let badSecretFilePath;
|
|
10
|
+
beforeAll(async () => {
|
|
11
|
+
wallet = await createTestWallet();
|
|
12
|
+
// Create temp file with valid connection secret
|
|
13
|
+
secretFilePath = join(tmpdir(), `nwc-test-secret-${Date.now()}.txt`);
|
|
14
|
+
writeFileSync(secretFilePath, wallet.nwcUrl);
|
|
15
|
+
// Create temp file with invalid/truncated content
|
|
16
|
+
badSecretFilePath = join(tmpdir(), `nwc-test-bad-secret-${Date.now()}.txt`);
|
|
17
|
+
writeFileSync(badSecretFilePath, "nostr+wallet");
|
|
18
|
+
}, 60000);
|
|
19
|
+
afterAll(() => {
|
|
20
|
+
try {
|
|
21
|
+
unlinkSync(secretFilePath);
|
|
22
|
+
}
|
|
23
|
+
catch { }
|
|
24
|
+
try {
|
|
25
|
+
unlinkSync(badSecretFilePath);
|
|
26
|
+
}
|
|
27
|
+
catch { }
|
|
28
|
+
});
|
|
29
|
+
test("accepts connection string directly", () => {
|
|
30
|
+
const result = runCli(`-c "${wallet.nwcUrl}" get-balance`);
|
|
31
|
+
expect(result.success).toBe(true);
|
|
32
|
+
});
|
|
33
|
+
test("reads connection secret from file", () => {
|
|
34
|
+
const result = runCli(`-c "${secretFilePath}" get-balance`);
|
|
35
|
+
expect(result.success).toBe(true);
|
|
36
|
+
});
|
|
37
|
+
test("errors when file does not exist", () => {
|
|
38
|
+
const result = runCli(`-c "/tmp/nonexistent-file-${Date.now()}" get-balance`);
|
|
39
|
+
expect(result.success).toBe(false);
|
|
40
|
+
expect(result.output.error).toContain("Failed to read connection secret file");
|
|
41
|
+
});
|
|
42
|
+
test("errors when file contains invalid connection string", () => {
|
|
43
|
+
const result = runCli(`-c "${badSecretFilePath}" get-balance`);
|
|
44
|
+
expect(result.success).toBe(false);
|
|
45
|
+
expect(result.output.error).toContain("Invalid connection secret");
|
|
46
|
+
});
|
|
47
|
+
test("errors when no connection secret provided", () => {
|
|
48
|
+
const result = runCli("get-balance");
|
|
49
|
+
expect(result.success).toBe(false);
|
|
50
|
+
expect(result.output.error).toContain("--connection-secret is required");
|
|
51
|
+
});
|
|
52
|
+
test("errors when connection string is malformed", () => {
|
|
53
|
+
const result = runCli(`-c "nostr+walletconnect://asdf" get-balance`);
|
|
54
|
+
expect(result.success).toBe(false);
|
|
55
|
+
expect(result.output.error).toContain("Invalid connection secret");
|
|
56
|
+
});
|
|
57
|
+
});
|
|
@@ -28,8 +28,8 @@ describe("Lightning Tools (no wallet required)", () => {
|
|
|
28
28
|
expect(result.success).toBe(true);
|
|
29
29
|
expect(result.output.valid).toBe(false);
|
|
30
30
|
});
|
|
31
|
-
test("request-invoice requests invoice from lightning address", async () => {
|
|
32
|
-
const result = runCli(`request-invoice -a "${exampleLightningAddress}" -s 100`);
|
|
31
|
+
test("request-invoice-from-lightning-address requests invoice from lightning address", async () => {
|
|
32
|
+
const result = runCli(`request-invoice-from-lightning-address -a "${exampleLightningAddress}" -s 100`);
|
|
33
33
|
expect(result.success).toBe(true);
|
|
34
34
|
expect(result.output.paymentRequest.toLowerCase()).toMatch(/^lnbc/);
|
|
35
35
|
});
|
package/build/tools/lightning/{request_invoice.js → request_invoice_from_lightning_address.js}
RENAMED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { LightningAddress } from "@getalby/lightning-tools";
|
|
2
|
-
export async function
|
|
2
|
+
export async function requestInvoiceFromLightningAddress(params) {
|
|
3
3
|
const ln = new LightningAddress(params.lightning_address);
|
|
4
4
|
await ln.fetch();
|
|
5
5
|
const { satoshi, ...invoice } = await ln.requestInvoice({
|
package/build/utils.js
CHANGED
|
@@ -1,12 +1,47 @@
|
|
|
1
1
|
import { NWCClient } from "@getalby/sdk";
|
|
2
|
+
import { readFileSync } from "node:fs";
|
|
2
3
|
export function getClient(program) {
|
|
3
4
|
const opts = program.opts();
|
|
4
|
-
|
|
5
|
+
let connectionSecret = opts.connectionSecret;
|
|
5
6
|
if (!connectionSecret) {
|
|
6
7
|
console.error("Error: --connection-secret is required for this command");
|
|
7
8
|
process.exit(1);
|
|
8
9
|
}
|
|
9
|
-
|
|
10
|
+
// Auto-detect: if it doesn't start with the protocol, treat as file path
|
|
11
|
+
if (!connectionSecret.startsWith("nostr+walletconnect://")) {
|
|
12
|
+
try {
|
|
13
|
+
connectionSecret = readFileSync(connectionSecret, "utf-8").trim();
|
|
14
|
+
}
|
|
15
|
+
catch (error) {
|
|
16
|
+
console.error(`Error: Failed to read connection secret file "${opts.connectionSecret}": ${error instanceof Error ? error.message : String(error)}`);
|
|
17
|
+
process.exit(1);
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
// Validate the connection string format
|
|
21
|
+
if (!connectionSecret.startsWith("nostr+walletconnect://")) {
|
|
22
|
+
console.error(`Error: Invalid connection secret. Expected format: nostr+walletconnect://...\n` +
|
|
23
|
+
`Got: "${connectionSecret.substring(0, 50)}${connectionSecret.length > 50 ? "..." : ""}"\n` +
|
|
24
|
+
`Hint: Make sure the connection string is complete and not truncated.`);
|
|
25
|
+
process.exit(1);
|
|
26
|
+
}
|
|
27
|
+
const client = new NWCClient({ nostrWalletConnectUrl: connectionSecret });
|
|
28
|
+
// Validate client properties
|
|
29
|
+
if (!client.secret || !/^[0-9a-f]{64}$/i.test(client.secret)) {
|
|
30
|
+
console.error(`Error: Invalid connection secret. Missing or invalid secret key.\n` +
|
|
31
|
+
`Hint: Make sure the connection string is complete and not truncated.`);
|
|
32
|
+
process.exit(1);
|
|
33
|
+
}
|
|
34
|
+
if (!client.walletPubkey || !/^[0-9a-f]{64}$/i.test(client.walletPubkey)) {
|
|
35
|
+
console.error(`Error: Invalid connection secret. Missing or invalid wallet pubkey.\n` +
|
|
36
|
+
`Hint: Make sure the connection string is complete and not truncated.`);
|
|
37
|
+
process.exit(1);
|
|
38
|
+
}
|
|
39
|
+
if (!client.relayUrls) {
|
|
40
|
+
console.error(`Error: Invalid connection secret. Missing relay URL.\n` +
|
|
41
|
+
`Hint: Make sure the connection string is complete and not truncated.`);
|
|
42
|
+
process.exit(1);
|
|
43
|
+
}
|
|
44
|
+
return client;
|
|
10
45
|
}
|
|
11
46
|
export function output(data) {
|
|
12
47
|
console.log(JSON.stringify(data, null, 2));
|
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "@getalby/cli",
|
|
3
3
|
"description": "CLI for Nostr Wallet Connect (NIP-47) with a few additional useful lightning tools",
|
|
4
4
|
"repository": "https://github.com/getAlby/cli.git",
|
|
5
|
-
"version": "0.
|
|
5
|
+
"version": "0.1.0",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"main": "build/index.js",
|
|
8
8
|
"bin": {
|