openship 0.6.7 → 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-LHFXSC36.js → chunk-3MP6ACKM.js} +183 -87
- package/dist/{chunk-MEE6OZFP.js → chunk-5DJOB7IX.js} +3 -3
- package/dist/{chunk-PPEBIY5F.js → chunk-5I3COAFH.js} +35 -12
- package/dist/{chunk-VQM6JHJB.js → chunk-5UXHT6ED.js} +9 -9
- package/dist/{chunk-XWHWTHEL.js → chunk-6GU6JSK6.js} +21 -2
- package/dist/{chunk-QHMDCYZ3.js → chunk-7PEXM2UI.js} +213 -45
- package/dist/{chunk-RDXT4BQB.js → chunk-7YBWXEM6.js} +4 -4
- package/dist/{chunk-H4FS5BED.js → chunk-AHJYP7DG.js} +13 -11
- package/dist/{chunk-VSTFHUCV.js → chunk-BO6CY4V2.js} +109 -23
- package/dist/{chunk-J3K6XABC.js → chunk-D3OEMXYP.js} +3 -3
- package/dist/{chunk-UWEV6XTS.js → chunk-D6HU4VUZ.js} +1 -1
- package/dist/{chunk-S6B2Z3OO.js → chunk-ENKJ7XIF.js} +21 -11
- package/dist/{chunk-BLEXTYRK.js → chunk-GNOMEMNI.js} +1 -1
- package/dist/{chunk-NRTJRNAE.js → chunk-IAMOUDHY.js} +138 -54
- package/dist/{chunk-CIJV43LH.js → chunk-MUWVC5R4.js} +161 -39
- package/dist/{chunk-ZPJ74LXE.js → chunk-NIYD7MD2.js} +197 -45
- package/dist/{chunk-FZ6QCF6B.js → chunk-NPSRGIHV.js} +2 -2
- package/dist/{chunk-2GWYYHOR.js → chunk-NRU2EBYZ.js} +2 -2
- package/dist/{chunk-J2XM3QSP.js → chunk-NUT6HKK6.js} +5 -5
- package/dist/{chunk-H3WT3A6R.js → chunk-P7RCYELG.js} +1 -1
- package/dist/{chunk-FYPFEDPZ.js → chunk-R5DC5PEF.js} +1 -1
- package/dist/{chunk-DDFUESPJ.js → chunk-RCMERVBL.js} +9 -9
- package/dist/{chunk-RXQBD4ZY.js → chunk-RHLNVZMS.js} +2 -2
- package/dist/{chunk-WU54BXFE.js → chunk-SK4FFCFR.js} +1 -1
- package/dist/{chunk-PVP4RGWT.js → chunk-T46BQOLY.js} +3 -3
- package/dist/{chunk-E2X26PXR.js → chunk-T6DVUX4F.js} +1 -1
- package/dist/{chunk-5T4UVOM3.js → chunk-TAZZZKYO.js} +1 -1
- package/dist/{chunk-GGIAIGUL.js → chunk-UAWY6X7V.js} +34 -12
- package/dist/{chunk-2ANV5ZQ5.js → chunk-VAI5VOOI.js} +1 -1
- package/dist/{chunk-2VSW6Q6C.js → chunk-WEZFTL4V.js} +655 -62
- package/dist/{chunk-GIPJNNI5.js → chunk-X53JXRS4.js} +3 -3
- package/dist/{cloud-KRTZ2IL5.js → cloud-2CRDKOWQ.js} +1 -1
- package/dist/cloud-REV3DPJH.js +42 -0
- package/dist/{detect-G4DR3U2N.js → detect-BBFT5GEU.js} +4 -4
- package/dist/{docker-YOVPCIB4.js → docker-EG3QSE6I.js} +10 -10
- package/dist/{docker-edge-executor-QFXD3337.js → docker-edge-executor-E5TOTLMR.js} +7 -7
- package/dist/edge-import-YJVJUGYC.js +56 -0
- package/dist/{edge-preflight-MXERVAMG.js → edge-preflight-QUW4BM7N.js} +13 -13
- package/dist/{ensure-container-edge-F4JUNQIK.js → ensure-container-edge-FOEFQ3PI.js} +13 -11
- package/dist/{executor-CVPAYYHR.js → executor-NTWDJ3DB.js} +6 -6
- package/dist/host-channel-preflight-OMCZMMKR.js +54 -0
- package/dist/index.js +158 -72
- package/dist/{local-executor-ODC6RANU.js → local-executor-OQ2GRN7K.js} +3 -3
- package/dist/{nginx-KLFF43TC.js → nginx-4U4C2XUS.js} +9 -9
- package/dist/{noop-NJ72F46T.js → noop-CLLJHCPL.js} +1 -1
- package/dist/{openresty-lua-VKWUV4GB.js → openresty-lua-LLWT5XQW.js} +7 -5
- package/dist/{privilege-7D3SSOL3.js → privilege-MNDVMDST.js} +5 -5
- package/dist/server/index.js +31643 -25553
- 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-I5AJC6NJ.js → setup-IFIMPQZJ.js} +12 -12
- package/dist/{ssh-client-625VWGKP.js → ssh-client-2XWZCMGI.js} +2 -2
- package/dist/up-HQS7IR7H.js +61 -0
- package/package.json +1 -1
- package/dist/bare-7QD623QV.js +0 -29
- package/dist/cloud-KNNABE3O.js +0 -40
- package/dist/edge-import-CRFVSR6P.js +0 -56
- package/dist/host-channel-preflight-QTDNGLCK.js +0 -54
- package/dist/up-QXTOOVXC.js +0 -61
|
@@ -1017,6 +1017,65 @@ function ownsNetworkEndpoint(resolvedNetworkMode) {
|
|
|
1017
1017
|
return !resolvedNetworkMode;
|
|
1018
1018
|
}
|
|
1019
1019
|
|
|
1020
|
+
// ../../packages/core/src/utils.ts
|
|
1021
|
+
var REDIRECT_STATUSES = [301, 302, 307, 308];
|
|
1022
|
+
var DEFAULT_REDIRECT_STATUS = 301;
|
|
1023
|
+
function resolveRedirectStatus(status) {
|
|
1024
|
+
return REDIRECT_STATUSES.includes(status) ? status : DEFAULT_REDIRECT_STATUS;
|
|
1025
|
+
}
|
|
1026
|
+
function isValidEmail(value) {
|
|
1027
|
+
const email3 = value?.trim() ?? "";
|
|
1028
|
+
if (!email3 || email3.length > 254) return false;
|
|
1029
|
+
return /^[A-Za-z0-9._%+-]+@[A-Za-z0-9-]+(\.[A-Za-z0-9-]+)*\.[A-Za-z]{2,}$/.test(email3);
|
|
1030
|
+
}
|
|
1031
|
+
function formatBytes(bytes, decimals = 2) {
|
|
1032
|
+
if (bytes <= 0) return "0 B";
|
|
1033
|
+
const k = 1024;
|
|
1034
|
+
const sizes = ["B", "KB", "MB", "GB", "TB"];
|
|
1035
|
+
const i = Math.min(Math.max(Math.floor(Math.log(bytes) / Math.log(k)), 0), sizes.length - 1);
|
|
1036
|
+
return `${parseFloat((bytes / Math.pow(k, i)).toFixed(decimals))} ${sizes[i]}`;
|
|
1037
|
+
}
|
|
1038
|
+
function withTimeout(promise2, ms, message) {
|
|
1039
|
+
if (!ms) return promise2;
|
|
1040
|
+
return new Promise((resolve, reject) => {
|
|
1041
|
+
const timer = setTimeout(() => reject(new Error(message)), ms);
|
|
1042
|
+
promise2.then(
|
|
1043
|
+
(v) => {
|
|
1044
|
+
clearTimeout(timer);
|
|
1045
|
+
resolve(v);
|
|
1046
|
+
},
|
|
1047
|
+
(e) => {
|
|
1048
|
+
clearTimeout(timer);
|
|
1049
|
+
reject(e);
|
|
1050
|
+
}
|
|
1051
|
+
);
|
|
1052
|
+
});
|
|
1053
|
+
}
|
|
1054
|
+
function isValidEnvKey(key) {
|
|
1055
|
+
return /^[A-Za-z_][A-Za-z0-9_]*$/.test(key);
|
|
1056
|
+
}
|
|
1057
|
+
function wrapText(text, width = 88) {
|
|
1058
|
+
const out = [];
|
|
1059
|
+
for (const paragraph of text.split("\n")) {
|
|
1060
|
+
const words = paragraph.split(/\s+/).filter(Boolean);
|
|
1061
|
+
if (words.length === 0) {
|
|
1062
|
+
out.push("");
|
|
1063
|
+
continue;
|
|
1064
|
+
}
|
|
1065
|
+
let line = "";
|
|
1066
|
+
for (const word of words) {
|
|
1067
|
+
if (line && line.length + 1 + word.length > width) {
|
|
1068
|
+
out.push(line);
|
|
1069
|
+
line = word;
|
|
1070
|
+
} else {
|
|
1071
|
+
line = line ? `${line} ${word}` : word;
|
|
1072
|
+
}
|
|
1073
|
+
}
|
|
1074
|
+
if (line) out.push(line);
|
|
1075
|
+
}
|
|
1076
|
+
return out;
|
|
1077
|
+
}
|
|
1078
|
+
|
|
1020
1079
|
// ../../packages/core/src/constants.ts
|
|
1021
1080
|
var DEFAULT_IMAGE_REGISTRY = "ghcr.io/oblien";
|
|
1022
1081
|
|
|
@@ -1199,62 +1258,6 @@ var SYSTEM = {
|
|
|
1199
1258
|
}
|
|
1200
1259
|
};
|
|
1201
1260
|
|
|
1202
|
-
// ../../packages/core/src/utils.ts
|
|
1203
|
-
var REDIRECT_STATUSES = [301, 302, 307, 308];
|
|
1204
|
-
var DEFAULT_REDIRECT_STATUS = 301;
|
|
1205
|
-
function resolveRedirectStatus(status) {
|
|
1206
|
-
return REDIRECT_STATUSES.includes(status) ? status : DEFAULT_REDIRECT_STATUS;
|
|
1207
|
-
}
|
|
1208
|
-
function isValidEmail(value) {
|
|
1209
|
-
const email3 = value?.trim() ?? "";
|
|
1210
|
-
if (!email3 || email3.length > 254) return false;
|
|
1211
|
-
return /^[A-Za-z0-9._%+-]+@[A-Za-z0-9-]+(\.[A-Za-z0-9-]+)*\.[A-Za-z]{2,}$/.test(email3);
|
|
1212
|
-
}
|
|
1213
|
-
function formatBytes(bytes, decimals = 2) {
|
|
1214
|
-
if (bytes <= 0) return "0 B";
|
|
1215
|
-
const k = 1024;
|
|
1216
|
-
const sizes = ["B", "KB", "MB", "GB", "TB"];
|
|
1217
|
-
const i = Math.min(Math.max(Math.floor(Math.log(bytes) / Math.log(k)), 0), sizes.length - 1);
|
|
1218
|
-
return `${parseFloat((bytes / Math.pow(k, i)).toFixed(decimals))} ${sizes[i]}`;
|
|
1219
|
-
}
|
|
1220
|
-
function withTimeout(promise2, ms, message) {
|
|
1221
|
-
if (!ms) return promise2;
|
|
1222
|
-
return new Promise((resolve, reject) => {
|
|
1223
|
-
const timer = setTimeout(() => reject(new Error(message)), ms);
|
|
1224
|
-
promise2.then(
|
|
1225
|
-
(v) => {
|
|
1226
|
-
clearTimeout(timer);
|
|
1227
|
-
resolve(v);
|
|
1228
|
-
},
|
|
1229
|
-
(e) => {
|
|
1230
|
-
clearTimeout(timer);
|
|
1231
|
-
reject(e);
|
|
1232
|
-
}
|
|
1233
|
-
);
|
|
1234
|
-
});
|
|
1235
|
-
}
|
|
1236
|
-
function wrapText(text, width = 88) {
|
|
1237
|
-
const out = [];
|
|
1238
|
-
for (const paragraph of text.split("\n")) {
|
|
1239
|
-
const words = paragraph.split(/\s+/).filter(Boolean);
|
|
1240
|
-
if (words.length === 0) {
|
|
1241
|
-
out.push("");
|
|
1242
|
-
continue;
|
|
1243
|
-
}
|
|
1244
|
-
let line = "";
|
|
1245
|
-
for (const word of words) {
|
|
1246
|
-
if (line && line.length + 1 + word.length > width) {
|
|
1247
|
-
out.push(line);
|
|
1248
|
-
line = word;
|
|
1249
|
-
} else {
|
|
1250
|
-
line = line ? `${line} ${word}` : word;
|
|
1251
|
-
}
|
|
1252
|
-
}
|
|
1253
|
-
if (line) out.push(line);
|
|
1254
|
-
}
|
|
1255
|
-
return out;
|
|
1256
|
-
}
|
|
1257
|
-
|
|
1258
1261
|
// ../../packages/core/src/errors.ts
|
|
1259
1262
|
var AppError = class extends Error {
|
|
1260
1263
|
constructor(message, statusCode = 500, code) {
|
|
@@ -2548,6 +2551,104 @@ function hostFirewallRuleRemoval(firewall, cidrs, port) {
|
|
|
2548
2551
|
|
|
2549
2552
|
// ../../packages/core/src/compose-spec.ts
|
|
2550
2553
|
var asIs = (s) => s;
|
|
2554
|
+
var REMOTE_BUILD_CONTEXT_RE = /^(?:[A-Za-z][A-Za-z0-9+.-]*:\/\/|[^@\s]+@[^:\s]+:)/;
|
|
2555
|
+
var ABSOLUTE_BUILD_CONTEXT_RE = /^(?:[\\/]|~(?:[\\/]|$)|[A-Za-z]:[\\/]|\\\\)/;
|
|
2556
|
+
function composeOptionRequestsSomething(value) {
|
|
2557
|
+
if (value === void 0 || value === null || value === false || value === "" || value === 0) {
|
|
2558
|
+
return false;
|
|
2559
|
+
}
|
|
2560
|
+
if (Array.isArray(value)) return value.length > 0;
|
|
2561
|
+
if (typeof value === "object") return Object.keys(value).length > 0;
|
|
2562
|
+
return true;
|
|
2563
|
+
}
|
|
2564
|
+
function composeBuildIssues(build, options = {}) {
|
|
2565
|
+
if (build === void 0 || build === null) return [];
|
|
2566
|
+
const issues = [];
|
|
2567
|
+
const block = (field, reason) => issues.push({ field, reason, blocking: true });
|
|
2568
|
+
const validateContext = (context) => {
|
|
2569
|
+
const resolved = (options.interpolate?.(context) ?? context).trim();
|
|
2570
|
+
if (!resolved) {
|
|
2571
|
+
block("build.context", "build.context must not be empty.");
|
|
2572
|
+
} else if (REMOTE_BUILD_CONTEXT_RE.test(resolved)) {
|
|
2573
|
+
block(
|
|
2574
|
+
"build.context",
|
|
2575
|
+
"build.context uses a remote URL, which Openship cannot build as a repository-relative context."
|
|
2576
|
+
);
|
|
2577
|
+
} else if (!options.allowAbsoluteContext && ABSOLUTE_BUILD_CONTEXT_RE.test(resolved)) {
|
|
2578
|
+
block(
|
|
2579
|
+
"build.context",
|
|
2580
|
+
"build.context is absolute or home-relative; Openship requires a path inside the linked repository."
|
|
2581
|
+
);
|
|
2582
|
+
}
|
|
2583
|
+
};
|
|
2584
|
+
if (typeof build === "string") {
|
|
2585
|
+
validateContext(build);
|
|
2586
|
+
return issues;
|
|
2587
|
+
}
|
|
2588
|
+
if (typeof build !== "object" || Array.isArray(build)) {
|
|
2589
|
+
block("build", "build must be a context string or an object.");
|
|
2590
|
+
return issues;
|
|
2591
|
+
}
|
|
2592
|
+
const value = build;
|
|
2593
|
+
if (value.context !== void 0) {
|
|
2594
|
+
if (typeof value.context !== "string") {
|
|
2595
|
+
block("build.context", "build.context must be a string.");
|
|
2596
|
+
} else {
|
|
2597
|
+
validateContext(value.context);
|
|
2598
|
+
}
|
|
2599
|
+
}
|
|
2600
|
+
if (value.dockerfile !== void 0) {
|
|
2601
|
+
if (typeof value.dockerfile !== "string") {
|
|
2602
|
+
block("build.dockerfile", "build.dockerfile must be a string.");
|
|
2603
|
+
} else {
|
|
2604
|
+
const dockerfile = (options.interpolate?.(value.dockerfile) ?? value.dockerfile).trim();
|
|
2605
|
+
if (!dockerfile) {
|
|
2606
|
+
block("build.dockerfile", "build.dockerfile must not be empty.");
|
|
2607
|
+
} else if (REMOTE_BUILD_CONTEXT_RE.test(dockerfile) || ABSOLUTE_BUILD_CONTEXT_RE.test(dockerfile) || dockerfile.split(/[\\/]/).includes("..")) {
|
|
2608
|
+
block("build.dockerfile", "build.dockerfile must be a path inside its build context.");
|
|
2609
|
+
}
|
|
2610
|
+
}
|
|
2611
|
+
}
|
|
2612
|
+
const args = value.args;
|
|
2613
|
+
if (args !== void 0) {
|
|
2614
|
+
if (Array.isArray(args)) {
|
|
2615
|
+
args.forEach((entry, index) => {
|
|
2616
|
+
if (typeof entry !== "string") {
|
|
2617
|
+
block(`build.args[${index}]`, "build.args list entries must be strings.");
|
|
2618
|
+
return;
|
|
2619
|
+
}
|
|
2620
|
+
const resolvedEntry = options.interpolate?.(entry) ?? entry;
|
|
2621
|
+
const equals = resolvedEntry.indexOf("=");
|
|
2622
|
+
const key = (equals >= 0 ? resolvedEntry.slice(0, equals) : resolvedEntry).trim();
|
|
2623
|
+
if (!isValidEnvKey(key)) {
|
|
2624
|
+
block(`build.args[${index}]`, "build.args contains an invalid argument name.");
|
|
2625
|
+
}
|
|
2626
|
+
});
|
|
2627
|
+
} else if (args && typeof args === "object") {
|
|
2628
|
+
for (const [key, argValue] of Object.entries(args)) {
|
|
2629
|
+
if (!isValidEnvKey(key)) {
|
|
2630
|
+
block("build.args", "build.args contains an invalid argument name.");
|
|
2631
|
+
}
|
|
2632
|
+
if (argValue !== null && argValue !== void 0 && !["string", "number", "boolean"].includes(typeof argValue)) {
|
|
2633
|
+
block(`build.args.${key}`, "build.args values must be scalar or null.");
|
|
2634
|
+
}
|
|
2635
|
+
}
|
|
2636
|
+
} else {
|
|
2637
|
+
block("build.args", "build.args must be a list of strings or a scalar/null map.");
|
|
2638
|
+
}
|
|
2639
|
+
}
|
|
2640
|
+
const modeled = /* @__PURE__ */ new Set(["context", "dockerfile", "args"]);
|
|
2641
|
+
for (const [key, optionValue] of Object.entries(value)) {
|
|
2642
|
+
if (modeled.has(key) || /^x-/i.test(key) || !composeOptionRequestsSomething(optionValue)) {
|
|
2643
|
+
continue;
|
|
2644
|
+
}
|
|
2645
|
+
block(
|
|
2646
|
+
`build.${key}`,
|
|
2647
|
+
`build.${key} is not supported; ignoring it could change the produced image or omit requested build behavior.`
|
|
2648
|
+
);
|
|
2649
|
+
}
|
|
2650
|
+
return issues;
|
|
2651
|
+
}
|
|
2551
2652
|
function composeMountToSpec(mount, interpolate = asIs) {
|
|
2552
2653
|
const source = mount.source ?? mount.name;
|
|
2553
2654
|
const target = mount.target;
|
|
@@ -6102,6 +6203,7 @@ COMMIT;
|
|
|
6102
6203
|
{
|
|
6103
6204
|
available: true,
|
|
6104
6205
|
verified: true,
|
|
6206
|
+
minEngine: "0.6.6",
|
|
6105
6207
|
id: "minio",
|
|
6106
6208
|
name: "MinIO",
|
|
6107
6209
|
description: "S3-compatible object storage with a web console \u2014 point any S3 client (aws-cli, SDKs) at it, or bind a bucket straight into another project's uploads.",
|
|
@@ -6596,6 +6698,7 @@ COMMIT;
|
|
|
6596
6698
|
{
|
|
6597
6699
|
available: true,
|
|
6598
6700
|
verified: false,
|
|
6701
|
+
minEngine: "0.6.6",
|
|
6599
6702
|
minResources: {
|
|
6600
6703
|
memoryMb: 4096,
|
|
6601
6704
|
cpuCores: 2
|
|
@@ -6777,6 +6880,7 @@ Two things worth knowing, both upstream behaviour: if the console shows a branch
|
|
|
6777
6880
|
{
|
|
6778
6881
|
available: true,
|
|
6779
6882
|
verified: false,
|
|
6883
|
+
minEngine: "0.6.6",
|
|
6780
6884
|
minResources: {
|
|
6781
6885
|
memoryMb: 16384,
|
|
6782
6886
|
cpuCores: 4
|
|
@@ -7599,6 +7703,7 @@ Two things worth knowing, both upstream behaviour: if the console shows a branch
|
|
|
7599
7703
|
{
|
|
7600
7704
|
available: true,
|
|
7601
7705
|
verified: false,
|
|
7706
|
+
minEngine: "0.6.6",
|
|
7602
7707
|
id: "redis",
|
|
7603
7708
|
name: "Valkey (Redis)",
|
|
7604
7709
|
description: "In-memory data store for caching, sessions, rate limits, and queues \u2014 Valkey, the open-source Redis fork. Drop-in Redis-compatible: point any redis client at it. Ships with RedisInsight, a browser UI that arrives already connected to this instance.",
|
|
@@ -8077,6 +8182,7 @@ Two things worth knowing, both upstream behaviour: if the console shows a branch
|
|
|
8077
8182
|
{
|
|
8078
8183
|
available: true,
|
|
8079
8184
|
verified: false,
|
|
8185
|
+
minEngine: "0.6.6",
|
|
8080
8186
|
id: "clickhouse",
|
|
8081
8187
|
name: "ClickHouse",
|
|
8082
8188
|
description: "The columnar SQL database for analytics \u2014 billions of rows scanned per second. Ships with the CH-UI console, so you get a SQL editor, schema browser and dashboards the moment it installs: sign in with the ClickHouse user and password below, no extra setup.",
|
|
@@ -8343,6 +8449,178 @@ Two things worth knowing, both upstream behaviour: if the console shows a branch
|
|
|
8343
8449
|
]
|
|
8344
8450
|
}
|
|
8345
8451
|
]
|
|
8452
|
+
},
|
|
8453
|
+
{
|
|
8454
|
+
available: true,
|
|
8455
|
+
verified: true,
|
|
8456
|
+
id: "mindwire",
|
|
8457
|
+
name: "MindWire",
|
|
8458
|
+
description: "Run coding-agent runtimes behind a self-hosted control plane. MindWire keeps the runtime private and gives you one console to manage agents, credentials, and sessions.",
|
|
8459
|
+
repository: "https://github.com/oblien/mindwire",
|
|
8460
|
+
kind: "template",
|
|
8461
|
+
logo: "mindwire",
|
|
8462
|
+
category: "automation",
|
|
8463
|
+
tags: [
|
|
8464
|
+
"ai",
|
|
8465
|
+
"agents",
|
|
8466
|
+
"coding",
|
|
8467
|
+
"control-plane"
|
|
8468
|
+
],
|
|
8469
|
+
framework: "docker-compose",
|
|
8470
|
+
services: [
|
|
8471
|
+
{
|
|
8472
|
+
name: "runtime",
|
|
8473
|
+
image: "ghcr.io/oblien/mindwire-runtime:0.1.3",
|
|
8474
|
+
environment: {
|
|
8475
|
+
ADDR: "0.0.0.0:8790",
|
|
8476
|
+
AGENT_TYPE: "claude-code",
|
|
8477
|
+
DAEMON_TOKEN: "{{config:MINDWIRE_RUNTIME_TOKEN}}"
|
|
8478
|
+
},
|
|
8479
|
+
secretEnv: [
|
|
8480
|
+
"DAEMON_TOKEN"
|
|
8481
|
+
],
|
|
8482
|
+
volumes: [
|
|
8483
|
+
"mindwire_runtime:/home/node"
|
|
8484
|
+
],
|
|
8485
|
+
restart: "unless-stopped"
|
|
8486
|
+
},
|
|
8487
|
+
{
|
|
8488
|
+
name: "console",
|
|
8489
|
+
image: "ghcr.io/oblien/mindwire-console:0.1.3",
|
|
8490
|
+
exposedPort: 8787,
|
|
8491
|
+
exposed: true,
|
|
8492
|
+
routes: [
|
|
8493
|
+
{
|
|
8494
|
+
port: 8787
|
|
8495
|
+
}
|
|
8496
|
+
],
|
|
8497
|
+
dependsOn: [
|
|
8498
|
+
"runtime"
|
|
8499
|
+
],
|
|
8500
|
+
environment: {
|
|
8501
|
+
NODE_ENV: "production",
|
|
8502
|
+
PORT: "8787",
|
|
8503
|
+
BASE_URL: "{{publicUrl:console}}",
|
|
8504
|
+
AUTH_DB_PATH: "/data/auth.db",
|
|
8505
|
+
CONSOLE_MODE: "self-hosted",
|
|
8506
|
+
CONSOLE_USERNAME: "{{config:CONSOLE_USERNAME}}",
|
|
8507
|
+
CONSOLE_PASSWORD: "{{config:CONSOLE_PASSWORD}}",
|
|
8508
|
+
AUTH_SECRET: "{{config:AUTH_SECRET}}",
|
|
8509
|
+
SECRETS_ENCRYPTION_KEY: "{{config:SECRETS_ENCRYPTION_KEY}}",
|
|
8510
|
+
ALLOW_LOCAL_RUNTIME: "true",
|
|
8511
|
+
ALLOW_SSH_RUNTIME: "true",
|
|
8512
|
+
ALLOW_DOCKER_RUNTIME: "true",
|
|
8513
|
+
ALLOW_REMOTE_RUNTIME: "true",
|
|
8514
|
+
DAEMON_URL: "http://runtime:8790",
|
|
8515
|
+
MINDWIRE_RUNTIME_TOKEN: "{{config:MINDWIRE_RUNTIME_TOKEN}}",
|
|
8516
|
+
SEED_DEFAULT_DAEMON: "true"
|
|
8517
|
+
},
|
|
8518
|
+
secretEnv: [
|
|
8519
|
+
"CONSOLE_PASSWORD",
|
|
8520
|
+
"AUTH_SECRET",
|
|
8521
|
+
"SECRETS_ENCRYPTION_KEY",
|
|
8522
|
+
"MINDWIRE_RUNTIME_TOKEN"
|
|
8523
|
+
],
|
|
8524
|
+
volumes: [
|
|
8525
|
+
"mindwire_console:/data"
|
|
8526
|
+
],
|
|
8527
|
+
healthcheck: {
|
|
8528
|
+
test: [
|
|
8529
|
+
"CMD-SHELL",
|
|
8530
|
+
`node -e "fetch('http://127.0.0.1:8787/api/ping').then(r => process.exit(r.ok ? 0 : 1)).catch(() => process.exit(1))"`
|
|
8531
|
+
],
|
|
8532
|
+
interval: "10s",
|
|
8533
|
+
timeout: "5s",
|
|
8534
|
+
retries: 12,
|
|
8535
|
+
startPeriod: "20s"
|
|
8536
|
+
},
|
|
8537
|
+
restart: "unless-stopped"
|
|
8538
|
+
}
|
|
8539
|
+
],
|
|
8540
|
+
configFields: [
|
|
8541
|
+
{
|
|
8542
|
+
key: "CONSOLE_USERNAME",
|
|
8543
|
+
service: "console",
|
|
8544
|
+
label: "Console username",
|
|
8545
|
+
help: "The single owner account for this self-hosted console.",
|
|
8546
|
+
default: "admin",
|
|
8547
|
+
required: true
|
|
8548
|
+
},
|
|
8549
|
+
{
|
|
8550
|
+
key: "CONSOLE_PASSWORD",
|
|
8551
|
+
service: "console",
|
|
8552
|
+
label: "Console password",
|
|
8553
|
+
help: "Auto-generated. Use this with the owner username to sign in.",
|
|
8554
|
+
type: "password",
|
|
8555
|
+
generate: "secret",
|
|
8556
|
+
secret: true
|
|
8557
|
+
},
|
|
8558
|
+
{
|
|
8559
|
+
key: "AUTH_SECRET",
|
|
8560
|
+
service: "console",
|
|
8561
|
+
label: "Session signing secret",
|
|
8562
|
+
help: "Auto-generated. Keep it stable so existing sign-in sessions remain valid after upgrades.",
|
|
8563
|
+
generate: "secret",
|
|
8564
|
+
secret: true
|
|
8565
|
+
},
|
|
8566
|
+
{
|
|
8567
|
+
key: "SECRETS_ENCRYPTION_KEY",
|
|
8568
|
+
service: "console",
|
|
8569
|
+
label: "Credential vault key",
|
|
8570
|
+
help: "Auto-generated 32-byte key used to encrypt provider and runtime credentials at rest.",
|
|
8571
|
+
generate: "secret",
|
|
8572
|
+
secret: true
|
|
8573
|
+
},
|
|
8574
|
+
{
|
|
8575
|
+
key: "MINDWIRE_RUNTIME_TOKEN",
|
|
8576
|
+
service: "runtime",
|
|
8577
|
+
label: "Runtime bearer token",
|
|
8578
|
+
help: "Auto-generated. Authenticates the Console to the private MindWire runtime.",
|
|
8579
|
+
generate: "secret",
|
|
8580
|
+
secret: true
|
|
8581
|
+
}
|
|
8582
|
+
],
|
|
8583
|
+
management: {
|
|
8584
|
+
kind: "schema"
|
|
8585
|
+
},
|
|
8586
|
+
connection: {
|
|
8587
|
+
title: "Sign in to MindWire",
|
|
8588
|
+
description: "Open the Console and sign in with the generated owner credentials. The runtime stays private to this deployment.",
|
|
8589
|
+
guide: {
|
|
8590
|
+
intro: "MindWire gives you a production control plane for coding-agent runtimes without exposing the runtime API to the public internet.",
|
|
8591
|
+
defaultMode: "public"
|
|
8592
|
+
},
|
|
8593
|
+
outputs: [
|
|
8594
|
+
{
|
|
8595
|
+
id: "console",
|
|
8596
|
+
label: "MindWire Console",
|
|
8597
|
+
source: "publicUrl:console",
|
|
8598
|
+
kind: "url",
|
|
8599
|
+
recommended: true,
|
|
8600
|
+
help: "Open the control plane in your browser."
|
|
8601
|
+
},
|
|
8602
|
+
{
|
|
8603
|
+
id: "username",
|
|
8604
|
+
label: "Owner username",
|
|
8605
|
+
source: "env:console:CONSOLE_USERNAME"
|
|
8606
|
+
},
|
|
8607
|
+
{
|
|
8608
|
+
id: "password",
|
|
8609
|
+
label: "Owner password",
|
|
8610
|
+
source: "env:console:CONSOLE_PASSWORD",
|
|
8611
|
+
secret: true
|
|
8612
|
+
}
|
|
8613
|
+
]
|
|
8614
|
+
},
|
|
8615
|
+
endpoints: [
|
|
8616
|
+
{
|
|
8617
|
+
service: "console",
|
|
8618
|
+
port: 8787,
|
|
8619
|
+
label: "MindWire Console",
|
|
8620
|
+
kind: "http",
|
|
8621
|
+
defaultMode: "domain"
|
|
8622
|
+
}
|
|
8623
|
+
]
|
|
8346
8624
|
}
|
|
8347
8625
|
]
|
|
8348
8626
|
};
|
|
@@ -24641,6 +24919,240 @@ var ANNUAL_ENABLED = PRICING.annual.enabled;
|
|
|
24641
24919
|
var ANNUAL_MONTHS_FREE = PRICING.annual.monthsFree;
|
|
24642
24920
|
var FREE_DOMAIN_SUFFIX = PRICING.freeDomainSuffix;
|
|
24643
24921
|
|
|
24922
|
+
// ../../packages/core/src/project-source.ts
|
|
24923
|
+
var MAX_IMAGE_NAME_LENGTH = 255;
|
|
24924
|
+
var MAX_IMAGE_TAG_LENGTH = 128;
|
|
24925
|
+
var IMAGE_PATH_COMPONENT_RE = /^[a-z0-9]+(?:(?:[._]|__|-+)[a-z0-9]+)*$/;
|
|
24926
|
+
var IMAGE_TAG_RE = /^[A-Za-z0-9_][A-Za-z0-9_.-]{0,127}$/;
|
|
24927
|
+
var IMAGE_DIGEST_RE = /^([A-Za-z][A-Za-z0-9]*(?:[+._-][A-Za-z][A-Za-z0-9]*)*):([A-Za-z0-9=_-]+)$/;
|
|
24928
|
+
var IMAGE_DOMAIN_LABEL_RE = /^[A-Za-z0-9](?:[A-Za-z0-9-]*[A-Za-z0-9])?$/;
|
|
24929
|
+
function validateImageReference(ref) {
|
|
24930
|
+
if (typeof ref !== "string") return "Container image reference must be a string.";
|
|
24931
|
+
if (!ref) return "Container image reference cannot be empty.";
|
|
24932
|
+
if (ref !== ref.trim()) return "Container image reference cannot have surrounding whitespace.";
|
|
24933
|
+
if (/\s/.test(ref))
|
|
24934
|
+
return `Container image reference ${JSON.stringify(ref)} cannot contain whitespace.`;
|
|
24935
|
+
if (ref.includes("\0"))
|
|
24936
|
+
return `Container image reference ${JSON.stringify(ref)} contains a NUL byte.`;
|
|
24937
|
+
if (ref.includes("://")) {
|
|
24938
|
+
return `Container image reference ${JSON.stringify(ref)} must not include a URL scheme.`;
|
|
24939
|
+
}
|
|
24940
|
+
const at = ref.indexOf("@");
|
|
24941
|
+
if (at !== -1 && at !== ref.lastIndexOf("@")) {
|
|
24942
|
+
return `Container image reference ${JSON.stringify(ref)} contains more than one digest separator (@).`;
|
|
24943
|
+
}
|
|
24944
|
+
const nameAndTag = at === -1 ? ref : ref.slice(0, at);
|
|
24945
|
+
const digest = at === -1 ? null : ref.slice(at + 1);
|
|
24946
|
+
if (!nameAndTag)
|
|
24947
|
+
return `Container image reference ${JSON.stringify(ref)} is missing a repository name.`;
|
|
24948
|
+
if (digest !== null) {
|
|
24949
|
+
const parsed = IMAGE_DIGEST_RE.exec(digest);
|
|
24950
|
+
if (!parsed) {
|
|
24951
|
+
return `Container image reference ${JSON.stringify(ref)} has an invalid OCI digest.`;
|
|
24952
|
+
}
|
|
24953
|
+
if (parsed[1].toLowerCase() === "sha256" && !/^[a-fA-F0-9]{64}$/.test(parsed[2])) {
|
|
24954
|
+
return `Container image reference ${JSON.stringify(ref)} has an invalid sha256 digest (expected 64 hexadecimal characters).`;
|
|
24955
|
+
}
|
|
24956
|
+
}
|
|
24957
|
+
const lastSlash = nameAndTag.lastIndexOf("/");
|
|
24958
|
+
const lastColon = nameAndTag.lastIndexOf(":");
|
|
24959
|
+
const hasTag = lastColon > lastSlash;
|
|
24960
|
+
const name = hasTag ? nameAndTag.slice(0, lastColon) : nameAndTag;
|
|
24961
|
+
const tag = hasTag ? nameAndTag.slice(lastColon + 1) : null;
|
|
24962
|
+
if (!name)
|
|
24963
|
+
return `Container image reference ${JSON.stringify(ref)} is missing a repository name.`;
|
|
24964
|
+
if (name.length > MAX_IMAGE_NAME_LENGTH) {
|
|
24965
|
+
return `Container image repository name is too long (${name.length}; maximum ${MAX_IMAGE_NAME_LENGTH}).`;
|
|
24966
|
+
}
|
|
24967
|
+
if (tag !== null) {
|
|
24968
|
+
if (!tag) return `Container image reference ${JSON.stringify(ref)} has an empty tag.`;
|
|
24969
|
+
if (tag.length > MAX_IMAGE_TAG_LENGTH || !IMAGE_TAG_RE.test(tag)) {
|
|
24970
|
+
return `Container image reference ${JSON.stringify(ref)} has an invalid tag; tags must be 1-${MAX_IMAGE_TAG_LENGTH} ASCII letters, digits, underscores, periods, or hyphens and cannot start with a period or hyphen.`;
|
|
24971
|
+
}
|
|
24972
|
+
}
|
|
24973
|
+
const parts = name.split("/");
|
|
24974
|
+
if (parts.some((part) => !part)) {
|
|
24975
|
+
return `Container image reference ${JSON.stringify(ref)} contains an empty repository path component.`;
|
|
24976
|
+
}
|
|
24977
|
+
const first = parts[0];
|
|
24978
|
+
const hasRegistry = parts.length > 1 && (first.includes(".") || first.includes(":") || first === "localhost" || first.startsWith("["));
|
|
24979
|
+
const path = hasRegistry ? parts.slice(1) : parts;
|
|
24980
|
+
if (path.length === 0 || path.some((part) => !IMAGE_PATH_COMPONENT_RE.test(part))) {
|
|
24981
|
+
return `Container image reference ${JSON.stringify(ref)} has an invalid repository path; repository names must be lowercase OCI path components.`;
|
|
24982
|
+
}
|
|
24983
|
+
if (hasRegistry) {
|
|
24984
|
+
const registryError = validateImageRegistry(first);
|
|
24985
|
+
if (registryError) return `Container image reference ${JSON.stringify(ref)} ${registryError}`;
|
|
24986
|
+
}
|
|
24987
|
+
return null;
|
|
24988
|
+
}
|
|
24989
|
+
function validateReleaseVersionUrl(value) {
|
|
24990
|
+
if (typeof value !== "string" || !value.trim()) {
|
|
24991
|
+
return "Release version URL cannot be empty.";
|
|
24992
|
+
}
|
|
24993
|
+
if (value !== value.trim()) {
|
|
24994
|
+
return "Release version URL cannot have surrounding whitespace.";
|
|
24995
|
+
}
|
|
24996
|
+
let url2;
|
|
24997
|
+
try {
|
|
24998
|
+
url2 = new URL(value);
|
|
24999
|
+
} catch {
|
|
25000
|
+
return "Release version URL must be a valid HTTPS URL.";
|
|
25001
|
+
}
|
|
25002
|
+
if (url2.protocol !== "https:") {
|
|
25003
|
+
return "Release version URL must use HTTPS.";
|
|
25004
|
+
}
|
|
25005
|
+
if (url2.username || url2.password) {
|
|
25006
|
+
return "Release version URL must not include embedded credentials.";
|
|
25007
|
+
}
|
|
25008
|
+
return null;
|
|
25009
|
+
}
|
|
25010
|
+
function validateReleaseRepository(value) {
|
|
25011
|
+
if (typeof value !== "string" || !value.trim()) {
|
|
25012
|
+
return 'GitHub release repository must use the "owner/repository" format.';
|
|
25013
|
+
}
|
|
25014
|
+
if (value !== value.trim()) {
|
|
25015
|
+
return "GitHub release repository cannot have surrounding whitespace.";
|
|
25016
|
+
}
|
|
25017
|
+
const parts = value.split("/");
|
|
25018
|
+
if (parts.length !== 2) {
|
|
25019
|
+
return 'GitHub release repository must use the "owner/repository" format.';
|
|
25020
|
+
}
|
|
25021
|
+
const [owner, repository] = parts;
|
|
25022
|
+
const ownerValid = owner.length <= 39 && /^[A-Za-z0-9](?:[A-Za-z0-9-]{0,37}[A-Za-z0-9])?$/.test(owner);
|
|
25023
|
+
const repositoryValid = repository.length <= 100 && repository !== "." && repository !== ".." && /^[A-Za-z0-9_.-]+$/.test(repository);
|
|
25024
|
+
if (!ownerValid || !repositoryValid) {
|
|
25025
|
+
return "GitHub release repository contains an invalid owner or repository name.";
|
|
25026
|
+
}
|
|
25027
|
+
return null;
|
|
25028
|
+
}
|
|
25029
|
+
function isValidBracketedIpv6Host(host) {
|
|
25030
|
+
if (!/^\[[0-9a-fA-F:]+\]$/.test(host)) return false;
|
|
25031
|
+
try {
|
|
25032
|
+
const parsed = new URL(`http://${host}/`);
|
|
25033
|
+
return parsed.hostname.startsWith("[") && parsed.hostname.endsWith("]");
|
|
25034
|
+
} catch {
|
|
25035
|
+
return false;
|
|
25036
|
+
}
|
|
25037
|
+
}
|
|
25038
|
+
function validateImageRegistry(registry2) {
|
|
25039
|
+
let host = registry2;
|
|
25040
|
+
let port = null;
|
|
25041
|
+
if (registry2.startsWith("[")) {
|
|
25042
|
+
const close = registry2.indexOf("]");
|
|
25043
|
+
if (close <= 1) return "has an invalid bracketed IPv6 registry host.";
|
|
25044
|
+
host = registry2.slice(0, close + 1);
|
|
25045
|
+
const rest = registry2.slice(close + 1);
|
|
25046
|
+
if (rest) {
|
|
25047
|
+
if (!rest.startsWith(":")) return "has invalid characters after its registry host.";
|
|
25048
|
+
port = rest.slice(1);
|
|
25049
|
+
}
|
|
25050
|
+
if (!isValidBracketedIpv6Host(host)) {
|
|
25051
|
+
return "has an invalid bracketed IPv6 registry host.";
|
|
25052
|
+
}
|
|
25053
|
+
} else {
|
|
25054
|
+
const colon = registry2.lastIndexOf(":");
|
|
25055
|
+
if (colon !== -1) {
|
|
25056
|
+
host = registry2.slice(0, colon);
|
|
25057
|
+
port = registry2.slice(colon + 1);
|
|
25058
|
+
}
|
|
25059
|
+
if (!host) return "is missing its registry host.";
|
|
25060
|
+
const labels = host.split(".");
|
|
25061
|
+
if (labels.some((label) => !IMAGE_DOMAIN_LABEL_RE.test(label))) {
|
|
25062
|
+
return "has an invalid registry hostname.";
|
|
25063
|
+
}
|
|
25064
|
+
}
|
|
25065
|
+
if (port !== null) {
|
|
25066
|
+
if (!/^\d+$/.test(port)) return "has a non-numeric registry port.";
|
|
25067
|
+
const value = Number(port);
|
|
25068
|
+
if (value < 1 || value > 65535) return "has a registry port outside 1-65535.";
|
|
25069
|
+
}
|
|
25070
|
+
return null;
|
|
25071
|
+
}
|
|
25072
|
+
var RELEASE_IMAGE_PLACEHOLDERS = ["version", "tag"];
|
|
25073
|
+
function renderReleaseImage(template, opts) {
|
|
25074
|
+
if (typeof template !== "string" || !template.trim()) {
|
|
25075
|
+
throw new Error("Release image template cannot be empty.");
|
|
25076
|
+
}
|
|
25077
|
+
if (template !== template.trim()) {
|
|
25078
|
+
throw new Error("Release image template cannot have surrounding whitespace.");
|
|
25079
|
+
}
|
|
25080
|
+
let insidePlaceholder = false;
|
|
25081
|
+
for (const char of template) {
|
|
25082
|
+
if (char === "{") {
|
|
25083
|
+
if (insidePlaceholder) {
|
|
25084
|
+
throw new Error(
|
|
25085
|
+
`Release image template ${JSON.stringify(template)} contains malformed placeholder braces.`
|
|
25086
|
+
);
|
|
25087
|
+
}
|
|
25088
|
+
insidePlaceholder = true;
|
|
25089
|
+
} else if (char === "}") {
|
|
25090
|
+
if (!insidePlaceholder) {
|
|
25091
|
+
throw new Error(
|
|
25092
|
+
`Release image template ${JSON.stringify(template)} contains malformed placeholder braces.`
|
|
25093
|
+
);
|
|
25094
|
+
}
|
|
25095
|
+
insidePlaceholder = false;
|
|
25096
|
+
}
|
|
25097
|
+
}
|
|
25098
|
+
if (insidePlaceholder) {
|
|
25099
|
+
throw new Error(
|
|
25100
|
+
`Release image template ${JSON.stringify(template)} contains malformed placeholder braces.`
|
|
25101
|
+
);
|
|
25102
|
+
}
|
|
25103
|
+
const placeholders2 = template.match(/\{[^{}]*\}/g) ?? [];
|
|
25104
|
+
const unknown2 = placeholders2.filter(
|
|
25105
|
+
(placeholder) => !RELEASE_IMAGE_PLACEHOLDERS.includes(placeholder.slice(1, -1))
|
|
25106
|
+
);
|
|
25107
|
+
if (unknown2.length > 0) {
|
|
25108
|
+
throw new Error(
|
|
25109
|
+
`Release image template ${JSON.stringify(template)} uses unknown placeholder(s) ${unknown2.join(", ")}. Supported: ${RELEASE_IMAGE_PLACEHOLDERS.map((p) => `{${p}}`).join(" ")}.`
|
|
25110
|
+
);
|
|
25111
|
+
}
|
|
25112
|
+
if (!template.includes("{version}") && !template.includes("{tag}")) {
|
|
25113
|
+
throw new Error(
|
|
25114
|
+
`Release image template ${JSON.stringify(template)} must contain {version} or {tag}.`
|
|
25115
|
+
);
|
|
25116
|
+
}
|
|
25117
|
+
if (template.includes("@")) {
|
|
25118
|
+
throw new Error(
|
|
25119
|
+
`Release image template ${JSON.stringify(template)} must use a tag, not a digest.`
|
|
25120
|
+
);
|
|
25121
|
+
}
|
|
25122
|
+
const lastSlash = template.lastIndexOf("/");
|
|
25123
|
+
const tagColon = template.lastIndexOf(":");
|
|
25124
|
+
if (tagColon <= lastSlash) {
|
|
25125
|
+
throw new Error(
|
|
25126
|
+
`Release image template ${JSON.stringify(template)} must put {version} or {tag} in an explicit image tag.`
|
|
25127
|
+
);
|
|
25128
|
+
}
|
|
25129
|
+
const tagStart = tagColon + 1;
|
|
25130
|
+
for (const placeholder of ["{version}", "{tag}"]) {
|
|
25131
|
+
let at = template.indexOf(placeholder);
|
|
25132
|
+
while (at !== -1) {
|
|
25133
|
+
if (at < tagStart) {
|
|
25134
|
+
throw new Error(
|
|
25135
|
+
`Release image template ${JSON.stringify(template)} may use ${placeholder} only in the image tag.`
|
|
25136
|
+
);
|
|
25137
|
+
}
|
|
25138
|
+
at = template.indexOf(placeholder, at + placeholder.length);
|
|
25139
|
+
}
|
|
25140
|
+
}
|
|
25141
|
+
const rawVersion = opts.version?.trim();
|
|
25142
|
+
const rawTag = opts.tag?.trim();
|
|
25143
|
+
if (!rawVersion) throw new Error("Release image version cannot be empty.");
|
|
25144
|
+
if (!rawTag) throw new Error("Release image tag cannot be empty.");
|
|
25145
|
+
const version2 = rawVersion.replace(/^v/i, "");
|
|
25146
|
+
if (!version2) throw new Error("Release image version cannot contain only a leading v.");
|
|
25147
|
+
const rendered = template.replace(
|
|
25148
|
+
/\{(?:version|tag)\}/g,
|
|
25149
|
+
(placeholder) => placeholder === "{version}" ? version2 : rawTag
|
|
25150
|
+
);
|
|
25151
|
+
const invalid = validateImageReference(rendered);
|
|
25152
|
+
if (invalid) throw new Error(invalid);
|
|
25153
|
+
return rendered;
|
|
25154
|
+
}
|
|
25155
|
+
|
|
24644
25156
|
// ../../packages/core/src/updates/types.ts
|
|
24645
25157
|
var GITHUB_REPO = "oblien/openship";
|
|
24646
25158
|
var RELEASES_LATEST_API = `https://api.github.com/repos/${GITHUB_REPO}/releases/latest`;
|
|
@@ -24772,6 +25284,13 @@ var AUDIT_EVENTS = {
|
|
|
24772
25284
|
tone: "warning",
|
|
24773
25285
|
description: "A delete was refused \u2014 something still depends on this app."
|
|
24774
25286
|
},
|
|
25287
|
+
"project.deletion.failed": {
|
|
25288
|
+
category: "apps",
|
|
25289
|
+
action: "failed to delete",
|
|
25290
|
+
label: "App deletion failed",
|
|
25291
|
+
tone: "warning",
|
|
25292
|
+
description: "Cleanup did not complete, so the app record was kept for a safe retry."
|
|
25293
|
+
},
|
|
24775
25294
|
"project:write": {
|
|
24776
25295
|
category: "apps",
|
|
24777
25296
|
action: "changed",
|
|
@@ -25259,6 +25778,20 @@ var AUDIT_EVENTS = {
|
|
|
25259
25778
|
tone: "warning",
|
|
25260
25779
|
description: "A full database dump left this instance."
|
|
25261
25780
|
},
|
|
25781
|
+
"instance.data.receive_code_created": {
|
|
25782
|
+
category: "security",
|
|
25783
|
+
action: "created a direct data-transfer receive code",
|
|
25784
|
+
label: "Data-transfer receive code created",
|
|
25785
|
+
tone: "warning",
|
|
25786
|
+
description: "A short-lived code was created to receive instance data directly."
|
|
25787
|
+
},
|
|
25788
|
+
"instance.data.sent": {
|
|
25789
|
+
category: "security",
|
|
25790
|
+
action: "sent instance data to another instance",
|
|
25791
|
+
label: "Instance data sent",
|
|
25792
|
+
tone: "warning",
|
|
25793
|
+
description: "Selected instance data was transferred directly to another instance."
|
|
25794
|
+
},
|
|
25262
25795
|
"instance.data.imported": {
|
|
25263
25796
|
category: "security",
|
|
25264
25797
|
action: "imported instance data",
|
|
@@ -25767,6 +26300,60 @@ function idSuffix(distroId) {
|
|
|
25767
26300
|
return distroId ? ` (os-release ID=${JSON.stringify(distroId)})` : "";
|
|
25768
26301
|
}
|
|
25769
26302
|
|
|
26303
|
+
// ../../packages/core/src/network.ts
|
|
26304
|
+
function ipv4Octets(host) {
|
|
26305
|
+
const parts = host.split(".");
|
|
26306
|
+
if (parts.length !== 4) return null;
|
|
26307
|
+
const octets = parts.map((part) => /^(?:0|[1-9]\d{0,2})$/.test(part) ? Number(part) : NaN);
|
|
26308
|
+
if (octets.some((octet) => !Number.isInteger(octet) || octet < 0 || octet > 255)) {
|
|
26309
|
+
return null;
|
|
26310
|
+
}
|
|
26311
|
+
return octets;
|
|
26312
|
+
}
|
|
26313
|
+
function ipv6Words(host) {
|
|
26314
|
+
let value = host;
|
|
26315
|
+
const zone = value.indexOf("%");
|
|
26316
|
+
if (zone >= 0) value = value.slice(0, zone);
|
|
26317
|
+
const dottedTail = /(?:^|:)(\d+\.\d+\.\d+\.\d+)$/.exec(value)?.[1];
|
|
26318
|
+
if (dottedTail) {
|
|
26319
|
+
const octets = ipv4Octets(dottedTail);
|
|
26320
|
+
if (!octets) return null;
|
|
26321
|
+
const replacement = `${(octets[0] << 8 | octets[1]).toString(16)}:${(octets[2] << 8 | octets[3]).toString(16)}`;
|
|
26322
|
+
value = value.slice(0, -dottedTail.length) + replacement;
|
|
26323
|
+
}
|
|
26324
|
+
if ((value.match(/::/g) ?? []).length > 1) return null;
|
|
26325
|
+
const [leftRaw, rightRaw] = value.split("::");
|
|
26326
|
+
const left = leftRaw ? leftRaw.split(":") : [];
|
|
26327
|
+
const right = rightRaw ? rightRaw.split(":") : [];
|
|
26328
|
+
if ([...left, ...right].some((word) => !/^[0-9a-f]{1,4}$/i.test(word))) return null;
|
|
26329
|
+
const compressed = value.includes("::");
|
|
26330
|
+
const missing = 8 - left.length - right.length;
|
|
26331
|
+
if (!compressed && missing !== 0 || compressed && missing < 1) return null;
|
|
26332
|
+
return [...left, ...Array.from({ length: missing }, () => "0"), ...right].map(
|
|
26333
|
+
(word) => Number.parseInt(word, 16)
|
|
26334
|
+
);
|
|
26335
|
+
}
|
|
26336
|
+
function isLoopbackHost(host) {
|
|
26337
|
+
if (!host) return false;
|
|
26338
|
+
let value = host.trim().toLowerCase();
|
|
26339
|
+
if (value.startsWith("[") && value.endsWith("]")) value = value.slice(1, -1);
|
|
26340
|
+
value = value.replace(/\.+$/, "");
|
|
26341
|
+
if (!value) return false;
|
|
26342
|
+
if (value === "localhost" || value.endsWith(".localhost") || value === "localhost.localdomain" || value === "ip6-localhost" || value === "ip6-loopback") {
|
|
26343
|
+
return true;
|
|
26344
|
+
}
|
|
26345
|
+
const ipv43 = ipv4Octets(value);
|
|
26346
|
+
if (ipv43) return ipv43[0] === 127 || ipv43.every((octet) => octet === 0);
|
|
26347
|
+
const words = ipv6Words(value);
|
|
26348
|
+
if (!words) return false;
|
|
26349
|
+
if (words.every((word) => word === 0)) return true;
|
|
26350
|
+
if (words.slice(0, 7).every((word) => word === 0) && words[7] === 1) return true;
|
|
26351
|
+
const ipv4PrefixIsLocal = words[6] >> 8 === 127;
|
|
26352
|
+
const mapped = words.slice(0, 5).every((word) => word === 0) && words[5] === 65535;
|
|
26353
|
+
const compatible = words.slice(0, 6).every((word) => word === 0);
|
|
26354
|
+
return ipv4PrefixIsLocal && (mapped || compatible);
|
|
26355
|
+
}
|
|
26356
|
+
|
|
25770
26357
|
export {
|
|
25771
26358
|
shellQuote,
|
|
25772
26359
|
shellSplitWords,
|
|
@@ -25782,6 +26369,13 @@ export {
|
|
|
25782
26369
|
appVolumeTargets,
|
|
25783
26370
|
parseComposeNamespace,
|
|
25784
26371
|
ownsNetworkEndpoint,
|
|
26372
|
+
resolveRedirectStatus,
|
|
26373
|
+
isValidEmail,
|
|
26374
|
+
formatBytes,
|
|
26375
|
+
withTimeout,
|
|
26376
|
+
isValidEnvKey,
|
|
26377
|
+
wrapText,
|
|
26378
|
+
composeBuildIssues,
|
|
25785
26379
|
composeMountToSpec,
|
|
25786
26380
|
composeMountIssues,
|
|
25787
26381
|
composePortToSpec,
|
|
@@ -25792,11 +26386,6 @@ export {
|
|
|
25792
26386
|
buildEdgeImageRef,
|
|
25793
26387
|
buildMailImageRef,
|
|
25794
26388
|
SYSTEM,
|
|
25795
|
-
resolveRedirectStatus,
|
|
25796
|
-
isValidEmail,
|
|
25797
|
-
formatBytes,
|
|
25798
|
-
withTimeout,
|
|
25799
|
-
wrapText,
|
|
25800
26389
|
AppError,
|
|
25801
26390
|
DeployError,
|
|
25802
26391
|
safeErrorMessage,
|
|
@@ -25816,6 +26405,9 @@ export {
|
|
|
25816
26405
|
missingOutputDirectoryMessage,
|
|
25817
26406
|
compareSemver,
|
|
25818
26407
|
PRICING,
|
|
26408
|
+
validateReleaseVersionUrl,
|
|
26409
|
+
validateReleaseRepository,
|
|
26410
|
+
renderReleaseImage,
|
|
25819
26411
|
resolveCliUpdatePlan,
|
|
25820
26412
|
cliInstallCommand,
|
|
25821
26413
|
PROXY_DIRECTIVES,
|
|
@@ -25850,5 +26442,6 @@ export {
|
|
|
25850
26442
|
summarizeHostChannelCause,
|
|
25851
26443
|
hostChannelRuleScope,
|
|
25852
26444
|
hostFirewallRule,
|
|
25853
|
-
hostFirewallRuleRemoval
|
|
26445
|
+
hostFirewallRuleRemoval,
|
|
26446
|
+
isLoopbackHost
|
|
25854
26447
|
};
|