openship 0.6.5 → 0.6.7

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 (68) hide show
  1. package/dist/bare-7QD623QV.js +29 -0
  2. package/dist/chunk-2ANV5ZQ5.js +88 -0
  3. package/dist/{chunk-LEYSL46B.js → chunk-2VSW6Q6C.js} +3911 -898
  4. package/dist/{chunk-5OUWIPOB.js → chunk-5T4UVOM3.js} +16 -86
  5. package/dist/{chunk-EZPPTWJQ.js → chunk-BLEXTYRK.js} +1 -1
  6. package/dist/{chunk-3ZAGC5NT.js → chunk-CIJV43LH.js} +83 -33
  7. package/dist/{chunk-7S4USQYF.js → chunk-DDFUESPJ.js} +67 -124
  8. package/dist/{chunk-6FH4AXVY.js → chunk-E2X26PXR.js} +1 -1
  9. package/dist/{chunk-XVOBL7BC.js → chunk-FYPFEDPZ.js} +1 -1
  10. package/dist/{chunk-WFRXYWK2.js → chunk-FZ6QCF6B.js} +8 -6
  11. package/dist/{chunk-VHMOQL5Y.js → chunk-GGIAIGUL.js} +125 -80
  12. package/dist/{chunk-WE7BXW6L.js → chunk-GIPJNNI5.js} +38 -44
  13. package/dist/{chunk-ZFJXPCQP.js → chunk-H3WT3A6R.js} +4 -3
  14. package/dist/chunk-H4FS5BED.js +936 -0
  15. package/dist/{chunk-HPXP6LXZ.js → chunk-J2XM3QSP.js} +8 -5
  16. package/dist/{chunk-6JELW75A.js → chunk-J3K6XABC.js} +5 -5
  17. package/dist/{chunk-VDESJSG6.js → chunk-LHFXSC36.js} +558 -109
  18. package/dist/{chunk-MSZUCNPV.js → chunk-MEE6OZFP.js} +3 -3
  19. package/dist/chunk-N5YZMO33.js +111 -0
  20. package/dist/{chunk-VLQ3UIVO.js → chunk-NRTJRNAE.js} +105 -37
  21. package/dist/chunk-PPEBIY5F.js +2331 -0
  22. package/dist/{chunk-RFWJE7ZN.js → chunk-PVP4RGWT.js} +3 -3
  23. package/dist/{chunk-PZRRL5PC.js → chunk-QHMDCYZ3.js} +15 -4
  24. package/dist/chunk-R4VDPFLS.js +106 -0
  25. package/dist/chunk-RDXT4BQB.js +310 -0
  26. package/dist/{chunk-GRTXMUHA.js → chunk-S6B2Z3OO.js} +145 -60
  27. package/dist/chunk-UWEV6XTS.js +249 -0
  28. package/dist/{chunk-QLX7NBEA.js → chunk-VQM6JHJB.js} +1004 -2046
  29. package/dist/{chunk-A7EWM2KA.js → chunk-VSTFHUCV.js} +44 -50
  30. package/dist/chunk-WU54BXFE.js +70 -0
  31. package/dist/{chunk-LFCSC4IU.js → chunk-XWHWTHEL.js} +313 -469
  32. package/dist/{chunk-7334NSJL.js → chunk-ZPJ74LXE.js} +76 -38
  33. package/dist/cloud-KNNABE3O.js +40 -0
  34. package/dist/{detect-P4CIQBRH.js → detect-G4DR3U2N.js} +9 -6
  35. package/dist/{docker-3K2NEBD6.js → docker-YOVPCIB4.js} +11 -7
  36. package/dist/{docker-edge-executor-YZZ6P7IC.js → docker-edge-executor-QFXD3337.js} +8 -6
  37. package/dist/edge-import-CRFVSR6P.js +56 -0
  38. package/dist/{edge-preflight-ES6FH4JK.js → edge-preflight-MXERVAMG.js} +15 -12
  39. package/dist/{ensure-container-edge-YPITNH45.js → ensure-container-edge-F4JUNQIK.js} +13 -10
  40. package/dist/{executor-DZGX55K2.js → executor-CVPAYYHR.js} +10 -5
  41. package/dist/host-channel-preflight-QTDNGLCK.js +54 -0
  42. package/dist/index.js +139 -92
  43. package/dist/{local-executor-WOFZD72X.js → local-executor-ODC6RANU.js} +3 -2
  44. package/dist/{nginx-NSAHWGJV.js → nginx-KLFF43TC.js} +13 -8
  45. package/dist/{openresty-lua-DR4D3EZ6.js → openresty-lua-VKWUV4GB.js} +8 -5
  46. package/dist/{privilege-YTVI4SKF.js → privilege-7D3SSOL3.js} +6 -5
  47. package/dist/server/engine/samples/amavisd/amavisd.conf +25 -0
  48. package/dist/server/engine/samples/postfix/master.cf +14 -0
  49. package/dist/server/index.js +44526 -33476
  50. package/dist/server/migrations/0105_mail_inbound_rules.sql +80 -0
  51. package/dist/server/migrations/0106_mcp_call_tracking.sql +23 -0
  52. package/dist/server/migrations/0107_build_minute_metering_indexes.sql +20 -0
  53. package/dist/server/migrations/0108_credential.sql +68 -0
  54. package/dist/server/migrations/meta/_journal.json +28 -0
  55. package/dist/setup-I5AJC6NJ.js +25 -0
  56. package/dist/ssh-client-625VWGKP.js +29 -0
  57. package/dist/up-QXTOOVXC.js +61 -0
  58. package/package.json +1 -1
  59. package/dist/bare-XQIPDO4R.js +0 -24
  60. package/dist/chunk-6HOA5JT7.js +0 -124
  61. package/dist/chunk-TM2IQPYU.js +0 -434
  62. package/dist/chunk-XRNRT37W.js +0 -316
  63. package/dist/cloud-3DODOWTR.js +0 -36
  64. package/dist/edge-import-MCJNVTZ2.js +0 -51
  65. package/dist/host-channel-preflight-MUXE3KZU.js +0 -49
  66. package/dist/setup-WUUZURHS.js +0 -22
  67. package/dist/up-XEO3SR6Z.js +0 -58
  68. /package/dist/{dist-es-ZP5QDSJ3.js → dist-es-AXFRS7PS.js} +0 -0
@@ -3,102 +3,33 @@
3
3
  import { createRequire as __ospCreateRequire } from "node:module";
4
4
  const require = __ospCreateRequire(import.meta.url);
5
5
  import {
6
- PACKAGE_ROOT_ONLY_EXCLUDES,
7
- hostFirewallRule,
8
6
  isSshAuthError,
9
- safeErrorMessage,
10
7
  systemDebug
11
- } from "./chunk-LEYSL46B.js";
12
-
13
- // ../../packages/adapters/src/archive.ts
14
- import { execFile } from "child_process";
15
- import { mkdtemp, rm, stat, writeFile } from "fs/promises";
16
- import { tmpdir } from "os";
17
- import { join } from "path";
18
- import { promisify } from "util";
19
- var execFileAsync = promisify(execFile);
20
- var ROOT_ONLY_EXCLUDES = new Set(PACKAGE_ROOT_ONLY_EXCLUDES);
21
- var NOOP_CLEANUP = async () => {
22
- };
23
- function getTarCreateEnv() {
24
- return {
25
- ...process.env,
26
- COPYFILE_DISABLE: "1",
27
- COPY_EXTENDED_ATTRIBUTES_DISABLE: "1"
28
- };
29
- }
30
- function tarCreateBaseArgs(localPath) {
31
- const darwinFlags = process.platform === "darwin" ? ["--no-mac-metadata", "--no-xattrs", "--no-acls", "--no-fflags"] : [];
32
- return [...darwinFlags, "-czf", "-", "-C", localPath];
33
- }
34
- async function gitTrackedFiles(localPath) {
35
- try {
36
- const { stdout } = await execFileAsync(
37
- "git",
38
- ["-C", localPath, "ls-files", "-z", "--cached", "--others", "--exclude-standard"],
39
- { maxBuffer: 256 * 1024 * 1024 }
40
- );
41
- const files = stdout.split("\0").filter(Boolean);
42
- return files.length > 0 ? files : null;
43
- } catch {
44
- return null;
45
- }
46
- }
47
- async function existingRelativePaths(root, paths) {
48
- if (!paths?.length) return [];
49
- const found = [];
50
- for (const p of paths) {
51
- try {
52
- await stat(join(root, p));
53
- found.push(p);
54
- } catch {
55
- }
56
- }
57
- return found;
58
- }
59
- async function prepareSourceTarArgs(localPath, options) {
60
- if (options?.includes?.length) {
61
- return { args: getTarCreateArgs(localPath, options), cleanup: NOOP_CLEANUP };
62
- }
63
- const files = await gitTrackedFiles(localPath);
64
- if (files) {
65
- const extra = await existingRelativePaths(localPath, options?.alsoInclude);
66
- const entries = extra.length ? [...files, ...extra] : files;
67
- const tmpDir = await mkdtemp(join(tmpdir(), "openship-tarlist-"));
68
- const listFile = join(tmpDir, "files.null");
69
- await writeFile(listFile, entries.join("\0"));
70
- const args = [...tarCreateBaseArgs(localPath), "--null", "-T", listFile];
71
- return { args, cleanup: () => rm(tmpDir, { recursive: true, force: true }) };
72
- }
73
- return { args: getTarCreateArgs(localPath, options), cleanup: NOOP_CLEANUP };
74
- }
75
- function getTarCreateArgs(localPath, options) {
76
- const args = tarCreateBaseArgs(localPath);
77
- if (options?.includes?.length) {
78
- args.push(...options.includes);
79
- return args;
80
- }
81
- for (const exclude of options?.excludes ?? []) {
82
- const pattern = ROOT_ONLY_EXCLUDES.has(exclude) ? `./${exclude}` : exclude;
83
- args.push(`--exclude=${pattern}`);
84
- }
85
- args.push(".");
86
- return args;
87
- }
8
+ } from "./chunk-N5YZMO33.js";
9
+ import {
10
+ HOST_CHANNEL_AUTH_REJECTED,
11
+ HOST_CHANNEL_NOT_PROVISIONED,
12
+ HOST_CHANNEL_ROW_CREDENTIALS_UNUSED,
13
+ hostFirewallRule,
14
+ safeErrorMessage
15
+ } from "./chunk-2VSW6Q6C.js";
88
16
 
89
17
  // ../../packages/adapters/src/system/ssh-client.ts
90
18
  import { Client } from "ssh2";
91
19
 
92
20
  // ../../packages/adapters/src/system/ssh-support.ts
93
- import { execFile as execFile2 } from "child_process";
21
+ import { execFile } from "child_process";
94
22
  import { access } from "fs/promises";
95
23
  import { homedir } from "os";
96
- import { join as join2 } from "path";
24
+ import { join } from "path";
97
25
  function formatSshTarget(config) {
98
26
  return `${config.username ?? "root"}@${config.host}:${config.port ?? 22}`;
99
27
  }
100
28
  function describeSshAuthFailure(config, originalMessage) {
101
29
  const target = formatSshTarget(config);
30
+ if (config.hostChannel) {
31
+ return `${HOST_CHANNEL_AUTH_REJECTED} Dialed ${target} from inside the Openship API container. ${HOST_CHANNEL_ROW_CREDENTIALS_UNUSED} ${HOST_CHANNEL_NOT_PROVISIONED} (${originalMessage})`;
32
+ }
102
33
  if (config.password) {
103
34
  return `SSH password authentication failed for ${target}. Check the username/password, or verify that the server allows password login. (${originalMessage})`;
104
35
  }
@@ -119,7 +50,7 @@ Or re-run \`openship up\`, which probes this and offers the exact rule. (${origi
119
50
  }
120
51
  function execFileText(command, args, timeout = 5e3) {
121
52
  return new Promise((resolve, reject) => {
122
- execFile2(command, args, { timeout }, (err, stdout, stderr) => {
53
+ execFile(command, args, { timeout }, (err, stdout, stderr) => {
123
54
  if (err) {
124
55
  reject(new Error(stderr.trim() || stdout.trim() || err.message));
125
56
  return;
@@ -141,7 +72,7 @@ function parseKnownHostsEntries(text) {
141
72
  return entries;
142
73
  }
143
74
  async function reconcileKnownHosts(config) {
144
- const knownHostsPath = join2(homedir(), ".ssh", "known_hosts");
75
+ const knownHostsPath = join(homedir(), ".ssh", "known_hosts");
145
76
  try {
146
77
  await access(knownHostsPath);
147
78
  } catch {
@@ -378,13 +309,12 @@ async function execSshCommand(client, command, timeout = 1e4) {
378
309
  }
379
310
 
380
311
  export {
381
- getTarCreateEnv,
382
- prepareSourceTarArgs,
383
312
  reconcileKnownHosts,
384
313
  connectSshClient,
385
314
  openSftp,
386
315
  openSshUnixSocket,
387
316
  DOCKER_DIAL_STDIO_COMMAND,
317
+ openSshExecChannel,
388
318
  attachDialStdioDiagnostics,
389
319
  dialStdioDiagnostics,
390
320
  openDockerDialStdioChannel,
@@ -9,7 +9,7 @@ import {
9
9
  firewallPersists,
10
10
  refused,
11
11
  shellQuote
12
- } from "./chunk-LEYSL46B.js";
12
+ } from "./chunk-2VSW6Q6C.js";
13
13
 
14
14
  // ../../packages/adapters/src/system/environment-ops.ts
15
15
  function opScript(steps) {
@@ -5,37 +5,45 @@ const require = __ospCreateRequire(import.meta.url);
5
5
  import {
6
6
  checkGit,
7
7
  installGit
8
- } from "./chunk-7334NSJL.js";
8
+ } from "./chunk-ZPJ74LXE.js";
9
9
  import {
10
- createDockerBuildContext,
11
- normalizeDockerRelativePath,
12
- resolveDockerfileCandidates,
13
- resolveWithinDirectory
14
- } from "./chunk-LFCSC4IU.js";
10
+ createDockerBuildContext
11
+ } from "./chunk-XWHWTHEL.js";
15
12
  import {
16
13
  prepareStackOutput,
17
14
  resolveProjectDir,
18
15
  resolveStaticOutputPath,
19
16
  runLocalBuild
20
- } from "./chunk-MSZUCNPV.js";
17
+ } from "./chunk-MEE6OZFP.js";
21
18
  import {
22
19
  BuildLogger,
20
+ dockerBuildContextDirectory,
21
+ droppedRuntimeEnvMessage,
23
22
  injectGitToken,
23
+ normalizeDockerRelativePath,
24
+ resolveContextDockerfileCandidates,
25
+ resolveDockerfileCandidates,
26
+ resolveWithinDirectory,
24
27
  runBuildPipeline,
28
+ splitRuntimeEnv,
25
29
  sq,
26
30
  transferLocalDirectory
27
- } from "./chunk-TM2IQPYU.js";
31
+ } from "./chunk-H4FS5BED.js";
28
32
  import {
33
+ isRuntimeNotFoundError
34
+ } from "./chunk-N5YZMO33.js";
35
+ import {
36
+ PRICING,
29
37
  STACKS,
30
38
  SYSTEM,
31
39
  TRANSFER_EXCLUDES,
32
40
  buildOutputTransferExcludes,
33
- isRuntimeNotFoundError,
34
41
  missingOutputDirectoryMessage,
42
+ nodeBinPathExport,
35
43
  packageManagerEnsureCommand,
36
44
  safeErrorMessage,
37
45
  shellSplitWords
38
- } from "./chunk-LEYSL46B.js";
46
+ } from "./chunk-2VSW6Q6C.js";
39
47
 
40
48
  // ../../packages/adapters/src/runtime/cloud.ts
41
49
  import { readFile } from "fs/promises";
@@ -47,6 +55,11 @@ var DEFAULT_RESOURCE_CONFIG = {
47
55
  memoryMb: 512,
48
56
  diskMb: 5120
49
57
  };
58
+ var DEFAULT_BUILD_RESOURCE_CONFIG = {
59
+ cpuCores: PRICING.oblien.buildResources.cpuCores,
60
+ memoryMb: PRICING.oblien.buildResources.memoryMb,
61
+ diskMb: PRICING.oblien.buildResources.diskGb * 1024
62
+ };
50
63
  function cloudCpus(cpuCores) {
51
64
  return Math.max(0.25, cpuCores);
52
65
  }
@@ -713,9 +726,11 @@ function restartPolicyForWorkload(policy) {
713
726
  return "always";
714
727
  }
715
728
  function resolveCloudWorkloadCmd(opts) {
716
- const { commandArgv, startCommand, workdir } = opts;
729
+ const { commandArgv, startCommand, workdir, binPathExport } = opts;
717
730
  if (commandArgv != null) return commandArgv.length > 0 ? commandArgv : void 0;
718
- return startCommand ? ["sh", "-c", `cd ${sq(workdir)} && ${startCommand}`] : void 0;
731
+ if (!startCommand) return void 0;
732
+ const body = `cd ${sq(workdir)} && ${startCommand}`;
733
+ return ["sh", "-c", binPathExport ? `${binPathExport} && ${body}` : body];
719
734
  }
720
735
  function exposeTarget(port, serviceName, slug, domain = SYSTEM.DOMAINS.CLOUD_DOMAIN) {
721
736
  const service = `service "${serviceName}" on port ${port}`;
@@ -1314,6 +1329,9 @@ function resolveCopyDestinationPath(copy, sourcePath, destination, destinationIs
1314
1329
  const basename = posix.basename(sourcePath.replace(/\/+$/g, "")) || ".";
1315
1330
  return posix.normalize(posix.join(destination, basename));
1316
1331
  }
1332
+ function cloudBuildContextPath(config) {
1333
+ return dockerBuildContextDirectory(config) || normalizeDockerRelativePath(config.rootDirectory);
1334
+ }
1317
1335
  var PAGE_CONTAINER_PREFIX = "page:";
1318
1336
  async function provisionCloudWorkspace(client, config, logger) {
1319
1337
  logger?.log(`Creating workspace from image "${config.image}"...
@@ -1414,7 +1432,12 @@ var CloudRuntime = class {
1414
1432
  // per service and continues, rather than the workload coming up on its own
1415
1433
  // network with nothing having said so (#533).
1416
1434
  "networkMode",
1417
- "pidMode"
1435
+ "pidMode",
1436
+ // A workspace is not a container whose ENTRYPOINT we compose: Oblien resolves its
1437
+ // own workload command, so an override (or a clearing `[]`) has nowhere to land
1438
+ // here. Declared so the deploy says so once per service and continues, rather
1439
+ // than the image's own launcher running with nothing having mentioned it (#575).
1440
+ "entrypoint"
1418
1441
  ]);
1419
1442
  client;
1420
1443
  // Optional admin-scoped proxy. Set when this runtime is constructed
@@ -1781,7 +1804,7 @@ var CloudRuntime = class {
1781
1804
  logger.log("Using Dockerfile content from source metadata.\n");
1782
1805
  return {
1783
1806
  kind: "remote",
1784
- contextRelativePath: normalizeDockerRelativePath(config.rootDirectory),
1807
+ contextRelativePath: cloudBuildContextPath(config),
1785
1808
  dockerfile: config.dockerfileContent,
1786
1809
  cleanup: async () => {
1787
1810
  }
@@ -1796,9 +1819,12 @@ var CloudRuntime = class {
1796
1819
  requireRepositoryDockerfile: true,
1797
1820
  onLog: logger.callback
1798
1821
  });
1799
- const dockerfilePath = resolveWithinDirectory(context.contextDir, context.dockerfileName);
1822
+ const dockerfilePath = resolveWithinDirectory(
1823
+ context.buildContextDir,
1824
+ context.dockerfileName
1825
+ );
1800
1826
  const dockerfile = await readFile(dockerfilePath, "utf-8");
1801
- const contextRoot = resolveWithinDirectory(context.contextDir, context.rootDirectory);
1827
+ const contextRoot = context.contextSubdir ? context.buildContextDir : resolveWithinDirectory(context.contextDir, context.rootDirectory);
1802
1828
  return {
1803
1829
  kind: "local",
1804
1830
  contextRoot,
@@ -1807,7 +1833,10 @@ var CloudRuntime = class {
1807
1833
  };
1808
1834
  }
1809
1835
  async resolveRemoteDockerfileBuildSource(config, logger) {
1810
- const candidates = resolveDockerfileCandidates(config.rootDirectory, config.dockerfilePath);
1836
+ const contextSubdir = dockerBuildContextDirectory(config);
1837
+ const candidates = contextSubdir ? resolveContextDockerfileCandidates(contextSubdir, config.dockerfilePath).map(
1838
+ (candidate) => `${contextSubdir}/${candidate}`
1839
+ ) : resolveDockerfileCandidates(config.rootDirectory, config.dockerfilePath);
1811
1840
  const sourceLabel = config.commitSha ? `${config.branch}@${config.commitSha.slice(0, 7)}` : config.branch;
1812
1841
  const checkoutRef = config.commitSha ?? "FETCH_HEAD";
1813
1842
  const sourceDir = "/openship/dockerfile-source";
@@ -1870,7 +1899,7 @@ var CloudRuntime = class {
1870
1899
  sourceWorkspaceId = void 0;
1871
1900
  return {
1872
1901
  kind: "remote",
1873
- contextRelativePath: normalizeDockerRelativePath(config.rootDirectory),
1902
+ contextRelativePath: cloudBuildContextPath(config),
1874
1903
  dockerfile,
1875
1904
  cleanup: async () => {
1876
1905
  await this.ws(workspaceId).delete().catch(() => {
@@ -2052,6 +2081,25 @@ var CloudRuntime = class {
2052
2081
  const contextSource = contextRelativePath ? joinWorkspacePath(repoRoot, contextRelativePath) : contextRoot;
2053
2082
  const prepareContextCommands = contextRelativePath ? [
2054
2083
  `echo "Copying Dockerfile context ${contextRelativePath}..."`,
2084
+ // `cp -a` FOLLOWS a symlinked source directory, so a repo that tracks
2085
+ // `svc -> /` and declares `build: ./svc` would copy the workspace's root
2086
+ // filesystem into its own build context. The lexical `..` refusal above
2087
+ // cannot see that; only a resolved path can. Same class as
2088
+ // GHSA-443m-7g52-94w8, reachable through the build context rather than
2089
+ // rootDirectory, and asserted here for the same reason the docker paths
2090
+ // assert it: at the sink.
2091
+ // `pwd -P` rather than `readlink -f`: it is a POSIX shell builtin, so it
2092
+ // exists in every workspace image, and it answers both questions at once —
2093
+ // the `cd` fails when the context is not a directory, and the resolved path
2094
+ // reveals a symlink that left the checkout.
2095
+ // BOTH sides resolved, and an unresolvable either side is fatal: an empty
2096
+ // `__root` would turn the prefix test into `"/"*`, which every absolute path
2097
+ // matches — a containment check that passes vacuously is worse than none.
2098
+ // `|| __x=""` keeps `set -e` from killing the script before the message.
2099
+ `__root="$(cd ${sq(repoRoot)} && pwd -P)" || __root=""`,
2100
+ `__ctx="$(cd ${sq(contextSource)} 2>/dev/null && pwd -P)" || __ctx=""`,
2101
+ `if [ -z "$__root" ] || [ -z "$__ctx" ]; then echo "Build context ${contextRelativePath} is not a directory in this repository." >&2; exit 1; fi`,
2102
+ `case "$__ctx/" in "$__root/"*) ;; *) echo "Build context ${contextRelativePath} resolves outside this repository." >&2; exit 1 ;; esac`,
2055
2103
  `mkdir -p ${sq(contextRoot)}`,
2056
2104
  `cp -a ${sq(`${contextSource}/.`)} ${sq(contextRoot)}/`
2057
2105
  ] : ['echo "Using repository root as Dockerfile context."'];
@@ -2374,15 +2422,28 @@ fi`;
2374
2422
  }
2375
2423
  }
2376
2424
  const startCommand = builtArtifact?.runtime.startCommand || config.startCommand || "npm start";
2425
+ const projectEnv = splitRuntimeEnv(config.envVars);
2426
+ if (projectEnv.dropped.length > 0) {
2427
+ onLog?.({
2428
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
2429
+ level: "warn",
2430
+ message: droppedRuntimeEnvMessage(projectEnv.dropped)
2431
+ });
2432
+ }
2377
2433
  const envArray = toEnvArray2({
2378
2434
  ...builtArtifact?.runtime.env ?? {},
2379
- ...config.envVars
2435
+ ...Object.fromEntries(projectEnv.entries)
2380
2436
  });
2381
2437
  const restartPolicy = config.restartPolicy === "no" ? "never" : config.restartPolicy ?? "always";
2438
+ const workloadCmd = resolveCloudWorkloadCmd({
2439
+ startCommand,
2440
+ workdir: workDir,
2441
+ binPathExport: builtArtifact ? "" : nodeBinPathExport(config.packageManager, [workDir, "/app"])
2442
+ });
2382
2443
  try {
2383
2444
  await ws.workloads.create({
2384
2445
  name: "app",
2385
- cmd: ["sh", "-c", `cd ${workDir} && ${startCommand}`],
2446
+ cmd: workloadCmd,
2386
2447
  working_dir: workDir,
2387
2448
  env: [...envArray, `PORT=${config.port}`],
2388
2449
  restart_policy: restartPolicy,
@@ -2684,15 +2745,7 @@ fi`;
2684
2745
  }
2685
2746
  async purge(deployment) {
2686
2747
  if (deployment.containerId?.startsWith(PAGE_CONTAINER_PREFIX)) {
2687
- const slug = deployment.containerId.slice(5);
2688
- try {
2689
- if (this.adminProxy?.deletePage) {
2690
- await this.adminProxy.deletePage(slug);
2691
- } else {
2692
- await this.client.pages.delete(slug);
2693
- }
2694
- } catch {
2695
- }
2748
+ await this.destroy(deployment.containerId);
2696
2749
  return;
2697
2750
  }
2698
2751
  if (!deployment.containerId) return;
@@ -2706,10 +2759,7 @@ fi`;
2706
2759
  err instanceof Error ? err.message : err
2707
2760
  );
2708
2761
  }
2709
- try {
2710
- await this.destroy(deployment.containerId);
2711
- } catch {
2712
- }
2762
+ await this.destroy(deployment.containerId);
2713
2763
  }
2714
2764
  // ── Observability ──────────────────────────────────────────────────────
2715
2765
  async getContainerInfo(containerId) {