@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
package/dist/standalone/build.js
CHANGED
|
@@ -2,7 +2,8 @@ import { spawnSync } from "node:child_process";
|
|
|
2
2
|
import { existsSync, mkdtempSync, rmSync } from "node:fs";
|
|
3
3
|
import { tmpdir } from "node:os";
|
|
4
4
|
import { join } from "node:path";
|
|
5
|
-
import {
|
|
5
|
+
import { goBuildEnv } from "../build-spk/dsm-arch.js";
|
|
6
|
+
import { resolveMinDsm, DEFAULT_MIN_DSM, isDsmAtLeast, PORTAL_MIN_DSM, portalMinDsmMessage } from "../build-spk/dsm-version.js";
|
|
6
7
|
import { checkCgiBinary } from "./binary.js";
|
|
7
8
|
import { loadStandaloneManifest } from "./manifest.js";
|
|
8
9
|
import { assembleStandaloneSpk, StandaloneBuildError } from "./spk.js";
|
|
@@ -10,7 +11,6 @@ import { buildDaemonControl } from "./daemon.js";
|
|
|
10
11
|
import { buildExecutor } from "../runtime/executor-build.js";
|
|
11
12
|
import { BACKGROUND_WORKER_MAIN } from "./background.js";
|
|
12
13
|
import { readApiPermissions } from "./api-permissions.js";
|
|
13
|
-
const GOARCH = { x86_64: "amd64", aarch64: "arm64" };
|
|
14
14
|
/** Argv only, never a shell; output streams to the terminal except stderr, which is also kept. */
|
|
15
15
|
export const runCommand = (command, args, options) => {
|
|
16
16
|
const result = spawnSync(command, args, {
|
|
@@ -38,11 +38,14 @@ export async function buildStandalone(options) {
|
|
|
38
38
|
if (manifest.capabilities.rootOps === true && !isDsmAtLeast(manifest.os_min_ver, DEFAULT_MIN_DSM)) {
|
|
39
39
|
throw new StandaloneBuildError("STANDALONE_OS_MIN_VER_INVALID", `root templates require DSM ${DEFAULT_MIN_DSM} or later.`);
|
|
40
40
|
}
|
|
41
|
+
if (manifest.portal && !isDsmAtLeast(manifest.os_min_ver, PORTAL_MIN_DSM)) {
|
|
42
|
+
throw new StandaloneBuildError("STANDALONE_PORTAL_MIN_DSM", `[STANDALONE_PORTAL_MIN_DSM] ${portalMinDsmMessage(manifest.os_min_ver, "portal")}`);
|
|
43
|
+
}
|
|
41
44
|
const run = options.run ?? runCommand;
|
|
42
45
|
const arches = options.arches ?? manifest.arches;
|
|
43
46
|
for (const arch of arches) {
|
|
44
47
|
if (!manifest.arches.includes(arch)) {
|
|
45
|
-
throw new StandaloneBuildError("STANDALONE_ARCH_NOT_DECLARED", `${arch} 不在 dsm-package.yaml 的 arches
|
|
48
|
+
throw new StandaloneBuildError("STANDALONE_ARCH_NOT_DECLARED", `${arch} 不在 dsm-package.yaml 的 arches 內;要建置它,請先把 ${arch} 加進 arches。`);
|
|
46
49
|
}
|
|
47
50
|
}
|
|
48
51
|
buildUi(manifest, run);
|
|
@@ -60,7 +63,7 @@ export async function buildStandalone(options) {
|
|
|
60
63
|
}
|
|
61
64
|
const outputPath = join(outDir, `${manifest.package}-${manifest.spkVersion}-${arch}.spk`);
|
|
62
65
|
const daemonControl = buildDaemonControl(manifest, arch, work, run);
|
|
63
|
-
const backgroundWorker = buildBackgroundWorker(
|
|
66
|
+
const backgroundWorker = buildBackgroundWorker(builtManifest, arch, work, run);
|
|
64
67
|
await assembleStandaloneSpk({ manifest: builtManifest, arch, cgiPath, outputPath, daemonControl, backgroundWorker, executor });
|
|
65
68
|
outputs.push(outputPath);
|
|
66
69
|
}
|
|
@@ -103,14 +106,16 @@ function backendFor(manifest, arch, work, run) {
|
|
|
103
106
|
`-X main.version=${manifest.spkVersion}`,
|
|
104
107
|
`-X dsmapp/internal/api.expectedPermissionsDigest=${manifest.apiPermissionsDigest}`,
|
|
105
108
|
`-X dsmapp/internal/api.expectedSDKPermissionsDigest=${manifest.apiSDKPermissionsDigest}`,
|
|
106
|
-
...(manifest
|
|
109
|
+
...delegateBuildFlags(manifest),
|
|
107
110
|
...(manifest.executor ? [`-X main.executorDigest=${manifest.executor.executorDigest}`, `-X main.policyDigest=${manifest.executor.policyDigest}`] : []),
|
|
108
111
|
...(manifest.capabilities.identity ? [`-X main.identity=${manifest.capabilities.identity}`] : []),
|
|
109
112
|
...(manifest.capabilities.files || manifest.capabilities.delegate ? [`-X main.fileStation=session`] : []),
|
|
113
|
+
...(manifest.entry ? [`-X main.entry=${manifest.entry}`] : []),
|
|
114
|
+
...(manifest.portal ? ["-X main.portal=on"] : []),
|
|
110
115
|
].join(" ");
|
|
111
116
|
const result = run("go", ["build", "-trimpath", "-buildvcs=false", "-ldflags", ldflags, "-o", out, backend.main], {
|
|
112
117
|
cwd: backend.dir,
|
|
113
|
-
env: { ...process.env,
|
|
118
|
+
env: { ...process.env, ...goBuildEnv(arch) },
|
|
114
119
|
});
|
|
115
120
|
if (result.missing) {
|
|
116
121
|
throw new StandaloneBuildError("STANDALONE_GO_MISSING", "找不到 go 指令。請安裝 Go 1.25 以上版本(https://go.dev/dl/),或改用 backend.binary 打包既有執行檔。");
|
|
@@ -125,10 +130,12 @@ function buildBackgroundWorker(manifest, arch, work, run) {
|
|
|
125
130
|
if (!manifest.capabilities.jobs || manifest.backend.kind !== "go")
|
|
126
131
|
return undefined;
|
|
127
132
|
const out = join(work, `background-worker-${arch}`);
|
|
128
|
-
const ldflags = ["-s", "-w", "-buildid=", `-X main.packageID=${manifest.package}
|
|
133
|
+
const ldflags = ["-s", "-w", "-buildid=", `-X main.packageID=${manifest.package}`, ...delegateBuildFlags(manifest),
|
|
134
|
+
...(manifest.executor ? [`-X main.executorDigest=${manifest.executor.executorDigest}`, `-X main.policyDigest=${manifest.executor.policyDigest}`] : []),
|
|
135
|
+
].join(" ");
|
|
129
136
|
const result = run("go", ["build", "-trimpath", "-buildvcs=false", "-ldflags", ldflags, "-o", out, BACKGROUND_WORKER_MAIN], {
|
|
130
137
|
cwd: manifest.backend.dir,
|
|
131
|
-
env: { ...process.env,
|
|
138
|
+
env: { ...process.env, ...goBuildEnv(arch) },
|
|
132
139
|
});
|
|
133
140
|
if (result.missing || result.status !== 0) {
|
|
134
141
|
throw new StandaloneBuildError("STANDALONE_BACKGROUND_BUILD_FAILED", `background worker(${BACKGROUND_WORKER_MAIN})build 失敗;這個專案的 backend 需要含該 package。\n${result.stderr}`);
|
|
@@ -138,3 +145,26 @@ function buildBackgroundWorker(manifest, arch, work, run) {
|
|
|
138
145
|
throw new StandaloneBuildError(problem.code, problem.message);
|
|
139
146
|
return out;
|
|
140
147
|
}
|
|
148
|
+
function delegateBuildFlags(manifest) {
|
|
149
|
+
const d = manifest.capabilities.delegate;
|
|
150
|
+
if (!d)
|
|
151
|
+
return [];
|
|
152
|
+
const values = {
|
|
153
|
+
delegateScope: d.scope,
|
|
154
|
+
delegateOperationsJSON: JSON.stringify([...d.operations].sort()),
|
|
155
|
+
delegateDestinationsJSON: JSON.stringify(d.destinations ? [...d.destinations].sort() : []),
|
|
156
|
+
delegateLifetime: d.lifetime ?? '',
|
|
157
|
+
};
|
|
158
|
+
// Go's quoted.Split does not process backslash escapes. Interior JSON quotes
|
|
159
|
+
// in a whitespace-free argument are literal; never shell-escape this string.
|
|
160
|
+
return Object.entries(values).map(([key, value]) => {
|
|
161
|
+
const arg = `main.${key}=${value}`;
|
|
162
|
+
if (!/\s/.test(arg))
|
|
163
|
+
return `-X ${arg}`;
|
|
164
|
+
if (!arg.includes("'"))
|
|
165
|
+
return `-X '${arg}'`;
|
|
166
|
+
if (!arg.includes('"'))
|
|
167
|
+
return `-X "${arg}"`;
|
|
168
|
+
throw new StandaloneBuildError('STANDALONE_GO_BUILD_FAILED', 'Delegate scope with whitespace and both quote types cannot be represented in Go linker flags.');
|
|
169
|
+
});
|
|
170
|
+
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type
|
|
1
|
+
import { type NativeArchFamily } from "../build-spk/dsm-arch.js";
|
|
2
2
|
import type { CommandRunner } from "./build.js";
|
|
3
3
|
import type { StandaloneError, StandaloneManifest } from "./manifest.js";
|
|
4
4
|
export interface Daemon {
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { chmodSync, copyFileSync, lstatSync, mkdirSync, readdirSync, realpathSync, writeFileSync } from "node:fs";
|
|
2
2
|
import { isAbsolute, join, relative, resolve } from "node:path";
|
|
3
|
+
import { goBuildEnv } from "../build-spk/dsm-arch.js";
|
|
3
4
|
import { templatesRoot } from "../template-paths.js";
|
|
4
5
|
import { checkCgiBinary } from "./binary.js";
|
|
5
6
|
import { StandaloneBuildError } from "./spk.js";
|
|
@@ -58,7 +59,7 @@ export function buildDaemonControl(manifest, arch, work, run) {
|
|
|
58
59
|
return undefined;
|
|
59
60
|
const out = join(work, `daemon-control-${arch}`);
|
|
60
61
|
const result = run("go", ["build", "-trimpath", "-buildvcs=false", "-ldflags", "-s -w -buildid=", "-o", out, "."], {
|
|
61
|
-
cwd: join(templatesRoot(), "_daemon"), env: { ...process.env,
|
|
62
|
+
cwd: join(templatesRoot(), "_daemon"), env: { ...process.env, ...goBuildEnv(arch) },
|
|
62
63
|
});
|
|
63
64
|
if (result.missing || result.status !== 0)
|
|
64
65
|
throw new StandaloneBuildError("STANDALONE_DAEMON_BUILD_FAILED", `daemon controller build 需要 Go 1.24 以上。\n${result.stderr}`);
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { StandaloneManifest } from "./manifest.js";
|
|
1
|
+
import type { EntryMode, StandaloneManifest } from "./manifest.js";
|
|
2
2
|
/** Same path the generated wrapper POSTs to, and the payload location of the CGI. */
|
|
3
3
|
export declare const CGI_PAYLOAD_PATH = "ui/cgi/api.cgi";
|
|
4
4
|
export declare function cgiUrl(packageId: string): string;
|
|
@@ -14,6 +14,8 @@ export declare const I18N_RUNTIME: string;
|
|
|
14
14
|
* cached from the previous version after an upgrade.
|
|
15
15
|
*/
|
|
16
16
|
export declare function windowScriptName(manifest: Pick<StandaloneManifest, "build">): string;
|
|
17
|
+
/** The launcher fields an entry mode fixes; inspect checks a built SPK against them. */
|
|
18
|
+
export declare function entryLauncherAccess(entry: EntryMode): Record<string, unknown>;
|
|
17
19
|
/** `ui/config`: registers the DSM main-menu app and its window class. */
|
|
18
20
|
export declare function renderUiConfig(manifest: StandaloneManifest): string;
|
|
19
21
|
/**
|
|
@@ -3,6 +3,7 @@ import { DSM_LANGS } from "./i18n.js";
|
|
|
3
3
|
import { ROOT_PANEL, ROOT_RUNTIME, rootTasks } from "./root-window.js";
|
|
4
4
|
import { HOST_ADAPTER_RUNTIME } from "./host-adapter.js";
|
|
5
5
|
import { permitsOrdinaryUsers } from "./api-permissions.js";
|
|
6
|
+
import { PORTAL_RUNTIME } from "./portal-window.js";
|
|
6
7
|
/** Same path the generated wrapper POSTs to, and the payload location of the CGI. */
|
|
7
8
|
export const CGI_PAYLOAD_PATH = "ui/cgi/api.cgi";
|
|
8
9
|
export function cgiUrl(packageId) {
|
|
@@ -47,6 +48,20 @@ export const I18N_RUNTIME = `function createI18n(dsmLang, catalogs) {
|
|
|
47
48
|
export function windowScriptName(manifest) {
|
|
48
49
|
return `window-${String(manifest.build).padStart(4, "0")}.js`;
|
|
49
50
|
}
|
|
51
|
+
/**
|
|
52
|
+
* Who sees the main-menu entry. app-privilege also registers the app in
|
|
53
|
+
* Control Panel > Application Privileges (local and directory accounts, with
|
|
54
|
+
* IP rules); DSM then filters the launcher and the CGI checks the same rule.
|
|
55
|
+
*/
|
|
56
|
+
function launcherAccess(manifest) {
|
|
57
|
+
return manifest.entry ? entryLauncherAccess(manifest.entry) : { allUsers: permitsOrdinaryUsers(manifest.apiPermissions) };
|
|
58
|
+
}
|
|
59
|
+
/** The launcher fields an entry mode fixes; inspect checks a built SPK against them. */
|
|
60
|
+
export function entryLauncherAccess(entry) {
|
|
61
|
+
return entry === "admin-only"
|
|
62
|
+
? { allUsers: false }
|
|
63
|
+
: { allUsers: true, grantPrivilege: "all", advanceGrantPrivilege: true };
|
|
64
|
+
}
|
|
50
65
|
/** `ui/config`: registers the DSM main-menu app and its window class. */
|
|
51
66
|
export function renderUiConfig(manifest) {
|
|
52
67
|
const ns = `SYNO.SDS.${manifest.package}`;
|
|
@@ -59,7 +74,7 @@ export function renderUiConfig(manifest) {
|
|
|
59
74
|
icon: "images/app_{0}.png",
|
|
60
75
|
appWindow: `${ns}.MainWindow`,
|
|
61
76
|
allowMultiInstance: false,
|
|
62
|
-
|
|
77
|
+
...launcherAccess(manifest),
|
|
63
78
|
depend: [`${ns}.MainWindow`],
|
|
64
79
|
},
|
|
65
80
|
[`${ns}.MainWindow`]: {
|
|
@@ -127,7 +142,14 @@ ${HOST_ADAPTER_RUNTIME}
|
|
|
127
142
|
return createHostAdapter;
|
|
128
143
|
})();
|
|
129
144
|
|
|
130
|
-
${root
|
|
145
|
+
${root || manifest.portal ? renderWebApi(ns) : ""}// A display hint for hiding or disabling administrator settings; the CGI
|
|
146
|
+
// decides access on its own and never trusts this value.
|
|
147
|
+
${ns}.sessionUser = function () {
|
|
148
|
+
var session = (SYNO.SDS && SYNO.SDS.Session) || {};
|
|
149
|
+
return { name: typeof session.user === "string" ? session.user : "", isAdmin: session.is_admin === true };
|
|
150
|
+
};
|
|
151
|
+
|
|
152
|
+
${root ? root.definitions : ""}${manifest.portal ? renderPortalDefinition(ns) : ""}${ns}.loadApp = function (done) {
|
|
131
153
|
var apps = window.DsmPackageApps || {};
|
|
132
154
|
if (apps[${p}]) {
|
|
133
155
|
done(apps[${p}]);
|
|
@@ -186,6 +208,7 @@ ${root ? root.mount : ""} ${ns}.loadApp(function (app) {
|
|
|
186
208
|
call: ${ns}.call,
|
|
187
209
|
files: {endpoint: ${JSON.stringify(cgiUrl(pkg))}, token: function () { return SYNO.SDS.Session && SYNO.SDS.Session.SynoToken; }},
|
|
188
210
|
reopen: me.reopenEvents.subscribe,
|
|
211
|
+
user: ${ns}.sessionUser(),${manifest.portal ? `\n portal: ${renderPortalAccess(ns, manifest)},` : ""}
|
|
189
212
|
host: me.hostAccess,
|
|
190
213
|
appWindow: me,
|
|
191
214
|
lang: lang,
|
|
@@ -221,6 +244,50 @@ ${root ? root.mount : ""} ${ns}.loadApp(function (app) {
|
|
|
221
244
|
}
|
|
222
245
|
}
|
|
223
246
|
});
|
|
247
|
+
`;
|
|
248
|
+
}
|
|
249
|
+
function renderPortalDefinition(ns) {
|
|
250
|
+
return `${ns}.createPortalAccess = (function () {
|
|
251
|
+
${PORTAL_RUNTIME}
|
|
252
|
+
return createPortalAccess;
|
|
253
|
+
})();
|
|
254
|
+
|
|
255
|
+
`;
|
|
256
|
+
}
|
|
257
|
+
/**
|
|
258
|
+
* `ctx.portal`: administrators use their own DSM session; isAdmin only picks
|
|
259
|
+
* the path. The SDK ships the panel text in every DSM language, so it follows
|
|
260
|
+
* DSM even where the app has no catalog.
|
|
261
|
+
*/
|
|
262
|
+
function renderPortalAccess(ns, manifest) {
|
|
263
|
+
return `${ns}.createPortalAccess({
|
|
264
|
+
appId: ${JSON.stringify(`${ns}.Instance`)},
|
|
265
|
+
defaultAlias: ${JSON.stringify(manifest.portal.alias)},
|
|
266
|
+
isAdmin: function () { return ${ns}.sessionUser().isAdmin; },
|
|
267
|
+
send: ${ns}.webApi(me),
|
|
268
|
+
call: ${ns}.call,
|
|
269
|
+
launch: function (app, params) { SYNO.SDS.AppLaunch(app, params); },
|
|
270
|
+
location: window.location,
|
|
271
|
+
locale: ${ns}.createI18n(lang, ${JSON.stringify(Object.fromEntries(Object.keys(DSM_LANGS).map((locale) => [locale, {}])))}).locale
|
|
272
|
+
})`;
|
|
273
|
+
}
|
|
274
|
+
/** DSM WebAPI in the window's own session: resolves data, rejects `{code: "dsm_<n>"}`. */
|
|
275
|
+
function renderWebApi(ns) {
|
|
276
|
+
return `// DSM WebAPI in the window's own session: resolves data, rejects {code: "dsm_<n>"}.
|
|
277
|
+
${ns}.webApi = function (win) {
|
|
278
|
+
return function (api, method, version, params) {
|
|
279
|
+
return new Promise(function (resolve, reject) {
|
|
280
|
+
win.sendWebAPI({
|
|
281
|
+
api: api, method: method, version: version, params: params, scope: win,
|
|
282
|
+
callback: function (success, response) {
|
|
283
|
+
if (success) resolve(response);
|
|
284
|
+
else reject({ code: "dsm_" + ((response && response.code) || "error") });
|
|
285
|
+
}
|
|
286
|
+
});
|
|
287
|
+
});
|
|
288
|
+
};
|
|
289
|
+
};
|
|
290
|
+
|
|
224
291
|
`;
|
|
225
292
|
}
|
|
226
293
|
const ROOT_PANEL_CSS = [
|
|
@@ -259,19 +326,9 @@ ${ROOT_PANEL}
|
|
|
259
326
|
};
|
|
260
327
|
var access = ${ns}.createRootAccess({
|
|
261
328
|
tasks: ${ns}.rootTasks,
|
|
262
|
-
pins: ${JSON.stringify({ executorDigest: manifest.executor.executorDigest, policyDigest: manifest.executor.policyDigest })},
|
|
329
|
+
pins: ${JSON.stringify({ package: manifest.package, executorDigest: manifest.executor.executorDigest, policyDigest: manifest.executor.policyDigest })},
|
|
263
330
|
user: session,
|
|
264
|
-
send:
|
|
265
|
-
return new Promise(function (resolve, reject) {
|
|
266
|
-
me.sendWebAPI({
|
|
267
|
-
api: api, method: method, version: version, params: params, scope: me,
|
|
268
|
-
callback: function (success, response) {
|
|
269
|
-
if (success) resolve(response);
|
|
270
|
-
else reject({ code: "dsm_" + ((response && response.code) || "error") });
|
|
271
|
-
}
|
|
272
|
-
});
|
|
273
|
-
});
|
|
274
|
-
},
|
|
331
|
+
send: ${ns}.webApi(me),
|
|
275
332
|
confirmPassword: function (suppliedPassword) {
|
|
276
333
|
// Send exactly one PasswordConfirm v2 auth with a masked password field.
|
|
277
334
|
return new Promise(function (resolve, reject) {
|
|
@@ -309,7 +366,7 @@ ${ROOT_PANEL}
|
|
|
309
366
|
wait: function (ms) { return new Promise(function (resolve) { setTimeout(resolve, ms); }); }
|
|
310
367
|
});
|
|
311
368
|
var panels = me.body && me.body.dom.querySelector(".dsmapp-rootpanels");
|
|
312
|
-
if (panels) {
|
|
369
|
+
if (panels && ${manifest.ui.build !== undefined && manifest.capabilities.delegate?.scope === "install-selected" ? "false" : "true"}) {
|
|
313
370
|
${ns}.mountRootPanel(panels, access, null, ${ns}.rootTasks, session().admin);
|
|
314
371
|
}
|
|
315
372
|
`,
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { type ExecutorInspection } from "../runtime/executor-inspect.js";
|
|
2
|
+
import type { EntryMode } from "./manifest.js";
|
|
2
3
|
import { type UninstallCleanupItem } from "../runtime/uninstall-cleanup.js";
|
|
3
4
|
export type SpkKind = "native-go" | "binary" | "container" | "native-layout";
|
|
4
5
|
export interface SpkInspection {
|
|
@@ -22,6 +23,13 @@ export interface SpkInspection {
|
|
|
22
23
|
}>;
|
|
23
24
|
executor?: ExecutorInspection;
|
|
24
25
|
uninstallCleanup?: UninstallCleanupItem[];
|
|
26
|
+
/** Application Portal entry; inactive until an administrator applies the alias. */
|
|
27
|
+
portal?: {
|
|
28
|
+
appId: string;
|
|
29
|
+
alias: string;
|
|
30
|
+
};
|
|
31
|
+
/** Declared `entry` mode; `admin-only` is the administrator floor of every route. */
|
|
32
|
+
entry?: EntryMode;
|
|
25
33
|
}
|
|
26
34
|
/** Reads an SPK without installing it and reports how it would run on DSM. */
|
|
27
35
|
export declare function inspectStandaloneSpk(spkPath: string): Promise<SpkInspection>;
|
|
@@ -1,22 +1,25 @@
|
|
|
1
1
|
import { inspectNativeLayout } from "./native-layout.js";
|
|
2
|
-
import { inspectExecutor } from "../runtime/executor-inspect.js";
|
|
2
|
+
import { inspectExecutor, inspectExecutorAnchor } from "../runtime/executor-inspect.js";
|
|
3
3
|
import { existsSync, mkdirSync, mkdtempSync, readdirSync, readFileSync, rmSync } from "node:fs";
|
|
4
4
|
import { tmpdir } from "node:os";
|
|
5
5
|
import { join } from "node:path";
|
|
6
6
|
import * as tar from "tar";
|
|
7
7
|
import { familyOfArchToken, NATIVE_ARCH_FAMILIES } from "../build-spk/dsm-arch.js";
|
|
8
|
-
import { CONTAINER_MIN_DSM, isDsmAtLeast } from "../build-spk/dsm-version.js";
|
|
8
|
+
import { CONTAINER_MIN_DSM, isDsmAtLeast, PORTAL_MIN_DSM } from "../build-spk/dsm-version.js";
|
|
9
9
|
import { ELF_MACHINE } from "./binary.js";
|
|
10
|
-
import { CGI_PAYLOAD_PATH } from "./dsm-window.js";
|
|
10
|
+
import { CGI_PAYLOAD_PATH, entryLauncherAccess } from "./dsm-window.js";
|
|
11
11
|
import { daemonConfig, readDaemon } from "./daemon.js";
|
|
12
12
|
import { BACKGROUND_UNIT_DIR, BACKGROUND_WORKER_PAYLOAD, backgroundUnitName, renderBackgroundUnit } from "./background.js";
|
|
13
13
|
import { renderStandaloneResource } from "./resource.js";
|
|
14
14
|
import { recoverSharedFolder, renderSharedFolderUpgradeWizardFiles, sharedFolderInstallSteps, SHARED_FOLDER_MARKER } from "./shared-folder.js";
|
|
15
15
|
import { renderInstallWizardFiles, renderNativeInstallWizard } from "../build-spk/install-wizard.js";
|
|
16
|
+
import { renderDelegateScopeSteps, renderDelegateInstallDraft } from "../build-spk/delegate-scope.js";
|
|
16
17
|
import { renderUninstallWizardFiles } from "../build-spk/uninstall-wizard.js";
|
|
17
18
|
import { renderStandaloneScript } from "./lifecycle.js";
|
|
18
19
|
import { listPortExposures, loadCompose } from "../compose/analyze.js";
|
|
19
20
|
import { uninstallCleanup } from "../runtime/uninstall-cleanup.js";
|
|
21
|
+
import { PORTAL_JSON_RELPATH, PORTAL_MUSTACHE_RELPATH, portalResource, renderPortalJson, renderPortalLifecycle, renderPortalMustache, renderPortalWizardStep } from "../portal/package.js";
|
|
22
|
+
import { isPortalAlias } from "../portal/alias.js";
|
|
20
23
|
const ROOT_SCRIPT_HINTS = [/\bsudo\b/, /\bchown\s+(-\w+\s+)*root\b/, /chmod\s+[ugo]*\+s\b/, /chmod\s+[2467][0-7]{3}\b/];
|
|
21
24
|
/** Reads an SPK without installing it and reports how it would run on DSM. */
|
|
22
25
|
export async function inspectStandaloneSpk(spkPath) {
|
|
@@ -48,7 +51,7 @@ export async function inspectSpk(spkPath, expectedKind) {
|
|
|
48
51
|
if (metadataText !== undefined) {
|
|
49
52
|
try {
|
|
50
53
|
const raw = JSON.parse(metadataText);
|
|
51
|
-
if (raw.schema !== 2 || !["go", "binary"].includes(raw.backend) || !Array.isArray(raw.capabilities) || raw.capabilities.some((name) => !["files", "jobs", "identity", "rootOps", "delegate"].includes(String(name))) || new Set(raw.capabilities).size !== raw.capabilities.length)
|
|
54
|
+
if (raw.schema !== 2 || !["go", "binary"].includes(raw.backend) || !Array.isArray(raw.capabilities) || raw.capabilities.some((name) => !["files", "jobs", "identity", "rootOps", "delegate"].includes(String(name))) || new Set(raw.capabilities).size !== raw.capabilities.length || (raw.entry !== undefined && !["admin-only", "app-privilege"].includes(raw.entry)))
|
|
52
55
|
throw new Error();
|
|
53
56
|
metadata = raw;
|
|
54
57
|
}
|
|
@@ -63,18 +66,19 @@ export async function inspectSpk(spkPath, expectedKind) {
|
|
|
63
66
|
await checkArch(dir, info, kind, paths.has(CGI_PAYLOAD_PATH), findings);
|
|
64
67
|
const background = kind === "native-go" && paths.has(BACKGROUND_WORKER_PAYLOAD);
|
|
65
68
|
const sharedFolder = standalone ? recoverSharedFolder(readOptional(join(dir, "scripts/postinst"))) : undefined;
|
|
66
|
-
|
|
69
|
+
const portal = await recoverPortal(dir, info, kind === "container" ? info.package ?? "" : `SYNO.SDS.${info.package}.Instance`, paths, payload, findings);
|
|
70
|
+
if (portal && info.os_min_ver && OS_MIN_VER.test(info.os_min_ver) && !isDsmAtLeast(info.os_min_ver, PORTAL_MIN_DSM)) {
|
|
71
|
+
findings.push(`INFO os_min_ver「${info.os_min_ver}」低於 portal 需要的 DSM ${PORTAL_MIN_DSM};DSM 7.1.1 以錯誤 320 拒裝帶 web-config app-portal 的未簽章套件,7.2 未驗證`);
|
|
72
|
+
}
|
|
73
|
+
checkConf(dir, findings, kind, background, sharedFolder, info.package, portal);
|
|
67
74
|
checkScripts(dir, findings);
|
|
68
|
-
if (standalone)
|
|
69
|
-
checkSharedFolder(dir, findings, info.package, sharedFolder);
|
|
70
75
|
checkUninstall(dir, findings);
|
|
71
76
|
if (kind === "container")
|
|
72
77
|
await checkContainerExposure(dir, join(dir, "package.tgz"), findings);
|
|
73
78
|
const hasExecutor = (metadata.capabilities ?? []).some(name => name === "rootOps" || name === "delegate") || paths.has("executor/executor");
|
|
79
|
+
const daemon = standalone ? await checkDaemon(dir, info, paths, payload, findings, hasExecutor, background) : undefined;
|
|
74
80
|
if (background)
|
|
75
|
-
checkBackground(dir, info, payload, findings, hasExecutor);
|
|
76
|
-
if (standalone)
|
|
77
|
-
await checkDaemon(dir, info, paths, payload, findings, hasExecutor);
|
|
81
|
+
checkBackground(dir, info, payload, findings, hasExecutor, daemon);
|
|
78
82
|
let executor;
|
|
79
83
|
if ((metadata.capabilities ?? []).some(name => name === "rootOps" || name === "delegate") || paths.has("executor/executor")) {
|
|
80
84
|
const executorDir = join(dir, "executor-inspection");
|
|
@@ -83,6 +87,8 @@ export async function inspectSpk(spkPath, expectedKind) {
|
|
|
83
87
|
const inspected = inspectExecutor(executorDir, info.package, info.arch);
|
|
84
88
|
findings.push(...inspected.findings);
|
|
85
89
|
executor = inspected.executor;
|
|
90
|
+
if (executor)
|
|
91
|
+
findings.push(...inspectExecutorAnchor(readFileSync(join(dir, "INFO"), "utf8"), executor.executorDigest));
|
|
86
92
|
if (payload.find(p => p.path === "executor/executor")?.mode !== "0755")
|
|
87
93
|
findings.push("Executor must be executable.");
|
|
88
94
|
if (payload.find(p => p.path === "executor/lifecycle")?.mode !== "0755")
|
|
@@ -99,6 +105,13 @@ export async function inspectSpk(spkPath, expectedKind) {
|
|
|
99
105
|
findings.push(`Executor WIZARD_UIFILES/${name} is missing cleanup guidance.`);
|
|
100
106
|
}
|
|
101
107
|
}
|
|
108
|
+
if (background && daemon && (executor?.policy.lifetime !== 'until-revoked' || !metadata.capabilities?.includes('delegate'))) {
|
|
109
|
+
findings.push('daemon/background combination requires an explicitly pinned until-revoked delegate policy.');
|
|
110
|
+
}
|
|
111
|
+
if (metadata.entry)
|
|
112
|
+
await checkEntryLauncher(dir, info.package, metadata.entry, findings);
|
|
113
|
+
if (standalone)
|
|
114
|
+
checkSharedFolder(dir, findings, info.package, sharedFolder, executor?.policy.scope === 'install-selected', portal);
|
|
102
115
|
const cgiEntry = payload.find((entry) => entry.path === CGI_PAYLOAD_PATH);
|
|
103
116
|
if (standalone && !cgiEntry) {
|
|
104
117
|
findings.push(`payload 沒有 ${CGI_PAYLOAD_PATH}`);
|
|
@@ -109,6 +122,8 @@ export async function inspectSpk(spkPath, expectedKind) {
|
|
|
109
122
|
return {
|
|
110
123
|
kind,
|
|
111
124
|
...(executor ? { executor } : {}),
|
|
125
|
+
...(portal ? { portal: { appId: portal.appId, alias: portal.alias } } : {}),
|
|
126
|
+
...(metadata.entry ? { entry: metadata.entry } : {}),
|
|
112
127
|
...(standalone ? { uninstallCleanup: uninstallCleanup(info.package, hasExecutor) } : {}),
|
|
113
128
|
package: info.package,
|
|
114
129
|
version: info.version,
|
|
@@ -124,6 +139,23 @@ export async function inspectSpk(spkPath, expectedKind) {
|
|
|
124
139
|
rmSync(dir, { recursive: true, force: true });
|
|
125
140
|
}
|
|
126
141
|
}
|
|
142
|
+
/** The launcher in ui/config must carry exactly the access the declared entry mode fixes. */
|
|
143
|
+
async function checkEntryLauncher(dir, pkg, entry, findings) {
|
|
144
|
+
const target = join(dir, "entry-inspection");
|
|
145
|
+
mkdirSync(target);
|
|
146
|
+
await tar.x({ file: join(dir, "package.tgz"), cwd: target, filter: path => path.replace(/^\.\//, "") === "ui/config" });
|
|
147
|
+
let launcher;
|
|
148
|
+
try {
|
|
149
|
+
const config = JSON.parse(readFileSync(join(target, "ui/config"), "utf8"));
|
|
150
|
+
launcher = Object.values(config)[0]?.[`SYNO.SDS.${pkg}.Instance`];
|
|
151
|
+
}
|
|
152
|
+
catch { /* reported below */ }
|
|
153
|
+
const expected = entryLauncherAccess(entry);
|
|
154
|
+
const fields = ["allUsers", "grantPrivilege", "advanceGrantPrivilege"];
|
|
155
|
+
if (!launcher || fields.some(field => launcher[field] !== expected[field])) {
|
|
156
|
+
findings.push(`ENTRY_LAUNCHER_MISMATCH sdk-package.json 宣告 entry: ${entry},但 ui/config 的 launcher 不是 ${JSON.stringify(expected)};請用 synopkgland build 重新產生`);
|
|
157
|
+
}
|
|
158
|
+
}
|
|
127
159
|
const OS_MIN_VER = /^7\.\d+(\.\d+)?-\d+$/;
|
|
128
160
|
/** INFO os_min_ver and arch must be values DSM accepts, and arch must match the shipped CGI. */
|
|
129
161
|
async function checkArch(dir, info, kind, hasCgi, findings) {
|
|
@@ -157,7 +189,7 @@ async function checkArch(dir, info, kind, hasCgi, findings) {
|
|
|
157
189
|
findings.push(`INFO arch 宣告 ${declared},但 ${CGI_PAYLOAD_PATH} 是 ${actual} 的執行檔`);
|
|
158
190
|
}
|
|
159
191
|
}
|
|
160
|
-
async function checkDaemon(dir, info, paths, payload, findings, executor) {
|
|
192
|
+
async function checkDaemon(dir, info, paths, payload, findings, executor, background) {
|
|
161
193
|
if (![...paths].some(path => path.startsWith("daemon/")))
|
|
162
194
|
return;
|
|
163
195
|
if (info.precheckstartstop !== "yes")
|
|
@@ -178,13 +210,14 @@ async function checkDaemon(dir, info, paths, payload, findings, executor) {
|
|
|
178
210
|
throw new Error("invalid config");
|
|
179
211
|
if (payload.find(p => p.path === `daemon/payload/${daemon.exec}`)?.mode !== "0755")
|
|
180
212
|
throw new Error("missing executable");
|
|
181
|
-
const manifest = { package: info.package, daemon, capabilities: executor ? { rootOps: true } : {} };
|
|
213
|
+
const manifest = { package: info.package, daemon, capabilities: { ...(executor ? { rootOps: true } : {}), ...(background ? { jobs: { worker: 'sha256-v1' } } : {}) } };
|
|
182
214
|
if (readFileSync(join(unpacked, "daemon/config.json"), "utf8") !== daemonConfig(manifest))
|
|
183
215
|
throw new Error("non-canonical daemon config");
|
|
184
216
|
for (const hook of ["start-stop-status", "preupgrade", "preuninst"]) {
|
|
185
217
|
if (readFileSync(join(dir, "scripts", hook), "utf8") !== renderStandaloneScript(hook, manifest))
|
|
186
218
|
findings.push(`daemon scripts/${hook} 與固定 lifecycle 不符`);
|
|
187
219
|
}
|
|
220
|
+
return daemon;
|
|
188
221
|
}
|
|
189
222
|
catch {
|
|
190
223
|
findings.push("daemon/config.json 或 payload 不符合固定 daemon 契約");
|
|
@@ -234,12 +267,12 @@ async function checkContainerExposure(dir, packageTgz, findings) {
|
|
|
234
267
|
function readOptional(path) {
|
|
235
268
|
return existsSync(path) ? readFileSync(path, "utf8") : undefined;
|
|
236
269
|
}
|
|
237
|
-
function checkConf(dir, findings, kind, background, sharedFolder, pkg) {
|
|
270
|
+
function checkConf(dir, findings, kind, background, sharedFolder, pkg, portal) {
|
|
238
271
|
const confDir = join(dir, "conf");
|
|
239
272
|
const entries = existsSync(confDir) ? readdirSync(confDir) : [];
|
|
240
|
-
const nativeResource = kind !== "container" ? renderStandaloneResource({ package: pkg ?? "", capabilities: background ? { jobs: { worker: "sha256-v1" } } : {}, ...(sharedFolder ? { sharedFolder } : {}) }) : undefined;
|
|
273
|
+
const nativeResource = kind !== "container" ? renderStandaloneResource({ package: pkg ?? "", capabilities: background ? { jobs: { worker: "sha256-v1" } } : {}, ...(sharedFolder ? { sharedFolder } : {}) }, portal) : undefined;
|
|
241
274
|
if (nativeResource !== undefined && readOptional(join(confDir, "resource")) !== nativeResource) {
|
|
242
|
-
findings.push(`conf/resource 必須是 ${nativeResource}(只有官方 data-share
|
|
275
|
+
findings.push(`conf/resource 必須是 ${nativeResource}(只有官方 data-share、systemd-user-unit 與 web-config,且與 install.sharedFolder、portal 宣告一致)`);
|
|
243
276
|
}
|
|
244
277
|
for (const name of entries) {
|
|
245
278
|
if (name !== "privilege" && !(kind === "container" && name === "resource") && !(nativeResource !== undefined && name === "resource") && !(background && name === "systemd")) {
|
|
@@ -252,9 +285,11 @@ function checkConf(dir, findings, kind, background, sharedFolder, pkg) {
|
|
|
252
285
|
if (!resource["docker-project"])
|
|
253
286
|
findings.push("container 缺少 docker-project resource");
|
|
254
287
|
for (const key of Object.keys(resource)) {
|
|
255
|
-
if (!["docker-project", "webservice"].includes(key))
|
|
288
|
+
if (!["docker-project", "webservice", ...(portal ? ["web-config"] : [])].includes(key))
|
|
256
289
|
findings.push(`未知 container resource: ${key}`);
|
|
257
290
|
}
|
|
291
|
+
if (portal && JSON.stringify(resource["web-config"]) !== JSON.stringify(portalResource()["web-config"]))
|
|
292
|
+
findings.push("container web-config 必須只有固定的 app-portal 註冊");
|
|
258
293
|
}
|
|
259
294
|
catch {
|
|
260
295
|
findings.push("container 缺少有效的 conf/resource");
|
|
@@ -283,19 +318,21 @@ function checkConf(dir, findings, kind, background, sharedFolder, pkg) {
|
|
|
283
318
|
}
|
|
284
319
|
}
|
|
285
320
|
/** The opt-in package user unit must be exactly what the generator writes. */
|
|
286
|
-
function checkBackground(dir, info, payload, findings, executor) {
|
|
321
|
+
function checkBackground(dir, info, payload, findings, executor, daemon) {
|
|
287
322
|
const pkg = info.package;
|
|
288
323
|
if (!pkg)
|
|
289
324
|
return;
|
|
290
325
|
const read = (path) => existsSync(join(dir, path)) ? readFileSync(join(dir, path), "utf8") : undefined;
|
|
291
326
|
if (payload.find(p => p.path === BACKGROUND_WORKER_PAYLOAD)?.mode !== "0755")
|
|
292
327
|
findings.push(`${BACKGROUND_WORKER_PAYLOAD} 缺少或非 executable`);
|
|
328
|
+
if (info.precheckstartstop !== "yes")
|
|
329
|
+
findings.push('background INFO 必須明確設定 precheckstartstop="yes"(prestart 復原失去 socket 的套件使用者 manager)');
|
|
293
330
|
const units = existsSync(join(dir, BACKGROUND_UNIT_DIR)) ? readdirSync(join(dir, BACKGROUND_UNIT_DIR)) : [];
|
|
294
331
|
const unit = backgroundUnitName(pkg);
|
|
295
332
|
if (units.length !== 1 || units[0] !== unit || read(`${BACKGROUND_UNIT_DIR}/${unit}`) !== renderBackgroundUnit(pkg)) {
|
|
296
333
|
findings.push(`${BACKGROUND_UNIT_DIR} 必須只有固定的 ${unit}(套件帳號執行,無 User=、無 root)`);
|
|
297
334
|
}
|
|
298
|
-
if (read("scripts/start-stop-status") !== renderStandaloneScript("start-stop-status", { package: pkg, capabilities: { jobs: { worker: "sha256-v1" }, ...(executor ? { rootOps: true } : {}) } }))
|
|
335
|
+
if (read("scripts/start-stop-status") !== renderStandaloneScript("start-stop-status", { package: pkg, ...(daemon ? { daemon } : {}), capabilities: { jobs: { worker: "sha256-v1" }, ...(executor ? { rootOps: true } : {}) } }))
|
|
299
336
|
findings.push("background scripts/start-stop-status 與固定 synosystemctl lifecycle 不符");
|
|
300
337
|
}
|
|
301
338
|
/**
|
|
@@ -304,7 +341,7 @@ function checkBackground(dir, info, payload, findings, executor) {
|
|
|
304
341
|
* declaration (without one, any present file must be the default native
|
|
305
342
|
* install wizard), and no other install/upgrade wizard file may exist.
|
|
306
343
|
*/
|
|
307
|
-
function checkSharedFolder(dir, findings, pkg, folder) {
|
|
344
|
+
function checkSharedFolder(dir, findings, pkg, folder, selected = false, portal) {
|
|
308
345
|
const wizardDir = join(dir, "WIZARD_UIFILES");
|
|
309
346
|
const present = (existsSync(wizardDir) ? readdirSync(wizardDir) : []).filter(name => /^(install|upgrade)_uifile(_|\.|$)/.test(name)).sort();
|
|
310
347
|
const postinst = readOptional(join(dir, "scripts/postinst"));
|
|
@@ -312,27 +349,69 @@ function checkSharedFolder(dir, findings, pkg, folder) {
|
|
|
312
349
|
findings.push("scripts/postinst 的 install.sharedFolder 宣告無效");
|
|
313
350
|
if (!pkg)
|
|
314
351
|
return;
|
|
315
|
-
const wizards = expectedWizardFiles(pkg, folder);
|
|
316
|
-
const source = folder ? "install.sharedFolder 宣告" : "native-go 預設安裝精靈";
|
|
317
|
-
// The default native wizard is optional; a declared shared folder needs all of its files.
|
|
318
|
-
for (const name of folder ? Object.keys(wizards) : present.filter(n => n in wizards)) {
|
|
352
|
+
const wizards = expectedWizardFiles(pkg, folder, selected, portal);
|
|
353
|
+
const source = selected ? "install-selected policy" : folder ? "install.sharedFolder 宣告" : portal ? "portal 宣告" : "native-go 預設安裝精靈";
|
|
354
|
+
// The default native wizard is optional; a declared shared folder or portal needs all of its files.
|
|
355
|
+
for (const name of folder || selected || portal ? Object.keys(wizards) : present.filter(n => n in wizards)) {
|
|
319
356
|
if (readOptional(join(wizardDir, name)) !== wizards[name])
|
|
320
357
|
findings.push(`WIZARD_UIFILES/${name} 與${source}不符`);
|
|
321
358
|
}
|
|
322
359
|
for (const name of present.filter(n => !(n in wizards)))
|
|
323
360
|
findings.push(`WIZARD_UIFILES/${name} 不是${source}產生的精靈檔`);
|
|
361
|
+
if (selected) {
|
|
362
|
+
for (const hook of ['preinst', 'postinst']) {
|
|
363
|
+
if (!readOptional(join(dir, 'scripts', hook))?.includes(renderDelegateInstallDraft(pkg, hook === 'preinst')))
|
|
364
|
+
findings.push(`scripts/${hook} missing install-selected draft handling`);
|
|
365
|
+
}
|
|
366
|
+
}
|
|
324
367
|
if (!folder)
|
|
325
368
|
return;
|
|
326
369
|
for (const hook of ["postinst", "postupgrade"]) {
|
|
327
|
-
if (readOptional(join(dir, "scripts", hook)) !== renderStandaloneScript(hook, { package: pkg, sharedFolder: folder })) {
|
|
370
|
+
if (readOptional(join(dir, "scripts", hook)) !== renderStandaloneScript(hook, { package: pkg, sharedFolder: folder, ...(selected ? { capabilities: { delegate: { scope: "install-selected" } } } : {}), ...portalManifest(portal) })) {
|
|
328
371
|
findings.push(`scripts/${hook} 與 install.sharedFolder 宣告的固定腳本不符`);
|
|
329
372
|
}
|
|
330
373
|
}
|
|
331
374
|
}
|
|
332
|
-
function expectedWizardFiles(pkg, folder) {
|
|
333
|
-
const
|
|
375
|
+
function expectedWizardFiles(pkg, folder, selected = false, portal) {
|
|
376
|
+
const steps = selected ? (t) => [...renderDelegateScopeSteps(t), ...(folder ? sharedFolderInstallSteps(pkg, folder)(t) : [])] : folder ? sharedFolderInstallSteps(pkg, folder) : renderNativeInstallWizard;
|
|
377
|
+
const install = renderInstallWizardFiles(pkg, portal ? (t) => [...steps(t), renderPortalWizardStep(t, portal.alias)] : steps);
|
|
334
378
|
return { ...install.static, ...install.dynamic, ...(folder ? renderSharedFolderUpgradeWizardFiles(pkg, folder) : {}) };
|
|
335
379
|
}
|
|
380
|
+
function portalManifest(portal) {
|
|
381
|
+
return portal ? { displayname: portal.application, portal: { alias: portal.alias } } : {};
|
|
382
|
+
}
|
|
383
|
+
/**
|
|
384
|
+
* The portal declaration is recovered from the payload's etc/portal.json. Its
|
|
385
|
+
* files and alias lifecycle must be exactly what the generator writes for the
|
|
386
|
+
* package's own DSM window (native AppInstance or Compose `.url` entry); activation is never part of the package.
|
|
387
|
+
*/
|
|
388
|
+
async function recoverPortal(dir, info, appId, paths, payload, findings) {
|
|
389
|
+
const files = [PORTAL_JSON_RELPATH, PORTAL_MUSTACHE_RELPATH];
|
|
390
|
+
if (!files.some(path => paths.has(path)))
|
|
391
|
+
return;
|
|
392
|
+
const unpacked = join(dir, "portal-inspection");
|
|
393
|
+
mkdirSync(unpacked);
|
|
394
|
+
await tar.x({ file: join(dir, "package.tgz"), cwd: unpacked, filter: path => files.includes(path.replace(/^\.\//, "")) });
|
|
395
|
+
try {
|
|
396
|
+
if (files.some(path => payload.find(p => p.path === path)?.mode !== "0644"))
|
|
397
|
+
throw new Error("mode");
|
|
398
|
+
const json = readFileSync(join(unpacked, PORTAL_JSON_RELPATH), "utf8");
|
|
399
|
+
const alias = JSON.parse(json).alias;
|
|
400
|
+
if (!isPortalAlias(alias))
|
|
401
|
+
throw new Error("alias");
|
|
402
|
+
const portal = { appId, application: info.displayname ?? "", alias };
|
|
403
|
+
if (json !== renderPortalJson(portal) || readFileSync(join(unpacked, PORTAL_MUSTACHE_RELPATH), "utf8") !== renderPortalMustache())
|
|
404
|
+
throw new Error("content");
|
|
405
|
+
for (const hook of ["preinst", "postinst", "postupgrade"]) {
|
|
406
|
+
if (!readOptional(join(dir, "scripts", hook))?.startsWith(`#!/bin/sh\n${renderPortalLifecycle(hook, portal)}`))
|
|
407
|
+
throw new Error(hook);
|
|
408
|
+
}
|
|
409
|
+
return portal;
|
|
410
|
+
}
|
|
411
|
+
catch {
|
|
412
|
+
findings.push("PORTAL_INVALID etc/portal.json、etc/portal.mustache 或 lifecycle 不符合固定 Application Portal 契約(應開啟套件自己的 DSM 視窗)");
|
|
413
|
+
}
|
|
414
|
+
}
|
|
336
415
|
function checkScripts(dir, findings) {
|
|
337
416
|
const scriptsDir = join(dir, "scripts");
|
|
338
417
|
if (!existsSync(join(scriptsDir, "start-stop-status"))) {
|