@synopackageland/cli 0.13.1 → 0.15.0
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/CHANGELOG.md +18 -0
- package/README.md +44 -1
- package/dist/build-spk/generator.d.ts +3 -2
- package/dist/build-spk/generator.js +6 -850
- package/dist/build-spk/resource-worker.d.ts +2 -14
- package/dist/build-spk/resource-worker.js +3 -166
- package/dist/cli.js +208 -56
- package/dist/compose/analyze.d.ts +3 -61
- package/dist/compose/analyze.js +35 -776
- package/dist/compose/env.d.ts +5 -3
- package/dist/compose/env.js +17 -25
- package/dist/compose/runner.js +23 -6
- package/dist/contract/app-launch.d.ts +11 -0
- package/dist/contract/app-launch.js +18 -0
- package/dist/contract/web-launch.d.ts +3 -13
- package/dist/contract/web-launch.js +14 -81
- package/dist/ctl/cli.d.ts +36 -0
- package/dist/ctl/cli.js +218 -0
- package/dist/ctl/descriptor.d.ts +81 -0
- package/dist/ctl/descriptor.js +93 -0
- package/dist/ctl/dsm-auth.d.ts +10 -0
- package/dist/ctl/dsm-auth.js +30 -0
- package/dist/ctl/errors.d.ts +17 -0
- package/dist/ctl/errors.js +32 -0
- package/dist/ctl/flows.d.ts +11 -0
- package/dist/ctl/flows.js +52 -0
- package/dist/ctl/input.d.ts +20 -0
- package/dist/ctl/input.js +128 -0
- package/dist/ctl/json.d.ts +2 -0
- package/dist/ctl/json.js +38 -0
- package/dist/ctl/redaction.d.ts +15 -0
- package/dist/ctl/redaction.js +198 -0
- package/dist/ctl/service.d.ts +38 -0
- package/dist/ctl/service.js +156 -0
- package/dist/ctl/store.d.ts +51 -0
- package/dist/ctl/store.js +93 -0
- package/dist/ctl/transfers.d.ts +14 -0
- package/dist/ctl/transfers.js +74 -0
- package/dist/ctl/transport.d.ts +11 -0
- package/dist/ctl/transport.js +79 -0
- package/dist/deploy/deploy.d.ts +4 -3
- package/dist/deploy/deploy.js +5 -210
- package/dist/deploy/plan.js +26 -7
- package/dist/dev.js +5 -4
- package/dist/error-registry.js +1020 -831
- package/dist/info.js +120 -28
- package/dist/init.js +188 -64
- package/dist/inspect.js +21 -6
- package/dist/runtime/executor-digests.json +1 -12
- package/dist/skill.js +51 -9
- package/dist/standalone/api-description-inspect.d.ts +4 -0
- package/dist/standalone/api-description-inspect.js +12 -0
- package/dist/standalone/api-description.d.ts +7 -0
- package/dist/standalone/api-description.js +73 -0
- package/dist/standalone/build.d.ts +3 -0
- package/dist/standalone/build.js +140 -33
- package/dist/standalone/compose-errors.d.ts +3 -0
- package/dist/standalone/compose-errors.js +243 -0
- package/dist/standalone/compose.d.ts +97 -10
- package/dist/standalone/compose.js +306 -75
- package/dist/standalone/daemon.js +111 -29
- package/dist/standalone/dsm-window.js +55 -9
- package/dist/standalone/identity-gateway.d.ts +9 -2
- package/dist/standalone/identity-gateway.js +282 -66
- package/dist/standalone/inspect-spk.d.ts +15 -0
- package/dist/standalone/inspect-spk.js +714 -128
- package/dist/standalone/lifecycle.js +124 -42
- package/dist/standalone/manifest.d.ts +10 -1
- package/dist/standalone/manifest.js +308 -87
- package/dist/standalone/resource.js +6 -2
- package/dist/standalone/spk.d.ts +4 -1
- package/dist/standalone/spk.js +139 -39
- package/dist/template-paths.d.ts +0 -2
- package/dist/template-paths.js +0 -5
- package/dist/test-command.d.ts +1 -1
- package/dist/test-command.js +1 -1
- package/dist/validate.d.ts +3 -3
- package/dist/validate.js +32 -140
- package/package.json +5 -3
- package/schema/synology-app.schema.json +1 -1
- package/schema/synopkgland-api-v1.schema.json +423 -0
- package/skills/SKILL.md +34 -1
- package/skills/reference/compose.md +349 -0
- package/skills/reference/create.md +26 -4
- package/skills/reference/ctl.md +41 -0
- package/skills/reference/deploy.md +1 -1
- package/skills/reference/files.md +4 -0
- package/skills/reference/identity-gateway.md +8 -138
- package/skills/reference/portal.md +31 -0
- package/skills/reference/ui-vue.md +31 -0
- package/skills/reference/wrap.md +10 -30
- package/assets/go/daemon/fixture_linux_test.go +0 -15
- package/assets/go/daemon/fixture_other_test.go +0 -7
- package/assets/go/daemon/go.mod +0 -5
- package/assets/go/daemon/main.go +0 -636
- package/assets/go/daemon/main_test.go +0 -315
- package/assets/go/daemon/network.go +0 -107
- package/assets/go/daemon/network_test.go +0 -86
- package/assets/go/daemon/process_linux.go +0 -55
- package/assets/go/daemon/process_linux_test.go +0 -161
- package/assets/go/daemon/process_other.go +0 -14
- package/assets/go/daemon/testdata/child.go +0 -44
- package/assets/go/gateway/auth.go +0 -101
- package/assets/go/gateway/auth_test.go +0 -216
- package/assets/go/gateway/cgi.go +0 -133
- package/assets/go/gateway/cgi_test.go +0 -156
- package/assets/go/gateway/csrf.go +0 -73
- package/assets/go/gateway/csrf_test.go +0 -155
- package/assets/go/gateway/directory.go +0 -108
- package/assets/go/gateway/directory_test.go +0 -202
- package/assets/go/gateway/gateway.go +0 -412
- package/assets/go/gateway/gateway_test.go +0 -676
- package/assets/go/gateway/go.mod +0 -5
- package/assets/go/gateway/groups.go +0 -66
- package/assets/go/gateway/main.go +0 -45
- package/dist/build-spk/gateway.d.ts +0 -31
- package/dist/build-spk/gateway.js +0 -60
- package/dist/compose/privilege.d.ts +0 -14
- package/dist/compose/privilege.js +0 -102
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { readFileSync } from 'node:fs';
|
|
2
|
+
import { goChildEnv } from '../runtime/go-env.js';
|
|
3
|
+
import { makeDescriptor, validateDescriptor, flowMethods } from '../ctl/descriptor.js';
|
|
4
|
+
import { sdkBackend } from './manifest.js';
|
|
5
|
+
import { readApiPermissions } from './api-permissions.js';
|
|
6
|
+
import { isBridgeBackend } from './bridge-backend.js';
|
|
7
|
+
import { StandaloneBuildError } from './spk.js';
|
|
8
|
+
import { join } from 'node:path';
|
|
9
|
+
export async function buildApiDescription(m, bridgeMethods, run) {
|
|
10
|
+
if (!m.ctl?.enabled)
|
|
11
|
+
return undefined;
|
|
12
|
+
if (m.backend.kind === 'binary' || m.compose)
|
|
13
|
+
throw new StandaloneBuildError('CTL_UNSUPPORTED_BACKEND', 'Automatic API descriptions require a standard SDK native App.');
|
|
14
|
+
let methods;
|
|
15
|
+
const policy = readApiPermissions(sdkBackend(m), m.capabilities);
|
|
16
|
+
if (isBridgeBackend(m.backend)) {
|
|
17
|
+
methods = [...(bridgeMethods ?? []), ...Object.keys(m.apiPermissions?.methods ?? {}).filter(name => Object.values(policy.sdk).some(group => Object.hasOwn(group, name)))].map(method => ({ method, permission: m.apiPermissions?.methods[method] ?? 'administrators' }));
|
|
18
|
+
}
|
|
19
|
+
else {
|
|
20
|
+
if (!readFileSync(join(m.backend.dir, m.backend.main, 'main.go'), 'utf8').includes('--describe'))
|
|
21
|
+
throw new StandaloneBuildError('CTL_DESCRIBE_REQUIRED', 'Go CGI must support build-only --describe; update its template.');
|
|
22
|
+
const flags = [`-X main.packageID=${m.package}`, `-X main.version=${m.spkVersion}`, ...(m.capabilities.files || m.capabilities.delegate ? ['-X main.fileStation=session'] : []), ...(m.portal ? ['-X main.portal=on'] : [])].join(' ');
|
|
23
|
+
const response = await run('go', ['run', '-trimpath', '-buildvcs=false', '-ldflags', flags, m.backend.main, '--describe'], { cwd: m.backend.dir, env: goChildEnv({ ...process.env, GOOS: undefined, GOARCH: undefined, GOARM: undefined, CGO_ENABLED: '0' }), capture: true });
|
|
24
|
+
if (response.status !== 0)
|
|
25
|
+
throw new StandaloneBuildError('CTL_DESCRIBE_FAILED', 'Go route metadata export failed.');
|
|
26
|
+
try {
|
|
27
|
+
methods = JSON.parse(response.stdout ?? '');
|
|
28
|
+
}
|
|
29
|
+
catch {
|
|
30
|
+
throw new StandaloneBuildError('CTL_DESCRIBE_FAILED', 'Go route exporter returned invalid JSON.');
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
if (!Array.isArray(methods) || methods.some(item => typeof item.method !== 'string' || typeof item.permission !== 'string'))
|
|
34
|
+
throw new StandaloneBuildError('CTL_DESCRIBE_FAILED', 'Invalid registry snapshot.');
|
|
35
|
+
const registry = new Map(methods.map(item => [item.method, item.permission]));
|
|
36
|
+
// Identity transport routes are outside the business registry but have immutable SDK ownership.
|
|
37
|
+
if (m.capabilities.identity) {
|
|
38
|
+
registry.set('identity.session', 'public');
|
|
39
|
+
registry.set('identity.logout', 'public');
|
|
40
|
+
}
|
|
41
|
+
const bindings = {};
|
|
42
|
+
const commands = {};
|
|
43
|
+
const weights = { read: 0, write: 1, destructive: 2 };
|
|
44
|
+
for (const [id, declared] of Object.entries(m.ctl.commands)) {
|
|
45
|
+
const command = structuredClone(declared);
|
|
46
|
+
const names = flowMethods(command.flow);
|
|
47
|
+
let permission = 'public';
|
|
48
|
+
for (const name of names) {
|
|
49
|
+
if (!registry.has(name))
|
|
50
|
+
throw new StandaloneBuildError('CTL_COMMAND_UNREGISTERED', `Command ${id} references unregistered method ${name}.`);
|
|
51
|
+
const effective = m.entry === 'admin-only' ? 'administrators' : registry.get(name);
|
|
52
|
+
registry.set(name, effective);
|
|
53
|
+
const effect = command.flow.type === 'job' && name !== command.flow.start ? 'read' : command.effect;
|
|
54
|
+
const previous = bindings[name];
|
|
55
|
+
if (!previous || weights[effect] > weights[previous.effect])
|
|
56
|
+
bindings[name] = { permission: effective, effect };
|
|
57
|
+
if (effective === 'administrators' || effective === 'authenticated' && permission === 'public')
|
|
58
|
+
permission = effective;
|
|
59
|
+
}
|
|
60
|
+
commands[id] = { ...command, permission };
|
|
61
|
+
}
|
|
62
|
+
if (m.capabilities.identity) {
|
|
63
|
+
for (const method of ['identity.session', 'identity.logout'])
|
|
64
|
+
bindings[method] = { permission: 'public', effect: 'write' };
|
|
65
|
+
}
|
|
66
|
+
const description = makeDescriptor({ package: { id: m.package, version: m.spkVersion, displayName: m.displayname }, aliases: m.ctl.aliases ?? [m.package.toLowerCase()], provenance: { developedBy: m.ctl.developedBy ?? 'unknown', generator: 'synopkgland-cli' }, entry: m.entry ?? 'policy', transport: { path: `/webman/3rdparty/${m.package}/cgi/api.cgi`, auth: m.capabilities.identity ? 'dsm-app-session' : 'dsm-session' }, commands, bindings });
|
|
67
|
+
try {
|
|
68
|
+
return { description: validateDescriptor(description, m.package, m.spkVersion), registry: Object.fromEntries(registry) };
|
|
69
|
+
}
|
|
70
|
+
catch (error) {
|
|
71
|
+
throw new StandaloneBuildError('CTL_DESCRIPTOR_INVALID', error.message);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
@@ -23,6 +23,9 @@ export interface StandaloneBuildOptions {
|
|
|
23
23
|
/** Architectures built at once; defaults to 1. */
|
|
24
24
|
jobs?: number;
|
|
25
25
|
run?: CommandRunner;
|
|
26
|
+
/** Saved anonymous image manifests/config for an offline Compose rebuild. */
|
|
27
|
+
composeImageProofs?: Record<string, unknown>;
|
|
28
|
+
composeImageProofsByArch?: Partial<Record<NativeArchFamily, Record<string, unknown>>>;
|
|
26
29
|
}
|
|
27
30
|
/** Argv only, never a shell; output streams to the terminal except stderr, which is also kept, and captured stdout. */
|
|
28
31
|
export declare const runCommand: CommandRunner;
|
package/dist/standalone/build.js
CHANGED
|
@@ -1,14 +1,16 @@
|
|
|
1
|
+
import { buildApiDescription } from "./api-description.js";
|
|
1
2
|
import { goChildEnv } from "../runtime/go-env.js";
|
|
2
3
|
import { spawn } from "node:child_process";
|
|
3
|
-
import { goModulePath, privateSdkHint, SDK_MODULE, sdkSource } from "../runtime/sdk-source.js";
|
|
4
|
+
import { goModulePath, privateSdkHint, SDK_MODULE, sdkSource, } from "../runtime/sdk-source.js";
|
|
4
5
|
import { existsSync, mkdirSync, mkdtempSync, rmSync } from "node:fs";
|
|
5
6
|
import { tmpdir } from "node:os";
|
|
6
7
|
import { join } from "node:path";
|
|
7
8
|
import { goBuildEnv } from "../build-spk/dsm-arch.js";
|
|
8
|
-
import { resolveMinDsm, DEFAULT_MIN_DSM, isDsmAtLeast, PORTAL_EXECUTOR_MIN_DSM, portalByExecutor, portalMinDsmMessage } from "../build-spk/dsm-version.js";
|
|
9
|
+
import { resolveMinDsm, DEFAULT_MIN_DSM, isDsmAtLeast, PORTAL_EXECUTOR_MIN_DSM, portalByExecutor, portalMinDsmMessage, } from "../build-spk/dsm-version.js";
|
|
9
10
|
import { checkCgiBinary } from "./binary.js";
|
|
10
|
-
import { loadStandaloneManifest } from "./manifest.js";
|
|
11
|
+
import { loadStandaloneManifest, } from "./manifest.js";
|
|
11
12
|
import { assembleStandaloneSpk, StandaloneBuildError } from "./spk.js";
|
|
13
|
+
import { buildComposeControl, lockComposeImages, dsmComposeProblem, } from "./compose.js";
|
|
12
14
|
import { buildDaemonControl } from "./daemon.js";
|
|
13
15
|
import { buildIdentityGateway } from "./identity-gateway.js";
|
|
14
16
|
import { buildExecutor } from "../runtime/executor-build.js";
|
|
@@ -17,7 +19,7 @@ import { readApiPermissions } from "./api-permissions.js";
|
|
|
17
19
|
import { checkJobs, commitOutputs, forEachArch } from "./arch-jobs.js";
|
|
18
20
|
import { isBridgeBackend } from "./bridge-backend.js";
|
|
19
21
|
import { requestTimeoutSeconds } from "./request-timeout.js";
|
|
20
|
-
import { buildBridge, prepareBridge } from "./bridge-build.js";
|
|
22
|
+
import { buildBridge, prepareBridge, } from "./bridge-build.js";
|
|
21
23
|
import { portalCatalogs } from "./portal-window.js";
|
|
22
24
|
/** Argv only, never a shell; output streams to the terminal except stderr, which is also kept, and captured stdout. */
|
|
23
25
|
export const runCommand = (command, args, options) => new Promise((done) => {
|
|
@@ -29,24 +31,37 @@ export const runCommand = (command, args, options) => new Promise((done) => {
|
|
|
29
31
|
let stderr = "";
|
|
30
32
|
let stdout = "";
|
|
31
33
|
child.stdout?.setEncoding("utf8");
|
|
32
|
-
child.stdout?.on("data", (chunk) => {
|
|
34
|
+
child.stdout?.on("data", (chunk) => {
|
|
35
|
+
stdout += chunk;
|
|
36
|
+
});
|
|
33
37
|
child.stderr.setEncoding("utf8");
|
|
34
38
|
child.stderr.on("data", (chunk) => {
|
|
35
39
|
stderr += chunk;
|
|
36
40
|
process.stderr.write(chunk);
|
|
37
41
|
});
|
|
38
42
|
child.on("error", (error) => done({ status: null, stderr, missing: error.code === "ENOENT" }));
|
|
39
|
-
child.on("close", (status) => done({
|
|
43
|
+
child.on("close", (status) => done({
|
|
44
|
+
status,
|
|
45
|
+
stderr,
|
|
46
|
+
missing: false,
|
|
47
|
+
...(options.capture ? { stdout } : {}),
|
|
48
|
+
}));
|
|
40
49
|
});
|
|
41
50
|
/** `runCommand` for concurrent builds: every output line goes to stderr behind `prefix`, so architectures stay apart. */
|
|
42
51
|
export function prefixedRunner(prefix) {
|
|
43
52
|
return (command, args, options) => new Promise((done) => {
|
|
44
|
-
const child = spawn(command, args, {
|
|
53
|
+
const child = spawn(command, args, {
|
|
54
|
+
cwd: options.cwd,
|
|
55
|
+
env: options.env,
|
|
56
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
57
|
+
});
|
|
45
58
|
let stderr = "";
|
|
46
59
|
let stdout = "";
|
|
47
60
|
if (options.capture) {
|
|
48
61
|
child.stdout.setEncoding("utf8");
|
|
49
|
-
child.stdout.on("data", (chunk) => {
|
|
62
|
+
child.stdout.on("data", (chunk) => {
|
|
63
|
+
stdout += chunk;
|
|
64
|
+
});
|
|
50
65
|
}
|
|
51
66
|
const relay = (stream, keep) => {
|
|
52
67
|
let partial = "";
|
|
@@ -59,14 +74,21 @@ export function prefixedRunner(prefix) {
|
|
|
59
74
|
for (const line of lines)
|
|
60
75
|
process.stderr.write(`${prefix}${line}\n`);
|
|
61
76
|
});
|
|
62
|
-
stream.on("end", () => {
|
|
63
|
-
|
|
77
|
+
stream.on("end", () => {
|
|
78
|
+
if (partial)
|
|
79
|
+
process.stderr.write(`${prefix}${partial}\n`);
|
|
80
|
+
});
|
|
64
81
|
};
|
|
65
82
|
if (!options.capture)
|
|
66
83
|
relay(child.stdout, false);
|
|
67
84
|
relay(child.stderr, true);
|
|
68
85
|
child.on("error", (error) => done({ status: null, stderr, missing: error.code === "ENOENT" }));
|
|
69
|
-
child.on("close", (status) => done({
|
|
86
|
+
child.on("close", (status) => done({
|
|
87
|
+
status,
|
|
88
|
+
stderr,
|
|
89
|
+
missing: false,
|
|
90
|
+
...(options.capture ? { stdout } : {}),
|
|
91
|
+
}));
|
|
70
92
|
});
|
|
71
93
|
}
|
|
72
94
|
/** The runner one architecture uses: the caller's, or a labelled one while several architectures share the terminal. */
|
|
@@ -83,14 +105,19 @@ export async function buildStandalone(options) {
|
|
|
83
105
|
const manifest = options.minDsm === undefined
|
|
84
106
|
? loaded.manifest
|
|
85
107
|
: { ...loaded.manifest, os_min_ver: resolveMinDsm(options.minDsm) };
|
|
86
|
-
if (manifest.capabilities.rootOps === true &&
|
|
108
|
+
if (manifest.capabilities.rootOps === true &&
|
|
109
|
+
!isDsmAtLeast(manifest.os_min_ver, DEFAULT_MIN_DSM)) {
|
|
87
110
|
throw new StandaloneBuildError("STANDALONE_OS_MIN_VER_INVALID", `root templates require DSM ${DEFAULT_MIN_DSM} or later.`);
|
|
88
111
|
}
|
|
89
|
-
if (manifest.portal &&
|
|
112
|
+
if (manifest.portal &&
|
|
113
|
+
!isDsmAtLeast(manifest.os_min_ver, PORTAL_EXECUTOR_MIN_DSM)) {
|
|
90
114
|
throw new StandaloneBuildError("STANDALONE_PORTAL_MIN_DSM", `[STANDALONE_PORTAL_MIN_DSM] ${portalMinDsmMessage(manifest.os_min_ver, "portal")}`);
|
|
91
115
|
}
|
|
92
116
|
if (manifest.compose && !isDsmAtLeast(manifest.os_min_ver, "7.2.1-69057"))
|
|
93
117
|
throw new StandaloneBuildError("STANDALONE_IDENTITY_GATEWAY_INVALID", "compose identity_gateway requires DSM >= 7.2.1.");
|
|
118
|
+
const composeProblem = manifest.compose && dsmComposeProblem(manifest.compose);
|
|
119
|
+
if (composeProblem)
|
|
120
|
+
throw new StandaloneBuildError(composeProblem.code, composeProblem.message);
|
|
94
121
|
const jobs = checkJobs(options.jobs);
|
|
95
122
|
const arches = options.arches ?? manifest.arches;
|
|
96
123
|
for (const arch of arches) {
|
|
@@ -104,20 +131,42 @@ export async function buildStandalone(options) {
|
|
|
104
131
|
const warnings = new Set();
|
|
105
132
|
try {
|
|
106
133
|
const bridge = await prepareBridge(manifest, work);
|
|
134
|
+
const apiArtifact = await buildApiDescription(manifest, bridge?.methods, options.run ?? runCommand);
|
|
135
|
+
const apiDescription = apiArtifact?.description;
|
|
136
|
+
const sourceManifest = manifest;
|
|
107
137
|
const outputs = await commitOutputs(outDir, (stage) => forEachArch(arches, jobs, async (arch) => {
|
|
138
|
+
const manifest = lockComposeImages(sourceManifest, arch, true, options.composeImageProofsByArch?.[arch] ??
|
|
139
|
+
options.composeImageProofs);
|
|
108
140
|
const run = runnerFor(options.run, jobs, arch);
|
|
109
141
|
const archWork = join(work, arch);
|
|
110
142
|
mkdirSync(archWork);
|
|
111
143
|
const executor = await buildExecutor(manifest, arch, archWork, run);
|
|
112
|
-
executor?.warnings.forEach(warning => warnings.add(warning));
|
|
144
|
+
executor?.warnings.forEach((warning) => warnings.add(warning));
|
|
113
145
|
const builtManifest = executor ? { ...manifest, executor } : manifest;
|
|
114
|
-
const { cgi: cgiPath, worker: backgroundWorker, app } = await backendFor(builtManifest, arch, archWork, run, bridge);
|
|
146
|
+
const { cgi: cgiPath, worker: backgroundWorker, app, } = await backendFor(builtManifest, arch, archWork, run, bridge);
|
|
115
147
|
const outputPath = join(stage, `${manifest.package}-${manifest.spkVersion}-${arch}.spk`);
|
|
116
148
|
const daemonControl = await buildDaemonControl(manifest, arch, archWork, run);
|
|
117
149
|
const identityGateway = await buildIdentityGateway(manifest, arch, archWork, run);
|
|
150
|
+
const composeControl = await buildComposeControl(manifest, arch, archWork, run);
|
|
118
151
|
// A portal needs the executor, so the executor's SDK carries the panel text.
|
|
119
|
-
const portalText = manifest.portal && executor
|
|
120
|
-
|
|
152
|
+
const portalText = manifest.portal && executor
|
|
153
|
+
? portalCatalogs(sdkSource(executor.sdk).goDir)
|
|
154
|
+
: undefined;
|
|
155
|
+
await assembleStandaloneSpk({
|
|
156
|
+
manifest: builtManifest,
|
|
157
|
+
arch,
|
|
158
|
+
cgiPath,
|
|
159
|
+
outputPath,
|
|
160
|
+
daemonControl,
|
|
161
|
+
identityGateway,
|
|
162
|
+
composeControl,
|
|
163
|
+
backgroundWorker,
|
|
164
|
+
executor,
|
|
165
|
+
app,
|
|
166
|
+
portalText,
|
|
167
|
+
apiDescription,
|
|
168
|
+
apiRegistry: apiArtifact?.registry,
|
|
169
|
+
});
|
|
121
170
|
return outputPath;
|
|
122
171
|
}));
|
|
123
172
|
return { outputs, warnings: [...warnings] };
|
|
@@ -132,9 +181,14 @@ async function buildUi(manifest, run) {
|
|
|
132
181
|
return;
|
|
133
182
|
}
|
|
134
183
|
const [command, ...args] = build.command;
|
|
135
|
-
const result = await run(command, args, {
|
|
184
|
+
const result = await run(command, args, {
|
|
185
|
+
cwd: build.cwd,
|
|
186
|
+
env: { ...process.env },
|
|
187
|
+
});
|
|
136
188
|
if (result.missing || result.status !== 0) {
|
|
137
|
-
const installHint = command === "npm" &&
|
|
189
|
+
const installHint = command === "npm" &&
|
|
190
|
+
existsSync(join(build.cwd, "package.json")) &&
|
|
191
|
+
!existsSync(join(build.cwd, "node_modules"))
|
|
138
192
|
? `\nThe UI dependencies are not installed. Run npm ${existsSync(join(build.cwd, "package-lock.json")) ? "ci" : "install"} in ${build.cwd}, then run build again.`
|
|
139
193
|
: "";
|
|
140
194
|
throw new StandaloneBuildError("STANDALONE_UI_BUILD_FAILED", `UI build (${build.command.join(" ")}) failed. ${installHint}\n${result.stderr}`);
|
|
@@ -143,19 +197,29 @@ async function buildUi(manifest, run) {
|
|
|
143
197
|
/** The checked CGI of one architecture and, with capabilities.jobs, its fixed worker; or the runtime and App of a bridge App. */
|
|
144
198
|
async function backendFor(manifest, arch, work, run, bridge) {
|
|
145
199
|
const backend = manifest.backend;
|
|
200
|
+
if (manifest.compose)
|
|
201
|
+
return {};
|
|
146
202
|
if (isBridgeBackend(backend))
|
|
147
203
|
return buildBridge(manifest, bridge, arch, work, run);
|
|
148
|
-
const cgi = backend.kind === "binary"
|
|
204
|
+
const cgi = backend.kind === "binary"
|
|
205
|
+
? backend.files[arch]
|
|
206
|
+
: await buildGoCgi(manifest, backend, arch, work, run);
|
|
149
207
|
const problem = checkCgiBinary(backend.kind === "go" ? work : manifest.root, cgi, arch);
|
|
150
208
|
if (problem) {
|
|
151
209
|
throw new StandaloneBuildError(problem.code, problem.message);
|
|
152
210
|
}
|
|
153
|
-
return {
|
|
211
|
+
return {
|
|
212
|
+
cgi,
|
|
213
|
+
worker: await buildBackgroundWorker(manifest, arch, work, run),
|
|
214
|
+
};
|
|
154
215
|
}
|
|
155
216
|
async function buildGoCgi(manifest, backend, arch, work, run) {
|
|
156
217
|
// UI build hooks run after validation. Refuse policy changes rather than
|
|
157
218
|
// compiling CGI permissions different from the launcher metadata snapshot.
|
|
158
|
-
if (readApiPermissions(backend, manifest.capabilities).digest !==
|
|
219
|
+
if (readApiPermissions(backend, manifest.capabilities).digest !==
|
|
220
|
+
manifest.apiPermissionsDigest ||
|
|
221
|
+
readApiPermissions(backend, manifest.capabilities).sdkDigest !==
|
|
222
|
+
manifest.apiSDKPermissionsDigest) {
|
|
159
223
|
throw new StandaloneBuildError("STANDALONE_API_PERMISSIONS_CHANGED", "API permissions changed during build; check and rebuild.");
|
|
160
224
|
}
|
|
161
225
|
const module = goModulePath(backend.dir);
|
|
@@ -172,13 +236,35 @@ async function buildGoCgi(manifest, backend, arch, work, run) {
|
|
|
172
236
|
`-X ${module}/internal/api.expectedPermissionsDigest=${manifest.apiPermissionsDigest}`,
|
|
173
237
|
`-X ${SDK_MODULE}/api.expectedSDKPermissionsDigest=${manifest.apiSDKPermissionsDigest}`,
|
|
174
238
|
...delegateBuildFlags(manifest),
|
|
175
|
-
...(manifest.executor
|
|
176
|
-
|
|
177
|
-
|
|
239
|
+
...(manifest.executor
|
|
240
|
+
? [
|
|
241
|
+
`-X main.executorDigest=${manifest.executor.executorDigest}`,
|
|
242
|
+
`-X main.policyDigest=${manifest.executor.policyDigest}`,
|
|
243
|
+
]
|
|
244
|
+
: []),
|
|
245
|
+
...(manifest.capabilities.identity
|
|
246
|
+
? [`-X main.identity=${manifest.capabilities.identity}`]
|
|
247
|
+
: []),
|
|
248
|
+
...(manifest.capabilities.files || manifest.capabilities.delegate
|
|
249
|
+
? [`-X main.fileStation=session`]
|
|
250
|
+
: []),
|
|
178
251
|
...(manifest.entry ? [`-X main.entry=${manifest.entry}`] : []),
|
|
179
|
-
...(manifest.portal
|
|
252
|
+
...(manifest.portal
|
|
253
|
+
? [
|
|
254
|
+
`-X main.portal=${portalByExecutor(manifest.os_min_ver) ? "executor" : "on"}`,
|
|
255
|
+
]
|
|
256
|
+
: []),
|
|
180
257
|
].join(" ");
|
|
181
|
-
const result = await run("go", [
|
|
258
|
+
const result = await run("go", [
|
|
259
|
+
"build",
|
|
260
|
+
"-trimpath",
|
|
261
|
+
"-buildvcs=false",
|
|
262
|
+
"-ldflags",
|
|
263
|
+
ldflags,
|
|
264
|
+
"-o",
|
|
265
|
+
out,
|
|
266
|
+
backend.main,
|
|
267
|
+
], {
|
|
182
268
|
cwd: backend.dir,
|
|
183
269
|
env: goChildEnv({ ...process.env, ...goBuildEnv(arch) }),
|
|
184
270
|
});
|
|
@@ -186,7 +272,9 @@ async function buildGoCgi(manifest, backend, arch, work, run) {
|
|
|
186
272
|
throw new StandaloneBuildError("STANDALONE_GO_MISSING", "The go command was not found. Install Go 1.25 or later (https://go.dev/dl/), or package an existing executable with backend.binary.");
|
|
187
273
|
}
|
|
188
274
|
if (result.status !== 0) {
|
|
189
|
-
const fetchingSdk = result.stderr.includes(`${SDK_MODULE}@`)
|
|
275
|
+
const fetchingSdk = result.stderr.includes(`${SDK_MODULE}@`)
|
|
276
|
+
? privateSdkHint()
|
|
277
|
+
: "";
|
|
190
278
|
throw new StandaloneBuildError("STANDALONE_GO_BUILD_FAILED", `go build (${arch}) failed.\n${result.stderr}${fetchingSdk}`);
|
|
191
279
|
}
|
|
192
280
|
return out;
|
|
@@ -196,10 +284,29 @@ async function buildBackgroundWorker(manifest, arch, work, run) {
|
|
|
196
284
|
if (!manifest.capabilities.jobs || manifest.backend.kind !== "go")
|
|
197
285
|
return undefined;
|
|
198
286
|
const out = join(work, `background-worker-${arch}`);
|
|
199
|
-
const ldflags = [
|
|
200
|
-
|
|
287
|
+
const ldflags = [
|
|
288
|
+
"-s",
|
|
289
|
+
"-w",
|
|
290
|
+
"-buildid=",
|
|
291
|
+
`-X main.packageID=${manifest.package}`,
|
|
292
|
+
...delegateBuildFlags(manifest),
|
|
293
|
+
...(manifest.executor
|
|
294
|
+
? [
|
|
295
|
+
`-X main.executorDigest=${manifest.executor.executorDigest}`,
|
|
296
|
+
`-X main.policyDigest=${manifest.executor.policyDigest}`,
|
|
297
|
+
]
|
|
298
|
+
: []),
|
|
201
299
|
].join(" ");
|
|
202
|
-
const result = await run("go", [
|
|
300
|
+
const result = await run("go", [
|
|
301
|
+
"build",
|
|
302
|
+
"-trimpath",
|
|
303
|
+
"-buildvcs=false",
|
|
304
|
+
"-ldflags",
|
|
305
|
+
ldflags,
|
|
306
|
+
"-o",
|
|
307
|
+
out,
|
|
308
|
+
BACKGROUND_WORKER_MAIN,
|
|
309
|
+
], {
|
|
203
310
|
cwd: manifest.backend.dir,
|
|
204
311
|
env: goChildEnv({ ...process.env, ...goBuildEnv(arch) }),
|
|
205
312
|
});
|
|
@@ -219,7 +326,7 @@ function delegateBuildFlags(manifest) {
|
|
|
219
326
|
delegateScope: d.scope,
|
|
220
327
|
delegateOperationsJSON: JSON.stringify([...d.operations].sort()),
|
|
221
328
|
delegateDestinationsJSON: JSON.stringify(d.destinations ? [...d.destinations].sort() : []),
|
|
222
|
-
delegateLifetime: d.lifetime ??
|
|
329
|
+
delegateLifetime: d.lifetime ?? "",
|
|
223
330
|
};
|
|
224
331
|
// Go's quoted.Split does not process backslash escapes. Interior JSON quotes
|
|
225
332
|
// in a whitespace-free argument are literal; never shell-escape this string.
|
|
@@ -231,6 +338,6 @@ function delegateBuildFlags(manifest) {
|
|
|
231
338
|
return `-X '${arg}'`;
|
|
232
339
|
if (!arg.includes('"'))
|
|
233
340
|
return `-X "${arg}"`;
|
|
234
|
-
throw new StandaloneBuildError(
|
|
341
|
+
throw new StandaloneBuildError("STANDALONE_GO_BUILD_FAILED", "Delegate scope with whitespace and both quote types cannot be represented in Go linker flags.");
|
|
235
342
|
});
|
|
236
343
|
}
|
|
@@ -0,0 +1,243 @@
|
|
|
1
|
+
/** SDK planner errors accepted at this versioned tool boundary. */
|
|
2
|
+
export const COMPOSE_ERRORS = {
|
|
3
|
+
COMPOSE_SUBNET_STATE_INVALID: {
|
|
4
|
+
code: "COMPOSE_SUBNET_STATE_INVALID",
|
|
5
|
+
description: "The private ingress history is invalid or has foreign ownership.",
|
|
6
|
+
suggestion: "Inspect the package private state; do not bypass route checks or claim network ownership.",
|
|
7
|
+
},
|
|
8
|
+
COMPOSE_SUBNET_INVALID: {
|
|
9
|
+
code: "COMPOSE_SUBNET_INVALID",
|
|
10
|
+
description: "Invalid ingress subnet candidate.",
|
|
11
|
+
suggestion: "Set compose.network.ingress_candidate to an integer from 0 through 255.",
|
|
12
|
+
},
|
|
13
|
+
COMPOSE_SUBNET_CONFLICT: {
|
|
14
|
+
code: "COMPOSE_SUBNET_CONFLICT",
|
|
15
|
+
description: "The fixed ingress subnet overlaps a NAS route.",
|
|
16
|
+
suggestion: "Choose a different compose.network.ingress_candidate and rebuild; do not change NAS networks.",
|
|
17
|
+
},
|
|
18
|
+
COMPOSE_SUBNET_INVENTORY_UNAVAILABLE: {
|
|
19
|
+
code: "COMPOSE_SUBNET_INVENTORY_UNAVAILABLE",
|
|
20
|
+
description: "Kernel route inventory is unavailable.",
|
|
21
|
+
suggestion: "Use a supported Linux package-account environment; never borrow Docker socket or administrator credentials.",
|
|
22
|
+
},
|
|
23
|
+
COMPOSE_PRIVATE_BIND_TIMING_UNSUPPORTED: {
|
|
24
|
+
code: "COMPOSE_PRIVATE_BIND_TIMING_UNSUPPORTED",
|
|
25
|
+
description: "DSM private-bind initialization is unavailable before official worker acquire.",
|
|
26
|
+
suggestion: "Use an explicitly selected arbitrary-UID-compatible named volume or keep the configuration unsupported; never initialize pkgvar as root.",
|
|
27
|
+
},
|
|
28
|
+
COMPOSE_HEALTH_REQUIRED: {
|
|
29
|
+
code: "COMPOSE_HEALTH_REQUIRED",
|
|
30
|
+
description: "Bridge ingress requires a declared App health endpoint.",
|
|
31
|
+
suggestion: "Set compose.web.health.path and its expected 2xx status.",
|
|
32
|
+
},
|
|
33
|
+
COMPOSE_READINESS_INVALID: {
|
|
34
|
+
code: "COMPOSE_READINESS_INVALID",
|
|
35
|
+
description: "Bridge readiness configuration is invalid.",
|
|
36
|
+
suggestion: "Rebuild with the matching SDK and declare a local health path and expected 2xx status.",
|
|
37
|
+
},
|
|
38
|
+
COMPOSE_ACQUIRE_GENERATION_INVALID: {
|
|
39
|
+
code: "COMPOSE_ACQUIRE_GENERATION_INVALID",
|
|
40
|
+
description: "The lifecycle acquire generation is missing, released or stale.",
|
|
41
|
+
suggestion: "Start the installed package through Package Center; this record does not attest Docker identity.",
|
|
42
|
+
},
|
|
43
|
+
COMPOSE_TCP_NOT_READY: {
|
|
44
|
+
code: "COMPOSE_TCP_NOT_READY",
|
|
45
|
+
description: "The declared ingress TCP endpoint is unavailable.",
|
|
46
|
+
suggestion: "Inspect the official project's startup result; do not expose upstream ports to the LAN.",
|
|
47
|
+
},
|
|
48
|
+
COMPOSE_HEALTH_NOT_READY: {
|
|
49
|
+
code: "COMPOSE_HEALTH_NOT_READY",
|
|
50
|
+
description: "The App health endpoint did not return the expected status.",
|
|
51
|
+
suggestion: "Check the declared App health path/status and upstream health; redirects are not followed.",
|
|
52
|
+
},
|
|
53
|
+
COMPOSE_REQUEST_INVALID: {
|
|
54
|
+
code: "COMPOSE_REQUEST_INVALID",
|
|
55
|
+
description: "The unified Compose request is invalid.",
|
|
56
|
+
suggestion: "Declare schema-2 files, web and supported DSM differences; see the Compose reference.",
|
|
57
|
+
},
|
|
58
|
+
COMPOSE_PROJECT_CONTROL_UNAVAILABLE: {
|
|
59
|
+
code: "COMPOSE_PROJECT_CONTROL_UNAVAILABLE",
|
|
60
|
+
description: "Protected official Compose project control is unavailable.",
|
|
61
|
+
suggestion: "Keep the project disabled until an official protected ACL/project Adapter has been verified; do not use host root or a destination grant.",
|
|
62
|
+
},
|
|
63
|
+
COMPOSE_ANONYMOUS_VOLUME_UNSUPPORTED: {
|
|
64
|
+
code: "COMPOSE_ANONYMOUS_VOLUME_UNSUPPORTED",
|
|
65
|
+
description: "The declared value:the declared value needs a stable declared volume.",
|
|
66
|
+
suggestion: "Correct the reported declaration in dsm-package.yaml; see the Compose support matrix.",
|
|
67
|
+
},
|
|
68
|
+
COMPOSE_BASE_PATH_UNSUPPORTED: {
|
|
69
|
+
code: "COMPOSE_BASE_PATH_UNSUPPORTED",
|
|
70
|
+
description: "Use the upstream's dedicated origin at /; automatic subpath asset rewriting is not supported.",
|
|
71
|
+
suggestion: "Correct the reported declaration in dsm-package.yaml; see the Compose support matrix.",
|
|
72
|
+
},
|
|
73
|
+
COMPOSE_BUILD_UNSUPPORTED: {
|
|
74
|
+
code: "COMPOSE_BUILD_UNSUPPORTED",
|
|
75
|
+
description: "The declared value must use an upstream prebuilt image.",
|
|
76
|
+
suggestion: "Correct the reported declaration in dsm-package.yaml; see the Compose support matrix.",
|
|
77
|
+
},
|
|
78
|
+
COMPOSE_EGRESS_ALLOWLIST_UNSUPPORTED: {
|
|
79
|
+
code: "COMPOSE_EGRESS_ALLOWLIST_UNSUPPORTED",
|
|
80
|
+
description: "Choose isolated or internet; destination allowlists are not implemented.",
|
|
81
|
+
suggestion: "Declare isolated or internet; destination allowlists are unsupported.",
|
|
82
|
+
},
|
|
83
|
+
COMPOSE_ENV_INVALID: {
|
|
84
|
+
code: "COMPOSE_ENV_INVALID",
|
|
85
|
+
description: "Invalid service environment.",
|
|
86
|
+
suggestion: "Correct the reported declaration in dsm-package.yaml; see the Compose support matrix.",
|
|
87
|
+
},
|
|
88
|
+
COMPOSE_EXTERNAL_VOLUME_UNVERIFIED: {
|
|
89
|
+
code: "COMPOSE_EXTERNAL_VOLUME_UNVERIFIED",
|
|
90
|
+
description: "External volume the declared value needs trusted admin ownership/driver readback; no official controller is configured.",
|
|
91
|
+
suggestion: "Correct the reported declaration in dsm-package.yaml; see the Compose support matrix.",
|
|
92
|
+
},
|
|
93
|
+
COMPOSE_FILE_RESOURCE_UNSUPPORTED: {
|
|
94
|
+
code: "COMPOSE_FILE_RESOURCE_UNSUPPORTED",
|
|
95
|
+
description: "Secret/config resource delivery is not implemented; use explicit package-owned configuration.",
|
|
96
|
+
suggestion: "Use bounded regular project-local files without symlink or remote dependencies, then rebuild from unchanged bytes.",
|
|
97
|
+
},
|
|
98
|
+
COMPOSE_IMAGE_INVALID: {
|
|
99
|
+
code: "COMPOSE_IMAGE_INVALID",
|
|
100
|
+
description: "Service the declared value needs an image.",
|
|
101
|
+
suggestion: "Use the intended anonymous registry repository and verified Linux platform digest; recheck source/image proofs and rebuild.",
|
|
102
|
+
},
|
|
103
|
+
COMPOSE_IMAGE_LOCK_INVALID: {
|
|
104
|
+
code: "COMPOSE_IMAGE_LOCK_INVALID",
|
|
105
|
+
description: "Unsafe image VOLUME target.",
|
|
106
|
+
suggestion: "Use the intended anonymous registry repository and verified Linux platform digest; recheck source/image proofs and rebuild.",
|
|
107
|
+
},
|
|
108
|
+
COMPOSE_IMAGE_PLATFORM_UNSUPPORTED: {
|
|
109
|
+
code: "COMPOSE_IMAGE_PLATFORM_UNSUPPORTED",
|
|
110
|
+
description: "The declared value has no Linux the declared value image.",
|
|
111
|
+
suggestion: "Use the intended anonymous registry repository and verified Linux platform digest; recheck source/image proofs and rebuild.",
|
|
112
|
+
},
|
|
113
|
+
COMPOSE_IMAGE_RESOLVE_FAILED: {
|
|
114
|
+
code: "COMPOSE_IMAGE_RESOLVE_FAILED",
|
|
115
|
+
description: "Registry mirror must be an HTTPS origin.",
|
|
116
|
+
suggestion: "Use the intended anonymous registry repository and verified Linux platform digest; recheck source/image proofs and rebuild.",
|
|
117
|
+
},
|
|
118
|
+
COMPOSE_INPUT_CHANGED: {
|
|
119
|
+
code: "COMPOSE_INPUT_CHANGED",
|
|
120
|
+
description: "Compose input bytes changed after planning.",
|
|
121
|
+
suggestion: "Use bounded regular project-local files without symlink or remote dependencies, then rebuild from unchanged bytes.",
|
|
122
|
+
},
|
|
123
|
+
COMPOSE_LEGACY_MANIFEST_REMOVED: {
|
|
124
|
+
code: "COMPOSE_LEGACY_MANIFEST_REMOVED",
|
|
125
|
+
description: "Compose requires a schema-2 dsm-package.yaml declaration.",
|
|
126
|
+
suggestion: "Run init -t compose and declare differences in schema-2 dsm-package.yaml while retaining upstream bytes.",
|
|
127
|
+
},
|
|
128
|
+
COMPOSE_LOCK_INVALID: {
|
|
129
|
+
code: "COMPOSE_LOCK_INVALID",
|
|
130
|
+
description: "Private storage inventory is invalid.",
|
|
131
|
+
suggestion: "Correct the reported declaration in dsm-package.yaml; see the Compose support matrix.",
|
|
132
|
+
},
|
|
133
|
+
COMPOSE_MODEL_INVALID: {
|
|
134
|
+
code: "COMPOSE_MODEL_INVALID",
|
|
135
|
+
description: "The declared value.",
|
|
136
|
+
suggestion: "Correct the reported declaration in dsm-package.yaml; see the Compose support matrix.",
|
|
137
|
+
},
|
|
138
|
+
COMPOSE_NETWORK_UNSUPPORTED: {
|
|
139
|
+
code: "COMPOSE_NETWORK_UNSUPPORTED",
|
|
140
|
+
description: "Network the declared value must be a managed bridge without custom driver/IPAM options.",
|
|
141
|
+
suggestion: "Correct the reported declaration in dsm-package.yaml; see the Compose support matrix.",
|
|
142
|
+
},
|
|
143
|
+
COMPOSE_OIDC_TOPOLOGY_UNSUPPORTED: {
|
|
144
|
+
code: "COMPOSE_OIDC_TOPOLOGY_UNSUPPORTED",
|
|
145
|
+
description: "Host service the declared value requires oidc Web ingress and a verified address_env.",
|
|
146
|
+
suggestion: "Correct the reported declaration in dsm-package.yaml; see the Compose support matrix.",
|
|
147
|
+
},
|
|
148
|
+
COMPOSE_PACKAGE_INVALID: {
|
|
149
|
+
code: "COMPOSE_PACKAGE_INVALID",
|
|
150
|
+
description: "Package must be 2-64 alphanumerics starting with a letter.",
|
|
151
|
+
suggestion: "Correct the reported declaration in dsm-package.yaml; see the Compose support matrix.",
|
|
152
|
+
},
|
|
153
|
+
COMPOSE_PRIVILEGE_UNSUPPORTED: {
|
|
154
|
+
code: "COMPOSE_PRIVILEGE_UNSUPPORTED",
|
|
155
|
+
description: "Container root capability the declared value is not supported.",
|
|
156
|
+
suggestion: "Correct the reported declaration in dsm-package.yaml; see the Compose support matrix.",
|
|
157
|
+
},
|
|
158
|
+
COMPOSE_ROOT_FORBIDDEN: {
|
|
159
|
+
code: "COMPOSE_ROOT_FORBIDDEN",
|
|
160
|
+
description: "Prepare must run as the package account.",
|
|
161
|
+
suggestion: "Use the package identity; container-root requires explicit reason, bridge and no host bind. Never elevate the App on the host.",
|
|
162
|
+
},
|
|
163
|
+
COMPOSE_ROOT_HOST_MOUNT_FORBIDDEN: {
|
|
164
|
+
code: "COMPOSE_ROOT_HOST_MOUNT_FORBIDDEN",
|
|
165
|
+
description: "Container-root service the declared value cannot bind host files or Shares.",
|
|
166
|
+
suggestion: "Use the package identity; container-root requires explicit reason, bridge and no host bind. Never elevate the App on the host.",
|
|
167
|
+
},
|
|
168
|
+
COMPOSE_ROOT_OPT_IN_INVALID: {
|
|
169
|
+
code: "COMPOSE_ROOT_OPT_IN_INVALID",
|
|
170
|
+
description: "Container root needs a reason and bridge networking.",
|
|
171
|
+
suggestion: "Use the package identity; container-root requires explicit reason, bridge and no host bind. Never elevate the App on the host.",
|
|
172
|
+
},
|
|
173
|
+
COMPOSE_RUN_AS_INVALID: {
|
|
174
|
+
code: "COMPOSE_RUN_AS_INVALID",
|
|
175
|
+
description: "Service the declared value run_as must be package or container-root.",
|
|
176
|
+
suggestion: "Correct the reported declaration in dsm-package.yaml; see the Compose support matrix.",
|
|
177
|
+
},
|
|
178
|
+
COMPOSE_SCALE_UNSUPPORTED: {
|
|
179
|
+
code: "COMPOSE_SCALE_UNSUPPORTED",
|
|
180
|
+
description: "The declared value deploy/replicas need an explicit local runtime contract.",
|
|
181
|
+
suggestion: "Correct the reported declaration in dsm-package.yaml; see the Compose support matrix.",
|
|
182
|
+
},
|
|
183
|
+
COMPOSE_SERVICE_UNKNOWN: {
|
|
184
|
+
code: "COMPOSE_SERVICE_UNKNOWN",
|
|
185
|
+
description: "Services.the declared value is not active.",
|
|
186
|
+
suggestion: "Correct the reported declaration in dsm-package.yaml; see the Compose support matrix.",
|
|
187
|
+
},
|
|
188
|
+
COMPOSE_SHARE_INVALID: {
|
|
189
|
+
code: "COMPOSE_SHARE_INVALID",
|
|
190
|
+
description: "Shares.the declared value needs admin selection, ro/rw and fixed mounts.",
|
|
191
|
+
suggestion: "Keep the project disabled until an official protected mount/project adapter is verified; never use a destination grant or host root workaround.",
|
|
192
|
+
},
|
|
193
|
+
COMPOSE_SHARE_NEEDS_APPROVAL: {
|
|
194
|
+
code: "COMPOSE_SHARE_NEEDS_APPROVAL",
|
|
195
|
+
description: "Declare capabilities.composeMounts: true; user destination grants cannot authorize package mounts.",
|
|
196
|
+
suggestion: "Keep the project disabled until an official protected mount/project adapter is verified; never use a destination grant or host root workaround.",
|
|
197
|
+
},
|
|
198
|
+
COMPOSE_SOURCE_UNSAFE: {
|
|
199
|
+
code: "COMPOSE_SOURCE_UNSAFE",
|
|
200
|
+
description: "Files must contain 1-32 local Compose files.",
|
|
201
|
+
suggestion: "Use bounded regular project-local files without symlink or remote dependencies, then rebuild from unchanged bytes.",
|
|
202
|
+
},
|
|
203
|
+
COMPOSE_STORAGE_INVALID: {
|
|
204
|
+
code: "COMPOSE_STORAGE_INVALID",
|
|
205
|
+
description: "Data.the declared value needs an active service, target, storage: package and safe path.",
|
|
206
|
+
suggestion: "Correct the reported declaration in dsm-package.yaml; see the Compose support matrix.",
|
|
207
|
+
},
|
|
208
|
+
COMPOSE_STORAGE_NOT_READY: {
|
|
209
|
+
code: "COMPOSE_STORAGE_NOT_READY",
|
|
210
|
+
description: "Invalid package var path.",
|
|
211
|
+
suggestion: "Correct the reported declaration in dsm-package.yaml; see the Compose support matrix.",
|
|
212
|
+
},
|
|
213
|
+
COMPOSE_STORAGE_UNMAPPED: {
|
|
214
|
+
code: "COMPOSE_STORAGE_UNMAPPED",
|
|
215
|
+
description: "The declared value:the declared value needs package data or an approved Share mapping.",
|
|
216
|
+
suggestion: "Correct the reported declaration in dsm-package.yaml; see the Compose support matrix.",
|
|
217
|
+
},
|
|
218
|
+
COMPOSE_VOLUME_DRIVER_UNSUPPORTED: {
|
|
219
|
+
code: "COMPOSE_VOLUME_DRIVER_UNSUPPORTED",
|
|
220
|
+
description: "Reserved image volume namespace collision.",
|
|
221
|
+
suggestion: "Correct the reported declaration in dsm-package.yaml; see the Compose support matrix.",
|
|
222
|
+
},
|
|
223
|
+
COMPOSE_WEB_INVALID: {
|
|
224
|
+
code: "COMPOSE_WEB_INVALID",
|
|
225
|
+
description: "Compose web ingress is invalid.",
|
|
226
|
+
suggestion: "Correct the reported declaration in dsm-package.yaml; see the Compose support matrix.",
|
|
227
|
+
},
|
|
228
|
+
SDK_COMPOSE_BUILD_FAILED: {
|
|
229
|
+
code: "SDK_COMPOSE_BUILD_FAILED",
|
|
230
|
+
description: "The trusted SDK Compose tool could not be compiled.",
|
|
231
|
+
suggestion: "Correct the reported declaration in dsm-package.yaml; see the Compose support matrix.",
|
|
232
|
+
},
|
|
233
|
+
SDK_COMPOSE_FAILED: {
|
|
234
|
+
code: "SDK_COMPOSE_FAILED",
|
|
235
|
+
description: "The SDK Compose tool could not return a valid plan.",
|
|
236
|
+
suggestion: "Correct the reported declaration in dsm-package.yaml; see the Compose support matrix.",
|
|
237
|
+
},
|
|
238
|
+
SDK_COMPOSE_UNSUPPORTED: {
|
|
239
|
+
code: "SDK_COMPOSE_UNSUPPORTED",
|
|
240
|
+
description: "This SDK release does not contain the unified Compose planner.",
|
|
241
|
+
suggestion: "Correct the reported declaration in dsm-package.yaml; see the Compose support matrix.",
|
|
242
|
+
},
|
|
243
|
+
};
|