camstack 0.5.2 → 0.5.3

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 +122 -15
  2. package/package.json +1 -1
package/dist/cli.js CHANGED
@@ -8,7 +8,7 @@ import { createRequire } from "module";
8
8
  import { fileURLToPath } from "url";
9
9
  import { dirname, resolve as resolve2 } from "path";
10
10
  import * as os4 from "os";
11
- import { parseArgs as parseArgs4 } from "util";
11
+ import { parseArgs as parseArgs5 } from "util";
12
12
 
13
13
  // src/commands/serve.ts
14
14
  import { parseArgs } from "util";
@@ -444,29 +444,29 @@ function isUnknown(_value) {
444
444
  async function resolveServerInteractive(presetNamespace) {
445
445
  const { discoverNodes, resolveHubFromDiscovered } = await import("./discover-NPUMWBRW.js");
446
446
  if (presetNamespace) {
447
- const spinner3 = clack.spinner();
448
- spinner3.start(`Discovering hub on LAN (namespace "${presetNamespace}")`);
447
+ const spinner4 = clack.spinner();
448
+ spinner4.start(`Discovering hub on LAN (namespace "${presetNamespace}")`);
449
449
  const filtered = await discoverNodes({ namespace: presetNamespace });
450
450
  if (filtered.length === 0) {
451
- spinner3.stop(`No nodes responded for namespace "${presetNamespace}".`);
451
+ spinner4.stop(`No nodes responded for namespace "${presetNamespace}".`);
452
452
  throw new Error(`Hub running? Same LAN?`);
453
453
  }
454
454
  const hub = await resolveHubFromDiscovered(filtered);
455
455
  if (!hub) {
456
- spinner3.stop(`Found ${filtered.length} node(s) but none responded on HTTPS :4443.`);
456
+ spinner4.stop(`Found ${filtered.length} node(s) but none responded on HTTPS :4443.`);
457
457
  throw new Error("Pass --server explicitly if the hub uses a non-default port.");
458
458
  }
459
- spinner3.stop(`Hub: ${hub.nodeID} @ https://${hub.address}:${hub.port}`);
459
+ spinner4.stop(`Hub: ${hub.nodeID} @ https://${hub.address}:${hub.port}`);
460
460
  return `https://${hub.address}:${hub.port}`;
461
461
  }
462
- const spinner2 = clack.spinner();
463
- spinner2.start("Scanning LAN for camstack nodes (UDP multicast)");
462
+ const spinner3 = clack.spinner();
463
+ spinner3.start("Scanning LAN for camstack nodes (UDP multicast)");
464
464
  const all = await discoverNodes({});
465
465
  if (all.length === 0) {
466
- spinner2.stop("No camstack nodes responded on LAN.");
466
+ spinner3.stop("No camstack nodes responded on LAN.");
467
467
  throw new Error("Either pass --server or verify the hub is running on the same network.");
468
468
  }
469
- spinner2.stop(`Found ${all.length} node(s).`);
469
+ spinner3.stop(`Found ${all.length} node(s).`);
470
470
  const chosen = await askSelect(
471
471
  "Select a hub to log into",
472
472
  all.map((n) => ({
@@ -571,8 +571,8 @@ async function logoutCommand(opts) {
571
571
  return;
572
572
  }
573
573
  clack.intro("camstack logout");
574
- const spinner2 = clack.spinner();
575
- spinner2.start(`Revoking token on ${session.server}`);
574
+ const spinner3 = clack.spinner();
575
+ spinner3.start(`Revoking token on ${session.server}`);
576
576
  try {
577
577
  await callTrpcMutation(
578
578
  `${session.server}/trpc/userManagement.revokeScopedToken?batch=1`,
@@ -580,9 +580,9 @@ async function logoutCommand(opts) {
580
580
  { id: session.tokenId },
581
581
  isUnknown
582
582
  );
583
- spinner2.stop("Token revoked server-side.");
583
+ spinner3.stop("Token revoked server-side.");
584
584
  } catch (err) {
585
- spinner2.stop(`Server-side revoke failed (${err instanceof Error ? err.message : String(err)}). Removing local cache anyway.`);
585
+ spinner3.stop(`Server-side revoke failed (${err instanceof Error ? err.message : String(err)}). Removing local cache anyway.`);
586
586
  }
587
587
  clearSession(session.server);
588
588
  clack.outro(`\u2713 Logged out of ${session.server}`);
@@ -601,6 +601,11 @@ function whoamiCommand(opts) {
601
601
  console.log(` createdAt: ${new Date(session.createdAt).toISOString()}`);
602
602
  }
603
603
 
604
+ // src/commands/update.ts
605
+ import { parseArgs as parseArgs4 } from "util";
606
+ import { spawn } from "child_process";
607
+ import * as clack2 from "@clack/prompts";
608
+
604
609
  // src/update-notifier.ts
605
610
  import * as fs4 from "fs";
606
611
  import * as path4 from "path";
@@ -700,6 +705,91 @@ function printBanner(pkgName, current, latest) {
700
705
  process.stderr.write(lines.join("\n"));
701
706
  }
702
707
 
708
+ // src/commands/update.ts
709
+ function isRunningViaNpx() {
710
+ const entry = process.argv[1] ?? "";
711
+ return entry.includes("/_npx/") || entry.includes("\\_npx\\");
712
+ }
713
+ function runNpmInstall(spec) {
714
+ return new Promise((resolve3, reject) => {
715
+ const proc = spawn("npm", ["install", "-g", spec], { stdio: "inherit" });
716
+ proc.on("exit", (code) => {
717
+ if (code === 0) resolve3();
718
+ else reject(new Error(`npm install -g ${spec} exited with code ${code}`));
719
+ });
720
+ proc.on("error", reject);
721
+ });
722
+ }
723
+ async function runUpdate(args) {
724
+ const { values } = parseArgs4({
725
+ args: [...args],
726
+ options: {
727
+ version: { type: "string", short: "V" },
728
+ yes: { type: "boolean", short: "y" }
729
+ },
730
+ strict: true,
731
+ allowPositionals: false
732
+ });
733
+ const pinnedVersion = typeof values.version === "string" ? values.version : void 0;
734
+ const skipConfirm = values.yes === true;
735
+ if (isRunningViaNpx()) {
736
+ console.log("You are running via npx \u2014 versions are fetched per-invocation.");
737
+ console.log("Just call `npx camstack@latest <cmd>` for the freshest release.");
738
+ console.log("To install globally: `npm install -g camstack@latest`");
739
+ return;
740
+ }
741
+ clack2.intro("camstack update");
742
+ let target;
743
+ if (pinnedVersion) {
744
+ target = pinnedVersion;
745
+ } else {
746
+ const spinner3 = clack2.spinner();
747
+ spinner3.start("Checking npm registry");
748
+ const latest = await fetchLatestVersion("camstack");
749
+ if (!latest) {
750
+ spinner3.stop("Could not reach the npm registry.");
751
+ throw new Error("Network error \u2014 try again or pass --version <x.y.z>");
752
+ }
753
+ spinner3.stop(`Latest published: ${latest}`);
754
+ target = latest;
755
+ }
756
+ const { createRequire: createRequire2 } = await import("module");
757
+ const { fileURLToPath: fileURLToPath2 } = await import("url");
758
+ const { dirname: dirname2, resolve: resolve3 } = await import("path");
759
+ const require3 = createRequire2(import.meta.url);
760
+ const here = dirname2(fileURLToPath2(import.meta.url));
761
+ let current = "0.0.0";
762
+ try {
763
+ const pkg = require3(resolve3(here, "..", "package.json"));
764
+ current = pkg.version;
765
+ } catch {
766
+ }
767
+ if (current === target && !pinnedVersion) {
768
+ clack2.outro(`Already at ${current} \u2014 no update needed.`);
769
+ return;
770
+ }
771
+ clack2.log.info(`Current: ${current} \u2192 Target: ${target}`);
772
+ if (!skipConfirm) {
773
+ const ok = await clack2.confirm({
774
+ message: `Install camstack@${target} globally with npm?`,
775
+ initialValue: true
776
+ });
777
+ if (clack2.isCancel(ok) || ok === false) {
778
+ clack2.cancel("Cancelled.");
779
+ return;
780
+ }
781
+ }
782
+ console.log("");
783
+ try {
784
+ await runNpmInstall(`camstack@${target}`);
785
+ } catch (err) {
786
+ clack2.log.error(err instanceof Error ? err.message : String(err));
787
+ clack2.log.warn("If you see EACCES, your global npm prefix may need sudo or a different prefix. See https://docs.npmjs.com/resolving-eacces-permissions-errors-when-installing-packages-globally");
788
+ throw err;
789
+ }
790
+ clack2.outro(`\u2713 Installed camstack@${target}. Verify with: camstack --version`);
791
+ }
792
+
703
793
  // src/cli.ts
704
794
  var __dirname = dirname(fileURLToPath(import.meta.url));
705
795
  var require2 = createRequire(import.meta.url);
@@ -852,6 +942,23 @@ function buildCommands() {
852
942
  " -c, --cluster Push to hub + every online agent (requires admin token)"
853
943
  ].join("\n")
854
944
  },
945
+ {
946
+ name: "update",
947
+ aliases: ["upgrade"],
948
+ summary: "Self-upgrade \u2014 runs `npm install -g camstack@latest`",
949
+ run: runUpdate,
950
+ help: () => [
951
+ "Usage: camstack update [options]",
952
+ " camstack upgrade [options] (alias)",
953
+ "",
954
+ "With no flags: fetches the latest version from npm, confirms,",
955
+ " then runs `npm install -g camstack@<latest>`.",
956
+ "",
957
+ "Options:",
958
+ " -V, --version <x.y.z> Install a specific version (pin/downgrade)",
959
+ " -y, --yes Skip confirmation (useful for scripts)"
960
+ ].join("\n")
961
+ },
855
962
  {
856
963
  name: "info",
857
964
  summary: "Print detailed version and platform info",
@@ -868,7 +975,7 @@ function buildCommands() {
868
975
  }
869
976
  function parseSubcommandArgs(args, options, allowPositionals) {
870
977
  if (args.some((a) => HELP_FLAGS.has(a))) return null;
871
- const parsed = parseArgs4({
978
+ const parsed = parseArgs5({
872
979
  args: [...args],
873
980
  options,
874
981
  allowPositionals,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "camstack",
3
- "version": "0.5.2",
3
+ "version": "0.5.3",
4
4
  "description": "CLI tool for managing and running CamStack server",
5
5
  "keywords": [
6
6
  "camstack",