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
|
@@ -16,10 +16,14 @@ import type {
|
|
|
16
16
|
} from './sync-api.ts';
|
|
17
17
|
// 通道枚举的唯一声明处(**零依赖**,可被浏览器半运行时 import):加一条通道只改这里一处。
|
|
18
18
|
import { SYNC_CHANNELS } from '../../utils/shared-constants.ts';
|
|
19
|
-
|
|
19
|
+
// zh 源目录按**值**引入:本文件的渲染模型(UiT 字典不含 syncflow.* 键)需要为「回滚未完成」
|
|
20
|
+
// 这类 sync 命名空间文案取词;组件可经 syncText 参数注入自己的翻译器(缺省 = zh 源)。
|
|
21
|
+
import { en as syncEnCatalog, zh as syncZhCatalog, type SyncKey } from './sync-locales.ts';
|
|
22
|
+
// 跳过原因码 → 字典键的唯一映射(与同步历史共用;绝不各自维护一份 switch)
|
|
23
|
+
import { skipReasonKey } from './history-model.ts';
|
|
20
24
|
import type { CloudConfigIssueCode } from '../../ui/sync-settings-view.ts';
|
|
21
25
|
import type { S3CompatProvider } from '../../sync/sync-config.ts';
|
|
22
|
-
import {
|
|
26
|
+
import { currentUiLanguage, type UiT } from '../../ui/i18n.ts';
|
|
23
27
|
|
|
24
28
|
/* ---------------------------------------------------------------- 私有仓库提示 */
|
|
25
29
|
|
|
@@ -37,7 +41,7 @@ export function needsGitToken(repoUrl: string): boolean {
|
|
|
37
41
|
return /^https?:\/\//i.test(repoUrl.trim());
|
|
38
42
|
}
|
|
39
43
|
|
|
40
|
-
export function privateRepoHint(t: UiT
|
|
44
|
+
export function privateRepoHint(t: UiT): string {
|
|
41
45
|
return t('sync.privateRepoHint');
|
|
42
46
|
}
|
|
43
47
|
|
|
@@ -62,14 +66,15 @@ export interface SyncSectionOption {
|
|
|
62
66
|
/** host 目录(SyncSectionInfo[])→ UI 勾选项(保留 id 顺序)。
|
|
63
67
|
* 同步分区必为导出目录(DEFAULT_CATEGORIES)的可移植子集:分组/描述从导出目录
|
|
64
68
|
* 补充(单一事实源,与「导出备份·自定义模式」的目录保持一致),未命中 id 兜底。 */
|
|
65
|
-
export function syncSectionOptions(info: readonly SyncSectionInfo[]): SyncSectionOption[] {
|
|
69
|
+
export function syncSectionOptions(info: readonly SyncSectionInfo[], t: UiT): SyncSectionOption[] {
|
|
66
70
|
const meta = new Map(DEFAULT_CATEGORIES.map((c) => [c.id, c]));
|
|
67
71
|
return info.map((s) => {
|
|
68
72
|
const cat = meta.get(s.id);
|
|
69
73
|
return {
|
|
70
74
|
id: s.id,
|
|
71
75
|
label: s.displayName,
|
|
72
|
-
|
|
76
|
+
// 描述是**字典键**(英文界面零中文):在这里按当前语言解析,渲染层直接显示
|
|
77
|
+
description: cat !== undefined ? t(cat.descriptionKey) : '',
|
|
73
78
|
group: cat?.group ?? 'general',
|
|
74
79
|
portability: s.portability,
|
|
75
80
|
defaultIncluded: s.defaultIncluded,
|
|
@@ -79,7 +84,7 @@ export function syncSectionOptions(info: readonly SyncSectionInfo[]): SyncSectio
|
|
|
79
84
|
|
|
80
85
|
/** 高级模式勾选目录 → 按导出分组(EXPORT_GROUPS)投影:与「导出备份·自定义模式」同构,
|
|
81
86
|
* 空分组省略;UI 直接渲染 groupCard。 */
|
|
82
|
-
export function syncSectionGroups(options: readonly SyncSectionOption[]): {
|
|
87
|
+
export function syncSectionGroups(options: readonly SyncSectionOption[], t: UiT): {
|
|
83
88
|
group: ExportGroup;
|
|
84
89
|
label: string;
|
|
85
90
|
note?: string;
|
|
@@ -89,7 +94,7 @@ export function syncSectionGroups(options: readonly SyncSectionOption[]): {
|
|
|
89
94
|
.map((g) => ({
|
|
90
95
|
group: g.id,
|
|
91
96
|
label: g.label,
|
|
92
|
-
...(g.
|
|
97
|
+
...(g.noteKey !== undefined ? { note: t(g.noteKey) } : {}),
|
|
93
98
|
items: options.filter((o) => o.group === g.id),
|
|
94
99
|
}))
|
|
95
100
|
.filter((g) => g.items.length > 0);
|
|
@@ -167,7 +172,7 @@ export function summarizePullChanges(changes: readonly PullChange[]): PullChange
|
|
|
167
172
|
}
|
|
168
173
|
|
|
169
174
|
/** PlanItemKind → 短标签(列表徽章) */
|
|
170
|
-
export function kindLabel(kind: PlanItemKind, t: UiT
|
|
175
|
+
export function kindLabel(kind: PlanItemKind, t: UiT): string {
|
|
171
176
|
switch (kind) {
|
|
172
177
|
case 'Create': return t('sync.kind.create');
|
|
173
178
|
case 'Update': return t('sync.kind.update');
|
|
@@ -184,7 +189,7 @@ export function kindLabel(kind: PlanItemKind, t: UiT = zhUiT): string {
|
|
|
184
189
|
}
|
|
185
190
|
|
|
186
191
|
/** severity → 短标签 */
|
|
187
|
-
export function severityLabel(severity: PlanItem['severity'], t: UiT
|
|
192
|
+
export function severityLabel(severity: PlanItem['severity'], t: UiT): string {
|
|
188
193
|
switch (severity) {
|
|
189
194
|
case 'error': return t('sync.severity.error');
|
|
190
195
|
case 'warning': return t('sync.severity.warning');
|
|
@@ -286,6 +291,26 @@ export function cloudIssueKey(code: string): SyncKey | null {
|
|
|
286
291
|
: null;
|
|
287
292
|
}
|
|
288
293
|
|
|
294
|
+
/* ------------------------------------------------ G-34:gist 通道不适合大分区 */
|
|
295
|
+
|
|
296
|
+
/**
|
|
297
|
+
* G-34(docs/spec/known-gaps.md):gist 通道**刻意不做内容寻址外置** —— 一次 GET 把全部
|
|
298
|
+
* 文件内容取回、单文件内容超限会被截断、文件数还有上限,因此会话这类大分区不适合走 gist。
|
|
299
|
+
*
|
|
300
|
+
* 本常量 = 「gist 表单」常驻提示的字典键(组件按它取 sync 字典文案渲染 hint)。
|
|
301
|
+
*/
|
|
302
|
+
export const GIST_LARGE_SECTION_HINT_KEY: SyncKey = 'cloud.gistLargeSectionHint'
|
|
303
|
+
|
|
304
|
+
/**
|
|
305
|
+
* 「通道 = gist 且勾选 sessions」时的风险提示键;不满足 → null。
|
|
306
|
+
*
|
|
307
|
+
* 只做判定、不渲染、**不改选择器逻辑**(不阻止勾选、不动勾选集合)—— 组件拿到非 null 时
|
|
308
|
+
* 用 sync 字典渲染一条 warn Banner(复用既有 hint / Banner 机制,不新增设置项与样式)。
|
|
309
|
+
*/
|
|
310
|
+
export function gistSessionsRiskKey(channel: SyncChannel, sections: readonly SectionId[]): SyncKey | null {
|
|
311
|
+
return channel === 'gist' && sections.includes('sessions') ? 'mode.gistSessionsWarn' : null
|
|
312
|
+
}
|
|
313
|
+
|
|
289
314
|
/**
|
|
290
315
|
* 按通道构造 `Record<SyncChannel, T>`(缺省值由 `make` 给出)。
|
|
291
316
|
*
|
|
@@ -424,10 +449,11 @@ function browserStorage(): Pick<Storage, 'getItem' | 'setItem'> | null {
|
|
|
424
449
|
|
|
425
450
|
/* ---------------------------------------------------------------- WebDAV 预设 */
|
|
426
451
|
|
|
427
|
-
/** 常见 WebDAV 服务器预设:
|
|
452
|
+
/** 常见 WebDAV 服务器预设:labelKey 展示名键 + url 模板(含 <占位> 待用户替换)。 */
|
|
428
453
|
export interface WebDavPreset {
|
|
429
454
|
id: string;
|
|
430
|
-
|
|
455
|
+
/** 展示名的**字典键**(sync 命名空间;与 SYNC_CHANNEL_LABEL_KEY / S3_PROVIDER_LABEL_KEY 同口径) */
|
|
456
|
+
labelKey: SyncKey;
|
|
431
457
|
/** url 模板;可能含 <server>/<user> 占位符,用户需替换为真实地址 */
|
|
432
458
|
url: string;
|
|
433
459
|
/** 是否需要用户替换占位符 */
|
|
@@ -436,13 +462,13 @@ export interface WebDavPreset {
|
|
|
436
462
|
|
|
437
463
|
/** 内置常见 WebDAV 服务器(预设下拉数据源;第一项为自定义)。 */
|
|
438
464
|
export const WEBDAV_PRESETS: readonly WebDavPreset[] = [
|
|
439
|
-
{ id: 'custom',
|
|
440
|
-
{ id: 'jianguoyun',
|
|
441
|
-
{ id: 'nextcloud',
|
|
442
|
-
{ id: 'owncloud',
|
|
443
|
-
{ id: 'seafile',
|
|
444
|
-
{ id: 'synology',
|
|
445
|
-
{ id: 'box',
|
|
465
|
+
{ id: 'custom', labelKey: 'webdav.preset.custom', url: '', hasPlaceholder: false },
|
|
466
|
+
{ id: 'jianguoyun', labelKey: 'webdav.preset.jianguoyun', url: 'https://dav.jianguoyun.com/dav/', hasPlaceholder: false },
|
|
467
|
+
{ id: 'nextcloud', labelKey: 'webdav.preset.nextcloud', url: 'https://<server>/remote.php/dav/files/<user>/', hasPlaceholder: true },
|
|
468
|
+
{ id: 'owncloud', labelKey: 'webdav.preset.owncloud', url: 'https://<server>/remote.php/dav/files/<user>/', hasPlaceholder: true },
|
|
469
|
+
{ id: 'seafile', labelKey: 'webdav.preset.seafile', url: 'https://<server>/seafdav/', hasPlaceholder: true },
|
|
470
|
+
{ id: 'synology', labelKey: 'webdav.preset.synology', url: 'https://<nas-ip>:5006/', hasPlaceholder: true },
|
|
471
|
+
{ id: 'box', labelKey: 'webdav.preset.box', url: 'https://dav.box.com/dav/', hasPlaceholder: false },
|
|
446
472
|
];
|
|
447
473
|
|
|
448
474
|
/** 默认预设(自定义)对应的 id。 */
|
|
@@ -484,7 +510,7 @@ export interface SyncButtons {
|
|
|
484
510
|
* - 活动通道远端地址未就绪(remoteReady=false)→ 禁用(无从同步);
|
|
485
511
|
* - busy 时按钮文案切换为「正在推送/拉取…」(配 Spinner)。
|
|
486
512
|
*/
|
|
487
|
-
export function computeSyncButtons(busy: 'sync' | 'push' | 'pull' | 'apply' | 'rollback' | null, remoteReady: boolean, t: UiT
|
|
513
|
+
export function computeSyncButtons(busy: 'sync' | 'push' | 'pull' | 'apply' | 'rollback' | null, remoteReady: boolean, t: UiT): SyncButtons {
|
|
488
514
|
const idle = busy === null;
|
|
489
515
|
const enabled = idle && remoteReady;
|
|
490
516
|
return {
|
|
@@ -509,7 +535,7 @@ export function computeSyncStatus(
|
|
|
509
535
|
statusInfo: SyncStatusResponse | null,
|
|
510
536
|
loading: boolean,
|
|
511
537
|
error: string | null,
|
|
512
|
-
t: UiT
|
|
538
|
+
t: UiT,
|
|
513
539
|
): SyncStatusSummary {
|
|
514
540
|
if (loading) return { kind: 'loading', text: t('sync.statusLoading') };
|
|
515
541
|
if (error !== null) return { kind: 'error', text: error };
|
|
@@ -543,7 +569,7 @@ export function formatDateTime(iso: string): string {
|
|
|
543
569
|
}
|
|
544
570
|
|
|
545
571
|
/** 上次同步时间的展示文本('' / undefined = 从未同步) */
|
|
546
|
-
export function formatLastSync(iso: string | undefined, t: UiT
|
|
572
|
+
export function formatLastSync(iso: string | undefined, t: UiT): string {
|
|
547
573
|
if (iso === undefined || iso === '') return t('sync.neverSyncedShort');
|
|
548
574
|
return formatDateTime(iso);
|
|
549
575
|
}
|
|
@@ -558,7 +584,7 @@ export interface PushReportView {
|
|
|
558
584
|
}
|
|
559
585
|
|
|
560
586
|
/** push 报告 → 渲染模型(ok 头部带快照 id;失败显示引擎 message;分区与告警透传) */
|
|
561
|
-
export function pushReportView(report: SyncPushReport | null, t: UiT
|
|
587
|
+
export function pushReportView(report: SyncPushReport | null, t: UiT): PushReportView | null {
|
|
562
588
|
if (report === null) return null;
|
|
563
589
|
if (!report.ok) {
|
|
564
590
|
return { kind: 'error', headline: report.message ?? t('sync.pushFailed'), sections: report.sections, warnings: report.warnings };
|
|
@@ -576,7 +602,8 @@ export function pushReportView(report: SyncPushReport | null, t: UiT = zhUiT): P
|
|
|
576
602
|
export interface PushPreviewView {
|
|
577
603
|
ok: boolean;
|
|
578
604
|
/** 将推送的分区行(含计数 + 是否相对基线有变化) */
|
|
579
|
-
|
|
605
|
+
/** 分区 id 来自宿主 SyncPushPreviewSection.section(SectionId)——写 string 会让渲染侧被迫转型 */
|
|
606
|
+
rows: { section: SectionId; count: number; changed: boolean }[];
|
|
580
607
|
/** 变更分区数(要展示「新增/更新 N 个分区」) */
|
|
581
608
|
changedCount: number;
|
|
582
609
|
/** 远端现有快照数(0 = 首次推送创建首个基线) */
|
|
@@ -592,7 +619,7 @@ export interface PushPreviewView {
|
|
|
592
619
|
}
|
|
593
620
|
|
|
594
621
|
/** push 预览 → 渲染模型(P0-②):列分区 + 变更计数 + 远端基线提示。 */
|
|
595
|
-
export function pushPreviewView(preview: SyncPushPreview | null, t: UiT
|
|
622
|
+
export function pushPreviewView(preview: SyncPushPreview | null, t: UiT): PushPreviewView | null {
|
|
596
623
|
if (preview === null) return null;
|
|
597
624
|
if (!preview.ok) {
|
|
598
625
|
return {
|
|
@@ -627,7 +654,7 @@ export interface PullReportView {
|
|
|
627
654
|
}
|
|
628
655
|
|
|
629
656
|
/** pull 报告 → 渲染模型(差异预览;empty = 无变更;error = 拉取失败) */
|
|
630
|
-
export function pullReportView(report: SyncPullReport | null, t: UiT
|
|
657
|
+
export function pullReportView(report: SyncPullReport | null, t: UiT): PullReportView | null {
|
|
631
658
|
if (report === null) return null;
|
|
632
659
|
if (!report.ok) {
|
|
633
660
|
return { kind: 'error', headline: report.message ?? t('sync.pullFailed'), summary: null, previewHint: '' };
|
|
@@ -677,7 +704,7 @@ export function computeGithubLoginView(
|
|
|
677
704
|
userCode: string,
|
|
678
705
|
verificationUri: string,
|
|
679
706
|
error: string | null,
|
|
680
|
-
t: UiT
|
|
707
|
+
t: UiT,
|
|
681
708
|
): GithubLoginView {
|
|
682
709
|
const inFlight = phase === 'starting' || phase === 'waiting' || phase === 'polling';
|
|
683
710
|
let statusText: string;
|
|
@@ -716,7 +743,7 @@ export function computeGithubLoginView(
|
|
|
716
743
|
}
|
|
717
744
|
|
|
718
745
|
/** 轮询终止态 → 用户可读消息(pending 不是终止态,返回空串;成功/拒绝/过期/错误给出明确文案) */
|
|
719
|
-
export function githubPollMessage(poll: GithubPollResponse, t: UiT
|
|
746
|
+
export function githubPollMessage(poll: GithubPollResponse, t: UiT): string {
|
|
720
747
|
switch (poll.status) {
|
|
721
748
|
case 'success':
|
|
722
749
|
return t('sync.github.pollSuccess');
|
|
@@ -895,6 +922,7 @@ export function buildAdoptions(
|
|
|
895
922
|
items: readonly SyncConfirmItem[],
|
|
896
923
|
adopted: ReadonlyMap<string, boolean>,
|
|
897
924
|
resolutions: ReadonlyMap<string, SyncConflictResolution>,
|
|
925
|
+
t: UiT,
|
|
898
926
|
): SyncItemAdoption[] {
|
|
899
927
|
const out: SyncItemAdoption[] = [];
|
|
900
928
|
for (const it of items) {
|
|
@@ -903,7 +931,7 @@ export function buildAdoptions(
|
|
|
903
931
|
if (it.kind === 'Conflict') {
|
|
904
932
|
const resolution = resolutions.get(it.itemId);
|
|
905
933
|
if (resolution === undefined) {
|
|
906
|
-
throw new Error(
|
|
934
|
+
throw new Error(t('sync.conflictUnresolved', { id: it.itemId }));
|
|
907
935
|
}
|
|
908
936
|
adoption.resolution = resolution;
|
|
909
937
|
}
|
|
@@ -919,7 +947,21 @@ export function buildAdoptions(
|
|
|
919
947
|
* `ok` 因此仍为 true;用户看到「已导入 N 个分区」就以为全部成功,
|
|
920
948
|
* 而排在插件之后的步骤(凭据写入)根本没执行 —— 真机表现就是「模型列表同步过来了、密钥没进来」。
|
|
921
949
|
*/
|
|
922
|
-
export type ApplyItemsViewKind = 'ok' | 'partial' | 'failed' | 'rolledBack';
|
|
950
|
+
export type ApplyItemsViewKind = 'ok' | 'partial' | 'failed' | 'rolledBack' | 'rollbackIncomplete';
|
|
951
|
+
|
|
952
|
+
/** sync 命名空间的取词器(缺省 = 该字典的 zh 源;组件可注入自己的 t 以本地化)。 */
|
|
953
|
+
export type SyncTextFn = (key: SyncKey, params?: Record<string, unknown>) => string
|
|
954
|
+
// **活**取词器(不是 zh 快照):sync 命名空间的 zh/en 目录都在手边,按当前语言取词 ——
|
|
955
|
+
// 否则英文界面上「回滚未完成」这类只住在 sync 字典的文案会原样透出中文。
|
|
956
|
+
const defaultSyncText: SyncTextFn = (key, params) => {
|
|
957
|
+
const raw = (currentUiLanguage() === 'zh' ? syncZhCatalog : syncEnCatalog)[key]
|
|
958
|
+
if (params === undefined) return raw
|
|
959
|
+
// 与字典的 {param} 口径一致(缺参原样保留,便于排查)
|
|
960
|
+
return raw.replace(/\{(\w+)\}/g, (m, name: string) => {
|
|
961
|
+
const value = params[name]
|
|
962
|
+
return value === undefined ? m : String(value)
|
|
963
|
+
})
|
|
964
|
+
}
|
|
923
965
|
|
|
924
966
|
export interface ApplyItemsView {
|
|
925
967
|
kind: ApplyItemsViewKind;
|
|
@@ -930,30 +972,45 @@ export interface ApplyItemsView {
|
|
|
930
972
|
ineffective: { itemId: string; adapter: string; message?: string }[];
|
|
931
973
|
restoreId: string;
|
|
932
974
|
needsRestart: boolean;
|
|
975
|
+
/**
|
|
976
|
+
* 整体回滚是否**完整**(诚实性,2026-10):`rolledBack=true` 只说明触发了整体回滚,
|
|
977
|
+
* 不代表每一项都补偿成功(凭据原本存在 → DSH 不可回读,无法自动恢复)。
|
|
978
|
+
* false 时 kind='rollbackIncomplete' —— **绝不**渲染成「已整体回滚」。
|
|
979
|
+
* 旧宿主不返回 → 字段缺席(按旧的「已整体回滚」处理,不误报)。
|
|
980
|
+
*/
|
|
981
|
+
rollbackFull?: boolean;
|
|
933
982
|
}
|
|
934
983
|
|
|
935
|
-
/** apply-items 执行结果 → 渲染模型(ok / failed /
|
|
984
|
+
/** apply-items 执行结果 → 渲染模型(ok / partial / failed / 整体回滚 / 回滚未完成)。 */
|
|
936
985
|
export function applyItemsReportView(
|
|
937
986
|
report: ApplyItemsResponse | null,
|
|
938
|
-
t: UiT
|
|
987
|
+
t: UiT,
|
|
988
|
+
syncText: SyncTextFn = defaultSyncText,
|
|
939
989
|
): ApplyItemsView | null {
|
|
940
990
|
if (report === null) return null;
|
|
941
991
|
const failedOnly = report.failed.length > 0 && !report.ok;
|
|
942
992
|
// 旧宿主不返回 ineffective → 空数组(按「无未生效项」处理,绝不据此误报部分成功)
|
|
943
993
|
const ineffective = report.ineffective ?? [];
|
|
944
|
-
const kind: ApplyItemsViewKind = !report.ok && report.
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
994
|
+
const kind: ApplyItemsViewKind = !report.ok && report.rollbackFull === false
|
|
995
|
+
// 回滚**未完成**优先于「已整体回滚」:半回滚态的配置可能停在中间状态,必须单独成态
|
|
996
|
+
? 'rollbackIncomplete'
|
|
997
|
+
: !report.ok && report.rolledBack
|
|
998
|
+
? 'rolledBack'
|
|
999
|
+
: failedOnly
|
|
1000
|
+
? 'failed'
|
|
1001
|
+
: ineffective.length > 0
|
|
1002
|
+
? 'partial'
|
|
1003
|
+
: 'ok';
|
|
951
1004
|
const headline = kind === 'ok'
|
|
952
1005
|
? t('sync.importDone', { n: String(report.applied.length) })
|
|
953
1006
|
: kind === 'partial'
|
|
954
1007
|
// 不用「失败」措辞:这次同步**大部分成功**,只是有 N 项没生效 —— 措辞必须与事实同强度
|
|
955
1008
|
? t('sync.importPartial', { n: String(ineffective.length) })
|
|
956
|
-
|
|
1009
|
+
// 回滚未完成:UiT 字典没有这个键('sync.importFailed' 的文案断言「已整体回滚」),
|
|
1010
|
+
// 取 sync 字典的专用文案 —— 措辞与事实同强度,绝不把半回滚说成已回滚
|
|
1011
|
+
: kind === 'rollbackIncomplete'
|
|
1012
|
+
? syncText('syncflow.importRollbackIncomplete')
|
|
1013
|
+
: t('sync.importFailed');
|
|
957
1014
|
return {
|
|
958
1015
|
kind,
|
|
959
1016
|
headline,
|
|
@@ -962,6 +1019,7 @@ export function applyItemsReportView(
|
|
|
962
1019
|
ineffective,
|
|
963
1020
|
restoreId: report.restoreId,
|
|
964
1021
|
needsRestart: report.needsRestart,
|
|
1022
|
+
...(report.rollbackFull !== undefined ? { rollbackFull: report.rollbackFull } : {}),
|
|
965
1023
|
};
|
|
966
1024
|
}
|
|
967
1025
|
|
|
@@ -990,7 +1048,7 @@ export function computeAutosyncCountdown(elapsedMs: number, intervalMs: number):
|
|
|
990
1048
|
* 剩余时长 → 可读文案(向上取整,避免出现「0 分钟」;≤0 视为 1 分钟兜底)。
|
|
991
1049
|
* 例:4 分钟 →「4 分钟」;90 分钟 →「2 小时」;30 小时 →「2 天」。
|
|
992
1050
|
*/
|
|
993
|
-
export function formatIntervalDuration(ms: number, t: UiT
|
|
1051
|
+
export function formatIntervalDuration(ms: number, t: UiT): string {
|
|
994
1052
|
const totalMinutes = Math.max(1, Math.ceil(ms / 60000));
|
|
995
1053
|
if (totalMinutes < 60) return t('sync.duration.min', { n: totalMinutes });
|
|
996
1054
|
const totalHours = Math.ceil(totalMinutes / 60);
|
|
@@ -999,8 +1057,33 @@ export function formatIntervalDuration(ms: number, t: UiT = zhUiT): string {
|
|
|
999
1057
|
return t('sync.duration.day', { n: totalDays });
|
|
1000
1058
|
}
|
|
1001
1059
|
|
|
1060
|
+
/**
|
|
1061
|
+
* 自动同步状态行的「本轮跳过原因」明细(issue #79)。
|
|
1062
|
+
*
|
|
1063
|
+
* 为什么单独成行而不是塞进状态徽章:跳过原因可能带数量(远端另有 N 个分区待确认),
|
|
1064
|
+
* 徽章会挤成两行;且用户最需要的信息正是「为什么没同步」——改造前它只进 info 级日志
|
|
1065
|
+
* (默认日志级别 warn),界面完全看不到。
|
|
1066
|
+
*
|
|
1067
|
+
* 未知码回退宿主原串(与同步历史同口径,不吞信息,也绝不渲染裸回调码当文案)。
|
|
1068
|
+
*/
|
|
1069
|
+
export function autosyncSkipDetail(
|
|
1070
|
+
status: AutosyncStatusResponse,
|
|
1071
|
+
syncText: SyncTextFn = defaultSyncText,
|
|
1072
|
+
): string | null {
|
|
1073
|
+
const reason = status.lastRunSkipReason
|
|
1074
|
+
if (reason === undefined || reason === '') return null
|
|
1075
|
+
const key = skipReasonKey(reason)
|
|
1076
|
+
// 未知码:给可读兜底,**绝不**把裸码渲染成文案(历史行保留原串以便排查,状态行只要可读)
|
|
1077
|
+
const label = key === null ? syncText('history.autosyncReasonUnknown') : syncText(key)
|
|
1078
|
+
// 冲突:补一句「远端另有 N 个分区待确认」(数量来自宿主的结构化字段,不是从文案里抠)
|
|
1079
|
+
if (reason === 'conflict' && status.lastRunSkipCount !== undefined && status.lastRunSkipCount > 0) {
|
|
1080
|
+
return syncText('autosync.skipReasonConflictCount', { reason: label, n: String(status.lastRunSkipCount) })
|
|
1081
|
+
}
|
|
1082
|
+
return label
|
|
1083
|
+
}
|
|
1084
|
+
|
|
1002
1085
|
/** 自动同步状态行的可读文案(未运行 / 上次状态 / 连续失败计数)。 */
|
|
1003
|
-
export function autosyncStatusText(status: AutosyncStatusResponse, t: UiT
|
|
1086
|
+
export function autosyncStatusText(status: AutosyncStatusResponse, t: UiT): string {
|
|
1004
1087
|
if (status.lastRunAt === undefined || status.lastRunAt === '' || status.lastRunStatus === undefined) {
|
|
1005
1088
|
return t('sync.autosyncNever');
|
|
1006
1089
|
}
|
package/src/core/analyzer.ts
CHANGED
|
@@ -26,7 +26,7 @@ import type { FilesSection, Manifest, SectionId } from '../schema/types.ts';
|
|
|
26
26
|
import { loadTombstones, isTombstoned } from '../schema/tombstones.ts';
|
|
27
27
|
import type { Tombstone, TombstoneKind } from '../schema/tombstones.ts';
|
|
28
28
|
import { DEFAULT_SENSITIVE_RELS, restoreVaultFiles } from '../security/vault.ts';
|
|
29
|
-
import { createSnapshot, planItemWritesTarget, resolveFileTarget, resolveFileTargetRel } from './backup.ts';
|
|
29
|
+
import { createSnapshot, fsForSection, planItemWritesTarget, resolveFileTarget, resolveFileTargetRel } from './backup.ts';
|
|
30
30
|
import { isCredentialConfigured } from './credential-status.ts';
|
|
31
31
|
import { rollback } from './rollback.ts';
|
|
32
32
|
import { compatibilityReasons, computeCompatibility } from './validator.ts';
|
|
@@ -37,7 +37,7 @@ import {
|
|
|
37
37
|
ImportNotConfirmedError, ImportUserSkippedError, type ApplyResult, type ConfigAdapter,
|
|
38
38
|
type ExecutedItem, type HostContext, type ImportAnalysis, type ImportContext,
|
|
39
39
|
type ImportDecisions, type ImportPlan, type ImportResult, type PathIssue,
|
|
40
|
-
type PathMapping, type PlanItem, type SessionFormatProbeResult, type SkippedTombstone,
|
|
40
|
+
type FileSystemFacade, type PathMapping, type PlanItem, type SessionFormatProbeResult, type SkippedTombstone,
|
|
41
41
|
type Snapshot, type SnapshotStore, type TransactionSnapshotContext,
|
|
42
42
|
} from './types.ts';
|
|
43
43
|
|
|
@@ -953,7 +953,7 @@ export class Analyzer {
|
|
|
953
953
|
let beforeFp: string | null = null;
|
|
954
954
|
if (Analyzer.fileRelFor(item, importCtx.target.profile) !== null) {
|
|
955
955
|
rel = Analyzer.fileRelFor(item, importCtx.target.profile) as string;
|
|
956
|
-
beforeFp = await this.fileFp(importCtx,
|
|
956
|
+
beforeFp = await this.fileFp(importCtx, item);
|
|
957
957
|
}
|
|
958
958
|
beforeFpByItem.set(item.id, beforeFp);
|
|
959
959
|
await sb.recordStep({
|
|
@@ -1055,7 +1055,7 @@ export class Analyzer {
|
|
|
1055
1055
|
const rel = Analyzer.fileRelFor(item, importCtx.target.profile);
|
|
1056
1056
|
let afterFp: string | null = null;
|
|
1057
1057
|
if (rel !== null && outcome.executed.status === 'ok') {
|
|
1058
|
-
afterFp = await this.fileFp(importCtx,
|
|
1058
|
+
afterFp = await this.fileFp(importCtx, item);
|
|
1059
1059
|
}
|
|
1060
1060
|
await sb.recordStep({
|
|
1061
1061
|
id: item.id,
|
|
@@ -1382,7 +1382,7 @@ export class Analyzer {
|
|
|
1382
1382
|
return planItemWritesTarget(item);
|
|
1383
1383
|
}
|
|
1384
1384
|
|
|
1385
|
-
/** 文件类且可指纹 →
|
|
1385
|
+
/** 文件类且可指纹 → 返回「根相对路径」(journal 的 ref 身份);否则 null(不可指纹外部项)。
|
|
1386
1386
|
* profile 必须传入:plugins 分区的 pnpm-workspace.yaml / patch 文件位于
|
|
1387
1387
|
* `profiles/<profile>/` 下,缺了它算出的相对路径不存在 → 指纹恒 null(issue #35)。 */
|
|
1388
1388
|
private static fileRelFor(item: PlanItem, profile?: string): string | null {
|
|
@@ -1392,11 +1392,33 @@ export class Analyzer {
|
|
|
1392
1392
|
return resolveFileTargetRel(item.adapter, ref, profile);
|
|
1393
1393
|
}
|
|
1394
1394
|
|
|
1395
|
-
/**
|
|
1396
|
-
|
|
1395
|
+
/**
|
|
1396
|
+
* 文件类且可指纹 → 返回「该分区自己的门面 + 绝对路径」。
|
|
1397
|
+
*
|
|
1398
|
+
* 为什么不能只拿 home-relative 路径去读:`pluginData` / `pluginAssets` 的根是**系统 home**
|
|
1399
|
+
* (见 `core/backup.ts` 的 `fsForSection`),用 `ctx.fs` 读会落到 $DSH_HOME 下的错误位置
|
|
1400
|
+
* 或被越界守卫拦下 —— 指纹会静默变成「读不到」,reconcile 因此永远判不出是否已应用。
|
|
1401
|
+
* 宿主未提供该门面 / 路径不可解析 → null(保守:不可证明)。
|
|
1402
|
+
*/
|
|
1403
|
+
private static fileTargetFor(
|
|
1404
|
+
ctx: ImportContext, item: PlanItem,
|
|
1405
|
+
): { fs: FileSystemFacade; abs: string } | null {
|
|
1406
|
+
if (!isFileSection(item.adapter)) return null;
|
|
1407
|
+
const ref = item.target?.ref;
|
|
1408
|
+
if (ref === undefined || ref === '') return null;
|
|
1409
|
+
try {
|
|
1410
|
+
return { fs: fsForSection(ctx.target, item.adapter), abs: resolveFileTarget(ctx.target, item.adapter, ref) };
|
|
1411
|
+
} catch {
|
|
1412
|
+
return null;
|
|
1413
|
+
}
|
|
1414
|
+
}
|
|
1415
|
+
|
|
1416
|
+
/** 读目标文件算 sha256(经该分区自己的门面,测试 mock 可注入)。失败返回 null。 */
|
|
1417
|
+
private async fileFp(ctx: ImportContext, item: PlanItem): Promise<string | null> {
|
|
1418
|
+
const target = Analyzer.fileTargetFor(ctx, item);
|
|
1419
|
+
if (target === null) return null;
|
|
1397
1420
|
try {
|
|
1398
|
-
|
|
1399
|
-
return sha256Hex(data);
|
|
1421
|
+
return sha256Hex(await target.fs.readFile(target.abs));
|
|
1400
1422
|
} catch {
|
|
1401
1423
|
return null;
|
|
1402
1424
|
}
|
|
@@ -16,7 +16,8 @@ import fs from 'node:fs/promises';
|
|
|
16
16
|
import path from 'node:path';
|
|
17
17
|
|
|
18
18
|
import {
|
|
19
|
-
DEFAULT_BACKUP_SECTIONS, OFFLINE_BACKUP_SECTIONS,
|
|
19
|
+
DEFAULT_BACKUP_SECTIONS, OFFLINE_BACKUP_SECTIONS, OFFLINE_EXCLUDED_SECTIONS, OFFLINE_UNAVAILABLE_SECTIONS,
|
|
20
|
+
OPT_IN_BACKUP_SECTIONS,
|
|
20
21
|
buildSectionFlags,
|
|
21
22
|
} from './backup-plan.ts';
|
|
22
23
|
import { APPLY_ORDER } from './analyzer.ts';
|
|
@@ -105,20 +106,34 @@ test('离线分区清单:注册表所有分区都必须显式归类(可收
|
|
|
105
106
|
// 「明确不参与离线」:sessions 体量巨大且属用户数据(GUI 侧单独 opt-in)、secrets 无分区载荷
|
|
106
107
|
// (凭据值走独立加密容器)。二者都不在 CLI 离线备份的目标集合内 —— 但必须**显式**列出,
|
|
107
108
|
// 否则新增分区会被静默漏收/漏报。
|
|
108
|
-
|
|
109
|
+
// pluginData / pluginAssets 也在此列(第三方插件数据的收集依赖 adapter 的来源注册表;
|
|
110
|
+
// 见 backup-plan.ts 的 OFFLINE_EXCLUDED_SECTIONS —— 直接引用产品常量,避免测试与实现各抄一份)
|
|
111
|
+
const NOT_OFFLINE_APPLICABLE: readonly SectionId[] = [
|
|
112
|
+
'sessions', 'secrets', ...OFFLINE_EXCLUDED_SECTIONS,
|
|
113
|
+
];
|
|
109
114
|
const classified = new Set<string>([...OFFLINE_BACKUP_SECTIONS, ...OFFLINE_UNAVAILABLE_SECTIONS, ...NOT_OFFLINE_APPLICABLE]);
|
|
110
115
|
assert.deepEqual([...classified].sort(), [...SECTION_IDS].sort(), '新增分区必须在 backup-plan 里显式归类');
|
|
111
116
|
});
|
|
112
117
|
|
|
113
118
|
/* ------------------------------------------------------------ FILE_BASES */
|
|
114
119
|
|
|
115
|
-
test('resolveFileTarget
|
|
116
|
-
//
|
|
120
|
+
test('resolveFileTarget:文件类分区都有明确根($DSH_HOME 内,或系统 home 根的那两个分区)', () => {
|
|
121
|
+
// 文件本来就在 $DSH_HOME 根的分区:AGENTS.md / 插件自有文件白名单
|
|
117
122
|
const HOME_ROOT_SECTIONS: readonly SectionId[] = ['agentInstructions', 'pluginFiles'];
|
|
118
|
-
|
|
123
|
+
// **系统 home 为根**的分区(第三方插件数据):$DSH_HOME 之外的第二个根,见 backup.ts 的 fsForSection
|
|
124
|
+
const OS_HOME_SECTIONS: readonly SectionId[] = ['pluginData', 'pluginAssets'];
|
|
125
|
+
const ctx = {
|
|
126
|
+
homeDir: path.join('C:', 'home', '.dsh'),
|
|
127
|
+
osHomeDir: path.join('C:', 'home'),
|
|
128
|
+
profile: 'web',
|
|
129
|
+
} as unknown as HostContext;
|
|
119
130
|
const atHomeRoot = path.join(ctx.homeDir, 'probe.txt');
|
|
120
131
|
for (const id of SECTION_IDS.filter((s) => isFileSection(s))) {
|
|
121
132
|
const target = resolveFileTarget(ctx, id, 'probe.txt');
|
|
133
|
+
if (OS_HOME_SECTIONS.includes(id)) {
|
|
134
|
+
assert.equal(target, path.join(ctx.osHomeDir as string, 'probe.txt'), `${id} 的根必须是系统 home`);
|
|
135
|
+
continue;
|
|
136
|
+
}
|
|
122
137
|
assert.ok(target.startsWith(ctx.homeDir), `${id} 的目标必须在 homeDir 下: ${target}`);
|
|
123
138
|
if (HOME_ROOT_SECTIONS.includes(id)) {
|
|
124
139
|
assert.equal(target, atHomeRoot, `${id} 是 home 根分区`);
|
|
@@ -169,8 +184,36 @@ test('t37 skippedLinks 措辞:普通目录超深不得被描述成「链接」
|
|
|
169
184
|
assert.match(depthLine, /层级超过上限/, '普通目录超深的原因必须指向层级上限,实际:' + depthLine);
|
|
170
185
|
assert.doesNotMatch(depthLine, /链接未进备份/, '普通目录超深不得被描述成链接问题,实际:' + depthLine);
|
|
171
186
|
assert.match(linkLine, /链接未进备份/, '链接类跳过仍必须是链接措辞,实际:' + linkLine);
|
|
187
|
+
// issue #76 附带修正:内核返回的已是 homeDir 相对路径,**不得再叠一层分区前缀**(曾打印成 skills/skills/outlink)
|
|
188
|
+
assert.match(linkLine, /skills\/outlink(/, '路径必须只出现一次且为 homeDir 相对形态,实际:' + linkLine);
|
|
189
|
+
assert.doesNotMatch(linkLine, /skills\/skills\//, '不得叠加分区前缀(skills/skills/…),实际:' + linkLine);
|
|
172
190
|
} finally {
|
|
173
191
|
await fs.rm(home, { recursive: true, force: true });
|
|
174
192
|
await fs.rm(outside, { recursive: true, force: true });
|
|
175
193
|
}
|
|
176
194
|
});
|
|
195
|
+
|
|
196
|
+
test('issue #76:离线备份跳过 skills 下的 node_modules,并在 warnings 里说明(真机形态)', async () => {
|
|
197
|
+
const os = await import('node:os');
|
|
198
|
+
const { collectBackupEntries } = await import('./backup-plan.ts');
|
|
199
|
+
const home = await fs.mkdtemp(path.join(os.tmpdir(), 'dcm-bp-deps-'));
|
|
200
|
+
try {
|
|
201
|
+
const nm = path.join(home, 'skills', 'md2pdf', 'node_modules', 'marked');
|
|
202
|
+
await fs.mkdir(nm, { recursive: true });
|
|
203
|
+
await fs.writeFile(path.join(home, 'skills', 'md2pdf', 'SKILL.md'), 'S');
|
|
204
|
+
await fs.writeFile(path.join(home, 'skills', 'md2pdf', 'package.json'), '{}');
|
|
205
|
+
await fs.writeFile(path.join(nm, 'index.js'), 'M');
|
|
206
|
+
await fs.writeFile(path.join(nm, 'extra.js'), 'M');
|
|
207
|
+
|
|
208
|
+
const plan = await collectBackupEntries(home, ['skills']);
|
|
209
|
+
const names = plan.entries.map((e) => e.name);
|
|
210
|
+
assert.ok(names.some((n) => n.endsWith('md2pdf/SKILL.md')), '技能本体必须进归档: ' + names.join(','));
|
|
211
|
+
assert.ok(names.some((n) => n.endsWith('md2pdf/package.json')), 'package.json 必须进归档(目标机据此重建依赖)');
|
|
212
|
+
assert.deepEqual(names.filter((n) => n.includes('node_modules/')), [], '依赖树不得进归档: ' + names.join(','));
|
|
213
|
+
const line = plan.warnings.find((w) => w.includes('运行时依赖目录')) ?? '';
|
|
214
|
+
assert.ok(line.length > 0, '缺依赖目录告警: ' + plan.warnings.join(' | '));
|
|
215
|
+
assert.match(line, /2 个文件/, '必须报出被跳过的文件数量级');
|
|
216
|
+
} finally {
|
|
217
|
+
await fs.rm(home, { recursive: true, force: true });
|
|
218
|
+
}
|
|
219
|
+
});
|