@synopackageland/cli 0.6.1 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (578) hide show
  1. package/CHANGELOG.md +613 -0
  2. package/README.md +81 -89
  3. package/dist/add.js +39 -16
  4. package/dist/build-spk/delegate-scope.d.ts +6 -0
  5. package/dist/build-spk/delegate-scope.js +34 -0
  6. package/dist/build-spk/dsm-arch.d.ts +7 -1
  7. package/dist/build-spk/dsm-arch.js +24 -28
  8. package/dist/build-spk/dsm-platforms.json +39 -0
  9. package/dist/build-spk/dsm-version.d.ts +11 -0
  10. package/dist/build-spk/dsm-version.js +17 -0
  11. package/dist/build-spk/gateway.d.ts +1 -1
  12. package/dist/build-spk/gateway.js +2 -2
  13. package/dist/build-spk/generator.js +30 -5
  14. package/dist/build-spk/notices.js +4 -1
  15. package/dist/build-spk/resource-worker.js +3 -0
  16. package/dist/build-spk/wizard-messages.d.ts +66 -0
  17. package/dist/build-spk/wizard-messages.js +75 -9
  18. package/dist/cli.js +15 -7
  19. package/dist/contract/schema-validate.js +8 -0
  20. package/dist/contract/validate.js +30 -0
  21. package/dist/deploy/deploy.d.ts +1 -1
  22. package/dist/deploy/deploy.js +8 -0
  23. package/dist/deploy/plan.js +1 -1
  24. package/dist/deploy/spk-version.js +1 -1
  25. package/dist/error-registry.js +43 -15
  26. package/dist/host/dsm-client.d.ts +8 -4
  27. package/dist/host/dsm-client.js +10 -4
  28. package/dist/host/types.d.ts +2 -1
  29. package/dist/info.js +18 -2
  30. package/dist/init.d.ts +3 -0
  31. package/dist/init.js +49 -18
  32. package/dist/package-metadata.d.ts +21 -0
  33. package/dist/package-metadata.js +101 -0
  34. package/dist/portal/alias.d.ts +25 -0
  35. package/dist/portal/alias.js +48 -0
  36. package/dist/portal/package.d.ts +33 -0
  37. package/dist/portal/package.js +116 -0
  38. package/dist/runtime/delegate-contract.d.ts +79 -0
  39. package/dist/runtime/delegate-contract.js +27 -0
  40. package/dist/runtime/executor-build.d.ts +1 -1
  41. package/dist/runtime/executor-build.js +5 -4
  42. package/dist/runtime/executor-inspect.d.ts +3 -0
  43. package/dist/runtime/executor-inspect.js +11 -5
  44. package/dist/runtime/executor-policy.d.ts +12 -0
  45. package/dist/runtime/executor-policy.js +43 -3
  46. package/dist/runtime/executor-scope-task.d.ts +4 -0
  47. package/dist/runtime/executor-scope-task.js +21 -0
  48. package/dist/runtime/executor-tasks.d.ts +2 -1
  49. package/dist/runtime/executor-tasks.js +4 -4
  50. package/dist/skill.js +1 -0
  51. package/dist/standalone/api-permissions.d.ts +10 -0
  52. package/dist/standalone/api-permissions.js +19 -2
  53. package/dist/standalone/background.d.ts +16 -0
  54. package/dist/standalone/background.js +69 -2
  55. package/dist/standalone/binary.d.ts +1 -1
  56. package/dist/standalone/binary.js +22 -8
  57. package/dist/standalone/build.d.ts +1 -1
  58. package/dist/standalone/build.js +38 -8
  59. package/dist/standalone/daemon.d.ts +1 -1
  60. package/dist/standalone/daemon.js +2 -1
  61. package/dist/standalone/dsm-window.d.ts +3 -1
  62. package/dist/standalone/dsm-window.js +72 -15
  63. package/dist/standalone/inspect-spk.d.ts +8 -0
  64. package/dist/standalone/inspect-spk.js +105 -26
  65. package/dist/standalone/lifecycle.js +86 -8
  66. package/dist/standalone/manifest.d.ts +13 -0
  67. package/dist/standalone/manifest.js +57 -8
  68. package/dist/standalone/native-authorization-window.d.ts +1 -1
  69. package/dist/standalone/native-authorization-window.js +2 -0
  70. package/dist/standalone/native-layout.d.ts +3 -0
  71. package/dist/standalone/native-layout.js +37 -5
  72. package/dist/standalone/portal-window.d.ts +21 -0
  73. package/dist/standalone/portal-window.js +155 -0
  74. package/dist/standalone/portal.d.ts +4 -0
  75. package/dist/standalone/portal.js +4 -0
  76. package/dist/standalone/resource.d.ts +5 -3
  77. package/dist/standalone/resource.js +6 -3
  78. package/dist/standalone/root-window.js +62 -15
  79. package/dist/standalone/spk.d.ts +1 -1
  80. package/dist/standalone/spk.js +25 -7
  81. package/dist/templates/_daemon/main_test.go +61 -8
  82. package/dist/templates/_executor/README.md +127 -16
  83. package/dist/templates/_executor/anchor.go +146 -0
  84. package/dist/templates/_executor/anchor_test.go +126 -0
  85. package/dist/templates/_executor/apppriv_review_test.go +1 -1
  86. package/dist/templates/_executor/cancel_contract_test.go +236 -0
  87. package/dist/templates/_executor/copy.go +64 -0
  88. package/dist/templates/_executor/copy_test.go +88 -0
  89. package/dist/templates/_executor/dsm.go +23 -10
  90. package/dist/templates/_executor/dsm_test.go +95 -1
  91. package/dist/templates/_executor/executor.go +260 -49
  92. package/dist/templates/_executor/executor_test.go +36 -6
  93. package/dist/templates/_executor/identity.go +27 -1
  94. package/dist/templates/_executor/identity_kernel_linux_test.go +60 -0
  95. package/dist/templates/_executor/identity_linux.go +82 -0
  96. package/dist/templates/_executor/identity_security_test.go +43 -0
  97. package/dist/templates/_executor/internal/lifecycle/lifecycle.go +5 -0
  98. package/dist/templates/_executor/internal/lifecycle/lifecycle_test.go +61 -3
  99. package/dist/templates/_executor/internal/taskscript/boot.sh +9 -9
  100. package/dist/templates/_executor/internal/taskscript/cmd/main.go +2 -2
  101. package/dist/templates/_executor/internal/taskscript/control.sh +15 -10
  102. package/dist/templates/_executor/internal/taskscript/tasks.go +5 -3
  103. package/dist/templates/_executor/lifecycle_test.go +11 -0
  104. package/dist/templates/_executor/link_linux.go +28 -0
  105. package/dist/templates/_executor/long_running_test.go +530 -0
  106. package/dist/templates/_executor/main.go +72 -20
  107. package/dist/templates/_executor/move_linux.go +490 -0
  108. package/dist/templates/_executor/move_linux_test.go +460 -0
  109. package/dist/templates/_executor/move_validation.go +46 -0
  110. package/dist/templates/_executor/move_validation_test.go +187 -0
  111. package/dist/templates/_executor/operations.go +926 -0
  112. package/dist/templates/_executor/platform_linux.go +24 -173
  113. package/dist/templates/_executor/platform_linux_test.go +9 -65
  114. package/dist/templates/_executor/platform_other.go +30 -4
  115. package/dist/templates/_executor/process.go +3 -0
  116. package/dist/templates/_executor/records.go +242 -0
  117. package/dist/templates/_executor/scope_cancel_contract_test.go +91 -0
  118. package/dist/templates/_executor/scope_client.go +37 -0
  119. package/dist/templates/_executor/scope_contract.go +12 -0
  120. package/dist/templates/_executor/selected_scope.go +146 -0
  121. package/dist/templates/_executor/selected_scope_test.go +186 -0
  122. package/dist/templates/_executor/status_disabled_test.go +33 -0
  123. package/dist/templates/_executor/store.go +4 -3
  124. package/dist/templates/_executor/supervise.go +109 -0
  125. package/dist/templates/_executor/supervise_test.go +130 -0
  126. package/dist/templates/_executor/task_race_review_test.go +1 -1
  127. package/dist/templates/_executor/tasks.go +7 -8
  128. package/dist/templates/_executor/tasks_review_test.go +1 -1
  129. package/dist/templates/_executor/tasks_test.go +4 -4
  130. package/dist/templates/_executor/watch.go +8 -0
  131. package/dist/templates/_executor/watch_test.go +23 -2
  132. package/dist/templates/_executor/write_review_linux_test.go +4 -12
  133. package/dist/templates/_shared/AGENTS.md +129 -0
  134. package/dist/templates/binary/dsm-package.yaml +24 -0
  135. package/dist/templates/binary/i18n/messages/de.json +4 -0
  136. package/dist/templates/binary/i18n/messages/en.json +4 -0
  137. package/dist/templates/binary/i18n/messages/es.json +4 -0
  138. package/dist/templates/binary/i18n/messages/fr.json +4 -0
  139. package/dist/templates/binary/i18n/messages/it.json +4 -0
  140. package/dist/templates/binary/i18n/messages/ja.json +4 -0
  141. package/dist/templates/binary/i18n/messages/ko.json +4 -0
  142. package/dist/templates/binary/i18n/messages/zh-CN.json +4 -0
  143. package/dist/templates/binary/i18n/messages/zh-TW.json +4 -0
  144. package/dist/templates/binary/ui/app.js +19 -4
  145. package/dist/templates/native-authorization/AuthorizationGate.vue +5 -3
  146. package/dist/templates/native-authorization/i18n/de.json +1 -0
  147. package/dist/templates/native-authorization/i18n/en.json +1 -0
  148. package/dist/templates/native-authorization/i18n/es.json +2 -1
  149. package/dist/templates/native-authorization/i18n/fr.json +1 -0
  150. package/dist/templates/native-authorization/i18n/it.json +11 -10
  151. package/dist/templates/native-authorization/i18n/ja.json +1 -0
  152. package/dist/templates/native-authorization/i18n/ko.json +10 -9
  153. package/dist/templates/native-authorization/i18n/zh-CN.json +6 -5
  154. package/dist/templates/native-authorization/i18n/zh-TW.json +6 -5
  155. package/dist/templates/native-go/README.md +43 -10
  156. package/dist/templates/native-go/backend/cmd/app/main.go +27 -7
  157. package/dist/templates/native-go/backend/internal/api/entry_test.go +47 -0
  158. package/dist/templates/native-go/backend/internal/api/permissions_test.go +3 -3
  159. package/dist/templates/native-go/backend/internal/api/registry.go +11 -0
  160. package/dist/templates/native-go/backend/internal/api/sdk-permissions.json +14 -1
  161. package/dist/templates/native-go/backend/internal/api/server.go +18 -5
  162. package/dist/templates/native-go/backend/internal/app/delegate_config.go +57 -0
  163. package/dist/templates/native-go/backend/internal/app/delegate_config_test.go +28 -0
  164. package/dist/templates/native-go/backend/internal/app/portal.go +30 -0
  165. package/dist/templates/native-go/backend/internal/app/portal_test.go +55 -0
  166. package/dist/templates/native-go/backend/internal/app/routes.go +8 -2
  167. package/dist/templates/native-go/backend/internal/background/runner.go +1 -1
  168. package/dist/templates/native-go/backend/internal/background/store.go +16 -14
  169. package/dist/templates/native-go/backend/internal/dsm/admin.go +8 -2
  170. package/dist/templates/native-go/backend/internal/dsm/admin_linux.go +43 -13
  171. package/dist/templates/native-go/backend/internal/dsm/admin_linux_test.go +56 -0
  172. package/dist/templates/native-go/backend/internal/dsm/admin_other.go +4 -1
  173. package/dist/templates/native-go/backend/internal/dsm/apppriv.go +30 -0
  174. package/dist/templates/native-go/backend/internal/dsm/bridge_file.go +75 -0
  175. package/dist/templates/native-go/backend/internal/dsm/bridge_file_test.go +57 -0
  176. package/dist/templates/native-go/backend/internal/dsm/entry.go +177 -0
  177. package/dist/templates/native-go/backend/internal/dsm/entry_test.go +315 -0
  178. package/dist/templates/native-go/backend/internal/dsm/native/admin-armv7.elf +0 -0
  179. package/dist/templates/native-go/backend/internal/dsm/native/apppriv-aarch64.elf +0 -0
  180. package/dist/templates/native-go/backend/internal/dsm/native/apppriv-armv7.elf +0 -0
  181. package/dist/templates/native-go/backend/internal/dsm/native/apppriv-x86_64.elf +0 -0
  182. package/dist/templates/native-go/backend/internal/dsm/native/apppriv_check.c +33 -0
  183. package/dist/templates/native-go/backend/internal/dsm/native/build.sh +13 -4
  184. package/dist/templates/native-go/backend/internal/dsm/native/provenance.json +9 -3
  185. package/dist/templates/native-go/backend/internal/filestation/check_permission.go +71 -0
  186. package/dist/templates/native-go/backend/internal/filestation/check_permission_test.go +77 -0
  187. package/dist/templates/native-go/backend/internal/filestation/sdk_policy_test.go +39 -0
  188. package/dist/templates/native-go/backend/internal/filestation/service.go +1 -1
  189. package/dist/templates/native-go/backend/internal/portal/portal.go +138 -0
  190. package/dist/templates/native-go/backend/internal/portal/portal_test.go +89 -0
  191. package/dist/templates/native-go/dsm-package.yaml +20 -1
  192. package/dist/templates/native-go/i18n/glossary.json +2 -1
  193. package/dist/templates/native-go/i18n/messages/de.json +1 -0
  194. package/dist/templates/native-go/i18n/messages/en.json +1 -0
  195. package/dist/templates/native-go/i18n/messages/es.json +1 -0
  196. package/dist/templates/native-go/i18n/messages/fr.json +1 -0
  197. package/dist/templates/native-go/i18n/messages/it.json +1 -0
  198. package/dist/templates/native-go/i18n/messages/ja.json +1 -0
  199. package/dist/templates/native-go/i18n/messages/ko.json +1 -0
  200. package/dist/templates/native-go/i18n/messages/zh-CN.json +1 -0
  201. package/dist/templates/native-go/i18n/messages/zh-TW.json +1 -0
  202. package/dist/templates/native-go/ui/app.css +10 -0
  203. package/dist/templates/native-go/ui/app.js +11 -1
  204. package/dist/templates/native-go-delegate/DELEGATE.md +126 -5
  205. package/dist/templates/native-go-delegate/backend/cmd/background-worker/main.go +7 -0
  206. package/dist/templates/native-go-delegate/backend/internal/app/delegate.go +11 -1
  207. package/dist/templates/native-go-delegate/backend/internal/app/delegate_routes_test.go +14 -0
  208. package/dist/templates/native-go-delegate/backend/internal/background/delegated_moves.go +270 -0
  209. package/dist/templates/native-go-delegate/backend/internal/delegate/admission_recovery_test.go +410 -0
  210. package/dist/templates/native-go-delegate/backend/internal/delegate/cancel_contract_test.go +95 -0
  211. package/dist/templates/native-go-delegate/backend/internal/delegate/client.go +8 -0
  212. package/dist/templates/native-go-delegate/backend/internal/delegate/destination.go +110 -0
  213. package/dist/templates/native-go-delegate/backend/internal/delegate/destination_state.go +117 -0
  214. package/dist/templates/native-go-delegate/backend/internal/delegate/destination_state_test.go +60 -0
  215. package/dist/templates/native-go-delegate/backend/internal/delegate/destination_test.go +116 -0
  216. package/dist/templates/native-go-delegate/backend/internal/delegate/example.go +81 -0
  217. package/dist/templates/native-go-delegate/backend/internal/delegate/example_test.go +91 -0
  218. package/dist/templates/native-go-delegate/backend/internal/delegate/executor_contract_test.go +511 -0
  219. package/dist/templates/native-go-delegate/backend/internal/delegate/files.go +1 -1
  220. package/dist/templates/native-go-delegate/backend/internal/delegate/long_running_routes.go +92 -0
  221. package/dist/templates/native-go-delegate/backend/internal/delegate/long_running_routes_test.go +99 -0
  222. package/dist/templates/native-go-delegate/backend/internal/delegate/move.go +348 -0
  223. package/dist/templates/native-go-delegate/backend/internal/delegate/move_test.go +351 -0
  224. package/dist/templates/native-go-delegate/backend/internal/delegate/operations_list.go +65 -0
  225. package/dist/templates/native-go-delegate/backend/internal/delegate/operations_list_test.go +114 -0
  226. package/dist/templates/native-go-delegate/backend/internal/delegate/retry_test.go +1 -1
  227. package/dist/templates/native-go-delegate/backend/internal/delegate/service.go +58 -17
  228. package/dist/templates/native-go-delegate/backend/internal/delegate/staging.go +121 -0
  229. package/dist/templates/native-go-delegate/backend/internal/delegate/status_disabled_test.go +51 -0
  230. package/dist/templates/native-go-delegate/backend/internal/delegate/testdata/executor_contract.txt +112 -0
  231. package/dist/templates/native-go-delegate/backend/internal/delegate/transient_contract_test.go +137 -0
  232. package/dist/templates/native-go-delegate/backend/internal/filestation/files.go +1 -31
  233. package/dist/templates/native-go-delegate/frontend/src/CapabilityPanels.vue +30 -3
  234. package/dist/templates/native-go-delegate/frontend/src/delegate/BackgroundSavePanel.vue +113 -0
  235. package/dist/templates/native-go-delegate/frontend/src/delegate/copy.ts +5 -6
  236. package/dist/templates/native-go-delegate/frontend/test/background-save.test.ts +121 -0
  237. package/dist/templates/native-go-delegate/frontend/test/scope-settings.test.ts +49 -0
  238. package/dist/templates/native-go-delegate/test.sh +1 -0
  239. package/dist/templates/native-go-jobs/frontend/src/background/i18n/de.json +2 -2
  240. package/dist/templates/native-go-jobs/frontend/src/background/i18n/en.json +2 -2
  241. package/dist/templates/native-go-jobs/frontend/src/background/i18n/es.json +12 -12
  242. package/dist/templates/native-go-jobs/frontend/src/background/i18n/fr.json +4 -4
  243. package/dist/templates/native-go-jobs/frontend/src/background/i18n/it.json +17 -17
  244. package/dist/templates/native-go-jobs/frontend/src/background/i18n/ja.json +7 -7
  245. package/dist/templates/native-go-jobs/frontend/src/background/i18n/ko.json +15 -15
  246. package/dist/templates/native-go-jobs/frontend/src/background/i18n/zh-CN.json +3 -3
  247. package/dist/templates/native-go-jobs/frontend/src/background/i18n/zh-TW.json +2 -2
  248. package/dist/templates/native-go-jobs/frontend/test/background.test.ts +25 -28
  249. package/dist/templates/native-go-vue/README.md +4 -0
  250. package/dist/templates/native-go-vue/frontend/package-lock.json +4 -4
  251. package/dist/templates/native-go-vue/frontend/package.json +1 -1
  252. package/dist/templates/native-go-vue/frontend/src/App.vue +18 -13
  253. package/dist/templates/native-go-vue/frontend/src/context.ts +31 -0
  254. package/dist/templates/native-go-vue/frontend/src/delegate/ExecutorSettings.vue +52 -8
  255. package/dist/templates/native-go-vue/frontend/src/delegate/catalogs.ts +11 -0
  256. package/dist/templates/native-go-vue/frontend/src/delegate/copy.test.ts +12 -0
  257. package/dist/templates/native-go-vue/frontend/src/delegate/copy.ts +4 -4
  258. package/dist/templates/native-go-vue/frontend/src/delegate/executor-setup.ts +4 -2
  259. package/dist/templates/native-go-vue/frontend/src/delegate/facade.test.ts +100 -0
  260. package/dist/templates/native-go-vue/frontend/src/delegate/facade.ts +85 -0
  261. package/dist/templates/native-go-vue/frontend/src/delegate/i18n/de.json +46 -0
  262. package/dist/templates/native-go-vue/frontend/src/delegate/i18n/en.json +12 -2
  263. package/dist/templates/native-go-vue/frontend/src/delegate/i18n/es.json +46 -0
  264. package/dist/templates/native-go-vue/frontend/src/delegate/i18n/fr.json +46 -0
  265. package/dist/templates/native-go-vue/frontend/src/delegate/i18n/it.json +46 -0
  266. package/dist/templates/native-go-vue/frontend/src/delegate/i18n/ja.json +46 -0
  267. package/dist/templates/native-go-vue/frontend/src/delegate/i18n/ko.json +46 -0
  268. package/dist/templates/native-go-vue/frontend/src/delegate/i18n/save/de.json +42 -0
  269. package/dist/templates/native-go-vue/frontend/src/delegate/i18n/save/en.json +42 -0
  270. package/dist/templates/native-go-vue/frontend/src/delegate/i18n/save/es.json +42 -0
  271. package/dist/templates/native-go-vue/frontend/src/delegate/i18n/save/fr.json +42 -0
  272. package/dist/templates/native-go-vue/frontend/src/delegate/i18n/save/it.json +42 -0
  273. package/dist/templates/native-go-vue/frontend/src/delegate/i18n/save/ja.json +42 -0
  274. package/dist/templates/native-go-vue/frontend/src/delegate/i18n/save/ko.json +42 -0
  275. package/dist/templates/native-go-vue/frontend/src/delegate/i18n/save/zh-CN.json +42 -0
  276. package/dist/templates/native-go-vue/frontend/src/delegate/i18n/save/zh-TW.json +42 -0
  277. package/dist/templates/native-go-vue/frontend/src/delegate/i18n/zh-CN.json +46 -0
  278. package/dist/templates/native-go-vue/frontend/src/delegate/i18n/zh-TW.json +15 -5
  279. package/dist/templates/native-go-vue/frontend/src/delegate/long-running.ts +57 -0
  280. package/dist/templates/native-go-vue/frontend/src/delegate/save-copy.test.ts +12 -0
  281. package/dist/templates/native-go-vue/frontend/src/delegate/save-copy.ts +24 -0
  282. package/dist/templates/native-go-vue/frontend/src/delegate/scoped-picker.test.ts +12 -0
  283. package/dist/templates/native-go-vue/frontend/src/delegate/scoped-picker.ts +21 -0
  284. package/dist/templates/native-go-vue/frontend/src/files/files.test.ts +7 -0
  285. package/dist/templates/native-go-vue/frontend/src/i18n/de.json +20 -0
  286. package/dist/templates/native-go-vue/frontend/src/i18n/en.json +20 -0
  287. package/dist/templates/native-go-vue/frontend/src/i18n/es.json +20 -0
  288. package/dist/templates/native-go-vue/frontend/src/i18n/fr.json +20 -0
  289. package/dist/templates/native-go-vue/frontend/src/i18n/it.json +20 -0
  290. package/dist/templates/native-go-vue/frontend/src/i18n/ja.json +20 -0
  291. package/dist/templates/native-go-vue/frontend/src/i18n/ko.json +20 -0
  292. package/dist/templates/native-go-vue/frontend/src/i18n/zh-CN.json +20 -0
  293. package/dist/templates/native-go-vue/frontend/src/i18n/zh-TW.json +20 -0
  294. package/dist/templates/native-go-vue/frontend/src/i18n.ts +13 -0
  295. package/dist/templates/native-go-vue/frontend/src/identity/LoginPage.vue +16 -14
  296. package/dist/templates/native-go-vue/frontend/src/identity/i18n/de.json +27 -0
  297. package/dist/templates/native-go-vue/frontend/src/identity/i18n/en.json +27 -0
  298. package/dist/templates/native-go-vue/frontend/src/identity/i18n/es.json +27 -0
  299. package/dist/templates/native-go-vue/frontend/src/identity/i18n/fr.json +27 -0
  300. package/dist/templates/native-go-vue/frontend/src/identity/i18n/it.json +27 -0
  301. package/dist/templates/native-go-vue/frontend/src/identity/i18n/ja.json +27 -0
  302. package/dist/templates/native-go-vue/frontend/src/identity/i18n/ko.json +27 -0
  303. package/dist/templates/native-go-vue/frontend/src/identity/i18n/zh-CN.json +27 -0
  304. package/dist/templates/native-go-vue/frontend/src/identity/i18n/zh-TW.json +27 -0
  305. package/dist/templates/native-go-vue/frontend/src/identity/i18n.ts +14 -0
  306. package/dist/templates/native-go-vue/frontend/src/identity/session.test.ts +4 -0
  307. package/dist/templates/native-go-vue/frontend/src/identity/session.ts +9 -8
  308. package/dist/templates/native-go-vue/frontend/src/nas/NasPicker.vue +31 -12
  309. package/dist/templates/native-go-vue/frontend/src/nas/destination.test.ts +48 -0
  310. package/dist/templates/native-go-vue/frontend/src/nas/i18n/files/de.json +1 -1
  311. package/dist/templates/native-go-vue/frontend/src/nas/i18n/files/en.json +1 -1
  312. package/dist/templates/native-go-vue/frontend/src/nas/i18n/files/es.json +10 -10
  313. package/dist/templates/native-go-vue/frontend/src/nas/i18n/files/fr.json +1 -1
  314. package/dist/templates/native-go-vue/frontend/src/nas/i18n/files/it.json +12 -12
  315. package/dist/templates/native-go-vue/frontend/src/nas/i18n/files/ja.json +15 -15
  316. package/dist/templates/native-go-vue/frontend/src/nas/i18n/files/ko.json +12 -12
  317. package/dist/templates/native-go-vue/frontend/src/nas/i18n/files/zh-CN.json +1 -1
  318. package/dist/templates/native-go-vue/frontend/src/nas/i18n/files/zh-TW.json +1 -1
  319. package/dist/templates/native-go-vue/frontend/src/portal/PortalSettings.vue +39 -0
  320. package/dist/templates/native-go-vue/frontend/src/shell/i18n/fr.json +1 -1
  321. package/dist/templates/native-go-vue/frontend/src/shell/i18n/ja.json +1 -1
  322. package/dist/templates/native-go-vue/frontend/test/apps.test.ts +14 -0
  323. package/dist/templates/native-go-vue/frontend/test/portal-settings.test.ts +71 -0
  324. package/dist/templates/native-go-vue/frontend/test/shell-i18n.test.ts +50 -0
  325. package/package.json +5 -1
  326. package/schema/package-metadata.schema.json +101 -0
  327. package/schema/synology-app.schema.json +112 -0
  328. package/skills/SKILL.md +106 -4
  329. package/skills/reference/create.md +101 -2
  330. package/skills/reference/deploy.md +2 -2
  331. package/skills/reference/files.md +113 -2
  332. package/skills/reference/identity.md +70 -1
  333. package/skills/reference/portal.md +17 -0
  334. package/skills/reference/ui-vue.md +12 -0
  335. package/templates/_daemon/main_test.go +61 -8
  336. package/templates/_executor/README.md +127 -16
  337. package/templates/_executor/anchor.go +146 -0
  338. package/templates/_executor/anchor_test.go +126 -0
  339. package/templates/_executor/apppriv_review_test.go +1 -1
  340. package/templates/_executor/cancel_contract_test.go +236 -0
  341. package/templates/_executor/copy.go +64 -0
  342. package/templates/_executor/copy_test.go +88 -0
  343. package/templates/_executor/dsm.go +23 -10
  344. package/templates/_executor/dsm_test.go +95 -1
  345. package/templates/_executor/executor.go +260 -49
  346. package/templates/_executor/executor_test.go +36 -6
  347. package/templates/_executor/identity.go +27 -1
  348. package/templates/_executor/identity_kernel_linux_test.go +60 -0
  349. package/templates/_executor/identity_linux.go +82 -0
  350. package/templates/_executor/identity_security_test.go +43 -0
  351. package/templates/_executor/internal/lifecycle/lifecycle.go +5 -0
  352. package/templates/_executor/internal/lifecycle/lifecycle_test.go +61 -3
  353. package/templates/_executor/internal/taskscript/boot.sh +9 -9
  354. package/templates/_executor/internal/taskscript/cmd/main.go +2 -2
  355. package/templates/_executor/internal/taskscript/control.sh +15 -10
  356. package/templates/_executor/internal/taskscript/tasks.go +5 -3
  357. package/templates/_executor/lifecycle_test.go +11 -0
  358. package/templates/_executor/link_linux.go +28 -0
  359. package/templates/_executor/long_running_test.go +530 -0
  360. package/templates/_executor/main.go +72 -20
  361. package/templates/_executor/move_linux.go +490 -0
  362. package/templates/_executor/move_linux_test.go +460 -0
  363. package/templates/_executor/move_validation.go +46 -0
  364. package/templates/_executor/move_validation_test.go +187 -0
  365. package/templates/_executor/operations.go +926 -0
  366. package/templates/_executor/platform_linux.go +24 -173
  367. package/templates/_executor/platform_linux_test.go +9 -65
  368. package/templates/_executor/platform_other.go +30 -4
  369. package/templates/_executor/process.go +3 -0
  370. package/templates/_executor/records.go +242 -0
  371. package/templates/_executor/scope_cancel_contract_test.go +91 -0
  372. package/templates/_executor/scope_client.go +37 -0
  373. package/templates/_executor/scope_contract.go +12 -0
  374. package/templates/_executor/selected_scope.go +146 -0
  375. package/templates/_executor/selected_scope_test.go +186 -0
  376. package/templates/_executor/status_disabled_test.go +33 -0
  377. package/templates/_executor/store.go +4 -3
  378. package/templates/_executor/supervise.go +109 -0
  379. package/templates/_executor/supervise_test.go +130 -0
  380. package/templates/_executor/task_race_review_test.go +1 -1
  381. package/templates/_executor/tasks.go +7 -8
  382. package/templates/_executor/tasks_review_test.go +1 -1
  383. package/templates/_executor/tasks_test.go +4 -4
  384. package/templates/_executor/watch.go +8 -0
  385. package/templates/_executor/watch_test.go +23 -2
  386. package/templates/_executor/write_review_linux_test.go +4 -12
  387. package/templates/_shared/AGENTS.md +129 -0
  388. package/templates/binary/dsm-package.yaml +24 -0
  389. package/templates/binary/i18n/messages/de.json +4 -0
  390. package/templates/binary/i18n/messages/en.json +4 -0
  391. package/templates/binary/i18n/messages/es.json +4 -0
  392. package/templates/binary/i18n/messages/fr.json +4 -0
  393. package/templates/binary/i18n/messages/it.json +4 -0
  394. package/templates/binary/i18n/messages/ja.json +4 -0
  395. package/templates/binary/i18n/messages/ko.json +4 -0
  396. package/templates/binary/i18n/messages/zh-CN.json +4 -0
  397. package/templates/binary/i18n/messages/zh-TW.json +4 -0
  398. package/templates/binary/ui/app.js +19 -4
  399. package/templates/native-authorization/AuthorizationGate.vue +5 -3
  400. package/templates/native-authorization/i18n/de.json +1 -0
  401. package/templates/native-authorization/i18n/en.json +1 -0
  402. package/templates/native-authorization/i18n/es.json +2 -1
  403. package/templates/native-authorization/i18n/fr.json +1 -0
  404. package/templates/native-authorization/i18n/it.json +11 -10
  405. package/templates/native-authorization/i18n/ja.json +1 -0
  406. package/templates/native-authorization/i18n/ko.json +10 -9
  407. package/templates/native-authorization/i18n/zh-CN.json +6 -5
  408. package/templates/native-authorization/i18n/zh-TW.json +6 -5
  409. package/templates/native-go/README.md +43 -10
  410. package/templates/native-go/backend/cmd/app/main.go +27 -7
  411. package/templates/native-go/backend/internal/api/entry_test.go +47 -0
  412. package/templates/native-go/backend/internal/api/permissions_test.go +3 -3
  413. package/templates/native-go/backend/internal/api/registry.go +11 -0
  414. package/templates/native-go/backend/internal/api/sdk-permissions.json +14 -1
  415. package/templates/native-go/backend/internal/api/server.go +18 -5
  416. package/templates/native-go/backend/internal/app/delegate_config.go +57 -0
  417. package/templates/native-go/backend/internal/app/delegate_config_test.go +28 -0
  418. package/templates/native-go/backend/internal/app/portal.go +30 -0
  419. package/templates/native-go/backend/internal/app/portal_test.go +55 -0
  420. package/templates/native-go/backend/internal/app/routes.go +8 -2
  421. package/templates/native-go/backend/internal/background/runner.go +1 -1
  422. package/templates/native-go/backend/internal/background/store.go +16 -14
  423. package/templates/native-go/backend/internal/dsm/admin.go +8 -2
  424. package/templates/native-go/backend/internal/dsm/admin_linux.go +43 -13
  425. package/templates/native-go/backend/internal/dsm/admin_linux_test.go +56 -0
  426. package/templates/native-go/backend/internal/dsm/admin_other.go +4 -1
  427. package/templates/native-go/backend/internal/dsm/apppriv.go +30 -0
  428. package/templates/native-go/backend/internal/dsm/bridge_file.go +75 -0
  429. package/templates/native-go/backend/internal/dsm/bridge_file_test.go +57 -0
  430. package/templates/native-go/backend/internal/dsm/entry.go +177 -0
  431. package/templates/native-go/backend/internal/dsm/entry_test.go +315 -0
  432. package/templates/native-go/backend/internal/dsm/native/admin-armv7.elf +0 -0
  433. package/templates/native-go/backend/internal/dsm/native/apppriv-aarch64.elf +0 -0
  434. package/templates/native-go/backend/internal/dsm/native/apppriv-armv7.elf +0 -0
  435. package/templates/native-go/backend/internal/dsm/native/apppriv-x86_64.elf +0 -0
  436. package/templates/native-go/backend/internal/dsm/native/apppriv_check.c +33 -0
  437. package/templates/native-go/backend/internal/dsm/native/build.sh +13 -4
  438. package/templates/native-go/backend/internal/dsm/native/provenance.json +9 -3
  439. package/templates/native-go/backend/internal/filestation/check_permission.go +71 -0
  440. package/templates/native-go/backend/internal/filestation/check_permission_test.go +77 -0
  441. package/templates/native-go/backend/internal/filestation/sdk_policy_test.go +39 -0
  442. package/templates/native-go/backend/internal/filestation/service.go +1 -1
  443. package/templates/native-go/backend/internal/portal/portal.go +138 -0
  444. package/templates/native-go/backend/internal/portal/portal_test.go +89 -0
  445. package/templates/native-go/dsm-package.yaml +20 -1
  446. package/templates/native-go/i18n/glossary.json +2 -1
  447. package/templates/native-go/i18n/messages/de.json +1 -0
  448. package/templates/native-go/i18n/messages/en.json +1 -0
  449. package/templates/native-go/i18n/messages/es.json +1 -0
  450. package/templates/native-go/i18n/messages/fr.json +1 -0
  451. package/templates/native-go/i18n/messages/it.json +1 -0
  452. package/templates/native-go/i18n/messages/ja.json +1 -0
  453. package/templates/native-go/i18n/messages/ko.json +1 -0
  454. package/templates/native-go/i18n/messages/zh-CN.json +1 -0
  455. package/templates/native-go/i18n/messages/zh-TW.json +1 -0
  456. package/templates/native-go/ui/app.css +10 -0
  457. package/templates/native-go/ui/app.js +11 -1
  458. package/templates/native-go-delegate/DELEGATE.md +126 -5
  459. package/templates/native-go-delegate/backend/cmd/background-worker/main.go +7 -0
  460. package/templates/native-go-delegate/backend/internal/app/delegate.go +11 -1
  461. package/templates/native-go-delegate/backend/internal/app/delegate_routes_test.go +14 -0
  462. package/templates/native-go-delegate/backend/internal/background/delegated_moves.go +270 -0
  463. package/templates/native-go-delegate/backend/internal/delegate/admission_recovery_test.go +410 -0
  464. package/templates/native-go-delegate/backend/internal/delegate/cancel_contract_test.go +95 -0
  465. package/templates/native-go-delegate/backend/internal/delegate/client.go +8 -0
  466. package/templates/native-go-delegate/backend/internal/delegate/destination.go +110 -0
  467. package/templates/native-go-delegate/backend/internal/delegate/destination_state.go +117 -0
  468. package/templates/native-go-delegate/backend/internal/delegate/destination_state_test.go +60 -0
  469. package/templates/native-go-delegate/backend/internal/delegate/destination_test.go +116 -0
  470. package/templates/native-go-delegate/backend/internal/delegate/example.go +81 -0
  471. package/templates/native-go-delegate/backend/internal/delegate/example_test.go +91 -0
  472. package/templates/native-go-delegate/backend/internal/delegate/executor_contract_test.go +511 -0
  473. package/templates/native-go-delegate/backend/internal/delegate/files.go +1 -1
  474. package/templates/native-go-delegate/backend/internal/delegate/long_running_routes.go +92 -0
  475. package/templates/native-go-delegate/backend/internal/delegate/long_running_routes_test.go +99 -0
  476. package/templates/native-go-delegate/backend/internal/delegate/move.go +348 -0
  477. package/templates/native-go-delegate/backend/internal/delegate/move_test.go +351 -0
  478. package/templates/native-go-delegate/backend/internal/delegate/operations_list.go +65 -0
  479. package/templates/native-go-delegate/backend/internal/delegate/operations_list_test.go +114 -0
  480. package/templates/native-go-delegate/backend/internal/delegate/retry_test.go +1 -1
  481. package/templates/native-go-delegate/backend/internal/delegate/service.go +58 -17
  482. package/templates/native-go-delegate/backend/internal/delegate/staging.go +121 -0
  483. package/templates/native-go-delegate/backend/internal/delegate/status_disabled_test.go +51 -0
  484. package/templates/native-go-delegate/backend/internal/delegate/testdata/executor_contract.txt +112 -0
  485. package/templates/native-go-delegate/backend/internal/delegate/transient_contract_test.go +137 -0
  486. package/templates/native-go-delegate/backend/internal/filestation/files.go +1 -31
  487. package/templates/native-go-delegate/frontend/src/CapabilityPanels.vue +30 -3
  488. package/templates/native-go-delegate/frontend/src/delegate/BackgroundSavePanel.vue +113 -0
  489. package/templates/native-go-delegate/frontend/src/delegate/copy.ts +5 -6
  490. package/templates/native-go-delegate/frontend/test/background-save.test.ts +121 -0
  491. package/templates/native-go-delegate/frontend/test/scope-settings.test.ts +49 -0
  492. package/templates/native-go-delegate/test.sh +1 -0
  493. package/templates/native-go-jobs/frontend/src/background/i18n/de.json +2 -2
  494. package/templates/native-go-jobs/frontend/src/background/i18n/en.json +2 -2
  495. package/templates/native-go-jobs/frontend/src/background/i18n/es.json +12 -12
  496. package/templates/native-go-jobs/frontend/src/background/i18n/fr.json +4 -4
  497. package/templates/native-go-jobs/frontend/src/background/i18n/it.json +17 -17
  498. package/templates/native-go-jobs/frontend/src/background/i18n/ja.json +7 -7
  499. package/templates/native-go-jobs/frontend/src/background/i18n/ko.json +15 -15
  500. package/templates/native-go-jobs/frontend/src/background/i18n/zh-CN.json +3 -3
  501. package/templates/native-go-jobs/frontend/src/background/i18n/zh-TW.json +2 -2
  502. package/templates/native-go-jobs/frontend/test/background.test.ts +25 -28
  503. package/templates/native-go-vue/README.md +4 -0
  504. package/templates/native-go-vue/frontend/package-lock.json +4 -4
  505. package/templates/native-go-vue/frontend/package.json +1 -1
  506. package/templates/native-go-vue/frontend/src/App.vue +18 -13
  507. package/templates/native-go-vue/frontend/src/context.ts +31 -0
  508. package/templates/native-go-vue/frontend/src/delegate/ExecutorSettings.vue +52 -8
  509. package/templates/native-go-vue/frontend/src/delegate/catalogs.ts +11 -0
  510. package/templates/native-go-vue/frontend/src/delegate/copy.test.ts +12 -0
  511. package/templates/native-go-vue/frontend/src/delegate/copy.ts +4 -4
  512. package/templates/native-go-vue/frontend/src/delegate/executor-setup.ts +4 -2
  513. package/templates/native-go-vue/frontend/src/delegate/facade.test.ts +100 -0
  514. package/templates/native-go-vue/frontend/src/delegate/facade.ts +85 -0
  515. package/templates/native-go-vue/frontend/src/delegate/i18n/de.json +46 -0
  516. package/templates/native-go-vue/frontend/src/delegate/i18n/en.json +12 -2
  517. package/templates/native-go-vue/frontend/src/delegate/i18n/es.json +46 -0
  518. package/templates/native-go-vue/frontend/src/delegate/i18n/fr.json +46 -0
  519. package/templates/native-go-vue/frontend/src/delegate/i18n/it.json +46 -0
  520. package/templates/native-go-vue/frontend/src/delegate/i18n/ja.json +46 -0
  521. package/templates/native-go-vue/frontend/src/delegate/i18n/ko.json +46 -0
  522. package/templates/native-go-vue/frontend/src/delegate/i18n/save/de.json +42 -0
  523. package/templates/native-go-vue/frontend/src/delegate/i18n/save/en.json +42 -0
  524. package/templates/native-go-vue/frontend/src/delegate/i18n/save/es.json +42 -0
  525. package/templates/native-go-vue/frontend/src/delegate/i18n/save/fr.json +42 -0
  526. package/templates/native-go-vue/frontend/src/delegate/i18n/save/it.json +42 -0
  527. package/templates/native-go-vue/frontend/src/delegate/i18n/save/ja.json +42 -0
  528. package/templates/native-go-vue/frontend/src/delegate/i18n/save/ko.json +42 -0
  529. package/templates/native-go-vue/frontend/src/delegate/i18n/save/zh-CN.json +42 -0
  530. package/templates/native-go-vue/frontend/src/delegate/i18n/save/zh-TW.json +42 -0
  531. package/templates/native-go-vue/frontend/src/delegate/i18n/zh-CN.json +46 -0
  532. package/templates/native-go-vue/frontend/src/delegate/i18n/zh-TW.json +15 -5
  533. package/templates/native-go-vue/frontend/src/delegate/long-running.ts +57 -0
  534. package/templates/native-go-vue/frontend/src/delegate/save-copy.test.ts +12 -0
  535. package/templates/native-go-vue/frontend/src/delegate/save-copy.ts +24 -0
  536. package/templates/native-go-vue/frontend/src/delegate/scoped-picker.test.ts +12 -0
  537. package/templates/native-go-vue/frontend/src/delegate/scoped-picker.ts +21 -0
  538. package/templates/native-go-vue/frontend/src/files/files.test.ts +7 -0
  539. package/templates/native-go-vue/frontend/src/i18n/de.json +20 -0
  540. package/templates/native-go-vue/frontend/src/i18n/en.json +20 -0
  541. package/templates/native-go-vue/frontend/src/i18n/es.json +20 -0
  542. package/templates/native-go-vue/frontend/src/i18n/fr.json +20 -0
  543. package/templates/native-go-vue/frontend/src/i18n/it.json +20 -0
  544. package/templates/native-go-vue/frontend/src/i18n/ja.json +20 -0
  545. package/templates/native-go-vue/frontend/src/i18n/ko.json +20 -0
  546. package/templates/native-go-vue/frontend/src/i18n/zh-CN.json +20 -0
  547. package/templates/native-go-vue/frontend/src/i18n/zh-TW.json +20 -0
  548. package/templates/native-go-vue/frontend/src/i18n.ts +13 -0
  549. package/templates/native-go-vue/frontend/src/identity/LoginPage.vue +16 -14
  550. package/templates/native-go-vue/frontend/src/identity/i18n/de.json +27 -0
  551. package/templates/native-go-vue/frontend/src/identity/i18n/en.json +27 -0
  552. package/templates/native-go-vue/frontend/src/identity/i18n/es.json +27 -0
  553. package/templates/native-go-vue/frontend/src/identity/i18n/fr.json +27 -0
  554. package/templates/native-go-vue/frontend/src/identity/i18n/it.json +27 -0
  555. package/templates/native-go-vue/frontend/src/identity/i18n/ja.json +27 -0
  556. package/templates/native-go-vue/frontend/src/identity/i18n/ko.json +27 -0
  557. package/templates/native-go-vue/frontend/src/identity/i18n/zh-CN.json +27 -0
  558. package/templates/native-go-vue/frontend/src/identity/i18n/zh-TW.json +27 -0
  559. package/templates/native-go-vue/frontend/src/identity/i18n.ts +14 -0
  560. package/templates/native-go-vue/frontend/src/identity/session.test.ts +4 -0
  561. package/templates/native-go-vue/frontend/src/identity/session.ts +9 -8
  562. package/templates/native-go-vue/frontend/src/nas/NasPicker.vue +31 -12
  563. package/templates/native-go-vue/frontend/src/nas/destination.test.ts +48 -0
  564. package/templates/native-go-vue/frontend/src/nas/i18n/files/de.json +1 -1
  565. package/templates/native-go-vue/frontend/src/nas/i18n/files/en.json +1 -1
  566. package/templates/native-go-vue/frontend/src/nas/i18n/files/es.json +10 -10
  567. package/templates/native-go-vue/frontend/src/nas/i18n/files/fr.json +1 -1
  568. package/templates/native-go-vue/frontend/src/nas/i18n/files/it.json +12 -12
  569. package/templates/native-go-vue/frontend/src/nas/i18n/files/ja.json +15 -15
  570. package/templates/native-go-vue/frontend/src/nas/i18n/files/ko.json +12 -12
  571. package/templates/native-go-vue/frontend/src/nas/i18n/files/zh-CN.json +1 -1
  572. package/templates/native-go-vue/frontend/src/nas/i18n/files/zh-TW.json +1 -1
  573. package/templates/native-go-vue/frontend/src/portal/PortalSettings.vue +39 -0
  574. package/templates/native-go-vue/frontend/src/shell/i18n/fr.json +1 -1
  575. package/templates/native-go-vue/frontend/src/shell/i18n/ja.json +1 -1
  576. package/templates/native-go-vue/frontend/test/apps.test.ts +14 -0
  577. package/templates/native-go-vue/frontend/test/portal-settings.test.ts +71 -0
  578. package/templates/native-go-vue/frontend/test/shell-i18n.test.ts +50 -0
@@ -41,3 +41,17 @@ test('failed status loading offers a retry and releases the mounting class',asyn
41
41
  f.app.unmount();expect(f.root.classList.contains('dsmapp-vue-root')).toBe(false)
42
42
  }finally{f.close()}
43
43
  })
44
+ test('a portal app shows the Application Portal section on the status page',async()=>{
45
+ const without=fixture('Plain')
46
+ const f=fixture('Portal')
47
+ try{
48
+ await settle();expect(without.root.textContent).not.toContain('Application Portal')
49
+ f.app.unmount()
50
+ const status={state:'active',alias:'portal',url:'https://nas.example/portal/',canManage:false,controlPanel:false,defaultAlias:'portal'} as const
51
+ f.ctx.portal={messages:{title:'Application Portal',link:'Direct link:'},status:vi.fn(async()=>status),apply:vi.fn(),disable:vi.fn(),openControlPanel:vi.fn(),mountPanel:vi.fn()}
52
+ const app=mount(f.root,f.ctx);await settle()
53
+ expect(f.root.textContent).toContain('Application Portal')
54
+ expect(f.root.querySelector('a[href="https://nas.example/portal/"]')).not.toBeNull()
55
+ app.unmount()
56
+ }finally{without.close();f.root.remove()}
57
+ })
@@ -0,0 +1,71 @@
1
+ // @vitest-environment jsdom
2
+ import {expect,test,vi} from 'vitest'
3
+ import {createApp,h,nextTick} from 'vue'
4
+ import {AppRoot} from '@synopackageland/ui'
5
+ import PortalSettings from '../src/portal/PortalSettings.vue'
6
+ import type {PortalAccess,PortalStatus} from '../src/context'
7
+
8
+ const messages={title:'Application Portal',link:'Direct link:',inactive:'Not active.',askAdmin:'Ask an administrator',alias:'Alias',apply:'Apply',disable:'Turn off',advanced:'Advanced',controlPanel:'Open Control Panel',portal_alias_in_use:'Alias in use',portal_unavailable:'DSM refused'}
9
+ const base:PortalStatus={state:'inactive',canManage:false,controlPanel:false,defaultAlias:'my-app'}
10
+ function access(status:PortalStatus,overrides:Partial<PortalAccess>={}):PortalAccess{
11
+ return {messages,status:vi.fn(async()=>status),apply:vi.fn(async()=>status),disable:vi.fn(async()=>status),openControlPanel:vi.fn(),mountPanel:vi.fn(),...overrides}
12
+ }
13
+ async function mount(portal:PortalAccess){
14
+ const root=document.createElement('div');document.body.append(root)
15
+ const app=createApp({render:()=>h(AppRoot,{},()=>h(PortalSettings,{portal}))})
16
+ app.mount(root);await nextTick();await nextTick();await new Promise(r=>setTimeout(r));await nextTick()
17
+ return {root,unmount(){app.unmount();root.remove()}}
18
+ }
19
+ const buttons=(root:HTMLElement)=>[...root.querySelectorAll('button')].map(b=>b.textContent?.trim())
20
+
21
+ test('an ordinary user sees the active link and nothing to change',async()=>{
22
+ const {root,unmount}=await mount(access({...base,state:'active',alias:'my-app',url:'https://nas.example/my-app/'}))
23
+ try{
24
+ expect(root.querySelector('a')?.getAttribute('href')).toBe('https://nas.example/my-app/')
25
+ expect(root.querySelector('input')).toBeNull();expect(root.querySelector('button')).toBeNull()
26
+ }finally{unmount()}
27
+ })
28
+
29
+ test('an ordinary user without an active alias is asked to contact an administrator',async()=>{
30
+ const {root,unmount}=await mount(access({...base,state:'unavailable'}))
31
+ try{
32
+ expect(root.textContent).toContain('Ask an administrator')
33
+ expect(root.querySelector('a,input,button')).toBeNull()
34
+ }finally{unmount()}
35
+ })
36
+
37
+ test('an administrator applies the prefilled alias and sees DSM refusals as portal text',async()=>{
38
+ const apply=vi.fn(async()=>Promise.reject({code:'portal_alias_in_use'}))
39
+ const portal=access({...base,canManage:true},{apply})
40
+ const {root,unmount}=await mount(portal)
41
+ try{
42
+ const input=root.querySelector('input')!
43
+ expect(input.value).toBe('my-app')
44
+ root.querySelector('form')!.dispatchEvent(new Event('submit',{cancelable:true}))
45
+ await new Promise(r=>setTimeout(r));await nextTick()
46
+ expect(apply).toHaveBeenCalledWith('my-app')
47
+ expect(root.querySelector('[role="alert"]')?.textContent).toContain('Alias in use')
48
+ ;[...root.querySelectorAll('button')].find(b=>b.textContent?.includes('Advanced'))!.click()
49
+ expect(portal.openControlPanel).toHaveBeenCalled()
50
+ }finally{unmount()}
51
+ })
52
+
53
+ test('an administrator turns an active alias off',async()=>{
54
+ const portal=access({...base,state:'active',alias:'my-app',url:'https://nas.example/my-app/',canManage:true})
55
+ const {root,unmount}=await mount(portal)
56
+ try{
57
+ expect(root.querySelector('a')?.getAttribute('href')).toBe('https://nas.example/my-app/')
58
+ ;[...root.querySelectorAll('button')].find(b=>b.textContent?.includes('Turn off'))!.click()
59
+ await nextTick()
60
+ expect(portal.disable).toHaveBeenCalled()
61
+ }finally{unmount()}
62
+ })
63
+
64
+ test('an administrator without DSM portal support gets only the Control Panel shortcut',async()=>{
65
+ const portal=access({...base,state:'unavailable',controlPanel:true})
66
+ const {root,unmount}=await mount(portal)
67
+ try{
68
+ expect(root.querySelector('input')).toBeNull()
69
+ expect(buttons(root)).toEqual(['Open Control Panel'])
70
+ }finally{unmount()}
71
+ })
@@ -0,0 +1,50 @@
1
+ // @vitest-environment jsdom
2
+ // @vitest-environment-options {"url":"https://nas.test"}
3
+ import {expect,test,vi} from 'vitest'
4
+ import {nextTick} from 'vue'
5
+ import {mount} from '../src/main'
6
+ import type {Context} from '../src/context'
7
+ import {appCopy} from '../src/i18n'
8
+ import {identityCopy} from '../src/identity/i18n'
9
+ async function settle(){for(let i=0;i<20;i++){await Promise.resolve();await nextTick()}}
10
+ const buttons=(root:HTMLElement)=>[...root.querySelectorAll('button')].map(b=>b.textContent?.trim())
11
+ function start(locale:string,call:(method:string)=>Promise<unknown>){
12
+ const ctx={locale,call:vi.fn(call),reopen(){return ()=>{}}} as unknown as Context
13
+ const root=document.createElement('div');document.body.append(root)
14
+ const app=mount(root,ctx)
15
+ return {root,close(){app.unmount();root.remove()}}
16
+ }
17
+ test('the status page and its errors follow the DSM language',async()=>{
18
+ const copy=appCopy('ger')
19
+ const {root,close}=start('de',async method=>{
20
+ if(method==='app.status')return {package:'Pkg',version:'1',user:'admin',admin:true}
21
+ if(method==='greeting.get')throw {code:'admin_required'}
22
+ throw {code:'unknown_method'}
23
+ })
24
+ try{
25
+ await settle()
26
+ expect(buttons(root)).toContain(copy.refresh)
27
+ expect(root.textContent).toContain(copy.statusTitle)
28
+ expect(root.textContent).toContain(copy.greeting)
29
+ expect(root.textContent).toContain(copy.errors.admin_required)
30
+ expect(root.querySelector('[aria-label]')?.getAttribute('aria-label')).not.toBe('Package settings')
31
+ }finally{close()}
32
+ })
33
+ test('unknown failures name the code in the DSM language',()=>{
34
+ expect(appCopy('ja').errors.request_failed).toContain('{code}')
35
+ })
36
+ test('the sign-in page and its session notices follow the DSM language',async()=>{
37
+ const copy=identityCopy('jpn')
38
+ const {root,close}=start('ja',async method=>{
39
+ if(method==='identity.status')return {enabled:true}
40
+ if(method==='identity.session')return {user:'alice',uid:'1',expiresAt:new Date(Date.now()-1000).toISOString()}
41
+ throw {code:'session_required'}
42
+ })
43
+ try{
44
+ await settle()
45
+ expect(root.textContent).toContain(copy.title)
46
+ expect(buttons(root)).toEqual(expect.arrayContaining([copy.signIn,copy.continueDSM]))
47
+ expect(root.querySelector('[role="status"]')?.textContent).toBe(copy.notices.expired)
48
+ expect(root.textContent).not.toMatch(/Sign in|Account|Password|session/)
49
+ }finally{close()}
50
+ })
package/package.json CHANGED
@@ -1,13 +1,17 @@
1
1
  {
2
2
  "name": "@synopackageland/cli",
3
- "version": "0.6.1",
3
+ "version": "0.7.0",
4
4
  "description": "Syno Package Land Developer Kit CLI",
5
5
  "type": "module",
6
6
  "bin": {
7
7
  "synopkgland": "./dist/bin.js"
8
8
  },
9
+ "engines": {
10
+ "node": ">=22.12"
11
+ },
9
12
  "files": [
10
13
  "dist",
14
+ "CHANGELOG.md",
11
15
  "templates",
12
16
  "schema",
13
17
  "skills"
@@ -0,0 +1,101 @@
1
+ {
2
+ "$schema": "http://json-schema.org/draft-07/schema#",
3
+ "type": "object",
4
+ "additionalProperties": false,
5
+ "properties": {
6
+ "changelog": {
7
+ "oneOf": [
8
+ {
9
+ "type": "string",
10
+ "maxLength": 16000
11
+ },
12
+ {
13
+ "type": "object",
14
+ "maxProperties": 32,
15
+ "additionalProperties": {
16
+ "type": "string",
17
+ "maxLength": 16000
18
+ }
19
+ }
20
+ ]
21
+ },
22
+ "contact_email": {
23
+ "type": "string",
24
+ "maxLength": 16000,
25
+ "pattern": "^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$"
26
+ },
27
+ "support_email": {
28
+ "type": "string",
29
+ "maxLength": 16000,
30
+ "pattern": "^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$"
31
+ },
32
+ "homepage": {
33
+ "type": "string",
34
+ "maxLength": 16000,
35
+ "pattern": "^https?://"
36
+ },
37
+ "source_url": {
38
+ "type": "string",
39
+ "maxLength": 16000,
40
+ "pattern": "^https?://"
41
+ },
42
+ "issues_url": {
43
+ "type": "string",
44
+ "maxLength": 16000,
45
+ "pattern": "^https?://"
46
+ },
47
+ "license": {
48
+ "type": "string",
49
+ "maxLength": 16000
50
+ },
51
+ "maintainer_url": {
52
+ "type": "string",
53
+ "maxLength": 16000,
54
+ "pattern": "^https?://"
55
+ },
56
+ "distributor": {
57
+ "type": "string",
58
+ "maxLength": 16000
59
+ },
60
+ "distributor_url": {
61
+ "type": "string",
62
+ "maxLength": 16000,
63
+ "pattern": "^https?://"
64
+ },
65
+ "support_url": {
66
+ "type": "string",
67
+ "maxLength": 16000,
68
+ "pattern": "^(https?://|mailto:)"
69
+ },
70
+ "helpurl": {
71
+ "type": "string",
72
+ "maxLength": 16000,
73
+ "pattern": "^https?://"
74
+ },
75
+ "long_description": {
76
+ "oneOf": [
77
+ {
78
+ "type": "string",
79
+ "maxLength": 16000
80
+ },
81
+ {
82
+ "type": "object",
83
+ "maxProperties": 32,
84
+ "additionalProperties": {
85
+ "type": "string",
86
+ "maxLength": 16000
87
+ }
88
+ }
89
+ ]
90
+ },
91
+ "screenshots": {
92
+ "type": "array",
93
+ "maxItems": 12,
94
+ "items": {
95
+ "type": "string",
96
+ "pattern": "^https?://",
97
+ "maxLength": 2048
98
+ }
99
+ }
100
+ }
101
+ }
@@ -118,6 +118,18 @@
118
118
  "pattern": "^(?:[A-Za-z0-9._-]{1,64}|[A-Za-z0-9._-]{1,64}\\\\[A-Za-z0-9._-](?:[A-Za-z0-9._ -]{0,126}[A-Za-z0-9._-])?|[A-Za-z0-9._-](?:[A-Za-z0-9._ -]{0,126}[A-Za-z0-9._-])?@[A-Za-z0-9._-]{1,64})$(?![\\s\\S])"
119
119
  },
120
120
  "description": "DSM group names allowed in; only with access: dsm-groups."
121
+ },
122
+ "portal": {
123
+ "type": "object",
124
+ "properties": {
125
+ "alias": {
126
+ "type": "string",
127
+ "pattern": "^[a-z0-9](?:[a-z0-9-]{0,62}[a-z0-9])?$",
128
+ "description": "Default DSM Application Portal alias (https://<nas>/<alias>/). Omit to use the package name. An administrator still applies it."
129
+ }
130
+ },
131
+ "additionalProperties": false,
132
+ "description": "Register the App in DSM Control Panel > Login Portal > Applications. Needs launch: dsm-window behind the DSM gateway (access other than public) and os_min_ver 7.3.2-86009 or later. Registration alone exposes nothing."
121
133
  }
122
134
  },
123
135
  "additionalProperties": true
@@ -149,6 +161,106 @@
149
161
  "type": "boolean"
150
162
  }
151
163
  }
164
+ },
165
+ "metadata": {
166
+ "type": "object",
167
+ "additionalProperties": false,
168
+ "properties": {
169
+ "changelog": {
170
+ "oneOf": [
171
+ {
172
+ "type": "string",
173
+ "maxLength": 16000
174
+ },
175
+ {
176
+ "type": "object",
177
+ "maxProperties": 32,
178
+ "additionalProperties": {
179
+ "type": "string",
180
+ "maxLength": 16000
181
+ }
182
+ }
183
+ ]
184
+ },
185
+ "contact_email": {
186
+ "type": "string",
187
+ "maxLength": 16000,
188
+ "pattern": "^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$"
189
+ },
190
+ "support_email": {
191
+ "type": "string",
192
+ "maxLength": 16000,
193
+ "pattern": "^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$"
194
+ },
195
+ "homepage": {
196
+ "type": "string",
197
+ "maxLength": 16000,
198
+ "pattern": "^https?://"
199
+ },
200
+ "source_url": {
201
+ "type": "string",
202
+ "maxLength": 16000,
203
+ "pattern": "^https?://"
204
+ },
205
+ "issues_url": {
206
+ "type": "string",
207
+ "maxLength": 16000,
208
+ "pattern": "^https?://"
209
+ },
210
+ "license": {
211
+ "type": "string",
212
+ "maxLength": 16000
213
+ },
214
+ "maintainer_url": {
215
+ "type": "string",
216
+ "maxLength": 16000,
217
+ "pattern": "^https?://"
218
+ },
219
+ "distributor": {
220
+ "type": "string",
221
+ "maxLength": 16000
222
+ },
223
+ "distributor_url": {
224
+ "type": "string",
225
+ "maxLength": 16000,
226
+ "pattern": "^https?://"
227
+ },
228
+ "support_url": {
229
+ "type": "string",
230
+ "maxLength": 16000,
231
+ "pattern": "^(https?://|mailto:)"
232
+ },
233
+ "helpurl": {
234
+ "type": "string",
235
+ "maxLength": 16000,
236
+ "pattern": "^https?://"
237
+ },
238
+ "long_description": {
239
+ "oneOf": [
240
+ {
241
+ "type": "string",
242
+ "maxLength": 16000
243
+ },
244
+ {
245
+ "type": "object",
246
+ "maxProperties": 32,
247
+ "additionalProperties": {
248
+ "type": "string",
249
+ "maxLength": 16000
250
+ }
251
+ }
252
+ ]
253
+ },
254
+ "screenshots": {
255
+ "type": "array",
256
+ "maxItems": 12,
257
+ "items": {
258
+ "type": "string",
259
+ "pattern": "^https?://",
260
+ "maxLength": 2048
261
+ }
262
+ }
263
+ }
152
264
  }
153
265
  },
154
266
  "additionalProperties": true
package/skills/SKILL.md CHANGED
@@ -5,15 +5,15 @@ description: Initialize, extend, check, build, inspect, and deploy Synology appl
5
5
 
6
6
  # Independent Synology SPKs
7
7
 
8
- Use this guide to initialize, build and inspect own applications or upstream OSS as independent packages. 依已實作能力區分 DSM session 檔案、私有 jobs 與 App 登入;delegate 已實作並隨 CLI 提供固定操作的離線委託;支援邊界與真機驗收依 delegate guide/驗收矩陣,禁止發明新的身分平台。
8
+ Use this guide to initialize, build and inspect own applications or upstream OSS as independent packages. 依已實作能力區分 DSM session 檔案、私有 jobs 與 App 登入;delegate 已實作並隨 CLI 提供固定操作的離線委託(預設 `files.moveFromStaging`);共用 delegate/rootOps executor 的完整裝置生命週期驗收仍待完成,不得宣稱已通過;支援邊界依 delegate guide/support matrix,禁止發明新的身分平台。
9
9
 
10
10
  Start with `synopkgland init -t native-go`, `synopkgland init -t binary`, or `synopkgland init -t compose`. Read reference/create.md or reference/wrap.md, then reference/deploy.md. Native CGI session checks belong to the package. Container authentication belongs to upstream; fixed service credentials govern mounted data access.
11
11
 
12
12
  Never install, uninstall, publish or modify NAS data without task authorization. Prefer check, inspect and deploy --dry-run for reviewable artifacts.
13
13
 
14
- Native projects use schema 2 capabilities and `--with files,jobs,login,root-ops`; use `add` for existing projects. Prefer `check --json`, `build --json`, `inspect <spk> --json`, `deploy --dry-run --json`, and `status --host <profile> --json` for structured results. Read the conflict policy in reference/create.md before adding overlays.
14
+ Native projects use schema 2 capabilities and `--with files,jobs,login,root-ops,delegate`; use `add` for existing projects. Prefer `check --json`, `build --json`, `inspect <spk> --json`, `deploy --dry-run --json`, and `status --host <profile> --json` for structured results. Read the conflict policy in reference/create.md before adding overlays.
15
15
 
16
- Read reference/files.md and reference/identity.md for capability boundaries. RootOps and delegate share one-time protected executor boot/control setup with digest approval. Existing native products can explicitly use the separate authorization-sdk build-time adapter; do not mix its tasks with generated executor capabilities. App sessions alone do not grant NAS file authority.
16
+ Read reference/portal.md before declaring or changing a DSM Application Portal alias (`add portal`); DSM owns the saved setting. Read reference/files.md and reference/identity.md for capability boundaries. RootOps and delegate share one-time protected executor boot/control setup with digest approval. Existing native products can explicitly use the separate authorization-sdk build-time adapter; do not mix its tasks with generated executor capabilities. App sessions alone do not grant NAS file authority.
17
17
 
18
18
  Read [reference/ui-vue.md](reference/ui-vue.md) when editing Vue components in the native-go Vue template; use the installed SDK contracts, keep DSM embedding boundaries, and verify the actual app.
19
19
 
@@ -26,4 +26,106 @@ Use `syno-pkg-land-ui` directly in npm scripts; keep AI guidance version-indepen
26
26
 
27
27
  For existing native products, schema 2 `backend: {kind: native}` preserves project-owned payload/metadata, prepare/compile argv hooks and pinned toolkits. See the native-layout guide; run `check`, `build`, then `inspect <spk>` locally. Keep lifecycle/systemd as the package account and preserve product-owned digest approval/revocation. Native layouts do not opt into generated Go capabilities.
28
28
 
29
- Delegate is for non-administrator subjects only and currently cannot combine with daemon. Gopeed-style completed-download integration needs an App-owned fixed worker and durable job/delegation binding; the shipped URL example is limited to 64 MiB / 60 seconds and grants to one hour. Scope is a usage restriction, not hard subtree isolation against user-controlled directory moves. Do not claim a daemon-based administrator-only app can simply enable delegate.
29
+
30
+ ## Permanent destination integration
31
+
32
+ `init --with delegate` / `add delegate` default to `operations: [files.moveFromStaging]`,
33
+ `destinations: [save]`, `lifetime: until-revoked`, `scope: install-selected`. Existing legacy
34
+ TTL declarations retain their 1–3600 second service / 30-day wire ceiling and
35
+ 64 MiB / 60-second URL example. Do not apply those limits to the new move API.
36
+ Delegate can coexist with daemon; ordinary standalone jobs cannot. Preserve
37
+ App business route floors. Binary/compose remain unsupported.
38
+
39
+ Use File Station `/home[/...]`, never `/homes/<user>`. The SDK destination picker
40
+ selection itself grants access after fresh DSM session/UID/write verification,
41
+ without a second consent dialog. Ordinary and administrator subjects use their
42
+ own nonzero UID/current groups, never root. An online App can create an in-scope
43
+ grant using a valid session; there is no trusted external consent or notification.
44
+
45
+ Use the default `createBackgroundSaveFacade(ctx)` / CapabilityPanels management
46
+ page. `delegate.status` exposes verified `disabled` and manifest mode; missing
47
+ protected evidence must not imply enabled. `delegate.operations.list` accepts
48
+ only `{}`, uses the backend move mirrors, and returns the caller's username+UID
49
+ operations only, including for admins. Mirrors are not completion receipts:
50
+ query original `delegate.operations.get` and show Saved only for committed with
51
+ a receipt. Cancel by the same reference; revoke by grant ID. Never resubmit
52
+ uncertain/interrupted work. Admin disable is protected and revokes grants;
53
+ enable/mailbox start does not resurrect them. Keep IDs, never session credentials.
54
+
55
+ App-owned daemon completion and scheduled CSV export call configured Go
56
+ `Service.MoveFromStaging` with `delegation_id`, stable `job_id`, private relative
57
+ `staging`, `name`, `size`. No owner/UID selector. Bounds: 16 GiB/file, queued16,
58
+ active1/package, 24-hour attempt, 5-second revalidation ticks (one check at a time, 30-second complete-check deadline; proven timeouts retain private copying, publication still revalidates), 10 MiB operation records,
59
+ 50 MiB audit. Scope is a usage restriction, not hard subtree confinement.
60
+ Local production-executor fixtures use fake DSM/peer/bulk writer; do not claim
61
+ NAS ACL, actual writer UID, logout/reboot or full lifecycle acceptance from them.
62
+ Gopeed product wiring remains a separate task.
63
+
64
+ Admission recovery (per job): on `EXECUTOR_UNAVAILABLE`, `EXECUTOR_TIMEOUT`,
65
+ `EXECUTOR_PROTOCOL`, `canceled`, `EXECUTOR_PAUSED`, `queue-full`,
66
+ `STORAGE_ERROR`, `ADMISSION_PENDING`, `jobs_busy` or `jobs_unavailable` (the
67
+ package journal was busy or failed transiently; a caller deadline or cancel
68
+ spent waiting for it is `canceled`), call `MoveFromStaging` again later with
69
+ the same exact params. It queries the original reference first and re-sends
70
+ admission only when the executor does not know it and the pinned source is
71
+ unchanged; executor tuple deduplication keeps one operation, so a lost reply or a
72
+ racing caller never copies twice. At most 10 possibly-delivered sends, 2 s
73
+ backoff doubling to 5 min; the worker sweep does the same. Terminal, do not
74
+ retry: `APP_DENIED`, `DELEGATION_DENIED`, `operation-conflict`, `disabled`,
75
+ `SCOPE_DENIED` and other executor refusals, `STAGING_CHANGED`,
76
+ `ADMISSION_EXHAUSTED`, `spool_unsafe` and `journal_corrupt` (repair the package
77
+ spool first), and `operation-not-found` for an accepted operation.
78
+ Never change job/grant IDs, delete mirrors or call the socket directly to retry.
79
+
80
+ Cancellation (per job): `CancelOperation` / `delegate.operations.cancel` with the
81
+ original ref is idempotent; repeat it with the same ref after any retryable
82
+ error or a lost reply. A reply `queued`→`canceled` is final at once. A copy in
83
+ flight answers `copying` with code `CANCEL_REQUESTED`: the cancel is accepted and
84
+ durable, not finished; poll `OperationStatus` on the same ref until terminal
85
+ (repeating the cancel is harmless). `canceled` (`OPERATION_CANCELED`) guarantees the writer
86
+ stopped, its part file is removed, nothing was published and staging is kept; it
87
+ stays canceled across executor crash, package stop/start and logout. Revoke, scope change and disable store their reason (`grant-revoked`,
88
+ `scope-changed`, `integration-disabled`) the same way: a copy in flight answers
89
+ `CANCEL_REQUESTED` with it and settles `canceled` with it across a restart. A
90
+ copy the executor stops without a cancel ends `interrupted`/`EXECUTOR_PAUSED`
91
+ (`interrupted`/`interrupted` after a crash), never `WRITE_FAILED`. `committed`
92
+ means publication won the race: the file stays (cancel never removes it) and only
93
+ the app record may be dropped. `uncertain` means a publish attempt failed before
94
+ the cancel; keep querying. A cancel of a ref the executor has not admitted yet
95
+ (reserved/sent mirror) returns `canceled` and leaves a tombstone, so a late or
96
+ recovered admission of that ref returns `canceled` and never copies; a producer
97
+ cancel is also written to the mirror first, so `MoveFromStaging` and the worker
98
+ sweep forward the cancel instead of admitting it.
99
+
100
+
101
+ ### Install-selected scope and the destination API
102
+
103
+ - The DSM install wizard dynamically lists local shares (`SYNO.Core.Share list`
104
+ via `api_store`) and offers home. It saves only a package-owned pre-selection
105
+ in `var/delegate-install.json`; never trust that file for authority.
106
+ - The existing administrator PasswordConfirm form displays this pre-selection
107
+ at first enable. `ctx.executor.authorize(password, selectedScope)` confirms it
108
+ through a pinned, exact-readback one-time root task. Settings changes use the
109
+ same form/password path; no App CGI route may set protected scope directly.
110
+ - The executor trusts `/usr/local/etc/synopkgland/<pkg>/delegations/selected-scope.record`
111
+ only. Its scope/generation are runtime protected state; shipped policy pins
112
+ lock the selection mode and operations. Changes revoke old grants and cancel
113
+ pending writes. Change-away-and-back never revives an old grant.
114
+ - Follow `selection` from verified `delegate.status` / `sdk.destination(key)`.
115
+ If the draft differs, display the difference and follow the protected value.
116
+ Home and fixed `shared:<name>` manifests stay compatible.
117
+ - Frontend: `const sdk = createBackgroundSaveFacade(ctx); const current = await
118
+ sdk.destination('save')` returns typed `{key, selection, destination, status}`.
119
+ `selection` contains `{scope, root, generation}`; `destination` is this caller's
120
+ grant or null. Handle active/missing/reselection-required/needs-scope-confirmation/disabled.
121
+ - Go: `Service.Destination(ctx, caller, request, key)` verifies the fresh DSM
122
+ session and username+UID. Capture its active grant ID into the App job. The
123
+ offline producer uses existing `Service.MoveFromStaging`; no user/UID selector,
124
+ cookie, token, or browser path cache. Executor revalidates at use/publication.
125
+ - The generated Background saves page includes a working **Save example file**
126
+ (`sdk.exportExample({key, job_id})`, `delegate.example.export`). Go
127
+ `ExportExample` creates a private JSON file, calls `MoveFromStaging` once, and
128
+ repeats the original request on repeated IDs. Review `example.go` to
129
+ adapt a real producer; show Saved only from a protected committed receipt.
130
+ - Wizard support is measured on vt-nas DSM 7.4 via a session-only SSH local
131
+ forward. This is not proof of other DSM releases or full writer/reboot behavior.
@@ -27,8 +27,107 @@ synopkgland init -t compose --from ./compose.yaml upstream-app
27
27
  ## Schema 2 能力
28
28
 
29
29
  native-go 與 binary 使用 `dsm-package.yaml` 的 `schema: 2`,backend 寫 `kind: go, dir: backend` 或 `kind: binary, files: {x86_64: prebuilt/api-x86_64}`。
30
- `--with files,jobs,login,root-ops` 分別宣告 `capabilities.files: session`、`jobs: true`、`identity: standalone`、`rootOps: true`。jobs 可寫 `{worker: sha256-v1}`,與 daemon 互斥;delegate 尚未實作(P2)。binary/compose 不支援 files/jobs/login/delegate。
30
+ `--with files,jobs,login,root-ops,delegate` 分別宣告 `capabilities.files: session`、`jobs: true`、`identity: standalone`、`rootOps: true`、`delegate: {operations: [files.moveFromStaging], destinations: [save], lifetime: until-revoked, scope: install-selected}`。jobs 可寫 `{worker: sha256-v1}`,與 daemon 互斥;delegate 隱含 worker 並可與 daemon 共存;admin/normal 都以本人 UID 執行。binary/compose 不支援 files/jobs/login/delegate。
31
31
  既有 native-go 專案用 `synopkgland add files jobs login`;已存在能力回報 no-op。overlay 檔案已有不同內容或 stub 被改動時,add 拒絕並列出衝突;不修改業務 routes 或 App.vue。
32
32
  check、inspect、build、deploy、status 支援 `--json`,stdout 只有單一 JSON 物件,錯誤包含 code、message、hint;進度在 stderr。
33
33
 
34
- 目前 rootOps 仍需 native-Go 管理員核准 adapter;P2 改為與 delegate 共用一次密碼 executor 設定,仍待整合。SDK off stub 與 routes_test.go 的預設 policy scaffold 只有內容仍等於 bundled base 時才會替換;App 自訂版本拒絕。
34
+ rootOps 與 delegate 已共用管理員 PasswordConfirm 核准的 protected executor、boot/control tasks;精確 task readback 與 digest health 一致才 ready,pins 變更須重核准。native 產品另可明確 opt-in authorization-sdk adapter,不與 generated executor tasks 混用。SDK off stub 與 routes_test.go 的預設 policy scaffold 只有內容仍等於 bundled base 時才會替換;App 自訂版本拒絕。
35
+
36
+ ## Permanent destination integration
37
+
38
+ `init --with delegate` / `add delegate` default to `operations: [files.moveFromStaging]`,
39
+ `destinations: [save]`, `lifetime: until-revoked`, `scope: install-selected`. Existing legacy
40
+ TTL declarations retain their 1–3600 second service / 30-day wire ceiling and
41
+ 64 MiB / 60-second URL example. Do not apply those limits to the new move API.
42
+ Delegate can coexist with daemon; ordinary standalone jobs cannot. Preserve
43
+ App business route floors. Binary/compose remain unsupported.
44
+
45
+ Use File Station `/home[/...]`, never `/homes/<user>`. The SDK destination picker
46
+ selection itself grants access after fresh DSM session/UID/write verification,
47
+ without a second consent dialog. Ordinary and administrator subjects use their
48
+ own nonzero UID/current groups, never root. An online App can create an in-scope
49
+ grant using a valid session; there is no trusted external consent or notification.
50
+
51
+ Use the default `createBackgroundSaveFacade(ctx)` / CapabilityPanels management
52
+ page. `delegate.status` exposes verified `disabled` and manifest mode; missing
53
+ protected evidence must not imply enabled. `delegate.operations.list` accepts
54
+ only `{}`, uses the backend move mirrors, and returns the caller's username+UID
55
+ operations only, including for admins. Mirrors are not completion receipts:
56
+ query original `delegate.operations.get` and show Saved only for committed with
57
+ a receipt. Cancel by the same reference; revoke by grant ID. Never resubmit
58
+ uncertain/interrupted work. Admin disable is protected and revokes grants;
59
+ enable/mailbox start does not resurrect them. Keep IDs, never session credentials.
60
+
61
+ App-owned daemon completion and scheduled CSV export call configured Go
62
+ `Service.MoveFromStaging` with `delegation_id`, stable `job_id`, private relative
63
+ `staging`, `name`, `size`. No owner/UID selector. Bounds: 16 GiB/file, queued16,
64
+ active1/package, 24-hour attempt, 5-second revalidation ticks (one check at a time, 30-second complete-check deadline; proven timeouts retain private copying, publication still revalidates), 10 MiB operation records,
65
+ 50 MiB audit. Scope is a usage restriction, not hard subtree confinement.
66
+ Local production-executor fixtures use fake DSM/peer/bulk writer; do not claim
67
+ NAS ACL, actual writer UID, logout/reboot or full lifecycle acceptance from them.
68
+ Gopeed product wiring remains a separate task.
69
+
70
+ Admission recovery (per job): on `EXECUTOR_UNAVAILABLE`, `EXECUTOR_TIMEOUT`,
71
+ `EXECUTOR_PROTOCOL`, `canceled`, `EXECUTOR_PAUSED`, `queue-full`,
72
+ `STORAGE_ERROR`, `ADMISSION_PENDING`, `jobs_busy` or `jobs_unavailable` (the
73
+ package journal was busy or failed transiently; a caller deadline or cancel
74
+ spent waiting for it is `canceled`), call `MoveFromStaging` again later with
75
+ the same exact params. It queries the original reference first and re-sends
76
+ admission only when the executor does not know it and the pinned source is
77
+ unchanged; executor tuple deduplication keeps one operation, so a lost reply or a
78
+ racing caller never copies twice. At most 10 possibly-delivered sends, 2 s
79
+ backoff doubling to 5 min; the worker sweep does the same. Terminal, do not
80
+ retry: `APP_DENIED`, `DELEGATION_DENIED`, `operation-conflict`, `disabled`,
81
+ `SCOPE_DENIED` and other executor refusals, `STAGING_CHANGED`,
82
+ `ADMISSION_EXHAUSTED`, `spool_unsafe` and `journal_corrupt` (repair the package
83
+ spool first), and `operation-not-found` for an accepted operation.
84
+ Never change job/grant IDs, delete mirrors or call the socket directly to retry.
85
+
86
+ Cancellation (per job): `CancelOperation` / `delegate.operations.cancel` with the
87
+ original ref is idempotent; repeat it with the same ref after any retryable
88
+ error or a lost reply. A reply `queued`→`canceled` is final at once. A copy in
89
+ flight answers `copying` with code `CANCEL_REQUESTED`: the cancel is accepted and
90
+ durable, not finished; poll `OperationStatus` on the same ref until terminal
91
+ (repeating the cancel is harmless). `canceled` (`OPERATION_CANCELED`) guarantees the writer
92
+ stopped, its part file is removed, nothing was published and staging is kept; it
93
+ stays canceled across executor crash, package stop/start and logout. `committed`
94
+ means publication won the race: the file stays (cancel never removes it) and only
95
+ the app record may be dropped. `uncertain` means a publish attempt failed before
96
+ the cancel; keep querying. A cancel of a ref the executor has not admitted yet
97
+ (reserved/sent mirror) returns `canceled` and leaves a tombstone, so a late or
98
+ recovered admission of that ref returns `canceled` and never copies; a producer
99
+ cancel is also written to the mirror first, so `MoveFromStaging` and the worker
100
+ sweep forward the cancel instead of admitting it.
101
+
102
+
103
+ ### Install-selected scope and the destination API
104
+
105
+ - The DSM install wizard dynamically lists local shares (`SYNO.Core.Share list`
106
+ via `api_store`) and offers home. It saves only a package-owned pre-selection
107
+ in `var/delegate-install.json`; never trust that file for authority.
108
+ - The existing administrator PasswordConfirm form displays this pre-selection
109
+ at first enable. `ctx.executor.authorize(password, selectedScope)` confirms it
110
+ through a pinned, exact-readback one-time root task. Settings changes use the
111
+ same form/password path; no App CGI route may set protected scope directly.
112
+ - The executor trusts `/usr/local/etc/synopkgland/<pkg>/delegations/selected-scope.record`
113
+ only. Its scope/generation are runtime protected state; shipped policy pins
114
+ lock the selection mode and operations. Changes revoke old grants and cancel
115
+ pending writes. Change-away-and-back never revives an old grant.
116
+ - Follow `selection` from verified `delegate.status` / `sdk.destination(key)`.
117
+ If the draft differs, display the difference and follow the protected value.
118
+ Home and fixed `shared:<name>` manifests stay compatible.
119
+ - Frontend: `const sdk = createBackgroundSaveFacade(ctx); const current = await
120
+ sdk.destination('save')` returns typed `{key, selection, destination, status}`.
121
+ `selection` contains `{scope, root, generation}`; `destination` is this caller's
122
+ grant or null. Handle active/missing/reselection-required/needs-scope-confirmation/disabled.
123
+ - Go: `Service.Destination(ctx, caller, request, key)` verifies the fresh DSM
124
+ session and username+UID. Capture its active grant ID into the App job. The
125
+ offline producer uses existing `Service.MoveFromStaging`; no user/UID selector,
126
+ cookie, token, or browser path cache. Executor revalidates at use/publication.
127
+ - The generated Background saves page includes a working **Save example file**
128
+ (`sdk.exportExample({key, job_id})`, `delegate.example.export`). Go
129
+ `ExportExample` creates a private JSON file, calls `MoveFromStaging` once, and
130
+ repeats the original request on repeated IDs. Review `example.go` to
131
+ adapt a real producer; show Saved only from a protected committed receipt.
132
+ - Wizard support is measured on vt-nas DSM 7.4 via a session-only SSH local
133
+ forward. This is not proof of other DSM releases or full writer/reboot behavior.
@@ -1,6 +1,6 @@
1
1
  # Build and deploy
2
2
 
3
- 1. Run `synopkgland check` and `synopkgland build --arch x86_64` (use aarch64 for that target).
3
+ 1. Run `synopkgland check` and `synopkgland build --arch x86_64` (use aarch64 or armv7 for those targets; armv7 must be listed in `arches`; its jobs, daemon and executor are built but not NAS-verified). deploy detects the NAS arch, except on Marvell CPUs, where it asks for `--arch`.
4
4
  2. Inspect the resulting file with `synopkgland inspect <file>`.
5
5
  3. Review `synopkgland deploy --dry-run --arch x86_64`, which does not connect to a NAS.
6
6
  4. Only when installation is authorized, run `synopkgland deploy --host <profile>`.
@@ -9,4 +9,4 @@ Deployment uploads directly to DSM. It does not publish artifacts. Container ins
9
9
 
10
10
  For native-go root-ops, approve fixed operations in DSM after installation. Updates that change the task text require fresh approval. Remove tasks before uninstall. Never delete installed packages or data as part of a code cleanup.
11
11
 
12
- 5. After installation run `synopkgland status --host <profile> --json`; inspect every capability state and Open URL. Installation is not readiness. P2 shared executor setup, integrated reboot/upgrade and cleanup still need acceptance. Delete App Data is not evidence of complete cleanup; verify retained directories/account artifacts.
12
+ 5. After installation run `synopkgland status --host <profile> --json`; inspect every capability state and Open URL. Installation is not readiness. Delegate is implemented and shipped (default `files.moveFromStaging`); full device lifecycle acceptance is still pending for the shared delegate/rootOps executor: install → setup → reboot → upgrade → revoke → uninstall. Delete App Data is not evidence of complete cleanup; verify retained directories/account artifacts.