@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
package/dist/info.js
CHANGED
|
@@ -6,11 +6,11 @@ import { appOriginFromContract } from "./host/open-url.js";
|
|
|
6
6
|
import { resolveDeployIdentity } from "./deploy/identity.js";
|
|
7
7
|
import { loadStandaloneManifest, sdkBackend, } from "./standalone/manifest.js";
|
|
8
8
|
import { inspectSpk } from "./standalone/inspect-spk.js";
|
|
9
|
-
import { isNativeLayout, loadNativeLayout } from "./standalone/native-layout.js";
|
|
10
|
-
import { executorPolicy, executorPolicyText, sha256 } from "./runtime/executor-policy.js";
|
|
9
|
+
import { isNativeLayout, loadNativeLayout, } from "./standalone/native-layout.js";
|
|
10
|
+
import { executorPolicy, executorPolicyText, sha256, } from "./runtime/executor-policy.js";
|
|
11
11
|
import { executorTasks } from "./runtime/executor-tasks.js";
|
|
12
12
|
import { requiredSdk } from "./runtime/sdk-source.js";
|
|
13
|
-
import { uninstallCleanup } from "./runtime/uninstall-cleanup.js";
|
|
13
|
+
import { uninstallCleanup, } from "./runtime/uninstall-cleanup.js";
|
|
14
14
|
export async function projectInfo(options) {
|
|
15
15
|
const identity = resolveDeployIdentity(options.project, { dev: options.dev });
|
|
16
16
|
const remote = await options.host.getInstalledPackageState?.(identity.packageId);
|
|
@@ -30,12 +30,14 @@ export function classifyRootTasks(expected, inventory) {
|
|
|
30
30
|
const result = (state, reason) => ({ name: "rootOps", state, reason });
|
|
31
31
|
if (inventory?.unsupported)
|
|
32
32
|
return result("unsupported", inventory.error ?? "DSM scheduler APIs are unavailable.");
|
|
33
|
-
if (!inventory ||
|
|
33
|
+
if (!inventory ||
|
|
34
|
+
inventory.error ||
|
|
34
35
|
!Array.isArray(inventory.tasks) ||
|
|
35
36
|
!Number.isInteger(inventory.total) ||
|
|
36
37
|
inventory.total !== inventory.tasks.length ||
|
|
37
38
|
!expected.length)
|
|
38
|
-
return result("blocked", inventory?.error ??
|
|
39
|
+
return result("blocked", inventory?.error ??
|
|
40
|
+
"Complete scheduler readback is unavailable; open the DSM package UI to verify approval.");
|
|
39
41
|
let approval = false;
|
|
40
42
|
for (const task of expected) {
|
|
41
43
|
const named = inventory.tasks.filter((record) => record && record.name === task.name);
|
|
@@ -49,11 +51,20 @@ export function classifyRootTasks(expected, inventory) {
|
|
|
49
51
|
const extra = actual.extra;
|
|
50
52
|
const boot = task.kind === "boot";
|
|
51
53
|
const script = boot ? actual.operation : extra?.script;
|
|
52
|
-
const rootOwner = actual.owner === "root" ||
|
|
53
|
-
|
|
54
|
+
const rootOwner = actual.owner === "root" ||
|
|
55
|
+
(boot && actual.owner?.["0"] === "root");
|
|
56
|
+
if ((!boot &&
|
|
57
|
+
(!["string", "number"].includes(typeof actual.id) ||
|
|
58
|
+
!/^\d+$/.test(String(actual.id)))) ||
|
|
54
59
|
!rootOwner ||
|
|
55
|
-
(!boot &&
|
|
56
|
-
|
|
60
|
+
(!boot &&
|
|
61
|
+
(actual.real_owner !== "root" ||
|
|
62
|
+
actual.enable !== false ||
|
|
63
|
+
actual.type !== "script")) ||
|
|
64
|
+
(boot &&
|
|
65
|
+
(actual.enable !== true ||
|
|
66
|
+
(actual.event ?? actual.trigger_event) !== "bootup" ||
|
|
67
|
+
actual.operation_type !== "script")) ||
|
|
57
68
|
typeof script !== "string")
|
|
58
69
|
return result("blocked", `Root task ownership or definition cannot be verified: ${task.name}.`);
|
|
59
70
|
if (script === task.script)
|
|
@@ -75,11 +86,23 @@ async function liveRootCapability(m, host, arch) {
|
|
|
75
86
|
return classified;
|
|
76
87
|
const observed = await host.getExecutorStatus?.(m.package);
|
|
77
88
|
if (observed?.ready !== true || observed.policy_digest !== policyDigest)
|
|
78
|
-
return {
|
|
79
|
-
|
|
89
|
+
return {
|
|
90
|
+
...classified,
|
|
91
|
+
state: "blocked",
|
|
92
|
+
reason: observed?.error ??
|
|
93
|
+
"Executor health or policy digest readback could not be verified.",
|
|
94
|
+
};
|
|
95
|
+
return {
|
|
96
|
+
...classified,
|
|
97
|
+
reason: "Root tasks and the running executor match the source policy (no local SPK, so executor bytes were not compared).",
|
|
98
|
+
};
|
|
80
99
|
}
|
|
81
100
|
catch {
|
|
82
|
-
return {
|
|
101
|
+
return {
|
|
102
|
+
name: "rootOps",
|
|
103
|
+
state: "blocked",
|
|
104
|
+
reason: "DSM executor or root task readback could not be verified.",
|
|
105
|
+
};
|
|
83
106
|
}
|
|
84
107
|
}
|
|
85
108
|
async function rootCapability(m, host, version) {
|
|
@@ -108,18 +131,35 @@ async function rootCapability(m, host, version) {
|
|
|
108
131
|
inspected.package !== m.package ||
|
|
109
132
|
inspected.version !== version)
|
|
110
133
|
return blocked("Executor SPK artifact failed inspection or identity checks.");
|
|
111
|
-
if (!inspected.executor ||
|
|
134
|
+
if (!inspected.executor ||
|
|
135
|
+
JSON.stringify(inspected.executor.policy) !==
|
|
136
|
+
JSON.stringify(executorPolicy(m, arch)))
|
|
112
137
|
return blocked("Executor policy differs from source; rebuild and deploy before approval.");
|
|
113
138
|
const inventory = await host.getRootTaskInventory?.(m.package);
|
|
114
139
|
const classified = classifyRootTasks(inspected.executor.tasks, inventory);
|
|
115
|
-
const executor = {
|
|
140
|
+
const executor = {
|
|
141
|
+
...inspected.executor,
|
|
142
|
+
tasks: inspected.executor.tasks.map((task) => {
|
|
116
143
|
const one = classifyRootTasks([task], inventory);
|
|
117
|
-
return {
|
|
118
|
-
|
|
144
|
+
return {
|
|
145
|
+
...task,
|
|
146
|
+
state: one.state === "unsupported" ? "blocked" : one.state,
|
|
147
|
+
};
|
|
148
|
+
}),
|
|
149
|
+
};
|
|
119
150
|
if (classified.state === "ready") {
|
|
120
151
|
const observed = await host.getExecutorStatus?.(m.package);
|
|
121
|
-
if (!observed ||
|
|
122
|
-
|
|
152
|
+
if (!observed ||
|
|
153
|
+
observed.ready !== true ||
|
|
154
|
+
observed.executor_digest !== executor.executorDigest ||
|
|
155
|
+
observed.policy_digest !== executor.policyDigest)
|
|
156
|
+
return {
|
|
157
|
+
...classified,
|
|
158
|
+
executor,
|
|
159
|
+
state: "blocked",
|
|
160
|
+
reason: observed?.error ??
|
|
161
|
+
"Executor health or digest readback could not be verified.",
|
|
162
|
+
};
|
|
123
163
|
}
|
|
124
164
|
return { ...classified, executor };
|
|
125
165
|
}
|
|
@@ -140,7 +180,14 @@ export async function standaloneInfo(options) {
|
|
|
140
180
|
const installed = state.installed;
|
|
141
181
|
const capabilities = [];
|
|
142
182
|
let executor;
|
|
143
|
-
for (const name of [
|
|
183
|
+
for (const name of [
|
|
184
|
+
"files",
|
|
185
|
+
"jobs",
|
|
186
|
+
"identity",
|
|
187
|
+
"rootOps",
|
|
188
|
+
"delegate",
|
|
189
|
+
"webapi",
|
|
190
|
+
]) {
|
|
144
191
|
if (!m.capabilities[name])
|
|
145
192
|
continue;
|
|
146
193
|
if (!installed)
|
|
@@ -150,7 +197,8 @@ export async function standaloneInfo(options) {
|
|
|
150
197
|
reason: "Package is not installed; deploy first.",
|
|
151
198
|
});
|
|
152
199
|
else if (name === "rootOps" || name === "delegate" || name === "webapi") {
|
|
153
|
-
const result = await rootCapability(m, options.host, remote?.version ??
|
|
200
|
+
const result = await rootCapability(m, options.host, remote?.version ??
|
|
201
|
+
(await options.host.getInstalledPackageVersion?.(m.package)));
|
|
154
202
|
executor = result.executor;
|
|
155
203
|
const { executor: ignored, ...capability } = result;
|
|
156
204
|
capabilities.push({ ...capability, name });
|
|
@@ -162,25 +210,69 @@ export async function standaloneInfo(options) {
|
|
|
162
210
|
reason: "Installed package supports this capability without administrator approval.",
|
|
163
211
|
});
|
|
164
212
|
}
|
|
213
|
+
if (m.compose) {
|
|
214
|
+
if (m.capabilities.composeMounts)
|
|
215
|
+
capabilities.push({
|
|
216
|
+
name: "composeMounts",
|
|
217
|
+
state: "unsupported",
|
|
218
|
+
reason: "COMPOSE_PROJECT_CONTROL_UNAVAILABLE: separate protected compose-mount ACL and project control are not verified; no mount grant is issued.",
|
|
219
|
+
});
|
|
220
|
+
if (m.compose.web &&
|
|
221
|
+
m.compose.plan.services.some((s) => s.network === "bridge"))
|
|
222
|
+
capabilities.push({
|
|
223
|
+
name: "compose",
|
|
224
|
+
state: installed && remote?.status === "running" ? "ready" : "blocked",
|
|
225
|
+
reason: "Bridge uses limited availability readiness: App health, loopback TCP and installed-version lifecycle generation. This does not attest actual Docker image/project/generation; use installed runtime status for readiness.",
|
|
226
|
+
});
|
|
227
|
+
}
|
|
165
228
|
if (m.portal)
|
|
166
|
-
capabilities.push(installed
|
|
167
|
-
|
|
168
|
-
|
|
229
|
+
capabilities.push(installed
|
|
230
|
+
? await portalCapability(m.package, m.portal.alias, options.host)
|
|
231
|
+
: {
|
|
232
|
+
name: "portal",
|
|
233
|
+
state: "blocked",
|
|
234
|
+
reason: "Package is not installed; deploy first.",
|
|
235
|
+
});
|
|
169
236
|
return {
|
|
170
237
|
...state,
|
|
238
|
+
...(m.compose
|
|
239
|
+
? {
|
|
240
|
+
openUrl: m.compose.web
|
|
241
|
+
? `${options.host.dsmOrigin.replace(/\/$/, "")}/webman/3rdparty/${m.package}/sso/open.cgi`
|
|
242
|
+
: undefined,
|
|
243
|
+
}
|
|
244
|
+
: {}),
|
|
171
245
|
capabilities,
|
|
172
246
|
...(executor ? { executor } : {}),
|
|
173
|
-
uninstallCleanup: uninstallCleanup(m.package, {
|
|
247
|
+
uninstallCleanup: uninstallCleanup(m.package, {
|
|
248
|
+
executor: Boolean(m.capabilities.rootOps ||
|
|
249
|
+
m.capabilities.delegate ||
|
|
250
|
+
m.capabilities.webapi),
|
|
251
|
+
portal: Boolean(m.portal && !portalByExecutor(m.os_min_ver)),
|
|
252
|
+
portalEntry: Boolean(m.portal && portalByExecutor(m.os_min_ver)),
|
|
253
|
+
}),
|
|
174
254
|
};
|
|
175
255
|
}
|
|
176
256
|
/** What the installed App's portal.status says; DSM owns the alias, an administrator sets it. */
|
|
177
257
|
async function portalCapability(pkg, defaultAlias, host) {
|
|
178
258
|
const status = await host.getPortalStatus?.(pkg);
|
|
179
259
|
if (status?.state === "active" && status.alias)
|
|
180
|
-
return {
|
|
260
|
+
return {
|
|
261
|
+
name: "portal",
|
|
262
|
+
state: "ready",
|
|
263
|
+
reason: `Users open the App at ${host.dsmOrigin.replace(/\/$/, "")}/${encodeURIComponent(status.alias)}/ after DSM sign-in.`,
|
|
264
|
+
};
|
|
181
265
|
if (status?.state === "inactive")
|
|
182
|
-
return {
|
|
183
|
-
|
|
266
|
+
return {
|
|
267
|
+
name: "portal",
|
|
268
|
+
state: "needs-admin-approval",
|
|
269
|
+
reason: `No alias is set; an administrator applies one (default ${defaultAlias}) in the App's Application Portal panel.`,
|
|
270
|
+
};
|
|
271
|
+
return {
|
|
272
|
+
name: "portal",
|
|
273
|
+
state: "blocked",
|
|
274
|
+
reason: "The App's portal.status could not be read; open the App window to check the Application Portal.",
|
|
275
|
+
};
|
|
184
276
|
}
|
|
185
277
|
/** A native layout has no SDK capabilities; report package state only. */
|
|
186
278
|
async function nativeLayoutInfo(root, host) {
|
|
@@ -210,7 +302,7 @@ export function formatProjectInfo(info) {
|
|
|
210
302
|
info.openUrl ? `Open URL: ${info.openUrl}` : "Open URL: (not set)",
|
|
211
303
|
info.readiness ? `Readiness: ${info.readiness}` : "",
|
|
212
304
|
...info.capabilities.map((cap) => `Capability ${cap.name}: ${cap.state} — ${cap.reason}`),
|
|
213
|
-
...(info.uninstallCleanup ?? []).map(item => `Uninstall cleanup (if needed): ${item.target} — ${item.condition} ${item.action}`),
|
|
305
|
+
...(info.uninstallCleanup ?? []).map((item) => `Uninstall cleanup (if needed): ${item.target} — ${item.condition} ${item.action}`),
|
|
214
306
|
]
|
|
215
307
|
.filter(Boolean)
|
|
216
308
|
.join("\n");
|