openship 0.6.6 → 0.6.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bare-FA66N7GC.js +29 -0
- package/dist/{chunk-77QDF6UD.js → chunk-3MP6ACKM.js} +587 -156
- package/dist/{chunk-YIMOCZBZ.js → chunk-5DJOB7IX.js} +3 -3
- package/dist/{chunk-WVAML4H6.js → chunk-5I3COAFH.js} +274 -65
- package/dist/{chunk-5DMWSTOM.js → chunk-5UXHT6ED.js} +940 -224
- package/dist/{chunk-LUKYPVJ4.js → chunk-6GU6JSK6.js} +329 -466
- package/dist/{chunk-RSR7PFIQ.js → chunk-7PEXM2UI.js} +213 -45
- package/dist/{chunk-VTGPEC6B.js → chunk-7YBWXEM6.js} +6 -4
- package/dist/chunk-AHJYP7DG.js +938 -0
- package/dist/chunk-BO6CY4V2.js +265 -0
- package/dist/{chunk-FE7ZZQSE.js → chunk-D3OEMXYP.js} +3 -3
- package/dist/chunk-D6HU4VUZ.js +249 -0
- package/dist/{chunk-LFE5KQ7Y.js → chunk-ENKJ7XIF.js} +152 -57
- package/dist/{chunk-YMMQRE5J.js → chunk-GNOMEMNI.js} +1 -1
- package/dist/{chunk-QVW76WDL.js → chunk-IAMOUDHY.js} +190 -64
- package/dist/{chunk-OBPKYXQL.js → chunk-MUWVC5R4.js} +219 -58
- package/dist/chunk-N5YZMO33.js +111 -0
- package/dist/{chunk-A4GXYE4L.js → chunk-NIYD7MD2.js} +206 -52
- package/dist/{chunk-UVNMQM7W.js → chunk-NPSRGIHV.js} +8 -6
- package/dist/{chunk-2GWYYHOR.js → chunk-NRU2EBYZ.js} +2 -2
- package/dist/{chunk-CBLD2HS5.js → chunk-NUT6HKK6.js} +8 -5
- package/dist/{chunk-ZFJXPCQP.js → chunk-P7RCYELG.js} +4 -3
- package/dist/chunk-R4VDPFLS.js +106 -0
- package/dist/{chunk-HKRQXPTI.js → chunk-R5DC5PEF.js} +1 -1
- package/dist/{chunk-NK5N5LXN.js → chunk-RCMERVBL.js} +24 -9
- package/dist/{chunk-RXQBD4ZY.js → chunk-RHLNVZMS.js} +2 -2
- package/dist/chunk-SK4FFCFR.js +70 -0
- package/dist/{chunk-4I2BM2DT.js → chunk-T46BQOLY.js} +3 -3
- package/dist/{chunk-6FH4AXVY.js → chunk-T6DVUX4F.js} +1 -1
- package/dist/{chunk-PCPVTH6M.js → chunk-TAZZZKYO.js} +6 -4
- package/dist/{chunk-MLYSEZQB.js → chunk-UAWY6X7V.js} +43 -72
- package/dist/{chunk-Q7WGTQQ5.js → chunk-VAI5VOOI.js} +1 -1
- package/dist/{chunk-UJ6JZM65.js → chunk-WEZFTL4V.js} +1325 -513
- package/dist/{chunk-2YM3NI2E.js → chunk-X53JXRS4.js} +38 -44
- package/dist/{cloud-KRTZ2IL5.js → cloud-2CRDKOWQ.js} +1 -1
- package/dist/cloud-REV3DPJH.js +42 -0
- package/dist/{detect-OIE5V3VZ.js → detect-BBFT5GEU.js} +9 -6
- package/dist/{docker-VTR2GK5Y.js → docker-EG3QSE6I.js} +11 -8
- package/dist/{docker-edge-executor-22IBXNYJ.js → docker-edge-executor-E5TOTLMR.js} +8 -6
- package/dist/edge-import-YJVJUGYC.js +56 -0
- package/dist/{edge-preflight-GBNCPNVU.js → edge-preflight-QUW4BM7N.js} +15 -12
- package/dist/{ensure-container-edge-KPI2CVXJ.js → ensure-container-edge-FOEFQ3PI.js} +15 -10
- package/dist/{executor-7LXXZPBR.js → executor-NTWDJ3DB.js} +8 -6
- package/dist/host-channel-preflight-OMCZMMKR.js +54 -0
- package/dist/index.js +198 -76
- package/dist/{local-executor-WOFZD72X.js → local-executor-OQ2GRN7K.js} +3 -2
- package/dist/{nginx-KUFMLMAC.js → nginx-4U4C2XUS.js} +11 -8
- package/dist/{noop-NJ72F46T.js → noop-CLLJHCPL.js} +1 -1
- package/dist/{openresty-lua-ATTH35BE.js → openresty-lua-LLWT5XQW.js} +8 -5
- package/dist/{privilege-I4W3ZNZK.js → privilege-MNDVMDST.js} +6 -5
- package/dist/server/index.js +40049 -30378
- package/dist/server/migrations/0109_service_build_args.sql +9 -0
- package/dist/server/migrations/0110_service_deployment_host_ports.sql +18 -0
- package/dist/server/migrations/0111_host_port_claim.sql +381 -0
- package/dist/server/migrations/0112_backup_run_execution_lease.sql +12 -0
- package/dist/server/migrations/0113_docker_migration_execution_lease.sql +11 -0
- package/dist/server/migrations/0114_build_session_execution_lease_index.sql +6 -0
- package/dist/server/migrations/0115_orphan_target_key.sql +3 -0
- package/dist/server/migrations/0116_domain_ssl_challenge.sql +2 -0
- package/dist/server/migrations/meta/_journal.json +56 -0
- package/dist/setup-IFIMPQZJ.js +25 -0
- package/dist/{ssh-client-JBMA7SN4.js → ssh-client-2XWZCMGI.js} +3 -2
- package/dist/up-HQS7IR7H.js +61 -0
- package/package.json +1 -1
- package/dist/bare-OXJVW3AK.js +0 -25
- package/dist/chunk-LH2PLRQO.js +0 -434
- package/dist/chunk-SM26HDS2.js +0 -185
- package/dist/cloud-75VIT4KV.js +0 -37
- package/dist/edge-import-WJUB4M7F.js +0 -52
- package/dist/host-channel-preflight-KE6OONYK.js +0 -50
- package/dist/setup-XGLGUUFW.js +0 -22
- package/dist/up-K4BV7466.js +0 -57
|
@@ -28,7 +28,7 @@ import {
|
|
|
28
28
|
resolvePorts,
|
|
29
29
|
sourceBuildDir,
|
|
30
30
|
verifyHostChannel
|
|
31
|
-
} from "./chunk-
|
|
31
|
+
} from "./chunk-5I3COAFH.js";
|
|
32
32
|
import {
|
|
33
33
|
bootstrapAdmin,
|
|
34
34
|
importMigratedSites,
|
|
@@ -36,18 +36,18 @@ import {
|
|
|
36
36
|
internalPost,
|
|
37
37
|
mintBareInternalToken,
|
|
38
38
|
waitHealthy
|
|
39
|
-
} from "./chunk-
|
|
39
|
+
} from "./chunk-7YBWXEM6.js";
|
|
40
40
|
import {
|
|
41
41
|
startUnitHint,
|
|
42
42
|
thisHost
|
|
43
|
-
} from "./chunk-
|
|
43
|
+
} from "./chunk-5UXHT6ED.js";
|
|
44
44
|
import {
|
|
45
45
|
completeHostEdge,
|
|
46
46
|
markStoppedProxyImported,
|
|
47
47
|
planAndApplyHostEdge,
|
|
48
48
|
previewHostEdge,
|
|
49
49
|
rollbackHostEdge
|
|
50
|
-
} from "./chunk-
|
|
50
|
+
} from "./chunk-NUT6HKK6.js";
|
|
51
51
|
import {
|
|
52
52
|
AUTH_SECRET_FILE,
|
|
53
53
|
DATA_DIR,
|
|
@@ -58,15 +58,17 @@ import {
|
|
|
58
58
|
} from "./chunk-ELGRMZOW.js";
|
|
59
59
|
import {
|
|
60
60
|
LocalExecutor
|
|
61
|
-
} from "./chunk-
|
|
61
|
+
} from "./chunk-T6DVUX4F.js";
|
|
62
62
|
import {
|
|
63
|
-
EDGE_CONTAINER_NAME,
|
|
64
63
|
edgeCrashReason,
|
|
65
64
|
edgeIsBroken
|
|
66
|
-
} from "./chunk-
|
|
65
|
+
} from "./chunk-X53JXRS4.js";
|
|
66
|
+
import {
|
|
67
|
+
EDGE_CONTAINER_NAME
|
|
68
|
+
} from "./chunk-D6HU4VUZ.js";
|
|
67
69
|
import {
|
|
68
70
|
isValidEmail
|
|
69
|
-
} from "./chunk-
|
|
71
|
+
} from "./chunk-WEZFTL4V.js";
|
|
70
72
|
|
|
71
73
|
// src/commands/up.ts
|
|
72
74
|
import { Command } from "commander";
|
|
@@ -609,6 +611,7 @@ async function planUp(opts) {
|
|
|
609
611
|
...opts.hostControl === false ? { noHostControl: true } : {},
|
|
610
612
|
...opts.hostSshHost ? { hostSshHost: opts.hostSshHost } : {},
|
|
611
613
|
...opts.hostSshPort ? { hostSshPort: opts.hostSshPort } : {},
|
|
614
|
+
...opts.hostSshUser ? { hostSshUser: opts.hostSshUser } : {},
|
|
612
615
|
...opts.resetSecrets ? { resetSecrets: true } : {},
|
|
613
616
|
...opts.mail ? { mail: true } : {}
|
|
614
617
|
});
|
|
@@ -630,6 +633,13 @@ async function planUp(opts) {
|
|
|
630
633
|
] : [],
|
|
631
634
|
...stack.hostChannel.rootUnavailable ? [
|
|
632
635
|
"\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)"
|
|
636
|
+
] : [],
|
|
637
|
+
// A plan that named one account and then settled on another would read as the
|
|
638
|
+
// plan being wrong, and this run genuinely cannot know which: only a dial can
|
|
639
|
+
// establish whether sshd accepts the first account, and dialing means writing
|
|
640
|
+
// the key a dry run exists not to write (#527, previewHostChannel).
|
|
641
|
+
...stack.hostChannel.fallbacks?.length ? [
|
|
642
|
+
`if this host's sshd refuses that account, falls back to ${stack.hostChannel.fallbacks.join(", ")} (pin one with --host-ssh-user)`
|
|
633
643
|
] : []
|
|
634
644
|
] : [],
|
|
635
645
|
...stack.mountDirs.map((d) => `${d}/ (edge routing state + certs)`),
|
|
@@ -1071,6 +1081,9 @@ var upCommand = new Command("up").description("Start Openship as a persistent se
|
|
|
1071
1081
|
).option(
|
|
1072
1082
|
"--host-ssh-port <port>",
|
|
1073
1083
|
"Compose mode: port the host's sshd listens on for host operations (default 22). Preserved across re-runs."
|
|
1084
|
+
).option(
|
|
1085
|
+
"--host-ssh-user <name>",
|
|
1086
|
+
"Compose mode: the host account the api container logs in as for host operations. Normally left unset \u2014 provisioning authorizes a key and DIALS to find an account this host's sshd actually accepts (root when invoked as root, otherwise the invoking user, falling back to $SUDO_USER when a root login is refused). Set this when neither is the account you want, or when sshd's rules mean neither works: the named account is used as-is and never falls back, so it must be able to log in over SSH and should have passwordless sudo (host operations elevate per command). Preserved across re-runs."
|
|
1074
1087
|
).option(
|
|
1075
1088
|
"--open-host-firewall",
|
|
1076
1089
|
"Compose mode: if the api container can't reach this machine's SSH port, add the host firewall rule that allows it (scoped to the container subnet, ufw/firewalld only). Without this the blocked channel is reported and left alone. Not implied by --yes."
|
|
@@ -1213,6 +1226,7 @@ async function runCompose(opts) {
|
|
|
1213
1226
|
// so the containers would keep an environment the file no longer shows.
|
|
1214
1227
|
hostSshHost: opts.hostSshHost,
|
|
1215
1228
|
hostSshPort: opts.hostSshPort,
|
|
1229
|
+
hostSshUser: opts.hostSshUser,
|
|
1216
1230
|
resetSecrets: opts.resetSecrets,
|
|
1217
1231
|
mail: opts.mail
|
|
1218
1232
|
});
|
|
@@ -1267,6 +1281,7 @@ async function runCompose(opts) {
|
|
|
1267
1281
|
// configured with rather than resetting it to host.docker.internal:22.
|
|
1268
1282
|
hostSshHost: opts.hostSshHost,
|
|
1269
1283
|
hostSshPort: opts.hostSshPort,
|
|
1284
|
+
hostSshUser: opts.hostSshUser,
|
|
1270
1285
|
resetSecrets: opts.resetSecrets,
|
|
1271
1286
|
// Absent on a re-run keeps the install's mode (resolveEnvConfig), so this
|
|
1272
1287
|
// never flips a mail box back to the platform shell.
|
|
@@ -1549,7 +1564,7 @@ async function runForeground(opts, source) {
|
|
|
1549
1564
|
const uiSpinner = ora("Preparing the dashboard\u2026").start();
|
|
1550
1565
|
try {
|
|
1551
1566
|
const bundle = await ensureDashboard({
|
|
1552
|
-
tag: source ? "local" : opts.uiVersion || `v${"0.6.
|
|
1567
|
+
tag: source ? "local" : opts.uiVersion || `v${"0.6.8"}`,
|
|
1553
1568
|
onProgress: (received, total) => {
|
|
1554
1569
|
if (total) {
|
|
1555
1570
|
uiSpinner.text = `Downloading dashboard\u2026 ${Math.round(received / total * 100)}%`;
|
|
@@ -7,9 +7,9 @@ const require = __ospCreateRequire(import.meta.url);
|
|
|
7
7
|
var NoopInfraProvider = class {
|
|
8
8
|
async registerRoute(_route) {
|
|
9
9
|
}
|
|
10
|
-
async removeRoute(_domain) {
|
|
10
|
+
async removeRoute(_domain, _opts) {
|
|
11
11
|
}
|
|
12
|
-
async provisionCert(domain) {
|
|
12
|
+
async provisionCert(domain, _opts) {
|
|
13
13
|
return { domain, expiresAt: "", issuer: "none", verified: false };
|
|
14
14
|
}
|
|
15
15
|
async renewCert(domain) {
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
#!/usr/bin/env sh
|
|
2
|
+
":" //# ; exec "$(command -v node || command -v bun)" "$0" "$@"
|
|
3
|
+
import { createRequire as __ospCreateRequire } from "node:module";
|
|
4
|
+
const require = __ospCreateRequire(import.meta.url);
|
|
5
|
+
import {
|
|
6
|
+
sq
|
|
7
|
+
} from "./chunk-P7RCYELG.js";
|
|
8
|
+
|
|
9
|
+
// ../../packages/adapters/src/runtime/managed-artifact.ts
|
|
10
|
+
var MANAGED_ARTIFACT_BASE = "/opt/openship";
|
|
11
|
+
var PROTECTED_ROOTS = /* @__PURE__ */ new Set([
|
|
12
|
+
MANAGED_ARTIFACT_BASE,
|
|
13
|
+
// BareRuntime's own layout (workDir = MANAGED_ARTIFACT_BASE): releases/<depId>,
|
|
14
|
+
// .builds/<sessionId>. The parents hold every project's releases at once.
|
|
15
|
+
`${MANAGED_ARTIFACT_BASE}/releases`,
|
|
16
|
+
`${MANAGED_ARTIFACT_BASE}/.builds`,
|
|
17
|
+
// The static tree, whose parent is ALSO the one directory bind-mounted into the
|
|
18
|
+
// edge container — removing it stops the box serving every static site it hosts.
|
|
19
|
+
`${MANAGED_ARTIFACT_BASE}/static`,
|
|
20
|
+
`${MANAGED_ARTIFACT_BASE}/static/releases`,
|
|
21
|
+
`${MANAGED_ARTIFACT_BASE}/static/.builds`
|
|
22
|
+
]);
|
|
23
|
+
function canonical(ref) {
|
|
24
|
+
const trimmed = ref.trim();
|
|
25
|
+
return trimmed.length > 1 ? trimmed.replace(/\/+$/, "") : trimmed;
|
|
26
|
+
}
|
|
27
|
+
function assertManagedArtifactPath(ref) {
|
|
28
|
+
const target = canonical(ref);
|
|
29
|
+
if (!target.startsWith("/") || target.includes("..")) {
|
|
30
|
+
throw new Error(
|
|
31
|
+
`Refusing to remove "${ref}": an artifact path must be absolute and free of "..".`
|
|
32
|
+
);
|
|
33
|
+
}
|
|
34
|
+
if (PROTECTED_ROOTS.has(target)) {
|
|
35
|
+
throw new Error(
|
|
36
|
+
`Refusing to remove "${ref}": that is a shared parent directory, not one deployment's output.`
|
|
37
|
+
);
|
|
38
|
+
}
|
|
39
|
+
if (target !== MANAGED_ARTIFACT_BASE && !target.startsWith(`${MANAGED_ARTIFACT_BASE}/`)) {
|
|
40
|
+
throw new Error(
|
|
41
|
+
`Refusing to remove "${ref}": outside ${MANAGED_ARTIFACT_BASE}. Openship removes only directories it created (release dirs, static doc-roots, build dirs).`
|
|
42
|
+
);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
async function removeManagedArtifact(executor, ref) {
|
|
46
|
+
assertManagedArtifactPath(ref);
|
|
47
|
+
const target = canonical(ref);
|
|
48
|
+
if (executor) {
|
|
49
|
+
await executor.exec(`rm -rf ${sq(target)}`);
|
|
50
|
+
if (await executor.exists(target)) {
|
|
51
|
+
throw new Error(`Failed to remove ${target}: still present after rm -rf.`);
|
|
52
|
+
}
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
const { rm, access } = await import("fs/promises");
|
|
56
|
+
await rm(target, { recursive: true, force: true });
|
|
57
|
+
const stillThere = await access(target).then(
|
|
58
|
+
() => true,
|
|
59
|
+
() => false
|
|
60
|
+
);
|
|
61
|
+
if (stillThere) throw new Error(`Failed to remove ${target}: still present after rm.`);
|
|
62
|
+
}
|
|
63
|
+
function isArtifactPathRef(ref) {
|
|
64
|
+
return ref.startsWith("/");
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export {
|
|
68
|
+
removeManagedArtifact,
|
|
69
|
+
isArtifactPathRef
|
|
70
|
+
};
|
|
@@ -4,15 +4,15 @@ import { createRequire as __ospCreateRequire } from "node:module";
|
|
|
4
4
|
const require = __ospCreateRequire(import.meta.url);
|
|
5
5
|
import {
|
|
6
6
|
hostRefusal
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-GNOMEMNI.js";
|
|
8
8
|
import {
|
|
9
9
|
elevatedExecutor,
|
|
10
10
|
resolveEnvironment
|
|
11
|
-
} from "./chunk-
|
|
11
|
+
} from "./chunk-NPSRGIHV.js";
|
|
12
12
|
import {
|
|
13
13
|
answered,
|
|
14
14
|
refused
|
|
15
|
-
} from "./chunk-
|
|
15
|
+
} from "./chunk-WEZFTL4V.js";
|
|
16
16
|
|
|
17
17
|
// ../../packages/adapters/src/system/privilege.ts
|
|
18
18
|
function rootChecked(executor, why) {
|
|
@@ -2,15 +2,17 @@
|
|
|
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
|
+
isSshAuthError,
|
|
7
|
+
systemDebug
|
|
8
|
+
} from "./chunk-N5YZMO33.js";
|
|
5
9
|
import {
|
|
6
10
|
HOST_CHANNEL_AUTH_REJECTED,
|
|
7
11
|
HOST_CHANNEL_NOT_PROVISIONED,
|
|
8
12
|
HOST_CHANNEL_ROW_CREDENTIALS_UNUSED,
|
|
9
13
|
hostFirewallRule,
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
systemDebug
|
|
13
|
-
} from "./chunk-UJ6JZM65.js";
|
|
14
|
+
safeErrorMessage
|
|
15
|
+
} from "./chunk-WEZFTL4V.js";
|
|
14
16
|
|
|
15
17
|
// ../../packages/adapters/src/system/ssh-client.ts
|
|
16
18
|
import { Client } from "ssh2";
|
|
@@ -2,103 +2,52 @@
|
|
|
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
|
+
probeTcpDetailed
|
|
7
|
+
} from "./chunk-R4VDPFLS.js";
|
|
5
8
|
import {
|
|
6
9
|
getTarCreateEnv,
|
|
7
10
|
prepareSourceTarArgs
|
|
8
|
-
} from "./chunk-
|
|
11
|
+
} from "./chunk-VAI5VOOI.js";
|
|
9
12
|
import {
|
|
10
13
|
connectSshClient,
|
|
11
14
|
openDockerDialStdioChannel,
|
|
12
15
|
openSftp,
|
|
13
16
|
openSshUnixSocket,
|
|
14
17
|
reconcileKnownHosts
|
|
15
|
-
} from "./chunk-
|
|
18
|
+
} from "./chunk-TAZZZKYO.js";
|
|
16
19
|
import {
|
|
17
20
|
LocalExecutor
|
|
18
|
-
} from "./chunk-
|
|
21
|
+
} from "./chunk-T6DVUX4F.js";
|
|
19
22
|
import {
|
|
20
23
|
emitBufferedLines,
|
|
21
24
|
flushBufferedLines,
|
|
22
25
|
hasLocalCommand,
|
|
23
26
|
logEntry,
|
|
24
27
|
sq
|
|
25
|
-
} from "./chunk-
|
|
28
|
+
} from "./chunk-P7RCYELG.js";
|
|
29
|
+
import {
|
|
30
|
+
HostChannelUnavailableError,
|
|
31
|
+
SshDisconnectedError,
|
|
32
|
+
commandForError,
|
|
33
|
+
isSshAuthError
|
|
34
|
+
} from "./chunk-N5YZMO33.js";
|
|
26
35
|
import {
|
|
27
36
|
HOST_CHANNEL_AUTH_REJECTED,
|
|
28
37
|
HOST_CHANNEL_NOT_PROVISIONED,
|
|
29
38
|
HOST_CHANNEL_UNPROVISIONED,
|
|
30
|
-
HostChannelUnavailableError,
|
|
31
|
-
SshDisconnectedError,
|
|
32
39
|
TRANSFER_EXCLUDES,
|
|
33
|
-
commandForError,
|
|
34
40
|
explainHostChannelCause,
|
|
35
41
|
formatBytes,
|
|
36
42
|
hostChannelAccount,
|
|
37
43
|
hostFirewallRule,
|
|
38
|
-
isSshAuthError,
|
|
39
44
|
safeErrorMessage
|
|
40
|
-
} from "./chunk-
|
|
45
|
+
} from "./chunk-WEZFTL4V.js";
|
|
41
46
|
|
|
42
47
|
// ../../packages/adapters/src/system/executor.ts
|
|
43
48
|
import { existsSync, readFileSync } from "fs";
|
|
44
49
|
import { networkInterfaces } from "os";
|
|
45
50
|
|
|
46
|
-
// ../../packages/adapters/src/system/reachability.ts
|
|
47
|
-
import { connect } from "net";
|
|
48
|
-
import { request } from "http";
|
|
49
|
-
function classifyDialError(code) {
|
|
50
|
-
switch (code) {
|
|
51
|
-
case "ECONNREFUSED":
|
|
52
|
-
case "ECONNRESET":
|
|
53
|
-
return "refused";
|
|
54
|
-
case "ENOTFOUND":
|
|
55
|
-
case "EAI_AGAIN":
|
|
56
|
-
return "unresolved";
|
|
57
|
-
case "EHOSTUNREACH":
|
|
58
|
-
case "ENETUNREACH":
|
|
59
|
-
case "ENETDOWN":
|
|
60
|
-
return "no_route";
|
|
61
|
-
// The kernel's own connect timeout, i.e. our `timeout` reported for us.
|
|
62
|
-
case "ETIMEDOUT":
|
|
63
|
-
return "timeout";
|
|
64
|
-
default:
|
|
65
|
-
return "error";
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
function probeTcpDetailed(host, port, timeoutMs = 2500) {
|
|
69
|
-
return new Promise((resolve) => {
|
|
70
|
-
let settled = false;
|
|
71
|
-
const done = (result, socket2) => {
|
|
72
|
-
if (settled) return;
|
|
73
|
-
settled = true;
|
|
74
|
-
try {
|
|
75
|
-
socket2?.destroy();
|
|
76
|
-
} catch {
|
|
77
|
-
}
|
|
78
|
-
resolve(result);
|
|
79
|
-
};
|
|
80
|
-
const socket = connect({ host, port });
|
|
81
|
-
socket.setTimeout(timeoutMs);
|
|
82
|
-
socket.once("connect", () => done({ ok: true }, socket));
|
|
83
|
-
socket.once(
|
|
84
|
-
"timeout",
|
|
85
|
-
() => done({ ok: false, reason: "timeout", message: `no response within ${timeoutMs}ms` }, socket)
|
|
86
|
-
);
|
|
87
|
-
socket.once("error", (err) => {
|
|
88
|
-
const errno = err;
|
|
89
|
-
done(
|
|
90
|
-
{
|
|
91
|
-
ok: false,
|
|
92
|
-
reason: classifyDialError(errno.code),
|
|
93
|
-
...errno.code ? { code: errno.code } : {},
|
|
94
|
-
...errno.message ? { message: errno.message } : {}
|
|
95
|
-
},
|
|
96
|
-
socket
|
|
97
|
-
);
|
|
98
|
-
});
|
|
99
|
-
});
|
|
100
|
-
}
|
|
101
|
-
|
|
102
51
|
// ../../packages/adapters/src/system/ssh-executor.ts
|
|
103
52
|
import { createReadStream } from "fs";
|
|
104
53
|
import { mkdtemp as mkdtemp2, rm as fsRm2, stat } from "fs/promises";
|
|
@@ -113,7 +62,10 @@ import { tmpdir } from "os";
|
|
|
113
62
|
import { join } from "path";
|
|
114
63
|
function packLocalArchive(tarArgs, outFile) {
|
|
115
64
|
return new Promise((resolve, reject) => {
|
|
116
|
-
const tar = spawn("tar", tarArgs, {
|
|
65
|
+
const tar = spawn("tar", tarArgs, {
|
|
66
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
67
|
+
env: getTarCreateEnv()
|
|
68
|
+
});
|
|
117
69
|
const out = createWriteStream(outFile);
|
|
118
70
|
let stderr = "";
|
|
119
71
|
let tarCode = null;
|
|
@@ -122,7 +74,12 @@ function packLocalArchive(tarArgs, outFile) {
|
|
|
122
74
|
const settle = () => {
|
|
123
75
|
if (!tarClosed || !outClosed) return;
|
|
124
76
|
if (tarCode === 0) resolve();
|
|
125
|
-
else
|
|
77
|
+
else
|
|
78
|
+
reject(
|
|
79
|
+
new Error(
|
|
80
|
+
`tar failed (exit ${tarCode})${stderr.trim() ? `: ${stderr.trim().slice(-300)}` : ""}`
|
|
81
|
+
)
|
|
82
|
+
);
|
|
126
83
|
};
|
|
127
84
|
tar.stderr.on("data", (d) => {
|
|
128
85
|
stderr += d.toString();
|
|
@@ -153,9 +110,17 @@ async function extractRemoteArchive(exec, remoteArchive, remotePath, expectedByt
|
|
|
153
110
|
);
|
|
154
111
|
onLog?.(logEntry(`Transferred ${formatBytes(expectedBytes)} and extracted on the server.`));
|
|
155
112
|
}
|
|
113
|
+
function formatPrivateKeyForOpenSsh(privateKey) {
|
|
114
|
+
const normalized = privateKey.replace(/^\uFEFF/, "").replace(/\r\n?/g, "\n");
|
|
115
|
+
return normalized.endsWith("\n") ? normalized : `${normalized}
|
|
116
|
+
`;
|
|
117
|
+
}
|
|
156
118
|
async function canUseRemoteRsync(deps) {
|
|
157
119
|
if (deps.config.privateKey && deps.config.privateKeyPassphrase && !deps.config.sshAgent) {
|
|
158
|
-
return {
|
|
120
|
+
return {
|
|
121
|
+
ok: false,
|
|
122
|
+
reason: "encrypted SSH keys without an agent are not supported by non-interactive rsync"
|
|
123
|
+
};
|
|
159
124
|
}
|
|
160
125
|
const [localRsync, localSsh, localSshpass, remoteRsync] = await Promise.all([
|
|
161
126
|
hasLocalCommand("rsync"),
|
|
@@ -165,7 +130,8 @@ async function canUseRemoteRsync(deps) {
|
|
|
165
130
|
]);
|
|
166
131
|
if (!localRsync) return { ok: false, reason: "local rsync is not installed" };
|
|
167
132
|
if (!localSsh) return { ok: false, reason: "local ssh is not installed" };
|
|
168
|
-
if (!localSshpass)
|
|
133
|
+
if (!localSshpass)
|
|
134
|
+
return { ok: false, reason: "local sshpass is not installed for password-based rsync" };
|
|
169
135
|
if (!remoteRsync) return { ok: false, reason: "remote rsync is not installed" };
|
|
170
136
|
return { ok: true };
|
|
171
137
|
}
|
|
@@ -208,7 +174,7 @@ async function withTemporaryPrivateKey(config, fn) {
|
|
|
208
174
|
const tempDir = await mkdtemp(join(tmpdir(), "openship-rsync-key-"));
|
|
209
175
|
const keyPath = join(tempDir, "id_rsa");
|
|
210
176
|
try {
|
|
211
|
-
await fsWriteFile(keyPath, config.privateKey);
|
|
177
|
+
await fsWriteFile(keyPath, formatPrivateKeyForOpenSsh(config.privateKey), { mode: 384 });
|
|
212
178
|
await chmod(keyPath, 384);
|
|
213
179
|
return await fn(keyPath);
|
|
214
180
|
} finally {
|
|
@@ -254,7 +220,12 @@ async function uploadFileWithRsync(localFile, remoteFile, deps, onLog, opts) {
|
|
|
254
220
|
let lastCode = 1;
|
|
255
221
|
for (let attempt = 1; attempt <= retries; attempt++) {
|
|
256
222
|
if (attempt > 1) {
|
|
257
|
-
onLog?.(
|
|
223
|
+
onLog?.(
|
|
224
|
+
logEntry(
|
|
225
|
+
`Connection dropped \u2014 resuming upload (attempt ${attempt}/${retries})...`,
|
|
226
|
+
"warn"
|
|
227
|
+
)
|
|
228
|
+
);
|
|
258
229
|
}
|
|
259
230
|
const args = [
|
|
260
231
|
"-a",
|
|
@@ -1720,7 +1691,7 @@ async function verifyHostChannelAuth(config) {
|
|
|
1720
1691
|
let hint = null;
|
|
1721
1692
|
let forwarding = "unknown";
|
|
1722
1693
|
try {
|
|
1723
|
-
const { connectSshClient: connectSshClient2 } = await import("./ssh-client-
|
|
1694
|
+
const { connectSshClient: connectSshClient2 } = await import("./ssh-client-2XWZCMGI.js");
|
|
1724
1695
|
const client = await connectSshClient2({
|
|
1725
1696
|
host: config.host,
|
|
1726
1697
|
port: config.port,
|
|
@@ -4,7 +4,7 @@ import { createRequire as __ospCreateRequire } from "node:module";
|
|
|
4
4
|
const require = __ospCreateRequire(import.meta.url);
|
|
5
5
|
import {
|
|
6
6
|
PACKAGE_ROOT_ONLY_EXCLUDES
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-WEZFTL4V.js";
|
|
8
8
|
|
|
9
9
|
// ../../packages/adapters/src/archive.ts
|
|
10
10
|
import { execFile } from "child_process";
|