dsh-config-manager 0.1.71 → 0.1.72
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/README.md +111 -96
- package/README.zh-CN.md +207 -86
- package/lib/adapters/agent-instructions.js +1 -1
- package/lib/adapters/file-collection.js +1 -1
- package/lib/adapters/index.d.ts +1 -1
- package/lib/adapters/index.js +1 -1
- package/lib/adapters/link-report.d.ts +1 -0
- package/lib/adapters/link-report.js +27 -3
- package/lib/adapters/messages.d.ts +18 -0
- package/lib/adapters/messages.js +50 -0
- package/lib/adapters/plugin-files.js +3 -2
- package/lib/adapters/plugins.js +4 -3
- package/lib/adapters/prompts.d.ts +1 -0
- package/lib/adapters/prompts.js +7 -1
- package/lib/adapters/self.d.ts +7 -1
- package/lib/adapters/self.js +1 -1
- package/lib/adapters/sessions.d.ts +1 -0
- package/lib/adapters/sessions.js +4 -2
- package/lib/adapters/ui.d.ts +2 -2
- package/lib/adapters/ui.js +25 -25
- package/lib/adapters/workspaces.d.ts +1 -0
- package/lib/adapters/workspaces.js +7 -3
- package/lib/cli/actions.d.ts +9 -1
- package/lib/cli/actions.js +5 -2
- package/lib/cli/help.js +23 -10
- package/lib/cli/index.d.ts +23 -0
- package/lib/cli/index.js +90 -32
- package/lib/cli/sessions-inspect.js +3 -1
- package/lib/cli/sessions-repair.js +25 -6
- package/lib/cli/web/page.d.ts +12 -0
- package/lib/cli/web/page.js +21 -12
- package/lib/cli/web/routes.js +2 -2
- package/lib/cli/web/server.js +4 -4
- package/lib/client.d.ts +1349 -490
- package/lib/client.js +43047 -36738
- package/lib/core/backup-plan.js +30 -17
- package/lib/core/backup-verify.js +24 -23
- package/lib/core/backup.d.ts +5 -0
- package/lib/core/backup.js +51 -25
- package/lib/core/boot-rescue.js +4 -3
- package/lib/core/boot-safety.js +2 -1
- package/lib/core/command-rollback.d.ts +178 -0
- package/lib/core/command-rollback.js +289 -0
- package/lib/core/consult-source.js +7 -6
- package/lib/core/disk-usage.d.ts +74 -10
- package/lib/core/disk-usage.js +67 -4
- package/lib/core/engine-locale.d.ts +87 -0
- package/lib/core/engine-locale.js +125 -0
- package/lib/core/exporter.js +3 -3
- package/lib/core/fork-reconcile.d.ts +128 -0
- package/lib/core/fork-reconcile.js +150 -0
- package/lib/core/journal.js +6 -5
- package/lib/core/local-plugin-host.js +3 -2
- package/lib/core/local-plugin-pack.js +11 -6
- package/lib/core/messages-core.d.ts +292 -0
- package/lib/core/messages-core.js +615 -0
- package/lib/core/messages.d.ts +60 -2
- package/lib/core/messages.js +116 -13
- package/lib/core/migration-consult.js +21 -20
- package/lib/core/migration-history.js +19 -5
- package/lib/core/model-tools.d.ts +0 -22
- package/lib/core/model-tools.js +50 -27
- package/lib/core/phase3-host.js +30 -5
- package/lib/core/plugin-cli.js +18 -19
- package/lib/core/reconcile.d.ts +2 -14
- package/lib/core/reconcile.js +87 -17
- package/lib/core/recovery-orchestrator.js +19 -18
- package/lib/core/reinstall.d.ts +4 -0
- package/lib/core/reinstall.js +26 -21
- package/lib/core/restore.js +20 -17
- package/lib/core/rollback-ignore-rules.d.ts +31 -0
- package/lib/core/rollback-ignore-rules.js +140 -0
- package/lib/core/rollback-point.d.ts +1208 -0
- package/lib/core/rollback-point.js +2993 -0
- package/lib/core/run-registry.js +6 -5
- package/lib/core/session-health.d.ts +15 -0
- package/lib/core/session-meta.d.ts +42 -2
- package/lib/core/session-meta.js +140 -1
- package/lib/core/session-title.d.ts +28 -0
- package/lib/core/session-title.js +185 -0
- package/lib/core/startup-barrier.d.ts +24 -0
- package/lib/core/startup-barrier.js +22 -0
- package/lib/core/transaction-coordinator.js +4 -3
- package/lib/core/types.d.ts +18 -1
- package/lib/core/validator.js +5 -4
- package/lib/core/verify-recovery.js +31 -30
- package/lib/foreign/bundle.js +3 -2
- package/lib/foreign/continue.js +2 -1
- package/lib/foreign/crush.js +2 -1
- package/lib/foreign/gemini.js +2 -1
- package/lib/foreign/goose.js +2 -1
- package/lib/foreign/kilocode.js +2 -1
- package/lib/foreign/kimi.js +2 -1
- package/lib/foreign/messages.d.ts +15 -0
- package/lib/foreign/messages.js +40 -0
- package/lib/foreign/mimocode.js +2 -1
- package/lib/foreign/openclaw.js +2 -1
- package/lib/foreign/opencode.js +2 -1
- package/lib/foreign/pi.js +2 -1
- package/lib/foreign/qoder.js +2 -1
- package/lib/foreign/qwen.js +2 -1
- package/lib/foreign/registry.js +7 -6
- package/lib/foreign/teleagent.js +2 -1
- package/lib/foreign/workbuddy.js +2 -1
- package/lib/foreign/zcode.js +2 -1
- package/lib/foreign/zed.js +2 -1
- package/lib/index.d.ts +223 -7
- package/lib/index.js +729 -54
- package/lib/market/git-file-writer.js +20 -19
- package/lib/market/github-repos.js +35 -19
- package/lib/market/index-parser.js +31 -30
- package/lib/market/messages-market.d.ts +138 -0
- package/lib/market/messages-market.js +305 -0
- package/lib/market/my-repo.d.ts +2 -2
- package/lib/market/my-repo.js +32 -29
- package/lib/market/prepare.js +18 -15
- package/lib/market/security.js +20 -19
- package/lib/market/types.js +2 -1
- package/lib/market/url.js +2 -1
- package/lib/migrations/index.js +6 -5
- package/lib/migrations/messages-migrations.d.ts +32 -0
- package/lib/migrations/messages-migrations.js +58 -0
- package/lib/profiles/dsh-profile-launcher.js +2 -1
- package/lib/profiles/dsh-profile-runtime.js +2 -1
- package/lib/profiles/messages.d.ts +7 -0
- package/lib/profiles/messages.js +27 -0
- package/lib/routes/backup.js +18 -2
- package/lib/routes/import.js +3 -3
- package/lib/routes/index.d.ts +1 -1
- package/lib/routes/kit.d.ts +3 -2
- package/lib/routes/kit.js +6 -1
- package/lib/routes/market.js +1 -1
- package/lib/routes/messages.d.ts +42 -0
- package/lib/routes/messages.js +100 -0
- package/lib/routes/profiles.js +12 -6
- package/lib/routes/recovery.js +378 -114
- package/lib/routes/snapshots.js +51 -4
- package/lib/routes/sync.js +68 -16
- package/lib/schema/config.d.ts +11 -1
- package/lib/schema/config.js +52 -20
- package/lib/schema/index.d.ts +8 -6
- package/lib/schema/index.js +8 -6
- package/lib/schema/manifest.js +19 -18
- package/lib/schema/messages.d.ts +47 -0
- package/lib/schema/messages.js +110 -0
- package/lib/schema/section-registry.d.ts +41 -14
- package/lib/schema/section-registry.js +35 -29
- package/lib/schema/versions.js +6 -5
- package/lib/security/encryption.js +16 -15
- package/lib/security/integrity.js +8 -7
- package/lib/security/messages.d.ts +30 -0
- package/lib/security/messages.js +77 -0
- package/lib/security/redaction.js +12 -0
- package/lib/security/secret-scanner.js +16 -13
- package/lib/security/vault.js +2 -1
- package/lib/security/zip-security.js +4 -3
- package/lib/sync/ancestor.js +2 -1
- package/lib/sync/autosync-config.d.ts +11 -0
- package/lib/sync/autosync-config.js +12 -0
- package/lib/sync/autosync-scheduler.d.ts +11 -1
- package/lib/sync/autosync-scheduler.js +115 -76
- package/lib/sync/backup-files.js +3 -2
- package/lib/sync/backup-scheduler.js +6 -5
- package/lib/sync/blob-store.js +2 -1
- package/lib/sync/git/git-transport.d.ts +55 -0
- package/lib/sync/git/git-transport.js +152 -2
- package/lib/sync/github-auth.js +10 -9
- package/lib/sync/layout.js +13 -12
- package/lib/sync/messages.d.ts +63 -0
- package/lib/sync/messages.js +149 -0
- package/lib/sync/review-queue.js +6 -5
- package/lib/sync/s3/messages.d.ts +7 -0
- package/lib/sync/s3/messages.js +16 -0
- package/lib/sync/s3/s3-providers.d.ts +4 -1
- package/lib/sync/s3/s3-providers.js +5 -5
- package/lib/sync/s3/s3-transport.js +1 -1
- package/lib/sync/s3/sigv4.js +2 -1
- package/lib/sync/snapshot-crypto.js +6 -5
- package/lib/sync/snapshot-json.js +3 -2
- package/lib/sync/sync-engine.d.ts +37 -15
- package/lib/sync/sync-engine.js +67 -37
- package/lib/sync/sync-history.js +7 -6
- package/lib/sync/sync-selection.d.ts +15 -4
- package/lib/sync/sync-selection.js +17 -5
- package/lib/sync/webdav/webdav-transport.d.ts +1 -0
- package/lib/sync/webdav/webdav-transport.js +4 -1
- package/lib/ui/artifact-view.js +4 -3
- package/lib/ui/commands.d.ts +1 -1
- package/lib/ui/commands.js +10 -0
- package/lib/ui/disk-usage-view.d.ts +33 -4
- package/lib/ui/disk-usage-view.js +29 -1
- package/lib/ui/errors.d.ts +1 -1
- package/lib/ui/errors.js +4 -3
- package/lib/ui/export-flow.js +24 -20
- package/lib/ui/foreign-view.d.ts +5 -1
- package/lib/ui/foreign-view.js +26 -2
- package/lib/ui/i18n.d.ts +65 -0
- package/lib/ui/i18n.js +114 -0
- package/lib/ui/import-wizard.d.ts +7 -0
- package/lib/ui/import-wizard.js +13 -10
- package/lib/ui/progress.d.ts +2 -2
- package/lib/ui/progress.js +1 -2
- package/lib/ui/report.d.ts +5 -5
- package/lib/ui/report.js +6 -6
- package/lib/ui/rollback-view.d.ts +756 -0
- package/lib/ui/rollback-view.js +724 -0
- package/lib/ui/selection-model.d.ts +41 -0
- package/lib/ui/selection-model.js +30 -0
- package/lib/ui/session-inventory-view.d.ts +97 -3
- package/lib/ui/session-inventory-view.js +76 -2
- package/lib/ui/session-issue-labels.d.ts +43 -0
- package/lib/ui/session-issue-labels.js +48 -0
- package/lib/ui/sync-settings-view.d.ts +33 -0
- package/lib/ui/sync-settings-view.js +40 -0
- package/lib/ui/types.d.ts +8 -3
- package/lib/ui/types.js +1 -1
- package/lib/utils/atomic-write.js +6 -5
- package/lib/utils/bundle-scan.d.ts +11 -1
- package/lib/utils/bundle-scan.js +12 -1
- package/lib/utils/env-lock.d.ts +10 -0
- package/lib/utils/env-lock.js +73 -46
- package/lib/utils/json.js +6 -5
- package/lib/utils/messages-utils.d.ts +96 -0
- package/lib/utils/messages-utils.js +216 -0
- package/lib/utils/proxy.js +18 -5
- package/lib/utils/recursive-walk.d.ts +22 -0
- package/lib/utils/recursive-walk.js +75 -1
- package/lib/utils/session-log-repair.js +10 -9
- package/lib/utils/zip.js +27 -26
- package/package.json +12 -2
- package/src/adapters/agent-instructions.ts +1 -1
- package/src/adapters/file-collection.test.ts +1 -0
- package/src/adapters/file-collection.ts +1 -1
- package/src/adapters/files.test.ts +27 -0
- package/src/adapters/index.ts +1 -1
- package/src/adapters/link-report.test.ts +22 -2
- package/src/adapters/link-report.ts +29 -4
- package/src/adapters/messages.ts +54 -0
- package/src/adapters/plugin-files.ts +3 -2
- package/src/adapters/plugins.ts +4 -3
- package/src/adapters/prompts.ts +7 -1
- package/src/adapters/self.test.ts +26 -0
- package/src/adapters/self.ts +8 -2
- package/src/adapters/sessions.ts +4 -2
- package/src/adapters/settings.test.ts +38 -1
- package/src/adapters/ui.test.ts +2 -2
- package/src/adapters/ui.ts +27 -26
- package/src/adapters/workspaces.ts +7 -3
- package/src/cli/actions.test.ts +28 -0
- package/src/cli/actions.ts +11 -2
- package/src/cli/help.ts +23 -10
- package/src/cli/index-guards.test.ts +193 -0
- package/src/cli/index.ts +106 -31
- package/src/cli/sessions-inspect.test.ts +15 -1
- package/src/cli/sessions-inspect.ts +3 -1
- package/src/cli/sessions-repair.test.ts +84 -0
- package/src/cli/sessions-repair.ts +33 -7
- package/src/cli/web/console-route-parity.test.ts +115 -0
- package/src/cli/web/page.ts +23 -12
- package/src/cli/web/routes.ts +2 -1
- package/src/cli/web/server.ts +4 -4
- package/src/cli/web/web.test.ts +2 -2
- package/src/client/ConfigManagerSection.tsx +30 -2
- package/src/client/about/AboutPanel.tsx +2 -2
- package/src/client/about/PluginUpdateDialog.tsx +1 -1
- package/src/client/about/ReleaseNotesDialog.tsx +2 -2
- package/src/client/about/about-view.test.ts +13 -3
- package/src/client/about/about-view.ts +10 -6
- package/src/client/about/release-notes-view.test.ts +14 -4
- package/src/client/about/release-notes-view.ts +4 -2
- package/src/client/api.ts +18 -6
- package/src/client/chat/ChatEntryView.tsx +391 -0
- package/src/client/chat/RollbackDiffTab.tsx +61 -0
- package/src/client/chat/RollbackFileIcon.tsx +96 -0
- package/src/client/chat/RollbackReviewDialog.tsx +591 -0
- package/src/client/chat/chat-entry-view.test.ts +1174 -0
- package/src/client/chat/chat-entry-view.ts +758 -0
- package/src/client/chat/chat-locales.ts +152 -0
- package/src/client/chat/chat-node-registration.ts +64 -0
- package/src/client/chat/chat-point-window.ts +173 -0
- package/src/client/chat/chat-slots.ts +107 -0
- package/src/client/chat/rollback-diff-tab.test.ts +105 -0
- package/src/client/chat/rollback-diff-tab.ts +164 -0
- package/src/client/chat/rollback-entry-view.test.ts +1469 -0
- package/src/client/chat/rollback-entry-view.ts +941 -0
- package/src/client/chat/sidebar-slots.ts +37 -0
- package/src/client/common/ConfirmDialog.tsx +7 -3
- package/src/client/common/ContentPicker.tsx +48 -10
- package/src/client/common/DiffTable.tsx +97 -0
- package/src/client/common/ErrorBanner.tsx +4 -4
- package/src/client/common/Icon.tsx +2 -0
- package/src/client/common/Modal.tsx +8 -1
- package/src/client/common/ProgressBar.tsx +8 -2
- package/src/client/common/ReportView.tsx +4 -4
- package/src/client/common/RunsCenter.tsx +1 -0
- package/src/client/common/client-route-parity.test.ts +78 -0
- package/src/client/common/info-hint-guard.test.ts +46 -13
- package/src/client/common/picker-lazy-units.test.ts +42 -0
- package/src/client/common/plan-text-redaction.test.ts +17 -6
- package/src/client/common/progress-view-edge.test.ts +8 -7
- package/src/client/common/progress-view.test.ts +9 -9
- package/src/client/common/progress-view.ts +2 -2
- package/src/client/common/routes.ts +28 -0
- package/src/client/common/ui.tsx +73 -10
- package/src/client/config-manager.module.css +996 -21
- package/src/client/css-scale-guard.test.ts +30 -0
- package/src/client/environment/EnvironmentPanel.tsx +11 -4
- package/src/client/environment/maintenance/DiskUsageCard.tsx +23 -2
- package/src/client/export/ExportView.tsx +13 -8
- package/src/client/history/HistoryPanel.tsx +4 -3
- package/src/client/history/history-api.ts +2 -2
- package/src/client/history/history-locales.ts +5 -1
- package/src/client/home/HomePanel.tsx +29 -8
- package/src/client/import/ForeignImportView.tsx +4 -5
- package/src/client/import/ImportWizardView.tsx +5 -3
- package/src/client/import/import-wizard-split.test.ts +3 -1
- package/src/client/import/import-wizard-steps.tsx +9 -9
- package/src/client/index.ts +186 -3
- package/src/client/library/LibraryActions.tsx +32 -7
- package/src/client/library/LibraryPanel.tsx +23 -3
- package/src/client/library/RestorePlanView.tsx +9 -66
- package/src/client/locales.ts +54 -5
- package/src/client/lucide-icons.d.ts +1 -0
- package/src/client/market/MarketPanel.tsx +13 -2
- package/src/client/market/MyConfigsView.tsx +5 -4
- package/src/client/market/MyConfigsWizard.tsx +10 -8
- package/src/client/market/market-api.test.ts +9 -8
- package/src/client/market/market-api.ts +2 -2
- package/src/client/market/market-locales.ts +3 -0
- package/src/client/market/market-view.test.ts +6 -6
- package/src/client/market/market-view.ts +3 -3
- package/src/client/market/my-configs-api.ts +2 -2
- package/src/client/market/my-configs-view.test.ts +23 -22
- package/src/client/market/my-configs-view.ts +8 -8
- package/src/client/recovery/IncompleteCopiesSection.tsx +2 -1
- package/src/client/recovery/RecoveryPanel.tsx +442 -283
- package/src/client/recovery/RollbackCard.tsx +665 -0
- package/src/client/recovery/RollbackDiffPane.tsx +58 -0
- package/src/client/recovery/RollbackFileChanges.tsx +163 -0
- package/src/client/recovery/incident-api.ts +2 -2
- package/src/client/recovery/recovery-api.ts +116 -6
- package/src/client/recovery/recovery-locales.test.ts +299 -0
- package/src/client/recovery/recovery-locales.ts +488 -149
- package/src/client/recovery/recovery-panel-layout.test.ts +132 -2
- package/src/client/recovery/rollback-file-changes.test.ts +73 -0
- package/src/client/recovery/session-dialog-mount.test.ts +54 -0
- package/src/client/run-store.ts +84 -21
- package/src/client/sync/AutosyncCard.tsx +25 -1
- package/src/client/sync/ChannelClearConfirmDialog.tsx +6 -3
- package/src/client/sync/ChannelConfigDialog.tsx +10 -10
- package/src/client/sync/DecryptPasswordCard.tsx +2 -2
- package/src/client/sync/SecurityOptionsCard.tsx +27 -17
- package/src/client/sync/SyncChannelEntryCard.tsx +14 -2
- package/src/client/sync/SyncConfirmView.tsx +46 -8
- package/src/client/sync/SyncLogList.tsx +1 -1
- package/src/client/sync/SyncPanel.tsx +31 -23
- package/src/client/sync/SyncRepositoryPicker.tsx +3 -5
- package/src/client/sync/SyncSectionPickerDialog.tsx +11 -2
- package/src/client/sync/SyncSettingsView.test.ts +20 -1
- package/src/client/sync/UnreadableSnapshotsBanner.tsx +1 -1
- package/src/client/sync/WorkDirCard.tsx +112 -0
- package/src/client/sync/history-model.test.ts +42 -9
- package/src/client/sync/history-model.ts +30 -15
- package/src/client/sync/sync-api.test.ts +57 -33
- package/src/client/sync/sync-api.ts +80 -6
- package/src/client/sync/sync-locales.ts +104 -3
- package/src/client/sync/sync-view-v2.test.ts +65 -13
- package/src/client/sync/sync-view.test.ts +107 -52
- package/src/client/sync/sync-view.ts +124 -41
- package/src/core/backup-plan.test.ts +28 -0
- package/src/core/backup-plan.ts +29 -17
- package/src/core/backup-verify.ts +24 -23
- package/src/core/backup.test.ts +76 -0
- package/src/core/backup.ts +55 -25
- package/src/core/boot-rescue.ts +4 -3
- package/src/core/boot-safety.ts +2 -1
- package/src/core/command-rollback.test.ts +536 -0
- package/src/core/command-rollback.ts +402 -0
- package/src/core/consult-source.ts +7 -6
- package/src/core/disk-usage.test.ts +257 -1
- package/src/core/disk-usage.ts +134 -14
- package/src/core/engine-locale.test.ts +593 -0
- package/src/core/engine-locale.ts +175 -0
- package/src/core/exporter.ts +3 -3
- package/src/core/fork-reconcile.test.ts +279 -0
- package/src/core/fork-reconcile.ts +234 -0
- package/src/core/journal.ts +6 -5
- package/src/core/local-plugin-host.ts +3 -2
- package/src/core/local-plugin-pack.ts +11 -6
- package/src/core/messages-core.ts +657 -0
- package/src/core/messages.ts +123 -17
- package/src/core/migration-consult.ts +21 -20
- package/src/core/migration-history.ts +19 -5
- package/src/core/model-tools.ts +28 -27
- package/src/core/phase3-host.ts +21 -5
- package/src/core/phase3-p2b-fingerprint.test.ts +22 -0
- package/src/core/plugin-cli.ts +18 -19
- package/src/core/reconcile.test.ts +117 -0
- package/src/core/reconcile.ts +85 -17
- package/src/core/recovery-orchestrator.ts +19 -18
- package/src/core/reinstall.ts +26 -21
- package/src/core/restore.ts +20 -17
- package/src/core/rollback-file-diff.test.ts +159 -0
- package/src/core/rollback-ignore-rules.ts +126 -0
- package/src/core/rollback-point.test.ts +2603 -0
- package/src/core/rollback-point.ts +3755 -0
- package/src/core/run-registry.ts +6 -5
- package/src/core/session-health.ts +15 -0
- package/src/core/session-meta.test.ts +117 -1
- package/src/core/session-meta.ts +149 -2
- package/src/core/session-title.test.ts +127 -0
- package/src/core/session-title.ts +189 -0
- package/src/core/startup-barrier.test.ts +69 -0
- package/src/core/startup-barrier.ts +27 -0
- package/src/core/transaction-coordinator.ts +4 -3
- package/src/core/types.ts +18 -1
- package/src/core/validator.ts +5 -4
- package/src/core/verify-recovery.ts +31 -30
- package/src/foreign/bundle.ts +3 -2
- package/src/foreign/continue.ts +2 -1
- package/src/foreign/crush.ts +2 -1
- package/src/foreign/file-budget.test.ts +2 -0
- package/src/foreign/gemini.ts +2 -1
- package/src/foreign/goose.ts +2 -1
- package/src/foreign/kilocode.ts +2 -1
- package/src/foreign/kimi.ts +2 -1
- package/src/foreign/messages.ts +44 -0
- package/src/foreign/mimocode.ts +2 -1
- package/src/foreign/openclaw.ts +2 -1
- package/src/foreign/opencode.ts +2 -1
- package/src/foreign/pi.ts +2 -1
- package/src/foreign/qoder.ts +2 -1
- package/src/foreign/qwen.ts +2 -1
- package/src/foreign/registry.ts +7 -6
- package/src/foreign/teleagent.ts +2 -1
- package/src/foreign/workbuddy.ts +2 -1
- package/src/foreign/zcode.ts +2 -1
- package/src/foreign/zed.ts +2 -1
- package/src/index.facade.test.ts +340 -1
- package/src/index.ts +813 -57
- package/src/market/git-file-writer.ts +20 -19
- package/src/market/github-repos.test.ts +23 -0
- package/src/market/github-repos.ts +37 -19
- package/src/market/index-parser.ts +31 -30
- package/src/market/messages-market.ts +325 -0
- package/src/market/my-repo.ts +32 -29
- package/src/market/prepare.ts +18 -15
- package/src/market/security.ts +20 -19
- package/src/market/types.ts +2 -1
- package/src/market/url.ts +2 -1
- package/src/migrations/index.ts +6 -5
- package/src/migrations/messages-migrations.ts +59 -0
- package/src/profiles/dsh-profile-launcher.ts +2 -1
- package/src/profiles/dsh-profile-runtime.ts +2 -1
- package/src/profiles/messages.ts +31 -0
- package/src/routes/backup.ts +18 -2
- package/src/routes/import.ts +3 -3
- package/src/routes/index.ts +1 -1
- package/src/routes/kit.ts +9 -3
- package/src/routes/market.ts +1 -1
- package/src/routes/messages.ts +104 -0
- package/src/routes/profiles.ts +12 -6
- package/src/routes/recovery.test.ts +405 -1
- package/src/routes/recovery.ts +377 -109
- package/src/routes/snapshots.ts +51 -4
- package/src/routes/sync.ts +67 -16
- package/src/schema/config.ts +53 -20
- package/src/schema/index.ts +7 -5
- package/src/schema/manifest.ts +19 -18
- package/src/schema/messages.ts +120 -0
- package/src/schema/registry.test.ts +19 -5
- package/src/schema/section-registry.ts +64 -39
- package/src/schema/versions.ts +6 -5
- package/src/security/encryption.ts +16 -15
- package/src/security/integrity.ts +8 -7
- package/src/security/messages.ts +87 -0
- package/src/security/redaction.test.ts +17 -1
- package/src/security/redaction.ts +13 -0
- package/src/security/secret-scanner.ts +19 -12
- package/src/security/security.test.ts +47 -0
- package/src/security/vault.ts +2 -1
- package/src/security/zip-security.ts +4 -3
- package/src/sync/ancestor.ts +2 -1
- package/src/sync/autosync-config.ts +21 -0
- package/src/sync/autosync-scheduler.test.ts +63 -7
- package/src/sync/autosync-scheduler.ts +118 -78
- package/src/sync/backup-files.ts +3 -2
- package/src/sync/backup-scheduler.ts +6 -5
- package/src/sync/blob-store.ts +2 -1
- package/src/sync/git/git-transport.test.ts +170 -8
- package/src/sync/git/git-transport.ts +176 -2
- package/src/sync/github-auth.ts +10 -9
- package/src/sync/layout.ts +13 -12
- package/src/sync/messages.ts +153 -0
- package/src/sync/review-queue.ts +6 -5
- package/src/sync/s3/messages.ts +16 -0
- package/src/sync/s3/s3-providers.ts +9 -6
- package/src/sync/s3/s3-transport.ts +1 -1
- package/src/sync/s3/sigv4.ts +3 -1
- package/src/sync/snapshot-crypto.ts +6 -5
- package/src/sync/snapshot-json.ts +3 -2
- package/src/sync/sync-engine-download-tombstone.test.ts +1 -1
- package/src/sync/sync-engine.test.ts +226 -8
- package/src/sync/sync-engine.ts +103 -41
- package/src/sync/sync-history.ts +7 -6
- package/src/sync/sync-selection.test.ts +12 -2
- package/src/sync/sync-selection.ts +18 -5
- package/src/sync/webdav/webdav-transport.ts +4 -1
- package/src/ui/artifact-view.ts +4 -3
- package/src/ui/commands.test.ts +1 -0
- package/src/ui/commands.ts +11 -1
- package/src/ui/disk-usage-view.test.ts +113 -6
- package/src/ui/disk-usage-view.ts +63 -7
- package/src/ui/errors.ts +4 -3
- package/src/ui/export-flow.ts +24 -20
- package/src/ui/foreign-view.test.ts +43 -0
- package/src/ui/foreign-view.ts +30 -3
- package/src/ui/i18n.ts +125 -0
- package/src/ui/import-wizard.ts +18 -10
- package/src/ui/progress.ts +2 -2
- package/src/ui/report.ts +7 -6
- package/src/ui/rollback-view.test.ts +819 -0
- package/src/ui/rollback-view.ts +1339 -0
- package/src/ui/selection-model.test.ts +33 -1
- package/src/ui/selection-model.ts +53 -0
- package/src/ui/session-inventory-view.test.ts +109 -0
- package/src/ui/session-inventory-view.ts +138 -3
- package/src/ui/session-issue-labels.test.ts +51 -0
- package/src/ui/session-issue-labels.ts +70 -0
- package/src/ui/sync-settings-view.test.ts +36 -0
- package/src/ui/sync-settings-view.ts +49 -0
- package/src/ui/types.ts +10 -4
- package/src/utils/atomic-write.ts +6 -5
- package/src/utils/bundle-scan.test.ts +2 -2
- package/src/utils/bundle-scan.ts +12 -1
- package/src/utils/env-lock.ts +83 -49
- package/src/utils/json.ts +7 -5
- package/src/utils/messages-utils.ts +232 -0
- package/src/utils/proxy.test.ts +25 -0
- package/src/utils/proxy.ts +19 -5
- package/src/utils/recursive-walk.test.ts +43 -0
- package/src/utils/recursive-walk.ts +75 -1
- package/src/utils/session-log-repair.ts +10 -9
- package/src/utils/zip.ts +27 -26
- package/lib/core/checkpoint.d.ts +0 -362
- package/lib/core/checkpoint.js +0 -1309
- package/src/core/checkpoint.test.ts +0 -665
- package/src/core/checkpoint.ts +0 -1614
|
@@ -29,6 +29,13 @@ export declare const s3Zh: {
|
|
|
29
29
|
readonly 'sync.s3.indexInvalid': '{url} 解析失败或结构非法: {err}';
|
|
30
30
|
readonly 'sync.s3.snapshotInvalid': '快照 {id} 解析失败或结构非法: {err}';
|
|
31
31
|
readonly 'sync.s3.indexMissingWithContent': '远端已有内容但索引 {url} 缺失(HTTP 404):拒绝以空集合覆盖权威索引,本次上传已中止(零写入)';
|
|
32
|
+
readonly 'sync.s3.providerNoteS3': 'AWS S3(默认 us-east-1;AWS 中国区/自建网关请覆盖 endpoint)';
|
|
33
|
+
readonly 'sync.s3.providerNoteOss': '阿里云 OSS(region 必填,如 cn-hangzhou;签名方言 OSS4,头名 x-oss-*)';
|
|
34
|
+
readonly 'sync.s3.providerNoteCos': '腾讯云 COS(region 必填,如 ap-guangzhou;2024 年后新建桶只支持 virtual-host 寻址)';
|
|
35
|
+
readonly 'sync.s3.providerNoteMinio': 'MinIO / 自建网关(endpoint 必填;默认 path-style,配了域名才用 virtual-host)';
|
|
36
|
+
readonly 'sync.s3.providerNoteKodo': '七牛 Kodo S3 兼容(region 必填,如 cn-east-1;endpoint 可覆盖)';
|
|
37
|
+
readonly 'sync.s3.unknownSignatureDialect': '未知签名方言: {id}';
|
|
38
|
+
readonly 'sync.s3.blobHashInvalid': '内容哈希形状非法,拒绝写入 blob 仓: {hash}';
|
|
32
39
|
};
|
|
33
40
|
export declare const s3En: Record<keyof typeof s3Zh, string>;
|
|
34
41
|
/**
|
package/lib/sync/s3/messages.js
CHANGED
|
@@ -17,6 +17,15 @@ export const s3Zh = {
|
|
|
17
17
|
'sync.s3.indexInvalid': '{url} 解析失败或结构非法: {err}',
|
|
18
18
|
'sync.s3.snapshotInvalid': '快照 {id} 解析失败或结构非法: {err}',
|
|
19
19
|
'sync.s3.indexMissingWithContent': '远端已有内容但索引 {url} 缺失(HTTP 404):拒绝以空集合覆盖权威索引,本次上传已中止(零写入)',
|
|
20
|
+
// ---- t6(英文界面零中文):变体说明(供 UI/文档用 msg() 解析;本字段自 t6 起是**目录键**而非文案)、
|
|
21
|
+
// 方言兜底与 blob 仓哈希形状守卫。键落本地目录 ⇒ composeMsg(host, s3Msg) 与「不传 msg」两条路径都不露裸键名。
|
|
22
|
+
'sync.s3.providerNoteS3': 'AWS S3(默认 us-east-1;AWS 中国区/自建网关请覆盖 endpoint)',
|
|
23
|
+
'sync.s3.providerNoteOss': '阿里云 OSS(region 必填,如 cn-hangzhou;签名方言 OSS4,头名 x-oss-*)',
|
|
24
|
+
'sync.s3.providerNoteCos': '腾讯云 COS(region 必填,如 ap-guangzhou;2024 年后新建桶只支持 virtual-host 寻址)',
|
|
25
|
+
'sync.s3.providerNoteMinio': 'MinIO / 自建网关(endpoint 必填;默认 path-style,配了域名才用 virtual-host)',
|
|
26
|
+
'sync.s3.providerNoteKodo': '七牛 Kodo S3 兼容(region 必填,如 cn-east-1;endpoint 可覆盖)',
|
|
27
|
+
'sync.s3.unknownSignatureDialect': '未知签名方言: {id}',
|
|
28
|
+
'sync.s3.blobHashInvalid': '内容哈希形状非法,拒绝写入 blob 仓: {hash}',
|
|
20
29
|
};
|
|
21
30
|
export const s3En = {
|
|
22
31
|
'sync.s3.providerUnknown': 'Unknown S3-compatible channel: {provider}',
|
|
@@ -37,6 +46,13 @@ export const s3En = {
|
|
|
37
46
|
'sync.s3.indexInvalid': '{url} failed to parse or is structurally invalid: {err}',
|
|
38
47
|
'sync.s3.snapshotInvalid': 'Snapshot {id} failed to parse or is structurally invalid: {err}',
|
|
39
48
|
'sync.s3.indexMissingWithContent': 'The remote already has content but index {url} is missing (HTTP 404): refusing to overwrite the authoritative index with an empty set; this upload was aborted (zero writes)',
|
|
49
|
+
'sync.s3.providerNoteS3': 'AWS S3 (defaults to us-east-1; override endpoint for AWS China or self-hosted gateways)',
|
|
50
|
+
'sync.s3.providerNoteOss': 'Alibaba Cloud OSS (region required, e.g. cn-hangzhou; OSS4 signature dialect with x-oss-* headers)',
|
|
51
|
+
'sync.s3.providerNoteCos': 'Tencent Cloud COS (region required, e.g. ap-guangzhou; buckets created after 2024 only support virtual-host addressing)',
|
|
52
|
+
'sync.s3.providerNoteMinio': 'MinIO / self-hosted gateway (endpoint required; path-style by default, virtual-host only when a domain is configured)',
|
|
53
|
+
'sync.s3.providerNoteKodo': 'Qiniu Kodo S3-compatible (region required, e.g. cn-east-1; endpoint can be overridden)',
|
|
54
|
+
'sync.s3.unknownSignatureDialect': 'Unknown signature dialect: {id}',
|
|
55
|
+
'sync.s3.blobHashInvalid': 'Malformed content hash; refusing to write to the blob store: {hash}',
|
|
40
56
|
};
|
|
41
57
|
/** 插值:{param} → 形参值;缺参原样保留(与 core/messages.ts 同口径)。 */
|
|
42
58
|
function interpolate(template, params) {
|
|
@@ -18,7 +18,10 @@ export interface S3ProviderVariant {
|
|
|
18
18
|
endpointTemplate?: string;
|
|
19
19
|
/** 默认寻址风格:true = path-style(<endpoint>/<bucket>/<key>),false = virtual-host(<bucket>.<endpoint>/<key>) */
|
|
20
20
|
defaultPathStyle: boolean;
|
|
21
|
-
/**
|
|
21
|
+
/**
|
|
22
|
+
* 变体说明的**目录键**(自 t6 起字段是键而不是文案 —— 中文只存在于 `sync/s3/messages.ts` 的本地目录,
|
|
23
|
+
* 经 `s3Msg` / `composeMsg(host, s3Msg)` 解析成当前语言;本字段本身不渲染)。
|
|
24
|
+
*/
|
|
22
25
|
note: string;
|
|
23
26
|
}
|
|
24
27
|
/**
|
|
@@ -31,26 +31,26 @@ export const S3_PROVIDER_VARIANTS = {
|
|
|
31
31
|
s3: {
|
|
32
32
|
id: 's3', dialectId: 'aws4', service: 's3', defaultRegion: 'us-east-1',
|
|
33
33
|
endpointTemplate: 'https://s3.{region}.amazonaws.com', defaultPathStyle: false,
|
|
34
|
-
note: '
|
|
34
|
+
note: 'sync.s3.providerNoteS3',
|
|
35
35
|
},
|
|
36
36
|
oss: {
|
|
37
37
|
id: 'oss', dialectId: 'oss4', service: 'oss',
|
|
38
38
|
endpointTemplate: 'https://oss-{region}.aliyuncs.com', defaultPathStyle: false,
|
|
39
|
-
note: '
|
|
39
|
+
note: 'sync.s3.providerNoteOss',
|
|
40
40
|
},
|
|
41
41
|
cos: {
|
|
42
42
|
id: 'cos', dialectId: 'aws4', service: 's3',
|
|
43
43
|
endpointTemplate: 'https://cos.{region}.myqcloud.com', defaultPathStyle: false,
|
|
44
|
-
note: '
|
|
44
|
+
note: 'sync.s3.providerNoteCos',
|
|
45
45
|
},
|
|
46
46
|
minio: {
|
|
47
47
|
id: 'minio', dialectId: 'aws4', service: 's3', defaultRegion: 'us-east-1', defaultPathStyle: true,
|
|
48
|
-
note: '
|
|
48
|
+
note: 'sync.s3.providerNoteMinio',
|
|
49
49
|
},
|
|
50
50
|
kodo: {
|
|
51
51
|
id: 'kodo', dialectId: 'aws4', service: 's3',
|
|
52
52
|
endpointTemplate: 'https://s3-{region}.qiniucs.com', defaultPathStyle: false,
|
|
53
|
-
note: '
|
|
53
|
+
note: 'sync.s3.providerNoteKodo',
|
|
54
54
|
},
|
|
55
55
|
};
|
|
56
56
|
/** 变体查表:非法通道 → undefined(调用方给 \`cloud.providerUnknown\` 码)。 */
|
|
@@ -447,7 +447,7 @@ export class S3Transport {
|
|
|
447
447
|
return {
|
|
448
448
|
put: async (hash, bytes) => {
|
|
449
449
|
if (!BLOB_HASH_RE.test(hash)) {
|
|
450
|
-
throw new S3TransportError('
|
|
450
|
+
throw new S3TransportError(this.o.msg('sync.s3.blobHashInvalid', { hash }));
|
|
451
451
|
}
|
|
452
452
|
if (index[hash] !== undefined)
|
|
453
453
|
return; // 内容未变 → 一个字都不传
|
package/lib/sync/s3/sigv4.js
CHANGED
|
@@ -19,6 +19,7 @@
|
|
|
19
19
|
* - 不做任何 I/O:请求头的注入与发送由通道层(s3-transport)负责。
|
|
20
20
|
*/
|
|
21
21
|
import { createHash, createHmac } from 'node:crypto';
|
|
22
|
+
import { s3Msg } from './messages.js';
|
|
22
23
|
/** 「不校验载荷」的载荷哈希字面量(OSS4 目前只支持该取值)。 */
|
|
23
24
|
export const UNSIGNED_PAYLOAD = 'UNSIGNED-PAYLOAD';
|
|
24
25
|
/** AWS SigV4(S3 / MinIO / COS / Kodo 共用)。 */
|
|
@@ -51,7 +52,7 @@ export function sigV4Dialect(id) {
|
|
|
51
52
|
return AWS_SIG_V4;
|
|
52
53
|
if (id === 'oss4')
|
|
53
54
|
return OSS_SIG_V4;
|
|
54
|
-
throw new Error('
|
|
55
|
+
throw new Error(s3Msg('sync.s3.unknownSignatureDialect', { id: String(id) }));
|
|
55
56
|
}
|
|
56
57
|
/** SHA-256 十六进制小写。 */
|
|
57
58
|
export function sha256Hex(data) {
|
|
@@ -17,6 +17,7 @@ import { collectCredentialRefs } from '../security/credentials-yaml.js';
|
|
|
17
17
|
import { decryptCredentials, encryptCredentials } from '../security/encryption.js';
|
|
18
18
|
import { parseJsonSafe, stringifyJsonSafe } from '../utils/json.js';
|
|
19
19
|
import { sectionsFromJsonSafe, sectionsToJsonSafe } from './snapshot-json.js';
|
|
20
|
+
import { engineMsg } from './messages.js';
|
|
20
21
|
/**
|
|
21
22
|
* 加密整个明文 sections Record → 密文载荷(info 进 manifest 非秘密参数;data 为 base64 密文)。
|
|
22
23
|
* 序列化经 snapshot-json:文件类分区字节以 base64 进入载荷,解密后还原 Uint8Array
|
|
@@ -24,18 +25,18 @@ import { sectionsFromJsonSafe, sectionsToJsonSafe } from './snapshot-json.js';
|
|
|
24
25
|
*/
|
|
25
26
|
export async function encryptSectionsPayload(sections, password) {
|
|
26
27
|
if (password === '')
|
|
27
|
-
throw new Error('
|
|
28
|
+
throw new Error(engineMsg('sync.encryptPasswordRequired'));
|
|
28
29
|
const { blob, info } = await encryptCredentials(stringifyJsonSafe(sectionsToJsonSafe(sections)), password);
|
|
29
30
|
return { encrypted: { info, data: Buffer.from(blob).toString('base64') } };
|
|
30
31
|
}
|
|
31
32
|
/** 解密密文载荷 → 明文 sections Record(密码错误 / 密文被篡改 → SecurityError)。 */
|
|
32
33
|
export async function decryptSectionsPayload(payload, password) {
|
|
33
34
|
if (password === '')
|
|
34
|
-
throw new Error('
|
|
35
|
+
throw new Error(engineMsg('sync.decryptPasswordRequired'));
|
|
35
36
|
const plain = await decryptCredentials(Buffer.from(payload.data, 'base64'), payload.info, password);
|
|
36
37
|
const parsed = parseJsonSafe(plain);
|
|
37
38
|
if (parsed === null || typeof parsed !== 'object' || Array.isArray(parsed)) {
|
|
38
|
-
throw new Error('
|
|
39
|
+
throw new Error(engineMsg('sync.encryptedContentInvalid'));
|
|
39
40
|
}
|
|
40
41
|
return sectionsFromJsonSafe(parsed);
|
|
41
42
|
}
|
|
@@ -50,14 +51,14 @@ export function encryptionInfoOf(payload) {
|
|
|
50
51
|
*/
|
|
51
52
|
export async function encryptCredentialsPayload(plaintext, password) {
|
|
52
53
|
if (password === '')
|
|
53
|
-
throw new Error('
|
|
54
|
+
throw new Error(engineMsg('sync.encryptPasswordRequired'));
|
|
54
55
|
const { blob, info } = await encryptCredentials(plaintext, password);
|
|
55
56
|
return { info, data: Buffer.from(blob).toString('base64') };
|
|
56
57
|
}
|
|
57
58
|
/** 解密凭据载荷 → `.credentials.yaml` 原文(密码错误 / 密文被篡改 → SecurityError)。 */
|
|
58
59
|
export async function decryptCredentialsPayload(payload, password) {
|
|
59
60
|
if (password === '')
|
|
60
|
-
throw new Error('
|
|
61
|
+
throw new Error(engineMsg('sync.decryptPasswordRequired'));
|
|
61
62
|
return await decryptCredentials(Buffer.from(payload.data, 'base64'), payload.info, password);
|
|
62
63
|
}
|
|
63
64
|
/**
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { MAX_OWN_PAYLOAD_JSON_BYTES, parseJsonSafe, stringifyJsonSafe } from '../utils/json.js';
|
|
2
2
|
import { isEncryptedCredentials, isEncryptedSections } from './transport.js';
|
|
3
|
+
import { engineMsg } from './messages.js';
|
|
3
4
|
/** 二进制数据在 JSON 中的标记对象键(base64 载荷)。 */
|
|
4
5
|
const BIN_KEY = '$bin';
|
|
5
6
|
/** duck-typing:文件类分区(version===1 + files 数组) */
|
|
@@ -68,7 +69,7 @@ export function sectionsFromJsonSafe(sections) {
|
|
|
68
69
|
if (isEncryptedSections(sections))
|
|
69
70
|
return sections;
|
|
70
71
|
if (sections === null || typeof sections !== 'object' || Array.isArray(sections)) {
|
|
71
|
-
throw new Error('
|
|
72
|
+
throw new Error(engineMsg('sync.snapshotSectionsNotObject'));
|
|
72
73
|
}
|
|
73
74
|
const out = {};
|
|
74
75
|
for (const [id, data] of Object.entries(sections)) {
|
|
@@ -104,7 +105,7 @@ export function deserializeSnapshot(raw) {
|
|
|
104
105
|
&& typeof snap.manifest === 'object' && snap.manifest !== null
|
|
105
106
|
&& typeof snap.sections === 'object' && snap.sections !== null;
|
|
106
107
|
if (!okShape) {
|
|
107
|
-
throw new Error('
|
|
108
|
+
throw new Error(engineMsg('sync.snapshotJsonInvalid'));
|
|
108
109
|
}
|
|
109
110
|
// issue #38:凭据载荷(形状不合法 → 视为不存在,不让坏数据破坏整份快照解析)
|
|
110
111
|
const rawCredentials = snap.credentials;
|
|
@@ -37,14 +37,17 @@ export interface SyncEngineOptions {
|
|
|
37
37
|
/** 消息翻译器(缺省 ctx.msg ?? zh) */
|
|
38
38
|
msg?: MsgFunc;
|
|
39
39
|
/**
|
|
40
|
-
*
|
|
41
|
-
* 的参与:只有 true 时,远端快照里的可选分区才会进入临时 ZIP 与合并输入。
|
|
40
|
+
* 本次运行**显式放行的「可选分区」集合**(拉取/合并/应用 + 推送两侧共用)。
|
|
42
41
|
*
|
|
43
|
-
*
|
|
44
|
-
* pull /
|
|
45
|
-
*
|
|
42
|
+
* 缺省空集 —— 可选分区默认永不参与同步。宿主按「该分区允许哪种驱动」决定传什么:
|
|
43
|
+
* - 手动 push/pull / 一键同步 → 持久化选择里的全部可选分区(OPT_IN_SYNC_SECTIONS ∩ 勾选);
|
|
44
|
+
* - 自动同步 / model-tools → 只传 'auto' 档(AUTO_SYNC_OPT_IN_SECTION_IDS ∩ 勾选),
|
|
45
|
+
* sessions('manual')因此**绝不悄悄下行/上行**。
|
|
46
|
+
*
|
|
47
|
+
* 为什么是集合而不是布尔:布尔只能表达「全放行 / 全不放行」,勾了 mcp 会连带放行
|
|
48
|
+
* sessions(issue #78 的回归点)。集合化后每个分区独立放行。
|
|
46
49
|
*/
|
|
47
|
-
|
|
50
|
+
optInSections?: readonly SectionId[];
|
|
48
51
|
/**
|
|
49
52
|
* 同步范围(高级/自定义导出模式持久化配置):只处理这些 portable 分区。
|
|
50
53
|
* 缺省 = 全部 portable 推荐分区。应用于 push / merge / applyMergePlan / applyItems
|
|
@@ -69,12 +72,15 @@ export interface SyncPushOptions {
|
|
|
69
72
|
/** 仅同步指定分区(缺省 = 全部 portable 推荐分区,即「默认/快速导出」模式)。
|
|
70
73
|
* 传入非 portable 或未知分区 → 忽略并告警(同步通道安全约束:
|
|
71
74
|
* deviceSpecific/platformSpecific 默认永不进入同步通道;未知 id 不静默吞掉)。
|
|
72
|
-
*
|
|
73
|
-
*
|
|
75
|
+
* 例外:构造期显式放行的可选分区(SyncEngineOptions.optInSections)才纳入 ——
|
|
76
|
+
* 未被放行的一律按非 portable 跳过并告警(不静默)。 */
|
|
74
77
|
sections?: SectionId[];
|
|
75
78
|
/**
|
|
76
|
-
* sessions
|
|
77
|
-
*
|
|
79
|
+
* sessions(历史会话)分区的**导出参数**(「最新 N 个会话」上限 / 逐会话点名)。
|
|
80
|
+
*
|
|
81
|
+
* 注意:它**不再兼任放行开关** —— 是否允许 sessions 进通道只由构造期
|
|
82
|
+
* SyncEngineOptions.optInSections 决定(issue #78 把「放行」与「参数」拆开,
|
|
83
|
+
* 否则勾了 mcp 会顺带解锁 sessions)。这里只决定「带哪几个会话」。
|
|
78
84
|
*
|
|
79
85
|
* include(显式勾选的会话单元 id,形如 sessions:<projectKey>/<sessionId>)非空时
|
|
80
86
|
* **优先于 limit**:用户点名的对话必须赢;此时不施加「最新 N 个」数量筛选,否则会退化成
|
|
@@ -182,9 +188,16 @@ export interface ApplyReport {
|
|
|
182
188
|
restoreId: string;
|
|
183
189
|
/** 是否触发了整体回滚 */
|
|
184
190
|
rolledBack: boolean;
|
|
191
|
+
/**
|
|
192
|
+
* 整体回滚是否**完整**(诚实性,2026-10)。rolledBack=true 只说明触发了回滚,
|
|
193
|
+
* 不代表每一项都补偿成功(典型:原本已存在的凭据值 DSH 不可回读、无法自动恢复)。
|
|
194
|
+
* false 时失败项已并入 warnings,界面不得渲染成「整体成功回滚」。
|
|
195
|
+
* 缺省 undefined = 没触发回滚(既有布尔字段语义不变)。
|
|
196
|
+
*/
|
|
197
|
+
rollbackFull?: boolean;
|
|
185
198
|
/** 失败时移到 review 队列的项(ReviewQueueItem 形态供 UI 直接渲染) */
|
|
186
199
|
review: import('./review-queue.ts').ReviewQueueItem[];
|
|
187
|
-
/** 来自 Importer.ImportResult 的 warnings;UI
|
|
200
|
+
/** 来自 Importer.ImportResult 的 warnings;UI 可用于红条提示(含回滚未完成项) */
|
|
188
201
|
warnings: string[];
|
|
189
202
|
}
|
|
190
203
|
/** applyItems 报告(§3.4 ApplyItemsResponse 的服务端形态) */
|
|
@@ -198,6 +211,12 @@ export interface ApplyItemsReport {
|
|
|
198
211
|
restoreId: string;
|
|
199
212
|
/** 任一失败是否整体回滚 */
|
|
200
213
|
rolledBack: boolean;
|
|
214
|
+
/**
|
|
215
|
+
* 整体回滚是否**完整**(诚实性,2026-10;与 ApplyReport.rollbackFull 同一语义):
|
|
216
|
+
* rolledBack=true 只说明触发了整体回滚。false = 有改动没能恢复,失败项已并入 warnings。
|
|
217
|
+
* 缺省 undefined = 未触发回滚(既有布尔字段语义不变)。
|
|
218
|
+
*/
|
|
219
|
+
rollbackFull?: boolean;
|
|
201
220
|
warnings: string[];
|
|
202
221
|
failed: {
|
|
203
222
|
itemId: string;
|
|
@@ -246,7 +265,8 @@ export declare class SyncEngine {
|
|
|
246
265
|
private readonly fsx;
|
|
247
266
|
private readonly msg;
|
|
248
267
|
private readonly sections;
|
|
249
|
-
|
|
268
|
+
/** 本次显式放行的可选分区(构造期已归一:只可能是 OPT_IN_SYNC_SECTIONS 的注册项) */
|
|
269
|
+
private readonly optInSections;
|
|
250
270
|
/** 远端快照保留策略提供者(缺省 = 保守的「保留最近 10 个」,与改造前逐字等价) */
|
|
251
271
|
private readonly retentionPolicyProvider;
|
|
252
272
|
/**
|
|
@@ -270,20 +290,22 @@ export declare class SyncEngine {
|
|
|
270
290
|
private portableAdapters;
|
|
271
291
|
/**
|
|
272
292
|
* 拉取/合并侧的分区范围 id 集合:portable(受注入的同步范围约束)
|
|
273
|
-
* +
|
|
293
|
+
* + **本次显式放行**的可选分区(optInSections)。
|
|
274
294
|
*
|
|
275
295
|
* 为什么单独一个助手:pull/preview/merge 都要把「远端快照里的哪些分区转进临时 ZIP」,
|
|
276
296
|
* 三处必须同一口径;散开写 `portableAdapters().map(...)` 迟早漏掉一处(漏掉的表现是
|
|
277
297
|
* 「推上去了、拉下来静默没有」)。
|
|
278
298
|
*/
|
|
279
299
|
private pullSectionIds;
|
|
300
|
+
/** 本次显式放行的「可选分区」adapter(空集 = 一个都不放行;顺序跟随 adapters) */
|
|
301
|
+
private optInAdapters;
|
|
280
302
|
/**
|
|
281
303
|
* push 候选 adapter:
|
|
282
304
|
* - sections 缺省/空 → 全部 portable(「默认/快速导出」模式);
|
|
283
305
|
* - sections 显式给出 → 只取 portable 且命中的(「高级/自定义导出」模式);
|
|
284
306
|
* 非 portable / 未知分区 → 警告跳过(安全约束 + 不静默,用户能看见自己勾了哪个无效项);
|
|
285
|
-
* -
|
|
286
|
-
*
|
|
307
|
+
* - 例外:本次构造显式放行的可选分区(optInSections,见 SyncEngineOptions)作为候选取出。
|
|
308
|
+
* 缺省空集 → 与改造前逐字等价(可选分区一律不进同步通道)。
|
|
287
309
|
*/
|
|
288
310
|
private pushTargets;
|
|
289
311
|
/** 结构性断言:凭据/秘密分区绝不进入同步载荷(双保险,portable 过滤之上) */
|
package/lib/sync/sync-engine.js
CHANGED
|
@@ -37,11 +37,39 @@ import { DEFAULT_RETENTION_POLICY, selectPruneCandidatesByPolicy } from './reten
|
|
|
37
37
|
import { hashSection, loadSyncState, saveSyncState } from './sync-state.js';
|
|
38
38
|
import { isEncryptedSections } from './transport.js';
|
|
39
39
|
import { msgOf, zhMsg } from '../core/messages.js';
|
|
40
|
+
import { engineMsg } from './messages.js';
|
|
40
41
|
import { DEFAULT_ANCESTOR_KEEP, pruneAncestors, tryLoadAncestor, writeAncestor } from './ancestor.js';
|
|
41
42
|
import { merge as mergeSections } from './merge.js';
|
|
42
43
|
import { createSnapshot, selectPruneCandidates } from '../core/backup.js';
|
|
43
44
|
import { rollback } from '../core/rollback.js';
|
|
44
45
|
import { FileSnapshotStore } from '../core/backup.js';
|
|
46
|
+
/**
|
|
47
|
+
* 失败路径的**显式兜底回滚**:把 core 的 RollbackReport 翻成「是否完整 + 告警行」。
|
|
48
|
+
*
|
|
49
|
+
* 为什么不能像改造前那样 catch 后 noop:回滚抛错或部分补偿失败会被静默吞掉,而调用方
|
|
50
|
+
* 紧接着写 rolledBack: true,界面于是把「有改动没恢复」渲染成「已整体回滚」。
|
|
51
|
+
* 现在失败项逐条进 warnings,完整度经 rollbackFull 如实回传。
|
|
52
|
+
*/
|
|
53
|
+
async function rollbackForFailure(ctx, snapshot, store, adapters) {
|
|
54
|
+
try {
|
|
55
|
+
const report = await rollback({ ctx, snapshot, store, adapters });
|
|
56
|
+
return { full: report.full, warnings: rollbackFailureWarnings(report, msgOf(ctx)) };
|
|
57
|
+
}
|
|
58
|
+
catch (err) {
|
|
59
|
+
// 回滚自身抛错 = 无法证明任何一项已还原:如实报「不完整」并留痕,绝不 noop
|
|
60
|
+
return {
|
|
61
|
+
full: false,
|
|
62
|
+
warnings: [engineMsg('sync.rollbackProofFailed', { err: err instanceof Error ? err.message : String(err) })],
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
/** RollbackReport.failed → 可读告警行(带 manualHint:人工怎么补)。 */
|
|
67
|
+
function rollbackFailureWarnings(report, msg = engineMsg) {
|
|
68
|
+
return report.failed.map((f) => {
|
|
69
|
+
const hint = f.manualHint !== undefined && f.manualHint !== '' ? msg('sync.rollbackManualHintParen', { hint: f.manualHint }) : '';
|
|
70
|
+
return msg('sync.rollbackIncomplete', { item: f.item, reason: f.reason, hint });
|
|
71
|
+
});
|
|
72
|
+
}
|
|
45
73
|
/** 同步通道结构性排除的敏感分区(即使 portable 判定有误也双保险拒绝;
|
|
46
74
|
* 注:'credentials' 不是合法 SectionId——凭据状态分区为 credentialsStatus) */
|
|
47
75
|
const FORBIDDEN_SECTIONS = ['credentialsStatus', 'secrets'];
|
|
@@ -72,7 +100,8 @@ export class SyncEngine {
|
|
|
72
100
|
fsx;
|
|
73
101
|
msg;
|
|
74
102
|
sections;
|
|
75
|
-
|
|
103
|
+
/** 本次显式放行的可选分区(构造期已归一:只可能是 OPT_IN_SYNC_SECTIONS 的注册项) */
|
|
104
|
+
optInSections;
|
|
76
105
|
/** 远端快照保留策略提供者(缺省 = 保守的「保留最近 10 个」,与改造前逐字等价) */
|
|
77
106
|
retentionPolicyProvider;
|
|
78
107
|
/**
|
|
@@ -116,7 +145,8 @@ export class SyncEngine {
|
|
|
116
145
|
this.fsx = opts.fsx ?? createSnapshotFs();
|
|
117
146
|
this.msg = opts.msg ?? msgOf(opts.ctx);
|
|
118
147
|
this.sections = opts.sections !== undefined && opts.sections.length > 0 ? [...opts.sections] : undefined;
|
|
119
|
-
|
|
148
|
+
// 归一:只有注册表声明的「可选分区」能被放行 —— 调用方传错 id 不会凭空解锁别的分区
|
|
149
|
+
this.optInSections = new Set((opts.optInSections ?? []).filter((id) => OPT_IN_SYNC_SECTIONS.includes(id)));
|
|
120
150
|
this.retentionPolicyProvider = opts.retentionPolicy ?? (() => DEFAULT_RETENTION_POLICY);
|
|
121
151
|
}
|
|
122
152
|
/** 同步只做 portable 分区(deviceSpecific/platformSpecific 永不参与)。
|
|
@@ -131,7 +161,7 @@ export class SyncEngine {
|
|
|
131
161
|
}
|
|
132
162
|
/**
|
|
133
163
|
* 拉取/合并侧的分区范围 id 集合:portable(受注入的同步范围约束)
|
|
134
|
-
* +
|
|
164
|
+
* + **本次显式放行**的可选分区(optInSections)。
|
|
135
165
|
*
|
|
136
166
|
* 为什么单独一个助手:pull/preview/merge 都要把「远端快照里的哪些分区转进临时 ZIP」,
|
|
137
167
|
* 三处必须同一口径;散开写 `portableAdapters().map(...)` 迟早漏掉一处(漏掉的表现是
|
|
@@ -139,33 +169,35 @@ export class SyncEngine {
|
|
|
139
169
|
*/
|
|
140
170
|
pullSectionIds() {
|
|
141
171
|
const ids = this.portableAdapters().map((a) => a.id);
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
if (OPT_IN_SYNC_SECTIONS.includes(a.id))
|
|
145
|
-
ids.push(a.id);
|
|
146
|
-
}
|
|
147
|
-
}
|
|
172
|
+
for (const a of this.optInAdapters())
|
|
173
|
+
ids.push(a.id);
|
|
148
174
|
return new Set(ids);
|
|
149
175
|
}
|
|
176
|
+
/** 本次显式放行的「可选分区」adapter(空集 = 一个都不放行;顺序跟随 adapters) */
|
|
177
|
+
optInAdapters() {
|
|
178
|
+
if (this.optInSections.size === 0)
|
|
179
|
+
return [];
|
|
180
|
+
return this.adapters.filter((a) => this.optInSections.has(a.id));
|
|
181
|
+
}
|
|
150
182
|
/**
|
|
151
183
|
* push 候选 adapter:
|
|
152
184
|
* - sections 缺省/空 → 全部 portable(「默认/快速导出」模式);
|
|
153
185
|
* - sections 显式给出 → 只取 portable 且命中的(「高级/自定义导出」模式);
|
|
154
186
|
* 非 portable / 未知分区 → 警告跳过(安全约束 + 不静默,用户能看见自己勾了哪个无效项);
|
|
155
|
-
* -
|
|
156
|
-
*
|
|
187
|
+
* - 例外:本次构造显式放行的可选分区(optInSections,见 SyncEngineOptions)作为候选取出。
|
|
188
|
+
* 缺省空集 → 与改造前逐字等价(可选分区一律不进同步通道)。
|
|
157
189
|
*/
|
|
158
|
-
pushTargets(sections, warnings
|
|
190
|
+
pushTargets(sections, warnings) {
|
|
159
191
|
const portable = this.portableAdapters();
|
|
192
|
+
const optIn = this.optInAdapters();
|
|
193
|
+
// 默认模式(未指定 sections):portable 全量 + 本次显式放行的可选分区。
|
|
194
|
+
// 自动同步的 push() 不带 sections,可选分区能否上榜完全由构造期 optInSections 决定
|
|
195
|
+
// (scheduler 只传 'auto' 档 → sessions 永不悄悄上行)。
|
|
160
196
|
if (sections === undefined || sections.length === 0)
|
|
161
|
-
return portable;
|
|
197
|
+
return [...portable, ...optIn];
|
|
162
198
|
const byId = new Map(portable.map((a) => [a.id, a]));
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
if (OPT_IN_SYNC_SECTIONS.includes(a.id))
|
|
166
|
-
byId.set(a.id, a);
|
|
167
|
-
}
|
|
168
|
-
}
|
|
199
|
+
for (const a of optIn)
|
|
200
|
+
byId.set(a.id, a);
|
|
169
201
|
const out = [];
|
|
170
202
|
for (const id of sections) {
|
|
171
203
|
const adapter = byId.get(id);
|
|
@@ -328,7 +360,7 @@ export class SyncEngine {
|
|
|
328
360
|
throw new Error(this.msg('sync.encryptRequiresPassword'));
|
|
329
361
|
}
|
|
330
362
|
const plainSections = {};
|
|
331
|
-
const targets = this.pushTargets(opts.sections, warnings
|
|
363
|
+
const targets = this.pushTargets(opts.sections, warnings);
|
|
332
364
|
for (const adapter of targets) {
|
|
333
365
|
let section;
|
|
334
366
|
try {
|
|
@@ -440,7 +472,7 @@ export class SyncEngine {
|
|
|
440
472
|
credentialsIncluded = false; // 读不到 = 不会带凭据;预览如实显示 false
|
|
441
473
|
}
|
|
442
474
|
}
|
|
443
|
-
const targets = this.pushTargets(opts.sections, warnings
|
|
475
|
+
const targets = this.pushTargets(opts.sections, warnings);
|
|
444
476
|
const plainSections = {};
|
|
445
477
|
const counts = {};
|
|
446
478
|
for (const adapter of targets) {
|
|
@@ -942,7 +974,7 @@ export class SyncEngine {
|
|
|
942
974
|
*/
|
|
943
975
|
async applyMergePlan(apply, opts = {}) {
|
|
944
976
|
if (!this.importer) {
|
|
945
|
-
throw new Error('
|
|
977
|
+
throw new Error(this.msg('sync.applyMergePlanNoImporter'));
|
|
946
978
|
}
|
|
947
979
|
const appliedIds = apply.autoApply.map((r) => r.id);
|
|
948
980
|
// 0) 空 autoApply:无物可应用,直接短路(不构造 ZIP、不调 Importer)
|
|
@@ -1001,7 +1033,7 @@ export class SyncEngine {
|
|
|
1001
1033
|
restoreId: '',
|
|
1002
1034
|
rolledBack: false,
|
|
1003
1035
|
review: [],
|
|
1004
|
-
warnings: [
|
|
1036
|
+
warnings: [this.msg('sync.preApplySnapshotFailed', { err: backupErr instanceof Error ? backupErr.message : String(backupErr) })],
|
|
1005
1037
|
};
|
|
1006
1038
|
}
|
|
1007
1039
|
// 3) 真正执行:Importer.executeImportPlan(rollbackOnError=true → 任一失败整体回滚)
|
|
@@ -1017,10 +1049,8 @@ export class SyncEngine {
|
|
|
1017
1049
|
snapshotBinding: opts.snapshotBinding,
|
|
1018
1050
|
});
|
|
1019
1051
|
if (!result.ok) {
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
}
|
|
1023
|
-
catch { /* noop */ }
|
|
1052
|
+
// 兜底回滚:把「是否完整」与失败项一并回传(绝不吞掉,也绝不把部分回滚说成整体回滚)
|
|
1053
|
+
const rb = await rollbackForFailure(this.ctx, snapshot, store, this.adapters);
|
|
1024
1054
|
// 失败路径不再写 review-queue(§7.4):历史 skipped/failed 标记由上层(路由/调度器)写入。
|
|
1025
1055
|
return {
|
|
1026
1056
|
ok: false,
|
|
@@ -1028,7 +1058,8 @@ export class SyncEngine {
|
|
|
1028
1058
|
restoreId: snapshot.id,
|
|
1029
1059
|
rolledBack: true,
|
|
1030
1060
|
review: [],
|
|
1031
|
-
warnings: result.warnings ?? [],
|
|
1061
|
+
warnings: [...(result.warnings ?? []), ...rb.warnings],
|
|
1062
|
+
rollbackFull: rb.full,
|
|
1032
1063
|
};
|
|
1033
1064
|
}
|
|
1034
1065
|
// 5) 全成功 → recordBaseline(更新祖先基线指向合并后的快照)
|
|
@@ -1070,7 +1101,7 @@ export class SyncEngine {
|
|
|
1070
1101
|
*/
|
|
1071
1102
|
async applyItems(zipPath, subPlan, opts = {}) {
|
|
1072
1103
|
if (!this.importer) {
|
|
1073
|
-
throw new Error('
|
|
1104
|
+
throw new Error(this.msg('sync.applyItemsNoImporter'));
|
|
1074
1105
|
}
|
|
1075
1106
|
if (subPlan.items.length === 0) {
|
|
1076
1107
|
return { ok: true, applied: [], restoreId: '', rolledBack: false, warnings: [], failed: [], ineffective: [], result: null };
|
|
@@ -1093,7 +1124,7 @@ export class SyncEngine {
|
|
|
1093
1124
|
applied: [],
|
|
1094
1125
|
restoreId: '',
|
|
1095
1126
|
rolledBack: false,
|
|
1096
|
-
warnings: [
|
|
1127
|
+
warnings: [this.msg('sync.preApplySnapshotFailed', { err: backupErr instanceof Error ? backupErr.message : String(backupErr) })],
|
|
1097
1128
|
failed: subPlan.items.map((i) => ({ itemId: i.id })),
|
|
1098
1129
|
// 快照都没建成 ⇒ 一项都没执行,全部项都未生效(与 failed 同源,界面按硬失败处理)
|
|
1099
1130
|
ineffective: [],
|
|
@@ -1111,20 +1142,19 @@ export class SyncEngine {
|
|
|
1111
1142
|
snapshotBinding: opts.snapshotBinding,
|
|
1112
1143
|
});
|
|
1113
1144
|
if (!result.ok) {
|
|
1114
|
-
// executeImportPlan 已内部回滚(rollbackOnError);这里再显式 rollback
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
}
|
|
1118
|
-
catch { /* noop */ }
|
|
1145
|
+
// executeImportPlan 已内部回滚(rollbackOnError);这里再显式 rollback 兜底(幂等),
|
|
1146
|
+
// 并把「是否完整」与失败项如实回传(改造前 noop + 硬编码 rolledBack:true 会掩盖半回滚)
|
|
1147
|
+
const rb = await rollbackForFailure(this.ctx, snapshot, store, this.adapters);
|
|
1119
1148
|
return {
|
|
1120
1149
|
ok: false,
|
|
1121
1150
|
applied: [],
|
|
1122
1151
|
restoreId: snapshot.id,
|
|
1123
1152
|
rolledBack: true,
|
|
1124
|
-
warnings: result.warnings ?? [],
|
|
1153
|
+
warnings: [...(result.warnings ?? []), ...rb.warnings],
|
|
1125
1154
|
failed: result.executed.filter((e) => e.status === 'failed').map((e) => ({ itemId: e.itemId, message: e.message })),
|
|
1126
1155
|
ineffective: result.ineffective ?? [],
|
|
1127
1156
|
result,
|
|
1157
|
+
rollbackFull: rb.full,
|
|
1128
1158
|
};
|
|
1129
1159
|
}
|
|
1130
1160
|
// 成功:recordBaseline 更新祖先基线(合并后的快照)
|
|
@@ -1170,7 +1200,7 @@ export class SyncEngine {
|
|
|
1170
1200
|
async snapshotToZip(snapshot, portableIds) {
|
|
1171
1201
|
// 防御:加密快照必须已由调用方解密(prepareSnapshot);此处不处理密文载荷
|
|
1172
1202
|
if (isEncryptedSections(snapshot.sections)) {
|
|
1173
|
-
throw new Error('
|
|
1203
|
+
throw new Error(this.msg('sync.snapshotStillEncrypted'));
|
|
1174
1204
|
}
|
|
1175
1205
|
const entries = [];
|
|
1176
1206
|
const sectionFlags = {};
|
package/lib/sync/sync-history.js
CHANGED
|
@@ -12,6 +12,7 @@ import fs from 'node:fs/promises';
|
|
|
12
12
|
import path from 'node:path';
|
|
13
13
|
import { parseJsonSafe, stringifyJsonSafe } from '../utils/json.js';
|
|
14
14
|
import { atomicWriteFile } from '../utils/atomic-write.js';
|
|
15
|
+
import { engineMsg } from './messages.js';
|
|
15
16
|
export const SYNC_HISTORY_FILE = 'sync-history.json';
|
|
16
17
|
export const SYNC_HISTORY_SCHEMA_VERSION = 1;
|
|
17
18
|
/** 自动同步历史保留上限 */
|
|
@@ -31,26 +32,26 @@ export async function readSyncHistory(dir) {
|
|
|
31
32
|
parsed = parseJsonSafe(raw);
|
|
32
33
|
}
|
|
33
34
|
catch {
|
|
34
|
-
throw new Error(
|
|
35
|
+
throw new Error(engineMsg('sync.historyJsonParseFailed', { file: SYNC_HISTORY_FILE }));
|
|
35
36
|
}
|
|
36
37
|
if (parsed === null || typeof parsed !== 'object' || Array.isArray(parsed)) {
|
|
37
|
-
throw new Error(
|
|
38
|
+
throw new Error(engineMsg('sync.historyNotObject', { file: SYNC_HISTORY_FILE }));
|
|
38
39
|
}
|
|
39
40
|
const obj = parsed;
|
|
40
41
|
if (obj['schemaVersion'] !== SYNC_HISTORY_SCHEMA_VERSION) {
|
|
41
|
-
throw new Error(
|
|
42
|
+
throw new Error(engineMsg('sync.historySchemaVersionMismatch', { file: SYNC_HISTORY_FILE, version: SYNC_HISTORY_SCHEMA_VERSION }));
|
|
42
43
|
}
|
|
43
44
|
if (!Array.isArray(obj['autosyncEntries'])) {
|
|
44
|
-
throw new Error(
|
|
45
|
+
throw new Error(engineMsg('sync.historyEntriesNotArray', { file: SYNC_HISTORY_FILE }));
|
|
45
46
|
}
|
|
46
47
|
const entries = [];
|
|
47
48
|
for (const it of obj['autosyncEntries']) {
|
|
48
49
|
if (it === null || typeof it !== 'object' || Array.isArray(it)) {
|
|
49
|
-
throw new Error(
|
|
50
|
+
throw new Error(engineMsg('sync.historyEntryNotObject', { file: SYNC_HISTORY_FILE }));
|
|
50
51
|
}
|
|
51
52
|
const e = it;
|
|
52
53
|
if (typeof e['direction'] !== 'string' || typeof e['status'] !== 'string' || typeof e['createdAt'] !== 'string') {
|
|
53
|
-
throw new Error(
|
|
54
|
+
throw new Error(engineMsg('sync.historyEntryFieldsMissing', { file: SYNC_HISTORY_FILE }));
|
|
54
55
|
}
|
|
55
56
|
entries.push(it);
|
|
56
57
|
}
|
|
@@ -33,13 +33,24 @@ export interface SyncSelection {
|
|
|
33
33
|
/** 全通道选择视图(v2 文件直接读取;status 路由一次返回两个通道的选择)。 */
|
|
34
34
|
export type SyncSelectionByChannel = Record<SyncTransportType, SyncSelection>;
|
|
35
35
|
/**
|
|
36
|
-
*
|
|
36
|
+
* 显式勾选才允许进入同步通道的「可选分区」。
|
|
37
37
|
*
|
|
38
|
-
*
|
|
39
|
-
*
|
|
40
|
-
*
|
|
38
|
+
* **单一事实源 = 分区注册表**(\`SectionMeta.syncOptIn !== 'off'\`)。本文件此前手写
|
|
39
|
+
* \`['sessions']\`,与 \`section-registry.ts\` 的同名字段是**两份字面量、零护栏**
|
|
40
|
+
* (改一处漏一处不会报错)—— issue #78 收敛为派生视图,并在测试里钉住。
|
|
41
|
+
*
|
|
42
|
+
* 当前 = mcp('auto',命令 / 路径可能机器相关)+ sessions('manual',内容敏感 + 设备相关)。
|
|
43
|
+
* 凭据 / 密钥分区不在此列(结构性拒绝,永不进同步)。
|
|
41
44
|
*/
|
|
42
45
|
export declare const OPT_IN_SYNC_SECTIONS: readonly SectionId[];
|
|
46
|
+
/**
|
|
47
|
+
* 「可选分区」里**允许参与自动同步**的子集(当前 = mcp)。
|
|
48
|
+
*
|
|
49
|
+
* 手动与自动的差别只在这里:sessions 是内容隐私 / 设备相关,自动同步绝不带
|
|
50
|
+
* (「会话绝不悄悄下行」从注释变成可测断言);mcp 只是命令 / 路径可能与目标机不同,
|
|
51
|
+
* 用户显式勾选后手动与自动都带。
|
|
52
|
+
*/
|
|
53
|
+
export declare const AUTO_SYNC_OPT_IN_SECTIONS: readonly SectionId[];
|
|
43
54
|
/** sessions 分区默认同步「最新 N 个会话」(用户可在弹窗里改;0 = 不带)。 */
|
|
44
55
|
export declare const DEFAULT_SYNC_SESSIONS_LIMIT = 5;
|
|
45
56
|
/** sessionsLimit 归一化:非整数 / 缺失 / 负数 → 默认值;超大 → 上限钳制。 */
|