openship 0.6.6 → 0.6.8

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.
Files changed (72) hide show
  1. package/dist/bare-FA66N7GC.js +29 -0
  2. package/dist/{chunk-77QDF6UD.js → chunk-3MP6ACKM.js} +587 -156
  3. package/dist/{chunk-YIMOCZBZ.js → chunk-5DJOB7IX.js} +3 -3
  4. package/dist/{chunk-WVAML4H6.js → chunk-5I3COAFH.js} +274 -65
  5. package/dist/{chunk-5DMWSTOM.js → chunk-5UXHT6ED.js} +940 -224
  6. package/dist/{chunk-LUKYPVJ4.js → chunk-6GU6JSK6.js} +329 -466
  7. package/dist/{chunk-RSR7PFIQ.js → chunk-7PEXM2UI.js} +213 -45
  8. package/dist/{chunk-VTGPEC6B.js → chunk-7YBWXEM6.js} +6 -4
  9. package/dist/chunk-AHJYP7DG.js +938 -0
  10. package/dist/chunk-BO6CY4V2.js +265 -0
  11. package/dist/{chunk-FE7ZZQSE.js → chunk-D3OEMXYP.js} +3 -3
  12. package/dist/chunk-D6HU4VUZ.js +249 -0
  13. package/dist/{chunk-LFE5KQ7Y.js → chunk-ENKJ7XIF.js} +152 -57
  14. package/dist/{chunk-YMMQRE5J.js → chunk-GNOMEMNI.js} +1 -1
  15. package/dist/{chunk-QVW76WDL.js → chunk-IAMOUDHY.js} +190 -64
  16. package/dist/{chunk-OBPKYXQL.js → chunk-MUWVC5R4.js} +219 -58
  17. package/dist/chunk-N5YZMO33.js +111 -0
  18. package/dist/{chunk-A4GXYE4L.js → chunk-NIYD7MD2.js} +206 -52
  19. package/dist/{chunk-UVNMQM7W.js → chunk-NPSRGIHV.js} +8 -6
  20. package/dist/{chunk-2GWYYHOR.js → chunk-NRU2EBYZ.js} +2 -2
  21. package/dist/{chunk-CBLD2HS5.js → chunk-NUT6HKK6.js} +8 -5
  22. package/dist/{chunk-ZFJXPCQP.js → chunk-P7RCYELG.js} +4 -3
  23. package/dist/chunk-R4VDPFLS.js +106 -0
  24. package/dist/{chunk-HKRQXPTI.js → chunk-R5DC5PEF.js} +1 -1
  25. package/dist/{chunk-NK5N5LXN.js → chunk-RCMERVBL.js} +24 -9
  26. package/dist/{chunk-RXQBD4ZY.js → chunk-RHLNVZMS.js} +2 -2
  27. package/dist/chunk-SK4FFCFR.js +70 -0
  28. package/dist/{chunk-4I2BM2DT.js → chunk-T46BQOLY.js} +3 -3
  29. package/dist/{chunk-6FH4AXVY.js → chunk-T6DVUX4F.js} +1 -1
  30. package/dist/{chunk-PCPVTH6M.js → chunk-TAZZZKYO.js} +6 -4
  31. package/dist/{chunk-MLYSEZQB.js → chunk-UAWY6X7V.js} +43 -72
  32. package/dist/{chunk-Q7WGTQQ5.js → chunk-VAI5VOOI.js} +1 -1
  33. package/dist/{chunk-UJ6JZM65.js → chunk-WEZFTL4V.js} +1325 -513
  34. package/dist/{chunk-2YM3NI2E.js → chunk-X53JXRS4.js} +38 -44
  35. package/dist/{cloud-KRTZ2IL5.js → cloud-2CRDKOWQ.js} +1 -1
  36. package/dist/cloud-REV3DPJH.js +42 -0
  37. package/dist/{detect-OIE5V3VZ.js → detect-BBFT5GEU.js} +9 -6
  38. package/dist/{docker-VTR2GK5Y.js → docker-EG3QSE6I.js} +11 -8
  39. package/dist/{docker-edge-executor-22IBXNYJ.js → docker-edge-executor-E5TOTLMR.js} +8 -6
  40. package/dist/edge-import-YJVJUGYC.js +56 -0
  41. package/dist/{edge-preflight-GBNCPNVU.js → edge-preflight-QUW4BM7N.js} +15 -12
  42. package/dist/{ensure-container-edge-KPI2CVXJ.js → ensure-container-edge-FOEFQ3PI.js} +15 -10
  43. package/dist/{executor-7LXXZPBR.js → executor-NTWDJ3DB.js} +8 -6
  44. package/dist/host-channel-preflight-OMCZMMKR.js +54 -0
  45. package/dist/index.js +198 -76
  46. package/dist/{local-executor-WOFZD72X.js → local-executor-OQ2GRN7K.js} +3 -2
  47. package/dist/{nginx-KUFMLMAC.js → nginx-4U4C2XUS.js} +11 -8
  48. package/dist/{noop-NJ72F46T.js → noop-CLLJHCPL.js} +1 -1
  49. package/dist/{openresty-lua-ATTH35BE.js → openresty-lua-LLWT5XQW.js} +8 -5
  50. package/dist/{privilege-I4W3ZNZK.js → privilege-MNDVMDST.js} +6 -5
  51. package/dist/server/index.js +40049 -30378
  52. package/dist/server/migrations/0109_service_build_args.sql +9 -0
  53. package/dist/server/migrations/0110_service_deployment_host_ports.sql +18 -0
  54. package/dist/server/migrations/0111_host_port_claim.sql +381 -0
  55. package/dist/server/migrations/0112_backup_run_execution_lease.sql +12 -0
  56. package/dist/server/migrations/0113_docker_migration_execution_lease.sql +11 -0
  57. package/dist/server/migrations/0114_build_session_execution_lease_index.sql +6 -0
  58. package/dist/server/migrations/0115_orphan_target_key.sql +3 -0
  59. package/dist/server/migrations/0116_domain_ssl_challenge.sql +2 -0
  60. package/dist/server/migrations/meta/_journal.json +56 -0
  61. package/dist/setup-IFIMPQZJ.js +25 -0
  62. package/dist/{ssh-client-JBMA7SN4.js → ssh-client-2XWZCMGI.js} +3 -2
  63. package/dist/up-HQS7IR7H.js +61 -0
  64. package/package.json +1 -1
  65. package/dist/bare-OXJVW3AK.js +0 -25
  66. package/dist/chunk-LH2PLRQO.js +0 -434
  67. package/dist/chunk-SM26HDS2.js +0 -185
  68. package/dist/cloud-75VIT4KV.js +0 -37
  69. package/dist/edge-import-WJUB4M7F.js +0 -52
  70. package/dist/host-channel-preflight-KE6OONYK.js +0 -50
  71. package/dist/setup-XGLGUUFW.js +0 -22
  72. package/dist/up-K4BV7466.js +0 -57
@@ -7,42 +7,59 @@ import {
7
7
  resolveProjectDir,
8
8
  resolveStaticOutputPath,
9
9
  runLocalBuild
10
- } from "./chunk-YIMOCZBZ.js";
10
+ } from "./chunk-5DJOB7IX.js";
11
+ import {
12
+ isArtifactPathRef,
13
+ removeManagedArtifact
14
+ } from "./chunk-SK4FFCFR.js";
11
15
  import {
12
16
  BuildCancelledError,
13
17
  BuildLogger,
14
18
  detectBuildKillHint,
19
+ droppedRuntimeEnvMessage,
20
+ isExcludedDocRootEntry,
15
21
  killProcessesUnderDir,
16
22
  parseLogLevel,
17
23
  runBuildPipeline,
24
+ splitRuntimeEnv,
18
25
  sq,
19
26
  transferLocalDirectory
20
- } from "./chunk-LH2PLRQO.js";
27
+ } from "./chunk-AHJYP7DG.js";
21
28
  import {
22
29
  LocalExecutor
23
- } from "./chunk-6FH4AXVY.js";
30
+ } from "./chunk-T6DVUX4F.js";
24
31
  import {
25
32
  privilegedExecutor
26
- } from "./chunk-4I2BM2DT.js";
33
+ } from "./chunk-T46BQOLY.js";
27
34
  import {
28
35
  HOST_STATE_DIR,
29
36
  envOps,
30
37
  hostRefusal,
31
38
  opScript
32
- } from "./chunk-YMMQRE5J.js";
39
+ } from "./chunk-GNOMEMNI.js";
33
40
  import {
34
41
  ensureOwnedDir,
35
42
  resolveEnvironment
36
- } from "./chunk-UVNMQM7W.js";
43
+ } from "./chunk-NPSRGIHV.js";
37
44
  import {
38
45
  sq as sq2,
39
46
  wrapLocalBuildCommand
40
- } from "./chunk-ZFJXPCQP.js";
47
+ } from "./chunk-P7RCYELG.js";
48
+ import {
49
+ portOccupantDetails,
50
+ probeListeningPort
51
+ } from "./chunk-BO6CY4V2.js";
41
52
  import {
53
+ managedDeploymentUnitName,
42
54
  probeExec,
43
- probeListeningPort,
44
55
  withReason
45
- } from "./chunk-SM26HDS2.js";
56
+ } from "./chunk-D6HU4VUZ.js";
57
+ import {
58
+ formatDuration,
59
+ isRemoteConnectionError,
60
+ isRetryableRemoteConnectionError,
61
+ systemDebug
62
+ } from "./chunk-N5YZMO33.js";
46
63
  import {
47
64
  DeployError,
48
65
  LANGUAGES,
@@ -50,16 +67,13 @@ import {
50
67
  answered,
51
68
  appVolumeTargets,
52
69
  buildOutputTransferExcludes,
53
- formatDuration,
54
- isRemoteConnectionError,
55
- isRetryableRemoteConnectionError,
56
70
  missingOutputDirectoryMessage,
71
+ nodeBinPathExport,
57
72
  packageManagerEnsureCommand,
58
73
  refused,
59
74
  safeErrorMessage,
60
- shellQuote,
61
- systemDebug
62
- } from "./chunk-UJ6JZM65.js";
75
+ shellQuote
76
+ } from "./chunk-WEZFTL4V.js";
63
77
 
64
78
  // ../../packages/adapters/src/system/remote-journal.ts
65
79
  var OPSH_RUN_VERSION = 1;
@@ -1114,7 +1128,6 @@ async function sampleBareUsage(executor, pid, cgroupCandidates = []) {
1114
1128
  }
1115
1129
 
1116
1130
  // ../../packages/adapters/src/runtime/supervisor/systemd.ts
1117
- var UNIT_PREFIX = "openship";
1118
1131
  function escapeSystemdEnvValue(value) {
1119
1132
  return value.replace(/\\/g, "\\\\").replace(/"/g, '\\"').replace(/%/g, "%%").replace(/\n/g, "\\n").replace(/\r/g, "\\r").replace(/\t/g, "\\t");
1120
1133
  }
@@ -1126,7 +1139,7 @@ var SystemdSupervisor = class {
1126
1139
  name = "systemd";
1127
1140
  // ── Helpers ──────────────────────────────────────────────────────────
1128
1141
  unitName(deploymentId) {
1129
- return `${UNIT_PREFIX}-${deploymentId}.service`;
1142
+ return managedDeploymentUnitName(deploymentId);
1130
1143
  }
1131
1144
  unitPath(deploymentId) {
1132
1145
  return `/etc/systemd/system/${this.unitName(deploymentId)}`;
@@ -1172,7 +1185,7 @@ Restart=on-failure
1172
1185
  RestartSec=3
1173
1186
  StandardOutput=journal
1174
1187
  StandardError=journal
1175
- SyslogIdentifier=${UNIT_PREFIX}-${opts.deploymentId}
1188
+ SyslogIdentifier=${this.unitName(opts.deploymentId).replace(/\.service$/, "")}
1176
1189
 
1177
1190
  [Install]
1178
1191
  WantedBy=multi-user.target
@@ -1206,16 +1219,7 @@ WantedBy=multi-user.target
1206
1219
  throw new DeployError(
1207
1220
  `Port ${opts.port} is already in use` + (occupant ? ` by ${occupant.command}` : "") + ". Stop the existing process before deploying.",
1208
1221
  "PORT_IN_USE",
1209
- {
1210
- port: opts.port,
1211
- pid: occupant?.pid,
1212
- command: occupant?.command,
1213
- rawCommand: occupant?.rawCommand,
1214
- systemdUnit: occupant?.systemdUnit,
1215
- systemdDescription: occupant?.systemdDescription,
1216
- deploymentId: occupant?.deploymentId,
1217
- isManagedDeployment: occupant?.isManagedDeployment
1218
- }
1222
+ portOccupantDetails(opts.port, occupant)
1219
1223
  );
1220
1224
  }
1221
1225
  const msg = hint ? `Process exited immediately after start. Last output:
@@ -1424,7 +1428,7 @@ var NohupSupervisor = class {
1424
1428
  throw new DeployError(
1425
1429
  `Port ${opts.port} is already in use` + (occupant ? ` by ${occupant.command}` : "") + ". Stop the existing process before deploying.",
1426
1430
  "PORT_IN_USE",
1427
- { port: opts.port, pid: occupant?.pid, command: occupant?.command }
1431
+ portOccupantDetails(opts.port, occupant)
1428
1432
  );
1429
1433
  }
1430
1434
  const msg = hint ? `Process exited immediately after spawn. Last output:
@@ -1604,6 +1608,16 @@ var BareRuntime = class {
1604
1608
  async inContainerExecutor() {
1605
1609
  return this.executor;
1606
1610
  }
1611
+ /**
1612
+ * Resolve a retained application release for an env-only refresh.
1613
+ * Deployment ids originate in our database, but reject path-shaped input at
1614
+ * this boundary so an imported/corrupt row cannot escape releases/.
1615
+ */
1616
+ async retainedReleaseArtifact(deploymentId) {
1617
+ if (!deploymentId || deploymentId.includes("/") || deploymentId.includes("\\")) return null;
1618
+ const path = this.releaseDir(deploymentId);
1619
+ return await this.executor.exists(path).catch(() => false) ? path : null;
1620
+ }
1607
1621
  /** Get or lazily initialise the process supervisor. */
1608
1622
  async supervisor() {
1609
1623
  if (this._supervisor) return this._supervisor;
@@ -1690,6 +1704,7 @@ var BareRuntime = class {
1690
1704
  async promoteBuildArtifact(artifactPath, deploymentId, previousDeploymentId) {
1691
1705
  const releaseDir = this.releaseDir(deploymentId);
1692
1706
  if (artifactPath === releaseDir) return releaseDir;
1707
+ const consumeSource = !artifactPath.startsWith(`${this.workDir}/releases/`);
1693
1708
  await ensureOwnedDir(this.executor, `${this.workDir}/releases`);
1694
1709
  await this.executor.rm(releaseDir);
1695
1710
  const previousReleaseDir = previousDeploymentId ? this.releaseDir(previousDeploymentId) : void 0;
@@ -1700,7 +1715,7 @@ var BareRuntime = class {
1700
1715
  `deploy:${deploymentId}:promote-rsync`,
1701
1716
  `rsync -a --delete --link-dest=${sq(previousReleaseDir)} ${sq(artifactPath)}/ ${sq(releaseDir)}/`
1702
1717
  );
1703
- await this.executor.rm(artifactPath).catch(() => {
1718
+ if (consumeSource) await this.executor.rm(artifactPath).catch(() => {
1704
1719
  });
1705
1720
  return releaseDir;
1706
1721
  } catch {
@@ -1708,6 +1723,14 @@ var BareRuntime = class {
1708
1723
  });
1709
1724
  }
1710
1725
  }
1726
+ if (!consumeSource) {
1727
+ await execReliable(
1728
+ this.executor,
1729
+ `deploy:${deploymentId}:promote-copy`,
1730
+ `mkdir -p ${sq(releaseDir)} && cp -a ${sq(artifactPath)}/. ${sq(releaseDir)}/`
1731
+ );
1732
+ return releaseDir;
1733
+ }
1711
1734
  await execReliable(
1712
1735
  this.executor,
1713
1736
  `deploy:${deploymentId}:promote-mv`,
@@ -1947,19 +1970,29 @@ var BareRuntime = class {
1947
1970
  ) : stagedDir;
1948
1971
  await this.linkPersistentPaths(workDir, config.projectId, config.volumes, _onLog);
1949
1972
  const sv = await this.supervisor();
1973
+ const projectEnv = splitRuntimeEnv(
1974
+ Object.fromEntries(Object.entries(config.envVars ?? {}).map(([k, v]) => [k, String(v)]))
1975
+ );
1976
+ if (projectEnv.dropped.length > 0) {
1977
+ _onLog?.({
1978
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
1979
+ level: "warn",
1980
+ message: droppedRuntimeEnvMessage(projectEnv.dropped)
1981
+ });
1982
+ }
1950
1983
  const env = {
1951
- ...Object.fromEntries(
1952
- Object.entries(config.envVars ?? {}).map(([k, v]) => [k, String(v)])
1953
- ),
1984
+ ...Object.fromEntries(projectEnv.entries),
1954
1985
  PORT: String(config.port),
1955
1986
  NODE_ENV: config.environment === "production" ? "production" : "development"
1956
1987
  };
1988
+ const startBinPath = nodeBinPathExport(config.packageManager, [workDir]);
1989
+ const resolvedStart = config.startCommand || "npm start";
1957
1990
  try {
1958
1991
  await sv.deploy({
1959
1992
  deploymentId: config.deploymentId,
1960
1993
  projectId: config.projectId,
1961
1994
  workDir,
1962
- startCommand: config.startCommand || "npm start",
1995
+ startCommand: startBinPath ? `${startBinPath} && ${resolvedStart}` : resolvedStart,
1963
1996
  port: config.port,
1964
1997
  env
1965
1998
  });
@@ -1979,13 +2012,40 @@ var BareRuntime = class {
1979
2012
  };
1980
2013
  }
1981
2014
  async deployStatic(config) {
1982
- const stagedDir = config.imageRef ?? this.projectDir(config.projectId);
1983
- const workDir = config.imageRef ? await this.promoteBuildArtifact(
1984
- stagedDir,
1985
- config.deploymentId,
1986
- config.previousDeploymentId
1987
- ) : stagedDir;
1988
- const staticRoot = resolveStaticOutputPath(workDir, config.outputDirectory);
2015
+ const workDir = await this.promoteStaticRelease({
2016
+ artifactPath: config.imageRef ?? this.projectDir(config.projectId),
2017
+ releaseId: config.deploymentId,
2018
+ previousReleaseId: config.previousDeploymentId,
2019
+ outputDirectory: config.outputDirectory,
2020
+ promote: Boolean(config.imageRef)
2021
+ });
2022
+ return {
2023
+ deploymentId: config.deploymentId,
2024
+ containerId: workDir,
2025
+ status: "running"
2026
+ };
2027
+ }
2028
+ /**
2029
+ * Promote an extracted static build into a stable release directory and PROVE it
2030
+ * can serve, returning the release dir.
2031
+ *
2032
+ * Split out of `deployStatic` so the compose/monorepo static path reuses the
2033
+ * identical promote + validation instead of registering a vhost straight at the
2034
+ * `.builds/<session>` staging dir. Two things came from that shortcut: the
2035
+ * public doc-root of a compose static sub-app was a per-build-session scratch
2036
+ * directory (nothing owned its lifetime, so superseded copies accumulated and
2037
+ * any sweep of `.builds` would take the live site down), and it skipped the only
2038
+ * HARD deploy-time output gate we have — so an extract that produced nothing
2039
+ * deployed green and 404'd.
2040
+ *
2041
+ * `releaseId` is the release's identity, not necessarily a deployment id: the
2042
+ * compose path passes `<deploymentId>-<serviceId>` so each static sub-app owns
2043
+ * its own release dir under one deployment.
2044
+ */
2045
+ async promoteStaticRelease(opts) {
2046
+ const stagedDir = opts.artifactPath;
2047
+ const workDir = opts.promote === false ? stagedDir : await this.promoteBuildArtifact(stagedDir, opts.releaseId, opts.previousReleaseId);
2048
+ const staticRoot = resolveStaticOutputPath(workDir, opts.outputDirectory);
1989
2049
  const abort = async (message) => {
1990
2050
  if (workDir !== stagedDir) {
1991
2051
  await this.executor.rm(workDir).catch(() => {
@@ -1994,18 +2054,47 @@ var BareRuntime = class {
1994
2054
  throw new Error(message);
1995
2055
  };
1996
2056
  if (!await this.executor.exists(staticRoot)) {
1997
- return abort(missingOutputDirectoryMessage(config.outputDirectory));
2057
+ return abort(missingOutputDirectoryMessage(opts.outputDirectory));
2058
+ }
2059
+ if (staticRoot === workDir) {
2060
+ const undeleted = await this.pruneBuildFilesFromDocRoot(staticRoot);
2061
+ if (undeleted.length > 0) {
2062
+ return abort(
2063
+ `Refusing to publish ${staticRoot}: could not remove ${undeleted.join(", ")} from the document root. Serving them would expose the repository (and, for a private repo, the access token in .git/config) at the site's public URL.`
2064
+ );
2065
+ }
1998
2066
  }
1999
2067
  if (await this.isEmptyDir(staticRoot)) {
2000
2068
  return abort(
2001
- `The output directory "${config.outputDirectory || "."}" is empty \u2014 the build produced no files to serve, so every request would 404. Check the build command and the Output Directory setting.`
2069
+ `The output directory "${opts.outputDirectory || "."}" is empty \u2014 the build produced no files to serve, so every request would 404. Check the build command and the Output Directory setting.`
2002
2070
  );
2003
2071
  }
2004
- return {
2005
- deploymentId: config.deploymentId,
2006
- containerId: workDir,
2007
- status: "running"
2008
- };
2072
+ return workDir;
2073
+ }
2074
+ /**
2075
+ * Remove top-level build inputs from a doc root, returning the names it could NOT
2076
+ * remove (empty on success, and on "there was nothing to remove").
2077
+ *
2078
+ * Reuses `isExcludedDocRootEntry` rather than re-listing the names, because the
2079
+ * Docker sandbox already owns that list and two copies of "what must not be
2080
+ * served" is precisely how the bare path came to serve `.git`.
2081
+ *
2082
+ * An unreadable listing returns empty — a probe that cannot read must not fail a
2083
+ * deploy — but a listed entry that survives its `rm` is reported, because the
2084
+ * caller must refuse to publish rather than expose a token.
2085
+ */
2086
+ async pruneBuildFilesFromDocRoot(root) {
2087
+ const listed = await this.executor.exec(`ls -A ${sq(root)} 2>/dev/null; true`).catch(() => null);
2088
+ if (listed === null) return [];
2089
+ const doomed = listed.split("\n").map((l) => l.trim()).filter(Boolean).filter(isExcludedDocRootEntry);
2090
+ if (doomed.length === 0) return [];
2091
+ await this.executor.exec(`rm -rf ${doomed.map((n) => sq(`${root}/${n}`)).join(" ")}`).catch(() => {
2092
+ });
2093
+ const survivors = [];
2094
+ for (const name of doomed) {
2095
+ if (await this.executor.exists(`${root}/${name}`)) survivors.push(name);
2096
+ }
2097
+ return survivors;
2009
2098
  }
2010
2099
  /**
2011
2100
  * Is this an existing directory with no entries? Any inconclusive answer (not a
@@ -2033,8 +2122,8 @@ var BareRuntime = class {
2033
2122
  await sv.restart(containerId);
2034
2123
  }
2035
2124
  async destroy(containerId) {
2036
- if (containerId.includes("/")) {
2037
- await this.executor.rm(containerId);
2125
+ if (isArtifactPathRef(containerId)) {
2126
+ await removeManagedArtifact(this.executor, containerId);
2038
2127
  return;
2039
2128
  }
2040
2129
  const sv = await this.supervisor();
@@ -2078,15 +2167,21 @@ var BareRuntime = class {
2078
2167
  }
2079
2168
  }
2080
2169
  async purge(deployment) {
2170
+ const failures = [];
2081
2171
  if (deployment.containerId) {
2082
- try {
2083
- await this.destroy(deployment.containerId);
2084
- } catch {
2085
- }
2172
+ await this.destroy(deployment.containerId).catch((err) => failures.push(err));
2086
2173
  }
2087
- try {
2088
- await this.executor.rm(this.releaseDir(deployment.id));
2089
- } catch {
2174
+ const releaseDir = this.releaseDir(deployment.id);
2175
+ if (releaseDir !== deployment.containerId) {
2176
+ await removeManagedArtifact(this.executor, releaseDir).catch(
2177
+ (err) => failures.push(err)
2178
+ );
2179
+ }
2180
+ if (failures.length === 1) throw failures[0];
2181
+ if (failures.length > 1) {
2182
+ throw new Error(
2183
+ failures.map((err) => err instanceof Error ? err.message : String(err)).join("; ")
2184
+ );
2090
2185
  }
2091
2186
  }
2092
2187
  // ── Observability ──────────────────────────────────────────────────────
@@ -9,7 +9,7 @@ import {
9
9
  firewallPersists,
10
10
  refused,
11
11
  shellQuote
12
- } from "./chunk-UJ6JZM65.js";
12
+ } from "./chunk-WEZFTL4V.js";
13
13
 
14
14
  // ../../packages/adapters/src/system/environment-ops.ts
15
15
  function opScript(steps) {