@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
@@ -18,7 +18,7 @@ export function nextDeployVersion(version, installed) {
18
18
  break;
19
19
  }
20
20
  if (order < 0)
21
- throw new Error(`Refusing downgrade from ${installed} to ${version}`);
21
+ throw new Error(`[DEPLOY_VERSION_INVALID] Refusing downgrade from ${installed} to ${version}`);
22
22
  if (order === 0)
23
23
  build = Math.max(build, Number(previous[2] ?? 0) + 1);
24
24
  }
@@ -1,4 +1,9 @@
1
1
  export const ERROR_REGISTRY = {
2
+ "NATIVE_LAYOUT_INVALID": {
3
+ code: "NATIVE_LAYOUT_INVALID",
4
+ description: "native layout 的 manifest、hook、toolkit 或產物違反 package-only 契約。",
5
+ suggestion: "使用 schema 2 與 backend.kind: native;檢查路徑、ELF 連結與 checksum pin。"
6
+ },
2
7
  "COMPOSE_ROUTE_UNSUPPORTED": {
3
8
  "code": "COMPOSE_ROUTE_UNSUPPORTED",
4
9
  "description": "Compose 沒有可用的 DSM resource worker 執行路徑。",
@@ -54,6 +59,16 @@ export const ERROR_REGISTRY = {
54
59
  "description": "Headless App 不需要宣告 Web Launch Mode。",
55
60
  "suggestion": "請省略 integration.web.launch,或補上對應 Compose published port 的 adminport。"
56
61
  },
62
+ "CONTRACT_PORTAL_INVALID": {
63
+ "code": "CONTRACT_PORTAL_INVALID",
64
+ "description": "integration.web.portal 的格式或 alias 不正確。",
65
+ "suggestion": "請使用 {} 或 { alias: my-app };alias 為 1–64 個小寫英數字或 -,且不是 DSM 保留路徑。"
66
+ },
67
+ "CONTRACT_PORTAL_UNSUPPORTED": {
68
+ "code": "CONTRACT_PORTAL_UNSUPPORTED",
69
+ "description": "這個 App 的網頁入口無法加入 DSM Application Portal。",
70
+ "suggestion": "Application Portal 需要 adminport、launch: dsm-window 與非 public 的 access;否則請移除 integration.web.portal。"
71
+ },
57
72
  "CONTRACT_ACCESS_INVALID": {
58
73
  "code": "CONTRACT_ACCESS_INVALID",
59
74
  "description": "integration.web.access 模式無效。",
@@ -111,13 +126,13 @@ export const ERROR_REGISTRY = {
111
126
  },
112
127
  "CONTRACT_SECRET_UNSUPPORTED": {
113
128
  "code": "CONTRACT_SECRET_UNSUPPORTED",
114
- "description": "install.<NAME>.type: secret 目前沒有可用的安裝路徑。2026-08-27 於 vt-nas 實測:無論是否在 Compose 引用,Package Center 安裝都以 DSM broken/150 結束且不建立 Container Manager project(同一台 NAS 上無參數的對照套件正常安裝)。",
115
- "suggestion": "請改用 type: string(非敏感值),或把敏感值改成 App 啟動後自行取得/由管理員放進 ${SYNO_APP_SECRETS_DIR} 的檔案。secret 型別要能用,需要先讓 DSM worker 的失敗原因可觀測(見 STATUS.md A22/A45)。"
129
+ "description": "install.<NAME>.type: secret 目前沒有可用的安裝路徑:無論是否在 Compose 引用,Package Center 安裝都以 DSM broken/150 結束,且不建立 Container Manager project。",
130
+ "suggestion": "請改用 type: string(非敏感值),或把敏感值改成 App 啟動後自行取得/由管理員放進 ${SYNO_APP_SECRETS_DIR} 的檔案。在 DSM worker 的失敗原因可觀測之前,secret 型別不會開放。"
116
131
  },
117
132
  "CONTRACT_HOMES_UNSUPPORTED": {
118
133
  "code": "CONTRACT_HOMES_UNSUPPORTED",
119
- "description": "install.<NAME>.type: homes 目前沒有可用的安裝路徑。2026-08-27 於 vt-nas 實測:wizard 不產生輸入控制項,Package Center 安裝以 DSM broken/150 結束;CLI deploy 能解析出 /volume1/homes,但 DSM 於 compound install 回 4540 拒絕。",
120
- "suggestion": "若 App 需要一個由管理員選定的資料夾,請改用 type: shared_folder。homes 型別要能用,需要補齊 wizard 控制項與 preinst materialization(見 STATUS.md A46)。"
134
+ "description": "install.<NAME>.type: homes 目前沒有可用的安裝路徑:安裝精靈不產生輸入控制項,Package Center 安裝以 DSM broken/150 結束;CLI deploy 能解析出 /volume1/homes,但 DSM 於 compound install 回 4540 拒絕。",
135
+ "suggestion": "若 App 需要一個由管理員選定的資料夾,請改用 type: shared_folder。在補齊安裝精靈控制項與 preinst 路徑解析之前,homes 型別不會開放。"
121
136
  },
122
137
  "INSTALL_PARAMETER_UNREFERENCED": {
123
138
  "code": "INSTALL_PARAMETER_UNREFERENCED",
@@ -467,7 +482,7 @@ export const ERROR_REGISTRY = {
467
482
  "STANDALONE_ARCHES_INVALID": {
468
483
  "code": "STANDALONE_ARCHES_INVALID",
469
484
  "description": "arches 不合法。",
470
- "suggestion": "請列出不重複的 x86_64 或 aarch64,至少一個。"
485
+ "suggestion": "請列出不重複的 x86_64、aarch64 或 armv7,至少一個。"
471
486
  },
472
487
  "STANDALONE_ARCH_NOT_DECLARED": {
473
488
  "code": "STANDALONE_ARCH_NOT_DECLARED",
@@ -501,8 +516,8 @@ export const ERROR_REGISTRY = {
501
516
  },
502
517
  "STANDALONE_BINARY_NOT_ELF": {
503
518
  "code": "STANDALONE_BINARY_NOT_ELF",
504
- "description": "backend 執行檔不是 64-bit little-endian Linux ELF。",
505
- "suggestion": "請提供為 Linux 編譯的 x86_64 或 aarch64 執行檔。"
519
+ "description": "backend 執行檔不是該架構需要的 little-endian Linux ELF(x86_64、aarch64 為 64-bit,armv7 為 32-bit)。",
520
+ "suggestion": "請提供為 Linux 編譯的 x86_64、aarch64 或 armv7 執行檔。"
506
521
  },
507
522
  "STANDALONE_BINARY_ARCH_MISMATCH": {
508
523
  "code": "STANDALONE_BINARY_ARCH_MISMATCH",
@@ -569,6 +584,11 @@ export const ERROR_REGISTRY = {
569
584
  "description": "go build 失敗。",
570
585
  "suggestion": "請在 backend 目錄執行 go build ./... 並修正錯誤。"
571
586
  },
587
+ "STANDALONE_PORTAL_INVALID": {
588
+ "code": "STANDALONE_PORTAL_INVALID",
589
+ "description": "dsm-package.yaml 的 portal 格式或 alias 不正確。",
590
+ "suggestion": "請使用 portal: {} 或 portal: { alias: my-app };alias 為 1–64 個小寫英數字或 -,且不是 DSM 保留路徑。"
591
+ },
572
592
  "STANDALONE_I18N_INVALID": {
573
593
  "code": "STANDALONE_I18N_INVALID",
574
594
  "description": "dsm-package.yaml 的 i18n 設定不合法。",
@@ -624,6 +644,8 @@ for (const [code, description, suggestion] of [
624
644
  ERROR_REGISTRY[code] = { code, description, suggestion };
625
645
  ERROR_REGISTRY["ADD_LAYOUT_UNSUPPORTED"] = { "code": "ADD_LAYOUT_UNSUPPORTED", "description": "SDK overlay 需要標準 backend 目錄。", "suggestion": "使用 backend.dir: backend,或手動合併 SDK overlay。" };
626
646
  ERROR_REGISTRY["MIN_DSM_CONTAINER_FLOOR"] = { "code": "MIN_DSM_CONTAINER_FLOOR", "description": "Compose 最低 DSM 版本過低。", "suggestion": "Compose 需要 DSM 7.2.1-69057 或以上。" };
647
+ for (const code of ["STANDALONE_PORTAL_MIN_DSM", "CONTRACT_PORTAL_MIN_DSM"])
648
+ ERROR_REGISTRY[code] = { code, "description": "宣告 portal 的套件 os_min_ver 低於 DSM 7.3.2-86009。", "suggestion": "DSM 7.1.1 以 320 拒裝帶 portal 的未簽章套件、7.2 未驗證;把 os_min_ver 提高到 7.3.2-86009 以上,或移除 portal。" };
627
649
  ERROR_REGISTRY["MIN_DSM_INVALID"] = { "code": "MIN_DSM_INVALID", "description": "最低 DSM 版本格式不合法。", "suggestion": "指定 7.x release 或完整 7.x-build。" };
628
650
  ERROR_REGISTRY["NOTICE_UNSAFE"] = { "code": "NOTICE_UNSAFE", "description": "授權檔案不符合一般檔案限制。", "suggestion": "移除 symlink、特殊檔與超大授權檔案。" };
629
651
  ERROR_REGISTRY["NOTICE_MISSING"] = { "code": "NOTICE_MISSING", "description": "缺少第三方授權資訊。", "suggestion": "重新安裝模板依賴並產生 third-party notices。" };
@@ -636,10 +658,9 @@ ERROR_REGISTRY["SPK_INSPECTION_FAILED"] = { "code": "SPK_INSPECTION_FAILED", "de
636
658
  ERROR_REGISTRY["IMAGE_PIN_REQUIRED"] = { "code": "IMAGE_PIN_REQUIRED", "description": "映像 digest 尚未固定。", "suggestion": "執行 images pin。" };
637
659
  ERROR_REGISTRY["INIT_TEMPLATE_REQUIRED"] = { "code": "INIT_TEMPLATE_REQUIRED", "description": "未指定模板。", "suggestion": "使用 -t native-go、binary 或 compose。" };
638
660
  ERROR_REGISTRY["INIT_TEMPLATE_UNKNOWN"] = { "code": "INIT_TEMPLATE_UNKNOWN", "description": "未知模板。", "suggestion": "使用 -t native-go、binary 或 compose。" };
639
- ERROR_REGISTRY["CLI_ARCH_INVALID"] = { "code": "CLI_ARCH_INVALID", "description": "架構選項不合法。", "suggestion": "使用 x86_64 或 aarch64。" };
640
- ERROR_REGISTRY["NATIVE_ARCH_UNDETERMINED"] = { "code": "NATIVE_ARCH_UNDETERMINED", "description": "無法判定 NAS 架構。", "suggestion": "指定 --arch x86_64 或 aarch64。" };
661
+ ERROR_REGISTRY["CLI_ARCH_INVALID"] = { "code": "CLI_ARCH_INVALID", "description": "架構選項不合法。", "suggestion": "使用 x86_64、aarch64 或 armv7(armv7 用於 native-go 與 binary,不用於 Container Manager App)。" };
662
+ ERROR_REGISTRY["NATIVE_ARCH_UNDETERMINED"] = { "code": "NATIVE_ARCH_UNDETERMINED", "description": "無法判定 NAS 架構。", "suggestion": "指定 --arch x86_64、aarch64 或 armv7。" };
641
663
  ERROR_REGISTRY["DEVELOPMENT_INSTANCE_UNSUPPORTED"] = { "code": "DEVELOPMENT_INSTANCE_UNSUPPORTED", "description": "原生專案不支援 --dev。", "suggestion": "init 時指定不同 package ID。" };
642
- ERROR_REGISTRY["INIT_TEMPLATE_INVALID"] = { "code": "INIT_TEMPLATE_INVALID", "description": "未知模板。", "suggestion": "使用 -t native-go、binary 或 compose。" };
643
664
  ERROR_REGISTRY["INIT_OPTIONS_UNSUPPORTED"] = { "code": "INIT_OPTIONS_UNSUPPORTED", "description": "選項不適用於這個模板。", "suggestion": "--from 僅 compose;--ui 僅 native-go。" };
644
665
  ERROR_REGISTRY["INIT_TARGET_NOT_EMPTY"] = { "code": "INIT_TARGET_NOT_EMPTY", "description": "init 目錄已有衝突檔案。", "suggestion": "使用空目錄;既有專案新增能力使用 add。" };
645
666
  ERROR_REGISTRY["INIT_COMPOSE_INVALID"] = { "code": "INIT_COMPOSE_INVALID", "description": "匯入的 Compose 不合法。", "suggestion": "--from 指向有 services 的 YAML 檔案。" };
@@ -657,10 +678,22 @@ ERROR_REGISTRY["CLI_OPTION_ARGUMENT_MISSING"] = { "code": "CLI_OPTION_ARGUMENT_M
657
678
  ERROR_REGISTRY["CLI_COMMAND_UNKNOWN"] = { "code": "CLI_COMMAND_UNKNOWN", "description": "未知指令。", "suggestion": "使用 synopkgland --help 查看指令。" };
658
679
  ERROR_REGISTRY["COMMAND_FAILED"] = { "code": "COMMAND_FAILED", "description": "指令執行失敗。", "suggestion": "查看 message 與 stderr,修正問題後重試。" };
659
680
  ERROR_REGISTRY["DEPLOY_PLAN_INVALID"] = { "code": "DEPLOY_PLAN_INVALID", "description": "部署計畫與 runtime 不符。", "suggestion": "重新 check 並產生部署計畫。" };
660
- ERROR_REGISTRY["DEPLOY_ARCH_UNDETERMINED"] = { "code": "DEPLOY_ARCH_UNDETERMINED", "description": "無法判定 NAS 架構。", "suggestion": "使用 --arch x86_64 或 aarch64。" };
681
+ ERROR_REGISTRY["DEPLOY_ARCH_UNDETERMINED"] = { "code": "DEPLOY_ARCH_UNDETERMINED", "description": "無法判定 NAS 架構。", "suggestion": "使用 --arch x86_64、aarch64 或 armv7。" };
682
+ ERROR_REGISTRY["CONTAINER_ARCH_UNSUPPORTED"] = { "code": "CONTAINER_ARCH_UNSUPPORTED", "description": "Container Manager App 只支援 x86_64 與 aarch64;DSM 不為 armv7 提供 Container Manager。", "suggestion": "改用 --arch x86_64 或 aarch64,或改用 native-go 模板。" };
661
683
  ERROR_REGISTRY["DEPLOY_BUILD_OUTPUT_INVALID"] = { "code": "DEPLOY_BUILD_OUTPUT_INVALID", "description": "部署建置輸出數量不符。", "suggestion": "指定單一架構並重新 build。" };
662
684
  export function getErrorCode(code) {
663
685
  return ERROR_REGISTRY[code];
664
686
  }
665
- for (const code of ["EXECUTOR_BUILD_FAILED", "EXECUTOR_PIN_REQUIRED", "EXECUTOR_POLICY", "EXECUTOR_PACKAGE", "EXECUTOR_DIGEST", "EXECUTOR_TASK_PERCENT", "EXECUTOR_TASK_LENGTH"])
666
- ERROR_REGISTRY[code] = { code, description: "Executor build or pinned task definition is invalid.", suggestion: "Check manifest capabilities and executor compiler output; rebuild before administrator approval." };
687
+ for (const [code, description, suggestion] of [
688
+ ["EXECUTOR_BUILD_FAILED", "executor 或 lifecycle helper 編譯失敗,或 CLI 套件缺少 executor 原始碼。", "確認已安裝 Go,查看訊息中的編譯器輸出;原始碼缺少時重新安裝 CLI。"],
689
+ ["EXECUTOR_PIN_REQUIRED", "宣告 rootOps 或 delegate 的套件建置時沒有 executor 產物。", "使用 synopkgland build,讓 CLI 編譯並 pin executor;不要直接呼叫低階 SPK API。"],
690
+ ["EXECUTOR_POLICY", "capabilities.delegate 的 operations 或 scope 不合法,無法產生 executor policy。", "改用 init/add 產生的 delegate 宣告,或依 manifest 參考修正 scope 與 operations。"],
691
+ ["EXECUTOR_PACKAGE", "pin 進 root task 的套件名稱不合法。", "package 必須以英文字母開頭,只含英數字(2–64 字)。"],
692
+ ["EXECUTOR_DIGEST", "executor 或 policy 的 sha256 pin 格式不合法。", "重新 build,讓 CLI 重新計算 pin;不要手改 SPK 內的 pin。"],
693
+ ["EXECUTOR_TASK_PERCENT", "root task 腳本含有 %,DSM 工作排程器無法保存。", "從 root/ 腳本移除 % 字元(例如 date 格式改用其他寫法)。"],
694
+ ["EXECUTOR_TASK_LENGTH", "root task 腳本超過 DSM 工作排程器的長度上限。", "縮短 root/ 腳本,把邏輯移到套件內的檔案再由腳本呼叫。"],
695
+ ])
696
+ ERROR_REGISTRY[code] = { code, description, suggestion };
697
+ ERROR_REGISTRY["STANDALONE_ENTRY_INVALID"] = { "code": "STANDALONE_ENTRY_INVALID", "description": "entry 設定不合法。", "suggestion": "entry 填 admin-only 或 app-privilege;app-privilege 需要至少一個 authenticated route。" };
698
+ ERROR_REGISTRY["STANDALONE_ENTRY_WIDENED"] = { "code": "STANDALONE_ENTRY_WIDENED", "description": "entry: admin-only 與放寬權限的 route 設定衝突。", "suggestion": "把 permissions.json 中 authenticated 或 public 的 route 改為 administrators;要開放給一般使用者請改用 entry: app-privilege 或省略 entry。" };
699
+ ERROR_REGISTRY["STANDALONE_ENTRY_UNSUPPORTED"] = { "code": "STANDALONE_ENTRY_UNSUPPORTED", "description": "backend 無法強制 entry 模式。", "suggestion": "使用 Go backend,複製 SDK 的 internal/dsm entry gate 並在 cmd/app/main.go 呼叫 dsm.NewEntryGate。" };
@@ -1,4 +1,5 @@
1
1
  import type { HostCredentials, NasHost } from "./types.js";
2
+ import type { NativeArchFamily } from "../build-spk/dsm-arch.js";
2
3
  export interface CreateNasHostOptions {
3
4
  host?: string;
4
5
  credentials?: HostCredentials;
@@ -17,9 +18,12 @@ declare function synoagentcli(args: string[], profileName?: string): {
17
18
  export declare function homesPathFromDsmUserHomeRead(result: Pick<ReturnType<typeof synoagentcli>, "status" | "stdout">): string | undefined;
18
19
  /**
19
20
  * DSM reports the CPU vendor, not the ISA. Every x86_64 platform Synology
20
- * ships is Intel or AMD; every aarch64 one is Realtek, Marvell, Annapurna or
21
- * Rockchip. An unrecognised vendor returns undefined so `deploy` asks for
22
- * `--arch` rather than guessing wrong and building an unusable SPK.
21
+ * ships is Intel or AMD; the aarch64 ones are Realtek (rtd1296, rtd1619b) and
22
+ * Rockchip; Annapurna Labs is DSM 7's alpine/alpine4k, which are armv7 (DS416
23
+ * reports "ANNAPURNALABS"). Marvell makes both armada37xx (aarch64) and
24
+ * armada38x (armv7), so it, like an unrecognised vendor, returns undefined and
25
+ * `deploy` asks for `--arch` rather than guessing wrong and building an
26
+ * unusable SPK.
23
27
  */
24
- export declare function archFamilyForCpuVendor(vendor: string | undefined): "x86_64" | "aarch64" | undefined;
28
+ export declare function archFamilyForCpuVendor(vendor: string | undefined): NativeArchFamily | undefined;
25
29
  export {};
@@ -275,17 +275,23 @@ function createDsmNasHost(credentials, profileName) {
275
275
  }
276
276
  /**
277
277
  * DSM reports the CPU vendor, not the ISA. Every x86_64 platform Synology
278
- * ships is Intel or AMD; every aarch64 one is Realtek, Marvell, Annapurna or
279
- * Rockchip. An unrecognised vendor returns undefined so `deploy` asks for
280
- * `--arch` rather than guessing wrong and building an unusable SPK.
278
+ * ships is Intel or AMD; the aarch64 ones are Realtek (rtd1296, rtd1619b) and
279
+ * Rockchip; Annapurna Labs is DSM 7's alpine/alpine4k, which are armv7 (DS416
280
+ * reports "ANNAPURNALABS"). Marvell makes both armada37xx (aarch64) and
281
+ * armada38x (armv7), so it, like an unrecognised vendor, returns undefined and
282
+ * `deploy` asks for `--arch` rather than guessing wrong and building an
283
+ * unusable SPK.
281
284
  */
282
285
  export function archFamilyForCpuVendor(vendor) {
283
286
  const normalized = (vendor ?? "").trim().toLowerCase();
284
287
  if (normalized === "intel" || normalized === "amd") {
285
288
  return "x86_64";
286
289
  }
287
- if (["realtek", "marvell", "annapurna labs", "annapurna", "rockchip"].includes(normalized)) {
290
+ if (["realtek", "rockchip"].includes(normalized)) {
288
291
  return "aarch64";
289
292
  }
293
+ if (["annapurnalabs", "annapurna labs", "annapurna"].includes(normalized)) {
294
+ return "armv7";
295
+ }
290
296
  return undefined;
291
297
  }
@@ -2,6 +2,7 @@ import type { SharePermissionListing } from "../deploy/share-permission-prefligh
2
2
  import type { InstalledPackageState } from "./catalog-install.js";
3
3
  import type { PortInfoEntry } from "./port-info.js";
4
4
  import type { DsmPortProfileInventory } from "./dsm-port-profile.js";
5
+ import type { NativeArchFamily } from "../build-spk/dsm-arch.js";
5
6
  export interface DeployIdentity {
6
7
  packageId: string;
7
8
  displayname: string;
@@ -33,7 +34,7 @@ export interface NasHost {
33
34
  executor_digest: string;
34
35
  policy_digest: string;
35
36
  } | undefined>;
36
- getArchFamily?(): Promise<"x86_64" | "aarch64" | undefined>;
37
+ getArchFamily?(): Promise<NativeArchFamily | undefined>;
37
38
  /** List the current logical Shared Folder names using an administrator session. */
38
39
  listShareNames?(): Promise<string[]>;
39
40
  resolveSharePath?(logicalName: string): Promise<string | undefined>;
package/dist/info.js CHANGED
@@ -5,7 +5,8 @@ import { appOriginFromContract } from "./host/open-url.js";
5
5
  import { resolveDeployIdentity } from "./deploy/identity.js";
6
6
  import { loadStandaloneManifest, } from "./standalone/manifest.js";
7
7
  import { inspectSpk } from "./standalone/inspect-spk.js";
8
- import { executorPolicy } from "./runtime/executor-policy.js";
8
+ import { executorPolicy, executorPolicyText, sha256 } from "./runtime/executor-policy.js";
9
+ import { executorTasks } from "./runtime/executor-tasks.js";
9
10
  import { uninstallCleanup } from "./runtime/uninstall-cleanup.js";
10
11
  export async function projectInfo(options) {
11
12
  const identity = resolveDeployIdentity(options.project, { dev: options.dev });
@@ -63,6 +64,21 @@ export function classifyRootTasks(expected, inventory) {
63
64
  ? result("needs-admin-approval", "Approve missing or outdated root tasks in the DSM package UI.")
64
65
  : result("ready", "Boot and control tasks match the executor and policy digests. Executor health must be checked in the App window.");
65
66
  }
67
+ async function liveRootCapability(m, host) {
68
+ try {
69
+ const policyDigest = sha256(executorPolicyText(executorPolicy(m)));
70
+ const classified = classifyRootTasks(executorTasks({ package: m.package, policyDigest }), await host.getRootTaskInventory?.(m.package));
71
+ if (classified.state !== "ready")
72
+ return classified;
73
+ const observed = await host.getExecutorStatus?.(m.package);
74
+ if (observed?.ready !== true || observed.policy_digest !== policyDigest)
75
+ return { ...classified, state: "blocked", reason: "Executor health or policy digest readback could not be verified." };
76
+ return { ...classified, reason: "Root tasks and the running executor match the source policy (no local SPK, so executor bytes were not compared)." };
77
+ }
78
+ catch {
79
+ return { name: "rootOps", state: "blocked", reason: "DSM executor or root task readback could not be verified." };
80
+ }
81
+ }
66
82
  async function rootCapability(m, host, version) {
67
83
  const blocked = (reason) => ({
68
84
  name: "rootOps",
@@ -82,7 +98,7 @@ async function rootCapability(m, host, version) {
82
98
  return blocked("NAS architecture cannot be matched to the local executor SPK artifact.");
83
99
  const spk = join(m.root, "dist", `${m.package}-${version}-${arch}.spk`);
84
100
  if (!existsSync(spk))
85
- return blocked("Built executor SPK artifact is unavailable; build this installed version first.");
101
+ return liveRootCapability(m, host);
86
102
  try {
87
103
  const inspected = await inspectSpk(spk);
88
104
  if (inspected.findings.length ||
package/dist/init.d.ts CHANGED
@@ -1,8 +1,11 @@
1
+ import { type CapabilityName } from "./capabilities.js";
1
2
  import type { TemplateName } from "./types.js";
2
3
  /** The container templates need DSM 7.2.1 or later; native-go and binary need 7.1. */
3
4
  export declare function isContainerTemplate(template: TemplateName): boolean;
4
5
  export declare function isTemplateName(value: string): value is TemplateName;
5
6
  export declare function listTemplates(): TemplateName[];
7
+ /** New native scaffolds opt in explicitly; existing manifests are never migrated. */
8
+ export declare function nativeCapabilitySettings(names: readonly CapabilityName[]): Record<string, unknown>;
6
9
  export interface InitOptions {
7
10
  /** os_min_ver of the new project: a release (7.2) or a full 7.x-build; default is the template's (DSM 7.1 for native-go/binary, DSM 7.2.1 for container templates). */
8
11
  minDsm?: string;
package/dist/init.js CHANGED
@@ -1,6 +1,7 @@
1
1
  import { spawnSync } from "node:child_process";
2
- import { parse, stringify } from "yaml";
2
+ import { isMap, isScalar, parse, parseDocument, stringify } from "yaml";
3
3
  import { capabilityNames, capabilitySettings } from "./capabilities.js";
4
+ import { MOVE_FROM_STAGING } from './runtime/delegate-contract.js';
4
5
  import { cpSync, existsSync, mkdirSync, readdirSync, readFileSync, renameSync, rmSync, statSync, writeFileSync } from "node:fs";
5
6
  import { basename, join, relative } from "node:path";
6
7
  import { templatesRoot } from "./template-paths.js";
@@ -25,6 +26,15 @@ export function isTemplateName(value) {
25
26
  export function listTemplates() {
26
27
  return [...TEMPLATE_NAMES];
27
28
  }
29
+ /** New native scaffolds opt in explicitly; existing manifests are never migrated. */
30
+ export function nativeCapabilitySettings(names) {
31
+ const settings = capabilitySettings(names);
32
+ if (names.includes('delegate'))
33
+ settings.delegate = {
34
+ scope: 'install-selected', operations: [MOVE_FROM_STAGING], destinations: ['save'], lifetime: 'until-revoked',
35
+ };
36
+ return settings;
37
+ }
28
38
  /**
29
39
  * native-go --with root-ops: files laid over the native-go template, and the
30
40
  * files they replace.
@@ -85,10 +95,12 @@ function isTemplateBuildArtifact(path) {
85
95
  }
86
96
  export function initFromTemplate(template, targetDir, options = {}) {
87
97
  if (!isTemplateName(template))
88
- throw new Error(`[INIT_TEMPLATE_INVALID] 未知模板:${template}`);
98
+ throw new Error(`[INIT_TEMPLATE_UNKNOWN] 未知模板:${template}`);
89
99
  const caps = capabilityNames(options.with ?? []);
90
100
  if (caps.includes("login") && resolvedUi(template, options) === "plain")
91
101
  throw new Error("[CAPABILITY_COMBINATION_INVALID] login 需要獨立登入頁;plain UI 不支援,請改用 --ui vue。");
102
+ if (caps.includes("delegate") && resolvedUi(template, options) === "plain")
103
+ throw new Error("[CAPABILITY_COMBINATION_INVALID] delegate 的 install-selected scope 只能在 Vue 面板確認;plain UI 不支援,請改用 --ui vue。");
92
104
  if (options.from && template !== "compose")
93
105
  throw new Error("[INIT_OPTIONS_UNSUPPORTED] --from 只適用於 compose。");
94
106
  if (caps.length && template !== "native-go")
@@ -194,20 +206,11 @@ export function initFromTemplate(template, targetDir, options = {}) {
194
206
  if (template !== "compose") {
195
207
  const path = join(targetDir, "dsm-package.yaml");
196
208
  const raw = parse(readFileSync(path, "utf8"));
197
- raw.capabilities = capabilitySettings(caps);
198
- writeFileSync(path, stringify(raw));
199
- }
200
- if (template === "compose" && options.from) {
201
- const imported = readFileSync(options.from, "utf8");
202
- const compose = parse(imported);
203
- if (!compose || typeof compose !== "object" || !compose.services)
204
- throw new Error("[INIT_COMPOSE_INVALID] --from 需要包含 services 的 Compose YAML。");
205
- writeFileSync(join(targetDir, "compose.yaml"), imported);
206
- const path = join(targetDir, "synology-app.yaml");
207
- const raw = parse(readFileSync(path, "utf8"));
208
- delete raw.install;
209
+ raw.capabilities = nativeCapabilitySettings(caps);
209
210
  writeFileSync(path, stringify(raw));
210
211
  }
212
+ if (template === "compose" && options.from)
213
+ importCompose(options.from, targetDir, options);
211
214
  if (template === "binary") {
212
215
  mkdirSync(join(targetDir, "prebuilt"), { recursive: true });
213
216
  for (const [arch, goarch] of [["x86_64", "amd64"], ["aarch64", "arm64"]]) {
@@ -221,6 +224,35 @@ export function initFromTemplate(template, targetDir, options = {}) {
221
224
  writeFileSync(manifest, readFileSync(manifest, "utf8") + sharedFolderYaml(options.sharedFolder));
222
225
  }
223
226
  }
227
+ /**
228
+ * --from replaces the example Compose. The manifest takes the requested (or
229
+ * directory-derived) identity and keeps only the example install entries the
230
+ * imported Compose references as `${KEY}` (or `${KEY_UID}`/`${KEY_GID}`).
231
+ */
232
+ function importCompose(from, targetDir, options) {
233
+ const imported = readFileSync(from, "utf8");
234
+ const compose = parse(imported);
235
+ if (!compose || typeof compose !== "object" || !compose.services)
236
+ throw new Error("[INIT_COMPOSE_INVALID] --from 需要包含 services 的 Compose YAML。");
237
+ writeFileSync(join(targetDir, "compose.yaml"), imported);
238
+ const path = join(targetDir, "synology-app.yaml");
239
+ const doc = parseDocument(readFileSync(path, "utf8"));
240
+ doc.set("package", options.packageId ?? defaultPackageId(targetDir));
241
+ doc.set("displayname", validDisplayName(options.displayName ?? defaultDisplayName(targetDir)));
242
+ const install = doc.get("install");
243
+ const referenced = new Set([...imported.matchAll(/\$\{([A-Z][A-Z0-9_]*?)(?:_UID|_GID)?\}/g)].map(match => match[1]));
244
+ if (isMap(install))
245
+ install.items = install.items.filter(item => referenced.has(String(isScalar(item.key) ? item.key.value : item.key)));
246
+ if (!isMap(install) || install.items.length === 0)
247
+ doc.delete("install");
248
+ writeFileSync(path, String(doc));
249
+ }
250
+ function validDisplayName(displayName) {
251
+ if (displayName.trim() === "" || /[\r\n"\\]/.test(displayName)) {
252
+ throw new Error("[INIT_DISPLAY_NAME_INVALID] 顯示名稱必須是單行,且不含 \" 或 \\。");
253
+ }
254
+ return displayName;
255
+ }
224
256
  /** The `install.sharedFolder` block, validated by the same reader build-spk uses. */
225
257
  function sharedFolderYaml(options) {
226
258
  const folder = { mode: options.mode, key: options.key ?? "shared", ...(options.name === undefined ? {} : { name: options.name }), permission: options.permission ?? "rw" };
@@ -243,15 +275,14 @@ function substitutionsFor(template, targetDir, options) {
243
275
  if (options.ui !== undefined && !["plain", "vue"].includes(options.ui))
244
276
  throw new Error("[INIT_UI_INVALID] --ui 只接受 plain 或 vue。");
245
277
  if (!SUBSTITUTED[template]) {
278
+ if (options.from && options.displayName !== undefined)
279
+ validDisplayName(options.displayName);
246
280
  return {};
247
281
  }
248
282
  const packageId = options.packageId ?? defaultPackageId(targetDir);
249
283
  if (!PACKAGE_ID.test(packageId)) {
250
284
  throw new Error(`[INIT_PACKAGE_INVALID] package「${packageId}」必須以英文字母開頭,只含英數字(2–64 字),例如 PhotoTagger。`);
251
285
  }
252
- const displayName = options.displayName ?? defaultDisplayName(targetDir);
253
- if (displayName.trim() === "" || /[\r\n"\\]/.test(displayName)) {
254
- throw new Error("[INIT_DISPLAY_NAME_INVALID] 顯示名稱必須是單行,且不含 \" 或 \\。");
255
- }
286
+ const displayName = validDisplayName(options.displayName ?? defaultDisplayName(targetDir));
256
287
  return { __PACKAGE__: packageId, __DISPLAY_NAME__: displayName, __BACKGROUND_ENABLED__: caps.includes("jobs") || caps.includes("delegate") ? "true" : "false" };
257
288
  }
@@ -0,0 +1,21 @@
1
+ /** Plain text, or a map from a BCP-47 tag (en, zh-TW, ...) to plain text. */
2
+ export type LocalizedText = string | Record<string, string>;
3
+ export interface PackageMetadata {
4
+ long_description?: LocalizedText;
5
+ changelog?: LocalizedText;
6
+ contact_email?: string;
7
+ support_email?: string;
8
+ homepage?: string;
9
+ source_url?: string;
10
+ issues_url?: string;
11
+ license?: string;
12
+ maintainer_url?: string;
13
+ distributor?: string;
14
+ distributor_url?: string;
15
+ support_url?: string;
16
+ helpurl?: string;
17
+ screenshots?: string[];
18
+ }
19
+ export declare function safeMetadataUrl(value: unknown, mail?: boolean): value is string;
20
+ export declare function validateMetadata(raw: unknown): PackageMetadata;
21
+ export declare function metadataInfoFields(metadata?: PackageMetadata): [string, string][];
@@ -0,0 +1,101 @@
1
+ export function safeMetadataUrl(value, mail = false) {
2
+ if (typeof value !== "string" || value.length > 2048)
3
+ return false;
4
+ try {
5
+ const u = new URL(value);
6
+ return (!u.username &&
7
+ !u.password &&
8
+ (["https:", "http:"].includes(u.protocol) ||
9
+ (mail &&
10
+ u.protocol === "mailto:" &&
11
+ /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(u.pathname))));
12
+ }
13
+ catch {
14
+ return false;
15
+ }
16
+ }
17
+ export function validateMetadata(raw) {
18
+ if (!raw || typeof raw !== "object" || Array.isArray(raw))
19
+ throw new Error("invalid metadata");
20
+ const allowed = [
21
+ "long_description",
22
+ "changelog",
23
+ "contact_email",
24
+ "support_email",
25
+ "homepage",
26
+ "source_url",
27
+ "issues_url",
28
+ "license",
29
+ "maintainer_url",
30
+ "distributor",
31
+ "distributor_url",
32
+ "support_url",
33
+ "helpurl",
34
+ "screenshots",
35
+ ];
36
+ for (const [k, v] of Object.entries(raw)) {
37
+ if (!allowed.includes(k))
38
+ throw new Error("unknown metadata");
39
+ if (k === "screenshots") {
40
+ if (!Array.isArray(v) ||
41
+ v.length > 12 ||
42
+ !v.every((u) => safeMetadataUrl(u)))
43
+ throw new Error("invalid screenshots");
44
+ }
45
+ else if ((k === "long_description" || k === "changelog") &&
46
+ typeof v === "object" &&
47
+ v &&
48
+ !Array.isArray(v)) {
49
+ if (Object.entries(v).length > 32 ||
50
+ !Object.entries(v).every(([lang, t]) => /^[a-z]{2}(?:-[A-Za-z]{2})?$/.test(lang) &&
51
+ typeof t === "string" &&
52
+ t.length <= 16000 &&
53
+ !/[\x00-\x08\x0b\x0c\x0e-\x1f]/.test(t)))
54
+ throw new Error("invalid localized description");
55
+ }
56
+ else if (typeof v !== "string" ||
57
+ v.length > 16000 ||
58
+ /[\x00-\x08\x0b\x0c\x0e-\x1f]/.test(v))
59
+ throw new Error("invalid text");
60
+ if (k.endsWith("_email") &&
61
+ (typeof v !== "string" || !/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(v)))
62
+ throw new Error("invalid email");
63
+ if ([
64
+ "homepage",
65
+ "source_url",
66
+ "issues_url",
67
+ "maintainer_url",
68
+ "distributor_url",
69
+ "support_url",
70
+ "helpurl",
71
+ ].includes(k) &&
72
+ !safeMetadataUrl(v, k === "support_url"))
73
+ throw new Error("invalid metadata URL");
74
+ }
75
+ if (JSON.stringify(raw).length > 32000)
76
+ throw new Error("metadata too large");
77
+ return raw;
78
+ }
79
+ export function metadataInfoFields(metadata) {
80
+ if (!metadata)
81
+ return [];
82
+ const m = validateMetadata(metadata), fields = [];
83
+ // INFO has one changelog; a per-language map contributes its English text.
84
+ const changelog = typeof m.changelog === "object"
85
+ ? (m.changelog.en ?? Object.values(m.changelog)[0])
86
+ : m.changelog;
87
+ for (const [k, v] of [
88
+ ["maintainer_url", m.maintainer_url],
89
+ ["distributor", m.distributor],
90
+ ["distributor_url", m.distributor_url],
91
+ ["support_url", m.support_url],
92
+ ["helpurl", m.helpurl],
93
+ ["changelog", changelog],
94
+ ])
95
+ if (v)
96
+ fields.push([k, v.replace(/\r?\n/g, " · ")]);
97
+ if (!m.support_url && m.support_email)
98
+ fields.push(["support_url", "mailto:" + m.support_email]);
99
+ fields.push(["spk_land_metadata", JSON.stringify(m)]);
100
+ return fields;
101
+ }
@@ -0,0 +1,25 @@
1
+ import type { AppContract } from "../types.js";
2
+ /**
3
+ * DSM Application Portal alias: `https://<nas>/<alias>/` opens the App after
4
+ * DSM sign-in. DSM itself only refuses empty aliases (4100), synoman
5
+ * directory names (4102) and aliases another App uses (4103); the SDK keeps a
6
+ * stricter, URL-safe rule so the same value works in links and shell scripts.
7
+ */
8
+ export declare const PORTAL_ALIAS_PATTERN: RegExp;
9
+ export declare const PORTAL_ALIAS_MAX = 64;
10
+ /**
11
+ * Names DSM already serves: /usr/syno/synoman entries and the default aliases
12
+ * of Synology Apps, as observed on DSM 7.4-90069. DSM stays the authority.
13
+ */
14
+ export declare const RESERVED_PORTAL_ALIASES: readonly string[];
15
+ export interface PortalDeclaration {
16
+ alias: string;
17
+ }
18
+ export declare function isPortalAlias(value: unknown): value is string;
19
+ /** The package name as a slug; a reserved or too long name falls back to a stable hash. */
20
+ export declare function defaultPortalAlias(packageId: string): string;
21
+ /** `{}` or `{alias}`; undefined for any other shape. */
22
+ export declare function readPortalDeclaration(value: unknown, packageId: string): PortalDeclaration | undefined;
23
+ /** A Compose App reaches the portal only as a DSM window behind the DSM-authenticated gateway. */
24
+ export declare function contractPortalSupported(contract: AppContract): boolean;
25
+ export declare function resolvedContractPortal(contract: AppContract): PortalDeclaration | undefined;
@@ -0,0 +1,48 @@
1
+ import { createHash } from "node:crypto";
2
+ import { readWebIntegration, resolvedWebLaunch, usesAuthGateway } from "../contract/web-launch.js";
3
+ /**
4
+ * DSM Application Portal alias: `https://<nas>/<alias>/` opens the App after
5
+ * DSM sign-in. DSM itself only refuses empty aliases (4100), synoman
6
+ * directory names (4102) and aliases another App uses (4103); the SDK keeps a
7
+ * stricter, URL-safe rule so the same value works in links and shell scripts.
8
+ */
9
+ export const PORTAL_ALIAS_PATTERN = /^[a-z0-9](?:[a-z0-9-]{0,62}[a-z0-9])?$/;
10
+ export const PORTAL_ALIAS_MAX = 64;
11
+ /**
12
+ * Names DSM already serves: /usr/syno/synoman entries and the default aliases
13
+ * of Synology Apps, as observed on DSM 7.4-90069. DSM stays the authority.
14
+ */
15
+ export const RESERVED_PORTAL_ALIASES = [
16
+ "dsfile", "indexdb", "mobile", "oauth", "scripts", "sharing", "ssdp", "synosdsjslib", "synohdpack", "webapi", "webman",
17
+ "calendar", "chatplus", "contacts", "drive", "file", "photo", "sso", "synologymeet",
18
+ ];
19
+ export function isPortalAlias(value) {
20
+ return typeof value === "string" && PORTAL_ALIAS_PATTERN.test(value) && !RESERVED_PORTAL_ALIASES.includes(value);
21
+ }
22
+ /** The package name as a slug; a reserved or too long name falls back to a stable hash. */
23
+ export function defaultPortalAlias(packageId) {
24
+ const slug = packageId.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
25
+ return isPortalAlias(slug) ? slug : `app-${createHash("sha256").update(packageId).digest("hex").slice(0, 12)}`;
26
+ }
27
+ /** `{}` or `{alias}`; undefined for any other shape. */
28
+ export function readPortalDeclaration(value, packageId) {
29
+ if (typeof value !== "object" || value === null || Array.isArray(value))
30
+ return undefined;
31
+ const keys = Object.keys(value);
32
+ if (keys.some((key) => key !== "alias"))
33
+ return undefined;
34
+ const alias = value.alias;
35
+ if (alias === undefined)
36
+ return { alias: defaultPortalAlias(packageId) };
37
+ return isPortalAlias(alias) ? { alias } : undefined;
38
+ }
39
+ /** A Compose App reaches the portal only as a DSM window behind the DSM-authenticated gateway. */
40
+ export function contractPortalSupported(contract) {
41
+ return resolvedWebLaunch(contract) === "dsm-window" && usesAuthGateway(contract);
42
+ }
43
+ export function resolvedContractPortal(contract) {
44
+ const portal = readWebIntegration(contract)?.portal;
45
+ if (portal === undefined || !contractPortalSupported(contract))
46
+ return undefined;
47
+ return readPortalDeclaration(portal, String(contract.package ?? ""));
48
+ }
@@ -0,0 +1,33 @@
1
+ import type { Translate } from "../build-spk/wizard-i18n.js";
2
+ import type { InstallWizardStep } from "../build-spk/install-wizard.js";
3
+ import type { AppContract } from "../types.js";
4
+ /** Package payload paths DSM's app-portal worker copies to /usr/syno/etc/www/app.d at start. */
5
+ export declare const PORTAL_JSON_RELPATH = "etc/portal.json";
6
+ export declare const PORTAL_MUSTACHE_RELPATH = "etc/portal.mustache";
7
+ /** Install wizard key; DSM exports it to preinst/postinst. */
8
+ export declare const PORTAL_INSTALL_KEY = "portal_alias";
9
+ /** Package-owned install draft, never DSM's saved portal configuration. */
10
+ export declare const PORTAL_DRAFT = "portal-install.json";
11
+ export interface PortalPackage {
12
+ /** The DSM app id the alias opens: an AppInstance or a `.url` entry of ui/config. */
13
+ appId: string;
14
+ /** Control Panel display name. */
15
+ application: string;
16
+ /** Default alias; the wizard answer replaces it. */
17
+ alias: string;
18
+ }
19
+ /** A Compose portal opens the App's `.url` entry of ui/config, keyed by the package id. */
20
+ export declare function contractPortal(contract: AppContract): PortalPackage | undefined;
21
+ export declare function portalResource(): Record<string, unknown>;
22
+ /** Every field of DSM's own app.d files; the worker refuses a partial file (synopkg start 272). */
23
+ export declare function renderPortalJson(portal: PortalPackage): string;
24
+ /** DSM's CGI location: index.cgi launches the app id nginx passes as REWRITE_APP. */
25
+ export declare function renderPortalMustache(): string;
26
+ export declare function renderPortalWizardItems(t: Translate, alias: string): Array<Record<string, unknown>>;
27
+ export declare function renderPortalWizardStep(t: Translate, alias: string): InstallWizardStep;
28
+ /**
29
+ * preinst validates the wizard answer, postinst stores it and writes it as the
30
+ * default alias of the package's own portal.json, postupgrade restores it into
31
+ * the new payload. Activation stays an administrator's AppPortal set call.
32
+ */
33
+ export declare function renderPortalLifecycle(name: string, portal: PortalPackage): string;