opal-security 4.0.3 → 4.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 CHANGED
@@ -22,7 +22,7 @@ $ npm install -g opal-security
22
22
  $ opal COMMAND
23
23
  running command...
24
24
  $ opal (--version)
25
- opal-security/4.0.3 darwin-arm64 node-v24.5.0
25
+ opal-security/4.1.0 darwin-arm64 node-v18.20.5
26
26
  $ opal --help [COMMAND]
27
27
  USAGE
28
28
  $ opal COMMAND
@@ -36,6 +36,7 @@ USAGE
36
36
  * [`opal autocomplete [SHELL]`](#opal-autocomplete-shell)
37
37
  * [`opal aws identity`](#opal-aws-identity)
38
38
  * [`opal clear-auth-config`](#opal-clear-auth-config)
39
+ * [`opal curl-example`](#opal-curl-example)
39
40
  * [`opal groups get`](#opal-groups-get)
40
41
  * [`opal help [COMMANDS]`](#opal-help-commands)
41
42
  * [`opal iam-roles start`](#opal-iam-roles-start)
@@ -105,7 +106,7 @@ EXAMPLES
105
106
  $ opal aws:identity
106
107
  ```
107
108
 
108
- _See code: [src/commands/aws/identity.ts](https://github.com/opalsecurity/opal-cli/blob/v4.0.3/src/commands/aws/identity.ts)_
109
+ _See code: [src/commands/aws/identity.ts](https://github.com/opalsecurity/opal-cli/blob/v4.1.0/src/commands/aws/identity.ts)_
109
110
 
110
111
  ## `opal clear-auth-config`
111
112
 
@@ -122,7 +123,24 @@ EXAMPLES
122
123
  $ opal clear-auth-config
123
124
  ```
124
125
 
125
- _See code: [src/commands/clear-auth-config.ts](https://github.com/opalsecurity/opal-cli/blob/v4.0.3/src/commands/clear-auth-config.ts)_
126
+ _See code: [src/commands/clear-auth-config.ts](https://github.com/opalsecurity/opal-cli/blob/v4.1.0/src/commands/clear-auth-config.ts)_
127
+
128
+ ## `opal curl-example`
129
+
130
+ Prints out an example cURL command containing the parameters the CLI uses to query the Opal server.
131
+
132
+ ```
133
+ USAGE
134
+ $ opal curl-example [-h]
135
+
136
+ FLAGS
137
+ -h, --help Show CLI help.
138
+
139
+ DESCRIPTION
140
+ Prints out an example cURL command containing the parameters the CLI uses to query the Opal server.
141
+ ```
142
+
143
+ _See code: [src/commands/curl-example.ts](https://github.com/opalsecurity/opal-cli/blob/v4.1.0/src/commands/curl-example.ts)_
126
144
 
127
145
  ## `opal groups get`
128
146
 
@@ -143,7 +161,7 @@ EXAMPLES
143
161
  $ opal groups:get --id 54052a3e-5375-4392-aeaf-0c6c44c131d4
144
162
  ```
145
163
 
146
- _See code: [src/commands/groups/get.ts](https://github.com/opalsecurity/opal-cli/blob/v4.0.3/src/commands/groups/get.ts)_
164
+ _See code: [src/commands/groups/get.ts](https://github.com/opalsecurity/opal-cli/blob/v4.1.0/src/commands/groups/get.ts)_
147
165
 
148
166
  ## `opal help [COMMANDS]`
149
167
 
@@ -193,7 +211,7 @@ EXAMPLES
193
211
  $ opal iam-roles:start --id 51f7176b-0464-4a6f-8369-e951e187b398 --profileName "custom-profile"
194
212
  ```
195
213
 
196
- _See code: [src/commands/iam-roles/start.ts](https://github.com/opalsecurity/opal-cli/blob/v4.0.3/src/commands/iam-roles/start.ts)_
214
+ _See code: [src/commands/iam-roles/start.ts](https://github.com/opalsecurity/opal-cli/blob/v4.1.0/src/commands/iam-roles/start.ts)_
197
215
 
198
216
  ## `opal kube-roles start`
199
217
 
@@ -224,7 +242,7 @@ EXAMPLES
224
242
  $ opal kube-roles:start --id 51f7176b-0464-4a6f-8369-e951e187b398 --accessLevelRemoteId "arn:aws:iam::712234975475:role/acme-eks-cluster-admin-role"
225
243
  ```
226
244
 
227
- _See code: [src/commands/kube-roles/start.ts](https://github.com/opalsecurity/opal-cli/blob/v4.0.3/src/commands/kube-roles/start.ts)_
245
+ _See code: [src/commands/kube-roles/start.ts](https://github.com/opalsecurity/opal-cli/blob/v4.1.0/src/commands/kube-roles/start.ts)_
228
246
 
229
247
  ## `opal login`
230
248
 
@@ -247,7 +265,7 @@ EXAMPLES
247
265
  $ opal login
248
266
  ```
249
267
 
250
- _See code: [src/commands/login.ts](https://github.com/opalsecurity/opal-cli/blob/v4.0.3/src/commands/login.ts)_
268
+ _See code: [src/commands/login.ts](https://github.com/opalsecurity/opal-cli/blob/v4.1.0/src/commands/login.ts)_
251
269
 
252
270
  ## `opal logout`
253
271
 
@@ -267,7 +285,7 @@ EXAMPLES
267
285
  $ opal logout
268
286
  ```
269
287
 
270
- _See code: [src/commands/logout.ts](https://github.com/opalsecurity/opal-cli/blob/v4.0.3/src/commands/logout.ts)_
288
+ _See code: [src/commands/logout.ts](https://github.com/opalsecurity/opal-cli/blob/v4.1.0/src/commands/logout.ts)_
271
289
 
272
290
  ## `opal postgres-instances start`
273
291
 
@@ -305,7 +323,7 @@ EXAMPLES
305
323
  $ opal postgres-instances:start --id 51f7176b-0464-4a6f-8369-e951e187b398 --accessLevelRemoteId fullaccess --action view
306
324
  ```
307
325
 
308
- _See code: [src/commands/postgres-instances/start.ts](https://github.com/opalsecurity/opal-cli/blob/v4.0.3/src/commands/postgres-instances/start.ts)_
326
+ _See code: [src/commands/postgres-instances/start.ts](https://github.com/opalsecurity/opal-cli/blob/v4.1.0/src/commands/postgres-instances/start.ts)_
309
327
 
310
328
  ## `opal request create`
311
329
 
@@ -331,7 +349,7 @@ DESCRIPTION
331
349
  Creates an Opal access request via an interactive form
332
350
  ```
333
351
 
334
- _See code: [src/commands/request/create.ts](https://github.com/opalsecurity/opal-cli/blob/v4.0.3/src/commands/request/create.ts)_
352
+ _See code: [src/commands/request/create.ts](https://github.com/opalsecurity/opal-cli/blob/v4.1.0/src/commands/request/create.ts)_
335
353
 
336
354
  ## `opal request get`
337
355
 
@@ -355,7 +373,7 @@ EXAMPLES
355
373
  $ opal request get --id 54052a3e-5375-4392-aeaf-0c6c44c131d4 --verbose
356
374
  ```
357
375
 
358
- _See code: [src/commands/request/get.ts](https://github.com/opalsecurity/opal-cli/blob/v4.0.3/src/commands/request/get.ts)_
376
+ _See code: [src/commands/request/get.ts](https://github.com/opalsecurity/opal-cli/blob/v4.1.0/src/commands/request/get.ts)_
359
377
 
360
378
  ## `opal request list`
361
379
 
@@ -387,7 +405,7 @@ EXAMPLES
387
405
  $ opal request list --n 5 --pending --verbose
388
406
  ```
389
407
 
390
- _See code: [src/commands/request/list.ts](https://github.com/opalsecurity/opal-cli/blob/v4.0.3/src/commands/request/list.ts)_
408
+ _See code: [src/commands/request/list.ts](https://github.com/opalsecurity/opal-cli/blob/v4.1.0/src/commands/request/list.ts)_
391
409
 
392
410
  ## `opal request ls`
393
411
 
@@ -438,7 +456,7 @@ EXAMPLES
438
456
  $ opal resources:get --id 54052a3e-5375-4392-aeaf-0c6c44c131d4
439
457
  ```
440
458
 
441
- _See code: [src/commands/resources/get.ts](https://github.com/opalsecurity/opal-cli/blob/v4.0.3/src/commands/resources/get.ts)_
459
+ _See code: [src/commands/resources/get.ts](https://github.com/opalsecurity/opal-cli/blob/v4.1.0/src/commands/resources/get.ts)_
442
460
 
443
461
  ## `opal set-auth-config`
444
462
 
@@ -468,7 +486,7 @@ EXAMPLES
468
486
  $ opal set-auth-config --organizationID=org-456 --clientID=abc123 --issuerUrl=https://auth.example.com
469
487
  ```
470
488
 
471
- _See code: [src/commands/set-auth-config.ts](https://github.com/opalsecurity/opal-cli/blob/v4.0.3/src/commands/set-auth-config.ts)_
489
+ _See code: [src/commands/set-auth-config.ts](https://github.com/opalsecurity/opal-cli/blob/v4.1.0/src/commands/set-auth-config.ts)_
472
490
 
473
491
  ## `opal set-custom-header`
474
492
 
@@ -489,7 +507,7 @@ EXAMPLES
489
507
  $ opal set-custom-header --header 'cf-access-token: $TOKEN'
490
508
  ```
491
509
 
492
- _See code: [src/commands/set-custom-header.ts](https://github.com/opalsecurity/opal-cli/blob/v4.0.3/src/commands/set-custom-header.ts)_
510
+ _See code: [src/commands/set-custom-header.ts](https://github.com/opalsecurity/opal-cli/blob/v4.1.0/src/commands/set-custom-header.ts)_
493
511
 
494
512
  ## `opal set-token`
495
513
 
@@ -509,7 +527,7 @@ EXAMPLES
509
527
  $ opal set-token
510
528
  ```
511
529
 
512
- _See code: [src/commands/set-token.ts](https://github.com/opalsecurity/opal-cli/blob/v4.0.3/src/commands/set-token.ts)_
530
+ _See code: [src/commands/set-token.ts](https://github.com/opalsecurity/opal-cli/blob/v4.1.0/src/commands/set-token.ts)_
513
531
 
514
532
  ## `opal set-url [URL]`
515
533
 
@@ -533,7 +551,7 @@ EXAMPLES
533
551
  $ opal set-url
534
552
  ```
535
553
 
536
- _See code: [src/commands/set-url.ts](https://github.com/opalsecurity/opal-cli/blob/v4.0.3/src/commands/set-url.ts)_
554
+ _See code: [src/commands/set-url.ts](https://github.com/opalsecurity/opal-cli/blob/v4.1.0/src/commands/set-url.ts)_
537
555
 
538
556
  ## `opal ssh copyFrom`
539
557
 
@@ -564,7 +582,7 @@ EXAMPLES
564
582
  $ opal ssh:copyFrom --src instance/dir --dest my/dir --id 51f7176b-0464-4a6f-8369-e951e187b398
565
583
  ```
566
584
 
567
- _See code: [src/commands/ssh/copyFrom.ts](https://github.com/opalsecurity/opal-cli/blob/v4.0.3/src/commands/ssh/copyFrom.ts)_
585
+ _See code: [src/commands/ssh/copyFrom.ts](https://github.com/opalsecurity/opal-cli/blob/v4.1.0/src/commands/ssh/copyFrom.ts)_
568
586
 
569
587
  ## `opal ssh copyTo`
570
588
 
@@ -595,7 +613,7 @@ EXAMPLES
595
613
  $ opal ssh:copyTo --src my/dir --dest instance/dir --id 51f7176b-0464-4a6f-8369-e951e187b398
596
614
  ```
597
615
 
598
- _See code: [src/commands/ssh/copyTo.ts](https://github.com/opalsecurity/opal-cli/blob/v4.0.3/src/commands/ssh/copyTo.ts)_
616
+ _See code: [src/commands/ssh/copyTo.ts](https://github.com/opalsecurity/opal-cli/blob/v4.1.0/src/commands/ssh/copyTo.ts)_
599
617
 
600
618
  ## `opal ssh start`
601
619
 
@@ -622,7 +640,7 @@ EXAMPLES
622
640
  $ opal ssh:start --id 51f7176b-0464-4a6f-8369-e951e187b398
623
641
  ```
624
642
 
625
- _See code: [src/commands/ssh/start.ts](https://github.com/opalsecurity/opal-cli/blob/v4.0.3/src/commands/ssh/start.ts)_
643
+ _See code: [src/commands/ssh/start.ts](https://github.com/opalsecurity/opal-cli/blob/v4.1.0/src/commands/ssh/start.ts)_
626
644
 
627
645
  ## `opal version`
628
646
 
@@ -659,5 +677,5 @@ DESCRIPTION
659
677
  Describes current url set, organization name, and logged in user if applicable.
660
678
  ```
661
679
 
662
- _See code: [src/commands/whoami.ts](https://github.com/opalsecurity/opal-cli/blob/v4.0.3/src/commands/whoami.ts)_
680
+ _See code: [src/commands/whoami.ts](https://github.com/opalsecurity/opal-cli/blob/v4.1.0/src/commands/whoami.ts)_
663
681
  <!-- commandsstop -->
@@ -0,0 +1,8 @@
1
+ import { Command } from "@oclif/core";
2
+ export default class CurlExample extends Command {
3
+ static description: string;
4
+ static flags: {
5
+ help: import("@oclif/core/lib/interfaces").BooleanFlag<void>;
6
+ };
7
+ run(): Promise<void>;
8
+ }
@@ -0,0 +1,35 @@
1
+ import { Command } from "@oclif/core";
2
+ import chalk from "chalk";
3
+ import { getOrCreateConfigData, urlKey } from "../lib/config.js";
4
+ import { SecretType, getOpalCredentials } from "../lib/credentials/index.js";
5
+ import { SHARED_FLAGS } from "../lib/flags.js";
6
+ class CurlExample extends Command {
7
+ async run() {
8
+ const opalCreds = await getOpalCredentials(this);
9
+ const secret = opalCreds === null || opalCreds === void 0 ? void 0 : opalCreds.secret;
10
+ const organizationID = opalCreds === null || opalCreds === void 0 ? void 0 : opalCreds.organizationID;
11
+ const configData = getOrCreateConfigData(this.config.configDir);
12
+ const url = configData[urlKey];
13
+ let authStr = "";
14
+ if (opalCreds.secretType === SecretType.ApiToken) {
15
+ authStr = `Authorization: Bearer ${secret}`;
16
+ }
17
+ else {
18
+ authStr = `Cookie: ${secret}`;
19
+ }
20
+ this.log(chalk.yellow(`WARN: This command will be removed in a future version of the Opal CLI. \n\
21
+ Opal's GraphQL API is not intended for developer use, please use our REST API instead`));
22
+ this.log(`
23
+ curl -v ${url}/query \\
24
+ --data-binary '{"query":"query ListSSHSessions {resources(input: {serviceType: SSH, onlyMine: true}) {... on ResourcesResult { resources { name } } } }"}' \\
25
+ --header "Content-Type: application/json" \\
26
+ --header "${authStr}" \\
27
+ --header "X-Opal-Organization-ID: ${organizationID}"
28
+ `);
29
+ }
30
+ }
31
+ CurlExample.description = "Prints out an example cURL command containing the parameters the CLI uses to query the Opal server.";
32
+ CurlExample.flags = {
33
+ help: SHARED_FLAGS.help,
34
+ };
35
+ export default CurlExample;
@@ -19,7 +19,6 @@ const ISSUER_PROD = "https://auth.opal.dev";
19
19
  const ISSUER_DEV = "https://authdev.opal.dev";
20
20
  const CLIENT_ID_PROD = "42rm6E5v7o67LBpRfjdT9KhnjrQHr9UF";
21
21
  const CLIENT_ID_DEV = "XYV8qoAvZG7dHnhRp2g5XMJ1zX9fBP6s";
22
- const REDIRECT_URI = "http://127.0.0.1:8080/callback";
23
22
  const CLISignInMethodDocumentLegacy = `
24
23
  query CLISignInMethod($input: SignInMethodInput!) {
25
24
  signInMethod(input: $input) {
@@ -251,7 +250,7 @@ Verify this code in your browser
251
250
  {
252
251
  type: "input",
253
252
  name: "continue",
254
- message: "Press Enter to open your browser and continue",
253
+ message: "Press Enter to open your browser and continue\n",
255
254
  },
256
255
  ]);
257
256
  this.log(`
@@ -271,12 +270,12 @@ If your browser doesn't automatically, go to:
271
270
  }
272
271
  }
273
272
  else {
274
- const serverPromise = startLocalServer();
273
+ const { port, urlPromise } = await startLocalServer();
275
274
  const code_verifier = client.randomPKCECodeVerifier();
276
275
  const code_challenge = await client.calculatePKCECodeChallenge(code_verifier);
277
276
  const clientState = client.randomState();
278
277
  const parameters = {
279
- redirect_uri: REDIRECT_URI,
278
+ redirect_uri: `http://127.0.0.1:${port}/callback`,
280
279
  scope,
281
280
  code_challenge,
282
281
  code_challenge_method: "S256",
@@ -300,18 +299,18 @@ To continue, please authorize this application in your browser.
300
299
  {
301
300
  type: "input",
302
301
  name: "continue",
303
- message: "Press Enter to open your browser and continue",
302
+ message: "Press Enter to open your browser and continue\n",
304
303
  },
305
304
  ]);
306
305
  this.log(`
307
- If your browser doesn't automatically, go to:
308
-
306
+ If your browser doesn't automatically, go to:
307
+
309
308
  ${redirectTo}
310
309
  `);
311
310
  ux.action.start("Waiting for authorization");
312
311
  try {
313
312
  await open(redirectTo.toString(), { wait: false });
314
- const url = await serverPromise;
313
+ const url = await urlPromise;
315
314
  tokens = await client.authorizationCodeGrant(config, new URL(url), {
316
315
  pkceCodeVerifier: code_verifier,
317
316
  expectedState: clientState,
@@ -1,5 +1,9 @@
1
1
  /**
2
- * Starts a local HTTP server on port 8080 to handle OAuth callback.
3
- * Returns a promise that resolves with the full callback URL when authentication succeeds.
2
+ * Starts a local HTTP server to handle OAuth callback.
3
+ * Tries ports in order: 49152, 49153, 49154
4
+ * Returns a promise that resolves with the actual port and a promise for the callback URL.
4
5
  */
5
- export declare function startLocalServer(): Promise<string>;
6
+ export declare function startLocalServer(): Promise<{
7
+ port: number;
8
+ urlPromise: Promise<string>;
9
+ }>;
@@ -1,14 +1,29 @@
1
1
  import * as http from "node:http";
2
2
  import { authErrorHtml, authMissingCodeHtml, authSuccessHtml, } from "./auth-success-template.js";
3
3
  /**
4
- * Starts a local HTTP server on port 8080 to handle OAuth callback.
5
- * Returns a promise that resolves with the full callback URL when authentication succeeds.
4
+ * Starts a local HTTP server to handle OAuth callback.
5
+ * Tries ports in order: 49152, 49153, 49154
6
+ * Returns a promise that resolves with the actual port and a promise for the callback URL.
6
7
  */
7
8
  export function startLocalServer() {
8
- return new Promise((resolve, reject) => {
9
+ const portsToTry = [49152, 49153, 49154];
10
+ return tryPorts(portsToTry, 0);
11
+ }
12
+ function tryPorts(ports, index) {
13
+ if (index >= ports.length) {
14
+ return Promise.reject(new Error(`Failed to start server: all ports (${ports.join(", ")}) are occupied`));
15
+ }
16
+ const port = ports[index];
17
+ return new Promise((resolveServer, rejectServer) => {
18
+ let resolveUrl;
19
+ let rejectUrl;
20
+ const urlPromise = new Promise((resolve, reject) => {
21
+ resolveUrl = resolve;
22
+ rejectUrl = reject;
23
+ });
9
24
  const server = http.createServer(async (req, res) => {
10
25
  try {
11
- const url = new URL(req.url || "", "http://127.0.0.1:8080");
26
+ const url = new URL(req.url || "", `http://127.0.0.1:${port}`);
12
27
  if (url.pathname === "/callback") {
13
28
  const error = url.searchParams.get("error");
14
29
  if (error) {
@@ -16,17 +31,17 @@ export function startLocalServer() {
16
31
  res.end(authErrorHtml(error));
17
32
  server.closeAllConnections();
18
33
  server.close(() => {
19
- reject(new Error(`Authentication failed: ${error}`));
34
+ rejectUrl(new Error(`Authentication failed: ${error}`));
20
35
  });
21
36
  return;
22
37
  }
23
38
  if (req.url) {
24
39
  res.writeHead(200, { "Content-Type": "text/html" });
25
40
  res.end(authSuccessHtml);
26
- const fullUrl = `http://127.0.0.1:8080${req.url}`;
41
+ const fullUrl = `http://127.0.0.1:${port}${req.url}`;
27
42
  server.closeAllConnections();
28
43
  server.close(() => {
29
- resolve(fullUrl);
44
+ resolveUrl(fullUrl);
30
45
  });
31
46
  }
32
47
  else {
@@ -34,7 +49,7 @@ export function startLocalServer() {
34
49
  res.end(authMissingCodeHtml);
35
50
  server.closeAllConnections();
36
51
  server.close(() => {
37
- reject(new Error("Missing authorization code"));
52
+ rejectUrl(new Error("Missing authorization code"));
38
53
  });
39
54
  }
40
55
  }
@@ -48,21 +63,31 @@ export function startLocalServer() {
48
63
  res.end();
49
64
  server.closeAllConnections();
50
65
  server.close(() => {
51
- reject(err);
66
+ rejectUrl(err);
52
67
  });
53
68
  }
54
69
  });
55
- server.listen(8080, "127.0.0.1", () => {
56
- console.log("Local server started on http://127.0.0.1:8080");
70
+ server.listen(port, "127.0.0.1", () => {
71
+ console.log(`Local server started on http://127.0.0.1:${port}`);
72
+ // Server successfully bound to port, resolve with port and urlPromise
73
+ resolveServer({ port, urlPromise });
57
74
  });
58
75
  server.on("error", (err) => {
59
- reject(err);
76
+ // If port is occupied, try the next one
77
+ if (err.code === "EADDRINUSE") {
78
+ tryPorts(ports, index + 1)
79
+ .then(resolveServer)
80
+ .catch(rejectServer);
81
+ }
82
+ else {
83
+ rejectServer(err);
84
+ }
60
85
  });
61
86
  // Timeout after 5 minutes
62
87
  setTimeout(() => {
63
88
  server.closeAllConnections();
64
89
  server.close(() => {
65
- reject(new Error("Authentication timeout"));
90
+ rejectUrl(new Error("Authentication timeout"));
66
91
  });
67
92
  }, 5 * 60 * 1000);
68
93
  });
@@ -23,6 +23,34 @@
23
23
  "clear-auth-config.js"
24
24
  ]
25
25
  },
26
+ "curl-example": {
27
+ "aliases": [],
28
+ "args": {},
29
+ "description": "Prints out an example cURL command containing the parameters the CLI uses to query the Opal server.",
30
+ "flags": {
31
+ "help": {
32
+ "char": "h",
33
+ "description": "Show CLI help.",
34
+ "name": "help",
35
+ "allowNo": false,
36
+ "type": "boolean"
37
+ }
38
+ },
39
+ "hasDynamicHelp": false,
40
+ "hiddenAliases": [],
41
+ "id": "curl-example",
42
+ "pluginAlias": "opal-security",
43
+ "pluginName": "opal-security",
44
+ "pluginType": "core",
45
+ "strict": true,
46
+ "enableJsonFlag": false,
47
+ "isESM": true,
48
+ "relativePath": [
49
+ "build",
50
+ "commands",
51
+ "curl-example.js"
52
+ ]
53
+ },
26
54
  "login": {
27
55
  "aliases": [],
28
56
  "args": {},
@@ -1026,5 +1054,5 @@
1026
1054
  ]
1027
1055
  }
1028
1056
  },
1029
- "version": "4.0.3"
1057
+ "version": "4.1.0"
1030
1058
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "opal-security",
3
3
  "description": "Opal allows you to centrally manage access to all of your sensitive systems.",
4
- "version": "4.0.3",
4
+ "version": "4.1.0",
5
5
  "type": "module",
6
6
  "author": "Opal Security",
7
7
  "bin": {
@@ -62,7 +62,7 @@
62
62
  "vitest": "^3.1.1"
63
63
  },
64
64
  "engines": {
65
- "node": ">=24.0.0"
65
+ "node": ">=18.0.0"
66
66
  },
67
67
  "files": [
68
68
  "/bin",