dsh-config-manager 0.1.71 → 0.1.73
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 +149 -98
- package/README.zh-CN.md +245 -88
- package/lib/adapters/agent-instructions.js +1 -1
- package/lib/adapters/file-collection.js +1 -1
- package/lib/adapters/index.d.ts +2 -1
- package/lib/adapters/index.js +6 -1
- package/lib/adapters/link-report.d.ts +1 -0
- package/lib/adapters/link-report.js +27 -3
- package/lib/adapters/messages.d.ts +21 -0
- package/lib/adapters/messages.js +57 -0
- package/lib/adapters/plugin-data.d.ts +114 -0
- package/lib/adapters/plugin-data.js +309 -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 +8 -2
- package/lib/adapters/self.js +3 -2
- package/lib/adapters/sessions.d.ts +1 -0
- package/lib/adapters/sessions.js +4 -2
- package/lib/adapters/test-helpers.d.ts +9 -0
- package/lib/adapters/test-helpers.js +14 -0
- 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 +1442 -494
- package/lib/client.js +15326 -8520
- package/lib/core/analyzer.d.ts +11 -2
- package/lib/core/analyzer.js +31 -8
- package/lib/core/backup-plan.d.ts +10 -0
- package/lib/core/backup-plan.js +51 -20
- package/lib/core/backup-verify.js +24 -23
- package/lib/core/backup.d.ts +25 -1
- package/lib/core/backup.js +99 -28
- package/lib/core/boot-rescue.js +4 -3
- package/lib/core/boot-safety.js +2 -1
- package/lib/core/command-rollback.d.ts +182 -0
- package/lib/core/command-rollback.js +293 -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 +294 -0
- package/lib/core/messages-core.js +619 -0
- package/lib/core/messages.d.ts +61 -2
- package/lib/core/messages.js +118 -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/plugin-data-registry.d.ts +124 -0
- package/lib/core/plugin-data-registry.js +198 -0
- 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-gate.d.ts +33 -0
- package/lib/core/rollback-gate.js +57 -0
- package/lib/core/rollback-ignore-rules.d.ts +31 -0
- package/lib/core/rollback-ignore-rules.js +140 -0
- package/lib/core/rollback-ledger.d.ts +248 -0
- package/lib/core/rollback-ledger.js +1282 -0
- package/lib/core/rollback-point.d.ts +1391 -0
- package/lib/core/rollback-point.js +3503 -0
- package/lib/core/rollback.js +6 -4
- 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 +37 -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.d.ts +1 -1
- package/lib/foreign/registry.js +8 -7
- 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 +227 -7
- package/lib/index.js +799 -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 +43 -0
- package/lib/routes/messages.js +102 -0
- package/lib/routes/prefs.js +26 -3
- package/lib/routes/profiles.js +12 -6
- package/lib/routes/recovery.d.ts +21 -0
- package/lib/routes/recovery.js +478 -94
- package/lib/routes/snapshots.js +51 -4
- package/lib/routes/sync.js +79 -17
- 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 +42 -15
- package/lib/schema/section-registry.js +67 -32
- package/lib/schema/types.d.ts +1 -1
- 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 +58 -22
- package/lib/sync/sync-engine.js +119 -48
- 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/ui-prefs.d.ts +22 -0
- package/lib/sync/ui-prefs.js +28 -0
- 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/diff-view.d.ts +10 -0
- package/lib/ui/diff-view.js +17 -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 +26 -20
- package/lib/ui/foreign-view.d.ts +5 -1
- package/lib/ui/foreign-view.js +26 -2
- package/lib/ui/i18n.d.ts +68 -1
- package/lib/ui/i18n.js +120 -2
- 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 +815 -0
- package/lib/ui/rollback-view.js +738 -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/test-helpers.js +2 -1
- 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 +6 -1
- package/src/adapters/link-report.test.ts +22 -2
- package/src/adapters/link-report.ts +29 -4
- package/src/adapters/messages.ts +61 -0
- package/src/adapters/plugin-data.test.ts +225 -0
- package/src/adapters/plugin-data.ts +351 -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 +10 -3
- package/src/adapters/sessions.ts +4 -2
- package/src/adapters/settings.test.ts +38 -1
- package/src/adapters/test-helpers.ts +15 -0
- 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 +411 -0
- package/src/client/chat/RollbackDiffTab.tsx +108 -0
- package/src/client/chat/RollbackFileIcon.tsx +96 -0
- package/src/client/chat/RollbackReviewDialog.tsx +607 -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 +183 -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 +174 -0
- package/src/client/chat/rollback-entry-view.test.ts +1469 -0
- package/src/client/chat/rollback-entry-view.ts +946 -0
- package/src/client/chat/sidebar-slots.ts +59 -0
- package/src/client/common/ConfirmDialog.tsx +7 -3
- package/src/client/common/ContentPicker.tsx +48 -10
- package/src/client/common/DiffTable.tsx +133 -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 +84 -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 +38 -0
- package/src/client/common/section-labels.test.ts +1 -0
- package/src/client/common/section-labels.ts +2 -0
- package/src/client/common/ui.tsx +73 -10
- package/src/client/config-manager.module.css +1109 -31
- 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 +232 -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 +62 -9
- 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 +859 -0
- package/src/client/recovery/RollbackDiffPane.tsx +69 -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 +144 -6
- package/src/client/recovery/recovery-locales.test.ts +299 -0
- package/src/client/recovery/recovery-locales.ts +520 -149
- package/src/client/recovery/recovery-panel-layout.test.ts +132 -2
- package/src/client/recovery/rollback-enabled.ts +41 -0
- package/src/client/recovery/rollback-file-changes.test.ts +114 -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/analyzer.ts +31 -9
- package/src/core/backup-plan.test.ts +48 -5
- package/src/core/backup-plan.ts +51 -20
- package/src/core/backup-verify.ts +24 -23
- package/src/core/backup.test.ts +76 -0
- package/src/core/backup.ts +105 -29
- 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 +406 -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 +661 -0
- package/src/core/messages.ts +125 -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/plugin-data-registry.test.ts +113 -0
- package/src/core/plugin-data-registry.ts +261 -0
- 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-gate.test.ts +66 -0
- package/src/core/rollback-gate.ts +61 -0
- package/src/core/rollback-ignore-rules.ts +126 -0
- package/src/core/rollback-ledger.test.ts +655 -0
- package/src/core/rollback-ledger.ts +1314 -0
- package/src/core/rollback-point.test.ts +2853 -0
- package/src/core/rollback-point.ts +4340 -0
- package/src/core/rollback.ts +5 -3
- 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 +37 -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.test.ts +1 -1
- package/src/foreign/registry.ts +8 -7
- 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 +876 -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 +106 -0
- package/src/routes/prefs.ts +31 -2
- package/src/routes/profiles.ts +12 -6
- package/src/routes/recovery.test.ts +405 -1
- package/src/routes/recovery.ts +477 -94
- package/src/routes/snapshots.ts +51 -4
- package/src/routes/sync.ts +82 -17
- package/src/schema/config.ts +53 -20
- package/src/schema/index.ts +7 -5
- package/src/schema/manifest-schema.test.ts +4 -4
- package/src/schema/manifest.ts +19 -18
- package/src/schema/messages.ts +120 -0
- package/src/schema/registry.test.ts +28 -7
- package/src/schema/section-registry.ts +96 -42
- package/src/schema/types.ts +2 -1
- 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-credentials.test.ts +119 -0
- 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 +154 -52
- 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/transport.test.ts +1 -0
- package/src/sync/ui-prefs.test.ts +44 -1
- package/src/sync/ui-prefs.ts +39 -0
- 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/diff-view.test.ts +9 -1
- package/src/ui/diff-view.ts +22 -0
- 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 +26 -20
- package/src/ui/foreign-view.test.ts +43 -0
- package/src/ui/foreign-view.ts +30 -3
- package/src/ui/i18n.ts +131 -2
- 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 +836 -0
- package/src/ui/rollback-view.ts +1405 -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/test-helpers.ts +2 -1
- 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
package/lib/core/analyzer.d.ts
CHANGED
|
@@ -171,11 +171,20 @@ export declare class Analyzer {
|
|
|
171
171
|
* 未采用其导入内容的 Conflict 都不写目标,记录只会保守化 reconcile 而无收益。
|
|
172
172
|
*/
|
|
173
173
|
private shouldJournalStep;
|
|
174
|
-
/** 文件类且可指纹 →
|
|
174
|
+
/** 文件类且可指纹 → 返回「根相对路径」(journal 的 ref 身份);否则 null(不可指纹外部项)。
|
|
175
175
|
* profile 必须传入:plugins 分区的 pnpm-workspace.yaml / patch 文件位于
|
|
176
176
|
* `profiles/<profile>/` 下,缺了它算出的相对路径不存在 → 指纹恒 null(issue #35)。 */
|
|
177
177
|
private static fileRelFor;
|
|
178
|
-
/**
|
|
178
|
+
/**
|
|
179
|
+
* 文件类且可指纹 → 返回「该分区自己的门面 + 绝对路径」。
|
|
180
|
+
*
|
|
181
|
+
* 为什么不能只拿 home-relative 路径去读:`pluginData` / `pluginAssets` 的根是**系统 home**
|
|
182
|
+
* (见 `core/backup.ts` 的 `fsForSection`),用 `ctx.fs` 读会落到 $DSH_HOME 下的错误位置
|
|
183
|
+
* 或被越界守卫拦下 —— 指纹会静默变成「读不到」,reconcile 因此永远判不出是否已应用。
|
|
184
|
+
* 宿主未提供该门面 / 路径不可解析 → null(保守:不可证明)。
|
|
185
|
+
*/
|
|
186
|
+
private static fileTargetFor;
|
|
187
|
+
/** 读目标文件算 sha256(经该分区自己的门面,测试 mock 可注入)。失败返回 null。 */
|
|
179
188
|
private fileFp;
|
|
180
189
|
private applyOne;
|
|
181
190
|
}
|
package/lib/core/analyzer.js
CHANGED
|
@@ -19,7 +19,7 @@ import { isAbsolutePath, applyPrefixMappings } from '../utils/paths.js';
|
|
|
19
19
|
import { parseZip } from '../utils/zip.js';
|
|
20
20
|
import { loadTombstones, isTombstoned } from '../schema/tombstones.js';
|
|
21
21
|
import { DEFAULT_SENSITIVE_RELS, restoreVaultFiles } from '../security/vault.js';
|
|
22
|
-
import { createSnapshot, planItemWritesTarget, resolveFileTarget, resolveFileTargetRel } from './backup.js';
|
|
22
|
+
import { createSnapshot, fsForSection, planItemWritesTarget, resolveFileTarget, resolveFileTargetRel } from './backup.js';
|
|
23
23
|
import { isCredentialConfigured } from './credential-status.js';
|
|
24
24
|
import { rollback } from './rollback.js';
|
|
25
25
|
import { compatibilityReasons, computeCompatibility } from './validator.js';
|
|
@@ -790,7 +790,7 @@ export class Analyzer {
|
|
|
790
790
|
let beforeFp = null;
|
|
791
791
|
if (Analyzer.fileRelFor(item, importCtx.target.profile) !== null) {
|
|
792
792
|
rel = Analyzer.fileRelFor(item, importCtx.target.profile);
|
|
793
|
-
beforeFp = await this.fileFp(importCtx,
|
|
793
|
+
beforeFp = await this.fileFp(importCtx, item);
|
|
794
794
|
}
|
|
795
795
|
beforeFpByItem.set(item.id, beforeFp);
|
|
796
796
|
await sb.recordStep({
|
|
@@ -896,7 +896,7 @@ export class Analyzer {
|
|
|
896
896
|
const rel = Analyzer.fileRelFor(item, importCtx.target.profile);
|
|
897
897
|
let afterFp = null;
|
|
898
898
|
if (rel !== null && outcome.executed.status === 'ok') {
|
|
899
|
-
afterFp = await this.fileFp(importCtx,
|
|
899
|
+
afterFp = await this.fileFp(importCtx, item);
|
|
900
900
|
}
|
|
901
901
|
await sb.recordStep({
|
|
902
902
|
id: item.id,
|
|
@@ -1227,7 +1227,7 @@ export class Analyzer {
|
|
|
1227
1227
|
shouldJournalStep(item) {
|
|
1228
1228
|
return planItemWritesTarget(item);
|
|
1229
1229
|
}
|
|
1230
|
-
/** 文件类且可指纹 →
|
|
1230
|
+
/** 文件类且可指纹 → 返回「根相对路径」(journal 的 ref 身份);否则 null(不可指纹外部项)。
|
|
1231
1231
|
* profile 必须传入:plugins 分区的 pnpm-workspace.yaml / patch 文件位于
|
|
1232
1232
|
* `profiles/<profile>/` 下,缺了它算出的相对路径不存在 → 指纹恒 null(issue #35)。 */
|
|
1233
1233
|
static fileRelFor(item, profile) {
|
|
@@ -1238,11 +1238,34 @@ export class Analyzer {
|
|
|
1238
1238
|
return null;
|
|
1239
1239
|
return resolveFileTargetRel(item.adapter, ref, profile);
|
|
1240
1240
|
}
|
|
1241
|
-
/**
|
|
1242
|
-
|
|
1241
|
+
/**
|
|
1242
|
+
* 文件类且可指纹 → 返回「该分区自己的门面 + 绝对路径」。
|
|
1243
|
+
*
|
|
1244
|
+
* 为什么不能只拿 home-relative 路径去读:`pluginData` / `pluginAssets` 的根是**系统 home**
|
|
1245
|
+
* (见 `core/backup.ts` 的 `fsForSection`),用 `ctx.fs` 读会落到 $DSH_HOME 下的错误位置
|
|
1246
|
+
* 或被越界守卫拦下 —— 指纹会静默变成「读不到」,reconcile 因此永远判不出是否已应用。
|
|
1247
|
+
* 宿主未提供该门面 / 路径不可解析 → null(保守:不可证明)。
|
|
1248
|
+
*/
|
|
1249
|
+
static fileTargetFor(ctx, item) {
|
|
1250
|
+
if (!isFileSection(item.adapter))
|
|
1251
|
+
return null;
|
|
1252
|
+
const ref = item.target?.ref;
|
|
1253
|
+
if (ref === undefined || ref === '')
|
|
1254
|
+
return null;
|
|
1255
|
+
try {
|
|
1256
|
+
return { fs: fsForSection(ctx.target, item.adapter), abs: resolveFileTarget(ctx.target, item.adapter, ref) };
|
|
1257
|
+
}
|
|
1258
|
+
catch {
|
|
1259
|
+
return null;
|
|
1260
|
+
}
|
|
1261
|
+
}
|
|
1262
|
+
/** 读目标文件算 sha256(经该分区自己的门面,测试 mock 可注入)。失败返回 null。 */
|
|
1263
|
+
async fileFp(ctx, item) {
|
|
1264
|
+
const target = Analyzer.fileTargetFor(ctx, item);
|
|
1265
|
+
if (target === null)
|
|
1266
|
+
return null;
|
|
1243
1267
|
try {
|
|
1244
|
-
|
|
1245
|
-
return sha256Hex(data);
|
|
1268
|
+
return sha256Hex(await target.fs.readFile(target.abs));
|
|
1246
1269
|
}
|
|
1247
1270
|
catch {
|
|
1248
1271
|
return null;
|
|
@@ -11,6 +11,16 @@ export declare const DEFAULT_BACKUP_SECTIONS: readonly SectionId[];
|
|
|
11
11
|
export declare const OPT_IN_BACKUP_SECTIONS: readonly SectionId[];
|
|
12
12
|
/** 全部可离线收集的分区(默认 ∪ 显式选中) */
|
|
13
13
|
export declare const OFFLINE_BACKUP_SECTIONS: readonly SectionId[];
|
|
14
|
+
/**
|
|
15
|
+
* 离线 CLI **明确不收集**的分区(第三方插件数据)。
|
|
16
|
+
*
|
|
17
|
+
* 为什么不做:它的收集目标不是静态路径,而是「本机内置来源注册表 + 已安装插件自述声明」推导出来的
|
|
18
|
+
* 区域(见 `core/plugin-data-registry.ts`),还要尊重环境变量覆盖判定 —— 这套判定住在 adapter 里
|
|
19
|
+
* (走 HostContext 门面),而 CLI 的硬约束是**绝不 import DSH 门面**(peer 缺失也要能跑)。
|
|
20
|
+
* 在这里手抄一份区域表会立刻与注册表漂移(正是本文件开头反复说的那类缺陷),所以选择**显式不适用**:
|
|
21
|
+
* 用户显式 `--sections pluginData` 时给一条可执行的提示(改用 GUI 导出/导入),而不是静默不收。
|
|
22
|
+
*/
|
|
23
|
+
export declare const OFFLINE_EXCLUDED_SECTIONS: readonly SectionId[];
|
|
14
24
|
/**
|
|
15
25
|
* 离线**无法**收集的分区(需要 DSH Service 门面读值/权威脱敏)。仅用于报告与参数校验,
|
|
16
26
|
* 绝不写进归档:这些分区的权威形态是 GUI 导出(经 DSH `describe({redactSecrets:true})`
|
package/lib/core/backup-plan.js
CHANGED
|
@@ -32,6 +32,8 @@
|
|
|
32
32
|
* 目标越出 homeDir 或断链的链接跳过但**写进 warnings**——绝不静默少收内容;
|
|
33
33
|
* 普通目录**超过层级上限**同样留痕(cli-F2),且告警措辞按**真实来源**分派(t37:链接类 vs 目录超深);
|
|
34
34
|
* - 命中 `isReservedInternalRel` 的内部命名空间(locks/snapshots/transactions…)跳过;
|
|
35
|
+
* - 运行时依赖目录 node_modules(任意深度)**整棵跳过**(issue #76:与 plugins 分区的 npm pack 同口径,
|
|
36
|
+
* 依赖树可由包内 package.json / lock 重建),跳过了哪些目录、各多少文件一律写进 warnings;
|
|
35
37
|
* - `pluginFiles`(`dsh-ssh.json` 等第三方插件自有文件)**默认不收**:该分区与 GUI
|
|
36
38
|
* 侧同为 deviceSpecific「任意文件直通、无内容过滤」,`dsh-ssh.json` 里是明文主机
|
|
37
39
|
* 密码——必须由用户显式 `--sections pluginFiles` 选中,并附带显式告警
|
|
@@ -39,6 +41,7 @@
|
|
|
39
41
|
*
|
|
40
42
|
* 零 DSH 运行时依赖:仅 `node:fs/promises` + `node:path` + 本仓库零依赖模块。
|
|
41
43
|
*/
|
|
44
|
+
import { engineMsg } from './messages-core.js';
|
|
42
45
|
import fs from 'node:fs/promises';
|
|
43
46
|
import path from 'node:path';
|
|
44
47
|
import { listRecursiveFollowingLinks } from '../utils/recursive-walk.js';
|
|
@@ -60,6 +63,16 @@ export const OPT_IN_BACKUP_SECTIONS = ['pluginFiles'];
|
|
|
60
63
|
export const OFFLINE_BACKUP_SECTIONS = [
|
|
61
64
|
...DEFAULT_BACKUP_SECTIONS, ...OPT_IN_BACKUP_SECTIONS,
|
|
62
65
|
];
|
|
66
|
+
/**
|
|
67
|
+
* 离线 CLI **明确不收集**的分区(第三方插件数据)。
|
|
68
|
+
*
|
|
69
|
+
* 为什么不做:它的收集目标不是静态路径,而是「本机内置来源注册表 + 已安装插件自述声明」推导出来的
|
|
70
|
+
* 区域(见 `core/plugin-data-registry.ts`),还要尊重环境变量覆盖判定 —— 这套判定住在 adapter 里
|
|
71
|
+
* (走 HostContext 门面),而 CLI 的硬约束是**绝不 import DSH 门面**(peer 缺失也要能跑)。
|
|
72
|
+
* 在这里手抄一份区域表会立刻与注册表漂移(正是本文件开头反复说的那类缺陷),所以选择**显式不适用**:
|
|
73
|
+
* 用户显式 `--sections pluginData` 时给一条可执行的提示(改用 GUI 导出/导入),而不是静默不收。
|
|
74
|
+
*/
|
|
75
|
+
export const OFFLINE_EXCLUDED_SECTIONS = ['pluginData', 'pluginAssets'];
|
|
63
76
|
/**
|
|
64
77
|
* 离线**无法**收集的分区(需要 DSH Service 门面读值/权威脱敏)。仅用于报告与参数校验,
|
|
65
78
|
* 绝不写进归档:这些分区的权威形态是 GUI 导出(经 DSH `describe({redactSecrets:true})`
|
|
@@ -71,7 +84,10 @@ export const OFFLINE_UNAVAILABLE_SECTIONS = [
|
|
|
71
84
|
// 加载期自检(注册表派生,t30):三个离线清单里的 id 必须都是**已注册分区**。
|
|
72
85
|
// 「离线可收集性」是注册表未建模的另一条轴(要脱离 DSH 读盘收集),故这三份清单仍是显式声明;
|
|
73
86
|
// 但拼错 id / 分区改名过去会让该分区「静默永不收集、永不报告」,现在模块加载即失败。
|
|
74
|
-
for (const id of [
|
|
87
|
+
for (const id of [
|
|
88
|
+
...DEFAULT_BACKUP_SECTIONS, ...OPT_IN_BACKUP_SECTIONS, ...OFFLINE_UNAVAILABLE_SECTIONS,
|
|
89
|
+
...OFFLINE_EXCLUDED_SECTIONS,
|
|
90
|
+
]) {
|
|
75
91
|
requireSectionMeta(id);
|
|
76
92
|
}
|
|
77
93
|
/**
|
|
@@ -109,8 +125,8 @@ const SECTION_SPECS = {
|
|
|
109
125
|
baseDir: '',
|
|
110
126
|
files: ['dsh-ssh.json', 'pet.json'],
|
|
111
127
|
dirs: ['plugin-config'],
|
|
112
|
-
label: '
|
|
113
|
-
risk
|
|
128
|
+
label: "Other Plugins' Loose Files",
|
|
129
|
+
get risk() { return engineMsg('core.backupPlan.pluginFilesRisk'); },
|
|
114
130
|
},
|
|
115
131
|
// adapters/self.ts(baseDir='dsh-config-manager',SELF_CONFIG_FILES 白名单)
|
|
116
132
|
self: {
|
|
@@ -124,7 +140,7 @@ const SECTION_SPECS = {
|
|
|
124
140
|
'dsh-config-manager/market/market-config.json',
|
|
125
141
|
'dsh-config-manager/exports/.backup-notes.json',
|
|
126
142
|
],
|
|
127
|
-
label: '
|
|
143
|
+
label: "This Plugin's Own Config",
|
|
128
144
|
},
|
|
129
145
|
};
|
|
130
146
|
/**
|
|
@@ -134,13 +150,16 @@ const SECTION_SPECS = {
|
|
|
134
150
|
* 跳过原因;而 `too-deep` 自 cli-F2 起对**普通目录**同样会记录(此前只有链接超深才记)—— 整块内容被
|
|
135
151
|
* 深度上限裁掉时若仍写成「链接未进备份」,用户看到的原因就与真实来源(目录层级超出上限)不符。
|
|
136
152
|
* 措辞里**不编具体层数**:上限常量住在 utils/recursive-walk.ts,本模块不 import 它的私有常量。
|
|
153
|
+
*
|
|
154
|
+
* 路径**不叠加分区前缀**:内核返回的 `s.path` 已是 homeDir 相对路径(含分区目录),再加一层
|
|
155
|
+
* 会打印成 `skills/skills/...`(真机规模探针实测),与相邻的「目录读取失败」行口径也不一致。
|
|
137
156
|
*/
|
|
138
|
-
function skippedLinkWarning(
|
|
139
|
-
const at =
|
|
157
|
+
function skippedLinkWarning(s) {
|
|
158
|
+
const at = engineMsg('core.backupPlan.pathWithReason', { path: s.path, reason: s.reason });
|
|
140
159
|
if (s.reason === 'too-deep') {
|
|
141
|
-
return
|
|
160
|
+
return engineMsg('core.backupPlan.tooDeep', { at });
|
|
142
161
|
}
|
|
143
|
-
return
|
|
162
|
+
return engineMsg('core.backupPlan.linkSkipped', { at });
|
|
144
163
|
}
|
|
145
164
|
/**
|
|
146
165
|
* 递归列出目录下全部普通文件(**相对 homeDir** 的 posix 路径,已排序);目录不存在 → 空。
|
|
@@ -149,16 +168,23 @@ function skippedLinkWarning(label, s) {
|
|
|
149
168
|
* 并把「跟随了多少链接」「哪些链接/目录没进来(及原因)」写进 warnings。
|
|
150
169
|
* 旧实现遇到链接目录直接跳过且零告警,用户拿到的是「成功但缺斤少两」的备份。
|
|
151
170
|
*/
|
|
152
|
-
async function listFilesRecursive(dirAbs, homeDir, warnings
|
|
171
|
+
async function listFilesRecursive(dirAbs, homeDir, warnings) {
|
|
153
172
|
const listing = await listRecursiveFollowingLinks(dirAbs, homeDir);
|
|
154
173
|
for (const s of listing.skippedLinks) {
|
|
155
|
-
warnings.push(skippedLinkWarning(
|
|
174
|
+
warnings.push(skippedLinkWarning(s));
|
|
156
175
|
}
|
|
157
176
|
for (const dir of listing.unreadableDirs) {
|
|
158
|
-
warnings.push(
|
|
177
|
+
warnings.push(engineMsg('core.backupPlan.dirUnreadable', { dir }));
|
|
178
|
+
}
|
|
179
|
+
// issue #76:运行时依赖目录整棵跳过(内容未进备份)——必须可见,绝不静默。
|
|
180
|
+
for (const dep of listing.skippedDepDirs) {
|
|
181
|
+
const files = dep.files === null
|
|
182
|
+
? engineMsg('core.backupPlan.countNotMeasured')
|
|
183
|
+
: engineMsg('core.backupPlan.fileCount', { count: dep.files });
|
|
184
|
+
warnings.push(engineMsg('core.backupPlan.depDirSkipped', { path: dep.path, files }));
|
|
159
185
|
}
|
|
160
186
|
if (listing.followedLinks > 0) {
|
|
161
|
-
warnings.push(
|
|
187
|
+
warnings.push(engineMsg('core.backupPlan.followedLinks', { count: listing.followedLinks }));
|
|
162
188
|
}
|
|
163
189
|
return listing.paths;
|
|
164
190
|
}
|
|
@@ -171,15 +197,20 @@ function toRelativePath(baseDir, homeRel) {
|
|
|
171
197
|
}
|
|
172
198
|
/** `--sections` 取值校验:返回选中分区或可读错误(未知 id 一律拒绝并列出合法值) */
|
|
173
199
|
export function parseSectionsArg(raw) {
|
|
174
|
-
const parts = raw.split(/[
|
|
200
|
+
const parts = raw.split(/[,\uFF0C\s]+/).filter((p) => p !== '');
|
|
175
201
|
if (parts.length === 0) {
|
|
176
|
-
return { ok: false, error: '
|
|
202
|
+
return { ok: false, error: engineMsg('core.backupPlan.sectionsMissingValue') };
|
|
203
|
+
}
|
|
204
|
+
// 显式请求「不支持离线」的分区 → 单独一条可执行提示(不得当成「不存在的分区」)
|
|
205
|
+
const notOffline = parts.filter((p) => OFFLINE_EXCLUDED_SECTIONS.includes(p));
|
|
206
|
+
if (notOffline.length > 0) {
|
|
207
|
+
return { ok: false, error: engineMsg('core.backupPlan.sectionNotOffline', { section: notOffline.join(', ') }) };
|
|
177
208
|
}
|
|
178
209
|
const unknown = parts.filter((p) => !OFFLINE_BACKUP_SECTIONS.includes(p));
|
|
179
210
|
if (unknown.length > 0) {
|
|
180
211
|
return {
|
|
181
212
|
ok: false,
|
|
182
|
-
error:
|
|
213
|
+
error: engineMsg('core.backupPlan.unknownSections', { unknown: unknown.join(', '), available: OFFLINE_BACKUP_SECTIONS.join(', ') }),
|
|
183
214
|
};
|
|
184
215
|
}
|
|
185
216
|
// 去重并保持声明顺序,保证产物可复现
|
|
@@ -207,7 +238,7 @@ export async function collectBackupEntries(homeDir, only = []) {
|
|
|
207
238
|
let excluded = 0;
|
|
208
239
|
const push = (zipName, data) => {
|
|
209
240
|
if (!isPathSafe(zipName)) {
|
|
210
|
-
warnings.push(
|
|
241
|
+
warnings.push(engineMsg('core.backupPlan.unsafeEntryName', { name: zipName }));
|
|
211
242
|
excluded += 1;
|
|
212
243
|
return;
|
|
213
244
|
}
|
|
@@ -229,7 +260,7 @@ export async function collectBackupEntries(homeDir, only = []) {
|
|
|
229
260
|
catch (err) {
|
|
230
261
|
// 不存在 → 静默跳过(按需创建的文件,如 dsh-ssh.json)
|
|
231
262
|
if (err.code !== 'ENOENT') {
|
|
232
|
-
warnings.push(
|
|
263
|
+
warnings.push(engineMsg('core.backupPlan.statFailed', { path: norm, reason: err instanceof Error ? err.message : String(err) }));
|
|
233
264
|
}
|
|
234
265
|
return;
|
|
235
266
|
}
|
|
@@ -242,7 +273,7 @@ export async function collectBackupEntries(homeDir, only = []) {
|
|
|
242
273
|
push(`${prefix}${toRelativePath(spec.baseDir, norm)}`, new Uint8Array(data));
|
|
243
274
|
}
|
|
244
275
|
catch (err) {
|
|
245
|
-
warnings.push(
|
|
276
|
+
warnings.push(engineMsg('core.backupPlan.readFailed', { path: norm, reason: err instanceof Error ? err.message : String(err) }));
|
|
246
277
|
}
|
|
247
278
|
};
|
|
248
279
|
// 1) 固定文件(存在才收)
|
|
@@ -251,7 +282,7 @@ export async function collectBackupEntries(homeDir, only = []) {
|
|
|
251
282
|
// 2) 递归目录(跟随 junction/符号链接;未跟随的链接与不可读目录写进 warnings)
|
|
252
283
|
for (const dirRel of spec.dirs ?? []) {
|
|
253
284
|
const dirAbs = path.join(homeDir, ...normalizePath(dirRel).split('/'));
|
|
254
|
-
const rels = await listFilesRecursive(dirAbs, homeDir, warnings
|
|
285
|
+
const rels = await listFilesRecursive(dirAbs, homeDir, warnings);
|
|
255
286
|
for (const homeRel of rels)
|
|
256
287
|
await consider(homeRel);
|
|
257
288
|
}
|
|
@@ -264,7 +295,7 @@ export async function collectBackupEntries(homeDir, only = []) {
|
|
|
264
295
|
}
|
|
265
296
|
else {
|
|
266
297
|
empty.push(sectionId);
|
|
267
|
-
warnings.push(
|
|
298
|
+
warnings.push(engineMsg('core.backupPlan.sectionEmpty', { section: sectionId }));
|
|
268
299
|
}
|
|
269
300
|
}
|
|
270
301
|
return { entries, sections, warnings, included, empty };
|
|
@@ -28,6 +28,7 @@
|
|
|
28
28
|
* (实测 `verifyChecksums` 只调用 `map.get()`,不会整体物化 Map)。
|
|
29
29
|
* 对 MB 级的 DSH 配置备份完全可接受。
|
|
30
30
|
*/
|
|
31
|
+
import { engineMsg } from './messages-core.js';
|
|
31
32
|
import fs from 'node:fs/promises';
|
|
32
33
|
import { verifyChecksums } from '../utils/hashing.js';
|
|
33
34
|
import { isENOENT } from '../utils/guards.js';
|
|
@@ -114,7 +115,7 @@ export async function verifyBackupZip(zipPath) {
|
|
|
114
115
|
try {
|
|
115
116
|
const stat = await fs.stat(zipPath);
|
|
116
117
|
if (!stat.isFile()) {
|
|
117
|
-
errors.push(
|
|
118
|
+
errors.push(engineMsg('core.backupVerify.notRegularFile', { zip: zipPath }));
|
|
118
119
|
return makeResult(zipPath, 'MISSING', errors, warnings);
|
|
119
120
|
}
|
|
120
121
|
sizeBytes = stat.size;
|
|
@@ -122,11 +123,11 @@ export async function verifyBackupZip(zipPath) {
|
|
|
122
123
|
}
|
|
123
124
|
catch (err) {
|
|
124
125
|
if (isENOENT(err)) {
|
|
125
|
-
errors.push(
|
|
126
|
+
errors.push(engineMsg('core.backupVerify.fileNotFound', { zip: zipPath }));
|
|
126
127
|
return makeResult(zipPath, 'MISSING', errors, warnings);
|
|
127
128
|
}
|
|
128
129
|
// 文件在但读不动(EACCES / EBUSY / IO 错):这是自检自身的失败,不是「备份坏了」
|
|
129
|
-
errors.push(
|
|
130
|
+
errors.push(engineMsg('core.backupVerify.fileUnreadable', { reason: msgOfError(err) }));
|
|
130
131
|
return makeResult(zipPath, 'VERIFY_ERROR', errors, warnings);
|
|
131
132
|
}
|
|
132
133
|
/** 已知字节数后的骨架(后续阶段失败时保留体积信息) */
|
|
@@ -137,7 +138,7 @@ export async function verifyBackupZip(zipPath) {
|
|
|
137
138
|
/* ---------------- 2. 打开为 ZIP ---------------- */
|
|
138
139
|
// 整体加密容器不是 ZIP(先识别,给出比「ZIP 损坏」准确得多的结论)
|
|
139
140
|
if (isArchiveBlob(buf)) {
|
|
140
|
-
errors.push('
|
|
141
|
+
errors.push(engineMsg('core.backupVerify.encryptedContainer'));
|
|
141
142
|
return withSize('UNSUPPORTED');
|
|
142
143
|
}
|
|
143
144
|
let archive;
|
|
@@ -145,7 +146,7 @@ export async function verifyBackupZip(zipPath) {
|
|
|
145
146
|
archive = createHardenedZipParser()(buf);
|
|
146
147
|
}
|
|
147
148
|
catch (err) {
|
|
148
|
-
errors.push(
|
|
149
|
+
errors.push(engineMsg('core.backupVerify.cannotOpenZip', { reason: msgOfError(err) }));
|
|
149
150
|
return withSize('CORRUPT');
|
|
150
151
|
}
|
|
151
152
|
const metas = archive.entries();
|
|
@@ -154,12 +155,12 @@ export async function verifyBackupZip(zipPath) {
|
|
|
154
155
|
file: zipPath, verdict, sizeBytes, entryCount, errors, warnings,
|
|
155
156
|
});
|
|
156
157
|
if (entryCount === 0) {
|
|
157
|
-
errors.push('
|
|
158
|
+
errors.push(engineMsg('core.backupVerify.emptyArchive'));
|
|
158
159
|
return withEntries('CORRUPT');
|
|
159
160
|
}
|
|
160
161
|
/* ---------------- 3. manifest.json ---------------- */
|
|
161
162
|
if (!archive.has(MANIFEST_FILE)) {
|
|
162
|
-
errors.push(
|
|
163
|
+
errors.push(engineMsg('core.backupVerify.missingManifest', { file: MANIFEST_FILE }));
|
|
163
164
|
return withEntries('CORRUPT');
|
|
164
165
|
}
|
|
165
166
|
let manifestRaw;
|
|
@@ -168,7 +169,7 @@ export async function verifyBackupZip(zipPath) {
|
|
|
168
169
|
}
|
|
169
170
|
catch (err) {
|
|
170
171
|
// 条目本身读不出来(CRC 失败/解压失败)→ 归档损坏,而非 manifest 语义非法
|
|
171
|
-
errors.push(
|
|
172
|
+
errors.push(engineMsg('core.backupVerify.manifestUnreadable', { file: MANIFEST_FILE, reason: msgOfError(err) }));
|
|
172
173
|
return withEntries('CORRUPT');
|
|
173
174
|
}
|
|
174
175
|
let manifest;
|
|
@@ -176,7 +177,7 @@ export async function verifyBackupZip(zipPath) {
|
|
|
176
177
|
manifest = parseManifest(manifestRaw);
|
|
177
178
|
}
|
|
178
179
|
catch (err) {
|
|
179
|
-
errors.push(
|
|
180
|
+
errors.push(engineMsg('core.backupVerify.invalidManifest', { file: MANIFEST_FILE, reason: msgOfError(err) }));
|
|
180
181
|
return withEntries('CORRUPT');
|
|
181
182
|
}
|
|
182
183
|
const sections = Object.entries(manifest.sections ?? {})
|
|
@@ -187,12 +188,12 @@ export async function verifyBackupZip(zipPath) {
|
|
|
187
188
|
});
|
|
188
189
|
if (!isSupported(manifest.schemaVersion)) {
|
|
189
190
|
// 版本超范围是「本插件读不了」,不是「备份坏了」——明确区分,避免误导用户重导
|
|
190
|
-
errors.push(
|
|
191
|
+
errors.push(engineMsg('core.backupVerify.unsupportedSchema', { version: describeVersion(manifest.schemaVersion) }));
|
|
191
192
|
return withManifest('UNSUPPORTED');
|
|
192
193
|
}
|
|
193
194
|
/* ---------------- 4. integrity/checksums.json ---------------- */
|
|
194
195
|
if (!archive.has(CHECKSUMS_FILE)) {
|
|
195
|
-
errors.push(
|
|
196
|
+
errors.push(engineMsg('core.backupVerify.missingChecksums', { file: CHECKSUMS_FILE }));
|
|
196
197
|
return withManifest('CORRUPT');
|
|
197
198
|
}
|
|
198
199
|
let table;
|
|
@@ -201,7 +202,7 @@ export async function verifyBackupZip(zipPath) {
|
|
|
201
202
|
}
|
|
202
203
|
catch (err) {
|
|
203
204
|
// readEntryText 的 CRC/解压失败与「表结构非法」都归 CORRUPT(两者都说明备份不可信)
|
|
204
|
-
errors.push(
|
|
205
|
+
errors.push(engineMsg('core.backupVerify.invalidChecksums', { file: CHECKSUMS_FILE, reason: msgOfError(err) }));
|
|
205
206
|
return withManifest('CORRUPT');
|
|
206
207
|
}
|
|
207
208
|
// 4a. 完整性比对:**复用官方 verifyChecksums**(与导出侧 buildChecksums 同源同口径),
|
|
@@ -209,7 +210,7 @@ export async function verifyBackupZip(zipPath) {
|
|
|
209
210
|
const unreadableNames = new Set();
|
|
210
211
|
const unreadable = [];
|
|
211
212
|
const lazyEntries = new LazyEntryMap(archive, unreadableNames, (name, reason) => {
|
|
212
|
-
unreadable.push(
|
|
213
|
+
unreadable.push(engineMsg('core.backupVerify.unreadableEntry', { name, reason }));
|
|
213
214
|
});
|
|
214
215
|
const tableKeys = Object.keys(table);
|
|
215
216
|
const check = verifyChecksums(lazyEntries, table);
|
|
@@ -219,10 +220,10 @@ export async function verifyBackupZip(zipPath) {
|
|
|
219
220
|
// 4b. 结构提示:未纳入校验表的条目(导出器只把 manifest 与校验表自身排除在外)
|
|
220
221
|
if (tableKeys.length === 0) {
|
|
221
222
|
if (entryCount > NON_DATA_ENTRIES.size) {
|
|
222
|
-
errors.push('
|
|
223
|
+
errors.push(engineMsg('core.backupVerify.emptyTableWithData'));
|
|
223
224
|
return withManifest('CORRUPT');
|
|
224
225
|
}
|
|
225
|
-
warnings.push('
|
|
226
|
+
warnings.push(engineMsg('core.backupVerify.emptyTable'));
|
|
226
227
|
}
|
|
227
228
|
const covered = new Set(tableKeys);
|
|
228
229
|
const uncovered = metas
|
|
@@ -230,7 +231,7 @@ export async function verifyBackupZip(zipPath) {
|
|
|
230
231
|
.map((m) => m.name);
|
|
231
232
|
if (uncovered.length > 0) {
|
|
232
233
|
const shown = uncovered.slice(0, 5).join(', ');
|
|
233
|
-
warnings.push(
|
|
234
|
+
warnings.push(engineMsg('core.backupVerify.uncoveredEntries', { count: uncovered.length, shown, suffix: uncovered.length > 5 ? ', …' : '' }));
|
|
234
235
|
}
|
|
235
236
|
// 4c. 结构提示:manifest 声明包含的分区在归档内应有对应条目
|
|
236
237
|
for (const id of sections) {
|
|
@@ -238,31 +239,31 @@ export async function verifyBackupZip(zipPath) {
|
|
|
238
239
|
const jsonPath = SECTION_JSON_PATHS[id];
|
|
239
240
|
if (prefix !== undefined) {
|
|
240
241
|
if (!metas.some((m) => m.name.startsWith(prefix) && m.name !== prefix)) {
|
|
241
|
-
warnings.push(
|
|
242
|
+
warnings.push(engineMsg('core.backupVerify.declaredSectionNoEntries', { id }));
|
|
242
243
|
}
|
|
243
244
|
}
|
|
244
245
|
else if (jsonPath !== undefined && !archive.has(jsonPath)) {
|
|
245
|
-
warnings.push(
|
|
246
|
+
warnings.push(engineMsg('core.backupVerify.declaredSectionPayloadMissing', { id, path: jsonPath }));
|
|
246
247
|
}
|
|
247
248
|
}
|
|
248
249
|
if (manifest.security?.encrypted === true && !metas.some((m) => SECRETS_ENC_RE.test(m.name))) {
|
|
249
|
-
warnings.push('
|
|
250
|
+
warnings.push(engineMsg('core.backupVerify.encryptedFlagNoSecrets'));
|
|
250
251
|
}
|
|
251
252
|
/* ---------------- 裁决 ---------------- */
|
|
252
253
|
if (missing.length > 0) {
|
|
253
|
-
errors.push(
|
|
254
|
+
errors.push(engineMsg('core.backupVerify.missingEntries', { count: missing.length, list: missing.join(', ') }));
|
|
254
255
|
}
|
|
255
256
|
if (mismatched.length > 0) {
|
|
256
|
-
errors.push(
|
|
257
|
+
errors.push(engineMsg('core.backupVerify.hashMismatch', { count: mismatched.length, list: mismatched.join(', ') }));
|
|
257
258
|
}
|
|
258
259
|
if (unreadable.length > 0) {
|
|
259
|
-
errors.push(
|
|
260
|
+
errors.push(engineMsg('core.backupVerify.unreadableEntries', { count: unreadable.length, list: unreadable.join(', ') }));
|
|
260
261
|
}
|
|
261
262
|
return withManifest(missing.length > 0 || mismatched.length > 0 || unreadable.length > 0 ? 'CORRUPT' : 'OK');
|
|
262
263
|
}
|
|
263
264
|
catch (err) {
|
|
264
265
|
// 5. 自检自身异常:如实报 VERIFY_ERROR,绝不降级成 OK
|
|
265
|
-
errors.push(
|
|
266
|
+
errors.push(engineMsg('core.backupVerify.verificationError', { reason: msgOfError(err) }));
|
|
266
267
|
return withSize('VERIFY_ERROR');
|
|
267
268
|
}
|
|
268
269
|
}
|
package/lib/core/backup.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { SectionId } from '../schema/types.ts';
|
|
2
|
-
import type { ConfigAdapter, HostContext, ImportPlan, PlanItem, Snapshot, SnapshotStatus, SnapshotStore } from './types.ts';
|
|
2
|
+
import type { ConfigAdapter, FileSystemFacade, HostContext, ImportPlan, PlanItem, Snapshot, SnapshotStatus, SnapshotStore } from './types.ts';
|
|
3
3
|
/**
|
|
4
4
|
* 「会写目标」的**单点事实源**:该计划项是否会被引擎交给 `adapter.applyItem`
|
|
5
5
|
* (即可能产生目标写入)。
|
|
@@ -38,6 +38,25 @@ export declare function planItemWritesTarget(item: PlanItem, exec?: PlanItemExec
|
|
|
38
38
|
* 行 id 是任意字符串,无法靠形状判断它是不是一个路径。
|
|
39
39
|
*/
|
|
40
40
|
export declare const PLUGIN_PATCH_REF_PREFIX = "patchFile:";
|
|
41
|
+
/**
|
|
42
|
+
* 以**系统 home 为根**的文件类分区(ref 是系统 home 相对路径)。
|
|
43
|
+
*
|
|
44
|
+
* 为什么单独一组:`FileSystemFacade` 被限制在 $DSH_HOME 内,而第三方插件的数据常在 $DSH_HOME 之外
|
|
45
|
+
* (`~/.dsh-wallpaper-engine/`、`~/.mnemon/` …)。这两个分区的路径解析与 I/O 门面都换根,
|
|
46
|
+
* 其余分区一字不动 —— 单根不变量只在**这两个分区**上被显式放宽,且仍然只允许白名单区域。
|
|
47
|
+
*/
|
|
48
|
+
export declare const OS_HOME_FILE_SECTIONS: ReadonlySet<SectionId>;
|
|
49
|
+
/** 该分区文件目标的根(`dsh` = $DSH_HOME;`osHome` = 系统 home) */
|
|
50
|
+
export declare function sectionFileRoot(adapter: SectionId): 'dsh' | 'osHome';
|
|
51
|
+
/** 系统 home:宿主显式给就用它,否则进程 `homedir()`(`~` 的定义,不是猜) */
|
|
52
|
+
export declare function osHomeDirOf(ctx: HostContext): string;
|
|
53
|
+
/**
|
|
54
|
+
* 该分区文件 I/O 应使用的门面。
|
|
55
|
+
*
|
|
56
|
+
* `osHome` 分区必须用 `homeFs`:缺了它**显式抛错**,绝不退回 `ctx.fs` —— 那会把
|
|
57
|
+
* `~/.mnemon/x.db` 写成 `$DSH_HOME/.mnemon/x.db`(看起来成功、实际写错位置)。
|
|
58
|
+
*/
|
|
59
|
+
export declare function fsForSection(ctx: HostContext, adapter: SectionId): FileSystemFacade;
|
|
41
60
|
/** 解析文件类目标的绝对路径(引擎通用快照与回滚共用) */
|
|
42
61
|
export declare function resolveFileTarget(ctx: HostContext, adapter: SectionId, ref: string): string;
|
|
43
62
|
/**
|
|
@@ -144,6 +163,11 @@ export interface FileSnapshotStoreOptions {
|
|
|
144
163
|
* 未注入 → `fallbackPruneSelector`(只按 keepLast,等价既有 FIFO;分层字段被忽略 = 保守少删)。
|
|
145
164
|
*/
|
|
146
165
|
pruneSelector?: PruneSelector;
|
|
166
|
+
/**
|
|
167
|
+
* 诊断回调(可选):保留清理**被放弃**或失败时的可见线索。
|
|
168
|
+
* 缺省 = 不记录(不改变行为);宿主接 `host.log.warn` —— 「这轮为什么没清理」必须能查。
|
|
169
|
+
*/
|
|
170
|
+
onWarn?: (message: string) => void;
|
|
147
171
|
}
|
|
148
172
|
/** 文件快照存储:<dir>/<id>/snapshot.json + <dir>/<id>/blobs/* */
|
|
149
173
|
export declare class FileSnapshotStore implements SnapshotStore {
|