@synopackageland/cli 0.15.0 → 0.15.2
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 -1
- package/README.md +51 -2
- package/dist/bin.js +2 -3
- package/dist/cli.d.ts +1 -0
- package/dist/cli.js +1 -0
- package/dist/contract/unsupported-types.js +1 -1
- package/dist/contract/validate.js +2 -2
- package/dist/e2e/dsm.d.ts +2 -2
- package/dist/e2e/dsm.js +10 -6
- package/dist/host/share-permission-lookup.d.ts +1 -1
- package/dist/host/share-permission.d.ts +1 -1
- package/dist/host/share-permission.js +1 -1
- package/dist/runtime/executor-digests.json +11 -0
- package/package.json +3 -2
- package/skills/SKILL.md +1 -1
- package/skills/reference/compose.md +6 -6
- package/skills/reference/create.md +3 -3
- package/skills/reference/files.md +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,22 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.15.2 (2026-10-18)
|
|
4
|
+
|
|
5
|
+
- Preserve command-set process exit codes instead of resetting them to 0 after CLI dispatch. `ctl` failures now reach scripts and CI with their documented exit codes; successful commands still exit 0 and uncaught exceptions exit 1.
|
|
6
|
+
- Add subprocess regression coverage for `ctl` exit codes 0–8, confirmation before writes, and failures from `check`, `build`, `inspect`, `status` and `test`. Document the existing `ctl` exit-code contract and informational discovery/session results.
|
|
7
|
+
- CLI-only patch: templates and SDK fixtures/examples remain pinned to 0.15.1; executor/lifecycle digests are unchanged.
|
|
8
|
+
|
|
9
|
+
繁中摘要:修正 CLI 在子指令完成後把退出碼覆寫成 0 的問題,讓 `ctl` 失敗正確傳遞至腳本與 CI;成功仍為 0,未捕捉例外仍為 1。新增實際子行程測試,涵蓋 `ctl` 的 0–8 退出碼、寫入確認及其他主要指令的失敗;README 記錄既有退出碼與 discovery/session 資訊結果。此次僅更新 CLI,templates/SDK 維持 0.15.1,executor/lifecycle digest 不變。
|
|
10
|
+
|
|
11
|
+
## 0.15.1 (2026-10-18)
|
|
12
|
+
|
|
13
|
+
- Require explicit `DSM_E2E_SSH` for e2e tunnels and use `dsm` as the default saved-profile target; set `DSM_E2E_TARGET` to reuse an earlier target name. Anonymize internal NAS fixtures and documentation.
|
|
14
|
+
- Pin templates and SDK fixtures/examples to 0.15.1. Add the 0.15.1 executor/lifecycle digest entry and retain the published 0.15.0 pins unchanged.
|
|
15
|
+
- Cover package-named control.sh refusal logs and rejected `.executor` / `.policy.json` staging cleanup.
|
|
16
|
+
- Executor task definitions change: Apps rebuilt with SDK 0.15.1 require one administrator reapproval after upgrade.
|
|
17
|
+
|
|
18
|
+
繁中摘要:e2e tunnel 必須明確設定 `DSM_E2E_SSH`,profile 預設 target 改為 `dsm`(沿用舊 profile 時設定 `DSM_E2E_TARGET`);清理內部 NAS 範例資訊。templates/SDK pins 同步 0.15.1;新增同版三架構 executor/lifecycle digest,保留 0.15.0 pins。驗證 control.sh 拒絕 log 的套件名與 runtime copies 清除。**task 定義改變:App 使用新 SDK 重建升級後,管理員需重新核准一次。**
|
|
19
|
+
|
|
3
20
|
## 0.15.0 (2026-10-18)
|
|
4
21
|
|
|
5
22
|
- Add `synopkgland ctl` host/session management, authenticated API v1 discovery, explicit command trust, typed invocation and fixed job/pagination/NDJSON/upload flows. Commands keep App backend authorization; writes require confirmation.
|
|
@@ -329,7 +346,7 @@ before its timeout.
|
|
|
329
346
|
- The window approval of an upgrade (outdated tasks) makes 11 scheduler calls
|
|
330
347
|
instead of 12: the outdated boot task is deleted by the approval's own first
|
|
331
348
|
EventScheduler get, made moments before the password, instead of a second
|
|
332
|
-
get (2.9 s on
|
|
349
|
+
get (2.9 s on the test NAS in t-0484). The control task is still read again by id
|
|
333
350
|
before its delete (spec/scheduler/v1). A fresh approval stays at 8.
|
|
334
351
|
繁中:升級核准的排程呼叫由 12 降為 11(boot 任務不再於刪除前重讀)。
|
|
335
352
|
- A scope approval reads the executor's selection back every second at most
|
package/README.md
CHANGED
|
@@ -68,6 +68,19 @@ templates checkout and `SYNOPKGLAND_SDK_DIR` to an SDK checkout; SPKs built with
|
|
|
68
68
|
packs the local SDK into its frontend (`npm install --install-links`) without building it: run
|
|
69
69
|
`npm ci && npm run build` in `$SYNOPKGLAND_SDK_DIR/ts` first, and again after SDK changes.
|
|
70
70
|
|
|
71
|
+
To check the CLI's DSM terminology rules against templates without a new templates release:
|
|
72
|
+
|
|
73
|
+
```sh
|
|
74
|
+
SYNOPKGLAND_TEMPLATES_DIR=/path/to/templates npm run test:glossary
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
This compares `OFF_GLOSSARY` and the Italian sentence rule, including regex sources and
|
|
78
|
+
flags. It supports declaration-only rules with read-only use in `glossaryProblems`;
|
|
79
|
+
post-declaration mutations, aliases and other unsupported rule uses fail the check instead of
|
|
80
|
+
comparing a partial snapshot. The command also fails on drift or missing source. It needs a checkout because templates
|
|
81
|
+
0.15.1 does not publish `scripts/test/i18n.test.mjs`. The npm gate runs this check when
|
|
82
|
+
`SYNOPKGLAND_TEMPLATES_DIR` is set; otherwise it explicitly reports that the drift check was skipped.
|
|
83
|
+
|
|
71
84
|
## Capabilities
|
|
72
85
|
|
|
73
86
|
Add them with `init --with …` or later with `synopkgland add <capability…>`.
|
|
@@ -150,12 +163,17 @@ await dismissNotifications(); // when a card shows later
|
|
|
150
163
|
```
|
|
151
164
|
|
|
152
165
|
`runWithTunnel` (the `tunnel --` command) opens an SSH tunnel to the NAS
|
|
153
|
-
(`DSM_E2E_SSH`,
|
|
166
|
+
(`DSM_E2E_SSH`, required, e.g. `admin@nas.example`) on a free loopback port and runs the
|
|
154
167
|
command with `DSM_E2E_URL` set. It masks DSM's session cookies (`id`, `did`,
|
|
155
168
|
`_SSID`), `X-SYNO-TOKEN`, `SynoToken` and `_sid` as `***` in the command's
|
|
156
169
|
output and in the files under `DSM_E2E_OUTPUT_DIR` (default `.e2e`), and makes
|
|
157
170
|
that directory private (700, files 600); saved sessions keep their cookies.
|
|
158
|
-
|
|
171
|
+
Set `DSM_E2E_SSH` to your NAS SSH destination in the environment or a local
|
|
172
|
+
untracked `.env`; an unset or blank value fails before SSH or the command starts.
|
|
173
|
+
`nas.example` is a fixture hostname; replace it with your own host. Accounts come
|
|
174
|
+
from mode 600 files named by `DSM_E2E_ACCOUNT_<USER>`. Saved profiles use
|
|
175
|
+
`DSM_E2E_TARGET` (default `dsm`) and the user as their names; set the target to
|
|
176
|
+
an existing profile's target name to keep reusing it.
|
|
159
177
|
`signIn` and `openDesktop` also register those values as e2e secrets in the
|
|
160
178
|
test's worker, so e2e masks them in its own report. A test that forwards
|
|
161
179
|
requests with `context.route` and `route.fetch` should end with
|
|
@@ -255,6 +273,37 @@ commands and their schemas. Run the listed alias/command with typed flags,
|
|
|
255
273
|
`--help` and `--dry-run`; writes require `--yes`. There is no mutable current
|
|
256
274
|
context. Use `--host a,b` or `--all-hosts` for bounded parallel operations.
|
|
257
275
|
|
|
276
|
+
`ctl` preserves these process exit codes, including with `--json`:
|
|
277
|
+
|
|
278
|
+
| Exit code | Meaning and examples |
|
|
279
|
+
| --- | --- |
|
|
280
|
+
| 0 | Command completed successfully (including help and `--dry-run`). |
|
|
281
|
+
| 1 | Unexpected internal error (`CTL_INTERNAL`), or an uncaught CLI exception. |
|
|
282
|
+
| 2 | Authentication/session failure: `CTL_NOT_AUTHENTICATED`, `CTL_AUTH_FAILED`, `CTL_CREDENTIAL_MISMATCH`, or unavailable OS credential storage. |
|
|
283
|
+
| 3 | Invalid parameters, schema validation or unknown names: `CTL_PARAMS_INVALID`, `CTL_ARGUMENT_INVALID`, `CTL_APP_UNKNOWN`, `CTL_COMMAND_UNKNOWN`. |
|
|
284
|
+
| 4 | Permission refusal that fails the command: `CTL_PERMISSION_DENIED`, `CTL_INVENTORY_DENIED`. |
|
|
285
|
+
| 5 | App/contract failure that fails the command: `CTL_APP_ERROR`, `CTL_RESPONSE_INVALID`, `CTL_JOB_FAILED`. |
|
|
286
|
+
| 6 | Network/HTTP failure or uncertain completion: `CTL_NETWORK`, `CTL_HTTP_ERROR`, `CTL_JOB_TIMEOUT`, `CTL_LOGOUT_FAILED`. |
|
|
287
|
+
| 7 | Confirmation required (`CTL_CONFIRM_REQUIRED`): a write/destructive command or host removal without confirmation; no business operation runs. |
|
|
288
|
+
| 8 | A multi-host command has at least one failed host (including when all hosts fail); inspect each host's error. |
|
|
289
|
+
|
|
290
|
+
For example, `synopkgland ctl studio drafts create --host nas --json` without
|
|
291
|
+
`--yes` returns `ok:false` with `CTL_CONFIRM_REQUIRED` and exits 7 once discovery
|
|
292
|
+
and parameter validation succeed. Missing sessions fail earlier with exit 2.
|
|
293
|
+
Discovery catches per-App description errors and excludes those Apps from its
|
|
294
|
+
results. For example, a changed pinned description (`CTL_DESCRIPTION_CHANGED`)
|
|
295
|
+
or an invalid descriptor (`CTL_DESCRIPTOR_INVALID`) appears in
|
|
296
|
+
`ctl list --host nas --refresh --json` under `data.diagnostics`; the listing still
|
|
297
|
+
returns `ok:true` and exits 0, even if no Apps remain. These diagnostic codes do
|
|
298
|
+
not become process exit codes 4 or 5. Attempting to execute an excluded App with
|
|
299
|
+
`--refresh` instead returns `ok:false`, `CTL_APP_UNKNOWN` and exit 3 because the
|
|
300
|
+
App is absent from discovery results; no business operation runs.
|
|
301
|
+
|
|
302
|
+
`ctl host list/show` can also succeed while reporting `authenticated:false` and
|
|
303
|
+
exit 0. Check discovery `apps`/`diagnostics` and session fields as well as the exit
|
|
304
|
+
code when deciding whether an App is ready.
|
|
305
|
+
After a network or job timeout, query the App's state before retrying a write.
|
|
306
|
+
|
|
258
307
|
Read `synopkgland skill show ctl` for the AI workflow, batch discovery approval,
|
|
259
308
|
cache refresh, host removal/revocation and explicit `ctl.commands` authoring.
|
|
260
309
|
API descriptions are private SPK files named `synopkgland-api.json`, served
|
package/dist/bin.js
CHANGED
|
@@ -3,9 +3,8 @@ import { loadDotEnv } from "./load-env.js";
|
|
|
3
3
|
import { runCli } from "./cli.js";
|
|
4
4
|
loadDotEnv();
|
|
5
5
|
// Package deployment is explicit; this CLI does not publish releases.
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
}).catch((error) => {
|
|
6
|
+
// Successful completion leaves Node's default 0 or the command's exitCode intact.
|
|
7
|
+
runCli(process.argv).catch((error) => {
|
|
9
8
|
console.error(error instanceof Error ? error.message : String(error));
|
|
10
9
|
process.exit(1);
|
|
11
10
|
});
|
package/dist/cli.d.ts
CHANGED
package/dist/cli.js
CHANGED
|
@@ -275,6 +275,7 @@ async function containerBuild(project, arches, options) {
|
|
|
275
275
|
return commitOutputs(options.outDir ? resolve(options.outDir) : project.root, (stage) => forEachArch(arches ?? CONTAINER_ARCH_FAMILIES, jobs, async (arch) => (await build(arch, join(stage, `${packageId}-${arch}.spk`)))
|
|
276
276
|
.outputPath));
|
|
277
277
|
}
|
|
278
|
+
/** Resolves after dispatch; commands may set process.exitCode. Rejections are failures. */
|
|
278
279
|
export async function runCli(argv) {
|
|
279
280
|
if (argv[2] === "ctl") {
|
|
280
281
|
const { runCtl } = await import("./ctl/cli.js");
|
|
@@ -3,7 +3,7 @@ import { collectInstallParameters } from "./load.js";
|
|
|
3
3
|
* Install parameter types that the schema still accepts but that have no working
|
|
4
4
|
* install path on the live package-only route.
|
|
5
5
|
*
|
|
6
|
-
* Measured on
|
|
6
|
+
* Measured on the test NAS 2026-08-27: both end in DSM broken/150 with no Container
|
|
7
7
|
* Manager project, while a no-parameter control package installed fine on the
|
|
8
8
|
* same NAS. `secret` renders its password field and fails; `homes` renders no
|
|
9
9
|
* input at all. `check` reports these as warnings so build keeps working
|
|
@@ -33,7 +33,7 @@ export function validateContract(parsed, contractFile) {
|
|
|
33
33
|
}
|
|
34
34
|
// DSM 7.4 Package Center refuses an SPK whose package identity exceeds 32
|
|
35
35
|
// characters -- "Invalid file format", before the install starts and with
|
|
36
|
-
// nothing written to synopkg.log. Measured on
|
|
36
|
+
// nothing written to synopkg.log. Measured on a DSM 7.4 test NAS: 32 installs, 33
|
|
37
37
|
// does not.
|
|
38
38
|
if (typeof contract.package === "string" && contract.package.length > DSM_PACKAGE_IDENTITY_MAX) {
|
|
39
39
|
const pos = getValuePosition(doc, ["package"]) ?? { line: 1, column: 1 };
|
|
@@ -164,7 +164,7 @@ function validateInstallParameters(parsed, contractFile) {
|
|
|
164
164
|
}
|
|
165
165
|
}
|
|
166
166
|
// A45/A46: both types are in the schema enum but neither has a working
|
|
167
|
-
// install path on the live package-only route -- measured on
|
|
167
|
+
// install path on the live package-only route -- measured on the test NAS as DSM
|
|
168
168
|
// broken/150 with no Container Manager project, against a no-parameter
|
|
169
169
|
// control that installed fine. Refusing here is what makes the failure
|
|
170
170
|
// honest: `homes` used to validate, build a valid SPK, and only break at
|
package/dist/e2e/dsm.d.ts
CHANGED
|
@@ -17,7 +17,7 @@ type Page = ReturnType<NonNullable<ReturnType<typeof surfaceOf>>["page"]>;
|
|
|
17
17
|
type Locator = ReturnType<Page["locator"]>;
|
|
18
18
|
/** The DSM URL the browser opens: DSM_E2E_URL, else the local end of the SSH tunnel. */
|
|
19
19
|
export declare function dsmUrl(env?: Env): string;
|
|
20
|
-
/** ssh arguments forwarding
|
|
20
|
+
/** ssh arguments forwarding loopback ports to the NAS; DSM_E2E_SSH is required. */
|
|
21
21
|
export declare function tunnelArgs(env?: Env): string[];
|
|
22
22
|
/** A loopback port nothing listens on now, for a tunnel that names none. */
|
|
23
23
|
export declare function freePort(): Promise<number>;
|
|
@@ -122,7 +122,7 @@ export declare function waitForDesktop(page?: Page): Promise<void>;
|
|
|
122
122
|
/**
|
|
123
123
|
* Closes every DSM notification card; returns how many were closed. DSM shows
|
|
124
124
|
* them a moment after the desktop (the admin's "Admin account security" card
|
|
125
|
-
* about 2 s later on
|
|
125
|
+
* about 2 s later on a DSM 7.4 test NAS), so it first waits up to
|
|
126
126
|
* DSM_E2E_NOTIFICATION_WAIT_MS (default 5000) for one.
|
|
127
127
|
*/
|
|
128
128
|
export declare function dismissNotifications(page?: Page, env?: Env): Promise<number>;
|
package/dist/e2e/dsm.js
CHANGED
|
@@ -41,11 +41,14 @@ function forwards(env) {
|
|
|
41
41
|
...extra(env.DSM_E2E_TUNNEL_HTTPS_PORT, env.DSM_E2E_REMOTE_HTTPS_PORT, "443"),
|
|
42
42
|
];
|
|
43
43
|
}
|
|
44
|
-
/** ssh arguments forwarding
|
|
44
|
+
/** ssh arguments forwarding loopback ports to the NAS; DSM_E2E_SSH is required. */
|
|
45
45
|
export function tunnelArgs(env = process.env) {
|
|
46
|
+
const target = env.DSM_E2E_SSH?.trim();
|
|
47
|
+
if (!target)
|
|
48
|
+
throw new Error("DSM_E2E_SSH is required to open a NAS tunnel; set it to user@nas.example (using your NAS host).");
|
|
46
49
|
return ["-N", "-o", "BatchMode=yes", "-o", "ExitOnForwardFailure=yes",
|
|
47
50
|
...forwards(env).flatMap(([local, remote]) => ["-L", `127.0.0.1:${local}:127.0.0.1:${remote}`]),
|
|
48
|
-
|
|
51
|
+
target];
|
|
49
52
|
}
|
|
50
53
|
/** A loopback port nothing listens on now, for a tunnel that names none. */
|
|
51
54
|
export function freePort() {
|
|
@@ -80,12 +83,13 @@ export async function runWithTunnel(command, outer = process.env) {
|
|
|
80
83
|
throw new Error("runWithTunnel needs a command to run.");
|
|
81
84
|
const port = Number(outer.DSM_E2E_TUNNEL_PORT || await freePort());
|
|
82
85
|
const env = { ...outer, DSM_E2E_TUNNEL_PORT: String(port) };
|
|
86
|
+
const args = tunnelArgs(env);
|
|
83
87
|
const ports = forwards(env).map(([local]) => Number(local));
|
|
84
88
|
for (const used of ports) {
|
|
85
89
|
if (await portOpen(used))
|
|
86
90
|
throw new Error(`Port ${used} is already in use; name another DSM_E2E_TUNNEL_* port or close it before opening the tunnel.`);
|
|
87
91
|
}
|
|
88
|
-
const ssh = spawn(env.DSM_E2E_SSH_BIN || "ssh",
|
|
92
|
+
const ssh = spawn(env.DSM_E2E_SSH_BIN || "ssh", args, { stdio: ["ignore", "ignore", "inherit"] });
|
|
89
93
|
let sshExited = false;
|
|
90
94
|
ssh.once("exit", () => { sshExited = true; });
|
|
91
95
|
try {
|
|
@@ -93,7 +97,7 @@ export async function runWithTunnel(command, outer = process.env) {
|
|
|
93
97
|
for (const waiting of ports) {
|
|
94
98
|
while (!(await portOpen(waiting))) {
|
|
95
99
|
if (sshExited || Date.now() > deadline)
|
|
96
|
-
throw new Error(`The SSH tunnel to ${
|
|
100
|
+
throw new Error(`The SSH tunnel to ${args.at(-1)} did not open on port ${waiting}.`);
|
|
97
101
|
await new Promise(accept => setTimeout(accept, 100));
|
|
98
102
|
}
|
|
99
103
|
}
|
|
@@ -364,7 +368,7 @@ const notificationCloseSelector = ".v-notification-container .v-notification-pan
|
|
|
364
368
|
/**
|
|
365
369
|
* Closes every DSM notification card; returns how many were closed. DSM shows
|
|
366
370
|
* them a moment after the desktop (the admin's "Admin account security" card
|
|
367
|
-
* about 2 s later on
|
|
371
|
+
* about 2 s later on a DSM 7.4 test NAS), so it first waits up to
|
|
368
372
|
* DSM_E2E_NOTIFICATION_WAIT_MS (default 5000) for one.
|
|
369
373
|
*/
|
|
370
374
|
export async function dismissNotifications(page = dsmPage(), env = process.env) {
|
|
@@ -454,7 +458,7 @@ async function restoredSessionLive(page) {
|
|
|
454
458
|
*/
|
|
455
459
|
export async function signIn(app, user = "admin", env = process.env) {
|
|
456
460
|
const url = dsmUrl(env);
|
|
457
|
-
const file = profilePath(env.DSM_E2E_TARGET || "
|
|
461
|
+
const file = profilePath(env.DSM_E2E_TARGET || "dsm", user, env);
|
|
458
462
|
const context = surface().context();
|
|
459
463
|
const profile = loadProfile(file, url);
|
|
460
464
|
if (profile)
|
|
@@ -8,7 +8,7 @@ import type { SharePermissionRow } from "./share-permission.js";
|
|
|
8
8
|
* the install proceed, while an empty list would read as a real denial and
|
|
9
9
|
* refuse the install on a transport error.
|
|
10
10
|
*
|
|
11
|
-
* The APIs used were confirmed present on DSM 7.4 (
|
|
11
|
+
* The APIs used were confirmed present on DSM 7.4 (the test NAS):
|
|
12
12
|
* `SYNO.Core.Share.Permission`, `SYNO.Core.Group`, `SYNO.Core.Group.Member`.
|
|
13
13
|
* `SYNO.Core.User.Group` also exists on the NAS but has no generated
|
|
14
14
|
* synoagentcli operation, which is why membership is derived group-by-group.
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
* extra_values directly, and share permission can change after install.
|
|
11
11
|
*
|
|
12
12
|
* The rows are SYNO.Core.Share.Permission v1 output verbatim. Read the tests
|
|
13
|
-
* before changing the predicate: on
|
|
13
|
+
* before changing the predicate: on the test NAS, `newadmin` has is_writable=false on
|
|
14
14
|
* its own row for `pkgland-p0b` and still writes there, because the
|
|
15
15
|
* `administrators` group row carries the grant. A user-row-only check rejects
|
|
16
16
|
* the only configuration with live proof behind it.
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
* extra_values directly, and share permission can change after install.
|
|
11
11
|
*
|
|
12
12
|
* The rows are SYNO.Core.Share.Permission v1 output verbatim. Read the tests
|
|
13
|
-
* before changing the predicate: on
|
|
13
|
+
* before changing the predicate: on the test NAS, `newadmin` has is_writable=false on
|
|
14
14
|
* its own row for `pkgland-p0b` and still writes there, because the
|
|
15
15
|
* `administrators` group row carries the grant. A user-row-only check rejects
|
|
16
16
|
* the only configuration with live proof behind it.
|
|
@@ -9,5 +9,16 @@
|
|
|
9
9
|
"armv7": "cafc97c89f5645c14a8cf31d6d9f258394ed6f2d086478a7aebd04d57c274b4e"
|
|
10
10
|
},
|
|
11
11
|
"goToolchain": "go1.26.9"
|
|
12
|
+
},
|
|
13
|
+
"0.15.1": {
|
|
14
|
+
"x86_64": "4f47fb43f6e60dbad064afb0bbec96dd009fa046670e2c4b590b128a855cb404",
|
|
15
|
+
"aarch64": "c903b09ad657a838f0c62b6688e65e9094572b22b969770fca92fcc3640a5364",
|
|
16
|
+
"armv7": "8178d90de7ea720530bb9496e50ed118086b0e57382d84675aadc68f482d219f",
|
|
17
|
+
"lifecycle": {
|
|
18
|
+
"x86_64": "d65500c4f04713a32b45d360059281391acfa5e00ee24895fdd2de7b82735372",
|
|
19
|
+
"aarch64": "94f6cd119186ff482b8aea56541dd1f9d53c147566bc1efdca6f5a1c86198331",
|
|
20
|
+
"armv7": "cafc97c89f5645c14a8cf31d6d9f258394ed6f2d086478a7aebd04d57c274b4e"
|
|
21
|
+
},
|
|
22
|
+
"goToolchain": "go1.26.9"
|
|
12
23
|
}
|
|
13
24
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@synopackageland/cli",
|
|
3
|
-
"version": "0.15.
|
|
3
|
+
"version": "0.15.2",
|
|
4
4
|
"description": "Syno Package Land Developer Kit CLI",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -19,6 +19,7 @@
|
|
|
19
19
|
"scripts": {
|
|
20
20
|
"build": "rm -rf dist && tsc",
|
|
21
21
|
"test": "npm run build && node --import tsx --test test/*.test.ts test/ctl/*.test.ts",
|
|
22
|
+
"test:glossary": "node --test scripts/test/glossary-drift.test.mjs",
|
|
22
23
|
"test:scripts": "node --test scripts/test/gate.test.mjs scripts/test/release.test.mjs scripts/test/pack-docker.test.mjs scripts/test/npm-pack.test.mjs scripts/test/executor-bump.test.mjs scripts/test/smoke-exports.test.mjs scripts/test/executor-digests.test.mjs scripts/test/parallel.test.mjs scripts/test/scaffold-matrix.test.mjs scripts/test/frontend-deps.test.mjs scripts/test/docker-gate.test.mjs scripts/test/gate-plan.test.mjs scripts/test/gates.test.mjs",
|
|
23
24
|
"gates": "bash scripts/ci/gates.sh",
|
|
24
25
|
"gates:fast": "bash scripts/ci/gates.sh --fast",
|
|
@@ -26,7 +27,7 @@
|
|
|
26
27
|
},
|
|
27
28
|
"dependencies": {
|
|
28
29
|
"@napi-rs/keyring": "^2.1.1",
|
|
29
|
-
"@synopackageland/templates": "0.15.
|
|
30
|
+
"@synopackageland/templates": "0.15.1",
|
|
30
31
|
"ajv": "^8.17.1",
|
|
31
32
|
"commander": "^13.1.0",
|
|
32
33
|
"json-canonicalize": "^3.0.1",
|
package/skills/SKILL.md
CHANGED
|
@@ -129,7 +129,7 @@ sweep forward the cancel instead of admitting it.
|
|
|
129
129
|
`ExportExample` creates a private JSON file, calls `MoveFromStaging` once, and
|
|
130
130
|
repeats the original request on repeated IDs. Review `example.go` to
|
|
131
131
|
adapt a real producer; show Saved only from a protected committed receipt.
|
|
132
|
-
- Wizard support is measured on
|
|
132
|
+
- Wizard support is measured on the test NAS DSM 7.4 via a session-only SSH local
|
|
133
133
|
forward. This is not proof of other DSM releases or full writer/reboot behavior.
|
|
134
134
|
|
|
135
135
|
## 開啟方式
|
|
@@ -23,7 +23,7 @@ compose:
|
|
|
23
23
|
network: {egress: isolated}
|
|
24
24
|
# Private package binds are rejected by DSM build; keep compatible upstream named volumes.
|
|
25
25
|
identity_gateway:
|
|
26
|
-
sdk: 0.15.
|
|
26
|
+
sdk: 0.15.1
|
|
27
27
|
mode: none
|
|
28
28
|
access: dsm-users
|
|
29
29
|
listen: {http: 28000}
|
|
@@ -151,7 +151,7 @@ declared availability readiness, LAN isolation/egress, protected grant lifecycle
|
|
|
151
151
|
ACLs, two-user visibility and clean install/upgrade/reboot/stop/uninstall before
|
|
152
152
|
calling the package supported on a NAS. No NAS is accessed by the build gate.
|
|
153
153
|
|
|
154
|
-
The unified Compose contract requires SDK, CLI and templates 0.15.
|
|
154
|
+
The unified Compose contract requires SDK, CLI and templates 0.15.1. Use the published, matching versions together; the legacy packaging route and bridge publish mode are removed.
|
|
155
155
|
|
|
156
156
|
Primary override files, and each included override set, must share a directory. Cross-directory override bases are rejected before loader I/O. Included implicit .env files are confined, inventoried and preserved; symlinks are rejected.
|
|
157
157
|
|
|
@@ -198,7 +198,7 @@ resources and does not migrate or delete retained data.
|
|
|
198
198
|
Bridge web ingress uses a dedicated `sdk_ingress` internal bridge. The web service
|
|
199
199
|
receives a fixed IPv4 address, and the package-account gateway connects directly
|
|
200
200
|
to its declared container port. Internal bridges were reachable by package-account
|
|
201
|
-
hooks in
|
|
201
|
+
hooks in feasibility probes on an SA3200D and another test NAS. No HAProxy helper, published
|
|
202
202
|
container port, Docker socket or host root is required. Explicit internet egress
|
|
203
203
|
still adds the existing outbound bridge; other App services retain their internal networks.
|
|
204
204
|
|
|
@@ -231,7 +231,7 @@ Legacy `.sc` residue is not purged by this topology. Migrating an already publis
|
|
|
231
231
|
installation requires a separately authorized cleanup or an official worker fix;
|
|
232
232
|
a no-publish rebuild cannot claim to remove historical service registrations.
|
|
233
233
|
|
|
234
|
-
On
|
|
234
|
+
On the test NAS DSM 7.4/Docker 24.0.2, internal networks blocked tested external IPv4
|
|
235
235
|
and external DNS, but allowed NAS host services through the bridge gateway and
|
|
236
236
|
NAS LAN address. Therefore `isolated` is not a host-service firewall. IPv6 had
|
|
237
237
|
no usable default route on this fixture, which is not proof of an IPv6 firewall.
|
|
@@ -245,7 +245,7 @@ Set-Cookie. Other App sessions/native cookies retain their normal semantics.
|
|
|
245
245
|
Cookies are shared across ports on the same hostname, so filtering only this
|
|
246
246
|
package's SDK cookie would leak another App's SDK session to an upstream.
|
|
247
247
|
|
|
248
|
-
Historical publish-topology
|
|
248
|
+
Historical publish-topology validation on the test NAS was release-blocked: the Container Manager
|
|
249
249
|
24.0.2-1706 docker-project worker can return profile error 1202 on update,
|
|
250
250
|
which Package Center reports as postinst-worker failure 277. The owned test
|
|
251
251
|
project and a real Vaultwarden package reproduced this; switching Compose
|
|
@@ -328,7 +328,7 @@ The [docker-project contract](https://help.synology.com/developer-guide/resource
|
|
|
328
328
|
documents project deletion on PREUNINST and build flags, without a separate
|
|
329
329
|
package-account profile-release option.
|
|
330
330
|
|
|
331
|
-
The original isolated Header fixture on
|
|
331
|
+
The original isolated Header fixture on the test NAS installed and started from a
|
|
332
332
|
clean configuration, then its upgrade still returned port conflict → 1202 →
|
|
333
333
|
277. Historical `.sc` residue alone is therefore insufficient to explain or
|
|
334
334
|
resolve that environment's upgrade failure. Preserve the timestamp/PID chain;
|
|
@@ -40,8 +40,8 @@ rootOps 與 delegate 已共用管理員 PasswordConfirm 核准的 protected exec
|
|
|
40
40
|
backend 寫 `runtime: python|node|rust`(`runtime: go` 等同 `kind: go`)。CGI 是 SDK 的 Go runtime(`bridge/cmd/runtime`),它驗證 DSM session、entry 與 `permissions.json` 後,以套件帳號(不是 root)每個請求啟動一次 App。App 放在 ui 以外的目錄,打包成 `target/app/`,build 寫入 `bridge.json`(spec/bridge/v1)並把它與 `permissions.json` 的 sha256 編進 runtime。
|
|
41
41
|
|
|
42
42
|
```yaml
|
|
43
|
-
backend: {runtime: python, dir: app, entry: main.py, sdk: 0.15.
|
|
44
|
-
backend: {runtime: node, dir: app, entry: main.mjs, sdk: 0.15.
|
|
43
|
+
backend: {runtime: python, dir: app, entry: main.py, sdk: 0.15.1}
|
|
44
|
+
backend: {runtime: node, dir: app, entry: main.mjs, sdk: 0.15.1, node: {package: Node.js_v20}}
|
|
45
45
|
backend: {runtime: rust, dir: app, entry: hello, methods: [hello.greet]}
|
|
46
46
|
```
|
|
47
47
|
|
|
@@ -213,7 +213,7 @@ sweep forward the cancel instead of admitting it.
|
|
|
213
213
|
`ExportExample` creates a private JSON file, calls `MoveFromStaging` once, and
|
|
214
214
|
repeats the original request on repeated IDs. Review `example.go` to
|
|
215
215
|
adapt a real producer; show Saved only from a protected committed receipt.
|
|
216
|
-
- Wizard support is measured on
|
|
216
|
+
- Wizard support is measured on the test NAS DSM 7.4 via a session-only SSH local
|
|
217
217
|
forward. This is not proof of other DSM releases or full writer/reboot behavior.
|
|
218
218
|
|
|
219
219
|
## 開啟方式
|
|
@@ -118,7 +118,7 @@ sweep forward the cancel instead of admitting it.
|
|
|
118
118
|
`ExportExample` creates a private JSON file, calls `MoveFromStaging` once, and
|
|
119
119
|
repeats the original request on repeated IDs. Review `example.go` to
|
|
120
120
|
adapt a real producer; show Saved only from a protected committed receipt.
|
|
121
|
-
- Wizard support is measured on
|
|
121
|
+
- Wizard support is measured on the test NAS DSM 7.4 via a session-only SSH local
|
|
122
122
|
forward. This is not proof of other DSM releases or full writer/reboot behavior.
|
|
123
123
|
|
|
124
124
|
The pinned scope-selection task also binds the protected generation observed before confirmation. The executor compares it under the publish lock and refuses stale tasks, so a retained task cannot replay an older selection after another confirmation.
|