camstack 0.7.1 → 0.7.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.
- package/dist/cli.js +94 -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,54 @@ 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
|
+
clearSession(session.server);
|
|
706
|
+
console.error(`[camstack] \u2717 Token rejected by server (revoked / expired).`);
|
|
707
|
+
console.error(`[camstack] Local cache cleared. Run \`camstack login\` to issue a new one.`);
|
|
708
|
+
process.exit(2);
|
|
709
|
+
}
|
|
710
|
+
if (!res.ok) {
|
|
711
|
+
const body = await res.text().catch(() => "");
|
|
712
|
+
console.error(`[camstack] \u2717 Server returned ${res.status}: ${body.slice(0, 160)}`);
|
|
713
|
+
process.exit(2);
|
|
714
|
+
}
|
|
715
|
+
const json = await res.json();
|
|
716
|
+
console.log(`[camstack] \u2713 Token valid on server.`);
|
|
717
|
+
console.log(` kind: ${json.kind ?? "unknown"}`);
|
|
718
|
+
if (json.userId) console.log(` userId: ${json.userId}`);
|
|
719
|
+
if (json.username) console.log(` username: ${json.username}`);
|
|
720
|
+
if (Array.isArray(json.scopes)) {
|
|
721
|
+
const scopeList = json.scopes.map((s) => `${s.type}:${s.target}`).join(", ");
|
|
722
|
+
console.log(` scopes: ${scopeList}`);
|
|
723
|
+
}
|
|
724
|
+
} catch (err) {
|
|
725
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
726
|
+
console.error(`[camstack] \u26A0 Could not reach server: ${msg}`);
|
|
727
|
+
console.error(`[camstack] The local cache may be stale. Try \`camstack login\` once the server is reachable.`);
|
|
728
|
+
process.exit(2);
|
|
729
|
+
}
|
|
648
730
|
}
|
|
649
731
|
|
|
650
732
|
// src/commands/update.ts
|
|
@@ -1241,7 +1323,7 @@ async function runLogout(args) {
|
|
|
1241
1323
|
const server = stringOpt(parsed.values, "server") ?? process.env.CAMSTACK_SERVER ?? "https://localhost:4443";
|
|
1242
1324
|
await logoutCommand({ server });
|
|
1243
1325
|
}
|
|
1244
|
-
function runWhoami(args) {
|
|
1326
|
+
async function runWhoami(args) {
|
|
1245
1327
|
const parsed = parseSubcommandArgs(args, {
|
|
1246
1328
|
server: { type: "string", short: "s" }
|
|
1247
1329
|
}, false);
|
|
@@ -1250,7 +1332,7 @@ function runWhoami(args) {
|
|
|
1250
1332
|
return;
|
|
1251
1333
|
}
|
|
1252
1334
|
const server = stringOpt(parsed.values, "server");
|
|
1253
|
-
whoamiCommand(server ? { server } : {});
|
|
1335
|
+
await whoamiCommand(server ? { server } : {});
|
|
1254
1336
|
}
|
|
1255
1337
|
async function runDeploy(args) {
|
|
1256
1338
|
const parsed = parseSubcommandArgs(args, {
|