@synopackageland/cli 0.6.1 → 0.7.1

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 (640) hide show
  1. package/CHANGELOG.md +846 -0
  2. package/README.md +81 -89
  3. package/dist/add.d.ts +4 -1
  4. package/dist/add.js +45 -17
  5. package/dist/build-spk/delegate-scope.d.ts +6 -0
  6. package/dist/build-spk/delegate-scope.js +34 -0
  7. package/dist/build-spk/dsm-arch.d.ts +7 -1
  8. package/dist/build-spk/dsm-arch.js +24 -28
  9. package/dist/build-spk/dsm-platforms.json +42 -0
  10. package/dist/build-spk/dsm-version.d.ts +11 -0
  11. package/dist/build-spk/dsm-version.js +17 -0
  12. package/dist/build-spk/gateway.js +8 -4
  13. package/dist/build-spk/generator.js +30 -5
  14. package/dist/build-spk/go-toolchain.d.ts +20 -0
  15. package/dist/build-spk/go-toolchain.js +44 -0
  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 +28 -14
  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/native-install-values.d.ts +17 -0
  26. package/dist/deploy/native-install-values.js +32 -0
  27. package/dist/deploy/plan.d.ts +6 -2
  28. package/dist/deploy/plan.js +58 -7
  29. package/dist/deploy/spk-version.js +1 -1
  30. package/dist/error-registry.js +54 -20
  31. package/dist/host/dsm-client.d.ts +8 -4
  32. package/dist/host/dsm-client.js +63 -21
  33. package/dist/host/dsm-http.js +24 -2
  34. package/dist/host/types.d.ts +3 -1
  35. package/dist/info.js +45 -10
  36. package/dist/init.d.ts +3 -0
  37. package/dist/init.js +50 -19
  38. package/dist/package-metadata.d.ts +21 -0
  39. package/dist/package-metadata.js +101 -0
  40. package/dist/portal/alias.d.ts +25 -0
  41. package/dist/portal/alias.js +48 -0
  42. package/dist/portal/package.d.ts +33 -0
  43. package/dist/portal/package.js +116 -0
  44. package/dist/runtime/delegate-contract.d.ts +79 -0
  45. package/dist/runtime/delegate-contract.js +27 -0
  46. package/dist/runtime/dsm-version.d.ts +6 -0
  47. package/dist/runtime/dsm-version.js +8 -0
  48. package/dist/runtime/executor-build.js +10 -6
  49. package/dist/runtime/executor-inspect.d.ts +3 -0
  50. package/dist/runtime/executor-inspect.js +11 -5
  51. package/dist/runtime/executor-policy.d.ts +12 -0
  52. package/dist/runtime/executor-policy.js +43 -3
  53. package/dist/runtime/executor-scope-task.d.ts +4 -0
  54. package/dist/runtime/executor-scope-task.js +21 -0
  55. package/dist/runtime/executor-tasks.d.ts +2 -1
  56. package/dist/runtime/executor-tasks.js +4 -4
  57. package/dist/skill.js +1 -0
  58. package/dist/standalone/api-permissions.d.ts +10 -0
  59. package/dist/standalone/api-permissions.js +19 -2
  60. package/dist/standalone/background.d.ts +16 -0
  61. package/dist/standalone/background.js +69 -2
  62. package/dist/standalone/binary.d.ts +1 -1
  63. package/dist/standalone/binary.js +22 -8
  64. package/dist/standalone/build.d.ts +1 -1
  65. package/dist/standalone/build.js +38 -8
  66. package/dist/standalone/daemon.d.ts +1 -0
  67. package/dist/standalone/daemon.js +10 -5
  68. package/dist/standalone/dsm-window.d.ts +3 -1
  69. package/dist/standalone/dsm-window.js +78 -49
  70. package/dist/standalone/inspect-spk.d.ts +8 -0
  71. package/dist/standalone/inspect-spk.js +110 -27
  72. package/dist/standalone/lifecycle.js +86 -8
  73. package/dist/standalone/manifest.d.ts +13 -0
  74. package/dist/standalone/manifest.js +57 -8
  75. package/dist/standalone/native-authorization-window.d.ts +1 -1
  76. package/dist/standalone/native-authorization-window.js +9 -1
  77. package/dist/standalone/native-layout.d.ts +3 -0
  78. package/dist/standalone/native-layout.js +37 -5
  79. package/dist/standalone/native-root-window.d.ts +4 -2
  80. package/dist/standalone/native-root-window.js +33 -18
  81. package/dist/standalone/portal-window.d.ts +21 -0
  82. package/dist/standalone/portal-window.js +155 -0
  83. package/dist/standalone/portal.d.ts +4 -0
  84. package/dist/standalone/portal.js +4 -0
  85. package/dist/standalone/resource.d.ts +5 -3
  86. package/dist/standalone/resource.js +6 -3
  87. package/dist/standalone/root-window.d.ts +4 -1
  88. package/dist/standalone/root-window.js +193 -36
  89. package/dist/standalone/spk.d.ts +1 -1
  90. package/dist/standalone/spk.js +25 -7
  91. package/dist/templates/_daemon/go.mod +1 -1
  92. package/dist/templates/_daemon/main.go +6 -3
  93. package/dist/templates/_daemon/main_test.go +82 -9
  94. package/dist/templates/_daemon/testdata/child.go +3 -0
  95. package/dist/templates/_executor/README.md +130 -16
  96. package/dist/templates/_executor/anchor.go +156 -0
  97. package/dist/templates/_executor/anchor_test.go +161 -0
  98. package/dist/templates/_executor/apppriv_review_test.go +1 -1
  99. package/dist/templates/_executor/cancel_contract_test.go +236 -0
  100. package/dist/templates/_executor/copy.go +64 -0
  101. package/dist/templates/_executor/copy_test.go +88 -0
  102. package/dist/templates/_executor/digest_review_test.go +2 -2
  103. package/dist/templates/_executor/dsm.go +63 -13
  104. package/dist/templates/_executor/dsm_test.go +95 -1
  105. package/dist/templates/_executor/executor.go +260 -49
  106. package/dist/templates/_executor/executor_test.go +36 -6
  107. package/dist/templates/_executor/go.mod +2 -0
  108. package/dist/templates/_executor/identity.go +27 -1
  109. package/dist/templates/_executor/identity_kernel_linux_test.go +60 -0
  110. package/dist/templates/_executor/identity_linux.go +82 -0
  111. package/dist/templates/_executor/identity_security_test.go +43 -0
  112. package/dist/templates/_executor/internal/lifecycle/lifecycle.go +5 -0
  113. package/dist/templates/_executor/internal/lifecycle/lifecycle_test.go +61 -3
  114. package/dist/templates/_executor/internal/taskscript/boot.sh +9 -9
  115. package/dist/templates/_executor/internal/taskscript/cmd/main.go +2 -2
  116. package/dist/templates/_executor/internal/taskscript/control.sh +15 -10
  117. package/dist/templates/_executor/internal/taskscript/tasks.go +5 -3
  118. package/dist/templates/_executor/lifecycle_test.go +11 -0
  119. package/dist/templates/_executor/link_linux.go +28 -0
  120. package/dist/templates/_executor/long_running_test.go +530 -0
  121. package/dist/templates/_executor/main.go +83 -25
  122. package/dist/templates/_executor/managed.go +3 -3
  123. package/dist/templates/_executor/managed_test.go +3 -3
  124. package/dist/templates/_executor/move_linux.go +490 -0
  125. package/dist/templates/_executor/move_linux_test.go +460 -0
  126. package/dist/templates/_executor/move_validation.go +46 -0
  127. package/dist/templates/_executor/move_validation_test.go +187 -0
  128. package/dist/templates/_executor/operations.go +926 -0
  129. package/dist/templates/_executor/path_test.go +18 -0
  130. package/dist/templates/_executor/pid.go +37 -0
  131. package/dist/templates/_executor/pid_test.go +113 -0
  132. package/dist/templates/_executor/platform_linux.go +24 -173
  133. package/dist/templates/_executor/platform_linux_test.go +9 -65
  134. package/dist/templates/_executor/platform_other.go +38 -4
  135. package/dist/templates/_executor/process.go +3 -0
  136. package/dist/templates/_executor/purge_log.go +18 -0
  137. package/dist/templates/_executor/records.go +242 -0
  138. package/dist/templates/_executor/scope_cancel_contract_test.go +91 -0
  139. package/dist/templates/_executor/scope_client.go +37 -0
  140. package/dist/templates/_executor/scope_contract.go +12 -0
  141. package/dist/templates/_executor/selected_scope.go +146 -0
  142. package/dist/templates/_executor/selected_scope_test.go +186 -0
  143. package/dist/templates/_executor/status_disabled_test.go +33 -0
  144. package/dist/templates/_executor/store.go +4 -3
  145. package/dist/templates/_executor/supervise.go +109 -0
  146. package/dist/templates/_executor/supervise_test.go +130 -0
  147. package/dist/templates/_executor/task_race_review_test.go +1 -1
  148. package/dist/templates/_executor/tasks.go +52 -15
  149. package/dist/templates/_executor/tasks_dsm71_test.go +114 -0
  150. package/dist/templates/_executor/tasks_review_test.go +1 -1
  151. package/dist/templates/_executor/tasks_test.go +62 -5
  152. package/dist/templates/_executor/upgrade_watch_test.go +13 -5
  153. package/dist/templates/_executor/watch.go +8 -0
  154. package/dist/templates/_executor/watch_test.go +23 -2
  155. package/dist/templates/_executor/write_review_linux_test.go +4 -12
  156. package/dist/templates/_gateway/go.mod +2 -0
  157. package/dist/templates/_shared/AGENTS.md +129 -0
  158. package/dist/templates/binary/dsm-package.yaml +24 -0
  159. package/dist/templates/binary/i18n/messages/de.json +4 -0
  160. package/dist/templates/binary/i18n/messages/en.json +4 -0
  161. package/dist/templates/binary/i18n/messages/es.json +4 -0
  162. package/dist/templates/binary/i18n/messages/fr.json +4 -0
  163. package/dist/templates/binary/i18n/messages/it.json +4 -0
  164. package/dist/templates/binary/i18n/messages/ja.json +4 -0
  165. package/dist/templates/binary/i18n/messages/ko.json +4 -0
  166. package/dist/templates/binary/i18n/messages/zh-CN.json +4 -0
  167. package/dist/templates/binary/i18n/messages/zh-TW.json +4 -0
  168. package/dist/templates/binary/ui/app.js +19 -4
  169. package/dist/templates/native-authorization/AuthorizationGate.vue +5 -3
  170. package/dist/templates/native-authorization/i18n/de.json +1 -0
  171. package/dist/templates/native-authorization/i18n/en.json +1 -0
  172. package/dist/templates/native-authorization/i18n/es.json +2 -1
  173. package/dist/templates/native-authorization/i18n/fr.json +1 -0
  174. package/dist/templates/native-authorization/i18n/it.json +11 -10
  175. package/dist/templates/native-authorization/i18n/ja.json +1 -0
  176. package/dist/templates/native-authorization/i18n/ko.json +10 -9
  177. package/dist/templates/native-authorization/i18n/zh-CN.json +6 -5
  178. package/dist/templates/native-authorization/i18n/zh-TW.json +6 -5
  179. package/dist/templates/native-go/README.md +43 -10
  180. package/dist/templates/native-go/backend/cmd/app/main.go +27 -7
  181. package/dist/templates/native-go/backend/go.mod +1 -1
  182. package/dist/templates/native-go/backend/internal/api/entry_test.go +47 -0
  183. package/dist/templates/native-go/backend/internal/api/permissions_test.go +3 -3
  184. package/dist/templates/native-go/backend/internal/api/registry.go +11 -0
  185. package/dist/templates/native-go/backend/internal/api/sdk-permissions.json +14 -1
  186. package/dist/templates/native-go/backend/internal/api/server.go +18 -5
  187. package/dist/templates/native-go/backend/internal/app/delegate_config.go +57 -0
  188. package/dist/templates/native-go/backend/internal/app/delegate_config_test.go +28 -0
  189. package/dist/templates/native-go/backend/internal/app/portal.go +30 -0
  190. package/dist/templates/native-go/backend/internal/app/portal_test.go +55 -0
  191. package/dist/templates/native-go/backend/internal/app/routes.go +8 -2
  192. package/dist/templates/native-go/backend/internal/background/runner.go +1 -1
  193. package/dist/templates/native-go/backend/internal/background/store.go +16 -14
  194. package/dist/templates/native-go/backend/internal/dsm/admin.go +8 -2
  195. package/dist/templates/native-go/backend/internal/dsm/admin_linux.go +43 -13
  196. package/dist/templates/native-go/backend/internal/dsm/admin_linux_test.go +56 -0
  197. package/dist/templates/native-go/backend/internal/dsm/admin_other.go +4 -1
  198. package/dist/templates/native-go/backend/internal/dsm/apppriv.go +30 -0
  199. package/dist/templates/native-go/backend/internal/dsm/bridge_file.go +75 -0
  200. package/dist/templates/native-go/backend/internal/dsm/bridge_file_test.go +57 -0
  201. package/dist/templates/native-go/backend/internal/dsm/entry.go +177 -0
  202. package/dist/templates/native-go/backend/internal/dsm/entry_test.go +315 -0
  203. package/dist/templates/native-go/backend/internal/dsm/native/admin-armv7.elf +0 -0
  204. package/dist/templates/native-go/backend/internal/dsm/native/apppriv-aarch64.elf +0 -0
  205. package/dist/templates/native-go/backend/internal/dsm/native/apppriv-armv7.elf +0 -0
  206. package/dist/templates/native-go/backend/internal/dsm/native/apppriv-x86_64.elf +0 -0
  207. package/dist/templates/native-go/backend/internal/dsm/native/apppriv_check.c +33 -0
  208. package/dist/templates/native-go/backend/internal/dsm/native/build.sh +13 -4
  209. package/dist/templates/native-go/backend/internal/dsm/native/provenance.json +9 -3
  210. package/dist/templates/native-go/backend/internal/filestation/check_permission.go +71 -0
  211. package/dist/templates/native-go/backend/internal/filestation/check_permission_test.go +77 -0
  212. package/dist/templates/native-go/backend/internal/filestation/client.go +39 -3
  213. package/dist/templates/native-go/backend/internal/filestation/download_dsm71_test.go +82 -0
  214. package/dist/templates/native-go/backend/internal/filestation/sdk_policy_test.go +39 -0
  215. package/dist/templates/native-go/backend/internal/filestation/service.go +1 -1
  216. package/dist/templates/native-go/backend/internal/identity/README.md +5 -1
  217. package/dist/templates/native-go/backend/internal/identity/dsm.go +24 -5
  218. package/dist/templates/native-go/backend/internal/identity/http.go +1 -1
  219. package/dist/templates/native-go/backend/internal/identity/identity_test.go +48 -1
  220. package/dist/templates/native-go/backend/internal/identity/service.go +1 -1
  221. package/dist/templates/native-go/backend/internal/portal/portal.go +138 -0
  222. package/dist/templates/native-go/backend/internal/portal/portal_test.go +89 -0
  223. package/dist/templates/native-go/dsm-package.yaml +20 -1
  224. package/dist/templates/native-go/i18n/glossary.json +2 -1
  225. package/dist/templates/native-go/i18n/messages/de.json +1 -0
  226. package/dist/templates/native-go/i18n/messages/en.json +1 -0
  227. package/dist/templates/native-go/i18n/messages/es.json +1 -0
  228. package/dist/templates/native-go/i18n/messages/fr.json +1 -0
  229. package/dist/templates/native-go/i18n/messages/it.json +1 -0
  230. package/dist/templates/native-go/i18n/messages/ja.json +1 -0
  231. package/dist/templates/native-go/i18n/messages/ko.json +1 -0
  232. package/dist/templates/native-go/i18n/messages/zh-CN.json +1 -0
  233. package/dist/templates/native-go/i18n/messages/zh-TW.json +1 -0
  234. package/dist/templates/native-go/ui/app.css +10 -0
  235. package/dist/templates/native-go/ui/app.js +11 -1
  236. package/dist/templates/native-go-delegate/DELEGATE.md +126 -5
  237. package/dist/templates/native-go-delegate/backend/cmd/background-worker/main.go +7 -0
  238. package/dist/templates/native-go-delegate/backend/internal/app/delegate.go +11 -1
  239. package/dist/templates/native-go-delegate/backend/internal/app/delegate_routes_test.go +14 -0
  240. package/dist/templates/native-go-delegate/backend/internal/background/delegated_moves.go +270 -0
  241. package/dist/templates/native-go-delegate/backend/internal/delegate/admission_recovery_test.go +410 -0
  242. package/dist/templates/native-go-delegate/backend/internal/delegate/cancel_contract_test.go +95 -0
  243. package/dist/templates/native-go-delegate/backend/internal/delegate/client.go +8 -0
  244. package/dist/templates/native-go-delegate/backend/internal/delegate/destination.go +110 -0
  245. package/dist/templates/native-go-delegate/backend/internal/delegate/destination_state.go +117 -0
  246. package/dist/templates/native-go-delegate/backend/internal/delegate/destination_state_test.go +60 -0
  247. package/dist/templates/native-go-delegate/backend/internal/delegate/destination_test.go +116 -0
  248. package/dist/templates/native-go-delegate/backend/internal/delegate/example.go +81 -0
  249. package/dist/templates/native-go-delegate/backend/internal/delegate/example_test.go +91 -0
  250. package/dist/templates/native-go-delegate/backend/internal/delegate/executor_contract_test.go +511 -0
  251. package/dist/templates/native-go-delegate/backend/internal/delegate/files.go +1 -1
  252. package/dist/templates/native-go-delegate/backend/internal/delegate/long_running_routes.go +92 -0
  253. package/dist/templates/native-go-delegate/backend/internal/delegate/long_running_routes_test.go +99 -0
  254. package/dist/templates/native-go-delegate/backend/internal/delegate/move.go +348 -0
  255. package/dist/templates/native-go-delegate/backend/internal/delegate/move_test.go +351 -0
  256. package/dist/templates/native-go-delegate/backend/internal/delegate/operations_list.go +65 -0
  257. package/dist/templates/native-go-delegate/backend/internal/delegate/operations_list_test.go +114 -0
  258. package/dist/templates/native-go-delegate/backend/internal/delegate/retry_test.go +1 -1
  259. package/dist/templates/native-go-delegate/backend/internal/delegate/service.go +58 -17
  260. package/dist/templates/native-go-delegate/backend/internal/delegate/staging.go +121 -0
  261. package/dist/templates/native-go-delegate/backend/internal/delegate/status_disabled_test.go +51 -0
  262. package/dist/templates/native-go-delegate/backend/internal/delegate/testdata/executor_contract.txt +112 -0
  263. package/dist/templates/native-go-delegate/backend/internal/delegate/transient_contract_test.go +137 -0
  264. package/dist/templates/native-go-delegate/backend/internal/filestation/files.go +1 -31
  265. package/dist/templates/native-go-delegate/frontend/src/CapabilityPanels.vue +30 -3
  266. package/dist/templates/native-go-delegate/frontend/src/delegate/BackgroundSavePanel.vue +113 -0
  267. package/dist/templates/native-go-delegate/frontend/src/delegate/copy.ts +5 -6
  268. package/dist/templates/native-go-delegate/frontend/test/background-save.test.ts +121 -0
  269. package/dist/templates/native-go-delegate/frontend/test/delegate.test.ts +15 -0
  270. package/dist/templates/native-go-delegate/frontend/test/scope-settings.test.ts +49 -0
  271. package/dist/templates/native-go-delegate/test.sh +1 -0
  272. package/dist/templates/native-go-jobs/frontend/src/background/i18n/de.json +2 -2
  273. package/dist/templates/native-go-jobs/frontend/src/background/i18n/en.json +2 -2
  274. package/dist/templates/native-go-jobs/frontend/src/background/i18n/es.json +12 -12
  275. package/dist/templates/native-go-jobs/frontend/src/background/i18n/fr.json +4 -4
  276. package/dist/templates/native-go-jobs/frontend/src/background/i18n/it.json +17 -17
  277. package/dist/templates/native-go-jobs/frontend/src/background/i18n/ja.json +7 -7
  278. package/dist/templates/native-go-jobs/frontend/src/background/i18n/ko.json +15 -15
  279. package/dist/templates/native-go-jobs/frontend/src/background/i18n/zh-CN.json +3 -3
  280. package/dist/templates/native-go-jobs/frontend/src/background/i18n/zh-TW.json +2 -2
  281. package/dist/templates/native-go-jobs/frontend/test/background.test.ts +25 -28
  282. package/dist/templates/native-go-root-ops/backend/internal/app/executor.go +30 -2
  283. package/dist/templates/native-go-root-ops/backend/internal/app/executor_test.go +64 -0
  284. package/dist/templates/native-go-vue/README.md +4 -0
  285. package/dist/templates/native-go-vue/frontend/package-lock.json +4 -4
  286. package/dist/templates/native-go-vue/frontend/package.json +1 -1
  287. package/dist/templates/native-go-vue/frontend/src/App.vue +18 -13
  288. package/dist/templates/native-go-vue/frontend/src/context.ts +31 -0
  289. package/dist/templates/native-go-vue/frontend/src/delegate/ExecutorSettings.vue +54 -9
  290. package/dist/templates/native-go-vue/frontend/src/delegate/catalogs.ts +11 -0
  291. package/dist/templates/native-go-vue/frontend/src/delegate/copy.test.ts +12 -0
  292. package/dist/templates/native-go-vue/frontend/src/delegate/copy.ts +4 -4
  293. package/dist/templates/native-go-vue/frontend/src/delegate/executor-settings.test.ts +23 -0
  294. package/dist/templates/native-go-vue/frontend/src/delegate/executor-setup.ts +8 -2
  295. package/dist/templates/native-go-vue/frontend/src/delegate/facade.test.ts +100 -0
  296. package/dist/templates/native-go-vue/frontend/src/delegate/facade.ts +85 -0
  297. package/dist/templates/native-go-vue/frontend/src/delegate/i18n/de.json +47 -0
  298. package/dist/templates/native-go-vue/frontend/src/delegate/i18n/en.json +13 -2
  299. package/dist/templates/native-go-vue/frontend/src/delegate/i18n/es.json +47 -0
  300. package/dist/templates/native-go-vue/frontend/src/delegate/i18n/fr.json +47 -0
  301. package/dist/templates/native-go-vue/frontend/src/delegate/i18n/it.json +47 -0
  302. package/dist/templates/native-go-vue/frontend/src/delegate/i18n/ja.json +47 -0
  303. package/dist/templates/native-go-vue/frontend/src/delegate/i18n/ko.json +47 -0
  304. package/dist/templates/native-go-vue/frontend/src/delegate/i18n/save/de.json +42 -0
  305. package/dist/templates/native-go-vue/frontend/src/delegate/i18n/save/en.json +42 -0
  306. package/dist/templates/native-go-vue/frontend/src/delegate/i18n/save/es.json +42 -0
  307. package/dist/templates/native-go-vue/frontend/src/delegate/i18n/save/fr.json +42 -0
  308. package/dist/templates/native-go-vue/frontend/src/delegate/i18n/save/it.json +42 -0
  309. package/dist/templates/native-go-vue/frontend/src/delegate/i18n/save/ja.json +42 -0
  310. package/dist/templates/native-go-vue/frontend/src/delegate/i18n/save/ko.json +42 -0
  311. package/dist/templates/native-go-vue/frontend/src/delegate/i18n/save/zh-CN.json +42 -0
  312. package/dist/templates/native-go-vue/frontend/src/delegate/i18n/save/zh-TW.json +42 -0
  313. package/dist/templates/native-go-vue/frontend/src/delegate/i18n/zh-CN.json +47 -0
  314. package/dist/templates/native-go-vue/frontend/src/delegate/i18n/zh-TW.json +16 -5
  315. package/dist/templates/native-go-vue/frontend/src/delegate/long-running.ts +57 -0
  316. package/dist/templates/native-go-vue/frontend/src/delegate/save-copy.test.ts +12 -0
  317. package/dist/templates/native-go-vue/frontend/src/delegate/save-copy.ts +24 -0
  318. package/dist/templates/native-go-vue/frontend/src/delegate/scoped-picker.test.ts +12 -0
  319. package/dist/templates/native-go-vue/frontend/src/delegate/scoped-picker.ts +21 -0
  320. package/dist/templates/native-go-vue/frontend/src/files/files.test.ts +7 -0
  321. package/dist/templates/native-go-vue/frontend/src/i18n/de.json +20 -0
  322. package/dist/templates/native-go-vue/frontend/src/i18n/en.json +20 -0
  323. package/dist/templates/native-go-vue/frontend/src/i18n/es.json +20 -0
  324. package/dist/templates/native-go-vue/frontend/src/i18n/fr.json +20 -0
  325. package/dist/templates/native-go-vue/frontend/src/i18n/it.json +20 -0
  326. package/dist/templates/native-go-vue/frontend/src/i18n/ja.json +20 -0
  327. package/dist/templates/native-go-vue/frontend/src/i18n/ko.json +20 -0
  328. package/dist/templates/native-go-vue/frontend/src/i18n/zh-CN.json +20 -0
  329. package/dist/templates/native-go-vue/frontend/src/i18n/zh-TW.json +20 -0
  330. package/dist/templates/native-go-vue/frontend/src/i18n.ts +13 -0
  331. package/dist/templates/native-go-vue/frontend/src/identity/LoginPage.vue +16 -14
  332. package/dist/templates/native-go-vue/frontend/src/identity/i18n/de.json +27 -0
  333. package/dist/templates/native-go-vue/frontend/src/identity/i18n/en.json +27 -0
  334. package/dist/templates/native-go-vue/frontend/src/identity/i18n/es.json +27 -0
  335. package/dist/templates/native-go-vue/frontend/src/identity/i18n/fr.json +27 -0
  336. package/dist/templates/native-go-vue/frontend/src/identity/i18n/it.json +27 -0
  337. package/dist/templates/native-go-vue/frontend/src/identity/i18n/ja.json +27 -0
  338. package/dist/templates/native-go-vue/frontend/src/identity/i18n/ko.json +27 -0
  339. package/dist/templates/native-go-vue/frontend/src/identity/i18n/zh-CN.json +27 -0
  340. package/dist/templates/native-go-vue/frontend/src/identity/i18n/zh-TW.json +27 -0
  341. package/dist/templates/native-go-vue/frontend/src/identity/i18n.ts +14 -0
  342. package/dist/templates/native-go-vue/frontend/src/identity/session.test.ts +4 -0
  343. package/dist/templates/native-go-vue/frontend/src/identity/session.ts +9 -8
  344. package/dist/templates/native-go-vue/frontend/src/nas/NasPicker.vue +31 -12
  345. package/dist/templates/native-go-vue/frontend/src/nas/destination.test.ts +48 -0
  346. package/dist/templates/native-go-vue/frontend/src/nas/i18n/files/de.json +1 -1
  347. package/dist/templates/native-go-vue/frontend/src/nas/i18n/files/en.json +1 -1
  348. package/dist/templates/native-go-vue/frontend/src/nas/i18n/files/es.json +10 -10
  349. package/dist/templates/native-go-vue/frontend/src/nas/i18n/files/fr.json +1 -1
  350. package/dist/templates/native-go-vue/frontend/src/nas/i18n/files/it.json +12 -12
  351. package/dist/templates/native-go-vue/frontend/src/nas/i18n/files/ja.json +15 -15
  352. package/dist/templates/native-go-vue/frontend/src/nas/i18n/files/ko.json +12 -12
  353. package/dist/templates/native-go-vue/frontend/src/nas/i18n/files/zh-CN.json +1 -1
  354. package/dist/templates/native-go-vue/frontend/src/nas/i18n/files/zh-TW.json +1 -1
  355. package/dist/templates/native-go-vue/frontend/src/portal/PortalSettings.vue +39 -0
  356. package/dist/templates/native-go-vue/frontend/src/shell/i18n/fr.json +1 -1
  357. package/dist/templates/native-go-vue/frontend/src/shell/i18n/ja.json +1 -1
  358. package/dist/templates/native-go-vue/frontend/test/apps.test.ts +14 -0
  359. package/dist/templates/native-go-vue/frontend/test/portal-settings.test.ts +71 -0
  360. package/dist/templates/native-go-vue/frontend/test/shell-i18n.test.ts +50 -0
  361. package/package.json +5 -1
  362. package/schema/package-metadata.schema.json +101 -0
  363. package/schema/synology-app.schema.json +112 -0
  364. package/skills/SKILL.md +106 -4
  365. package/skills/reference/create.md +101 -2
  366. package/skills/reference/deploy.md +2 -2
  367. package/skills/reference/files.md +113 -2
  368. package/skills/reference/identity.md +70 -1
  369. package/skills/reference/portal.md +17 -0
  370. package/skills/reference/ui-vue.md +12 -0
  371. package/templates/_daemon/go.mod +1 -1
  372. package/templates/_daemon/main.go +6 -3
  373. package/templates/_daemon/main_test.go +82 -9
  374. package/templates/_daemon/testdata/child.go +3 -0
  375. package/templates/_executor/README.md +130 -16
  376. package/templates/_executor/anchor.go +156 -0
  377. package/templates/_executor/anchor_test.go +161 -0
  378. package/templates/_executor/apppriv_review_test.go +1 -1
  379. package/templates/_executor/cancel_contract_test.go +236 -0
  380. package/templates/_executor/copy.go +64 -0
  381. package/templates/_executor/copy_test.go +88 -0
  382. package/templates/_executor/digest_review_test.go +2 -2
  383. package/templates/_executor/dsm.go +63 -13
  384. package/templates/_executor/dsm_test.go +95 -1
  385. package/templates/_executor/executor.go +260 -49
  386. package/templates/_executor/executor_test.go +36 -6
  387. package/templates/_executor/go.mod +2 -0
  388. package/templates/_executor/identity.go +27 -1
  389. package/templates/_executor/identity_kernel_linux_test.go +60 -0
  390. package/templates/_executor/identity_linux.go +82 -0
  391. package/templates/_executor/identity_security_test.go +43 -0
  392. package/templates/_executor/internal/lifecycle/lifecycle.go +5 -0
  393. package/templates/_executor/internal/lifecycle/lifecycle_test.go +61 -3
  394. package/templates/_executor/internal/taskscript/boot.sh +9 -9
  395. package/templates/_executor/internal/taskscript/cmd/main.go +2 -2
  396. package/templates/_executor/internal/taskscript/control.sh +15 -10
  397. package/templates/_executor/internal/taskscript/tasks.go +5 -3
  398. package/templates/_executor/lifecycle_test.go +11 -0
  399. package/templates/_executor/link_linux.go +28 -0
  400. package/templates/_executor/long_running_test.go +530 -0
  401. package/templates/_executor/main.go +83 -25
  402. package/templates/_executor/managed.go +3 -3
  403. package/templates/_executor/managed_test.go +3 -3
  404. package/templates/_executor/move_linux.go +490 -0
  405. package/templates/_executor/move_linux_test.go +460 -0
  406. package/templates/_executor/move_validation.go +46 -0
  407. package/templates/_executor/move_validation_test.go +187 -0
  408. package/templates/_executor/operations.go +926 -0
  409. package/templates/_executor/path_test.go +18 -0
  410. package/templates/_executor/pid.go +37 -0
  411. package/templates/_executor/pid_test.go +113 -0
  412. package/templates/_executor/platform_linux.go +24 -173
  413. package/templates/_executor/platform_linux_test.go +9 -65
  414. package/templates/_executor/platform_other.go +38 -4
  415. package/templates/_executor/process.go +3 -0
  416. package/templates/_executor/purge_log.go +18 -0
  417. package/templates/_executor/records.go +242 -0
  418. package/templates/_executor/scope_cancel_contract_test.go +91 -0
  419. package/templates/_executor/scope_client.go +37 -0
  420. package/templates/_executor/scope_contract.go +12 -0
  421. package/templates/_executor/selected_scope.go +146 -0
  422. package/templates/_executor/selected_scope_test.go +186 -0
  423. package/templates/_executor/status_disabled_test.go +33 -0
  424. package/templates/_executor/store.go +4 -3
  425. package/templates/_executor/supervise.go +109 -0
  426. package/templates/_executor/supervise_test.go +130 -0
  427. package/templates/_executor/task_race_review_test.go +1 -1
  428. package/templates/_executor/tasks.go +52 -15
  429. package/templates/_executor/tasks_dsm71_test.go +114 -0
  430. package/templates/_executor/tasks_review_test.go +1 -1
  431. package/templates/_executor/tasks_test.go +62 -5
  432. package/templates/_executor/upgrade_watch_test.go +13 -5
  433. package/templates/_executor/watch.go +8 -0
  434. package/templates/_executor/watch_test.go +23 -2
  435. package/templates/_executor/write_review_linux_test.go +4 -12
  436. package/templates/_gateway/go.mod +2 -0
  437. package/templates/_shared/AGENTS.md +129 -0
  438. package/templates/binary/dsm-package.yaml +24 -0
  439. package/templates/binary/i18n/messages/de.json +4 -0
  440. package/templates/binary/i18n/messages/en.json +4 -0
  441. package/templates/binary/i18n/messages/es.json +4 -0
  442. package/templates/binary/i18n/messages/fr.json +4 -0
  443. package/templates/binary/i18n/messages/it.json +4 -0
  444. package/templates/binary/i18n/messages/ja.json +4 -0
  445. package/templates/binary/i18n/messages/ko.json +4 -0
  446. package/templates/binary/i18n/messages/zh-CN.json +4 -0
  447. package/templates/binary/i18n/messages/zh-TW.json +4 -0
  448. package/templates/binary/ui/app.js +19 -4
  449. package/templates/native-authorization/AuthorizationGate.vue +5 -3
  450. package/templates/native-authorization/i18n/de.json +1 -0
  451. package/templates/native-authorization/i18n/en.json +1 -0
  452. package/templates/native-authorization/i18n/es.json +2 -1
  453. package/templates/native-authorization/i18n/fr.json +1 -0
  454. package/templates/native-authorization/i18n/it.json +11 -10
  455. package/templates/native-authorization/i18n/ja.json +1 -0
  456. package/templates/native-authorization/i18n/ko.json +10 -9
  457. package/templates/native-authorization/i18n/zh-CN.json +6 -5
  458. package/templates/native-authorization/i18n/zh-TW.json +6 -5
  459. package/templates/native-go/README.md +43 -10
  460. package/templates/native-go/backend/cmd/app/main.go +27 -7
  461. package/templates/native-go/backend/go.mod +1 -1
  462. package/templates/native-go/backend/internal/api/entry_test.go +47 -0
  463. package/templates/native-go/backend/internal/api/permissions_test.go +3 -3
  464. package/templates/native-go/backend/internal/api/registry.go +11 -0
  465. package/templates/native-go/backend/internal/api/sdk-permissions.json +14 -1
  466. package/templates/native-go/backend/internal/api/server.go +18 -5
  467. package/templates/native-go/backend/internal/app/delegate_config.go +57 -0
  468. package/templates/native-go/backend/internal/app/delegate_config_test.go +28 -0
  469. package/templates/native-go/backend/internal/app/portal.go +30 -0
  470. package/templates/native-go/backend/internal/app/portal_test.go +55 -0
  471. package/templates/native-go/backend/internal/app/routes.go +8 -2
  472. package/templates/native-go/backend/internal/background/runner.go +1 -1
  473. package/templates/native-go/backend/internal/background/store.go +16 -14
  474. package/templates/native-go/backend/internal/dsm/admin.go +8 -2
  475. package/templates/native-go/backend/internal/dsm/admin_linux.go +43 -13
  476. package/templates/native-go/backend/internal/dsm/admin_linux_test.go +56 -0
  477. package/templates/native-go/backend/internal/dsm/admin_other.go +4 -1
  478. package/templates/native-go/backend/internal/dsm/apppriv.go +30 -0
  479. package/templates/native-go/backend/internal/dsm/bridge_file.go +75 -0
  480. package/templates/native-go/backend/internal/dsm/bridge_file_test.go +57 -0
  481. package/templates/native-go/backend/internal/dsm/entry.go +177 -0
  482. package/templates/native-go/backend/internal/dsm/entry_test.go +315 -0
  483. package/templates/native-go/backend/internal/dsm/native/admin-armv7.elf +0 -0
  484. package/templates/native-go/backend/internal/dsm/native/apppriv-aarch64.elf +0 -0
  485. package/templates/native-go/backend/internal/dsm/native/apppriv-armv7.elf +0 -0
  486. package/templates/native-go/backend/internal/dsm/native/apppriv-x86_64.elf +0 -0
  487. package/templates/native-go/backend/internal/dsm/native/apppriv_check.c +33 -0
  488. package/templates/native-go/backend/internal/dsm/native/build.sh +13 -4
  489. package/templates/native-go/backend/internal/dsm/native/provenance.json +9 -3
  490. package/templates/native-go/backend/internal/filestation/check_permission.go +71 -0
  491. package/templates/native-go/backend/internal/filestation/check_permission_test.go +77 -0
  492. package/templates/native-go/backend/internal/filestation/client.go +39 -3
  493. package/templates/native-go/backend/internal/filestation/download_dsm71_test.go +82 -0
  494. package/templates/native-go/backend/internal/filestation/sdk_policy_test.go +39 -0
  495. package/templates/native-go/backend/internal/filestation/service.go +1 -1
  496. package/templates/native-go/backend/internal/identity/README.md +5 -1
  497. package/templates/native-go/backend/internal/identity/dsm.go +24 -5
  498. package/templates/native-go/backend/internal/identity/http.go +1 -1
  499. package/templates/native-go/backend/internal/identity/identity_test.go +48 -1
  500. package/templates/native-go/backend/internal/identity/service.go +1 -1
  501. package/templates/native-go/backend/internal/portal/portal.go +138 -0
  502. package/templates/native-go/backend/internal/portal/portal_test.go +89 -0
  503. package/templates/native-go/dsm-package.yaml +20 -1
  504. package/templates/native-go/i18n/glossary.json +2 -1
  505. package/templates/native-go/i18n/messages/de.json +1 -0
  506. package/templates/native-go/i18n/messages/en.json +1 -0
  507. package/templates/native-go/i18n/messages/es.json +1 -0
  508. package/templates/native-go/i18n/messages/fr.json +1 -0
  509. package/templates/native-go/i18n/messages/it.json +1 -0
  510. package/templates/native-go/i18n/messages/ja.json +1 -0
  511. package/templates/native-go/i18n/messages/ko.json +1 -0
  512. package/templates/native-go/i18n/messages/zh-CN.json +1 -0
  513. package/templates/native-go/i18n/messages/zh-TW.json +1 -0
  514. package/templates/native-go/ui/app.css +10 -0
  515. package/templates/native-go/ui/app.js +11 -1
  516. package/templates/native-go-delegate/DELEGATE.md +126 -5
  517. package/templates/native-go-delegate/backend/cmd/background-worker/main.go +7 -0
  518. package/templates/native-go-delegate/backend/internal/app/delegate.go +11 -1
  519. package/templates/native-go-delegate/backend/internal/app/delegate_routes_test.go +14 -0
  520. package/templates/native-go-delegate/backend/internal/background/delegated_moves.go +270 -0
  521. package/templates/native-go-delegate/backend/internal/delegate/admission_recovery_test.go +410 -0
  522. package/templates/native-go-delegate/backend/internal/delegate/cancel_contract_test.go +95 -0
  523. package/templates/native-go-delegate/backend/internal/delegate/client.go +8 -0
  524. package/templates/native-go-delegate/backend/internal/delegate/destination.go +110 -0
  525. package/templates/native-go-delegate/backend/internal/delegate/destination_state.go +117 -0
  526. package/templates/native-go-delegate/backend/internal/delegate/destination_state_test.go +60 -0
  527. package/templates/native-go-delegate/backend/internal/delegate/destination_test.go +116 -0
  528. package/templates/native-go-delegate/backend/internal/delegate/example.go +81 -0
  529. package/templates/native-go-delegate/backend/internal/delegate/example_test.go +91 -0
  530. package/templates/native-go-delegate/backend/internal/delegate/executor_contract_test.go +511 -0
  531. package/templates/native-go-delegate/backend/internal/delegate/files.go +1 -1
  532. package/templates/native-go-delegate/backend/internal/delegate/long_running_routes.go +92 -0
  533. package/templates/native-go-delegate/backend/internal/delegate/long_running_routes_test.go +99 -0
  534. package/templates/native-go-delegate/backend/internal/delegate/move.go +348 -0
  535. package/templates/native-go-delegate/backend/internal/delegate/move_test.go +351 -0
  536. package/templates/native-go-delegate/backend/internal/delegate/operations_list.go +65 -0
  537. package/templates/native-go-delegate/backend/internal/delegate/operations_list_test.go +114 -0
  538. package/templates/native-go-delegate/backend/internal/delegate/retry_test.go +1 -1
  539. package/templates/native-go-delegate/backend/internal/delegate/service.go +58 -17
  540. package/templates/native-go-delegate/backend/internal/delegate/staging.go +121 -0
  541. package/templates/native-go-delegate/backend/internal/delegate/status_disabled_test.go +51 -0
  542. package/templates/native-go-delegate/backend/internal/delegate/testdata/executor_contract.txt +112 -0
  543. package/templates/native-go-delegate/backend/internal/delegate/transient_contract_test.go +137 -0
  544. package/templates/native-go-delegate/backend/internal/filestation/files.go +1 -31
  545. package/templates/native-go-delegate/frontend/src/CapabilityPanels.vue +30 -3
  546. package/templates/native-go-delegate/frontend/src/delegate/BackgroundSavePanel.vue +113 -0
  547. package/templates/native-go-delegate/frontend/src/delegate/copy.ts +5 -6
  548. package/templates/native-go-delegate/frontend/test/background-save.test.ts +121 -0
  549. package/templates/native-go-delegate/frontend/test/delegate.test.ts +15 -0
  550. package/templates/native-go-delegate/frontend/test/scope-settings.test.ts +49 -0
  551. package/templates/native-go-delegate/test.sh +1 -0
  552. package/templates/native-go-jobs/frontend/src/background/i18n/de.json +2 -2
  553. package/templates/native-go-jobs/frontend/src/background/i18n/en.json +2 -2
  554. package/templates/native-go-jobs/frontend/src/background/i18n/es.json +12 -12
  555. package/templates/native-go-jobs/frontend/src/background/i18n/fr.json +4 -4
  556. package/templates/native-go-jobs/frontend/src/background/i18n/it.json +17 -17
  557. package/templates/native-go-jobs/frontend/src/background/i18n/ja.json +7 -7
  558. package/templates/native-go-jobs/frontend/src/background/i18n/ko.json +15 -15
  559. package/templates/native-go-jobs/frontend/src/background/i18n/zh-CN.json +3 -3
  560. package/templates/native-go-jobs/frontend/src/background/i18n/zh-TW.json +2 -2
  561. package/templates/native-go-jobs/frontend/test/background.test.ts +25 -28
  562. package/templates/native-go-root-ops/backend/internal/app/executor.go +30 -2
  563. package/templates/native-go-root-ops/backend/internal/app/executor_test.go +64 -0
  564. package/templates/native-go-vue/README.md +4 -0
  565. package/templates/native-go-vue/frontend/package-lock.json +4 -4
  566. package/templates/native-go-vue/frontend/package.json +1 -1
  567. package/templates/native-go-vue/frontend/src/App.vue +18 -13
  568. package/templates/native-go-vue/frontend/src/context.ts +31 -0
  569. package/templates/native-go-vue/frontend/src/delegate/ExecutorSettings.vue +54 -9
  570. package/templates/native-go-vue/frontend/src/delegate/catalogs.ts +11 -0
  571. package/templates/native-go-vue/frontend/src/delegate/copy.test.ts +12 -0
  572. package/templates/native-go-vue/frontend/src/delegate/copy.ts +4 -4
  573. package/templates/native-go-vue/frontend/src/delegate/executor-settings.test.ts +23 -0
  574. package/templates/native-go-vue/frontend/src/delegate/executor-setup.ts +8 -2
  575. package/templates/native-go-vue/frontend/src/delegate/facade.test.ts +100 -0
  576. package/templates/native-go-vue/frontend/src/delegate/facade.ts +85 -0
  577. package/templates/native-go-vue/frontend/src/delegate/i18n/de.json +47 -0
  578. package/templates/native-go-vue/frontend/src/delegate/i18n/en.json +13 -2
  579. package/templates/native-go-vue/frontend/src/delegate/i18n/es.json +47 -0
  580. package/templates/native-go-vue/frontend/src/delegate/i18n/fr.json +47 -0
  581. package/templates/native-go-vue/frontend/src/delegate/i18n/it.json +47 -0
  582. package/templates/native-go-vue/frontend/src/delegate/i18n/ja.json +47 -0
  583. package/templates/native-go-vue/frontend/src/delegate/i18n/ko.json +47 -0
  584. package/templates/native-go-vue/frontend/src/delegate/i18n/save/de.json +42 -0
  585. package/templates/native-go-vue/frontend/src/delegate/i18n/save/en.json +42 -0
  586. package/templates/native-go-vue/frontend/src/delegate/i18n/save/es.json +42 -0
  587. package/templates/native-go-vue/frontend/src/delegate/i18n/save/fr.json +42 -0
  588. package/templates/native-go-vue/frontend/src/delegate/i18n/save/it.json +42 -0
  589. package/templates/native-go-vue/frontend/src/delegate/i18n/save/ja.json +42 -0
  590. package/templates/native-go-vue/frontend/src/delegate/i18n/save/ko.json +42 -0
  591. package/templates/native-go-vue/frontend/src/delegate/i18n/save/zh-CN.json +42 -0
  592. package/templates/native-go-vue/frontend/src/delegate/i18n/save/zh-TW.json +42 -0
  593. package/templates/native-go-vue/frontend/src/delegate/i18n/zh-CN.json +47 -0
  594. package/templates/native-go-vue/frontend/src/delegate/i18n/zh-TW.json +16 -5
  595. package/templates/native-go-vue/frontend/src/delegate/long-running.ts +57 -0
  596. package/templates/native-go-vue/frontend/src/delegate/save-copy.test.ts +12 -0
  597. package/templates/native-go-vue/frontend/src/delegate/save-copy.ts +24 -0
  598. package/templates/native-go-vue/frontend/src/delegate/scoped-picker.test.ts +12 -0
  599. package/templates/native-go-vue/frontend/src/delegate/scoped-picker.ts +21 -0
  600. package/templates/native-go-vue/frontend/src/files/files.test.ts +7 -0
  601. package/templates/native-go-vue/frontend/src/i18n/de.json +20 -0
  602. package/templates/native-go-vue/frontend/src/i18n/en.json +20 -0
  603. package/templates/native-go-vue/frontend/src/i18n/es.json +20 -0
  604. package/templates/native-go-vue/frontend/src/i18n/fr.json +20 -0
  605. package/templates/native-go-vue/frontend/src/i18n/it.json +20 -0
  606. package/templates/native-go-vue/frontend/src/i18n/ja.json +20 -0
  607. package/templates/native-go-vue/frontend/src/i18n/ko.json +20 -0
  608. package/templates/native-go-vue/frontend/src/i18n/zh-CN.json +20 -0
  609. package/templates/native-go-vue/frontend/src/i18n/zh-TW.json +20 -0
  610. package/templates/native-go-vue/frontend/src/i18n.ts +13 -0
  611. package/templates/native-go-vue/frontend/src/identity/LoginPage.vue +16 -14
  612. package/templates/native-go-vue/frontend/src/identity/i18n/de.json +27 -0
  613. package/templates/native-go-vue/frontend/src/identity/i18n/en.json +27 -0
  614. package/templates/native-go-vue/frontend/src/identity/i18n/es.json +27 -0
  615. package/templates/native-go-vue/frontend/src/identity/i18n/fr.json +27 -0
  616. package/templates/native-go-vue/frontend/src/identity/i18n/it.json +27 -0
  617. package/templates/native-go-vue/frontend/src/identity/i18n/ja.json +27 -0
  618. package/templates/native-go-vue/frontend/src/identity/i18n/ko.json +27 -0
  619. package/templates/native-go-vue/frontend/src/identity/i18n/zh-CN.json +27 -0
  620. package/templates/native-go-vue/frontend/src/identity/i18n/zh-TW.json +27 -0
  621. package/templates/native-go-vue/frontend/src/identity/i18n.ts +14 -0
  622. package/templates/native-go-vue/frontend/src/identity/session.test.ts +4 -0
  623. package/templates/native-go-vue/frontend/src/identity/session.ts +9 -8
  624. package/templates/native-go-vue/frontend/src/nas/NasPicker.vue +31 -12
  625. package/templates/native-go-vue/frontend/src/nas/destination.test.ts +48 -0
  626. package/templates/native-go-vue/frontend/src/nas/i18n/files/de.json +1 -1
  627. package/templates/native-go-vue/frontend/src/nas/i18n/files/en.json +1 -1
  628. package/templates/native-go-vue/frontend/src/nas/i18n/files/es.json +10 -10
  629. package/templates/native-go-vue/frontend/src/nas/i18n/files/fr.json +1 -1
  630. package/templates/native-go-vue/frontend/src/nas/i18n/files/it.json +12 -12
  631. package/templates/native-go-vue/frontend/src/nas/i18n/files/ja.json +15 -15
  632. package/templates/native-go-vue/frontend/src/nas/i18n/files/ko.json +12 -12
  633. package/templates/native-go-vue/frontend/src/nas/i18n/files/zh-CN.json +1 -1
  634. package/templates/native-go-vue/frontend/src/nas/i18n/files/zh-TW.json +1 -1
  635. package/templates/native-go-vue/frontend/src/portal/PortalSettings.vue +39 -0
  636. package/templates/native-go-vue/frontend/src/shell/i18n/fr.json +1 -1
  637. package/templates/native-go-vue/frontend/src/shell/i18n/ja.json +1 -1
  638. package/templates/native-go-vue/frontend/test/apps.test.ts +14 -0
  639. package/templates/native-go-vue/frontend/test/portal-settings.test.ts +71 -0
  640. package/templates/native-go-vue/frontend/test/shell-i18n.test.ts +50 -0
@@ -1,28 +1,47 @@
1
1
  import { bootDefinition, controlDefinition } from '../runtime/executor-tasks.js';
2
+ import { executorScopeTask } from '../runtime/executor-scope-task.js';
3
+ import { versionUnsupported } from '../runtime/dsm-version.js';
2
4
  export { executorTasks as rootTasks } from '../runtime/executor-tasks.js';
3
5
  /** Exact readback is authoritative. PasswordConfirm tokens are reused immediately,
4
6
  * across both Root APIs, and never persisted (DSM validity is about 300 seconds). */
5
7
  export const ROOT_RUNTIME = `function createRootAccess(options) {
6
8
  var eventAPI = "SYNO.Core.EventScheduler", taskAPI = "SYNO.Core.TaskScheduler";
7
- var busy = null;
8
- function fail(code) { var e = new Error(code); e.code = code; return e; }
9
+ var busy = null, legacy = false;
10
+ var versionUnsupported = ${versionUnsupported.toString()};
11
+ function fail(code, detail) { var e = new Error(code); e.code = code; if (detail) e.detail = detail; return e; }
9
12
  function reason(e) {
10
13
  var code = String(e && e.code || "executor_bad_response");
14
+ if (e && e.passwordConfirm) return code + (code === "dsm_3121" ? ": DSM rejected the administrator password." : ": administrator password confirmation failed.") + " Nothing was created.";
15
+ if (e && e.detail) return code + ": " + e.detail;
11
16
  return code + (code === "105" || code === "dsm_105" ? ": DSM denied administrator scheduler access. Check the DSM session and scheduler; a NAS restart resolved this on the measured DSM. No automatic retry was made." : ": scheduler readback or operation failed.");
12
17
  }
13
- function wire(params) {
14
- var encoded = {};
15
- Object.keys(params).forEach(function (key) {
16
- var value = params[key];
17
- encoded[key] = value === "" || typeof value === "boolean" || typeof value === "object" ? JSON.stringify(value) : value;
18
+ // AppWindow.sendWebAPI encodes JSON parameters. Preserve their native types;
19
+ // pre-encoding turns objects/booleans into strings and DSM rejects create.
20
+ function send(api, method, version, params) { return options.send(api, method, version, params || {}); }
21
+ // DSM 7.1 has no Root APIs: TaskScheduler stops at list v2 (offset) and get v3.
22
+ // Only an unsupported method version on a read selects that profile, which
23
+ // then creates through the plain scheduler APIs. Writes never fall back.
24
+ function taskRead(method, version, params, legacyVersion, legacyParams) {
25
+ if (legacy) return send(taskAPI, method, legacyVersion, legacyParams);
26
+ return send(taskAPI, method, version, params).catch(function (e) {
27
+ if (!versionUnsupported(e && e.code)) throw e;
28
+ legacy = true;
29
+ return send(taskAPI, method, legacyVersion, legacyParams);
18
30
  });
19
- return encoded;
20
31
  }
21
- function send(api, method, version, params) { return options.send(api, method, version, wire(params || {})); }
32
+ function create(boot, params, confirmation) {
33
+ if (legacy) return send(boot ? eventAPI : taskAPI, "create", boot ? 1 : 3, params);
34
+ params.SynoConfirmPWToken = confirmation.token;
35
+ return send(boot ? eventAPI + ".Root" : taskAPI + ".Root", "create", boot ? 1 : 4, params);
36
+ }
37
+ function remove(task, ref) {
38
+ var boot = task.kind === "boot";
39
+ return send(boot ? eventAPI : taskAPI, "delete", boot ? 1 : 2, boot ? ref : {tasks: [ref]});
40
+ }
22
41
  function admin() { if (!options.user() || options.user().admin !== true) throw fail("admin_required"); }
23
42
  function find(task) {
24
43
  var boot = task.kind === "boot";
25
- return send(boot ? eventAPI : taskAPI, "list", boot ? 1 : 3, boot ? {} : {start: 0, limit: 500}).then(function (data) {
44
+ return (boot ? send(eventAPI, "list", 1, {}) : taskRead("list", 3, {start: 0, limit: 500}, 2, {offset: 0, limit: 500})).then(function (data) {
26
45
  var records = boot ? data : data && data.tasks;
27
46
  if (!Array.isArray(records) || records.length > 500 || (!boot && (!Number.isInteger(data.total) || data.total !== records.length))) throw fail("executor_bad_response");
28
47
  var named = records.filter(function (r) { return r && (boot ? r.task_name : r.name) === task.name; });
@@ -31,7 +50,7 @@ export const ROOT_RUNTIME = `function createRootAccess(options) {
31
50
  var summary = named[0];
32
51
  if (!boot && !/^[0-9]+$/.test(String(summary.id))) throw fail("executor_bad_response");
33
52
  var ref = boot ? {task_name: task.name} : {id: summary.id, real_owner: "root"};
34
- var detail = send(boot ? eventAPI : taskAPI, "get", boot ? 1 : 4, ref);
53
+ var detail = boot ? send(eventAPI, "get", 1, ref) : taskRead("get", 4, ref, 3, ref);
35
54
  return detail.then(function (r) {
36
55
  var owner = r && r.owner;
37
56
  var root = owner === "root" || (boot && owner && owner["0"] === "root");
@@ -54,15 +73,30 @@ export const ROOT_RUNTIME = `function createRootAccess(options) {
54
73
  return result;
55
74
  });
56
75
  }
76
+ // Removes, newest first, the tasks one approval created and still reads as
77
+ // exactly ours. Resolves with the last refusal's reason, if any.
78
+ function withdraw(created) {
79
+ var failed;
80
+ return created.reduceRight(function (withdrawn, task) {
81
+ return withdrawn.then(function () { return find(task); }).then(function (current) {
82
+ if (current.state === "ready") return remove(task, current.ref);
83
+ }).catch(function (e) { failed = reason(e); });
84
+ }, Promise.resolve()).then(function () { return failed; });
85
+ }
57
86
  function definition(task) { return task.kind === "boot" ? (${bootDefinition.toString()})(task) : (${controlDefinition.toString()})(task); }
58
- function approve(password) {
87
+ function approve(password, selectedScope) {
88
+ var selectionTask;
89
+ if (selectedScope !== undefined) selectionTask = (${executorScopeTask.toString()})(options.pins, selectedScope, options.user() && options.user().name, 0);
59
90
  return read().then(function (before) {
60
91
  if (before.state === "blocked" || before.state === "unsupported") return before;
61
- if (before.state === "ready") return start(before);
62
- return options.confirmPassword(password).then(function (confirmation) {
92
+ if (before.state === "ready" && !selectionTask) return start(before);
93
+ return options.confirmPassword(password).catch(function (e) {
94
+ throw {code: e && e.code, passwordConfirm: true};
95
+ }).then(function (confirmation) {
63
96
  if (!confirmation || confirmation.confirmed !== true) return {state: "cancelled", tasks: before.tasks};
64
- if (typeof confirmation.token !== "string" || !confirmation.token) throw fail("executor_confirmation_missing");
65
- var errors = {};
97
+ // DSM 7.1 confirms the password server-side but issues no create token.
98
+ if (!legacy && (typeof confirmation.token !== "string" || !confirmation.token)) throw fail("executor_confirmation_missing");
99
+ var errors = {}, created = [], withdrawFailed;
66
100
  // Delete only a verified, same-header outdated task, then create the pinned
67
101
  // replacement. set has no measured wire contract. Re-read before each write.
68
102
  var chain = Promise.resolve();
@@ -71,32 +105,106 @@ export const ROOT_RUNTIME = `function createRootAccess(options) {
71
105
  return find(task).then(function (current) {
72
106
  if (current.state === "ready") return;
73
107
  if (current.state === "blocked") throw fail(current.reason);
74
- var boot = task.kind === "boot";
75
- var removed = current.ref ? send(boot ? eventAPI : taskAPI, "delete", boot ? 1 : 2, boot ? current.ref : {tasks: [current.ref]}) : Promise.resolve();
76
- return removed.then(function () {
77
- var params = definition(task); params.SynoConfirmPWToken = confirmation.token;
78
- return send(boot ? eventAPI + ".Root" : taskAPI + ".Root", "create", boot ? 1 : 4, params);
79
- });
108
+ var removed = current.ref ? remove(task, current.ref) : Promise.resolve();
109
+ return removed.then(function () { return create(task.kind === "boot", definition(task), confirmation); }).then(function () { created.push(task); });
80
110
  }).catch(function (e) { errors[task.kind] = reason(e); });
81
111
  });
82
112
  });
83
- return chain.then(read).then(function (after) {
113
+ // A partial approval must not leave an enabled root bootup task that no
114
+ // control task or uninstall inventory owns: withdraw what it created.
115
+ return chain.then(function () {
116
+ if (Object.keys(errors).length) return withdraw(created).then(function (failed) { withdrawFailed = failed; });
117
+ }).then(read).then(function (after) {
84
118
  Object.keys(errors).forEach(function (kind) {
85
119
  if (after.tasks[kind].state !== "ready") { after.tasks[kind].state = "blocked"; after.tasks[kind].reason = errors[kind]; after.state = "blocked"; after.reason = errors[kind]; }
86
120
  });
87
- return after.state === "ready" ? start(after) : after;
121
+ if (withdrawFailed) { after.state = "blocked"; after.reason = (after.reason ? after.reason + " " : "") + "Withdrawing the tasks this approval created failed: " + withdrawFailed; }
122
+ // A scope change must not restart a running executor: the boot task
123
+ // stops it, which interrupts in-flight copies instead of canceling them.
124
+ return after.state === "ready" ? (selectionTask ? running(after) : Promise.resolve(null)).then(function (live) { return live || start(after); }).then(function (started) {
125
+ // Without a running executor there is no task inventory, so uninstall
126
+ // cannot find these tasks: withdraw what this approval created.
127
+ if (started.state === "ready" || !created.length) return started;
128
+ return withdraw(created).then(function (failed) {
129
+ return read().then(function (current) {
130
+ current.state = "blocked";
131
+ current.reason = started.reason + (failed ? " Withdrawing the tasks this approval created failed: " + failed : " The tasks this approval created were withdrawn.");
132
+ current.retry = !failed;
133
+ return current;
134
+ });
135
+ });
136
+ }).then(function (started) {
137
+ if (!selectionTask || started.state !== "ready") return started;
138
+ var generation = started.executor.selection ? started.executor.selection.generation : 0;
139
+ if (started.executor.selection && (!Number.isSafeInteger(generation) || generation <= 0)) throw fail("executor_selection_invalid");
140
+ selectionTask = (${executorScopeTask.toString()})(options.pins, selectedScope, options.user() && options.user().name, generation);
141
+ // The root task may still land after this reply. requested lets the
142
+ // app converge by readback: applied once status().executor.selection
143
+ // shows this scope above this generation. A resubmit is pinned and safe.
144
+ return selectScope(selectionTask, confirmation, started, selectedScope).catch(function (e) {
145
+ return {state: "blocked", reason: reason(e), tasks: started.tasks, requested: {scope: selectedScope, generation: generation}};
146
+ });
147
+ }) : after;
88
148
  });
89
149
  });
90
150
  });
91
151
  }
92
- function health(verified, attempts) {
152
+ function selectScope(task, confirmation, verified, selectedScope) {
153
+ function removeExact() {
154
+ return find(task).then(function (current) {
155
+ if (current.state === "ready") return remove(task, current.ref);
156
+ if (current.ref) throw fail("executor_selection_cleanup_conflict");
157
+ });
158
+ }
159
+ return find(task).then(function (current) {
160
+ if (current.state === "blocked" || current.state === "unsupported") throw fail("executor_selection_conflict");
161
+ var removed = current.ref ? remove(task, current.ref) : Promise.resolve();
162
+ return removed.then(function () { return create(false, definition(task), confirmation); });
163
+ }).then(function () { return find(task); }).then(function (current) {
164
+ if (current.state !== "ready") throw fail("executor_selection_readback_mismatch");
165
+ return send(taskAPI, "run", 2, {tasks: [current.ref]});
166
+ }).then(function () {
167
+ function readSelection(attempts) {
168
+ return health(verified, 0).then(function (current) {
169
+ if (current.state === "ready" && current.executor.selection && current.executor.selection.scope === selectedScope && Number.isSafeInteger(current.executor.selection.generation) && current.executor.selection.generation > 0) return current;
170
+ if (attempts > 1 && options.wait) return options.wait(500).then(function () { verified.state = "ready"; return readSelection(attempts - 1); });
171
+ throw fail("executor_selection_unconfirmed");
172
+ });
173
+ }
174
+ return readSelection(40);
175
+ }).then(function (result) { return removeExact().then(function () { return result; }); }, function (error) {
176
+ return removeExact().then(function () { throw error; });
177
+ });
178
+ }
179
+ // Why a status reply is not the approved, running executor; null when it is.
180
+ function mismatch(status) {
181
+ if (!status || status.ready !== true) return "the approved executor is not answering" + (status && (status.reason || status.state) ? " (" + (status.reason || status.state) + ")" : "") + ".";
182
+ if (!options.pins || status.executor_digest !== options.pins.executorDigest || status.policy_digest !== options.pins.policyDigest) return "the running executor reports other digests than this package pins.";
183
+ return null;
184
+ }
185
+ function pinned(status) { return mismatch(status) === null; }
186
+ function running(verified) {
187
+ return options.call("executor.status", {}).then(function (status) {
188
+ if (!pinned(status)) return null;
189
+ verified.executor = status; return verified;
190
+ }, function () { return null; });
191
+ }
192
+ // budget: how long to keep asking after a start (ms). The wait doubles from
193
+ // 500 ms to 2 s so an open window does not poll the executor twice a second.
194
+ function health(verified, budget, delay) {
93
195
  if (verified.state !== "ready") return Promise.resolve(verified);
196
+ delay = Math.min(delay || 500, 2000);
94
197
  return options.call("executor.status", {}).then(function (status) {
95
- if (!status || status.ready !== true || !options.pins || status.executor_digest !== options.pins.executorDigest || status.policy_digest !== options.pins.policyDigest) throw fail("executor_status_mismatch");
198
+ var why = mismatch(status);
199
+ if (why) throw fail("executor_status_mismatch", why);
96
200
  verified.executor = status; return verified;
201
+ }, function (e) {
202
+ throw fail(e && e.code, "the executor status request failed.");
97
203
  }).catch(function (e) {
98
- if (attempts > 1 && options.wait) return options.wait(500).then(function () { return health(verified, attempts - 1); });
99
- verified.state = "blocked"; verified.reason = reason(e); return verified;
204
+ if (budget >= delay && options.wait) return options.wait(delay).then(function () { return health(verified, budget - delay, delay * 2); });
205
+ // The tasks are approved: approving again runs start, which restarts the
206
+ // executor without a password, so the panel offers it.
207
+ verified.state = "blocked"; verified.reason = reason(e); verified.retry = true; return verified;
100
208
  });
101
209
  }
102
210
  function start(verified) {
@@ -104,13 +212,17 @@ export const ROOT_RUNTIME = `function createRootAccess(options) {
104
212
  return send(eventAPI, "run", 1, verified.tasks.boot.ref);
105
213
  }).then(function () {
106
214
  // run can succeed for a nonexistent task. Read both definitions again.
107
- return read().then(function (after) { return health(after, 20); });
215
+ return read().then(function (after) { return health(after, 10000); });
108
216
  }).catch(function (e) { verified.state = "blocked"; verified.reason = reason(e); return verified; });
109
217
  }
218
+ // The window panel and the app's own settings share this access; each
219
+ // result reaches every view so neither shows a stale state.
220
+ var listeners = [];
221
+ function publish(result) { listeners.forEach(function (listener) { listener(result); }); return result; }
110
222
  return {
111
- status: function () { return read().then(function (verified) { return health(verified, 1); }); },
112
- authorize: function (password) {
113
- if (!busy) busy = approve(password).catch(function (e) { return {state: "blocked", reason: reason(e), tasks: {}}; }).then(function (r) { busy = null; return r; });
223
+ status: function () { return read().then(function (verified) { return health(verified, 0); }).then(publish); },
224
+ authorize: function (password, selectedScope) {
225
+ if (!busy) busy = Promise.resolve().then(function () { return approve(password, selectedScope); }).catch(function (e) { return {state: "blocked", reason: reason(e), tasks: {}, retry: !!(e && e.passwordConfirm)}; }).then(function (r) { busy = null; return publish(r); });
114
226
  return busy;
115
227
  },
116
228
  control: function (action) {
@@ -119,9 +231,10 @@ export const ROOT_RUNTIME = `function createRootAccess(options) {
119
231
  if (verified.state !== "ready") throw fail("executor_not_ready");
120
232
  return options.call("executor.control", {action: action}).then(function () {
121
233
  return send(taskAPI, "run", 2, {tasks: [verified.tasks.control.ref]});
122
- }).then(read).then(function (after) { return action === "start" ? health(after, 20) : after; });
123
- });
124
- }
234
+ }).then(read).then(function (after) { return action === "start" ? health(after, 10000) : after; });
235
+ }).then(publish);
236
+ },
237
+ subscribe: function (listener) { listeners.push(listener); }
125
238
  };
126
239
  }`;
127
240
  export const ROOT_PANEL = `function mountRootPanel(host, access, i18n, tasks, admin) {
@@ -129,9 +242,53 @@ export const ROOT_PANEL = `function mountRootPanel(host, access, i18n, tasks, ad
129
242
  var status = document.createElement("p"), approve = document.createElement("button"), retry = document.createElement("button");
130
243
  approve.textContent = "Approve background access"; retry.textContent = "Check status";
131
244
  host.appendChild(status); host.appendChild(approve); host.appendChild(retry);
132
- function show(result) { status.textContent = result.state + (result.reason ? ": " + result.reason : ""); approve.hidden = result.state !== "needs-admin-approval"; }
245
+ function show(result) { status.textContent = result.state + (result.reason ? ": " + result.reason : ""); approve.hidden = result.state !== "needs-admin-approval" && result.retry !== true; }
133
246
  function run(fn) { approve.disabled = retry.disabled = true; return fn().then(show, function(e) { show({state: "blocked", reason: e.message || e.code}); }).then(function() { approve.disabled = retry.disabled = false; }); }
247
+ access.subscribe(show);
134
248
  approve.onclick = function() { return run(function() { return access.authorize(); }); };
135
249
  retry.onclick = function() { return run(function() { return access.status(); }); };
136
250
  run(function() { return access.status().then(function(result) { return result.tasks && result.tasks.boot && result.tasks.control && result.tasks.boot.state === "ready" && result.tasks.control.state === "ready" ? access.control("start") : result; }); });
137
251
  }`;
252
+ /** Administrator password confirmation in the window's own session: a supplied
253
+ * password, or a masked form when none is given. */
254
+ export const PASSWORD_CONFIRM = `function createPasswordConfirm(win) {
255
+ var versionUnsupported = ${versionUnsupported.toString()};
256
+ function send(password, version) {
257
+ return new Promise(function (resolve, reject) {
258
+ win.sendWebAPI({api: "SYNO.Core.User.PasswordConfirm", method: "auth", version: version, params: {password: password}, scope: win,
259
+ callback: function (success, response) {
260
+ if (!success) reject({code: "dsm_" + ((response && response.code) || "error")});
261
+ else resolve({confirmed: true, token: response && response.SynoConfirmPWToken});
262
+ }
263
+ });
264
+ });
265
+ }
266
+ // DSM 7.1 has PasswordConfirm v1 only, which checks the password but issues no
267
+ // token. Only an unsupported version is retried; a wrong password is not.
268
+ function auth(password) {
269
+ return send(password, 2).catch(function (error) {
270
+ if (!versionUnsupported(error.code)) throw error;
271
+ return send(password, 1);
272
+ });
273
+ }
274
+ return function (suppliedPassword) {
275
+ if (typeof suppliedPassword === "string" && suppliedPassword) return auth(suppliedPassword);
276
+ return new Promise(function (resolve, reject) {
277
+ var form = document.createElement("form"), label = document.createElement("label"), input = document.createElement("input");
278
+ var submit = document.createElement("button"), cancel = document.createElement("button");
279
+ label.textContent = "Confirm your DSM administrator password";
280
+ input.type = "password"; input.autocomplete = "current-password"; input.required = true;
281
+ label.appendChild(input); form.appendChild(label);
282
+ submit.type = "submit"; submit.textContent = "Approve"; form.appendChild(submit);
283
+ cancel.type = "button"; cancel.textContent = "Cancel"; form.appendChild(cancel);
284
+ win.body.dom.appendChild(form); input.focus();
285
+ function close() { input.value = ""; if (form.parentNode) form.parentNode.removeChild(form); }
286
+ cancel.onclick = function () { close(); resolve({confirmed: false}); };
287
+ form.onsubmit = function (event) {
288
+ event.preventDefault(); submit.disabled = cancel.disabled = true;
289
+ var password = input.value; input.value = "";
290
+ auth(password).then(function (result) { close(); resolve(result); }, function (error) { close(); reject(error); });
291
+ };
292
+ });
293
+ };
294
+ }`;
@@ -14,7 +14,7 @@ export interface AssembleOptions {
14
14
  outputPath: string;
15
15
  executor?: ExecutorArtifact;
16
16
  daemonControl?: string;
17
- /** The compiled background worker, required exactly when the manifest declares background. */
17
+ /** The compiled background worker, required exactly when capabilities.jobs is enabled. */
18
18
  backgroundWorker?: string;
19
19
  }
20
20
  /**
@@ -1,3 +1,4 @@
1
+ import { metadataInfoFields } from "../package-metadata.js";
1
2
  import { createHash } from "node:crypto";
2
3
  import { chmodSync, copyFileSync, existsSync, lstatSync, mkdirSync, mkdtempSync, readdirSync, readFileSync, rmSync, writeFileSync, } from "node:fs";
3
4
  import { tmpdir } from "node:os";
@@ -8,6 +9,7 @@ import { renderNativePrivilege } from "../build-spk/native-lifecycle.js";
8
9
  import { renderUninstallWizardFiles } from "../build-spk/uninstall-wizard.js";
9
10
  import { renderInstallWizardFiles, renderNativeInstallWizard } from "../build-spk/install-wizard.js";
10
11
  import { writeWizardUiFiles } from "../build-spk/wizard-i18n.js";
12
+ import { renderDelegateScopeSteps } from '../build-spk/delegate-scope.js';
11
13
  import { createReproducibleTar } from "../build-spk/reproducible-archive.js";
12
14
  import { renderStandaloneScript } from "./lifecycle.js";
13
15
  import { DSM_LANGS, LOCALES } from "./i18n.js";
@@ -17,7 +19,10 @@ import { hostScriptName, renderHostAdapterScript } from "./host-adapter.js";
17
19
  import { copyDaemonPayload } from "./daemon.js";
18
20
  import { BACKGROUND_UNIT_DIR, BACKGROUND_WORKER_PAYLOAD, backgroundUnitName, renderBackgroundUnit } from "./background.js";
19
21
  import { renderStandaloneResource } from "./resource.js";
22
+ import { nativePortal } from "./portal.js";
23
+ import { PORTAL_JSON_RELPATH, PORTAL_MUSTACHE_RELPATH, renderPortalJson, renderPortalMustache, renderPortalWizardStep } from "../portal/package.js";
20
24
  import { executorPlan } from "../runtime/executor-build.js";
25
+ import { EXECUTOR_ANCHOR } from "../runtime/executor-inspect.js";
21
26
  import { renderSharedFolderUpgradeWizardFiles, sharedFolderInstallSteps } from "./shared-folder.js";
22
27
  const FILE_MODE = 0o644;
23
28
  const EXEC_MODE = 0o755;
@@ -77,6 +82,11 @@ export async function assembleStandaloneSpk(options) {
77
82
  copyStandaloneNotices(manifest.root, manifest.ui.dir, join(staging, 'package'), manifest.backend.kind === 'go' || Boolean(manifest.daemon), manifest.backend.kind === 'go' ? manifest.backend.dir : undefined);
78
83
  if (manifest.daemon)
79
84
  copyFileSync(new URL('../../templates/_shared/LICENSES/TEMPLATE.txt', import.meta.url), join(staging, 'package', 'LICENSES', 'DAEMON-LICENSE.txt'));
85
+ const portal = nativePortal(manifest);
86
+ if (portal) {
87
+ writeFile(join(staging, "package", PORTAL_JSON_RELPATH), renderPortalJson(portal));
88
+ writeFile(join(staging, "package", PORTAL_MUSTACHE_RELPATH), renderPortalMustache());
89
+ }
80
90
  if (options.executor) {
81
91
  const executorDir = join(staging, "package", "executor");
82
92
  mkdirSync(executorDir);
@@ -93,11 +103,11 @@ export async function assembleStandaloneSpk(options) {
93
103
  await createReproducibleTar(join(staging, "package"), packageTgz, { gzip: true });
94
104
  rmSync(join(staging, "package"), { recursive: true, force: true });
95
105
  const checksum = createHash("md5").update(readFileSync(packageTgz)).digest("hex");
96
- writeFile(join(staging, "sdk-package.json"), JSON.stringify({ schema: 2, backend: manifest.backend.kind, capabilities: Object.keys(manifest.capabilities).sort() }));
97
- writeFile(join(staging, "INFO"), renderInfo(manifest, options.arch, checksum));
106
+ writeFile(join(staging, "sdk-package.json"), JSON.stringify({ schema: 2, backend: manifest.backend.kind, capabilities: Object.keys(manifest.capabilities).sort(), ...(manifest.entry ? { entry: manifest.entry } : {}) }));
107
+ writeFile(join(staging, "INFO"), renderInfo(manifest, options.arch, checksum, options.executor?.executorDigest));
98
108
  mkdirSync(join(staging, "conf"));
99
109
  writeFile(join(staging, "conf", "privilege"), renderNativePrivilege());
100
- const resource = renderStandaloneResource(manifest);
110
+ const resource = renderStandaloneResource(manifest, portal);
101
111
  if (resource)
102
112
  writeFile(join(staging, "conf", "resource"), resource);
103
113
  if (manifest.capabilities.jobs) {
@@ -109,7 +119,10 @@ export async function assembleStandaloneSpk(options) {
109
119
  writeFile(join(staging, "scripts", name), renderStandaloneScript(name, manifest), EXEC_MODE);
110
120
  }
111
121
  const folder = manifest.sharedFolder;
112
- const install = renderInstallWizardFiles(manifest.package, folder ? sharedFolderInstallSteps(manifest.package, folder) : renderNativeInstallWizard);
122
+ const steps = manifest.capabilities.delegate?.scope === 'install-selected'
123
+ ? (t) => [...renderDelegateScopeSteps(t), ...(folder ? sharedFolderInstallSteps(manifest.package, folder)(t) : [])]
124
+ : folder ? sharedFolderInstallSteps(manifest.package, folder) : renderNativeInstallWizard;
125
+ const install = renderInstallWizardFiles(manifest.package, portal ? (t) => [...steps(t), renderPortalWizardStep(t, portal.alias)] : steps);
113
126
  writeWizardUiFiles(staging, {
114
127
  ...install.static,
115
128
  ...install.dynamic,
@@ -130,10 +143,10 @@ function writeFile(path, content, mode = FILE_MODE) {
130
143
  chmodSync(path, mode);
131
144
  }
132
145
  function quote(value) {
133
- return `"${value}"`;
146
+ return `"${value.replace(/([\\"$`])/g, "\\$1")}"`;
134
147
  }
135
148
  /** Manifest validation already refused quotes, backslashes and newlines. */
136
- function renderInfo(manifest, arch, checksum) {
149
+ function renderInfo(manifest, arch, checksum, executorDigest) {
137
150
  const fields = [
138
151
  ["package", manifest.package],
139
152
  ["version", manifest.spkVersion],
@@ -148,7 +161,12 @@ function renderInfo(manifest, arch, checksum) {
148
161
  ["startable", "yes"],
149
162
  ["checksum", checksum],
150
163
  ];
151
- if (manifest.daemon)
164
+ // Package Center writes INFO as root; root tasks accept only this executor.
165
+ if (executorDigest)
166
+ fields.push([EXECUTOR_ANCHOR, executorDigest]);
167
+ fields.push(...metadataInfoFields(manifest.metadata));
168
+ // Background packages need prestart: it runs before DSM's systemd-user-unit acquire.
169
+ if (manifest.daemon || manifest.capabilities.jobs)
152
170
  fields.push(["precheckstartstop", "yes"]);
153
171
  for (const locale of LOCALES) {
154
172
  const text = manifest.i18n?.info[locale];
@@ -2,4 +2,4 @@ module synopkgland-daemon
2
2
 
3
3
  go 1.24
4
4
 
5
- toolchain go1.26.8
5
+ toolchain go1.26.9
@@ -28,6 +28,9 @@ type deadlines struct {
28
28
  Start int `json:"start"`
29
29
  Stop int `json:"stop"`
30
30
  Status int `json:"status"`
31
+ // Uninstall bounds uninstall_args separately from Stop: a revoke on a
32
+ // loaded NAS can take longer than stopping the daemon.
33
+ Uninstall int `json:"uninstall"`
31
34
  }
32
35
  type config struct {
33
36
  Package string `json:"package"`
@@ -88,7 +91,7 @@ func load(dest, data string) (*controller, error) {
88
91
  if cfg.Exec == "" || filepath.IsAbs(cfg.Exec) || filepath.Clean(cfg.Exec) != cfg.Exec || strings.HasPrefix(cfg.Exec, "../") {
89
92
  return nil, errors.New("invalid fixed executable")
90
93
  }
91
- if cfg.Timeouts.Start < 2 || cfg.Timeouts.Start > 120 || cfg.Timeouts.Stop < 2 || cfg.Timeouts.Stop > 120 || cfg.Timeouts.Status < 1 || cfg.Timeouts.Status > 10 {
94
+ if cfg.Timeouts.Start < 2 || cfg.Timeouts.Start > 120 || cfg.Timeouts.Stop < 2 || cfg.Timeouts.Stop > 120 || cfg.Timeouts.Status < 1 || cfg.Timeouts.Status > 10 || cfg.Timeouts.Uninstall < 2 || cfg.Timeouts.Uninstall > 120 {
92
95
  return nil, errors.New("invalid daemon deadlines")
93
96
  }
94
97
  hash := sha256.Sum256(raw)
@@ -162,7 +165,7 @@ func (c *controller) run(action string) (int, error) {
162
165
  budget += c.Timeouts.Start
163
166
  }
164
167
  if action == "uninstall" {
165
- budget += c.Timeouts.Stop + 1
168
+ budget += c.Timeouts.Uninstall + 2
166
169
  }
167
170
  if action != "start" && action != "stop" && action != "status" && action != "restart" && action != "uninstall" {
168
171
  return 1, errors.New("unsupported daemon action")
@@ -414,7 +417,7 @@ func (c *controller) uninstall() error {
414
417
  select {
415
418
  case err := <-done:
416
419
  return err
417
- case <-time.After(time.Duration(c.Timeouts.Stop-1) * time.Second):
420
+ case <-time.After(time.Duration(c.Timeouts.Uninstall) * time.Second):
418
421
  cmd.Process.Signal(syscall.SIGTERM)
419
422
  }
420
423
  select {
@@ -41,10 +41,16 @@ func fixture(t *testing.T, mode string) (string, string, string) {
41
41
  if err != nil {
42
42
  t.Fatal(err)
43
43
  }
44
- if err := os.WriteFile(filepath.Join(dest, "daemon/payload/app"), raw, 0755); err != nil {
44
+ app := filepath.Join(dest, "daemon/payload/app")
45
+ if err := os.WriteFile(app, raw, 0755); err != nil {
45
46
  t.Fatal(err)
46
47
  }
47
- cfg := config{Package: "DaemonFixture", Exec: "app", Args: []string{"${SYNOPKG_PKGVAR}/a space", "$(touch BAD)'", "${SYNOPKG_PKGDEST}"}, Env: map[string]string{"APP_MODE": mode}, Timeouts: deadlines{Start: 2, Stop: 2, Status: 1}}
48
+ // macOS verifies a newly written executable on its first exec, which takes
49
+ // seconds on a loaded host; pay it here, not inside a one-second budget.
50
+ if raw, err := exec.Command(app, "cleanup").CombinedOutput(); err != nil {
51
+ t.Fatalf("payload warm-up: %v %s", err, raw)
52
+ }
53
+ cfg := config{Package: "DaemonFixture", Exec: "app", Args: []string{"${SYNOPKG_PKGVAR}/a space", "$(touch BAD)'", "${SYNOPKG_PKGDEST}"}, Env: map[string]string{"APP_MODE": mode}, Timeouts: deadlines{Start: 2, Stop: 2, Status: 1, Uninstall: 2}}
48
54
  if os.Geteuid() == 0 {
49
55
  cfg.Package = "nobody"
50
56
  } else {
@@ -91,22 +97,69 @@ func runControl(control, action, dest, data string) (int, string) {
91
97
  return -1, err.Error()
92
98
  }
93
99
 
100
+ // Readiness only proves the child stayed alive for a second, not that it reached
101
+ // main: on a loaded host a stop can kill it before it writes its line. Spawn
102
+ // identity therefore comes from the inventory, and log content is awaited.
103
+ func recordedChild(t *testing.T, data string) int {
104
+ t.Helper()
105
+ raw, err := os.ReadFile(filepath.Join(data, "daemon/daemon.pid"))
106
+ if err != nil {
107
+ t.Fatal(err)
108
+ }
109
+ var r record
110
+ if err := json.Unmarshal(raw, &r); err != nil || r.Child < 1 {
111
+ t.Fatalf("daemon inventory: %v %s", err, raw)
112
+ }
113
+ return r.Child
114
+ }
115
+
116
+ func awaitChildLines(t *testing.T, data string, want int) {
117
+ t.Helper()
118
+ path := filepath.Join(data, "daemon/daemon.log")
119
+ for deadline := time.Now().Add(10 * time.Second); ; time.Sleep(20 * time.Millisecond) {
120
+ raw, _ := os.ReadFile(path)
121
+ if got := strings.Count(string(raw), `"uid":`); got >= want || time.Now().After(deadline) {
122
+ if got != want {
123
+ t.Fatalf("child lines=%d want=%d: %s", got, want, raw)
124
+ }
125
+ return
126
+ }
127
+ }
128
+ }
129
+
94
130
  func TestDaemonLifecycle(t *testing.T) {
95
131
  control, dest, data := fixture(t, "normal")
96
- for _, step := range []struct {
97
- action string
98
- want int
99
- }{{"status", 3}, {"stop", 0}, {"start", 0}, {"status", 0}, {"start", 0}, {"restart", 0}, {"stop", 0}, {"stop", 0}, {"status", 3}, {"unknown", 1}} {
100
- if rc, out := runControl(control, step.action, dest, data); rc != step.want {
101
- t.Fatalf("%s rc=%d want=%d: %s", step.action, rc, step.want, out)
132
+ step := func(action string, want int) {
133
+ t.Helper()
134
+ if rc, out := runControl(control, action, dest, data); rc != want {
135
+ t.Fatalf("%s rc=%d want=%d: %s", action, rc, want, out)
102
136
  }
103
137
  }
138
+ step("status", 3)
139
+ step("stop", 0)
140
+ step("start", 0)
141
+ awaitChildLines(t, data, 1)
142
+ first := recordedChild(t, data)
143
+ step("status", 0)
144
+ step("start", 0)
145
+ if child := recordedChild(t, data); child != first {
146
+ t.Fatalf("duplicate start spawned another child: %d replaced %d", child, first)
147
+ }
148
+ step("restart", 0)
149
+ if child := recordedChild(t, data); child == first {
150
+ t.Fatalf("restart kept child %d", child)
151
+ }
152
+ awaitChildLines(t, data, 2)
153
+ step("stop", 0)
154
+ step("stop", 0)
155
+ step("status", 3)
156
+ step("unknown", 1)
104
157
  raw, err := os.ReadFile(filepath.Join(data, "daemon/daemon.log"))
105
158
  if err != nil {
106
159
  t.Fatal(err)
107
160
  }
108
161
  if strings.Count(string(raw), `"uid":`) != 2 {
109
- t.Fatalf("duplicate start spawned another child: %s", raw)
162
+ t.Fatalf("unexpected child spawn: %s", raw)
110
163
  }
111
164
  if strings.Contains(string(raw), "must-not-leak") {
112
165
  t.Fatalf("ambient environment leaked: %s", raw)
@@ -197,6 +250,26 @@ func TestUninstallCleanup(t *testing.T) {
197
250
  }
198
251
  }
199
252
 
253
+ // A revoke on uninstall can outlast the stop deadline on a loaded NAS; it has
254
+ // its own budget instead of Stop-1.
255
+ func TestUninstallCommandHasItsOwnBudget(t *testing.T) {
256
+ control, dest, data := fixture(t, "normal")
257
+ path := filepath.Join(dest, "daemon/config.json")
258
+ raw, _ := os.ReadFile(path)
259
+ var cfg config
260
+ json.Unmarshal(raw, &cfg)
261
+ cfg.UninstallArgs = []string{"cleanup-slow"}
262
+ cfg.Timeouts.Uninstall = 5
263
+ raw, _ = json.Marshal(cfg)
264
+ os.WriteFile(path, raw, 0644)
265
+ if rc, out := runControl(control, "start", dest, data); rc != 0 {
266
+ t.Fatalf("start: %d %s", rc, out)
267
+ }
268
+ if rc, out := runControl(control, "uninstall", dest, data); rc != 0 {
269
+ t.Fatalf("slow uninstall within its budget failed: %d %s", rc, out)
270
+ }
271
+ }
272
+
200
273
  func TestPackageAccountIdentity(t *testing.T) {
201
274
  control, dest, data := fixture(t, "normal")
202
275
  path := filepath.Join(dest, "daemon/config.json")
@@ -14,6 +14,9 @@ func main() {
14
14
  switch os.Args[1] {
15
15
  case "cleanup":
16
16
  os.Exit(0)
17
+ case "cleanup-slow":
18
+ time.Sleep(2500 * time.Millisecond)
19
+ os.Exit(0)
17
20
  case "cleanup-fail":
18
21
  os.Exit(2)
19
22
  case "cleanup-hang":