openship 0.6.6 → 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.
Files changed (72) hide show
  1. package/dist/bare-FA66N7GC.js +29 -0
  2. package/dist/{chunk-77QDF6UD.js → chunk-3MP6ACKM.js} +587 -156
  3. package/dist/{chunk-YIMOCZBZ.js → chunk-5DJOB7IX.js} +3 -3
  4. package/dist/{chunk-WVAML4H6.js → chunk-5I3COAFH.js} +274 -65
  5. package/dist/{chunk-5DMWSTOM.js → chunk-5UXHT6ED.js} +940 -224
  6. package/dist/{chunk-LUKYPVJ4.js → chunk-6GU6JSK6.js} +329 -466
  7. package/dist/{chunk-RSR7PFIQ.js → chunk-7PEXM2UI.js} +213 -45
  8. package/dist/{chunk-VTGPEC6B.js → chunk-7YBWXEM6.js} +6 -4
  9. package/dist/chunk-AHJYP7DG.js +938 -0
  10. package/dist/chunk-BO6CY4V2.js +265 -0
  11. package/dist/{chunk-FE7ZZQSE.js → chunk-D3OEMXYP.js} +3 -3
  12. package/dist/chunk-D6HU4VUZ.js +249 -0
  13. package/dist/{chunk-LFE5KQ7Y.js → chunk-ENKJ7XIF.js} +152 -57
  14. package/dist/{chunk-YMMQRE5J.js → chunk-GNOMEMNI.js} +1 -1
  15. package/dist/{chunk-QVW76WDL.js → chunk-IAMOUDHY.js} +190 -64
  16. package/dist/{chunk-OBPKYXQL.js → chunk-MUWVC5R4.js} +219 -58
  17. package/dist/chunk-N5YZMO33.js +111 -0
  18. package/dist/{chunk-A4GXYE4L.js → chunk-NIYD7MD2.js} +206 -52
  19. package/dist/{chunk-UVNMQM7W.js → chunk-NPSRGIHV.js} +8 -6
  20. package/dist/{chunk-2GWYYHOR.js → chunk-NRU2EBYZ.js} +2 -2
  21. package/dist/{chunk-CBLD2HS5.js → chunk-NUT6HKK6.js} +8 -5
  22. package/dist/{chunk-ZFJXPCQP.js → chunk-P7RCYELG.js} +4 -3
  23. package/dist/chunk-R4VDPFLS.js +106 -0
  24. package/dist/{chunk-HKRQXPTI.js → chunk-R5DC5PEF.js} +1 -1
  25. package/dist/{chunk-NK5N5LXN.js → chunk-RCMERVBL.js} +24 -9
  26. package/dist/{chunk-RXQBD4ZY.js → chunk-RHLNVZMS.js} +2 -2
  27. package/dist/chunk-SK4FFCFR.js +70 -0
  28. package/dist/{chunk-4I2BM2DT.js → chunk-T46BQOLY.js} +3 -3
  29. package/dist/{chunk-6FH4AXVY.js → chunk-T6DVUX4F.js} +1 -1
  30. package/dist/{chunk-PCPVTH6M.js → chunk-TAZZZKYO.js} +6 -4
  31. package/dist/{chunk-MLYSEZQB.js → chunk-UAWY6X7V.js} +43 -72
  32. package/dist/{chunk-Q7WGTQQ5.js → chunk-VAI5VOOI.js} +1 -1
  33. package/dist/{chunk-UJ6JZM65.js → chunk-WEZFTL4V.js} +1325 -513
  34. package/dist/{chunk-2YM3NI2E.js → chunk-X53JXRS4.js} +38 -44
  35. package/dist/{cloud-KRTZ2IL5.js → cloud-2CRDKOWQ.js} +1 -1
  36. package/dist/cloud-REV3DPJH.js +42 -0
  37. package/dist/{detect-OIE5V3VZ.js → detect-BBFT5GEU.js} +9 -6
  38. package/dist/{docker-VTR2GK5Y.js → docker-EG3QSE6I.js} +11 -8
  39. package/dist/{docker-edge-executor-22IBXNYJ.js → docker-edge-executor-E5TOTLMR.js} +8 -6
  40. package/dist/edge-import-YJVJUGYC.js +56 -0
  41. package/dist/{edge-preflight-GBNCPNVU.js → edge-preflight-QUW4BM7N.js} +15 -12
  42. package/dist/{ensure-container-edge-KPI2CVXJ.js → ensure-container-edge-FOEFQ3PI.js} +15 -10
  43. package/dist/{executor-7LXXZPBR.js → executor-NTWDJ3DB.js} +8 -6
  44. package/dist/host-channel-preflight-OMCZMMKR.js +54 -0
  45. package/dist/index.js +198 -76
  46. package/dist/{local-executor-WOFZD72X.js → local-executor-OQ2GRN7K.js} +3 -2
  47. package/dist/{nginx-KUFMLMAC.js → nginx-4U4C2XUS.js} +11 -8
  48. package/dist/{noop-NJ72F46T.js → noop-CLLJHCPL.js} +1 -1
  49. package/dist/{openresty-lua-ATTH35BE.js → openresty-lua-LLWT5XQW.js} +8 -5
  50. package/dist/{privilege-I4W3ZNZK.js → privilege-MNDVMDST.js} +6 -5
  51. package/dist/server/index.js +40049 -30378
  52. package/dist/server/migrations/0109_service_build_args.sql +9 -0
  53. package/dist/server/migrations/0110_service_deployment_host_ports.sql +18 -0
  54. package/dist/server/migrations/0111_host_port_claim.sql +381 -0
  55. package/dist/server/migrations/0112_backup_run_execution_lease.sql +12 -0
  56. package/dist/server/migrations/0113_docker_migration_execution_lease.sql +11 -0
  57. package/dist/server/migrations/0114_build_session_execution_lease_index.sql +6 -0
  58. package/dist/server/migrations/0115_orphan_target_key.sql +3 -0
  59. package/dist/server/migrations/0116_domain_ssl_challenge.sql +2 -0
  60. package/dist/server/migrations/meta/_journal.json +56 -0
  61. package/dist/setup-IFIMPQZJ.js +25 -0
  62. package/dist/{ssh-client-JBMA7SN4.js → ssh-client-2XWZCMGI.js} +3 -2
  63. package/dist/up-HQS7IR7H.js +61 -0
  64. package/package.json +1 -1
  65. package/dist/bare-OXJVW3AK.js +0 -25
  66. package/dist/chunk-LH2PLRQO.js +0 -434
  67. package/dist/chunk-SM26HDS2.js +0 -185
  68. package/dist/cloud-75VIT4KV.js +0 -37
  69. package/dist/edge-import-WJUB4M7F.js +0 -52
  70. package/dist/host-channel-preflight-KE6OONYK.js +0 -50
  71. package/dist/setup-XGLGUUFW.js +0 -22
  72. package/dist/up-K4BV7466.js +0 -57
@@ -6,6 +6,57 @@ import {
6
6
  __export
7
7
  } from "./chunk-PXKCZB2I.js";
8
8
 
9
+ // ../../packages/core/src/shell-split.ts
10
+ function shellQuote(value) {
11
+ return `'${value.replace(/'/g, "'\\''")}'`;
12
+ }
13
+ function shellSplitWords(value) {
14
+ const words = [];
15
+ let current = "";
16
+ let quoted = false;
17
+ let inSingle = false;
18
+ let inDouble = false;
19
+ let escaped = false;
20
+ for (const char of value.trim()) {
21
+ if (escaped) {
22
+ current += char;
23
+ escaped = false;
24
+ continue;
25
+ }
26
+ if (char === "\\" && !inSingle) {
27
+ escaped = true;
28
+ continue;
29
+ }
30
+ if (char === "'" && !inDouble) {
31
+ inSingle = !inSingle;
32
+ quoted = true;
33
+ continue;
34
+ }
35
+ if (char === '"' && !inSingle) {
36
+ inDouble = !inDouble;
37
+ quoted = true;
38
+ continue;
39
+ }
40
+ if (/\s/.test(char) && !inSingle && !inDouble) {
41
+ if (current || quoted) {
42
+ words.push(current);
43
+ current = "";
44
+ quoted = false;
45
+ }
46
+ continue;
47
+ }
48
+ current += char;
49
+ }
50
+ if (escaped) current += "\\";
51
+ if (current || quoted) words.push(current);
52
+ return words;
53
+ }
54
+ function commandToArgv(command) {
55
+ if (command == null) return null;
56
+ if (Array.isArray(command)) return command.map((part) => String(part));
57
+ return shellSplitWords(command);
58
+ }
59
+
9
60
  // ../../packages/core/src/stacks.ts
10
61
  var LANGUAGES = {
11
62
  javascript: {
@@ -831,6 +882,21 @@ function packageManagerEnsureCommand(packageManager) {
831
882
  if (packageManager !== "pnpm" && packageManager !== "yarn") return "";
832
883
  return `(corepack enable ${packageManager} || corepack enable || npm i -g ${packageManager}) >/dev/null 2>&1 || true`;
833
884
  }
885
+ var NODE_BIN_PACKAGE_MANAGERS = /* @__PURE__ */ new Set(["npm", "yarn", "pnpm", "bun"]);
886
+ function nodeBinDirs(packageManager, roots) {
887
+ if (!packageManager || !NODE_BIN_PACKAGE_MANAGERS.has(packageManager)) return [];
888
+ const dirs = [];
889
+ for (const root of roots) {
890
+ const dir = `${root.replace(/\/+$/, "")}/node_modules/.bin`;
891
+ if (!dirs.includes(dir)) dirs.push(dir);
892
+ }
893
+ return dirs;
894
+ }
895
+ function nodeBinPathExport(packageManager, roots) {
896
+ const dirs = nodeBinDirs(packageManager, roots);
897
+ if (dirs.length === 0) return "";
898
+ return `export PATH=${dirs.map(shellQuote).join(":")}:"$PATH"`;
899
+ }
834
900
  var DI = "https://cdn.jsdelivr.net/gh/devicons/devicon@latest/icons";
835
901
  var STACK_ICONS = {
836
902
  // JS/TS - Frontend & Fullstack
@@ -951,6 +1017,65 @@ function ownsNetworkEndpoint(resolvedNetworkMode) {
951
1017
  return !resolvedNetworkMode;
952
1018
  }
953
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
+
954
1079
  // ../../packages/core/src/constants.ts
955
1080
  var DEFAULT_IMAGE_REGISTRY = "ghcr.io/oblien";
956
1081
 
@@ -1133,62 +1258,6 @@ var SYSTEM = {
1133
1258
  }
1134
1259
  };
1135
1260
 
1136
- // ../../packages/core/src/utils.ts
1137
- var REDIRECT_STATUSES = [301, 302, 307, 308];
1138
- var DEFAULT_REDIRECT_STATUS = 301;
1139
- function resolveRedirectStatus(status) {
1140
- return REDIRECT_STATUSES.includes(status) ? status : DEFAULT_REDIRECT_STATUS;
1141
- }
1142
- function isValidEmail(value) {
1143
- const email3 = value?.trim() ?? "";
1144
- if (!email3 || email3.length > 254) return false;
1145
- return /^[A-Za-z0-9._%+-]+@[A-Za-z0-9-]+(\.[A-Za-z0-9-]+)*\.[A-Za-z]{2,}$/.test(email3);
1146
- }
1147
- function formatBytes(bytes, decimals = 2) {
1148
- if (bytes <= 0) return "0 B";
1149
- const k = 1024;
1150
- const sizes = ["B", "KB", "MB", "GB", "TB"];
1151
- const i = Math.min(Math.max(Math.floor(Math.log(bytes) / Math.log(k)), 0), sizes.length - 1);
1152
- return `${parseFloat((bytes / Math.pow(k, i)).toFixed(decimals))} ${sizes[i]}`;
1153
- }
1154
- function withTimeout(promise2, ms, message) {
1155
- if (!ms) return promise2;
1156
- return new Promise((resolve, reject) => {
1157
- const timer = setTimeout(() => reject(new Error(message)), ms);
1158
- promise2.then(
1159
- (v) => {
1160
- clearTimeout(timer);
1161
- resolve(v);
1162
- },
1163
- (e) => {
1164
- clearTimeout(timer);
1165
- reject(e);
1166
- }
1167
- );
1168
- });
1169
- }
1170
- function wrapText(text, width = 88) {
1171
- const out = [];
1172
- for (const paragraph of text.split("\n")) {
1173
- const words = paragraph.split(/\s+/).filter(Boolean);
1174
- if (words.length === 0) {
1175
- out.push("");
1176
- continue;
1177
- }
1178
- let line = "";
1179
- for (const word of words) {
1180
- if (line && line.length + 1 + word.length > width) {
1181
- out.push(line);
1182
- line = word;
1183
- } else {
1184
- line = line ? `${line} ${word}` : word;
1185
- }
1186
- }
1187
- if (line) out.push(line);
1188
- }
1189
- return out;
1190
- }
1191
-
1192
1261
  // ../../packages/core/src/errors.ts
1193
1262
  var AppError = class extends Error {
1194
1263
  constructor(message, statusCode = 500, code) {
@@ -2482,6 +2551,104 @@ function hostFirewallRuleRemoval(firewall, cidrs, port) {
2482
2551
 
2483
2552
  // ../../packages/core/src/compose-spec.ts
2484
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
+ }
2485
2652
  function composeMountToSpec(mount, interpolate = asIs) {
2486
2653
  const source = mount.source ?? mount.name;
2487
2654
  const target = mount.target;
@@ -2561,57 +2728,6 @@ function isAwsS3Endpoint(endpoint2) {
2561
2728
  return !endpoint2 || endpoint2.includes(".amazonaws.com");
2562
2729
  }
2563
2730
 
2564
- // ../../packages/core/src/shell-split.ts
2565
- function shellQuote(value) {
2566
- return `'${value.replace(/'/g, "'\\''")}'`;
2567
- }
2568
- function shellSplitWords(value) {
2569
- const words = [];
2570
- let current = "";
2571
- let quoted = false;
2572
- let inSingle = false;
2573
- let inDouble = false;
2574
- let escaped = false;
2575
- for (const char of value.trim()) {
2576
- if (escaped) {
2577
- current += char;
2578
- escaped = false;
2579
- continue;
2580
- }
2581
- if (char === "\\" && !inSingle) {
2582
- escaped = true;
2583
- continue;
2584
- }
2585
- if (char === "'" && !inDouble) {
2586
- inSingle = !inSingle;
2587
- quoted = true;
2588
- continue;
2589
- }
2590
- if (char === '"' && !inSingle) {
2591
- inDouble = !inDouble;
2592
- quoted = true;
2593
- continue;
2594
- }
2595
- if (/\s/.test(char) && !inSingle && !inDouble) {
2596
- if (current || quoted) {
2597
- words.push(current);
2598
- current = "";
2599
- quoted = false;
2600
- }
2601
- continue;
2602
- }
2603
- current += char;
2604
- }
2605
- if (escaped) current += "\\";
2606
- if (current || quoted) words.push(current);
2607
- return words;
2608
- }
2609
- function commandToArgv(command) {
2610
- if (command == null) return null;
2611
- if (Array.isArray(command)) return command.map((part) => String(part));
2612
- return shellSplitWords(command);
2613
- }
2614
-
2615
2731
  // ../../packages/core/src/mail-image-ref.ts
2616
2732
  function buildMailImageRef(opts) {
2617
2733
  return buildImageRef(opts, {
@@ -2621,6 +2737,284 @@ function buildMailImageRef(opts) {
2621
2737
  });
2622
2738
  }
2623
2739
 
2740
+ // ../../packages/core/src/backup-catalog.ts
2741
+ var PAYLOAD_KINDS = [
2742
+ "pg_dump",
2743
+ "mysql_dump",
2744
+ "redis_rdb",
2745
+ "mongo_dump",
2746
+ "custom_command",
2747
+ "path",
2748
+ "volume"
2749
+ ];
2750
+ var PAYLOAD_CONFIG_KEYS = [
2751
+ "sourceIds",
2752
+ "exclude",
2753
+ "quiesce",
2754
+ "compression",
2755
+ "paths",
2756
+ "clearPath",
2757
+ "produceCommand",
2758
+ "command",
2759
+ "restoreCommand",
2760
+ "artifactName"
2761
+ ];
2762
+ var UNIVERSAL_PAYLOAD_CONFIG_KEYS = ["verifyOnPrepare", "mail"];
2763
+ var ALL_PAYLOAD_CONFIG_KEYS = [
2764
+ ...PAYLOAD_CONFIG_KEYS,
2765
+ ...UNIVERSAL_PAYLOAD_CONFIG_KEYS
2766
+ ];
2767
+ var HOUR_MS = 60 * 60 * 1e3;
2768
+ var BACKUP_PAYLOADS = {
2769
+ pg_dump: {
2770
+ kind: "pg_dump",
2771
+ label: "PostgreSQL",
2772
+ method: "pg_dump",
2773
+ shape: "database",
2774
+ images: { names: ["postgres", "postgis/postgis"] },
2775
+ restoreNeedsLiveContainer: true,
2776
+ restoreAppliesAfterBounce: false,
2777
+ restoreTimeoutMs: HOUR_MS,
2778
+ // Inert: `pg_restore --clean --if-exists` owns replacement.
2779
+ restoreClearsTargetByDefault: false,
2780
+ // The only kind that can claim this, and only since `producers/pg-dump.ts` restores
2781
+ // under `--single-transaction`. Before that flag, a DROP blocked by a dependent view
2782
+ // was skipped and the archive's rows were loaded ON TOP of the live ones — one table
2783
+ // holding a merge of two points in time, reported as "errors ignored on restore".
2784
+ restoreFailureLeavesTargetIntact: true,
2785
+ requiresRestoreCommand: false,
2786
+ operatorSelectable: true,
2787
+ configKeys: []
2788
+ },
2789
+ mysql_dump: {
2790
+ kind: "mysql_dump",
2791
+ label: "MySQL/MariaDB",
2792
+ method: "mysqldump",
2793
+ shape: "database",
2794
+ // `percona/percona-server` and `percona/percona-server-mongodb` are kept apart by
2795
+ // the `(:|$)` anchor: what follows `percona-server` in the MongoDB image is `-`,
2796
+ // which is neither a tag separator nor the end of the string.
2797
+ images: { names: ["mysql", "mariadb", "percona/percona-server"] },
2798
+ restoreNeedsLiveContainer: true,
2799
+ restoreAppliesAfterBounce: false,
2800
+ restoreTimeoutMs: HOUR_MS,
2801
+ restoreClearsTargetByDefault: false,
2802
+ // MySQL has no transactional DDL: replaying a dump commits each statement as it
2803
+ // goes, so a failure halfway leaves half the schema and part of the data.
2804
+ restoreFailureLeavesTargetIntact: false,
2805
+ requiresRestoreCommand: false,
2806
+ operatorSelectable: true,
2807
+ configKeys: []
2808
+ },
2809
+ redis_rdb: {
2810
+ kind: "redis_rdb",
2811
+ label: "Redis",
2812
+ method: "RDB snapshot",
2813
+ shape: "database",
2814
+ images: { names: ["redis"], namespaces: ["redis"] },
2815
+ restoreNeedsLiveContainer: true,
2816
+ // The one kind whose write is inert until the service restarts. See the field doc.
2817
+ restoreAppliesAfterBounce: true,
2818
+ restoreTimeoutMs: 30 * 60 * 1e3,
2819
+ restoreClearsTargetByDefault: false,
2820
+ // The write is a stream into `dump.rdb`. A failure halfway leaves a TRUNCATED
2821
+ // snapshot — which the running server has not read (see restoreAppliesAfterBounce)
2822
+ // but the next start will, and refuse.
2823
+ restoreFailureLeavesTargetIntact: false,
2824
+ requiresRestoreCommand: false,
2825
+ operatorSelectable: true,
2826
+ configKeys: []
2827
+ },
2828
+ mongo_dump: {
2829
+ kind: "mongo_dump",
2830
+ label: "MongoDB",
2831
+ method: "mongodump",
2832
+ shape: "database",
2833
+ images: { names: ["mongo", "percona/percona-server-mongodb"] },
2834
+ restoreNeedsLiveContainer: true,
2835
+ restoreAppliesAfterBounce: false,
2836
+ restoreTimeoutMs: HOUR_MS,
2837
+ restoreClearsTargetByDefault: false,
2838
+ // `mongorestore --drop` drops and reloads per collection, so a failure between two
2839
+ // collections leaves one restored and the next dropped.
2840
+ restoreFailureLeavesTargetIntact: false,
2841
+ requiresRestoreCommand: false,
2842
+ operatorSelectable: true,
2843
+ configKeys: []
2844
+ },
2845
+ custom_command: {
2846
+ kind: "custom_command",
2847
+ label: "Custom command",
2848
+ method: "your command",
2849
+ shape: "opaque",
2850
+ restoreNeedsLiveContainer: true,
2851
+ restoreAppliesAfterBounce: false,
2852
+ restoreTimeoutMs: HOUR_MS,
2853
+ // Inert: the operator's own inverse command decides what it replaces.
2854
+ restoreClearsTargetByDefault: false,
2855
+ // We cannot reason about someone else's command. `opaque` in, opaque out.
2856
+ restoreFailureLeavesTargetIntact: false,
2857
+ requiresRestoreCommand: true,
2858
+ operatorSelectable: true,
2859
+ configKeys: ["produceCommand", "command", "restoreCommand", "artifactName"]
2860
+ },
2861
+ path: {
2862
+ kind: "path",
2863
+ label: "Files and folders",
2864
+ method: "tar",
2865
+ shape: "filesystem",
2866
+ // Explicit-only: a path is something the operator names. Nothing about an image
2867
+ // says "back up /var/www", and guessing would either miss the data or archive an
2868
+ // OS.
2869
+ restoreNeedsLiveContainer: true,
2870
+ restoreAppliesAfterBounce: false,
2871
+ // Matches the docker helper's own absolute ceiling for the volume directions, so
2872
+ // one number covers all three: a tree big enough to need hours to capture needs
2873
+ // hours to restore.
2874
+ restoreTimeoutMs: 6 * HOUR_MS,
2875
+ // Merge, unless the policy sets `clearPath`. A folder is not necessarily this
2876
+ // artifact's alone.
2877
+ restoreClearsTargetByDefault: false,
2878
+ // `tar -x` writes file by file: a failure halfway leaves the tree part-updated,
2879
+ // and with `clearPath` set it leaves it part-updated on top of an emptied folder.
2880
+ restoreFailureLeavesTargetIntact: false,
2881
+ requiresRestoreCommand: false,
2882
+ operatorSelectable: true,
2883
+ configKeys: ["paths", "exclude", "compression", "clearPath"]
2884
+ },
2885
+ volume: {
2886
+ kind: "volume",
2887
+ label: "Volume snapshot",
2888
+ method: "tar",
2889
+ shape: "filesystem",
2890
+ // No `images`: this is the fallback the registry uses when nothing else detects,
2891
+ // not a match on anything.
2892
+ //
2893
+ // A volume restore extracts through a SEPARATE helper container mounted on the
2894
+ // volume, so it is the only kind that can restore into a service whose container
2895
+ // is gone — or that never deployed.
2896
+ restoreNeedsLiveContainer: false,
2897
+ restoreAppliesAfterBounce: false,
2898
+ restoreTimeoutMs: 6 * HOUR_MS,
2899
+ // Replace: a volume is a single-purpose store, so a merge would leave files this
2900
+ // artifact never held.
2901
+ restoreClearsTargetByDefault: true,
2902
+ // Clear-then-extract, and the clear happens in the helper's PRELUDE — so by the
2903
+ // time any error escapes, the volume is already empty. This is the case the
2904
+ // orchestrator's old inference was built around, and it is still exactly right.
2905
+ restoreFailureLeavesTargetIntact: false,
2906
+ requiresRestoreCommand: false,
2907
+ operatorSelectable: true,
2908
+ configKeys: ["sourceIds", "exclude", "quiesce", "compression"]
2909
+ }
2910
+ };
2911
+ function payloadSpec(kind) {
2912
+ const spec = BACKUP_PAYLOADS[kind];
2913
+ if (!spec) {
2914
+ throw new Error(
2915
+ `Unknown backup payload kind "${kind}". Known kinds: ${PAYLOAD_KINDS.join(", ")}.`
2916
+ );
2917
+ }
2918
+ return spec;
2919
+ }
2920
+ function payloadSpecs() {
2921
+ return PAYLOAD_KINDS.map((k) => BACKUP_PAYLOADS[k]);
2922
+ }
2923
+ function imageDetectableSpecs() {
2924
+ return payloadSpecs().filter(
2925
+ (s) => !!s.images
2926
+ );
2927
+ }
2928
+ var SYSTEM_ROOTS = [
2929
+ "/bin",
2930
+ "/boot",
2931
+ "/dev",
2932
+ "/etc",
2933
+ "/lib",
2934
+ "/lib32",
2935
+ "/lib64",
2936
+ "/libx32",
2937
+ "/proc",
2938
+ "/root",
2939
+ "/run",
2940
+ "/sbin",
2941
+ "/sys",
2942
+ "/usr",
2943
+ "/var"
2944
+ ];
2945
+ function normalizeBackupPath(input) {
2946
+ const collapsed = input.trim().replace(/\/{2,}/g, "/");
2947
+ if (collapsed === "/") return "/";
2948
+ return collapsed.replace(/\/+$/, "");
2949
+ }
2950
+ function validateBackupPath(input) {
2951
+ const path = normalizeBackupPath(input);
2952
+ if (path === "") return "A backup path cannot be empty.";
2953
+ if (!path.startsWith("/")) {
2954
+ return `Backup path "${input}" must be absolute \u2014 it is resolved inside the service, where there is no meaningful working directory.`;
2955
+ }
2956
+ if (path.includes("\0")) return `Backup path "${input}" contains a NUL byte.`;
2957
+ if (path.split("/").includes("..")) {
2958
+ return `Backup path "${input}" contains "..". Name the directory you mean directly; a traversal segment would archive somewhere other than what is shown here.`;
2959
+ }
2960
+ if (path === "/") {
2961
+ return "Refusing to archive `/` \u2014 that is the whole filesystem, including /proc and /sys. Name the data directory instead.";
2962
+ }
2963
+ return null;
2964
+ }
2965
+ function validateClearPath(input) {
2966
+ const path = normalizeBackupPath(input);
2967
+ const invalid = validateBackupPath(path);
2968
+ if (invalid) return invalid;
2969
+ const segments = path.split("/").filter(Boolean);
2970
+ if (segments.length < 2) {
2971
+ return `Refusing to clear "${path}" before restoring: it is a top-level directory. Name a subdirectory, or leave clearing off so the restore extracts over the existing files.`;
2972
+ }
2973
+ const top = `/${segments[0]}`;
2974
+ if (SYSTEM_ROOTS.includes(top)) {
2975
+ return `Refusing to clear "${path}" before restoring: it is inside ${top}, which holds the operating system rather than your data. Leave clearing off, or restore into a path you own.`;
2976
+ }
2977
+ return null;
2978
+ }
2979
+
2980
+ // ../../packages/core/src/backup-image-detect.ts
2981
+ var escapeRe = (n) => n.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
2982
+ function matcherFor(names, namespaces = []) {
2983
+ const exact = `^(?:${names.map(escapeRe).join("|")})(?::|$)`;
2984
+ if (namespaces.length === 0) return new RegExp(exact, "i");
2985
+ return new RegExp(`${exact}|^(?:${namespaces.map(escapeRe).join("|")})/`, "i");
2986
+ }
2987
+ var MATCHERS = imageDetectableSpecs().map(
2988
+ (spec) => ({
2989
+ re: matcherFor(spec.images.names, spec.images.namespaces),
2990
+ match: { payloadKind: spec.kind, label: spec.label, method: spec.method }
2991
+ })
2992
+ );
2993
+ function normalizeImageRef(image) {
2994
+ let ref = image.trim();
2995
+ const at = ref.indexOf("@");
2996
+ if (at > 0) ref = ref.slice(0, at);
2997
+ const parts = ref.split("/");
2998
+ const first = parts[0] ?? "";
2999
+ if (parts.length > 1 && (first.includes(".") || first.includes(":") || first === "localhost")) {
3000
+ parts.shift();
3001
+ }
3002
+ const library = parts.lastIndexOf("library");
3003
+ if (library >= 0 && library < parts.length - 1) parts.splice(0, library + 1);
3004
+ return parts.join("/");
3005
+ }
3006
+ function detectDbImage(image) {
3007
+ const img = normalizeImageRef(image ?? "");
3008
+ if (!img) return null;
3009
+ for (const { re, match } of MATCHERS) {
3010
+ if (re.test(img)) return match;
3011
+ }
3012
+ return null;
3013
+ }
3014
+ function isDbImage(image, kind) {
3015
+ return detectDbImage(image)?.payloadKind === kind;
3016
+ }
3017
+
2624
3018
  // ../../packages/core/src/runtime-config.ts
2625
3019
  var DEFAULT_PORT = {
2626
3020
  web: 3e3,
@@ -5809,6 +6203,7 @@ COMMIT;
5809
6203
  {
5810
6204
  available: true,
5811
6205
  verified: true,
6206
+ minEngine: "0.6.6",
5812
6207
  id: "minio",
5813
6208
  name: "MinIO",
5814
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.",
@@ -6303,6 +6698,7 @@ COMMIT;
6303
6698
  {
6304
6699
  available: true,
6305
6700
  verified: false,
6701
+ minEngine: "0.6.6",
6306
6702
  minResources: {
6307
6703
  memoryMb: 4096,
6308
6704
  cpuCores: 2
@@ -6484,6 +6880,7 @@ Two things worth knowing, both upstream behaviour: if the console shows a branch
6484
6880
  {
6485
6881
  available: true,
6486
6882
  verified: false,
6883
+ minEngine: "0.6.6",
6487
6884
  minResources: {
6488
6885
  memoryMb: 16384,
6489
6886
  cpuCores: 4
@@ -7306,6 +7703,7 @@ Two things worth knowing, both upstream behaviour: if the console shows a branch
7306
7703
  {
7307
7704
  available: true,
7308
7705
  verified: false,
7706
+ minEngine: "0.6.6",
7309
7707
  id: "redis",
7310
7708
  name: "Valkey (Redis)",
7311
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.",
@@ -7784,6 +8182,7 @@ Two things worth knowing, both upstream behaviour: if the console shows a branch
7784
8182
  {
7785
8183
  available: true,
7786
8184
  verified: false,
8185
+ minEngine: "0.6.6",
7787
8186
  id: "clickhouse",
7788
8187
  name: "ClickHouse",
7789
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.",
@@ -7958,96 +8357,268 @@ Two things worth knowing, both upstream behaviour: if the console shows a branch
7958
8357
  help: "ClickHouse's HTTP interface (port 8123) \u2014 what clickhouse-connect, clickhouse-js, the JDBC/ODBC drivers and plain curl talk to. Send the user and password as basic auth. Switch to Internal for apps on the same project network; Public only resolves if you gave this port a domain."
7959
8358
  },
7960
8359
  {
7961
- id: "httpDsn",
7962
- label: "HTTP DSN",
7963
- source: "template:http://{{env:clickhouse:CLICKHOUSE_USER}}:{{env:clickhouse:CLICKHOUSE_PASSWORD}}@clickhouse:8123/{{env:clickhouse:CLICKHOUSE_DB}}",
7964
- sourceLabel: "Internal",
7965
- service: "clickhouse",
7966
- secret: true,
7967
- envKey: "CLICKHOUSE_HTTP_DSN",
8360
+ id: "httpDsn",
8361
+ label: "HTTP DSN",
8362
+ source: "template:http://{{env:clickhouse:CLICKHOUSE_USER}}:{{env:clickhouse:CLICKHOUSE_PASSWORD}}@clickhouse:8123/{{env:clickhouse:CLICKHOUSE_DB}}",
8363
+ sourceLabel: "Internal",
8364
+ service: "clickhouse",
8365
+ secret: true,
8366
+ envKey: "CLICKHOUSE_HTTP_DSN",
8367
+ recommended: true,
8368
+ help: "One string with the credentials baked in, over the private project network. For a client outside this server, use the public HTTP endpoint above plus the user and password."
8369
+ },
8370
+ {
8371
+ id: "nativeDsn",
8372
+ label: "Native TCP DSN",
8373
+ source: "template:clickhouse://{{env:clickhouse:CLICKHOUSE_USER}}:{{env:clickhouse:CLICKHOUSE_PASSWORD}}@clickhouse:9000/{{env:clickhouse:CLICKHOUSE_DB}}",
8374
+ sourceLabel: "Internal",
8375
+ service: "clickhouse",
8376
+ secret: true,
8377
+ envKey: "CLICKHOUSE_DSN",
8378
+ help: "ClickHouse's native protocol (port 9000) \u2014 faster and what clickhouse-client, clickhouse-go and clickhouse-driver prefer. Reachable from inside the project only; the port is never published to the host."
8379
+ },
8380
+ {
8381
+ id: "user",
8382
+ label: "User",
8383
+ source: "env:clickhouse:CLICKHOUSE_USER",
8384
+ service: "clickhouse",
8385
+ envKey: "CLICKHOUSE_USER",
8386
+ recommended: true,
8387
+ width: "half"
8388
+ },
8389
+ {
8390
+ id: "password",
8391
+ label: "Password",
8392
+ source: "env:clickhouse:CLICKHOUSE_PASSWORD",
8393
+ service: "clickhouse",
8394
+ envKey: "CLICKHOUSE_PASSWORD",
8395
+ secret: true,
8396
+ recommended: true,
8397
+ width: "half"
8398
+ },
8399
+ {
8400
+ id: "database",
8401
+ label: "Database",
8402
+ source: "env:clickhouse:CLICKHOUSE_DB",
8403
+ service: "clickhouse",
8404
+ envKey: "CLICKHOUSE_DATABASE",
8405
+ width: "half"
8406
+ }
8407
+ ]
8408
+ },
8409
+ provides: [
8410
+ {
8411
+ id: "clickhouse",
8412
+ outputRefs: [
8413
+ "httpUrl",
8414
+ "httpDsn",
8415
+ "user",
8416
+ "password",
8417
+ "database"
8418
+ ],
8419
+ category: "database"
8420
+ }
8421
+ ],
8422
+ endpoints: [
8423
+ {
8424
+ service: "ch-ui",
8425
+ port: 3488,
8426
+ label: "Console",
8427
+ kind: "http",
8428
+ required: true,
8429
+ scope: "public",
8430
+ defaultMode: "domain"
8431
+ },
8432
+ {
8433
+ service: "clickhouse",
8434
+ port: 8123,
8435
+ label: "HTTP API",
8436
+ kind: "http",
8437
+ scope: "public",
8438
+ defaultMode: "port"
8439
+ },
8440
+ {
8441
+ service: "clickhouse",
8442
+ port: 9e3,
8443
+ label: "Native protocol",
8444
+ kind: "tcp",
8445
+ scope: "internal",
8446
+ defaultMode: "internal",
8447
+ allowedModes: [
8448
+ "internal"
8449
+ ]
8450
+ }
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",
7968
8599
  recommended: true,
7969
- help: "One string with the credentials baked in, over the private project network. For a client outside this server, use the public HTTP endpoint above plus the user and password."
7970
- },
7971
- {
7972
- id: "nativeDsn",
7973
- label: "Native TCP DSN",
7974
- source: "template:clickhouse://{{env:clickhouse:CLICKHOUSE_USER}}:{{env:clickhouse:CLICKHOUSE_PASSWORD}}@clickhouse:9000/{{env:clickhouse:CLICKHOUSE_DB}}",
7975
- sourceLabel: "Internal",
7976
- service: "clickhouse",
7977
- secret: true,
7978
- envKey: "CLICKHOUSE_DSN",
7979
- help: "ClickHouse's native protocol (port 9000) \u2014 faster and what clickhouse-client, clickhouse-go and clickhouse-driver prefer. Reachable from inside the project only; the port is never published to the host."
8600
+ help: "Open the control plane in your browser."
7980
8601
  },
7981
8602
  {
7982
- id: "user",
7983
- label: "User",
7984
- source: "env:clickhouse:CLICKHOUSE_USER",
7985
- service: "clickhouse",
7986
- envKey: "CLICKHOUSE_USER",
7987
- recommended: true,
7988
- width: "half"
8603
+ id: "username",
8604
+ label: "Owner username",
8605
+ source: "env:console:CONSOLE_USERNAME"
7989
8606
  },
7990
8607
  {
7991
8608
  id: "password",
7992
- label: "Password",
7993
- source: "env:clickhouse:CLICKHOUSE_PASSWORD",
7994
- service: "clickhouse",
7995
- envKey: "CLICKHOUSE_PASSWORD",
7996
- secret: true,
7997
- recommended: true,
7998
- width: "half"
7999
- },
8000
- {
8001
- id: "database",
8002
- label: "Database",
8003
- source: "env:clickhouse:CLICKHOUSE_DB",
8004
- service: "clickhouse",
8005
- envKey: "CLICKHOUSE_DATABASE",
8006
- width: "half"
8609
+ label: "Owner password",
8610
+ source: "env:console:CONSOLE_PASSWORD",
8611
+ secret: true
8007
8612
  }
8008
8613
  ]
8009
8614
  },
8010
- provides: [
8011
- {
8012
- id: "clickhouse",
8013
- outputRefs: [
8014
- "httpUrl",
8015
- "httpDsn",
8016
- "user",
8017
- "password",
8018
- "database"
8019
- ],
8020
- category: "database"
8021
- }
8022
- ],
8023
8615
  endpoints: [
8024
8616
  {
8025
- service: "ch-ui",
8026
- port: 3488,
8027
- label: "Console",
8617
+ service: "console",
8618
+ port: 8787,
8619
+ label: "MindWire Console",
8028
8620
  kind: "http",
8029
- required: true,
8030
- scope: "public",
8031
8621
  defaultMode: "domain"
8032
- },
8033
- {
8034
- service: "clickhouse",
8035
- port: 8123,
8036
- label: "HTTP API",
8037
- kind: "http",
8038
- scope: "public",
8039
- defaultMode: "port"
8040
- },
8041
- {
8042
- service: "clickhouse",
8043
- port: 9e3,
8044
- label: "Native protocol",
8045
- kind: "tcp",
8046
- scope: "internal",
8047
- defaultMode: "internal",
8048
- allowedModes: [
8049
- "internal"
8050
- ]
8051
8622
  }
8052
8623
  ]
8053
8624
  }
@@ -22928,6 +23499,25 @@ var planLimitsSchema = external_exports.object({
22928
23499
  * `null` = uncapped (enterprise).
22929
23500
  */
22930
23501
  maxResourceTier: external_exports.enum(RESOURCE_TIER_ORDER).nullable(),
23502
+ /**
23503
+ * App runtime included per month, in COMPUTE MINUTES — one minute of a `low`
23504
+ * machine. Bigger machines burn proportionally more (`computeUnitsPerMinute`:
23505
+ * medium 2×, high 4×, xlarge 8×), so one published number covers every size
23506
+ * without quoting a separate allowance per tier of machine.
23507
+ *
23508
+ * This replaced an authored `credits` blob whose numbers meant nothing: 60,000
23509
+ * credits against a 43,200-minute month, on a tier advertising 50 running
23510
+ * services — i.e. not enough to run ONE app around the clock. The unit now has
23511
+ * a definition, and `planMonthlyCredits()` derives the Oblien grant from it.
23512
+ *
23513
+ * `runningServices` is a CONCURRENCY cap, this is the METER. A tier may allow
23514
+ * more always-on apps than its minutes cover; that is what "up to" means and is
23515
+ * how metered compute works everywhere.
23516
+ *
23517
+ * 0 is meaningful and correct for a static-only tier: the edge serves static
23518
+ * sites, `runningServices` is already 0, so no app compute is consumed.
23519
+ */
23520
+ computeMinutesPerMonth: limitNumber,
22931
23521
  buildMinutesPerMonth: limitNumber,
22932
23522
  freeSubdomains: limitNumber,
22933
23523
  customDomains: limitNumber,
@@ -22949,10 +23539,6 @@ var planSchema = external_exports.object({
22949
23539
  support: external_exports.enum(["community", "email", "priority", "dedicated"]),
22950
23540
  /** Tier whose features this one builds on — drives the "Everything in X" line. */
22951
23541
  inherits: external_exports.string().min(1).optional(),
22952
- credits: external_exports.object({
22953
- /** Milli-credits granted per period; null = granted by hand (enterprise). */
22954
- monthlyMilli: external_exports.number().int().positive().nullable()
22955
- }),
22956
23542
  limits: planLimitsSchema,
22957
23543
  /** Copy keys, in display order. Each must exist in `locales/en.json#features`. */
22958
23544
  features: external_exports.array(external_exports.string().min(1)).min(1)
@@ -23025,6 +23611,20 @@ var pricingCatalogSchema = external_exports.object({
23025
23611
  campaigns: external_exports.array(campaignSchema),
23026
23612
  plans: external_exports.array(planSchema).min(1),
23027
23613
  creditPacks: external_exports.array(creditPackSchema),
23614
+ /**
23615
+ * Copy for what EVERY cloud tier includes, stated once instead of repeated
23616
+ * down each column.
23617
+ *
23618
+ * This exists because the per-tier lists had become the differentiator, and
23619
+ * they were differentiating on nothing: the audit log, live logs and metrics
23620
+ * are ungated for everyone, so listing them only under Team implied a paywall
23621
+ * that no code enforces. A tier's bullets are now its NUMBERS; anything true
23622
+ * everywhere belongs here.
23623
+ *
23624
+ * Only put a key here that is actually shipped and reachable on cloud — the
23625
+ * defect this block replaces was advertising capability, not describing it.
23626
+ */
23627
+ standard: external_exports.object({ features: external_exports.array(external_exports.string().min(1)).min(1) }),
23028
23628
  selfHosted: external_exports.object({ features: external_exports.array(external_exports.string().min(1)).min(1) })
23029
23629
  }).superRefine((data, ctx) => {
23030
23630
  if (data.schemaVersion > MAX_SUPPORTED_PRICING_SCHEMA) {
@@ -23123,7 +23723,13 @@ var pricingCopySchema = external_exports.object({
23123
23723
  billedMonthly: external_exports.string(),
23124
23724
  campaignBadge: external_exports.string(),
23125
23725
  campaignEnds: external_exports.string(),
23126
- wasPrice: external_exports.string()
23726
+ wasPrice: external_exports.string(),
23727
+ /** Heading over the `standard.features` block. Lives here, not in an app
23728
+ * dictionary, because the marketing site has no i18n framework. */
23729
+ standardTitle: external_exports.string(),
23730
+ /** What a top-up pack buys, in recognisable units. Interpolates
23731
+ * `{appHours}` and `{buildMinutes}`, both derived from the catalog. */
23732
+ creditPackNote: external_exports.string()
23127
23733
  }).partial().optional()
23128
23734
  });
23129
23735
 
@@ -23159,9 +23765,6 @@ var pricing_default = {
23159
23765
  popular: false,
23160
23766
  contactSales: null,
23161
23767
  support: "community",
23162
- credits: {
23163
- monthlyMilli: 5e5
23164
- },
23165
23768
  limits: {
23166
23769
  workloads: [
23167
23770
  "static"
@@ -23170,7 +23773,8 @@ var pricing_default = {
23170
23773
  runningServices: 0,
23171
23774
  maxProjects: 3,
23172
23775
  maxResourceTier: "low",
23173
- buildMinutesPerMonth: 15,
23776
+ computeMinutesPerMonth: 0,
23777
+ buildMinutesPerMonth: 500,
23174
23778
  freeSubdomains: 10,
23175
23779
  customDomains: null,
23176
23780
  seats: null
@@ -23178,10 +23782,8 @@ var pricing_default = {
23178
23782
  features: [
23179
23783
  "staticOnly",
23180
23784
  "buildMinutes",
23181
- "freeSubdomains",
23182
- "customDomains",
23183
23785
  "projects",
23184
- "unlimitedSeats",
23786
+ "freeSubdomains",
23185
23787
  "communitySupport"
23186
23788
  ]
23187
23789
  },
@@ -23199,9 +23801,6 @@ var pricing_default = {
23199
23801
  contactSales: null,
23200
23802
  support: "email",
23201
23803
  inherits: "free",
23202
- credits: {
23203
- monthlyMilli: 2e6
23204
- },
23205
23804
  limits: {
23206
23805
  workloads: [
23207
23806
  "static",
@@ -23210,9 +23809,10 @@ var pricing_default = {
23210
23809
  ],
23211
23810
  services: true,
23212
23811
  runningServices: 3,
23213
- maxProjects: 5,
23812
+ maxProjects: 10,
23214
23813
  maxResourceTier: "medium",
23215
- buildMinutesPerMonth: 400,
23814
+ computeMinutesPerMonth: 15e4,
23815
+ buildMinutesPerMonth: 3e3,
23216
23816
  freeSubdomains: 25,
23217
23817
  customDomains: null,
23218
23818
  seats: null
@@ -23220,15 +23820,11 @@ var pricing_default = {
23220
23820
  features: [
23221
23821
  "everythingIn",
23222
23822
  "anyWorkload",
23223
- "managedServices",
23224
23823
  "runningServices",
23824
+ "computeMinutes",
23825
+ "buildMinutes",
23225
23826
  "power",
23226
23827
  "projects",
23227
- "buildMinutes",
23228
- "credits",
23229
- "meteredCredits",
23230
- "freeSubdomains",
23231
- "previewDeploys",
23232
23828
  "emailSupport"
23233
23829
  ]
23234
23830
  },
@@ -23246,9 +23842,6 @@ var pricing_default = {
23246
23842
  contactSales: null,
23247
23843
  support: "priority",
23248
23844
  inherits: "starter",
23249
- credits: {
23250
- monthlyMilli: 1e7
23251
- },
23252
23845
  limits: {
23253
23846
  workloads: [
23254
23847
  "static",
@@ -23257,9 +23850,10 @@ var pricing_default = {
23257
23850
  ],
23258
23851
  services: true,
23259
23852
  runningServices: 10,
23260
- maxProjects: 25,
23853
+ maxProjects: 50,
23261
23854
  maxResourceTier: "high",
23262
- buildMinutesPerMonth: 2e3,
23855
+ computeMinutesPerMonth: 6e5,
23856
+ buildMinutesPerMonth: 12e3,
23263
23857
  freeSubdomains: 100,
23264
23858
  customDomains: null,
23265
23859
  seats: null
@@ -23267,15 +23861,10 @@ var pricing_default = {
23267
23861
  features: [
23268
23862
  "everythingIn",
23269
23863
  "runningServices",
23864
+ "computeMinutes",
23865
+ "buildMinutes",
23270
23866
  "power",
23271
23867
  "projects",
23272
- "buildMinutes",
23273
- "credits",
23274
- "meteredCredits",
23275
- "freeSubdomains",
23276
- "backups",
23277
- "observability",
23278
- "mailServer",
23279
23868
  "prioritySupport"
23280
23869
  ]
23281
23870
  },
@@ -23293,9 +23882,6 @@ var pricing_default = {
23293
23882
  contactSales: null,
23294
23883
  support: "priority",
23295
23884
  inherits: "pro",
23296
- credits: {
23297
- monthlyMilli: 6e7
23298
- },
23299
23885
  limits: {
23300
23886
  workloads: [
23301
23887
  "static",
@@ -23306,7 +23892,8 @@ var pricing_default = {
23306
23892
  runningServices: 50,
23307
23893
  maxProjects: null,
23308
23894
  maxResourceTier: "xlarge",
23309
- buildMinutesPerMonth: 6e3,
23895
+ computeMinutesPerMonth: 22e5,
23896
+ buildMinutesPerMonth: 36e3,
23310
23897
  freeSubdomains: null,
23311
23898
  customDomains: null,
23312
23899
  seats: null
@@ -23314,14 +23901,11 @@ var pricing_default = {
23314
23901
  features: [
23315
23902
  "everythingIn",
23316
23903
  "runningServices",
23904
+ "computeMinutes",
23905
+ "buildMinutes",
23317
23906
  "power",
23318
23907
  "projectsUnlimited",
23319
- "buildMinutes",
23320
- "credits",
23321
- "meteredCredits",
23322
23908
  "freeSubdomainsUnlimited",
23323
- "auditLog",
23324
- "sharedChannel",
23325
23909
  "prioritySupport"
23326
23910
  ]
23327
23911
  },
@@ -23339,9 +23923,6 @@ var pricing_default = {
23339
23923
  contactSales: "mailto:sales@openship.io",
23340
23924
  support: "dedicated",
23341
23925
  inherits: "team",
23342
- credits: {
23343
- monthlyMilli: null
23344
- },
23345
23926
  limits: {
23346
23927
  workloads: [
23347
23928
  "static",
@@ -23352,6 +23933,7 @@ var pricing_default = {
23352
23933
  runningServices: null,
23353
23934
  maxProjects: null,
23354
23935
  maxResourceTier: null,
23936
+ computeMinutesPerMonth: null,
23355
23937
  buildMinutesPerMonth: null,
23356
23938
  freeSubdomains: null,
23357
23939
  customDomains: null,
@@ -23390,6 +23972,15 @@ var pricing_default = {
23390
23972
  sortOrder: 30
23391
23973
  }
23392
23974
  ],
23975
+ standard: {
23976
+ features: [
23977
+ "gitDeploys",
23978
+ "customDomains",
23979
+ "deployLogs",
23980
+ "auditLog",
23981
+ "unlimitedSeats"
23982
+ ]
23983
+ },
23393
23984
  selfHosted: {
23394
23985
  features: [
23395
23986
  "selfHostedOpenSource",
@@ -23429,20 +24020,20 @@ var en_default2 = {
23429
24020
  features: {
23430
24021
  everythingIn: "Everything in {inherited}, plus:",
23431
24022
  staticOnly: "Static sites only \u2014 SSG, SPA, and plain HTML",
23432
- anyWorkload: "Run anything \u2014 containers, Compose stacks, and workers",
23433
- managedServices: "Managed Postgres, Redis, and the full app catalog",
24023
+ anyWorkload: "Run anything \u2014 containers, Compose, workers, Postgres, Redis",
24024
+ runningServices: "{runningServices} running apps, databases or workers",
24025
+ computeMinutes: "{computeMinutes} compute minutes per month",
23434
24026
  buildMinutes: "{buildMinutes} build minutes per month",
23435
- credits: "{credits} compute credits per month",
24027
+ power: "Up to {powerCpu} vCPU and {powerRamGb} GB RAM per app",
24028
+ projects: "{maxProjects} projects",
24029
+ projectsUnlimited: "Unlimited projects",
23436
24030
  freeSubdomains: "{freeSubdomains} free {freeDomainSuffix} subdomains",
23437
24031
  freeSubdomainsUnlimited: "Unlimited free {freeDomainSuffix} subdomains",
24032
+ gitDeploys: "Deploy from GitHub, GitLab, or any Git remote",
23438
24033
  customDomains: "Unlimited custom domains, TLS included",
24034
+ deployLogs: "Live build and deploy logs",
24035
+ auditLog: "Audit log of every change",
23439
24036
  unlimitedSeats: "Unlimited team members \u2014 never pay per seat",
23440
- previewDeploys: "Preview deploys on every push",
23441
- backups: "Daily backups with point-in-time recovery",
23442
- observability: "Live logs, metrics, and health alerts",
23443
- mailServer: "Built-in mail server \u2014 SMTP, IMAP, and webmail",
23444
- auditLog: "Audit log with extended retention",
23445
- sharedChannel: "Shared Slack or Discord channel",
23446
24037
  sso: "SSO and SAML",
23447
24038
  customLimits: "Custom limits, negotiated per contract",
23448
24039
  dedicatedInfra: "Dedicated infrastructure and regions",
@@ -23455,12 +24046,7 @@ var en_default2 = {
23455
24046
  selfHostedUnlimited: "Unlimited deploys, projects, servers, and domains",
23456
24047
  selfHostedServices: "Every managed service \u2014 Postgres, Redis, mail",
23457
24048
  selfHostedClients: "CLI, web, and desktop \u2014 one backend",
23458
- selfHostedFreeSubdomains: "{freeSubdomains} free {freeDomainSuffix} subdomains when you connect to Cloud",
23459
- runningServices: "{runningServices} running services \u2014 apps, databases, workers",
23460
- power: "Up to {powerCpu} vCPU and {powerRamGb} GB RAM per service",
23461
- projects: "{maxProjects} projects",
23462
- projectsUnlimited: "Unlimited projects",
23463
- meteredCredits: "Compute, bandwidth and storage all draw from your monthly credits"
24049
+ selfHostedFreeSubdomains: "{freeSubdomains} free {freeDomainSuffix} subdomains when you connect to Cloud"
23464
24050
  },
23465
24051
  selfHosted: {
23466
24052
  name: "Self-hosted",
@@ -23470,9 +24056,9 @@ var en_default2 = {
23470
24056
  cta: "Start self-hosting"
23471
24057
  },
23472
24058
  creditPacks: {
23473
- pack_5k: "{credits} credits",
23474
- pack_25k: "{credits} credits",
23475
- pack_100k: "{credits} credits"
24059
+ pack_5k: "{computeMinutes} compute minutes",
24060
+ pack_25k: "{computeMinutes} compute minutes",
24061
+ pack_100k: "{computeMinutes} compute minutes"
23476
24062
  },
23477
24063
  ui: {
23478
24064
  perMonth: "/mo",
@@ -23488,7 +24074,9 @@ var en_default2 = {
23488
24074
  billedMonthly: "billed monthly, cancel anytime",
23489
24075
  campaignBadge: "{percentOff}% off",
23490
24076
  campaignEnds: "Offer ends {date}",
23491
- wasPrice: "was {price}"
24077
+ wasPrice: "was {price}",
24078
+ standardTitle: "In every plan",
24079
+ creditPackNote: "\u2248 {appHours} hours of a small app, or {buildMinutes} build minutes"
23492
24080
  }
23493
24081
  };
23494
24082
 
@@ -23519,20 +24107,20 @@ var ar_default2 = {
23519
24107
  features: {
23520
24108
  everythingIn: "\u0643\u0644 \u0645\u0627 \u0641\u064A \u062E\u0637\u0629 \xAB{inherited}\xBB\u060C \u0628\u0627\u0644\u0625\u0636\u0627\u0641\u0629 \u0625\u0644\u0649:",
23521
24109
  staticOnly: "\u0627\u0644\u0645\u0648\u0627\u0642\u0639 \u0627\u0644\u062B\u0627\u0628\u062A\u0629 \u0641\u0642\u0637 \u2014 SSG \u0648SPA \u0648HTML \u0639\u0627\u062F\u064A",
23522
- anyWorkload: "\u0634\u063A\u0651\u0644 \u0623\u064A \u0634\u064A\u0621 \u2014 \u062D\u0627\u0648\u064A\u0627\u062A \u0648\u062D\u0650\u0632\u0645 Compose \u0648\u0639\u0645\u0644\u064A\u0627\u062A \u062E\u0644\u0641\u064A\u0629",
23523
- managedServices: "\u062E\u062F\u0645\u0627\u062A Postgres \u0648Redis \u0645\u064F\u062F\u0627\u0631\u0629\u060C \u0645\u0639 \u0643\u062A\u0627\u0644\u0648\u062C \u0627\u0644\u062A\u0637\u0628\u064A\u0642\u0627\u062A \u0627\u0644\u0643\u0627\u0645\u0644",
24110
+ anyWorkload: "\u0634\u063A\u0651\u0644 \u0623\u064A \u0634\u064A\u0621 \u2014 \u062D\u0627\u0648\u064A\u0627\u062A \u0648Compose \u0648\u0639\u0645\u0644\u064A\u0627\u062A \u062E\u0644\u0641\u064A\u0629 \u0648Postgres \u0648Redis",
24111
+ runningServices: "{runningServices} \u062A\u0637\u0628\u064A\u0642 \u0623\u0648 \u0642\u0627\u0639\u062F\u0629 \u0628\u064A\u0627\u0646\u0627\u062A \u0623\u0648 \u0639\u0645\u0644\u064A\u0629 \u062E\u0644\u0641\u064A\u0629 \u0642\u064A\u062F \u0627\u0644\u062A\u0634\u063A\u064A\u0644",
24112
+ computeMinutes: "{computeMinutes} \u062F\u0642\u064A\u0642\u0629 \u062D\u0648\u0633\u0628\u0629 \u0634\u0647\u0631\u064A\u064B\u0627",
23524
24113
  buildMinutes: "{buildMinutes} \u062F\u0642\u064A\u0642\u0629 \u0628\u0646\u0627\u0621 \u0634\u0647\u0631\u064A\u064B\u0627",
23525
- credits: "{credits} \u0648\u062D\u062F\u0629 \u0631\u0635\u064A\u062F \u062D\u0648\u0633\u0628\u0629 \u0634\u0647\u0631\u064A\u064B\u0627",
24114
+ power: "\u062D\u062A\u0649 {powerCpu} vCPU \u0648{powerRamGb} GB RAM \u0644\u0643\u0644 \u062A\u0637\u0628\u064A\u0642",
24115
+ projects: "{maxProjects} \u0645\u0634\u0631\u0648\u0639",
24116
+ projectsUnlimited: "\u0645\u0634\u0627\u0631\u064A\u0639 \u063A\u064A\u0631 \u0645\u062D\u062F\u0648\u062F\u0629",
23526
24117
  freeSubdomains: "{freeSubdomains} \u0646\u0637\u0627\u0642 \u0641\u0631\u0639\u064A \u0645\u062C\u0627\u0646\u064A \u0639\u0644\u0649 {freeDomainSuffix}",
23527
24118
  freeSubdomainsUnlimited: "\u0646\u0637\u0627\u0642\u0627\u062A \u0641\u0631\u0639\u064A\u0629 \u0645\u062C\u0627\u0646\u064A\u0629 \u063A\u064A\u0631 \u0645\u062D\u062F\u0648\u062F\u0629 \u0639\u0644\u0649 {freeDomainSuffix}",
24119
+ gitDeploys: "\u0627\u0644\u0646\u0634\u0631 \u0645\u0646 GitHub \u0623\u0648 GitLab \u0623\u0648 \u0623\u064A \u0645\u0633\u062A\u0648\u062F\u0639 Git",
23528
24120
  customDomains: "\u0646\u0637\u0627\u0642\u0627\u062A \u0645\u062E\u0635\u0651\u0635\u0629 \u063A\u064A\u0631 \u0645\u062D\u062F\u0648\u062F\u0629\u060C \u0645\u0639 TLS",
24121
+ deployLogs: "\u0633\u062C\u0644\u0627\u062A \u062D\u064A\u0651\u0629 \u0644\u0644\u0628\u0646\u0627\u0621 \u0648\u0627\u0644\u0646\u0634\u0631",
24122
+ auditLog: "\u0633\u062C\u0644 \u062A\u062F\u0642\u064A\u0642 \u0644\u0643\u0644 \u062A\u063A\u064A\u064A\u0631",
23529
24123
  unlimitedSeats: "\u0623\u0639\u0636\u0627\u0621 \u0641\u0631\u064A\u0642 \u063A\u064A\u0631 \u0645\u062D\u062F\u0648\u062F\u064A\u0646 \u2014 \u0628\u0644\u0627 \u0631\u0633\u0648\u0645 \u0639\u0644\u0649 \u0643\u0644 \u0645\u0633\u062A\u062E\u062F\u0645",
23530
- previewDeploys: "\u0646\u0634\u0631 \u0645\u0639\u0627\u064A\u0646\u0629 \u0639\u0646\u062F \u0643\u0644 \u062F\u0641\u0639 \u0644\u0644\u0643\u0648\u062F",
23531
- backups: "\u0646\u0633\u062E \u0627\u062D\u062A\u064A\u0627\u0637\u064A \u064A\u0648\u0645\u064A \u0645\u0639 \u0627\u0633\u062A\u0639\u0627\u062F\u0629 \u0625\u0644\u0649 \u0644\u062D\u0638\u0629 \u0632\u0645\u0646\u064A\u0629 \u0645\u062D\u062F\u0651\u062F\u0629",
23532
- observability: "\u0633\u062C\u0644\u0627\u062A \u062D\u064A\u0651\u0629 \u0648\u0645\u0642\u0627\u064A\u064A\u0633 \u0648\u062A\u0646\u0628\u064A\u0647\u0627\u062A \u0644\u0635\u062D\u0629 \u0627\u0644\u062E\u062F\u0645\u0629",
23533
- mailServer: "\u062E\u0627\u062F\u0645 \u0628\u0631\u064A\u062F \u0645\u062F\u0645\u062C \u2014 SMTP \u0648IMAP \u0648\u0628\u0631\u064A\u062F \u0639\u0644\u0649 \u0627\u0644\u0648\u064A\u0628",
23534
- auditLog: "\u0633\u062C\u0644 \u062A\u062F\u0642\u064A\u0642 \u0628\u0645\u062F\u0629 \u062D\u0641\u0638 \u0645\u0645\u062A\u062F\u0629",
23535
- sharedChannel: "\u0642\u0646\u0627\u0629 \u0645\u0634\u062A\u0631\u0643\u0629 \u0639\u0644\u0649 Slack \u0623\u0648 Discord",
23536
24124
  sso: "SSO \u0648SAML",
23537
24125
  customLimits: "\u062D\u062F\u0648\u062F \u0645\u062E\u0635\u0651\u0635\u0629 \u064A\u064F\u062A\u0641\u0642 \u0639\u0644\u064A\u0647\u0627 \u0641\u064A \u0627\u0644\u0639\u0642\u062F",
23538
24126
  dedicatedInfra: "\u0628\u0646\u064A\u0629 \u062A\u062D\u062A\u064A\u0629 \u0648\u0645\u0646\u0627\u0637\u0642 \u0645\u062E\u0635\u0651\u0635\u0629",
@@ -23545,12 +24133,7 @@ var ar_default2 = {
23545
24133
  selfHostedUnlimited: "\u0639\u0645\u0644\u064A\u0627\u062A \u0646\u0634\u0631 \u0648\u0645\u0634\u0627\u0631\u064A\u0639 \u0648\u062E\u0648\u0627\u062F\u0645 \u0648\u0646\u0637\u0627\u0642\u0627\u062A \u063A\u064A\u0631 \u0645\u062D\u062F\u0648\u062F\u0629",
23546
24134
  selfHostedServices: "\u0643\u0644 \u0627\u0644\u062E\u062F\u0645\u0627\u062A \u0627\u0644\u0645\u064F\u062F\u0627\u0631\u0629 \u2014 Postgres \u0648Redis \u0648\u0627\u0644\u0628\u0631\u064A\u062F",
23547
24135
  selfHostedClients: "CLI \u0648\u0648\u0627\u062C\u0647\u0629 \u0648\u064A\u0628 \u0648\u062A\u0637\u0628\u064A\u0642 \u0633\u0637\u062D \u0645\u0643\u062A\u0628 \u2014 \u062E\u062F\u0645\u0629 \u062E\u0644\u0641\u064A\u0629 \u0648\u0627\u062D\u062F\u0629",
23548
- selfHostedFreeSubdomains: "{freeSubdomains} \u0646\u0637\u0627\u0642 \u0641\u0631\u0639\u064A \u0645\u062C\u0627\u0646\u064A \u0639\u0644\u0649 {freeDomainSuffix} \u0639\u0646\u062F \u0627\u0644\u0631\u0628\u0637 \u0645\u0639 Openship Cloud",
23549
- runningServices: "{runningServices} \u062E\u062F\u0645\u0629 \u0642\u064A\u062F \u0627\u0644\u062A\u0634\u063A\u064A\u0644 \u2014 \u062A\u0637\u0628\u064A\u0642\u0627\u062A \u0648\u0642\u0648\u0627\u0639\u062F \u0628\u064A\u0627\u0646\u0627\u062A \u0648\u0639\u0645\u0644\u064A\u0627\u062A \u062E\u0644\u0641\u064A\u0629",
23550
- power: "\u062D\u062A\u0649 {powerCpu} vCPU \u0648{powerRamGb} GB RAM \u0644\u0643\u0644 \u062E\u062F\u0645\u0629",
23551
- projects: "{maxProjects} \u0645\u0634\u0631\u0648\u0639",
23552
- projectsUnlimited: "\u0645\u0634\u0627\u0631\u064A\u0639 \u063A\u064A\u0631 \u0645\u062D\u062F\u0648\u062F\u0629",
23553
- meteredCredits: "\u0627\u0644\u062D\u0648\u0633\u0628\u0629 \u0648\u0627\u0644\u0646\u0637\u0627\u0642 \u0627\u0644\u062A\u0631\u062F\u062F\u064A \u0648\u0627\u0644\u062A\u062E\u0632\u064A\u0646 \u062A\u064F\u062E\u0635\u0645 \u0643\u0644\u0647\u0627 \u0645\u0646 \u0631\u0635\u064A\u062F\u0643 \u0627\u0644\u0634\u0647\u0631\u064A"
24136
+ selfHostedFreeSubdomains: "{freeSubdomains} \u0646\u0637\u0627\u0642 \u0641\u0631\u0639\u064A \u0645\u062C\u0627\u0646\u064A \u0639\u0644\u0649 {freeDomainSuffix} \u0639\u0646\u062F \u0627\u0644\u0631\u0628\u0637 \u0645\u0639 Openship Cloud"
23554
24137
  },
23555
24138
  selfHosted: {
23556
24139
  name: "\u0627\u0633\u062A\u0636\u0627\u0641\u0629 \u0630\u0627\u062A\u064A\u0629",
@@ -23560,9 +24143,9 @@ var ar_default2 = {
23560
24143
  cta: "\u0627\u0628\u062F\u0623 \u0627\u0644\u0627\u0633\u062A\u0636\u0627\u0641\u0629 \u0627\u0644\u0630\u0627\u062A\u064A\u0629"
23561
24144
  },
23562
24145
  creditPacks: {
23563
- pack_5k: "{credits} \u0648\u062D\u062F\u0629 \u0631\u0635\u064A\u062F",
23564
- pack_25k: "{credits} \u0648\u062D\u062F\u0629 \u0631\u0635\u064A\u062F",
23565
- pack_100k: "{credits} \u0648\u062D\u062F\u0629 \u0631\u0635\u064A\u062F"
24146
+ pack_5k: "{computeMinutes} \u062F\u0642\u064A\u0642\u0629 \u062D\u0648\u0633\u0628\u0629",
24147
+ pack_25k: "{computeMinutes} \u062F\u0642\u064A\u0642\u0629 \u062D\u0648\u0633\u0628\u0629",
24148
+ pack_100k: "{computeMinutes} \u062F\u0642\u064A\u0642\u0629 \u062D\u0648\u0633\u0628\u0629"
23566
24149
  },
23567
24150
  ui: {
23568
24151
  perMonth: "/\u0634\u0647\u0631",
@@ -23578,7 +24161,9 @@ var ar_default2 = {
23578
24161
  billedMonthly: "\u0641\u0627\u062A\u0648\u0631\u0629 \u0634\u0647\u0631\u064A\u0629\u060C \u0648\u0627\u0644\u0625\u0644\u063A\u0627\u0621 \u0645\u062A\u0627\u062D \u0641\u064A \u0623\u064A \u0648\u0642\u062A",
23579
24162
  campaignBadge: "\u062E\u0635\u0645 {percentOff}%",
23580
24163
  campaignEnds: "\u064A\u0646\u062A\u0647\u064A \u0627\u0644\u0639\u0631\u0636 \u0641\u064A {date}",
23581
- wasPrice: "\u0633\u0627\u0628\u0642\u064B\u0627 {price}"
24164
+ wasPrice: "\u0633\u0627\u0628\u0642\u064B\u0627 {price}",
24165
+ standardTitle: "\u0641\u064A \u0643\u0644 \u0627\u0644\u062E\u0637\u0637",
24166
+ creditPackNote: "\u2248 {appHours} \u0633\u0627\u0639\u0629 \u0644\u062A\u0637\u0628\u064A\u0642 \u0635\u063A\u064A\u0631\u060C \u0623\u0648 {buildMinutes} \u062F\u0642\u064A\u0642\u0629 \u0628\u0646\u0627\u0621"
23582
24167
  }
23583
24168
  };
23584
24169
 
@@ -23609,20 +24194,20 @@ var de_default2 = {
23609
24194
  features: {
23610
24195
  everythingIn: "Alles aus {inherited}, plus:",
23611
24196
  staticOnly: "Nur statische Websites \u2013 SSG, SPA und reines HTML",
23612
- anyWorkload: "Beliebige Workloads \u2013 Container, Compose-Stacks und Worker",
23613
- managedServices: "Managed Postgres, Redis und der komplette App-Katalog",
24197
+ anyWorkload: "Beliebige Workloads \u2013 Container, Compose, Worker, Postgres, Redis",
24198
+ runningServices: "{runningServices} laufende Apps, Datenbanken oder Worker",
24199
+ computeMinutes: "{computeMinutes} Compute-Minuten pro Monat",
23614
24200
  buildMinutes: "{buildMinutes} Build-Minuten pro Monat",
23615
- credits: "{credits} Compute-Credits pro Monat",
24201
+ power: "Bis zu {powerCpu} vCPU und {powerRamGb} GB RAM pro App",
24202
+ projects: "{maxProjects} Projekte",
24203
+ projectsUnlimited: "Unbegrenzt viele Projekte",
23616
24204
  freeSubdomains: "{freeSubdomains} kostenlose Subdomains unter {freeDomainSuffix}",
23617
24205
  freeSubdomainsUnlimited: "Unbegrenzt viele kostenlose Subdomains unter {freeDomainSuffix}",
24206
+ gitDeploys: "Deployen von GitHub, GitLab oder jedem Git-Remote",
23618
24207
  customDomains: "Unbegrenzt viele eigene Domains, TLS inklusive",
24208
+ deployLogs: "Live-Logs f\xFCr Build und Deployment",
24209
+ auditLog: "Audit-Log f\xFCr jede \xC4nderung",
23619
24210
  unlimitedSeats: "Unbegrenzt viele Teammitglieder \u2013 nie Kosten pro Nutzer",
23620
- previewDeploys: "Preview-Deploys bei jedem Push",
23621
- backups: "T\xE4gliche Backups mit Point-in-Time-Recovery",
23622
- observability: "Live-Logs, Metriken und Health-Alerts",
23623
- mailServer: "Integrierter Mailserver \u2013 SMTP, IMAP und Webmail",
23624
- auditLog: "Audit-Log mit verl\xE4ngerter Aufbewahrungsdauer",
23625
- sharedChannel: "Gemeinsamer Slack- oder Discord-Channel",
23626
24211
  sso: "SSO und SAML",
23627
24212
  customLimits: "Individuelle Limits, vertraglich vereinbart",
23628
24213
  dedicatedInfra: "Dedizierte Infrastruktur und Regionen",
@@ -23635,12 +24220,7 @@ var de_default2 = {
23635
24220
  selfHostedUnlimited: "Unbegrenzt viele Deploys, Projekte, Server und Domains",
23636
24221
  selfHostedServices: "Alle Managed Services \u2013 Postgres, Redis, Mail",
23637
24222
  selfHostedClients: "CLI, Web und Desktop \u2013 ein Backend",
23638
- selfHostedFreeSubdomains: "{freeSubdomains} kostenlose Subdomains unter {freeDomainSuffix} bei Verbindung mit Openship Cloud",
23639
- runningServices: "{runningServices} laufende Services \u2013 Apps, Datenbanken, Worker",
23640
- power: "Bis zu {powerCpu} vCPU und {powerRamGb} GB RAM pro Service",
23641
- projects: "{maxProjects} Projekte",
23642
- projectsUnlimited: "Unbegrenzt viele Projekte",
23643
- meteredCredits: "Compute, Daten\xFCbertragung und Speicher werden alle \xFCber die monatlichen Credits abgerechnet"
24223
+ selfHostedFreeSubdomains: "{freeSubdomains} kostenlose Subdomains unter {freeDomainSuffix} bei Verbindung mit Openship Cloud"
23644
24224
  },
23645
24225
  selfHosted: {
23646
24226
  name: "Selbst gehostet",
@@ -23650,9 +24230,9 @@ var de_default2 = {
23650
24230
  cta: "Self-Hosting starten"
23651
24231
  },
23652
24232
  creditPacks: {
23653
- pack_5k: "{credits} Credits",
23654
- pack_25k: "{credits} Credits",
23655
- pack_100k: "{credits} Credits"
24233
+ pack_5k: "{computeMinutes} Compute-Minuten",
24234
+ pack_25k: "{computeMinutes} Compute-Minuten",
24235
+ pack_100k: "{computeMinutes} Compute-Minuten"
23656
24236
  },
23657
24237
  ui: {
23658
24238
  perMonth: "/Mon.",
@@ -23668,7 +24248,9 @@ var de_default2 = {
23668
24248
  billedMonthly: "monatliche Abrechnung, jederzeit k\xFCndbar",
23669
24249
  campaignBadge: "{percentOff} % Rabatt",
23670
24250
  campaignEnds: "Angebot endet am {date}",
23671
- wasPrice: "vorher {price}"
24251
+ wasPrice: "vorher {price}",
24252
+ standardTitle: "In jedem Plan enthalten",
24253
+ creditPackNote: "\u2248 {appHours} Stunden einer kleinen App oder {buildMinutes} Build-Minuten"
23672
24254
  }
23673
24255
  };
23674
24256
 
@@ -23699,20 +24281,20 @@ var es_default2 = {
23699
24281
  features: {
23700
24282
  everythingIn: "Todo lo de {inherited}, y adem\xE1s:",
23701
24283
  staticOnly: "Solo sitios est\xE1ticos: SSG, SPA y HTML puro",
23702
- anyWorkload: "Ejecuta lo que sea: contenedores, stacks de Compose y workers",
23703
- managedServices: "Postgres y Redis gestionados, y el cat\xE1logo completo de apps",
24284
+ anyWorkload: "Ejecuta lo que sea: contenedores, Compose, workers, Postgres, Redis",
24285
+ runningServices: "{runningServices} apps, bases de datos o workers en ejecuci\xF3n",
24286
+ computeMinutes: "{computeMinutes} minutos de c\xF3mputo al mes",
23704
24287
  buildMinutes: "{buildMinutes} minutos de build al mes",
23705
- credits: "{credits} cr\xE9ditos de c\xF3mputo al mes",
24288
+ power: "Hasta {powerCpu} vCPU y {powerRamGb} GB de RAM por app",
24289
+ projects: "{maxProjects} proyectos",
24290
+ projectsUnlimited: "Proyectos ilimitados",
23706
24291
  freeSubdomains: "{freeSubdomains} subdominios {freeDomainSuffix} gratis",
23707
24292
  freeSubdomainsUnlimited: "Subdominios {freeDomainSuffix} gratis e ilimitados",
24293
+ gitDeploys: "Despliega desde GitHub, GitLab o cualquier remoto Git",
23708
24294
  customDomains: "Dominios propios ilimitados, con TLS incluido",
24295
+ deployLogs: "Logs en vivo de build y despliegue",
24296
+ auditLog: "Registro de auditor\xEDa de cada cambio",
23709
24297
  unlimitedSeats: "Miembros de equipo ilimitados: nunca pagas por usuario",
23710
- previewDeploys: "Despliegues de vista previa en cada push",
23711
- backups: "Copias de seguridad diarias con recuperaci\xF3n a un punto en el tiempo",
23712
- observability: "Logs en vivo, m\xE9tricas y alertas de estado",
23713
- mailServer: "Servidor de correo integrado: SMTP, IMAP y webmail",
23714
- auditLog: "Registro de auditor\xEDa con retenci\xF3n ampliada",
23715
- sharedChannel: "Canal compartido en Slack o Discord",
23716
24298
  sso: "SSO y SAML",
23717
24299
  customLimits: "L\xEDmites a medida, negociados por contrato",
23718
24300
  dedicatedInfra: "Infraestructura y regiones dedicadas",
@@ -23725,12 +24307,7 @@ var es_default2 = {
23725
24307
  selfHostedUnlimited: "Despliegues, proyectos, servidores y dominios ilimitados",
23726
24308
  selfHostedServices: "Todos los servicios gestionados: Postgres, Redis, correo",
23727
24309
  selfHostedClients: "CLI, web y escritorio: un solo backend",
23728
- selfHostedFreeSubdomains: "{freeSubdomains} subdominios {freeDomainSuffix} gratis al conectarte a Cloud",
23729
- runningServices: "{runningServices} servicios en ejecuci\xF3n: apps, bases de datos, workers",
23730
- power: "Hasta {powerCpu} vCPU y {powerRamGb} GB de RAM por servicio",
23731
- projects: "{maxProjects} proyectos",
23732
- projectsUnlimited: "Proyectos ilimitados",
23733
- meteredCredits: "El c\xF3mputo, el ancho de banda y el almacenamiento se descuentan de tus cr\xE9ditos mensuales"
24310
+ selfHostedFreeSubdomains: "{freeSubdomains} subdominios {freeDomainSuffix} gratis al conectarte a Cloud"
23734
24311
  },
23735
24312
  selfHosted: {
23736
24313
  name: "Autoalojado",
@@ -23740,9 +24317,9 @@ var es_default2 = {
23740
24317
  cta: "Empezar a autoalojar"
23741
24318
  },
23742
24319
  creditPacks: {
23743
- pack_5k: "{credits} cr\xE9ditos",
23744
- pack_25k: "{credits} cr\xE9ditos",
23745
- pack_100k: "{credits} cr\xE9ditos"
24320
+ pack_5k: "{computeMinutes} minutos de c\xF3mputo",
24321
+ pack_25k: "{computeMinutes} minutos de c\xF3mputo",
24322
+ pack_100k: "{computeMinutes} minutos de c\xF3mputo"
23746
24323
  },
23747
24324
  ui: {
23748
24325
  perMonth: "/mes",
@@ -23758,7 +24335,9 @@ var es_default2 = {
23758
24335
  billedMonthly: "facturaci\xF3n mensual, cancela cuando quieras",
23759
24336
  campaignBadge: "{percentOff}% de descuento",
23760
24337
  campaignEnds: "La oferta termina el {date}",
23761
- wasPrice: "antes {price}"
24338
+ wasPrice: "antes {price}",
24339
+ standardTitle: "En todos los planes",
24340
+ creditPackNote: "\u2248 {appHours} horas de una app peque\xF1a, o {buildMinutes} minutos de build"
23762
24341
  }
23763
24342
  };
23764
24343
 
@@ -23789,20 +24368,20 @@ var fr_default2 = {
23789
24368
  features: {
23790
24369
  everythingIn: "Tout ce que contient {inherited}, plus :",
23791
24370
  staticOnly: "Sites statiques uniquement \u2014 SSG, SPA et HTML brut",
23792
- anyWorkload: "Ex\xE9cutez ce que vous voulez \u2014 conteneurs, stacks Compose et workers",
23793
- managedServices: "Postgres et Redis manag\xE9s, et tout le catalogue d'applications",
24371
+ anyWorkload: "Ex\xE9cutez tout \u2014 conteneurs, Compose, workers, Postgres, Redis",
24372
+ runningServices: "{runningServices} applications, bases de donn\xE9es ou workers actifs",
24373
+ computeMinutes: "{computeMinutes} minutes de calcul par mois",
23794
24374
  buildMinutes: "{buildMinutes} minutes de build par mois",
23795
- credits: "{credits} cr\xE9dits de calcul par mois",
24375
+ power: "Jusqu'\xE0 {powerCpu} vCPU et {powerRamGb} GB de RAM par application",
24376
+ projects: "{maxProjects} projets",
24377
+ projectsUnlimited: "Projets illimit\xE9s",
23796
24378
  freeSubdomains: "{freeSubdomains} sous-domaines {freeDomainSuffix} gratuits",
23797
24379
  freeSubdomainsUnlimited: "Sous-domaines {freeDomainSuffix} gratuits en illimit\xE9",
24380
+ gitDeploys: "D\xE9ployez depuis GitHub, GitLab ou n'importe quel d\xE9p\xF4t Git",
23798
24381
  customDomains: "Domaines personnalis\xE9s illimit\xE9s, TLS inclus",
24382
+ deployLogs: "Logs de build et de d\xE9ploiement en direct",
24383
+ auditLog: "Journal d'audit de chaque modification",
23799
24384
  unlimitedSeats: "Membres d'\xE9quipe illimit\xE9s \u2014 jamais de facturation par utilisateur",
23800
- previewDeploys: "D\xE9ploiements de pr\xE9visualisation \xE0 chaque push",
23801
- backups: "Sauvegardes quotidiennes avec restauration \xE0 un instant pr\xE9cis",
23802
- observability: "Logs en direct, m\xE9triques et alertes de sant\xE9",
23803
- mailServer: "Serveur mail int\xE9gr\xE9 \u2014 SMTP, IMAP et webmail",
23804
- auditLog: "Journal d'audit avec r\xE9tention \xE9tendue",
23805
- sharedChannel: "Canal Slack ou Discord partag\xE9",
23806
24385
  sso: "SSO et SAML",
23807
24386
  customLimits: "Limites sur mesure, n\xE9goci\xE9es par contrat",
23808
24387
  dedicatedInfra: "Infrastructure et r\xE9gions d\xE9di\xE9es",
@@ -23815,12 +24394,7 @@ var fr_default2 = {
23815
24394
  selfHostedUnlimited: "D\xE9ploiements, projets, serveurs et domaines illimit\xE9s",
23816
24395
  selfHostedServices: "Tous les services manag\xE9s \u2014 Postgres, Redis, mail",
23817
24396
  selfHostedClients: "CLI, web et desktop \u2014 un seul backend",
23818
- selfHostedFreeSubdomains: "{freeSubdomains} sous-domaines {freeDomainSuffix} gratuits en vous connectant au Cloud",
23819
- runningServices: "{runningServices} services actifs \u2014 applications, bases de donn\xE9es, workers",
23820
- power: "Jusqu'\xE0 {powerCpu} vCPU et {powerRamGb} GB de RAM par service",
23821
- projects: "{maxProjects} projets",
23822
- projectsUnlimited: "Projets illimit\xE9s",
23823
- meteredCredits: "Le calcul, la bande passante et le stockage sont tous d\xE9duits de vos cr\xE9dits mensuels"
24397
+ selfHostedFreeSubdomains: "{freeSubdomains} sous-domaines {freeDomainSuffix} gratuits en vous connectant au Cloud"
23824
24398
  },
23825
24399
  selfHosted: {
23826
24400
  name: "Auto-h\xE9berg\xE9",
@@ -23830,9 +24404,9 @@ var fr_default2 = {
23830
24404
  cta: "Commencer l'auto-h\xE9bergement"
23831
24405
  },
23832
24406
  creditPacks: {
23833
- pack_5k: "{credits} cr\xE9dits",
23834
- pack_25k: "{credits} cr\xE9dits",
23835
- pack_100k: "{credits} cr\xE9dits"
24407
+ pack_5k: "{computeMinutes} minutes de calcul",
24408
+ pack_25k: "{computeMinutes} minutes de calcul",
24409
+ pack_100k: "{computeMinutes} minutes de calcul"
23836
24410
  },
23837
24411
  ui: {
23838
24412
  perMonth: "/mois",
@@ -23848,7 +24422,9 @@ var fr_default2 = {
23848
24422
  billedMonthly: "factur\xE9 mensuellement, r\xE9siliable \xE0 tout moment",
23849
24423
  campaignBadge: "{percentOff} % de remise",
23850
24424
  campaignEnds: "L'offre se termine le {date}",
23851
- wasPrice: "avant {price}"
24425
+ wasPrice: "avant {price}",
24426
+ standardTitle: "Dans tous les forfaits",
24427
+ creditPackNote: "\u2248 {appHours} heures d'une petite application, ou {buildMinutes} minutes de build"
23852
24428
  }
23853
24429
  };
23854
24430
 
@@ -23879,20 +24455,20 @@ var ja_default2 = {
23879
24455
  features: {
23880
24456
  everythingIn: "{inherited} \u306E\u5168\u6A5F\u80FD\u306B\u52A0\u3048\u3066:",
23881
24457
  staticOnly: "\u9759\u7684\u30B5\u30A4\u30C8\u306E\u307F \u2014 SSG\u3001SPA\u3001\u7D20\u306E HTML",
23882
- anyWorkload: "\u4F55\u3067\u3082\u52D5\u304B\u305B\u308B \u2014 \u30B3\u30F3\u30C6\u30CA\u3001Compose \u30B9\u30BF\u30C3\u30AF\u3001\u30EF\u30FC\u30AB\u30FC",
23883
- managedServices: "\u30DE\u30CD\u30FC\u30B8\u30C9\u306E Postgres \u3068 Redis\u3001\u30A2\u30D7\u30EA\u30AB\u30BF\u30ED\u30B0\u3059\u3079\u3066",
24458
+ anyWorkload: "\u4F55\u3067\u3082\u52D5\u304B\u305B\u308B \u2014 \u30B3\u30F3\u30C6\u30CA\u3001Compose\u3001\u30EF\u30FC\u30AB\u30FC\u3001Postgres\u3001Redis",
24459
+ runningServices: "\u540C\u6642\u306B\u7A3C\u50CD\u3059\u308B\u30A2\u30D7\u30EA\u30FB\u30C7\u30FC\u30BF\u30D9\u30FC\u30B9\u30FB\u30EF\u30FC\u30AB\u30FC {runningServices} \u500B",
24460
+ computeMinutes: "\u6BCE\u6708 {computeMinutes} \u30B3\u30F3\u30D4\u30E5\u30FC\u30C8\u5206",
23884
24461
  buildMinutes: "\u6BCE\u6708 {buildMinutes} \u5206\u306E\u30D3\u30EB\u30C9\u6642\u9593",
23885
- credits: "\u6BCE\u6708 {credits} \u30B3\u30F3\u30D4\u30E5\u30FC\u30C8\u30AF\u30EC\u30B8\u30C3\u30C8",
24462
+ power: "\u30A2\u30D7\u30EA 1 \u3064\u3042\u305F\u308A\u6700\u5927 {powerCpu} vCPU \u3068 {powerRamGb} GB RAM",
24463
+ projects: "\u30D7\u30ED\u30B8\u30A7\u30AF\u30C8 {maxProjects} \u500B",
24464
+ projectsUnlimited: "\u30D7\u30ED\u30B8\u30A7\u30AF\u30C8\u306F\u7121\u5236\u9650",
23886
24465
  freeSubdomains: "\u7121\u6599\u306E {freeDomainSuffix} \u30B5\u30D6\u30C9\u30E1\u30A4\u30F3 {freeSubdomains} \u500B",
23887
24466
  freeSubdomainsUnlimited: "\u7121\u6599\u306E {freeDomainSuffix} \u30B5\u30D6\u30C9\u30E1\u30A4\u30F3\u304C\u7121\u5236\u9650",
24467
+ gitDeploys: "GitHub\u3001GitLab\u3001\u4EFB\u610F\u306E Git \u30EA\u30E2\u30FC\u30C8\u304B\u3089\u30C7\u30D7\u30ED\u30A4",
23888
24468
  customDomains: "\u30AB\u30B9\u30BF\u30E0\u30C9\u30E1\u30A4\u30F3\u306F\u7121\u5236\u9650\u3001TLS \u8FBC\u307F",
24469
+ deployLogs: "\u30D3\u30EB\u30C9\u3068\u30C7\u30D7\u30ED\u30A4\u306E\u30E9\u30A4\u30D6\u30ED\u30B0",
24470
+ auditLog: "\u3059\u3079\u3066\u306E\u5909\u66F4\u3092\u8A18\u9332\u3059\u308B\u76E3\u67FB\u30ED\u30B0",
23889
24471
  unlimitedSeats: "\u30C1\u30FC\u30E0\u30E1\u30F3\u30D0\u30FC\u306F\u7121\u5236\u9650 \u2014 \u4EBA\u6570\u8AB2\u91D1\u306F\u4E00\u5207\u306A\u3057",
23890
- previewDeploys: "\u30D7\u30C3\u30B7\u30E5\u3054\u3068\u306B\u30D7\u30EC\u30D3\u30E5\u30FC\u30C7\u30D7\u30ED\u30A4",
23891
- backups: "\u65E5\u6B21\u30D0\u30C3\u30AF\u30A2\u30C3\u30D7\u3068\u30DD\u30A4\u30F3\u30C8\u30A4\u30F3\u30BF\u30A4\u30E0\u30EA\u30AB\u30D0\u30EA",
23892
- observability: "\u30E9\u30A4\u30D6\u30ED\u30B0\u3001\u30E1\u30C8\u30EA\u30AF\u30B9\u3001\u30D8\u30EB\u30B9\u30A2\u30E9\u30FC\u30C8",
23893
- mailServer: "\u30E1\u30FC\u30EB\u30B5\u30FC\u30D0\u30FC\u5185\u8535 \u2014 SMTP\u3001IMAP\u3001\u30A6\u30A7\u30D6\u30E1\u30FC\u30EB",
23894
- auditLog: "\u4FDD\u6301\u671F\u9593\u3092\u5EF6\u9577\u3057\u305F\u76E3\u67FB\u30ED\u30B0",
23895
- sharedChannel: "Slack \u307E\u305F\u306F Discord \u306E\u5171\u6709\u30C1\u30E3\u30F3\u30CD\u30EB",
23896
24472
  sso: "SSO \u3068 SAML",
23897
24473
  customLimits: "\u5951\u7D04\u3054\u3068\u306B\u8ABF\u6574\u3059\u308B\u30AB\u30B9\u30BF\u30E0\u306E\u4E0A\u9650",
23898
24474
  dedicatedInfra: "\u5C02\u7528\u30A4\u30F3\u30D5\u30E9\u3068\u5C02\u7528\u30EA\u30FC\u30B8\u30E7\u30F3",
@@ -23905,12 +24481,7 @@ var ja_default2 = {
23905
24481
  selfHostedUnlimited: "\u30C7\u30D7\u30ED\u30A4\u3001\u30D7\u30ED\u30B8\u30A7\u30AF\u30C8\u3001\u30B5\u30FC\u30D0\u30FC\u3001\u30C9\u30E1\u30A4\u30F3\u3059\u3079\u3066\u7121\u5236\u9650",
23906
24482
  selfHostedServices: "\u30DE\u30CD\u30FC\u30B8\u30C9\u30B5\u30FC\u30D3\u30B9\u306F\u3059\u3079\u3066\u5229\u7528\u53EF\u80FD \u2014 Postgres\u3001Redis\u3001\u30E1\u30FC\u30EB",
23907
24483
  selfHostedClients: "CLI\u3001Web\u3001\u30C7\u30B9\u30AF\u30C8\u30C3\u30D7 \u2014 \u30D0\u30C3\u30AF\u30A8\u30F3\u30C9\u306F\u4E00\u3064",
23908
- selfHostedFreeSubdomains: "Openship Cloud \u306B\u63A5\u7D9A\u3059\u308B\u3068\u3001\u7121\u6599\u306E {freeDomainSuffix} \u30B5\u30D6\u30C9\u30E1\u30A4\u30F3\u304C {freeSubdomains} \u500B",
23909
- runningServices: "\u540C\u6642\u306B\u7A3C\u50CD\u3059\u308B\u30B5\u30FC\u30D3\u30B9 {runningServices} \u500B \u2014 \u30A2\u30D7\u30EA\u3001\u30C7\u30FC\u30BF\u30D9\u30FC\u30B9\u3001\u30EF\u30FC\u30AB\u30FC",
23910
- power: "\u30B5\u30FC\u30D3\u30B9 1 \u3064\u3042\u305F\u308A\u6700\u5927 {powerCpu} vCPU \u3068 {powerRamGb} GB RAM",
23911
- projects: "\u30D7\u30ED\u30B8\u30A7\u30AF\u30C8 {maxProjects} \u500B",
23912
- projectsUnlimited: "\u30D7\u30ED\u30B8\u30A7\u30AF\u30C8\u306F\u7121\u5236\u9650",
23913
- meteredCredits: "\u30B3\u30F3\u30D4\u30E5\u30FC\u30C8\u3001\u5E2F\u57DF\u5E45\u3001\u30B9\u30C8\u30EC\u30FC\u30B8\u306F\u3059\u3079\u3066\u6BCE\u6708\u306E\u30AF\u30EC\u30B8\u30C3\u30C8\u304B\u3089\u6D88\u8CBB\u3055\u308C\u307E\u3059"
24484
+ selfHostedFreeSubdomains: "Openship Cloud \u306B\u63A5\u7D9A\u3059\u308B\u3068\u3001\u7121\u6599\u306E {freeDomainSuffix} \u30B5\u30D6\u30C9\u30E1\u30A4\u30F3\u304C {freeSubdomains} \u500B"
23914
24485
  },
23915
24486
  selfHosted: {
23916
24487
  name: "\u30BB\u30EB\u30D5\u30DB\u30B9\u30C8",
@@ -23920,9 +24491,9 @@ var ja_default2 = {
23920
24491
  cta: "\u30BB\u30EB\u30D5\u30DB\u30B9\u30C8\u3092\u59CB\u3081\u308B"
23921
24492
  },
23922
24493
  creditPacks: {
23923
- pack_5k: "{credits} \u30AF\u30EC\u30B8\u30C3\u30C8",
23924
- pack_25k: "{credits} \u30AF\u30EC\u30B8\u30C3\u30C8",
23925
- pack_100k: "{credits} \u30AF\u30EC\u30B8\u30C3\u30C8"
24494
+ pack_5k: "{computeMinutes} \u30B3\u30F3\u30D4\u30E5\u30FC\u30C8\u5206",
24495
+ pack_25k: "{computeMinutes} \u30B3\u30F3\u30D4\u30E5\u30FC\u30C8\u5206",
24496
+ pack_100k: "{computeMinutes} \u30B3\u30F3\u30D4\u30E5\u30FC\u30C8\u5206"
23926
24497
  },
23927
24498
  ui: {
23928
24499
  perMonth: "/\u6708",
@@ -23938,7 +24509,9 @@ var ja_default2 = {
23938
24509
  billedMonthly: "\u6708\u984D\u8ACB\u6C42\u3001\u3044\u3064\u3067\u3082\u30AD\u30E3\u30F3\u30BB\u30EB\u53EF\u80FD",
23939
24510
  campaignBadge: "{percentOff}% \u30AA\u30D5",
23940
24511
  campaignEnds: "\u30AD\u30E3\u30F3\u30DA\u30FC\u30F3\u306F {date} \u307E\u3067",
23941
- wasPrice: "\u901A\u5E38 {price}"
24512
+ wasPrice: "\u901A\u5E38 {price}",
24513
+ standardTitle: "\u3059\u3079\u3066\u306E\u30D7\u30E9\u30F3\u306B\u542B\u307E\u308C\u308B\u3082\u306E",
24514
+ creditPackNote: "\u5C0F\u898F\u6A21\u30A2\u30D7\u30EA\u7D04 {appHours} \u6642\u9593\u3001\u307E\u305F\u306F\u30D3\u30EB\u30C9 {buildMinutes} \u5206"
23942
24515
  }
23943
24516
  };
23944
24517
 
@@ -23969,20 +24542,20 @@ var pt_default2 = {
23969
24542
  features: {
23970
24543
  everythingIn: "Tudo do {inherited}, e mais:",
23971
24544
  staticOnly: "Somente sites est\xE1ticos: SSG, SPA e HTML puro",
23972
- anyWorkload: "Execute qualquer coisa: cont\xEAineres, stacks do Compose e workers",
23973
- managedServices: "Postgres e Redis gerenciados, e o cat\xE1logo completo de apps",
24545
+ anyWorkload: "Execute qualquer coisa: cont\xEAineres, Compose, workers, Postgres, Redis",
24546
+ runningServices: "{runningServices} apps, bancos de dados ou workers em execu\xE7\xE3o",
24547
+ computeMinutes: "{computeMinutes} minutos de computa\xE7\xE3o por m\xEAs",
23974
24548
  buildMinutes: "{buildMinutes} minutos de build por m\xEAs",
23975
- credits: "{credits} cr\xE9ditos de computa\xE7\xE3o por m\xEAs",
24549
+ power: "At\xE9 {powerCpu} vCPU e {powerRamGb} GB de RAM por app",
24550
+ projects: "{maxProjects} projetos",
24551
+ projectsUnlimited: "Projetos ilimitados",
23976
24552
  freeSubdomains: "{freeSubdomains} subdom\xEDnios {freeDomainSuffix} gratuitos",
23977
24553
  freeSubdomainsUnlimited: "Subdom\xEDnios {freeDomainSuffix} gratuitos e ilimitados",
24554
+ gitDeploys: "Fa\xE7a deploy do GitHub, GitLab ou qualquer remoto Git",
23978
24555
  customDomains: "Dom\xEDnios pr\xF3prios ilimitados, com TLS inclu\xEDdo",
24556
+ deployLogs: "Logs de build e deploy em tempo real",
24557
+ auditLog: "Log de auditoria de cada altera\xE7\xE3o",
23979
24558
  unlimitedSeats: "Membros de equipe ilimitados: nunca pague por usu\xE1rio",
23980
- previewDeploys: "Deploys de pr\xE9-visualiza\xE7\xE3o a cada push",
23981
- backups: "Backups di\xE1rios com recupera\xE7\xE3o para um ponto no tempo",
23982
- observability: "Logs em tempo real, m\xE9tricas e alertas de sa\xFAde",
23983
- mailServer: "Servidor de e-mail integrado: SMTP, IMAP e webmail",
23984
- auditLog: "Log de auditoria com reten\xE7\xE3o estendida",
23985
- sharedChannel: "Canal compartilhado no Slack ou Discord",
23986
24559
  sso: "SSO e SAML",
23987
24560
  customLimits: "Limites sob medida, negociados por contrato",
23988
24561
  dedicatedInfra: "Infraestrutura e regi\xF5es dedicadas",
@@ -23995,12 +24568,7 @@ var pt_default2 = {
23995
24568
  selfHostedUnlimited: "Deploys, projetos, servidores e dom\xEDnios ilimitados",
23996
24569
  selfHostedServices: "Todos os servi\xE7os gerenciados: Postgres, Redis, e-mail",
23997
24570
  selfHostedClients: "CLI, web e desktop: um \xFAnico backend",
23998
- selfHostedFreeSubdomains: "{freeSubdomains} subdom\xEDnios {freeDomainSuffix} gratuitos ao conectar \xE0 Cloud",
23999
- runningServices: "{runningServices} servi\xE7os em execu\xE7\xE3o: apps, bancos de dados, workers",
24000
- power: "At\xE9 {powerCpu} vCPU e {powerRamGb} GB de RAM por servi\xE7o",
24001
- projects: "{maxProjects} projetos",
24002
- projectsUnlimited: "Projetos ilimitados",
24003
- meteredCredits: "Computa\xE7\xE3o, largura de banda e armazenamento consomem os seus cr\xE9ditos mensais"
24571
+ selfHostedFreeSubdomains: "{freeSubdomains} subdom\xEDnios {freeDomainSuffix} gratuitos ao conectar \xE0 Cloud"
24004
24572
  },
24005
24573
  selfHosted: {
24006
24574
  name: "Auto-hospedado",
@@ -24010,9 +24578,9 @@ var pt_default2 = {
24010
24578
  cta: "Come\xE7ar a auto-hospedar"
24011
24579
  },
24012
24580
  creditPacks: {
24013
- pack_5k: "{credits} cr\xE9ditos",
24014
- pack_25k: "{credits} cr\xE9ditos",
24015
- pack_100k: "{credits} cr\xE9ditos"
24581
+ pack_5k: "{computeMinutes} minutos de computa\xE7\xE3o",
24582
+ pack_25k: "{computeMinutes} minutos de computa\xE7\xE3o",
24583
+ pack_100k: "{computeMinutes} minutos de computa\xE7\xE3o"
24016
24584
  },
24017
24585
  ui: {
24018
24586
  perMonth: "/m\xEAs",
@@ -24028,7 +24596,9 @@ var pt_default2 = {
24028
24596
  billedMonthly: "cobran\xE7a mensal, cancele quando quiser",
24029
24597
  campaignBadge: "{percentOff}% de desconto",
24030
24598
  campaignEnds: "Oferta termina em {date}",
24031
- wasPrice: "antes {price}"
24599
+ wasPrice: "antes {price}",
24600
+ standardTitle: "Em todos os planos",
24601
+ creditPackNote: "\u2248 {appHours} horas de um app pequeno, ou {buildMinutes} minutos de build"
24032
24602
  }
24033
24603
  };
24034
24604
 
@@ -24059,20 +24629,20 @@ var tr_default2 = {
24059
24629
  features: {
24060
24630
  everythingIn: "{inherited} plan\u0131ndaki her \u015Fey, ayr\u0131ca:",
24061
24631
  staticOnly: "Yaln\u0131zca statik siteler \u2014 SSG, SPA ve d\xFCz HTML",
24062
- anyWorkload: "Her \u015Feyi \xE7al\u0131\u015Ft\u0131r\u0131n \u2014 container'lar, Compose y\u0131\u011F\u0131nlar\u0131 ve worker'lar",
24063
- managedServices: "Y\xF6netilen Postgres, Redis ve uygulama katalo\u011Funun tamam\u0131",
24632
+ anyWorkload: "Her \u015Feyi \xE7al\u0131\u015Ft\u0131r\u0131n \u2014 container, Compose, worker, Postgres, Redis",
24633
+ runningServices: "{runningServices} \xE7al\u0131\u015Fan uygulama, veritaban\u0131 veya worker",
24634
+ computeMinutes: "Ayda {computeMinutes} hesaplama dakikas\u0131",
24064
24635
  buildMinutes: "Ayda {buildMinutes} build dakikas\u0131",
24065
- credits: "Ayda {credits} hesaplama kredisi",
24636
+ power: "Uygulama ba\u015F\u0131na en fazla {powerCpu} vCPU ve {powerRamGb} GB RAM",
24637
+ projects: "{maxProjects} proje",
24638
+ projectsUnlimited: "S\u0131n\u0131rs\u0131z proje",
24066
24639
  freeSubdomains: "{freeSubdomains} \xFCcretsiz {freeDomainSuffix} alt alan ad\u0131",
24067
24640
  freeSubdomainsUnlimited: "S\u0131n\u0131rs\u0131z \xFCcretsiz {freeDomainSuffix} alt alan ad\u0131",
24641
+ gitDeploys: "GitHub, GitLab veya herhangi bir Git uzak deposundan da\u011F\u0131t\u0131n",
24068
24642
  customDomains: "S\u0131n\u0131rs\u0131z \xF6zel alan ad\u0131, TLS dahil",
24643
+ deployLogs: "Canl\u0131 derleme ve da\u011F\u0131t\u0131m loglar\u0131",
24644
+ auditLog: "Her de\u011Fi\u015Fikli\u011Fin denetim kayd\u0131",
24069
24645
  unlimitedSeats: "S\u0131n\u0131rs\u0131z ekip \xFCyesi \u2014 kullan\u0131c\u0131 ba\u015F\u0131na \xF6deme yok",
24070
- previewDeploys: "Her push'ta \xF6nizleme da\u011F\u0131t\u0131m\u0131",
24071
- backups: "G\xFCnl\xFCk yedekler ve belirli bir ana geri d\xF6n\xFC\u015F",
24072
- observability: "Canl\u0131 loglar, metrikler ve sa\u011Fl\u0131k uyar\u0131lar\u0131",
24073
- mailServer: "Dahili posta sunucusu \u2014 SMTP, IMAP ve webmail",
24074
- auditLog: "Denetim kayd\u0131, uzat\u0131lm\u0131\u015F saklama s\xFCresiyle",
24075
- sharedChannel: "Payla\u015F\u0131ml\u0131 Slack veya Discord kanal\u0131",
24076
24646
  sso: "SSO ve SAML",
24077
24647
  customLimits: "S\xF6zle\u015Fmeye g\xF6re belirlenen \xF6zel limitler",
24078
24648
  dedicatedInfra: "Size ayr\u0131lm\u0131\u015F altyap\u0131 ve b\xF6lgeler",
@@ -24085,12 +24655,7 @@ var tr_default2 = {
24085
24655
  selfHostedUnlimited: "S\u0131n\u0131rs\u0131z da\u011F\u0131t\u0131m, proje, sunucu ve alan ad\u0131",
24086
24656
  selfHostedServices: "T\xFCm y\xF6netilen servisler \u2014 Postgres, Redis, posta",
24087
24657
  selfHostedClients: "CLI, web ve masa\xFCst\xFC \u2014 tek backend",
24088
- selfHostedFreeSubdomains: "Cloud'a ba\u011Fland\u0131\u011F\u0131n\u0131zda {freeSubdomains} \xFCcretsiz {freeDomainSuffix} alt alan ad\u0131",
24089
- runningServices: "{runningServices} \xE7al\u0131\u015Fan servis \u2014 uygulamalar, veritabanlar\u0131, worker'lar",
24090
- power: "Servis ba\u015F\u0131na en fazla {powerCpu} vCPU ve {powerRamGb} GB RAM",
24091
- projects: "{maxProjects} proje",
24092
- projectsUnlimited: "S\u0131n\u0131rs\u0131z proje",
24093
- meteredCredits: "Hesaplama, bant geni\u015Fli\u011Fi ve depolaman\u0131n tamam\u0131 ayl\u0131k kredilerinizden d\xFC\u015Fer"
24658
+ selfHostedFreeSubdomains: "Cloud'a ba\u011Fland\u0131\u011F\u0131n\u0131zda {freeSubdomains} \xFCcretsiz {freeDomainSuffix} alt alan ad\u0131"
24094
24659
  },
24095
24660
  selfHosted: {
24096
24661
  name: "Kendi sunucunuzda",
@@ -24100,9 +24665,9 @@ var tr_default2 = {
24100
24665
  cta: "Kendi sunucunuzda ba\u015Flay\u0131n"
24101
24666
  },
24102
24667
  creditPacks: {
24103
- pack_5k: "{credits} kredi",
24104
- pack_25k: "{credits} kredi",
24105
- pack_100k: "{credits} kredi"
24668
+ pack_5k: "{computeMinutes} hesaplama dakikas\u0131",
24669
+ pack_25k: "{computeMinutes} hesaplama dakikas\u0131",
24670
+ pack_100k: "{computeMinutes} hesaplama dakikas\u0131"
24106
24671
  },
24107
24672
  ui: {
24108
24673
  perMonth: "/ay",
@@ -24118,7 +24683,9 @@ var tr_default2 = {
24118
24683
  billedMonthly: "ayl\u0131k faturalan\u0131r, istedi\u011Finiz zaman iptal edin",
24119
24684
  campaignBadge: "%{percentOff} indirim",
24120
24685
  campaignEnds: "Kampanya {date} tarihinde sona eriyor",
24121
- wasPrice: "eski fiyat {price}"
24686
+ wasPrice: "eski fiyat {price}",
24687
+ standardTitle: "T\xFCm planlarda",
24688
+ creditPackNote: "\u2248 K\xFC\xE7\xFCk bir uygulama i\xE7in {appHours} saat veya {buildMinutes} derleme dakikas\u0131"
24122
24689
  }
24123
24690
  };
24124
24691
 
@@ -24149,20 +24716,20 @@ var zh_default = {
24149
24716
  features: {
24150
24717
  everythingIn: "\u5305\u542B {inherited} \u7684\u5168\u90E8\u529F\u80FD,\u53E6\u6709:",
24151
24718
  staticOnly: "\u4EC5\u652F\u6301\u9759\u6001\u7AD9\u70B9:SSG\u3001SPA \u548C\u7EAF HTML",
24152
- anyWorkload: "\u4EFB\u610F\u5DE5\u4F5C\u8D1F\u8F7D\u90FD\u80FD\u8FD0\u884C:\u5BB9\u5668\u3001Compose \u7F16\u6392\u548C\u540E\u53F0\u4EFB\u52A1",
24153
- managedServices: "\u6258\u7BA1\u7684 Postgres\u3001Redis \u548C\u5B8C\u6574\u5E94\u7528\u76EE\u5F55",
24719
+ anyWorkload: "\u4EFB\u610F\u5DE5\u4F5C\u8D1F\u8F7D:\u5BB9\u5668\u3001Compose\u3001\u540E\u53F0\u4EFB\u52A1\u3001Postgres\u3001Redis",
24720
+ runningServices: "{runningServices} \u4E2A\u540C\u65F6\u8FD0\u884C\u7684\u5E94\u7528\u3001\u6570\u636E\u5E93\u6216\u540E\u53F0\u4EFB\u52A1",
24721
+ computeMinutes: "\u6BCF\u6708 {computeMinutes} \u8BA1\u7B97\u5206\u949F",
24154
24722
  buildMinutes: "\u6BCF\u6708 {buildMinutes} \u5206\u949F\u6784\u5EFA\u65F6\u957F",
24155
- credits: "\u6BCF\u6708 {credits} \u8BA1\u7B97\u79EF\u5206",
24723
+ power: "\u6BCF\u4E2A\u5E94\u7528\u6700\u9AD8\u53EF\u8FBE {powerCpu} vCPU \u548C {powerRamGb} GB RAM",
24724
+ projects: "{maxProjects} \u4E2A\u9879\u76EE",
24725
+ projectsUnlimited: "\u9879\u76EE\u4E0D\u9650\u6570\u91CF",
24156
24726
  freeSubdomains: "{freeSubdomains} \u4E2A\u514D\u8D39 {freeDomainSuffix} \u5B50\u57DF\u540D",
24157
24727
  freeSubdomainsUnlimited: "\u514D\u8D39 {freeDomainSuffix} \u5B50\u57DF\u540D\u4E0D\u9650\u6570\u91CF",
24728
+ gitDeploys: "\u4ECE GitHub\u3001GitLab \u6216\u4EFB\u610F Git \u8FDC\u7AEF\u90E8\u7F72",
24158
24729
  customDomains: "\u81EA\u5B9A\u4E49\u57DF\u540D\u4E0D\u9650\u6570\u91CF,\u542B TLS",
24730
+ deployLogs: "\u5B9E\u65F6\u6784\u5EFA\u4E0E\u90E8\u7F72\u65E5\u5FD7",
24731
+ auditLog: "\u8BB0\u5F55\u6BCF\u4E00\u6B21\u53D8\u66F4\u7684\u5BA1\u8BA1\u65E5\u5FD7",
24159
24732
  unlimitedSeats: "\u56E2\u961F\u6210\u5458\u4E0D\u9650\u4EBA\u6570,\u6C38\u4E0D\u6309\u5E2D\u4F4D\u8BA1\u8D39",
24160
- previewDeploys: "\u6BCF\u6B21\u63A8\u9001\u90FD\u751F\u6210\u9884\u89C8\u90E8\u7F72",
24161
- backups: "\u6BCF\u65E5\u5907\u4EFD,\u652F\u6301\u6309\u65F6\u95F4\u70B9\u6062\u590D",
24162
- observability: "\u5B9E\u65F6\u65E5\u5FD7\u3001\u6307\u6807\u548C\u5065\u5EB7\u544A\u8B66",
24163
- mailServer: "\u5185\u7F6E\u90AE\u4EF6\u670D\u52A1\u5668:SMTP\u3001IMAP \u548C\u7F51\u9875\u90AE\u7BB1",
24164
- auditLog: "\u5BA1\u8BA1\u65E5\u5FD7,\u4FDD\u7559\u671F\u66F4\u957F",
24165
- sharedChannel: "\u5171\u4EAB\u7684 Slack \u6216 Discord \u9891\u9053",
24166
24733
  sso: "SSO \u4E0E SAML",
24167
24734
  customLimits: "\u81EA\u5B9A\u4E49\u914D\u989D,\u6309\u5408\u540C\u5546\u5B9A",
24168
24735
  dedicatedInfra: "\u4E13\u5C5E\u57FA\u7840\u8BBE\u65BD\u4E0E\u4E13\u5C5E\u533A\u57DF",
@@ -24175,12 +24742,7 @@ var zh_default = {
24175
24742
  selfHostedUnlimited: "\u90E8\u7F72\u3001\u9879\u76EE\u3001\u670D\u52A1\u5668\u548C\u57DF\u540D\u5747\u4E0D\u9650\u6570\u91CF",
24176
24743
  selfHostedServices: "\u5168\u90E8\u6258\u7BA1\u670D\u52A1:Postgres\u3001Redis\u3001\u90AE\u4EF6\u670D\u52A1",
24177
24744
  selfHostedClients: "CLI\u3001\u7F51\u9875\u7AEF\u548C\u684C\u9762\u7AEF,\u5171\u7528\u4E00\u4E2A\u540E\u7AEF",
24178
- selfHostedFreeSubdomains: "\u8FDE\u63A5 Cloud \u540E\u53EF\u83B7\u5F97 {freeSubdomains} \u4E2A\u514D\u8D39 {freeDomainSuffix} \u5B50\u57DF\u540D",
24179
- runningServices: "{runningServices} \u4E2A\u540C\u65F6\u8FD0\u884C\u7684\u670D\u52A1:\u5E94\u7528\u3001\u6570\u636E\u5E93\u3001\u540E\u53F0\u4EFB\u52A1",
24180
- power: "\u6BCF\u4E2A\u670D\u52A1\u6700\u9AD8\u53EF\u8FBE {powerCpu} vCPU \u548C {powerRamGb} GB RAM",
24181
- projects: "{maxProjects} \u4E2A\u9879\u76EE",
24182
- projectsUnlimited: "\u9879\u76EE\u4E0D\u9650\u6570\u91CF",
24183
- meteredCredits: "\u8BA1\u7B97\u3001\u6D41\u91CF\u548C\u5B58\u50A8\u90FD\u4ECE\u4F60\u7684\u6BCF\u6708\u79EF\u5206\u4E2D\u6263\u9664"
24745
+ selfHostedFreeSubdomains: "\u8FDE\u63A5 Cloud \u540E\u53EF\u83B7\u5F97 {freeSubdomains} \u4E2A\u514D\u8D39 {freeDomainSuffix} \u5B50\u57DF\u540D"
24184
24746
  },
24185
24747
  selfHosted: {
24186
24748
  name: "\u81EA\u6258\u7BA1",
@@ -24190,9 +24752,9 @@ var zh_default = {
24190
24752
  cta: "\u5F00\u59CB\u81EA\u6258\u7BA1"
24191
24753
  },
24192
24754
  creditPacks: {
24193
- pack_5k: "{credits} \u79EF\u5206",
24194
- pack_25k: "{credits} \u79EF\u5206",
24195
- pack_100k: "{credits} \u79EF\u5206"
24755
+ pack_5k: "{computeMinutes} \u8BA1\u7B97\u5206\u949F",
24756
+ pack_25k: "{computeMinutes} \u8BA1\u7B97\u5206\u949F",
24757
+ pack_100k: "{computeMinutes} \u8BA1\u7B97\u5206\u949F"
24196
24758
  },
24197
24759
  ui: {
24198
24760
  perMonth: "/\u6708",
@@ -24208,7 +24770,9 @@ var zh_default = {
24208
24770
  billedMonthly: "\u6309\u6708\u8BA1\u8D39,\u53EF\u968F\u65F6\u53D6\u6D88",
24209
24771
  campaignBadge: "\u7ACB\u51CF {percentOff}%",
24210
24772
  campaignEnds: "\u4F18\u60E0\u622A\u6B62 {date}",
24211
- wasPrice: "\u539F\u4EF7 {price}"
24773
+ wasPrice: "\u539F\u4EF7 {price}",
24774
+ standardTitle: "\u6240\u6709\u5957\u9910\u5747\u5305\u542B",
24775
+ creditPackNote: "\u2248 \u5C0F\u578B\u5E94\u7528\u8FD0\u884C {appHours} \u5C0F\u65F6,\u6216 {buildMinutes} \u6784\u5EFA\u5206\u949F"
24212
24776
  }
24213
24777
  };
24214
24778
 
@@ -24258,6 +24822,17 @@ function featureTemplate(key, locale) {
24258
24822
  return copyFor(locale).features?.[key] ?? en_default2.features[key] ?? null;
24259
24823
  }
24260
24824
  var PLAN_BY_ID = new Map(PRICING.plans.map((p) => [p.id, p]));
24825
+ var MILLI_PER_COMPUTE_MINUTE = 1e3;
24826
+ function buildUnitsPerMinute() {
24827
+ return PRICING.oblien.buildResources.cpuCores / RESOURCE_TIER_SPECS.low.cpuCores;
24828
+ }
24829
+ function planMonthlyCredits(planId) {
24830
+ const plan = PLAN_BY_ID.get(planId ?? "") ?? PLAN_BY_ID.get(DEFAULT_PLAN_TIER);
24831
+ const { computeMinutesPerMonth, buildMinutesPerMonth } = plan.limits;
24832
+ if (computeMinutesPerMonth === null || buildMinutesPerMonth === null) return null;
24833
+ const units = computeMinutesPerMonth + buildMinutesPerMonth * buildUnitsPerMinute();
24834
+ return Math.round(units * MILLI_PER_COMPUTE_MINUTE);
24835
+ }
24261
24836
  function toOblienLimits(limits) {
24262
24837
  const { runningServices, maxResourceTier } = limits;
24263
24838
  if (runningServices === null && maxResourceTier === null) return null;
@@ -24287,11 +24862,12 @@ function placeholders(plan, locale) {
24287
24862
  powerCpu: svc ? formatDecimal(svc.cpuCores, locale) : unlimited,
24288
24863
  powerRamGb: svc ? formatDecimal(svc.memoryMb / 1024, locale) : unlimited,
24289
24864
  powerDiskGb: svc ? formatCount(Math.round(svc.diskMb / 1024), locale) : unlimited,
24290
- credits: plan.credits.monthlyMilli === null ? unlimited : formatCount(plan.credits.monthlyMilli / 1e3, locale),
24865
+ computeMinutes: n(limits.computeMinutesPerMonth),
24291
24866
  inherited: plan.inherits ? planName(plan.inherits, locale) : "",
24292
24867
  freeDomainSuffix: PRICING.freeDomainSuffix
24293
24868
  };
24294
24869
  }
24870
+ var EVERYTHING_IN_KEY = "everythingIn";
24295
24871
  function resolvePlan(planId, locale = "en") {
24296
24872
  const plan = PLAN_BY_ID.get(planId) ?? PLAN_BY_ID.get(DEFAULT_PLAN_TIER);
24297
24873
  const values = placeholders(plan, locale);
@@ -24300,10 +24876,14 @@ function resolvePlan(planId, locale = "en") {
24300
24876
  name: planName(plan.id, locale),
24301
24877
  description: planTagline(plan.id, locale),
24302
24878
  price: plan.price,
24303
- monthlyCredits: plan.credits.monthlyMilli,
24879
+ monthlyCredits: planMonthlyCredits(plan.id),
24304
24880
  oblienLimits: toOblienLimits(plan.limits),
24305
24881
  limits: plan.limits,
24306
- features: plan.features.map((key) => {
24882
+ ...(() => {
24883
+ const lead = plan.features.includes(EVERYTHING_IN_KEY) ? featureTemplate(EVERYTHING_IN_KEY, locale) : null;
24884
+ return lead === null ? {} : { inheritedFrom: fill(lead, values) };
24885
+ })(),
24886
+ features: plan.features.filter((key) => key !== EVERYTHING_IN_KEY).map((key) => {
24307
24887
  const template = featureTemplate(key, locale);
24308
24888
  return template === null ? null : fill(template, values);
24309
24889
  }).filter((s) => s !== null),
@@ -24317,10 +24897,17 @@ var PLANS = Object.fromEntries(
24317
24897
  );
24318
24898
  function resolveCreditPacks(locale = "en") {
24319
24899
  return [...PRICING.creditPacks].sort((a, b) => a.sortOrder - b.sortOrder).map((pack) => {
24320
- const template = copyFor(locale).creditPacks?.[pack.id] ?? en_default2.creditPacks[pack.id] ?? "{credits}";
24900
+ const template = copyFor(locale).creditPacks?.[pack.id] ?? en_default2.creditPacks[pack.id] ?? "{computeMinutes}";
24901
+ const minutes = pack.creditsMilli / MILLI_PER_COMPUTE_MINUTE;
24902
+ const amount = formatCount(minutes, locale);
24903
+ const noteTemplate = copyFor(locale).ui?.creditPackNote ?? en_default2.ui.creditPackNote;
24321
24904
  return {
24322
24905
  id: pack.id,
24323
- name: fill(template, { credits: formatCount(pack.creditsMilli / 1e3, locale) }),
24906
+ name: fill(template, { computeMinutes: amount, credits: amount }),
24907
+ explains: fill(noteTemplate, {
24908
+ appHours: formatCount(Math.round(minutes / 60), locale),
24909
+ buildMinutes: formatCount(Math.round(minutes / buildUnitsPerMinute()), locale)
24910
+ }),
24324
24911
  credits_milli: pack.creditsMilli,
24325
24912
  price_cents: pack.priceCents,
24326
24913
  sortOrder: pack.sortOrder
@@ -24332,6 +24919,240 @@ var ANNUAL_ENABLED = PRICING.annual.enabled;
24332
24919
  var ANNUAL_MONTHS_FREE = PRICING.annual.monthsFree;
24333
24920
  var FREE_DOMAIN_SUFFIX = PRICING.freeDomainSuffix;
24334
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
+
24335
25156
  // ../../packages/core/src/updates/types.ts
24336
25157
  var GITHUB_REPO = "oblien/openship";
24337
25158
  var RELEASES_LATEST_API = `https://api.github.com/repos/${GITHUB_REPO}/releases/latest`;
@@ -24463,6 +25284,13 @@ var AUDIT_EVENTS = {
24463
25284
  tone: "warning",
24464
25285
  description: "A delete was refused \u2014 something still depends on this app."
24465
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
+ },
24466
25294
  "project:write": {
24467
25295
  category: "apps",
24468
25296
  action: "changed",
@@ -24688,6 +25516,13 @@ var AUDIT_EVENTS = {
24688
25516
  label: "Server removed",
24689
25517
  tone: "danger"
24690
25518
  },
25519
+ "server.removal.rejected": {
25520
+ category: "servers",
25521
+ action: "tried to remove the server",
25522
+ label: "Server removal refused",
25523
+ tone: "warning",
25524
+ description: "A removal was refused because a workload could not be torn down, or was destroyed but left a resource behind for cleanup. The server row was kept so those resources can still be reclaimed."
25525
+ },
24691
25526
  "server.exec": {
24692
25527
  category: "servers",
24693
25528
  action: "ran a command on the server",
@@ -24943,6 +25778,20 @@ var AUDIT_EVENTS = {
24943
25778
  tone: "warning",
24944
25779
  description: "A full database dump left this instance."
24945
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
+ },
24946
25795
  "instance.data.imported": {
24947
25796
  category: "security",
24948
25797
  action: "imported instance data",
@@ -25451,132 +26300,100 @@ function idSuffix(distroId) {
25451
26300
  return distroId ? ` (os-release ID=${JSON.stringify(distroId)})` : "";
25452
26301
  }
25453
26302
 
25454
- // ../../packages/adapters/src/system/errors.ts
25455
- var SSH_AUTH_ERROR_PATTERNS = [
25456
- "All configured authentication methods failed"
25457
- ];
25458
- var RETRYABLE_CONNECTION_ERROR_PATTERNS = [
25459
- "ECONNRESET",
25460
- "ECONNREFUSED",
25461
- "ETIMEDOUT",
25462
- "EHOSTUNREACH",
25463
- "ENETUNREACH",
25464
- "Timed out",
25465
- "Not connected",
25466
- "Connection lost",
25467
- "read ECONNRESET",
25468
- "Handshake failed",
25469
- "keepalive timeout",
25470
- "SSH connection closed before ready",
25471
- // ssh2 emits "Channel open failure: open failed" / "Channel open failure: …"
25472
- // when the SSH SERVER refuses a new channel - typically because the cached
25473
- // connection has gone half-dead (peer-side LOGOUT, network blip,
25474
- // remote sshd MaxSessions). The inner SshExecutor retries `exec`/
25475
- // `streamExec` on this, but `pipeLocal`, `transferIn`, and any other
25476
- // channel-bearing op bubble it up. Listing it here lets `withExecutor`
25477
- // drop the dead connection and retry transparently - fixes the
25478
- // "first redeploy fails, second click works" pattern.
25479
- "Channel open failure",
25480
- "open failed"
25481
- // NOT "socket hang up": it is what an HTTP client reports for ANY far end that vanished
25482
- // mid-request, including a permission-denied Docker socket and a wedged channel, neither
25483
- // of which a fresh connection fixes. A match here makes `withExecutor` replay its whole
25484
- // callback — and those callbacks install mail, create mailboxes and ensure the edge, so a
25485
- // non-retryable cause becomes a repeated mutation. `core/connectivity.ts` labels it
25486
- // without retrying, `api/lib/remote-state.ts` still maps it to a 503, and the bridge's
25487
- // 502 carries the real reason — so leaving it out here costs no diagnosis.
25488
- ];
25489
- var SshDisconnectedError = class extends Error {
25490
- constructor(message = "SSH connection lost") {
25491
- super(message);
25492
- this.name = "SshDisconnectedError";
25493
- }
25494
- };
25495
- function isSshDisconnectedError(err) {
25496
- return err instanceof SshDisconnectedError || err instanceof Error && err.name === "SshDisconnectedError";
25497
- }
25498
- function isSshAuthError(err) {
25499
- if (!(err instanceof Error)) return false;
25500
- return SSH_AUTH_ERROR_PATTERNS.some((pattern) => err.message.includes(pattern));
25501
- }
25502
- var HostChannelUnavailableError = class extends Error {
25503
- code;
25504
- constructor(code, message) {
25505
- super(message);
25506
- this.name = "HostChannelUnavailableError";
25507
- this.code = code;
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;
25508
26310
  }
25509
- };
25510
- function isRetryableRemoteConnectionError(err) {
25511
- if (isSshDisconnectedError(err)) return true;
25512
- if (!(err instanceof Error)) return false;
25513
- return RETRYABLE_CONNECTION_ERROR_PATTERNS.some(
25514
- (pattern) => err.message.includes(pattern)
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)
25515
26334
  );
25516
26335
  }
25517
- function isRemoteConnectionError(err) {
25518
- return isSshAuthError(err) || isRetryableRemoteConnectionError(err);
25519
- }
25520
- var MAX_COMMAND_CHARS = 120;
25521
- function commandForError(command) {
25522
- const oneLine = command.trim().replace(/\s+/g, " ");
25523
- return oneLine.length > MAX_COMMAND_CHARS ? `${oneLine.slice(0, MAX_COMMAND_CHARS)}\u2026 (${command.length} chars)` : oneLine;
25524
- }
25525
- function isRuntimeNotFoundError(err) {
25526
- if (!err || typeof err !== "object") return false;
25527
- const e = err;
25528
- if (e.statusCode === 404) return true;
25529
- return typeof e.message === "string" && /no such (container|image|volume|network)/i.test(e.message);
25530
- }
25531
-
25532
- // ../../packages/adapters/src/system/debug.ts
25533
- function isTruthy(value) {
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(/\.+$/, "");
25534
26341
  if (!value) return false;
25535
- return ["1", "true", "yes", "on", "debug"].includes(value.toLowerCase());
25536
- }
25537
- function isSystemDebugEnabled() {
25538
- return isTruthy(process.env.SYSTEM_DEBUG_LOGS);
25539
- }
25540
- function systemDebug(scope, message) {
25541
- if (!isSystemDebugEnabled()) return;
25542
- const timestamp = (/* @__PURE__ */ new Date()).toISOString();
25543
- console.log(`[system-debug] ${timestamp} [${scope}] ${message}`);
25544
- }
25545
- function formatDuration(startMs) {
25546
- return `${Date.now() - startMs}ms`;
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);
25547
26355
  }
25548
26356
 
25549
26357
  export {
26358
+ shellQuote,
26359
+ shellSplitWords,
26360
+ commandToArgv,
25550
26361
  LANGUAGES,
25551
26362
  STACKS,
25552
26363
  TRANSFER_EXCLUDES,
25553
26364
  buildOutputTransferExcludes,
25554
26365
  PACKAGE_ROOT_ONLY_EXCLUDES,
25555
26366
  packageManagerEnsureCommand,
26367
+ nodeBinDirs,
26368
+ nodeBinPathExport,
25556
26369
  appVolumeTargets,
25557
26370
  parseComposeNamespace,
25558
26371
  ownsNetworkEndpoint,
26372
+ resolveRedirectStatus,
26373
+ isValidEmail,
26374
+ formatBytes,
26375
+ withTimeout,
26376
+ isValidEnvKey,
26377
+ wrapText,
26378
+ composeBuildIssues,
25559
26379
  composeMountToSpec,
25560
26380
  composeMountIssues,
25561
26381
  composePortToSpec,
25562
26382
  isAwsS3Endpoint,
25563
26383
  DEFAULT_IMAGE_REGISTRY,
25564
- shellQuote,
25565
- shellSplitWords,
25566
- commandToArgv,
25567
26384
  registryForImage,
25568
26385
  registryConfigKeys,
25569
26386
  buildEdgeImageRef,
25570
26387
  buildMailImageRef,
25571
26388
  SYSTEM,
25572
- resolveRedirectStatus,
25573
- isValidEmail,
25574
- formatBytes,
25575
- withTimeout,
25576
- wrapText,
25577
26389
  AppError,
25578
26390
  DeployError,
25579
26391
  safeErrorMessage,
26392
+ payloadSpec,
26393
+ normalizeBackupPath,
26394
+ validateBackupPath,
26395
+ validateClearPath,
26396
+ isDbImage,
25580
26397
  LOCAL_DASHBOARD_URL,
25581
26398
  LOCAL_API_URL,
25582
26399
  CLOUD_DASHBOARD_URL,
@@ -25588,6 +26405,9 @@ export {
25588
26405
  missingOutputDirectoryMessage,
25589
26406
  compareSemver,
25590
26407
  PRICING,
26408
+ validateReleaseVersionUrl,
26409
+ validateReleaseRepository,
26410
+ renderReleaseImage,
25591
26411
  resolveCliUpdatePlan,
25592
26412
  cliInstallCommand,
25593
26413
  PROXY_DIRECTIVES,
@@ -25623,13 +26443,5 @@ export {
25623
26443
  hostChannelRuleScope,
25624
26444
  hostFirewallRule,
25625
26445
  hostFirewallRuleRemoval,
25626
- SshDisconnectedError,
25627
- isSshAuthError,
25628
- HostChannelUnavailableError,
25629
- isRetryableRemoteConnectionError,
25630
- isRemoteConnectionError,
25631
- commandForError,
25632
- isRuntimeNotFoundError,
25633
- systemDebug,
25634
- formatDuration
26446
+ isLoopbackHost
25635
26447
  };