@synopackageland/cli 0.6.1 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (578) hide show
  1. package/CHANGELOG.md +613 -0
  2. package/README.md +81 -89
  3. package/dist/add.js +39 -16
  4. package/dist/build-spk/delegate-scope.d.ts +6 -0
  5. package/dist/build-spk/delegate-scope.js +34 -0
  6. package/dist/build-spk/dsm-arch.d.ts +7 -1
  7. package/dist/build-spk/dsm-arch.js +24 -28
  8. package/dist/build-spk/dsm-platforms.json +39 -0
  9. package/dist/build-spk/dsm-version.d.ts +11 -0
  10. package/dist/build-spk/dsm-version.js +17 -0
  11. package/dist/build-spk/gateway.d.ts +1 -1
  12. package/dist/build-spk/gateway.js +2 -2
  13. package/dist/build-spk/generator.js +30 -5
  14. package/dist/build-spk/notices.js +4 -1
  15. package/dist/build-spk/resource-worker.js +3 -0
  16. package/dist/build-spk/wizard-messages.d.ts +66 -0
  17. package/dist/build-spk/wizard-messages.js +75 -9
  18. package/dist/cli.js +15 -7
  19. package/dist/contract/schema-validate.js +8 -0
  20. package/dist/contract/validate.js +30 -0
  21. package/dist/deploy/deploy.d.ts +1 -1
  22. package/dist/deploy/deploy.js +8 -0
  23. package/dist/deploy/plan.js +1 -1
  24. package/dist/deploy/spk-version.js +1 -1
  25. package/dist/error-registry.js +43 -15
  26. package/dist/host/dsm-client.d.ts +8 -4
  27. package/dist/host/dsm-client.js +10 -4
  28. package/dist/host/types.d.ts +2 -1
  29. package/dist/info.js +18 -2
  30. package/dist/init.d.ts +3 -0
  31. package/dist/init.js +49 -18
  32. package/dist/package-metadata.d.ts +21 -0
  33. package/dist/package-metadata.js +101 -0
  34. package/dist/portal/alias.d.ts +25 -0
  35. package/dist/portal/alias.js +48 -0
  36. package/dist/portal/package.d.ts +33 -0
  37. package/dist/portal/package.js +116 -0
  38. package/dist/runtime/delegate-contract.d.ts +79 -0
  39. package/dist/runtime/delegate-contract.js +27 -0
  40. package/dist/runtime/executor-build.d.ts +1 -1
  41. package/dist/runtime/executor-build.js +5 -4
  42. package/dist/runtime/executor-inspect.d.ts +3 -0
  43. package/dist/runtime/executor-inspect.js +11 -5
  44. package/dist/runtime/executor-policy.d.ts +12 -0
  45. package/dist/runtime/executor-policy.js +43 -3
  46. package/dist/runtime/executor-scope-task.d.ts +4 -0
  47. package/dist/runtime/executor-scope-task.js +21 -0
  48. package/dist/runtime/executor-tasks.d.ts +2 -1
  49. package/dist/runtime/executor-tasks.js +4 -4
  50. package/dist/skill.js +1 -0
  51. package/dist/standalone/api-permissions.d.ts +10 -0
  52. package/dist/standalone/api-permissions.js +19 -2
  53. package/dist/standalone/background.d.ts +16 -0
  54. package/dist/standalone/background.js +69 -2
  55. package/dist/standalone/binary.d.ts +1 -1
  56. package/dist/standalone/binary.js +22 -8
  57. package/dist/standalone/build.d.ts +1 -1
  58. package/dist/standalone/build.js +38 -8
  59. package/dist/standalone/daemon.d.ts +1 -1
  60. package/dist/standalone/daemon.js +2 -1
  61. package/dist/standalone/dsm-window.d.ts +3 -1
  62. package/dist/standalone/dsm-window.js +72 -15
  63. package/dist/standalone/inspect-spk.d.ts +8 -0
  64. package/dist/standalone/inspect-spk.js +105 -26
  65. package/dist/standalone/lifecycle.js +86 -8
  66. package/dist/standalone/manifest.d.ts +13 -0
  67. package/dist/standalone/manifest.js +57 -8
  68. package/dist/standalone/native-authorization-window.d.ts +1 -1
  69. package/dist/standalone/native-authorization-window.js +2 -0
  70. package/dist/standalone/native-layout.d.ts +3 -0
  71. package/dist/standalone/native-layout.js +37 -5
  72. package/dist/standalone/portal-window.d.ts +21 -0
  73. package/dist/standalone/portal-window.js +155 -0
  74. package/dist/standalone/portal.d.ts +4 -0
  75. package/dist/standalone/portal.js +4 -0
  76. package/dist/standalone/resource.d.ts +5 -3
  77. package/dist/standalone/resource.js +6 -3
  78. package/dist/standalone/root-window.js +62 -15
  79. package/dist/standalone/spk.d.ts +1 -1
  80. package/dist/standalone/spk.js +25 -7
  81. package/dist/templates/_daemon/main_test.go +61 -8
  82. package/dist/templates/_executor/README.md +127 -16
  83. package/dist/templates/_executor/anchor.go +146 -0
  84. package/dist/templates/_executor/anchor_test.go +126 -0
  85. package/dist/templates/_executor/apppriv_review_test.go +1 -1
  86. package/dist/templates/_executor/cancel_contract_test.go +236 -0
  87. package/dist/templates/_executor/copy.go +64 -0
  88. package/dist/templates/_executor/copy_test.go +88 -0
  89. package/dist/templates/_executor/dsm.go +23 -10
  90. package/dist/templates/_executor/dsm_test.go +95 -1
  91. package/dist/templates/_executor/executor.go +260 -49
  92. package/dist/templates/_executor/executor_test.go +36 -6
  93. package/dist/templates/_executor/identity.go +27 -1
  94. package/dist/templates/_executor/identity_kernel_linux_test.go +60 -0
  95. package/dist/templates/_executor/identity_linux.go +82 -0
  96. package/dist/templates/_executor/identity_security_test.go +43 -0
  97. package/dist/templates/_executor/internal/lifecycle/lifecycle.go +5 -0
  98. package/dist/templates/_executor/internal/lifecycle/lifecycle_test.go +61 -3
  99. package/dist/templates/_executor/internal/taskscript/boot.sh +9 -9
  100. package/dist/templates/_executor/internal/taskscript/cmd/main.go +2 -2
  101. package/dist/templates/_executor/internal/taskscript/control.sh +15 -10
  102. package/dist/templates/_executor/internal/taskscript/tasks.go +5 -3
  103. package/dist/templates/_executor/lifecycle_test.go +11 -0
  104. package/dist/templates/_executor/link_linux.go +28 -0
  105. package/dist/templates/_executor/long_running_test.go +530 -0
  106. package/dist/templates/_executor/main.go +72 -20
  107. package/dist/templates/_executor/move_linux.go +490 -0
  108. package/dist/templates/_executor/move_linux_test.go +460 -0
  109. package/dist/templates/_executor/move_validation.go +46 -0
  110. package/dist/templates/_executor/move_validation_test.go +187 -0
  111. package/dist/templates/_executor/operations.go +926 -0
  112. package/dist/templates/_executor/platform_linux.go +24 -173
  113. package/dist/templates/_executor/platform_linux_test.go +9 -65
  114. package/dist/templates/_executor/platform_other.go +30 -4
  115. package/dist/templates/_executor/process.go +3 -0
  116. package/dist/templates/_executor/records.go +242 -0
  117. package/dist/templates/_executor/scope_cancel_contract_test.go +91 -0
  118. package/dist/templates/_executor/scope_client.go +37 -0
  119. package/dist/templates/_executor/scope_contract.go +12 -0
  120. package/dist/templates/_executor/selected_scope.go +146 -0
  121. package/dist/templates/_executor/selected_scope_test.go +186 -0
  122. package/dist/templates/_executor/status_disabled_test.go +33 -0
  123. package/dist/templates/_executor/store.go +4 -3
  124. package/dist/templates/_executor/supervise.go +109 -0
  125. package/dist/templates/_executor/supervise_test.go +130 -0
  126. package/dist/templates/_executor/task_race_review_test.go +1 -1
  127. package/dist/templates/_executor/tasks.go +7 -8
  128. package/dist/templates/_executor/tasks_review_test.go +1 -1
  129. package/dist/templates/_executor/tasks_test.go +4 -4
  130. package/dist/templates/_executor/watch.go +8 -0
  131. package/dist/templates/_executor/watch_test.go +23 -2
  132. package/dist/templates/_executor/write_review_linux_test.go +4 -12
  133. package/dist/templates/_shared/AGENTS.md +129 -0
  134. package/dist/templates/binary/dsm-package.yaml +24 -0
  135. package/dist/templates/binary/i18n/messages/de.json +4 -0
  136. package/dist/templates/binary/i18n/messages/en.json +4 -0
  137. package/dist/templates/binary/i18n/messages/es.json +4 -0
  138. package/dist/templates/binary/i18n/messages/fr.json +4 -0
  139. package/dist/templates/binary/i18n/messages/it.json +4 -0
  140. package/dist/templates/binary/i18n/messages/ja.json +4 -0
  141. package/dist/templates/binary/i18n/messages/ko.json +4 -0
  142. package/dist/templates/binary/i18n/messages/zh-CN.json +4 -0
  143. package/dist/templates/binary/i18n/messages/zh-TW.json +4 -0
  144. package/dist/templates/binary/ui/app.js +19 -4
  145. package/dist/templates/native-authorization/AuthorizationGate.vue +5 -3
  146. package/dist/templates/native-authorization/i18n/de.json +1 -0
  147. package/dist/templates/native-authorization/i18n/en.json +1 -0
  148. package/dist/templates/native-authorization/i18n/es.json +2 -1
  149. package/dist/templates/native-authorization/i18n/fr.json +1 -0
  150. package/dist/templates/native-authorization/i18n/it.json +11 -10
  151. package/dist/templates/native-authorization/i18n/ja.json +1 -0
  152. package/dist/templates/native-authorization/i18n/ko.json +10 -9
  153. package/dist/templates/native-authorization/i18n/zh-CN.json +6 -5
  154. package/dist/templates/native-authorization/i18n/zh-TW.json +6 -5
  155. package/dist/templates/native-go/README.md +43 -10
  156. package/dist/templates/native-go/backend/cmd/app/main.go +27 -7
  157. package/dist/templates/native-go/backend/internal/api/entry_test.go +47 -0
  158. package/dist/templates/native-go/backend/internal/api/permissions_test.go +3 -3
  159. package/dist/templates/native-go/backend/internal/api/registry.go +11 -0
  160. package/dist/templates/native-go/backend/internal/api/sdk-permissions.json +14 -1
  161. package/dist/templates/native-go/backend/internal/api/server.go +18 -5
  162. package/dist/templates/native-go/backend/internal/app/delegate_config.go +57 -0
  163. package/dist/templates/native-go/backend/internal/app/delegate_config_test.go +28 -0
  164. package/dist/templates/native-go/backend/internal/app/portal.go +30 -0
  165. package/dist/templates/native-go/backend/internal/app/portal_test.go +55 -0
  166. package/dist/templates/native-go/backend/internal/app/routes.go +8 -2
  167. package/dist/templates/native-go/backend/internal/background/runner.go +1 -1
  168. package/dist/templates/native-go/backend/internal/background/store.go +16 -14
  169. package/dist/templates/native-go/backend/internal/dsm/admin.go +8 -2
  170. package/dist/templates/native-go/backend/internal/dsm/admin_linux.go +43 -13
  171. package/dist/templates/native-go/backend/internal/dsm/admin_linux_test.go +56 -0
  172. package/dist/templates/native-go/backend/internal/dsm/admin_other.go +4 -1
  173. package/dist/templates/native-go/backend/internal/dsm/apppriv.go +30 -0
  174. package/dist/templates/native-go/backend/internal/dsm/bridge_file.go +75 -0
  175. package/dist/templates/native-go/backend/internal/dsm/bridge_file_test.go +57 -0
  176. package/dist/templates/native-go/backend/internal/dsm/entry.go +177 -0
  177. package/dist/templates/native-go/backend/internal/dsm/entry_test.go +315 -0
  178. package/dist/templates/native-go/backend/internal/dsm/native/admin-armv7.elf +0 -0
  179. package/dist/templates/native-go/backend/internal/dsm/native/apppriv-aarch64.elf +0 -0
  180. package/dist/templates/native-go/backend/internal/dsm/native/apppriv-armv7.elf +0 -0
  181. package/dist/templates/native-go/backend/internal/dsm/native/apppriv-x86_64.elf +0 -0
  182. package/dist/templates/native-go/backend/internal/dsm/native/apppriv_check.c +33 -0
  183. package/dist/templates/native-go/backend/internal/dsm/native/build.sh +13 -4
  184. package/dist/templates/native-go/backend/internal/dsm/native/provenance.json +9 -3
  185. package/dist/templates/native-go/backend/internal/filestation/check_permission.go +71 -0
  186. package/dist/templates/native-go/backend/internal/filestation/check_permission_test.go +77 -0
  187. package/dist/templates/native-go/backend/internal/filestation/sdk_policy_test.go +39 -0
  188. package/dist/templates/native-go/backend/internal/filestation/service.go +1 -1
  189. package/dist/templates/native-go/backend/internal/portal/portal.go +138 -0
  190. package/dist/templates/native-go/backend/internal/portal/portal_test.go +89 -0
  191. package/dist/templates/native-go/dsm-package.yaml +20 -1
  192. package/dist/templates/native-go/i18n/glossary.json +2 -1
  193. package/dist/templates/native-go/i18n/messages/de.json +1 -0
  194. package/dist/templates/native-go/i18n/messages/en.json +1 -0
  195. package/dist/templates/native-go/i18n/messages/es.json +1 -0
  196. package/dist/templates/native-go/i18n/messages/fr.json +1 -0
  197. package/dist/templates/native-go/i18n/messages/it.json +1 -0
  198. package/dist/templates/native-go/i18n/messages/ja.json +1 -0
  199. package/dist/templates/native-go/i18n/messages/ko.json +1 -0
  200. package/dist/templates/native-go/i18n/messages/zh-CN.json +1 -0
  201. package/dist/templates/native-go/i18n/messages/zh-TW.json +1 -0
  202. package/dist/templates/native-go/ui/app.css +10 -0
  203. package/dist/templates/native-go/ui/app.js +11 -1
  204. package/dist/templates/native-go-delegate/DELEGATE.md +126 -5
  205. package/dist/templates/native-go-delegate/backend/cmd/background-worker/main.go +7 -0
  206. package/dist/templates/native-go-delegate/backend/internal/app/delegate.go +11 -1
  207. package/dist/templates/native-go-delegate/backend/internal/app/delegate_routes_test.go +14 -0
  208. package/dist/templates/native-go-delegate/backend/internal/background/delegated_moves.go +270 -0
  209. package/dist/templates/native-go-delegate/backend/internal/delegate/admission_recovery_test.go +410 -0
  210. package/dist/templates/native-go-delegate/backend/internal/delegate/cancel_contract_test.go +95 -0
  211. package/dist/templates/native-go-delegate/backend/internal/delegate/client.go +8 -0
  212. package/dist/templates/native-go-delegate/backend/internal/delegate/destination.go +110 -0
  213. package/dist/templates/native-go-delegate/backend/internal/delegate/destination_state.go +117 -0
  214. package/dist/templates/native-go-delegate/backend/internal/delegate/destination_state_test.go +60 -0
  215. package/dist/templates/native-go-delegate/backend/internal/delegate/destination_test.go +116 -0
  216. package/dist/templates/native-go-delegate/backend/internal/delegate/example.go +81 -0
  217. package/dist/templates/native-go-delegate/backend/internal/delegate/example_test.go +91 -0
  218. package/dist/templates/native-go-delegate/backend/internal/delegate/executor_contract_test.go +511 -0
  219. package/dist/templates/native-go-delegate/backend/internal/delegate/files.go +1 -1
  220. package/dist/templates/native-go-delegate/backend/internal/delegate/long_running_routes.go +92 -0
  221. package/dist/templates/native-go-delegate/backend/internal/delegate/long_running_routes_test.go +99 -0
  222. package/dist/templates/native-go-delegate/backend/internal/delegate/move.go +348 -0
  223. package/dist/templates/native-go-delegate/backend/internal/delegate/move_test.go +351 -0
  224. package/dist/templates/native-go-delegate/backend/internal/delegate/operations_list.go +65 -0
  225. package/dist/templates/native-go-delegate/backend/internal/delegate/operations_list_test.go +114 -0
  226. package/dist/templates/native-go-delegate/backend/internal/delegate/retry_test.go +1 -1
  227. package/dist/templates/native-go-delegate/backend/internal/delegate/service.go +58 -17
  228. package/dist/templates/native-go-delegate/backend/internal/delegate/staging.go +121 -0
  229. package/dist/templates/native-go-delegate/backend/internal/delegate/status_disabled_test.go +51 -0
  230. package/dist/templates/native-go-delegate/backend/internal/delegate/testdata/executor_contract.txt +112 -0
  231. package/dist/templates/native-go-delegate/backend/internal/delegate/transient_contract_test.go +137 -0
  232. package/dist/templates/native-go-delegate/backend/internal/filestation/files.go +1 -31
  233. package/dist/templates/native-go-delegate/frontend/src/CapabilityPanels.vue +30 -3
  234. package/dist/templates/native-go-delegate/frontend/src/delegate/BackgroundSavePanel.vue +113 -0
  235. package/dist/templates/native-go-delegate/frontend/src/delegate/copy.ts +5 -6
  236. package/dist/templates/native-go-delegate/frontend/test/background-save.test.ts +121 -0
  237. package/dist/templates/native-go-delegate/frontend/test/scope-settings.test.ts +49 -0
  238. package/dist/templates/native-go-delegate/test.sh +1 -0
  239. package/dist/templates/native-go-jobs/frontend/src/background/i18n/de.json +2 -2
  240. package/dist/templates/native-go-jobs/frontend/src/background/i18n/en.json +2 -2
  241. package/dist/templates/native-go-jobs/frontend/src/background/i18n/es.json +12 -12
  242. package/dist/templates/native-go-jobs/frontend/src/background/i18n/fr.json +4 -4
  243. package/dist/templates/native-go-jobs/frontend/src/background/i18n/it.json +17 -17
  244. package/dist/templates/native-go-jobs/frontend/src/background/i18n/ja.json +7 -7
  245. package/dist/templates/native-go-jobs/frontend/src/background/i18n/ko.json +15 -15
  246. package/dist/templates/native-go-jobs/frontend/src/background/i18n/zh-CN.json +3 -3
  247. package/dist/templates/native-go-jobs/frontend/src/background/i18n/zh-TW.json +2 -2
  248. package/dist/templates/native-go-jobs/frontend/test/background.test.ts +25 -28
  249. package/dist/templates/native-go-vue/README.md +4 -0
  250. package/dist/templates/native-go-vue/frontend/package-lock.json +4 -4
  251. package/dist/templates/native-go-vue/frontend/package.json +1 -1
  252. package/dist/templates/native-go-vue/frontend/src/App.vue +18 -13
  253. package/dist/templates/native-go-vue/frontend/src/context.ts +31 -0
  254. package/dist/templates/native-go-vue/frontend/src/delegate/ExecutorSettings.vue +52 -8
  255. package/dist/templates/native-go-vue/frontend/src/delegate/catalogs.ts +11 -0
  256. package/dist/templates/native-go-vue/frontend/src/delegate/copy.test.ts +12 -0
  257. package/dist/templates/native-go-vue/frontend/src/delegate/copy.ts +4 -4
  258. package/dist/templates/native-go-vue/frontend/src/delegate/executor-setup.ts +4 -2
  259. package/dist/templates/native-go-vue/frontend/src/delegate/facade.test.ts +100 -0
  260. package/dist/templates/native-go-vue/frontend/src/delegate/facade.ts +85 -0
  261. package/dist/templates/native-go-vue/frontend/src/delegate/i18n/de.json +46 -0
  262. package/dist/templates/native-go-vue/frontend/src/delegate/i18n/en.json +12 -2
  263. package/dist/templates/native-go-vue/frontend/src/delegate/i18n/es.json +46 -0
  264. package/dist/templates/native-go-vue/frontend/src/delegate/i18n/fr.json +46 -0
  265. package/dist/templates/native-go-vue/frontend/src/delegate/i18n/it.json +46 -0
  266. package/dist/templates/native-go-vue/frontend/src/delegate/i18n/ja.json +46 -0
  267. package/dist/templates/native-go-vue/frontend/src/delegate/i18n/ko.json +46 -0
  268. package/dist/templates/native-go-vue/frontend/src/delegate/i18n/save/de.json +42 -0
  269. package/dist/templates/native-go-vue/frontend/src/delegate/i18n/save/en.json +42 -0
  270. package/dist/templates/native-go-vue/frontend/src/delegate/i18n/save/es.json +42 -0
  271. package/dist/templates/native-go-vue/frontend/src/delegate/i18n/save/fr.json +42 -0
  272. package/dist/templates/native-go-vue/frontend/src/delegate/i18n/save/it.json +42 -0
  273. package/dist/templates/native-go-vue/frontend/src/delegate/i18n/save/ja.json +42 -0
  274. package/dist/templates/native-go-vue/frontend/src/delegate/i18n/save/ko.json +42 -0
  275. package/dist/templates/native-go-vue/frontend/src/delegate/i18n/save/zh-CN.json +42 -0
  276. package/dist/templates/native-go-vue/frontend/src/delegate/i18n/save/zh-TW.json +42 -0
  277. package/dist/templates/native-go-vue/frontend/src/delegate/i18n/zh-CN.json +46 -0
  278. package/dist/templates/native-go-vue/frontend/src/delegate/i18n/zh-TW.json +15 -5
  279. package/dist/templates/native-go-vue/frontend/src/delegate/long-running.ts +57 -0
  280. package/dist/templates/native-go-vue/frontend/src/delegate/save-copy.test.ts +12 -0
  281. package/dist/templates/native-go-vue/frontend/src/delegate/save-copy.ts +24 -0
  282. package/dist/templates/native-go-vue/frontend/src/delegate/scoped-picker.test.ts +12 -0
  283. package/dist/templates/native-go-vue/frontend/src/delegate/scoped-picker.ts +21 -0
  284. package/dist/templates/native-go-vue/frontend/src/files/files.test.ts +7 -0
  285. package/dist/templates/native-go-vue/frontend/src/i18n/de.json +20 -0
  286. package/dist/templates/native-go-vue/frontend/src/i18n/en.json +20 -0
  287. package/dist/templates/native-go-vue/frontend/src/i18n/es.json +20 -0
  288. package/dist/templates/native-go-vue/frontend/src/i18n/fr.json +20 -0
  289. package/dist/templates/native-go-vue/frontend/src/i18n/it.json +20 -0
  290. package/dist/templates/native-go-vue/frontend/src/i18n/ja.json +20 -0
  291. package/dist/templates/native-go-vue/frontend/src/i18n/ko.json +20 -0
  292. package/dist/templates/native-go-vue/frontend/src/i18n/zh-CN.json +20 -0
  293. package/dist/templates/native-go-vue/frontend/src/i18n/zh-TW.json +20 -0
  294. package/dist/templates/native-go-vue/frontend/src/i18n.ts +13 -0
  295. package/dist/templates/native-go-vue/frontend/src/identity/LoginPage.vue +16 -14
  296. package/dist/templates/native-go-vue/frontend/src/identity/i18n/de.json +27 -0
  297. package/dist/templates/native-go-vue/frontend/src/identity/i18n/en.json +27 -0
  298. package/dist/templates/native-go-vue/frontend/src/identity/i18n/es.json +27 -0
  299. package/dist/templates/native-go-vue/frontend/src/identity/i18n/fr.json +27 -0
  300. package/dist/templates/native-go-vue/frontend/src/identity/i18n/it.json +27 -0
  301. package/dist/templates/native-go-vue/frontend/src/identity/i18n/ja.json +27 -0
  302. package/dist/templates/native-go-vue/frontend/src/identity/i18n/ko.json +27 -0
  303. package/dist/templates/native-go-vue/frontend/src/identity/i18n/zh-CN.json +27 -0
  304. package/dist/templates/native-go-vue/frontend/src/identity/i18n/zh-TW.json +27 -0
  305. package/dist/templates/native-go-vue/frontend/src/identity/i18n.ts +14 -0
  306. package/dist/templates/native-go-vue/frontend/src/identity/session.test.ts +4 -0
  307. package/dist/templates/native-go-vue/frontend/src/identity/session.ts +9 -8
  308. package/dist/templates/native-go-vue/frontend/src/nas/NasPicker.vue +31 -12
  309. package/dist/templates/native-go-vue/frontend/src/nas/destination.test.ts +48 -0
  310. package/dist/templates/native-go-vue/frontend/src/nas/i18n/files/de.json +1 -1
  311. package/dist/templates/native-go-vue/frontend/src/nas/i18n/files/en.json +1 -1
  312. package/dist/templates/native-go-vue/frontend/src/nas/i18n/files/es.json +10 -10
  313. package/dist/templates/native-go-vue/frontend/src/nas/i18n/files/fr.json +1 -1
  314. package/dist/templates/native-go-vue/frontend/src/nas/i18n/files/it.json +12 -12
  315. package/dist/templates/native-go-vue/frontend/src/nas/i18n/files/ja.json +15 -15
  316. package/dist/templates/native-go-vue/frontend/src/nas/i18n/files/ko.json +12 -12
  317. package/dist/templates/native-go-vue/frontend/src/nas/i18n/files/zh-CN.json +1 -1
  318. package/dist/templates/native-go-vue/frontend/src/nas/i18n/files/zh-TW.json +1 -1
  319. package/dist/templates/native-go-vue/frontend/src/portal/PortalSettings.vue +39 -0
  320. package/dist/templates/native-go-vue/frontend/src/shell/i18n/fr.json +1 -1
  321. package/dist/templates/native-go-vue/frontend/src/shell/i18n/ja.json +1 -1
  322. package/dist/templates/native-go-vue/frontend/test/apps.test.ts +14 -0
  323. package/dist/templates/native-go-vue/frontend/test/portal-settings.test.ts +71 -0
  324. package/dist/templates/native-go-vue/frontend/test/shell-i18n.test.ts +50 -0
  325. package/package.json +5 -1
  326. package/schema/package-metadata.schema.json +101 -0
  327. package/schema/synology-app.schema.json +112 -0
  328. package/skills/SKILL.md +106 -4
  329. package/skills/reference/create.md +101 -2
  330. package/skills/reference/deploy.md +2 -2
  331. package/skills/reference/files.md +113 -2
  332. package/skills/reference/identity.md +70 -1
  333. package/skills/reference/portal.md +17 -0
  334. package/skills/reference/ui-vue.md +12 -0
  335. package/templates/_daemon/main_test.go +61 -8
  336. package/templates/_executor/README.md +127 -16
  337. package/templates/_executor/anchor.go +146 -0
  338. package/templates/_executor/anchor_test.go +126 -0
  339. package/templates/_executor/apppriv_review_test.go +1 -1
  340. package/templates/_executor/cancel_contract_test.go +236 -0
  341. package/templates/_executor/copy.go +64 -0
  342. package/templates/_executor/copy_test.go +88 -0
  343. package/templates/_executor/dsm.go +23 -10
  344. package/templates/_executor/dsm_test.go +95 -1
  345. package/templates/_executor/executor.go +260 -49
  346. package/templates/_executor/executor_test.go +36 -6
  347. package/templates/_executor/identity.go +27 -1
  348. package/templates/_executor/identity_kernel_linux_test.go +60 -0
  349. package/templates/_executor/identity_linux.go +82 -0
  350. package/templates/_executor/identity_security_test.go +43 -0
  351. package/templates/_executor/internal/lifecycle/lifecycle.go +5 -0
  352. package/templates/_executor/internal/lifecycle/lifecycle_test.go +61 -3
  353. package/templates/_executor/internal/taskscript/boot.sh +9 -9
  354. package/templates/_executor/internal/taskscript/cmd/main.go +2 -2
  355. package/templates/_executor/internal/taskscript/control.sh +15 -10
  356. package/templates/_executor/internal/taskscript/tasks.go +5 -3
  357. package/templates/_executor/lifecycle_test.go +11 -0
  358. package/templates/_executor/link_linux.go +28 -0
  359. package/templates/_executor/long_running_test.go +530 -0
  360. package/templates/_executor/main.go +72 -20
  361. package/templates/_executor/move_linux.go +490 -0
  362. package/templates/_executor/move_linux_test.go +460 -0
  363. package/templates/_executor/move_validation.go +46 -0
  364. package/templates/_executor/move_validation_test.go +187 -0
  365. package/templates/_executor/operations.go +926 -0
  366. package/templates/_executor/platform_linux.go +24 -173
  367. package/templates/_executor/platform_linux_test.go +9 -65
  368. package/templates/_executor/platform_other.go +30 -4
  369. package/templates/_executor/process.go +3 -0
  370. package/templates/_executor/records.go +242 -0
  371. package/templates/_executor/scope_cancel_contract_test.go +91 -0
  372. package/templates/_executor/scope_client.go +37 -0
  373. package/templates/_executor/scope_contract.go +12 -0
  374. package/templates/_executor/selected_scope.go +146 -0
  375. package/templates/_executor/selected_scope_test.go +186 -0
  376. package/templates/_executor/status_disabled_test.go +33 -0
  377. package/templates/_executor/store.go +4 -3
  378. package/templates/_executor/supervise.go +109 -0
  379. package/templates/_executor/supervise_test.go +130 -0
  380. package/templates/_executor/task_race_review_test.go +1 -1
  381. package/templates/_executor/tasks.go +7 -8
  382. package/templates/_executor/tasks_review_test.go +1 -1
  383. package/templates/_executor/tasks_test.go +4 -4
  384. package/templates/_executor/watch.go +8 -0
  385. package/templates/_executor/watch_test.go +23 -2
  386. package/templates/_executor/write_review_linux_test.go +4 -12
  387. package/templates/_shared/AGENTS.md +129 -0
  388. package/templates/binary/dsm-package.yaml +24 -0
  389. package/templates/binary/i18n/messages/de.json +4 -0
  390. package/templates/binary/i18n/messages/en.json +4 -0
  391. package/templates/binary/i18n/messages/es.json +4 -0
  392. package/templates/binary/i18n/messages/fr.json +4 -0
  393. package/templates/binary/i18n/messages/it.json +4 -0
  394. package/templates/binary/i18n/messages/ja.json +4 -0
  395. package/templates/binary/i18n/messages/ko.json +4 -0
  396. package/templates/binary/i18n/messages/zh-CN.json +4 -0
  397. package/templates/binary/i18n/messages/zh-TW.json +4 -0
  398. package/templates/binary/ui/app.js +19 -4
  399. package/templates/native-authorization/AuthorizationGate.vue +5 -3
  400. package/templates/native-authorization/i18n/de.json +1 -0
  401. package/templates/native-authorization/i18n/en.json +1 -0
  402. package/templates/native-authorization/i18n/es.json +2 -1
  403. package/templates/native-authorization/i18n/fr.json +1 -0
  404. package/templates/native-authorization/i18n/it.json +11 -10
  405. package/templates/native-authorization/i18n/ja.json +1 -0
  406. package/templates/native-authorization/i18n/ko.json +10 -9
  407. package/templates/native-authorization/i18n/zh-CN.json +6 -5
  408. package/templates/native-authorization/i18n/zh-TW.json +6 -5
  409. package/templates/native-go/README.md +43 -10
  410. package/templates/native-go/backend/cmd/app/main.go +27 -7
  411. package/templates/native-go/backend/internal/api/entry_test.go +47 -0
  412. package/templates/native-go/backend/internal/api/permissions_test.go +3 -3
  413. package/templates/native-go/backend/internal/api/registry.go +11 -0
  414. package/templates/native-go/backend/internal/api/sdk-permissions.json +14 -1
  415. package/templates/native-go/backend/internal/api/server.go +18 -5
  416. package/templates/native-go/backend/internal/app/delegate_config.go +57 -0
  417. package/templates/native-go/backend/internal/app/delegate_config_test.go +28 -0
  418. package/templates/native-go/backend/internal/app/portal.go +30 -0
  419. package/templates/native-go/backend/internal/app/portal_test.go +55 -0
  420. package/templates/native-go/backend/internal/app/routes.go +8 -2
  421. package/templates/native-go/backend/internal/background/runner.go +1 -1
  422. package/templates/native-go/backend/internal/background/store.go +16 -14
  423. package/templates/native-go/backend/internal/dsm/admin.go +8 -2
  424. package/templates/native-go/backend/internal/dsm/admin_linux.go +43 -13
  425. package/templates/native-go/backend/internal/dsm/admin_linux_test.go +56 -0
  426. package/templates/native-go/backend/internal/dsm/admin_other.go +4 -1
  427. package/templates/native-go/backend/internal/dsm/apppriv.go +30 -0
  428. package/templates/native-go/backend/internal/dsm/bridge_file.go +75 -0
  429. package/templates/native-go/backend/internal/dsm/bridge_file_test.go +57 -0
  430. package/templates/native-go/backend/internal/dsm/entry.go +177 -0
  431. package/templates/native-go/backend/internal/dsm/entry_test.go +315 -0
  432. package/templates/native-go/backend/internal/dsm/native/admin-armv7.elf +0 -0
  433. package/templates/native-go/backend/internal/dsm/native/apppriv-aarch64.elf +0 -0
  434. package/templates/native-go/backend/internal/dsm/native/apppriv-armv7.elf +0 -0
  435. package/templates/native-go/backend/internal/dsm/native/apppriv-x86_64.elf +0 -0
  436. package/templates/native-go/backend/internal/dsm/native/apppriv_check.c +33 -0
  437. package/templates/native-go/backend/internal/dsm/native/build.sh +13 -4
  438. package/templates/native-go/backend/internal/dsm/native/provenance.json +9 -3
  439. package/templates/native-go/backend/internal/filestation/check_permission.go +71 -0
  440. package/templates/native-go/backend/internal/filestation/check_permission_test.go +77 -0
  441. package/templates/native-go/backend/internal/filestation/sdk_policy_test.go +39 -0
  442. package/templates/native-go/backend/internal/filestation/service.go +1 -1
  443. package/templates/native-go/backend/internal/portal/portal.go +138 -0
  444. package/templates/native-go/backend/internal/portal/portal_test.go +89 -0
  445. package/templates/native-go/dsm-package.yaml +20 -1
  446. package/templates/native-go/i18n/glossary.json +2 -1
  447. package/templates/native-go/i18n/messages/de.json +1 -0
  448. package/templates/native-go/i18n/messages/en.json +1 -0
  449. package/templates/native-go/i18n/messages/es.json +1 -0
  450. package/templates/native-go/i18n/messages/fr.json +1 -0
  451. package/templates/native-go/i18n/messages/it.json +1 -0
  452. package/templates/native-go/i18n/messages/ja.json +1 -0
  453. package/templates/native-go/i18n/messages/ko.json +1 -0
  454. package/templates/native-go/i18n/messages/zh-CN.json +1 -0
  455. package/templates/native-go/i18n/messages/zh-TW.json +1 -0
  456. package/templates/native-go/ui/app.css +10 -0
  457. package/templates/native-go/ui/app.js +11 -1
  458. package/templates/native-go-delegate/DELEGATE.md +126 -5
  459. package/templates/native-go-delegate/backend/cmd/background-worker/main.go +7 -0
  460. package/templates/native-go-delegate/backend/internal/app/delegate.go +11 -1
  461. package/templates/native-go-delegate/backend/internal/app/delegate_routes_test.go +14 -0
  462. package/templates/native-go-delegate/backend/internal/background/delegated_moves.go +270 -0
  463. package/templates/native-go-delegate/backend/internal/delegate/admission_recovery_test.go +410 -0
  464. package/templates/native-go-delegate/backend/internal/delegate/cancel_contract_test.go +95 -0
  465. package/templates/native-go-delegate/backend/internal/delegate/client.go +8 -0
  466. package/templates/native-go-delegate/backend/internal/delegate/destination.go +110 -0
  467. package/templates/native-go-delegate/backend/internal/delegate/destination_state.go +117 -0
  468. package/templates/native-go-delegate/backend/internal/delegate/destination_state_test.go +60 -0
  469. package/templates/native-go-delegate/backend/internal/delegate/destination_test.go +116 -0
  470. package/templates/native-go-delegate/backend/internal/delegate/example.go +81 -0
  471. package/templates/native-go-delegate/backend/internal/delegate/example_test.go +91 -0
  472. package/templates/native-go-delegate/backend/internal/delegate/executor_contract_test.go +511 -0
  473. package/templates/native-go-delegate/backend/internal/delegate/files.go +1 -1
  474. package/templates/native-go-delegate/backend/internal/delegate/long_running_routes.go +92 -0
  475. package/templates/native-go-delegate/backend/internal/delegate/long_running_routes_test.go +99 -0
  476. package/templates/native-go-delegate/backend/internal/delegate/move.go +348 -0
  477. package/templates/native-go-delegate/backend/internal/delegate/move_test.go +351 -0
  478. package/templates/native-go-delegate/backend/internal/delegate/operations_list.go +65 -0
  479. package/templates/native-go-delegate/backend/internal/delegate/operations_list_test.go +114 -0
  480. package/templates/native-go-delegate/backend/internal/delegate/retry_test.go +1 -1
  481. package/templates/native-go-delegate/backend/internal/delegate/service.go +58 -17
  482. package/templates/native-go-delegate/backend/internal/delegate/staging.go +121 -0
  483. package/templates/native-go-delegate/backend/internal/delegate/status_disabled_test.go +51 -0
  484. package/templates/native-go-delegate/backend/internal/delegate/testdata/executor_contract.txt +112 -0
  485. package/templates/native-go-delegate/backend/internal/delegate/transient_contract_test.go +137 -0
  486. package/templates/native-go-delegate/backend/internal/filestation/files.go +1 -31
  487. package/templates/native-go-delegate/frontend/src/CapabilityPanels.vue +30 -3
  488. package/templates/native-go-delegate/frontend/src/delegate/BackgroundSavePanel.vue +113 -0
  489. package/templates/native-go-delegate/frontend/src/delegate/copy.ts +5 -6
  490. package/templates/native-go-delegate/frontend/test/background-save.test.ts +121 -0
  491. package/templates/native-go-delegate/frontend/test/scope-settings.test.ts +49 -0
  492. package/templates/native-go-delegate/test.sh +1 -0
  493. package/templates/native-go-jobs/frontend/src/background/i18n/de.json +2 -2
  494. package/templates/native-go-jobs/frontend/src/background/i18n/en.json +2 -2
  495. package/templates/native-go-jobs/frontend/src/background/i18n/es.json +12 -12
  496. package/templates/native-go-jobs/frontend/src/background/i18n/fr.json +4 -4
  497. package/templates/native-go-jobs/frontend/src/background/i18n/it.json +17 -17
  498. package/templates/native-go-jobs/frontend/src/background/i18n/ja.json +7 -7
  499. package/templates/native-go-jobs/frontend/src/background/i18n/ko.json +15 -15
  500. package/templates/native-go-jobs/frontend/src/background/i18n/zh-CN.json +3 -3
  501. package/templates/native-go-jobs/frontend/src/background/i18n/zh-TW.json +2 -2
  502. package/templates/native-go-jobs/frontend/test/background.test.ts +25 -28
  503. package/templates/native-go-vue/README.md +4 -0
  504. package/templates/native-go-vue/frontend/package-lock.json +4 -4
  505. package/templates/native-go-vue/frontend/package.json +1 -1
  506. package/templates/native-go-vue/frontend/src/App.vue +18 -13
  507. package/templates/native-go-vue/frontend/src/context.ts +31 -0
  508. package/templates/native-go-vue/frontend/src/delegate/ExecutorSettings.vue +52 -8
  509. package/templates/native-go-vue/frontend/src/delegate/catalogs.ts +11 -0
  510. package/templates/native-go-vue/frontend/src/delegate/copy.test.ts +12 -0
  511. package/templates/native-go-vue/frontend/src/delegate/copy.ts +4 -4
  512. package/templates/native-go-vue/frontend/src/delegate/executor-setup.ts +4 -2
  513. package/templates/native-go-vue/frontend/src/delegate/facade.test.ts +100 -0
  514. package/templates/native-go-vue/frontend/src/delegate/facade.ts +85 -0
  515. package/templates/native-go-vue/frontend/src/delegate/i18n/de.json +46 -0
  516. package/templates/native-go-vue/frontend/src/delegate/i18n/en.json +12 -2
  517. package/templates/native-go-vue/frontend/src/delegate/i18n/es.json +46 -0
  518. package/templates/native-go-vue/frontend/src/delegate/i18n/fr.json +46 -0
  519. package/templates/native-go-vue/frontend/src/delegate/i18n/it.json +46 -0
  520. package/templates/native-go-vue/frontend/src/delegate/i18n/ja.json +46 -0
  521. package/templates/native-go-vue/frontend/src/delegate/i18n/ko.json +46 -0
  522. package/templates/native-go-vue/frontend/src/delegate/i18n/save/de.json +42 -0
  523. package/templates/native-go-vue/frontend/src/delegate/i18n/save/en.json +42 -0
  524. package/templates/native-go-vue/frontend/src/delegate/i18n/save/es.json +42 -0
  525. package/templates/native-go-vue/frontend/src/delegate/i18n/save/fr.json +42 -0
  526. package/templates/native-go-vue/frontend/src/delegate/i18n/save/it.json +42 -0
  527. package/templates/native-go-vue/frontend/src/delegate/i18n/save/ja.json +42 -0
  528. package/templates/native-go-vue/frontend/src/delegate/i18n/save/ko.json +42 -0
  529. package/templates/native-go-vue/frontend/src/delegate/i18n/save/zh-CN.json +42 -0
  530. package/templates/native-go-vue/frontend/src/delegate/i18n/save/zh-TW.json +42 -0
  531. package/templates/native-go-vue/frontend/src/delegate/i18n/zh-CN.json +46 -0
  532. package/templates/native-go-vue/frontend/src/delegate/i18n/zh-TW.json +15 -5
  533. package/templates/native-go-vue/frontend/src/delegate/long-running.ts +57 -0
  534. package/templates/native-go-vue/frontend/src/delegate/save-copy.test.ts +12 -0
  535. package/templates/native-go-vue/frontend/src/delegate/save-copy.ts +24 -0
  536. package/templates/native-go-vue/frontend/src/delegate/scoped-picker.test.ts +12 -0
  537. package/templates/native-go-vue/frontend/src/delegate/scoped-picker.ts +21 -0
  538. package/templates/native-go-vue/frontend/src/files/files.test.ts +7 -0
  539. package/templates/native-go-vue/frontend/src/i18n/de.json +20 -0
  540. package/templates/native-go-vue/frontend/src/i18n/en.json +20 -0
  541. package/templates/native-go-vue/frontend/src/i18n/es.json +20 -0
  542. package/templates/native-go-vue/frontend/src/i18n/fr.json +20 -0
  543. package/templates/native-go-vue/frontend/src/i18n/it.json +20 -0
  544. package/templates/native-go-vue/frontend/src/i18n/ja.json +20 -0
  545. package/templates/native-go-vue/frontend/src/i18n/ko.json +20 -0
  546. package/templates/native-go-vue/frontend/src/i18n/zh-CN.json +20 -0
  547. package/templates/native-go-vue/frontend/src/i18n/zh-TW.json +20 -0
  548. package/templates/native-go-vue/frontend/src/i18n.ts +13 -0
  549. package/templates/native-go-vue/frontend/src/identity/LoginPage.vue +16 -14
  550. package/templates/native-go-vue/frontend/src/identity/i18n/de.json +27 -0
  551. package/templates/native-go-vue/frontend/src/identity/i18n/en.json +27 -0
  552. package/templates/native-go-vue/frontend/src/identity/i18n/es.json +27 -0
  553. package/templates/native-go-vue/frontend/src/identity/i18n/fr.json +27 -0
  554. package/templates/native-go-vue/frontend/src/identity/i18n/it.json +27 -0
  555. package/templates/native-go-vue/frontend/src/identity/i18n/ja.json +27 -0
  556. package/templates/native-go-vue/frontend/src/identity/i18n/ko.json +27 -0
  557. package/templates/native-go-vue/frontend/src/identity/i18n/zh-CN.json +27 -0
  558. package/templates/native-go-vue/frontend/src/identity/i18n/zh-TW.json +27 -0
  559. package/templates/native-go-vue/frontend/src/identity/i18n.ts +14 -0
  560. package/templates/native-go-vue/frontend/src/identity/session.test.ts +4 -0
  561. package/templates/native-go-vue/frontend/src/identity/session.ts +9 -8
  562. package/templates/native-go-vue/frontend/src/nas/NasPicker.vue +31 -12
  563. package/templates/native-go-vue/frontend/src/nas/destination.test.ts +48 -0
  564. package/templates/native-go-vue/frontend/src/nas/i18n/files/de.json +1 -1
  565. package/templates/native-go-vue/frontend/src/nas/i18n/files/en.json +1 -1
  566. package/templates/native-go-vue/frontend/src/nas/i18n/files/es.json +10 -10
  567. package/templates/native-go-vue/frontend/src/nas/i18n/files/fr.json +1 -1
  568. package/templates/native-go-vue/frontend/src/nas/i18n/files/it.json +12 -12
  569. package/templates/native-go-vue/frontend/src/nas/i18n/files/ja.json +15 -15
  570. package/templates/native-go-vue/frontend/src/nas/i18n/files/ko.json +12 -12
  571. package/templates/native-go-vue/frontend/src/nas/i18n/files/zh-CN.json +1 -1
  572. package/templates/native-go-vue/frontend/src/nas/i18n/files/zh-TW.json +1 -1
  573. package/templates/native-go-vue/frontend/src/portal/PortalSettings.vue +39 -0
  574. package/templates/native-go-vue/frontend/src/shell/i18n/fr.json +1 -1
  575. package/templates/native-go-vue/frontend/src/shell/i18n/ja.json +1 -1
  576. package/templates/native-go-vue/frontend/test/apps.test.ts +14 -0
  577. package/templates/native-go-vue/frontend/test/portal-settings.test.ts +71 -0
  578. package/templates/native-go-vue/frontend/test/shell-i18n.test.ts +50 -0
@@ -1,27 +1,56 @@
1
- import { renderBackgroundControl, backgroundUnitName } from "./background.js";
1
+ import { renderBackgroundControl, renderBackgroundWorkerStart, renderBackgroundWorkerStop, renderUserManagerRecovery, backgroundUnitName } from "./background.js";
2
2
  import { renderPackagePostuninst } from "../build-spk/native-lifecycle.js";
3
3
  import { renderSharedFolderScript } from "./shared-folder.js";
4
+ import { renderDelegateInstallDraft } from '../build-spk/delegate-scope.js';
5
+ import { renderPortalLifecycle } from "../portal/package.js";
6
+ import { nativePortal } from "./portal.js";
4
7
  /** Package-account lifecycle hooks for CGI, daemon and background packages. */
5
8
  export function renderStandaloneScript(name, manifest) {
9
+ const portal = nativePortal(manifest);
10
+ const script = renderCapabilityScript(name, manifest);
11
+ return portal ? script.replace("#!/bin/sh\n", () => `#!/bin/sh\n${renderPortalLifecycle(name, portal)}`) : script;
12
+ }
13
+ function renderCapabilityScript(name, manifest) {
14
+ if (name === 'preupgrade' && (manifest.capabilities.rootOps || manifest.capabilities.delegate)) {
15
+ const installed = `\${SYNOPKG_PKGDEST:-/var/packages/${manifest.package}/target}`;
16
+ // Incoming preupgrade sees the OLD payload. Only complete absence is first
17
+ // introduction; a partial or symlinked executor must still fail closed.
18
+ const legacy = manifest.daemon
19
+ ? ` if [ -e "${installed}/daemon" ] || [ -L "${installed}/daemon" ]; then\n "${installed}/daemon/control" stop "${installed}" "\${SYNOPKG_PKGVAR:-/var/packages/${manifest.package}/var}" || exit $?\n fi\n`
20
+ : '';
21
+ return renderStandaloneScriptBody(name, manifest).replace('#!/bin/sh\n', `#!/bin/sh\nif [ ! -e "${installed}/executor" ] && [ ! -L "${installed}/executor" ]; then\n${legacy} exit 0\nfi\n`);
22
+ }
23
+ return renderStandaloneScriptBody(name, manifest);
24
+ }
25
+ function renderStandaloneScriptBody(name, manifest) {
26
+ if (manifest.capabilities?.delegate?.scope === 'install-selected' && ['preinst', 'postinst'].includes(name)) {
27
+ const normal = renderCapabilityScript(name, { ...manifest, capabilities: { ...manifest.capabilities, delegate: { ...manifest.capabilities.delegate, scope: 'home' } } });
28
+ return normal.replace('#!/bin/sh\n', () => '#!/bin/sh\n' + renderDelegateInstallDraft(manifest.package, name === 'preinst'));
29
+ }
30
+ if (manifest.daemon && manifest.capabilities?.jobs && (manifest.capabilities.delegate || manifest.capabilities.rootOps)
31
+ && ['start-stop-status', 'preupgrade', 'preuninst'].includes(name))
32
+ return renderDelegatedDaemonScript(name, manifest);
6
33
  if ((manifest.capabilities?.rootOps || manifest.capabilities?.delegate) && ["preuninst", "preupgrade", "start-stop-status"].includes(name)) {
7
34
  const helper = `"\${SYNOPKG_PKGDEST:-/var/packages/${manifest.package}/target}/executor/lifecycle"`;
8
- const stopWorker = manifest.capabilities.jobs ? `/usr/syno/bin/synosystemctl stop ${backgroundUnitName(manifest.package)} || echo "worker stop unconfirmed; continuing executor cleanup" >&2\n` : "";
35
+ // Attempt worker stop first, but let executor shutdown drain requests and
36
+ // close ingress even when it fails; a still-running worker fails the hook.
37
+ const workerStop = manifest.capabilities.jobs ? renderBackgroundWorkerStop(manifest.package) : "";
38
+ const stopWorker = manifest.capabilities.jobs ? "worker_result=0\nstop_background_worker || worker_result=1\n" : "";
39
+ const workerResult = manifest.capabilities.jobs ? 'exit "$worker_result"\n' : "exit 0\n";
9
40
  const request = (action) => `${helper} ${action} --package ${manifest.package} || exit $?\n`;
10
41
  if (name === "start-stop-status") {
11
- const normal = renderStandaloneScript(name, { ...manifest, capabilities: { ...manifest.capabilities, rootOps: undefined, delegate: undefined } });
12
- // Attempt worker stop first. DSM may have already stopped it or deny this
13
- // lifecycle context; executor shutdown drains requests and closes ingress.
42
+ const normal = renderCapabilityScript(name, { ...manifest, capabilities: { ...manifest.capabilities, rootOps: undefined, delegate: undefined } });
14
43
  const stopDaemon = manifest.daemon ? `"/var/packages/${manifest.package}/target/daemon/control" stop "\${SYNOPKG_PKGDEST:-/var/packages/${manifest.package}/target}" "\${SYNOPKG_PKGVAR:-/var/packages/${manifest.package}/var}" || exit $?\n` : "";
15
- return normal.replace("#!/bin/sh\n", `#!/bin/sh\nif [ "$1" = start ]; then\n${request("start")}fi\nif [ "$1" = stop ]; then\n${stopWorker}${stopDaemon}${request("stop")}exit 0\nfi\n`);
44
+ return normal.replace("#!/bin/sh\n", `#!/bin/sh\n${workerStop}if [ "$1" = start ]; then\n${request("start")}fi\nif [ "$1" = stop ]; then\n${stopWorker}${stopDaemon}${request("stop")}${workerResult}fi\n`);
16
45
  }
17
46
  const action = name === "preuninst" ? '"$action"' : "stop";
18
47
  const choose = name === "preuninst" ? 'action=purge\n[ "${SYNOPKG_PKG_STATUS:-}" != UPGRADE ] || action=stop\n' : "";
19
- return `#!/bin/sh\n${stopWorker}${choose}${request(action)}${manifest.daemon ? renderDaemonScript(name, manifest).replace("#!/bin/sh\n", "") : "exit 0\n"}`;
48
+ return `#!/bin/sh\n${workerStop}${stopWorker}${choose}${request(action)}${manifest.daemon ? renderDaemonScript(name, manifest).replace("#!/bin/sh\n", "") : workerResult}`;
20
49
  }
21
50
  if (name === "postuninst")
22
51
  return renderPackagePostuninst(manifest.package);
23
52
  if ((manifest.capabilities?.rootOps || manifest.capabilities?.delegate) && ["postinst", "postupgrade"].includes(name)) {
24
- const normal = renderStandaloneScript(name, { ...manifest, capabilities: { ...manifest.capabilities, rootOps: undefined, delegate: undefined } });
53
+ const normal = renderCapabilityScript(name, { ...manifest, capabilities: { ...manifest.capabilities, rootOps: undefined, delegate: undefined } });
25
54
  return normal.replace("#!/bin/sh\n", `#!/bin/sh\n"\${SYNOPKG_PKGDEST:-/var/packages/${manifest.package}/target}/executor/lifecycle" start --package ${manifest.package} || exit $?\n`);
26
55
  }
27
56
  if (manifest.sharedFolder && (name === "postinst" || name === "postupgrade"))
@@ -40,6 +69,55 @@ esac
40
69
  }
41
70
  return "#!/bin/sh\nexit 0\n";
42
71
  }
72
+ /** DSM owns the single user unit; the daemon controller owns only its producer. */
73
+ function renderDelegatedDaemonScript(name, manifest) {
74
+ const pkg = manifest.package;
75
+ const daemon = `"/var/packages/${pkg}/target/daemon/control"`;
76
+ const paths = `"\${SYNOPKG_PKGDEST:-/var/packages/${pkg}/target}" "\${SYNOPKG_PKGVAR:-/var/packages/${pkg}/var}"`;
77
+ const helper = `"\${SYNOPKG_PKGDEST:-/var/packages/${pkg}/target}/executor/lifecycle"`;
78
+ const executor = (action) => `${helper} ${action} --package ${pkg}`;
79
+ const worker = (action) => `/usr/syno/bin/synosystemctl ${action} ${backgroundUnitName(pkg)}`;
80
+ // Try every owner even on failure; never leave protected ingress open merely
81
+ // because a producer could not stop. A partial teardown remains a failure.
82
+ const stop = (daemonAction, executorAction) => `result=0\nstop_background_worker || result=1\n${daemon} ${daemonAction} ${paths} || result=1\n${executor(executorAction)} || result=1\nexit "$result"\n`;
83
+ const workerStop = renderBackgroundWorkerStop(pkg);
84
+ if (name !== 'start-stop-status') {
85
+ if (name === 'preupgrade')
86
+ return `#!/bin/sh\n${workerStop}${stop('stop', 'stop')}`;
87
+ return `#!/bin/sh\n${workerStop}if [ "\${SYNOPKG_PKG_STATUS:-}" = UPGRADE ]; then\n${stop('stop', 'stop')}fi\n${stop(manifest.daemon?.uninstall_args ? 'uninstall' : 'stop', 'purge')}`;
88
+ }
89
+ return `#!/bin/sh
90
+ ${workerStop}${renderUserManagerRecovery()}${renderBackgroundWorkerStart(pkg)}case "$1" in
91
+ prestart) recover_user_manager ;;
92
+ prestop) exit 0 ;;
93
+ start)
94
+ ${executor('start')} || exit $?
95
+ if ! ${daemon} start ${paths}; then
96
+ ${daemon} stop ${paths}
97
+ ${executor('stop')}
98
+ exit 1
99
+ fi
100
+ if ! start_background_worker; then
101
+ ${worker('stop')}
102
+ ${daemon} stop ${paths}
103
+ ${executor('stop')}
104
+ exit 1
105
+ fi
106
+ exit 0 ;;
107
+ status)
108
+ ${daemon} status ${paths} || exit $?
109
+ worker_state="$(${worker('get-active-status')})" || exit 3
110
+ [ "$worker_state" = active ] && exit 0
111
+ exit 3 ;;
112
+ stop)
113
+ ${stop('stop', 'stop')} ;;
114
+ restart)
115
+ "$0" stop || exit $?
116
+ exec "$0" start ;;
117
+ *) exit 1 ;;
118
+ esac
119
+ `;
120
+ }
43
121
  function renderDaemonScript(name, manifest) {
44
122
  const pkg = manifest.package;
45
123
  const control = `"/var/packages/${pkg}/target/daemon/control"`;
@@ -1,3 +1,4 @@
1
+ import { type PackageMetadata } from "../package-metadata.js";
1
2
  import { type DelegateCapability } from "../runtime/executor-policy.js";
2
3
  import type { ExecutorPins } from "../runtime/executor-tasks.js";
3
4
  import { type NativeArchFamily } from "../build-spk/dsm-arch.js";
@@ -7,6 +8,7 @@ import { type Daemon } from "./daemon.js";
7
8
  import { type Background } from "./background.js";
8
9
  import { type SharedFolder } from "./shared-folder.js";
9
10
  import { type ApiPermissions } from "./api-permissions.js";
11
+ import { type PortalDeclaration } from "../portal/alias.js";
10
12
  /**
11
13
  * `dsm-package.yaml`: a Standalone Package, i.e. an unsigned native SPK that
12
14
  * runs as its own DSM package account with a CGI backend and a DSM window UI.
@@ -31,6 +33,7 @@ export interface StandaloneI18n {
31
33
  info: Partial<Record<Locale, InfoText>>;
32
34
  }
33
35
  export interface StandaloneManifest {
36
+ metadata?: PackageMetadata;
34
37
  root: string;
35
38
  package: string;
36
39
  version: string;
@@ -62,7 +65,16 @@ export interface StandaloneManifest {
62
65
  daemon?: Daemon;
63
66
  /** `install.sharedFolder`: the Shared Folder the install wizard selects or `data-share` creates. */
64
67
  sharedFolder?: SharedFolder;
68
+ /** Who may use the app at all; absent keeps the route policy alone. */
69
+ entry?: EntryMode;
70
+ /** `portal`: registers the DSM Application Portal entry; an administrator activates it. */
71
+ portal?: PortalDeclaration;
65
72
  }
73
+ /**
74
+ * `admin-only`: DSM administrators only. `app-privilege`: administrators and
75
+ * accounts DSM's Control Panel > Application Privileges allows for the app.
76
+ */
77
+ export type EntryMode = "admin-only" | "app-privilege";
66
78
  export interface Capabilities {
67
79
  files?: "session";
68
80
  identity?: "standalone";
@@ -81,6 +93,7 @@ export type ManifestResult = {
81
93
  ok: false;
82
94
  errors: StandaloneError[];
83
95
  };
96
+ export declare const KNOWN_FIELDS: Set<string>;
84
97
  export declare function loadStandaloneManifest(root: string): ManifestResult;
85
98
  /**
86
99
  * The nearest directory at or above `start` that holds dsm-package.yaml,
@@ -1,3 +1,4 @@
1
+ import { validateMetadata } from "../package-metadata.js";
1
2
  import { validDelegate } from "../runtime/executor-policy.js";
2
3
  import { existsSync, readFileSync } from "node:fs";
3
4
  import { isAbsolute, join, relative, resolve } from "node:path";
@@ -7,17 +8,18 @@ import { checkGlossary, isLocale, loadCatalogs } from "./i18n.js";
7
8
  import { loadRootOps, ROOT_OPS_DIR } from "./root-ops.js";
8
9
  import { readDaemon } from "./daemon.js";
9
10
  import { readBackground } from "./background.js";
10
- import { DEFAULT_MIN_DSM, isDsmAtLeast } from "../build-spk/dsm-version.js";
11
+ import { DEFAULT_MIN_DSM, isDsmAtLeast, PORTAL_MIN_DSM, portalMinDsmMessage } from "../build-spk/dsm-version.js";
11
12
  import { readSharedFolder } from "./shared-folder.js";
12
- import { readApiPermissions } from "./api-permissions.js";
13
+ import { floorAtAdministrators, permitsOrdinaryUsers, readApiPermissions } from "./api-permissions.js";
14
+ import { readPortalDeclaration } from "../portal/alias.js";
13
15
  /**
14
16
  * `dsm-package.yaml`: a Standalone Package, i.e. an unsigned native SPK that
15
17
  * runs as its own DSM package account with a CGI backend and a DSM window UI.
16
18
  */
17
19
  export const STANDALONE_MANIFEST = "dsm-package.yaml";
18
- const KNOWN_FIELDS = new Set([
20
+ export const KNOWN_FIELDS = new Set([
19
21
  "schema", "package", "version", "build", "displayname", "description",
20
- "maintainer", "os_min_ver", "arches", "backend", "ui", "capabilities", "i18n", "daemon", "install",
22
+ "metadata", "maintainer", "os_min_ver", "arches", "backend", "ui", "capabilities", "i18n", "daemon", "install", "entry", "portal",
21
23
  ]);
22
24
  const I18N_FIELDS = new Set(["messages", "glossary", "info"]);
23
25
  const INFO_TEXT_FIELDS = new Set(["displayname", "description"]);
@@ -108,6 +110,14 @@ function readManifest(raw, c) {
108
110
  const displayname = c.text(raw.displayname, "displayname");
109
111
  const description = c.text(raw.description, "description");
110
112
  const maintainer = c.text(raw.maintainer, "maintainer");
113
+ let metadata;
114
+ if (raw.metadata !== undefined)
115
+ try {
116
+ metadata = validateMetadata(raw.metadata);
117
+ }
118
+ catch {
119
+ c.fail("STANDALONE_TEXT_INVALID", "metadata 欄位格式不正確;請檢查文字、email、URLs 與 screenshots。");
120
+ }
111
121
  const arches = readArches(raw.arches, c);
112
122
  const backend = arches && readBackend(raw.backend, arches, c);
113
123
  let permissions;
@@ -120,8 +130,8 @@ function readManifest(raw, c) {
120
130
  }
121
131
  }
122
132
  const daemon = arches && readDaemon(raw.daemon, c.root, arches, e => c.fail(e.code, e.message));
123
- if (capabilities.jobs && raw.daemon !== undefined) {
124
- c.fail("CAPABILITY_COMBINATION_INVALID", "daemon 與 capabilities.jobs 互斥。");
133
+ if (capabilities.jobs && raw.daemon !== undefined && capabilities.delegate?.lifetime !== 'until-revoked') {
134
+ c.fail("CAPABILITY_COMBINATION_INVALID", "daemon 僅可與明確 until-revoked delegate 的固定 worker 共存;一般 capabilities.jobs 仍互斥。");
125
135
  }
126
136
  if (backend?.kind === "binary") {
127
137
  for (const name of ["files", "jobs", "identity", "delegate"]) {
@@ -130,10 +140,25 @@ function readManifest(raw, c) {
130
140
  }
131
141
  }
132
142
  const sharedFolder = raw.install === undefined ? undefined : readSharedFolder(raw.install, e => c.fail(e.code, e.message));
143
+ const entry = raw.entry === undefined ? undefined : readEntry(raw.entry, backend, permissions?.policy, c);
144
+ if (entry === "admin-only" && permissions) {
145
+ const floored = floorAtAdministrators(permissions.policy);
146
+ if (floored.widened.length) {
147
+ c.fail("STANDALONE_ENTRY_WIDENED", `entry: admin-only 以管理員為所有 route 的下限;permissions.json 的 ${floored.widened.join("、")} 會開放給其他人。請改為 administrators,或改用 entry: app-privilege/省略 entry。`);
148
+ }
149
+ permissions.policy = floored.policy;
150
+ }
133
151
  const ui = readUi(raw.ui, c);
134
152
  if (capabilities.identity && ui && !ui.build)
135
153
  c.fail("CAPABILITY_COMBINATION_INVALID", "capabilities.identity 需要獨立登入頁;plain UI 不支援,請改用 vue UI(init --ui vue)。");
154
+ if (capabilities.delegate?.scope === "install-selected" && ui && !ui.build)
155
+ c.fail("CAPABILITY_COMBINATION_INVALID", "delegate scope install-selected 只能在 Vue 面板確認;plain UI 不支援,請改用 vue UI(init --ui vue)。");
136
156
  const i18n = raw.i18n === undefined ? undefined : readI18n(raw.i18n, c);
157
+ const portal = raw.portal === undefined || !packageId ? undefined : readPortalDeclaration(raw.portal, packageId)
158
+ ?? c.fail("STANDALONE_PORTAL_INVALID", "portal 必須是 {} 或 { alias: <小寫英數與 -,1–64 字,不可為 DSM 保留路徑> }。");
159
+ if (portal && osMinVer && !isDsmAtLeast(osMinVer, PORTAL_MIN_DSM)) {
160
+ c.fail("STANDALONE_PORTAL_MIN_DSM", portalMinDsmMessage(osMinVer, "portal"));
161
+ }
137
162
  if (!packageId || !version || !build || !osMinVer || !displayname || !description || !maintainer
138
163
  || !arches || !backend || !ui || (capabilities.rootOps === true && !rootOps)) {
139
164
  return undefined;
@@ -147,6 +172,7 @@ function readManifest(raw, c) {
147
172
  displayname,
148
173
  description,
149
174
  maintainer,
175
+ ...(metadata ? { metadata } : {}),
150
176
  os_min_ver: osMinVer,
151
177
  arches,
152
178
  backend,
@@ -157,8 +183,31 @@ function readManifest(raw, c) {
157
183
  ...(rootOps ? { rootOps } : {}),
158
184
  ...(daemon ? { daemon } : {}),
159
185
  ...(sharedFolder ? { sharedFolder } : {}),
186
+ ...(entry ? { entry } : {}),
187
+ ...(portal ? { portal } : {}),
160
188
  };
161
189
  }
190
+ /**
191
+ * The CGI enforces the entry mode only when the project's backend carries the
192
+ * SDK gate and main wires it; `-X main.entry` on an older main would be
193
+ * silently dropped and leave a registered app ungated.
194
+ */
195
+ function readEntry(value, backend, policy, c) {
196
+ if (value !== "admin-only" && value !== "app-privilege") {
197
+ return c.fail("STANDALONE_ENTRY_INVALID", "entry 必須是 admin-only 或 app-privilege;省略則維持既有 permissions.json 行為。");
198
+ }
199
+ if (!backend)
200
+ return undefined;
201
+ const main = backend.kind === "go" ? join(backend.dir, backend.main, "main.go") : "";
202
+ if (backend.kind !== "go" || !existsSync(join(backend.dir, "internal/dsm/entry.go"))
203
+ || !existsSync(main) || !readFileSync(main, "utf8").includes("dsm.NewEntryGate(")) {
204
+ return c.fail("STANDALONE_ENTRY_UNSUPPORTED", "entry 需要 Go backend 內的 SDK entry gate:請從 SDK 範本複製 internal/dsm/entry.go、apppriv.go 與 native bridge,並在 cmd/app/main.go 以 dsm.NewEntryGate 包住 authenticator。");
205
+ }
206
+ if (value === "app-privilege" && !permitsOrdinaryUsers(policy)) {
207
+ return c.fail("STANDALONE_ENTRY_INVALID", "entry: app-privilege 需要至少一個 permissions.json 標為 authenticated 的 route,一般使用者才有功能可用。");
208
+ }
209
+ return value;
210
+ }
162
211
  function readRootOps(enabled, c) {
163
212
  const dir = join(c.root, ROOT_OPS_DIR);
164
213
  if (!enabled) {
@@ -180,7 +229,7 @@ function readArches(value, c) {
180
229
  && new Set(value).size === value.length;
181
230
  return ok
182
231
  ? value
183
- : c.fail("STANDALONE_ARCHES_INVALID", "arches 必須是不重複的 x86_64/aarch64 清單,至少一個。");
232
+ : c.fail("STANDALONE_ARCHES_INVALID", "arches 必須是不重複的 x86_64/aarch64/armv7 清單,至少一個。");
184
233
  }
185
234
  function readBackend(value, arches, c) {
186
235
  if (!isRecord(value) || !["go", "binary"].includes(String(value.kind))) {
@@ -314,7 +363,7 @@ function readCapabilities(value, c) {
314
363
  if (validDelegate(setting))
315
364
  result.delegate = setting;
316
365
  else
317
- c.fail("CAPABILITY_INVALID", "delegate requires nonempty unique §2.4 files operations and scope home or shared:<name>.");
366
+ c.fail("CAPABILITY_INVALID", "delegate requires scope home or shared:<name> and legacy files operations, or scope home/shared:<name>/install-selected with exactly files.moveFromStaging, nonempty unique destinations and lifetime: until-revoked.");
318
367
  }
319
368
  else if (name === "files" && setting === "session")
320
369
  result.files = "session";
@@ -1,2 +1,2 @@
1
1
  /** Shared host flow. CGI plans are never accepted as completed authorization. */
2
- export declare const AUTHORIZATION_RUNTIME = "function createAuthorizationAccess(options) {\n var busy = null;\n function fail(code) { var error = new Error(code); error.code = code; return error; }\n function status() { return options.call(\"authorization.status\").then(function (d) {\n if (!d || d.command !== \"authorization\" || d.schema_version !== 1 || [\"ready\",\"authorization_required\",\"not_admin\",\"session_expired\",\"unavailable\"].indexOf(d.state) < 0) throw fail(\"authorization_bad_response\");\n return d;\n }); }\n function plan(method) { return options.call(method).then(function (p) {\n if (!p || p.command !== \"authorization\" || p.schema_version !== 1 || p.phase !== \"plan\" || !Array.isArray(p.operations) || p.operations.length === 0 || p.operations.length > options.tasks.length) throw fail(\"authorization_bad_response\");\n var seen = {};\n p.operations.forEach(function (op) { if (seen[op] || !options.tasks.some(function (t) { return t.op === op; })) throw fail(\"authorization_bad_response\"); seen[op] = true; });\n return p;\n }); }\n function exclusive(fn) { if (busy) return busy; busy = Promise.resolve().then(fn).then(function (s) { busy = null; return s; },function (e) { busy = null; throw e; });return busy; }\n function authorize(ops,run) { return ops.reduce(function (previous,op) { return previous.then(function () {\n return options.root.authorize(op).then(function (state) { if (state !== \"ready\") throw fail(\"root_cancelled\"); return run ? options.root.run(op) : null; });\n }); },Promise.resolve()); }\n return {canManage:function () {return !!options.user && options.user().admin === true;},status:status,request:function () { return exclusive(function () {\n return plan(\"authorization.request\").then(function (p) { return authorize(p.operations,p.run === true); }).then(status,function (e) { if (e.code === \"root_cancelled\") return status();throw e; });\n }); },revoke:function () { return exclusive(function () {\n return plan(\"authorization.revoke\").then(function (p) {\n if (p.withdraw === true && typeof options.withdraw !== \"function\") throw fail(\"authorization_bad_response\");\n var disabled = p.withdraw === true ? options.withdraw() : p.disable === true ? authorize([\"helper_disable\"],true) : Promise.resolve();\n return disabled.then(function () { return p.purge === true ? authorize([\"helper_purge\"],true) : null; }).then(function () { return p.operations.reduce(function (previous,op) {return previous.then(function () {return options.root.remove(op);});},Promise.resolve()); });\n }).then(status,function (e) { if (e.code === \"root_cancelled\") return status();throw e; });\n }); }};\n}";
2
+ export declare const AUTHORIZATION_RUNTIME = "function createAuthorizationAccess(options) {\n var busy = null;\n function fail(code) { var error = new Error(code); error.code = code; return error; }\n function status() { return options.call(\"authorization.status\").then(function (d) {\n if (!d || d.command !== \"authorization\" || d.schema_version !== 1 || [\"ready\",\"authorization_required\",\"not_admin\",\"session_expired\",\"unavailable\"].indexOf(d.state) < 0) throw fail(\"authorization_bad_response\");\n // upgrade_reapproval is a reason-only tombstone: it can only explain a pending request.\n if (d.reason === \"upgrade_reapproval\" && d.state !== \"authorization_required\") throw fail(\"authorization_bad_response\");\n return d;\n }); }\n function plan(method) { return options.call(method).then(function (p) {\n if (!p || p.command !== \"authorization\" || p.schema_version !== 1 || p.phase !== \"plan\" || !Array.isArray(p.operations) || p.operations.length === 0 || p.operations.length > options.tasks.length) throw fail(\"authorization_bad_response\");\n var seen = {};\n p.operations.forEach(function (op) { if (seen[op] || !options.tasks.some(function (t) { return t.op === op; })) throw fail(\"authorization_bad_response\"); seen[op] = true; });\n return p;\n }); }\n function exclusive(fn) { if (busy) return busy; busy = Promise.resolve().then(fn).then(function (s) { busy = null; return s; },function (e) { busy = null; throw e; });return busy; }\n function authorize(ops,run) { return ops.reduce(function (previous,op) { return previous.then(function () {\n return options.root.authorize(op).then(function (state) { if (state !== \"ready\") throw fail(\"root_cancelled\"); return run ? options.root.run(op) : null; });\n }); },Promise.resolve()); }\n return {canManage:function () {return !!options.user && options.user().admin === true;},status:status,request:function () { return exclusive(function () {\n return plan(\"authorization.request\").then(function (p) { return authorize(p.operations,p.run === true); }).then(status,function (e) { if (e.code === \"root_cancelled\") return status();throw e; });\n }); },revoke:function () { return exclusive(function () {\n return plan(\"authorization.revoke\").then(function (p) {\n if (p.withdraw === true && typeof options.withdraw !== \"function\") throw fail(\"authorization_bad_response\");\n var disabled = p.withdraw === true ? options.withdraw() : p.disable === true ? authorize([\"helper_disable\"],true) : Promise.resolve();\n return disabled.then(function () { return p.purge === true ? authorize([\"helper_purge\"],true) : null; }).then(function () { return p.operations.reduce(function (previous,op) {return previous.then(function () {return options.root.remove(op);});},Promise.resolve()); });\n }).then(status,function (e) { if (e.code === \"root_cancelled\") return status();throw e; });\n }); }};\n}";
@@ -4,6 +4,8 @@ export const AUTHORIZATION_RUNTIME = `function createAuthorizationAccess(options
4
4
  function fail(code) { var error = new Error(code); error.code = code; return error; }
5
5
  function status() { return options.call("authorization.status").then(function (d) {
6
6
  if (!d || d.command !== "authorization" || d.schema_version !== 1 || ["ready","authorization_required","not_admin","session_expired","unavailable"].indexOf(d.state) < 0) throw fail("authorization_bad_response");
7
+ // upgrade_reapproval is a reason-only tombstone: it can only explain a pending request.
8
+ if (d.reason === "upgrade_reapproval" && d.state !== "authorization_required") throw fail("authorization_bad_response");
7
9
  return d;
8
10
  }); }
9
11
  function plan(method) { return options.call(method).then(function (p) {
@@ -1,3 +1,4 @@
1
+ import { type PackageMetadata } from "../package-metadata.js";
1
2
  import type { CommandRunner } from './build.js';
2
3
  export declare const LAYOUT_ARCHES: {
3
4
  readonly x86_64: readonly [2, 62];
@@ -21,6 +22,7 @@ type Binary = {
21
22
  arches?: LayoutArch[];
22
23
  };
23
24
  export interface NativeLayout {
25
+ metadata?: PackageMetadata;
24
26
  root: string;
25
27
  package: string;
26
28
  version: string;
@@ -39,6 +41,7 @@ export interface NativeLayout {
39
41
  payload: string;
40
42
  metadata: string;
41
43
  binaries: Binary[];
44
+ anchors?: Record<string, string>;
42
45
  };
43
46
  native: {
44
47
  prepare?: Hook;
@@ -1,3 +1,4 @@
1
+ import { validateMetadata, metadataInfoFields } from "../package-metadata.js";
1
2
  /** Existing native products: the CLI owns archives/INFO, the project owns its layout. */
2
3
  import { createHash } from 'node:crypto';
3
4
  import { chmodSync, copyFileSync, existsSync, lstatSync, mkdirSync, mkdtempSync, readFileSync, readdirSync, realpathSync, renameSync, rmSync, writeFileSync } from 'node:fs';
@@ -36,6 +37,20 @@ function inside(root, path) {
36
37
  fail('path escapes project');
37
38
  return full;
38
39
  }
40
+ // Package Center writes INFO as root, so product root helpers can trust these
41
+ // payload digests across upgrades. The synopkgland_ prefix stays reserved.
42
+ function anchors(value) {
43
+ const a = object(value, Object.keys(value ?? {}));
44
+ if (Object.keys(a).length > 16)
45
+ fail('at most 16 INFO anchors');
46
+ for (const [key, path] of Object.entries(a)) {
47
+ if (!/^[a-z][a-z0-9_]{0,48}_sha256$/.test(key) || key.startsWith('synopkgland_'))
48
+ fail(`invalid INFO anchor key: ${key}`);
49
+ if (pathText(path).includes('{arch}'))
50
+ fail('INFO anchor path is relative to the architecture payload');
51
+ }
52
+ return a;
53
+ }
39
54
  function hook(value) {
40
55
  const h = object(value, ['command', 'cwd', 'env']);
41
56
  if (!Array.isArray(h.command) || !h.command.length || !h.command.every((x) => typeof x === 'string' && x.length > 0 && !x.includes('\0')))
@@ -56,13 +71,15 @@ export function isNativeLayout(root) {
56
71
  }
57
72
  export function loadNativeLayout(root) {
58
73
  try {
59
- const m = object(parse(readFileSync(join(root, 'dsm-package.yaml'), 'utf8')), ['schema', 'backend', 'package', 'version', 'build', 'displayname', 'description', 'maintainer', 'os_min_ver', 'privilege', 'arches', 'layout', 'native']);
74
+ const m = object(parse(readFileSync(join(root, 'dsm-package.yaml'), 'utf8')), ['schema', 'backend', 'package', 'version', 'build', 'displayname', 'description', 'maintainer', 'metadata', 'os_min_ver', 'privilege', 'arches', 'layout', 'native']);
60
75
  if (m.schema !== 2)
61
76
  fail('schema 2 required; migrate schema: 1 to schema: 2 and add backend: {kind: native}');
62
77
  const backend = object(m.backend, ['kind']);
63
78
  if (backend.kind !== 'native' || (m.privilege !== undefined && m.privilege !== 'none'))
64
79
  fail('backend.kind: native and package-only privilege required');
65
80
  m.privilege = 'none';
81
+ if (m.metadata !== undefined)
82
+ m.metadata = validateMetadata(m.metadata);
66
83
  if (existsSync(join(root, 'compose.yaml')))
67
84
  fail('compose.yaml and dsm-package.yaml are mutually exclusive');
68
85
  if (!/^[A-Za-z][A-Za-z0-9]{1,63}$/.test(text(m.package)) || !/^\d+(\.\d+){0,3}$/.test(text(m.version)) || !/^7\.\d+(\.\d+)?-\d+$/.test(text(m.os_min_ver)))
@@ -73,11 +90,13 @@ export function loadNativeLayout(root) {
73
90
  text(m[k]);
74
91
  if (!Array.isArray(m.arches) || !m.arches.length || new Set(m.arches).size !== m.arches.length || m.arches.some((a) => !Object.hasOwn(LAYOUT_ARCHES, a)))
75
92
  fail('invalid architecture list');
76
- const layout = object(m.layout, ['payload', 'metadata', 'binaries']);
93
+ const layout = object(m.layout, ['payload', 'metadata', 'binaries', 'anchors']);
77
94
  layout.payload = pathText(layout.payload);
78
95
  layout.metadata = pathText(layout.metadata);
79
96
  if (!Array.isArray(layout.binaries) || !layout.binaries.length)
80
97
  fail('binary assertions required');
98
+ if (layout.anchors !== undefined)
99
+ layout.anchors = anchors(layout.anchors);
81
100
  for (const b of layout.binaries) {
82
101
  object(b, ['path', 'linkage', 'arches']);
83
102
  pathText(b.path);
@@ -300,8 +319,11 @@ export async function buildNativeLayout(options) {
300
319
  await createReproducibleTar(target, join(stage, 'package.tgz'), { gzip: true });
301
320
  rmSync(target, { recursive: true });
302
321
  const fields = { package: m.package, version, arch, os_min_ver: m.os_min_ver, displayname: m.displayname, description: m.description, maintainer: m.maintainer, dsmuidir: 'ui', dsmappname: `SYNO.SDS.${m.package}.Instance`, startable: 'yes', thirdparty: 'yes', checksum: createHash('md5').update(readFileSync(join(stage, 'package.tgz'))).digest('hex') };
303
- writeFileSync(join(stage, 'INFO'), Object.entries(fields).map(([k, v]) => `${k}="${v}"\n`).join(''));
304
- writeFileSync(join(stage, 'native-layout.json'), JSON.stringify({ format: 1, arch, binaries: binaries.map(({ path, linkage }) => ({ path, linkage })), files }));
322
+ const info = [...Object.entries(fields), ...metadataInfoFields(m.metadata), ...anchorFields(m.layout.anchors ?? {}, files)];
323
+ if (new Set(info.map(([k]) => k)).size !== info.length)
324
+ fail('INFO anchor collides with an INFO field');
325
+ writeFileSync(join(stage, 'INFO'), info.map(([k, v]) => `${k}="${v.replace(/([\\"$`])/g, '\\$1')}"\n`).join(''));
326
+ writeFileSync(join(stage, 'native-layout.json'), JSON.stringify({ format: 1, arch, binaries: binaries.map(({ path, linkage }) => ({ path, linkage })), files, ...(m.layout.anchors ? { anchors: m.layout.anchors } : {}) }));
305
327
  const output = join(options.outDir ?? join(m.root, 'dist'), `${m.package}-${version}-${arch}.spk`);
306
328
  mkdirSync(dirname(output), { recursive: true });
307
329
  await createReproducibleTar(stage, output);
@@ -319,7 +341,7 @@ export function inspectNativeLayout(dir, payload, info) {
319
341
  try {
320
342
  if (!/^[A-Za-z][A-Za-z0-9]{1,63}$/.test(info.package ?? '') || !/^\d+(\.\d+){0,3}-\d+$/.test(info.version ?? '') || !/^7\.\d+(\.\d+)?-\d+$/.test(info.os_min_ver ?? ''))
321
343
  fail('invalid native INFO identity/version/DSM');
322
- const inv = object(JSON.parse(readFileSync(join(dir, 'native-layout.json'), 'utf8')), ['format', 'arch', 'binaries', 'files']);
344
+ const inv = object(JSON.parse(readFileSync(join(dir, 'native-layout.json'), 'utf8')), ['format', 'arch', 'binaries', 'files', 'anchors']);
323
345
  if (inv.format !== 1 || inv.arch !== info.arch || !Object.hasOwn(LAYOUT_ARCHES, inv.arch))
324
346
  fail('invalid native layout inventory');
325
347
  checkLayoutMetadata(dir, true, info.package);
@@ -337,9 +359,19 @@ export function inspectNativeLayout(dir, payload, info) {
337
359
  }
338
360
  if (createHash('md5').update(readFileSync(join(dir, 'package.tgz'))).digest('hex') !== info.checksum)
339
361
  fail('package checksum mismatch');
362
+ checkInfoAnchors(readFileSync(join(dir, 'INFO'), 'utf8'), inv.anchors === undefined ? {} : anchors(inv.anchors), actual);
340
363
  return [];
341
364
  }
342
365
  catch (e) {
343
366
  return [String(e)];
344
367
  }
345
368
  }
369
+ function anchorFields(declared, files) {
370
+ return Object.entries(declared).map(([key, path]) => [key, files.find(f => f.path === path)?.sha256 ?? fail(`INFO anchor file missing: ${path}`)]);
371
+ }
372
+ function checkInfoAnchors(info, declared, files) {
373
+ const lines = info.split('\n').filter(line => /^[a-z0-9_]+_sha256=/.test(line));
374
+ const expected = anchorFields(declared, files).map(([key, digest]) => `${key}="${digest}"`);
375
+ if (JSON.stringify(lines.sort()) !== JSON.stringify(expected.sort()))
376
+ fail('INFO anchors do not match the payload');
377
+ }
@@ -0,0 +1,21 @@
1
+ type PortalLocale = "en" | "zh-TW" | "zh-CN" | "ja" | "ko" | "de" | "fr" | "es" | "it";
2
+ /** SDK-owned portal panel text, shared by the plain panel and the Vue template. */
3
+ export declare const PORTAL_MESSAGES: Record<string, Record<PortalLocale, string>>;
4
+ /**
5
+ * `createPortalAccess(options)` → `ctx.portal`, plain ES5 for DSM's desktop.
6
+ *
7
+ * An administrator reads and sets the alias with DSM's own
8
+ * SYNO.Core.AppPortal API in their own session, after SYNO.API.Info shows the
9
+ * API; DSM itself refuses everyone else (105). An ordinary user reads the
10
+ * alias only through the package backend's `portal.status`. Anything
11
+ * unexpected reads as `unavailable`; an administrator then gets only the
12
+ * Control Panel shortcut.
13
+ *
14
+ * `status()` resolves `{state, alias?, url?, canManage, controlPanel,
15
+ * defaultAlias}`; `apply(alias)` and `disable()` resolve the new status or
16
+ * reject `{code}`; `openControlPanel()` opens Login Portal > Applications.
17
+ * `messages` is the panel text in the window's locale; `mountPanel(element)`
18
+ * renders a plain panel for UIs without their own.
19
+ */
20
+ export declare const PORTAL_RUNTIME: string;
21
+ export {};