@iamken/cloudtunnel 0.4.0 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -2,15 +2,16 @@
2
2
  import {
3
3
  listZones,
4
4
  resolveZone
5
- } from "./chunk-2TCFCMJS.js";
5
+ } from "./chunk-CWJA4L7J.js";
6
6
  import {
7
7
  createCname,
8
8
  deleteDnsRecord,
9
9
  findCname,
10
10
  isManagedDns
11
- } from "./chunk-YLTQB4F7.js";
11
+ } from "./chunk-NZKGIDIW.js";
12
12
  import {
13
13
  CliError,
14
+ __export,
14
15
  binDir,
15
16
  cfPaginate,
16
17
  cfRequest,
@@ -24,7 +25,7 @@ import {
24
25
  reportError,
25
26
  resolveCf,
26
27
  saveConfig
27
- } from "./chunk-UPBVRXLF.js";
28
+ } from "./chunk-RWR6VXNB.js";
28
29
 
29
30
  // src/index.ts
30
31
  import { Command } from "commander";
@@ -32,7 +33,7 @@ import { createRequire } from "module";
32
33
  import pc2 from "picocolors";
33
34
 
34
35
  // src/config/legacy-migrate.ts
35
- import { existsSync as existsSync2, readFileSync, renameSync, writeFileSync as writeFileSync2 } from "fs";
36
+ import { existsSync as existsSync3, readFileSync, renameSync, writeFileSync as writeFileSync4 } from "fs";
36
37
 
37
38
  // src/ui/output.ts
38
39
  import pc from "picocolors";
@@ -66,10 +67,10 @@ function printTable(head, rows) {
66
67
  for (const row of rows) table.push(row);
67
68
  console.log(table.toString());
68
69
  }
69
- async function selectOne(message, items, label) {
70
+ async function selectOne(message, items, label4) {
70
71
  const value = await select({
71
72
  message,
72
- options: items.map((item, i) => ({ value: String(i), label: label(item) }))
73
+ options: items.map((item, i) => ({ value: String(i), label: label4(item) }))
73
74
  });
74
75
  if (isCancel(value)) {
75
76
  cancel("Cancelled.");
@@ -78,11 +79,10 @@ async function selectOne(message, items, label) {
78
79
  return items[Number(value)];
79
80
  }
80
81
 
81
- // src/core/systemd.ts
82
- import { execFileSync } from "child_process";
83
- import { existsSync, realpathSync, writeFileSync } from "fs";
84
- import os, { tmpdir } from "os";
85
- import { dirname, join } from "path";
82
+ // src/core/service-exec.ts
83
+ import { realpathSync } from "fs";
84
+ import os from "os";
85
+ import { join } from "path";
86
86
 
87
87
  // src/core/ingress.ts
88
88
  var HOSTNAME_RE = /^[a-zA-Z0-9.-]+$/;
@@ -157,29 +157,74 @@ function formatTunnelSpec(s) {
157
157
  return `${s.subdomain}:${s.port}${s.host ? `@${s.host}` : ""}`;
158
158
  }
159
159
 
160
- // src/core/systemd.ts
161
- function serviceName(fqdn) {
162
- return `cloudtunnel-${fqdn.replace(/[^a-zA-Z0-9]+/g, "-")}.service`;
160
+ // src/core/service-exec.ts
161
+ var fqdnFor = (subdomain, zone) => subdomain === "@" ? zone : `${subdomain}.${zone}`;
162
+ var serviceSlug = (fqdn) => fqdn.replace(/[^a-zA-Z0-9]+/g, "-");
163
+ function buildUpArgs(p) {
164
+ const spec = formatTunnelSpec({ subdomain: p.subdomain, port: p.port, host: p.host });
165
+ return [
166
+ "up",
167
+ spec,
168
+ "-d",
169
+ p.zone,
170
+ ...p.proto === "https" ? ["--proto", "https"] : [],
171
+ ...p.protocol ? ["--protocol", p.protocol] : [],
172
+ "-f",
173
+ "-y"
174
+ ];
163
175
  }
164
- function unitPath(fqdn) {
165
- return `/etc/systemd/system/${serviceName(fqdn)}`;
176
+ function entryScript() {
177
+ const p = process.argv[1];
178
+ if (!p) throw new CliError("Cannot resolve the cloudtunnel executable path.");
179
+ return realpathSync(p);
166
180
  }
167
- function buildUnit(p) {
168
- const nodeBin = dirname(p.nodePath);
169
- const proto = p.proto === "https" ? " --proto https" : "";
170
- const protocol = p.protocol ? ` --protocol ${p.protocol}` : "";
181
+ function describeService(p) {
182
+ const fqdn = fqdnFor(p.subdomain, p.zone);
183
+ const slug = serviceSlug(fqdn);
184
+ return {
185
+ fqdn,
186
+ slug,
187
+ argv: buildUpArgs(p),
188
+ nodePath: process.execPath,
189
+ scriptPath: entryScript(),
190
+ user: os.userInfo().username,
191
+ home: os.homedir(),
192
+ logFile: join(logDir, `${slug}.service.log`)
193
+ };
194
+ }
195
+
196
+ // src/core/service-systemd.ts
197
+ var service_systemd_exports = {};
198
+ __export(service_systemd_exports, {
199
+ assertSupported: () => assertSupported,
200
+ buildUnit: () => buildUnit,
201
+ install: () => install,
202
+ label: () => label,
203
+ legacyUnitExists: () => legacyUnitExists,
204
+ removeLegacyUnit: () => removeLegacyUnit,
205
+ state: () => state,
206
+ uninstall: () => uninstall
207
+ });
208
+ import { execFileSync } from "child_process";
209
+ import { existsSync, writeFileSync } from "fs";
210
+ import { tmpdir } from "os";
211
+ import { dirname, join as join2 } from "path";
212
+ var label = (fqdn) => `cloudtunnel-${serviceSlug(fqdn)}.service`;
213
+ var unitPath = (fqdn) => `/etc/systemd/system/${label(fqdn)}`;
214
+ function buildUnit(d) {
215
+ const nodeBin = dirname(d.nodePath);
171
216
  return [
172
217
  "[Unit]",
173
- `Description=cloudtunnel ${p.fqdn} (Cloudflare Tunnel)`,
218
+ `Description=cloudtunnel ${d.fqdn} (Cloudflare Tunnel)`,
174
219
  "After=network-online.target",
175
220
  "Wants=network-online.target",
176
221
  "",
177
222
  "[Service]",
178
223
  "Type=simple",
179
- `User=${p.user}`,
180
- `Environment=HOME=${p.home}`,
224
+ `User=${d.user}`,
225
+ `Environment=HOME=${d.home}`,
181
226
  `Environment=PATH=${nodeBin}:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin`,
182
- `ExecStart=${p.nodePath} ${p.scriptPath} up ${p.spec} -d ${p.zone}${proto}${protocol} -f -y`,
227
+ `ExecStart=${d.nodePath} ${d.scriptPath} ${d.argv.join(" ")}`,
183
228
  "Restart=on-failure",
184
229
  "RestartSec=5",
185
230
  "",
@@ -188,18 +233,6 @@ function buildUnit(p) {
188
233
  ""
189
234
  ].join("\n");
190
235
  }
191
- function assertSystemd() {
192
- if (process.platform !== "linux") {
193
- throw new CliError("Service registration is Linux/systemd only.", {
194
- hint: "on macOS/Windows run `cloudtunnel up <spec> --detach` at login instead"
195
- });
196
- }
197
- try {
198
- execFileSync("systemctl", ["--version"], { stdio: "ignore" });
199
- } catch {
200
- throw new CliError("systemd (systemctl) was not found on this host.");
201
- }
202
- }
203
236
  function privileged(args) {
204
237
  const isRoot = typeof process.getuid === "function" && process.getuid() === 0;
205
238
  const argv = isRoot ? args : ["sudo", ...args];
@@ -213,40 +246,37 @@ function query(args) {
213
246
  return out ? out.toString().trim() : "";
214
247
  }
215
248
  }
216
- function entryScript() {
217
- const p = process.argv[1];
218
- if (!p) throw new CliError("Cannot resolve the cloudtunnel executable path.");
219
- return realpathSync(p);
249
+ function assertSupported() {
250
+ try {
251
+ execFileSync("systemctl", ["--version"], { stdio: "ignore" });
252
+ } catch {
253
+ throw new CliError("systemd (systemctl) was not found on this host.");
254
+ }
220
255
  }
221
- function installServiceForSpec(params) {
222
- assertSystemd();
223
- const fqdn = params.subdomain === "@" ? params.zone : `${params.subdomain}.${params.zone}`;
224
- const unit = buildUnit({
225
- fqdn,
226
- spec: formatTunnelSpec({ subdomain: params.subdomain, port: params.port, host: params.host }),
227
- zone: params.zone,
228
- proto: params.proto,
229
- user: os.userInfo().username,
230
- home: os.homedir(),
231
- nodePath: process.execPath,
232
- scriptPath: entryScript(),
233
- protocol: params.protocol
234
- });
235
- const tmp = join(tmpdir(), serviceName(fqdn));
236
- writeFileSync(tmp, unit, { mode: 420 });
237
- privileged(["install", "-m", "0644", tmp, unitPath(fqdn)]);
256
+ function install(d) {
257
+ assertSupported();
258
+ const tmp = join2(tmpdir(), label(d.fqdn));
259
+ writeFileSync(tmp, buildUnit(d), { mode: 420 });
260
+ privileged(["install", "-m", "0644", tmp, unitPath(d.fqdn)]);
238
261
  privileged(["systemctl", "daemon-reload"]);
239
- privileged(["systemctl", "enable", "--now", serviceName(fqdn)]);
262
+ privileged(["systemctl", "enable", "--now", label(d.fqdn)]);
240
263
  }
241
- function uninstallService(fqdn) {
242
- assertSystemd();
264
+ function uninstall(fqdn) {
243
265
  try {
244
- privileged(["systemctl", "disable", "--now", serviceName(fqdn)]);
266
+ privileged(["systemctl", "disable", "--now", label(fqdn)]);
245
267
  } catch {
246
268
  }
247
269
  privileged(["rm", "-f", unitPath(fqdn)]);
248
270
  privileged(["systemctl", "daemon-reload"]);
249
271
  }
272
+ function state(fqdn) {
273
+ const name = label(fqdn);
274
+ if (query(["is-active", name]) === "active") return "active";
275
+ const enabled = query(["is-enabled", name]);
276
+ if (enabled === "enabled" || enabled === "enabled-runtime") return "enabled";
277
+ if (enabled === "disabled" || enabled === "static") return "disabled";
278
+ return "none";
279
+ }
250
280
  function legacyUnitExists(profile) {
251
281
  return existsSync(`/etc/systemd/system/cloudtunnel-${profile}.service`);
252
282
  }
@@ -259,27 +289,207 @@ function removeLegacyUnit(profile) {
259
289
  privileged(["rm", "-f", `/etc/systemd/system/${name}`]);
260
290
  privileged(["systemctl", "daemon-reload"]);
261
291
  }
292
+
293
+ // src/core/service-launchd.ts
294
+ var service_launchd_exports = {};
295
+ __export(service_launchd_exports, {
296
+ assertSupported: () => assertSupported2,
297
+ buildPlist: () => buildPlist,
298
+ install: () => install2,
299
+ label: () => label2,
300
+ state: () => state2,
301
+ uninstall: () => uninstall2
302
+ });
303
+ import { execFileSync as execFileSync2 } from "child_process";
304
+ import { existsSync as existsSync2, mkdirSync, rmSync, writeFileSync as writeFileSync2 } from "fs";
305
+ import { dirname as dirname2, join as join3 } from "path";
306
+ import os2 from "os";
307
+ var label2 = (fqdn) => `com.cloudtunnel.${serviceSlug(fqdn)}`;
308
+ var agentsDir = () => join3(os2.homedir(), "Library", "LaunchAgents");
309
+ var plistPath = (fqdn) => join3(agentsDir(), `${label2(fqdn)}.plist`);
310
+ var xml = (s) => s.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
311
+ function buildPlist(d) {
312
+ const args = [d.nodePath, d.scriptPath, ...d.argv].map((a) => ` <string>${xml(a)}</string>`).join("\n");
313
+ const nodeBin = dirname2(d.nodePath);
314
+ const path = `${nodeBin}:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin`;
315
+ return [
316
+ '<?xml version="1.0" encoding="UTF-8"?>',
317
+ '<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">',
318
+ '<plist version="1.0">',
319
+ "<dict>",
320
+ ` <key>Label</key><string>${xml(label2(d.fqdn))}</string>`,
321
+ " <key>ProgramArguments</key>",
322
+ " <array>",
323
+ args,
324
+ " </array>",
325
+ " <key>RunAtLoad</key><true/>",
326
+ " <key>KeepAlive</key><true/>",
327
+ " <key>EnvironmentVariables</key>",
328
+ " <dict>",
329
+ ` <key>PATH</key><string>${xml(path)}</string>`,
330
+ ` <key>HOME</key><string>${xml(d.home)}</string>`,
331
+ " </dict>",
332
+ ` <key>StandardOutPath</key><string>${xml(d.logFile)}</string>`,
333
+ ` <key>StandardErrorPath</key><string>${xml(d.logFile)}</string>`,
334
+ "</dict>",
335
+ "</plist>",
336
+ ""
337
+ ].join("\n");
338
+ }
339
+ function launchctl(args) {
340
+ try {
341
+ return execFileSync2("launchctl", args, { stdio: ["ignore", "pipe", "ignore"], encoding: "utf8" });
342
+ } catch (err) {
343
+ const out = err.stdout;
344
+ return out ? out.toString() : "";
345
+ }
346
+ }
347
+ function assertSupported2() {
348
+ }
349
+ function install2(d) {
350
+ ensureDirs();
351
+ mkdirSync(agentsDir(), { recursive: true });
352
+ const plist = plistPath(d.fqdn);
353
+ writeFileSync2(plist, buildPlist(d), { mode: 420 });
354
+ launchctl(["unload", "-w", plist]);
355
+ execFileSync2("launchctl", ["load", "-w", plist], { stdio: "inherit" });
356
+ }
357
+ function uninstall2(fqdn) {
358
+ const plist = plistPath(fqdn);
359
+ launchctl(["unload", "-w", plist]);
360
+ rmSync(plist, { force: true });
361
+ }
362
+ function state2(fqdn) {
363
+ const info = launchctl(["list", label2(fqdn)]);
364
+ if (/"PID"\s*=/.test(info)) return "active";
365
+ return existsSync2(plistPath(fqdn)) ? "enabled" : "none";
366
+ }
367
+
368
+ // src/core/service-windows.ts
369
+ var service_windows_exports = {};
370
+ __export(service_windows_exports, {
371
+ assertSupported: () => assertSupported3,
372
+ buildTaskXml: () => buildTaskXml,
373
+ install: () => install3,
374
+ label: () => label3,
375
+ state: () => state3,
376
+ uninstall: () => uninstall3
377
+ });
378
+ import { execFileSync as execFileSync3 } from "child_process";
379
+ import { writeFileSync as writeFileSync3 } from "fs";
380
+ import { tmpdir as tmpdir2 } from "os";
381
+ import { join as join4 } from "path";
382
+ var label3 = (fqdn) => `cloudtunnel\\${serviceSlug(fqdn)}`;
383
+ var xml2 = (s) => s.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
384
+ function buildTaskXml(d) {
385
+ const args = `"${d.scriptPath}" ${d.argv.join(" ")}`;
386
+ return [
387
+ '<?xml version="1.0" encoding="UTF-16"?>',
388
+ '<Task version="1.2" xmlns="http://schemas.microsoft.com/windows/2004/02/mit/task">',
389
+ ` <RegistrationInfo><Description>cloudtunnel ${xml2(d.fqdn)} (Cloudflare Tunnel)</Description></RegistrationInfo>`,
390
+ ` <Triggers><LogonTrigger><Enabled>true</Enabled><UserId>${xml2(d.user)}</UserId></LogonTrigger></Triggers>`,
391
+ ` <Principals><Principal id="Author"><UserId>${xml2(d.user)}</UserId><LogonType>InteractiveToken</LogonType><RunLevel>LeastPrivilege</RunLevel></Principal></Principals>`,
392
+ " <Settings>",
393
+ " <MultipleInstancesPolicy>IgnoreNew</MultipleInstancesPolicy>",
394
+ " <DisallowStartIfOnBatteries>false</DisallowStartIfOnBatteries>",
395
+ " <StopIfGoingOnBatteries>false</StopIfGoingOnBatteries>",
396
+ " <StartWhenAvailable>true</StartWhenAvailable>",
397
+ " <ExecutionTimeLimit>PT0S</ExecutionTimeLimit>",
398
+ " <RestartOnFailure><Interval>PT1M</Interval><Count>3</Count></RestartOnFailure>",
399
+ " <Enabled>true</Enabled>",
400
+ " </Settings>",
401
+ ' <Actions Context="Author">',
402
+ ` <Exec><Command>${xml2(d.nodePath)}</Command><Arguments>${xml2(args)}</Arguments></Exec>`,
403
+ " </Actions>",
404
+ "</Task>",
405
+ ""
406
+ ].join("\r\n");
407
+ }
408
+ function schtasks(args) {
409
+ try {
410
+ return execFileSync3("schtasks", args, { stdio: ["ignore", "pipe", "ignore"], encoding: "utf8" });
411
+ } catch (err) {
412
+ const out = err.stdout;
413
+ return out ? out.toString() : "";
414
+ }
415
+ }
416
+ function assertSupported3() {
417
+ }
418
+ function install3(d) {
419
+ const file = join4(tmpdir2(), `${d.slug}.task.xml`);
420
+ writeFileSync3(file, "\uFEFF" + buildTaskXml(d), { encoding: "utf16le" });
421
+ execFileSync3("schtasks", ["/Create", "/TN", label3(d.fqdn), "/XML", file, "/F"], { stdio: "inherit" });
422
+ schtasks(["/Run", "/TN", label3(d.fqdn)]);
423
+ }
424
+ function uninstall3(fqdn) {
425
+ schtasks(["/Delete", "/TN", label3(fqdn), "/F"]);
426
+ }
427
+ function state3(fqdn) {
428
+ const out = schtasks(["/Query", "/TN", label3(fqdn), "/FO", "LIST"]);
429
+ if (!out) return "none";
430
+ if (/\bRunning\b/.test(out)) return "active";
431
+ if (/\bDisabled\b/.test(out)) return "disabled";
432
+ if (/\bReady\b/.test(out)) return "enabled";
433
+ return "enabled";
434
+ }
435
+
436
+ // src/core/service.ts
437
+ function pick() {
438
+ switch (process.platform) {
439
+ case "linux":
440
+ return service_systemd_exports;
441
+ case "darwin":
442
+ return service_launchd_exports;
443
+ case "win32":
444
+ return service_windows_exports;
445
+ default:
446
+ return null;
447
+ }
448
+ }
449
+ function required() {
450
+ const b = pick();
451
+ if (!b) {
452
+ throw new CliError(`Boot services aren't supported on ${process.platform}.`, {
453
+ hint: "run `cloudtunnel up <spec> --detach` and use your OS's own autostart"
454
+ });
455
+ }
456
+ return b;
457
+ }
458
+ function assertServiceSupported() {
459
+ required().assertSupported();
460
+ }
461
+ function serviceName(fqdn) {
462
+ return pick()?.label(fqdn) ?? `cloudtunnel-${fqdn}`;
463
+ }
464
+ function installServiceForSpec(params) {
465
+ const b = required();
466
+ b.assertSupported();
467
+ b.install(describeService(params));
468
+ }
469
+ function uninstallService(fqdn) {
470
+ pick()?.uninstall(fqdn);
471
+ }
262
472
  function serviceState(fqdn) {
263
- if (process.platform !== "linux") return "none";
264
- const name = serviceName(fqdn);
265
- if (query(["is-active", name]) === "active") return "active";
266
- const enabled = query(["is-enabled", name]);
267
- if (enabled === "enabled" || enabled === "enabled-runtime") return "enabled";
268
- if (enabled === "disabled" || enabled === "static") return "disabled";
269
- return "none";
473
+ return pick()?.state(fqdn) ?? "none";
474
+ }
475
+ function legacyUnitExists2(profile) {
476
+ return process.platform === "linux" ? legacyUnitExists(profile) : false;
477
+ }
478
+ function removeLegacyUnit2(profile) {
479
+ if (process.platform === "linux") removeLegacyUnit(profile);
270
480
  }
271
481
 
272
482
  // src/config/legacy-migrate.ts
273
483
  var skipMarker = `${profilesFile}.migrate-skip`;
274
484
  async function migrateLegacyProfiles() {
275
- if (!existsSync2(profilesFile) || existsSync2(skipMarker)) return;
485
+ if (!existsSync3(profilesFile) || existsSync3(skipMarker)) return;
276
486
  let profiles;
277
487
  try {
278
488
  profiles = JSON.parse(readFileSync(profilesFile, "utf8"));
279
489
  } catch {
280
490
  return;
281
491
  }
282
- const legacy = Object.entries(profiles).filter(([name]) => legacyUnitExists(name));
492
+ const legacy = Object.entries(profiles).filter(([name]) => legacyUnitExists2(name));
283
493
  if (legacy.length === 0) {
284
494
  try {
285
495
  renameSync(profilesFile, `${profilesFile}.migrated`);
@@ -289,7 +499,7 @@ async function migrateLegacyProfiles() {
289
499
  }
290
500
  const ok = await confirm(`Found ${legacy.length} boot service(s) from an older cloudtunnel. Migrate them now? (needs sudo)`);
291
501
  if (!ok) {
292
- writeFileSync2(skipMarker, "");
502
+ writeFileSync4(skipMarker, "");
293
503
  say.dim(` Skipped. Delete ${skipMarker} to be asked again.`);
294
504
  return;
295
505
  }
@@ -309,12 +519,12 @@ async function migrateLegacyProfiles() {
309
519
  });
310
520
  migrated++;
311
521
  }
312
- removeLegacyUnit(name);
522
+ removeLegacyUnit2(name);
313
523
  }
314
524
  renameSync(profilesFile, `${profilesFile}.migrated`);
315
525
  say.ok(`Migrated ${migrated} boot service(s). See them with: cloudtunnel ls`);
316
526
  } catch (err) {
317
- writeFileSync2(skipMarker, "");
527
+ writeFileSync4(skipMarker, "");
318
528
  say.warn(`Migration incomplete: ${err.message}. Won't retry automatically (delete ${skipMarker} to retry).`);
319
529
  }
320
530
  }
@@ -478,10 +688,10 @@ async function ensureAuth() {
478
688
  }
479
689
 
480
690
  // src/connector/binary.ts
481
- import { execFileSync as execFileSync2 } from "child_process";
691
+ import { execFileSync as execFileSync4 } from "child_process";
482
692
  import { createHash } from "crypto";
483
- import { chmodSync, existsSync as existsSync3, readFileSync as readFileSync2, writeFileSync as writeFileSync3 } from "fs";
484
- import { join as join2 } from "path";
693
+ import { chmodSync, existsSync as existsSync4, readFileSync as readFileSync2, writeFileSync as writeFileSync5 } from "fs";
694
+ import { join as join5 } from "path";
485
695
  var PINNED_VERSION = "2025.1.0";
486
696
  var RELEASE_BASE = `https://github.com/cloudflare/cloudflared/releases/download/${PINNED_VERSION}`;
487
697
  var ASSETS = {
@@ -493,14 +703,14 @@ var ASSETS = {
493
703
  };
494
704
  function binaryWorks(bin) {
495
705
  try {
496
- execFileSync2(bin, ["--version"], { stdio: "ignore" });
706
+ execFileSync4(bin, ["--version"], { stdio: "ignore" });
497
707
  return true;
498
708
  } catch {
499
709
  return false;
500
710
  }
501
711
  }
502
712
  function cachedPath() {
503
- return join2(binDir, process.platform === "win32" ? "cloudflared.exe" : "cloudflared");
713
+ return join5(binDir, process.platform === "win32" ? "cloudflared.exe" : "cloudflared");
504
714
  }
505
715
  function isMusl() {
506
716
  try {
@@ -512,7 +722,7 @@ function isMusl() {
512
722
  async function ensureCloudflared() {
513
723
  if (binaryWorks("cloudflared")) return "cloudflared";
514
724
  const cached = cachedPath();
515
- if (existsSync3(cached) && binaryWorks(cached)) return cached;
725
+ if (existsSync4(cached) && binaryWorks(cached)) return cached;
516
726
  return downloadCloudflared(cached);
517
727
  }
518
728
  async function downloadCloudflared(dest) {
@@ -540,7 +750,7 @@ async function downloadCloudflared(dest) {
540
750
  }
541
751
  ensureDirs();
542
752
  const binary = asset.archive ? extractTgz(bytes) : bytes;
543
- writeFileSync3(dest, binary, { mode: 493 });
753
+ writeFileSync5(dest, binary, { mode: 493 });
544
754
  chmodSync(dest, 493);
545
755
  if (!binaryWorks(dest)) throw new CliError("Downloaded cloudflared is not runnable.");
546
756
  return dest;
@@ -552,17 +762,17 @@ function extractTgz(_bytes) {
552
762
  }
553
763
 
554
764
  // src/core/up-runner.ts
555
- import { join as join3 } from "path";
765
+ import { join as join6 } from "path";
556
766
  import * as clack2 from "@clack/prompts";
557
767
 
558
768
  // src/connector/process.ts
559
- import { execFileSync as execFileSync3, spawn as spawn2 } from "child_process";
769
+ import { execFileSync as execFileSync5, spawn as spawn2 } from "child_process";
560
770
  import { openSync } from "fs";
561
771
 
562
772
  // src/connector/registry.ts
563
- import { existsSync as existsSync4, readFileSync as readFileSync3, renameSync as renameSync2, writeFileSync as writeFileSync4 } from "fs";
773
+ import { existsSync as existsSync5, readFileSync as readFileSync3, renameSync as renameSync2, writeFileSync as writeFileSync6 } from "fs";
564
774
  import { readFile } from "fs/promises";
565
- import os2 from "os";
775
+ import os3 from "os";
566
776
  import lockfile from "proper-lockfile";
567
777
  function entryFqdn(e) {
568
778
  return e.subdomain === "@" ? e.zone : `${e.subdomain}.${e.zone}`;
@@ -571,8 +781,8 @@ function currentBootId() {
571
781
  try {
572
782
  return readFileSync3("/proc/sys/kernel/random/boot_id", "utf8").trim();
573
783
  } catch {
574
- const bootMinute = Math.floor((Date.now() - os2.uptime() * 1e3) / 6e4);
575
- return `boot-${bootMinute}-${os2.hostname()}`;
784
+ const bootMinute = Math.floor((Date.now() - os3.uptime() * 1e3) / 6e4);
785
+ return `boot-${bootMinute}-${os3.hostname()}`;
576
786
  }
577
787
  }
578
788
  function readRegistry() {
@@ -585,12 +795,12 @@ function readRegistry() {
585
795
  function writeRegistry(reg) {
586
796
  ensureDirs();
587
797
  const tmp = `${registryFile}.tmp`;
588
- writeFileSync4(tmp, JSON.stringify(reg, null, 2), { mode: 384 });
798
+ writeFileSync6(tmp, JSON.stringify(reg, null, 2), { mode: 384 });
589
799
  renameSync2(tmp, registryFile);
590
800
  }
591
801
  async function mutateRegistry(fn) {
592
802
  ensureDirs();
593
- if (!existsSync4(registryFile)) writeFileSync4(registryFile, "{}", { mode: 384 });
803
+ if (!existsSync5(registryFile)) writeFileSync6(registryFile, "{}", { mode: 384 });
594
804
  const release = await lockfile.lock(registryFile, { retries: { retries: 10, minTimeout: 50 } });
595
805
  try {
596
806
  const reg = readRegistry();
@@ -698,7 +908,7 @@ async function stopConnector(entry) {
698
908
  const pid = entry.pid;
699
909
  if (process.platform === "win32") {
700
910
  try {
701
- execFileSync3("taskkill", ["/pid", String(pid), "/T", "/F"], { stdio: "ignore" });
911
+ execFileSync5("taskkill", ["/pid", String(pid), "/T", "/F"], { stdio: "ignore" });
702
912
  } catch {
703
913
  return false;
704
914
  }
@@ -829,10 +1039,10 @@ var NOUNS = [
829
1039
  "tiger",
830
1040
  "walnut"
831
1041
  ];
832
- var pick = (arr) => arr[randomInt(arr.length)];
1042
+ var pick2 = (arr) => arr[randomInt(arr.length)];
833
1043
  function randomSlug() {
834
1044
  const suffix = randomInt(65536).toString(16).padStart(4, "0");
835
- return `${pick(ADJECTIVES)}-${pick(NOUNS)}-${suffix}`;
1045
+ return `${pick2(ADJECTIVES)}-${pick2(NOUNS)}-${suffix}`;
836
1046
  }
837
1047
  function resolveHostSpec(opts, defaultZone) {
838
1048
  if (opts.hostname) {
@@ -889,8 +1099,8 @@ async function createTunnelSubdomain(cf, opts) {
889
1099
  let dnsRecordId;
890
1100
  try {
891
1101
  const suffix = randomInt2(65536).toString(16).padStart(4, "0");
892
- const label = host.subdomain === "@" ? "root" : host.subdomain;
893
- const tunnel = await createTunnel(cf, `${MANAGED_TUNNEL_PREFIX}${label}-${suffix}`);
1102
+ const label4 = host.subdomain === "@" ? "root" : host.subdomain;
1103
+ const tunnel = await createTunnel(cf, `${MANAGED_TUNNEL_PREFIX}${label4}-${suffix}`);
894
1104
  tunnelId = tunnel.id;
895
1105
  const token = await getTunnelToken(cf, tunnelId);
896
1106
  await putIngress(cf, tunnelId, buildIngress({ hostname: host.hostname, port: opts.port, proto: opts.proto, host: opts.host }));
@@ -1037,8 +1247,8 @@ async function listAll(cf, opts = {}) {
1037
1247
  };
1038
1248
  });
1039
1249
  if (opts.all) {
1040
- const { listCargoCnames } = await import("./dns-PAPFSYFP.js");
1041
- const { listZones: listZones3 } = await import("./zones-YNGQYXAF.js");
1250
+ const { listCargoCnames } = await import("./dns-5OXFAQ4D.js");
1251
+ const { listZones: listZones3 } = await import("./zones-QYV3DSEY.js");
1042
1252
  const tracked = new Set(entries.map(entryFqdn));
1043
1253
  for (const zone of await listZones3(cf.token)) {
1044
1254
  for (const rec of await listCargoCnames(cf.token, zone.id)) {
@@ -1053,7 +1263,7 @@ async function listAll(cf, opts = {}) {
1053
1263
 
1054
1264
  // src/core/up-runner.ts
1055
1265
  function logFileFor(subdomain) {
1056
- return join3(logDir, `${subdomain === "@" ? "root" : subdomain}.log`);
1266
+ return join6(logDir, `${subdomain === "@" ? "root" : subdomain}.log`);
1057
1267
  }
1058
1268
  async function startTunnels(cf, bin, items, opts = {}) {
1059
1269
  const started = [];
@@ -1202,7 +1412,7 @@ async function runUp(specArgs, opts) {
1202
1412
  await startTunnels(cf, bin, items, { detach: opts.detach, protocol });
1203
1413
  }
1204
1414
  function registerServices(items, domain, proto, protocol) {
1205
- assertSystemd();
1415
+ assertServiceSupported();
1206
1416
  if (!protocol) {
1207
1417
  say.warn("No edge protocol set \u2014 cloudflared will pick QUIC, which some networks drop.");
1208
1418
  say.dim(" \u2192 add --protocol http2 for UDP-hostile networks");
@@ -1219,7 +1429,7 @@ function registerServices(items, domain, proto, protocol) {
1219
1429
  say.dim(" \u2192 check them: cloudtunnel ls \xB7 remove: cloudtunnel delete <#>");
1220
1430
  }
1221
1431
  function registerUp(program) {
1222
- program.command("up", { isDefault: true }).argument("[specs...]", "tunnels to start: [subdomain:]port[@host] (e.g. api:8080 web:8081@localhost)").description("Start one or more tunnels (also: `cloudtunnel 8080`)").option("-d, --domain <domain>", "domain for the subdomains (prompted from a list if unset)").option("--proto <proto>", "local service protocol: http | https", "http").option("--protocol <proto>", "cloudflared edge transport: auto | http2 | quic (http2 for UDP-hostile networks)").option("--detach", "run the connectors in the background").option("--service", "register each subdomain as a systemd boot service (Linux; needs sudo)").option("-f, --force", "replace a non-tunnel DNS record occupying the hostname").option("-y, --yes", "don't prompt; don't ask before replacing an existing record").action((specs, opts) => runUp(specs, opts));
1432
+ program.command("up", { isDefault: true }).argument("[specs...]", "tunnels to start: [subdomain:]port[@host] (e.g. api:8080 web:8081@localhost)").description("Start one or more tunnels (also: `cloudtunnel 8080`)").option("-d, --domain <domain>", "domain for the subdomains (prompted from a list if unset)").option("--proto <proto>", "local service protocol: http | https", "http").option("--protocol <proto>", "cloudflared edge transport: auto | http2 | quic (http2 for UDP-hostile networks)").option("--detach", "run the connectors in the background").option("--service", "register each subdomain as a boot service (Linux systemd \xB7 macOS launchd \xB7 Windows Task Scheduler)").option("-f, --force", "replace a non-tunnel DNS record occupying the hostname").option("-y, --yes", "don't prompt; don't ask before replacing an existing record").action((specs, opts) => runUp(specs, opts));
1223
1433
  }
1224
1434
 
1225
1435
  // src/commands/ls.ts
@@ -1241,14 +1451,12 @@ function registerLs(program) {
1241
1451
 
1242
1452
  // src/commands/delete.ts
1243
1453
  async function deleteOne(cf, fqdn, opts) {
1454
+ const hasService = serviceState(fqdn) !== "none";
1455
+ if (hasService && !opts.dryRun) uninstallService(fqdn);
1244
1456
  await removeTunnelSubdomain(cf, fqdn, { force: opts.force, dryRun: opts.dryRun });
1245
- if (serviceState(fqdn) === "none") return;
1246
- if (opts.dryRun) {
1247
- say.info(`Would also remove boot service ${serviceName(fqdn)}`);
1248
- return;
1249
- }
1250
- uninstallService(fqdn);
1251
- say.ok(`Removed boot service ${serviceName(fqdn)}`);
1457
+ if (!hasService) return;
1458
+ if (opts.dryRun) say.info(`Would also remove boot service ${serviceName(fqdn)}`);
1459
+ else say.ok(`Removed boot service ${serviceName(fqdn)}`);
1252
1460
  }
1253
1461
  function registerDelete(program) {
1254
1462
  program.command("delete").argument("[targets...]", "subdomains to remove by # / name / URL (omit with --all)").description("Release tunnel(s) \u2014 deletes the tunnel + DNS, and any systemd boot service").option("--all", "release every tracked subdomain").option("-f, --force", "release even a resource not created by cloudtunnel").option("--dry-run", "show what would be released without doing it").action(async (targets, opts) => {
@@ -1279,7 +1487,7 @@ function registerDelete(program) {
1279
1487
  }
1280
1488
 
1281
1489
  // src/commands/logs.ts
1282
- import { closeSync, existsSync as existsSync5, openSync as openSync2, readFileSync as readFileSync4, readSync, statSync, watch } from "fs";
1490
+ import { closeSync, existsSync as existsSync6, openSync as openSync2, readFileSync as readFileSync4, readSync, statSync, watch } from "fs";
1283
1491
  function printTail(file, n) {
1284
1492
  const lines = readFileSync4(file, "utf8").split("\n");
1285
1493
  const tail = lines.slice(-n).join("\n");
@@ -1313,7 +1521,7 @@ function follow(file, fromPos) {
1313
1521
  function registerLogs(program) {
1314
1522
  program.command("logs").argument("<target>", "subdomain name / hostname / id / #").description("Show the connector log for a subdomain (use -f to follow)").option("-f, --follow", "keep printing new log lines (like tail -f)").option("-n, --lines <n>", "number of lines to show", "50").action((name, opts) => {
1315
1523
  const { fqdn, entry } = resolveTarget(name);
1316
- if (!entry?.logFile || !existsSync5(entry.logFile)) {
1524
+ if (!entry?.logFile || !existsSync6(entry.logFile)) {
1317
1525
  throw new CliError(`No logs for ${fqdn} yet.`, { hint: "start it with `cloudtunnel up` or `cloudtunnel run`" });
1318
1526
  }
1319
1527
  const n = Math.max(1, Number(opts.lines) || 50);