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
@@ -5,38 +5,46 @@ const require = __ospCreateRequire(import.meta.url);
5
5
  import {
6
6
  checkGit,
7
7
  installGit
8
- } from "./chunk-A4GXYE4L.js";
8
+ } from "./chunk-NIYD7MD2.js";
9
9
  import {
10
10
  createDockerBuildContext,
11
- normalizeDockerRelativePath,
12
- resolveDockerfileCandidates,
13
- resolveWithinDirectory
14
- } from "./chunk-LUKYPVJ4.js";
11
+ resolveDockerBuildArgs
12
+ } from "./chunk-6GU6JSK6.js";
15
13
  import {
16
14
  prepareStackOutput,
17
15
  resolveProjectDir,
18
16
  resolveStaticOutputPath,
19
17
  runLocalBuild
20
- } from "./chunk-YIMOCZBZ.js";
18
+ } from "./chunk-5DJOB7IX.js";
21
19
  import {
22
20
  BuildLogger,
21
+ dockerBuildContextDirectory,
22
+ droppedRuntimeEnvMessage,
23
23
  injectGitToken,
24
+ normalizeDockerRelativePath,
25
+ resolveContextDockerfileCandidates,
26
+ resolveDockerfileCandidates,
27
+ resolveWithinDirectory,
24
28
  runBuildPipeline,
29
+ splitRuntimeEnv,
25
30
  sq,
26
31
  transferLocalDirectory
27
- } from "./chunk-LH2PLRQO.js";
32
+ } from "./chunk-AHJYP7DG.js";
33
+ import {
34
+ isRuntimeNotFoundError
35
+ } from "./chunk-N5YZMO33.js";
28
36
  import {
29
37
  PRICING,
30
38
  STACKS,
31
39
  SYSTEM,
32
40
  TRANSFER_EXCLUDES,
33
41
  buildOutputTransferExcludes,
34
- isRuntimeNotFoundError,
35
42
  missingOutputDirectoryMessage,
43
+ nodeBinPathExport,
36
44
  packageManagerEnsureCommand,
37
45
  safeErrorMessage,
38
46
  shellSplitWords
39
- } from "./chunk-UJ6JZM65.js";
47
+ } from "./chunk-WEZFTL4V.js";
40
48
 
41
49
  // ../../packages/adapters/src/runtime/cloud.ts
42
50
  import { readFile } from "fs/promises";
@@ -719,9 +727,11 @@ function restartPolicyForWorkload(policy) {
719
727
  return "always";
720
728
  }
721
729
  function resolveCloudWorkloadCmd(opts) {
722
- const { commandArgv, startCommand, workdir } = opts;
730
+ const { commandArgv, startCommand, workdir, binPathExport } = opts;
723
731
  if (commandArgv != null) return commandArgv.length > 0 ? commandArgv : void 0;
724
- return startCommand ? ["sh", "-c", `cd ${sq(workdir)} && ${startCommand}`] : void 0;
732
+ if (!startCommand) return void 0;
733
+ const body = `cd ${sq(workdir)} && ${startCommand}`;
734
+ return ["sh", "-c", binPathExport ? `${binPathExport} && ${body}` : body];
725
735
  }
726
736
  function exposeTarget(port, serviceName, slug, domain = SYSTEM.DOMAINS.CLOUD_DOMAIN) {
727
737
  const service = `service "${serviceName}" on port ${port}`;
@@ -1320,6 +1330,14 @@ function resolveCopyDestinationPath(copy, sourcePath, destination, destinationIs
1320
1330
  const basename = posix.basename(sourcePath.replace(/\/+$/g, "")) || ".";
1321
1331
  return posix.normalize(posix.join(destination, basename));
1322
1332
  }
1333
+ function cloudBuildContextPath(config) {
1334
+ return dockerBuildContextDirectory(config) || normalizeDockerRelativePath(config.rootDirectory);
1335
+ }
1336
+ function compileCloudDockerfilePlan(source, config) {
1337
+ return compileDockerfileToWorkspacePlan(source, {
1338
+ buildArgs: resolveDockerBuildArgs(config)
1339
+ });
1340
+ }
1323
1341
  var PAGE_CONTAINER_PREFIX = "page:";
1324
1342
  async function provisionCloudWorkspace(client, config, logger) {
1325
1343
  logger?.log(`Creating workspace from image "${config.image}"...
@@ -1338,15 +1356,22 @@ async function provisionCloudWorkspace(client, config, logger) {
1338
1356
  }
1339
1357
  });
1340
1358
  } catch (err) {
1341
- logger?.log(`Failed to create workspace from image "${config.image}": ${formatCloudError(err)}
1342
- `, "error");
1359
+ logger?.log(
1360
+ `Failed to create workspace from image "${config.image}": ${formatCloudError(err)}
1361
+ `,
1362
+ "error"
1363
+ );
1343
1364
  throw err;
1344
1365
  }
1345
1366
  const ws = client.workspace(wsData.id);
1346
1367
  try {
1347
1368
  if (config.mode === "temporary" && config.ttl) {
1348
1369
  try {
1349
- await ws.lifecycle.makeTemporary({ ttl: config.ttl, ttl_action: "remove", remove_on_exit: true });
1370
+ await ws.lifecycle.makeTemporary({
1371
+ ttl: config.ttl,
1372
+ ttl_action: "remove",
1373
+ remove_on_exit: true
1374
+ });
1350
1375
  } catch {
1351
1376
  }
1352
1377
  }
@@ -1386,6 +1411,7 @@ var CloudRuntime = class {
1386
1411
  name = "cloud";
1387
1412
  capabilities = /* @__PURE__ */ new Set([
1388
1413
  "build",
1414
+ "prebuiltImage",
1389
1415
  "deploy",
1390
1416
  "multiServiceDeploy",
1391
1417
  "stop",
@@ -1537,6 +1563,94 @@ var CloudRuntime = class {
1537
1563
  this.activeBuilds.get(sessionId)?.workspaceIds.delete(workspaceId);
1538
1564
  }
1539
1565
  // ── Build lifecycle ────────────────────────────────────────────────────
1566
+ /**
1567
+ * Materialize a prebuilt container image as the same opaque artifact Cloud's
1568
+ * deploy path already understands: a temporary workspace id.
1569
+ *
1570
+ * Unlike a source build, no command is executed in the workspace. Creating it
1571
+ * directly from the application image preserves the image's own ENTRYPOINT,
1572
+ * CMD and WORKDIR; deploy keeps those defaults when `prebuiltImage` is set.
1573
+ */
1574
+ async prepareImage(config, logger) {
1575
+ const log = logger ?? new BuildLogger();
1576
+ const startedAt = Date.now();
1577
+ const activeBuild = this.createActiveBuild(config.sessionId);
1578
+ let workspaceId;
1579
+ const discardWorkspace = async () => {
1580
+ if (!workspaceId) return;
1581
+ await this.ws(workspaceId).delete().catch(() => {
1582
+ });
1583
+ this.builtArtifacts.delete(workspaceId);
1584
+ this.untrackActiveBuildWorkspace(config.sessionId, workspaceId);
1585
+ };
1586
+ const cancelled = async () => {
1587
+ await discardWorkspace();
1588
+ log.step("build", "failed", "Image preparation cancelled");
1589
+ return {
1590
+ sessionId: config.sessionId,
1591
+ status: "cancelled",
1592
+ // Keep the ref even after our best-effort delete: the pipeline can make
1593
+ // an idempotent cleanup retry if that delete failed mid-cancellation.
1594
+ imageRef: workspaceId,
1595
+ durationMs: Date.now() - startedAt,
1596
+ artifactOwned: true
1597
+ };
1598
+ };
1599
+ try {
1600
+ if (activeBuild.abort.signal.aborted) return await cancelled();
1601
+ const imageRef = config.imageRef.trim();
1602
+ if (!imageRef) throw new Error("Prebuilt image reference is required");
1603
+ log.step("build", "running", `Creating workspace from prebuilt image ${imageRef}`);
1604
+ const provisioned = await this.provisionWorkspace(
1605
+ {
1606
+ name: config.slug ?? `image-${config.projectId.slice(0, 20)}`,
1607
+ image: imageRef,
1608
+ mode: "temporary",
1609
+ resources: config.resources,
1610
+ env: config.envVars,
1611
+ ttl: "15m"
1612
+ },
1613
+ log
1614
+ );
1615
+ workspaceId = provisioned.workspaceId;
1616
+ this.trackActiveBuildWorkspace(config.sessionId, workspaceId);
1617
+ if (activeBuild.abort.signal.aborted) return await cancelled();
1618
+ this.builtArtifacts.set(workspaceId, {
1619
+ workspaceId,
1620
+ runtime: {
1621
+ workdir: "/",
1622
+ env: {},
1623
+ exposedPorts: [],
1624
+ baseImage: imageRef
1625
+ }
1626
+ });
1627
+ log.step("build", "completed", `Prebuilt image workspace ${workspaceId} is ready`);
1628
+ return {
1629
+ sessionId: config.sessionId,
1630
+ status: "deploying",
1631
+ imageRef: workspaceId,
1632
+ durationMs: Date.now() - startedAt,
1633
+ artifactOwned: true
1634
+ };
1635
+ } catch (err) {
1636
+ if (activeBuild.abort.signal.aborted) return await cancelled();
1637
+ await discardWorkspace();
1638
+ const message = formatCloudError(err);
1639
+ log.step("build", "failed", `Image preparation failed: ${message}`);
1640
+ return {
1641
+ sessionId: config.sessionId,
1642
+ status: "failed",
1643
+ // Same retry contract as cancellation. Undefined means provisioning
1644
+ // failed before Oblien handed us an owned workspace.
1645
+ imageRef: workspaceId,
1646
+ durationMs: Date.now() - startedAt,
1647
+ errorMessage: `Image preparation failed: ${message}`,
1648
+ artifactOwned: true
1649
+ };
1650
+ } finally {
1651
+ this.activeBuilds.delete(config.sessionId);
1652
+ }
1653
+ }
1540
1654
  async build(config, logger) {
1541
1655
  const log = logger ?? new BuildLogger();
1542
1656
  const activeBuild = this.createActiveBuild(config.sessionId);
@@ -1595,7 +1709,9 @@ var CloudRuntime = class {
1595
1709
  resolveProjectDir(buildDir, config.rootDirectory)
1596
1710
  );
1597
1711
  if (selfContained) {
1598
- log.log("Detected self-contained build output \u2014 uploading the bundle (no install on cloud).\n");
1712
+ log.log(
1713
+ "Detected self-contained build output \u2014 uploading the bundle (no install on cloud).\n"
1714
+ );
1599
1715
  await transferLocalDirectory(
1600
1716
  selfContained.bundleDir,
1601
1717
  { kind: "cloud-runtime", runtime: rt, path: "/app" },
@@ -1633,7 +1749,11 @@ var CloudRuntime = class {
1633
1749
  log.log("Installing production dependencies on cloud...\n");
1634
1750
  const pmEnsure = packageManagerEnsureCommand(config.packageManager);
1635
1751
  const fullInstall = pmEnsure ? `${pmEnsure} && ${installCmd}` : installCmd;
1636
- await this.execAndStream(rt, ["sh", "-c", `cd /app && ${fullInstall}`], log.callback);
1752
+ await this.execAndStream(
1753
+ rt,
1754
+ ["sh", "-c", `cd /app && ${fullInstall}`],
1755
+ log.callback
1756
+ );
1637
1757
  }
1638
1758
  }
1639
1759
  });
@@ -1718,7 +1838,7 @@ var CloudRuntime = class {
1718
1838
  try {
1719
1839
  log.log("Build strategy: Dockerfile plan (build in Oblien workspaces)\n");
1720
1840
  source = await this.resolveDockerfileBuildSource(config, log);
1721
- const plan = compileDockerfileToWorkspacePlan(source.dockerfile);
1841
+ const plan = compileCloudDockerfilePlan(source.dockerfile, config);
1722
1842
  const blocking = plan.diagnostics.filter(
1723
1843
  (item) => item.severity === "error" || item.severity === "unsupported"
1724
1844
  );
@@ -1792,7 +1912,7 @@ var CloudRuntime = class {
1792
1912
  logger.log("Using Dockerfile content from source metadata.\n");
1793
1913
  return {
1794
1914
  kind: "remote",
1795
- contextRelativePath: normalizeDockerRelativePath(config.rootDirectory),
1915
+ contextRelativePath: cloudBuildContextPath(config),
1796
1916
  dockerfile: config.dockerfileContent,
1797
1917
  cleanup: async () => {
1798
1918
  }
@@ -1807,9 +1927,9 @@ var CloudRuntime = class {
1807
1927
  requireRepositoryDockerfile: true,
1808
1928
  onLog: logger.callback
1809
1929
  });
1810
- const dockerfilePath = resolveWithinDirectory(context.contextDir, context.dockerfileName);
1930
+ const dockerfilePath = resolveWithinDirectory(context.buildContextDir, context.dockerfileName);
1811
1931
  const dockerfile = await readFile(dockerfilePath, "utf-8");
1812
- const contextRoot = resolveWithinDirectory(context.contextDir, context.rootDirectory);
1932
+ const contextRoot = context.contextSubdir ? context.buildContextDir : resolveWithinDirectory(context.contextDir, context.rootDirectory);
1813
1933
  return {
1814
1934
  kind: "local",
1815
1935
  contextRoot,
@@ -1818,7 +1938,10 @@ var CloudRuntime = class {
1818
1938
  };
1819
1939
  }
1820
1940
  async resolveRemoteDockerfileBuildSource(config, logger) {
1821
- const candidates = resolveDockerfileCandidates(config.rootDirectory, config.dockerfilePath);
1941
+ const contextSubdir = dockerBuildContextDirectory(config);
1942
+ const candidates = contextSubdir ? resolveContextDockerfileCandidates(contextSubdir, config.dockerfilePath).map(
1943
+ (candidate) => `${contextSubdir}/${candidate}`
1944
+ ) : resolveDockerfileCandidates(config.rootDirectory, config.dockerfilePath);
1822
1945
  const sourceLabel = config.commitSha ? `${config.branch}@${config.commitSha.slice(0, 7)}` : config.branch;
1823
1946
  const checkoutRef = config.commitSha ?? "FETCH_HEAD";
1824
1947
  const sourceDir = "/openship/dockerfile-source";
@@ -1867,11 +1990,9 @@ var CloudRuntime = class {
1867
1990
  logger.log(`Checking Dockerfile candidate: ${candidate}
1868
1991
  `);
1869
1992
  const dockerfile = await executor.exec(
1870
- [
1871
- `cd ${sq(sourceDir)}`,
1872
- `test -f ${sq(candidate)}`,
1873
- `cat ${sq(candidate)}`
1874
- ].join(" && "),
1993
+ [`cd ${sq(sourceDir)}`, `test -f ${sq(candidate)}`, `cat ${sq(candidate)}`].join(
1994
+ " && "
1995
+ ),
1875
1996
  { timeout: 3e4 }
1876
1997
  ).catch(() => null);
1877
1998
  if (dockerfile !== null) {
@@ -1881,7 +2002,7 @@ var CloudRuntime = class {
1881
2002
  sourceWorkspaceId = void 0;
1882
2003
  return {
1883
2004
  kind: "remote",
1884
- contextRelativePath: normalizeDockerRelativePath(config.rootDirectory),
2005
+ contextRelativePath: cloudBuildContextPath(config),
1885
2006
  dockerfile,
1886
2007
  cleanup: async () => {
1887
2008
  await this.ws(workspaceId).delete().catch(() => {
@@ -2063,6 +2184,25 @@ var CloudRuntime = class {
2063
2184
  const contextSource = contextRelativePath ? joinWorkspacePath(repoRoot, contextRelativePath) : contextRoot;
2064
2185
  const prepareContextCommands = contextRelativePath ? [
2065
2186
  `echo "Copying Dockerfile context ${contextRelativePath}..."`,
2187
+ // `cp -a` FOLLOWS a symlinked source directory, so a repo that tracks
2188
+ // `svc -> /` and declares `build: ./svc` would copy the workspace's root
2189
+ // filesystem into its own build context. The lexical `..` refusal above
2190
+ // cannot see that; only a resolved path can. Same class as
2191
+ // GHSA-443m-7g52-94w8, reachable through the build context rather than
2192
+ // rootDirectory, and asserted here for the same reason the docker paths
2193
+ // assert it: at the sink.
2194
+ // `pwd -P` rather than `readlink -f`: it is a POSIX shell builtin, so it
2195
+ // exists in every workspace image, and it answers both questions at once —
2196
+ // the `cd` fails when the context is not a directory, and the resolved path
2197
+ // reveals a symlink that left the checkout.
2198
+ // BOTH sides resolved, and an unresolvable either side is fatal: an empty
2199
+ // `__root` would turn the prefix test into `"/"*`, which every absolute path
2200
+ // matches — a containment check that passes vacuously is worse than none.
2201
+ // `|| __x=""` keeps `set -e` from killing the script before the message.
2202
+ `__root="$(cd ${sq(repoRoot)} && pwd -P)" || __root=""`,
2203
+ `__ctx="$(cd ${sq(contextSource)} 2>/dev/null && pwd -P)" || __ctx=""`,
2204
+ `if [ -z "$__root" ] || [ -z "$__ctx" ]; then echo "Build context ${contextRelativePath} is not a directory in this repository." >&2; exit 1; fi`,
2205
+ `case "$__ctx/" in "$__root/"*) ;; *) echo "Build context ${contextRelativePath} resolves outside this repository." >&2; exit 1 ;; esac`,
2066
2206
  `mkdir -p ${sq(contextRoot)}`,
2067
2207
  `cp -a ${sq(`${contextSource}/.`)} ${sq(contextRoot)}/`
2068
2208
  ] : ['echo "Using repository root as Dockerfile context."'];
@@ -2082,8 +2222,10 @@ var CloudRuntime = class {
2082
2222
  ...prepareContextCommands,
2083
2223
  'echo "Dockerfile context prepared."'
2084
2224
  ].join("\n");
2085
- logger.log(`Preparing Dockerfile build workspace for repository clone (branch: ${config.branch})...
2086
- `);
2225
+ logger.log(
2226
+ `Preparing Dockerfile build workspace for repository clone (branch: ${config.branch})...
2227
+ `
2228
+ );
2087
2229
  await this.ensureWorkspaceGit(targetRuntime, logger, "Dockerfile build workspace");
2088
2230
  logger.log(`Cloning Dockerfile context in build workspace (branch: ${config.branch})...
2089
2231
  `);
@@ -2336,8 +2478,8 @@ var CloudRuntime = class {
2336
2478
  }
2337
2479
  }
2338
2480
  const builtArtifact = this.builtArtifacts.get(workspaceId);
2339
- const prodPaths = config.productionPaths;
2340
- const workDir = builtArtifact?.runtime.workdir ?? (prodPaths?.length ? "/app/production" : "/app");
2481
+ const prodPaths = config.prebuiltImage ? void 0 : config.productionPaths;
2482
+ const workDir = config.prebuiltImage ? "/" : builtArtifact?.runtime.workdir ?? (prodPaths?.length ? "/app/production" : "/app");
2341
2483
  if (prodPaths?.length) {
2342
2484
  try {
2343
2485
  const rt = await ws.runtime();
@@ -2384,23 +2526,48 @@ fi`;
2384
2526
  );
2385
2527
  }
2386
2528
  }
2387
- const startCommand = builtArtifact?.runtime.startCommand || config.startCommand || "npm start";
2529
+ const startCommand = builtArtifact?.runtime.startCommand || config.startCommand || (config.prebuiltImage ? void 0 : "npm start");
2530
+ const projectEnv = splitRuntimeEnv(config.envVars);
2531
+ if (projectEnv.dropped.length > 0) {
2532
+ onLog?.({
2533
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
2534
+ level: "warn",
2535
+ message: droppedRuntimeEnvMessage(projectEnv.dropped)
2536
+ });
2537
+ }
2388
2538
  const envArray = toEnvArray2({
2389
2539
  ...builtArtifact?.runtime.env ?? {},
2390
- ...config.envVars
2540
+ ...Object.fromEntries(projectEnv.entries)
2391
2541
  });
2392
2542
  const restartPolicy = config.restartPolicy === "no" ? "never" : config.restartPolicy ?? "always";
2393
- try {
2394
- await ws.workloads.create({
2395
- name: "app",
2396
- cmd: ["sh", "-c", `cd ${workDir} && ${startCommand}`],
2397
- working_dir: workDir,
2398
- env: [...envArray, `PORT=${config.port}`],
2399
- restart_policy: restartPolicy,
2400
- max_restarts: 10
2543
+ const workloadCmd = resolveCloudWorkloadCmd({
2544
+ startCommand,
2545
+ workdir: workDir,
2546
+ binPathExport: builtArtifact || config.prebuiltImage ? "" : nodeBinPathExport(config.packageManager, [workDir, "/app"])
2547
+ });
2548
+ if (workloadCmd) {
2549
+ try {
2550
+ if (config.prebuiltImage) {
2551
+ await ws.workloads.delete("app").catch(() => {
2552
+ });
2553
+ }
2554
+ await ws.workloads.create({
2555
+ name: "app",
2556
+ cmd: workloadCmd,
2557
+ working_dir: workDir,
2558
+ env: [...envArray, `PORT=${config.port}`],
2559
+ restart_policy: restartPolicy,
2560
+ max_restarts: 10
2561
+ });
2562
+ } catch (err) {
2563
+ throw new Error(`Failed to create workload: ${err instanceof Error ? err.message : err}`);
2564
+ }
2565
+ } else {
2566
+ log({
2567
+ timestamp: now2(),
2568
+ level: "info",
2569
+ message: "Using the prebuilt image's default command and working directory.\n"
2401
2570
  });
2402
- } catch (err) {
2403
- throw new Error(`Failed to create workload: ${err instanceof Error ? err.message : err}`);
2404
2571
  }
2405
2572
  let url;
2406
2573
  const primaryEndpoint = primaryPublicEndpoint(config);
@@ -2538,7 +2705,9 @@ fi`;
2538
2705
  pg = result.page;
2539
2706
  } catch (err) {
2540
2707
  if (isOutputPathError(err)) throw outputDirError();
2541
- throw new Error(`Failed to create static page for slug "${pageSlug}": ${safeErrorMessage(err)}`);
2708
+ throw new Error(
2709
+ `Failed to create static page for slug "${pageSlug}": ${safeErrorMessage(err)}`
2710
+ );
2542
2711
  }
2543
2712
  return { ...pg, url: void 0 };
2544
2713
  };
@@ -2553,7 +2722,9 @@ fi`;
2553
2722
  });
2554
2723
  page = result.page;
2555
2724
  } catch (err) {
2556
- throw new Error(`Failed to redeploy static page for slug "${pageSlug}": ${safeErrorMessage(err)}`);
2725
+ throw new Error(
2726
+ `Failed to redeploy static page for slug "${pageSlug}": ${safeErrorMessage(err)}`
2727
+ );
2557
2728
  }
2558
2729
  } else {
2559
2730
  if (existingPage) {
@@ -2695,15 +2866,7 @@ fi`;
2695
2866
  }
2696
2867
  async purge(deployment) {
2697
2868
  if (deployment.containerId?.startsWith(PAGE_CONTAINER_PREFIX)) {
2698
- const slug = deployment.containerId.slice(5);
2699
- try {
2700
- if (this.adminProxy?.deletePage) {
2701
- await this.adminProxy.deletePage(slug);
2702
- } else {
2703
- await this.client.pages.delete(slug);
2704
- }
2705
- } catch {
2706
- }
2869
+ await this.destroy(deployment.containerId);
2707
2870
  return;
2708
2871
  }
2709
2872
  if (!deployment.containerId) return;
@@ -2717,10 +2880,7 @@ fi`;
2717
2880
  err instanceof Error ? err.message : err
2718
2881
  );
2719
2882
  }
2720
- try {
2721
- await this.destroy(deployment.containerId);
2722
- } catch {
2723
- }
2883
+ await this.destroy(deployment.containerId);
2724
2884
  }
2725
2885
  // ── Observability ──────────────────────────────────────────────────────
2726
2886
  async getContainerInfo(containerId) {
@@ -3183,6 +3343,7 @@ function clampShellWindow(value, fallback, min, max) {
3183
3343
  }
3184
3344
 
3185
3345
  export {
3346
+ compileCloudDockerfilePlan,
3186
3347
  PAGE_CONTAINER_PREFIX,
3187
3348
  provisionCloudWorkspace,
3188
3349
  HostBuildForbiddenError,
@@ -0,0 +1,111 @@
1
+ #!/usr/bin/env sh
2
+ ":" //# ; exec "$(command -v node || command -v bun)" "$0" "$@"
3
+ import { createRequire as __ospCreateRequire } from "node:module";
4
+ const require = __ospCreateRequire(import.meta.url);
5
+
6
+ // ../../packages/adapters/src/system/errors.ts
7
+ var SSH_AUTH_ERROR_PATTERNS = [
8
+ "All configured authentication methods failed"
9
+ ];
10
+ var RETRYABLE_CONNECTION_ERROR_PATTERNS = [
11
+ "ECONNRESET",
12
+ "ECONNREFUSED",
13
+ "ETIMEDOUT",
14
+ "EHOSTUNREACH",
15
+ "ENETUNREACH",
16
+ "Timed out",
17
+ "Not connected",
18
+ "Connection lost",
19
+ "read ECONNRESET",
20
+ "Handshake failed",
21
+ "keepalive timeout",
22
+ "SSH connection closed before ready",
23
+ // ssh2 emits "Channel open failure: open failed" / "Channel open failure: …"
24
+ // when the SSH SERVER refuses a new channel - typically because the cached
25
+ // connection has gone half-dead (peer-side LOGOUT, network blip,
26
+ // remote sshd MaxSessions). The inner SshExecutor retries `exec`/
27
+ // `streamExec` on this, but `pipeLocal`, `transferIn`, and any other
28
+ // channel-bearing op bubble it up. Listing it here lets `withExecutor`
29
+ // drop the dead connection and retry transparently - fixes the
30
+ // "first redeploy fails, second click works" pattern.
31
+ "Channel open failure",
32
+ "open failed"
33
+ // NOT "socket hang up": it is what an HTTP client reports for ANY far end that vanished
34
+ // mid-request, including a permission-denied Docker socket and a wedged channel, neither
35
+ // of which a fresh connection fixes. A match here makes `withExecutor` replay its whole
36
+ // callback — and those callbacks install mail, create mailboxes and ensure the edge, so a
37
+ // non-retryable cause becomes a repeated mutation. `core/connectivity.ts` labels it
38
+ // without retrying, `api/lib/remote-state.ts` still maps it to a 503, and the bridge's
39
+ // 502 carries the real reason — so leaving it out here costs no diagnosis.
40
+ ];
41
+ var SshDisconnectedError = class extends Error {
42
+ constructor(message = "SSH connection lost") {
43
+ super(message);
44
+ this.name = "SshDisconnectedError";
45
+ }
46
+ };
47
+ function isSshDisconnectedError(err) {
48
+ return err instanceof SshDisconnectedError || err instanceof Error && err.name === "SshDisconnectedError";
49
+ }
50
+ function isSshAuthError(err) {
51
+ if (!(err instanceof Error)) return false;
52
+ return SSH_AUTH_ERROR_PATTERNS.some((pattern) => err.message.includes(pattern));
53
+ }
54
+ var HostChannelUnavailableError = class extends Error {
55
+ code;
56
+ constructor(code, message) {
57
+ super(message);
58
+ this.name = "HostChannelUnavailableError";
59
+ this.code = code;
60
+ }
61
+ };
62
+ function isRetryableRemoteConnectionError(err) {
63
+ if (isSshDisconnectedError(err)) return true;
64
+ if (!(err instanceof Error)) return false;
65
+ return RETRYABLE_CONNECTION_ERROR_PATTERNS.some(
66
+ (pattern) => err.message.includes(pattern)
67
+ );
68
+ }
69
+ function isRemoteConnectionError(err) {
70
+ return isSshAuthError(err) || isRetryableRemoteConnectionError(err);
71
+ }
72
+ var MAX_COMMAND_CHARS = 120;
73
+ function commandForError(command) {
74
+ const oneLine = command.trim().replace(/\s+/g, " ");
75
+ return oneLine.length > MAX_COMMAND_CHARS ? `${oneLine.slice(0, MAX_COMMAND_CHARS)}\u2026 (${command.length} chars)` : oneLine;
76
+ }
77
+ function isRuntimeNotFoundError(err) {
78
+ if (!err || typeof err !== "object") return false;
79
+ const e = err;
80
+ if (e.statusCode === 404) return true;
81
+ return typeof e.message === "string" && /no such (container|image|volume|network)/i.test(e.message);
82
+ }
83
+
84
+ // ../../packages/adapters/src/system/debug.ts
85
+ function isTruthy(value) {
86
+ if (!value) return false;
87
+ return ["1", "true", "yes", "on", "debug"].includes(value.toLowerCase());
88
+ }
89
+ function isSystemDebugEnabled() {
90
+ return isTruthy(process.env.SYSTEM_DEBUG_LOGS);
91
+ }
92
+ function systemDebug(scope, message) {
93
+ if (!isSystemDebugEnabled()) return;
94
+ const timestamp = (/* @__PURE__ */ new Date()).toISOString();
95
+ console.log(`[system-debug] ${timestamp} [${scope}] ${message}`);
96
+ }
97
+ function formatDuration(startMs) {
98
+ return `${Date.now() - startMs}ms`;
99
+ }
100
+
101
+ export {
102
+ SshDisconnectedError,
103
+ isSshAuthError,
104
+ HostChannelUnavailableError,
105
+ isRetryableRemoteConnectionError,
106
+ isRemoteConnectionError,
107
+ commandForError,
108
+ isRuntimeNotFoundError,
109
+ systemDebug,
110
+ formatDuration
111
+ };