@kinosec/cli 0.7.1 → 0.9.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.
Files changed (3) hide show
  1. package/dist/cli.js +265 -22
  2. package/dist/cli.js.map +1 -1
  3. package/package.json +12 -12
package/dist/cli.js CHANGED
@@ -6289,6 +6289,7 @@ var KinoSecClient = class {
6289
6289
  reports;
6290
6290
  tokens;
6291
6291
  me;
6292
+ credits;
6292
6293
  audit;
6293
6294
  constructor(config) {
6294
6295
  const prefixPathname = new URL(config.apiUrl).pathname.replace(/\/?$/, "/");
@@ -6338,6 +6339,7 @@ var KinoSecClient = class {
6338
6339
  this.reports = new ReportsResource(this.api);
6339
6340
  this.tokens = new TokensResource(this.api);
6340
6341
  this.me = new MeResource(this.api);
6342
+ this.credits = new CreditsResource(this.api);
6341
6343
  this.audit = new AuditResource(this.api);
6342
6344
  }
6343
6345
  };
@@ -6387,6 +6389,9 @@ var ScansResource = class {
6387
6389
  async cancel(id) {
6388
6390
  return this.api.post(`v1/scans/${id}/cancel`, { json: {} }).json();
6389
6391
  }
6392
+ async scoutStatus(profileId) {
6393
+ return this.api.get(`v1/scans/${profileId}/scout-status`).json();
6394
+ }
6390
6395
  };
6391
6396
  var SchedulesResource = class {
6392
6397
  constructor(api) {
@@ -6472,6 +6477,15 @@ var MeResource = class {
6472
6477
  return this.api.get("v1/me").json();
6473
6478
  }
6474
6479
  };
6480
+ var CreditsResource = class {
6481
+ constructor(api) {
6482
+ this.api = api;
6483
+ }
6484
+ api;
6485
+ async get() {
6486
+ return this.api.get("v1/credits").json();
6487
+ }
6488
+ };
6475
6489
  var AuditResource = class {
6476
6490
  constructor(api) {
6477
6491
  this.api = api;
@@ -7353,6 +7367,32 @@ ${content}`);
7353
7367
  // src/lib/errors.ts
7354
7368
  function handleError(error2) {
7355
7369
  if (error2 instanceof ApiError) {
7370
+ if (error2.code === "insufficient_credits") {
7371
+ if (shouldUseJson()) {
7372
+ process.stderr.write(
7373
+ JSON.stringify({
7374
+ error: {
7375
+ status: error2.status,
7376
+ code: error2.code,
7377
+ detail: error2.detail,
7378
+ request_id: error2.requestId
7379
+ }
7380
+ }) + "\n"
7381
+ );
7382
+ } else if (isTTY()) {
7383
+ console.error(import_picocolors2.default.red("\u2717 Insufficient credits"));
7384
+ console.error();
7385
+ console.error(" You don't have enough credits for this scan.");
7386
+ console.error(" Check your balance:");
7387
+ console.error();
7388
+ console.error(` ${import_picocolors2.default.cyan("kinosec credits")}`);
7389
+ console.error();
7390
+ console.error(` Need more? Contact ${import_picocolors2.default.bold("sales@kinosec.com")}`);
7391
+ } else {
7392
+ console.error("Insufficient credits. Check your balance: kinosec credits. Contact sales@kinosec.com");
7393
+ }
7394
+ process.exit(77);
7395
+ }
7356
7396
  if (shouldUseJson()) {
7357
7397
  process.stderr.write(
7358
7398
  JSON.stringify({
@@ -7704,8 +7744,38 @@ Examples:
7704
7744
  });
7705
7745
  }
7706
7746
 
7747
+ // src/commands/credits.ts
7748
+ function registerCreditsCommand(program3) {
7749
+ program3.command("credits").description("Show your credit balance").addHelpText("after", `
7750
+ Examples:
7751
+ $ kinosec credits
7752
+ $ kinosec credits --json`).action(async () => {
7753
+ try {
7754
+ const opts = program3.opts();
7755
+ const { token, apiUrl } = resolveToken(opts);
7756
+ const client = new KinoSecClient({ apiUrl, token });
7757
+ const credits = await client.credits.get();
7758
+ if (opts.json || opts.output === "json") {
7759
+ printJson(credits);
7760
+ return;
7761
+ }
7762
+ const fields = [
7763
+ { label: "Pentest credits", value: String(credits.pentest_credits) },
7764
+ { label: "Quick credits", value: String(credits.quick_pentest_credits) },
7765
+ { label: "Free quick credit", value: String(credits.free_quick_pentest_credits) }
7766
+ ];
7767
+ if (credits.source?.type === "org_pool" && credits.source.org_name) {
7768
+ fields.push({ label: "Source", value: `${credits.source.org_name} org pool` });
7769
+ }
7770
+ printDetail(fields);
7771
+ } catch (error2) {
7772
+ handleError(error2);
7773
+ }
7774
+ });
7775
+ }
7776
+
7707
7777
  // src/commands/pentest.ts
7708
- var import_picocolors6 = __toESM(require_picocolors(), 1);
7778
+ var import_picocolors7 = __toESM(require_picocolors(), 1);
7709
7779
 
7710
7780
  // src/lib/confirm.ts
7711
7781
  var import_picocolors3 = __toESM(require_picocolors(), 1);
@@ -11460,6 +11530,122 @@ async function printFindingsSummary(client, profileId, tty3) {
11460
11530
  }
11461
11531
  }
11462
11532
 
11533
+ // src/lib/scout.ts
11534
+ var import_picocolors6 = __toESM(require_picocolors(), 1);
11535
+ var UNICODE2 = isUnicodeSupported();
11536
+ var sym2 = {
11537
+ success: UNICODE2 ? "\u2713" : "v",
11538
+ fail: UNICODE2 ? "\u2717" : "x",
11539
+ warn: UNICODE2 ? "\u26A0" : "!"
11540
+ };
11541
+ function printOnPremInstructions(result) {
11542
+ const tty3 = isTTY();
11543
+ const header = tty3 ? import_picocolors6.default.bold("On-Prem Scout Setup") : "On-Prem Scout Setup";
11544
+ console.log(`
11545
+ ${header}
11546
+ `);
11547
+ console.log(` Session ID: ${result.session_id}`);
11548
+ console.log(` Profile ID: ${result.scan_profile_id}`);
11549
+ const curlCmd = `curl -fsSL https://api.kinosec.ai/functions/v1/scout-bootstrap/launch/${result.bootstrap_token} | bash`;
11550
+ if (tty3) {
11551
+ console.log(`
11552
+ Run on target host:
11553
+ `);
11554
+ console.log(` ${import_picocolors6.default.cyan(curlCmd)}
11555
+ `);
11556
+ console.log(` ${import_picocolors6.default.yellow(sym2.warn)} Token expires in ${import_picocolors6.default.bold("15 minutes")}.`);
11557
+ } else {
11558
+ console.log(`
11559
+ Run on target host:
11560
+ `);
11561
+ console.log(` ${curlCmd}
11562
+ `);
11563
+ console.log(` ${sym2.warn} Token expires in 15 minutes.`);
11564
+ }
11565
+ }
11566
+ async function waitForScoutConnection(client, profileId, options) {
11567
+ const { timeoutMs, intervalMs, useJson } = options;
11568
+ const tty3 = isTTY();
11569
+ const loopStart = Date.now();
11570
+ let spinner = null;
11571
+ if (!useJson && tty3) {
11572
+ spinner = ora({
11573
+ text: "Waiting for Scout connection...",
11574
+ color: "green",
11575
+ isEnabled: true
11576
+ }).start();
11577
+ }
11578
+ const cleanup = () => {
11579
+ if (spinner) spinner.stop();
11580
+ };
11581
+ const onSigint = () => {
11582
+ cleanup();
11583
+ console.log("\nWatch cancelled. Pentest is waiting for Scout connection.");
11584
+ process.exit(0);
11585
+ };
11586
+ process.once("SIGINT", onSigint);
11587
+ while (true) {
11588
+ const status = await client.scans.scoutStatus(profileId);
11589
+ if (useJson) {
11590
+ const elapsed = Math.round((Date.now() - loopStart) / 1e3);
11591
+ process.stdout.write(
11592
+ JSON.stringify({ ...status, elapsed_seconds: elapsed }) + "\n"
11593
+ );
11594
+ }
11595
+ switch (status.status) {
11596
+ case "pending":
11597
+ if (spinner) spinner.text = import_picocolors6.default.green("Waiting for Scout bootstrap...");
11598
+ break;
11599
+ case "bootstrapped":
11600
+ if (spinner) spinner.text = import_picocolors6.default.green("Scout bootstrapped, establishing connection...");
11601
+ break;
11602
+ case "connected": {
11603
+ cleanup();
11604
+ process.removeListener("SIGINT", onSigint);
11605
+ if (!useJson) {
11606
+ const icon = tty3 ? import_picocolors6.default.green(sym2.success) : sym2.success;
11607
+ console.log(`${icon} Scout connected`);
11608
+ if (status.hostname) {
11609
+ console.log(` Hostname: ${status.hostname}`);
11610
+ }
11611
+ if (status.tools.length > 0) {
11612
+ console.log(` Tools: ${status.tools.join(", ")}`);
11613
+ }
11614
+ }
11615
+ return;
11616
+ }
11617
+ case "expired": {
11618
+ cleanup();
11619
+ process.removeListener("SIGINT", onSigint);
11620
+ const icon = tty3 ? import_picocolors6.default.red(sym2.fail) : sym2.fail;
11621
+ console.error(`
11622
+ ${icon} Scout bootstrap token expired.`);
11623
+ console.error("Start a new pentest to get a fresh token.");
11624
+ process.exit(1);
11625
+ }
11626
+ case "disconnected": {
11627
+ cleanup();
11628
+ process.removeListener("SIGINT", onSigint);
11629
+ const icon = tty3 ? import_picocolors6.default.red(sym2.fail) : sym2.fail;
11630
+ console.error(`
11631
+ ${icon} Scout disconnected unexpectedly.`);
11632
+ console.error(`Check status: kinosec pentest get ${profileId}`);
11633
+ process.exit(1);
11634
+ }
11635
+ }
11636
+ if (Date.now() - loopStart > timeoutMs) {
11637
+ cleanup();
11638
+ process.removeListener("SIGINT", onSigint);
11639
+ const secs = Math.round(timeoutMs / 1e3);
11640
+ console.error(`
11641
+ Timeout: Scout did not connect within ${secs}s.`);
11642
+ console.error("The bootstrap token may have expired. Start a new pentest.");
11643
+ process.exit(3);
11644
+ }
11645
+ await new Promise((resolve) => setTimeout(resolve, intervalMs));
11646
+ }
11647
+ }
11648
+
11463
11649
  // src/commands/pentest.ts
11464
11650
  function stripProtocol(input) {
11465
11651
  const match = input.match(/^https?:\/\/(.+)/i);
@@ -11470,15 +11656,23 @@ function isValidDomain(value) {
11470
11656
  return value.includes(".") && !/\s/.test(value);
11471
11657
  }
11472
11658
  function registerPentestCommand(program3) {
11473
- const pentest = program3.command("pentest").description("Run and manage pentests").option("-t, --target <domain>", "Target domain to pentest").option("--profile <id>", "Pentest profile ID (alternative to --target)").option("--mode <mode>", "Pentest mode: quick or full", "full").option("-y, --yes", "Accept authorization terms without prompting").option("--no-watch", "Skip auto-watch after starting the pentest").addHelpText("after", `
11659
+ const pentest = program3.command("pentest").description("Run and manage pentests").option("-t, --target <domain>", "Target domain to pentest").option("--profile <id>", "Pentest profile ID (alternative to --target)").option("--mode <mode>", "Pentest mode: quick or full", "full").option("-y, --yes", "Accept authorization terms without prompting").option("--no-watch", "Skip auto-watch after starting the pentest").option("--deployment <type>", "Deployment type: cloud or on-prem", "cloud").addHelpText("after", `
11474
11660
  Examples:
11475
11661
  $ kinosec pentest -t delikado.org
11476
11662
  $ kinosec pentest --target delikado.org --mode quick
11477
- $ kinosec pentest --profile abc123`).action(async (options) => {
11663
+ $ kinosec pentest --profile abc123
11664
+ $ kinosec pentest -t internal.corp --deployment on-prem`).action(async (options) => {
11478
11665
  if (!options.target && !options.profile) {
11479
11666
  console.error("Provide --target <domain> or --profile <id>.");
11480
11667
  process.exit(2);
11481
11668
  }
11669
+ if (options.deployment) {
11670
+ options.deployment = options.deployment.replace(/-/g, "_");
11671
+ }
11672
+ if (options.deployment !== "cloud" && options.deployment !== "on_prem") {
11673
+ console.error("Invalid deployment type. Must be: cloud or on-prem.");
11674
+ process.exit(2);
11675
+ }
11482
11676
  try {
11483
11677
  const opts = program3.opts();
11484
11678
  const { token, apiUrl } = resolveToken(opts);
@@ -11515,10 +11709,18 @@ Examples:
11515
11709
  }
11516
11710
  }
11517
11711
  targetDisplay = domain;
11518
- params = { asset_id: asset.id, scan_mode: options.mode };
11712
+ const assetParams = {
11713
+ asset_id: asset.id,
11714
+ scan_mode: options.mode
11715
+ };
11716
+ if (options.deployment !== "cloud") {
11717
+ assetParams.deployment_type = options.deployment;
11718
+ }
11719
+ params = assetParams;
11519
11720
  }
11520
11721
  await confirmAuthorization(targetDisplay, Boolean(options.yes));
11521
11722
  const result = await client.scans.create(params);
11723
+ const isOnPrem = result.deployment_type === "on_prem";
11522
11724
  if (opts.json || opts.output === "json") {
11523
11725
  printJson(result);
11524
11726
  return;
@@ -11526,13 +11728,37 @@ Examples:
11526
11728
  printSuccess(`Pentest started: ${result.domain_or_ip}`);
11527
11729
  console.log(` Profile ID: ${result.scan_profile_id}`);
11528
11730
  console.log(` Mode: ${result.scan_mode}`);
11731
+ if (isOnPrem) {
11732
+ printOnPremInstructions(result);
11733
+ if (options.watch && isTTY()) {
11734
+ const divider2 = import_picocolors7.default.dim("\u2500".repeat(37));
11735
+ console.log(`
11736
+ ${divider2}
11737
+ `);
11738
+ await waitForScoutConnection(client, result.scan_profile_id, {
11739
+ timeoutMs: 15 * 60 * 1e3,
11740
+ intervalMs: 5 * 1e3,
11741
+ useJson: false
11742
+ });
11743
+ await watchPentest(client, result.scan_profile_id, {
11744
+ timeoutMs: 1800 * 1e3,
11745
+ intervalMs: 5 * 1e3,
11746
+ useJson: false,
11747
+ showFindingsSummary: true
11748
+ });
11749
+ } else {
11750
+ console.log(`
11751
+ Watch connection: kinosec pentest watch ${result.scan_profile_id}`);
11752
+ }
11753
+ return;
11754
+ }
11529
11755
  if (!options.watch || !isTTY()) {
11530
11756
  console.log(` Status: ${result.scan_status}`);
11531
11757
  console.log(`
11532
11758
  Watch progress: kinosec pentest watch ${result.scan_profile_id}`);
11533
11759
  return;
11534
11760
  }
11535
- const divider = isTTY() ? import_picocolors6.default.dim("\u2500".repeat(37)) : "-".repeat(37);
11761
+ const divider = isTTY() ? import_picocolors7.default.dim("\u2500".repeat(37)) : "-".repeat(37);
11536
11762
  console.log(`
11537
11763
  ${divider}
11538
11764
  `);
@@ -11578,7 +11804,7 @@ Examples:
11578
11804
  ])
11579
11805
  );
11580
11806
  if (result.next_cursor) {
11581
- console.log(import_picocolors6.default.dim(`
11807
+ console.log(import_picocolors7.default.dim(`
11582
11808
  More results available. Use --cursor ${result.next_cursor}`));
11583
11809
  }
11584
11810
  } catch (error2) {
@@ -11605,6 +11831,7 @@ Examples:
11605
11831
  ["Target", detail.domain_or_ip],
11606
11832
  ["Mode", detail.scan_mode],
11607
11833
  ["Type", detail.scan_type],
11834
+ ...detail.deployment_type ? [["Deployment", detail.deployment_type === "on_prem" ? "On-Prem" : "Cloud"]] : [],
11608
11835
  ["Status", detail.scan_status],
11609
11836
  ["Activity", detail.current_activity || "(none)"],
11610
11837
  ["Created", detail.created_at],
@@ -11641,10 +11868,25 @@ Examples:
11641
11868
  const opts = program3.opts();
11642
11869
  const { token, apiUrl } = resolveToken(opts);
11643
11870
  const client = new KinoSecClient({ apiUrl, token });
11871
+ const useJson = Boolean(opts.json || opts.output === "json");
11872
+ const detail = await client.scans.get(id);
11873
+ if (detail.deployment_type === "on_prem") {
11874
+ try {
11875
+ const scout = await client.scans.scoutStatus(id);
11876
+ if (scout.status !== "connected") {
11877
+ await waitForScoutConnection(client, id, {
11878
+ timeoutMs: 15 * 60 * 1e3,
11879
+ intervalMs: parseInt(options.interval, 10) * 1e3,
11880
+ useJson
11881
+ });
11882
+ }
11883
+ } catch {
11884
+ }
11885
+ }
11644
11886
  await watchPentest(client, id, {
11645
11887
  timeoutMs: parseInt(options.timeout, 10) * 1e3,
11646
11888
  intervalMs: parseInt(options.interval, 10) * 1e3,
11647
- useJson: Boolean(opts.json || opts.output === "json"),
11889
+ useJson,
11648
11890
  showFindingsSummary: true
11649
11891
  });
11650
11892
  } catch (error2) {
@@ -11654,10 +11896,10 @@ Examples:
11654
11896
  }
11655
11897
 
11656
11898
  // src/commands/asset.ts
11657
- var import_picocolors8 = __toESM(require_picocolors(), 1);
11899
+ var import_picocolors9 = __toESM(require_picocolors(), 1);
11658
11900
 
11659
11901
  // src/commands/asset-auth.ts
11660
- var import_picocolors7 = __toESM(require_picocolors(), 1);
11902
+ var import_picocolors8 = __toESM(require_picocolors(), 1);
11661
11903
 
11662
11904
  // src/lib/resolve-asset.ts
11663
11905
  var UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-/i;
@@ -11914,7 +12156,7 @@ Examples:
11914
12156
  }
11915
12157
  if (!creds.has_credentials) {
11916
12158
  console.log(`Asset: ${asset.asset_value}`);
11917
- console.log(`Auth Type: ${import_picocolors7.default.dim("(none)")}`);
12159
+ console.log(`Auth Type: ${import_picocolors8.default.dim("(none)")}`);
11918
12160
  console.log(`Has Credentials: no`);
11919
12161
  return;
11920
12162
  }
@@ -11935,9 +12177,9 @@ Examples:
11935
12177
  const hasAny = ta.has_username || ta.has_cookie || ta.has_bearer_token || ta.has_apikey;
11936
12178
  if (hasAny) {
11937
12179
  const detail = ta.has_username ? "username" : ta.has_bearer_token ? "bearer token" : ta.has_cookie ? "cookie" : "API key";
11938
- console.log(`Target Account: ${import_picocolors7.default.green("configured")} (${detail})`);
12180
+ console.log(`Target Account: ${import_picocolors8.default.green("configured")} (${detail})`);
11939
12181
  } else {
11940
- console.log(`Target Account: ${import_picocolors7.default.dim("not configured")}`);
12182
+ console.log(`Target Account: ${import_picocolors8.default.dim("not configured")}`);
11941
12183
  }
11942
12184
  }
11943
12185
  } catch (error2) {
@@ -12088,7 +12330,7 @@ Examples:
12088
12330
  ])
12089
12331
  );
12090
12332
  if (result.next_cursor) {
12091
- console.log(import_picocolors8.default.dim(`
12333
+ console.log(import_picocolors9.default.dim(`
12092
12334
  More results available. Use --cursor ${result.next_cursor}`));
12093
12335
  }
12094
12336
  } catch (error2) {
@@ -12200,7 +12442,7 @@ Examples:
12200
12442
  }
12201
12443
 
12202
12444
  // src/commands/finding.ts
12203
- var import_picocolors9 = __toESM(require_picocolors(), 1);
12445
+ var import_picocolors10 = __toESM(require_picocolors(), 1);
12204
12446
  function formatPoc(poc) {
12205
12447
  if (poc == null) return null;
12206
12448
  if (typeof poc === "string") return poc.trim() || null;
@@ -12241,7 +12483,7 @@ Examples:
12241
12483
  ])
12242
12484
  );
12243
12485
  if (result.next_cursor) {
12244
- console.log(import_picocolors9.default.dim(`
12486
+ console.log(import_picocolors10.default.dim(`
12245
12487
  More results available. Use --cursor ${result.next_cursor}`));
12246
12488
  }
12247
12489
  } catch (error2) {
@@ -12329,7 +12571,7 @@ Examples:
12329
12571
  }
12330
12572
 
12331
12573
  // src/commands/report.ts
12332
- var import_picocolors10 = __toESM(require_picocolors(), 1);
12574
+ var import_picocolors11 = __toESM(require_picocolors(), 1);
12333
12575
  function renderMarkdown(data) {
12334
12576
  const { scan, findings } = data;
12335
12577
  const lines = [];
@@ -12406,7 +12648,7 @@ Examples:
12406
12648
  ])
12407
12649
  );
12408
12650
  if (result.next_cursor) {
12409
- console.log(import_picocolors10.default.dim(`
12651
+ console.log(import_picocolors11.default.dim(`
12410
12652
  More results available. Use --cursor ${result.next_cursor}`));
12411
12653
  }
12412
12654
  } catch (error2) {
@@ -12477,7 +12719,7 @@ Examples:
12477
12719
  }
12478
12720
 
12479
12721
  // src/commands/token.ts
12480
- var import_picocolors11 = __toESM(require_picocolors(), 1);
12722
+ var import_picocolors12 = __toESM(require_picocolors(), 1);
12481
12723
  function registerTokenCommand(program3) {
12482
12724
  const token = program3.command("token").description("Manage API tokens");
12483
12725
  token.command("list").description("List API tokens").option("--limit <n>", "Max results per page", "20").option("--cursor <cursor>", "Pagination cursor").addHelpText("after", `
@@ -12512,7 +12754,7 @@ Examples:
12512
12754
  ])
12513
12755
  );
12514
12756
  if (result.next_cursor) {
12515
- console.log(import_picocolors11.default.dim(`
12757
+ console.log(import_picocolors12.default.dim(`
12516
12758
  More results available. Use --cursor ${result.next_cursor}`));
12517
12759
  }
12518
12760
  } catch (error2) {
@@ -12539,7 +12781,7 @@ Examples:
12539
12781
  }
12540
12782
  printSuccess(`Token created: ${result.name}`);
12541
12783
  console.log("");
12542
- console.log(import_picocolors11.default.bold(" Token: ") + result.token);
12784
+ console.log(import_picocolors12.default.bold(" Token: ") + result.token);
12543
12785
  console.log("");
12544
12786
  printWarning("Save this token \u2014 it will not be shown again.");
12545
12787
  console.log(` ID: ${result.id}`);
@@ -12572,15 +12814,16 @@ Examples:
12572
12814
 
12573
12815
  // src/cli.ts
12574
12816
  var program2 = new Command();
12575
- program2.name("kinosec").description("KinoSec CLI \u2014 run pentests from your terminal").version("0.7.1", "-v, --version").option("--token <pat>", "API token (overrides KINOSEC_TOKEN)").option("--output <format>", "Output format: text or json", "text").option("--json", "Shorthand for --output json").option("--no-color", "Disable colors");
12817
+ program2.name("kinosec").description("KinoSec CLI \u2014 run pentests from your terminal").version("0.9.0", "-v, --version").option("--token <pat>", "API token (overrides KINOSEC_TOKEN)").option("--output <format>", "Output format: text or json", "text").option("--json", "Shorthand for --output json").option("--no-color", "Disable colors");
12576
12818
  registerAuthCommand(program2);
12577
12819
  registerMeCommand(program2);
12820
+ registerCreditsCommand(program2);
12578
12821
  registerPentestCommand(program2);
12579
12822
  registerAssetCommand(program2);
12580
12823
  registerFindingCommand(program2);
12581
12824
  registerReportCommand(program2);
12582
12825
  registerTokenCommand(program2);
12583
- program2.parseAsync(process.argv).then(() => checkForUpdate("0.7.1")).catch(handleError);
12826
+ program2.parseAsync(process.argv).then(() => checkForUpdate("0.9.0")).catch(handleError);
12584
12827
  /*! Bundled license information:
12585
12828
 
12586
12829
  ky/distribution/index.js: