dsh-config-manager 0.1.71 → 0.1.72
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +111 -96
- package/README.zh-CN.md +207 -86
- package/lib/adapters/agent-instructions.js +1 -1
- package/lib/adapters/file-collection.js +1 -1
- package/lib/adapters/index.d.ts +1 -1
- package/lib/adapters/index.js +1 -1
- package/lib/adapters/link-report.d.ts +1 -0
- package/lib/adapters/link-report.js +27 -3
- package/lib/adapters/messages.d.ts +18 -0
- package/lib/adapters/messages.js +50 -0
- package/lib/adapters/plugin-files.js +3 -2
- package/lib/adapters/plugins.js +4 -3
- package/lib/adapters/prompts.d.ts +1 -0
- package/lib/adapters/prompts.js +7 -1
- package/lib/adapters/self.d.ts +7 -1
- package/lib/adapters/self.js +1 -1
- package/lib/adapters/sessions.d.ts +1 -0
- package/lib/adapters/sessions.js +4 -2
- package/lib/adapters/ui.d.ts +2 -2
- package/lib/adapters/ui.js +25 -25
- package/lib/adapters/workspaces.d.ts +1 -0
- package/lib/adapters/workspaces.js +7 -3
- package/lib/cli/actions.d.ts +9 -1
- package/lib/cli/actions.js +5 -2
- package/lib/cli/help.js +23 -10
- package/lib/cli/index.d.ts +23 -0
- package/lib/cli/index.js +90 -32
- package/lib/cli/sessions-inspect.js +3 -1
- package/lib/cli/sessions-repair.js +25 -6
- package/lib/cli/web/page.d.ts +12 -0
- package/lib/cli/web/page.js +21 -12
- package/lib/cli/web/routes.js +2 -2
- package/lib/cli/web/server.js +4 -4
- package/lib/client.d.ts +1349 -490
- package/lib/client.js +43047 -36738
- package/lib/core/backup-plan.js +30 -17
- package/lib/core/backup-verify.js +24 -23
- package/lib/core/backup.d.ts +5 -0
- package/lib/core/backup.js +51 -25
- package/lib/core/boot-rescue.js +4 -3
- package/lib/core/boot-safety.js +2 -1
- package/lib/core/command-rollback.d.ts +178 -0
- package/lib/core/command-rollback.js +289 -0
- package/lib/core/consult-source.js +7 -6
- package/lib/core/disk-usage.d.ts +74 -10
- package/lib/core/disk-usage.js +67 -4
- package/lib/core/engine-locale.d.ts +87 -0
- package/lib/core/engine-locale.js +125 -0
- package/lib/core/exporter.js +3 -3
- package/lib/core/fork-reconcile.d.ts +128 -0
- package/lib/core/fork-reconcile.js +150 -0
- package/lib/core/journal.js +6 -5
- package/lib/core/local-plugin-host.js +3 -2
- package/lib/core/local-plugin-pack.js +11 -6
- package/lib/core/messages-core.d.ts +292 -0
- package/lib/core/messages-core.js +615 -0
- package/lib/core/messages.d.ts +60 -2
- package/lib/core/messages.js +116 -13
- package/lib/core/migration-consult.js +21 -20
- package/lib/core/migration-history.js +19 -5
- package/lib/core/model-tools.d.ts +0 -22
- package/lib/core/model-tools.js +50 -27
- package/lib/core/phase3-host.js +30 -5
- package/lib/core/plugin-cli.js +18 -19
- package/lib/core/reconcile.d.ts +2 -14
- package/lib/core/reconcile.js +87 -17
- package/lib/core/recovery-orchestrator.js +19 -18
- package/lib/core/reinstall.d.ts +4 -0
- package/lib/core/reinstall.js +26 -21
- package/lib/core/restore.js +20 -17
- package/lib/core/rollback-ignore-rules.d.ts +31 -0
- package/lib/core/rollback-ignore-rules.js +140 -0
- package/lib/core/rollback-point.d.ts +1208 -0
- package/lib/core/rollback-point.js +2993 -0
- package/lib/core/run-registry.js +6 -5
- package/lib/core/session-health.d.ts +15 -0
- package/lib/core/session-meta.d.ts +42 -2
- package/lib/core/session-meta.js +140 -1
- package/lib/core/session-title.d.ts +28 -0
- package/lib/core/session-title.js +185 -0
- package/lib/core/startup-barrier.d.ts +24 -0
- package/lib/core/startup-barrier.js +22 -0
- package/lib/core/transaction-coordinator.js +4 -3
- package/lib/core/types.d.ts +18 -1
- package/lib/core/validator.js +5 -4
- package/lib/core/verify-recovery.js +31 -30
- package/lib/foreign/bundle.js +3 -2
- package/lib/foreign/continue.js +2 -1
- package/lib/foreign/crush.js +2 -1
- package/lib/foreign/gemini.js +2 -1
- package/lib/foreign/goose.js +2 -1
- package/lib/foreign/kilocode.js +2 -1
- package/lib/foreign/kimi.js +2 -1
- package/lib/foreign/messages.d.ts +15 -0
- package/lib/foreign/messages.js +40 -0
- package/lib/foreign/mimocode.js +2 -1
- package/lib/foreign/openclaw.js +2 -1
- package/lib/foreign/opencode.js +2 -1
- package/lib/foreign/pi.js +2 -1
- package/lib/foreign/qoder.js +2 -1
- package/lib/foreign/qwen.js +2 -1
- package/lib/foreign/registry.js +7 -6
- package/lib/foreign/teleagent.js +2 -1
- package/lib/foreign/workbuddy.js +2 -1
- package/lib/foreign/zcode.js +2 -1
- package/lib/foreign/zed.js +2 -1
- package/lib/index.d.ts +223 -7
- package/lib/index.js +729 -54
- package/lib/market/git-file-writer.js +20 -19
- package/lib/market/github-repos.js +35 -19
- package/lib/market/index-parser.js +31 -30
- package/lib/market/messages-market.d.ts +138 -0
- package/lib/market/messages-market.js +305 -0
- package/lib/market/my-repo.d.ts +2 -2
- package/lib/market/my-repo.js +32 -29
- package/lib/market/prepare.js +18 -15
- package/lib/market/security.js +20 -19
- package/lib/market/types.js +2 -1
- package/lib/market/url.js +2 -1
- package/lib/migrations/index.js +6 -5
- package/lib/migrations/messages-migrations.d.ts +32 -0
- package/lib/migrations/messages-migrations.js +58 -0
- package/lib/profiles/dsh-profile-launcher.js +2 -1
- package/lib/profiles/dsh-profile-runtime.js +2 -1
- package/lib/profiles/messages.d.ts +7 -0
- package/lib/profiles/messages.js +27 -0
- package/lib/routes/backup.js +18 -2
- package/lib/routes/import.js +3 -3
- package/lib/routes/index.d.ts +1 -1
- package/lib/routes/kit.d.ts +3 -2
- package/lib/routes/kit.js +6 -1
- package/lib/routes/market.js +1 -1
- package/lib/routes/messages.d.ts +42 -0
- package/lib/routes/messages.js +100 -0
- package/lib/routes/profiles.js +12 -6
- package/lib/routes/recovery.js +378 -114
- package/lib/routes/snapshots.js +51 -4
- package/lib/routes/sync.js +68 -16
- package/lib/schema/config.d.ts +11 -1
- package/lib/schema/config.js +52 -20
- package/lib/schema/index.d.ts +8 -6
- package/lib/schema/index.js +8 -6
- package/lib/schema/manifest.js +19 -18
- package/lib/schema/messages.d.ts +47 -0
- package/lib/schema/messages.js +110 -0
- package/lib/schema/section-registry.d.ts +41 -14
- package/lib/schema/section-registry.js +35 -29
- package/lib/schema/versions.js +6 -5
- package/lib/security/encryption.js +16 -15
- package/lib/security/integrity.js +8 -7
- package/lib/security/messages.d.ts +30 -0
- package/lib/security/messages.js +77 -0
- package/lib/security/redaction.js +12 -0
- package/lib/security/secret-scanner.js +16 -13
- package/lib/security/vault.js +2 -1
- package/lib/security/zip-security.js +4 -3
- package/lib/sync/ancestor.js +2 -1
- package/lib/sync/autosync-config.d.ts +11 -0
- package/lib/sync/autosync-config.js +12 -0
- package/lib/sync/autosync-scheduler.d.ts +11 -1
- package/lib/sync/autosync-scheduler.js +115 -76
- package/lib/sync/backup-files.js +3 -2
- package/lib/sync/backup-scheduler.js +6 -5
- package/lib/sync/blob-store.js +2 -1
- package/lib/sync/git/git-transport.d.ts +55 -0
- package/lib/sync/git/git-transport.js +152 -2
- package/lib/sync/github-auth.js +10 -9
- package/lib/sync/layout.js +13 -12
- package/lib/sync/messages.d.ts +63 -0
- package/lib/sync/messages.js +149 -0
- package/lib/sync/review-queue.js +6 -5
- package/lib/sync/s3/messages.d.ts +7 -0
- package/lib/sync/s3/messages.js +16 -0
- package/lib/sync/s3/s3-providers.d.ts +4 -1
- package/lib/sync/s3/s3-providers.js +5 -5
- package/lib/sync/s3/s3-transport.js +1 -1
- package/lib/sync/s3/sigv4.js +2 -1
- package/lib/sync/snapshot-crypto.js +6 -5
- package/lib/sync/snapshot-json.js +3 -2
- package/lib/sync/sync-engine.d.ts +37 -15
- package/lib/sync/sync-engine.js +67 -37
- package/lib/sync/sync-history.js +7 -6
- package/lib/sync/sync-selection.d.ts +15 -4
- package/lib/sync/sync-selection.js +17 -5
- package/lib/sync/webdav/webdav-transport.d.ts +1 -0
- package/lib/sync/webdav/webdav-transport.js +4 -1
- package/lib/ui/artifact-view.js +4 -3
- package/lib/ui/commands.d.ts +1 -1
- package/lib/ui/commands.js +10 -0
- package/lib/ui/disk-usage-view.d.ts +33 -4
- package/lib/ui/disk-usage-view.js +29 -1
- package/lib/ui/errors.d.ts +1 -1
- package/lib/ui/errors.js +4 -3
- package/lib/ui/export-flow.js +24 -20
- package/lib/ui/foreign-view.d.ts +5 -1
- package/lib/ui/foreign-view.js +26 -2
- package/lib/ui/i18n.d.ts +65 -0
- package/lib/ui/i18n.js +114 -0
- package/lib/ui/import-wizard.d.ts +7 -0
- package/lib/ui/import-wizard.js +13 -10
- package/lib/ui/progress.d.ts +2 -2
- package/lib/ui/progress.js +1 -2
- package/lib/ui/report.d.ts +5 -5
- package/lib/ui/report.js +6 -6
- package/lib/ui/rollback-view.d.ts +756 -0
- package/lib/ui/rollback-view.js +724 -0
- package/lib/ui/selection-model.d.ts +41 -0
- package/lib/ui/selection-model.js +30 -0
- package/lib/ui/session-inventory-view.d.ts +97 -3
- package/lib/ui/session-inventory-view.js +76 -2
- package/lib/ui/session-issue-labels.d.ts +43 -0
- package/lib/ui/session-issue-labels.js +48 -0
- package/lib/ui/sync-settings-view.d.ts +33 -0
- package/lib/ui/sync-settings-view.js +40 -0
- package/lib/ui/types.d.ts +8 -3
- package/lib/ui/types.js +1 -1
- package/lib/utils/atomic-write.js +6 -5
- package/lib/utils/bundle-scan.d.ts +11 -1
- package/lib/utils/bundle-scan.js +12 -1
- package/lib/utils/env-lock.d.ts +10 -0
- package/lib/utils/env-lock.js +73 -46
- package/lib/utils/json.js +6 -5
- package/lib/utils/messages-utils.d.ts +96 -0
- package/lib/utils/messages-utils.js +216 -0
- package/lib/utils/proxy.js +18 -5
- package/lib/utils/recursive-walk.d.ts +22 -0
- package/lib/utils/recursive-walk.js +75 -1
- package/lib/utils/session-log-repair.js +10 -9
- package/lib/utils/zip.js +27 -26
- package/package.json +12 -2
- package/src/adapters/agent-instructions.ts +1 -1
- package/src/adapters/file-collection.test.ts +1 -0
- package/src/adapters/file-collection.ts +1 -1
- package/src/adapters/files.test.ts +27 -0
- package/src/adapters/index.ts +1 -1
- package/src/adapters/link-report.test.ts +22 -2
- package/src/adapters/link-report.ts +29 -4
- package/src/adapters/messages.ts +54 -0
- package/src/adapters/plugin-files.ts +3 -2
- package/src/adapters/plugins.ts +4 -3
- package/src/adapters/prompts.ts +7 -1
- package/src/adapters/self.test.ts +26 -0
- package/src/adapters/self.ts +8 -2
- package/src/adapters/sessions.ts +4 -2
- package/src/adapters/settings.test.ts +38 -1
- package/src/adapters/ui.test.ts +2 -2
- package/src/adapters/ui.ts +27 -26
- package/src/adapters/workspaces.ts +7 -3
- package/src/cli/actions.test.ts +28 -0
- package/src/cli/actions.ts +11 -2
- package/src/cli/help.ts +23 -10
- package/src/cli/index-guards.test.ts +193 -0
- package/src/cli/index.ts +106 -31
- package/src/cli/sessions-inspect.test.ts +15 -1
- package/src/cli/sessions-inspect.ts +3 -1
- package/src/cli/sessions-repair.test.ts +84 -0
- package/src/cli/sessions-repair.ts +33 -7
- package/src/cli/web/console-route-parity.test.ts +115 -0
- package/src/cli/web/page.ts +23 -12
- package/src/cli/web/routes.ts +2 -1
- package/src/cli/web/server.ts +4 -4
- package/src/cli/web/web.test.ts +2 -2
- package/src/client/ConfigManagerSection.tsx +30 -2
- package/src/client/about/AboutPanel.tsx +2 -2
- package/src/client/about/PluginUpdateDialog.tsx +1 -1
- package/src/client/about/ReleaseNotesDialog.tsx +2 -2
- package/src/client/about/about-view.test.ts +13 -3
- package/src/client/about/about-view.ts +10 -6
- package/src/client/about/release-notes-view.test.ts +14 -4
- package/src/client/about/release-notes-view.ts +4 -2
- package/src/client/api.ts +18 -6
- package/src/client/chat/ChatEntryView.tsx +391 -0
- package/src/client/chat/RollbackDiffTab.tsx +61 -0
- package/src/client/chat/RollbackFileIcon.tsx +96 -0
- package/src/client/chat/RollbackReviewDialog.tsx +591 -0
- package/src/client/chat/chat-entry-view.test.ts +1174 -0
- package/src/client/chat/chat-entry-view.ts +758 -0
- package/src/client/chat/chat-locales.ts +152 -0
- package/src/client/chat/chat-node-registration.ts +64 -0
- package/src/client/chat/chat-point-window.ts +173 -0
- package/src/client/chat/chat-slots.ts +107 -0
- package/src/client/chat/rollback-diff-tab.test.ts +105 -0
- package/src/client/chat/rollback-diff-tab.ts +164 -0
- package/src/client/chat/rollback-entry-view.test.ts +1469 -0
- package/src/client/chat/rollback-entry-view.ts +941 -0
- package/src/client/chat/sidebar-slots.ts +37 -0
- package/src/client/common/ConfirmDialog.tsx +7 -3
- package/src/client/common/ContentPicker.tsx +48 -10
- package/src/client/common/DiffTable.tsx +97 -0
- package/src/client/common/ErrorBanner.tsx +4 -4
- package/src/client/common/Icon.tsx +2 -0
- package/src/client/common/Modal.tsx +8 -1
- package/src/client/common/ProgressBar.tsx +8 -2
- package/src/client/common/ReportView.tsx +4 -4
- package/src/client/common/RunsCenter.tsx +1 -0
- package/src/client/common/client-route-parity.test.ts +78 -0
- package/src/client/common/info-hint-guard.test.ts +46 -13
- package/src/client/common/picker-lazy-units.test.ts +42 -0
- package/src/client/common/plan-text-redaction.test.ts +17 -6
- package/src/client/common/progress-view-edge.test.ts +8 -7
- package/src/client/common/progress-view.test.ts +9 -9
- package/src/client/common/progress-view.ts +2 -2
- package/src/client/common/routes.ts +28 -0
- package/src/client/common/ui.tsx +73 -10
- package/src/client/config-manager.module.css +996 -21
- package/src/client/css-scale-guard.test.ts +30 -0
- package/src/client/environment/EnvironmentPanel.tsx +11 -4
- package/src/client/environment/maintenance/DiskUsageCard.tsx +23 -2
- package/src/client/export/ExportView.tsx +13 -8
- package/src/client/history/HistoryPanel.tsx +4 -3
- package/src/client/history/history-api.ts +2 -2
- package/src/client/history/history-locales.ts +5 -1
- package/src/client/home/HomePanel.tsx +29 -8
- package/src/client/import/ForeignImportView.tsx +4 -5
- package/src/client/import/ImportWizardView.tsx +5 -3
- package/src/client/import/import-wizard-split.test.ts +3 -1
- package/src/client/import/import-wizard-steps.tsx +9 -9
- package/src/client/index.ts +186 -3
- package/src/client/library/LibraryActions.tsx +32 -7
- package/src/client/library/LibraryPanel.tsx +23 -3
- package/src/client/library/RestorePlanView.tsx +9 -66
- package/src/client/locales.ts +54 -5
- package/src/client/lucide-icons.d.ts +1 -0
- package/src/client/market/MarketPanel.tsx +13 -2
- package/src/client/market/MyConfigsView.tsx +5 -4
- package/src/client/market/MyConfigsWizard.tsx +10 -8
- package/src/client/market/market-api.test.ts +9 -8
- package/src/client/market/market-api.ts +2 -2
- package/src/client/market/market-locales.ts +3 -0
- package/src/client/market/market-view.test.ts +6 -6
- package/src/client/market/market-view.ts +3 -3
- package/src/client/market/my-configs-api.ts +2 -2
- package/src/client/market/my-configs-view.test.ts +23 -22
- package/src/client/market/my-configs-view.ts +8 -8
- package/src/client/recovery/IncompleteCopiesSection.tsx +2 -1
- package/src/client/recovery/RecoveryPanel.tsx +442 -283
- package/src/client/recovery/RollbackCard.tsx +665 -0
- package/src/client/recovery/RollbackDiffPane.tsx +58 -0
- package/src/client/recovery/RollbackFileChanges.tsx +163 -0
- package/src/client/recovery/incident-api.ts +2 -2
- package/src/client/recovery/recovery-api.ts +116 -6
- package/src/client/recovery/recovery-locales.test.ts +299 -0
- package/src/client/recovery/recovery-locales.ts +488 -149
- package/src/client/recovery/recovery-panel-layout.test.ts +132 -2
- package/src/client/recovery/rollback-file-changes.test.ts +73 -0
- package/src/client/recovery/session-dialog-mount.test.ts +54 -0
- package/src/client/run-store.ts +84 -21
- package/src/client/sync/AutosyncCard.tsx +25 -1
- package/src/client/sync/ChannelClearConfirmDialog.tsx +6 -3
- package/src/client/sync/ChannelConfigDialog.tsx +10 -10
- package/src/client/sync/DecryptPasswordCard.tsx +2 -2
- package/src/client/sync/SecurityOptionsCard.tsx +27 -17
- package/src/client/sync/SyncChannelEntryCard.tsx +14 -2
- package/src/client/sync/SyncConfirmView.tsx +46 -8
- package/src/client/sync/SyncLogList.tsx +1 -1
- package/src/client/sync/SyncPanel.tsx +31 -23
- package/src/client/sync/SyncRepositoryPicker.tsx +3 -5
- package/src/client/sync/SyncSectionPickerDialog.tsx +11 -2
- package/src/client/sync/SyncSettingsView.test.ts +20 -1
- package/src/client/sync/UnreadableSnapshotsBanner.tsx +1 -1
- package/src/client/sync/WorkDirCard.tsx +112 -0
- package/src/client/sync/history-model.test.ts +42 -9
- package/src/client/sync/history-model.ts +30 -15
- package/src/client/sync/sync-api.test.ts +57 -33
- package/src/client/sync/sync-api.ts +80 -6
- package/src/client/sync/sync-locales.ts +104 -3
- package/src/client/sync/sync-view-v2.test.ts +65 -13
- package/src/client/sync/sync-view.test.ts +107 -52
- package/src/client/sync/sync-view.ts +124 -41
- package/src/core/backup-plan.test.ts +28 -0
- package/src/core/backup-plan.ts +29 -17
- package/src/core/backup-verify.ts +24 -23
- package/src/core/backup.test.ts +76 -0
- package/src/core/backup.ts +55 -25
- package/src/core/boot-rescue.ts +4 -3
- package/src/core/boot-safety.ts +2 -1
- package/src/core/command-rollback.test.ts +536 -0
- package/src/core/command-rollback.ts +402 -0
- package/src/core/consult-source.ts +7 -6
- package/src/core/disk-usage.test.ts +257 -1
- package/src/core/disk-usage.ts +134 -14
- package/src/core/engine-locale.test.ts +593 -0
- package/src/core/engine-locale.ts +175 -0
- package/src/core/exporter.ts +3 -3
- package/src/core/fork-reconcile.test.ts +279 -0
- package/src/core/fork-reconcile.ts +234 -0
- package/src/core/journal.ts +6 -5
- package/src/core/local-plugin-host.ts +3 -2
- package/src/core/local-plugin-pack.ts +11 -6
- package/src/core/messages-core.ts +657 -0
- package/src/core/messages.ts +123 -17
- package/src/core/migration-consult.ts +21 -20
- package/src/core/migration-history.ts +19 -5
- package/src/core/model-tools.ts +28 -27
- package/src/core/phase3-host.ts +21 -5
- package/src/core/phase3-p2b-fingerprint.test.ts +22 -0
- package/src/core/plugin-cli.ts +18 -19
- package/src/core/reconcile.test.ts +117 -0
- package/src/core/reconcile.ts +85 -17
- package/src/core/recovery-orchestrator.ts +19 -18
- package/src/core/reinstall.ts +26 -21
- package/src/core/restore.ts +20 -17
- package/src/core/rollback-file-diff.test.ts +159 -0
- package/src/core/rollback-ignore-rules.ts +126 -0
- package/src/core/rollback-point.test.ts +2603 -0
- package/src/core/rollback-point.ts +3755 -0
- package/src/core/run-registry.ts +6 -5
- package/src/core/session-health.ts +15 -0
- package/src/core/session-meta.test.ts +117 -1
- package/src/core/session-meta.ts +149 -2
- package/src/core/session-title.test.ts +127 -0
- package/src/core/session-title.ts +189 -0
- package/src/core/startup-barrier.test.ts +69 -0
- package/src/core/startup-barrier.ts +27 -0
- package/src/core/transaction-coordinator.ts +4 -3
- package/src/core/types.ts +18 -1
- package/src/core/validator.ts +5 -4
- package/src/core/verify-recovery.ts +31 -30
- package/src/foreign/bundle.ts +3 -2
- package/src/foreign/continue.ts +2 -1
- package/src/foreign/crush.ts +2 -1
- package/src/foreign/file-budget.test.ts +2 -0
- package/src/foreign/gemini.ts +2 -1
- package/src/foreign/goose.ts +2 -1
- package/src/foreign/kilocode.ts +2 -1
- package/src/foreign/kimi.ts +2 -1
- package/src/foreign/messages.ts +44 -0
- package/src/foreign/mimocode.ts +2 -1
- package/src/foreign/openclaw.ts +2 -1
- package/src/foreign/opencode.ts +2 -1
- package/src/foreign/pi.ts +2 -1
- package/src/foreign/qoder.ts +2 -1
- package/src/foreign/qwen.ts +2 -1
- package/src/foreign/registry.ts +7 -6
- package/src/foreign/teleagent.ts +2 -1
- package/src/foreign/workbuddy.ts +2 -1
- package/src/foreign/zcode.ts +2 -1
- package/src/foreign/zed.ts +2 -1
- package/src/index.facade.test.ts +340 -1
- package/src/index.ts +813 -57
- package/src/market/git-file-writer.ts +20 -19
- package/src/market/github-repos.test.ts +23 -0
- package/src/market/github-repos.ts +37 -19
- package/src/market/index-parser.ts +31 -30
- package/src/market/messages-market.ts +325 -0
- package/src/market/my-repo.ts +32 -29
- package/src/market/prepare.ts +18 -15
- package/src/market/security.ts +20 -19
- package/src/market/types.ts +2 -1
- package/src/market/url.ts +2 -1
- package/src/migrations/index.ts +6 -5
- package/src/migrations/messages-migrations.ts +59 -0
- package/src/profiles/dsh-profile-launcher.ts +2 -1
- package/src/profiles/dsh-profile-runtime.ts +2 -1
- package/src/profiles/messages.ts +31 -0
- package/src/routes/backup.ts +18 -2
- package/src/routes/import.ts +3 -3
- package/src/routes/index.ts +1 -1
- package/src/routes/kit.ts +9 -3
- package/src/routes/market.ts +1 -1
- package/src/routes/messages.ts +104 -0
- package/src/routes/profiles.ts +12 -6
- package/src/routes/recovery.test.ts +405 -1
- package/src/routes/recovery.ts +377 -109
- package/src/routes/snapshots.ts +51 -4
- package/src/routes/sync.ts +67 -16
- package/src/schema/config.ts +53 -20
- package/src/schema/index.ts +7 -5
- package/src/schema/manifest.ts +19 -18
- package/src/schema/messages.ts +120 -0
- package/src/schema/registry.test.ts +19 -5
- package/src/schema/section-registry.ts +64 -39
- package/src/schema/versions.ts +6 -5
- package/src/security/encryption.ts +16 -15
- package/src/security/integrity.ts +8 -7
- package/src/security/messages.ts +87 -0
- package/src/security/redaction.test.ts +17 -1
- package/src/security/redaction.ts +13 -0
- package/src/security/secret-scanner.ts +19 -12
- package/src/security/security.test.ts +47 -0
- package/src/security/vault.ts +2 -1
- package/src/security/zip-security.ts +4 -3
- package/src/sync/ancestor.ts +2 -1
- package/src/sync/autosync-config.ts +21 -0
- package/src/sync/autosync-scheduler.test.ts +63 -7
- package/src/sync/autosync-scheduler.ts +118 -78
- package/src/sync/backup-files.ts +3 -2
- package/src/sync/backup-scheduler.ts +6 -5
- package/src/sync/blob-store.ts +2 -1
- package/src/sync/git/git-transport.test.ts +170 -8
- package/src/sync/git/git-transport.ts +176 -2
- package/src/sync/github-auth.ts +10 -9
- package/src/sync/layout.ts +13 -12
- package/src/sync/messages.ts +153 -0
- package/src/sync/review-queue.ts +6 -5
- package/src/sync/s3/messages.ts +16 -0
- package/src/sync/s3/s3-providers.ts +9 -6
- package/src/sync/s3/s3-transport.ts +1 -1
- package/src/sync/s3/sigv4.ts +3 -1
- package/src/sync/snapshot-crypto.ts +6 -5
- package/src/sync/snapshot-json.ts +3 -2
- package/src/sync/sync-engine-download-tombstone.test.ts +1 -1
- package/src/sync/sync-engine.test.ts +226 -8
- package/src/sync/sync-engine.ts +103 -41
- package/src/sync/sync-history.ts +7 -6
- package/src/sync/sync-selection.test.ts +12 -2
- package/src/sync/sync-selection.ts +18 -5
- package/src/sync/webdav/webdav-transport.ts +4 -1
- package/src/ui/artifact-view.ts +4 -3
- package/src/ui/commands.test.ts +1 -0
- package/src/ui/commands.ts +11 -1
- package/src/ui/disk-usage-view.test.ts +113 -6
- package/src/ui/disk-usage-view.ts +63 -7
- package/src/ui/errors.ts +4 -3
- package/src/ui/export-flow.ts +24 -20
- package/src/ui/foreign-view.test.ts +43 -0
- package/src/ui/foreign-view.ts +30 -3
- package/src/ui/i18n.ts +125 -0
- package/src/ui/import-wizard.ts +18 -10
- package/src/ui/progress.ts +2 -2
- package/src/ui/report.ts +7 -6
- package/src/ui/rollback-view.test.ts +819 -0
- package/src/ui/rollback-view.ts +1339 -0
- package/src/ui/selection-model.test.ts +33 -1
- package/src/ui/selection-model.ts +53 -0
- package/src/ui/session-inventory-view.test.ts +109 -0
- package/src/ui/session-inventory-view.ts +138 -3
- package/src/ui/session-issue-labels.test.ts +51 -0
- package/src/ui/session-issue-labels.ts +70 -0
- package/src/ui/sync-settings-view.test.ts +36 -0
- package/src/ui/sync-settings-view.ts +49 -0
- package/src/ui/types.ts +10 -4
- package/src/utils/atomic-write.ts +6 -5
- package/src/utils/bundle-scan.test.ts +2 -2
- package/src/utils/bundle-scan.ts +12 -1
- package/src/utils/env-lock.ts +83 -49
- package/src/utils/json.ts +7 -5
- package/src/utils/messages-utils.ts +232 -0
- package/src/utils/proxy.test.ts +25 -0
- package/src/utils/proxy.ts +19 -5
- package/src/utils/recursive-walk.test.ts +43 -0
- package/src/utils/recursive-walk.ts +75 -1
- package/src/utils/session-log-repair.ts +10 -9
- package/src/utils/zip.ts +27 -26
- package/lib/core/checkpoint.d.ts +0 -362
- package/lib/core/checkpoint.js +0 -1309
- package/src/core/checkpoint.test.ts +0 -665
- package/src/core/checkpoint.ts +0 -1614
package/lib/utils/zip.js
CHANGED
|
@@ -17,6 +17,7 @@ import path from 'node:path';
|
|
|
17
17
|
import zlib from 'node:zlib';
|
|
18
18
|
import { parseJsonSafe } from './json.js';
|
|
19
19
|
import { isPathSafe, isSameOrChild } from './paths.js';
|
|
20
|
+
import { utilsMsg } from './messages-utils.js';
|
|
20
21
|
export const DEFAULT_ZIP_SAFETY_LIMITS = {
|
|
21
22
|
maxEntries: 10_000,
|
|
22
23
|
maxTotalBytes: 500 * 1024 * 1024,
|
|
@@ -78,7 +79,7 @@ export function assertZipEntryCount(count, limits = {}) {
|
|
|
78
79
|
const readSideMax = DEFAULT_ZIP_SAFETY_LIMITS.maxEntries;
|
|
79
80
|
const max = Math.min(limits.maxEntries ?? readSideMax, readSideMax, ZIP_MAX_WRITABLE_ENTRIES);
|
|
80
81
|
if (count > max) {
|
|
81
|
-
throw new ZipSafetyError(
|
|
82
|
+
throw new ZipSafetyError(utilsMsg('utils.zip.entryCountWriteExceeded', { count, max }));
|
|
82
83
|
}
|
|
83
84
|
}
|
|
84
85
|
/* ---------------- 写出侧布局片段(zipToBuffer 与 writeZip 共用,避免两份布局漂移) ----------------
|
|
@@ -171,7 +172,7 @@ export function zipToBuffer(entries, limits = {}) {
|
|
|
171
172
|
const { time, date } = dosDateTime();
|
|
172
173
|
for (const entry of entries) {
|
|
173
174
|
if (!isPathSafe(entry.name)) {
|
|
174
|
-
throw new ZipSafetyError(
|
|
175
|
+
throw new ZipSafetyError(utilsMsg('utils.zip.unsafeEntryNameWrite', { name: entry.name }));
|
|
175
176
|
}
|
|
176
177
|
const nameBuf = Buffer.from(entry.name, 'utf8');
|
|
177
178
|
const data = Buffer.isBuffer(entry.data) ? entry.data : Buffer.from(entry.data);
|
|
@@ -206,7 +207,7 @@ export async function writeZip(zipPath, entries, limits = {}) {
|
|
|
206
207
|
// 先全量校验条目名:不安全的名字在**落任何字节之前**拒绝(与 zipToBuffer 的拒绝顺序一致)
|
|
207
208
|
const prepared = entries.map((entry) => {
|
|
208
209
|
if (!isPathSafe(entry.name))
|
|
209
|
-
throw new ZipSafetyError(
|
|
210
|
+
throw new ZipSafetyError(utilsMsg('utils.zip.unsafeEntryNameWrite', { name: entry.name }));
|
|
210
211
|
return {
|
|
211
212
|
nameBuf: Buffer.from(entry.name, 'utf8'),
|
|
212
213
|
data: Buffer.isBuffer(entry.data) ? entry.data : Buffer.from(entry.data),
|
|
@@ -342,11 +343,11 @@ export class ZipArchive {
|
|
|
342
343
|
out = zlib.inflateRawSync(raw, { maxOutputLength: this.limits.maxSingleBytes });
|
|
343
344
|
}
|
|
344
345
|
catch (err) {
|
|
345
|
-
throw new ZipSafetyError(
|
|
346
|
+
throw new ZipSafetyError(utilsMsg('utils.zip.entryInflateFailed', { name, reason: err instanceof Error ? err.message : String(err) }));
|
|
346
347
|
}
|
|
347
348
|
}
|
|
348
349
|
else {
|
|
349
|
-
throw new ZipSafetyError(
|
|
350
|
+
throw new ZipSafetyError(utilsMsg('utils.zip.unknownCompression', { name, method: meta.method }));
|
|
350
351
|
}
|
|
351
352
|
return this.accept(name, meta, out);
|
|
352
353
|
}
|
|
@@ -372,11 +373,11 @@ export class ZipArchive {
|
|
|
372
373
|
out = await inflateRawAsync(raw, this.limits.maxSingleBytes);
|
|
373
374
|
}
|
|
374
375
|
catch (err) {
|
|
375
|
-
throw new ZipSafetyError(
|
|
376
|
+
throw new ZipSafetyError(utilsMsg('utils.zip.entryInflateFailed', { name, reason: err instanceof Error ? err.message : String(err) }));
|
|
376
377
|
}
|
|
377
378
|
}
|
|
378
379
|
else {
|
|
379
|
-
throw new ZipSafetyError(
|
|
380
|
+
throw new ZipSafetyError(utilsMsg('utils.zip.unknownCompression', { name, method: meta.method }));
|
|
380
381
|
}
|
|
381
382
|
return this.accept(name, meta, out);
|
|
382
383
|
}
|
|
@@ -384,7 +385,7 @@ export class ZipArchive {
|
|
|
384
385
|
metaOf(name) {
|
|
385
386
|
const meta = this.metas.find((m) => m.name === name);
|
|
386
387
|
if (!meta)
|
|
387
|
-
throw new ZipSafetyError(
|
|
388
|
+
throw new ZipSafetyError(utilsMsg('utils.zip.entryMissing', { name }));
|
|
388
389
|
return meta;
|
|
389
390
|
}
|
|
390
391
|
/** 本地文件头校验 + 定位压缩字节区间(越界 → 抛错)。 */
|
|
@@ -392,32 +393,32 @@ export class ZipArchive {
|
|
|
392
393
|
const lhOffset = meta.localOffset;
|
|
393
394
|
const lh = this.buf.subarray(lhOffset, lhOffset + 30);
|
|
394
395
|
if (lh.length < 30 || lh.readUInt32LE(0) !== LOCAL_SIG) {
|
|
395
|
-
throw new ZipSafetyError(
|
|
396
|
+
throw new ZipSafetyError(utilsMsg('utils.zip.localHeaderCorrupt', { name }));
|
|
396
397
|
}
|
|
397
398
|
const nameLen = lh.readUInt16LE(26);
|
|
398
399
|
const extraLen = lh.readUInt16LE(28);
|
|
399
400
|
const dataStart = lhOffset + 30 + nameLen + extraLen;
|
|
400
401
|
if (dataStart + meta.compressedSize > this.buf.length) {
|
|
401
|
-
throw new ZipSafetyError(
|
|
402
|
+
throw new ZipSafetyError(utilsMsg('utils.zip.dataOutOfRange', { name }));
|
|
402
403
|
}
|
|
403
404
|
return this.buf.subarray(dataStart, dataStart + meta.compressedSize);
|
|
404
405
|
}
|
|
405
406
|
/** 解压后的验收:尺寸 → CRC32 → 解压总量预算 → 压缩比(zip bomb)。两处解压路径共用同一套判据。 */
|
|
406
407
|
accept(name, meta, out) {
|
|
407
408
|
if (out.length !== meta.uncompressedSize) {
|
|
408
|
-
throw new ZipSafetyError(
|
|
409
|
+
throw new ZipSafetyError(utilsMsg('utils.zip.sizeMismatch', { name, actual: out.length, expected: meta.uncompressedSize }));
|
|
409
410
|
}
|
|
410
411
|
if (crc32(out) !== meta.crc32) {
|
|
411
|
-
throw new ZipSafetyError(
|
|
412
|
+
throw new ZipSafetyError(utilsMsg('utils.zip.crcMismatch', { name }));
|
|
412
413
|
}
|
|
413
414
|
this.totalUncompressed += out.length;
|
|
414
415
|
if (this.totalUncompressed > this.limits.maxTotalBytes) {
|
|
415
|
-
throw new ZipSafetyError('
|
|
416
|
+
throw new ZipSafetyError(utilsMsg('utils.zip.totalUncompressedExceeded'));
|
|
416
417
|
}
|
|
417
418
|
if (meta.compressedSize > 0) {
|
|
418
419
|
const ratio = out.length / meta.compressedSize;
|
|
419
420
|
if (ratio > this.limits.maxRatio) {
|
|
420
|
-
throw new ZipSafetyError(
|
|
421
|
+
throw new ZipSafetyError(utilsMsg('utils.zip.ratioExceeded', { name, ratio: ratio.toFixed(1) }));
|
|
421
422
|
}
|
|
422
423
|
}
|
|
423
424
|
return out;
|
|
@@ -440,19 +441,19 @@ export function parseZip(buf, limits = {}) {
|
|
|
440
441
|
const merged = { ...DEFAULT_ZIP_SAFETY_LIMITS, ...limits };
|
|
441
442
|
const b = Buffer.isBuffer(buf) ? buf : Buffer.from(buf);
|
|
442
443
|
if (b.length < 22)
|
|
443
|
-
throw new ZipSafetyError('
|
|
444
|
+
throw new ZipSafetyError(utilsMsg('utils.zip.tooSmall'));
|
|
444
445
|
const eocdIdx = b.lastIndexOf(EOCD_SIG);
|
|
445
446
|
if (eocdIdx < 0 || eocdIdx + 22 > b.length) {
|
|
446
|
-
throw new ZipSafetyError('
|
|
447
|
+
throw new ZipSafetyError(utilsMsg('utils.zip.missingEocd'));
|
|
447
448
|
}
|
|
448
449
|
const totalEntries = b.readUInt16LE(eocdIdx + 10);
|
|
449
450
|
const cdSize = b.readUInt32LE(eocdIdx + 12);
|
|
450
451
|
const cdOffset = b.readUInt32LE(eocdIdx + 16);
|
|
451
452
|
if (cdOffset + cdSize > b.length) {
|
|
452
|
-
throw new ZipSafetyError('
|
|
453
|
+
throw new ZipSafetyError(utilsMsg('utils.zip.centralDirOutOfRange'));
|
|
453
454
|
}
|
|
454
455
|
if (totalEntries > merged.maxEntries) {
|
|
455
|
-
throw new ZipSafetyError(
|
|
456
|
+
throw new ZipSafetyError(utilsMsg('utils.zip.entryCountReadExceeded', { count: totalEntries, max: merged.maxEntries }));
|
|
456
457
|
}
|
|
457
458
|
const metas = [];
|
|
458
459
|
/** 已见条目名:重复条目会让「按名读取」产生歧义(读取到哪一个取决于遍历顺序) */
|
|
@@ -461,9 +462,9 @@ export function parseZip(buf, limits = {}) {
|
|
|
461
462
|
let compressedTotal = 0;
|
|
462
463
|
for (let i = 0; i < totalEntries; i++) {
|
|
463
464
|
if (pos + 46 > cdOffset + cdSize)
|
|
464
|
-
throw new ZipSafetyError('
|
|
465
|
+
throw new ZipSafetyError(utilsMsg('utils.zip.centralEntryOutOfRange'));
|
|
465
466
|
if (b.readUInt32LE(pos) !== CENTRAL_SIG)
|
|
466
|
-
throw new ZipSafetyError('
|
|
467
|
+
throw new ZipSafetyError(utilsMsg('utils.zip.centralSignatureCorrupt'));
|
|
467
468
|
const method = b.readUInt16LE(pos + 10);
|
|
468
469
|
const crc = b.readUInt32LE(pos + 16);
|
|
469
470
|
const compSize = b.readUInt32LE(pos + 20);
|
|
@@ -475,21 +476,21 @@ export function parseZip(buf, limits = {}) {
|
|
|
475
476
|
const localOffset = b.readUInt32LE(pos + 42);
|
|
476
477
|
const name = b.subarray(pos + 46, pos + 46 + nameLen).toString('utf8');
|
|
477
478
|
if (!isPathSafe(name))
|
|
478
|
-
throw new ZipSafetyError(
|
|
479
|
+
throw new ZipSafetyError(utilsMsg('utils.zip.unsafeEntryName', { name }));
|
|
479
480
|
// —— 以下三项原属 security/zip-security.ts 的 parseZipHardened,现并入默认解析器 ——
|
|
480
481
|
// (默认值即最严:未注入 parseZipOverride 的调用方不再拿到「弱版」)
|
|
481
482
|
if (seenNames.has(name))
|
|
482
|
-
throw new ZipSafetyError(
|
|
483
|
+
throw new ZipSafetyError(utilsMsg('utils.zip.duplicateEntryName', { name }));
|
|
483
484
|
seenNames.add(name);
|
|
484
485
|
if (((externalAttrs >>> 16) & S_IFMT) === S_IFLNK) {
|
|
485
|
-
throw new ZipSafetyError(
|
|
486
|
+
throw new ZipSafetyError(utilsMsg('utils.zip.symlinkEntryRejected', { name }));
|
|
486
487
|
}
|
|
487
488
|
if (localOffset + 30 > b.length) {
|
|
488
|
-
throw new ZipSafetyError(
|
|
489
|
+
throw new ZipSafetyError(utilsMsg('utils.zip.localHeaderOutOfRange', { name }));
|
|
489
490
|
}
|
|
490
491
|
compressedTotal += compSize;
|
|
491
492
|
if (compressedTotal > merged.maxCompressedBytes) {
|
|
492
|
-
throw new ZipSafetyError('
|
|
493
|
+
throw new ZipSafetyError(utilsMsg('utils.zip.compressedTotalExceeded'));
|
|
493
494
|
}
|
|
494
495
|
metas.push({
|
|
495
496
|
name,
|
|
@@ -518,7 +519,7 @@ export async function safeExtract(zipPath, destDir, limits = {}) {
|
|
|
518
519
|
continue;
|
|
519
520
|
const target = path.join(destDir, ...meta.name.split('/'));
|
|
520
521
|
if (!isSameOrChild(target, destDir)) {
|
|
521
|
-
throw new ZipSafetyError(
|
|
522
|
+
throw new ZipSafetyError(utilsMsg('utils.zip.extractTargetOutOfRange', { name: meta.name }));
|
|
522
523
|
}
|
|
523
524
|
await fs.mkdir(path.dirname(target), { recursive: true });
|
|
524
525
|
await fs.writeFile(target, archive.readEntry(meta.name));
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-config-manager",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.72",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"description": "Backup, restore, export, import, migrate and sync your DeepSeek Harness (DSH) configuration: settings, plugins, MCP servers, skills, agent presets and workspaces. One-click migration to another machine. DSH 配置一键备份、恢复、导出、导入、迁移与同步插件。",
|
|
6
6
|
"icon": "./icon.svg",
|
|
@@ -99,8 +99,10 @@
|
|
|
99
99
|
"@deepseek-ai/dsh-client-connection": ">=0.1.0-rc.6 <0.3.0-0",
|
|
100
100
|
"@deepseek-ai/dsh-client-locale": ">=0.1.0-rc.6 <0.3.0-0",
|
|
101
101
|
"@deepseek-ai/dsh-client-runtime": ">=0.1.0-rc.6 <0.3.0-0",
|
|
102
|
+
"@deepseek-ai/dsh-client-ui-primitives": ">=0.1.0-rc.6 <0.3.0-0",
|
|
102
103
|
"@deepseek-ai/dsh-client-ui-settings": ">=0.1.0-rc.6 <0.3.0-0",
|
|
103
104
|
"@deepseek-ai/dsh-client-ui-slots": ">=0.1.0-rc.6 <0.3.0-0",
|
|
105
|
+
"@deepseek-ai/dsh-commands": ">=0.1.0-rc.6 <0.3.0-0",
|
|
104
106
|
"@deepseek-ai/dsh-credentials": ">=0.1.0-rc.6 <0.3.0-0",
|
|
105
107
|
"@deepseek-ai/dsh-home-paths": ">=0.1.0-rc.6 <0.3.0-0",
|
|
106
108
|
"@deepseek-ai/dsh-host-plugin-inventory": ">=0.1.0-rc.6 <0.3.0-0",
|
|
@@ -125,12 +127,18 @@
|
|
|
125
127
|
"@deepseek-ai/dsh-client-runtime": {
|
|
126
128
|
"optional": true
|
|
127
129
|
},
|
|
130
|
+
"@deepseek-ai/dsh-client-ui-primitives": {
|
|
131
|
+
"optional": true
|
|
132
|
+
},
|
|
128
133
|
"@deepseek-ai/dsh-client-ui-settings": {
|
|
129
134
|
"optional": true
|
|
130
135
|
},
|
|
131
136
|
"@deepseek-ai/dsh-client-ui-slots": {
|
|
132
137
|
"optional": true
|
|
133
138
|
},
|
|
139
|
+
"@deepseek-ai/dsh-commands": {
|
|
140
|
+
"optional": true
|
|
141
|
+
},
|
|
134
142
|
"@deepseek-ai/dsh-credentials": {
|
|
135
143
|
"optional": true
|
|
136
144
|
},
|
|
@@ -167,8 +175,10 @@
|
|
|
167
175
|
"@deepseek-ai/dsh-client-connection": "^0.1.0-rc.6",
|
|
168
176
|
"@deepseek-ai/dsh-client-locale": "^0.2.0-rc.2",
|
|
169
177
|
"@deepseek-ai/dsh-client-runtime": "^0.1.0-rc.6",
|
|
170
|
-
"@deepseek-ai/dsh-client-ui-
|
|
178
|
+
"@deepseek-ai/dsh-client-ui-primitives": "^0.2.0-rc.2",
|
|
179
|
+
"@deepseek-ai/dsh-client-ui-settings": "^0.2.0-rc.2",
|
|
171
180
|
"@deepseek-ai/dsh-client-ui-slots": "^0.2.0-rc.2",
|
|
181
|
+
"@deepseek-ai/dsh-commands": "^0.2.0-rc.2",
|
|
172
182
|
"@deepseek-ai/dsh-credentials": "^0.1.0-rc.6",
|
|
173
183
|
"@deepseek-ai/dsh-home-paths": "^0.1.0-rc.6",
|
|
174
184
|
"@deepseek-ai/dsh-host-plugin-inventory": "^0.1.0-rc.6",
|
|
@@ -40,7 +40,7 @@ export class AgentInstructionsAdapter extends FileCollectionAdapter {
|
|
|
40
40
|
* (单文件代价可忽略,且 `collect` 之后还要读它)。读不到 → 空清单(与「文件不存在 = 空分区」同语义)。
|
|
41
41
|
*/
|
|
42
42
|
protected override async listRelPaths(ctx: HostContext): Promise<RecursiveListing> {
|
|
43
|
-
const empty: RecursiveListing = { paths: [], skippedLinks: [], followedLinks: 0, unreadableDirs: [] };
|
|
43
|
+
const empty: RecursiveListing = { paths: [], skippedLinks: [], skippedDepDirs: [], followedLinks: 0, unreadableDirs: [] };
|
|
44
44
|
const rel = AgentInstructionsAdapter.FILE;
|
|
45
45
|
try {
|
|
46
46
|
if (ctx.fs.statSize !== undefined) {
|
|
@@ -191,7 +191,7 @@ export abstract class FileCollectionAdapter implements ConfigAdapter<FilesSectio
|
|
|
191
191
|
// issue #37:用「跟随 junction/符号链接」的遍历,并把跟随/跳过的链接写进告警——
|
|
192
192
|
// 此前链接目录及其全部内容被静默排除,备份仍报成功。
|
|
193
193
|
// 枚举走 listRelPaths 钩子(单文件分区覆写它,避免对 home 根整目录递归)。
|
|
194
|
-
let listing: RecursiveListing = { paths: [], skippedLinks: [], followedLinks: 0, unreadableDirs: [] };
|
|
194
|
+
let listing: RecursiveListing = { paths: [], skippedLinks: [], skippedDepDirs: [], followedLinks: 0, unreadableDirs: [] };
|
|
195
195
|
try {
|
|
196
196
|
listing = await this.listRelPaths(ctx);
|
|
197
197
|
} catch {
|
|
@@ -215,6 +215,7 @@ test('issue #37: skills 导出把「跟随/跳过的链接」写进 warnings(
|
|
|
215
215
|
src.fs.listRecursiveDetailed = async () => ({
|
|
216
216
|
paths: ['skills/coding.md', 'skills/shared/inner.md'],
|
|
217
217
|
skippedLinks: [{ path: 'skills/broken', reason: 'broken' }],
|
|
218
|
+
skippedDepDirs: [],
|
|
218
219
|
followedLinks: 1,
|
|
219
220
|
unreadableDirs: [],
|
|
220
221
|
});
|
|
@@ -225,6 +226,32 @@ test('issue #37: skills 导出把「跟随/跳过的链接」写进 warnings(
|
|
|
225
226
|
assert.equal(out.warnings.some((w) => w.includes('skills/broken')), true, '必须点名被跳过的链接');
|
|
226
227
|
});
|
|
227
228
|
|
|
229
|
+
test('issue #76: skills 导出跳过 node_modules 依赖目录,并在导出报告里说明(不静默)', async () => {
|
|
230
|
+
const src = makeContext('win32', 'C:\\Users\\alice');
|
|
231
|
+
await src.fs.writeFile('skills/md2pdf/SKILL.md', Buffer.from('# md2pdf\n', 'utf8'));
|
|
232
|
+
await src.fs.writeFile('skills/md2pdf/package.json', Buffer.from('{"name":"md2pdf"}\n', 'utf8'));
|
|
233
|
+
// 宿主枚举已按 issue #76 排除依赖树,并把它带回来(真实 walk 的形态)
|
|
234
|
+
src.fs.listRecursiveDetailed = async () => ({
|
|
235
|
+
paths: ['skills/md2pdf/SKILL.md', 'skills/md2pdf/package.json'],
|
|
236
|
+
skippedLinks: [],
|
|
237
|
+
skippedDepDirs: [{ path: 'skills/md2pdf/node_modules', files: 11625 }],
|
|
238
|
+
followedLinks: 0,
|
|
239
|
+
unreadableDirs: [],
|
|
240
|
+
});
|
|
241
|
+
|
|
242
|
+
const out = await new SkillsAdapter().export(src, { includeSecrets: false });
|
|
243
|
+
assert.deepEqual(
|
|
244
|
+
out.data.files.map((f) => f.relativePath).sort(),
|
|
245
|
+
['md2pdf/SKILL.md', 'md2pdf/package.json'],
|
|
246
|
+
'依赖树不得进包,技能本体与 package.json 必须进',
|
|
247
|
+
);
|
|
248
|
+
const line = out.warnings.find((w) => w.includes('运行时依赖目录')) ?? '';
|
|
249
|
+
assert.ok(line.length > 0, '缺依赖目录告警: ' + out.warnings.join(' | '));
|
|
250
|
+
assert.match(line, /11625 个文件/);
|
|
251
|
+
assert.match(line, /未进备份/);
|
|
252
|
+
assert.doesNotMatch(line, /链接/, '依赖目录不是链接问题,措辞不得混淆');
|
|
253
|
+
});
|
|
254
|
+
|
|
228
255
|
test('agentPresets: 目录 bundle 文件收集与写入(.agent-presets 基准目录)', async () => {
|
|
229
256
|
const src = makeContext('win32', 'C:\\Users\\alice');
|
|
230
257
|
await src.fs.writeFile('.agent-presets/work/agent.cordis.yml', Buffer.from('services:\n - name: work\n', 'utf8'));
|
package/src/adapters/index.ts
CHANGED
|
@@ -77,7 +77,7 @@ export type { NamespaceProvider } from './settings.ts';
|
|
|
77
77
|
export type { CredentialRefsProvider } from './credentials.ts';
|
|
78
78
|
|
|
79
79
|
export { SettingsAdapter } from './settings.ts';
|
|
80
|
-
export { UiAdapter, isUiNamespace, KNOWN_UI_NAMESPACE_PREFIXES,
|
|
80
|
+
export { UiAdapter, isUiNamespace, KNOWN_UI_NAMESPACE_PREFIXES, uiMigrationNotes } from './ui.ts';
|
|
81
81
|
export { ProvidersAdapter, DEFAULT_PROVIDER_NAMESPACES, type ProviderExportEntry, type ProviderExportSection } from './providers.ts';
|
|
82
82
|
export { PluginsAdapter, USER_PATCH_FILE } from './plugins.ts';
|
|
83
83
|
export type { LocalPluginPackHook } from './plugins.ts';
|
|
@@ -8,7 +8,7 @@ import { zhMsg } from '../core/messages.ts';
|
|
|
8
8
|
import type { FileSystemFacade } from '../core/types.ts';
|
|
9
9
|
import type { RecursiveListing } from '../utils/recursive-walk.ts';
|
|
10
10
|
|
|
11
|
-
const clean: RecursiveListing = { paths: ['skills/a.md'], skippedLinks: [], followedLinks: 0, unreadableDirs: [] };
|
|
11
|
+
const clean: RecursiveListing = { paths: ['skills/a.md'], skippedLinks: [], skippedDepDirs: [], followedLinks: 0, unreadableDirs: [] };
|
|
12
12
|
|
|
13
13
|
test('linkWarnings:无链接 → 零告警(不制造噪音)', () => {
|
|
14
14
|
assert.deepEqual(linkWarnings(zhMsg, 'Skills', clean), []);
|
|
@@ -26,6 +26,7 @@ test('linkWarnings:跳过项按原因归并,给出数量、原因与路径',
|
|
|
26
26
|
const out = linkWarnings(zhMsg, 'Skills', {
|
|
27
27
|
paths: [],
|
|
28
28
|
followedLinks: 1,
|
|
29
|
+
skippedDepDirs: [],
|
|
29
30
|
unreadableDirs: [],
|
|
30
31
|
skippedLinks: [
|
|
31
32
|
{ path: 'skills/self', reason: 'loop' },
|
|
@@ -48,7 +49,7 @@ test('linkWarnings:跳过项按原因归并,给出数量、原因与路径',
|
|
|
48
49
|
|
|
49
50
|
test('linkWarnings:目录读取失败也必须告警(其内容同样未进备份)', () => {
|
|
50
51
|
const out = linkWarnings(zhMsg, 'Skills', {
|
|
51
|
-
paths: [], followedLinks: 0, skippedLinks: [], unreadableDirs: ['skills/locked', 'skills/denied'],
|
|
52
|
+
paths: [], followedLinks: 0, skippedLinks: [], skippedDepDirs: [], unreadableDirs: ['skills/locked', 'skills/denied'],
|
|
52
53
|
});
|
|
53
54
|
assert.equal(out.length, 1);
|
|
54
55
|
assert.match(out[0]!, /2 个目录读取失败/);
|
|
@@ -61,5 +62,24 @@ test('listFilesDetailed:宿主未实现 detailed 时回退到 listRecursive(
|
|
|
61
62
|
const listing = await listFilesDetailed(fs, 'skills');
|
|
62
63
|
assert.deepEqual(listing.paths, ['skills/x.md']);
|
|
63
64
|
assert.deepEqual(listing.skippedLinks, []);
|
|
65
|
+
assert.deepEqual(listing.skippedDepDirs, []);
|
|
64
66
|
assert.equal(listing.followedLinks, 0);
|
|
65
67
|
});
|
|
68
|
+
|
|
69
|
+
test('issue #76:跳过的 node_modules 依赖目录必须告警(点名路径 + 文件数 + 可重建说明)', () => {
|
|
70
|
+
const out = linkWarnings(zhMsg, 'Skills', {
|
|
71
|
+
...clean,
|
|
72
|
+
skippedDepDirs: [
|
|
73
|
+
{ path: 'skills/md2pdf/node_modules', files: 11625 },
|
|
74
|
+
{ path: 'skills/other/node_modules', files: null },
|
|
75
|
+
],
|
|
76
|
+
});
|
|
77
|
+
assert.equal(out.length, 1, '汇总成一条,不逐目录刷屏');
|
|
78
|
+
const line = out[0]!;
|
|
79
|
+
assert.match(line, /2 个运行时依赖目录/);
|
|
80
|
+
assert.match(line, /未进备份/);
|
|
81
|
+
assert.match(line, /skills\/md2pdf\/node_modules/);
|
|
82
|
+
assert.match(line, /11625 个文件/);
|
|
83
|
+
assert.match(line, /文件数未统计/, '计数预算耗尽时不得猜一个数(更不得写成 0)');
|
|
84
|
+
assert.match(line, /重新安装依赖/);
|
|
85
|
+
});
|
|
@@ -9,7 +9,9 @@
|
|
|
9
9
|
*/
|
|
10
10
|
import type { FileSystemFacade } from '../core/types.ts';
|
|
11
11
|
import type { MsgFunc } from '../core/messages.ts';
|
|
12
|
-
|
|
12
|
+
// t6:注册 adapters 域追加目录(本模块只消费注入的 msg,故只引它的注册副作用)。
|
|
13
|
+
import './messages.ts';
|
|
14
|
+
import type { RecursiveListing, SkippedDepDir, SkippedLink } from '../utils/recursive-walk.ts';
|
|
13
15
|
|
|
14
16
|
/** 跳过原因 → 消息 key(与 SkippedLink.reason 一一对应,穷尽映射) */
|
|
15
17
|
const REASON_KEY: Record<SkippedLink['reason'], string> = {
|
|
@@ -23,10 +25,27 @@ const REASON_KEY: Record<SkippedLink['reason'], string> = {
|
|
|
23
25
|
/** 单条原因下最多列出的路径数(其余折叠为计数,避免把报告撑爆) */
|
|
24
26
|
const MAX_PATHS_PER_REASON = 5
|
|
25
27
|
|
|
28
|
+
/**
|
|
29
|
+
* 被跳过的依赖目录 → 详情片段(路径 + 文件数,issue #76)。
|
|
30
|
+
*
|
|
31
|
+
* 文件数是**量级说明**而非完整性判据:计数预算耗尽时如实写「未统计」,绝不猜一个数
|
|
32
|
+
* (把未知写成 0 会被读成「这个目录是空的」)。
|
|
33
|
+
*/
|
|
34
|
+
function depDirDetail(msg: MsgFunc, dirs: SkippedDepDir[]): string {
|
|
35
|
+
const shown = dirs.slice(0, MAX_PATHS_PER_REASON).map((dir) => {
|
|
36
|
+
const files = dir.files === null
|
|
37
|
+
? msg('adapter.depsDirFilesUnknown')
|
|
38
|
+
: msg('adapter.depsDirFiles', { count: String(dir.files) })
|
|
39
|
+
return msg('adapter.linkDirEntry', { path: dir.path, files })
|
|
40
|
+
})
|
|
41
|
+
const more = dirs.length - shown.length
|
|
42
|
+
return (more > 0 ? [...shown, `(+ ${more})`] : shown).join(', ')
|
|
43
|
+
}
|
|
44
|
+
|
|
26
45
|
/** 取遍历结果:宿主未实现 listRecursiveDetailed(旧版/测试 mock)时回退,行为与旧版一致。 */
|
|
27
46
|
export async function listFilesDetailed(fs: FileSystemFacade, dir: string): Promise<RecursiveListing> {
|
|
28
47
|
if (fs.listRecursiveDetailed !== undefined) return fs.listRecursiveDetailed(dir)
|
|
29
|
-
return { paths: await fs.listRecursive(dir), skippedLinks: [], followedLinks: 0, unreadableDirs: [] }
|
|
48
|
+
return { paths: await fs.listRecursive(dir), skippedLinks: [], skippedDepDirs: [], followedLinks: 0, unreadableDirs: [] }
|
|
30
49
|
}
|
|
31
50
|
|
|
32
51
|
/** 把遍历结果转成告警行(无链接时返回空数组 —— 不制造噪音)。 */
|
|
@@ -45,8 +64,8 @@ export function linkWarnings(msg: MsgFunc, type: string, listing: RecursiveListi
|
|
|
45
64
|
const detail = [...byReason.entries()].map(([reason, paths]) => {
|
|
46
65
|
const shown = paths.slice(0, MAX_PATHS_PER_REASON).join(', ')
|
|
47
66
|
const more = paths.length > MAX_PATHS_PER_REASON ? ` (+ ${paths.length - MAX_PATHS_PER_REASON})` : ''
|
|
48
|
-
return
|
|
49
|
-
}).join('
|
|
67
|
+
return msg('adapter.linkSkipLine', { reason: msg(REASON_KEY[reason]), count: paths.length, paths: shown, more })
|
|
68
|
+
}).join(msg('adapter.listSeparator'))
|
|
50
69
|
out.push(msg('adapter.linksSkipped', { type, count: String(listing.skippedLinks.length), detail }))
|
|
51
70
|
}
|
|
52
71
|
if (listing.unreadableDirs.length > 0) {
|
|
@@ -57,5 +76,11 @@ export function linkWarnings(msg: MsgFunc, type: string, listing: RecursiveListi
|
|
|
57
76
|
type, count: String(listing.unreadableDirs.length), detail: `${shown}${more}`,
|
|
58
77
|
}))
|
|
59
78
|
}
|
|
79
|
+
// issue #76:运行时依赖目录整棵跳过(内容未进备份)——与链接/不可读目录同等对待:必须可见,绝不静默。
|
|
80
|
+
if (listing.skippedDepDirs.length > 0) {
|
|
81
|
+
out.push(msg('adapter.depsDirsSkipped', {
|
|
82
|
+
type, count: String(listing.skippedDepDirs.length), detail: depDirDetail(msg, listing.skippedDepDirs),
|
|
83
|
+
}))
|
|
84
|
+
}
|
|
60
85
|
return out
|
|
61
86
|
}
|
|
@@ -0,0 +1,54 @@
|
|
|
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.ts';
|
|
13
|
+
import type { MsgFunc } from '../core/msg-types.ts';
|
|
14
|
+
|
|
15
|
+
export const zh = {
|
|
16
|
+
// ---------- 链接 / 依赖目录报告(issue #37 / #76)----------
|
|
17
|
+
'adapter.linkDirEntry': '{path}({files})',
|
|
18
|
+
'adapter.linkSkipLine': '{reason} {count} 个: {paths}{more}',
|
|
19
|
+
'adapter.listSeparator': ';',
|
|
20
|
+
// ---------- pluginFiles / tarball ----------
|
|
21
|
+
'adapter.pluginFilesCollectDirInvalid': 'pluginFiles collectDir 非法(须为相对 ~/.dsh 根的安全路径): {dir}',
|
|
22
|
+
'adapter.tarballRelPathInvalid': '非法 tarball 相对路径: {path}',
|
|
23
|
+
'adapter.localTarballUnpackFailed': '本地插件 {name} 的 tarball 解包失败,回退原始 spec:{err}',
|
|
24
|
+
// ---------- prompts 计划项明细 ----------
|
|
25
|
+
'adapter.promptDiffDetail': '行 {line} current={current} imported={imported}',
|
|
26
|
+
// ---------- 会话连带失败明细 ----------
|
|
27
|
+
'adapter.sessionUncoupledNote': '{id}({reason})',
|
|
28
|
+
// ---------- 工作区告警拼接 ----------
|
|
29
|
+
'adapter.workspaceNotesParen': '({notes})',
|
|
30
|
+
// ---------- localStorage 等 Host 无通道的 UI 状态说明 ----------
|
|
31
|
+
'adapter.uiLocalStorageTaskBoard': '任务看板数据存于浏览器 localStorage,Host 侧无访问通道,需在目标机器重新配置',
|
|
32
|
+
'adapter.uiLocalStoragePanel': '面板宽度/折叠状态存于浏览器 localStorage,Host 侧无访问通道,需在目标机器重新配置',
|
|
33
|
+
'adapter.uiLocalStorageGeneric': '浏览器端 UI 状态无法经 Host API 迁移',
|
|
34
|
+
} as const;
|
|
35
|
+
|
|
36
|
+
export const en: Record<keyof typeof zh, string> = {
|
|
37
|
+
'adapter.linkDirEntry': '{path} ({files})',
|
|
38
|
+
'adapter.linkSkipLine': '{reason} {count} item(s): {paths}{more}',
|
|
39
|
+
'adapter.listSeparator': '; ',
|
|
40
|
+
'adapter.pluginFilesCollectDirInvalid': 'pluginFiles collectDir is invalid (must be a safe path relative to the ~/.dsh root): {dir}',
|
|
41
|
+
'adapter.tarballRelPathInvalid': 'Invalid relative tarball path: {path}',
|
|
42
|
+
'adapter.localTarballUnpackFailed': 'Failed to unpack the tarball of local plugin {name}; falling back to the original spec: {err}',
|
|
43
|
+
'adapter.promptDiffDetail': 'line {line} current={current} imported={imported}',
|
|
44
|
+
'adapter.sessionUncoupledNote': '{id} ({reason})',
|
|
45
|
+
'adapter.workspaceNotesParen': ' ({notes})',
|
|
46
|
+
'adapter.uiLocalStorageTaskBoard': 'Task-board data lives in the browser localStorage and the host has no access path; reconfigure it on the target machine',
|
|
47
|
+
'adapter.uiLocalStoragePanel': 'Panel width/collapsed state lives in the browser localStorage and the host has no access path; reconfigure it on the target machine',
|
|
48
|
+
'adapter.uiLocalStorageGeneric': 'Browser-side UI state cannot be migrated through the host API',
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
registerMessageCatalog({ id: 'adapters-domain', zh, en });
|
|
52
|
+
|
|
53
|
+
/** 本域活翻译器(与 core 的 engineMsg 同一实现;导入本模块即完成目录注册)。 */
|
|
54
|
+
export const engineMsg: MsgFunc = engineMsgOf;
|
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
*/
|
|
9
9
|
import { sha256Hex } from '../utils/hashing.ts';
|
|
10
10
|
import { msgOf, zhMsg } from '../core/messages.ts';
|
|
11
|
+
import { engineMsg } from './messages.ts';
|
|
11
12
|
import { linkWarnings, listFilesDetailed } from './link-report.ts';
|
|
12
13
|
import type { RecursiveListing } from '../utils/recursive-walk.ts';
|
|
13
14
|
import { isPathSafe, isReservedInternalRel } from '../utils/paths.ts';
|
|
@@ -36,7 +37,7 @@ export class PluginFilesAdapter implements ConfigAdapter<FilesSection> {
|
|
|
36
37
|
constructor(whitelist: string[] = [...DEFAULT_PLUGIN_FILE_WHITELIST], collectDir?: string) {
|
|
37
38
|
this.whitelist = whitelist;
|
|
38
39
|
if (collectDir !== undefined && collectDir !== '' && !isPathSafe(collectDir)) {
|
|
39
|
-
throw new Error(
|
|
40
|
+
throw new Error(engineMsg('adapter.pluginFilesCollectDirInvalid', { dir: collectDir }));
|
|
40
41
|
}
|
|
41
42
|
this.collectDir = collectDir === '' ? undefined : collectDir;
|
|
42
43
|
}
|
|
@@ -63,7 +64,7 @@ export class PluginFilesAdapter implements ConfigAdapter<FilesSection> {
|
|
|
63
64
|
}
|
|
64
65
|
// 2) 约定配置目录递归收集(相对 ~/.dsh 根的完整路径;与白名单文件去重)
|
|
65
66
|
// issue #37:与 skills 等同一条遍历(跟随 junction/符号链接 + 跳过留痕)
|
|
66
|
-
let listing: RecursiveListing = { paths: [], skippedLinks: [], followedLinks: 0, unreadableDirs: [] };
|
|
67
|
+
let listing: RecursiveListing = { paths: [], skippedLinks: [], skippedDepDirs: [], followedLinks: 0, unreadableDirs: [] };
|
|
67
68
|
if (this.collectDir !== undefined) {
|
|
68
69
|
try {
|
|
69
70
|
listing = await listFilesDetailed(ctx.fs, this.collectDir);
|
package/src/adapters/plugins.ts
CHANGED
|
@@ -18,6 +18,7 @@ import { classifyPluginSpec, LOCAL_PLUGIN_DIR, resolveLocalPluginPath } from '..
|
|
|
18
18
|
import type { PackLocalPluginsResult } from '../core/local-plugin-pack.ts';
|
|
19
19
|
import { msgOf, zhMsg } from '../core/messages.ts';
|
|
20
20
|
import type { MsgFunc } from '../core/messages.ts';
|
|
21
|
+
import { engineMsg } from './messages.ts';
|
|
21
22
|
import { stringifyJsonSafe } from '../utils/json.ts';
|
|
22
23
|
import { isPathSafe, normalizePath } from '../utils/paths.ts';
|
|
23
24
|
import { isENOENT } from '../utils/guards.ts';
|
|
@@ -77,7 +78,7 @@ function tarballOf(data: PluginsSection, pkgName: string): LocalPluginTarball |
|
|
|
77
78
|
export function localTarballCacheName(relativePath: string): string {
|
|
78
79
|
const base = relativePath.split('/').pop() ?? '';
|
|
79
80
|
if (base === '' || base.includes('\\') || base === '.' || base === '..') {
|
|
80
|
-
throw new Error(
|
|
81
|
+
throw new Error(engineMsg('adapter.tarballRelPathInvalid', { path: relativePath }));
|
|
81
82
|
}
|
|
82
83
|
return base;
|
|
83
84
|
}
|
|
@@ -697,7 +698,7 @@ export class PluginsAdapter implements ConfigAdapter<PluginsSection> {
|
|
|
697
698
|
names: clean.dropped.map((d) => d.name).join(', '),
|
|
698
699
|
}));
|
|
699
700
|
}
|
|
700
|
-
return { ok: true, needsRestart: true, message: notes.join('
|
|
701
|
+
return { ok: true, needsRestart: true, message: notes.join(msg('adapter.listSeparator')) };
|
|
701
702
|
} catch (err) {
|
|
702
703
|
const reason = err instanceof Error ? err.message : String(err);
|
|
703
704
|
return {
|
|
@@ -730,7 +731,7 @@ export class PluginsAdapter implements ConfigAdapter<PluginsSection> {
|
|
|
730
731
|
spec = `file:${abs}`;
|
|
731
732
|
} catch (err) {
|
|
732
733
|
// 解包失败不阻断安装尝试:退回原 spec(可能失败,但会如实报错而非静默丢失)
|
|
733
|
-
ctx.onLog?.(
|
|
734
|
+
ctx.onLog?.(msg('adapter.localTarballUnpackFailed', { name, err: err instanceof Error ? err.message : String(err) }));
|
|
734
735
|
}
|
|
735
736
|
}
|
|
736
737
|
// 执行日志:记录实际将发起的子进程命令行(与宿主 DshPluginsFacade 的
|
package/src/adapters/prompts.ts
CHANGED
|
@@ -14,6 +14,8 @@
|
|
|
14
14
|
*/
|
|
15
15
|
import { msgOf, zhMsg } from '../core/messages.ts';
|
|
16
16
|
import type { MsgFunc } from '../core/messages.ts';
|
|
17
|
+
// t6:注册 adapters 域追加目录(本模块只消费注入的 msg,故只引它的注册副作用)。
|
|
18
|
+
import './messages.ts';
|
|
17
19
|
import type { PromptEntry } from '../schema/types.ts';
|
|
18
20
|
import type {
|
|
19
21
|
ApplyResult, ConfigAdapter, ExportOptions, ExportSection, HostContext,
|
|
@@ -159,7 +161,11 @@ export class PromptsAdapter implements ConfigAdapter<PromptsExportSection> {
|
|
|
159
161
|
items.push({
|
|
160
162
|
id, kind: 'Conflict', adapter: 'prompts',
|
|
161
163
|
description: msg('adapter.promptDiff', { name: p.name }),
|
|
162
|
-
detail:
|
|
164
|
+
detail: msg('adapter.promptDiffDetail', {
|
|
165
|
+
line: String(p.sourceLineId),
|
|
166
|
+
current: JSON.stringify(sameName.text).slice(0, 80),
|
|
167
|
+
imported: JSON.stringify(p.text).slice(0, 80),
|
|
168
|
+
}),
|
|
163
169
|
severity: 'warning',
|
|
164
170
|
// 层身份随计划项走:快照按这一层记原行、回滚才能写回原文件(issue #71)
|
|
165
171
|
target: { adapter: 'prompts', ref: p.sourceLineId ?? '', file: targetLine.file },
|
|
@@ -128,6 +128,32 @@ test('self: 预览只列白名单 —— 快照/同步工作副本等内部数
|
|
|
128
128
|
);
|
|
129
129
|
});
|
|
130
130
|
|
|
131
|
+
test('self: 磁盘体检新统计的目录(rollback-points / 遗留 checkpoints)不进本分区载荷', async () => {
|
|
132
|
+
const src = makeContext('win32', 'C:\\Users\\alice');
|
|
133
|
+
const syncConfig = Buffer.from('{"transport":"git"}', 'utf8');
|
|
134
|
+
await src.fs.writeFile('dsh-config-manager/sync/sync-config.json', syncConfig);
|
|
135
|
+
// 磁盘体检会统计这两块占用(回滚点库 + 旧检查点库)——但它们是**数据**,不是 self 分区的配置
|
|
136
|
+
await src.fs.writeFile('dsh-config-manager/rollback-points/index.json', Buffer.from('x'.repeat(4096), 'utf8'));
|
|
137
|
+
await src.fs.writeFile('dsh-config-manager/rollback-points/objects/rb-1/session.v4.jsonl.zstd', Buffer.from('x'.repeat(4096), 'utf8'));
|
|
138
|
+
await src.fs.writeFile('dsh-config-manager/checkpoints/cp-1/files/a.bin', Buffer.from('x'.repeat(4096), 'utf8'));
|
|
139
|
+
await src.fs.writeFile('dsh-config-manager/checkpoints/index.json', Buffer.from('{}', 'utf8'));
|
|
140
|
+
|
|
141
|
+
const adapter = new SelfAdapter();
|
|
142
|
+
const exported = await adapter.export(src, { includeSecrets: false });
|
|
143
|
+
const previewed = await adapter.preview(src, { includeSecrets: false });
|
|
144
|
+
const rels = exported.data.files.map((f) => f.relativePath);
|
|
145
|
+
|
|
146
|
+
assert.deepEqual(rels, ['sync/sync-config.json'], '白名单外一律不收(新增统计目录不得混进载荷)');
|
|
147
|
+
assert.equal(new Set(rels).size, rels.length, '不得出现重复条目');
|
|
148
|
+
assert.equal(exported.counts.files, 1);
|
|
149
|
+
assert.deepEqual(
|
|
150
|
+
previewed.items.map((u) => u.id),
|
|
151
|
+
['self:sync/sync-config.json'],
|
|
152
|
+
'预览与导出同口径(只列白名单)',
|
|
153
|
+
);
|
|
154
|
+
assert.equal(previewed.sizeBytes, syncConfig.byteLength, '体积只算白名单文件');
|
|
155
|
+
});
|
|
156
|
+
|
|
131
157
|
test('self: 宿主未实现 statSize(旧版门面)时,白名单判定仍只收配置、不误列内部数据', async () => {
|
|
132
158
|
const src = makeContext('win32', 'C:\\Users\\alice');
|
|
133
159
|
await src.fs.writeFile('dsh-config-manager/sync/sync-config.json', Buffer.from('{}', 'utf8'));
|
package/src/adapters/self.ts
CHANGED
|
@@ -8,7 +8,13 @@
|
|
|
8
8
|
* - market/market-config.json 配置市场配置
|
|
9
9
|
*
|
|
10
10
|
* 排除项:sync/sync-history.json(执行记录,属数据非配置)、market/cache/(缓存)、
|
|
11
|
-
* snapshots/(快照)、tmp/ exports
|
|
11
|
+
* snapshots/(快照)、tmp/ exports/(临时/导出产物)、rollback-points/(回滚点库——磁盘体检
|
|
12
|
+
* 会统计它,但它是**数据**不是配置)、checkpoints/(遗留旧检查点库,已不再读取,也不删)
|
|
13
|
+
* ——只备份「配置」,不备份数据。
|
|
14
|
+
*
|
|
15
|
+
* 与磁盘体检(`src/core/disk-usage.ts`)的关系:那边新增统计 `rollback-points` / 遗留
|
|
16
|
+
* `checkpoints` **不改变本分区的载荷**——本清单是白名单(`SELF_CONFIG_FILES`),
|
|
17
|
+
* 被统计的目录一律不在其中,既不重复也不遗漏(守卫见 `self.test.ts`)。
|
|
12
18
|
*
|
|
13
19
|
* 实现:继承 FileCollectionAdapter 复用 analyzeImport/applyItem/validate(幂等 hash 比对、
|
|
14
20
|
* 快照/回滚路径一致);**清单来自覆写的 listRelPaths()(白名单),export() 与 preview() 都经
|
|
@@ -137,7 +143,7 @@ export class SelfAdapter extends FileCollectionAdapter {
|
|
|
137
143
|
* 同时是单元 id(self:<rel>)与 includeItems 的比对键 —— 不能随平台漂移成反斜杠。
|
|
138
144
|
*/
|
|
139
145
|
protected override async listRelPaths(ctx: HostContext): Promise<RecursiveListing> {
|
|
140
|
-
const empty: RecursiveListing = { paths: [], skippedLinks: [], followedLinks: 0, unreadableDirs: [] };
|
|
146
|
+
const empty: RecursiveListing = { paths: [], skippedLinks: [], skippedDepDirs: [], followedLinks: 0, unreadableDirs: [] };
|
|
141
147
|
const found: string[] = [];
|
|
142
148
|
for (const rel of SELF_CONFIG_FILES) {
|
|
143
149
|
const full = `${this.baseDir}/${rel}`;
|