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/adapters/index.d.ts
CHANGED
|
@@ -40,7 +40,7 @@ export declare function createAdapters(options?: AdapterRegistryOptions): Config
|
|
|
40
40
|
export type { NamespaceProvider } from './settings.ts';
|
|
41
41
|
export type { CredentialRefsProvider } from './credentials.ts';
|
|
42
42
|
export { SettingsAdapter } from './settings.ts';
|
|
43
|
-
export { UiAdapter, isUiNamespace, KNOWN_UI_NAMESPACE_PREFIXES,
|
|
43
|
+
export { UiAdapter, isUiNamespace, KNOWN_UI_NAMESPACE_PREFIXES, uiMigrationNotes } from './ui.ts';
|
|
44
44
|
export { ProvidersAdapter, DEFAULT_PROVIDER_NAMESPACES, type ProviderExportEntry, type ProviderExportSection } from './providers.ts';
|
|
45
45
|
export { PluginsAdapter, USER_PATCH_FILE } from './plugins.ts';
|
|
46
46
|
export type { LocalPluginPackHook } from './plugins.ts';
|
|
@@ -54,5 +54,6 @@ export { CredentialsAdapter, defaultCredentialRefs } from './credentials.ts';
|
|
|
54
54
|
export { PluginFilesAdapter, DEFAULT_PLUGIN_FILE_WHITELIST } from './plugin-files.ts';
|
|
55
55
|
export { SessionsAdapter } from './sessions.ts';
|
|
56
56
|
export { SelfAdapter, SELF_CONFIG_FILES } from './self.ts';
|
|
57
|
+
export { PluginDataAdapter, type PluginDataAdapterOptions, type PluginDataEntry } from './plugin-data.ts';
|
|
57
58
|
export { FileCollectionAdapter } from './file-collection.ts';
|
|
58
59
|
export { validateJsonSection, type SectionIssues } from './json-section.ts';
|
package/lib/adapters/index.js
CHANGED
|
@@ -12,6 +12,7 @@ import { CredentialsAdapter } from './credentials.js';
|
|
|
12
12
|
import { PluginFilesAdapter } from './plugin-files.js';
|
|
13
13
|
import { SessionsAdapter } from './sessions.js';
|
|
14
14
|
import { SelfAdapter } from './self.js';
|
|
15
|
+
import { PluginDataAdapter } from './plugin-data.js';
|
|
15
16
|
/** 组装默认 adapter 列表(pluginFiles/sessions 恒挂载但 defaultIncluded=false,用户勾选才导出) */
|
|
16
17
|
export function createAdapters(options = {}) {
|
|
17
18
|
const namespaces = options.namespaces ?? [];
|
|
@@ -28,6 +29,9 @@ export function createAdapters(options = {}) {
|
|
|
28
29
|
new WorkspacesAdapter(),
|
|
29
30
|
new CredentialsAdapter({ namespaces, refs: options.credentialsRefs }),
|
|
30
31
|
new PluginFilesAdapter(options.pluginFiles, options.pluginFilesDir),
|
|
32
|
+
// 第三方插件数据(两个分区:配置类 / 大资产类;均 defaultIncluded=false,用户显式勾选)
|
|
33
|
+
new PluginDataAdapter('pluginData'),
|
|
34
|
+
new PluginDataAdapter('pluginAssets'),
|
|
31
35
|
];
|
|
32
36
|
// self 分区:插件自身配置(同步/市场/偏好),portable 默认包含;'' = 不挂载
|
|
33
37
|
if (options.selfDir !== '') {
|
|
@@ -38,7 +42,7 @@ export function createAdapters(options = {}) {
|
|
|
38
42
|
return adapters;
|
|
39
43
|
}
|
|
40
44
|
export { SettingsAdapter } from './settings.js';
|
|
41
|
-
export { UiAdapter, isUiNamespace, KNOWN_UI_NAMESPACE_PREFIXES,
|
|
45
|
+
export { UiAdapter, isUiNamespace, KNOWN_UI_NAMESPACE_PREFIXES, uiMigrationNotes } from './ui.js';
|
|
42
46
|
export { ProvidersAdapter, DEFAULT_PROVIDER_NAMESPACES } from './providers.js';
|
|
43
47
|
export { PluginsAdapter, USER_PATCH_FILE } from './plugins.js';
|
|
44
48
|
export { McpAdapter, extractMcpServers, buildMcpPatchLine } from './mcp.js';
|
|
@@ -51,6 +55,7 @@ export { CredentialsAdapter, defaultCredentialRefs } from './credentials.js';
|
|
|
51
55
|
export { PluginFilesAdapter, DEFAULT_PLUGIN_FILE_WHITELIST } from './plugin-files.js';
|
|
52
56
|
export { SessionsAdapter } from './sessions.js';
|
|
53
57
|
export { SelfAdapter, SELF_CONFIG_FILES } from './self.js';
|
|
58
|
+
export { PluginDataAdapter } from './plugin-data.js';
|
|
54
59
|
export { FileCollectionAdapter } from './file-collection.js';
|
|
55
60
|
/* JSON 分区共享校验骨架(本导出是其接入点)。收敛轨迹:
|
|
56
61
|
* · t31 抽出骨架,settings / ui / providers / plugins / mcp / prompts / credentials /
|
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
*/
|
|
10
10
|
import type { FileSystemFacade } from '../core/types.ts';
|
|
11
11
|
import type { MsgFunc } from '../core/messages.ts';
|
|
12
|
+
import './messages.ts';
|
|
12
13
|
import type { RecursiveListing } from '../utils/recursive-walk.ts';
|
|
13
14
|
/** 取遍历结果:宿主未实现 listRecursiveDetailed(旧版/测试 mock)时回退,行为与旧版一致。 */
|
|
14
15
|
export declare function listFilesDetailed(fs: FileSystemFacade, dir: string): Promise<RecursiveListing>;
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
// t6:注册 adapters 域追加目录(本模块只消费注入的 msg,故只引它的注册副作用)。
|
|
2
|
+
import './messages.js';
|
|
1
3
|
/** 跳过原因 → 消息 key(与 SkippedLink.reason 一一对应,穷尽映射) */
|
|
2
4
|
const REASON_KEY = {
|
|
3
5
|
loop: 'adapter.linkReason.loop',
|
|
@@ -8,11 +10,27 @@ const REASON_KEY = {
|
|
|
8
10
|
};
|
|
9
11
|
/** 单条原因下最多列出的路径数(其余折叠为计数,避免把报告撑爆) */
|
|
10
12
|
const MAX_PATHS_PER_REASON = 5;
|
|
13
|
+
/**
|
|
14
|
+
* 被跳过的依赖目录 → 详情片段(路径 + 文件数,issue #76)。
|
|
15
|
+
*
|
|
16
|
+
* 文件数是**量级说明**而非完整性判据:计数预算耗尽时如实写「未统计」,绝不猜一个数
|
|
17
|
+
* (把未知写成 0 会被读成「这个目录是空的」)。
|
|
18
|
+
*/
|
|
19
|
+
function depDirDetail(msg, dirs) {
|
|
20
|
+
const shown = dirs.slice(0, MAX_PATHS_PER_REASON).map((dir) => {
|
|
21
|
+
const files = dir.files === null
|
|
22
|
+
? msg('adapter.depsDirFilesUnknown')
|
|
23
|
+
: msg('adapter.depsDirFiles', { count: String(dir.files) });
|
|
24
|
+
return msg('adapter.linkDirEntry', { path: dir.path, files });
|
|
25
|
+
});
|
|
26
|
+
const more = dirs.length - shown.length;
|
|
27
|
+
return (more > 0 ? [...shown, `(+ ${more})`] : shown).join(', ');
|
|
28
|
+
}
|
|
11
29
|
/** 取遍历结果:宿主未实现 listRecursiveDetailed(旧版/测试 mock)时回退,行为与旧版一致。 */
|
|
12
30
|
export async function listFilesDetailed(fs, dir) {
|
|
13
31
|
if (fs.listRecursiveDetailed !== undefined)
|
|
14
32
|
return fs.listRecursiveDetailed(dir);
|
|
15
|
-
return { paths: await fs.listRecursive(dir), skippedLinks: [], followedLinks: 0, unreadableDirs: [] };
|
|
33
|
+
return { paths: await fs.listRecursive(dir), skippedLinks: [], skippedDepDirs: [], followedLinks: 0, unreadableDirs: [] };
|
|
16
34
|
}
|
|
17
35
|
/** 把遍历结果转成告警行(无链接时返回空数组 —— 不制造噪音)。 */
|
|
18
36
|
export function linkWarnings(msg, type, listing) {
|
|
@@ -30,8 +48,8 @@ export function linkWarnings(msg, type, listing) {
|
|
|
30
48
|
const detail = [...byReason.entries()].map(([reason, paths]) => {
|
|
31
49
|
const shown = paths.slice(0, MAX_PATHS_PER_REASON).join(', ');
|
|
32
50
|
const more = paths.length > MAX_PATHS_PER_REASON ? ` (+ ${paths.length - MAX_PATHS_PER_REASON})` : '';
|
|
33
|
-
return
|
|
34
|
-
}).join('
|
|
51
|
+
return msg('adapter.linkSkipLine', { reason: msg(REASON_KEY[reason]), count: paths.length, paths: shown, more });
|
|
52
|
+
}).join(msg('adapter.listSeparator'));
|
|
35
53
|
out.push(msg('adapter.linksSkipped', { type, count: String(listing.skippedLinks.length), detail }));
|
|
36
54
|
}
|
|
37
55
|
if (listing.unreadableDirs.length > 0) {
|
|
@@ -42,6 +60,12 @@ export function linkWarnings(msg, type, listing) {
|
|
|
42
60
|
type, count: String(listing.unreadableDirs.length), detail: `${shown}${more}`,
|
|
43
61
|
}));
|
|
44
62
|
}
|
|
63
|
+
// issue #76:运行时依赖目录整棵跳过(内容未进备份)——与链接/不可读目录同等对待:必须可见,绝不静默。
|
|
64
|
+
if (listing.skippedDepDirs.length > 0) {
|
|
65
|
+
out.push(msg('adapter.depsDirsSkipped', {
|
|
66
|
+
type, count: String(listing.skippedDepDirs.length), detail: depDirDetail(msg, listing.skippedDepDirs),
|
|
67
|
+
}));
|
|
68
|
+
}
|
|
45
69
|
return out;
|
|
46
70
|
}
|
|
47
71
|
//# sourceMappingURL=link-report.js.map
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { MsgFunc } from '../core/msg-types.ts';
|
|
2
|
+
export declare const zh: {
|
|
3
|
+
readonly 'adapter.linkDirEntry': '{path}({files})';
|
|
4
|
+
readonly 'adapter.linkSkipLine': '{reason} {count} 个: {paths}{more}';
|
|
5
|
+
readonly 'adapter.listSeparator': ';';
|
|
6
|
+
readonly 'adapter.pluginFilesCollectDirInvalid': 'pluginFiles collectDir 非法(须为相对 ~/.dsh 根的安全路径): {dir}';
|
|
7
|
+
readonly 'adapter.tarballRelPathInvalid': '非法 tarball 相对路径: {path}';
|
|
8
|
+
readonly 'adapter.localTarballUnpackFailed': '本地插件 {name} 的 tarball 解包失败,回退原始 spec:{err}';
|
|
9
|
+
readonly 'adapter.promptDiffDetail': '行 {line} current={current} imported={imported}';
|
|
10
|
+
readonly 'adapter.sessionUncoupledNote': '{id}({reason})';
|
|
11
|
+
readonly 'adapter.workspaceNotesParen': '({notes})';
|
|
12
|
+
readonly 'adapter.uiLocalStorageTaskBoard': '任务看板数据存于浏览器 localStorage,Host 侧无访问通道,需在目标机器重新配置';
|
|
13
|
+
readonly 'adapter.uiLocalStoragePanel': '面板宽度/折叠状态存于浏览器 localStorage,Host 侧无访问通道,需在目标机器重新配置';
|
|
14
|
+
readonly 'adapter.uiLocalStorageGeneric': '浏览器端 UI 状态无法经 Host API 迁移';
|
|
15
|
+
readonly 'adapter.pluginDataEnvOverridden': '{path} 的位置被环境变量 {env} 覆盖,本次已跳过(不猜它指向哪里)';
|
|
16
|
+
readonly 'adapter.pluginDataPathNotAllowed': '路径不在第三方插件数据的允许范围内,已拒绝写入: {path}';
|
|
17
|
+
readonly 'adapter.pluginDataNoOsHome': '第三方插件的数据在系统主目录下,但当前宿主没有提供该目录的文件通道(旧版本宿主):本次不读取也不导入 / third-party plugin data lives under the system home directory, but this host offers no file channel for it (older host): nothing is read or imported this time';
|
|
18
|
+
};
|
|
19
|
+
export declare const en: Record<keyof typeof zh, string>;
|
|
20
|
+
/** 本域活翻译器(与 core 的 engineMsg 同一实现;导入本模块即完成目录注册)。 */
|
|
21
|
+
export declare const engineMsg: MsgFunc;
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* adapters 域(src/adapters/**)的追加消息目录(zh 源 / en 镜像),归 t6 作用域。
|
|
3
|
+
*
|
|
4
|
+
* 为什么不在 \`src/core/messages.ts\` 追加:该文件不在本作用域的 inScope 内,且既有 372 键有冻结指纹。
|
|
5
|
+
* 本模块经 \`registerMessageCatalog\` 注册**追加目录**:engineMsg / makeMsg 的查找顺序是
|
|
6
|
+
* 「既有目录 → en 下回退既有 zh → 注册目录 → 键名」,所以本域新键只在既有目录未命中时生效,
|
|
7
|
+
* 不可能遮蔽 core 的既有 adapter.* 键。
|
|
8
|
+
*
|
|
9
|
+
* 纪律:zh 值 = 改动前的既有中文文案**逐字原样**;en 为 \`Record<keyof typeof zh, string>\`(缺键即编译错)。
|
|
10
|
+
* **导入本模块即完成注册**:本域消费面是注入的 ctx.msg(MsgFunc)与 engineMsg,两者都查注册目录。
|
|
11
|
+
*/
|
|
12
|
+
import { engineMsg as engineMsgOf, registerMessageCatalog } from '../core/engine-locale.js';
|
|
13
|
+
export const zh = {
|
|
14
|
+
// ---------- 链接 / 依赖目录报告(issue #37 / #76)----------
|
|
15
|
+
'adapter.linkDirEntry': '{path}({files})',
|
|
16
|
+
'adapter.linkSkipLine': '{reason} {count} 个: {paths}{more}',
|
|
17
|
+
'adapter.listSeparator': ';',
|
|
18
|
+
// ---------- pluginFiles / tarball ----------
|
|
19
|
+
'adapter.pluginFilesCollectDirInvalid': 'pluginFiles collectDir 非法(须为相对 ~/.dsh 根的安全路径): {dir}',
|
|
20
|
+
'adapter.tarballRelPathInvalid': '非法 tarball 相对路径: {path}',
|
|
21
|
+
'adapter.localTarballUnpackFailed': '本地插件 {name} 的 tarball 解包失败,回退原始 spec:{err}',
|
|
22
|
+
// ---------- prompts 计划项明细 ----------
|
|
23
|
+
'adapter.promptDiffDetail': '行 {line} current={current} imported={imported}',
|
|
24
|
+
// ---------- 会话连带失败明细 ----------
|
|
25
|
+
'adapter.sessionUncoupledNote': '{id}({reason})',
|
|
26
|
+
// ---------- 工作区告警拼接 ----------
|
|
27
|
+
'adapter.workspaceNotesParen': '({notes})',
|
|
28
|
+
// ---------- localStorage 等 Host 无通道的 UI 状态说明 ----------
|
|
29
|
+
'adapter.uiLocalStorageTaskBoard': '任务看板数据存于浏览器 localStorage,Host 侧无访问通道,需在目标机器重新配置',
|
|
30
|
+
'adapter.uiLocalStoragePanel': '面板宽度/折叠状态存于浏览器 localStorage,Host 侧无访问通道,需在目标机器重新配置',
|
|
31
|
+
'adapter.uiLocalStorageGeneric': '浏览器端 UI 状态无法经 Host API 迁移',
|
|
32
|
+
// ---------- pluginData / pluginAssets(第三方插件数据迁移) ----------
|
|
33
|
+
'adapter.pluginDataEnvOverridden': '{path} 的位置被环境变量 {env} 覆盖,本次已跳过(不猜它指向哪里)',
|
|
34
|
+
'adapter.pluginDataPathNotAllowed': '路径不在第三方插件数据的允许范围内,已拒绝写入: {path}',
|
|
35
|
+
'adapter.pluginDataNoOsHome': '第三方插件的数据在系统主目录下,但当前宿主没有提供该目录的文件通道(旧版本宿主):本次不读取也不导入 / third-party plugin data lives under the system home directory, but this host offers no file channel for it (older host): nothing is read or imported this time',
|
|
36
|
+
};
|
|
37
|
+
export const en = {
|
|
38
|
+
'adapter.linkDirEntry': '{path} ({files})',
|
|
39
|
+
'adapter.linkSkipLine': '{reason} {count} item(s): {paths}{more}',
|
|
40
|
+
'adapter.listSeparator': '; ',
|
|
41
|
+
'adapter.pluginFilesCollectDirInvalid': 'pluginFiles collectDir is invalid (must be a safe path relative to the ~/.dsh root): {dir}',
|
|
42
|
+
'adapter.tarballRelPathInvalid': 'Invalid relative tarball path: {path}',
|
|
43
|
+
'adapter.localTarballUnpackFailed': 'Failed to unpack the tarball of local plugin {name}; falling back to the original spec: {err}',
|
|
44
|
+
'adapter.promptDiffDetail': 'line {line} current={current} imported={imported}',
|
|
45
|
+
'adapter.sessionUncoupledNote': '{id} ({reason})',
|
|
46
|
+
'adapter.workspaceNotesParen': ' ({notes})',
|
|
47
|
+
'adapter.uiLocalStorageTaskBoard': 'Task-board data lives in the browser localStorage and the host has no access path; reconfigure it on the target machine',
|
|
48
|
+
'adapter.uiLocalStoragePanel': 'Panel width/collapsed state lives in the browser localStorage and the host has no access path; reconfigure it on the target machine',
|
|
49
|
+
'adapter.uiLocalStorageGeneric': 'Browser-side UI state cannot be migrated through the host API',
|
|
50
|
+
'adapter.pluginDataEnvOverridden': "the location of {path} is overridden by the {env} environment variable; skipped this time (the new location is never guessed)",
|
|
51
|
+
'adapter.pluginDataPathNotAllowed': 'the path is outside the range allowed for third-party plugin data and was refused: {path}',
|
|
52
|
+
'adapter.pluginDataNoOsHome': 'third-party plugin data lives under the system home directory, but this host offers no file channel for it (older host): nothing is read or imported this time',
|
|
53
|
+
};
|
|
54
|
+
registerMessageCatalog({ id: 'adapters-domain', zh, en });
|
|
55
|
+
/** 本域活翻译器(与 core 的 engineMsg 同一实现;导入本模块即完成目录注册)。 */
|
|
56
|
+
export const engineMsg = engineMsgOf;
|
|
57
|
+
//# sourceMappingURL=messages.js.map
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import type { RecursiveListing } from '../utils/recursive-walk.ts';
|
|
2
|
+
import type { PluginDataArea, PluginDataSource, PluginDataSection } from '../core/plugin-data-registry.ts';
|
|
3
|
+
import { FileCollectionAdapter } from './file-collection.ts';
|
|
4
|
+
import type { FilesSection } from '../schema/types.ts';
|
|
5
|
+
import type { ApplyResult, ExportOptions, ExportSection, HostContext, ImportContext, PlanItem, SectionPreview } from '../core/types.ts';
|
|
6
|
+
/** 区域条目(来源 + 区域),编译期与运行期唯一的区域表示 */
|
|
7
|
+
export type PluginDataEntry = {
|
|
8
|
+
source: PluginDataSource;
|
|
9
|
+
area: PluginDataArea;
|
|
10
|
+
};
|
|
11
|
+
export interface PluginDataAdapterOptions {
|
|
12
|
+
/** 内置来源(缺省 = BUILTIN_PLUGIN_DATA_SOURCES;测试注入假来源) */
|
|
13
|
+
readonly sources?: readonly PluginDataSource[];
|
|
14
|
+
/** 自述声明缓存 TTL;测试可传 0 关闭缓存 */
|
|
15
|
+
readonly declaredTtlMs?: number;
|
|
16
|
+
/**
|
|
17
|
+
* 环境变量快照(缺省 process.env)。
|
|
18
|
+
*
|
|
19
|
+
* 为什么走构造参数而不是 HostContext:HostContext 上没有 env(它是宿主门面的集合),
|
|
20
|
+
* 而「区域被环境变量搬走」这件事必须在**同一次调用**里可判定、可注入测试值 ——
|
|
21
|
+
* 直接读 process.env 会让单测无法造「被覆盖」的场景(本机若恰好设了该变量,测试还会 flaky)。
|
|
22
|
+
*/
|
|
23
|
+
readonly env?: Readonly<Record<string, string | undefined>>;
|
|
24
|
+
}
|
|
25
|
+
export declare class PluginDataAdapter extends FileCollectionAdapter {
|
|
26
|
+
readonly id: PluginDataSection;
|
|
27
|
+
readonly displayName: string;
|
|
28
|
+
readonly defaultIncluded = false;
|
|
29
|
+
readonly portability: 'deviceSpecific';
|
|
30
|
+
/** home 根(相对路径 = home 相对路径,与 agentInstructions 同形) */
|
|
31
|
+
readonly baseDir = "";
|
|
32
|
+
private readonly builtin;
|
|
33
|
+
private readonly declaredTtlMs;
|
|
34
|
+
private readonly env;
|
|
35
|
+
/**
|
|
36
|
+
* 自述声明缓存。**必须带 ctx 键**(homeDir + profile):这台机器装了某插件,不代表另一台装了 ——
|
|
37
|
+
* 缓存若按实例共享,导入到「没装该插件」的目标机时会拿源机的声明去放行写入,
|
|
38
|
+
* 白名单就变成了「谁先导入谁说了算」。
|
|
39
|
+
*/
|
|
40
|
+
private declaredMemo;
|
|
41
|
+
/** 本轮清单(listRelPaths 写入;unitIdOf 是同步纯函数,只能从这里拿区域表) */
|
|
42
|
+
private active;
|
|
43
|
+
/** 本轮告警(环境变量覆盖跳过等;export/preview 收尾合并进返回的 warnings) */
|
|
44
|
+
private pendingWarnings;
|
|
45
|
+
constructor(section: PluginDataSection, options?: PluginDataAdapterOptions);
|
|
46
|
+
/** 本分区可收集区域(内置 + 自述声明) */
|
|
47
|
+
private allowedEntries;
|
|
48
|
+
private allSources;
|
|
49
|
+
/**
|
|
50
|
+
* 已安装插件的自述声明(dsh.dataDirs)。
|
|
51
|
+
*
|
|
52
|
+
* 为什么读 profile 的 node_modules 而不是全局目录:DSH 的插件装在 \`profiles/<profile>/node_modules\`
|
|
53
|
+
* 下(pnpm 隔离布局),且同一个包在不同 profile 可以是不同版本 —— 声明必须取**本 profile 实际装的**那份。
|
|
54
|
+
*
|
|
55
|
+
* 读不到(未安装 / 无声明 / 形状非法)一律跳过:本机制是**附加**信息,绝不因为一个包读不出来
|
|
56
|
+
* 就影响内置来源或整次导出。
|
|
57
|
+
*/
|
|
58
|
+
private declaredSources;
|
|
59
|
+
/**
|
|
60
|
+
* 区域是否被环境变量覆盖(覆盖 → 返回变量名;未覆盖 / 未声明 → null)。
|
|
61
|
+
*
|
|
62
|
+
* 判定口径:变量**存在且解析结果与默认位置不同**才算覆盖 —— 仅仅 \`DSH_WE_DATA_DIR=~/.dsh-wallpaper-engine\`
|
|
63
|
+
* 这种「设成同一个值」的写法不该让区域凭空消失(那会让用户以为「没有数据」)。
|
|
64
|
+
* 解析只做 \`~\` 展开与分隔符归一(不做 realpath:不读盘、不猜)。
|
|
65
|
+
*/
|
|
66
|
+
private envOverrideOf;
|
|
67
|
+
/**
|
|
68
|
+
* 区域是否是**普通文件**(而不是目录 / 不存在)。
|
|
69
|
+
*
|
|
70
|
+
* 判据顺序为什么是这样:`statSize` 对「非普通文件」与「不存在」都返回 null,无法单独区分
|
|
71
|
+
* 目录与缺失;所以先判「是文件」,其余一律按目录走遍历 —— 目录不存在时遍历返回空,
|
|
72
|
+
* 结果同样是「没有内容」,不会把缺失误报成文件,也不会把目录误读成空内容而静默丢数据。
|
|
73
|
+
* 门面未实现 statSize(旧宿主)时退回读一次内容:读得出来就是文件。
|
|
74
|
+
*/
|
|
75
|
+
private isRegularFile;
|
|
76
|
+
private static emptyListing;
|
|
77
|
+
/**
|
|
78
|
+
* 清单枚举 = 来源注册表的区域(覆写基类的目录递归;baseDir='' 时基类实现会递归**整个 home**)。
|
|
79
|
+
*
|
|
80
|
+
* **必须覆写**:home 根上递归会遍历 profiles/ sessions/ attachments/ 等全部内容(本机实测
|
|
81
|
+
* 4000+ 文件 / 数百 MB),既慢又会把无关目录当成该分区的「可勾选单元」。
|
|
82
|
+
*/
|
|
83
|
+
protected listRelPaths(ctx: HostContext): Promise<RecursiveListing>;
|
|
84
|
+
/**
|
|
85
|
+
* 单元 = 区域(最长前缀归属)。
|
|
86
|
+
*
|
|
87
|
+
* 用「本轮清单写入的区域表」而不是重新解析:listRelPaths 与本函数在同一次 collect 里成对调用,
|
|
88
|
+
* 重算会让两次解析结果不一致时出现「单元 id 不在清单里」(选择器勾不上)。测试/异常路径下
|
|
89
|
+
* \`active\` 可能是 undefined(例如直接调 listUnits),此时退回**内置来源**的最长前缀 —— 与清单口径同源。
|
|
90
|
+
*/
|
|
91
|
+
protected unitIdOf(relativePath: string): string;
|
|
92
|
+
/**
|
|
93
|
+
* 读一个成员文件:**换根到系统 home 门面**(基类的其余逻辑 —— 体积、哈希、字节闸门 —— 一字不动)。
|
|
94
|
+
*
|
|
95
|
+
* 能走到这里说明 listRelPaths 产出过条目(缺门面时它不产出任何路径),因此缺门面即**抛错**,
|
|
96
|
+
* 不再造一个「空文件」出来(那会把缺通道伪装成空内容)。
|
|
97
|
+
*/
|
|
98
|
+
protected readMember(ctx: HostContext, rel: string, mode: 'content' | 'size', statTimes?: Map<string, number>): Promise<{
|
|
99
|
+
data: Uint8Array;
|
|
100
|
+
sizeBytes: number;
|
|
101
|
+
contentHash: string;
|
|
102
|
+
}>;
|
|
103
|
+
export(ctx: HostContext, options: ExportOptions): Promise<ExportSection<FilesSection>>;
|
|
104
|
+
preview(ctx: HostContext, options: ExportOptions): Promise<SectionPreview<FilesSection>>;
|
|
105
|
+
/**
|
|
106
|
+
* 导入分析 + **写入路径白名单**(本分区唯一的越界防线)。
|
|
107
|
+
*
|
|
108
|
+
* 判据:路径必须是本机可解析的某个区域(内置来源,或已安装插件的自述声明)之下,且未被该区域
|
|
109
|
+
* 的 excludeRel 排除。不满足 → Error(计划里可见、执行期不写),绝不「尽力而为」地写出去。
|
|
110
|
+
*/
|
|
111
|
+
analyzeImport(data: FilesSection, ctx: ImportContext): Promise<PlanItem[]>;
|
|
112
|
+
/** 应用前再查一遍白名单(纵深防御:plan 可能来自消费者手工构造/缓存反序列化) */
|
|
113
|
+
applyItem(item: PlanItem, ctx: ImportContext): Promise<ApplyResult>;
|
|
114
|
+
}
|
|
@@ -0,0 +1,309 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* pluginData / pluginAssets 分区 adapter(第三方插件数据迁移)。
|
|
3
|
+
*
|
|
4
|
+
* 数据来源 = \`src/core/plugin-data-registry.ts\` 的来源注册表:内置已取证来源 + 已安装插件在
|
|
5
|
+
* 自己 package.json 里声明的 \`dsh.dataDirs\`。每个「区域(area)」在备份里是**一个可勾选单元**,
|
|
6
|
+
* 单元 id = 区域的 home 相对 POSIX 路径(如 \`.dsh-wallpaper-engine/uploads\`)——
|
|
7
|
+
* 用户能在导出选择器里逐区域看到「几个文件 / 多大」,也能只带走配置、不带几百 MB 的上传壁纸。
|
|
8
|
+
*
|
|
9
|
+
* 为什么两个分区而不是一个:
|
|
10
|
+
* - \`pluginData\` = 配置与轻量状态;\`pluginAssets\` = 大体积用户数据(上传的壁纸、本地数据库);
|
|
11
|
+
* - 「默认不勾选大资产」在既有 UI 选择模型里**只有分区级开关**这一条可靠通道(单元级只有
|
|
12
|
+
* 「默认全选 + 稀疏排除」,没有「默认排除」的概念)—— 拆成两个分区后,「可见但默认不勾选」
|
|
13
|
+
* 就是既有的 \`defaultIncluded=false\` 语义,不需要动共享选择模型,也不会与导入/市场两处选择语义打架。
|
|
14
|
+
*
|
|
15
|
+
* 实现:继承 FileCollectionAdapter(baseDir = '',即 home 根;与 agentInstructions 同一形态),
|
|
16
|
+
* 复用它的单元分组 / 单分区字节闸门 / 预览-导出一致性 / 幂等哈希比对;只覆写三处:
|
|
17
|
+
* ① \`listRelPaths()\` —— 清单来自来源注册表(并如实记录被环境变量覆盖而跳过的区域);
|
|
18
|
+
* ② \`unitIdOf()\` —— 单元 = 区域(最长前缀归属);
|
|
19
|
+
* ③ \`analyzeImport() / applyItem()\` —— **写入路径白名单**(见下)。
|
|
20
|
+
*
|
|
21
|
+
* 安全不变量(本分区是全仓唯一「按 home 相对路径任意写」的分区,必须自己守住):
|
|
22
|
+
* - **导入白名单**:只有「本机内置来源的区域」与「本机已安装插件声明的区域」之下的路径才允许写。
|
|
23
|
+
* 恶意/损坏的包想把 \`.credentials.yaml\` 或 \`AGENTS.md\` 塞进来,在这里被判 Error(计划里可见,
|
|
24
|
+
* 执行期不写);applyItem 再查一遍(纵深防御),内层还有基类的 control-plane namespace 拒绝。
|
|
25
|
+
* - **缓存永不收录**:role === 'cache' 的区域不属于任何分区,导出时根本不进清单。
|
|
26
|
+
* - **环境变量覆盖即跳过**:区域声明的 env 存在且解析结果与默认位置不同 → 报码跳过,绝不猜新位置。
|
|
27
|
+
* - **不产生用户可见散文**:告警与计划项文案一律走消息目录(i18n 铁律)。
|
|
28
|
+
*/
|
|
29
|
+
import { normalizePath } from '../utils/paths.js';
|
|
30
|
+
import { engineMsg } from './messages.js';
|
|
31
|
+
import { listFilesDetailed } from './link-report.js';
|
|
32
|
+
import { sectionMeta } from '../schema/section-registry.js';
|
|
33
|
+
import { BUILTIN_PLUGIN_DATA_SOURCES, areaOfPath, areasForSection, isExcludedRel, normalizeHomeRel, parseDeclaredDataDirs, } from '../core/plugin-data-registry.js';
|
|
34
|
+
import { osHomeDirOf } from '../core/backup.js';
|
|
35
|
+
import { FileCollectionAdapter } from './file-collection.js';
|
|
36
|
+
/** 声明来源缓存的 TTL(毫秒):一次导出/导入/执行里会多次解析,逐次读 15+ 个 package.json 是净开销 */
|
|
37
|
+
const DECLARED_TTL_MS = 5_000;
|
|
38
|
+
/** 单次解析里最多读取多少个已安装插件的 manifest(防御性上限:profile 依赖数量异常时不无限读) */
|
|
39
|
+
const MAX_DECLARED_PACKAGES = 300;
|
|
40
|
+
export class PluginDataAdapter extends FileCollectionAdapter {
|
|
41
|
+
id;
|
|
42
|
+
displayName;
|
|
43
|
+
defaultIncluded = false;
|
|
44
|
+
portability = 'deviceSpecific';
|
|
45
|
+
/** home 根(相对路径 = home 相对路径,与 agentInstructions 同形) */
|
|
46
|
+
baseDir = '';
|
|
47
|
+
builtin;
|
|
48
|
+
declaredTtlMs;
|
|
49
|
+
env;
|
|
50
|
+
/**
|
|
51
|
+
* 自述声明缓存。**必须带 ctx 键**(homeDir + profile):这台机器装了某插件,不代表另一台装了 ——
|
|
52
|
+
* 缓存若按实例共享,导入到「没装该插件」的目标机时会拿源机的声明去放行写入,
|
|
53
|
+
* 白名单就变成了「谁先导入谁说了算」。
|
|
54
|
+
*/
|
|
55
|
+
declaredMemo;
|
|
56
|
+
/** 本轮清单(listRelPaths 写入;unitIdOf 是同步纯函数,只能从这里拿区域表) */
|
|
57
|
+
active;
|
|
58
|
+
/** 本轮告警(环境变量覆盖跳过等;export/preview 收尾合并进返回的 warnings) */
|
|
59
|
+
pendingWarnings = [];
|
|
60
|
+
constructor(section, options = {}) {
|
|
61
|
+
super();
|
|
62
|
+
this.id = section;
|
|
63
|
+
this.displayName = sectionMeta(section).displayName;
|
|
64
|
+
this.builtin = options.sources ?? BUILTIN_PLUGIN_DATA_SOURCES;
|
|
65
|
+
this.declaredTtlMs = options.declaredTtlMs ?? DECLARED_TTL_MS;
|
|
66
|
+
this.env = options.env ?? process.env;
|
|
67
|
+
}
|
|
68
|
+
/** 本分区可收集区域(内置 + 自述声明) */
|
|
69
|
+
async allowedEntries(ctx) {
|
|
70
|
+
const sources = await this.allSources(ctx);
|
|
71
|
+
return areasForSection(sources, this.id);
|
|
72
|
+
}
|
|
73
|
+
async allSources(ctx) {
|
|
74
|
+
const declared = await this.declaredSources(ctx);
|
|
75
|
+
return [...this.builtin, ...declared];
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* 已安装插件的自述声明(dsh.dataDirs)。
|
|
79
|
+
*
|
|
80
|
+
* 为什么读 profile 的 node_modules 而不是全局目录:DSH 的插件装在 \`profiles/<profile>/node_modules\`
|
|
81
|
+
* 下(pnpm 隔离布局),且同一个包在不同 profile 可以是不同版本 —— 声明必须取**本 profile 实际装的**那份。
|
|
82
|
+
*
|
|
83
|
+
* 读不到(未安装 / 无声明 / 形状非法)一律跳过:本机制是**附加**信息,绝不因为一个包读不出来
|
|
84
|
+
* 就影响内置来源或整次导出。
|
|
85
|
+
*/
|
|
86
|
+
async declaredSources(ctx) {
|
|
87
|
+
const now = Date.now();
|
|
88
|
+
const profile = ctx.profile !== undefined && ctx.profile !== '' ? ctx.profile : 'web';
|
|
89
|
+
const memoKey = ctx.homeDir + '|' + profile;
|
|
90
|
+
if (this.declaredMemo !== undefined
|
|
91
|
+
&& this.declaredMemo.key === memoKey
|
|
92
|
+
&& now - this.declaredMemo.at < this.declaredTtlMs) {
|
|
93
|
+
return this.declaredMemo.sources;
|
|
94
|
+
}
|
|
95
|
+
const base = 'profiles/' + profile;
|
|
96
|
+
const out = [];
|
|
97
|
+
let deps = [];
|
|
98
|
+
try {
|
|
99
|
+
const raw = await ctx.fs.readFile(base + '/package.json');
|
|
100
|
+
const parsed = JSON.parse(new TextDecoder().decode(raw));
|
|
101
|
+
const dep = parsed?.dependencies;
|
|
102
|
+
if (dep !== null && typeof dep === 'object')
|
|
103
|
+
deps = Object.keys(dep).slice(0, MAX_DECLARED_PACKAGES);
|
|
104
|
+
}
|
|
105
|
+
catch {
|
|
106
|
+
deps = [];
|
|
107
|
+
}
|
|
108
|
+
for (const name of deps) {
|
|
109
|
+
try {
|
|
110
|
+
const raw = await ctx.fs.readFile(base + '/node_modules/' + name + '/package.json');
|
|
111
|
+
const manifest = JSON.parse(new TextDecoder().decode(raw));
|
|
112
|
+
const source = parseDeclaredDataDirs(name, manifest);
|
|
113
|
+
if (source !== null)
|
|
114
|
+
out.push(source);
|
|
115
|
+
}
|
|
116
|
+
catch {
|
|
117
|
+
// 未安装 / 无 manifest → 无声明,跳过
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
this.declaredMemo = { key: memoKey, at: now, sources: out };
|
|
121
|
+
return out;
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* 区域是否被环境变量覆盖(覆盖 → 返回变量名;未覆盖 / 未声明 → null)。
|
|
125
|
+
*
|
|
126
|
+
* 判定口径:变量**存在且解析结果与默认位置不同**才算覆盖 —— 仅仅 \`DSH_WE_DATA_DIR=~/.dsh-wallpaper-engine\`
|
|
127
|
+
* 这种「设成同一个值」的写法不该让区域凭空消失(那会让用户以为「没有数据」)。
|
|
128
|
+
* 解析只做 \`~\` 展开与分隔符归一(不做 realpath:不读盘、不猜)。
|
|
129
|
+
*/
|
|
130
|
+
envOverrideOf(ctx, area) {
|
|
131
|
+
for (const name of area.envOverrides ?? []) {
|
|
132
|
+
const raw = this.env[name];
|
|
133
|
+
if (raw === undefined || raw.trim() === '')
|
|
134
|
+
continue;
|
|
135
|
+
const normalized = raw.trim().replace(/\\/g, '/').replace(/\/+$/, '');
|
|
136
|
+
// 默认位置以**系统 home** 为根(本分区的根;见 core/backup.ts 的 OS_HOME_FILE_SECTIONS)
|
|
137
|
+
const home = osHomeDirOf(ctx).replace(/\\/g, '/').replace(/\/+$/, '');
|
|
138
|
+
const expanded = normalized.startsWith('~/') ? home + normalized.slice(1) : normalized;
|
|
139
|
+
const defaultAbs = home + '/' + area.relPath;
|
|
140
|
+
if (expanded !== defaultAbs)
|
|
141
|
+
return name;
|
|
142
|
+
}
|
|
143
|
+
return null;
|
|
144
|
+
}
|
|
145
|
+
/**
|
|
146
|
+
* 区域是否是**普通文件**(而不是目录 / 不存在)。
|
|
147
|
+
*
|
|
148
|
+
* 判据顺序为什么是这样:`statSize` 对「非普通文件」与「不存在」都返回 null,无法单独区分
|
|
149
|
+
* 目录与缺失;所以先判「是文件」,其余一律按目录走遍历 —— 目录不存在时遍历返回空,
|
|
150
|
+
* 结果同样是「没有内容」,不会把缺失误报成文件,也不会把目录误读成空内容而静默丢数据。
|
|
151
|
+
* 门面未实现 statSize(旧宿主)时退回读一次内容:读得出来就是文件。
|
|
152
|
+
*/
|
|
153
|
+
async isRegularFile(fsFacade, rel) {
|
|
154
|
+
if (fsFacade.statSize !== undefined) {
|
|
155
|
+
try {
|
|
156
|
+
return (await fsFacade.statSize(rel)) !== null;
|
|
157
|
+
}
|
|
158
|
+
catch {
|
|
159
|
+
return false;
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
try {
|
|
163
|
+
await fsFacade.readFile(rel);
|
|
164
|
+
return true;
|
|
165
|
+
}
|
|
166
|
+
catch {
|
|
167
|
+
return false;
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
static emptyListing() {
|
|
171
|
+
return { paths: [], skippedLinks: [], skippedDepDirs: [], followedLinks: 0, unreadableDirs: [] };
|
|
172
|
+
}
|
|
173
|
+
/**
|
|
174
|
+
* 清单枚举 = 来源注册表的区域(覆写基类的目录递归;baseDir='' 时基类实现会递归**整个 home**)。
|
|
175
|
+
*
|
|
176
|
+
* **必须覆写**:home 根上递归会遍历 profiles/ sessions/ attachments/ 等全部内容(本机实测
|
|
177
|
+
* 4000+ 文件 / 数百 MB),既慢又会把无关目录当成该分区的「可勾选单元」。
|
|
178
|
+
*/
|
|
179
|
+
async listRelPaths(ctx) {
|
|
180
|
+
// 本分区的根是**系统 home**(~/.dsh-wallpaper-engine、~/.mnemon …),不是 $DSH_HOME ——
|
|
181
|
+
// 内容 I/O 一律走 homeFs;宿主没提供就什么都不收集,并如实报码(绝不退回 $DSH_HOME 去读错位置)。
|
|
182
|
+
const homeFs = ctx.homeFs;
|
|
183
|
+
if (homeFs === undefined) {
|
|
184
|
+
this.active = [];
|
|
185
|
+
this.pendingWarnings = [engineMsg('adapter.pluginDataNoOsHome')];
|
|
186
|
+
return PluginDataAdapter.emptyListing();
|
|
187
|
+
}
|
|
188
|
+
const entries = await this.allowedEntries(ctx);
|
|
189
|
+
this.active = entries;
|
|
190
|
+
const warnings = [];
|
|
191
|
+
const paths = [];
|
|
192
|
+
const listing = PluginDataAdapter.emptyListing();
|
|
193
|
+
for (const entry of entries) {
|
|
194
|
+
const rel = entry.area.relPath;
|
|
195
|
+
const env = this.envOverrideOf(ctx, entry.area);
|
|
196
|
+
if (env !== null) {
|
|
197
|
+
warnings.push(engineMsg('adapter.pluginDataEnvOverridden', { path: rel, env }));
|
|
198
|
+
continue;
|
|
199
|
+
}
|
|
200
|
+
if (await this.isRegularFile(homeFs, rel)) {
|
|
201
|
+
paths.push(rel);
|
|
202
|
+
continue;
|
|
203
|
+
}
|
|
204
|
+
let sub;
|
|
205
|
+
try {
|
|
206
|
+
sub = await listFilesDetailed(homeFs, rel);
|
|
207
|
+
}
|
|
208
|
+
catch {
|
|
209
|
+
// 目录不存在 / 读不到 → 视为无内容(存在的空目录与缺失在这里同义:都没有文件可带)
|
|
210
|
+
continue;
|
|
211
|
+
}
|
|
212
|
+
listing.skippedLinks.push(...sub.skippedLinks);
|
|
213
|
+
listing.skippedDepDirs.push(...sub.skippedDepDirs);
|
|
214
|
+
listing.unreadableDirs.push(...sub.unreadableDirs);
|
|
215
|
+
listing.followedLinks += sub.followedLinks;
|
|
216
|
+
for (const p of sub.paths) {
|
|
217
|
+
if (isExcludedRel(entry.area, p))
|
|
218
|
+
continue;
|
|
219
|
+
paths.push(p);
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
listing.paths = paths;
|
|
223
|
+
this.pendingWarnings = warnings;
|
|
224
|
+
return listing;
|
|
225
|
+
}
|
|
226
|
+
/**
|
|
227
|
+
* 单元 = 区域(最长前缀归属)。
|
|
228
|
+
*
|
|
229
|
+
* 用「本轮清单写入的区域表」而不是重新解析:listRelPaths 与本函数在同一次 collect 里成对调用,
|
|
230
|
+
* 重算会让两次解析结果不一致时出现「单元 id 不在清单里」(选择器勾不上)。测试/异常路径下
|
|
231
|
+
* \`active\` 可能是 undefined(例如直接调 listUnits),此时退回**内置来源**的最长前缀 —— 与清单口径同源。
|
|
232
|
+
*/
|
|
233
|
+
unitIdOf(relativePath) {
|
|
234
|
+
const entries = this.active ?? areasForSection(this.builtin, this.id);
|
|
235
|
+
const hit = areaOfPath(entries, normalizePath(relativePath));
|
|
236
|
+
return hit === null ? normalizePath(relativePath) : hit.area.relPath;
|
|
237
|
+
}
|
|
238
|
+
/**
|
|
239
|
+
* 读一个成员文件:**换根到系统 home 门面**(基类的其余逻辑 —— 体积、哈希、字节闸门 —— 一字不动)。
|
|
240
|
+
*
|
|
241
|
+
* 能走到这里说明 listRelPaths 产出过条目(缺门面时它不产出任何路径),因此缺门面即**抛错**,
|
|
242
|
+
* 不再造一个「空文件」出来(那会把缺通道伪装成空内容)。
|
|
243
|
+
*/
|
|
244
|
+
async readMember(ctx, rel, mode, statTimes) {
|
|
245
|
+
const homeFs = ctx.homeFs;
|
|
246
|
+
if (homeFs === undefined)
|
|
247
|
+
throw new Error(engineMsg('adapter.pluginDataNoOsHome'));
|
|
248
|
+
return super.readMember({ ...ctx, fs: homeFs }, rel, mode, statTimes);
|
|
249
|
+
}
|
|
250
|
+
async export(ctx, options) {
|
|
251
|
+
this.pendingWarnings = [];
|
|
252
|
+
const out = await super.export(ctx, options);
|
|
253
|
+
return { ...out, warnings: [...out.warnings, ...this.pendingWarnings] };
|
|
254
|
+
}
|
|
255
|
+
async preview(ctx, options) {
|
|
256
|
+
this.pendingWarnings = [];
|
|
257
|
+
const out = await super.preview(ctx, options);
|
|
258
|
+
return {
|
|
259
|
+
...out,
|
|
260
|
+
section: { ...out.section, warnings: [...out.section.warnings, ...this.pendingWarnings] },
|
|
261
|
+
};
|
|
262
|
+
}
|
|
263
|
+
/**
|
|
264
|
+
* 导入分析 + **写入路径白名单**(本分区唯一的越界防线)。
|
|
265
|
+
*
|
|
266
|
+
* 判据:路径必须是本机可解析的某个区域(内置来源,或已安装插件的自述声明)之下,且未被该区域
|
|
267
|
+
* 的 excludeRel 排除。不满足 → Error(计划里可见、执行期不写),绝不「尽力而为」地写出去。
|
|
268
|
+
*/
|
|
269
|
+
async analyzeImport(data, ctx) {
|
|
270
|
+
// 宿主没有系统 home 门面(旧宿主)→ 白名单为空 + 目标读也换不了根:**全部拒绝**并说明原因。
|
|
271
|
+
// 绝不退回 $DSH_HOME 的 ctx.fs 去比对/写入(那会落到错误位置)。
|
|
272
|
+
const homeFs = ctx.target.homeFs;
|
|
273
|
+
const entries = homeFs === undefined ? [] : await this.allowedEntries(ctx.target);
|
|
274
|
+
const items = await super.analyzeImport(data, homeFs === undefined ? ctx : { ...ctx, target: { ...ctx.target, fs: homeFs } });
|
|
275
|
+
return items.map((item) => {
|
|
276
|
+
const ref = item.target?.ref;
|
|
277
|
+
if (ref === undefined)
|
|
278
|
+
return item;
|
|
279
|
+
const rel = normalizeHomeRel(ref);
|
|
280
|
+
if (rel !== null && areaOfPath(entries, rel) !== null)
|
|
281
|
+
return item;
|
|
282
|
+
return {
|
|
283
|
+
id: item.id,
|
|
284
|
+
...(item.unitId !== undefined ? { unitId: item.unitId } : {}),
|
|
285
|
+
kind: 'Error',
|
|
286
|
+
adapter: this.id,
|
|
287
|
+
description: engineMsg(homeFs === undefined ? 'adapter.pluginDataNoOsHome' : 'adapter.pluginDataPathNotAllowed', { path: ref }),
|
|
288
|
+
severity: 'error',
|
|
289
|
+
};
|
|
290
|
+
});
|
|
291
|
+
}
|
|
292
|
+
/** 应用前再查一遍白名单(纵深防御:plan 可能来自消费者手工构造/缓存反序列化) */
|
|
293
|
+
async applyItem(item, ctx) {
|
|
294
|
+
const homeFs = ctx.target.homeFs;
|
|
295
|
+
if (homeFs === undefined) {
|
|
296
|
+
return { ok: false, message: engineMsg('adapter.pluginDataNoOsHome') };
|
|
297
|
+
}
|
|
298
|
+
const ref = item.target?.ref;
|
|
299
|
+
if (ref !== undefined) {
|
|
300
|
+
const rel = normalizeHomeRel(ref);
|
|
301
|
+
const entries = await this.allowedEntries(ctx.target);
|
|
302
|
+
if (rel === null || areaOfPath(entries, rel) === null) {
|
|
303
|
+
return { ok: false, message: engineMsg('adapter.pluginDataPathNotAllowed', { path: ref }) };
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
return super.applyItem(item, { ...ctx, target: { ...ctx.target, fs: homeFs } });
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
//# sourceMappingURL=plugin-data.js.map
|
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
*/
|
|
9
9
|
import { sha256Hex } from '../utils/hashing.js';
|
|
10
10
|
import { msgOf, zhMsg } from '../core/messages.js';
|
|
11
|
+
import { engineMsg } from './messages.js';
|
|
11
12
|
import { linkWarnings, listFilesDetailed } from './link-report.js';
|
|
12
13
|
import { isPathSafe, isReservedInternalRel } from '../utils/paths.js';
|
|
13
14
|
import { unitAllowed, unitsFromFiles } from './units.js';
|
|
@@ -26,7 +27,7 @@ export class PluginFilesAdapter {
|
|
|
26
27
|
constructor(whitelist = [...DEFAULT_PLUGIN_FILE_WHITELIST], collectDir) {
|
|
27
28
|
this.whitelist = whitelist;
|
|
28
29
|
if (collectDir !== undefined && collectDir !== '' && !isPathSafe(collectDir)) {
|
|
29
|
-
throw new Error(
|
|
30
|
+
throw new Error(engineMsg('adapter.pluginFilesCollectDirInvalid', { dir: collectDir }));
|
|
30
31
|
}
|
|
31
32
|
this.collectDir = collectDir === '' ? undefined : collectDir;
|
|
32
33
|
}
|
|
@@ -55,7 +56,7 @@ export class PluginFilesAdapter {
|
|
|
55
56
|
}
|
|
56
57
|
// 2) 约定配置目录递归收集(相对 ~/.dsh 根的完整路径;与白名单文件去重)
|
|
57
58
|
// issue #37:与 skills 等同一条遍历(跟随 junction/符号链接 + 跳过留痕)
|
|
58
|
-
let listing = { paths: [], skippedLinks: [], followedLinks: 0, unreadableDirs: [] };
|
|
59
|
+
let listing = { paths: [], skippedLinks: [], skippedDepDirs: [], followedLinks: 0, unreadableDirs: [] };
|
|
59
60
|
if (this.collectDir !== undefined) {
|
|
60
61
|
try {
|
|
61
62
|
listing = await listFilesDetailed(ctx.fs, this.collectDir);
|