@h-ai/deploy 1.0.0-beta.22 → 1.0.0-beta.24
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.en.md +6 -10
- package/README.ja.md +5 -9
- package/README.md +5 -9
- package/dist/deploy-runtime-env.mjs +3 -11
- package/dist/index.d.ts +6 -38
- package/dist/index.js +1156 -625
- package/dist/index.js.map +1 -1
- package/package.json +5 -5
- package/docs/credential-import.en.md +0 -40
- package/docs/credential-import.ja.md +0 -40
- package/docs/credential-import.md +0 -70
package/dist/index.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { createHash, randomUUID, randomBytes, X509Certificate, createPublicKey, createPrivateKey } from 'crypto';
|
|
2
|
-
import { createReadStream, readFileSync, existsSync, lstatSync, mkdirSync, readdirSync,
|
|
2
|
+
import { createReadStream, readFileSync, existsSync, lstatSync, mkdirSync, readdirSync, openSync, constants, fstatSync, readSync, closeSync, fchmodSync, writeFileSync, fsyncSync, renameSync, unlinkSync, chmodSync, mkdtempSync, copyFileSync, rmSync, realpathSync, createWriteStream, statSync } from 'fs';
|
|
3
3
|
import { resolve, extname, join, dirname, basename, relative, sep } from 'path';
|
|
4
4
|
import { z } from 'zod';
|
|
5
5
|
import { core, err, ok, CoreConfigSchema, RequestAccessConfigSchema, TrustedProxiesSchema } from '@h-ai/core';
|
|
6
|
-
import {
|
|
6
|
+
import { execFileSync, spawn } from 'child_process';
|
|
7
7
|
import process from 'process';
|
|
8
8
|
import { LineCounter, parseDocument, parse, stringify, parseAllDocuments } from 'yaml';
|
|
9
9
|
import { Buffer, constants as constants$1 } from 'buffer';
|
|
@@ -22,17 +22,14 @@ import { isIP } from 'net';
|
|
|
22
22
|
// src/deploy-artifact.ts
|
|
23
23
|
var DeploymentArtifactSchema = z.object({
|
|
24
24
|
/** 清单格式版本,与应用版本和部署环境无关。 */
|
|
25
|
-
version: z.
|
|
26
|
-
z.literal(4),
|
|
27
|
-
z.literal(5)
|
|
28
|
-
]),
|
|
25
|
+
version: z.literal(5),
|
|
29
26
|
/** 主运行服务:独立后端为 serv,独立浏览器站点(静态或 SSR)为 web。 */
|
|
30
27
|
primaryService: z.enum(["serv", "web"]),
|
|
31
28
|
/** 应用根 package.json 的版本,用于归档、Compose 与桌面交付;最长 80 字符。 */
|
|
32
29
|
appVersion: z.string().max(80).regex(/^(?:0|[1-9]\d*)\.(?:0|[1-9]\d*)\.(?:0|[1-9]\d*)(?:-[0-9a-z-]+(?:\.[0-9a-z-]+)*)?(?:\+[0-9a-z-]+(?:\.[0-9a-z-]+)*)?$/i),
|
|
33
30
|
/** 本次构建标识;同一应用版本允许重复构建。 */
|
|
34
31
|
buildId: z.string().max(128).regex(/^[a-z0-9][\w.+-]*$/i),
|
|
35
|
-
/** 构建启动时观察到的 Git
|
|
32
|
+
/** 构建启动时观察到的 Git 来源。 */
|
|
36
33
|
source: z.object({
|
|
37
34
|
/** 工程 package.json 所在仓库的提交 SHA;无法确定时为 unknown。 */
|
|
38
35
|
commit: z.string().regex(/^(?:[a-f0-9]{40}|[a-f0-9]{64}|unknown)$/),
|
|
@@ -43,8 +40,8 @@ var DeploymentArtifactSchema = z.object({
|
|
|
43
40
|
partial: z.boolean().default(false),
|
|
44
41
|
/** 是否包含浏览器端;仅构建 serv 时也保留完整工程的 Web 拓扑。 */
|
|
45
42
|
hasWeb: z.boolean().default(false),
|
|
46
|
-
/** Web
|
|
47
|
-
webBasePath: z.string().refine((value) => value === "" || /^\/[\w.-]+(?:\/[\w.-]+)*$/.test(value) && !value.split("/").some((segment) => segment === "." || segment === ".."))
|
|
43
|
+
/** Web 构建的公开挂载目录,无末尾斜杠;空字符串表示域名根目录。增量制品须保留完整拓扑的同一值。 */
|
|
44
|
+
webBasePath: z.string().refine((value) => value === "" || /^\/[\w.-]+(?:\/[\w.-]+)*$/.test(value) && !value.split("/").some((segment) => segment === "." || segment === "..")),
|
|
48
45
|
/** 工程显式配置的后端端口,部分构建未包含 serv 时仍保留。 */
|
|
49
46
|
servContainerPort: z.number().int().min(1).max(65535).optional(),
|
|
50
47
|
/** 工程显式配置的 Web 端口,部分构建未包含 web 镜像时仍保留;缺省使用部署默认值。 */
|
|
@@ -59,12 +56,12 @@ var DeploymentArtifactSchema = z.object({
|
|
|
59
56
|
imageRef: z.string().regex(/^[a-z0-9][a-z0-9./:_-]*:[a-z0-9_][a-z0-9_.-]*$/).refine((value) => !value.endsWith(":latest")),
|
|
60
57
|
/** tar 内容的 SHA-256,用于部署前校验。 */
|
|
61
58
|
sha256: z.string().regex(/^[a-f0-9]{64}$/),
|
|
62
|
-
/** 引擎实际构建的镜像 OS
|
|
59
|
+
/** 引擎实际构建的镜像 OS/架构。 */
|
|
63
60
|
platform: z.string().regex(/^[a-z0-9][a-z0-9_.-]*\/[a-z0-9][a-z0-9_.-]*$/).optional(),
|
|
64
61
|
/** 容器内部监听端口;服务器公开端口由部署选择。 */
|
|
65
62
|
containerPort: z.number().int().min(1).max(65535).optional()
|
|
66
63
|
})).min(1).max(2).refine((images) => new Set(images.map((image) => image.name)).size === images.length)
|
|
67
|
-
}).refine((artifact) =>
|
|
64
|
+
}).refine((artifact) => !artifact.webBasePath || artifact.hasWeb).refine((artifact) => artifact.images.every((image) => image.file === `${image.name}-${artifact.appVersion}.tar`)).refine((artifact) => artifact.primaryService !== "web" || artifact.hasWeb).refine((artifact) => artifact.images.every((image) => image.name === artifact.primaryService || image.name === "web" && artifact.hasWeb)).refine((artifact) => artifact.partial || artifact.images.some((image) => image.name === artifact.primaryService)).refine((artifact) => artifact.partial || !artifact.hasWeb || artifact.images.some((image) => image.name === "web"));
|
|
68
65
|
async function artifactChecksum(file) {
|
|
69
66
|
const hash = createHash("sha256");
|
|
70
67
|
for await (const chunk of createReadStream(file))
|
|
@@ -189,9 +186,7 @@ var en_US_default = {
|
|
|
189
186
|
deploy_uploadPasswordCancelled: "Password input cancelled; no server connection was made.",
|
|
190
187
|
deploy_uploadPasswordInvalid: "SSH passwords must contain 1\u20131023 UTF-8 bytes without line breaks.",
|
|
191
188
|
deploy_managedSecretsInvalid: "Invalid generated middleware credential candidates. Rebuild the deployment bundle; server credentials will not be rotated automatically.",
|
|
192
|
-
deploy_secretImportFailed: "Legacy credential import was rejected during {phase}; check private input permissions, project/volume identity, live credentials and the deployment lock. No services were updated; inspect the protected state file if commit is unconfirmed",
|
|
193
189
|
deploy_projectNameRequired: "The root package.json.name is missing or cannot be normalized into an image and deployment resource identity.",
|
|
194
|
-
deploy_projectNameOverrideRemoved: "Deployment identity comes from the root package.json.name; remove the obsolete projectName override.",
|
|
195
190
|
deploy_requestAccessInvalid: "Invalid request access policy ({field}). Check fields, IP addresses, timezones and time windows. For a file reference, use readable YAML no larger than 1 MiB. Fix the configuration and create a new bundle.",
|
|
196
191
|
deploy_httpsPortsInvalid: "HTTP and HTTPS ports must differ; Let\u2019s Encrypt HTTP-01 requires host port 80.",
|
|
197
192
|
deploy_httpsCertificateInvalid: "Invalid HTTPS certificate. Check readable PEM files, matching unencrypted private key, validity dates and domain, then repackage.",
|
|
@@ -334,9 +329,7 @@ var ja_JP_default = {
|
|
|
334
329
|
deploy_uploadPasswordCancelled: "\u30D1\u30B9\u30EF\u30FC\u30C9\u5165\u529B\u3092\u4E2D\u6B62\u3057\u307E\u3057\u305F\u3002\u30B5\u30FC\u30D0\u30FC\u306B\u306F\u672A\u63A5\u7D9A\u3067\u3059\u3002",
|
|
335
330
|
deploy_uploadPasswordInvalid: "SSH \u30D1\u30B9\u30EF\u30FC\u30C9\u306F\u6539\u884C\u3092\u542B\u307E\u306A\u3044 1\u20131023 UTF-8 \u30D0\u30A4\u30C8\u306B\u3057\u3066\u304F\u3060\u3055\u3044\u3002",
|
|
336
331
|
deploy_managedSecretsInvalid: "\u81EA\u5EFA\u30DF\u30C9\u30EB\u30A6\u30A7\u30A2\u306E\u5019\u88DC\u8A8D\u8A3C\u60C5\u5831\u304C\u7121\u52B9\u3067\u3059\u3002\u914D\u7F6E\u30D1\u30C3\u30B1\u30FC\u30B8\u3092\u518D\u751F\u6210\u3057\u3066\u304F\u3060\u3055\u3044\u3002\u30B5\u30FC\u30D0\u30FC\u306E\u8A8D\u8A3C\u60C5\u5831\u306F\u81EA\u52D5\u5909\u66F4\u3055\u308C\u307E\u305B\u3093\u3002",
|
|
337
|
-
deploy_secretImportFailed: "\u65E7\u8A8D\u8A3C\u60C5\u5831\u306E\u30A4\u30F3\u30DD\u30FC\u30C8\u306F {phase} \u6BB5\u968E\u3067\u62D2\u5426\u3055\u308C\u307E\u3057\u305F\u3002\u5165\u529B\u6A29\u9650\u3001\u30D7\u30ED\u30B8\u30A7\u30AF\u30C8\u3068\u30DC\u30EA\u30E5\u30FC\u30E0\u3001\u5B9F\u969B\u306E\u8A8D\u8A3C\u60C5\u5831\u3001\u30C7\u30D7\u30ED\u30A4\u30ED\u30C3\u30AF\u3092\u78BA\u8A8D\u3057\u3066\u304F\u3060\u3055\u3044\u3002\u30B5\u30FC\u30D3\u30B9\u306F\u66F4\u65B0\u3055\u308C\u3066\u3044\u307E\u305B\u3093\u3002\u78BA\u5B9A\u304C\u4E0D\u660E\u306A\u5834\u5408\u306F\u4FDD\u8B77\u3055\u308C\u305F\u72B6\u614B\u30D5\u30A1\u30A4\u30EB\u3092\u78BA\u8A8D\u3057\u3066\u304F\u3060\u3055\u3044",
|
|
338
332
|
deploy_projectNameRequired: "\u5DE5\u7A0B\u30EB\u30FC\u30C8\u306E package.json.name \u304C\u306A\u3044\u304B\u6B63\u898F\u5316\u3067\u304D\u306A\u3044\u305F\u3081\u3001\u30A4\u30E1\u30FC\u30B8\u3068\u30C7\u30D7\u30ED\u30A4\u30EA\u30BD\u30FC\u30B9\u306E\u8B58\u5225\u540D\u3092\u751F\u6210\u3067\u304D\u307E\u305B\u3093\u3002",
|
|
339
|
-
deploy_projectNameOverrideRemoved: "\u30C7\u30D7\u30ED\u30A4\u8B58\u5225\u540D\u306F\u5DE5\u7A0B\u30EB\u30FC\u30C8\u306E package.json.name \u304B\u3089\u53D6\u5F97\u3057\u307E\u3059\u3002\u65E7 projectName \u4E0A\u66F8\u304D\u5F15\u6570\u3092\u524A\u9664\u3057\u3066\u304F\u3060\u3055\u3044\u3002",
|
|
340
333
|
deploy_requestAccessInvalid: "\u30EA\u30AF\u30A8\u30B9\u30C8\u30A2\u30AF\u30BB\u30B9\u8A2D\u5B9A\uFF08{field}\uFF09\u304C\u7121\u52B9\u3067\u3059\u3002\u30D5\u30A3\u30FC\u30EB\u30C9\u3001IP\u3001\u30BF\u30A4\u30E0\u30BE\u30FC\u30F3\u3001\u6642\u9593\u5E2F\u3092\u78BA\u8A8D\u3057\u3066\u304F\u3060\u3055\u3044\u3002\u30D5\u30A1\u30A4\u30EB\u306F\u8AAD\u307F\u53D6\u308A\u53EF\u80FD\u306A 1 MiB \u4EE5\u4E0B\u306E YAML \u3092\u6307\u5B9A\u3057\u3001\u4FEE\u6B63\u5F8C\u306B\u518D\u30D1\u30C3\u30B1\u30FC\u30B8\u3057\u3066\u304F\u3060\u3055\u3044\u3002",
|
|
341
334
|
deploy_httpsPortsInvalid: "HTTP \u3068 HTTPS \u306E\u30DD\u30FC\u30C8\u306F\u7570\u306A\u308B\u5FC5\u8981\u304C\u3042\u308A\u307E\u3059\u3002Let\u2019s Encrypt HTTP-01 \u306B\u306F\u30DB\u30B9\u30C8\u306E\u30DD\u30FC\u30C8 80 \u304C\u5FC5\u8981\u3067\u3059\u3002",
|
|
342
335
|
deploy_httpsCertificateInvalid: "HTTPS \u8A3C\u660E\u66F8\u304C\u7121\u52B9\u3067\u3059\u3002PEM \u30D5\u30A1\u30A4\u30EB\u3001\u6697\u53F7\u5316\u3055\u308C\u3066\u3044\u306A\u3044\u79D8\u5BC6\u9375\u3068\u306E\u4E00\u81F4\u3001\u6709\u52B9\u671F\u9593\u3001\u30C9\u30E1\u30A4\u30F3\u3092\u78BA\u8A8D\u3057\u3001\u518D\u4F5C\u6210\u3057\u3066\u304F\u3060\u3055\u3044\u3002",
|
|
@@ -479,9 +472,7 @@ var zh_CN_default = {
|
|
|
479
472
|
deploy_uploadPasswordCancelled: "\u5DF2\u53D6\u6D88\u5BC6\u7801\u8F93\u5165\uFF0C\u5C1A\u672A\u8FDE\u63A5\u670D\u52A1\u5668\u3002",
|
|
480
473
|
deploy_uploadPasswordInvalid: "SSH \u5BC6\u7801\u987B\u4E3A 1\u20131023 \u4E2A UTF-8 \u5B57\u8282\uFF0C\u4E14\u4E0D\u80FD\u5305\u542B\u6362\u884C\u3002",
|
|
481
474
|
deploy_managedSecretsInvalid: "\u81EA\u5EFA\u4E2D\u95F4\u4EF6\u5019\u9009\u51ED\u636E\u65E0\u6548\uFF0C\u8BF7\u91CD\u65B0\u751F\u6210\u90E8\u7F72\u5305\uFF1B\u670D\u52A1\u5668\u51ED\u636E\u4E0D\u4F1A\u81EA\u52A8\u66F4\u6362\u3002",
|
|
482
|
-
deploy_secretImportFailed: "\u65E7\u51ED\u636E\u5BFC\u5165\u5728 {phase} \u9636\u6BB5\u88AB\u62D2\u7EDD\uFF1B\u8BF7\u6838\u5BF9\u79C1\u6709\u8F93\u5165\u6743\u9650\u3001\u9879\u76EE/\u5377\u8EAB\u4EFD\u3001\u5B9E\u9645\u670D\u52A1\u51ED\u636E\u53CA\u90E8\u7F72\u9501\uFF0C\u672A\u6267\u884C\u670D\u52A1\u66F4\u65B0\uFF1B\u63D0\u4EA4\u7ED3\u679C\u4E0D\u660E\u65F6\u8BF7\u6838\u5BF9\u53D7\u4FDD\u62A4\u7684\u72B6\u6001\u6587\u4EF6",
|
|
483
475
|
deploy_projectNameRequired: "\u5DE5\u7A0B\u6839 package.json.name \u7F3A\u5931\u6216\u65E0\u6CD5\u5F52\u4E00\u5316\uFF0C\u4E0D\u80FD\u751F\u6210\u955C\u50CF\u4E0E\u90E8\u7F72\u8D44\u6E90\u8EAB\u4EFD\u3002",
|
|
484
|
-
deploy_projectNameOverrideRemoved: "\u90E8\u7F72\u9879\u76EE\u540D\u7EDF\u4E00\u6765\u81EA\u5DE5\u7A0B\u6839 package.json.name\uFF1B\u8BF7\u79FB\u9664\u65E7 projectName \u8986\u76D6\u53C2\u6570\u3002",
|
|
485
476
|
deploy_requestAccessInvalid: "\u8BF7\u6C42\u8BBF\u95EE\u7B56\u7565\u914D\u7F6E\u65E0\u6548\uFF08{field}\uFF09\u3002\u8BF7\u68C0\u67E5\u5B57\u6BB5\u3001IP\u3001\u65F6\u533A\u53CA\u65F6\u95F4\u7A97\u53E3\uFF1B\u5982\u5F15\u7528\u6587\u4EF6\uFF0C\u8BF7\u786E\u8BA4\u6587\u4EF6\u53EF\u8BFB\u3001\u683C\u5F0F\u4E3A YAML \u4E14\u4E0D\u8D85\u8FC7 1 MiB\u3002\u4FEE\u6B63\u540E\u91CD\u65B0\u6253\u5305\u3002",
|
|
486
477
|
deploy_httpsPortsInvalid: "HTTP \u4E0E HTTPS \u7AEF\u53E3\u5FC5\u987B\u4E0D\u540C\uFF1BLet\u2019s Encrypt \u7684 HTTP-01 \u5165\u53E3\u5FC5\u987B\u4F7F\u7528\u5BBF\u4E3B\u673A 80 \u7AEF\u53E3\u3002",
|
|
487
478
|
deploy_httpsCertificateInvalid: "HTTPS \u8BC1\u4E66\u65E0\u6548\uFF1A\u8BF7\u6838\u5BF9 PEM \u6587\u4EF6\u53EF\u8BFB\u3001\u8BC1\u4E66\u4E0E\u672A\u52A0\u5BC6\u79C1\u94A5\u5339\u914D\u3001\u8BC1\u4E66\u6709\u6548\u671F\u53CA\u57DF\u540D\uFF0C\u7136\u540E\u91CD\u65B0\u6253\u5305\u3002",
|
|
@@ -2263,6 +2254,320 @@ var deploy_config_shapes_default = {
|
|
|
2263
2254
|
pattern: "^[A-Z_]\\w*$"
|
|
2264
2255
|
}
|
|
2265
2256
|
},
|
|
2257
|
+
environmentIds: {
|
|
2258
|
+
default: [],
|
|
2259
|
+
maxItems: 64,
|
|
2260
|
+
type: "array",
|
|
2261
|
+
items: {
|
|
2262
|
+
type: "string",
|
|
2263
|
+
pattern: "^[a-z0-9][\\w-]{0,95}$"
|
|
2264
|
+
}
|
|
2265
|
+
},
|
|
2266
|
+
allowedCapabilities: {
|
|
2267
|
+
default: [],
|
|
2268
|
+
maxItems: 64,
|
|
2269
|
+
type: "array",
|
|
2270
|
+
items: {
|
|
2271
|
+
anyOf: [
|
|
2272
|
+
{
|
|
2273
|
+
type: "string",
|
|
2274
|
+
pattern: "^[a-z0-9][\\w-]{0,95}$"
|
|
2275
|
+
},
|
|
2276
|
+
{
|
|
2277
|
+
type: "string",
|
|
2278
|
+
pattern: "^[\\w.-]{1,96}$"
|
|
2279
|
+
}
|
|
2280
|
+
]
|
|
2281
|
+
}
|
|
2282
|
+
},
|
|
2283
|
+
defaults: {
|
|
2284
|
+
type: "object",
|
|
2285
|
+
properties: {
|
|
2286
|
+
model: {
|
|
2287
|
+
type: "object",
|
|
2288
|
+
properties: {
|
|
2289
|
+
id: {
|
|
2290
|
+
type: "string",
|
|
2291
|
+
minLength: 1,
|
|
2292
|
+
maxLength: 100,
|
|
2293
|
+
pattern: "^[\\w.-]+$"
|
|
2294
|
+
},
|
|
2295
|
+
reasoningEffort: {
|
|
2296
|
+
type: "string",
|
|
2297
|
+
minLength: 1,
|
|
2298
|
+
maxLength: 100,
|
|
2299
|
+
pattern: "^[\\w.-]+$"
|
|
2300
|
+
},
|
|
2301
|
+
temperature: {
|
|
2302
|
+
type: "number",
|
|
2303
|
+
minimum: 0,
|
|
2304
|
+
maximum: 2
|
|
2305
|
+
},
|
|
2306
|
+
maxOutputTokens: {
|
|
2307
|
+
type: "integer",
|
|
2308
|
+
exclusiveMinimum: 0,
|
|
2309
|
+
maximum: 9007199254740991
|
|
2310
|
+
}
|
|
2311
|
+
},
|
|
2312
|
+
additionalProperties: false
|
|
2313
|
+
},
|
|
2314
|
+
execution: {
|
|
2315
|
+
type: "object",
|
|
2316
|
+
properties: {
|
|
2317
|
+
mode: {
|
|
2318
|
+
type: "string",
|
|
2319
|
+
enum: [
|
|
2320
|
+
"auto",
|
|
2321
|
+
"shared",
|
|
2322
|
+
"user",
|
|
2323
|
+
"task"
|
|
2324
|
+
]
|
|
2325
|
+
},
|
|
2326
|
+
environmentId: {
|
|
2327
|
+
type: "string",
|
|
2328
|
+
minLength: 1,
|
|
2329
|
+
maxLength: 100,
|
|
2330
|
+
pattern: "^[\\w.-]+$"
|
|
2331
|
+
},
|
|
2332
|
+
resources: {
|
|
2333
|
+
type: "object",
|
|
2334
|
+
properties: {
|
|
2335
|
+
cpu: {
|
|
2336
|
+
type: "number",
|
|
2337
|
+
exclusiveMinimum: 0,
|
|
2338
|
+
maximum: 1024
|
|
2339
|
+
},
|
|
2340
|
+
memoryMiB: {
|
|
2341
|
+
type: "integer",
|
|
2342
|
+
minimum: 16,
|
|
2343
|
+
maximum: 9007199254740991
|
|
2344
|
+
}
|
|
2345
|
+
},
|
|
2346
|
+
additionalProperties: false
|
|
2347
|
+
}
|
|
2348
|
+
},
|
|
2349
|
+
additionalProperties: false
|
|
2350
|
+
},
|
|
2351
|
+
workspaceId: {
|
|
2352
|
+
type: "string",
|
|
2353
|
+
minLength: 1,
|
|
2354
|
+
maxLength: 100,
|
|
2355
|
+
pattern: "^[\\w.-]+$"
|
|
2356
|
+
},
|
|
2357
|
+
context: {
|
|
2358
|
+
type: "object",
|
|
2359
|
+
properties: {
|
|
2360
|
+
fileIds: {
|
|
2361
|
+
maxItems: 64,
|
|
2362
|
+
type: "array",
|
|
2363
|
+
items: {
|
|
2364
|
+
type: "string",
|
|
2365
|
+
minLength: 1,
|
|
2366
|
+
maxLength: 100,
|
|
2367
|
+
pattern: "^[\\w.-]+$"
|
|
2368
|
+
}
|
|
2369
|
+
},
|
|
2370
|
+
knowledgeBaseIds: {
|
|
2371
|
+
maxItems: 64,
|
|
2372
|
+
type: "array",
|
|
2373
|
+
items: {
|
|
2374
|
+
type: "string",
|
|
2375
|
+
minLength: 1,
|
|
2376
|
+
maxLength: 100,
|
|
2377
|
+
pattern: "^[\\w.-]+$"
|
|
2378
|
+
}
|
|
2379
|
+
},
|
|
2380
|
+
skillIds: {
|
|
2381
|
+
maxItems: 64,
|
|
2382
|
+
type: "array",
|
|
2383
|
+
items: {
|
|
2384
|
+
type: "string",
|
|
2385
|
+
minLength: 1,
|
|
2386
|
+
maxLength: 100,
|
|
2387
|
+
pattern: "^[\\w.-]+$"
|
|
2388
|
+
}
|
|
2389
|
+
},
|
|
2390
|
+
memory: {
|
|
2391
|
+
type: "object",
|
|
2392
|
+
properties: {
|
|
2393
|
+
scope: {
|
|
2394
|
+
type: "string",
|
|
2395
|
+
enum: [
|
|
2396
|
+
"none",
|
|
2397
|
+
"user",
|
|
2398
|
+
"space"
|
|
2399
|
+
]
|
|
2400
|
+
},
|
|
2401
|
+
spaceId: {
|
|
2402
|
+
type: "string",
|
|
2403
|
+
minLength: 1,
|
|
2404
|
+
maxLength: 100,
|
|
2405
|
+
pattern: "^[\\w.-]+$"
|
|
2406
|
+
},
|
|
2407
|
+
write: {
|
|
2408
|
+
type: "string",
|
|
2409
|
+
enum: [
|
|
2410
|
+
"none",
|
|
2411
|
+
"confirm",
|
|
2412
|
+
"on-success"
|
|
2413
|
+
]
|
|
2414
|
+
}
|
|
2415
|
+
},
|
|
2416
|
+
required: [
|
|
2417
|
+
"scope"
|
|
2418
|
+
],
|
|
2419
|
+
additionalProperties: false
|
|
2420
|
+
}
|
|
2421
|
+
},
|
|
2422
|
+
additionalProperties: false
|
|
2423
|
+
},
|
|
2424
|
+
tools: {
|
|
2425
|
+
type: "object",
|
|
2426
|
+
properties: {
|
|
2427
|
+
allow: {
|
|
2428
|
+
maxItems: 64,
|
|
2429
|
+
type: "array",
|
|
2430
|
+
items: {
|
|
2431
|
+
type: "string",
|
|
2432
|
+
minLength: 1,
|
|
2433
|
+
maxLength: 100,
|
|
2434
|
+
pattern: "^[\\w.-]+$"
|
|
2435
|
+
}
|
|
2436
|
+
}
|
|
2437
|
+
},
|
|
2438
|
+
additionalProperties: false
|
|
2439
|
+
},
|
|
2440
|
+
capabilities: {
|
|
2441
|
+
maxItems: 64,
|
|
2442
|
+
type: "array",
|
|
2443
|
+
items: {
|
|
2444
|
+
type: "string",
|
|
2445
|
+
minLength: 1,
|
|
2446
|
+
maxLength: 100,
|
|
2447
|
+
pattern: "^[\\w.-]+$"
|
|
2448
|
+
}
|
|
2449
|
+
},
|
|
2450
|
+
permissions: {
|
|
2451
|
+
type: "object",
|
|
2452
|
+
properties: {
|
|
2453
|
+
profile: {
|
|
2454
|
+
type: "string",
|
|
2455
|
+
enum: [
|
|
2456
|
+
"read-only",
|
|
2457
|
+
"workspace",
|
|
2458
|
+
"full-access"
|
|
2459
|
+
]
|
|
2460
|
+
},
|
|
2461
|
+
approval: {
|
|
2462
|
+
type: "string",
|
|
2463
|
+
enum: [
|
|
2464
|
+
"on-request",
|
|
2465
|
+
"never"
|
|
2466
|
+
]
|
|
2467
|
+
},
|
|
2468
|
+
ruleIds: {
|
|
2469
|
+
maxItems: 64,
|
|
2470
|
+
type: "array",
|
|
2471
|
+
items: {
|
|
2472
|
+
type: "string",
|
|
2473
|
+
minLength: 1,
|
|
2474
|
+
maxLength: 100,
|
|
2475
|
+
pattern: "^[\\w.-]+$"
|
|
2476
|
+
}
|
|
2477
|
+
}
|
|
2478
|
+
},
|
|
2479
|
+
additionalProperties: false
|
|
2480
|
+
},
|
|
2481
|
+
limits: {
|
|
2482
|
+
type: "object",
|
|
2483
|
+
properties: {
|
|
2484
|
+
queueTimeoutMs: {
|
|
2485
|
+
type: "integer",
|
|
2486
|
+
minimum: 1,
|
|
2487
|
+
maximum: 864e5
|
|
2488
|
+
},
|
|
2489
|
+
runTimeoutMs: {
|
|
2490
|
+
type: "integer",
|
|
2491
|
+
minimum: 1,
|
|
2492
|
+
maximum: 864e5
|
|
2493
|
+
},
|
|
2494
|
+
approvalTimeoutMs: {
|
|
2495
|
+
type: "integer",
|
|
2496
|
+
minimum: 1,
|
|
2497
|
+
maximum: 864e5
|
|
2498
|
+
},
|
|
2499
|
+
maxOutputBytes: {
|
|
2500
|
+
type: "integer",
|
|
2501
|
+
minimum: 1,
|
|
2502
|
+
maximum: 8388608
|
|
2503
|
+
}
|
|
2504
|
+
},
|
|
2505
|
+
additionalProperties: false
|
|
2506
|
+
},
|
|
2507
|
+
output: {
|
|
2508
|
+
type: "object",
|
|
2509
|
+
properties: {
|
|
2510
|
+
format: {
|
|
2511
|
+
type: "string",
|
|
2512
|
+
enum: [
|
|
2513
|
+
"text",
|
|
2514
|
+
"json"
|
|
2515
|
+
]
|
|
2516
|
+
},
|
|
2517
|
+
schema: {
|
|
2518
|
+
type: "object",
|
|
2519
|
+
propertyNames: {
|
|
2520
|
+
type: "string"
|
|
2521
|
+
},
|
|
2522
|
+
additionalProperties: {
|
|
2523
|
+
$ref: "#/$defs/__schema0"
|
|
2524
|
+
}
|
|
2525
|
+
}
|
|
2526
|
+
},
|
|
2527
|
+
required: [
|
|
2528
|
+
"format"
|
|
2529
|
+
],
|
|
2530
|
+
additionalProperties: false
|
|
2531
|
+
},
|
|
2532
|
+
native: {
|
|
2533
|
+
type: "object",
|
|
2534
|
+
properties: {
|
|
2535
|
+
adapter: {
|
|
2536
|
+
type: "string",
|
|
2537
|
+
minLength: 1,
|
|
2538
|
+
maxLength: 100,
|
|
2539
|
+
pattern: "^[\\w.-]+$"
|
|
2540
|
+
},
|
|
2541
|
+
options: {
|
|
2542
|
+
type: "object",
|
|
2543
|
+
propertyNames: {
|
|
2544
|
+
type: "string"
|
|
2545
|
+
},
|
|
2546
|
+
additionalProperties: {
|
|
2547
|
+
$ref: "#/$defs/__schema1"
|
|
2548
|
+
}
|
|
2549
|
+
}
|
|
2550
|
+
},
|
|
2551
|
+
required: [
|
|
2552
|
+
"adapter",
|
|
2553
|
+
"options"
|
|
2554
|
+
],
|
|
2555
|
+
additionalProperties: false
|
|
2556
|
+
},
|
|
2557
|
+
metadata: {
|
|
2558
|
+
type: "object",
|
|
2559
|
+
propertyNames: {
|
|
2560
|
+
type: "string",
|
|
2561
|
+
pattern: "^[\\w.-]{1,48}$"
|
|
2562
|
+
},
|
|
2563
|
+
additionalProperties: {
|
|
2564
|
+
type: "string",
|
|
2565
|
+
maxLength: 128
|
|
2566
|
+
}
|
|
2567
|
+
}
|
|
2568
|
+
},
|
|
2569
|
+
additionalProperties: false
|
|
2570
|
+
},
|
|
2266
2571
|
mode: {
|
|
2267
2572
|
type: "string",
|
|
2268
2573
|
const: "custom-service"
|
|
@@ -2354,6 +2659,320 @@ var deploy_config_shapes_default = {
|
|
|
2354
2659
|
pattern: "^[A-Z_]\\w*$"
|
|
2355
2660
|
}
|
|
2356
2661
|
},
|
|
2662
|
+
environmentIds: {
|
|
2663
|
+
default: [],
|
|
2664
|
+
maxItems: 64,
|
|
2665
|
+
type: "array",
|
|
2666
|
+
items: {
|
|
2667
|
+
type: "string",
|
|
2668
|
+
pattern: "^[a-z0-9][\\w-]{0,95}$"
|
|
2669
|
+
}
|
|
2670
|
+
},
|
|
2671
|
+
allowedCapabilities: {
|
|
2672
|
+
default: [],
|
|
2673
|
+
maxItems: 64,
|
|
2674
|
+
type: "array",
|
|
2675
|
+
items: {
|
|
2676
|
+
anyOf: [
|
|
2677
|
+
{
|
|
2678
|
+
type: "string",
|
|
2679
|
+
pattern: "^[a-z0-9][\\w-]{0,95}$"
|
|
2680
|
+
},
|
|
2681
|
+
{
|
|
2682
|
+
type: "string",
|
|
2683
|
+
pattern: "^[\\w.-]{1,96}$"
|
|
2684
|
+
}
|
|
2685
|
+
]
|
|
2686
|
+
}
|
|
2687
|
+
},
|
|
2688
|
+
defaults: {
|
|
2689
|
+
type: "object",
|
|
2690
|
+
properties: {
|
|
2691
|
+
model: {
|
|
2692
|
+
type: "object",
|
|
2693
|
+
properties: {
|
|
2694
|
+
id: {
|
|
2695
|
+
type: "string",
|
|
2696
|
+
minLength: 1,
|
|
2697
|
+
maxLength: 100,
|
|
2698
|
+
pattern: "^[\\w.-]+$"
|
|
2699
|
+
},
|
|
2700
|
+
reasoningEffort: {
|
|
2701
|
+
type: "string",
|
|
2702
|
+
minLength: 1,
|
|
2703
|
+
maxLength: 100,
|
|
2704
|
+
pattern: "^[\\w.-]+$"
|
|
2705
|
+
},
|
|
2706
|
+
temperature: {
|
|
2707
|
+
type: "number",
|
|
2708
|
+
minimum: 0,
|
|
2709
|
+
maximum: 2
|
|
2710
|
+
},
|
|
2711
|
+
maxOutputTokens: {
|
|
2712
|
+
type: "integer",
|
|
2713
|
+
exclusiveMinimum: 0,
|
|
2714
|
+
maximum: 9007199254740991
|
|
2715
|
+
}
|
|
2716
|
+
},
|
|
2717
|
+
additionalProperties: false
|
|
2718
|
+
},
|
|
2719
|
+
execution: {
|
|
2720
|
+
type: "object",
|
|
2721
|
+
properties: {
|
|
2722
|
+
mode: {
|
|
2723
|
+
type: "string",
|
|
2724
|
+
enum: [
|
|
2725
|
+
"auto",
|
|
2726
|
+
"shared",
|
|
2727
|
+
"user",
|
|
2728
|
+
"task"
|
|
2729
|
+
]
|
|
2730
|
+
},
|
|
2731
|
+
environmentId: {
|
|
2732
|
+
type: "string",
|
|
2733
|
+
minLength: 1,
|
|
2734
|
+
maxLength: 100,
|
|
2735
|
+
pattern: "^[\\w.-]+$"
|
|
2736
|
+
},
|
|
2737
|
+
resources: {
|
|
2738
|
+
type: "object",
|
|
2739
|
+
properties: {
|
|
2740
|
+
cpu: {
|
|
2741
|
+
type: "number",
|
|
2742
|
+
exclusiveMinimum: 0,
|
|
2743
|
+
maximum: 1024
|
|
2744
|
+
},
|
|
2745
|
+
memoryMiB: {
|
|
2746
|
+
type: "integer",
|
|
2747
|
+
minimum: 16,
|
|
2748
|
+
maximum: 9007199254740991
|
|
2749
|
+
}
|
|
2750
|
+
},
|
|
2751
|
+
additionalProperties: false
|
|
2752
|
+
}
|
|
2753
|
+
},
|
|
2754
|
+
additionalProperties: false
|
|
2755
|
+
},
|
|
2756
|
+
workspaceId: {
|
|
2757
|
+
type: "string",
|
|
2758
|
+
minLength: 1,
|
|
2759
|
+
maxLength: 100,
|
|
2760
|
+
pattern: "^[\\w.-]+$"
|
|
2761
|
+
},
|
|
2762
|
+
context: {
|
|
2763
|
+
type: "object",
|
|
2764
|
+
properties: {
|
|
2765
|
+
fileIds: {
|
|
2766
|
+
maxItems: 64,
|
|
2767
|
+
type: "array",
|
|
2768
|
+
items: {
|
|
2769
|
+
type: "string",
|
|
2770
|
+
minLength: 1,
|
|
2771
|
+
maxLength: 100,
|
|
2772
|
+
pattern: "^[\\w.-]+$"
|
|
2773
|
+
}
|
|
2774
|
+
},
|
|
2775
|
+
knowledgeBaseIds: {
|
|
2776
|
+
maxItems: 64,
|
|
2777
|
+
type: "array",
|
|
2778
|
+
items: {
|
|
2779
|
+
type: "string",
|
|
2780
|
+
minLength: 1,
|
|
2781
|
+
maxLength: 100,
|
|
2782
|
+
pattern: "^[\\w.-]+$"
|
|
2783
|
+
}
|
|
2784
|
+
},
|
|
2785
|
+
skillIds: {
|
|
2786
|
+
maxItems: 64,
|
|
2787
|
+
type: "array",
|
|
2788
|
+
items: {
|
|
2789
|
+
type: "string",
|
|
2790
|
+
minLength: 1,
|
|
2791
|
+
maxLength: 100,
|
|
2792
|
+
pattern: "^[\\w.-]+$"
|
|
2793
|
+
}
|
|
2794
|
+
},
|
|
2795
|
+
memory: {
|
|
2796
|
+
type: "object",
|
|
2797
|
+
properties: {
|
|
2798
|
+
scope: {
|
|
2799
|
+
type: "string",
|
|
2800
|
+
enum: [
|
|
2801
|
+
"none",
|
|
2802
|
+
"user",
|
|
2803
|
+
"space"
|
|
2804
|
+
]
|
|
2805
|
+
},
|
|
2806
|
+
spaceId: {
|
|
2807
|
+
type: "string",
|
|
2808
|
+
minLength: 1,
|
|
2809
|
+
maxLength: 100,
|
|
2810
|
+
pattern: "^[\\w.-]+$"
|
|
2811
|
+
},
|
|
2812
|
+
write: {
|
|
2813
|
+
type: "string",
|
|
2814
|
+
enum: [
|
|
2815
|
+
"none",
|
|
2816
|
+
"confirm",
|
|
2817
|
+
"on-success"
|
|
2818
|
+
]
|
|
2819
|
+
}
|
|
2820
|
+
},
|
|
2821
|
+
required: [
|
|
2822
|
+
"scope"
|
|
2823
|
+
],
|
|
2824
|
+
additionalProperties: false
|
|
2825
|
+
}
|
|
2826
|
+
},
|
|
2827
|
+
additionalProperties: false
|
|
2828
|
+
},
|
|
2829
|
+
tools: {
|
|
2830
|
+
type: "object",
|
|
2831
|
+
properties: {
|
|
2832
|
+
allow: {
|
|
2833
|
+
maxItems: 64,
|
|
2834
|
+
type: "array",
|
|
2835
|
+
items: {
|
|
2836
|
+
type: "string",
|
|
2837
|
+
minLength: 1,
|
|
2838
|
+
maxLength: 100,
|
|
2839
|
+
pattern: "^[\\w.-]+$"
|
|
2840
|
+
}
|
|
2841
|
+
}
|
|
2842
|
+
},
|
|
2843
|
+
additionalProperties: false
|
|
2844
|
+
},
|
|
2845
|
+
capabilities: {
|
|
2846
|
+
maxItems: 64,
|
|
2847
|
+
type: "array",
|
|
2848
|
+
items: {
|
|
2849
|
+
type: "string",
|
|
2850
|
+
minLength: 1,
|
|
2851
|
+
maxLength: 100,
|
|
2852
|
+
pattern: "^[\\w.-]+$"
|
|
2853
|
+
}
|
|
2854
|
+
},
|
|
2855
|
+
permissions: {
|
|
2856
|
+
type: "object",
|
|
2857
|
+
properties: {
|
|
2858
|
+
profile: {
|
|
2859
|
+
type: "string",
|
|
2860
|
+
enum: [
|
|
2861
|
+
"read-only",
|
|
2862
|
+
"workspace",
|
|
2863
|
+
"full-access"
|
|
2864
|
+
]
|
|
2865
|
+
},
|
|
2866
|
+
approval: {
|
|
2867
|
+
type: "string",
|
|
2868
|
+
enum: [
|
|
2869
|
+
"on-request",
|
|
2870
|
+
"never"
|
|
2871
|
+
]
|
|
2872
|
+
},
|
|
2873
|
+
ruleIds: {
|
|
2874
|
+
maxItems: 64,
|
|
2875
|
+
type: "array",
|
|
2876
|
+
items: {
|
|
2877
|
+
type: "string",
|
|
2878
|
+
minLength: 1,
|
|
2879
|
+
maxLength: 100,
|
|
2880
|
+
pattern: "^[\\w.-]+$"
|
|
2881
|
+
}
|
|
2882
|
+
}
|
|
2883
|
+
},
|
|
2884
|
+
additionalProperties: false
|
|
2885
|
+
},
|
|
2886
|
+
limits: {
|
|
2887
|
+
type: "object",
|
|
2888
|
+
properties: {
|
|
2889
|
+
queueTimeoutMs: {
|
|
2890
|
+
type: "integer",
|
|
2891
|
+
minimum: 1,
|
|
2892
|
+
maximum: 864e5
|
|
2893
|
+
},
|
|
2894
|
+
runTimeoutMs: {
|
|
2895
|
+
type: "integer",
|
|
2896
|
+
minimum: 1,
|
|
2897
|
+
maximum: 864e5
|
|
2898
|
+
},
|
|
2899
|
+
approvalTimeoutMs: {
|
|
2900
|
+
type: "integer",
|
|
2901
|
+
minimum: 1,
|
|
2902
|
+
maximum: 864e5
|
|
2903
|
+
},
|
|
2904
|
+
maxOutputBytes: {
|
|
2905
|
+
type: "integer",
|
|
2906
|
+
minimum: 1,
|
|
2907
|
+
maximum: 8388608
|
|
2908
|
+
}
|
|
2909
|
+
},
|
|
2910
|
+
additionalProperties: false
|
|
2911
|
+
},
|
|
2912
|
+
output: {
|
|
2913
|
+
type: "object",
|
|
2914
|
+
properties: {
|
|
2915
|
+
format: {
|
|
2916
|
+
type: "string",
|
|
2917
|
+
enum: [
|
|
2918
|
+
"text",
|
|
2919
|
+
"json"
|
|
2920
|
+
]
|
|
2921
|
+
},
|
|
2922
|
+
schema: {
|
|
2923
|
+
type: "object",
|
|
2924
|
+
propertyNames: {
|
|
2925
|
+
type: "string"
|
|
2926
|
+
},
|
|
2927
|
+
additionalProperties: {
|
|
2928
|
+
$ref: "#/$defs/__schema0"
|
|
2929
|
+
}
|
|
2930
|
+
}
|
|
2931
|
+
},
|
|
2932
|
+
required: [
|
|
2933
|
+
"format"
|
|
2934
|
+
],
|
|
2935
|
+
additionalProperties: false
|
|
2936
|
+
},
|
|
2937
|
+
native: {
|
|
2938
|
+
type: "object",
|
|
2939
|
+
properties: {
|
|
2940
|
+
adapter: {
|
|
2941
|
+
type: "string",
|
|
2942
|
+
minLength: 1,
|
|
2943
|
+
maxLength: 100,
|
|
2944
|
+
pattern: "^[\\w.-]+$"
|
|
2945
|
+
},
|
|
2946
|
+
options: {
|
|
2947
|
+
type: "object",
|
|
2948
|
+
propertyNames: {
|
|
2949
|
+
type: "string"
|
|
2950
|
+
},
|
|
2951
|
+
additionalProperties: {
|
|
2952
|
+
$ref: "#/$defs/__schema1"
|
|
2953
|
+
}
|
|
2954
|
+
}
|
|
2955
|
+
},
|
|
2956
|
+
required: [
|
|
2957
|
+
"adapter",
|
|
2958
|
+
"options"
|
|
2959
|
+
],
|
|
2960
|
+
additionalProperties: false
|
|
2961
|
+
},
|
|
2962
|
+
metadata: {
|
|
2963
|
+
type: "object",
|
|
2964
|
+
propertyNames: {
|
|
2965
|
+
type: "string",
|
|
2966
|
+
pattern: "^[\\w.-]{1,48}$"
|
|
2967
|
+
},
|
|
2968
|
+
additionalProperties: {
|
|
2969
|
+
type: "string",
|
|
2970
|
+
maxLength: 128
|
|
2971
|
+
}
|
|
2972
|
+
}
|
|
2973
|
+
},
|
|
2974
|
+
additionalProperties: false
|
|
2975
|
+
},
|
|
2357
2976
|
command: {
|
|
2358
2977
|
type: "string",
|
|
2359
2978
|
minLength: 1
|
|
@@ -2425,6 +3044,320 @@ var deploy_config_shapes_default = {
|
|
|
2425
3044
|
pattern: "^[A-Z_]\\w*$"
|
|
2426
3045
|
}
|
|
2427
3046
|
},
|
|
3047
|
+
environmentIds: {
|
|
3048
|
+
default: [],
|
|
3049
|
+
maxItems: 64,
|
|
3050
|
+
type: "array",
|
|
3051
|
+
items: {
|
|
3052
|
+
type: "string",
|
|
3053
|
+
pattern: "^[a-z0-9][\\w-]{0,95}$"
|
|
3054
|
+
}
|
|
3055
|
+
},
|
|
3056
|
+
allowedCapabilities: {
|
|
3057
|
+
default: [],
|
|
3058
|
+
maxItems: 64,
|
|
3059
|
+
type: "array",
|
|
3060
|
+
items: {
|
|
3061
|
+
anyOf: [
|
|
3062
|
+
{
|
|
3063
|
+
type: "string",
|
|
3064
|
+
pattern: "^[a-z0-9][\\w-]{0,95}$"
|
|
3065
|
+
},
|
|
3066
|
+
{
|
|
3067
|
+
type: "string",
|
|
3068
|
+
pattern: "^[\\w.-]{1,96}$"
|
|
3069
|
+
}
|
|
3070
|
+
]
|
|
3071
|
+
}
|
|
3072
|
+
},
|
|
3073
|
+
defaults: {
|
|
3074
|
+
type: "object",
|
|
3075
|
+
properties: {
|
|
3076
|
+
model: {
|
|
3077
|
+
type: "object",
|
|
3078
|
+
properties: {
|
|
3079
|
+
id: {
|
|
3080
|
+
type: "string",
|
|
3081
|
+
minLength: 1,
|
|
3082
|
+
maxLength: 100,
|
|
3083
|
+
pattern: "^[\\w.-]+$"
|
|
3084
|
+
},
|
|
3085
|
+
reasoningEffort: {
|
|
3086
|
+
type: "string",
|
|
3087
|
+
minLength: 1,
|
|
3088
|
+
maxLength: 100,
|
|
3089
|
+
pattern: "^[\\w.-]+$"
|
|
3090
|
+
},
|
|
3091
|
+
temperature: {
|
|
3092
|
+
type: "number",
|
|
3093
|
+
minimum: 0,
|
|
3094
|
+
maximum: 2
|
|
3095
|
+
},
|
|
3096
|
+
maxOutputTokens: {
|
|
3097
|
+
type: "integer",
|
|
3098
|
+
exclusiveMinimum: 0,
|
|
3099
|
+
maximum: 9007199254740991
|
|
3100
|
+
}
|
|
3101
|
+
},
|
|
3102
|
+
additionalProperties: false
|
|
3103
|
+
},
|
|
3104
|
+
execution: {
|
|
3105
|
+
type: "object",
|
|
3106
|
+
properties: {
|
|
3107
|
+
mode: {
|
|
3108
|
+
type: "string",
|
|
3109
|
+
enum: [
|
|
3110
|
+
"auto",
|
|
3111
|
+
"shared",
|
|
3112
|
+
"user",
|
|
3113
|
+
"task"
|
|
3114
|
+
]
|
|
3115
|
+
},
|
|
3116
|
+
environmentId: {
|
|
3117
|
+
type: "string",
|
|
3118
|
+
minLength: 1,
|
|
3119
|
+
maxLength: 100,
|
|
3120
|
+
pattern: "^[\\w.-]+$"
|
|
3121
|
+
},
|
|
3122
|
+
resources: {
|
|
3123
|
+
type: "object",
|
|
3124
|
+
properties: {
|
|
3125
|
+
cpu: {
|
|
3126
|
+
type: "number",
|
|
3127
|
+
exclusiveMinimum: 0,
|
|
3128
|
+
maximum: 1024
|
|
3129
|
+
},
|
|
3130
|
+
memoryMiB: {
|
|
3131
|
+
type: "integer",
|
|
3132
|
+
minimum: 16,
|
|
3133
|
+
maximum: 9007199254740991
|
|
3134
|
+
}
|
|
3135
|
+
},
|
|
3136
|
+
additionalProperties: false
|
|
3137
|
+
}
|
|
3138
|
+
},
|
|
3139
|
+
additionalProperties: false
|
|
3140
|
+
},
|
|
3141
|
+
workspaceId: {
|
|
3142
|
+
type: "string",
|
|
3143
|
+
minLength: 1,
|
|
3144
|
+
maxLength: 100,
|
|
3145
|
+
pattern: "^[\\w.-]+$"
|
|
3146
|
+
},
|
|
3147
|
+
context: {
|
|
3148
|
+
type: "object",
|
|
3149
|
+
properties: {
|
|
3150
|
+
fileIds: {
|
|
3151
|
+
maxItems: 64,
|
|
3152
|
+
type: "array",
|
|
3153
|
+
items: {
|
|
3154
|
+
type: "string",
|
|
3155
|
+
minLength: 1,
|
|
3156
|
+
maxLength: 100,
|
|
3157
|
+
pattern: "^[\\w.-]+$"
|
|
3158
|
+
}
|
|
3159
|
+
},
|
|
3160
|
+
knowledgeBaseIds: {
|
|
3161
|
+
maxItems: 64,
|
|
3162
|
+
type: "array",
|
|
3163
|
+
items: {
|
|
3164
|
+
type: "string",
|
|
3165
|
+
minLength: 1,
|
|
3166
|
+
maxLength: 100,
|
|
3167
|
+
pattern: "^[\\w.-]+$"
|
|
3168
|
+
}
|
|
3169
|
+
},
|
|
3170
|
+
skillIds: {
|
|
3171
|
+
maxItems: 64,
|
|
3172
|
+
type: "array",
|
|
3173
|
+
items: {
|
|
3174
|
+
type: "string",
|
|
3175
|
+
minLength: 1,
|
|
3176
|
+
maxLength: 100,
|
|
3177
|
+
pattern: "^[\\w.-]+$"
|
|
3178
|
+
}
|
|
3179
|
+
},
|
|
3180
|
+
memory: {
|
|
3181
|
+
type: "object",
|
|
3182
|
+
properties: {
|
|
3183
|
+
scope: {
|
|
3184
|
+
type: "string",
|
|
3185
|
+
enum: [
|
|
3186
|
+
"none",
|
|
3187
|
+
"user",
|
|
3188
|
+
"space"
|
|
3189
|
+
]
|
|
3190
|
+
},
|
|
3191
|
+
spaceId: {
|
|
3192
|
+
type: "string",
|
|
3193
|
+
minLength: 1,
|
|
3194
|
+
maxLength: 100,
|
|
3195
|
+
pattern: "^[\\w.-]+$"
|
|
3196
|
+
},
|
|
3197
|
+
write: {
|
|
3198
|
+
type: "string",
|
|
3199
|
+
enum: [
|
|
3200
|
+
"none",
|
|
3201
|
+
"confirm",
|
|
3202
|
+
"on-success"
|
|
3203
|
+
]
|
|
3204
|
+
}
|
|
3205
|
+
},
|
|
3206
|
+
required: [
|
|
3207
|
+
"scope"
|
|
3208
|
+
],
|
|
3209
|
+
additionalProperties: false
|
|
3210
|
+
}
|
|
3211
|
+
},
|
|
3212
|
+
additionalProperties: false
|
|
3213
|
+
},
|
|
3214
|
+
tools: {
|
|
3215
|
+
type: "object",
|
|
3216
|
+
properties: {
|
|
3217
|
+
allow: {
|
|
3218
|
+
maxItems: 64,
|
|
3219
|
+
type: "array",
|
|
3220
|
+
items: {
|
|
3221
|
+
type: "string",
|
|
3222
|
+
minLength: 1,
|
|
3223
|
+
maxLength: 100,
|
|
3224
|
+
pattern: "^[\\w.-]+$"
|
|
3225
|
+
}
|
|
3226
|
+
}
|
|
3227
|
+
},
|
|
3228
|
+
additionalProperties: false
|
|
3229
|
+
},
|
|
3230
|
+
capabilities: {
|
|
3231
|
+
maxItems: 64,
|
|
3232
|
+
type: "array",
|
|
3233
|
+
items: {
|
|
3234
|
+
type: "string",
|
|
3235
|
+
minLength: 1,
|
|
3236
|
+
maxLength: 100,
|
|
3237
|
+
pattern: "^[\\w.-]+$"
|
|
3238
|
+
}
|
|
3239
|
+
},
|
|
3240
|
+
permissions: {
|
|
3241
|
+
type: "object",
|
|
3242
|
+
properties: {
|
|
3243
|
+
profile: {
|
|
3244
|
+
type: "string",
|
|
3245
|
+
enum: [
|
|
3246
|
+
"read-only",
|
|
3247
|
+
"workspace",
|
|
3248
|
+
"full-access"
|
|
3249
|
+
]
|
|
3250
|
+
},
|
|
3251
|
+
approval: {
|
|
3252
|
+
type: "string",
|
|
3253
|
+
enum: [
|
|
3254
|
+
"on-request",
|
|
3255
|
+
"never"
|
|
3256
|
+
]
|
|
3257
|
+
},
|
|
3258
|
+
ruleIds: {
|
|
3259
|
+
maxItems: 64,
|
|
3260
|
+
type: "array",
|
|
3261
|
+
items: {
|
|
3262
|
+
type: "string",
|
|
3263
|
+
minLength: 1,
|
|
3264
|
+
maxLength: 100,
|
|
3265
|
+
pattern: "^[\\w.-]+$"
|
|
3266
|
+
}
|
|
3267
|
+
}
|
|
3268
|
+
},
|
|
3269
|
+
additionalProperties: false
|
|
3270
|
+
},
|
|
3271
|
+
limits: {
|
|
3272
|
+
type: "object",
|
|
3273
|
+
properties: {
|
|
3274
|
+
queueTimeoutMs: {
|
|
3275
|
+
type: "integer",
|
|
3276
|
+
minimum: 1,
|
|
3277
|
+
maximum: 864e5
|
|
3278
|
+
},
|
|
3279
|
+
runTimeoutMs: {
|
|
3280
|
+
type: "integer",
|
|
3281
|
+
minimum: 1,
|
|
3282
|
+
maximum: 864e5
|
|
3283
|
+
},
|
|
3284
|
+
approvalTimeoutMs: {
|
|
3285
|
+
type: "integer",
|
|
3286
|
+
minimum: 1,
|
|
3287
|
+
maximum: 864e5
|
|
3288
|
+
},
|
|
3289
|
+
maxOutputBytes: {
|
|
3290
|
+
type: "integer",
|
|
3291
|
+
minimum: 1,
|
|
3292
|
+
maximum: 8388608
|
|
3293
|
+
}
|
|
3294
|
+
},
|
|
3295
|
+
additionalProperties: false
|
|
3296
|
+
},
|
|
3297
|
+
output: {
|
|
3298
|
+
type: "object",
|
|
3299
|
+
properties: {
|
|
3300
|
+
format: {
|
|
3301
|
+
type: "string",
|
|
3302
|
+
enum: [
|
|
3303
|
+
"text",
|
|
3304
|
+
"json"
|
|
3305
|
+
]
|
|
3306
|
+
},
|
|
3307
|
+
schema: {
|
|
3308
|
+
type: "object",
|
|
3309
|
+
propertyNames: {
|
|
3310
|
+
type: "string"
|
|
3311
|
+
},
|
|
3312
|
+
additionalProperties: {
|
|
3313
|
+
$ref: "#/$defs/__schema0"
|
|
3314
|
+
}
|
|
3315
|
+
}
|
|
3316
|
+
},
|
|
3317
|
+
required: [
|
|
3318
|
+
"format"
|
|
3319
|
+
],
|
|
3320
|
+
additionalProperties: false
|
|
3321
|
+
},
|
|
3322
|
+
native: {
|
|
3323
|
+
type: "object",
|
|
3324
|
+
properties: {
|
|
3325
|
+
adapter: {
|
|
3326
|
+
type: "string",
|
|
3327
|
+
minLength: 1,
|
|
3328
|
+
maxLength: 100,
|
|
3329
|
+
pattern: "^[\\w.-]+$"
|
|
3330
|
+
},
|
|
3331
|
+
options: {
|
|
3332
|
+
type: "object",
|
|
3333
|
+
propertyNames: {
|
|
3334
|
+
type: "string"
|
|
3335
|
+
},
|
|
3336
|
+
additionalProperties: {
|
|
3337
|
+
$ref: "#/$defs/__schema1"
|
|
3338
|
+
}
|
|
3339
|
+
}
|
|
3340
|
+
},
|
|
3341
|
+
required: [
|
|
3342
|
+
"adapter",
|
|
3343
|
+
"options"
|
|
3344
|
+
],
|
|
3345
|
+
additionalProperties: false
|
|
3346
|
+
},
|
|
3347
|
+
metadata: {
|
|
3348
|
+
type: "object",
|
|
3349
|
+
propertyNames: {
|
|
3350
|
+
type: "string",
|
|
3351
|
+
pattern: "^[\\w.-]{1,48}$"
|
|
3352
|
+
},
|
|
3353
|
+
additionalProperties: {
|
|
3354
|
+
type: "string",
|
|
3355
|
+
maxLength: 128
|
|
3356
|
+
}
|
|
3357
|
+
}
|
|
3358
|
+
},
|
|
3359
|
+
additionalProperties: false
|
|
3360
|
+
},
|
|
2428
3361
|
command: {
|
|
2429
3362
|
type: "string",
|
|
2430
3363
|
minLength: 1
|
|
@@ -2627,6 +3560,12 @@ var deploy_config_shapes_default = {
|
|
|
2627
3560
|
type: "integer",
|
|
2628
3561
|
exclusiveMinimum: 0,
|
|
2629
3562
|
maximum: 9007199254740991
|
|
3563
|
+
},
|
|
3564
|
+
workerIdleTimeoutMs: {
|
|
3565
|
+
default: 3e5,
|
|
3566
|
+
type: "integer",
|
|
3567
|
+
minimum: 1e3,
|
|
3568
|
+
maximum: 864e5
|
|
2630
3569
|
}
|
|
2631
3570
|
},
|
|
2632
3571
|
additionalProperties: false
|
|
@@ -2855,9 +3794,14 @@ var deploy_config_shapes_default = {
|
|
|
2855
3794
|
enum: [
|
|
2856
3795
|
"stdio",
|
|
2857
3796
|
"http",
|
|
2858
|
-
"sse"
|
|
3797
|
+
"sse",
|
|
3798
|
+
"job"
|
|
2859
3799
|
]
|
|
2860
3800
|
},
|
|
3801
|
+
environmentId: {
|
|
3802
|
+
type: "string",
|
|
3803
|
+
pattern: "^[\\w.-]{1,128}$"
|
|
3804
|
+
},
|
|
2861
3805
|
command: {
|
|
2862
3806
|
type: "string",
|
|
2863
3807
|
minLength: 1
|
|
@@ -2883,15 +3827,54 @@ var deploy_config_shapes_default = {
|
|
|
2883
3827
|
type: "string"
|
|
2884
3828
|
}
|
|
2885
3829
|
},
|
|
2886
|
-
headerEnv: {
|
|
2887
|
-
default: {},
|
|
3830
|
+
headerEnv: {
|
|
3831
|
+
default: {},
|
|
3832
|
+
type: "object",
|
|
3833
|
+
propertyNames: {
|
|
3834
|
+
type: "string"
|
|
3835
|
+
},
|
|
3836
|
+
additionalProperties: {
|
|
3837
|
+
type: "string"
|
|
3838
|
+
}
|
|
3839
|
+
},
|
|
3840
|
+
allowedTools: {
|
|
3841
|
+
minItems: 1,
|
|
3842
|
+
maxItems: 256,
|
|
3843
|
+
type: "array",
|
|
3844
|
+
items: {
|
|
3845
|
+
type: "string",
|
|
3846
|
+
minLength: 1,
|
|
3847
|
+
maxLength: 128
|
|
3848
|
+
}
|
|
3849
|
+
},
|
|
3850
|
+
timeoutMs: {
|
|
3851
|
+
type: "integer",
|
|
3852
|
+
minimum: 1,
|
|
3853
|
+
maximum: 3e5
|
|
3854
|
+
},
|
|
3855
|
+
pool: {
|
|
2888
3856
|
type: "object",
|
|
2889
|
-
|
|
2890
|
-
|
|
3857
|
+
properties: {
|
|
3858
|
+
maxRunning: {
|
|
3859
|
+
type: "integer",
|
|
3860
|
+
minimum: 1,
|
|
3861
|
+
maximum: 1e3
|
|
3862
|
+
},
|
|
3863
|
+
minReady: {
|
|
3864
|
+
type: "integer",
|
|
3865
|
+
minimum: 0,
|
|
3866
|
+
maximum: 1e3
|
|
3867
|
+
},
|
|
3868
|
+
maxTotal: {
|
|
3869
|
+
type: "integer",
|
|
3870
|
+
minimum: 1,
|
|
3871
|
+
maximum: 1e3
|
|
3872
|
+
}
|
|
2891
3873
|
},
|
|
2892
|
-
|
|
2893
|
-
|
|
2894
|
-
|
|
3874
|
+
required: [
|
|
3875
|
+
"maxRunning"
|
|
3876
|
+
],
|
|
3877
|
+
additionalProperties: false
|
|
2895
3878
|
}
|
|
2896
3879
|
},
|
|
2897
3880
|
required: [
|
|
@@ -3516,6 +4499,70 @@ var deploy_config_shapes_default = {
|
|
|
3516
4499
|
}
|
|
3517
4500
|
}
|
|
3518
4501
|
}
|
|
4502
|
+
},
|
|
4503
|
+
$defs: {
|
|
4504
|
+
__schema0: {
|
|
4505
|
+
anyOf: [
|
|
4506
|
+
{
|
|
4507
|
+
type: "string"
|
|
4508
|
+
},
|
|
4509
|
+
{
|
|
4510
|
+
type: "number"
|
|
4511
|
+
},
|
|
4512
|
+
{
|
|
4513
|
+
type: "boolean"
|
|
4514
|
+
},
|
|
4515
|
+
{
|
|
4516
|
+
type: "null"
|
|
4517
|
+
},
|
|
4518
|
+
{
|
|
4519
|
+
type: "array",
|
|
4520
|
+
items: {
|
|
4521
|
+
$ref: "#/$defs/__schema0"
|
|
4522
|
+
}
|
|
4523
|
+
},
|
|
4524
|
+
{
|
|
4525
|
+
type: "object",
|
|
4526
|
+
propertyNames: {
|
|
4527
|
+
type: "string"
|
|
4528
|
+
},
|
|
4529
|
+
additionalProperties: {
|
|
4530
|
+
$ref: "#/$defs/__schema0"
|
|
4531
|
+
}
|
|
4532
|
+
}
|
|
4533
|
+
]
|
|
4534
|
+
},
|
|
4535
|
+
__schema1: {
|
|
4536
|
+
anyOf: [
|
|
4537
|
+
{
|
|
4538
|
+
type: "string"
|
|
4539
|
+
},
|
|
4540
|
+
{
|
|
4541
|
+
type: "number"
|
|
4542
|
+
},
|
|
4543
|
+
{
|
|
4544
|
+
type: "boolean"
|
|
4545
|
+
},
|
|
4546
|
+
{
|
|
4547
|
+
type: "null"
|
|
4548
|
+
},
|
|
4549
|
+
{
|
|
4550
|
+
type: "array",
|
|
4551
|
+
items: {
|
|
4552
|
+
$ref: "#/$defs/__schema1"
|
|
4553
|
+
}
|
|
4554
|
+
},
|
|
4555
|
+
{
|
|
4556
|
+
type: "object",
|
|
4557
|
+
propertyNames: {
|
|
4558
|
+
type: "string"
|
|
4559
|
+
},
|
|
4560
|
+
additionalProperties: {
|
|
4561
|
+
$ref: "#/$defs/__schema1"
|
|
4562
|
+
}
|
|
4563
|
+
}
|
|
4564
|
+
]
|
|
4565
|
+
}
|
|
3519
4566
|
}
|
|
3520
4567
|
},
|
|
3521
4568
|
vecdb: {
|
|
@@ -6261,9 +7308,9 @@ function existingFile(path) {
|
|
|
6261
7308
|
throw error;
|
|
6262
7309
|
}
|
|
6263
7310
|
}
|
|
6264
|
-
function writeLocalFile(path, content,
|
|
7311
|
+
function writeLocalFile(path, content, privateFile = false) {
|
|
6265
7312
|
const previous = existingFile(path);
|
|
6266
|
-
const mode =
|
|
7313
|
+
const mode = privateFile ? 384 : previous ? Number(previous.mode & 0o777n) : 420;
|
|
6267
7314
|
const temporary = join(dirname(path), `.${basename(path)}.${randomUUID()}.tmp`);
|
|
6268
7315
|
let fd;
|
|
6269
7316
|
let owned;
|
|
@@ -6470,7 +7517,7 @@ function isSecret(name, value) {
|
|
|
6470
7517
|
}
|
|
6471
7518
|
function generatedVariables(values) {
|
|
6472
7519
|
const generated = {};
|
|
6473
|
-
const
|
|
7520
|
+
const groups2 = /* @__PURE__ */ new Map();
|
|
6474
7521
|
const structures = /* @__PURE__ */ new Set();
|
|
6475
7522
|
for (const [name, value] of Object.entries(values)) {
|
|
6476
7523
|
if (value === void 0)
|
|
@@ -6482,9 +7529,9 @@ function generatedVariables(values) {
|
|
|
6482
7529
|
const module = /^HAI_([A-Z0-9]+)(?:_|$)/.exec(name)?.[1]?.toLowerCase();
|
|
6483
7530
|
if (!module || !shapes[module])
|
|
6484
7531
|
continue;
|
|
6485
|
-
const group =
|
|
7532
|
+
const group = groups2.get(module) ?? {};
|
|
6486
7533
|
group[name] = text;
|
|
6487
|
-
|
|
7534
|
+
groups2.set(module, group);
|
|
6488
7535
|
try {
|
|
6489
7536
|
const parsed = JSON.parse(text);
|
|
6490
7537
|
if (parsed !== null && typeof parsed === "object")
|
|
@@ -6492,7 +7539,7 @@ function generatedVariables(values) {
|
|
|
6492
7539
|
} catch {
|
|
6493
7540
|
}
|
|
6494
7541
|
}
|
|
6495
|
-
for (const [module, group] of
|
|
7542
|
+
for (const [module, group] of groups2) {
|
|
6496
7543
|
const structuredNames = Object.keys(group).filter((name) => structures.has(name));
|
|
6497
7544
|
if (!structuredNames.length)
|
|
6498
7545
|
continue;
|
|
@@ -6539,7 +7586,7 @@ function generatedVariables(values) {
|
|
|
6539
7586
|
}
|
|
6540
7587
|
return generated;
|
|
6541
7588
|
}
|
|
6542
|
-
function updateFile(path, values,
|
|
7589
|
+
function updateFile(path, values, privateFile) {
|
|
6543
7590
|
let content = existsSync(path) ? readLocalFile(path).toString("utf8") : "# Local private deployment configuration; do not commit.\n";
|
|
6544
7591
|
for (const [name, value] of Object.entries(values)) {
|
|
6545
7592
|
if (!/^[A-Z][A-Z0-9_]*$/.test(name))
|
|
@@ -6563,15 +7610,15 @@ ${line}
|
|
|
6563
7610
|
content = withoutParent.slice(0, child.index) + parent[0].replace(/\r?\n?$/, "\n") + withoutParent.slice(child.index);
|
|
6564
7611
|
}
|
|
6565
7612
|
}
|
|
6566
|
-
writeLocalFile(path, content,
|
|
7613
|
+
writeLocalFile(path, content, privateFile);
|
|
6567
7614
|
}
|
|
6568
|
-
function removeOverrides(path, names,
|
|
7615
|
+
function removeOverrides(path, names, privateFile = true) {
|
|
6569
7616
|
if (!existsSync(path) || names.length === 0)
|
|
6570
7617
|
return;
|
|
6571
7618
|
let content = readLocalFile(path).toString("utf8");
|
|
6572
7619
|
for (const name of names)
|
|
6573
7620
|
content = content.replace(new RegExp(`^[\\t ]*(?:export[\\t ]+)?${name}[\\t ]*=[\\t ]*(?:'[^']*'|"(?:[^"\\\\]|\\\\.)*"|\`[^\`]*\`|[^\\r\\n]*)[^\\r\\n]*(?:\\r?\\n|$)`, "gm"), "");
|
|
6574
|
-
writeLocalFile(path, content,
|
|
7621
|
+
writeLocalFile(path, content, privateFile);
|
|
6575
7622
|
}
|
|
6576
7623
|
function saveDeploymentEnvironment(environment, values, options = {}) {
|
|
6577
7624
|
try {
|
|
@@ -7134,47 +8181,6 @@ async function scanApp(inputDir, options) {
|
|
|
7134
8181
|
}
|
|
7135
8182
|
}
|
|
7136
8183
|
|
|
7137
|
-
// src/container/deploy-compose-identity.ts
|
|
7138
|
-
function createComposeContainerLookupScript() {
|
|
7139
|
-
const labelFormat = [
|
|
7140
|
-
"com.docker.compose.project",
|
|
7141
|
-
"com.docker.compose.service",
|
|
7142
|
-
"io.podman.compose.project",
|
|
7143
|
-
"io.podman.compose.service"
|
|
7144
|
-
].map((key) => `{{index .Config.Labels \`${key}\`}}`).join("|");
|
|
7145
|
-
return String.raw`compose_container_ids() (
|
|
7146
|
-
if [ "$#" -ne 4 ]; then printf 'Compose container lookup requires runtime, project, service and selector.\n' >&2; exit 1; fi
|
|
7147
|
-
lookup_runtime=$1
|
|
7148
|
-
lookup_project=$2
|
|
7149
|
-
lookup_service=$3
|
|
7150
|
-
lookup_selector=$4
|
|
7151
|
-
case "$lookup_runtime" in docker|podman) ;; *) printf 'Invalid Compose lookup runtime.\n' >&2; exit 1;; esac
|
|
7152
|
-
case "$lookup_project" in ''|*[!A-Za-z0-9_.-]*) printf 'Invalid Compose project identity.\n' >&2; exit 1;; esac
|
|
7153
|
-
case "$lookup_service" in ''|*[!A-Za-z0-9_.-]*) printf 'Invalid Compose service identity.\n' >&2; exit 1;; esac
|
|
7154
|
-
case "$lookup_selector" in -q|-aq) ;; *) printf 'Invalid Compose container selector.\n' >&2; exit 1;; esac
|
|
7155
|
-
lookup_ids=''
|
|
7156
|
-
for lookup_namespace in com.docker.compose io.podman.compose; do
|
|
7157
|
-
lookup_found=$("$lookup_runtime" ps "$lookup_selector" --filter "label=$lookup_namespace.project=$lookup_project" --filter "label=$lookup_namespace.service=$lookup_service") || { printf 'Cannot query Compose containers.\n' >&2; exit 1; }
|
|
7158
|
-
printf '%s\n' "$lookup_found" | LC_ALL=C awk 'NF {if(NF!=1 || $0!~/^[A-Za-z0-9_][A-Za-z0-9_.-]*$/) exit 1}' || { printf 'Invalid Compose container identifiers.\n' >&2; exit 1; }
|
|
7159
|
-
lookup_ids=$(printf '%s\n%s\n' "$lookup_ids" "$lookup_found" | LC_ALL=C awk 'NF && !seen[$0]++ {print}') || exit 1
|
|
7160
|
-
done
|
|
7161
|
-
for lookup_id in $lookup_ids; do
|
|
7162
|
-
lookup_labels=$("$lookup_runtime" inspect --format '${labelFormat}' "$lookup_id") || { printf 'Cannot inspect Compose container labels.\n' >&2; exit 1; }
|
|
7163
|
-
printf '%s\n' "$lookup_labels" | LC_ALL=C awk -F'|' -v project="$lookup_project" -v service="$lookup_service" '
|
|
7164
|
-
NR!=1 || NF!=4 {exit 1}
|
|
7165
|
-
{
|
|
7166
|
-
docker=($1!="" && $1!="<no value>") || ($2!="" && $2!="<no value>")
|
|
7167
|
-
podman=($3!="" && $3!="<no value>") || ($4!="" && $4!="<no value>")
|
|
7168
|
-
if((!docker && !podman) || (docker && ($1!=project || $2!=service)) || (podman && ($3!=project || $4!=service))) exit 1
|
|
7169
|
-
}
|
|
7170
|
-
END {if(NR!=1) exit 1}
|
|
7171
|
-
' || { printf 'Container labels do not confirm the requested Compose project and service.\n' >&2; exit 1; }
|
|
7172
|
-
done
|
|
7173
|
-
if [ -n "$lookup_ids" ]; then printf '%s\n' "$lookup_ids"; fi
|
|
7174
|
-
)
|
|
7175
|
-
`;
|
|
7176
|
-
}
|
|
7177
|
-
|
|
7178
8184
|
// src/container/deploy-json.ts
|
|
7179
8185
|
function createDeploymentJsonTool() {
|
|
7180
8186
|
return String.raw`function fail(message) {
|
|
@@ -7499,7 +8505,7 @@ deployment_metadata_init() {
|
|
|
7499
8505
|
deployment_metadata_file() {
|
|
7500
8506
|
metadata_directory=$1
|
|
7501
8507
|
metadata_name=$2
|
|
7502
|
-
case "$metadata_name" in layout|health-paths|image-list|dependencies|managed-
|
|
8508
|
+
case "$metadata_name" in layout|health-paths|image-list|dependencies|managed-bindings|managed-volumes|managed-app-keys|unmanaged-app-keys|state-backends|storage-routing|web-base-path|node-runtime|proxy-service|https) ;; *) printf 'Unknown deployment metadata field.\n' >&2; return 1;; esac
|
|
7503
8509
|
test -f "$metadata_directory/manifest.json" && test ! -L "$metadata_directory/manifest.json" || return 1
|
|
7504
8510
|
metadata_identity=$(printf '%s' "$metadata_directory" | sha256sum | awk '{print $1}')
|
|
7505
8511
|
metadata_cached="$metadata_work/$metadata_identity-$metadata_name"
|
|
@@ -7596,8 +8602,6 @@ function createManagedSecretAssets(projectName, nativeEnvironment, compose) {
|
|
|
7596
8602
|
}
|
|
7597
8603
|
}
|
|
7598
8604
|
return {
|
|
7599
|
-
"managed-project": `hai-${projectName}
|
|
7600
|
-
`,
|
|
7601
8605
|
"managed-candidates.env": candidates.length ? `${candidates.join("\n")}
|
|
7602
8606
|
` : "",
|
|
7603
8607
|
"managed-bindings": bindings.size ? `${[...bindings].join("\n")}
|
|
@@ -7607,29 +8611,6 @@ function createManagedSecretAssets(projectName, nativeEnvironment, compose) {
|
|
|
7607
8611
|
"resolve-managed-secrets.sh": createManagedSecretScript()
|
|
7608
8612
|
};
|
|
7609
8613
|
}
|
|
7610
|
-
function createManagedSecretValidationScript() {
|
|
7611
|
-
return String.raw`valid_values() {
|
|
7612
|
-
version=$(awk -F= '$1=="FORMAT_VERSION" {print $2}' "$1")
|
|
7613
|
-
if [ "$version" = 2 ]; then
|
|
7614
|
-
LC_ALL=C awk -F= '
|
|
7615
|
-
NF {
|
|
7616
|
-
if(NF!=2 || seen[$1]++) exit 1
|
|
7617
|
-
if($1=="FORMAT_VERSION") {if($2!="2") exit 1; next}
|
|
7618
|
-
if($1=="PROJECT") {if($2!~/^[A-Za-z0-9][A-Za-z0-9_.-]*$/) exit 1; project=1; next}
|
|
7619
|
-
if($1~/^VOLUME_[0-9]+$/) {if($2!~/^(DB_PASSWORD|STORAGE_PASSWORD|LDAP_PASSWORD):[A-Za-z0-9][A-Za-z0-9_.-]*$/) exit 1; next}
|
|
7620
|
-
if($1!~/^(DB_PASSWORD|STORAGE_PASSWORD|LDAP_PASSWORD)$/ || $2!~/^[0-9a-f]+$/ || length($2)%2 || length($2)>8192) exit 1
|
|
7621
|
-
for(i=1;i<=length($2);i+=2) {b=substr($2,i,2); if(b~/^[01]/ || b=="7f") exit 1}
|
|
7622
|
-
}
|
|
7623
|
-
END {if(!project) exit 1}
|
|
7624
|
-
' "$1"
|
|
7625
|
-
return
|
|
7626
|
-
fi
|
|
7627
|
-
LC_ALL=C awk -F= '
|
|
7628
|
-
NF {if(NF!=2 || $1!~/^(DB_PASSWORD|STORAGE_PASSWORD|LDAP_PASSWORD)$/ || $2!~/^[A-Za-z0-9_-]+$/ || length($2)!=32 || seen[$1]++) exit 1}
|
|
7629
|
-
' "$1"
|
|
7630
|
-
}
|
|
7631
|
-
`;
|
|
7632
|
-
}
|
|
7633
8614
|
function createManagedSecretScript() {
|
|
7634
8615
|
return String.raw`#!/bin/sh
|
|
7635
8616
|
# 由 apply.sh 在项目部署锁内调用;长期凭据只保存在服务器,不依赖打包机。
|
|
@@ -7644,12 +8625,15 @@ test -d "$root" && test ! -L "$root" && test -d "$release/.hai" && test ! -L "$r
|
|
|
7644
8625
|
@@MANIFEST_FUNCTIONS@@
|
|
7645
8626
|
deployment_metadata_init
|
|
7646
8627
|
trap deployment_metadata_cleanup EXIT
|
|
7647
|
-
managed_project=$(deployment_metadata_file "$release" managed-project)
|
|
7648
8628
|
managed_bindings=$(deployment_metadata_file "$release" managed-bindings)
|
|
7649
8629
|
managed_volumes=$(deployment_metadata_file "$release" managed-volumes)
|
|
7650
8630
|
test -s "$candidate_file" || exit 0
|
|
7651
8631
|
fail() { printf '%s\n' "$*" >&2; exit 1; }
|
|
7652
|
-
|
|
8632
|
+
valid_values() {
|
|
8633
|
+
LC_ALL=C awk -F= '
|
|
8634
|
+
NF {if(NF!=2 || $1!~/^(DB_PASSWORD|STORAGE_PASSWORD|LDAP_PASSWORD)$/ || $2!~/^[A-Za-z0-9_-]+$/ || length($2)!=32 || seen[$1]++) exit 1}
|
|
8635
|
+
' "$1"
|
|
8636
|
+
}
|
|
7653
8637
|
value_of() { awk -F= -v key="$2" '$1==key {print $2}' "$1"; }
|
|
7654
8638
|
test ! -L "$state" || fail "Invalid managed middleware credential state: $state"
|
|
7655
8639
|
valid_values "$candidate_file" || fail 'Invalid managed middleware credential candidates; rebuild the deployment bundle.'
|
|
@@ -7660,22 +8644,6 @@ if [ -f "$state" ]; then
|
|
|
7660
8644
|
else
|
|
7661
8645
|
: > "$next"
|
|
7662
8646
|
fi
|
|
7663
|
-
format=$(value_of "$next" FORMAT_VERSION)
|
|
7664
|
-
if [ "$format" = 2 ]; then
|
|
7665
|
-
test "$(value_of "$next" PROJECT)" = "$(cat "$managed_project")" || fail 'Managed middleware credential project mismatch.'
|
|
7666
|
-
known=$("$runtime" volume ls -q) || fail 'Cannot check persistent volumes; managed middleware credentials were not changed.'
|
|
7667
|
-
while IFS=' ' read -r volume_group volume; do
|
|
7668
|
-
test -n "$volume" || continue
|
|
7669
|
-
binding="$volume_group:$volume"
|
|
7670
|
-
if ! awk -F= -v binding="$binding" '$1~/^VOLUME_[0-9]+$/ && $2==binding {found=1} END {exit !found}' "$next"; then
|
|
7671
|
-
if printf '%s\n' "$known" | grep -Fxq "$volume"; then
|
|
7672
|
-
fail 'Existing persistent volume is not bound to the imported credential state. Import its verified identity first.'
|
|
7673
|
-
fi
|
|
7674
|
-
index=$(awk -F= '$1~/^VOLUME_[0-9]+$/ {n=substr($1,8)+0; if(n>max)max=n} END {print max+1}' "$next")
|
|
7675
|
-
printf 'VOLUME_%s=%s\n' "$index" "$binding" >> "$next"
|
|
7676
|
-
fi
|
|
7677
|
-
done < "$managed_volumes"
|
|
7678
|
-
fi
|
|
7679
8647
|
volumes_checked=false
|
|
7680
8648
|
known_volumes=''
|
|
7681
8649
|
while IFS='=' read -r name candidate; do
|
|
@@ -7692,64 +8660,24 @@ while IFS='=' read -r name candidate; do
|
|
|
7692
8660
|
fail "Missing $name in $state for existing persistent volume $volume. Restore the server credential state before retrying."
|
|
7693
8661
|
fi
|
|
7694
8662
|
done < "$managed_volumes"
|
|
7695
|
-
|
|
7696
|
-
if [ "$format" = 2 ]; then value=$(printf '%s' "$candidate" | od -An -tx1 | tr -d ' \n'); fi
|
|
7697
|
-
printf '%s=%s\n' "$name" "$value" >> "$next"
|
|
8663
|
+
printf '%s=%s\n' "$name" "$candidate" >> "$next"
|
|
7698
8664
|
fi
|
|
7699
8665
|
done < "$candidate_file"
|
|
7700
8666
|
while IFS='=' read -r name candidate; do
|
|
7701
8667
|
test -n "$name" || continue
|
|
7702
8668
|
value=$(value_of "$next" "$name")
|
|
7703
8669
|
for file in config/.env.runtime .hai/middleware-runtime.env; do
|
|
7704
|
-
|
|
7705
|
-
if [ "$file" = .hai/middleware-runtime.env ]; then compose_environment=true; fi
|
|
7706
|
-
HAI_DEPLOY_SECRET_CANDIDATE="$candidate" HAI_DEPLOY_SECRET_VALUE="$value" LC_ALL=C awk -v group="$name" -v format="$format" -v compose_environment="$compose_environment" '
|
|
8670
|
+
HAI_DEPLOY_SECRET_CANDIDATE="$candidate" HAI_DEPLOY_SECRET_VALUE="$value" LC_ALL=C awk -v group="$name" '
|
|
7707
8671
|
FILENAME==ARGV[1] {if($1==group) {keys[$2]=$3}; next}
|
|
7708
8672
|
function replace_literal(line, needle, value, result, at) {
|
|
7709
8673
|
result=""
|
|
7710
8674
|
while((at=index(line,needle))>0) {result=result substr(line,1,at-1) value; line=substr(line,at+length(needle))}
|
|
7711
8675
|
return result line
|
|
7712
8676
|
}
|
|
7713
|
-
function decode_hex(hex, result, i, high, low) {
|
|
7714
|
-
for(i=1;i<=length(hex);i+=2) {high=index("0123456789abcdef",substr(hex,i,1))-1; low=index("0123456789abcdef",substr(hex,i+1,1))-1; result=result sprintf("%c",high*16+low)}
|
|
7715
|
-
return result
|
|
7716
|
-
}
|
|
7717
|
-
function json_escape(value, apostrophe, result, i, ch) {
|
|
7718
|
-
for(i=1;i<=length(value);i++) {
|
|
7719
|
-
ch=substr(value,i,1)
|
|
7720
|
-
if(ch=="\\") result=result "\\\\"
|
|
7721
|
-
else if(ch=="\"") result=result "\\\""
|
|
7722
|
-
else if(ch==sprintf("%c",39) && apostrophe) result=result "\\u0027"
|
|
7723
|
-
else result=result ch
|
|
7724
|
-
}
|
|
7725
|
-
return result
|
|
7726
|
-
}
|
|
7727
|
-
function uri_escape(value, result, i, ch, j, ordinal) {
|
|
7728
|
-
for(i=1;i<=length(value);i++) {
|
|
7729
|
-
ch=substr(value,i,1)
|
|
7730
|
-
if(ch~/[A-Za-z0-9_.~-]/) result=result ch
|
|
7731
|
-
else {ordinal=0; for(j=1;j<=255;j++) if(sprintf("%c",j)==ch) {ordinal=j; break}; result=result sprintf("%%%02X",ordinal)}
|
|
7732
|
-
}
|
|
7733
|
-
return result
|
|
7734
|
-
}
|
|
7735
8677
|
{
|
|
7736
8678
|
equal=index($0,"="); key=substr($0,1,equal-1); scoped=0
|
|
7737
8679
|
for(prefix in keys) if(key==prefix || (keys[prefix]=="prefix" && index(key,prefix "_")==1)) scoped=1
|
|
7738
|
-
if(scoped
|
|
7739
|
-
candidate=ENVIRON["HAI_DEPLOY_SECRET_CANDIDATE"]; value=decode_hex(ENVIRON["HAI_DEPLOY_SECRET_VALUE"])
|
|
7740
|
-
literal=substr($0,equal+1); quote=sprintf("%c",39)
|
|
7741
|
-
json=substr(literal,1,1)==quote && (substr(literal,2,1)=="{" || substr(literal,2,1)=="[")
|
|
7742
|
-
if(json) literal=substr(literal,2,length(literal)-2)
|
|
7743
|
-
literal=replace_literal(literal,":" candidate "@",":" uri_escape(value) "@")
|
|
7744
|
-
if(json) {literal=replace_literal(literal,candidate,json_escape(value,1)); print key "=" quote literal quote}
|
|
7745
|
-
else {
|
|
7746
|
-
if(substr(literal,1,1)==quote || substr(literal,1,1)=="\"") literal=substr(literal,2,length(literal)-2)
|
|
7747
|
-
literal=replace_literal(literal,candidate,value)
|
|
7748
|
-
literal=json_escape(literal,0); if(compose_environment=="true") literal=replace_literal(literal,"$","$")
|
|
7749
|
-
print key "=\"" literal "\""
|
|
7750
|
-
}
|
|
7751
|
-
}
|
|
7752
|
-
else if(scoped) print replace_literal($0,ENVIRON["HAI_DEPLOY_SECRET_CANDIDATE"],ENVIRON["HAI_DEPLOY_SECRET_VALUE"])
|
|
8680
|
+
if(scoped) print replace_literal($0,ENVIRON["HAI_DEPLOY_SECRET_CANDIDATE"],ENVIRON["HAI_DEPLOY_SECRET_VALUE"])
|
|
7753
8681
|
else print
|
|
7754
8682
|
}
|
|
7755
8683
|
' "$managed_bindings" "$release/$file" > "$release/$file.next"
|
|
@@ -7759,7 +8687,7 @@ done < "$candidate_file"
|
|
|
7759
8687
|
chmod 600 "$next"
|
|
7760
8688
|
mv "$next" "$state"
|
|
7761
8689
|
printf '[INFO] Managed middleware credential source: %s (server state; values hidden).\n' "$state"
|
|
7762
|
-
`.replace("@@
|
|
8690
|
+
`.replace("@@MANIFEST_FUNCTIONS@@", () => createDeploymentManifestFunctions());
|
|
7763
8691
|
}
|
|
7764
8692
|
|
|
7765
8693
|
// src/container/deploy-backend-state.ts
|
|
@@ -7822,11 +8750,11 @@ backend_manifest=$(deployment_metadata_file "$release" state-backends)
|
|
|
7822
8750
|
regular() { test -f "$1" && test ! -L "$1" || fail "Backend state must be a regular file: $1"; }
|
|
7823
8751
|
absent_or_regular() { if [ -e "$1" ] || [ -L "$1" ]; then regular "$1"; fi; }
|
|
7824
8752
|
valid_tuples() {
|
|
7825
|
-
LC_ALL=C awk
|
|
8753
|
+
LC_ALL=C awk '
|
|
7826
8754
|
NF {
|
|
7827
|
-
if(
|
|
8755
|
+
if(NF!=3 || roles[$1]++) exit 1
|
|
7828
8756
|
if(!(($1=="db" && $2~/^(postgresql|mysql)$/) || ($1=="storage" && $2~/^(rustfs|minio)$/) || ($1=="vecdb" && $2~/^(qdrant|pgvector|chroma)$/))) exit 1
|
|
7829
|
-
if(
|
|
8757
|
+
if($3!~/^[A-Za-z0-9_][A-Za-z0-9_.-]*$/ || volumes[$3]++) exit 1
|
|
7830
8758
|
}
|
|
7831
8759
|
' "$1"
|
|
7832
8760
|
}
|
|
@@ -7877,45 +8805,6 @@ known=$("$runtime" volume ls -q) || fail 'Cannot list persistent volumes; no bac
|
|
|
7877
8805
|
printf '%s\n' "$known" | LC_ALL=C awk 'NF {if(NF!=1 || $1!~/^[A-Za-z0-9_][A-Za-z0-9_.-]*$/ || seen[$1]++) exit 1}' || fail 'Invalid persistent volume listing.'
|
|
7878
8806
|
exists_volume() { printf '%s\n' "$known" | grep -Fxq "$1"; }
|
|
7879
8807
|
ledger_tuple() { printf '%s\n' "$ledger" | awk -v volume="$1" '$3==volume {print}'; }
|
|
7880
|
-
@@MANAGED_VALUES_VALIDATOR@@
|
|
7881
|
-
@@COMPOSE_CONTAINER_LOOKUP@@
|
|
7882
|
-
# 状态补证:卷挂载和镜像均取自同项目、同角色的唯一实际容器。
|
|
7883
|
-
container_binding() {
|
|
7884
|
-
binding_service=$1
|
|
7885
|
-
binding_family=$2
|
|
7886
|
-
binding_expected=$3
|
|
7887
|
-
binding_running=$4
|
|
7888
|
-
binding_flags=-aq
|
|
7889
|
-
if [ "$binding_running" = true ]; then binding_flags=-q; fi
|
|
7890
|
-
binding_containers=$(compose_container_ids "$runtime" "$project" "$binding_service" "$binding_flags") || fail 'Cannot inspect successful deployment containers.'
|
|
7891
|
-
printf '%s\n' "$binding_containers" | LC_ALL=C awk 'NR!=1 || NF!=1 || $1!~/^[A-Za-z0-9_][A-Za-z0-9_.-]*$/ {exit 1} END {if(NR!=1) exit 1}' || fail "Cannot prove the previous volume binding for $binding_service. Restore verified backend state."
|
|
7892
|
-
if [ "$binding_running" = true ]; then
|
|
7893
|
-
binding_status=$("$runtime" inspect --format '{{.State.Running}}' "$binding_containers") || fail 'Cannot verify the imported service is running.'
|
|
7894
|
-
test "$binding_status" = true || fail "Imported backend requires its verified original container to be running: $binding_service."
|
|
7895
|
-
fi
|
|
7896
|
-
binding_mounted=$("$runtime" inspect --format '{{range .Mounts}}{{if eq .Type "volume"}}{{println .Name}}{{end}}{{end}}' "$binding_containers") || fail 'Cannot inspect previous named volume mounts.'
|
|
7897
|
-
printf '%s\n' "$binding_mounted" | LC_ALL=C awk 'NR!=1 || NF!=1 || $1!~/^[A-Za-z0-9_][A-Za-z0-9_.-]*$/ {exit 1} END {if(NR!=1) exit 1}' || fail "Ambiguous previous volume binding for $binding_service."
|
|
7898
|
-
binding_image=$("$runtime" inspect --format '{{.Config.Image}}' "$binding_containers") || fail 'Cannot identify the previous container image.'
|
|
7899
|
-
binding_actual_family=$(printf '%s\n' "$binding_image" | LC_ALL=C awk 'NR!=1 || NF!=1 {exit 1} {image=$0; sub(/^.*\//,"",image); sub(/[:@].*$/,"",image); if(image=="postgres") image="postgresql"; print image}') || fail 'Invalid previous container image identity.'
|
|
7900
|
-
test "$binding_actual_family" = "$binding_family" || fail "Actual previous container image does not confirm its backend family: $binding_service."
|
|
7901
|
-
if [ -n "$binding_expected" ] && [ "$binding_expected" != "$binding_mounted" ]; then fail "Actual previous volume differs from its backend manifest: $binding_service."; fi
|
|
7902
|
-
printf '%s\n' "$binding_mounted"
|
|
7903
|
-
}
|
|
7904
|
-
confirmed_import() {
|
|
7905
|
-
import_service=$1
|
|
7906
|
-
import_family=$2
|
|
7907
|
-
import_volume=$3
|
|
7908
|
-
import_state="$root/managed-secrets.env"
|
|
7909
|
-
test -f "$import_state" && test ! -L "$import_state" || return 1
|
|
7910
|
-
valid_values "$import_state" || return 1
|
|
7911
|
-
test "$(awk -F= '$1=="FORMAT_VERSION" {print $2}' "$import_state")" = 2 || return 1
|
|
7912
|
-
test "$(awk -F= '$1=="PROJECT" {print $2}' "$import_state")" = "$project" || return 1
|
|
7913
|
-
case "$import_service" in db|vecdb) import_group=DB_PASSWORD;; storage) import_group=STORAGE_PASSWORD;; *) return 1;; esac
|
|
7914
|
-
awk -F= -v group="$import_group" -v binding="$import_group:$import_volume" '$1==group && $2!="" {credential=1} $1~/^VOLUME_[0-9]+$/ && $2==binding {volume=1} END {exit !(credential && volume)}' "$import_state" || return 1
|
|
7915
|
-
# 密码不证明目录格式;受控导入还须与唯一运行原容器的实际卷及镜像家族共同吻合。
|
|
7916
|
-
import_actual=$(container_binding "$import_service" "$import_family" "$import_volume" true) || return 1
|
|
7917
|
-
test "$import_actual" = "$import_volume"
|
|
7918
|
-
}
|
|
7919
8808
|
snapshot_previous=$previous
|
|
7920
8809
|
if [ -z "$snapshot_previous" ]; then snapshot_previous=-; fi
|
|
7921
8810
|
if [ "$mode" = recover ]; then
|
|
@@ -7933,7 +8822,7 @@ RECOVERY_TUPLES
|
|
|
7933
8822
|
exit 0
|
|
7934
8823
|
fi
|
|
7935
8824
|
regular "$backend_manifest"
|
|
7936
|
-
valid_tuples "$backend_manifest"
|
|
8825
|
+
valid_tuples "$backend_manifest" || fail 'Invalid backend manifest; rebuild the deployment bundle.'
|
|
7937
8826
|
active=''
|
|
7938
8827
|
if [ -n "$previous" ]; then
|
|
7939
8828
|
baseline="$root/releases/$previous"
|
|
@@ -7942,7 +8831,7 @@ if [ -n "$previous" ]; then
|
|
|
7942
8831
|
baseline_services=$(deployment_services_file "$baseline")
|
|
7943
8832
|
previous_manifest=$(deployment_metadata_file "$baseline" state-backends)
|
|
7944
8833
|
regular "$previous_manifest"
|
|
7945
|
-
valid_tuples "$previous_manifest"
|
|
8834
|
+
valid_tuples "$previous_manifest" || fail 'Invalid successful backend manifest.'
|
|
7946
8835
|
LC_ALL=C awk 'NF {if(NF!=1 || $1!~/^[A-Za-z0-9_][A-Za-z0-9_.-]*$/ || seen[$1]++) exit 1}' "$baseline_services" || fail 'Invalid successful deployment services.'
|
|
7947
8836
|
for active_service in db storage vecdb; do
|
|
7948
8837
|
if ! grep -Fxq "$active_service" "$baseline_services"; then continue; fi
|
|
@@ -7955,28 +8844,18 @@ if [ -n "$previous" ]; then
|
|
|
7955
8844
|
printf '%s %s %s\n' "$active_service" "$active_family" "$active_volume" | LC_ALL=C awk 'NF!=3 || !(($1=="db" && $2~/^(postgresql|mysql)$/) || ($1=="storage" && $2~/^(rustfs|minio)$/) || ($1=="vecdb" && $2~/^(qdrant|pgvector|chroma)$/)) {exit 1}' || fail "Unknown installed backend for $active_service."
|
|
7956
8845
|
exists_volume "$active_volume" || fail "Active persistent volume is missing: $active_volume. Restore its backup instead of creating an empty replacement."
|
|
7957
8846
|
confirmed=$(ledger_tuple "$active_volume")
|
|
7958
|
-
test
|
|
7959
|
-
if [ -z "$confirmed" ]; then
|
|
7960
|
-
proven_volume=$(container_binding "$active_service" "$active_family" "$active_volume" false)
|
|
7961
|
-
test "$proven_volume" = "$active_volume" || fail "Cannot confirm the active persistent volume: $active_service."
|
|
7962
|
-
fi
|
|
8847
|
+
test "$confirmed" = "$active_service $active_family $active_volume" || fail "Active persistent backend is missing from or conflicts with the protected ledger: $active_service. Restore verified backend state."
|
|
7963
8848
|
active=$(printf '%s\n%s %s %s\n' "$active" "$active_service" "$active_family" "$active_volume" | sed '/^$/d')
|
|
7964
8849
|
done
|
|
7965
8850
|
fi
|
|
7966
|
-
#
|
|
7967
|
-
printf '%s\n' "$active" | valid_tuples /dev/stdin
|
|
8851
|
+
# 同一实际卷不能绑定给多个有状态服务。
|
|
8852
|
+
printf '%s\n' "$active" | valid_tuples /dev/stdin || fail 'Conflicting successful persistent volume bindings.'
|
|
7968
8853
|
# 合并时只替换同一卷的无数据预认领,不能按本次拓扑裁剪历史绑定。
|
|
7969
8854
|
merged=$ledger
|
|
7970
8855
|
merge_tuple() {
|
|
7971
8856
|
merged=$(printf '%s\n' "$merged" | awk -v volume="$3" 'NF && $3!=volume {print}')
|
|
7972
8857
|
merged=$(printf '%s\n%s %s %s\n' "$merged" "$1" "$2" "$3" | sed '/^$/d')
|
|
7973
8858
|
}
|
|
7974
|
-
while IFS=' ' read -r active_service active_family active_volume; do
|
|
7975
|
-
test -n "$active_service" || continue
|
|
7976
|
-
merge_tuple "$active_service" "$active_family" "$active_volume"
|
|
7977
|
-
done <<ACTIVE_TUPLES
|
|
7978
|
-
$active
|
|
7979
|
-
ACTIVE_TUPLES
|
|
7980
8859
|
while IFS=' ' read -r candidate_service candidate_family candidate_volume; do
|
|
7981
8860
|
test -n "$candidate_service" || continue
|
|
7982
8861
|
prior_active=$(printf '%s\n' "$active" | awk -v service="$candidate_service" '$1==service {print}')
|
|
@@ -7984,8 +8863,6 @@ while IFS=' ' read -r candidate_service candidate_family candidate_volume; do
|
|
|
7984
8863
|
test -z "$prior_active" || test "$prior_active" = "$tuple" || fail "Persistent backend $candidate_service changed or its active volume binding changed. Use a separate deployment identity and migrate or restore data explicitly."
|
|
7985
8864
|
if exists_volume "$candidate_volume"; then
|
|
7986
8865
|
confirmed=$(ledger_tuple "$candidate_volume")
|
|
7987
|
-
if [ -z "$confirmed" ]; then confirmed=$(printf '%s\n' "$active" | awk -v volume="$candidate_volume" '$3==volume {print}'); fi
|
|
7988
|
-
if [ -z "$confirmed" ] && confirmed_import "$candidate_service" "$candidate_family" "$candidate_volume"; then confirmed=$tuple; fi
|
|
7989
8866
|
test "$confirmed" = "$tuple" || fail "Existing persistent volume has an unknown or incompatible backend: $candidate_service. Restore verified backend state; in-place reuse is refused."
|
|
7990
8867
|
else
|
|
7991
8868
|
users=$("$runtime" ps -aq --filter "volume=$candidate_volume") || fail 'Cannot check persistent volume users.'
|
|
@@ -8005,7 +8882,7 @@ mv -f -- "$snapshot.next" "$snapshot"
|
|
|
8005
8882
|
(set -C; printf '%s\n' "$state_content" > "$state.next") || fail 'Cannot stage the persistent backend ledger.'
|
|
8006
8883
|
chmod 600 "$state.next"
|
|
8007
8884
|
mv -f -- "$state.next" "$state"
|
|
8008
|
-
`.replace("@@PROJECT@@", () => projectName).replace("@@
|
|
8885
|
+
`.replace("@@PROJECT@@", () => projectName).replace("@@MANIFEST_FUNCTIONS@@", () => createDeploymentManifestFunctions());
|
|
8009
8886
|
}
|
|
8010
8887
|
var nativeJob = String.raw`
|
|
8011
8888
|
using System;
|
|
@@ -8204,7 +9081,7 @@ async function groupExited(pid) {
|
|
|
8204
9081
|
async function helper(command, args, timeoutMs) {
|
|
8205
9082
|
if (timeoutMs <= 0)
|
|
8206
9083
|
return false;
|
|
8207
|
-
return new Promise((
|
|
9084
|
+
return new Promise((resolve8) => {
|
|
8208
9085
|
const child = spawn(command, args, { windowsHide: true, shell: false, stdio: "ignore" });
|
|
8209
9086
|
let settled = false;
|
|
8210
9087
|
let timer;
|
|
@@ -8214,7 +9091,7 @@ async function helper(command, args, timeoutMs) {
|
|
|
8214
9091
|
settled = true;
|
|
8215
9092
|
if (timer)
|
|
8216
9093
|
clearTimeout(timer);
|
|
8217
|
-
|
|
9094
|
+
resolve8(success);
|
|
8218
9095
|
};
|
|
8219
9096
|
timer = globalThis.setTimeout(() => {
|
|
8220
9097
|
child.kill("SIGKILL");
|
|
@@ -8367,7 +9244,7 @@ ${deployM(state.localTermination === "confirmed" ? "deploy_localStopped" : state
|
|
|
8367
9244
|
ext: { commandExecution: state }
|
|
8368
9245
|
});
|
|
8369
9246
|
try {
|
|
8370
|
-
return await new Promise((
|
|
9247
|
+
return await new Promise((resolve8) => {
|
|
8371
9248
|
if (typeof command !== "string" || !command || command.includes("\0") || args.some((argument) => typeof argument !== "string" || argument.includes("\0")))
|
|
8372
9249
|
throw new TypeError("Invalid command arguments");
|
|
8373
9250
|
if (options.input !== void 0 && typeof options.input !== "string")
|
|
@@ -8410,7 +9287,7 @@ ${deployM(state.localTermination === "confirmed" ? "deploy_localStopped" : state
|
|
|
8410
9287
|
settled = true;
|
|
8411
9288
|
if (timer)
|
|
8412
9289
|
clearTimeout(timer);
|
|
8413
|
-
|
|
9290
|
+
resolve8(result);
|
|
8414
9291
|
};
|
|
8415
9292
|
const interrupt = (reason, message) => {
|
|
8416
9293
|
interrupted = true;
|
|
@@ -8597,6 +9474,47 @@ watch_certificate &
|
|
|
8597
9474
|
`;
|
|
8598
9475
|
}
|
|
8599
9476
|
|
|
9477
|
+
// src/container/deploy-compose-identity.ts
|
|
9478
|
+
function createComposeContainerLookupScript() {
|
|
9479
|
+
const labelFormat = [
|
|
9480
|
+
"com.docker.compose.project",
|
|
9481
|
+
"com.docker.compose.service",
|
|
9482
|
+
"io.podman.compose.project",
|
|
9483
|
+
"io.podman.compose.service"
|
|
9484
|
+
].map((key) => `{{index .Config.Labels \`${key}\`}}`).join("|");
|
|
9485
|
+
return String.raw`compose_container_ids() (
|
|
9486
|
+
if [ "$#" -ne 4 ]; then printf 'Compose container lookup requires runtime, project, service and selector.\n' >&2; exit 1; fi
|
|
9487
|
+
lookup_runtime=$1
|
|
9488
|
+
lookup_project=$2
|
|
9489
|
+
lookup_service=$3
|
|
9490
|
+
lookup_selector=$4
|
|
9491
|
+
case "$lookup_runtime" in docker|podman) ;; *) printf 'Invalid Compose lookup runtime.\n' >&2; exit 1;; esac
|
|
9492
|
+
case "$lookup_project" in ''|*[!A-Za-z0-9_.-]*) printf 'Invalid Compose project identity.\n' >&2; exit 1;; esac
|
|
9493
|
+
case "$lookup_service" in ''|*[!A-Za-z0-9_.-]*) printf 'Invalid Compose service identity.\n' >&2; exit 1;; esac
|
|
9494
|
+
case "$lookup_selector" in -q|-aq) ;; *) printf 'Invalid Compose container selector.\n' >&2; exit 1;; esac
|
|
9495
|
+
lookup_ids=''
|
|
9496
|
+
for lookup_namespace in com.docker.compose io.podman.compose; do
|
|
9497
|
+
lookup_found=$("$lookup_runtime" ps "$lookup_selector" --filter "label=$lookup_namespace.project=$lookup_project" --filter "label=$lookup_namespace.service=$lookup_service") || { printf 'Cannot query Compose containers.\n' >&2; exit 1; }
|
|
9498
|
+
printf '%s\n' "$lookup_found" | LC_ALL=C awk 'NF {if(NF!=1 || $0!~/^[A-Za-z0-9_][A-Za-z0-9_.-]*$/) exit 1}' || { printf 'Invalid Compose container identifiers.\n' >&2; exit 1; }
|
|
9499
|
+
lookup_ids=$(printf '%s\n%s\n' "$lookup_ids" "$lookup_found" | LC_ALL=C awk 'NF && !seen[$0]++ {print}') || exit 1
|
|
9500
|
+
done
|
|
9501
|
+
for lookup_id in $lookup_ids; do
|
|
9502
|
+
lookup_labels=$("$lookup_runtime" inspect --format '${labelFormat}' "$lookup_id") || { printf 'Cannot inspect Compose container labels.\n' >&2; exit 1; }
|
|
9503
|
+
printf '%s\n' "$lookup_labels" | LC_ALL=C awk -F'|' -v project="$lookup_project" -v service="$lookup_service" '
|
|
9504
|
+
NR!=1 || NF!=4 {exit 1}
|
|
9505
|
+
{
|
|
9506
|
+
docker=($1!="" && $1!="<no value>") || ($2!="" && $2!="<no value>")
|
|
9507
|
+
podman=($3!="" && $3!="<no value>") || ($4!="" && $4!="<no value>")
|
|
9508
|
+
if((!docker && !podman) || (docker && ($1!=project || $2!=service)) || (podman && ($3!=project || $4!=service))) exit 1
|
|
9509
|
+
}
|
|
9510
|
+
END {if(NR!=1) exit 1}
|
|
9511
|
+
' || { printf 'Container labels do not confirm the requested Compose project and service.\n' >&2; exit 1; }
|
|
9512
|
+
done
|
|
9513
|
+
if [ -n "$lookup_ids" ]; then printf '%s\n' "$lookup_ids"; fi
|
|
9514
|
+
)
|
|
9515
|
+
`;
|
|
9516
|
+
}
|
|
9517
|
+
|
|
8600
9518
|
// src/container/deploy-runtime-script.ts
|
|
8601
9519
|
function createRuntimeConfigurationScript(primaryService) {
|
|
8602
9520
|
return String.raw`configuration_mount() {
|
|
@@ -9256,7 +10174,7 @@ if [ "$partial" = true ]; then
|
|
|
9256
10174
|
fi
|
|
9257
10175
|
;;
|
|
9258
10176
|
esac
|
|
9259
|
-
#
|
|
10177
|
+
# Web 挂载目录由完整拓扑确定,服务增量必须匹配已部署的目录。
|
|
9260
10178
|
current_web_base=$(deployment_json read metadata.web-base-path string "$source_dir/manifest.json")
|
|
9261
10179
|
previous_web_base=$(deployment_json read metadata.web-base-path string "$root/releases/$previous/manifest.json")
|
|
9262
10180
|
test "$current_web_base" = "$previous_web_base" || { printf 'Web base path changed. Match the installed prefix for partial updates, or create a full deployment.\n' >&2; exit 1; }
|
|
@@ -10152,7 +11070,7 @@ async function createDeploymentBundle(options) {
|
|
|
10152
11070
|
const metadata = {
|
|
10153
11071
|
"layout": `${config.expose.hostPort}:${options.containerPort}:${options.hasWebConfig ? options.proxyService === "gateway" ? "gateway" : "true" : "false"}:${artifact.primaryService}:${options.hasWebConfig ? await artifactChecksum(join(directory, "web.conf")) : ""}${httpsFingerprint ? `:${httpsFingerprint}` : ""}
|
|
10154
11072
|
`,
|
|
10155
|
-
"web-base-path": `${artifact.webBasePath
|
|
11073
|
+
"web-base-path": `${artifact.webBasePath}
|
|
10156
11074
|
`,
|
|
10157
11075
|
"health-paths": `${options.healthPaths.join("\n")}
|
|
10158
11076
|
`,
|
|
@@ -10165,7 +11083,6 @@ async function createDeploymentBundle(options) {
|
|
|
10165
11083
|
"node-runtime": String(nodeRuntime),
|
|
10166
11084
|
"proxy-service": options.hasWebConfig ? options.proxyService ?? "web" : "",
|
|
10167
11085
|
"https": options.httpsAssets?.https ?? "",
|
|
10168
|
-
"managed-project": managed["managed-project"],
|
|
10169
11086
|
// 未选应用时只带容器原生绑定,保持当前发布的应用连接归属。
|
|
10170
11087
|
"managed-bindings": updatesApp ? managed["managed-bindings"] : managed["managed-bindings"].replace(/^.* prefix\n/gm, ""),
|
|
10171
11088
|
"managed-volumes": managed["managed-volumes"],
|
|
@@ -11470,7 +12387,7 @@ function makeDeploymentId() {
|
|
|
11470
12387
|
return `${core.time.formatDate(/* @__PURE__ */ new Date(), "YYYYMMDD-HHmmss")}-${randomBytes(3).toString("hex")}`;
|
|
11471
12388
|
}
|
|
11472
12389
|
function sleep(ms) {
|
|
11473
|
-
return new Promise((
|
|
12390
|
+
return new Promise((resolve8) => setTimeout(resolve8, ms));
|
|
11474
12391
|
}
|
|
11475
12392
|
function createDockerSshProvider(config, container) {
|
|
11476
12393
|
const ssh = config.ssh ? createSshExecutor(config.ssh) : void 0;
|
|
@@ -11502,16 +12419,16 @@ function createDockerSshProvider(config, container) {
|
|
|
11502
12419
|
for (let attempt = 0; attempt < HEALTH_MAX_ATTEMPTS; attempt++) {
|
|
11503
12420
|
try {
|
|
11504
12421
|
if (certificate) {
|
|
11505
|
-
const healthy = await new Promise((
|
|
12422
|
+
const healthy = await new Promise((resolve8) => {
|
|
11506
12423
|
const request$1 = request(url, {
|
|
11507
12424
|
ca: [...rootCertificates, certificate],
|
|
11508
12425
|
timeout: 5e3
|
|
11509
12426
|
}, (response) => {
|
|
11510
12427
|
response.resume();
|
|
11511
|
-
|
|
12428
|
+
resolve8(response.statusCode !== void 0 && response.statusCode >= 200 && response.statusCode < 300);
|
|
11512
12429
|
});
|
|
11513
|
-
request$1.on("error", () =>
|
|
11514
|
-
request$1.on("close", () =>
|
|
12430
|
+
request$1.on("error", () => resolve8(false));
|
|
12431
|
+
request$1.on("close", () => resolve8(false));
|
|
11515
12432
|
request$1.on("timeout", () => request$1.destroy());
|
|
11516
12433
|
request$1.end();
|
|
11517
12434
|
});
|
|
@@ -11562,7 +12479,7 @@ function createDockerSshProvider(config, container) {
|
|
|
11562
12479
|
const webContainerPort = web?.containerPort ?? artifact.webContainerPort ?? 8080;
|
|
11563
12480
|
const host = config.ssh?.host;
|
|
11564
12481
|
const url = https ? `https://${https.domain}${https.port === 443 ? "" : `:${https.port}`}` : host ? `http://${host.includes(":") ? `[${host}]` : host}:${hostPort}` : `http://SERVER_HOST:${hostPort}`;
|
|
11565
|
-
const webBasePath = artifact.webBasePath
|
|
12482
|
+
const webBasePath = artifact.webBasePath;
|
|
11566
12483
|
const applicationUrl = `${url}${webBasePath ? `${webBasePath}/` : ""}`;
|
|
11567
12484
|
const standaloneWebConfig = primaryService === "web" && !scan.isSvelteKit && (!!webBasePath || !!https);
|
|
11568
12485
|
const healthPaths = hasWebProxy ? ["/_hai/web-health", `${webBasePath}/`] : scan.isSvelteKit ? [`${webBasePath}/`] : primaryService === "serv" ? [] : standaloneWebConfig ? ["/health", `${webBasePath}/`] : ["/health"];
|
|
@@ -12340,7 +13257,7 @@ async function pollDeploymentStatus(token, deploymentId) {
|
|
|
12340
13257
|
if (deployment.readyState === "ERROR" || deployment.readyState === "CANCELED") {
|
|
12341
13258
|
return "error";
|
|
12342
13259
|
}
|
|
12343
|
-
await new Promise((
|
|
13260
|
+
await new Promise((resolve8) => setTimeout(resolve8, POLL_INTERVAL));
|
|
12344
13261
|
}
|
|
12345
13262
|
return "building";
|
|
12346
13263
|
}
|
|
@@ -13161,8 +14078,6 @@ var deploy = {
|
|
|
13161
14078
|
if (!currentProvider && !currentContainerProvider) {
|
|
13162
14079
|
return notInitialized.result();
|
|
13163
14080
|
}
|
|
13164
|
-
if (options && "projectName" in options && options.projectName !== void 0)
|
|
13165
|
-
return err(HaiDeployError.CONFIG_ERROR, deployM("deploy_projectNameOverrideRemoved"));
|
|
13166
14081
|
logger12.info("Starting deployment", { appDir, environment: options?.environment });
|
|
13167
14082
|
const scanOptions = { ...options, environment: options?.environment ?? currentConfig?.environment };
|
|
13168
14083
|
const scanResult = await scanApp(appDir, scanOptions);
|
|
@@ -13896,8 +14811,6 @@ function describeMiddlewareBackend(backend) {
|
|
|
13896
14811
|
return deployM(backend.purpose);
|
|
13897
14812
|
}
|
|
13898
14813
|
async function createDeploymentPlan(appDir, input, options = {}) {
|
|
13899
|
-
if ("projectName" in options && options.projectName !== void 0)
|
|
13900
|
-
return err(HaiDeployError.CONFIG_ERROR, deployM("deploy_projectNameOverrideRemoved"));
|
|
13901
14814
|
const parsed = DeployConfigSchema.safeParse(input);
|
|
13902
14815
|
if (!parsed.success) {
|
|
13903
14816
|
return err({
|
|
@@ -14098,389 +15011,7 @@ async function createDeploymentPlan(appDir, input, options = {}) {
|
|
|
14098
15011
|
return err(HaiDeployError.CONFIG_ERROR, deployM("deploy_artifactInvalid"));
|
|
14099
15012
|
}
|
|
14100
15013
|
}
|
|
14101
|
-
var nameSchema = z.string().regex(/^[a-z0-9][\w.-]{0,127}$/i);
|
|
14102
|
-
var passwordSchema = z.string().min(1).max(1024).refine((value) => Buffer.from(value, "utf8").toString("utf8") === value && [...value].every((character) => character.codePointAt(0) >= 32 && character.codePointAt(0) !== 127));
|
|
14103
|
-
var importSchema = z.strictObject({
|
|
14104
|
-
version: z.literal(1),
|
|
14105
|
-
projectName: nameSchema,
|
|
14106
|
-
credentials: z.strictObject({
|
|
14107
|
-
DB_PASSWORD: passwordSchema.optional(),
|
|
14108
|
-
STORAGE_PASSWORD: passwordSchema.optional(),
|
|
14109
|
-
LDAP_PASSWORD: passwordSchema.optional()
|
|
14110
|
-
}),
|
|
14111
|
-
services: z.array(z.strictObject({
|
|
14112
|
-
service: z.enum(["db", "vecdb", "storage", "ldap"]),
|
|
14113
|
-
container: nameSchema,
|
|
14114
|
-
kind: z.enum(["postgresql", "mysql", "s3", "ldap"]),
|
|
14115
|
-
volumes: z.array(nameSchema).min(1).max(8),
|
|
14116
|
-
username: z.string().min(1).max(128).default("hai"),
|
|
14117
|
-
database: z.string().min(1).max(128).default("hai"),
|
|
14118
|
-
bucket: z.string().regex(/^[a-z0-9][a-z0-9.-]{1,61}[a-z0-9]$/).default("hai"),
|
|
14119
|
-
accessKeyId: z.string().min(1).max(128).default("hai-storage"),
|
|
14120
|
-
region: z.string().regex(/^[a-z0-9-]+$/).default("us-east-1"),
|
|
14121
|
-
bindDn: z.string().min(1).max(512).default("cn=admin,dc=example,dc=org")
|
|
14122
|
-
})).min(1).max(4)
|
|
14123
|
-
});
|
|
14124
|
-
var groups2 = {
|
|
14125
|
-
db: "DB_PASSWORD",
|
|
14126
|
-
vecdb: "DB_PASSWORD",
|
|
14127
|
-
storage: "STORAGE_PASSWORD",
|
|
14128
|
-
ldap: "LDAP_PASSWORD"
|
|
14129
|
-
};
|
|
14130
|
-
var importCommitScript = String.raw`(async()=>{const fs=require('node:fs'),path=require('node:path'),crypto=require('node:crypto');const root=process.argv[1];let temporary;
|
|
14131
|
-
try{process.stdout.write('LOCKED\n');let input='';for await(const chunk of process.stdin){input+=chunk;if(input.length>65536)throw Error()}
|
|
14132
|
-
if(!input)return;const {content}=JSON.parse(input);if(typeof content!=='string'||!content.startsWith('FORMAT_VERSION=2\n'))throw Error();
|
|
14133
|
-
const state=path.join(root,'managed-secrets.env');try{fs.lstatSync(state);throw Error('exists')}catch(error){if(error.code!=='ENOENT')throw error}
|
|
14134
|
-
temporary=path.join(root,'.managed-secrets-'+crypto.randomUUID()+'.next');const fd=fs.openSync(temporary,fs.constants.O_WRONLY|fs.constants.O_CREAT|fs.constants.O_EXCL|fs.constants.O_NOFOLLOW,0o600);
|
|
14135
|
-
try{fs.writeFileSync(fd,content);fs.fsyncSync(fd)}finally{fs.closeSync(fd)}fs.renameSync(temporary,state);temporary=undefined;
|
|
14136
|
-
const directory=fs.openSync(root,fs.constants.O_RDONLY);try{fs.fsyncSync(directory)}finally{fs.closeSync(directory)}process.stdout.write('COMMITTED\n');
|
|
14137
|
-
}catch{process.exitCode=1}finally{if(temporary)try{fs.unlinkSync(temporary)}catch{}}})()`;
|
|
14138
|
-
var s3VerificationScript = String.raw`(async()=>{try{
|
|
14139
|
-
const {createHash,createHmac}=require('node:crypto');let input='';for await(const chunk of process.stdin)input+=chunk;
|
|
14140
|
-
const {endpoint,bucket,accessKeyId,secret,region}=JSON.parse(input);const url=new URL('/'+encodeURIComponent(bucket),endpoint);
|
|
14141
|
-
const hash=value=>createHash('sha256').update(value).digest('hex');const hmac=(key,value)=>createHmac('sha256',key).update(value).digest();
|
|
14142
|
-
const date=new Date().toISOString().replace(/[:-]|\.\d{3}/g,'');const day=date.slice(0,8);const payload=hash('');
|
|
14143
|
-
const headers='host:'+url.host+'\nx-amz-content-sha256:'+payload+'\nx-amz-date:'+date+'\n';const signed='host;x-amz-content-sha256;x-amz-date';
|
|
14144
|
-
const scope=day+'/'+region+'/s3/aws4_request';const canonical=['HEAD',url.pathname,'',headers,signed,payload].join('\n');
|
|
14145
|
-
const key=hmac(hmac(hmac(hmac('AWS4'+secret,day),region),'s3'),'aws4_request');const signature=hmac(key,['AWS4-HMAC-SHA256',date,scope,hash(canonical)].join('\n')).toString('hex');
|
|
14146
|
-
const response=await fetch(url,{method:'HEAD',redirect:'manual',signal:AbortSignal.timeout(10000),headers:{'x-amz-date':date,'x-amz-content-sha256':payload,Authorization:'AWS4-HMAC-SHA256 Credential='+accessKeyId+'/'+scope+', SignedHeaders='+signed+', Signature='+signature}});
|
|
14147
|
-
await response.body?.cancel();if(response.ok)process.stdout.write('VERIFIED');else process.exitCode=1;
|
|
14148
|
-
}catch{process.exitCode=1}})()`;
|
|
14149
|
-
async function importDeploymentSecrets(options) {
|
|
14150
|
-
let lock;
|
|
14151
|
-
let commitAttempted = false;
|
|
14152
|
-
let phase = "input";
|
|
14153
|
-
try {
|
|
14154
|
-
if (!["docker", "podman"].includes(options.runtime))
|
|
14155
|
-
throw new Error("input");
|
|
14156
|
-
const root = resolve(options.directory);
|
|
14157
|
-
if (realpathSync(root) !== root || !lstatSync(root).isDirectory() || process.platform !== "win32" && lstatSync(root).mode & 18)
|
|
14158
|
-
throw new Error("input");
|
|
14159
|
-
const input = privateFile(resolve(options.inputFile));
|
|
14160
|
-
const parsed = importSchema.safeParse(JSON.parse(input));
|
|
14161
|
-
if (!parsed.success) {
|
|
14162
|
-
return err({
|
|
14163
|
-
...HaiDeployError.CONFIG_ERROR,
|
|
14164
|
-
message: deployM("deploy_secretImportFailed", {
|
|
14165
|
-
params: { phase }
|
|
14166
|
-
}),
|
|
14167
|
-
ext: {
|
|
14168
|
-
phase,
|
|
14169
|
-
state: "unchanged",
|
|
14170
|
-
fields: parsed.error.issues.map((issue) => issue.path.join("."))
|
|
14171
|
-
}
|
|
14172
|
-
});
|
|
14173
|
-
}
|
|
14174
|
-
const config = parsed.data;
|
|
14175
|
-
const project = `hai-${config.projectName}`;
|
|
14176
|
-
const selectedGroups = [
|
|
14177
|
-
...new Set(config.services.map((service) => groups2[service.service]))
|
|
14178
|
-
].sort();
|
|
14179
|
-
if (new Set(config.services.map((service) => service.service)).size !== config.services.length || Object.keys(config.credentials).some((group) => !selectedGroups.includes(group)) || selectedGroups.some((group) => !config.credentials[group])) {
|
|
14180
|
-
throw new Error("input");
|
|
14181
|
-
}
|
|
14182
|
-
const secrets = Object.values(config.credentials).filter((value) => typeof value === "string");
|
|
14183
|
-
const stateFile = join(root, "managed-secrets.env");
|
|
14184
|
-
const lockFile = join(root, ".deploy.lock");
|
|
14185
|
-
phase = "lock";
|
|
14186
|
-
if (options.dryRun === false) {
|
|
14187
|
-
if (process.platform !== "linux")
|
|
14188
|
-
throw new Error("lock");
|
|
14189
|
-
if (existsSync(lockFile) && (lstatSync(lockFile).isSymbolicLink() || !lstatSync(lockFile).isFile() || lstatSync(lockFile).nlink !== 1))
|
|
14190
|
-
throw new Error("lock");
|
|
14191
|
-
lock = await acquireImportLock(lockFile, root);
|
|
14192
|
-
}
|
|
14193
|
-
phase = "identity";
|
|
14194
|
-
const volumes = [];
|
|
14195
|
-
for (const service of config.services) {
|
|
14196
|
-
const kindValid = service.service === "storage" ? service.kind === "s3" : service.service === "ldap" ? service.kind === "ldap" : ["postgresql", "mysql"].includes(service.kind);
|
|
14197
|
-
if (!kindValid)
|
|
14198
|
-
throw new Error("identity");
|
|
14199
|
-
if (["postgresql", "mysql"].includes(service.kind) && (service.username !== "hai" || service.database !== "hai") || service.kind === "s3" && (service.accessKeyId !== "hai-storage" || service.bucket !== "hai" || service.region !== "us-east-1") || service.kind === "ldap" && service.bindDn !== "cn=admin,dc=example,dc=org") {
|
|
14200
|
-
throw new Error("identity");
|
|
14201
|
-
}
|
|
14202
|
-
const identity = await inspectContainer(options.runtime, service.container, secrets);
|
|
14203
|
-
if (identity.project !== project || identity.service !== service.service || !identity.running)
|
|
14204
|
-
throw new Error("identity");
|
|
14205
|
-
for (const volume of service.volumes) {
|
|
14206
|
-
if (!identity.volumes.includes(volume))
|
|
14207
|
-
throw new Error("identity");
|
|
14208
|
-
volumes.push({ group: groups2[service.service], name: volume });
|
|
14209
|
-
}
|
|
14210
|
-
phase = "verification";
|
|
14211
|
-
const password = config.credentials[groups2[service.service]];
|
|
14212
|
-
let verified = false;
|
|
14213
|
-
if (service.kind === "s3") {
|
|
14214
|
-
const helper2 = `hai-${config.projectName}-app`;
|
|
14215
|
-
const application = await inspectContainer(options.runtime, helper2, secrets);
|
|
14216
|
-
if (application.project !== project || application.service !== "app" || !application.running || !application.networks.some((network) => identity.networks.includes(network)))
|
|
14217
|
-
throw new Error("identity");
|
|
14218
|
-
const result = await runCommand(options.runtime, [
|
|
14219
|
-
"exec",
|
|
14220
|
-
"-i",
|
|
14221
|
-
helper2,
|
|
14222
|
-
"node",
|
|
14223
|
-
"-e",
|
|
14224
|
-
s3VerificationScript
|
|
14225
|
-
], {
|
|
14226
|
-
input: JSON.stringify({
|
|
14227
|
-
endpoint: `http://${service.container}:9000`,
|
|
14228
|
-
bucket: service.bucket,
|
|
14229
|
-
accessKeyId: service.accessKeyId,
|
|
14230
|
-
secret: password,
|
|
14231
|
-
region: service.region
|
|
14232
|
-
}),
|
|
14233
|
-
secrets,
|
|
14234
|
-
timeoutMs: 15e3,
|
|
14235
|
-
maxCaptureChars: 1024
|
|
14236
|
-
});
|
|
14237
|
-
verified = result.success && result.data.stdout.trim() === "VERIFIED";
|
|
14238
|
-
} else {
|
|
14239
|
-
const args = service.kind === "ldap" ? [
|
|
14240
|
-
"exec",
|
|
14241
|
-
"-i",
|
|
14242
|
-
service.container,
|
|
14243
|
-
"ldapwhoami",
|
|
14244
|
-
"-x",
|
|
14245
|
-
"-H",
|
|
14246
|
-
"ldap://127.0.0.1:389",
|
|
14247
|
-
"-D",
|
|
14248
|
-
service.bindDn,
|
|
14249
|
-
"-y",
|
|
14250
|
-
"/dev/stdin"
|
|
14251
|
-
] : [
|
|
14252
|
-
"exec",
|
|
14253
|
-
"-i",
|
|
14254
|
-
service.container,
|
|
14255
|
-
"sh",
|
|
14256
|
-
"-c",
|
|
14257
|
-
service.kind === "postgresql" ? 'IFS= read -r PGPASSWORD; export PGPASSWORD; exec psql --no-psqlrc -h "$3" -U "$1" -d "dbname=$2 require_auth=scram-sha-256,md5,password" -Atc "SELECT 1"' : 'IFS= read -r MYSQL_PWD; export MYSQL_PWD; exec mysql -h 127.0.0.1 -u "$1" "$2" -Nse "SELECT 1"',
|
|
14258
|
-
"hai-secret-check",
|
|
14259
|
-
service.username,
|
|
14260
|
-
service.database,
|
|
14261
|
-
service.container
|
|
14262
|
-
];
|
|
14263
|
-
const result = await runCommand(options.runtime, args, {
|
|
14264
|
-
input: service.kind === "ldap" ? password : `${password}
|
|
14265
|
-
`,
|
|
14266
|
-
secrets,
|
|
14267
|
-
timeoutMs: 15e3,
|
|
14268
|
-
maxCaptureChars: 1024
|
|
14269
|
-
});
|
|
14270
|
-
verified = result.success && !result.data.stdoutTruncated && (service.kind === "ldap" ? result.data.stdout.trim().startsWith("dn:") : result.data.stdout.trim() === "1");
|
|
14271
|
-
if (verified && service.kind === "mysql") {
|
|
14272
|
-
const rootCheck = await runCommand(options.runtime, [
|
|
14273
|
-
...args.slice(0, -3),
|
|
14274
|
-
"root",
|
|
14275
|
-
service.database,
|
|
14276
|
-
service.container
|
|
14277
|
-
], {
|
|
14278
|
-
input: `${password}
|
|
14279
|
-
`,
|
|
14280
|
-
secrets,
|
|
14281
|
-
timeoutMs: 15e3,
|
|
14282
|
-
maxCaptureChars: 1024
|
|
14283
|
-
});
|
|
14284
|
-
verified = rootCheck.success && !rootCheck.data.stdoutTruncated && rootCheck.data.stdout.trim() === "1";
|
|
14285
|
-
const invalidPassword = `hai-import-invalid-${randomUUID()}`;
|
|
14286
|
-
for (const username of [service.username, "root"]) {
|
|
14287
|
-
if (!verified)
|
|
14288
|
-
break;
|
|
14289
|
-
const negative = await runCommand(options.runtime, [
|
|
14290
|
-
...args.slice(0, -3),
|
|
14291
|
-
username,
|
|
14292
|
-
service.database,
|
|
14293
|
-
service.container
|
|
14294
|
-
], {
|
|
14295
|
-
input: `${invalidPassword}
|
|
14296
|
-
`,
|
|
14297
|
-
secrets: [...secrets, invalidPassword],
|
|
14298
|
-
timeoutMs: 15e3,
|
|
14299
|
-
maxCaptureChars: 1024
|
|
14300
|
-
});
|
|
14301
|
-
verified = !negative.success && /ERROR 1045 \(28000\)/.test(negative.error.message);
|
|
14302
|
-
}
|
|
14303
|
-
}
|
|
14304
|
-
}
|
|
14305
|
-
if (!verified)
|
|
14306
|
-
throw new Error("verification");
|
|
14307
|
-
phase = "identity";
|
|
14308
|
-
}
|
|
14309
|
-
phase = "state";
|
|
14310
|
-
const uniqueVolumes = [
|
|
14311
|
-
...new Map(volumes.map((volume) => [`${volume.group}:${volume.name}`, volume])).values()
|
|
14312
|
-
].sort((a, b) => `${a.group}:${a.name}`.localeCompare(`${b.group}:${b.name}`));
|
|
14313
|
-
const lines = [
|
|
14314
|
-
"FORMAT_VERSION=2",
|
|
14315
|
-
`PROJECT=${project}`,
|
|
14316
|
-
...selectedGroups.map((group) => `${group}=${Buffer.from(config.credentials[group], "utf8").toString("hex")}`),
|
|
14317
|
-
...uniqueVolumes.map((volume, index) => `VOLUME_${index + 1}=${volume.group}:${volume.name}`)
|
|
14318
|
-
];
|
|
14319
|
-
let changed = false;
|
|
14320
|
-
if (existsSync(stateFile) || isSymbolicLink(stateFile)) {
|
|
14321
|
-
const existing = readImportedState(privateFile(stateFile));
|
|
14322
|
-
if (existing.FORMAT_VERSION !== "2" || existing.PROJECT !== project || selectedGroups.some((group) => existing[group] !== Buffer.from(config.credentials[group], "utf8").toString("hex")) || uniqueVolumes.some((volume) => !Object.entries(existing).some(([key, value]) => /^VOLUME_\d+$/.test(key) && value === `${volume.group}:${volume.name}`)))
|
|
14323
|
-
throw new Error("state");
|
|
14324
|
-
} else if (options.dryRun === false) {
|
|
14325
|
-
commitAttempted = true;
|
|
14326
|
-
if (!lock || !await lock.commit(`${lines.join("\n")}
|
|
14327
|
-
`))
|
|
14328
|
-
throw new Error("lock");
|
|
14329
|
-
changed = true;
|
|
14330
|
-
}
|
|
14331
|
-
return ok({
|
|
14332
|
-
projectName: config.projectName,
|
|
14333
|
-
stateVersion: 2,
|
|
14334
|
-
groups: selectedGroups,
|
|
14335
|
-
volumes: uniqueVolumes.map((volume) => volume.name),
|
|
14336
|
-
changed
|
|
14337
|
-
});
|
|
14338
|
-
} catch {
|
|
14339
|
-
return err({
|
|
14340
|
-
...HaiDeployError.CONFIG_ERROR,
|
|
14341
|
-
message: deployM("deploy_secretImportFailed", {
|
|
14342
|
-
params: { phase }
|
|
14343
|
-
}),
|
|
14344
|
-
ext: {
|
|
14345
|
-
phase,
|
|
14346
|
-
state: commitAttempted ? "unknown" : "unchanged"
|
|
14347
|
-
}
|
|
14348
|
-
});
|
|
14349
|
-
} finally {
|
|
14350
|
-
await lock?.release();
|
|
14351
|
-
}
|
|
14352
|
-
}
|
|
14353
|
-
function readImportedState(text) {
|
|
14354
|
-
const result = {};
|
|
14355
|
-
for (const line of text.trimEnd().split("\n")) {
|
|
14356
|
-
const match = /^(FORMAT_VERSION|PROJECT|DB_PASSWORD|STORAGE_PASSWORD|LDAP_PASSWORD|VOLUME_[1-9]\d*)=([^=]+)$/.exec(line);
|
|
14357
|
-
if (!match || Object.hasOwn(result, match[1]))
|
|
14358
|
-
throw new Error("state");
|
|
14359
|
-
const key = match[1];
|
|
14360
|
-
const value = match[2];
|
|
14361
|
-
if (key.endsWith("_PASSWORD")) {
|
|
14362
|
-
if (!/^(?:[a-f0-9]{2})+$/.test(value))
|
|
14363
|
-
throw new Error("state");
|
|
14364
|
-
const decoded = Buffer.from(value, "hex").toString("utf8");
|
|
14365
|
-
if (!passwordSchema.safeParse(decoded).success || Buffer.from(decoded, "utf8").toString("hex") !== value)
|
|
14366
|
-
throw new Error("state");
|
|
14367
|
-
} else if (key.startsWith("VOLUME_") && !/^(?:DB_PASSWORD|STORAGE_PASSWORD|LDAP_PASSWORD):[a-zA-Z0-9][\w.-]*$/.test(value)) {
|
|
14368
|
-
throw new Error("state");
|
|
14369
|
-
}
|
|
14370
|
-
result[key] = value;
|
|
14371
|
-
}
|
|
14372
|
-
if (result.FORMAT_VERSION !== "2" || !nameSchema.safeParse(result.PROJECT).success)
|
|
14373
|
-
throw new Error("state");
|
|
14374
|
-
return result;
|
|
14375
|
-
}
|
|
14376
|
-
function isSymbolicLink(path) {
|
|
14377
|
-
try {
|
|
14378
|
-
return lstatSync(path).isSymbolicLink();
|
|
14379
|
-
} catch {
|
|
14380
|
-
return false;
|
|
14381
|
-
}
|
|
14382
|
-
}
|
|
14383
|
-
function privateFile(path) {
|
|
14384
|
-
return readLocalFile(path, { maxBytes: 65536, private: true }).toString("utf8");
|
|
14385
|
-
}
|
|
14386
|
-
async function inspectContainer(runtime, container, secrets) {
|
|
14387
|
-
const result = await runCommand(runtime, [
|
|
14388
|
-
"inspect",
|
|
14389
|
-
"--format",
|
|
14390
|
-
"{{json .Config.Labels}}\n{{json .Mounts}}\n{{json .State.Running}}\n{{json .NetworkSettings.Networks}}",
|
|
14391
|
-
container
|
|
14392
|
-
], { secrets, timeoutMs: 1e4, maxCaptureChars: 32768 });
|
|
14393
|
-
if (!result.success || result.data.stdoutTruncated)
|
|
14394
|
-
throw new Error("identity");
|
|
14395
|
-
const [labelsText, mountsText, runningText, networksText] = result.data.stdout.trim().split("\n");
|
|
14396
|
-
const labels = z.record(z.string(), z.string()).parse(JSON.parse(labelsText));
|
|
14397
|
-
const mounts = z.array(z.object({
|
|
14398
|
-
Type: z.string(),
|
|
14399
|
-
Name: z.string().optional()
|
|
14400
|
-
})).parse(JSON.parse(mountsText));
|
|
14401
|
-
const networks = z.record(z.string(), z.object({
|
|
14402
|
-
NetworkID: z.string()
|
|
14403
|
-
})).parse(JSON.parse(networksText) ?? {});
|
|
14404
|
-
const dockerProject = labels["com.docker.compose.project"] || void 0;
|
|
14405
|
-
const podmanProject = labels["io.podman.compose.project"] || void 0;
|
|
14406
|
-
const dockerService = labels["com.docker.compose.service"] || void 0;
|
|
14407
|
-
const podmanService = labels["io.podman.compose.service"] || void 0;
|
|
14408
|
-
if (Boolean(dockerProject) !== Boolean(dockerService) || Boolean(podmanProject) !== Boolean(podmanService) || dockerProject && podmanProject && dockerProject !== podmanProject || dockerService && podmanService && dockerService !== podmanService) {
|
|
14409
|
-
throw new Error("identity");
|
|
14410
|
-
}
|
|
14411
|
-
return {
|
|
14412
|
-
project: dockerProject ?? podmanProject,
|
|
14413
|
-
service: dockerService ?? podmanService,
|
|
14414
|
-
running: runningText === "true",
|
|
14415
|
-
volumes: mounts.flatMap((mount) => mount.Type === "volume" && mount.Name ? [mount.Name] : []),
|
|
14416
|
-
networks: Object.values(networks).map((network) => network.NetworkID)
|
|
14417
|
-
};
|
|
14418
|
-
}
|
|
14419
|
-
async function acquireImportLock(path, root) {
|
|
14420
|
-
return new Promise((resolveLock, reject) => {
|
|
14421
|
-
const child = spawn("flock", [
|
|
14422
|
-
"-n",
|
|
14423
|
-
path,
|
|
14424
|
-
process.execPath,
|
|
14425
|
-
"-e",
|
|
14426
|
-
importCommitScript,
|
|
14427
|
-
root
|
|
14428
|
-
], {
|
|
14429
|
-
stdio: ["pipe", "pipe", "ignore"],
|
|
14430
|
-
windowsHide: true
|
|
14431
|
-
});
|
|
14432
|
-
let acquired = false;
|
|
14433
|
-
let exited = false;
|
|
14434
|
-
let output = "";
|
|
14435
|
-
const finished = new Promise((done) => child.once("close", () => {
|
|
14436
|
-
exited = true;
|
|
14437
|
-
done();
|
|
14438
|
-
}));
|
|
14439
|
-
child.stdin.on("error", () => {
|
|
14440
|
-
});
|
|
14441
|
-
const timer = setTimeout(() => {
|
|
14442
|
-
child.stdin.end();
|
|
14443
|
-
child.kill();
|
|
14444
|
-
reject(new Error("lock"));
|
|
14445
|
-
}, 5e3);
|
|
14446
|
-
child.once("error", () => {
|
|
14447
|
-
clearTimeout(timer);
|
|
14448
|
-
reject(new Error("lock"));
|
|
14449
|
-
});
|
|
14450
|
-
child.once("exit", () => {
|
|
14451
|
-
if (!acquired) {
|
|
14452
|
-
clearTimeout(timer);
|
|
14453
|
-
reject(new Error("lock"));
|
|
14454
|
-
}
|
|
14455
|
-
});
|
|
14456
|
-
child.stdout.on("data", (data) => {
|
|
14457
|
-
output += data.toString();
|
|
14458
|
-
if (acquired || !output.includes("\n"))
|
|
14459
|
-
return;
|
|
14460
|
-
if (!output.startsWith("LOCKED\n") || exited) {
|
|
14461
|
-
child.stdin.end();
|
|
14462
|
-
reject(new Error("lock"));
|
|
14463
|
-
return;
|
|
14464
|
-
}
|
|
14465
|
-
acquired = true;
|
|
14466
|
-
clearTimeout(timer);
|
|
14467
|
-
resolveLock({
|
|
14468
|
-
commit: async (content) => {
|
|
14469
|
-
if (exited)
|
|
14470
|
-
return false;
|
|
14471
|
-
child.stdin.end(JSON.stringify({ content }));
|
|
14472
|
-
await finished;
|
|
14473
|
-
return child.exitCode === 0 && output === "LOCKED\nCOMMITTED\n";
|
|
14474
|
-
},
|
|
14475
|
-
release: async () => {
|
|
14476
|
-
child.stdin.end();
|
|
14477
|
-
await finished;
|
|
14478
|
-
}
|
|
14479
|
-
});
|
|
14480
|
-
});
|
|
14481
|
-
});
|
|
14482
|
-
}
|
|
14483
15014
|
|
|
14484
|
-
export { DEPLOY_MIDDLEWARE, DeployConfigSchema, DeploymentArtifactSchema, HaiDeployError, ProviderConfigSchema, artifactChecksum, canKeepMiddleware, createDeploymentPlan, deploy, describeMiddlewareBackend, getDeployCommandState, getDeployRecovery,
|
|
15015
|
+
export { DEPLOY_MIDDLEWARE, DeployConfigSchema, DeploymentArtifactSchema, HaiDeployError, ProviderConfigSchema, artifactChecksum, canKeepMiddleware, createDeploymentPlan, deploy, describeMiddlewareBackend, getDeployCommandState, getDeployRecovery, loadDeploymentConfig, loadDeploymentEnvironment, mergeEnvironmentLayers, normalizeProjectName, parseMiddlewareField, readDeploymentArtifact, resolveDownloadProxy, resolveImageReference, saveDeploymentEnvironment, saveMiddlewareEnvironment, validateMiddlewareField };
|
|
14485
15016
|
//# sourceMappingURL=index.js.map
|
|
14486
15017
|
//# sourceMappingURL=index.js.map
|