@synopackageland/templates 0.10.2 → 0.10.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (30) hide show
  1. package/README.md +8 -3
  2. package/package.json +1 -1
  3. package/templates/_shared/AGENTS.md +42 -0
  4. package/templates/_shared/dsm-scripts/postinst +53 -0
  5. package/templates/_shared/dsm-scripts/postuninst +51 -0
  6. package/templates/_shared/dsm-scripts/postupgrade +54 -0
  7. package/templates/_shared/dsm-scripts/preinst +49 -0
  8. package/templates/_shared/dsm-scripts/preuninst +50 -0
  9. package/templates/_shared/dsm-scripts/preupgrade +52 -0
  10. package/templates/_shared/dsm-scripts/start-stop-status +59 -0
  11. package/templates/native-go/README.md +18 -2
  12. package/templates/native-go/backend/THIRD_PARTY_NOTICES.txt +1 -1
  13. package/templates/native-go/backend/cmd/app/main.go +4 -4
  14. package/templates/native-go/backend/cmd/app/main_test.go +4 -4
  15. package/templates/native-go/backend/go.mod +1 -1
  16. package/templates/native-go/backend/go.sum +2 -2
  17. package/templates/native-go-delegate/frontend/test/capability-panels.test.ts +3 -3
  18. package/templates/native-go-root-ops/backend/internal/app/executor.go +3 -3
  19. package/templates/native-go-root-ops/backend/internal/app/executor_test.go +8 -0
  20. package/templates/native-go-vue/README.md +3 -0
  21. package/templates/native-go-vue/frontend/package-lock.json +9 -9
  22. package/templates/native-go-vue/frontend/package.json +2 -2
  23. package/templates/native-go-vue/frontend/src/i18n/zh-CN.json +1 -1
  24. package/templates/native-node/README.md +7 -0
  25. package/templates/native-node/dsm-package.yaml +1 -1
  26. package/templates/native-python/README.md +7 -0
  27. package/templates/native-python/dsm-package.yaml +1 -1
  28. package/templates/native-rust/README.md +7 -0
  29. package/templates/native-rust/backend/Cargo.lock +2 -2
  30. package/templates/native-rust/backend/Cargo.toml +1 -1
package/README.md CHANGED
@@ -18,7 +18,7 @@ the files an App owns; the SDK (`github.com/synopackageland/syno-pkg-land-sdk`,
18
18
  | `native-{python,node,rust}-vue` | The README and `.gitignore` of the Vue shell |
19
19
  | `binary` | A prebuilt static CGI (`example/main.go`) with a plain UI |
20
20
  | `compose` | Container Manager project (`compose.yaml`, `synology-app.yaml`) |
21
- | `_shared` | Agent guides, template license, Go license texts, third-party notice writer |
21
+ | `_shared` | Agent guides, template license, Go license texts, third-party notice writer, `dsm-scripts/` (DSM lifecycle scripts) |
22
22
 
23
23
  Overlays are copied over `native-go` in the order root-ops, vue, jobs, delegate.
24
24
  An overlay may replace `internal/app/*_none.go` (the CLI deletes the matching
@@ -31,14 +31,19 @@ also makes `app.status` and `report.save` `authenticated`, and the Rust
31
31
  manifest gets the overlays' methods. `init` copies the App SDK into
32
32
  `backend/vendor` (Python `synopkgland_app`; Node `@synopackageland/app`, its
33
33
  package.json without devDependencies and scripts, and `dist/`).
34
+ `_shared/dsm-scripts/` holds DSM's lifecycle scripts (`preinst`, `postinst`, `preupgrade`,
35
+ `postupgrade`, `preuninst`, `postuninst`, `start-stop-status`), each commented and doing
36
+ nothing; `init` (CLI 0.10.3 and later) copies them into every project's `dsm-scripts/`, and `build`
37
+ calls each one from the generated script of the same name (`synopkgland skill show lifecycle`).
34
38
  The placeholders `__PACKAGE__` (also the Go module path), `__DISPLAY_NAME__` and
35
39
  `__BACKGROUND_ENABLED__` are substituted by `init`.
36
40
 
37
- `backend/go.mod` requires `github.com/synopackageland/syno-pkg-land-sdk v0.10.2`; that line is
41
+ `backend/go.mod` requires `github.com/synopackageland/syno-pkg-land-sdk v0.10.3`; that line is
38
42
  the single source of the SDK version and `frontend/package.json` pins
39
43
  `@synopackageland/sdk` to the same version. Python and Node.js name it in
40
44
  `backend.sdk`, Rust in the `syno-pkg-land-app` tag of `backend/Cargo.toml`
41
- (and `Cargo.lock`); all of them pin 0.10.2.
45
+ (and `Cargo.lock`); all of them pin 0.10.3. The Vue frontend pins
46
+ `@synopackageland/ui` 0.9.5.
42
47
 
43
48
  ## Notice writer
44
49
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@synopackageland/templates",
3
- "version": "0.10.2",
3
+ "version": "0.10.3",
4
4
  "description": "Project templates for synopkgland init: native-go, native-python, native-node and native-rust (plain or Vue, with jobs, delegate and root-ops overlays), binary and compose.",
5
5
  "license": "MIT",
6
6
  "repository": {"type": "git", "url": "git+https://github.com/synopackageland/syno-pkg-land-templates.git"},
@@ -25,6 +25,27 @@ From the project root, run `synopkgland check --json`,
25
25
  For Vue, first run `npm ci`, `npm test` and `npm run build` in frontend.
26
26
  Do not connect to or change a NAS without explicit task authorization.
27
27
 
28
+ ## Lifecycle scripts (`dsm-scripts/`)
29
+
30
+ `dsm-scripts/` holds DSM's own lifecycle scripts (preinst, postinst, preupgrade,
31
+ postupgrade, preuninst, postuninst, start-stop-status); each does nothing until
32
+ edited. The build calls each from the generated script of the same name, as the
33
+ package account, around the SDK's own steps. Read
34
+ `synopkgland skill show lifecycle` before editing them; never add root work,
35
+ never repeat SDK steps, and keep only DSM script names in that folder
36
+ (build tools belong in `scripts/`).
37
+
38
+ Root cleanup or migration after an install or upgrade (for example removing
39
+ what an older version left in a root-owned place) is not a lifecycle script:
40
+ name a `root/` program as `root.start` in `dsm-package.yaml`. The executor runs
41
+ it as root once after each install or upgrade, at the first start of the new
42
+ version and only while the administrator's approval is live; it must not
43
+ declare `run` or `job`, should be safe to run twice, and declaring it means the
44
+ administrator approves the App again. Package start only requests the
45
+ executor, so `postinst`, `postupgrade` and `start` cannot wait for the step:
46
+ keep the unprivileged follow-up in `dsm-scripts/`, and read the step's result
47
+ from the executor `status` (`start_step`).
48
+
28
49
  ## Who may use the app (`entry`)
29
50
 
30
51
  Omit `entry` to keep legacy access. `entry: admin-only` admits DSM administrators
@@ -62,6 +83,27 @@ from the portal panel (`ctx.portal.register()`), which the executor's approved
62
83
  DSM resource. Beyond that, support is detected (SYNO.API.Info), not
63
84
  version-gated.
64
85
 
86
+ ### Login page look (`login_style`)
87
+
88
+ A portal build gives the Login Portal entry a default look: a neutral
89
+ wallpaper, its thumbnail in Control Panel > Login Portal > Applications, the
90
+ login button colour `#2F6BD8`, and the App icon beside the NAS name on the
91
+ alias login page. To use your own images, add to dsm-package.yaml (each key is
92
+ optional; `wallpaper` and `wallpaper_thumbnail` go together):
93
+
94
+ ```yaml
95
+ login_style:
96
+ color: "#0F766E" # login button, #RGB or #RRGGBB
97
+ wallpaper: branding/login-wallpaper.jpg # JPEG, at least 1920x1080, at most 2 MiB
98
+ wallpaper_thumbnail: branding/login-thumbnail.jpg # JPEG 320x200, the same picture
99
+ title: branding/login-title.png # PNG 680x124, replaces icon and NAS name
100
+ ```
101
+
102
+ `synopkgland check` refuses a missing image or one of the wrong format or size
103
+ (`STANDALONE_LOGIN_STYLE_IMAGE_INVALID`) and prints the style in effect;
104
+ `synopkgland inspect <spk>` shows what the SPK carries. Use only images you
105
+ may redistribute; never copy Synology's.
106
+
65
107
  ## Background jobs (`jobs`)
66
108
 
67
109
  The worker runs the App's Work for one claimed job at a time, as the package
@@ -0,0 +1,53 @@
1
+ #!/bin/sh
2
+ # postinst: runs after DSM copied the package files, at install and at the
3
+ # install half of an upgrade (SYNOPKG_PKG_STATUS=INSTALL or UPGRADE). During an
4
+ # upgrade, postupgrade follows it.
5
+ #
6
+ # Order: the SDK's postinst steps first (executor, Shared Folder, portal), then
7
+ # this script. If the SDK steps fail, this script does not run.
8
+ #
9
+ # Runs as the package account, never as root. Root work belongs in root/*.sh
10
+ # through the SDK executor (synopkgland add root-ops). Root cleanup or
11
+ # migration after an install or upgrade is the executor's start step
12
+ # (root.start): it runs once per new version, at the executor's first start
13
+ # after an administrator's approval. Package start only requests the executor
14
+ # and does not wait for it, so this script cannot see that step; keep only the
15
+ # unprivileged follow-up here. The App reads the step's result from the
16
+ # executor status (start_step).
17
+ #
18
+ # Environment (DSM sets these; the SDK passes them through unchanged):
19
+ # SYNOPKG_PKGNAME, SYNOPKG_PKGVER package id and the version being installed
20
+ # SYNOPKG_OLD_PKGVER the previous version (upgrade only)
21
+ # SYNOPKG_PKG_STATUS INSTALL, UPGRADE, UNINSTALL, START, STOP,
22
+ # or empty at boot and shutdown
23
+ # SYNOPKG_PKGDEST installed files (/var/packages/<id>/target)
24
+ # SYNOPKG_PKGVAR, SYNOPKG_PKGHOME package data and home; kept across upgrades
25
+ # SYNOPKG_TEMP_LOGFILE write a message for the user here
26
+ # SYNOPKG_DSM_LANGUAGE DSM language code (enu, cht, chs, jpn, ...)
27
+ # SYNOPKG_DSM_VERSION_MAJOR, _MINOR, _BUILD, SYNOPKG_DSM_ARCH
28
+ # SYNOPKG_USERNAME who started the action; empty when DSM did
29
+ #
30
+ # Install wizard answers arrive as environment variables named by each item's
31
+ # key ("true", "false" or text). The SDK's own wizard keys are not for you.
32
+ #
33
+ # Exit code: 0 is success; non-zero leaves the package in a failed state
34
+ # (DSM's own rule).
35
+ #
36
+ # To tell the user why something failed, write plain text to the message file
37
+ # and exit non-zero; DSM shows it after the action:
38
+ # echo "Port 8080 is already in use." > "$SYNOPKG_TEMP_LOGFILE"
39
+ # exit 1
40
+ #
41
+ # Do not:
42
+ # - start, stop or clean up what the SDK owns (daemon, background worker,
43
+ # executor, portal, Shared Folder, app data deletion, uninstall.purge);
44
+ # - need root or try to gain it;
45
+ # - block for long: neither DSM nor the SDK documents a timeout, so a script
46
+ # that hangs blocks Package Center.
47
+ #
48
+ # More: synopkgland skill show lifecycle
49
+
50
+ # Example: create the App's own folder in its data directory.
51
+ # mkdir -p "$SYNOPKG_PKGVAR/cache" || exit 1
52
+
53
+ exit 0
@@ -0,0 +1,51 @@
1
+ #!/bin/sh
2
+ # postuninst: runs after DSM deleted the package files. It runs at uninstall
3
+ # (SYNOPKG_PKG_STATUS=UNINSTALL) AND during an upgrade (UPGRADE), where it is
4
+ # the OLD version's script. SYNOPKG_PKGDEST no longer exists here.
5
+ #
6
+ # Order: the SDK's postuninst steps first (deleting app data when the user
7
+ # chose it in the uninstall wizard), then this script. If the SDK steps fail,
8
+ # this script does not run.
9
+ #
10
+ # Runs as the package account, never as root. Root work belongs in root/*.sh
11
+ # through the SDK executor (synopkgland add root-ops).
12
+ #
13
+ # Environment (DSM sets these; the SDK passes them through unchanged):
14
+ # SYNOPKG_PKGNAME, SYNOPKG_PKGVER package id and the version being installed
15
+ # SYNOPKG_OLD_PKGVER the previous version (upgrade only)
16
+ # SYNOPKG_PKG_STATUS INSTALL, UPGRADE, UNINSTALL, START, STOP,
17
+ # or empty at boot and shutdown
18
+ # SYNOPKG_PKGDEST installed files (/var/packages/<id>/target)
19
+ # SYNOPKG_PKGVAR, SYNOPKG_PKGHOME package data and home; kept across upgrades
20
+ # SYNOPKG_TEMP_LOGFILE write a message for the user here
21
+ # SYNOPKG_DSM_LANGUAGE DSM language code (enu, cht, chs, jpn, ...)
22
+ # SYNOPKG_DSM_VERSION_MAJOR, _MINOR, _BUILD, SYNOPKG_DSM_ARCH
23
+ # SYNOPKG_USERNAME who started the action; empty when DSM did
24
+ #
25
+ # Uninstall wizard answers arrive as environment variables named by each
26
+ # item's key. The SDK's uninstall wizard already asks about deleting app data.
27
+ #
28
+ # Exit code: DSM documents no meaning for postuninst; exit 0 unless something
29
+ # the user must know failed.
30
+ #
31
+ # To tell the user why something failed, write plain text to the message file
32
+ # and exit non-zero; DSM shows it after the action:
33
+ # echo "Port 8080 is already in use." > "$SYNOPKG_TEMP_LOGFILE"
34
+ # exit 1
35
+ #
36
+ # Do not:
37
+ # - start, stop or clean up what the SDK owns (daemon, background worker,
38
+ # executor, portal, Shared Folder, app data deletion, uninstall.purge);
39
+ # - need root or try to gain it;
40
+ # - block for long: neither DSM nor the SDK documents a timeout, so a script
41
+ # that hangs blocks Package Center.
42
+ #
43
+ # More: synopkgland skill show lifecycle
44
+
45
+ # Example: on a real uninstall only, remove a file the App wrote outside its
46
+ # data directories.
47
+ # if [ "${SYNOPKG_PKG_STATUS:-}" = UNINSTALL ]; then
48
+ # rm -f "$SYNOPKG_PKGHOME/settings-backup.json"
49
+ # fi
50
+
51
+ exit 0
@@ -0,0 +1,54 @@
1
+ #!/bin/sh
2
+ # postupgrade: runs at the end of an upgrade, after the new postinst
3
+ # (SYNOPKG_PKG_STATUS=UPGRADE). It is the NEW version's script.
4
+ #
5
+ # Order: the SDK's postupgrade steps first, then this script. If the SDK steps
6
+ # fail, this script does not run.
7
+ #
8
+ # Runs as the package account, never as root. Root work belongs in root/*.sh
9
+ # through the SDK executor (synopkgland add root-ops). Root cleanup or
10
+ # migration after an install or upgrade is the executor's start step
11
+ # (root.start): it runs once per new version, at the executor's first start
12
+ # after an administrator's approval. Package start only requests the executor
13
+ # and does not wait for it, so this script cannot see that step; keep only the
14
+ # unprivileged follow-up here. The App reads the step's result from the
15
+ # executor status (start_step).
16
+ #
17
+ # Environment (DSM sets these; the SDK passes them through unchanged):
18
+ # SYNOPKG_PKGNAME, SYNOPKG_PKGVER package id and the version being installed
19
+ # SYNOPKG_OLD_PKGVER the previous version (upgrade only)
20
+ # SYNOPKG_PKG_STATUS INSTALL, UPGRADE, UNINSTALL, START, STOP,
21
+ # or empty at boot and shutdown
22
+ # SYNOPKG_PKGDEST installed files (/var/packages/<id>/target)
23
+ # SYNOPKG_PKGVAR, SYNOPKG_PKGHOME package data and home; kept across upgrades
24
+ # SYNOPKG_TEMP_LOGFILE write a message for the user here
25
+ # SYNOPKG_DSM_LANGUAGE DSM language code (enu, cht, chs, jpn, ...)
26
+ # SYNOPKG_DSM_VERSION_MAJOR, _MINOR, _BUILD, SYNOPKG_DSM_ARCH
27
+ # SYNOPKG_USERNAME who started the action; empty when DSM did
28
+ #
29
+ # Upgrade wizard answers arrive as environment variables named by each item's
30
+ # key. A silent upgrade shows no wizard, so always have a default.
31
+ #
32
+ # Exit code: 0 is success; non-zero leaves the package in a failed state
33
+ # (DSM's own rule).
34
+ #
35
+ # To tell the user why something failed, write plain text to the message file
36
+ # and exit non-zero; DSM shows it after the action:
37
+ # echo "Port 8080 is already in use." > "$SYNOPKG_TEMP_LOGFILE"
38
+ # exit 1
39
+ #
40
+ # Do not:
41
+ # - start, stop or clean up what the SDK owns (daemon, background worker,
42
+ # executor, portal, Shared Folder, app data deletion, uninstall.purge);
43
+ # - need root or try to gain it;
44
+ # - block for long: neither DSM nor the SDK documents a timeout, so a script
45
+ # that hangs blocks Package Center.
46
+ #
47
+ # More: synopkgland skill show lifecycle
48
+
49
+ # Example: migrate the App's data once, from versions before 1.2.
50
+ # case "${SYNOPKG_OLD_PKGVER:-}" in
51
+ # 0.*|1.0*|1.1*) "$SYNOPKG_PKGDEST/app/migrate" || exit 1 ;;
52
+ # esac
53
+
54
+ exit 0
@@ -0,0 +1,49 @@
1
+ #!/bin/sh
2
+ # preinst: runs before DSM copies the package files, at install and at the
3
+ # install half of an upgrade (SYNOPKG_PKG_STATUS=INSTALL or UPGRADE).
4
+ #
5
+ # Order: the SDK's preinst steps first, then this script. If the SDK steps
6
+ # fail, this script does not run.
7
+ #
8
+ # Runs as the package account, never as root. Root work belongs in root/*.sh
9
+ # through the SDK executor (synopkgland add root-ops).
10
+ #
11
+ # Environment (DSM sets these; the SDK passes them through unchanged):
12
+ # SYNOPKG_PKGNAME, SYNOPKG_PKGVER package id and the version being installed
13
+ # SYNOPKG_OLD_PKGVER the previous version (upgrade only)
14
+ # SYNOPKG_PKG_STATUS INSTALL, UPGRADE, UNINSTALL, START, STOP,
15
+ # or empty at boot and shutdown
16
+ # SYNOPKG_PKGDEST installed files (/var/packages/<id>/target)
17
+ # SYNOPKG_PKGVAR, SYNOPKG_PKGHOME package data and home; kept across upgrades
18
+ # SYNOPKG_TEMP_LOGFILE write a message for the user here
19
+ # SYNOPKG_DSM_LANGUAGE DSM language code (enu, cht, chs, jpn, ...)
20
+ # SYNOPKG_DSM_VERSION_MAJOR, _MINOR, _BUILD, SYNOPKG_DSM_ARCH
21
+ # SYNOPKG_USERNAME who started the action; empty when DSM did
22
+ #
23
+ # Install wizard answers arrive as environment variables named by each item's
24
+ # key ("true", "false" or text). The SDK's own wizard keys are not for you.
25
+ #
26
+ # Exit code: 0 continues; non-zero aborts the install or upgrade (DSM's own
27
+ # rule). Keep this to checks: nothing undoes a change made here.
28
+ #
29
+ # To tell the user why something failed, write plain text to the message file
30
+ # and exit non-zero; DSM shows it after the action:
31
+ # echo "Port 8080 is already in use." > "$SYNOPKG_TEMP_LOGFILE"
32
+ # exit 1
33
+ #
34
+ # Do not:
35
+ # - start, stop or clean up what the SDK owns (daemon, background worker,
36
+ # executor, portal, Shared Folder, app data deletion, uninstall.purge);
37
+ # - need root or try to gain it;
38
+ # - block for long: neither DSM nor the SDK documents a timeout, so a script
39
+ # that hangs blocks Package Center.
40
+ #
41
+ # More: synopkgland skill show lifecycle
42
+
43
+ # Example: refuse DSM older than 7.2.
44
+ # if [ "${SYNOPKG_DSM_VERSION_MAJOR:-7}" -eq 7 ] && [ "${SYNOPKG_DSM_VERSION_MINOR:-0}" -lt 2 ]; then
45
+ # echo "This App needs DSM 7.2 or later." > "$SYNOPKG_TEMP_LOGFILE"
46
+ # exit 1
47
+ # fi
48
+
49
+ exit 0
@@ -0,0 +1,50 @@
1
+ #!/bin/sh
2
+ # preuninst: runs before DSM deletes the package files, after DSM stopped the
3
+ # package. It runs at uninstall (SYNOPKG_PKG_STATUS=UNINSTALL) AND during an
4
+ # upgrade (UPGRADE), where it is the OLD version's script.
5
+ #
6
+ # Order: this script first, then the SDK's preuninst steps (stop, the App's
7
+ # uninstall.purge, executor cleanup).
8
+ #
9
+ # Runs as the package account, never as root. Root work belongs in root/*.sh
10
+ # through the SDK executor (synopkgland add root-ops).
11
+ #
12
+ # Environment (DSM sets these; the SDK passes them through unchanged):
13
+ # SYNOPKG_PKGNAME, SYNOPKG_PKGVER package id and the version being installed
14
+ # SYNOPKG_OLD_PKGVER the previous version (upgrade only)
15
+ # SYNOPKG_PKG_STATUS INSTALL, UPGRADE, UNINSTALL, START, STOP,
16
+ # or empty at boot and shutdown
17
+ # SYNOPKG_PKGDEST installed files (/var/packages/<id>/target)
18
+ # SYNOPKG_PKGVAR, SYNOPKG_PKGHOME package data and home; kept across upgrades
19
+ # SYNOPKG_TEMP_LOGFILE write a message for the user here
20
+ # SYNOPKG_DSM_LANGUAGE DSM language code (enu, cht, chs, jpn, ...)
21
+ # SYNOPKG_DSM_VERSION_MAJOR, _MINOR, _BUILD, SYNOPKG_DSM_ARCH
22
+ # SYNOPKG_USERNAME who started the action; empty when DSM did
23
+ #
24
+ # Uninstall wizard answers arrive as environment variables named by each
25
+ # item's key. The SDK's uninstall wizard already asks about deleting app data.
26
+ #
27
+ # Exit code: 0 continues; non-zero aborts the uninstall or upgrade before the
28
+ # SDK steps run (DSM's own rule).
29
+ #
30
+ # To tell the user why something failed, write plain text to the message file
31
+ # and exit non-zero; DSM shows it after the action:
32
+ # echo "Port 8080 is already in use." > "$SYNOPKG_TEMP_LOGFILE"
33
+ # exit 1
34
+ #
35
+ # Do not:
36
+ # - start, stop or clean up what the SDK owns (daemon, background worker,
37
+ # executor, portal, Shared Folder, app data deletion, uninstall.purge);
38
+ # - need root or try to gain it;
39
+ # - block for long: neither DSM nor the SDK documents a timeout, so a script
40
+ # that hangs blocks Package Center.
41
+ #
42
+ # More: synopkgland skill show lifecycle
43
+
44
+ # Example: on a real uninstall only, export the App's settings to a file the
45
+ # user keeps.
46
+ # if [ "${SYNOPKG_PKG_STATUS:-}" = UNINSTALL ]; then
47
+ # cp "$SYNOPKG_PKGVAR/settings.json" "$SYNOPKG_PKGHOME/settings-backup.json" 2>/dev/null || true
48
+ # fi
49
+
50
+ exit 0
@@ -0,0 +1,52 @@
1
+ #!/bin/sh
2
+ # preupgrade: runs at the start of an upgrade, after DSM stopped the old
3
+ # version (SYNOPKG_PKG_STATUS=UPGRADE). It is the NEW version's script, but
4
+ # SYNOPKG_PKGDEST still holds the OLD version's files.
5
+ #
6
+ # Upgrade order: old start-stop-status stop, new preupgrade, old preuninst,
7
+ # old files deleted, old postuninst, new preinst, new files, new postinst,
8
+ # new postupgrade, new start-stop-status start.
9
+ #
10
+ # Order in this script: this script first, then the SDK's preupgrade steps.
11
+ #
12
+ # Runs as the package account, never as root. Root work belongs in root/*.sh
13
+ # through the SDK executor (synopkgland add root-ops).
14
+ #
15
+ # Environment (DSM sets these; the SDK passes them through unchanged):
16
+ # SYNOPKG_PKGNAME, SYNOPKG_PKGVER package id and the version being installed
17
+ # SYNOPKG_OLD_PKGVER the previous version (upgrade only)
18
+ # SYNOPKG_PKG_STATUS INSTALL, UPGRADE, UNINSTALL, START, STOP,
19
+ # or empty at boot and shutdown
20
+ # SYNOPKG_PKGDEST installed files (/var/packages/<id>/target)
21
+ # SYNOPKG_PKGVAR, SYNOPKG_PKGHOME package data and home; kept across upgrades
22
+ # SYNOPKG_TEMP_LOGFILE write a message for the user here
23
+ # SYNOPKG_DSM_LANGUAGE DSM language code (enu, cht, chs, jpn, ...)
24
+ # SYNOPKG_DSM_VERSION_MAJOR, _MINOR, _BUILD, SYNOPKG_DSM_ARCH
25
+ # SYNOPKG_USERNAME who started the action; empty when DSM did
26
+ #
27
+ # Upgrade wizard answers arrive as environment variables named by each item's
28
+ # key. A silent upgrade shows no wizard, so always have a default.
29
+ #
30
+ # Exit code: 0 continues; non-zero aborts the upgrade before the SDK steps run
31
+ # (DSM's own rule). Keep this to checks and to saving what postupgrade needs.
32
+ #
33
+ # To tell the user why something failed, write plain text to the message file
34
+ # and exit non-zero; DSM shows it after the action:
35
+ # echo "Port 8080 is already in use." > "$SYNOPKG_TEMP_LOGFILE"
36
+ # exit 1
37
+ #
38
+ # Do not:
39
+ # - start, stop or clean up what the SDK owns (daemon, background worker,
40
+ # executor, portal, Shared Folder, app data deletion, uninstall.purge);
41
+ # - need root or try to gain it;
42
+ # - block for long: neither DSM nor the SDK documents a timeout, so a script
43
+ # that hangs blocks Package Center.
44
+ #
45
+ # More: synopkgland skill show lifecycle
46
+
47
+ # Example: refuse a downgrade the App's data format cannot take.
48
+ # case "${SYNOPKG_OLD_PKGVER:-}" in
49
+ # 2.*) echo "Downgrading from 2.x is not supported." > "$SYNOPKG_TEMP_LOGFILE"; exit 1 ;;
50
+ # esac
51
+
52
+ exit 0
@@ -0,0 +1,59 @@
1
+ #!/bin/sh
2
+ # start-stop-status: DSM calls it with one argument:
3
+ # start the user started the package, at boot, or after install/upgrade
4
+ # stop the user stopped it, at shutdown, before upgrade and uninstall
5
+ # status DSM asks whether the package runs
6
+ # prestart, prestop only when INFO has precheckstartstop (the SDK sets it
7
+ # for daemon and background-job Apps); non-zero refuses the action
8
+ #
9
+ # Order: start, prestart and status run the SDK steps first, then this script.
10
+ # stop and prestop run this script first, then the SDK steps; the SDK stop
11
+ # always runs. If this script fails at start, the SDK stops again.
12
+ # SYNOPKG_PKG_STATUS tells why (START, STOP, INSTALL, UPGRADE, UNINSTALL, or
13
+ # empty at boot and shutdown).
14
+ #
15
+ # Runs as the package account, never as root. Root work belongs in root/*.sh
16
+ # through the SDK executor (synopkgland add root-ops).
17
+ #
18
+ # Environment (DSM sets these; the SDK passes them through unchanged):
19
+ # SYNOPKG_PKGNAME, SYNOPKG_PKGVER package id and the version being installed
20
+ # SYNOPKG_OLD_PKGVER the previous version (upgrade only)
21
+ # SYNOPKG_PKG_STATUS INSTALL, UPGRADE, UNINSTALL, START, STOP,
22
+ # or empty at boot and shutdown
23
+ # SYNOPKG_PKGDEST installed files (/var/packages/<id>/target)
24
+ # SYNOPKG_PKGVAR, SYNOPKG_PKGHOME package data and home; kept across upgrades
25
+ # SYNOPKG_TEMP_LOGFILE write a message for the user here
26
+ # SYNOPKG_DSM_LANGUAGE DSM language code (enu, cht, chs, jpn, ...)
27
+ # SYNOPKG_DSM_VERSION_MAJOR, _MINOR, _BUILD, SYNOPKG_DSM_ARCH
28
+ # SYNOPKG_USERNAME who started the action; empty when DSM did
29
+ #
30
+ # Exit code: for start and stop, 0 is success and non-zero a failure. For
31
+ # status, the SDK asks this script only while the SDK runs: 0 running,
32
+ # 3 not running, 4 unknown, 150 broken (DSM's own codes).
33
+ #
34
+ # To tell the user why something failed, write plain text to the message file
35
+ # and exit non-zero; DSM shows it after the action:
36
+ # echo "Port 8080 is already in use." > "$SYNOPKG_TEMP_LOGFILE"
37
+ # exit 1
38
+ #
39
+ # Do not:
40
+ # - start, stop or clean up what the SDK owns (daemon, background worker,
41
+ # executor, portal, Shared Folder, app data deletion, uninstall.purge);
42
+ # - need root or try to gain it;
43
+ # - block for long: neither DSM nor the SDK documents a timeout, so a script
44
+ # that hangs blocks Package Center.
45
+ #
46
+ # More: synopkgland skill show lifecycle
47
+
48
+ case "$1" in
49
+ start)
50
+ # Example: refuse to start without a setting the App needs.
51
+ # if [ ! -s "$SYNOPKG_PKGVAR/settings.json" ]; then
52
+ # echo "Open the App once to finish its setup." > "$SYNOPKG_TEMP_LOGFILE"
53
+ # exit 1
54
+ # fi
55
+ exit 0 ;;
56
+ stop) exit 0 ;;
57
+ status) exit 0 ;;
58
+ *) exit 0 ;;
59
+ esac
@@ -315,12 +315,28 @@ A portal needs DSM 7.3.2-86009 or later, so `add portal` raises a lower
315
315
  package that registers a portal (Package Center error 320); 7.3.2 and 7.4
316
316
  install it; 7.2 is untested. To keep supporting older DSM, remove `portal:`.
317
317
 
318
+ The build also gives the portal a login page look: a neutral default
319
+ wallpaper (its thumbnail is what Control Panel > Login Portal > Applications
320
+ shows), a button colour and the App icon. Replace them with your own in
321
+ dsm-package.yaml:
322
+
323
+ ```yaml
324
+ login_style:
325
+ color: "#0F766E" # login button
326
+ wallpaper: branding/login-wallpaper.jpg # JPEG, at least 1920x1080, at most 2 MiB
327
+ wallpaper_thumbnail: branding/login-thumbnail.jpg # JPEG 320x200; required with wallpaper
328
+ title: branding/login-title.png # PNG 680x124, in place of icon and NAS name
329
+ ```
330
+
331
+ `synopkgland check` validates the files and prints the style in effect.
332
+
318
333
  ## Data, upgrades, uninstall
319
334
 
320
335
  The backend stores data in `/var/packages/__PACKAGE__/var` (the package's
321
336
  own folder, kept across upgrades). Bump `build` for every SPK you hand out;
322
- bump `version` for feature releases. There are no lifecycle scripts to write:
323
- the CGI starts per request, so start/stop do nothing.
337
+ bump `version` for feature releases. You need no lifecycle scripts: the CGI
338
+ starts per request, so start/stop do nothing. For extra install, upgrade or
339
+ uninstall work, edit `dsm-scripts/` (`synopkgland skill show lifecycle`).
324
340
 
325
341
  ### What an upgrade keeps
326
342
 
@@ -1,6 +1,6 @@
1
1
  Native Go package build dependencies
2
2
 
3
- github.com/synopackageland/syno-pkg-land-sdk v0.10.2 (MIT)
3
+ github.com/synopackageland/syno-pkg-land-sdk v0.10.3 (MIT)
4
4
 
5
5
  Copyright (c) 2026 synopkgland
6
6
 
@@ -42,16 +42,16 @@ var (
42
42
  // "on" when dsm-package.yaml declares portal: serves portal.status;
43
43
  // "executor" below DSM 7.2, where the executor registers the entry.
44
44
  portal = ""
45
- // backend.timeout_seconds, 60 unless set.
45
+ // backend.timeout_seconds, api.DefaultRequestTimeout unless set.
46
46
  requestTimeout = ""
47
47
  )
48
48
 
49
- // deadline is the CGI request deadline: requestTimeout seconds, or 60 when it
50
- // is unset or not a positive number.
49
+ // deadline is the CGI request deadline: requestTimeout seconds, or
50
+ // api.DefaultRequestTimeout when it is unset or not a positive number.
51
51
  func deadline() api.DeadlineOption {
52
52
  seconds, err := strconv.Atoi(requestTimeout)
53
53
  if err != nil || seconds <= 0 {
54
- seconds = 60
54
+ return api.Timeout(api.DefaultRequestTimeout)
55
55
  }
56
56
  return api.Timeout(time.Duration(seconds) * time.Second)
57
57
  }
@@ -13,12 +13,12 @@ func TestRequestDeadlineFollowsTheBuildSetting(t *testing.T) {
13
13
  saved := requestTimeout
14
14
  t.Cleanup(func() { requestTimeout = saved })
15
15
  for setting, want := range map[string]time.Duration{
16
- "": 60 * time.Second,
16
+ "": api.DefaultRequestTimeout,
17
17
  "120": 120 * time.Second,
18
18
  "1": time.Second,
19
- "0": 60 * time.Second,
20
- "-5": 60 * time.Second,
21
- "x": 60 * time.Second,
19
+ "0": api.DefaultRequestTimeout,
20
+ "-5": api.DefaultRequestTimeout,
21
+ "x": api.DefaultRequestTimeout,
22
22
  } {
23
23
  requestTimeout = setting
24
24
  var left time.Duration
@@ -4,6 +4,6 @@ go 1.25.0
4
4
 
5
5
  toolchain go1.26.9
6
6
 
7
- require github.com/synopackageland/syno-pkg-land-sdk v0.10.2
7
+ require github.com/synopackageland/syno-pkg-land-sdk v0.10.3
8
8
 
9
9
  require golang.org/x/sys v0.44.0 // indirect
@@ -1,4 +1,4 @@
1
- github.com/synopackageland/syno-pkg-land-sdk v0.10.2 h1:2sQTuKCkwbFk5vnnY+21N3xIBKu7D8PVydgwxmIeGWI=
2
- github.com/synopackageland/syno-pkg-land-sdk v0.10.2/go.mod h1:hlsvaVuH2wsAoR8jzI1Rg/NeejQSFpAaN6zpIWsVbjI=
1
+ github.com/synopackageland/syno-pkg-land-sdk v0.10.3 h1:YMwtSPqR7sS6pGKF0x7vMoMxNSY7GIMcxTa++VCTlz8=
2
+ github.com/synopackageland/syno-pkg-land-sdk v0.10.3/go.mod h1:hlsvaVuH2wsAoR8jzI1Rg/NeejQSFpAaN6zpIWsVbjI=
3
3
  golang.org/x/sys v0.44.0 h1:ildZl3J4uzeKP07r2F++Op7E9B29JRUy+a27EibtBTQ=
4
4
  golang.org/x/sys v0.44.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
@@ -29,7 +29,7 @@ test('the capability surface uses an explicit long-running facade without legacy
29
29
  } satisfies BackgroundSaveFacade
30
30
  const call=vi.fn(async()=>{throw {code:'unexpected-legacy-call'}})
31
31
  const root=render({delegate:sdk,call,locale:'en'});await settle()
32
- expect(root.textContent).toContain('My destinations');expect(root.textContent).toContain('/home/Exports')
32
+ expect(root.textContent).toContain('My saves');expect(sdk.operations).toHaveBeenCalled()
33
33
  expect(call).not.toHaveBeenCalled()
34
34
  })
35
35
  test('the default surface binds the real routes and the session-only picker',async()=>{
@@ -46,12 +46,12 @@ test('the default surface binds the real routes and the session-only picker',asy
46
46
  throw {code:'unexpected'}
47
47
  })
48
48
  const root=render({call,locale:'en'});await settle()
49
- button(root,'Choose save destination').click();await settle()
49
+ button(root,'Choose folder').click();await settle()
50
50
  // The SDK picker selects a folder with a row click.
51
51
  const row=root.querySelector<HTMLElement>('tr[data-row-key="/home"]')!;expect(row).toBeTruthy();row.dispatchEvent(new MouseEvent('click',{bubbles:true}));await settle()
52
52
  button(root,'Confirm selection').click();await settle()
53
53
  expect(call).toHaveBeenCalledWith('delegate.select_destination',{key:'save',path:'/home'})
54
- // The template demonstrates the save flow; SDK 0.9.9 shows it only on request.
54
+ // The SDK hides the sample save unless an App opts in; the template opts in with sample-save.
55
55
  button(root,'Save example file').click();await settle()
56
56
  expect(call).toHaveBeenCalledWith('delegate.example.export',expect.objectContaining({key:'save'}))
57
57
  })
@@ -6,7 +6,6 @@ import (
6
6
  "errors"
7
7
  "net/http"
8
8
  "path/filepath"
9
- "time"
10
9
 
11
10
  "github.com/synopackageland/syno-pkg-land-sdk/api"
12
11
  "github.com/synopackageland/syno-pkg-land-sdk/delegate"
@@ -59,7 +58,8 @@ func (cfg Config) executorHoldsThisBuild(ctx context.Context) error {
59
58
  var executorSocketBase = "/run/synopkgland"
60
59
 
61
60
  // executor keeps the executor's protocol v1 refusal codes (POLICY_DENIED,
62
- // EXECUTOR_UNAVAILABLE, ...) as the API error code.
61
+ // EXECUTOR_UNAVAILABLE, ...) as the API error code. Without its own Timeout
62
+ // the client waits as the SDK says: delegate.ExecutorTimeout, root.run longer.
63
63
  func (cfg Config) executor() *delegate.Client {
64
- return &delegate.Client{Socket: filepath.Join(executorSocketBase, cfg.PackageID, "executor.sock"), Timeout: 30 * time.Second}
64
+ return &delegate.Client{Socket: filepath.Join(executorSocketBase, cfg.PackageID, "executor.sock")}
65
65
  }
@@ -163,3 +163,11 @@ func TestExecutorRootRunReportsTheExecutorsRefusalCode(t *testing.T) {
163
163
  }
164
164
  }
165
165
  }
166
+
167
+ // The executor client waits as long as the SDK says (spec/protocol/v1,
168
+ // Timeouts): delegate.ExecutorTimeout for status and control, root.run longer.
169
+ func TestTheExecutorClientWaitsTheSDKsTimeout(t *testing.T) {
170
+ if got := testConfig(t).executor().Timeout; got != 0 {
171
+ t.Fatalf("executor client sets its own wait %v instead of the SDK's", got)
172
+ }
173
+ }
@@ -29,6 +29,9 @@ and the background picker uses the retained session provider.
29
29
  With `portal:` declared (`synopkgland add portal`, executor Apps only), the status page shows
30
30
  the SDK's `PortalSettings`: the DSM Application Portal link, the alias form
31
31
  for administrators, and the Control Panel shortcut.
32
+ The alias login page's wallpaper, button colour and title come from
33
+ `login_style:` in dsm-package.yaml (a default otherwise); see
34
+ `synopkgland skill show portal`.
32
35
 
33
36
  Use public library controls, the embedded theme, per-app modal scope and unique
34
37
  Vue ID prefixes. Backend route permissions and DSM checks remain authoritative.
@@ -7,8 +7,8 @@
7
7
  "name": "native-dsm-vue-app",
8
8
  "dependencies": {
9
9
  "@lucide/vue": "1.48.0",
10
- "@synopackageland/sdk": "0.10.2",
11
- "@synopackageland/ui": "0.9.4",
10
+ "@synopackageland/sdk": "0.10.3",
11
+ "@synopackageland/ui": "0.9.5",
12
12
  "vue": "3.5.43"
13
13
  },
14
14
  "devDependencies": {
@@ -1148,13 +1148,13 @@
1148
1148
  }
1149
1149
  },
1150
1150
  "node_modules/@synopackageland/sdk": {
1151
- "version": "0.10.2",
1152
- "resolved": "https://registry.npmjs.org/@synopackageland/sdk/-/sdk-0.10.2.tgz",
1153
- "integrity": "sha512-ZX854q5grp/dpCFze/JxmbWS/a6CKOGA8YpgXutMrY807G1JU0PGlfAeeQ4zQ37vZ741VNgOUEo9x5hZDdSB+w==",
1151
+ "version": "0.10.3",
1152
+ "resolved": "https://registry.npmjs.org/@synopackageland/sdk/-/sdk-0.10.3.tgz",
1153
+ "integrity": "sha512-1YsPAtNm4azqCEhAUv/99o10dxwXMtjOg4aRKnwbrSpjrOYQrhsGKfyFj9bFimqIGy0mj2NvRPKGz8ET0HqyEg==",
1154
1154
  "license": "MIT",
1155
1155
  "peerDependencies": {
1156
1156
  "@lucide/vue": "^1.48.0",
1157
- "@synopackageland/ui": "^0.9.4",
1157
+ "@synopackageland/ui": "^0.9.5",
1158
1158
  "vue": "^3.5.0"
1159
1159
  },
1160
1160
  "peerDependenciesMeta": {
@@ -1167,9 +1167,9 @@
1167
1167
  }
1168
1168
  },
1169
1169
  "node_modules/@synopackageland/ui": {
1170
- "version": "0.9.4",
1171
- "resolved": "https://registry.npmjs.org/@synopackageland/ui/-/ui-0.9.4.tgz",
1172
- "integrity": "sha512-yu0RrYtT/QcAn/lNwqZBoqqjLTXrpqqnSWB+0swGou8IWLrhfwXrHVBf+oJc+NjLj+G4kB7AyP9kbvw9FHxNOA==",
1170
+ "version": "0.9.5",
1171
+ "resolved": "https://registry.npmjs.org/@synopackageland/ui/-/ui-0.9.5.tgz",
1172
+ "integrity": "sha512-fytttAWLZ48H0w5UYsDzfJu7I4yBmafFviMoku+WZtwroNm6z26gTBdCgkg9a5MmxghFU8e1p5NWYnKfVVPbEw==",
1173
1173
  "license": "MIT",
1174
1174
  "dependencies": {
1175
1175
  "@internationalized/date": "3.12.4",
@@ -8,8 +8,8 @@
8
8
  },
9
9
  "dependencies": {
10
10
  "@lucide/vue": "1.48.0",
11
- "@synopackageland/sdk": "0.10.2",
12
- "@synopackageland/ui": "0.9.4",
11
+ "@synopackageland/sdk": "0.10.3",
12
+ "@synopackageland/ui": "0.9.5",
13
13
  "vue": "3.5.43"
14
14
  },
15
15
  "devDependencies": {
@@ -11,7 +11,7 @@
11
11
  "errors": {
12
12
  "session_required": "DSM 登录已过期,请重新登录。",
13
13
  "admin_required": "此操作必须由 DSM 管理员执行。",
14
- "app_privilege_denied": "DSM 应用程序权限不允许此帐户使用此 App。",
14
+ "app_privilege_denied": "DSM 应用程序权限不允许此账户使用此 App。",
15
15
  "auth_unavailable": "DSM 验证暂时不可用,请刷新后重试。",
16
16
  "request_failed": "请求失败({code}),请刷新后重试。"
17
17
  }
@@ -119,6 +119,13 @@ A service refuses with its own code (`limit_exceeded`, `read_failed`, the
119
119
  executor's refusals); the examples pass it on to the browser. `synopkgland add`
120
120
  works on Go backends only: start a Node.js App with the capabilities it needs.
121
121
 
122
+ With `portal:` in dsm-package.yaml (executor Apps only, see
123
+ `synopkgland skill show portal`), the build also gives the Login Portal entry a
124
+ default wallpaper, thumbnail, button colour and the App icon. `login_style:`
125
+ replaces them: `color`, `wallpaper` (JPEG, at least 1920x1080) with
126
+ `wallpaper_thumbnail` (JPEG 320x200), and `title` (PNG 680x124); `synopkgland
127
+ check` validates the files.
128
+
122
129
  ## The SDK
123
130
 
124
131
  `backend.sdk` in dsm-package.yaml names the SDK release: the build ships its runtime and its Node SDK as `backend/app/node_modules/@synopackageland/app`, and reads the method list from `main.js --describe`. `backend/vendor/@synopackageland/app` is the same SDK for tests and type checks; replace it with the new release's when you change `backend.sdk`. Upgrade the SDK by changing that version; `synopkgland check`
@@ -49,7 +49,7 @@ backend:
49
49
  runtime: node
50
50
  dir: backend/app
51
51
  entry: main.js
52
- sdk: 0.10.2
52
+ sdk: 0.10.3
53
53
  node: { package: Node.js_v20 }
54
54
 
55
55
  ui:
@@ -116,6 +116,13 @@ A service refuses with its own code (`limit_exceeded`, `read_failed`, the
116
116
  executor's refusals); the examples pass it on to the browser. `synopkgland add`
117
117
  works on Go backends only: start a Python App with the capabilities it needs.
118
118
 
119
+ With `portal:` in dsm-package.yaml (executor Apps only, see
120
+ `synopkgland skill show portal`), the build also gives the Login Portal entry a
121
+ default wallpaper, thumbnail, button colour and the App icon. `login_style:`
122
+ replaces them: `color`, `wallpaper` (JPEG, at least 1920x1080) with
123
+ `wallpaper_thumbnail` (JPEG 320x200), and `title` (PNG 680x124); `synopkgland
124
+ check` validates the files.
125
+
119
126
  ## The SDK
120
127
 
121
128
  `backend.sdk` in dsm-package.yaml names the SDK release: the build ships its runtime and its Python SDK as `backend/app/vendor/synopkgland_app`, and reads the method list from `main.py --describe`. `backend/vendor/synopkgland_app` is the same SDK for tests; replace it with the new release's when you change `backend.sdk`. Upgrade the SDK by changing that version; `synopkgland check`
@@ -47,7 +47,7 @@ backend:
47
47
  runtime: python
48
48
  dir: backend/app
49
49
  entry: main.py
50
- sdk: 0.10.2
50
+ sdk: 0.10.3
51
51
 
52
52
  ui:
53
53
  # Static files served at /webman/3rdparty/<package>/app/. app.js and app.css
@@ -125,6 +125,13 @@ A service refuses with its own code (`limit_exceeded`, `read_failed`, the
125
125
  executor's refusals); the examples pass it on to the browser. `synopkgland add`
126
126
  works on Go backends only: start a Rust App with the capabilities it needs.
127
127
 
128
+ With `portal:` in dsm-package.yaml (executor Apps only, see
129
+ `synopkgland skill show portal`), the build also gives the Login Portal entry a
130
+ default wallpaper, thumbnail, button colour and the App icon. `login_style:`
131
+ replaces them: `color`, `wallpaper` (JPEG, at least 1920x1080) with
132
+ `wallpaper_thumbnail` (JPEG 320x200), and `title` (PNG 680x124); `synopkgland
133
+ check` validates the files.
134
+
128
135
  ## The SDK
129
136
 
130
137
  The `syno-pkg-land-app` git dependency's `tag` in `backend/Cargo.toml` names the SDK release; the build reads it from `Cargo.lock` and ships that release's runtime. A cross-compiled binary cannot run on the build machine, so `backend.methods` in dsm-package.yaml lists every method the App registers; `synopkgland check` refuses a list that differs from the App's own. Upgrade the SDK by changing that version; `synopkgland check`
@@ -104,8 +104,8 @@ dependencies = [
104
104
 
105
105
  [[package]]
106
106
  name = "syno-pkg-land-app"
107
- version = "0.10.2"
108
- source = "git+https://github.com/synopackageland/syno-pkg-land-sdk?tag=v0.10.2#3255e7ef0cfdfc89ae1308bfe1ddf2eabfe6f1b6"
107
+ version = "0.10.3"
108
+ source = "git+https://github.com/synopackageland/syno-pkg-land-sdk?tag=v0.10.3#4e0fe8ee1f71a9818e50a38242882cebaa776cee"
109
109
  dependencies = [
110
110
  "libc",
111
111
  "serde",
@@ -9,7 +9,7 @@ libc = "0.2"
9
9
  serde = { version = "1", features = ["derive"] }
10
10
  serde_json = "1"
11
11
  # The SDK version is the tag; `synopkgland check` reads it from Cargo.lock.
12
- syno-pkg-land-app = { git = "https://github.com/synopackageland/syno-pkg-land-sdk", tag = "v0.10.2" }
12
+ syno-pkg-land-app = { git = "https://github.com/synopackageland/syno-pkg-land-sdk", tag = "v0.10.3" }
13
13
 
14
14
  [profile.release]
15
15
  # A handler panic becomes an "internal" reply; abort would end the App without one.