camstack 0.7.1 → 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 +92 -12
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -376,11 +376,23 @@ function resolveSessionFile(serverUrl) {
|
|
|
376
376
|
if (files.length === 0) return null;
|
|
377
377
|
return files.map((f) => ({ f, mtime: fs3.statSync(f).mtimeMs })).sort((a, b) => b.mtime - a.mtime)[0].f;
|
|
378
378
|
}
|
|
379
|
+
function isSessionFile(value) {
|
|
380
|
+
if (value === null || typeof value !== "object") return false;
|
|
381
|
+
const v = value;
|
|
382
|
+
if (typeof v.server !== "string") return false;
|
|
383
|
+
if (typeof v.username !== "string") return false;
|
|
384
|
+
if (typeof v.token !== "string") return false;
|
|
385
|
+
if (typeof v.tokenId !== "string") return false;
|
|
386
|
+
if (typeof v.createdAt !== "number") return false;
|
|
387
|
+
if (!Array.isArray(v.scopes)) return false;
|
|
388
|
+
return true;
|
|
389
|
+
}
|
|
379
390
|
function loadSession(serverUrl) {
|
|
380
391
|
const file = resolveSessionFile(serverUrl);
|
|
381
392
|
if (!file) return null;
|
|
382
393
|
try {
|
|
383
|
-
|
|
394
|
+
const parsed = JSON.parse(fs3.readFileSync(file, "utf8"));
|
|
395
|
+
return isSessionFile(parsed) ? parsed : null;
|
|
384
396
|
} catch {
|
|
385
397
|
return null;
|
|
386
398
|
}
|
|
@@ -526,17 +538,51 @@ async function resolveServerInteractive(presetNamespace) {
|
|
|
526
538
|
probeSpinner.stop(`Reachable at https://${probed.address}:${probed.port}`);
|
|
527
539
|
return `https://${probed.address}:${probed.port}`;
|
|
528
540
|
}
|
|
541
|
+
var BACK_SENTINEL = "__back__";
|
|
542
|
+
async function askWithBack(label, defaultValue) {
|
|
543
|
+
const v = await askText(`${label} (or type :back)`, defaultValue);
|
|
544
|
+
return v.trim() === ":back" || v.trim() === ":b" ? BACK_SENTINEL : v;
|
|
545
|
+
}
|
|
546
|
+
async function askPasswordWithBack() {
|
|
547
|
+
const v = await askPassword("Password (or type :back to change username)");
|
|
548
|
+
return v.trim() === ":back" || v.trim() === ":b" ? BACK_SENTINEL : v;
|
|
549
|
+
}
|
|
529
550
|
async function loginCommand(opts) {
|
|
530
551
|
clack.intro("camstack login");
|
|
531
|
-
let
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
552
|
+
let step = opts.server ? "username" : "server";
|
|
553
|
+
let server = opts.server ?? "";
|
|
554
|
+
let username = opts.username ?? "";
|
|
555
|
+
let password2 = opts.password ?? "";
|
|
556
|
+
if (opts.server) clack.log.info(`Server: ${opts.server}`);
|
|
557
|
+
while (step !== "password" || !password2) {
|
|
558
|
+
if (step === "server") {
|
|
559
|
+
server = await resolveServerInteractive(opts.namespace);
|
|
560
|
+
step = "username";
|
|
561
|
+
continue;
|
|
562
|
+
}
|
|
563
|
+
if (step === "username") {
|
|
564
|
+
const result2 = opts.username && !username ? opts.username : await askWithBack("Username", username || "admin");
|
|
565
|
+
if (result2 === BACK_SENTINEL) {
|
|
566
|
+
if (opts.server) {
|
|
567
|
+
clack.log.warn("Server was passed via --server; nothing to go back to. Re-enter username.");
|
|
568
|
+
continue;
|
|
569
|
+
}
|
|
570
|
+
step = "server";
|
|
571
|
+
continue;
|
|
572
|
+
}
|
|
573
|
+
username = result2;
|
|
574
|
+
step = "password";
|
|
575
|
+
continue;
|
|
576
|
+
}
|
|
577
|
+
const result = opts.password && !password2 ? opts.password : await askPasswordWithBack();
|
|
578
|
+
if (result === BACK_SENTINEL) {
|
|
579
|
+
step = "username";
|
|
580
|
+
continue;
|
|
581
|
+
}
|
|
582
|
+
password2 = result;
|
|
583
|
+
break;
|
|
536
584
|
}
|
|
537
|
-
const username = opts.username ?? await askText("Username", "admin");
|
|
538
585
|
const MAX_ATTEMPTS = 3;
|
|
539
|
-
let password2 = opts.password ?? await askPassword("Password");
|
|
540
586
|
let attempt = 1;
|
|
541
587
|
let jwt;
|
|
542
588
|
let displayName;
|
|
@@ -633,18 +679,52 @@ async function logoutCommand(opts) {
|
|
|
633
679
|
clearSession(session.server);
|
|
634
680
|
clack.outro(`\u2713 Logged out of ${session.server}`);
|
|
635
681
|
}
|
|
636
|
-
function whoamiCommand(opts) {
|
|
682
|
+
async function whoamiCommand(opts) {
|
|
637
683
|
const session = loadSession(opts.server);
|
|
638
684
|
if (!session) {
|
|
639
685
|
console.log(`[camstack] Not logged in${opts.server ? ` to ${opts.server}` : ""}.`);
|
|
640
686
|
process.exit(1);
|
|
641
687
|
}
|
|
642
|
-
console.log(`[camstack] Active session:`);
|
|
688
|
+
console.log(`[camstack] Active session (local cache):`);
|
|
643
689
|
console.log(` server: ${session.server}`);
|
|
644
690
|
console.log(` username: ${session.username}`);
|
|
645
691
|
console.log(` token: ${session.token.slice(0, 12)}\u2026 (id: ${session.tokenId})`);
|
|
646
692
|
console.log(` scopes: ${session.scopes.map((s) => `${s.type}:${s.target}`).join(", ")}`);
|
|
647
693
|
console.log(` createdAt: ${new Date(session.createdAt).toISOString()}`);
|
|
694
|
+
console.log("");
|
|
695
|
+
console.log(`[camstack] Pinging ${session.server}/api/auth/whoami\u2026`);
|
|
696
|
+
try {
|
|
697
|
+
const controller = new AbortController();
|
|
698
|
+
const timer = setTimeout(() => controller.abort(), 3e3);
|
|
699
|
+
const res = await fetch(`${session.server}/api/auth/whoami`, {
|
|
700
|
+
headers: { Authorization: `Bearer ${session.token}` },
|
|
701
|
+
signal: controller.signal
|
|
702
|
+
});
|
|
703
|
+
clearTimeout(timer);
|
|
704
|
+
if (res.status === 401) {
|
|
705
|
+
console.error(`[camstack] \u2717 Token rejected by server (revoked / expired). Run \`camstack login\` to refresh.`);
|
|
706
|
+
process.exit(2);
|
|
707
|
+
}
|
|
708
|
+
if (!res.ok) {
|
|
709
|
+
const body = await res.text().catch(() => "");
|
|
710
|
+
console.error(`[camstack] \u2717 Server returned ${res.status}: ${body.slice(0, 160)}`);
|
|
711
|
+
process.exit(2);
|
|
712
|
+
}
|
|
713
|
+
const json = await res.json();
|
|
714
|
+
console.log(`[camstack] \u2713 Token valid on server.`);
|
|
715
|
+
console.log(` kind: ${json.kind ?? "unknown"}`);
|
|
716
|
+
if (json.userId) console.log(` userId: ${json.userId}`);
|
|
717
|
+
if (json.username) console.log(` username: ${json.username}`);
|
|
718
|
+
if (Array.isArray(json.scopes)) {
|
|
719
|
+
const scopeList = json.scopes.map((s) => `${s.type}:${s.target}`).join(", ");
|
|
720
|
+
console.log(` scopes: ${scopeList}`);
|
|
721
|
+
}
|
|
722
|
+
} catch (err) {
|
|
723
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
724
|
+
console.error(`[camstack] \u26A0 Could not reach server: ${msg}`);
|
|
725
|
+
console.error(`[camstack] The local cache may be stale. Try \`camstack login\` once the server is reachable.`);
|
|
726
|
+
process.exit(2);
|
|
727
|
+
}
|
|
648
728
|
}
|
|
649
729
|
|
|
650
730
|
// src/commands/update.ts
|
|
@@ -1241,7 +1321,7 @@ async function runLogout(args) {
|
|
|
1241
1321
|
const server = stringOpt(parsed.values, "server") ?? process.env.CAMSTACK_SERVER ?? "https://localhost:4443";
|
|
1242
1322
|
await logoutCommand({ server });
|
|
1243
1323
|
}
|
|
1244
|
-
function runWhoami(args) {
|
|
1324
|
+
async function runWhoami(args) {
|
|
1245
1325
|
const parsed = parseSubcommandArgs(args, {
|
|
1246
1326
|
server: { type: "string", short: "s" }
|
|
1247
1327
|
}, false);
|
|
@@ -1250,7 +1330,7 @@ function runWhoami(args) {
|
|
|
1250
1330
|
return;
|
|
1251
1331
|
}
|
|
1252
1332
|
const server = stringOpt(parsed.values, "server");
|
|
1253
|
-
whoamiCommand(server ? { server } : {});
|
|
1333
|
+
await whoamiCommand(server ? { server } : {});
|
|
1254
1334
|
}
|
|
1255
1335
|
async function runDeploy(args) {
|
|
1256
1336
|
const parsed = parseSubcommandArgs(args, {
|