npmguard-cli 1.1.1 → 1.1.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.
- package/README.md +194 -6
- package/dist/api.js +3 -0
- package/dist/commands/install.js +92 -13
- package/dist/index.js +15 -4
- package/dist/utils.js +25 -0
- package/dist/wallet/walletconnect.js +8 -4
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -1,9 +1,197 @@
|
|
|
1
|
-
#
|
|
1
|
+
# npmguard-cli
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Security-gated npm install. Runs every package through NpmGuard's audit
|
|
4
|
+
engine before it touches your `node_modules`.
|
|
4
5
|
|
|
5
|
-
|
|
6
|
+
```bash
|
|
7
|
+
npx npmguard-cli install express
|
|
8
|
+
```
|
|
6
9
|
|
|
7
|
-
-
|
|
8
|
-
-
|
|
9
|
-
-
|
|
10
|
+
- **SAFE** → installs immediately
|
|
11
|
+
- **DANGEROUS** → warns, shows findings, asks before installing
|
|
12
|
+
- **No audit yet** → offers to pay for one (Stripe or crypto), then streams
|
|
13
|
+
the results in real time
|
|
14
|
+
|
|
15
|
+
## Install
|
|
16
|
+
|
|
17
|
+
No install required — `npx` pulls the latest from npm:
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
npx npmguard-cli@latest install <pkg>
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
Or install globally:
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
npm install -g npmguard-cli
|
|
27
|
+
npmguard install <pkg>
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## Commands
|
|
31
|
+
|
|
32
|
+
### `npmguard install <package>[@version]`
|
|
33
|
+
|
|
34
|
+
The main command. Runs the full gate-then-install flow.
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
npmguard install express
|
|
38
|
+
npmguard install lodash@4.17.21
|
|
39
|
+
npmguard install @types/node@22
|
|
40
|
+
|
|
41
|
+
# Force install even if the package is flagged DANGEROUS
|
|
42
|
+
npmguard install left-pad --force
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
The command auto-detects your package manager (`npm` / `pnpm` / `yarn`) from
|
|
46
|
+
lockfiles and runs the correct add command (`npm install`, `pnpm add`,
|
|
47
|
+
`yarn add`).
|
|
48
|
+
|
|
49
|
+
**Flow**:
|
|
50
|
+
|
|
51
|
+
1. Resolves the version (`latest` if omitted)
|
|
52
|
+
2. Asks the engine if the package has an existing audit
|
|
53
|
+
3. **Found + SAFE** → runs `<pm> add <pkg>` directly
|
|
54
|
+
4. **Found + DANGEROUS** → shows findings + capabilities, prompts `y/N`
|
|
55
|
+
(bypass with `--force`)
|
|
56
|
+
5. **Not found** → asks how you want to pay for the audit:
|
|
57
|
+
- Stripe (credit card) — browser checkout via QR
|
|
58
|
+
- Browser wallet — MetaMask/Rabby signs in Brave or Chrome
|
|
59
|
+
- WalletConnect — mobile wallet signs a tx on Base Sepolia
|
|
60
|
+
- Install without audit (yolo)
|
|
61
|
+
- Cancel
|
|
62
|
+
6. Streams audit events live for Stripe/WalletConnect, or waits for the
|
|
63
|
+
report when the browser-wallet page owns the live view
|
|
64
|
+
7. Runs the install if the verdict is SAFE, or prompts otherwise
|
|
65
|
+
|
|
66
|
+
### `npmguard audit <package>[@version]`
|
|
67
|
+
|
|
68
|
+
Run a standalone audit without installing. Returns the verdict and exits.
|
|
69
|
+
|
|
70
|
+
```bash
|
|
71
|
+
npmguard audit is-number
|
|
72
|
+
npmguard audit express@5.2.1
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
If the package hasn't been audited yet, the standalone audit command starts
|
|
76
|
+
the Stripe checkout flow. Use `install` for the interactive browser-wallet
|
|
77
|
+
and WalletConnect choices.
|
|
78
|
+
|
|
79
|
+
### `npmguard check [--path <dir>]`
|
|
80
|
+
|
|
81
|
+
Walk `package.json` in the given directory and check every dependency
|
|
82
|
+
against NpmGuard's audit database. Useful for auditing an existing project.
|
|
83
|
+
|
|
84
|
+
```bash
|
|
85
|
+
cd my-project
|
|
86
|
+
npmguard check
|
|
87
|
+
# or
|
|
88
|
+
npmguard check --path /path/to/other-project
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
## Payment options
|
|
92
|
+
|
|
93
|
+
When a package hasn't been audited yet, an audit run costs real compute
|
|
94
|
+
(LLM calls, sandbox execution). Three ways to pay:
|
|
95
|
+
|
|
96
|
+
### Stripe (fiat)
|
|
97
|
+
|
|
98
|
+
Opens a Stripe checkout page in the browser. After payment, the engine
|
|
99
|
+
triggers the audit automatically. Works from any machine, no wallet
|
|
100
|
+
required.
|
|
101
|
+
|
|
102
|
+
### Browser wallet (crypto)
|
|
103
|
+
|
|
104
|
+
The CLI prints a `https://npmguard.com/pay?...` URL and can open it in your
|
|
105
|
+
default browser. Open that page in Brave or Chrome with MetaMask/Rabby
|
|
106
|
+
enabled, connect the wallet, and confirm the Base Sepolia transaction. The
|
|
107
|
+
browser starts the server-side audit with the tx hash, while the CLI waits
|
|
108
|
+
for the persisted report before deciding whether to install.
|
|
109
|
+
|
|
110
|
+
This is the desktop-friendly flow for extension wallets.
|
|
111
|
+
|
|
112
|
+
### WalletConnect (crypto)
|
|
113
|
+
|
|
114
|
+
The CLI generates a WalletConnect v2 QR code in the terminal. Scan it with
|
|
115
|
+
any mobile wallet (MetaMask, Rainbow, Coinbase Wallet, etc.) and confirm
|
|
116
|
+
the transaction. It also prints the raw WalletConnect URI for desktop
|
|
117
|
+
wallets that support a "connect by URI" flow.
|
|
118
|
+
|
|
119
|
+
Browser extension wallets do not automatically connect to a terminal
|
|
120
|
+
process. If your wallet only works as a Brave/Chrome extension, use the
|
|
121
|
+
browser wallet option instead.
|
|
122
|
+
|
|
123
|
+
- **Chain**: Base Sepolia (testnet — free ETH from
|
|
124
|
+
[Alchemy faucet](https://www.alchemy.com/faucets/base-sepolia))
|
|
125
|
+
- **Fee**: `0.0001 ETH` per audit
|
|
126
|
+
- **Contract**: [`0xBF562626e4Afb883423Ec719e0270DB232bcB9eD`](https://sepolia.basescan.org/address/0xbf562626e4afb883423ec719e0270db232bcb9ed)
|
|
127
|
+
|
|
128
|
+
Flow:
|
|
129
|
+
|
|
130
|
+
1. CLI asks the engine for public crypto config (contract + fee), with a direct contract-read fallback
|
|
131
|
+
2. You approve the tx in your wallet
|
|
132
|
+
3. Engine verifies the receipt on Base Sepolia via Alchemy
|
|
133
|
+
4. Audit starts, CLI streams events
|
|
134
|
+
|
|
135
|
+
The on-chain event `AuditRequested(packageName, version, requester, feePaid)`
|
|
136
|
+
acts as the payment proof. The engine decodes it and matches the args
|
|
137
|
+
against your request before launching the audit.
|
|
138
|
+
|
|
139
|
+
## Configuration
|
|
140
|
+
|
|
141
|
+
The CLI talks to `https://npmguard.com` by default. You can override the
|
|
142
|
+
API URL and the web app URL for local development:
|
|
143
|
+
|
|
144
|
+
```bash
|
|
145
|
+
# via flag
|
|
146
|
+
npmguard --api http://localhost:8000 --web http://localhost:3000 install lodash
|
|
147
|
+
|
|
148
|
+
# via env
|
|
149
|
+
export NPMGUARD_API_URL=http://localhost:8000
|
|
150
|
+
export NPMGUARD_WEB_URL=http://localhost:3000
|
|
151
|
+
npmguard install lodash
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
No private key or paid RPC config is required from the user. For the
|
|
155
|
+
WalletConnect path, the CLI uses `viem` with a public Base Sepolia RPC to
|
|
156
|
+
read/confirm the transaction for local UX; your wallet broadcasts the tx,
|
|
157
|
+
and the engine re-verifies the receipt with its own RPC before starting the
|
|
158
|
+
audit.
|
|
159
|
+
|
|
160
|
+
## Exit codes
|
|
161
|
+
|
|
162
|
+
| Code | Meaning |
|
|
163
|
+
|---|---|
|
|
164
|
+
| 0 | Audit passed, package installed |
|
|
165
|
+
| 1 | Audit failed / install aborted / network error |
|
|
166
|
+
|
|
167
|
+
## Dependencies
|
|
168
|
+
|
|
169
|
+
Intentionally minimal:
|
|
170
|
+
|
|
171
|
+
- `commander` — CLI arg parsing
|
|
172
|
+
- `chalk`, `ora`, `qrcode-terminal` — terminal UI
|
|
173
|
+
- `eventsource` — SSE client for audit events
|
|
174
|
+
- `viem` — read contract, encode calldata, wait for receipt
|
|
175
|
+
- `@walletconnect/sign-client` — WalletConnect v2 session
|
|
176
|
+
|
|
177
|
+
No private key handling in the CLI. The wallet signs and broadcasts; the
|
|
178
|
+
CLI only observes.
|
|
179
|
+
|
|
180
|
+
## Development
|
|
181
|
+
|
|
182
|
+
```bash
|
|
183
|
+
cd cli
|
|
184
|
+
npm install
|
|
185
|
+
npm run build
|
|
186
|
+
|
|
187
|
+
# Test against local engine
|
|
188
|
+
node dist/index.js --api http://localhost:8000 install is-number
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
## Release
|
|
192
|
+
|
|
193
|
+
```bash
|
|
194
|
+
npm version patch # or minor / major
|
|
195
|
+
npm run build
|
|
196
|
+
npm publish --access public
|
|
197
|
+
```
|
package/dist/api.js
CHANGED
|
@@ -10,6 +10,9 @@ async function request(url, options) {
|
|
|
10
10
|
}
|
|
11
11
|
return res.json();
|
|
12
12
|
}
|
|
13
|
+
export async function getPublicConfig(apiUrl) {
|
|
14
|
+
return request(`${apiUrl}/config/public`);
|
|
15
|
+
}
|
|
13
16
|
export async function checkout(apiUrl, packageName, version) {
|
|
14
17
|
const body = { packageName };
|
|
15
18
|
if (version)
|
package/dist/commands/install.js
CHANGED
|
@@ -3,7 +3,7 @@ import ora from "ora";
|
|
|
3
3
|
import { spawnSync } from "node:child_process";
|
|
4
4
|
import { formatEther } from "viem";
|
|
5
5
|
import * as api from "../api.js";
|
|
6
|
-
import { parsePackageArg, prompt, resolveLatestVersion, detectPackageManager, } from "../utils.js";
|
|
6
|
+
import { parsePackageArg, prompt, resolveLatestVersion, detectPackageManager, openExternalUrl, } from "../utils.js";
|
|
7
7
|
import { auditCommand } from "./audit.js";
|
|
8
8
|
import { payViaWalletConnect, readAuditFee } from "../wallet/walletconnect.js";
|
|
9
9
|
import { streamAuditEvents } from "../stream.js";
|
|
@@ -30,6 +30,7 @@ function extractCapabilities(report) {
|
|
|
30
30
|
}
|
|
31
31
|
export async function installCommand(packageSpec, opts) {
|
|
32
32
|
const apiUrl = opts.api;
|
|
33
|
+
const webUrl = opts.web;
|
|
33
34
|
let parsed;
|
|
34
35
|
try {
|
|
35
36
|
parsed = parsePackageArg(packageSpec);
|
|
@@ -73,20 +74,25 @@ export async function installCommand(packageSpec, opts) {
|
|
|
73
74
|
console.log();
|
|
74
75
|
console.log(chalk.bold(" How do you want to pay for the audit?"));
|
|
75
76
|
console.log(" 1) Stripe (credit card)");
|
|
76
|
-
console.log(" 2)
|
|
77
|
-
console.log(" 3)
|
|
78
|
-
console.log(" 4)
|
|
77
|
+
console.log(" 2) Browser wallet — MetaMask/Rabby in Brave or Chrome");
|
|
78
|
+
console.log(" 3) WalletConnect — QR/mobile wallet");
|
|
79
|
+
console.log(" 4) Install without audit (at your own risk)");
|
|
80
|
+
console.log(" 5) Cancel");
|
|
79
81
|
console.log();
|
|
80
|
-
const choice = await prompt(" Choice [1/2/3/4]: ");
|
|
82
|
+
const choice = await prompt(" Choice [1/2/3/4/5]: ");
|
|
81
83
|
if (choice === "1") {
|
|
82
84
|
await runStripeAuditAndInstall(fullSpec, name, version, apiUrl);
|
|
83
85
|
return;
|
|
84
86
|
}
|
|
85
87
|
if (choice === "2") {
|
|
86
|
-
await
|
|
88
|
+
await runBrowserWalletAuditAndInstall(fullSpec, name, version, apiUrl, webUrl);
|
|
87
89
|
return;
|
|
88
90
|
}
|
|
89
91
|
if (choice === "3") {
|
|
92
|
+
await runCryptoAuditAndInstall(fullSpec, name, version, apiUrl);
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
if (choice === "4") {
|
|
90
96
|
console.log(chalk.yellow(" Installing without audit. Proceed at your own risk."));
|
|
91
97
|
process.exit(runInstall(fullSpec));
|
|
92
98
|
}
|
|
@@ -139,16 +145,86 @@ async function runStripeAuditAndInstall(fullSpec, name, version, apiUrl) {
|
|
|
139
145
|
}
|
|
140
146
|
await finalizeAfterAudit(fullSpec, name, version, apiUrl);
|
|
141
147
|
}
|
|
148
|
+
function buildBrowserWalletUrl(webUrl, packageName, version) {
|
|
149
|
+
const url = new URL("/pay", webUrl.replace(/\/+$/, ""));
|
|
150
|
+
url.searchParams.set("packageName", packageName);
|
|
151
|
+
url.searchParams.set("version", version);
|
|
152
|
+
url.searchParams.set("source", "cli");
|
|
153
|
+
return url.toString();
|
|
154
|
+
}
|
|
155
|
+
function sleep(ms) {
|
|
156
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
157
|
+
}
|
|
158
|
+
async function waitForPackageReport(apiUrl, packageName, version, timeoutMs) {
|
|
159
|
+
const deadline = Date.now() + timeoutMs;
|
|
160
|
+
while (Date.now() < deadline) {
|
|
161
|
+
const report = await api.getPackageReport(apiUrl, packageName, version);
|
|
162
|
+
if (report)
|
|
163
|
+
return report;
|
|
164
|
+
await sleep(3000);
|
|
165
|
+
}
|
|
166
|
+
return null;
|
|
167
|
+
}
|
|
168
|
+
async function runBrowserWalletAuditAndInstall(fullSpec, name, version, apiUrl, webUrl) {
|
|
169
|
+
const paymentUrl = buildBrowserWalletUrl(webUrl, name, version);
|
|
170
|
+
console.log();
|
|
171
|
+
console.log(chalk.bold(" Open this URL in Brave or Chrome with MetaMask/Rabby:"));
|
|
172
|
+
console.log(chalk.blue.underline(` ${paymentUrl}`));
|
|
173
|
+
console.log();
|
|
174
|
+
console.log(chalk.gray(" The browser page will connect your wallet, sign the Base Sepolia tx, and start the audit."));
|
|
175
|
+
console.log(chalk.gray(" Keep this terminal open; it will continue when the report is ready."));
|
|
176
|
+
console.log();
|
|
177
|
+
const openAnswer = await prompt(" Open it now in your default browser? (Y/n) ");
|
|
178
|
+
if (openAnswer !== "n" && openAnswer !== "no") {
|
|
179
|
+
const opened = openExternalUrl(paymentUrl);
|
|
180
|
+
if (!opened) {
|
|
181
|
+
console.log(chalk.yellow(" Could not open automatically. Copy the URL above."));
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
const spinner = ora(" Waiting for browser payment and audit report...").start();
|
|
185
|
+
let report = null;
|
|
186
|
+
try {
|
|
187
|
+
report = await waitForPackageReport(apiUrl, name, version, 30 * 60 * 1000);
|
|
188
|
+
}
|
|
189
|
+
catch (err) {
|
|
190
|
+
spinner.fail("Could not read report: " +
|
|
191
|
+
(err instanceof Error ? err.message : String(err)));
|
|
192
|
+
process.exit(1);
|
|
193
|
+
}
|
|
194
|
+
if (!report) {
|
|
195
|
+
spinner.fail("Timed out waiting for the browser audit report.");
|
|
196
|
+
console.log(chalk.gray(` Check ${webUrl.replace(/\/+$/, "")}/package/${encodeURIComponent(name)}`));
|
|
197
|
+
process.exit(1);
|
|
198
|
+
}
|
|
199
|
+
spinner.succeed("Audit report received");
|
|
200
|
+
await finalizeWithReport(fullSpec, report);
|
|
201
|
+
}
|
|
142
202
|
async function runCryptoAuditAndInstall(fullSpec, name, version, apiUrl) {
|
|
143
|
-
// 1. Read current fee from
|
|
203
|
+
// 1. Read current fee from the engine's public config, then fall back to
|
|
204
|
+
// direct contract read if the engine is older or config is unavailable.
|
|
144
205
|
let feeWei;
|
|
206
|
+
let contractAddress;
|
|
145
207
|
try {
|
|
146
|
-
|
|
208
|
+
const publicConfig = await api.getPublicConfig(apiUrl);
|
|
209
|
+
const contract = publicConfig.crypto?.contract;
|
|
210
|
+
const auditFeeWei = publicConfig.crypto?.auditFeeWei;
|
|
211
|
+
if (contract && /^0x[0-9a-fA-F]{40}$/.test(contract) && auditFeeWei) {
|
|
212
|
+
contractAddress = contract;
|
|
213
|
+
feeWei = BigInt(auditFeeWei);
|
|
214
|
+
}
|
|
215
|
+
else {
|
|
216
|
+
feeWei = await readAuditFee();
|
|
217
|
+
}
|
|
147
218
|
}
|
|
148
219
|
catch (err) {
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
220
|
+
try {
|
|
221
|
+
feeWei = await readAuditFee();
|
|
222
|
+
}
|
|
223
|
+
catch {
|
|
224
|
+
console.error(chalk.red("Could not read fee from engine or contract: " +
|
|
225
|
+
(err instanceof Error ? err.message : String(err))));
|
|
226
|
+
process.exit(1);
|
|
227
|
+
}
|
|
152
228
|
}
|
|
153
229
|
const feeDisplay = `${formatEther(feeWei)} ETH`;
|
|
154
230
|
const confirm = await prompt(chalk.yellow(` Pay ${feeDisplay} on Base Sepolia? (y/N) `));
|
|
@@ -157,7 +233,7 @@ async function runCryptoAuditAndInstall(fullSpec, name, version, apiUrl) {
|
|
|
157
233
|
process.exit(0);
|
|
158
234
|
}
|
|
159
235
|
// 2. WalletConnect → user signs → we get txHash
|
|
160
|
-
const result = await payViaWalletConnect(name, version, feeWei, feeDisplay);
|
|
236
|
+
const result = await payViaWalletConnect(name, version, feeWei, feeDisplay, contractAddress);
|
|
161
237
|
if (!result.paid || !result.txHash) {
|
|
162
238
|
console.log(chalk.red(" Payment failed, aborting."));
|
|
163
239
|
process.exit(1);
|
|
@@ -189,7 +265,10 @@ async function finalizeAfterAudit(fullSpec, name, version, apiUrl) {
|
|
|
189
265
|
console.log(chalk.red(" Audit finished but report not found."));
|
|
190
266
|
process.exit(1);
|
|
191
267
|
}
|
|
192
|
-
|
|
268
|
+
await finalizeWithReport(fullSpec, freshReport);
|
|
269
|
+
}
|
|
270
|
+
async function finalizeWithReport(fullSpec, report) {
|
|
271
|
+
const verdict = extractVerdict(report);
|
|
193
272
|
if (verdict === "SAFE") {
|
|
194
273
|
console.log(chalk.green("\n ✓ SAFE — proceeding with install"));
|
|
195
274
|
process.exit(runInstall(fullSpec));
|
package/dist/index.js
CHANGED
|
@@ -1,14 +1,25 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { Command } from "commander";
|
|
3
|
+
import { readFileSync } from "node:fs";
|
|
3
4
|
import { auditCommand } from "./commands/audit.js";
|
|
4
5
|
import { checkCommand } from "./commands/check.js";
|
|
5
6
|
import { installCommand } from "./commands/install.js";
|
|
7
|
+
function readPackageVersion() {
|
|
8
|
+
try {
|
|
9
|
+
const pkg = JSON.parse(readFileSync(new URL("../package.json", import.meta.url), "utf-8"));
|
|
10
|
+
return typeof pkg.version === "string" ? pkg.version : "0.0.0";
|
|
11
|
+
}
|
|
12
|
+
catch {
|
|
13
|
+
return "0.0.0";
|
|
14
|
+
}
|
|
15
|
+
}
|
|
6
16
|
const program = new Command();
|
|
7
17
|
program
|
|
8
18
|
.name("npmguard")
|
|
9
19
|
.description("NpmGuard CLI — audit npm packages for security issues")
|
|
10
|
-
.version(
|
|
11
|
-
.option("--api <url>", "NpmGuard engine API URL", process.env.NPMGUARD_API_URL ?? "https://npmguard.com")
|
|
20
|
+
.version(readPackageVersion())
|
|
21
|
+
.option("--api <url>", "NpmGuard engine API URL", process.env.NPMGUARD_API_URL ?? "https://npmguard.com")
|
|
22
|
+
.option("--web <url>", "NpmGuard web app URL for browser wallet payments", process.env.NPMGUARD_WEB_URL ?? "https://npmguard.com");
|
|
12
23
|
program
|
|
13
24
|
.command("audit")
|
|
14
25
|
.description("Pay for and run a security audit on an npm package")
|
|
@@ -23,8 +34,8 @@ program
|
|
|
23
34
|
.argument("<package>", "Package name, optionally with version (e.g. express@4.18.0)")
|
|
24
35
|
.option("-f, --force", "Install even if the package is flagged as dangerous")
|
|
25
36
|
.action(async (pkg, cmdOpts) => {
|
|
26
|
-
const
|
|
27
|
-
await installCommand(pkg, { api:
|
|
37
|
+
const opts = program.opts();
|
|
38
|
+
await installCommand(pkg, { api: opts.api, web: opts.web, force: cmdOpts.force });
|
|
28
39
|
});
|
|
29
40
|
program
|
|
30
41
|
.command("check")
|
package/dist/utils.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { createInterface } from "node:readline";
|
|
2
2
|
import { existsSync } from "node:fs";
|
|
3
3
|
import { join } from "node:path";
|
|
4
|
+
import { spawn } from "node:child_process";
|
|
4
5
|
export function parsePackageArg(pkg) {
|
|
5
6
|
if (pkg.startsWith("@")) {
|
|
6
7
|
const slashIndex = pkg.indexOf("/");
|
|
@@ -54,3 +55,27 @@ export function detectPackageManager(cwd = process.cwd()) {
|
|
|
54
55
|
return "yarn";
|
|
55
56
|
return "npm";
|
|
56
57
|
}
|
|
58
|
+
export function openExternalUrl(url) {
|
|
59
|
+
try {
|
|
60
|
+
const platform = process.platform;
|
|
61
|
+
const command = platform === "darwin"
|
|
62
|
+
? "open"
|
|
63
|
+
: platform === "win32"
|
|
64
|
+
? "cmd"
|
|
65
|
+
: "xdg-open";
|
|
66
|
+
const args = platform === "win32"
|
|
67
|
+
? ["/c", "start", "", url]
|
|
68
|
+
: [url];
|
|
69
|
+
const child = spawn(command, args, {
|
|
70
|
+
detached: true,
|
|
71
|
+
stdio: "ignore",
|
|
72
|
+
windowsHide: true,
|
|
73
|
+
});
|
|
74
|
+
child.on("error", () => { });
|
|
75
|
+
child.unref();
|
|
76
|
+
return true;
|
|
77
|
+
}
|
|
78
|
+
catch {
|
|
79
|
+
return false;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
@@ -14,7 +14,7 @@ function generateQrCode(text) {
|
|
|
14
14
|
});
|
|
15
15
|
});
|
|
16
16
|
}
|
|
17
|
-
export async function payViaWalletConnect(packageName, version, feeWei, feeDisplay) {
|
|
17
|
+
export async function payViaWalletConnect(packageName, version, feeWei, feeDisplay, contractAddress = AUDIT_REQUEST_ADDRESS_BASE_SEPOLIA) {
|
|
18
18
|
const calldata = encodeFunctionData({
|
|
19
19
|
abi: AUDIT_REQUEST_ABI,
|
|
20
20
|
functionName: "requestAudit",
|
|
@@ -63,6 +63,10 @@ export async function payViaWalletConnect(packageName, version, feeWei, feeDispl
|
|
|
63
63
|
console.log();
|
|
64
64
|
await generateQrCode(uri);
|
|
65
65
|
console.log();
|
|
66
|
+
console.log(chalk.cyan(" Desktop wallet? Paste this WalletConnect URI into your wallet:"));
|
|
67
|
+
console.log(chalk.gray(` ${uri}`));
|
|
68
|
+
console.log(chalk.gray(" Keep it private; it is only for this pairing session."));
|
|
69
|
+
console.log();
|
|
66
70
|
const pairSpinner = ora(" Waiting for wallet connection...").start();
|
|
67
71
|
const session = await approval();
|
|
68
72
|
const accounts = session.namespaces.eip155?.accounts ?? [];
|
|
@@ -84,7 +88,7 @@ export async function payViaWalletConnect(packageName, version, feeWei, feeDispl
|
|
|
84
88
|
params: [
|
|
85
89
|
{
|
|
86
90
|
from: sender,
|
|
87
|
-
to:
|
|
91
|
+
to: contractAddress,
|
|
88
92
|
data: calldata,
|
|
89
93
|
value: "0x" + feeWei.toString(16),
|
|
90
94
|
},
|
|
@@ -120,13 +124,13 @@ export async function payViaWalletConnect(packageName, version, feeWei, feeDispl
|
|
|
120
124
|
setTimeout(() => process.off("uncaughtException", wcErrorHandler), 5000);
|
|
121
125
|
}
|
|
122
126
|
}
|
|
123
|
-
export async function readAuditFee() {
|
|
127
|
+
export async function readAuditFee(contractAddress = AUDIT_REQUEST_ADDRESS_BASE_SEPOLIA) {
|
|
124
128
|
const publicClient = createPublicClient({
|
|
125
129
|
chain: baseSepolia,
|
|
126
130
|
transport: http(),
|
|
127
131
|
});
|
|
128
132
|
return (await publicClient.readContract({
|
|
129
|
-
address:
|
|
133
|
+
address: contractAddress,
|
|
130
134
|
abi: AUDIT_REQUEST_ABI,
|
|
131
135
|
functionName: "auditFee",
|
|
132
136
|
}));
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "npmguard-cli",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.3",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "NpmGuard CLI — check npm packages against NpmGuard security audits",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
"dist"
|
|
9
9
|
],
|
|
10
10
|
"bin": {
|
|
11
|
-
"npmguard": "
|
|
11
|
+
"npmguard": "dist/index.js"
|
|
12
12
|
},
|
|
13
13
|
"scripts": {
|
|
14
14
|
"build": "tsc"
|
|
@@ -27,7 +27,7 @@
|
|
|
27
27
|
"eventsource": "^2.0.2",
|
|
28
28
|
"ora": "^8.1.0",
|
|
29
29
|
"qrcode-terminal": "^0.12.0",
|
|
30
|
-
"viem": "^2.
|
|
30
|
+
"viem": "^2.52.2"
|
|
31
31
|
},
|
|
32
32
|
"devDependencies": {
|
|
33
33
|
"@types/node": "^22.0.0",
|