@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.
- package/CHANGELOG.md +846 -0
- package/README.md +81 -89
- package/dist/add.d.ts +4 -1
- package/dist/add.js +45 -17
- package/dist/build-spk/delegate-scope.d.ts +6 -0
- package/dist/build-spk/delegate-scope.js +34 -0
- package/dist/build-spk/dsm-arch.d.ts +7 -1
- package/dist/build-spk/dsm-arch.js +24 -28
- package/dist/build-spk/dsm-platforms.json +42 -0
- package/dist/build-spk/dsm-version.d.ts +11 -0
- package/dist/build-spk/dsm-version.js +17 -0
- package/dist/build-spk/gateway.js +8 -4
- package/dist/build-spk/generator.js +30 -5
- package/dist/build-spk/go-toolchain.d.ts +20 -0
- package/dist/build-spk/go-toolchain.js +44 -0
- package/dist/build-spk/notices.js +4 -1
- package/dist/build-spk/resource-worker.js +3 -0
- package/dist/build-spk/wizard-messages.d.ts +66 -0
- package/dist/build-spk/wizard-messages.js +75 -9
- package/dist/cli.js +28 -14
- package/dist/contract/schema-validate.js +8 -0
- package/dist/contract/validate.js +30 -0
- package/dist/deploy/deploy.d.ts +1 -1
- package/dist/deploy/deploy.js +8 -0
- package/dist/deploy/native-install-values.d.ts +17 -0
- package/dist/deploy/native-install-values.js +32 -0
- package/dist/deploy/plan.d.ts +6 -2
- package/dist/deploy/plan.js +58 -7
- package/dist/deploy/spk-version.js +1 -1
- package/dist/error-registry.js +54 -20
- package/dist/host/dsm-client.d.ts +8 -4
- package/dist/host/dsm-client.js +63 -21
- package/dist/host/dsm-http.js +24 -2
- package/dist/host/types.d.ts +3 -1
- package/dist/info.js +45 -10
- package/dist/init.d.ts +3 -0
- package/dist/init.js +50 -19
- package/dist/package-metadata.d.ts +21 -0
- package/dist/package-metadata.js +101 -0
- package/dist/portal/alias.d.ts +25 -0
- package/dist/portal/alias.js +48 -0
- package/dist/portal/package.d.ts +33 -0
- package/dist/portal/package.js +116 -0
- package/dist/runtime/delegate-contract.d.ts +79 -0
- package/dist/runtime/delegate-contract.js +27 -0
- package/dist/runtime/dsm-version.d.ts +6 -0
- package/dist/runtime/dsm-version.js +8 -0
- package/dist/runtime/executor-build.js +10 -6
- package/dist/runtime/executor-inspect.d.ts +3 -0
- package/dist/runtime/executor-inspect.js +11 -5
- package/dist/runtime/executor-policy.d.ts +12 -0
- package/dist/runtime/executor-policy.js +43 -3
- package/dist/runtime/executor-scope-task.d.ts +4 -0
- package/dist/runtime/executor-scope-task.js +21 -0
- package/dist/runtime/executor-tasks.d.ts +2 -1
- package/dist/runtime/executor-tasks.js +4 -4
- package/dist/skill.js +1 -0
- package/dist/standalone/api-permissions.d.ts +10 -0
- package/dist/standalone/api-permissions.js +19 -2
- package/dist/standalone/background.d.ts +16 -0
- package/dist/standalone/background.js +69 -2
- package/dist/standalone/binary.d.ts +1 -1
- package/dist/standalone/binary.js +22 -8
- package/dist/standalone/build.d.ts +1 -1
- package/dist/standalone/build.js +38 -8
- package/dist/standalone/daemon.d.ts +1 -0
- package/dist/standalone/daemon.js +10 -5
- package/dist/standalone/dsm-window.d.ts +3 -1
- package/dist/standalone/dsm-window.js +78 -49
- package/dist/standalone/inspect-spk.d.ts +8 -0
- package/dist/standalone/inspect-spk.js +110 -27
- package/dist/standalone/lifecycle.js +86 -8
- package/dist/standalone/manifest.d.ts +13 -0
- package/dist/standalone/manifest.js +57 -8
- package/dist/standalone/native-authorization-window.d.ts +1 -1
- package/dist/standalone/native-authorization-window.js +9 -1
- package/dist/standalone/native-layout.d.ts +3 -0
- package/dist/standalone/native-layout.js +37 -5
- package/dist/standalone/native-root-window.d.ts +4 -2
- package/dist/standalone/native-root-window.js +33 -18
- package/dist/standalone/portal-window.d.ts +21 -0
- package/dist/standalone/portal-window.js +155 -0
- package/dist/standalone/portal.d.ts +4 -0
- package/dist/standalone/portal.js +4 -0
- package/dist/standalone/resource.d.ts +5 -3
- package/dist/standalone/resource.js +6 -3
- package/dist/standalone/root-window.d.ts +4 -1
- package/dist/standalone/root-window.js +193 -36
- package/dist/standalone/spk.d.ts +1 -1
- package/dist/standalone/spk.js +25 -7
- package/dist/templates/_daemon/go.mod +1 -1
- package/dist/templates/_daemon/main.go +6 -3
- package/dist/templates/_daemon/main_test.go +82 -9
- package/dist/templates/_daemon/testdata/child.go +3 -0
- package/dist/templates/_executor/README.md +130 -16
- package/dist/templates/_executor/anchor.go +156 -0
- package/dist/templates/_executor/anchor_test.go +161 -0
- package/dist/templates/_executor/apppriv_review_test.go +1 -1
- package/dist/templates/_executor/cancel_contract_test.go +236 -0
- package/dist/templates/_executor/copy.go +64 -0
- package/dist/templates/_executor/copy_test.go +88 -0
- package/dist/templates/_executor/digest_review_test.go +2 -2
- package/dist/templates/_executor/dsm.go +63 -13
- package/dist/templates/_executor/dsm_test.go +95 -1
- package/dist/templates/_executor/executor.go +260 -49
- package/dist/templates/_executor/executor_test.go +36 -6
- package/dist/templates/_executor/go.mod +2 -0
- package/dist/templates/_executor/identity.go +27 -1
- package/dist/templates/_executor/identity_kernel_linux_test.go +60 -0
- package/dist/templates/_executor/identity_linux.go +82 -0
- package/dist/templates/_executor/identity_security_test.go +43 -0
- package/dist/templates/_executor/internal/lifecycle/lifecycle.go +5 -0
- package/dist/templates/_executor/internal/lifecycle/lifecycle_test.go +61 -3
- package/dist/templates/_executor/internal/taskscript/boot.sh +9 -9
- package/dist/templates/_executor/internal/taskscript/cmd/main.go +2 -2
- package/dist/templates/_executor/internal/taskscript/control.sh +15 -10
- package/dist/templates/_executor/internal/taskscript/tasks.go +5 -3
- package/dist/templates/_executor/lifecycle_test.go +11 -0
- package/dist/templates/_executor/link_linux.go +28 -0
- package/dist/templates/_executor/long_running_test.go +530 -0
- package/dist/templates/_executor/main.go +83 -25
- package/dist/templates/_executor/managed.go +3 -3
- package/dist/templates/_executor/managed_test.go +3 -3
- package/dist/templates/_executor/move_linux.go +490 -0
- package/dist/templates/_executor/move_linux_test.go +460 -0
- package/dist/templates/_executor/move_validation.go +46 -0
- package/dist/templates/_executor/move_validation_test.go +187 -0
- package/dist/templates/_executor/operations.go +926 -0
- package/dist/templates/_executor/path_test.go +18 -0
- package/dist/templates/_executor/pid.go +37 -0
- package/dist/templates/_executor/pid_test.go +113 -0
- package/dist/templates/_executor/platform_linux.go +24 -173
- package/dist/templates/_executor/platform_linux_test.go +9 -65
- package/dist/templates/_executor/platform_other.go +38 -4
- package/dist/templates/_executor/process.go +3 -0
- package/dist/templates/_executor/purge_log.go +18 -0
- package/dist/templates/_executor/records.go +242 -0
- package/dist/templates/_executor/scope_cancel_contract_test.go +91 -0
- package/dist/templates/_executor/scope_client.go +37 -0
- package/dist/templates/_executor/scope_contract.go +12 -0
- package/dist/templates/_executor/selected_scope.go +146 -0
- package/dist/templates/_executor/selected_scope_test.go +186 -0
- package/dist/templates/_executor/status_disabled_test.go +33 -0
- package/dist/templates/_executor/store.go +4 -3
- package/dist/templates/_executor/supervise.go +109 -0
- package/dist/templates/_executor/supervise_test.go +130 -0
- package/dist/templates/_executor/task_race_review_test.go +1 -1
- package/dist/templates/_executor/tasks.go +52 -15
- package/dist/templates/_executor/tasks_dsm71_test.go +114 -0
- package/dist/templates/_executor/tasks_review_test.go +1 -1
- package/dist/templates/_executor/tasks_test.go +62 -5
- package/dist/templates/_executor/upgrade_watch_test.go +13 -5
- package/dist/templates/_executor/watch.go +8 -0
- package/dist/templates/_executor/watch_test.go +23 -2
- package/dist/templates/_executor/write_review_linux_test.go +4 -12
- package/dist/templates/_gateway/go.mod +2 -0
- package/dist/templates/_shared/AGENTS.md +129 -0
- package/dist/templates/binary/dsm-package.yaml +24 -0
- package/dist/templates/binary/i18n/messages/de.json +4 -0
- package/dist/templates/binary/i18n/messages/en.json +4 -0
- package/dist/templates/binary/i18n/messages/es.json +4 -0
- package/dist/templates/binary/i18n/messages/fr.json +4 -0
- package/dist/templates/binary/i18n/messages/it.json +4 -0
- package/dist/templates/binary/i18n/messages/ja.json +4 -0
- package/dist/templates/binary/i18n/messages/ko.json +4 -0
- package/dist/templates/binary/i18n/messages/zh-CN.json +4 -0
- package/dist/templates/binary/i18n/messages/zh-TW.json +4 -0
- package/dist/templates/binary/ui/app.js +19 -4
- package/dist/templates/native-authorization/AuthorizationGate.vue +5 -3
- package/dist/templates/native-authorization/i18n/de.json +1 -0
- package/dist/templates/native-authorization/i18n/en.json +1 -0
- package/dist/templates/native-authorization/i18n/es.json +2 -1
- package/dist/templates/native-authorization/i18n/fr.json +1 -0
- package/dist/templates/native-authorization/i18n/it.json +11 -10
- package/dist/templates/native-authorization/i18n/ja.json +1 -0
- package/dist/templates/native-authorization/i18n/ko.json +10 -9
- package/dist/templates/native-authorization/i18n/zh-CN.json +6 -5
- package/dist/templates/native-authorization/i18n/zh-TW.json +6 -5
- package/dist/templates/native-go/README.md +43 -10
- package/dist/templates/native-go/backend/cmd/app/main.go +27 -7
- package/dist/templates/native-go/backend/go.mod +1 -1
- package/dist/templates/native-go/backend/internal/api/entry_test.go +47 -0
- package/dist/templates/native-go/backend/internal/api/permissions_test.go +3 -3
- package/dist/templates/native-go/backend/internal/api/registry.go +11 -0
- package/dist/templates/native-go/backend/internal/api/sdk-permissions.json +14 -1
- package/dist/templates/native-go/backend/internal/api/server.go +18 -5
- package/dist/templates/native-go/backend/internal/app/delegate_config.go +57 -0
- package/dist/templates/native-go/backend/internal/app/delegate_config_test.go +28 -0
- package/dist/templates/native-go/backend/internal/app/portal.go +30 -0
- package/dist/templates/native-go/backend/internal/app/portal_test.go +55 -0
- package/dist/templates/native-go/backend/internal/app/routes.go +8 -2
- package/dist/templates/native-go/backend/internal/background/runner.go +1 -1
- package/dist/templates/native-go/backend/internal/background/store.go +16 -14
- package/dist/templates/native-go/backend/internal/dsm/admin.go +8 -2
- package/dist/templates/native-go/backend/internal/dsm/admin_linux.go +43 -13
- package/dist/templates/native-go/backend/internal/dsm/admin_linux_test.go +56 -0
- package/dist/templates/native-go/backend/internal/dsm/admin_other.go +4 -1
- package/dist/templates/native-go/backend/internal/dsm/apppriv.go +30 -0
- package/dist/templates/native-go/backend/internal/dsm/bridge_file.go +75 -0
- package/dist/templates/native-go/backend/internal/dsm/bridge_file_test.go +57 -0
- package/dist/templates/native-go/backend/internal/dsm/entry.go +177 -0
- package/dist/templates/native-go/backend/internal/dsm/entry_test.go +315 -0
- package/dist/templates/native-go/backend/internal/dsm/native/admin-armv7.elf +0 -0
- package/dist/templates/native-go/backend/internal/dsm/native/apppriv-aarch64.elf +0 -0
- package/dist/templates/native-go/backend/internal/dsm/native/apppriv-armv7.elf +0 -0
- package/dist/templates/native-go/backend/internal/dsm/native/apppriv-x86_64.elf +0 -0
- package/dist/templates/native-go/backend/internal/dsm/native/apppriv_check.c +33 -0
- package/dist/templates/native-go/backend/internal/dsm/native/build.sh +13 -4
- package/dist/templates/native-go/backend/internal/dsm/native/provenance.json +9 -3
- package/dist/templates/native-go/backend/internal/filestation/check_permission.go +71 -0
- package/dist/templates/native-go/backend/internal/filestation/check_permission_test.go +77 -0
- package/dist/templates/native-go/backend/internal/filestation/client.go +39 -3
- package/dist/templates/native-go/backend/internal/filestation/download_dsm71_test.go +82 -0
- package/dist/templates/native-go/backend/internal/filestation/sdk_policy_test.go +39 -0
- package/dist/templates/native-go/backend/internal/filestation/service.go +1 -1
- package/dist/templates/native-go/backend/internal/identity/README.md +5 -1
- package/dist/templates/native-go/backend/internal/identity/dsm.go +24 -5
- package/dist/templates/native-go/backend/internal/identity/http.go +1 -1
- package/dist/templates/native-go/backend/internal/identity/identity_test.go +48 -1
- package/dist/templates/native-go/backend/internal/identity/service.go +1 -1
- package/dist/templates/native-go/backend/internal/portal/portal.go +138 -0
- package/dist/templates/native-go/backend/internal/portal/portal_test.go +89 -0
- package/dist/templates/native-go/dsm-package.yaml +20 -1
- package/dist/templates/native-go/i18n/glossary.json +2 -1
- package/dist/templates/native-go/i18n/messages/de.json +1 -0
- package/dist/templates/native-go/i18n/messages/en.json +1 -0
- package/dist/templates/native-go/i18n/messages/es.json +1 -0
- package/dist/templates/native-go/i18n/messages/fr.json +1 -0
- package/dist/templates/native-go/i18n/messages/it.json +1 -0
- package/dist/templates/native-go/i18n/messages/ja.json +1 -0
- package/dist/templates/native-go/i18n/messages/ko.json +1 -0
- package/dist/templates/native-go/i18n/messages/zh-CN.json +1 -0
- package/dist/templates/native-go/i18n/messages/zh-TW.json +1 -0
- package/dist/templates/native-go/ui/app.css +10 -0
- package/dist/templates/native-go/ui/app.js +11 -1
- package/dist/templates/native-go-delegate/DELEGATE.md +126 -5
- package/dist/templates/native-go-delegate/backend/cmd/background-worker/main.go +7 -0
- package/dist/templates/native-go-delegate/backend/internal/app/delegate.go +11 -1
- package/dist/templates/native-go-delegate/backend/internal/app/delegate_routes_test.go +14 -0
- package/dist/templates/native-go-delegate/backend/internal/background/delegated_moves.go +270 -0
- package/dist/templates/native-go-delegate/backend/internal/delegate/admission_recovery_test.go +410 -0
- package/dist/templates/native-go-delegate/backend/internal/delegate/cancel_contract_test.go +95 -0
- package/dist/templates/native-go-delegate/backend/internal/delegate/client.go +8 -0
- package/dist/templates/native-go-delegate/backend/internal/delegate/destination.go +110 -0
- package/dist/templates/native-go-delegate/backend/internal/delegate/destination_state.go +117 -0
- package/dist/templates/native-go-delegate/backend/internal/delegate/destination_state_test.go +60 -0
- package/dist/templates/native-go-delegate/backend/internal/delegate/destination_test.go +116 -0
- package/dist/templates/native-go-delegate/backend/internal/delegate/example.go +81 -0
- package/dist/templates/native-go-delegate/backend/internal/delegate/example_test.go +91 -0
- package/dist/templates/native-go-delegate/backend/internal/delegate/executor_contract_test.go +511 -0
- package/dist/templates/native-go-delegate/backend/internal/delegate/files.go +1 -1
- package/dist/templates/native-go-delegate/backend/internal/delegate/long_running_routes.go +92 -0
- package/dist/templates/native-go-delegate/backend/internal/delegate/long_running_routes_test.go +99 -0
- package/dist/templates/native-go-delegate/backend/internal/delegate/move.go +348 -0
- package/dist/templates/native-go-delegate/backend/internal/delegate/move_test.go +351 -0
- package/dist/templates/native-go-delegate/backend/internal/delegate/operations_list.go +65 -0
- package/dist/templates/native-go-delegate/backend/internal/delegate/operations_list_test.go +114 -0
- package/dist/templates/native-go-delegate/backend/internal/delegate/retry_test.go +1 -1
- package/dist/templates/native-go-delegate/backend/internal/delegate/service.go +58 -17
- package/dist/templates/native-go-delegate/backend/internal/delegate/staging.go +121 -0
- package/dist/templates/native-go-delegate/backend/internal/delegate/status_disabled_test.go +51 -0
- package/dist/templates/native-go-delegate/backend/internal/delegate/testdata/executor_contract.txt +112 -0
- package/dist/templates/native-go-delegate/backend/internal/delegate/transient_contract_test.go +137 -0
- package/dist/templates/native-go-delegate/backend/internal/filestation/files.go +1 -31
- package/dist/templates/native-go-delegate/frontend/src/CapabilityPanels.vue +30 -3
- package/dist/templates/native-go-delegate/frontend/src/delegate/BackgroundSavePanel.vue +113 -0
- package/dist/templates/native-go-delegate/frontend/src/delegate/copy.ts +5 -6
- package/dist/templates/native-go-delegate/frontend/test/background-save.test.ts +121 -0
- package/dist/templates/native-go-delegate/frontend/test/delegate.test.ts +15 -0
- package/dist/templates/native-go-delegate/frontend/test/scope-settings.test.ts +49 -0
- package/dist/templates/native-go-delegate/test.sh +1 -0
- package/dist/templates/native-go-jobs/frontend/src/background/i18n/de.json +2 -2
- package/dist/templates/native-go-jobs/frontend/src/background/i18n/en.json +2 -2
- package/dist/templates/native-go-jobs/frontend/src/background/i18n/es.json +12 -12
- package/dist/templates/native-go-jobs/frontend/src/background/i18n/fr.json +4 -4
- package/dist/templates/native-go-jobs/frontend/src/background/i18n/it.json +17 -17
- package/dist/templates/native-go-jobs/frontend/src/background/i18n/ja.json +7 -7
- package/dist/templates/native-go-jobs/frontend/src/background/i18n/ko.json +15 -15
- package/dist/templates/native-go-jobs/frontend/src/background/i18n/zh-CN.json +3 -3
- package/dist/templates/native-go-jobs/frontend/src/background/i18n/zh-TW.json +2 -2
- package/dist/templates/native-go-jobs/frontend/test/background.test.ts +25 -28
- package/dist/templates/native-go-root-ops/backend/internal/app/executor.go +30 -2
- package/dist/templates/native-go-root-ops/backend/internal/app/executor_test.go +64 -0
- package/dist/templates/native-go-vue/README.md +4 -0
- package/dist/templates/native-go-vue/frontend/package-lock.json +4 -4
- package/dist/templates/native-go-vue/frontend/package.json +1 -1
- package/dist/templates/native-go-vue/frontend/src/App.vue +18 -13
- package/dist/templates/native-go-vue/frontend/src/context.ts +31 -0
- package/dist/templates/native-go-vue/frontend/src/delegate/ExecutorSettings.vue +54 -9
- package/dist/templates/native-go-vue/frontend/src/delegate/catalogs.ts +11 -0
- package/dist/templates/native-go-vue/frontend/src/delegate/copy.test.ts +12 -0
- package/dist/templates/native-go-vue/frontend/src/delegate/copy.ts +4 -4
- package/dist/templates/native-go-vue/frontend/src/delegate/executor-settings.test.ts +23 -0
- package/dist/templates/native-go-vue/frontend/src/delegate/executor-setup.ts +8 -2
- package/dist/templates/native-go-vue/frontend/src/delegate/facade.test.ts +100 -0
- package/dist/templates/native-go-vue/frontend/src/delegate/facade.ts +85 -0
- package/dist/templates/native-go-vue/frontend/src/delegate/i18n/de.json +47 -0
- package/dist/templates/native-go-vue/frontend/src/delegate/i18n/en.json +13 -2
- package/dist/templates/native-go-vue/frontend/src/delegate/i18n/es.json +47 -0
- package/dist/templates/native-go-vue/frontend/src/delegate/i18n/fr.json +47 -0
- package/dist/templates/native-go-vue/frontend/src/delegate/i18n/it.json +47 -0
- package/dist/templates/native-go-vue/frontend/src/delegate/i18n/ja.json +47 -0
- package/dist/templates/native-go-vue/frontend/src/delegate/i18n/ko.json +47 -0
- package/dist/templates/native-go-vue/frontend/src/delegate/i18n/save/de.json +42 -0
- package/dist/templates/native-go-vue/frontend/src/delegate/i18n/save/en.json +42 -0
- package/dist/templates/native-go-vue/frontend/src/delegate/i18n/save/es.json +42 -0
- package/dist/templates/native-go-vue/frontend/src/delegate/i18n/save/fr.json +42 -0
- package/dist/templates/native-go-vue/frontend/src/delegate/i18n/save/it.json +42 -0
- package/dist/templates/native-go-vue/frontend/src/delegate/i18n/save/ja.json +42 -0
- package/dist/templates/native-go-vue/frontend/src/delegate/i18n/save/ko.json +42 -0
- package/dist/templates/native-go-vue/frontend/src/delegate/i18n/save/zh-CN.json +42 -0
- package/dist/templates/native-go-vue/frontend/src/delegate/i18n/save/zh-TW.json +42 -0
- package/dist/templates/native-go-vue/frontend/src/delegate/i18n/zh-CN.json +47 -0
- package/dist/templates/native-go-vue/frontend/src/delegate/i18n/zh-TW.json +16 -5
- package/dist/templates/native-go-vue/frontend/src/delegate/long-running.ts +57 -0
- package/dist/templates/native-go-vue/frontend/src/delegate/save-copy.test.ts +12 -0
- package/dist/templates/native-go-vue/frontend/src/delegate/save-copy.ts +24 -0
- package/dist/templates/native-go-vue/frontend/src/delegate/scoped-picker.test.ts +12 -0
- package/dist/templates/native-go-vue/frontend/src/delegate/scoped-picker.ts +21 -0
- package/dist/templates/native-go-vue/frontend/src/files/files.test.ts +7 -0
- package/dist/templates/native-go-vue/frontend/src/i18n/de.json +20 -0
- package/dist/templates/native-go-vue/frontend/src/i18n/en.json +20 -0
- package/dist/templates/native-go-vue/frontend/src/i18n/es.json +20 -0
- package/dist/templates/native-go-vue/frontend/src/i18n/fr.json +20 -0
- package/dist/templates/native-go-vue/frontend/src/i18n/it.json +20 -0
- package/dist/templates/native-go-vue/frontend/src/i18n/ja.json +20 -0
- package/dist/templates/native-go-vue/frontend/src/i18n/ko.json +20 -0
- package/dist/templates/native-go-vue/frontend/src/i18n/zh-CN.json +20 -0
- package/dist/templates/native-go-vue/frontend/src/i18n/zh-TW.json +20 -0
- package/dist/templates/native-go-vue/frontend/src/i18n.ts +13 -0
- package/dist/templates/native-go-vue/frontend/src/identity/LoginPage.vue +16 -14
- package/dist/templates/native-go-vue/frontend/src/identity/i18n/de.json +27 -0
- package/dist/templates/native-go-vue/frontend/src/identity/i18n/en.json +27 -0
- package/dist/templates/native-go-vue/frontend/src/identity/i18n/es.json +27 -0
- package/dist/templates/native-go-vue/frontend/src/identity/i18n/fr.json +27 -0
- package/dist/templates/native-go-vue/frontend/src/identity/i18n/it.json +27 -0
- package/dist/templates/native-go-vue/frontend/src/identity/i18n/ja.json +27 -0
- package/dist/templates/native-go-vue/frontend/src/identity/i18n/ko.json +27 -0
- package/dist/templates/native-go-vue/frontend/src/identity/i18n/zh-CN.json +27 -0
- package/dist/templates/native-go-vue/frontend/src/identity/i18n/zh-TW.json +27 -0
- package/dist/templates/native-go-vue/frontend/src/identity/i18n.ts +14 -0
- package/dist/templates/native-go-vue/frontend/src/identity/session.test.ts +4 -0
- package/dist/templates/native-go-vue/frontend/src/identity/session.ts +9 -8
- package/dist/templates/native-go-vue/frontend/src/nas/NasPicker.vue +31 -12
- package/dist/templates/native-go-vue/frontend/src/nas/destination.test.ts +48 -0
- package/dist/templates/native-go-vue/frontend/src/nas/i18n/files/de.json +1 -1
- package/dist/templates/native-go-vue/frontend/src/nas/i18n/files/en.json +1 -1
- package/dist/templates/native-go-vue/frontend/src/nas/i18n/files/es.json +10 -10
- package/dist/templates/native-go-vue/frontend/src/nas/i18n/files/fr.json +1 -1
- package/dist/templates/native-go-vue/frontend/src/nas/i18n/files/it.json +12 -12
- package/dist/templates/native-go-vue/frontend/src/nas/i18n/files/ja.json +15 -15
- package/dist/templates/native-go-vue/frontend/src/nas/i18n/files/ko.json +12 -12
- package/dist/templates/native-go-vue/frontend/src/nas/i18n/files/zh-CN.json +1 -1
- package/dist/templates/native-go-vue/frontend/src/nas/i18n/files/zh-TW.json +1 -1
- package/dist/templates/native-go-vue/frontend/src/portal/PortalSettings.vue +39 -0
- package/dist/templates/native-go-vue/frontend/src/shell/i18n/fr.json +1 -1
- package/dist/templates/native-go-vue/frontend/src/shell/i18n/ja.json +1 -1
- package/dist/templates/native-go-vue/frontend/test/apps.test.ts +14 -0
- package/dist/templates/native-go-vue/frontend/test/portal-settings.test.ts +71 -0
- package/dist/templates/native-go-vue/frontend/test/shell-i18n.test.ts +50 -0
- package/package.json +5 -1
- package/schema/package-metadata.schema.json +101 -0
- package/schema/synology-app.schema.json +112 -0
- package/skills/SKILL.md +106 -4
- package/skills/reference/create.md +101 -2
- package/skills/reference/deploy.md +2 -2
- package/skills/reference/files.md +113 -2
- package/skills/reference/identity.md +70 -1
- package/skills/reference/portal.md +17 -0
- package/skills/reference/ui-vue.md +12 -0
- package/templates/_daemon/go.mod +1 -1
- package/templates/_daemon/main.go +6 -3
- package/templates/_daemon/main_test.go +82 -9
- package/templates/_daemon/testdata/child.go +3 -0
- package/templates/_executor/README.md +130 -16
- package/templates/_executor/anchor.go +156 -0
- package/templates/_executor/anchor_test.go +161 -0
- package/templates/_executor/apppriv_review_test.go +1 -1
- package/templates/_executor/cancel_contract_test.go +236 -0
- package/templates/_executor/copy.go +64 -0
- package/templates/_executor/copy_test.go +88 -0
- package/templates/_executor/digest_review_test.go +2 -2
- package/templates/_executor/dsm.go +63 -13
- package/templates/_executor/dsm_test.go +95 -1
- package/templates/_executor/executor.go +260 -49
- package/templates/_executor/executor_test.go +36 -6
- package/templates/_executor/go.mod +2 -0
- package/templates/_executor/identity.go +27 -1
- package/templates/_executor/identity_kernel_linux_test.go +60 -0
- package/templates/_executor/identity_linux.go +82 -0
- package/templates/_executor/identity_security_test.go +43 -0
- package/templates/_executor/internal/lifecycle/lifecycle.go +5 -0
- package/templates/_executor/internal/lifecycle/lifecycle_test.go +61 -3
- package/templates/_executor/internal/taskscript/boot.sh +9 -9
- package/templates/_executor/internal/taskscript/cmd/main.go +2 -2
- package/templates/_executor/internal/taskscript/control.sh +15 -10
- package/templates/_executor/internal/taskscript/tasks.go +5 -3
- package/templates/_executor/lifecycle_test.go +11 -0
- package/templates/_executor/link_linux.go +28 -0
- package/templates/_executor/long_running_test.go +530 -0
- package/templates/_executor/main.go +83 -25
- package/templates/_executor/managed.go +3 -3
- package/templates/_executor/managed_test.go +3 -3
- package/templates/_executor/move_linux.go +490 -0
- package/templates/_executor/move_linux_test.go +460 -0
- package/templates/_executor/move_validation.go +46 -0
- package/templates/_executor/move_validation_test.go +187 -0
- package/templates/_executor/operations.go +926 -0
- package/templates/_executor/path_test.go +18 -0
- package/templates/_executor/pid.go +37 -0
- package/templates/_executor/pid_test.go +113 -0
- package/templates/_executor/platform_linux.go +24 -173
- package/templates/_executor/platform_linux_test.go +9 -65
- package/templates/_executor/platform_other.go +38 -4
- package/templates/_executor/process.go +3 -0
- package/templates/_executor/purge_log.go +18 -0
- package/templates/_executor/records.go +242 -0
- package/templates/_executor/scope_cancel_contract_test.go +91 -0
- package/templates/_executor/scope_client.go +37 -0
- package/templates/_executor/scope_contract.go +12 -0
- package/templates/_executor/selected_scope.go +146 -0
- package/templates/_executor/selected_scope_test.go +186 -0
- package/templates/_executor/status_disabled_test.go +33 -0
- package/templates/_executor/store.go +4 -3
- package/templates/_executor/supervise.go +109 -0
- package/templates/_executor/supervise_test.go +130 -0
- package/templates/_executor/task_race_review_test.go +1 -1
- package/templates/_executor/tasks.go +52 -15
- package/templates/_executor/tasks_dsm71_test.go +114 -0
- package/templates/_executor/tasks_review_test.go +1 -1
- package/templates/_executor/tasks_test.go +62 -5
- package/templates/_executor/upgrade_watch_test.go +13 -5
- package/templates/_executor/watch.go +8 -0
- package/templates/_executor/watch_test.go +23 -2
- package/templates/_executor/write_review_linux_test.go +4 -12
- package/templates/_gateway/go.mod +2 -0
- package/templates/_shared/AGENTS.md +129 -0
- package/templates/binary/dsm-package.yaml +24 -0
- package/templates/binary/i18n/messages/de.json +4 -0
- package/templates/binary/i18n/messages/en.json +4 -0
- package/templates/binary/i18n/messages/es.json +4 -0
- package/templates/binary/i18n/messages/fr.json +4 -0
- package/templates/binary/i18n/messages/it.json +4 -0
- package/templates/binary/i18n/messages/ja.json +4 -0
- package/templates/binary/i18n/messages/ko.json +4 -0
- package/templates/binary/i18n/messages/zh-CN.json +4 -0
- package/templates/binary/i18n/messages/zh-TW.json +4 -0
- package/templates/binary/ui/app.js +19 -4
- package/templates/native-authorization/AuthorizationGate.vue +5 -3
- package/templates/native-authorization/i18n/de.json +1 -0
- package/templates/native-authorization/i18n/en.json +1 -0
- package/templates/native-authorization/i18n/es.json +2 -1
- package/templates/native-authorization/i18n/fr.json +1 -0
- package/templates/native-authorization/i18n/it.json +11 -10
- package/templates/native-authorization/i18n/ja.json +1 -0
- package/templates/native-authorization/i18n/ko.json +10 -9
- package/templates/native-authorization/i18n/zh-CN.json +6 -5
- package/templates/native-authorization/i18n/zh-TW.json +6 -5
- package/templates/native-go/README.md +43 -10
- package/templates/native-go/backend/cmd/app/main.go +27 -7
- package/templates/native-go/backend/go.mod +1 -1
- package/templates/native-go/backend/internal/api/entry_test.go +47 -0
- package/templates/native-go/backend/internal/api/permissions_test.go +3 -3
- package/templates/native-go/backend/internal/api/registry.go +11 -0
- package/templates/native-go/backend/internal/api/sdk-permissions.json +14 -1
- package/templates/native-go/backend/internal/api/server.go +18 -5
- package/templates/native-go/backend/internal/app/delegate_config.go +57 -0
- package/templates/native-go/backend/internal/app/delegate_config_test.go +28 -0
- package/templates/native-go/backend/internal/app/portal.go +30 -0
- package/templates/native-go/backend/internal/app/portal_test.go +55 -0
- package/templates/native-go/backend/internal/app/routes.go +8 -2
- package/templates/native-go/backend/internal/background/runner.go +1 -1
- package/templates/native-go/backend/internal/background/store.go +16 -14
- package/templates/native-go/backend/internal/dsm/admin.go +8 -2
- package/templates/native-go/backend/internal/dsm/admin_linux.go +43 -13
- package/templates/native-go/backend/internal/dsm/admin_linux_test.go +56 -0
- package/templates/native-go/backend/internal/dsm/admin_other.go +4 -1
- package/templates/native-go/backend/internal/dsm/apppriv.go +30 -0
- package/templates/native-go/backend/internal/dsm/bridge_file.go +75 -0
- package/templates/native-go/backend/internal/dsm/bridge_file_test.go +57 -0
- package/templates/native-go/backend/internal/dsm/entry.go +177 -0
- package/templates/native-go/backend/internal/dsm/entry_test.go +315 -0
- package/templates/native-go/backend/internal/dsm/native/admin-armv7.elf +0 -0
- package/templates/native-go/backend/internal/dsm/native/apppriv-aarch64.elf +0 -0
- package/templates/native-go/backend/internal/dsm/native/apppriv-armv7.elf +0 -0
- package/templates/native-go/backend/internal/dsm/native/apppriv-x86_64.elf +0 -0
- package/templates/native-go/backend/internal/dsm/native/apppriv_check.c +33 -0
- package/templates/native-go/backend/internal/dsm/native/build.sh +13 -4
- package/templates/native-go/backend/internal/dsm/native/provenance.json +9 -3
- package/templates/native-go/backend/internal/filestation/check_permission.go +71 -0
- package/templates/native-go/backend/internal/filestation/check_permission_test.go +77 -0
- package/templates/native-go/backend/internal/filestation/client.go +39 -3
- package/templates/native-go/backend/internal/filestation/download_dsm71_test.go +82 -0
- package/templates/native-go/backend/internal/filestation/sdk_policy_test.go +39 -0
- package/templates/native-go/backend/internal/filestation/service.go +1 -1
- package/templates/native-go/backend/internal/identity/README.md +5 -1
- package/templates/native-go/backend/internal/identity/dsm.go +24 -5
- package/templates/native-go/backend/internal/identity/http.go +1 -1
- package/templates/native-go/backend/internal/identity/identity_test.go +48 -1
- package/templates/native-go/backend/internal/identity/service.go +1 -1
- package/templates/native-go/backend/internal/portal/portal.go +138 -0
- package/templates/native-go/backend/internal/portal/portal_test.go +89 -0
- package/templates/native-go/dsm-package.yaml +20 -1
- package/templates/native-go/i18n/glossary.json +2 -1
- package/templates/native-go/i18n/messages/de.json +1 -0
- package/templates/native-go/i18n/messages/en.json +1 -0
- package/templates/native-go/i18n/messages/es.json +1 -0
- package/templates/native-go/i18n/messages/fr.json +1 -0
- package/templates/native-go/i18n/messages/it.json +1 -0
- package/templates/native-go/i18n/messages/ja.json +1 -0
- package/templates/native-go/i18n/messages/ko.json +1 -0
- package/templates/native-go/i18n/messages/zh-CN.json +1 -0
- package/templates/native-go/i18n/messages/zh-TW.json +1 -0
- package/templates/native-go/ui/app.css +10 -0
- package/templates/native-go/ui/app.js +11 -1
- package/templates/native-go-delegate/DELEGATE.md +126 -5
- package/templates/native-go-delegate/backend/cmd/background-worker/main.go +7 -0
- package/templates/native-go-delegate/backend/internal/app/delegate.go +11 -1
- package/templates/native-go-delegate/backend/internal/app/delegate_routes_test.go +14 -0
- package/templates/native-go-delegate/backend/internal/background/delegated_moves.go +270 -0
- package/templates/native-go-delegate/backend/internal/delegate/admission_recovery_test.go +410 -0
- package/templates/native-go-delegate/backend/internal/delegate/cancel_contract_test.go +95 -0
- package/templates/native-go-delegate/backend/internal/delegate/client.go +8 -0
- package/templates/native-go-delegate/backend/internal/delegate/destination.go +110 -0
- package/templates/native-go-delegate/backend/internal/delegate/destination_state.go +117 -0
- package/templates/native-go-delegate/backend/internal/delegate/destination_state_test.go +60 -0
- package/templates/native-go-delegate/backend/internal/delegate/destination_test.go +116 -0
- package/templates/native-go-delegate/backend/internal/delegate/example.go +81 -0
- package/templates/native-go-delegate/backend/internal/delegate/example_test.go +91 -0
- package/templates/native-go-delegate/backend/internal/delegate/executor_contract_test.go +511 -0
- package/templates/native-go-delegate/backend/internal/delegate/files.go +1 -1
- package/templates/native-go-delegate/backend/internal/delegate/long_running_routes.go +92 -0
- package/templates/native-go-delegate/backend/internal/delegate/long_running_routes_test.go +99 -0
- package/templates/native-go-delegate/backend/internal/delegate/move.go +348 -0
- package/templates/native-go-delegate/backend/internal/delegate/move_test.go +351 -0
- package/templates/native-go-delegate/backend/internal/delegate/operations_list.go +65 -0
- package/templates/native-go-delegate/backend/internal/delegate/operations_list_test.go +114 -0
- package/templates/native-go-delegate/backend/internal/delegate/retry_test.go +1 -1
- package/templates/native-go-delegate/backend/internal/delegate/service.go +58 -17
- package/templates/native-go-delegate/backend/internal/delegate/staging.go +121 -0
- package/templates/native-go-delegate/backend/internal/delegate/status_disabled_test.go +51 -0
- package/templates/native-go-delegate/backend/internal/delegate/testdata/executor_contract.txt +112 -0
- package/templates/native-go-delegate/backend/internal/delegate/transient_contract_test.go +137 -0
- package/templates/native-go-delegate/backend/internal/filestation/files.go +1 -31
- package/templates/native-go-delegate/frontend/src/CapabilityPanels.vue +30 -3
- package/templates/native-go-delegate/frontend/src/delegate/BackgroundSavePanel.vue +113 -0
- package/templates/native-go-delegate/frontend/src/delegate/copy.ts +5 -6
- package/templates/native-go-delegate/frontend/test/background-save.test.ts +121 -0
- package/templates/native-go-delegate/frontend/test/delegate.test.ts +15 -0
- package/templates/native-go-delegate/frontend/test/scope-settings.test.ts +49 -0
- package/templates/native-go-delegate/test.sh +1 -0
- package/templates/native-go-jobs/frontend/src/background/i18n/de.json +2 -2
- package/templates/native-go-jobs/frontend/src/background/i18n/en.json +2 -2
- package/templates/native-go-jobs/frontend/src/background/i18n/es.json +12 -12
- package/templates/native-go-jobs/frontend/src/background/i18n/fr.json +4 -4
- package/templates/native-go-jobs/frontend/src/background/i18n/it.json +17 -17
- package/templates/native-go-jobs/frontend/src/background/i18n/ja.json +7 -7
- package/templates/native-go-jobs/frontend/src/background/i18n/ko.json +15 -15
- package/templates/native-go-jobs/frontend/src/background/i18n/zh-CN.json +3 -3
- package/templates/native-go-jobs/frontend/src/background/i18n/zh-TW.json +2 -2
- package/templates/native-go-jobs/frontend/test/background.test.ts +25 -28
- package/templates/native-go-root-ops/backend/internal/app/executor.go +30 -2
- package/templates/native-go-root-ops/backend/internal/app/executor_test.go +64 -0
- package/templates/native-go-vue/README.md +4 -0
- package/templates/native-go-vue/frontend/package-lock.json +4 -4
- package/templates/native-go-vue/frontend/package.json +1 -1
- package/templates/native-go-vue/frontend/src/App.vue +18 -13
- package/templates/native-go-vue/frontend/src/context.ts +31 -0
- package/templates/native-go-vue/frontend/src/delegate/ExecutorSettings.vue +54 -9
- package/templates/native-go-vue/frontend/src/delegate/catalogs.ts +11 -0
- package/templates/native-go-vue/frontend/src/delegate/copy.test.ts +12 -0
- package/templates/native-go-vue/frontend/src/delegate/copy.ts +4 -4
- package/templates/native-go-vue/frontend/src/delegate/executor-settings.test.ts +23 -0
- package/templates/native-go-vue/frontend/src/delegate/executor-setup.ts +8 -2
- package/templates/native-go-vue/frontend/src/delegate/facade.test.ts +100 -0
- package/templates/native-go-vue/frontend/src/delegate/facade.ts +85 -0
- package/templates/native-go-vue/frontend/src/delegate/i18n/de.json +47 -0
- package/templates/native-go-vue/frontend/src/delegate/i18n/en.json +13 -2
- package/templates/native-go-vue/frontend/src/delegate/i18n/es.json +47 -0
- package/templates/native-go-vue/frontend/src/delegate/i18n/fr.json +47 -0
- package/templates/native-go-vue/frontend/src/delegate/i18n/it.json +47 -0
- package/templates/native-go-vue/frontend/src/delegate/i18n/ja.json +47 -0
- package/templates/native-go-vue/frontend/src/delegate/i18n/ko.json +47 -0
- package/templates/native-go-vue/frontend/src/delegate/i18n/save/de.json +42 -0
- package/templates/native-go-vue/frontend/src/delegate/i18n/save/en.json +42 -0
- package/templates/native-go-vue/frontend/src/delegate/i18n/save/es.json +42 -0
- package/templates/native-go-vue/frontend/src/delegate/i18n/save/fr.json +42 -0
- package/templates/native-go-vue/frontend/src/delegate/i18n/save/it.json +42 -0
- package/templates/native-go-vue/frontend/src/delegate/i18n/save/ja.json +42 -0
- package/templates/native-go-vue/frontend/src/delegate/i18n/save/ko.json +42 -0
- package/templates/native-go-vue/frontend/src/delegate/i18n/save/zh-CN.json +42 -0
- package/templates/native-go-vue/frontend/src/delegate/i18n/save/zh-TW.json +42 -0
- package/templates/native-go-vue/frontend/src/delegate/i18n/zh-CN.json +47 -0
- package/templates/native-go-vue/frontend/src/delegate/i18n/zh-TW.json +16 -5
- package/templates/native-go-vue/frontend/src/delegate/long-running.ts +57 -0
- package/templates/native-go-vue/frontend/src/delegate/save-copy.test.ts +12 -0
- package/templates/native-go-vue/frontend/src/delegate/save-copy.ts +24 -0
- package/templates/native-go-vue/frontend/src/delegate/scoped-picker.test.ts +12 -0
- package/templates/native-go-vue/frontend/src/delegate/scoped-picker.ts +21 -0
- package/templates/native-go-vue/frontend/src/files/files.test.ts +7 -0
- package/templates/native-go-vue/frontend/src/i18n/de.json +20 -0
- package/templates/native-go-vue/frontend/src/i18n/en.json +20 -0
- package/templates/native-go-vue/frontend/src/i18n/es.json +20 -0
- package/templates/native-go-vue/frontend/src/i18n/fr.json +20 -0
- package/templates/native-go-vue/frontend/src/i18n/it.json +20 -0
- package/templates/native-go-vue/frontend/src/i18n/ja.json +20 -0
- package/templates/native-go-vue/frontend/src/i18n/ko.json +20 -0
- package/templates/native-go-vue/frontend/src/i18n/zh-CN.json +20 -0
- package/templates/native-go-vue/frontend/src/i18n/zh-TW.json +20 -0
- package/templates/native-go-vue/frontend/src/i18n.ts +13 -0
- package/templates/native-go-vue/frontend/src/identity/LoginPage.vue +16 -14
- package/templates/native-go-vue/frontend/src/identity/i18n/de.json +27 -0
- package/templates/native-go-vue/frontend/src/identity/i18n/en.json +27 -0
- package/templates/native-go-vue/frontend/src/identity/i18n/es.json +27 -0
- package/templates/native-go-vue/frontend/src/identity/i18n/fr.json +27 -0
- package/templates/native-go-vue/frontend/src/identity/i18n/it.json +27 -0
- package/templates/native-go-vue/frontend/src/identity/i18n/ja.json +27 -0
- package/templates/native-go-vue/frontend/src/identity/i18n/ko.json +27 -0
- package/templates/native-go-vue/frontend/src/identity/i18n/zh-CN.json +27 -0
- package/templates/native-go-vue/frontend/src/identity/i18n/zh-TW.json +27 -0
- package/templates/native-go-vue/frontend/src/identity/i18n.ts +14 -0
- package/templates/native-go-vue/frontend/src/identity/session.test.ts +4 -0
- package/templates/native-go-vue/frontend/src/identity/session.ts +9 -8
- package/templates/native-go-vue/frontend/src/nas/NasPicker.vue +31 -12
- package/templates/native-go-vue/frontend/src/nas/destination.test.ts +48 -0
- package/templates/native-go-vue/frontend/src/nas/i18n/files/de.json +1 -1
- package/templates/native-go-vue/frontend/src/nas/i18n/files/en.json +1 -1
- package/templates/native-go-vue/frontend/src/nas/i18n/files/es.json +10 -10
- package/templates/native-go-vue/frontend/src/nas/i18n/files/fr.json +1 -1
- package/templates/native-go-vue/frontend/src/nas/i18n/files/it.json +12 -12
- package/templates/native-go-vue/frontend/src/nas/i18n/files/ja.json +15 -15
- package/templates/native-go-vue/frontend/src/nas/i18n/files/ko.json +12 -12
- package/templates/native-go-vue/frontend/src/nas/i18n/files/zh-CN.json +1 -1
- package/templates/native-go-vue/frontend/src/nas/i18n/files/zh-TW.json +1 -1
- package/templates/native-go-vue/frontend/src/portal/PortalSettings.vue +39 -0
- package/templates/native-go-vue/frontend/src/shell/i18n/fr.json +1 -1
- package/templates/native-go-vue/frontend/src/shell/i18n/ja.json +1 -1
- package/templates/native-go-vue/frontend/test/apps.test.ts +14 -0
- package/templates/native-go-vue/frontend/test/portal-settings.test.ts +71 -0
- 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
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
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
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
if(
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
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
|
-
|
|
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",
|
|
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.
|
|
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.
|
|
1151
|
-
"resolved": "https://registry.npmjs.org/@synopackageland/ui/-/ui-0.8.
|
|
1152
|
-
"integrity": "sha512-
|
|
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.
|
|
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
|
-
|
|
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=
|
|
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">
|
|
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">
|
|
67
|
-
<div class="integration-app" :aria-busy="busy" aria-label="
|
|
68
|
-
<p v-if="busy" role="status">
|
|
69
|
-
<Callout v-if="error" tone="danger" title="
|
|
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="
|
|
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">
|
|
78
|
+
<p v-else-if="error" role="status">{{ copy.statusError }}</p>
|
|
75
79
|
</SettingsSection>
|
|
76
|
-
<
|
|
77
|
-
|
|
78
|
-
|
|
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
|
|
11
|
-
|
|
12
|
-
|
|
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
|
-
<
|
|
20
|
-
|
|
21
|
-
|
|
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
|
|
2
|
-
import
|
|
3
|
-
export function delegateCopy(locale='en'){return locale
|
|
4
|
-
export function errorCopy(error:unknown,copy:
|
|
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
|
-
|
|
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
|
+
})
|