@logictan/dsh-config-manager 0.1.60
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/LICENSE +21 -0
- package/README.md +105 -0
- package/cordis.patch.yml +21 -0
- package/lib/adapters/agent-instructions.d.ts +13 -0
- package/lib/adapters/agent-instructions.js +46 -0
- package/lib/adapters/agent-presets.d.ts +13 -0
- package/lib/adapters/agent-presets.js +14 -0
- package/lib/adapters/credentials.d.ts +37 -0
- package/lib/adapters/credentials.js +122 -0
- package/lib/adapters/file-collection.d.ts +15 -0
- package/lib/adapters/file-collection.js +127 -0
- package/lib/adapters/index.d.ts +57 -0
- package/lib/adapters/index.js +55 -0
- package/lib/adapters/link-report.d.ts +16 -0
- package/lib/adapters/link-report.js +47 -0
- package/lib/adapters/mcp.d.ts +28 -0
- package/lib/adapters/mcp.js +178 -0
- package/lib/adapters/plugin-files.d.ts +18 -0
- package/lib/adapters/plugin-files.js +142 -0
- package/lib/adapters/plugins.d.ts +41 -0
- package/lib/adapters/plugins.js +603 -0
- package/lib/adapters/pnpm-workspace.d.ts +26 -0
- package/lib/adapters/pnpm-workspace.js +147 -0
- package/lib/adapters/prompts.d.ts +30 -0
- package/lib/adapters/prompts.js +231 -0
- package/lib/adapters/providers.d.ts +34 -0
- package/lib/adapters/providers.js +155 -0
- package/lib/adapters/self.d.ts +17 -0
- package/lib/adapters/self.js +71 -0
- package/lib/adapters/sessions.d.ts +14 -0
- package/lib/adapters/sessions.js +15 -0
- package/lib/adapters/settings.d.ts +28 -0
- package/lib/adapters/settings.js +165 -0
- package/lib/adapters/skills.d.ts +13 -0
- package/lib/adapters/skills.js +14 -0
- package/lib/adapters/test-helpers.d.ts +125 -0
- package/lib/adapters/test-helpers.js +236 -0
- package/lib/adapters/ui.d.ts +27 -0
- package/lib/adapters/ui.js +89 -0
- package/lib/adapters/workspaces.d.ts +13 -0
- package/lib/adapters/workspaces.js +106 -0
- package/lib/client.d.ts +1026 -0
- package/lib/client.js +10047 -0
- package/lib/core/analyzer.d.ts +99 -0
- package/lib/core/analyzer.js +934 -0
- package/lib/core/backup.d.ts +138 -0
- package/lib/core/backup.js +513 -0
- package/lib/core/boot-rescue.d.ts +146 -0
- package/lib/core/boot-rescue.js +618 -0
- package/lib/core/cache-cleaner.d.ts +31 -0
- package/lib/core/cache-cleaner.js +95 -0
- package/lib/core/config-lifecycle.d.ts +180 -0
- package/lib/core/config-lifecycle.js +481 -0
- package/lib/core/config-snapshot.d.ts +135 -0
- package/lib/core/config-snapshot.js +424 -0
- package/lib/core/config-state.d.ts +70 -0
- package/lib/core/config-state.js +177 -0
- package/lib/core/consult-source.d.ts +58 -0
- package/lib/core/consult-source.js +260 -0
- package/lib/core/crash-report.d.ts +84 -0
- package/lib/core/crash-report.js +317 -0
- package/lib/core/exporter.d.ts +68 -0
- package/lib/core/exporter.js +375 -0
- package/lib/core/ghost-sweep.d.ts +19 -0
- package/lib/core/ghost-sweep.js +52 -0
- package/lib/core/importer.d.ts +60 -0
- package/lib/core/importer.js +29 -0
- package/lib/core/index.d.ts +14 -0
- package/lib/core/index.js +14 -0
- package/lib/core/journal.d.ts +197 -0
- package/lib/core/journal.js +418 -0
- package/lib/core/local-plugin-host.d.ts +35 -0
- package/lib/core/local-plugin-host.js +120 -0
- package/lib/core/local-plugin-pack.d.ts +135 -0
- package/lib/core/local-plugin-pack.js +278 -0
- package/lib/core/messages.d.ts +310 -0
- package/lib/core/messages.js +621 -0
- package/lib/core/migration-consult.d.ts +117 -0
- package/lib/core/migration-consult.js +363 -0
- package/lib/core/migration-history.d.ts +153 -0
- package/lib/core/migration-history.js +411 -0
- package/lib/core/model-tools.d.ts +50 -0
- package/lib/core/model-tools.js +148 -0
- package/lib/core/msg-types.d.ts +8 -0
- package/lib/core/msg-types.js +6 -0
- package/lib/core/phase3-child-crash.d.ts +1 -0
- package/lib/core/phase3-child-crash.js +87 -0
- package/lib/core/phase3-host.d.ts +138 -0
- package/lib/core/phase3-host.js +359 -0
- package/lib/core/phase3-prod-child.d.ts +1 -0
- package/lib/core/phase3-prod-child.js +35 -0
- package/lib/core/phase4-crash-child.d.ts +1 -0
- package/lib/core/phase4-crash-child.js +103 -0
- package/lib/core/plugin-cli.d.ts +150 -0
- package/lib/core/plugin-cli.js +497 -0
- package/lib/core/reconcile.d.ts +120 -0
- package/lib/core/reconcile.js +389 -0
- package/lib/core/recovery-orchestrator.d.ts +94 -0
- package/lib/core/recovery-orchestrator.js +327 -0
- package/lib/core/restore.d.ts +162 -0
- package/lib/core/restore.js +703 -0
- package/lib/core/rollback.d.ts +12 -0
- package/lib/core/rollback.js +145 -0
- package/lib/core/run-registry.d.ts +85 -0
- package/lib/core/run-registry.js +157 -0
- package/lib/core/startup-barrier.d.ts +53 -0
- package/lib/core/startup-barrier.js +77 -0
- package/lib/core/types.d.ts +515 -0
- package/lib/core/types.js +27 -0
- package/lib/core/undo.d.ts +73 -0
- package/lib/core/undo.js +87 -0
- package/lib/core/validator.d.ts +18 -0
- package/lib/core/validator.js +77 -0
- package/lib/core/verify-recovery.d.ts +21 -0
- package/lib/core/verify-recovery.js +256 -0
- package/lib/core/watcher.d.ts +101 -0
- package/lib/core/watcher.js +219 -0
- package/lib/index.d.ts +238 -0
- package/lib/index.js +2764 -0
- package/lib/migrations/index.d.ts +29 -0
- package/lib/migrations/index.js +56 -0
- package/lib/migrations/v1-to-v2.d.ts +9 -0
- package/lib/migrations/v1-to-v2.js +10 -0
- package/lib/profiles/index.d.ts +4 -0
- package/lib/profiles/index.js +5 -0
- package/lib/profiles/profile-manager.d.ts +106 -0
- package/lib/profiles/profile-manager.js +543 -0
- package/lib/schema/config.d.ts +28 -0
- package/lib/schema/config.js +165 -0
- package/lib/schema/index.d.ts +24 -0
- package/lib/schema/index.js +26 -0
- package/lib/schema/manifest.d.ts +28 -0
- package/lib/schema/manifest.js +109 -0
- package/lib/schema/tombstones.d.ts +46 -0
- package/lib/schema/tombstones.js +64 -0
- package/lib/schema/types.d.ts +232 -0
- package/lib/schema/types.js +8 -0
- package/lib/schema/versions.d.ts +27 -0
- package/lib/schema/versions.js +50 -0
- package/lib/security/encryption.d.ts +85 -0
- package/lib/security/encryption.js +279 -0
- package/lib/security/index.d.ts +16 -0
- package/lib/security/index.js +17 -0
- package/lib/security/integrity.d.ts +30 -0
- package/lib/security/integrity.js +78 -0
- package/lib/security/redaction.d.ts +26 -0
- package/lib/security/redaction.js +95 -0
- package/lib/security/secret-scanner.d.ts +86 -0
- package/lib/security/secret-scanner.js +404 -0
- package/lib/security/vault.d.ts +59 -0
- package/lib/security/vault.js +151 -0
- package/lib/security/zip-security.d.ts +27 -0
- package/lib/security/zip-security.js +154 -0
- package/lib/sync/autosync-config.d.ts +39 -0
- package/lib/sync/autosync-config.js +151 -0
- package/lib/sync/autosync-scheduler.d.ts +139 -0
- package/lib/sync/autosync-scheduler.js +564 -0
- package/lib/sync/fs.d.ts +18 -0
- package/lib/sync/fs.js +49 -0
- package/lib/sync/git/git-transport.d.ts +100 -0
- package/lib/sync/git/git-transport.js +437 -0
- package/lib/sync/github-auth.d.ts +131 -0
- package/lib/sync/github-auth.js +264 -0
- package/lib/sync/layout.d.ts +43 -0
- package/lib/sync/layout.js +203 -0
- package/lib/sync/snapshot-json.d.ts +9 -0
- package/lib/sync/snapshot-json.js +107 -0
- package/lib/sync/sync-config.d.ts +81 -0
- package/lib/sync/sync-config.js +281 -0
- package/lib/sync/sync-engine.d.ts +269 -0
- package/lib/sync/sync-engine.js +591 -0
- package/lib/sync/sync-history.d.ts +42 -0
- package/lib/sync/sync-history.js +78 -0
- package/lib/sync/sync-selection.d.ts +29 -0
- package/lib/sync/sync-selection.js +127 -0
- package/lib/sync/sync-session.d.ts +40 -0
- package/lib/sync/sync-session.js +54 -0
- package/lib/sync/sync-state.d.ts +37 -0
- package/lib/sync/sync-state.js +108 -0
- package/lib/sync/transport.d.ts +75 -0
- package/lib/sync/transport.js +55 -0
- package/lib/sync/ui-prefs.d.ts +34 -0
- package/lib/sync/ui-prefs.js +109 -0
- package/lib/sync/webdav/webdav-transport.d.ts +77 -0
- package/lib/sync/webdav/webdav-transport.js +451 -0
- package/lib/ui/errors.d.ts +19 -0
- package/lib/ui/errors.js +81 -0
- package/lib/ui/export-flow.d.ts +82 -0
- package/lib/ui/export-flow.js +98 -0
- package/lib/ui/i18n.d.ts +289 -0
- package/lib/ui/i18n.js +581 -0
- package/lib/ui/migration-consult-view.d.ts +91 -0
- package/lib/ui/migration-consult-view.js +115 -0
- package/lib/ui/progress.d.ts +38 -0
- package/lib/ui/progress.js +87 -0
- package/lib/ui/report.d.ts +23 -0
- package/lib/ui/report.js +153 -0
- package/lib/ui/test-helpers.d.ts +69 -0
- package/lib/ui/test-helpers.js +154 -0
- package/lib/ui/types.d.ts +257 -0
- package/lib/ui/types.js +21 -0
- package/lib/utils/atomic-write.d.ts +88 -0
- package/lib/utils/atomic-write.js +399 -0
- package/lib/utils/env-lock.d.ts +393 -0
- package/lib/utils/env-lock.js +930 -0
- package/lib/utils/hashing.d.ts +17 -0
- package/lib/utils/hashing.js +41 -0
- package/lib/utils/json.d.ts +24 -0
- package/lib/utils/json.js +76 -0
- package/lib/utils/logger.d.ts +39 -0
- package/lib/utils/logger.js +82 -0
- package/lib/utils/paths.d.ts +42 -0
- package/lib/utils/paths.js +189 -0
- package/lib/utils/proxy.d.ts +69 -0
- package/lib/utils/proxy.js +455 -0
- package/lib/utils/recursive-walk.d.ts +24 -0
- package/lib/utils/recursive-walk.js +134 -0
- package/lib/utils/zip.d.ts +54 -0
- package/lib/utils/zip.js +285 -0
- package/package.json +188 -0
- package/src/adapters/agent-instructions.ts +48 -0
- package/src/adapters/agent-presets.ts +14 -0
- package/src/adapters/credentials.test.ts +90 -0
- package/src/adapters/credentials.ts +143 -0
- package/src/adapters/file-collection.ts +137 -0
- package/src/adapters/files.test.ts +353 -0
- package/src/adapters/index.ts +94 -0
- package/src/adapters/link-report.test.ts +65 -0
- package/src/adapters/link-report.ts +61 -0
- package/src/adapters/mcp.test.ts +96 -0
- package/src/adapters/mcp.ts +185 -0
- package/src/adapters/plugin-files.ts +146 -0
- package/src/adapters/plugins.test.ts +326 -0
- package/src/adapters/plugins.ts +615 -0
- package/src/adapters/pnpm-workspace.test.ts +87 -0
- package/src/adapters/pnpm-workspace.ts +150 -0
- package/src/adapters/prompts.test.ts +110 -0
- package/src/adapters/prompts.ts +230 -0
- package/src/adapters/providers.test.ts +126 -0
- package/src/adapters/providers.ts +173 -0
- package/src/adapters/roundtrip.test.ts +190 -0
- package/src/adapters/self.test.ts +92 -0
- package/src/adapters/self.ts +75 -0
- package/src/adapters/sessions.ts +15 -0
- package/src/adapters/settings.test.ts +100 -0
- package/src/adapters/settings.ts +187 -0
- package/src/adapters/skills.ts +14 -0
- package/src/adapters/test-helpers.ts +248 -0
- package/src/adapters/ui.test.ts +57 -0
- package/src/adapters/ui.ts +108 -0
- package/src/adapters/workspaces.test.ts +106 -0
- package/src/adapters/workspaces.ts +109 -0
- package/src/client/ConfigManagerSection.tsx +90 -0
- package/src/client/api.ts +66 -0
- package/src/client/client-types.ts +18 -0
- package/src/client/common/ErrorBanner.tsx +73 -0
- package/src/client/common/Icon.tsx +129 -0
- package/src/client/common/Modal.tsx +173 -0
- package/src/client/common/ToastViewport.tsx +73 -0
- package/src/client/common/toast-store.test.ts +192 -0
- package/src/client/common/toast-store.ts +181 -0
- package/src/client/common/toast-types.ts +7 -0
- package/src/client/common/ui.tsx +342 -0
- package/src/client/config-manager.module.css +2971 -0
- package/src/client/css-modules.d.ts +8 -0
- package/src/client/index.ts +65 -0
- package/src/client/locales.ts +56 -0
- package/src/client/lucide-icons.d.ts +38 -0
- package/src/client/run-store.test.ts +223 -0
- package/src/client/run-store.ts +372 -0
- package/src/client/sync/SyncConfirmView.tsx +379 -0
- package/src/client/sync/SyncHistoryView.test.ts +40 -0
- package/src/client/sync/SyncHistoryView.tsx +220 -0
- package/src/client/sync/SyncSettingsView.tsx +1535 -0
- package/src/client/sync/history-model.test.ts +194 -0
- package/src/client/sync/history-model.ts +195 -0
- package/src/client/sync/sync-api.test.ts +585 -0
- package/src/client/sync/sync-api.ts +543 -0
- package/src/client/sync/sync-locales.ts +416 -0
- package/src/client/sync/sync-push-preview.test.ts +52 -0
- package/src/client/sync/sync-view-v2.test.ts +213 -0
- package/src/client/sync/sync-view.test.ts +487 -0
- package/src/client/sync/sync-view.ts +759 -0
- package/src/core/analyzer.ts +1068 -0
- package/src/core/backup.ts +605 -0
- package/src/core/boot-rescue.test.ts +847 -0
- package/src/core/boot-rescue.ts +737 -0
- package/src/core/cache-cleaner.test.ts +169 -0
- package/src/core/cache-cleaner.ts +121 -0
- package/src/core/config-lifecycle.test.ts +727 -0
- package/src/core/config-lifecycle.ts +573 -0
- package/src/core/config-snapshot.test.ts +369 -0
- package/src/core/config-snapshot.ts +522 -0
- package/src/core/config-state.test.ts +381 -0
- package/src/core/config-state.ts +207 -0
- package/src/core/consult-source.test.ts +199 -0
- package/src/core/consult-source.ts +293 -0
- package/src/core/crash-report.test.ts +380 -0
- package/src/core/crash-report.ts +369 -0
- package/src/core/exporter.ts +417 -0
- package/src/core/ghost-sweep.test.ts +94 -0
- package/src/core/ghost-sweep.ts +67 -0
- package/src/core/importer.ts +83 -0
- package/src/core/index.ts +56 -0
- package/src/core/journal.test.ts +257 -0
- package/src/core/journal.ts +518 -0
- package/src/core/local-plugin-host.ts +152 -0
- package/src/core/local-plugin-pack.test.ts +359 -0
- package/src/core/local-plugin-pack.ts +363 -0
- package/src/core/messages.test.ts +54 -0
- package/src/core/messages.ts +664 -0
- package/src/core/migration-consult.test.ts +315 -0
- package/src/core/migration-consult.ts +494 -0
- package/src/core/migration-history.test.ts +319 -0
- package/src/core/migration-history.ts +484 -0
- package/src/core/model-tools.test.ts +190 -0
- package/src/core/model-tools.ts +208 -0
- package/src/core/msg-types.ts +10 -0
- package/src/core/phase3-child-crash.ts +95 -0
- package/src/core/phase3-host.ts +411 -0
- package/src/core/phase3-p1.test.ts +212 -0
- package/src/core/phase3-p2b-fingerprint.test.ts +189 -0
- package/src/core/phase3-prod-child.ts +36 -0
- package/src/core/phase3-production-integration.test.ts +286 -0
- package/src/core/phase4-crash-child.ts +101 -0
- package/src/core/phase4-crash-injection.test.ts +166 -0
- package/src/core/plugin-cli.fs.test.ts +184 -0
- package/src/core/plugin-cli.test.ts +137 -0
- package/src/core/plugin-cli.ts +563 -0
- package/src/core/reconcile.test.ts +392 -0
- package/src/core/reconcile.ts +498 -0
- package/src/core/recovery-orchestrator.test.ts +145 -0
- package/src/core/recovery-orchestrator.ts +366 -0
- package/src/core/restore-manage.test.ts +94 -0
- package/src/core/restore.ts +849 -0
- package/src/core/rollback.ts +151 -0
- package/src/core/run-progress.test.ts +237 -0
- package/src/core/run-registry.test.ts +232 -0
- package/src/core/run-registry.ts +209 -0
- package/src/core/smoke.test.ts +842 -0
- package/src/core/startup-barrier.ts +99 -0
- package/src/core/types.ts +542 -0
- package/src/core/undo.ts +116 -0
- package/src/core/validator.ts +84 -0
- package/src/core/verify-recovery.ts +265 -0
- package/src/core/watcher.test.ts +326 -0
- package/src/core/watcher.ts +263 -0
- package/src/index.facade.test.ts +265 -0
- package/src/index.sync.test.ts +217 -0
- package/src/index.ts +2975 -0
- package/src/migrations/index.ts +71 -0
- package/src/migrations/v1-to-v2.ts +17 -0
- package/src/profiles/index.ts +8 -0
- package/src/profiles/profile-manager.test.ts +282 -0
- package/src/profiles/profile-manager.ts +630 -0
- package/src/schema/config.ts +165 -0
- package/src/schema/index.ts +71 -0
- package/src/schema/manifest-schema.test.ts +519 -0
- package/src/schema/manifest.ts +124 -0
- package/src/schema/tombstones.test.ts +131 -0
- package/src/schema/tombstones.ts +89 -0
- package/src/schema/types.ts +245 -0
- package/src/schema/versions.ts +55 -0
- package/src/security/encryption.ts +335 -0
- package/src/security/index.ts +16 -0
- package/src/security/integrity.ts +102 -0
- package/src/security/redaction.ts +101 -0
- package/src/security/secret-scanner.ts +445 -0
- package/src/security/security.test.ts +1215 -0
- package/src/security/vault.test.ts +164 -0
- package/src/security/vault.ts +198 -0
- package/src/security/zip-security.ts +180 -0
- package/src/sync/autosync-config.test.ts +147 -0
- package/src/sync/autosync-config.ts +179 -0
- package/src/sync/autosync-scheduler.test.ts +490 -0
- package/src/sync/autosync-scheduler.ts +646 -0
- package/src/sync/fs.ts +48 -0
- package/src/sync/git/git-transport.test.ts +673 -0
- package/src/sync/git/git-transport.ts +491 -0
- package/src/sync/github-auth.test.ts +280 -0
- package/src/sync/github-auth.ts +356 -0
- package/src/sync/layout.test.ts +270 -0
- package/src/sync/layout.ts +222 -0
- package/src/sync/snapshot-json.test.ts +119 -0
- package/src/sync/snapshot-json.ts +128 -0
- package/src/sync/sync-config.test.ts +310 -0
- package/src/sync/sync-config.ts +299 -0
- package/src/sync/sync-engine.test.ts +1074 -0
- package/src/sync/sync-engine.ts +754 -0
- package/src/sync/sync-history.test.ts +116 -0
- package/src/sync/sync-history.ts +120 -0
- package/src/sync/sync-selection.test.ts +186 -0
- package/src/sync/sync-selection.ts +143 -0
- package/src/sync/sync-session.test.ts +143 -0
- package/src/sync/sync-session.ts +77 -0
- package/src/sync/sync-state.test.ts +164 -0
- package/src/sync/sync-state.ts +131 -0
- package/src/sync/transport.test.ts +111 -0
- package/src/sync/transport.ts +118 -0
- package/src/sync/ui-prefs.test.ts +152 -0
- package/src/sync/ui-prefs.ts +135 -0
- package/src/sync/webdav/webdav-transport.test.ts +871 -0
- package/src/sync/webdav/webdav-transport.ts +535 -0
- package/src/ui/errors.test.ts +47 -0
- package/src/ui/errors.ts +100 -0
- package/src/ui/export-flow.test.ts +87 -0
- package/src/ui/export-flow.ts +156 -0
- package/src/ui/i18n.ts +590 -0
- package/src/ui/migration-consult-view.test.ts +151 -0
- package/src/ui/migration-consult-view.ts +178 -0
- package/src/ui/progress.test.ts +47 -0
- package/src/ui/progress.ts +102 -0
- package/src/ui/report.test.ts +101 -0
- package/src/ui/report.ts +146 -0
- package/src/ui/test-helpers.ts +184 -0
- package/src/ui/types.ts +325 -0
- package/src/utils/atomic-write.test.ts +312 -0
- package/src/utils/atomic-write.ts +422 -0
- package/src/utils/env-lock.test.ts +999 -0
- package/src/utils/env-lock.ts +1086 -0
- package/src/utils/hashing.ts +53 -0
- package/src/utils/json.ts +77 -0
- package/src/utils/logger.ts +114 -0
- package/src/utils/paths.ts +176 -0
- package/src/utils/proxy.test.ts +465 -0
- package/src/utils/proxy.ts +493 -0
- package/src/utils/recursive-walk.test.ts +148 -0
- package/src/utils/recursive-walk.ts +145 -0
- package/src/utils/zip.ts +332 -0
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import type { Manifest, SectionId } from '../schema/types.ts';
|
|
2
|
+
import type { ManifestIssue } from '../schema/manifest.ts';
|
|
3
|
+
import type { SensitiveHit } from './types.ts';
|
|
4
|
+
export type ConsultSourceType = 'export-zip' | 'local-snapshot' | 'remote-snapshot' | 'profile';
|
|
5
|
+
export type HealthVerdict = 'healthy' | 'needs-attention' | 'critical';
|
|
6
|
+
export type Recommendation = 'proceed' | 'review' | 'block';
|
|
7
|
+
export type ConsultDimensionId = 'compatibility' | 'integrity' | 'sections' | 'consistency' | 'sensitive' | 'migratability';
|
|
8
|
+
export interface ConsultSourceRef {
|
|
9
|
+
type: ConsultSourceType;
|
|
10
|
+
/** 源标识:ZIP 路径 / snapshotId / 远端快照 id / profile 名 */
|
|
11
|
+
id: string;
|
|
12
|
+
/** 关联 snapshotId(local-snapshot / remote-snapshot) */
|
|
13
|
+
snapshotId?: string;
|
|
14
|
+
}
|
|
15
|
+
export interface ConsultIssue {
|
|
16
|
+
severity: 'info' | 'warning' | 'error';
|
|
17
|
+
/** 稳定问题码(i18n 键后缀;如 'integrity.manifestInvalid') */
|
|
18
|
+
code: string;
|
|
19
|
+
/** 已 redact 的可读消息(宿主/UI 层负责脱敏) */
|
|
20
|
+
message: string;
|
|
21
|
+
/** 已 redact 的证据(可选) */
|
|
22
|
+
evidence?: string;
|
|
23
|
+
}
|
|
24
|
+
export interface HealthDimension {
|
|
25
|
+
id: ConsultDimensionId;
|
|
26
|
+
/** 0-100 */
|
|
27
|
+
score: number;
|
|
28
|
+
verdict: HealthVerdict;
|
|
29
|
+
issues: ConsultIssue[];
|
|
30
|
+
}
|
|
31
|
+
/** 可迁移性(宿主经 analyzeImport / createImportPlan / analyzeSwitch 计算;null = 未评估) */
|
|
32
|
+
export interface MigratabilityResult {
|
|
33
|
+
ok: boolean;
|
|
34
|
+
itemCount: number;
|
|
35
|
+
fatalConflicts: number;
|
|
36
|
+
warnings: number;
|
|
37
|
+
sections: SectionId[];
|
|
38
|
+
errors: string[];
|
|
39
|
+
}
|
|
40
|
+
/** 归一化后的源数据(宿主按源类型读取填充;核心只做评分) */
|
|
41
|
+
export interface ConsultSourceData {
|
|
42
|
+
source: ConsultSourceRef;
|
|
43
|
+
manifest: Manifest | null;
|
|
44
|
+
manifestIssues: ManifestIssue[];
|
|
45
|
+
sections: Map<SectionId, unknown>;
|
|
46
|
+
sectionFiles: Map<string, Uint8Array>;
|
|
47
|
+
checksums: Record<string, string> | null;
|
|
48
|
+
checksumIssues: string[];
|
|
49
|
+
zipSlipIssues: string[];
|
|
50
|
+
encrypted: boolean;
|
|
51
|
+
containsSecrets: boolean;
|
|
52
|
+
sourceDsh: string;
|
|
53
|
+
sourcePlatform: string;
|
|
54
|
+
schemaVersion: number;
|
|
55
|
+
missingSections: SectionId[];
|
|
56
|
+
/** 敏感暴露面(宿主经 scanAndRedact 统计;只含路径/字段名,不含值) */
|
|
57
|
+
sensitiveHits: SensitiveHit[];
|
|
58
|
+
/** 可迁移性(宿主计算;null = 未评估) */
|
|
59
|
+
migratability: MigratabilityResult | null;
|
|
60
|
+
}
|
|
61
|
+
export interface ConsultTarget {
|
|
62
|
+
targetDsh: string;
|
|
63
|
+
targetPlatform: string;
|
|
64
|
+
}
|
|
65
|
+
export interface ConsultOptions {
|
|
66
|
+
/** block 是否允许(health=critical 且此 true 时 recommendation=block;缺省 false) */
|
|
67
|
+
allowBlock?: boolean;
|
|
68
|
+
/** 各维度权重(缺省内置;总和应为 1) */
|
|
69
|
+
weights?: Partial<Record<ConsultDimensionId, number>>;
|
|
70
|
+
}
|
|
71
|
+
export interface ConsultWillApply {
|
|
72
|
+
sections: SectionId[];
|
|
73
|
+
itemCount: number;
|
|
74
|
+
conflicts: number;
|
|
75
|
+
risks: number;
|
|
76
|
+
overwritten: number;
|
|
77
|
+
/** 咨询恒为 dry-run(只读分析) */
|
|
78
|
+
dryRun: boolean;
|
|
79
|
+
}
|
|
80
|
+
export interface ConsultReport {
|
|
81
|
+
source: ConsultSourceRef;
|
|
82
|
+
/** 0-100 */
|
|
83
|
+
healthScore: number;
|
|
84
|
+
verdict: HealthVerdict;
|
|
85
|
+
recommendation: Recommendation;
|
|
86
|
+
/** 触发项(已 redact) */
|
|
87
|
+
recommendationReasons: string[];
|
|
88
|
+
dimensions: HealthDimension[];
|
|
89
|
+
willApply: ConsultWillApply;
|
|
90
|
+
bound: {
|
|
91
|
+
manifest?: Manifest;
|
|
92
|
+
snapshotId?: string;
|
|
93
|
+
sourceId: string;
|
|
94
|
+
};
|
|
95
|
+
generatedAt: string;
|
|
96
|
+
}
|
|
97
|
+
/** 内置维度权重(总和 = 1) */
|
|
98
|
+
export declare const DEFAULT_DIMENSION_WEIGHTS: Record<ConsultDimensionId, number>;
|
|
99
|
+
/** 维度 verdict 阈值:score>=90 → healthy;60<=score<90 → needs-attention;score<60 → critical */
|
|
100
|
+
export declare const HEALTHY_THRESHOLD = 90;
|
|
101
|
+
export declare const CRITICAL_THRESHOLD = 60;
|
|
102
|
+
/** 兼容性维度:复用 computeCompatibility(扩展而非重写) */
|
|
103
|
+
export declare function scoreCompatibility(data: ConsultSourceData, target: ConsultTarget): HealthDimension;
|
|
104
|
+
/** 结构完整性维度:manifest 合法 + checksums 匹配 + 无 Zip Slip */
|
|
105
|
+
export declare function scoreIntegrity(data: ConsultSourceData): HealthDimension;
|
|
106
|
+
/** 分区完整性维度:每个声明分区的数据可解析、无残缺 */
|
|
107
|
+
export declare function scoreSections(data: ConsultSourceData): HealthDimension;
|
|
108
|
+
/** 一致性维度:跨分区引用(凭据引用)是否自洽;孤立/悬空引用 */
|
|
109
|
+
export declare function scoreConsistency(data: ConsultSourceData): HealthDimension;
|
|
110
|
+
/** 敏感暴露维度:快照是否含未加密 secret、高熵 token 泄漏面 */
|
|
111
|
+
export declare function scoreSensitive(data: ConsultSourceData): HealthDimension;
|
|
112
|
+
/** 可迁移性维度:dry-run 应用能否通过(无致命冲突) */
|
|
113
|
+
export declare function scoreMigratability(data: ConsultSourceData): HealthDimension;
|
|
114
|
+
/** 计算全部维度(确定性) */
|
|
115
|
+
export declare function computeDimensions(data: ConsultSourceData, target: ConsultTarget, weights?: Record<ConsultDimensionId, number>): HealthDimension[];
|
|
116
|
+
/** 汇总健康评分 + verdict + recommendation(确定性) */
|
|
117
|
+
export declare function computeConsultReport(data: ConsultSourceData, target: ConsultTarget, opts?: ConsultOptions): ConsultReport;
|
|
@@ -0,0 +1,363 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 迁移前咨询(Phase 7):健康评分 + 咨询报告模型(纯函数,可测)。
|
|
3
|
+
*
|
|
4
|
+
* 三段式引擎第一段:迁移前咨询(Manifest + 健康评分)→ 迁移中事务(Phase 3 Journal)
|
|
5
|
+
* → 迁移后可审计(Phase 6 迁移历史)。本模块是**只读分析层**,不新建任何持久化 framework。
|
|
6
|
+
*
|
|
7
|
+
* Core Invariants:
|
|
8
|
+
* - READ-ONLY:本模块纯函数,绝不产生 mutation(不写配置/快照/journal)。
|
|
9
|
+
* - ACCURATE:评分规则驱动、确定性(同一源任何时候评分一致);verdict 与评分/证据一致。
|
|
10
|
+
* - COMPLETE:覆盖全部 4 种可迁移源类型(export-zip / local-snapshot / remote-snapshot / profile)。
|
|
11
|
+
* - REDACTED:报告绝不含 secret/凭据/敏感值(宿主在填充 sensitiveHits 时已脱敏;
|
|
12
|
+
* 渲染前由 UI 层 redact() 兜底)。
|
|
13
|
+
* - ACTIONABLE:产出 proceed / review / block 三态建议并能解释为什么(recommendationReasons)。
|
|
14
|
+
* - BOUND:报告关联 manifest / snapshotId / 源标识(可追溯)。
|
|
15
|
+
* - NON-BLOCKING 安全:咨询是建议性;默认 review 级不强制阻断,health=critical 且
|
|
16
|
+
* allowBlock=true 时可 block(block 逻辑可回退/可覆盖,且记录在案)。
|
|
17
|
+
*
|
|
18
|
+
* 复用现有:computeCompatibility(扩展而非重写)、validateManifest、scanAndRedact、
|
|
19
|
+
* integrity/zip-security、import-wizard 分析管线(宿主填充 migratability)。
|
|
20
|
+
*/
|
|
21
|
+
import { computeCompatibility } from './validator.js';
|
|
22
|
+
import { redact } from '../security/redaction.js';
|
|
23
|
+
/* ---------------- 常量 ---------------- */
|
|
24
|
+
/** 内置维度权重(总和 = 1) */
|
|
25
|
+
export const DEFAULT_DIMENSION_WEIGHTS = {
|
|
26
|
+
compatibility: 0.20,
|
|
27
|
+
integrity: 0.25,
|
|
28
|
+
sections: 0.20,
|
|
29
|
+
consistency: 0.10,
|
|
30
|
+
sensitive: 0.15,
|
|
31
|
+
migratability: 0.10,
|
|
32
|
+
};
|
|
33
|
+
/** 维度 verdict 阈值:score>=90 → healthy;60<=score<90 → needs-attention;score<60 → critical */
|
|
34
|
+
export const HEALTHY_THRESHOLD = 90;
|
|
35
|
+
export const CRITICAL_THRESHOLD = 60;
|
|
36
|
+
/* ---------------- 评分辅助 ---------------- */
|
|
37
|
+
function dimensionVerdict(score) {
|
|
38
|
+
if (score >= HEALTHY_THRESHOLD)
|
|
39
|
+
return 'healthy';
|
|
40
|
+
if (score >= CRITICAL_THRESHOLD)
|
|
41
|
+
return 'needs-attention';
|
|
42
|
+
return 'critical';
|
|
43
|
+
}
|
|
44
|
+
function clampScore(score) {
|
|
45
|
+
return Math.max(0, Math.min(100, Math.round(score)));
|
|
46
|
+
}
|
|
47
|
+
function worstVerdict(a, b) {
|
|
48
|
+
const rank = { healthy: 0, 'needs-attention': 1, critical: 2 };
|
|
49
|
+
return rank[a] >= rank[b] ? a : b;
|
|
50
|
+
}
|
|
51
|
+
/* ---------------- 各维度评分 ---------------- */
|
|
52
|
+
/** 兼容性维度:复用 computeCompatibility(扩展而非重写) */
|
|
53
|
+
export function scoreCompatibility(data, target) {
|
|
54
|
+
const issues = [];
|
|
55
|
+
let score;
|
|
56
|
+
let compat;
|
|
57
|
+
if (data.manifest === null) {
|
|
58
|
+
compat = 'unsupported';
|
|
59
|
+
score = 0;
|
|
60
|
+
issues.push({ severity: 'error', code: 'compatibility.noManifest', message: '无法评估兼容性:manifest 缺失' });
|
|
61
|
+
}
|
|
62
|
+
else {
|
|
63
|
+
compat = computeCompatibility({
|
|
64
|
+
sourceDsh: data.sourceDsh,
|
|
65
|
+
targetDsh: target.targetDsh,
|
|
66
|
+
sourcePlatform: data.sourcePlatform,
|
|
67
|
+
targetPlatform: target.targetPlatform,
|
|
68
|
+
schemaVersion: data.schemaVersion,
|
|
69
|
+
missingSections: data.missingSections,
|
|
70
|
+
});
|
|
71
|
+
switch (compat) {
|
|
72
|
+
case 'excellent':
|
|
73
|
+
score = 100;
|
|
74
|
+
break;
|
|
75
|
+
case 'good':
|
|
76
|
+
score = 90;
|
|
77
|
+
break;
|
|
78
|
+
case 'partial':
|
|
79
|
+
score = 60;
|
|
80
|
+
issues.push({ severity: 'warning', code: 'compatibility.partial', message: '部分兼容:跨平台 / 分区缺失 / 版本超前,需人工确认' });
|
|
81
|
+
break;
|
|
82
|
+
case 'unsupported':
|
|
83
|
+
score = 0;
|
|
84
|
+
issues.push({ severity: 'error', code: 'compatibility.unsupported', message: '不受支持:schema 超出本插件支持范围' });
|
|
85
|
+
break;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
return { id: 'compatibility', score, verdict: dimensionVerdict(score), issues };
|
|
89
|
+
}
|
|
90
|
+
/** 结构完整性维度:manifest 合法 + checksums 匹配 + 无 Zip Slip */
|
|
91
|
+
export function scoreIntegrity(data) {
|
|
92
|
+
const issues = [];
|
|
93
|
+
let score = 100;
|
|
94
|
+
if (data.manifest === null) {
|
|
95
|
+
score = 0;
|
|
96
|
+
issues.push({ severity: 'error', code: 'integrity.noManifest', message: 'manifest 缺失,无法校验结构完整性' });
|
|
97
|
+
}
|
|
98
|
+
else {
|
|
99
|
+
for (const issue of data.manifestIssues) {
|
|
100
|
+
if (issue.severity === 'error') {
|
|
101
|
+
score -= 30;
|
|
102
|
+
issues.push({ severity: 'error', code: 'integrity.manifestInvalid', message: `manifest 无效:${issue.message}`, evidence: issue.path });
|
|
103
|
+
}
|
|
104
|
+
else {
|
|
105
|
+
score -= 10;
|
|
106
|
+
issues.push({ severity: 'warning', code: 'integrity.manifestWarning', message: `manifest 警告:${issue.message}`, evidence: issue.path });
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
for (const c of data.checksumIssues) {
|
|
111
|
+
score -= 30;
|
|
112
|
+
issues.push({ severity: 'error', code: 'integrity.checksumMismatch', message: `完整性校验失败:${c}` });
|
|
113
|
+
}
|
|
114
|
+
// 源无 checksum 表(manifest 存在但 checksums 缺失)→ warning(轻度完整性顾虑)
|
|
115
|
+
if (data.manifest !== null && data.checksums === null) {
|
|
116
|
+
score -= 10;
|
|
117
|
+
issues.push({ severity: 'warning', code: 'integrity.noChecksums', message: '源缺少 checksum 表,无法校验条目完整性' });
|
|
118
|
+
}
|
|
119
|
+
for (const z of data.zipSlipIssues) {
|
|
120
|
+
score -= 30;
|
|
121
|
+
issues.push({ severity: 'error', code: 'integrity.zipSlip', message: `ZIP 路径越界:${z}` });
|
|
122
|
+
}
|
|
123
|
+
return { id: 'integrity', score: clampScore(score), verdict: dimensionVerdict(clampScore(score)), issues };
|
|
124
|
+
}
|
|
125
|
+
/** 分区完整性维度:每个声明分区的数据可解析、无残缺 */
|
|
126
|
+
export function scoreSections(data) {
|
|
127
|
+
const issues = [];
|
|
128
|
+
let score = 100;
|
|
129
|
+
for (const sid of data.missingSections) {
|
|
130
|
+
score -= 20;
|
|
131
|
+
issues.push({ severity: 'warning', code: 'sections.missing', message: `声明包含但数据缺失:${sid}`, evidence: sid });
|
|
132
|
+
}
|
|
133
|
+
// 分区数据不可解析(manifest 声明但 sections 中无对应数据且非 missingSections 覆盖)
|
|
134
|
+
if (data.manifest !== null) {
|
|
135
|
+
for (const [sid, enabled] of Object.entries(data.manifest.sections)) {
|
|
136
|
+
if (!enabled)
|
|
137
|
+
continue;
|
|
138
|
+
if (data.missingSections.includes(sid))
|
|
139
|
+
continue;
|
|
140
|
+
if (!data.sections.has(sid)) {
|
|
141
|
+
score -= 30;
|
|
142
|
+
issues.push({ severity: 'error', code: 'sections.unparseable', message: `分区数据不可解析:${sid}`, evidence: sid });
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
return { id: 'sections', score: clampScore(score), verdict: dimensionVerdict(clampScore(score)), issues };
|
|
147
|
+
}
|
|
148
|
+
/* ---------------- 一致性检查(凭据引用) ---------------- */
|
|
149
|
+
/** 从 settings 分区提取凭据引用名(apiKeyEnv / 引用类字段值) */
|
|
150
|
+
function extractSettingsCredentialRefs(data) {
|
|
151
|
+
const refs = new Set();
|
|
152
|
+
const settings = data.sections.get('settings');
|
|
153
|
+
const REFERENCE_REF_FIELDS = new Set([
|
|
154
|
+
'apikeyenv', 'api_key_env', 'apikeyname', 'tokenenv', 'accesstokenenv',
|
|
155
|
+
'refreshtokenenv', 'clientsecretenv', 'passwordenv',
|
|
156
|
+
]);
|
|
157
|
+
for (const rec of Object.values(settings?.namespaces ?? {})) {
|
|
158
|
+
const value = (rec.value ?? {});
|
|
159
|
+
for (const [k, v] of Object.entries(value)) {
|
|
160
|
+
const norm = k.toLowerCase().replace(/[^a-z0-9]/g, '');
|
|
161
|
+
if (REFERENCE_REF_FIELDS.has(norm) && typeof v === 'string' && v !== '')
|
|
162
|
+
refs.add(v);
|
|
163
|
+
}
|
|
164
|
+
if (typeof value['apiKeyEnv'] === 'string' && value['apiKeyEnv'] !== '')
|
|
165
|
+
refs.add(value['apiKeyEnv']);
|
|
166
|
+
const providers = value['providers'];
|
|
167
|
+
if (providers !== null && typeof providers === 'object') {
|
|
168
|
+
for (const pv of Object.values(providers)) {
|
|
169
|
+
if (pv !== null && typeof pv === 'object' && typeof pv.apiKeyEnv === 'string' && pv.apiKeyEnv !== '') {
|
|
170
|
+
refs.add(pv.apiKeyEnv);
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
return [...refs];
|
|
176
|
+
}
|
|
177
|
+
/** 从 providers 分区提取凭据引用名(providers[route].apiKeyEnv) */
|
|
178
|
+
function extractProvidersCredentialRefs(data) {
|
|
179
|
+
const refs = new Set();
|
|
180
|
+
const providers = data.sections.get('providers');
|
|
181
|
+
for (const pv of Object.values(providers?.providers ?? {})) {
|
|
182
|
+
if (pv !== null && typeof pv === 'object' && typeof pv.apiKeyEnv === 'string' && pv.apiKeyEnv !== '') {
|
|
183
|
+
refs.add(pv.apiKeyEnv);
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
return [...refs];
|
|
187
|
+
}
|
|
188
|
+
/** 从 credentialsStatus 分区提取已声明 ref 集 */
|
|
189
|
+
function extractDeclaredCredentialRefs(data) {
|
|
190
|
+
const refs = new Set();
|
|
191
|
+
const creds = data.sections.get('credentialsStatus');
|
|
192
|
+
for (const c of creds?.credentials ?? []) {
|
|
193
|
+
if (typeof c?.ref === 'string' && c.ref !== '')
|
|
194
|
+
refs.add(c.ref);
|
|
195
|
+
}
|
|
196
|
+
return refs;
|
|
197
|
+
}
|
|
198
|
+
/** 一致性维度:跨分区引用(凭据引用)是否自洽;孤立/悬空引用 */
|
|
199
|
+
export function scoreConsistency(data) {
|
|
200
|
+
const issues = [];
|
|
201
|
+
let score = 100;
|
|
202
|
+
const referenced = new Set([
|
|
203
|
+
...extractSettingsCredentialRefs(data),
|
|
204
|
+
...extractProvidersCredentialRefs(data),
|
|
205
|
+
]);
|
|
206
|
+
const declared = extractDeclaredCredentialRefs(data);
|
|
207
|
+
for (const ref of referenced) {
|
|
208
|
+
if (!declared.has(ref)) {
|
|
209
|
+
score -= 15;
|
|
210
|
+
issues.push({
|
|
211
|
+
severity: 'warning',
|
|
212
|
+
code: 'consistency.danglingCredentialRef',
|
|
213
|
+
message: `凭据引用未在 credentials 分区声明:${ref}`,
|
|
214
|
+
evidence: ref,
|
|
215
|
+
});
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
return { id: 'consistency', score: clampScore(score), verdict: dimensionVerdict(clampScore(score)), issues };
|
|
219
|
+
}
|
|
220
|
+
/** 敏感暴露维度:快照是否含未加密 secret、高熵 token 泄漏面 */
|
|
221
|
+
export function scoreSensitive(data) {
|
|
222
|
+
const issues = [];
|
|
223
|
+
let score = 100;
|
|
224
|
+
if (data.containsSecrets && !data.encrypted) {
|
|
225
|
+
score -= 40;
|
|
226
|
+
issues.push({ severity: 'error', code: 'sensitive.unencryptedSecrets', message: '快照含未加密秘密(凭据值明文暴露)' });
|
|
227
|
+
}
|
|
228
|
+
const hitCount = data.sensitiveHits.length;
|
|
229
|
+
if (hitCount > 0) {
|
|
230
|
+
const penalty = Math.min(hitCount * 5, 40);
|
|
231
|
+
score -= penalty;
|
|
232
|
+
issues.push({
|
|
233
|
+
severity: 'warning',
|
|
234
|
+
code: 'sensitive.hits',
|
|
235
|
+
message: `检测到 ${hitCount} 处敏感字段暴露(已脱敏)`,
|
|
236
|
+
});
|
|
237
|
+
}
|
|
238
|
+
return { id: 'sensitive', score: clampScore(score), verdict: dimensionVerdict(clampScore(score)), issues };
|
|
239
|
+
}
|
|
240
|
+
/** 可迁移性维度:dry-run 应用能否通过(无致命冲突) */
|
|
241
|
+
export function scoreMigratability(data) {
|
|
242
|
+
const issues = [];
|
|
243
|
+
let score;
|
|
244
|
+
const m = data.migratability;
|
|
245
|
+
if (m === null) {
|
|
246
|
+
// 未评估可迁移性 = warning 级 → needs-attention(60),与 warning 标签一致(不落到 critical)
|
|
247
|
+
score = 60;
|
|
248
|
+
issues.push({ severity: 'warning', code: 'migratability.notEvaluated', message: '未评估可迁移性(dry-run 未执行)' });
|
|
249
|
+
}
|
|
250
|
+
else if (!m.ok) {
|
|
251
|
+
score = 0;
|
|
252
|
+
issues.push({ severity: 'error', code: 'migratability.failed', message: '可迁移性检查失败:dry-run 无法通过' });
|
|
253
|
+
for (const e of m.errors) {
|
|
254
|
+
issues.push({ severity: 'error', code: 'migratability.error', message: e });
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
else {
|
|
258
|
+
score = 100;
|
|
259
|
+
for (let i = 0; i < m.fatalConflicts; i++) {
|
|
260
|
+
score -= 30;
|
|
261
|
+
issues.push({ severity: 'error', code: 'migratability.fatalConflict', message: '存在致命冲突,需人工决策' });
|
|
262
|
+
}
|
|
263
|
+
for (let i = 0; i < m.warnings; i++) {
|
|
264
|
+
score -= 10;
|
|
265
|
+
issues.push({ severity: 'warning', code: 'migratability.warning', message: '存在需注意的迁移项' });
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
return { id: 'migratability', score: clampScore(score), verdict: dimensionVerdict(clampScore(score)), issues };
|
|
269
|
+
}
|
|
270
|
+
/* ---------------- 汇总 ---------------- */
|
|
271
|
+
/**
|
|
272
|
+
* 构造时脱敏(REDACTED 不变量主防线):对每个 issue 的 message/evidence 应用 redact()。
|
|
273
|
+
* 渲染前 redact() 仅作辅防线;本函数保证报告对象本身不含敏感值。
|
|
274
|
+
*/
|
|
275
|
+
function redactIssues(dims) {
|
|
276
|
+
return dims.map((d) => ({
|
|
277
|
+
...d,
|
|
278
|
+
issues: d.issues.map((i) => ({
|
|
279
|
+
...i,
|
|
280
|
+
message: redact(i.message),
|
|
281
|
+
evidence: i.evidence !== undefined ? redact(i.evidence) : undefined,
|
|
282
|
+
})),
|
|
283
|
+
}));
|
|
284
|
+
}
|
|
285
|
+
/** 计算全部维度(确定性) */
|
|
286
|
+
export function computeDimensions(data, target, weights = DEFAULT_DIMENSION_WEIGHTS) {
|
|
287
|
+
const dims = [
|
|
288
|
+
scoreCompatibility(data, target),
|
|
289
|
+
scoreIntegrity(data),
|
|
290
|
+
scoreSections(data),
|
|
291
|
+
scoreConsistency(data),
|
|
292
|
+
scoreSensitive(data),
|
|
293
|
+
scoreMigratability(data),
|
|
294
|
+
];
|
|
295
|
+
// 权重仅用于加权平均;维度 verdict 与权重无关(确定性)
|
|
296
|
+
void weights;
|
|
297
|
+
return redactIssues(dims);
|
|
298
|
+
}
|
|
299
|
+
/** 汇总健康评分 + verdict + recommendation(确定性) */
|
|
300
|
+
export function computeConsultReport(data, target, opts = {}) {
|
|
301
|
+
const weights = {
|
|
302
|
+
...DEFAULT_DIMENSION_WEIGHTS,
|
|
303
|
+
...opts.weights,
|
|
304
|
+
};
|
|
305
|
+
const dimensions = computeDimensions(data, target, weights);
|
|
306
|
+
// 加权平均
|
|
307
|
+
let weighted = 0;
|
|
308
|
+
for (const dim of dimensions) {
|
|
309
|
+
weighted += (weights[dim.id] ?? 0) * dim.score;
|
|
310
|
+
}
|
|
311
|
+
const healthScore = clampScore(weighted);
|
|
312
|
+
// verdict = 最差维度 verdict
|
|
313
|
+
let verdict = 'healthy';
|
|
314
|
+
for (const dim of dimensions)
|
|
315
|
+
verdict = worstVerdict(verdict, dim.verdict);
|
|
316
|
+
// recommendation
|
|
317
|
+
let recommendation;
|
|
318
|
+
if (verdict === 'critical') {
|
|
319
|
+
recommendation = opts.allowBlock === true ? 'block' : 'review';
|
|
320
|
+
}
|
|
321
|
+
else if (verdict === 'needs-attention') {
|
|
322
|
+
recommendation = 'review';
|
|
323
|
+
}
|
|
324
|
+
else {
|
|
325
|
+
recommendation = 'proceed';
|
|
326
|
+
}
|
|
327
|
+
// 触发项(全部 error/warning issue 的 message,已 redact)
|
|
328
|
+
const recommendationReasons = [];
|
|
329
|
+
for (const dim of dimensions) {
|
|
330
|
+
for (const issue of dim.issues) {
|
|
331
|
+
if (issue.severity === 'error' || issue.severity === 'warning') {
|
|
332
|
+
recommendationReasons.push(issue.message);
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
// willApply(来自 migratability;dryRun 恒 true)
|
|
337
|
+
const m = data.migratability;
|
|
338
|
+
const willApply = {
|
|
339
|
+
sections: m?.sections ?? [],
|
|
340
|
+
itemCount: m?.itemCount ?? 0,
|
|
341
|
+
conflicts: m?.fatalConflicts ?? 0,
|
|
342
|
+
risks: m?.warnings ?? 0,
|
|
343
|
+
overwritten: m?.fatalConflicts ?? 0,
|
|
344
|
+
dryRun: true,
|
|
345
|
+
};
|
|
346
|
+
const bound = { sourceId: data.source.id };
|
|
347
|
+
if (data.manifest !== null)
|
|
348
|
+
bound.manifest = data.manifest;
|
|
349
|
+
if (data.source.snapshotId !== undefined)
|
|
350
|
+
bound.snapshotId = data.source.snapshotId;
|
|
351
|
+
return {
|
|
352
|
+
source: data.source,
|
|
353
|
+
healthScore,
|
|
354
|
+
verdict,
|
|
355
|
+
recommendation,
|
|
356
|
+
recommendationReasons,
|
|
357
|
+
dimensions,
|
|
358
|
+
willApply,
|
|
359
|
+
bound,
|
|
360
|
+
generatedAt: new Date().toISOString(),
|
|
361
|
+
};
|
|
362
|
+
}
|
|
363
|
+
//# sourceMappingURL=migration-consult.js.map
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
export declare const MIGRATION_HISTORY_SCHEMA_VERSION = 1;
|
|
2
|
+
export declare const MIGRATION_HISTORY_DIR = "migration-history";
|
|
3
|
+
/** 默认保留上限(retention 删最旧)。 */
|
|
4
|
+
export declare const DEFAULT_MIGRATION_RETENTION = 1000;
|
|
5
|
+
/** 合法 kind 集合(§5 COMPLETE 清单 + 评审补 'profile-import')。 */
|
|
6
|
+
declare const MIGRATION_KINDS: readonly ['import', 'restore', 'rollback', 'profile-switch', 'profile-delete', 'profile-rename', 'profile-save', 'profile-import', 'sync-apply', 'autosync', 'recovery', 'backup', 'snapshot-delete', 'snapshot-prune'];
|
|
7
|
+
/** §5 覆盖的操作 kind(COMPLETE 不变量;常量枚举,天然非敏感)。 */
|
|
8
|
+
export type MigrationKind = (typeof MIGRATION_KINDS)[number];
|
|
9
|
+
/** 操作结果。 */
|
|
10
|
+
export type MigrationResult = 'success' | 'failed' | 'skipped';
|
|
11
|
+
/** 触发来源(常量)。 */
|
|
12
|
+
export type MigrationSource = 'api' | 'autosync' | 'backup-scheduler' | 'recovery' | 'cli' | 'internal';
|
|
13
|
+
/** 一条不可变的迁移历史记录(磁盘形态;schemaVersion 内联)。 */
|
|
14
|
+
export interface MigrationHistoryEntry {
|
|
15
|
+
schemaVersion: number;
|
|
16
|
+
at: string;
|
|
17
|
+
kind: MigrationKind;
|
|
18
|
+
result: MigrationResult;
|
|
19
|
+
/** 涉及分区 adapter id(常量集合)。 */
|
|
20
|
+
sections: string[];
|
|
21
|
+
/** Journal operation id(UUID,BOUND)。 */
|
|
22
|
+
operationId?: string;
|
|
23
|
+
/** 快照 id(UUID,BOUND)。 */
|
|
24
|
+
snapshotId?: string;
|
|
25
|
+
/** run-registry runId(UUID,BOUND)。 */
|
|
26
|
+
runId?: string;
|
|
27
|
+
source: MigrationSource;
|
|
28
|
+
/** 非敏感摘要(redact + high-entropy 后)。 */
|
|
29
|
+
summary: string;
|
|
30
|
+
/** 失败原因(redact + high-entropy 后)。 */
|
|
31
|
+
error?: string;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* 磁盘形态:schemaVersion + contentHash(对除 contentHash 外全字段的 SHA-256)。
|
|
35
|
+
* contentHash 用于 **append-only 篡改检测**:合法 JSON 内改字段值(含 result/summary/at)
|
|
36
|
+
* → 读回 hash 不符 → 该文件被识别为损坏并跳过(满足「篡改必须被拒绝或检测」)。
|
|
37
|
+
* 仅是内联字段(非 MAC/签名 / 非第二套 integrity framework;审计史非对抗性安全边界)。
|
|
38
|
+
*/
|
|
39
|
+
export interface StoredMigrationHistoryEntry extends Omit<MigrationHistoryEntry, 'schemaVersion'> {
|
|
40
|
+
schemaVersion: number;
|
|
41
|
+
contentHash: string;
|
|
42
|
+
}
|
|
43
|
+
/** 查询条件(纯函数过滤)。 */
|
|
44
|
+
export interface MigrationQuery {
|
|
45
|
+
kinds?: MigrationKind[];
|
|
46
|
+
/** 起始时间(epoch ms,含)。 */
|
|
47
|
+
from?: number;
|
|
48
|
+
/** 结束时间(epoch ms,含)。 */
|
|
49
|
+
to?: number;
|
|
50
|
+
result?: MigrationResult[];
|
|
51
|
+
/** sections 子集匹配(条目 sections 含任一查询分区即命中)。 */
|
|
52
|
+
sections?: string[];
|
|
53
|
+
}
|
|
54
|
+
/** 统计(纯函数)。 */
|
|
55
|
+
export interface MigrationHistoryStats {
|
|
56
|
+
total: number;
|
|
57
|
+
byKind: Partial<Record<MigrationKind, number>>;
|
|
58
|
+
byResult: Partial<Record<MigrationResult, number>>;
|
|
59
|
+
}
|
|
60
|
+
export type ExportFormat = 'json' | 'markdown';
|
|
61
|
+
/** 读取历史的结果:合法条目 + 被识别为损坏/跳过的文件名(append-only 篡改检测)。 */
|
|
62
|
+
export interface ReadMigrationResult {
|
|
63
|
+
entries: StoredMigrationHistoryEntry[];
|
|
64
|
+
/** 被跳过/损坏的文件名(读取时识别,UI 可选警示;不静默接受)。 */
|
|
65
|
+
corrupted: string[];
|
|
66
|
+
}
|
|
67
|
+
/** 追加写入结果(best-effort 调用方据此判断降级)。 */
|
|
68
|
+
export interface AppendResult {
|
|
69
|
+
ok: boolean;
|
|
70
|
+
entry: StoredMigrationHistoryEntry;
|
|
71
|
+
file?: string;
|
|
72
|
+
error?: string;
|
|
73
|
+
}
|
|
74
|
+
/** 可注入 IO 门面(测试注入失败点);默认包 node:fs/promises。 */
|
|
75
|
+
export interface MigrationIo {
|
|
76
|
+
mkdir(dir: string, opts: {
|
|
77
|
+
recursive: boolean;
|
|
78
|
+
}): Promise<void>;
|
|
79
|
+
readdirNames(dir: string): Promise<string[]>;
|
|
80
|
+
readFileText(p: string): Promise<string>;
|
|
81
|
+
writeAll(target: string, content: string): Promise<void>;
|
|
82
|
+
rm(p: string, opts: {
|
|
83
|
+
recursive?: boolean;
|
|
84
|
+
force?: boolean;
|
|
85
|
+
}): Promise<void>;
|
|
86
|
+
lstat(p: string): Promise<{
|
|
87
|
+
isSymbolicLink(): boolean;
|
|
88
|
+
} | null>;
|
|
89
|
+
exists(p: string): Promise<boolean>;
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* 历史文本强脱敏:redact(结构化字段 + 已知值形状)+ 高熵长 token 掩码。
|
|
93
|
+
* 用于 summary / error 等可能嵌入任意值的字段(REDACTED 不变量)。
|
|
94
|
+
*/
|
|
95
|
+
export declare function redactHistoryText(text: string): string;
|
|
96
|
+
/**
|
|
97
|
+
* 构造安全条目:对 summary / error 做强脱敏(scanAndRedact 高熵档 + redactHistoryText),
|
|
98
|
+
* 做白名单字段规范化,并计算 contentHash(对除 contentHash 外全字段的 SHA-256)。
|
|
99
|
+
* **历史写入的唯一出口入口**,禁止绕过。
|
|
100
|
+
*/
|
|
101
|
+
export declare function sanitizeEntry(raw: Omit<MigrationHistoryEntry, 'schemaVersion'> & {
|
|
102
|
+
schemaVersion?: number;
|
|
103
|
+
}): StoredMigrationHistoryEntry;
|
|
104
|
+
/** 合法 kind 判定(类型守卫 + 运行时校验)。 */
|
|
105
|
+
export declare function isValidMigrationKind(k: unknown): k is MigrationKind;
|
|
106
|
+
/** 判断文件 basename 是否是可追踪历史文件(<sortable>-<hex>.json)。忽略 tmp 及其它。 */
|
|
107
|
+
export declare function isHistoryBasename(name: string): boolean;
|
|
108
|
+
/** 文件名安全:仅 basename(不含目录分隔符/穿越)。 */
|
|
109
|
+
export declare function isSafeHistoryFilename(name: string): boolean;
|
|
110
|
+
/**
|
|
111
|
+
* 生成排序文件名(Windows-safe 定宽时间戳 + 随机后缀 + kind 标记)。
|
|
112
|
+
*
|
|
113
|
+
* 时间戳契约(Windows/排序可靠性):`toISOString().replace(/[:Z]/g,'')`
|
|
114
|
+
* → `2026-08-30T120000.123`(UTC 定宽、仅 ASCII、无 Windows 非法字符 `<>:"/\|?*`、
|
|
115
|
+
* 字典序== 时间序)。**禁止**以原始 `toISOString()`(含 `:`)直接落文件名(Windows rename 失败)。
|
|
116
|
+
* 与既有 `dateStamp`(`YYYYMMDD-HHmmss`)语义同为「定宽 + 仅 ASCII + 可排序」,本实现用 UTC 含毫秒
|
|
117
|
+
* 以获得更高时序精度;排序可靠性一致。
|
|
118
|
+
*/
|
|
119
|
+
export declare function makeHistoryFilename(now: Date, kind: MigrationKind): string;
|
|
120
|
+
export interface MigrationStoreOptions {
|
|
121
|
+
/** history 存放目录(<dataDir>/migration-history)。 */
|
|
122
|
+
dir: string;
|
|
123
|
+
io?: MigrationIo;
|
|
124
|
+
/** 保留上限(retention 用;调用方可覆盖)。 */
|
|
125
|
+
retention?: number;
|
|
126
|
+
}
|
|
127
|
+
/** MigrationHistory:per-file append-only 历史存储。 */
|
|
128
|
+
export declare class MigrationStore {
|
|
129
|
+
private readonly dir;
|
|
130
|
+
private readonly io;
|
|
131
|
+
constructor(opts: MigrationStoreOptions);
|
|
132
|
+
/** 追加一条历史(原子写)。best-effort:调用方须经 tryAppendHistory 处理失败降级。 */
|
|
133
|
+
append(raw: Omit<MigrationHistoryEntry, 'schemaVersion' | 'at' | 'kind'> & {
|
|
134
|
+
kind: MigrationKind;
|
|
135
|
+
at?: string;
|
|
136
|
+
}): Promise<AppendResult>;
|
|
137
|
+
/** 读取全部合法历史条目(旧→新;损坏/非史文件跳过并计数)。 */
|
|
138
|
+
read(): Promise<ReadMigrationResult>;
|
|
139
|
+
/**
|
|
140
|
+
* 保留清理:按文件名排序删除最旧合法历史文件,幂等。只删合法历史文件(防误删)。
|
|
141
|
+
* 返回本次删除的文件名。
|
|
142
|
+
*/
|
|
143
|
+
retention(limit?: number): Promise<string[]>;
|
|
144
|
+
}
|
|
145
|
+
/** 纯函数过滤(无 IO)。 */
|
|
146
|
+
export declare function queryHistory(entries: StoredMigrationHistoryEntry[], q: MigrationQuery): StoredMigrationHistoryEntry[];
|
|
147
|
+
/** 纯函数统计(kind/result 计数)。 */
|
|
148
|
+
export declare function summarizeHistory(entries: StoredMigrationHistoryEntry[]): MigrationHistoryStats;
|
|
149
|
+
/** 导出报告(纯函数)。渲染文本统一过 redact() 兜底(安全不变量)。 */
|
|
150
|
+
export declare function renderExport(entries: StoredMigrationHistoryEntry[], format: ExportFormat, locale?: 'zh' | 'en'): string;
|
|
151
|
+
/** 解析查询参数为 MigrationQuery(宿主路由复用;过滤非法输入)。 */
|
|
152
|
+
export declare function parseHistoryQuery(raw: Record<string, unknown>): MigrationQuery;
|
|
153
|
+
export {};
|