@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
@@ -97,15 +97,14 @@ test('a null job list from the worker renders as no jobs',async()=>{
97
97
  expect(f.root.textContent).toContain(backgroundCopy('en').noJobs);f.close()
98
98
  })
99
99
 
100
- test('the picker dialog explains the one-time read',async()=>{
101
- for(const locale of Object.keys(backgroundCatalogs)){
102
- const f=mount(async(m,p)=>m==='bg.status'?status():m==='bg.jobs.list'?{jobs:[]}:browse(p),locale);await settle()
103
- const copy=backgroundCopy(locale)
104
- click(f.root,copy.chooseFile);await settle()
105
- const dialog=f.root.querySelector('[role=dialog]')!.textContent!
106
- expect(dialog).toContain(copy.pickerConsent)
107
- f.close()
108
- }
100
+ // One test per locale: each mounts its own app, so a slow host cannot push nine mounts past one test timeout.
101
+ test.each(Object.keys(backgroundCatalogs))('the picker dialog explains the one-time read in %s',async locale=>{
102
+ const f=mount(async(m,p)=>m==='bg.status'?status():m==='bg.jobs.list'?{jobs:[]}:browse(p),locale);await settle()
103
+ const copy=backgroundCopy(locale)
104
+ click(f.root,copy.chooseFile);await settle()
105
+ const dialog=f.root.querySelector('[role=dialog]')!.textContent!
106
+ expect(dialog).toContain(copy.pickerConsent)
107
+ f.close()
109
108
  })
110
109
 
111
110
  test('ordinary owner can submit but has no administrator controls',async()=>{
@@ -186,23 +185,21 @@ test('fresh ordinary user can pick and submit without administrator switches',as
186
185
  expect(calls).not.toContain('bg.mode.set');expect(calls).not.toContain('bg.users.set');f.close()
187
186
  })
188
187
 
189
- test('jobs_busy renders a retry message in all nine locales and permits retry',async()=>{
190
- for(const locale of Object.keys(backgroundCatalogs)){
191
- let attempts=0
192
- const f=mount(async(m)=>{
193
- if(m==='bg.status')return status()
194
- if(m==='bg.jobs.list'){
195
- if(attempts++===0)throw {code:'jobs_busy',message:'CANARY'}
196
- return {jobs:[]}
197
- }
198
- return {schedules:[]}
199
- },locale);await settle()
200
- const copy=backgroundCopy(locale)
201
- expect(copy.errors.jobs_busy).toBeTruthy()
202
- expect(copy.errors.jobs_busy).not.toBe(copy.errors.failed)
203
- expect(f.root.querySelector('[role=alert]')?.textContent).toBe(copy.errors.jobs_busy)
204
- expect(f.root.textContent).not.toContain('CANARY')
205
- click(f.root,copy.refresh);await settle();expect(attempts).toBe(2)
206
- f.close()
207
- }
188
+ test.each(Object.keys(backgroundCatalogs))('jobs_busy renders a retry message in %s and permits retry',async locale=>{
189
+ let attempts=0
190
+ const f=mount(async(m)=>{
191
+ if(m==='bg.status')return status()
192
+ if(m==='bg.jobs.list'){
193
+ if(attempts++===0)throw {code:'jobs_busy',message:'CANARY'}
194
+ return {jobs:[]}
195
+ }
196
+ return {schedules:[]}
197
+ },locale);await settle()
198
+ const copy=backgroundCopy(locale)
199
+ expect(copy.errors.jobs_busy).toBeTruthy()
200
+ expect(copy.errors.jobs_busy).not.toBe(copy.errors.failed)
201
+ expect(f.root.querySelector('[role=alert]')?.textContent).toBe(copy.errors.jobs_busy)
202
+ expect(f.root.textContent).not.toContain('CANARY')
203
+ click(f.root,copy.refresh);await settle();expect(attempts).toBe(2)
204
+ f.close()
208
205
  })
@@ -9,6 +9,8 @@ import (
9
9
  "net/http"
10
10
  "os"
11
11
  "path/filepath"
12
+ "strings"
13
+ "syscall"
12
14
  "time"
13
15
 
14
16
  "dsmapp/internal/api"
@@ -51,8 +53,31 @@ func (cfg Config) executorControl(_ context.Context, caller api.Caller, raw json
51
53
  return map[string]bool{"written": true}, nil
52
54
  }
53
55
 
56
+ // The executor closes its socket while paused: the package is stopped, or an
57
+ // upgrade changed the pins and awaits administrator approval.
54
58
  func (cfg Config) executorStatus(ctx context.Context, _ api.Caller, _ json.RawMessage) (any, error) {
55
- return cfg.executorRequest(ctx, "status", nil, nil)
59
+ status, err := cfg.executorRequest(ctx, "status", nil, nil)
60
+ if errors.Is(err, syscall.ENOENT) || errors.Is(err, syscall.ECONNREFUSED) {
61
+ next := "Start the package."
62
+ if cfg.startRequested() {
63
+ next = "The package is running: an administrator approves background access again in Administrator setup; the executor log names the cause."
64
+ }
65
+ return map[string]any{"ready": false, "state": "paused", "reason": "executor_not_listening", "next": next}, nil
66
+ }
67
+ return status, err
68
+ }
69
+
70
+ // Package start and stop write the lifecycle mailbox; its last request tells a
71
+ // stopped package from a running one whose executor stays paused.
72
+ func (cfg Config) startRequested() bool {
73
+ f, err := os.Open(filepath.Join(cfg.DataDir, "executor", "control"))
74
+ if err != nil {
75
+ return false
76
+ }
77
+ defer f.Close()
78
+ b := make([]byte, 16)
79
+ n, _ := f.Read(b)
80
+ return strings.TrimSpace(string(b[:n])) == "start"
56
81
  }
57
82
 
58
83
  func (cfg Config) executorRootRun(ctx context.Context, caller api.Caller, req *http.Request, raw json.RawMessage) (any, error) {
@@ -73,8 +98,11 @@ func (cfg Config) executorRootRun(ctx context.Context, caller api.Caller, req *h
73
98
  return cfg.executorRequest(ctx, "root.run", auth, map[string]string{"name": in.Name})
74
99
  }
75
100
 
101
+ // executorSocketBase is a variable so tests can use a temporary directory.
102
+ var executorSocketBase = "/run/synopkgland"
103
+
76
104
  func (cfg Config) executorRequest(ctx context.Context, op string, auth, params any) (any, error) {
77
- conn, err := (&net.Dialer{Timeout: 5 * time.Second}).DialContext(ctx, "unix", "/run/synopkgland/"+cfg.PackageID+"/executor.sock")
105
+ conn, err := (&net.Dialer{Timeout: 5 * time.Second}).DialContext(ctx, "unix", filepath.Join(executorSocketBase, cfg.PackageID, "executor.sock"))
78
106
  if err != nil {
79
107
  return nil, err
80
108
  }
@@ -1,8 +1,10 @@
1
1
  package app
2
2
 
3
3
  import (
4
+ "net"
4
5
  "os"
5
6
  "path/filepath"
7
+ "strings"
6
8
  "testing"
7
9
  )
8
10
 
@@ -51,3 +53,65 @@ func TestExecutorRootRunRequiresCurrentDSMCookie(t *testing.T) {
51
53
  t.Fatal("root.run accepted absent DSM cookie")
52
54
  }
53
55
  }
56
+
57
+ // The executor closes its listener while paused (package stopped, or pins
58
+ // awaiting administrator approval). Status then answers, not "internal".
59
+ func TestExecutorStatusReportsAPausedExecutor(t *testing.T) {
60
+ cfg := testConfig(t)
61
+ previous := executorSocketBase
62
+ // A short base: unix socket paths are limited to about 100 bytes.
63
+ base, err := os.MkdirTemp("/tmp", "exec")
64
+ if err != nil {
65
+ t.Fatal(err)
66
+ }
67
+ executorSocketBase = base
68
+ defer func() { executorSocketBase = previous; os.RemoveAll(base) }()
69
+ check := func(name string) {
70
+ t.Helper()
71
+ code, envelope := call(t, cfg, alice, "executor.status", `{}`)
72
+ out, _ := envelope["data"].(map[string]any)
73
+ if code != "" || out["ready"] != false || out["state"] != "paused" || out["reason"] != "executor_not_listening" || out["next"] == "" || out["next"] == nil {
74
+ t.Fatalf("%s: %s %v", name, code, out)
75
+ }
76
+ }
77
+ check("no socket")
78
+ if code, _ := call(t, cfg, bob, "executor.status", `{}`); code != "" {
79
+ t.Fatal("authenticated user cannot read a paused status", code)
80
+ }
81
+ dir := filepath.Join(executorSocketBase, cfg.PackageID)
82
+ os.MkdirAll(dir, 0700)
83
+ ln, err := net.ListenUnix("unix", &net.UnixAddr{Name: filepath.Join(dir, "executor.sock"), Net: "unix"})
84
+ if err != nil {
85
+ t.Fatal(err)
86
+ }
87
+ ln.SetUnlinkOnClose(false)
88
+ ln.Close()
89
+ check("closed listener")
90
+ }
91
+
92
+ // next follows the last lifecycle request: a running package needs approval
93
+ // again, not a start.
94
+ func TestExecutorStatusNextFollowsTheLifecycleRequest(t *testing.T) {
95
+ cfg := testConfig(t)
96
+ previous := executorSocketBase
97
+ executorSocketBase = t.TempDir()
98
+ defer func() { executorSocketBase = previous }()
99
+ next := func() string {
100
+ t.Helper()
101
+ _, envelope := call(t, cfg, alice, "executor.status", `{}`)
102
+ out, _ := envelope["data"].(map[string]any)
103
+ s, _ := out["next"].(string)
104
+ return s
105
+ }
106
+ if !strings.HasPrefix(next(), "Start the package.") {
107
+ t.Fatal("no request:", next())
108
+ }
109
+ mailbox := filepath.Join(cfg.DataDir, "executor")
110
+ os.MkdirAll(mailbox, 0700)
111
+ for request, start := range map[string]bool{"stop\n": true, "start\n": false, "start": false} {
112
+ os.WriteFile(filepath.Join(mailbox, "control"), []byte(request), 0600)
113
+ if got := next(); strings.HasPrefix(got, "Start the package") != start || got == "" {
114
+ t.Fatalf("%q: %s", request, got)
115
+ }
116
+ }
117
+ }
@@ -18,6 +18,10 @@ subscriptions; unmount disposes subscriptions and ignores late responses.
18
18
  operations. `--with jobs` adds package-account jobs and schedules; it combines with root-ops and files. `--with files` enables current-session File Station access,
19
19
  and the background picker uses the retained session provider.
20
20
 
21
+ With `portal:` declared (`synopkgland add portal`), the status page shows
22
+ `src/portal/PortalSettings.vue`: the DSM Application Portal link, the alias form
23
+ for administrators, and the Control Panel shortcut.
24
+
21
25
  Use public library controls, the embedded theme, per-app modal scope and unique
22
26
  Vue ID prefixes. Backend route permissions and DSM checks remain authoritative.
23
27
  Generated build and unit tests do not establish NAS runtime acceptance.
@@ -7,7 +7,7 @@
7
7
  "name": "native-dsm-vue-app",
8
8
  "dependencies": {
9
9
  "@lucide/vue": "1.48.0",
10
- "@synopackageland/ui": "0.8.0",
10
+ "@synopackageland/ui": "0.8.1",
11
11
  "vue": "3.5.43"
12
12
  },
13
13
  "devDependencies": {
@@ -1147,9 +1147,9 @@
1147
1147
  }
1148
1148
  },
1149
1149
  "node_modules/@synopackageland/ui": {
1150
- "version": "0.8.0",
1151
- "resolved": "https://registry.npmjs.org/@synopackageland/ui/-/ui-0.8.0.tgz",
1152
- "integrity": "sha512-RR7vvIqZbVtp1jRG16/sqILGQjgAuxSN5eAlXluWshUTh9xcIMpsC39/j9L63iZyic58Drw7KiZyZyqYQL4NHw==",
1150
+ "version": "0.8.1",
1151
+ "resolved": "https://registry.npmjs.org/@synopackageland/ui/-/ui-0.8.1.tgz",
1152
+ "integrity": "sha512-3BykWpWrDGjIDVjdL0WeMwD0oO3z1/IIEishR9+2sYAL/spd3G9sEQL1J26jHl0EfvoPvwPZHNJdIL14JQJQYA==",
1153
1153
  "license": "MIT",
1154
1154
  "dependencies": {
1155
1155
  "@internationalized/date": "3.12.4",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "native-dsm-vue-app", "private": true, "type": "module",
3
3
  "scripts": {"build": "vue-tsc --noEmit && vite build", "test": "vitest run"},
4
- "dependencies": {"@lucide/vue": "1.48.0", "@synopackageland/ui": "0.8.0", "vue": "3.5.43"},
4
+ "dependencies": {"@lucide/vue": "1.48.0", "@synopackageland/ui": "0.8.1", "vue": "3.5.43"},
5
5
  "devDependencies": {"@vitejs/plugin-vue": "6.0.9", "vite": "7.3.6", "typescript": "5.9.3", "vue-tsc": "3.3.11", "vitest": "4.1.11", "jsdom": "27.4.0"}
6
6
  }
@@ -2,15 +2,18 @@
2
2
  import {computed, onMounted, onUnmounted, ref, watch} from 'vue'
3
3
  import {AppRoot, ConfigProvider, Button, Callout, Form, FormActions, FormItem, SettingsSection, Textarea} from '@synopackageland/ui'
4
4
  import {uiLocale} from './nas/locale'
5
+ import {appCopy} from './i18n'
5
6
  import type {AppStatus, Context} from './context'
6
7
  import AppShell from './shell/AppShell.vue'
7
8
  import CapabilityPanels from './CapabilityPanels.vue'
8
9
  import FilesPage from './files/FilesPage.vue'
10
+ import PortalSettings from './portal/PortalSettings.vue'
9
11
  import {shellPages, type ShellPage} from './shell/pages'
10
12
  import {createSession} from './identity/session'
11
13
  import LoginPage from './identity/LoginPage.vue'
12
14
  const props = defineProps<{ctx: Context}>()
13
15
  const login = createSession(props.ctx)
16
+ const copy = computed(() => appCopy(props.ctx.locale))
14
17
  const app = ref<AppStatus>()
15
18
  const busy = ref(false)
16
19
  const error = ref('')
@@ -25,7 +28,8 @@ const pages = computed(() => shellPages({background:backgroundEnabled}))
25
28
  const subtitle = computed(() => app.value ? `${app.value.package} · ${app.value.user} · ${app.value.version}` : undefined)
26
29
  function message(e: unknown) {
27
30
  const code = (e as {code?: string})?.code || 'request_failed'
28
- return ({session_required:'Your DSM session expired. Sign in again.',admin_required:'A DSM administrator must perform this action.',auth_unavailable:'DSM authentication is unavailable. Refresh to try again.'} as Record<string,string>)[code] || `Request failed (${code}). Refresh and try again.`
31
+ const errors = copy.value.errors as Record<string, string>
32
+ return (code !== 'request_failed' && errors[code]) || errors.request_failed!.replace('{code}', code)
29
33
  }
30
34
  async function refresh() {
31
35
  if(disposed || busy.value || (login.enabled.value && !login.subject.value)) return
@@ -39,7 +43,7 @@ async function refresh() {
39
43
  async function save() {
40
44
  if(disposed || busy.value) return
41
45
  busy.value = true; error.value='';notice.value=''
42
- try {await login.call('greeting.set',{text:text.value});if(!disposed) notice.value='Greeting saved.'}
46
+ try {await login.call('greeting.set',{text:text.value});if(!disposed) notice.value=copy.value.saved}
43
47
  catch(e) {if(!disposed) error.value=message(e)} finally {if(!disposed) busy.value=false}
44
48
  }
45
49
  watch(login.subject, (subject, previous) => {
@@ -60,22 +64,23 @@ onUnmounted(()=>{disposed=true;unsubscribe?.();login.dispose();text.value=''})
60
64
  <template>
61
65
  <AppRoot modal-scope="app" style="height: 100%">
62
66
  <ConfigProvider :locale="uiLocale(ctx.locale)">
63
- <p v-if="!login.ready.value" role="status">Checking session…</p>
64
- <LoginPage v-else-if="login.enabled.value && !login.subject.value" :session="login" />
67
+ <p v-if="!login.ready.value" role="status">{{ copy.checking }}</p>
68
+ <LoginPage v-else-if="login.enabled.value && !login.subject.value" :session="login" :locale="ctx.locale" />
65
69
  <AppShell v-else v-model="page" :locale="ctx.locale" :pages="pages" :description="subtitle">
66
- <template #actions><Button variant="secondary" :disabled="busy" @click="refresh">Refresh</Button><Button v-if="login.enabled.value" variant="secondary" :disabled="busy" @click="signOut">Sign out</Button></template>
67
- <div class="integration-app" :aria-busy="busy" aria-label="Package settings">
68
- <p v-if="busy" role="status">Loading package data…</p>
69
- <Callout v-if="error" tone="danger" title="Action required"><span role="alert">{{ error }}</span></Callout>
70
+ <template #actions><Button variant="secondary" :disabled="busy" @click="refresh">{{ copy.refresh }}</Button><Button v-if="login.enabled.value" variant="secondary" :disabled="busy" @click="signOut">{{ copy.signOut }}</Button></template>
71
+ <div class="integration-app" :aria-busy="busy" :aria-label="copy.settings">
72
+ <p v-if="busy" role="status">{{ copy.loading }}</p>
73
+ <Callout v-if="error" tone="danger" :title="copy.actionRequired"><span role="alert">{{ error }}</span></Callout>
70
74
  <p v-if="notice" role="status">{{ notice }}</p>
71
75
  <div data-page="status" v-show="page==='status'">
72
- <SettingsSection title="Package status" :collapsible="false">
76
+ <SettingsSection :title="copy.statusTitle" :collapsible="false">
73
77
  <p v-if="app">{{ app.package }} · {{ app.user }} · {{ app.version }}</p>
74
- <p v-else-if="error" role="status">Status could not be loaded. Refresh to try again.</p>
78
+ <p v-else-if="error" role="status">{{ copy.statusError }}</p>
75
79
  </SettingsSection>
76
- <SettingsSection v-if="app?.admin" title="Greeting" :collapsible="false">
77
- <Form @submit.prevent="save"><FormItem label="Greeting"><Textarea v-model="text" :disabled="busy" /></FormItem>
78
- <FormActions><Button variant="primary" :disabled="busy || !text.trim()" @click="save">Save</Button></FormActions>
80
+ <PortalSettings v-if="ctx.portal && app" :key="app.user" :portal="ctx.portal" />
81
+ <SettingsSection v-if="app?.admin" :title="copy.greeting" :collapsible="false">
82
+ <Form @submit.prevent="save"><FormItem :label="copy.greeting"><Textarea v-model="text" :disabled="busy" /></FormItem>
83
+ <FormActions><Button variant="primary" :disabled="busy || !text.trim()" @click="save">{{ copy.save }}</Button></FormActions>
79
84
  </Form>
80
85
  </SettingsSection>
81
86
  </div>
@@ -35,10 +35,41 @@ export interface HostAdapter {
35
35
  subscribe(event: HostEvent, listener: () => void): HostSubscriptionResult
36
36
  dispose(): void
37
37
  }
38
+ export interface PortalStatus {
39
+ state: 'active' | 'inactive' | 'unavailable'
40
+ // Set when active: https://<the NAS address in use>/<alias>/
41
+ alias?: string
42
+ url?: string
43
+ // An administrator can set or clear the alias here; otherwise Control Panel or nothing.
44
+ canManage: boolean
45
+ controlPanel: boolean
46
+ defaultAlias: string
47
+ }
48
+
49
+ // Rejects {code}: portal_alias_invalid, portal_alias_reserved, portal_alias_in_use, admin_required, portal_unavailable.
50
+ export interface PortalAccess {
51
+ // SDK panel text in DSM's language, keyed like the error codes plus title, link, apply, …
52
+ messages: Record<string, string>
53
+ status(): Promise<PortalStatus>
54
+ apply(alias: string): Promise<PortalStatus>
55
+ disable(): Promise<PortalStatus>
56
+ openControlPanel(): void
57
+ // The SDK's plain panel; Vue apps use portal/PortalSettings.vue instead.
58
+ mountPanel(element: HTMLElement): {refresh(): Promise<void>; dispose(): void}
59
+ }
60
+
38
61
  export interface Context {
62
+ // Explicit long-running opt-in; bind with createBackgroundSaveFacade and an
63
+ // owner-bound App job metadata provider. Legacy downloads use the other panel.
64
+ delegate?: import('./delegate/long-running').BackgroundSaveFacade
39
65
  files?: {endpoint:string;token?:()=>string|undefined}
40
66
  displayName?: string
41
67
  executor?: import('./delegate/executor-setup').ExecutorSetup
68
+ // DSM's signed-in account. isAdmin only hides or disables administrator
69
+ // settings; the backend enforces access with permissions.json and RequireAdmin.
70
+ user?: {readonly name: string; readonly isAdmin: boolean}
71
+ // DSM Application Portal, present only when dsm-package.yaml declares portal.
72
+ portal?: PortalAccess
42
73
  call<T = unknown>(method: string, params?: Record<string, unknown>): Promise<T>
43
74
  reopen(fn: () => void): () => void
44
75
  locale: string
@@ -1,24 +1,69 @@
1
1
  <script setup lang="ts">
2
2
  import {computed,onBeforeUnmount,onMounted,ref} from 'vue'
3
- import {Button,Callout,Form,FormActions,FormItem,Input,SettingsSection} from '@synopackageland/ui'
3
+ import {Button,Callout,Form,FormActions,FormItem,Input,Select,SettingsSection} from '@synopackageland/ui'
4
+ import type {Context} from '../context'
5
+ import type {ScopeSelection} from './long-running'
4
6
  import {useExecutorSetup,type CapabilityStatus} from './executor-setup'
5
7
  import {delegateCopy} from './copy'
6
- const props=defineProps<{admin:boolean;locale?:string}>()
8
+ const props=defineProps<{admin:boolean;locale?:string;ctx?:Pick<Context,'call'>}>()
9
+ const emit=defineEmits<{confirmed:[]}>()
7
10
  const setup=useExecutorSetup(),copy=computed(()=>delegateCopy(props.locale))
8
11
  const status=ref<CapabilityStatus>(),password=ref(''),busy=ref(false),error=ref(false)
12
+ const scope=ref<{scope_mode?:string;preselection?:string;selection?:ScopeSelection|null;mismatch?:boolean}>(),chosen=ref(''),shares=ref<Array<{label:string;value:string}>>([])
13
+ const dynamic=computed(()=>scope.value?.scope_mode==='install-selected')
14
+ const options=computed(()=>[{label:copy.value.homeScope,value:'home'},...shares.value])
15
+ const canConfirm=computed(()=>!dynamic.value||!!chosen.value&&options.value.some(o=>o.value===chosen.value))
16
+ const needsScope=computed(()=>dynamic.value&&!scope.value?.selection)
17
+ const changed=computed(()=>chosen.value!==scope.value?.selection?.scope)
9
18
  let disposed=false
10
- async function refresh(){if(!props.admin||busy.value)return;busy.value=true;error.value=false;try{const next=await setup.status();if(!disposed)status.value=next}catch{if(!disposed)error.value=true}finally{if(!disposed)busy.value=false}}
11
- async function authorize(){if(!props.admin||busy.value||!password.value)return;busy.value=true;error.value=false;const entered=password.value;password.value='';try{const next=await setup.authorize(entered);if(!disposed)status.value=next}catch{if(!disposed)error.value=true}finally{if(!disposed)busy.value=false}}
12
- onMounted(()=>void refresh());onBeforeUnmount(()=>{disposed=true;password.value=''})
19
+ async function load(approved?:CapabilityStatus){
20
+ const next=approved??await setup.status()
21
+ if(disposed)return
22
+ status.value=next
23
+ if(!props.ctx)return
24
+ const detail=await props.ctx.call<NonNullable<typeof scope.value>>('delegate.status')
25
+ if(disposed)return
26
+ scope.value=detail
27
+ if(detail.scope_mode!=='install-selected')return
28
+ const all:Array<{label:string;value:string}>=[]
29
+ let offset=0
30
+ for(let count=0;count<128;count++){
31
+ const page=await props.ctx.call<{shares:Array<{name:string;path:string;isdir:boolean}>;total:number;offset:number}>('bg.browse',{offset})
32
+ if(disposed)return
33
+ if(!Array.isArray(page.shares)||!Number.isSafeInteger(page.total)||page.total<0||!page.shares.length&&offset<page.total)throw {code:'EXECUTOR_PROTOCOL'}
34
+ for(const entry of page.shares){if(entry.isdir&&entry.path==='/'+entry.name&&!['home','homes'].includes(entry.name.toLowerCase()))all.push({label:entry.name,value:'shared:'+entry.name})}
35
+ offset+=page.shares.length
36
+ if(offset>=page.total)break
37
+ if(count===127)throw {code:'EXECUTOR_PROTOCOL'}
38
+ }
39
+ shares.value=all
40
+ chosen.value=detail.selection?.scope??detail.preselection??''
41
+ }
42
+ async function refresh(){if(!props.admin||busy.value)return;busy.value=true;error.value=false;try{await load()}catch{if(!disposed)error.value=true}finally{if(!disposed)busy.value=false}}
43
+ async function authorize(){
44
+ if(!props.admin||busy.value||!password.value||!canConfirm.value||error.value)return
45
+ busy.value=true;error.value=false;const entered=password.value;password.value=''
46
+ try{const next=dynamic.value?await setup.authorize(entered,chosen.value):await setup.authorize(entered);await load(next);if(!disposed)emit('confirmed')}
47
+ catch{if(!disposed)error.value=true}
48
+ finally{if(!disposed)busy.value=false}
49
+ }
50
+ onMounted(()=>{setup.subscribe?.(next=>{if(!disposed)status.value=next});void refresh()});onBeforeUnmount(()=>{disposed=true;password.value=''})
13
51
  </script>
14
52
  <template>
15
53
  <SettingsSection v-if="admin" :title="copy.setupTitle" :collapsible="false" :aria-busy="busy">
16
54
  <p>{{ copy.setupHelp }}</p>
17
55
  <Callout v-if="error" tone="danger" role="alert">{{ copy.setupError }}</Callout>
18
- <p role="status">{{ status?.state==='ready'?copy.ready:status?.state==='needs-admin-approval'?copy.needs:copy.blocked }}</p>
19
- <Form v-if="status?.state==='needs-admin-approval'" @submit.prevent="authorize">
20
- <FormItem :label="copy.password" label-for="delegate-password"><Input id="delegate-password" v-model="password" type="password" autocomplete="current-password" :disabled="busy" /></FormItem>
21
- <FormActions><Button type="submit" variant="primary" :disabled="busy||!password">{{ copy.authorize }}</Button></FormActions>
56
+ <p role="status">{{ status?.state==='ready'?(needsScope?copy.scopeNeeds:copy.ready):status?.state==='needs-admin-approval'?copy.needs:copy.blocked }}</p>
57
+ <p v-if="status?.reason&&(status.state==='blocked'||status.state==='unsupported')">{{ copy.details }}: {{ status.reason }}</p>
58
+ <p v-if="scope?.selection">{{ copy.protectedScope }}: {{ scope.selection.root }}</p>
59
+ <Callout v-if="scope?.mismatch" tone="warning">{{ copy.scopeMismatch }} {{ copy.preselection }}: {{ scope.preselection }} · {{ copy.protectedScope }}: {{ scope.selection?.root }}</Callout>
60
+ <Form v-if="status?.state==='needs-admin-approval'||status?.retry||dynamic&&status?.state==='ready'" @submit.prevent="authorize">
61
+ <template v-if="dynamic">
62
+ <p>{{ scope?.selection?copy.scopeChangeHelp:copy.preselectionHelp }}</p>
63
+ <FormItem :label="copy.scopeLabel" label-for="delegate-scope"><Select id="delegate-scope" v-model="chosen" :options="options" :disabled="busy||error" /></FormItem>
64
+ </template>
65
+ <FormItem :label="copy.password" label-for="delegate-password"><Input id="delegate-password" v-model="password" type="password" autocomplete="current-password" :disabled="busy||error" /></FormItem>
66
+ <FormActions><Button type="submit" variant="primary" :disabled="busy||error||!password||!canConfirm||dynamic&&!!scope?.selection&&!changed">{{ dynamic?(scope?.selection?copy.confirmScope:copy.confirmShare):copy.authorize }}</Button></FormActions>
22
67
  </Form>
23
68
  <Button variant="secondary" :disabled="busy" @click="refresh">{{ copy.refresh }}</Button>
24
69
  </SettingsSection>
@@ -0,0 +1,11 @@
1
+ import en from './i18n/en.json'
2
+ import zhTW from './i18n/zh-TW.json'
3
+ import zhCN from './i18n/zh-CN.json'
4
+ import ja from './i18n/ja.json'
5
+ import ko from './i18n/ko.json'
6
+ import de from './i18n/de.json'
7
+ import fr from './i18n/fr.json'
8
+ import es from './i18n/es.json'
9
+ import it from './i18n/it.json'
10
+ export type DelegateCopy=typeof en
11
+ export const delegateCatalogs:Record<string,DelegateCopy>={en,'zh-TW':zhTW,'zh-CN':zhCN,ja,ko,de,fr,es,it}
@@ -0,0 +1,12 @@
1
+ import {expect,test} from 'vitest'
2
+ import {delegateCopy} from './copy'
3
+ import {saveCopy} from './save-copy'
4
+ import zhCN from './i18n/zh-CN.json'
5
+ import saveJa from './i18n/save/ja.json'
6
+ test('delegate and save copy follow the DSM language across all nine catalogs',()=>{
7
+ expect(delegateCopy('chs')).toMatchObject(zhCN)
8
+ expect(delegateCopy('zh_CN')).toMatchObject(zhCN)
9
+ expect(saveCopy('jpn')).toEqual(saveJa)
10
+ expect(delegateCopy('unknown').refresh).toBe('Refresh')
11
+ expect(saveCopy().refresh).toBe('Refresh')
12
+ })
@@ -1,4 +1,4 @@
1
- import en from './i18n/en.json'
2
- import zh from './i18n/zh-TW.json'
3
- export function delegateCopy(locale='en'){return locale.toLowerCase().startsWith('zh')?zh:en}
4
- export function errorCopy(error:unknown,copy:typeof en){const code=(error as {code?:string})?.code;return ['session_required','AUTH_DENIED'].includes(code??'')?copy.session:['DENIED','SCOPE_DENIED','PERMISSION_DENIED','APP_DENIED','INELIGIBLE','denied'].includes(code??'')?copy.denied:copy.error}
1
+ import {uiLocale} from '../nas/locale'
2
+ import {delegateCatalogs,type DelegateCopy} from './catalogs'
3
+ export function delegateCopy(locale='en'){return delegateCatalogs[uiLocale(locale)]!}
4
+ export function errorCopy(error:unknown,copy:DelegateCopy){const code=(error as {code?:string})?.code;return ['session_required','AUTH_DENIED'].includes(code??'')?copy.session:['DENIED','SCOPE_DENIED','PERMISSION_DENIED','APP_DENIED','INELIGIBLE','denied'].includes(code??'')?copy.denied:copy.error}
@@ -0,0 +1,23 @@
1
+ // @vitest-environment jsdom
2
+ import {expect,test} from 'vitest'
3
+ import {createApp,h,nextTick} from 'vue'
4
+ import {AppRoot} from '@synopackageland/ui'
5
+ import ExecutorSettings from './ExecutorSettings.vue'
6
+ import {ExecutorSetupKey,type CapabilityStatus} from './executor-setup'
7
+ const settle=async()=>{for(let i=0;i<10;i++){await nextTick();await new Promise(r=>setTimeout(r))}}
8
+ // t-0237: a wrong password left only "Check status"; a retryable result keeps the form.
9
+ test('a retryable blocked approval keeps the password form for another try',async()=>{
10
+ const blocked:CapabilityStatus&{retry:boolean}={state:'blocked',reason:'dsm_3121: DSM rejected the administrator password. Nothing was created.',retry:true}
11
+ const setup={async status():Promise<CapabilityStatus>{return {state:'needs-admin-approval'}},async authorize(){return blocked},async control(){return blocked}}
12
+ const root=document.createElement('div');document.body.append(root)
13
+ const app=createApp({render:()=>h(AppRoot,{},()=>h(ExecutorSettings,{admin:true,locale:'en'}))})
14
+ app.provide(ExecutorSetupKey,setup)
15
+ try{
16
+ app.mount(root);await settle()
17
+ const input=root.querySelector<HTMLInputElement>('#delegate-password')!
18
+ input.value='wrong';input.dispatchEvent(new Event('input'));await settle()
19
+ root.querySelector('form')!.dispatchEvent(new Event('submit'));await settle()
20
+ expect(root.textContent).toContain('dsm_3121')
21
+ expect(root.querySelector('#delegate-password')).not.toBeNull()
22
+ }finally{app.unmount();root.remove()}
23
+ })
@@ -1,12 +1,18 @@
1
1
  import {inject,type InjectionKey} from 'vue'
2
2
  export type CapabilityState='unsupported'|'needs-admin-approval'|'ready'|'blocked'|'incompatible'
3
- export interface CapabilityStatus {state:CapabilityState;reason?:string}
3
+ // requested: a scope change whose root task may still land; it applied once
4
+ // status() reports that scope with a generation above this one.
5
+ // retry: approving again may succeed: nothing of this approval is left behind, or
6
+ // the approved tasks remain and approving again restarts the executor.
7
+ export interface CapabilityStatus {state:CapabilityState;reason?:string;requested?:{scope:string;generation:number};retry?:boolean}
4
8
  // p2-setup supplies the implementation through app.provide(ExecutorSetupKey, setup).
5
9
  // Passwords belong only to this call and must never be persisted or logged.
6
10
  export interface ExecutorSetup {
7
11
  status():Promise<CapabilityStatus>
8
- authorize(password:string):Promise<CapabilityStatus>
12
+ authorize(password:string,selectedScope?:string):Promise<CapabilityStatus>
9
13
  control(action:'start'|'stop'|'purge'):Promise<CapabilityStatus>
14
+ // Results of calls made elsewhere (the DSM window panel) on the same access.
15
+ subscribe?(listener:(status:CapabilityStatus)=>void):void
10
16
  }
11
17
  export const ExecutorSetupKey:InjectionKey<ExecutorSetup>=Symbol('executor-setup')
12
18
  export function useExecutorSetup():ExecutorSetup {
@@ -0,0 +1,100 @@
1
+ import {expect,test,vi} from 'vitest'
2
+ import {createBackgroundSaveFacade} from './facade'
3
+ test('destination lookup returns protected selection and rejects a grant outside its root',async()=>{
4
+ const selection={scope:'shared:reports',root:'/reports',generation:2}
5
+ const destination={id:'mine',key:'save',path:'/reports/exports',username:'alice',lifetime:'until-revoked'}
6
+ const call=vi.fn(async()=>({key:'save',selection,destination,status:'active'}))
7
+ const sdk=createBackgroundSaveFacade({call:call as any})
8
+ expect(await sdk.destination('save')).toEqual({key:'save',selection,destination,status:'active'})
9
+ expect(call).toHaveBeenCalledExactlyOnceWith('delegate.destination',{key:'save'})
10
+ call.mockResolvedValue({key:'save',selection,destination:{...destination,path:'/other'},status:'active'})
11
+ await expect(sdk.destination('save')).rejects.toMatchObject({code:'EXECUTOR_PROTOCOL'})
12
+ })
13
+ const ref={delegation_id:'mine',job_id:'original'}
14
+ test('operation cancellation keeps its bounded readable cause through the facade',async()=>{
15
+ const call=vi.fn(async()=>({job_id:'original',state:'canceled',code:'OPERATION_CANCELED',reason:'grant-revoked'}))
16
+ const sdk=createBackgroundSaveFacade({call:call as any})
17
+ expect(await sdk.cancel(ref)).toMatchObject({state:'canceled',reason:'grant-revoked'})
18
+ for(const reason of [123, 'x'.repeat(129), 'raw DSM response /path']){
19
+ call.mockResolvedValue({...await call(),reason} as any)
20
+ await expect(sdk.cancel(ref)).rejects.toMatchObject({code:'EXECUTOR_PROTOCOL'})
21
+ }
22
+ })
23
+ test('a copy cancel acknowledgement passes through unchanged for same-ref polling',async()=>{
24
+ const ack={job_id:'original',state:'copying',code:'CANCEL_REQUESTED',reason:'operation-canceled-by-user'}
25
+ const call=vi.fn(async()=>ack)
26
+ expect(await createBackgroundSaveFacade({call:call as any}).cancel(ref)).toEqual(ack)
27
+ expect(call).toHaveBeenCalledWith('delegate.operations.cancel',ref)
28
+ })
29
+ const grant={delegation_id:'mine',destination:'save',path:'/home/Exports',subject:{username:'normal1',uid:1205},lifetime:'until-revoked',revoked:false}
30
+ function fixture(){
31
+ const call=vi.fn(async(method:string)=>{
32
+ if(method==='delegate.status')return {state:'ready',disabled:false}
33
+ if(method==='delegate.list')return {delegations:[grant,{...grant,delegation_id:'revoked',revoked:true},{...grant,delegation_id:'legacy',lifetime:''}]}
34
+ if(method==='delegate.operations.get')return {job_id:'original',state:'committed',receipt:{path:'/home/Exports/report.csv',size:4}}
35
+ if(method==='delegate.operations.cancel')return {job_id:'original',state:'canceled'}
36
+ if(method==='delegate.revoke')return {revoked:true}
37
+ if(method==='delegate.disable')return {disabled:true}
38
+ if(method==='delegate.enable')return {disabled:false}
39
+ if(method==='delegate.select_destination')return {id:'mine',key:'save',path:'/home/Exports',username:'normal1',lifetime:'until-revoked'}
40
+ throw {code:'unknown_method'}
41
+ })
42
+ const jobs=vi.fn(async()=>[{...ref,name:'report.csv'}])
43
+ return {call,jobs,sdk:createBackgroundSaveFacade({call:call as any},jobs)}
44
+ }
45
+ test('W2 list is mapped to active permanent destinations without changing owner or legacy semantics',async()=>{
46
+ const {sdk,call}=fixture()
47
+ expect(await sdk.list()).toEqual([{id:'mine',key:'save',path:'/home/Exports',username:'normal1',lifetime:'until-revoked'}])
48
+ expect(call).toHaveBeenCalledExactlyOnceWith('delegate.list')
49
+ await sdk.selectDestination({key:'save',path:'/home/Exports'})
50
+ expect(call).toHaveBeenLastCalledWith('delegate.select_destination',{key:'save',path:'/home/Exports'})
51
+ })
52
+ test('App metadata is joined with protected status by the original two IDs, never a synthetic list route',async()=>{
53
+ const {sdk,call,jobs}=fixture()
54
+ expect(await sdk.operations()).toEqual([{...ref,name:'report.csv',state:'committed',receipt:{path:'/home/Exports/report.csv',size:4}}])
55
+ expect(jobs).toHaveBeenCalledOnce()
56
+ expect(call).toHaveBeenCalledExactlyOnceWith('delegate.operations.get',ref)
57
+ await sdk.cancel(ref);expect(call).toHaveBeenLastCalledWith('delegate.operations.cancel',ref)
58
+ await sdk.revoke({delegation_id:'mine'});expect(call).toHaveBeenLastCalledWith('delegate.revoke',{delegation_id:'mine'})
59
+ })
60
+ test('missing protected disabled status cannot be reported as enabled',async()=>{
61
+ const {sdk,call}=fixture();call.mockResolvedValue({state:'ready'} as any)
62
+ await expect(sdk.status()).rejects.toMatchObject({code:'EXECUTOR_PROTOCOL'})
63
+ })
64
+ test('malformed grant and App metadata references cannot be coerced into valid IDs',async()=>{
65
+ const {sdk,call}=fixture()
66
+ call.mockResolvedValue({delegations:[{...grant,delegation_id:undefined}]} as any)
67
+ await expect(sdk.list()).rejects.toMatchObject({code:'EXECUTOR_PROTOCOL'})
68
+ const invalid=createBackgroundSaveFacade({call:call as any},async()=>[{job_id:undefined,delegation_id:'mine',name:'report.csv'}] as any)
69
+ await expect(invalid.operations()).rejects.toMatchObject({code:'EXECUTOR_PROTOCOL'})
70
+ expect(call).toHaveBeenCalledTimes(1)
71
+ })
72
+ test('malformed or mismatched operation evidence never becomes Saved',async()=>{
73
+ for(const value of [{job_id:'other',state:'committed',receipt:{path:'/home/a',size:1}},{job_id:'original',state:'committed'},{job_id:'original',state:'copying',receipt:{path:'/home/a',size:1}},{job_id:'original',state:'committed',receipt:{path:'/home/a',size:-1}}]){
74
+ const {sdk,call}=fixture();call.mockResolvedValue(value as any)
75
+ await expect(sdk.operations()).rejects.toMatchObject({code:'EXECUTOR_PROTOCOL'})
76
+ }
77
+ })
78
+ test('read timeout never admits or resubmits an operation',async()=>{
79
+ const {sdk,call}=fixture();call.mockRejectedValue({code:'EXECUTOR_TIMEOUT'})
80
+ await expect(sdk.operations()).rejects.toMatchObject({code:'EXECUTOR_TIMEOUT'})
81
+ expect(call).toHaveBeenCalledExactlyOnceWith('delegate.operations.get',ref)
82
+ })
83
+ test('disable and enable use authenticated protected routes, not mailbox controls',async()=>{
84
+ const {sdk,call}=fixture()
85
+ expect(await sdk.status()).toEqual({disabled:false})
86
+ expect(await sdk.disable()).toEqual({disabled:true});expect(call).toHaveBeenLastCalledWith('delegate.disable')
87
+ expect(await sdk.enable()).toEqual({disabled:false});expect(call).toHaveBeenLastCalledWith('delegate.enable')
88
+ call.mockResolvedValue({} as any);await expect(sdk.disable()).rejects.toMatchObject({code:'EXECUTOR_PROTOCOL'})
89
+ })
90
+ test('the default facade lists backend mirrors but ignores their committed claim until live receipt',async()=>{
91
+ const {call}=fixture()
92
+ call.mockImplementation(async(method:string)=>{
93
+ if(method==='delegate.operations.list')return {operations:[{...ref,name:'report.csv',state:'committed'}]} as any
94
+ if(method==='delegate.operations.get')return {job_id:'original',state:'uncertain'} as any
95
+ throw {code:'unexpected'}
96
+ })
97
+ const sdk=createBackgroundSaveFacade({call:call as any})
98
+ expect(await sdk.operations()).toEqual([{...ref,name:'report.csv',state:'uncertain'}])
99
+ expect(call.mock.calls).toEqual([['delegate.operations.list'],['delegate.operations.get',ref]])
100
+ })