@iamken/cloudtunnel 0.10.2 → 0.10.3
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 +9 -9
- package/dist/index.js +77 -19
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -128,19 +128,19 @@ cloudtunnel relay cfapi -d you.com --detach # background
|
|
|
128
128
|
cloudtunnel relay cfapi -d you.com --service # run on boot
|
|
129
129
|
```
|
|
130
130
|
|
|
131
|
-
**On the blocked client** —
|
|
131
|
+
**On the blocked client** — one command. `cloudtunnel relay` prints a **relay key**
|
|
132
|
+
(base + secret in one blob); paste it into `login` and everything is saved to config
|
|
133
|
+
(`0600`) for good — no env vars to export, ever:
|
|
132
134
|
|
|
133
135
|
```bash
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
cloudtunnel up web:8080 -d you.com # now manages tunnels through the relay
|
|
136
|
+
cloudtunnel login --relay ctr_XXXXXXXX… # mint/paste the CF token when asked
|
|
137
|
+
cloudtunnel ls # from now on: nothing to set
|
|
138
|
+
cloudtunnel up web:8080 -d you.com # manages tunnels through the relay
|
|
138
139
|
```
|
|
139
140
|
|
|
140
|
-
|
|
141
|
-
`
|
|
142
|
-
|
|
143
|
-
whether a **Relay secret** is set (never the value).
|
|
141
|
+
(Prefer env/flags? `CLOUDTUNNEL_API_BASE` + `CLOUDTUNNEL_RELAY_SECRET`, or
|
|
142
|
+
`login --api-base <url> --relay-secret-stdin`, work too.) `cloudtunnel login --status`
|
|
143
|
+
shows the active **Base** and whether a **Relay secret** is set (never the value).
|
|
144
144
|
|
|
145
145
|
The proxy forwards to **`api.cloudflare.com` only** (no open-proxy / SSRF) and
|
|
146
146
|
returns `403` to any request missing the shared secret.
|
package/dist/index.js
CHANGED
|
@@ -533,22 +533,52 @@ import { ProxyAgent, setGlobalDispatcher } from "undici";
|
|
|
533
533
|
function proxyFromEnv() {
|
|
534
534
|
return process.env.HTTPS_PROXY ?? process.env.https_proxy ?? process.env.HTTP_PROXY ?? process.env.http_proxy ?? process.env.ALL_PROXY ?? process.env.all_proxy;
|
|
535
535
|
}
|
|
536
|
-
function
|
|
537
|
-
if (process.platform !== "linux") return void 0;
|
|
536
|
+
function run(cmd, args) {
|
|
538
537
|
try {
|
|
539
|
-
|
|
540
|
-
if (get("org.gnome.system.proxy", "mode") !== "manual") return void 0;
|
|
541
|
-
for (const scheme of ["https", "http"]) {
|
|
542
|
-
const host = get(`org.gnome.system.proxy.${scheme}`, "host");
|
|
543
|
-
const port = Number(get(`org.gnome.system.proxy.${scheme}`, "port"));
|
|
544
|
-
if (host && port) return `http://${host}:${port}`;
|
|
545
|
-
}
|
|
538
|
+
return execFileSync4(cmd, args, { encoding: "utf8", timeout: 1500 }).trim();
|
|
546
539
|
} catch {
|
|
540
|
+
return "";
|
|
541
|
+
}
|
|
542
|
+
}
|
|
543
|
+
function parseMacProxy(out) {
|
|
544
|
+
const val = (k) => out.match(new RegExp(`\\b${k}\\s*:\\s*(\\S+)`))?.[1];
|
|
545
|
+
for (const [en, host, port] of [["HTTPSEnable", "HTTPSProxy", "HTTPSPort"], ["HTTPEnable", "HTTPProxy", "HTTPPort"]]) {
|
|
546
|
+
if (val(en) === "1" && val(host) && val(port)) return `http://${val(host)}:${val(port)}`;
|
|
547
547
|
}
|
|
548
548
|
return void 0;
|
|
549
549
|
}
|
|
550
|
+
function parseWindowsProxy(enableOut, serverOut) {
|
|
551
|
+
if (!/ProxyEnable\s+REG_DWORD\s+0x1/i.test(enableOut)) return void 0;
|
|
552
|
+
const raw = serverOut.match(/ProxyServer\s+REG_SZ\s+(\S+)/i)?.[1];
|
|
553
|
+
if (!raw) return void 0;
|
|
554
|
+
const scheme = raw.match(/https=([^;]+)/i)?.[1] ?? raw.match(/http=([^;]+)/i)?.[1] ?? (raw.includes("=") ? void 0 : raw);
|
|
555
|
+
return scheme ? `http://${scheme}` : void 0;
|
|
556
|
+
}
|
|
557
|
+
function proxyFromGnome() {
|
|
558
|
+
const get = (schema, key) => run("gsettings", ["get", schema, key]).replace(/^'|'$/g, "");
|
|
559
|
+
if (get("org.gnome.system.proxy", "mode") !== "manual") return void 0;
|
|
560
|
+
for (const scheme of ["https", "http"]) {
|
|
561
|
+
const host = get(`org.gnome.system.proxy.${scheme}`, "host");
|
|
562
|
+
const port = Number(get(`org.gnome.system.proxy.${scheme}`, "port"));
|
|
563
|
+
if (host && port) return `http://${host}:${port}`;
|
|
564
|
+
}
|
|
565
|
+
return void 0;
|
|
566
|
+
}
|
|
567
|
+
var WIN_INET = "HKCU\\Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings";
|
|
568
|
+
function proxyFromSystem() {
|
|
569
|
+
switch (process.platform) {
|
|
570
|
+
case "darwin":
|
|
571
|
+
return parseMacProxy(run("scutil", ["--proxy"]));
|
|
572
|
+
case "win32":
|
|
573
|
+
return parseWindowsProxy(run("reg", ["query", WIN_INET, "/v", "ProxyEnable"]), run("reg", ["query", WIN_INET, "/v", "ProxyServer"]));
|
|
574
|
+
case "linux":
|
|
575
|
+
return proxyFromGnome();
|
|
576
|
+
default:
|
|
577
|
+
return void 0;
|
|
578
|
+
}
|
|
579
|
+
}
|
|
550
580
|
function configureProxy() {
|
|
551
|
-
const proxy = proxyFromEnv() ??
|
|
581
|
+
const proxy = proxyFromEnv() ?? proxyFromSystem();
|
|
552
582
|
if (!proxy) return;
|
|
553
583
|
try {
|
|
554
584
|
setGlobalDispatcher(new ProxyAgent(proxy));
|
|
@@ -632,6 +662,28 @@ function listZones2(token) {
|
|
|
632
662
|
return cfGet("/zones?per_page=50", token);
|
|
633
663
|
}
|
|
634
664
|
|
|
665
|
+
// src/config/relay-key.ts
|
|
666
|
+
var PREFIX = "ctr_";
|
|
667
|
+
function encodeRelayKey(base, secret) {
|
|
668
|
+
return PREFIX + Buffer.from(JSON.stringify({ b: base, s: secret })).toString("base64url");
|
|
669
|
+
}
|
|
670
|
+
function decodeRelayKey(key) {
|
|
671
|
+
const invalid = () => {
|
|
672
|
+
throw new CliError("Invalid relay key.", {
|
|
673
|
+
hint: "copy the full key printed by `cloudtunnel relay` on the relay host"
|
|
674
|
+
});
|
|
675
|
+
};
|
|
676
|
+
if (!key.startsWith(PREFIX)) return invalid();
|
|
677
|
+
let obj;
|
|
678
|
+
try {
|
|
679
|
+
obj = JSON.parse(Buffer.from(key.slice(PREFIX.length), "base64url").toString("utf8"));
|
|
680
|
+
} catch {
|
|
681
|
+
return invalid();
|
|
682
|
+
}
|
|
683
|
+
if (typeof obj.b !== "string" || !isHttpUrl(obj.b) || typeof obj.s !== "string" || !obj.s) return invalid();
|
|
684
|
+
return { base: obj.b, secret: obj.s };
|
|
685
|
+
}
|
|
686
|
+
|
|
635
687
|
// src/commands/login.ts
|
|
636
688
|
async function readStdin() {
|
|
637
689
|
const chunks = [];
|
|
@@ -665,6 +717,12 @@ async function acquireToken(opts) {
|
|
|
665
717
|
return { token, fromEnv: false };
|
|
666
718
|
}
|
|
667
719
|
async function runLoginFlow(opts = {}) {
|
|
720
|
+
let relaySecretInput;
|
|
721
|
+
if (opts.relay) {
|
|
722
|
+
const decoded = decodeRelayKey(opts.relay);
|
|
723
|
+
opts.apiBase = decoded.base;
|
|
724
|
+
relaySecretInput = decoded.secret;
|
|
725
|
+
}
|
|
668
726
|
if (opts.apiBase && !isHttpUrl(opts.apiBase)) {
|
|
669
727
|
throw new CliError(`Invalid --api-base "${opts.apiBase}".`, {
|
|
670
728
|
hint: "must be an http(s) URL, e.g. https://cfapi.example.com/client/v4"
|
|
@@ -676,8 +734,9 @@ async function runLoginFlow(opts = {}) {
|
|
|
676
734
|
});
|
|
677
735
|
}
|
|
678
736
|
if (opts.apiBase) process.env.CLOUDTUNNEL_API_BASE = opts.apiBase;
|
|
679
|
-
|
|
680
|
-
|
|
737
|
+
if (relaySecretInput) {
|
|
738
|
+
process.env.CLOUDTUNNEL_RELAY_SECRET = relaySecretInput;
|
|
739
|
+
} else if (opts.relaySecretStdin) {
|
|
681
740
|
relaySecretInput = await readStdin();
|
|
682
741
|
if (relaySecretInput) process.env.CLOUDTUNNEL_RELAY_SECRET = relaySecretInput;
|
|
683
742
|
}
|
|
@@ -744,7 +803,7 @@ function showStatus() {
|
|
|
744
803
|
say.dim(`Config: ${configFile}`);
|
|
745
804
|
}
|
|
746
805
|
function registerLogin(program) {
|
|
747
|
-
program.command("login").description("Authenticate with Cloudflare (paste a token once; account + domain auto-resolved)").option("--token-stdin", "read the API token from stdin (scriptable, avoids shell history)").option("--token <token>", "[discouraged] token as an argument (leaks into shell history)").option("--account <id>", "Cloudflare account id (auto-resolved when you have one account)").option("--zone <domain>", "default domain for new tunnels (auto-resolved when you have one)").option("--api-base <url>", "route the CF API through a relay (when api.cloudflare.com is blocked)").option("--relay-secret-stdin", "read the relay shared secret from stdin (pairs with --api-base)").option("--status", "show current identity (redacted) and exit").action(async (opts) => {
|
|
806
|
+
program.command("login").description("Authenticate with Cloudflare (paste a token once; account + domain auto-resolved)").option("--token-stdin", "read the API token from stdin (scriptable, avoids shell history)").option("--token <token>", "[discouraged] token as an argument (leaks into shell history)").option("--account <id>", "Cloudflare account id (auto-resolved when you have one account)").option("--zone <domain>", "default domain for new tunnels (auto-resolved when you have one)").option("--relay <key>", "one-shot relay setup: paste the key printed by `cloudtunnel relay` (sets base + secret)").option("--api-base <url>", "route the CF API through a relay (when api.cloudflare.com is blocked)").option("--relay-secret-stdin", "read the relay shared secret from stdin (pairs with --api-base)").option("--status", "show current identity (redacted) and exit").action(async (opts) => {
|
|
748
807
|
if (opts.status) return showStatus();
|
|
749
808
|
await runLoginFlow(opts);
|
|
750
809
|
});
|
|
@@ -1859,15 +1918,14 @@ function relayReadyLines(fqdn, secret, tty) {
|
|
|
1859
1918
|
const url = `https://${fqdn}`;
|
|
1860
1919
|
if (!tty) return [`relay ready at ${url}`];
|
|
1861
1920
|
const base = `${url}/client/v4`;
|
|
1921
|
+
const key = encodeRelayKey(base, secret);
|
|
1862
1922
|
return [
|
|
1863
1923
|
`URL ${pc.green(url)}`,
|
|
1864
|
-
`Secret ${pc.bold(secret)} ${pc.dim("(
|
|
1924
|
+
`Secret ${pc.bold(secret)} ${pc.dim("(shown once)")}`,
|
|
1865
1925
|
"",
|
|
1866
|
-
pc.bold("On the blocked client:"),
|
|
1867
|
-
`
|
|
1868
|
-
|
|
1869
|
-
` printf %s "$CF_TOKEN" | cloudtunnel login --token-stdin`,
|
|
1870
|
-
pc.dim(" (mint the CF token on an unblocked host \u2014 dash.cloudflare.com is blocked too)")
|
|
1926
|
+
pc.bold("On the blocked client \u2014 one command, no env, saved for good:"),
|
|
1927
|
+
` cloudtunnel login --relay ${key}`,
|
|
1928
|
+
pc.dim(" \u2192 then just `cloudtunnel ls` / `up \u2026`. Mint the CF token on an unblocked host (dash.cloudflare.com is blocked too).")
|
|
1871
1929
|
];
|
|
1872
1930
|
}
|
|
1873
1931
|
function printRelayReady(fqdn, secret, kind) {
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/config/legacy-migrate.ts","../src/core/service.ts","../src/core/service-exec.ts","../src/core/ingress.ts","../src/core/tunnel-spec.ts","../src/core/service-systemd.ts","../src/core/service-launchd.ts","../src/core/service-windows.ts","../src/config/proxy.ts","../src/commands/login.ts","../src/config/token-url.ts","../src/config/resolve-identity.ts","../src/commands/up.ts","../src/config/ensure-auth.ts","../src/connector/binary.ts","../src/core/up-runner.ts","../src/connector/process.ts","../src/connector/registry.ts","../src/cloudflare/tunnels.ts","../src/connector/health.ts","../src/core/orchestrator-create.ts","../src/core/slug.ts","../src/core/unmanaged-scan-cache.ts","../src/core/orchestrator-manage.ts","../src/core/resolve-domain.ts","../src/core/transport-protocol.ts","../src/commands/ls.ts","../src/commands/delete.ts","../src/commands/logs.ts","../src/commands/relay.ts","../src/core/api-proxy-server.ts"],"sourcesContent":["import { Command } from \"commander\";\nimport { createRequire } from \"node:module\";\nimport pc from \"picocolors\";\nimport { reportError } from \"./ui/errors.js\";\nimport { migrateLegacyProfiles } from \"./config/legacy-migrate.js\";\nimport { configureProxy } from \"./config/proxy.js\";\n\nimport { registerLogin } from \"./commands/login.js\";\nimport { registerUp } from \"./commands/up.js\";\nimport { registerLs } from \"./commands/ls.js\";\nimport { registerDelete } from \"./commands/delete.js\";\nimport { registerLogs } from \"./commands/logs.js\";\nimport { registerRelay } from \"./commands/relay.js\";\n\nconst require = createRequire(import.meta.url);\nconst pkg = require(\"../package.json\") as { version: string };\n\nfunction buildProgram(): Command {\n const program = new Command();\n program\n .name(\"cloudtunnel\")\n .description(\"Expose local ports at HTTPS subdomains on your own Cloudflare domains.\")\n .version(pkg.version, \"-v, --version\")\n .showHelpAfterError();\n\n program.addHelpText(\n \"before\",\n [\n pc.bold(\"Quickstart:\"),\n ` ${pc.cyan(\"cloudtunnel login\")} once — paste a token (or set CLOUDFLARE_API_TOKEN)`,\n ` ${pc.cyan(\"cloudtunnel 8080\")} your local :8080 goes live at an HTTPS URL`,\n ` ${pc.cyan(\"cloudtunnel api:8080\")} api.<domain> → localhost:8080`,\n ` ${pc.cyan(\"cloudtunnel ls\")} list tunnels ${pc.dim(\"·\")} ${pc.cyan(\"cloudtunnel delete <#>\")} remove one`,\n \"\",\n ].join(\"\\n\"),\n );\n\n for (const register of [registerLogin, registerUp, registerLs, registerDelete, registerLogs, registerRelay]) {\n register(program);\n }\n return program;\n}\n\n/** Migrate legacy profiles only in a real terminal (systemd changes need an\n * interactive sudo) and not for help/version, so scripts/CI stay quiet. */\nfunction shouldMigrate(argv: string[]): boolean {\n if (!process.stdin.isTTY || !process.stdout.isTTY) return false;\n const rest = argv.slice(2);\n const infoFlag = new Set([\"-h\", \"--help\", \"-v\", \"--version\", \"help\"]);\n return !rest.some((a) => infoFlag.has(a));\n}\n\nasync function main(): Promise<void> {\n // Route fetch through a proxy if one is configured (Node's fetch ignores\n // proxies by default → UND_ERR_CONNECT_TIMEOUT behind a corporate proxy).\n configureProxy();\n // One-time, best-effort upgrade from the old profile model.\n if (shouldMigrate(process.argv)) await migrateLegacyProfiles();\n const program = buildProgram();\n try {\n await program.parseAsync(process.argv);\n } catch (err) {\n process.exitCode = reportError(err);\n }\n}\n\nvoid main();\n","import { existsSync, readFileSync, renameSync, writeFileSync } from \"node:fs\";\nimport { profilesFile } from \"./paths.js\";\nimport { confirm, say } from \"../ui/output.js\";\nimport { installServiceForSpec, legacyUnitExists, removeLegacyUnit } from \"../core/service.js\";\nimport type { TransportProtocol } from \"../core/transport-protocol.js\";\n\n// Shape of the retired profiles file (self-contained; no dependency on the\n// deleted profile store).\ninterface LegacyService { name: string; port: number; proto: \"http\" | \"https\"; host?: string; domain?: string }\ninterface LegacyProfile { services?: LegacyService[]; domain?: string; protocol?: TransportProtocol }\n\nconst skipMarker = `${profilesFile}.migrate-skip`;\n\n/**\n * One-time, best-effort migration from the old profile model. If a legacy profiles\n * file exists, convert any profile that was registered as a systemd service\n * (`cloudtunnel-<profile>.service`) into the new per-subdomain units. Asks for\n * consent first (it needs sudo), and on decline/failure drops a skip-marker so it\n * never re-prompts on later commands. Caller gates this to an interactive TTY.\n */\nexport async function migrateLegacyProfiles(): Promise<void> {\n if (!existsSync(profilesFile) || existsSync(skipMarker)) return; // fast path\n\n let profiles: Record<string, LegacyProfile>;\n try {\n profiles = JSON.parse(readFileSync(profilesFile, \"utf8\")) as Record<string, LegacyProfile>;\n } catch {\n return; // unreadable → leave it alone\n }\n\n // Only boot-registered profiles need migrating; the rest are just stale saved defs.\n const legacy = Object.entries(profiles).filter(([name]) => legacyUnitExists(name));\n if (legacy.length === 0) {\n try { renameSync(profilesFile, `${profilesFile}.migrated`); } catch { /* ignore */ }\n return;\n }\n\n const ok = await confirm(`Found ${legacy.length} boot service(s) from an older cloudtunnel. Migrate them now? (needs sudo)`);\n if (!ok) {\n writeFileSync(skipMarker, \"\");\n say.dim(` Skipped. Delete ${skipMarker} to be asked again.`);\n return;\n }\n\n let migrated = 0;\n try {\n for (const [name, profile] of legacy) {\n for (const svc of profile.services ?? []) {\n const zone = svc.domain ?? profile.domain;\n if (!zone) continue; // can't resolve a hostname → skip this service\n installServiceForSpec({\n subdomain: svc.name, port: svc.port, host: svc.host,\n zone, proto: svc.proto, protocol: profile.protocol,\n });\n migrated++;\n }\n removeLegacyUnit(name);\n }\n renameSync(profilesFile, `${profilesFile}.migrated`);\n say.ok(`Migrated ${migrated} boot service(s). See them with: cloudtunnel ls`);\n } catch (err) {\n writeFileSync(skipMarker, \"\"); // stop auto-retrying on every command\n say.warn(`Migration incomplete: ${(err as Error).message}. Won't retry automatically (delete ${skipMarker} to retry).`);\n }\n}\n","import { join } from \"node:path\";\nimport { CliError } from \"../ui/errors.js\";\nimport { logDir } from \"../config/paths.js\";\nimport { describeService, serviceSlug, type ServiceDescriptor, type ServiceSpecParams, type ServiceState } from \"./service-exec.js\";\nimport * as systemd from \"./service-systemd.js\";\nimport * as launchd from \"./service-launchd.js\";\nimport * as windows from \"./service-windows.js\";\n\nexport type { ServiceState, ServiceSpecParams } from \"./service-exec.js\";\n\n/** Per-OS boot-service backend. */\ninterface Backend {\n label(fqdn: string): string;\n assertSupported(): void;\n install(d: ServiceDescriptor): void;\n uninstall(fqdn: string): void;\n state(fqdn: string): ServiceState;\n}\n\n/** The backend for the current OS, or null where boot services aren't supported. */\nfunction pick(): Backend | null {\n switch (process.platform) {\n case \"linux\": return systemd;\n case \"darwin\": return launchd;\n case \"win32\": return windows;\n default: return null;\n }\n}\n\nfunction required(): Backend {\n const b = pick();\n if (!b) {\n throw new CliError(`Boot services aren't supported on ${process.platform}.`, {\n hint: \"run `cloudtunnel up <spec> --detach` and use your OS's own autostart\",\n });\n }\n return b;\n}\n\n/** Throw if `--service` can't work here (unsupported OS, or systemd missing). */\nexport function assertServiceSupported(): void {\n required().assertSupported();\n}\n\n/** Backend-specific display name/id for a subdomain's service. */\nexport function serviceName(fqdn: string): string {\n return pick()?.label(fqdn) ?? `cloudtunnel-${fqdn}`;\n}\n\n/** Install + enable a boot service for one subdomain (runs now + at login/boot). */\nexport function installServiceForSpec(params: ServiceSpecParams): void {\n const b = required();\n b.assertSupported();\n b.install(describeService(params));\n}\n\n/** Remove a subdomain's boot service (best-effort; no-op on unsupported OS). */\nexport function uninstallService(fqdn: string): void {\n pick()?.uninstall(fqdn);\n}\n\n/** Current state of a subdomain's service (\"none\" on an unsupported OS). */\nexport function serviceState(fqdn: string): ServiceState {\n return pick()?.state(fqdn) ?? \"none\";\n}\n\n/** Platform command/path to inspect why a subdomain's boot service isn't up yet. */\nexport function serviceLogsHint(fqdn: string): string {\n switch (process.platform) {\n case \"linux\":\n return `journalctl -u ${serviceName(fqdn)} -n 50 --no-pager`;\n case \"darwin\":\n return `tail ${join(logDir, `${serviceSlug(fqdn)}.service.log`)}`;\n case \"win32\":\n return `schtasks /Query /TN \"${serviceName(fqdn)}\" /V /FO LIST`;\n default:\n return \"check your OS service logs\";\n }\n}\n\n// --- Legacy (Linux-only) migration from the old profile-based units ---\nexport function legacyUnitExists(profile: string): boolean {\n return process.platform === \"linux\" ? systemd.legacyUnitExists(profile) : false;\n}\nexport function removeLegacyUnit(profile: string): void {\n if (process.platform === \"linux\") systemd.removeLegacyUnit(profile);\n}\n","import { realpathSync } from \"node:fs\";\nimport os from \"node:os\";\nimport { join } from \"node:path\";\nimport { CliError } from \"../ui/errors.js\";\nimport { logDir } from \"../config/paths.js\";\nimport { formatTunnelSpec } from \"./tunnel-spec.js\";\nimport type { TransportProtocol } from \"./transport-protocol.js\";\n\nexport type ServiceState = \"active\" | \"enabled\" | \"disabled\" | \"none\";\n\n/** What `up --service` (and the migration) hand to a platform backend. */\nexport interface ServiceSpecParams {\n subdomain: string;\n port: number;\n host?: string;\n zone: string;\n proto: \"http\" | \"https\";\n protocol?: TransportProtocol;\n /** Which foreground command the boot unit re-runs. Default \"up\"; \"relay\" makes\n * the unit start the CF-API relay instead of a plain tunnel. */\n command?: \"up\" | \"relay\";\n}\n\n/** Normalized, OS-agnostic description of the boot service for one subdomain. */\nexport interface ServiceDescriptor {\n fqdn: string;\n slug: string; // fqdn reduced to [a-z0-9-], unique per domain\n argv: string[]; // cloudtunnel args, e.g. [\"up\",\"api:8080@localhost\",\"-d\",\"abc.com\",\"-f\",\"-y\"]\n nodePath: string; // absolute node binary\n scriptPath: string; // absolute cloudtunnel entry\n user: string;\n home: string;\n logFile: string;\n}\n\nexport const fqdnFor = (subdomain: string, zone: string): string =>\n subdomain === \"@\" ? zone : `${subdomain}.${zone}`;\n\n/** Stable, filesystem-safe id derived from the fqdn (shared by every backend). */\nexport const serviceSlug = (fqdn: string): string => fqdn.replace(/[^a-zA-Z0-9]+/g, \"-\");\n\n/** The cloudtunnel args a boot service re-runs: recreate this one subdomain in the\n * foreground, non-interactively. Round-trips through `parseTunnelSpec` on boot.\n * A relay unit re-runs `relay <sub>` instead (it picks a fresh proxy port itself\n * and reads the persisted secret from config — nothing sensitive in the unit). */\nexport function buildUpArgs(p: ServiceSpecParams): string[] {\n if (p.command === \"relay\") {\n return [\n \"relay\", p.subdomain, \"-d\", p.zone,\n ...(p.proto === \"https\" ? [\"--proto\", \"https\"] : []),\n \"-f\", \"-y\",\n ];\n }\n const spec = formatTunnelSpec({ subdomain: p.subdomain, port: p.port, host: p.host });\n return [\n \"up\", spec, \"-d\", p.zone,\n ...(p.proto === \"https\" ? [\"--proto\", \"https\"] : []),\n ...(p.protocol ? [\"--protocol\", p.protocol] : []),\n \"-f\", \"-y\",\n ];\n}\n\n/** Resolve the running cloudtunnel entry, for a stable service command. */\nfunction entryScript(): string {\n const p = process.argv[1];\n if (!p) throw new CliError(\"Cannot resolve the cloudtunnel executable path.\");\n return realpathSync(p);\n}\n\nexport function describeService(p: ServiceSpecParams): ServiceDescriptor {\n const fqdn = fqdnFor(p.subdomain, p.zone);\n const slug = serviceSlug(fqdn);\n return {\n fqdn,\n slug,\n argv: buildUpArgs(p),\n nodePath: process.execPath,\n scriptPath: entryScript(),\n user: os.userInfo().username,\n home: os.homedir(),\n logFile: join(logDir, `${slug}.service.log`),\n };\n}\n","import type { IngressRule } from \"../cloudflare/types.js\";\nimport { CliError } from \"../ui/errors.js\";\n\nconst HOSTNAME_RE = /^[a-zA-Z0-9.-]+$/; // hostname or IPv4\nconst IPV6_RE = /^[0-9a-fA-F:.]+$/; // IPv6 literal (incl. IPv4-mapped ::ffff:1.2.3.4)\n\n/**\n * Validate a forward-target host before it lands in the ingress service URL.\n * Rejects anything that could break out of `proto://host:port` — a scheme,\n * path, or whitespace — so `--source` can't inject extra ingress syntax.\n *\n * IPv6 is accepted bare (`::1`) or bracketed (`[::1]`) and stored bare. IPv6 is\n * detected by `::` or ≥2 colons, so a single-colon `10.0.0.2:8080` (an IPv4:port\n * mistake) still fails the hostname check instead of passing as a bogus literal.\n */\nexport function validateHost(host: string): string {\n let h = host.trim();\n const bracketed = h.startsWith(\"[\") && h.endsWith(\"]\");\n if (bracketed) h = h.slice(1, -1);\n const isV6 = bracketed || h.includes(\"::\") || (h.match(/:/g)?.length ?? 0) >= 2;\n const ok = h.length > 0 && (isV6 ? IPV6_RE.test(h) : HOSTNAME_RE.test(h));\n if (!ok) {\n throw new CliError(`Invalid host \"${host}\".`, {\n hint: \"use a hostname, IPv4, or IPv6 literal (e.g. 192.168.1.5 or ::1) — no port, scheme, or path\",\n });\n }\n return h;\n}\n\n/** Compose a `proto://host:port` service URL, bracketing an IPv6 literal. */\nexport function serviceUrl(proto: \"http\" | \"https\", host: string, port: number): string {\n const authority = host.includes(\":\") ? `[${host}]` : host;\n return `${proto}://${authority}:${port}`;\n}\n\n/**\n * Build the ingress config for a single-hostname tunnel. The mandatory\n * catch-all `http_status:404` rule must come last (Cloudflare rejects configs\n * without it). One-tunnel-per-subdomain keeps this a fixed two-rule list, so\n * the full-replace PUT is always safe (no merge with other hostnames).\n *\n * `host` defaults to `localhost`; pass another host/IP to forward to a different\n * machine this connector can reach (a LAN device, a container, another server).\n */\nexport function buildIngress(opts: {\n hostname: string;\n port: number;\n proto: \"http\" | \"https\";\n host?: string;\n}): IngressRule[] {\n return [\n { hostname: opts.hostname, service: serviceUrl(opts.proto, opts.host ?? \"localhost\", opts.port) },\n { service: \"http_status:404\" },\n ];\n}\n","import { CliError } from \"../ui/errors.js\";\nimport { validateHost } from \"./ingress.js\";\n\n/** One tunnel to bring up, parsed from a positional `up` argument. */\nexport interface TunnelSpec {\n subdomain?: string; // absent ⇒ random slug; \"@\" ⇒ root/apex domain\n port: number;\n host?: string; // forward target (absent ⇒ localhost)\n}\n\n/**\n * Parse a `[subdomain:]port[@host]` spec, e.g. `8080`, `api:8080`,\n * `api:8080@192.168.1.20`, `api:8080@localhost`, `api:8080@::1`. The local-service\n * protocol is NOT part of the spec — it comes from the global `--proto` flag.\n *\n * A leading `@` means the root/apex domain (kept as the subdomain), which is\n * distinct from the `@host` forward-target delimiter that follows the port.\n */\nexport function parseTunnelSpec(spec: string): TunnelSpec {\n const raw = spec.trim();\n const bad = (hint: string): CliError => new CliError(`Invalid spec \"${spec}\".`, { hint });\n if (!raw) throw bad(\"use [subdomain:]port[@host], e.g. api:8080 or api:8080@192.168.1.20\");\n\n let rest = raw;\n let subdomain: string | undefined;\n\n // Leading `@` = root/apex domain; consume it before looking for the host `@`.\n if (rest.startsWith(\"@\")) {\n subdomain = \"@\";\n rest = rest.slice(1);\n if (rest.startsWith(\":\")) rest = rest.slice(1);\n }\n\n // Forward host after `@` (may contain colons for an IPv6 literal).\n let host: string | undefined;\n const at = rest.indexOf(\"@\");\n if (at >= 0) {\n host = validateHost(rest.slice(at + 1));\n rest = rest.slice(0, at);\n }\n\n // `rest` is now `[subdomain:]port`.\n const parts = rest.split(\":\");\n let portStr: string;\n if (parts.length === 1) {\n portStr = parts[0]!;\n } else if (parts.length === 2) {\n if (subdomain === undefined) {\n if (!parts[0]) throw bad(\"subdomain label is empty\");\n subdomain = parts[0];\n } else if (parts[0]) {\n throw bad(\"unexpected label after '@' root marker\");\n }\n portStr = parts[1]!;\n } else {\n throw bad(\"too many ':' — spec is [subdomain:]port[@host] (protocol via --proto)\");\n }\n\n const port = Number(portStr);\n if (!Number.isInteger(port) || port < 1 || port > 65535) {\n throw bad(\"port must be a number 1–65535\");\n }\n // A DNS label (or \"@\" for the apex). Guards the Cloudflare API and, with\n // `--service`, keeps the subdomain a single unquoted token in the unit ExecStart.\n if (subdomain !== undefined && subdomain !== \"@\" && !/^[a-zA-Z0-9-]+$/.test(subdomain)) {\n throw bad(\"subdomain may contain only letters, digits, and hyphens\");\n }\n return { subdomain, port, ...(host ? { host } : {}) };\n}\n\n/**\n * Render a concrete spec back to its `subdomain:port[@host]` string — used to bake\n * a stable spec into a systemd unit's ExecStart so it round-trips through\n * `parseTunnelSpec` on boot.\n */\nexport function formatTunnelSpec(s: { subdomain: string; port: number; host?: string }): string {\n return `${s.subdomain}:${s.port}${s.host ? `@${s.host}` : \"\"}`;\n}\n","import { execFileSync } from \"node:child_process\";\nimport { existsSync, writeFileSync } from \"node:fs\";\nimport { tmpdir } from \"node:os\";\nimport { dirname, join } from \"node:path\";\nimport { CliError } from \"../ui/errors.js\";\nimport { serviceSlug, type ServiceDescriptor, type ServiceState } from \"./service-exec.js\";\n\nexport const label = (fqdn: string): string => `cloudtunnel-${serviceSlug(fqdn)}.service`;\nconst unitPath = (fqdn: string): string => `/etc/systemd/system/${label(fqdn)}`;\n\n/**\n * Build the systemd unit text (pure — unit-tested). ExecStart re-runs the\n * `cloudtunnel up …` args in the FOREGROUND so systemd supervises one connector;\n * `systemctl stop` → SIGTERM → `up` releases its tunnel and exits 0 (not restarted).\n * Absolute node + script and an explicit PATH are used because systemd starts with\n * a minimal environment.\n */\nexport function buildUnit(d: ServiceDescriptor): string {\n const nodeBin = dirname(d.nodePath);\n return [\n \"[Unit]\",\n `Description=cloudtunnel ${d.fqdn} (Cloudflare Tunnel)`,\n \"After=network-online.target\",\n \"Wants=network-online.target\",\n \"\",\n \"[Service]\",\n \"Type=simple\",\n `User=${d.user}`,\n `Environment=HOME=${d.home}`,\n `Environment=PATH=${nodeBin}:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin`,\n `ExecStart=${d.nodePath} ${d.scriptPath} ${d.argv.join(\" \")}`,\n \"Restart=on-failure\",\n \"RestartSec=5\",\n \"\",\n \"[Install]\",\n \"WantedBy=multi-user.target\",\n \"\",\n ].join(\"\\n\");\n}\n\n/** Run a privileged command, prefixing `sudo` unless already root. */\nfunction privileged(args: string[]): void {\n const isRoot = typeof process.getuid === \"function\" && process.getuid() === 0;\n const argv = isRoot ? args : [\"sudo\", ...args];\n execFileSync(argv[0]!, argv.slice(1), { stdio: \"inherit\" });\n}\n\n/** Read-only systemctl query; returns trimmed stdout (\"\" on any error). */\nfunction query(args: string[]): string {\n try {\n return execFileSync(\"systemctl\", args, { stdio: [\"ignore\", \"pipe\", \"ignore\"], encoding: \"utf8\" }).trim();\n } catch (err) {\n const out = (err as { stdout?: Buffer | string }).stdout;\n return out ? out.toString().trim() : \"\";\n }\n}\n\nexport function assertSupported(): void {\n try {\n execFileSync(\"systemctl\", [\"--version\"], { stdio: \"ignore\" });\n } catch {\n throw new CliError(\"systemd (systemctl) was not found on this host.\");\n }\n}\n\n/** Install + enable a boot unit (runs now + on boot). Needs sudo. */\nexport function install(d: ServiceDescriptor): void {\n assertSupported();\n const tmp = join(tmpdir(), label(d.fqdn));\n writeFileSync(tmp, buildUnit(d), { mode: 0o644 });\n privileged([\"install\", \"-m\", \"0644\", tmp, unitPath(d.fqdn)]);\n privileged([\"systemctl\", \"daemon-reload\"]);\n privileged([\"systemctl\", \"enable\", \"--now\", label(d.fqdn)]);\n}\n\n/** Stop, disable, and delete the unit. Needs sudo. Best-effort. */\nexport function uninstall(fqdn: string): void {\n try {\n privileged([\"systemctl\", \"disable\", \"--now\", label(fqdn)]);\n } catch {\n /* not enabled / already gone */\n }\n privileged([\"rm\", \"-f\", unitPath(fqdn)]);\n privileged([\"systemctl\", \"daemon-reload\"]);\n}\n\nexport function state(fqdn: string): ServiceState {\n const name = label(fqdn);\n if (query([\"is-active\", name]) === \"active\") return \"active\";\n const enabled = query([\"is-enabled\", name]);\n if (enabled === \"enabled\" || enabled === \"enabled-runtime\") return \"enabled\";\n if (enabled === \"disabled\" || enabled === \"static\") return \"disabled\";\n return \"none\";\n}\n\n/** Whether a legacy profile-named unit is installed (one-time migration only). */\nexport function legacyUnitExists(profile: string): boolean {\n return existsSync(`/etc/systemd/system/cloudtunnel-${profile}.service`);\n}\n\n/** Remove a legacy profile-named unit (migration only). Needs sudo. */\nexport function removeLegacyUnit(profile: string): void {\n const name = `cloudtunnel-${profile}.service`;\n try {\n privileged([\"systemctl\", \"disable\", \"--now\", name]);\n } catch {\n /* not enabled / already gone */\n }\n privileged([\"rm\", \"-f\", `/etc/systemd/system/${name}`]);\n privileged([\"systemctl\", \"daemon-reload\"]);\n}\n","import { execFileSync } from \"node:child_process\";\nimport { existsSync, mkdirSync, rmSync, writeFileSync } from \"node:fs\";\nimport { dirname, join } from \"node:path\";\nimport os from \"node:os\";\nimport { ensureDirs } from \"../config/paths.js\";\nimport { serviceSlug, type ServiceDescriptor, type ServiceState } from \"./service-exec.js\";\n\nexport const label = (fqdn: string): string => `com.cloudtunnel.${serviceSlug(fqdn)}`;\nconst agentsDir = (): string => join(os.homedir(), \"Library\", \"LaunchAgents\");\nconst plistPath = (fqdn: string): string => join(agentsDir(), `${label(fqdn)}.plist`);\n\nconst xml = (s: string): string =>\n s.replace(/&/g, \"&\").replace(/</g, \"<\").replace(/>/g, \">\");\n\n/**\n * Build the launchd LaunchAgent plist (pure — unit-tested). A user agent (no sudo)\n * that runs at login (`RunAtLoad`) and is restarted on exit (`KeepAlive`), i.e. the\n * macOS equivalent of enable-now + restart-on-failure. ProgramArguments re-run the\n * same `cloudtunnel up …` the connector needs.\n */\nexport function buildPlist(d: ServiceDescriptor): string {\n const args = [d.nodePath, d.scriptPath, ...d.argv].map((a) => ` <string>${xml(a)}</string>`).join(\"\\n\");\n const nodeBin = dirname(d.nodePath);\n const path = `${nodeBin}:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin`;\n return [\n '<?xml version=\"1.0\" encoding=\"UTF-8\"?>',\n '<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">',\n '<plist version=\"1.0\">',\n \"<dict>\",\n ` <key>Label</key><string>${xml(label(d.fqdn))}</string>`,\n \" <key>ProgramArguments</key>\",\n \" <array>\",\n args,\n \" </array>\",\n \" <key>RunAtLoad</key><true/>\",\n \" <key>KeepAlive</key><true/>\",\n \" <key>EnvironmentVariables</key>\",\n \" <dict>\",\n ` <key>PATH</key><string>${xml(path)}</string>`,\n ` <key>HOME</key><string>${xml(d.home)}</string>`,\n \" </dict>\",\n ` <key>StandardOutPath</key><string>${xml(d.logFile)}</string>`,\n ` <key>StandardErrorPath</key><string>${xml(d.logFile)}</string>`,\n \"</dict>\",\n \"</plist>\",\n \"\",\n ].join(\"\\n\");\n}\n\n/** Run a launchctl command, ignoring failures (returns \"\" on error). */\nfunction launchctl(args: string[]): string {\n try {\n return execFileSync(\"launchctl\", args, { stdio: [\"ignore\", \"pipe\", \"ignore\"], encoding: \"utf8\" });\n } catch (err) {\n const out = (err as { stdout?: Buffer | string }).stdout;\n return out ? out.toString() : \"\";\n }\n}\n\nexport function assertSupported(): void {\n /* launchctl ships with macOS; the darwin platform check is enough. */\n}\n\nexport function install(d: ServiceDescriptor): void {\n ensureDirs();\n mkdirSync(agentsDir(), { recursive: true });\n const plist = plistPath(d.fqdn);\n writeFileSync(plist, buildPlist(d), { mode: 0o644 });\n launchctl([\"unload\", \"-w\", plist]); // best-effort: reload cleanly if already loaded\n // Surface a load failure (e.g. run over SSH / no GUI session) instead of\n // reporting a false success — the plist is written but nothing started.\n execFileSync(\"launchctl\", [\"load\", \"-w\", plist], { stdio: \"inherit\" });\n}\n\nexport function uninstall(fqdn: string): void {\n const plist = plistPath(fqdn);\n launchctl([\"unload\", \"-w\", plist]);\n rmSync(plist, { force: true });\n}\n\nexport function state(fqdn: string): ServiceState {\n const info = launchctl([\"list\", label(fqdn)]);\n if (/\"PID\"\\s*=/.test(info)) return \"active\"; // loaded and has a running pid\n return existsSync(plistPath(fqdn)) ? \"enabled\" : \"none\";\n}\n","import { execFileSync } from \"node:child_process\";\nimport { writeFileSync } from \"node:fs\";\nimport { tmpdir } from \"node:os\";\nimport { join } from \"node:path\";\nimport { serviceSlug, type ServiceDescriptor, type ServiceState } from \"./service-exec.js\";\n\n/** Task Scheduler path: a task named by the fqdn slug under a `cloudtunnel` folder. */\nexport const label = (fqdn: string): string => `cloudtunnel\\\\${serviceSlug(fqdn)}`;\n\nconst xml = (s: string): string =>\n s.replace(/&/g, \"&\").replace(/</g, \"<\").replace(/>/g, \">\").replace(/\"/g, \""\");\n\n/**\n * Build a Task Scheduler definition (pure — unit-tested). A LeastPrivilege logon\n * task (no admin) that starts at logon, restarts on failure, and runs the same\n * `cloudtunnel up …` the connector needs. Written as UTF-16 (schtasks /XML).\n */\nexport function buildTaskXml(d: ServiceDescriptor): string {\n const args = `\"${d.scriptPath}\" ${d.argv.join(\" \")}`;\n return [\n '<?xml version=\"1.0\" encoding=\"UTF-16\"?>',\n '<Task version=\"1.2\" xmlns=\"http://schemas.microsoft.com/windows/2004/02/mit/task\">',\n ` <RegistrationInfo><Description>cloudtunnel ${xml(d.fqdn)} (Cloudflare Tunnel)</Description></RegistrationInfo>`,\n ` <Triggers><LogonTrigger><Enabled>true</Enabled><UserId>${xml(d.user)}</UserId></LogonTrigger></Triggers>`,\n ` <Principals><Principal id=\"Author\"><UserId>${xml(d.user)}</UserId><LogonType>InteractiveToken</LogonType><RunLevel>LeastPrivilege</RunLevel></Principal></Principals>`,\n \" <Settings>\",\n \" <MultipleInstancesPolicy>IgnoreNew</MultipleInstancesPolicy>\",\n \" <DisallowStartIfOnBatteries>false</DisallowStartIfOnBatteries>\",\n \" <StopIfGoingOnBatteries>false</StopIfGoingOnBatteries>\",\n \" <StartWhenAvailable>true</StartWhenAvailable>\",\n \" <ExecutionTimeLimit>PT0S</ExecutionTimeLimit>\",\n \" <RestartOnFailure><Interval>PT1M</Interval><Count>3</Count></RestartOnFailure>\",\n \" <Enabled>true</Enabled>\",\n \" </Settings>\",\n ' <Actions Context=\"Author\">',\n ` <Exec><Command>${xml(d.nodePath)}</Command><Arguments>${xml(args)}</Arguments></Exec>`,\n \" </Actions>\",\n \"</Task>\",\n \"\",\n ].join(\"\\r\\n\");\n}\n\n/** Run schtasks, ignoring failures (returns \"\" on error). */\nfunction schtasks(args: string[]): string {\n try {\n return execFileSync(\"schtasks\", args, { stdio: [\"ignore\", \"pipe\", \"ignore\"], encoding: \"utf8\" });\n } catch (err) {\n const out = (err as { stdout?: Buffer | string }).stdout;\n return out ? out.toString() : \"\";\n }\n}\n\nexport function assertSupported(): void {\n /* schtasks ships with Windows; the win32 platform check is enough. */\n}\n\nexport function install(d: ServiceDescriptor): void {\n const file = join(tmpdir(), `${d.slug}.task.xml`);\n // schtasks /XML wants a UTF-16 file with a BOM.\n writeFileSync(file, \"\\uFEFF\" + buildTaskXml(d), { encoding: \"utf16le\" });\n execFileSync(\"schtasks\", [\"/Create\", \"/TN\", label(d.fqdn), \"/XML\", file, \"/F\"], { stdio: \"inherit\" });\n schtasks([\"/Run\", \"/TN\", label(d.fqdn)]); // start now\n}\n\nexport function uninstall(fqdn: string): void {\n schtasks([\"/Delete\", \"/TN\", label(fqdn), \"/F\"]);\n}\n\nexport function state(fqdn: string): ServiceState {\n const out = schtasks([\"/Query\", \"/TN\", label(fqdn), \"/FO\", \"LIST\"]);\n if (!out) return \"none\";\n if (/\\bRunning\\b/.test(out)) return \"active\";\n if (/\\bDisabled\\b/.test(out)) return \"disabled\";\n if (/\\bReady\\b/.test(out)) return \"enabled\";\n return \"enabled\"; // task exists but status unrecognized (e.g. localized)\n}\n","import { execFileSync } from \"node:child_process\";\nimport { ProxyAgent, setGlobalDispatcher } from \"undici\";\nimport { say } from \"../ui/output.js\";\n\n/** A proxy URL from the standard CLI env vars (upper- and lower-case). */\nexport function proxyFromEnv(): string | undefined {\n return (\n process.env.HTTPS_PROXY ?? process.env.https_proxy ??\n process.env.HTTP_PROXY ?? process.env.http_proxy ??\n process.env.ALL_PROXY ?? process.env.all_proxy\n );\n}\n\n/**\n * Best-effort read of the GNOME system proxy — what GUI apps (Postman/Chromium)\n * use — so a manually-configured desktop proxy is honored without exporting env\n * vars. Linux + gsettings only; any failure ⇒ undefined (env/direct still apply).\n */\nfunction proxyFromGnome(): string | undefined {\n if (process.platform !== \"linux\") return undefined;\n try {\n const get = (schema: string, key: string): string =>\n execFileSync(\"gsettings\", [\"get\", schema, key], { encoding: \"utf8\", timeout: 1500 })\n .trim().replace(/^'|'$/g, \"\");\n if (get(\"org.gnome.system.proxy\", \"mode\") !== \"manual\") return undefined;\n for (const scheme of [\"https\", \"http\"]) {\n const host = get(`org.gnome.system.proxy.${scheme}`, \"host\");\n const port = Number(get(`org.gnome.system.proxy.${scheme}`, \"port\"));\n if (host && port) return `http://${host}:${port}`;\n }\n } catch {\n /* no gsettings / not GNOME / headless — fine, fall through */\n }\n return undefined;\n}\n\n/**\n * Route Node's global `fetch` through a proxy when one is configured. Node's\n * built-in fetch (undici) ignores proxies by default, so behind a corporate/\n * internal proxy every API call connects direct and times out\n * (`UND_ERR_CONNECT_TIMEOUT`) even though curl/Postman work. Sources, in order:\n * proxy env vars → GNOME system proxy (best-effort). No proxy found ⇒ unchanged.\n */\nexport function configureProxy(): void {\n const proxy = proxyFromEnv() ?? proxyFromGnome();\n if (!proxy) return;\n try {\n setGlobalDispatcher(new ProxyAgent(proxy));\n say.debug(`[proxy] routing fetch through ${proxy}`);\n } catch {\n /* keep the default dispatcher — a direct attempt with a clear error beats a crash */\n }\n}\n","import type { Command } from \"commander\";\nimport * as clack from \"@clack/prompts\";\nimport { CliError } from \"../ui/errors.js\";\nimport { redactToken, say, selectOne } from \"../ui/output.js\";\nimport { configFile } from \"../config/paths.js\";\nimport { loadConfig, saveConfig, type CloudtunnelConfig } from \"../config/store.js\";\nimport { REQUIRED_SCOPES, openBrowser, tokenCreateUrl } from \"../config/token-url.js\";\nimport { listAccounts, listZones } from \"../config/resolve-identity.js\";\nimport { getApiBase, isHttpUrl } from \"../config/api-base.js\";\n\ninterface LoginOptions {\n tokenStdin?: boolean;\n token?: string; // deprecated: leaks into shell history\n account?: string;\n zone?: string;\n status?: boolean;\n apiBase?: string; // point the CF API at a relay (blocked control plane)\n relaySecretStdin?: boolean; // read the relay shared secret from stdin\n}\n\n/** Read the whole stdin pipe (for `--token-stdin`). */\nasync function readStdin(): Promise<string> {\n const chunks: Buffer[] = [];\n for await (const chunk of process.stdin) chunks.push(chunk as Buffer);\n return Buffer.concat(chunks).toString(\"utf8\").trim();\n}\n\n/** Acquire the API token: env (silent) → stdin → deprecated flag → masked prompt.\n * Env tokens are NOT persisted (the env stays the source of truth). */\nasync function acquireToken(opts: LoginOptions): Promise<{ token: string; fromEnv: boolean }> {\n const envToken = process.env.CLOUDFLARE_API_TOKEN;\n if (envToken) {\n say.dim(\"Using token from CLOUDFLARE_API_TOKEN.\");\n return { token: envToken, fromEnv: true };\n }\n if (opts.tokenStdin) return { token: await readStdin(), fromEnv: false };\n if (opts.token) {\n say.warn(\"--token puts the token in your shell history — prefer --token-stdin or the prompt. Rotate it if this is a shared host.\");\n return { token: opts.token, fromEnv: false };\n }\n if (!process.stdin.isTTY) {\n throw new CliError(\"No token provided and no interactive terminal.\", {\n hint: \"pipe it: `printf %s $TOKEN | cloudtunnel login --token-stdin`\",\n });\n }\n clack.note(REQUIRED_SCOPES.map((s) => `• ${s}`).join(\"\\n\"), \"Create a token with these scopes\");\n openBrowser(tokenCreateUrl());\n say.dim(`(opened ${tokenCreateUrl()})`);\n const token = await clack.password({ message: \"Paste your Cloudflare API token\", mask: \"•\" });\n if (clack.isCancel(token) || !token) {\n clack.cancel(\"Cancelled.\");\n throw new CliError(\"Cancelled.\", { exitCode: 130 });\n }\n return { token, fromEnv: false };\n}\n\nasync function runLoginFlow(opts: LoginOptions = {}): Promise<void> {\n if (opts.apiBase && !isHttpUrl(opts.apiBase)) {\n throw new CliError(`Invalid --api-base \"${opts.apiBase}\".`, {\n hint: \"must be an http(s) URL, e.g. https://cfapi.example.com/client/v4\",\n });\n }\n if (opts.tokenStdin && opts.relaySecretStdin) {\n throw new CliError(\"Can't read both the token and the relay secret from stdin.\", {\n hint: \"run login twice, or set one via env (CLOUDFLARE_API_TOKEN / CLOUDTUNNEL_RELAY_SECRET)\",\n });\n }\n // Apply relay overrides to THIS process BEFORE the verify calls: on a blocked\n // client the login-time listAccounts/listZones must also ride the relay, and\n // config isn't saved yet — so seed the env the transport reads from.\n if (opts.apiBase) process.env.CLOUDTUNNEL_API_BASE = opts.apiBase;\n let relaySecretInput: string | undefined;\n if (opts.relaySecretStdin) {\n relaySecretInput = await readStdin();\n if (relaySecretInput) process.env.CLOUDTUNNEL_RELAY_SECRET = relaySecretInput;\n }\n\n if (process.stdout.isTTY) clack.intro(\"cloudtunnel · connect to Cloudflare\");\n const { token, fromEnv } = await acquireToken(opts);\n\n const spin = clack.spinner();\n spin.start(\"Verifying token…\");\n const [accounts, zones] = await Promise.all([listAccounts(token), listZones(token)]).catch((err: unknown) => {\n spin.stop(\"Token check failed\");\n throw err;\n });\n spin.stop(\"Token verified\");\n\n if (accounts.length === 0) throw new CliError(\"Token can't see any Cloudflare account.\");\n let account = opts.account ? accounts.find((a) => a.id === opts.account) : undefined;\n if (opts.account && !account) throw new CliError(`Account ${opts.account} not visible to this token.`);\n if (!account) {\n account = accounts.length === 1 || !process.stdin.isTTY\n ? accounts[0]!\n : await selectOne(\"Select an account\", accounts, (a) => `${a.name} (${a.id})`);\n }\n\n let defaultZone = opts.zone;\n if (!defaultZone) {\n if (zones.length === 1) defaultZone = zones[0]!.name;\n else if (zones.length > 1 && process.stdin.isTTY) {\n defaultZone = (await selectOne(\"Select a default domain\", zones, (z) => z.name)).name;\n }\n }\n\n // MERGE-save: preserve apiBase/relaySecret/defaultZone across re-logins (a plain\n // replace here would wipe a previously-configured relay base or secret).\n saveConfig(buildMergedConfig(loadConfig(), {\n token, fromEnv, accountId: account.id, defaultZone,\n apiBase: opts.apiBase, relaySecret: relaySecretInput,\n }));\n const summary = `Logged in as ${account.name}${defaultZone ? ` · default domain ${defaultZone}` : \"\"}`;\n if (process.stdout.isTTY) clack.outro(summary);\n else say.ok(summary);\n if (!defaultZone) say.dim(\"No default domain set — pass -d <domain> on `up`, or re-run `login --zone <domain>`.\");\n}\n\n/** Merge fresh login results onto the existing config so a re-login never wipes a\n * previously-set relay base/secret or saved default domain. An env-sourced token\n * is not persisted (env stays the source of truth). Pure → unit-tested. */\nexport function buildMergedConfig(\n prev: CloudtunnelConfig,\n args: { token: string; fromEnv: boolean; accountId: string; defaultZone?: string; apiBase?: string; relaySecret?: string },\n): CloudtunnelConfig {\n return {\n ...prev,\n apiToken: args.fromEnv ? undefined : args.token,\n accountId: args.accountId,\n defaultZone: args.defaultZone ?? prev.defaultZone,\n apiBase: args.apiBase ?? prev.apiBase,\n relaySecret: args.relaySecret ?? prev.relaySecret,\n };\n}\n\nfunction showStatus(): void {\n const config = loadConfig();\n const token = process.env.CLOUDFLARE_API_TOKEN ?? config.apiToken;\n if (!token) {\n say.warn(\"Not logged in. Run `cloudtunnel login`.\");\n return;\n }\n const source = process.env.CLOUDFLARE_API_TOKEN ? \"env\" : \"config\";\n say.info(`Token: ${redactToken(token)} (${source})`);\n say.info(`Account: ${config.accountId ?? \"(from env / unresolved)\"}`);\n say.info(`Domain: ${config.defaultZone ?? \"(none)\"}`);\n const baseSrc = process.env.CLOUDTUNNEL_API_BASE ? \"env\" : config.apiBase ? \"config\" : \"default\";\n say.info(`Base: ${getApiBase()} (${baseSrc})`);\n const secretSrc = process.env.CLOUDTUNNEL_RELAY_SECRET ? \"env\" : config.relaySecret ? \"config\" : undefined;\n say.info(`Relay secret: ${secretSrc ? `set (${secretSrc})` : \"(none)\"}`);\n say.dim(`Config: ${configFile}`);\n}\n\nexport function registerLogin(program: Command): void {\n program\n .command(\"login\")\n .description(\"Authenticate with Cloudflare (paste a token once; account + domain auto-resolved)\")\n .option(\"--token-stdin\", \"read the API token from stdin (scriptable, avoids shell history)\")\n .option(\"--token <token>\", \"[discouraged] token as an argument (leaks into shell history)\")\n .option(\"--account <id>\", \"Cloudflare account id (auto-resolved when you have one account)\")\n .option(\"--zone <domain>\", \"default domain for new tunnels (auto-resolved when you have one)\")\n .option(\"--api-base <url>\", \"route the CF API through a relay (when api.cloudflare.com is blocked)\")\n .option(\"--relay-secret-stdin\", \"read the relay shared secret from stdin (pairs with --api-base)\")\n .option(\"--status\", \"show current identity (redacted) and exit\")\n .action(async (opts: LoginOptions) => {\n if (opts.status) return showStatus();\n await runLoginFlow(opts);\n });\n}\n\nexport { runLoginFlow };\n","import { spawn } from \"node:child_process\";\n\n/** The exact scopes cloudtunnel needs. Printed so the user selects them when\n * minting a token — least-privilege, account-wide only where required. */\nexport const REQUIRED_SCOPES = [\n \"Account · Cloudflare Tunnel · Edit\",\n \"Account · Account Settings · Read\",\n \"Zone · DNS · Edit\",\n \"Zone · Zone · Read\",\n] as const;\n\n/** Cloudflare \"Create Custom Token\" page. `name` is pre-filled best-effort;\n * the user still selects the scopes above (dashboard pre-fill params are not a\n * versioned API, so we rely on the printed scope list, not URL params). */\nexport function tokenCreateUrl(): string {\n return \"https://dash.cloudflare.com/profile/api-tokens?name=cloudtunnel\";\n}\n\n/** Best-effort open a URL in the default browser. Never throws — if no opener\n * exists (headless/CI), the caller still prints the URL. */\nexport function openBrowser(url: string): void {\n const cmd =\n process.platform === \"darwin\" ? \"open\"\n : process.platform === \"win32\" ? \"cmd\"\n : \"xdg-open\";\n const args = process.platform === \"win32\" ? [\"/c\", \"start\", \"\", url] : [url];\n try {\n const child = spawn(cmd, args, { stdio: \"ignore\", detached: true });\n child.on(\"error\", () => {}); // swallow: opener may not exist\n child.unref();\n } catch {\n // ignore — printing the URL is the fallback\n }\n}\n","import { CliError, fetchErrorReason } from \"../ui/errors.js\";\nimport { say } from \"../ui/output.js\";\nimport { REQUIRED_SCOPES, tokenCreateUrl } from \"./token-url.js\";\nimport { getApiBase, DEFAULT_API_BASE } from \"./api-base.js\";\nimport { RELAY_SECRET_HEADER, getRelaySecret } from \"./relay-secret.js\";\n\nexport interface CfAccount { id: string; name: string }\nexport interface CfZone { id: string; name: string; account?: { id: string } }\n\n/**\n * Raw Cloudflare GET used only for login-time validation (the typed SDK client\n * is wired in Phase 3). Errors are sanitized: the token never appears in any\n * thrown message. A 403 is mapped to a missing-scope hint.\n */\nasync function cfGet<T>(path: string, token: string): Promise<T[]> {\n const base = getApiBase();\n const viaRelay = base !== DEFAULT_API_BASE;\n const secret = getRelaySecret();\n const headers: Record<string, string> = {\n Authorization: `Bearer ${token}`,\n \"Content-Type\": \"application/json\",\n };\n if (secret && viaRelay) headers[RELAY_SECRET_HEADER] = secret;\n let res: Response;\n try {\n res = await fetch(`${base}${path}`, { headers });\n } catch (err) {\n const reason = fetchErrorReason(err);\n say.debug(`[cf] GET ${base}${path} -> network error: ${reason}${viaRelay ? \" (relay)\" : \"\"}`);\n throw new CliError(`Could not reach the Cloudflare API (${reason})${viaRelay ? ` via relay ${base}` : \"\"}.`, {\n hint: viaRelay ? \"is the relay tunnel up and the base URL correct? run with CLOUDTUNNEL_DEBUG=1\" : undefined,\n });\n }\n say.debug(`[cf] GET ${base}${path} -> ${res.status}${viaRelay ? \" (relay)\" : \"\"}`);\n const body = (await res.json().catch(() => ({}))) as { success?: boolean; result?: T[]; error?: string; errors?: unknown };\n // A relay rejects with its own `{error}` shape — surface it so a relay/secret\n // failure isn't misread as an invalid-token or missing-scope problem.\n if (viaRelay && !res.ok && body.error && !body.errors) {\n throw new CliError(`Relay rejected the request (${res.status}): ${body.error}.`, {\n hint: res.status === 403 ? \"does CLOUDTUNNEL_RELAY_SECRET match the relay's secret?\" : `relay base: ${base}`,\n });\n }\n if (res.status === 401) {\n throw new CliError(\"Cloudflare rejected the token (invalid or expired).\", {\n hint: `mint a new token: ${tokenCreateUrl()}`,\n });\n }\n if (res.status === 403) {\n throw new CliError(`Token is missing a required scope for ${path}.`, {\n hint: `token needs: ${REQUIRED_SCOPES.join(\", \")}`,\n });\n }\n if (!res.ok || !body.success) {\n throw new CliError(`Cloudflare API error (${res.status}) on ${path}.`);\n }\n return body.result ?? [];\n}\n\nexport function listAccounts(token: string): Promise<CfAccount[]> {\n return cfGet<CfAccount>(\"/accounts?per_page=50\", token);\n}\n\nexport function listZones(token: string): Promise<CfZone[]> {\n return cfGet<CfZone>(\"/zones?per_page=50\", token);\n}\n","import type { Command } from \"commander\";\nimport * as clack from \"@clack/prompts\";\nimport { CliError } from \"../ui/errors.js\";\nimport { say, printTable } from \"../ui/output.js\";\nimport { ensureAuth } from \"../config/ensure-auth.js\";\nimport { resolveCf, type Cf } from \"../cloudflare/client.js\";\nimport { ensureCloudflared } from \"../connector/binary.js\";\nimport type { CreateOptions } from \"../core/orchestrator-create.js\";\nimport { startTunnels } from \"../core/up-runner.js\";\nimport { resolveDomain } from \"../core/resolve-domain.js\";\nimport { listAll } from \"../core/orchestrator-manage.js\";\nimport { getEntry } from \"../connector/registry.js\";\nimport { parseTunnelSpec, type TunnelSpec } from \"../core/tunnel-spec.js\";\nimport { parseTransportProtocol, type TransportProtocol } from \"../core/transport-protocol.js\";\nimport { randomSlug } from \"../core/slug.js\";\nimport { assertServiceSupported, installServiceForSpec, serviceLogsHint } from \"../core/service.js\";\n\ninterface UpOptions {\n domain?: string;\n proto: \"http\" | \"https\";\n protocol?: string; // edge transport: auto | http2 | quic\n detach?: boolean;\n service?: boolean; // register each subdomain as a systemd boot service\n force?: boolean;\n yes?: boolean;\n}\n\nfunction promptOrExit<T>(value: T | symbol): T {\n if (clack.isCancel(value)) {\n clack.cancel(\"Cancelled.\");\n process.exit(130);\n }\n return value as T;\n}\n\n/** Interactive port prompt (0-arg wizard). */\nasync function promptPort(): Promise<number> {\n const input = promptOrExit(\n await clack.text({\n message: \"Port to expose\",\n placeholder: \"e.g. 3000\",\n validate: (v) => {\n const n = Number(v);\n if (!Number.isInteger(n) || n < 1 || n > 65535) return \"Enter a port 1–65535\";\n return undefined;\n },\n }),\n );\n return Number(input);\n}\n\n/** The subdomain for a spec: explicit in the spec → used as-is; otherwise prompt\n * (TTY, blank = random) or random (non-TTY / `-y`). Returns undefined for random. */\nasync function resolveSpecSubdomain(spec: TunnelSpec, opts: UpOptions): Promise<string | undefined> {\n if (spec.subdomain !== undefined) return spec.subdomain;\n if (opts.yes || !process.stdin.isTTY) return undefined; // random\n const input = promptOrExit(\n await clack.text({ message: `Subdomain for :${spec.port}`, placeholder: \"blank = random · @ = root domain\" }),\n );\n return (input as string).trim() || undefined; // blank → random\n}\n\nasync function runUp(specArgs: string[], opts: UpOptions): Promise<void> {\n const protocol: TransportProtocol | undefined = opts.protocol ? parseTransportProtocol(opts.protocol) : undefined;\n // Parse specs up front (fail fast on a typo before touching the network). 0 args\n // → wizard, which needs a TTY.\n const parsed: TunnelSpec[] | null = specArgs.length ? specArgs.map(parseTunnelSpec) : null;\n if (parsed === null && !process.stdin.isTTY) {\n throw new CliError(\"No tunnel spec given.\", { hint: \"e.g. cloudtunnel api:8080\" });\n }\n\n const creds = await ensureAuth();\n const cf = resolveCf();\n const bin = await ensureCloudflared();\n\n if (process.stdout.isTTY) clack.intro(\"cloudtunnel\");\n\n const specs: TunnelSpec[] = parsed ?? [{ port: await promptPort() }];\n const domain = await resolveDomain(cf, opts, creds);\n\n // Build create-opts per spec. `--service` needs a concrete subdomain baked in\n // (never random-per-boot), so materialise a random one now when unnamed.\n const items: CreateOptions[] = [];\n for (const spec of specs) {\n let name = await resolveSpecSubdomain(spec, opts);\n if (opts.service && name === undefined) name = randomSlug();\n items.push({\n port: spec.port, proto: opts.proto, name, zone: domain, host: spec.host,\n defaultZone: creds.defaultZone, force: opts.force, yes: opts.yes,\n });\n }\n\n if (opts.service) {\n await registerServices(cf, items, domain, opts.proto, protocol);\n return;\n }\n\n await startTunnels(cf, bin, items, { detach: opts.detach, protocol });\n}\n\n/** How long `--service` waits for the boot services to bring their connectors up\n * before showing the `ls` view (registry \"running\" lands within a few seconds). */\nconst SERVICE_UP_TIMEOUT_MS = 20_000;\nconst delay = (ms: number): Promise<void> => new Promise((resolve) => setTimeout(resolve, ms));\n\n/**\n * Install + start a boot service per subdomain (systemd `enable --now` · launchd\n * `RunAtLoad` · Task Scheduler `/Run` all start it now and on boot), then WAIT for\n * the services to bring their connectors up and show the `ls` view. The services\n * own the connector; the CLI just watches the registry they write — so `ls`/`ps`\n * shows them right after this returns instead of after an invisible delay.\n * `--detach` is a no-op here — the service already backgrounds.\n */\nasync function registerServices(\n cf: Cf, items: CreateOptions[], domain: string,\n proto: \"http\" | \"https\", protocol?: TransportProtocol,\n): Promise<void> {\n assertServiceSupported();\n if (!protocol) {\n say.warn(\"No edge protocol set — cloudflared will pick QUIC, which some networks drop.\");\n say.dim(\" → add --protocol http2 for UDP-hostile networks\");\n }\n const fqdns: string[] = [];\n for (const item of items) {\n const subdomain = item.name!; // concrete (baked above)\n const fqdn = subdomain === \"@\" ? domain : `${subdomain}.${domain}`;\n installServiceForSpec({ subdomain, port: item.port, host: item.host, zone: domain, proto, protocol });\n fqdns.push(fqdn);\n }\n say.ok(`Registered ${fqdns.length} boot service(s) — waiting for them to come up…`);\n\n const notUp = await waitServicesUp(fqdns, SERVICE_UP_TIMEOUT_MS);\n\n const rows = await listAll(cf);\n if (rows.length) {\n printTable(\n [\"#\", \"URL\", \"TARGET\", \"PROTOCOL\", \"STATE\", \"SERVICE\", \"PID\"],\n rows.map((r) => [r.num, r.url, r.target, r.protocol, r.state, r.service, r.pid]),\n );\n }\n // A service that never reports up either failed to start or resolved a\n // different config dir than this shell — point at its logs so it's not silent.\n if (notUp.length) {\n say.warn(`${notUp.length} service(s) didn't report up within ${SERVICE_UP_TIMEOUT_MS / 1000}s:`);\n for (const fqdn of notUp) say.dim(` ${fqdn} → ${serviceLogsHint(fqdn)}`);\n }\n say.dim(\" → manage: cloudtunnel ls · remove: cloudtunnel delete <#>\");\n}\n\n/** Poll the registry until every fqdn's service has a live connector (state\n * \"running\" + a pid), or the timeout elapses. Returns the fqdns still not up.\n * Reads the registry the boot service writes; a pid means `ls` will show \"up\". */\nasync function waitServicesUp(fqdns: string[], timeoutMs: number): Promise<string[]> {\n const deadline = Date.now() + timeoutMs;\n const pending = new Set(fqdns);\n while (pending.size > 0) {\n for (const fqdn of [...pending]) {\n const entry = getEntry(fqdn);\n if (entry?.state === \"running\" && entry.pid) pending.delete(fqdn);\n }\n if (pending.size === 0 || Date.now() >= deadline) break;\n await delay(500);\n }\n return [...pending];\n}\n\nexport function registerUp(program: Command): void {\n program\n .command(\"up\", { isDefault: true })\n .argument(\"[specs...]\", \"tunnels to start: [subdomain:]port[@host] (e.g. api:8080 web:8081@localhost)\")\n .description(\"Start one or more tunnels (also: `cloudtunnel 8080`)\")\n .option(\"-d, --domain <domain>\", \"domain for the subdomains (prompted from a list if unset)\")\n .option(\"--proto <proto>\", \"local service protocol: http | https\", \"http\")\n .option(\"--protocol <proto>\", \"cloudflared edge transport: auto | http2 | quic (http2 for UDP-hostile networks)\")\n .option(\"--detach\", \"run the connectors in the background\")\n .option(\"--service\", \"register each subdomain as a boot service (Linux systemd · macOS launchd · Windows Task Scheduler)\")\n .option(\"-f, --force\", \"replace a non-tunnel DNS record occupying the hostname\")\n .option(\"-y, --yes\", \"don't prompt; don't ask before replacing an existing record\")\n .action((specs: string[], opts: UpOptions) => runUp(specs, opts));\n}\n","import { CliError } from \"../ui/errors.js\";\nimport { say } from \"../ui/output.js\";\nimport { getCredentials, type Credentials } from \"./store.js\";\nimport { runLoginFlow } from \"../commands/login.js\";\n\n/**\n * Single auth entry point for every command. Returns credentials if present;\n * on a fresh machine with a TTY it runs onboarding inline and continues, so\n * `cloudtunnel 3000` on a new box just works. Non-TTY (CI) → actionable error.\n */\nexport async function ensureAuth(): Promise<Credentials> {\n try {\n return getCredentials();\n } catch (err) {\n if (err instanceof CliError && process.stdin.isTTY) {\n say.info(\"Welcome to cloudtunnel — let's get you connected to Cloudflare first.\");\n await runLoginFlow();\n return getCredentials();\n }\n throw err;\n }\n}\n","import { execFileSync } from \"node:child_process\";\nimport { createHash } from \"node:crypto\";\nimport { chmodSync, existsSync, readFileSync, writeFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { gunzipSync } from \"node:zlib\";\nimport { CliError } from \"../ui/errors.js\";\nimport { say } from \"../ui/output.js\";\nimport { binDir, ensureDirs } from \"../config/paths.js\";\n\n// Pinned release for reproducible, checksum-verified auto-install. cloudflared\n// ships no sha256 manifest, so these digests are computed by downloading each\n// asset once at pin time (trust-on-pin). Bump the version and RE-HASH every\n// asset together — a stale digest fails closed and disables auto-install.\nconst PINNED_VERSION = \"2026.7.3\";\nconst RELEASE_BASE = `https://github.com/cloudflare/cloudflared/releases/download/${PINNED_VERSION}`;\n\ninterface Asset { file: string; archive: boolean; sha256: string }\n\n// sha256 is the digest of the DOWNLOADED asset (the .tgz for darwin), verified\n// before extraction. Empty string ⇒ fail closed (never run an unverified binary).\nconst ASSETS: Record<string, Asset | undefined> = {\n \"linux-x64\": { file: \"cloudflared-linux-amd64\", archive: false, sha256: \"9d71c677db00134c1bd4144b7783486b654ad281b1ea62b4972098d19f770f17\" },\n \"linux-arm64\": { file: \"cloudflared-linux-arm64\", archive: false, sha256: \"65259e652a7bea08bf5df603233ab22b8bf3116af8df9f9206209af6a1b955c0\" },\n \"linux-arm\": { file: \"cloudflared-linux-arm\", archive: false, sha256: \"6dadd979b8833760e9f6d840a6239a8c08c8bcf73b4231ec537f483873f37c73\" }, // armv7 (Raspberry Pi)\n \"darwin-x64\": { file: \"cloudflared-darwin-amd64.tgz\", archive: true, sha256: \"70d1c8684fa6d14b5843787ec8d1ea8e18b23650e424f4ea43d849a506487c3b\" },\n \"darwin-arm64\": { file: \"cloudflared-darwin-arm64.tgz\", archive: true, sha256: \"90c5a4f914d705fd70c135dba6d80b1791d254b08d6d4136301941f88330dd09\" },\n \"win32-x64\": { file: \"cloudflared-windows-amd64.exe\", archive: false, sha256: \"8635da433b6df8194746e88ed9d2589566c20e38bfc2a80e431a348b7c765841\" },\n};\n\n/**\n * Map the running platform to an ASSETS key. Windows-on-ARM has no native\n * cloudflared build, so it reuses the amd64 exe under x64 emulation.\n * Exported for tests.\n */\nexport function resolveAssetKey(platform: string, arch: string): string {\n const key = `${platform}-${arch}`;\n return key === \"win32-arm64\" ? \"win32-x64\" : key;\n}\n\nfunction binaryWorks(bin: string): boolean {\n try {\n execFileSync(bin, [\"--version\"], { stdio: \"ignore\" });\n return true;\n } catch {\n return false;\n }\n}\n\nfunction cachedPath(): string {\n return join(binDir, process.platform === \"win32\" ? \"cloudflared.exe\" : \"cloudflared\");\n}\n\n/** True on Alpine/musl, where cloudflared has no prebuilt binary. */\nfunction isMusl(): boolean {\n try {\n return process.platform === \"linux\" && readFileSync(\"/usr/bin/ldd\", \"utf8\").includes(\"musl\");\n } catch {\n return false;\n }\n}\n\n/**\n * Return a runnable `cloudflared` with zero user action: PATH → cached download\n * → verified auto-download. Fails closed (never runs an unverified binary).\n */\nexport async function ensureCloudflared(): Promise<string> {\n if (binaryWorks(\"cloudflared\")) return \"cloudflared\";\n const cached = cachedPath();\n if (existsSync(cached) && binaryWorks(cached)) return cached;\n return downloadCloudflared(cached);\n}\n\n/** Exported for tests (fail-closed verification). */\nexport async function downloadCloudflared(dest: string): Promise<string> {\n if (isMusl()) {\n throw new CliError(\"cloudflared has no musl (Alpine) build.\", {\n hint: \"install it manually: https://github.com/cloudflare/cloudflared/releases\",\n });\n }\n const key = resolveAssetKey(process.platform, process.arch);\n const asset = ASSETS[key];\n if (!asset || !asset.sha256) {\n throw new CliError(`Auto-install unavailable for ${key} (no pinned checksum).`, {\n hint: \"install cloudflared manually: https://github.com/cloudflare/cloudflared/releases\",\n });\n }\n\n say.step(`cloudflared not found — downloading v${PINNED_VERSION} (checksum-verified)…`);\n let bytes: Buffer;\n try {\n const res = await fetch(`${RELEASE_BASE}/${asset.file}`, { signal: AbortSignal.timeout(120_000) });\n if (!res.ok) throw new CliError(`Download failed (HTTP ${res.status}).`);\n bytes = Buffer.from(await res.arrayBuffer());\n } catch (err) {\n if (err instanceof CliError) throw err;\n throw new CliError(`Could not download cloudflared (${(err as Error).message}).`, {\n hint: \"check your network, or install cloudflared manually: https://github.com/cloudflare/cloudflared/releases\",\n });\n }\n\n const digest = createHash(\"sha256\").update(bytes).digest(\"hex\");\n if (digest !== asset.sha256) {\n throw new CliError(\"cloudflared checksum mismatch — refusing to run the download.\", {\n hint: \"network tampering or an outdated pin; install manually instead\",\n });\n }\n\n ensureDirs();\n const binary = asset.archive ? extractTgz(bytes) : bytes;\n writeFileSync(dest, binary, { mode: 0o755 });\n chmodSync(dest, 0o755);\n if (!binaryWorks(dest)) throw new CliError(\"Downloaded cloudflared is not runnable.\");\n return dest;\n}\n\n/**\n * Extract the `cloudflared` entry from a gzipped tar (darwin assets ship a\n * single-file .tgz in ustar format). Minimal tar reader — no external dep.\n * Exported for tests.\n */\nexport function extractTgz(bytes: Buffer): Buffer {\n const tar = gunzipSync(bytes);\n for (let off = 0; off + 512 <= tar.length; ) {\n const name = tar.toString(\"utf8\", off, off + 100).replace(/\\0.*/s, \"\");\n if (!name) break; // trailing zero block ⇒ archive end\n const size = parseInt(tar.toString(\"utf8\", off + 124, off + 136).replace(/\\0.*/s, \"\").trim(), 8) || 0;\n const type = tar[off + 156]; // 0x30 '0' or 0x00 ⇒ regular file\n const dataStart = off + 512;\n if ((type === 0x30 || type === 0) && name.split(\"/\").pop() === \"cloudflared\") {\n return tar.subarray(dataStart, dataStart + size);\n }\n off = dataStart + Math.ceil(size / 512) * 512;\n }\n throw new CliError(\"cloudflared entry not found in downloaded archive.\", {\n hint: \"install cloudflared via `brew install cloudflared`\",\n });\n}\n","import { join } from \"node:path\";\nimport * as clack from \"@clack/prompts\";\nimport { reportError } from \"../ui/errors.js\";\nimport { dim, formatRoute, say } from \"../ui/output.js\";\nimport type { Cf } from \"../cloudflare/client.js\";\nimport { logDir } from \"../config/paths.js\";\nimport { startConnector } from \"../connector/process.js\";\nimport { waitHealthy, type HealthResult } from \"../connector/health.js\";\nimport { currentBootId, patchEntry } from \"../connector/registry.js\";\nimport { createTunnelSubdomain, type CreateOptions } from \"./orchestrator-create.js\";\nimport { removeTunnelSubdomain } from \"./orchestrator-manage.js\";\nimport { serviceUrl } from \"./ingress.js\";\nimport type { TransportProtocol } from \"./transport-protocol.js\";\n\ninterface StartedTunnel {\n fqdn: string;\n subdomain: string;\n tunnelId: string;\n target: string;\n pid: number;\n}\n\n/** Log-file label for a subdomain (\"@\" → root). */\nfunction logFileFor(subdomain: string): string {\n return join(logDir, `${subdomain === \"@\" ? \"root\" : subdomain}.log`);\n}\n\n/**\n * Create + connect a batch of tunnels (1..N). Foreground: waits for health, then\n * any exit (Ctrl-C / crash) releases every tunnel started here (2-state model).\n * `--detach`: starts them all in the background and returns.\n */\nexport async function startTunnels(\n cf: Cf,\n bin: string,\n items: CreateOptions[],\n opts: { detach?: boolean; protocol?: TransportProtocol } = {},\n): Promise<void> {\n const started: StartedTunnel[] = [];\n\n // Foreground is up-while-running: any exit (Ctrl-C, a signal, or a connector\n // crash) releases every tunnel started here (2-state model). Defined before the\n // create loop so `onExit` can reference it; registered as signal handlers before\n // the health wait so a Ctrl-C during that ≤30s window doesn't leak resources.\n let tornDown = false;\n const teardownAll = async (code: number): Promise<void> => {\n if (tornDown) return;\n tornDown = true;\n try {\n for (const s of started) {\n try {\n await removeTunnelSubdomain(cf, s.fqdn, { force: true, quiet: true });\n } catch {\n /* best-effort release */\n }\n }\n if (process.stdout.isTTY) clack.outro(`Stopped · released ${started.length} subdomain(s)`);\n } catch (err) {\n reportError(err);\n } finally {\n process.exit(code);\n }\n };\n\n const spin = clack.spinner();\n spin.start(items.length > 1 ? \"Creating tunnels…\" : \"Creating tunnel…\");\n for (const item of items) {\n spin.message(`Creating ${item.name ?? \"tunnel\"} (:${item.port})…`);\n const result = await createTunnelSubdomain(cf, item);\n const fqdn = result.host.hostname;\n const logFile = logFileFor(result.host.subdomain);\n const conn = startConnector({\n bin, token: result.token, detach: !!opts.detach, logFile, protocol: opts.protocol,\n onExit: opts.detach ? undefined : (code) => {\n if (!tornDown) {\n say.warn(`Connector for ${fqdn} exited.`);\n void teardownAll(code ?? 1);\n }\n },\n });\n await patchEntry(fqdn, { pid: conn.pid, bootId: currentBootId(), logFile, protocol: opts.protocol });\n started.push({\n fqdn, subdomain: result.host.subdomain, tunnelId: result.tunnelId,\n target: serviceUrl(item.proto, item.host ?? \"localhost\", item.port), pid: conn.pid,\n });\n }\n\n // Detached: print URLs + pids and exit; the connectors keep running.\n if (opts.detach) {\n spin.stop(`${started.length} tunnel(s) started in the background`);\n const lines = started.map((s) => `${formatRoute(s.fqdn, s.target)} ${dim(`pid ${s.pid}`)}`);\n clack.note(lines.join(\"\\n\"), \"running in background\");\n if (process.stdout.isTTY) clack.outro(\"Stop with: cloudtunnel delete <#|--all>\");\n return;\n }\n\n for (const sig of [\"SIGINT\", \"SIGHUP\", \"SIGTERM\"] as const) {\n process.on(sig, () => void teardownAll(0));\n }\n\n spin.message(\"Connecting to the Cloudflare edge…\");\n const healths = await Promise.all(started.map((s) => waitHealthy(cf, s.tunnelId, { timeoutMs: 30_000 })));\n const live = healths.filter((h: HealthResult) => h === \"healthy\").length;\n spin.stop(`${started.length} tunnel(s) started`);\n\n const lines = started.map((s, i) => `${formatRoute(s.fqdn, s.target)}${healths[i] === \"healthy\" ? \"\" : dim(` (${healths[i]})`)}`);\n clack.note(lines.join(\"\\n\"), `${live}/${started.length} live`);\n say.dim(\"Ctrl-C stops and releases them.\");\n}\n","import { type ChildProcess, execFileSync, spawn } from \"node:child_process\";\nimport { openSync } from \"node:fs\";\nimport { CliError } from \"../ui/errors.js\";\nimport { isOurConnector, type RegistryEntry } from \"./registry.js\";\n\nexport interface StartOptions {\n bin: string;\n token: string;\n detach: boolean;\n logFile: string;\n /** cloudflared edge transport (quic | http2 | auto). Omitted ⇒ cloudflared's\n * default. Force `http2` on UDP-hostile networks that drop idle QUIC. */\n protocol?: string;\n /** Foreground only: fired when the connector exits for ANY reason (crash,\n * bad token, or a signal) so the caller can tear down / report. */\n onExit?: (code: number | null) => void;\n}\n\nexport interface StartedConnector {\n pid: number;\n child?: ChildProcess;\n}\n\nconst sleep = (ms: number) => new Promise((r) => setTimeout(r, ms));\n\n/**\n * Spawn `cloudflared tunnel run`. The token is passed via the TUNNEL_TOKEN env\n * var — NEVER as an argv arg (argv is world-readable via `ps`/proc). Output goes\n * to a 0600 logfile (both foreground and detached) so the CLI can render its own\n * clean status instead of cloudflared's raw logs.\n */\nexport function startConnector(opts: StartOptions): StartedConnector {\n const args = [\"tunnel\", \"run\"];\n // Edge transport: pass as an explicit flag so it also lands in the connector\n // cmdline (visible/reproducible), not only via env.\n if (opts.protocol) args.push(\"--protocol\", opts.protocol);\n const env = { ...process.env, TUNNEL_TOKEN: opts.token };\n const fd = openSync(opts.logFile, \"a\", 0o600);\n const child = spawn(opts.bin, args, { env, detached: opts.detach, stdio: [\"ignore\", fd, fd] });\n if (!child.pid) throw new CliError(\"Failed to start the cloudflared connector.\");\n\n if (opts.detach) {\n child.unref();\n return { pid: child.pid };\n }\n child.on(\"exit\", (code) => opts.onExit?.(code));\n child.on(\"error\", () => opts.onExit?.(1));\n return { pid: child.pid, child };\n}\n\n/**\n * Stop a connector by registry entry. Verifies the pid is still OUR cloudflared\n * (alive, same boot, right cmdline) BEFORE signalling, so a reused pid held by\n * an unrelated process is never killed. Returns true if a stop was issued.\n */\nexport async function stopConnector(entry: RegistryEntry): Promise<boolean> {\n if (!entry.pid || !(await isOurConnector(entry))) return false;\n const pid = entry.pid;\n\n if (process.platform === \"win32\") {\n try {\n execFileSync(\"taskkill\", [\"/pid\", String(pid), \"/T\", \"/F\"], { stdio: \"ignore\" });\n } catch {\n return false;\n }\n return true;\n }\n\n try {\n process.kill(pid, \"SIGTERM\");\n } catch {\n return false;\n }\n await sleep(3000);\n if (await isOurConnector(entry)) {\n try {\n process.kill(pid, \"SIGKILL\");\n } catch {\n // already gone\n }\n }\n return true;\n}\n","import { existsSync, readFileSync, renameSync, writeFileSync } from \"node:fs\";\nimport { readFile } from \"node:fs/promises\";\nimport os from \"node:os\";\nimport lockfile from \"proper-lockfile\";\nimport { ensureDirs, registryFile } from \"../config/paths.js\";\nimport type { TransportProtocol } from \"../core/transport-protocol.js\";\n\nexport type EntryState = \"provisioning\" | \"running\" | \"stopped\" | \"orphaned\";\n\nexport interface RegistryEntry {\n subdomain: string;\n zone: string;\n zoneId: string;\n index?: number; // small stable handle shown as `#` in `ls` (target by number)\n tunnelId?: string;\n dnsRecordId?: string;\n port: number;\n proto: \"http\" | \"https\";\n host?: string; // forward target host (absent = localhost)\n protocol?: TransportProtocol; // cloudflared edge transport (absent = auto)\n pid?: number;\n bootId?: string;\n logFile?: string;\n createdAt: string;\n state: EntryState;\n}\n\n/** The real hostname for an entry. `@` is the apex, keyed in the registry by the\n * bare zone (NOT `@.zone`), so every entry→fqdn reconstruction must go through\n * this — otherwise apex tunnels become untargetable and leak. */\nexport function entryFqdn(e: Pick<RegistryEntry, \"subdomain\" | \"zone\">): string {\n return e.subdomain === \"@\" ? e.zone : `${e.subdomain}.${e.zone}`;\n}\n\ntype Registry = Record<string, RegistryEntry>;\n\n/** Stable per-boot id so a pid reused after a reboot is never mistaken for ours.\n * On systems without the Linux boot_id file (e.g. macOS), fall back to the boot\n * *time* bucketed to the minute — this is constant between invocations (unlike\n * `os.uptime()`, which increases every second and would break connector tracking). */\nexport function currentBootId(): string {\n try {\n return readFileSync(\"/proc/sys/kernel/random/boot_id\", \"utf8\").trim();\n } catch {\n const bootMinute = Math.floor((Date.now() - os.uptime() * 1000) / 60_000);\n return `boot-${bootMinute}-${os.hostname()}`;\n }\n}\n\nfunction readRegistry(): Registry {\n try {\n return JSON.parse(readFileSync(registryFile, \"utf8\")) as Registry;\n } catch {\n return {};\n }\n}\n\nfunction writeRegistry(reg: Registry): void {\n ensureDirs();\n const tmp = `${registryFile}.tmp`;\n writeFileSync(tmp, JSON.stringify(reg, null, 2), { mode: 0o600 });\n renameSync(tmp, registryFile); // atomic on the same filesystem\n}\n\n/** Lock-guarded read-modify-write (prevents lost updates across concurrent runs). */\nexport async function mutateRegistry<T>(fn: (reg: Registry) => T): Promise<T> {\n ensureDirs();\n if (!existsSync(registryFile)) writeFileSync(registryFile, \"{}\", { mode: 0o600 });\n const release = await lockfile.lock(registryFile, { retries: { retries: 10, minTimeout: 50 } });\n try {\n const reg = readRegistry();\n const result = fn(reg);\n writeRegistry(reg);\n return result;\n } finally {\n await release();\n }\n}\n\nexport function listEntries(): RegistryEntry[] {\n return Object.values(readRegistry());\n}\n\nexport function getEntry(fqdn: string): RegistryEntry | undefined {\n return readRegistry()[fqdn];\n}\n\nexport function upsertEntry(fqdn: string, patch: Partial<RegistryEntry> & Pick<RegistryEntry, \"subdomain\" | \"zone\" | \"zoneId\" | \"port\" | \"proto\">): Promise<void> {\n return mutateRegistry((reg) => {\n const prev = reg[fqdn];\n reg[fqdn] = {\n createdAt: prev?.createdAt ?? new Date().toISOString(),\n index: prev?.index ?? nextIndex(reg),\n state: \"provisioning\",\n ...prev,\n ...patch,\n };\n });\n}\n\n/** Smallest positive integer not currently used as an entry index (reused when\n * an entry is removed) — the friendly `#` handle shown in `ls`. */\nfunction nextIndex(reg: Registry): number {\n const used = new Set(\n Object.values(reg)\n .map((e) => e.index)\n .filter((n): n is number => typeof n === \"number\"),\n );\n let i = 1;\n while (used.has(i)) i++;\n return i;\n}\n\n/** Merge changed fields onto an existing entry under the lock (no stale\n * full-snapshot read outside the lock — avoids lost updates). No-op if absent. */\nexport function patchEntry(fqdn: string, patch: Partial<RegistryEntry>): Promise<void> {\n return mutateRegistry((reg) => {\n const prev = reg[fqdn];\n if (prev) reg[fqdn] = { ...prev, ...patch };\n });\n}\n\nexport function removeEntry(fqdn: string): Promise<void> {\n return mutateRegistry((reg) => {\n delete reg[fqdn];\n });\n}\n\nfunction pidAlive(pid: number): boolean {\n try {\n process.kill(pid, 0);\n return true;\n } catch {\n return false;\n }\n}\n\n/** Verify a pid is still OUR cloudflared: alive, same boot, and (Linux) its\n * cmdline is cloudflared — so we never signal a reused pid. */\nexport async function isOurConnector(entry: RegistryEntry): Promise<boolean> {\n if (!entry.pid || entry.bootId !== currentBootId()) return false;\n if (!pidAlive(entry.pid)) return false;\n if (process.platform === \"linux\") {\n try {\n const cmdline = await readFile(`/proc/${entry.pid}/cmdline`, \"utf8\");\n return cmdline.includes(\"cloudflared\");\n } catch {\n return false;\n }\n }\n return true; // non-Linux: bootId + liveness (best effort)\n}\n\n/** Mark entries whose connector is no longer alive as `stopped`. */\nexport async function reconcile(): Promise<RegistryEntry[]> {\n const entries = listEntries();\n for (const entry of entries) {\n if (entry.state === \"running\" && !(await isOurConnector(entry))) {\n const fqdn = entryFqdn(entry);\n await mutateRegistry((reg) => {\n const e = reg[fqdn];\n if (e) {\n e.state = \"stopped\";\n delete e.pid;\n }\n });\n }\n }\n return listEntries();\n}\n","import { cfPaginate, cfRequest, type Cf } from \"./client.js\";\nimport type { Connection, IngressRule, Tunnel } from \"./types.js\";\nimport { CliError } from \"../ui/errors.js\";\n\n/** Tunnels created by cloudtunnel carry this name prefix (ownership marker). */\nexport const MANAGED_TUNNEL_PREFIX = \"ct-\";\n\nexport function isManagedTunnel(tunnel: Tunnel): boolean {\n return tunnel.name.startsWith(MANAGED_TUNNEL_PREFIX);\n}\n\nexport async function createTunnel(cf: Cf, name: string): Promise<Tunnel> {\n const env = await cfRequest<Tunnel>(cf.token, \"POST\", `/accounts/${cf.accountId}/cfd_tunnel`, {\n name,\n config_src: \"cloudflare\",\n });\n return env.result;\n}\n\nexport function listTunnels(cf: Cf): Promise<Tunnel[]> {\n return cfPaginate<Tunnel>(cf.token, `/accounts/${cf.accountId}/cfd_tunnel?is_deleted=false`);\n}\n\nexport async function getTunnel(cf: Cf, id: string): Promise<Tunnel> {\n return (await cfRequest<Tunnel>(cf.token, \"GET\", `/accounts/${cf.accountId}/cfd_tunnel/${id}`)).result;\n}\n\nexport async function deleteTunnel(cf: Cf, id: string): Promise<void> {\n await cfRequest<unknown>(cf.token, \"DELETE\", `/accounts/${cf.accountId}/cfd_tunnel/${id}`);\n}\n\n/** Force-disconnect a tunnel's (possibly stale) connectors so it can be deleted. */\nexport async function cleanupConnections(cf: Cf, id: string): Promise<void> {\n await cfRequest<unknown>(cf.token, \"DELETE\", `/accounts/${cf.accountId}/cfd_tunnel/${id}/connections`);\n}\n\n/** Delete a tunnel; if Cloudflare refuses because it still has active\n * connections (a connector died but the edge hasn't reaped it yet), clean the\n * connections up and retry once. */\nexport async function deleteTunnelWithConnections(cf: Cf, id: string): Promise<void> {\n try {\n await deleteTunnel(cf, id);\n } catch (err) {\n if (err instanceof CliError && /active connections/i.test(err.message)) {\n await cleanupConnections(cf, id);\n await deleteTunnel(cf, id);\n } else {\n throw err;\n }\n }\n}\n\n/** The connector token (encodes tunnelId + secret) passed to `cloudflared`. */\nexport async function getTunnelToken(cf: Cf, id: string): Promise<string> {\n return (await cfRequest<string>(cf.token, \"GET\", `/accounts/${cf.accountId}/cfd_tunnel/${id}/token`)).result;\n}\n\n/** Full-replace ingress config (safe: one hostname + catch-all per tunnel). */\nexport async function putIngress(cf: Cf, id: string, ingress: IngressRule[]): Promise<void> {\n await cfRequest<unknown>(cf.token, \"PUT\", `/accounts/${cf.accountId}/cfd_tunnel/${id}/configurations`, {\n config: { ingress },\n });\n}\n\n/** Active connector instances (≥1 ⇒ tunnel is serving). */\nexport async function getConnections(cf: Cf, id: string): Promise<Connection[]> {\n const env = await cfRequest<Connection[]>(\n cf.token,\n \"GET\",\n `/accounts/${cf.accountId}/cfd_tunnel/${id}/connections`,\n );\n return env.result ?? [];\n}\n","import { getConnections } from \"../cloudflare/tunnels.js\";\nimport type { Cf } from \"../cloudflare/client.js\";\n\nexport type HealthResult = \"healthy\" | \"provisioning\" | \"dead\";\n\nconst sleep = (ms: number) => new Promise((r) => setTimeout(r, ms));\n\n/**\n * Poll the tunnel's connections until it's serving. `signal` is fired by the\n * caller when the connector process exits, so a dead connector returns `dead`\n * immediately instead of waiting out the timeout. `provisioning` is only\n * returned if the process is still alive at the deadline (never a false\n * \"healthy\"). Note: this measures connector↔edge, not local-origin, health.\n */\nexport async function waitHealthy(\n cf: Cf,\n tunnelId: string,\n opts: { signal?: AbortSignal; timeoutMs?: number } = {},\n): Promise<HealthResult> {\n const deadline = Date.now() + (opts.timeoutMs ?? 30_000);\n while (Date.now() < deadline) {\n if (opts.signal?.aborted) return \"dead\";\n try {\n const connections = await getConnections(cf, tunnelId);\n if (connections.length > 0) return \"healthy\";\n } catch {\n // transient API error — keep polling until the deadline\n }\n await sleep(2000);\n }\n return opts.signal?.aborted ? \"dead\" : \"provisioning\";\n}\n","import { randomInt } from \"node:crypto\";\nimport type { Cf } from \"../cloudflare/client.js\";\nimport { resolveZone } from \"../cloudflare/zones.js\";\nimport {\n MANAGED_TUNNEL_PREFIX,\n createTunnel,\n deleteTunnel,\n deleteTunnelWithConnections,\n getTunnel,\n getTunnelToken,\n isManagedTunnel,\n putIngress,\n} from \"../cloudflare/tunnels.js\";\nimport { createCname, deleteDnsRecord, findCname } from \"../cloudflare/dns.js\";\nimport type { DnsRecord } from \"../cloudflare/types.js\";\nimport { buildIngress } from \"./ingress.js\";\nimport { resolveHostSpec, type HostSpec } from \"./slug.js\";\nimport { currentBootId, patchEntry, removeEntry, upsertEntry } from \"../connector/registry.js\";\nimport { CliError } from \"../ui/errors.js\";\nimport { confirm, say } from \"../ui/output.js\";\n\nexport interface CreateOptions {\n port: number;\n proto: \"http\" | \"https\";\n name?: string;\n zone?: string;\n hostname?: string;\n host?: string; // forward target host (absent = localhost)\n defaultZone?: string;\n force?: boolean;\n yes?: boolean; // skip the \"replace existing record?\" confirmation\n}\n\nexport interface CreateResult {\n host: HostSpec;\n tunnelId: string;\n token: string;\n}\n\nconst tunnelIdFromCname = (content: string): string => content.replace(/\\.cfargotunnel\\.com\\.?$/, \"\");\n\n/**\n * Create a tunnel subdomain transactionally (idempotent). Any leftover tunnel\n * record for the same hostname is cleaned up first, so re-running `up` never\n * conflicts. A `provisioning` registry entry is written BEFORE any Cloudflare\n * resource; on failure everything is unwound in reverse and the original error\n * is surfaced.\n */\nexport async function createTunnelSubdomain(cf: Cf, opts: CreateOptions): Promise<CreateResult> {\n const host = resolveHostSpec(opts, opts.defaultZone);\n const zone = await resolveZone(cf.token, host.zone);\n\n const existing = await findCname(cf.token, zone.id, host.hostname);\n if (existing) {\n // A leftover tunnel record → replaceable. A non-tunnel DNS record (A record,\n // ordinary CNAME) → refuse unless --force, to avoid clobbering unrelated DNS.\n const isTunnelRecord = existing.content.endsWith(\".cfargotunnel.com\");\n if (!isTunnelRecord && !opts.force) {\n throw new CliError(`${host.hostname} is taken by a non-tunnel DNS record.`, {\n hint: \"pick another --subdomain/--hostname, or pass -f/--force to replace it\",\n });\n }\n // Confirm before replacing an existing record (interactive only; -f/-y skip).\n if (!opts.force && !opts.yes && process.stdin.isTTY) {\n const kind = isTunnelRecord ? \"tunnel\" : \"DNS\";\n if (!(await confirm(`${host.hostname} already has a ${kind} record. Replace it?`))) {\n throw new CliError(\"Cancelled.\", { exitCode: 130 });\n }\n }\n await releaseHostname(cf, zone.id, existing);\n }\n\n // Track provisioning BEFORE creating anything irreversible.\n await upsertEntry(host.hostname, {\n subdomain: host.subdomain, zone: host.zone, zoneId: zone.id,\n port: opts.port, proto: opts.proto, host: opts.host, state: \"provisioning\",\n });\n\n let tunnelId: string | undefined;\n let dnsRecordId: string | undefined;\n try {\n const suffix = randomInt(0x10000).toString(16).padStart(4, \"0\");\n const label = host.subdomain === \"@\" ? \"root\" : host.subdomain;\n const tunnel = await createTunnel(cf, `${MANAGED_TUNNEL_PREFIX}${label}-${suffix}`);\n tunnelId = tunnel.id;\n const token = await getTunnelToken(cf, tunnelId);\n await putIngress(cf, tunnelId, buildIngress({ hostname: host.hostname, port: opts.port, proto: opts.proto, host: opts.host }));\n const record = await createCname(cf.token, zone.id, host.hostname, tunnelId);\n dnsRecordId = record.id;\n await recordRunning(host, zone.id, tunnelId, dnsRecordId, opts);\n return { host, tunnelId, token };\n } catch (err) {\n const clean = await rollback(cf, zone.id, tunnelId, dnsRecordId, host.hostname);\n if (clean) await removeEntry(host.hostname);\n else await patchEntry(host.hostname, { state: \"orphaned\" });\n throw err;\n }\n}\n\nasync function recordRunning(host: HostSpec, zoneId: string, tunnelId: string, dnsRecordId: string, opts: CreateOptions): Promise<void> {\n await upsertEntry(host.hostname, {\n subdomain: host.subdomain, zone: host.zone, zoneId,\n tunnelId, dnsRecordId, port: opts.port, proto: opts.proto, host: opts.host,\n bootId: currentBootId(), state: \"running\",\n });\n}\n\n/** Free a hostname before recreating: delete its DNS record, and if it pointed\n * at a cloudtunnel-managed tunnel, delete that tunnel too (cleaning up any\n * lingering connections). A foreign tunnel is left alone — we only free the name. */\nasync function releaseHostname(cf: Cf, zoneId: string, record: DnsRecord): Promise<void> {\n if (record.content.endsWith(\".cfargotunnel.com\")) {\n const oldTunnelId = tunnelIdFromCname(record.content);\n try {\n const tunnel = await getTunnel(cf, oldTunnelId);\n if (isManagedTunnel(tunnel)) await deleteTunnelWithConnections(cf, oldTunnelId);\n } catch {\n /* tunnel already gone or not accessible — freeing the DNS name is enough */\n }\n }\n await deleteDnsRecord(cf.token, zoneId, record.id);\n}\n\n/** Unwind created resources in reverse. Never masks the original error; if a\n * step fails, report the leaked id and return false so the caller marks the\n * entry `orphaned`. */\nasync function rollback(cf: Cf, zoneId: string, tunnelId?: string, dnsRecordId?: string, hostname?: string): Promise<boolean> {\n let clean = true;\n if (dnsRecordId) {\n try { await deleteDnsRecord(cf.token, zoneId, dnsRecordId); }\n catch { clean = false; say.warn(`Left a DNS record behind for ${hostname} (${dnsRecordId}).`); }\n }\n if (tunnelId) {\n try { await deleteTunnel(cf, tunnelId); }\n catch { clean = false; say.warn(`Left tunnel ${tunnelId} behind — remove it with \\`cloudtunnel delete ${tunnelId} -f\\`.`); }\n }\n return clean;\n}\n","import { randomInt } from \"node:crypto\";\nimport { CliError } from \"../ui/errors.js\";\n\nconst ADJECTIVES = [\n \"brave\", \"calm\", \"clever\", \"eager\", \"gentle\", \"happy\", \"jolly\", \"kind\",\n \"lively\", \"mighty\", \"nimble\", \"proud\", \"quick\", \"royal\", \"swift\", \"witty\",\n];\nconst NOUNS = [\n \"otter\", \"falcon\", \"maple\", \"comet\", \"harbor\", \"lynx\", \"willow\", \"cedar\",\n \"raven\", \"meadow\", \"pixel\", \"quartz\", \"river\", \"sparrow\", \"tiger\", \"walnut\",\n];\n\nconst pick = <T>(arr: T[]): T => arr[randomInt(arr.length)]!;\n\n/** A friendly random subdomain, e.g. `brave-otter-1a2b` (the default when unnamed). */\nexport function randomSlug(): string {\n const suffix = randomInt(0x10000).toString(16).padStart(4, \"0\");\n return `${pick(ADJECTIVES)}-${pick(NOUNS)}-${suffix}`;\n}\n\nexport interface HostSpec {\n subdomain: string;\n zone: string;\n hostname: string;\n}\n\n/**\n * Resolve the target hostname from flags. Precedence: --hostname > --name+zone >\n * random-slug+zone. Zone comes from --zone or the saved default; missing zone is\n * an actionable error. (--hostname assumes `label.zone`; deeper subdomains need\n * the zone to be an actual Cloudflare zone.)\n */\nexport function resolveHostSpec(\n opts: { name?: string; zone?: string; hostname?: string },\n defaultZone?: string,\n): HostSpec {\n if (opts.hostname) {\n const dot = opts.hostname.indexOf(\".\");\n if (dot <= 0) throw new CliError(`Invalid hostname: ${opts.hostname}`);\n return {\n subdomain: opts.hostname.slice(0, dot),\n zone: opts.hostname.slice(dot + 1),\n hostname: opts.hostname,\n };\n }\n const zone = opts.zone ?? defaultZone;\n if (!zone) {\n throw new CliError(\"No zone specified and no default zone set.\", {\n hint: \"pass --zone <domain>, or run `cloudtunnel login --zone <domain>`\",\n });\n }\n const subdomain = opts.name ?? randomSlug();\n // `@` means the root/apex domain (Cloudflare flattens the proxied CNAME).\n const hostname = subdomain === \"@\" ? zone : `${subdomain}.${zone}`;\n return { subdomain, zone, hostname };\n}\n","import { readFileSync, writeFileSync } from \"node:fs\";\nimport { ensureDirs, scanCacheFile } from \"../config/paths.js\";\n\n/**\n * `ls --all` numbers the unmanaged tunnels it finds so they can be targeted by\n * `#` like tracked ones. Those rows live only on Cloudflare (no registry entry),\n * so the number→hostname mapping of the LAST scan is pinned here. A later\n * `delete <#>` resolves against this pin — never against a fresh re-scan —\n * so the number always means exactly the row the user saw on screen, even if\n * the account changed in between (deletion re-verifies DNS freshly anyway).\n */\nexport interface ScannedUnmanaged {\n fqdn: string;\n tunnelId: string;\n}\n\ntype ScanCache = Record<string, ScannedUnmanaged>;\n\n/** Replace the pin with this scan's numbering. Best-effort: listing must never\n * fail because the cache can't be written. */\nexport function saveUnmanagedScan(rows: Map<number, ScannedUnmanaged>): void {\n try {\n ensureDirs();\n const cache: ScanCache = {};\n for (const [num, row] of rows) cache[String(num)] = row;\n writeFileSync(scanCacheFile, JSON.stringify(cache, null, 2), { mode: 0o600 });\n } catch {\n /* the pin is a convenience, not state */\n }\n}\n\n/** The unmanaged row this `#` pointed at in the last `ls --all` (undefined when\n * the number was never shown, or no scan has run). */\nexport function lookupUnmanagedByIndex(num: number): ScannedUnmanaged | undefined {\n try {\n const cache = JSON.parse(readFileSync(scanCacheFile, \"utf8\")) as ScanCache;\n return cache[String(num)];\n } catch {\n return undefined;\n }\n}\n","import type { Cf } from \"../cloudflare/client.js\";\nimport { resolveZone } from \"../cloudflare/zones.js\";\nimport { deleteTunnelWithConnections, getTunnel, isManagedTunnel, listTunnels } from \"../cloudflare/tunnels.js\";\nimport { deleteDnsRecord, findCname, isManagedDns } from \"../cloudflare/dns.js\";\nimport type { DnsRecord, Tunnel } from \"../cloudflare/types.js\";\nimport { CliError } from \"../ui/errors.js\";\nimport { say } from \"../ui/output.js\";\nimport { entryFqdn, getEntry, listEntries, reconcile, removeEntry, type RegistryEntry } from \"../connector/registry.js\";\nimport { stopConnector } from \"../connector/process.js\";\nimport { serviceUrl } from \"./ingress.js\";\nimport { serviceState } from \"./service.js\";\nimport { saveUnmanagedScan, type ScannedUnmanaged } from \"./unmanaged-scan-cache.js\";\n\nconst tunnelIdFromCname = (content: string): string => content.replace(/\\.cfargotunnel\\.com\\.?$/, \"\");\nconst isNotFound = (err: unknown): boolean => err instanceof CliError && err.status === 404;\nconst zoneFromFqdn = (fqdn: string): string => fqdn.slice(fqdn.indexOf(\".\") + 1);\n\n/** Resolve a target to its registry entry / fqdn. Accepts a full hostname, the\n * `#` number, a subdomain name, or a tunnel-id prefix (all shown in `ls`).\n * Refuses an ambiguous match. */\nexport function resolveTarget(target: string): { fqdn: string; entry?: RegistryEntry } {\n if (target.includes(\".\")) return { fqdn: target, entry: getEntry(target) };\n const entries = listEntries();\n if (/^\\d+$/.test(target)) {\n const byIndex = entries.find((e) => e.index === Number(target));\n if (byIndex) return { fqdn: entryFqdn(byIndex), entry: byIndex };\n }\n const byId = entries.filter((e) => e.tunnelId?.startsWith(target));\n const matches = byId.length > 0 ? byId : entries.filter((e) => e.subdomain === target);\n if (matches.length > 1) {\n throw new CliError(`\"${target}\" matches multiple subdomains.`, {\n hint: `use a full hostname or a longer id: ${matches.map(entryFqdn).join(\", \")}`,\n });\n }\n const entry = matches[0];\n if (!entry) {\n throw new CliError(`No tracked subdomain matching \"${target}\".`, { hint: \"see `cloudtunnel ls` for the #, name, or id\" });\n }\n return { fqdn: entryFqdn(entry), entry };\n}\n\nexport interface RemoveOptions { force?: boolean; dryRun?: boolean; quiet?: boolean }\n\n/** Release a subdomain: stop the connector, then delete the tunnel + DNS on\n * Cloudflare. Re-verifies fresh state (cached ids are hints), ownership-gates\n * unmanaged resources, and tolerates already-deleted parts. */\nexport async function removeTunnelSubdomain(cf: Cf, target: string, opts: RemoveOptions = {}): Promise<void> {\n const { fqdn, entry } = resolveTarget(target);\n if (!entry && !opts.force) {\n throw new CliError(`${fqdn} is not managed by cloudtunnel.`, { hint: \"pass --force to release it anyway\" });\n }\n const zoneId = entry?.zoneId ?? (await resolveZone(cf.token, zoneFromFqdn(fqdn))).id;\n\n const record = await findCname(cf.token, zoneId, fqdn); // fresh, authoritative\n if (record && !isManagedDns(record) && !opts.force) {\n throw new CliError(`${fqdn} points to a record not managed by cloudtunnel.`, { hint: \"pass --force to release it\" });\n }\n const tunnelId = record ? tunnelIdFromCname(record.content) : entry?.tunnelId;\n\n if (opts.dryRun) {\n say.info(`Would release: tunnel ${tunnelId ?? \"(none)\"}${record ? `, DNS ${record.id}` : \"\"}`);\n return;\n }\n\n if (entry) await stopConnector(entry);\n if (tunnelId) {\n let tunnel: Tunnel | undefined;\n try {\n tunnel = await getTunnel(cf, tunnelId);\n } catch (err) {\n if (!isNotFound(err)) throw err; // transient error → don't silently orphan\n }\n if (tunnel && !isManagedTunnel(tunnel) && !opts.force) {\n throw new CliError(`Tunnel ${tunnelId} is not managed by cloudtunnel.`, { hint: \"pass --force\" });\n }\n if (tunnel) {\n try {\n await deleteTunnelWithConnections(cf, tunnelId);\n } catch (err) {\n if (!isNotFound(err)) throw err;\n }\n }\n }\n if (record) {\n try {\n await deleteDnsRecord(cf.token, zoneId, record.id);\n } catch (err) {\n if (!isNotFound(err)) throw err;\n }\n }\n await removeEntry(fqdn);\n if (!opts.quiet) say.ok(`Released ${fqdn}`);\n}\n\n/** Only strings that look like a tunnel-id prefix (UUID chars, ≥6) are matched\n * remotely — a mistyped name or `#` number must never match an account tunnel. */\nconst TUNNEL_ID_PREFIX_RE = /^[0-9a-f][0-9a-f-]{5,}$/;\n\nexport interface RemoteTarget { tunnel: Tunnel; fqdn?: string }\n\n/** Resolve a target the registry doesn't know as a tunnel-id prefix on the\n * Cloudflare account (an untracked tunnel has no registry entry, so its id can\n * only be matched remotely). The hostname is recovered from the tunnel's\n * cfargotunnel CNAME when one exists; a DNS-less (leaked) tunnel comes back\n * without an fqdn. Null when the target doesn't look like an id or matches\n * no tunnel. */\nexport async function resolveRemoteTarget(cf: Cf, target: string): Promise<RemoteTarget | null> {\n if (!TUNNEL_ID_PREFIX_RE.test(target)) return null;\n const matches = (await listTunnels(cf)).filter((t) => t.id.startsWith(target));\n if (matches.length > 1) {\n throw new CliError(`\"${target}\" matches ${matches.length} tunnels on the account.`, { hint: \"use a longer id prefix\" });\n }\n const tunnel = matches[0];\n if (!tunnel) return null;\n const { listCargoCnames } = await import(\"../cloudflare/dns.js\");\n const { listZones } = await import(\"../cloudflare/zones.js\");\n for (const zone of await listZones(cf.token)) {\n const rec = (await listCargoCnames(cf.token, zone.id)).find((r) => tunnelIdFromCname(r.content) === tunnel.id);\n if (rec) return { tunnel, fqdn: rec.name };\n }\n return { tunnel };\n}\n\n/** Release a tunnel that has no DNS record (an id-only target): same ownership\n * gate as the fqdn path, honors --dry-run. */\nexport async function removeTunnelById(cf: Cf, tunnel: Tunnel, opts: RemoveOptions = {}): Promise<void> {\n if (!isManagedTunnel(tunnel) && !opts.force) {\n throw new CliError(`Tunnel ${tunnel.id} is not managed by cloudtunnel.`, { hint: \"pass --force to release it\" });\n }\n if (opts.dryRun) {\n say.info(`Would release: tunnel ${tunnel.id} (no DNS record)`);\n return;\n }\n await deleteTunnelWithConnections(cf, tunnel.id);\n if (!opts.quiet) say.ok(`Released tunnel ${tunnel.id}`);\n}\n\nexport interface LsRow { num: string; url: string; target: string; protocol: string; state: string; service: string; pid: string; managed: boolean }\n\n/** Reconcile + list tracked subdomains: `# | URL | TARGET | PROTOCOL | STATE | SERVICE | PID`.\n * PROTOCOL is the cloudflared edge transport the connector was started with\n * (absent = \"auto\", cloudflared's default). SERVICE is the per-subdomain systemd\n * unit's state (\"-\" when none). `all` also scans every zone for cfargotunnel\n * CNAMEs created outside cloudtunnel. */\nexport async function listAll(cf: Cf, opts: { all?: boolean } = {}): Promise<LsRow[]> {\n const entries = await reconcile();\n const tunnels = new Map((await listTunnels(cf)).map((t) => [t.id, t]));\n const rows: LsRow[] = entries.map((e) => {\n const fqdn = entryFqdn(e);\n const gone = e.tunnelId ? !tunnels.has(e.tunnelId) : false;\n const svc = serviceState(fqdn);\n return {\n num: e.index ? String(e.index) : \"-\",\n url: `https://${fqdn}`,\n target: serviceUrl(e.proto, e.host ?? \"localhost\", e.port),\n protocol: e.protocol ?? \"auto\",\n state: !gone && e.state === \"running\" ? \"up\" : \"down\",\n service: svc === \"none\" ? \"-\" : svc,\n pid: e.state === \"running\" && e.pid ? String(e.pid) : \"-\",\n managed: true,\n };\n });\n if (opts.all) {\n const { listCargoCnames } = await import(\"../cloudflare/dns.js\");\n const { listZones } = await import(\"../cloudflare/zones.js\");\n const tracked = new Set(entries.map(entryFqdn));\n const unmanaged: DnsRecord[] = [];\n for (const zone of await listZones(cf.token)) {\n for (const rec of await listCargoCnames(cf.token, zone.id)) {\n if (!tracked.has(rec.name)) unmanaged.push(rec);\n }\n }\n // Unmanaged rows get a `#` too (continuing after the tracked ones, sorted\n // for a stable display) so `delete <#>` works on them. The numbering is\n // pinned to this scan — see unmanaged-scan-cache.\n unmanaged.sort((a, b) => a.name.localeCompare(b.name));\n let next = Math.max(0, ...entries.map((e) => e.index ?? 0)) + 1;\n const scan = new Map<number, ScannedUnmanaged>();\n for (const rec of unmanaged) {\n scan.set(next, { fqdn: rec.name, tunnelId: tunnelIdFromCname(rec.content) });\n rows.push({ num: String(next), url: `https://${rec.name}`, target: \"-\", protocol: \"-\", state: \"unmanaged\", service: \"-\", pid: \"-\", managed: false });\n next++;\n }\n saveUnmanagedScan(scan);\n }\n return rows;\n}\n","import { CliError } from \"../ui/errors.js\";\nimport { selectOne } from \"../ui/output.js\";\nimport type { Cf } from \"../cloudflare/client.js\";\nimport { listZones } from \"../cloudflare/zones.js\";\nimport type { Credentials } from \"../config/store.js\";\n\n/** The domain for a command: `-d` → single zone → picker (TTY) → saved default\n * (non-TTY) → error. Shared by `up` and `relay` so both resolve identically. */\nexport async function resolveDomain(cf: Cf, opts: { domain?: string }, creds: Credentials): Promise<string> {\n if (opts.domain) return opts.domain;\n const zones = await listZones(cf.token);\n if (zones.length === 0) throw new CliError(\"No domains found in this Cloudflare account.\");\n if (zones.length === 1) return zones[0]!.name;\n if (process.stdin.isTTY) return (await selectOne(\"Choose a domain\", zones, (z) => z.name)).name;\n if (creds.defaultZone) return creds.defaultZone;\n throw new CliError(\"Multiple domains in this account — pick one.\", { hint: \"pass -d <domain>\" });\n}\n","import { CliError } from \"../ui/errors.js\";\n\n/**\n * cloudflared edge transport (NOT the local service scheme). `quic` is UDP-based\n * and fastest, but UDP-hostile networks drop idle QUIC sessions (→ Cloudflare\n * 530/502); `http2` runs over TCP and stays stable there. `auto` lets cloudflared\n * choose (defaults to quic when the network probe passes).\n */\nexport type TransportProtocol = \"auto\" | \"http2\" | \"quic\";\n\nexport function parseTransportProtocol(value: string): TransportProtocol {\n if (value === \"auto\" || value === \"http2\" || value === \"quic\") return value;\n throw new CliError(`Invalid protocol \"${value}\".`, { hint: \"use auto, http2, or quic\" });\n}\n","import type { Command } from \"commander\";\nimport { printTable, say } from \"../ui/output.js\";\nimport { ensureAuth } from \"../config/ensure-auth.js\";\nimport { resolveCf } from \"../cloudflare/client.js\";\nimport { listAll } from \"../core/orchestrator-manage.js\";\n\nexport function registerLs(program: Command): void {\n program\n .command(\"ls\")\n .alias(\"ps\")\n .description(\"List tunnel subdomains (managed by default; --all scans the whole account)\")\n .option(\"--all\", \"scan every zone in the account (slower; shows unmanaged tunnels too)\")\n .action(async (opts: { all?: boolean }) => {\n await ensureAuth();\n const cf = resolveCf();\n const rows = await listAll(cf, { all: opts.all });\n if (rows.length === 0) {\n say.info(\"No tunnel subdomains yet. Create one: `cloudtunnel 3000`\");\n return;\n }\n printTable(\n [\"#\", \"URL\", \"TARGET\", \"PROTOCOL\", \"STATE\", \"SERVICE\", \"PID\"],\n rows.map((r) => [r.num, r.url, r.target, r.protocol, r.state, r.service, r.pid]),\n );\n });\n}\n","import type { Command } from \"commander\";\nimport { CliError } from \"../ui/errors.js\";\nimport { say } from \"../ui/output.js\";\nimport { ensureAuth } from \"../config/ensure-auth.js\";\nimport { resolveCf, type Cf } from \"../cloudflare/client.js\";\nimport { entryFqdn, listEntries } from \"../connector/registry.js\";\nimport { removeTunnelById, removeTunnelSubdomain, resolveRemoteTarget, resolveTarget } from \"../core/orchestrator-manage.js\";\nimport { lookupUnmanagedByIndex } from \"../core/unmanaged-scan-cache.js\";\nimport { serviceName, serviceState, uninstallService } from \"../core/service.js\";\n\ninterface DeleteOptions { all?: boolean; force?: boolean; dryRun?: boolean }\n\n/** Remove a subdomain's boot service (if any) first so its supervisor can't\n * restart the connector mid-teardown, then release the tunnel + DNS. */\nasync function deleteOne(cf: Cf, fqdn: string, opts: DeleteOptions): Promise<void> {\n const hasService = serviceState(fqdn) !== \"none\";\n if (hasService && !opts.dryRun) uninstallService(fqdn);\n await removeTunnelSubdomain(cf, fqdn, { force: opts.force, dryRun: opts.dryRun });\n if (!hasService) return;\n if (opts.dryRun) say.info(`Would also remove boot service ${serviceName(fqdn)}`);\n else say.ok(`Removed boot service ${serviceName(fqdn)}`);\n}\n\nexport function registerDelete(program: Command): void {\n program\n .command(\"delete\")\n .argument(\"[targets...]\", \"subdomains to remove by # / name / URL / tunnel-id (omit with --all)\")\n .description(\"Release tunnel(s) — deletes the tunnel + DNS, and any systemd boot service\")\n .option(\"--all\", \"release every tracked subdomain\")\n .option(\"-f, --force\", \"release even a resource not created by cloudtunnel\")\n .option(\"--dry-run\", \"show what would be released without doing it\")\n .action(async (targets: string[], opts: DeleteOptions) => {\n await ensureAuth();\n const cf = resolveCf();\n\n if (opts.all) {\n const entries = listEntries();\n if (entries.length === 0) {\n say.info(\"Nothing to release.\");\n return;\n }\n for (const e of entries) {\n const fqdn = entryFqdn(e);\n try {\n await deleteOne(cf, fqdn, opts);\n } catch (err) {\n say.warn(`Could not release ${fqdn}: ${(err as Error).message}`);\n }\n }\n return;\n }\n\n if (targets.length === 0) throw new CliError(\"Pass a subdomain (# / name / URL / tunnel-id) or --all.\");\n for (const target of targets) {\n let fqdn: string;\n try {\n ({ fqdn } = resolveTarget(target));\n } catch (err) {\n // Unknown to the local registry — the target may be the `#` an\n // unmanaged tunnel was shown with in the last `ls --all`, or the id\n // of an untracked tunnel (created elsewhere, or leaked by a failed\n // run); try both against what's pinned/on the account before giving up.\n const scanned = /^\\d+$/.test(target) ? lookupUnmanagedByIndex(Number(target)) : undefined;\n if (scanned) {\n say.info(`${target} → ${scanned.fqdn} (unmanaged, numbered by the last \\`ls --all\\`)`);\n fqdn = scanned.fqdn;\n } else {\n const remote = await resolveRemoteTarget(cf, target);\n if (!remote) throw err;\n if (!remote.fqdn) {\n await removeTunnelById(cf, remote.tunnel, opts);\n continue;\n }\n fqdn = remote.fqdn;\n }\n }\n await deleteOne(cf, fqdn, opts);\n }\n });\n}\n","import type { Command } from \"commander\";\nimport { closeSync, existsSync, openSync, readFileSync, readSync, statSync, watch } from \"node:fs\";\nimport { CliError } from \"../ui/errors.js\";\nimport { say } from \"../ui/output.js\";\nimport { resolveTarget } from \"../core/orchestrator-manage.js\";\n\ninterface LogsOptions {\n follow?: boolean;\n lines?: string;\n}\n\n/** Print the last `n` lines of a file; return the file's byte size (follow start). */\nfunction printTail(file: string, n: number): number {\n const lines = readFileSync(file, \"utf8\").split(\"\\n\");\n const tail = lines.slice(-n).join(\"\\n\");\n process.stdout.write(tail.endsWith(\"\\n\") ? tail : `${tail}\\n`);\n return statSync(file).size;\n}\n\n/** Tail -f: print appended bytes as the connector writes them. Ctrl-C to stop. */\nfunction follow(file: string, fromPos: number): void {\n let pos = fromPos;\n say.dim(\"— following (Ctrl-C to stop) —\");\n const watcher = watch(file, () => {\n const size = statSync(file).size;\n if (size < pos) {\n pos = 0; // file was truncated/rotated\n return;\n }\n if (size > pos) {\n const fd = openSync(file, \"r\");\n const buf = Buffer.alloc(size - pos);\n readSync(fd, buf, 0, size - pos, pos);\n closeSync(fd);\n process.stdout.write(buf.toString(\"utf8\"));\n pos = size;\n }\n });\n process.on(\"SIGINT\", () => {\n watcher.close();\n process.exit(0);\n });\n}\n\nexport function registerLogs(program: Command): void {\n program\n .command(\"logs\")\n .argument(\"<target>\", \"subdomain name / hostname / id / #\")\n .description(\"Show the connector log for a subdomain (use -f to follow)\")\n .option(\"-f, --follow\", \"keep printing new log lines (like tail -f)\")\n .option(\"-n, --lines <n>\", \"number of lines to show\", \"50\")\n .action((name: string, opts: LogsOptions) => {\n const { fqdn, entry } = resolveTarget(name);\n if (!entry?.logFile || !existsSync(entry.logFile)) {\n throw new CliError(`No logs for ${fqdn} yet.`, { hint: \"start it with `cloudtunnel up` or `cloudtunnel run`\" });\n }\n const n = Math.max(1, Number(opts.lines) || 50);\n const pos = printTail(entry.logFile, n);\n if (opts.follow) follow(entry.logFile, pos);\n });\n}\n","import type { Command } from \"commander\";\nimport { openSync } from \"node:fs\";\nimport { spawn } from \"node:child_process\";\nimport { join } from \"node:path\";\nimport pc from \"picocolors\";\nimport { CliError } from \"../ui/errors.js\";\nimport { say, note } from \"../ui/output.js\";\nimport { ensureAuth } from \"../config/ensure-auth.js\";\nimport { resolveCf } from \"../cloudflare/client.js\";\nimport { ensureCloudflared } from \"../connector/binary.js\";\nimport { logDir, ensureDirs } from \"../config/paths.js\";\nimport { ensureRelaySecret } from \"../config/relay-secret.js\";\nimport { startProxy } from \"../core/api-proxy-server.js\";\nimport { resolveDomain } from \"../core/resolve-domain.js\";\nimport { startTunnels } from \"../core/up-runner.js\";\nimport type { CreateOptions } from \"../core/orchestrator-create.js\";\nimport { assertServiceSupported, installServiceForSpec } from \"../core/service.js\";\n\ninterface RelayOptions {\n domain?: string;\n detach?: boolean;\n service?: boolean;\n force?: boolean;\n yes?: boolean;\n}\n\nconst DEFAULT_SUB = \"cfapi\";\n\n/** fqdn for a relay subdomain (\"@\" → apex). */\nfunction fqdnFor(sub: string, domain: string): string {\n return sub === \"@\" ? domain : `${sub}.${domain}`;\n}\n\n/**\n * Re-spawn `cloudtunnel relay <sub>` as a DETACHED child running the foreground\n * path (note: NO `--detach` in argv → no recursion). The whole command re-spawns,\n * not just the connector, because the reverse proxy lives IN this process and\n * would die when the CLI exits. The secret is NOT passed on argv — the child\n * reads it back from 0600 config via `getRelaySecret()`.\n */\nfunction spawnDetachedRelay(sub: string, domain: string): void {\n const script = process.argv[1];\n if (!script) throw new CliError(\"Cannot resolve the cloudtunnel executable path.\");\n ensureDirs();\n const logFile = join(logDir, `relay-${sub === \"@\" ? \"root\" : sub}.log`);\n const fd = openSync(logFile, \"a\", 0o600);\n const args = [script, \"relay\", sub, \"-d\", domain, \"-f\", \"-y\"];\n const child = spawn(process.execPath, args, { detached: true, stdio: [\"ignore\", fd, fd] });\n child.unref();\n}\n\n/**\n * The \"relay ready\" lines. The secret is included ONLY for an interactive (TTY)\n * operator. A re-spawned detach child or boot service runs the same foreground\n * path with stdout → a logfile / journald — where the secret must NEVER land: the\n * relay hostname is public and the secret is its sole access gate. Non-TTY ⇒ a\n * bare readiness line, no secret. Pure + exported so the guard is unit-tested.\n */\nexport function relayReadyLines(fqdn: string, secret: string, tty: boolean): string[] {\n const url = `https://${fqdn}`;\n if (!tty) return [`relay ready at ${url}`];\n const base = `${url}/client/v4`;\n return [\n `URL ${pc.green(url)}`,\n `Secret ${pc.bold(secret)} ${pc.dim(\"(store it — the client needs it, shown once)\")}`,\n \"\",\n pc.bold(\"On the blocked client:\"),\n ` export CLOUDTUNNEL_API_BASE=${base}`,\n ` export CLOUDTUNNEL_RELAY_SECRET=${secret}`,\n ` printf %s \"$CF_TOKEN\" | cloudtunnel login --token-stdin`,\n pc.dim(\" (mint the CF token on an unblocked host — dash.cloudflare.com is blocked too)\"),\n ];\n}\n\n/** Print the relay URL + (interactive only) the shared secret and client hint. */\nfunction printRelayReady(fqdn: string, secret: string, kind: \"foreground\" | \"detach\" | \"service\"): void {\n const tty = !!process.stdout.isTTY;\n const lines = relayReadyLines(fqdn, secret, tty);\n if (tty) note(lines.join(\"\\n\"), \"relay ready\");\n else say.dim(lines[0]!);\n if (tty && kind === \"detach\") say.dim(\" → running in background · stop with: cloudtunnel delete \" + fqdn);\n if (tty && kind === \"service\") say.dim(\" → boot service installed · view: cloudtunnel ls · remove: cloudtunnel delete \" + fqdn);\n}\n\nasync function runRelay(sub: string, opts: RelayOptions): Promise<void> {\n const creds = await ensureAuth();\n const cf = resolveCf();\n const domain = await resolveDomain(cf, opts, creds);\n const fqdn = fqdnFor(sub, domain);\n\n // --service: install a boot unit that re-runs the relay foreground on boot.\n if (opts.service) {\n assertServiceSupported();\n const secret = ensureRelaySecret();\n installServiceForSpec({ command: \"relay\", subdomain: sub, port: 0, zone: domain, proto: \"http\" });\n printRelayReady(fqdn, secret, \"service\");\n return;\n }\n\n // --detach: re-spawn the whole command detached (proxy must live with it).\n if (opts.detach) {\n const secret = ensureRelaySecret();\n spawnDetachedRelay(sub, domain);\n printRelayReady(fqdn, secret, \"detach\");\n return;\n }\n\n // Foreground: start the proxy in-process, then expose its ephemeral loopback\n // port through the normal tunnel machinery. The proxy speaks plain http on\n // loopback, so the tunnel ingress is always http.\n const bin = await ensureCloudflared();\n const secret = ensureRelaySecret();\n const proxy = await startProxy({ secret, log: (line) => say.dim(line) });\n const item: CreateOptions = {\n port: proxy.port, proto: \"http\", name: sub, zone: domain,\n defaultZone: creds.defaultZone, force: opts.force, yes: opts.yes,\n };\n await startTunnels(cf, bin, [item], {});\n printRelayReady(fqdn, secret, \"foreground\");\n}\n\nexport function registerRelay(program: Command): void {\n program\n .command(\"relay [subdomain]\")\n .description(\"Expose a locked Cloudflare-API reverse proxy via a tunnel (for clients that can't reach api.cloudflare.com directly)\")\n .option(\"-d, --domain <domain>\", \"domain for the relay subdomain (prompted from a list if unset)\")\n .option(\"--detach\", \"run the relay in the background\")\n .option(\"--service\", \"register the relay as a boot service (systemd · launchd · Task Scheduler)\")\n .option(\"-f, --force\", \"replace a non-tunnel DNS record occupying the hostname\")\n .option(\"-y, --yes\", \"don't prompt before replacing an existing record\")\n .action((subdomain: string | undefined, opts: RelayOptions) => runRelay(subdomain ?? DEFAULT_SUB, opts));\n}\n","import http from \"node:http\";\nimport type { AddressInfo } from \"node:net\";\nimport { RELAY_SECRET_HEADER } from \"../config/relay-secret.js\";\n\n/** The ONLY host this relay ever forwards to. A compile-time constant is the\n * structural SSRF/open-proxy defense: the upstream is never derived from the\n * request. `upstream` is overridable ONLY for tests. */\nconst DEFAULT_UPSTREAM = \"https://api.cloudflare.com\";\nconst SECRET_HEADER_LC = RELAY_SECRET_HEADER.toLowerCase();\n\n/** Request headers never forwarded upstream (hop-by-hop + ones `fetch` sets from\n * the URL). The secret header is dropped separately. */\nconst HOP_BY_HOP = new Set([\n \"connection\", \"keep-alive\", \"proxy-authenticate\", \"proxy-authorization\",\n \"te\", \"trailer\", \"transfer-encoding\", \"upgrade\", \"host\", \"content-length\",\n]);\n\nexport interface ProxyHandle {\n port: number;\n url: string;\n close(): Promise<void>;\n}\n\nexport interface StartProxyOptions {\n /** Shared secret the client must present in `X-CT-Relay-Secret`. */\n secret: string;\n /** Upstream origin — tests only; defaults to api.cloudflare.com. */\n upstream?: string;\n /** Per-request access-log sink (method + path + status only — NEVER headers).\n * The `relay` command passes one so operators can see traffic arriving. */\n log?: (line: string) => void;\n}\n\nfunction send(res: http.ServerResponse, code: number, body?: unknown): void {\n res.writeHead(code, { \"content-type\": \"application/json\" });\n res.end(body === undefined ? \"\" : JSON.stringify(body));\n}\n\n/**\n * Start a locked-down reverse proxy on `127.0.0.1:0` (ephemeral, loopback only).\n * Forwards origin-form requests to a single constant upstream, gated by a shared\n * secret. Never logs the Authorization or secret header. Pure module: no CLI, no\n * tunnel — the caller exposes `handle.port` however it likes.\n */\nexport function startProxy(opts: StartProxyOptions): Promise<ProxyHandle> {\n const upstream = opts.upstream ?? DEFAULT_UPSTREAM;\n const upstreamOrigin = new URL(upstream).origin;\n const log = opts.log ?? (() => {});\n\n const server = http.createServer((req, res) => {\n handle(req, res).catch(() => {\n if (!res.headersSent) send(res, 500, { error: \"relay internal error\" });\n else res.end();\n });\n });\n\n async function handle(req: http.IncomingMessage, res: http.ServerResponse): Promise<void> {\n // 1. Origin-form only. Reject CONNECT + absolute/authority-form request lines.\n // Access log: method + path + status only. NEVER headers (no token/secret).\n const trace = (status: number, note?: string): void =>\n log(`[relay] ${status} ${req.method} ${req.url ?? \"-\"}${note ? \" \" + note : \"\"}`);\n\n if (req.method === \"CONNECT\" || !req.url || !req.url.startsWith(\"/\")) {\n trace(400, \"bad-form\");\n return send(res, 400, { error: \"origin-form request required\" });\n }\n // Structural SSRF guard: resolve against the constant upstream and require the\n // origin to stay put. Catches `//evil.com` (protocol-relative) and absolute URIs.\n let target: URL;\n try {\n target = new URL(req.url, upstream);\n } catch {\n trace(400, \"bad-path\");\n return send(res, 400, { error: \"bad request path\" });\n }\n if (target.origin !== upstreamOrigin) {\n trace(400, \"ssrf\");\n return send(res, 400, { error: \"path escapes upstream\" });\n }\n\n // 2. Secret gate — before ANY upstream I/O.\n if (req.headers[SECRET_HEADER_LC] !== opts.secret) {\n trace(403, \"secret\");\n return send(res, 403, { error: \"relay secret missing or invalid\" });\n }\n\n // 3. Buffer body (CF API payloads are small → avoids duplex streaming).\n const hasBody = req.method !== \"GET\" && req.method !== \"HEAD\";\n let body: Buffer | undefined;\n if (hasBody) {\n const chunks: Buffer[] = [];\n for await (const c of req) chunks.push(c as Buffer);\n body = chunks.length ? Buffer.concat(chunks) : undefined;\n }\n\n // 4. Forward headers minus hop-by-hop + secret. `fetch` sets Host from the URL.\n const headers: Record<string, string> = {};\n for (const [k, v] of Object.entries(req.headers)) {\n if (v === undefined) continue;\n const lk = k.toLowerCase();\n if (HOP_BY_HOP.has(lk) || lk === SECRET_HEADER_LC) continue;\n headers[k] = Array.isArray(v) ? v.join(\", \") : v;\n }\n\n let up: Response;\n try {\n up = await fetch(target.href, { method: req.method, headers, body, redirect: \"manual\" });\n } catch {\n trace(502, \"upstream-error\");\n return send(res, 502, { error: \"relay upstream unreachable\" });\n }\n\n // 5. Pass status + content-type + body through. Forward retry-after too, so\n // the client's rate-limit backoff honors the upstream through the relay.\n trace(up.status);\n const outHeaders: Record<string, string> = {\n \"content-type\": up.headers.get(\"content-type\") ?? \"application/json\",\n };\n const retryAfter = up.headers.get(\"retry-after\");\n if (retryAfter) outHeaders[\"retry-after\"] = retryAfter;\n res.writeHead(up.status, outHeaders);\n res.end(Buffer.from(await up.arrayBuffer()));\n }\n\n return new Promise<ProxyHandle>((resolve, reject) => {\n server.once(\"error\", reject);\n server.listen(0, \"127.0.0.1\", () => {\n const port = (server.address() as AddressInfo).port;\n resolve({\n port,\n url: `http://127.0.0.1:${port}`,\n close: () => new Promise<void>((res) => server.close(() => res())),\n });\n });\n });\n}\n"],"mappings":"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","names":["pc","existsSync","writeFileSync","join","join","join","assertSupported","install","label","state","uninstall","execFileSync","existsSync","writeFileSync","dirname","join","os","label","join","os","dirname","execFileSync","assertSupported","install","writeFileSync","uninstall","state","existsSync","assertSupported","install","label","state","uninstall","execFileSync","writeFileSync","tmpdir","join","label","xml","execFileSync","assertSupported","install","join","tmpdir","writeFileSync","uninstall","state","join","legacyUnitExists","removeLegacyUnit","existsSync","legacyUnitExists","writeFileSync","removeLegacyUnit","execFileSync","execFileSync","listZones","listZones","clack","execFileSync","existsSync","readFileSync","writeFileSync","join","execFileSync","join","readFileSync","existsSync","writeFileSync","join","clack","execFileSync","spawn","existsSync","readFileSync","renameSync","writeFileSync","os","readFileSync","os","writeFileSync","renameSync","existsSync","spawn","execFileSync","sleep","randomInt","pick","randomInt","label","readFileSync","writeFileSync","writeFileSync","readFileSync","tunnelIdFromCname","listZones","join","lines","existsSync","openSync","readFileSync","readFileSync","openSync","existsSync","openSync","spawn","join","note","fqdnFor","join","openSync","spawn","secret","require","pc"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/config/legacy-migrate.ts","../src/core/service.ts","../src/core/service-exec.ts","../src/core/ingress.ts","../src/core/tunnel-spec.ts","../src/core/service-systemd.ts","../src/core/service-launchd.ts","../src/core/service-windows.ts","../src/config/proxy.ts","../src/commands/login.ts","../src/config/token-url.ts","../src/config/resolve-identity.ts","../src/config/relay-key.ts","../src/commands/up.ts","../src/config/ensure-auth.ts","../src/connector/binary.ts","../src/core/up-runner.ts","../src/connector/process.ts","../src/connector/registry.ts","../src/cloudflare/tunnels.ts","../src/connector/health.ts","../src/core/orchestrator-create.ts","../src/core/slug.ts","../src/core/unmanaged-scan-cache.ts","../src/core/orchestrator-manage.ts","../src/core/resolve-domain.ts","../src/core/transport-protocol.ts","../src/commands/ls.ts","../src/commands/delete.ts","../src/commands/logs.ts","../src/commands/relay.ts","../src/core/api-proxy-server.ts"],"sourcesContent":["import { Command } from \"commander\";\nimport { createRequire } from \"node:module\";\nimport pc from \"picocolors\";\nimport { reportError } from \"./ui/errors.js\";\nimport { migrateLegacyProfiles } from \"./config/legacy-migrate.js\";\nimport { configureProxy } from \"./config/proxy.js\";\n\nimport { registerLogin } from \"./commands/login.js\";\nimport { registerUp } from \"./commands/up.js\";\nimport { registerLs } from \"./commands/ls.js\";\nimport { registerDelete } from \"./commands/delete.js\";\nimport { registerLogs } from \"./commands/logs.js\";\nimport { registerRelay } from \"./commands/relay.js\";\n\nconst require = createRequire(import.meta.url);\nconst pkg = require(\"../package.json\") as { version: string };\n\nfunction buildProgram(): Command {\n const program = new Command();\n program\n .name(\"cloudtunnel\")\n .description(\"Expose local ports at HTTPS subdomains on your own Cloudflare domains.\")\n .version(pkg.version, \"-v, --version\")\n .showHelpAfterError();\n\n program.addHelpText(\n \"before\",\n [\n pc.bold(\"Quickstart:\"),\n ` ${pc.cyan(\"cloudtunnel login\")} once — paste a token (or set CLOUDFLARE_API_TOKEN)`,\n ` ${pc.cyan(\"cloudtunnel 8080\")} your local :8080 goes live at an HTTPS URL`,\n ` ${pc.cyan(\"cloudtunnel api:8080\")} api.<domain> → localhost:8080`,\n ` ${pc.cyan(\"cloudtunnel ls\")} list tunnels ${pc.dim(\"·\")} ${pc.cyan(\"cloudtunnel delete <#>\")} remove one`,\n \"\",\n ].join(\"\\n\"),\n );\n\n for (const register of [registerLogin, registerUp, registerLs, registerDelete, registerLogs, registerRelay]) {\n register(program);\n }\n return program;\n}\n\n/** Migrate legacy profiles only in a real terminal (systemd changes need an\n * interactive sudo) and not for help/version, so scripts/CI stay quiet. */\nfunction shouldMigrate(argv: string[]): boolean {\n if (!process.stdin.isTTY || !process.stdout.isTTY) return false;\n const rest = argv.slice(2);\n const infoFlag = new Set([\"-h\", \"--help\", \"-v\", \"--version\", \"help\"]);\n return !rest.some((a) => infoFlag.has(a));\n}\n\nasync function main(): Promise<void> {\n // Route fetch through a proxy if one is configured (Node's fetch ignores\n // proxies by default → UND_ERR_CONNECT_TIMEOUT behind a corporate proxy).\n configureProxy();\n // One-time, best-effort upgrade from the old profile model.\n if (shouldMigrate(process.argv)) await migrateLegacyProfiles();\n const program = buildProgram();\n try {\n await program.parseAsync(process.argv);\n } catch (err) {\n process.exitCode = reportError(err);\n }\n}\n\nvoid main();\n","import { existsSync, readFileSync, renameSync, writeFileSync } from \"node:fs\";\nimport { profilesFile } from \"./paths.js\";\nimport { confirm, say } from \"../ui/output.js\";\nimport { installServiceForSpec, legacyUnitExists, removeLegacyUnit } from \"../core/service.js\";\nimport type { TransportProtocol } from \"../core/transport-protocol.js\";\n\n// Shape of the retired profiles file (self-contained; no dependency on the\n// deleted profile store).\ninterface LegacyService { name: string; port: number; proto: \"http\" | \"https\"; host?: string; domain?: string }\ninterface LegacyProfile { services?: LegacyService[]; domain?: string; protocol?: TransportProtocol }\n\nconst skipMarker = `${profilesFile}.migrate-skip`;\n\n/**\n * One-time, best-effort migration from the old profile model. If a legacy profiles\n * file exists, convert any profile that was registered as a systemd service\n * (`cloudtunnel-<profile>.service`) into the new per-subdomain units. Asks for\n * consent first (it needs sudo), and on decline/failure drops a skip-marker so it\n * never re-prompts on later commands. Caller gates this to an interactive TTY.\n */\nexport async function migrateLegacyProfiles(): Promise<void> {\n if (!existsSync(profilesFile) || existsSync(skipMarker)) return; // fast path\n\n let profiles: Record<string, LegacyProfile>;\n try {\n profiles = JSON.parse(readFileSync(profilesFile, \"utf8\")) as Record<string, LegacyProfile>;\n } catch {\n return; // unreadable → leave it alone\n }\n\n // Only boot-registered profiles need migrating; the rest are just stale saved defs.\n const legacy = Object.entries(profiles).filter(([name]) => legacyUnitExists(name));\n if (legacy.length === 0) {\n try { renameSync(profilesFile, `${profilesFile}.migrated`); } catch { /* ignore */ }\n return;\n }\n\n const ok = await confirm(`Found ${legacy.length} boot service(s) from an older cloudtunnel. Migrate them now? (needs sudo)`);\n if (!ok) {\n writeFileSync(skipMarker, \"\");\n say.dim(` Skipped. Delete ${skipMarker} to be asked again.`);\n return;\n }\n\n let migrated = 0;\n try {\n for (const [name, profile] of legacy) {\n for (const svc of profile.services ?? []) {\n const zone = svc.domain ?? profile.domain;\n if (!zone) continue; // can't resolve a hostname → skip this service\n installServiceForSpec({\n subdomain: svc.name, port: svc.port, host: svc.host,\n zone, proto: svc.proto, protocol: profile.protocol,\n });\n migrated++;\n }\n removeLegacyUnit(name);\n }\n renameSync(profilesFile, `${profilesFile}.migrated`);\n say.ok(`Migrated ${migrated} boot service(s). See them with: cloudtunnel ls`);\n } catch (err) {\n writeFileSync(skipMarker, \"\"); // stop auto-retrying on every command\n say.warn(`Migration incomplete: ${(err as Error).message}. Won't retry automatically (delete ${skipMarker} to retry).`);\n }\n}\n","import { join } from \"node:path\";\nimport { CliError } from \"../ui/errors.js\";\nimport { logDir } from \"../config/paths.js\";\nimport { describeService, serviceSlug, type ServiceDescriptor, type ServiceSpecParams, type ServiceState } from \"./service-exec.js\";\nimport * as systemd from \"./service-systemd.js\";\nimport * as launchd from \"./service-launchd.js\";\nimport * as windows from \"./service-windows.js\";\n\nexport type { ServiceState, ServiceSpecParams } from \"./service-exec.js\";\n\n/** Per-OS boot-service backend. */\ninterface Backend {\n label(fqdn: string): string;\n assertSupported(): void;\n install(d: ServiceDescriptor): void;\n uninstall(fqdn: string): void;\n state(fqdn: string): ServiceState;\n}\n\n/** The backend for the current OS, or null where boot services aren't supported. */\nfunction pick(): Backend | null {\n switch (process.platform) {\n case \"linux\": return systemd;\n case \"darwin\": return launchd;\n case \"win32\": return windows;\n default: return null;\n }\n}\n\nfunction required(): Backend {\n const b = pick();\n if (!b) {\n throw new CliError(`Boot services aren't supported on ${process.platform}.`, {\n hint: \"run `cloudtunnel up <spec> --detach` and use your OS's own autostart\",\n });\n }\n return b;\n}\n\n/** Throw if `--service` can't work here (unsupported OS, or systemd missing). */\nexport function assertServiceSupported(): void {\n required().assertSupported();\n}\n\n/** Backend-specific display name/id for a subdomain's service. */\nexport function serviceName(fqdn: string): string {\n return pick()?.label(fqdn) ?? `cloudtunnel-${fqdn}`;\n}\n\n/** Install + enable a boot service for one subdomain (runs now + at login/boot). */\nexport function installServiceForSpec(params: ServiceSpecParams): void {\n const b = required();\n b.assertSupported();\n b.install(describeService(params));\n}\n\n/** Remove a subdomain's boot service (best-effort; no-op on unsupported OS). */\nexport function uninstallService(fqdn: string): void {\n pick()?.uninstall(fqdn);\n}\n\n/** Current state of a subdomain's service (\"none\" on an unsupported OS). */\nexport function serviceState(fqdn: string): ServiceState {\n return pick()?.state(fqdn) ?? \"none\";\n}\n\n/** Platform command/path to inspect why a subdomain's boot service isn't up yet. */\nexport function serviceLogsHint(fqdn: string): string {\n switch (process.platform) {\n case \"linux\":\n return `journalctl -u ${serviceName(fqdn)} -n 50 --no-pager`;\n case \"darwin\":\n return `tail ${join(logDir, `${serviceSlug(fqdn)}.service.log`)}`;\n case \"win32\":\n return `schtasks /Query /TN \"${serviceName(fqdn)}\" /V /FO LIST`;\n default:\n return \"check your OS service logs\";\n }\n}\n\n// --- Legacy (Linux-only) migration from the old profile-based units ---\nexport function legacyUnitExists(profile: string): boolean {\n return process.platform === \"linux\" ? systemd.legacyUnitExists(profile) : false;\n}\nexport function removeLegacyUnit(profile: string): void {\n if (process.platform === \"linux\") systemd.removeLegacyUnit(profile);\n}\n","import { realpathSync } from \"node:fs\";\nimport os from \"node:os\";\nimport { join } from \"node:path\";\nimport { CliError } from \"../ui/errors.js\";\nimport { logDir } from \"../config/paths.js\";\nimport { formatTunnelSpec } from \"./tunnel-spec.js\";\nimport type { TransportProtocol } from \"./transport-protocol.js\";\n\nexport type ServiceState = \"active\" | \"enabled\" | \"disabled\" | \"none\";\n\n/** What `up --service` (and the migration) hand to a platform backend. */\nexport interface ServiceSpecParams {\n subdomain: string;\n port: number;\n host?: string;\n zone: string;\n proto: \"http\" | \"https\";\n protocol?: TransportProtocol;\n /** Which foreground command the boot unit re-runs. Default \"up\"; \"relay\" makes\n * the unit start the CF-API relay instead of a plain tunnel. */\n command?: \"up\" | \"relay\";\n}\n\n/** Normalized, OS-agnostic description of the boot service for one subdomain. */\nexport interface ServiceDescriptor {\n fqdn: string;\n slug: string; // fqdn reduced to [a-z0-9-], unique per domain\n argv: string[]; // cloudtunnel args, e.g. [\"up\",\"api:8080@localhost\",\"-d\",\"abc.com\",\"-f\",\"-y\"]\n nodePath: string; // absolute node binary\n scriptPath: string; // absolute cloudtunnel entry\n user: string;\n home: string;\n logFile: string;\n}\n\nexport const fqdnFor = (subdomain: string, zone: string): string =>\n subdomain === \"@\" ? zone : `${subdomain}.${zone}`;\n\n/** Stable, filesystem-safe id derived from the fqdn (shared by every backend). */\nexport const serviceSlug = (fqdn: string): string => fqdn.replace(/[^a-zA-Z0-9]+/g, \"-\");\n\n/** The cloudtunnel args a boot service re-runs: recreate this one subdomain in the\n * foreground, non-interactively. Round-trips through `parseTunnelSpec` on boot.\n * A relay unit re-runs `relay <sub>` instead (it picks a fresh proxy port itself\n * and reads the persisted secret from config — nothing sensitive in the unit). */\nexport function buildUpArgs(p: ServiceSpecParams): string[] {\n if (p.command === \"relay\") {\n return [\n \"relay\", p.subdomain, \"-d\", p.zone,\n ...(p.proto === \"https\" ? [\"--proto\", \"https\"] : []),\n \"-f\", \"-y\",\n ];\n }\n const spec = formatTunnelSpec({ subdomain: p.subdomain, port: p.port, host: p.host });\n return [\n \"up\", spec, \"-d\", p.zone,\n ...(p.proto === \"https\" ? [\"--proto\", \"https\"] : []),\n ...(p.protocol ? [\"--protocol\", p.protocol] : []),\n \"-f\", \"-y\",\n ];\n}\n\n/** Resolve the running cloudtunnel entry, for a stable service command. */\nfunction entryScript(): string {\n const p = process.argv[1];\n if (!p) throw new CliError(\"Cannot resolve the cloudtunnel executable path.\");\n return realpathSync(p);\n}\n\nexport function describeService(p: ServiceSpecParams): ServiceDescriptor {\n const fqdn = fqdnFor(p.subdomain, p.zone);\n const slug = serviceSlug(fqdn);\n return {\n fqdn,\n slug,\n argv: buildUpArgs(p),\n nodePath: process.execPath,\n scriptPath: entryScript(),\n user: os.userInfo().username,\n home: os.homedir(),\n logFile: join(logDir, `${slug}.service.log`),\n };\n}\n","import type { IngressRule } from \"../cloudflare/types.js\";\nimport { CliError } from \"../ui/errors.js\";\n\nconst HOSTNAME_RE = /^[a-zA-Z0-9.-]+$/; // hostname or IPv4\nconst IPV6_RE = /^[0-9a-fA-F:.]+$/; // IPv6 literal (incl. IPv4-mapped ::ffff:1.2.3.4)\n\n/**\n * Validate a forward-target host before it lands in the ingress service URL.\n * Rejects anything that could break out of `proto://host:port` — a scheme,\n * path, or whitespace — so `--source` can't inject extra ingress syntax.\n *\n * IPv6 is accepted bare (`::1`) or bracketed (`[::1]`) and stored bare. IPv6 is\n * detected by `::` or ≥2 colons, so a single-colon `10.0.0.2:8080` (an IPv4:port\n * mistake) still fails the hostname check instead of passing as a bogus literal.\n */\nexport function validateHost(host: string): string {\n let h = host.trim();\n const bracketed = h.startsWith(\"[\") && h.endsWith(\"]\");\n if (bracketed) h = h.slice(1, -1);\n const isV6 = bracketed || h.includes(\"::\") || (h.match(/:/g)?.length ?? 0) >= 2;\n const ok = h.length > 0 && (isV6 ? IPV6_RE.test(h) : HOSTNAME_RE.test(h));\n if (!ok) {\n throw new CliError(`Invalid host \"${host}\".`, {\n hint: \"use a hostname, IPv4, or IPv6 literal (e.g. 192.168.1.5 or ::1) — no port, scheme, or path\",\n });\n }\n return h;\n}\n\n/** Compose a `proto://host:port` service URL, bracketing an IPv6 literal. */\nexport function serviceUrl(proto: \"http\" | \"https\", host: string, port: number): string {\n const authority = host.includes(\":\") ? `[${host}]` : host;\n return `${proto}://${authority}:${port}`;\n}\n\n/**\n * Build the ingress config for a single-hostname tunnel. The mandatory\n * catch-all `http_status:404` rule must come last (Cloudflare rejects configs\n * without it). One-tunnel-per-subdomain keeps this a fixed two-rule list, so\n * the full-replace PUT is always safe (no merge with other hostnames).\n *\n * `host` defaults to `localhost`; pass another host/IP to forward to a different\n * machine this connector can reach (a LAN device, a container, another server).\n */\nexport function buildIngress(opts: {\n hostname: string;\n port: number;\n proto: \"http\" | \"https\";\n host?: string;\n}): IngressRule[] {\n return [\n { hostname: opts.hostname, service: serviceUrl(opts.proto, opts.host ?? \"localhost\", opts.port) },\n { service: \"http_status:404\" },\n ];\n}\n","import { CliError } from \"../ui/errors.js\";\nimport { validateHost } from \"./ingress.js\";\n\n/** One tunnel to bring up, parsed from a positional `up` argument. */\nexport interface TunnelSpec {\n subdomain?: string; // absent ⇒ random slug; \"@\" ⇒ root/apex domain\n port: number;\n host?: string; // forward target (absent ⇒ localhost)\n}\n\n/**\n * Parse a `[subdomain:]port[@host]` spec, e.g. `8080`, `api:8080`,\n * `api:8080@192.168.1.20`, `api:8080@localhost`, `api:8080@::1`. The local-service\n * protocol is NOT part of the spec — it comes from the global `--proto` flag.\n *\n * A leading `@` means the root/apex domain (kept as the subdomain), which is\n * distinct from the `@host` forward-target delimiter that follows the port.\n */\nexport function parseTunnelSpec(spec: string): TunnelSpec {\n const raw = spec.trim();\n const bad = (hint: string): CliError => new CliError(`Invalid spec \"${spec}\".`, { hint });\n if (!raw) throw bad(\"use [subdomain:]port[@host], e.g. api:8080 or api:8080@192.168.1.20\");\n\n let rest = raw;\n let subdomain: string | undefined;\n\n // Leading `@` = root/apex domain; consume it before looking for the host `@`.\n if (rest.startsWith(\"@\")) {\n subdomain = \"@\";\n rest = rest.slice(1);\n if (rest.startsWith(\":\")) rest = rest.slice(1);\n }\n\n // Forward host after `@` (may contain colons for an IPv6 literal).\n let host: string | undefined;\n const at = rest.indexOf(\"@\");\n if (at >= 0) {\n host = validateHost(rest.slice(at + 1));\n rest = rest.slice(0, at);\n }\n\n // `rest` is now `[subdomain:]port`.\n const parts = rest.split(\":\");\n let portStr: string;\n if (parts.length === 1) {\n portStr = parts[0]!;\n } else if (parts.length === 2) {\n if (subdomain === undefined) {\n if (!parts[0]) throw bad(\"subdomain label is empty\");\n subdomain = parts[0];\n } else if (parts[0]) {\n throw bad(\"unexpected label after '@' root marker\");\n }\n portStr = parts[1]!;\n } else {\n throw bad(\"too many ':' — spec is [subdomain:]port[@host] (protocol via --proto)\");\n }\n\n const port = Number(portStr);\n if (!Number.isInteger(port) || port < 1 || port > 65535) {\n throw bad(\"port must be a number 1–65535\");\n }\n // A DNS label (or \"@\" for the apex). Guards the Cloudflare API and, with\n // `--service`, keeps the subdomain a single unquoted token in the unit ExecStart.\n if (subdomain !== undefined && subdomain !== \"@\" && !/^[a-zA-Z0-9-]+$/.test(subdomain)) {\n throw bad(\"subdomain may contain only letters, digits, and hyphens\");\n }\n return { subdomain, port, ...(host ? { host } : {}) };\n}\n\n/**\n * Render a concrete spec back to its `subdomain:port[@host]` string — used to bake\n * a stable spec into a systemd unit's ExecStart so it round-trips through\n * `parseTunnelSpec` on boot.\n */\nexport function formatTunnelSpec(s: { subdomain: string; port: number; host?: string }): string {\n return `${s.subdomain}:${s.port}${s.host ? `@${s.host}` : \"\"}`;\n}\n","import { execFileSync } from \"node:child_process\";\nimport { existsSync, writeFileSync } from \"node:fs\";\nimport { tmpdir } from \"node:os\";\nimport { dirname, join } from \"node:path\";\nimport { CliError } from \"../ui/errors.js\";\nimport { serviceSlug, type ServiceDescriptor, type ServiceState } from \"./service-exec.js\";\n\nexport const label = (fqdn: string): string => `cloudtunnel-${serviceSlug(fqdn)}.service`;\nconst unitPath = (fqdn: string): string => `/etc/systemd/system/${label(fqdn)}`;\n\n/**\n * Build the systemd unit text (pure — unit-tested). ExecStart re-runs the\n * `cloudtunnel up …` args in the FOREGROUND so systemd supervises one connector;\n * `systemctl stop` → SIGTERM → `up` releases its tunnel and exits 0 (not restarted).\n * Absolute node + script and an explicit PATH are used because systemd starts with\n * a minimal environment.\n */\nexport function buildUnit(d: ServiceDescriptor): string {\n const nodeBin = dirname(d.nodePath);\n return [\n \"[Unit]\",\n `Description=cloudtunnel ${d.fqdn} (Cloudflare Tunnel)`,\n \"After=network-online.target\",\n \"Wants=network-online.target\",\n \"\",\n \"[Service]\",\n \"Type=simple\",\n `User=${d.user}`,\n `Environment=HOME=${d.home}`,\n `Environment=PATH=${nodeBin}:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin`,\n `ExecStart=${d.nodePath} ${d.scriptPath} ${d.argv.join(\" \")}`,\n \"Restart=on-failure\",\n \"RestartSec=5\",\n \"\",\n \"[Install]\",\n \"WantedBy=multi-user.target\",\n \"\",\n ].join(\"\\n\");\n}\n\n/** Run a privileged command, prefixing `sudo` unless already root. */\nfunction privileged(args: string[]): void {\n const isRoot = typeof process.getuid === \"function\" && process.getuid() === 0;\n const argv = isRoot ? args : [\"sudo\", ...args];\n execFileSync(argv[0]!, argv.slice(1), { stdio: \"inherit\" });\n}\n\n/** Read-only systemctl query; returns trimmed stdout (\"\" on any error). */\nfunction query(args: string[]): string {\n try {\n return execFileSync(\"systemctl\", args, { stdio: [\"ignore\", \"pipe\", \"ignore\"], encoding: \"utf8\" }).trim();\n } catch (err) {\n const out = (err as { stdout?: Buffer | string }).stdout;\n return out ? out.toString().trim() : \"\";\n }\n}\n\nexport function assertSupported(): void {\n try {\n execFileSync(\"systemctl\", [\"--version\"], { stdio: \"ignore\" });\n } catch {\n throw new CliError(\"systemd (systemctl) was not found on this host.\");\n }\n}\n\n/** Install + enable a boot unit (runs now + on boot). Needs sudo. */\nexport function install(d: ServiceDescriptor): void {\n assertSupported();\n const tmp = join(tmpdir(), label(d.fqdn));\n writeFileSync(tmp, buildUnit(d), { mode: 0o644 });\n privileged([\"install\", \"-m\", \"0644\", tmp, unitPath(d.fqdn)]);\n privileged([\"systemctl\", \"daemon-reload\"]);\n privileged([\"systemctl\", \"enable\", \"--now\", label(d.fqdn)]);\n}\n\n/** Stop, disable, and delete the unit. Needs sudo. Best-effort. */\nexport function uninstall(fqdn: string): void {\n try {\n privileged([\"systemctl\", \"disable\", \"--now\", label(fqdn)]);\n } catch {\n /* not enabled / already gone */\n }\n privileged([\"rm\", \"-f\", unitPath(fqdn)]);\n privileged([\"systemctl\", \"daemon-reload\"]);\n}\n\nexport function state(fqdn: string): ServiceState {\n const name = label(fqdn);\n if (query([\"is-active\", name]) === \"active\") return \"active\";\n const enabled = query([\"is-enabled\", name]);\n if (enabled === \"enabled\" || enabled === \"enabled-runtime\") return \"enabled\";\n if (enabled === \"disabled\" || enabled === \"static\") return \"disabled\";\n return \"none\";\n}\n\n/** Whether a legacy profile-named unit is installed (one-time migration only). */\nexport function legacyUnitExists(profile: string): boolean {\n return existsSync(`/etc/systemd/system/cloudtunnel-${profile}.service`);\n}\n\n/** Remove a legacy profile-named unit (migration only). Needs sudo. */\nexport function removeLegacyUnit(profile: string): void {\n const name = `cloudtunnel-${profile}.service`;\n try {\n privileged([\"systemctl\", \"disable\", \"--now\", name]);\n } catch {\n /* not enabled / already gone */\n }\n privileged([\"rm\", \"-f\", `/etc/systemd/system/${name}`]);\n privileged([\"systemctl\", \"daemon-reload\"]);\n}\n","import { execFileSync } from \"node:child_process\";\nimport { existsSync, mkdirSync, rmSync, writeFileSync } from \"node:fs\";\nimport { dirname, join } from \"node:path\";\nimport os from \"node:os\";\nimport { ensureDirs } from \"../config/paths.js\";\nimport { serviceSlug, type ServiceDescriptor, type ServiceState } from \"./service-exec.js\";\n\nexport const label = (fqdn: string): string => `com.cloudtunnel.${serviceSlug(fqdn)}`;\nconst agentsDir = (): string => join(os.homedir(), \"Library\", \"LaunchAgents\");\nconst plistPath = (fqdn: string): string => join(agentsDir(), `${label(fqdn)}.plist`);\n\nconst xml = (s: string): string =>\n s.replace(/&/g, \"&\").replace(/</g, \"<\").replace(/>/g, \">\");\n\n/**\n * Build the launchd LaunchAgent plist (pure — unit-tested). A user agent (no sudo)\n * that runs at login (`RunAtLoad`) and is restarted on exit (`KeepAlive`), i.e. the\n * macOS equivalent of enable-now + restart-on-failure. ProgramArguments re-run the\n * same `cloudtunnel up …` the connector needs.\n */\nexport function buildPlist(d: ServiceDescriptor): string {\n const args = [d.nodePath, d.scriptPath, ...d.argv].map((a) => ` <string>${xml(a)}</string>`).join(\"\\n\");\n const nodeBin = dirname(d.nodePath);\n const path = `${nodeBin}:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin`;\n return [\n '<?xml version=\"1.0\" encoding=\"UTF-8\"?>',\n '<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">',\n '<plist version=\"1.0\">',\n \"<dict>\",\n ` <key>Label</key><string>${xml(label(d.fqdn))}</string>`,\n \" <key>ProgramArguments</key>\",\n \" <array>\",\n args,\n \" </array>\",\n \" <key>RunAtLoad</key><true/>\",\n \" <key>KeepAlive</key><true/>\",\n \" <key>EnvironmentVariables</key>\",\n \" <dict>\",\n ` <key>PATH</key><string>${xml(path)}</string>`,\n ` <key>HOME</key><string>${xml(d.home)}</string>`,\n \" </dict>\",\n ` <key>StandardOutPath</key><string>${xml(d.logFile)}</string>`,\n ` <key>StandardErrorPath</key><string>${xml(d.logFile)}</string>`,\n \"</dict>\",\n \"</plist>\",\n \"\",\n ].join(\"\\n\");\n}\n\n/** Run a launchctl command, ignoring failures (returns \"\" on error). */\nfunction launchctl(args: string[]): string {\n try {\n return execFileSync(\"launchctl\", args, { stdio: [\"ignore\", \"pipe\", \"ignore\"], encoding: \"utf8\" });\n } catch (err) {\n const out = (err as { stdout?: Buffer | string }).stdout;\n return out ? out.toString() : \"\";\n }\n}\n\nexport function assertSupported(): void {\n /* launchctl ships with macOS; the darwin platform check is enough. */\n}\n\nexport function install(d: ServiceDescriptor): void {\n ensureDirs();\n mkdirSync(agentsDir(), { recursive: true });\n const plist = plistPath(d.fqdn);\n writeFileSync(plist, buildPlist(d), { mode: 0o644 });\n launchctl([\"unload\", \"-w\", plist]); // best-effort: reload cleanly if already loaded\n // Surface a load failure (e.g. run over SSH / no GUI session) instead of\n // reporting a false success — the plist is written but nothing started.\n execFileSync(\"launchctl\", [\"load\", \"-w\", plist], { stdio: \"inherit\" });\n}\n\nexport function uninstall(fqdn: string): void {\n const plist = plistPath(fqdn);\n launchctl([\"unload\", \"-w\", plist]);\n rmSync(plist, { force: true });\n}\n\nexport function state(fqdn: string): ServiceState {\n const info = launchctl([\"list\", label(fqdn)]);\n if (/\"PID\"\\s*=/.test(info)) return \"active\"; // loaded and has a running pid\n return existsSync(plistPath(fqdn)) ? \"enabled\" : \"none\";\n}\n","import { execFileSync } from \"node:child_process\";\nimport { writeFileSync } from \"node:fs\";\nimport { tmpdir } from \"node:os\";\nimport { join } from \"node:path\";\nimport { serviceSlug, type ServiceDescriptor, type ServiceState } from \"./service-exec.js\";\n\n/** Task Scheduler path: a task named by the fqdn slug under a `cloudtunnel` folder. */\nexport const label = (fqdn: string): string => `cloudtunnel\\\\${serviceSlug(fqdn)}`;\n\nconst xml = (s: string): string =>\n s.replace(/&/g, \"&\").replace(/</g, \"<\").replace(/>/g, \">\").replace(/\"/g, \""\");\n\n/**\n * Build a Task Scheduler definition (pure — unit-tested). A LeastPrivilege logon\n * task (no admin) that starts at logon, restarts on failure, and runs the same\n * `cloudtunnel up …` the connector needs. Written as UTF-16 (schtasks /XML).\n */\nexport function buildTaskXml(d: ServiceDescriptor): string {\n const args = `\"${d.scriptPath}\" ${d.argv.join(\" \")}`;\n return [\n '<?xml version=\"1.0\" encoding=\"UTF-16\"?>',\n '<Task version=\"1.2\" xmlns=\"http://schemas.microsoft.com/windows/2004/02/mit/task\">',\n ` <RegistrationInfo><Description>cloudtunnel ${xml(d.fqdn)} (Cloudflare Tunnel)</Description></RegistrationInfo>`,\n ` <Triggers><LogonTrigger><Enabled>true</Enabled><UserId>${xml(d.user)}</UserId></LogonTrigger></Triggers>`,\n ` <Principals><Principal id=\"Author\"><UserId>${xml(d.user)}</UserId><LogonType>InteractiveToken</LogonType><RunLevel>LeastPrivilege</RunLevel></Principal></Principals>`,\n \" <Settings>\",\n \" <MultipleInstancesPolicy>IgnoreNew</MultipleInstancesPolicy>\",\n \" <DisallowStartIfOnBatteries>false</DisallowStartIfOnBatteries>\",\n \" <StopIfGoingOnBatteries>false</StopIfGoingOnBatteries>\",\n \" <StartWhenAvailable>true</StartWhenAvailable>\",\n \" <ExecutionTimeLimit>PT0S</ExecutionTimeLimit>\",\n \" <RestartOnFailure><Interval>PT1M</Interval><Count>3</Count></RestartOnFailure>\",\n \" <Enabled>true</Enabled>\",\n \" </Settings>\",\n ' <Actions Context=\"Author\">',\n ` <Exec><Command>${xml(d.nodePath)}</Command><Arguments>${xml(args)}</Arguments></Exec>`,\n \" </Actions>\",\n \"</Task>\",\n \"\",\n ].join(\"\\r\\n\");\n}\n\n/** Run schtasks, ignoring failures (returns \"\" on error). */\nfunction schtasks(args: string[]): string {\n try {\n return execFileSync(\"schtasks\", args, { stdio: [\"ignore\", \"pipe\", \"ignore\"], encoding: \"utf8\" });\n } catch (err) {\n const out = (err as { stdout?: Buffer | string }).stdout;\n return out ? out.toString() : \"\";\n }\n}\n\nexport function assertSupported(): void {\n /* schtasks ships with Windows; the win32 platform check is enough. */\n}\n\nexport function install(d: ServiceDescriptor): void {\n const file = join(tmpdir(), `${d.slug}.task.xml`);\n // schtasks /XML wants a UTF-16 file with a BOM.\n writeFileSync(file, \"\\uFEFF\" + buildTaskXml(d), { encoding: \"utf16le\" });\n execFileSync(\"schtasks\", [\"/Create\", \"/TN\", label(d.fqdn), \"/XML\", file, \"/F\"], { stdio: \"inherit\" });\n schtasks([\"/Run\", \"/TN\", label(d.fqdn)]); // start now\n}\n\nexport function uninstall(fqdn: string): void {\n schtasks([\"/Delete\", \"/TN\", label(fqdn), \"/F\"]);\n}\n\nexport function state(fqdn: string): ServiceState {\n const out = schtasks([\"/Query\", \"/TN\", label(fqdn), \"/FO\", \"LIST\"]);\n if (!out) return \"none\";\n if (/\\bRunning\\b/.test(out)) return \"active\";\n if (/\\bDisabled\\b/.test(out)) return \"disabled\";\n if (/\\bReady\\b/.test(out)) return \"enabled\";\n return \"enabled\"; // task exists but status unrecognized (e.g. localized)\n}\n","import { execFileSync } from \"node:child_process\";\nimport { ProxyAgent, setGlobalDispatcher } from \"undici\";\nimport { say } from \"../ui/output.js\";\n\n/** A proxy URL from the standard CLI env vars (upper- and lower-case). Works on\n * every OS and is the reliable baseline. */\nexport function proxyFromEnv(): string | undefined {\n return (\n process.env.HTTPS_PROXY ?? process.env.https_proxy ??\n process.env.HTTP_PROXY ?? process.env.http_proxy ??\n process.env.ALL_PROXY ?? process.env.all_proxy\n );\n}\n\n/** Run a command, returning trimmed stdout (\"\" on any failure). */\nfunction run(cmd: string, args: string[]): string {\n try {\n return execFileSync(cmd, args, { encoding: \"utf8\", timeout: 1500 }).trim();\n } catch {\n return \"\";\n }\n}\n\n// --- Pure parsers (unit-tested; the OS-command wrappers below feed them) ---\n\n/** Parse `scutil --proxy` (macOS). Prefers HTTPS, then HTTP, when enabled. */\nexport function parseMacProxy(out: string): string | undefined {\n const val = (k: string): string | undefined => out.match(new RegExp(`\\\\b${k}\\\\s*:\\\\s*(\\\\S+)`))?.[1];\n for (const [en, host, port] of [[\"HTTPSEnable\", \"HTTPSProxy\", \"HTTPSPort\"], [\"HTTPEnable\", \"HTTPProxy\", \"HTTPPort\"]] as const) {\n if (val(en) === \"1\" && val(host) && val(port)) return `http://${val(host)}:${val(port)}`;\n }\n return undefined;\n}\n\n/** Parse `reg query … Internet Settings` (Windows) ProxyEnable + ProxyServer.\n * ProxyServer is either `host:port` or `http=h:p;https=h:p`. */\nexport function parseWindowsProxy(enableOut: string, serverOut: string): string | undefined {\n if (!/ProxyEnable\\s+REG_DWORD\\s+0x1/i.test(enableOut)) return undefined;\n const raw = serverOut.match(/ProxyServer\\s+REG_SZ\\s+(\\S+)/i)?.[1];\n if (!raw) return undefined;\n const scheme = raw.match(/https=([^;]+)/i)?.[1] ?? raw.match(/http=([^;]+)/i)?.[1] ?? (raw.includes(\"=\") ? undefined : raw);\n return scheme ? `http://${scheme}` : undefined;\n}\n\n// --- OS-specific system-proxy readers (best-effort; any failure ⇒ undefined) ---\n\nfunction proxyFromGnome(): string | undefined {\n const get = (schema: string, key: string): string =>\n run(\"gsettings\", [\"get\", schema, key]).replace(/^'|'$/g, \"\");\n if (get(\"org.gnome.system.proxy\", \"mode\") !== \"manual\") return undefined;\n for (const scheme of [\"https\", \"http\"]) {\n const host = get(`org.gnome.system.proxy.${scheme}`, \"host\");\n const port = Number(get(`org.gnome.system.proxy.${scheme}`, \"port\"));\n if (host && port) return `http://${host}:${port}`;\n }\n return undefined;\n}\n\nconst WIN_INET = \"HKCU\\\\Software\\\\Microsoft\\\\Windows\\\\CurrentVersion\\\\Internet Settings\";\n\n/**\n * The system proxy configured in the OS (what GUI apps like Postman use), so a\n * desktop proxy is honored without exporting env vars. Per-OS best-effort:\n * macOS `scutil`, Windows registry, Linux GNOME `gsettings`. Unsupported desktop\n * (KDE, headless) ⇒ undefined; set `HTTPS_PROXY` there instead.\n */\nexport function proxyFromSystem(): string | undefined {\n switch (process.platform) {\n case \"darwin\": return parseMacProxy(run(\"scutil\", [\"--proxy\"]));\n case \"win32\": return parseWindowsProxy(run(\"reg\", [\"query\", WIN_INET, \"/v\", \"ProxyEnable\"]), run(\"reg\", [\"query\", WIN_INET, \"/v\", \"ProxyServer\"]));\n case \"linux\": return proxyFromGnome();\n default: return undefined;\n }\n}\n\n/**\n * Route Node's global `fetch` through a proxy when one is configured. Node's\n * built-in fetch (undici) ignores proxies by default, so behind a corporate/\n * internal proxy every API call connects direct and times out\n * (`UND_ERR_CONNECT_TIMEOUT`) even though curl/Postman work. Sources, in order:\n * proxy env vars → OS system proxy (best-effort). No proxy found ⇒ unchanged.\n */\nexport function configureProxy(): void {\n const proxy = proxyFromEnv() ?? proxyFromSystem();\n if (!proxy) return;\n try {\n setGlobalDispatcher(new ProxyAgent(proxy));\n say.debug(`[proxy] routing fetch through ${proxy}`);\n } catch {\n /* keep the default dispatcher — a direct attempt with a clear error beats a crash */\n }\n}\n","import type { Command } from \"commander\";\nimport * as clack from \"@clack/prompts\";\nimport { CliError } from \"../ui/errors.js\";\nimport { redactToken, say, selectOne } from \"../ui/output.js\";\nimport { configFile } from \"../config/paths.js\";\nimport { loadConfig, saveConfig, type CloudtunnelConfig } from \"../config/store.js\";\nimport { REQUIRED_SCOPES, openBrowser, tokenCreateUrl } from \"../config/token-url.js\";\nimport { listAccounts, listZones } from \"../config/resolve-identity.js\";\nimport { getApiBase, isHttpUrl } from \"../config/api-base.js\";\nimport { decodeRelayKey } from \"../config/relay-key.js\";\n\ninterface LoginOptions {\n tokenStdin?: boolean;\n token?: string; // deprecated: leaks into shell history\n account?: string;\n zone?: string;\n status?: boolean;\n apiBase?: string; // point the CF API at a relay (blocked control plane)\n relaySecretStdin?: boolean; // read the relay shared secret from stdin\n relay?: string; // one-shot relay key (base+secret) from `cloudtunnel relay`\n}\n\n/** Read the whole stdin pipe (for `--token-stdin`). */\nasync function readStdin(): Promise<string> {\n const chunks: Buffer[] = [];\n for await (const chunk of process.stdin) chunks.push(chunk as Buffer);\n return Buffer.concat(chunks).toString(\"utf8\").trim();\n}\n\n/** Acquire the API token: env (silent) → stdin → deprecated flag → masked prompt.\n * Env tokens are NOT persisted (the env stays the source of truth). */\nasync function acquireToken(opts: LoginOptions): Promise<{ token: string; fromEnv: boolean }> {\n const envToken = process.env.CLOUDFLARE_API_TOKEN;\n if (envToken) {\n say.dim(\"Using token from CLOUDFLARE_API_TOKEN.\");\n return { token: envToken, fromEnv: true };\n }\n if (opts.tokenStdin) return { token: await readStdin(), fromEnv: false };\n if (opts.token) {\n say.warn(\"--token puts the token in your shell history — prefer --token-stdin or the prompt. Rotate it if this is a shared host.\");\n return { token: opts.token, fromEnv: false };\n }\n if (!process.stdin.isTTY) {\n throw new CliError(\"No token provided and no interactive terminal.\", {\n hint: \"pipe it: `printf %s $TOKEN | cloudtunnel login --token-stdin`\",\n });\n }\n clack.note(REQUIRED_SCOPES.map((s) => `• ${s}`).join(\"\\n\"), \"Create a token with these scopes\");\n openBrowser(tokenCreateUrl());\n say.dim(`(opened ${tokenCreateUrl()})`);\n const token = await clack.password({ message: \"Paste your Cloudflare API token\", mask: \"•\" });\n if (clack.isCancel(token) || !token) {\n clack.cancel(\"Cancelled.\");\n throw new CliError(\"Cancelled.\", { exitCode: 130 });\n }\n return { token, fromEnv: false };\n}\n\nasync function runLoginFlow(opts: LoginOptions = {}): Promise<void> {\n let relaySecretInput: string | undefined;\n // A relay key bundles base+secret into one flag → the simplest client setup\n // (`login --relay <key>`); no env exports, no double stdin.\n if (opts.relay) {\n const decoded = decodeRelayKey(opts.relay);\n opts.apiBase = decoded.base;\n relaySecretInput = decoded.secret;\n }\n if (opts.apiBase && !isHttpUrl(opts.apiBase)) {\n throw new CliError(`Invalid --api-base \"${opts.apiBase}\".`, {\n hint: \"must be an http(s) URL, e.g. https://cfapi.example.com/client/v4\",\n });\n }\n if (opts.tokenStdin && opts.relaySecretStdin) {\n throw new CliError(\"Can't read both the token and the relay secret from stdin.\", {\n hint: \"run login twice, or set one via env (CLOUDFLARE_API_TOKEN / CLOUDTUNNEL_RELAY_SECRET)\",\n });\n }\n // Apply relay overrides to THIS process BEFORE the verify calls: on a blocked\n // client the login-time listAccounts/listZones must also ride the relay, and\n // config isn't saved yet — so seed the env the transport reads from.\n if (opts.apiBase) process.env.CLOUDTUNNEL_API_BASE = opts.apiBase;\n if (relaySecretInput) {\n process.env.CLOUDTUNNEL_RELAY_SECRET = relaySecretInput;\n } else if (opts.relaySecretStdin) {\n relaySecretInput = await readStdin();\n if (relaySecretInput) process.env.CLOUDTUNNEL_RELAY_SECRET = relaySecretInput;\n }\n\n if (process.stdout.isTTY) clack.intro(\"cloudtunnel · connect to Cloudflare\");\n const { token, fromEnv } = await acquireToken(opts);\n\n const spin = clack.spinner();\n spin.start(\"Verifying token…\");\n const [accounts, zones] = await Promise.all([listAccounts(token), listZones(token)]).catch((err: unknown) => {\n spin.stop(\"Token check failed\");\n throw err;\n });\n spin.stop(\"Token verified\");\n\n if (accounts.length === 0) throw new CliError(\"Token can't see any Cloudflare account.\");\n let account = opts.account ? accounts.find((a) => a.id === opts.account) : undefined;\n if (opts.account && !account) throw new CliError(`Account ${opts.account} not visible to this token.`);\n if (!account) {\n account = accounts.length === 1 || !process.stdin.isTTY\n ? accounts[0]!\n : await selectOne(\"Select an account\", accounts, (a) => `${a.name} (${a.id})`);\n }\n\n let defaultZone = opts.zone;\n if (!defaultZone) {\n if (zones.length === 1) defaultZone = zones[0]!.name;\n else if (zones.length > 1 && process.stdin.isTTY) {\n defaultZone = (await selectOne(\"Select a default domain\", zones, (z) => z.name)).name;\n }\n }\n\n // MERGE-save: preserve apiBase/relaySecret/defaultZone across re-logins (a plain\n // replace here would wipe a previously-configured relay base or secret).\n saveConfig(buildMergedConfig(loadConfig(), {\n token, fromEnv, accountId: account.id, defaultZone,\n apiBase: opts.apiBase, relaySecret: relaySecretInput,\n }));\n const summary = `Logged in as ${account.name}${defaultZone ? ` · default domain ${defaultZone}` : \"\"}`;\n if (process.stdout.isTTY) clack.outro(summary);\n else say.ok(summary);\n if (!defaultZone) say.dim(\"No default domain set — pass -d <domain> on `up`, or re-run `login --zone <domain>`.\");\n}\n\n/** Merge fresh login results onto the existing config so a re-login never wipes a\n * previously-set relay base/secret or saved default domain. An env-sourced token\n * is not persisted (env stays the source of truth). Pure → unit-tested. */\nexport function buildMergedConfig(\n prev: CloudtunnelConfig,\n args: { token: string; fromEnv: boolean; accountId: string; defaultZone?: string; apiBase?: string; relaySecret?: string },\n): CloudtunnelConfig {\n return {\n ...prev,\n apiToken: args.fromEnv ? undefined : args.token,\n accountId: args.accountId,\n defaultZone: args.defaultZone ?? prev.defaultZone,\n apiBase: args.apiBase ?? prev.apiBase,\n relaySecret: args.relaySecret ?? prev.relaySecret,\n };\n}\n\nfunction showStatus(): void {\n const config = loadConfig();\n const token = process.env.CLOUDFLARE_API_TOKEN ?? config.apiToken;\n if (!token) {\n say.warn(\"Not logged in. Run `cloudtunnel login`.\");\n return;\n }\n const source = process.env.CLOUDFLARE_API_TOKEN ? \"env\" : \"config\";\n say.info(`Token: ${redactToken(token)} (${source})`);\n say.info(`Account: ${config.accountId ?? \"(from env / unresolved)\"}`);\n say.info(`Domain: ${config.defaultZone ?? \"(none)\"}`);\n const baseSrc = process.env.CLOUDTUNNEL_API_BASE ? \"env\" : config.apiBase ? \"config\" : \"default\";\n say.info(`Base: ${getApiBase()} (${baseSrc})`);\n const secretSrc = process.env.CLOUDTUNNEL_RELAY_SECRET ? \"env\" : config.relaySecret ? \"config\" : undefined;\n say.info(`Relay secret: ${secretSrc ? `set (${secretSrc})` : \"(none)\"}`);\n say.dim(`Config: ${configFile}`);\n}\n\nexport function registerLogin(program: Command): void {\n program\n .command(\"login\")\n .description(\"Authenticate with Cloudflare (paste a token once; account + domain auto-resolved)\")\n .option(\"--token-stdin\", \"read the API token from stdin (scriptable, avoids shell history)\")\n .option(\"--token <token>\", \"[discouraged] token as an argument (leaks into shell history)\")\n .option(\"--account <id>\", \"Cloudflare account id (auto-resolved when you have one account)\")\n .option(\"--zone <domain>\", \"default domain for new tunnels (auto-resolved when you have one)\")\n .option(\"--relay <key>\", \"one-shot relay setup: paste the key printed by `cloudtunnel relay` (sets base + secret)\")\n .option(\"--api-base <url>\", \"route the CF API through a relay (when api.cloudflare.com is blocked)\")\n .option(\"--relay-secret-stdin\", \"read the relay shared secret from stdin (pairs with --api-base)\")\n .option(\"--status\", \"show current identity (redacted) and exit\")\n .action(async (opts: LoginOptions) => {\n if (opts.status) return showStatus();\n await runLoginFlow(opts);\n });\n}\n\nexport { runLoginFlow };\n","import { spawn } from \"node:child_process\";\n\n/** The exact scopes cloudtunnel needs. Printed so the user selects them when\n * minting a token — least-privilege, account-wide only where required. */\nexport const REQUIRED_SCOPES = [\n \"Account · Cloudflare Tunnel · Edit\",\n \"Account · Account Settings · Read\",\n \"Zone · DNS · Edit\",\n \"Zone · Zone · Read\",\n] as const;\n\n/** Cloudflare \"Create Custom Token\" page. `name` is pre-filled best-effort;\n * the user still selects the scopes above (dashboard pre-fill params are not a\n * versioned API, so we rely on the printed scope list, not URL params). */\nexport function tokenCreateUrl(): string {\n return \"https://dash.cloudflare.com/profile/api-tokens?name=cloudtunnel\";\n}\n\n/** Best-effort open a URL in the default browser. Never throws — if no opener\n * exists (headless/CI), the caller still prints the URL. */\nexport function openBrowser(url: string): void {\n const cmd =\n process.platform === \"darwin\" ? \"open\"\n : process.platform === \"win32\" ? \"cmd\"\n : \"xdg-open\";\n const args = process.platform === \"win32\" ? [\"/c\", \"start\", \"\", url] : [url];\n try {\n const child = spawn(cmd, args, { stdio: \"ignore\", detached: true });\n child.on(\"error\", () => {}); // swallow: opener may not exist\n child.unref();\n } catch {\n // ignore — printing the URL is the fallback\n }\n}\n","import { CliError, fetchErrorReason } from \"../ui/errors.js\";\nimport { say } from \"../ui/output.js\";\nimport { REQUIRED_SCOPES, tokenCreateUrl } from \"./token-url.js\";\nimport { getApiBase, DEFAULT_API_BASE } from \"./api-base.js\";\nimport { RELAY_SECRET_HEADER, getRelaySecret } from \"./relay-secret.js\";\n\nexport interface CfAccount { id: string; name: string }\nexport interface CfZone { id: string; name: string; account?: { id: string } }\n\n/**\n * Raw Cloudflare GET used only for login-time validation (the typed SDK client\n * is wired in Phase 3). Errors are sanitized: the token never appears in any\n * thrown message. A 403 is mapped to a missing-scope hint.\n */\nasync function cfGet<T>(path: string, token: string): Promise<T[]> {\n const base = getApiBase();\n const viaRelay = base !== DEFAULT_API_BASE;\n const secret = getRelaySecret();\n const headers: Record<string, string> = {\n Authorization: `Bearer ${token}`,\n \"Content-Type\": \"application/json\",\n };\n if (secret && viaRelay) headers[RELAY_SECRET_HEADER] = secret;\n let res: Response;\n try {\n res = await fetch(`${base}${path}`, { headers });\n } catch (err) {\n const reason = fetchErrorReason(err);\n say.debug(`[cf] GET ${base}${path} -> network error: ${reason}${viaRelay ? \" (relay)\" : \"\"}`);\n throw new CliError(`Could not reach the Cloudflare API (${reason})${viaRelay ? ` via relay ${base}` : \"\"}.`, {\n hint: viaRelay ? \"is the relay tunnel up and the base URL correct? run with CLOUDTUNNEL_DEBUG=1\" : undefined,\n });\n }\n say.debug(`[cf] GET ${base}${path} -> ${res.status}${viaRelay ? \" (relay)\" : \"\"}`);\n const body = (await res.json().catch(() => ({}))) as { success?: boolean; result?: T[]; error?: string; errors?: unknown };\n // A relay rejects with its own `{error}` shape — surface it so a relay/secret\n // failure isn't misread as an invalid-token or missing-scope problem.\n if (viaRelay && !res.ok && body.error && !body.errors) {\n throw new CliError(`Relay rejected the request (${res.status}): ${body.error}.`, {\n hint: res.status === 403 ? \"does CLOUDTUNNEL_RELAY_SECRET match the relay's secret?\" : `relay base: ${base}`,\n });\n }\n if (res.status === 401) {\n throw new CliError(\"Cloudflare rejected the token (invalid or expired).\", {\n hint: `mint a new token: ${tokenCreateUrl()}`,\n });\n }\n if (res.status === 403) {\n throw new CliError(`Token is missing a required scope for ${path}.`, {\n hint: `token needs: ${REQUIRED_SCOPES.join(\", \")}`,\n });\n }\n if (!res.ok || !body.success) {\n throw new CliError(`Cloudflare API error (${res.status}) on ${path}.`);\n }\n return body.result ?? [];\n}\n\nexport function listAccounts(token: string): Promise<CfAccount[]> {\n return cfGet<CfAccount>(\"/accounts?per_page=50\", token);\n}\n\nexport function listZones(token: string): Promise<CfZone[]> {\n return cfGet<CfZone>(\"/zones?per_page=50\", token);\n}\n","import { CliError } from \"../ui/errors.js\";\nimport { isHttpUrl } from \"./api-base.js\";\n\n/** Marks a cloudtunnel relay key so a mistyped/foreign blob fails clearly. */\nconst PREFIX = \"ctr_\";\n\n/**\n * Encode a relay's base URL + shared secret into one short, copy-pasteable key,\n * so a client is pointed at a relay with a single `login --relay <key>` — no env\n * exports, no juggling two stdin pipes. (The secret is only obfuscated, not\n * encrypted — treat the key like the secret itself.)\n */\nexport function encodeRelayKey(base: string, secret: string): string {\n return PREFIX + Buffer.from(JSON.stringify({ b: base, s: secret })).toString(\"base64url\");\n}\n\n/** Decode a relay key back to `{ base, secret }`; throws actionably if malformed. */\nexport function decodeRelayKey(key: string): { base: string; secret: string } {\n const invalid = (): never => {\n throw new CliError(\"Invalid relay key.\", {\n hint: \"copy the full key printed by `cloudtunnel relay` on the relay host\",\n });\n };\n if (!key.startsWith(PREFIX)) return invalid();\n let obj: { b?: unknown; s?: unknown };\n try {\n obj = JSON.parse(Buffer.from(key.slice(PREFIX.length), \"base64url\").toString(\"utf8\"));\n } catch {\n return invalid();\n }\n if (typeof obj.b !== \"string\" || !isHttpUrl(obj.b) || typeof obj.s !== \"string\" || !obj.s) return invalid();\n return { base: obj.b, secret: obj.s };\n}\n","import type { Command } from \"commander\";\nimport * as clack from \"@clack/prompts\";\nimport { CliError } from \"../ui/errors.js\";\nimport { say, printTable } from \"../ui/output.js\";\nimport { ensureAuth } from \"../config/ensure-auth.js\";\nimport { resolveCf, type Cf } from \"../cloudflare/client.js\";\nimport { ensureCloudflared } from \"../connector/binary.js\";\nimport type { CreateOptions } from \"../core/orchestrator-create.js\";\nimport { startTunnels } from \"../core/up-runner.js\";\nimport { resolveDomain } from \"../core/resolve-domain.js\";\nimport { listAll } from \"../core/orchestrator-manage.js\";\nimport { getEntry } from \"../connector/registry.js\";\nimport { parseTunnelSpec, type TunnelSpec } from \"../core/tunnel-spec.js\";\nimport { parseTransportProtocol, type TransportProtocol } from \"../core/transport-protocol.js\";\nimport { randomSlug } from \"../core/slug.js\";\nimport { assertServiceSupported, installServiceForSpec, serviceLogsHint } from \"../core/service.js\";\n\ninterface UpOptions {\n domain?: string;\n proto: \"http\" | \"https\";\n protocol?: string; // edge transport: auto | http2 | quic\n detach?: boolean;\n service?: boolean; // register each subdomain as a systemd boot service\n force?: boolean;\n yes?: boolean;\n}\n\nfunction promptOrExit<T>(value: T | symbol): T {\n if (clack.isCancel(value)) {\n clack.cancel(\"Cancelled.\");\n process.exit(130);\n }\n return value as T;\n}\n\n/** Interactive port prompt (0-arg wizard). */\nasync function promptPort(): Promise<number> {\n const input = promptOrExit(\n await clack.text({\n message: \"Port to expose\",\n placeholder: \"e.g. 3000\",\n validate: (v) => {\n const n = Number(v);\n if (!Number.isInteger(n) || n < 1 || n > 65535) return \"Enter a port 1–65535\";\n return undefined;\n },\n }),\n );\n return Number(input);\n}\n\n/** The subdomain for a spec: explicit in the spec → used as-is; otherwise prompt\n * (TTY, blank = random) or random (non-TTY / `-y`). Returns undefined for random. */\nasync function resolveSpecSubdomain(spec: TunnelSpec, opts: UpOptions): Promise<string | undefined> {\n if (spec.subdomain !== undefined) return spec.subdomain;\n if (opts.yes || !process.stdin.isTTY) return undefined; // random\n const input = promptOrExit(\n await clack.text({ message: `Subdomain for :${spec.port}`, placeholder: \"blank = random · @ = root domain\" }),\n );\n return (input as string).trim() || undefined; // blank → random\n}\n\nasync function runUp(specArgs: string[], opts: UpOptions): Promise<void> {\n const protocol: TransportProtocol | undefined = opts.protocol ? parseTransportProtocol(opts.protocol) : undefined;\n // Parse specs up front (fail fast on a typo before touching the network). 0 args\n // → wizard, which needs a TTY.\n const parsed: TunnelSpec[] | null = specArgs.length ? specArgs.map(parseTunnelSpec) : null;\n if (parsed === null && !process.stdin.isTTY) {\n throw new CliError(\"No tunnel spec given.\", { hint: \"e.g. cloudtunnel api:8080\" });\n }\n\n const creds = await ensureAuth();\n const cf = resolveCf();\n const bin = await ensureCloudflared();\n\n if (process.stdout.isTTY) clack.intro(\"cloudtunnel\");\n\n const specs: TunnelSpec[] = parsed ?? [{ port: await promptPort() }];\n const domain = await resolveDomain(cf, opts, creds);\n\n // Build create-opts per spec. `--service` needs a concrete subdomain baked in\n // (never random-per-boot), so materialise a random one now when unnamed.\n const items: CreateOptions[] = [];\n for (const spec of specs) {\n let name = await resolveSpecSubdomain(spec, opts);\n if (opts.service && name === undefined) name = randomSlug();\n items.push({\n port: spec.port, proto: opts.proto, name, zone: domain, host: spec.host,\n defaultZone: creds.defaultZone, force: opts.force, yes: opts.yes,\n });\n }\n\n if (opts.service) {\n await registerServices(cf, items, domain, opts.proto, protocol);\n return;\n }\n\n await startTunnels(cf, bin, items, { detach: opts.detach, protocol });\n}\n\n/** How long `--service` waits for the boot services to bring their connectors up\n * before showing the `ls` view (registry \"running\" lands within a few seconds). */\nconst SERVICE_UP_TIMEOUT_MS = 20_000;\nconst delay = (ms: number): Promise<void> => new Promise((resolve) => setTimeout(resolve, ms));\n\n/**\n * Install + start a boot service per subdomain (systemd `enable --now` · launchd\n * `RunAtLoad` · Task Scheduler `/Run` all start it now and on boot), then WAIT for\n * the services to bring their connectors up and show the `ls` view. The services\n * own the connector; the CLI just watches the registry they write — so `ls`/`ps`\n * shows them right after this returns instead of after an invisible delay.\n * `--detach` is a no-op here — the service already backgrounds.\n */\nasync function registerServices(\n cf: Cf, items: CreateOptions[], domain: string,\n proto: \"http\" | \"https\", protocol?: TransportProtocol,\n): Promise<void> {\n assertServiceSupported();\n if (!protocol) {\n say.warn(\"No edge protocol set — cloudflared will pick QUIC, which some networks drop.\");\n say.dim(\" → add --protocol http2 for UDP-hostile networks\");\n }\n const fqdns: string[] = [];\n for (const item of items) {\n const subdomain = item.name!; // concrete (baked above)\n const fqdn = subdomain === \"@\" ? domain : `${subdomain}.${domain}`;\n installServiceForSpec({ subdomain, port: item.port, host: item.host, zone: domain, proto, protocol });\n fqdns.push(fqdn);\n }\n say.ok(`Registered ${fqdns.length} boot service(s) — waiting for them to come up…`);\n\n const notUp = await waitServicesUp(fqdns, SERVICE_UP_TIMEOUT_MS);\n\n const rows = await listAll(cf);\n if (rows.length) {\n printTable(\n [\"#\", \"URL\", \"TARGET\", \"PROTOCOL\", \"STATE\", \"SERVICE\", \"PID\"],\n rows.map((r) => [r.num, r.url, r.target, r.protocol, r.state, r.service, r.pid]),\n );\n }\n // A service that never reports up either failed to start or resolved a\n // different config dir than this shell — point at its logs so it's not silent.\n if (notUp.length) {\n say.warn(`${notUp.length} service(s) didn't report up within ${SERVICE_UP_TIMEOUT_MS / 1000}s:`);\n for (const fqdn of notUp) say.dim(` ${fqdn} → ${serviceLogsHint(fqdn)}`);\n }\n say.dim(\" → manage: cloudtunnel ls · remove: cloudtunnel delete <#>\");\n}\n\n/** Poll the registry until every fqdn's service has a live connector (state\n * \"running\" + a pid), or the timeout elapses. Returns the fqdns still not up.\n * Reads the registry the boot service writes; a pid means `ls` will show \"up\". */\nasync function waitServicesUp(fqdns: string[], timeoutMs: number): Promise<string[]> {\n const deadline = Date.now() + timeoutMs;\n const pending = new Set(fqdns);\n while (pending.size > 0) {\n for (const fqdn of [...pending]) {\n const entry = getEntry(fqdn);\n if (entry?.state === \"running\" && entry.pid) pending.delete(fqdn);\n }\n if (pending.size === 0 || Date.now() >= deadline) break;\n await delay(500);\n }\n return [...pending];\n}\n\nexport function registerUp(program: Command): void {\n program\n .command(\"up\", { isDefault: true })\n .argument(\"[specs...]\", \"tunnels to start: [subdomain:]port[@host] (e.g. api:8080 web:8081@localhost)\")\n .description(\"Start one or more tunnels (also: `cloudtunnel 8080`)\")\n .option(\"-d, --domain <domain>\", \"domain for the subdomains (prompted from a list if unset)\")\n .option(\"--proto <proto>\", \"local service protocol: http | https\", \"http\")\n .option(\"--protocol <proto>\", \"cloudflared edge transport: auto | http2 | quic (http2 for UDP-hostile networks)\")\n .option(\"--detach\", \"run the connectors in the background\")\n .option(\"--service\", \"register each subdomain as a boot service (Linux systemd · macOS launchd · Windows Task Scheduler)\")\n .option(\"-f, --force\", \"replace a non-tunnel DNS record occupying the hostname\")\n .option(\"-y, --yes\", \"don't prompt; don't ask before replacing an existing record\")\n .action((specs: string[], opts: UpOptions) => runUp(specs, opts));\n}\n","import { CliError } from \"../ui/errors.js\";\nimport { say } from \"../ui/output.js\";\nimport { getCredentials, type Credentials } from \"./store.js\";\nimport { runLoginFlow } from \"../commands/login.js\";\n\n/**\n * Single auth entry point for every command. Returns credentials if present;\n * on a fresh machine with a TTY it runs onboarding inline and continues, so\n * `cloudtunnel 3000` on a new box just works. Non-TTY (CI) → actionable error.\n */\nexport async function ensureAuth(): Promise<Credentials> {\n try {\n return getCredentials();\n } catch (err) {\n if (err instanceof CliError && process.stdin.isTTY) {\n say.info(\"Welcome to cloudtunnel — let's get you connected to Cloudflare first.\");\n await runLoginFlow();\n return getCredentials();\n }\n throw err;\n }\n}\n","import { execFileSync } from \"node:child_process\";\nimport { createHash } from \"node:crypto\";\nimport { chmodSync, existsSync, readFileSync, writeFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { gunzipSync } from \"node:zlib\";\nimport { CliError } from \"../ui/errors.js\";\nimport { say } from \"../ui/output.js\";\nimport { binDir, ensureDirs } from \"../config/paths.js\";\n\n// Pinned release for reproducible, checksum-verified auto-install. cloudflared\n// ships no sha256 manifest, so these digests are computed by downloading each\n// asset once at pin time (trust-on-pin). Bump the version and RE-HASH every\n// asset together — a stale digest fails closed and disables auto-install.\nconst PINNED_VERSION = \"2026.7.3\";\nconst RELEASE_BASE = `https://github.com/cloudflare/cloudflared/releases/download/${PINNED_VERSION}`;\n\ninterface Asset { file: string; archive: boolean; sha256: string }\n\n// sha256 is the digest of the DOWNLOADED asset (the .tgz for darwin), verified\n// before extraction. Empty string ⇒ fail closed (never run an unverified binary).\nconst ASSETS: Record<string, Asset | undefined> = {\n \"linux-x64\": { file: \"cloudflared-linux-amd64\", archive: false, sha256: \"9d71c677db00134c1bd4144b7783486b654ad281b1ea62b4972098d19f770f17\" },\n \"linux-arm64\": { file: \"cloudflared-linux-arm64\", archive: false, sha256: \"65259e652a7bea08bf5df603233ab22b8bf3116af8df9f9206209af6a1b955c0\" },\n \"linux-arm\": { file: \"cloudflared-linux-arm\", archive: false, sha256: \"6dadd979b8833760e9f6d840a6239a8c08c8bcf73b4231ec537f483873f37c73\" }, // armv7 (Raspberry Pi)\n \"darwin-x64\": { file: \"cloudflared-darwin-amd64.tgz\", archive: true, sha256: \"70d1c8684fa6d14b5843787ec8d1ea8e18b23650e424f4ea43d849a506487c3b\" },\n \"darwin-arm64\": { file: \"cloudflared-darwin-arm64.tgz\", archive: true, sha256: \"90c5a4f914d705fd70c135dba6d80b1791d254b08d6d4136301941f88330dd09\" },\n \"win32-x64\": { file: \"cloudflared-windows-amd64.exe\", archive: false, sha256: \"8635da433b6df8194746e88ed9d2589566c20e38bfc2a80e431a348b7c765841\" },\n};\n\n/**\n * Map the running platform to an ASSETS key. Windows-on-ARM has no native\n * cloudflared build, so it reuses the amd64 exe under x64 emulation.\n * Exported for tests.\n */\nexport function resolveAssetKey(platform: string, arch: string): string {\n const key = `${platform}-${arch}`;\n return key === \"win32-arm64\" ? \"win32-x64\" : key;\n}\n\nfunction binaryWorks(bin: string): boolean {\n try {\n execFileSync(bin, [\"--version\"], { stdio: \"ignore\" });\n return true;\n } catch {\n return false;\n }\n}\n\nfunction cachedPath(): string {\n return join(binDir, process.platform === \"win32\" ? \"cloudflared.exe\" : \"cloudflared\");\n}\n\n/** True on Alpine/musl, where cloudflared has no prebuilt binary. */\nfunction isMusl(): boolean {\n try {\n return process.platform === \"linux\" && readFileSync(\"/usr/bin/ldd\", \"utf8\").includes(\"musl\");\n } catch {\n return false;\n }\n}\n\n/**\n * Return a runnable `cloudflared` with zero user action: PATH → cached download\n * → verified auto-download. Fails closed (never runs an unverified binary).\n */\nexport async function ensureCloudflared(): Promise<string> {\n if (binaryWorks(\"cloudflared\")) return \"cloudflared\";\n const cached = cachedPath();\n if (existsSync(cached) && binaryWorks(cached)) return cached;\n return downloadCloudflared(cached);\n}\n\n/** Exported for tests (fail-closed verification). */\nexport async function downloadCloudflared(dest: string): Promise<string> {\n if (isMusl()) {\n throw new CliError(\"cloudflared has no musl (Alpine) build.\", {\n hint: \"install it manually: https://github.com/cloudflare/cloudflared/releases\",\n });\n }\n const key = resolveAssetKey(process.platform, process.arch);\n const asset = ASSETS[key];\n if (!asset || !asset.sha256) {\n throw new CliError(`Auto-install unavailable for ${key} (no pinned checksum).`, {\n hint: \"install cloudflared manually: https://github.com/cloudflare/cloudflared/releases\",\n });\n }\n\n say.step(`cloudflared not found — downloading v${PINNED_VERSION} (checksum-verified)…`);\n let bytes: Buffer;\n try {\n const res = await fetch(`${RELEASE_BASE}/${asset.file}`, { signal: AbortSignal.timeout(120_000) });\n if (!res.ok) throw new CliError(`Download failed (HTTP ${res.status}).`);\n bytes = Buffer.from(await res.arrayBuffer());\n } catch (err) {\n if (err instanceof CliError) throw err;\n throw new CliError(`Could not download cloudflared (${(err as Error).message}).`, {\n hint: \"check your network, or install cloudflared manually: https://github.com/cloudflare/cloudflared/releases\",\n });\n }\n\n const digest = createHash(\"sha256\").update(bytes).digest(\"hex\");\n if (digest !== asset.sha256) {\n throw new CliError(\"cloudflared checksum mismatch — refusing to run the download.\", {\n hint: \"network tampering or an outdated pin; install manually instead\",\n });\n }\n\n ensureDirs();\n const binary = asset.archive ? extractTgz(bytes) : bytes;\n writeFileSync(dest, binary, { mode: 0o755 });\n chmodSync(dest, 0o755);\n if (!binaryWorks(dest)) throw new CliError(\"Downloaded cloudflared is not runnable.\");\n return dest;\n}\n\n/**\n * Extract the `cloudflared` entry from a gzipped tar (darwin assets ship a\n * single-file .tgz in ustar format). Minimal tar reader — no external dep.\n * Exported for tests.\n */\nexport function extractTgz(bytes: Buffer): Buffer {\n const tar = gunzipSync(bytes);\n for (let off = 0; off + 512 <= tar.length; ) {\n const name = tar.toString(\"utf8\", off, off + 100).replace(/\\0.*/s, \"\");\n if (!name) break; // trailing zero block ⇒ archive end\n const size = parseInt(tar.toString(\"utf8\", off + 124, off + 136).replace(/\\0.*/s, \"\").trim(), 8) || 0;\n const type = tar[off + 156]; // 0x30 '0' or 0x00 ⇒ regular file\n const dataStart = off + 512;\n if ((type === 0x30 || type === 0) && name.split(\"/\").pop() === \"cloudflared\") {\n return tar.subarray(dataStart, dataStart + size);\n }\n off = dataStart + Math.ceil(size / 512) * 512;\n }\n throw new CliError(\"cloudflared entry not found in downloaded archive.\", {\n hint: \"install cloudflared via `brew install cloudflared`\",\n });\n}\n","import { join } from \"node:path\";\nimport * as clack from \"@clack/prompts\";\nimport { reportError } from \"../ui/errors.js\";\nimport { dim, formatRoute, say } from \"../ui/output.js\";\nimport type { Cf } from \"../cloudflare/client.js\";\nimport { logDir } from \"../config/paths.js\";\nimport { startConnector } from \"../connector/process.js\";\nimport { waitHealthy, type HealthResult } from \"../connector/health.js\";\nimport { currentBootId, patchEntry } from \"../connector/registry.js\";\nimport { createTunnelSubdomain, type CreateOptions } from \"./orchestrator-create.js\";\nimport { removeTunnelSubdomain } from \"./orchestrator-manage.js\";\nimport { serviceUrl } from \"./ingress.js\";\nimport type { TransportProtocol } from \"./transport-protocol.js\";\n\ninterface StartedTunnel {\n fqdn: string;\n subdomain: string;\n tunnelId: string;\n target: string;\n pid: number;\n}\n\n/** Log-file label for a subdomain (\"@\" → root). */\nfunction logFileFor(subdomain: string): string {\n return join(logDir, `${subdomain === \"@\" ? \"root\" : subdomain}.log`);\n}\n\n/**\n * Create + connect a batch of tunnels (1..N). Foreground: waits for health, then\n * any exit (Ctrl-C / crash) releases every tunnel started here (2-state model).\n * `--detach`: starts them all in the background and returns.\n */\nexport async function startTunnels(\n cf: Cf,\n bin: string,\n items: CreateOptions[],\n opts: { detach?: boolean; protocol?: TransportProtocol } = {},\n): Promise<void> {\n const started: StartedTunnel[] = [];\n\n // Foreground is up-while-running: any exit (Ctrl-C, a signal, or a connector\n // crash) releases every tunnel started here (2-state model). Defined before the\n // create loop so `onExit` can reference it; registered as signal handlers before\n // the health wait so a Ctrl-C during that ≤30s window doesn't leak resources.\n let tornDown = false;\n const teardownAll = async (code: number): Promise<void> => {\n if (tornDown) return;\n tornDown = true;\n try {\n for (const s of started) {\n try {\n await removeTunnelSubdomain(cf, s.fqdn, { force: true, quiet: true });\n } catch {\n /* best-effort release */\n }\n }\n if (process.stdout.isTTY) clack.outro(`Stopped · released ${started.length} subdomain(s)`);\n } catch (err) {\n reportError(err);\n } finally {\n process.exit(code);\n }\n };\n\n const spin = clack.spinner();\n spin.start(items.length > 1 ? \"Creating tunnels…\" : \"Creating tunnel…\");\n for (const item of items) {\n spin.message(`Creating ${item.name ?? \"tunnel\"} (:${item.port})…`);\n const result = await createTunnelSubdomain(cf, item);\n const fqdn = result.host.hostname;\n const logFile = logFileFor(result.host.subdomain);\n const conn = startConnector({\n bin, token: result.token, detach: !!opts.detach, logFile, protocol: opts.protocol,\n onExit: opts.detach ? undefined : (code) => {\n if (!tornDown) {\n say.warn(`Connector for ${fqdn} exited.`);\n void teardownAll(code ?? 1);\n }\n },\n });\n await patchEntry(fqdn, { pid: conn.pid, bootId: currentBootId(), logFile, protocol: opts.protocol });\n started.push({\n fqdn, subdomain: result.host.subdomain, tunnelId: result.tunnelId,\n target: serviceUrl(item.proto, item.host ?? \"localhost\", item.port), pid: conn.pid,\n });\n }\n\n // Detached: print URLs + pids and exit; the connectors keep running.\n if (opts.detach) {\n spin.stop(`${started.length} tunnel(s) started in the background`);\n const lines = started.map((s) => `${formatRoute(s.fqdn, s.target)} ${dim(`pid ${s.pid}`)}`);\n clack.note(lines.join(\"\\n\"), \"running in background\");\n if (process.stdout.isTTY) clack.outro(\"Stop with: cloudtunnel delete <#|--all>\");\n return;\n }\n\n for (const sig of [\"SIGINT\", \"SIGHUP\", \"SIGTERM\"] as const) {\n process.on(sig, () => void teardownAll(0));\n }\n\n spin.message(\"Connecting to the Cloudflare edge…\");\n const healths = await Promise.all(started.map((s) => waitHealthy(cf, s.tunnelId, { timeoutMs: 30_000 })));\n const live = healths.filter((h: HealthResult) => h === \"healthy\").length;\n spin.stop(`${started.length} tunnel(s) started`);\n\n const lines = started.map((s, i) => `${formatRoute(s.fqdn, s.target)}${healths[i] === \"healthy\" ? \"\" : dim(` (${healths[i]})`)}`);\n clack.note(lines.join(\"\\n\"), `${live}/${started.length} live`);\n say.dim(\"Ctrl-C stops and releases them.\");\n}\n","import { type ChildProcess, execFileSync, spawn } from \"node:child_process\";\nimport { openSync } from \"node:fs\";\nimport { CliError } from \"../ui/errors.js\";\nimport { isOurConnector, type RegistryEntry } from \"./registry.js\";\n\nexport interface StartOptions {\n bin: string;\n token: string;\n detach: boolean;\n logFile: string;\n /** cloudflared edge transport (quic | http2 | auto). Omitted ⇒ cloudflared's\n * default. Force `http2` on UDP-hostile networks that drop idle QUIC. */\n protocol?: string;\n /** Foreground only: fired when the connector exits for ANY reason (crash,\n * bad token, or a signal) so the caller can tear down / report. */\n onExit?: (code: number | null) => void;\n}\n\nexport interface StartedConnector {\n pid: number;\n child?: ChildProcess;\n}\n\nconst sleep = (ms: number) => new Promise((r) => setTimeout(r, ms));\n\n/**\n * Spawn `cloudflared tunnel run`. The token is passed via the TUNNEL_TOKEN env\n * var — NEVER as an argv arg (argv is world-readable via `ps`/proc). Output goes\n * to a 0600 logfile (both foreground and detached) so the CLI can render its own\n * clean status instead of cloudflared's raw logs.\n */\nexport function startConnector(opts: StartOptions): StartedConnector {\n const args = [\"tunnel\", \"run\"];\n // Edge transport: pass as an explicit flag so it also lands in the connector\n // cmdline (visible/reproducible), not only via env.\n if (opts.protocol) args.push(\"--protocol\", opts.protocol);\n const env = { ...process.env, TUNNEL_TOKEN: opts.token };\n const fd = openSync(opts.logFile, \"a\", 0o600);\n const child = spawn(opts.bin, args, { env, detached: opts.detach, stdio: [\"ignore\", fd, fd] });\n if (!child.pid) throw new CliError(\"Failed to start the cloudflared connector.\");\n\n if (opts.detach) {\n child.unref();\n return { pid: child.pid };\n }\n child.on(\"exit\", (code) => opts.onExit?.(code));\n child.on(\"error\", () => opts.onExit?.(1));\n return { pid: child.pid, child };\n}\n\n/**\n * Stop a connector by registry entry. Verifies the pid is still OUR cloudflared\n * (alive, same boot, right cmdline) BEFORE signalling, so a reused pid held by\n * an unrelated process is never killed. Returns true if a stop was issued.\n */\nexport async function stopConnector(entry: RegistryEntry): Promise<boolean> {\n if (!entry.pid || !(await isOurConnector(entry))) return false;\n const pid = entry.pid;\n\n if (process.platform === \"win32\") {\n try {\n execFileSync(\"taskkill\", [\"/pid\", String(pid), \"/T\", \"/F\"], { stdio: \"ignore\" });\n } catch {\n return false;\n }\n return true;\n }\n\n try {\n process.kill(pid, \"SIGTERM\");\n } catch {\n return false;\n }\n await sleep(3000);\n if (await isOurConnector(entry)) {\n try {\n process.kill(pid, \"SIGKILL\");\n } catch {\n // already gone\n }\n }\n return true;\n}\n","import { existsSync, readFileSync, renameSync, writeFileSync } from \"node:fs\";\nimport { readFile } from \"node:fs/promises\";\nimport os from \"node:os\";\nimport lockfile from \"proper-lockfile\";\nimport { ensureDirs, registryFile } from \"../config/paths.js\";\nimport type { TransportProtocol } from \"../core/transport-protocol.js\";\n\nexport type EntryState = \"provisioning\" | \"running\" | \"stopped\" | \"orphaned\";\n\nexport interface RegistryEntry {\n subdomain: string;\n zone: string;\n zoneId: string;\n index?: number; // small stable handle shown as `#` in `ls` (target by number)\n tunnelId?: string;\n dnsRecordId?: string;\n port: number;\n proto: \"http\" | \"https\";\n host?: string; // forward target host (absent = localhost)\n protocol?: TransportProtocol; // cloudflared edge transport (absent = auto)\n pid?: number;\n bootId?: string;\n logFile?: string;\n createdAt: string;\n state: EntryState;\n}\n\n/** The real hostname for an entry. `@` is the apex, keyed in the registry by the\n * bare zone (NOT `@.zone`), so every entry→fqdn reconstruction must go through\n * this — otherwise apex tunnels become untargetable and leak. */\nexport function entryFqdn(e: Pick<RegistryEntry, \"subdomain\" | \"zone\">): string {\n return e.subdomain === \"@\" ? e.zone : `${e.subdomain}.${e.zone}`;\n}\n\ntype Registry = Record<string, RegistryEntry>;\n\n/** Stable per-boot id so a pid reused after a reboot is never mistaken for ours.\n * On systems without the Linux boot_id file (e.g. macOS), fall back to the boot\n * *time* bucketed to the minute — this is constant between invocations (unlike\n * `os.uptime()`, which increases every second and would break connector tracking). */\nexport function currentBootId(): string {\n try {\n return readFileSync(\"/proc/sys/kernel/random/boot_id\", \"utf8\").trim();\n } catch {\n const bootMinute = Math.floor((Date.now() - os.uptime() * 1000) / 60_000);\n return `boot-${bootMinute}-${os.hostname()}`;\n }\n}\n\nfunction readRegistry(): Registry {\n try {\n return JSON.parse(readFileSync(registryFile, \"utf8\")) as Registry;\n } catch {\n return {};\n }\n}\n\nfunction writeRegistry(reg: Registry): void {\n ensureDirs();\n const tmp = `${registryFile}.tmp`;\n writeFileSync(tmp, JSON.stringify(reg, null, 2), { mode: 0o600 });\n renameSync(tmp, registryFile); // atomic on the same filesystem\n}\n\n/** Lock-guarded read-modify-write (prevents lost updates across concurrent runs). */\nexport async function mutateRegistry<T>(fn: (reg: Registry) => T): Promise<T> {\n ensureDirs();\n if (!existsSync(registryFile)) writeFileSync(registryFile, \"{}\", { mode: 0o600 });\n const release = await lockfile.lock(registryFile, { retries: { retries: 10, minTimeout: 50 } });\n try {\n const reg = readRegistry();\n const result = fn(reg);\n writeRegistry(reg);\n return result;\n } finally {\n await release();\n }\n}\n\nexport function listEntries(): RegistryEntry[] {\n return Object.values(readRegistry());\n}\n\nexport function getEntry(fqdn: string): RegistryEntry | undefined {\n return readRegistry()[fqdn];\n}\n\nexport function upsertEntry(fqdn: string, patch: Partial<RegistryEntry> & Pick<RegistryEntry, \"subdomain\" | \"zone\" | \"zoneId\" | \"port\" | \"proto\">): Promise<void> {\n return mutateRegistry((reg) => {\n const prev = reg[fqdn];\n reg[fqdn] = {\n createdAt: prev?.createdAt ?? new Date().toISOString(),\n index: prev?.index ?? nextIndex(reg),\n state: \"provisioning\",\n ...prev,\n ...patch,\n };\n });\n}\n\n/** Smallest positive integer not currently used as an entry index (reused when\n * an entry is removed) — the friendly `#` handle shown in `ls`. */\nfunction nextIndex(reg: Registry): number {\n const used = new Set(\n Object.values(reg)\n .map((e) => e.index)\n .filter((n): n is number => typeof n === \"number\"),\n );\n let i = 1;\n while (used.has(i)) i++;\n return i;\n}\n\n/** Merge changed fields onto an existing entry under the lock (no stale\n * full-snapshot read outside the lock — avoids lost updates). No-op if absent. */\nexport function patchEntry(fqdn: string, patch: Partial<RegistryEntry>): Promise<void> {\n return mutateRegistry((reg) => {\n const prev = reg[fqdn];\n if (prev) reg[fqdn] = { ...prev, ...patch };\n });\n}\n\nexport function removeEntry(fqdn: string): Promise<void> {\n return mutateRegistry((reg) => {\n delete reg[fqdn];\n });\n}\n\nfunction pidAlive(pid: number): boolean {\n try {\n process.kill(pid, 0);\n return true;\n } catch {\n return false;\n }\n}\n\n/** Verify a pid is still OUR cloudflared: alive, same boot, and (Linux) its\n * cmdline is cloudflared — so we never signal a reused pid. */\nexport async function isOurConnector(entry: RegistryEntry): Promise<boolean> {\n if (!entry.pid || entry.bootId !== currentBootId()) return false;\n if (!pidAlive(entry.pid)) return false;\n if (process.platform === \"linux\") {\n try {\n const cmdline = await readFile(`/proc/${entry.pid}/cmdline`, \"utf8\");\n return cmdline.includes(\"cloudflared\");\n } catch {\n return false;\n }\n }\n return true; // non-Linux: bootId + liveness (best effort)\n}\n\n/** Mark entries whose connector is no longer alive as `stopped`. */\nexport async function reconcile(): Promise<RegistryEntry[]> {\n const entries = listEntries();\n for (const entry of entries) {\n if (entry.state === \"running\" && !(await isOurConnector(entry))) {\n const fqdn = entryFqdn(entry);\n await mutateRegistry((reg) => {\n const e = reg[fqdn];\n if (e) {\n e.state = \"stopped\";\n delete e.pid;\n }\n });\n }\n }\n return listEntries();\n}\n","import { cfPaginate, cfRequest, type Cf } from \"./client.js\";\nimport type { Connection, IngressRule, Tunnel } from \"./types.js\";\nimport { CliError } from \"../ui/errors.js\";\n\n/** Tunnels created by cloudtunnel carry this name prefix (ownership marker). */\nexport const MANAGED_TUNNEL_PREFIX = \"ct-\";\n\nexport function isManagedTunnel(tunnel: Tunnel): boolean {\n return tunnel.name.startsWith(MANAGED_TUNNEL_PREFIX);\n}\n\nexport async function createTunnel(cf: Cf, name: string): Promise<Tunnel> {\n const env = await cfRequest<Tunnel>(cf.token, \"POST\", `/accounts/${cf.accountId}/cfd_tunnel`, {\n name,\n config_src: \"cloudflare\",\n });\n return env.result;\n}\n\nexport function listTunnels(cf: Cf): Promise<Tunnel[]> {\n return cfPaginate<Tunnel>(cf.token, `/accounts/${cf.accountId}/cfd_tunnel?is_deleted=false`);\n}\n\nexport async function getTunnel(cf: Cf, id: string): Promise<Tunnel> {\n return (await cfRequest<Tunnel>(cf.token, \"GET\", `/accounts/${cf.accountId}/cfd_tunnel/${id}`)).result;\n}\n\nexport async function deleteTunnel(cf: Cf, id: string): Promise<void> {\n await cfRequest<unknown>(cf.token, \"DELETE\", `/accounts/${cf.accountId}/cfd_tunnel/${id}`);\n}\n\n/** Force-disconnect a tunnel's (possibly stale) connectors so it can be deleted. */\nexport async function cleanupConnections(cf: Cf, id: string): Promise<void> {\n await cfRequest<unknown>(cf.token, \"DELETE\", `/accounts/${cf.accountId}/cfd_tunnel/${id}/connections`);\n}\n\n/** Delete a tunnel; if Cloudflare refuses because it still has active\n * connections (a connector died but the edge hasn't reaped it yet), clean the\n * connections up and retry once. */\nexport async function deleteTunnelWithConnections(cf: Cf, id: string): Promise<void> {\n try {\n await deleteTunnel(cf, id);\n } catch (err) {\n if (err instanceof CliError && /active connections/i.test(err.message)) {\n await cleanupConnections(cf, id);\n await deleteTunnel(cf, id);\n } else {\n throw err;\n }\n }\n}\n\n/** The connector token (encodes tunnelId + secret) passed to `cloudflared`. */\nexport async function getTunnelToken(cf: Cf, id: string): Promise<string> {\n return (await cfRequest<string>(cf.token, \"GET\", `/accounts/${cf.accountId}/cfd_tunnel/${id}/token`)).result;\n}\n\n/** Full-replace ingress config (safe: one hostname + catch-all per tunnel). */\nexport async function putIngress(cf: Cf, id: string, ingress: IngressRule[]): Promise<void> {\n await cfRequest<unknown>(cf.token, \"PUT\", `/accounts/${cf.accountId}/cfd_tunnel/${id}/configurations`, {\n config: { ingress },\n });\n}\n\n/** Active connector instances (≥1 ⇒ tunnel is serving). */\nexport async function getConnections(cf: Cf, id: string): Promise<Connection[]> {\n const env = await cfRequest<Connection[]>(\n cf.token,\n \"GET\",\n `/accounts/${cf.accountId}/cfd_tunnel/${id}/connections`,\n );\n return env.result ?? [];\n}\n","import { getConnections } from \"../cloudflare/tunnels.js\";\nimport type { Cf } from \"../cloudflare/client.js\";\n\nexport type HealthResult = \"healthy\" | \"provisioning\" | \"dead\";\n\nconst sleep = (ms: number) => new Promise((r) => setTimeout(r, ms));\n\n/**\n * Poll the tunnel's connections until it's serving. `signal` is fired by the\n * caller when the connector process exits, so a dead connector returns `dead`\n * immediately instead of waiting out the timeout. `provisioning` is only\n * returned if the process is still alive at the deadline (never a false\n * \"healthy\"). Note: this measures connector↔edge, not local-origin, health.\n */\nexport async function waitHealthy(\n cf: Cf,\n tunnelId: string,\n opts: { signal?: AbortSignal; timeoutMs?: number } = {},\n): Promise<HealthResult> {\n const deadline = Date.now() + (opts.timeoutMs ?? 30_000);\n while (Date.now() < deadline) {\n if (opts.signal?.aborted) return \"dead\";\n try {\n const connections = await getConnections(cf, tunnelId);\n if (connections.length > 0) return \"healthy\";\n } catch {\n // transient API error — keep polling until the deadline\n }\n await sleep(2000);\n }\n return opts.signal?.aborted ? \"dead\" : \"provisioning\";\n}\n","import { randomInt } from \"node:crypto\";\nimport type { Cf } from \"../cloudflare/client.js\";\nimport { resolveZone } from \"../cloudflare/zones.js\";\nimport {\n MANAGED_TUNNEL_PREFIX,\n createTunnel,\n deleteTunnel,\n deleteTunnelWithConnections,\n getTunnel,\n getTunnelToken,\n isManagedTunnel,\n putIngress,\n} from \"../cloudflare/tunnels.js\";\nimport { createCname, deleteDnsRecord, findCname } from \"../cloudflare/dns.js\";\nimport type { DnsRecord } from \"../cloudflare/types.js\";\nimport { buildIngress } from \"./ingress.js\";\nimport { resolveHostSpec, type HostSpec } from \"./slug.js\";\nimport { currentBootId, patchEntry, removeEntry, upsertEntry } from \"../connector/registry.js\";\nimport { CliError } from \"../ui/errors.js\";\nimport { confirm, say } from \"../ui/output.js\";\n\nexport interface CreateOptions {\n port: number;\n proto: \"http\" | \"https\";\n name?: string;\n zone?: string;\n hostname?: string;\n host?: string; // forward target host (absent = localhost)\n defaultZone?: string;\n force?: boolean;\n yes?: boolean; // skip the \"replace existing record?\" confirmation\n}\n\nexport interface CreateResult {\n host: HostSpec;\n tunnelId: string;\n token: string;\n}\n\nconst tunnelIdFromCname = (content: string): string => content.replace(/\\.cfargotunnel\\.com\\.?$/, \"\");\n\n/**\n * Create a tunnel subdomain transactionally (idempotent). Any leftover tunnel\n * record for the same hostname is cleaned up first, so re-running `up` never\n * conflicts. A `provisioning` registry entry is written BEFORE any Cloudflare\n * resource; on failure everything is unwound in reverse and the original error\n * is surfaced.\n */\nexport async function createTunnelSubdomain(cf: Cf, opts: CreateOptions): Promise<CreateResult> {\n const host = resolveHostSpec(opts, opts.defaultZone);\n const zone = await resolveZone(cf.token, host.zone);\n\n const existing = await findCname(cf.token, zone.id, host.hostname);\n if (existing) {\n // A leftover tunnel record → replaceable. A non-tunnel DNS record (A record,\n // ordinary CNAME) → refuse unless --force, to avoid clobbering unrelated DNS.\n const isTunnelRecord = existing.content.endsWith(\".cfargotunnel.com\");\n if (!isTunnelRecord && !opts.force) {\n throw new CliError(`${host.hostname} is taken by a non-tunnel DNS record.`, {\n hint: \"pick another --subdomain/--hostname, or pass -f/--force to replace it\",\n });\n }\n // Confirm before replacing an existing record (interactive only; -f/-y skip).\n if (!opts.force && !opts.yes && process.stdin.isTTY) {\n const kind = isTunnelRecord ? \"tunnel\" : \"DNS\";\n if (!(await confirm(`${host.hostname} already has a ${kind} record. Replace it?`))) {\n throw new CliError(\"Cancelled.\", { exitCode: 130 });\n }\n }\n await releaseHostname(cf, zone.id, existing);\n }\n\n // Track provisioning BEFORE creating anything irreversible.\n await upsertEntry(host.hostname, {\n subdomain: host.subdomain, zone: host.zone, zoneId: zone.id,\n port: opts.port, proto: opts.proto, host: opts.host, state: \"provisioning\",\n });\n\n let tunnelId: string | undefined;\n let dnsRecordId: string | undefined;\n try {\n const suffix = randomInt(0x10000).toString(16).padStart(4, \"0\");\n const label = host.subdomain === \"@\" ? \"root\" : host.subdomain;\n const tunnel = await createTunnel(cf, `${MANAGED_TUNNEL_PREFIX}${label}-${suffix}`);\n tunnelId = tunnel.id;\n const token = await getTunnelToken(cf, tunnelId);\n await putIngress(cf, tunnelId, buildIngress({ hostname: host.hostname, port: opts.port, proto: opts.proto, host: opts.host }));\n const record = await createCname(cf.token, zone.id, host.hostname, tunnelId);\n dnsRecordId = record.id;\n await recordRunning(host, zone.id, tunnelId, dnsRecordId, opts);\n return { host, tunnelId, token };\n } catch (err) {\n const clean = await rollback(cf, zone.id, tunnelId, dnsRecordId, host.hostname);\n if (clean) await removeEntry(host.hostname);\n else await patchEntry(host.hostname, { state: \"orphaned\" });\n throw err;\n }\n}\n\nasync function recordRunning(host: HostSpec, zoneId: string, tunnelId: string, dnsRecordId: string, opts: CreateOptions): Promise<void> {\n await upsertEntry(host.hostname, {\n subdomain: host.subdomain, zone: host.zone, zoneId,\n tunnelId, dnsRecordId, port: opts.port, proto: opts.proto, host: opts.host,\n bootId: currentBootId(), state: \"running\",\n });\n}\n\n/** Free a hostname before recreating: delete its DNS record, and if it pointed\n * at a cloudtunnel-managed tunnel, delete that tunnel too (cleaning up any\n * lingering connections). A foreign tunnel is left alone — we only free the name. */\nasync function releaseHostname(cf: Cf, zoneId: string, record: DnsRecord): Promise<void> {\n if (record.content.endsWith(\".cfargotunnel.com\")) {\n const oldTunnelId = tunnelIdFromCname(record.content);\n try {\n const tunnel = await getTunnel(cf, oldTunnelId);\n if (isManagedTunnel(tunnel)) await deleteTunnelWithConnections(cf, oldTunnelId);\n } catch {\n /* tunnel already gone or not accessible — freeing the DNS name is enough */\n }\n }\n await deleteDnsRecord(cf.token, zoneId, record.id);\n}\n\n/** Unwind created resources in reverse. Never masks the original error; if a\n * step fails, report the leaked id and return false so the caller marks the\n * entry `orphaned`. */\nasync function rollback(cf: Cf, zoneId: string, tunnelId?: string, dnsRecordId?: string, hostname?: string): Promise<boolean> {\n let clean = true;\n if (dnsRecordId) {\n try { await deleteDnsRecord(cf.token, zoneId, dnsRecordId); }\n catch { clean = false; say.warn(`Left a DNS record behind for ${hostname} (${dnsRecordId}).`); }\n }\n if (tunnelId) {\n try { await deleteTunnel(cf, tunnelId); }\n catch { clean = false; say.warn(`Left tunnel ${tunnelId} behind — remove it with \\`cloudtunnel delete ${tunnelId} -f\\`.`); }\n }\n return clean;\n}\n","import { randomInt } from \"node:crypto\";\nimport { CliError } from \"../ui/errors.js\";\n\nconst ADJECTIVES = [\n \"brave\", \"calm\", \"clever\", \"eager\", \"gentle\", \"happy\", \"jolly\", \"kind\",\n \"lively\", \"mighty\", \"nimble\", \"proud\", \"quick\", \"royal\", \"swift\", \"witty\",\n];\nconst NOUNS = [\n \"otter\", \"falcon\", \"maple\", \"comet\", \"harbor\", \"lynx\", \"willow\", \"cedar\",\n \"raven\", \"meadow\", \"pixel\", \"quartz\", \"river\", \"sparrow\", \"tiger\", \"walnut\",\n];\n\nconst pick = <T>(arr: T[]): T => arr[randomInt(arr.length)]!;\n\n/** A friendly random subdomain, e.g. `brave-otter-1a2b` (the default when unnamed). */\nexport function randomSlug(): string {\n const suffix = randomInt(0x10000).toString(16).padStart(4, \"0\");\n return `${pick(ADJECTIVES)}-${pick(NOUNS)}-${suffix}`;\n}\n\nexport interface HostSpec {\n subdomain: string;\n zone: string;\n hostname: string;\n}\n\n/**\n * Resolve the target hostname from flags. Precedence: --hostname > --name+zone >\n * random-slug+zone. Zone comes from --zone or the saved default; missing zone is\n * an actionable error. (--hostname assumes `label.zone`; deeper subdomains need\n * the zone to be an actual Cloudflare zone.)\n */\nexport function resolveHostSpec(\n opts: { name?: string; zone?: string; hostname?: string },\n defaultZone?: string,\n): HostSpec {\n if (opts.hostname) {\n const dot = opts.hostname.indexOf(\".\");\n if (dot <= 0) throw new CliError(`Invalid hostname: ${opts.hostname}`);\n return {\n subdomain: opts.hostname.slice(0, dot),\n zone: opts.hostname.slice(dot + 1),\n hostname: opts.hostname,\n };\n }\n const zone = opts.zone ?? defaultZone;\n if (!zone) {\n throw new CliError(\"No zone specified and no default zone set.\", {\n hint: \"pass --zone <domain>, or run `cloudtunnel login --zone <domain>`\",\n });\n }\n const subdomain = opts.name ?? randomSlug();\n // `@` means the root/apex domain (Cloudflare flattens the proxied CNAME).\n const hostname = subdomain === \"@\" ? zone : `${subdomain}.${zone}`;\n return { subdomain, zone, hostname };\n}\n","import { readFileSync, writeFileSync } from \"node:fs\";\nimport { ensureDirs, scanCacheFile } from \"../config/paths.js\";\n\n/**\n * `ls --all` numbers the unmanaged tunnels it finds so they can be targeted by\n * `#` like tracked ones. Those rows live only on Cloudflare (no registry entry),\n * so the number→hostname mapping of the LAST scan is pinned here. A later\n * `delete <#>` resolves against this pin — never against a fresh re-scan —\n * so the number always means exactly the row the user saw on screen, even if\n * the account changed in between (deletion re-verifies DNS freshly anyway).\n */\nexport interface ScannedUnmanaged {\n fqdn: string;\n tunnelId: string;\n}\n\ntype ScanCache = Record<string, ScannedUnmanaged>;\n\n/** Replace the pin with this scan's numbering. Best-effort: listing must never\n * fail because the cache can't be written. */\nexport function saveUnmanagedScan(rows: Map<number, ScannedUnmanaged>): void {\n try {\n ensureDirs();\n const cache: ScanCache = {};\n for (const [num, row] of rows) cache[String(num)] = row;\n writeFileSync(scanCacheFile, JSON.stringify(cache, null, 2), { mode: 0o600 });\n } catch {\n /* the pin is a convenience, not state */\n }\n}\n\n/** The unmanaged row this `#` pointed at in the last `ls --all` (undefined when\n * the number was never shown, or no scan has run). */\nexport function lookupUnmanagedByIndex(num: number): ScannedUnmanaged | undefined {\n try {\n const cache = JSON.parse(readFileSync(scanCacheFile, \"utf8\")) as ScanCache;\n return cache[String(num)];\n } catch {\n return undefined;\n }\n}\n","import type { Cf } from \"../cloudflare/client.js\";\nimport { resolveZone } from \"../cloudflare/zones.js\";\nimport { deleteTunnelWithConnections, getTunnel, isManagedTunnel, listTunnels } from \"../cloudflare/tunnels.js\";\nimport { deleteDnsRecord, findCname, isManagedDns } from \"../cloudflare/dns.js\";\nimport type { DnsRecord, Tunnel } from \"../cloudflare/types.js\";\nimport { CliError } from \"../ui/errors.js\";\nimport { say } from \"../ui/output.js\";\nimport { entryFqdn, getEntry, listEntries, reconcile, removeEntry, type RegistryEntry } from \"../connector/registry.js\";\nimport { stopConnector } from \"../connector/process.js\";\nimport { serviceUrl } from \"./ingress.js\";\nimport { serviceState } from \"./service.js\";\nimport { saveUnmanagedScan, type ScannedUnmanaged } from \"./unmanaged-scan-cache.js\";\n\nconst tunnelIdFromCname = (content: string): string => content.replace(/\\.cfargotunnel\\.com\\.?$/, \"\");\nconst isNotFound = (err: unknown): boolean => err instanceof CliError && err.status === 404;\nconst zoneFromFqdn = (fqdn: string): string => fqdn.slice(fqdn.indexOf(\".\") + 1);\n\n/** Resolve a target to its registry entry / fqdn. Accepts a full hostname, the\n * `#` number, a subdomain name, or a tunnel-id prefix (all shown in `ls`).\n * Refuses an ambiguous match. */\nexport function resolveTarget(target: string): { fqdn: string; entry?: RegistryEntry } {\n if (target.includes(\".\")) return { fqdn: target, entry: getEntry(target) };\n const entries = listEntries();\n if (/^\\d+$/.test(target)) {\n const byIndex = entries.find((e) => e.index === Number(target));\n if (byIndex) return { fqdn: entryFqdn(byIndex), entry: byIndex };\n }\n const byId = entries.filter((e) => e.tunnelId?.startsWith(target));\n const matches = byId.length > 0 ? byId : entries.filter((e) => e.subdomain === target);\n if (matches.length > 1) {\n throw new CliError(`\"${target}\" matches multiple subdomains.`, {\n hint: `use a full hostname or a longer id: ${matches.map(entryFqdn).join(\", \")}`,\n });\n }\n const entry = matches[0];\n if (!entry) {\n throw new CliError(`No tracked subdomain matching \"${target}\".`, { hint: \"see `cloudtunnel ls` for the #, name, or id\" });\n }\n return { fqdn: entryFqdn(entry), entry };\n}\n\nexport interface RemoveOptions { force?: boolean; dryRun?: boolean; quiet?: boolean }\n\n/** Release a subdomain: stop the connector, then delete the tunnel + DNS on\n * Cloudflare. Re-verifies fresh state (cached ids are hints), ownership-gates\n * unmanaged resources, and tolerates already-deleted parts. */\nexport async function removeTunnelSubdomain(cf: Cf, target: string, opts: RemoveOptions = {}): Promise<void> {\n const { fqdn, entry } = resolveTarget(target);\n if (!entry && !opts.force) {\n throw new CliError(`${fqdn} is not managed by cloudtunnel.`, { hint: \"pass --force to release it anyway\" });\n }\n const zoneId = entry?.zoneId ?? (await resolveZone(cf.token, zoneFromFqdn(fqdn))).id;\n\n const record = await findCname(cf.token, zoneId, fqdn); // fresh, authoritative\n if (record && !isManagedDns(record) && !opts.force) {\n throw new CliError(`${fqdn} points to a record not managed by cloudtunnel.`, { hint: \"pass --force to release it\" });\n }\n const tunnelId = record ? tunnelIdFromCname(record.content) : entry?.tunnelId;\n\n if (opts.dryRun) {\n say.info(`Would release: tunnel ${tunnelId ?? \"(none)\"}${record ? `, DNS ${record.id}` : \"\"}`);\n return;\n }\n\n if (entry) await stopConnector(entry);\n if (tunnelId) {\n let tunnel: Tunnel | undefined;\n try {\n tunnel = await getTunnel(cf, tunnelId);\n } catch (err) {\n if (!isNotFound(err)) throw err; // transient error → don't silently orphan\n }\n if (tunnel && !isManagedTunnel(tunnel) && !opts.force) {\n throw new CliError(`Tunnel ${tunnelId} is not managed by cloudtunnel.`, { hint: \"pass --force\" });\n }\n if (tunnel) {\n try {\n await deleteTunnelWithConnections(cf, tunnelId);\n } catch (err) {\n if (!isNotFound(err)) throw err;\n }\n }\n }\n if (record) {\n try {\n await deleteDnsRecord(cf.token, zoneId, record.id);\n } catch (err) {\n if (!isNotFound(err)) throw err;\n }\n }\n await removeEntry(fqdn);\n if (!opts.quiet) say.ok(`Released ${fqdn}`);\n}\n\n/** Only strings that look like a tunnel-id prefix (UUID chars, ≥6) are matched\n * remotely — a mistyped name or `#` number must never match an account tunnel. */\nconst TUNNEL_ID_PREFIX_RE = /^[0-9a-f][0-9a-f-]{5,}$/;\n\nexport interface RemoteTarget { tunnel: Tunnel; fqdn?: string }\n\n/** Resolve a target the registry doesn't know as a tunnel-id prefix on the\n * Cloudflare account (an untracked tunnel has no registry entry, so its id can\n * only be matched remotely). The hostname is recovered from the tunnel's\n * cfargotunnel CNAME when one exists; a DNS-less (leaked) tunnel comes back\n * without an fqdn. Null when the target doesn't look like an id or matches\n * no tunnel. */\nexport async function resolveRemoteTarget(cf: Cf, target: string): Promise<RemoteTarget | null> {\n if (!TUNNEL_ID_PREFIX_RE.test(target)) return null;\n const matches = (await listTunnels(cf)).filter((t) => t.id.startsWith(target));\n if (matches.length > 1) {\n throw new CliError(`\"${target}\" matches ${matches.length} tunnels on the account.`, { hint: \"use a longer id prefix\" });\n }\n const tunnel = matches[0];\n if (!tunnel) return null;\n const { listCargoCnames } = await import(\"../cloudflare/dns.js\");\n const { listZones } = await import(\"../cloudflare/zones.js\");\n for (const zone of await listZones(cf.token)) {\n const rec = (await listCargoCnames(cf.token, zone.id)).find((r) => tunnelIdFromCname(r.content) === tunnel.id);\n if (rec) return { tunnel, fqdn: rec.name };\n }\n return { tunnel };\n}\n\n/** Release a tunnel that has no DNS record (an id-only target): same ownership\n * gate as the fqdn path, honors --dry-run. */\nexport async function removeTunnelById(cf: Cf, tunnel: Tunnel, opts: RemoveOptions = {}): Promise<void> {\n if (!isManagedTunnel(tunnel) && !opts.force) {\n throw new CliError(`Tunnel ${tunnel.id} is not managed by cloudtunnel.`, { hint: \"pass --force to release it\" });\n }\n if (opts.dryRun) {\n say.info(`Would release: tunnel ${tunnel.id} (no DNS record)`);\n return;\n }\n await deleteTunnelWithConnections(cf, tunnel.id);\n if (!opts.quiet) say.ok(`Released tunnel ${tunnel.id}`);\n}\n\nexport interface LsRow { num: string; url: string; target: string; protocol: string; state: string; service: string; pid: string; managed: boolean }\n\n/** Reconcile + list tracked subdomains: `# | URL | TARGET | PROTOCOL | STATE | SERVICE | PID`.\n * PROTOCOL is the cloudflared edge transport the connector was started with\n * (absent = \"auto\", cloudflared's default). SERVICE is the per-subdomain systemd\n * unit's state (\"-\" when none). `all` also scans every zone for cfargotunnel\n * CNAMEs created outside cloudtunnel. */\nexport async function listAll(cf: Cf, opts: { all?: boolean } = {}): Promise<LsRow[]> {\n const entries = await reconcile();\n const tunnels = new Map((await listTunnels(cf)).map((t) => [t.id, t]));\n const rows: LsRow[] = entries.map((e) => {\n const fqdn = entryFqdn(e);\n const gone = e.tunnelId ? !tunnels.has(e.tunnelId) : false;\n const svc = serviceState(fqdn);\n return {\n num: e.index ? String(e.index) : \"-\",\n url: `https://${fqdn}`,\n target: serviceUrl(e.proto, e.host ?? \"localhost\", e.port),\n protocol: e.protocol ?? \"auto\",\n state: !gone && e.state === \"running\" ? \"up\" : \"down\",\n service: svc === \"none\" ? \"-\" : svc,\n pid: e.state === \"running\" && e.pid ? String(e.pid) : \"-\",\n managed: true,\n };\n });\n if (opts.all) {\n const { listCargoCnames } = await import(\"../cloudflare/dns.js\");\n const { listZones } = await import(\"../cloudflare/zones.js\");\n const tracked = new Set(entries.map(entryFqdn));\n const unmanaged: DnsRecord[] = [];\n for (const zone of await listZones(cf.token)) {\n for (const rec of await listCargoCnames(cf.token, zone.id)) {\n if (!tracked.has(rec.name)) unmanaged.push(rec);\n }\n }\n // Unmanaged rows get a `#` too (continuing after the tracked ones, sorted\n // for a stable display) so `delete <#>` works on them. The numbering is\n // pinned to this scan — see unmanaged-scan-cache.\n unmanaged.sort((a, b) => a.name.localeCompare(b.name));\n let next = Math.max(0, ...entries.map((e) => e.index ?? 0)) + 1;\n const scan = new Map<number, ScannedUnmanaged>();\n for (const rec of unmanaged) {\n scan.set(next, { fqdn: rec.name, tunnelId: tunnelIdFromCname(rec.content) });\n rows.push({ num: String(next), url: `https://${rec.name}`, target: \"-\", protocol: \"-\", state: \"unmanaged\", service: \"-\", pid: \"-\", managed: false });\n next++;\n }\n saveUnmanagedScan(scan);\n }\n return rows;\n}\n","import { CliError } from \"../ui/errors.js\";\nimport { selectOne } from \"../ui/output.js\";\nimport type { Cf } from \"../cloudflare/client.js\";\nimport { listZones } from \"../cloudflare/zones.js\";\nimport type { Credentials } from \"../config/store.js\";\n\n/** The domain for a command: `-d` → single zone → picker (TTY) → saved default\n * (non-TTY) → error. Shared by `up` and `relay` so both resolve identically. */\nexport async function resolveDomain(cf: Cf, opts: { domain?: string }, creds: Credentials): Promise<string> {\n if (opts.domain) return opts.domain;\n const zones = await listZones(cf.token);\n if (zones.length === 0) throw new CliError(\"No domains found in this Cloudflare account.\");\n if (zones.length === 1) return zones[0]!.name;\n if (process.stdin.isTTY) return (await selectOne(\"Choose a domain\", zones, (z) => z.name)).name;\n if (creds.defaultZone) return creds.defaultZone;\n throw new CliError(\"Multiple domains in this account — pick one.\", { hint: \"pass -d <domain>\" });\n}\n","import { CliError } from \"../ui/errors.js\";\n\n/**\n * cloudflared edge transport (NOT the local service scheme). `quic` is UDP-based\n * and fastest, but UDP-hostile networks drop idle QUIC sessions (→ Cloudflare\n * 530/502); `http2` runs over TCP and stays stable there. `auto` lets cloudflared\n * choose (defaults to quic when the network probe passes).\n */\nexport type TransportProtocol = \"auto\" | \"http2\" | \"quic\";\n\nexport function parseTransportProtocol(value: string): TransportProtocol {\n if (value === \"auto\" || value === \"http2\" || value === \"quic\") return value;\n throw new CliError(`Invalid protocol \"${value}\".`, { hint: \"use auto, http2, or quic\" });\n}\n","import type { Command } from \"commander\";\nimport { printTable, say } from \"../ui/output.js\";\nimport { ensureAuth } from \"../config/ensure-auth.js\";\nimport { resolveCf } from \"../cloudflare/client.js\";\nimport { listAll } from \"../core/orchestrator-manage.js\";\n\nexport function registerLs(program: Command): void {\n program\n .command(\"ls\")\n .alias(\"ps\")\n .description(\"List tunnel subdomains (managed by default; --all scans the whole account)\")\n .option(\"--all\", \"scan every zone in the account (slower; shows unmanaged tunnels too)\")\n .action(async (opts: { all?: boolean }) => {\n await ensureAuth();\n const cf = resolveCf();\n const rows = await listAll(cf, { all: opts.all });\n if (rows.length === 0) {\n say.info(\"No tunnel subdomains yet. Create one: `cloudtunnel 3000`\");\n return;\n }\n printTable(\n [\"#\", \"URL\", \"TARGET\", \"PROTOCOL\", \"STATE\", \"SERVICE\", \"PID\"],\n rows.map((r) => [r.num, r.url, r.target, r.protocol, r.state, r.service, r.pid]),\n );\n });\n}\n","import type { Command } from \"commander\";\nimport { CliError } from \"../ui/errors.js\";\nimport { say } from \"../ui/output.js\";\nimport { ensureAuth } from \"../config/ensure-auth.js\";\nimport { resolveCf, type Cf } from \"../cloudflare/client.js\";\nimport { entryFqdn, listEntries } from \"../connector/registry.js\";\nimport { removeTunnelById, removeTunnelSubdomain, resolveRemoteTarget, resolveTarget } from \"../core/orchestrator-manage.js\";\nimport { lookupUnmanagedByIndex } from \"../core/unmanaged-scan-cache.js\";\nimport { serviceName, serviceState, uninstallService } from \"../core/service.js\";\n\ninterface DeleteOptions { all?: boolean; force?: boolean; dryRun?: boolean }\n\n/** Remove a subdomain's boot service (if any) first so its supervisor can't\n * restart the connector mid-teardown, then release the tunnel + DNS. */\nasync function deleteOne(cf: Cf, fqdn: string, opts: DeleteOptions): Promise<void> {\n const hasService = serviceState(fqdn) !== \"none\";\n if (hasService && !opts.dryRun) uninstallService(fqdn);\n await removeTunnelSubdomain(cf, fqdn, { force: opts.force, dryRun: opts.dryRun });\n if (!hasService) return;\n if (opts.dryRun) say.info(`Would also remove boot service ${serviceName(fqdn)}`);\n else say.ok(`Removed boot service ${serviceName(fqdn)}`);\n}\n\nexport function registerDelete(program: Command): void {\n program\n .command(\"delete\")\n .argument(\"[targets...]\", \"subdomains to remove by # / name / URL / tunnel-id (omit with --all)\")\n .description(\"Release tunnel(s) — deletes the tunnel + DNS, and any systemd boot service\")\n .option(\"--all\", \"release every tracked subdomain\")\n .option(\"-f, --force\", \"release even a resource not created by cloudtunnel\")\n .option(\"--dry-run\", \"show what would be released without doing it\")\n .action(async (targets: string[], opts: DeleteOptions) => {\n await ensureAuth();\n const cf = resolveCf();\n\n if (opts.all) {\n const entries = listEntries();\n if (entries.length === 0) {\n say.info(\"Nothing to release.\");\n return;\n }\n for (const e of entries) {\n const fqdn = entryFqdn(e);\n try {\n await deleteOne(cf, fqdn, opts);\n } catch (err) {\n say.warn(`Could not release ${fqdn}: ${(err as Error).message}`);\n }\n }\n return;\n }\n\n if (targets.length === 0) throw new CliError(\"Pass a subdomain (# / name / URL / tunnel-id) or --all.\");\n for (const target of targets) {\n let fqdn: string;\n try {\n ({ fqdn } = resolveTarget(target));\n } catch (err) {\n // Unknown to the local registry — the target may be the `#` an\n // unmanaged tunnel was shown with in the last `ls --all`, or the id\n // of an untracked tunnel (created elsewhere, or leaked by a failed\n // run); try both against what's pinned/on the account before giving up.\n const scanned = /^\\d+$/.test(target) ? lookupUnmanagedByIndex(Number(target)) : undefined;\n if (scanned) {\n say.info(`${target} → ${scanned.fqdn} (unmanaged, numbered by the last \\`ls --all\\`)`);\n fqdn = scanned.fqdn;\n } else {\n const remote = await resolveRemoteTarget(cf, target);\n if (!remote) throw err;\n if (!remote.fqdn) {\n await removeTunnelById(cf, remote.tunnel, opts);\n continue;\n }\n fqdn = remote.fqdn;\n }\n }\n await deleteOne(cf, fqdn, opts);\n }\n });\n}\n","import type { Command } from \"commander\";\nimport { closeSync, existsSync, openSync, readFileSync, readSync, statSync, watch } from \"node:fs\";\nimport { CliError } from \"../ui/errors.js\";\nimport { say } from \"../ui/output.js\";\nimport { resolveTarget } from \"../core/orchestrator-manage.js\";\n\ninterface LogsOptions {\n follow?: boolean;\n lines?: string;\n}\n\n/** Print the last `n` lines of a file; return the file's byte size (follow start). */\nfunction printTail(file: string, n: number): number {\n const lines = readFileSync(file, \"utf8\").split(\"\\n\");\n const tail = lines.slice(-n).join(\"\\n\");\n process.stdout.write(tail.endsWith(\"\\n\") ? tail : `${tail}\\n`);\n return statSync(file).size;\n}\n\n/** Tail -f: print appended bytes as the connector writes them. Ctrl-C to stop. */\nfunction follow(file: string, fromPos: number): void {\n let pos = fromPos;\n say.dim(\"— following (Ctrl-C to stop) —\");\n const watcher = watch(file, () => {\n const size = statSync(file).size;\n if (size < pos) {\n pos = 0; // file was truncated/rotated\n return;\n }\n if (size > pos) {\n const fd = openSync(file, \"r\");\n const buf = Buffer.alloc(size - pos);\n readSync(fd, buf, 0, size - pos, pos);\n closeSync(fd);\n process.stdout.write(buf.toString(\"utf8\"));\n pos = size;\n }\n });\n process.on(\"SIGINT\", () => {\n watcher.close();\n process.exit(0);\n });\n}\n\nexport function registerLogs(program: Command): void {\n program\n .command(\"logs\")\n .argument(\"<target>\", \"subdomain name / hostname / id / #\")\n .description(\"Show the connector log for a subdomain (use -f to follow)\")\n .option(\"-f, --follow\", \"keep printing new log lines (like tail -f)\")\n .option(\"-n, --lines <n>\", \"number of lines to show\", \"50\")\n .action((name: string, opts: LogsOptions) => {\n const { fqdn, entry } = resolveTarget(name);\n if (!entry?.logFile || !existsSync(entry.logFile)) {\n throw new CliError(`No logs for ${fqdn} yet.`, { hint: \"start it with `cloudtunnel up` or `cloudtunnel run`\" });\n }\n const n = Math.max(1, Number(opts.lines) || 50);\n const pos = printTail(entry.logFile, n);\n if (opts.follow) follow(entry.logFile, pos);\n });\n}\n","import type { Command } from \"commander\";\nimport { openSync } from \"node:fs\";\nimport { spawn } from \"node:child_process\";\nimport { join } from \"node:path\";\nimport pc from \"picocolors\";\nimport { CliError } from \"../ui/errors.js\";\nimport { say, note } from \"../ui/output.js\";\nimport { ensureAuth } from \"../config/ensure-auth.js\";\nimport { resolveCf } from \"../cloudflare/client.js\";\nimport { ensureCloudflared } from \"../connector/binary.js\";\nimport { logDir, ensureDirs } from \"../config/paths.js\";\nimport { ensureRelaySecret } from \"../config/relay-secret.js\";\nimport { encodeRelayKey } from \"../config/relay-key.js\";\nimport { startProxy } from \"../core/api-proxy-server.js\";\nimport { resolveDomain } from \"../core/resolve-domain.js\";\nimport { startTunnels } from \"../core/up-runner.js\";\nimport type { CreateOptions } from \"../core/orchestrator-create.js\";\nimport { assertServiceSupported, installServiceForSpec } from \"../core/service.js\";\n\ninterface RelayOptions {\n domain?: string;\n detach?: boolean;\n service?: boolean;\n force?: boolean;\n yes?: boolean;\n}\n\nconst DEFAULT_SUB = \"cfapi\";\n\n/** fqdn for a relay subdomain (\"@\" → apex). */\nfunction fqdnFor(sub: string, domain: string): string {\n return sub === \"@\" ? domain : `${sub}.${domain}`;\n}\n\n/**\n * Re-spawn `cloudtunnel relay <sub>` as a DETACHED child running the foreground\n * path (note: NO `--detach` in argv → no recursion). The whole command re-spawns,\n * not just the connector, because the reverse proxy lives IN this process and\n * would die when the CLI exits. The secret is NOT passed on argv — the child\n * reads it back from 0600 config via `getRelaySecret()`.\n */\nfunction spawnDetachedRelay(sub: string, domain: string): void {\n const script = process.argv[1];\n if (!script) throw new CliError(\"Cannot resolve the cloudtunnel executable path.\");\n ensureDirs();\n const logFile = join(logDir, `relay-${sub === \"@\" ? \"root\" : sub}.log`);\n const fd = openSync(logFile, \"a\", 0o600);\n const args = [script, \"relay\", sub, \"-d\", domain, \"-f\", \"-y\"];\n const child = spawn(process.execPath, args, { detached: true, stdio: [\"ignore\", fd, fd] });\n child.unref();\n}\n\n/**\n * The \"relay ready\" lines. The secret is included ONLY for an interactive (TTY)\n * operator. A re-spawned detach child or boot service runs the same foreground\n * path with stdout → a logfile / journald — where the secret must NEVER land: the\n * relay hostname is public and the secret is its sole access gate. Non-TTY ⇒ a\n * bare readiness line, no secret. Pure + exported so the guard is unit-tested.\n */\nexport function relayReadyLines(fqdn: string, secret: string, tty: boolean): string[] {\n const url = `https://${fqdn}`;\n if (!tty) return [`relay ready at ${url}`];\n const base = `${url}/client/v4`;\n const key = encodeRelayKey(base, secret);\n return [\n `URL ${pc.green(url)}`,\n `Secret ${pc.bold(secret)} ${pc.dim(\"(shown once)\")}`,\n \"\",\n pc.bold(\"On the blocked client — one command, no env, saved for good:\"),\n ` cloudtunnel login --relay ${key}`,\n pc.dim(\" → then just `cloudtunnel ls` / `up …`. Mint the CF token on an unblocked host (dash.cloudflare.com is blocked too).\"),\n ];\n}\n\n/** Print the relay URL + (interactive only) the shared secret and client hint. */\nfunction printRelayReady(fqdn: string, secret: string, kind: \"foreground\" | \"detach\" | \"service\"): void {\n const tty = !!process.stdout.isTTY;\n const lines = relayReadyLines(fqdn, secret, tty);\n if (tty) note(lines.join(\"\\n\"), \"relay ready\");\n else say.dim(lines[0]!);\n if (tty && kind === \"detach\") say.dim(\" → running in background · stop with: cloudtunnel delete \" + fqdn);\n if (tty && kind === \"service\") say.dim(\" → boot service installed · view: cloudtunnel ls · remove: cloudtunnel delete \" + fqdn);\n}\n\nasync function runRelay(sub: string, opts: RelayOptions): Promise<void> {\n const creds = await ensureAuth();\n const cf = resolveCf();\n const domain = await resolveDomain(cf, opts, creds);\n const fqdn = fqdnFor(sub, domain);\n\n // --service: install a boot unit that re-runs the relay foreground on boot.\n if (opts.service) {\n assertServiceSupported();\n const secret = ensureRelaySecret();\n installServiceForSpec({ command: \"relay\", subdomain: sub, port: 0, zone: domain, proto: \"http\" });\n printRelayReady(fqdn, secret, \"service\");\n return;\n }\n\n // --detach: re-spawn the whole command detached (proxy must live with it).\n if (opts.detach) {\n const secret = ensureRelaySecret();\n spawnDetachedRelay(sub, domain);\n printRelayReady(fqdn, secret, \"detach\");\n return;\n }\n\n // Foreground: start the proxy in-process, then expose its ephemeral loopback\n // port through the normal tunnel machinery. The proxy speaks plain http on\n // loopback, so the tunnel ingress is always http.\n const bin = await ensureCloudflared();\n const secret = ensureRelaySecret();\n const proxy = await startProxy({ secret, log: (line) => say.dim(line) });\n const item: CreateOptions = {\n port: proxy.port, proto: \"http\", name: sub, zone: domain,\n defaultZone: creds.defaultZone, force: opts.force, yes: opts.yes,\n };\n await startTunnels(cf, bin, [item], {});\n printRelayReady(fqdn, secret, \"foreground\");\n}\n\nexport function registerRelay(program: Command): void {\n program\n .command(\"relay [subdomain]\")\n .description(\"Expose a locked Cloudflare-API reverse proxy via a tunnel (for clients that can't reach api.cloudflare.com directly)\")\n .option(\"-d, --domain <domain>\", \"domain for the relay subdomain (prompted from a list if unset)\")\n .option(\"--detach\", \"run the relay in the background\")\n .option(\"--service\", \"register the relay as a boot service (systemd · launchd · Task Scheduler)\")\n .option(\"-f, --force\", \"replace a non-tunnel DNS record occupying the hostname\")\n .option(\"-y, --yes\", \"don't prompt before replacing an existing record\")\n .action((subdomain: string | undefined, opts: RelayOptions) => runRelay(subdomain ?? DEFAULT_SUB, opts));\n}\n","import http from \"node:http\";\nimport type { AddressInfo } from \"node:net\";\nimport { RELAY_SECRET_HEADER } from \"../config/relay-secret.js\";\n\n/** The ONLY host this relay ever forwards to. A compile-time constant is the\n * structural SSRF/open-proxy defense: the upstream is never derived from the\n * request. `upstream` is overridable ONLY for tests. */\nconst DEFAULT_UPSTREAM = \"https://api.cloudflare.com\";\nconst SECRET_HEADER_LC = RELAY_SECRET_HEADER.toLowerCase();\n\n/** Request headers never forwarded upstream (hop-by-hop + ones `fetch` sets from\n * the URL). The secret header is dropped separately. */\nconst HOP_BY_HOP = new Set([\n \"connection\", \"keep-alive\", \"proxy-authenticate\", \"proxy-authorization\",\n \"te\", \"trailer\", \"transfer-encoding\", \"upgrade\", \"host\", \"content-length\",\n]);\n\nexport interface ProxyHandle {\n port: number;\n url: string;\n close(): Promise<void>;\n}\n\nexport interface StartProxyOptions {\n /** Shared secret the client must present in `X-CT-Relay-Secret`. */\n secret: string;\n /** Upstream origin — tests only; defaults to api.cloudflare.com. */\n upstream?: string;\n /** Per-request access-log sink (method + path + status only — NEVER headers).\n * The `relay` command passes one so operators can see traffic arriving. */\n log?: (line: string) => void;\n}\n\nfunction send(res: http.ServerResponse, code: number, body?: unknown): void {\n res.writeHead(code, { \"content-type\": \"application/json\" });\n res.end(body === undefined ? \"\" : JSON.stringify(body));\n}\n\n/**\n * Start a locked-down reverse proxy on `127.0.0.1:0` (ephemeral, loopback only).\n * Forwards origin-form requests to a single constant upstream, gated by a shared\n * secret. Never logs the Authorization or secret header. Pure module: no CLI, no\n * tunnel — the caller exposes `handle.port` however it likes.\n */\nexport function startProxy(opts: StartProxyOptions): Promise<ProxyHandle> {\n const upstream = opts.upstream ?? DEFAULT_UPSTREAM;\n const upstreamOrigin = new URL(upstream).origin;\n const log = opts.log ?? (() => {});\n\n const server = http.createServer((req, res) => {\n handle(req, res).catch(() => {\n if (!res.headersSent) send(res, 500, { error: \"relay internal error\" });\n else res.end();\n });\n });\n\n async function handle(req: http.IncomingMessage, res: http.ServerResponse): Promise<void> {\n // 1. Origin-form only. Reject CONNECT + absolute/authority-form request lines.\n // Access log: method + path + status only. NEVER headers (no token/secret).\n const trace = (status: number, note?: string): void =>\n log(`[relay] ${status} ${req.method} ${req.url ?? \"-\"}${note ? \" \" + note : \"\"}`);\n\n if (req.method === \"CONNECT\" || !req.url || !req.url.startsWith(\"/\")) {\n trace(400, \"bad-form\");\n return send(res, 400, { error: \"origin-form request required\" });\n }\n // Structural SSRF guard: resolve against the constant upstream and require the\n // origin to stay put. Catches `//evil.com` (protocol-relative) and absolute URIs.\n let target: URL;\n try {\n target = new URL(req.url, upstream);\n } catch {\n trace(400, \"bad-path\");\n return send(res, 400, { error: \"bad request path\" });\n }\n if (target.origin !== upstreamOrigin) {\n trace(400, \"ssrf\");\n return send(res, 400, { error: \"path escapes upstream\" });\n }\n\n // 2. Secret gate — before ANY upstream I/O.\n if (req.headers[SECRET_HEADER_LC] !== opts.secret) {\n trace(403, \"secret\");\n return send(res, 403, { error: \"relay secret missing or invalid\" });\n }\n\n // 3. Buffer body (CF API payloads are small → avoids duplex streaming).\n const hasBody = req.method !== \"GET\" && req.method !== \"HEAD\";\n let body: Buffer | undefined;\n if (hasBody) {\n const chunks: Buffer[] = [];\n for await (const c of req) chunks.push(c as Buffer);\n body = chunks.length ? Buffer.concat(chunks) : undefined;\n }\n\n // 4. Forward headers minus hop-by-hop + secret. `fetch` sets Host from the URL.\n const headers: Record<string, string> = {};\n for (const [k, v] of Object.entries(req.headers)) {\n if (v === undefined) continue;\n const lk = k.toLowerCase();\n if (HOP_BY_HOP.has(lk) || lk === SECRET_HEADER_LC) continue;\n headers[k] = Array.isArray(v) ? v.join(\", \") : v;\n }\n\n let up: Response;\n try {\n up = await fetch(target.href, { method: req.method, headers, body, redirect: \"manual\" });\n } catch {\n trace(502, \"upstream-error\");\n return send(res, 502, { error: \"relay upstream unreachable\" });\n }\n\n // 5. Pass status + content-type + body through. Forward retry-after too, so\n // the client's rate-limit backoff honors the upstream through the relay.\n trace(up.status);\n const outHeaders: Record<string, string> = {\n \"content-type\": up.headers.get(\"content-type\") ?? \"application/json\",\n };\n const retryAfter = up.headers.get(\"retry-after\");\n if (retryAfter) outHeaders[\"retry-after\"] = retryAfter;\n res.writeHead(up.status, outHeaders);\n res.end(Buffer.from(await up.arrayBuffer()));\n }\n\n return new Promise<ProxyHandle>((resolve, reject) => {\n server.once(\"error\", reject);\n server.listen(0, \"127.0.0.1\", () => {\n const port = (server.address() as AddressInfo).port;\n resolve({\n port,\n url: `http://127.0.0.1:${port}`,\n close: () => new Promise<void>((res) => server.close(() => res())),\n });\n });\n });\n}\n"],"mappings":"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","names":["pc","existsSync","writeFileSync","join","join","join","assertSupported","install","label","state","uninstall","execFileSync","existsSync","writeFileSync","dirname","join","os","label","join","os","dirname","execFileSync","assertSupported","install","writeFileSync","uninstall","state","existsSync","assertSupported","install","label","state","uninstall","execFileSync","writeFileSync","tmpdir","join","label","xml","execFileSync","assertSupported","install","join","tmpdir","writeFileSync","uninstall","state","join","legacyUnitExists","removeLegacyUnit","existsSync","legacyUnitExists","writeFileSync","removeLegacyUnit","execFileSync","execFileSync","listZones","listZones","clack","execFileSync","existsSync","readFileSync","writeFileSync","join","execFileSync","join","readFileSync","existsSync","writeFileSync","join","clack","execFileSync","spawn","existsSync","readFileSync","renameSync","writeFileSync","os","readFileSync","os","writeFileSync","renameSync","existsSync","spawn","execFileSync","sleep","randomInt","pick","randomInt","label","readFileSync","writeFileSync","writeFileSync","readFileSync","tunnelIdFromCname","listZones","join","lines","existsSync","openSync","readFileSync","readFileSync","openSync","existsSync","openSync","spawn","join","note","fqdnFor","join","openSync","spawn","secret","require","pc"]}
|
package/package.json
CHANGED