@synopackageland/cli 0.6.1 → 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 -89
- package/dist/add.js +39 -16
- 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 +15 -7
- 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 +43 -15
- 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 +8 -0
- package/dist/standalone/inspect-spk.js +105 -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 +1 -1
- package/dist/standalone/native-authorization-window.js +2 -0
- package/dist/standalone/native-layout.d.ts +3 -0
- package/dist/standalone/native-layout.js +37 -5
- 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 +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/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/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/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 +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/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,79 @@
|
|
|
1
|
+
/** Frozen schema-2 / v1 extension. Definitions alone do not enable capabilities. */
|
|
2
|
+
export declare const MOVE_FROM_STAGING: "files.moveFromStaging";
|
|
3
|
+
export declare const DELEGATE_EXTENSION_OPERATIONS: readonly ["files.moveFromStaging", "files.operationStatus", "files.cancelOperation", "delegate.disable", "delegate.enable", "delegate.audit"];
|
|
4
|
+
export declare const DELEGATE_OPERATION_STATES: readonly ["queued", "copying", "committed", "canceled", "interrupted", "uncertain"];
|
|
5
|
+
export type DelegateOperationState = typeof DELEGATE_OPERATION_STATES[number];
|
|
6
|
+
export interface LongRunningDelegateCapability {
|
|
7
|
+
scope: string;
|
|
8
|
+
operations: [typeof MOVE_FROM_STAGING];
|
|
9
|
+
destinations: string[];
|
|
10
|
+
lifetime: 'until-revoked';
|
|
11
|
+
}
|
|
12
|
+
export interface SelectDestination {
|
|
13
|
+
key: string;
|
|
14
|
+
path: string;
|
|
15
|
+
}
|
|
16
|
+
/** Protected executor selection, not the package-owned installer draft. */
|
|
17
|
+
export interface ScopeSelection {
|
|
18
|
+
scope: string;
|
|
19
|
+
root: string;
|
|
20
|
+
generation: number;
|
|
21
|
+
}
|
|
22
|
+
export interface DestinationState {
|
|
23
|
+
key: string;
|
|
24
|
+
selection: ScopeSelection | null;
|
|
25
|
+
destination: DestinationGrant | null;
|
|
26
|
+
status: 'active' | 'missing' | 'reselection-required' | 'needs-scope-confirmation' | 'disabled';
|
|
27
|
+
}
|
|
28
|
+
export interface DestinationGrant {
|
|
29
|
+
id: string;
|
|
30
|
+
key: string;
|
|
31
|
+
path: string;
|
|
32
|
+
username: string;
|
|
33
|
+
lifetime: 'until-revoked';
|
|
34
|
+
}
|
|
35
|
+
/** Owner/UID/destination are resolved from the protected grant, never supplied here. */
|
|
36
|
+
export interface MoveFromStaging {
|
|
37
|
+
delegation_id: string;
|
|
38
|
+
job_id: string;
|
|
39
|
+
staging: string;
|
|
40
|
+
name: string;
|
|
41
|
+
size: number;
|
|
42
|
+
}
|
|
43
|
+
export interface OperationRef {
|
|
44
|
+
delegation_id: string;
|
|
45
|
+
job_id: string;
|
|
46
|
+
}
|
|
47
|
+
export interface OperationResult {
|
|
48
|
+
job_id: string;
|
|
49
|
+
state: DelegateOperationState;
|
|
50
|
+
/** Only a committed executor receipt may supply a final destination path. */
|
|
51
|
+
receipt?: {
|
|
52
|
+
path: string;
|
|
53
|
+
size: number;
|
|
54
|
+
};
|
|
55
|
+
code?: string;
|
|
56
|
+
reason?: string;
|
|
57
|
+
}
|
|
58
|
+
export declare const DELEGATE_ROUTES: {
|
|
59
|
+
readonly 'delegate.example.export': "authenticated";
|
|
60
|
+
readonly 'delegate.destination': "authenticated";
|
|
61
|
+
readonly 'delegate.select_destination': "authenticated";
|
|
62
|
+
readonly 'delegate.move_from_staging': "authenticated";
|
|
63
|
+
readonly 'delegate.operations.get': "authenticated";
|
|
64
|
+
readonly 'delegate.operations.list': "authenticated";
|
|
65
|
+
readonly 'delegate.operations.cancel': "authenticated";
|
|
66
|
+
readonly 'delegate.disable': "administrators";
|
|
67
|
+
readonly 'delegate.enable': "administrators";
|
|
68
|
+
readonly 'delegate.audit': "authenticated";
|
|
69
|
+
};
|
|
70
|
+
/** Shared, explicit limits; legacy TTL/job limits remain separate. */
|
|
71
|
+
export declare const DELEGATE_LIMITS: {
|
|
72
|
+
readonly maxFileBytes: number;
|
|
73
|
+
readonly maxQueued: 16;
|
|
74
|
+
readonly maxActive: 1;
|
|
75
|
+
readonly maxCopySeconds: 86400;
|
|
76
|
+
readonly maxOperationBytes: number;
|
|
77
|
+
readonly maxAuditBytes: number;
|
|
78
|
+
readonly revalidateSeconds: 5;
|
|
79
|
+
};
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/** Frozen schema-2 / v1 extension. Definitions alone do not enable capabilities. */
|
|
2
|
+
export const MOVE_FROM_STAGING = 'files.moveFromStaging';
|
|
3
|
+
export const DELEGATE_EXTENSION_OPERATIONS = [
|
|
4
|
+
MOVE_FROM_STAGING, 'files.operationStatus', 'files.cancelOperation',
|
|
5
|
+
'delegate.disable', 'delegate.enable', 'delegate.audit',
|
|
6
|
+
];
|
|
7
|
+
export const DELEGATE_OPERATION_STATES = [
|
|
8
|
+
'queued', 'copying', 'committed', 'canceled', 'interrupted', 'uncertain',
|
|
9
|
+
];
|
|
10
|
+
export const DELEGATE_ROUTES = {
|
|
11
|
+
'delegate.example.export': 'authenticated',
|
|
12
|
+
'delegate.destination': 'authenticated',
|
|
13
|
+
'delegate.select_destination': 'authenticated',
|
|
14
|
+
'delegate.move_from_staging': 'authenticated',
|
|
15
|
+
'delegate.operations.get': 'authenticated',
|
|
16
|
+
'delegate.operations.list': 'authenticated',
|
|
17
|
+
'delegate.operations.cancel': 'authenticated',
|
|
18
|
+
'delegate.disable': 'administrators',
|
|
19
|
+
'delegate.enable': 'administrators',
|
|
20
|
+
'delegate.audit': 'authenticated',
|
|
21
|
+
};
|
|
22
|
+
/** Shared, explicit limits; legacy TTL/job limits remain separate. */
|
|
23
|
+
export const DELEGATE_LIMITS = {
|
|
24
|
+
maxFileBytes: 16 * 1024 ** 3, maxQueued: 16, maxActive: 1,
|
|
25
|
+
maxCopySeconds: 86400, maxOperationBytes: 10 * 1024 ** 2,
|
|
26
|
+
maxAuditBytes: 50 * 1024 ** 2, revalidateSeconds: 5,
|
|
27
|
+
};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type
|
|
1
|
+
import { type NativeArchFamily } from '../build-spk/dsm-arch.js';
|
|
2
2
|
import type { CommandRunner } from '../standalone/build.js';
|
|
3
3
|
import type { StandaloneManifest } from '../standalone/manifest.js';
|
|
4
4
|
import { type ExecutorPolicy } from './executor-policy.js';
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import { existsSync, mkdirSync, readFileSync } from 'node:fs';
|
|
2
2
|
import { join } from 'node:path';
|
|
3
3
|
import { templatesRoot } from '../template-paths.js';
|
|
4
|
+
import { goBuildEnv } from '../build-spk/dsm-arch.js';
|
|
4
5
|
import { StandaloneBuildError } from '../standalone/spk.js';
|
|
5
6
|
import { checkCgiBinary } from '../standalone/binary.js';
|
|
6
|
-
import { executorPolicy, sha256 } from './executor-policy.js';
|
|
7
|
+
import { executorPolicy, executorPolicyText, sha256 } from './executor-policy.js';
|
|
7
8
|
import { executorTasks } from './executor-tasks.js';
|
|
8
9
|
export function buildExecutor(m, arch, work, run) {
|
|
9
10
|
if (!m.capabilities.delegate && !m.capabilities.rootOps)
|
|
@@ -14,20 +15,20 @@ export function buildExecutor(m, arch, work, run) {
|
|
|
14
15
|
if (!existsSync(join(source, 'go.mod')))
|
|
15
16
|
throw new StandaloneBuildError('EXECUTOR_BUILD_FAILED', 'Executor sources are missing.');
|
|
16
17
|
const binary = join(dir, 'executor');
|
|
17
|
-
const result = run('go', ['build', '-trimpath', '-buildvcs=false', '-ldflags', '-s -w -buildid=', '-o', binary, '.'], { cwd: source, env: { ...process.env,
|
|
18
|
+
const result = run('go', ['build', '-trimpath', '-buildvcs=false', '-ldflags', '-s -w -buildid=', '-o', binary, '.'], { cwd: source, env: { ...process.env, ...goBuildEnv(arch) } });
|
|
18
19
|
if (result.missing || result.status !== 0)
|
|
19
20
|
throw new StandaloneBuildError('EXECUTOR_BUILD_FAILED', `Executor build failed (${arch}). ${result.stderr}`);
|
|
20
21
|
const problem = checkCgiBinary(work, binary, arch);
|
|
21
22
|
if (problem)
|
|
22
23
|
throw new StandaloneBuildError(problem.code, problem.message);
|
|
23
24
|
const lifecycleBinary = join(dir, 'lifecycle');
|
|
24
|
-
const lifecycleResult = run('go', ['build', '-trimpath', '-buildvcs=false', '-ldflags', '-s -w -buildid=', '-o', lifecycleBinary, './cmd/lifecycle'], { cwd: source, env: { ...process.env,
|
|
25
|
+
const lifecycleResult = run('go', ['build', '-trimpath', '-buildvcs=false', '-ldflags', '-s -w -buildid=', '-o', lifecycleBinary, './cmd/lifecycle'], { cwd: source, env: { ...process.env, ...goBuildEnv(arch) } });
|
|
25
26
|
if (lifecycleResult.missing || lifecycleResult.status !== 0)
|
|
26
27
|
throw new StandaloneBuildError('EXECUTOR_BUILD_FAILED', `Lifecycle helper build failed (${arch}). ${lifecycleResult.stderr}`);
|
|
27
28
|
const lifecycleProblem = checkCgiBinary(work, lifecycleBinary, arch);
|
|
28
29
|
if (lifecycleProblem)
|
|
29
30
|
throw new StandaloneBuildError(lifecycleProblem.code, lifecycleProblem.message);
|
|
30
|
-
const policy = executorPolicy(m), policyText =
|
|
31
|
+
const policy = executorPolicy(m), policyText = executorPolicyText(policy);
|
|
31
32
|
const pins = { package: m.package, executorDigest: sha256(readFileSync(binary)), policyDigest: sha256(policyText) };
|
|
32
33
|
return { ...pins, binary, lifecycleBinary, policy, policyText, tasks: executorTasks(pins), implementation: 'executor' };
|
|
33
34
|
}
|
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
import { type ExecutorPolicy } from './executor-policy.js';
|
|
2
2
|
import { type ExecutorTask } from './executor-tasks.js';
|
|
3
|
+
/** INFO key that anchors the executor digest; Package Center writes INFO as root. */
|
|
4
|
+
export declare const EXECUTOR_ANCHOR = "synopkgland_executor_sha256";
|
|
5
|
+
export declare function inspectExecutorAnchor(info: string, executorDigest: string): string[];
|
|
3
6
|
export interface ExecutorInspection {
|
|
4
7
|
executorDigest: string;
|
|
5
8
|
policyDigest: string;
|
|
@@ -1,19 +1,25 @@
|
|
|
1
1
|
import { existsSync, lstatSync, readFileSync } from 'node:fs';
|
|
2
2
|
import { join } from 'node:path';
|
|
3
|
-
import {
|
|
3
|
+
import { validExecutorPolicy, sha256 } from './executor-policy.js';
|
|
4
|
+
import { parseDocument } from 'yaml';
|
|
4
5
|
import { executorTasks } from './executor-tasks.js';
|
|
5
6
|
import { familyOfArchToken } from '../build-spk/dsm-arch.js';
|
|
6
7
|
import { ELF_MACHINE } from '../standalone/binary.js';
|
|
8
|
+
/** INFO key that anchors the executor digest; Package Center writes INFO as root. */
|
|
9
|
+
export const EXECUTOR_ANCHOR = 'synopkgland_executor_sha256';
|
|
10
|
+
export function inspectExecutorAnchor(info, executorDigest) {
|
|
11
|
+
const anchors = info.split('\n').filter(line => line.startsWith(`${EXECUTOR_ANCHOR}=`));
|
|
12
|
+
return anchors.length === 1 && anchors[0] === `${EXECUTOR_ANCHOR}="${executorDigest}"` ? [] : ['Executor INFO anchor must name the shipped executor digest exactly once.'];
|
|
13
|
+
}
|
|
7
14
|
export function inspectExecutor(dir, pkg, arch) {
|
|
8
15
|
try {
|
|
9
16
|
const binary = readFileSync(join(dir, 'executor/executor'));
|
|
10
|
-
if (!binary.subarray(0, 4).equals(Buffer.from([127, 69, 76, 70])) || binary.readUInt16LE(18) !==
|
|
17
|
+
if (!binary.subarray(0, 4).equals(Buffer.from([127, 69, 76, 70])) || binary.readUInt16LE(18) !== ELF_MACHINE[familyOfArchToken(arch.split(' ')[0]) ?? 'x86_64'])
|
|
11
18
|
throw new Error('Executor ELF architecture mismatch.');
|
|
12
19
|
const text = readFileSync(join(dir, 'executor/policy.json'), 'utf8');
|
|
13
20
|
const plan = JSON.parse(readFileSync(join(dir, 'executor/setup.json'), 'utf8'));
|
|
14
21
|
const policy = JSON.parse(text);
|
|
15
|
-
|
|
16
|
-
if (policy.v !== 1 || policy.app !== `SYNO.SDS.${pkg}.Instance` || policy.package !== pkg || !Array.isArray(policy.operations) || !policy.operations.includes('status') || policy.operations.some(op => !allowed.includes(op)) || new Set(policy.operations).size !== policy.operations.length || !policy.root || typeof policy.root !== 'object' || Array.isArray(policy.root))
|
|
22
|
+
if (parseDocument(text, { uniqueKeys: true }).errors.length || !validExecutorPolicy(policy, pkg))
|
|
17
23
|
throw new Error('Invalid executor policy.');
|
|
18
24
|
for (const [name, digest] of Object.entries(policy.root)) {
|
|
19
25
|
const path = `root/${name}.sh`;
|
|
@@ -21,7 +27,7 @@ export function inspectExecutor(dir, pkg, arch) {
|
|
|
21
27
|
throw new Error('Root script digest mismatch.');
|
|
22
28
|
}
|
|
23
29
|
const executorDigest = sha256(binary), policyDigest = sha256(text);
|
|
24
|
-
const tasks = executorTasks({ package: pkg,
|
|
30
|
+
const tasks = executorTasks({ package: pkg, policyDigest });
|
|
25
31
|
if (plan.package !== pkg || plan.executorDigest !== executorDigest || plan.policyDigest !== policyDigest || JSON.stringify(plan.policy) !== JSON.stringify(policy) || JSON.stringify(plan.tasks) !== JSON.stringify(tasks) || plan.implementation !== 'executor')
|
|
26
32
|
throw new Error('Executor pins or task binding mismatch.');
|
|
27
33
|
return { executor: { executorDigest, policyDigest, policy, implementation: plan.implementation, tasks: tasks.map(task => ({ ...task, state: 'unknown' })) }, findings: [] };
|
|
@@ -1,25 +1,37 @@
|
|
|
1
1
|
import type { RootOp } from '../standalone/root-ops.js';
|
|
2
|
+
import type { EntryMode } from '../standalone/manifest.js';
|
|
2
3
|
export declare const FILE_OPERATIONS: readonly ["files.list", "files.info", "files.createFolder", "files.checkPermission", "files.write"];
|
|
3
4
|
export interface DelegateCapability {
|
|
4
5
|
operations: string[];
|
|
5
6
|
scope: string;
|
|
7
|
+
destinations?: string[];
|
|
8
|
+
lifetime?: 'until-revoked';
|
|
6
9
|
}
|
|
7
10
|
export declare function validDelegate(value: unknown): value is DelegateCapability;
|
|
8
11
|
export declare const sha256: (data: string | Buffer) => string;
|
|
9
12
|
export interface ExecutorPolicy {
|
|
10
13
|
v: 1;
|
|
11
14
|
app: string;
|
|
15
|
+
/** Present only with a manifest `entry`; the executor then applies the same App Privileges rule offline. */
|
|
16
|
+
entry?: EntryMode;
|
|
12
17
|
scope?: string;
|
|
18
|
+
destinations?: string[];
|
|
19
|
+
lifetime?: 'until-revoked';
|
|
13
20
|
package: string;
|
|
14
21
|
operations: string[];
|
|
15
22
|
root: Record<string, string>;
|
|
16
23
|
}
|
|
24
|
+
/** The exact policy file bytes whose sha256 is the policy pin. */
|
|
25
|
+
export declare const executorPolicyText: (policy: ExecutorPolicy) => string;
|
|
17
26
|
/** Version-independent, sorted policy. Only §2.4 operation IDs are admitted. */
|
|
18
27
|
export declare function executorPolicy(m: {
|
|
19
28
|
package: string;
|
|
29
|
+
entry?: EntryMode;
|
|
20
30
|
capabilities: {
|
|
21
31
|
delegate?: DelegateCapability;
|
|
22
32
|
rootOps?: true;
|
|
23
33
|
};
|
|
24
34
|
rootOps?: RootOp[];
|
|
25
35
|
}): ExecutorPolicy;
|
|
36
|
+
/** Inventory rejects malformed extensions even if a caller recomputes every pin. */
|
|
37
|
+
export declare function validExecutorPolicy(value: unknown, pkg: string): value is ExecutorPolicy;
|
|
@@ -1,24 +1,64 @@
|
|
|
1
1
|
import { createHash } from 'node:crypto';
|
|
2
|
+
import { MOVE_FROM_STAGING, DELEGATE_EXTENSION_OPERATIONS } from './delegate-contract.js';
|
|
2
3
|
export const FILE_OPERATIONS = ['files.list', 'files.info', 'files.createFolder', 'files.checkPermission', 'files.write'];
|
|
3
4
|
export function validDelegate(value) {
|
|
4
5
|
if (!value || typeof value !== 'object' || Array.isArray(value))
|
|
5
6
|
return false;
|
|
6
7
|
const d = value;
|
|
7
|
-
|
|
8
|
+
if (!Object.keys(d).every(key => ['operations', 'scope', 'destinations', 'lifetime'].includes(key)) || !Array.isArray(d.operations) || d.operations.length === 0 || new Set(d.operations).size !== d.operations.length || typeof d.scope !== 'string' || !(d.scope === 'home' || d.scope === 'install-selected' && d.lifetime === 'until-revoked' || /^shared:[^/\\\x00-\x1f:]+$/.test(d.scope) && d.scope.slice(7).trim() === d.scope.slice(7) && !['.', '..'].includes(d.scope.slice(7))))
|
|
9
|
+
return false;
|
|
10
|
+
if ('destinations' in d || 'lifetime' in d || d.operations.includes(MOVE_FROM_STAGING)) {
|
|
11
|
+
return d.lifetime === 'until-revoked' && d.operations.length === 1 && d.operations[0] === MOVE_FROM_STAGING
|
|
12
|
+
&& Array.isArray(d.destinations) && d.destinations.length > 0 && d.destinations.length <= 64 && new Set(d.destinations).size === d.destinations.length
|
|
13
|
+
&& d.destinations.every(key => typeof key === 'string' && /^[a-z][a-z0-9_-]{0,63}$/.exec(key)?.[0] === key);
|
|
14
|
+
}
|
|
15
|
+
return d.operations.every(op => FILE_OPERATIONS.includes(op));
|
|
8
16
|
}
|
|
9
17
|
export const sha256 = (data) => createHash('sha256').update(data).digest('hex');
|
|
18
|
+
/** The exact policy file bytes whose sha256 is the policy pin. */
|
|
19
|
+
export const executorPolicyText = (policy) => JSON.stringify(policy) + '\n';
|
|
10
20
|
/** Version-independent, sorted policy. Only §2.4 operation IDs are admitted. */
|
|
11
21
|
export function executorPolicy(m) {
|
|
12
22
|
if (m.capabilities.delegate && !validDelegate(m.capabilities.delegate))
|
|
13
23
|
throw new Error('[EXECUTOR_POLICY] Invalid delegate operations or scope.');
|
|
14
24
|
const operations = ['status'];
|
|
15
|
-
if (m.capabilities.delegate)
|
|
25
|
+
if (m.capabilities.delegate) {
|
|
16
26
|
operations.push('delegate.create', 'delegate.list', 'delegate.revoke', ...m.capabilities.delegate.operations);
|
|
27
|
+
if (m.capabilities.delegate.lifetime === 'until-revoked')
|
|
28
|
+
operations.push(...DELEGATE_EXTENSION_OPERATIONS);
|
|
29
|
+
}
|
|
17
30
|
const root = {};
|
|
18
31
|
if (m.capabilities.rootOps) {
|
|
19
32
|
operations.push('root.run');
|
|
20
33
|
for (const op of [...m.rootOps ?? []].sort((a, b) => a.name.localeCompare(b.name, 'en')))
|
|
21
34
|
root[op.name] = sha256(op.body);
|
|
22
35
|
}
|
|
23
|
-
|
|
36
|
+
const d = m.capabilities.delegate;
|
|
37
|
+
return { v: 1, package: m.package, app: `SYNO.SDS.${m.package}.Instance`, ...(m.entry ? { entry: m.entry } : {}), ...(d ? { scope: d.scope } : {}),
|
|
38
|
+
...(d?.lifetime ? { destinations: [...d.destinations].sort(), lifetime: d.lifetime } : {}),
|
|
39
|
+
operations: [...new Set(operations)].sort(), root };
|
|
40
|
+
}
|
|
41
|
+
/** Inventory rejects malformed extensions even if a caller recomputes every pin. */
|
|
42
|
+
export function validExecutorPolicy(value, pkg) {
|
|
43
|
+
if (!value || typeof value !== 'object' || Array.isArray(value))
|
|
44
|
+
return false;
|
|
45
|
+
const p = value;
|
|
46
|
+
if (Object.keys(p).some(key => !['v', 'package', 'app', 'entry', 'scope', 'destinations', 'lifetime', 'operations', 'root'].includes(key))
|
|
47
|
+
|| p.v !== 1 || p.package !== pkg || p.app !== `SYNO.SDS.${pkg}.Instance`
|
|
48
|
+
|| 'entry' in p && p.entry !== 'admin-only' && p.entry !== 'app-privilege'
|
|
49
|
+
|| !Array.isArray(p.operations) || !p.operations.includes('status') || new Set(p.operations).size !== p.operations.length
|
|
50
|
+
|| !p.root || typeof p.root !== 'object' || Array.isArray(p.root))
|
|
51
|
+
return false;
|
|
52
|
+
const base = ['status', 'root.run', 'delegate.create', 'delegate.list', 'delegate.revoke'];
|
|
53
|
+
if (p.operations.some(op => ![...base, ...FILE_OPERATIONS, ...DELEGATE_EXTENSION_OPERATIONS].includes(op)))
|
|
54
|
+
return false;
|
|
55
|
+
const delegated = p.operations.some(op => op.startsWith('files.') || op.startsWith('delegate.'));
|
|
56
|
+
if (!delegated)
|
|
57
|
+
return p.scope === undefined && p.lifetime === undefined && p.destinations === undefined;
|
|
58
|
+
const capability = { scope: p.scope, operations: p.operations.filter(op => FILE_OPERATIONS.includes(op) || op === MOVE_FROM_STAGING),
|
|
59
|
+
...('lifetime' in p ? { lifetime: p.lifetime } : {}), ...('destinations' in p ? { destinations: p.destinations } : {}) };
|
|
60
|
+
if (!validDelegate(capability))
|
|
61
|
+
return false;
|
|
62
|
+
const expected = executorPolicy({ package: pkg, capabilities: { delegate: capability, ...(p.operations.includes('root.run') ? { rootOps: true } : {}) } }).operations;
|
|
63
|
+
return expected.length === p.operations.length && expected.every(op => p.operations.includes(op));
|
|
24
64
|
}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { ExecutorPins, ExecutorTask } from './executor-tasks.js';
|
|
2
|
+
/** Self-contained: the same function is embedded in the DSM window. No draft
|
|
3
|
+
* files are read by this task; the confirmed scope is a literal pinned argument. */
|
|
4
|
+
export declare function executorScopeTask(pins: ExecutorPins, scope: string, actor: string, expectedGeneration: number): ExecutorTask;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/** Self-contained: the same function is embedded in the DSM window. No draft
|
|
2
|
+
* files are read by this task; the confirmed scope is a literal pinned argument. */
|
|
3
|
+
export function executorScopeTask(pins, scope, actor, expectedGeneration) {
|
|
4
|
+
const share = scope?.startsWith('shared:') ? scope.slice(7) : '';
|
|
5
|
+
if (!(/^[A-Za-z][A-Za-z0-9]{1,63}$/.exec(pins.package)?.[0] === pins.package)
|
|
6
|
+
|| ![pins.executorDigest, pins.policyDigest].every(v => typeof v === 'string' && v.length === 64 && /^[a-f0-9]{64}$/.test(v))
|
|
7
|
+
|| !(scope === 'home' || share && share.trim() === share && !/[\/\\:\x00-\x1f\x7f%]/.test(share) && !share.includes('..') && !['.', 'home', 'homes'].includes(share.toLowerCase()))
|
|
8
|
+
|| !Number.isSafeInteger(expectedGeneration) || expectedGeneration < 0
|
|
9
|
+
|| !actor || /[\x00-\x1f\x7f%]/.test(actor))
|
|
10
|
+
throw new Error('executor_scope_invalid');
|
|
11
|
+
const header = '#!/bin/sh\n# synopkgland executor selection\n';
|
|
12
|
+
const script = header + `set -eu
|
|
13
|
+
export PATH=/usr/bin:/bin
|
|
14
|
+
d=/usr/local/etc/synopkgland/${pins.package}/runtime
|
|
15
|
+
{ echo ${pins.executorDigest}" $d/executor"; echo ${pins.policyDigest}" $d/policy.json"; }|sha256sum -c - >/dev/null
|
|
16
|
+
exec "$d/executor" select-scope --package '${pins.package}' --scope '${scope.replaceAll("'", "'\\''")}' --actor '${actor.replaceAll("'", "'\\''")}' --expected-generation ${expectedGeneration}
|
|
17
|
+
`;
|
|
18
|
+
if (new TextEncoder().encode(script).length > 6000)
|
|
19
|
+
throw new Error('executor_scope_invalid');
|
|
20
|
+
return { kind: 'control', name: pins.package + ' executor selection', header, script };
|
|
21
|
+
}
|
|
@@ -11,6 +11,7 @@ export interface ExecutorTask {
|
|
|
11
11
|
script: string;
|
|
12
12
|
}
|
|
13
13
|
export declare function validateTaskScript(script: string, limit: number): string;
|
|
14
|
-
|
|
14
|
+
/** Tasks pin only the approved policy; the SPK's root-owned INFO anchors executor bytes. */
|
|
15
|
+
export declare function executorTasks(pins: Pick<ExecutorPins, 'package' | 'policyDigest'>): ExecutorTask[];
|
|
15
16
|
export declare function bootDefinition(task: ExecutorTask): Record<string, unknown>;
|
|
16
17
|
export declare function controlDefinition(task: ExecutorTask): Record<string, unknown>;
|
|
@@ -8,15 +8,15 @@ export function validateTaskScript(script, limit) {
|
|
|
8
8
|
throw new Error('[EXECUTOR_TASK_LENGTH] Task script exceeds DSM byte limit.');
|
|
9
9
|
return script;
|
|
10
10
|
}
|
|
11
|
+
/** Tasks pin only the approved policy; the SPK's root-owned INFO anchors executor bytes. */
|
|
11
12
|
export function executorTasks(pins) {
|
|
12
13
|
if (!/^[A-Za-z][A-Za-z0-9]{1,63}$/.test(pins.package))
|
|
13
14
|
throw new Error('[EXECUTOR_PACKAGE] Invalid package identity.');
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
throw new Error('[EXECUTOR_DIGEST] Invalid sha256 pin.');
|
|
15
|
+
if (!/^[a-f0-9]{64}$/.test(pins.policyDigest))
|
|
16
|
+
throw new Error('[EXECUTOR_DIGEST] Invalid sha256 pin.');
|
|
17
17
|
return ['boot', 'control'].map(kind => {
|
|
18
18
|
const script = readFileSync(join(templatesRoot(), '_executor', 'internal', 'taskscript', `${kind}.sh`), 'utf8')
|
|
19
|
-
.replaceAll('@PACKAGE@', pins.package).replaceAll('@
|
|
19
|
+
.replaceAll('@PACKAGE@', pins.package).replaceAll('@POLICY_DIGEST@', pins.policyDigest);
|
|
20
20
|
return { kind, name: `${pins.package} executor ${kind}`, header: `#!/bin/sh\n# synopkgland executor ${kind}\n`, script: validateTaskScript(script, kind === 'boot' ? 1024 : 6000) };
|
|
21
21
|
});
|
|
22
22
|
}
|
package/dist/skill.js
CHANGED
|
@@ -7,6 +7,7 @@ const SKILL_CATALOG = [
|
|
|
7
7
|
{ id: "wrap", file: "reference/wrap.md", when: "已有 Compose 或要補契約" },
|
|
8
8
|
{ id: "identity", file: "reference/identity.md", when: "需要 DSM 登入者" },
|
|
9
9
|
{ id: "files", file: "reference/files.md", when: "需要 NAS 檔案" },
|
|
10
|
+
{ id: "portal", file: "reference/portal.md", when: "需要 DSM Application Portal 別名連結" },
|
|
10
11
|
{ id: "ui-vue", file: "reference/ui-vue.md", when: "編寫/檢查 Vue UI" },
|
|
11
12
|
{ id: "ui-json", file: "reference/ui-json.md", when: "產生/檢查 JSON UI" },
|
|
12
13
|
{ id: "deploy", file: "reference/deploy.md", when: "要放到 NAS" },
|
|
@@ -14,5 +14,15 @@ export declare function readApiPermissions(backendDir: string, capabilities?: Ca
|
|
|
14
14
|
digest: string;
|
|
15
15
|
sdkDigest: string;
|
|
16
16
|
};
|
|
17
|
+
/**
|
|
18
|
+
* `entry: admin-only` makes administrators the floor of every route. App
|
|
19
|
+
* routes that would admit anyone else are returned as conflicts; SDK routes a
|
|
20
|
+
* capability added are raised, except the public login ceremony of
|
|
21
|
+
* `identity: standalone`, which admits no one by itself.
|
|
22
|
+
*/
|
|
23
|
+
export declare function floorAtAdministrators(policy: ApiPermissions): {
|
|
24
|
+
policy: ApiPermissions;
|
|
25
|
+
widened: string[];
|
|
26
|
+
};
|
|
17
27
|
/** Launcher access is derived from the same API policy; no second setting. */
|
|
18
28
|
export declare function permitsOrdinaryUsers(policy?: ApiPermissions): boolean;
|
|
@@ -4,6 +4,7 @@ import { join } from "node:path";
|
|
|
4
4
|
import { templatesRoot } from "../template-paths.js";
|
|
5
5
|
import { parseDocument } from "yaml";
|
|
6
6
|
export const API_PERMISSIONS_FILE = "internal/api/permissions.json";
|
|
7
|
+
const SDK_PERMISSIONS_TEMPLATE = "native-go/backend/internal/api/sdk-permissions.json";
|
|
7
8
|
export const MAX_PERMISSIONS_BYTES = 65536;
|
|
8
9
|
export const MAX_PERMISSION_METHODS = 1024;
|
|
9
10
|
export function parseApiPermissions(raw) {
|
|
@@ -37,7 +38,7 @@ export function readApiPermissions(backendDir, capabilities = {}) {
|
|
|
37
38
|
throw new Error("permissions must be a regular bounded JSON file");
|
|
38
39
|
const raw = readFileSync(file);
|
|
39
40
|
const policy = parseApiPermissions(raw);
|
|
40
|
-
const sdkRaw = readFileSync(join(templatesRoot(),
|
|
41
|
+
const sdkRaw = readFileSync(join(templatesRoot(), SDK_PERMISSIONS_TEMPLATE));
|
|
41
42
|
const sdkFile = join(backendDir, 'internal/api/sdk-permissions.json');
|
|
42
43
|
if (existsSync(sdkFile)) {
|
|
43
44
|
const sdkStat = lstatSync(sdkFile);
|
|
@@ -45,7 +46,7 @@ export function readApiPermissions(backendDir, capabilities = {}) {
|
|
|
45
46
|
throw new Error('SDK route policy differs from the CLI policy; restore the SDK-owned file');
|
|
46
47
|
}
|
|
47
48
|
}
|
|
48
|
-
const sdk =
|
|
49
|
+
const sdk = readSdkPermissions();
|
|
49
50
|
for (const methods of Object.values(sdk))
|
|
50
51
|
for (const method of Object.keys(methods)) {
|
|
51
52
|
if (Object.hasOwn(policy.methods, method))
|
|
@@ -57,6 +58,22 @@ export function readApiPermissions(backendDir, capabilities = {}) {
|
|
|
57
58
|
}
|
|
58
59
|
return { policy, sdkDigest: createHash("sha256").update(sdkRaw).digest("hex"), digest: createHash("sha256").update(raw).digest("hex") };
|
|
59
60
|
}
|
|
61
|
+
/**
|
|
62
|
+
* `entry: admin-only` makes administrators the floor of every route. App
|
|
63
|
+
* routes that would admit anyone else are returned as conflicts; SDK routes a
|
|
64
|
+
* capability added are raised, except the public login ceremony of
|
|
65
|
+
* `identity: standalone`, which admits no one by itself.
|
|
66
|
+
*/
|
|
67
|
+
export function floorAtAdministrators(policy) {
|
|
68
|
+
const sdk = new Set(Object.values(readSdkPermissions()).flatMap(methods => Object.keys(methods)));
|
|
69
|
+
const methods = Object.fromEntries(Object.entries(policy.methods)
|
|
70
|
+
.map(([method, permission]) => [method, sdk.has(method) && permission === "public" ? permission : "administrators"]));
|
|
71
|
+
const widened = Object.entries(policy.methods).filter(([method, permission]) => !sdk.has(method) && permission !== "administrators").map(([method]) => method);
|
|
72
|
+
return { policy: { ...policy, methods }, widened };
|
|
73
|
+
}
|
|
74
|
+
function readSdkPermissions() {
|
|
75
|
+
return JSON.parse(readFileSync(join(templatesRoot(), SDK_PERMISSIONS_TEMPLATE), 'utf8'));
|
|
76
|
+
}
|
|
60
77
|
/** Launcher access is derived from the same API policy; no second setting. */
|
|
61
78
|
export function permitsOrdinaryUsers(policy) {
|
|
62
79
|
return policy !== undefined && Object.values(policy.methods).some(permission => permission !== "administrators");
|
|
@@ -17,5 +17,21 @@ export declare function readBackground(value: unknown, fail: (error: StandaloneE
|
|
|
17
17
|
/** `conf/resource`: only the official user-unit resource. */
|
|
18
18
|
export declare function renderBackgroundResource(): string;
|
|
19
19
|
export declare function renderBackgroundUnit(pkg: string): string;
|
|
20
|
+
/** Shell function that stops the worker in lifecycle hooks; call it as `stop_background_worker`. */
|
|
21
|
+
export declare function renderBackgroundWorkerStop(pkg: string): string;
|
|
22
|
+
/**
|
|
23
|
+
* Shell function for `prestart`, which DSM runs before its systemd-user-unit
|
|
24
|
+
* acquire: call it as `recover_user_manager`.
|
|
25
|
+
*
|
|
26
|
+
* On a restart under load, pkgctl's `Requires=user@UID` can start the package
|
|
27
|
+
* user's new manager in the old runtime dir just before logind removes it.
|
|
28
|
+
* That manager stays active without its private socket, so every later
|
|
29
|
+
* synosystemctl call fails with "Failed to get D-Bus connection" until it
|
|
30
|
+
* exits. Stop only such a manager, as the package user; the acquire that
|
|
31
|
+
* follows then starts a fresh one.
|
|
32
|
+
*/
|
|
33
|
+
export declare function renderUserManagerRecovery(): string;
|
|
34
|
+
/** Shell function that starts the worker and names the failed step; call it as `start_background_worker`. */
|
|
35
|
+
export declare function renderBackgroundWorkerStart(pkg: string): string;
|
|
20
36
|
/** Package Center's start/stop/status map straight onto the user unit. */
|
|
21
37
|
export declare function renderBackgroundControl(pkg: string): string;
|
|
@@ -35,12 +35,79 @@ TimeoutStopSec=10
|
|
|
35
35
|
Restart=no
|
|
36
36
|
`;
|
|
37
37
|
}
|
|
38
|
+
/** Shell function that stops the worker in lifecycle hooks; call it as `stop_background_worker`. */
|
|
39
|
+
export function renderBackgroundWorkerStop(pkg) {
|
|
40
|
+
return `stop_background_worker() {
|
|
41
|
+
${SYNOSYSTEMCTL} stop ${backgroundUnitName(pkg)} && return 0
|
|
42
|
+
# DSM's native uninstall stops the unit, then releases the package user's
|
|
43
|
+
# systemd manager before preuninst, so a repeated stop fails. That is only
|
|
44
|
+
# "already stopped" when no process runs the worker executable.
|
|
45
|
+
[ -d /proc/self ] || return 1
|
|
46
|
+
worker="$(readlink -f "\${SYNOPKG_PKGDEST:-/var/packages/${pkg}/target}/${BACKGROUND_WORKER_PAYLOAD}")" || return 1
|
|
47
|
+
for exe in /proc/[0-9]*/exe; do
|
|
48
|
+
case "$(readlink "$exe" 2>/dev/null)" in
|
|
49
|
+
"$worker"|"$worker (deleted)") echo "background worker still running" >&2; return 1 ;;
|
|
50
|
+
esac
|
|
51
|
+
done
|
|
52
|
+
echo "background worker already stopped" >&2
|
|
53
|
+
}
|
|
54
|
+
`;
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Shell function for `prestart`, which DSM runs before its systemd-user-unit
|
|
58
|
+
* acquire: call it as `recover_user_manager`.
|
|
59
|
+
*
|
|
60
|
+
* On a restart under load, pkgctl's `Requires=user@UID` can start the package
|
|
61
|
+
* user's new manager in the old runtime dir just before logind removes it.
|
|
62
|
+
* That manager stays active without its private socket, so every later
|
|
63
|
+
* synosystemctl call fails with "Failed to get D-Bus connection" until it
|
|
64
|
+
* exits. Stop only such a manager, as the package user; the acquire that
|
|
65
|
+
* follows then starts a fresh one.
|
|
66
|
+
*/
|
|
67
|
+
export function renderUserManagerRecovery() {
|
|
68
|
+
return `recover_user_manager() {
|
|
69
|
+
uid="$(id -u)" || return 1
|
|
70
|
+
[ "$uid" != 0 ] || return 0
|
|
71
|
+
i=0
|
|
72
|
+
while [ "$(/usr/bin/loginctl show-user "$uid" -p State 2>/dev/null)" = State=closing ] && [ "$i" -lt 100 ]; do
|
|
73
|
+
sleep 0.1; i=$((i + 1))
|
|
74
|
+
done
|
|
75
|
+
[ -S "/run/user/$uid/systemd/private" ] && return 0
|
|
76
|
+
for exe in /proc/[0-9]*/exe; do
|
|
77
|
+
pid="\${exe%/exe}"; pid="\${pid##*/}"
|
|
78
|
+
[ -O "/proc/$pid" ] && [ "$(readlink "$exe" 2>/dev/null)" = /usr/lib/systemd/systemd ] || continue
|
|
79
|
+
case " $(tr '\\0' ' ' < "/proc/$pid/cmdline" 2>/dev/null)" in *" --user "*) ;; *) continue ;; esac
|
|
80
|
+
echo "package user manager $pid has no socket in /run/user/$uid; stopping it so DSM starts a fresh one" >&2
|
|
81
|
+
kill -TERM "$pid" || return 1
|
|
82
|
+
i=0
|
|
83
|
+
while kill -0 "$pid" 2>/dev/null; do
|
|
84
|
+
[ "$i" -lt 100 ] || { echo "package user manager $pid did not exit" >&2; return 1; }
|
|
85
|
+
sleep 0.1; i=$((i + 1))
|
|
86
|
+
done
|
|
87
|
+
done
|
|
88
|
+
}
|
|
89
|
+
`;
|
|
90
|
+
}
|
|
91
|
+
/** Shell function that starts the worker and names the failed step; call it as `start_background_worker`. */
|
|
92
|
+
export function renderBackgroundWorkerStart(pkg) {
|
|
93
|
+
const unit = backgroundUnitName(pkg);
|
|
94
|
+
return `start_background_worker() {
|
|
95
|
+
${SYNOSYSTEMCTL} start ${unit} && return 0
|
|
96
|
+
rc=$?; socket=missing
|
|
97
|
+
[ -S "/run/user/$(id -u)/systemd/private" ] && socket=present
|
|
98
|
+
echo "background worker ${unit} failed to start (exit $rc; package user manager socket $socket)" >&2
|
|
99
|
+
return "$rc"
|
|
100
|
+
}
|
|
101
|
+
`;
|
|
102
|
+
}
|
|
38
103
|
/** Package Center's start/stop/status map straight onto the user unit. */
|
|
39
104
|
export function renderBackgroundControl(pkg) {
|
|
40
105
|
const unit = backgroundUnitName(pkg);
|
|
41
106
|
return `#!/bin/sh
|
|
42
|
-
case "$1" in
|
|
43
|
-
|
|
107
|
+
${renderUserManagerRecovery()}${renderBackgroundWorkerStart(pkg)}case "$1" in
|
|
108
|
+
prestart) recover_user_manager ;;
|
|
109
|
+
prestop) exit 0 ;;
|
|
110
|
+
start) start_background_worker ;;
|
|
44
111
|
stop) exec ${SYNOSYSTEMCTL} stop ${unit} ;;
|
|
45
112
|
status)
|
|
46
113
|
[ "$(${SYNOSYSTEMCTL} get-active-status ${unit})" = active ] && exit 0
|
|
@@ -3,7 +3,7 @@ import type { StandaloneError } from "./manifest.js";
|
|
|
3
3
|
export declare const ELF_MACHINE: Record<NativeArchFamily, number>;
|
|
4
4
|
/**
|
|
5
5
|
* Checks a prebuilt CGI before it is packaged: a regular file inside the
|
|
6
|
-
* project, a
|
|
6
|
+
* project, a little-endian ELF of `arch`'s class and machine, and statically linked
|
|
7
7
|
* (no PT_INTERP), because DSM models ship different libc versions.
|
|
8
8
|
* Returns undefined when the file is acceptable.
|
|
9
9
|
*/
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { closeSync, lstatSync, openSync, readSync, realpathSync } from "node:fs";
|
|
2
2
|
import { relative } from "node:path";
|
|
3
|
-
export const ELF_MACHINE = { x86_64: 62, aarch64: 183 };
|
|
3
|
+
export const ELF_MACHINE = { x86_64: 62, aarch64: 183, armv7: 40 };
|
|
4
|
+
const ELF_CLASS64 = { x86_64: true, aarch64: true, armv7: false };
|
|
4
5
|
const PT_INTERP = 3;
|
|
5
6
|
const MAX_BINARY_BYTES = 256 * 1024 * 1024;
|
|
6
7
|
const HEADER_BYTES = 64 * 1024;
|
|
@@ -9,7 +10,7 @@ function fail(code, message) {
|
|
|
9
10
|
}
|
|
10
11
|
/**
|
|
11
12
|
* Checks a prebuilt CGI before it is packaged: a regular file inside the
|
|
12
|
-
* project, a
|
|
13
|
+
* project, a little-endian ELF of `arch`'s class and machine, and statically linked
|
|
13
14
|
* (no PT_INTERP), because DSM models ship different libc versions.
|
|
14
15
|
* Returns undefined when the file is acceptable.
|
|
15
16
|
*/
|
|
@@ -43,18 +44,31 @@ function readHeader(file) {
|
|
|
43
44
|
closeSync(fd);
|
|
44
45
|
}
|
|
45
46
|
}
|
|
47
|
+
/** The little-endian Linux ELF header fields the checks need, for either ELF class. */
|
|
48
|
+
function readElfHeader(buf) {
|
|
49
|
+
if (buf.length < 52 || buf.toString("latin1", 0, 4) !== "\x7fELF" || buf[5] !== 1) {
|
|
50
|
+
return undefined;
|
|
51
|
+
}
|
|
52
|
+
const machine = buf.readUInt16LE(18);
|
|
53
|
+
if (buf[4] === 2 && buf.length >= 64) {
|
|
54
|
+
return { class64: true, machine, phoff: Number(buf.readBigUInt64LE(32)), phentsize: buf.readUInt16LE(54), phnum: buf.readUInt16LE(56) };
|
|
55
|
+
}
|
|
56
|
+
if (buf[4] === 1) {
|
|
57
|
+
return { class64: false, machine, phoff: buf.readUInt32LE(28), phentsize: buf.readUInt16LE(42), phnum: buf.readUInt16LE(44) };
|
|
58
|
+
}
|
|
59
|
+
return undefined;
|
|
60
|
+
}
|
|
46
61
|
function checkElf(buf, file, arch) {
|
|
47
|
-
const
|
|
48
|
-
|
|
62
|
+
const bits = ELF_CLASS64[arch] ? 64 : 32;
|
|
63
|
+
const notElf = fail("STANDALONE_BINARY_NOT_ELF", `${file} 不是 ${arch} 需要的 ${bits}-bit little-endian Linux ELF 執行檔。`);
|
|
64
|
+
const header = readElfHeader(buf);
|
|
65
|
+
if (!header || header.class64 !== ELF_CLASS64[arch]) {
|
|
49
66
|
return notElf;
|
|
50
67
|
}
|
|
51
|
-
const machine =
|
|
68
|
+
const { machine, phoff, phentsize, phnum } = header;
|
|
52
69
|
if (machine !== ELF_MACHINE[arch]) {
|
|
53
70
|
return fail("STANDALONE_BINARY_ARCH_MISMATCH", `${file} 的 ELF machine 是 ${machine},不是 ${arch}。`);
|
|
54
71
|
}
|
|
55
|
-
const phoff = Number(buf.readBigUInt64LE(32));
|
|
56
|
-
const phentsize = buf.readUInt16LE(54);
|
|
57
|
-
const phnum = buf.readUInt16LE(56);
|
|
58
72
|
if (phentsize < 4 || phoff + phentsize * phnum > buf.length) {
|
|
59
73
|
return notElf;
|
|
60
74
|
}
|