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.
- package/dist/{bare-XQIPDO4R.js → bare-OXJVW3AK.js} +11 -10
- package/dist/{chunk-WE7BXW6L.js → chunk-2YM3NI2E.js} +2 -2
- package/dist/{chunk-RFWJE7ZN.js → chunk-4I2BM2DT.js} +3 -3
- package/dist/{chunk-QLX7NBEA.js → chunk-5DMWSTOM.js} +78 -1836
- package/dist/{chunk-VDESJSG6.js → chunk-77QDF6UD.js} +135 -21
- package/dist/{chunk-7334NSJL.js → chunk-A4GXYE4L.js} +67 -31
- package/dist/{chunk-HPXP6LXZ.js → chunk-CBLD2HS5.js} +3 -3
- package/dist/{chunk-6JELW75A.js → chunk-FE7ZZQSE.js} +3 -3
- package/dist/{chunk-XVOBL7BC.js → chunk-HKRQXPTI.js} +1 -1
- package/dist/{chunk-GRTXMUHA.js → chunk-LFE5KQ7Y.js} +11 -11
- package/dist/{chunk-TM2IQPYU.js → chunk-LH2PLRQO.js} +2 -2
- package/dist/{chunk-LFCSC4IU.js → chunk-LUKYPVJ4.js} +3 -3
- package/dist/{chunk-VHMOQL5Y.js → chunk-MLYSEZQB.js} +115 -19
- package/dist/{chunk-7S4USQYF.js → chunk-NK5N5LXN.js} +50 -122
- package/dist/{chunk-3ZAGC5NT.js → chunk-OBPKYXQL.js} +17 -6
- package/dist/{chunk-5OUWIPOB.js → chunk-PCPVTH6M.js} +12 -84
- package/dist/chunk-Q7WGTQQ5.js +88 -0
- package/dist/{chunk-VLQ3UIVO.js → chunk-QVW76WDL.js} +52 -26
- package/dist/{chunk-PZRRL5PC.js → chunk-RSR7PFIQ.js} +14 -3
- package/dist/{chunk-A7EWM2KA.js → chunk-SM26HDS2.js} +1 -1
- package/dist/{chunk-LEYSL46B.js → chunk-UJ6JZM65.js} +3426 -632
- package/dist/{chunk-WFRXYWK2.js → chunk-UVNMQM7W.js} +1 -1
- package/dist/chunk-VTGPEC6B.js +308 -0
- package/dist/chunk-WVAML4H6.js +2145 -0
- package/dist/{chunk-MSZUCNPV.js → chunk-YIMOCZBZ.js} +1 -1
- package/dist/{chunk-EZPPTWJQ.js → chunk-YMMQRE5J.js} +1 -1
- package/dist/{cloud-3DODOWTR.js → cloud-75VIT4KV.js} +16 -15
- package/dist/{detect-P4CIQBRH.js → detect-OIE5V3VZ.js} +3 -3
- package/dist/{docker-3K2NEBD6.js → docker-VTR2GK5Y.js} +7 -6
- package/dist/{docker-edge-executor-YZZ6P7IC.js → docker-edge-executor-22IBXNYJ.js} +4 -4
- package/dist/{edge-import-MCJNVTZ2.js → edge-import-WJUB4M7F.js} +23 -22
- package/dist/{edge-preflight-ES6FH4JK.js → edge-preflight-GBNCPNVU.js} +10 -10
- package/dist/{ensure-container-edge-YPITNH45.js → ensure-container-edge-KPI2CVXJ.js} +9 -9
- package/dist/{executor-DZGX55K2.js → executor-7LXXZPBR.js} +6 -3
- package/dist/{host-channel-preflight-MUXE3KZU.js → host-channel-preflight-KE6OONYK.js} +23 -22
- package/dist/index.js +95 -84
- package/dist/{nginx-NSAHWGJV.js → nginx-KUFMLMAC.js} +9 -7
- package/dist/{openresty-lua-DR4D3EZ6.js → openresty-lua-ATTH35BE.js} +6 -4
- package/dist/{privilege-YTVI4SKF.js → privilege-I4W3ZNZK.js} +4 -4
- package/dist/server/engine/samples/amavisd/amavisd.conf +25 -0
- package/dist/server/engine/samples/postfix/master.cf +14 -0
- package/dist/server/index.js +35002 -27533
- package/dist/server/migrations/0105_mail_inbound_rules.sql +80 -0
- package/dist/server/migrations/0106_mcp_call_tracking.sql +23 -0
- package/dist/server/migrations/0107_build_minute_metering_indexes.sql +20 -0
- package/dist/server/migrations/0108_credential.sql +68 -0
- package/dist/server/migrations/meta/_journal.json +28 -0
- package/dist/{setup-WUUZURHS.js → setup-XGLGUUFW.js} +10 -10
- package/dist/ssh-client-JBMA7SN4.js +28 -0
- package/dist/{up-XEO3SR6Z.js → up-K4BV7466.js} +26 -27
- package/package.json +1 -1
- package/dist/chunk-6HOA5JT7.js +0 -124
- package/dist/chunk-XRNRT37W.js +0 -316
- /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-
|
|
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-
|
|
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
|
|
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
|
|
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 {
|
|
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
|
-
|
|
1713
|
-
|
|
1714
|
-
|
|
1715
|
-
|
|
1716
|
-
|
|
1717
|
-
|
|
1718
|
-
|
|
1719
|
-
|
|
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-
|
|
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-
|
|
66
|
+
} from "./chunk-2YM3NI2E.js";
|
|
62
67
|
import {
|
|
63
68
|
isValidEmail
|
|
64
|
-
} from "./chunk-
|
|
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
|
|
72
|
-
import { createWriteStream as createWriteStream2, existsSync as
|
|
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}
|
|
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
|
|
931
|
-
|
|
932
|
-
|
|
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 (
|
|
1104
|
-
|
|
1105
|
-
const secret =
|
|
1106
|
-
|
|
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 (!
|
|
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
|
-
|
|
1492
|
-
|
|
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 (
|
|
1459
|
+
if (existsSync4(engineDir)) env.MAIL_SERVER_ENGINE_DIR = engineDir;
|
|
1525
1460
|
}
|
|
1526
1461
|
env.OPENSHIP_REQUIRE_AUTH = "true";
|
|
1527
|
-
env.INTERNAL_TOKEN =
|
|
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.
|
|
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-
|
|
8
|
+
} from "./chunk-A4GXYE4L.js";
|
|
9
9
|
import {
|
|
10
10
|
createDockerBuildContext,
|
|
11
11
|
normalizeDockerRelativePath,
|
|
12
12
|
resolveDockerfileCandidates,
|
|
13
13
|
resolveWithinDirectory
|
|
14
|
-
} from "./chunk-
|
|
14
|
+
} from "./chunk-LUKYPVJ4.js";
|
|
15
15
|
import {
|
|
16
16
|
prepareStackOutput,
|
|
17
17
|
resolveProjectDir,
|
|
18
18
|
resolveStaticOutputPath,
|
|
19
19
|
runLocalBuild
|
|
20
|
-
} from "./chunk-
|
|
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-
|
|
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-
|
|
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
|
-
|
|
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-
|
|
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
|
|
19
|
+
import { execFile } from "child_process";
|
|
94
20
|
import { access } from "fs/promises";
|
|
95
21
|
import { homedir } from "os";
|
|
96
|
-
import { join
|
|
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
|
-
|
|
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 =
|
|
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,
|