rekor-cli 0.1.26 → 0.1.28
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/index.js +419 -43
- package/dist/index.js.map +1 -1
- package/package.json +4 -1
package/dist/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
// src/program.ts
|
|
4
|
-
import { Command as
|
|
4
|
+
import { Command as Command21 } from "commander";
|
|
5
5
|
|
|
6
6
|
// src/commands/login.ts
|
|
7
7
|
import { Command } from "commander";
|
|
@@ -326,7 +326,7 @@ async function clearAllTokens() {
|
|
|
326
326
|
import * as http from "http";
|
|
327
327
|
var OAUTH_CALLBACK_PORT = 3927;
|
|
328
328
|
var DEFAULT_AUTHKIT_DOMAIN = "https://engaging-tip-62.authkit.app";
|
|
329
|
-
var DEFAULT_AUTHKIT_CLIENT_ID = "
|
|
329
|
+
var DEFAULT_AUTHKIT_CLIENT_ID = "client_01KSMZJ6YN7VAKTP2BW41V56SZ";
|
|
330
330
|
function getAuthKitDomain() {
|
|
331
331
|
return process.env["REKOR_AUTHKIT_DOMAIN"] || DEFAULT_AUTHKIT_DOMAIN;
|
|
332
332
|
}
|
|
@@ -527,6 +527,9 @@ async function currentAuthKind() {
|
|
|
527
527
|
const resolved = await getResolvedToken();
|
|
528
528
|
return resolved?.kind ?? null;
|
|
529
529
|
}
|
|
530
|
+
async function isAuthenticated() {
|
|
531
|
+
return await getResolvedToken() !== null;
|
|
532
|
+
}
|
|
530
533
|
|
|
531
534
|
// src/pkce.ts
|
|
532
535
|
import * as crypto from "crypto";
|
|
@@ -540,6 +543,17 @@ function generateState() {
|
|
|
540
543
|
return crypto.randomBytes(16).toString("base64url");
|
|
541
544
|
}
|
|
542
545
|
|
|
546
|
+
// src/env.ts
|
|
547
|
+
function isCI() {
|
|
548
|
+
return Boolean(process.env["CI"]);
|
|
549
|
+
}
|
|
550
|
+
function isInteractive() {
|
|
551
|
+
return Boolean(process.stdin.isTTY && process.stdout.isTTY);
|
|
552
|
+
}
|
|
553
|
+
function isSSH() {
|
|
554
|
+
return Boolean(process.env["SSH_CONNECTION"] || process.env["SSH_TTY"]);
|
|
555
|
+
}
|
|
556
|
+
|
|
543
557
|
// src/commands/login.ts
|
|
544
558
|
var FALLBACK_URL_DELAY_MS = 5e3;
|
|
545
559
|
var loginCommand = new Command("login").description("Authenticate with Rekor").option("--token <token>", "API key for headless/CI authentication (rec_...)").option("--api-url <url>", "API base URL").action(async (opts) => {
|
|
@@ -548,18 +562,10 @@ var loginCommand = new Command("login").description("Authenticate with Rekor").o
|
|
|
548
562
|
console.log("Authenticated successfully");
|
|
549
563
|
return;
|
|
550
564
|
}
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
console.log("Authenticated successfully");
|
|
554
|
-
} catch (err) {
|
|
555
|
-
console.error(
|
|
556
|
-
`Login failed: ${err instanceof Error ? err.message : "Unknown error"}`
|
|
557
|
-
);
|
|
558
|
-
process.exit(1);
|
|
559
|
-
}
|
|
565
|
+
await browserLoginPkce(opts.apiUrl);
|
|
566
|
+
console.log("Authenticated successfully");
|
|
560
567
|
});
|
|
561
568
|
async function browserLoginPkce(apiUrl) {
|
|
562
|
-
const open = await import("open").then((m) => m.default);
|
|
563
569
|
const codeVerifier = generateCodeVerifier();
|
|
564
570
|
const codeChallenge = generateCodeChallenge(codeVerifier);
|
|
565
571
|
const state = generateState();
|
|
@@ -575,24 +581,31 @@ async function browserLoginPkce(apiUrl) {
|
|
|
575
581
|
authorizeUrl.searchParams.set("code_challenge_method", "S256");
|
|
576
582
|
authorizeUrl.searchParams.set("state", state);
|
|
577
583
|
authorizeUrl.searchParams.set("scope", "openid profile email offline_access");
|
|
578
|
-
console.log("Opening browser for authentication...");
|
|
579
584
|
const callbackPromise = startCallbackServer(port, state);
|
|
580
|
-
let
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
console.log(
|
|
584
|
-
}
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
585
|
+
let fallbackTimer;
|
|
586
|
+
if (isSSH()) {
|
|
587
|
+
console.log("SSH session detected. Open this URL in a browser to authenticate:");
|
|
588
|
+
console.log(authorizeUrl.toString());
|
|
589
|
+
} else {
|
|
590
|
+
console.log("Opening browser for authentication...");
|
|
591
|
+
const open = await import("open").then((m) => m.default);
|
|
592
|
+
let fallbackPrinted = false;
|
|
593
|
+
fallbackTimer = setTimeout(() => {
|
|
594
|
+
fallbackPrinted = true;
|
|
595
|
+
console.log(`If the browser didn't open, visit: ${authorizeUrl.toString()}`);
|
|
596
|
+
}, FALLBACK_URL_DELAY_MS);
|
|
597
|
+
open(authorizeUrl.toString()).catch(() => {
|
|
598
|
+
if (fallbackPrinted) return;
|
|
599
|
+
clearTimeout(fallbackTimer);
|
|
600
|
+
console.log("Could not open browser automatically.");
|
|
601
|
+
console.log(`Visit: ${authorizeUrl.toString()}`);
|
|
602
|
+
});
|
|
603
|
+
}
|
|
591
604
|
let code;
|
|
592
605
|
try {
|
|
593
606
|
({ code } = await callbackPromise);
|
|
594
607
|
} finally {
|
|
595
|
-
clearTimeout(fallbackTimer);
|
|
608
|
+
if (fallbackTimer) clearTimeout(fallbackTimer);
|
|
596
609
|
}
|
|
597
610
|
const tokens = await exchangeCodeForTokens(authkitDomain, clientId, code, codeVerifier, redirectUri);
|
|
598
611
|
const expiresAt = expiresInToIso(tokens.expires_in);
|
|
@@ -607,6 +620,43 @@ async function browserLoginPkce(apiUrl) {
|
|
|
607
620
|
// src/commands/logout.ts
|
|
608
621
|
import { Command as Command2 } from "commander";
|
|
609
622
|
|
|
623
|
+
// src/errors.ts
|
|
624
|
+
var ApiError = class extends Error {
|
|
625
|
+
constructor(status, message, code, body) {
|
|
626
|
+
super(message);
|
|
627
|
+
this.status = status;
|
|
628
|
+
this.code = code;
|
|
629
|
+
this.body = body;
|
|
630
|
+
this.name = "ApiError";
|
|
631
|
+
}
|
|
632
|
+
/** 4xx are user-facing (bad input, auth, quota) — not bugs worth reporting. */
|
|
633
|
+
get isExpected() {
|
|
634
|
+
return this.status >= 400 && this.status < 500;
|
|
635
|
+
}
|
|
636
|
+
};
|
|
637
|
+
function friendlyHint(err) {
|
|
638
|
+
if (!(err instanceof ApiError)) return null;
|
|
639
|
+
switch (err.status) {
|
|
640
|
+
case 401:
|
|
641
|
+
return "Authentication failed or session expired. Run `rekor login`.";
|
|
642
|
+
case 402:
|
|
643
|
+
return "Quota exceeded for your plan. Review usage or upgrade your plan.";
|
|
644
|
+
case 403:
|
|
645
|
+
return "Access denied. Your token may lack permission for this resource.";
|
|
646
|
+
case 429:
|
|
647
|
+
return "Rate limited. Wait a moment and try again.";
|
|
648
|
+
default:
|
|
649
|
+
if (err.status >= 500) return "Rekor server error. Try again shortly.";
|
|
650
|
+
return null;
|
|
651
|
+
}
|
|
652
|
+
}
|
|
653
|
+
function handleCliError(err) {
|
|
654
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
655
|
+
console.error(`Error: ${message}`);
|
|
656
|
+
const hint = friendlyHint(err);
|
|
657
|
+
if (hint) console.error(hint);
|
|
658
|
+
}
|
|
659
|
+
|
|
610
660
|
// src/client.ts
|
|
611
661
|
var ApiClient = class {
|
|
612
662
|
baseUrl;
|
|
@@ -632,11 +682,24 @@ var ApiClient = class {
|
|
|
632
682
|
},
|
|
633
683
|
body: body ? JSON.stringify(body) : void 0
|
|
634
684
|
});
|
|
635
|
-
const
|
|
685
|
+
const text = await res.text();
|
|
686
|
+
let parsed;
|
|
687
|
+
if (text) {
|
|
688
|
+
try {
|
|
689
|
+
parsed = JSON.parse(text);
|
|
690
|
+
} catch {
|
|
691
|
+
parsed = void 0;
|
|
692
|
+
}
|
|
693
|
+
}
|
|
636
694
|
if (!res.ok) {
|
|
637
|
-
throw new
|
|
695
|
+
throw new ApiError(
|
|
696
|
+
res.status,
|
|
697
|
+
parsed?.error?.message ?? `HTTP ${res.status}`,
|
|
698
|
+
parsed?.error?.code,
|
|
699
|
+
parsed
|
|
700
|
+
);
|
|
638
701
|
}
|
|
639
|
-
return
|
|
702
|
+
return parsed?.data;
|
|
640
703
|
}
|
|
641
704
|
async uploadFile(url, body, contentType) {
|
|
642
705
|
const res = await fetch(url, {
|
|
@@ -648,7 +711,7 @@ var ApiClient = class {
|
|
|
648
711
|
body
|
|
649
712
|
});
|
|
650
713
|
if (!res.ok) {
|
|
651
|
-
throw new
|
|
714
|
+
throw new ApiError(res.status, `Upload failed: HTTP ${res.status}`);
|
|
652
715
|
}
|
|
653
716
|
}
|
|
654
717
|
};
|
|
@@ -732,6 +795,22 @@ function getFormat(cmd) {
|
|
|
732
795
|
return cmd.parent?.parent?.opts().output ?? cmd.parent?.opts().output ?? "table";
|
|
733
796
|
}
|
|
734
797
|
|
|
798
|
+
// src/prompt.ts
|
|
799
|
+
import * as readline from "readline";
|
|
800
|
+
function confirm(question, opts = {}) {
|
|
801
|
+
if (!isInteractive()) return Promise.resolve(true);
|
|
802
|
+
const suffix = opts.defaultYes ? "[Y/n]" : "[y/N]";
|
|
803
|
+
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
804
|
+
return new Promise((resolve) => {
|
|
805
|
+
rl.question(`${question} ${suffix}: `, (answer) => {
|
|
806
|
+
rl.close();
|
|
807
|
+
const a = answer.trim().toLowerCase();
|
|
808
|
+
if (a === "") return resolve(Boolean(opts.defaultYes));
|
|
809
|
+
resolve(a === "y" || a === "yes");
|
|
810
|
+
});
|
|
811
|
+
});
|
|
812
|
+
}
|
|
813
|
+
|
|
735
814
|
// src/commands/databases.ts
|
|
736
815
|
var databasesCommand = new Command3("databases").description("Manage databases");
|
|
737
816
|
databasesCommand.command("list").description("List all databases").option("--tag <tag>", "Filter by tag").action(async function(opts) {
|
|
@@ -764,9 +843,13 @@ databasesCommand.command("tag <id>").description("Set tags on a database").requi
|
|
|
764
843
|
});
|
|
765
844
|
console.log(formatOutput(data, getFormat(this)));
|
|
766
845
|
});
|
|
767
|
-
databasesCommand.command("delete <id>").description("Delete a database").action(async (
|
|
846
|
+
databasesCommand.command("delete <id>").description("Delete a database").option("-y, --yes", "Skip confirmation prompt").action(async (id, opts) => {
|
|
847
|
+
if (!opts.yes && !await confirm(`Delete database "${id}"? This cannot be undone.`)) {
|
|
848
|
+
console.log("Aborted");
|
|
849
|
+
return;
|
|
850
|
+
}
|
|
768
851
|
const client = new ApiClient();
|
|
769
|
-
await client.request("DELETE", `/v1/databases/${
|
|
852
|
+
await client.request("DELETE", `/v1/databases/${id}`);
|
|
770
853
|
console.log("Deleted");
|
|
771
854
|
});
|
|
772
855
|
databasesCommand.command("create-preview <production-id>").description("Create a preview database linked to a production database").requiredOption("--name <name>", "Preview database name").option("--description <desc>", "Description").action(async function(productionId, opts) {
|
|
@@ -833,7 +916,11 @@ collectionsCommand.command("upsert <id>").description("Create or update a collec
|
|
|
833
916
|
const data = await client.request("PUT", `/v1/${ws}/collections/${id}`, body);
|
|
834
917
|
console.log(formatOutput(data, getFormat(this)));
|
|
835
918
|
});
|
|
836
|
-
collectionsCommand.command("delete <id>").description("Delete a collection").action(async function(id) {
|
|
919
|
+
collectionsCommand.command("delete <id>").description("Delete a collection").option("-y, --yes", "Skip confirmation prompt").action(async function(id, opts) {
|
|
920
|
+
if (!opts.yes && !await confirm(`Delete collection "${id}"? This cannot be undone.`)) {
|
|
921
|
+
console.log("Aborted");
|
|
922
|
+
return;
|
|
923
|
+
}
|
|
837
924
|
const ws = getDatabase(this);
|
|
838
925
|
const client = new ApiClient();
|
|
839
926
|
await client.request("DELETE", `/v1/${ws}/collections/${id}`);
|
|
@@ -859,7 +946,11 @@ documentsCommand.command("get <collection> <id>").description("Get a document by
|
|
|
859
946
|
const data = await client.request("GET", `/v1/${ws}/documents/${collection}/${id}`);
|
|
860
947
|
console.log(formatOutput(data, getFormat(this)));
|
|
861
948
|
});
|
|
862
|
-
documentsCommand.command("delete <collection> <id>").description("Delete a document").action(async function(collection, id) {
|
|
949
|
+
documentsCommand.command("delete <collection> <id>").description("Delete a document").option("-y, --yes", "Skip confirmation prompt").action(async function(collection, id, opts) {
|
|
950
|
+
if (!opts.yes && !await confirm(`Delete document ${collection}/${id}? This cannot be undone.`)) {
|
|
951
|
+
console.log("Aborted");
|
|
952
|
+
return;
|
|
953
|
+
}
|
|
863
954
|
const ws = getDatabase(this);
|
|
864
955
|
const client = new ApiClient();
|
|
865
956
|
await client.request("DELETE", `/v1/${ws}/documents/${collection}/${id}`);
|
|
@@ -925,7 +1016,11 @@ relationshipsCommand.command("get <id>").description("Get a relationship by ID")
|
|
|
925
1016
|
const data = await client.request("GET", `/v1/${ws}/relationships/${id}`);
|
|
926
1017
|
console.log(formatOutput(data, getFormat(this)));
|
|
927
1018
|
});
|
|
928
|
-
relationshipsCommand.command("delete <id>").description("Delete a relationship").action(async function(id) {
|
|
1019
|
+
relationshipsCommand.command("delete <id>").description("Delete a relationship").option("-y, --yes", "Skip confirmation prompt").action(async function(id, opts) {
|
|
1020
|
+
if (!opts.yes && !await confirm(`Delete relationship ${id}? This cannot be undone.`)) {
|
|
1021
|
+
console.log("Aborted");
|
|
1022
|
+
return;
|
|
1023
|
+
}
|
|
929
1024
|
const ws = getDatabase(this);
|
|
930
1025
|
const client = new ApiClient();
|
|
931
1026
|
await client.request("DELETE", `/v1/${ws}/relationships/${id}`);
|
|
@@ -985,7 +1080,11 @@ attachmentsCommand.command("list <collection> <id>").description("List attachmen
|
|
|
985
1080
|
const data = await client.request("GET", `/v1/${ws}/documents/${collection}/${id}/attachments${query}`);
|
|
986
1081
|
console.log(formatOutput(data, getFormat(this)));
|
|
987
1082
|
});
|
|
988
|
-
attachmentsCommand.command("delete <collection> <id> <attachment-id>").description("Delete an attachment").action(async function(collection, id, attachmentId) {
|
|
1083
|
+
attachmentsCommand.command("delete <collection> <id> <attachment-id>").description("Delete an attachment").option("-y, --yes", "Skip confirmation prompt").action(async function(collection, id, attachmentId, opts) {
|
|
1084
|
+
if (!opts.yes && !await confirm(`Delete attachment ${attachmentId} on ${collection}/${id}? This cannot be undone.`)) {
|
|
1085
|
+
console.log("Aborted");
|
|
1086
|
+
return;
|
|
1087
|
+
}
|
|
989
1088
|
const ws = getDatabase(this);
|
|
990
1089
|
const client = new ApiClient();
|
|
991
1090
|
await client.request("DELETE", `/v1/${ws}/documents/${collection}/${id}/attachments/${attachmentId}`);
|
|
@@ -1023,7 +1122,11 @@ hooksCommand.command("list").description("List all hooks").action(async function
|
|
|
1023
1122
|
const data = await client.request("GET", `/v1/${ws}/hooks`);
|
|
1024
1123
|
console.log(formatOutput(data, getFormat(this)));
|
|
1025
1124
|
});
|
|
1026
|
-
hooksCommand.command("delete <id>").description("Delete a hook").action(async function(id) {
|
|
1125
|
+
hooksCommand.command("delete <id>").description("Delete a hook").option("-y, --yes", "Skip confirmation prompt").action(async function(id, opts) {
|
|
1126
|
+
if (!opts.yes && !await confirm(`Delete hook ${id}? This cannot be undone.`)) {
|
|
1127
|
+
console.log("Aborted");
|
|
1128
|
+
return;
|
|
1129
|
+
}
|
|
1027
1130
|
const ws = getDatabase(this);
|
|
1028
1131
|
const client = new ApiClient();
|
|
1029
1132
|
await client.request("DELETE", `/v1/${ws}/hooks/${id}`);
|
|
@@ -1066,7 +1169,11 @@ triggersCommand.command("list").description("List all triggers").action(async fu
|
|
|
1066
1169
|
const data = await client.request("GET", `/v1/${ws}/triggers`);
|
|
1067
1170
|
console.log(formatOutput(data, getFormat(this)));
|
|
1068
1171
|
});
|
|
1069
|
-
triggersCommand.command("delete <id>").description("Delete a trigger").action(async function(id) {
|
|
1172
|
+
triggersCommand.command("delete <id>").description("Delete a trigger").option("-y, --yes", "Skip confirmation prompt").action(async function(id, opts) {
|
|
1173
|
+
if (!opts.yes && !await confirm(`Delete trigger ${id}? This cannot be undone.`)) {
|
|
1174
|
+
console.log("Aborted");
|
|
1175
|
+
return;
|
|
1176
|
+
}
|
|
1070
1177
|
const ws = getDatabase(this);
|
|
1071
1178
|
const client = new ApiClient();
|
|
1072
1179
|
await client.request("DELETE", `/v1/${ws}/triggers/${id}`);
|
|
@@ -1120,7 +1227,11 @@ endpointsCommand.command("list").description("List all endpoints").action(async
|
|
|
1120
1227
|
const data = await client.request("GET", `/v1/${ws}/endpoints`);
|
|
1121
1228
|
console.log(formatOutput(data, getFormat(this)));
|
|
1122
1229
|
});
|
|
1123
|
-
endpointsCommand.command("delete <slug>").description("Delete an endpoint").action(async function(slug) {
|
|
1230
|
+
endpointsCommand.command("delete <slug>").description("Delete an endpoint").option("-y, --yes", "Skip confirmation prompt").action(async function(slug, opts) {
|
|
1231
|
+
if (!opts.yes && !await confirm(`Delete endpoint "${slug}"? This cannot be undone.`)) {
|
|
1232
|
+
console.log("Aborted");
|
|
1233
|
+
return;
|
|
1234
|
+
}
|
|
1124
1235
|
const ws = getDatabase(this);
|
|
1125
1236
|
const client = new ApiClient();
|
|
1126
1237
|
await client.request("DELETE", `/v1/${ws}/endpoints/${slug}`);
|
|
@@ -1180,8 +1291,8 @@ providersCommand.command("export <provider>").description(`Export collections as
|
|
|
1180
1291
|
const query = opts.collections ? `?collections=${opts.collections}` : "";
|
|
1181
1292
|
const data = await client.request("GET", `/v1/${ws}/providers/${provider}/export${query}`);
|
|
1182
1293
|
if (opts.output) {
|
|
1183
|
-
const { writeFileSync:
|
|
1184
|
-
|
|
1294
|
+
const { writeFileSync: writeFileSync4 } = await import("fs");
|
|
1295
|
+
writeFileSync4(opts.output, JSON.stringify(data, null, 2));
|
|
1185
1296
|
console.log(`Written to ${opts.output}`);
|
|
1186
1297
|
} else {
|
|
1187
1298
|
console.log(formatOutput(data, getFormat(this)));
|
|
@@ -1350,10 +1461,95 @@ var reportBugCommand = new Command17("report-bug").description("Submit a bug rep
|
|
|
1350
1461
|
}
|
|
1351
1462
|
});
|
|
1352
1463
|
|
|
1464
|
+
// src/commands/update.ts
|
|
1465
|
+
import { Command as Command18 } from "commander";
|
|
1466
|
+
import { execFileSync } from "child_process";
|
|
1467
|
+
|
|
1468
|
+
// src/version.ts
|
|
1469
|
+
function isNewerVersion(latest, current) {
|
|
1470
|
+
const strip = (v) => v.replace(/[-+].*$/, "");
|
|
1471
|
+
const l = strip(latest).split(".").map(Number);
|
|
1472
|
+
const c = strip(current).split(".").map(Number);
|
|
1473
|
+
for (let i = 0; i < Math.max(l.length, c.length); i++) {
|
|
1474
|
+
const lv = l[i] ?? 0;
|
|
1475
|
+
const cv = c[i] ?? 0;
|
|
1476
|
+
if (Number.isNaN(lv) || Number.isNaN(cv)) return false;
|
|
1477
|
+
if (lv > cv) return true;
|
|
1478
|
+
if (lv < cv) return false;
|
|
1479
|
+
}
|
|
1480
|
+
return false;
|
|
1481
|
+
}
|
|
1482
|
+
|
|
1483
|
+
// src/version-check.ts
|
|
1484
|
+
import { readFileSync as readFileSync6, writeFileSync as writeFileSync3, mkdirSync as mkdirSync3 } from "fs";
|
|
1485
|
+
import { join as join2 } from "path";
|
|
1486
|
+
import { execFile } from "child_process";
|
|
1487
|
+
var NPM_PACKAGE = "rekor-cli";
|
|
1488
|
+
var CACHE_FILE = join2(CONFIG_DIR, "update-check.json");
|
|
1489
|
+
var MAX_AGE_MS = 24 * 60 * 60 * 1e3;
|
|
1490
|
+
function isDisabled() {
|
|
1491
|
+
return Boolean(
|
|
1492
|
+
process.env["NO_UPDATE_NOTIFIER"] || process.env["REKOR_NO_UPDATE_CHECK"] || isCI()
|
|
1493
|
+
);
|
|
1494
|
+
}
|
|
1495
|
+
function readCache() {
|
|
1496
|
+
try {
|
|
1497
|
+
const parsed = JSON.parse(readFileSync6(CACHE_FILE, "utf-8"));
|
|
1498
|
+
if (typeof parsed.lastCheck !== "number") return null;
|
|
1499
|
+
if (parsed.latest !== null && typeof parsed.latest !== "string") return null;
|
|
1500
|
+
return parsed;
|
|
1501
|
+
} catch {
|
|
1502
|
+
return null;
|
|
1503
|
+
}
|
|
1504
|
+
}
|
|
1505
|
+
function writeCache(cache) {
|
|
1506
|
+
try {
|
|
1507
|
+
mkdirSync3(CONFIG_DIR, { recursive: true, mode: 448 });
|
|
1508
|
+
writeFileSync3(CACHE_FILE, JSON.stringify(cache), { mode: 384 });
|
|
1509
|
+
} catch {
|
|
1510
|
+
}
|
|
1511
|
+
}
|
|
1512
|
+
function readCachedLatest(current) {
|
|
1513
|
+
const cache = readCache();
|
|
1514
|
+
if (!cache?.latest) return null;
|
|
1515
|
+
if (Date.now() - cache.lastCheck > MAX_AGE_MS) return null;
|
|
1516
|
+
return isNewerVersion(cache.latest, current) ? cache.latest : null;
|
|
1517
|
+
}
|
|
1518
|
+
function notify(latest, current) {
|
|
1519
|
+
console.error(`
|
|
1520
|
+
Update available: ${current} \u2192 ${latest}
|
|
1521
|
+
Run \`rekor update\` to update.`);
|
|
1522
|
+
}
|
|
1523
|
+
function checkForUpdates(current) {
|
|
1524
|
+
if (isDisabled()) return;
|
|
1525
|
+
try {
|
|
1526
|
+
const cache = readCache();
|
|
1527
|
+
if (cache && Date.now() - cache.lastCheck < MAX_AGE_MS) {
|
|
1528
|
+
if (cache.latest && isNewerVersion(cache.latest, current)) notify(cache.latest, current);
|
|
1529
|
+
return;
|
|
1530
|
+
}
|
|
1531
|
+
writeCache({ lastCheck: Date.now(), latest: cache?.latest ?? null });
|
|
1532
|
+
const child = execFile(
|
|
1533
|
+
"npm",
|
|
1534
|
+
["view", NPM_PACKAGE, "version"],
|
|
1535
|
+
{ timeout: 5e3, shell: true },
|
|
1536
|
+
(err, stdout) => {
|
|
1537
|
+
if (err) return;
|
|
1538
|
+
const latest = stdout.trim();
|
|
1539
|
+
if (!latest) return;
|
|
1540
|
+
writeCache({ lastCheck: Date.now(), latest });
|
|
1541
|
+
if (isNewerVersion(latest, current)) notify(latest, current);
|
|
1542
|
+
}
|
|
1543
|
+
);
|
|
1544
|
+
child.unref();
|
|
1545
|
+
} catch {
|
|
1546
|
+
}
|
|
1547
|
+
}
|
|
1548
|
+
|
|
1353
1549
|
// package.json
|
|
1354
1550
|
var package_default = {
|
|
1355
1551
|
name: "rekor-cli",
|
|
1356
|
-
version: "0.1.
|
|
1552
|
+
version: "0.1.28",
|
|
1357
1553
|
type: "module",
|
|
1358
1554
|
engines: {
|
|
1359
1555
|
node: ">=20.0.0"
|
|
@@ -1377,6 +1573,9 @@ var package_default = {
|
|
|
1377
1573
|
commander: "^13.1.0",
|
|
1378
1574
|
open: "^11.0.0"
|
|
1379
1575
|
},
|
|
1576
|
+
optionalDependencies: {
|
|
1577
|
+
"@sentry/node": "^10.0.0"
|
|
1578
|
+
},
|
|
1380
1579
|
devDependencies: {
|
|
1381
1580
|
"@types/node": "^25.5.0",
|
|
1382
1581
|
tsup: "^8.4.0",
|
|
@@ -1385,10 +1584,110 @@ var package_default = {
|
|
|
1385
1584
|
}
|
|
1386
1585
|
};
|
|
1387
1586
|
|
|
1587
|
+
// src/commands/update.ts
|
|
1588
|
+
var updateCommand = new Command18("update").description("Update the Rekor CLI to the latest published version").action(() => {
|
|
1589
|
+
console.log(`Current version: ${package_default.version}`);
|
|
1590
|
+
console.log("Checking for updates...");
|
|
1591
|
+
let latest;
|
|
1592
|
+
try {
|
|
1593
|
+
latest = execFileSync("npm", ["view", NPM_PACKAGE, "version"], { timeout: 1e4, shell: true }).toString().trim();
|
|
1594
|
+
} catch {
|
|
1595
|
+
console.error(`Could not reach npm to check for updates.`);
|
|
1596
|
+
console.error(`Try manually: npm install -g ${NPM_PACKAGE}`);
|
|
1597
|
+
process.exit(1);
|
|
1598
|
+
}
|
|
1599
|
+
if (!isNewerVersion(latest, package_default.version)) {
|
|
1600
|
+
console.log(`Already on the latest version (${package_default.version}).`);
|
|
1601
|
+
return;
|
|
1602
|
+
}
|
|
1603
|
+
console.log(`Updating ${package_default.version} \u2192 ${latest}...`);
|
|
1604
|
+
try {
|
|
1605
|
+
execFileSync("npm", ["install", "-g", `${NPM_PACKAGE}@latest`], {
|
|
1606
|
+
timeout: 12e4,
|
|
1607
|
+
shell: true,
|
|
1608
|
+
stdio: "inherit"
|
|
1609
|
+
});
|
|
1610
|
+
console.log(`Updated to ${latest}.`);
|
|
1611
|
+
} catch (err) {
|
|
1612
|
+
console.error(`Update failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
1613
|
+
console.error(`Try manually: npm install -g ${NPM_PACKAGE}`);
|
|
1614
|
+
process.exit(1);
|
|
1615
|
+
}
|
|
1616
|
+
});
|
|
1617
|
+
|
|
1618
|
+
// src/commands/whoami.ts
|
|
1619
|
+
import { Command as Command19 } from "commander";
|
|
1620
|
+
var whoamiCommand = new Command19("whoami").description("Show the authenticated identity").action(async function() {
|
|
1621
|
+
const client = new ApiClient();
|
|
1622
|
+
const me = await client.request("GET", "/v1/auth/me");
|
|
1623
|
+
if (getFormat(this) === "json") {
|
|
1624
|
+
console.log(JSON.stringify(me, null, 2));
|
|
1625
|
+
return;
|
|
1626
|
+
}
|
|
1627
|
+
console.log(`auth_kind: ${me.auth_kind}`);
|
|
1628
|
+
if (me.email) console.log(`email: ${me.email}`);
|
|
1629
|
+
if (me.user_id) console.log(`user_id: ${me.user_id}`);
|
|
1630
|
+
if (me.org_id) console.log(`org_id: ${me.org_id}`);
|
|
1631
|
+
console.log(`grants: ${me.grants_summary}`);
|
|
1632
|
+
if (me.orgs && me.orgs.length > 0) {
|
|
1633
|
+
console.log("organizations:");
|
|
1634
|
+
for (const o of me.orgs) {
|
|
1635
|
+
console.log(` ${o.name} (${o.org_id}) \u2014 ${o.role}`);
|
|
1636
|
+
}
|
|
1637
|
+
}
|
|
1638
|
+
});
|
|
1639
|
+
|
|
1640
|
+
// src/commands/status.ts
|
|
1641
|
+
import { Command as Command20 } from "commander";
|
|
1642
|
+
var statusCommand = new Command20("status").description("Show auth, connectivity, and CLI version diagnostics").action(async function() {
|
|
1643
|
+
const config = loadConfig();
|
|
1644
|
+
const loggedIn = await isAuthenticated();
|
|
1645
|
+
const cliLatest = readCachedLatest(package_default.version);
|
|
1646
|
+
let tokenValid = null;
|
|
1647
|
+
let me = null;
|
|
1648
|
+
if (loggedIn) {
|
|
1649
|
+
try {
|
|
1650
|
+
me = await new ApiClient().request("GET", "/v1/auth/me");
|
|
1651
|
+
tokenValid = true;
|
|
1652
|
+
} catch (err) {
|
|
1653
|
+
if (err instanceof ApiError && (err.status === 401 || err.status === 403)) tokenValid = false;
|
|
1654
|
+
}
|
|
1655
|
+
}
|
|
1656
|
+
const snapshot = {
|
|
1657
|
+
logged_in: loggedIn,
|
|
1658
|
+
token_valid: tokenValid,
|
|
1659
|
+
api_url: config.api_url,
|
|
1660
|
+
auth_kind: me?.auth_kind ?? null,
|
|
1661
|
+
email: me?.email ?? null,
|
|
1662
|
+
org_id: me?.org_id ?? null,
|
|
1663
|
+
grants: me?.grants_summary ?? null,
|
|
1664
|
+
cli: { version: package_default.version, latest: cliLatest }
|
|
1665
|
+
};
|
|
1666
|
+
if (getFormat(this) === "json") {
|
|
1667
|
+
console.log(JSON.stringify(snapshot, null, 2));
|
|
1668
|
+
return;
|
|
1669
|
+
}
|
|
1670
|
+
if (!loggedIn) {
|
|
1671
|
+
console.log("Not logged in. Run `rekor login`.");
|
|
1672
|
+
console.log(`API: ${config.api_url}`);
|
|
1673
|
+
if (cliLatest) console.log(`CLI: ${package_default.version} (latest: ${cliLatest} \u2014 run \`rekor update\`)`);
|
|
1674
|
+
return;
|
|
1675
|
+
}
|
|
1676
|
+
const tokenStatus = tokenValid === true ? "token valid" : tokenValid === false ? "token rejected \u2014 run `rekor login` to re-authenticate" : "token validity unknown \u2014 backend unreachable";
|
|
1677
|
+
console.log(`Logged in${me?.email ? ` as ${me.email}` : ""} \u2014 ${tokenStatus}`);
|
|
1678
|
+
console.log(`API: ${config.api_url}`);
|
|
1679
|
+
if (me?.auth_kind) console.log(`Auth: ${me.auth_kind}`);
|
|
1680
|
+
if (me?.org_id) console.log(`Org: ${me.org_id}`);
|
|
1681
|
+
if (me?.grants_summary) console.log(`Grants: ${me.grants_summary}`);
|
|
1682
|
+
if (cliLatest) console.log(`CLI: ${package_default.version} (latest: ${cliLatest} \u2014 run \`rekor update\`)`);
|
|
1683
|
+
});
|
|
1684
|
+
|
|
1388
1685
|
// src/program.ts
|
|
1389
|
-
var program = new
|
|
1686
|
+
var program = new Command21("rekor").description("Rekor CLI \u2014 System of Record for AI agents").version(package_default.version).option("--database <id>", "Database ID").option("--output <format>", "Output format: json or table", "table");
|
|
1390
1687
|
program.addCommand(loginCommand);
|
|
1391
1688
|
program.addCommand(logoutCommand);
|
|
1689
|
+
program.addCommand(whoamiCommand);
|
|
1690
|
+
program.addCommand(statusCommand);
|
|
1392
1691
|
program.addCommand(databasesCommand);
|
|
1393
1692
|
program.addCommand(collectionsCommand);
|
|
1394
1693
|
program.addCommand(documentsCommand);
|
|
@@ -1404,7 +1703,84 @@ program.addCommand(tokensCommand);
|
|
|
1404
1703
|
program.addCommand(endpointsCommand);
|
|
1405
1704
|
program.addCommand(debugCommand);
|
|
1406
1705
|
program.addCommand(reportBugCommand);
|
|
1706
|
+
program.addCommand(updateCommand);
|
|
1707
|
+
|
|
1708
|
+
// src/telemetry.ts
|
|
1709
|
+
import { homedir as homedir2, platform, arch } from "os";
|
|
1710
|
+
var sentry = null;
|
|
1711
|
+
function getDsn() {
|
|
1712
|
+
if (process.env["REKOR_TELEMETRY_DISABLED"]) return void 0;
|
|
1713
|
+
return process.env["REKOR_CLI_SENTRY_DSN"] || void 0;
|
|
1714
|
+
}
|
|
1715
|
+
function scrubHome(event, home) {
|
|
1716
|
+
const root = home.replace(/[\\/]+$/, "");
|
|
1717
|
+
if (!root) return event;
|
|
1718
|
+
const re = new RegExp(root.replace(/[.*+?^${}()|[\]\\]/g, "\\$&") + "(?=[\\\\/]|$)", "g");
|
|
1719
|
+
const scrub = (s) => s.replace(re, "~");
|
|
1720
|
+
for (const value of event.exception?.values ?? []) {
|
|
1721
|
+
if (value.value) value.value = scrub(value.value);
|
|
1722
|
+
for (const frame of value.stacktrace?.frames ?? []) {
|
|
1723
|
+
if (frame.filename) frame.filename = scrub(frame.filename);
|
|
1724
|
+
if (frame.abs_path) frame.abs_path = scrub(frame.abs_path);
|
|
1725
|
+
}
|
|
1726
|
+
}
|
|
1727
|
+
return event;
|
|
1728
|
+
}
|
|
1729
|
+
async function initTelemetry(command2, version) {
|
|
1730
|
+
if (sentry) return;
|
|
1731
|
+
const dsn = getDsn();
|
|
1732
|
+
if (!dsn) return;
|
|
1733
|
+
try {
|
|
1734
|
+
const mod = await import("@sentry/node");
|
|
1735
|
+
const home = homedir2();
|
|
1736
|
+
mod.init({
|
|
1737
|
+
dsn,
|
|
1738
|
+
tracesSampleRate: 0,
|
|
1739
|
+
release: `rekor-cli@${version}`,
|
|
1740
|
+
environment: "production",
|
|
1741
|
+
defaultIntegrations: false,
|
|
1742
|
+
beforeSend: (event) => scrubHome(event, home)
|
|
1743
|
+
});
|
|
1744
|
+
mod.setTag("command", command2 || "unknown");
|
|
1745
|
+
mod.setTag("node_version", process.version);
|
|
1746
|
+
mod.setTag("os_platform", platform());
|
|
1747
|
+
mod.setTag("os_arch", arch());
|
|
1748
|
+
sentry = mod;
|
|
1749
|
+
} catch {
|
|
1750
|
+
sentry = null;
|
|
1751
|
+
}
|
|
1752
|
+
}
|
|
1753
|
+
function captureException(err) {
|
|
1754
|
+
if (!sentry) return;
|
|
1755
|
+
if (err instanceof ApiError && err.isExpected) return;
|
|
1756
|
+
try {
|
|
1757
|
+
sentry.captureException(err);
|
|
1758
|
+
} catch {
|
|
1759
|
+
}
|
|
1760
|
+
}
|
|
1761
|
+
async function closeTelemetry() {
|
|
1762
|
+
if (!sentry) return;
|
|
1763
|
+
try {
|
|
1764
|
+
await sentry.close(2e3);
|
|
1765
|
+
} catch {
|
|
1766
|
+
}
|
|
1767
|
+
sentry = null;
|
|
1768
|
+
}
|
|
1407
1769
|
|
|
1408
1770
|
// src/index.ts
|
|
1409
|
-
|
|
1771
|
+
var SKIP_UPDATE_CHECK = ["--version", "-v", "--help", "-h", "help", "update"];
|
|
1772
|
+
var command = process.argv[2];
|
|
1773
|
+
async function main() {
|
|
1774
|
+
await initTelemetry(command, package_default.version);
|
|
1775
|
+
await program.parseAsync();
|
|
1776
|
+
if (command && !SKIP_UPDATE_CHECK.includes(command)) {
|
|
1777
|
+
checkForUpdates(package_default.version);
|
|
1778
|
+
}
|
|
1779
|
+
}
|
|
1780
|
+
main().then(() => closeTelemetry()).catch(async (err) => {
|
|
1781
|
+
handleCliError(err);
|
|
1782
|
+
captureException(err);
|
|
1783
|
+
await closeTelemetry();
|
|
1784
|
+
process.exit(1);
|
|
1785
|
+
});
|
|
1410
1786
|
//# sourceMappingURL=index.js.map
|