@freecodecamp/universe-cli 0.4.0 → 0.5.1
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 +69 -67
- package/dist/index.cjs +468 -48
- package/dist/index.js +476 -50
- package/package.json +3 -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,26 +22768,16 @@ 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();
|
|
22654
|
-
if (git.dirty) {
|
|
22780
|
+
if (git.dirty && !options.json) {
|
|
22655
22781
|
warn(
|
|
22656
22782
|
"git working tree is dirty \u2014 uncommitted changes will not be reflected."
|
|
22657
22783
|
);
|
|
@@ -22663,7 +22789,7 @@ async function deploy(options, deps = {}) {
|
|
|
22663
22789
|
cwd,
|
|
22664
22790
|
outputDir
|
|
22665
22791
|
});
|
|
22666
|
-
if (buildResult.skipped) {
|
|
22792
|
+
if (buildResult.skipped && !options.json) {
|
|
22667
22793
|
info("build.command not set \u2014 using pre-built output.");
|
|
22668
22794
|
}
|
|
22669
22795
|
const resolvedOutputDir = buildResult.outputDir;
|
|
@@ -22722,7 +22848,7 @@ async function deploy(options, deps = {}) {
|
|
|
22722
22848
|
);
|
|
22723
22849
|
} else {
|
|
22724
22850
|
const sizeKB = (uploadResult.totalSize / 1024).toFixed(1);
|
|
22725
|
-
const nextLine = mode === "preview" ?
|
|
22851
|
+
const nextLine = mode === "preview" ? `Next: universe static promote --from ${finalizeResult.deployId}` : "Promoted to production.\nPreview alias unchanged.";
|
|
22726
22852
|
success2(
|
|
22727
22853
|
[
|
|
22728
22854
|
`Deployed ${finalizeResult.deployId}`,
|
|
@@ -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);
|
|
@@ -23020,7 +23146,10 @@ async function ls(options, deps = {}) {
|
|
|
23020
23146
|
getAuthToken: () => identity.token
|
|
23021
23147
|
});
|
|
23022
23148
|
const raw = await client.siteDeploys({ site });
|
|
23023
|
-
const
|
|
23149
|
+
const sorted = [...raw].sort(
|
|
23150
|
+
(a, b) => b.deployId.localeCompare(a.deployId)
|
|
23151
|
+
);
|
|
23152
|
+
const deploys = sorted.map((d2) => parseDeployId(d2.deployId));
|
|
23024
23153
|
if (options.json) {
|
|
23025
23154
|
emitJson4(
|
|
23026
23155
|
buildEnvelope("ls", true, {
|
|
@@ -23043,7 +23172,7 @@ async function ls(options, deps = {}) {
|
|
|
23043
23172
|
} else {
|
|
23044
23173
|
error48(message);
|
|
23045
23174
|
}
|
|
23046
|
-
exit(code
|
|
23175
|
+
exit(code);
|
|
23047
23176
|
}
|
|
23048
23177
|
}
|
|
23049
23178
|
|
|
@@ -23113,15 +23242,17 @@ async function promote(options, deps = {}) {
|
|
|
23113
23242
|
})
|
|
23114
23243
|
);
|
|
23115
23244
|
} else {
|
|
23116
|
-
|
|
23117
|
-
|
|
23118
|
-
|
|
23119
|
-
|
|
23120
|
-
|
|
23121
|
-
|
|
23122
|
-
|
|
23123
|
-
|
|
23124
|
-
|
|
23245
|
+
const lines = [
|
|
23246
|
+
`Promoted ${result.deployId} to production`,
|
|
23247
|
+
``,
|
|
23248
|
+
` Site: ${config2.site}`,
|
|
23249
|
+
` Deploy: ${result.deployId}`,
|
|
23250
|
+
` Production: ${result.url}`
|
|
23251
|
+
];
|
|
23252
|
+
if (options.from) {
|
|
23253
|
+
lines.push(``, "Preview alias unchanged.");
|
|
23254
|
+
}
|
|
23255
|
+
success2(lines.join("\n"));
|
|
23125
23256
|
}
|
|
23126
23257
|
} catch (err) {
|
|
23127
23258
|
const { code, message } = wrapProxyError("promote", err);
|
|
@@ -23130,7 +23261,7 @@ async function promote(options, deps = {}) {
|
|
|
23130
23261
|
} else {
|
|
23131
23262
|
error48(message);
|
|
23132
23263
|
}
|
|
23133
|
-
exit(code
|
|
23264
|
+
exit(code);
|
|
23134
23265
|
}
|
|
23135
23266
|
}
|
|
23136
23267
|
|
|
@@ -23217,7 +23348,7 @@ async function rollback(options, deps = {}) {
|
|
|
23217
23348
|
} else {
|
|
23218
23349
|
error48(message);
|
|
23219
23350
|
}
|
|
23220
|
-
exit(code
|
|
23351
|
+
exit(code);
|
|
23221
23352
|
}
|
|
23222
23353
|
}
|
|
23223
23354
|
|
|
@@ -23241,7 +23372,7 @@ async function whoami(options, deps = {}) {
|
|
|
23241
23372
|
} else {
|
|
23242
23373
|
error48(msg);
|
|
23243
23374
|
}
|
|
23244
|
-
exit(EXIT_CREDENTIALS
|
|
23375
|
+
exit(EXIT_CREDENTIALS);
|
|
23245
23376
|
return;
|
|
23246
23377
|
}
|
|
23247
23378
|
const baseUrl = env["UNIVERSE_PROXY_URL"] ?? DEFAULT_PROXY_URL2;
|
|
@@ -23251,21 +23382,22 @@ async function whoami(options, deps = {}) {
|
|
|
23251
23382
|
});
|
|
23252
23383
|
try {
|
|
23253
23384
|
const result = await client.whoami();
|
|
23385
|
+
const count = result.authorizedSites.length;
|
|
23254
23386
|
if (options.json) {
|
|
23255
23387
|
emitJson7(
|
|
23256
23388
|
buildEnvelope("whoami", true, {
|
|
23257
23389
|
login: result.login,
|
|
23258
|
-
|
|
23259
|
-
|
|
23390
|
+
identitySource: identity.source,
|
|
23391
|
+
authorizedSitesCount: count
|
|
23260
23392
|
})
|
|
23261
23393
|
);
|
|
23262
23394
|
} else {
|
|
23263
|
-
const
|
|
23395
|
+
const sitesLine = count === 0 ? "Authorized for 0 sites." : `Authorized for ${count} site${count === 1 ? "" : "s"} \u2014 run \`universe sites ls --mine\``;
|
|
23264
23396
|
success2(
|
|
23265
23397
|
[
|
|
23266
23398
|
`Logged in as: ${result.login}`,
|
|
23267
|
-
`
|
|
23268
|
-
|
|
23399
|
+
`Identity source: ${identity.source}`,
|
|
23400
|
+
sitesLine
|
|
23269
23401
|
].join("\n")
|
|
23270
23402
|
);
|
|
23271
23403
|
}
|
|
@@ -23277,7 +23409,226 @@ async function whoami(options, deps = {}) {
|
|
|
23277
23409
|
} else {
|
|
23278
23410
|
error48(message);
|
|
23279
23411
|
}
|
|
23280
|
-
exit(exitCode
|
|
23412
|
+
exit(exitCode);
|
|
23413
|
+
}
|
|
23414
|
+
}
|
|
23415
|
+
|
|
23416
|
+
// src/commands/sites/_shared.ts
|
|
23417
|
+
function emitJson8(envelope) {
|
|
23418
|
+
process.stdout.write(JSON.stringify(envelope) + "\n");
|
|
23419
|
+
}
|
|
23420
|
+
function parseTeamsFlag(raw) {
|
|
23421
|
+
if (raw === void 0 || raw === null) return [];
|
|
23422
|
+
const tokens = Array.isArray(raw) ? raw : [raw];
|
|
23423
|
+
return tokens.flatMap((s) => s.split(",")).map((s) => s.trim()).filter((s) => s.length > 0);
|
|
23424
|
+
}
|
|
23425
|
+
async function setupClient(deps) {
|
|
23426
|
+
const env = deps.env ?? process.env;
|
|
23427
|
+
const resolveId = deps.resolveIdentity ?? resolveIdentity;
|
|
23428
|
+
const mkClient = deps.createProxyClient ?? createProxyClient;
|
|
23429
|
+
const identity = await resolveId({ env });
|
|
23430
|
+
if (!identity) {
|
|
23431
|
+
throw new CredentialError(
|
|
23432
|
+
"No GitHub identity available. Run `universe login`, set $GITHUB_TOKEN, or install the gh CLI."
|
|
23433
|
+
);
|
|
23434
|
+
}
|
|
23435
|
+
const baseUrl = env["UNIVERSE_PROXY_URL"] ?? DEFAULT_PROXY_URL;
|
|
23436
|
+
const client = mkClient({
|
|
23437
|
+
baseUrl,
|
|
23438
|
+
getAuthToken: () => identity.token
|
|
23439
|
+
});
|
|
23440
|
+
return { client, identitySource: identity.source };
|
|
23441
|
+
}
|
|
23442
|
+
|
|
23443
|
+
// src/commands/sites/ls.ts
|
|
23444
|
+
function formatTable2(rows) {
|
|
23445
|
+
if (rows.length === 0) return "No registered sites.";
|
|
23446
|
+
const headers = ["SLUG", "TEAMS", "CREATED BY", "CREATED AT"];
|
|
23447
|
+
const cells = rows.map((r) => [
|
|
23448
|
+
r.slug,
|
|
23449
|
+
r.teams.join(","),
|
|
23450
|
+
r.createdBy,
|
|
23451
|
+
r.createdAt
|
|
23452
|
+
]);
|
|
23453
|
+
const widths = headers.map(
|
|
23454
|
+
(h, i) => Math.max(h.length, ...cells.map((row) => row[i]?.length ?? 0))
|
|
23455
|
+
);
|
|
23456
|
+
const fmt = (row) => row.map((c2, i) => c2.padEnd(widths[i] ?? 0)).join(" ");
|
|
23457
|
+
return [fmt(headers), ...cells.map(fmt)].join("\n");
|
|
23458
|
+
}
|
|
23459
|
+
async function ls2(options, deps = {}) {
|
|
23460
|
+
const command = "sites ls";
|
|
23461
|
+
const success2 = deps.logSuccess ?? ((s) => O2.message(s));
|
|
23462
|
+
const error48 = deps.logError ?? ((s) => O2.error(s));
|
|
23463
|
+
const exit = deps.exit ?? exitWithCode;
|
|
23464
|
+
try {
|
|
23465
|
+
const { client } = await setupClient(deps);
|
|
23466
|
+
let rows = await client.listSites();
|
|
23467
|
+
let scope = "all";
|
|
23468
|
+
if (options.mine) {
|
|
23469
|
+
const me2 = await client.whoami();
|
|
23470
|
+
const allowed = new Set(me2.authorizedSites);
|
|
23471
|
+
rows = rows.filter((r) => allowed.has(r.slug));
|
|
23472
|
+
scope = "mine";
|
|
23473
|
+
}
|
|
23474
|
+
if (options.json) {
|
|
23475
|
+
emitJson8(
|
|
23476
|
+
buildEnvelope(command, true, {
|
|
23477
|
+
count: rows.length,
|
|
23478
|
+
scope,
|
|
23479
|
+
sites: rows
|
|
23480
|
+
})
|
|
23481
|
+
);
|
|
23482
|
+
} else {
|
|
23483
|
+
success2(formatTable2(rows));
|
|
23484
|
+
}
|
|
23485
|
+
} catch (err) {
|
|
23486
|
+
const { code, message } = wrapProxyError(command, err);
|
|
23487
|
+
if (options.json) {
|
|
23488
|
+
emitJson8(buildErrorEnvelope(command, code, message));
|
|
23489
|
+
} else {
|
|
23490
|
+
error48(message);
|
|
23491
|
+
}
|
|
23492
|
+
exit(code);
|
|
23493
|
+
}
|
|
23494
|
+
}
|
|
23495
|
+
|
|
23496
|
+
// src/commands/sites/register.ts
|
|
23497
|
+
async function register(options, deps = {}) {
|
|
23498
|
+
const command = "sites register";
|
|
23499
|
+
const success2 = deps.logSuccess ?? ((s) => O2.success(s));
|
|
23500
|
+
const error48 = deps.logError ?? ((s) => O2.error(s));
|
|
23501
|
+
const exit = deps.exit ?? exitWithCode;
|
|
23502
|
+
try {
|
|
23503
|
+
if (!options.slug || options.slug.trim().length === 0) {
|
|
23504
|
+
throw new UsageError("slug is required (positional argument)");
|
|
23505
|
+
}
|
|
23506
|
+
const teams = parseTeamsFlag(options.team);
|
|
23507
|
+
const { client } = await setupClient(deps);
|
|
23508
|
+
const row = await client.registerSite({
|
|
23509
|
+
slug: options.slug,
|
|
23510
|
+
teams: teams.length > 0 ? teams : void 0
|
|
23511
|
+
});
|
|
23512
|
+
if (options.json) {
|
|
23513
|
+
emitJson8(
|
|
23514
|
+
buildEnvelope(command, true, {
|
|
23515
|
+
slug: row.slug,
|
|
23516
|
+
teams: row.teams,
|
|
23517
|
+
createdAt: row.createdAt,
|
|
23518
|
+
createdBy: row.createdBy
|
|
23519
|
+
})
|
|
23520
|
+
);
|
|
23521
|
+
} else {
|
|
23522
|
+
success2(
|
|
23523
|
+
[
|
|
23524
|
+
`Registered ${row.slug}`,
|
|
23525
|
+
``,
|
|
23526
|
+
` Slug: ${row.slug}`,
|
|
23527
|
+
` Teams: ${row.teams.join(", ")}`,
|
|
23528
|
+
` Created by: ${row.createdBy}`,
|
|
23529
|
+
` Created at: ${row.createdAt}`
|
|
23530
|
+
].join("\n")
|
|
23531
|
+
);
|
|
23532
|
+
}
|
|
23533
|
+
} catch (err) {
|
|
23534
|
+
const { code, message } = wrapProxyError(command, err);
|
|
23535
|
+
if (options.json) {
|
|
23536
|
+
emitJson8(buildErrorEnvelope(command, code, message));
|
|
23537
|
+
} else {
|
|
23538
|
+
error48(message);
|
|
23539
|
+
}
|
|
23540
|
+
exit(code);
|
|
23541
|
+
}
|
|
23542
|
+
}
|
|
23543
|
+
|
|
23544
|
+
// src/commands/sites/rm.ts
|
|
23545
|
+
async function rm2(options, deps = {}) {
|
|
23546
|
+
const command = "sites rm";
|
|
23547
|
+
const success2 = deps.logSuccess ?? ((s) => O2.success(s));
|
|
23548
|
+
const error48 = deps.logError ?? ((s) => O2.error(s));
|
|
23549
|
+
const exit = deps.exit ?? exitWithCode;
|
|
23550
|
+
try {
|
|
23551
|
+
if (!options.slug || options.slug.trim().length === 0) {
|
|
23552
|
+
throw new UsageError("slug is required (positional argument)");
|
|
23553
|
+
}
|
|
23554
|
+
const { client } = await setupClient(deps);
|
|
23555
|
+
await client.deleteSite({ slug: options.slug });
|
|
23556
|
+
if (options.json) {
|
|
23557
|
+
emitJson8(
|
|
23558
|
+
buildEnvelope(command, true, {
|
|
23559
|
+
slug: options.slug,
|
|
23560
|
+
deleted: true
|
|
23561
|
+
})
|
|
23562
|
+
);
|
|
23563
|
+
} else {
|
|
23564
|
+
success2(
|
|
23565
|
+
[
|
|
23566
|
+
`Deleted ${options.slug}`,
|
|
23567
|
+
``,
|
|
23568
|
+
` Note: R2 deploy bytes are NOT removed; they age out via the`,
|
|
23569
|
+
` post-GA cleanup cron.`
|
|
23570
|
+
].join("\n")
|
|
23571
|
+
);
|
|
23572
|
+
}
|
|
23573
|
+
} catch (err) {
|
|
23574
|
+
const { code, message } = wrapProxyError(command, err);
|
|
23575
|
+
if (options.json) {
|
|
23576
|
+
emitJson8(buildErrorEnvelope(command, code, message));
|
|
23577
|
+
} else {
|
|
23578
|
+
error48(message);
|
|
23579
|
+
}
|
|
23580
|
+
exit(code);
|
|
23581
|
+
}
|
|
23582
|
+
}
|
|
23583
|
+
|
|
23584
|
+
// src/commands/sites/update.ts
|
|
23585
|
+
async function update(options, deps = {}) {
|
|
23586
|
+
const command = "sites update";
|
|
23587
|
+
const success2 = deps.logSuccess ?? ((s) => O2.success(s));
|
|
23588
|
+
const error48 = deps.logError ?? ((s) => O2.error(s));
|
|
23589
|
+
const exit = deps.exit ?? exitWithCode;
|
|
23590
|
+
try {
|
|
23591
|
+
if (!options.slug || options.slug.trim().length === 0) {
|
|
23592
|
+
throw new UsageError("slug is required (positional argument)");
|
|
23593
|
+
}
|
|
23594
|
+
const teams = parseTeamsFlag(options.team);
|
|
23595
|
+
if (teams.length === 0) {
|
|
23596
|
+
throw new UsageError(
|
|
23597
|
+
"--team is required with at least one slug; use `sites rm` to remove a site"
|
|
23598
|
+
);
|
|
23599
|
+
}
|
|
23600
|
+
const { client } = await setupClient(deps);
|
|
23601
|
+
const row = await client.updateSite({
|
|
23602
|
+
slug: options.slug,
|
|
23603
|
+
teams
|
|
23604
|
+
});
|
|
23605
|
+
if (options.json) {
|
|
23606
|
+
emitJson8(
|
|
23607
|
+
buildEnvelope(command, true, {
|
|
23608
|
+
slug: row.slug,
|
|
23609
|
+
teams: row.teams,
|
|
23610
|
+
updatedAt: row.updatedAt
|
|
23611
|
+
})
|
|
23612
|
+
);
|
|
23613
|
+
} else {
|
|
23614
|
+
success2(
|
|
23615
|
+
[
|
|
23616
|
+
`Updated ${row.slug}`,
|
|
23617
|
+
``,
|
|
23618
|
+
` Slug: ${row.slug}`,
|
|
23619
|
+
` Teams: ${row.teams.join(", ")}`,
|
|
23620
|
+
` Updated at: ${row.updatedAt}`
|
|
23621
|
+
].join("\n")
|
|
23622
|
+
);
|
|
23623
|
+
}
|
|
23624
|
+
} catch (err) {
|
|
23625
|
+
const { code, message } = wrapProxyError(command, err);
|
|
23626
|
+
if (options.json) {
|
|
23627
|
+
emitJson8(buildErrorEnvelope(command, code, message));
|
|
23628
|
+
} else {
|
|
23629
|
+
error48(message);
|
|
23630
|
+
}
|
|
23631
|
+
exit(code);
|
|
23281
23632
|
}
|
|
23282
23633
|
}
|
|
23283
23634
|
|
|
@@ -23343,13 +23694,13 @@ function outputError(ctx, code, message, issues) {
|
|
|
23343
23694
|
}
|
|
23344
23695
|
|
|
23345
23696
|
// src/cli.ts
|
|
23346
|
-
var version2 = true ? "0.
|
|
23697
|
+
var version2 = true ? "0.5.1" : "0.0.0";
|
|
23347
23698
|
function handleActionError(command, json2, err) {
|
|
23348
23699
|
const ctx = { json: json2, command };
|
|
23349
23700
|
const message = err instanceof Error ? err.message : "unknown error";
|
|
23350
23701
|
const code = err instanceof CliError ? err.exitCode : EXIT_USAGE;
|
|
23351
23702
|
outputError(ctx, code, message);
|
|
23352
|
-
exitWithCode(code
|
|
23703
|
+
exitWithCode(code);
|
|
23353
23704
|
}
|
|
23354
23705
|
function findFirstPositional(args) {
|
|
23355
23706
|
for (let i = 0; i < args.length; i += 1) {
|
|
@@ -23361,7 +23712,75 @@ function findFirstPositional(args) {
|
|
|
23361
23712
|
function run(argv = process.argv) {
|
|
23362
23713
|
const args = argv.slice(2);
|
|
23363
23714
|
const firstPosIdx = findFirstPositional(args);
|
|
23364
|
-
const
|
|
23715
|
+
const namespace = firstPosIdx >= 0 ? args[firstPosIdx] : void 0;
|
|
23716
|
+
const isStatic = namespace === "static";
|
|
23717
|
+
const isSites = namespace === "sites";
|
|
23718
|
+
if (isSites) {
|
|
23719
|
+
const sitesArgs = [
|
|
23720
|
+
...args.slice(0, firstPosIdx),
|
|
23721
|
+
...args.slice(firstPosIdx + 1)
|
|
23722
|
+
];
|
|
23723
|
+
const sitesCli = cac("universe sites");
|
|
23724
|
+
sitesCli.command("register <slug>", "Register a new static site (staff only)").option("--json", "Output as JSON").option(
|
|
23725
|
+
"--team <name>",
|
|
23726
|
+
"GitHub team slug (repeatable, or comma-separated). Defaults to staff."
|
|
23727
|
+
).action(
|
|
23728
|
+
async (slug, flags) => {
|
|
23729
|
+
try {
|
|
23730
|
+
await register({
|
|
23731
|
+
json: flags.json ?? false,
|
|
23732
|
+
slug,
|
|
23733
|
+
team: flags.team
|
|
23734
|
+
});
|
|
23735
|
+
} catch (err) {
|
|
23736
|
+
handleActionError("sites register", flags.json ?? false, err);
|
|
23737
|
+
}
|
|
23738
|
+
}
|
|
23739
|
+
);
|
|
23740
|
+
sitesCli.command("ls", "List sites in the registry").option("--json", "Output as JSON").option(
|
|
23741
|
+
"--mine",
|
|
23742
|
+
"Filter to sites your GitHub identity is authorized for"
|
|
23743
|
+
).action(async (flags) => {
|
|
23744
|
+
try {
|
|
23745
|
+
await ls2({
|
|
23746
|
+
json: flags.json ?? false,
|
|
23747
|
+
mine: flags.mine ?? false
|
|
23748
|
+
});
|
|
23749
|
+
} catch (err) {
|
|
23750
|
+
handleActionError("sites ls", flags.json ?? false, err);
|
|
23751
|
+
}
|
|
23752
|
+
});
|
|
23753
|
+
sitesCli.command(
|
|
23754
|
+
"update <slug>",
|
|
23755
|
+
"Replace the teams list for an existing site (staff only)"
|
|
23756
|
+
).option("--json", "Output as JSON").option(
|
|
23757
|
+
"--team <name>",
|
|
23758
|
+
"GitHub team slug (repeatable, or comma-separated). Required."
|
|
23759
|
+
).action(
|
|
23760
|
+
async (slug, flags) => {
|
|
23761
|
+
try {
|
|
23762
|
+
await update({
|
|
23763
|
+
json: flags.json ?? false,
|
|
23764
|
+
slug,
|
|
23765
|
+
team: flags.team
|
|
23766
|
+
});
|
|
23767
|
+
} catch (err) {
|
|
23768
|
+
handleActionError("sites update", flags.json ?? false, err);
|
|
23769
|
+
}
|
|
23770
|
+
}
|
|
23771
|
+
);
|
|
23772
|
+
sitesCli.command("rm <slug>", "Remove a site from the registry (staff only)").option("--json", "Output as JSON").action(async (slug, flags) => {
|
|
23773
|
+
try {
|
|
23774
|
+
await rm2({ json: flags.json ?? false, slug });
|
|
23775
|
+
} catch (err) {
|
|
23776
|
+
handleActionError("sites rm", flags.json ?? false, err);
|
|
23777
|
+
}
|
|
23778
|
+
});
|
|
23779
|
+
sitesCli.help();
|
|
23780
|
+
sitesCli.version(version2);
|
|
23781
|
+
sitesCli.parse(["node", "universe-sites", ...sitesArgs]);
|
|
23782
|
+
return;
|
|
23783
|
+
}
|
|
23365
23784
|
if (isStatic) {
|
|
23366
23785
|
const staticArgs = [
|
|
23367
23786
|
...args.slice(0, firstPosIdx),
|
|
@@ -23444,6 +23863,7 @@ function run(argv = process.argv) {
|
|
|
23444
23863
|
}
|
|
23445
23864
|
});
|
|
23446
23865
|
cli.command("static <subcommand>", "Static site deployment commands");
|
|
23866
|
+
cli.command("sites <subcommand>", "Static site registry commands");
|
|
23447
23867
|
cli.help();
|
|
23448
23868
|
cli.version(version2);
|
|
23449
23869
|
cli.parse(argv);
|