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
package/dist/index.js CHANGED
@@ -26,7 +26,7 @@ import {
26
26
  stop,
27
27
  upCommand,
28
28
  writeCliInstall
29
- } from "./chunk-NK5N5LXN.js";
29
+ } from "./chunk-RCMERVBL.js";
30
30
  import {
31
31
  composeDown,
32
32
  composeInternalToken,
@@ -60,7 +60,7 @@ import {
60
60
  sourceBuildDir,
61
61
  storedApiPort,
62
62
  storedDashboardPort
63
- } from "./chunk-WVAML4H6.js";
63
+ } from "./chunk-5I3COAFH.js";
64
64
  import {
65
65
  bootstrapAdmin,
66
66
  detectPublicIp,
@@ -71,10 +71,10 @@ import {
71
71
  internalTokenRejectedProblem,
72
72
  waitDashboard,
73
73
  waitHealthy
74
- } from "./chunk-VTGPEC6B.js";
74
+ } from "./chunk-7YBWXEM6.js";
75
75
  import {
76
76
  startUnitHint
77
- } from "./chunk-5DMWSTOM.js";
77
+ } from "./chunk-5UXHT6ED.js";
78
78
  import "./chunk-QWHBRY2K.js";
79
79
  import {
80
80
  completeHostEdge,
@@ -85,13 +85,13 @@ import {
85
85
  renderEdgeConflict,
86
86
  repairEdgeConflict,
87
87
  rollbackHostEdge
88
- } from "./chunk-CBLD2HS5.js";
88
+ } from "./chunk-NUT6HKK6.js";
89
89
  import {
90
90
  DATA_DIR,
91
91
  LOG_DIR,
92
92
  OS_DIR
93
93
  } from "./chunk-ELGRMZOW.js";
94
- import "./chunk-HKRQXPTI.js";
94
+ import "./chunk-R5DC5PEF.js";
95
95
  import "./chunk-ONKEYSMY.js";
96
96
  import "./chunk-XZYFZ6A5.js";
97
97
  import "./chunk-PDW4L3NV.js";
@@ -100,35 +100,39 @@ import "./chunk-42WQABXM.js";
100
100
  import "./chunk-DRK3MEDS.js";
101
101
  import "./chunk-3NQQXMDR.js";
102
102
  import "./chunk-ZWLDLTJP.js";
103
- import "./chunk-FE7ZZQSE.js";
104
- import "./chunk-OBPKYXQL.js";
103
+ import "./chunk-D3OEMXYP.js";
104
+ import "./chunk-MUWVC5R4.js";
105
105
  import {
106
106
  detectInstalledProxy,
107
107
  scanImportableSites
108
- } from "./chunk-A4GXYE4L.js";
109
- import "./chunk-QVW76WDL.js";
110
- import "./chunk-RSR7PFIQ.js";
108
+ } from "./chunk-NIYD7MD2.js";
109
+ import "./chunk-IAMOUDHY.js";
110
+ import "./chunk-7PEXM2UI.js";
111
111
  import "./chunk-I3DPMTSF.js";
112
112
  import "./chunk-WNWQBB7F.js";
113
- import "./chunk-2GWYYHOR.js";
114
- import "./chunk-RXQBD4ZY.js";
115
- import "./chunk-77QDF6UD.js";
116
- import "./chunk-LUKYPVJ4.js";
117
- import "./chunk-LFE5KQ7Y.js";
118
- import "./chunk-YIMOCZBZ.js";
119
- import "./chunk-LH2PLRQO.js";
120
- import "./chunk-MLYSEZQB.js";
121
- import "./chunk-Q7WGTQQ5.js";
122
- import "./chunk-PCPVTH6M.js";
113
+ import "./chunk-NRU2EBYZ.js";
114
+ import "./chunk-RHLNVZMS.js";
115
+ import "./chunk-3MP6ACKM.js";
116
+ import "./chunk-6GU6JSK6.js";
117
+ import "./chunk-ENKJ7XIF.js";
118
+ import "./chunk-5DJOB7IX.js";
119
+ import "./chunk-SK4FFCFR.js";
120
+ import "./chunk-AHJYP7DG.js";
121
+ import "./chunk-UAWY6X7V.js";
122
+ import "./chunk-R4VDPFLS.js";
123
+ import "./chunk-VAI5VOOI.js";
124
+ import "./chunk-TAZZZKYO.js";
123
125
  import {
124
126
  LocalExecutor
125
- } from "./chunk-6FH4AXVY.js";
126
- import "./chunk-4I2BM2DT.js";
127
- import "./chunk-YMMQRE5J.js";
128
- import "./chunk-UVNMQM7W.js";
129
- import "./chunk-ZFJXPCQP.js";
130
- import "./chunk-2YM3NI2E.js";
131
- import "./chunk-SM26HDS2.js";
127
+ } from "./chunk-T6DVUX4F.js";
128
+ import "./chunk-T46BQOLY.js";
129
+ import "./chunk-GNOMEMNI.js";
130
+ import "./chunk-NPSRGIHV.js";
131
+ import "./chunk-P7RCYELG.js";
132
+ import "./chunk-X53JXRS4.js";
133
+ import "./chunk-BO6CY4V2.js";
134
+ import "./chunk-D6HU4VUZ.js";
135
+ import "./chunk-N5YZMO33.js";
132
136
  import {
133
137
  CLOUD_DASHBOARD_URL,
134
138
  HOST_CHANNEL_AUTH_REJECTED_SHORT,
@@ -138,15 +142,19 @@ import {
138
142
  buildMailImageRef,
139
143
  cliInstallCommand,
140
144
  commandToArgv,
145
+ composeBuildIssues,
141
146
  composeMountIssues,
142
147
  composeMountToSpec,
143
148
  composePortToSpec,
144
149
  isValidEmail,
145
150
  parseComposeNamespace,
146
151
  parseOpenshipConfigJson,
152
+ renderReleaseImage,
147
153
  resolveCliUpdatePlan,
148
- summarizeHostChannelCause
149
- } from "./chunk-UJ6JZM65.js";
154
+ summarizeHostChannelCause,
155
+ validateReleaseRepository,
156
+ validateReleaseVersionUrl
157
+ } from "./chunk-WEZFTL4V.js";
150
158
  import "./chunk-PXKCZB2I.js";
151
159
 
152
160
  // src/index.ts
@@ -305,7 +313,7 @@ function getDashboardUrl(name) {
305
313
  }
306
314
 
307
315
  // src/lib/api-client.ts
308
- var USER_AGENT = `openship-cli/${true ? "0.6.6" : "dev"}`;
316
+ var USER_AGENT = `openship-cli/${true ? "0.6.8" : "dev"}`;
309
317
  function getApiUrl2() {
310
318
  return `${getApiUrl()}/api`;
311
319
  }
@@ -1101,7 +1109,7 @@ function dockerNameRunning(nameFilter) {
1101
1109
  return r.stdout.trim().length > 0;
1102
1110
  }
1103
1111
  async function hostControlCheck(api) {
1104
- const { checkHostChannel } = await import("./host-channel-preflight-KE6OONYK.js");
1112
+ const { checkHostChannel } = await import("./host-channel-preflight-OMCZMMKR.js");
1105
1113
  return hostControlRow(await checkHostChannel().catch(() => null), api);
1106
1114
  }
1107
1115
  var NEVER_PROVISIONED_DETAIL = `never provisioned \u2014 run \`${HOST_CHANNEL_PROVISION_COMMAND}\``;
@@ -1506,7 +1514,7 @@ async function interactiveDoctor() {
1506
1514
  continue;
1507
1515
  }
1508
1516
  if (action2 === "host-control") {
1509
- const { verifyHostChannel } = await import("./host-channel-preflight-KE6OONYK.js");
1517
+ const { verifyHostChannel } = await import("./host-channel-preflight-OMCZMMKR.js");
1510
1518
  const report3 = await verifyHostChannel().catch(() => null);
1511
1519
  if (report3?.status === "fixed") log2.success("Host control is reachable now.");
1512
1520
  else if (report3?.status === "ok") log2.success("Host control was already reachable.");
@@ -1613,8 +1621,6 @@ async function deployFolder(opts) {
1613
1621
  tarball,
1614
1622
  "--exclude=./node_modules",
1615
1623
  "--exclude=./.git",
1616
- "--exclude=./dist",
1617
- "--exclude=./.next",
1618
1624
  "--exclude=./.DS_Store",
1619
1625
  "-C",
1620
1626
  cwd,
@@ -1682,7 +1688,11 @@ async function deployFolder(opts) {
1682
1688
  })
1683
1689
  });
1684
1690
  if (!dep.deployment_id) throw new Error(dep.error || "Failed to start deployment");
1685
- return { deploymentId: dep.deployment_id, projectId: dep.project_id };
1691
+ return {
1692
+ deploymentId: dep.deployment_id,
1693
+ projectId: dep.project_id,
1694
+ ...scan.configDiagnostics && { configDiagnostics: scan.configDiagnostics }
1695
+ };
1686
1696
  }
1687
1697
 
1688
1698
  // src/lib/sse.ts
@@ -1871,8 +1881,18 @@ var deployCommand = new Command11("deploy").description("Trigger a deployment fo
1871
1881
  }
1872
1882
  });
1873
1883
  deploymentId = result2.deploymentId;
1874
- payload = { success: true, deployment_id: result2.deploymentId, project_id: result2.projectId };
1884
+ payload = {
1885
+ success: true,
1886
+ deployment_id: result2.deploymentId,
1887
+ project_id: result2.projectId,
1888
+ ...result2.configDiagnostics && { configDiagnostics: result2.configDiagnostics }
1889
+ };
1875
1890
  spinner5?.succeed(deploymentId ? `Deployment queued: ${deploymentId}` : "Deployment queued");
1891
+ if (result2.configDiagnostics?.wholeFile) {
1892
+ err(" \u2717 openship.json was ignored entirely \u2014 deployed with detected settings:");
1893
+ }
1894
+ for (const e of result2.configDiagnostics?.errors ?? []) err(` \u2022 ${e}`);
1895
+ for (const w of result2.configDiagnostics?.warnings ?? []) info(` \u26A0 ${w}`);
1876
1896
  } catch (e) {
1877
1897
  spinner5?.fail("Folder deploy failed");
1878
1898
  err(e instanceof ApiError ? e.message : String(e));
@@ -2179,7 +2199,10 @@ function printProject(project) {
2179
2199
  ["name", project.name],
2180
2200
  ["slug", project.slug],
2181
2201
  ["framework", project.framework],
2182
- ["gitRepo", project.gitOwner && project.gitRepo ? `${project.gitOwner}/${project.gitRepo}` : null],
2202
+ [
2203
+ "gitRepo",
2204
+ project.gitOwner && project.gitRepo ? `${project.gitOwner}/${project.gitRepo}` : null
2205
+ ],
2183
2206
  ["gitBranch", project.gitBranch],
2184
2207
  ["autoDeploy", project.autoDeploy],
2185
2208
  ["status", project.status]
@@ -2191,6 +2214,46 @@ function printProject(project) {
2191
2214
  }
2192
2215
  }
2193
2216
  var ENVIRONMENTS = ["production", "preview", "development"];
2217
+ function releaseImageSourceFromOptions(opts) {
2218
+ const imageTemplate = opts.imageTemplate.trim();
2219
+ const repo = opts.githubRepo?.trim() || void 0;
2220
+ const versionUrl = opts.versionUrl?.trim() || void 0;
2221
+ const pinnedVersion = opts.pin?.trim() || void 0;
2222
+ const validationTag = pinnedVersion ?? "v1.2.3";
2223
+ renderReleaseImage(imageTemplate, {
2224
+ version: validationTag.replace(/^v/i, ""),
2225
+ tag: validationTag
2226
+ });
2227
+ if (repo && versionUrl) {
2228
+ throw new Error("--github-repo cannot be combined with --version-url.");
2229
+ }
2230
+ if (repo) {
2231
+ const invalidRepo = validateReleaseRepository(repo);
2232
+ if (invalidRepo)
2233
+ throw new Error(invalidRepo.replace("GitHub release repository", "--github-repo"));
2234
+ return {
2235
+ artifactKind: "image",
2236
+ mode: "github",
2237
+ imageTemplate,
2238
+ repo,
2239
+ ...pinnedVersion ? { pinnedVersion } : {}
2240
+ };
2241
+ }
2242
+ if (!versionUrl && !pinnedVersion) {
2243
+ throw new Error("Pass --github-repo, --version-url, or --pin.");
2244
+ }
2245
+ if (versionUrl) {
2246
+ const invalidUrl = validateReleaseVersionUrl(versionUrl);
2247
+ if (invalidUrl) throw new Error(invalidUrl.replace("Release version URL", "--version-url"));
2248
+ }
2249
+ return {
2250
+ artifactKind: "image",
2251
+ mode: "url",
2252
+ imageTemplate,
2253
+ ...versionUrl ? { versionUrl } : {},
2254
+ ...pinnedVersion ? { pinnedVersion } : {}
2255
+ };
2256
+ }
2194
2257
  var listCmd2 = new Command14("list").alias("ls").description("List projects in the active organization").action(
2195
2258
  action(async () => {
2196
2259
  const rows = [];
@@ -2214,10 +2277,27 @@ var getCmd = new Command14("get").description("Show a single project").argument(
2214
2277
  printProject(data);
2215
2278
  })
2216
2279
  );
2217
- var createCmd = new Command14("create").description("Create a project").requiredOption("--name <name>", "Project name").option("--slug <slug>", "Free-subdomain slug (slug.opsh.io)").option("--git-owner <owner>", "GitHub owner/org").option("--git-repo <repo>", "GitHub repository name").option("--git-branch <branch>", "Git branch to deploy").option("--framework <framework>", "Stack/framework id").option("--local-path <path>", "Local source path").option("--port <port>", "Container port", (v) => Number(v)).option(
2218
- "--type <type>",
2219
- "Project type: app | docker | services | monorepo"
2220
- ).action(
2280
+ var releaseImageCmd = new Command14("release-image").description("Track and deploy a versioned prebuilt container image").argument("<id>", "Project ID").requiredOption(
2281
+ "--image-template <reference>",
2282
+ "Image reference with {tag} or {version}, e.g. ghcr.io/acme/api:{tag}"
2283
+ ).option("--github-repo <owner/repo>", "Resolve versions from GitHub Releases").option("--version-url <https-url>", "HTTPS endpoint returning a version or tag").option("--pin <version>", "Deploy a fixed version instead of the discovered latest release").action(
2284
+ action(async (id, opts) => {
2285
+ const source = releaseImageSourceFromOptions(opts);
2286
+ const result = await apiRequest(
2287
+ `/projects/${encodeURIComponent(id)}/release-image-source`,
2288
+ { method: "PUT", body: JSON.stringify(source) }
2289
+ );
2290
+ if (isJsonMode()) {
2291
+ printJson(result.data);
2292
+ return;
2293
+ }
2294
+ const upstream = source.mode === "github" ? `GitHub Releases (${source.repo})` : source.versionUrl ? `version URL (${source.versionUrl})` : `pinned version (${source.pinnedVersion})`;
2295
+ ok(`
2296
+ Project ${id} now deploys ${source.imageTemplate} from ${upstream}
2297
+ `);
2298
+ })
2299
+ );
2300
+ var createCmd = new Command14("create").description("Create a project").requiredOption("--name <name>", "Project name").option("--slug <slug>", "Free-subdomain slug (slug.opsh.io)").option("--git-owner <owner>", "GitHub owner/org").option("--git-repo <repo>", "GitHub repository name").option("--git-branch <branch>", "Git branch to deploy").option("--framework <framework>", "Stack/framework id").option("--local-path <path>", "Local source path").option("--port <port>", "Container port", (v) => Number(v)).option("--type <type>", "Project type: app | docker | services | monorepo").action(
2221
2301
  action(async (opts) => {
2222
2302
  const body = { name: opts.name };
2223
2303
  if (opts.slug) body.slug = opts.slug;
@@ -2531,7 +2611,9 @@ var logsCmd = new Command14("logs").description("Show or stream runtime (contain
2531
2611
  for (const entry of data) printLogEntry(entry);
2532
2612
  return;
2533
2613
  }
2534
- for await (const ev of sseRequest(`/projects/${encodeURIComponent(id)}/logs/stream${tailQs}`)) {
2614
+ for await (const ev of sseRequest(
2615
+ `/projects/${encodeURIComponent(id)}/logs/stream${tailQs}`
2616
+ )) {
2535
2617
  if (ev.event === "error") {
2536
2618
  const parsed = safeParse(ev.data);
2537
2619
  err(` ${parsed?.error ?? ev.data}`);
@@ -2596,6 +2678,7 @@ function printLogEntry(entry) {
2596
2678
  var projectCommand = new Command14("project").alias("projects").description("Manage Openship projects");
2597
2679
  projectCommand.addCommand(listCmd2);
2598
2680
  projectCommand.addCommand(getCmd);
2681
+ projectCommand.addCommand(releaseImageCmd);
2599
2682
  projectCommand.addCommand(createCmd);
2600
2683
  projectCommand.addCommand(deleteCmd);
2601
2684
  projectCommand.addCommand(envCmd);
@@ -2736,10 +2819,7 @@ var getCmd2 = stackCommand("get").description("Show one service's configuration"
2736
2819
  fail(e);
2737
2820
  }
2738
2821
  });
2739
- var createCmd2 = stackCommand("create").description("Add a service to a stack").argument("<name>", "Service name (unique within the stack)").option("--image <image>", "Container image (e.g. postgres:16)").option("--build <context>", "Build context path (relative to repo root)").option("--dockerfile <path>", "Dockerfile path (relative to build context)").option("--port <mapping>", "Port mapping, e.g. 8080:80 (repeatable)", collect, []).option("--depends-on <service>", "Service this depends on (repeatable)", collect, []).option("--env <KEY=VALUE>", "Compose environment default (repeatable)", collect, []).option("--command <command>", "Override the container command").option(
2740
- "--restart <policy>",
2741
- "Restart policy: no | always | on-failure | unless-stopped"
2742
- ).option("--expose", "Expose the service publicly through managed routing").option("--exposed-port <port>", "Container port to expose publicly").option("--domain <label>", "Free subdomain label (with --expose)").action(async (name, opts) => {
2822
+ var createCmd2 = stackCommand("create").description("Add a service to a stack").argument("<name>", "Service name (unique within the stack)").option("--image <image>", "Container image (e.g. postgres:16)").option("--build <context>", "Build context path (relative to repo root)").option("--dockerfile <path>", "Dockerfile path (relative to build context)").option("--port <mapping>", "Port mapping, e.g. 8080:80 (repeatable)", collect, []).option("--depends-on <service>", "Service this depends on (repeatable)", collect, []).option("--env <KEY=VALUE>", "Compose environment default (repeatable)", collect, []).option("--command <command>", "Override the container command").option("--restart <policy>", "Restart policy: no | always | on-failure | unless-stopped").option("--expose", "Expose the service publicly through managed routing").option("--exposed-port <port>", "Container port to expose publicly").option("--domain <label>", "Free subdomain label (with --expose)").action(async (name, opts) => {
2743
2823
  requireAuth();
2744
2824
  try {
2745
2825
  const projectId = await resolveProject(opts.project);
@@ -2843,6 +2923,17 @@ function mapDependsOn(deps) {
2843
2923
  if (deps && typeof deps === "object") return Object.keys(deps);
2844
2924
  return [];
2845
2925
  }
2926
+ function mapBuildArgs(raw) {
2927
+ if (!raw || typeof raw !== "object" || Array.isArray(raw)) return void 0;
2928
+ const args = {};
2929
+ for (const [key, value] of Object.entries(raw)) {
2930
+ if (value === null || value === void 0) args[key] = null;
2931
+ else if (["string", "number", "boolean"].includes(typeof value)) {
2932
+ args[key] = String(value);
2933
+ }
2934
+ }
2935
+ return Object.keys(args).length > 0 ? args : void 0;
2936
+ }
2846
2937
  function mapNamespaces(name, d) {
2847
2938
  const advanced = {};
2848
2939
  const errors = [];
@@ -2862,12 +2953,17 @@ function mapComposeService(name, def, baseDir, errors) {
2862
2953
  const svc = { name };
2863
2954
  if (typeof d.image === "string") svc.image = d.image;
2864
2955
  const build = d.build;
2956
+ for (const issue of composeBuildIssues(build, { allowAbsoluteContext: true })) {
2957
+ if (issue.blocking) errors.push(` ${name}: ${issue.reason}`);
2958
+ }
2865
2959
  if (typeof build === "string") {
2866
2960
  svc.build = relativizeContext(build, baseDir);
2867
2961
  } else if (build && typeof build === "object") {
2868
2962
  const b = build;
2869
2963
  svc.build = relativizeContext(typeof b.context === "string" ? b.context : ".", baseDir);
2870
2964
  if (typeof b.dockerfile === "string") svc.dockerfile = b.dockerfile;
2965
+ const buildArgs = mapBuildArgs(b.args);
2966
+ if (buildArgs) svc.buildArgs = buildArgs;
2871
2967
  }
2872
2968
  const ports = mapPorts(d.ports);
2873
2969
  if (ports.length) svc.ports = ports;
@@ -2885,17 +2981,21 @@ function mapComposeService(name, def, baseDir, errors) {
2885
2981
  if (typeof d.restart === "string") svc.restart = d.restart;
2886
2982
  const { advanced, errors: namespaceErrors } = mapNamespaces(name, d);
2887
2983
  errors.push(...namespaceErrors);
2984
+ if (build && typeof build === "object" && Object.hasOwn(build, "args")) {
2985
+ advanced.buildArgTemplateKeys = [];
2986
+ }
2888
2987
  if (Object.keys(advanced).length > 0) svc.advanced = advanced;
2889
2988
  return svc;
2890
2989
  }
2891
- var syncCmd = stackCommand("sync").description("Sync a stack's services from a docker-compose file (services not in the file are removed)").argument("<compose-file>", "Path to docker-compose.yml / compose.yaml").option("-y, --yes", "Skip the confirmation prompt").action(async (composeFile, opts) => {
2990
+ var syncCmd = stackCommand("sync").description(
2991
+ "Sync a stack's services from a docker-compose file (services not in the file are removed)"
2992
+ ).argument("<compose-file>", "Path to docker-compose.yml / compose.yaml").option("-y, --yes", "Skip the confirmation prompt").action(async (composeFile, opts) => {
2892
2993
  requireAuth();
2893
2994
  const abs = path.resolve(composeFile);
2894
- const proc = spawnSync2(
2895
- "docker",
2896
- ["compose", "-f", abs, "config", "--format", "json"],
2897
- { encoding: "utf8", maxBuffer: 64 * 1024 * 1024 }
2898
- );
2995
+ const proc = spawnSync2("docker", ["compose", "-f", abs, "config", "--format", "json"], {
2996
+ encoding: "utf8",
2997
+ maxBuffer: 64 * 1024 * 1024
2998
+ });
2899
2999
  if (proc.error) {
2900
3000
  if (proc.error.code === "ENOENT") {
2901
3001
  err(" `docker` not found. `service sync` uses Docker Compose to parse the file.");
@@ -2953,16 +3053,36 @@ ${mapErrors.join("\n")}`
2953
3053
  }
2954
3054
  });
2955
3055
  function containerActionCommand(action2) {
2956
- return stackCommand(action2).description(`${action2[0].toUpperCase()}${action2.slice(1)} a service's container`).argument("<service>", "Service name or id").action(async (service, opts) => {
3056
+ const cmd = stackCommand(action2).description(
3057
+ action2 === "restart" ? "Bounce a service's container (does NOT apply changed env \u2014 use `deploy --refresh` for that)" : `${action2[0].toUpperCase()}${action2.slice(1)} a service's container`
3058
+ ).argument("<service>", "Service name or id");
3059
+ if (action2 === "restart") {
3060
+ cmd.option("--force", "Bounce even with pending env changes (they will NOT be applied)");
3061
+ }
3062
+ return cmd.action(async (service, opts) => {
2957
3063
  requireAuth();
3064
+ let serviceId = "";
2958
3065
  try {
2959
3066
  const projectId = await resolveProject(opts.project);
2960
3067
  const svc = await resolveService(projectId, service);
2961
- await apiRequest(`/projects/${projectId}/services/${svc.id}/${action2}`, {
3068
+ serviceId = svc.id;
3069
+ const query2 = action2 === "restart" && opts.force ? "?force=true" : "";
3070
+ await apiRequest(`/projects/${projectId}/services/${svc.id}/${action2}${query2}`, {
2962
3071
  method: "POST"
2963
3072
  });
2964
3073
  ok(` ${action2}ed "${svc.name}".`);
2965
3074
  } catch (e) {
3075
+ const body = e instanceof ApiError ? e.body : null;
3076
+ if (body?.code === "SERVICE_CONFIG_STALE") {
3077
+ const keys = Array.isArray(body.staleEnvKeys) ? body.staleEnvKeys : [];
3078
+ err(` Restart refused: "${body.serviceName ?? service}" has pending environment changes.`);
3079
+ if (keys.length > 0) info(` Pending: ${keys.join(", ")}`);
3080
+ info(
3081
+ ` Apply them: openship deploy --refresh --service-ids ${serviceId} -p ${opts.project}`
3082
+ );
3083
+ info(` Bounce anyway (changes NOT applied): add --force`);
3084
+ process.exit(1);
3085
+ }
2966
3086
  fail(e);
2967
3087
  }
2968
3088
  });
@@ -3049,10 +3169,11 @@ var envGetCmd = stackCommand("get").description("List a service's environment va
3049
3169
  fail(e);
3050
3170
  }
3051
3171
  });
3052
- var envSetCmd = stackCommand("set").description("Set a service's environment variables for one environment").argument("<service>", "Service name or id").argument("<pairs...>", "KEY=VALUE pairs").option("-e, --env <environment>", "Environment: production | preview | development", "production").option("--secret", "Mark the provided variables as secret").option(
3053
- "--replace",
3054
- "Replace ALL variables for this environment with only the given pairs"
3055
- ).action(async (service, pairs, opts) => {
3172
+ var envSetCmd = stackCommand("set").description("Set a service's environment variables for one environment").argument("<service>", "Service name or id").argument("<pairs...>", "KEY=VALUE pairs").option(
3173
+ "-e, --env <environment>",
3174
+ "Environment: production | preview | development",
3175
+ "production"
3176
+ ).option("--secret", "Mark the provided variables as secret").option("--replace", "Replace ALL variables for this environment with only the given pairs").action(async (service, pairs, opts) => {
3056
3177
  requireAuth();
3057
3178
  try {
3058
3179
  const projectId = await resolveProject(opts.project);
@@ -3116,9 +3237,7 @@ var logsCmd2 = stackCommand("logs").description("Show or stream a service's runt
3116
3237
  const svc = await resolveService(projectId, service);
3117
3238
  const tail = opts.tail ? `?tail=${encodeURIComponent(opts.tail)}` : "";
3118
3239
  if (!opts.follow) {
3119
- const res = await apiRequest(
3120
- `/projects/${projectId}/services/${svc.id}/logs${tail}`
3121
- );
3240
+ const res = await apiRequest(`/projects/${projectId}/services/${svc.id}/logs${tail}`);
3122
3241
  const entries = res.data ?? [];
3123
3242
  if (isJsonMode()) {
3124
3243
  printJson(entries);
@@ -3127,14 +3246,17 @@ var logsCmd2 = stackCommand("logs").description("Show or stream a service's runt
3127
3246
  for (const e of entries) printLogEntry2(e);
3128
3247
  return;
3129
3248
  }
3130
- for await (const ev of sseRequest(`/projects/${projectId}/services/${svc.id}/logs/stream${tail}`)) {
3249
+ for await (const ev of sseRequest(
3250
+ `/projects/${projectId}/services/${svc.id}/logs/stream${tail}`
3251
+ )) {
3131
3252
  if (ev.event === "error") {
3132
3253
  const parsed = safeParse2(ev.data);
3133
3254
  throw new ApiError(parsed?.error || "Log stream error", 0, parsed);
3134
3255
  }
3135
3256
  if (ev.event === "log") {
3136
3257
  const parsed = safeParse2(ev.data);
3137
- if (parsed) printLogEntry2(parsed);
3258
+ if (parsed)
3259
+ printLogEntry2(parsed);
3138
3260
  }
3139
3261
  }
3140
3262
  } catch (e) {
@@ -3463,7 +3585,7 @@ async function runRepair2(mode) {
3463
3585
  requireLinux();
3464
3586
  const sp = spin2(mode === "migrate" ? "Migrating the existing proxy's sites into the edge\u2026" : "Taking over :80/:443\u2026");
3465
3587
  try {
3466
- const { repairEdgeConflict: repairEdgeConflict2 } = await import("./edge-preflight-GBNCPNVU.js");
3588
+ const { repairEdgeConflict: repairEdgeConflict2 } = await import("./edge-preflight-QUW4BM7N.js");
3467
3589
  sp?.stop();
3468
3590
  const res = await repairEdgeConflict2(mode, apiPort(), onLog);
3469
3591
  reportRepair(res);
@@ -3612,7 +3734,7 @@ async function panelStatus() {
3612
3734
  async function panelRepair(mode) {
3613
3735
  const sp = spin2(mode === "migrate" ? "Migrating the existing proxy's sites\u2026" : "Taking over :80/:443\u2026");
3614
3736
  try {
3615
- const { repairEdgeConflict: repairEdgeConflict2 } = await import("./edge-preflight-GBNCPNVU.js");
3737
+ const { repairEdgeConflict: repairEdgeConflict2 } = await import("./edge-preflight-QUW4BM7N.js");
3616
3738
  sp?.stop();
3617
3739
  const res = await repairEdgeConflict2(mode, apiPort(), onLog);
3618
3740
  renderRepair(res);
@@ -4454,7 +4576,7 @@ function printMonitoringNextSteps() {
4454
4576
  info(" openship edge analytics -p <project> per-domain analytics");
4455
4577
  }
4456
4578
  async function runMonitoringInstall(opts) {
4457
- const up = await import("./up-K4BV7466.js");
4579
+ const up = await import("./up-HQS7IR7H.js");
4458
4580
  let adminName = opts.adminName;
4459
4581
  let adminEmail = opts.adminEmail;
4460
4582
  let adminPassword = opts.adminPassword;
@@ -6611,7 +6733,7 @@ function applyOnlyArgv(selfArgs, json) {
6611
6733
  // src/commands/update.ts
6612
6734
  async function verifyHostChannelAfterUpdate() {
6613
6735
  if (isJsonMode()) return;
6614
- const { verifyHostChannel } = await import("./host-channel-preflight-KE6OONYK.js");
6736
+ const { verifyHostChannel } = await import("./host-channel-preflight-OMCZMMKR.js");
6615
6737
  await verifyHostChannel().catch(() => {
6616
6738
  });
6617
6739
  }
@@ -6688,7 +6810,7 @@ async function runSourceUpdate(source, opts) {
6688
6810
  }
6689
6811
  var REINSTALL_HINT = "curl -fsSL https://get.openship.io | sh";
6690
6812
  async function runTarballUpdate(install, opts) {
6691
- const current = "0.6.6";
6813
+ const current = "0.6.8";
6692
6814
  let latest;
6693
6815
  try {
6694
6816
  latest = (await resolveLatestTag()).replace(/^v/, "");
@@ -6772,7 +6894,7 @@ var updateCommand = new Command26("update").description("Update the Openship CLI
6772
6894
  if (source) return runSourceUpdate(source, opts);
6773
6895
  const cliInstall = readCliInstall();
6774
6896
  if (cliInstall?.method === "tarball") return runTarballUpdate(cliInstall, opts);
6775
- const current = "0.6.6";
6897
+ const current = "0.6.8";
6776
6898
  let latest;
6777
6899
  try {
6778
6900
  latest = (await resolveLatestTag()).replace(/^v/, "");
@@ -7380,7 +7502,7 @@ async function runWizard() {
7380
7502
  }
7381
7503
  }
7382
7504
  }
7383
- const uiTag = `v${"0.6.6"}`;
7505
+ const uiTag = `v${"0.6.8"}`;
7384
7506
  if (method === "bare") {
7385
7507
  const dl = spinner4();
7386
7508
  dl.start("Pulling the Openship dist from GitHub");
@@ -7440,7 +7562,7 @@ async function runWizard() {
7440
7562
  const dashboardPort = String(ports.dashboard);
7441
7563
  const moved = portMoveNotice(ports, composeTrustedOriginUrls());
7442
7564
  if (moved.length) log5.info(moved.join("\n"));
7443
- if (!pinnedImagesReady({ version: "0.6.6" })) {
7565
+ if (!pinnedImagesReady({ version: "0.6.8" })) {
7444
7566
  cancel4("The pinned Openship image tag isn't in the registry yet \u2014 nothing on this box was changed.");
7445
7567
  process.exit(1);
7446
7568
  }
@@ -7464,7 +7586,7 @@ async function runWizard() {
7464
7586
  dashboardPort,
7465
7587
  publicUrl,
7466
7588
  trustProxy: behindProxy,
7467
- version: "0.6.6",
7589
+ version: "0.6.8",
7468
7590
  noHostControl: !allowHostControl
7469
7591
  });
7470
7592
  if (!fetched.ok) {
@@ -7493,7 +7615,7 @@ async function runWizard() {
7493
7615
  dashboardPort,
7494
7616
  publicUrl,
7495
7617
  trustProxy: behindProxy,
7496
- version: "0.6.6",
7618
+ version: "0.6.8",
7497
7619
  noHostControl: !allowHostControl
7498
7620
  });
7499
7621
  if (!up.ok) {
@@ -7594,7 +7716,7 @@ async function runWizard() {
7594
7716
  if (result.liveUrl) liveUrl2 = result.liveUrl;
7595
7717
  else if (!result.domainRegistered) liveUrl2 = `http://localhost:${started.dashPort}`;
7596
7718
  for (const w of result.warnings) log5.warn(w);
7597
- const { verifyHostChannel } = await import("./host-channel-preflight-KE6OONYK.js");
7719
+ const { verifyHostChannel } = await import("./host-channel-preflight-OMCZMMKR.js");
7598
7720
  await verifyHostChannel().catch(() => {
7599
7721
  });
7600
7722
  finishSetup({
@@ -7873,7 +7995,7 @@ function attachCompletion(program2) {
7873
7995
 
7874
7996
  // src/index.ts
7875
7997
  var program = new Command28();
7876
- program.name("openship").description("Openship CLI \u2014 install, run, and manage Openship from your terminal").version("0.6.6").option("--json", "Machine-readable JSON output (stdout data only)").hook("preAction", (thisCommand) => {
7998
+ program.name("openship").description("Openship CLI \u2014 install, run, and manage Openship from your terminal").version("0.6.8").option("--json", "Machine-readable JSON output (stdout data only)").hook("preAction", (thisCommand) => {
7877
7999
  if (thisCommand.opts().json) setJsonMode(true);
7878
8000
  }).action(async () => {
7879
8001
  const installed = serviceStatus().installed || readInstallMethod() === "compose";
@@ -4,8 +4,9 @@ import { createRequire as __ospCreateRequire } from "node:module";
4
4
  const require = __ospCreateRequire(import.meta.url);
5
5
  import {
6
6
  LocalExecutor
7
- } from "./chunk-6FH4AXVY.js";
8
- import "./chunk-ZFJXPCQP.js";
7
+ } from "./chunk-T6DVUX4F.js";
8
+ import "./chunk-P7RCYELG.js";
9
+ import "./chunk-WEZFTL4V.js";
9
10
  import "./chunk-PXKCZB2I.js";
10
11
  export {
11
12
  LocalExecutor
@@ -14,15 +14,18 @@ import {
14
14
  renderProxyOptions,
15
15
  renderProxyTlsOptions,
16
16
  summarizeCertbotFailure
17
- } from "./chunk-QVW76WDL.js";
18
- import "./chunk-RSR7PFIQ.js";
17
+ } from "./chunk-IAMOUDHY.js";
18
+ import "./chunk-7PEXM2UI.js";
19
19
  import "./chunk-I3DPMTSF.js";
20
- import "./chunk-YMMQRE5J.js";
21
- import "./chunk-UVNMQM7W.js";
22
- import "./chunk-ZFJXPCQP.js";
23
- import "./chunk-2YM3NI2E.js";
24
- import "./chunk-SM26HDS2.js";
25
- import "./chunk-UJ6JZM65.js";
20
+ import "./chunk-R4VDPFLS.js";
21
+ import "./chunk-GNOMEMNI.js";
22
+ import "./chunk-NPSRGIHV.js";
23
+ import "./chunk-P7RCYELG.js";
24
+ import "./chunk-X53JXRS4.js";
25
+ import "./chunk-BO6CY4V2.js";
26
+ import "./chunk-D6HU4VUZ.js";
27
+ import "./chunk-N5YZMO33.js";
28
+ import "./chunk-WEZFTL4V.js";
26
29
  import "./chunk-PXKCZB2I.js";
27
30
  export {
28
31
  MANAGED_STATIC_BASE,
@@ -4,7 +4,7 @@ import { createRequire as __ospCreateRequire } from "node:module";
4
4
  const require = __ospCreateRequire(import.meta.url);
5
5
  import {
6
6
  NoopInfraProvider
7
- } from "./chunk-RXQBD4ZY.js";
7
+ } from "./chunk-RHLNVZMS.js";
8
8
  import "./chunk-PXKCZB2I.js";
9
9
  export {
10
10
  NoopInfraProvider
@@ -40,11 +40,14 @@ import {
40
40
  ensureOpenRestyConfig,
41
41
  luaSourceAvailable,
42
42
  parseNginxVersion
43
- } from "./chunk-RSR7PFIQ.js";
44
- import "./chunk-YMMQRE5J.js";
45
- import "./chunk-UVNMQM7W.js";
46
- import "./chunk-ZFJXPCQP.js";
47
- import "./chunk-UJ6JZM65.js";
43
+ } from "./chunk-7PEXM2UI.js";
44
+ import "./chunk-GNOMEMNI.js";
45
+ import "./chunk-NPSRGIHV.js";
46
+ import "./chunk-P7RCYELG.js";
47
+ import "./chunk-BO6CY4V2.js";
48
+ import "./chunk-D6HU4VUZ.js";
49
+ import "./chunk-N5YZMO33.js";
50
+ import "./chunk-WEZFTL4V.js";
48
51
  import "./chunk-PXKCZB2I.js";
49
52
  export {
50
53
  ACME_CHALLENGE_LOCATION,
@@ -6,11 +6,12 @@ import {
6
6
  privilegedExecutor,
7
7
  rootChecked,
8
8
  rootOrDegrade
9
- } from "./chunk-4I2BM2DT.js";
10
- import "./chunk-YMMQRE5J.js";
11
- import "./chunk-UVNMQM7W.js";
12
- import "./chunk-ZFJXPCQP.js";
13
- import "./chunk-UJ6JZM65.js";
9
+ } from "./chunk-T46BQOLY.js";
10
+ import "./chunk-GNOMEMNI.js";
11
+ import "./chunk-NPSRGIHV.js";
12
+ import "./chunk-P7RCYELG.js";
13
+ import "./chunk-N5YZMO33.js";
14
+ import "./chunk-WEZFTL4V.js";
14
15
  import "./chunk-PXKCZB2I.js";
15
16
  export {
16
17
  privilegedExecutor,