openship 0.6.5 → 0.6.7

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 (68) hide show
  1. package/dist/bare-7QD623QV.js +29 -0
  2. package/dist/chunk-2ANV5ZQ5.js +88 -0
  3. package/dist/{chunk-LEYSL46B.js → chunk-2VSW6Q6C.js} +3911 -898
  4. package/dist/{chunk-5OUWIPOB.js → chunk-5T4UVOM3.js} +16 -86
  5. package/dist/{chunk-EZPPTWJQ.js → chunk-BLEXTYRK.js} +1 -1
  6. package/dist/{chunk-3ZAGC5NT.js → chunk-CIJV43LH.js} +83 -33
  7. package/dist/{chunk-7S4USQYF.js → chunk-DDFUESPJ.js} +67 -124
  8. package/dist/{chunk-6FH4AXVY.js → chunk-E2X26PXR.js} +1 -1
  9. package/dist/{chunk-XVOBL7BC.js → chunk-FYPFEDPZ.js} +1 -1
  10. package/dist/{chunk-WFRXYWK2.js → chunk-FZ6QCF6B.js} +8 -6
  11. package/dist/{chunk-VHMOQL5Y.js → chunk-GGIAIGUL.js} +125 -80
  12. package/dist/{chunk-WE7BXW6L.js → chunk-GIPJNNI5.js} +38 -44
  13. package/dist/{chunk-ZFJXPCQP.js → chunk-H3WT3A6R.js} +4 -3
  14. package/dist/chunk-H4FS5BED.js +936 -0
  15. package/dist/{chunk-HPXP6LXZ.js → chunk-J2XM3QSP.js} +8 -5
  16. package/dist/{chunk-6JELW75A.js → chunk-J3K6XABC.js} +5 -5
  17. package/dist/{chunk-VDESJSG6.js → chunk-LHFXSC36.js} +558 -109
  18. package/dist/{chunk-MSZUCNPV.js → chunk-MEE6OZFP.js} +3 -3
  19. package/dist/chunk-N5YZMO33.js +111 -0
  20. package/dist/{chunk-VLQ3UIVO.js → chunk-NRTJRNAE.js} +105 -37
  21. package/dist/chunk-PPEBIY5F.js +2331 -0
  22. package/dist/{chunk-RFWJE7ZN.js → chunk-PVP4RGWT.js} +3 -3
  23. package/dist/{chunk-PZRRL5PC.js → chunk-QHMDCYZ3.js} +15 -4
  24. package/dist/chunk-R4VDPFLS.js +106 -0
  25. package/dist/chunk-RDXT4BQB.js +310 -0
  26. package/dist/{chunk-GRTXMUHA.js → chunk-S6B2Z3OO.js} +145 -60
  27. package/dist/chunk-UWEV6XTS.js +249 -0
  28. package/dist/{chunk-QLX7NBEA.js → chunk-VQM6JHJB.js} +1004 -2046
  29. package/dist/{chunk-A7EWM2KA.js → chunk-VSTFHUCV.js} +44 -50
  30. package/dist/chunk-WU54BXFE.js +70 -0
  31. package/dist/{chunk-LFCSC4IU.js → chunk-XWHWTHEL.js} +313 -469
  32. package/dist/{chunk-7334NSJL.js → chunk-ZPJ74LXE.js} +76 -38
  33. package/dist/cloud-KNNABE3O.js +40 -0
  34. package/dist/{detect-P4CIQBRH.js → detect-G4DR3U2N.js} +9 -6
  35. package/dist/{docker-3K2NEBD6.js → docker-YOVPCIB4.js} +11 -7
  36. package/dist/{docker-edge-executor-YZZ6P7IC.js → docker-edge-executor-QFXD3337.js} +8 -6
  37. package/dist/edge-import-CRFVSR6P.js +56 -0
  38. package/dist/{edge-preflight-ES6FH4JK.js → edge-preflight-MXERVAMG.js} +15 -12
  39. package/dist/{ensure-container-edge-YPITNH45.js → ensure-container-edge-F4JUNQIK.js} +13 -10
  40. package/dist/{executor-DZGX55K2.js → executor-CVPAYYHR.js} +10 -5
  41. package/dist/host-channel-preflight-QTDNGLCK.js +54 -0
  42. package/dist/index.js +139 -92
  43. package/dist/{local-executor-WOFZD72X.js → local-executor-ODC6RANU.js} +3 -2
  44. package/dist/{nginx-NSAHWGJV.js → nginx-KLFF43TC.js} +13 -8
  45. package/dist/{openresty-lua-DR4D3EZ6.js → openresty-lua-VKWUV4GB.js} +8 -5
  46. package/dist/{privilege-YTVI4SKF.js → privilege-7D3SSOL3.js} +6 -5
  47. package/dist/server/engine/samples/amavisd/amavisd.conf +25 -0
  48. package/dist/server/engine/samples/postfix/master.cf +14 -0
  49. package/dist/server/index.js +44526 -33476
  50. package/dist/server/migrations/0105_mail_inbound_rules.sql +80 -0
  51. package/dist/server/migrations/0106_mcp_call_tracking.sql +23 -0
  52. package/dist/server/migrations/0107_build_minute_metering_indexes.sql +20 -0
  53. package/dist/server/migrations/0108_credential.sql +68 -0
  54. package/dist/server/migrations/meta/_journal.json +28 -0
  55. package/dist/setup-I5AJC6NJ.js +25 -0
  56. package/dist/ssh-client-625VWGKP.js +29 -0
  57. package/dist/up-QXTOOVXC.js +61 -0
  58. package/package.json +1 -1
  59. package/dist/bare-XQIPDO4R.js +0 -24
  60. package/dist/chunk-6HOA5JT7.js +0 -124
  61. package/dist/chunk-TM2IQPYU.js +0 -434
  62. package/dist/chunk-XRNRT37W.js +0 -316
  63. package/dist/cloud-3DODOWTR.js +0 -36
  64. package/dist/edge-import-MCJNVTZ2.js +0 -51
  65. package/dist/host-channel-preflight-MUXE3KZU.js +0 -49
  66. package/dist/setup-WUUZURHS.js +0 -22
  67. package/dist/up-XEO3SR6Z.js +0 -58
  68. /package/dist/{dist-es-ZP5QDSJ3.js → dist-es-AXFRS7PS.js} +0 -0
@@ -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-EZPPTWJQ.js";
7
+ } from "./chunk-BLEXTYRK.js";
8
8
  import {
9
9
  elevatedExecutor,
10
10
  resolveEnvironment
11
- } from "./chunk-WFRXYWK2.js";
11
+ } from "./chunk-FZ6QCF6B.js";
12
12
  import {
13
13
  answered,
14
14
  refused
15
- } from "./chunk-LEYSL46B.js";
15
+ } from "./chunk-2VSW6Q6C.js";
16
16
 
17
17
  // ../../packages/adapters/src/system/privilege.ts
18
18
  function rootChecked(executor, why) {
@@ -5,19 +5,19 @@ const require = __ospCreateRequire(import.meta.url);
5
5
  import {
6
6
  envOps,
7
7
  opScript
8
- } from "./chunk-EZPPTWJQ.js";
8
+ } from "./chunk-BLEXTYRK.js";
9
9
  import {
10
10
  resolveEnvironment
11
- } from "./chunk-WFRXYWK2.js";
11
+ } from "./chunk-FZ6QCF6B.js";
12
12
  import {
13
13
  sq
14
- } from "./chunk-ZFJXPCQP.js";
14
+ } from "./chunk-H3WT3A6R.js";
15
15
  import {
16
16
  SYSTEM,
17
17
  answered,
18
18
  refused,
19
19
  safeErrorMessage
20
- } from "./chunk-LEYSL46B.js";
20
+ } from "./chunk-2VSW6Q6C.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,
@@ -0,0 +1,106 @@
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
+
6
+ // ../../packages/adapters/src/system/reachability.ts
7
+ import { connect } from "net";
8
+ import { request } from "http";
9
+ function classifyDialError(code) {
10
+ switch (code) {
11
+ case "ECONNREFUSED":
12
+ case "ECONNRESET":
13
+ return "refused";
14
+ case "ENOTFOUND":
15
+ case "EAI_AGAIN":
16
+ return "unresolved";
17
+ case "EHOSTUNREACH":
18
+ case "ENETUNREACH":
19
+ case "ENETDOWN":
20
+ return "no_route";
21
+ // The kernel's own connect timeout, i.e. our `timeout` reported for us.
22
+ case "ETIMEDOUT":
23
+ return "timeout";
24
+ default:
25
+ return "error";
26
+ }
27
+ }
28
+ function probeTcpDetailed(host, port, timeoutMs = 2500) {
29
+ return new Promise((resolve) => {
30
+ let settled = false;
31
+ const done = (result, socket2) => {
32
+ if (settled) return;
33
+ settled = true;
34
+ try {
35
+ socket2?.destroy();
36
+ } catch {
37
+ }
38
+ resolve(result);
39
+ };
40
+ const socket = connect({ host, port });
41
+ socket.setTimeout(timeoutMs);
42
+ socket.once("connect", () => done({ ok: true }, socket));
43
+ socket.once(
44
+ "timeout",
45
+ () => done({ ok: false, reason: "timeout", message: `no response within ${timeoutMs}ms` }, socket)
46
+ );
47
+ socket.once("error", (err) => {
48
+ const errno = err;
49
+ done(
50
+ {
51
+ ok: false,
52
+ reason: classifyDialError(errno.code),
53
+ ...errno.code ? { code: errno.code } : {},
54
+ ...errno.message ? { message: errno.message } : {}
55
+ },
56
+ socket
57
+ );
58
+ });
59
+ });
60
+ }
61
+ function probeHostedHttp(opts) {
62
+ const { hostname, path = "/", port = 80, timeoutMs = 3e3 } = opts;
63
+ return new Promise((resolve) => {
64
+ let settled = false;
65
+ const done = (value) => {
66
+ if (settled) return;
67
+ settled = true;
68
+ resolve(value);
69
+ };
70
+ const req = request(
71
+ {
72
+ host: "127.0.0.1",
73
+ port,
74
+ path,
75
+ method: "GET",
76
+ timeout: timeoutMs,
77
+ // Claim https so the :80 block SERVES instead of answering our own
78
+ // HTTP→HTTPS redirect before the doc root is consulted — see the curl twin in
79
+ // output-exists.ts for why a 301 here read as a healthy site.
80
+ headers: { Host: hostname, "X-Forwarded-Proto": "https" }
81
+ },
82
+ (res) => {
83
+ const status = res.statusCode ?? 0;
84
+ res.resume();
85
+ done(
86
+ status > 0 ? { status, served: status >= 200 && status < 400 || status === 401 || status === 403 } : null
87
+ );
88
+ }
89
+ );
90
+ const fail = () => {
91
+ try {
92
+ req.destroy();
93
+ } catch {
94
+ }
95
+ done(null);
96
+ };
97
+ req.once("timeout", fail);
98
+ req.once("error", fail);
99
+ req.end();
100
+ });
101
+ }
102
+
103
+ export {
104
+ probeTcpDetailed,
105
+ probeHostedHttp
106
+ };
@@ -0,0 +1,310 @@
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
+ COMPOSE_ENV_FILE,
7
+ composeInstallExists,
8
+ readComposeEnvFile,
9
+ startUnitHint
10
+ } from "./chunk-VQM6JHJB.js";
11
+ import {
12
+ INTERNAL_TOKEN_FILE,
13
+ OS_DIR
14
+ } from "./chunk-ELGRMZOW.js";
15
+ import {
16
+ LocalExecutor
17
+ } from "./chunk-E2X26PXR.js";
18
+ import {
19
+ edgeCrashReason
20
+ } from "./chunk-GIPJNNI5.js";
21
+ import {
22
+ EDGE_CONTAINER_NAME
23
+ } from "./chunk-UWEV6XTS.js";
24
+
25
+ // src/lib/edge-import.ts
26
+ import chalk from "chalk";
27
+ import ora from "ora";
28
+
29
+ // src/lib/internal-token.ts
30
+ import { existsSync, mkdirSync, readFileSync, writeFileSync } from "fs";
31
+ import { randomBytes } from "crypto";
32
+ function mintBareInternalToken() {
33
+ if (existsSync(INTERNAL_TOKEN_FILE)) return readFileSync(INTERNAL_TOKEN_FILE, "utf8").trim();
34
+ mkdirSync(OS_DIR, { recursive: true, mode: 448 });
35
+ const token = randomBytes(32).toString("hex");
36
+ writeFileSync(INTERNAL_TOKEN_FILE, token, { mode: 384 });
37
+ return token;
38
+ }
39
+ function bareInternalToken() {
40
+ try {
41
+ return readFileSync(INTERNAL_TOKEN_FILE, "utf8").trim() || null;
42
+ } catch {
43
+ return null;
44
+ }
45
+ }
46
+ function internalTokenSources() {
47
+ const tokens = [];
48
+ const problems = [];
49
+ if (composeInstallExists()) {
50
+ const { env, unreadable } = readComposeEnvFile();
51
+ const composeToken = env.INTERNAL_TOKEN?.trim();
52
+ if (composeToken) {
53
+ tokens.push(composeToken);
54
+ } else if (unreadable) {
55
+ problems.push(
56
+ `can't read ${COMPOSE_ENV_FILE} (${unreadable}) \u2014 that file holds the running stack's token, so re-run this command with sudo`
57
+ );
58
+ } else {
59
+ problems.push(
60
+ `${COMPOSE_ENV_FILE} has no INTERNAL_TOKEN \u2014 re-run \`openship up\` to regenerate it`
61
+ );
62
+ }
63
+ }
64
+ const bare = bareInternalToken();
65
+ if (bare && !tokens.includes(bare)) tokens.push(bare);
66
+ return { tokens, problems };
67
+ }
68
+ function resolveInternalToken() {
69
+ return internalTokenSources().tokens[0] ?? null;
70
+ }
71
+ function internalTokenProblem() {
72
+ const { problems } = internalTokenSources();
73
+ if (problems.length) return problems.join("; ");
74
+ return `no internal token found on this machine (looked in ${COMPOSE_ENV_FILE} and ${INTERNAL_TOKEN_FILE}) \u2014 run \`openship up\` on this box first`;
75
+ }
76
+ function internalTokenRejectedProblem() {
77
+ const { tokens, problems } = internalTokenSources();
78
+ const plural = tokens.length > 1 ? `both of this machine's internal tokens` : `this machine's internal token`;
79
+ const unreadable = problems.length ? `${problems.join("; ")}. ` : "";
80
+ return `${unreadable}the API rejected ${plural}. It's running with a different one \u2014 restart it (\`openship up\`) so it picks up the current token, or check that this port belongs to this install`;
81
+ }
82
+
83
+ // src/lib/loopback-api.ts
84
+ function isInternalAuthRejection(body) {
85
+ try {
86
+ return JSON.parse(body).error === "Unauthorized";
87
+ } catch {
88
+ return false;
89
+ }
90
+ }
91
+ function replay(res, body) {
92
+ return new Response(body, { status: res.status, statusText: res.statusText, headers: res.headers });
93
+ }
94
+ async function internalFetch(port, path, init = {}, token) {
95
+ const candidates = token ? [token] : internalTokenSources().tokens;
96
+ if (candidates.length === 0) return { kind: "no-token", detail: internalTokenProblem() };
97
+ const url = `http://127.0.0.1:${port}${path}`;
98
+ let refused;
99
+ for (const candidate of candidates) {
100
+ let res;
101
+ try {
102
+ res = await fetch(url, {
103
+ ...init,
104
+ headers: {
105
+ ...init.headers,
106
+ "X-Internal-Token": candidate
107
+ }
108
+ });
109
+ } catch (err) {
110
+ return { kind: "unreachable", detail: err.message };
111
+ }
112
+ if (res.status !== 401) return { kind: "response", res };
113
+ const body = await res.text().catch(() => "");
114
+ if (!isInternalAuthRejection(body)) return { kind: "response", res: replay(res, body) };
115
+ refused = replay(res, body);
116
+ }
117
+ return { kind: "response", res: refused, tokenRejected: true };
118
+ }
119
+ async function internalGet(port, path, token) {
120
+ const call = await internalFetch(
121
+ port,
122
+ path,
123
+ { signal: AbortSignal.timeout(1e4) },
124
+ token
125
+ );
126
+ if (call.kind !== "response" || !call.res.ok) return null;
127
+ return await call.res.json().catch(() => null);
128
+ }
129
+ async function internalPost(port, path, body, token) {
130
+ const call = await internalFetch(
131
+ port,
132
+ path,
133
+ {
134
+ method: "POST",
135
+ headers: { "Content-Type": "application/json" },
136
+ body: JSON.stringify(body),
137
+ signal: AbortSignal.timeout(3e4)
138
+ },
139
+ token
140
+ );
141
+ if (call.kind !== "response") return { ok: false, data: { error: call.detail } };
142
+ const data = await call.res.json().catch(() => ({}));
143
+ if (call.tokenRejected && !token) {
144
+ return { ok: false, data: { ...data, error: internalTokenRejectedProblem() } };
145
+ }
146
+ return { ok: call.res.ok, data };
147
+ }
148
+ async function bootstrapAdmin(apiPort, admin, token) {
149
+ const { ok, data } = await internalPost(apiPort, "/api/system/bootstrap-admin", admin, token);
150
+ if (ok) return { ok: true };
151
+ if (data?.error === "An admin account already exists") return { ok: true, message: "already-exists" };
152
+ return { ok: false, message: data?.error || "failed" };
153
+ }
154
+ async function waitHealthy(apiPort, seconds = 90) {
155
+ for (let i = 0; i < seconds; i++) {
156
+ await new Promise((r) => setTimeout(r, 1e3));
157
+ try {
158
+ await fetch(`http://127.0.0.1:${apiPort}/api/health`, { signal: AbortSignal.timeout(2e3) });
159
+ return true;
160
+ } catch {
161
+ }
162
+ }
163
+ return false;
164
+ }
165
+ async function waitDashboard(dashPort, seconds = 45) {
166
+ for (let i = 0; i < seconds; i++) {
167
+ await new Promise((r) => setTimeout(r, 1e3));
168
+ try {
169
+ const res = await fetch(`http://127.0.0.1:${dashPort}/`, {
170
+ redirect: "manual",
171
+ signal: AbortSignal.timeout(2e3)
172
+ });
173
+ if (res.status > 0) return true;
174
+ } catch {
175
+ }
176
+ }
177
+ return false;
178
+ }
179
+ async function detectPublicIp() {
180
+ for (const url of ["https://api.ipify.org", "https://ifconfig.me/ip"]) {
181
+ try {
182
+ const res = await fetch(url, { signal: AbortSignal.timeout(3e3) });
183
+ if (!res.ok) continue;
184
+ const ip = (await res.text()).trim();
185
+ if (/^[0-9.]+$/.test(ip) || ip.includes(":")) return ip;
186
+ } catch {
187
+ }
188
+ }
189
+ return null;
190
+ }
191
+
192
+ // src/lib/edge-import.ts
193
+ async function waitForApiHealth(port, tries) {
194
+ for (let i = 0; i < tries; i++) {
195
+ try {
196
+ const r = await fetch(`http://127.0.0.1:${port}/api/health`, { signal: AbortSignal.timeout(2e3) });
197
+ if (r.ok) return true;
198
+ } catch {
199
+ }
200
+ await new Promise((r) => setTimeout(r, 1e3));
201
+ }
202
+ return false;
203
+ }
204
+ async function waitForEdgeRunning(tries) {
205
+ const exec = new LocalExecutor();
206
+ for (let i = 0; i < tries; i++) {
207
+ const out = await exec.exec(`docker inspect -f '{{.State.Running}}' ${EDGE_CONTAINER_NAME}`).catch(() => "");
208
+ if (out.trim() === "true") return true;
209
+ await new Promise((r) => setTimeout(r, 1e3));
210
+ }
211
+ return false;
212
+ }
213
+ async function importMigratedSites(apiPort, sites, certPems, staticRootOverrides) {
214
+ if (!resolveInternalToken()) {
215
+ const error = internalTokenProblem();
216
+ console.log(chalk.yellow(` Skipping site import \u2014 ${error}. Re-run to retry.
217
+ `));
218
+ return { ok: false, registered: [], error };
219
+ }
220
+ const spinner = ora(
221
+ `Importing ${sites.length} migrated site${sites.length === 1 ? "" : "s"} into the edge\u2026`
222
+ ).start();
223
+ if (!await waitForApiHealth(apiPort, 60)) {
224
+ const error = "API didn't become healthy in time";
225
+ spinner.warn(`${error} \u2014 migrated sites not imported. Re-run \`openship up\` to retry.`);
226
+ return { ok: false, registered: [], error };
227
+ }
228
+ if (!await waitForEdgeRunning(60)) {
229
+ const error = `the ${EDGE_CONTAINER_NAME} container isn't running yet`;
230
+ spinner.warn(`${error} \u2014 migrated sites not imported. Re-run \`openship up\` to retry.`);
231
+ return { ok: false, registered: [], error };
232
+ }
233
+ try {
234
+ const call = await internalFetch(apiPort, "/api/system/edge/import-sites", {
235
+ method: "POST",
236
+ headers: { "Content-Type": "application/json" },
237
+ body: JSON.stringify({ sites, certPems, staticRootOverrides }),
238
+ signal: AbortSignal.timeout(12e4)
239
+ });
240
+ if (call.kind !== "response") {
241
+ spinner.warn(`Site import failed: ${call.detail}. Re-run to retry.`);
242
+ return { ok: false, registered: [], error: call.detail };
243
+ }
244
+ const r = call.res;
245
+ const data = await r.json().catch(() => ({}));
246
+ if (!r.ok) {
247
+ const error = data.error ?? `HTTP ${r.status}`;
248
+ spinner.warn(`Site import failed: ${error}. Re-run to retry.`);
249
+ return { ok: false, registered: [], error };
250
+ }
251
+ const registered = data.registered ?? [];
252
+ const warnings = data.warnings ?? [];
253
+ const missed = sites.length - registered.length;
254
+ if (sites.length > 0 && registered.length === 0) {
255
+ spinner.fail(
256
+ `Migrated NONE of the ${sites.length} site${sites.length === 1 ? "" : "s"} \u2014 Openship holds :80/:443 and ${sites.length === 1 ? "that hostname is" : "those hostnames are"} NOT being served.`
257
+ );
258
+ for (const w of warnings.slice(0, 8)) console.log(chalk.red(` \u2022 ${w}`));
259
+ const edgeLog = await edgeCrashReason(new LocalExecutor());
260
+ if (edgeLog) {
261
+ console.log(chalk.red(`
262
+ The edge container is not running. Its log says:`));
263
+ console.log(chalk.red(` ${edgeLog}`));
264
+ }
265
+ const back = startUnitHint("nginx");
266
+ console.log(
267
+ chalk.yellow(
268
+ `
269
+ Retry the import with \`openship up\` once the cause above is fixed, or put your
270
+ ` + (back ? ` previous proxy back: docker stop ${EDGE_CONTAINER_NAME} && ${back}
271
+ ` : ` previous proxy back by stopping ${EDGE_CONTAINER_NAME} and starting it the way
272
+ this host starts services.
273
+ `)
274
+ )
275
+ );
276
+ return { ok: false, registered, error: warnings[0] ?? "no sites were registered" };
277
+ }
278
+ if (missed > 0) {
279
+ spinner.warn(
280
+ `Migrated ${registered.length}/${sites.length} sites \u2014 ${missed} not served (see below).`
281
+ );
282
+ for (const w of warnings.slice(0, 8)) console.log(chalk.yellow(` \u2022 ${w}`));
283
+ return { ok: false, registered, error: `${missed} site(s) not registered` };
284
+ }
285
+ spinner.succeed(
286
+ `Migrated ${registered.length} site${registered.length === 1 ? "" : "s"} into Openship's edge.`
287
+ );
288
+ for (const w of warnings.slice(0, 8)) console.log(chalk.dim(` \u2022 ${w}`));
289
+ return { ok: true, registered };
290
+ } catch (e) {
291
+ const error = e.message;
292
+ spinner.warn(`Site import failed: ${error}. Re-run to retry.`);
293
+ return { ok: false, registered: [], error };
294
+ }
295
+ }
296
+
297
+ export {
298
+ mintBareInternalToken,
299
+ internalTokenRejectedProblem,
300
+ internalFetch,
301
+ internalGet,
302
+ internalPost,
303
+ bootstrapAdmin,
304
+ waitHealthy,
305
+ waitDashboard,
306
+ detectPublicIp,
307
+ waitForApiHealth,
308
+ waitForEdgeRunning,
309
+ importMigratedSites
310
+ };