@synopackageland/cli 0.6.0 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +613 -0
- package/README.md +81 -78
- package/dist/add.js +39 -16
- package/dist/authorization-sdk.d.ts +7 -0
- package/dist/authorization-sdk.js +7 -0
- 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 +39 -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.d.ts +1 -1
- package/dist/build-spk/gateway.js +2 -2
- package/dist/build-spk/generator.js +30 -5
- 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 +30 -18
- 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/plan.js +1 -1
- package/dist/deploy/spk-version.js +1 -1
- package/dist/error-registry.js +46 -13
- package/dist/host/dsm-client.d.ts +8 -4
- package/dist/host/dsm-client.js +10 -4
- package/dist/host/types.d.ts +2 -1
- package/dist/info.js +18 -2
- package/dist/init.d.ts +3 -0
- package/dist/init.js +49 -18
- 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/executor-build.d.ts +1 -1
- package/dist/runtime/executor-build.js +5 -4
- 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 -1
- package/dist/standalone/daemon.js +2 -1
- package/dist/standalone/dsm-window.d.ts +3 -1
- package/dist/standalone/dsm-window.js +72 -15
- package/dist/standalone/inspect-spk.d.ts +9 -1
- package/dist/standalone/inspect-spk.js +119 -26
- 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 +2 -0
- package/dist/standalone/native-authorization-window.js +30 -0
- package/dist/standalone/native-layout.d.ts +82 -0
- package/dist/standalone/native-layout.js +377 -0
- package/dist/standalone/native-root-window.d.ts +26 -0
- package/dist/standalone/native-root-window.js +282 -0
- 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.js +62 -15
- package/dist/standalone/spk.d.ts +1 -1
- package/dist/standalone/spk.js +25 -7
- package/dist/templates/_daemon/main_test.go +61 -8
- package/dist/templates/_executor/README.md +127 -16
- package/dist/templates/_executor/anchor.go +146 -0
- package/dist/templates/_executor/anchor_test.go +126 -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/dsm.go +23 -10
- 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/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 +72 -20
- 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/platform_linux.go +24 -173
- package/dist/templates/_executor/platform_linux_test.go +9 -65
- package/dist/templates/_executor/platform_other.go +30 -4
- package/dist/templates/_executor/process.go +3 -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 +7 -8
- package/dist/templates/_executor/tasks_review_test.go +1 -1
- package/dist/templates/_executor/tasks_test.go +4 -4
- 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/_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 +91 -0
- package/dist/templates/native-authorization/i18n/de.json +37 -0
- package/dist/templates/native-authorization/i18n/en.json +37 -0
- package/dist/templates/native-authorization/i18n/es.json +37 -0
- package/dist/templates/native-authorization/i18n/fr.json +37 -0
- package/dist/templates/native-authorization/i18n/index.ts +15 -0
- package/dist/templates/native-authorization/i18n/it.json +37 -0
- package/dist/templates/native-authorization/i18n/ja.json +37 -0
- package/dist/templates/native-authorization/i18n/ko.json +37 -0
- package/dist/templates/native-authorization/i18n/zh-CN.json +37 -0
- package/dist/templates/native-authorization/i18n/zh-TW.json +37 -0
- 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/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/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/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/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-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 +52 -8
- 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-setup.ts +4 -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 +46 -0
- package/dist/templates/native-go-vue/frontend/src/delegate/i18n/en.json +12 -2
- package/dist/templates/native-go-vue/frontend/src/delegate/i18n/es.json +46 -0
- package/dist/templates/native-go-vue/frontend/src/delegate/i18n/fr.json +46 -0
- package/dist/templates/native-go-vue/frontend/src/delegate/i18n/it.json +46 -0
- package/dist/templates/native-go-vue/frontend/src/delegate/i18n/ja.json +46 -0
- package/dist/templates/native-go-vue/frontend/src/delegate/i18n/ko.json +46 -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 +46 -0
- package/dist/templates/native-go-vue/frontend/src/delegate/i18n/zh-TW.json +15 -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/dist/validate.js +2 -1
- package/package.json +12 -2
- package/schema/package-metadata.schema.json +101 -0
- package/schema/synology-app.schema.json +112 -0
- package/skills/SKILL.md +109 -3
- 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/main_test.go +61 -8
- package/templates/_executor/README.md +127 -16
- package/templates/_executor/anchor.go +146 -0
- package/templates/_executor/anchor_test.go +126 -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/dsm.go +23 -10
- 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/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 +72 -20
- 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/platform_linux.go +24 -173
- package/templates/_executor/platform_linux_test.go +9 -65
- package/templates/_executor/platform_other.go +30 -4
- package/templates/_executor/process.go +3 -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 +7 -8
- package/templates/_executor/tasks_review_test.go +1 -1
- package/templates/_executor/tasks_test.go +4 -4
- 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/_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 +91 -0
- package/templates/native-authorization/i18n/de.json +37 -0
- package/templates/native-authorization/i18n/en.json +37 -0
- package/templates/native-authorization/i18n/es.json +37 -0
- package/templates/native-authorization/i18n/fr.json +37 -0
- package/templates/native-authorization/i18n/index.ts +15 -0
- package/templates/native-authorization/i18n/it.json +37 -0
- package/templates/native-authorization/i18n/ja.json +37 -0
- package/templates/native-authorization/i18n/ko.json +37 -0
- package/templates/native-authorization/i18n/zh-CN.json +37 -0
- package/templates/native-authorization/i18n/zh-TW.json +37 -0
- package/templates/native-go/README.md +43 -10
- package/templates/native-go/backend/cmd/app/main.go +27 -7
- 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/sdk_policy_test.go +39 -0
- package/templates/native-go/backend/internal/filestation/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/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-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 +52 -8
- 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-setup.ts +4 -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 +46 -0
- package/templates/native-go-vue/frontend/src/delegate/i18n/en.json +12 -2
- package/templates/native-go-vue/frontend/src/delegate/i18n/es.json +46 -0
- package/templates/native-go-vue/frontend/src/delegate/i18n/fr.json +46 -0
- package/templates/native-go-vue/frontend/src/delegate/i18n/it.json +46 -0
- package/templates/native-go-vue/frontend/src/delegate/i18n/ja.json +46 -0
- package/templates/native-go-vue/frontend/src/delegate/i18n/ko.json +46 -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 +46 -0
- package/templates/native-go-vue/frontend/src/delegate/i18n/zh-TW.json +15 -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
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
// Package portal reads the DSM Application Portal alias an administrator set
|
|
2
|
+
// for this package. DSM owns the setting: the package never writes it, and
|
|
3
|
+
// anything this reader does not fully understand reads as Unavailable.
|
|
4
|
+
package portal
|
|
5
|
+
|
|
6
|
+
import (
|
|
7
|
+
"bytes"
|
|
8
|
+
"encoding/json"
|
|
9
|
+
"errors"
|
|
10
|
+
"io"
|
|
11
|
+
"io/fs"
|
|
12
|
+
"os"
|
|
13
|
+
"regexp"
|
|
14
|
+
"syscall"
|
|
15
|
+
"unicode/utf8"
|
|
16
|
+
|
|
17
|
+
"dsmapp/internal/jsonlimits"
|
|
18
|
+
)
|
|
19
|
+
|
|
20
|
+
// MaxConfigBytes bounds DSM's saved setting; its real size is a few hundred bytes.
|
|
21
|
+
const MaxConfigBytes = 64 << 10
|
|
22
|
+
|
|
23
|
+
const (
|
|
24
|
+
// Active means users can open the app at /<alias>/ after DSM sign-in.
|
|
25
|
+
Active = "active"
|
|
26
|
+
// Inactive means no administrator has set an alias yet.
|
|
27
|
+
Inactive = "inactive"
|
|
28
|
+
// Unavailable means the setting could not be read safely.
|
|
29
|
+
Unavailable = "unavailable"
|
|
30
|
+
)
|
|
31
|
+
|
|
32
|
+
// Status is what portal.status returns; Alias is set only when Active.
|
|
33
|
+
type Status struct {
|
|
34
|
+
State string `json:"state"`
|
|
35
|
+
Alias string `json:"alias,omitempty"`
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// A single URL path segment; DSM's own validation is looser than this.
|
|
39
|
+
var aliasPattern = regexp.MustCompile(`^[A-Za-z0-9][A-Za-z0-9_-]{0,63}$`)
|
|
40
|
+
|
|
41
|
+
// ConfigPath is where DSM saves the package's portal setting
|
|
42
|
+
// (/var/packages/<package>/etc is the package's @appconf directory).
|
|
43
|
+
func ConfigPath(packageID string) string {
|
|
44
|
+
return "/var/packages/" + packageID + "/etc/AppPortal.json"
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// Read returns the saved alias when the setting belongs to appID.
|
|
48
|
+
func Read(path, appID string) Status {
|
|
49
|
+
raw, err := readBounded(path)
|
|
50
|
+
if errors.Is(err, fs.ErrNotExist) {
|
|
51
|
+
return Status{State: Inactive}
|
|
52
|
+
}
|
|
53
|
+
if err != nil {
|
|
54
|
+
return Status{State: Unavailable}
|
|
55
|
+
}
|
|
56
|
+
id, alias, ok := parse(raw)
|
|
57
|
+
if !ok || id != appID {
|
|
58
|
+
return Status{State: Unavailable}
|
|
59
|
+
}
|
|
60
|
+
if alias == "" {
|
|
61
|
+
return Status{State: Inactive}
|
|
62
|
+
}
|
|
63
|
+
if !aliasPattern.MatchString(alias) {
|
|
64
|
+
return Status{State: Unavailable}
|
|
65
|
+
}
|
|
66
|
+
return Status{State: Active, Alias: alias}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
func readBounded(path string) ([]byte, error) {
|
|
70
|
+
f, err := os.OpenFile(path, os.O_RDONLY|syscall.O_NOFOLLOW|syscall.O_NONBLOCK, 0)
|
|
71
|
+
if err != nil {
|
|
72
|
+
return nil, err
|
|
73
|
+
}
|
|
74
|
+
defer f.Close()
|
|
75
|
+
info, err := f.Stat()
|
|
76
|
+
if err != nil {
|
|
77
|
+
return nil, err
|
|
78
|
+
}
|
|
79
|
+
if !info.Mode().IsRegular() {
|
|
80
|
+
return nil, errors.New("not a regular file")
|
|
81
|
+
}
|
|
82
|
+
raw, err := io.ReadAll(io.LimitReader(f, MaxConfigBytes+1))
|
|
83
|
+
if err != nil {
|
|
84
|
+
return nil, err
|
|
85
|
+
}
|
|
86
|
+
if len(raw) > MaxConfigBytes {
|
|
87
|
+
return nil, errors.New("too large")
|
|
88
|
+
}
|
|
89
|
+
return raw, nil
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// parse accepts exactly one JSON object with unique top-level keys, a string
|
|
93
|
+
// id and a string or null alias; other DSM fields are skipped unread.
|
|
94
|
+
func parse(raw []byte) (id, alias string, ok bool) {
|
|
95
|
+
if !utf8.Valid(raw) || !jsonlimits.WithinLimits(raw) {
|
|
96
|
+
return "", "", false
|
|
97
|
+
}
|
|
98
|
+
d := json.NewDecoder(bytes.NewReader(raw))
|
|
99
|
+
if token, err := d.Token(); err != nil || token != json.Delim('{') {
|
|
100
|
+
return "", "", false
|
|
101
|
+
}
|
|
102
|
+
seen := map[string]bool{}
|
|
103
|
+
hasID := false
|
|
104
|
+
for d.More() {
|
|
105
|
+
token, err := d.Token()
|
|
106
|
+
key, isKey := token.(string)
|
|
107
|
+
if err != nil || !isKey || seen[key] {
|
|
108
|
+
return "", "", false
|
|
109
|
+
}
|
|
110
|
+
seen[key] = true
|
|
111
|
+
var value json.RawMessage
|
|
112
|
+
if err := d.Decode(&value); err != nil {
|
|
113
|
+
return "", "", false
|
|
114
|
+
}
|
|
115
|
+
switch key {
|
|
116
|
+
case "id":
|
|
117
|
+
if json.Unmarshal(value, &id) != nil || id == "" {
|
|
118
|
+
return "", "", false
|
|
119
|
+
}
|
|
120
|
+
hasID = true
|
|
121
|
+
case "alias":
|
|
122
|
+
var text *string
|
|
123
|
+
if json.Unmarshal(value, &text) != nil {
|
|
124
|
+
return "", "", false
|
|
125
|
+
}
|
|
126
|
+
if text != nil {
|
|
127
|
+
alias = *text
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
if token, err := d.Token(); err != nil || token != json.Delim('}') {
|
|
132
|
+
return "", "", false
|
|
133
|
+
}
|
|
134
|
+
if _, err := d.Token(); err != io.EOF {
|
|
135
|
+
return "", "", false
|
|
136
|
+
}
|
|
137
|
+
return id, alias, hasID
|
|
138
|
+
}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
package portal
|
|
2
|
+
|
|
3
|
+
import (
|
|
4
|
+
"os"
|
|
5
|
+
"path/filepath"
|
|
6
|
+
"strings"
|
|
7
|
+
"testing"
|
|
8
|
+
)
|
|
9
|
+
|
|
10
|
+
const appID = "SYNO.SDS.HelloGo.Instance"
|
|
11
|
+
|
|
12
|
+
// saved mirrors DSM 7.4's AppPortal.json after an administrator sets an alias.
|
|
13
|
+
func saved(alias string) string {
|
|
14
|
+
return "{\n\t\"acl\" : \"\",\n\t\"alias\" : " + alias + ",\n\t\"alternatePort\" : null,\n\t\"application\" : \"Hello Go\",\n\t\"fqdn\" : null,\n\t\"https\" : \n\t{\n\t\t\"hsts\" : false\n\t},\n\t\"id\" : \"" + appID + "\",\n\t\"version\" : 3\n}\n"
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
func write(t *testing.T, content string) string {
|
|
18
|
+
t.Helper()
|
|
19
|
+
path := filepath.Join(t.TempDir(), "AppPortal.json")
|
|
20
|
+
if err := os.WriteFile(path, []byte(content), 0o644); err != nil {
|
|
21
|
+
t.Fatal(err)
|
|
22
|
+
}
|
|
23
|
+
return path
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
func TestActiveAlias(t *testing.T) {
|
|
27
|
+
got := Read(write(t, saved(`"hello-go"`)), appID)
|
|
28
|
+
if got != (Status{State: Active, Alias: "hello-go"}) {
|
|
29
|
+
t.Fatalf("got %+v", got)
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
func TestNoSavedSettingIsInactive(t *testing.T) {
|
|
34
|
+
if got := Read(filepath.Join(t.TempDir(), "AppPortal.json"), appID); got != (Status{State: Inactive}) {
|
|
35
|
+
t.Fatalf("missing file: %+v", got)
|
|
36
|
+
}
|
|
37
|
+
for _, alias := range []string{`null`, `""`} {
|
|
38
|
+
if got := Read(write(t, saved(alias)), appID); got != (Status{State: Inactive}) {
|
|
39
|
+
t.Fatalf("alias %s: %+v", alias, got)
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
func TestAnythingElseFailsClosed(t *testing.T) {
|
|
45
|
+
other := strings.Replace(saved(`"hello-go"`), appID, "SYNO.SDS.Other.Instance", 1)
|
|
46
|
+
cases := map[string]string{
|
|
47
|
+
"another app": other,
|
|
48
|
+
"no id": `{"alias":"hello-go"}`,
|
|
49
|
+
"duplicate alias": `{"alias":"hello-go","alias":"evil","id":"` + appID + `"}`,
|
|
50
|
+
"duplicate id": `{"id":"SYNO.SDS.Other.Instance","alias":"hello-go","id":"` + appID + `"}`,
|
|
51
|
+
"alias number": saved(`1`),
|
|
52
|
+
"alias path": saved(`"a/b"`),
|
|
53
|
+
"alias dots": saved(`".."`),
|
|
54
|
+
"alias query": saved(`"a?b"`),
|
|
55
|
+
"alias too long": saved(`"` + strings.Repeat("a", 65) + `"`),
|
|
56
|
+
"array": `[]`,
|
|
57
|
+
"trailing value": saved(`"hello-go"`) + `{}`,
|
|
58
|
+
"not json": `alias=hello-go`,
|
|
59
|
+
"not utf8": "{\"alias\":\"\xff\",\"id\":\"" + appID + "\"}",
|
|
60
|
+
"too large": saved(`"hello-go"`) + strings.Repeat(" ", MaxConfigBytes),
|
|
61
|
+
"too deep": strings.Replace(saved(`"hello-go"`), `"acl" : ""`, `"acl" : `+strings.Repeat("[", 64)+strings.Repeat("]", 64), 1),
|
|
62
|
+
}
|
|
63
|
+
for name, content := range cases {
|
|
64
|
+
if got := Read(write(t, content), appID); got != (Status{State: Unavailable}) {
|
|
65
|
+
t.Errorf("%s: %+v", name, got)
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
func TestOnlyARegularFileIsRead(t *testing.T) {
|
|
71
|
+
dir := t.TempDir()
|
|
72
|
+
target := write(t, saved(`"hello-go"`))
|
|
73
|
+
link := filepath.Join(dir, "AppPortal.json")
|
|
74
|
+
if err := os.Symlink(target, link); err != nil {
|
|
75
|
+
t.Fatal(err)
|
|
76
|
+
}
|
|
77
|
+
if got := Read(link, appID); got != (Status{State: Unavailable}) {
|
|
78
|
+
t.Fatalf("symlink: %+v", got)
|
|
79
|
+
}
|
|
80
|
+
if got := Read(dir, appID); got != (Status{State: Unavailable}) {
|
|
81
|
+
t.Fatalf("directory: %+v", got)
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
func TestConfigPathIsThePackagesOwnSetting(t *testing.T) {
|
|
86
|
+
if got := ConfigPath("HelloGo"); got != "/var/packages/HelloGo/etc/AppPortal.json" {
|
|
87
|
+
t.Fatal(got)
|
|
88
|
+
}
|
|
89
|
+
}
|
|
@@ -11,9 +11,28 @@ build: 1
|
|
|
11
11
|
displayname: __DISPLAY_NAME__
|
|
12
12
|
description: A native DSM package with a Go backend and a DSM window UI.
|
|
13
13
|
maintainer: Your Name
|
|
14
|
+
# Optional INFO/website metadata. Omit fields you do not need.
|
|
15
|
+
# metadata:
|
|
16
|
+
# contact_email: you@example.com
|
|
17
|
+
# support_email: support@example.com
|
|
18
|
+
# homepage: https://example.com
|
|
19
|
+
# source_url: https://github.com/you/your-app
|
|
20
|
+
# issues_url: https://github.com/you/your-app/issues
|
|
21
|
+
# license: MIT
|
|
22
|
+
# long_description:
|
|
23
|
+
# en: |-
|
|
24
|
+
# Explain what the app does and how to use it.
|
|
25
|
+
# zh-TW: Translated description.
|
|
26
|
+
# changelog: # text, or per-language like long_description
|
|
27
|
+
# en: |-
|
|
28
|
+
# - Most important change first. Omit to use Release notes.
|
|
29
|
+
# zh-TW: |-
|
|
30
|
+
# - Translated changes.
|
|
31
|
+
# screenshots: [https://example.com/screenshot.png]
|
|
14
32
|
os_min_ver: "7.1-42661"
|
|
15
33
|
|
|
16
|
-
# One SPK per architecture. Supported: x86_64, aarch64
|
|
34
|
+
# One SPK per architecture. Supported: x86_64, aarch64, armv7 (add it here to
|
|
35
|
+
# build it; armv7 jobs/delegate/root-ops/daemon are built but not NAS-verified).
|
|
17
36
|
arches: [x86_64, aarch64]
|
|
18
37
|
|
|
19
38
|
backend:
|
|
@@ -580,7 +580,8 @@
|
|
|
580
580
|
"it": "Siamo spiacenti, a questo account utente non sono stati assegnati privilegi appropriati per eseguire questa azione."
|
|
581
581
|
},
|
|
582
582
|
"keys": [
|
|
583
|
-
"error.admin_required"
|
|
583
|
+
"error.admin_required",
|
|
584
|
+
"error.app_privilege_denied"
|
|
584
585
|
]
|
|
585
586
|
},
|
|
586
587
|
{
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
"app.refresh": "Aktualisieren",
|
|
4
4
|
"app.title": "Es funktioniert",
|
|
5
5
|
"error.admin_required": "Dieses Benutzerkonto verfügt nicht über die erforderlichen Berechtigungen, um diese Aktion durchzuführen.",
|
|
6
|
+
"error.app_privilege_denied": "Dieses Benutzerkonto verfügt nicht über die erforderlichen Berechtigungen, um diese Aktion durchzuführen.",
|
|
6
7
|
"error.auth_unavailable": "Dieser Vorgang kann nicht ausgeführt werden. Bitte versuchen Sie es später erneut.",
|
|
7
8
|
"error.bad_request": "Überprüfen Sie den eingegebenen Wert und versuchen Sie es erneut.",
|
|
8
9
|
"error.bad_response": "Dieser Vorgang kann nicht ausgeführt werden. Bitte versuchen Sie es später erneut.",
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
"app.refresh": "Refresh",
|
|
4
4
|
"app.title": "It works",
|
|
5
5
|
"error.admin_required": "Sorry, this user account has not been assigned proper privileges to perform this action.",
|
|
6
|
+
"error.app_privilege_denied": "Sorry, this user account has not been assigned proper privileges to perform this action.",
|
|
6
7
|
"error.auth_unavailable": "Unable to perform this operation. Please try again later.",
|
|
7
8
|
"error.bad_request": "Check the entered value and try again.",
|
|
8
9
|
"error.bad_response": "Unable to perform this operation. Please try again later.",
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
"app.refresh": "Actualizar",
|
|
4
4
|
"app.title": "Funciona",
|
|
5
5
|
"error.admin_required": "A esta cuenta de usuario no se le han asignado los privilegios adecuados para realizar esta acción.",
|
|
6
|
+
"error.app_privilege_denied": "A esta cuenta de usuario no se le han asignado los privilegios adecuados para realizar esta acción.",
|
|
6
7
|
"error.auth_unavailable": "No se puede realizar esta operación. Vuelva a intentarlo más tarde.",
|
|
7
8
|
"error.bad_request": "Compruebe el valor introducido y vuelva a intentarlo.",
|
|
8
9
|
"error.bad_response": "No se puede realizar esta operación. Vuelva a intentarlo más tarde.",
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
"app.refresh": "Actualiser",
|
|
4
4
|
"app.title": "Ça fonctionne",
|
|
5
5
|
"error.admin_required": "Désolé, ce compte utilisateur ne dispose pas des droits nécessaires pour accomplir cette action.",
|
|
6
|
+
"error.app_privilege_denied": "Désolé, ce compte utilisateur ne dispose pas des droits nécessaires pour accomplir cette action.",
|
|
6
7
|
"error.auth_unavailable": "Impossible d'effectuer cette opération. Veuillez réessayer ultérieurement.",
|
|
7
8
|
"error.bad_request": "Vérifiez la valeur saisie et réessayez.",
|
|
8
9
|
"error.bad_response": "Impossible d'effectuer cette opération. Veuillez réessayer ultérieurement.",
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
"app.refresh": "Aggiorna",
|
|
4
4
|
"app.title": "Funziona",
|
|
5
5
|
"error.admin_required": "Siamo spiacenti, a questo account utente non sono stati assegnati privilegi appropriati per eseguire questa azione.",
|
|
6
|
+
"error.app_privilege_denied": "Siamo spiacenti, a questo account utente non sono stati assegnati privilegi appropriati per eseguire questa azione.",
|
|
6
7
|
"error.auth_unavailable": "Impossibile eseguire questa operazione. Riprovare più tardi.",
|
|
7
8
|
"error.bad_request": "Controllare il valore inserito e riprovare.",
|
|
8
9
|
"error.bad_response": "Impossibile eseguire questa operazione. Riprovare più tardi.",
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
"app.refresh": "最新の情報に更新",
|
|
4
4
|
"app.title": "動作しています",
|
|
5
5
|
"error.admin_required": "申し訳ありませんが、このユーザー アカウントにはこの操作を行うための権限が与えられていません。",
|
|
6
|
+
"error.app_privilege_denied": "申し訳ありませんが、このユーザー アカウントにはこの操作を行うための権限が与えられていません。",
|
|
6
7
|
"error.auth_unavailable": "この操作は実行できません。後でもう一度お試しください。",
|
|
7
8
|
"error.bad_request": "入力した値を確認して、もう一度お試しください。",
|
|
8
9
|
"error.bad_response": "この操作は実行できません。後でもう一度お試しください。",
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
"app.refresh": "새로 고침",
|
|
4
4
|
"app.title": "정상적으로 작동합니다",
|
|
5
5
|
"error.admin_required": "죄송합니다. 이 사용자 계정에는 이 작업을 수행하기에 적절한 권한이 할당되지 않았습니다.",
|
|
6
|
+
"error.app_privilege_denied": "죄송합니다. 이 사용자 계정에는 이 작업을 수행하기에 적절한 권한이 할당되지 않았습니다.",
|
|
6
7
|
"error.auth_unavailable": "이 작업을 수행할 수 없습니다. 나중에 다시 시도하십시오.",
|
|
7
8
|
"error.bad_request": "입력한 값을 확인한 후 다시 시도하십시오.",
|
|
8
9
|
"error.bad_response": "이 작업을 수행할 수 없습니다. 나중에 다시 시도하십시오.",
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
"app.refresh": "刷新",
|
|
4
4
|
"app.title": "运行成功",
|
|
5
5
|
"error.admin_required": "抱歉,此用户帐户权限不足,无法执行此操作。",
|
|
6
|
+
"error.app_privilege_denied": "抱歉,此用户帐户权限不足,无法执行此操作。",
|
|
6
7
|
"error.auth_unavailable": "无法执行此操作。请稍后再试。",
|
|
7
8
|
"error.bad_request": "请检查输入的值后重试。",
|
|
8
9
|
"error.bad_response": "无法执行此操作。请稍后再试。",
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
"app.refresh": "重新整理",
|
|
4
4
|
"app.title": "成功運作了",
|
|
5
5
|
"error.admin_required": "抱歉,此使用者帳號權限不足,無法執行此動作。",
|
|
6
|
+
"error.app_privilege_denied": "抱歉,此使用者帳號權限不足,無法執行此動作。",
|
|
6
7
|
"error.auth_unavailable": "無法執行此操作。請稍後再試。",
|
|
7
8
|
"error.bad_request": "請檢查輸入的值後再試一次。",
|
|
8
9
|
"error.bad_response": "無法執行此操作。請稍後再試。",
|
|
@@ -33,3 +33,13 @@
|
|
|
33
33
|
.dsmapp-facts { display: grid; grid-template-columns: max-content 1fr; gap: 4px 16px; margin: 0; }
|
|
34
34
|
.dsmapp-facts dt { color: #5d6b7a; }
|
|
35
35
|
.dsmapp-facts dd { margin: 0; }
|
|
36
|
+
/* SDK portal panel (ctx.portal.mountPanel). */
|
|
37
|
+
.dsmapp-portal-form { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; max-width: 560px; }
|
|
38
|
+
.dsmapp-portal input { height: 28px; padding: 0 8px; border: 1px solid #c8d0da; border-radius: 3px; font: inherit; }
|
|
39
|
+
.dsmapp-portal button {
|
|
40
|
+
height: 30px; padding: 0 12px; border: 1px solid #c8d0da; border-radius: 3px;
|
|
41
|
+
background: #fff; color: inherit; font: inherit; cursor: pointer; margin-top: 8px;
|
|
42
|
+
}
|
|
43
|
+
.dsmapp-portal-form button { margin-top: 0; }
|
|
44
|
+
.dsmapp-portal-form button[type="submit"] { border-color: #0086e5; background: #0086e5; color: #fff; }
|
|
45
|
+
.dsmapp-portal-error { color: #c0392b; }
|
|
@@ -15,6 +15,9 @@
|
|
|
15
15
|
// host-theme/language-change unsupported
|
|
16
16
|
// ctx.host.openFileManager(path) / revealFile(path) -> File Station virtual path
|
|
17
17
|
// request only; preview/details/settings return unsupported, never a grant
|
|
18
|
+
// ctx.portal -> only with `portal:` in dsm-package.yaml;
|
|
19
|
+
// mountPanel(element) shows the DSM Application
|
|
20
|
+
// Portal link, or the alias form to administrators
|
|
18
21
|
// Replace everything below with your own UI (plain JS, or a bundle you build
|
|
19
22
|
// with Vite/Vue into this folder -- see README.md).
|
|
20
23
|
(function () {
|
|
@@ -40,6 +43,7 @@
|
|
|
40
43
|
var message = el("p", { class: "dsmapp-message", role: "status" });
|
|
41
44
|
var form = el("form", { class: "dsmapp-form", hidden: "" }, [input, save]);
|
|
42
45
|
var adminOnly = el("p", { class: "dsmapp-note", hidden: "", text: t("greeting.adminOnly") });
|
|
46
|
+
var portal = ctx.portal ? el("section", { class: "dsmapp-portal" }) : null;
|
|
43
47
|
|
|
44
48
|
root.classList.add("dsmapp");
|
|
45
49
|
root.replaceChildren(
|
|
@@ -51,6 +55,7 @@
|
|
|
51
55
|
el("h2", { text: t("status.heading") }),
|
|
52
56
|
status
|
|
53
57
|
);
|
|
58
|
+
var portalPanel = portal && (root.appendChild(portal), ctx.portal.mountPanel(portal));
|
|
54
59
|
|
|
55
60
|
// Errors arrive as { code }; the backend's English message is only a fallback.
|
|
56
61
|
function showError(error) {
|
|
@@ -93,7 +98,12 @@
|
|
|
93
98
|
|
|
94
99
|
ctx.reopen(refresh);
|
|
95
100
|
refresh();
|
|
96
|
-
return {
|
|
101
|
+
return {
|
|
102
|
+
unmount: function () {
|
|
103
|
+
if (portalPanel) portalPanel.dispose();
|
|
104
|
+
root.replaceChildren();
|
|
105
|
+
}
|
|
106
|
+
};
|
|
97
107
|
}
|
|
98
108
|
|
|
99
109
|
window.DsmPackageApps = window.DsmPackageApps || {};
|
|
@@ -8,10 +8,12 @@ A worker stores only a delegation ID and job ID, never session credentials.
|
|
|
8
8
|
## CLI assembly
|
|
9
9
|
|
|
10
10
|
`init --with delegate` and `add delegate` apply base → jobs → delegate.
|
|
11
|
-
The SDK registers `DelegateRoutes` using the trusted manifest scope and
|
|
12
|
-
all
|
|
13
|
-
|
|
14
|
-
|
|
11
|
+
The SDK registers `DelegateRoutes` using the trusted manifest scope and lists
|
|
12
|
+
all 17 delegate methods in `sdk-permissions.json`: `delegate.disable` and
|
|
13
|
+
`delegate.enable` require administrators, the others `authenticated`.
|
|
14
|
+
App-owned permissions remain in `permissions.json`. `init --with delegate` sets
|
|
15
|
+
`app.status` to authenticated so ordinary users can mount their panels;
|
|
16
|
+
`add delegate` leaves `permissions.json` unchanged. The CLI binds both
|
|
15
17
|
policy files to the CGI build. SDK policy edits and overrides fail validation.
|
|
16
18
|
|
|
17
19
|
`background.Job.DelegationID` is an optional field in the shared base store.
|
|
@@ -23,7 +25,7 @@ shared store before activating delegate.
|
|
|
23
25
|
```ts
|
|
24
26
|
interface ExecutorSetup {
|
|
25
27
|
status(): Promise<CapabilityStatus>
|
|
26
|
-
authorize(password: string): Promise<CapabilityStatus>
|
|
28
|
+
authorize(password: string, selectedScope?: string): Promise<CapabilityStatus>
|
|
27
29
|
control(action: 'start' | 'stop' | 'purge'): Promise<CapabilityStatus>
|
|
28
30
|
}
|
|
29
31
|
```
|
|
@@ -140,3 +142,122 @@ The fixtures are temporary and are removed after each run.
|
|
|
140
142
|
These tests do not claim DSM kernel ACL, owner, reboot, real task or real executor
|
|
141
143
|
validation. CLI activation and real executor/setup integration belong to the other
|
|
142
144
|
P2 workstreams.
|
|
145
|
+
|
|
146
|
+
## Choose the scope at install and confirm it once
|
|
147
|
+
|
|
148
|
+
New `init` / `add delegate` projects default to `scope: install-selected`.
|
|
149
|
+
The DSM install wizard's native `api_store` combobox dynamically lists local
|
|
150
|
+
shares using the installer's administrator session (`SYNO.Core.Share list v1`).
|
|
151
|
+
A checkbox offers each user's `/home` instead; choosing a share never creates it
|
|
152
|
+
or grants its ACL to the package account. This wizard was measured on vt-nas
|
|
153
|
+
DSM 7.4 through a session-only SSH tunnel. Other DSM versions remain unverified.
|
|
154
|
+
|
|
155
|
+
Installation saves `{schema:1, scope}` in package-owned
|
|
156
|
+
`var/delegate-install.json`. It is **only a pre-selection**. The existing
|
|
157
|
+
administrator enable form displays it alongside the DSM password field. One
|
|
158
|
+
PasswordConfirm authorizes the pinned boot/control tasks and a one-time exact
|
|
159
|
+
selection task. Only the root executor writes
|
|
160
|
+
`/usr/local/etc/synopkgland/<pkg>/delegations/selected-scope.record`
|
|
161
|
+
(`schema`, `scope`, `generation`, root 0600). The package peer cannot call the
|
|
162
|
+
internal selection operation, even with an administrator cookie. Shipped
|
|
163
|
+
executor/policy pins lock the selection mode and operations; the chosen scope
|
|
164
|
+
is protected runtime state, so changing it does not rewrite shipped pins.
|
|
165
|
+
|
|
166
|
+
Settings uses this same form and `ctx.executor.authorize(password, scope)` to
|
|
167
|
+
change the scope. There is no extra dialog or step. A new scope generation
|
|
168
|
+
revokes existing grants and cancels pending saves under the publication fence.
|
|
169
|
+
Changing away and back never revives old grants. Existing destination files
|
|
170
|
+
remain. Selecting another directory for the same destination key replaces only
|
|
171
|
+
that person's previous grant for the key. Every person must select again after
|
|
172
|
+
scope changes. If the package draft differs, the UI shows both and follows the
|
|
173
|
+
protected value. Existing `scope: home` and `shared:<name>` manifests retain their
|
|
174
|
+
fixed-scope API and legacy TTL behavior. Dynamic selection requires the
|
|
175
|
+
`until-revoked` / `files.moveFromStaging` declaration.
|
|
176
|
+
|
|
177
|
+
A scope change on a running executor whose pins verify runs only the selection
|
|
178
|
+
task. It never restarts the executor, so a copy in flight ends `canceled` with
|
|
179
|
+
reason `scope-changed`, not `interrupted`. `authorize` waits about 20 seconds
|
|
180
|
+
for the protected readback. A `blocked` reply that carries `requested: {scope,
|
|
181
|
+
generation}` means the root task may still land: read `ctx.executor.status()`
|
|
182
|
+
(or `delegate.status`) until `selection.scope` equals `requested.scope` with a
|
|
183
|
+
`selection.generation` above `requested.generation` (applied); otherwise it was
|
|
184
|
+
not applied. Submitting the same scope again is safe: the task is pinned to the
|
|
185
|
+
current generation and an already selected scope is answered unchanged.
|
|
186
|
+
|
|
187
|
+
## One typed destination lookup, online and offline
|
|
188
|
+
|
|
189
|
+
The generated Vue template contains the complete administrator setup, confirmed
|
|
190
|
+
scope, scope-limited NasPicker, personal destinations and operations, and admin
|
|
191
|
+
kill switch. Use its existing facade:
|
|
192
|
+
|
|
193
|
+
```ts
|
|
194
|
+
import {createBackgroundSaveFacade} from './delegate/facade'
|
|
195
|
+
const sdk = createBackgroundSaveFacade(ctx)
|
|
196
|
+
const current = await sdk.destination('save')
|
|
197
|
+
// current.selection: {scope, root, generation} | null — protected executor evidence
|
|
198
|
+
// current.destination: {id, key, path, username, lifetime} | null — this caller only
|
|
199
|
+
// current.status: active | missing | reselection-required | needs-scope-confirmation | disabled
|
|
200
|
+
if (current.status === 'active') {
|
|
201
|
+
const ref = await sdk.exportExample({key: 'save', job_id: stableJobID})
|
|
202
|
+
// The generated My saves panel queries this reference; admission is not Saved.
|
|
203
|
+
}
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
The authenticated CGI is `delegate.destination` with `{key}`; it verifies the
|
|
207
|
+
fresh DSM session and username+UID, configured key, protected scope/generation
|
|
208
|
+
and current grant. Neither browser storage nor the installation draft supplies
|
|
209
|
+
a destination. Errors or missing protected evidence disable new selections.
|
|
210
|
+
|
|
211
|
+
Go uses `service.Destination(ctx, caller, request, "save") (DestinationState,error)`.
|
|
212
|
+
Capture `current.Destination.ID` in the App's own job only when status is active.
|
|
213
|
+
After logout, a producer uses that ID with `service.MoveFromStaging(ctx, params)`.
|
|
214
|
+
It does not use the package draft, pass a virtual destination path, or retain
|
|
215
|
+
session credentials. The executor rechecks scope generation, account, grant,
|
|
216
|
+
directory identity and DSM ACL before writing as the user's nonzero UID.
|
|
217
|
+
|
|
218
|
+
The shipped **Save example file** button calls `delegate.example.export` with
|
|
219
|
+
`{key, job_id}`. `backend/internal/delegate/example.go` is a working producer:
|
|
220
|
+
it obtains this user's active destination, creates a small package-private JSON
|
|
221
|
+
file (0600) under staging, and invokes `MoveFromStaging`. Repeated job IDs repeat
|
|
222
|
+
the original request: they return the original operation, admit only one the
|
|
223
|
+
executor does not know, and never overwrite staging. Adapt this producer for
|
|
224
|
+
daemon/export jobs and keep its stable ID. An uncertain result has no automatic
|
|
225
|
+
retry; only a protected committed receipt means Saved.
|
|
226
|
+
|
|
227
|
+
If admission failed or its reply was lost, call `MoveFromStaging` again later
|
|
228
|
+
with the same params. It queries the original first and re-sends only an
|
|
229
|
+
operation the executor does not know, with the pinned source unchanged (at most
|
|
230
|
+
10 possibly-delivered sends, 2 s backoff doubling to 5 min; the worker sweep
|
|
231
|
+
does the same). Retry on `EXECUTOR_UNAVAILABLE`, `EXECUTOR_TIMEOUT`,
|
|
232
|
+
`EXECUTOR_PROTOCOL`, `canceled`, `EXECUTOR_PAUSED`, `queue-full`,
|
|
233
|
+
`STORAGE_ERROR`, `ADMISSION_PENDING`, and on the package journal codes
|
|
234
|
+
`jobs_busy` (lock busy), `jobs_unavailable` (transient I/O) and `canceled` (the
|
|
235
|
+
caller's deadline or cancel ended the wait). None of these admitted anything a
|
|
236
|
+
retry could duplicate; while the journal is unavailable, a ref the executor
|
|
237
|
+
already knows still answers its state, so a committed move stays committed.
|
|
238
|
+
Terminal: executor refusals such as `APP_DENIED`, `DELEGATION_DENIED`,
|
|
239
|
+
`operation-conflict`, `disabled`, plus `STAGING_CHANGED`, `ADMISSION_EXHAUSTED`,
|
|
240
|
+
`spool_unsafe` and `journal_corrupt` (the package spool needs repair), and
|
|
241
|
+
`operation-not-found` for an accepted operation. See the delegate guide's Admission recovery table.
|
|
242
|
+
|
|
243
|
+
Cancel with the original reference (`CancelOperation`, or
|
|
244
|
+
`delegate.operations.cancel` from the UI); it is idempotent, so repeat it with
|
|
245
|
+
the same reference after a retryable error or a lost reply. `queued` becomes
|
|
246
|
+
`canceled` at once. A copy in flight answers `copying` with code
|
|
247
|
+
`CANCEL_REQUESTED`: accepted and durable, not finished. Poll `OperationStatus`
|
|
248
|
+
on the same reference until `canceled` (writer stopped, part removed, nothing
|
|
249
|
+
published, staging kept), `committed` (publication won; the file stays, drop only
|
|
250
|
+
the app record) or `uncertain` (a publish attempt failed first; keep querying).
|
|
251
|
+
The request survives executor crash, package stop/start and logout.
|
|
252
|
+
Revoke, scope change and disable store their reason (`grant-revoked`,
|
|
253
|
+
`scope-changed`, `integration-disabled`) the same way: a copy in flight answers
|
|
254
|
+
`CANCEL_REQUESTED` with it and settles `canceled` with it across a restart. A
|
|
255
|
+
copy the executor stops without a cancel ends `interrupted`/`EXECUTOR_PAUSED`
|
|
256
|
+
(`interrupted`/`interrupted` after a crash), never `WRITE_FAILED`. A cancel of
|
|
257
|
+
a reference not yet admitted leaves a `canceled` tombstone that a late or
|
|
258
|
+
recovered admission returns instead of copying, and a producer cancel is written
|
|
259
|
+
to the mirror first, so `MoveFromStaging` and the worker sweep forward it rather
|
|
260
|
+
than admit. See the delegate guide's Cancellation table.
|
|
261
|
+
|
|
262
|
+
|
|
263
|
+
The pinned scope-selection task also binds the protected generation observed before confirmation. The executor compares it under the publish lock and refuses stale tasks, so a retained task cannot replay an older selection after another confirmation.
|
|
@@ -12,12 +12,14 @@ import (
|
|
|
12
12
|
"strconv"
|
|
13
13
|
"syscall"
|
|
14
14
|
|
|
15
|
+
"dsmapp/internal/app"
|
|
15
16
|
"dsmapp/internal/background"
|
|
16
17
|
"dsmapp/internal/delegate"
|
|
17
18
|
)
|
|
18
19
|
|
|
19
20
|
// Set by `synopkgland build-spk` through -ldflags -X.
|
|
20
21
|
var packageID = "dev"
|
|
22
|
+
var delegateScope, delegateOperationsJSON, delegateDestinationsJSON, delegateLifetime string
|
|
21
23
|
|
|
22
24
|
func main() {
|
|
23
25
|
log.SetFlags(0)
|
|
@@ -35,6 +37,11 @@ func main() {
|
|
|
35
37
|
log.Printf("started as uid %s", strconv.Itoa(info.UID))
|
|
36
38
|
dataDir := "/var/packages/" + packageID + "/var"
|
|
37
39
|
service := &delegate.Service{Client: delegate.Local(packageID), Store: background.NewStore(dataDir + "/background"), StagingDir: dataDir + "/executor-staging"}
|
|
40
|
+
settings, err := app.ParseDelegateConfig(app.Config{DelegateScope: delegateScope, DelegateOperationsJSON: delegateOperationsJSON, DelegateDestinationsJSON: delegateDestinationsJSON, DelegateLifetime: delegateLifetime})
|
|
41
|
+
if err != nil {
|
|
42
|
+
log.Fatal(err)
|
|
43
|
+
}
|
|
44
|
+
service.Scope, service.Operations, service.Destinations, service.Lifetime = settings.Scope, settings.Operations, settings.Destinations, settings.Lifetime
|
|
38
45
|
if err := service.Run(ctx, info); err != nil {
|
|
39
46
|
log.Fatal(err)
|
|
40
47
|
}
|
|
@@ -4,12 +4,22 @@ import (
|
|
|
4
4
|
"dsmapp/internal/api"
|
|
5
5
|
"dsmapp/internal/background"
|
|
6
6
|
"dsmapp/internal/delegate"
|
|
7
|
+
"dsmapp/internal/filestation"
|
|
7
8
|
"path/filepath"
|
|
8
9
|
)
|
|
9
10
|
|
|
10
11
|
// DelegateRoutes is appended by the CLI's delegate capability assembly.
|
|
11
12
|
// scope comes from the trusted manifest, never an HTTP parameter.
|
|
12
13
|
func DelegateRoutes(cfg Config, scope string) []api.Route {
|
|
13
|
-
service := &delegate.Service{ExecutorDigest: cfg.ExecutorDigest, PolicyDigest: cfg.PolicyDigest, Client: delegate.Local(cfg.PackageID), Store: background.NewRequestStore(filepath.Join(cfg.DataDir, "background")), Scope: scope, StagingDir: filepath.Join(cfg.DataDir, "executor-staging")}
|
|
14
|
+
service := &delegate.Service{ExecutorDigest: cfg.ExecutorDigest, PolicyDigest: cfg.PolicyDigest, Client: delegate.Local(cfg.PackageID), Store: background.NewRequestStore(filepath.Join(cfg.DataDir, "background")), Scope: scope, StagingDir: filepath.Join(cfg.DataDir, "executor-staging"), InstallDraftPath: filepath.Join(cfg.DataDir, "delegate-install.json")}
|
|
15
|
+
settings, err := ParseDelegateConfig(cfg)
|
|
16
|
+
if err == nil {
|
|
17
|
+
service.Destinations, service.Operations, service.Lifetime = settings.Destinations, settings.Operations, settings.Lifetime
|
|
18
|
+
}
|
|
19
|
+
if cfg.SessionFiles != nil {
|
|
20
|
+
service.SessionFiles = cfg.SessionFiles
|
|
21
|
+
} else {
|
|
22
|
+
service.SessionFiles = filestation.Local(cfg.DataDir)
|
|
23
|
+
}
|
|
14
24
|
return service.Routes()
|
|
15
25
|
}
|
|
@@ -1,6 +1,20 @@
|
|
|
1
1
|
package app
|
|
2
2
|
|
|
3
3
|
func init() {
|
|
4
|
+
for method, params := range map[string]string{
|
|
5
|
+
"delegate.example.export": `{"key":"save","job_id":"example-1"}`,
|
|
6
|
+
"delegate.destination": `{"key":"save"}`,
|
|
7
|
+
"delegate.select_destination": `{"key":"save","path":"/home"}`,
|
|
8
|
+
"delegate.move_from_staging": `{"delegation_id":"grant","job_id":"export-1","staging":"a","name":"a","size":3}`,
|
|
9
|
+
"delegate.operations.get": `{"delegation_id":"grant","job_id":"export-1"}`,
|
|
10
|
+
"delegate.operations.list": `{}`,
|
|
11
|
+
"delegate.operations.cancel": `{"delegation_id":"grant","job_id":"export-1"}`,
|
|
12
|
+
"delegate.disable": `{}`, "delegate.enable": `{}`, "delegate.audit": `{}`,
|
|
13
|
+
} {
|
|
14
|
+
sampleParams[method] = params
|
|
15
|
+
sampleOutcome[method] = "session_required"
|
|
16
|
+
}
|
|
17
|
+
|
|
4
18
|
sampleParams["delegate.status"] = `{}`
|
|
5
19
|
sampleParams["delegate.create"] = `{"job_id":"job1","ops":["files.write"],"path":"/home","ttl_seconds":60}`
|
|
6
20
|
sampleParams["delegate.list"] = `{}`
|