npmguard-cli 0.4.0 → 0.5.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.
@@ -1,7 +1,189 @@
1
1
  import chalk from "chalk";
2
2
  import ora from "ora";
3
+ import qrcode from "qrcode-terminal";
4
+ import { createInterface } from "node:readline";
3
5
  import { execSync } from "node:child_process";
6
+ import { createPublicClient, createWalletClient, http, formatEther, encodeFunctionData, } from "viem";
7
+ import { privateKeyToAccount } from "viem/accounts";
8
+ import { defineChain } from "viem";
9
+ import { SignClient } from "@walletconnect/sign-client";
10
+ import { AUDIT_REQUEST_ADDRESS_0G, AUDIT_REQUEST_ABI, } from "../contract.js";
11
+ const ogGalileo = defineChain({
12
+ id: 16602,
13
+ name: "0G-Galileo-Testnet",
14
+ nativeCurrency: { name: "0G", symbol: "0G", decimals: 18 },
15
+ rpcUrls: { default: { http: ["https://evmrpc-testnet.0g.ai"] } },
16
+ blockExplorers: { default: { name: "0G Explorer", url: "https://chainscan-galileo.0g.ai" } },
17
+ testnet: true,
18
+ });
4
19
  const IPFS_GATEWAY = "https://gateway.pinata.cloud/ipfs";
20
+ const ZERO_ADDRESS = "0x0000000000000000000000000000000000000000";
21
+ const DEFAULT_AUDIT_API_URL = "http://209.38.42.28:8000/audit";
22
+ const WALLETCONNECT_PROJECT_ID = process.env.WALLETCONNECT_PROJECT_ID ?? "d5eb170c427570e15ac00ae53acc93ba";
23
+ const OG_RPC = "https://evmrpc-testnet.0g.ai";
24
+ const BLOCK_EXPLORER = "https://chainscan-galileo.0g.ai";
25
+ function prompt(question) {
26
+ const rl = createInterface({ input: process.stdin, output: process.stdout });
27
+ return new Promise((resolve) => {
28
+ rl.question(question, (answer) => {
29
+ rl.close();
30
+ resolve(answer.trim().toLowerCase());
31
+ });
32
+ });
33
+ }
34
+ function generateQrCode(text) {
35
+ return new Promise((resolve) => {
36
+ qrcode.generate(text, { small: true }, (code) => {
37
+ console.log(code);
38
+ resolve();
39
+ });
40
+ });
41
+ }
42
+ async function askInstallWithoutAudit(packageSpec) {
43
+ const answer = await prompt(chalk.white(` Install from npm without audit? (y/n) `));
44
+ if (answer === "y" || answer === "yes") {
45
+ console.log();
46
+ execSync(`npm install ${packageSpec}`, { stdio: "inherit" });
47
+ }
48
+ }
49
+ async function requestAuditOnChain(packageName, version, privateKey) {
50
+ const account = privateKeyToAccount(privateKey);
51
+ const rpcUrl = OG_RPC;
52
+ const publicClient = createPublicClient({
53
+ chain: ogGalileo,
54
+ transport: http(rpcUrl),
55
+ });
56
+ const walletClient = createWalletClient({
57
+ account,
58
+ chain: ogGalileo,
59
+ transport: http(rpcUrl),
60
+ });
61
+ const auditFee = await publicClient.readContract({
62
+ address: AUDIT_REQUEST_ADDRESS_0G,
63
+ abi: AUDIT_REQUEST_ABI,
64
+ functionName: "auditFee",
65
+ });
66
+ const hash = await walletClient.writeContract({
67
+ address: AUDIT_REQUEST_ADDRESS_0G,
68
+ abi: AUDIT_REQUEST_ABI,
69
+ functionName: "requestAudit",
70
+ args: [packageName, version],
71
+ value: auditFee,
72
+ });
73
+ await publicClient.waitForTransactionReceipt({ hash });
74
+ return hash;
75
+ }
76
+ async function payViaWalletConnect(packageName, version, feeWei, feeDisplay) {
77
+ const calldata = encodeFunctionData({
78
+ abi: AUDIT_REQUEST_ABI,
79
+ functionName: "requestAudit",
80
+ args: [packageName, version],
81
+ });
82
+ let signClient = null;
83
+ // WalletConnect throws unhandled errors when MetaMask sends
84
+ // session events after the session is cleaned up — suppress them
85
+ const wcErrorHandler = (err) => {
86
+ if (err?.message?.includes("No matching key"))
87
+ return;
88
+ console.error(err);
89
+ process.exit(1);
90
+ };
91
+ process.on("uncaughtException", wcErrorHandler);
92
+ try {
93
+ const initSpinner = ora(" Connecting to WalletConnect...").start();
94
+ signClient = await SignClient.init({
95
+ projectId: WALLETCONNECT_PROJECT_ID,
96
+ metadata: {
97
+ name: "NpmGuard",
98
+ description: "NPM package security audit",
99
+ url: "https://npmguard.dev",
100
+ icons: [],
101
+ },
102
+ });
103
+ initSpinner.stop();
104
+ const { uri, approval } = await signClient.connect({
105
+ requiredNamespaces: {
106
+ eip155: {
107
+ methods: ["eth_sendTransaction"],
108
+ chains: ["eip155:16602"],
109
+ events: ["chainChanged", "accountsChanged"],
110
+ },
111
+ },
112
+ });
113
+ if (!uri) {
114
+ console.log(chalk.red(" Failed to generate WalletConnect URI"));
115
+ return false;
116
+ }
117
+ console.log();
118
+ console.log(chalk.cyan(` Scan with your wallet to connect:`));
119
+ console.log();
120
+ await generateQrCode(uri);
121
+ console.log();
122
+ const pairSpinner = ora(" Waiting for wallet connection...").start();
123
+ const session = await approval();
124
+ // Find the 0G Galileo account in approved namespaces
125
+ const accounts = session.namespaces.eip155?.accounts ?? [];
126
+ const ogAccount = accounts.find((a) => a.startsWith("eip155:16602:"));
127
+ const account = ogAccount
128
+ ? ogAccount.split(":")[2]
129
+ : accounts[0]?.split(":")[2];
130
+ if (!account) {
131
+ pairSpinner.fail("Wallet did not approve any accounts");
132
+ return false;
133
+ }
134
+ pairSpinner.succeed(`Connected: ${account.slice(0, 6)}...${account.slice(-4)}`);
135
+ console.log(chalk.cyan(` Confirm the ${feeDisplay} transaction in your wallet...`));
136
+ const txHash = await signClient.request({
137
+ topic: session.topic,
138
+ chainId: "eip155:16602",
139
+ request: {
140
+ method: "eth_sendTransaction",
141
+ params: [
142
+ {
143
+ from: account,
144
+ to: AUDIT_REQUEST_ADDRESS_0G,
145
+ data: calldata,
146
+ value: "0x" + feeWei.toString(16),
147
+ },
148
+ ],
149
+ },
150
+ });
151
+ const confirmSpinner = ora(" Waiting for on-chain confirmation...").start();
152
+ const ogClient = createPublicClient({
153
+ chain: ogGalileo,
154
+ transport: http(OG_RPC),
155
+ });
156
+ const receipt = await ogClient.waitForTransactionReceipt({
157
+ hash: txHash,
158
+ });
159
+ if (receipt.status === "success") {
160
+ confirmSpinner.succeed("Payment confirmed on-chain!");
161
+ console.log(chalk.gray(` Tx: ${BLOCK_EXPLORER}/tx/${txHash}`));
162
+ console.log();
163
+ return true;
164
+ }
165
+ else {
166
+ confirmSpinner.fail("Transaction reverted");
167
+ return false;
168
+ }
169
+ }
170
+ catch (err) {
171
+ const msg = err.message ?? String(err);
172
+ if (msg.includes("rejected") || msg.includes("denied")) {
173
+ console.log(chalk.yellow(" Transaction rejected by user."));
174
+ }
175
+ else {
176
+ console.log(chalk.red(` WalletConnect error: ${msg}`));
177
+ }
178
+ console.log();
179
+ return false;
180
+ }
181
+ finally {
182
+ signClient = null;
183
+ // Remove handler after a delay to catch late WC events
184
+ setTimeout(() => process.off("uncaughtException", wcErrorHandler), 5000);
185
+ }
186
+ }
5
187
  export async function installCommand(packageSpec, auditSource, force = false) {
6
188
  const atIndex = packageSpec.lastIndexOf("@");
7
189
  let packageName;
@@ -35,16 +217,144 @@ export async function installCommand(packageSpec, auditSource, force = false) {
35
217
  console.log();
36
218
  console.log(chalk.bold(` ${packageName}@${requestedVersion}`));
37
219
  console.log();
38
- // No audit found — fallback to npm
220
+ // ─── No audit found ───────────────────────────────────────────────
39
221
  if (!audit) {
40
222
  console.log(chalk.gray(` NOT AUDITED — no NpmGuard record found for this version.`));
41
223
  console.log();
42
- console.log(chalk.gray(" Falling back to npm install..."));
43
- console.log();
44
- execSync(`npm install ${packageSpec}`, { stdio: "inherit" });
45
- return;
224
+ const privateKey = process.env.NPMGUARD_PRIVATE_KEY;
225
+ const contractDeployed = AUDIT_REQUEST_ADDRESS_0G !== ZERO_ADDRESS;
226
+ if (contractDeployed) {
227
+ const publicClient = createPublicClient({
228
+ chain: ogGalileo,
229
+ transport: http(OG_RPC),
230
+ });
231
+ // Check if user already paid (previous attempt where audit engine failed)
232
+ let alreadyPaid = false;
233
+ try {
234
+ alreadyPaid = (await publicClient.readContract({
235
+ address: AUDIT_REQUEST_ADDRESS_0G,
236
+ abi: AUDIT_REQUEST_ABI,
237
+ functionName: "isRequested",
238
+ args: [packageName, requestedVersion],
239
+ }));
240
+ }
241
+ catch {
242
+ // can't read — assume not paid
243
+ }
244
+ if (alreadyPaid) {
245
+ console.log(chalk.cyan(` Already paid on-chain — re-triggering audit...`));
246
+ console.log();
247
+ }
248
+ else {
249
+ // Read fee for display
250
+ let feeDisplay = "0.01 0G";
251
+ let feeWei = 10000000000000000n;
252
+ try {
253
+ feeWei = (await publicClient.readContract({
254
+ address: AUDIT_REQUEST_ADDRESS_0G,
255
+ abi: AUDIT_REQUEST_ABI,
256
+ functionName: "auditFee",
257
+ }));
258
+ feeDisplay = `${formatEther(feeWei)} 0G`;
259
+ }
260
+ catch { }
261
+ const wantAudit = await prompt(chalk.yellow(` Request on-chain audit for ${feeDisplay}? (y/n) `));
262
+ if (wantAudit !== "y" && wantAudit !== "yes") {
263
+ return askInstallWithoutAudit(packageSpec);
264
+ }
265
+ // Ask how to pay
266
+ console.log();
267
+ console.log(chalk.bold(` How to pay?`));
268
+ if (privateKey) {
269
+ console.log(` 1) Wallet (NPMGUARD_PRIVATE_KEY)`);
270
+ console.log(` 2) WalletConnect (mobile wallet)`);
271
+ console.log(` 3) Back`);
272
+ }
273
+ else {
274
+ console.log(` 1) WalletConnect (mobile wallet)`);
275
+ console.log(` 2) Back`);
276
+ }
277
+ console.log();
278
+ const choice = await prompt(` Choice: `);
279
+ const backChoice = privateKey ? "3" : "2";
280
+ if (choice === backChoice) {
281
+ return askInstallWithoutAudit(packageSpec);
282
+ }
283
+ if (privateKey && choice === "1") {
284
+ const txSpinner = ora(" Sending payment transaction...").start();
285
+ try {
286
+ const txHash = await requestAuditOnChain(packageName, requestedVersion, privateKey);
287
+ txSpinner.succeed("Payment confirmed on-chain!");
288
+ console.log(chalk.gray(` Tx: ${BLOCK_EXPLORER}/tx/${txHash}`));
289
+ console.log();
290
+ }
291
+ catch (err) {
292
+ txSpinner.fail("Transaction failed");
293
+ console.log(chalk.red(` ${err.shortMessage ?? err.message}`));
294
+ console.log();
295
+ return askInstallWithoutAudit(packageSpec);
296
+ }
297
+ }
298
+ else if ((privateKey && choice === "2") || (!privateKey && choice === "1")) {
299
+ const paid = await payViaWalletConnect(packageName, requestedVersion, feeWei, feeDisplay);
300
+ if (!paid)
301
+ return askInstallWithoutAudit(packageSpec);
302
+ }
303
+ else {
304
+ return askInstallWithoutAudit(packageSpec);
305
+ }
306
+ }
307
+ // Trigger audit engine
308
+ const auditApiUrl = process.env.NPMGUARD_AUDIT_API_URL ?? DEFAULT_AUDIT_API_URL;
309
+ const auditSpinner = ora(" Running security audit...").start();
310
+ try {
311
+ const resp = await fetch(auditApiUrl, {
312
+ method: "POST",
313
+ headers: { "Content-Type": "application/json" },
314
+ body: JSON.stringify({ packageName, version: requestedVersion }),
315
+ });
316
+ if (!resp.ok)
317
+ throw new Error(`Audit engine returned ${resp.status}`);
318
+ const result = await resp.json();
319
+ auditSpinner.stop();
320
+ console.log();
321
+ const verdict = (result.verdict ?? "UNKNOWN").toUpperCase();
322
+ const capabilities = result.capabilities ?? [];
323
+ if (verdict === "SAFE") {
324
+ console.log(chalk.green(` Verdict: SAFE`));
325
+ }
326
+ else if (verdict === "DANGEROUS") {
327
+ console.log(chalk.red(` Verdict: DANGEROUS`));
328
+ }
329
+ else {
330
+ console.log(chalk.yellow(` Verdict: ${verdict}`));
331
+ }
332
+ if (capabilities.length > 0) {
333
+ console.log(chalk.gray(` Capabilities: ${capabilities.join(", ")}`));
334
+ }
335
+ console.log();
336
+ if (verdict === "DANGEROUS" && !force) {
337
+ console.log(chalk.red.bold(" Installation blocked. This package is dangerous."));
338
+ console.log(chalk.gray(" Use --force to install anyway."));
339
+ console.log();
340
+ return;
341
+ }
342
+ console.log(chalk.gray(" Installing from npm..."));
343
+ console.log();
344
+ execSync(`npm install ${packageSpec}`, { stdio: "inherit" });
345
+ }
346
+ catch (err) {
347
+ auditSpinner.fail("Audit engine unreachable");
348
+ console.log(chalk.gray(` ${err.message ?? err}`));
349
+ console.log();
350
+ return askInstallWithoutAudit(packageSpec);
351
+ }
352
+ return;
353
+ }
354
+ // Contract not deployed
355
+ return askInstallWithoutAudit(packageSpec);
46
356
  }
47
- // Show verdict
357
+ // ─── Audit found — show verdict ───────────────────────────────────
48
358
  if (audit.verdict === "SAFE") {
49
359
  console.log(chalk.green(` SAFE (score: ${audit.score})`));
50
360
  }
@@ -82,7 +392,6 @@ export async function installCommand(packageSpec, auditSource, force = false) {
82
392
  }
83
393
  }
84
394
  else {
85
- // No sourceCid — install from npm
86
395
  console.log(chalk.gray(" No IPFS source available, installing from npm..."));
87
396
  console.log();
88
397
  execSync(`npm install ${packageSpec}`, { stdio: "inherit" });
@@ -0,0 +1,121 @@
1
+ export declare const AUDIT_REQUEST_ADDRESS: `0x${string}`;
2
+ export declare const AUDIT_REQUEST_ADDRESS_0G: `0x${string}`;
3
+ export declare const AUDIT_REQUEST_ABI: readonly [{
4
+ readonly inputs: readonly [{
5
+ readonly name: "_auditFee";
6
+ readonly type: "uint256";
7
+ }];
8
+ readonly stateMutability: "nonpayable";
9
+ readonly type: "constructor";
10
+ }, {
11
+ readonly anonymous: false;
12
+ readonly inputs: readonly [{
13
+ readonly indexed: false;
14
+ readonly name: "packageName";
15
+ readonly type: "string";
16
+ }, {
17
+ readonly indexed: false;
18
+ readonly name: "version";
19
+ readonly type: "string";
20
+ }, {
21
+ readonly indexed: true;
22
+ readonly name: "requester";
23
+ readonly type: "address";
24
+ }];
25
+ readonly name: "AuditRequested";
26
+ readonly type: "event";
27
+ }, {
28
+ readonly anonymous: false;
29
+ readonly inputs: readonly [{
30
+ readonly indexed: true;
31
+ readonly name: "key";
32
+ readonly type: "bytes32";
33
+ }, {
34
+ readonly indexed: true;
35
+ readonly name: "requester";
36
+ readonly type: "address";
37
+ }];
38
+ readonly name: "AuditRequestedByKey";
39
+ readonly type: "event";
40
+ }, {
41
+ readonly inputs: readonly [{
42
+ readonly name: "packageName";
43
+ readonly type: "string";
44
+ }, {
45
+ readonly name: "version";
46
+ readonly type: "string";
47
+ }];
48
+ readonly name: "requestAudit";
49
+ readonly outputs: readonly [];
50
+ readonly stateMutability: "payable";
51
+ readonly type: "function";
52
+ }, {
53
+ readonly inputs: readonly [{
54
+ readonly name: "key";
55
+ readonly type: "bytes32";
56
+ }];
57
+ readonly name: "requestAuditByKey";
58
+ readonly outputs: readonly [];
59
+ readonly stateMutability: "payable";
60
+ readonly type: "function";
61
+ }, {
62
+ readonly inputs: readonly [];
63
+ readonly name: "auditFee";
64
+ readonly outputs: readonly [{
65
+ readonly name: "";
66
+ readonly type: "uint256";
67
+ }];
68
+ readonly stateMutability: "view";
69
+ readonly type: "function";
70
+ }, {
71
+ readonly inputs: readonly [];
72
+ readonly name: "owner";
73
+ readonly outputs: readonly [{
74
+ readonly name: "";
75
+ readonly type: "address";
76
+ }];
77
+ readonly stateMutability: "view";
78
+ readonly type: "function";
79
+ }, {
80
+ readonly inputs: readonly [{
81
+ readonly name: "packageName";
82
+ readonly type: "string";
83
+ }, {
84
+ readonly name: "version";
85
+ readonly type: "string";
86
+ }];
87
+ readonly name: "isRequested";
88
+ readonly outputs: readonly [{
89
+ readonly name: "";
90
+ readonly type: "bool";
91
+ }];
92
+ readonly stateMutability: "view";
93
+ readonly type: "function";
94
+ }, {
95
+ readonly inputs: readonly [{
96
+ readonly name: "";
97
+ readonly type: "bytes32";
98
+ }];
99
+ readonly name: "requested";
100
+ readonly outputs: readonly [{
101
+ readonly name: "";
102
+ readonly type: "bool";
103
+ }];
104
+ readonly stateMutability: "view";
105
+ readonly type: "function";
106
+ }, {
107
+ readonly inputs: readonly [{
108
+ readonly name: "_fee";
109
+ readonly type: "uint256";
110
+ }];
111
+ readonly name: "setFee";
112
+ readonly outputs: readonly [];
113
+ readonly stateMutability: "nonpayable";
114
+ readonly type: "function";
115
+ }, {
116
+ readonly inputs: readonly [];
117
+ readonly name: "withdraw";
118
+ readonly outputs: readonly [];
119
+ readonly stateMutability: "nonpayable";
120
+ readonly type: "function";
121
+ }];
@@ -0,0 +1,92 @@
1
+ // NpmGuardAuditRequest contract — deployed on Sepolia + 0G Galileo Testnet
2
+ // Update these addresses after running: cd contracts && npm run deploy
3
+ export const AUDIT_REQUEST_ADDRESS = "0x4bbaf196bde9e02594631e03c28ebe16719214f3"; // Sepolia
4
+ export const AUDIT_REQUEST_ADDRESS_0G = "0x1201448ae5f00e1783036439569e71ab3757d0de"; // 0G Galileo Testnet
5
+ export const AUDIT_REQUEST_ABI = [
6
+ {
7
+ inputs: [{ name: "_auditFee", type: "uint256" }],
8
+ stateMutability: "nonpayable",
9
+ type: "constructor",
10
+ },
11
+ {
12
+ anonymous: false,
13
+ inputs: [
14
+ { indexed: false, name: "packageName", type: "string" },
15
+ { indexed: false, name: "version", type: "string" },
16
+ { indexed: true, name: "requester", type: "address" },
17
+ ],
18
+ name: "AuditRequested",
19
+ type: "event",
20
+ },
21
+ {
22
+ anonymous: false,
23
+ inputs: [
24
+ { indexed: true, name: "key", type: "bytes32" },
25
+ { indexed: true, name: "requester", type: "address" },
26
+ ],
27
+ name: "AuditRequestedByKey",
28
+ type: "event",
29
+ },
30
+ {
31
+ inputs: [
32
+ { name: "packageName", type: "string" },
33
+ { name: "version", type: "string" },
34
+ ],
35
+ name: "requestAudit",
36
+ outputs: [],
37
+ stateMutability: "payable",
38
+ type: "function",
39
+ },
40
+ {
41
+ inputs: [{ name: "key", type: "bytes32" }],
42
+ name: "requestAuditByKey",
43
+ outputs: [],
44
+ stateMutability: "payable",
45
+ type: "function",
46
+ },
47
+ {
48
+ inputs: [],
49
+ name: "auditFee",
50
+ outputs: [{ name: "", type: "uint256" }],
51
+ stateMutability: "view",
52
+ type: "function",
53
+ },
54
+ {
55
+ inputs: [],
56
+ name: "owner",
57
+ outputs: [{ name: "", type: "address" }],
58
+ stateMutability: "view",
59
+ type: "function",
60
+ },
61
+ {
62
+ inputs: [
63
+ { name: "packageName", type: "string" },
64
+ { name: "version", type: "string" },
65
+ ],
66
+ name: "isRequested",
67
+ outputs: [{ name: "", type: "bool" }],
68
+ stateMutability: "view",
69
+ type: "function",
70
+ },
71
+ {
72
+ inputs: [{ name: "", type: "bytes32" }],
73
+ name: "requested",
74
+ outputs: [{ name: "", type: "bool" }],
75
+ stateMutability: "view",
76
+ type: "function",
77
+ },
78
+ {
79
+ inputs: [{ name: "_fee", type: "uint256" }],
80
+ name: "setFee",
81
+ outputs: [],
82
+ stateMutability: "nonpayable",
83
+ type: "function",
84
+ },
85
+ {
86
+ inputs: [],
87
+ name: "withdraw",
88
+ outputs: [],
89
+ stateMutability: "nonpayable",
90
+ type: "function",
91
+ },
92
+ ];
@@ -1,16 +1,25 @@
1
1
  import { createPublicClient, http } from "viem";
2
2
  import { sepolia } from "viem/chains";
3
- const client = createPublicClient({
4
- chain: sepolia,
5
- transport: http("https://ethereum-sepolia-rpc.publicnode.com"),
6
- });
3
+ const RPC_URLS = process.env.SEPOLIA_RPC_URL
4
+ ? [process.env.SEPOLIA_RPC_URL]
5
+ : [
6
+ "https://ethereum-sepolia-rpc.publicnode.com",
7
+ "https://rpc.sepolia.org",
8
+ "https://sepolia.drpc.org",
9
+ ];
10
+ function makeClient(url) {
11
+ return createPublicClient({ chain: sepolia, transport: http(url) });
12
+ }
7
13
  async function getText(ensName, key) {
8
- try {
9
- return await client.getEnsText({ name: ensName, key });
10
- }
11
- catch {
12
- return null;
14
+ for (const url of RPC_URLS) {
15
+ try {
16
+ return await makeClient(url).getEnsText({ name: ensName, key });
17
+ }
18
+ catch {
19
+ continue;
20
+ }
13
21
  }
22
+ return null;
14
23
  }
15
24
  export class ENSAuditSource {
16
25
  async getAudit(packageName, version) {
package/dist/index.d.ts CHANGED
@@ -1,2 +1,2 @@
1
1
  #!/usr/bin/env node
2
- export {};
2
+ import "dotenv/config";
package/dist/index.js CHANGED
@@ -1,4 +1,5 @@
1
1
  #!/usr/bin/env node
2
+ import "dotenv/config";
2
3
  import { Command } from "commander";
3
4
  import { resolve } from "node:path";
4
5
  import { ENSAuditSource } from "./ens-source.js";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "npmguard-cli",
3
- "version": "0.4.0",
3
+ "version": "0.5.0",
4
4
  "type": "module",
5
5
  "description": "Check npm packages against NpmGuard security audits on ENS before installing",
6
6
  "bin": "./dist/index.js",
@@ -22,10 +22,13 @@
22
22
  ],
23
23
  "license": "MIT",
24
24
  "dependencies": {
25
+ "@walletconnect/sign-client": "^2.23.9",
25
26
  "chalk": "^5.4.0",
26
27
  "cli-table3": "^0.6.5",
27
28
  "commander": "^13.1.0",
29
+ "dotenv": "^17.4.0",
28
30
  "ora": "^8.2.0",
31
+ "qrcode-terminal": "^0.12.0",
29
32
  "viem": "^2.34.0"
30
33
  },
31
34
  "devDependencies": {