@freecodecamp/universe-cli 0.5.1 → 0.7.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 +3 -43
- package/dist/index.cjs +1123 -244
- package/dist/index.js +422 -194
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -6,7 +6,7 @@ import { cac } from "cac";
|
|
|
6
6
|
// src/commands/deploy.ts
|
|
7
7
|
import { readFile as readFile2 } from "fs/promises";
|
|
8
8
|
import { resolve as resolve2 } from "path";
|
|
9
|
-
import { log } from "@clack/prompts";
|
|
9
|
+
import { log as log2, spinner } from "@clack/prompts";
|
|
10
10
|
|
|
11
11
|
// src/output/exit-codes.ts
|
|
12
12
|
var EXIT_USAGE = 10;
|
|
@@ -429,6 +429,14 @@ function suggest(target, candidates, threshold = 2) {
|
|
|
429
429
|
}
|
|
430
430
|
|
|
431
431
|
// src/lib/proxy-client.ts
|
|
432
|
+
var DEFAULT_FETCH_TIMEOUT_MS = 3e4;
|
|
433
|
+
function parseFetchTimeoutMs(env) {
|
|
434
|
+
const raw = env["UNIVERSE_FETCH_TIMEOUT_MS"];
|
|
435
|
+
if (raw === void 0 || raw === "") return void 0;
|
|
436
|
+
const n = Number(raw);
|
|
437
|
+
if (!Number.isFinite(n) || n < 0) return void 0;
|
|
438
|
+
return Math.floor(n);
|
|
439
|
+
}
|
|
432
440
|
var ProxyError = class extends CliError {
|
|
433
441
|
exitCode;
|
|
434
442
|
status;
|
|
@@ -440,6 +448,13 @@ var ProxyError = class extends CliError {
|
|
|
440
448
|
this.exitCode = mapExitCode(status);
|
|
441
449
|
}
|
|
442
450
|
};
|
|
451
|
+
var AliasDriftError = class extends ProxyError {
|
|
452
|
+
current;
|
|
453
|
+
constructor(message, current) {
|
|
454
|
+
super(409, "alias_drift", message);
|
|
455
|
+
this.current = current;
|
|
456
|
+
}
|
|
457
|
+
};
|
|
443
458
|
function mapExitCode(status) {
|
|
444
459
|
if (status === 401 || status === 403) return EXIT_CREDENTIALS;
|
|
445
460
|
if (status === 422 || status === 0 || status >= 500) return EXIT_STORAGE;
|
|
@@ -475,9 +490,11 @@ async function readErrorEnvelope(response) {
|
|
|
475
490
|
};
|
|
476
491
|
}
|
|
477
492
|
if (isProxyErrorEnvelope(raw) && raw.error) {
|
|
493
|
+
const current = typeof raw.current === "string" ? raw.current : void 0;
|
|
478
494
|
return {
|
|
479
495
|
code: raw.error.code ?? `http_${status}`,
|
|
480
|
-
message: raw.error.message ?? response.statusText ?? "request failed"
|
|
496
|
+
message: raw.error.message ?? response.statusText ?? "request failed",
|
|
497
|
+
...current === void 0 ? {} : { current }
|
|
481
498
|
};
|
|
482
499
|
}
|
|
483
500
|
return {
|
|
@@ -485,12 +502,36 @@ async function readErrorEnvelope(response) {
|
|
|
485
502
|
message: response.statusText || "request failed"
|
|
486
503
|
};
|
|
487
504
|
}
|
|
505
|
+
function throwProxyError(status, env) {
|
|
506
|
+
if (status === 409 && env.code === "alias_drift") {
|
|
507
|
+
throw new AliasDriftError(env.message, env.current ?? "");
|
|
508
|
+
}
|
|
509
|
+
throw new ProxyError(status, env.code, env.message);
|
|
510
|
+
}
|
|
488
511
|
function stripTrailingSlash(url) {
|
|
489
512
|
return url.endsWith("/") ? url.slice(0, -1) : url;
|
|
490
513
|
}
|
|
491
514
|
function createProxyClient(cfg) {
|
|
492
515
|
const base = stripTrailingSlash(cfg.baseUrl);
|
|
493
516
|
const fetchImpl = cfg.fetch ?? globalThis.fetch.bind(globalThis);
|
|
517
|
+
const timeoutMs = cfg.timeoutMs ?? DEFAULT_FETCH_TIMEOUT_MS;
|
|
518
|
+
function withTimeoutSignal(init) {
|
|
519
|
+
if (timeoutMs <= 0) return init;
|
|
520
|
+
const timeoutSignal = AbortSignal.timeout(timeoutMs);
|
|
521
|
+
const merged = init.signal ? AbortSignal.any([init.signal, timeoutSignal]) : timeoutSignal;
|
|
522
|
+
return { ...init, signal: merged };
|
|
523
|
+
}
|
|
524
|
+
function translateFetchError(err) {
|
|
525
|
+
if (err instanceof DOMException && (err.name === "TimeoutError" || err.name === "AbortError")) {
|
|
526
|
+
throw new ProxyError(
|
|
527
|
+
0,
|
|
528
|
+
"timeout",
|
|
529
|
+
`proxy timed out after ${timeoutMs}ms`
|
|
530
|
+
);
|
|
531
|
+
}
|
|
532
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
533
|
+
throw new ProxyError(0, "network_error", `proxy unreachable: ${message}`);
|
|
534
|
+
}
|
|
494
535
|
async function userBearer() {
|
|
495
536
|
const tok = await cfg.getAuthToken();
|
|
496
537
|
return `Bearer ${tok}`;
|
|
@@ -498,14 +539,13 @@ function createProxyClient(cfg) {
|
|
|
498
539
|
async function call(url, init) {
|
|
499
540
|
let response;
|
|
500
541
|
try {
|
|
501
|
-
response = await fetchImpl(url, init);
|
|
542
|
+
response = await fetchImpl(url, withTimeoutSignal(init));
|
|
502
543
|
} catch (err) {
|
|
503
|
-
|
|
504
|
-
throw new ProxyError(0, "network_error", `proxy unreachable: ${message}`);
|
|
544
|
+
translateFetchError(err);
|
|
505
545
|
}
|
|
506
546
|
if (!response.ok) {
|
|
507
547
|
const env = await readErrorEnvelope(response);
|
|
508
|
-
|
|
548
|
+
throwProxyError(response.status, env);
|
|
509
549
|
}
|
|
510
550
|
if (response.status === 204) {
|
|
511
551
|
return void 0;
|
|
@@ -569,18 +609,53 @@ function createProxyClient(cfg) {
|
|
|
569
609
|
}
|
|
570
610
|
});
|
|
571
611
|
},
|
|
612
|
+
async getAlias(req) {
|
|
613
|
+
const url = `${base}/api/site/${encodeURIComponent(req.site)}/alias/${encodeURIComponent(req.mode)}`;
|
|
614
|
+
let response;
|
|
615
|
+
try {
|
|
616
|
+
response = await fetchImpl(
|
|
617
|
+
url,
|
|
618
|
+
withTimeoutSignal({
|
|
619
|
+
method: "GET",
|
|
620
|
+
headers: {
|
|
621
|
+
Authorization: await userBearer(),
|
|
622
|
+
Accept: "application/json"
|
|
623
|
+
}
|
|
624
|
+
})
|
|
625
|
+
);
|
|
626
|
+
} catch (err) {
|
|
627
|
+
translateFetchError(err);
|
|
628
|
+
}
|
|
629
|
+
if (response.status === 404) return null;
|
|
630
|
+
if (!response.ok) {
|
|
631
|
+
const env = await readErrorEnvelope(response);
|
|
632
|
+
throwProxyError(response.status, env);
|
|
633
|
+
}
|
|
634
|
+
return await response.json();
|
|
635
|
+
},
|
|
572
636
|
async sitePromote(req) {
|
|
573
637
|
const url = `${base}/api/site/${encodeURIComponent(req.site)}/promote`;
|
|
638
|
+
const headers = {
|
|
639
|
+
Authorization: await userBearer(),
|
|
640
|
+
Accept: "application/json"
|
|
641
|
+
};
|
|
642
|
+
const body = {};
|
|
643
|
+
if (req.deployId !== void 0) body.deployId = req.deployId;
|
|
644
|
+
if (req.expectedCurrent !== void 0)
|
|
645
|
+
body.expectedCurrent = req.expectedCurrent;
|
|
646
|
+
const hasBody = Object.keys(body).length > 0;
|
|
647
|
+
if (hasBody) headers["Content-Type"] = "application/json";
|
|
574
648
|
return call(url, {
|
|
575
649
|
method: "POST",
|
|
576
|
-
headers
|
|
577
|
-
|
|
578
|
-
Accept: "application/json"
|
|
579
|
-
}
|
|
650
|
+
headers,
|
|
651
|
+
...hasBody ? { body: JSON.stringify(body) } : {}
|
|
580
652
|
});
|
|
581
653
|
},
|
|
582
654
|
async siteRollback(req) {
|
|
583
655
|
const url = `${base}/api/site/${encodeURIComponent(req.site)}/rollback`;
|
|
656
|
+
const body = { to: req.to };
|
|
657
|
+
if (req.expectedCurrent !== void 0)
|
|
658
|
+
body.expectedCurrent = req.expectedCurrent;
|
|
584
659
|
return call(url, {
|
|
585
660
|
method: "POST",
|
|
586
661
|
headers: {
|
|
@@ -588,7 +663,7 @@ function createProxyClient(cfg) {
|
|
|
588
663
|
Accept: "application/json",
|
|
589
664
|
"Content-Type": "application/json"
|
|
590
665
|
},
|
|
591
|
-
body: JSON.stringify(
|
|
666
|
+
body: JSON.stringify(body)
|
|
592
667
|
});
|
|
593
668
|
},
|
|
594
669
|
async registerSite(req) {
|
|
@@ -793,6 +868,106 @@ function buildErrorEnvelope(command, code, message, issues) {
|
|
|
793
868
|
};
|
|
794
869
|
}
|
|
795
870
|
|
|
871
|
+
// src/output/format.ts
|
|
872
|
+
import { log } from "@clack/prompts";
|
|
873
|
+
|
|
874
|
+
// src/output/redact.ts
|
|
875
|
+
var AWS_KEY_PREFIX_PATTERN = /(?:AKIA|ASIA|AROA|AIDA|ACCA|ANPA|ABIA|AGPA)[A-Z0-9]{12,}/g;
|
|
876
|
+
var URL_CREDS_PATTERN = /https?:\/\/[^@\s]+@/g;
|
|
877
|
+
var CREDENTIAL_CONTEXT_PATTERN = /(?:access_key_id|secret_access_key|accessKeyId|secretAccessKey|secret|password|token|key|credential|auth)\s*[=:]\s*([A-Za-z0-9/+=]{21,})/gi;
|
|
878
|
+
var HEX_CREDENTIAL_CONTEXT_PATTERN = /(?:secret|password|token|key|credential|auth|access_key_id|secret_access_key)\s*[=:]\s*([a-f0-9]{32,})/gi;
|
|
879
|
+
var JSON_CREDENTIAL_PATTERN = /"(?:secret|password|token|key|credential|auth|access_key_id|secret_access_key|accessKeyId|secretAccessKey)"\s*:\s*"[^"]+"/gi;
|
|
880
|
+
var BEARER_PATTERN = /\bBearer\s+[A-Za-z0-9._~+/=-]+/gi;
|
|
881
|
+
var CREDENTIAL_KEY_PATTERN = /(?:secret|password|token|key|credential|auth)/i;
|
|
882
|
+
var EXACT_CREDENTIAL_KEYS = /* @__PURE__ */ new Set([
|
|
883
|
+
"accesskeyid",
|
|
884
|
+
"secretaccesskey",
|
|
885
|
+
"access_key_id",
|
|
886
|
+
"secret_access_key"
|
|
887
|
+
]);
|
|
888
|
+
var STANDALONE_LONG_SECRET = /^[A-Za-z0-9/+=]{21,}$/;
|
|
889
|
+
function maskAwsKey(match) {
|
|
890
|
+
return match.slice(0, 4) + "****" + match.slice(-4);
|
|
891
|
+
}
|
|
892
|
+
function maskUrlCreds(match) {
|
|
893
|
+
const atIndex = match.lastIndexOf("@");
|
|
894
|
+
const protocolEnd = match.indexOf("://") + 3;
|
|
895
|
+
return match.slice(0, protocolEnd) + "****:****@" + match.slice(atIndex + 1);
|
|
896
|
+
}
|
|
897
|
+
function redact(value) {
|
|
898
|
+
let result = value;
|
|
899
|
+
result = result.replace(URL_CREDS_PATTERN, maskUrlCreds);
|
|
900
|
+
result = result.replace(AWS_KEY_PREFIX_PATTERN, maskAwsKey);
|
|
901
|
+
result = result.replace(BEARER_PATTERN, "Bearer ****");
|
|
902
|
+
result = result.replace(JSON_CREDENTIAL_PATTERN, (match) => {
|
|
903
|
+
const colonIndex = match.indexOf(":");
|
|
904
|
+
return match.slice(0, colonIndex + 1) + '"****"';
|
|
905
|
+
});
|
|
906
|
+
result = result.replace(
|
|
907
|
+
CREDENTIAL_CONTEXT_PATTERN,
|
|
908
|
+
(_match, _secret, _offset, _full) => {
|
|
909
|
+
const eqIndex = _match.indexOf("=");
|
|
910
|
+
const colonIndex = _match.indexOf(":");
|
|
911
|
+
const sepIndex = eqIndex >= 0 && colonIndex >= 0 ? Math.min(eqIndex, colonIndex) : eqIndex >= 0 ? eqIndex : colonIndex;
|
|
912
|
+
const prefix = _match.slice(0, sepIndex + 1);
|
|
913
|
+
return prefix + "****";
|
|
914
|
+
}
|
|
915
|
+
);
|
|
916
|
+
result = result.replace(HEX_CREDENTIAL_CONTEXT_PATTERN, (_match, _secret) => {
|
|
917
|
+
const eqIndex = _match.indexOf("=");
|
|
918
|
+
const colonIndex = _match.indexOf(":");
|
|
919
|
+
const sepIndex = eqIndex >= 0 && colonIndex >= 0 ? Math.min(eqIndex, colonIndex) : eqIndex >= 0 ? eqIndex : colonIndex;
|
|
920
|
+
const prefix = _match.slice(0, sepIndex + 1);
|
|
921
|
+
return prefix + "****";
|
|
922
|
+
});
|
|
923
|
+
return result;
|
|
924
|
+
}
|
|
925
|
+
function redactValue(value, keyName) {
|
|
926
|
+
if (typeof value === "string") {
|
|
927
|
+
if (keyName && EXACT_CREDENTIAL_KEYS.has(keyName.toLowerCase())) {
|
|
928
|
+
return "****";
|
|
929
|
+
}
|
|
930
|
+
const redacted = redact(value);
|
|
931
|
+
if (redacted === value && keyName && CREDENTIAL_KEY_PATTERN.test(keyName) && STANDALONE_LONG_SECRET.test(value)) {
|
|
932
|
+
return "****";
|
|
933
|
+
}
|
|
934
|
+
return redacted;
|
|
935
|
+
}
|
|
936
|
+
if (Array.isArray(value)) {
|
|
937
|
+
return value.map((v) => redactValue(v, keyName));
|
|
938
|
+
}
|
|
939
|
+
if (value !== null && typeof value === "object") {
|
|
940
|
+
return redactObject(value);
|
|
941
|
+
}
|
|
942
|
+
return value;
|
|
943
|
+
}
|
|
944
|
+
function redactObject(obj) {
|
|
945
|
+
const result = {};
|
|
946
|
+
for (const [key, value] of Object.entries(obj)) {
|
|
947
|
+
result[key] = redactValue(value, key);
|
|
948
|
+
}
|
|
949
|
+
return result;
|
|
950
|
+
}
|
|
951
|
+
|
|
952
|
+
// src/output/format.ts
|
|
953
|
+
function outputError(ctx, code, message, optsOrIssues) {
|
|
954
|
+
const opts = Array.isArray(optsOrIssues) ? { issues: optsOrIssues } : optsOrIssues ?? {};
|
|
955
|
+
const redactedMessage = redact(message);
|
|
956
|
+
const redactedIssues = opts.issues?.map(redact);
|
|
957
|
+
if (ctx.json) {
|
|
958
|
+
const envelope = buildErrorEnvelope(
|
|
959
|
+
ctx.command,
|
|
960
|
+
code,
|
|
961
|
+
redactedMessage,
|
|
962
|
+
redactedIssues
|
|
963
|
+
);
|
|
964
|
+
const payload = opts.extras ? { ...envelope, ...redactObject(opts.extras) } : envelope;
|
|
965
|
+
process.stdout.write(JSON.stringify(payload) + "\n");
|
|
966
|
+
} else {
|
|
967
|
+
(opts.logError ?? ((m) => log.error(m)))(redactedMessage);
|
|
968
|
+
}
|
|
969
|
+
}
|
|
970
|
+
|
|
796
971
|
// src/commands/deploy.ts
|
|
797
972
|
var defaultReadPlatformYaml = async (cwd) => {
|
|
798
973
|
return readFile2(resolve2(cwd, "platform.yaml"), "utf-8");
|
|
@@ -886,10 +1061,11 @@ async function deploy(options, deps = {}) {
|
|
|
886
1061
|
const build = deps.runBuild ?? runBuild;
|
|
887
1062
|
const walk = deps.walkFiles ?? walkFiles;
|
|
888
1063
|
const upload = deps.uploadFiles ?? uploadFiles;
|
|
889
|
-
const
|
|
890
|
-
const
|
|
891
|
-
const
|
|
892
|
-
const
|
|
1064
|
+
const mkSpinner = deps.createSpinner ?? (() => spinner());
|
|
1065
|
+
const success = deps.logSuccess ?? ((s) => log2.success(s));
|
|
1066
|
+
const info = deps.logInfo ?? ((s) => log2.info(s));
|
|
1067
|
+
const warn = deps.logWarn ?? ((s) => log2.warn(s));
|
|
1068
|
+
const error = deps.logError ?? ((s) => log2.error(s));
|
|
893
1069
|
const exit = deps.exit ?? exitWithCode;
|
|
894
1070
|
try {
|
|
895
1071
|
const identity = await resolveId({ env });
|
|
@@ -902,7 +1078,8 @@ async function deploy(options, deps = {}) {
|
|
|
902
1078
|
const baseUrl = env["UNIVERSE_PROXY_URL"] ?? DEFAULT_PROXY_URL;
|
|
903
1079
|
const client = mkClient({
|
|
904
1080
|
baseUrl,
|
|
905
|
-
getAuthToken: () => identity.token
|
|
1081
|
+
getAuthToken: () => identity.token,
|
|
1082
|
+
timeoutMs: parseFetchTimeoutMs(env)
|
|
906
1083
|
});
|
|
907
1084
|
let me;
|
|
908
1085
|
try {
|
|
@@ -953,17 +1130,22 @@ async function deploy(options, deps = {}) {
|
|
|
953
1130
|
} catch (err) {
|
|
954
1131
|
rethrowProxy("deploy init failed", err);
|
|
955
1132
|
}
|
|
1133
|
+
const spin = options.json ? null : mkSpinner();
|
|
1134
|
+
spin?.start(`Uploading 0/${filtered.length} files`);
|
|
956
1135
|
const uploadResult = await upload({
|
|
957
1136
|
client,
|
|
958
1137
|
deployId: initResult.deployId,
|
|
959
1138
|
jwt: initResult.jwt,
|
|
960
|
-
files: filtered
|
|
1139
|
+
files: filtered,
|
|
1140
|
+
onProgress: spin ? (p) => spin.message(`Uploading ${p.uploaded}/${p.total} \u2014 ${p.current}`) : void 0
|
|
961
1141
|
});
|
|
962
1142
|
if (uploadResult.errors.length > 0) {
|
|
1143
|
+
spin?.error(`Upload failed: ${uploadResult.errors.length} file(s)`);
|
|
963
1144
|
const message = `Upload partially failed: ${uploadResult.errors.length} file(s) failed:
|
|
964
1145
|
- ${uploadResult.errors.join("\n - ")}`;
|
|
965
1146
|
throw new PartialUploadError(message);
|
|
966
1147
|
}
|
|
1148
|
+
spin?.stop(`Uploaded ${uploadResult.fileCount} files`);
|
|
967
1149
|
const mode = options.promote ? "production" : "preview";
|
|
968
1150
|
let finalizeResult;
|
|
969
1151
|
try {
|
|
@@ -976,6 +1158,25 @@ async function deploy(options, deps = {}) {
|
|
|
976
1158
|
} catch (err) {
|
|
977
1159
|
rethrowProxy("deploy finalize failed", err);
|
|
978
1160
|
}
|
|
1161
|
+
if (options.promote && !options.json) {
|
|
1162
|
+
try {
|
|
1163
|
+
const preview = await client.getAlias({
|
|
1164
|
+
site: config.site,
|
|
1165
|
+
mode: "preview"
|
|
1166
|
+
});
|
|
1167
|
+
if (preview && preview.deployId !== finalizeResult.deployId) {
|
|
1168
|
+
warn(
|
|
1169
|
+
`Preview alias still points to ${preview.deployId}; it will not auto-update. Run \`universe static deploy\` (without --promote) to refresh preview.`
|
|
1170
|
+
);
|
|
1171
|
+
}
|
|
1172
|
+
} catch (err) {
|
|
1173
|
+
if (err instanceof ProxyError && (err.status === 401 || err.status === 403)) {
|
|
1174
|
+
warn(
|
|
1175
|
+
`Preview alias probe got ${err.status} (${err.code}) \u2014 token may need rotation: ${err.message}`
|
|
1176
|
+
);
|
|
1177
|
+
}
|
|
1178
|
+
}
|
|
1179
|
+
}
|
|
979
1180
|
if (options.json) {
|
|
980
1181
|
emitJson(
|
|
981
1182
|
buildEnvelope("deploy", true, {
|
|
@@ -1022,17 +1223,15 @@ async function deploy(options, deps = {}) {
|
|
|
1022
1223
|
code = EXIT_USAGE;
|
|
1023
1224
|
message = String(err);
|
|
1024
1225
|
}
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
}
|
|
1028
|
-
error(message);
|
|
1029
|
-
}
|
|
1226
|
+
outputError({ json: options.json, command: "deploy" }, code, message, {
|
|
1227
|
+
logError: error
|
|
1228
|
+
});
|
|
1030
1229
|
exit(code);
|
|
1031
1230
|
}
|
|
1032
1231
|
}
|
|
1033
1232
|
|
|
1034
1233
|
// src/commands/login.ts
|
|
1035
|
-
import { log as
|
|
1234
|
+
import { log as log3 } from "@clack/prompts";
|
|
1036
1235
|
|
|
1037
1236
|
// src/lib/device-flow.ts
|
|
1038
1237
|
var DEVICE_CODE_URL = "https://github.com/login/device/code";
|
|
@@ -1117,9 +1316,9 @@ async function login(options, deps = {}) {
|
|
|
1117
1316
|
const runFlow = deps.runDeviceFlow ?? runDeviceFlow;
|
|
1118
1317
|
const save = deps.saveToken ?? saveToken;
|
|
1119
1318
|
const load = deps.loadToken ?? loadToken;
|
|
1120
|
-
const success = deps.logSuccess ?? ((s) =>
|
|
1121
|
-
const info = deps.logInfo ?? ((s) =>
|
|
1122
|
-
const error = deps.logError ?? ((s) =>
|
|
1319
|
+
const success = deps.logSuccess ?? ((s) => log3.success(s));
|
|
1320
|
+
const info = deps.logInfo ?? ((s) => log3.info(s));
|
|
1321
|
+
const error = deps.logError ?? ((s) => log3.error(s));
|
|
1123
1322
|
const exit = deps.exit ?? exitWithCode;
|
|
1124
1323
|
const envClientId = env["UNIVERSE_GH_CLIENT_ID"];
|
|
1125
1324
|
const clientId = envClientId && envClientId.trim().length > 0 ? envClientId : DEFAULT_GH_CLIENT_ID;
|
|
@@ -1170,17 +1369,12 @@ async function login(options, deps = {}) {
|
|
|
1170
1369
|
});
|
|
1171
1370
|
} catch (err) {
|
|
1172
1371
|
const message = err instanceof Error ? err.message : String(err);
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
error: { code: EXIT_CREDENTIALS, message }
|
|
1180
|
-
});
|
|
1181
|
-
} else {
|
|
1182
|
-
error(message);
|
|
1183
|
-
}
|
|
1372
|
+
outputError(
|
|
1373
|
+
{ json: options.json, command: "login" },
|
|
1374
|
+
EXIT_CREDENTIALS,
|
|
1375
|
+
message,
|
|
1376
|
+
{ logError: error }
|
|
1377
|
+
);
|
|
1184
1378
|
exit(EXIT_CREDENTIALS);
|
|
1185
1379
|
return;
|
|
1186
1380
|
}
|
|
@@ -1193,15 +1387,15 @@ async function login(options, deps = {}) {
|
|
|
1193
1387
|
}
|
|
1194
1388
|
|
|
1195
1389
|
// src/commands/logout.ts
|
|
1196
|
-
import { log as
|
|
1390
|
+
import { log as log4 } from "@clack/prompts";
|
|
1197
1391
|
function emitJson3(envelope) {
|
|
1198
1392
|
process.stdout.write(JSON.stringify(envelope) + "\n");
|
|
1199
1393
|
}
|
|
1200
1394
|
async function logout(options, deps = {}) {
|
|
1201
1395
|
const load = deps.loadToken ?? loadToken;
|
|
1202
1396
|
const del = deps.deleteToken ?? deleteToken;
|
|
1203
|
-
const success = deps.logSuccess ?? ((s) =>
|
|
1204
|
-
const info = deps.logInfo ?? ((s) =>
|
|
1397
|
+
const success = deps.logSuccess ?? ((s) => log4.success(s));
|
|
1398
|
+
const info = deps.logInfo ?? ((s) => log4.info(s));
|
|
1205
1399
|
const existing = await load();
|
|
1206
1400
|
await del();
|
|
1207
1401
|
if (options.json) {
|
|
@@ -1218,14 +1412,14 @@ async function logout(options, deps = {}) {
|
|
|
1218
1412
|
// src/commands/ls.ts
|
|
1219
1413
|
import { readFile as readFile3 } from "fs/promises";
|
|
1220
1414
|
import { resolve as resolve3 } from "path";
|
|
1221
|
-
import { log as
|
|
1415
|
+
import { log as log5 } from "@clack/prompts";
|
|
1222
1416
|
var defaultReadPlatformYaml2 = async (cwd) => {
|
|
1223
1417
|
return readFile3(resolve3(cwd, "platform.yaml"), "utf-8");
|
|
1224
1418
|
};
|
|
1225
1419
|
function emitJson4(envelope) {
|
|
1226
1420
|
process.stdout.write(JSON.stringify(envelope) + "\n");
|
|
1227
1421
|
}
|
|
1228
|
-
var DEPLOY_ID_RE = /^(\d{8})-(\d{6})-(
|
|
1422
|
+
var DEPLOY_ID_RE = /^(\d{8})-(\d{6})-(\S+)$/;
|
|
1229
1423
|
function parseDeployId(deployId) {
|
|
1230
1424
|
const m = DEPLOY_ID_RE.exec(deployId);
|
|
1231
1425
|
if (!m) return { deployId, timestamp: null, sha: null };
|
|
@@ -1268,9 +1462,9 @@ async function ls(options, deps = {}) {
|
|
|
1268
1462
|
const readYaml = deps.readPlatformYaml ?? defaultReadPlatformYaml2;
|
|
1269
1463
|
const resolveId = deps.resolveIdentity ?? resolveIdentity;
|
|
1270
1464
|
const mkClient = deps.createProxyClient ?? createProxyClient;
|
|
1271
|
-
const success = deps.logSuccess ?? ((s) =>
|
|
1272
|
-
const info = deps.logInfo ?? ((s) =>
|
|
1273
|
-
const error = deps.logError ?? ((s) =>
|
|
1465
|
+
const success = deps.logSuccess ?? ((s) => log5.success(s));
|
|
1466
|
+
const info = deps.logInfo ?? ((s) => log5.info(s));
|
|
1467
|
+
const error = deps.logError ?? ((s) => log5.error(s));
|
|
1274
1468
|
const exit = deps.exit ?? exitWithCode;
|
|
1275
1469
|
try {
|
|
1276
1470
|
const identity = await resolveId({ env });
|
|
@@ -1291,7 +1485,8 @@ async function ls(options, deps = {}) {
|
|
|
1291
1485
|
const baseUrl = env["UNIVERSE_PROXY_URL"] ?? DEFAULT_PROXY_URL;
|
|
1292
1486
|
const client = mkClient({
|
|
1293
1487
|
baseUrl,
|
|
1294
|
-
getAuthToken: () => identity.token
|
|
1488
|
+
getAuthToken: () => identity.token,
|
|
1489
|
+
timeoutMs: parseFetchTimeoutMs(env)
|
|
1295
1490
|
});
|
|
1296
1491
|
const raw = await client.siteDeploys({ site });
|
|
1297
1492
|
const sorted = [...raw].sort(
|
|
@@ -1315,11 +1510,9 @@ async function ls(options, deps = {}) {
|
|
|
1315
1510
|
success(formatTable(deploys));
|
|
1316
1511
|
} catch (err) {
|
|
1317
1512
|
const { code, message } = wrapProxyError("ls", err);
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
}
|
|
1321
|
-
error(message);
|
|
1322
|
-
}
|
|
1513
|
+
outputError({ json: options.json, command: "ls" }, code, message, {
|
|
1514
|
+
logError: error
|
|
1515
|
+
});
|
|
1323
1516
|
exit(code);
|
|
1324
1517
|
}
|
|
1325
1518
|
}
|
|
@@ -1327,7 +1520,12 @@ async function ls(options, deps = {}) {
|
|
|
1327
1520
|
// src/commands/promote.ts
|
|
1328
1521
|
import { readFile as readFile4 } from "fs/promises";
|
|
1329
1522
|
import { resolve as resolve4 } from "path";
|
|
1330
|
-
import { log as
|
|
1523
|
+
import { confirm, isCancel, log as log6 } from "@clack/prompts";
|
|
1524
|
+
var defaultPromptConfirm = async (msg) => {
|
|
1525
|
+
const r = await confirm({ message: msg, initialValue: false });
|
|
1526
|
+
if (isCancel(r)) return false;
|
|
1527
|
+
return r === true;
|
|
1528
|
+
};
|
|
1331
1529
|
var defaultReadPlatformYaml3 = async (cwd) => {
|
|
1332
1530
|
return readFile4(resolve4(cwd, "platform.yaml"), "utf-8");
|
|
1333
1531
|
};
|
|
@@ -1356,9 +1554,10 @@ async function promote(options, deps = {}) {
|
|
|
1356
1554
|
const readYaml = deps.readPlatformYaml ?? defaultReadPlatformYaml3;
|
|
1357
1555
|
const resolveId = deps.resolveIdentity ?? resolveIdentity;
|
|
1358
1556
|
const mkClient = deps.createProxyClient ?? createProxyClient;
|
|
1359
|
-
const success = deps.logSuccess ?? ((s) =>
|
|
1360
|
-
const error = deps.logError ?? ((s) =>
|
|
1557
|
+
const success = deps.logSuccess ?? ((s) => log6.success(s));
|
|
1558
|
+
const error = deps.logError ?? ((s) => log6.error(s));
|
|
1361
1559
|
const exit = deps.exit ?? exitWithCode;
|
|
1560
|
+
const promptConfirm = deps.promptConfirm ?? defaultPromptConfirm;
|
|
1362
1561
|
try {
|
|
1363
1562
|
const identity = await resolveId({ env });
|
|
1364
1563
|
if (!identity) {
|
|
@@ -1370,16 +1569,80 @@ async function promote(options, deps = {}) {
|
|
|
1370
1569
|
const baseUrl = env["UNIVERSE_PROXY_URL"] ?? DEFAULT_PROXY_URL;
|
|
1371
1570
|
const client = mkClient({
|
|
1372
1571
|
baseUrl,
|
|
1373
|
-
getAuthToken: () => identity.token
|
|
1572
|
+
getAuthToken: () => identity.token,
|
|
1573
|
+
timeoutMs: parseFetchTimeoutMs(env)
|
|
1374
1574
|
});
|
|
1375
1575
|
let result;
|
|
1376
1576
|
if (options.from) {
|
|
1377
|
-
|
|
1577
|
+
const prod = await client.getAlias({
|
|
1378
1578
|
site: config.site,
|
|
1379
|
-
|
|
1579
|
+
mode: "production"
|
|
1380
1580
|
});
|
|
1581
|
+
const initialExpected = prod?.deployId ?? "";
|
|
1582
|
+
try {
|
|
1583
|
+
result = await client.siteRollback({
|
|
1584
|
+
site: config.site,
|
|
1585
|
+
to: options.from,
|
|
1586
|
+
expectedCurrent: initialExpected
|
|
1587
|
+
});
|
|
1588
|
+
} catch (err) {
|
|
1589
|
+
if (!(err instanceof AliasDriftError)) throw err;
|
|
1590
|
+
if (options.json) throw err;
|
|
1591
|
+
error(
|
|
1592
|
+
`drift: production moved to ${err.current}, expected ${initialExpected}`
|
|
1593
|
+
);
|
|
1594
|
+
const ok = await promptConfirm(
|
|
1595
|
+
`Retry promote --from with expectedCurrent='${err.current}'?`
|
|
1596
|
+
);
|
|
1597
|
+
if (!ok) throw err;
|
|
1598
|
+
result = await client.siteRollback({
|
|
1599
|
+
site: config.site,
|
|
1600
|
+
to: options.from,
|
|
1601
|
+
expectedCurrent: err.current
|
|
1602
|
+
});
|
|
1603
|
+
}
|
|
1381
1604
|
} else {
|
|
1382
|
-
|
|
1605
|
+
const preview = await client.getAlias({
|
|
1606
|
+
site: config.site,
|
|
1607
|
+
mode: "preview"
|
|
1608
|
+
});
|
|
1609
|
+
if (preview === null) {
|
|
1610
|
+
throw new ConfigError(
|
|
1611
|
+
"no preview alias to promote \u2014 run `universe static deploy` first"
|
|
1612
|
+
);
|
|
1613
|
+
}
|
|
1614
|
+
const prod = await client.getAlias({
|
|
1615
|
+
site: config.site,
|
|
1616
|
+
mode: "production"
|
|
1617
|
+
});
|
|
1618
|
+
if (!options.json) {
|
|
1619
|
+
success(
|
|
1620
|
+
`Promoting ${preview.deployId} \u2192 ${prod?.deployId ?? "<none>"}`
|
|
1621
|
+
);
|
|
1622
|
+
}
|
|
1623
|
+
const initialExpected = prod?.deployId ?? "";
|
|
1624
|
+
try {
|
|
1625
|
+
result = await client.sitePromote({
|
|
1626
|
+
site: config.site,
|
|
1627
|
+
deployId: preview.deployId,
|
|
1628
|
+
expectedCurrent: initialExpected
|
|
1629
|
+
});
|
|
1630
|
+
} catch (err) {
|
|
1631
|
+
if (!(err instanceof AliasDriftError)) throw err;
|
|
1632
|
+
if (options.json) throw err;
|
|
1633
|
+
error(
|
|
1634
|
+
`drift: production moved to ${err.current}, expected ${initialExpected}`
|
|
1635
|
+
);
|
|
1636
|
+
const ok = await promptConfirm(
|
|
1637
|
+
`Retry promote with expectedCurrent='${err.current}'?`
|
|
1638
|
+
);
|
|
1639
|
+
if (!ok) throw err;
|
|
1640
|
+
result = await client.sitePromote({
|
|
1641
|
+
site: config.site,
|
|
1642
|
+
deployId: preview.deployId,
|
|
1643
|
+
expectedCurrent: err.current
|
|
1644
|
+
});
|
|
1645
|
+
}
|
|
1383
1646
|
}
|
|
1384
1647
|
if (options.json) {
|
|
1385
1648
|
emitJson5(
|
|
@@ -1405,11 +1668,11 @@ async function promote(options, deps = {}) {
|
|
|
1405
1668
|
}
|
|
1406
1669
|
} catch (err) {
|
|
1407
1670
|
const { code, message } = wrapProxyError("promote", err);
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
}
|
|
1671
|
+
const extras = err instanceof AliasDriftError ? { current: err.current } : void 0;
|
|
1672
|
+
outputError({ json: options.json, command: "promote" }, code, message, {
|
|
1673
|
+
logError: error,
|
|
1674
|
+
extras
|
|
1675
|
+
});
|
|
1413
1676
|
exit(code);
|
|
1414
1677
|
}
|
|
1415
1678
|
}
|
|
@@ -1417,7 +1680,12 @@ async function promote(options, deps = {}) {
|
|
|
1417
1680
|
// src/commands/rollback.ts
|
|
1418
1681
|
import { readFile as readFile5 } from "fs/promises";
|
|
1419
1682
|
import { resolve as resolve5 } from "path";
|
|
1420
|
-
import { log as
|
|
1683
|
+
import { confirm as confirm2, isCancel as isCancel2, log as log7 } from "@clack/prompts";
|
|
1684
|
+
var defaultPromptConfirm2 = async (msg) => {
|
|
1685
|
+
const r = await confirm2({ message: msg, initialValue: false });
|
|
1686
|
+
if (isCancel2(r)) return false;
|
|
1687
|
+
return r === true;
|
|
1688
|
+
};
|
|
1421
1689
|
var defaultReadPlatformYaml4 = async (cwd) => {
|
|
1422
1690
|
return readFile5(resolve5(cwd, "platform.yaml"), "utf-8");
|
|
1423
1691
|
};
|
|
@@ -1446,9 +1714,10 @@ async function rollback(options, deps = {}) {
|
|
|
1446
1714
|
const readYaml = deps.readPlatformYaml ?? defaultReadPlatformYaml4;
|
|
1447
1715
|
const resolveId = deps.resolveIdentity ?? resolveIdentity;
|
|
1448
1716
|
const mkClient = deps.createProxyClient ?? createProxyClient;
|
|
1449
|
-
const success = deps.logSuccess ?? ((s) =>
|
|
1450
|
-
const error = deps.logError ?? ((s) =>
|
|
1717
|
+
const success = deps.logSuccess ?? ((s) => log7.success(s));
|
|
1718
|
+
const error = deps.logError ?? ((s) => log7.error(s));
|
|
1451
1719
|
const exit = deps.exit ?? exitWithCode;
|
|
1720
|
+
const promptConfirm = deps.promptConfirm ?? defaultPromptConfirm2;
|
|
1452
1721
|
try {
|
|
1453
1722
|
if (!options.to || options.to.trim().length === 0) {
|
|
1454
1723
|
throw new UsageError(
|
|
@@ -1465,12 +1734,38 @@ async function rollback(options, deps = {}) {
|
|
|
1465
1734
|
const baseUrl = env["UNIVERSE_PROXY_URL"] ?? DEFAULT_PROXY_URL;
|
|
1466
1735
|
const client = mkClient({
|
|
1467
1736
|
baseUrl,
|
|
1468
|
-
getAuthToken: () => identity.token
|
|
1737
|
+
getAuthToken: () => identity.token,
|
|
1738
|
+
timeoutMs: parseFetchTimeoutMs(env)
|
|
1469
1739
|
});
|
|
1470
|
-
const
|
|
1740
|
+
const to = options.to.trim();
|
|
1741
|
+
const prod = await client.getAlias({
|
|
1471
1742
|
site: config.site,
|
|
1472
|
-
|
|
1743
|
+
mode: "production"
|
|
1473
1744
|
});
|
|
1745
|
+
const initialExpected = prod?.deployId ?? "";
|
|
1746
|
+
let result;
|
|
1747
|
+
try {
|
|
1748
|
+
result = await client.siteRollback({
|
|
1749
|
+
site: config.site,
|
|
1750
|
+
to,
|
|
1751
|
+
expectedCurrent: initialExpected
|
|
1752
|
+
});
|
|
1753
|
+
} catch (err) {
|
|
1754
|
+
if (!(err instanceof AliasDriftError)) throw err;
|
|
1755
|
+
if (options.json) throw err;
|
|
1756
|
+
error(
|
|
1757
|
+
`drift: production moved to ${err.current}, expected ${initialExpected}`
|
|
1758
|
+
);
|
|
1759
|
+
const ok = await promptConfirm(
|
|
1760
|
+
`Retry rollback with expectedCurrent='${err.current}'?`
|
|
1761
|
+
);
|
|
1762
|
+
if (!ok) throw err;
|
|
1763
|
+
result = await client.siteRollback({
|
|
1764
|
+
site: config.site,
|
|
1765
|
+
to,
|
|
1766
|
+
expectedCurrent: err.current
|
|
1767
|
+
});
|
|
1768
|
+
}
|
|
1474
1769
|
if (options.json) {
|
|
1475
1770
|
emitJson6(
|
|
1476
1771
|
buildEnvelope("rollback", true, {
|
|
@@ -1493,17 +1788,17 @@ async function rollback(options, deps = {}) {
|
|
|
1493
1788
|
}
|
|
1494
1789
|
} catch (err) {
|
|
1495
1790
|
const { code, message } = wrapProxyError("rollback", err);
|
|
1496
|
-
|
|
1497
|
-
|
|
1498
|
-
|
|
1499
|
-
|
|
1500
|
-
}
|
|
1791
|
+
const extras = err instanceof AliasDriftError ? { current: err.current } : void 0;
|
|
1792
|
+
outputError({ json: options.json, command: "rollback" }, code, message, {
|
|
1793
|
+
logError: error,
|
|
1794
|
+
extras
|
|
1795
|
+
});
|
|
1501
1796
|
exit(code);
|
|
1502
1797
|
}
|
|
1503
1798
|
}
|
|
1504
1799
|
|
|
1505
1800
|
// src/commands/whoami.ts
|
|
1506
|
-
import { log as
|
|
1801
|
+
import { log as log8 } from "@clack/prompts";
|
|
1507
1802
|
var DEFAULT_PROXY_URL2 = "https://uploads.freecode.camp";
|
|
1508
1803
|
function emitJson7(envelope) {
|
|
1509
1804
|
process.stdout.write(JSON.stringify(envelope) + "\n");
|
|
@@ -1512,24 +1807,26 @@ async function whoami(options, deps = {}) {
|
|
|
1512
1807
|
const env = deps.env ?? process.env;
|
|
1513
1808
|
const resolve6 = deps.resolveIdentity ?? resolveIdentity;
|
|
1514
1809
|
const mkClient = deps.createProxyClient ?? createProxyClient;
|
|
1515
|
-
const success = deps.logSuccess ?? ((s) =>
|
|
1516
|
-
const error = deps.logError ?? ((s) =>
|
|
1810
|
+
const success = deps.logSuccess ?? ((s) => log8.success(s));
|
|
1811
|
+
const error = deps.logError ?? ((s) => log8.error(s));
|
|
1517
1812
|
const exit = deps.exit ?? exitWithCode;
|
|
1518
1813
|
const identity = await resolve6({ env });
|
|
1519
1814
|
if (!identity) {
|
|
1520
1815
|
const msg = "No GitHub identity available. Run `universe login`, set $GITHUB_TOKEN, or install the gh CLI.";
|
|
1521
|
-
|
|
1522
|
-
|
|
1523
|
-
|
|
1524
|
-
|
|
1525
|
-
|
|
1816
|
+
outputError(
|
|
1817
|
+
{ json: options.json, command: "whoami" },
|
|
1818
|
+
EXIT_CREDENTIALS,
|
|
1819
|
+
msg,
|
|
1820
|
+
{ logError: error }
|
|
1821
|
+
);
|
|
1526
1822
|
exit(EXIT_CREDENTIALS);
|
|
1527
1823
|
return;
|
|
1528
1824
|
}
|
|
1529
1825
|
const baseUrl = env["UNIVERSE_PROXY_URL"] ?? DEFAULT_PROXY_URL2;
|
|
1530
1826
|
const client = mkClient({
|
|
1531
1827
|
baseUrl,
|
|
1532
|
-
getAuthToken: () => identity.token
|
|
1828
|
+
getAuthToken: () => identity.token,
|
|
1829
|
+
timeoutMs: parseFetchTimeoutMs(env)
|
|
1533
1830
|
});
|
|
1534
1831
|
try {
|
|
1535
1832
|
const result = await client.whoami();
|
|
@@ -1555,17 +1852,15 @@ async function whoami(options, deps = {}) {
|
|
|
1555
1852
|
} catch (err) {
|
|
1556
1853
|
const exitCode = err instanceof CliError ? err.exitCode : EXIT_CREDENTIALS;
|
|
1557
1854
|
const message = err instanceof ProxyError ? `whoami failed (${err.code}): ${err.message}` : err instanceof Error ? err.message : String(err);
|
|
1558
|
-
|
|
1559
|
-
|
|
1560
|
-
}
|
|
1561
|
-
error(message);
|
|
1562
|
-
}
|
|
1855
|
+
outputError({ json: options.json, command: "whoami" }, exitCode, message, {
|
|
1856
|
+
logError: error
|
|
1857
|
+
});
|
|
1563
1858
|
exit(exitCode);
|
|
1564
1859
|
}
|
|
1565
1860
|
}
|
|
1566
1861
|
|
|
1567
1862
|
// src/commands/sites/ls.ts
|
|
1568
|
-
import { log as
|
|
1863
|
+
import { log as log9 } from "@clack/prompts";
|
|
1569
1864
|
|
|
1570
1865
|
// src/commands/sites/_shared.ts
|
|
1571
1866
|
function emitJson8(envelope) {
|
|
@@ -1589,7 +1884,8 @@ async function setupClient(deps) {
|
|
|
1589
1884
|
const baseUrl = env["UNIVERSE_PROXY_URL"] ?? DEFAULT_PROXY_URL;
|
|
1590
1885
|
const client = mkClient({
|
|
1591
1886
|
baseUrl,
|
|
1592
|
-
getAuthToken: () => identity.token
|
|
1887
|
+
getAuthToken: () => identity.token,
|
|
1888
|
+
timeoutMs: parseFetchTimeoutMs(env)
|
|
1593
1889
|
});
|
|
1594
1890
|
return { client, identitySource: identity.source };
|
|
1595
1891
|
}
|
|
@@ -1612,11 +1908,11 @@ function formatTable2(rows) {
|
|
|
1612
1908
|
}
|
|
1613
1909
|
async function ls2(options, deps = {}) {
|
|
1614
1910
|
const command = "sites ls";
|
|
1615
|
-
const success = deps.logSuccess ?? ((s) =>
|
|
1616
|
-
const error = deps.logError ?? ((s) =>
|
|
1911
|
+
const success = deps.logSuccess ?? ((s) => log9.message(s));
|
|
1912
|
+
const error = deps.logError ?? ((s) => log9.error(s));
|
|
1617
1913
|
const exit = deps.exit ?? exitWithCode;
|
|
1618
1914
|
try {
|
|
1619
|
-
const { client } = await setupClient(deps);
|
|
1915
|
+
const { client, identitySource } = await setupClient(deps);
|
|
1620
1916
|
let rows = await client.listSites();
|
|
1621
1917
|
let scope = "all";
|
|
1622
1918
|
if (options.mine) {
|
|
@@ -1630,7 +1926,8 @@ async function ls2(options, deps = {}) {
|
|
|
1630
1926
|
buildEnvelope(command, true, {
|
|
1631
1927
|
count: rows.length,
|
|
1632
1928
|
scope,
|
|
1633
|
-
sites: rows
|
|
1929
|
+
sites: rows,
|
|
1930
|
+
identitySource
|
|
1634
1931
|
})
|
|
1635
1932
|
);
|
|
1636
1933
|
} else {
|
|
@@ -1638,28 +1935,26 @@ async function ls2(options, deps = {}) {
|
|
|
1638
1935
|
}
|
|
1639
1936
|
} catch (err) {
|
|
1640
1937
|
const { code, message } = wrapProxyError(command, err);
|
|
1641
|
-
|
|
1642
|
-
|
|
1643
|
-
}
|
|
1644
|
-
error(message);
|
|
1645
|
-
}
|
|
1938
|
+
outputError({ json: options.json, command }, code, message, {
|
|
1939
|
+
logError: error
|
|
1940
|
+
});
|
|
1646
1941
|
exit(code);
|
|
1647
1942
|
}
|
|
1648
1943
|
}
|
|
1649
1944
|
|
|
1650
1945
|
// src/commands/sites/register.ts
|
|
1651
|
-
import { log as
|
|
1946
|
+
import { log as log10 } from "@clack/prompts";
|
|
1652
1947
|
async function register(options, deps = {}) {
|
|
1653
1948
|
const command = "sites register";
|
|
1654
|
-
const success = deps.logSuccess ?? ((s) =>
|
|
1655
|
-
const error = deps.logError ?? ((s) =>
|
|
1949
|
+
const success = deps.logSuccess ?? ((s) => log10.success(s));
|
|
1950
|
+
const error = deps.logError ?? ((s) => log10.error(s));
|
|
1656
1951
|
const exit = deps.exit ?? exitWithCode;
|
|
1657
1952
|
try {
|
|
1658
1953
|
if (!options.slug || options.slug.trim().length === 0) {
|
|
1659
1954
|
throw new UsageError("slug is required (positional argument)");
|
|
1660
1955
|
}
|
|
1661
1956
|
const teams = parseTeamsFlag(options.team);
|
|
1662
|
-
const { client } = await setupClient(deps);
|
|
1957
|
+
const { client, identitySource } = await setupClient(deps);
|
|
1663
1958
|
const row = await client.registerSite({
|
|
1664
1959
|
slug: options.slug,
|
|
1665
1960
|
teams: teams.length > 0 ? teams : void 0
|
|
@@ -1670,7 +1965,8 @@ async function register(options, deps = {}) {
|
|
|
1670
1965
|
slug: row.slug,
|
|
1671
1966
|
teams: row.teams,
|
|
1672
1967
|
createdAt: row.createdAt,
|
|
1673
|
-
createdBy: row.createdBy
|
|
1968
|
+
createdBy: row.createdBy,
|
|
1969
|
+
identitySource
|
|
1674
1970
|
})
|
|
1675
1971
|
);
|
|
1676
1972
|
} else {
|
|
@@ -1687,33 +1983,32 @@ async function register(options, deps = {}) {
|
|
|
1687
1983
|
}
|
|
1688
1984
|
} catch (err) {
|
|
1689
1985
|
const { code, message } = wrapProxyError(command, err);
|
|
1690
|
-
|
|
1691
|
-
|
|
1692
|
-
}
|
|
1693
|
-
error(message);
|
|
1694
|
-
}
|
|
1986
|
+
outputError({ json: options.json, command }, code, message, {
|
|
1987
|
+
logError: error
|
|
1988
|
+
});
|
|
1695
1989
|
exit(code);
|
|
1696
1990
|
}
|
|
1697
1991
|
}
|
|
1698
1992
|
|
|
1699
1993
|
// src/commands/sites/rm.ts
|
|
1700
|
-
import { log as
|
|
1994
|
+
import { log as log11 } from "@clack/prompts";
|
|
1701
1995
|
async function rm2(options, deps = {}) {
|
|
1702
1996
|
const command = "sites rm";
|
|
1703
|
-
const success = deps.logSuccess ?? ((s) =>
|
|
1704
|
-
const error = deps.logError ?? ((s) =>
|
|
1997
|
+
const success = deps.logSuccess ?? ((s) => log11.success(s));
|
|
1998
|
+
const error = deps.logError ?? ((s) => log11.error(s));
|
|
1705
1999
|
const exit = deps.exit ?? exitWithCode;
|
|
1706
2000
|
try {
|
|
1707
2001
|
if (!options.slug || options.slug.trim().length === 0) {
|
|
1708
2002
|
throw new UsageError("slug is required (positional argument)");
|
|
1709
2003
|
}
|
|
1710
|
-
const { client } = await setupClient(deps);
|
|
2004
|
+
const { client, identitySource } = await setupClient(deps);
|
|
1711
2005
|
await client.deleteSite({ slug: options.slug });
|
|
1712
2006
|
if (options.json) {
|
|
1713
2007
|
emitJson8(
|
|
1714
2008
|
buildEnvelope(command, true, {
|
|
1715
2009
|
slug: options.slug,
|
|
1716
|
-
deleted: true
|
|
2010
|
+
deleted: true,
|
|
2011
|
+
identitySource
|
|
1717
2012
|
})
|
|
1718
2013
|
);
|
|
1719
2014
|
} else {
|
|
@@ -1728,21 +2023,19 @@ async function rm2(options, deps = {}) {
|
|
|
1728
2023
|
}
|
|
1729
2024
|
} catch (err) {
|
|
1730
2025
|
const { code, message } = wrapProxyError(command, err);
|
|
1731
|
-
|
|
1732
|
-
|
|
1733
|
-
}
|
|
1734
|
-
error(message);
|
|
1735
|
-
}
|
|
2026
|
+
outputError({ json: options.json, command }, code, message, {
|
|
2027
|
+
logError: error
|
|
2028
|
+
});
|
|
1736
2029
|
exit(code);
|
|
1737
2030
|
}
|
|
1738
2031
|
}
|
|
1739
2032
|
|
|
1740
2033
|
// src/commands/sites/update.ts
|
|
1741
|
-
import { log as
|
|
2034
|
+
import { log as log12 } from "@clack/prompts";
|
|
1742
2035
|
async function update(options, deps = {}) {
|
|
1743
2036
|
const command = "sites update";
|
|
1744
|
-
const success = deps.logSuccess ?? ((s) =>
|
|
1745
|
-
const error = deps.logError ?? ((s) =>
|
|
2037
|
+
const success = deps.logSuccess ?? ((s) => log12.success(s));
|
|
2038
|
+
const error = deps.logError ?? ((s) => log12.error(s));
|
|
1746
2039
|
const exit = deps.exit ?? exitWithCode;
|
|
1747
2040
|
try {
|
|
1748
2041
|
if (!options.slug || options.slug.trim().length === 0) {
|
|
@@ -1754,7 +2047,7 @@ async function update(options, deps = {}) {
|
|
|
1754
2047
|
"--team is required with at least one slug; use `sites rm` to remove a site"
|
|
1755
2048
|
);
|
|
1756
2049
|
}
|
|
1757
|
-
const { client } = await setupClient(deps);
|
|
2050
|
+
const { client, identitySource } = await setupClient(deps);
|
|
1758
2051
|
const row = await client.updateSite({
|
|
1759
2052
|
slug: options.slug,
|
|
1760
2053
|
teams
|
|
@@ -1764,7 +2057,8 @@ async function update(options, deps = {}) {
|
|
|
1764
2057
|
buildEnvelope(command, true, {
|
|
1765
2058
|
slug: row.slug,
|
|
1766
2059
|
teams: row.teams,
|
|
1767
|
-
updatedAt: row.updatedAt
|
|
2060
|
+
updatedAt: row.updatedAt,
|
|
2061
|
+
identitySource
|
|
1768
2062
|
})
|
|
1769
2063
|
);
|
|
1770
2064
|
} else {
|
|
@@ -1780,81 +2074,15 @@ async function update(options, deps = {}) {
|
|
|
1780
2074
|
}
|
|
1781
2075
|
} catch (err) {
|
|
1782
2076
|
const { code, message } = wrapProxyError(command, err);
|
|
1783
|
-
|
|
1784
|
-
|
|
1785
|
-
}
|
|
1786
|
-
error(message);
|
|
1787
|
-
}
|
|
2077
|
+
outputError({ json: options.json, command }, code, message, {
|
|
2078
|
+
logError: error
|
|
2079
|
+
});
|
|
1788
2080
|
exit(code);
|
|
1789
2081
|
}
|
|
1790
2082
|
}
|
|
1791
2083
|
|
|
1792
|
-
// src/output/format.ts
|
|
1793
|
-
import { log as log12 } from "@clack/prompts";
|
|
1794
|
-
|
|
1795
|
-
// src/output/redact.ts
|
|
1796
|
-
var AWS_KEY_PREFIX_PATTERN = /(?:AKIA|ASIA|AROA|AIDA|ACCA|ANPA|ABIA|AGPA)[A-Z0-9]{12,}/g;
|
|
1797
|
-
var URL_CREDS_PATTERN = /https?:\/\/[^@\s]+@/g;
|
|
1798
|
-
var CREDENTIAL_CONTEXT_PATTERN = /(?:access_key_id|secret_access_key|accessKeyId|secretAccessKey|secret|password|token|key|credential|auth)\s*[=:]\s*([A-Za-z0-9/+=]{21,})/gi;
|
|
1799
|
-
var HEX_CREDENTIAL_CONTEXT_PATTERN = /(?:secret|password|token|key|credential|auth|access_key_id|secret_access_key)\s*[=:]\s*([a-f0-9]{32,})/gi;
|
|
1800
|
-
var JSON_CREDENTIAL_PATTERN = /"(?:secret|password|token|key|credential|auth|access_key_id|secret_access_key|accessKeyId|secretAccessKey)"\s*:\s*"[^"]+"/gi;
|
|
1801
|
-
var BEARER_PATTERN = /\bBearer\s+[A-Za-z0-9._~+/=-]+/gi;
|
|
1802
|
-
function maskAwsKey(match) {
|
|
1803
|
-
return match.slice(0, 4) + "****" + match.slice(-4);
|
|
1804
|
-
}
|
|
1805
|
-
function maskUrlCreds(match) {
|
|
1806
|
-
const atIndex = match.lastIndexOf("@");
|
|
1807
|
-
const protocolEnd = match.indexOf("://") + 3;
|
|
1808
|
-
return match.slice(0, protocolEnd) + "****:****@" + match.slice(atIndex + 1);
|
|
1809
|
-
}
|
|
1810
|
-
function redact(value) {
|
|
1811
|
-
let result = value;
|
|
1812
|
-
result = result.replace(URL_CREDS_PATTERN, maskUrlCreds);
|
|
1813
|
-
result = result.replace(AWS_KEY_PREFIX_PATTERN, maskAwsKey);
|
|
1814
|
-
result = result.replace(BEARER_PATTERN, "Bearer ****");
|
|
1815
|
-
result = result.replace(JSON_CREDENTIAL_PATTERN, (match) => {
|
|
1816
|
-
const colonIndex = match.indexOf(":");
|
|
1817
|
-
return match.slice(0, colonIndex + 1) + '"****"';
|
|
1818
|
-
});
|
|
1819
|
-
result = result.replace(
|
|
1820
|
-
CREDENTIAL_CONTEXT_PATTERN,
|
|
1821
|
-
(_match, _secret, _offset, _full) => {
|
|
1822
|
-
const eqIndex = _match.indexOf("=");
|
|
1823
|
-
const colonIndex = _match.indexOf(":");
|
|
1824
|
-
const sepIndex = eqIndex >= 0 && colonIndex >= 0 ? Math.min(eqIndex, colonIndex) : eqIndex >= 0 ? eqIndex : colonIndex;
|
|
1825
|
-
const prefix = _match.slice(0, sepIndex + 1);
|
|
1826
|
-
return prefix + "****";
|
|
1827
|
-
}
|
|
1828
|
-
);
|
|
1829
|
-
result = result.replace(HEX_CREDENTIAL_CONTEXT_PATTERN, (_match, _secret) => {
|
|
1830
|
-
const eqIndex = _match.indexOf("=");
|
|
1831
|
-
const colonIndex = _match.indexOf(":");
|
|
1832
|
-
const sepIndex = eqIndex >= 0 && colonIndex >= 0 ? Math.min(eqIndex, colonIndex) : eqIndex >= 0 ? eqIndex : colonIndex;
|
|
1833
|
-
const prefix = _match.slice(0, sepIndex + 1);
|
|
1834
|
-
return prefix + "****";
|
|
1835
|
-
});
|
|
1836
|
-
return result;
|
|
1837
|
-
}
|
|
1838
|
-
|
|
1839
|
-
// src/output/format.ts
|
|
1840
|
-
function outputError(ctx, code, message, issues) {
|
|
1841
|
-
const redactedMessage = redact(message);
|
|
1842
|
-
const redactedIssues = issues?.map(redact);
|
|
1843
|
-
if (ctx.json) {
|
|
1844
|
-
const envelope = buildErrorEnvelope(
|
|
1845
|
-
ctx.command,
|
|
1846
|
-
code,
|
|
1847
|
-
redactedMessage,
|
|
1848
|
-
redactedIssues
|
|
1849
|
-
);
|
|
1850
|
-
process.stdout.write(JSON.stringify(envelope) + "\n");
|
|
1851
|
-
} else {
|
|
1852
|
-
log12.error(redactedMessage);
|
|
1853
|
-
}
|
|
1854
|
-
}
|
|
1855
|
-
|
|
1856
2084
|
// src/cli.ts
|
|
1857
|
-
var version = true ? "0.
|
|
2085
|
+
var version = true ? "0.7.0" : "0.0.0";
|
|
1858
2086
|
function handleActionError(command, json, err) {
|
|
1859
2087
|
const ctx = { json, command };
|
|
1860
2088
|
const message = err instanceof Error ? err.message : "unknown error";
|