@synopackageland/sdk 0.9.5 → 0.9.7

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 (38) hide show
  1. package/README.md +29 -0
  2. package/dist/i18n/background/de.json +10 -0
  3. package/dist/i18n/background/en.json +10 -0
  4. package/dist/i18n/background/es.json +10 -0
  5. package/dist/i18n/background/fr.json +10 -0
  6. package/dist/i18n/background/it.json +10 -0
  7. package/dist/i18n/background/ja.json +9 -0
  8. package/dist/i18n/background/ko.json +9 -0
  9. package/dist/i18n/background/zh-CN.json +9 -0
  10. package/dist/i18n/background/zh-TW.json +9 -0
  11. package/dist/i18n/save/de.json +1 -0
  12. package/dist/i18n/save/en.json +1 -0
  13. package/dist/i18n/save/es.json +1 -0
  14. package/dist/i18n/save/fr.json +1 -0
  15. package/dist/i18n/save/it.json +1 -0
  16. package/dist/i18n/save/ja.json +1 -0
  17. package/dist/i18n/save/ko.json +1 -0
  18. package/dist/i18n/save/zh-CN.json +1 -0
  19. package/dist/i18n/save/zh-TW.json +1 -0
  20. package/dist/index.js +2165 -2048
  21. package/dist/src/background/i18n.d.ts +10 -0
  22. package/dist/src/context.d.ts +3 -0
  23. package/dist/src/delegate/save-copy.d.ts +3 -0
  24. package/dist/src/dsm/file-station.d.ts +27 -0
  25. package/dist/src/i18n.d.ts +17 -0
  26. package/dist/src/index.d.ts +2 -2
  27. package/dist/src/protocol/v1.d.ts +15 -1
  28. package/dist/style.css +1 -1
  29. package/dist/vue.js +1378 -1305
  30. package/package.json +2 -2
  31. package/spec/protocol/v1/README.md +72 -9
  32. package/spec/protocol/v1/errors.json +8 -0
  33. package/spec/protocol/v1/schema/portal.register.json +11 -0
  34. package/spec/protocol/v1/schema/portal.unregister.json +11 -0
  35. package/spec/protocol/v1/vectors/files.jsonl +1 -0
  36. package/spec/protocol/v1/vectors/portal.jsonl +8 -0
  37. package/spec/protocol/v1/vectors/status.jsonl +1 -0
  38. package/dist/src/nas/i18n.d.ts +0 -2
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@synopackageland/sdk",
3
- "version": "0.9.5",
3
+ "version": "0.9.7",
4
4
  "description": "Frontend SDK for DSM packages built with synopkgland: headless client and Vue components",
5
5
  "license": "MIT",
6
6
  "homepage": "https://github.com/synopackageland/syno-pkg-land-sdk/tree/main/ts#readme",
@@ -53,7 +53,7 @@
53
53
  },
54
54
  "devDependencies": {
55
55
  "@lucide/vue": "1.48.0",
56
- "@synopackageland/ui": "0.9.1",
56
+ "@synopackageland/ui": "0.9.2",
57
57
  "@types/node": "24.19.2",
58
58
  "@vitejs/plugin-vue": "6.0.9",
59
59
  "jsdom": "27.4.0",
@@ -40,9 +40,13 @@ the executor against the vectors here.
40
40
  `dsm_synotoken` and `remote_addr` (since 0.9.4), the browser's address
41
41
  (REMOTE_ADDR). DSM's Application Privileges are evaluated for that address;
42
42
  an op that checks them MUST be refused with `APP_DENIED` when a present
43
- `auth` has no usable `remote_addr`. Requests without `auth` are checked as
44
- coming from 127.0.0.1. A present `auth` is always treated as caller
45
- authentication, never ignored, even when empty. Ops whose schema says
43
+ `auth` has no usable `remote_addr`. A grant records the `remote_addr` of the
44
+ `delegate.create` that made it (creating it again records the new one), and
45
+ an operation records the address of its `files.moveFromStaging` (since
46
+ 0.9.6). A request without `auth` under a grant, and the operation's
47
+ background revalidation, are checked from that recorded address; work with no
48
+ recorded address is checked as coming from 127.0.0.1. A present `auth` is
49
+ always treated as caller authentication, never ignored, even when empty. Ops whose schema says
46
50
  `"auth": "required"` MUST be refused with `AUTH_DENIED` without a valid DSM
47
51
  session.
48
52
  - `params` MUST be an object valid against `schema/<op>.json`. A member that
@@ -70,9 +74,67 @@ the executor against the vectors here.
70
74
  ## Status
71
75
 
72
76
  `status` answers `version` (`"1"`), `ready`, `disabled`, `executor_digest`
73
- and `policy_digest`. The executor MUST NOT report the SDK version; the binary
77
+ and `policy_digest`. With `auth` (since 0.9.6) it also answers `allowed`:
78
+ whether DSM's Application Privileges let the caller use the App and File
79
+ Station from `remote_addr`, the check `delegate.create` makes; an invalid
80
+ session is `AUTH_DENIED`. A CGI sends the caller's `auth` so an App the
81
+ executor would refuse is never shown as ready. The executor MUST NOT report the SDK version; the binary
74
82
  digest MUST change only when the executor source changes.
75
83
 
84
+ ## Application Portal
85
+
86
+ Since 0.9.6. A policy MAY carry `"portal": {"application": "<display name>",
87
+ "alias": "<default alias>"}` together with both `portal.register` and
88
+ `portal.unregister`; a policy with only some of the three is invalid. The
89
+ executor then registers the App's Application Portal entry the way DSM's
90
+ web-config worker does, on every DSM version: an App whose `os_min_ver` is
91
+ below 7.2 cannot declare that resource, since DSM 7.1 refuses it for unsigned
92
+ packages.
93
+
94
+ - Both ops take no params and require the session of an administrator whom
95
+ Application Privileges allow to use the App (`ADMIN_REQUIRED`,
96
+ `APP_DENIED`). The alias itself stays an administrator's
97
+ `SYNO.Core.AppPortal set`; the executor never writes it.
98
+ - `portal.register` writes `/usr/syno/etc/www/app.d/<package>.mustache`, then
99
+ `<package>.json` (id `SYNO.SDS.<package>.Instance`), runs
100
+ `synow3tool --gen-app=<package>`, and requires `nginx -t` and the App in
101
+ `SYNO.Core.AppPortal list`. Registering again rewrites the same files.
102
+ - `portal.unregister` removes both app.d files and
103
+ `/usr/syno/etc/packages/<package>/AppPortal.json`, disables and
104
+ unregisters every `{dsm,www,alias,.alias}.syno-app-portal.<package>.conf`
105
+ conf and `{server,.server}` site still enabled or still in DSM's registry,
106
+ and reloads nginx once `nginx -t` passes. A step with nothing to remove is
107
+ skipped, so the op MAY be repeated.
108
+ - A failed change is `PORTAL_FAILED`. A failed register is undone the same
109
+ way. Until a later `portal.unregister` succeeds, `status` reports
110
+ `portal` `cleanup-required`.
111
+ - The executor MUST remove the entry when it purges for an uninstall; DSM
112
+ does not.
113
+ - `status` answers `portal` for such a policy: `registered` (the executor's
114
+ own files and DSM's generated conf), `unregistered`, or `cleanup-required`
115
+ (anything else, including a failed change).
116
+
117
+ ## Uninstall purge step
118
+
119
+ Since 0.9.7. A policy MAY carry `"purge": "<name>"`, naming one of its `root`
120
+ ops as the App's root step at uninstall; any other value makes the policy
121
+ invalid. Approving that policy is the administrator's consent that the op
122
+ runs as root at uninstall, with no session.
123
+
124
+ - `root.run` MUST refuse that op with `POLICY_DENIED`: a session cannot start
125
+ the uninstall step early.
126
+ - When the package account requests purge, the executor MUST run the op
127
+ before it removes Application Privileges rules, its portal entry and its
128
+ approval tasks, and only while the approval is live: the package ships the
129
+ approved executor and policy pins, and both approval tasks of that policy
130
+ stand with their exact scripts. Otherwise, and on an offline purge, it MUST
131
+ skip the op and log `purge root step skipped: not approved`.
132
+ - The op gets the argument `delete-state` when the package account wrote
133
+ `delete-state` to `var/executor/uninstall` before its request (the
134
+ `executor/lifecycle purge --delete-state` helper does), and no argument
135
+ otherwise. It runs for at most 60 seconds; its exit status and output go to
136
+ the purge log, and a failure MUST NOT stop the rest of the purge.
137
+
76
138
  ## Grants
77
139
 
78
140
  - A time-limited grant (`job_id`, `ttl_seconds` ≤ 30 days) covers the listed
@@ -86,11 +148,12 @@ digest MUST change only when the executor source changes.
86
148
  Application Privileges, scope, directory identity and write permission
87
149
  against DSM. A failure the user or an administrator can undo MUST fail only
88
150
  that operation and MUST keep the grant: `PERMISSION_DENIED` (the ACL refuses
89
- the write) and `not-found` (the granted folder is not at its path). Once the
90
- cause is fixed, a new operation under the same grant succeeds. Any other
91
- revalidation failure (`DELEGATION_DENIED`, `INELIGIBLE`, `APP_DENIED`,
92
- `SCOPE_DENIED`, including a different folder at the granted path) MUST
93
- revoke the grant; the user then chooses the destination again.
151
+ the write), `not-found` (the granted folder is not at its path) and, since
152
+ 0.9.6, `APP_DENIED` (an Application Privileges rule refuses the account from
153
+ the checked address). Once the cause is fixed, a new operation under the same
154
+ grant succeeds. Any other revalidation failure (`DELEGATION_DENIED`,
155
+ `INELIGIBLE`, `SCOPE_DENIED`, including a different folder at the granted
156
+ path) MUST revoke the grant; the user then chooses the destination again.
94
157
  - `files.taskStatus` and `files.taskCancel` follow the task's op and MUST NOT
95
158
  be granted directly.
96
159
  - An administrator MAY turn the integration off with `delegate.disable`; then
@@ -154,6 +154,14 @@
154
154
  "en": "The root operation exited with a non-zero status or failed to run.",
155
155
  "zh-TW": "root 操作以非零狀態結束,或無法執行。"
156
156
  },
157
+ {
158
+ "code": "PORTAL_FAILED",
159
+ "kind": "refusal",
160
+ "retryable": true,
161
+ "httpStatus": 502,
162
+ "en": "DSM did not accept the Application Portal change; status portal says whether the entry needs another removal.",
163
+ "zh-TW": "DSM 未接受應用程式入口的變更;status 的 portal 說明是否需要再移除一次。"
164
+ },
157
165
  {
158
166
  "code": "NO_SPACE",
159
167
  "kind": "refusal",
@@ -0,0 +1,11 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "https://github.com/synopackageland/syno-pkg-land-sdk/spec/protocol/v1/schema/portal.register.json",
4
+ "title": "portal.register",
5
+ "description": "Administrator: register the policy's Application Portal entry in DSM's app.d (DSM before 7.2 refuses the web-config resource of unsigned packages).",
6
+ "since": "0.9.6",
7
+ "auth": "required",
8
+ "type": "object",
9
+ "properties": {},
10
+ "additionalProperties": false
11
+ }
@@ -0,0 +1,11 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "https://github.com/synopackageland/syno-pkg-land-sdk/spec/protocol/v1/schema/portal.unregister.json",
4
+ "title": "portal.unregister",
5
+ "description": "Administrator: remove the App's Application Portal entry, its saved alias and every nginx conf DSM generated for it.",
6
+ "since": "0.9.6",
7
+ "auth": "required",
8
+ "type": "object",
9
+ "properties": {},
10
+ "additionalProperties": false
11
+ }
@@ -7,4 +7,5 @@
7
7
  {"name":"move-missing-size","request":{"v":1,"op":"files.moveFromStaging","params":{"delegation_id":"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","job_id":"export-20261009","staging":"exports/report.csv","name":"report.csv"}},"policy":{"v":1,"package":"contract","app":"app","scope":"home","operations":["status","delegate.create","delegate.list","delegate.revoke","files.list","files.write","files.moveFromStaging","files.operationStatus","files.cancelOperation","delegate.disable","delegate.enable","delegate.audit","root.run"],"destinations":["save"],"lifetime":"until-revoked","root":{}},"state":{"grants":[{"id":"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","username":"alice","key":"save","path":"/home/exports"}]},"expect":{"ok":false,"code":"INVALID_REQUEST"}}
8
8
  {"name":"operation-status-unknown","request":{"v":1,"op":"files.operationStatus","params":{"delegation_id":"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","job_id":"export-20261009"}},"policy":{"v":1,"package":"contract","app":"app","scope":"home","operations":["status","delegate.create","delegate.list","delegate.revoke","files.list","files.write","files.moveFromStaging","files.operationStatus","files.cancelOperation","delegate.disable","delegate.enable","delegate.audit","root.run"],"destinations":["save"],"lifetime":"until-revoked","root":{}},"state":{"grants":[{"id":"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","username":"alice","key":"save","path":"/home/exports"}]},"expect":{"ok":false,"code":"operation-not-found"}}
9
9
  {"name":"root-run-unpinned","request":{"v":1,"op":"root.run","auth":{"dsm_cookie":"admin","dsm_synotoken":"contract-token"},"params":{"name":"whoami"}},"policy":{"v":1,"package":"contract","app":"app","scope":"home","operations":["status","delegate.create","delegate.list","delegate.revoke","files.list","files.write","files.moveFromStaging","files.operationStatus","files.cancelOperation","delegate.disable","delegate.enable","delegate.audit","root.run"],"destinations":["save"],"lifetime":"until-revoked","root":{}},"state":{},"expect":{"ok":false,"code":"POLICY_DENIED"}}
10
+ {"name":"root-run-purge-step","request":{"v":1,"op":"root.run","auth":{"dsm_cookie":"admin","dsm_synotoken":"contract-token"},"params":{"name":"purge"}},"policy":{"v":1,"package":"contract","app":"app","scope":"home","operations":["status","delegate.create","delegate.list","delegate.revoke","files.list","files.write","files.moveFromStaging","files.operationStatus","files.cancelOperation","delegate.disable","delegate.enable","delegate.audit","root.run"],"destinations":["save"],"lifetime":"until-revoked","root":{"purge":"bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"},"purge":"purge"},"state":{},"expect":{"ok":false,"code":"POLICY_DENIED"}}
10
11
  {"name":"root-run-bad-name","request":{"v":1,"op":"root.run","auth":{"dsm_cookie":"admin","dsm_synotoken":"contract-token"},"params":{"name":"../x"}},"policy":{"v":1,"package":"contract","app":"app","scope":"home","operations":["status","delegate.create","delegate.list","delegate.revoke","files.list","files.write","files.moveFromStaging","files.operationStatus","files.cancelOperation","delegate.disable","delegate.enable","delegate.audit","root.run"],"destinations":["save"],"lifetime":"until-revoked","root":{}},"state":{},"expect":{"ok":false,"code":"INVALID_REQUEST"}}
@@ -0,0 +1,8 @@
1
+ {"name":"register","request":{"v":1,"op":"portal.register","auth":{"dsm_cookie":"admin","dsm_synotoken":"contract-token","remote_addr":"192.0.2.1"},"params":{}},"policy":{"v":1,"package":"contract","app":"app","operations":["status","portal.register","portal.unregister"],"portal":{"application":"Contract","alias":"contract"}},"state":{},"expect":{"ok":true,"result":{"entry":"registered"}}}
2
+ {"name":"unregister","request":{"v":1,"op":"portal.unregister","auth":{"dsm_cookie":"admin","dsm_synotoken":"contract-token","remote_addr":"192.0.2.1"},"params":{}},"policy":{"v":1,"package":"contract","app":"app","operations":["status","portal.register","portal.unregister"],"portal":{"application":"Contract","alias":"contract"}},"state":{},"expect":{"ok":true,"result":{"entry":"unregistered"}}}
3
+ {"name":"register-not-admin","request":{"v":1,"op":"portal.register","auth":{"dsm_cookie":"alice","dsm_synotoken":"contract-token","remote_addr":"192.0.2.1"},"params":{}},"policy":{"v":1,"package":"contract","app":"app","operations":["status","portal.register","portal.unregister"],"portal":{"application":"Contract","alias":"contract"}},"state":{},"expect":{"ok":false,"code":"ADMIN_REQUIRED"}}
4
+ {"name":"unregister-not-admin","request":{"v":1,"op":"portal.unregister","auth":{"dsm_cookie":"alice","dsm_synotoken":"contract-token","remote_addr":"192.0.2.1"},"params":{}},"policy":{"v":1,"package":"contract","app":"app","operations":["status","portal.register","portal.unregister"],"portal":{"application":"Contract","alias":"contract"}},"state":{},"expect":{"ok":false,"code":"ADMIN_REQUIRED"}}
5
+ {"name":"register-without-auth","request":{"v":1,"op":"portal.register","params":{}},"policy":{"v":1,"package":"contract","app":"app","operations":["status","portal.register","portal.unregister"],"portal":{"application":"Contract","alias":"contract"}},"state":{},"expect":{"ok":false,"code":"AUTH_DENIED"}}
6
+ {"name":"register-outside-policy","request":{"v":1,"op":"portal.register","auth":{"dsm_cookie":"admin","dsm_synotoken":"contract-token","remote_addr":"192.0.2.1"},"params":{}},"policy":{"v":1,"package":"contract","app":"app","scope":"home","operations":["status","delegate.create","delegate.list","delegate.revoke","files.list","files.write","files.moveFromStaging","files.operationStatus","files.cancelOperation","delegate.disable","delegate.enable","delegate.audit","root.run"],"destinations":["save"],"lifetime":"until-revoked","root":{}},"state":{},"expect":{"ok":false,"code":"POLICY_DENIED"}}
7
+ {"name":"register-with-params","request":{"v":1,"op":"portal.register","auth":{"dsm_cookie":"admin","dsm_synotoken":"contract-token","remote_addr":"192.0.2.1"},"params":{"alias":"x"}},"policy":{"v":1,"package":"contract","app":"app","operations":["status","portal.register","portal.unregister"],"portal":{"application":"Contract","alias":"contract"}},"state":{},"expect":{"ok":false,"code":"INVALID_REQUEST"}}
8
+ {"name":"status-entry","request":{"v":1,"op":"status","params":{}},"policy":{"v":1,"package":"contract","app":"app","operations":["status","portal.register","portal.unregister"],"portal":{"application":"Contract","alias":"contract"}},"state":{},"expect":{"ok":true,"result":{"portal":"unregistered"}}}
@@ -1,2 +1,3 @@
1
1
  {"name":"ready","request":{"v":1,"op":"status","params":{}},"policy":{"v":1,"package":"contract","app":"app","scope":"home","operations":["status","delegate.create","delegate.list","delegate.revoke","files.list","files.write","files.moveFromStaging","files.operationStatus","files.cancelOperation","delegate.disable","delegate.enable","delegate.audit","root.run"],"destinations":["save"],"lifetime":"until-revoked","root":{}},"state":{},"expect":{"ok":true,"result":{"version":"1","ready":true,"disabled":false,"executor_digest":"<any>","policy_digest":"<any>"}}}
2
2
  {"name":"disabled","request":{"v":1,"op":"status","params":{}},"policy":{"v":1,"package":"contract","app":"app","scope":"home","operations":["status","delegate.create","delegate.list","delegate.revoke","files.list","files.write","files.moveFromStaging","files.operationStatus","files.cancelOperation","delegate.disable","delegate.enable","delegate.audit","root.run"],"destinations":["save"],"lifetime":"until-revoked","root":{}},"state":{"disabled":true},"expect":{"ok":true,"result":{"version":"1","ready":true,"disabled":true,"executor_digest":"<any>","policy_digest":"<any>"}}}
3
+ {"name":"caller-allowed","request":{"v":1,"op":"status","params":{},"auth":{"dsm_cookie":"alice","dsm_synotoken":"contract-token","remote_addr":"192.0.2.1"}},"policy":{"v":1,"package":"contract","app":"app","scope":"home","operations":["status","delegate.create","delegate.list","delegate.revoke","files.list","files.write","files.moveFromStaging","files.operationStatus","files.cancelOperation","delegate.disable","delegate.enable","delegate.audit","root.run"],"destinations":["save"],"lifetime":"until-revoked","root":{}},"state":{},"expect":{"ok":true,"result":{"allowed":true}}}
@@ -1,2 +0,0 @@
1
- /** Substitute the single display value in localized picker copy. */
2
- export declare function format(template: string, value: string | number): string;