@codions/portta 0.0.0-develop.155.sha-e6e3870 → 0.0.0-develop.159.sha-0da77d5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.js CHANGED
@@ -20141,6 +20141,26 @@ function isLoopbackAddress(address) {
20141
20141
  function isWildcardAddress(address) {
20142
20142
  return address === "0.0.0.0" || address === "::";
20143
20143
  }
20144
+ function isLoopbackIp(address) {
20145
+ return address === "::1" || /^127(\.\d{1,3}){3}$/.test(address);
20146
+ }
20147
+ function localhostDnsVerdict(addresses) {
20148
+ const hint = "see docs/product/guides/local-development.md#how-localhost-resolves";
20149
+ if (addresses.length === 0) return check2("dns.local", "warn", "local DNS", "could not confirm *.localhost resolution", hint);
20150
+ const stray = addresses.filter((address) => !isLoopbackIp(address));
20151
+ return stray.length === 0 ? check2("dns.local", "pass", "local DNS", "*.localhost resolves to loopback") : check2("dns.local", "warn", "local DNS", `*.localhost resolves to ${stray.join(", ")}, not loopback`, hint);
20152
+ }
20153
+ function loopbackDomainVerdict(domain2, resolved2, bind) {
20154
+ if (!isLoopbackIp(resolved2)) return null;
20155
+ const served = isWildcardAddress(bind) || bind === resolved2 || isLoopbackAddress(bind) && resolved2 === "127.0.0.1";
20156
+ return served ? check2("domain.reachable", "pass", "project hostnames", `*.${domain2} resolves to loopback, served on ${bind}`) : check2(
20157
+ "domain.reachable",
20158
+ "warn",
20159
+ "project hostnames",
20160
+ `*.${domain2} resolves to ${resolved2}, but Traefik listens on ${bind}`,
20161
+ "point the wildcard at an address Traefik listens on; see docs/product/guides/local-domains.md"
20162
+ );
20163
+ }
20144
20164
  function publishesSensitivePort(published) {
20145
20165
  return SENSITIVE_PORTS.some((port) => published.includes(`0.0.0.0:`) && published.includes(`->${port}`));
20146
20166
  }
@@ -22364,7 +22384,7 @@ var init_package = __esm({
22364
22384
  "package.json"() {
22365
22385
  package_default = {
22366
22386
  name: "@codions/portta",
22367
- version: "0.0.0-develop.155.sha-e6e3870",
22387
+ version: "0.0.0-develop.159.sha-0da77d5",
22368
22388
  description: "Machine-first CLI for a shared Docker development gateway",
22369
22389
  license: "MIT",
22370
22390
  type: "module",
@@ -22438,7 +22458,7 @@ var init_version = __esm({
22438
22458
  "use strict";
22439
22459
  init_package();
22440
22460
  CLI_VERSION = package_default.version;
22441
- CLI_BUILD_DATE = true ? "2026-09-16T16:54:30Z" : void 0;
22461
+ CLI_BUILD_DATE = true ? "2026-09-17T17:01:02Z" : void 0;
22442
22462
  }
22443
22463
  });
22444
22464
 
@@ -89799,7 +89819,7 @@ function createDaemonTaskflowHost(context) {
89799
89819
  port: hostListen(env).port,
89800
89820
  workflowBuiltinsDir: taskflowAssets(env).workflowBuiltinsDir,
89801
89821
  version: env.PORTTA_VERSION || "0.0.0-local",
89802
- buildDate: true ? "2026-09-16T16:54:30Z" : (/* @__PURE__ */ new Date()).toISOString(),
89822
+ buildDate: true ? "2026-09-17T17:01:02Z" : (/* @__PURE__ */ new Date()).toISOString(),
89803
89823
  environmentStore: createEnvironmentStore(paths3.database),
89804
89824
  hostEnvironmentProvider: new HostEnvironmentProvider(),
89805
89825
  devContainerProvider: new DevContainerProvider(),
@@ -100395,7 +100415,7 @@ var require_network = __commonJS({
100395
100415
  var exec3 = __require("child_process").exec;
100396
100416
  var execSync = __require("child_process").execSync;
100397
100417
  var execFileSync2 = __require("child_process").execFileSync;
100398
- var readFileSync38 = __require("fs").readFileSync;
100418
+ var readFileSync39 = __require("fs").readFileSync;
100399
100419
  var fs = __require("fs");
100400
100420
  var util2 = require_util2();
100401
100421
  var _platform = process.platform;
@@ -100883,7 +100903,7 @@ var require_network = __commonJS({
100883
100903
  return result;
100884
100904
  }
100885
100905
  try {
100886
- const content = readFileSync38(file2, { encoding: "utf8" });
100906
+ const content = readFileSync39(file2, { encoding: "utf8" });
100887
100907
  const lines = content.split("\n").filter((l) => /iface|source/.test(l));
100888
100908
  lines.forEach((line4) => {
100889
100909
  const parts2 = line4.replace(/\s+/g, " ").trim().split(" ");
@@ -120141,7 +120161,7 @@ async function runtimeAction(action, options, command) {
120141
120161
  init_src();
120142
120162
  init_src();
120143
120163
  init_context();
120144
- import { chmodSync as chmodSync9, existsSync as existsSync38, mkdirSync as mkdirSync18, readFileSync as readFileSync30, readdirSync as readdirSync8, rmSync as rmSync10 } from "node:fs";
120164
+ import { chmodSync as chmodSync9, existsSync as existsSync38, mkdirSync as mkdirSync18, readFileSync as readFileSync31, readdirSync as readdirSync8, rmSync as rmSync10 } from "node:fs";
120145
120165
  import { join as join57 } from "node:path";
120146
120166
  init_errors3();
120147
120167
  init_version();
@@ -120763,35 +120783,9 @@ async function runDoctor(context) {
120763
120783
  const socket = (proxy.mounts ?? []).find((mount) => mount.source.includes("docker.sock"));
120764
120784
  add(!socket ? check2("proxy.socket", "warn", "docker socket mount", "could not determine mount mode") : socket.readWrite ? check2("proxy.socket", "fail", "docker socket mount", "mounted read-write", "mount /var/run/docker.sock read-only (:ro)") : check2("proxy.socket", "pass", "docker socket mount", "read-only"));
120765
120785
  }
120766
- const database = gatewayContainer(containers, "db");
120767
- const dbNetwork = env["PORTTA_DB_NETWORK"] || `${config3.network}-data`;
120768
- if (database) {
120769
- add(database.state === "running" ? check2("db.state", "pass", "panel database", "running") : config3.webEnabled ? check2("db.state", "fail", "panel database", `state=${database.state}; the panel refuses to start without it`, "portta web up") : check2("db.state", "warn", "panel database", `state=${database.state}; the panel is off, so nothing needs it`, "portta web up"));
120770
- const dbPublished = publishedBindings(database);
120771
- add(dbPublished ? check2(
120772
- "db.exposure",
120773
- "fail",
120774
- "panel database exposure",
120775
- `publishes host ports: ${dbPublished}`,
120776
- "remove every ports entry from docker/compose/features/db.yaml and recreate the database container"
120777
- ) : check2("db.exposure", "pass", "panel database exposure", "no host ports published"));
120778
- add(database.networks.includes(config3.network) ? check2(
120779
- "db.network.shared",
120780
- "fail",
120781
- "panel database network",
120782
- `attached to the shared HTTP network '${config3.network}'`,
120783
- `detach it; the panel database belongs only on '${dbNetwork}'`
120784
- ) : check2("db.network.shared", "pass", "panel database network", "off the shared HTTP network"));
120785
- const data = await networkFacts(dbNetwork);
120786
- add(!data.exists ? check2("db.network.internal", "warn", "panel data network", "not created yet", "portta web up") : data.internal ? check2("db.network.internal", "pass", "panel data network", `${dbNetwork} (internal)`) : check2(
120787
- "db.network.internal",
120788
- "fail",
120789
- "panel data network",
120790
- `${dbNetwork} is not internal`,
120791
- "recreate the panel database network from docker/compose/features/db.yaml"
120792
- ));
120793
- } else if (config3.webEnabled) {
120794
- add(check2("db.state", "warn", "panel database", "container not created; the panel will refuse to start without it", "portta web up"));
120786
+ if (config3.webEnabled) {
120787
+ const databaseFile = databaseFileFor(root2);
120788
+ add(existsSync33(databaseFile) ? check2("db.file", "pass", "panel database", databaseFile) : check2("db.file", "warn", "panel database", `${databaseFile} not created yet; the panel creates it on first boot`, "portta web up"));
120795
120789
  }
120796
120790
  const panelIsPublic = (env["PORTTA_WEB_EXPOSE"] || "local") === "public";
120797
120791
  if (traefik && traefik.state === "running") {
@@ -121153,14 +121147,7 @@ async function domainChecks(config3, env) {
121153
121147
  const domain2 = config3.domain;
121154
121148
  const panelExpose = env["PORTTA_WEB_EXPOSE"] || "local";
121155
121149
  if (domain2 === "localhost" || domain2.endsWith(".localhost")) {
121156
- const probe = await resolveIpv4("portta-probe.localhost");
121157
- checks.push(probe.length > 0 ? check2("dns.local", "pass", "local DNS", "*.localhost resolves to loopback") : check2(
121158
- "dns.local",
121159
- "warn",
121160
- "local DNS",
121161
- "could not confirm *.localhost resolution",
121162
- "see docs/product/guides/local-development.md if hostnames do not resolve"
121163
- ));
121150
+ checks.push(localhostDnsVerdict(await resolveIpv4("portta-probe.localhost")));
121164
121151
  if (panelExpose !== "local") {
121165
121152
  checks.push(check2(
121166
121153
  "domain.reachable",
@@ -121176,6 +121163,8 @@ async function domainChecks(config3, env) {
121176
121163
  const resolved2 = (await resolveIpv4(`portta-probe.${domain2}`))[0];
121177
121164
  if (!resolved2) {
121178
121165
  checks.push(check2("dns.wildcard", "fail", "wildcard DNS", `*.${domain2} does not resolve`, "portta dns setup"));
121166
+ } else if (isLoopbackIp(resolved2)) {
121167
+ checks.push(check2("dns.wildcard", "pass", "wildcard DNS", `*.${domain2} -> ${resolved2} (this machine)`));
121179
121168
  } else {
121180
121169
  const expected = env["PORTTA_PUBLIC_IP"] ?? "";
121181
121170
  checks.push(!expected ? check2("dns.wildcard", "pass", "wildcard DNS", `*.${domain2} -> ${resolved2}`) : resolved2 === expected ? check2("dns.wildcard", "pass", "wildcard DNS", `*.${domain2} -> ${resolved2} (this host)`) : check2(
@@ -121187,7 +121176,19 @@ async function domainChecks(config3, env) {
121187
121176
  ));
121188
121177
  }
121189
121178
  const bind = config3.bindAddress;
121190
- if (isLoopbackAddress(bind)) {
121179
+ const loopback = resolved2 ? loopbackDomainVerdict(domain2, resolved2, bind) : null;
121180
+ if (loopback) {
121181
+ checks.push(loopback);
121182
+ if (panelExpose !== "local") {
121183
+ checks.push(check2(
121184
+ "domain.reachable.remote",
121185
+ "warn",
121186
+ "project hostnames",
121187
+ `*.${domain2} resolves to loopback, and the panel is reached from elsewhere`,
121188
+ "point the wildcard at this host, or portta config set domain.mode auto"
121189
+ ));
121190
+ }
121191
+ } else if (isLoopbackAddress(bind)) {
121191
121192
  const publicIp = env["PORTTA_PUBLIC_IP"] ?? "";
121192
121193
  const fix = publicIp && isPrivateAddress(publicIp) ? `portta config set gateway.bindAddress ${publicIp} serves them on that network only` : "portta public enable exposes the HTTP services that opted in";
121193
121194
  checks.push(check2(
@@ -121380,7 +121381,7 @@ async function reexecBuiltCli(root2) {
121380
121381
  init_src();
121381
121382
  init_context();
121382
121383
  init_errors3();
121383
- import { existsSync as existsSync37, readdirSync as readdirSync7 } from "node:fs";
121384
+ import { existsSync as existsSync37, readdirSync as readdirSync7, readFileSync as readFileSync30 } from "node:fs";
121384
121385
  import { basename as basename19, join as join56 } from "node:path";
121385
121386
  var PORTTA_DIRECTORY = ".portta";
121386
121387
  var OVERLAY_FILE = "compose.portta.yaml";
@@ -121388,6 +121389,7 @@ var COMPOSE_FILE = "compose.yaml";
121388
121389
  var PANEL_WAIT_MS2 = 12e4;
121389
121390
  var PANEL_POLL_MS2 = 500;
121390
121391
  var DEMO_DIRECTORY_PREFIX = "portta-demo-";
121392
+ var MANIFEST_FILE = "portta.example.json";
121391
121393
  function demoDirectories(home2) {
121392
121394
  if (!existsSync37(home2)) return [];
121393
121395
  return readdirSync7(home2, { withFileTypes: true }).filter((entry) => entry.isDirectory() && entry.name.startsWith(DEMO_DIRECTORY_PREFIX)).map((entry) => join56(home2, entry.name)).sort();
@@ -121410,6 +121412,48 @@ function demoComposeArgs(stack, action) {
121410
121412
  if (action === "up") return ["compose", ...files, "up", "-d"];
121411
121413
  return ["compose", ...files, "down", "-v"];
121412
121414
  }
121415
+ function readDemoProject(stackDir) {
121416
+ const path7 = join56(stackDir, PORTTA_DIRECTORY, MANIFEST_FILE);
121417
+ if (!existsSync37(path7)) return null;
121418
+ try {
121419
+ const manifest = JSON.parse(readFileSync30(path7, "utf8"));
121420
+ const project2 = manifest.project;
121421
+ if (typeof project2?.["slug"] !== "string" || typeof project2["name"] !== "string") return null;
121422
+ const repositories = (Array.isArray(manifest.repositories) ? manifest.repositories : []).filter((repository) => typeof repository?.["name"] === "string" && typeof repository["relativePath"] === "string").map((repository) => ({
121423
+ name: repository["name"],
121424
+ role: typeof repository["role"] === "string" ? repository["role"] : null,
121425
+ relativePath: repository["relativePath"]
121426
+ }));
121427
+ return {
121428
+ slug: project2["slug"],
121429
+ name: project2["name"],
121430
+ description: typeof project2["description"] === "string" ? project2["description"] : null,
121431
+ relativePath: typeof project2["relativePath"] === "string" ? project2["relativePath"] : basename19(stackDir),
121432
+ repositories
121433
+ };
121434
+ } catch {
121435
+ return null;
121436
+ }
121437
+ }
121438
+ async function registerDemoProjects(command, stacks, token) {
121439
+ const projects2 = stacks.map((stack) => readDemoProject(stack.dir)).filter((project2) => project2 !== null);
121440
+ if (projects2.length === 0) return;
121441
+ const { client, output: output2 } = clientFor(command, { actor: void 0, actorKind: "human", ...token ? { token } : {} });
121442
+ for (const { repositories, ...project2 } of projects2) {
121443
+ const created = await client.answer("POST", "/projects", project2);
121444
+ if (created.ok) output2.progress(`registered Project ${project2.name} (${project2.slug})`);
121445
+ else if (created.status === 409) output2.progress(`Project ${project2.slug} already registered`);
121446
+ else {
121447
+ output2.warning(`could not register Project ${project2.slug}: the panel answered ${created.status}`);
121448
+ output2.hint(`portta projects create --slug ${project2.slug} --path ${project2.relativePath}`);
121449
+ continue;
121450
+ }
121451
+ for (const repository of repositories) {
121452
+ const added = await client.answer("POST", `/projects/${encodeURIComponent(project2.slug)}/repositories`, repository);
121453
+ if (!added.ok && added.status !== 409) output2.warning(`could not register repository ${repository.name} on ${project2.slug}: the panel answered ${added.status}`);
121454
+ }
121455
+ }
121456
+ }
121413
121457
  function outputFor(command) {
121414
121458
  return new Output(workGlobals(command));
121415
121459
  }
@@ -121419,15 +121463,24 @@ function demoHome(command) {
121419
121463
  const configured = context.env["PORTTA_PROJECTS_HOME"]?.trim() || defaultProjectsHome(userHome, typeof process.getuid === "function" && process.getuid() === 0);
121420
121464
  return normalizeProjectsHome(configured, context.root, userHome);
121421
121465
  }
121422
- async function demoStacksUp(command) {
121466
+ function requireDemoStacks(command) {
121467
+ const home2 = demoHome(command);
121468
+ const stacks = findDemoStacks(home2);
121469
+ if (stacks.length > 0) return stacks;
121470
+ throw new PreconditionError(
121471
+ `--demo found no portta-demo-* Compose project in ${home2}`,
121472
+ "clone the example repositories there, or set PORTTA_PROJECTS_HOME in .env to the directory that holds them"
121473
+ );
121474
+ }
121475
+ async function demoStacksUp(command, token) {
121423
121476
  const output2 = outputFor(command);
121424
- const stacks = findDemoStacks(demoHome(command));
121425
- if (stacks.length === 0) return;
121477
+ const stacks = requireDemoStacks(command);
121426
121478
  output2.step("demo stacks");
121427
121479
  for (const stack of stacks) {
121428
121480
  output2.progress(`starting ${stack.name}`);
121429
121481
  await runProcess("docker", demoComposeArgs(stack, "up"), { cwd: stack.dir, stdio: "inherit" });
121430
121482
  }
121483
+ await registerDemoProjects(command, stacks, token);
121431
121484
  }
121432
121485
  async function demoStacksDown(command) {
121433
121486
  const output2 = outputFor(command);
@@ -121625,6 +121678,7 @@ async function upCommand(profile, options, command) {
121625
121678
  if (options.localRelease) selectLocalRelease(gatewayContext({ profile: profile ?? globals8(command).profile }));
121626
121679
  const context = gatewayContext({ profile: profile ?? globals8(command).profile });
121627
121680
  if (options.localRelease) context.env["PORTTA_LOCAL_RELEASE"] = "true";
121681
+ if (options.demo) requireDemoStacks(command);
121628
121682
  if (context.config.profile === "remote-public" && context.config.tcpEnabled) throw new RefusedError("TCP entrypoints must not run on the remote-public profile");
121629
121683
  if (context.config.profile === "remote-public" && context.config.webEnabled && context.config.webExpose === "vpn") {
121630
121684
  throw new RefusedError(
@@ -121683,6 +121737,7 @@ async function devCommand(profile, options, command) {
121683
121737
  await reexecBuiltCli(existing.root);
121684
121738
  return;
121685
121739
  }
121740
+ if (options.demo) requireDemoStacks(command);
121686
121741
  const needsBootstrap = !existsSync38(join57(existing.root, ".env"));
121687
121742
  output2.progress(`dev runs: ${[
121688
121743
  ...options.reset ? ["wipe Portta-managed containers and volumes"] : [],
@@ -121704,7 +121759,7 @@ async function devCommand(profile, options, command) {
121704
121759
  await waitForPanel(command);
121705
121760
  const demoToken = await ensureDevDemoOwner(command);
121706
121761
  await finishWebUp(panel, command, false, demoToken);
121707
- if (options.demo) await demoStacksUp(command);
121762
+ if (options.demo) await demoStacksUp(command, demoToken);
121708
121763
  await urlsCommand({}, command);
121709
121764
  }
121710
121765
  async function downCommand(options, command) {
@@ -121886,7 +121941,7 @@ function readAliases(root2) {
121886
121941
  const path7 = join57(root2, "config/traefik/dynamic/portta-aliases.yaml");
121887
121942
  if (!existsSync38(path7)) return [];
121888
121943
  try {
121889
- return parseAliases(readFileSync30(path7, "utf8"));
121944
+ return parseAliases(readFileSync31(path7, "utf8"));
121890
121945
  } catch {
121891
121946
  return [];
121892
121947
  }
@@ -122039,7 +122094,7 @@ async function dnsSetup(options, command) {
122039
122094
 
122040
122095
  // src/commands/host-service.ts
122041
122096
  init_config2();
122042
- import { chmodSync as chmodSync10, existsSync as existsSync39, mkdirSync as mkdirSync19, readFileSync as readFileSync31, realpathSync as realpathSync7, unlinkSync as unlinkSync6, writeFileSync as writeFileSync15 } from "node:fs";
122097
+ import { chmodSync as chmodSync10, existsSync as existsSync39, mkdirSync as mkdirSync19, readFileSync as readFileSync32, realpathSync as realpathSync7, unlinkSync as unlinkSync6, writeFileSync as writeFileSync15 } from "node:fs";
122043
122098
  import { homedir as homedir10 } from "node:os";
122044
122099
  import { dirname as dirname29, join as join59 } from "node:path";
122045
122100
  init_context();
@@ -122120,7 +122175,7 @@ var LAUNCHD_ENV_ENTRY_RE = /<key>([^<]+)<\/key>\s*<string>([^<]*)<\/string>/g;
122120
122175
  function readEnvVarsFromUnit(filePath, platform4) {
122121
122176
  let text;
122122
122177
  try {
122123
- text = readFileSync31(filePath, "utf8");
122178
+ text = readFileSync32(filePath, "utf8");
122124
122179
  } catch {
122125
122180
  return {};
122126
122181
  }
@@ -122357,7 +122412,7 @@ async function configGet(name, command) {
122357
122412
  const global3 = globals10(command);
122358
122413
  const context = gatewayContext({ profile: global3.profile });
122359
122414
  const item = setting(name);
122360
- if (SECRETS.has(item.key)) throw new RefusedError(`${name} is a secret and is never printed`, "portta web auth status reports whether it is set");
122415
+ if (SECRETS.has(item.key)) throw new RefusedError(`${name} is a secret and is never printed`, "portta doctor reports whether it is set");
122361
122416
  const value3 = context.env[item.key] ?? "";
122362
122417
  const output2 = new Output(global3);
122363
122418
  if (output2.json) output2.data({ setting: name, variable: item.key, value: value3 });
@@ -122521,7 +122576,7 @@ async function configSet(name, value3, options, command) {
122521
122576
  const output2 = new Output(global3);
122522
122577
  const context = gatewayContext({ profile: global3.profile });
122523
122578
  const item = setting(name);
122524
- if (SECRETS.has(item.key)) throw new RefusedError(`${name} is a secret and is not set this way`, "portta web auth set writes the panel credential");
122579
+ if (SECRETS.has(item.key)) throw new RefusedError(`${name} is a secret and is not set this way`, "edit .env; portta web up generates PORTTA_AUTH_SECRET when it is empty");
122525
122580
  if (item.allowed && !item.allowed.includes(value3)) throw new UsageError(`${name} must be one of: ${item.allowed.join(", ")}`);
122526
122581
  let values;
122527
122582
  if (name === "panel.access") values = await setPanelAccess(context.root, value3, output2);
@@ -122644,7 +122699,7 @@ async function setupCommand(options, command) {
122644
122699
  const envFile = join61(target, ".env");
122645
122700
  prepareEnvFile(envFile);
122646
122701
  record6("environment", "updated", "environment prepared from .env.example");
122647
- for (const directory of ["state", "state/auth", "state/ssh", "state/runner", "state/access", "state/cloudflared", "state/git", "state/github", "state/metrics", "state/environment", "state/logs", "config/tls", "config/traefik/dynamic"]) mkdirSync20(join61(target, directory), { recursive: true });
122702
+ for (const directory of ["state", "state/auth", "state/ssh", "state/runner", "state/access", "state/cloudflared", "state/git", "state/metrics", "state/environment", "state/logs", "config/tls", "config/traefik/dynamic"]) mkdirSync20(join61(target, directory), { recursive: true });
122648
122703
  for (const directory of ["state/auth", "state/ssh", "state/runner", "state/cloudflared"]) chmodSync11(join61(target, directory), 448);
122649
122704
  record6("directories", "ok", "gateway state directories");
122650
122705
  const context = gatewayContext({ root: target, profile });
@@ -122666,7 +122721,7 @@ async function setupCommand(options, command) {
122666
122721
  // src/commands/auth.ts
122667
122722
  init_context();
122668
122723
  init_errors3();
122669
- import { readFileSync as readFileSync32 } from "node:fs";
122724
+ import { readFileSync as readFileSync33 } from "node:fs";
122670
122725
  function globals12(command) {
122671
122726
  return command.optsWithGlobals();
122672
122727
  }
@@ -122681,7 +122736,7 @@ function panelUrlFor2(command) {
122681
122736
  }
122682
122737
  async function authBootstrap(options, command) {
122683
122738
  if (!options.name || !options.email) throw new UsageError("--name and --email are required");
122684
- const password = options.passwordStdin ? readFileSync32(0, "utf8").trim() : "";
122739
+ const password = options.passwordStdin ? readFileSync33(0, "utf8").trim() : "";
122685
122740
  if (!password) throw new UsageError("the password is read from stdin", 'printf %s "$PASSWORD" | portta auth bootstrap --name \u2026 --email \u2026 --password-stdin');
122686
122741
  if (password.length < 10) throw new UsageError("the password must be at least 10 characters");
122687
122742
  const { client, output: output2 } = clientFor(command, { actor: void 0, actorKind: "human" });
@@ -122714,7 +122769,7 @@ async function authLogin(options, command) {
122714
122769
  }
122715
122770
  async function readSecret() {
122716
122771
  if (!process.stdin.isTTY) {
122717
- const piped = readFileSync32(0, "utf8").trim();
122772
+ const piped = readFileSync33(0, "utf8").trim();
122718
122773
  if (piped) return piped;
122719
122774
  throw new UsageError("no token on stdin", "pass --token, or run this in a terminal");
122720
122775
  }
@@ -122853,7 +122908,7 @@ init_src();
122853
122908
  init_context();
122854
122909
  init_errors3();
122855
122910
  import { randomBytes as randomBytes8 } from "node:crypto";
122856
- import { chmodSync as chmodSync12, mkdirSync as mkdirSync21, readFileSync as readFileSync33 } from "node:fs";
122911
+ import { chmodSync as chmodSync12, mkdirSync as mkdirSync21, readFileSync as readFileSync34 } from "node:fs";
122857
122912
  import { dirname as dirname31, join as join62 } from "node:path";
122858
122913
  function globals13(command) {
122859
122914
  return command.optsWithGlobals();
@@ -122885,7 +122940,7 @@ async function protectHost(hostInput, options, command) {
122885
122940
  const entryPoint = options.entrypoint ?? (gatewayContext({ profile: globals13(command).profile }).config.tlsEnabled ? "websecure" : "web");
122886
122941
  if (!/^[A-Za-z0-9_-]+$/.test(entryPoint)) throw new UsageError(`invalid entrypoint: ${entryPoint}`);
122887
122942
  const generated = options.passwordStdin !== true;
122888
- const password = generated ? randomBytes8(20).toString("base64url").slice(0, 20).match(/.{1,5}/g).join("-") : readFileSync33(0, "utf8").trim();
122943
+ const password = generated ? randomBytes8(20).toString("base64url").slice(0, 20).match(/.{1,5}/g).join("-") : readFileSync34(0, "utf8").trim();
122889
122944
  if (!password) throw new UsageError("no password on stdin");
122890
122945
  const target = paths(command);
122891
122946
  const existing = readProtectionStore(target.store);
@@ -122938,11 +122993,11 @@ async function protectStatus(hostInput, command) {
122938
122993
  init_src();
122939
122994
  init_errors3();
122940
122995
  init_context();
122941
- import { readFileSync as readFileSync34 } from "node:fs";
122996
+ import { readFileSync as readFileSync35 } from "node:fs";
122942
122997
  import { randomBytes as randomBytes9 } from "node:crypto";
122943
122998
  function passwordFrom(options) {
122944
122999
  if (options.passwordStdin) {
122945
- const password2 = readFileSync34(0, "utf8").trim();
123000
+ const password2 = readFileSync35(0, "utf8").trim();
122946
123001
  if (!password2) throw new UsageError("no password on stdin");
122947
123002
  if (password2.length < 10) throw new UsageError("the password must be at least 10 characters");
122948
123003
  return { password: password2, generated: false };
@@ -123335,7 +123390,7 @@ function paths2(root2) {
123335
123390
  init_src();
123336
123391
  init_context();
123337
123392
  init_errors3();
123338
- import { chmodSync as chmodSync14, copyFileSync as copyFileSync5, cpSync as cpSync2, existsSync as existsSync43, mkdirSync as mkdirSync23, mkdtempSync as mkdtempSync3, readFileSync as readFileSync35, rmSync as rmSync12, statSync as statSync12, writeFileSync as writeFileSync17 } from "node:fs";
123393
+ import { chmodSync as chmodSync14, copyFileSync as copyFileSync5, cpSync as cpSync2, existsSync as existsSync43, mkdirSync as mkdirSync23, mkdtempSync as mkdtempSync3, readFileSync as readFileSync36, rmSync as rmSync12, statSync as statSync12, writeFileSync as writeFileSync17 } from "node:fs";
123339
123394
  import { hostname as hostname3, tmpdir as tmpdir5 } from "node:os";
123340
123395
  import { dirname as dirname32, join as join65 } from "node:path";
123341
123396
  function globals16(command) {
@@ -123398,7 +123453,7 @@ async function backupCommand(options, command) {
123398
123453
  database = await dumpDatabase(context, join65(staging, "panel.db"));
123399
123454
  if (!database) {
123400
123455
  output2.warning("the panel database was not included");
123401
- output2.hint("it is only there when the panel is running; --no-database silences this");
123456
+ output2.hint("it exists once the panel has started, and copying it needs sqlite3 on this host; --no-database silences this");
123402
123457
  }
123403
123458
  }
123404
123459
  const previous = process.umask(63);
@@ -123447,7 +123502,7 @@ async function restoreCommand(archive, options, command) {
123447
123502
  if (extracted.failed) throw new UsageError("that file is not a Portta backup");
123448
123503
  const manifestPath2 = join65(staging, "portta-backup.json");
123449
123504
  if (!existsSync43(manifestPath2)) throw new UsageError("that archive has no Portta manifest");
123450
- const manifest = parseManifest(readFileSync35(manifestPath2, "utf8"));
123505
+ const manifest = parseManifest(readFileSync36(manifestPath2, "utf8"));
123451
123506
  if (!manifest) throw new UsageError("that archive has an unreadable Portta manifest");
123452
123507
  output2.progress(`restoring a backup taken from Portta ${manifest.portta}`);
123453
123508
  if (!options.force && await containerRunning(`${context.env["PORTTA_PROJECT_NAME"] || "portta"}-traefik-1`)) {
@@ -128559,7 +128614,7 @@ async function buildCommand(command) {
128559
128614
 
128560
128615
  // src/commands/remote.ts
128561
128616
  init_src();
128562
- import { existsSync as existsSync44, mkdirSync as mkdirSync24, readFileSync as readFileSync36, readdirSync as readdirSync10, rmSync as rmSync13, writeFileSync as writeFileSync18 } from "node:fs";
128617
+ import { existsSync as existsSync44, mkdirSync as mkdirSync24, readFileSync as readFileSync37, readdirSync as readdirSync10, rmSync as rmSync13, writeFileSync as writeFileSync18 } from "node:fs";
128563
128618
  import { basename as basename20, join as join67 } from "node:path";
128564
128619
  import { createServer as createServer3 } from "node:net";
128565
128620
  import { randomBytes as randomBytes10 } from "node:crypto";
@@ -128770,7 +128825,7 @@ function readTunnels(root2) {
128770
128825
  for (const name of readdirSync10(directory).filter((entry) => entry.endsWith(".json"))) {
128771
128826
  const path7 = join67(directory, name);
128772
128827
  try {
128773
- const record6 = parseTunnelRecord(basename20(name, ".json"), readFileSync36(path7, "utf8"));
128828
+ const record6 = parseTunnelRecord(basename20(name, ".json"), readFileSync37(path7, "utf8"));
128774
128829
  if (record6) records.push(record6);
128775
128830
  } catch {
128776
128831
  }
@@ -128904,7 +128959,7 @@ init_src();
128904
128959
  init_src();
128905
128960
  init_context();
128906
128961
  init_errors3();
128907
- import { chmodSync as chmodSync15, closeSync as closeSync3, existsSync as existsSync45, mkdirSync as mkdirSync25, openSync as openSync3, readFileSync as readFileSync37, rmSync as rmSync14, writeFileSync as writeFileSync19 } from "node:fs";
128962
+ import { chmodSync as chmodSync15, closeSync as closeSync3, existsSync as existsSync45, mkdirSync as mkdirSync25, openSync as openSync3, readFileSync as readFileSync38, rmSync as rmSync14, writeFileSync as writeFileSync19 } from "node:fs";
128908
128963
  import { ReadStream } from "node:tty";
128909
128964
  import { join as join68 } from "node:path";
128910
128965
  function globals20(command) {
@@ -129047,7 +129102,7 @@ async function tunnelSetup(options, command) {
129047
129102
  let token;
129048
129103
  if (options.tokenFile) {
129049
129104
  if (!existsSync45(options.tokenFile)) throw new UsageError(`no such file: ${options.tokenFile}`);
129050
- token = readFileSync37(options.tokenFile, "utf8").replace(/\s+/g, "");
129105
+ token = readFileSync38(options.tokenFile, "utf8").replace(/\s+/g, "");
129051
129106
  } else {
129052
129107
  token = await promptForToken();
129053
129108
  }
@@ -129225,10 +129280,10 @@ describe3(program2.command("version"), "Print the installed version").action((_o
129225
129280
  describe3(program2.command("setup"), "Install or update a gateway from the packaged runtime").option("--dir <path>", "gateway installation directory").option("--dry-run", "print the idempotent plan without changing anything").option("--skip-pull", "do not pull images").action(setupCommand);
129226
129281
  describe3(program2.command("bootstrap"), "Prepare this installation and run diagnostics").option("--skip-pull", "do not pull component images").action(bootstrapCommand);
129227
129282
  describe3(program2.command("build"), "Build every local Portta image for this checkout").action((_options, command) => buildCommand(command));
129228
- describe3(program2.command("up [profile]"), "Start gateway components").option("--local-release", "use the locally built image for this checkout").option("--attach", "run in the foreground").option("--demo", "also start portta-demo-* projects from PORTTA_PROJECTS_HOME and import their panel records").action((profile, options, command) => hasComposeRuntime() ? runtimeAction("up", {}, command) : upCommand(profile, options, command));
129229
- describe3(program2.command("dev [profile]"), "Start a checkout from local Dockerfiles, never the published images").option("--reset", "stop every Portta-managed stack, drop their volumes, and start as if this checkout were new").option("--demo", "also start portta-demo-* projects from PORTTA_PROJECTS_HOME; example data is imported either way").action((profile, options, command) => devCommand(profile, options, command));
129283
+ describe3(program2.command("up [profile]"), "Start gateway components").option("--local-release", "use the locally built image for this checkout").option("--attach", "run in the foreground").option("--demo", "also start portta-demo-* projects from PORTTA_PROJECTS_HOME").action((profile, options, command) => hasComposeRuntime() ? runtimeAction("up", {}, command) : upCommand(profile, options, command));
129284
+ describe3(program2.command("dev [profile]"), "Start a checkout from local Dockerfiles, never the published images").option("--reset", "stop every Portta-managed stack, drop their volumes, and start as if this checkout were new").option("--demo", "also start portta-demo-* projects from PORTTA_PROJECTS_HOME").action((profile, options, command) => devCommand(profile, options, command));
129230
129285
  describe3(program2.command("down"), "Stop the current Compose runtime, or gateway components; keep projects and data").option("--demo", "also stop portta-demo-* projects from PORTTA_PROJECTS_HOME and drop their volumes").action((options, command) => hasComposeRuntime() ? runtimeAction("down", {}, command) : downCommand(options, command));
129231
- describe3(program2.command("reset"), "Stop every Portta-managed stack, drop their volumes, and restart this checkout as if it were new").option("--demo", "also recreate portta-demo-* projects from PORTTA_PROJECTS_HOME and import their panel records").action((options, command) => resetCommand(options, command));
129286
+ describe3(program2.command("reset"), "Stop every Portta-managed stack, drop their volumes, and restart this checkout as if it were new").option("--demo", "also recreate portta-demo-* projects from PORTTA_PROJECTS_HOME").action((options, command) => resetCommand(options, command));
129232
129287
  describe3(program2.command("restart"), "Restart the current Compose runtime, or recreate gateway components").action((_options, command) => hasComposeRuntime() ? runtimeAction("restart", {}, command) : restartCommand(command));
129233
129288
  describe3(program2.command("status"), "Show the current Compose runtime, or gateway status").action((_options, command) => hasComposeRuntime() ? runtimeAction("status", {}, command) : statusCommand(command));
129234
129289
  describe3(program2.command("doctor"), "Run read-only host and gateway diagnostics").action((_options, command) => doctorCommand(command));
@@ -129297,7 +129352,7 @@ describe3(dns.command("check"), "Resolve a wildcard probe").action((_options, co
129297
129352
  describe3(dns.command("status"), "Show DNS configuration without secrets").action((_options, command) => dnsStatus(command));
129298
129353
  describe3(dns.command("setup"), "Plan or apply a Cloudflare wildcard record").option("--target <ip>").option("--dry-run").action(dnsSetup);
129299
129354
  var web = describe3(program2.command("web"), "Run the optional administration panel").option("--local-release", "use the locally built image for this checkout");
129300
- describe3(web.command("up"), "Enable and start the panel").option("--expose <scope>", "local, tailscale, public or vpn").option("--port <number>").option("--read-only").option("--writable").action((options, command) => webUp({ ...options, localRelease: command.optsWithGlobals().localRelease }, command));
129355
+ describe3(web.command("up"), "Enable and start the panel").option("--expose <scope>", "local, tailscale, vpn, public or domain").option("--port <number>").option("--read-only").option("--writable").action((options, command) => webUp({ ...options, localRelease: command.optsWithGlobals().localRelease }, command));
129301
129356
  describe3(web.command("dev"), "Start the panel with hot reload").option("--expose <scope>").option("--port <number>").option("--read-only").option("--writable").action((options, command) => webUp({ ...options, dev: true }, command));
129302
129357
  describe3(web.command("down"), "Stop the panel only").action((_options, command) => webDown(command));
129303
129358
  describe3(web.command("disable"), "Stop and disable the panel").action((_options, command) => webDisable(command));