@synopackageland/cli 0.3.1 → 0.3.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/attestation/app-package.js +8 -1
- package/dist/attestation/platform-service.d.ts +70 -0
- package/dist/attestation/platform-service.js +250 -0
- package/dist/bin.js +0 -0
- package/dist/build-spk/generator.js +200 -10
- package/dist/build-spk/resource-worker.d.ts +14 -0
- package/dist/build-spk/resource-worker.js +140 -0
- package/dist/cli.js +69 -10
- package/dist/compose/analyze.d.ts +42 -1
- package/dist/compose/analyze.js +482 -18
- package/dist/contract/load.d.ts +1 -0
- package/dist/contract/load.js +1 -1
- package/dist/contract/posix-all-shares.d.ts +16 -0
- package/dist/contract/posix-all-shares.js +63 -0
- package/dist/contract/schema-validate.js +26 -1
- package/dist/contract/validate.js +57 -0
- package/dist/contract/web-launch.d.ts +6 -0
- package/dist/contract/web-launch.js +25 -0
- package/dist/deploy/deploy.d.ts +1 -0
- package/dist/deploy/deploy.js +94 -2
- package/dist/deploy/homes-values.d.ts +19 -0
- package/dist/deploy/homes-values.js +29 -0
- package/dist/deploy/param-values.d.ts +21 -0
- package/dist/deploy/param-values.js +51 -0
- package/dist/deploy/spk-version.d.ts +13 -0
- package/dist/deploy/spk-version.js +80 -27
- package/dist/doctor.d.ts +22 -0
- package/dist/doctor.js +594 -0
- package/dist/error-collector.d.ts +2 -0
- package/dist/error-collector.js +46 -0
- package/dist/error-registry.d.ts +7 -0
- package/dist/error-registry.js +837 -0
- package/dist/host/broker-upgrade-preflight.d.ts +61 -0
- package/dist/host/broker-upgrade-preflight.js +156 -0
- package/dist/host/catalog-install.d.ts +20 -0
- package/dist/host/catalog-install.js +143 -17
- package/dist/host/credentials.js +35 -5
- package/dist/host/dsm-client.d.ts +12 -0
- package/dist/host/dsm-client.js +69 -1
- package/dist/host/dsm-compound.d.ts +37 -0
- package/dist/host/dsm-compound.js +120 -0
- package/dist/host/fake-nas.d.ts +1 -0
- package/dist/host/fake-nas.js +3 -0
- package/dist/host/types.d.ts +6 -0
- package/dist/publish/curated-release.js +8 -4
- package/dist/publish/platform-service-release.d.ts +60 -0
- package/dist/publish/platform-service-release.js +333 -0
- package/dist/publish/preflight.js +24 -2
- package/dist/release-bin.js +5 -1
- package/dist/templates/hello-files/compose.yaml +2 -0
- package/dist/templates/hello-web/compose.yaml +14 -3
- package/dist/templates/hello-web/synology-app.yaml +1 -1
- package/dist/templates/hello-web-page/compose.yaml +14 -3
- package/dist/templates/hello-web-page/synology-app.yaml +1 -1
- package/dist/test-host.d.ts +2 -0
- package/dist/test-host.js +15 -2
- package/dist/types.d.ts +1 -1
- package/dist/validate.js +14 -3
- package/package.json +2 -2
- package/schema/doctor.schema.json +38 -0
- package/schema/synology-app.schema.json +1 -1
- package/skills/reference/create.md +19 -0
- package/skills/reference/diagnose.md +35 -2
- package/skills/reference/files.md +10 -0
- package/templates/hello-files/compose.yaml +2 -0
- package/templates/hello-web/compose.yaml +14 -3
- package/templates/hello-web/synology-app.yaml +1 -1
- package/templates/hello-web-page/compose.yaml +14 -3
- package/templates/hello-web-page/synology-app.yaml +1 -1
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
import { cookieStripHeaders, resolvedWebLaunch, webStationAlias, webStationServiceName, } from "../contract/web-launch.js";
|
|
2
|
+
export { classifyComposeRoute, resourceWorkerEligibility, } from "../compose/analyze.js";
|
|
3
|
+
export function composeProjectName(packageId) {
|
|
4
|
+
const slug = packageId
|
|
5
|
+
.toLowerCase()
|
|
6
|
+
.replace(/[^a-z0-9_-]+/g, "-")
|
|
7
|
+
.replace(/^[-_]+|[-_]+$/g, "");
|
|
8
|
+
return slug.length > 0 ? slug : "app";
|
|
9
|
+
}
|
|
10
|
+
export function renderDockerProjectResource(packageId, projectPath = "compose") {
|
|
11
|
+
const projectName = composeProjectName(packageId);
|
|
12
|
+
return {
|
|
13
|
+
"docker-project": {
|
|
14
|
+
projects: [
|
|
15
|
+
{
|
|
16
|
+
name: projectName,
|
|
17
|
+
path: projectPath,
|
|
18
|
+
build_params: {
|
|
19
|
+
force_pull: true,
|
|
20
|
+
force_recreate: true,
|
|
21
|
+
build: false,
|
|
22
|
+
},
|
|
23
|
+
},
|
|
24
|
+
],
|
|
25
|
+
},
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
export function renderWebserviceResource(contract, adminport) {
|
|
29
|
+
const alias = webStationAlias(String(contract.package));
|
|
30
|
+
const launch = resolvedWebLaunch(contract);
|
|
31
|
+
const cookieHeaders = cookieStripHeaders(contract);
|
|
32
|
+
const proxyHeaderEntries = cookieHeaders.map((header) => ({
|
|
33
|
+
name: header.name,
|
|
34
|
+
value: header.value,
|
|
35
|
+
}));
|
|
36
|
+
const service = {
|
|
37
|
+
service: webStationServiceName(alias),
|
|
38
|
+
display_name: String(contract.displayname ?? contract.package),
|
|
39
|
+
type: "reverse_proxy",
|
|
40
|
+
support_alias: true,
|
|
41
|
+
support_server: false,
|
|
42
|
+
proxy_target: `http://127.0.0.1:${adminport}`,
|
|
43
|
+
proxy_headers: proxyHeaderEntries,
|
|
44
|
+
};
|
|
45
|
+
const portal = {
|
|
46
|
+
service: webStationServiceName(alias),
|
|
47
|
+
name: alias,
|
|
48
|
+
type: "alias",
|
|
49
|
+
alias,
|
|
50
|
+
};
|
|
51
|
+
if (launch === "dsm-window") {
|
|
52
|
+
portal.app = String(contract.package);
|
|
53
|
+
}
|
|
54
|
+
return {
|
|
55
|
+
webservice: {
|
|
56
|
+
services: [service],
|
|
57
|
+
portals: [portal],
|
|
58
|
+
},
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
export function renderResourceConfig(contract, eligibility) {
|
|
62
|
+
const packageId = String(contract.package);
|
|
63
|
+
const dockerResource = renderDockerProjectResource(packageId, "compose");
|
|
64
|
+
let combined = { ...dockerResource };
|
|
65
|
+
if (eligibility.adminport !== undefined) {
|
|
66
|
+
const webResource = renderWebserviceResource(contract, eligibility.adminport);
|
|
67
|
+
combined = {
|
|
68
|
+
...combined,
|
|
69
|
+
...webResource,
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
return `${JSON.stringify(combined, null, 2)}\n`;
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Renders the lifecycle control script for the Docker Project resource-worker
|
|
76
|
+
* route. ContainerManager owns the actual project start/stop operation through
|
|
77
|
+
* the worker timing hooks; the package script must wait for that operation to
|
|
78
|
+
* leave a reachable service and must not claim running unconditionally.
|
|
79
|
+
*/
|
|
80
|
+
export function renderResourceWorkerStartStopStatus(adminport) {
|
|
81
|
+
if (adminport === undefined) {
|
|
82
|
+
return `#!/bin/sh
|
|
83
|
+
case "$1" in
|
|
84
|
+
start|status)
|
|
85
|
+
echo "resource-worker package has no published port to probe" >&2
|
|
86
|
+
exit 3
|
|
87
|
+
;;
|
|
88
|
+
stop) exit 0 ;;
|
|
89
|
+
*) exit 1 ;;
|
|
90
|
+
esac
|
|
91
|
+
`;
|
|
92
|
+
}
|
|
93
|
+
const url = `http://127.0.0.1:${adminport}/`;
|
|
94
|
+
return `#!/bin/sh
|
|
95
|
+
probe_service() {
|
|
96
|
+
if command -v curl >/dev/null 2>&1; then
|
|
97
|
+
curl --silent --output /dev/null --connect-timeout 2 --max-time 5 "${url}"
|
|
98
|
+
return $?
|
|
99
|
+
fi
|
|
100
|
+
if command -v wget >/dev/null 2>&1; then
|
|
101
|
+
wget --quiet --output-document=/dev/null --timeout=5 --tries=1 "${url}"
|
|
102
|
+
return $?
|
|
103
|
+
fi
|
|
104
|
+
return 127
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
wait_for_service() {
|
|
108
|
+
attempts=0
|
|
109
|
+
while [ "$attempts" -lt 30 ]; do
|
|
110
|
+
if probe_service; then
|
|
111
|
+
return 0
|
|
112
|
+
fi
|
|
113
|
+
attempts=$((attempts + 1))
|
|
114
|
+
sleep 1
|
|
115
|
+
done
|
|
116
|
+
return 1
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
case "$1" in
|
|
120
|
+
start)
|
|
121
|
+
if wait_for_service; then
|
|
122
|
+
exit 0
|
|
123
|
+
fi
|
|
124
|
+
exit 3
|
|
125
|
+
;;
|
|
126
|
+
stop)
|
|
127
|
+
exit 0
|
|
128
|
+
;;
|
|
129
|
+
status)
|
|
130
|
+
if probe_service; then
|
|
131
|
+
exit 0
|
|
132
|
+
fi
|
|
133
|
+
exit 3
|
|
134
|
+
;;
|
|
135
|
+
*)
|
|
136
|
+
exit 1
|
|
137
|
+
;;
|
|
138
|
+
esac
|
|
139
|
+
`;
|
|
140
|
+
}
|
package/dist/cli.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { Command } from "commander";
|
|
2
2
|
import { resolve, dirname } from "node:path";
|
|
3
3
|
import { findProjectRoot } from "./discovery.js";
|
|
4
|
+
import { loadContract } from "./contract/load.js";
|
|
4
5
|
import { formatErrors } from "./errors.js";
|
|
5
6
|
import { formatInspectReport, inspectProject } from "./inspect.js";
|
|
6
7
|
import { initFromTemplate, isTemplateName, listTemplates } from "./init.js";
|
|
@@ -19,6 +20,7 @@ import { publishProject, resolveCommunityAppsRoot } from "./publish/publish.js";
|
|
|
19
20
|
import { formatPreflight, runPublicReleasePreflight } from "./publish/preflight.js";
|
|
20
21
|
import { createExecGitHubCli, createExecGitOps } from "./publish/github-pr.js";
|
|
21
22
|
import { communityAppsGitHubRepo } from "./publish/github-repo.js";
|
|
23
|
+
import { resolveInstallParameterExtraValues } from "./deploy/param-values.js";
|
|
22
24
|
function collectKvOption(value, previous, flag, example) {
|
|
23
25
|
const separator = value.indexOf("=");
|
|
24
26
|
if (separator <= 0) {
|
|
@@ -35,9 +37,15 @@ function collectShareOption(value, previous) {
|
|
|
35
37
|
function collectRunAsOption(value, previous) {
|
|
36
38
|
return collectKvOption(value, previous, "--run-as", "--run-as RUN_AS=alice");
|
|
37
39
|
}
|
|
40
|
+
function collectParamOption(value, previous) {
|
|
41
|
+
return collectKvOption(value, previous, "--param", "--param APP_MODE=prod");
|
|
42
|
+
}
|
|
38
43
|
function resolveProject(cwd, project) {
|
|
39
44
|
const start = project ? resolve(cwd, project) : cwd;
|
|
40
|
-
|
|
45
|
+
return findProjectRoot(start);
|
|
46
|
+
}
|
|
47
|
+
function requireProject(cwd, project) {
|
|
48
|
+
const found = resolveProject(cwd, project);
|
|
41
49
|
if (!found) {
|
|
42
50
|
console.error("找不到 synology-app.yaml。請在專案根目錄執行,或使用 --project 指定。");
|
|
43
51
|
process.exit(1);
|
|
@@ -50,12 +58,53 @@ export async function runCli(argv) {
|
|
|
50
58
|
.name("synopkgland")
|
|
51
59
|
.description("Syno Package Land Developer Kit CLI")
|
|
52
60
|
.option("--project <dir>", "App 專案根目錄");
|
|
61
|
+
program
|
|
62
|
+
.command("doctor")
|
|
63
|
+
.description("唯讀診斷本機合約/Compose 與 NAS 部署狀態")
|
|
64
|
+
.option("--host <name>", "synoagentcli profile 或 NAS 名稱")
|
|
65
|
+
.option("--package <id>", "指定診斷的 Package Identity(省略時由合約判定)")
|
|
66
|
+
.option("--dev", "診斷 Development Package Identity({package}.dev)")
|
|
67
|
+
.option("--json", "輸出 machine-readable JSON 診斷報告")
|
|
68
|
+
.action(async (options, command) => {
|
|
69
|
+
const global = command.parent?.opts() ?? {};
|
|
70
|
+
const project = resolveProject(process.cwd(), global.project);
|
|
71
|
+
let host = null;
|
|
72
|
+
if (options.host) {
|
|
73
|
+
host = createNasHost({ host: options.host });
|
|
74
|
+
if (!host) {
|
|
75
|
+
console.error(`[${"HOST_CREDENTIALS_MISSING"}] 找不到 NAS 憑證。請設定 synoagentcli profile 或 DSM_URL/DSM_PASSWORD。`);
|
|
76
|
+
process.exit(1);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
try {
|
|
80
|
+
const { runDoctor, formatDoctorReport } = await import("./doctor.js");
|
|
81
|
+
const report = await runDoctor({
|
|
82
|
+
project,
|
|
83
|
+
host,
|
|
84
|
+
packageId: options.package,
|
|
85
|
+
dev: options.dev,
|
|
86
|
+
});
|
|
87
|
+
if (options.json) {
|
|
88
|
+
console.log(JSON.stringify(report, null, 2));
|
|
89
|
+
}
|
|
90
|
+
else {
|
|
91
|
+
console.log(formatDoctorReport(report));
|
|
92
|
+
}
|
|
93
|
+
if (!report.ok) {
|
|
94
|
+
process.exit(1);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
catch (error) {
|
|
98
|
+
console.error(error instanceof Error ? error.message : String(error));
|
|
99
|
+
process.exit(1);
|
|
100
|
+
}
|
|
101
|
+
});
|
|
53
102
|
program
|
|
54
103
|
.command("validate")
|
|
55
104
|
.description("驗證 App Contract 與 Compose")
|
|
56
105
|
.action((options, command) => {
|
|
57
106
|
const global = command.parent?.opts() ?? {};
|
|
58
|
-
const project =
|
|
107
|
+
const project = requireProject(process.cwd(), global.project);
|
|
59
108
|
const result = validateProject(project, {
|
|
60
109
|
profile: "personal",
|
|
61
110
|
});
|
|
@@ -73,7 +122,7 @@ export async function runCli(argv) {
|
|
|
73
122
|
.description("檢視 Compose 意圖與 Web entry 提示")
|
|
74
123
|
.action((_options, command) => {
|
|
75
124
|
const global = command.parent?.opts() ?? {};
|
|
76
|
-
const project =
|
|
125
|
+
const project = requireProject(process.cwd(), global.project);
|
|
77
126
|
const report = inspectProject(project);
|
|
78
127
|
console.log(formatInspectReport(report));
|
|
79
128
|
});
|
|
@@ -112,7 +161,7 @@ export async function runCli(argv) {
|
|
|
112
161
|
.option("--files-root <dir>", "將本機目錄對應為 NAS Logical Path")
|
|
113
162
|
.action(async (options, command) => {
|
|
114
163
|
const global = command.parent?.opts() ?? {};
|
|
115
|
-
const project =
|
|
164
|
+
const project = requireProject(process.cwd(), global.project);
|
|
116
165
|
const validation = validateProject(project);
|
|
117
166
|
if (!validation.ok) {
|
|
118
167
|
console.error(formatErrors(validation.errors));
|
|
@@ -146,7 +195,7 @@ export async function runCli(argv) {
|
|
|
146
195
|
.option("--dev", "檢查 Development Package Identity({package}.dev)")
|
|
147
196
|
.action(async (options, command) => {
|
|
148
197
|
const global = command.parent?.opts() ?? {};
|
|
149
|
-
const project =
|
|
198
|
+
const project = requireProject(process.cwd(), global.project);
|
|
150
199
|
if (options.host) {
|
|
151
200
|
const host = createNasHost({ host: options.host });
|
|
152
201
|
if (!host) {
|
|
@@ -189,9 +238,18 @@ export async function runCli(argv) {
|
|
|
189
238
|
.option("--replace-curated", "危險:將既有 Curated App Package Attestation 降級為 Local Attestation")
|
|
190
239
|
.option("--share <slot=name>", "此 NAS 上的 Shared Folder 邏輯名(契約 shared_folder 槽,可重複)", collectShareOption)
|
|
191
240
|
.option("--run-as <slot=name>", "此 NAS 上的 DSM 使用者邏輯名(契約 dsm_user 槽)", collectRunAsOption)
|
|
241
|
+
.option("--param <NAME=value>", "契約 string/boolean Install Parameter 答案(可重複)", collectParamOption)
|
|
192
242
|
.action(async (options, command) => {
|
|
193
243
|
const global = command.parent?.opts() ?? {};
|
|
194
|
-
const project =
|
|
244
|
+
const project = requireProject(process.cwd(), global.project);
|
|
245
|
+
const parameterCheck = resolveInstallParameterExtraValues({
|
|
246
|
+
contract: loadContract(project.contractPath).contract,
|
|
247
|
+
provided: options.param,
|
|
248
|
+
});
|
|
249
|
+
if (!parameterCheck.ok) {
|
|
250
|
+
console.error(`[${parameterCheck.code}] ${parameterCheck.message}`);
|
|
251
|
+
process.exit(1);
|
|
252
|
+
}
|
|
195
253
|
const host = createNasHost({ host: options.host });
|
|
196
254
|
if (!host) {
|
|
197
255
|
console.error("找不到 NAS 憑證。請設定 synoagentcli profile 或 DSM_URL/DSM_PASSWORD。");
|
|
@@ -210,6 +268,7 @@ export async function runCli(argv) {
|
|
|
210
268
|
replaceCurated: options.replaceCurated,
|
|
211
269
|
shareValues: options.share,
|
|
212
270
|
runAsValues: options.runAs,
|
|
271
|
+
paramValues: options.param,
|
|
213
272
|
});
|
|
214
273
|
if (!result.ok) {
|
|
215
274
|
console.error(`[${result.code ?? "DEPLOY_FAILED"}] ${result.message ?? "部署失敗。"}`);
|
|
@@ -232,7 +291,7 @@ export async function runCli(argv) {
|
|
|
232
291
|
.option("--dev", "查詢 Development Package Identity")
|
|
233
292
|
.action(async (options, command) => {
|
|
234
293
|
const global = command.parent?.opts() ?? {};
|
|
235
|
-
const project =
|
|
294
|
+
const project = requireProject(process.cwd(), global.project);
|
|
236
295
|
const host = createNasHost({ host: options.host });
|
|
237
296
|
if (!host) {
|
|
238
297
|
console.error("找不到 NAS 憑證。請設定 synoagentcli profile 或 DSM_URL/DSM_PASSWORD。");
|
|
@@ -254,7 +313,7 @@ export async function runCli(argv) {
|
|
|
254
313
|
.option("--dev", "查詢 Development Package Identity")
|
|
255
314
|
.action(async (options, command) => {
|
|
256
315
|
const global = command.parent?.opts() ?? {};
|
|
257
|
-
const project =
|
|
316
|
+
const project = requireProject(process.cwd(), global.project);
|
|
258
317
|
const host = createNasHost({ host: options.host });
|
|
259
318
|
if (!host) {
|
|
260
319
|
console.error("找不到 NAS 憑證。請設定 synoagentcli profile 或 DSM_URL/DSM_PASSWORD。");
|
|
@@ -312,7 +371,7 @@ export async function runCli(argv) {
|
|
|
312
371
|
.option("--arch <family>", "native App 的目標架構:x86_64 或 aarch64(Compose App 不需要)")
|
|
313
372
|
.action(async (options, command) => {
|
|
314
373
|
const global = command.parent?.opts() ?? {};
|
|
315
|
-
const project =
|
|
374
|
+
const project = requireProject(process.cwd(), global.project);
|
|
316
375
|
if (options.arch !== undefined && !isNativeArchFamily(String(options.arch))) {
|
|
317
376
|
console.error(`--arch 只接受 x86_64 或 aarch64,收到 ${String(options.arch)}。`);
|
|
318
377
|
process.exit(1);
|
|
@@ -336,7 +395,7 @@ export async function runCli(argv) {
|
|
|
336
395
|
.option("--base <branch>", "PR 目標分支", "master")
|
|
337
396
|
.action(async (options, command) => {
|
|
338
397
|
const global = command.parent?.opts() ?? {};
|
|
339
|
-
const project =
|
|
398
|
+
const project = requireProject(process.cwd(), global.project);
|
|
340
399
|
const preflight = await runPublicReleasePreflight(project);
|
|
341
400
|
if (!preflight.ok) {
|
|
342
401
|
// A gate's reasons belong on stderr so CI logs and `2>` capture them.
|
|
@@ -1,20 +1,61 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { type Document } from "yaml";
|
|
2
|
+
import type { AppContract, ComposeDocument, ComposeService, ValidationError } from "../types.js";
|
|
2
3
|
import { type PrivilegeFlag } from "./privilege.js";
|
|
3
4
|
export interface ParsedCompose {
|
|
4
5
|
compose: ComposeDocument;
|
|
5
6
|
source: string;
|
|
7
|
+
document: Document;
|
|
6
8
|
}
|
|
7
9
|
export declare function loadCompose(composePath: string): ParsedCompose | null;
|
|
8
10
|
export declare function listServiceNames(compose: ComposeDocument): string[];
|
|
9
11
|
export declare function extractPublishedTcpPorts(service: ComposeService): number[];
|
|
10
12
|
export declare function collectPublishedTcpPorts(compose: ComposeDocument): Map<number, string[]>;
|
|
13
|
+
export type ResourceWorkerIneligibleReason = "HAS_INSTALL_PARAMETERS" | "HAS_COMPOSE_INTERPOLATION" | "HAS_COMPOSE_SECRETS_OR_CONFIGS" | "HAS_BUILD_STANZA" | "HAS_PRIVILEGED_CAPABILITY" | "WEB_APP_MISSING_OR_AMBIGUOUS_ADMINPORT";
|
|
14
|
+
export interface ResourceWorkerEligibility {
|
|
15
|
+
eligible: boolean;
|
|
16
|
+
reasons: ResourceWorkerIneligibleReason[];
|
|
17
|
+
adminport?: number;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Checks if a Compose contract and document are eligible for the zero-config DSM resource-worker path.
|
|
21
|
+
* Zero-config slice rejects:
|
|
22
|
+
* - any install parameter
|
|
23
|
+
* - any ${...} interpolation anywhere in compose document
|
|
24
|
+
* - compose secrets or configs
|
|
25
|
+
* - build: stanzas
|
|
26
|
+
* - privileged / host network / device / root-like capabilities
|
|
27
|
+
* - any App without a unique literal published adminport status probe
|
|
28
|
+
*/
|
|
29
|
+
export declare function resourceWorkerEligibility(contract: AppContract, compose: ComposeDocument): ResourceWorkerEligibility;
|
|
30
|
+
export type ComposeRoute = "static-zero-config" | "dynamic" | "broker-installer" | "reject";
|
|
31
|
+
export interface ComposeRouteClassification {
|
|
32
|
+
route: ComposeRoute;
|
|
33
|
+
eligibility: ResourceWorkerEligibility;
|
|
34
|
+
errors: ValidationError[];
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Chooses the local route D0 can safely emit. The first dynamic slice is
|
|
38
|
+
* deliberately small: only string/boolean wizard answers interpolated as
|
|
39
|
+
* ordinary Compose values. Secrets/configs remain rejected because the live
|
|
40
|
+
* DSM worker does not load docker-compose.override.yml, and no secret may be
|
|
41
|
+
* placed in the project .env.
|
|
42
|
+
*/
|
|
43
|
+
export declare function classifyComposeRoute(contract: AppContract, parsed: ParsedCompose, composeFile?: string): ComposeRouteClassification;
|
|
44
|
+
export declare function brokerInstallerRouteWarning(compose: ComposeDocument, composeFile?: string): ValidationError;
|
|
11
45
|
export declare function collectComposeInterpolations(compose: ComposeDocument): Set<string>;
|
|
12
46
|
export declare function validateComposeSecurity(compose: ComposeDocument, composeFile: string, options: {
|
|
13
47
|
profile: "personal" | "listing";
|
|
14
48
|
declaredPrivilege: ReadonlySet<PrivilegeFlag>;
|
|
15
49
|
}): ValidationError[];
|
|
50
|
+
/**
|
|
51
|
+
* DSM's Docker Project resource worker can lose YAML quotes while acquiring a
|
|
52
|
+
* project. A quoted numeric command argument can therefore become a number;
|
|
53
|
+
* reject that platform-invalid shape before it reaches the NAS.
|
|
54
|
+
*/
|
|
55
|
+
export declare function validateComposeNumericCommandArgs(compose: ComposeDocument, composeFile: string): ValidationError[];
|
|
16
56
|
export interface HostBindContext {
|
|
17
57
|
sharedFolderParams: ReadonlySet<string>;
|
|
58
|
+
homesParams?: ReadonlySet<string>;
|
|
18
59
|
installParamNames: ReadonlySet<string>;
|
|
19
60
|
}
|
|
20
61
|
export declare function collectVolumeSourceInterpolations(compose: ComposeDocument): Set<string>;
|