@freecodecamp/universe-cli 0.7.0 → 0.7.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 +8 -8
- package/dist/index.cjs +57 -8
- package/dist/index.js +57 -8
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -88,8 +88,8 @@ All commands support `--json` for CI integration.
|
|
|
88
88
|
The CLI resolves a GitHub identity in this order — first match wins:
|
|
89
89
|
|
|
90
90
|
1. `$GITHUB_TOKEN` / `$GH_TOKEN` env (CI explicit)
|
|
91
|
-
1.
|
|
92
|
-
1.
|
|
91
|
+
1. Device-flow stored token at `~/.config/universe-cli/token` (`universe login`)
|
|
92
|
+
1. `gh auth token` shell-out (laptop fallback when no `universe login` token)
|
|
93
93
|
|
|
94
94
|
CI runners must export `$GITHUB_TOKEN` explicitly. artemis validates the bearer via GitHub `GET /user`, then authorizes server-side against the Valkey-backed registry. Run `universe whoami` to see which slot resolved; inspect the sites you can deploy to with `universe sites ls --mine`.
|
|
95
95
|
|
|
@@ -111,10 +111,10 @@ Full operator walkthrough (login → deploy → promote → rollback, CI shape,
|
|
|
111
111
|
|
|
112
112
|
## Environment overrides
|
|
113
113
|
|
|
114
|
-
| Env | Default
|
|
115
|
-
| --------------------------- |
|
|
116
|
-
| `UNIVERSE_PROXY_URL` | `https://uploads.freecode.camp`
|
|
117
|
-
| `UNIVERSE_GH_CLIENT_ID` | _baked-in freeCodeCamp
|
|
118
|
-
| `GITHUB_TOKEN` / `GH_TOKEN` | —
|
|
114
|
+
| Env | Default | Purpose |
|
|
115
|
+
| --------------------------- | ---------------------------------------------- | ---------------------------------------------------------- |
|
|
116
|
+
| `UNIVERSE_PROXY_URL` | `https://uploads.freecode.camp` | Override proxy host (staging etc.) |
|
|
117
|
+
| `UNIVERSE_GH_CLIENT_ID` | _baked-in freeCodeCamp-Universe GitHub App id_ | Override GitHub App client id (fork tenants, `login` only) |
|
|
118
|
+
| `GITHUB_TOKEN` / `GH_TOKEN` | — | Slot 1 of identity chain |
|
|
119
119
|
|
|
120
|
-
The shipped binary embeds the `freeCodeCamp` GitHub
|
|
120
|
+
The shipped binary embeds the `freeCodeCamp-Universe` GitHub App client id (public; device flow uses no `client_secret`), so `universe login` works out of the box for staff once the App is installed on their org. Fork operators and self-hosted mirror tenants set `UNIVERSE_GH_CLIENT_ID` to their own GitHub App's id — env value wins when set.
|
package/dist/index.cjs
CHANGED
|
@@ -8962,14 +8962,14 @@ async function resolveIdentity(opts = {}) {
|
|
|
8962
8962
|
if (isNonEmpty(ghTokenEnv)) {
|
|
8963
8963
|
return { token: ghTokenEnv.trim(), source: "env_GH_TOKEN" };
|
|
8964
8964
|
}
|
|
8965
|
-
const ghCli = await execGh();
|
|
8966
|
-
if (isNonEmpty(ghCli)) {
|
|
8967
|
-
return { token: ghCli.trim(), source: "gh_cli" };
|
|
8968
|
-
}
|
|
8969
8965
|
const stored = await loadStored();
|
|
8970
8966
|
if (isNonEmpty(stored)) {
|
|
8971
8967
|
return { token: stored.trim(), source: "device_flow" };
|
|
8972
8968
|
}
|
|
8969
|
+
const ghCli = await execGh();
|
|
8970
|
+
if (isNonEmpty(ghCli)) {
|
|
8971
|
+
return { token: ghCli.trim(), source: "gh_cli" };
|
|
8972
|
+
}
|
|
8973
8973
|
return null;
|
|
8974
8974
|
}
|
|
8975
8975
|
|
|
@@ -23810,6 +23810,19 @@ async function runDeviceFlow(opts) {
|
|
|
23810
23810
|
|
|
23811
23811
|
// src/commands/login.ts
|
|
23812
23812
|
var DEFAULT_SCOPE = "read:org user:email";
|
|
23813
|
+
var DEFAULT_PROXY_URL2 = "https://uploads.freecode.camp";
|
|
23814
|
+
var NO_SITES_WARNING = [
|
|
23815
|
+
"Logged in, but the proxy reports 0 authorized sites for your account.",
|
|
23816
|
+
"This usually means the Universe CLI GitHub App is not installed on the org",
|
|
23817
|
+
"that owns the registry-authz team (production: `freeCodeCamp-Universe`), or",
|
|
23818
|
+
"your account is not on a team granted access to any site.",
|
|
23819
|
+
"",
|
|
23820
|
+
"Next steps:",
|
|
23821
|
+
" 1. Run `universe whoami` to confirm the identity that resolved.",
|
|
23822
|
+
" 2. Ask an org owner to install the Universe CLI GitHub App on the org.",
|
|
23823
|
+
" 3. Confirm your team membership at",
|
|
23824
|
+
" https://github.com/orgs/freeCodeCamp-Universe/teams."
|
|
23825
|
+
].join("\n");
|
|
23813
23826
|
function emitJson2(envelope) {
|
|
23814
23827
|
process.stdout.write(JSON.stringify(envelope) + "\n");
|
|
23815
23828
|
}
|
|
@@ -23881,10 +23894,46 @@ async function login(options, deps = {}) {
|
|
|
23881
23894
|
return;
|
|
23882
23895
|
}
|
|
23883
23896
|
await save(token);
|
|
23897
|
+
const selfCheck = await postLoginSelfCheck(token, env, deps);
|
|
23884
23898
|
if (options.json) {
|
|
23885
|
-
emitJson2(
|
|
23899
|
+
emitJson2(
|
|
23900
|
+
buildEnvelope("login", true, {
|
|
23901
|
+
stored: true,
|
|
23902
|
+
...selfCheck.checked ? {
|
|
23903
|
+
authorizedSitesCount: selfCheck.authorizedSitesCount,
|
|
23904
|
+
...selfCheck.warning ? { warning: selfCheck.warning } : {}
|
|
23905
|
+
} : {}
|
|
23906
|
+
})
|
|
23907
|
+
);
|
|
23886
23908
|
} else {
|
|
23887
23909
|
success2("Logged in. Token stored at ~/.config/universe-cli/token.");
|
|
23910
|
+
if (selfCheck.checked && selfCheck.warning) {
|
|
23911
|
+
const warn = deps.logWarn ?? ((s) => O2.warn(s));
|
|
23912
|
+
warn(selfCheck.warning);
|
|
23913
|
+
}
|
|
23914
|
+
}
|
|
23915
|
+
}
|
|
23916
|
+
async function postLoginSelfCheck(token, env, deps) {
|
|
23917
|
+
const mkClient = deps.createProxyClient ?? createProxyClient;
|
|
23918
|
+
try {
|
|
23919
|
+
const baseUrl = env["UNIVERSE_PROXY_URL"] ?? DEFAULT_PROXY_URL2;
|
|
23920
|
+
const client = mkClient({
|
|
23921
|
+
baseUrl,
|
|
23922
|
+
getAuthToken: () => token,
|
|
23923
|
+
timeoutMs: parseFetchTimeoutMs(env)
|
|
23924
|
+
});
|
|
23925
|
+
const result = await client.whoami();
|
|
23926
|
+
const count = result.authorizedSites.length;
|
|
23927
|
+
if (count === 0) {
|
|
23928
|
+
return {
|
|
23929
|
+
checked: true,
|
|
23930
|
+
authorizedSitesCount: 0,
|
|
23931
|
+
warning: NO_SITES_WARNING
|
|
23932
|
+
};
|
|
23933
|
+
}
|
|
23934
|
+
return { checked: true, authorizedSitesCount: count };
|
|
23935
|
+
} catch {
|
|
23936
|
+
return { checked: false, authorizedSitesCount: 0 };
|
|
23888
23937
|
}
|
|
23889
23938
|
}
|
|
23890
23939
|
|
|
@@ -24296,7 +24345,7 @@ async function rollback(options, deps = {}) {
|
|
|
24296
24345
|
}
|
|
24297
24346
|
|
|
24298
24347
|
// src/commands/whoami.ts
|
|
24299
|
-
var
|
|
24348
|
+
var DEFAULT_PROXY_URL3 = "https://uploads.freecode.camp";
|
|
24300
24349
|
function emitJson7(envelope) {
|
|
24301
24350
|
process.stdout.write(JSON.stringify(envelope) + "\n");
|
|
24302
24351
|
}
|
|
@@ -24319,7 +24368,7 @@ async function whoami(options, deps = {}) {
|
|
|
24319
24368
|
exit(EXIT_CREDENTIALS);
|
|
24320
24369
|
return;
|
|
24321
24370
|
}
|
|
24322
|
-
const baseUrl = env["UNIVERSE_PROXY_URL"] ??
|
|
24371
|
+
const baseUrl = env["UNIVERSE_PROXY_URL"] ?? DEFAULT_PROXY_URL3;
|
|
24323
24372
|
const client = mkClient({
|
|
24324
24373
|
baseUrl,
|
|
24325
24374
|
getAuthToken: () => identity.token,
|
|
@@ -24573,7 +24622,7 @@ async function update(options, deps = {}) {
|
|
|
24573
24622
|
}
|
|
24574
24623
|
|
|
24575
24624
|
// src/cli.ts
|
|
24576
|
-
var version2 = true ? "0.7.
|
|
24625
|
+
var version2 = true ? "0.7.1" : "0.0.0";
|
|
24577
24626
|
function handleActionError(command, json2, err) {
|
|
24578
24627
|
const ctx = { json: json2, command };
|
|
24579
24628
|
const message = err instanceof Error ? err.message : "unknown error";
|
package/dist/index.js
CHANGED
|
@@ -221,14 +221,14 @@ async function resolveIdentity(opts = {}) {
|
|
|
221
221
|
if (isNonEmpty(ghTokenEnv)) {
|
|
222
222
|
return { token: ghTokenEnv.trim(), source: "env_GH_TOKEN" };
|
|
223
223
|
}
|
|
224
|
-
const ghCli = await execGh();
|
|
225
|
-
if (isNonEmpty(ghCli)) {
|
|
226
|
-
return { token: ghCli.trim(), source: "gh_cli" };
|
|
227
|
-
}
|
|
228
224
|
const stored = await loadStored();
|
|
229
225
|
if (isNonEmpty(stored)) {
|
|
230
226
|
return { token: stored.trim(), source: "device_flow" };
|
|
231
227
|
}
|
|
228
|
+
const ghCli = await execGh();
|
|
229
|
+
if (isNonEmpty(ghCli)) {
|
|
230
|
+
return { token: ghCli.trim(), source: "gh_cli" };
|
|
231
|
+
}
|
|
232
232
|
return null;
|
|
233
233
|
}
|
|
234
234
|
|
|
@@ -1308,6 +1308,19 @@ async function runDeviceFlow(opts) {
|
|
|
1308
1308
|
|
|
1309
1309
|
// src/commands/login.ts
|
|
1310
1310
|
var DEFAULT_SCOPE = "read:org user:email";
|
|
1311
|
+
var DEFAULT_PROXY_URL2 = "https://uploads.freecode.camp";
|
|
1312
|
+
var NO_SITES_WARNING = [
|
|
1313
|
+
"Logged in, but the proxy reports 0 authorized sites for your account.",
|
|
1314
|
+
"This usually means the Universe CLI GitHub App is not installed on the org",
|
|
1315
|
+
"that owns the registry-authz team (production: `freeCodeCamp-Universe`), or",
|
|
1316
|
+
"your account is not on a team granted access to any site.",
|
|
1317
|
+
"",
|
|
1318
|
+
"Next steps:",
|
|
1319
|
+
" 1. Run `universe whoami` to confirm the identity that resolved.",
|
|
1320
|
+
" 2. Ask an org owner to install the Universe CLI GitHub App on the org.",
|
|
1321
|
+
" 3. Confirm your team membership at",
|
|
1322
|
+
" https://github.com/orgs/freeCodeCamp-Universe/teams."
|
|
1323
|
+
].join("\n");
|
|
1311
1324
|
function emitJson2(envelope) {
|
|
1312
1325
|
process.stdout.write(JSON.stringify(envelope) + "\n");
|
|
1313
1326
|
}
|
|
@@ -1379,10 +1392,46 @@ async function login(options, deps = {}) {
|
|
|
1379
1392
|
return;
|
|
1380
1393
|
}
|
|
1381
1394
|
await save(token);
|
|
1395
|
+
const selfCheck = await postLoginSelfCheck(token, env, deps);
|
|
1382
1396
|
if (options.json) {
|
|
1383
|
-
emitJson2(
|
|
1397
|
+
emitJson2(
|
|
1398
|
+
buildEnvelope("login", true, {
|
|
1399
|
+
stored: true,
|
|
1400
|
+
...selfCheck.checked ? {
|
|
1401
|
+
authorizedSitesCount: selfCheck.authorizedSitesCount,
|
|
1402
|
+
...selfCheck.warning ? { warning: selfCheck.warning } : {}
|
|
1403
|
+
} : {}
|
|
1404
|
+
})
|
|
1405
|
+
);
|
|
1384
1406
|
} else {
|
|
1385
1407
|
success("Logged in. Token stored at ~/.config/universe-cli/token.");
|
|
1408
|
+
if (selfCheck.checked && selfCheck.warning) {
|
|
1409
|
+
const warn = deps.logWarn ?? ((s) => log3.warn(s));
|
|
1410
|
+
warn(selfCheck.warning);
|
|
1411
|
+
}
|
|
1412
|
+
}
|
|
1413
|
+
}
|
|
1414
|
+
async function postLoginSelfCheck(token, env, deps) {
|
|
1415
|
+
const mkClient = deps.createProxyClient ?? createProxyClient;
|
|
1416
|
+
try {
|
|
1417
|
+
const baseUrl = env["UNIVERSE_PROXY_URL"] ?? DEFAULT_PROXY_URL2;
|
|
1418
|
+
const client = mkClient({
|
|
1419
|
+
baseUrl,
|
|
1420
|
+
getAuthToken: () => token,
|
|
1421
|
+
timeoutMs: parseFetchTimeoutMs(env)
|
|
1422
|
+
});
|
|
1423
|
+
const result = await client.whoami();
|
|
1424
|
+
const count = result.authorizedSites.length;
|
|
1425
|
+
if (count === 0) {
|
|
1426
|
+
return {
|
|
1427
|
+
checked: true,
|
|
1428
|
+
authorizedSitesCount: 0,
|
|
1429
|
+
warning: NO_SITES_WARNING
|
|
1430
|
+
};
|
|
1431
|
+
}
|
|
1432
|
+
return { checked: true, authorizedSitesCount: count };
|
|
1433
|
+
} catch {
|
|
1434
|
+
return { checked: false, authorizedSitesCount: 0 };
|
|
1386
1435
|
}
|
|
1387
1436
|
}
|
|
1388
1437
|
|
|
@@ -1799,7 +1848,7 @@ async function rollback(options, deps = {}) {
|
|
|
1799
1848
|
|
|
1800
1849
|
// src/commands/whoami.ts
|
|
1801
1850
|
import { log as log8 } from "@clack/prompts";
|
|
1802
|
-
var
|
|
1851
|
+
var DEFAULT_PROXY_URL3 = "https://uploads.freecode.camp";
|
|
1803
1852
|
function emitJson7(envelope) {
|
|
1804
1853
|
process.stdout.write(JSON.stringify(envelope) + "\n");
|
|
1805
1854
|
}
|
|
@@ -1822,7 +1871,7 @@ async function whoami(options, deps = {}) {
|
|
|
1822
1871
|
exit(EXIT_CREDENTIALS);
|
|
1823
1872
|
return;
|
|
1824
1873
|
}
|
|
1825
|
-
const baseUrl = env["UNIVERSE_PROXY_URL"] ??
|
|
1874
|
+
const baseUrl = env["UNIVERSE_PROXY_URL"] ?? DEFAULT_PROXY_URL3;
|
|
1826
1875
|
const client = mkClient({
|
|
1827
1876
|
baseUrl,
|
|
1828
1877
|
getAuthToken: () => identity.token,
|
|
@@ -2082,7 +2131,7 @@ async function update(options, deps = {}) {
|
|
|
2082
2131
|
}
|
|
2083
2132
|
|
|
2084
2133
|
// src/cli.ts
|
|
2085
|
-
var version = true ? "0.7.
|
|
2134
|
+
var version = true ? "0.7.1" : "0.0.0";
|
|
2086
2135
|
function handleActionError(command, json, err) {
|
|
2087
2136
|
const ctx = { json, command };
|
|
2088
2137
|
const message = err instanceof Error ? err.message : "unknown error";
|