@synopackageland/cli 0.6.0 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (585) hide show
  1. package/CHANGELOG.md +613 -0
  2. package/README.md +81 -78
  3. package/dist/add.js +39 -16
  4. package/dist/authorization-sdk.d.ts +7 -0
  5. package/dist/authorization-sdk.js +7 -0
  6. package/dist/build-spk/delegate-scope.d.ts +6 -0
  7. package/dist/build-spk/delegate-scope.js +34 -0
  8. package/dist/build-spk/dsm-arch.d.ts +7 -1
  9. package/dist/build-spk/dsm-arch.js +24 -28
  10. package/dist/build-spk/dsm-platforms.json +39 -0
  11. package/dist/build-spk/dsm-version.d.ts +11 -0
  12. package/dist/build-spk/dsm-version.js +17 -0
  13. package/dist/build-spk/gateway.d.ts +1 -1
  14. package/dist/build-spk/gateway.js +2 -2
  15. package/dist/build-spk/generator.js +30 -5
  16. package/dist/build-spk/notices.js +4 -1
  17. package/dist/build-spk/resource-worker.js +3 -0
  18. package/dist/build-spk/wizard-messages.d.ts +66 -0
  19. package/dist/build-spk/wizard-messages.js +75 -9
  20. package/dist/cli.js +30 -18
  21. package/dist/contract/schema-validate.js +8 -0
  22. package/dist/contract/validate.js +30 -0
  23. package/dist/deploy/deploy.d.ts +1 -1
  24. package/dist/deploy/deploy.js +8 -0
  25. package/dist/deploy/plan.js +1 -1
  26. package/dist/deploy/spk-version.js +1 -1
  27. package/dist/error-registry.js +46 -13
  28. package/dist/host/dsm-client.d.ts +8 -4
  29. package/dist/host/dsm-client.js +10 -4
  30. package/dist/host/types.d.ts +2 -1
  31. package/dist/info.js +18 -2
  32. package/dist/init.d.ts +3 -0
  33. package/dist/init.js +49 -18
  34. package/dist/package-metadata.d.ts +21 -0
  35. package/dist/package-metadata.js +101 -0
  36. package/dist/portal/alias.d.ts +25 -0
  37. package/dist/portal/alias.js +48 -0
  38. package/dist/portal/package.d.ts +33 -0
  39. package/dist/portal/package.js +116 -0
  40. package/dist/runtime/delegate-contract.d.ts +79 -0
  41. package/dist/runtime/delegate-contract.js +27 -0
  42. package/dist/runtime/executor-build.d.ts +1 -1
  43. package/dist/runtime/executor-build.js +5 -4
  44. package/dist/runtime/executor-inspect.d.ts +3 -0
  45. package/dist/runtime/executor-inspect.js +11 -5
  46. package/dist/runtime/executor-policy.d.ts +12 -0
  47. package/dist/runtime/executor-policy.js +43 -3
  48. package/dist/runtime/executor-scope-task.d.ts +4 -0
  49. package/dist/runtime/executor-scope-task.js +21 -0
  50. package/dist/runtime/executor-tasks.d.ts +2 -1
  51. package/dist/runtime/executor-tasks.js +4 -4
  52. package/dist/skill.js +1 -0
  53. package/dist/standalone/api-permissions.d.ts +10 -0
  54. package/dist/standalone/api-permissions.js +19 -2
  55. package/dist/standalone/background.d.ts +16 -0
  56. package/dist/standalone/background.js +69 -2
  57. package/dist/standalone/binary.d.ts +1 -1
  58. package/dist/standalone/binary.js +22 -8
  59. package/dist/standalone/build.d.ts +1 -1
  60. package/dist/standalone/build.js +38 -8
  61. package/dist/standalone/daemon.d.ts +1 -1
  62. package/dist/standalone/daemon.js +2 -1
  63. package/dist/standalone/dsm-window.d.ts +3 -1
  64. package/dist/standalone/dsm-window.js +72 -15
  65. package/dist/standalone/inspect-spk.d.ts +9 -1
  66. package/dist/standalone/inspect-spk.js +119 -26
  67. package/dist/standalone/lifecycle.js +86 -8
  68. package/dist/standalone/manifest.d.ts +13 -0
  69. package/dist/standalone/manifest.js +57 -8
  70. package/dist/standalone/native-authorization-window.d.ts +2 -0
  71. package/dist/standalone/native-authorization-window.js +30 -0
  72. package/dist/standalone/native-layout.d.ts +82 -0
  73. package/dist/standalone/native-layout.js +377 -0
  74. package/dist/standalone/native-root-window.d.ts +26 -0
  75. package/dist/standalone/native-root-window.js +282 -0
  76. package/dist/standalone/portal-window.d.ts +21 -0
  77. package/dist/standalone/portal-window.js +155 -0
  78. package/dist/standalone/portal.d.ts +4 -0
  79. package/dist/standalone/portal.js +4 -0
  80. package/dist/standalone/resource.d.ts +5 -3
  81. package/dist/standalone/resource.js +6 -3
  82. package/dist/standalone/root-window.js +62 -15
  83. package/dist/standalone/spk.d.ts +1 -1
  84. package/dist/standalone/spk.js +25 -7
  85. package/dist/templates/_daemon/main_test.go +61 -8
  86. package/dist/templates/_executor/README.md +127 -16
  87. package/dist/templates/_executor/anchor.go +146 -0
  88. package/dist/templates/_executor/anchor_test.go +126 -0
  89. package/dist/templates/_executor/apppriv_review_test.go +1 -1
  90. package/dist/templates/_executor/cancel_contract_test.go +236 -0
  91. package/dist/templates/_executor/copy.go +64 -0
  92. package/dist/templates/_executor/copy_test.go +88 -0
  93. package/dist/templates/_executor/dsm.go +23 -10
  94. package/dist/templates/_executor/dsm_test.go +95 -1
  95. package/dist/templates/_executor/executor.go +260 -49
  96. package/dist/templates/_executor/executor_test.go +36 -6
  97. package/dist/templates/_executor/identity.go +27 -1
  98. package/dist/templates/_executor/identity_kernel_linux_test.go +60 -0
  99. package/dist/templates/_executor/identity_linux.go +82 -0
  100. package/dist/templates/_executor/identity_security_test.go +43 -0
  101. package/dist/templates/_executor/internal/lifecycle/lifecycle.go +5 -0
  102. package/dist/templates/_executor/internal/lifecycle/lifecycle_test.go +61 -3
  103. package/dist/templates/_executor/internal/taskscript/boot.sh +9 -9
  104. package/dist/templates/_executor/internal/taskscript/cmd/main.go +2 -2
  105. package/dist/templates/_executor/internal/taskscript/control.sh +15 -10
  106. package/dist/templates/_executor/internal/taskscript/tasks.go +5 -3
  107. package/dist/templates/_executor/lifecycle_test.go +11 -0
  108. package/dist/templates/_executor/link_linux.go +28 -0
  109. package/dist/templates/_executor/long_running_test.go +530 -0
  110. package/dist/templates/_executor/main.go +72 -20
  111. package/dist/templates/_executor/move_linux.go +490 -0
  112. package/dist/templates/_executor/move_linux_test.go +460 -0
  113. package/dist/templates/_executor/move_validation.go +46 -0
  114. package/dist/templates/_executor/move_validation_test.go +187 -0
  115. package/dist/templates/_executor/operations.go +926 -0
  116. package/dist/templates/_executor/platform_linux.go +24 -173
  117. package/dist/templates/_executor/platform_linux_test.go +9 -65
  118. package/dist/templates/_executor/platform_other.go +30 -4
  119. package/dist/templates/_executor/process.go +3 -0
  120. package/dist/templates/_executor/records.go +242 -0
  121. package/dist/templates/_executor/scope_cancel_contract_test.go +91 -0
  122. package/dist/templates/_executor/scope_client.go +37 -0
  123. package/dist/templates/_executor/scope_contract.go +12 -0
  124. package/dist/templates/_executor/selected_scope.go +146 -0
  125. package/dist/templates/_executor/selected_scope_test.go +186 -0
  126. package/dist/templates/_executor/status_disabled_test.go +33 -0
  127. package/dist/templates/_executor/store.go +4 -3
  128. package/dist/templates/_executor/supervise.go +109 -0
  129. package/dist/templates/_executor/supervise_test.go +130 -0
  130. package/dist/templates/_executor/task_race_review_test.go +1 -1
  131. package/dist/templates/_executor/tasks.go +7 -8
  132. package/dist/templates/_executor/tasks_review_test.go +1 -1
  133. package/dist/templates/_executor/tasks_test.go +4 -4
  134. package/dist/templates/_executor/watch.go +8 -0
  135. package/dist/templates/_executor/watch_test.go +23 -2
  136. package/dist/templates/_executor/write_review_linux_test.go +4 -12
  137. package/dist/templates/_shared/AGENTS.md +129 -0
  138. package/dist/templates/binary/dsm-package.yaml +24 -0
  139. package/dist/templates/binary/i18n/messages/de.json +4 -0
  140. package/dist/templates/binary/i18n/messages/en.json +4 -0
  141. package/dist/templates/binary/i18n/messages/es.json +4 -0
  142. package/dist/templates/binary/i18n/messages/fr.json +4 -0
  143. package/dist/templates/binary/i18n/messages/it.json +4 -0
  144. package/dist/templates/binary/i18n/messages/ja.json +4 -0
  145. package/dist/templates/binary/i18n/messages/ko.json +4 -0
  146. package/dist/templates/binary/i18n/messages/zh-CN.json +4 -0
  147. package/dist/templates/binary/i18n/messages/zh-TW.json +4 -0
  148. package/dist/templates/binary/ui/app.js +19 -4
  149. package/dist/templates/native-authorization/AuthorizationGate.vue +91 -0
  150. package/dist/templates/native-authorization/i18n/de.json +37 -0
  151. package/dist/templates/native-authorization/i18n/en.json +37 -0
  152. package/dist/templates/native-authorization/i18n/es.json +37 -0
  153. package/dist/templates/native-authorization/i18n/fr.json +37 -0
  154. package/dist/templates/native-authorization/i18n/index.ts +15 -0
  155. package/dist/templates/native-authorization/i18n/it.json +37 -0
  156. package/dist/templates/native-authorization/i18n/ja.json +37 -0
  157. package/dist/templates/native-authorization/i18n/ko.json +37 -0
  158. package/dist/templates/native-authorization/i18n/zh-CN.json +37 -0
  159. package/dist/templates/native-authorization/i18n/zh-TW.json +37 -0
  160. package/dist/templates/native-go/README.md +43 -10
  161. package/dist/templates/native-go/backend/cmd/app/main.go +27 -7
  162. package/dist/templates/native-go/backend/internal/api/entry_test.go +47 -0
  163. package/dist/templates/native-go/backend/internal/api/permissions_test.go +3 -3
  164. package/dist/templates/native-go/backend/internal/api/registry.go +11 -0
  165. package/dist/templates/native-go/backend/internal/api/sdk-permissions.json +14 -1
  166. package/dist/templates/native-go/backend/internal/api/server.go +18 -5
  167. package/dist/templates/native-go/backend/internal/app/delegate_config.go +57 -0
  168. package/dist/templates/native-go/backend/internal/app/delegate_config_test.go +28 -0
  169. package/dist/templates/native-go/backend/internal/app/portal.go +30 -0
  170. package/dist/templates/native-go/backend/internal/app/portal_test.go +55 -0
  171. package/dist/templates/native-go/backend/internal/app/routes.go +8 -2
  172. package/dist/templates/native-go/backend/internal/background/runner.go +1 -1
  173. package/dist/templates/native-go/backend/internal/background/store.go +16 -14
  174. package/dist/templates/native-go/backend/internal/dsm/admin.go +8 -2
  175. package/dist/templates/native-go/backend/internal/dsm/admin_linux.go +43 -13
  176. package/dist/templates/native-go/backend/internal/dsm/admin_linux_test.go +56 -0
  177. package/dist/templates/native-go/backend/internal/dsm/admin_other.go +4 -1
  178. package/dist/templates/native-go/backend/internal/dsm/apppriv.go +30 -0
  179. package/dist/templates/native-go/backend/internal/dsm/bridge_file.go +75 -0
  180. package/dist/templates/native-go/backend/internal/dsm/bridge_file_test.go +57 -0
  181. package/dist/templates/native-go/backend/internal/dsm/entry.go +177 -0
  182. package/dist/templates/native-go/backend/internal/dsm/entry_test.go +315 -0
  183. package/dist/templates/native-go/backend/internal/dsm/native/admin-armv7.elf +0 -0
  184. package/dist/templates/native-go/backend/internal/dsm/native/apppriv-aarch64.elf +0 -0
  185. package/dist/templates/native-go/backend/internal/dsm/native/apppriv-armv7.elf +0 -0
  186. package/dist/templates/native-go/backend/internal/dsm/native/apppriv-x86_64.elf +0 -0
  187. package/dist/templates/native-go/backend/internal/dsm/native/apppriv_check.c +33 -0
  188. package/dist/templates/native-go/backend/internal/dsm/native/build.sh +13 -4
  189. package/dist/templates/native-go/backend/internal/dsm/native/provenance.json +9 -3
  190. package/dist/templates/native-go/backend/internal/filestation/check_permission.go +71 -0
  191. package/dist/templates/native-go/backend/internal/filestation/check_permission_test.go +77 -0
  192. package/dist/templates/native-go/backend/internal/filestation/sdk_policy_test.go +39 -0
  193. package/dist/templates/native-go/backend/internal/filestation/service.go +1 -1
  194. package/dist/templates/native-go/backend/internal/portal/portal.go +138 -0
  195. package/dist/templates/native-go/backend/internal/portal/portal_test.go +89 -0
  196. package/dist/templates/native-go/dsm-package.yaml +20 -1
  197. package/dist/templates/native-go/i18n/glossary.json +2 -1
  198. package/dist/templates/native-go/i18n/messages/de.json +1 -0
  199. package/dist/templates/native-go/i18n/messages/en.json +1 -0
  200. package/dist/templates/native-go/i18n/messages/es.json +1 -0
  201. package/dist/templates/native-go/i18n/messages/fr.json +1 -0
  202. package/dist/templates/native-go/i18n/messages/it.json +1 -0
  203. package/dist/templates/native-go/i18n/messages/ja.json +1 -0
  204. package/dist/templates/native-go/i18n/messages/ko.json +1 -0
  205. package/dist/templates/native-go/i18n/messages/zh-CN.json +1 -0
  206. package/dist/templates/native-go/i18n/messages/zh-TW.json +1 -0
  207. package/dist/templates/native-go/ui/app.css +10 -0
  208. package/dist/templates/native-go/ui/app.js +11 -1
  209. package/dist/templates/native-go-delegate/DELEGATE.md +126 -5
  210. package/dist/templates/native-go-delegate/backend/cmd/background-worker/main.go +7 -0
  211. package/dist/templates/native-go-delegate/backend/internal/app/delegate.go +11 -1
  212. package/dist/templates/native-go-delegate/backend/internal/app/delegate_routes_test.go +14 -0
  213. package/dist/templates/native-go-delegate/backend/internal/background/delegated_moves.go +270 -0
  214. package/dist/templates/native-go-delegate/backend/internal/delegate/admission_recovery_test.go +410 -0
  215. package/dist/templates/native-go-delegate/backend/internal/delegate/cancel_contract_test.go +95 -0
  216. package/dist/templates/native-go-delegate/backend/internal/delegate/client.go +8 -0
  217. package/dist/templates/native-go-delegate/backend/internal/delegate/destination.go +110 -0
  218. package/dist/templates/native-go-delegate/backend/internal/delegate/destination_state.go +117 -0
  219. package/dist/templates/native-go-delegate/backend/internal/delegate/destination_state_test.go +60 -0
  220. package/dist/templates/native-go-delegate/backend/internal/delegate/destination_test.go +116 -0
  221. package/dist/templates/native-go-delegate/backend/internal/delegate/example.go +81 -0
  222. package/dist/templates/native-go-delegate/backend/internal/delegate/example_test.go +91 -0
  223. package/dist/templates/native-go-delegate/backend/internal/delegate/executor_contract_test.go +511 -0
  224. package/dist/templates/native-go-delegate/backend/internal/delegate/files.go +1 -1
  225. package/dist/templates/native-go-delegate/backend/internal/delegate/long_running_routes.go +92 -0
  226. package/dist/templates/native-go-delegate/backend/internal/delegate/long_running_routes_test.go +99 -0
  227. package/dist/templates/native-go-delegate/backend/internal/delegate/move.go +348 -0
  228. package/dist/templates/native-go-delegate/backend/internal/delegate/move_test.go +351 -0
  229. package/dist/templates/native-go-delegate/backend/internal/delegate/operations_list.go +65 -0
  230. package/dist/templates/native-go-delegate/backend/internal/delegate/operations_list_test.go +114 -0
  231. package/dist/templates/native-go-delegate/backend/internal/delegate/retry_test.go +1 -1
  232. package/dist/templates/native-go-delegate/backend/internal/delegate/service.go +58 -17
  233. package/dist/templates/native-go-delegate/backend/internal/delegate/staging.go +121 -0
  234. package/dist/templates/native-go-delegate/backend/internal/delegate/status_disabled_test.go +51 -0
  235. package/dist/templates/native-go-delegate/backend/internal/delegate/testdata/executor_contract.txt +112 -0
  236. package/dist/templates/native-go-delegate/backend/internal/delegate/transient_contract_test.go +137 -0
  237. package/dist/templates/native-go-delegate/backend/internal/filestation/files.go +1 -31
  238. package/dist/templates/native-go-delegate/frontend/src/CapabilityPanels.vue +30 -3
  239. package/dist/templates/native-go-delegate/frontend/src/delegate/BackgroundSavePanel.vue +113 -0
  240. package/dist/templates/native-go-delegate/frontend/src/delegate/copy.ts +5 -6
  241. package/dist/templates/native-go-delegate/frontend/test/background-save.test.ts +121 -0
  242. package/dist/templates/native-go-delegate/frontend/test/scope-settings.test.ts +49 -0
  243. package/dist/templates/native-go-delegate/test.sh +1 -0
  244. package/dist/templates/native-go-jobs/frontend/src/background/i18n/de.json +2 -2
  245. package/dist/templates/native-go-jobs/frontend/src/background/i18n/en.json +2 -2
  246. package/dist/templates/native-go-jobs/frontend/src/background/i18n/es.json +12 -12
  247. package/dist/templates/native-go-jobs/frontend/src/background/i18n/fr.json +4 -4
  248. package/dist/templates/native-go-jobs/frontend/src/background/i18n/it.json +17 -17
  249. package/dist/templates/native-go-jobs/frontend/src/background/i18n/ja.json +7 -7
  250. package/dist/templates/native-go-jobs/frontend/src/background/i18n/ko.json +15 -15
  251. package/dist/templates/native-go-jobs/frontend/src/background/i18n/zh-CN.json +3 -3
  252. package/dist/templates/native-go-jobs/frontend/src/background/i18n/zh-TW.json +2 -2
  253. package/dist/templates/native-go-jobs/frontend/test/background.test.ts +25 -28
  254. package/dist/templates/native-go-vue/README.md +4 -0
  255. package/dist/templates/native-go-vue/frontend/package-lock.json +4 -4
  256. package/dist/templates/native-go-vue/frontend/package.json +1 -1
  257. package/dist/templates/native-go-vue/frontend/src/App.vue +18 -13
  258. package/dist/templates/native-go-vue/frontend/src/context.ts +31 -0
  259. package/dist/templates/native-go-vue/frontend/src/delegate/ExecutorSettings.vue +52 -8
  260. package/dist/templates/native-go-vue/frontend/src/delegate/catalogs.ts +11 -0
  261. package/dist/templates/native-go-vue/frontend/src/delegate/copy.test.ts +12 -0
  262. package/dist/templates/native-go-vue/frontend/src/delegate/copy.ts +4 -4
  263. package/dist/templates/native-go-vue/frontend/src/delegate/executor-setup.ts +4 -2
  264. package/dist/templates/native-go-vue/frontend/src/delegate/facade.test.ts +100 -0
  265. package/dist/templates/native-go-vue/frontend/src/delegate/facade.ts +85 -0
  266. package/dist/templates/native-go-vue/frontend/src/delegate/i18n/de.json +46 -0
  267. package/dist/templates/native-go-vue/frontend/src/delegate/i18n/en.json +12 -2
  268. package/dist/templates/native-go-vue/frontend/src/delegate/i18n/es.json +46 -0
  269. package/dist/templates/native-go-vue/frontend/src/delegate/i18n/fr.json +46 -0
  270. package/dist/templates/native-go-vue/frontend/src/delegate/i18n/it.json +46 -0
  271. package/dist/templates/native-go-vue/frontend/src/delegate/i18n/ja.json +46 -0
  272. package/dist/templates/native-go-vue/frontend/src/delegate/i18n/ko.json +46 -0
  273. package/dist/templates/native-go-vue/frontend/src/delegate/i18n/save/de.json +42 -0
  274. package/dist/templates/native-go-vue/frontend/src/delegate/i18n/save/en.json +42 -0
  275. package/dist/templates/native-go-vue/frontend/src/delegate/i18n/save/es.json +42 -0
  276. package/dist/templates/native-go-vue/frontend/src/delegate/i18n/save/fr.json +42 -0
  277. package/dist/templates/native-go-vue/frontend/src/delegate/i18n/save/it.json +42 -0
  278. package/dist/templates/native-go-vue/frontend/src/delegate/i18n/save/ja.json +42 -0
  279. package/dist/templates/native-go-vue/frontend/src/delegate/i18n/save/ko.json +42 -0
  280. package/dist/templates/native-go-vue/frontend/src/delegate/i18n/save/zh-CN.json +42 -0
  281. package/dist/templates/native-go-vue/frontend/src/delegate/i18n/save/zh-TW.json +42 -0
  282. package/dist/templates/native-go-vue/frontend/src/delegate/i18n/zh-CN.json +46 -0
  283. package/dist/templates/native-go-vue/frontend/src/delegate/i18n/zh-TW.json +15 -5
  284. package/dist/templates/native-go-vue/frontend/src/delegate/long-running.ts +57 -0
  285. package/dist/templates/native-go-vue/frontend/src/delegate/save-copy.test.ts +12 -0
  286. package/dist/templates/native-go-vue/frontend/src/delegate/save-copy.ts +24 -0
  287. package/dist/templates/native-go-vue/frontend/src/delegate/scoped-picker.test.ts +12 -0
  288. package/dist/templates/native-go-vue/frontend/src/delegate/scoped-picker.ts +21 -0
  289. package/dist/templates/native-go-vue/frontend/src/files/files.test.ts +7 -0
  290. package/dist/templates/native-go-vue/frontend/src/i18n/de.json +20 -0
  291. package/dist/templates/native-go-vue/frontend/src/i18n/en.json +20 -0
  292. package/dist/templates/native-go-vue/frontend/src/i18n/es.json +20 -0
  293. package/dist/templates/native-go-vue/frontend/src/i18n/fr.json +20 -0
  294. package/dist/templates/native-go-vue/frontend/src/i18n/it.json +20 -0
  295. package/dist/templates/native-go-vue/frontend/src/i18n/ja.json +20 -0
  296. package/dist/templates/native-go-vue/frontend/src/i18n/ko.json +20 -0
  297. package/dist/templates/native-go-vue/frontend/src/i18n/zh-CN.json +20 -0
  298. package/dist/templates/native-go-vue/frontend/src/i18n/zh-TW.json +20 -0
  299. package/dist/templates/native-go-vue/frontend/src/i18n.ts +13 -0
  300. package/dist/templates/native-go-vue/frontend/src/identity/LoginPage.vue +16 -14
  301. package/dist/templates/native-go-vue/frontend/src/identity/i18n/de.json +27 -0
  302. package/dist/templates/native-go-vue/frontend/src/identity/i18n/en.json +27 -0
  303. package/dist/templates/native-go-vue/frontend/src/identity/i18n/es.json +27 -0
  304. package/dist/templates/native-go-vue/frontend/src/identity/i18n/fr.json +27 -0
  305. package/dist/templates/native-go-vue/frontend/src/identity/i18n/it.json +27 -0
  306. package/dist/templates/native-go-vue/frontend/src/identity/i18n/ja.json +27 -0
  307. package/dist/templates/native-go-vue/frontend/src/identity/i18n/ko.json +27 -0
  308. package/dist/templates/native-go-vue/frontend/src/identity/i18n/zh-CN.json +27 -0
  309. package/dist/templates/native-go-vue/frontend/src/identity/i18n/zh-TW.json +27 -0
  310. package/dist/templates/native-go-vue/frontend/src/identity/i18n.ts +14 -0
  311. package/dist/templates/native-go-vue/frontend/src/identity/session.test.ts +4 -0
  312. package/dist/templates/native-go-vue/frontend/src/identity/session.ts +9 -8
  313. package/dist/templates/native-go-vue/frontend/src/nas/NasPicker.vue +31 -12
  314. package/dist/templates/native-go-vue/frontend/src/nas/destination.test.ts +48 -0
  315. package/dist/templates/native-go-vue/frontend/src/nas/i18n/files/de.json +1 -1
  316. package/dist/templates/native-go-vue/frontend/src/nas/i18n/files/en.json +1 -1
  317. package/dist/templates/native-go-vue/frontend/src/nas/i18n/files/es.json +10 -10
  318. package/dist/templates/native-go-vue/frontend/src/nas/i18n/files/fr.json +1 -1
  319. package/dist/templates/native-go-vue/frontend/src/nas/i18n/files/it.json +12 -12
  320. package/dist/templates/native-go-vue/frontend/src/nas/i18n/files/ja.json +15 -15
  321. package/dist/templates/native-go-vue/frontend/src/nas/i18n/files/ko.json +12 -12
  322. package/dist/templates/native-go-vue/frontend/src/nas/i18n/files/zh-CN.json +1 -1
  323. package/dist/templates/native-go-vue/frontend/src/nas/i18n/files/zh-TW.json +1 -1
  324. package/dist/templates/native-go-vue/frontend/src/portal/PortalSettings.vue +39 -0
  325. package/dist/templates/native-go-vue/frontend/src/shell/i18n/fr.json +1 -1
  326. package/dist/templates/native-go-vue/frontend/src/shell/i18n/ja.json +1 -1
  327. package/dist/templates/native-go-vue/frontend/test/apps.test.ts +14 -0
  328. package/dist/templates/native-go-vue/frontend/test/portal-settings.test.ts +71 -0
  329. package/dist/templates/native-go-vue/frontend/test/shell-i18n.test.ts +50 -0
  330. package/dist/validate.js +2 -1
  331. package/package.json +12 -2
  332. package/schema/package-metadata.schema.json +101 -0
  333. package/schema/synology-app.schema.json +112 -0
  334. package/skills/SKILL.md +109 -3
  335. package/skills/reference/create.md +101 -2
  336. package/skills/reference/deploy.md +2 -2
  337. package/skills/reference/files.md +113 -2
  338. package/skills/reference/identity.md +70 -1
  339. package/skills/reference/portal.md +17 -0
  340. package/skills/reference/ui-vue.md +12 -0
  341. package/templates/_daemon/main_test.go +61 -8
  342. package/templates/_executor/README.md +127 -16
  343. package/templates/_executor/anchor.go +146 -0
  344. package/templates/_executor/anchor_test.go +126 -0
  345. package/templates/_executor/apppriv_review_test.go +1 -1
  346. package/templates/_executor/cancel_contract_test.go +236 -0
  347. package/templates/_executor/copy.go +64 -0
  348. package/templates/_executor/copy_test.go +88 -0
  349. package/templates/_executor/dsm.go +23 -10
  350. package/templates/_executor/dsm_test.go +95 -1
  351. package/templates/_executor/executor.go +260 -49
  352. package/templates/_executor/executor_test.go +36 -6
  353. package/templates/_executor/identity.go +27 -1
  354. package/templates/_executor/identity_kernel_linux_test.go +60 -0
  355. package/templates/_executor/identity_linux.go +82 -0
  356. package/templates/_executor/identity_security_test.go +43 -0
  357. package/templates/_executor/internal/lifecycle/lifecycle.go +5 -0
  358. package/templates/_executor/internal/lifecycle/lifecycle_test.go +61 -3
  359. package/templates/_executor/internal/taskscript/boot.sh +9 -9
  360. package/templates/_executor/internal/taskscript/cmd/main.go +2 -2
  361. package/templates/_executor/internal/taskscript/control.sh +15 -10
  362. package/templates/_executor/internal/taskscript/tasks.go +5 -3
  363. package/templates/_executor/lifecycle_test.go +11 -0
  364. package/templates/_executor/link_linux.go +28 -0
  365. package/templates/_executor/long_running_test.go +530 -0
  366. package/templates/_executor/main.go +72 -20
  367. package/templates/_executor/move_linux.go +490 -0
  368. package/templates/_executor/move_linux_test.go +460 -0
  369. package/templates/_executor/move_validation.go +46 -0
  370. package/templates/_executor/move_validation_test.go +187 -0
  371. package/templates/_executor/operations.go +926 -0
  372. package/templates/_executor/platform_linux.go +24 -173
  373. package/templates/_executor/platform_linux_test.go +9 -65
  374. package/templates/_executor/platform_other.go +30 -4
  375. package/templates/_executor/process.go +3 -0
  376. package/templates/_executor/records.go +242 -0
  377. package/templates/_executor/scope_cancel_contract_test.go +91 -0
  378. package/templates/_executor/scope_client.go +37 -0
  379. package/templates/_executor/scope_contract.go +12 -0
  380. package/templates/_executor/selected_scope.go +146 -0
  381. package/templates/_executor/selected_scope_test.go +186 -0
  382. package/templates/_executor/status_disabled_test.go +33 -0
  383. package/templates/_executor/store.go +4 -3
  384. package/templates/_executor/supervise.go +109 -0
  385. package/templates/_executor/supervise_test.go +130 -0
  386. package/templates/_executor/task_race_review_test.go +1 -1
  387. package/templates/_executor/tasks.go +7 -8
  388. package/templates/_executor/tasks_review_test.go +1 -1
  389. package/templates/_executor/tasks_test.go +4 -4
  390. package/templates/_executor/watch.go +8 -0
  391. package/templates/_executor/watch_test.go +23 -2
  392. package/templates/_executor/write_review_linux_test.go +4 -12
  393. package/templates/_shared/AGENTS.md +129 -0
  394. package/templates/binary/dsm-package.yaml +24 -0
  395. package/templates/binary/i18n/messages/de.json +4 -0
  396. package/templates/binary/i18n/messages/en.json +4 -0
  397. package/templates/binary/i18n/messages/es.json +4 -0
  398. package/templates/binary/i18n/messages/fr.json +4 -0
  399. package/templates/binary/i18n/messages/it.json +4 -0
  400. package/templates/binary/i18n/messages/ja.json +4 -0
  401. package/templates/binary/i18n/messages/ko.json +4 -0
  402. package/templates/binary/i18n/messages/zh-CN.json +4 -0
  403. package/templates/binary/i18n/messages/zh-TW.json +4 -0
  404. package/templates/binary/ui/app.js +19 -4
  405. package/templates/native-authorization/AuthorizationGate.vue +91 -0
  406. package/templates/native-authorization/i18n/de.json +37 -0
  407. package/templates/native-authorization/i18n/en.json +37 -0
  408. package/templates/native-authorization/i18n/es.json +37 -0
  409. package/templates/native-authorization/i18n/fr.json +37 -0
  410. package/templates/native-authorization/i18n/index.ts +15 -0
  411. package/templates/native-authorization/i18n/it.json +37 -0
  412. package/templates/native-authorization/i18n/ja.json +37 -0
  413. package/templates/native-authorization/i18n/ko.json +37 -0
  414. package/templates/native-authorization/i18n/zh-CN.json +37 -0
  415. package/templates/native-authorization/i18n/zh-TW.json +37 -0
  416. package/templates/native-go/README.md +43 -10
  417. package/templates/native-go/backend/cmd/app/main.go +27 -7
  418. package/templates/native-go/backend/internal/api/entry_test.go +47 -0
  419. package/templates/native-go/backend/internal/api/permissions_test.go +3 -3
  420. package/templates/native-go/backend/internal/api/registry.go +11 -0
  421. package/templates/native-go/backend/internal/api/sdk-permissions.json +14 -1
  422. package/templates/native-go/backend/internal/api/server.go +18 -5
  423. package/templates/native-go/backend/internal/app/delegate_config.go +57 -0
  424. package/templates/native-go/backend/internal/app/delegate_config_test.go +28 -0
  425. package/templates/native-go/backend/internal/app/portal.go +30 -0
  426. package/templates/native-go/backend/internal/app/portal_test.go +55 -0
  427. package/templates/native-go/backend/internal/app/routes.go +8 -2
  428. package/templates/native-go/backend/internal/background/runner.go +1 -1
  429. package/templates/native-go/backend/internal/background/store.go +16 -14
  430. package/templates/native-go/backend/internal/dsm/admin.go +8 -2
  431. package/templates/native-go/backend/internal/dsm/admin_linux.go +43 -13
  432. package/templates/native-go/backend/internal/dsm/admin_linux_test.go +56 -0
  433. package/templates/native-go/backend/internal/dsm/admin_other.go +4 -1
  434. package/templates/native-go/backend/internal/dsm/apppriv.go +30 -0
  435. package/templates/native-go/backend/internal/dsm/bridge_file.go +75 -0
  436. package/templates/native-go/backend/internal/dsm/bridge_file_test.go +57 -0
  437. package/templates/native-go/backend/internal/dsm/entry.go +177 -0
  438. package/templates/native-go/backend/internal/dsm/entry_test.go +315 -0
  439. package/templates/native-go/backend/internal/dsm/native/admin-armv7.elf +0 -0
  440. package/templates/native-go/backend/internal/dsm/native/apppriv-aarch64.elf +0 -0
  441. package/templates/native-go/backend/internal/dsm/native/apppriv-armv7.elf +0 -0
  442. package/templates/native-go/backend/internal/dsm/native/apppriv-x86_64.elf +0 -0
  443. package/templates/native-go/backend/internal/dsm/native/apppriv_check.c +33 -0
  444. package/templates/native-go/backend/internal/dsm/native/build.sh +13 -4
  445. package/templates/native-go/backend/internal/dsm/native/provenance.json +9 -3
  446. package/templates/native-go/backend/internal/filestation/check_permission.go +71 -0
  447. package/templates/native-go/backend/internal/filestation/check_permission_test.go +77 -0
  448. package/templates/native-go/backend/internal/filestation/sdk_policy_test.go +39 -0
  449. package/templates/native-go/backend/internal/filestation/service.go +1 -1
  450. package/templates/native-go/backend/internal/portal/portal.go +138 -0
  451. package/templates/native-go/backend/internal/portal/portal_test.go +89 -0
  452. package/templates/native-go/dsm-package.yaml +20 -1
  453. package/templates/native-go/i18n/glossary.json +2 -1
  454. package/templates/native-go/i18n/messages/de.json +1 -0
  455. package/templates/native-go/i18n/messages/en.json +1 -0
  456. package/templates/native-go/i18n/messages/es.json +1 -0
  457. package/templates/native-go/i18n/messages/fr.json +1 -0
  458. package/templates/native-go/i18n/messages/it.json +1 -0
  459. package/templates/native-go/i18n/messages/ja.json +1 -0
  460. package/templates/native-go/i18n/messages/ko.json +1 -0
  461. package/templates/native-go/i18n/messages/zh-CN.json +1 -0
  462. package/templates/native-go/i18n/messages/zh-TW.json +1 -0
  463. package/templates/native-go/ui/app.css +10 -0
  464. package/templates/native-go/ui/app.js +11 -1
  465. package/templates/native-go-delegate/DELEGATE.md +126 -5
  466. package/templates/native-go-delegate/backend/cmd/background-worker/main.go +7 -0
  467. package/templates/native-go-delegate/backend/internal/app/delegate.go +11 -1
  468. package/templates/native-go-delegate/backend/internal/app/delegate_routes_test.go +14 -0
  469. package/templates/native-go-delegate/backend/internal/background/delegated_moves.go +270 -0
  470. package/templates/native-go-delegate/backend/internal/delegate/admission_recovery_test.go +410 -0
  471. package/templates/native-go-delegate/backend/internal/delegate/cancel_contract_test.go +95 -0
  472. package/templates/native-go-delegate/backend/internal/delegate/client.go +8 -0
  473. package/templates/native-go-delegate/backend/internal/delegate/destination.go +110 -0
  474. package/templates/native-go-delegate/backend/internal/delegate/destination_state.go +117 -0
  475. package/templates/native-go-delegate/backend/internal/delegate/destination_state_test.go +60 -0
  476. package/templates/native-go-delegate/backend/internal/delegate/destination_test.go +116 -0
  477. package/templates/native-go-delegate/backend/internal/delegate/example.go +81 -0
  478. package/templates/native-go-delegate/backend/internal/delegate/example_test.go +91 -0
  479. package/templates/native-go-delegate/backend/internal/delegate/executor_contract_test.go +511 -0
  480. package/templates/native-go-delegate/backend/internal/delegate/files.go +1 -1
  481. package/templates/native-go-delegate/backend/internal/delegate/long_running_routes.go +92 -0
  482. package/templates/native-go-delegate/backend/internal/delegate/long_running_routes_test.go +99 -0
  483. package/templates/native-go-delegate/backend/internal/delegate/move.go +348 -0
  484. package/templates/native-go-delegate/backend/internal/delegate/move_test.go +351 -0
  485. package/templates/native-go-delegate/backend/internal/delegate/operations_list.go +65 -0
  486. package/templates/native-go-delegate/backend/internal/delegate/operations_list_test.go +114 -0
  487. package/templates/native-go-delegate/backend/internal/delegate/retry_test.go +1 -1
  488. package/templates/native-go-delegate/backend/internal/delegate/service.go +58 -17
  489. package/templates/native-go-delegate/backend/internal/delegate/staging.go +121 -0
  490. package/templates/native-go-delegate/backend/internal/delegate/status_disabled_test.go +51 -0
  491. package/templates/native-go-delegate/backend/internal/delegate/testdata/executor_contract.txt +112 -0
  492. package/templates/native-go-delegate/backend/internal/delegate/transient_contract_test.go +137 -0
  493. package/templates/native-go-delegate/backend/internal/filestation/files.go +1 -31
  494. package/templates/native-go-delegate/frontend/src/CapabilityPanels.vue +30 -3
  495. package/templates/native-go-delegate/frontend/src/delegate/BackgroundSavePanel.vue +113 -0
  496. package/templates/native-go-delegate/frontend/src/delegate/copy.ts +5 -6
  497. package/templates/native-go-delegate/frontend/test/background-save.test.ts +121 -0
  498. package/templates/native-go-delegate/frontend/test/scope-settings.test.ts +49 -0
  499. package/templates/native-go-delegate/test.sh +1 -0
  500. package/templates/native-go-jobs/frontend/src/background/i18n/de.json +2 -2
  501. package/templates/native-go-jobs/frontend/src/background/i18n/en.json +2 -2
  502. package/templates/native-go-jobs/frontend/src/background/i18n/es.json +12 -12
  503. package/templates/native-go-jobs/frontend/src/background/i18n/fr.json +4 -4
  504. package/templates/native-go-jobs/frontend/src/background/i18n/it.json +17 -17
  505. package/templates/native-go-jobs/frontend/src/background/i18n/ja.json +7 -7
  506. package/templates/native-go-jobs/frontend/src/background/i18n/ko.json +15 -15
  507. package/templates/native-go-jobs/frontend/src/background/i18n/zh-CN.json +3 -3
  508. package/templates/native-go-jobs/frontend/src/background/i18n/zh-TW.json +2 -2
  509. package/templates/native-go-jobs/frontend/test/background.test.ts +25 -28
  510. package/templates/native-go-vue/README.md +4 -0
  511. package/templates/native-go-vue/frontend/package-lock.json +4 -4
  512. package/templates/native-go-vue/frontend/package.json +1 -1
  513. package/templates/native-go-vue/frontend/src/App.vue +18 -13
  514. package/templates/native-go-vue/frontend/src/context.ts +31 -0
  515. package/templates/native-go-vue/frontend/src/delegate/ExecutorSettings.vue +52 -8
  516. package/templates/native-go-vue/frontend/src/delegate/catalogs.ts +11 -0
  517. package/templates/native-go-vue/frontend/src/delegate/copy.test.ts +12 -0
  518. package/templates/native-go-vue/frontend/src/delegate/copy.ts +4 -4
  519. package/templates/native-go-vue/frontend/src/delegate/executor-setup.ts +4 -2
  520. package/templates/native-go-vue/frontend/src/delegate/facade.test.ts +100 -0
  521. package/templates/native-go-vue/frontend/src/delegate/facade.ts +85 -0
  522. package/templates/native-go-vue/frontend/src/delegate/i18n/de.json +46 -0
  523. package/templates/native-go-vue/frontend/src/delegate/i18n/en.json +12 -2
  524. package/templates/native-go-vue/frontend/src/delegate/i18n/es.json +46 -0
  525. package/templates/native-go-vue/frontend/src/delegate/i18n/fr.json +46 -0
  526. package/templates/native-go-vue/frontend/src/delegate/i18n/it.json +46 -0
  527. package/templates/native-go-vue/frontend/src/delegate/i18n/ja.json +46 -0
  528. package/templates/native-go-vue/frontend/src/delegate/i18n/ko.json +46 -0
  529. package/templates/native-go-vue/frontend/src/delegate/i18n/save/de.json +42 -0
  530. package/templates/native-go-vue/frontend/src/delegate/i18n/save/en.json +42 -0
  531. package/templates/native-go-vue/frontend/src/delegate/i18n/save/es.json +42 -0
  532. package/templates/native-go-vue/frontend/src/delegate/i18n/save/fr.json +42 -0
  533. package/templates/native-go-vue/frontend/src/delegate/i18n/save/it.json +42 -0
  534. package/templates/native-go-vue/frontend/src/delegate/i18n/save/ja.json +42 -0
  535. package/templates/native-go-vue/frontend/src/delegate/i18n/save/ko.json +42 -0
  536. package/templates/native-go-vue/frontend/src/delegate/i18n/save/zh-CN.json +42 -0
  537. package/templates/native-go-vue/frontend/src/delegate/i18n/save/zh-TW.json +42 -0
  538. package/templates/native-go-vue/frontend/src/delegate/i18n/zh-CN.json +46 -0
  539. package/templates/native-go-vue/frontend/src/delegate/i18n/zh-TW.json +15 -5
  540. package/templates/native-go-vue/frontend/src/delegate/long-running.ts +57 -0
  541. package/templates/native-go-vue/frontend/src/delegate/save-copy.test.ts +12 -0
  542. package/templates/native-go-vue/frontend/src/delegate/save-copy.ts +24 -0
  543. package/templates/native-go-vue/frontend/src/delegate/scoped-picker.test.ts +12 -0
  544. package/templates/native-go-vue/frontend/src/delegate/scoped-picker.ts +21 -0
  545. package/templates/native-go-vue/frontend/src/files/files.test.ts +7 -0
  546. package/templates/native-go-vue/frontend/src/i18n/de.json +20 -0
  547. package/templates/native-go-vue/frontend/src/i18n/en.json +20 -0
  548. package/templates/native-go-vue/frontend/src/i18n/es.json +20 -0
  549. package/templates/native-go-vue/frontend/src/i18n/fr.json +20 -0
  550. package/templates/native-go-vue/frontend/src/i18n/it.json +20 -0
  551. package/templates/native-go-vue/frontend/src/i18n/ja.json +20 -0
  552. package/templates/native-go-vue/frontend/src/i18n/ko.json +20 -0
  553. package/templates/native-go-vue/frontend/src/i18n/zh-CN.json +20 -0
  554. package/templates/native-go-vue/frontend/src/i18n/zh-TW.json +20 -0
  555. package/templates/native-go-vue/frontend/src/i18n.ts +13 -0
  556. package/templates/native-go-vue/frontend/src/identity/LoginPage.vue +16 -14
  557. package/templates/native-go-vue/frontend/src/identity/i18n/de.json +27 -0
  558. package/templates/native-go-vue/frontend/src/identity/i18n/en.json +27 -0
  559. package/templates/native-go-vue/frontend/src/identity/i18n/es.json +27 -0
  560. package/templates/native-go-vue/frontend/src/identity/i18n/fr.json +27 -0
  561. package/templates/native-go-vue/frontend/src/identity/i18n/it.json +27 -0
  562. package/templates/native-go-vue/frontend/src/identity/i18n/ja.json +27 -0
  563. package/templates/native-go-vue/frontend/src/identity/i18n/ko.json +27 -0
  564. package/templates/native-go-vue/frontend/src/identity/i18n/zh-CN.json +27 -0
  565. package/templates/native-go-vue/frontend/src/identity/i18n/zh-TW.json +27 -0
  566. package/templates/native-go-vue/frontend/src/identity/i18n.ts +14 -0
  567. package/templates/native-go-vue/frontend/src/identity/session.test.ts +4 -0
  568. package/templates/native-go-vue/frontend/src/identity/session.ts +9 -8
  569. package/templates/native-go-vue/frontend/src/nas/NasPicker.vue +31 -12
  570. package/templates/native-go-vue/frontend/src/nas/destination.test.ts +48 -0
  571. package/templates/native-go-vue/frontend/src/nas/i18n/files/de.json +1 -1
  572. package/templates/native-go-vue/frontend/src/nas/i18n/files/en.json +1 -1
  573. package/templates/native-go-vue/frontend/src/nas/i18n/files/es.json +10 -10
  574. package/templates/native-go-vue/frontend/src/nas/i18n/files/fr.json +1 -1
  575. package/templates/native-go-vue/frontend/src/nas/i18n/files/it.json +12 -12
  576. package/templates/native-go-vue/frontend/src/nas/i18n/files/ja.json +15 -15
  577. package/templates/native-go-vue/frontend/src/nas/i18n/files/ko.json +12 -12
  578. package/templates/native-go-vue/frontend/src/nas/i18n/files/zh-CN.json +1 -1
  579. package/templates/native-go-vue/frontend/src/nas/i18n/files/zh-TW.json +1 -1
  580. package/templates/native-go-vue/frontend/src/portal/PortalSettings.vue +39 -0
  581. package/templates/native-go-vue/frontend/src/shell/i18n/fr.json +1 -1
  582. package/templates/native-go-vue/frontend/src/shell/i18n/ja.json +1 -1
  583. package/templates/native-go-vue/frontend/test/apps.test.ts +14 -0
  584. package/templates/native-go-vue/frontend/test/portal-settings.test.ts +71 -0
  585. package/templates/native-go-vue/frontend/test/shell-i18n.test.ts +50 -0
@@ -0,0 +1,116 @@
1
+ import { PORTAL_ALIAS_MAX, PORTAL_ALIAS_PATTERN, RESERVED_PORTAL_ALIASES, resolvedContractPortal } from "./alias.js";
2
+ /** Package payload paths DSM's app-portal worker copies to /usr/syno/etc/www/app.d at start. */
3
+ export const PORTAL_JSON_RELPATH = "etc/portal.json";
4
+ export const PORTAL_MUSTACHE_RELPATH = "etc/portal.mustache";
5
+ /** Install wizard key; DSM exports it to preinst/postinst. */
6
+ export const PORTAL_INSTALL_KEY = "portal_alias";
7
+ /** Package-owned install draft, never DSM's saved portal configuration. */
8
+ export const PORTAL_DRAFT = "portal-install.json";
9
+ /** A Compose portal opens the App's `.url` entry of ui/config, keyed by the package id. */
10
+ export function contractPortal(contract) {
11
+ const portal = resolvedContractPortal(contract);
12
+ const packageId = String(contract.package);
13
+ return portal && { appId: packageId, application: String(contract.displayname ?? packageId), alias: portal.alias };
14
+ }
15
+ export function portalResource() {
16
+ return { "web-config": { "app-portal": { "json-relpath": PORTAL_JSON_RELPATH, "mustache-relpath": PORTAL_MUSTACHE_RELPATH } } };
17
+ }
18
+ /** Every field of DSM's own app.d files; the worker refuses a partial file (synopkg start 272). */
19
+ export function renderPortalJson(portal) {
20
+ return `${JSON.stringify({
21
+ application: portal.application,
22
+ id: portal.appId,
23
+ alias: portal.alias,
24
+ fqdn: null,
25
+ alternatePort: {},
26
+ "x-accel": false,
27
+ include: [],
28
+ injectable: false,
29
+ ssl: { spdy: false, hsts: false },
30
+ index: "index.cgi",
31
+ root: "/usr/syno/synoman",
32
+ version: 2,
33
+ })}\n`;
34
+ }
35
+ /** DSM's CGI location: index.cgi launches the app id nginx passes as REWRITE_APP. */
36
+ export function renderPortalMustache() {
37
+ return "{{> /usr/syno/share/nginx/cgi}}\n";
38
+ }
39
+ /** DSM renders wizard descriptions as HTML; "<alias>" would vanish as a tag. */
40
+ function htmlText(text) {
41
+ return text.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;");
42
+ }
43
+ export function renderPortalWizardItems(t, alias) {
44
+ return [
45
+ { desc: htmlText(t("portal.notice")) },
46
+ {
47
+ type: "textfield",
48
+ desc: t("portal.alias"),
49
+ subitems: [{
50
+ key: PORTAL_INSTALL_KEY,
51
+ desc: t("portal.alias"),
52
+ defaultValue: alias,
53
+ validator: { allowBlank: false, regex: { expr: `/${PORTAL_ALIAS_PATTERN.source}/`, errorText: t("portal.aliasInvalid") } },
54
+ }],
55
+ },
56
+ ];
57
+ }
58
+ export function renderPortalWizardStep(t, alias) {
59
+ return { step_title: t("portal.step"), items: renderPortalWizardItems(t, alias) };
60
+ }
61
+ function shellQuote(value) {
62
+ return `'${value.replaceAll("'", "'\\''")}'`;
63
+ }
64
+ const MARKER = "SYNOPKGLAND_PORTAL_ALIAS";
65
+ /**
66
+ * preinst validates the wizard answer, postinst stores it and writes it as the
67
+ * default alias of the package's own portal.json, postupgrade restores it into
68
+ * the new payload. Activation stays an administrator's AppPortal set call.
69
+ */
70
+ export function renderPortalLifecycle(name, portal) {
71
+ if (!["preinst", "postinst", "postupgrade"].includes(name))
72
+ return "";
73
+ const [prefix, suffix] = renderPortalJson({ ...portal, alias: MARKER }).trimEnd().split(MARKER);
74
+ const helpers = `synopkgland_portal_valid() {
75
+ case "$1" in ''|-*|*-|*[!a-z0-9-]*) return 1 ;; esac
76
+ [ "\${#1}" -le ${PORTAL_ALIAS_MAX} ] || return 1
77
+ case ' ${RESERVED_PORTAL_ALIASES.join(" ")} ' in *" $1 "*) return 1 ;; esac
78
+ return 0
79
+ }
80
+ synopkgland_portal_var="\${SYNOPKG_PKGVAR:-/var/packages/\${SYNOPKG_PKGNAME:-}/var}"
81
+ synopkgland_portal_json="\${SYNOPKG_PKGDEST:-/var/packages/\${SYNOPKG_PKGNAME:-}/target}/${PORTAL_JSON_RELPATH}"
82
+ synopkgland_portal_write() {
83
+ synopkgland_portal_tmp="$synopkgland_portal_json.$$"
84
+ printf '%s%s%s\\n' ${shellQuote(prefix)} "$1" ${shellQuote(suffix)} > "$synopkgland_portal_tmp" &&
85
+ chmod 644 "$synopkgland_portal_tmp" && mv -f "$synopkgland_portal_tmp" "$synopkgland_portal_json"
86
+ }
87
+ `;
88
+ const answer = `synopkgland_portal_alias="\${${PORTAL_INSTALL_KEY}:-${portal.alias}}"
89
+ if ! synopkgland_portal_valid "$synopkgland_portal_alias"; then
90
+ echo "Application Portal alias: use 1-64 lowercase letters, digits or -, not a DSM path." >&2
91
+ exit 1
92
+ fi
93
+ `;
94
+ if (name === "preinst") {
95
+ return `${helpers}if [ "\${SYNOPKG_PKG_STATUS:-}" != UPGRADE ]; then
96
+ ${answer}fi
97
+ `;
98
+ }
99
+ if (name === "postinst") {
100
+ return `${helpers}if [ "\${SYNOPKG_PKG_STATUS:-}" != UPGRADE ]; then
101
+ ${answer} (umask 077 && mkdir -p "$synopkgland_portal_var" &&
102
+ printf '{"schema":1,"alias":"%s"}\\n' "$synopkgland_portal_alias" > "$synopkgland_portal_var/.${PORTAL_DRAFT}.$$" &&
103
+ mv -f "$synopkgland_portal_var/.${PORTAL_DRAFT}.$$" "$synopkgland_portal_var/${PORTAL_DRAFT}") || exit 1
104
+ synopkgland_portal_write "$synopkgland_portal_alias" || exit 1
105
+ fi
106
+ `;
107
+ }
108
+ return `${helpers}synopkgland_portal_draft="$synopkgland_portal_var/${PORTAL_DRAFT}"
109
+ if [ -f "$synopkgland_portal_draft" ] && [ ! -L "$synopkgland_portal_draft" ]; then
110
+ synopkgland_portal_alias="$(head -c 128 "$synopkgland_portal_draft" | sed -n 's/^{"schema":1,"alias":"\\([a-z0-9-]*\\)"}$/\\1/p')"
111
+ if synopkgland_portal_valid "$synopkgland_portal_alias"; then
112
+ synopkgland_portal_write "$synopkgland_portal_alias" || exit 1
113
+ fi
114
+ fi
115
+ `;
116
+ }
@@ -0,0 +1,79 @@
1
+ /** Frozen schema-2 / v1 extension. Definitions alone do not enable capabilities. */
2
+ export declare const MOVE_FROM_STAGING: "files.moveFromStaging";
3
+ export declare const DELEGATE_EXTENSION_OPERATIONS: readonly ["files.moveFromStaging", "files.operationStatus", "files.cancelOperation", "delegate.disable", "delegate.enable", "delegate.audit"];
4
+ export declare const DELEGATE_OPERATION_STATES: readonly ["queued", "copying", "committed", "canceled", "interrupted", "uncertain"];
5
+ export type DelegateOperationState = typeof DELEGATE_OPERATION_STATES[number];
6
+ export interface LongRunningDelegateCapability {
7
+ scope: string;
8
+ operations: [typeof MOVE_FROM_STAGING];
9
+ destinations: string[];
10
+ lifetime: 'until-revoked';
11
+ }
12
+ export interface SelectDestination {
13
+ key: string;
14
+ path: string;
15
+ }
16
+ /** Protected executor selection, not the package-owned installer draft. */
17
+ export interface ScopeSelection {
18
+ scope: string;
19
+ root: string;
20
+ generation: number;
21
+ }
22
+ export interface DestinationState {
23
+ key: string;
24
+ selection: ScopeSelection | null;
25
+ destination: DestinationGrant | null;
26
+ status: 'active' | 'missing' | 'reselection-required' | 'needs-scope-confirmation' | 'disabled';
27
+ }
28
+ export interface DestinationGrant {
29
+ id: string;
30
+ key: string;
31
+ path: string;
32
+ username: string;
33
+ lifetime: 'until-revoked';
34
+ }
35
+ /** Owner/UID/destination are resolved from the protected grant, never supplied here. */
36
+ export interface MoveFromStaging {
37
+ delegation_id: string;
38
+ job_id: string;
39
+ staging: string;
40
+ name: string;
41
+ size: number;
42
+ }
43
+ export interface OperationRef {
44
+ delegation_id: string;
45
+ job_id: string;
46
+ }
47
+ export interface OperationResult {
48
+ job_id: string;
49
+ state: DelegateOperationState;
50
+ /** Only a committed executor receipt may supply a final destination path. */
51
+ receipt?: {
52
+ path: string;
53
+ size: number;
54
+ };
55
+ code?: string;
56
+ reason?: string;
57
+ }
58
+ export declare const DELEGATE_ROUTES: {
59
+ readonly 'delegate.example.export': "authenticated";
60
+ readonly 'delegate.destination': "authenticated";
61
+ readonly 'delegate.select_destination': "authenticated";
62
+ readonly 'delegate.move_from_staging': "authenticated";
63
+ readonly 'delegate.operations.get': "authenticated";
64
+ readonly 'delegate.operations.list': "authenticated";
65
+ readonly 'delegate.operations.cancel': "authenticated";
66
+ readonly 'delegate.disable': "administrators";
67
+ readonly 'delegate.enable': "administrators";
68
+ readonly 'delegate.audit': "authenticated";
69
+ };
70
+ /** Shared, explicit limits; legacy TTL/job limits remain separate. */
71
+ export declare const DELEGATE_LIMITS: {
72
+ readonly maxFileBytes: number;
73
+ readonly maxQueued: 16;
74
+ readonly maxActive: 1;
75
+ readonly maxCopySeconds: 86400;
76
+ readonly maxOperationBytes: number;
77
+ readonly maxAuditBytes: number;
78
+ readonly revalidateSeconds: 5;
79
+ };
@@ -0,0 +1,27 @@
1
+ /** Frozen schema-2 / v1 extension. Definitions alone do not enable capabilities. */
2
+ export const MOVE_FROM_STAGING = 'files.moveFromStaging';
3
+ export const DELEGATE_EXTENSION_OPERATIONS = [
4
+ MOVE_FROM_STAGING, 'files.operationStatus', 'files.cancelOperation',
5
+ 'delegate.disable', 'delegate.enable', 'delegate.audit',
6
+ ];
7
+ export const DELEGATE_OPERATION_STATES = [
8
+ 'queued', 'copying', 'committed', 'canceled', 'interrupted', 'uncertain',
9
+ ];
10
+ export const DELEGATE_ROUTES = {
11
+ 'delegate.example.export': 'authenticated',
12
+ 'delegate.destination': 'authenticated',
13
+ 'delegate.select_destination': 'authenticated',
14
+ 'delegate.move_from_staging': 'authenticated',
15
+ 'delegate.operations.get': 'authenticated',
16
+ 'delegate.operations.list': 'authenticated',
17
+ 'delegate.operations.cancel': 'authenticated',
18
+ 'delegate.disable': 'administrators',
19
+ 'delegate.enable': 'administrators',
20
+ 'delegate.audit': 'authenticated',
21
+ };
22
+ /** Shared, explicit limits; legacy TTL/job limits remain separate. */
23
+ export const DELEGATE_LIMITS = {
24
+ maxFileBytes: 16 * 1024 ** 3, maxQueued: 16, maxActive: 1,
25
+ maxCopySeconds: 86400, maxOperationBytes: 10 * 1024 ** 2,
26
+ maxAuditBytes: 50 * 1024 ** 2, revalidateSeconds: 5,
27
+ };
@@ -1,4 +1,4 @@
1
- import type { NativeArchFamily } from '../build-spk/dsm-arch.js';
1
+ import { type NativeArchFamily } from '../build-spk/dsm-arch.js';
2
2
  import type { CommandRunner } from '../standalone/build.js';
3
3
  import type { StandaloneManifest } from '../standalone/manifest.js';
4
4
  import { type ExecutorPolicy } from './executor-policy.js';
@@ -1,9 +1,10 @@
1
1
  import { existsSync, mkdirSync, readFileSync } from 'node:fs';
2
2
  import { join } from 'node:path';
3
3
  import { templatesRoot } from '../template-paths.js';
4
+ import { goBuildEnv } from '../build-spk/dsm-arch.js';
4
5
  import { StandaloneBuildError } from '../standalone/spk.js';
5
6
  import { checkCgiBinary } from '../standalone/binary.js';
6
- import { executorPolicy, sha256 } from './executor-policy.js';
7
+ import { executorPolicy, executorPolicyText, sha256 } from './executor-policy.js';
7
8
  import { executorTasks } from './executor-tasks.js';
8
9
  export function buildExecutor(m, arch, work, run) {
9
10
  if (!m.capabilities.delegate && !m.capabilities.rootOps)
@@ -14,20 +15,20 @@ export function buildExecutor(m, arch, work, run) {
14
15
  if (!existsSync(join(source, 'go.mod')))
15
16
  throw new StandaloneBuildError('EXECUTOR_BUILD_FAILED', 'Executor sources are missing.');
16
17
  const binary = join(dir, 'executor');
17
- const result = run('go', ['build', '-trimpath', '-buildvcs=false', '-ldflags', '-s -w -buildid=', '-o', binary, '.'], { cwd: source, env: { ...process.env, CGO_ENABLED: '0', GOOS: 'linux', GOARCH: arch === 'x86_64' ? 'amd64' : 'arm64' } });
18
+ const result = run('go', ['build', '-trimpath', '-buildvcs=false', '-ldflags', '-s -w -buildid=', '-o', binary, '.'], { cwd: source, env: { ...process.env, ...goBuildEnv(arch) } });
18
19
  if (result.missing || result.status !== 0)
19
20
  throw new StandaloneBuildError('EXECUTOR_BUILD_FAILED', `Executor build failed (${arch}). ${result.stderr}`);
20
21
  const problem = checkCgiBinary(work, binary, arch);
21
22
  if (problem)
22
23
  throw new StandaloneBuildError(problem.code, problem.message);
23
24
  const lifecycleBinary = join(dir, 'lifecycle');
24
- const lifecycleResult = run('go', ['build', '-trimpath', '-buildvcs=false', '-ldflags', '-s -w -buildid=', '-o', lifecycleBinary, './cmd/lifecycle'], { cwd: source, env: { ...process.env, CGO_ENABLED: '0', GOOS: 'linux', GOARCH: arch === 'x86_64' ? 'amd64' : 'arm64' } });
25
+ const lifecycleResult = run('go', ['build', '-trimpath', '-buildvcs=false', '-ldflags', '-s -w -buildid=', '-o', lifecycleBinary, './cmd/lifecycle'], { cwd: source, env: { ...process.env, ...goBuildEnv(arch) } });
25
26
  if (lifecycleResult.missing || lifecycleResult.status !== 0)
26
27
  throw new StandaloneBuildError('EXECUTOR_BUILD_FAILED', `Lifecycle helper build failed (${arch}). ${lifecycleResult.stderr}`);
27
28
  const lifecycleProblem = checkCgiBinary(work, lifecycleBinary, arch);
28
29
  if (lifecycleProblem)
29
30
  throw new StandaloneBuildError(lifecycleProblem.code, lifecycleProblem.message);
30
- const policy = executorPolicy(m), policyText = JSON.stringify(policy) + '\n';
31
+ const policy = executorPolicy(m), policyText = executorPolicyText(policy);
31
32
  const pins = { package: m.package, executorDigest: sha256(readFileSync(binary)), policyDigest: sha256(policyText) };
32
33
  return { ...pins, binary, lifecycleBinary, policy, policyText, tasks: executorTasks(pins), implementation: 'executor' };
33
34
  }
@@ -1,5 +1,8 @@
1
1
  import { type ExecutorPolicy } from './executor-policy.js';
2
2
  import { type ExecutorTask } from './executor-tasks.js';
3
+ /** INFO key that anchors the executor digest; Package Center writes INFO as root. */
4
+ export declare const EXECUTOR_ANCHOR = "synopkgland_executor_sha256";
5
+ export declare function inspectExecutorAnchor(info: string, executorDigest: string): string[];
3
6
  export interface ExecutorInspection {
4
7
  executorDigest: string;
5
8
  policyDigest: string;
@@ -1,19 +1,25 @@
1
1
  import { existsSync, lstatSync, readFileSync } from 'node:fs';
2
2
  import { join } from 'node:path';
3
- import { FILE_OPERATIONS, sha256 } from './executor-policy.js';
3
+ import { validExecutorPolicy, sha256 } from './executor-policy.js';
4
+ import { parseDocument } from 'yaml';
4
5
  import { executorTasks } from './executor-tasks.js';
5
6
  import { familyOfArchToken } from '../build-spk/dsm-arch.js';
6
7
  import { ELF_MACHINE } from '../standalone/binary.js';
8
+ /** INFO key that anchors the executor digest; Package Center writes INFO as root. */
9
+ export const EXECUTOR_ANCHOR = 'synopkgland_executor_sha256';
10
+ export function inspectExecutorAnchor(info, executorDigest) {
11
+ const anchors = info.split('\n').filter(line => line.startsWith(`${EXECUTOR_ANCHOR}=`));
12
+ return anchors.length === 1 && anchors[0] === `${EXECUTOR_ANCHOR}="${executorDigest}"` ? [] : ['Executor INFO anchor must name the shipped executor digest exactly once.'];
13
+ }
7
14
  export function inspectExecutor(dir, pkg, arch) {
8
15
  try {
9
16
  const binary = readFileSync(join(dir, 'executor/executor'));
10
- if (!binary.subarray(0, 4).equals(Buffer.from([127, 69, 76, 70])) || binary.readUInt16LE(18) !== (familyOfArchToken(arch.split(' ')[0]) === 'aarch64' ? ELF_MACHINE.aarch64 : ELF_MACHINE.x86_64))
17
+ if (!binary.subarray(0, 4).equals(Buffer.from([127, 69, 76, 70])) || binary.readUInt16LE(18) !== ELF_MACHINE[familyOfArchToken(arch.split(' ')[0]) ?? 'x86_64'])
11
18
  throw new Error('Executor ELF architecture mismatch.');
12
19
  const text = readFileSync(join(dir, 'executor/policy.json'), 'utf8');
13
20
  const plan = JSON.parse(readFileSync(join(dir, 'executor/setup.json'), 'utf8'));
14
21
  const policy = JSON.parse(text);
15
- const allowed = ['status', 'delegate.create', 'delegate.list', 'delegate.revoke', 'root.run', ...FILE_OPERATIONS];
16
- if (policy.v !== 1 || policy.app !== `SYNO.SDS.${pkg}.Instance` || policy.package !== pkg || !Array.isArray(policy.operations) || !policy.operations.includes('status') || policy.operations.some(op => !allowed.includes(op)) || new Set(policy.operations).size !== policy.operations.length || !policy.root || typeof policy.root !== 'object' || Array.isArray(policy.root))
22
+ if (parseDocument(text, { uniqueKeys: true }).errors.length || !validExecutorPolicy(policy, pkg))
17
23
  throw new Error('Invalid executor policy.');
18
24
  for (const [name, digest] of Object.entries(policy.root)) {
19
25
  const path = `root/${name}.sh`;
@@ -21,7 +27,7 @@ export function inspectExecutor(dir, pkg, arch) {
21
27
  throw new Error('Root script digest mismatch.');
22
28
  }
23
29
  const executorDigest = sha256(binary), policyDigest = sha256(text);
24
- const tasks = executorTasks({ package: pkg, executorDigest, policyDigest });
30
+ const tasks = executorTasks({ package: pkg, policyDigest });
25
31
  if (plan.package !== pkg || plan.executorDigest !== executorDigest || plan.policyDigest !== policyDigest || JSON.stringify(plan.policy) !== JSON.stringify(policy) || JSON.stringify(plan.tasks) !== JSON.stringify(tasks) || plan.implementation !== 'executor')
26
32
  throw new Error('Executor pins or task binding mismatch.');
27
33
  return { executor: { executorDigest, policyDigest, policy, implementation: plan.implementation, tasks: tasks.map(task => ({ ...task, state: 'unknown' })) }, findings: [] };
@@ -1,25 +1,37 @@
1
1
  import type { RootOp } from '../standalone/root-ops.js';
2
+ import type { EntryMode } from '../standalone/manifest.js';
2
3
  export declare const FILE_OPERATIONS: readonly ["files.list", "files.info", "files.createFolder", "files.checkPermission", "files.write"];
3
4
  export interface DelegateCapability {
4
5
  operations: string[];
5
6
  scope: string;
7
+ destinations?: string[];
8
+ lifetime?: 'until-revoked';
6
9
  }
7
10
  export declare function validDelegate(value: unknown): value is DelegateCapability;
8
11
  export declare const sha256: (data: string | Buffer) => string;
9
12
  export interface ExecutorPolicy {
10
13
  v: 1;
11
14
  app: string;
15
+ /** Present only with a manifest `entry`; the executor then applies the same App Privileges rule offline. */
16
+ entry?: EntryMode;
12
17
  scope?: string;
18
+ destinations?: string[];
19
+ lifetime?: 'until-revoked';
13
20
  package: string;
14
21
  operations: string[];
15
22
  root: Record<string, string>;
16
23
  }
24
+ /** The exact policy file bytes whose sha256 is the policy pin. */
25
+ export declare const executorPolicyText: (policy: ExecutorPolicy) => string;
17
26
  /** Version-independent, sorted policy. Only §2.4 operation IDs are admitted. */
18
27
  export declare function executorPolicy(m: {
19
28
  package: string;
29
+ entry?: EntryMode;
20
30
  capabilities: {
21
31
  delegate?: DelegateCapability;
22
32
  rootOps?: true;
23
33
  };
24
34
  rootOps?: RootOp[];
25
35
  }): ExecutorPolicy;
36
+ /** Inventory rejects malformed extensions even if a caller recomputes every pin. */
37
+ export declare function validExecutorPolicy(value: unknown, pkg: string): value is ExecutorPolicy;
@@ -1,24 +1,64 @@
1
1
  import { createHash } from 'node:crypto';
2
+ import { MOVE_FROM_STAGING, DELEGATE_EXTENSION_OPERATIONS } from './delegate-contract.js';
2
3
  export const FILE_OPERATIONS = ['files.list', 'files.info', 'files.createFolder', 'files.checkPermission', 'files.write'];
3
4
  export function validDelegate(value) {
4
5
  if (!value || typeof value !== 'object' || Array.isArray(value))
5
6
  return false;
6
7
  const d = value;
7
- return Object.keys(d).every(key => ['operations', 'scope'].includes(key)) && Array.isArray(d.operations) && d.operations.length > 0 && new Set(d.operations).size === d.operations.length && d.operations.every(op => FILE_OPERATIONS.includes(op)) && typeof d.scope === 'string' && (d.scope === 'home' || /^shared:[^/\\\x00-\x1f:]+$/.test(d.scope) && d.scope.slice(7).trim() === d.scope.slice(7) && !['.', '..'].includes(d.scope.slice(7)));
8
+ if (!Object.keys(d).every(key => ['operations', 'scope', 'destinations', 'lifetime'].includes(key)) || !Array.isArray(d.operations) || d.operations.length === 0 || new Set(d.operations).size !== d.operations.length || typeof d.scope !== 'string' || !(d.scope === 'home' || d.scope === 'install-selected' && d.lifetime === 'until-revoked' || /^shared:[^/\\\x00-\x1f:]+$/.test(d.scope) && d.scope.slice(7).trim() === d.scope.slice(7) && !['.', '..'].includes(d.scope.slice(7))))
9
+ return false;
10
+ if ('destinations' in d || 'lifetime' in d || d.operations.includes(MOVE_FROM_STAGING)) {
11
+ return d.lifetime === 'until-revoked' && d.operations.length === 1 && d.operations[0] === MOVE_FROM_STAGING
12
+ && Array.isArray(d.destinations) && d.destinations.length > 0 && d.destinations.length <= 64 && new Set(d.destinations).size === d.destinations.length
13
+ && d.destinations.every(key => typeof key === 'string' && /^[a-z][a-z0-9_-]{0,63}$/.exec(key)?.[0] === key);
14
+ }
15
+ return d.operations.every(op => FILE_OPERATIONS.includes(op));
8
16
  }
9
17
  export const sha256 = (data) => createHash('sha256').update(data).digest('hex');
18
+ /** The exact policy file bytes whose sha256 is the policy pin. */
19
+ export const executorPolicyText = (policy) => JSON.stringify(policy) + '\n';
10
20
  /** Version-independent, sorted policy. Only §2.4 operation IDs are admitted. */
11
21
  export function executorPolicy(m) {
12
22
  if (m.capabilities.delegate && !validDelegate(m.capabilities.delegate))
13
23
  throw new Error('[EXECUTOR_POLICY] Invalid delegate operations or scope.');
14
24
  const operations = ['status'];
15
- if (m.capabilities.delegate)
25
+ if (m.capabilities.delegate) {
16
26
  operations.push('delegate.create', 'delegate.list', 'delegate.revoke', ...m.capabilities.delegate.operations);
27
+ if (m.capabilities.delegate.lifetime === 'until-revoked')
28
+ operations.push(...DELEGATE_EXTENSION_OPERATIONS);
29
+ }
17
30
  const root = {};
18
31
  if (m.capabilities.rootOps) {
19
32
  operations.push('root.run');
20
33
  for (const op of [...m.rootOps ?? []].sort((a, b) => a.name.localeCompare(b.name, 'en')))
21
34
  root[op.name] = sha256(op.body);
22
35
  }
23
- return { v: 1, package: m.package, app: `SYNO.SDS.${m.package}.Instance`, ...(m.capabilities.delegate ? { scope: m.capabilities.delegate.scope } : {}), operations: [...new Set(operations)].sort(), root };
36
+ const d = m.capabilities.delegate;
37
+ return { v: 1, package: m.package, app: `SYNO.SDS.${m.package}.Instance`, ...(m.entry ? { entry: m.entry } : {}), ...(d ? { scope: d.scope } : {}),
38
+ ...(d?.lifetime ? { destinations: [...d.destinations].sort(), lifetime: d.lifetime } : {}),
39
+ operations: [...new Set(operations)].sort(), root };
40
+ }
41
+ /** Inventory rejects malformed extensions even if a caller recomputes every pin. */
42
+ export function validExecutorPolicy(value, pkg) {
43
+ if (!value || typeof value !== 'object' || Array.isArray(value))
44
+ return false;
45
+ const p = value;
46
+ if (Object.keys(p).some(key => !['v', 'package', 'app', 'entry', 'scope', 'destinations', 'lifetime', 'operations', 'root'].includes(key))
47
+ || p.v !== 1 || p.package !== pkg || p.app !== `SYNO.SDS.${pkg}.Instance`
48
+ || 'entry' in p && p.entry !== 'admin-only' && p.entry !== 'app-privilege'
49
+ || !Array.isArray(p.operations) || !p.operations.includes('status') || new Set(p.operations).size !== p.operations.length
50
+ || !p.root || typeof p.root !== 'object' || Array.isArray(p.root))
51
+ return false;
52
+ const base = ['status', 'root.run', 'delegate.create', 'delegate.list', 'delegate.revoke'];
53
+ if (p.operations.some(op => ![...base, ...FILE_OPERATIONS, ...DELEGATE_EXTENSION_OPERATIONS].includes(op)))
54
+ return false;
55
+ const delegated = p.operations.some(op => op.startsWith('files.') || op.startsWith('delegate.'));
56
+ if (!delegated)
57
+ return p.scope === undefined && p.lifetime === undefined && p.destinations === undefined;
58
+ const capability = { scope: p.scope, operations: p.operations.filter(op => FILE_OPERATIONS.includes(op) || op === MOVE_FROM_STAGING),
59
+ ...('lifetime' in p ? { lifetime: p.lifetime } : {}), ...('destinations' in p ? { destinations: p.destinations } : {}) };
60
+ if (!validDelegate(capability))
61
+ return false;
62
+ const expected = executorPolicy({ package: pkg, capabilities: { delegate: capability, ...(p.operations.includes('root.run') ? { rootOps: true } : {}) } }).operations;
63
+ return expected.length === p.operations.length && expected.every(op => p.operations.includes(op));
24
64
  }
@@ -0,0 +1,4 @@
1
+ import type { ExecutorPins, ExecutorTask } from './executor-tasks.js';
2
+ /** Self-contained: the same function is embedded in the DSM window. No draft
3
+ * files are read by this task; the confirmed scope is a literal pinned argument. */
4
+ export declare function executorScopeTask(pins: ExecutorPins, scope: string, actor: string, expectedGeneration: number): ExecutorTask;
@@ -0,0 +1,21 @@
1
+ /** Self-contained: the same function is embedded in the DSM window. No draft
2
+ * files are read by this task; the confirmed scope is a literal pinned argument. */
3
+ export function executorScopeTask(pins, scope, actor, expectedGeneration) {
4
+ const share = scope?.startsWith('shared:') ? scope.slice(7) : '';
5
+ if (!(/^[A-Za-z][A-Za-z0-9]{1,63}$/.exec(pins.package)?.[0] === pins.package)
6
+ || ![pins.executorDigest, pins.policyDigest].every(v => typeof v === 'string' && v.length === 64 && /^[a-f0-9]{64}$/.test(v))
7
+ || !(scope === 'home' || share && share.trim() === share && !/[\/\\:\x00-\x1f\x7f%]/.test(share) && !share.includes('..') && !['.', 'home', 'homes'].includes(share.toLowerCase()))
8
+ || !Number.isSafeInteger(expectedGeneration) || expectedGeneration < 0
9
+ || !actor || /[\x00-\x1f\x7f%]/.test(actor))
10
+ throw new Error('executor_scope_invalid');
11
+ const header = '#!/bin/sh\n# synopkgland executor selection\n';
12
+ const script = header + `set -eu
13
+ export PATH=/usr/bin:/bin
14
+ d=/usr/local/etc/synopkgland/${pins.package}/runtime
15
+ { echo ${pins.executorDigest}" $d/executor"; echo ${pins.policyDigest}" $d/policy.json"; }|sha256sum -c - >/dev/null
16
+ exec "$d/executor" select-scope --package '${pins.package}' --scope '${scope.replaceAll("'", "'\\''")}' --actor '${actor.replaceAll("'", "'\\''")}' --expected-generation ${expectedGeneration}
17
+ `;
18
+ if (new TextEncoder().encode(script).length > 6000)
19
+ throw new Error('executor_scope_invalid');
20
+ return { kind: 'control', name: pins.package + ' executor selection', header, script };
21
+ }
@@ -11,6 +11,7 @@ export interface ExecutorTask {
11
11
  script: string;
12
12
  }
13
13
  export declare function validateTaskScript(script: string, limit: number): string;
14
- export declare function executorTasks(pins: ExecutorPins): ExecutorTask[];
14
+ /** Tasks pin only the approved policy; the SPK's root-owned INFO anchors executor bytes. */
15
+ export declare function executorTasks(pins: Pick<ExecutorPins, 'package' | 'policyDigest'>): ExecutorTask[];
15
16
  export declare function bootDefinition(task: ExecutorTask): Record<string, unknown>;
16
17
  export declare function controlDefinition(task: ExecutorTask): Record<string, unknown>;
@@ -8,15 +8,15 @@ export function validateTaskScript(script, limit) {
8
8
  throw new Error('[EXECUTOR_TASK_LENGTH] Task script exceeds DSM byte limit.');
9
9
  return script;
10
10
  }
11
+ /** Tasks pin only the approved policy; the SPK's root-owned INFO anchors executor bytes. */
11
12
  export function executorTasks(pins) {
12
13
  if (!/^[A-Za-z][A-Za-z0-9]{1,63}$/.test(pins.package))
13
14
  throw new Error('[EXECUTOR_PACKAGE] Invalid package identity.');
14
- for (const digest of [pins.executorDigest, pins.policyDigest])
15
- if (!/^[a-f0-9]{64}$/.test(digest))
16
- throw new Error('[EXECUTOR_DIGEST] Invalid sha256 pin.');
15
+ if (!/^[a-f0-9]{64}$/.test(pins.policyDigest))
16
+ throw new Error('[EXECUTOR_DIGEST] Invalid sha256 pin.');
17
17
  return ['boot', 'control'].map(kind => {
18
18
  const script = readFileSync(join(templatesRoot(), '_executor', 'internal', 'taskscript', `${kind}.sh`), 'utf8')
19
- .replaceAll('@PACKAGE@', pins.package).replaceAll('@EXECUTOR_DIGEST@', pins.executorDigest).replaceAll('@POLICY_DIGEST@', pins.policyDigest);
19
+ .replaceAll('@PACKAGE@', pins.package).replaceAll('@POLICY_DIGEST@', pins.policyDigest);
20
20
  return { kind, name: `${pins.package} executor ${kind}`, header: `#!/bin/sh\n# synopkgland executor ${kind}\n`, script: validateTaskScript(script, kind === 'boot' ? 1024 : 6000) };
21
21
  });
22
22
  }
package/dist/skill.js CHANGED
@@ -7,6 +7,7 @@ const SKILL_CATALOG = [
7
7
  { id: "wrap", file: "reference/wrap.md", when: "已有 Compose 或要補契約" },
8
8
  { id: "identity", file: "reference/identity.md", when: "需要 DSM 登入者" },
9
9
  { id: "files", file: "reference/files.md", when: "需要 NAS 檔案" },
10
+ { id: "portal", file: "reference/portal.md", when: "需要 DSM Application Portal 別名連結" },
10
11
  { id: "ui-vue", file: "reference/ui-vue.md", when: "編寫/檢查 Vue UI" },
11
12
  { id: "ui-json", file: "reference/ui-json.md", when: "產生/檢查 JSON UI" },
12
13
  { id: "deploy", file: "reference/deploy.md", when: "要放到 NAS" },
@@ -14,5 +14,15 @@ export declare function readApiPermissions(backendDir: string, capabilities?: Ca
14
14
  digest: string;
15
15
  sdkDigest: string;
16
16
  };
17
+ /**
18
+ * `entry: admin-only` makes administrators the floor of every route. App
19
+ * routes that would admit anyone else are returned as conflicts; SDK routes a
20
+ * capability added are raised, except the public login ceremony of
21
+ * `identity: standalone`, which admits no one by itself.
22
+ */
23
+ export declare function floorAtAdministrators(policy: ApiPermissions): {
24
+ policy: ApiPermissions;
25
+ widened: string[];
26
+ };
17
27
  /** Launcher access is derived from the same API policy; no second setting. */
18
28
  export declare function permitsOrdinaryUsers(policy?: ApiPermissions): boolean;
@@ -4,6 +4,7 @@ import { join } from "node:path";
4
4
  import { templatesRoot } from "../template-paths.js";
5
5
  import { parseDocument } from "yaml";
6
6
  export const API_PERMISSIONS_FILE = "internal/api/permissions.json";
7
+ const SDK_PERMISSIONS_TEMPLATE = "native-go/backend/internal/api/sdk-permissions.json";
7
8
  export const MAX_PERMISSIONS_BYTES = 65536;
8
9
  export const MAX_PERMISSION_METHODS = 1024;
9
10
  export function parseApiPermissions(raw) {
@@ -37,7 +38,7 @@ export function readApiPermissions(backendDir, capabilities = {}) {
37
38
  throw new Error("permissions must be a regular bounded JSON file");
38
39
  const raw = readFileSync(file);
39
40
  const policy = parseApiPermissions(raw);
40
- const sdkRaw = readFileSync(join(templatesRoot(), 'native-go/backend/internal/api/sdk-permissions.json'));
41
+ const sdkRaw = readFileSync(join(templatesRoot(), SDK_PERMISSIONS_TEMPLATE));
41
42
  const sdkFile = join(backendDir, 'internal/api/sdk-permissions.json');
42
43
  if (existsSync(sdkFile)) {
43
44
  const sdkStat = lstatSync(sdkFile);
@@ -45,7 +46,7 @@ export function readApiPermissions(backendDir, capabilities = {}) {
45
46
  throw new Error('SDK route policy differs from the CLI policy; restore the SDK-owned file');
46
47
  }
47
48
  }
48
- const sdk = JSON.parse(sdkRaw.toString('utf8'));
49
+ const sdk = readSdkPermissions();
49
50
  for (const methods of Object.values(sdk))
50
51
  for (const method of Object.keys(methods)) {
51
52
  if (Object.hasOwn(policy.methods, method))
@@ -57,6 +58,22 @@ export function readApiPermissions(backendDir, capabilities = {}) {
57
58
  }
58
59
  return { policy, sdkDigest: createHash("sha256").update(sdkRaw).digest("hex"), digest: createHash("sha256").update(raw).digest("hex") };
59
60
  }
61
+ /**
62
+ * `entry: admin-only` makes administrators the floor of every route. App
63
+ * routes that would admit anyone else are returned as conflicts; SDK routes a
64
+ * capability added are raised, except the public login ceremony of
65
+ * `identity: standalone`, which admits no one by itself.
66
+ */
67
+ export function floorAtAdministrators(policy) {
68
+ const sdk = new Set(Object.values(readSdkPermissions()).flatMap(methods => Object.keys(methods)));
69
+ const methods = Object.fromEntries(Object.entries(policy.methods)
70
+ .map(([method, permission]) => [method, sdk.has(method) && permission === "public" ? permission : "administrators"]));
71
+ const widened = Object.entries(policy.methods).filter(([method, permission]) => !sdk.has(method) && permission !== "administrators").map(([method]) => method);
72
+ return { policy: { ...policy, methods }, widened };
73
+ }
74
+ function readSdkPermissions() {
75
+ return JSON.parse(readFileSync(join(templatesRoot(), SDK_PERMISSIONS_TEMPLATE), 'utf8'));
76
+ }
60
77
  /** Launcher access is derived from the same API policy; no second setting. */
61
78
  export function permitsOrdinaryUsers(policy) {
62
79
  return policy !== undefined && Object.values(policy.methods).some(permission => permission !== "administrators");
@@ -17,5 +17,21 @@ export declare function readBackground(value: unknown, fail: (error: StandaloneE
17
17
  /** `conf/resource`: only the official user-unit resource. */
18
18
  export declare function renderBackgroundResource(): string;
19
19
  export declare function renderBackgroundUnit(pkg: string): string;
20
+ /** Shell function that stops the worker in lifecycle hooks; call it as `stop_background_worker`. */
21
+ export declare function renderBackgroundWorkerStop(pkg: string): string;
22
+ /**
23
+ * Shell function for `prestart`, which DSM runs before its systemd-user-unit
24
+ * acquire: call it as `recover_user_manager`.
25
+ *
26
+ * On a restart under load, pkgctl's `Requires=user@UID` can start the package
27
+ * user's new manager in the old runtime dir just before logind removes it.
28
+ * That manager stays active without its private socket, so every later
29
+ * synosystemctl call fails with "Failed to get D-Bus connection" until it
30
+ * exits. Stop only such a manager, as the package user; the acquire that
31
+ * follows then starts a fresh one.
32
+ */
33
+ export declare function renderUserManagerRecovery(): string;
34
+ /** Shell function that starts the worker and names the failed step; call it as `start_background_worker`. */
35
+ export declare function renderBackgroundWorkerStart(pkg: string): string;
20
36
  /** Package Center's start/stop/status map straight onto the user unit. */
21
37
  export declare function renderBackgroundControl(pkg: string): string;