@cliftonc/cfld 0.2.5 → 0.2.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.js
CHANGED
|
@@ -3,12 +3,12 @@
|
|
|
3
3
|
// src/cli.ts
|
|
4
4
|
import { parseArgs } from "util";
|
|
5
5
|
import { readFileSync as readFileSync7, realpathSync } from "fs";
|
|
6
|
-
import { dirname, join as
|
|
6
|
+
import { dirname, join as join8 } from "path";
|
|
7
7
|
import { fileURLToPath } from "url";
|
|
8
8
|
import pc7 from "picocolors";
|
|
9
9
|
|
|
10
10
|
// src/commands/run.ts
|
|
11
|
-
import { join as
|
|
11
|
+
import { join as join6 } from "path";
|
|
12
12
|
|
|
13
13
|
// src/core/state-machine.ts
|
|
14
14
|
import { existsSync as existsSync2 } from "fs";
|
|
@@ -392,6 +392,24 @@ function certHasZone(certPath) {
|
|
|
392
392
|
const token = readCertToken(certPath);
|
|
393
393
|
return Boolean(token?.zoneID);
|
|
394
394
|
}
|
|
395
|
+
async function certZoneName(certPath) {
|
|
396
|
+
const token = readCertToken(certPath);
|
|
397
|
+
if (!token?.zoneID || !token.apiToken) return void 0;
|
|
398
|
+
try {
|
|
399
|
+
const res = await fetch(
|
|
400
|
+
`https://api.cloudflare.com/client/v4/zones/${token.zoneID}`,
|
|
401
|
+
{
|
|
402
|
+
headers: { Authorization: `Bearer ${token.apiToken}` },
|
|
403
|
+
signal: AbortSignal.timeout(5e3)
|
|
404
|
+
}
|
|
405
|
+
);
|
|
406
|
+
if (!res.ok) return void 0;
|
|
407
|
+
const body = await res.json();
|
|
408
|
+
return body.result?.name?.toLowerCase();
|
|
409
|
+
} catch {
|
|
410
|
+
return void 0;
|
|
411
|
+
}
|
|
412
|
+
}
|
|
395
413
|
|
|
396
414
|
// src/core/cert-store.ts
|
|
397
415
|
function ensureStoreDir() {
|
|
@@ -408,8 +426,19 @@ function listStoredZones() {
|
|
|
408
426
|
return [];
|
|
409
427
|
}
|
|
410
428
|
}
|
|
429
|
+
function storedZoneClaiming(certPath, except) {
|
|
430
|
+
const zoneID = readCertToken(certPath)?.zoneID;
|
|
431
|
+
if (!zoneID) return void 0;
|
|
432
|
+
return listStoredZones().find(
|
|
433
|
+
(z) => z !== except && readCertToken(certPathForZone(z))?.zoneID === zoneID
|
|
434
|
+
);
|
|
435
|
+
}
|
|
411
436
|
function resolveCert(zone) {
|
|
412
|
-
|
|
437
|
+
const stored = storedCertForZone(zone);
|
|
438
|
+
if (stored) return stored;
|
|
439
|
+
const def = defaultCertPath();
|
|
440
|
+
if (!certExists(def) || storedZoneClaiming(def, zone)) return void 0;
|
|
441
|
+
return def;
|
|
413
442
|
}
|
|
414
443
|
function importCert(zone, fromPath = defaultCertPath()) {
|
|
415
444
|
ensureStoreDir();
|
|
@@ -936,7 +965,8 @@ function colorLevel(level) {
|
|
|
936
965
|
|
|
937
966
|
// src/commands/login.ts
|
|
938
967
|
import { spawn as spawn4 } from "child_process";
|
|
939
|
-
import { existsSync as existsSync8 } from "fs";
|
|
968
|
+
import { copyFileSync as copyFileSync2, existsSync as existsSync8, mkdirSync as mkdirSync3, renameSync, rmSync } from "fs";
|
|
969
|
+
import { join as join4 } from "path";
|
|
940
970
|
|
|
941
971
|
// src/ui/prompts.ts
|
|
942
972
|
async function clack() {
|
|
@@ -1031,23 +1061,73 @@ function normalizeZone(input) {
|
|
|
1031
1061
|
return input.trim().toLowerCase().replace(/^https?:\/\//, "").replace(/\/.*$/, "");
|
|
1032
1062
|
}
|
|
1033
1063
|
async function loginCommand(flags) {
|
|
1034
|
-
const interactive = isInteractive();
|
|
1035
1064
|
const haveCert = certExists(defaultCertPath());
|
|
1065
|
+
let certPath = defaultCertPath();
|
|
1036
1066
|
if (flags.reauth || !haveCert) {
|
|
1037
|
-
await runLogin();
|
|
1067
|
+
certPath = await runLogin();
|
|
1038
1068
|
} else {
|
|
1039
1069
|
info("A Cloudflare certificate already exists. Use --reauth to authorize another domain.");
|
|
1040
1070
|
}
|
|
1041
|
-
|
|
1042
|
-
|
|
1071
|
+
try {
|
|
1072
|
+
if (!certHasZone(certPath)) {
|
|
1073
|
+
printZeroZoneGuidance();
|
|
1074
|
+
throw new CfldError(
|
|
1075
|
+
"Logged in, but your account has no domain to authorize.",
|
|
1076
|
+
"Add a domain (see above), then run `cfld login --reauth`."
|
|
1077
|
+
);
|
|
1078
|
+
}
|
|
1079
|
+
const zone = await saveCert(flags.zone ? normalizeZone(flags.zone) : void 0, certPath);
|
|
1080
|
+
done(`Saved cert for ${zone}. You're ready \u2014 run \`cfld\`.`);
|
|
1081
|
+
} finally {
|
|
1082
|
+
discardLoginCert(certPath);
|
|
1083
|
+
}
|
|
1084
|
+
}
|
|
1085
|
+
async function runLogin() {
|
|
1086
|
+
const bin = await resolveBinary();
|
|
1087
|
+
const def = defaultCertPath();
|
|
1088
|
+
const backup = existsSync8(def) ? `${def}.cfld-backup` : void 0;
|
|
1089
|
+
if (backup) renameSync(def, backup);
|
|
1090
|
+
try {
|
|
1091
|
+
step("Opening your browser to authorize with Cloudflare (no API token needed)\u2026");
|
|
1092
|
+
note(" Pick the domain you want to use. Waiting for you to finish\u2026");
|
|
1093
|
+
await new Promise((resolve, reject) => {
|
|
1094
|
+
const child = spawn4(bin, ["tunnel", "login"], { stdio: "inherit" });
|
|
1095
|
+
child.on("error", reject);
|
|
1096
|
+
child.on("exit", (code) => {
|
|
1097
|
+
if (code === 0 || existsSync8(def)) resolve();
|
|
1098
|
+
else reject(new CfldError("Cloudflare login did not complete.", "Re-run `cfld login`."));
|
|
1099
|
+
});
|
|
1100
|
+
});
|
|
1101
|
+
if (!existsSync8(def)) {
|
|
1102
|
+
throw new CfldError(
|
|
1103
|
+
"Login finished but no certificate was written.",
|
|
1104
|
+
"Re-run `cfld login` and complete the browser step."
|
|
1105
|
+
);
|
|
1106
|
+
}
|
|
1107
|
+
if (!backup) return def;
|
|
1108
|
+
mkdirSync3(cfldDir(), { recursive: true });
|
|
1109
|
+
const fresh = join4(cfldDir(), "login-cert.tmp");
|
|
1110
|
+
copyFileSync2(def, fresh);
|
|
1111
|
+
rmSync(def);
|
|
1112
|
+
return fresh;
|
|
1113
|
+
} finally {
|
|
1114
|
+
if (backup) renameSync(backup, def);
|
|
1115
|
+
}
|
|
1116
|
+
}
|
|
1117
|
+
function discardLoginCert(certPath) {
|
|
1118
|
+
if (certPath !== defaultCertPath()) rmSync(certPath, { force: true });
|
|
1119
|
+
}
|
|
1120
|
+
async function saveCert(expected, certPath) {
|
|
1121
|
+
const actual = await certZoneName(certPath);
|
|
1122
|
+
if (expected && actual && actual !== expected) {
|
|
1043
1123
|
throw new CfldError(
|
|
1044
|
-
|
|
1045
|
-
|
|
1124
|
+
`You authorized ${actual} in the browser, not ${expected}.`,
|
|
1125
|
+
`Run \`cfld login --reauth --zone ${expected}\` and pick ${expected} this time.`
|
|
1046
1126
|
);
|
|
1047
1127
|
}
|
|
1048
|
-
let zone =
|
|
1128
|
+
let zone = expected ?? actual;
|
|
1049
1129
|
if (!zone) {
|
|
1050
|
-
if (!
|
|
1130
|
+
if (!isInteractive()) {
|
|
1051
1131
|
throw new CfldError(
|
|
1052
1132
|
"Pass --zone to record which domain this cert is for.",
|
|
1053
1133
|
"e.g. `cfld login --zone example.com`"
|
|
@@ -1055,48 +1135,57 @@ async function loginCommand(flags) {
|
|
|
1055
1135
|
}
|
|
1056
1136
|
zone = normalizeZone(await askText("Which domain did you authorize?", "example.com"));
|
|
1057
1137
|
}
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
}
|
|
1061
|
-
async function runLogin() {
|
|
1062
|
-
const bin = await resolveBinary();
|
|
1063
|
-
step("Opening your browser to authorize with Cloudflare (no API token needed)\u2026");
|
|
1064
|
-
note(" Pick the domain you want to use. Waiting for you to finish\u2026");
|
|
1065
|
-
await new Promise((resolve, reject) => {
|
|
1066
|
-
const child = spawn4(bin, ["tunnel", "login"], { stdio: "inherit" });
|
|
1067
|
-
child.on("error", reject);
|
|
1068
|
-
child.on("exit", (code) => {
|
|
1069
|
-
if (code === 0 || existsSync8(defaultCertPath())) resolve();
|
|
1070
|
-
else reject(new CfldError("Cloudflare login did not complete.", "Re-run `cfld login`."));
|
|
1071
|
-
});
|
|
1072
|
-
});
|
|
1073
|
-
if (!existsSync8(defaultCertPath())) {
|
|
1138
|
+
const clash = storedZoneClaiming(certPath, zone);
|
|
1139
|
+
if (clash && !actual) {
|
|
1074
1140
|
throw new CfldError(
|
|
1075
|
-
|
|
1076
|
-
|
|
1141
|
+
`That cert is for the same domain as your saved ${clash} cert, not ${zone}.`,
|
|
1142
|
+
`Run \`cfld login --reauth --zone ${zone}\` and pick ${zone} in the browser.`
|
|
1077
1143
|
);
|
|
1078
1144
|
}
|
|
1145
|
+
importCert(zone, certPath);
|
|
1146
|
+
return zone;
|
|
1079
1147
|
}
|
|
1080
1148
|
async function ensureCert(zone, opts = { interactive: true }) {
|
|
1081
|
-
const
|
|
1082
|
-
if (
|
|
1149
|
+
const usable = await usableCert(zone);
|
|
1150
|
+
if (usable) return usable;
|
|
1083
1151
|
if (!opts.interactive) {
|
|
1084
1152
|
throw new CfldError(
|
|
1085
1153
|
`No Cloudflare certificate found for ${zone}.`,
|
|
1086
|
-
|
|
1154
|
+
`Run \`cfld login --reauth --zone ${zone}\` first (needs a browser), or set TUNNEL_ORIGIN_CERT.`
|
|
1087
1155
|
);
|
|
1088
1156
|
}
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1157
|
+
info(`No certificate for ${zone} yet \u2014 authorize it in the browser.`);
|
|
1158
|
+
const fresh = await runLogin();
|
|
1159
|
+
try {
|
|
1160
|
+
if (!certHasZone(fresh)) {
|
|
1161
|
+
printZeroZoneGuidance();
|
|
1162
|
+
throw new CfldError(
|
|
1163
|
+
"Your Cloudflare account has no domain yet, so a persistent URL isn't possible.",
|
|
1164
|
+
"Add a domain (see above), then re-run. Or use `cfld --quick` for a throwaway URL now."
|
|
1165
|
+
);
|
|
1166
|
+
}
|
|
1167
|
+
await saveCert(zone, fresh);
|
|
1168
|
+
} finally {
|
|
1169
|
+
discardLoginCert(fresh);
|
|
1096
1170
|
}
|
|
1097
|
-
const stored = importCert(zone);
|
|
1098
1171
|
info(`Saved cert for ${zone}.`);
|
|
1099
|
-
return
|
|
1172
|
+
return certPathForZone(zone);
|
|
1173
|
+
}
|
|
1174
|
+
async function usableCert(zone) {
|
|
1175
|
+
const stored = storedCertForZone(zone);
|
|
1176
|
+
if (stored) {
|
|
1177
|
+
if (!storedZoneClaiming(stored, zone)) return stored;
|
|
1178
|
+
const actual2 = await certZoneName(stored);
|
|
1179
|
+
if (!actual2 || actual2 === zone) return stored;
|
|
1180
|
+
warn(`The saved cert for ${zone} is actually for ${actual2} \u2014 discarding it.`);
|
|
1181
|
+
rmSync(stored);
|
|
1182
|
+
}
|
|
1183
|
+
const def = defaultCertPath();
|
|
1184
|
+
if (!certHasZone(def)) return void 0;
|
|
1185
|
+
const actual = await certZoneName(def);
|
|
1186
|
+
if (actual === zone) return importCert(zone, def);
|
|
1187
|
+
if (!actual && !storedZoneClaiming(def, zone)) return def;
|
|
1188
|
+
return void 0;
|
|
1100
1189
|
}
|
|
1101
1190
|
function printZeroZoneGuidance() {
|
|
1102
1191
|
warn("cfld can't create a permanent custom URL yet \u2014 your account has no domains.");
|
|
@@ -1123,11 +1212,11 @@ function printZeroZoneGuidance() {
|
|
|
1123
1212
|
|
|
1124
1213
|
// src/util/gitignore.ts
|
|
1125
1214
|
import { existsSync as existsSync9, readFileSync as readFileSync6, writeFileSync as writeFileSync5 } from "fs";
|
|
1126
|
-
import { join as
|
|
1215
|
+
import { join as join5 } from "path";
|
|
1127
1216
|
function ensureGitignored(cwd, pattern) {
|
|
1128
|
-
const gitignorePath =
|
|
1217
|
+
const gitignorePath = join5(cwd, ".gitignore");
|
|
1129
1218
|
const hasGitignore = existsSync9(gitignorePath);
|
|
1130
|
-
const inRepo = hasGitignore || existsSync9(
|
|
1219
|
+
const inRepo = hasGitignore || existsSync9(join5(cwd, ".git"));
|
|
1131
1220
|
if (!inRepo) return false;
|
|
1132
1221
|
const existing = hasGitignore ? readFileSync6(gitignorePath, "utf8") : "";
|
|
1133
1222
|
const present = existing.split("\n").map((l) => l.trim()).includes(pattern);
|
|
@@ -1309,7 +1398,7 @@ ${opts.reason}`);
|
|
|
1309
1398
|
);
|
|
1310
1399
|
process.once("SIGHUP", () => shutdown({ reason: "Terminal closed \u2014 stopping." }));
|
|
1311
1400
|
if (interactive) {
|
|
1312
|
-
const { startDashboard } = await import("./dashboard-
|
|
1401
|
+
const { startDashboard } = await import("./dashboard-Z4AN7H5V.js");
|
|
1313
1402
|
dashboard = startDashboard(
|
|
1314
1403
|
{
|
|
1315
1404
|
url,
|
|
@@ -1376,7 +1465,7 @@ ${opts.reason}`);
|
|
|
1376
1465
|
if (!up) warn(`Nothing is listening on :${ctx.port} yet \u2014 going live anyway.`);
|
|
1377
1466
|
}
|
|
1378
1467
|
if (!ctx.noEnv) {
|
|
1379
|
-
const { changed } = upsertEnv(
|
|
1468
|
+
const { changed } = upsertEnv(join6(ctx.cwd, ".env"), ctx.envKey, url);
|
|
1380
1469
|
if (changed && !dashboard) info(`Wrote ${ctx.envKey}=${url} to .env`);
|
|
1381
1470
|
}
|
|
1382
1471
|
if (dashboard) {
|
|
@@ -1553,7 +1642,7 @@ ${opts.reason ?? "Stopped ephemeral tunnel."}`);
|
|
|
1553
1642
|
});
|
|
1554
1643
|
}
|
|
1555
1644
|
if (interactive) {
|
|
1556
|
-
const { startDashboard } = await import("./dashboard-
|
|
1645
|
+
const { startDashboard } = await import("./dashboard-Z4AN7H5V.js");
|
|
1557
1646
|
dashboard = startDashboard({
|
|
1558
1647
|
url,
|
|
1559
1648
|
target,
|
|
@@ -1753,20 +1842,26 @@ async function setupCommand(flags) {
|
|
|
1753
1842
|
);
|
|
1754
1843
|
return;
|
|
1755
1844
|
}
|
|
1756
|
-
await runLogin();
|
|
1757
|
-
|
|
1758
|
-
|
|
1759
|
-
if (
|
|
1760
|
-
|
|
1761
|
-
|
|
1845
|
+
const fresh = await runLogin();
|
|
1846
|
+
let zone;
|
|
1847
|
+
try {
|
|
1848
|
+
if (!certHasZone(fresh)) {
|
|
1849
|
+
printZeroZoneGuidance();
|
|
1850
|
+
if (await askConfirm("Start a temporary quick tunnel now instead?")) {
|
|
1851
|
+
await quickCommand(flags);
|
|
1852
|
+
return;
|
|
1853
|
+
}
|
|
1854
|
+
throw new CfldError(
|
|
1855
|
+
"Your Cloudflare account has no domain yet, so a persistent URL isn't possible.",
|
|
1856
|
+
"Add a domain (see above), then run `cfld setup` again."
|
|
1857
|
+
);
|
|
1762
1858
|
}
|
|
1763
|
-
|
|
1764
|
-
|
|
1765
|
-
|
|
1766
|
-
);
|
|
1859
|
+
const detected = await certZoneName(fresh);
|
|
1860
|
+
zone = await saveCert(detected ?? await askAuthorizedZone(knownZones), fresh);
|
|
1861
|
+
} finally {
|
|
1862
|
+
discardLoginCert(fresh);
|
|
1767
1863
|
}
|
|
1768
|
-
const
|
|
1769
|
-
const certPath = importCert(zone);
|
|
1864
|
+
const certPath = certPathForZone(zone);
|
|
1770
1865
|
info(`Saved cert for ${zone}.`);
|
|
1771
1866
|
await configureProject(flags, { zone, certPath, cwd });
|
|
1772
1867
|
await offerGoLive(flags, zone);
|
|
@@ -1997,9 +2092,9 @@ function resolveCurrentEntry2() {
|
|
|
1997
2092
|
import pc5 from "picocolors";
|
|
1998
2093
|
|
|
1999
2094
|
// src/core/service.ts
|
|
2000
|
-
import { existsSync as existsSync10, mkdirSync as
|
|
2095
|
+
import { existsSync as existsSync10, mkdirSync as mkdirSync4, rmSync as rmSync2, writeFileSync as writeFileSync6 } from "fs";
|
|
2001
2096
|
import { homedir as homedir2, platform } from "os";
|
|
2002
|
-
import { join as
|
|
2097
|
+
import { join as join7 } from "path";
|
|
2003
2098
|
function serviceSlug(name) {
|
|
2004
2099
|
return name.replace(/^cfld-/, "");
|
|
2005
2100
|
}
|
|
@@ -2007,16 +2102,16 @@ function launchAgentLabel(name) {
|
|
|
2007
2102
|
return `com.cfld.${serviceSlug(name)}`;
|
|
2008
2103
|
}
|
|
2009
2104
|
function launchAgentPath(name) {
|
|
2010
|
-
return
|
|
2105
|
+
return join7(homedir2(), "Library", "LaunchAgents", `${launchAgentLabel(name)}.plist`);
|
|
2011
2106
|
}
|
|
2012
2107
|
function systemdUnitName(name) {
|
|
2013
2108
|
return `cfld-${serviceSlug(name)}.service`;
|
|
2014
2109
|
}
|
|
2015
2110
|
function systemdUnitPath(name) {
|
|
2016
|
-
return
|
|
2111
|
+
return join7(homedir2(), ".config", "systemd", "user", systemdUnitName(name));
|
|
2017
2112
|
}
|
|
2018
2113
|
function logDir() {
|
|
2019
|
-
return
|
|
2114
|
+
return join7(cfldDir(), "logs");
|
|
2020
2115
|
}
|
|
2021
2116
|
function renderLaunchAgent(spec) {
|
|
2022
2117
|
const args = [
|
|
@@ -2030,8 +2125,8 @@ function renderLaunchAgent(spec) {
|
|
|
2030
2125
|
spec.name
|
|
2031
2126
|
];
|
|
2032
2127
|
const argsXml = args.map((a) => ` <string>${escapeXml(a)}</string>`).join("\n");
|
|
2033
|
-
const out =
|
|
2034
|
-
const err =
|
|
2128
|
+
const out = join7(logDir(), `${serviceSlug(spec.name)}.out.log`);
|
|
2129
|
+
const err = join7(logDir(), `${serviceSlug(spec.name)}.err.log`);
|
|
2035
2130
|
return `<?xml version="1.0" encoding="UTF-8"?>
|
|
2036
2131
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
2037
2132
|
<plist version="1.0">
|
|
@@ -2096,10 +2191,10 @@ function assertSupported() {
|
|
|
2096
2191
|
}
|
|
2097
2192
|
async function installService(spec) {
|
|
2098
2193
|
const p = assertSupported();
|
|
2099
|
-
|
|
2194
|
+
mkdirSync4(logDir(), { recursive: true });
|
|
2100
2195
|
if (p === "darwin") {
|
|
2101
2196
|
const path = launchAgentPath(spec.name);
|
|
2102
|
-
|
|
2197
|
+
mkdirSync4(join7(homedir2(), "Library", "LaunchAgents"), { recursive: true });
|
|
2103
2198
|
writeFileSync6(path, renderLaunchAgent(spec), "utf8");
|
|
2104
2199
|
await exec("launchctl", ["unload", path]);
|
|
2105
2200
|
const res = await exec("launchctl", ["load", "-w", path]);
|
|
@@ -2111,7 +2206,7 @@ async function installService(spec) {
|
|
|
2111
2206
|
}
|
|
2112
2207
|
} else {
|
|
2113
2208
|
const path = systemdUnitPath(spec.name);
|
|
2114
|
-
|
|
2209
|
+
mkdirSync4(join7(homedir2(), ".config", "systemd", "user"), { recursive: true });
|
|
2115
2210
|
writeFileSync6(path, renderSystemdUnit(spec), "utf8");
|
|
2116
2211
|
await exec("systemctl", ["--user", "daemon-reload"]);
|
|
2117
2212
|
const res = await exec("systemctl", [
|
|
@@ -2134,13 +2229,13 @@ async function uninstallService(name) {
|
|
|
2134
2229
|
const path = launchAgentPath(name);
|
|
2135
2230
|
if (existsSync10(path)) {
|
|
2136
2231
|
await exec("launchctl", ["unload", "-w", path]);
|
|
2137
|
-
|
|
2232
|
+
rmSync2(path, { force: true });
|
|
2138
2233
|
}
|
|
2139
2234
|
} else {
|
|
2140
2235
|
const unit = systemdUnitName(name);
|
|
2141
2236
|
await exec("systemctl", ["--user", "disable", "--now", unit]);
|
|
2142
2237
|
const path = systemdUnitPath(name);
|
|
2143
|
-
if (existsSync10(path))
|
|
2238
|
+
if (existsSync10(path)) rmSync2(path, { force: true });
|
|
2144
2239
|
await exec("systemctl", ["--user", "daemon-reload"]);
|
|
2145
2240
|
}
|
|
2146
2241
|
}
|
|
@@ -2260,6 +2355,14 @@ async function doctorCommand() {
|
|
|
2260
2355
|
const zones = listStoredZones();
|
|
2261
2356
|
if (zones.length) out(` ${ok} cert store: ${zones.join(", ")}`);
|
|
2262
2357
|
else out(` ${pc6.dim("\xB7")} cert store empty`);
|
|
2358
|
+
for (const zone of zones) {
|
|
2359
|
+
const actual = await certZoneName(certPathForZone(zone));
|
|
2360
|
+
if (actual && actual !== zone) {
|
|
2361
|
+
out(
|
|
2362
|
+
` ${bad} ${zone}.pem is really a cert for ${actual} \u2014 fix with \`cfld login --reauth --zone ${zone}\``
|
|
2363
|
+
);
|
|
2364
|
+
}
|
|
2365
|
+
}
|
|
2263
2366
|
const entries = listEntries();
|
|
2264
2367
|
out(` ${ok} registry: ${entries.length} tunnel(s)`);
|
|
2265
2368
|
out("");
|
|
@@ -2269,7 +2372,7 @@ async function doctorCommand() {
|
|
|
2269
2372
|
function readVersion() {
|
|
2270
2373
|
try {
|
|
2271
2374
|
const here = dirname(fileURLToPath(import.meta.url));
|
|
2272
|
-
const pkg = JSON.parse(readFileSync7(
|
|
2375
|
+
const pkg = JSON.parse(readFileSync7(join8(here, "../package.json"), "utf8"));
|
|
2273
2376
|
return typeof pkg.version === "string" ? pkg.version : "0.0.0";
|
|
2274
2377
|
} catch {
|
|
2275
2378
|
return "0.0.0";
|
|
@@ -94,7 +94,12 @@ function LogColumn({
|
|
|
94
94
|
borderLeft: Boolean(divider),
|
|
95
95
|
children: [
|
|
96
96
|
title ? /* @__PURE__ */ jsx(Text, { color: titleColor, bold: true, wrap: "truncate-end", children: title }) : null,
|
|
97
|
-
/* @__PURE__ */ jsx(Box, { flexDirection: "column", flexGrow: 1, justifyContent: "flex-end", overflow: "hidden", children: lines.length === 0 ? /* @__PURE__ */ jsx(Text, { dimColor: true, children: placeholder }) :
|
|
97
|
+
/* @__PURE__ */ jsx(Box, { flexDirection: "column", flexGrow: 1, justifyContent: "flex-end", overflow: "hidden", children: lines.length === 0 ? /* @__PURE__ */ jsx(Text, { dimColor: true, children: placeholder }) : (
|
|
98
|
+
// Wrap rather than truncate so long lines (e.g. magic-link / OAuth
|
|
99
|
+
// URLs) stay fully visible and selectable instead of ending in "…".
|
|
100
|
+
// The bottom-pinned box clips older wrapped lines off the top.
|
|
101
|
+
lines.slice(-rows).map((line, i) => /* @__PURE__ */ jsx(Text, { wrap: "wrap", children: line }, i))
|
|
102
|
+
) })
|
|
98
103
|
]
|
|
99
104
|
}
|
|
100
105
|
);
|