@codions/portta 0.0.0-develop.155.sha-e6e3870 → 0.0.0-develop.157.sha-ae5ce5e

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
@@ -22364,7 +22364,7 @@ var init_package = __esm({
22364
22364
  "package.json"() {
22365
22365
  package_default = {
22366
22366
  name: "@codions/portta",
22367
- version: "0.0.0-develop.155.sha-e6e3870",
22367
+ version: "0.0.0-develop.157.sha-ae5ce5e",
22368
22368
  description: "Machine-first CLI for a shared Docker development gateway",
22369
22369
  license: "MIT",
22370
22370
  type: "module",
@@ -22438,7 +22438,7 @@ var init_version = __esm({
22438
22438
  "use strict";
22439
22439
  init_package();
22440
22440
  CLI_VERSION = package_default.version;
22441
- CLI_BUILD_DATE = true ? "2026-09-16T16:54:30Z" : void 0;
22441
+ CLI_BUILD_DATE = true ? "2026-09-16T21:32:25Z" : void 0;
22442
22442
  }
22443
22443
  });
22444
22444
 
@@ -89799,7 +89799,7 @@ function createDaemonTaskflowHost(context) {
89799
89799
  port: hostListen(env).port,
89800
89800
  workflowBuiltinsDir: taskflowAssets(env).workflowBuiltinsDir,
89801
89801
  version: env.PORTTA_VERSION || "0.0.0-local",
89802
- buildDate: true ? "2026-09-16T16:54:30Z" : (/* @__PURE__ */ new Date()).toISOString(),
89802
+ buildDate: true ? "2026-09-16T21:32:25Z" : (/* @__PURE__ */ new Date()).toISOString(),
89803
89803
  environmentStore: createEnvironmentStore(paths3.database),
89804
89804
  hostEnvironmentProvider: new HostEnvironmentProvider(),
89805
89805
  devContainerProvider: new DevContainerProvider(),
@@ -100395,7 +100395,7 @@ var require_network = __commonJS({
100395
100395
  var exec3 = __require("child_process").exec;
100396
100396
  var execSync = __require("child_process").execSync;
100397
100397
  var execFileSync2 = __require("child_process").execFileSync;
100398
- var readFileSync38 = __require("fs").readFileSync;
100398
+ var readFileSync39 = __require("fs").readFileSync;
100399
100399
  var fs = __require("fs");
100400
100400
  var util2 = require_util2();
100401
100401
  var _platform = process.platform;
@@ -100883,7 +100883,7 @@ var require_network = __commonJS({
100883
100883
  return result;
100884
100884
  }
100885
100885
  try {
100886
- const content = readFileSync38(file2, { encoding: "utf8" });
100886
+ const content = readFileSync39(file2, { encoding: "utf8" });
100887
100887
  const lines = content.split("\n").filter((l) => /iface|source/.test(l));
100888
100888
  lines.forEach((line4) => {
100889
100889
  const parts2 = line4.replace(/\s+/g, " ").trim().split(" ");
@@ -120141,7 +120141,7 @@ async function runtimeAction(action, options, command) {
120141
120141
  init_src();
120142
120142
  init_src();
120143
120143
  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";
120144
+ import { chmodSync as chmodSync9, existsSync as existsSync38, mkdirSync as mkdirSync18, readFileSync as readFileSync31, readdirSync as readdirSync8, rmSync as rmSync10 } from "node:fs";
120145
120145
  import { join as join57 } from "node:path";
120146
120146
  init_errors3();
120147
120147
  init_version();
@@ -120763,35 +120763,9 @@ async function runDoctor(context) {
120763
120763
  const socket = (proxy.mounts ?? []).find((mount) => mount.source.includes("docker.sock"));
120764
120764
  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
120765
  }
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"));
120766
+ if (config3.webEnabled) {
120767
+ const databaseFile = databaseFileFor(root2);
120768
+ 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
120769
  }
120796
120770
  const panelIsPublic = (env["PORTTA_WEB_EXPOSE"] || "local") === "public";
120797
120771
  if (traefik && traefik.state === "running") {
@@ -121380,7 +121354,7 @@ async function reexecBuiltCli(root2) {
121380
121354
  init_src();
121381
121355
  init_context();
121382
121356
  init_errors3();
121383
- import { existsSync as existsSync37, readdirSync as readdirSync7 } from "node:fs";
121357
+ import { existsSync as existsSync37, readdirSync as readdirSync7, readFileSync as readFileSync30 } from "node:fs";
121384
121358
  import { basename as basename19, join as join56 } from "node:path";
121385
121359
  var PORTTA_DIRECTORY = ".portta";
121386
121360
  var OVERLAY_FILE = "compose.portta.yaml";
@@ -121388,6 +121362,7 @@ var COMPOSE_FILE = "compose.yaml";
121388
121362
  var PANEL_WAIT_MS2 = 12e4;
121389
121363
  var PANEL_POLL_MS2 = 500;
121390
121364
  var DEMO_DIRECTORY_PREFIX = "portta-demo-";
121365
+ var MANIFEST_FILE = "portta.example.json";
121391
121366
  function demoDirectories(home2) {
121392
121367
  if (!existsSync37(home2)) return [];
121393
121368
  return readdirSync7(home2, { withFileTypes: true }).filter((entry) => entry.isDirectory() && entry.name.startsWith(DEMO_DIRECTORY_PREFIX)).map((entry) => join56(home2, entry.name)).sort();
@@ -121410,6 +121385,48 @@ function demoComposeArgs(stack, action) {
121410
121385
  if (action === "up") return ["compose", ...files, "up", "-d"];
121411
121386
  return ["compose", ...files, "down", "-v"];
121412
121387
  }
121388
+ function readDemoProject(stackDir) {
121389
+ const path7 = join56(stackDir, PORTTA_DIRECTORY, MANIFEST_FILE);
121390
+ if (!existsSync37(path7)) return null;
121391
+ try {
121392
+ const manifest = JSON.parse(readFileSync30(path7, "utf8"));
121393
+ const project2 = manifest.project;
121394
+ if (typeof project2?.["slug"] !== "string" || typeof project2["name"] !== "string") return null;
121395
+ const repositories = (Array.isArray(manifest.repositories) ? manifest.repositories : []).filter((repository) => typeof repository?.["name"] === "string" && typeof repository["relativePath"] === "string").map((repository) => ({
121396
+ name: repository["name"],
121397
+ role: typeof repository["role"] === "string" ? repository["role"] : null,
121398
+ relativePath: repository["relativePath"]
121399
+ }));
121400
+ return {
121401
+ slug: project2["slug"],
121402
+ name: project2["name"],
121403
+ description: typeof project2["description"] === "string" ? project2["description"] : null,
121404
+ relativePath: typeof project2["relativePath"] === "string" ? project2["relativePath"] : basename19(stackDir),
121405
+ repositories
121406
+ };
121407
+ } catch {
121408
+ return null;
121409
+ }
121410
+ }
121411
+ async function registerDemoProjects(command, stacks, token) {
121412
+ const projects2 = stacks.map((stack) => readDemoProject(stack.dir)).filter((project2) => project2 !== null);
121413
+ if (projects2.length === 0) return;
121414
+ const { client, output: output2 } = clientFor(command, { actor: void 0, actorKind: "human", ...token ? { token } : {} });
121415
+ for (const { repositories, ...project2 } of projects2) {
121416
+ const created = await client.answer("POST", "/projects", project2);
121417
+ if (created.ok) output2.progress(`registered Project ${project2.name} (${project2.slug})`);
121418
+ else if (created.status === 409) output2.progress(`Project ${project2.slug} already registered`);
121419
+ else {
121420
+ output2.warning(`could not register Project ${project2.slug}: the panel answered ${created.status}`);
121421
+ output2.hint(`portta projects create --slug ${project2.slug} --path ${project2.relativePath}`);
121422
+ continue;
121423
+ }
121424
+ for (const repository of repositories) {
121425
+ const added = await client.answer("POST", `/projects/${encodeURIComponent(project2.slug)}/repositories`, repository);
121426
+ if (!added.ok && added.status !== 409) output2.warning(`could not register repository ${repository.name} on ${project2.slug}: the panel answered ${added.status}`);
121427
+ }
121428
+ }
121429
+ }
121413
121430
  function outputFor(command) {
121414
121431
  return new Output(workGlobals(command));
121415
121432
  }
@@ -121419,15 +121436,24 @@ function demoHome(command) {
121419
121436
  const configured = context.env["PORTTA_PROJECTS_HOME"]?.trim() || defaultProjectsHome(userHome, typeof process.getuid === "function" && process.getuid() === 0);
121420
121437
  return normalizeProjectsHome(configured, context.root, userHome);
121421
121438
  }
121422
- async function demoStacksUp(command) {
121439
+ function requireDemoStacks(command) {
121440
+ const home2 = demoHome(command);
121441
+ const stacks = findDemoStacks(home2);
121442
+ if (stacks.length > 0) return stacks;
121443
+ throw new PreconditionError(
121444
+ `--demo found no portta-demo-* Compose project in ${home2}`,
121445
+ "clone the example repositories there, or set PORTTA_PROJECTS_HOME in .env to the directory that holds them"
121446
+ );
121447
+ }
121448
+ async function demoStacksUp(command, token) {
121423
121449
  const output2 = outputFor(command);
121424
- const stacks = findDemoStacks(demoHome(command));
121425
- if (stacks.length === 0) return;
121450
+ const stacks = requireDemoStacks(command);
121426
121451
  output2.step("demo stacks");
121427
121452
  for (const stack of stacks) {
121428
121453
  output2.progress(`starting ${stack.name}`);
121429
121454
  await runProcess("docker", demoComposeArgs(stack, "up"), { cwd: stack.dir, stdio: "inherit" });
121430
121455
  }
121456
+ await registerDemoProjects(command, stacks, token);
121431
121457
  }
121432
121458
  async function demoStacksDown(command) {
121433
121459
  const output2 = outputFor(command);
@@ -121625,6 +121651,7 @@ async function upCommand(profile, options, command) {
121625
121651
  if (options.localRelease) selectLocalRelease(gatewayContext({ profile: profile ?? globals8(command).profile }));
121626
121652
  const context = gatewayContext({ profile: profile ?? globals8(command).profile });
121627
121653
  if (options.localRelease) context.env["PORTTA_LOCAL_RELEASE"] = "true";
121654
+ if (options.demo) requireDemoStacks(command);
121628
121655
  if (context.config.profile === "remote-public" && context.config.tcpEnabled) throw new RefusedError("TCP entrypoints must not run on the remote-public profile");
121629
121656
  if (context.config.profile === "remote-public" && context.config.webEnabled && context.config.webExpose === "vpn") {
121630
121657
  throw new RefusedError(
@@ -121683,6 +121710,7 @@ async function devCommand(profile, options, command) {
121683
121710
  await reexecBuiltCli(existing.root);
121684
121711
  return;
121685
121712
  }
121713
+ if (options.demo) requireDemoStacks(command);
121686
121714
  const needsBootstrap = !existsSync38(join57(existing.root, ".env"));
121687
121715
  output2.progress(`dev runs: ${[
121688
121716
  ...options.reset ? ["wipe Portta-managed containers and volumes"] : [],
@@ -121704,7 +121732,7 @@ async function devCommand(profile, options, command) {
121704
121732
  await waitForPanel(command);
121705
121733
  const demoToken = await ensureDevDemoOwner(command);
121706
121734
  await finishWebUp(panel, command, false, demoToken);
121707
- if (options.demo) await demoStacksUp(command);
121735
+ if (options.demo) await demoStacksUp(command, demoToken);
121708
121736
  await urlsCommand({}, command);
121709
121737
  }
121710
121738
  async function downCommand(options, command) {
@@ -121886,7 +121914,7 @@ function readAliases(root2) {
121886
121914
  const path7 = join57(root2, "config/traefik/dynamic/portta-aliases.yaml");
121887
121915
  if (!existsSync38(path7)) return [];
121888
121916
  try {
121889
- return parseAliases(readFileSync30(path7, "utf8"));
121917
+ return parseAliases(readFileSync31(path7, "utf8"));
121890
121918
  } catch {
121891
121919
  return [];
121892
121920
  }
@@ -122039,7 +122067,7 @@ async function dnsSetup(options, command) {
122039
122067
 
122040
122068
  // src/commands/host-service.ts
122041
122069
  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";
122070
+ 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
122071
  import { homedir as homedir10 } from "node:os";
122044
122072
  import { dirname as dirname29, join as join59 } from "node:path";
122045
122073
  init_context();
@@ -122120,7 +122148,7 @@ var LAUNCHD_ENV_ENTRY_RE = /<key>([^<]+)<\/key>\s*<string>([^<]*)<\/string>/g;
122120
122148
  function readEnvVarsFromUnit(filePath, platform4) {
122121
122149
  let text;
122122
122150
  try {
122123
- text = readFileSync31(filePath, "utf8");
122151
+ text = readFileSync32(filePath, "utf8");
122124
122152
  } catch {
122125
122153
  return {};
122126
122154
  }
@@ -122357,7 +122385,7 @@ async function configGet(name, command) {
122357
122385
  const global3 = globals10(command);
122358
122386
  const context = gatewayContext({ profile: global3.profile });
122359
122387
  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");
122388
+ if (SECRETS.has(item.key)) throw new RefusedError(`${name} is a secret and is never printed`, "portta doctor reports whether it is set");
122361
122389
  const value3 = context.env[item.key] ?? "";
122362
122390
  const output2 = new Output(global3);
122363
122391
  if (output2.json) output2.data({ setting: name, variable: item.key, value: value3 });
@@ -122521,7 +122549,7 @@ async function configSet(name, value3, options, command) {
122521
122549
  const output2 = new Output(global3);
122522
122550
  const context = gatewayContext({ profile: global3.profile });
122523
122551
  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");
122552
+ 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
122553
  if (item.allowed && !item.allowed.includes(value3)) throw new UsageError(`${name} must be one of: ${item.allowed.join(", ")}`);
122526
122554
  let values;
122527
122555
  if (name === "panel.access") values = await setPanelAccess(context.root, value3, output2);
@@ -122644,7 +122672,7 @@ async function setupCommand(options, command) {
122644
122672
  const envFile = join61(target, ".env");
122645
122673
  prepareEnvFile(envFile);
122646
122674
  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 });
122675
+ 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
122676
  for (const directory of ["state/auth", "state/ssh", "state/runner", "state/cloudflared"]) chmodSync11(join61(target, directory), 448);
122649
122677
  record6("directories", "ok", "gateway state directories");
122650
122678
  const context = gatewayContext({ root: target, profile });
@@ -122666,7 +122694,7 @@ async function setupCommand(options, command) {
122666
122694
  // src/commands/auth.ts
122667
122695
  init_context();
122668
122696
  init_errors3();
122669
- import { readFileSync as readFileSync32 } from "node:fs";
122697
+ import { readFileSync as readFileSync33 } from "node:fs";
122670
122698
  function globals12(command) {
122671
122699
  return command.optsWithGlobals();
122672
122700
  }
@@ -122681,7 +122709,7 @@ function panelUrlFor2(command) {
122681
122709
  }
122682
122710
  async function authBootstrap(options, command) {
122683
122711
  if (!options.name || !options.email) throw new UsageError("--name and --email are required");
122684
- const password = options.passwordStdin ? readFileSync32(0, "utf8").trim() : "";
122712
+ const password = options.passwordStdin ? readFileSync33(0, "utf8").trim() : "";
122685
122713
  if (!password) throw new UsageError("the password is read from stdin", 'printf %s "$PASSWORD" | portta auth bootstrap --name \u2026 --email \u2026 --password-stdin');
122686
122714
  if (password.length < 10) throw new UsageError("the password must be at least 10 characters");
122687
122715
  const { client, output: output2 } = clientFor(command, { actor: void 0, actorKind: "human" });
@@ -122714,7 +122742,7 @@ async function authLogin(options, command) {
122714
122742
  }
122715
122743
  async function readSecret() {
122716
122744
  if (!process.stdin.isTTY) {
122717
- const piped = readFileSync32(0, "utf8").trim();
122745
+ const piped = readFileSync33(0, "utf8").trim();
122718
122746
  if (piped) return piped;
122719
122747
  throw new UsageError("no token on stdin", "pass --token, or run this in a terminal");
122720
122748
  }
@@ -122853,7 +122881,7 @@ init_src();
122853
122881
  init_context();
122854
122882
  init_errors3();
122855
122883
  import { randomBytes as randomBytes8 } from "node:crypto";
122856
- import { chmodSync as chmodSync12, mkdirSync as mkdirSync21, readFileSync as readFileSync33 } from "node:fs";
122884
+ import { chmodSync as chmodSync12, mkdirSync as mkdirSync21, readFileSync as readFileSync34 } from "node:fs";
122857
122885
  import { dirname as dirname31, join as join62 } from "node:path";
122858
122886
  function globals13(command) {
122859
122887
  return command.optsWithGlobals();
@@ -122885,7 +122913,7 @@ async function protectHost(hostInput, options, command) {
122885
122913
  const entryPoint = options.entrypoint ?? (gatewayContext({ profile: globals13(command).profile }).config.tlsEnabled ? "websecure" : "web");
122886
122914
  if (!/^[A-Za-z0-9_-]+$/.test(entryPoint)) throw new UsageError(`invalid entrypoint: ${entryPoint}`);
122887
122915
  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();
122916
+ const password = generated ? randomBytes8(20).toString("base64url").slice(0, 20).match(/.{1,5}/g).join("-") : readFileSync34(0, "utf8").trim();
122889
122917
  if (!password) throw new UsageError("no password on stdin");
122890
122918
  const target = paths(command);
122891
122919
  const existing = readProtectionStore(target.store);
@@ -122938,11 +122966,11 @@ async function protectStatus(hostInput, command) {
122938
122966
  init_src();
122939
122967
  init_errors3();
122940
122968
  init_context();
122941
- import { readFileSync as readFileSync34 } from "node:fs";
122969
+ import { readFileSync as readFileSync35 } from "node:fs";
122942
122970
  import { randomBytes as randomBytes9 } from "node:crypto";
122943
122971
  function passwordFrom(options) {
122944
122972
  if (options.passwordStdin) {
122945
- const password2 = readFileSync34(0, "utf8").trim();
122973
+ const password2 = readFileSync35(0, "utf8").trim();
122946
122974
  if (!password2) throw new UsageError("no password on stdin");
122947
122975
  if (password2.length < 10) throw new UsageError("the password must be at least 10 characters");
122948
122976
  return { password: password2, generated: false };
@@ -123335,7 +123363,7 @@ function paths2(root2) {
123335
123363
  init_src();
123336
123364
  init_context();
123337
123365
  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";
123366
+ 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
123367
  import { hostname as hostname3, tmpdir as tmpdir5 } from "node:os";
123340
123368
  import { dirname as dirname32, join as join65 } from "node:path";
123341
123369
  function globals16(command) {
@@ -123398,7 +123426,7 @@ async function backupCommand(options, command) {
123398
123426
  database = await dumpDatabase(context, join65(staging, "panel.db"));
123399
123427
  if (!database) {
123400
123428
  output2.warning("the panel database was not included");
123401
- output2.hint("it is only there when the panel is running; --no-database silences this");
123429
+ output2.hint("it exists once the panel has started, and copying it needs sqlite3 on this host; --no-database silences this");
123402
123430
  }
123403
123431
  }
123404
123432
  const previous = process.umask(63);
@@ -123447,7 +123475,7 @@ async function restoreCommand(archive, options, command) {
123447
123475
  if (extracted.failed) throw new UsageError("that file is not a Portta backup");
123448
123476
  const manifestPath2 = join65(staging, "portta-backup.json");
123449
123477
  if (!existsSync43(manifestPath2)) throw new UsageError("that archive has no Portta manifest");
123450
- const manifest = parseManifest(readFileSync35(manifestPath2, "utf8"));
123478
+ const manifest = parseManifest(readFileSync36(manifestPath2, "utf8"));
123451
123479
  if (!manifest) throw new UsageError("that archive has an unreadable Portta manifest");
123452
123480
  output2.progress(`restoring a backup taken from Portta ${manifest.portta}`);
123453
123481
  if (!options.force && await containerRunning(`${context.env["PORTTA_PROJECT_NAME"] || "portta"}-traefik-1`)) {
@@ -128559,7 +128587,7 @@ async function buildCommand(command) {
128559
128587
 
128560
128588
  // src/commands/remote.ts
128561
128589
  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";
128590
+ import { existsSync as existsSync44, mkdirSync as mkdirSync24, readFileSync as readFileSync37, readdirSync as readdirSync10, rmSync as rmSync13, writeFileSync as writeFileSync18 } from "node:fs";
128563
128591
  import { basename as basename20, join as join67 } from "node:path";
128564
128592
  import { createServer as createServer3 } from "node:net";
128565
128593
  import { randomBytes as randomBytes10 } from "node:crypto";
@@ -128770,7 +128798,7 @@ function readTunnels(root2) {
128770
128798
  for (const name of readdirSync10(directory).filter((entry) => entry.endsWith(".json"))) {
128771
128799
  const path7 = join67(directory, name);
128772
128800
  try {
128773
- const record6 = parseTunnelRecord(basename20(name, ".json"), readFileSync36(path7, "utf8"));
128801
+ const record6 = parseTunnelRecord(basename20(name, ".json"), readFileSync37(path7, "utf8"));
128774
128802
  if (record6) records.push(record6);
128775
128803
  } catch {
128776
128804
  }
@@ -128904,7 +128932,7 @@ init_src();
128904
128932
  init_src();
128905
128933
  init_context();
128906
128934
  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";
128935
+ 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
128936
  import { ReadStream } from "node:tty";
128909
128937
  import { join as join68 } from "node:path";
128910
128938
  function globals20(command) {
@@ -129047,7 +129075,7 @@ async function tunnelSetup(options, command) {
129047
129075
  let token;
129048
129076
  if (options.tokenFile) {
129049
129077
  if (!existsSync45(options.tokenFile)) throw new UsageError(`no such file: ${options.tokenFile}`);
129050
- token = readFileSync37(options.tokenFile, "utf8").replace(/\s+/g, "");
129078
+ token = readFileSync38(options.tokenFile, "utf8").replace(/\s+/g, "");
129051
129079
  } else {
129052
129080
  token = await promptForToken();
129053
129081
  }
@@ -129225,10 +129253,10 @@ describe3(program2.command("version"), "Print the installed version").action((_o
129225
129253
  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
129254
  describe3(program2.command("bootstrap"), "Prepare this installation and run diagnostics").option("--skip-pull", "do not pull component images").action(bootstrapCommand);
129227
129255
  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));
129256
+ 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));
129257
+ 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
129258
  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));
129259
+ 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
129260
  describe3(program2.command("restart"), "Restart the current Compose runtime, or recreate gateway components").action((_options, command) => hasComposeRuntime() ? runtimeAction("restart", {}, command) : restartCommand(command));
129233
129261
  describe3(program2.command("status"), "Show the current Compose runtime, or gateway status").action((_options, command) => hasComposeRuntime() ? runtimeAction("status", {}, command) : statusCommand(command));
129234
129262
  describe3(program2.command("doctor"), "Run read-only host and gateway diagnostics").action((_options, command) => doctorCommand(command));
@@ -129297,7 +129325,7 @@ describe3(dns.command("check"), "Resolve a wildcard probe").action((_options, co
129297
129325
  describe3(dns.command("status"), "Show DNS configuration without secrets").action((_options, command) => dnsStatus(command));
129298
129326
  describe3(dns.command("setup"), "Plan or apply a Cloudflare wildcard record").option("--target <ip>").option("--dry-run").action(dnsSetup);
129299
129327
  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));
129328
+ 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
129329
  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
129330
  describe3(web.command("down"), "Stop the panel only").action((_options, command) => webDown(command));
129303
129331
  describe3(web.command("disable"), "Stop and disable the panel").action((_options, command) => webDisable(command));