@freecodecamp/universe-cli 0.4.0 → 0.5.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/README.md +71 -55
- package/dist/index.cjs +450 -35
- package/dist/index.js +458 -37
- package/package.json +2 -2
package/dist/index.cjs
CHANGED
|
@@ -8106,10 +8106,7 @@ var EXIT_STORAGE = 13;
|
|
|
8106
8106
|
var EXIT_GIT = 15;
|
|
8107
8107
|
var EXIT_CONFIRM = 18;
|
|
8108
8108
|
var EXIT_PARTIAL = 19;
|
|
8109
|
-
function exitWithCode(code
|
|
8110
|
-
if (message !== void 0) {
|
|
8111
|
-
process.stderr.write(message + "\n");
|
|
8112
|
-
}
|
|
8109
|
+
function exitWithCode(code) {
|
|
8113
8110
|
process.exit(code);
|
|
8114
8111
|
}
|
|
8115
8112
|
|
|
@@ -22241,6 +22238,53 @@ See docs/platform-yaml.md.`
|
|
|
22241
22238
|
return { ok: true, value: result.data };
|
|
22242
22239
|
}
|
|
22243
22240
|
|
|
22241
|
+
// src/lib/similarity.ts
|
|
22242
|
+
function editDistance(a, b) {
|
|
22243
|
+
const m = a.length;
|
|
22244
|
+
const n = b.length;
|
|
22245
|
+
if (m === 0) return n;
|
|
22246
|
+
if (n === 0) return m;
|
|
22247
|
+
const d2 = Array.from(
|
|
22248
|
+
{ length: m + 1 },
|
|
22249
|
+
() => new Array(n + 1).fill(0)
|
|
22250
|
+
);
|
|
22251
|
+
for (let i = 0; i <= m; i++) d2[i][0] = i;
|
|
22252
|
+
for (let j = 0; j <= n; j++) d2[0][j] = j;
|
|
22253
|
+
for (let i = 1; i <= m; i++) {
|
|
22254
|
+
for (let j = 1; j <= n; j++) {
|
|
22255
|
+
const cost = a[i - 1] === b[j - 1] ? 0 : 1;
|
|
22256
|
+
d2[i][j] = Math.min(
|
|
22257
|
+
d2[i - 1][j] + 1,
|
|
22258
|
+
d2[i][j - 1] + 1,
|
|
22259
|
+
d2[i - 1][j - 1] + cost
|
|
22260
|
+
);
|
|
22261
|
+
if (i > 1 && j > 1 && a[i - 1] === b[j - 2] && a[i - 2] === b[j - 1]) {
|
|
22262
|
+
d2[i][j] = Math.min(d2[i][j], d2[i - 2][j - 2] + 1);
|
|
22263
|
+
}
|
|
22264
|
+
}
|
|
22265
|
+
}
|
|
22266
|
+
return d2[m][n];
|
|
22267
|
+
}
|
|
22268
|
+
function suggest(target, candidates, threshold = 2) {
|
|
22269
|
+
if (candidates.length === 0) return null;
|
|
22270
|
+
const lc = target.toLowerCase();
|
|
22271
|
+
const sub = candidates.find((c2) => {
|
|
22272
|
+
const clc = c2.toLowerCase();
|
|
22273
|
+
return clc.includes(lc) || lc.includes(clc);
|
|
22274
|
+
});
|
|
22275
|
+
if (sub) return sub;
|
|
22276
|
+
let best = null;
|
|
22277
|
+
let bestD = threshold + 1;
|
|
22278
|
+
for (const c2 of candidates) {
|
|
22279
|
+
const d2 = editDistance(lc, c2.toLowerCase());
|
|
22280
|
+
if (d2 < bestD) {
|
|
22281
|
+
bestD = d2;
|
|
22282
|
+
best = c2;
|
|
22283
|
+
}
|
|
22284
|
+
}
|
|
22285
|
+
return bestD <= threshold ? best : null;
|
|
22286
|
+
}
|
|
22287
|
+
|
|
22244
22288
|
// src/lib/proxy-client.ts
|
|
22245
22289
|
var ProxyError = class extends CliError {
|
|
22246
22290
|
exitCode;
|
|
@@ -22403,6 +22447,52 @@ function createProxyClient(cfg) {
|
|
|
22403
22447
|
},
|
|
22404
22448
|
body: JSON.stringify({ to: req.to })
|
|
22405
22449
|
});
|
|
22450
|
+
},
|
|
22451
|
+
async registerSite(req) {
|
|
22452
|
+
const body = { slug: req.slug };
|
|
22453
|
+
if (req.teams && req.teams.length > 0) {
|
|
22454
|
+
body.teams = req.teams;
|
|
22455
|
+
}
|
|
22456
|
+
return call(`${base}/api/site/register`, {
|
|
22457
|
+
method: "POST",
|
|
22458
|
+
headers: {
|
|
22459
|
+
Authorization: await userBearer(),
|
|
22460
|
+
Accept: "application/json",
|
|
22461
|
+
"Content-Type": "application/json"
|
|
22462
|
+
},
|
|
22463
|
+
body: JSON.stringify(body)
|
|
22464
|
+
});
|
|
22465
|
+
},
|
|
22466
|
+
async listSites() {
|
|
22467
|
+
return call(`${base}/api/sites`, {
|
|
22468
|
+
method: "GET",
|
|
22469
|
+
headers: {
|
|
22470
|
+
Authorization: await userBearer(),
|
|
22471
|
+
Accept: "application/json"
|
|
22472
|
+
}
|
|
22473
|
+
});
|
|
22474
|
+
},
|
|
22475
|
+
async updateSite(req) {
|
|
22476
|
+
const url2 = `${base}/api/site/${encodeURIComponent(req.slug)}`;
|
|
22477
|
+
return call(url2, {
|
|
22478
|
+
method: "PATCH",
|
|
22479
|
+
headers: {
|
|
22480
|
+
Authorization: await userBearer(),
|
|
22481
|
+
Accept: "application/json",
|
|
22482
|
+
"Content-Type": "application/json"
|
|
22483
|
+
},
|
|
22484
|
+
body: JSON.stringify({ teams: req.teams })
|
|
22485
|
+
});
|
|
22486
|
+
},
|
|
22487
|
+
async deleteSite(req) {
|
|
22488
|
+
const url2 = `${base}/api/site/${encodeURIComponent(req.slug)}`;
|
|
22489
|
+
return call(url2, {
|
|
22490
|
+
method: "DELETE",
|
|
22491
|
+
headers: {
|
|
22492
|
+
Authorization: await userBearer(),
|
|
22493
|
+
Accept: "application/json"
|
|
22494
|
+
}
|
|
22495
|
+
});
|
|
22406
22496
|
}
|
|
22407
22497
|
};
|
|
22408
22498
|
}
|
|
@@ -22597,6 +22687,52 @@ function rethrowProxy(prefix, err) {
|
|
|
22597
22687
|
if (err instanceof Error) throw new StorageError(`${prefix}: ${err.message}`);
|
|
22598
22688
|
throw new StorageError(`${prefix}: ${String(err)}`);
|
|
22599
22689
|
}
|
|
22690
|
+
var PREFLIGHT_INLINE_LIST_CAP = 10;
|
|
22691
|
+
function formatUnauthorizedSiteError(a) {
|
|
22692
|
+
const lines = [
|
|
22693
|
+
`Site '${a.attempted}' is not registered for your GitHub identity.`,
|
|
22694
|
+
``,
|
|
22695
|
+
` You are: ${a.login}`,
|
|
22696
|
+
``
|
|
22697
|
+
];
|
|
22698
|
+
if (a.authorized.length === 0) {
|
|
22699
|
+
lines.push(
|
|
22700
|
+
` Your identity is authorized for no sites yet.`,
|
|
22701
|
+
``,
|
|
22702
|
+
` Likely causes:`,
|
|
22703
|
+
` 1. The '${a.attempted}' slug is not registered.`,
|
|
22704
|
+
` Admin (staff): universe sites register ${a.attempted} --team <team>`,
|
|
22705
|
+
` 2. You are not in any team listed on any registered site.`,
|
|
22706
|
+
` Admin (staff): universe sites update <slug> --team +<your-team>`
|
|
22707
|
+
);
|
|
22708
|
+
return lines.join("\n");
|
|
22709
|
+
}
|
|
22710
|
+
const hint = suggest(a.attempted, a.authorized);
|
|
22711
|
+
if (hint) {
|
|
22712
|
+
lines.push(` Did you mean: ${hint}?`, ``);
|
|
22713
|
+
}
|
|
22714
|
+
lines.push(
|
|
22715
|
+
` Likely causes (most common first):`,
|
|
22716
|
+
` 1. Typo in platform.yaml \`site:\` \u2014 check the spelling above.`,
|
|
22717
|
+
` 2. The '${a.attempted}' slug is not registered yet.`,
|
|
22718
|
+
` Admin (staff): universe sites register ${a.attempted} --team <team>`,
|
|
22719
|
+
` 3. You are not in any team authorized for '${a.attempted}'.`,
|
|
22720
|
+
` Admin (staff): universe sites update ${a.attempted} --team +<your-team>`,
|
|
22721
|
+
``
|
|
22722
|
+
);
|
|
22723
|
+
if (a.authorized.length <= PREFLIGHT_INLINE_LIST_CAP) {
|
|
22724
|
+
lines.push(
|
|
22725
|
+
` Your authorized sites (${a.authorized.length}):`,
|
|
22726
|
+
...[...a.authorized].sort().map((s) => ` - ${s}`)
|
|
22727
|
+
);
|
|
22728
|
+
} else {
|
|
22729
|
+
lines.push(
|
|
22730
|
+
` You have ${a.authorized.length} authorized sites \u2014 too many to inline.`,
|
|
22731
|
+
` Run \`universe sites ls --mine\` to inspect the full list.`
|
|
22732
|
+
);
|
|
22733
|
+
}
|
|
22734
|
+
return lines.join("\n");
|
|
22735
|
+
}
|
|
22600
22736
|
async function deploy(options, deps = {}) {
|
|
22601
22737
|
const cwd = deps.cwd ?? process.cwd();
|
|
22602
22738
|
const env = deps.env ?? process.env;
|
|
@@ -22632,22 +22768,12 @@ async function deploy(options, deps = {}) {
|
|
|
22632
22768
|
rethrowProxy("whoami preflight failed", err);
|
|
22633
22769
|
}
|
|
22634
22770
|
if (!me2.authorizedSites.includes(config2.site)) {
|
|
22635
|
-
const sitesLine = me2.authorizedSites.length > 0 ? me2.authorizedSites.join(", ") : "(no sites authorized)";
|
|
22636
22771
|
throw new CredentialError(
|
|
22637
|
-
|
|
22638
|
-
|
|
22639
|
-
|
|
22640
|
-
|
|
22641
|
-
|
|
22642
|
-
``,
|
|
22643
|
-
`Likely causes (most common first):`,
|
|
22644
|
-
` 1. Platform admin has not added '${config2.site}' to artemis`,
|
|
22645
|
-
` 'config/sites.yaml' yet (one-time, per site).`,
|
|
22646
|
-
` 2. You are not in any GitHub team listed for '${config2.site}'.`,
|
|
22647
|
-
``,
|
|
22648
|
-
`Runbook:`,
|
|
22649
|
-
` https://github.com/freeCodeCamp/infra/blob/main/docs/runbooks/01-deploy-new-constellation-site.md`
|
|
22650
|
-
].join("\n")
|
|
22772
|
+
formatUnauthorizedSiteError({
|
|
22773
|
+
attempted: config2.site,
|
|
22774
|
+
login: me2.login,
|
|
22775
|
+
authorized: me2.authorizedSites
|
|
22776
|
+
})
|
|
22651
22777
|
);
|
|
22652
22778
|
}
|
|
22653
22779
|
const git = gitState();
|
|
@@ -22758,7 +22884,7 @@ async function deploy(options, deps = {}) {
|
|
|
22758
22884
|
} else {
|
|
22759
22885
|
error48(message);
|
|
22760
22886
|
}
|
|
22761
|
-
exit(code
|
|
22887
|
+
exit(code);
|
|
22762
22888
|
}
|
|
22763
22889
|
}
|
|
22764
22890
|
|
|
@@ -22866,7 +22992,7 @@ async function login(options, deps = {}) {
|
|
|
22866
22992
|
} else {
|
|
22867
22993
|
error48(msg);
|
|
22868
22994
|
}
|
|
22869
|
-
exit(EXIT_CONFIRM
|
|
22995
|
+
exit(EXIT_CONFIRM);
|
|
22870
22996
|
return;
|
|
22871
22997
|
}
|
|
22872
22998
|
}
|
|
@@ -22909,7 +23035,7 @@ async function login(options, deps = {}) {
|
|
|
22909
23035
|
} else {
|
|
22910
23036
|
error48(message);
|
|
22911
23037
|
}
|
|
22912
|
-
exit(EXIT_CREDENTIALS
|
|
23038
|
+
exit(EXIT_CREDENTIALS);
|
|
22913
23039
|
return;
|
|
22914
23040
|
}
|
|
22915
23041
|
await save(token);
|
|
@@ -23043,7 +23169,7 @@ async function ls(options, deps = {}) {
|
|
|
23043
23169
|
} else {
|
|
23044
23170
|
error48(message);
|
|
23045
23171
|
}
|
|
23046
|
-
exit(code
|
|
23172
|
+
exit(code);
|
|
23047
23173
|
}
|
|
23048
23174
|
}
|
|
23049
23175
|
|
|
@@ -23130,7 +23256,7 @@ async function promote(options, deps = {}) {
|
|
|
23130
23256
|
} else {
|
|
23131
23257
|
error48(message);
|
|
23132
23258
|
}
|
|
23133
|
-
exit(code
|
|
23259
|
+
exit(code);
|
|
23134
23260
|
}
|
|
23135
23261
|
}
|
|
23136
23262
|
|
|
@@ -23217,7 +23343,7 @@ async function rollback(options, deps = {}) {
|
|
|
23217
23343
|
} else {
|
|
23218
23344
|
error48(message);
|
|
23219
23345
|
}
|
|
23220
|
-
exit(code
|
|
23346
|
+
exit(code);
|
|
23221
23347
|
}
|
|
23222
23348
|
}
|
|
23223
23349
|
|
|
@@ -23241,7 +23367,7 @@ async function whoami(options, deps = {}) {
|
|
|
23241
23367
|
} else {
|
|
23242
23368
|
error48(msg);
|
|
23243
23369
|
}
|
|
23244
|
-
exit(EXIT_CREDENTIALS
|
|
23370
|
+
exit(EXIT_CREDENTIALS);
|
|
23245
23371
|
return;
|
|
23246
23372
|
}
|
|
23247
23373
|
const baseUrl = env["UNIVERSE_PROXY_URL"] ?? DEFAULT_PROXY_URL2;
|
|
@@ -23251,21 +23377,22 @@ async function whoami(options, deps = {}) {
|
|
|
23251
23377
|
});
|
|
23252
23378
|
try {
|
|
23253
23379
|
const result = await client.whoami();
|
|
23380
|
+
const count = result.authorizedSites.length;
|
|
23254
23381
|
if (options.json) {
|
|
23255
23382
|
emitJson7(
|
|
23256
23383
|
buildEnvelope("whoami", true, {
|
|
23257
23384
|
login: result.login,
|
|
23258
|
-
|
|
23259
|
-
|
|
23385
|
+
identitySource: identity.source,
|
|
23386
|
+
authorizedSitesCount: count
|
|
23260
23387
|
})
|
|
23261
23388
|
);
|
|
23262
23389
|
} else {
|
|
23263
|
-
const
|
|
23390
|
+
const sitesLine = count === 0 ? "Authorized for 0 sites." : `Authorized for ${count} site${count === 1 ? "" : "s"} \u2014 run \`universe sites ls --mine\``;
|
|
23264
23391
|
success2(
|
|
23265
23392
|
[
|
|
23266
23393
|
`Logged in as: ${result.login}`,
|
|
23267
|
-
`
|
|
23268
|
-
|
|
23394
|
+
`Identity source: ${identity.source}`,
|
|
23395
|
+
sitesLine
|
|
23269
23396
|
].join("\n")
|
|
23270
23397
|
);
|
|
23271
23398
|
}
|
|
@@ -23277,7 +23404,226 @@ async function whoami(options, deps = {}) {
|
|
|
23277
23404
|
} else {
|
|
23278
23405
|
error48(message);
|
|
23279
23406
|
}
|
|
23280
|
-
exit(exitCode
|
|
23407
|
+
exit(exitCode);
|
|
23408
|
+
}
|
|
23409
|
+
}
|
|
23410
|
+
|
|
23411
|
+
// src/commands/sites/_shared.ts
|
|
23412
|
+
function emitJson8(envelope) {
|
|
23413
|
+
process.stdout.write(JSON.stringify(envelope) + "\n");
|
|
23414
|
+
}
|
|
23415
|
+
function parseTeamsFlag(raw) {
|
|
23416
|
+
if (raw === void 0 || raw === null) return [];
|
|
23417
|
+
const tokens = Array.isArray(raw) ? raw : [raw];
|
|
23418
|
+
return tokens.flatMap((s) => s.split(",")).map((s) => s.trim()).filter((s) => s.length > 0);
|
|
23419
|
+
}
|
|
23420
|
+
async function setupClient(deps) {
|
|
23421
|
+
const env = deps.env ?? process.env;
|
|
23422
|
+
const resolveId = deps.resolveIdentity ?? resolveIdentity;
|
|
23423
|
+
const mkClient = deps.createProxyClient ?? createProxyClient;
|
|
23424
|
+
const identity = await resolveId({ env });
|
|
23425
|
+
if (!identity) {
|
|
23426
|
+
throw new CredentialError(
|
|
23427
|
+
"No GitHub identity available. Run `universe login`, set $GITHUB_TOKEN, or install the gh CLI."
|
|
23428
|
+
);
|
|
23429
|
+
}
|
|
23430
|
+
const baseUrl = env["UNIVERSE_PROXY_URL"] ?? DEFAULT_PROXY_URL;
|
|
23431
|
+
const client = mkClient({
|
|
23432
|
+
baseUrl,
|
|
23433
|
+
getAuthToken: () => identity.token
|
|
23434
|
+
});
|
|
23435
|
+
return { client, identitySource: identity.source };
|
|
23436
|
+
}
|
|
23437
|
+
|
|
23438
|
+
// src/commands/sites/ls.ts
|
|
23439
|
+
function formatTable2(rows) {
|
|
23440
|
+
if (rows.length === 0) return "No registered sites.";
|
|
23441
|
+
const headers = ["SLUG", "TEAMS", "CREATED BY", "CREATED AT"];
|
|
23442
|
+
const cells = rows.map((r) => [
|
|
23443
|
+
r.slug,
|
|
23444
|
+
r.teams.join(","),
|
|
23445
|
+
r.createdBy,
|
|
23446
|
+
r.createdAt
|
|
23447
|
+
]);
|
|
23448
|
+
const widths = headers.map(
|
|
23449
|
+
(h, i) => Math.max(h.length, ...cells.map((row) => row[i]?.length ?? 0))
|
|
23450
|
+
);
|
|
23451
|
+
const fmt = (row) => row.map((c2, i) => c2.padEnd(widths[i] ?? 0)).join(" ");
|
|
23452
|
+
return [fmt(headers), ...cells.map(fmt)].join("\n");
|
|
23453
|
+
}
|
|
23454
|
+
async function ls2(options, deps = {}) {
|
|
23455
|
+
const command = "sites ls";
|
|
23456
|
+
const success2 = deps.logSuccess ?? ((s) => O2.message(s));
|
|
23457
|
+
const error48 = deps.logError ?? ((s) => O2.error(s));
|
|
23458
|
+
const exit = deps.exit ?? exitWithCode;
|
|
23459
|
+
try {
|
|
23460
|
+
const { client } = await setupClient(deps);
|
|
23461
|
+
let rows = await client.listSites();
|
|
23462
|
+
let scope = "all";
|
|
23463
|
+
if (options.mine) {
|
|
23464
|
+
const me2 = await client.whoami();
|
|
23465
|
+
const allowed = new Set(me2.authorizedSites);
|
|
23466
|
+
rows = rows.filter((r) => allowed.has(r.slug));
|
|
23467
|
+
scope = "mine";
|
|
23468
|
+
}
|
|
23469
|
+
if (options.json) {
|
|
23470
|
+
emitJson8(
|
|
23471
|
+
buildEnvelope(command, true, {
|
|
23472
|
+
count: rows.length,
|
|
23473
|
+
scope,
|
|
23474
|
+
sites: rows
|
|
23475
|
+
})
|
|
23476
|
+
);
|
|
23477
|
+
} else {
|
|
23478
|
+
success2(formatTable2(rows));
|
|
23479
|
+
}
|
|
23480
|
+
} catch (err) {
|
|
23481
|
+
const { code, message } = wrapProxyError(command, err);
|
|
23482
|
+
if (options.json) {
|
|
23483
|
+
emitJson8(buildErrorEnvelope(command, code, message));
|
|
23484
|
+
} else {
|
|
23485
|
+
error48(message);
|
|
23486
|
+
}
|
|
23487
|
+
exit(code);
|
|
23488
|
+
}
|
|
23489
|
+
}
|
|
23490
|
+
|
|
23491
|
+
// src/commands/sites/register.ts
|
|
23492
|
+
async function register(options, deps = {}) {
|
|
23493
|
+
const command = "sites register";
|
|
23494
|
+
const success2 = deps.logSuccess ?? ((s) => O2.success(s));
|
|
23495
|
+
const error48 = deps.logError ?? ((s) => O2.error(s));
|
|
23496
|
+
const exit = deps.exit ?? exitWithCode;
|
|
23497
|
+
try {
|
|
23498
|
+
if (!options.slug || options.slug.trim().length === 0) {
|
|
23499
|
+
throw new UsageError("slug is required (positional argument)");
|
|
23500
|
+
}
|
|
23501
|
+
const teams = parseTeamsFlag(options.team);
|
|
23502
|
+
const { client } = await setupClient(deps);
|
|
23503
|
+
const row = await client.registerSite({
|
|
23504
|
+
slug: options.slug,
|
|
23505
|
+
teams: teams.length > 0 ? teams : void 0
|
|
23506
|
+
});
|
|
23507
|
+
if (options.json) {
|
|
23508
|
+
emitJson8(
|
|
23509
|
+
buildEnvelope(command, true, {
|
|
23510
|
+
slug: row.slug,
|
|
23511
|
+
teams: row.teams,
|
|
23512
|
+
createdAt: row.createdAt,
|
|
23513
|
+
createdBy: row.createdBy
|
|
23514
|
+
})
|
|
23515
|
+
);
|
|
23516
|
+
} else {
|
|
23517
|
+
success2(
|
|
23518
|
+
[
|
|
23519
|
+
`Registered ${row.slug}`,
|
|
23520
|
+
``,
|
|
23521
|
+
` Slug: ${row.slug}`,
|
|
23522
|
+
` Teams: ${row.teams.join(", ")}`,
|
|
23523
|
+
` Created by: ${row.createdBy}`,
|
|
23524
|
+
` Created at: ${row.createdAt}`
|
|
23525
|
+
].join("\n")
|
|
23526
|
+
);
|
|
23527
|
+
}
|
|
23528
|
+
} catch (err) {
|
|
23529
|
+
const { code, message } = wrapProxyError(command, err);
|
|
23530
|
+
if (options.json) {
|
|
23531
|
+
emitJson8(buildErrorEnvelope(command, code, message));
|
|
23532
|
+
} else {
|
|
23533
|
+
error48(message);
|
|
23534
|
+
}
|
|
23535
|
+
exit(code);
|
|
23536
|
+
}
|
|
23537
|
+
}
|
|
23538
|
+
|
|
23539
|
+
// src/commands/sites/rm.ts
|
|
23540
|
+
async function rm2(options, deps = {}) {
|
|
23541
|
+
const command = "sites rm";
|
|
23542
|
+
const success2 = deps.logSuccess ?? ((s) => O2.success(s));
|
|
23543
|
+
const error48 = deps.logError ?? ((s) => O2.error(s));
|
|
23544
|
+
const exit = deps.exit ?? exitWithCode;
|
|
23545
|
+
try {
|
|
23546
|
+
if (!options.slug || options.slug.trim().length === 0) {
|
|
23547
|
+
throw new UsageError("slug is required (positional argument)");
|
|
23548
|
+
}
|
|
23549
|
+
const { client } = await setupClient(deps);
|
|
23550
|
+
await client.deleteSite({ slug: options.slug });
|
|
23551
|
+
if (options.json) {
|
|
23552
|
+
emitJson8(
|
|
23553
|
+
buildEnvelope(command, true, {
|
|
23554
|
+
slug: options.slug,
|
|
23555
|
+
deleted: true
|
|
23556
|
+
})
|
|
23557
|
+
);
|
|
23558
|
+
} else {
|
|
23559
|
+
success2(
|
|
23560
|
+
[
|
|
23561
|
+
`Deleted ${options.slug}`,
|
|
23562
|
+
``,
|
|
23563
|
+
` Note: R2 deploy bytes are NOT removed; they age out via the`,
|
|
23564
|
+
` post-GA cleanup cron.`
|
|
23565
|
+
].join("\n")
|
|
23566
|
+
);
|
|
23567
|
+
}
|
|
23568
|
+
} catch (err) {
|
|
23569
|
+
const { code, message } = wrapProxyError(command, err);
|
|
23570
|
+
if (options.json) {
|
|
23571
|
+
emitJson8(buildErrorEnvelope(command, code, message));
|
|
23572
|
+
} else {
|
|
23573
|
+
error48(message);
|
|
23574
|
+
}
|
|
23575
|
+
exit(code);
|
|
23576
|
+
}
|
|
23577
|
+
}
|
|
23578
|
+
|
|
23579
|
+
// src/commands/sites/update.ts
|
|
23580
|
+
async function update(options, deps = {}) {
|
|
23581
|
+
const command = "sites update";
|
|
23582
|
+
const success2 = deps.logSuccess ?? ((s) => O2.success(s));
|
|
23583
|
+
const error48 = deps.logError ?? ((s) => O2.error(s));
|
|
23584
|
+
const exit = deps.exit ?? exitWithCode;
|
|
23585
|
+
try {
|
|
23586
|
+
if (!options.slug || options.slug.trim().length === 0) {
|
|
23587
|
+
throw new UsageError("slug is required (positional argument)");
|
|
23588
|
+
}
|
|
23589
|
+
const teams = parseTeamsFlag(options.team);
|
|
23590
|
+
if (teams.length === 0) {
|
|
23591
|
+
throw new UsageError(
|
|
23592
|
+
"--team is required with at least one slug; use `sites rm` to remove a site"
|
|
23593
|
+
);
|
|
23594
|
+
}
|
|
23595
|
+
const { client } = await setupClient(deps);
|
|
23596
|
+
const row = await client.updateSite({
|
|
23597
|
+
slug: options.slug,
|
|
23598
|
+
teams
|
|
23599
|
+
});
|
|
23600
|
+
if (options.json) {
|
|
23601
|
+
emitJson8(
|
|
23602
|
+
buildEnvelope(command, true, {
|
|
23603
|
+
slug: row.slug,
|
|
23604
|
+
teams: row.teams,
|
|
23605
|
+
updatedAt: row.updatedAt
|
|
23606
|
+
})
|
|
23607
|
+
);
|
|
23608
|
+
} else {
|
|
23609
|
+
success2(
|
|
23610
|
+
[
|
|
23611
|
+
`Updated ${row.slug}`,
|
|
23612
|
+
``,
|
|
23613
|
+
` Slug: ${row.slug}`,
|
|
23614
|
+
` Teams: ${row.teams.join(", ")}`,
|
|
23615
|
+
` Updated at: ${row.updatedAt}`
|
|
23616
|
+
].join("\n")
|
|
23617
|
+
);
|
|
23618
|
+
}
|
|
23619
|
+
} catch (err) {
|
|
23620
|
+
const { code, message } = wrapProxyError(command, err);
|
|
23621
|
+
if (options.json) {
|
|
23622
|
+
emitJson8(buildErrorEnvelope(command, code, message));
|
|
23623
|
+
} else {
|
|
23624
|
+
error48(message);
|
|
23625
|
+
}
|
|
23626
|
+
exit(code);
|
|
23281
23627
|
}
|
|
23282
23628
|
}
|
|
23283
23629
|
|
|
@@ -23343,13 +23689,13 @@ function outputError(ctx, code, message, issues) {
|
|
|
23343
23689
|
}
|
|
23344
23690
|
|
|
23345
23691
|
// src/cli.ts
|
|
23346
|
-
var version2 = true ? "0.
|
|
23692
|
+
var version2 = true ? "0.5.0" : "0.0.0";
|
|
23347
23693
|
function handleActionError(command, json2, err) {
|
|
23348
23694
|
const ctx = { json: json2, command };
|
|
23349
23695
|
const message = err instanceof Error ? err.message : "unknown error";
|
|
23350
23696
|
const code = err instanceof CliError ? err.exitCode : EXIT_USAGE;
|
|
23351
23697
|
outputError(ctx, code, message);
|
|
23352
|
-
exitWithCode(code
|
|
23698
|
+
exitWithCode(code);
|
|
23353
23699
|
}
|
|
23354
23700
|
function findFirstPositional(args) {
|
|
23355
23701
|
for (let i = 0; i < args.length; i += 1) {
|
|
@@ -23361,7 +23707,75 @@ function findFirstPositional(args) {
|
|
|
23361
23707
|
function run(argv = process.argv) {
|
|
23362
23708
|
const args = argv.slice(2);
|
|
23363
23709
|
const firstPosIdx = findFirstPositional(args);
|
|
23364
|
-
const
|
|
23710
|
+
const namespace = firstPosIdx >= 0 ? args[firstPosIdx] : void 0;
|
|
23711
|
+
const isStatic = namespace === "static";
|
|
23712
|
+
const isSites = namespace === "sites";
|
|
23713
|
+
if (isSites) {
|
|
23714
|
+
const sitesArgs = [
|
|
23715
|
+
...args.slice(0, firstPosIdx),
|
|
23716
|
+
...args.slice(firstPosIdx + 1)
|
|
23717
|
+
];
|
|
23718
|
+
const sitesCli = cac("universe sites");
|
|
23719
|
+
sitesCli.command("register <slug>", "Register a new static site (staff only)").option("--json", "Output as JSON").option(
|
|
23720
|
+
"--team <name>",
|
|
23721
|
+
"GitHub team slug (repeatable, or comma-separated). Defaults to staff."
|
|
23722
|
+
).action(
|
|
23723
|
+
async (slug, flags) => {
|
|
23724
|
+
try {
|
|
23725
|
+
await register({
|
|
23726
|
+
json: flags.json ?? false,
|
|
23727
|
+
slug,
|
|
23728
|
+
team: flags.team
|
|
23729
|
+
});
|
|
23730
|
+
} catch (err) {
|
|
23731
|
+
handleActionError("sites register", flags.json ?? false, err);
|
|
23732
|
+
}
|
|
23733
|
+
}
|
|
23734
|
+
);
|
|
23735
|
+
sitesCli.command("ls", "List sites in the registry").option("--json", "Output as JSON").option(
|
|
23736
|
+
"--mine",
|
|
23737
|
+
"Filter to sites your GitHub identity is authorized for"
|
|
23738
|
+
).action(async (flags) => {
|
|
23739
|
+
try {
|
|
23740
|
+
await ls2({
|
|
23741
|
+
json: flags.json ?? false,
|
|
23742
|
+
mine: flags.mine ?? false
|
|
23743
|
+
});
|
|
23744
|
+
} catch (err) {
|
|
23745
|
+
handleActionError("sites ls", flags.json ?? false, err);
|
|
23746
|
+
}
|
|
23747
|
+
});
|
|
23748
|
+
sitesCli.command(
|
|
23749
|
+
"update <slug>",
|
|
23750
|
+
"Replace the teams list for an existing site (staff only)"
|
|
23751
|
+
).option("--json", "Output as JSON").option(
|
|
23752
|
+
"--team <name>",
|
|
23753
|
+
"GitHub team slug (repeatable, or comma-separated). Required."
|
|
23754
|
+
).action(
|
|
23755
|
+
async (slug, flags) => {
|
|
23756
|
+
try {
|
|
23757
|
+
await update({
|
|
23758
|
+
json: flags.json ?? false,
|
|
23759
|
+
slug,
|
|
23760
|
+
team: flags.team
|
|
23761
|
+
});
|
|
23762
|
+
} catch (err) {
|
|
23763
|
+
handleActionError("sites update", flags.json ?? false, err);
|
|
23764
|
+
}
|
|
23765
|
+
}
|
|
23766
|
+
);
|
|
23767
|
+
sitesCli.command("rm <slug>", "Remove a site from the registry (staff only)").option("--json", "Output as JSON").action(async (slug, flags) => {
|
|
23768
|
+
try {
|
|
23769
|
+
await rm2({ json: flags.json ?? false, slug });
|
|
23770
|
+
} catch (err) {
|
|
23771
|
+
handleActionError("sites rm", flags.json ?? false, err);
|
|
23772
|
+
}
|
|
23773
|
+
});
|
|
23774
|
+
sitesCli.help();
|
|
23775
|
+
sitesCli.version(version2);
|
|
23776
|
+
sitesCli.parse(["node", "universe-sites", ...sitesArgs]);
|
|
23777
|
+
return;
|
|
23778
|
+
}
|
|
23365
23779
|
if (isStatic) {
|
|
23366
23780
|
const staticArgs = [
|
|
23367
23781
|
...args.slice(0, firstPosIdx),
|
|
@@ -23444,6 +23858,7 @@ function run(argv = process.argv) {
|
|
|
23444
23858
|
}
|
|
23445
23859
|
});
|
|
23446
23860
|
cli.command("static <subcommand>", "Static site deployment commands");
|
|
23861
|
+
cli.command("sites <subcommand>", "Static site registry commands");
|
|
23447
23862
|
cli.help();
|
|
23448
23863
|
cli.version(version2);
|
|
23449
23864
|
cli.parse(argv);
|