@within-7/jetr 0.7.0 → 0.7.2

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 (2) hide show
  1. package/dist/cli.js +83 -108
  2. package/package.json +1 -1
package/dist/cli.js CHANGED
@@ -469,8 +469,22 @@ async function promptSelection(options) {
469
469
 
470
470
  // src/cli.ts
471
471
  var program = new Command();
472
- program.name("jetr").description("Deploy static sites instantly").version("0.2.0");
473
- program.argument("[paths...]", "Files or directory to deploy").action(async (paths) => {
472
+ program.name("jetr").description(`Deploy static sites instantly to {name}.jetr.within-7.com
473
+
474
+ Just run "jetr" in any directory to deploy. First time? Run "jetr login" first.
475
+
476
+ The name can be a simple name (gets .jetr.within-7.com subdomain) or a full
477
+ domain like docs.within-7.com or blog.example.com for custom domain hosting.
478
+
479
+ Examples:
480
+ $ jetr Deploy current directory (random name or from .jetrrc)
481
+ $ jetr ./dist Deploy the ./dist directory
482
+ $ jetr ./dist my-blog Deploy as my-blog.jetr.within-7.com
483
+ $ jetr ./dist docs.within-7.com Deploy to custom domain (auto-configures route)
484
+ $ jetr ./dist blog.example.com Deploy to external domain (shows DNS setup)
485
+ $ jetr index.html Deploy a single file
486
+ $ jetr *.html *.css my-site Deploy multiple files to a named site`).version("0.7.1");
487
+ program.argument("[paths...]", "directory, file(s), and/or site name to deploy").action(async (paths) => {
474
488
  const config = loadConfig();
475
489
  if (!config.token) {
476
490
  console.error(chalk2.red("Not logged in."));
@@ -478,51 +492,68 @@ program.argument("[paths...]", "Files or directory to deploy").action(async (pat
478
492
  process.exit(1);
479
493
  }
480
494
  const { mode, dir, files, explicitName } = parseDeployArgs(paths);
481
- if (mode === "directory") {
482
- const rcDir = dir;
483
- let siteName = await resolveSiteName(rcDir, explicitName);
484
- if (!siteName) {
485
- siteName = generateName();
486
- console.log(chalk2.dim(`Site name: ${siteName}`));
495
+ let rawName = mode === "directory" ? await resolveSiteName(dir, explicitName) : explicitName;
496
+ if (!rawName) {
497
+ rawName = generateName();
498
+ console.log(chalk2.dim(`Site name: ${rawName}`));
499
+ }
500
+ if (mode === "files") {
501
+ console.log(chalk2.dim(`File mode: ${files.map((f) => basename2(f)).join(", ")}`));
502
+ }
503
+ const isCustomDomain = rawName.includes(".");
504
+ const siteName = isCustomDomain ? sanitizeName(rawName.replace(/\./g, "-")) || generateName() : sanitizeName(rawName) || generateName();
505
+ const customHostname = isCustomDomain ? rawName.toLowerCase().trim() : null;
506
+ const spinner = ora("").start();
507
+ try {
508
+ await ensureSiteExists(spinner, siteName);
509
+ if (customHostname) {
510
+ spinner.text = `Binding domain ${customHostname}...`;
511
+ try {
512
+ const domainResult = await api.addDomain(siteName, customHostname);
513
+ if (domainResult.route_registered) {
514
+ spinner.text = `Route registered for ${customHostname}`;
515
+ }
516
+ } catch (e) {
517
+ if (!e.message.includes("already in use")) throw e;
518
+ }
487
519
  }
488
- siteName = sanitizeName(siteName);
489
- if (!siteName) siteName = generateName();
490
- const spinner = ora("").start();
491
- try {
492
- await ensureSiteExists(spinner, siteName);
493
- const result = await deploy(siteName, rcDir, (msg) => {
494
- spinner.text = msg;
495
- });
496
- printDeployResult(spinner, siteName, result);
497
- const rc = loadRc(rcDir);
498
- saveRc(rcDir, updateRc(rc, siteName));
499
- } catch (e) {
500
- spinner.fail(e.message);
501
- process.exit(1);
520
+ const result = mode === "directory" ? await deploy(siteName, dir, (msg) => {
521
+ spinner.text = msg;
522
+ }) : await deployFiles(siteName, files, (msg) => {
523
+ spinner.text = msg;
524
+ });
525
+ spinner.succeed("Deployed!");
526
+ console.log();
527
+ if (customHostname) {
528
+ console.log(` ${chalk2.bold("Domain:")} ${chalk2.cyan(customHostname)}`);
529
+ console.log(` ${chalk2.bold("Site:")} ${chalk2.dim(siteName)}`);
530
+ } else {
531
+ console.log(` ${chalk2.bold("Site:")} ${chalk2.cyan(siteName)}`);
502
532
  }
503
- } else {
504
- console.log(chalk2.dim(`File mode: ${files.map((f) => basename2(f)).join(", ")}`));
505
- let siteName = explicitName;
506
- if (!siteName) {
507
- siteName = generateName();
508
- console.log(chalk2.dim(`Site name: ${siteName}`));
533
+ console.log(` ${chalk2.bold("URL:")} ${chalk2.green(customHostname ? `https://${customHostname}` : result.url)}`);
534
+ console.log(
535
+ ` ${chalk2.bold("Files:")} ${result.filesUploaded} uploaded` + (result.filesDeleted > 0 ? `, ${result.filesDeleted} deleted` : "") + (result.filesUnchanged > 0 ? `, ${result.filesUnchanged} unchanged` : "") + (result.filesIgnored > 0 ? chalk2.dim(` (${result.filesIgnored} ignored)`) : "")
536
+ );
537
+ console.log(` ${chalk2.bold("Size:")} ${formatBytes(result.totalSize)}`);
538
+ if (customHostname) {
539
+ const isWithin7 = customHostname.endsWith(".within-7.com");
540
+ if (!isWithin7) {
541
+ console.log();
542
+ console.log(chalk2.yellow(` DNS setup required:`));
543
+ console.log(` ${customHostname} CNAME jetr-serve.lixilei.workers.dev`);
544
+ }
509
545
  }
510
- siteName = sanitizeName(siteName);
511
- if (!siteName) siteName = generateName();
512
- const spinner = ora("").start();
513
- try {
514
- await ensureSiteExists(spinner, siteName);
515
- const result = await deployFiles(siteName, files, (msg) => {
516
- spinner.text = msg;
517
- });
518
- printDeployResult(spinner, siteName, result);
519
- } catch (e) {
520
- spinner.fail(e.message);
521
- process.exit(1);
546
+ console.log();
547
+ if (mode === "directory") {
548
+ const rc = loadRc(dir);
549
+ saveRc(dir, updateRc(rc, rawName));
522
550
  }
551
+ } catch (e) {
552
+ spinner.fail(e.message);
553
+ process.exit(1);
523
554
  }
524
555
  });
525
- program.command("login").description("Login with username and password").argument("[user]", "Username").argument("[password]", "Password").option("--token <token>", "Login directly with a JWT token").action(async (user, password, opts) => {
556
+ program.command("login").description("Authenticate with auth-gateway (interactive prompts if args omitted)").argument("[user]", "your username").argument("[password]", "your password (hidden input if omitted)").option("--token <token>", "skip login, use a JWT token directly (for CI/scripts)").action(async (user, password, opts) => {
526
557
  if (opts?.token) {
527
558
  saveConfig({ token: opts.token });
528
559
  console.log(chalk2.green("\u2713 Token saved"));
@@ -554,7 +585,7 @@ program.command("login").description("Login with username and password").argumen
554
585
  process.exit(1);
555
586
  }
556
587
  });
557
- program.command("whoami").description("Show current login status").action(() => {
588
+ program.command("whoami").description("Show who is logged in").action(() => {
558
589
  const config = loadConfig();
559
590
  if (!config.token) {
560
591
  console.log(chalk2.red("Not logged in."));
@@ -564,11 +595,11 @@ program.command("whoami").description("Show current login status").action(() =>
564
595
  console.log(`User: ${chalk2.bold(config.user || "unknown")}`);
565
596
  console.log(`Token: ${config.token.slice(0, 20)}...`);
566
597
  });
567
- program.command("logout").description("Clear saved login credentials").action(() => {
598
+ program.command("logout").description("Clear saved credentials (~/.jetr/config.json)").action(() => {
568
599
  saveConfig({ user: void 0, token: void 0 });
569
600
  console.log(chalk2.green("\u2713 Logged out"));
570
601
  });
571
- program.command("init").description("Create .jetrignore with default patterns").argument("[directory]", "Target directory", ".").action(async (directory) => {
602
+ program.command("init").description("Generate .jetrignore with sensible defaults (node_modules, .env, .git, etc)").argument("[directory]", "target directory", ".").action(async (directory) => {
572
603
  const created = createJetrignore(resolve2(directory));
573
604
  if (created) {
574
605
  console.log(chalk2.green("\u2713 Created .jetrignore with default patterns"));
@@ -576,7 +607,7 @@ program.command("init").description("Create .jetrignore with default patterns").
576
607
  console.log(chalk2.yellow(".jetrignore already exists"));
577
608
  }
578
609
  });
579
- program.command("create").description("Create a new site").argument("<name>", "Site name").option("-p, --password <password>", "Set access password").option("-e, --expires <seconds>", "Expire after N seconds", parseInt).action(async (name, opts) => {
610
+ program.command("create").description("Create an empty site without deploying files").argument("<name>", "site name (e.g. my-blog) or domain (e.g. docs.example.com)").option("-p, --password <password>", "require password to view the site").option("-e, --expires <seconds>", "auto-delete after N seconds (e.g. 86400 = 1 day)", parseInt).action(async (name, opts) => {
580
611
  const spinner = ora("Creating site...").start();
581
612
  try {
582
613
  const site = await api.createSite(name, {
@@ -596,7 +627,7 @@ program.command("create").description("Create a new site").argument("<name>", "S
596
627
  process.exit(1);
597
628
  }
598
629
  });
599
- program.command("list").alias("ls").description("List your sites").action(async () => {
630
+ program.command("list").alias("ls").description("List all your deployed sites").action(async () => {
600
631
  try {
601
632
  const { sites } = await api.listSites();
602
633
  if (sites.length === 0) {
@@ -621,7 +652,7 @@ program.command("list").alias("ls").description("List your sites").action(async
621
652
  process.exit(1);
622
653
  }
623
654
  });
624
- program.command("info").description("Show site details").argument("<name>", "Site name").action(async (name) => {
655
+ program.command("info").description("Show site details: files, tokens, config").argument("<name>", "site name").action(async (name) => {
625
656
  try {
626
657
  const site = await api.getSite(name);
627
658
  console.log(`${chalk2.bold(site.name)}`);
@@ -660,7 +691,7 @@ program.command("info").description("Show site details").argument("<name>", "Sit
660
691
  process.exit(1);
661
692
  }
662
693
  });
663
- program.command("delete").alias("rm").description("Delete a site and all its files").argument("<name>", "Site name").action(async (name) => {
694
+ program.command("delete").alias("rm").description("Permanently delete a site, all its files, and custom domains").argument("<name>", "site name").action(async (name) => {
664
695
  const spinner = ora(`Deleting ${name}...`).start();
665
696
  try {
666
697
  await api.deleteSite(name);
@@ -670,7 +701,7 @@ program.command("delete").alias("rm").description("Delete a site and all its fil
670
701
  process.exit(1);
671
702
  }
672
703
  });
673
- program.command("password").description("Set or remove site password").argument("<name>", "Site name").argument("[password]", "New password (omit to remove)").action(async (name, password) => {
704
+ program.command("password").description("Set a visitor password, or remove it (omit password to remove)").argument("<name>", "site name").argument("[password]", "new password (omit to make site public)").action(async (name, password) => {
674
705
  try {
675
706
  const site = await api.updateSite(name, { password: password ?? null });
676
707
  if (site.password_protected) {
@@ -683,8 +714,8 @@ program.command("password").description("Set or remove site password").argument(
683
714
  process.exit(1);
684
715
  }
685
716
  });
686
- var tokenCmd = program.command("token").description("Manage share tokens");
687
- tokenCmd.command("create").description("Create a share token").argument("<site>", "Site name").option("-n, --note <note>", "Token note").option("-e, --expires <seconds>", "Expire after N seconds", parseInt).action(
717
+ var tokenCmd = program.command("token").description("Generate or revoke share links for password-protected sites");
718
+ tokenCmd.command("create").description("Generate a share URL that bypasses password (e.g. for QA team)").argument("<site>", "site name").option("-n, --note <note>", "label for this token (e.g. 'QA team')").option("-e, --expires <seconds>", "auto-expire after N seconds", parseInt).action(
688
719
  async (site, opts) => {
689
720
  try {
690
721
  const token = await api.createToken(site, {
@@ -705,7 +736,7 @@ tokenCmd.command("create").description("Create a share token").argument("<site>"
705
736
  }
706
737
  }
707
738
  );
708
- tokenCmd.command("revoke").description("Revoke a share token").argument("<site>", "Site name").argument("<id>", "Token ID").action(async (site, id) => {
739
+ tokenCmd.command("revoke").description("Revoke a share link (get ID from `jetr info <site>`)").argument("<site>", "site name").argument("<id>", "token ID").action(async (site, id) => {
709
740
  try {
710
741
  await api.revokeToken(site, id);
711
742
  console.log(chalk2.green(`\u2713 Token ${id} revoked`));
@@ -714,51 +745,6 @@ tokenCmd.command("revoke").description("Revoke a share token").argument("<site>"
714
745
  process.exit(1);
715
746
  }
716
747
  });
717
- var domainCmd = program.command("domain").description("Manage custom domains");
718
- domainCmd.command("add").description("Add a custom domain to a site").argument("<site>", "Site name").argument("<hostname>", "Domain (e.g. docs.example.com)").action(async (site, hostname) => {
719
- try {
720
- const result = await api.addDomain(site, hostname);
721
- console.log(chalk2.green(`\u2713 Domain added: ${result.hostname}`));
722
- if (result.route_registered) {
723
- console.log(chalk2.green(` Route auto-registered on Cloudflare`));
724
- }
725
- if (result.note) {
726
- console.log(` ${chalk2.dim(String(result.note))}`);
727
- }
728
- if (result.cname_target) {
729
- console.log();
730
- console.log(` DNS: ${chalk2.cyan(String(result.hostname))} CNAME ${chalk2.dim(String(result.cname_target))}`);
731
- }
732
- console.log();
733
- } catch (e) {
734
- console.error(chalk2.red(e.message));
735
- process.exit(1);
736
- }
737
- });
738
- domainCmd.command("list").description("List custom domains for a site").argument("<site>", "Site name").action(async (site) => {
739
- try {
740
- const { domains } = await api.listDomains(site);
741
- if (domains.length === 0) {
742
- console.log(chalk2.dim("No custom domains. Run: jetr domain add <site> <hostname>"));
743
- return;
744
- }
745
- for (const d of domains) {
746
- console.log(` ${chalk2.bold(d.hostname)} ${chalk2.dim(`(id: ${d.id})`)}`);
747
- }
748
- } catch (e) {
749
- console.error(chalk2.red(e.message));
750
- process.exit(1);
751
- }
752
- });
753
- domainCmd.command("remove").description("Remove a custom domain").argument("<site>", "Site name").argument("<id>", "Domain ID").action(async (site, id) => {
754
- try {
755
- await api.removeDomain(site, id);
756
- console.log(chalk2.green(`\u2713 Domain removed`));
757
- } catch (e) {
758
- console.error(chalk2.red(e.message));
759
- process.exit(1);
760
- }
761
- });
762
748
  function parseDeployArgs(paths) {
763
749
  if (paths.length === 0) {
764
750
  return { mode: "directory", dir: "." };
@@ -828,17 +814,6 @@ async function ensureSiteExists(spinner, siteName) {
828
814
  await api.createSite(siteName);
829
815
  }
830
816
  }
831
- function printDeployResult(spinner, siteName, result) {
832
- spinner.succeed("Deployed!");
833
- console.log();
834
- console.log(` ${chalk2.bold("Site:")} ${chalk2.cyan(siteName)}`);
835
- console.log(` ${chalk2.bold("URL:")} ${chalk2.green(result.url)}`);
836
- console.log(
837
- ` ${chalk2.bold("Files:")} ${result.filesUploaded} uploaded` + (result.filesDeleted > 0 ? `, ${result.filesDeleted} deleted` : "") + (result.filesUnchanged > 0 ? `, ${result.filesUnchanged} unchanged` : "") + (result.filesIgnored > 0 ? chalk2.dim(` (${result.filesIgnored} ignored)`) : "")
838
- );
839
- console.log(` ${chalk2.bold("Size:")} ${formatBytes(result.totalSize)}`);
840
- console.log();
841
- }
842
817
  function readSecret(prompt) {
843
818
  return new Promise((resolve3) => {
844
819
  process.stdout.write(prompt);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@within-7/jetr",
3
- "version": "0.7.0",
3
+ "version": "0.7.2",
4
4
  "description": "CLI for Jetr static site hosting",
5
5
  "type": "module",
6
6
  "bin": {