@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,22 +1,25 @@
1
1
  import { inspectNativeLayout } from "./native-layout.js";
2
- import { inspectExecutor } from "../runtime/executor-inspect.js";
2
+ import { inspectExecutor, inspectExecutorAnchor } from "../runtime/executor-inspect.js";
3
3
  import { existsSync, mkdirSync, mkdtempSync, readdirSync, readFileSync, rmSync } from "node:fs";
4
4
  import { tmpdir } from "node:os";
5
5
  import { join } from "node:path";
6
6
  import * as tar from "tar";
7
7
  import { familyOfArchToken, NATIVE_ARCH_FAMILIES } from "../build-spk/dsm-arch.js";
8
- import { CONTAINER_MIN_DSM, isDsmAtLeast } from "../build-spk/dsm-version.js";
8
+ import { CONTAINER_MIN_DSM, isDsmAtLeast, PORTAL_MIN_DSM } from "../build-spk/dsm-version.js";
9
9
  import { ELF_MACHINE } from "./binary.js";
10
- import { CGI_PAYLOAD_PATH } from "./dsm-window.js";
10
+ import { CGI_PAYLOAD_PATH, entryLauncherAccess } from "./dsm-window.js";
11
11
  import { daemonConfig, readDaemon } from "./daemon.js";
12
12
  import { BACKGROUND_UNIT_DIR, BACKGROUND_WORKER_PAYLOAD, backgroundUnitName, renderBackgroundUnit } from "./background.js";
13
13
  import { renderStandaloneResource } from "./resource.js";
14
14
  import { recoverSharedFolder, renderSharedFolderUpgradeWizardFiles, sharedFolderInstallSteps, SHARED_FOLDER_MARKER } from "./shared-folder.js";
15
15
  import { renderInstallWizardFiles, renderNativeInstallWizard } from "../build-spk/install-wizard.js";
16
+ import { renderDelegateScopeSteps, renderDelegateInstallDraft } from "../build-spk/delegate-scope.js";
16
17
  import { renderUninstallWizardFiles } from "../build-spk/uninstall-wizard.js";
17
18
  import { renderStandaloneScript } from "./lifecycle.js";
18
19
  import { listPortExposures, loadCompose } from "../compose/analyze.js";
19
20
  import { uninstallCleanup } from "../runtime/uninstall-cleanup.js";
21
+ import { PORTAL_JSON_RELPATH, PORTAL_MUSTACHE_RELPATH, portalResource, renderPortalJson, renderPortalLifecycle, renderPortalMustache, renderPortalWizardStep } from "../portal/package.js";
22
+ import { isPortalAlias } from "../portal/alias.js";
20
23
  const ROOT_SCRIPT_HINTS = [/\bsudo\b/, /\bchown\s+(-\w+\s+)*root\b/, /chmod\s+[ugo]*\+s\b/, /chmod\s+[2467][0-7]{3}\b/];
21
24
  /** Reads an SPK without installing it and reports how it would run on DSM. */
22
25
  export async function inspectStandaloneSpk(spkPath) {
@@ -48,7 +51,7 @@ export async function inspectSpk(spkPath, expectedKind) {
48
51
  if (metadataText !== undefined) {
49
52
  try {
50
53
  const raw = JSON.parse(metadataText);
51
- if (raw.schema !== 2 || !["go", "binary"].includes(raw.backend) || !Array.isArray(raw.capabilities) || raw.capabilities.some((name) => !["files", "jobs", "identity", "rootOps", "delegate"].includes(String(name))) || new Set(raw.capabilities).size !== raw.capabilities.length)
54
+ if (raw.schema !== 2 || !["go", "binary"].includes(raw.backend) || !Array.isArray(raw.capabilities) || raw.capabilities.some((name) => !["files", "jobs", "identity", "rootOps", "delegate"].includes(String(name))) || new Set(raw.capabilities).size !== raw.capabilities.length || (raw.entry !== undefined && !["admin-only", "app-privilege"].includes(raw.entry)))
52
55
  throw new Error();
53
56
  metadata = raw;
54
57
  }
@@ -63,18 +66,19 @@ export async function inspectSpk(spkPath, expectedKind) {
63
66
  await checkArch(dir, info, kind, paths.has(CGI_PAYLOAD_PATH), findings);
64
67
  const background = kind === "native-go" && paths.has(BACKGROUND_WORKER_PAYLOAD);
65
68
  const sharedFolder = standalone ? recoverSharedFolder(readOptional(join(dir, "scripts/postinst"))) : undefined;
66
- checkConf(dir, findings, kind, background, sharedFolder, info.package);
69
+ const portal = await recoverPortal(dir, info, kind === "container" ? info.package ?? "" : `SYNO.SDS.${info.package}.Instance`, paths, payload, findings);
70
+ if (portal && info.os_min_ver && OS_MIN_VER.test(info.os_min_ver) && !isDsmAtLeast(info.os_min_ver, PORTAL_MIN_DSM)) {
71
+ findings.push(`INFO os_min_ver「${info.os_min_ver}」低於 portal 需要的 DSM ${PORTAL_MIN_DSM};DSM 7.1.1 以錯誤 320 拒裝帶 web-config app-portal 的未簽章套件,7.2 未驗證`);
72
+ }
73
+ checkConf(dir, findings, kind, background, sharedFolder, info.package, portal);
67
74
  checkScripts(dir, findings);
68
- if (standalone)
69
- checkSharedFolder(dir, findings, info.package, sharedFolder);
70
75
  checkUninstall(dir, findings);
71
76
  if (kind === "container")
72
77
  await checkContainerExposure(dir, join(dir, "package.tgz"), findings);
73
78
  const hasExecutor = (metadata.capabilities ?? []).some(name => name === "rootOps" || name === "delegate") || paths.has("executor/executor");
79
+ const daemon = standalone ? await checkDaemon(dir, info, paths, payload, findings, hasExecutor, background) : undefined;
74
80
  if (background)
75
- checkBackground(dir, info, payload, findings, hasExecutor);
76
- if (standalone)
77
- await checkDaemon(dir, info, paths, payload, findings, hasExecutor);
81
+ checkBackground(dir, info, payload, findings, hasExecutor, daemon);
78
82
  let executor;
79
83
  if ((metadata.capabilities ?? []).some(name => name === "rootOps" || name === "delegate") || paths.has("executor/executor")) {
80
84
  const executorDir = join(dir, "executor-inspection");
@@ -83,6 +87,8 @@ export async function inspectSpk(spkPath, expectedKind) {
83
87
  const inspected = inspectExecutor(executorDir, info.package, info.arch);
84
88
  findings.push(...inspected.findings);
85
89
  executor = inspected.executor;
90
+ if (executor)
91
+ findings.push(...inspectExecutorAnchor(readFileSync(join(dir, "INFO"), "utf8"), executor.executorDigest));
86
92
  if (payload.find(p => p.path === "executor/executor")?.mode !== "0755")
87
93
  findings.push("Executor must be executable.");
88
94
  if (payload.find(p => p.path === "executor/lifecycle")?.mode !== "0755")
@@ -99,6 +105,13 @@ export async function inspectSpk(spkPath, expectedKind) {
99
105
  findings.push(`Executor WIZARD_UIFILES/${name} is missing cleanup guidance.`);
100
106
  }
101
107
  }
108
+ if (background && daemon && (executor?.policy.lifetime !== 'until-revoked' || !metadata.capabilities?.includes('delegate'))) {
109
+ findings.push('daemon/background combination requires an explicitly pinned until-revoked delegate policy.');
110
+ }
111
+ if (metadata.entry)
112
+ await checkEntryLauncher(dir, info.package, metadata.entry, findings);
113
+ if (standalone)
114
+ checkSharedFolder(dir, findings, info.package, sharedFolder, executor?.policy.scope === 'install-selected', portal);
102
115
  const cgiEntry = payload.find((entry) => entry.path === CGI_PAYLOAD_PATH);
103
116
  if (standalone && !cgiEntry) {
104
117
  findings.push(`payload 沒有 ${CGI_PAYLOAD_PATH}`);
@@ -109,6 +122,8 @@ export async function inspectSpk(spkPath, expectedKind) {
109
122
  return {
110
123
  kind,
111
124
  ...(executor ? { executor } : {}),
125
+ ...(portal ? { portal: { appId: portal.appId, alias: portal.alias } } : {}),
126
+ ...(metadata.entry ? { entry: metadata.entry } : {}),
112
127
  ...(standalone ? { uninstallCleanup: uninstallCleanup(info.package, hasExecutor) } : {}),
113
128
  package: info.package,
114
129
  version: info.version,
@@ -124,6 +139,23 @@ export async function inspectSpk(spkPath, expectedKind) {
124
139
  rmSync(dir, { recursive: true, force: true });
125
140
  }
126
141
  }
142
+ /** The launcher in ui/config must carry exactly the access the declared entry mode fixes. */
143
+ async function checkEntryLauncher(dir, pkg, entry, findings) {
144
+ const target = join(dir, "entry-inspection");
145
+ mkdirSync(target);
146
+ await tar.x({ file: join(dir, "package.tgz"), cwd: target, filter: path => path.replace(/^\.\//, "") === "ui/config" });
147
+ let launcher;
148
+ try {
149
+ const config = JSON.parse(readFileSync(join(target, "ui/config"), "utf8"));
150
+ launcher = Object.values(config)[0]?.[`SYNO.SDS.${pkg}.Instance`];
151
+ }
152
+ catch { /* reported below */ }
153
+ const expected = entryLauncherAccess(entry);
154
+ const fields = ["allUsers", "grantPrivilege", "advanceGrantPrivilege"];
155
+ if (!launcher || fields.some(field => launcher[field] !== expected[field])) {
156
+ findings.push(`ENTRY_LAUNCHER_MISMATCH sdk-package.json 宣告 entry: ${entry},但 ui/config 的 launcher 不是 ${JSON.stringify(expected)};請用 synopkgland build 重新產生`);
157
+ }
158
+ }
127
159
  const OS_MIN_VER = /^7\.\d+(\.\d+)?-\d+$/;
128
160
  /** INFO os_min_ver and arch must be values DSM accepts, and arch must match the shipped CGI. */
129
161
  async function checkArch(dir, info, kind, hasCgi, findings) {
@@ -157,7 +189,7 @@ async function checkArch(dir, info, kind, hasCgi, findings) {
157
189
  findings.push(`INFO arch 宣告 ${declared},但 ${CGI_PAYLOAD_PATH} 是 ${actual} 的執行檔`);
158
190
  }
159
191
  }
160
- async function checkDaemon(dir, info, paths, payload, findings, executor) {
192
+ async function checkDaemon(dir, info, paths, payload, findings, executor, background) {
161
193
  if (![...paths].some(path => path.startsWith("daemon/")))
162
194
  return;
163
195
  if (info.precheckstartstop !== "yes")
@@ -178,13 +210,18 @@ async function checkDaemon(dir, info, paths, payload, findings, executor) {
178
210
  throw new Error("invalid config");
179
211
  if (payload.find(p => p.path === `daemon/payload/${daemon.exec}`)?.mode !== "0755")
180
212
  throw new Error("missing executable");
181
- const manifest = { package: info.package, daemon, capabilities: executor ? { rootOps: true } : {} };
182
- if (readFileSync(join(unpacked, "daemon/config.json"), "utf8") !== daemonConfig(manifest))
213
+ const manifest = { package: info.package, daemon, capabilities: { ...(executor ? { rootOps: true } : {}), ...(background ? { jobs: { worker: 'sha256-v1' } } : {}) } };
214
+ // A config from before timeouts.uninstall pairs with a control that rejects the key.
215
+ const legacy = typeof config.timeouts === "object" && config.timeouts !== null && !("uninstall" in config.timeouts);
216
+ const { uninstall: _, ...legacyTimeouts } = daemon.timeouts;
217
+ const canonical = daemonConfig(legacy ? { ...manifest, daemon: { ...daemon, timeouts: legacyTimeouts } } : manifest);
218
+ if (readFileSync(join(unpacked, "daemon/config.json"), "utf8") !== canonical)
183
219
  throw new Error("non-canonical daemon config");
184
220
  for (const hook of ["start-stop-status", "preupgrade", "preuninst"]) {
185
221
  if (readFileSync(join(dir, "scripts", hook), "utf8") !== renderStandaloneScript(hook, manifest))
186
222
  findings.push(`daemon scripts/${hook} 與固定 lifecycle 不符`);
187
223
  }
224
+ return daemon;
188
225
  }
189
226
  catch {
190
227
  findings.push("daemon/config.json 或 payload 不符合固定 daemon 契約");
@@ -234,12 +271,12 @@ async function checkContainerExposure(dir, packageTgz, findings) {
234
271
  function readOptional(path) {
235
272
  return existsSync(path) ? readFileSync(path, "utf8") : undefined;
236
273
  }
237
- function checkConf(dir, findings, kind, background, sharedFolder, pkg) {
274
+ function checkConf(dir, findings, kind, background, sharedFolder, pkg, portal) {
238
275
  const confDir = join(dir, "conf");
239
276
  const entries = existsSync(confDir) ? readdirSync(confDir) : [];
240
- const nativeResource = kind !== "container" ? renderStandaloneResource({ package: pkg ?? "", capabilities: background ? { jobs: { worker: "sha256-v1" } } : {}, ...(sharedFolder ? { sharedFolder } : {}) }) : undefined;
277
+ const nativeResource = kind !== "container" ? renderStandaloneResource({ package: pkg ?? "", capabilities: background ? { jobs: { worker: "sha256-v1" } } : {}, ...(sharedFolder ? { sharedFolder } : {}) }, portal) : undefined;
241
278
  if (nativeResource !== undefined && readOptional(join(confDir, "resource")) !== nativeResource) {
242
- findings.push(`conf/resource 必須是 ${nativeResource}(只有官方 data-share 與 systemd-user-unit,且與 install.sharedFolder 宣告一致)`);
279
+ findings.push(`conf/resource 必須是 ${nativeResource}(只有官方 data-share、systemd-user-unit 與 web-config,且與 install.sharedFolder、portal 宣告一致)`);
243
280
  }
244
281
  for (const name of entries) {
245
282
  if (name !== "privilege" && !(kind === "container" && name === "resource") && !(nativeResource !== undefined && name === "resource") && !(background && name === "systemd")) {
@@ -252,9 +289,11 @@ function checkConf(dir, findings, kind, background, sharedFolder, pkg) {
252
289
  if (!resource["docker-project"])
253
290
  findings.push("container 缺少 docker-project resource");
254
291
  for (const key of Object.keys(resource)) {
255
- if (!["docker-project", "webservice"].includes(key))
292
+ if (!["docker-project", "webservice", ...(portal ? ["web-config"] : [])].includes(key))
256
293
  findings.push(`未知 container resource: ${key}`);
257
294
  }
295
+ if (portal && JSON.stringify(resource["web-config"]) !== JSON.stringify(portalResource()["web-config"]))
296
+ findings.push("container web-config 必須只有固定的 app-portal 註冊");
258
297
  }
259
298
  catch {
260
299
  findings.push("container 缺少有效的 conf/resource");
@@ -283,19 +322,21 @@ function checkConf(dir, findings, kind, background, sharedFolder, pkg) {
283
322
  }
284
323
  }
285
324
  /** The opt-in package user unit must be exactly what the generator writes. */
286
- function checkBackground(dir, info, payload, findings, executor) {
325
+ function checkBackground(dir, info, payload, findings, executor, daemon) {
287
326
  const pkg = info.package;
288
327
  if (!pkg)
289
328
  return;
290
329
  const read = (path) => existsSync(join(dir, path)) ? readFileSync(join(dir, path), "utf8") : undefined;
291
330
  if (payload.find(p => p.path === BACKGROUND_WORKER_PAYLOAD)?.mode !== "0755")
292
331
  findings.push(`${BACKGROUND_WORKER_PAYLOAD} 缺少或非 executable`);
332
+ if (info.precheckstartstop !== "yes")
333
+ findings.push('background INFO 必須明確設定 precheckstartstop="yes"(prestart 復原失去 socket 的套件使用者 manager)');
293
334
  const units = existsSync(join(dir, BACKGROUND_UNIT_DIR)) ? readdirSync(join(dir, BACKGROUND_UNIT_DIR)) : [];
294
335
  const unit = backgroundUnitName(pkg);
295
336
  if (units.length !== 1 || units[0] !== unit || read(`${BACKGROUND_UNIT_DIR}/${unit}`) !== renderBackgroundUnit(pkg)) {
296
337
  findings.push(`${BACKGROUND_UNIT_DIR} 必須只有固定的 ${unit}(套件帳號執行,無 User=、無 root)`);
297
338
  }
298
- if (read("scripts/start-stop-status") !== renderStandaloneScript("start-stop-status", { package: pkg, capabilities: { jobs: { worker: "sha256-v1" }, ...(executor ? { rootOps: true } : {}) } }))
339
+ if (read("scripts/start-stop-status") !== renderStandaloneScript("start-stop-status", { package: pkg, ...(daemon ? { daemon } : {}), capabilities: { jobs: { worker: "sha256-v1" }, ...(executor ? { rootOps: true } : {}) } }))
299
340
  findings.push("background scripts/start-stop-status 與固定 synosystemctl lifecycle 不符");
300
341
  }
301
342
  /**
@@ -304,7 +345,7 @@ function checkBackground(dir, info, payload, findings, executor) {
304
345
  * declaration (without one, any present file must be the default native
305
346
  * install wizard), and no other install/upgrade wizard file may exist.
306
347
  */
307
- function checkSharedFolder(dir, findings, pkg, folder) {
348
+ function checkSharedFolder(dir, findings, pkg, folder, selected = false, portal) {
308
349
  const wizardDir = join(dir, "WIZARD_UIFILES");
309
350
  const present = (existsSync(wizardDir) ? readdirSync(wizardDir) : []).filter(name => /^(install|upgrade)_uifile(_|\.|$)/.test(name)).sort();
310
351
  const postinst = readOptional(join(dir, "scripts/postinst"));
@@ -312,27 +353,69 @@ function checkSharedFolder(dir, findings, pkg, folder) {
312
353
  findings.push("scripts/postinst 的 install.sharedFolder 宣告無效");
313
354
  if (!pkg)
314
355
  return;
315
- const wizards = expectedWizardFiles(pkg, folder);
316
- const source = folder ? "install.sharedFolder 宣告" : "native-go 預設安裝精靈";
317
- // The default native wizard is optional; a declared shared folder needs all of its files.
318
- for (const name of folder ? Object.keys(wizards) : present.filter(n => n in wizards)) {
356
+ const wizards = expectedWizardFiles(pkg, folder, selected, portal);
357
+ const source = selected ? "install-selected policy" : folder ? "install.sharedFolder 宣告" : portal ? "portal 宣告" : "native-go 預設安裝精靈";
358
+ // The default native wizard is optional; a declared shared folder or portal needs all of its files.
359
+ for (const name of folder || selected || portal ? Object.keys(wizards) : present.filter(n => n in wizards)) {
319
360
  if (readOptional(join(wizardDir, name)) !== wizards[name])
320
361
  findings.push(`WIZARD_UIFILES/${name} 與${source}不符`);
321
362
  }
322
363
  for (const name of present.filter(n => !(n in wizards)))
323
364
  findings.push(`WIZARD_UIFILES/${name} 不是${source}產生的精靈檔`);
365
+ if (selected) {
366
+ for (const hook of ['preinst', 'postinst']) {
367
+ if (!readOptional(join(dir, 'scripts', hook))?.includes(renderDelegateInstallDraft(pkg, hook === 'preinst')))
368
+ findings.push(`scripts/${hook} missing install-selected draft handling`);
369
+ }
370
+ }
324
371
  if (!folder)
325
372
  return;
326
373
  for (const hook of ["postinst", "postupgrade"]) {
327
- if (readOptional(join(dir, "scripts", hook)) !== renderStandaloneScript(hook, { package: pkg, sharedFolder: folder })) {
374
+ if (readOptional(join(dir, "scripts", hook)) !== renderStandaloneScript(hook, { package: pkg, sharedFolder: folder, ...(selected ? { capabilities: { delegate: { scope: "install-selected" } } } : {}), ...portalManifest(portal) })) {
328
375
  findings.push(`scripts/${hook} 與 install.sharedFolder 宣告的固定腳本不符`);
329
376
  }
330
377
  }
331
378
  }
332
- function expectedWizardFiles(pkg, folder) {
333
- const install = renderInstallWizardFiles(pkg, folder ? sharedFolderInstallSteps(pkg, folder) : renderNativeInstallWizard);
379
+ function expectedWizardFiles(pkg, folder, selected = false, portal) {
380
+ const steps = selected ? (t) => [...renderDelegateScopeSteps(t), ...(folder ? sharedFolderInstallSteps(pkg, folder)(t) : [])] : folder ? sharedFolderInstallSteps(pkg, folder) : renderNativeInstallWizard;
381
+ const install = renderInstallWizardFiles(pkg, portal ? (t) => [...steps(t), renderPortalWizardStep(t, portal.alias)] : steps);
334
382
  return { ...install.static, ...install.dynamic, ...(folder ? renderSharedFolderUpgradeWizardFiles(pkg, folder) : {}) };
335
383
  }
384
+ function portalManifest(portal) {
385
+ return portal ? { displayname: portal.application, portal: { alias: portal.alias } } : {};
386
+ }
387
+ /**
388
+ * The portal declaration is recovered from the payload's etc/portal.json. Its
389
+ * files and alias lifecycle must be exactly what the generator writes for the
390
+ * package's own DSM window (native AppInstance or Compose `.url` entry); activation is never part of the package.
391
+ */
392
+ async function recoverPortal(dir, info, appId, paths, payload, findings) {
393
+ const files = [PORTAL_JSON_RELPATH, PORTAL_MUSTACHE_RELPATH];
394
+ if (!files.some(path => paths.has(path)))
395
+ return;
396
+ const unpacked = join(dir, "portal-inspection");
397
+ mkdirSync(unpacked);
398
+ await tar.x({ file: join(dir, "package.tgz"), cwd: unpacked, filter: path => files.includes(path.replace(/^\.\//, "")) });
399
+ try {
400
+ if (files.some(path => payload.find(p => p.path === path)?.mode !== "0644"))
401
+ throw new Error("mode");
402
+ const json = readFileSync(join(unpacked, PORTAL_JSON_RELPATH), "utf8");
403
+ const alias = JSON.parse(json).alias;
404
+ if (!isPortalAlias(alias))
405
+ throw new Error("alias");
406
+ const portal = { appId, application: info.displayname ?? "", alias };
407
+ if (json !== renderPortalJson(portal) || readFileSync(join(unpacked, PORTAL_MUSTACHE_RELPATH), "utf8") !== renderPortalMustache())
408
+ throw new Error("content");
409
+ for (const hook of ["preinst", "postinst", "postupgrade"]) {
410
+ if (!readOptional(join(dir, "scripts", hook))?.startsWith(`#!/bin/sh\n${renderPortalLifecycle(hook, portal)}`))
411
+ throw new Error(hook);
412
+ }
413
+ return portal;
414
+ }
415
+ catch {
416
+ findings.push("PORTAL_INVALID etc/portal.json、etc/portal.mustache 或 lifecycle 不符合固定 Application Portal 契約(應開啟套件自己的 DSM 視窗)");
417
+ }
418
+ }
336
419
  function checkScripts(dir, findings) {
337
420
  const scriptsDir = join(dir, "scripts");
338
421
  if (!existsSync(join(scriptsDir, "start-stop-status"))) {
@@ -1,27 +1,56 @@
1
- import { renderBackgroundControl, backgroundUnitName } from "./background.js";
1
+ import { renderBackgroundControl, renderBackgroundWorkerStart, renderBackgroundWorkerStop, renderUserManagerRecovery, backgroundUnitName } from "./background.js";
2
2
  import { renderPackagePostuninst } from "../build-spk/native-lifecycle.js";
3
3
  import { renderSharedFolderScript } from "./shared-folder.js";
4
+ import { renderDelegateInstallDraft } from '../build-spk/delegate-scope.js';
5
+ import { renderPortalLifecycle } from "../portal/package.js";
6
+ import { nativePortal } from "./portal.js";
4
7
  /** Package-account lifecycle hooks for CGI, daemon and background packages. */
5
8
  export function renderStandaloneScript(name, manifest) {
9
+ const portal = nativePortal(manifest);
10
+ const script = renderCapabilityScript(name, manifest);
11
+ return portal ? script.replace("#!/bin/sh\n", () => `#!/bin/sh\n${renderPortalLifecycle(name, portal)}`) : script;
12
+ }
13
+ function renderCapabilityScript(name, manifest) {
14
+ if (name === 'preupgrade' && (manifest.capabilities.rootOps || manifest.capabilities.delegate)) {
15
+ const installed = `\${SYNOPKG_PKGDEST:-/var/packages/${manifest.package}/target}`;
16
+ // Incoming preupgrade sees the OLD payload. Only complete absence is first
17
+ // introduction; a partial or symlinked executor must still fail closed.
18
+ const legacy = manifest.daemon
19
+ ? ` if [ -e "${installed}/daemon" ] || [ -L "${installed}/daemon" ]; then\n "${installed}/daemon/control" stop "${installed}" "\${SYNOPKG_PKGVAR:-/var/packages/${manifest.package}/var}" || exit $?\n fi\n`
20
+ : '';
21
+ return renderStandaloneScriptBody(name, manifest).replace('#!/bin/sh\n', `#!/bin/sh\nif [ ! -e "${installed}/executor" ] && [ ! -L "${installed}/executor" ]; then\n${legacy} exit 0\nfi\n`);
22
+ }
23
+ return renderStandaloneScriptBody(name, manifest);
24
+ }
25
+ function renderStandaloneScriptBody(name, manifest) {
26
+ if (manifest.capabilities?.delegate?.scope === 'install-selected' && ['preinst', 'postinst'].includes(name)) {
27
+ const normal = renderCapabilityScript(name, { ...manifest, capabilities: { ...manifest.capabilities, delegate: { ...manifest.capabilities.delegate, scope: 'home' } } });
28
+ return normal.replace('#!/bin/sh\n', () => '#!/bin/sh\n' + renderDelegateInstallDraft(manifest.package, name === 'preinst'));
29
+ }
30
+ if (manifest.daemon && manifest.capabilities?.jobs && (manifest.capabilities.delegate || manifest.capabilities.rootOps)
31
+ && ['start-stop-status', 'preupgrade', 'preuninst'].includes(name))
32
+ return renderDelegatedDaemonScript(name, manifest);
6
33
  if ((manifest.capabilities?.rootOps || manifest.capabilities?.delegate) && ["preuninst", "preupgrade", "start-stop-status"].includes(name)) {
7
34
  const helper = `"\${SYNOPKG_PKGDEST:-/var/packages/${manifest.package}/target}/executor/lifecycle"`;
8
- const stopWorker = manifest.capabilities.jobs ? `/usr/syno/bin/synosystemctl stop ${backgroundUnitName(manifest.package)} || echo "worker stop unconfirmed; continuing executor cleanup" >&2\n` : "";
35
+ // Attempt worker stop first, but let executor shutdown drain requests and
36
+ // close ingress even when it fails; a still-running worker fails the hook.
37
+ const workerStop = manifest.capabilities.jobs ? renderBackgroundWorkerStop(manifest.package) : "";
38
+ const stopWorker = manifest.capabilities.jobs ? "worker_result=0\nstop_background_worker || worker_result=1\n" : "";
39
+ const workerResult = manifest.capabilities.jobs ? 'exit "$worker_result"\n' : "exit 0\n";
9
40
  const request = (action) => `${helper} ${action} --package ${manifest.package} || exit $?\n`;
10
41
  if (name === "start-stop-status") {
11
- const normal = renderStandaloneScript(name, { ...manifest, capabilities: { ...manifest.capabilities, rootOps: undefined, delegate: undefined } });
12
- // Attempt worker stop first. DSM may have already stopped it or deny this
13
- // lifecycle context; executor shutdown drains requests and closes ingress.
42
+ const normal = renderCapabilityScript(name, { ...manifest, capabilities: { ...manifest.capabilities, rootOps: undefined, delegate: undefined } });
14
43
  const stopDaemon = manifest.daemon ? `"/var/packages/${manifest.package}/target/daemon/control" stop "\${SYNOPKG_PKGDEST:-/var/packages/${manifest.package}/target}" "\${SYNOPKG_PKGVAR:-/var/packages/${manifest.package}/var}" || exit $?\n` : "";
15
- return normal.replace("#!/bin/sh\n", `#!/bin/sh\nif [ "$1" = start ]; then\n${request("start")}fi\nif [ "$1" = stop ]; then\n${stopWorker}${stopDaemon}${request("stop")}exit 0\nfi\n`);
44
+ return normal.replace("#!/bin/sh\n", `#!/bin/sh\n${workerStop}if [ "$1" = start ]; then\n${request("start")}fi\nif [ "$1" = stop ]; then\n${stopWorker}${stopDaemon}${request("stop")}${workerResult}fi\n`);
16
45
  }
17
46
  const action = name === "preuninst" ? '"$action"' : "stop";
18
47
  const choose = name === "preuninst" ? 'action=purge\n[ "${SYNOPKG_PKG_STATUS:-}" != UPGRADE ] || action=stop\n' : "";
19
- return `#!/bin/sh\n${stopWorker}${choose}${request(action)}${manifest.daemon ? renderDaemonScript(name, manifest).replace("#!/bin/sh\n", "") : "exit 0\n"}`;
48
+ return `#!/bin/sh\n${workerStop}${stopWorker}${choose}${request(action)}${manifest.daemon ? renderDaemonScript(name, manifest).replace("#!/bin/sh\n", "") : workerResult}`;
20
49
  }
21
50
  if (name === "postuninst")
22
51
  return renderPackagePostuninst(manifest.package);
23
52
  if ((manifest.capabilities?.rootOps || manifest.capabilities?.delegate) && ["postinst", "postupgrade"].includes(name)) {
24
- const normal = renderStandaloneScript(name, { ...manifest, capabilities: { ...manifest.capabilities, rootOps: undefined, delegate: undefined } });
53
+ const normal = renderCapabilityScript(name, { ...manifest, capabilities: { ...manifest.capabilities, rootOps: undefined, delegate: undefined } });
25
54
  return normal.replace("#!/bin/sh\n", `#!/bin/sh\n"\${SYNOPKG_PKGDEST:-/var/packages/${manifest.package}/target}/executor/lifecycle" start --package ${manifest.package} || exit $?\n`);
26
55
  }
27
56
  if (manifest.sharedFolder && (name === "postinst" || name === "postupgrade"))
@@ -40,6 +69,55 @@ esac
40
69
  }
41
70
  return "#!/bin/sh\nexit 0\n";
42
71
  }
72
+ /** DSM owns the single user unit; the daemon controller owns only its producer. */
73
+ function renderDelegatedDaemonScript(name, manifest) {
74
+ const pkg = manifest.package;
75
+ const daemon = `"/var/packages/${pkg}/target/daemon/control"`;
76
+ const paths = `"\${SYNOPKG_PKGDEST:-/var/packages/${pkg}/target}" "\${SYNOPKG_PKGVAR:-/var/packages/${pkg}/var}"`;
77
+ const helper = `"\${SYNOPKG_PKGDEST:-/var/packages/${pkg}/target}/executor/lifecycle"`;
78
+ const executor = (action) => `${helper} ${action} --package ${pkg}`;
79
+ const worker = (action) => `/usr/syno/bin/synosystemctl ${action} ${backgroundUnitName(pkg)}`;
80
+ // Try every owner even on failure; never leave protected ingress open merely
81
+ // because a producer could not stop. A partial teardown remains a failure.
82
+ const stop = (daemonAction, executorAction) => `result=0\nstop_background_worker || result=1\n${daemon} ${daemonAction} ${paths} || result=1\n${executor(executorAction)} || result=1\nexit "$result"\n`;
83
+ const workerStop = renderBackgroundWorkerStop(pkg);
84
+ if (name !== 'start-stop-status') {
85
+ if (name === 'preupgrade')
86
+ return `#!/bin/sh\n${workerStop}${stop('stop', 'stop')}`;
87
+ return `#!/bin/sh\n${workerStop}if [ "\${SYNOPKG_PKG_STATUS:-}" = UPGRADE ]; then\n${stop('stop', 'stop')}fi\n${stop(manifest.daemon?.uninstall_args ? 'uninstall' : 'stop', 'purge')}`;
88
+ }
89
+ return `#!/bin/sh
90
+ ${workerStop}${renderUserManagerRecovery()}${renderBackgroundWorkerStart(pkg)}case "$1" in
91
+ prestart) recover_user_manager ;;
92
+ prestop) exit 0 ;;
93
+ start)
94
+ ${executor('start')} || exit $?
95
+ if ! ${daemon} start ${paths}; then
96
+ ${daemon} stop ${paths}
97
+ ${executor('stop')}
98
+ exit 1
99
+ fi
100
+ if ! start_background_worker; then
101
+ ${worker('stop')}
102
+ ${daemon} stop ${paths}
103
+ ${executor('stop')}
104
+ exit 1
105
+ fi
106
+ exit 0 ;;
107
+ status)
108
+ ${daemon} status ${paths} || exit $?
109
+ worker_state="$(${worker('get-active-status')})" || exit 3
110
+ [ "$worker_state" = active ] && exit 0
111
+ exit 3 ;;
112
+ stop)
113
+ ${stop('stop', 'stop')} ;;
114
+ restart)
115
+ "$0" stop || exit $?
116
+ exec "$0" start ;;
117
+ *) exit 1 ;;
118
+ esac
119
+ `;
120
+ }
43
121
  function renderDaemonScript(name, manifest) {
44
122
  const pkg = manifest.package;
45
123
  const control = `"/var/packages/${pkg}/target/daemon/control"`;
@@ -1,3 +1,4 @@
1
+ import { type PackageMetadata } from "../package-metadata.js";
1
2
  import { type DelegateCapability } from "../runtime/executor-policy.js";
2
3
  import type { ExecutorPins } from "../runtime/executor-tasks.js";
3
4
  import { type NativeArchFamily } from "../build-spk/dsm-arch.js";
@@ -7,6 +8,7 @@ import { type Daemon } from "./daemon.js";
7
8
  import { type Background } from "./background.js";
8
9
  import { type SharedFolder } from "./shared-folder.js";
9
10
  import { type ApiPermissions } from "./api-permissions.js";
11
+ import { type PortalDeclaration } from "../portal/alias.js";
10
12
  /**
11
13
  * `dsm-package.yaml`: a Standalone Package, i.e. an unsigned native SPK that
12
14
  * runs as its own DSM package account with a CGI backend and a DSM window UI.
@@ -31,6 +33,7 @@ export interface StandaloneI18n {
31
33
  info: Partial<Record<Locale, InfoText>>;
32
34
  }
33
35
  export interface StandaloneManifest {
36
+ metadata?: PackageMetadata;
34
37
  root: string;
35
38
  package: string;
36
39
  version: string;
@@ -62,7 +65,16 @@ export interface StandaloneManifest {
62
65
  daemon?: Daemon;
63
66
  /** `install.sharedFolder`: the Shared Folder the install wizard selects or `data-share` creates. */
64
67
  sharedFolder?: SharedFolder;
68
+ /** Who may use the app at all; absent keeps the route policy alone. */
69
+ entry?: EntryMode;
70
+ /** `portal`: registers the DSM Application Portal entry; an administrator activates it. */
71
+ portal?: PortalDeclaration;
65
72
  }
73
+ /**
74
+ * `admin-only`: DSM administrators only. `app-privilege`: administrators and
75
+ * accounts DSM's Control Panel > Application Privileges allows for the app.
76
+ */
77
+ export type EntryMode = "admin-only" | "app-privilege";
66
78
  export interface Capabilities {
67
79
  files?: "session";
68
80
  identity?: "standalone";
@@ -81,6 +93,7 @@ export type ManifestResult = {
81
93
  ok: false;
82
94
  errors: StandaloneError[];
83
95
  };
96
+ export declare const KNOWN_FIELDS: Set<string>;
84
97
  export declare function loadStandaloneManifest(root: string): ManifestResult;
85
98
  /**
86
99
  * The nearest directory at or above `start` that holds dsm-package.yaml,
@@ -1,3 +1,4 @@
1
+ import { validateMetadata } from "../package-metadata.js";
1
2
  import { validDelegate } from "../runtime/executor-policy.js";
2
3
  import { existsSync, readFileSync } from "node:fs";
3
4
  import { isAbsolute, join, relative, resolve } from "node:path";
@@ -7,17 +8,18 @@ import { checkGlossary, isLocale, loadCatalogs } from "./i18n.js";
7
8
  import { loadRootOps, ROOT_OPS_DIR } from "./root-ops.js";
8
9
  import { readDaemon } from "./daemon.js";
9
10
  import { readBackground } from "./background.js";
10
- import { DEFAULT_MIN_DSM, isDsmAtLeast } from "../build-spk/dsm-version.js";
11
+ import { DEFAULT_MIN_DSM, isDsmAtLeast, PORTAL_MIN_DSM, portalMinDsmMessage } from "../build-spk/dsm-version.js";
11
12
  import { readSharedFolder } from "./shared-folder.js";
12
- import { readApiPermissions } from "./api-permissions.js";
13
+ import { floorAtAdministrators, permitsOrdinaryUsers, readApiPermissions } from "./api-permissions.js";
14
+ import { readPortalDeclaration } from "../portal/alias.js";
13
15
  /**
14
16
  * `dsm-package.yaml`: a Standalone Package, i.e. an unsigned native SPK that
15
17
  * runs as its own DSM package account with a CGI backend and a DSM window UI.
16
18
  */
17
19
  export const STANDALONE_MANIFEST = "dsm-package.yaml";
18
- const KNOWN_FIELDS = new Set([
20
+ export const KNOWN_FIELDS = new Set([
19
21
  "schema", "package", "version", "build", "displayname", "description",
20
- "maintainer", "os_min_ver", "arches", "backend", "ui", "capabilities", "i18n", "daemon", "install",
22
+ "metadata", "maintainer", "os_min_ver", "arches", "backend", "ui", "capabilities", "i18n", "daemon", "install", "entry", "portal",
21
23
  ]);
22
24
  const I18N_FIELDS = new Set(["messages", "glossary", "info"]);
23
25
  const INFO_TEXT_FIELDS = new Set(["displayname", "description"]);
@@ -108,6 +110,14 @@ function readManifest(raw, c) {
108
110
  const displayname = c.text(raw.displayname, "displayname");
109
111
  const description = c.text(raw.description, "description");
110
112
  const maintainer = c.text(raw.maintainer, "maintainer");
113
+ let metadata;
114
+ if (raw.metadata !== undefined)
115
+ try {
116
+ metadata = validateMetadata(raw.metadata);
117
+ }
118
+ catch {
119
+ c.fail("STANDALONE_TEXT_INVALID", "metadata 欄位格式不正確;請檢查文字、email、URLs 與 screenshots。");
120
+ }
111
121
  const arches = readArches(raw.arches, c);
112
122
  const backend = arches && readBackend(raw.backend, arches, c);
113
123
  let permissions;
@@ -120,8 +130,8 @@ function readManifest(raw, c) {
120
130
  }
121
131
  }
122
132
  const daemon = arches && readDaemon(raw.daemon, c.root, arches, e => c.fail(e.code, e.message));
123
- if (capabilities.jobs && raw.daemon !== undefined) {
124
- c.fail("CAPABILITY_COMBINATION_INVALID", "daemon 與 capabilities.jobs 互斥。");
133
+ if (capabilities.jobs && raw.daemon !== undefined && capabilities.delegate?.lifetime !== 'until-revoked') {
134
+ c.fail("CAPABILITY_COMBINATION_INVALID", "daemon 僅可與明確 until-revoked delegate 的固定 worker 共存;一般 capabilities.jobs 仍互斥。");
125
135
  }
126
136
  if (backend?.kind === "binary") {
127
137
  for (const name of ["files", "jobs", "identity", "delegate"]) {
@@ -130,10 +140,25 @@ function readManifest(raw, c) {
130
140
  }
131
141
  }
132
142
  const sharedFolder = raw.install === undefined ? undefined : readSharedFolder(raw.install, e => c.fail(e.code, e.message));
143
+ const entry = raw.entry === undefined ? undefined : readEntry(raw.entry, backend, permissions?.policy, c);
144
+ if (entry === "admin-only" && permissions) {
145
+ const floored = floorAtAdministrators(permissions.policy);
146
+ if (floored.widened.length) {
147
+ c.fail("STANDALONE_ENTRY_WIDENED", `entry: admin-only 以管理員為所有 route 的下限;permissions.json 的 ${floored.widened.join("、")} 會開放給其他人。請改為 administrators,或改用 entry: app-privilege/省略 entry。`);
148
+ }
149
+ permissions.policy = floored.policy;
150
+ }
133
151
  const ui = readUi(raw.ui, c);
134
152
  if (capabilities.identity && ui && !ui.build)
135
153
  c.fail("CAPABILITY_COMBINATION_INVALID", "capabilities.identity 需要獨立登入頁;plain UI 不支援,請改用 vue UI(init --ui vue)。");
154
+ if (capabilities.delegate?.scope === "install-selected" && ui && !ui.build)
155
+ c.fail("CAPABILITY_COMBINATION_INVALID", "delegate scope install-selected 只能在 Vue 面板確認;plain UI 不支援,請改用 vue UI(init --ui vue)。");
136
156
  const i18n = raw.i18n === undefined ? undefined : readI18n(raw.i18n, c);
157
+ const portal = raw.portal === undefined || !packageId ? undefined : readPortalDeclaration(raw.portal, packageId)
158
+ ?? c.fail("STANDALONE_PORTAL_INVALID", "portal 必須是 {} 或 { alias: <小寫英數與 -,1–64 字,不可為 DSM 保留路徑> }。");
159
+ if (portal && osMinVer && !isDsmAtLeast(osMinVer, PORTAL_MIN_DSM)) {
160
+ c.fail("STANDALONE_PORTAL_MIN_DSM", portalMinDsmMessage(osMinVer, "portal"));
161
+ }
137
162
  if (!packageId || !version || !build || !osMinVer || !displayname || !description || !maintainer
138
163
  || !arches || !backend || !ui || (capabilities.rootOps === true && !rootOps)) {
139
164
  return undefined;
@@ -147,6 +172,7 @@ function readManifest(raw, c) {
147
172
  displayname,
148
173
  description,
149
174
  maintainer,
175
+ ...(metadata ? { metadata } : {}),
150
176
  os_min_ver: osMinVer,
151
177
  arches,
152
178
  backend,
@@ -157,8 +183,31 @@ function readManifest(raw, c) {
157
183
  ...(rootOps ? { rootOps } : {}),
158
184
  ...(daemon ? { daemon } : {}),
159
185
  ...(sharedFolder ? { sharedFolder } : {}),
186
+ ...(entry ? { entry } : {}),
187
+ ...(portal ? { portal } : {}),
160
188
  };
161
189
  }
190
+ /**
191
+ * The CGI enforces the entry mode only when the project's backend carries the
192
+ * SDK gate and main wires it; `-X main.entry` on an older main would be
193
+ * silently dropped and leave a registered app ungated.
194
+ */
195
+ function readEntry(value, backend, policy, c) {
196
+ if (value !== "admin-only" && value !== "app-privilege") {
197
+ return c.fail("STANDALONE_ENTRY_INVALID", "entry 必須是 admin-only 或 app-privilege;省略則維持既有 permissions.json 行為。");
198
+ }
199
+ if (!backend)
200
+ return undefined;
201
+ const main = backend.kind === "go" ? join(backend.dir, backend.main, "main.go") : "";
202
+ if (backend.kind !== "go" || !existsSync(join(backend.dir, "internal/dsm/entry.go"))
203
+ || !existsSync(main) || !readFileSync(main, "utf8").includes("dsm.NewEntryGate(")) {
204
+ return c.fail("STANDALONE_ENTRY_UNSUPPORTED", "entry 需要 Go backend 內的 SDK entry gate:請從 SDK 範本複製 internal/dsm/entry.go、apppriv.go 與 native bridge,並在 cmd/app/main.go 以 dsm.NewEntryGate 包住 authenticator。");
205
+ }
206
+ if (value === "app-privilege" && !permitsOrdinaryUsers(policy)) {
207
+ return c.fail("STANDALONE_ENTRY_INVALID", "entry: app-privilege 需要至少一個 permissions.json 標為 authenticated 的 route,一般使用者才有功能可用。");
208
+ }
209
+ return value;
210
+ }
162
211
  function readRootOps(enabled, c) {
163
212
  const dir = join(c.root, ROOT_OPS_DIR);
164
213
  if (!enabled) {
@@ -180,7 +229,7 @@ function readArches(value, c) {
180
229
  && new Set(value).size === value.length;
181
230
  return ok
182
231
  ? value
183
- : c.fail("STANDALONE_ARCHES_INVALID", "arches 必須是不重複的 x86_64/aarch64 清單,至少一個。");
232
+ : c.fail("STANDALONE_ARCHES_INVALID", "arches 必須是不重複的 x86_64/aarch64/armv7 清單,至少一個。");
184
233
  }
185
234
  function readBackend(value, arches, c) {
186
235
  if (!isRecord(value) || !["go", "binary"].includes(String(value.kind))) {
@@ -314,7 +363,7 @@ function readCapabilities(value, c) {
314
363
  if (validDelegate(setting))
315
364
  result.delegate = setting;
316
365
  else
317
- c.fail("CAPABILITY_INVALID", "delegate requires nonempty unique §2.4 files operations and scope home or shared:<name>.");
366
+ c.fail("CAPABILITY_INVALID", "delegate requires scope home or shared:<name> and legacy files operations, or scope home/shared:<name>/install-selected with exactly files.moveFromStaging, nonempty unique destinations and lifetime: until-revoked.");
318
367
  }
319
368
  else if (name === "files" && setting === "session")
320
369
  result.files = "session";
@@ -1,2 +1,2 @@
1
1
  /** Shared host flow. CGI plans are never accepted as completed authorization. */
2
- export declare const AUTHORIZATION_RUNTIME = "function createAuthorizationAccess(options) {\n var busy = null;\n function fail(code) { var error = new Error(code); error.code = code; return error; }\n function status() { return options.call(\"authorization.status\").then(function (d) {\n if (!d || d.command !== \"authorization\" || d.schema_version !== 1 || [\"ready\",\"authorization_required\",\"not_admin\",\"session_expired\",\"unavailable\"].indexOf(d.state) < 0) throw fail(\"authorization_bad_response\");\n return d;\n }); }\n function plan(method) { return options.call(method).then(function (p) {\n if (!p || p.command !== \"authorization\" || p.schema_version !== 1 || p.phase !== \"plan\" || !Array.isArray(p.operations) || p.operations.length === 0 || p.operations.length > options.tasks.length) throw fail(\"authorization_bad_response\");\n var seen = {};\n p.operations.forEach(function (op) { if (seen[op] || !options.tasks.some(function (t) { return t.op === op; })) throw fail(\"authorization_bad_response\"); seen[op] = true; });\n return p;\n }); }\n function exclusive(fn) { if (busy) return busy; busy = Promise.resolve().then(fn).then(function (s) { busy = null; return s; },function (e) { busy = null; throw e; });return busy; }\n function authorize(ops,run) { return ops.reduce(function (previous,op) { return previous.then(function () {\n return options.root.authorize(op).then(function (state) { if (state !== \"ready\") throw fail(\"root_cancelled\"); return run ? options.root.run(op) : null; });\n }); },Promise.resolve()); }\n return {canManage:function () {return !!options.user && options.user().admin === true;},status:status,request:function () { return exclusive(function () {\n return plan(\"authorization.request\").then(function (p) { return authorize(p.operations,p.run === true); }).then(status,function (e) { if (e.code === \"root_cancelled\") return status();throw e; });\n }); },revoke:function () { return exclusive(function () {\n return plan(\"authorization.revoke\").then(function (p) {\n if (p.withdraw === true && typeof options.withdraw !== \"function\") throw fail(\"authorization_bad_response\");\n var disabled = p.withdraw === true ? options.withdraw() : p.disable === true ? authorize([\"helper_disable\"],true) : Promise.resolve();\n return disabled.then(function () { return p.purge === true ? authorize([\"helper_purge\"],true) : null; }).then(function () { return p.operations.reduce(function (previous,op) {return previous.then(function () {return options.root.remove(op);});},Promise.resolve()); });\n }).then(status,function (e) { if (e.code === \"root_cancelled\") return status();throw e; });\n }); }};\n}";
2
+ export declare const AUTHORIZATION_RUNTIME = "function createAuthorizationAccess(options) {\n var busy = null;\n function fail(code) { var error = new Error(code); error.code = code; return error; }\n function status() { return options.call(\"authorization.status\").then(function (d) {\n if (!d || d.command !== \"authorization\" || d.schema_version !== 1 || [\"ready\",\"authorization_required\",\"not_admin\",\"session_expired\",\"unavailable\"].indexOf(d.state) < 0) throw fail(\"authorization_bad_response\");\n // upgrade_reapproval is a reason-only tombstone: it can only explain a pending request.\n if (d.reason === \"upgrade_reapproval\" && d.state !== \"authorization_required\") throw fail(\"authorization_bad_response\");\n return d;\n }); }\n function plan(method) { return options.call(method).then(function (p) {\n if (!p || p.command !== \"authorization\" || p.schema_version !== 1 || p.phase !== \"plan\" || !Array.isArray(p.operations) || p.operations.length === 0 || p.operations.length > options.tasks.length) throw fail(\"authorization_bad_response\");\n var seen = {};\n p.operations.forEach(function (op) { if (seen[op] || !options.tasks.some(function (t) { return t.op === op; })) throw fail(\"authorization_bad_response\"); seen[op] = true; });\n return p;\n }); }\n // The backend's grant decides: a task still waiting at the window's limit, or\n // one signalled after it wrote the grant, has still authorized the package.\n function settled(e) {\n if (!e || (e.code !== \"root_timeout\" && e.code !== \"root_task_failed\")) throw e;\n return status().then(function (d) { if (d.state !== \"ready\") throw e; });\n }\n function exclusive(fn) { if (busy) return busy; busy = Promise.resolve().then(fn).then(function (s) { busy = null; return s; },function (e) { busy = null; throw e; });return busy; }\n function authorize(ops,run) { return ops.reduce(function (previous,op) { return previous.then(function () {\n return options.root.authorize(op).then(function (state) { if (state !== \"ready\") throw fail(\"root_cancelled\"); return run ? options.root.run(op).catch(settled) : null; });\n }); },Promise.resolve()); }\n return {canManage:function () {return !!options.user && options.user().admin === true;},status:status,request:function () { return exclusive(function () {\n return plan(\"authorization.request\").then(function (p) { return authorize(p.operations,p.run === true); }).then(status,function (e) { if (e.code === \"root_cancelled\") return status();throw e; });\n }); },revoke:function () { return exclusive(function () {\n return plan(\"authorization.revoke\").then(function (p) {\n if (p.withdraw === true && typeof options.withdraw !== \"function\") throw fail(\"authorization_bad_response\");\n var disabled = p.withdraw === true ? options.withdraw() : p.disable === true ? authorize([\"helper_disable\"],true) : Promise.resolve();\n return disabled.then(function () { return p.purge === true ? authorize([\"helper_purge\"],true) : null; }).then(function () { return p.operations.reduce(function (previous,op) {return previous.then(function () {return options.root.remove(op);});},Promise.resolve()); });\n }).then(status,function (e) { if (e.code === \"root_cancelled\") return status();throw e; });\n }); }};\n}";