@net-protocol/cli 0.1.7 → 0.1.9

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
@@ -364,6 +364,8 @@ Profile operations for managing your Net Protocol profile.
364
364
  - `profile set-picture` - Set your profile picture URL
365
365
  - `profile set-x-username` - Set your X (Twitter) username
366
366
  - `profile set-bio` - Set your profile bio
367
+ - `profile set-canvas` - Set your profile canvas (HTML content)
368
+ - `profile get-canvas` - Get profile canvas for an address
367
369
 
368
370
  ##### Profile Get
369
371
 
@@ -513,6 +515,99 @@ netp profile set-bio \
513
515
  --encode-only
514
516
  ```
515
517
 
518
+ ##### Profile Set Canvas
519
+
520
+ Set your profile canvas (HTML content, max 60KB). Canvas content is stored using ChunkedStorage with gzip compression.
521
+
522
+ ```bash
523
+ netp profile set-canvas \
524
+ --file <path> | --content <html> \
525
+ [--private-key <0x...>] \
526
+ [--chain-id <8453|1|...>] \
527
+ [--rpc-url <custom-rpc>] \
528
+ [--encode-only]
529
+ ```
530
+
531
+ **Profile Set Canvas Arguments:**
532
+
533
+ - `--file` (optional): Path to file containing canvas content (HTML, images, etc.)
534
+ - `--content` (optional): HTML content for canvas (inline). Must provide either `--file` or `--content`, but not both.
535
+ - `--private-key` (optional): Private key. Can also be set via `NET_PRIVATE_KEY` environment variable
536
+ - `--chain-id` (optional): Chain ID. Can also be set via `NET_CHAIN_ID` environment variable
537
+ - `--rpc-url` (optional): Custom RPC URL. Can also be set via `NET_RPC_URL` environment variable
538
+ - `--encode-only` (optional): Output transaction data as JSON instead of executing
539
+
540
+ **Notes:**
541
+ - Maximum canvas size is 60KB
542
+ - Binary files (images) are automatically converted to data URIs
543
+ - Content is compressed using gzip before storage
544
+
545
+ **Example:**
546
+
547
+ ```bash
548
+ # Set canvas from file
549
+ netp profile set-canvas \
550
+ --file ./my-canvas.html \
551
+ --chain-id 8453
552
+
553
+ # Set canvas from inline content
554
+ netp profile set-canvas \
555
+ --content "<html><body><h1>My Profile</h1></body></html>" \
556
+ --chain-id 8453
557
+
558
+ # Encode-only
559
+ netp profile set-canvas \
560
+ --file ./my-canvas.html \
561
+ --chain-id 8453 \
562
+ --encode-only
563
+ ```
564
+
565
+ ##### Profile Get Canvas
566
+
567
+ Get profile canvas for an address.
568
+
569
+ ```bash
570
+ netp profile get-canvas \
571
+ --address <wallet-address> \
572
+ [--output <path>] \
573
+ [--chain-id <8453|1|...>] \
574
+ [--rpc-url <custom-rpc>] \
575
+ [--json]
576
+ ```
577
+
578
+ **Profile Get Canvas Arguments:**
579
+
580
+ - `--address` (required): Wallet address to get canvas for
581
+ - `--output` (optional): Write canvas content to file instead of stdout
582
+ - `--chain-id` (optional): Chain ID. Can also be set via `NET_CHAIN_ID` environment variable
583
+ - `--rpc-url` (optional): Custom RPC URL. Can also be set via `NET_RPC_URL` environment variable
584
+ - `--json` (optional): Output in JSON format (includes metadata like size and type)
585
+
586
+ **Notes:**
587
+ - Binary content (data URIs) is automatically converted to binary files when using `--output`
588
+ - JSON output includes: canvas content, filename, size, and whether it's a data URI
589
+
590
+ **Example:**
591
+
592
+ ```bash
593
+ # Output to stdout
594
+ netp profile get-canvas \
595
+ --address 0x1234567890abcdef1234567890abcdef12345678 \
596
+ --chain-id 8453
597
+
598
+ # Save to file
599
+ netp profile get-canvas \
600
+ --address 0x1234567890abcdef1234567890abcdef12345678 \
601
+ --output ./canvas.html \
602
+ --chain-id 8453
603
+
604
+ # JSON output
605
+ netp profile get-canvas \
606
+ --address 0x1234567890abcdef1234567890abcdef12345678 \
607
+ --chain-id 8453 \
608
+ --json
609
+ ```
610
+
516
611
  #### Info Command
517
612
 
518
613
  Show contract info and stats.
@@ -1,18 +1,20 @@
1
1
  #!/usr/bin/env node
2
+ import { ProxyAgent, setGlobalDispatcher } from 'undici';
2
3
  import 'dotenv/config';
3
4
  import { Command } from 'commander';
4
5
  import { createRequire } from 'module';
5
6
  import chalk4 from 'chalk';
6
- import * as fs from 'fs';
7
+ import * as fs2 from 'fs';
7
8
  import { readFileSync } from 'fs';
8
- import { StorageClient, detectFileTypeFromBase64, base64ToDataUri, shouldSuggestXmlStorage, getStorageKeyBytes, encodeStorageKeyForUrl, STORAGE_CONTRACT as STORAGE_CONTRACT$1, CHUNKED_STORAGE_CONTRACT } from '@net-protocol/storage';
9
+ import { StorageClient, detectFileTypeFromBase64, base64ToDataUri, shouldSuggestXmlStorage, getStorageKeyBytes, encodeStorageKeyForUrl, chunkDataForStorage, CHUNKED_STORAGE_CONTRACT, STORAGE_CONTRACT as STORAGE_CONTRACT$1 } from '@net-protocol/storage';
9
10
  import { stringToHex, createWalletClient, http, hexToString, parseEther, encodeFunctionData, publicActions, defineChain } from 'viem';
10
11
  import { privateKeyToAccount } from 'viem/accounts';
11
- import { getNetContract, getChainName, getPublicClient, getChainRpcUrls, NetClient } from '@net-protocol/core';
12
+ import { getNetContract, getChainName, getPublicClient, getChainRpcUrls, NetClient, toBytes32 } from '@net-protocol/core';
12
13
  import { createRelayX402Client, createRelaySession, checkBackendWalletBalance, fundBackendWallet, batchTransactions, submitTransactionsViaRelay, waitForConfirmations, retryFailedTransactions as retryFailedTransactions$1 } from '@net-protocol/relay';
13
14
  import { isNetrSupportedChain, NetrClient } from '@net-protocol/netr';
14
- import { PROFILE_PICTURE_STORAGE_KEY, PROFILE_METADATA_STORAGE_KEY, parseProfileMetadata, isValidUrl, getProfilePictureStorageArgs, STORAGE_CONTRACT, isValidXUsername, getXUsernameStorageArgs, isValidBio, getProfileMetadataStorageArgs } from '@net-protocol/profiles';
15
+ import { PROFILE_PICTURE_STORAGE_KEY, PROFILE_METADATA_STORAGE_KEY, parseProfileMetadata, PROFILE_CANVAS_STORAGE_KEY, isValidUrl, getProfilePictureStorageArgs, STORAGE_CONTRACT, isValidXUsername, getXUsernameStorageArgs, isValidBio, getProfileMetadataStorageArgs } from '@net-protocol/profiles';
15
16
  import { base } from 'viem/chains';
17
+ import * as path from 'path';
16
18
 
17
19
  function getRequiredChainId(optionValue) {
18
20
  const chainId = optionValue || (process.env.NET_CHAIN_ID ? parseInt(process.env.NET_CHAIN_ID, 10) : void 0);
@@ -1191,7 +1193,7 @@ async function encodeStorageUpload(options) {
1191
1193
  } else {
1192
1194
  operatorAddress = "0x0000000000000000000000000000000000000000";
1193
1195
  }
1194
- const fileContent = fs.readFileSync(options.filePath, "utf-8");
1196
+ const fileContent = fs2.readFileSync(options.filePath, "utf-8");
1195
1197
  const fileSize = Buffer.byteLength(fileContent, "utf-8");
1196
1198
  const client = new StorageClient({
1197
1199
  chainId: readOnlyOptions.chainId
@@ -1475,8 +1477,8 @@ function registerStorageCommand(program2) {
1475
1477
  console.log(chalk4.blue(`\u{1F4C1} Reading file: ${options.file}`));
1476
1478
  console.log(chalk4.blue(`\u{1F517} Using relay API: ${options.apiUrl}`));
1477
1479
  const result = await uploadFileWithRelay(uploadRelayOptions);
1478
- const { privateKeyToAccount: privateKeyToAccount9 } = await import('viem/accounts');
1479
- const userAccount = privateKeyToAccount9(commonOptions.privateKey);
1480
+ const { privateKeyToAccount: privateKeyToAccount10 } = await import('viem/accounts');
1481
+ const userAccount = privateKeyToAccount10(commonOptions.privateKey);
1480
1482
  const storageUrl = generateStorageUrl(
1481
1483
  userAccount.address,
1482
1484
  commonOptions.chainId,
@@ -2274,7 +2276,24 @@ async function executeProfileGet(options) {
2274
2276
  throw error;
2275
2277
  }
2276
2278
  }
2277
- const hasProfile = profilePicture || xUsername || bio;
2279
+ let canvasSize;
2280
+ let canvasIsDataUri = false;
2281
+ try {
2282
+ const canvasResult = await client.readChunkedStorage({
2283
+ key: PROFILE_CANVAS_STORAGE_KEY,
2284
+ operator: options.address
2285
+ });
2286
+ if (canvasResult.data) {
2287
+ canvasSize = canvasResult.data.length;
2288
+ canvasIsDataUri = canvasResult.data.startsWith("data:");
2289
+ }
2290
+ } catch (error) {
2291
+ const errorMessage = error instanceof Error ? error.message : String(error);
2292
+ if (errorMessage !== "ChunkedStorage metadata not found" && !errorMessage.includes("not found")) {
2293
+ throw error;
2294
+ }
2295
+ }
2296
+ const hasProfile = profilePicture || xUsername || bio || canvasSize;
2278
2297
  if (options.json) {
2279
2298
  const output = {
2280
2299
  address: options.address,
@@ -2282,6 +2301,7 @@ async function executeProfileGet(options) {
2282
2301
  profilePicture: profilePicture || null,
2283
2302
  xUsername: xUsername || null,
2284
2303
  bio: bio || null,
2304
+ canvas: canvasSize ? { size: canvasSize, isDataUri: canvasIsDataUri } : null,
2285
2305
  hasProfile
2286
2306
  };
2287
2307
  console.log(JSON.stringify(output, null, 2));
@@ -2299,6 +2319,9 @@ async function executeProfileGet(options) {
2299
2319
  console.log(
2300
2320
  ` ${chalk4.cyan("Bio:")} ${bio || chalk4.gray("(not set)")}`
2301
2321
  );
2322
+ console.log(
2323
+ ` ${chalk4.cyan("Canvas:")} ${canvasSize ? `${canvasSize} bytes${canvasIsDataUri ? " (data URI)" : ""}` : chalk4.gray("(not set)")}`
2324
+ );
2302
2325
  if (!hasProfile) {
2303
2326
  console.log(chalk4.yellow("\n No profile data found for this address."));
2304
2327
  }
@@ -2532,6 +2555,251 @@ Bio updated successfully!
2532
2555
  );
2533
2556
  }
2534
2557
  }
2558
+ var MAX_CANVAS_SIZE = 60 * 1024;
2559
+ var CANVAS_FILENAME = "profile-compressed.html";
2560
+ function isBinaryContent(buffer) {
2561
+ const sampleSize = Math.min(buffer.length, 8192);
2562
+ for (let i = 0; i < sampleSize; i++) {
2563
+ const byte = buffer[i];
2564
+ if (byte === 0) return true;
2565
+ if (byte < 32 && byte !== 9 && byte !== 10 && byte !== 13) return true;
2566
+ }
2567
+ return false;
2568
+ }
2569
+ function getMimeType(filePath) {
2570
+ const ext = path.extname(filePath).toLowerCase();
2571
+ const mimeTypes = {
2572
+ ".png": "image/png",
2573
+ ".jpg": "image/jpeg",
2574
+ ".jpeg": "image/jpeg",
2575
+ ".gif": "image/gif",
2576
+ ".webp": "image/webp",
2577
+ ".svg": "image/svg+xml",
2578
+ ".pdf": "application/pdf",
2579
+ ".html": "text/html",
2580
+ ".htm": "text/html",
2581
+ ".css": "text/css",
2582
+ ".js": "application/javascript",
2583
+ ".json": "application/json",
2584
+ ".txt": "text/plain"
2585
+ };
2586
+ return mimeTypes[ext] || "application/octet-stream";
2587
+ }
2588
+ function bufferToDataUri(buffer, mimeType) {
2589
+ const base64 = buffer.toString("base64");
2590
+ return `data:${mimeType};base64,${base64}`;
2591
+ }
2592
+ async function executeProfileSetCanvas(options) {
2593
+ if (!options.file && !options.content) {
2594
+ exitWithError(
2595
+ "Must provide either --file or --content to set canvas content."
2596
+ );
2597
+ }
2598
+ if (options.file && options.content) {
2599
+ exitWithError("Cannot provide both --file and --content. Choose one.");
2600
+ }
2601
+ let canvasContent;
2602
+ if (options.file) {
2603
+ const filePath = path.resolve(options.file);
2604
+ if (!fs2.existsSync(filePath)) {
2605
+ exitWithError(`File not found: ${filePath}`);
2606
+ }
2607
+ const buffer = fs2.readFileSync(filePath);
2608
+ if (buffer.length > MAX_CANVAS_SIZE) {
2609
+ exitWithError(
2610
+ `File too large: ${buffer.length} bytes exceeds maximum of ${MAX_CANVAS_SIZE} bytes (60KB).`
2611
+ );
2612
+ }
2613
+ if (isBinaryContent(buffer)) {
2614
+ const mimeType = getMimeType(filePath);
2615
+ canvasContent = bufferToDataUri(buffer, mimeType);
2616
+ } else {
2617
+ canvasContent = buffer.toString("utf-8");
2618
+ }
2619
+ } else {
2620
+ canvasContent = options.content;
2621
+ const contentSize = Buffer.byteLength(canvasContent, "utf-8");
2622
+ if (contentSize > MAX_CANVAS_SIZE) {
2623
+ exitWithError(
2624
+ `Content too large: ${contentSize} bytes exceeds maximum of ${MAX_CANVAS_SIZE} bytes (60KB).`
2625
+ );
2626
+ }
2627
+ }
2628
+ const bytesKey = toBytes32(PROFILE_CANVAS_STORAGE_KEY);
2629
+ const chunks = chunkDataForStorage(canvasContent);
2630
+ if (options.encodeOnly) {
2631
+ const readOnlyOptions = parseReadOnlyOptions({
2632
+ chainId: options.chainId,
2633
+ rpcUrl: options.rpcUrl
2634
+ });
2635
+ const encoded = encodeTransaction(
2636
+ {
2637
+ to: CHUNKED_STORAGE_CONTRACT.address,
2638
+ abi: CHUNKED_STORAGE_CONTRACT.abi,
2639
+ functionName: "put",
2640
+ args: [bytesKey, CANVAS_FILENAME, chunks]
2641
+ },
2642
+ readOnlyOptions.chainId
2643
+ );
2644
+ console.log(JSON.stringify(encoded, null, 2));
2645
+ return;
2646
+ }
2647
+ const commonOptions = parseCommonOptions(
2648
+ {
2649
+ privateKey: options.privateKey,
2650
+ chainId: options.chainId,
2651
+ rpcUrl: options.rpcUrl
2652
+ },
2653
+ true
2654
+ // supports --encode-only
2655
+ );
2656
+ try {
2657
+ const account = privateKeyToAccount(commonOptions.privateKey);
2658
+ const rpcUrls = getChainRpcUrls({
2659
+ chainId: commonOptions.chainId,
2660
+ rpcUrl: commonOptions.rpcUrl
2661
+ });
2662
+ const client = createWalletClient({
2663
+ account,
2664
+ chain: base,
2665
+ // TODO: Support other chains
2666
+ transport: http(rpcUrls[0])
2667
+ }).extend(publicActions);
2668
+ console.log(chalk4.blue(`Setting profile canvas...`));
2669
+ console.log(
2670
+ chalk4.gray(` Content size: ${Buffer.byteLength(canvasContent)} bytes`)
2671
+ );
2672
+ console.log(chalk4.gray(` Chunks: ${chunks.length}`));
2673
+ console.log(chalk4.gray(` Address: ${account.address}`));
2674
+ const hash = await client.writeContract({
2675
+ address: CHUNKED_STORAGE_CONTRACT.address,
2676
+ abi: CHUNKED_STORAGE_CONTRACT.abi,
2677
+ functionName: "put",
2678
+ args: [bytesKey, CANVAS_FILENAME, chunks]
2679
+ });
2680
+ console.log(chalk4.blue(`Waiting for confirmation...`));
2681
+ const receipt = await client.waitForTransactionReceipt({ hash });
2682
+ if (receipt.status === "success") {
2683
+ console.log(
2684
+ chalk4.green(
2685
+ `
2686
+ Canvas updated successfully!
2687
+ Transaction: ${hash}
2688
+ Content size: ${Buffer.byteLength(canvasContent)} bytes
2689
+ Chunks: ${chunks.length}`
2690
+ )
2691
+ );
2692
+ } else {
2693
+ exitWithError(`Transaction failed: ${hash}`);
2694
+ }
2695
+ } catch (error) {
2696
+ exitWithError(
2697
+ `Failed to set canvas: ${error instanceof Error ? error.message : String(error)}`
2698
+ );
2699
+ }
2700
+ }
2701
+ function isDataUri(content) {
2702
+ return content.startsWith("data:");
2703
+ }
2704
+ function parseDataUri(dataUri) {
2705
+ const match = dataUri.match(/^data:([^;]+);base64,(.+)$/);
2706
+ if (!match) {
2707
+ throw new Error("Invalid data URI format");
2708
+ }
2709
+ const mimeType = match[1];
2710
+ const base64Data = match[2];
2711
+ const buffer = Buffer.from(base64Data, "base64");
2712
+ return { buffer, mimeType };
2713
+ }
2714
+ function getExtensionFromMimeType(mimeType) {
2715
+ const extensions = {
2716
+ "image/png": ".png",
2717
+ "image/jpeg": ".jpg",
2718
+ "image/gif": ".gif",
2719
+ "image/webp": ".webp",
2720
+ "image/svg+xml": ".svg",
2721
+ "application/pdf": ".pdf",
2722
+ "text/html": ".html",
2723
+ "text/css": ".css",
2724
+ "application/javascript": ".js",
2725
+ "application/json": ".json",
2726
+ "text/plain": ".txt",
2727
+ "application/octet-stream": ".bin"
2728
+ };
2729
+ return extensions[mimeType] || ".bin";
2730
+ }
2731
+ async function executeProfileGetCanvas(options) {
2732
+ const readOnlyOptions = parseReadOnlyOptions({
2733
+ chainId: options.chainId,
2734
+ rpcUrl: options.rpcUrl
2735
+ });
2736
+ const client = new StorageClient({
2737
+ chainId: readOnlyOptions.chainId,
2738
+ overrides: options.rpcUrl ? { rpcUrls: [options.rpcUrl] } : void 0
2739
+ });
2740
+ try {
2741
+ let canvasContent;
2742
+ let canvasText;
2743
+ try {
2744
+ const result = await client.readChunkedStorage({
2745
+ key: PROFILE_CANVAS_STORAGE_KEY,
2746
+ operator: options.address
2747
+ });
2748
+ if (result.data) {
2749
+ canvasContent = result.data;
2750
+ canvasText = result.text;
2751
+ }
2752
+ } catch (error) {
2753
+ const errorMessage = error instanceof Error ? error.message : String(error);
2754
+ if (errorMessage !== "ChunkedStorage metadata not found" && !errorMessage.includes("not found")) {
2755
+ throw error;
2756
+ }
2757
+ }
2758
+ if (options.json) {
2759
+ const output = {
2760
+ address: options.address,
2761
+ chainId: readOnlyOptions.chainId,
2762
+ canvas: canvasContent || null,
2763
+ filename: canvasText || null,
2764
+ hasCanvas: !!canvasContent,
2765
+ isDataUri: canvasContent ? isDataUri(canvasContent) : false,
2766
+ contentLength: canvasContent ? canvasContent.length : 0
2767
+ };
2768
+ console.log(JSON.stringify(output, null, 2));
2769
+ return;
2770
+ }
2771
+ if (!canvasContent) {
2772
+ exitWithError(`No canvas found for address: ${options.address}`);
2773
+ }
2774
+ if (options.output) {
2775
+ const outputPath = path.resolve(options.output);
2776
+ if (isDataUri(canvasContent)) {
2777
+ const { buffer, mimeType } = parseDataUri(canvasContent);
2778
+ let finalPath = outputPath;
2779
+ if (!path.extname(outputPath)) {
2780
+ finalPath = outputPath + getExtensionFromMimeType(mimeType);
2781
+ }
2782
+ fs2.writeFileSync(finalPath, buffer);
2783
+ console.log(
2784
+ chalk4.green(`Canvas written to: ${finalPath} (${buffer.length} bytes)`)
2785
+ );
2786
+ } else {
2787
+ fs2.writeFileSync(outputPath, canvasContent, "utf-8");
2788
+ console.log(
2789
+ chalk4.green(
2790
+ `Canvas written to: ${outputPath} (${canvasContent.length} bytes)`
2791
+ )
2792
+ );
2793
+ }
2794
+ return;
2795
+ }
2796
+ console.log(canvasContent);
2797
+ } catch (error) {
2798
+ exitWithError(
2799
+ `Failed to read canvas: ${error instanceof Error ? error.message : String(error)}`
2800
+ );
2801
+ }
2802
+ }
2535
2803
 
2536
2804
  // src/commands/profile/index.ts
2537
2805
  function registerProfileCommand(program2) {
@@ -2620,13 +2888,59 @@ function registerProfileCommand(program2) {
2620
2888
  encodeOnly: options.encodeOnly
2621
2889
  });
2622
2890
  });
2891
+ const setCanvasCommand = new Command("set-canvas").description("Set your profile canvas (HTML content)").option("--file <path>", "Path to file containing canvas content").option("--content <html>", "HTML content for canvas (inline)").option(
2892
+ "--private-key <key>",
2893
+ "Private key (0x-prefixed hex, 66 characters). Can also be set via NET_PRIVATE_KEY env var"
2894
+ ).option(
2895
+ "--chain-id <id>",
2896
+ "Chain ID. Can also be set via NET_CHAIN_ID env var",
2897
+ (value) => parseInt(value, 10)
2898
+ ).option(
2899
+ "--rpc-url <url>",
2900
+ "Custom RPC URL. Can also be set via NET_RPC_URL env var"
2901
+ ).option(
2902
+ "--encode-only",
2903
+ "Output transaction data as JSON instead of executing"
2904
+ ).action(async (options) => {
2905
+ await executeProfileSetCanvas({
2906
+ file: options.file,
2907
+ content: options.content,
2908
+ privateKey: options.privateKey,
2909
+ chainId: options.chainId,
2910
+ rpcUrl: options.rpcUrl,
2911
+ encodeOnly: options.encodeOnly
2912
+ });
2913
+ });
2914
+ const getCanvasCommand = new Command("get-canvas").description("Get profile canvas for an address").requiredOption("--address <address>", "Wallet address to get canvas for").option("--output <path>", "Write canvas content to file instead of stdout").option(
2915
+ "--chain-id <id>",
2916
+ "Chain ID. Can also be set via NET_CHAIN_ID env var",
2917
+ (value) => parseInt(value, 10)
2918
+ ).option(
2919
+ "--rpc-url <url>",
2920
+ "Custom RPC URL. Can also be set via NET_RPC_URL env var"
2921
+ ).option("--json", "Output in JSON format").action(async (options) => {
2922
+ await executeProfileGetCanvas({
2923
+ address: options.address,
2924
+ output: options.output,
2925
+ chainId: options.chainId,
2926
+ rpcUrl: options.rpcUrl,
2927
+ json: options.json
2928
+ });
2929
+ });
2623
2930
  profileCommand.addCommand(getCommand);
2624
2931
  profileCommand.addCommand(setPictureCommand);
2625
2932
  profileCommand.addCommand(setUsernameCommand);
2626
2933
  profileCommand.addCommand(setBioCommand);
2934
+ profileCommand.addCommand(setCanvasCommand);
2935
+ profileCommand.addCommand(getCanvasCommand);
2627
2936
  }
2628
2937
 
2629
2938
  // src/cli/index.ts
2939
+ var proxyUrl = process.env.https_proxy || process.env.HTTPS_PROXY;
2940
+ if (proxyUrl) {
2941
+ const agent = new ProxyAgent(proxyUrl);
2942
+ setGlobalDispatcher(agent);
2943
+ }
2630
2944
  var require2 = createRequire(import.meta.url);
2631
2945
  var { version } = require2("../../package.json");
2632
2946
  var program = new Command();