openship 0.6.5 → 0.6.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (54) hide show
  1. package/dist/{bare-XQIPDO4R.js → bare-OXJVW3AK.js} +11 -10
  2. package/dist/{chunk-WE7BXW6L.js → chunk-2YM3NI2E.js} +2 -2
  3. package/dist/{chunk-RFWJE7ZN.js → chunk-4I2BM2DT.js} +3 -3
  4. package/dist/{chunk-QLX7NBEA.js → chunk-5DMWSTOM.js} +78 -1836
  5. package/dist/{chunk-VDESJSG6.js → chunk-77QDF6UD.js} +135 -21
  6. package/dist/{chunk-7334NSJL.js → chunk-A4GXYE4L.js} +67 -31
  7. package/dist/{chunk-HPXP6LXZ.js → chunk-CBLD2HS5.js} +3 -3
  8. package/dist/{chunk-6JELW75A.js → chunk-FE7ZZQSE.js} +3 -3
  9. package/dist/{chunk-XVOBL7BC.js → chunk-HKRQXPTI.js} +1 -1
  10. package/dist/{chunk-GRTXMUHA.js → chunk-LFE5KQ7Y.js} +11 -11
  11. package/dist/{chunk-TM2IQPYU.js → chunk-LH2PLRQO.js} +2 -2
  12. package/dist/{chunk-LFCSC4IU.js → chunk-LUKYPVJ4.js} +3 -3
  13. package/dist/{chunk-VHMOQL5Y.js → chunk-MLYSEZQB.js} +115 -19
  14. package/dist/{chunk-7S4USQYF.js → chunk-NK5N5LXN.js} +50 -122
  15. package/dist/{chunk-3ZAGC5NT.js → chunk-OBPKYXQL.js} +17 -6
  16. package/dist/{chunk-5OUWIPOB.js → chunk-PCPVTH6M.js} +12 -84
  17. package/dist/chunk-Q7WGTQQ5.js +88 -0
  18. package/dist/{chunk-VLQ3UIVO.js → chunk-QVW76WDL.js} +52 -26
  19. package/dist/{chunk-PZRRL5PC.js → chunk-RSR7PFIQ.js} +14 -3
  20. package/dist/{chunk-A7EWM2KA.js → chunk-SM26HDS2.js} +1 -1
  21. package/dist/{chunk-LEYSL46B.js → chunk-UJ6JZM65.js} +3426 -632
  22. package/dist/{chunk-WFRXYWK2.js → chunk-UVNMQM7W.js} +1 -1
  23. package/dist/chunk-VTGPEC6B.js +308 -0
  24. package/dist/chunk-WVAML4H6.js +2145 -0
  25. package/dist/{chunk-MSZUCNPV.js → chunk-YIMOCZBZ.js} +1 -1
  26. package/dist/{chunk-EZPPTWJQ.js → chunk-YMMQRE5J.js} +1 -1
  27. package/dist/{cloud-3DODOWTR.js → cloud-75VIT4KV.js} +16 -15
  28. package/dist/{detect-P4CIQBRH.js → detect-OIE5V3VZ.js} +3 -3
  29. package/dist/{docker-3K2NEBD6.js → docker-VTR2GK5Y.js} +7 -6
  30. package/dist/{docker-edge-executor-YZZ6P7IC.js → docker-edge-executor-22IBXNYJ.js} +4 -4
  31. package/dist/{edge-import-MCJNVTZ2.js → edge-import-WJUB4M7F.js} +23 -22
  32. package/dist/{edge-preflight-ES6FH4JK.js → edge-preflight-GBNCPNVU.js} +10 -10
  33. package/dist/{ensure-container-edge-YPITNH45.js → ensure-container-edge-KPI2CVXJ.js} +9 -9
  34. package/dist/{executor-DZGX55K2.js → executor-7LXXZPBR.js} +6 -3
  35. package/dist/{host-channel-preflight-MUXE3KZU.js → host-channel-preflight-KE6OONYK.js} +23 -22
  36. package/dist/index.js +95 -84
  37. package/dist/{nginx-NSAHWGJV.js → nginx-KUFMLMAC.js} +9 -7
  38. package/dist/{openresty-lua-DR4D3EZ6.js → openresty-lua-ATTH35BE.js} +6 -4
  39. package/dist/{privilege-YTVI4SKF.js → privilege-I4W3ZNZK.js} +4 -4
  40. package/dist/server/engine/samples/amavisd/amavisd.conf +25 -0
  41. package/dist/server/engine/samples/postfix/master.cf +14 -0
  42. package/dist/server/index.js +35002 -27533
  43. package/dist/server/migrations/0105_mail_inbound_rules.sql +80 -0
  44. package/dist/server/migrations/0106_mcp_call_tracking.sql +23 -0
  45. package/dist/server/migrations/0107_build_minute_metering_indexes.sql +20 -0
  46. package/dist/server/migrations/0108_credential.sql +68 -0
  47. package/dist/server/migrations/meta/_journal.json +28 -0
  48. package/dist/{setup-WUUZURHS.js → setup-XGLGUUFW.js} +10 -10
  49. package/dist/ssh-client-JBMA7SN4.js +28 -0
  50. package/dist/{up-XEO3SR6Z.js → up-K4BV7466.js} +26 -27
  51. package/package.json +1 -1
  52. package/dist/chunk-6HOA5JT7.js +0 -124
  53. package/dist/chunk-XRNRT37W.js +0 -316
  54. /package/dist/{dist-es-ZP5QDSJ3.js → dist-es-AXFRS7PS.js} +0 -0
@@ -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
+ };
@@ -2,14 +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
- edgeDownExplanation,
7
- explainEdgeDown,
8
- isEdgeDownFailure
9
- } from "./chunk-I3DPMTSF.js";
10
- import {
11
- edgeFailureReason
12
- } from "./chunk-WE7BXW6L.js";
13
5
  import {
14
6
  ACME_CHALLENGE_LOCATION,
15
7
  ACME_HTTP01_PORT,
@@ -26,14 +18,25 @@ import {
26
18
  edgeChallengeVhostConf,
27
19
  isCloudFrontedHost,
28
20
  luaSourceAvailable
29
- } from "./chunk-PZRRL5PC.js";
21
+ } from "./chunk-RSR7PFIQ.js";
22
+ import {
23
+ edgeDownExplanation,
24
+ explainEdgeDown,
25
+ isEdgeDownFailure
26
+ } from "./chunk-I3DPMTSF.js";
30
27
  import {
31
28
  EXECUTOR_DELEGATE
32
- } from "./chunk-WFRXYWK2.js";
29
+ } from "./chunk-UVNMQM7W.js";
33
30
  import {
34
31
  logEntry,
35
32
  sq
36
33
  } from "./chunk-ZFJXPCQP.js";
34
+ import {
35
+ edgeFailureReason
36
+ } from "./chunk-2YM3NI2E.js";
37
+ import {
38
+ probeListeningPort
39
+ } from "./chunk-SM26HDS2.js";
37
40
  import {
38
41
  PROXY_DIRECTIVES,
39
42
  parseProxyValue,
@@ -41,7 +44,7 @@ import {
41
44
  resolveRedirectStatus,
42
45
  safeErrorMessage,
43
46
  sanitizeProxySettings
44
- } from "./chunk-LEYSL46B.js";
47
+ } from "./chunk-UJ6JZM65.js";
45
48
 
46
49
  // ../../packages/adapters/src/infra/nginx.ts
47
50
  import {
@@ -321,14 +324,15 @@ var PROXY_HEADERS = `proxy_set_header Host $host;
321
324
  proxy_set_header X-Forwarded-Proto $openship_fwd_proto;
322
325
  proxy_http_version 1.1;
323
326
  proxy_set_header Upgrade $http_upgrade;
324
- proxy_set_header Connection "upgrade";`;
327
+ proxy_set_header Connection "upgrade";
328
+ proxy_pass_header X-Accel-Buffering;`;
325
329
  var FORWARD_VARS = ` set $openship_fwd_proto $scheme;
326
330
  set $openship_redirect_https 1;
327
331
  if ($http_x_forwarded_proto = "https") {
328
332
  set $openship_fwd_proto https;
329
333
  set $openship_redirect_https 0;
330
334
  }`;
331
- var VHOST_GENERATION = 1;
335
+ var VHOST_GENERATION = 2;
332
336
  var GENERATION_MARKER = `# openship-vhost-gen: ${VHOST_GENERATION}`;
333
337
  var GENERATED_HEADER = `# Auto-generated by Openship - do not edit manually
334
338
  ${GENERATION_MARKER}`;
@@ -638,6 +642,11 @@ function assertValidStaticRoot(root, opts) {
638
642
  }
639
643
  }
640
644
  var execFileAsync = promisify(cpExecFile);
645
+ function isAcmePortBindFailure(output) {
646
+ return /could not bind (?:tcp )?port|address already in use|problem binding to port/i.test(
647
+ output || ""
648
+ );
649
+ }
641
650
  function summarizeCertbotFailure(output, domain) {
642
651
  const edgeDown = edgeDownExplanation(output);
643
652
  if (edgeDown) return edgeDown;
@@ -654,7 +663,9 @@ function certbotDiagnosis(output, domain) {
654
663
  const challengeUrl = pick(/https?:\/\/\S*\.well-known\/acme-challenge\/\S*/i);
655
664
  const redirectedToHttps = /^https:/i.test(challengeUrl ?? "");
656
665
  let diagnosis;
657
- if (/timeout during connect|connection refused|failed to connect|Timeout/i.test(text)) {
666
+ if (isAcmePortBindFailure(text)) {
667
+ diagnosis = `Couldn't start the certificate challenge listener for ${domain} on port ${ACME_HTTP01_PORT}: something on this server is already holding it. That port is used only while a certificate is being issued, so this is almost always a certificate run from an earlier attempt that hasn't exited \u2014 retry, and if it persists find the holder with \`ss -ltnp | grep ${ACME_HTTP01_PORT}\` and stop it. Nothing was requested from Let's Encrypt, so this is not a DNS or routing problem.`;
668
+ } else if (/timeout during connect|connection refused|failed to connect|Timeout/i.test(text)) {
658
669
  diagnosis = `Port 80 for ${domain} isn't reachable from the internet \u2014 a firewall / cloud security group is blocking it, the domain doesn't point at this server, or another proxy is still bound to :80.`;
659
670
  } else if (/NXDOMAIN|no\s+(A|AAAA)\s+record|DNS problem|could not be resolved|no records? found/i.test(text)) {
660
671
  diagnosis = `${domain} doesn't resolve to this server yet \u2014 the DNS A record is missing or hasn't propagated. Wait for propagation, then retry from the Domains tab.`;
@@ -1450,18 +1461,24 @@ ${serveLocation}
1450
1461
  /**
1451
1462
  * Provision a TLS certificate using certbot.
1452
1463
  *
1453
- * Runs `certbot certonly` in webroot mode using the ACME challenge
1454
- * directory served by OpenResty, then rewrites the config to include
1455
- * SSL and reloads.
1464
+ * HTTP-01 ONLY there is no DNS-01 path anywhere in this file, and the DNS credentials the
1465
+ * product stores are for record MANAGEMENT, not for challenges.
1466
+ *
1467
+ * The authenticator is `--standalone` on {@link ACME_HTTP01_PORT} (a loopback alt-port), with
1468
+ * the edge proxying `/.well-known/acme-challenge/` to it — see `ACME_CHALLENGE_LOCATION`. So
1469
+ * Let's Encrypt fetches the challenge over :80 from OpenResty, which forwards it to certbot's
1470
+ * transient listener. No port-80 fight, no webroot directory to keep in sync, and zero
1471
+ * downtime: certbot holds that port only while issuing.
1472
+ *
1473
+ * THIS COMMENT USED TO DESCRIBE `--webroot`, and argued that `--standalone` was dead code
1474
+ * because "certbot --standalone binds to port 80 itself, but OpenResty already owns 80". That
1475
+ * was true of plain `--standalone`, and it is exactly the problem `--http-01-port` solves —
1476
+ * the two sat contradicting each other in one file, with the doc describing a mechanism the
1477
+ * code below had stopped using. Left recorded because the stale version's reasoning is
1478
+ * persuasive enough to "fix" the code back.
1456
1479
  *
1457
- * Only --webroot is attempted. The previous --standalone fallback was
1458
- * dead code on any normal install: certbot --standalone binds to port
1459
- * 80 itself, but OpenResty already owns 80 on the same box, so the
1460
- * fallback would always fail with EADDRINUSE — amplifying a transient
1461
- * --webroot failure into an immediate hard error. The caller
1462
- * (route-registration.ts) already wraps provisionCert in try/catch so
1463
- * a webroot failure becomes a "deploy continues on HTTP, retry from
1464
- * Domains tab" warning instead of a deploy abort.
1480
+ * The caller (route-registration.ts) wraps this in try/catch, so a failure becomes a "deploy
1481
+ * continues on HTTP, retry from the Domains tab" warning rather than a deploy abort.
1465
1482
  */
1466
1483
  async provisionCert(domain, opts) {
1467
1484
  assertValidDomain(domain);
@@ -1493,7 +1510,15 @@ ${serveLocation}
1493
1510
  ...opts?.force ? ["--force-renewal"] : []
1494
1511
  ], opts?.onLog);
1495
1512
  } catch (err) {
1496
- throw new Error(summarizeCertbotFailure(this.redactAcmeSecrets(safeErrorMessage(err)), domain));
1513
+ const raw = this.redactAcmeSecrets(safeErrorMessage(err));
1514
+ let summary = summarizeCertbotFailure(raw, domain);
1515
+ if (isAcmePortBindFailure(raw) && this.executor) {
1516
+ const occupant = await probeListeningPort(this.executor, ACME_HTTP01_PORT).catch(() => null);
1517
+ if (occupant) {
1518
+ summary += ` Currently held by ${occupant.command}` + (occupant.pid !== null ? ` (pid ${occupant.pid})` : "") + (occupant.systemdUnit ? ` via ${occupant.systemdUnit}` : "") + `.`;
1519
+ }
1520
+ }
1521
+ throw new Error(summary);
1497
1522
  } finally {
1498
1523
  if (eabConfig) await this._rm(dirname(eabConfig)).catch(() => void 0);
1499
1524
  }
@@ -1965,6 +1990,7 @@ export {
1965
1990
  acmeKeyArgs,
1966
1991
  assertValidChallengeToken,
1967
1992
  MANAGED_STATIC_BASE,
1993
+ isAcmePortBindFailure,
1968
1994
  summarizeCertbotFailure,
1969
1995
  NginxProvider
1970
1996
  };
@@ -5,10 +5,10 @@ const require = __ospCreateRequire(import.meta.url);
5
5
  import {
6
6
  envOps,
7
7
  opScript
8
- } from "./chunk-EZPPTWJQ.js";
8
+ } from "./chunk-YMMQRE5J.js";
9
9
  import {
10
10
  resolveEnvironment
11
- } from "./chunk-WFRXYWK2.js";
11
+ } from "./chunk-UVNMQM7W.js";
12
12
  import {
13
13
  sq
14
14
  } from "./chunk-ZFJXPCQP.js";
@@ -17,7 +17,7 @@ import {
17
17
  answered,
18
18
  refused,
19
19
  safeErrorMessage
20
- } from "./chunk-LEYSL46B.js";
20
+ } from "./chunk-UJ6JZM65.js";
21
21
 
22
22
  // ../../packages/adapters/src/infra/openresty-lua.ts
23
23
  import { createHash, randomBytes } from "crypto";
@@ -248,6 +248,7 @@ var OPENRESTY_MGMT_PORT = 9145;
248
248
  var OPENRESTY_SITE_LUALIB = OPENRESTY_LUA_DIR.replace(/\/[^/]+$/, "");
249
249
  var EDGE_LUA_PACKAGE_PATH = `${OPENRESTY_SITE_LUALIB}/?.lua;;`;
250
250
  var EDGE_SERVER_NAMES_HASH_BUCKET_SIZE = 128;
251
+ var EDGE_CLIENT_MAX_BODY_SIZE = "50m";
251
252
  var EDGE_SHARED_DICTS = [
252
253
  /** Analytics counters. */
253
254
  { name: "analytics", size: "256m" },
@@ -440,6 +441,12 @@ async function ensureOpenRestyConfig(executor, paths) {
440
441
  );
441
442
  }
442
443
  }
444
+ const hasBodySize = await executor.exec(`grep -qE '^[[:space:]]*client_max_body_size' ${paths.confPath}`).then(() => true).catch(() => false);
445
+ if (!hasBodySize) {
446
+ await executor.exec(
447
+ `sed -i '/http *{/a \\ client_max_body_size ${EDGE_CLIENT_MAX_BODY_SIZE};' ${paths.confPath}`
448
+ );
449
+ }
443
450
  }
444
451
  function MINIMAL_NGINX_CONF(confDir, sitesDir) {
445
452
  return `# Auto-generated by Openship - safe to extend
@@ -456,6 +463,9 @@ http {
456
463
  # \`nginx -t\` outright for any server_name past ~63 chars, refusing the reload
457
464
  # and wedging every route on the box, not just the long one.
458
465
  server_names_hash_bucket_size ${EDGE_SERVER_NAMES_HASH_BUCKET_SIZE};
466
+ # See EDGE_CLIENT_MAX_BODY_SIZE \u2014 nginx's 1 MB default 413s an upload before the app
467
+ # ever sees it. A project's own value overrides this (server beats http).
468
+ client_max_body_size ${EDGE_CLIENT_MAX_BODY_SIZE};
459
469
  include ${sitesDir}/*.conf;
460
470
  }
461
471
  `;
@@ -715,6 +725,7 @@ export {
715
725
  OPENRESTY_MGMT_PORT,
716
726
  EDGE_LUA_PACKAGE_PATH,
717
727
  EDGE_SERVER_NAMES_HASH_BUCKET_SIZE,
728
+ EDGE_CLIENT_MAX_BODY_SIZE,
718
729
  EDGE_SHARED_DICTS,
719
730
  parseNginxVersion,
720
731
  OPENRESTY_DEFAULT_PATHS,
@@ -7,7 +7,7 @@ import {
7
7
  isRemoteConnectionError,
8
8
  safeErrorMessage,
9
9
  systemDebug
10
- } from "./chunk-LEYSL46B.js";
10
+ } from "./chunk-UJ6JZM65.js";
11
11
 
12
12
  // ../../packages/adapters/src/system/probe-exec.ts
13
13
  async function tryExec(executor, command, opts) {