openship 0.6.1 → 0.6.5

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 (59) hide show
  1. package/README.md +7 -2
  2. package/dist/{bare-IWS2QHJ3.js → bare-XQIPDO4R.js} +10 -9
  3. package/dist/{chunk-RRJJGVKE.js → chunk-3ZAGC5NT.js} +64 -17
  4. package/dist/{chunk-6ODUBKUB.js → chunk-5OUWIPOB.js} +69 -7
  5. package/dist/chunk-6HOA5JT7.js +124 -0
  6. package/dist/{chunk-TWJCLD4W.js → chunk-6JELW75A.js} +4 -4
  7. package/dist/{chunk-IOT5ZX23.js → chunk-7334NSJL.js} +399 -369
  8. package/dist/chunk-7S4USQYF.js +1753 -0
  9. package/dist/{chunk-3LDWWY7U.js → chunk-A7EWM2KA.js} +41 -7
  10. package/dist/chunk-ELGRMZOW.js +25 -0
  11. package/dist/chunk-EZPPTWJQ.js +292 -0
  12. package/dist/{chunk-IXM5245F.js → chunk-GRTXMUHA.js} +553 -538
  13. package/dist/chunk-HPXP6LXZ.js +428 -0
  14. package/dist/{chunk-OTLSJYAC.js → chunk-LEYSL46B.js} +2135 -85
  15. package/dist/{chunk-EMXI4UCU.js → chunk-LFCSC4IU.js} +29 -10
  16. package/dist/{chunk-JIMUYEKN.js → chunk-MSZUCNPV.js} +1 -1
  17. package/dist/{chunk-YIPMDGG6.js → chunk-PZRRL5PC.js} +68 -23
  18. package/dist/{chunk-2NZSUXOB.js → chunk-QLX7NBEA.js} +1208 -599
  19. package/dist/chunk-RFWJE7ZN.js +68 -0
  20. package/dist/{chunk-VDDMADOF.js → chunk-TM2IQPYU.js} +16 -2
  21. package/dist/{chunk-3FYIXVX5.js → chunk-VDESJSG6.js} +451 -106
  22. package/dist/{chunk-UC7YIEHT.js → chunk-VHMOQL5Y.js} +254 -45
  23. package/dist/{chunk-EW6KB2J7.js → chunk-VLQ3UIVO.js} +393 -53
  24. package/dist/{chunk-VZ2ZHPS2.js → chunk-WE7BXW6L.js} +3 -9
  25. package/dist/chunk-WFRXYWK2.js +444 -0
  26. package/dist/chunk-XRNRT37W.js +316 -0
  27. package/dist/{chunk-3NZICQHF.js → chunk-XVOBL7BC.js} +31 -8
  28. package/dist/cloud-3DODOWTR.js +36 -0
  29. package/dist/{detect-ALF4GXRX.js → detect-P4CIQBRH.js} +3 -3
  30. package/dist/{docker-QECQJ5P4.js → docker-3K2NEBD6.js} +15 -8
  31. package/dist/{docker-edge-executor-62N64MGS.js → docker-edge-executor-YZZ6P7IC.js} +5 -5
  32. package/dist/{edge-import-SAYTRJLO.js → edge-import-MCJNVTZ2.js} +26 -23
  33. package/dist/edge-preflight-ES6FH4JK.js +42 -0
  34. package/dist/{ensure-container-edge-CFSCPGZK.js → ensure-container-edge-YPITNH45.js} +9 -8
  35. package/dist/{executor-PT6UEJ5M.js → executor-DZGX55K2.js} +12 -5
  36. package/dist/host-channel-preflight-MUXE3KZU.js +49 -0
  37. package/dist/index.js +4089 -4538
  38. package/dist/{nginx-YOEEFBTE.js → nginx-NSAHWGJV.js} +11 -5
  39. package/dist/node-bootstrap.js +241 -0
  40. package/dist/node-entry.js +52 -1
  41. package/dist/{openresty-lua-WGBO2M3Z.js → openresty-lua-DR4D3EZ6.js} +6 -2
  42. package/dist/privilege-YTVI4SKF.js +19 -0
  43. package/dist/server/engine/samples/fail2ban/jail.d/sshd.local +4 -1
  44. package/dist/server/index.js +21391 -13110
  45. package/dist/server/migrations/0098_instance_product_mode.sql +12 -0
  46. package/dist/server/migrations/0099_mail_webmail_project.sql +24 -0
  47. package/dist/server/migrations/0100_server_ssh_private_key.sql +11 -0
  48. package/dist/server/migrations/0101_mail_resume_step.sql +12 -0
  49. package/dist/server/migrations/0102_project_workload_axes.sql +19 -0
  50. package/dist/server/migrations/0103_dns_credential.sql +41 -0
  51. package/dist/server/migrations/0104_instance_host_control.sql +15 -0
  52. package/dist/server/migrations/meta/_journal.json +49 -0
  53. package/dist/{setup-M2ZDGH3O.js → setup-WUUZURHS.js} +10 -9
  54. package/dist/up-XEO3SR6Z.js +58 -0
  55. package/package.json +4 -2
  56. package/dist/chunk-IW2UKBVF.js +0 -144
  57. package/dist/chunk-ZCFVHA25.js +0 -89
  58. package/dist/cloud-M2YLWEXO.js +0 -31
  59. /package/dist/{dist-es-JGBPABJI.js → dist-es-ZP5QDSJ3.js} +0 -0
@@ -5,14 +5,12 @@ const require = __ospCreateRequire(import.meta.url);
5
5
  import {
6
6
  connectSshClient,
7
7
  getTarCreateEnv,
8
+ openDockerDialStdioChannel,
8
9
  openSftp,
9
10
  openSshUnixSocket,
10
11
  prepareSourceTarArgs,
11
12
  reconcileKnownHosts
12
- } from "./chunk-6ODUBKUB.js";
13
- import {
14
- SshDisconnectedError
15
- } from "./chunk-ZCFVHA25.js";
13
+ } from "./chunk-5OUWIPOB.js";
16
14
  import {
17
15
  LocalExecutor
18
16
  } from "./chunk-6FH4AXVY.js";
@@ -24,13 +22,77 @@ import {
24
22
  sq
25
23
  } from "./chunk-ZFJXPCQP.js";
26
24
  import {
25
+ HOST_CHANNEL_NOT_PROVISIONED,
26
+ HOST_CHANNEL_UNPROVISIONED,
27
+ HostChannelUnavailableError,
28
+ SshDisconnectedError,
27
29
  TRANSFER_EXCLUDES,
30
+ commandForError,
31
+ explainHostChannelCause,
28
32
  formatBytes,
33
+ hostFirewallRule,
29
34
  safeErrorMessage
30
- } from "./chunk-OTLSJYAC.js";
35
+ } from "./chunk-LEYSL46B.js";
31
36
 
32
37
  // ../../packages/adapters/src/system/executor.ts
33
38
  import { existsSync, readFileSync } from "fs";
39
+ import { networkInterfaces } from "os";
40
+
41
+ // ../../packages/adapters/src/system/reachability.ts
42
+ import { connect } from "net";
43
+ import { request } from "http";
44
+ function classifyDialError(code) {
45
+ switch (code) {
46
+ case "ECONNREFUSED":
47
+ case "ECONNRESET":
48
+ return "refused";
49
+ case "ENOTFOUND":
50
+ case "EAI_AGAIN":
51
+ return "unresolved";
52
+ case "EHOSTUNREACH":
53
+ case "ENETUNREACH":
54
+ case "ENETDOWN":
55
+ return "no_route";
56
+ // The kernel's own connect timeout, i.e. our `timeout` reported for us.
57
+ case "ETIMEDOUT":
58
+ return "timeout";
59
+ default:
60
+ return "error";
61
+ }
62
+ }
63
+ function probeTcpDetailed(host, port, timeoutMs = 2500) {
64
+ return new Promise((resolve) => {
65
+ let settled = false;
66
+ const done = (result, socket2) => {
67
+ if (settled) return;
68
+ settled = true;
69
+ try {
70
+ socket2?.destroy();
71
+ } catch {
72
+ }
73
+ resolve(result);
74
+ };
75
+ const socket = connect({ host, port });
76
+ socket.setTimeout(timeoutMs);
77
+ socket.once("connect", () => done({ ok: true }, socket));
78
+ socket.once(
79
+ "timeout",
80
+ () => done({ ok: false, reason: "timeout", message: `no response within ${timeoutMs}ms` }, socket)
81
+ );
82
+ socket.once("error", (err) => {
83
+ const errno = err;
84
+ done(
85
+ {
86
+ ok: false,
87
+ reason: classifyDialError(errno.code),
88
+ ...errno.code ? { code: errno.code } : {},
89
+ ...errno.message ? { message: errno.message } : {}
90
+ },
91
+ socket
92
+ );
93
+ });
94
+ });
95
+ }
34
96
 
35
97
  // ../../packages/adapters/src/system/ssh-executor.ts
36
98
  import { createReadStream } from "fs";
@@ -240,7 +302,7 @@ var SshExecutor = class _SshExecutor {
240
302
  async connect() {
241
303
  if (this.client) return this.client;
242
304
  if (this.connecting) return this.connecting;
243
- this.connecting = (async () => {
305
+ const attempt = (async () => {
244
306
  const client = await connectSshClient(this.config);
245
307
  const onTransportDown = (cause) => {
246
308
  if (this.client !== client) return;
@@ -252,10 +314,14 @@ var SshExecutor = class _SshExecutor {
252
314
  client.on("end", () => onTransportDown());
253
315
  client.on("error", (err) => onTransportDown(err));
254
316
  this.client = client;
255
- this.connecting = null;
256
317
  return client;
257
318
  })();
258
- return this.connecting;
319
+ this.connecting = attempt;
320
+ const clearMemo = () => {
321
+ if (this.connecting === attempt) this.connecting = null;
322
+ };
323
+ attempt.then(clearMemo, clearMemo);
324
+ return attempt;
259
325
  }
260
326
  /**
261
327
  * Subscribe to transport-level disconnects. Returns an unsubscribe fn.
@@ -402,7 +468,9 @@ var SshExecutor = class _SshExecutor {
402
468
  };
403
469
  this.inflight.add(abort);
404
470
  timer = setTimeout(
405
- () => finish(() => reject(new Error(`Command timed out after ${timeout}ms: ${command}`))),
471
+ () => finish(
472
+ () => reject(new Error(`Command timed out after ${timeout}ms: ${commandForError(command)}`))
473
+ ),
406
474
  timeout
407
475
  );
408
476
  client.exec(_SshExecutor.ENV_PREFIX + command, (err, stream) => {
@@ -706,24 +774,23 @@ var SshExecutor = class _SshExecutor {
706
774
  * Carry the Docker Engine API over a `docker system dial-stdio` exec channel
707
775
  * on the pooled connection — the streamlocal-free transport used when the
708
776
  * SSH server (or the Bun-compiled desktop runtime) can't do socket
709
- * forwarding. Uses the SAME ENV_PREFIX as streamExec so `docker` resolves on
710
- * PATH exactly as it does for the remote build (which is proven to work).
777
+ * forwarding.
778
+ *
779
+ * Deliberately WITHOUT `ENV_PREFIX`: that prefix only exports apt's
780
+ * non-interactive variables (it sets no PATH, despite what the comment here
781
+ * used to claim), and the bridge's own ephemeral dial-stdio client opens the
782
+ * bare command. Two dial-stdio channels that differ by a shell prefix are two
783
+ * transports, and the bridge picks one by probing the other.
784
+ *
785
+ * Failure diagnostics (daemon down, socket permission, `docker: command not
786
+ * found`, an sshd ForceCommand banner) ride ON the returned channel — see
787
+ * `attachDialStdioDiagnostics`. They used to go to `console.warn` while the
788
+ * caller got a bare socket reset, which is how a host with no running dockerd
789
+ * reported itself as `socket hang up`.
711
790
  */
712
791
  async openDockerDialStdio() {
713
792
  const client = await this.connect();
714
- return new Promise((resolve, reject) => {
715
- client.exec(_SshExecutor.ENV_PREFIX + "docker system dial-stdio", (err, stream) => {
716
- if (err) return reject(err);
717
- stream.stderr?.on("data", (d) => {
718
- const text = d.toString().trim();
719
- if (text) console.warn(`[docker-ssh] dial-stdio stderr: ${text}`);
720
- });
721
- stream.on("exit", (code) => {
722
- if (code) console.warn(`[docker-ssh] dial-stdio exited early (code=${code})`);
723
- });
724
- resolve(stream);
725
- });
726
- });
793
+ return openDockerDialStdioChannel(client);
727
794
  }
728
795
  async forwardPort(remoteHost, remotePort) {
729
796
  const client = await this.connect();
@@ -1200,7 +1267,7 @@ var SystemSshExecutor = class _SystemSshExecutor {
1200
1267
  const timeout = opts?.timeout ?? 3e4;
1201
1268
  const res = await this.runSsh(_SystemSshExecutor.ENV_PREFIX + command, { timeout });
1202
1269
  if (res.timedOut) {
1203
- throw new Error(`Command timed out after ${timeout}ms: ${command}`);
1270
+ throw new Error(`Command timed out after ${timeout}ms: ${commandForError(command)}`);
1204
1271
  }
1205
1272
  if (res.code !== 0) {
1206
1273
  if (res.code === 255) throw describeSshFailure(res.stderr, `Exit code ${res.code}`);
@@ -1208,14 +1275,22 @@ var SystemSshExecutor = class _SystemSshExecutor {
1208
1275
  }
1209
1276
  return res.stdout.trim();
1210
1277
  }
1211
- async streamExec(command, onLog) {
1278
+ async streamExec(command, onLog, opts) {
1212
1279
  await this.ensureMaster();
1280
+ const signal = opts?.signal;
1281
+ if (signal?.aborted) return { code: 0, output: "" };
1213
1282
  return new Promise((resolve) => {
1214
1283
  const child = spawn2(
1215
1284
  "ssh",
1216
1285
  [...this.baseArgs(), sshTarget(this.config), _SystemSshExecutor.ENV_PREFIX + command],
1217
1286
  { env: sshChildEnv(this.config), stdio: ["ignore", "pipe", "pipe"] }
1218
1287
  );
1288
+ let aborted = false;
1289
+ const onAbort = () => {
1290
+ aborted = true;
1291
+ child.kill("SIGKILL");
1292
+ };
1293
+ signal?.addEventListener("abort", onAbort, { once: true });
1219
1294
  const chunks = [];
1220
1295
  const onChunk = (chunk, level) => {
1221
1296
  const text = chunk.toString();
@@ -1226,11 +1301,13 @@ var SystemSshExecutor = class _SystemSshExecutor {
1226
1301
  child.stdout.on("data", (chunk) => onChunk(chunk, "info"));
1227
1302
  child.stderr.on("data", (chunk) => onChunk(chunk, "warn"));
1228
1303
  child.on("error", (err) => {
1304
+ signal?.removeEventListener("abort", onAbort);
1229
1305
  onLog(logEntry(`Process error: ${err.message}`, "error"));
1230
1306
  resolve({ code: 1, output: err.message });
1231
1307
  });
1232
1308
  child.on("close", (code) => {
1233
- const c = code ?? 1;
1309
+ signal?.removeEventListener("abort", onAbort);
1310
+ const c = aborted ? 0 : code ?? 1;
1234
1311
  void this.maybeSignalDisconnect(c).catch(() => {
1235
1312
  });
1236
1313
  resolve({ code: c, output: chunks.join("") });
@@ -1524,8 +1601,12 @@ function createExecutor(ssh) {
1524
1601
  }
1525
1602
  return localExecutor;
1526
1603
  }
1604
+ var hostControlOverride = null;
1605
+ function setHostControlOverride(disabled) {
1606
+ hostControlOverride = disabled;
1607
+ }
1527
1608
  function hostControlDisabled() {
1528
- return process.env.OPENSHIP_HOST_CONTROL?.trim().toLowerCase() === "false";
1609
+ return hostControlOverride ?? process.env.OPENSHIP_HOST_CONTROL?.trim().toLowerCase() === "false";
1529
1610
  }
1530
1611
  function runningInContainer() {
1531
1612
  if (process.env.OPENSHIP_IN_CONTAINER?.trim().toLowerCase() === "true") return true;
@@ -1539,39 +1620,163 @@ function runningInContainer() {
1539
1620
  return false;
1540
1621
  }
1541
1622
  }
1623
+ function hostChannelPort() {
1624
+ const raw = Number(process.env.OPENSHIP_HOST_SSH_PORT || "22");
1625
+ return Number.isInteger(raw) && raw > 0 && raw < 65536 ? raw : 22;
1626
+ }
1627
+ function hostChannelUser() {
1628
+ return process.env.OPENSHIP_HOST_SSH_USER?.trim() || "root";
1629
+ }
1630
+ function readHostChannelKey(keyPath) {
1631
+ let contents;
1632
+ try {
1633
+ contents = readFileSync(keyPath, "utf8");
1634
+ } catch (err) {
1635
+ const detail = err instanceof Error ? err.message : String(err);
1636
+ return { reason: `Cannot read the host SSH key at ${keyPath} (${detail}).` };
1637
+ }
1638
+ if (!contents.trim()) return { reason: `The host SSH key at ${keyPath} is empty.` };
1639
+ return { key: contents };
1640
+ }
1641
+ function containerBridgeCidr() {
1642
+ for (const addrs of Object.values(networkInterfaces())) {
1643
+ for (const addr of addrs ?? []) {
1644
+ if (addr.family !== "IPv4" || addr.internal) continue;
1645
+ const prefix = Number(addr.cidr?.split("/")[1]);
1646
+ if (!Number.isInteger(prefix) || prefix < 8 || prefix > 32) continue;
1647
+ const octets = addr.address.split(".").map(Number);
1648
+ if (octets.length !== 4 || octets.some((o) => !Number.isInteger(o) || o < 0 || o > 255)) continue;
1649
+ const asInt = (octets[0] << 24 | octets[1] << 16 | octets[2] << 8 | octets[3]) >>> 0;
1650
+ const mask = 4294967295 << 32 - prefix >>> 0;
1651
+ const net = (asInt & mask) >>> 0;
1652
+ return `${net >>> 24 & 255}.${net >>> 16 & 255}.${net >>> 8 & 255}.${net & 255}/${prefix}`;
1653
+ }
1654
+ }
1655
+ return null;
1656
+ }
1657
+ function probeDetail(probe) {
1658
+ const { code, message } = probe;
1659
+ if (code && message && !message.includes(code)) return `${code}: ${message}`;
1660
+ return code ?? message;
1661
+ }
1662
+ function explainDialFailure(probe, host, port, target) {
1663
+ const { headline, body, firewallShaped } = explainHostChannelCause(probe.reason, {
1664
+ target,
1665
+ host,
1666
+ detail: probeDetail(probe)
1667
+ });
1668
+ const hint = body ? `${headline}. ${body}` : `${headline}.`;
1669
+ if (!firewallShaped) return { hint };
1670
+ const cidr = containerBridgeCidr();
1671
+ return { hint, rule: hostFirewallRule("unknown", cidr ? [cidr] : [], port) };
1672
+ }
1673
+ async function hostChannelHealth(timeoutMs = 2500) {
1674
+ if (hostControlDisabled()) {
1675
+ return {
1676
+ ok: false,
1677
+ code: "disabled",
1678
+ hint: "Host control is off (OPENSHIP_HOST_CONTROL=false). Re-run `openship up` without --no-host-control."
1679
+ };
1680
+ }
1681
+ const host = process.env.OPENSHIP_HOST_SSH_HOST?.trim();
1682
+ if (!host) {
1683
+ if (runningInContainer()) {
1684
+ return {
1685
+ ok: false,
1686
+ code: "not_configured",
1687
+ hint: `${HOST_CHANNEL_UNPROVISIONED} ${HOST_CHANNEL_NOT_PROVISIONED}`
1688
+ };
1689
+ }
1690
+ return { ok: true, code: "not_applicable" };
1691
+ }
1692
+ const port = hostChannelPort();
1693
+ const target = `${hostChannelUser()}@${host}:${port}`;
1694
+ const keyPath = process.env.OPENSHIP_HOST_SSH_KEY?.trim();
1695
+ if (keyPath) {
1696
+ const { reason } = readHostChannelKey(keyPath);
1697
+ if (reason) {
1698
+ return {
1699
+ ok: false,
1700
+ code: "key_unreadable",
1701
+ host,
1702
+ port,
1703
+ target,
1704
+ hint: `${reason} ${HOST_CHANNEL_NOT_PROVISIONED}`
1705
+ };
1706
+ }
1707
+ }
1708
+ const probe = await probeTcpDetailed(host, port, timeoutMs);
1709
+ if (probe.ok) {
1710
+ return { ok: true, code: "ok", host, port, target };
1711
+ }
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
+ };
1721
+ }
1722
+ function unavailableExecutor(reason, code = "not_configured") {
1723
+ const refuse = async () => {
1724
+ throw new HostChannelUnavailableError(code, reason);
1725
+ };
1726
+ return {
1727
+ exec: refuse,
1728
+ streamExec: refuse,
1729
+ writeFile: refuse,
1730
+ readFile: refuse,
1731
+ exists: refuse,
1732
+ mkdir: refuse,
1733
+ rm: refuse,
1734
+ transferIn: refuse,
1735
+ // Nothing was ever opened, so releasing must not be the one call that throws.
1736
+ dispose: async () => {
1737
+ }
1738
+ };
1739
+ }
1542
1740
  function createHostExecutor() {
1543
1741
  if (hostControlDisabled()) {
1544
- throw new Error(
1742
+ throw new HostChannelUnavailableError(
1743
+ "disabled",
1545
1744
  "Host control is disabled on this instance (OPENSHIP_HOST_CONTROL=false). Re-run `openship up` without --no-host-control to allow host operations."
1546
1745
  );
1547
1746
  }
1548
1747
  const host = process.env.OPENSHIP_HOST_SSH_HOST?.trim();
1549
1748
  if (!host) {
1550
1749
  if (runningInContainer()) {
1551
- throw new Error(
1552
- "This operation targets the HOST machine, but no host channel is configured (OPENSHIP_HOST_SSH_HOST is unset) and Openship is running in a container \u2014 so it would have run inside the container instead, against the wrong filesystem. Re-run `openship up` to provision the host channel."
1750
+ throw new HostChannelUnavailableError(
1751
+ "not_configured",
1752
+ `This operation targets the HOST machine, but no host channel is configured (OPENSHIP_HOST_SSH_HOST is unset) and Openship is running in a container \u2014 so it would have run inside the container instead, against the wrong filesystem. ${HOST_CHANNEL_NOT_PROVISIONED}`
1553
1753
  );
1554
1754
  }
1555
1755
  return localExecutor;
1556
1756
  }
1557
1757
  const keyPath = process.env.OPENSHIP_HOST_SSH_KEY?.trim();
1558
- const portRaw = Number(process.env.OPENSHIP_HOST_SSH_PORT || "22");
1559
- const port = Number.isInteger(portRaw) && portRaw > 0 && portRaw < 65536 ? portRaw : 22;
1560
- let privateKey;
1561
- if (keyPath) {
1562
- try {
1563
- privateKey = readFileSync(keyPath, "utf8");
1564
- } catch (err) {
1565
- throw new Error(
1566
- `Cannot read host SSH key at OPENSHIP_HOST_SSH_KEY=${keyPath}: ${err instanceof Error ? err.message : String(err)}`
1567
- );
1568
- }
1758
+ if (!keyPath) {
1759
+ throw new HostChannelUnavailableError(
1760
+ "not_configured",
1761
+ `A host channel is configured (OPENSHIP_HOST_SSH_HOST) but OPENSHIP_HOST_SSH_KEY is unset, so there is no credential to connect to the host with. ${HOST_CHANNEL_NOT_PROVISIONED}`
1762
+ );
1763
+ }
1764
+ const read = readHostChannelKey(keyPath);
1765
+ if (read.reason) {
1766
+ throw new HostChannelUnavailableError(
1767
+ "not_configured",
1768
+ `${read.reason} ${HOST_CHANNEL_NOT_PROVISIONED}`
1769
+ );
1569
1770
  }
1570
1771
  return new SshExecutor({
1571
1772
  host,
1572
- port,
1573
- username: process.env.OPENSHIP_HOST_SSH_USER?.trim() || "root",
1574
- privateKey
1773
+ port: hostChannelPort(),
1774
+ username: hostChannelUser(),
1775
+ privateKey: read.key,
1776
+ // One hop over the docker bridge: it answers at once or it is being filtered.
1777
+ // 20s of silence per host operation reads as a hang, not as a failure.
1778
+ readyTimeoutMs: 8e3,
1779
+ hostChannel: true
1575
1780
  });
1576
1781
  }
1577
1782
 
@@ -1579,6 +1784,10 @@ export {
1579
1784
  SshExecutor,
1580
1785
  SystemSshExecutor,
1581
1786
  createExecutor,
1787
+ setHostControlOverride,
1582
1788
  hostControlDisabled,
1789
+ containerBridgeCidr,
1790
+ hostChannelHealth,
1791
+ unavailableExecutor,
1583
1792
  createHostExecutor
1584
1793
  };