openship 0.2.3 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1216,7 +1216,13 @@ function findClosingBracket(body, start) {
1216
1216
  const ch = body[i];
1217
1217
  const prev = body[i - 1];
1218
1218
  if (inDouble) {
1219
- if (ch === '"' && prev !== "\\") inDouble = false;
1219
+ if (ch === '"') {
1220
+ let backslashRun = 0;
1221
+ for (let j = i - 1; j >= 0 && body[j] === "\\"; j--) {
1222
+ backslashRun++;
1223
+ }
1224
+ if (backslashRun % 2 === 0) inDouble = false;
1225
+ }
1220
1226
  continue;
1221
1227
  }
1222
1228
  if (inSingle) {
@@ -3840,7 +3846,7 @@ async function runForeground(opts, source) {
3840
3846
  const uiSpinner = ora("Preparing the dashboard\u2026").start();
3841
3847
  try {
3842
3848
  const bundle = await ensureDashboard({
3843
- tag: source ? "local" : opts.uiVersion || `v${"0.2.3"}`,
3849
+ tag: source ? "local" : opts.uiVersion || `v${"0.3.0"}`,
3844
3850
  onProgress: (received, total) => {
3845
3851
  if (total) {
3846
3852
  uiSpinner.text = `Downloading dashboard\u2026 ${Math.round(received / total * 100)}%`;
@@ -7520,7 +7526,9 @@ function assetForPlatform() {
7520
7526
  return { name: arch === "arm64" ? "Openship-arm64.dmg" : "Openship-x64.dmg", kind: "dmg" };
7521
7527
  }
7522
7528
  if (platform === "win32") return { name: "Openship-win32-x64.zip", kind: "zip" };
7523
- if (platform === "linux") return { name: "Openship.AppImage", kind: "appimage" };
7529
+ if (platform === "linux") {
7530
+ return { name: arch === "arm64" ? "Openship-arm64.AppImage" : "Openship.AppImage", kind: "appimage" };
7531
+ }
7524
7532
  throw new Error(`Unsupported platform: ${platform} (${arch})`);
7525
7533
  }
7526
7534
  function installDmg(dmg) {
@@ -7715,7 +7723,7 @@ function detectPackageManager2(override) {
7715
7723
  return hasBun ? "bun" : "npm";
7716
7724
  }
7717
7725
  var updateCommand = new Command25("update").description("Update the Openship CLI + bundled server to the latest release").option("--check", "Only report the current + latest version; don't install").option("--via <manager>", "Package manager to update with: bun | npm").action(async (opts) => {
7718
- const current = "0.2.3";
7726
+ const current = "0.3.0";
7719
7727
  let latest;
7720
7728
  try {
7721
7729
  latest = (await resolveLatestTag()).replace(/^v/, "");
@@ -7743,7 +7751,7 @@ var updateCommand = new Command25("update").description("Update the Openship CLI
7743
7751
  const ref = `openship@${latest}`;
7744
7752
  const argv = pm === "bun" ? ["add", "-g", ref] : ["install", "-g", ref];
7745
7753
  info(`Updating v${current} \u2192 v${latest} (${cliInstallCommand(pm, latest)})...`);
7746
- const res = spawnSync6(pm, argv, { stdio: "inherit" });
7754
+ const res = spawnSync6(pm, argv, { stdio: "inherit", shell: process.platform === "win32" });
7747
7755
  if (res.status !== 0) {
7748
7756
  err(`Update failed (${pm} exited ${res.status ?? "with a signal"}). Reinstall manually: ${cliInstallCommand(pm, latest)}`);
7749
7757
  process.exitCode = 1;
@@ -8025,12 +8033,13 @@ async function promptLocalAdmin() {
8025
8033
  }
8026
8034
  async function streamProvision(port, sessionId, s) {
8027
8035
  let ok3 = false;
8036
+ let detail;
8028
8037
  try {
8029
8038
  const res = await fetch(`http://127.0.0.1:${port}/api/system/self-register/stream?id=${sessionId}`, {
8030
8039
  headers: { "X-Internal-Token": ensureInternalToken() },
8031
8040
  signal: AbortSignal.timeout(3e5)
8032
8041
  });
8033
- if (!res.ok || !res.body) return false;
8042
+ if (!res.ok || !res.body) return { ok: false };
8034
8043
  const reader = res.body.getReader();
8035
8044
  const decoder = new TextDecoder();
8036
8045
  let buffer = "";
@@ -8048,23 +8057,29 @@ async function streamProvision(port, sessionId, s) {
8048
8057
  if (event === "log" && dataRaw) {
8049
8058
  try {
8050
8059
  const d = JSON.parse(dataRaw);
8051
- if (d.message) s.message(String(d.message).replace(/\s+/g, " ").slice(0, 68));
8060
+ if (d.message) {
8061
+ const msg = String(d.message).replace(/\s+/g, " ");
8062
+ s.message(msg.slice(0, 68));
8063
+ if (d.level === "warn" || d.level === "error") detail = msg;
8064
+ }
8052
8065
  } catch {
8053
8066
  }
8054
8067
  } else if (event === "complete" && dataRaw) {
8055
8068
  try {
8056
- ok3 = JSON.parse(dataRaw).status === "completed";
8069
+ const d = JSON.parse(dataRaw);
8070
+ ok3 = d.status === "completed";
8071
+ if (!ok3 && typeof d.error === "string") detail = d.error;
8057
8072
  } catch {
8058
8073
  }
8059
8074
  } else if (event === "end") {
8060
- return ok3;
8075
+ return { ok: ok3, detail };
8061
8076
  }
8062
8077
  }
8063
8078
  }
8064
8079
  } catch {
8065
- return ok3;
8080
+ return { ok: ok3, detail };
8066
8081
  }
8067
- return ok3;
8082
+ return { ok: ok3, detail };
8068
8083
  }
8069
8084
  async function runWizard() {
8070
8085
  intro2(`${chalk17.bgCyan(chalk17.black(" Openship "))}${chalk17.dim(" setup")}`);
@@ -8269,7 +8284,7 @@ async function runWizard() {
8269
8284
  domainPlan = { type: "byo", hostname };
8270
8285
  break planning;
8271
8286
  }
8272
- const uiTag = `v${"0.2.3"}`;
8287
+ const uiTag = `v${"0.3.0"}`;
8273
8288
  const dl = spinner2();
8274
8289
  dl.start("Pulling the Openship dist from GitHub");
8275
8290
  try {
@@ -8388,6 +8403,20 @@ async function runWizard() {
8388
8403
  if (status && !status.canProceedClean && status.occupants?.length) {
8389
8404
  const owner = status.occupants.map((o) => o.command ?? `port ${o.port}`).join(", ");
8390
8405
  const known = status.classification === "known";
8406
+ const sites = pf.ok && Array.isArray(pf.data?.sites) ? pf.data.sites : [];
8407
+ if (sites.length > 0) {
8408
+ const lines = sites.map((st) => {
8409
+ const host = (st.serverNames ?? []).join(", ") || "(no server_name)";
8410
+ const dest = st.target?.kind === "static" ? `static: ${st.target?.root ?? ""}` : st.target?.url ?? "";
8411
+ return `${chalk17.bold(host)} \u2192 ${chalk17.dim(dest)}${st.ssl ? chalk17.green(" [TLS]") : ""}`;
8412
+ });
8413
+ note(lines.join("\n"), `Detected ${sites.length} site${sites.length === 1 ? "" : "s"} on ${owner}`);
8414
+ }
8415
+ const warns = pf.ok && Array.isArray(pf.data?.warnings) ? pf.data.warnings : [];
8416
+ if (warns.length > 0) {
8417
+ log2.warn(`${warns.length} config item${warns.length === 1 ? "" : "s"} won't migrate automatically:`);
8418
+ for (const w of warns.slice(0, 8)) log2.message(chalk17.dim(`\u2022 ${w}`));
8419
+ }
8391
8420
  const choice = ensure(
8392
8421
  await select({
8393
8422
  message: known ? `An existing reverse proxy (${owner}) is serving ports 80/443.` : `Ports 80/443 are in use by ${owner}, which we couldn't identify.`,
@@ -8434,10 +8463,13 @@ async function runWizard() {
8434
8463
  if (res.ok && res.data?.sessionId) {
8435
8464
  const s2 = spinner2();
8436
8465
  s2.start("Issuing HTTPS certificate (OpenResty + Let's Encrypt)");
8437
- const done = await streamProvision(port, res.data.sessionId, s2);
8466
+ const { ok: done, detail } = await streamProvision(port, res.data.sessionId, s2);
8438
8467
  liveUrl = res.data.url ?? liveUrl;
8439
8468
  if (done) s2.stop(`HTTPS ready: ${liveUrl}`);
8440
- else s2.stop("HTTPS isn't ready yet \u2014 it retries on reboot; the site serves over HTTP meanwhile.", 1);
8469
+ else {
8470
+ s2.stop("HTTPS isn't ready yet \u2014 it retries on reboot; the site serves over HTTP meanwhile.", 1);
8471
+ if (detail) log2.warn(detail);
8472
+ }
8441
8473
  } else {
8442
8474
  log2.warn(`Couldn't start domain provisioning: ${res.data?.error || "failed"}`);
8443
8475
  }
@@ -8545,7 +8577,7 @@ ${chalk17.dim("Dashboard".padEnd(11))}${dashUrl}
8545
8577
 
8546
8578
  // src/index.ts
8547
8579
  var program = new Command27();
8548
- program.name("openship").description("Openship CLI \u2014 install, run, and manage Openship from your terminal").version("0.2.3").option("--json", "Machine-readable JSON output (stdout data only)").hook("preAction", (thisCommand) => {
8580
+ program.name("openship").description("Openship CLI \u2014 install, run, and manage Openship from your terminal").version("0.3.0").option("--json", "Machine-readable JSON output (stdout data only)").hook("preAction", (thisCommand) => {
8549
8581
  if (thisCommand.opts().json) setJsonMode(true);
8550
8582
  }).action(async () => {
8551
8583
  if (serviceStatus().installed) await runControl();
@@ -4,43 +4,25 @@ var __getProtoOf = Object.getPrototypeOf;
4
4
  var __defProp = Object.defineProperty;
5
5
  var __getOwnPropNames = Object.getOwnPropertyNames;
6
6
  var __hasOwnProp = Object.prototype.hasOwnProperty;
7
- function __accessProp(key) {
8
- return this[key];
9
- }
10
- var __toESMCache_node;
11
- var __toESMCache_esm;
12
7
  var __toESM = (mod, isNodeMode, target) => {
13
- var canCache = mod != null && typeof mod === "object";
14
- if (canCache) {
15
- var cache = isNodeMode ? __toESMCache_node ??= new WeakMap : __toESMCache_esm ??= new WeakMap;
16
- var cached = cache.get(mod);
17
- if (cached)
18
- return cached;
19
- }
20
8
  target = mod != null ? __create(__getProtoOf(mod)) : {};
21
9
  const to = isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target;
22
10
  for (let key of __getOwnPropNames(mod))
23
11
  if (!__hasOwnProp.call(to, key))
24
12
  __defProp(to, key, {
25
- get: __accessProp.bind(mod, key),
13
+ get: () => mod[key],
26
14
  enumerable: true
27
15
  });
28
- if (canCache)
29
- cache.set(mod, to);
30
16
  return to;
31
17
  };
32
18
  var __commonJS = (cb, mod) => () => (mod || cb((mod = { exports: {} }).exports, mod), mod.exports);
33
- var __returnValue = (v) => v;
34
- function __exportSetter(name2, newValue) {
35
- this[name2] = __returnValue.bind(null, newValue);
36
- }
37
19
  var __export = (target, all) => {
38
20
  for (var name2 in all)
39
21
  __defProp(target, name2, {
40
22
  get: all[name2],
41
23
  enumerable: true,
42
24
  configurable: true,
43
- set: __exportSetter.bind(all, name2)
25
+ set: (newValue) => all[name2] = () => newValue
44
26
  });
45
27
  };
46
28
  var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
@@ -1166,7 +1148,7 @@ function bytesToBase64Url(bytes) {
1166
1148
  return b64.replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
1167
1149
  }
1168
1150
  function slugify(text) {
1169
- return text.toLowerCase().replace(/[^\w\s-]/g, "").replace(/[\s_]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 100);
1151
+ return text.toLowerCase().replace(/[^\w\s-]/g, "").replace(/[\s_]+/g, "-").slice(0, 100).replace(/^-+|-+$/g, "");
1170
1152
  }
1171
1153
  function normalizeCustomHostname(raw) {
1172
1154
  return raw.trim().toLowerCase().replace(/^https?:\/\//, "").replace(/\/+$/, "");
@@ -1194,11 +1176,11 @@ function generateId(prefix) {
1194
1176
  return prefix ? `${prefix}_${id}` : id;
1195
1177
  }
1196
1178
  function formatBytes(bytes, decimals = 2) {
1197
- if (bytes === 0)
1179
+ if (bytes <= 0)
1198
1180
  return "0 B";
1199
1181
  const k = 1024;
1200
1182
  const sizes = ["B", "KB", "MB", "GB", "TB"];
1201
- const i2 = Math.floor(Math.log(bytes) / Math.log(k));
1183
+ const i2 = Math.min(Math.max(Math.floor(Math.log(bytes) / Math.log(k)), 0), sizes.length - 1);
1202
1184
  return `${parseFloat((bytes / Math.pow(k, i2)).toFixed(decimals))} ${sizes[i2]}`;
1203
1185
  }
1204
1186
 
@@ -1408,8 +1390,14 @@ function findClosingBracket(body2, start2) {
1408
1390
  const ch = body2[i2];
1409
1391
  const prev = body2[i2 - 1];
1410
1392
  if (inDouble) {
1411
- if (ch === '"' && prev !== "\\")
1412
- inDouble = false;
1393
+ if (ch === '"') {
1394
+ let backslashRun = 0;
1395
+ for (let j = i2 - 1;j >= 0 && body2[j] === "\\"; j--) {
1396
+ backslashRun++;
1397
+ }
1398
+ if (backslashRun % 2 === 0)
1399
+ inDouble = false;
1400
+ }
1413
1401
  continue;
1414
1402
  }
1415
1403
  if (inSingle) {
@@ -12421,15 +12409,17 @@ function certbotInstallPlan(profile) {
12421
12409
  var OPENRESTY_APT_SOURCES, systemCatalog;
12422
12410
  var init_catalog2 = __esm(() => {
12423
12411
  OPENRESTY_APT_SOURCES = [
12412
+ 'OR_ARCH="$(dpkg --print-architecture 2>/dev/null || echo amd64)"',
12413
+ 'if [ "$OR_ARCH" = arm64 ]; then OR_BASE="http://openresty.org/package/arm64/$REPO"; else OR_BASE="http://openresty.org/package/$REPO"; fi',
12424
12414
  'REPO_CODENAME="$(lsb_release -sc 2>/dev/null || { . /etc/os-release 2>/dev/null && echo "$VERSION_CODENAME"; })" || REPO_CODENAME=""',
12425
12415
  'if [ "$REPO" = debian ]; then OR_FALLBACKS="bookworm bullseye"; else OR_FALLBACKS="noble jammy focal"; fi',
12426
12416
  'OR_CODENAME=""',
12427
12417
  "for c in $REPO_CODENAME $OR_FALLBACKS; do",
12428
- ' if wget -q --spider --tries=2 --timeout=15 "http://openresty.org/package/$REPO/dists/$c/Release"; then OR_CODENAME="$c"; break; fi',
12418
+ ' if wget -q --spider --tries=2 --timeout=15 "$OR_BASE/dists/$c/Release"; then OR_CODENAME="$c"; break; fi',
12429
12419
  "done",
12430
12420
  'if [ -z "$OR_CODENAME" ]; then case "$REPO" in debian) OR_CODENAME=bookworm ;; *) OR_CODENAME=noble ;; esac; fi',
12431
12421
  '[ "$OR_CODENAME" = "$REPO_CODENAME" ] || echo "[openresty] apt repo has no codename $REPO_CODENAME; using nearest supported LTS $OR_CODENAME" >&2',
12432
- 'echo "deb [signed-by=/usr/share/keyrings/openresty.gpg] http://openresty.org/package/$REPO $OR_CODENAME main" > /etc/apt/sources.list.d/openresty.list'
12422
+ 'echo "deb [arch=$OR_ARCH signed-by=/usr/share/keyrings/openresty.gpg] $OR_BASE $OR_CODENAME main" > /etc/apt/sources.list.d/openresty.list'
12433
12423
  ];
12434
12424
  systemCatalog = {
12435
12425
  checks: {
@@ -13509,7 +13499,12 @@ async function ensureEdgeClear(executor, config, onLog) {
13509
13499
  scan = await scanImportableSites(executor, proxy2);
13510
13500
  }
13511
13501
  const migratable = scan && scan.sites.length > 0;
13512
- const message = migratable ? `Openship needs ports 80 and 443, but ${owner} is already serving them ` + `(${scan.sites.length} site${scan.sites.length === 1 ? "" : "s"}). Migrate those sites ` + `into Openship and take over, just stop it and take over, or cancel?` : known ? `Openship needs ports 80 and 443, but ${owner} is already serving them. ` + `Stop it and take over, or cancel and leave it running?` : `Openship needs ports 80 and 443, but ${owner} is already using them and ` + `we can't identify it. Stop it and take over, or cancel and leave it running?`;
13502
+ const message = migratable ? `Openship runs its own load balancer (OpenResty) on ports 80 and 443, but ${owner} is ` + `already serving them (${scan.sites.length} site${scan.sites.length === 1 ? "" : "s"}). ` + `Migrate those sites into Openship and take over, just stop it and take over, or cancel?` : known ? `Openship runs its own load balancer (OpenResty) on ports 80 and 443, but ${owner} is ` + `already serving them. Stop it and take over, or cancel and leave it running?` : `Openship runs its own load balancer (OpenResty) on ports 80 and 443, but ${owner} is ` + `already using them and we can't identify it. Stop it and take over, or cancel and leave it running?`;
13503
+ const details = {
13504
+ edge: status,
13505
+ sites: scan?.sites ?? [],
13506
+ warnings: scan?.warnings ?? []
13507
+ };
13513
13508
  const action = await config.promptUser({
13514
13509
  promptId: "edge_conflict",
13515
13510
  title: known ? "Existing reverse proxy detected" : "Ports 80/443 are in use",
@@ -13519,7 +13514,7 @@ async function ensureEdgeClear(executor, config, onLog) {
13519
13514
  { id: "override", label: "Stop it & take over", variant: "danger" },
13520
13515
  { id: "cancel", label: "Cancel", variant: "secondary" }
13521
13516
  ],
13522
- details: { edge: status, sites: scan?.sites ?? [], warnings: scan?.warnings ?? [] }
13517
+ details
13523
13518
  });
13524
13519
  if (action === "migrate" && scan) {
13525
13520
  throw new EdgeMigrateRequested(status, scan.sites, scan.warnings);
@@ -29125,7 +29120,7 @@ var require_serde = __commonJS((exports) => {
29125
29120
  }
29126
29121
  return value.slice(idx);
29127
29122
  };
29128
- var LazyJsonString = function LazyJsonString2(val) {
29123
+ var LazyJsonString = function LazyJsonString(val) {
29129
29124
  const str = Object.assign(new String(val), {
29130
29125
  deserializeJSON() {
29131
29126
  return JSON.parse(String(val));
@@ -34442,7 +34437,7 @@ var require_dist_cjs5 = __commonJS((exports) => {
34442
34437
  return httpRequest;
34443
34438
  }
34444
34439
  }
34445
- var createIsIdentityExpiredFunction = (expirationMs) => function isIdentityExpired2(identity2) {
34440
+ var createIsIdentityExpiredFunction = (expirationMs) => function isIdentityExpired(identity2) {
34446
34441
  return doesIdentityRequireRefresh(identity2) && identity2.expiration.getTime() - Date.now() < expirationMs;
34447
34442
  };
34448
34443
  var EXPIRATION_MS = 300000;
@@ -57255,7 +57250,7 @@ var require_checksum = __commonJS((exports) => {
57255
57250
  totalBytesRead += slice.size;
57256
57251
  }
57257
57252
  }
57258
- var blobHasher = async function blobHasher2(hashCtor, blob) {
57253
+ var blobHasher = async function blobHasher(hashCtor, blob) {
57259
57254
  const hash = new hashCtor;
57260
57255
  await blobReader(blob, (chunk) => {
57261
57256
  hash.update(chunk);
@@ -80888,7 +80883,7 @@ var require_lib = __commonJS((exports, module2) => {
80888
80883
  3793,
80889
80884
  7920
80890
80885
  ];
80891
- var toUTF8Array = function toUTF8Array2(str) {
80886
+ var toUTF8Array = function toUTF8Array(str) {
80892
80887
  var char;
80893
80888
  var i2 = 0;
80894
80889
  var p = 0;
@@ -80915,7 +80910,7 @@ var require_lib = __commonJS((exports, module2) => {
80915
80910
  }
80916
80911
  return utf8;
80917
80912
  };
80918
- var generate = module2.exports = function generate2(str) {
80913
+ var generate = module2.exports = function generate(str) {
80919
80914
  var char;
80920
80915
  var i2 = 0;
80921
80916
  var start2 = -1;
@@ -140159,6 +140154,9 @@ var init_infer_result = () => {};
140159
140154
 
140160
140155
  // ../../node_modules/kysely/dist/esm/index.js
140161
140156
  var init_esm = __esm(() => {
140157
+ init_expression_builder();
140158
+ init_log_once();
140159
+ init_query_id();
140162
140160
  init_kysely();
140163
140161
  init_query_creator();
140164
140162
  init_expression();
@@ -172653,8 +172651,9 @@ function createProjectRepo(db5) {
172653
172651
  };
172654
172652
  },
172655
172653
  async create(data) {
172656
- const id2 = generateId("proj");
172657
- const row = { id: id2, ...data };
172654
+ const { id: providedId, ...rest } = data;
172655
+ const id2 = providedId ?? generateId("proj");
172656
+ const row = { id: id2, ...rest };
172658
172657
  await db5.insert(project).values(row);
172659
172658
  return { ...row, createdAt: new Date, updatedAt: new Date };
172660
172659
  },
@@ -210866,7 +210865,7 @@ var require_node2 = __commonJS((exports) => {
210866
210865
  readString16 = readString2(3);
210867
210866
  readString32 = readString2(5);
210868
210867
  function readString2(headerLength) {
210869
- return function readString3(length) {
210868
+ return function readString(length) {
210870
210869
  let string10 = strings[stringPosition++];
210871
210870
  if (string10 == null) {
210872
210871
  if (bundledStrings$1)
@@ -230130,12 +230129,12 @@ var require_expression = __commonJS((exports, module2) => {
230130
230129
  }
230131
230130
  return resultArr.join(" ");
230132
230131
  };
230133
- CronExpression.parse = function parse17(expression2, options) {
230132
+ CronExpression.parse = function parse(expression2, options) {
230134
230133
  var self2 = this;
230135
230134
  if (typeof options === "function") {
230136
230135
  options = {};
230137
230136
  }
230138
- function parse18(expression3, options2) {
230137
+ function parse17(expression3, options2) {
230139
230138
  if (!options2) {
230140
230139
  options2 = {};
230141
230140
  }
@@ -230191,7 +230190,7 @@ var require_expression = __commonJS((exports, module2) => {
230191
230190
  return val2;
230192
230191
  }
230193
230192
  }
230194
- return parse18(expression2, options);
230193
+ return parse17(expression2, options);
230195
230194
  };
230196
230195
  CronExpression.fieldsToExpression = function fieldsToExpression(fields, options) {
230197
230196
  function validateConstraints(field2, values2, constraints) {
@@ -230255,13 +230254,13 @@ var require_parser3 = __commonJS((exports, module2) => {
230255
230254
  throw new Error("Invalid entry: " + entry);
230256
230255
  }
230257
230256
  };
230258
- CronParser.parseExpression = function parseExpression2(expression2, options) {
230257
+ CronParser.parseExpression = function parseExpression(expression2, options) {
230259
230258
  return CronExpression.parse(expression2, options);
230260
230259
  };
230261
230260
  CronParser.fieldsToExpression = function fieldsToExpression(fields, options) {
230262
230261
  return CronExpression.fieldsToExpression(fields, options);
230263
230262
  };
230264
- CronParser.parseString = function parseString2(data) {
230263
+ CronParser.parseString = function parseString(data) {
230265
230264
  var blocks = data.split(`
230266
230265
  `);
230267
230266
  var response = {
@@ -231852,7 +231851,7 @@ var require_sandbox = __commonJS((exports) => {
231852
231851
  Object.defineProperty(exports, "__esModule", { value: true });
231853
231852
  var enums_1 = require_enums();
231854
231853
  var sandbox = (processFile, childPool) => {
231855
- return async function process5(job2, token, signal) {
231854
+ return async function process(job2, token, signal) {
231856
231855
  let child;
231857
231856
  let msgHandler;
231858
231857
  let exitHandler;
@@ -239477,6 +239476,48 @@ async function createProductionProject(data, slug, organizationId) {
239477
239476
  throw err2;
239478
239477
  }
239479
239478
  }
239479
+ async function createServicesProjectWithId(opts) {
239480
+ await assertProjectQuota(opts.organizationId);
239481
+ const slug = await uniqueProjectSlug(opts.organizationId, opts.slug);
239482
+ const group = await repos.projectGroup.create({
239483
+ organizationId: opts.organizationId,
239484
+ name: opts.name,
239485
+ slug,
239486
+ gitProvider: opts.gitProvider ?? undefined,
239487
+ gitOwner: opts.gitOwner ?? undefined,
239488
+ gitRepo: opts.gitRepo ?? undefined,
239489
+ gitUrl: projectGitUrl(opts.gitOwner, opts.gitRepo)
239490
+ });
239491
+ try {
239492
+ const routing2 = deriveNextProjectRouteState({ slug }, { slug });
239493
+ const created = await repos.project.create({
239494
+ id: opts.id,
239495
+ organizationId: opts.organizationId,
239496
+ groupId: group.id,
239497
+ name: opts.name,
239498
+ slug,
239499
+ environmentName: "Production",
239500
+ environmentSlug: "production",
239501
+ environmentType: "production",
239502
+ gitProvider: opts.gitProvider ?? "github",
239503
+ gitOwner: opts.gitOwner ?? undefined,
239504
+ gitRepo: opts.gitRepo ?? undefined,
239505
+ gitBranch: opts.gitBranch ?? "main",
239506
+ gitUrl: projectGitUrl(opts.gitOwner, opts.gitRepo),
239507
+ autoDeploy: !!opts.autoDeploy,
239508
+ framework: "unknown",
239509
+ packageManager: "npm",
239510
+ hasServer: true,
239511
+ hasBuild: opts.hasBuild ?? false,
239512
+ runtimeMode: opts.runtimeMode === "bare" ? "bare" : "docker"
239513
+ });
239514
+ await persistProjectRouteState(created.id, routing2.publicEndpoints);
239515
+ return created;
239516
+ } catch (err2) {
239517
+ await repos.projectGroup.softDelete(group.id).catch(() => {});
239518
+ throw err2;
239519
+ }
239520
+ }
239480
239521
  async function uniqueProjectSlug(organizationId, baseSlug) {
239481
239522
  let slug = baseSlug;
239482
239523
  let suffix = 2;
@@ -255152,8 +255193,11 @@ async function triggerDeployment(ctx2, data) {
255152
255193
  if (!reuse) {
255153
255194
  snapshot.buildStrategy = await resolveStrategy(snapshot.framework, snapshot.buildStrategy, { deployTarget: snapshot.deployTarget });
255154
255195
  }
255196
+ const { useServicePipeline, servicePreflightServices } = await resolveServicePipelineMode(project2, snapshot);
255155
255197
  await runDeploymentPreflight(snapshot, routeState, {
255156
255198
  ctx: ctx2,
255199
+ composeServices: servicePreflightServices,
255200
+ multiService: useServicePipeline,
255157
255201
  gitOwner: project2.gitOwner,
255158
255202
  projectId: project2.id
255159
255203
  });
@@ -260195,6 +260239,22 @@ async function runEnsure(progress, options) {
260195
260239
  return { ok: false, reason: "migrate_failed" };
260196
260240
  return { ok: true };
260197
260241
  }
260242
+ if (!options?.edgeTakeover) {
260243
+ const status = await probeEdge2(executor);
260244
+ if (!status.canProceedClean && status.occupants.length > 0) {
260245
+ const owner = status.occupants.map((o4) => o4.command ?? `port ${o4.port}`).join(", ");
260246
+ let siteCount = 0;
260247
+ try {
260248
+ const proxy2 = status.occupants.find((o4) => o4.proxy)?.proxy;
260249
+ if (proxy2 && canImportProxy2(proxy2)) {
260250
+ siteCount = (await scanImportableSites2(executor, proxy2)).sites.length;
260251
+ }
260252
+ } catch {}
260253
+ const sitesNote = siteCount > 0 ? ` serving ${siteCount} site${siteCount === 1 ? "" : "s"}` : "";
260254
+ log7(`An existing proxy (${owner})${sitesNote} is using ports 80 and 443. Openship needs its own ` + `load balancer (OpenResty) there for managed HTTPS — left it running. Re-run setup and choose ` + `migrate or take-over to continue.`, "warn");
260255
+ return { ok: false, reason: "edge_conflict", occupants: owner, siteCount };
260256
+ }
260257
+ }
260198
260258
  const installerConfig = options?.edgeTakeover ? { edgePolicy: { mode: "takeover", stopTargets: [] } } : undefined;
260199
260259
  const system2 = new SystemManager2("bare", { executor, installerConfig });
260200
260260
  await system2.ensureFeature("ssl", (entry) => log7(entry.message));
@@ -267880,9 +267940,51 @@ function reconcileStack(opts) {
267880
267940
  networks: networks.map((n3) => ({ name: n3.name, driver: n3.driver })),
267881
267941
  warnings,
267882
267942
  adoptable: services.length > 0,
267883
- alreadyManaged
267943
+ alreadyManaged,
267944
+ openshipProjects: opts.openshipProjects ?? []
267884
267945
  };
267885
267946
  }
267947
+ function reconcileOpenshipProjects(opts) {
267948
+ const { managedDetails, manifestById, knownHereIds, imageDefaults, imageCmds } = opts;
267949
+ const byProject = new Map;
267950
+ for (const d2 of managedDetails) {
267951
+ const projectId = d2.labels["openship.project"];
267952
+ if (!projectId)
267953
+ continue;
267954
+ if (d2.labels["openship.build"])
267955
+ continue;
267956
+ const list9 = byProject.get(projectId) ?? [];
267957
+ list9.push(d2);
267958
+ byProject.set(projectId, list9);
267959
+ }
267960
+ const out2 = [];
267961
+ for (const [projectId, details] of byProject) {
267962
+ const entry = manifestById?.get(projectId);
267963
+ const services = details.map((d2) => {
267964
+ const svc = toDiscoveredService(d2, undefined, imageDefaults?.get(d2.image ?? ""), imageCmds?.get(d2.image ?? ""));
267965
+ const serviceLabel = d2.labels["openship.service"];
267966
+ return serviceLabel ? { ...svc, name: serviceLabel } : svc;
267967
+ });
267968
+ const deploymentId = details.find((d2) => d2.labels["openship.deployment"])?.labels["openship.deployment"] ?? entry?.deployment?.id;
267969
+ out2.push({
267970
+ projectId,
267971
+ knownHere: knownHereIds.has(projectId),
267972
+ suggestedName: entry?.name || entry?.slug || details.find((d2) => d2.composeProject)?.composeProject || `openship-${projectId.replace(/^proj_/, "").slice(0, 8)}`,
267973
+ slug: entry?.slug,
267974
+ domains: entry?.domains,
267975
+ source: entry ? {
267976
+ gitProvider: entry.gitProvider,
267977
+ gitOwner: entry.gitOwner,
267978
+ gitRepo: entry.gitRepo,
267979
+ gitBranch: entry.gitBranch
267980
+ } : undefined,
267981
+ runtimeMode: entry?.runtimeMode ?? undefined,
267982
+ deploymentId,
267983
+ services
267984
+ });
267985
+ }
267986
+ return out2;
267987
+ }
267886
267988
  var ENV_DENYLIST, EDGE_PORTS2;
267887
267989
  var init_docker_reconcile = __esm(() => {
267888
267990
  init_src2();
@@ -267964,8 +268066,12 @@ async function discoverServerStack(serverId, organizationId, onProgress) {
267964
268066
  const isOpenshipOwned = (labels) => Object.keys(labels).some((k4) => k4 === "openship" || k4.startsWith("openship."));
267965
268067
  const managed = containers.filter((c2) => isOpenshipOwned(c2.labels));
267966
268068
  const candidates = containers.filter((c2) => !isOpenshipOwned(c2.labels));
268069
+ const managedApp = managed.filter((c2) => c2.labels["openship.project"] && !c2.labels["openship.build"]);
267967
268070
  step(`Inspecting ${candidates.length} container(s)…`);
267968
- const details = (await mapLimit(candidates, 5, (c2) => rt2.inspectContainer(c2.id))).filter((d2) => d2 !== null);
268071
+ const [details, managedDetails] = await Promise.all([
268072
+ mapLimit(candidates, 5, (c2) => rt2.inspectContainer(c2.id)).then((d2) => d2.filter((x6) => x6 !== null)),
268073
+ mapLimit(managedApp, 5, (c2) => rt2.inspectContainer(c2.id)).then((d2) => d2.filter((x6) => x6 !== null))
268074
+ ]);
267969
268075
  const groups = new Map;
267970
268076
  for (const d2 of details) {
267971
268077
  const key = d2.composeProject ?? "";
@@ -267975,22 +268081,47 @@ async function discoverServerStack(serverId, organizationId, onProgress) {
267975
268081
  }
267976
268082
  step("Reading compose files…");
267977
268083
  const declared = await readComposeDeclarations(serverId, groups);
267978
- const uniqueImages = [...new Set(details.map((d2) => d2.image).filter(Boolean))];
268084
+ const uniqueImages = [
268085
+ ...new Set([...details, ...managedDetails].map((d2) => d2.image).filter(Boolean))
268086
+ ];
267979
268087
  const imageInfoPairs = await mapLimit(uniqueImages, 4, async (ref) => {
267980
268088
  const [env6, cmd] = await Promise.all([rt2.inspectImageEnv(ref), rt2.inspectImageCmd(ref)]);
267981
268089
  return [ref, { env: new Set(env6), cmd }];
267982
268090
  });
267983
268091
  const imageDefaults = new Map(imageInfoPairs.map(([ref, v3]) => [ref, v3.env]));
267984
268092
  const imageCmds = new Map(imageInfoPairs.map(([ref, v3]) => [ref, v3.cmd]));
268093
+ let openshipProjects = [];
268094
+ let alreadyManaged = 0;
268095
+ const projectIds = [...new Set(managedApp.map((c2) => c2.labels["openship.project"]).filter(Boolean))];
268096
+ if (projectIds.length > 0) {
268097
+ step("Recovering Openship projects…");
268098
+ const manifest = await sshManager.withExecutor(serverId, (exec3) => readManifest2(exec3)).catch(() => null);
268099
+ const manifestById = manifest ? new Map(manifest.projects.map((p4) => [p4.id, p4])) : null;
268100
+ const knownHereIds = new Set;
268101
+ await Promise.all(projectIds.map(async (id2) => {
268102
+ const row = await repos.project.findByIdInOrganization(id2, organizationId);
268103
+ if (row)
268104
+ knownHereIds.add(id2);
268105
+ }));
268106
+ openshipProjects = reconcileOpenshipProjects({
268107
+ managedDetails,
268108
+ manifestById,
268109
+ knownHereIds,
268110
+ imageDefaults,
268111
+ imageCmds
268112
+ });
268113
+ alreadyManaged = managedApp.filter((c2) => knownHereIds.has(c2.labels["openship.project"])).length;
268114
+ }
267985
268115
  return reconcileStack({
267986
268116
  serverId,
267987
268117
  details,
267988
268118
  volumes,
267989
268119
  networks,
267990
268120
  declared,
267991
- alreadyManaged: managed.length,
268121
+ alreadyManaged,
267992
268122
  imageDefaults,
267993
- imageCmds
268123
+ imageCmds,
268124
+ openshipProjects
267994
268125
  });
267995
268126
  } finally {
267996
268127
  await rt2.dispose();
@@ -267998,9 +268129,11 @@ async function discoverServerStack(serverId, organizationId, onProgress) {
267998
268129
  }
267999
268130
  var init_docker_inspect_service = __esm(async () => {
268000
268131
  init_compose_parser();
268132
+ init_openship_manifest();
268001
268133
  init_docker_reconcile();
268002
268134
  init_docker_reconcile();
268003
268135
  await __promiseAll([
268136
+ init_src3(),
268004
268137
  init_deployment_runtime(),
268005
268138
  init_ssh_manager()
268006
268139
  ]);
@@ -268031,28 +268164,7 @@ function normalizeHostPorts(ports, claimed) {
268031
268164
  });
268032
268165
  return { ports: out2, droppedDuplicates };
268033
268166
  }
268034
- async function adoptServerStack(opts) {
268035
- const { serverId, organizationId, projectName, serviceNames, sameServer, volumeStrategies } = opts;
268036
- const stack = await discoverServerStack(serverId, organizationId);
268037
- const selected = new Set(serviceNames);
268038
- const chosen = stack.services.filter((s4) => selected.has(s4.name) && !s4.proxyKind);
268039
- if (chosen.length === 0) {
268040
- throw new Error("None of the selected services were found on the server.");
268041
- }
268042
- if (!sameServer) {
268043
- const built = chosen.filter((s4) => Boolean(s4.build)).map((s4) => s4.name);
268044
- if (built.length > 0) {
268045
- throw new Error(`Cross-server migration can't move locally-built images yet (${built.join(", ")}). ` + `Take these over in place (migrate to the same server), or rebuild them from a registry image. Cross-server for built images is coming soon.`);
268046
- }
268047
- }
268048
- const anyBuild = chosen.some((s4) => !s4.image && Boolean(s4.build));
268049
- const ensureBody = {
268050
- name: projectName,
268051
- projectType: "services",
268052
- hasServer: true,
268053
- hasBuild: anyBuild
268054
- };
268055
- const { project_id, created } = await ensureProject(ensureBody, organizationId);
268167
+ function buildAdoptedServiceRows(chosen, selected) {
268056
268168
  const nameCounts = new Map;
268057
268169
  const firstUnique = new Map;
268058
268170
  const uniqueNames = chosen.map((s4) => {
@@ -268064,7 +268176,7 @@ async function adoptServerStack(opts) {
268064
268176
  return unique;
268065
268177
  });
268066
268178
  const claimedHostPorts = new Set;
268067
- const parsed = chosen.map((s4, i3) => {
268179
+ return chosen.map((s4, i3) => {
268068
268180
  const { ports, droppedDuplicates } = normalizeHostPorts(s4.ports, claimedHostPorts);
268069
268181
  if (droppedDuplicates.length > 0) {
268070
268182
  s4.warnings.push(`Host port(s) ${droppedDuplicates.join(", ")} already published by another service — ` + `kept ${uniqueNames[i3]} on the internal network only (reachable as ${uniqueNames[i3]}:<port>).`);
@@ -268084,6 +268196,30 @@ async function adoptServerStack(opts) {
268084
268196
  advanced: s4.healthcheck ? { healthcheck: s4.healthcheck } : undefined
268085
268197
  };
268086
268198
  });
268199
+ }
268200
+ async function adoptServerStack(opts) {
268201
+ const { serverId, organizationId, projectName, serviceNames, sameServer, volumeStrategies } = opts;
268202
+ const stack = await discoverServerStack(serverId, organizationId);
268203
+ const selected = new Set(serviceNames);
268204
+ const chosen = stack.services.filter((s4) => selected.has(s4.name) && !s4.proxyKind);
268205
+ if (chosen.length === 0) {
268206
+ throw new Error("None of the selected services were found on the server.");
268207
+ }
268208
+ if (!sameServer) {
268209
+ const built = chosen.filter((s4) => Boolean(s4.build)).map((s4) => s4.name);
268210
+ if (built.length > 0) {
268211
+ throw new Error(`Cross-server migration can't move locally-built images yet (${built.join(", ")}). ` + `Take these over in place (migrate to the same server), or rebuild them from a registry image. Cross-server for built images is coming soon.`);
268212
+ }
268213
+ }
268214
+ const anyBuild = chosen.some((s4) => !s4.image && Boolean(s4.build));
268215
+ const ensureBody = {
268216
+ name: projectName,
268217
+ projectType: "services",
268218
+ hasServer: true,
268219
+ hasBuild: anyBuild
268220
+ };
268221
+ const { project_id, created } = await ensureProject(ensureBody, organizationId);
268222
+ const parsed = buildAdoptedServiceRows(chosen, selected);
268087
268223
  const createdServices = await repos.service.syncFromCompose(project_id, parsed);
268088
268224
  for (const svc of createdServices) {
268089
268225
  const copy = Boolean(sameServer) && volumeStrategies?.[svc.name] === "copy";
@@ -268099,13 +268235,66 @@ async function adoptServerStack(opts) {
268099
268235
  adopted: chosen.map((s4) => s4.name)
268100
268236
  };
268101
268237
  }
268238
+ async function reimportOpenshipProject(opts) {
268239
+ const { serverId, organizationId, projectId, projectName, serviceNames } = opts;
268240
+ if (!PROJECT_ID_RE.test(projectId)) {
268241
+ throw new Error("Invalid Openship project id.");
268242
+ }
268243
+ const existing = await repos.project.findById(projectId);
268244
+ if (existing) {
268245
+ throw new Error("A project with this id already exists here — nothing to re-import.");
268246
+ }
268247
+ const stack = await discoverServerStack(serverId, organizationId);
268248
+ const group = stack.openshipProjects.find((p4) => p4.projectId === projectId);
268249
+ if (!group) {
268250
+ throw new Error("That Openship project was not found on the server.");
268251
+ }
268252
+ if (group.knownHere) {
268253
+ throw new Error("That Openship project is already managed by this instance.");
268254
+ }
268255
+ const selected = serviceNames?.length ? new Set(serviceNames) : new Set(group.services.map((s4) => s4.name));
268256
+ const chosen = group.services.filter((s4) => selected.has(s4.name) && !s4.proxyKind);
268257
+ if (chosen.length === 0) {
268258
+ throw new Error("None of the selected services were found on the server.");
268259
+ }
268260
+ const name2 = projectName?.trim() || group.suggestedName;
268261
+ const anyBuild = chosen.some((s4) => !s4.image && Boolean(s4.build));
268262
+ const created = await createServicesProjectWithId({
268263
+ id: projectId,
268264
+ name: name2,
268265
+ slug: group.slug || slugify(name2),
268266
+ organizationId,
268267
+ hasBuild: anyBuild,
268268
+ runtimeMode: group.runtimeMode === "bare" ? "bare" : "docker",
268269
+ gitProvider: group.source?.gitProvider ?? undefined,
268270
+ gitOwner: group.source?.gitOwner ?? undefined,
268271
+ gitRepo: group.source?.gitRepo ?? undefined,
268272
+ gitBranch: group.source?.gitBranch ?? undefined
268273
+ });
268274
+ const parsed = buildAdoptedServiceRows(chosen, selected);
268275
+ const createdServices = await repos.service.syncFromCompose(created.id, parsed);
268276
+ for (const svc of createdServices) {
268277
+ if (svc.namespaceVolumes !== false) {
268278
+ await repos.service.update(svc.id, { namespaceVolumes: false });
268279
+ }
268280
+ }
268281
+ return {
268282
+ projectId: created.id,
268283
+ slug: created.slug,
268284
+ reimported: chosen.map((s4) => s4.name),
268285
+ deferredDeployment: true
268286
+ };
268287
+ }
268288
+ var PROJECT_ID_RE;
268102
268289
  var init_migrate_service = __esm(async () => {
268290
+ init_src();
268103
268291
  init_docker_reconcile();
268104
268292
  await __promiseAll([
268105
268293
  init_src3(),
268106
268294
  init_project_crud_service(),
268107
268295
  init_docker_inspect_service()
268108
268296
  ]);
268297
+ PROJECT_ID_RE = /^proj_[A-Za-z0-9]+$/;
268109
268298
  });
268110
268299
 
268111
268300
  // ../api/src/modules/migration/migration-preflight.ts
@@ -268679,6 +268868,35 @@ async function adoptServer(c2) {
268679
268868
  return c2.json({ error: `Adopt failed: ${safeErrorMessage(err2)}` }, 502);
268680
268869
  }
268681
268870
  }
268871
+ async function reimportServer(c2) {
268872
+ const body2 = await c2.req.json();
268873
+ const { serverId, projectId, projectName, serviceNames } = body2;
268874
+ if (!serverId)
268875
+ return c2.json({ error: "serverId is required" }, 400);
268876
+ if (!projectId?.trim())
268877
+ return c2.json({ error: "projectId is required" }, 400);
268878
+ const ctx2 = getRequestContext(c2);
268879
+ await permission.assert(ctx2, {
268880
+ resourceType: "server",
268881
+ resourceId: serverId,
268882
+ action: "write"
268883
+ });
268884
+ if (!await isServerInOrg(ctx2, serverId)) {
268885
+ return c2.json({ error: "Server not found" }, 404);
268886
+ }
268887
+ try {
268888
+ const result2 = await reimportOpenshipProject({
268889
+ serverId,
268890
+ organizationId: ctx2.organizationId,
268891
+ projectId: projectId.trim(),
268892
+ projectName: projectName?.trim() || undefined,
268893
+ serviceNames: Array.isArray(serviceNames) ? serviceNames : undefined
268894
+ });
268895
+ return c2.json({ success: true, ...result2 });
268896
+ } catch (err2) {
268897
+ return c2.json({ error: `Re-import failed: ${safeErrorMessage(err2)}` }, 502);
268898
+ }
268899
+ }
268682
268900
  async function previewMigration(c2) {
268683
268901
  const body2 = await c2.req.json();
268684
268902
  const sourceServerId = body2.sourceServerId;
@@ -268821,6 +269039,7 @@ var init_migration_routes = __esm(async () => {
268821
269039
  r26.post("/scan", { tag: "server:write", collection: true }, scanServer2);
268822
269040
  r26.get("/scan/stream", { tag: "server:write", collection: true }, scanServerStream);
268823
269041
  r26.post("/adopt", { tag: "server:write", collection: true }, adoptServer);
269042
+ r26.post("/reimport", { tag: "server:write", collection: true }, reimportServer);
268824
269043
  r26.post("/preview", { tag: "server:write", collection: true }, previewMigration);
268825
269044
  r26.post("/migrate", { tag: "server:write", collection: true }, startMigration);
268826
269045
  r26.get("/migrations/:id", { tag: "server:read", collection: true }, getMigration);
@@ -278693,6 +278912,7 @@ var deploymentRoutes = r4.hono;
278693
278912
 
278694
278913
  // ../api/src/modules/domains/domain.routes.ts
278695
278914
  init_dist2();
278915
+
278696
278916
  // ../../node_modules/@sinclair/typebox/build/esm/errors/function.mjs
278697
278917
  function DefaultErrorFunction(error101) {
278698
278918
  switch (error101.errorType) {
@@ -279970,7 +280190,6 @@ function Errors(...args2) {
279970
280190
  const iterator = args2.length === 3 ? Visit6(args2[0], args2[1], "", args2[2]) : Visit6(args2[0], [], "", args2[1]);
279971
280191
  return new ValueErrorIterator(iterator);
279972
280192
  }
279973
-
279974
280193
  // ../../node_modules/@sinclair/typebox/build/esm/value/assert/assert.mjs
279975
280194
  var __classPrivateFieldSet = function(receiver, state, value, kind, f3) {
279976
280195
  if (kind === "m")
@@ -284822,7 +285041,7 @@ import { hostname as hostname6, userInfo } from "node:os";
284822
285041
  // ../api/package.json
284823
285042
  var package_default = {
284824
285043
  name: "@repo/api",
284825
- version: "0.2.3",
285044
+ version: "0.3.0",
284826
285045
  license: "Apache-2.0",
284827
285046
  private: true,
284828
285047
  type: "module",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "openship",
3
- "version": "0.2.3",
3
+ "version": "0.3.0",
4
4
  "description": "Openship CLI - deploy, manage, and open your Openship platform from the terminal",
5
5
  "license": "Apache-2.0",
6
6
  "repository": {