@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.
- package/dist/cli.js +83 -108
- 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(
|
|
473
|
-
|
|
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
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
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
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
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
|
-
}
|
|
504
|
-
console.log(
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
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
|
-
|
|
511
|
-
if (
|
|
512
|
-
|
|
513
|
-
|
|
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("
|
|
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
|
|
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
|
|
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("
|
|
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
|
|
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>", "
|
|
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("
|
|
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
|
|
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("
|
|
687
|
-
tokenCmd.command("create").description("
|
|
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
|
|
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);
|