openship 0.6.5 → 0.6.6

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 (54) hide show
  1. package/dist/{bare-XQIPDO4R.js → bare-OXJVW3AK.js} +11 -10
  2. package/dist/{chunk-WE7BXW6L.js → chunk-2YM3NI2E.js} +2 -2
  3. package/dist/{chunk-RFWJE7ZN.js → chunk-4I2BM2DT.js} +3 -3
  4. package/dist/{chunk-QLX7NBEA.js → chunk-5DMWSTOM.js} +78 -1836
  5. package/dist/{chunk-VDESJSG6.js → chunk-77QDF6UD.js} +135 -21
  6. package/dist/{chunk-7334NSJL.js → chunk-A4GXYE4L.js} +67 -31
  7. package/dist/{chunk-HPXP6LXZ.js → chunk-CBLD2HS5.js} +3 -3
  8. package/dist/{chunk-6JELW75A.js → chunk-FE7ZZQSE.js} +3 -3
  9. package/dist/{chunk-XVOBL7BC.js → chunk-HKRQXPTI.js} +1 -1
  10. package/dist/{chunk-GRTXMUHA.js → chunk-LFE5KQ7Y.js} +11 -11
  11. package/dist/{chunk-TM2IQPYU.js → chunk-LH2PLRQO.js} +2 -2
  12. package/dist/{chunk-LFCSC4IU.js → chunk-LUKYPVJ4.js} +3 -3
  13. package/dist/{chunk-VHMOQL5Y.js → chunk-MLYSEZQB.js} +115 -19
  14. package/dist/{chunk-7S4USQYF.js → chunk-NK5N5LXN.js} +50 -122
  15. package/dist/{chunk-3ZAGC5NT.js → chunk-OBPKYXQL.js} +17 -6
  16. package/dist/{chunk-5OUWIPOB.js → chunk-PCPVTH6M.js} +12 -84
  17. package/dist/chunk-Q7WGTQQ5.js +88 -0
  18. package/dist/{chunk-VLQ3UIVO.js → chunk-QVW76WDL.js} +52 -26
  19. package/dist/{chunk-PZRRL5PC.js → chunk-RSR7PFIQ.js} +14 -3
  20. package/dist/{chunk-A7EWM2KA.js → chunk-SM26HDS2.js} +1 -1
  21. package/dist/{chunk-LEYSL46B.js → chunk-UJ6JZM65.js} +3426 -632
  22. package/dist/{chunk-WFRXYWK2.js → chunk-UVNMQM7W.js} +1 -1
  23. package/dist/chunk-VTGPEC6B.js +308 -0
  24. package/dist/chunk-WVAML4H6.js +2145 -0
  25. package/dist/{chunk-MSZUCNPV.js → chunk-YIMOCZBZ.js} +1 -1
  26. package/dist/{chunk-EZPPTWJQ.js → chunk-YMMQRE5J.js} +1 -1
  27. package/dist/{cloud-3DODOWTR.js → cloud-75VIT4KV.js} +16 -15
  28. package/dist/{detect-P4CIQBRH.js → detect-OIE5V3VZ.js} +3 -3
  29. package/dist/{docker-3K2NEBD6.js → docker-VTR2GK5Y.js} +7 -6
  30. package/dist/{docker-edge-executor-YZZ6P7IC.js → docker-edge-executor-22IBXNYJ.js} +4 -4
  31. package/dist/{edge-import-MCJNVTZ2.js → edge-import-WJUB4M7F.js} +23 -22
  32. package/dist/{edge-preflight-ES6FH4JK.js → edge-preflight-GBNCPNVU.js} +10 -10
  33. package/dist/{ensure-container-edge-YPITNH45.js → ensure-container-edge-KPI2CVXJ.js} +9 -9
  34. package/dist/{executor-DZGX55K2.js → executor-7LXXZPBR.js} +6 -3
  35. package/dist/{host-channel-preflight-MUXE3KZU.js → host-channel-preflight-KE6OONYK.js} +23 -22
  36. package/dist/index.js +95 -84
  37. package/dist/{nginx-NSAHWGJV.js → nginx-KUFMLMAC.js} +9 -7
  38. package/dist/{openresty-lua-DR4D3EZ6.js → openresty-lua-ATTH35BE.js} +6 -4
  39. package/dist/{privilege-YTVI4SKF.js → privilege-I4W3ZNZK.js} +4 -4
  40. package/dist/server/engine/samples/amavisd/amavisd.conf +25 -0
  41. package/dist/server/engine/samples/postfix/master.cf +14 -0
  42. package/dist/server/index.js +35002 -27533
  43. package/dist/server/migrations/0105_mail_inbound_rules.sql +80 -0
  44. package/dist/server/migrations/0106_mcp_call_tracking.sql +23 -0
  45. package/dist/server/migrations/0107_build_minute_metering_indexes.sql +20 -0
  46. package/dist/server/migrations/0108_credential.sql +68 -0
  47. package/dist/server/migrations/meta/_journal.json +28 -0
  48. package/dist/{setup-WUUZURHS.js → setup-XGLGUUFW.js} +10 -10
  49. package/dist/ssh-client-JBMA7SN4.js +28 -0
  50. package/dist/{up-XEO3SR6Z.js → up-K4BV7466.js} +26 -27
  51. package/package.json +1 -1
  52. package/dist/chunk-6HOA5JT7.js +0 -124
  53. package/dist/chunk-XRNRT37W.js +0 -316
  54. /package/dist/{dist-es-ZP5QDSJ3.js → dist-es-AXFRS7PS.js} +0 -0
@@ -3,14 +3,16 @@
3
3
  import { createRequire as __ospCreateRequire } from "node:module";
4
4
  const require = __ospCreateRequire(import.meta.url);
5
5
  import {
6
- connectSshClient,
7
6
  getTarCreateEnv,
7
+ prepareSourceTarArgs
8
+ } from "./chunk-Q7WGTQQ5.js";
9
+ import {
10
+ connectSshClient,
8
11
  openDockerDialStdioChannel,
9
12
  openSftp,
10
13
  openSshUnixSocket,
11
- prepareSourceTarArgs,
12
14
  reconcileKnownHosts
13
- } from "./chunk-5OUWIPOB.js";
15
+ } from "./chunk-PCPVTH6M.js";
14
16
  import {
15
17
  LocalExecutor
16
18
  } from "./chunk-6FH4AXVY.js";
@@ -22,6 +24,7 @@ import {
22
24
  sq
23
25
  } from "./chunk-ZFJXPCQP.js";
24
26
  import {
27
+ HOST_CHANNEL_AUTH_REJECTED,
25
28
  HOST_CHANNEL_NOT_PROVISIONED,
26
29
  HOST_CHANNEL_UNPROVISIONED,
27
30
  HostChannelUnavailableError,
@@ -30,9 +33,11 @@ import {
30
33
  commandForError,
31
34
  explainHostChannelCause,
32
35
  formatBytes,
36
+ hostChannelAccount,
33
37
  hostFirewallRule,
38
+ isSshAuthError,
34
39
  safeErrorMessage
35
- } from "./chunk-LEYSL46B.js";
40
+ } from "./chunk-UJ6JZM65.js";
36
41
 
37
42
  // ../../packages/adapters/src/system/executor.ts
38
43
  import { existsSync, readFileSync } from "fs";
@@ -1625,7 +1630,7 @@ function hostChannelPort() {
1625
1630
  return Number.isInteger(raw) && raw > 0 && raw < 65536 ? raw : 22;
1626
1631
  }
1627
1632
  function hostChannelUser() {
1628
- return process.env.OPENSHIP_HOST_SSH_USER?.trim() || "root";
1633
+ return hostChannelAccount(process.env);
1629
1634
  }
1630
1635
  function readHostChannelKey(keyPath) {
1631
1636
  let contents;
@@ -1670,6 +1675,80 @@ function explainDialFailure(probe, host, port, target) {
1670
1675
  const cidr = containerBridgeCidr();
1671
1676
  return { hint, rule: hostFirewallRule("unknown", cidr ? [cidr] : [], port) };
1672
1677
  }
1678
+ var AUTH_MEMO_OK_MS = 3e4;
1679
+ var AUTH_MEMO_FAIL_MS = 5e3;
1680
+ var authMemo = null;
1681
+ function invalidateHostChannelAuth() {
1682
+ authMemo = null;
1683
+ }
1684
+ function probeForwarding(client, timeoutMs) {
1685
+ return new Promise((resolve) => {
1686
+ let settled = false;
1687
+ const finish = (verdict) => {
1688
+ if (settled) return;
1689
+ settled = true;
1690
+ clearTimeout(timer);
1691
+ resolve(verdict);
1692
+ };
1693
+ const timer = setTimeout(() => finish("unknown"), timeoutMs);
1694
+ timer.unref?.();
1695
+ try {
1696
+ client.forwardOut("127.0.0.1", 0, "127.0.0.1", 1, (err, stream) => {
1697
+ if (stream) {
1698
+ stream.destroy();
1699
+ finish("ok");
1700
+ return;
1701
+ }
1702
+ const reason = err?.reason;
1703
+ const message = err?.message;
1704
+ if (reason === 1 || reason === 3) return finish("blocked");
1705
+ if (typeof message === "string" && /administratively prohibited/i.test(message)) {
1706
+ return finish("blocked");
1707
+ }
1708
+ finish("ok");
1709
+ });
1710
+ } catch {
1711
+ finish("unknown");
1712
+ }
1713
+ });
1714
+ }
1715
+ async function verifyHostChannelAuth(config) {
1716
+ const memoKey = `${config.username}@${config.host}:${config.port}#${config.privateKey.length}`;
1717
+ if (authMemo?.key === memoKey && authMemo.expires > Date.now()) {
1718
+ return authMemo.hint ? { hint: authMemo.hint, forwarding: authMemo.forwarding } : { forwarding: authMemo.forwarding };
1719
+ }
1720
+ let hint = null;
1721
+ let forwarding = "unknown";
1722
+ try {
1723
+ const { connectSshClient: connectSshClient2 } = await import("./ssh-client-JBMA7SN4.js");
1724
+ const client = await connectSshClient2({
1725
+ host: config.host,
1726
+ port: config.port,
1727
+ username: config.username,
1728
+ privateKey: config.privateKey,
1729
+ readyTimeoutMs: config.timeoutMs,
1730
+ // Load-bearing: it selects describeSshAuthFailure's host-channel wording, so the
1731
+ // hint an operator reads here is the same sentence the deploy log gives them.
1732
+ hostChannel: true
1733
+ });
1734
+ forwarding = await probeForwarding(
1735
+ client,
1736
+ config.timeoutMs
1737
+ );
1738
+ client.end();
1739
+ } catch (err) {
1740
+ if (isSshAuthError(err)) {
1741
+ hint = err instanceof Error ? err.message : HOST_CHANNEL_AUTH_REJECTED;
1742
+ }
1743
+ }
1744
+ authMemo = {
1745
+ key: memoKey,
1746
+ hint,
1747
+ forwarding,
1748
+ expires: Date.now() + (hint ? AUTH_MEMO_FAIL_MS : AUTH_MEMO_OK_MS)
1749
+ };
1750
+ return hint ? { hint, forwarding } : { forwarding };
1751
+ }
1673
1752
  async function hostChannelHealth(timeoutMs = 2500) {
1674
1753
  if (hostControlDisabled()) {
1675
1754
  return {
@@ -1692,32 +1771,48 @@ async function hostChannelHealth(timeoutMs = 2500) {
1692
1771
  const port = hostChannelPort();
1693
1772
  const target = `${hostChannelUser()}@${host}:${port}`;
1694
1773
  const keyPath = process.env.OPENSHIP_HOST_SSH_KEY?.trim();
1774
+ let privateKey;
1695
1775
  if (keyPath) {
1696
- const { reason } = readHostChannelKey(keyPath);
1697
- if (reason) {
1776
+ const read = readHostChannelKey(keyPath);
1777
+ if (read.reason) {
1698
1778
  return {
1699
1779
  ok: false,
1700
1780
  code: "key_unreadable",
1701
1781
  host,
1702
1782
  port,
1703
1783
  target,
1704
- hint: `${reason} ${HOST_CHANNEL_NOT_PROVISIONED}`
1784
+ hint: `${read.reason} ${HOST_CHANNEL_NOT_PROVISIONED}`
1705
1785
  };
1706
1786
  }
1787
+ privateKey = read.key;
1707
1788
  }
1708
1789
  const probe = await probeTcpDetailed(host, port, timeoutMs);
1709
- if (probe.ok) {
1710
- return { ok: true, code: "ok", host, port, target };
1790
+ if (!probe.ok) {
1791
+ return {
1792
+ ok: false,
1793
+ code: "unreachable",
1794
+ host,
1795
+ port,
1796
+ target,
1797
+ cause: probe.reason,
1798
+ ...explainDialFailure(probe, host, port, target)
1799
+ };
1711
1800
  }
1712
- return {
1713
- ok: false,
1714
- code: "unreachable",
1715
- host,
1716
- port,
1717
- target,
1718
- cause: probe.reason,
1719
- ...explainDialFailure(probe, host, port, target)
1720
- };
1801
+ let forwarding = "unknown";
1802
+ if (privateKey) {
1803
+ const verdict = await verifyHostChannelAuth({
1804
+ host,
1805
+ port,
1806
+ username: hostChannelUser(),
1807
+ privateKey,
1808
+ timeoutMs
1809
+ });
1810
+ if (verdict.hint) {
1811
+ return { ok: false, code: "auth_rejected", host, port, target, hint: verdict.hint };
1812
+ }
1813
+ forwarding = verdict.forwarding;
1814
+ }
1815
+ return { ok: true, code: "ok", host, port, target, forwarding };
1721
1816
  }
1722
1817
  function unavailableExecutor(reason, code = "not_configured") {
1723
1818
  const refuse = async () => {
@@ -1787,6 +1882,7 @@ export {
1787
1882
  setHostControlOverride,
1788
1883
  hostControlDisabled,
1789
1884
  containerBridgeCidr,
1885
+ invalidateHostChannelAuth,
1790
1886
  hostChannelHealth,
1791
1887
  unavailableExecutor,
1792
1888
  createHostExecutor
@@ -2,19 +2,6 @@
2
2
  ":" //# ; exec "$(command -v node || command -v bun)" "$0" "$@"
3
3
  import { createRequire as __ospCreateRequire } from "node:module";
4
4
  const require = __ospCreateRequire(import.meta.url);
5
- import {
6
- importMigratedSites
7
- } from "./chunk-6HOA5JT7.js";
8
- import {
9
- completeHostEdge,
10
- markStoppedProxyImported,
11
- planAndApplyHostEdge,
12
- previewHostEdge,
13
- rollbackHostEdge
14
- } from "./chunk-HPXP6LXZ.js";
15
- import {
16
- verifyHostChannel
17
- } from "./chunk-XRNRT37W.js";
18
5
  import {
19
6
  DEFAULT_REPO,
20
7
  PORTS_FILE,
@@ -40,9 +27,27 @@ import {
40
27
  resolveComposePorts,
41
28
  resolvePorts,
42
29
  sourceBuildDir,
30
+ verifyHostChannel
31
+ } from "./chunk-WVAML4H6.js";
32
+ import {
33
+ bootstrapAdmin,
34
+ importMigratedSites,
35
+ internalFetch,
36
+ internalPost,
37
+ mintBareInternalToken,
38
+ waitHealthy
39
+ } from "./chunk-VTGPEC6B.js";
40
+ import {
43
41
  startUnitHint,
44
42
  thisHost
45
- } from "./chunk-QLX7NBEA.js";
43
+ } from "./chunk-5DMWSTOM.js";
44
+ import {
45
+ completeHostEdge,
46
+ markStoppedProxyImported,
47
+ planAndApplyHostEdge,
48
+ previewHostEdge,
49
+ rollbackHostEdge
50
+ } from "./chunk-CBLD2HS5.js";
46
51
  import {
47
52
  AUTH_SECRET_FILE,
48
53
  DATA_DIR,
@@ -58,18 +63,18 @@ import {
58
63
  EDGE_CONTAINER_NAME,
59
64
  edgeCrashReason,
60
65
  edgeIsBroken
61
- } from "./chunk-WE7BXW6L.js";
66
+ } from "./chunk-2YM3NI2E.js";
62
67
  import {
63
68
  isValidEmail
64
- } from "./chunk-LEYSL46B.js";
69
+ } from "./chunk-UJ6JZM65.js";
65
70
 
66
71
  // src/commands/up.ts
67
72
  import { Command } from "commander";
68
73
  import chalk2 from "chalk";
69
74
  import ora from "ora";
70
75
  import { spawn } from "child_process";
71
- import { randomBytes as randomBytes2 } from "crypto";
72
- import { createWriteStream as createWriteStream2, existsSync as existsSync5, mkdirSync as mkdirSync6, readFileSync as readFileSync4, writeFileSync as writeFileSync5 } from "fs";
76
+ import { randomBytes } from "crypto";
77
+ import { createWriteStream as createWriteStream2, existsSync as existsSync4, mkdirSync as mkdirSync5, readFileSync as readFileSync3, writeFileSync as writeFileSync4 } from "fs";
73
78
  import { dirname as dirname2, join as join5 } from "path";
74
79
  import { fileURLToPath } from "url";
75
80
 
@@ -615,9 +620,16 @@ async function planUp(opts) {
615
620
  ...stack.buildFile ? [stack.buildFile] : [],
616
621
  ...stack.hostChannel ? [
617
622
  stack.hostChannel.keyPath,
618
- `${stack.hostChannel.authKeysPath}${stack.hostChannel.viaSudo ? " (via sudo)" : ""} (authorizes that key for ${stack.hostChannel.user}'s host ops; --no-host-control skips it)`,
623
+ `${stack.hostChannel.authKeysPath} (authorizes that key for ${stack.hostChannel.user}'s host ops; --no-host-control skips it)`,
624
+ // Say which route to root this channel will take, rather than repeating a root
625
+ // warning at a box that reaches root perfectly well via sudo. `sudo` is a
626
+ // supported shape now, not a degradation, and the preview is where an operator
627
+ // decides whether that is what they wanted.
628
+ ...stack.hostChannel.elevation === "sudo" ? [
629
+ `logs in as ${stack.hostChannel.user} and elevates with sudo for root host ops (no root login is created)`
630
+ ] : [],
619
631
  ...stack.hostChannel.rootUnavailable ? [
620
- "\u26A0 host control needs root \u2014 this user isn't root and passwordless sudo isn't available, so mail/edge host ops will fail (re-run as root to fix)"
632
+ "\u26A0 host control needs root \u2014 this user isn't root and passwordless sudo isn't available, so mail/edge host ops will fail (re-run as root, or enable passwordless sudo, to fix)"
621
633
  ] : []
622
634
  ] : [],
623
635
  ...stack.mountDirs.map((d) => `${d}/ (edge routing state + certs)`),
@@ -768,87 +780,6 @@ function serviceWord(kind) {
768
780
  }
769
781
  }
770
782
 
771
- // src/lib/loopback-api.ts
772
- import { existsSync as existsSync4, readFileSync as readFileSync3, mkdirSync as mkdirSync5, writeFileSync as writeFileSync4 } from "fs";
773
- import { randomBytes } from "crypto";
774
- function ensureInternalToken() {
775
- const path = INTERNAL_TOKEN_FILE;
776
- if (existsSync4(path)) return readFileSync3(path, "utf8").trim();
777
- mkdirSync5(OS_DIR, { recursive: true, mode: 448 });
778
- const token = randomBytes(32).toString("hex");
779
- writeFileSync4(path, token, { mode: 384 });
780
- return token;
781
- }
782
- async function internalGet(port, path, token) {
783
- try {
784
- const res = await fetch(`http://127.0.0.1:${port}${path}`, {
785
- headers: { "X-Internal-Token": token ?? ensureInternalToken() },
786
- signal: AbortSignal.timeout(1e4)
787
- });
788
- if (!res.ok) return null;
789
- return await res.json();
790
- } catch {
791
- return null;
792
- }
793
- }
794
- async function internalPost(port, path, body, token) {
795
- try {
796
- const res = await fetch(`http://127.0.0.1:${port}${path}`, {
797
- method: "POST",
798
- headers: { "Content-Type": "application/json", "X-Internal-Token": token ?? ensureInternalToken() },
799
- body: JSON.stringify(body),
800
- signal: AbortSignal.timeout(3e4)
801
- });
802
- const data = await res.json().catch(() => ({}));
803
- return { ok: res.ok, data };
804
- } catch (err) {
805
- return { ok: false, data: { error: err.message } };
806
- }
807
- }
808
- async function bootstrapAdmin(apiPort, admin, token) {
809
- const { ok, data } = await internalPost(apiPort, "/api/system/bootstrap-admin", admin, token);
810
- if (ok) return { ok: true };
811
- if (data?.error === "An admin account already exists") return { ok: true, message: "already-exists" };
812
- return { ok: false, message: data?.error || "failed" };
813
- }
814
- async function waitHealthy(apiPort, seconds = 90) {
815
- for (let i = 0; i < seconds; i++) {
816
- await new Promise((r) => setTimeout(r, 1e3));
817
- try {
818
- await fetch(`http://127.0.0.1:${apiPort}/api/health`, { signal: AbortSignal.timeout(2e3) });
819
- return true;
820
- } catch {
821
- }
822
- }
823
- return false;
824
- }
825
- async function waitDashboard(dashPort, seconds = 45) {
826
- for (let i = 0; i < seconds; i++) {
827
- await new Promise((r) => setTimeout(r, 1e3));
828
- try {
829
- const res = await fetch(`http://127.0.0.1:${dashPort}/`, {
830
- redirect: "manual",
831
- signal: AbortSignal.timeout(2e3)
832
- });
833
- if (res.status > 0) return true;
834
- } catch {
835
- }
836
- }
837
- return false;
838
- }
839
- async function detectPublicIp() {
840
- for (const url of ["https://api.ipify.org", "https://ifconfig.me/ip"]) {
841
- try {
842
- const res = await fetch(url, { signal: AbortSignal.timeout(3e3) });
843
- if (!res.ok) continue;
844
- const ip = (await res.text()).trim();
845
- if (/^[0-9.]+$/.test(ip) || ip.includes(":")) return ip;
846
- } catch {
847
- }
848
- }
849
- return null;
850
- }
851
-
852
783
  // src/lib/instance-provision.ts
853
784
  var HeadlessInputError = class extends Error {
854
785
  code = "HEADLESS_INPUT";
@@ -927,10 +858,14 @@ async function bootstrapOrReset(port, admin, token) {
927
858
  async function drainProvisionStream(port, sessionId, token, onLog) {
928
859
  let detail;
929
860
  try {
930
- const res = await fetch(`http://127.0.0.1:${port}/api/system/self-register/stream?id=${sessionId}`, {
931
- headers: { "X-Internal-Token": token ?? ensureInternalToken() },
932
- signal: AbortSignal.timeout(18e4)
933
- });
861
+ const call = await internalFetch(
862
+ port,
863
+ `/api/system/self-register/stream?id=${sessionId}`,
864
+ { signal: AbortSignal.timeout(18e4) },
865
+ token
866
+ );
867
+ if (call.kind !== "response") return { completed: false, detail: call.detail };
868
+ const res = call.res;
934
869
  if (!res.body) return { completed: false };
935
870
  const reader = res.body.getReader();
936
871
  const decoder = new TextDecoder();
@@ -1100,10 +1035,10 @@ var DIST_DIR = dirname2(fileURLToPath(import.meta.url));
1100
1035
  var SERVER_DIR = join5(DIST_DIR, "server");
1101
1036
  function ensureAuthSecret() {
1102
1037
  const path = AUTH_SECRET_FILE;
1103
- if (existsSync5(path)) return readFileSync4(path, "utf8").trim();
1104
- mkdirSync6(OS_DIR, { recursive: true, mode: 448 });
1105
- const secret = randomBytes2(32).toString("hex");
1106
- writeFileSync5(path, secret, { mode: 384 });
1038
+ if (existsSync4(path)) return readFileSync3(path, "utf8").trim();
1039
+ mkdirSync5(OS_DIR, { recursive: true, mode: 448 });
1040
+ const secret = randomBytes(32).toString("hex");
1041
+ writeFileSync4(path, secret, { mode: 384 });
1107
1042
  return secret;
1108
1043
  }
1109
1044
  var upCommand = new Command("up").description("Start Openship as a persistent service (boot + auto-restart); --foreground to run attached").option("--port <port>", "API port to listen on (default: 4000, or the next free port if it's taken)").option("--data-dir <dir>", "Directory for the embedded database").option("--dashboard-port <port>", "Dashboard port (default: 3001, or the next free port if it's taken)").option("--no-ui", "Run the API only \u2014 don't download/serve the dashboard").option("--ui-version <tag>", "Dashboard release tag to run (default: this CLI's version)").option(
@@ -1471,7 +1406,7 @@ async function runForeground(opts, source) {
1471
1406
  apiCwd = source.apiDir;
1472
1407
  } else {
1473
1408
  const serverEntry = join5(SERVER_DIR, "index.js");
1474
- if (!existsSync5(serverEntry)) {
1409
+ if (!existsSync4(serverEntry)) {
1475
1410
  console.error(
1476
1411
  chalk2.red("\n Bundled server not found in this install.") + chalk2.dim("\n Reinstall with `openship update` (or `npm i -g openship`).\n")
1477
1412
  );
@@ -1488,8 +1423,8 @@ async function runForeground(opts, source) {
1488
1423
  const publicUrl = effectivePublicUrl(opts.publicUrl);
1489
1424
  const managedEdge = Boolean(opts.managedEdge && publicUrl);
1490
1425
  const dataDir = opts.dataDir || DATA_DIR;
1491
- mkdirSync6(dataDir, { recursive: true });
1492
- mkdirSync6(LOG_DIR, { recursive: true });
1426
+ mkdirSync5(dataDir, { recursive: true });
1427
+ mkdirSync5(LOG_DIR, { recursive: true });
1493
1428
  const instanceLogPath = join5(LOG_DIR, "instance.log");
1494
1429
  const instanceLog = createWriteStream2(instanceLogPath, { flags: "w" });
1495
1430
  const env = {
@@ -1521,10 +1456,10 @@ async function runForeground(opts, source) {
1521
1456
  env.OPENSHIP_MIGRATIONS_DIR = join5(SERVER_DIR, "migrations");
1522
1457
  env.OPENSHIP_PGLITE_ASSETS_DIR = join5(SERVER_DIR, "pglite");
1523
1458
  const engineDir = join5(SERVER_DIR, "engine");
1524
- if (existsSync5(engineDir)) env.MAIL_SERVER_ENGINE_DIR = engineDir;
1459
+ if (existsSync4(engineDir)) env.MAIL_SERVER_ENGINE_DIR = engineDir;
1525
1460
  }
1526
1461
  env.OPENSHIP_REQUIRE_AUTH = "true";
1527
- env.INTERNAL_TOKEN = ensureInternalToken();
1462
+ env.INTERNAL_TOKEN = mintBareInternalToken();
1528
1463
  if (opts.mail) env.OPENSHIP_PRODUCT = "mail";
1529
1464
  if (!env.OPENSHIP_AUTH_MODE) env.OPENSHIP_AUTH_MODE = "local";
1530
1465
  env.OPENSHIP_API_HOST = "127.0.0.1";
@@ -1614,7 +1549,7 @@ async function runForeground(opts, source) {
1614
1549
  const uiSpinner = ora("Preparing the dashboard\u2026").start();
1615
1550
  try {
1616
1551
  const bundle = await ensureDashboard({
1617
- tag: source ? "local" : opts.uiVersion || `v${"0.6.5"}`,
1552
+ tag: source ? "local" : opts.uiVersion || `v${"0.6.6"}`,
1618
1553
  onProgress: (received, total) => {
1619
1554
  if (total) {
1620
1555
  uiSpinner.text = `Downloading dashboard\u2026 ${Math.round(received / total * 100)}%`;
@@ -1738,13 +1673,6 @@ export {
1738
1673
  restart,
1739
1674
  serviceStatus,
1740
1675
  stop,
1741
- ensureInternalToken,
1742
- internalGet,
1743
- internalPost,
1744
- bootstrapAdmin,
1745
- waitHealthy,
1746
- waitDashboard,
1747
- detectPublicIp,
1748
1676
  headlessProvision,
1749
1677
  normalizeUrl,
1750
1678
  upCommand,
@@ -5,27 +5,28 @@ const require = __ospCreateRequire(import.meta.url);
5
5
  import {
6
6
  checkGit,
7
7
  installGit
8
- } from "./chunk-7334NSJL.js";
8
+ } from "./chunk-A4GXYE4L.js";
9
9
  import {
10
10
  createDockerBuildContext,
11
11
  normalizeDockerRelativePath,
12
12
  resolveDockerfileCandidates,
13
13
  resolveWithinDirectory
14
- } from "./chunk-LFCSC4IU.js";
14
+ } from "./chunk-LUKYPVJ4.js";
15
15
  import {
16
16
  prepareStackOutput,
17
17
  resolveProjectDir,
18
18
  resolveStaticOutputPath,
19
19
  runLocalBuild
20
- } from "./chunk-MSZUCNPV.js";
20
+ } from "./chunk-YIMOCZBZ.js";
21
21
  import {
22
22
  BuildLogger,
23
23
  injectGitToken,
24
24
  runBuildPipeline,
25
25
  sq,
26
26
  transferLocalDirectory
27
- } from "./chunk-TM2IQPYU.js";
27
+ } from "./chunk-LH2PLRQO.js";
28
28
  import {
29
+ PRICING,
29
30
  STACKS,
30
31
  SYSTEM,
31
32
  TRANSFER_EXCLUDES,
@@ -35,7 +36,7 @@ import {
35
36
  packageManagerEnsureCommand,
36
37
  safeErrorMessage,
37
38
  shellSplitWords
38
- } from "./chunk-LEYSL46B.js";
39
+ } from "./chunk-UJ6JZM65.js";
39
40
 
40
41
  // ../../packages/adapters/src/runtime/cloud.ts
41
42
  import { readFile } from "fs/promises";
@@ -47,6 +48,11 @@ var DEFAULT_RESOURCE_CONFIG = {
47
48
  memoryMb: 512,
48
49
  diskMb: 5120
49
50
  };
51
+ var DEFAULT_BUILD_RESOURCE_CONFIG = {
52
+ cpuCores: PRICING.oblien.buildResources.cpuCores,
53
+ memoryMb: PRICING.oblien.buildResources.memoryMb,
54
+ diskMb: PRICING.oblien.buildResources.diskGb * 1024
55
+ };
50
56
  function cloudCpus(cpuCores) {
51
57
  return Math.max(0.25, cpuCores);
52
58
  }
@@ -1414,7 +1420,12 @@ var CloudRuntime = class {
1414
1420
  // per service and continues, rather than the workload coming up on its own
1415
1421
  // network with nothing having said so (#533).
1416
1422
  "networkMode",
1417
- "pidMode"
1423
+ "pidMode",
1424
+ // A workspace is not a container whose ENTRYPOINT we compose: Oblien resolves its
1425
+ // own workload command, so an override (or a clearing `[]`) has nowhere to land
1426
+ // here. Declared so the deploy says so once per service and continues, rather
1427
+ // than the image's own launcher running with nothing having mentioned it (#575).
1428
+ "entrypoint"
1418
1429
  ]);
1419
1430
  client;
1420
1431
  // Optional admin-scoped proxy. Set when this runtime is constructed
@@ -3,102 +3,31 @@
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,
6
+ HOST_CHANNEL_AUTH_REJECTED,
7
+ HOST_CHANNEL_NOT_PROVISIONED,
8
+ HOST_CHANNEL_ROW_CREDENTIALS_UNUSED,
7
9
  hostFirewallRule,
8
10
  isSshAuthError,
9
11
  safeErrorMessage,
10
12
  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
- }
13
+ } from "./chunk-UJ6JZM65.js";
88
14
 
89
15
  // ../../packages/adapters/src/system/ssh-client.ts
90
16
  import { Client } from "ssh2";
91
17
 
92
18
  // ../../packages/adapters/src/system/ssh-support.ts
93
- import { execFile as execFile2 } from "child_process";
19
+ import { execFile } from "child_process";
94
20
  import { access } from "fs/promises";
95
21
  import { homedir } from "os";
96
- import { join as join2 } from "path";
22
+ import { join } from "path";
97
23
  function formatSshTarget(config) {
98
24
  return `${config.username ?? "root"}@${config.host}:${config.port ?? 22}`;
99
25
  }
100
26
  function describeSshAuthFailure(config, originalMessage) {
101
27
  const target = formatSshTarget(config);
28
+ if (config.hostChannel) {
29
+ return `${HOST_CHANNEL_AUTH_REJECTED} Dialed ${target} from inside the Openship API container. ${HOST_CHANNEL_ROW_CREDENTIALS_UNUSED} ${HOST_CHANNEL_NOT_PROVISIONED} (${originalMessage})`;
30
+ }
102
31
  if (config.password) {
103
32
  return `SSH password authentication failed for ${target}. Check the username/password, or verify that the server allows password login. (${originalMessage})`;
104
33
  }
@@ -119,7 +48,7 @@ Or re-run \`openship up\`, which probes this and offers the exact rule. (${origi
119
48
  }
120
49
  function execFileText(command, args, timeout = 5e3) {
121
50
  return new Promise((resolve, reject) => {
122
- execFile2(command, args, { timeout }, (err, stdout, stderr) => {
51
+ execFile(command, args, { timeout }, (err, stdout, stderr) => {
123
52
  if (err) {
124
53
  reject(new Error(stderr.trim() || stdout.trim() || err.message));
125
54
  return;
@@ -141,7 +70,7 @@ function parseKnownHostsEntries(text) {
141
70
  return entries;
142
71
  }
143
72
  async function reconcileKnownHosts(config) {
144
- const knownHostsPath = join2(homedir(), ".ssh", "known_hosts");
73
+ const knownHostsPath = join(homedir(), ".ssh", "known_hosts");
145
74
  try {
146
75
  await access(knownHostsPath);
147
76
  } catch {
@@ -378,13 +307,12 @@ async function execSshCommand(client, command, timeout = 1e4) {
378
307
  }
379
308
 
380
309
  export {
381
- getTarCreateEnv,
382
- prepareSourceTarArgs,
383
310
  reconcileKnownHosts,
384
311
  connectSshClient,
385
312
  openSftp,
386
313
  openSshUnixSocket,
387
314
  DOCKER_DIAL_STDIO_COMMAND,
315
+ openSshExecChannel,
388
316
  attachDialStdioDiagnostics,
389
317
  dialStdioDiagnostics,
390
318
  openDockerDialStdioChannel,