@freecodecamp/universe-cli 0.8.1 → 0.9.0
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.cjs +141 -17
- package/dist/index.js +146 -21
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -23566,6 +23566,16 @@ function createProxyClient(cfg) {
|
|
|
23566
23566
|
(raw) => repoRowSchema.parse(raw)
|
|
23567
23567
|
);
|
|
23568
23568
|
},
|
|
23569
|
+
async deleteRepoRequest(req) {
|
|
23570
|
+
const url2 = `${base}/api/repo/${encodeURIComponent(req.id)}`;
|
|
23571
|
+
return call(url2, {
|
|
23572
|
+
method: "DELETE",
|
|
23573
|
+
headers: {
|
|
23574
|
+
Authorization: await userBearer(),
|
|
23575
|
+
Accept: "application/json"
|
|
23576
|
+
}
|
|
23577
|
+
});
|
|
23578
|
+
},
|
|
23569
23579
|
async listRepoTemplates() {
|
|
23570
23580
|
const res = await call(
|
|
23571
23581
|
`${base}/api/repo/templates`,
|
|
@@ -25295,7 +25305,9 @@ async function create(options, deps = {}) {
|
|
|
25295
25305
|
}
|
|
25296
25306
|
} catch (err) {
|
|
25297
25307
|
const { code, message, kind, requestId } = wrapProxyError(command, err);
|
|
25298
|
-
|
|
25308
|
+
const display = kind === "already_exists" && !options.json ? `${message}
|
|
25309
|
+
\u2192 run \`universe repo ls --status all\` to find the existing request (it may be active or failed)` : message;
|
|
25310
|
+
outputError({ json: options.json, command }, code, display, {
|
|
25299
25311
|
logError: error48,
|
|
25300
25312
|
kind,
|
|
25301
25313
|
requestId,
|
|
@@ -25314,19 +25326,20 @@ async function ls3(options, deps = {}) {
|
|
|
25314
25326
|
const exit = deps.exit ?? exitWithCode;
|
|
25315
25327
|
let identitySource;
|
|
25316
25328
|
try {
|
|
25317
|
-
|
|
25329
|
+
const requestedStatus = options.all ? "all" : options.status;
|
|
25330
|
+
if (requestedStatus !== void 0 && !LS_STATUSES.includes(requestedStatus)) {
|
|
25318
25331
|
throw new UsageError(
|
|
25319
|
-
`invalid --status "${
|
|
25332
|
+
`invalid --status "${requestedStatus}": must be one of ${LS_STATUSES.join(", ")}`
|
|
25320
25333
|
);
|
|
25321
25334
|
}
|
|
25322
25335
|
const setup = await setupClient2(deps);
|
|
25323
25336
|
const client = setup.client;
|
|
25324
25337
|
identitySource = setup.identitySource;
|
|
25325
25338
|
const rows = await client.listRepoRequests({
|
|
25326
|
-
status:
|
|
25339
|
+
status: requestedStatus,
|
|
25327
25340
|
mine: options.mine ?? false
|
|
25328
25341
|
});
|
|
25329
|
-
const status2 =
|
|
25342
|
+
const status2 = requestedStatus ?? "pending";
|
|
25330
25343
|
if (options.json) {
|
|
25331
25344
|
emitJson9(
|
|
25332
25345
|
buildEnvelope(command, true, {
|
|
@@ -25425,6 +25438,62 @@ async function reject(options, deps = {}) {
|
|
|
25425
25438
|
}
|
|
25426
25439
|
}
|
|
25427
25440
|
|
|
25441
|
+
// src/commands/repo/rm.ts
|
|
25442
|
+
async function rm3(options, deps = {}) {
|
|
25443
|
+
const command = "repo rm";
|
|
25444
|
+
const success2 = deps.logSuccess ?? ((s) => O2.success(s));
|
|
25445
|
+
const error48 = deps.logError ?? ((s) => O2.error(s));
|
|
25446
|
+
const exit = deps.exit ?? exitWithCode;
|
|
25447
|
+
const prompts = deps.prompts ?? defaultRepoPrompts;
|
|
25448
|
+
const isTTY = deps.isTTY ?? Boolean(process.stdout.isTTY);
|
|
25449
|
+
let identitySource;
|
|
25450
|
+
try {
|
|
25451
|
+
if (!options.id || options.id.trim().length === 0) {
|
|
25452
|
+
throw new UsageError("request id is required (positional argument)");
|
|
25453
|
+
}
|
|
25454
|
+
const setup = await setupClient2(deps);
|
|
25455
|
+
const client = setup.client;
|
|
25456
|
+
identitySource = setup.identitySource;
|
|
25457
|
+
if (!options.json && !options.yes) {
|
|
25458
|
+
if (!isTTY) {
|
|
25459
|
+
throw new UsageError(
|
|
25460
|
+
"non-interactive session: pass --yes to delete without confirmation"
|
|
25461
|
+
);
|
|
25462
|
+
}
|
|
25463
|
+
const cur = await client.getRepoRequest(options.id);
|
|
25464
|
+
const ok = await prompts.confirm({
|
|
25465
|
+
message: `Delete the ${cur.status} request for "${cur.name}" (${cur.id})? This frees the repo name.`
|
|
25466
|
+
});
|
|
25467
|
+
if (prompts.isCancel(ok) || ok === false) {
|
|
25468
|
+
throw new ConfirmError("repo rm cancelled");
|
|
25469
|
+
}
|
|
25470
|
+
}
|
|
25471
|
+
await client.deleteRepoRequest({ id: options.id });
|
|
25472
|
+
if (options.json) {
|
|
25473
|
+
emitJson9(
|
|
25474
|
+
buildEnvelope(command, true, {
|
|
25475
|
+
id: options.id,
|
|
25476
|
+
deleted: true,
|
|
25477
|
+
identitySource
|
|
25478
|
+
})
|
|
25479
|
+
);
|
|
25480
|
+
} else {
|
|
25481
|
+
success2(
|
|
25482
|
+
`Deleted request ${options.id} \u2014 the repo name is free to request again`
|
|
25483
|
+
);
|
|
25484
|
+
}
|
|
25485
|
+
} catch (err) {
|
|
25486
|
+
const { code, message, kind, requestId } = wrapProxyError(command, err);
|
|
25487
|
+
outputError({ json: options.json, command }, code, message, {
|
|
25488
|
+
logError: error48,
|
|
25489
|
+
kind,
|
|
25490
|
+
requestId,
|
|
25491
|
+
extras: identitySource ? { identitySource } : void 0
|
|
25492
|
+
});
|
|
25493
|
+
exit(code);
|
|
25494
|
+
}
|
|
25495
|
+
}
|
|
25496
|
+
|
|
25428
25497
|
// src/commands/repo/status.ts
|
|
25429
25498
|
function humanRow(row) {
|
|
25430
25499
|
const lines = [
|
|
@@ -25481,6 +25550,7 @@ async function status(options, deps = {}) {
|
|
|
25481
25550
|
}
|
|
25482
25551
|
|
|
25483
25552
|
// src/lib/update-notifier.ts
|
|
25553
|
+
var import_node_child_process4 = require("child_process");
|
|
25484
25554
|
var import_node_fs3 = require("fs");
|
|
25485
25555
|
var import_promises8 = require("fs/promises");
|
|
25486
25556
|
var import_node_os2 = require("os");
|
|
@@ -25489,8 +25559,21 @@ var APP_DIR2 = "universe-cli";
|
|
|
25489
25559
|
var CACHE_FILE = "update-check.json";
|
|
25490
25560
|
var PKG_NAME = "@freecodecamp/universe-cli";
|
|
25491
25561
|
var NPM_LATEST_URL = `https://registry.npmjs.org/${PKG_NAME}/latest`;
|
|
25492
|
-
var
|
|
25562
|
+
var DEFAULT_TTL_MS = 60 * 60 * 1e3;
|
|
25493
25563
|
var FETCH_TIMEOUT_MS = 3e3;
|
|
25564
|
+
var REFRESH_ENV = "UNIVERSE_REFRESH_WORKER";
|
|
25565
|
+
function ttlMs() {
|
|
25566
|
+
const raw = process.env["UNIVERSE_UPDATE_TTL_MS"];
|
|
25567
|
+
if (raw !== void 0) {
|
|
25568
|
+
const n = Number.parseInt(raw, 10);
|
|
25569
|
+
if (Number.isFinite(n) && n >= 0) return n;
|
|
25570
|
+
}
|
|
25571
|
+
return DEFAULT_TTL_MS;
|
|
25572
|
+
}
|
|
25573
|
+
function latestUrl() {
|
|
25574
|
+
const override = process.env["UNIVERSE_UPDATE_URL"];
|
|
25575
|
+
return override && override.length > 0 ? override : NPM_LATEST_URL;
|
|
25576
|
+
}
|
|
25494
25577
|
function configBase2() {
|
|
25495
25578
|
const xdg = process.env["XDG_CONFIG_HOME"];
|
|
25496
25579
|
if (xdg && xdg.length > 0) return xdg;
|
|
@@ -25544,7 +25627,7 @@ async function fetchLatest() {
|
|
|
25544
25627
|
const ctl = new AbortController();
|
|
25545
25628
|
const timer = setTimeout(() => ctl.abort(), FETCH_TIMEOUT_MS);
|
|
25546
25629
|
try {
|
|
25547
|
-
const res = await fetch(
|
|
25630
|
+
const res = await fetch(latestUrl(), {
|
|
25548
25631
|
signal: ctl.signal,
|
|
25549
25632
|
headers: { accept: "application/json" }
|
|
25550
25633
|
});
|
|
@@ -25581,7 +25664,7 @@ async function refreshIfStale(now = Date.now(), options = {}) {
|
|
|
25581
25664
|
if (isDisabled()) return;
|
|
25582
25665
|
if (!options.force) {
|
|
25583
25666
|
const cache = await readCache();
|
|
25584
|
-
if (cache !== null && now - cache.lastCheck <
|
|
25667
|
+
if (cache !== null && now - cache.lastCheck < ttlMs()) return;
|
|
25585
25668
|
}
|
|
25586
25669
|
const latest = await fetchLatest();
|
|
25587
25670
|
if (latest === null) return;
|
|
@@ -25590,6 +25673,25 @@ async function refreshIfStale(now = Date.now(), options = {}) {
|
|
|
25590
25673
|
} catch {
|
|
25591
25674
|
}
|
|
25592
25675
|
}
|
|
25676
|
+
function spawnRefresh(now = Date.now()) {
|
|
25677
|
+
if (isDisabled()) return;
|
|
25678
|
+
const cache = readCacheSync();
|
|
25679
|
+
if (cache !== null && now - cache.lastCheck < ttlMs()) return;
|
|
25680
|
+
try {
|
|
25681
|
+
const entry = process.argv[1];
|
|
25682
|
+
const args = entry ? [entry] : [];
|
|
25683
|
+
const child = (0, import_node_child_process4.spawn)(process.execPath, args, {
|
|
25684
|
+
detached: true,
|
|
25685
|
+
stdio: "ignore",
|
|
25686
|
+
env: { ...process.env, [REFRESH_ENV]: "1" }
|
|
25687
|
+
});
|
|
25688
|
+
child.unref();
|
|
25689
|
+
} catch {
|
|
25690
|
+
}
|
|
25691
|
+
}
|
|
25692
|
+
async function runRefreshWorker() {
|
|
25693
|
+
await refreshIfStale(Date.now(), { force: true });
|
|
25694
|
+
}
|
|
25593
25695
|
function getNoticeSync(current) {
|
|
25594
25696
|
if (isDisabled()) return null;
|
|
25595
25697
|
const cache = readCacheSync();
|
|
@@ -25630,14 +25732,17 @@ function installExitNotice(current) {
|
|
|
25630
25732
|
printed = true;
|
|
25631
25733
|
const n = getNoticeSync(current);
|
|
25632
25734
|
if (n === null) return;
|
|
25633
|
-
|
|
25735
|
+
try {
|
|
25736
|
+
(0, import_node_fs3.writeSync)(2, formatNotice(n));
|
|
25737
|
+
} catch {
|
|
25738
|
+
}
|
|
25634
25739
|
};
|
|
25635
25740
|
process.on("beforeExit", emit);
|
|
25636
25741
|
process.on("exit", emit);
|
|
25637
25742
|
}
|
|
25638
25743
|
|
|
25639
25744
|
// src/cli.ts
|
|
25640
|
-
var version2 = true ? "0.
|
|
25745
|
+
var version2 = true ? "0.9.0" : "0.0.0";
|
|
25641
25746
|
function handleActionError(command, json2, err) {
|
|
25642
25747
|
const ctx = { json: json2, command };
|
|
25643
25748
|
const message = err instanceof Error ? err.message : "unknown error";
|
|
@@ -25660,8 +25765,7 @@ async function run(argv = process.argv) {
|
|
|
25660
25765
|
const args = argv.slice(2);
|
|
25661
25766
|
const versionRequested = isVersionRequest(args);
|
|
25662
25767
|
if (!versionRequested) {
|
|
25663
|
-
|
|
25664
|
-
});
|
|
25768
|
+
spawnRefresh();
|
|
25665
25769
|
}
|
|
25666
25770
|
const firstPosIdx = findFirstPositional(args);
|
|
25667
25771
|
const namespace = firstPosIdx >= 0 ? args[firstPosIdx] : void 0;
|
|
@@ -25765,13 +25869,14 @@ async function run(argv = process.argv) {
|
|
|
25765
25869
|
repoCli.command("ls", "List repo requests (default: pending)").option("--json", "Output as JSON").option(
|
|
25766
25870
|
"--status <status>",
|
|
25767
25871
|
"pending | approved | active | rejected | failed | all"
|
|
25768
|
-
).option("--mine", "Only requests you submitted").action(
|
|
25872
|
+
).option("--mine", "Only requests you submitted").option("--all", "Show every state (shorthand for --status all)").action(
|
|
25769
25873
|
async (flags) => {
|
|
25770
25874
|
try {
|
|
25771
25875
|
await ls3({
|
|
25772
25876
|
json: flags.json ?? false,
|
|
25773
25877
|
status: flags.status,
|
|
25774
|
-
mine: flags.mine ?? false
|
|
25878
|
+
mine: flags.mine ?? false,
|
|
25879
|
+
all: flags.all ?? false
|
|
25775
25880
|
});
|
|
25776
25881
|
} catch (err) {
|
|
25777
25882
|
handleActionError("repo ls", flags.json ?? false, err);
|
|
@@ -25813,6 +25918,20 @@ async function run(argv = process.argv) {
|
|
|
25813
25918
|
handleActionError("repo status", flags.json ?? false, err);
|
|
25814
25919
|
}
|
|
25815
25920
|
});
|
|
25921
|
+
repoCli.command(
|
|
25922
|
+
"rm <id>",
|
|
25923
|
+
"Delete a request, freeing its repo name (admin only)"
|
|
25924
|
+
).option("--json", "Output as JSON").option("--yes", "Skip confirmation prompts (required for non-TTY/CI)").action(async (id, flags) => {
|
|
25925
|
+
try {
|
|
25926
|
+
await rm3({
|
|
25927
|
+
json: flags.json ?? false,
|
|
25928
|
+
id,
|
|
25929
|
+
yes: flags.yes ?? false
|
|
25930
|
+
});
|
|
25931
|
+
} catch (err) {
|
|
25932
|
+
handleActionError("repo rm", flags.json ?? false, err);
|
|
25933
|
+
}
|
|
25934
|
+
});
|
|
25816
25935
|
repoCli.help();
|
|
25817
25936
|
repoCli.version(version2);
|
|
25818
25937
|
const knownRepoSubs = /* @__PURE__ */ new Set([
|
|
@@ -25820,7 +25939,8 @@ async function run(argv = process.argv) {
|
|
|
25820
25939
|
"ls",
|
|
25821
25940
|
"approve",
|
|
25822
25941
|
"reject",
|
|
25823
|
-
"status"
|
|
25942
|
+
"status",
|
|
25943
|
+
"rm"
|
|
25824
25944
|
]);
|
|
25825
25945
|
const repoValueFlags = /* @__PURE__ */ new Set([
|
|
25826
25946
|
"--visibility",
|
|
@@ -25975,5 +26095,9 @@ function installFatalHandlers(onFatal = defaultOnFatal) {
|
|
|
25975
26095
|
}
|
|
25976
26096
|
|
|
25977
26097
|
// src/index.ts
|
|
25978
|
-
|
|
25979
|
-
void
|
|
26098
|
+
if (process.env[REFRESH_ENV] === "1") {
|
|
26099
|
+
void runRefreshWorker();
|
|
26100
|
+
} else {
|
|
26101
|
+
installFatalHandlers();
|
|
26102
|
+
void run();
|
|
26103
|
+
}
|
package/dist/index.js
CHANGED
|
@@ -889,6 +889,16 @@ function createProxyClient(cfg) {
|
|
|
889
889
|
(raw) => repoRowSchema.parse(raw)
|
|
890
890
|
);
|
|
891
891
|
},
|
|
892
|
+
async deleteRepoRequest(req) {
|
|
893
|
+
const url = `${base}/api/repo/${encodeURIComponent(req.id)}`;
|
|
894
|
+
return call(url, {
|
|
895
|
+
method: "DELETE",
|
|
896
|
+
headers: {
|
|
897
|
+
Authorization: await userBearer(),
|
|
898
|
+
Accept: "application/json"
|
|
899
|
+
}
|
|
900
|
+
});
|
|
901
|
+
},
|
|
892
902
|
async listRepoTemplates() {
|
|
893
903
|
const res = await call(
|
|
894
904
|
`${base}/api/repo/templates`,
|
|
@@ -2645,7 +2655,9 @@ async function create(options, deps = {}) {
|
|
|
2645
2655
|
}
|
|
2646
2656
|
} catch (err) {
|
|
2647
2657
|
const { code, message, kind, requestId } = wrapProxyError(command, err);
|
|
2648
|
-
|
|
2658
|
+
const display = kind === "already_exists" && !options.json ? `${message}
|
|
2659
|
+
\u2192 run \`universe repo ls --status all\` to find the existing request (it may be active or failed)` : message;
|
|
2660
|
+
outputError({ json: options.json, command }, code, display, {
|
|
2649
2661
|
logError: error,
|
|
2650
2662
|
kind,
|
|
2651
2663
|
requestId,
|
|
@@ -2665,19 +2677,20 @@ async function ls3(options, deps = {}) {
|
|
|
2665
2677
|
const exit = deps.exit ?? exitWithCode;
|
|
2666
2678
|
let identitySource;
|
|
2667
2679
|
try {
|
|
2668
|
-
|
|
2680
|
+
const requestedStatus = options.all ? "all" : options.status;
|
|
2681
|
+
if (requestedStatus !== void 0 && !LS_STATUSES.includes(requestedStatus)) {
|
|
2669
2682
|
throw new UsageError(
|
|
2670
|
-
`invalid --status "${
|
|
2683
|
+
`invalid --status "${requestedStatus}": must be one of ${LS_STATUSES.join(", ")}`
|
|
2671
2684
|
);
|
|
2672
2685
|
}
|
|
2673
2686
|
const setup = await setupClient2(deps);
|
|
2674
2687
|
const client = setup.client;
|
|
2675
2688
|
identitySource = setup.identitySource;
|
|
2676
2689
|
const rows = await client.listRepoRequests({
|
|
2677
|
-
status:
|
|
2690
|
+
status: requestedStatus,
|
|
2678
2691
|
mine: options.mine ?? false
|
|
2679
2692
|
});
|
|
2680
|
-
const status2 =
|
|
2693
|
+
const status2 = requestedStatus ?? "pending";
|
|
2681
2694
|
if (options.json) {
|
|
2682
2695
|
emitJson9(
|
|
2683
2696
|
buildEnvelope(command, true, {
|
|
@@ -2777,8 +2790,65 @@ async function reject(options, deps = {}) {
|
|
|
2777
2790
|
}
|
|
2778
2791
|
}
|
|
2779
2792
|
|
|
2780
|
-
// src/commands/repo/
|
|
2793
|
+
// src/commands/repo/rm.ts
|
|
2781
2794
|
import { log as log17 } from "@clack/prompts";
|
|
2795
|
+
async function rm3(options, deps = {}) {
|
|
2796
|
+
const command = "repo rm";
|
|
2797
|
+
const success = deps.logSuccess ?? ((s) => log17.success(s));
|
|
2798
|
+
const error = deps.logError ?? ((s) => log17.error(s));
|
|
2799
|
+
const exit = deps.exit ?? exitWithCode;
|
|
2800
|
+
const prompts = deps.prompts ?? defaultRepoPrompts;
|
|
2801
|
+
const isTTY = deps.isTTY ?? Boolean(process.stdout.isTTY);
|
|
2802
|
+
let identitySource;
|
|
2803
|
+
try {
|
|
2804
|
+
if (!options.id || options.id.trim().length === 0) {
|
|
2805
|
+
throw new UsageError("request id is required (positional argument)");
|
|
2806
|
+
}
|
|
2807
|
+
const setup = await setupClient2(deps);
|
|
2808
|
+
const client = setup.client;
|
|
2809
|
+
identitySource = setup.identitySource;
|
|
2810
|
+
if (!options.json && !options.yes) {
|
|
2811
|
+
if (!isTTY) {
|
|
2812
|
+
throw new UsageError(
|
|
2813
|
+
"non-interactive session: pass --yes to delete without confirmation"
|
|
2814
|
+
);
|
|
2815
|
+
}
|
|
2816
|
+
const cur = await client.getRepoRequest(options.id);
|
|
2817
|
+
const ok = await prompts.confirm({
|
|
2818
|
+
message: `Delete the ${cur.status} request for "${cur.name}" (${cur.id})? This frees the repo name.`
|
|
2819
|
+
});
|
|
2820
|
+
if (prompts.isCancel(ok) || ok === false) {
|
|
2821
|
+
throw new ConfirmError("repo rm cancelled");
|
|
2822
|
+
}
|
|
2823
|
+
}
|
|
2824
|
+
await client.deleteRepoRequest({ id: options.id });
|
|
2825
|
+
if (options.json) {
|
|
2826
|
+
emitJson9(
|
|
2827
|
+
buildEnvelope(command, true, {
|
|
2828
|
+
id: options.id,
|
|
2829
|
+
deleted: true,
|
|
2830
|
+
identitySource
|
|
2831
|
+
})
|
|
2832
|
+
);
|
|
2833
|
+
} else {
|
|
2834
|
+
success(
|
|
2835
|
+
`Deleted request ${options.id} \u2014 the repo name is free to request again`
|
|
2836
|
+
);
|
|
2837
|
+
}
|
|
2838
|
+
} catch (err) {
|
|
2839
|
+
const { code, message, kind, requestId } = wrapProxyError(command, err);
|
|
2840
|
+
outputError({ json: options.json, command }, code, message, {
|
|
2841
|
+
logError: error,
|
|
2842
|
+
kind,
|
|
2843
|
+
requestId,
|
|
2844
|
+
extras: identitySource ? { identitySource } : void 0
|
|
2845
|
+
});
|
|
2846
|
+
exit(code);
|
|
2847
|
+
}
|
|
2848
|
+
}
|
|
2849
|
+
|
|
2850
|
+
// src/commands/repo/status.ts
|
|
2851
|
+
import { log as log18 } from "@clack/prompts";
|
|
2782
2852
|
function humanRow(row) {
|
|
2783
2853
|
const lines = [
|
|
2784
2854
|
`Request ${row.id}`,
|
|
@@ -2799,8 +2869,8 @@ function humanRow(row) {
|
|
|
2799
2869
|
}
|
|
2800
2870
|
async function status(options, deps = {}) {
|
|
2801
2871
|
const command = "repo status";
|
|
2802
|
-
const message = deps.logMessage ?? ((s) =>
|
|
2803
|
-
const error = deps.logError ?? ((s) =>
|
|
2872
|
+
const message = deps.logMessage ?? ((s) => log18.message(s));
|
|
2873
|
+
const error = deps.logError ?? ((s) => log18.error(s));
|
|
2804
2874
|
const exit = deps.exit ?? exitWithCode;
|
|
2805
2875
|
let identitySource;
|
|
2806
2876
|
try {
|
|
@@ -2834,7 +2904,8 @@ async function status(options, deps = {}) {
|
|
|
2834
2904
|
}
|
|
2835
2905
|
|
|
2836
2906
|
// src/lib/update-notifier.ts
|
|
2837
|
-
import {
|
|
2907
|
+
import { spawn as spawn2 } from "child_process";
|
|
2908
|
+
import { readFileSync, writeSync } from "fs";
|
|
2838
2909
|
import { mkdir as mkdir2, readFile as readFile6, writeFile as writeFile2 } from "fs/promises";
|
|
2839
2910
|
import { homedir as homedir2 } from "os";
|
|
2840
2911
|
import { dirname as dirname2, join as join3 } from "path";
|
|
@@ -2842,8 +2913,21 @@ var APP_DIR2 = "universe-cli";
|
|
|
2842
2913
|
var CACHE_FILE = "update-check.json";
|
|
2843
2914
|
var PKG_NAME = "@freecodecamp/universe-cli";
|
|
2844
2915
|
var NPM_LATEST_URL = `https://registry.npmjs.org/${PKG_NAME}/latest`;
|
|
2845
|
-
var
|
|
2916
|
+
var DEFAULT_TTL_MS = 60 * 60 * 1e3;
|
|
2846
2917
|
var FETCH_TIMEOUT_MS = 3e3;
|
|
2918
|
+
var REFRESH_ENV = "UNIVERSE_REFRESH_WORKER";
|
|
2919
|
+
function ttlMs() {
|
|
2920
|
+
const raw = process.env["UNIVERSE_UPDATE_TTL_MS"];
|
|
2921
|
+
if (raw !== void 0) {
|
|
2922
|
+
const n = Number.parseInt(raw, 10);
|
|
2923
|
+
if (Number.isFinite(n) && n >= 0) return n;
|
|
2924
|
+
}
|
|
2925
|
+
return DEFAULT_TTL_MS;
|
|
2926
|
+
}
|
|
2927
|
+
function latestUrl() {
|
|
2928
|
+
const override = process.env["UNIVERSE_UPDATE_URL"];
|
|
2929
|
+
return override && override.length > 0 ? override : NPM_LATEST_URL;
|
|
2930
|
+
}
|
|
2847
2931
|
function configBase2() {
|
|
2848
2932
|
const xdg = process.env["XDG_CONFIG_HOME"];
|
|
2849
2933
|
if (xdg && xdg.length > 0) return xdg;
|
|
@@ -2897,7 +2981,7 @@ async function fetchLatest() {
|
|
|
2897
2981
|
const ctl = new AbortController();
|
|
2898
2982
|
const timer = setTimeout(() => ctl.abort(), FETCH_TIMEOUT_MS);
|
|
2899
2983
|
try {
|
|
2900
|
-
const res = await fetch(
|
|
2984
|
+
const res = await fetch(latestUrl(), {
|
|
2901
2985
|
signal: ctl.signal,
|
|
2902
2986
|
headers: { accept: "application/json" }
|
|
2903
2987
|
});
|
|
@@ -2934,7 +3018,7 @@ async function refreshIfStale(now = Date.now(), options = {}) {
|
|
|
2934
3018
|
if (isDisabled()) return;
|
|
2935
3019
|
if (!options.force) {
|
|
2936
3020
|
const cache = await readCache();
|
|
2937
|
-
if (cache !== null && now - cache.lastCheck <
|
|
3021
|
+
if (cache !== null && now - cache.lastCheck < ttlMs()) return;
|
|
2938
3022
|
}
|
|
2939
3023
|
const latest = await fetchLatest();
|
|
2940
3024
|
if (latest === null) return;
|
|
@@ -2943,6 +3027,25 @@ async function refreshIfStale(now = Date.now(), options = {}) {
|
|
|
2943
3027
|
} catch {
|
|
2944
3028
|
}
|
|
2945
3029
|
}
|
|
3030
|
+
function spawnRefresh(now = Date.now()) {
|
|
3031
|
+
if (isDisabled()) return;
|
|
3032
|
+
const cache = readCacheSync();
|
|
3033
|
+
if (cache !== null && now - cache.lastCheck < ttlMs()) return;
|
|
3034
|
+
try {
|
|
3035
|
+
const entry = process.argv[1];
|
|
3036
|
+
const args = entry ? [entry] : [];
|
|
3037
|
+
const child = spawn2(process.execPath, args, {
|
|
3038
|
+
detached: true,
|
|
3039
|
+
stdio: "ignore",
|
|
3040
|
+
env: { ...process.env, [REFRESH_ENV]: "1" }
|
|
3041
|
+
});
|
|
3042
|
+
child.unref();
|
|
3043
|
+
} catch {
|
|
3044
|
+
}
|
|
3045
|
+
}
|
|
3046
|
+
async function runRefreshWorker() {
|
|
3047
|
+
await refreshIfStale(Date.now(), { force: true });
|
|
3048
|
+
}
|
|
2946
3049
|
function getNoticeSync(current) {
|
|
2947
3050
|
if (isDisabled()) return null;
|
|
2948
3051
|
const cache = readCacheSync();
|
|
@@ -2983,14 +3086,17 @@ function installExitNotice(current) {
|
|
|
2983
3086
|
printed = true;
|
|
2984
3087
|
const n = getNoticeSync(current);
|
|
2985
3088
|
if (n === null) return;
|
|
2986
|
-
|
|
3089
|
+
try {
|
|
3090
|
+
writeSync(2, formatNotice(n));
|
|
3091
|
+
} catch {
|
|
3092
|
+
}
|
|
2987
3093
|
};
|
|
2988
3094
|
process.on("beforeExit", emit);
|
|
2989
3095
|
process.on("exit", emit);
|
|
2990
3096
|
}
|
|
2991
3097
|
|
|
2992
3098
|
// src/cli.ts
|
|
2993
|
-
var version = true ? "0.
|
|
3099
|
+
var version = true ? "0.9.0" : "0.0.0";
|
|
2994
3100
|
function handleActionError(command, json, err) {
|
|
2995
3101
|
const ctx = { json, command };
|
|
2996
3102
|
const message = err instanceof Error ? err.message : "unknown error";
|
|
@@ -3013,8 +3119,7 @@ async function run(argv = process.argv) {
|
|
|
3013
3119
|
const args = argv.slice(2);
|
|
3014
3120
|
const versionRequested = isVersionRequest(args);
|
|
3015
3121
|
if (!versionRequested) {
|
|
3016
|
-
|
|
3017
|
-
});
|
|
3122
|
+
spawnRefresh();
|
|
3018
3123
|
}
|
|
3019
3124
|
const firstPosIdx = findFirstPositional(args);
|
|
3020
3125
|
const namespace = firstPosIdx >= 0 ? args[firstPosIdx] : void 0;
|
|
@@ -3118,13 +3223,14 @@ async function run(argv = process.argv) {
|
|
|
3118
3223
|
repoCli.command("ls", "List repo requests (default: pending)").option("--json", "Output as JSON").option(
|
|
3119
3224
|
"--status <status>",
|
|
3120
3225
|
"pending | approved | active | rejected | failed | all"
|
|
3121
|
-
).option("--mine", "Only requests you submitted").action(
|
|
3226
|
+
).option("--mine", "Only requests you submitted").option("--all", "Show every state (shorthand for --status all)").action(
|
|
3122
3227
|
async (flags) => {
|
|
3123
3228
|
try {
|
|
3124
3229
|
await ls3({
|
|
3125
3230
|
json: flags.json ?? false,
|
|
3126
3231
|
status: flags.status,
|
|
3127
|
-
mine: flags.mine ?? false
|
|
3232
|
+
mine: flags.mine ?? false,
|
|
3233
|
+
all: flags.all ?? false
|
|
3128
3234
|
});
|
|
3129
3235
|
} catch (err) {
|
|
3130
3236
|
handleActionError("repo ls", flags.json ?? false, err);
|
|
@@ -3166,6 +3272,20 @@ async function run(argv = process.argv) {
|
|
|
3166
3272
|
handleActionError("repo status", flags.json ?? false, err);
|
|
3167
3273
|
}
|
|
3168
3274
|
});
|
|
3275
|
+
repoCli.command(
|
|
3276
|
+
"rm <id>",
|
|
3277
|
+
"Delete a request, freeing its repo name (admin only)"
|
|
3278
|
+
).option("--json", "Output as JSON").option("--yes", "Skip confirmation prompts (required for non-TTY/CI)").action(async (id, flags) => {
|
|
3279
|
+
try {
|
|
3280
|
+
await rm3({
|
|
3281
|
+
json: flags.json ?? false,
|
|
3282
|
+
id,
|
|
3283
|
+
yes: flags.yes ?? false
|
|
3284
|
+
});
|
|
3285
|
+
} catch (err) {
|
|
3286
|
+
handleActionError("repo rm", flags.json ?? false, err);
|
|
3287
|
+
}
|
|
3288
|
+
});
|
|
3169
3289
|
repoCli.help();
|
|
3170
3290
|
repoCli.version(version);
|
|
3171
3291
|
const knownRepoSubs = /* @__PURE__ */ new Set([
|
|
@@ -3173,7 +3293,8 @@ async function run(argv = process.argv) {
|
|
|
3173
3293
|
"ls",
|
|
3174
3294
|
"approve",
|
|
3175
3295
|
"reject",
|
|
3176
|
-
"status"
|
|
3296
|
+
"status",
|
|
3297
|
+
"rm"
|
|
3177
3298
|
]);
|
|
3178
3299
|
const repoValueFlags = /* @__PURE__ */ new Set([
|
|
3179
3300
|
"--visibility",
|
|
@@ -3328,5 +3449,9 @@ function installFatalHandlers(onFatal = defaultOnFatal) {
|
|
|
3328
3449
|
}
|
|
3329
3450
|
|
|
3330
3451
|
// src/index.ts
|
|
3331
|
-
|
|
3332
|
-
void
|
|
3452
|
+
if (process.env[REFRESH_ENV] === "1") {
|
|
3453
|
+
void runRefreshWorker();
|
|
3454
|
+
} else {
|
|
3455
|
+
installFatalHandlers();
|
|
3456
|
+
void run();
|
|
3457
|
+
}
|