@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
@@ -2,11 +2,29 @@ import { spawnSync } from "node:child_process";
2
2
  import { mkdtempSync, readFileSync, rmSync, writeFileSync } from "node:fs";
3
3
  import { tmpdir } from "node:os";
4
4
  import { join } from "node:path";
5
- function isHostUnreachable(error) {
6
- const code = error.cause?.code
5
+ function errorCode(error) {
6
+ return error.cause?.code
7
7
  ?? error.code;
8
+ }
9
+ function isHostUnreachable(error) {
10
+ const code = errorCode(error);
8
11
  return code === "EHOSTUNREACH" || code === "ENETUNREACH";
9
12
  }
13
+ const UNTRUSTED_CERTIFICATE = new Set([
14
+ "DEPTH_ZERO_SELF_SIGNED_CERT",
15
+ "SELF_SIGNED_CERT_IN_CHAIN",
16
+ "UNABLE_TO_VERIFY_LEAF_SIGNATURE",
17
+ "UNABLE_TO_GET_ISSUER_CERT_LOCALLY",
18
+ "CERT_HAS_EXPIRED",
19
+ "ERR_TLS_CERT_ALTNAME_INVALID",
20
+ ]);
21
+ /** Node reports an untrusted certificate only as "fetch failed"; say what to do. Verification stays on. */
22
+ function untrustedCertificate(url, code) {
23
+ const origin = new URL(url).origin;
24
+ return Object.assign(new Error(`DSM at ${origin} presented a TLS certificate this computer does not trust (${code}). ` +
25
+ `Export the NAS certificate or its CA as PEM and run with NODE_EXTRA_CA_CERTS=<that file>, ` +
26
+ `or give DSM a certificate whose name matches the host profile. Certificate checks stay on.`), { code: "DSM_TLS_UNTRUSTED" });
27
+ }
10
28
  function headerRecord(headers) {
11
29
  if (!headers) {
12
30
  return {};
@@ -24,6 +42,10 @@ export async function dsmFetch(url, init = {}) {
24
42
  return await fetch(url, init);
25
43
  }
26
44
  catch (error) {
45
+ const code = errorCode(error);
46
+ if (code && UNTRUSTED_CERTIFICATE.has(code)) {
47
+ throw untrustedCertificate(url, code);
48
+ }
27
49
  if (!isHostUnreachable(error)) {
28
50
  throw error;
29
51
  }
@@ -2,6 +2,7 @@ import type { SharePermissionListing } from "../deploy/share-permission-prefligh
2
2
  import type { InstalledPackageState } from "./catalog-install.js";
3
3
  import type { PortInfoEntry } from "./port-info.js";
4
4
  import type { DsmPortProfileInventory } from "./dsm-port-profile.js";
5
+ import type { NativeArchFamily } from "../build-spk/dsm-arch.js";
5
6
  export interface DeployIdentity {
6
7
  packageId: string;
7
8
  displayname: string;
@@ -32,8 +33,9 @@ export interface NasHost {
32
33
  ready: boolean;
33
34
  executor_digest: string;
34
35
  policy_digest: string;
36
+ error?: string;
35
37
  } | undefined>;
36
- getArchFamily?(): Promise<"x86_64" | "aarch64" | undefined>;
38
+ getArchFamily?(): Promise<NativeArchFamily | undefined>;
37
39
  /** List the current logical Shared Folder names using an administrator session. */
38
40
  listShareNames?(): Promise<string[]>;
39
41
  resolveSharePath?(logicalName: string): Promise<string | undefined>;
package/dist/info.js CHANGED
@@ -5,7 +5,9 @@ import { appOriginFromContract } from "./host/open-url.js";
5
5
  import { resolveDeployIdentity } from "./deploy/identity.js";
6
6
  import { loadStandaloneManifest, } from "./standalone/manifest.js";
7
7
  import { inspectSpk } from "./standalone/inspect-spk.js";
8
- import { executorPolicy } from "./runtime/executor-policy.js";
8
+ import { isNativeLayout, loadNativeLayout } from "./standalone/native-layout.js";
9
+ import { executorPolicy, executorPolicyText, sha256 } from "./runtime/executor-policy.js";
10
+ import { executorTasks } from "./runtime/executor-tasks.js";
9
11
  import { uninstallCleanup } from "./runtime/uninstall-cleanup.js";
10
12
  export async function projectInfo(options) {
11
13
  const identity = resolveDeployIdentity(options.project, { dev: options.dev });
@@ -63,6 +65,21 @@ export function classifyRootTasks(expected, inventory) {
63
65
  ? result("needs-admin-approval", "Approve missing or outdated root tasks in the DSM package UI.")
64
66
  : result("ready", "Boot and control tasks match the executor and policy digests. Executor health must be checked in the App window.");
65
67
  }
68
+ async function liveRootCapability(m, host) {
69
+ try {
70
+ const policyDigest = sha256(executorPolicyText(executorPolicy(m)));
71
+ const classified = classifyRootTasks(executorTasks({ package: m.package, policyDigest }), await host.getRootTaskInventory?.(m.package));
72
+ if (classified.state !== "ready")
73
+ return classified;
74
+ const observed = await host.getExecutorStatus?.(m.package);
75
+ if (observed?.ready !== true || observed.policy_digest !== policyDigest)
76
+ return { ...classified, state: "blocked", reason: observed?.error ?? "Executor health or policy digest readback could not be verified." };
77
+ return { ...classified, reason: "Root tasks and the running executor match the source policy (no local SPK, so executor bytes were not compared)." };
78
+ }
79
+ catch {
80
+ return { name: "rootOps", state: "blocked", reason: "DSM executor or root task readback could not be verified." };
81
+ }
82
+ }
66
83
  async function rootCapability(m, host, version) {
67
84
  const blocked = (reason) => ({
68
85
  name: "rootOps",
@@ -82,7 +99,7 @@ async function rootCapability(m, host, version) {
82
99
  return blocked("NAS architecture cannot be matched to the local executor SPK artifact.");
83
100
  const spk = join(m.root, "dist", `${m.package}-${version}-${arch}.spk`);
84
101
  if (!existsSync(spk))
85
- return blocked("Built executor SPK artifact is unavailable; build this installed version first.");
102
+ return liveRootCapability(m, host);
86
103
  try {
87
104
  const inspected = await inspectSpk(spk);
88
105
  if (inspected.findings.length ||
@@ -100,7 +117,7 @@ async function rootCapability(m, host, version) {
100
117
  if (classified.state === "ready") {
101
118
  const observed = await host.getExecutorStatus?.(m.package);
102
119
  if (!observed || observed.ready !== true || observed.executor_digest !== executor.executorDigest || observed.policy_digest !== executor.policyDigest)
103
- return { ...classified, executor, state: "blocked", reason: "Executor health or digest readback could not be verified." };
120
+ return { ...classified, executor, state: "blocked", reason: observed?.error ?? "Executor health or digest readback could not be verified." };
104
121
  }
105
122
  return { ...classified, executor };
106
123
  }
@@ -111,12 +128,14 @@ async function rootCapability(m, host, version) {
111
128
  export async function standaloneInfo(options) {
112
129
  if (options.dev)
113
130
  throw Object.assign(new Error("--dev is not supported for native-go/binary; choose a distinct package name at init"), { code: "DEVELOPMENT_INSTANCE_UNSUPPORTED" });
131
+ if (isNativeLayout(options.root))
132
+ return nativeLayoutInfo(options.root, options.host);
114
133
  const loaded = loadStandaloneManifest(options.root);
115
134
  if (!loaded.ok)
116
135
  throw Object.assign(new Error(loaded.errors.map((e) => e.message).join("\n")), { code: loaded.errors[0].code });
117
136
  const m = loaded.manifest;
118
- const remote = await options.host.getInstalledPackageState?.(m.package);
119
- const installed = await options.host.isPackageInstalled(m.package);
137
+ const { remote, ...state } = await packageState(m.package, options.host);
138
+ const installed = state.installed;
120
139
  const capabilities = [];
121
140
  let executor;
122
141
  for (const name of ["files", "jobs", "identity", "rootOps", "delegate"]) {
@@ -142,16 +161,32 @@ export async function standaloneInfo(options) {
142
161
  });
143
162
  }
144
163
  return {
145
- packageIdentity: m.package,
146
- developmentInstance: false,
147
- installed,
148
- readiness: remote?.status ?? (installed ? "UNKNOWN" : "NOT_INSTALLED"),
149
- openUrl: `${options.host.dsmOrigin.replace(/\/$/, "")}/?launchApp=SYNO.SDS.${m.package}.Instance`,
164
+ ...state,
150
165
  capabilities,
151
166
  ...(executor ? { executor } : {}),
152
167
  uninstallCleanup: uninstallCleanup(m.package, Boolean(m.capabilities.rootOps || m.capabilities.delegate)),
153
168
  };
154
169
  }
170
+ /** A native layout has no SDK capabilities; report package state only. */
171
+ async function nativeLayoutInfo(root, host) {
172
+ const loaded = loadNativeLayout(root);
173
+ if (!loaded.ok)
174
+ throw Object.assign(new Error(loaded.errors.map((e) => e.message).join("\n")), { code: loaded.errors[0].code });
175
+ const { remote, ...state } = await packageState(loaded.manifest.package, host);
176
+ return { ...state, capabilities: [] };
177
+ }
178
+ async function packageState(pkg, host) {
179
+ const remote = await host.getInstalledPackageState?.(pkg);
180
+ const installed = await host.isPackageInstalled(pkg);
181
+ return {
182
+ remote,
183
+ packageIdentity: pkg,
184
+ developmentInstance: false,
185
+ installed,
186
+ readiness: remote?.status ?? (installed ? "UNKNOWN" : "NOT_INSTALLED"),
187
+ openUrl: `${host.dsmOrigin.replace(/\/$/, "")}/?launchApp=SYNO.SDS.${pkg}.Instance`,
188
+ };
189
+ }
155
190
  export function formatProjectInfo(info) {
156
191
  return [
157
192
  `Package Identity:${info.packageIdentity}`,
package/dist/init.d.ts CHANGED
@@ -1,8 +1,11 @@
1
+ import { type CapabilityName } from "./capabilities.js";
1
2
  import type { TemplateName } from "./types.js";
2
3
  /** The container templates need DSM 7.2.1 or later; native-go and binary need 7.1. */
3
4
  export declare function isContainerTemplate(template: TemplateName): boolean;
4
5
  export declare function isTemplateName(value: string): value is TemplateName;
5
6
  export declare function listTemplates(): TemplateName[];
7
+ /** New native scaffolds opt in explicitly; existing manifests are never migrated. */
8
+ export declare function nativeCapabilitySettings(names: readonly CapabilityName[]): Record<string, unknown>;
6
9
  export interface InitOptions {
7
10
  /** os_min_ver of the new project: a release (7.2) or a full 7.x-build; default is the template's (DSM 7.1 for native-go/binary, DSM 7.2.1 for container templates). */
8
11
  minDsm?: string;
package/dist/init.js CHANGED
@@ -1,6 +1,7 @@
1
1
  import { spawnSync } from "node:child_process";
2
- import { parse, stringify } from "yaml";
2
+ import { isMap, isScalar, parse, parseDocument, stringify } from "yaml";
3
3
  import { capabilityNames, capabilitySettings } from "./capabilities.js";
4
+ import { MOVE_FROM_STAGING } from './runtime/delegate-contract.js';
4
5
  import { cpSync, existsSync, mkdirSync, readdirSync, readFileSync, renameSync, rmSync, statSync, writeFileSync } from "node:fs";
5
6
  import { basename, join, relative } from "node:path";
6
7
  import { templatesRoot } from "./template-paths.js";
@@ -25,6 +26,15 @@ export function isTemplateName(value) {
25
26
  export function listTemplates() {
26
27
  return [...TEMPLATE_NAMES];
27
28
  }
29
+ /** New native scaffolds opt in explicitly; existing manifests are never migrated. */
30
+ export function nativeCapabilitySettings(names) {
31
+ const settings = capabilitySettings(names);
32
+ if (names.includes('delegate'))
33
+ settings.delegate = {
34
+ scope: 'install-selected', operations: [MOVE_FROM_STAGING], destinations: ['save'], lifetime: 'until-revoked',
35
+ };
36
+ return settings;
37
+ }
28
38
  /**
29
39
  * native-go --with root-ops: files laid over the native-go template, and the
30
40
  * files they replace.
@@ -85,10 +95,12 @@ function isTemplateBuildArtifact(path) {
85
95
  }
86
96
  export function initFromTemplate(template, targetDir, options = {}) {
87
97
  if (!isTemplateName(template))
88
- throw new Error(`[INIT_TEMPLATE_INVALID] 未知模板:${template}`);
98
+ throw new Error(`[INIT_TEMPLATE_UNKNOWN] 未知模板:${template}`);
89
99
  const caps = capabilityNames(options.with ?? []);
90
100
  if (caps.includes("login") && resolvedUi(template, options) === "plain")
91
101
  throw new Error("[CAPABILITY_COMBINATION_INVALID] login 需要獨立登入頁;plain UI 不支援,請改用 --ui vue。");
102
+ if (caps.includes("delegate") && resolvedUi(template, options) === "plain")
103
+ throw new Error("[CAPABILITY_COMBINATION_INVALID] delegate 的 install-selected scope 只能在 Vue 面板確認;plain UI 不支援,請改用 --ui vue。");
92
104
  if (options.from && template !== "compose")
93
105
  throw new Error("[INIT_OPTIONS_UNSUPPORTED] --from 只適用於 compose。");
94
106
  if (caps.length && template !== "native-go")
@@ -183,7 +195,7 @@ export function initFromTemplate(template, targetDir, options = {}) {
183
195
  }
184
196
  if (executor) {
185
197
  const path = join(targetDir, "backend/internal/app/routes_test.go");
186
- writeFileSync(path, readFileSync(path, "utf8").replace('if strings.HasPrefix(r.Method(), "jobs.") {', 'if r.Method() == "executor.status" { want = "internal" }\n\t\tif r.Method() == "root.run" { want = "session_required" }\n\t\tif strings.HasPrefix(r.Method(), "jobs.") {'));
198
+ writeFileSync(path, readFileSync(path, "utf8").replace('if strings.HasPrefix(r.Method(), "jobs.") {', 'if r.Method() == "root.run" { want = "session_required" }\n\t\tif strings.HasPrefix(r.Method(), "jobs.") {'));
187
199
  }
188
200
  if (background && ui === "vue") {
189
201
  const app = join(targetDir, "frontend/src/App.vue");
@@ -194,20 +206,11 @@ export function initFromTemplate(template, targetDir, options = {}) {
194
206
  if (template !== "compose") {
195
207
  const path = join(targetDir, "dsm-package.yaml");
196
208
  const raw = parse(readFileSync(path, "utf8"));
197
- raw.capabilities = capabilitySettings(caps);
198
- writeFileSync(path, stringify(raw));
199
- }
200
- if (template === "compose" && options.from) {
201
- const imported = readFileSync(options.from, "utf8");
202
- const compose = parse(imported);
203
- if (!compose || typeof compose !== "object" || !compose.services)
204
- throw new Error("[INIT_COMPOSE_INVALID] --from 需要包含 services 的 Compose YAML。");
205
- writeFileSync(join(targetDir, "compose.yaml"), imported);
206
- const path = join(targetDir, "synology-app.yaml");
207
- const raw = parse(readFileSync(path, "utf8"));
208
- delete raw.install;
209
+ raw.capabilities = nativeCapabilitySettings(caps);
209
210
  writeFileSync(path, stringify(raw));
210
211
  }
212
+ if (template === "compose" && options.from)
213
+ importCompose(options.from, targetDir, options);
211
214
  if (template === "binary") {
212
215
  mkdirSync(join(targetDir, "prebuilt"), { recursive: true });
213
216
  for (const [arch, goarch] of [["x86_64", "amd64"], ["aarch64", "arm64"]]) {
@@ -221,6 +224,35 @@ export function initFromTemplate(template, targetDir, options = {}) {
221
224
  writeFileSync(manifest, readFileSync(manifest, "utf8") + sharedFolderYaml(options.sharedFolder));
222
225
  }
223
226
  }
227
+ /**
228
+ * --from replaces the example Compose. The manifest takes the requested (or
229
+ * directory-derived) identity and keeps only the example install entries the
230
+ * imported Compose references as `${KEY}` (or `${KEY_UID}`/`${KEY_GID}`).
231
+ */
232
+ function importCompose(from, targetDir, options) {
233
+ const imported = readFileSync(from, "utf8");
234
+ const compose = parse(imported);
235
+ if (!compose || typeof compose !== "object" || !compose.services)
236
+ throw new Error("[INIT_COMPOSE_INVALID] --from 需要包含 services 的 Compose YAML。");
237
+ writeFileSync(join(targetDir, "compose.yaml"), imported);
238
+ const path = join(targetDir, "synology-app.yaml");
239
+ const doc = parseDocument(readFileSync(path, "utf8"));
240
+ doc.set("package", options.packageId ?? defaultPackageId(targetDir));
241
+ doc.set("displayname", validDisplayName(options.displayName ?? defaultDisplayName(targetDir)));
242
+ const install = doc.get("install");
243
+ const referenced = new Set([...imported.matchAll(/\$\{([A-Z][A-Z0-9_]*?)(?:_UID|_GID)?\}/g)].map(match => match[1]));
244
+ if (isMap(install))
245
+ install.items = install.items.filter(item => referenced.has(String(isScalar(item.key) ? item.key.value : item.key)));
246
+ if (!isMap(install) || install.items.length === 0)
247
+ doc.delete("install");
248
+ writeFileSync(path, String(doc));
249
+ }
250
+ function validDisplayName(displayName) {
251
+ if (displayName.trim() === "" || /[\r\n"\\]/.test(displayName)) {
252
+ throw new Error("[INIT_DISPLAY_NAME_INVALID] 顯示名稱必須是單行,且不含 \" 或 \\。");
253
+ }
254
+ return displayName;
255
+ }
224
256
  /** The `install.sharedFolder` block, validated by the same reader build-spk uses. */
225
257
  function sharedFolderYaml(options) {
226
258
  const folder = { mode: options.mode, key: options.key ?? "shared", ...(options.name === undefined ? {} : { name: options.name }), permission: options.permission ?? "rw" };
@@ -243,15 +275,14 @@ function substitutionsFor(template, targetDir, options) {
243
275
  if (options.ui !== undefined && !["plain", "vue"].includes(options.ui))
244
276
  throw new Error("[INIT_UI_INVALID] --ui 只接受 plain 或 vue。");
245
277
  if (!SUBSTITUTED[template]) {
278
+ if (options.from && options.displayName !== undefined)
279
+ validDisplayName(options.displayName);
246
280
  return {};
247
281
  }
248
282
  const packageId = options.packageId ?? defaultPackageId(targetDir);
249
283
  if (!PACKAGE_ID.test(packageId)) {
250
284
  throw new Error(`[INIT_PACKAGE_INVALID] package「${packageId}」必須以英文字母開頭,只含英數字(2–64 字),例如 PhotoTagger。`);
251
285
  }
252
- const displayName = options.displayName ?? defaultDisplayName(targetDir);
253
- if (displayName.trim() === "" || /[\r\n"\\]/.test(displayName)) {
254
- throw new Error("[INIT_DISPLAY_NAME_INVALID] 顯示名稱必須是單行,且不含 \" 或 \\。");
255
- }
286
+ const displayName = validDisplayName(options.displayName ?? defaultDisplayName(targetDir));
256
287
  return { __PACKAGE__: packageId, __DISPLAY_NAME__: displayName, __BACKGROUND_ENABLED__: caps.includes("jobs") || caps.includes("delegate") ? "true" : "false" };
257
288
  }
@@ -0,0 +1,21 @@
1
+ /** Plain text, or a map from a BCP-47 tag (en, zh-TW, ...) to plain text. */
2
+ export type LocalizedText = string | Record<string, string>;
3
+ export interface PackageMetadata {
4
+ long_description?: LocalizedText;
5
+ changelog?: LocalizedText;
6
+ contact_email?: string;
7
+ support_email?: string;
8
+ homepage?: string;
9
+ source_url?: string;
10
+ issues_url?: string;
11
+ license?: string;
12
+ maintainer_url?: string;
13
+ distributor?: string;
14
+ distributor_url?: string;
15
+ support_url?: string;
16
+ helpurl?: string;
17
+ screenshots?: string[];
18
+ }
19
+ export declare function safeMetadataUrl(value: unknown, mail?: boolean): value is string;
20
+ export declare function validateMetadata(raw: unknown): PackageMetadata;
21
+ export declare function metadataInfoFields(metadata?: PackageMetadata): [string, string][];
@@ -0,0 +1,101 @@
1
+ export function safeMetadataUrl(value, mail = false) {
2
+ if (typeof value !== "string" || value.length > 2048)
3
+ return false;
4
+ try {
5
+ const u = new URL(value);
6
+ return (!u.username &&
7
+ !u.password &&
8
+ (["https:", "http:"].includes(u.protocol) ||
9
+ (mail &&
10
+ u.protocol === "mailto:" &&
11
+ /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(u.pathname))));
12
+ }
13
+ catch {
14
+ return false;
15
+ }
16
+ }
17
+ export function validateMetadata(raw) {
18
+ if (!raw || typeof raw !== "object" || Array.isArray(raw))
19
+ throw new Error("invalid metadata");
20
+ const allowed = [
21
+ "long_description",
22
+ "changelog",
23
+ "contact_email",
24
+ "support_email",
25
+ "homepage",
26
+ "source_url",
27
+ "issues_url",
28
+ "license",
29
+ "maintainer_url",
30
+ "distributor",
31
+ "distributor_url",
32
+ "support_url",
33
+ "helpurl",
34
+ "screenshots",
35
+ ];
36
+ for (const [k, v] of Object.entries(raw)) {
37
+ if (!allowed.includes(k))
38
+ throw new Error("unknown metadata");
39
+ if (k === "screenshots") {
40
+ if (!Array.isArray(v) ||
41
+ v.length > 12 ||
42
+ !v.every((u) => safeMetadataUrl(u)))
43
+ throw new Error("invalid screenshots");
44
+ }
45
+ else if ((k === "long_description" || k === "changelog") &&
46
+ typeof v === "object" &&
47
+ v &&
48
+ !Array.isArray(v)) {
49
+ if (Object.entries(v).length > 32 ||
50
+ !Object.entries(v).every(([lang, t]) => /^[a-z]{2}(?:-[A-Za-z]{2})?$/.test(lang) &&
51
+ typeof t === "string" &&
52
+ t.length <= 16000 &&
53
+ !/[\x00-\x08\x0b\x0c\x0e-\x1f]/.test(t)))
54
+ throw new Error("invalid localized description");
55
+ }
56
+ else if (typeof v !== "string" ||
57
+ v.length > 16000 ||
58
+ /[\x00-\x08\x0b\x0c\x0e-\x1f]/.test(v))
59
+ throw new Error("invalid text");
60
+ if (k.endsWith("_email") &&
61
+ (typeof v !== "string" || !/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(v)))
62
+ throw new Error("invalid email");
63
+ if ([
64
+ "homepage",
65
+ "source_url",
66
+ "issues_url",
67
+ "maintainer_url",
68
+ "distributor_url",
69
+ "support_url",
70
+ "helpurl",
71
+ ].includes(k) &&
72
+ !safeMetadataUrl(v, k === "support_url"))
73
+ throw new Error("invalid metadata URL");
74
+ }
75
+ if (JSON.stringify(raw).length > 32000)
76
+ throw new Error("metadata too large");
77
+ return raw;
78
+ }
79
+ export function metadataInfoFields(metadata) {
80
+ if (!metadata)
81
+ return [];
82
+ const m = validateMetadata(metadata), fields = [];
83
+ // INFO has one changelog; a per-language map contributes its English text.
84
+ const changelog = typeof m.changelog === "object"
85
+ ? (m.changelog.en ?? Object.values(m.changelog)[0])
86
+ : m.changelog;
87
+ for (const [k, v] of [
88
+ ["maintainer_url", m.maintainer_url],
89
+ ["distributor", m.distributor],
90
+ ["distributor_url", m.distributor_url],
91
+ ["support_url", m.support_url],
92
+ ["helpurl", m.helpurl],
93
+ ["changelog", changelog],
94
+ ])
95
+ if (v)
96
+ fields.push([k, v.replace(/\r?\n/g, " · ")]);
97
+ if (!m.support_url && m.support_email)
98
+ fields.push(["support_url", "mailto:" + m.support_email]);
99
+ fields.push(["spk_land_metadata", JSON.stringify(m)]);
100
+ return fields;
101
+ }
@@ -0,0 +1,25 @@
1
+ import type { AppContract } from "../types.js";
2
+ /**
3
+ * DSM Application Portal alias: `https://<nas>/<alias>/` opens the App after
4
+ * DSM sign-in. DSM itself only refuses empty aliases (4100), synoman
5
+ * directory names (4102) and aliases another App uses (4103); the SDK keeps a
6
+ * stricter, URL-safe rule so the same value works in links and shell scripts.
7
+ */
8
+ export declare const PORTAL_ALIAS_PATTERN: RegExp;
9
+ export declare const PORTAL_ALIAS_MAX = 64;
10
+ /**
11
+ * Names DSM already serves: /usr/syno/synoman entries and the default aliases
12
+ * of Synology Apps, as observed on DSM 7.4-90069. DSM stays the authority.
13
+ */
14
+ export declare const RESERVED_PORTAL_ALIASES: readonly string[];
15
+ export interface PortalDeclaration {
16
+ alias: string;
17
+ }
18
+ export declare function isPortalAlias(value: unknown): value is string;
19
+ /** The package name as a slug; a reserved or too long name falls back to a stable hash. */
20
+ export declare function defaultPortalAlias(packageId: string): string;
21
+ /** `{}` or `{alias}`; undefined for any other shape. */
22
+ export declare function readPortalDeclaration(value: unknown, packageId: string): PortalDeclaration | undefined;
23
+ /** A Compose App reaches the portal only as a DSM window behind the DSM-authenticated gateway. */
24
+ export declare function contractPortalSupported(contract: AppContract): boolean;
25
+ export declare function resolvedContractPortal(contract: AppContract): PortalDeclaration | undefined;
@@ -0,0 +1,48 @@
1
+ import { createHash } from "node:crypto";
2
+ import { readWebIntegration, resolvedWebLaunch, usesAuthGateway } from "../contract/web-launch.js";
3
+ /**
4
+ * DSM Application Portal alias: `https://<nas>/<alias>/` opens the App after
5
+ * DSM sign-in. DSM itself only refuses empty aliases (4100), synoman
6
+ * directory names (4102) and aliases another App uses (4103); the SDK keeps a
7
+ * stricter, URL-safe rule so the same value works in links and shell scripts.
8
+ */
9
+ export const PORTAL_ALIAS_PATTERN = /^[a-z0-9](?:[a-z0-9-]{0,62}[a-z0-9])?$/;
10
+ export const PORTAL_ALIAS_MAX = 64;
11
+ /**
12
+ * Names DSM already serves: /usr/syno/synoman entries and the default aliases
13
+ * of Synology Apps, as observed on DSM 7.4-90069. DSM stays the authority.
14
+ */
15
+ export const RESERVED_PORTAL_ALIASES = [
16
+ "dsfile", "indexdb", "mobile", "oauth", "scripts", "sharing", "ssdp", "synosdsjslib", "synohdpack", "webapi", "webman",
17
+ "calendar", "chatplus", "contacts", "drive", "file", "photo", "sso", "synologymeet",
18
+ ];
19
+ export function isPortalAlias(value) {
20
+ return typeof value === "string" && PORTAL_ALIAS_PATTERN.test(value) && !RESERVED_PORTAL_ALIASES.includes(value);
21
+ }
22
+ /** The package name as a slug; a reserved or too long name falls back to a stable hash. */
23
+ export function defaultPortalAlias(packageId) {
24
+ const slug = packageId.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
25
+ return isPortalAlias(slug) ? slug : `app-${createHash("sha256").update(packageId).digest("hex").slice(0, 12)}`;
26
+ }
27
+ /** `{}` or `{alias}`; undefined for any other shape. */
28
+ export function readPortalDeclaration(value, packageId) {
29
+ if (typeof value !== "object" || value === null || Array.isArray(value))
30
+ return undefined;
31
+ const keys = Object.keys(value);
32
+ if (keys.some((key) => key !== "alias"))
33
+ return undefined;
34
+ const alias = value.alias;
35
+ if (alias === undefined)
36
+ return { alias: defaultPortalAlias(packageId) };
37
+ return isPortalAlias(alias) ? { alias } : undefined;
38
+ }
39
+ /** A Compose App reaches the portal only as a DSM window behind the DSM-authenticated gateway. */
40
+ export function contractPortalSupported(contract) {
41
+ return resolvedWebLaunch(contract) === "dsm-window" && usesAuthGateway(contract);
42
+ }
43
+ export function resolvedContractPortal(contract) {
44
+ const portal = readWebIntegration(contract)?.portal;
45
+ if (portal === undefined || !contractPortalSupported(contract))
46
+ return undefined;
47
+ return readPortalDeclaration(portal, String(contract.package ?? ""));
48
+ }
@@ -0,0 +1,33 @@
1
+ import type { Translate } from "../build-spk/wizard-i18n.js";
2
+ import type { InstallWizardStep } from "../build-spk/install-wizard.js";
3
+ import type { AppContract } from "../types.js";
4
+ /** Package payload paths DSM's app-portal worker copies to /usr/syno/etc/www/app.d at start. */
5
+ export declare const PORTAL_JSON_RELPATH = "etc/portal.json";
6
+ export declare const PORTAL_MUSTACHE_RELPATH = "etc/portal.mustache";
7
+ /** Install wizard key; DSM exports it to preinst/postinst. */
8
+ export declare const PORTAL_INSTALL_KEY = "portal_alias";
9
+ /** Package-owned install draft, never DSM's saved portal configuration. */
10
+ export declare const PORTAL_DRAFT = "portal-install.json";
11
+ export interface PortalPackage {
12
+ /** The DSM app id the alias opens: an AppInstance or a `.url` entry of ui/config. */
13
+ appId: string;
14
+ /** Control Panel display name. */
15
+ application: string;
16
+ /** Default alias; the wizard answer replaces it. */
17
+ alias: string;
18
+ }
19
+ /** A Compose portal opens the App's `.url` entry of ui/config, keyed by the package id. */
20
+ export declare function contractPortal(contract: AppContract): PortalPackage | undefined;
21
+ export declare function portalResource(): Record<string, unknown>;
22
+ /** Every field of DSM's own app.d files; the worker refuses a partial file (synopkg start 272). */
23
+ export declare function renderPortalJson(portal: PortalPackage): string;
24
+ /** DSM's CGI location: index.cgi launches the app id nginx passes as REWRITE_APP. */
25
+ export declare function renderPortalMustache(): string;
26
+ export declare function renderPortalWizardItems(t: Translate, alias: string): Array<Record<string, unknown>>;
27
+ export declare function renderPortalWizardStep(t: Translate, alias: string): InstallWizardStep;
28
+ /**
29
+ * preinst validates the wizard answer, postinst stores it and writes it as the
30
+ * default alias of the package's own portal.json, postupgrade restores it into
31
+ * the new payload. Activation stays an administrator's AppPortal set call.
32
+ */
33
+ export declare function renderPortalLifecycle(name: string, portal: PortalPackage): string;
@@ -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
+ }