@synopackageland/cli 0.41.2 → 0.41.4
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 +17 -0
- package/README.md +8 -2
- package/dist/error-registry.js +20 -0
- package/dist/runtime/webapi-profile.js +1 -1
- package/dist/standalone/build.js +4 -3
- package/dist/standalone/manifest.js +11 -0
- package/dist/standalone/portal.d.ts +2 -0
- package/dist/standalone/portal.js +5 -0
- package/package.json +1 -1
- package/skills/SKILL.md +1 -1
- package/skills/reference/portal.md +2 -0
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,23 @@
|
|
|
2
2
|
|
|
3
3
|
## Unreleased
|
|
4
4
|
|
|
5
|
+
## 0.41.4 (2026-10-30)
|
|
6
|
+
|
|
7
|
+
- Allow `webapi` raw profiles to set `raw_max_bytes` up to 2 GiB, matching
|
|
8
|
+
SDK/App 0.38.2. That SDK removes Go `JobWebAPI.CallRaw` and
|
|
9
|
+
`RawWebAPIResult.Body`; raw consumers must move to `CallRawTo(..., io.Writer)`.
|
|
10
|
+
Templates ~0.35.2 still pin SDK 0.38.1; declare SDK 0.38.2 to use larger raw
|
|
11
|
+
downloads.
|
|
12
|
+
|
|
13
|
+
## 0.41.3 (2026-10-30)
|
|
14
|
+
|
|
15
|
+
- Build Portal Apps without an executor on DSM 7.2-64570+ using the App SDK
|
|
16
|
+
pin for the panel text. Keep executor SDK selection unchanged.
|
|
17
|
+
- Reject a no-executor Portal without an SDK during both check and build
|
|
18
|
+
(`STANDALONE_PORTAL_SDK_REQUIRED`); cover the Portal/executor/SDK/DSM matrix
|
|
19
|
+
and a real Go CGI-to-SPK build.
|
|
20
|
+
- Complete diagnostics for invalid external caller declarations and entry policies.
|
|
21
|
+
|
|
5
22
|
## 0.41.2 (2026-10-29)
|
|
6
23
|
|
|
7
24
|
- Require templates ~0.35.2 with SDK/App 0.38.1 and clean registry metadata.
|
package/README.md
CHANGED
|
@@ -76,13 +76,19 @@ and checks both payloads against the resolved SDK's `executor-manifest.json`.
|
|
|
76
76
|
The manifest declares format 1, the exact executor release, its Go toolchain and
|
|
77
77
|
six SHA-256 digests. SDK minor versions are independent of CLI minor versions.
|
|
78
78
|
|
|
79
|
+
Portal panel text comes from the App's SDK even when it has no executor: the
|
|
80
|
+
existing `identity_gateway.sdk` pin, or otherwise the backend SDK requirement
|
|
81
|
+
(`backend/go.mod`, `backend.sdk`, Rust SDK tag, or project `go.mod` for a binary).
|
|
82
|
+
A Portal without an SDK is rejected by both `check` and `build` with
|
|
83
|
+
`STANDALONE_PORTAL_SDK_REQUIRED`. DSM 7.2-64570+ needs no executor for Portal.
|
|
84
|
+
|
|
79
85
|
SDK module downloads preserve normal Go checksum verification and the user's Go
|
|
80
86
|
settings; Go Apps also check their existing `backend/go.sum`. Commit that file
|
|
81
87
|
alongside npm lockfiles. Do not disable checksum verification for public SDK
|
|
82
88
|
packages. Private mirrors can use explicitly configured Go authentication and
|
|
83
89
|
checksum settings; a failed fetch reports `SDK_DOWNLOAD_FAILED`.
|
|
84
90
|
|
|
85
|
-
CLI 0.41.
|
|
91
|
+
CLI 0.41.4 uses templates ~0.35.2, whose default SDK pin is 0.38.1. Compose import and SDK defaults
|
|
86
92
|
read the SDK pin from the installed templates; explicit App SDK declarations take precedence.
|
|
87
93
|
Every App method must be declared in the permissions table. Dynamic names and
|
|
88
94
|
custom HTTP dispatchers are rejected. With SDK 0.33.0, use fixed
|
|
@@ -93,7 +99,7 @@ unimported development commands are excluded. Use
|
|
|
93
99
|
authentication. See [App entry and registration](docs/app-entry.md).
|
|
94
100
|
|
|
95
101
|
`init` and `add` read their templates from the locally installed
|
|
96
|
-
`@synopackageland/templates`. CLI 0.41.
|
|
102
|
+
`@synopackageland/templates`. CLI 0.41.4 depends on `~0.35.2`: npm installation or
|
|
97
103
|
update can select the latest templates patch in the 0.35 minor line. Templates
|
|
98
104
|
patches need only a templates release; update the npm installation to receive them.
|
|
99
105
|
The CLI never fetches templates at runtime, so offline use works after installation;
|
package/dist/error-registry.js
CHANGED
|
@@ -38,6 +38,21 @@ export const ERROR_REGISTRY = {
|
|
|
38
38
|
description: "The launcher has Portal fields without a matching explicit Portal declaration.",
|
|
39
39
|
suggestion: "Remove defaultAlias and login fields, or explicitly enable portal and rebuild.",
|
|
40
40
|
},
|
|
41
|
+
CTL_EXTERNAL_TRANSPORT: {
|
|
42
|
+
code: "CTL_EXTERNAL_TRANSPORT",
|
|
43
|
+
description: "External callers require the DSM session transport.",
|
|
44
|
+
suggestion: "Use the DSM session transport for external entries; remove capabilities.identity from an external-enabled App.",
|
|
45
|
+
},
|
|
46
|
+
CTL_EXTERNAL_ENTRY: {
|
|
47
|
+
code: "CTL_EXTERNAL_ENTRY",
|
|
48
|
+
description: "An external entry is not a registered administrator RPC method.",
|
|
49
|
+
suggestion: "Register a fixed RPC method, declare it as administrators in permissions.json, and reference it in ctl.external.",
|
|
50
|
+
},
|
|
51
|
+
CTL_EXTERNAL_DECLARATION_INVALID: {
|
|
52
|
+
code: "CTL_EXTERNAL_DECLARATION_INVALID",
|
|
53
|
+
description: "ctl.external contains an invalid protocol or RPC method declaration.",
|
|
54
|
+
suggestion: "Use protocol names mapped to objects with a fixed method, for example ctl.external.mcp: {method: app.mcp}.",
|
|
55
|
+
},
|
|
41
56
|
LAUNCHER_TYPE_MISMATCH: {
|
|
42
57
|
code: "LAUNCHER_TYPE_MISMATCH",
|
|
43
58
|
description: "The built launcher or INFO entry differs from the declared launch mode.",
|
|
@@ -618,6 +633,11 @@ export const ERROR_REGISTRY = {
|
|
|
618
633
|
description: "console is declared together with portal.",
|
|
619
634
|
suggestion: "Declare either console or portal, not both.",
|
|
620
635
|
},
|
|
636
|
+
STANDALONE_PORTAL_SDK_REQUIRED: {
|
|
637
|
+
code: "STANDALONE_PORTAL_SDK_REQUIRED",
|
|
638
|
+
description: "A Portal App has no SDK pin for its panel text.",
|
|
639
|
+
suggestion: "Declare the App SDK in backend/go.mod (project go.mod for a binary), backend.sdk, the Rust SDK dependency, or identity_gateway.sdk; otherwise remove portal. An executor is not required on DSM 7.2-64570+.",
|
|
640
|
+
},
|
|
621
641
|
STANDALONE_PORTAL_REQUIRES_EXECUTOR: {
|
|
622
642
|
code: "STANDALONE_PORTAL_REQUIRES_EXECUTOR",
|
|
623
643
|
description: "An App targeting DSM below 7.2-64570 declares portal without an executor.",
|
|
@@ -8,7 +8,7 @@ export function validWebAPIProfiles(value) {
|
|
|
8
8
|
for (const [name, p] of Object.entries(value)) {
|
|
9
9
|
if (!/^[a-z][a-z0-9_-]{0,63}$/.test(name) || name === 'home' || !record(p) || Object.keys(p).some(k => !['purpose', 'apis', 'raw_max_bytes'].includes(k)) || typeof p.purpose !== 'string' || !p.purpose.trim() || Buffer.byteLength(p.purpose) > 1024 || /[\x00-\x1f\x7f]/.test(p.purpose) || !record(p.apis) || !Object.keys(p.apis).length)
|
|
10
10
|
return false;
|
|
11
|
-
if (p.raw_max_bytes !== undefined && (!Number.isSafeInteger(p.raw_max_bytes) || Number(p.raw_max_bytes) < 0 || Number(p.raw_max_bytes) >
|
|
11
|
+
if (p.raw_max_bytes !== undefined && (!Number.isSafeInteger(p.raw_max_bytes) || Number(p.raw_max_bytes) < 0 || Number(p.raw_max_bytes) > 2147483648))
|
|
12
12
|
return false;
|
|
13
13
|
for (const [api, methods] of Object.entries(p.apis)) {
|
|
14
14
|
if (!/^SYNO\.[A-Za-z][A-Za-z0-9]*(\.[A-Za-z][A-Za-z0-9]*)*$/.test(api) || api.toLowerCase().split('.').slice(1).some(x => blocked.has(x)) || !record(methods) || !Object.keys(methods).length)
|
package/dist/standalone/build.js
CHANGED
|
@@ -25,6 +25,7 @@ import { isBridgeBackend } from "./bridge-backend.js";
|
|
|
25
25
|
import { requestTimeoutSeconds } from "./request-timeout.js";
|
|
26
26
|
import { buildBridge, prepareBridge, } from "./bridge-build.js";
|
|
27
27
|
import { portalCatalogs } from "./portal-window.js";
|
|
28
|
+
import { portalSdkVersion } from "./portal.js";
|
|
28
29
|
/** Argv only, never a shell; output streams to the terminal except stderr, which is also kept, and captured stdout. */
|
|
29
30
|
export const runCommand = (command, args, options) => new Promise((done) => {
|
|
30
31
|
const child = spawn(command, args, {
|
|
@@ -159,9 +160,9 @@ export async function buildStandalone(options) {
|
|
|
159
160
|
const installSettingsControl = await buildInstallSettings(manifest, arch, archWork, run);
|
|
160
161
|
const appBackupControl = await buildAppBackup(manifest, arch, archWork, run, (w) => warnings.add(w));
|
|
161
162
|
const composeStorageInit = await buildComposeStorageInit(manifest, arch, archWork, run);
|
|
162
|
-
//
|
|
163
|
-
const portalText = manifest.portal
|
|
164
|
-
? portalCatalogs(sdkSource(executor
|
|
163
|
+
// Executor Apps keep their executor's SDK; no-root Apps use their own SDK pin.
|
|
164
|
+
const portalText = manifest.portal
|
|
165
|
+
? portalCatalogs(sdkSource(executor?.sdk ?? portalSdkVersion(manifest)).goDir)
|
|
165
166
|
: undefined;
|
|
166
167
|
await assembleStandaloneSpk({
|
|
167
168
|
manifest: builtManifest,
|
|
@@ -30,6 +30,7 @@ import { LOGIN_COLOR, loginImageProblem, } from "../build-spk/login-style.js";
|
|
|
30
30
|
import { validateReproducible } from "../reproducible-config.js";
|
|
31
31
|
import { appSdkVersion, SDK_MODULE, sdkSource, } from "../runtime/sdk-source.js";
|
|
32
32
|
import { sdkProblems } from "./sdk-check.js";
|
|
33
|
+
import { portalSdkVersion } from "./portal.js";
|
|
33
34
|
import { readWindowSize } from "./window-size.js";
|
|
34
35
|
import { appSdkTimeouts } from "../runtime/sdk-timeouts.js";
|
|
35
36
|
import { requestTimeoutSeconds, rootRunDeadlineProblem, TIMEOUT_USAGE, validTimeoutSeconds, } from "./request-timeout.js";
|
|
@@ -466,6 +467,16 @@ function readManifest(raw, c) {
|
|
|
466
467
|
if (portal && osMinVer && !isDsmAtLeast(osMinVer, PORTAL_EXECUTOR_MIN_DSM)) {
|
|
467
468
|
c.fail("STANDALONE_PORTAL_MIN_DSM", portalMinDsmMessage(osMinVer, "portal"));
|
|
468
469
|
}
|
|
470
|
+
if (portal && backend && osMinVer && !portalByExecutor(osMinVer) &&
|
|
471
|
+
!capabilities.rootOps && !capabilities.delegate && !capabilities.webapi) {
|
|
472
|
+
try {
|
|
473
|
+
if (!portalSdkVersion({ backend, root: c.root, identityGateway }))
|
|
474
|
+
c.fail("STANDALONE_PORTAL_SDK_REQUIRED", "portal needs the App's SDK for its panel text: require github.com/synopackageland/syno-pkg-land-sdk in the Go backend's go.mod (project go.mod for a binary), set backend.sdk for Python/Node, pin the Rust SDK release, or use identity_gateway.sdk. Remove portal if the App has no SDK.");
|
|
475
|
+
}
|
|
476
|
+
catch {
|
|
477
|
+
// sdkProblems reports an invalid backend SDK pin above.
|
|
478
|
+
}
|
|
479
|
+
}
|
|
469
480
|
const entryProblem = portal && packageId && displayname && osMinVer && portalByExecutor(osMinVer)
|
|
470
481
|
? portalEntryProblem(packageId, {
|
|
471
482
|
application: displayname,
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import type { StandaloneManifest } from "./manifest.js";
|
|
2
2
|
import type { PortalPackage } from "../portal/package.js";
|
|
3
|
+
/** The App's existing gateway pin or backend SDK; never the CLI/templates default. */
|
|
4
|
+
export declare function portalSdkVersion(manifest: Pick<StandaloneManifest, "backend" | "root" | "identityGateway">): string | undefined;
|
|
3
5
|
/**
|
|
4
6
|
* A native portal registered through DSM's web-config resource; it opens the
|
|
5
7
|
* package's own DSM window. Below DSM 7.2 the executor registers it instead.
|
|
@@ -1,4 +1,9 @@
|
|
|
1
1
|
import { portalByExecutor } from "../build-spk/dsm-version.js";
|
|
2
|
+
import { appSdkVersion } from "../runtime/sdk-source.js";
|
|
3
|
+
/** The App's existing gateway pin or backend SDK; never the CLI/templates default. */
|
|
4
|
+
export function portalSdkVersion(manifest) {
|
|
5
|
+
return manifest.identityGateway?.sdk ?? appSdkVersion(manifest.backend.kind === "binary" ? { kind: "go", dir: manifest.root } : manifest.backend);
|
|
6
|
+
}
|
|
2
7
|
/**
|
|
3
8
|
* A native portal registered through DSM's web-config resource; it opens the
|
|
4
9
|
* package's own DSM window. Below DSM 7.2 the executor registers it instead.
|
package/package.json
CHANGED
package/skills/SKILL.md
CHANGED
|
@@ -24,7 +24,7 @@ Use this guide to initialize, build and inspect own applications or upstream OSS
|
|
|
24
24
|
|
|
25
25
|
For an existing open-source App, first read [reference/upstream-fork.md](reference/upstream-fork.md): upstream fork/history + untouched upstream files + all packaging in `synology/`, official container images pinned by tag/index digest, native official release binaries pinned by SHA-256 or a source build hook, and a single tag-merge upgrade command. Use `init --upstream-fork`; never rewrite the App as a component. Integrated upstream Apps require direct DSM administrator entry without a separate App password or an OIDC/SSO server: supported trusted headers first, disabling native login second; the shipped OpenList pattern uses the existing SDK 0.17.3 token adapter (`mode: token`, `access: dsm-admins`, `entry: admin-only`) with server-side Authorization injection. All DSM admins share one upstream admin; token reset requires package restart; WebDAV retains its native credentials.
|
|
26
26
|
|
|
27
|
-
Use SDK/App 0.38.
|
|
27
|
+
Use SDK/App 0.38.2 and CLI 0.41.4 for App entry enforcement. Declare every App method
|
|
28
28
|
in the permissions table; dynamic names and unregistered HTTP dispatchers are rejected.
|
|
29
29
|
Use fixed SDK routes and `identity_gateway.upstream_auth_paths` for client API
|
|
30
30
|
paths that rely on upstream authentication. Read
|
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
Tier: a schema-2 App targeting DSM 7.2-64570+ may declare a Portal without an executor, including a unified Compose App. The CLI adds `instuninst_restart_services="nginx.service"` to INFO (merging service names without duplicates), so DSM reloads nginx after install, upgrade and uninstall. App hooks still run as the package account; a no-privilege App never runs root code. Declaring a Portal does not activate an alias or automatically open it: an administrator must apply the alias through DSM.
|
|
4
4
|
|
|
5
|
+
Portal needs the App's SDK for its panel text, including the no-executor route. The CLI uses the existing `identity_gateway.sdk` pin when present; otherwise it reads the backend SDK (`backend/go.mod` for Go, `backend.sdk` for Python/Node, the Rust release tag in Cargo.lock, or project `go.mod` for a binary). It does not substitute the CLI/templates SDK default. Without an SDK, `check` and `build` both refuse `STANDALONE_PORTAL_SDK_REQUIRED`: declare the App's SDK or remove Portal. Executor Apps continue using their executor's SDK.
|
|
6
|
+
|
|
5
7
|
On DSM 7.1 the unsigned web-config worker is unavailable, so entry registration still requires an executor (`rootOps`, `delegate` or `webapi`). `check`/`build` and `add portal` refuse a no-executor App targeting below 7.2 with `STANDALONE_PORTAL_REQUIRES_EXECUTOR`; raise `os_min_ver` to 7.2-64570+, add an executor capability, or omit Portal. `add portal` preserves a supported DSM target; it does not silently upgrade a no-privilege App's target or add root capabilities. The legacy `integration.web.portal` contract is unsupported; use the schema-2 top-level `portal` declaration.
|
|
6
8
|
|
|
7
9
|
Declare: `portal: {}` or `portal: {alias: my-app}` in dsm-package.yaml (`synopkgland add portal` with os_min_ver 7.2-64570+, or `synopkgland add root-ops portal` for the DSM 7.1 executor route). The alias defaults to the package name in lowercase. Aliases are 1–64 lowercase letters, digits or `-`, not starting or ending with `-`, and not a DSM path or Synology App alias (`webman`, `file`, …). The native-layout `backend: {kind: native}` owns its conf/scripts and its own root helper: `portal: {}` or `{alias}` there needs os_min_ver 7.1-42661+ and writes `synopkgland_portal_alias="<alias>"` and the nginx reload declaration into INFO, which Package Center writes as root, so the product's root helper can trust it; the helper registers the app.d entry (application = INFO displayname) and owns its uninstall cleanup and wizard. inspect checks that INFO line against `native-layout.json`.
|