openship 0.6.1 → 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 (67) hide show
  1. package/README.md +7 -2
  2. package/dist/{bare-IWS2QHJ3.js → bare-OXJVW3AK.js} +11 -9
  3. package/dist/{chunk-VZ2ZHPS2.js → chunk-2YM3NI2E.js} +3 -9
  4. package/dist/chunk-4I2BM2DT.js +68 -0
  5. package/dist/{chunk-2NZSUXOB.js → chunk-5DMWSTOM.js} +310 -1459
  6. package/dist/{chunk-3FYIXVX5.js → chunk-77QDF6UD.js} +581 -122
  7. package/dist/{chunk-IOT5ZX23.js → chunk-A4GXYE4L.js} +449 -383
  8. package/dist/chunk-CBLD2HS5.js +428 -0
  9. package/dist/chunk-ELGRMZOW.js +25 -0
  10. package/dist/{chunk-TWJCLD4W.js → chunk-FE7ZZQSE.js} +5 -5
  11. package/dist/{chunk-3NZICQHF.js → chunk-HKRQXPTI.js} +31 -8
  12. package/dist/{chunk-IXM5245F.js → chunk-LFE5KQ7Y.js} +553 -538
  13. package/dist/{chunk-VDDMADOF.js → chunk-LH2PLRQO.js} +16 -2
  14. package/dist/{chunk-EMXI4UCU.js → chunk-LUKYPVJ4.js} +29 -10
  15. package/dist/{chunk-UC7YIEHT.js → chunk-MLYSEZQB.js} +352 -47
  16. package/dist/chunk-NK5N5LXN.js +1681 -0
  17. package/dist/{chunk-RRJJGVKE.js → chunk-OBPKYXQL.js} +75 -17
  18. package/dist/{chunk-6ODUBKUB.js → chunk-PCPVTH6M.js} +78 -88
  19. package/dist/chunk-Q7WGTQQ5.js +88 -0
  20. package/dist/{chunk-EW6KB2J7.js → chunk-QVW76WDL.js} +440 -74
  21. package/dist/{chunk-YIPMDGG6.js → chunk-RSR7PFIQ.js} +79 -23
  22. package/dist/{chunk-3LDWWY7U.js → chunk-SM26HDS2.js} +41 -7
  23. package/dist/{chunk-OTLSJYAC.js → chunk-UJ6JZM65.js} +5185 -341
  24. package/dist/chunk-UVNMQM7W.js +444 -0
  25. package/dist/chunk-VTGPEC6B.js +308 -0
  26. package/dist/chunk-WVAML4H6.js +2145 -0
  27. package/dist/{chunk-JIMUYEKN.js → chunk-YIMOCZBZ.js} +1 -1
  28. package/dist/chunk-YMMQRE5J.js +292 -0
  29. package/dist/cloud-75VIT4KV.js +37 -0
  30. package/dist/{detect-ALF4GXRX.js → detect-OIE5V3VZ.js} +3 -3
  31. package/dist/{docker-QECQJ5P4.js → docker-VTR2GK5Y.js} +16 -8
  32. package/dist/{docker-edge-executor-62N64MGS.js → docker-edge-executor-22IBXNYJ.js} +5 -5
  33. package/dist/{edge-import-SAYTRJLO.js → edge-import-WJUB4M7F.js} +27 -23
  34. package/dist/edge-preflight-GBNCPNVU.js +42 -0
  35. package/dist/{ensure-container-edge-CFSCPGZK.js → ensure-container-edge-KPI2CVXJ.js} +9 -8
  36. package/dist/{executor-PT6UEJ5M.js → executor-7LXXZPBR.js} +15 -5
  37. package/dist/host-channel-preflight-KE6OONYK.js +50 -0
  38. package/dist/index.js +3783 -4221
  39. package/dist/{nginx-YOEEFBTE.js → nginx-KUFMLMAC.js} +13 -5
  40. package/dist/node-bootstrap.js +241 -0
  41. package/dist/node-entry.js +52 -1
  42. package/dist/{openresty-lua-WGBO2M3Z.js → openresty-lua-ATTH35BE.js} +8 -2
  43. package/dist/privilege-I4W3ZNZK.js +19 -0
  44. package/dist/server/engine/samples/amavisd/amavisd.conf +25 -0
  45. package/dist/server/engine/samples/fail2ban/jail.d/sshd.local +4 -1
  46. package/dist/server/engine/samples/postfix/master.cf +14 -0
  47. package/dist/server/index.js +77659 -61909
  48. package/dist/server/migrations/0098_instance_product_mode.sql +12 -0
  49. package/dist/server/migrations/0099_mail_webmail_project.sql +24 -0
  50. package/dist/server/migrations/0100_server_ssh_private_key.sql +11 -0
  51. package/dist/server/migrations/0101_mail_resume_step.sql +12 -0
  52. package/dist/server/migrations/0102_project_workload_axes.sql +19 -0
  53. package/dist/server/migrations/0103_dns_credential.sql +41 -0
  54. package/dist/server/migrations/0104_instance_host_control.sql +15 -0
  55. package/dist/server/migrations/0105_mail_inbound_rules.sql +80 -0
  56. package/dist/server/migrations/0106_mcp_call_tracking.sql +23 -0
  57. package/dist/server/migrations/0107_build_minute_metering_indexes.sql +20 -0
  58. package/dist/server/migrations/0108_credential.sql +68 -0
  59. package/dist/server/migrations/meta/_journal.json +77 -0
  60. package/dist/{setup-M2ZDGH3O.js → setup-XGLGUUFW.js} +10 -9
  61. package/dist/ssh-client-JBMA7SN4.js +28 -0
  62. package/dist/up-K4BV7466.js +57 -0
  63. package/package.json +4 -2
  64. package/dist/chunk-IW2UKBVF.js +0 -144
  65. package/dist/chunk-ZCFVHA25.js +0 -89
  66. package/dist/cloud-M2YLWEXO.js +0 -31
  67. /package/dist/{dist-es-JGBPABJI.js → dist-es-AXFRS7PS.js} +0 -0
@@ -5,42 +5,42 @@ const require = __ospCreateRequire(import.meta.url);
5
5
  import {
6
6
  checkGit,
7
7
  installGit
8
- } from "./chunk-IOT5ZX23.js";
8
+ } from "./chunk-A4GXYE4L.js";
9
9
  import {
10
10
  createDockerBuildContext,
11
11
  normalizeDockerRelativePath,
12
- resolveDockerfileCandidates
13
- } from "./chunk-EMXI4UCU.js";
12
+ resolveDockerfileCandidates,
13
+ resolveWithinDirectory
14
+ } from "./chunk-LUKYPVJ4.js";
14
15
  import {
15
16
  prepareStackOutput,
16
17
  resolveProjectDir,
17
18
  resolveStaticOutputPath,
18
19
  runLocalBuild
19
- } from "./chunk-JIMUYEKN.js";
20
+ } from "./chunk-YIMOCZBZ.js";
20
21
  import {
21
22
  BuildLogger,
22
23
  injectGitToken,
23
24
  runBuildPipeline,
24
25
  sq,
25
26
  transferLocalDirectory
26
- } from "./chunk-VDDMADOF.js";
27
- import {
28
- isRuntimeNotFoundError
29
- } from "./chunk-ZCFVHA25.js";
27
+ } from "./chunk-LH2PLRQO.js";
30
28
  import {
29
+ PRICING,
31
30
  STACKS,
32
31
  SYSTEM,
33
32
  TRANSFER_EXCLUDES,
34
33
  buildOutputTransferExcludes,
34
+ isRuntimeNotFoundError,
35
35
  missingOutputDirectoryMessage,
36
36
  packageManagerEnsureCommand,
37
37
  safeErrorMessage,
38
38
  shellSplitWords
39
- } from "./chunk-OTLSJYAC.js";
39
+ } from "./chunk-UJ6JZM65.js";
40
40
 
41
41
  // ../../packages/adapters/src/runtime/cloud.ts
42
42
  import { readFile } from "fs/promises";
43
- import { join, posix } from "path";
43
+ import { posix } from "path";
44
44
 
45
45
  // ../../packages/adapters/src/types.ts
46
46
  var DEFAULT_RESOURCE_CONFIG = {
@@ -48,6 +48,11 @@ var DEFAULT_RESOURCE_CONFIG = {
48
48
  memoryMb: 512,
49
49
  diskMb: 5120
50
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
+ };
51
56
  function cloudCpus(cpuCores) {
52
57
  return Math.max(0.25, cpuCores);
53
58
  }
@@ -1367,6 +1372,16 @@ async function provisionCloudWorkspace(client, config, logger) {
1367
1372
  throw err;
1368
1373
  }
1369
1374
  }
1375
+ var HostBuildForbiddenError = class extends Error {
1376
+ code = "HOST_BUILD_FORBIDDEN";
1377
+ constructor(message) {
1378
+ super(message);
1379
+ this.name = "HostBuildForbiddenError";
1380
+ }
1381
+ };
1382
+ function isHostBuildForbiddenError(err) {
1383
+ return err instanceof HostBuildForbiddenError || err instanceof Error && err.name === "HostBuildForbiddenError";
1384
+ }
1370
1385
  var CloudRuntime = class {
1371
1386
  name = "cloud";
1372
1387
  capabilities = /* @__PURE__ */ new Set([
@@ -1387,7 +1402,10 @@ var CloudRuntime = class {
1387
1402
  // disk is still there to start back up (see makeActive).
1388
1403
  "unitRestore",
1389
1404
  "serviceShell",
1390
- "inContainerExec"
1405
+ "inContainerExec",
1406
+ // Runs in the REMOTE workspace over the Oblien API, so it never touches the
1407
+ // control-plane host.
1408
+ "isolatedExec"
1391
1409
  ]);
1392
1410
  /**
1393
1411
  * Services map to Oblien workspaces, not Docker containers, so host-level
@@ -1396,7 +1414,18 @@ var CloudRuntime = class {
1396
1414
  * logging/dns — join this set as they're added in later phases.
1397
1415
  */
1398
1416
  unsupportedComposeKeys = /* @__PURE__ */ new Set([
1399
- "healthcheck"
1417
+ "healthcheck",
1418
+ // Namespace sharing has no Oblien equivalent — a workspace is not a container
1419
+ // whose netns/pidns a peer can join. Declared here so the deploy warns once
1420
+ // per service and continues, rather than the workload coming up on its own
1421
+ // network with nothing having said so (#533).
1422
+ "networkMode",
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"
1400
1429
  ]);
1401
1430
  client;
1402
1431
  // Optional admin-scoped proxy. Set when this runtime is constructed
@@ -1409,9 +1438,20 @@ var CloudRuntime = class {
1409
1438
  builtArtifacts = /* @__PURE__ */ new Map();
1410
1439
  activeBuilds = /* @__PURE__ */ new Map();
1411
1440
  compose;
1441
+ /**
1442
+ * May this runtime do work on the machine the API process runs on?
1443
+ *
1444
+ * DEFAULT DENY, unlike every other optional knob on PlatformConfig. The permissive
1445
+ * default is what the two host arms below relied on, and the cost of forgetting to
1446
+ * set it is a tenant's build commands running on a multi-tenant control plane —
1447
+ * while the cost of a missed opt-IN is a self-hosted local-orchestrated cloud deploy
1448
+ * failing loudly with the message below. Loud beats silent here.
1449
+ */
1450
+ allowHostBuild;
1412
1451
  constructor(client, opts) {
1413
1452
  this.client = client;
1414
1453
  this.adminProxy = opts?.adminProxy;
1454
+ this.allowHostBuild = opts?.allowHostBuild ?? false;
1415
1455
  this.compose = new CloudComposeSupport({
1416
1456
  client,
1417
1457
  builtArtifacts: this.builtArtifacts,
@@ -1420,6 +1460,16 @@ var CloudRuntime = class {
1420
1460
  execAndStream: (runtime, command, onLog, timeoutSeconds) => this.execAndStream(runtime, command, onLog, timeoutSeconds)
1421
1461
  });
1422
1462
  }
1463
+ /**
1464
+ * Refuse work on the API's own machine unless this runtime was explicitly built to
1465
+ * allow it. `what` completes the sentence "… is not allowed to <what>".
1466
+ */
1467
+ assertHostWorkAllowed(what) {
1468
+ if (this.allowHostBuild) return;
1469
+ throw new HostBuildForbiddenError(
1470
+ `This deployment asked to ${what}, which is not permitted on this instance. Cloud builds run inside your Oblien workspace. If this project was moved here from a self-hosted install, redeploy it so its build settings are re-resolved for the cloud (its stored settings still say "build locally").`
1471
+ );
1472
+ }
1423
1473
  supports(cap) {
1424
1474
  return this.capabilities.has(cap);
1425
1475
  }
@@ -1496,6 +1546,11 @@ var CloudRuntime = class {
1496
1546
  return activeBuild.abort.signal.aborted ? { ...result2, status: "cancelled", errorMessage: void 0 } : result2;
1497
1547
  }
1498
1548
  const buildLocally = config.buildStrategy === "local";
1549
+ if (buildLocally) {
1550
+ this.assertHostWorkAllowed(
1551
+ "run this project's install and build commands on the machine this API runs on"
1552
+ );
1553
+ }
1499
1554
  let wsId;
1500
1555
  let rt;
1501
1556
  try {
@@ -1629,6 +1684,9 @@ var CloudRuntime = class {
1629
1684
  await this.ensureWorkspaceGit(rt, plog, "build workspace");
1630
1685
  return;
1631
1686
  }
1687
+ this.assertHostWorkAllowed(
1688
+ "package a source directory from the machine this API runs on"
1689
+ );
1632
1690
  await transferLocalDirectory(
1633
1691
  cfg.localPath,
1634
1692
  {
@@ -1743,17 +1801,15 @@ var CloudRuntime = class {
1743
1801
  if (!config.localPath) {
1744
1802
  return this.resolveRemoteDockerfileBuildSource(config, logger);
1745
1803
  }
1804
+ this.assertHostWorkAllowed("read and package a source tree from the machine this API runs on");
1746
1805
  logger.log("Preparing local Dockerfile source...\n");
1747
1806
  const context = await createDockerBuildContext(config, {
1748
1807
  requireRepositoryDockerfile: true,
1749
1808
  onLog: logger.callback
1750
1809
  });
1751
- const dockerfilePath = join(context.contextDir, ...context.dockerfileName.split("/"));
1810
+ const dockerfilePath = resolveWithinDirectory(context.contextDir, context.dockerfileName);
1752
1811
  const dockerfile = await readFile(dockerfilePath, "utf-8");
1753
- const contextRoot = join(
1754
- context.contextDir,
1755
- ...normalizeDockerRelativePath(context.rootDirectory).split("/").filter(Boolean)
1756
- );
1812
+ const contextRoot = resolveWithinDirectory(context.contextDir, context.rootDirectory);
1757
1813
  return {
1758
1814
  kind: "local",
1759
1815
  contextRoot,
@@ -3129,5 +3185,7 @@ function clampShellWindow(value, fallback, min, max) {
3129
3185
  export {
3130
3186
  PAGE_CONTAINER_PREFIX,
3131
3187
  provisionCloudWorkspace,
3188
+ HostBuildForbiddenError,
3189
+ isHostBuildForbiddenError,
3132
3190
  CloudRuntime
3133
3191
  };
@@ -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
+ HOST_CHANNEL_AUTH_REJECTED,
7
+ HOST_CHANNEL_NOT_PROVISIONED,
8
+ HOST_CHANNEL_ROW_CREDENTIALS_UNUSED,
9
+ hostFirewallRule,
6
10
  isSshAuthError,
11
+ safeErrorMessage,
7
12
  systemDebug
8
- } from "./chunk-ZCFVHA25.js";
9
- import {
10
- PACKAGE_ROOT_ONLY_EXCLUDES
11
- } from "./chunk-OTLSJYAC.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
  }
@@ -107,9 +36,19 @@ function describeSshAuthFailure(config, originalMessage) {
107
36
  }
108
37
  return `SSH authentication failed for ${target}. (${originalMessage})`;
109
38
  }
39
+ function describeSshConnectFailure(config, originalMessage) {
40
+ const target = formatSshTarget(config);
41
+ const port = config.port ?? 22;
42
+ if (config.hostChannel) {
43
+ return `Cannot reach the host SSH endpoint ${target} from inside the Openship API container. Host control is configured, but the connection never completed. The usual cause is a host firewall dropping traffic from the Docker bridge to the host's SSH port \u2014 allow it with:
44
+ ${hostFirewallRule("unknown", [], port)}
45
+ Or re-run \`openship up\`, which probes this and offers the exact rule. (${originalMessage})`;
46
+ }
47
+ return `Cannot reach ${target} over SSH. Check that the host is up, that port ${port} is open, and that no firewall or security group is dropping the connection. (${originalMessage})`;
48
+ }
110
49
  function execFileText(command, args, timeout = 5e3) {
111
50
  return new Promise((resolve, reject) => {
112
- execFile2(command, args, { timeout }, (err, stdout, stderr) => {
51
+ execFile(command, args, { timeout }, (err, stdout, stderr) => {
113
52
  if (err) {
114
53
  reject(new Error(stderr.trim() || stdout.trim() || err.message));
115
54
  return;
@@ -131,7 +70,7 @@ function parseKnownHostsEntries(text) {
131
70
  return entries;
132
71
  }
133
72
  async function reconcileKnownHosts(config) {
134
- const knownHostsPath = join2(homedir(), ".ssh", "known_hosts");
73
+ const knownHostsPath = join(homedir(), ".ssh", "known_hosts");
135
74
  try {
136
75
  await access(knownHostsPath);
137
76
  } catch {
@@ -205,6 +144,10 @@ function toConnectConfig(config) {
205
144
  passphrase: config.privateKeyPassphrase,
206
145
  agent: config.sshAgent,
207
146
  tryKeyboard: false,
147
+ // Explicit rather than relying on ssh2's implicit 20s so callers can shorten it.
148
+ // The container→host channel does (8s): a filtered SYN there would otherwise
149
+ // stall every host operation for 20s apiece with no output.
150
+ readyTimeout: config.readyTimeoutMs ?? 2e4,
208
151
  keepaliveInterval: 15e3,
209
152
  // 10 (~150s) rather than 3 (45s): a small server pegged by a heavy
210
153
  // `docker build` / `bun install` can briefly starve sshd of keepalive
@@ -230,12 +173,12 @@ async function connectSshClient(config) {
230
173
  reject(new Error(describeSshAuthFailure(config, err.message)));
231
174
  return;
232
175
  }
233
- reject(err);
176
+ reject(new Error(describeSshConnectFailure(config, err.message)));
234
177
  });
235
178
  client.on("close", () => {
236
179
  if (settled) return;
237
180
  settled = true;
238
- reject(new Error("SSH connection closed before ready"));
181
+ reject(new Error(describeSshConnectFailure(config, "SSH connection closed before ready")));
239
182
  });
240
183
  client.connect(toConnectConfig(config));
241
184
  });
@@ -263,6 +206,7 @@ async function openSshUnixSocket(client, socketPath) {
263
206
  });
264
207
  });
265
208
  }
209
+ var DOCKER_DIAL_STDIO_COMMAND = "docker system dial-stdio";
266
210
  async function openSshExecChannel(client, command) {
267
211
  return new Promise((resolve, reject) => {
268
212
  client.exec(command, (err, stream) => {
@@ -274,6 +218,50 @@ async function openSshExecChannel(client, command) {
274
218
  });
275
219
  });
276
220
  }
221
+ var DIAL_STDIO_DIAGNOSTICS = /* @__PURE__ */ Symbol("openship.dialStdioDiagnostics");
222
+ var DIAL_STDIO_STDERR_CAP = 4096;
223
+ function attachDialStdioDiagnostics(stream) {
224
+ const channel = stream;
225
+ if (channel[DIAL_STDIO_DIAGNOSTICS]) return stream;
226
+ const diagnostics = {
227
+ stderr: "",
228
+ exitCode: null,
229
+ signal: null,
230
+ error: null
231
+ };
232
+ channel[DIAL_STDIO_DIAGNOSTICS] = diagnostics;
233
+ const stderr = stream.stderr;
234
+ stderr?.on("data", (chunk) => {
235
+ if (diagnostics.stderr.length >= DIAL_STDIO_STDERR_CAP) return;
236
+ diagnostics.stderr = (diagnostics.stderr + chunk.toString("utf8")).slice(
237
+ 0,
238
+ DIAL_STDIO_STDERR_CAP
239
+ );
240
+ });
241
+ stream.on("exit", (code, signal) => {
242
+ diagnostics.exitCode = typeof code === "number" ? code : null;
243
+ diagnostics.signal = typeof signal === "string" ? signal : null;
244
+ });
245
+ stream.on("error", (err) => {
246
+ diagnostics.error ??= safeErrorMessage(err);
247
+ });
248
+ return stream;
249
+ }
250
+ function dialStdioDiagnostics(stream) {
251
+ const diagnostics = stream?.[DIAL_STDIO_DIAGNOSTICS];
252
+ if (!diagnostics) return "";
253
+ const parts = [];
254
+ const stderr = diagnostics.stderr.trim();
255
+ if (stderr) parts.push(stderr.replace(/\s+/g, " "));
256
+ if (diagnostics.error) parts.push(diagnostics.error);
257
+ if (diagnostics.signal) parts.push(`killed by ${diagnostics.signal}`);
258
+ else if (diagnostics.exitCode) parts.push(`exit code ${diagnostics.exitCode}`);
259
+ if (parts.length === 0) return "";
260
+ return `\`${DOCKER_DIAL_STDIO_COMMAND}\` reported: ${parts.join(" \u2014 ")}`;
261
+ }
262
+ async function openDockerDialStdioChannel(client) {
263
+ return attachDialStdioDiagnostics(await openSshExecChannel(client, DOCKER_DIAL_STDIO_COMMAND));
264
+ }
277
265
  async function execSshCommand(client, command, timeout = 1e4) {
278
266
  return new Promise((resolve, reject) => {
279
267
  let stdout = "";
@@ -319,12 +307,14 @@ async function execSshCommand(client, command, timeout = 1e4) {
319
307
  }
320
308
 
321
309
  export {
322
- getTarCreateEnv,
323
- prepareSourceTarArgs,
324
310
  reconcileKnownHosts,
325
311
  connectSshClient,
326
312
  openSftp,
327
313
  openSshUnixSocket,
314
+ DOCKER_DIAL_STDIO_COMMAND,
328
315
  openSshExecChannel,
316
+ attachDialStdioDiagnostics,
317
+ dialStdioDiagnostics,
318
+ openDockerDialStdioChannel,
329
319
  execSshCommand
330
320
  };
@@ -0,0 +1,88 @@
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
+ PACKAGE_ROOT_ONLY_EXCLUDES
7
+ } from "./chunk-UJ6JZM65.js";
8
+
9
+ // ../../packages/adapters/src/archive.ts
10
+ import { execFile } from "child_process";
11
+ import { mkdtemp, rm, stat, writeFile } from "fs/promises";
12
+ import { tmpdir } from "os";
13
+ import { join } from "path";
14
+ import { promisify } from "util";
15
+ var execFileAsync = promisify(execFile);
16
+ var ROOT_ONLY_EXCLUDES = new Set(PACKAGE_ROOT_ONLY_EXCLUDES);
17
+ var NOOP_CLEANUP = async () => {
18
+ };
19
+ function getTarCreateEnv() {
20
+ return {
21
+ ...process.env,
22
+ COPYFILE_DISABLE: "1",
23
+ COPY_EXTENDED_ATTRIBUTES_DISABLE: "1"
24
+ };
25
+ }
26
+ function tarCreateBaseArgs(localPath) {
27
+ const darwinFlags = process.platform === "darwin" ? ["--no-mac-metadata", "--no-xattrs", "--no-acls", "--no-fflags"] : [];
28
+ return [...darwinFlags, "-czf", "-", "-C", localPath];
29
+ }
30
+ async function gitTrackedFiles(localPath) {
31
+ try {
32
+ const { stdout } = await execFileAsync(
33
+ "git",
34
+ ["-C", localPath, "ls-files", "-z", "--cached", "--others", "--exclude-standard"],
35
+ { maxBuffer: 256 * 1024 * 1024 }
36
+ );
37
+ const files = stdout.split("\0").filter(Boolean);
38
+ return files.length > 0 ? files : null;
39
+ } catch {
40
+ return null;
41
+ }
42
+ }
43
+ async function existingRelativePaths(root, paths) {
44
+ if (!paths?.length) return [];
45
+ const found = [];
46
+ for (const p of paths) {
47
+ try {
48
+ await stat(join(root, p));
49
+ found.push(p);
50
+ } catch {
51
+ }
52
+ }
53
+ return found;
54
+ }
55
+ async function prepareSourceTarArgs(localPath, options) {
56
+ if (options?.includes?.length) {
57
+ return { args: getTarCreateArgs(localPath, options), cleanup: NOOP_CLEANUP };
58
+ }
59
+ const files = await gitTrackedFiles(localPath);
60
+ if (files) {
61
+ const extra = await existingRelativePaths(localPath, options?.alsoInclude);
62
+ const entries = extra.length ? [...files, ...extra] : files;
63
+ const tmpDir = await mkdtemp(join(tmpdir(), "openship-tarlist-"));
64
+ const listFile = join(tmpDir, "files.null");
65
+ await writeFile(listFile, entries.join("\0"));
66
+ const args = [...tarCreateBaseArgs(localPath), "--null", "-T", listFile];
67
+ return { args, cleanup: () => rm(tmpDir, { recursive: true, force: true }) };
68
+ }
69
+ return { args: getTarCreateArgs(localPath, options), cleanup: NOOP_CLEANUP };
70
+ }
71
+ function getTarCreateArgs(localPath, options) {
72
+ const args = tarCreateBaseArgs(localPath);
73
+ if (options?.includes?.length) {
74
+ args.push(...options.includes);
75
+ return args;
76
+ }
77
+ for (const exclude of options?.excludes ?? []) {
78
+ const pattern = ROOT_ONLY_EXCLUDES.has(exclude) ? `./${exclude}` : exclude;
79
+ args.push(`--exclude=${pattern}`);
80
+ }
81
+ args.push(".");
82
+ return args;
83
+ }
84
+
85
+ export {
86
+ getTarCreateEnv,
87
+ prepareSourceTarArgs
88
+ };