@synopackageland/cli 0.6.0 → 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 (585) hide show
  1. package/CHANGELOG.md +613 -0
  2. package/README.md +81 -78
  3. package/dist/add.js +39 -16
  4. package/dist/authorization-sdk.d.ts +7 -0
  5. package/dist/authorization-sdk.js +7 -0
  6. package/dist/build-spk/delegate-scope.d.ts +6 -0
  7. package/dist/build-spk/delegate-scope.js +34 -0
  8. package/dist/build-spk/dsm-arch.d.ts +7 -1
  9. package/dist/build-spk/dsm-arch.js +24 -28
  10. package/dist/build-spk/dsm-platforms.json +39 -0
  11. package/dist/build-spk/dsm-version.d.ts +11 -0
  12. package/dist/build-spk/dsm-version.js +17 -0
  13. package/dist/build-spk/gateway.d.ts +1 -1
  14. package/dist/build-spk/gateway.js +2 -2
  15. package/dist/build-spk/generator.js +30 -5
  16. package/dist/build-spk/notices.js +4 -1
  17. package/dist/build-spk/resource-worker.js +3 -0
  18. package/dist/build-spk/wizard-messages.d.ts +66 -0
  19. package/dist/build-spk/wizard-messages.js +75 -9
  20. package/dist/cli.js +30 -18
  21. package/dist/contract/schema-validate.js +8 -0
  22. package/dist/contract/validate.js +30 -0
  23. package/dist/deploy/deploy.d.ts +1 -1
  24. package/dist/deploy/deploy.js +8 -0
  25. package/dist/deploy/plan.js +1 -1
  26. package/dist/deploy/spk-version.js +1 -1
  27. package/dist/error-registry.js +46 -13
  28. package/dist/host/dsm-client.d.ts +8 -4
  29. package/dist/host/dsm-client.js +10 -4
  30. package/dist/host/types.d.ts +2 -1
  31. package/dist/info.js +18 -2
  32. package/dist/init.d.ts +3 -0
  33. package/dist/init.js +49 -18
  34. package/dist/package-metadata.d.ts +21 -0
  35. package/dist/package-metadata.js +101 -0
  36. package/dist/portal/alias.d.ts +25 -0
  37. package/dist/portal/alias.js +48 -0
  38. package/dist/portal/package.d.ts +33 -0
  39. package/dist/portal/package.js +116 -0
  40. package/dist/runtime/delegate-contract.d.ts +79 -0
  41. package/dist/runtime/delegate-contract.js +27 -0
  42. package/dist/runtime/executor-build.d.ts +1 -1
  43. package/dist/runtime/executor-build.js +5 -4
  44. package/dist/runtime/executor-inspect.d.ts +3 -0
  45. package/dist/runtime/executor-inspect.js +11 -5
  46. package/dist/runtime/executor-policy.d.ts +12 -0
  47. package/dist/runtime/executor-policy.js +43 -3
  48. package/dist/runtime/executor-scope-task.d.ts +4 -0
  49. package/dist/runtime/executor-scope-task.js +21 -0
  50. package/dist/runtime/executor-tasks.d.ts +2 -1
  51. package/dist/runtime/executor-tasks.js +4 -4
  52. package/dist/skill.js +1 -0
  53. package/dist/standalone/api-permissions.d.ts +10 -0
  54. package/dist/standalone/api-permissions.js +19 -2
  55. package/dist/standalone/background.d.ts +16 -0
  56. package/dist/standalone/background.js +69 -2
  57. package/dist/standalone/binary.d.ts +1 -1
  58. package/dist/standalone/binary.js +22 -8
  59. package/dist/standalone/build.d.ts +1 -1
  60. package/dist/standalone/build.js +38 -8
  61. package/dist/standalone/daemon.d.ts +1 -1
  62. package/dist/standalone/daemon.js +2 -1
  63. package/dist/standalone/dsm-window.d.ts +3 -1
  64. package/dist/standalone/dsm-window.js +72 -15
  65. package/dist/standalone/inspect-spk.d.ts +9 -1
  66. package/dist/standalone/inspect-spk.js +119 -26
  67. package/dist/standalone/lifecycle.js +86 -8
  68. package/dist/standalone/manifest.d.ts +13 -0
  69. package/dist/standalone/manifest.js +57 -8
  70. package/dist/standalone/native-authorization-window.d.ts +2 -0
  71. package/dist/standalone/native-authorization-window.js +30 -0
  72. package/dist/standalone/native-layout.d.ts +82 -0
  73. package/dist/standalone/native-layout.js +377 -0
  74. package/dist/standalone/native-root-window.d.ts +26 -0
  75. package/dist/standalone/native-root-window.js +282 -0
  76. package/dist/standalone/portal-window.d.ts +21 -0
  77. package/dist/standalone/portal-window.js +155 -0
  78. package/dist/standalone/portal.d.ts +4 -0
  79. package/dist/standalone/portal.js +4 -0
  80. package/dist/standalone/resource.d.ts +5 -3
  81. package/dist/standalone/resource.js +6 -3
  82. package/dist/standalone/root-window.js +62 -15
  83. package/dist/standalone/spk.d.ts +1 -1
  84. package/dist/standalone/spk.js +25 -7
  85. package/dist/templates/_daemon/main_test.go +61 -8
  86. package/dist/templates/_executor/README.md +127 -16
  87. package/dist/templates/_executor/anchor.go +146 -0
  88. package/dist/templates/_executor/anchor_test.go +126 -0
  89. package/dist/templates/_executor/apppriv_review_test.go +1 -1
  90. package/dist/templates/_executor/cancel_contract_test.go +236 -0
  91. package/dist/templates/_executor/copy.go +64 -0
  92. package/dist/templates/_executor/copy_test.go +88 -0
  93. package/dist/templates/_executor/dsm.go +23 -10
  94. package/dist/templates/_executor/dsm_test.go +95 -1
  95. package/dist/templates/_executor/executor.go +260 -49
  96. package/dist/templates/_executor/executor_test.go +36 -6
  97. package/dist/templates/_executor/identity.go +27 -1
  98. package/dist/templates/_executor/identity_kernel_linux_test.go +60 -0
  99. package/dist/templates/_executor/identity_linux.go +82 -0
  100. package/dist/templates/_executor/identity_security_test.go +43 -0
  101. package/dist/templates/_executor/internal/lifecycle/lifecycle.go +5 -0
  102. package/dist/templates/_executor/internal/lifecycle/lifecycle_test.go +61 -3
  103. package/dist/templates/_executor/internal/taskscript/boot.sh +9 -9
  104. package/dist/templates/_executor/internal/taskscript/cmd/main.go +2 -2
  105. package/dist/templates/_executor/internal/taskscript/control.sh +15 -10
  106. package/dist/templates/_executor/internal/taskscript/tasks.go +5 -3
  107. package/dist/templates/_executor/lifecycle_test.go +11 -0
  108. package/dist/templates/_executor/link_linux.go +28 -0
  109. package/dist/templates/_executor/long_running_test.go +530 -0
  110. package/dist/templates/_executor/main.go +72 -20
  111. package/dist/templates/_executor/move_linux.go +490 -0
  112. package/dist/templates/_executor/move_linux_test.go +460 -0
  113. package/dist/templates/_executor/move_validation.go +46 -0
  114. package/dist/templates/_executor/move_validation_test.go +187 -0
  115. package/dist/templates/_executor/operations.go +926 -0
  116. package/dist/templates/_executor/platform_linux.go +24 -173
  117. package/dist/templates/_executor/platform_linux_test.go +9 -65
  118. package/dist/templates/_executor/platform_other.go +30 -4
  119. package/dist/templates/_executor/process.go +3 -0
  120. package/dist/templates/_executor/records.go +242 -0
  121. package/dist/templates/_executor/scope_cancel_contract_test.go +91 -0
  122. package/dist/templates/_executor/scope_client.go +37 -0
  123. package/dist/templates/_executor/scope_contract.go +12 -0
  124. package/dist/templates/_executor/selected_scope.go +146 -0
  125. package/dist/templates/_executor/selected_scope_test.go +186 -0
  126. package/dist/templates/_executor/status_disabled_test.go +33 -0
  127. package/dist/templates/_executor/store.go +4 -3
  128. package/dist/templates/_executor/supervise.go +109 -0
  129. package/dist/templates/_executor/supervise_test.go +130 -0
  130. package/dist/templates/_executor/task_race_review_test.go +1 -1
  131. package/dist/templates/_executor/tasks.go +7 -8
  132. package/dist/templates/_executor/tasks_review_test.go +1 -1
  133. package/dist/templates/_executor/tasks_test.go +4 -4
  134. package/dist/templates/_executor/watch.go +8 -0
  135. package/dist/templates/_executor/watch_test.go +23 -2
  136. package/dist/templates/_executor/write_review_linux_test.go +4 -12
  137. package/dist/templates/_shared/AGENTS.md +129 -0
  138. package/dist/templates/binary/dsm-package.yaml +24 -0
  139. package/dist/templates/binary/i18n/messages/de.json +4 -0
  140. package/dist/templates/binary/i18n/messages/en.json +4 -0
  141. package/dist/templates/binary/i18n/messages/es.json +4 -0
  142. package/dist/templates/binary/i18n/messages/fr.json +4 -0
  143. package/dist/templates/binary/i18n/messages/it.json +4 -0
  144. package/dist/templates/binary/i18n/messages/ja.json +4 -0
  145. package/dist/templates/binary/i18n/messages/ko.json +4 -0
  146. package/dist/templates/binary/i18n/messages/zh-CN.json +4 -0
  147. package/dist/templates/binary/i18n/messages/zh-TW.json +4 -0
  148. package/dist/templates/binary/ui/app.js +19 -4
  149. package/dist/templates/native-authorization/AuthorizationGate.vue +91 -0
  150. package/dist/templates/native-authorization/i18n/de.json +37 -0
  151. package/dist/templates/native-authorization/i18n/en.json +37 -0
  152. package/dist/templates/native-authorization/i18n/es.json +37 -0
  153. package/dist/templates/native-authorization/i18n/fr.json +37 -0
  154. package/dist/templates/native-authorization/i18n/index.ts +15 -0
  155. package/dist/templates/native-authorization/i18n/it.json +37 -0
  156. package/dist/templates/native-authorization/i18n/ja.json +37 -0
  157. package/dist/templates/native-authorization/i18n/ko.json +37 -0
  158. package/dist/templates/native-authorization/i18n/zh-CN.json +37 -0
  159. package/dist/templates/native-authorization/i18n/zh-TW.json +37 -0
  160. package/dist/templates/native-go/README.md +43 -10
  161. package/dist/templates/native-go/backend/cmd/app/main.go +27 -7
  162. package/dist/templates/native-go/backend/internal/api/entry_test.go +47 -0
  163. package/dist/templates/native-go/backend/internal/api/permissions_test.go +3 -3
  164. package/dist/templates/native-go/backend/internal/api/registry.go +11 -0
  165. package/dist/templates/native-go/backend/internal/api/sdk-permissions.json +14 -1
  166. package/dist/templates/native-go/backend/internal/api/server.go +18 -5
  167. package/dist/templates/native-go/backend/internal/app/delegate_config.go +57 -0
  168. package/dist/templates/native-go/backend/internal/app/delegate_config_test.go +28 -0
  169. package/dist/templates/native-go/backend/internal/app/portal.go +30 -0
  170. package/dist/templates/native-go/backend/internal/app/portal_test.go +55 -0
  171. package/dist/templates/native-go/backend/internal/app/routes.go +8 -2
  172. package/dist/templates/native-go/backend/internal/background/runner.go +1 -1
  173. package/dist/templates/native-go/backend/internal/background/store.go +16 -14
  174. package/dist/templates/native-go/backend/internal/dsm/admin.go +8 -2
  175. package/dist/templates/native-go/backend/internal/dsm/admin_linux.go +43 -13
  176. package/dist/templates/native-go/backend/internal/dsm/admin_linux_test.go +56 -0
  177. package/dist/templates/native-go/backend/internal/dsm/admin_other.go +4 -1
  178. package/dist/templates/native-go/backend/internal/dsm/apppriv.go +30 -0
  179. package/dist/templates/native-go/backend/internal/dsm/bridge_file.go +75 -0
  180. package/dist/templates/native-go/backend/internal/dsm/bridge_file_test.go +57 -0
  181. package/dist/templates/native-go/backend/internal/dsm/entry.go +177 -0
  182. package/dist/templates/native-go/backend/internal/dsm/entry_test.go +315 -0
  183. package/dist/templates/native-go/backend/internal/dsm/native/admin-armv7.elf +0 -0
  184. package/dist/templates/native-go/backend/internal/dsm/native/apppriv-aarch64.elf +0 -0
  185. package/dist/templates/native-go/backend/internal/dsm/native/apppriv-armv7.elf +0 -0
  186. package/dist/templates/native-go/backend/internal/dsm/native/apppriv-x86_64.elf +0 -0
  187. package/dist/templates/native-go/backend/internal/dsm/native/apppriv_check.c +33 -0
  188. package/dist/templates/native-go/backend/internal/dsm/native/build.sh +13 -4
  189. package/dist/templates/native-go/backend/internal/dsm/native/provenance.json +9 -3
  190. package/dist/templates/native-go/backend/internal/filestation/check_permission.go +71 -0
  191. package/dist/templates/native-go/backend/internal/filestation/check_permission_test.go +77 -0
  192. package/dist/templates/native-go/backend/internal/filestation/sdk_policy_test.go +39 -0
  193. package/dist/templates/native-go/backend/internal/filestation/service.go +1 -1
  194. package/dist/templates/native-go/backend/internal/portal/portal.go +138 -0
  195. package/dist/templates/native-go/backend/internal/portal/portal_test.go +89 -0
  196. package/dist/templates/native-go/dsm-package.yaml +20 -1
  197. package/dist/templates/native-go/i18n/glossary.json +2 -1
  198. package/dist/templates/native-go/i18n/messages/de.json +1 -0
  199. package/dist/templates/native-go/i18n/messages/en.json +1 -0
  200. package/dist/templates/native-go/i18n/messages/es.json +1 -0
  201. package/dist/templates/native-go/i18n/messages/fr.json +1 -0
  202. package/dist/templates/native-go/i18n/messages/it.json +1 -0
  203. package/dist/templates/native-go/i18n/messages/ja.json +1 -0
  204. package/dist/templates/native-go/i18n/messages/ko.json +1 -0
  205. package/dist/templates/native-go/i18n/messages/zh-CN.json +1 -0
  206. package/dist/templates/native-go/i18n/messages/zh-TW.json +1 -0
  207. package/dist/templates/native-go/ui/app.css +10 -0
  208. package/dist/templates/native-go/ui/app.js +11 -1
  209. package/dist/templates/native-go-delegate/DELEGATE.md +126 -5
  210. package/dist/templates/native-go-delegate/backend/cmd/background-worker/main.go +7 -0
  211. package/dist/templates/native-go-delegate/backend/internal/app/delegate.go +11 -1
  212. package/dist/templates/native-go-delegate/backend/internal/app/delegate_routes_test.go +14 -0
  213. package/dist/templates/native-go-delegate/backend/internal/background/delegated_moves.go +270 -0
  214. package/dist/templates/native-go-delegate/backend/internal/delegate/admission_recovery_test.go +410 -0
  215. package/dist/templates/native-go-delegate/backend/internal/delegate/cancel_contract_test.go +95 -0
  216. package/dist/templates/native-go-delegate/backend/internal/delegate/client.go +8 -0
  217. package/dist/templates/native-go-delegate/backend/internal/delegate/destination.go +110 -0
  218. package/dist/templates/native-go-delegate/backend/internal/delegate/destination_state.go +117 -0
  219. package/dist/templates/native-go-delegate/backend/internal/delegate/destination_state_test.go +60 -0
  220. package/dist/templates/native-go-delegate/backend/internal/delegate/destination_test.go +116 -0
  221. package/dist/templates/native-go-delegate/backend/internal/delegate/example.go +81 -0
  222. package/dist/templates/native-go-delegate/backend/internal/delegate/example_test.go +91 -0
  223. package/dist/templates/native-go-delegate/backend/internal/delegate/executor_contract_test.go +511 -0
  224. package/dist/templates/native-go-delegate/backend/internal/delegate/files.go +1 -1
  225. package/dist/templates/native-go-delegate/backend/internal/delegate/long_running_routes.go +92 -0
  226. package/dist/templates/native-go-delegate/backend/internal/delegate/long_running_routes_test.go +99 -0
  227. package/dist/templates/native-go-delegate/backend/internal/delegate/move.go +348 -0
  228. package/dist/templates/native-go-delegate/backend/internal/delegate/move_test.go +351 -0
  229. package/dist/templates/native-go-delegate/backend/internal/delegate/operations_list.go +65 -0
  230. package/dist/templates/native-go-delegate/backend/internal/delegate/operations_list_test.go +114 -0
  231. package/dist/templates/native-go-delegate/backend/internal/delegate/retry_test.go +1 -1
  232. package/dist/templates/native-go-delegate/backend/internal/delegate/service.go +58 -17
  233. package/dist/templates/native-go-delegate/backend/internal/delegate/staging.go +121 -0
  234. package/dist/templates/native-go-delegate/backend/internal/delegate/status_disabled_test.go +51 -0
  235. package/dist/templates/native-go-delegate/backend/internal/delegate/testdata/executor_contract.txt +112 -0
  236. package/dist/templates/native-go-delegate/backend/internal/delegate/transient_contract_test.go +137 -0
  237. package/dist/templates/native-go-delegate/backend/internal/filestation/files.go +1 -31
  238. package/dist/templates/native-go-delegate/frontend/src/CapabilityPanels.vue +30 -3
  239. package/dist/templates/native-go-delegate/frontend/src/delegate/BackgroundSavePanel.vue +113 -0
  240. package/dist/templates/native-go-delegate/frontend/src/delegate/copy.ts +5 -6
  241. package/dist/templates/native-go-delegate/frontend/test/background-save.test.ts +121 -0
  242. package/dist/templates/native-go-delegate/frontend/test/scope-settings.test.ts +49 -0
  243. package/dist/templates/native-go-delegate/test.sh +1 -0
  244. package/dist/templates/native-go-jobs/frontend/src/background/i18n/de.json +2 -2
  245. package/dist/templates/native-go-jobs/frontend/src/background/i18n/en.json +2 -2
  246. package/dist/templates/native-go-jobs/frontend/src/background/i18n/es.json +12 -12
  247. package/dist/templates/native-go-jobs/frontend/src/background/i18n/fr.json +4 -4
  248. package/dist/templates/native-go-jobs/frontend/src/background/i18n/it.json +17 -17
  249. package/dist/templates/native-go-jobs/frontend/src/background/i18n/ja.json +7 -7
  250. package/dist/templates/native-go-jobs/frontend/src/background/i18n/ko.json +15 -15
  251. package/dist/templates/native-go-jobs/frontend/src/background/i18n/zh-CN.json +3 -3
  252. package/dist/templates/native-go-jobs/frontend/src/background/i18n/zh-TW.json +2 -2
  253. package/dist/templates/native-go-jobs/frontend/test/background.test.ts +25 -28
  254. package/dist/templates/native-go-vue/README.md +4 -0
  255. package/dist/templates/native-go-vue/frontend/package-lock.json +4 -4
  256. package/dist/templates/native-go-vue/frontend/package.json +1 -1
  257. package/dist/templates/native-go-vue/frontend/src/App.vue +18 -13
  258. package/dist/templates/native-go-vue/frontend/src/context.ts +31 -0
  259. package/dist/templates/native-go-vue/frontend/src/delegate/ExecutorSettings.vue +52 -8
  260. package/dist/templates/native-go-vue/frontend/src/delegate/catalogs.ts +11 -0
  261. package/dist/templates/native-go-vue/frontend/src/delegate/copy.test.ts +12 -0
  262. package/dist/templates/native-go-vue/frontend/src/delegate/copy.ts +4 -4
  263. package/dist/templates/native-go-vue/frontend/src/delegate/executor-setup.ts +4 -2
  264. package/dist/templates/native-go-vue/frontend/src/delegate/facade.test.ts +100 -0
  265. package/dist/templates/native-go-vue/frontend/src/delegate/facade.ts +85 -0
  266. package/dist/templates/native-go-vue/frontend/src/delegate/i18n/de.json +46 -0
  267. package/dist/templates/native-go-vue/frontend/src/delegate/i18n/en.json +12 -2
  268. package/dist/templates/native-go-vue/frontend/src/delegate/i18n/es.json +46 -0
  269. package/dist/templates/native-go-vue/frontend/src/delegate/i18n/fr.json +46 -0
  270. package/dist/templates/native-go-vue/frontend/src/delegate/i18n/it.json +46 -0
  271. package/dist/templates/native-go-vue/frontend/src/delegate/i18n/ja.json +46 -0
  272. package/dist/templates/native-go-vue/frontend/src/delegate/i18n/ko.json +46 -0
  273. package/dist/templates/native-go-vue/frontend/src/delegate/i18n/save/de.json +42 -0
  274. package/dist/templates/native-go-vue/frontend/src/delegate/i18n/save/en.json +42 -0
  275. package/dist/templates/native-go-vue/frontend/src/delegate/i18n/save/es.json +42 -0
  276. package/dist/templates/native-go-vue/frontend/src/delegate/i18n/save/fr.json +42 -0
  277. package/dist/templates/native-go-vue/frontend/src/delegate/i18n/save/it.json +42 -0
  278. package/dist/templates/native-go-vue/frontend/src/delegate/i18n/save/ja.json +42 -0
  279. package/dist/templates/native-go-vue/frontend/src/delegate/i18n/save/ko.json +42 -0
  280. package/dist/templates/native-go-vue/frontend/src/delegate/i18n/save/zh-CN.json +42 -0
  281. package/dist/templates/native-go-vue/frontend/src/delegate/i18n/save/zh-TW.json +42 -0
  282. package/dist/templates/native-go-vue/frontend/src/delegate/i18n/zh-CN.json +46 -0
  283. package/dist/templates/native-go-vue/frontend/src/delegate/i18n/zh-TW.json +15 -5
  284. package/dist/templates/native-go-vue/frontend/src/delegate/long-running.ts +57 -0
  285. package/dist/templates/native-go-vue/frontend/src/delegate/save-copy.test.ts +12 -0
  286. package/dist/templates/native-go-vue/frontend/src/delegate/save-copy.ts +24 -0
  287. package/dist/templates/native-go-vue/frontend/src/delegate/scoped-picker.test.ts +12 -0
  288. package/dist/templates/native-go-vue/frontend/src/delegate/scoped-picker.ts +21 -0
  289. package/dist/templates/native-go-vue/frontend/src/files/files.test.ts +7 -0
  290. package/dist/templates/native-go-vue/frontend/src/i18n/de.json +20 -0
  291. package/dist/templates/native-go-vue/frontend/src/i18n/en.json +20 -0
  292. package/dist/templates/native-go-vue/frontend/src/i18n/es.json +20 -0
  293. package/dist/templates/native-go-vue/frontend/src/i18n/fr.json +20 -0
  294. package/dist/templates/native-go-vue/frontend/src/i18n/it.json +20 -0
  295. package/dist/templates/native-go-vue/frontend/src/i18n/ja.json +20 -0
  296. package/dist/templates/native-go-vue/frontend/src/i18n/ko.json +20 -0
  297. package/dist/templates/native-go-vue/frontend/src/i18n/zh-CN.json +20 -0
  298. package/dist/templates/native-go-vue/frontend/src/i18n/zh-TW.json +20 -0
  299. package/dist/templates/native-go-vue/frontend/src/i18n.ts +13 -0
  300. package/dist/templates/native-go-vue/frontend/src/identity/LoginPage.vue +16 -14
  301. package/dist/templates/native-go-vue/frontend/src/identity/i18n/de.json +27 -0
  302. package/dist/templates/native-go-vue/frontend/src/identity/i18n/en.json +27 -0
  303. package/dist/templates/native-go-vue/frontend/src/identity/i18n/es.json +27 -0
  304. package/dist/templates/native-go-vue/frontend/src/identity/i18n/fr.json +27 -0
  305. package/dist/templates/native-go-vue/frontend/src/identity/i18n/it.json +27 -0
  306. package/dist/templates/native-go-vue/frontend/src/identity/i18n/ja.json +27 -0
  307. package/dist/templates/native-go-vue/frontend/src/identity/i18n/ko.json +27 -0
  308. package/dist/templates/native-go-vue/frontend/src/identity/i18n/zh-CN.json +27 -0
  309. package/dist/templates/native-go-vue/frontend/src/identity/i18n/zh-TW.json +27 -0
  310. package/dist/templates/native-go-vue/frontend/src/identity/i18n.ts +14 -0
  311. package/dist/templates/native-go-vue/frontend/src/identity/session.test.ts +4 -0
  312. package/dist/templates/native-go-vue/frontend/src/identity/session.ts +9 -8
  313. package/dist/templates/native-go-vue/frontend/src/nas/NasPicker.vue +31 -12
  314. package/dist/templates/native-go-vue/frontend/src/nas/destination.test.ts +48 -0
  315. package/dist/templates/native-go-vue/frontend/src/nas/i18n/files/de.json +1 -1
  316. package/dist/templates/native-go-vue/frontend/src/nas/i18n/files/en.json +1 -1
  317. package/dist/templates/native-go-vue/frontend/src/nas/i18n/files/es.json +10 -10
  318. package/dist/templates/native-go-vue/frontend/src/nas/i18n/files/fr.json +1 -1
  319. package/dist/templates/native-go-vue/frontend/src/nas/i18n/files/it.json +12 -12
  320. package/dist/templates/native-go-vue/frontend/src/nas/i18n/files/ja.json +15 -15
  321. package/dist/templates/native-go-vue/frontend/src/nas/i18n/files/ko.json +12 -12
  322. package/dist/templates/native-go-vue/frontend/src/nas/i18n/files/zh-CN.json +1 -1
  323. package/dist/templates/native-go-vue/frontend/src/nas/i18n/files/zh-TW.json +1 -1
  324. package/dist/templates/native-go-vue/frontend/src/portal/PortalSettings.vue +39 -0
  325. package/dist/templates/native-go-vue/frontend/src/shell/i18n/fr.json +1 -1
  326. package/dist/templates/native-go-vue/frontend/src/shell/i18n/ja.json +1 -1
  327. package/dist/templates/native-go-vue/frontend/test/apps.test.ts +14 -0
  328. package/dist/templates/native-go-vue/frontend/test/portal-settings.test.ts +71 -0
  329. package/dist/templates/native-go-vue/frontend/test/shell-i18n.test.ts +50 -0
  330. package/dist/validate.js +2 -1
  331. package/package.json +12 -2
  332. package/schema/package-metadata.schema.json +101 -0
  333. package/schema/synology-app.schema.json +112 -0
  334. package/skills/SKILL.md +109 -3
  335. package/skills/reference/create.md +101 -2
  336. package/skills/reference/deploy.md +2 -2
  337. package/skills/reference/files.md +113 -2
  338. package/skills/reference/identity.md +70 -1
  339. package/skills/reference/portal.md +17 -0
  340. package/skills/reference/ui-vue.md +12 -0
  341. package/templates/_daemon/main_test.go +61 -8
  342. package/templates/_executor/README.md +127 -16
  343. package/templates/_executor/anchor.go +146 -0
  344. package/templates/_executor/anchor_test.go +126 -0
  345. package/templates/_executor/apppriv_review_test.go +1 -1
  346. package/templates/_executor/cancel_contract_test.go +236 -0
  347. package/templates/_executor/copy.go +64 -0
  348. package/templates/_executor/copy_test.go +88 -0
  349. package/templates/_executor/dsm.go +23 -10
  350. package/templates/_executor/dsm_test.go +95 -1
  351. package/templates/_executor/executor.go +260 -49
  352. package/templates/_executor/executor_test.go +36 -6
  353. package/templates/_executor/identity.go +27 -1
  354. package/templates/_executor/identity_kernel_linux_test.go +60 -0
  355. package/templates/_executor/identity_linux.go +82 -0
  356. package/templates/_executor/identity_security_test.go +43 -0
  357. package/templates/_executor/internal/lifecycle/lifecycle.go +5 -0
  358. package/templates/_executor/internal/lifecycle/lifecycle_test.go +61 -3
  359. package/templates/_executor/internal/taskscript/boot.sh +9 -9
  360. package/templates/_executor/internal/taskscript/cmd/main.go +2 -2
  361. package/templates/_executor/internal/taskscript/control.sh +15 -10
  362. package/templates/_executor/internal/taskscript/tasks.go +5 -3
  363. package/templates/_executor/lifecycle_test.go +11 -0
  364. package/templates/_executor/link_linux.go +28 -0
  365. package/templates/_executor/long_running_test.go +530 -0
  366. package/templates/_executor/main.go +72 -20
  367. package/templates/_executor/move_linux.go +490 -0
  368. package/templates/_executor/move_linux_test.go +460 -0
  369. package/templates/_executor/move_validation.go +46 -0
  370. package/templates/_executor/move_validation_test.go +187 -0
  371. package/templates/_executor/operations.go +926 -0
  372. package/templates/_executor/platform_linux.go +24 -173
  373. package/templates/_executor/platform_linux_test.go +9 -65
  374. package/templates/_executor/platform_other.go +30 -4
  375. package/templates/_executor/process.go +3 -0
  376. package/templates/_executor/records.go +242 -0
  377. package/templates/_executor/scope_cancel_contract_test.go +91 -0
  378. package/templates/_executor/scope_client.go +37 -0
  379. package/templates/_executor/scope_contract.go +12 -0
  380. package/templates/_executor/selected_scope.go +146 -0
  381. package/templates/_executor/selected_scope_test.go +186 -0
  382. package/templates/_executor/status_disabled_test.go +33 -0
  383. package/templates/_executor/store.go +4 -3
  384. package/templates/_executor/supervise.go +109 -0
  385. package/templates/_executor/supervise_test.go +130 -0
  386. package/templates/_executor/task_race_review_test.go +1 -1
  387. package/templates/_executor/tasks.go +7 -8
  388. package/templates/_executor/tasks_review_test.go +1 -1
  389. package/templates/_executor/tasks_test.go +4 -4
  390. package/templates/_executor/watch.go +8 -0
  391. package/templates/_executor/watch_test.go +23 -2
  392. package/templates/_executor/write_review_linux_test.go +4 -12
  393. package/templates/_shared/AGENTS.md +129 -0
  394. package/templates/binary/dsm-package.yaml +24 -0
  395. package/templates/binary/i18n/messages/de.json +4 -0
  396. package/templates/binary/i18n/messages/en.json +4 -0
  397. package/templates/binary/i18n/messages/es.json +4 -0
  398. package/templates/binary/i18n/messages/fr.json +4 -0
  399. package/templates/binary/i18n/messages/it.json +4 -0
  400. package/templates/binary/i18n/messages/ja.json +4 -0
  401. package/templates/binary/i18n/messages/ko.json +4 -0
  402. package/templates/binary/i18n/messages/zh-CN.json +4 -0
  403. package/templates/binary/i18n/messages/zh-TW.json +4 -0
  404. package/templates/binary/ui/app.js +19 -4
  405. package/templates/native-authorization/AuthorizationGate.vue +91 -0
  406. package/templates/native-authorization/i18n/de.json +37 -0
  407. package/templates/native-authorization/i18n/en.json +37 -0
  408. package/templates/native-authorization/i18n/es.json +37 -0
  409. package/templates/native-authorization/i18n/fr.json +37 -0
  410. package/templates/native-authorization/i18n/index.ts +15 -0
  411. package/templates/native-authorization/i18n/it.json +37 -0
  412. package/templates/native-authorization/i18n/ja.json +37 -0
  413. package/templates/native-authorization/i18n/ko.json +37 -0
  414. package/templates/native-authorization/i18n/zh-CN.json +37 -0
  415. package/templates/native-authorization/i18n/zh-TW.json +37 -0
  416. package/templates/native-go/README.md +43 -10
  417. package/templates/native-go/backend/cmd/app/main.go +27 -7
  418. package/templates/native-go/backend/internal/api/entry_test.go +47 -0
  419. package/templates/native-go/backend/internal/api/permissions_test.go +3 -3
  420. package/templates/native-go/backend/internal/api/registry.go +11 -0
  421. package/templates/native-go/backend/internal/api/sdk-permissions.json +14 -1
  422. package/templates/native-go/backend/internal/api/server.go +18 -5
  423. package/templates/native-go/backend/internal/app/delegate_config.go +57 -0
  424. package/templates/native-go/backend/internal/app/delegate_config_test.go +28 -0
  425. package/templates/native-go/backend/internal/app/portal.go +30 -0
  426. package/templates/native-go/backend/internal/app/portal_test.go +55 -0
  427. package/templates/native-go/backend/internal/app/routes.go +8 -2
  428. package/templates/native-go/backend/internal/background/runner.go +1 -1
  429. package/templates/native-go/backend/internal/background/store.go +16 -14
  430. package/templates/native-go/backend/internal/dsm/admin.go +8 -2
  431. package/templates/native-go/backend/internal/dsm/admin_linux.go +43 -13
  432. package/templates/native-go/backend/internal/dsm/admin_linux_test.go +56 -0
  433. package/templates/native-go/backend/internal/dsm/admin_other.go +4 -1
  434. package/templates/native-go/backend/internal/dsm/apppriv.go +30 -0
  435. package/templates/native-go/backend/internal/dsm/bridge_file.go +75 -0
  436. package/templates/native-go/backend/internal/dsm/bridge_file_test.go +57 -0
  437. package/templates/native-go/backend/internal/dsm/entry.go +177 -0
  438. package/templates/native-go/backend/internal/dsm/entry_test.go +315 -0
  439. package/templates/native-go/backend/internal/dsm/native/admin-armv7.elf +0 -0
  440. package/templates/native-go/backend/internal/dsm/native/apppriv-aarch64.elf +0 -0
  441. package/templates/native-go/backend/internal/dsm/native/apppriv-armv7.elf +0 -0
  442. package/templates/native-go/backend/internal/dsm/native/apppriv-x86_64.elf +0 -0
  443. package/templates/native-go/backend/internal/dsm/native/apppriv_check.c +33 -0
  444. package/templates/native-go/backend/internal/dsm/native/build.sh +13 -4
  445. package/templates/native-go/backend/internal/dsm/native/provenance.json +9 -3
  446. package/templates/native-go/backend/internal/filestation/check_permission.go +71 -0
  447. package/templates/native-go/backend/internal/filestation/check_permission_test.go +77 -0
  448. package/templates/native-go/backend/internal/filestation/sdk_policy_test.go +39 -0
  449. package/templates/native-go/backend/internal/filestation/service.go +1 -1
  450. package/templates/native-go/backend/internal/portal/portal.go +138 -0
  451. package/templates/native-go/backend/internal/portal/portal_test.go +89 -0
  452. package/templates/native-go/dsm-package.yaml +20 -1
  453. package/templates/native-go/i18n/glossary.json +2 -1
  454. package/templates/native-go/i18n/messages/de.json +1 -0
  455. package/templates/native-go/i18n/messages/en.json +1 -0
  456. package/templates/native-go/i18n/messages/es.json +1 -0
  457. package/templates/native-go/i18n/messages/fr.json +1 -0
  458. package/templates/native-go/i18n/messages/it.json +1 -0
  459. package/templates/native-go/i18n/messages/ja.json +1 -0
  460. package/templates/native-go/i18n/messages/ko.json +1 -0
  461. package/templates/native-go/i18n/messages/zh-CN.json +1 -0
  462. package/templates/native-go/i18n/messages/zh-TW.json +1 -0
  463. package/templates/native-go/ui/app.css +10 -0
  464. package/templates/native-go/ui/app.js +11 -1
  465. package/templates/native-go-delegate/DELEGATE.md +126 -5
  466. package/templates/native-go-delegate/backend/cmd/background-worker/main.go +7 -0
  467. package/templates/native-go-delegate/backend/internal/app/delegate.go +11 -1
  468. package/templates/native-go-delegate/backend/internal/app/delegate_routes_test.go +14 -0
  469. package/templates/native-go-delegate/backend/internal/background/delegated_moves.go +270 -0
  470. package/templates/native-go-delegate/backend/internal/delegate/admission_recovery_test.go +410 -0
  471. package/templates/native-go-delegate/backend/internal/delegate/cancel_contract_test.go +95 -0
  472. package/templates/native-go-delegate/backend/internal/delegate/client.go +8 -0
  473. package/templates/native-go-delegate/backend/internal/delegate/destination.go +110 -0
  474. package/templates/native-go-delegate/backend/internal/delegate/destination_state.go +117 -0
  475. package/templates/native-go-delegate/backend/internal/delegate/destination_state_test.go +60 -0
  476. package/templates/native-go-delegate/backend/internal/delegate/destination_test.go +116 -0
  477. package/templates/native-go-delegate/backend/internal/delegate/example.go +81 -0
  478. package/templates/native-go-delegate/backend/internal/delegate/example_test.go +91 -0
  479. package/templates/native-go-delegate/backend/internal/delegate/executor_contract_test.go +511 -0
  480. package/templates/native-go-delegate/backend/internal/delegate/files.go +1 -1
  481. package/templates/native-go-delegate/backend/internal/delegate/long_running_routes.go +92 -0
  482. package/templates/native-go-delegate/backend/internal/delegate/long_running_routes_test.go +99 -0
  483. package/templates/native-go-delegate/backend/internal/delegate/move.go +348 -0
  484. package/templates/native-go-delegate/backend/internal/delegate/move_test.go +351 -0
  485. package/templates/native-go-delegate/backend/internal/delegate/operations_list.go +65 -0
  486. package/templates/native-go-delegate/backend/internal/delegate/operations_list_test.go +114 -0
  487. package/templates/native-go-delegate/backend/internal/delegate/retry_test.go +1 -1
  488. package/templates/native-go-delegate/backend/internal/delegate/service.go +58 -17
  489. package/templates/native-go-delegate/backend/internal/delegate/staging.go +121 -0
  490. package/templates/native-go-delegate/backend/internal/delegate/status_disabled_test.go +51 -0
  491. package/templates/native-go-delegate/backend/internal/delegate/testdata/executor_contract.txt +112 -0
  492. package/templates/native-go-delegate/backend/internal/delegate/transient_contract_test.go +137 -0
  493. package/templates/native-go-delegate/backend/internal/filestation/files.go +1 -31
  494. package/templates/native-go-delegate/frontend/src/CapabilityPanels.vue +30 -3
  495. package/templates/native-go-delegate/frontend/src/delegate/BackgroundSavePanel.vue +113 -0
  496. package/templates/native-go-delegate/frontend/src/delegate/copy.ts +5 -6
  497. package/templates/native-go-delegate/frontend/test/background-save.test.ts +121 -0
  498. package/templates/native-go-delegate/frontend/test/scope-settings.test.ts +49 -0
  499. package/templates/native-go-delegate/test.sh +1 -0
  500. package/templates/native-go-jobs/frontend/src/background/i18n/de.json +2 -2
  501. package/templates/native-go-jobs/frontend/src/background/i18n/en.json +2 -2
  502. package/templates/native-go-jobs/frontend/src/background/i18n/es.json +12 -12
  503. package/templates/native-go-jobs/frontend/src/background/i18n/fr.json +4 -4
  504. package/templates/native-go-jobs/frontend/src/background/i18n/it.json +17 -17
  505. package/templates/native-go-jobs/frontend/src/background/i18n/ja.json +7 -7
  506. package/templates/native-go-jobs/frontend/src/background/i18n/ko.json +15 -15
  507. package/templates/native-go-jobs/frontend/src/background/i18n/zh-CN.json +3 -3
  508. package/templates/native-go-jobs/frontend/src/background/i18n/zh-TW.json +2 -2
  509. package/templates/native-go-jobs/frontend/test/background.test.ts +25 -28
  510. package/templates/native-go-vue/README.md +4 -0
  511. package/templates/native-go-vue/frontend/package-lock.json +4 -4
  512. package/templates/native-go-vue/frontend/package.json +1 -1
  513. package/templates/native-go-vue/frontend/src/App.vue +18 -13
  514. package/templates/native-go-vue/frontend/src/context.ts +31 -0
  515. package/templates/native-go-vue/frontend/src/delegate/ExecutorSettings.vue +52 -8
  516. package/templates/native-go-vue/frontend/src/delegate/catalogs.ts +11 -0
  517. package/templates/native-go-vue/frontend/src/delegate/copy.test.ts +12 -0
  518. package/templates/native-go-vue/frontend/src/delegate/copy.ts +4 -4
  519. package/templates/native-go-vue/frontend/src/delegate/executor-setup.ts +4 -2
  520. package/templates/native-go-vue/frontend/src/delegate/facade.test.ts +100 -0
  521. package/templates/native-go-vue/frontend/src/delegate/facade.ts +85 -0
  522. package/templates/native-go-vue/frontend/src/delegate/i18n/de.json +46 -0
  523. package/templates/native-go-vue/frontend/src/delegate/i18n/en.json +12 -2
  524. package/templates/native-go-vue/frontend/src/delegate/i18n/es.json +46 -0
  525. package/templates/native-go-vue/frontend/src/delegate/i18n/fr.json +46 -0
  526. package/templates/native-go-vue/frontend/src/delegate/i18n/it.json +46 -0
  527. package/templates/native-go-vue/frontend/src/delegate/i18n/ja.json +46 -0
  528. package/templates/native-go-vue/frontend/src/delegate/i18n/ko.json +46 -0
  529. package/templates/native-go-vue/frontend/src/delegate/i18n/save/de.json +42 -0
  530. package/templates/native-go-vue/frontend/src/delegate/i18n/save/en.json +42 -0
  531. package/templates/native-go-vue/frontend/src/delegate/i18n/save/es.json +42 -0
  532. package/templates/native-go-vue/frontend/src/delegate/i18n/save/fr.json +42 -0
  533. package/templates/native-go-vue/frontend/src/delegate/i18n/save/it.json +42 -0
  534. package/templates/native-go-vue/frontend/src/delegate/i18n/save/ja.json +42 -0
  535. package/templates/native-go-vue/frontend/src/delegate/i18n/save/ko.json +42 -0
  536. package/templates/native-go-vue/frontend/src/delegate/i18n/save/zh-CN.json +42 -0
  537. package/templates/native-go-vue/frontend/src/delegate/i18n/save/zh-TW.json +42 -0
  538. package/templates/native-go-vue/frontend/src/delegate/i18n/zh-CN.json +46 -0
  539. package/templates/native-go-vue/frontend/src/delegate/i18n/zh-TW.json +15 -5
  540. package/templates/native-go-vue/frontend/src/delegate/long-running.ts +57 -0
  541. package/templates/native-go-vue/frontend/src/delegate/save-copy.test.ts +12 -0
  542. package/templates/native-go-vue/frontend/src/delegate/save-copy.ts +24 -0
  543. package/templates/native-go-vue/frontend/src/delegate/scoped-picker.test.ts +12 -0
  544. package/templates/native-go-vue/frontend/src/delegate/scoped-picker.ts +21 -0
  545. package/templates/native-go-vue/frontend/src/files/files.test.ts +7 -0
  546. package/templates/native-go-vue/frontend/src/i18n/de.json +20 -0
  547. package/templates/native-go-vue/frontend/src/i18n/en.json +20 -0
  548. package/templates/native-go-vue/frontend/src/i18n/es.json +20 -0
  549. package/templates/native-go-vue/frontend/src/i18n/fr.json +20 -0
  550. package/templates/native-go-vue/frontend/src/i18n/it.json +20 -0
  551. package/templates/native-go-vue/frontend/src/i18n/ja.json +20 -0
  552. package/templates/native-go-vue/frontend/src/i18n/ko.json +20 -0
  553. package/templates/native-go-vue/frontend/src/i18n/zh-CN.json +20 -0
  554. package/templates/native-go-vue/frontend/src/i18n/zh-TW.json +20 -0
  555. package/templates/native-go-vue/frontend/src/i18n.ts +13 -0
  556. package/templates/native-go-vue/frontend/src/identity/LoginPage.vue +16 -14
  557. package/templates/native-go-vue/frontend/src/identity/i18n/de.json +27 -0
  558. package/templates/native-go-vue/frontend/src/identity/i18n/en.json +27 -0
  559. package/templates/native-go-vue/frontend/src/identity/i18n/es.json +27 -0
  560. package/templates/native-go-vue/frontend/src/identity/i18n/fr.json +27 -0
  561. package/templates/native-go-vue/frontend/src/identity/i18n/it.json +27 -0
  562. package/templates/native-go-vue/frontend/src/identity/i18n/ja.json +27 -0
  563. package/templates/native-go-vue/frontend/src/identity/i18n/ko.json +27 -0
  564. package/templates/native-go-vue/frontend/src/identity/i18n/zh-CN.json +27 -0
  565. package/templates/native-go-vue/frontend/src/identity/i18n/zh-TW.json +27 -0
  566. package/templates/native-go-vue/frontend/src/identity/i18n.ts +14 -0
  567. package/templates/native-go-vue/frontend/src/identity/session.test.ts +4 -0
  568. package/templates/native-go-vue/frontend/src/identity/session.ts +9 -8
  569. package/templates/native-go-vue/frontend/src/nas/NasPicker.vue +31 -12
  570. package/templates/native-go-vue/frontend/src/nas/destination.test.ts +48 -0
  571. package/templates/native-go-vue/frontend/src/nas/i18n/files/de.json +1 -1
  572. package/templates/native-go-vue/frontend/src/nas/i18n/files/en.json +1 -1
  573. package/templates/native-go-vue/frontend/src/nas/i18n/files/es.json +10 -10
  574. package/templates/native-go-vue/frontend/src/nas/i18n/files/fr.json +1 -1
  575. package/templates/native-go-vue/frontend/src/nas/i18n/files/it.json +12 -12
  576. package/templates/native-go-vue/frontend/src/nas/i18n/files/ja.json +15 -15
  577. package/templates/native-go-vue/frontend/src/nas/i18n/files/ko.json +12 -12
  578. package/templates/native-go-vue/frontend/src/nas/i18n/files/zh-CN.json +1 -1
  579. package/templates/native-go-vue/frontend/src/nas/i18n/files/zh-TW.json +1 -1
  580. package/templates/native-go-vue/frontend/src/portal/PortalSettings.vue +39 -0
  581. package/templates/native-go-vue/frontend/src/shell/i18n/fr.json +1 -1
  582. package/templates/native-go-vue/frontend/src/shell/i18n/ja.json +1 -1
  583. package/templates/native-go-vue/frontend/test/apps.test.ts +14 -0
  584. package/templates/native-go-vue/frontend/test/portal-settings.test.ts +71 -0
  585. package/templates/native-go-vue/frontend/test/shell-i18n.test.ts +50 -0
@@ -1,6 +1,7 @@
1
+ import { metadataInfoFields } from "../package-metadata.js";
1
2
  import { chmodSync, copyFileSync, cpSync, existsSync, mkdirSync, mkdtempSync, readFileSync, readdirSync, rmSync, statSync, writeFileSync, } from "node:fs";
2
3
  import { tmpdir } from "node:os";
3
- import { join } from "node:path";
4
+ import { dirname, join } from "node:path";
4
5
  import { createHash } from "node:crypto";
5
6
  import * as tar from "tar";
6
7
  import { parseDocument, Scalar } from "yaml";
@@ -11,13 +12,14 @@ import { classifyComposeRoute, loadCompose } from "../compose/analyze.js";
11
12
  import { scanComposePrivilege } from "../compose/privilege.js";
12
13
  import { validateProject } from "../validate.js";
13
14
  import { writeDsmWindowIcons, writePackageCenterIcons } from "./dsm-icons.js";
14
- import { infoArchValue } from "./dsm-arch.js";
15
- import { assertContainerMinDsm, resolveMinDsm } from "./dsm-version.js";
15
+ import { infoArchValue, isContainerArchFamily } from "./dsm-arch.js";
16
+ import { assertContainerMinDsm, assertPortalMinDsm, resolveMinDsm } from "./dsm-version.js";
16
17
  import { createReproducibleTar } from "./reproducible-archive.js";
17
18
  import { copyProjectNotices } from "./notices.js";
18
19
  import { renderDeleteAppDataBlock } from "./delete-app-data.js";
19
20
  import { renderUninstallWizardFiles } from "./uninstall-wizard.js";
20
21
  import { renderInstallWizardFiles } from "./install-wizard.js";
22
+ import { contractPortal, PORTAL_JSON_RELPATH, PORTAL_MUSTACHE_RELPATH, renderPortalJson, renderPortalLifecycle, renderPortalMustache, renderPortalWizardStep } from "../portal/package.js";
21
23
  import { renderWizardFiles, writeWizardUiFiles } from "./wizard-i18n.js";
22
24
  import { sharedFolderCombobox } from "./share-combobox.js";
23
25
  import { renderHeadlessResourceMarker, renderResourceConfig, renderResourceWorkerStartStopStatus, } from "./resource-worker.js";
@@ -38,6 +40,9 @@ const SCRIPT_NAMES = [
38
40
  "start-stop-status",
39
41
  ];
40
42
  export async function buildSpk(options) {
43
+ if (options.archFamily && !isContainerArchFamily(options.archFamily)) {
44
+ return { ok: false, errors: ["CONTAINER_ARCH_UNSUPPORTED"] };
45
+ }
41
46
  const validation = validateProject(options.project);
42
47
  if (!validation.ok) {
43
48
  return {
@@ -61,6 +66,8 @@ export async function buildSpk(options) {
61
66
  }
62
67
  if (options.minDsm !== undefined) {
63
68
  assertContainerMinDsm(String(contract.os_min_ver));
69
+ if (contractPortal(contract))
70
+ assertPortalMinDsm(String(contract.os_min_ver));
64
71
  }
65
72
  const parsedCompose = loadCompose(options.project.composePath);
66
73
  const route = parsedCompose?.compose
@@ -114,7 +121,7 @@ export async function buildSpk(options) {
114
121
  mkdirSync(join(staging, "scripts"), { recursive: true });
115
122
  for (const name of SCRIPT_NAMES) {
116
123
  const scriptPath = join(staging, "scripts", name);
117
- writeFileSync(scriptPath, renderManagedScript(name, contract, isResourceWorkerRoute, rwEligibility?.adminport, isDynamicRoute, rwEligibility?.headless === true), "utf8");
124
+ writeFileSync(scriptPath, withPortalLifecycle(name, contract, renderManagedScript(name, contract, isResourceWorkerRoute, rwEligibility?.adminport, isDynamicRoute, rwEligibility?.headless === true)), "utf8");
118
125
  chmodSync(scriptPath, SCRIPT_MODE);
119
126
  }
120
127
  mkdirSync(join(staging, "package"), { recursive: true });
@@ -172,6 +179,14 @@ export async function buildSpk(options) {
172
179
  writeFileSync(join(uiDir, "config"), renderDsmWindowConfig(contract), "utf8");
173
180
  writeDsmWindowIcons(uiDir);
174
181
  }
182
+ const portal = contractPortal(contract);
183
+ if (portal) {
184
+ for (const [path, text] of [[PORTAL_JSON_RELPATH, renderPortalJson(portal)], [PORTAL_MUSTACHE_RELPATH, renderPortalMustache()]]) {
185
+ mkdirSync(dirname(join(staging, "package", path)), { recursive: true });
186
+ writeFileSync(join(staging, "package", path), text, "utf8");
187
+ chmodSync(join(staging, "package", path), FILE_MODE);
188
+ }
189
+ }
175
190
  if (gateway && gatewayBinary) {
176
191
  writeGatewayPayload(join(staging, "package"), gatewayBinary, gateway);
177
192
  }
@@ -208,6 +223,8 @@ function renderInfo(contract, archFamily, isResourceWorker = false) {
208
223
  add("displayname", String(contract.displayname));
209
224
  add("description", String(contract.description));
210
225
  add("maintainer", String(contract.maintainer));
226
+ for (const [key, value] of metadataInfoFields(contract.metadata))
227
+ add(key, value);
211
228
  add("arch", archFamily === undefined ? "noarch" : infoArchValue(archFamily));
212
229
  add("os_min_ver", String(contract.os_min_ver));
213
230
  const viaGateway = isResourceWorker && usesAuthGateway(contract);
@@ -584,7 +601,11 @@ function renderUpgradeWizard(t, contract, compose) {
584
601
  }];
585
602
  }
586
603
  function writeWizards(staging, contract, compose, kind) {
587
- const install = renderInstallWizardFiles(String(contract.package), (t) => renderInstallWizard(t, contract, compose));
604
+ const portal = contractPortal(contract);
605
+ const install = renderInstallWizardFiles(String(contract.package), (t) => [
606
+ ...renderInstallWizard(t, contract, compose),
607
+ ...(portal ? [renderPortalWizardStep(t, portal.alias)] : []),
608
+ ]);
588
609
  writeWizardUiFiles(staging, {
589
610
  ...install.static,
590
611
  ...install.dynamic,
@@ -783,6 +804,10 @@ function renderDynamicCanonicalMirror(headless = false) {
783
804
  function shellQuote(value) {
784
805
  return "'" + value.replace(/'/g, "'\\''") + "'";
785
806
  }
807
+ function withPortalLifecycle(name, contract, script) {
808
+ const portal = contractPortal(contract);
809
+ return portal ? script.replace("#!/bin/sh\n", () => `#!/bin/sh\n${renderPortalLifecycle(name, portal)}`) : script;
810
+ }
786
811
  function renderManagedScript(name, contract, isResourceWorker = false, resourceWorkerAdminport, isDynamicRoute = false, isHeadlessRoute = false) {
787
812
  if (isResourceWorker) {
788
813
  if (name === "start-stop-status") {
@@ -38,7 +38,10 @@ export function copyStandaloneNotices(root, uiDir, payload, go, goDir = join(roo
38
38
  }
39
39
  const nativeAdmin = join(goDir, 'internal/dsm/native');
40
40
  if (go && existsSync(join(nativeAdmin, 'admin_check.c'))) {
41
- for (const name of ['admin_check.c', 'build.sh', 'provenance.json', 'licenses']) {
41
+ for (const name of ['admin_check.c', 'apppriv_check.c', 'build.sh', 'provenance.json', 'licenses']) {
42
+ // Projects scaffolded before the AppPriv bridge carry only the administrator bridge.
43
+ if (name === 'apppriv_check.c' && !existsSync(join(nativeAdmin, name)))
44
+ continue;
42
45
  copyNoticeTree(join(nativeAdmin, name), join(dest, 'native-admin', name));
43
46
  }
44
47
  }
@@ -1,3 +1,4 @@
1
+ import { contractPortal, portalResource } from "../portal/package.js";
1
2
  import { collectUnpinnedImages } from "../compose/analyze.js";
2
3
  import { cookieStripHeaders, resolvedWebLaunch, usesAuthGateway, webStationAlias, webStationServiceName, } from "../contract/web-launch.js";
3
4
  export { classifyComposeRoute, resourceWorkerEligibility, } from "../compose/analyze.js";
@@ -78,6 +79,8 @@ export function renderResourceConfig(contract, eligibility, compose) {
78
79
  ...webResource,
79
80
  };
80
81
  }
82
+ if (contractPortal(contract))
83
+ combined = { ...combined, ...portalResource() };
81
84
  return `${JSON.stringify(combined, null, 2)}\n`;
82
85
  }
83
86
  /**
@@ -1,4 +1,70 @@
1
1
  export declare const WIZARD_MESSAGES: {
2
+ "delegate.preselection": {
3
+ en: string;
4
+ "zh-TW": string;
5
+ "zh-CN": string;
6
+ ja: string;
7
+ ko: string;
8
+ de: string;
9
+ fr: string;
10
+ es: string;
11
+ it: string;
12
+ };
13
+ "delegate.home": {
14
+ en: string;
15
+ "zh-TW": string;
16
+ "zh-CN": string;
17
+ ja: string;
18
+ ko: string;
19
+ de: string;
20
+ fr: string;
21
+ es: string;
22
+ it: string;
23
+ };
24
+ "portal.step": {
25
+ en: string;
26
+ "zh-TW": string;
27
+ "zh-CN": string;
28
+ ja: string;
29
+ ko: string;
30
+ de: string;
31
+ fr: string;
32
+ es: string;
33
+ it: string;
34
+ };
35
+ "portal.notice": {
36
+ en: string;
37
+ "zh-TW": string;
38
+ "zh-CN": string;
39
+ ja: string;
40
+ ko: string;
41
+ de: string;
42
+ fr: string;
43
+ es: string;
44
+ it: string;
45
+ };
46
+ "portal.alias": {
47
+ en: string;
48
+ "zh-TW": string;
49
+ "zh-CN": string;
50
+ ja: string;
51
+ ko: string;
52
+ de: string;
53
+ fr: string;
54
+ es: string;
55
+ it: string;
56
+ };
57
+ "portal.aliasInvalid": {
58
+ en: string;
59
+ "zh-TW": string;
60
+ "zh-CN": string;
61
+ ja: string;
62
+ ko: string;
63
+ de: string;
64
+ fr: string;
65
+ es: string;
66
+ it: string;
67
+ };
2
68
  "install.step": {
3
69
  en: string;
4
70
  "zh-TW": string;
@@ -1,4 +1,70 @@
1
1
  export const WIZARD_MESSAGES = {
2
+ "delegate.preselection": {
3
+ en: "Pre-select the shared folder for background saves. Confirm this selection with your administrator password when first enabling background access in the app. This does not grant the package account access to the share.",
4
+ "zh-TW": "預選背景存檔使用的共用資料夾。首次在 App 啟用背景存取時,以管理員密碼確認這個選項。這不會授予套件帳號共用資料夾權限。",
5
+ "zh-CN": "预选后台保存使用的共享文件夹。首次在应用启用后台访问时,以管理员密码确认此选项。这不会授予软件包账户共享文件夹权限。",
6
+ ja: "バックグラウンド保存先の共有フォルダを仮選択します。アプリで初めて有効にする際、管理者パスワードで確認してください。パッケージアカウントに共有へのアクセス権は付与されません。",
7
+ ko: "백그라운드 저장에 사용할 공유 폴더를 미리 선택합니다. 앱에서 처음 활성화할 때 관리자 암호로 확인하십시오. 패키지 계정에 공유 접근 권한을 부여하지 않습니다.",
8
+ de: "Wählen Sie den gemeinsamen Ordner für Hintergrundspeicherungen vor. Bestätigen Sie ihn beim ersten Aktivieren in der App mit Ihrem Administratorpasswort. Das Paketkonto erhält dadurch keinen Zugriff.",
9
+ fr: "Présélectionnez le dossier partagé pour les enregistrements en arrière-plan. Confirmez-le avec votre mot de passe administrateur lors de la première activation dans l'application. Aucun accès n'est accordé au compte du paquet.",
10
+ es: "Preseleccione la carpeta compartida para guardar en segundo plano. Confírmela con su contraseña de administrador al activar el acceso en la aplicación. No se otorga acceso a la cuenta del paquete.",
11
+ it: "Preselezionare la cartella condivisa per i salvataggi in background. Confermarla con la password amministratore alla prima attivazione nell'app. Non concede accesso all'account del pacchetto.",
12
+ },
13
+ "delegate.home": {
14
+ en: "Use each user's home folder instead (ignore the shared folder above)",
15
+ "zh-TW": "改用各使用者的 home 資料夾(忽略上方共用資料夾)",
16
+ "zh-CN": "改用各用户的 home 文件夹(忽略上方共享文件夹)",
17
+ ja: "代わりに各ユーザーの home フォルダを使う(上の共有フォルダは無視)",
18
+ ko: "대신 각 사용자의 home 폴더 사용 (위 공유 폴더 무시)",
19
+ de: "Stattdessen den home-Ordner jedes Benutzers verwenden (Auswahl oben ignorieren)",
20
+ fr: "Utiliser le dossier home de chaque utilisateur (ignorer le dossier ci-dessus)",
21
+ es: "Usar la carpeta home de cada usuario (ignorar la carpeta de arriba)",
22
+ it: "Usa la cartella home di ogni utente (ignora la cartella sopra)",
23
+ },
24
+ "portal.step": {
25
+ en: "Application Portal",
26
+ "zh-TW": "應用程式入口",
27
+ "zh-CN": "应用程序门户",
28
+ ja: "アプリケーションポータル",
29
+ ko: "응용 프로그램 포털",
30
+ de: "Anwendungsportal",
31
+ fr: "Portail d'application",
32
+ es: "Portal de aplicaciones",
33
+ it: "Portale applicazioni",
34
+ },
35
+ "portal.notice": {
36
+ en: "Application Portal: an administrator can later let users open this app directly at https://<your NAS>/<alias>/ after DSM sign-in. Nothing is published now; activate it in the app's settings or in Control Panel > Login Portal > Applications.",
37
+ "zh-TW": "Application Portal:管理員之後可讓使用者在 DSM 登入後,直接以 https://<你的 NAS>/<alias>/ 開啟這個 App。現在不會公開任何網址;請在 App 的設定或「控制台 > 登入入口 > 應用程式」中啟用。",
38
+ "zh-CN": "Application Portal:管理员之后可让用户在 DSM 登录后,直接通过 https://<你的 NAS>/<alias>/ 打开此应用。现在不会公开任何网址;请在应用的设置或“控制面板 > 登录门户 > 应用程序”中启用。",
39
+ ja: "Application Portal:管理者は後で、DSM サインイン後に https://<NAS>/<alias>/ でこのアプリを直接開けるようにできます。現時点では何も公開されません。アプリの設定、または「コントロールパネル > ログインポータル > アプリケーション」で有効にしてください。",
40
+ ko: "Application Portal: 관리자는 나중에 사용자가 DSM 로그인 후 https://<NAS>/<alias>/ 에서 이 앱을 바로 열 수 있게 할 수 있습니다. 지금은 아무것도 공개되지 않습니다. 앱 설정이나 '제어판 > 로그인 포털 > 응용 프로그램'에서 활성화하십시오.",
41
+ de: "Application Portal: Ein Administrator kann Benutzern später erlauben, diese App nach der DSM-Anmeldung direkt unter https://<Ihr NAS>/<alias>/ zu öffnen. Jetzt wird nichts veröffentlicht; aktivieren Sie es in den App-Einstellungen oder unter Systemsteuerung > Anmeldeportal > Anwendungen.",
42
+ fr: "Application Portal : un administrateur pourra ensuite permettre d'ouvrir cette application directement sur https://<votre NAS>/<alias>/ après connexion à DSM. Rien n'est publié maintenant ; activez-le dans les paramètres de l'application ou dans Panneau de configuration > Portail de connexion > Applications.",
43
+ es: "Application Portal: un administrador podrá permitir más tarde abrir esta aplicación directamente en https://<su NAS>/<alias>/ tras iniciar sesión en DSM. Ahora no se publica nada; actívelo en los ajustes de la aplicación o en Panel de control > Portal de inicio de sesión > Aplicaciones.",
44
+ it: "Application Portal: un amministratore potrà in seguito consentire di aprire questa app direttamente su https://<NAS>/<alias>/ dopo l'accesso a DSM. Ora non viene pubblicato nulla; attivarlo nelle impostazioni dell'app o in Pannello di controllo > Portale di accesso > Applicazioni.",
45
+ },
46
+ "portal.alias": {
47
+ en: "Suggested portal alias",
48
+ "zh-TW": "建議的入口別名",
49
+ "zh-CN": "建议的门户别名",
50
+ ja: "推奨ポータルエイリアス",
51
+ ko: "권장 포털 별칭",
52
+ de: "Vorgeschlagener Portal-Alias",
53
+ fr: "Alias de portail suggéré",
54
+ es: "Alias de portal sugerido",
55
+ it: "Alias del portale suggerito",
56
+ },
57
+ "portal.aliasInvalid": {
58
+ en: "Use 1-64 lowercase letters, digits or hyphens, not starting or ending with a hyphen.",
59
+ "zh-TW": "請使用 1–64 個小寫英文字母、數字或連字號,且不可以連字號開頭或結尾。",
60
+ "zh-CN": "请使用 1–64 个小写英文字母、数字或连字符,且不能以连字符开头或结尾。",
61
+ ja: "1~64 文字の小文字英字・数字・ハイフンを使用し、先頭と末尾にハイフンは使えません。",
62
+ ko: "1~64자의 영문 소문자, 숫자 또는 하이픈을 사용하고 하이픈으로 시작하거나 끝나지 않게 하십시오.",
63
+ de: "Verwenden Sie 1–64 Kleinbuchstaben, Ziffern oder Bindestriche, ohne Bindestrich am Anfang oder Ende.",
64
+ fr: "Utilisez 1 à 64 lettres minuscules, chiffres ou tirets, sans tiret au début ni à la fin.",
65
+ es: "Use de 1 a 64 letras minúsculas, dígitos o guiones, sin guion al principio ni al final.",
66
+ it: "Usare da 1 a 64 lettere minuscole, cifre o trattini, senza trattino all'inizio o alla fine.",
67
+ },
2
68
  "install.step": {
3
69
  en: "Install settings",
4
70
  "zh-TW": "安裝設定",
@@ -15,11 +81,11 @@ export const WIZARD_MESSAGES = {
15
81
  "zh-TW": "這台 NAS 還留有先前安裝的資料(@appdata/{package}),本次安裝會沿用:已存的設定與產生的 secrets 都會保留。若要從空白開始,請先取消並清除該資料——解除安裝時勾選「一併刪除上列 App 資料」——再重新安裝。",
16
82
  "zh-CN": "这台 NAS 还保留着先前安装的数据(@appdata/{package}),本次安装将继续使用:已保存的设置和生成的密钥都会保留。若要从空白开始,请先取消并清除该数据——卸载时勾选“同时删除上列应用数据”——然后重新安装。",
17
83
  ja: "この NAS には以前のインストールのデータ(@appdata/{package})が残っているため、今回のインストールはそれを引き継ぎます。保存済みの設定と生成済みシークレットは保持されます。空の状態から始めるには、いったん中止し、そのデータを削除して(アンインストール時に「上記のアプリデータも削除する」をオン)から再インストールしてください。",
18
- ko: "이 NAS에는 이전 설치의 데이터(@appdata/{package})가 남아 있어 이번 설치에서 그대로 사용합니다. 저장된 설정과 생성된 시크릿이 유지됩니다. 빈 상태로 시작하려면 먼저 취소하고 해당 데이터를 지운 뒤(제거 시 '위에 나열된 앱 데이터도 삭제' 선택) 다시 설치하세요.",
84
+ ko: "이 NAS에는 이전 설치의 데이터(@appdata/{package})가 남아 있어 이번 설치에서 그대로 사용합니다. 저장된 설정과 생성된 시크릿이 유지됩니다. 빈 상태로 시작하려면 먼저 취소하고 해당 데이터를 지운 뒤(제거 시 '위에 나열된 앱 데이터도 삭제' 선택) 다시 설치하십시오.",
19
85
  de: "Auf diesem NAS liegen noch Daten einer früheren Installation (@appdata/{package}); diese Installation verwendet sie weiter: gespeicherte Einstellungen und erzeugte Geheimnisse bleiben erhalten. Für einen leeren Start brechen Sie ab, löschen diese Daten (Deinstallation mit aktiviertem „Oben aufgeführte App-Daten ebenfalls löschen“) und installieren erneut.",
20
86
  fr: "Ce NAS contient encore les données d'une installation précédente (@appdata/{package}) ; cette installation les réutilise : les réglages enregistrés et les secrets générés sont conservés. Pour repartir de zéro, annulez, supprimez ces données (désinstallation avec « Supprimer aussi les données de l'application listées ci-dessus » cochée), puis réinstallez.",
21
87
  es: "Este NAS todavía conserva datos de una instalación anterior (@appdata/{package}); esta instalación los reutiliza: se mantienen los ajustes guardados y los secretos generados. Para empezar de cero, cancele, borre esos datos (desinstale con «Eliminar también los datos de la aplicación indicados arriba» marcado) y vuelva a instalar.",
22
- it: "Questo NAS conserva ancora i dati di un'installazione precedente (@appdata/{package}); questa installazione li riutilizza: le impostazioni salvate e i segreti generati restano. Per partire da zero, annulla, elimina quei dati (disinstallazione con «Elimina anche i dati dell'app elencati sopra» selezionato) e reinstalla.",
88
+ it: "Questo NAS conserva ancora i dati di un'installazione precedente (@appdata/{package}); questa installazione li riutilizza: le impostazioni salvate e i segreti generati restano. Per partire da zero, annullare, eliminare quei dati (disinstallazione con «Elimina anche i dati dell'app elencati sopra» selezionato) e reinstallare.",
23
89
  },
24
90
  "upgrade.step": {
25
91
  en: "Upgrade settings",
@@ -147,7 +213,7 @@ export const WIZARD_MESSAGES = {
147
213
  "zh-TW": "此 App 會使用安裝當下這台 NAS 的全部 Shared Folder(App-wide,不依個別 DSM ACL);容器內可讀(或讀寫,依 Compose 模式)所有這些資料夾的檔案。安裝後新建的共用資料夾不會自動加入。Package Center 無法提供安裝當下的 Shared Folder 清單快照;要以全部 Share 安裝,請用 CLI `synopkgland deploy`,由 CLI 以管理員權限列舉全部 Share 並寫入快照。由管理員決定是否安裝。",
148
214
  "zh-CN": "此应用会使用安装时这台 NAS 上的全部共享文件夹(应用级,不依据各个 DSM ACL);容器内可读取(或读写,取决于 Compose 模式)所有这些文件夹中的文件。安装后新建的共享文件夹不会自动加入。Package Center 无法提供安装时的共享文件夹列表快照;若要以全部共享文件夹安装,请使用 CLI `synopkgland deploy`,由 CLI 以管理员权限列出全部共享文件夹并写入快照。是否安装由管理员决定。",
149
215
  ja: "このアプリは、インストール時点でこの NAS にあるすべての共有フォルダを使用します(アプリ全体に適用され、個々の DSM ACL には従いません)。コンテナはそれらすべてのフォルダ内のファイルを読み取れます(Compose のモードによっては読み書きも可能です)。インストール後に作成された共有フォルダは自動的には追加されません。Package Center ではインストール時点の共有フォルダ一覧のスナップショットを提供できません。すべての共有フォルダでインストールするには、CLI の `synopkgland deploy` を使用してください。CLI が管理者権限ですべての共有フォルダを列挙し、スナップショットを書き込みます。インストールするかどうかは管理者が判断します。",
150
- ko: "이 앱은 설치 시점에 이 NAS에 있는 모든 공유 폴더를 사용합니다(앱 전체 범위이며 개별 DSM ACL을 따르지 않음). 컨테이너는 해당 폴더의 파일을 모두 읽을 수 있으며 Compose 모드에 따라 읽고 쓸 수도 있습니다. 설치 후 새로 만든 공유 폴더는 자동으로 추가되지 않습니다. Package Center는 설치 시점의 공유 폴더 목록 스냅샷을 제공할 수 없습니다. 모든 공유 폴더로 설치하려면 CLI `synopkgland deploy`를 사용하세요. CLI가 관리자 권한으로 모든 공유 폴더를 나열하고 스냅샷을 기록합니다. 설치 여부는 관리자가 결정합니다.",
216
+ ko: "이 앱은 설치 시점에 이 NAS에 있는 모든 공유 폴더를 사용합니다(앱 전체 범위이며 개별 DSM ACL을 따르지 않음). 컨테이너는 해당 폴더의 파일을 모두 읽을 수 있으며 Compose 모드에 따라 읽고 쓸 수도 있습니다. 설치 후 새로 만든 공유 폴더는 자동으로 추가되지 않습니다. Package Center는 설치 시점의 공유 폴더 목록 스냅샷을 제공할 수 없습니다. 모든 공유 폴더로 설치하려면 CLI `synopkgland deploy`를 사용하십시오. CLI가 관리자 권한으로 모든 공유 폴더를 나열하고 스냅샷을 기록합니다. 설치 여부는 관리자가 결정합니다.",
151
217
  de: "Diese App verwendet alle zum Installationszeitpunkt vorhandenen freigegebenen Ordner dieses NAS (appweit, unabhängig von einzelnen DSM-ACLs); die Container können die Dateien in allen diesen Ordnern lesen (oder je nach Compose-Modus lesen und schreiben). Nach der Installation angelegte freigegebene Ordner werden nicht automatisch hinzugefügt. Das Paket-Zentrum kann keine Momentaufnahme der Liste der freigegebenen Ordner zum Installationszeitpunkt bereitstellen. Um mit allen Freigaben zu installieren, verwenden Sie die CLI `synopkgland deploy`: Sie listet alle Freigaben mit Administratorrechten auf und schreibt die Momentaufnahme. Der Administrator entscheidet, ob installiert wird.",
152
218
  fr: "Cette application utilise tous les dossiers partagés de ce NAS existant au moment de l'installation (à l'échelle de l'application, sans tenir compte des ACL DSM individuelles) ; les conteneurs peuvent lire (ou lire et écrire, selon le mode Compose) les fichiers de tous ces dossiers. Les dossiers partagés créés après l'installation ne sont pas ajoutés automatiquement. Package Center ne peut pas fournir un instantané de la liste des dossiers partagés au moment de l'installation. Pour installer avec tous les dossiers partagés, utilisez la CLI `synopkgland deploy` : elle énumère tous les dossiers partagés avec les droits d'administrateur et écrit l'instantané. L'administrateur décide de l'installation.",
153
219
  es: "Esta aplicación usa todas las carpetas compartidas de este NAS existentes en el momento de la instalación (a nivel de aplicación, sin respetar las ACL de DSM individuales); los contenedores pueden leer (o leer y escribir, según el modo de Compose) los archivos de todas ellas. Las carpetas compartidas creadas después de la instalación no se añaden automáticamente. Package Center no puede proporcionar una instantánea de la lista de carpetas compartidas en el momento de la instalación. Para instalar con todas las carpetas compartidas, use la CLI `synopkgland deploy`: enumera todas las carpetas compartidas con derechos de administrador y escribe la instantánea. El administrador decide si se instala.",
@@ -162,7 +228,7 @@ export const WIZARD_MESSAGES = {
162
228
  de: "Der Container dieser App hat hohe Rechte, nahe an root auf diesem NAS und damit fast am gesamten System. Sie als Administrator entscheiden, ob die App installiert wird.",
163
229
  fr: "Le conteneur de cette application dispose de privilèges élevés, proches de root sur ce NAS et donc de presque toute la machine. C'est à vous, administrateur, de décider de l'installer ou non.",
164
230
  es: "El contenedor de esta aplicación tiene privilegios elevados, cercanos a root en este NAS y, por tanto, a casi toda la máquina. Usted, como administrador, decide si se instala.",
165
- it: "Il container di questa app ha privilegi elevati, vicini a root su questo NAS e quindi a quasi tutta la macchina. Spetta a lei, come amministratore, decidere se installarla.",
231
+ it: "Il container di questa app ha privilegi elevati, vicini a root su questo NAS e quindi a quasi tutta la macchina. Spetta all'amministratore decidere se installarla.",
166
232
  },
167
233
  "upgrade.posixNotice": {
168
234
  en: "An upgrade keeps the existing snapshot (all Shared Folders the CLI listed at install time). The mounted scope can only be changed explicitly through the CLI (`synopkgland deploy`); a Package Center upgrade does not change the existing snapshot.",
@@ -217,7 +283,7 @@ export const WIZARD_MESSAGES = {
217
283
  de: "{label} ändern (nicht aktiviert = aktuelle Einstellung beibehalten)",
218
284
  fr: "Modifier {label} (non coché = conserver le réglage actuel)",
219
285
  es: "Cambiar {label} (sin marcar = mantener la configuración actual)",
220
- it: "Modifica {label} (non selezionato = mantieni l'impostazione attuale)",
286
+ it: "Modifica {label} (non selezionato = mantenere l'impostazione attuale)",
221
287
  },
222
288
  "upgrade.changeSub": {
223
289
  en: "Change {label}",
@@ -300,8 +366,8 @@ export const WIZARD_MESSAGES = {
300
366
  "en": "DSM may retain empty @appdata/{package}, @appconf/{package}, @apphome/{package}, @appshare/{package}, the package-config symlink /usr/syno/etc/packages/{package}, and the package account/group. The checkbox empties data/config/home contents; it does not remove DSM directory roots, shared folders or system identities. After uninstall, an administrator can review unused remnants and ACLs before removing only this package’s infrastructure.",
301
367
  "zh-TW": "DSM 可能保留空的 @appdata/{package}、@appconf/{package}、@apphome/{package}、@appshare/{package}、套件設定連結 /usr/syno/etc/packages/{package} 與套件帳號/群組。勾選後會清空資料、設定與 home 內容,保留 DSM 目錄本身、共用資料夾與系統身分。解除安裝後,管理員可先確認殘留項目與 ACL 已不再使用,再清除此套件的基礎項目。",
302
368
  "zh-CN": "DSM 可能保留空的 @appdata/{package}、@appconf/{package}、@apphome/{package}、@appshare/{package}、套件配置链接 /usr/syno/etc/packages/{package} 和套件账户/组。勾选后会清空数据、配置和 home 内容,保留 DSM 目录本身、共享文件夹和系统身份。卸载后,管理员可检查残留项目与 ACL 不再使用后,再清理本套件的基础项目。",
303
- "ja": "DSM は空の @appdata/{package}、@appconf/{package}、@apphome/{package}、@appshare/{package}、設定リンク /usr/syno/etc/packages/{package}、パッケージのアカウントとグループを保持する場合があります。チェックするとデータ・設定・home の内容を削除しますが、DSM のディレクトリ、共有フォルダー、システム ID は保持します。アンインストール後、管理者は不要な項目と ACL を確認してから、このパッケージの項目だけを削除してください。",
304
- "ko": "DSM은 빈 @appdata/{package}, @appconf/{package}, @apphome/{package}, @appshare/{package}, 설정 링크 /usr/syno/etc/packages/{package}, 패키지 계정/그룹을 유지할 수 있습니다. 체크하면 데이터/설정/home 내용만 비우며 DSM 디렉터리, 공유 폴더, 시스템 ID는 유지합니다. 제거 후 관리자는 남은 항목과 ACL을 확인하고 이 패키지의 불필요한 항목만 정리하세요.",
369
+ "ja": "DSM は空の @appdata/{package}、@appconf/{package}、@apphome/{package}、@appshare/{package}、設定リンク /usr/syno/etc/packages/{package}、パッケージのアカウントとグループを保持する場合があります。チェックするとデータ・設定・home の内容を削除しますが、DSM のディレクトリ、共有フォルダ、システム ID は保持します。アンインストール後、管理者は不要な項目と ACL を確認してから、このパッケージの項目だけを削除してください。",
370
+ "ko": "DSM은 빈 @appdata/{package}, @appconf/{package}, @apphome/{package}, @appshare/{package}, 설정 링크 /usr/syno/etc/packages/{package}, 패키지 계정/그룹을 유지할 수 있습니다. 체크하면 데이터/설정/home 내용만 비우며 DSM 디렉터리, 공유 폴더, 시스템 ID는 유지합니다. 제거 후 관리자는 남은 항목과 ACL을 확인하고 이 패키지의 불필요한 항목만 정리하십시오.",
305
371
  "de": "DSM kann leere @appdata/{package}, @appconf/{package}, @apphome/{package}, @appshare/{package}, den Konfigurationslink /usr/syno/etc/packages/{package} sowie Paketkonto und -gruppe behalten. Das Kontrollkästchen leert Daten, Konfiguration und home, behält aber DSM-Verzeichnisse, Freigaben und Systemidentitäten. Nach der Deinstallation kann ein Administrator ungenutzte Reste und ACLs prüfen und nur die Infrastruktur dieses Pakets entfernen.",
306
372
  "fr": "DSM peut conserver les répertoires vides @appdata/{package}, @appconf/{package}, @apphome/{package}, @appshare/{package}, le lien de configuration /usr/syno/etc/packages/{package} et le compte/groupe du paquet. La case vide les données, la configuration et home, mais conserve les répertoires DSM, dossiers partagés et identités système. Après la désinstallation, un administrateur peut vérifier les éléments inutilisés et leurs ACL avant de supprimer uniquement ceux de ce paquet.",
307
373
  "es": "DSM puede conservar @appdata/{package}, @appconf/{package}, @apphome/{package}, @appshare/{package} vacíos, el enlace de configuración /usr/syno/etc/packages/{package} y la cuenta/grupo del paquete. La casilla vacía datos, configuración y home; conserva directorios DSM, carpetas compartidas e identidades del sistema. Tras desinstalar, un administrador puede revisar los restos y ACL antes de eliminar solo la infraestructura de este paquete.",
@@ -367,11 +433,11 @@ export const WIZARD_MESSAGES = {
367
433
  "zh-TW": "此 App 會把檔案存放在你於下方選擇的共用資料夾。DSM 不會自動讓 App 的套件帳號存取這裡選的資料夾:安裝後請到「控制台」>「共用資料夾」,編輯該資料夾,在「權限」>「系統內部使用者」中授予套件帳號 {account}「{permission}」權限。在此之前,App 會回報無法使用該資料夾。",
368
434
  "zh-CN": "此应用会把文件存放在你在下方选择的共享文件夹。DSM 不会自动让应用的套件账户访问此处所选的文件夹:安装后请前往“控制面板”>“共享文件夹”,编辑该文件夹,在“权限”>“系统内部用户”中授予套件账户 {account}“{permission}”权限。在此之前,应用会报告无法使用该文件夹。",
369
435
  ja: "このアプリは、以下で選択した共有フォルダにファイルを保存します。DSM は、ここで選択したフォルダへのアクセス権をアプリのパッケージアカウントに自動では付与しません。インストール後に [コントロールパネル] > [共有フォルダ] でそのフォルダを編集し、[権限] > [システム内部ユーザー] でパッケージアカウント {account} に「{permission}」権限を付与してください。付与するまで、アプリはこのフォルダを使用できないと報告します。",
370
- ko: "이 앱은 아래에서 선택한 공유 폴더에 파일을 저장합니다. DSM은 여기서 선택한 폴더에 대한 접근 권한을 앱의 패키지 계정에 자동으로 부여하지 않습니다. 설치 후 [제어판] > [공유 폴더]에서 해당 폴더를 편집하고 [권한] > [시스템 내부 사용자]에서 패키지 계정 {account}에 '{permission}' 권한을 부여하세요. 그때까지 앱은 이 폴더를 사용할 수 없다고 보고합니다.",
436
+ ko: "이 앱은 아래에서 선택한 공유 폴더에 파일을 저장합니다. DSM은 여기서 선택한 폴더에 대한 접근 권한을 앱의 패키지 계정에 자동으로 부여하지 않습니다. 설치 후 [제어판] > [공유 폴더]에서 해당 폴더를 편집하고 [권한] > [시스템 내부 사용자]에서 패키지 계정 {account}에 '{permission}' 권한을 부여하십시오. 그때까지 앱은 이 폴더를 사용할 수 없다고 보고합니다.",
371
437
  de: "Diese App speichert ihre Dateien in einem freigegebenen Ordner, den Sie unten auswählen. DSM gewährt dem Paketkonto der App keinen automatischen Zugriff auf hier gewählte Ordner: Öffnen Sie nach der Installation Systemsteuerung > Gemeinsamer Ordner, bearbeiten Sie den Ordner und geben Sie dem Paketkonto {account} unter Berechtigungen > Interner Systembenutzer die Berechtigung „{permission}“. Bis dahin meldet die App, dass sie den Ordner nicht verwenden kann.",
372
438
  fr: "Cette application enregistre ses fichiers dans un dossier partagé que vous sélectionnez ci-dessous. DSM n'accorde pas automatiquement au compte de paquet de l'application l'accès à un dossier choisi ici : après l'installation, ouvrez Panneau de configuration > Dossier partagé, modifiez le dossier et, sous Autorisations > Utilisateur système interne, accordez au compte de paquet {account} l'accès « {permission} ». D'ici là, l'application signale qu'elle ne peut pas utiliser le dossier.",
373
439
  es: "Esta aplicación guarda sus archivos en una carpeta compartida que seleccione a continuación. DSM no concede automáticamente a la cuenta de paquete de la aplicación acceso a una carpeta elegida aquí: después de instalar, abra Panel de control > Carpeta compartida, edite la carpeta y, en Permisos > Usuario interno del sistema, conceda a la cuenta de paquete {account} el acceso «{permission}». Hasta entonces, la aplicación informará de que no puede usar la carpeta.",
374
- it: "Questa app salva i propri file in una cartella condivisa selezionata qui sotto. DSM non concede automaticamente all'account del pacchetto dell'app l'accesso a una cartella scelta qui: dopo l'installazione apri Pannello di controllo > Cartella condivisa, modifica la cartella e, in Autorizzazioni > Utente interno del sistema, concedi all'account del pacchetto {account} l'accesso «{permission}». Fino ad allora l'app segnalerà di non poter usare la cartella.",
440
+ it: "Questa app salva i propri file in una cartella condivisa selezionata qui sotto. DSM non concede automaticamente all'account del pacchetto dell'app l'accesso a una cartella scelta qui: dopo l'installazione aprire Pannello di controllo > Cartella condivisa, modificare la cartella e, in Autorizzazioni > Utente interno del sistema, concedere all'account del pacchetto {account} l'accesso «{permission}». Fino ad allora l'app segnalerà di non poter usare la cartella.",
375
441
  },
376
442
  "nativeShare.createIntro": {
377
443
  en: "This app creates the Shared Folder {name} and gives its package account {permission} access to it. If {name} already exists, it is kept and only the package account's permission is set. Uninstalling the app never deletes this Shared Folder or the files in it.",
package/dist/cli.js CHANGED
@@ -9,11 +9,12 @@ import { formatPinReport, pinProjectImages } from "./images/pin.js";
9
9
  import { initFromTemplate, isTemplateName, listTemplates } from "./init.js";
10
10
  import { templatesRoot } from "./template-paths.js";
11
11
  import { addCapabilities } from "./add.js";
12
+ import { CAPABILITY_NAMES } from "./capabilities.js";
12
13
  import { validateProject } from "./validate.js";
13
14
  import { runDev } from "./dev.js";
14
15
  import { testProject } from "./test-command.js";
15
16
  import { buildSpk } from "./build-spk/generator.js";
16
- import { isNativeArchFamily, NATIVE_ARCH_FAMILIES, parseArchList, } from "./build-spk/dsm-arch.js";
17
+ import { CONTAINER_ARCH_FAMILIES, isContainerArchFamily, isNativeArchFamily, NATIVE_ARCH_FAMILIES, parseArchList, } from "./build-spk/dsm-arch.js";
17
18
  import { DEFAULT_MIN_DSM, CONTAINER_MIN_DSM } from "./build-spk/dsm-version.js";
18
19
  import { deployProject } from "./deploy/deploy.js";
19
20
  import { planDeployment, deployStandalone } from "./deploy/plan.js";
@@ -23,6 +24,7 @@ import { followProjectLogs, followStandaloneLogs } from "./log.js";
23
24
  import { formatSkillList, readSkill, resolveSkillsRoot } from "./skill.js";
24
25
  import { resolveInstallParameterExtraValues } from "./deploy/param-values.js";
25
26
  import { findStandaloneRoot, loadStandaloneManifest, } from "./standalone/manifest.js";
27
+ import { buildNativeLayout, isNativeLayout, loadNativeLayout, LAYOUT_ARCHES } from "./standalone/native-layout.js";
26
28
  import { buildStandalone } from "./standalone/build.js";
27
29
  import { inspectSpk } from "./standalone/inspect-spk.js";
28
30
  function failure(code, message, hint = "修正設定後重試;使用 --help 查看指令用法。") {
@@ -53,6 +55,9 @@ function formatSpkInspection(spk, report) {
53
55
  `arch:${report.arch ?? "?"} runtime:${report.kind ?? "?"}`,
54
56
  `簽署:${report.signed ? "有 Synology 簽章" : "未簽署(Package Center 需允許任何發行者)"}`,
55
57
  `CGI:${report.cgi ?? "無"}`,
58
+ ...(report.entry === "admin-only" ? ["入口:entry: admin-only,只有 DSM 管理員能開啟與呼叫任何 route(含 SDK routes)"] : []),
59
+ ...(report.entry === "app-privilege" ? ["入口:entry: app-privilege,由 DSM 控制台「應用程式權限」決定誰能使用"] : []),
60
+ ...(report.portal ? [`Application Portal:預設別名 /${report.portal.alias}/(${report.portal.appId}),由管理員套用後啟用`] : []),
56
61
  ...report.capabilities.map((cap) => `能力 ${cap.name}:${cap.requiresAdminApproval ? "需要 DSM 管理員核准" : "安裝後 ready"}`),
57
62
  "payload:",
58
63
  ...(report.uninstallCleanup ?? []).map(item => `Uninstall cleanup (if needed):${item.target} — ${item.condition} ${item.action}`),
@@ -69,12 +74,9 @@ async function standaloneBuild(root, arches, options) {
69
74
  : options.outDir
70
75
  ? resolve(options.outDir)
71
76
  : undefined;
72
- const result = await buildStandalone({
73
- root,
74
- arches,
75
- outDir,
76
- minDsm: options.minDsm,
77
- });
77
+ const result = isNativeLayout(root)
78
+ ? await buildNativeLayout({ root, arches, outDir, minDsm: options.minDsm })
79
+ : await buildStandalone({ root, arches: arches, outDir, minDsm: options.minDsm });
78
80
  return result.outputs.map((output) => {
79
81
  if (!options.output)
80
82
  return output;
@@ -88,6 +90,9 @@ const ARCH_REQUIRED = [
88
90
  "GATEWAY_ARCH_FAMILY_REQUIRED",
89
91
  ];
90
92
  async function containerBuild(project, arches, options) {
93
+ const unsupported = arches?.find((arch) => !isContainerArchFamily(arch));
94
+ if (unsupported)
95
+ throw failure("CONTAINER_ARCH_UNSUPPORTED", `Container Manager App 只支援 ${CONTAINER_ARCH_FAMILIES.join("、")};DSM 不為 ${unsupported} 提供 Container Manager。`);
91
96
  const output = options.output ? resolve(options.output) : undefined;
92
97
  const build = (archFamily, outputPath = output) => buildSpk({ project, archFamily, outputPath, minDsm: options.minDsm });
93
98
  let results = [];
@@ -101,7 +106,7 @@ async function containerBuild(project, arches, options) {
101
106
  if (output)
102
107
  throw failure("CLI_OUTPUT_ARCH_REQUIRED", "-o 只能搭配 --arch 建置單一架構。");
103
108
  const packageId = String(loadContract(project.contractPath).contract.package);
104
- for (const arch of arches ?? NATIVE_ARCH_FAMILIES)
109
+ for (const arch of arches ?? CONTAINER_ARCH_FAMILIES)
105
110
  results.push(await build(arch, join(options.outDir ? resolve(options.outDir) : project.root, `${packageId}-${arch}.spk`)));
106
111
  }
107
112
  }
@@ -141,12 +146,12 @@ export async function runCli(argv) {
141
146
  throw failure(validation.errors[0].code, formatErrors(validation.errors), validation.errors[0].suggestion);
142
147
  const standalone = standaloneRoot(rootOf(command));
143
148
  if (standalone) {
144
- const loaded = loadStandaloneManifest(standalone);
149
+ const loaded = isNativeLayout(standalone) ? loadNativeLayout(standalone) : loadStandaloneManifest(standalone);
145
150
  if (!loaded.ok)
146
151
  throw failure(loaded.errors[0].code, loaded.errors.map((e) => `[${e.code}] ${e.message}`).join("\n"), "將 dsm-package.yaml 更新為 schema: 2,修正宣告後重試。");
147
152
  emit("check", {
148
153
  package: loaded.manifest.package,
149
- capabilities: loaded.manifest.capabilities,
154
+ capabilities: "capabilities" in loaded.manifest ? loaded.manifest.capabilities : {},
150
155
  }, "驗證通過。");
151
156
  return;
152
157
  }
@@ -173,7 +178,8 @@ export async function runCli(argv) {
173
178
  const images = program.command("images").description("Container image 工具");
174
179
  images
175
180
  .command("pin")
176
- .option("--check", "只檢查,不寫入")
181
+ .description("把 compose.yaml 的 image tag 解析為 digest 並寫回(匿名 registry 查詢)")
182
+ .option("--check", "只檢查,不寫入;未 pin 或過期時失敗")
177
183
  .action(async (options, command) => {
178
184
  const project = requireProject(process.cwd(), command.parent?.parent?.opts().project);
179
185
  const result = await pinProjectImages({
@@ -191,7 +197,7 @@ export async function runCli(argv) {
191
197
  .option("--package <id>", "INFO.package(預設由目錄名產生)")
192
198
  .option("--name <display>", "顯示名稱")
193
199
  .option("--ui <vue|plain>", `native-go:UI,預設 vue(UI ${JSON.parse(readFileSync(join(templatesRoot(), "native-go-vue/frontend/package.json"), "utf8")).dependencies["@synopackageland/ui"]});plain 使用 ui/app.js`)
194
- .option("--with <caps>", "能力(逗號分隔):files,jobs,login,root-ops")
200
+ .option("--with <caps>", `能力(逗號分隔):${CAPABILITY_NAMES.join(",")}`)
195
201
  .option("--from <compose.yaml>", "compose:匯入現有 Compose;省略時建立範例")
196
202
  .option("--shared-folder <select|create>", "安裝精靈選擇或建立共用資料夾")
197
203
  .option("--shared-folder-name <name>", "建立的共用資料夾名稱")
@@ -232,7 +238,7 @@ export async function runCli(argv) {
232
238
  });
233
239
  program
234
240
  .command("add <caps...>")
235
- .description("加入能力與 SDK-owned overlay;不覆寫既有應用檔案")
241
+ .description(`加入能力(${CAPABILITY_NAMES.join("、")})或 portal 與 SDK-owned overlay;不覆寫既有應用檔案`)
236
242
  .action((caps, _options, command) => {
237
243
  const result = addCapabilities(rootOf(command), caps);
238
244
  stdout(`已新增:${result.added.join(", ") || "無"};已存在:${result.unchanged.join(", ") || "無"}`);
@@ -342,7 +348,7 @@ export async function runCli(argv) {
342
348
  });
343
349
  program
344
350
  .command("logs")
345
- .description("App log 與遮罩後的診斷資訊")
351
+ .description("狀態診斷:DSM 套件狀態與遮罩後的診斷資訊;不串流應用程式日誌")
346
352
  .requiredOption("--host <name>", "NAS profile")
347
353
  .option("--dev", "Development Package Identity")
348
354
  .action(async (options, command) => {
@@ -377,21 +383,27 @@ export async function runCli(argv) {
377
383
  .option("--json", "輸出單一 JSON 物件")
378
384
  .option("-o, --output <path>", "輸出 SPK,搭配單一 --arch")
379
385
  .option("--out-dir <dir>", "輸出目錄,原生專案預設 dist/")
380
- .option("--arch <families>", `架構,可用逗號分隔:${NATIVE_ARCH_FAMILIES.join(",")}`)
386
+ .option("--arch <families>", `架構,可用逗號分隔:${NATIVE_ARCH_FAMILIES.join(",")}(armv7 用於 native-go 與 binary,不用於 Container Manager App);native layout 另支援 armv8`)
381
387
  .option("--min-dsm <version>", "最低 DSM 版本,省略時使用 manifest 值")
382
388
  .action(async (options, command) => {
389
+ const root = rootOf(command);
390
+ const standalone = standaloneRoot(root);
383
391
  let arches;
384
392
  try {
385
393
  arches =
386
394
  options.arch === undefined
387
395
  ? undefined
388
- : parseArchList(String(options.arch));
396
+ : standalone && isNativeLayout(standalone)
397
+ ? String(options.arch).split(",").map((arch) => {
398
+ if (!Object.hasOwn(LAYOUT_ARCHES, arch))
399
+ throw new Error(`Native layout arch must be one of ${Object.keys(LAYOUT_ARCHES).join(", ")}`);
400
+ return arch;
401
+ })
402
+ : parseArchList(String(options.arch));
389
403
  }
390
404
  catch (error) {
391
405
  throw failure("CLI_ARCH_INVALID", error instanceof Error ? error.message : String(error));
392
406
  }
393
- const root = rootOf(command);
394
- const standalone = standaloneRoot(root);
395
407
  const outputs = standalone
396
408
  ? await standaloneBuild(standalone, arches, options)
397
409
  : await containerBuild(requireProject(root), arches, options);
@@ -1,3 +1,4 @@
1
+ import { validateMetadata } from "../package-metadata.js";
1
2
  import { Ajv } from "ajv";
2
3
  import { readFileSync } from "node:fs";
3
4
  import { dirname, join } from "node:path";
@@ -33,6 +34,13 @@ export function validateContractSchema(parsed, contractFile) {
33
34
  const data = parsed.doc.toJSON();
34
35
  const valid = validate(data);
35
36
  if (valid) {
37
+ try {
38
+ if (data.metadata !== undefined)
39
+ validateMetadata(data.metadata);
40
+ }
41
+ catch {
42
+ return [makeError("CONTRACT_SCHEMA_INVALID", contractSpan(contractFile, 1, 1, "metadata"), "Invalid metadata fields.", "Check email, URL and text bounds.")];
43
+ }
36
44
  return [];
37
45
  }
38
46
  const errors = validate.errors ?? [];
@@ -4,9 +4,11 @@ import { contractSpan, makeError } from "../errors.js";
4
4
  import { collectInstallParameters, getValuePosition, stringViolations, } from "./load.js";
5
5
  import { validateContractSchema } from "./schema-validate.js";
6
6
  import { DSM_GROUP_NAME, isWebAccessMode, isWebLaunchMode, readWebIntegration } from "./web-launch.js";
7
+ import { contractPortalSupported, readPortalDeclaration } from "../portal/alias.js";
7
8
  import { loadCompose } from "../compose/analyze.js";
8
9
  import { collectPosixAllSharesParameters, isPosixAllSharesType, validatePosixAllSharesSources, } from "./posix-all-shares.js";
9
10
  import { getErrorCode } from "../error-registry.js";
11
+ import { isDsmAtLeast, PORTAL_MIN_DSM, portalMinDsmMessage } from "../build-spk/dsm-version.js";
10
12
  import { FORMAL_PACKAGE_IDENTITY_MAX_LENGTH, PACKAGE_IDENTITY_MAX_LENGTH, } from "../deploy/identity-shared.js";
11
13
  /** DSM 7.4 Package Center rejects a longer package identity outright. */
12
14
  const DSM_PACKAGE_IDENTITY_MAX = 32;
@@ -53,6 +55,7 @@ export function validateContract(parsed, contractFile) {
53
55
  errors.push(...validatePosixAllShares(parsed, contractFile));
54
56
  errors.push(...validateWebLaunch(parsed, contractFile));
55
57
  errors.push(...validateWebAccess(parsed, contractFile));
58
+ errors.push(...validateWebPortal(parsed, contractFile));
56
59
  return errors;
57
60
  }
58
61
  function validatePosixAllShares(parsed, contractFile) {
@@ -119,6 +122,33 @@ function validateWebAccess(parsed, contractFile) {
119
122
  }
120
123
  return [];
121
124
  }
125
+ function validateWebPortal(parsed, contractFile) {
126
+ const { contract, doc } = parsed;
127
+ const portal = readWebIntegration(contract)?.portal;
128
+ if (portal === undefined) {
129
+ return [];
130
+ }
131
+ const pos = getValuePosition(doc, ["integration", "web", "portal"]) ?? { line: 1, column: 1 };
132
+ const at = contractSpan(contractFile, pos.line, pos.column, "integration.web.portal");
133
+ if (!contractPortalSupported(contract)) {
134
+ return [
135
+ makeError("CONTRACT_PORTAL_UNSUPPORTED", at, "Application Portal 只支援以 DSM 視窗開啟、經 DSM 登入閘道的網頁 App。", "請使用 adminport、launch: dsm-window 與非 public 的 access,或移除 integration.web.portal。public App 已有同名的 Web Station 網址。"),
136
+ ];
137
+ }
138
+ if (!readPortalDeclaration(portal, String(contract.package ?? ""))) {
139
+ return [
140
+ makeError("CONTRACT_PORTAL_INVALID", at, "integration.web.portal 必須是 {} 或 { alias },alias 為 1–64 個小寫英數字或 -,且不是 DSM 保留路徑。", "請省略 alias 以使用套件名稱,或改成例如 alias: my-app。"),
141
+ ];
142
+ }
143
+ const osMinVer = contract.os_min_ver;
144
+ if (typeof osMinVer === "string" && FULL_OS_MIN_VER.test(osMinVer) && !isDsmAtLeast(osMinVer, PORTAL_MIN_DSM)) {
145
+ return [
146
+ makeError("CONTRACT_PORTAL_MIN_DSM", at, portalMinDsmMessage(osMinVer, "integration.web.portal"), `請把 os_min_ver 改成 "${PORTAL_MIN_DSM}" 或更新,或移除 integration.web.portal。`),
147
+ ];
148
+ }
149
+ return [];
150
+ }
151
+ const FULL_OS_MIN_VER = /^7\.\d+(\.\d+)?-\d+$/;
122
152
  function validateDeveloperString(value, contractFile, path, pos) {
123
153
  const errors = [];
124
154
  const violations = stringViolations(value);
@@ -1,5 +1,5 @@
1
1
  import type { CommandRunner } from "../standalone/build.js";
2
- import type { NativeArchFamily } from "../build-spk/dsm-arch.js";
2
+ import { type NativeArchFamily } from "../build-spk/dsm-arch.js";
3
3
  import type { ProjectRoot } from "../discovery.js";
4
4
  import type { NasHost } from "../host/types.js";
5
5
  import { resolveDeploySpkVersion } from "./spk-version.js";
@@ -1,4 +1,5 @@
1
1
  import { buildSpk } from "../build-spk/generator.js";
2
+ import { CONTAINER_ARCH_FAMILIES, isContainerArchFamily } from "../build-spk/dsm-arch.js";
2
3
  import { loadContract } from "../contract/load.js";
3
4
  import { usesAuthGateway } from "../contract/web-launch.js";
4
5
  import { findUnsupportedInstallTypes } from "../contract/unsupported-types.js";
@@ -174,6 +175,13 @@ export async function deployProject(options) {
174
175
  };
175
176
  }
176
177
  }
178
+ if (archFamily !== undefined && !isContainerArchFamily(archFamily)) {
179
+ return {
180
+ ok: false,
181
+ code: "CONTAINER_ARCH_UNSUPPORTED",
182
+ message: `Container Manager App 只支援 ${CONTAINER_ARCH_FAMILIES.join("、")};DSM 不為 ${archFamily} 提供 Container Manager。`,
183
+ };
184
+ }
177
185
  const build = await buildSpk({
178
186
  project: options.project,
179
187
  packageIdentity: identity.packageId,
@@ -22,7 +22,7 @@ export function planDeployment(root, options = {}) {
22
22
  if (options.dev)
23
23
  throw fail("DEVELOPMENT_INSTANCE_UNSUPPORTED", "--dev is not supported for native-go/binary; choose a distinct package name at init");
24
24
  if (options.arch && !m.arches.includes(options.arch))
25
- throw fail("STANDALONE_ARCH_NOT_DECLARED", `Architecture not declared: ${options.arch}`);
25
+ throw fail("STANDALONE_ARCH_NOT_DECLARED", `Architecture not declared: ${options.arch}; add it to dsm-package.yaml arches to deploy it`);
26
26
  return {
27
27
  root: standalone,
28
28
  kind: m.backend.kind === "go" ? "native-go" : "binary",