@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,81 @@
|
|
|
1
|
+
import type { MsgFunc } from '../core/messages.ts';
|
|
2
|
+
export declare const SYNC_CONFIG_FILE = "sync-config.json";
|
|
3
|
+
/** 同步通道类型(git / webdav;host 侧统一引用,autosync-config / sync-selection 复用)。 */
|
|
4
|
+
export type SyncTransportType = 'git' | 'webdav';
|
|
5
|
+
/** 当前 sync-config.json schema 版本号(v3:双命名空间共存,切换通道不丢失另一通道配置)。 */
|
|
6
|
+
export declare const SYNC_CONFIG_SCHEMA_VERSION = 3;
|
|
7
|
+
/** 历史可读取版本:v1、v2、v3。 */
|
|
8
|
+
export declare const SYNC_CONFIG_SUPPORTED_VERSIONS: readonly number[];
|
|
9
|
+
/** git 通道配置(不含任何凭据;git 可执行文件固定使用系统 PATH 中的 git) */
|
|
10
|
+
export interface GitConfig {
|
|
11
|
+
repoUrl: string;
|
|
12
|
+
}
|
|
13
|
+
/** webdav 通道配置(不含 password;password 走 DSH credentials) */
|
|
14
|
+
export interface WebDavConfig {
|
|
15
|
+
/** WebDAV 端点地址(不含凭据;拒绝 userinfo) */
|
|
16
|
+
url: string;
|
|
17
|
+
/** 可选用户名(可回显) */
|
|
18
|
+
username?: string;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* 完整双命名空间配置视图(v3 文件直接读取,供 status 路由回填另一通道的 repoUrl/url)。
|
|
22
|
+
* 与可辨识联合 SyncConfig 不同:git 和 webdav 命名空间同时存在,可能缺失。
|
|
23
|
+
*/
|
|
24
|
+
export interface FullSyncConfig {
|
|
25
|
+
transport: 'git' | 'webdav';
|
|
26
|
+
git?: GitConfig;
|
|
27
|
+
webdav?: WebDavConfig;
|
|
28
|
+
}
|
|
29
|
+
/** 持久化的同步通道配置:可辨识联合(schemaVersion 恒 2) */
|
|
30
|
+
export type SyncConfig = {
|
|
31
|
+
schemaVersion: 2;
|
|
32
|
+
transport: 'git';
|
|
33
|
+
git: GitConfig;
|
|
34
|
+
} | {
|
|
35
|
+
schemaVersion: 2;
|
|
36
|
+
transport: 'webdav';
|
|
37
|
+
webdav: WebDavConfig;
|
|
38
|
+
};
|
|
39
|
+
/** git 通道守卫 */
|
|
40
|
+
export declare function isGitConfig(cfg: SyncConfig): cfg is Extract<SyncConfig, {
|
|
41
|
+
transport: 'git';
|
|
42
|
+
}>;
|
|
43
|
+
/** webdav 通道守卫 */
|
|
44
|
+
export declare function isWebDavConfig(cfg: SyncConfig): cfg is Extract<SyncConfig, {
|
|
45
|
+
transport: 'webdav';
|
|
46
|
+
}>;
|
|
47
|
+
/**
|
|
48
|
+
* 读取同步通道配置;文件不存在/损坏/不支持 schema → null(视为未配置,UI 显示空表单)。
|
|
49
|
+
* 兼容旧文件:缺 schemaVersion 字段视为 v1(git 通道)。
|
|
50
|
+
* 恒返回 schemaVersion=2 的规范形态(v1 读取时归一为 git)。
|
|
51
|
+
*/
|
|
52
|
+
export declare function readSyncConfig(dir: string): Promise<SyncConfig | null>;
|
|
53
|
+
/**
|
|
54
|
+
* 读取完整的双命名空间配置(供 status 路由回填另一通道的 repoUrl/url)。
|
|
55
|
+
* 文件不存在/损坏/无任何通道配置 → null(视为未配置)。
|
|
56
|
+
*/
|
|
57
|
+
export declare function readFullSyncConfig(dir: string): Promise<FullSyncConfig | null>;
|
|
58
|
+
/**
|
|
59
|
+
* 读取指定通道的同步通道配置(供自动同步调度器按通道运行)。
|
|
60
|
+
* 从完整双命名空间配置取对应通道构造可辨识联合 SyncConfig;该通道未配置 → null。
|
|
61
|
+
*/
|
|
62
|
+
export declare function readSyncConfigFor(dir: string, channel: SyncTransportType): Promise<SyncConfig | null>;
|
|
63
|
+
/**
|
|
64
|
+
* 保存同步通道配置(自动创建目录;恒写 schemaVersion=3 双命名空间)。
|
|
65
|
+
* - 写入当前通道的命名空间(git/webdav);
|
|
66
|
+
* - 另一通道之前配置过 → 一并保留(切换通道不丢失另一通道的 repoUrl/url);
|
|
67
|
+
* - 覆盖旧值;未配置过的字段不写入。
|
|
68
|
+
*/
|
|
69
|
+
export declare function writeSyncConfig(dir: string, cfg: SyncConfig): Promise<void>;
|
|
70
|
+
/**
|
|
71
|
+
* 仓库地址合法性校验(返回错误消息;null = 合法)。
|
|
72
|
+
* 安全约束:token 永不拼入 repoUrl —— http(s) 地址带 userinfo(username[:password]@)直接拒绝,
|
|
73
|
+
* 引导用户把 token 放凭据字段(DSH credentials),避免 token 经 URL 泄漏进 git 历史/日志。
|
|
74
|
+
*/
|
|
75
|
+
export declare function validateRepoUrl(repoUrl: string, msg?: MsgFunc): string | null;
|
|
76
|
+
/**
|
|
77
|
+
* WebDAV 端点地址合法性校验(返回错误消息;null = 合法)。
|
|
78
|
+
* 安全约束:口令/密码永不拼入 url —— 仅接受 http(s),且拒绝带 userinfo
|
|
79
|
+
* (username[:password]@)的地址,引导用户把口令放 DSH credentials,避免凭据经 URL 泄漏进出入口/日志。
|
|
80
|
+
*/
|
|
81
|
+
export declare function validateWebDavUrl(url: string, msg?: MsgFunc): string | null;
|
|
@@ -0,0 +1,281 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* m-sync-ui:同步通道配置的持久化(sync-config.json,schemaVersion v2)。
|
|
3
|
+
*
|
|
4
|
+
* 与 sync-state.json 的分工:sync-state 记录「同步了哪些分区、何时同步」(t4 拥有),
|
|
5
|
+
* 本文件只记录「上一次使用的同步通道配置」,供 UI 打开设置页时回填表单。
|
|
6
|
+
*
|
|
7
|
+
* schema v2(统一接口契约,captain 冻结):
|
|
8
|
+
* - 顶层形状:
|
|
9
|
+
* { "schemaVersion": 2, "transport": "git"|"webdav",
|
|
10
|
+
* "git": { "repoUrl": "..." }, // transport=git 时
|
|
11
|
+
* "webdav": { "url": "...", "username": "..." } } // transport=webdav 时
|
|
12
|
+
* - 顶层 transport 选择 + git/webdav 命名空间对象(嵌套,非扁平,避免歧义)。
|
|
13
|
+
* git 命名空间不再含 gitBin(git 可执行文件固定使用系统 PATH 中的 git)。
|
|
14
|
+
* - webdav.url 不含任何凭据、拒绝 userinfo;username 可回显;
|
|
15
|
+
* password 绝不入文件(走 DSH credentials ref `DSH_CONFIG_MANAGER_SYNC_WEBDAV_PASSWORD`)。
|
|
16
|
+
* - 代码内为可辨识联合 SyncConfig + isGitConfig()/isWebDavConfig() 守卫。
|
|
17
|
+
* - 兼容旧 v1 文件({schemaVersion:1, repoUrl, gitBin?} 或缺 schemaVersion 视为 v1)
|
|
18
|
+
* → 读取时归一为 v2 git 形态(旧 gitBin 字段被忽略/下一次保存时丢弃)。
|
|
19
|
+
*
|
|
20
|
+
* 安全不变量:
|
|
21
|
+
* - 配置文件绝不出现密码/token(webdav 仅存 url/可选 username;口令走 DSH credentials)。
|
|
22
|
+
* - url 校验:拒绝空白、非 http(s)、含 userinfo(username:password@)——仿 validateRepoUrl。
|
|
23
|
+
*/
|
|
24
|
+
import fs from 'node:fs/promises';
|
|
25
|
+
import path from 'node:path';
|
|
26
|
+
import { zhMsg } from '../core/messages.js';
|
|
27
|
+
import { parseJsonSafe, stringifyJsonSafe } from '../utils/json.js';
|
|
28
|
+
import { atomicWriteFile } from '../utils/atomic-write.js';
|
|
29
|
+
export const SYNC_CONFIG_FILE = 'sync-config.json';
|
|
30
|
+
/** 当前 sync-config.json schema 版本号(v3:双命名空间共存,切换通道不丢失另一通道配置)。 */
|
|
31
|
+
export const SYNC_CONFIG_SCHEMA_VERSION = 3;
|
|
32
|
+
/** 历史可读取版本:v1、v2、v3。 */
|
|
33
|
+
export const SYNC_CONFIG_SUPPORTED_VERSIONS = [1, 2, 3];
|
|
34
|
+
/** git 通道守卫 */
|
|
35
|
+
export function isGitConfig(cfg) {
|
|
36
|
+
return cfg.transport === 'git';
|
|
37
|
+
}
|
|
38
|
+
/** webdav 通道守卫 */
|
|
39
|
+
export function isWebDavConfig(cfg) {
|
|
40
|
+
return cfg.transport === 'webdav';
|
|
41
|
+
}
|
|
42
|
+
/** 从 v1 扁平形态解析 git 配置;缺 repoUrl → null(gitBin 已废弃:始终使用系统 PATH 中的 git) */
|
|
43
|
+
function parseV1Git(obj) {
|
|
44
|
+
if (typeof obj['repoUrl'] !== 'string' || obj['repoUrl'] === '')
|
|
45
|
+
return null;
|
|
46
|
+
return { repoUrl: obj['repoUrl'] };
|
|
47
|
+
}
|
|
48
|
+
/** 从 v2 git 命名空间解析;缺有效 repoUrl → null */
|
|
49
|
+
function parseV2GitNamespace(ns) {
|
|
50
|
+
if (ns === null || typeof ns !== 'object' || Array.isArray(ns))
|
|
51
|
+
return null;
|
|
52
|
+
return parseV1Git(ns);
|
|
53
|
+
}
|
|
54
|
+
/** 从 v2 webdav 命名空间解析;缺有效 url → null */
|
|
55
|
+
function parseV2WebDavNamespace(ns) {
|
|
56
|
+
if (ns === null || typeof ns !== 'object' || Array.isArray(ns))
|
|
57
|
+
return null;
|
|
58
|
+
const o = ns;
|
|
59
|
+
if (typeof o['url'] !== 'string' || o['url'] === '')
|
|
60
|
+
return null;
|
|
61
|
+
const webdav = { url: o['url'] };
|
|
62
|
+
if (typeof o['username'] === 'string' && o['username'] !== '')
|
|
63
|
+
webdav.username = o['username'];
|
|
64
|
+
return webdav;
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* 读取同步通道配置;文件不存在/损坏/不支持 schema → null(视为未配置,UI 显示空表单)。
|
|
68
|
+
* 兼容旧文件:缺 schemaVersion 字段视为 v1(git 通道)。
|
|
69
|
+
* 恒返回 schemaVersion=2 的规范形态(v1 读取时归一为 git)。
|
|
70
|
+
*/
|
|
71
|
+
export async function readSyncConfig(dir) {
|
|
72
|
+
const file = path.join(dir, SYNC_CONFIG_FILE);
|
|
73
|
+
let raw;
|
|
74
|
+
try {
|
|
75
|
+
raw = await fs.readFile(file, 'utf8');
|
|
76
|
+
}
|
|
77
|
+
catch {
|
|
78
|
+
return null;
|
|
79
|
+
}
|
|
80
|
+
let parsed;
|
|
81
|
+
try {
|
|
82
|
+
parsed = parseJsonSafe(raw);
|
|
83
|
+
}
|
|
84
|
+
catch {
|
|
85
|
+
// 损坏 JSON / 体积超限 / 嵌套过深 → 视为未配置(不抛错)
|
|
86
|
+
return null;
|
|
87
|
+
}
|
|
88
|
+
if (parsed === null || typeof parsed !== 'object' || Array.isArray(parsed))
|
|
89
|
+
return null;
|
|
90
|
+
const obj = parsed;
|
|
91
|
+
// schemaVersion:缺省视为 v1(兼容旧文件);非缺省但不在支持列表 → 拒绝
|
|
92
|
+
if (obj['schemaVersion'] !== undefined && typeof obj['schemaVersion'] !== 'number') {
|
|
93
|
+
return null;
|
|
94
|
+
}
|
|
95
|
+
const ver = typeof obj['schemaVersion'] === 'number' ? obj['schemaVersion'] : 1;
|
|
96
|
+
if (!SYNC_CONFIG_SUPPORTED_VERSIONS.includes(ver))
|
|
97
|
+
return null;
|
|
98
|
+
if (ver === 1) {
|
|
99
|
+
const git = parseV1Git(obj);
|
|
100
|
+
if (git === null)
|
|
101
|
+
return null;
|
|
102
|
+
return { schemaVersion: 2, transport: 'git', git };
|
|
103
|
+
}
|
|
104
|
+
// v2 / v3:顶层 transport 选择(v3 双命名空间并存,按 transport 返回对应通道)
|
|
105
|
+
const transport = obj['transport'];
|
|
106
|
+
if (transport !== 'git' && transport !== 'webdav')
|
|
107
|
+
return null;
|
|
108
|
+
if (transport === 'git') {
|
|
109
|
+
const git = parseV2GitNamespace(obj['git']);
|
|
110
|
+
if (git === null)
|
|
111
|
+
return null;
|
|
112
|
+
return { schemaVersion: 2, transport: 'git', git };
|
|
113
|
+
}
|
|
114
|
+
const webdav = parseV2WebDavNamespace(obj['webdav']);
|
|
115
|
+
if (webdav === null)
|
|
116
|
+
return null;
|
|
117
|
+
return { schemaVersion: 2, transport: 'webdav', webdav };
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* 读取 sync-config.json 原始内容,提取 git/webdav 两个命名空间(不存在/无效 → undefined)。
|
|
121
|
+
* 供 writeSyncConfig 合并保留另一通道配置用:切换通道保存时不得丢弃另一通道的 repoUrl/url。
|
|
122
|
+
*/
|
|
123
|
+
function readBothNamespaces(file) {
|
|
124
|
+
return (async () => {
|
|
125
|
+
let raw;
|
|
126
|
+
try {
|
|
127
|
+
raw = await fs.readFile(file, 'utf8');
|
|
128
|
+
}
|
|
129
|
+
catch {
|
|
130
|
+
return {}; // 文件不存在:无历史配置
|
|
131
|
+
}
|
|
132
|
+
let parsed;
|
|
133
|
+
try {
|
|
134
|
+
parsed = parseJsonSafe(raw);
|
|
135
|
+
}
|
|
136
|
+
catch {
|
|
137
|
+
return {}; // 损坏 JSON:按无历史配置处理(不阻塞保存)
|
|
138
|
+
}
|
|
139
|
+
if (parsed === null || typeof parsed !== 'object' || Array.isArray(parsed))
|
|
140
|
+
return {};
|
|
141
|
+
const o = parsed;
|
|
142
|
+
const out = {};
|
|
143
|
+
const git = parseV2GitNamespace(o['git']);
|
|
144
|
+
if (git !== null)
|
|
145
|
+
out.git = git;
|
|
146
|
+
const webdav = parseV2WebDavNamespace(o['webdav']);
|
|
147
|
+
if (webdav !== null)
|
|
148
|
+
out.webdav = webdav;
|
|
149
|
+
// v1 旧文件(无命名空间):git 读扁平 repoUrl
|
|
150
|
+
if (out.git === undefined && out.webdav === undefined) {
|
|
151
|
+
const v1 = parseV1Git(o);
|
|
152
|
+
if (v1 !== null)
|
|
153
|
+
out.git = v1;
|
|
154
|
+
}
|
|
155
|
+
return out;
|
|
156
|
+
})();
|
|
157
|
+
}
|
|
158
|
+
/**
|
|
159
|
+
* 读取完整的双命名空间配置(供 status 路由回填另一通道的 repoUrl/url)。
|
|
160
|
+
* 文件不存在/损坏/无任何通道配置 → null(视为未配置)。
|
|
161
|
+
*/
|
|
162
|
+
export async function readFullSyncConfig(dir) {
|
|
163
|
+
const file = path.join(dir, SYNC_CONFIG_FILE);
|
|
164
|
+
const both = await readBothNamespaces(file);
|
|
165
|
+
if (both.git === undefined && both.webdav === undefined)
|
|
166
|
+
return null;
|
|
167
|
+
// 从原始文件读取当前活动 transport 字段
|
|
168
|
+
let transport = 'git';
|
|
169
|
+
try {
|
|
170
|
+
const raw = await fs.readFile(file, 'utf8');
|
|
171
|
+
const parsed = parseJsonSafe(raw);
|
|
172
|
+
if (parsed !== null && typeof parsed === 'object' && !Array.isArray(parsed)) {
|
|
173
|
+
const o = parsed;
|
|
174
|
+
if (o['transport'] === 'webdav')
|
|
175
|
+
transport = 'webdav';
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
catch { /* 默认 git */ }
|
|
179
|
+
return { transport, git: both.git, webdav: both.webdav };
|
|
180
|
+
}
|
|
181
|
+
/**
|
|
182
|
+
* 读取指定通道的同步通道配置(供自动同步调度器按通道运行)。
|
|
183
|
+
* 从完整双命名空间配置取对应通道构造可辨识联合 SyncConfig;该通道未配置 → null。
|
|
184
|
+
*/
|
|
185
|
+
export async function readSyncConfigFor(dir, channel) {
|
|
186
|
+
const full = await readFullSyncConfig(dir);
|
|
187
|
+
if (full === null)
|
|
188
|
+
return null;
|
|
189
|
+
if (channel === 'webdav') {
|
|
190
|
+
if (full.webdav === undefined)
|
|
191
|
+
return null;
|
|
192
|
+
return { schemaVersion: 2, transport: 'webdav', webdav: full.webdav };
|
|
193
|
+
}
|
|
194
|
+
if (full.git === undefined)
|
|
195
|
+
return null;
|
|
196
|
+
return { schemaVersion: 2, transport: 'git', git: full.git };
|
|
197
|
+
}
|
|
198
|
+
/**
|
|
199
|
+
* 保存同步通道配置(自动创建目录;恒写 schemaVersion=3 双命名空间)。
|
|
200
|
+
* - 写入当前通道的命名空间(git/webdav);
|
|
201
|
+
* - 另一通道之前配置过 → 一并保留(切换通道不丢失另一通道的 repoUrl/url);
|
|
202
|
+
* - 覆盖旧值;未配置过的字段不写入。
|
|
203
|
+
*/
|
|
204
|
+
export async function writeSyncConfig(dir, cfg) {
|
|
205
|
+
await fs.mkdir(dir, { recursive: true });
|
|
206
|
+
const file = path.join(dir, SYNC_CONFIG_FILE);
|
|
207
|
+
const existing = await readBothNamespaces(file);
|
|
208
|
+
const payload = {
|
|
209
|
+
schemaVersion: SYNC_CONFIG_SCHEMA_VERSION,
|
|
210
|
+
transport: cfg.transport,
|
|
211
|
+
};
|
|
212
|
+
if (isGitConfig(cfg)) {
|
|
213
|
+
payload.git = cfg.git;
|
|
214
|
+
// 保留另一通道的 webdav 配置(存在时)
|
|
215
|
+
if (existing.webdav !== undefined)
|
|
216
|
+
payload.webdav = existing.webdav;
|
|
217
|
+
}
|
|
218
|
+
else {
|
|
219
|
+
payload.webdav = cfg.webdav;
|
|
220
|
+
// 保留另一通道的 git 配置(存在时)
|
|
221
|
+
if (existing.git !== undefined)
|
|
222
|
+
payload.git = existing.git;
|
|
223
|
+
}
|
|
224
|
+
await atomicWriteFile(file, stringifyJsonSafe(payload, { space: 2 }), { mode: 0o600 });
|
|
225
|
+
}
|
|
226
|
+
/**
|
|
227
|
+
* 仓库地址合法性校验(返回错误消息;null = 合法)。
|
|
228
|
+
* 安全约束:token 永不拼入 repoUrl —— http(s) 地址带 userinfo(username[:password]@)直接拒绝,
|
|
229
|
+
* 引导用户把 token 放凭据字段(DSH credentials),避免 token 经 URL 泄漏进 git 历史/日志。
|
|
230
|
+
*/
|
|
231
|
+
export function validateRepoUrl(repoUrl, msg = zhMsg) {
|
|
232
|
+
if (typeof repoUrl !== 'string' || repoUrl.trim() === '') {
|
|
233
|
+
return 'repoUrl is required';
|
|
234
|
+
}
|
|
235
|
+
const url = repoUrl.trim();
|
|
236
|
+
if (/\s/.test(url)) {
|
|
237
|
+
return msg('sync.configWhitespace');
|
|
238
|
+
}
|
|
239
|
+
if (/^https?:\/\//i.test(url)) {
|
|
240
|
+
let parsed;
|
|
241
|
+
try {
|
|
242
|
+
parsed = new URL(url);
|
|
243
|
+
}
|
|
244
|
+
catch {
|
|
245
|
+
return msg('sync.configUnparseable', { url });
|
|
246
|
+
}
|
|
247
|
+
if (parsed.username !== '' || parsed.password !== '') {
|
|
248
|
+
return msg('sync.configUserinfo');
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
return null;
|
|
252
|
+
}
|
|
253
|
+
/**
|
|
254
|
+
* WebDAV 端点地址合法性校验(返回错误消息;null = 合法)。
|
|
255
|
+
* 安全约束:口令/密码永不拼入 url —— 仅接受 http(s),且拒绝带 userinfo
|
|
256
|
+
* (username[:password]@)的地址,引导用户把口令放 DSH credentials,避免凭据经 URL 泄漏进出入口/日志。
|
|
257
|
+
*/
|
|
258
|
+
export function validateWebDavUrl(url, msg = zhMsg) {
|
|
259
|
+
if (typeof url !== 'string' || url.trim() === '') {
|
|
260
|
+
return 'url is required';
|
|
261
|
+
}
|
|
262
|
+
const cleaned = url.trim();
|
|
263
|
+
if (/\s/.test(cleaned)) {
|
|
264
|
+
return msg('sync.configWhitespace');
|
|
265
|
+
}
|
|
266
|
+
if (!/^https?:\/\//i.test(cleaned)) {
|
|
267
|
+
return msg('sync.configUnparseable', { url: cleaned });
|
|
268
|
+
}
|
|
269
|
+
let parsed;
|
|
270
|
+
try {
|
|
271
|
+
parsed = new URL(cleaned);
|
|
272
|
+
}
|
|
273
|
+
catch {
|
|
274
|
+
return msg('sync.configUnparseable', { url: cleaned });
|
|
275
|
+
}
|
|
276
|
+
if (parsed.username !== '' || parsed.password !== '') {
|
|
277
|
+
return msg('sync.configUserinfo');
|
|
278
|
+
}
|
|
279
|
+
return null;
|
|
280
|
+
}
|
|
281
|
+
//# sourceMappingURL=sync-config.js.map
|
|
@@ -0,0 +1,269 @@
|
|
|
1
|
+
import { Importer } from '../core/importer.ts';
|
|
2
|
+
import type { ConfigAdapter, GlobalConflictStrategy, HostContext, ImportAnalysis, ImportPlan, ImportResult, PlanItem, PlanItemKind } from '../core/types.ts';
|
|
3
|
+
import type { SectionData, SectionId } from '../schema/types.ts';
|
|
4
|
+
import type { SnapshotFs } from './fs.ts';
|
|
5
|
+
import type { SyncSnapshot, SyncSnapshotMeta, SyncTransport } from './transport.ts';
|
|
6
|
+
import type { MsgFunc } from '../core/messages.ts';
|
|
7
|
+
import type { TransactionSnapshotContext } from '../core/types.ts';
|
|
8
|
+
import type { PlanItemProgress } from '../core/analyzer.ts';
|
|
9
|
+
export interface SyncEngineOptions {
|
|
10
|
+
ctx: HostContext;
|
|
11
|
+
transport: SyncTransport;
|
|
12
|
+
/** sync-state.json 所在目录 */
|
|
13
|
+
stateDir: string;
|
|
14
|
+
/** 分区 adapter 列表(缺省 createAdapters();宿主应注入 namespaces 等) */
|
|
15
|
+
adapters?: ConfigAdapter[];
|
|
16
|
+
/** pull 需要;不注入则 pull 抛错(push 不受影响) */
|
|
17
|
+
importer?: Importer;
|
|
18
|
+
now?: () => Date;
|
|
19
|
+
/** 快照 id 生成器(缺省 sync-<uuid>;须符合通道安全字符集) */
|
|
20
|
+
snapshotId?: () => string;
|
|
21
|
+
/** 记录到 sync-state.transport.ref(如 git 分支名) */
|
|
22
|
+
transportRef?: string;
|
|
23
|
+
/** 本地散文件快照副本目录(push 落盘审计副本;不传则跳过本地落盘) */
|
|
24
|
+
localSnapshotsDir?: string;
|
|
25
|
+
/** 一键回滚兜底快照目录(apply-items 落盘;缺省 <stateDir>/snapshots,
|
|
26
|
+
* 与 Host /sync/rollback 路由读取的目录保持一致,否则回滚找不到 snapshot.json) */
|
|
27
|
+
rollbackSnapshotsDir?: string;
|
|
28
|
+
/** pull 临时 ZIP 目录(缺省 os.tmpdir()) */
|
|
29
|
+
zipDir?: string;
|
|
30
|
+
exporterVersion?: string;
|
|
31
|
+
fsx?: SnapshotFs;
|
|
32
|
+
/** 消息翻译器(缺省 ctx.msg ?? zh) */
|
|
33
|
+
msg?: MsgFunc;
|
|
34
|
+
/**
|
|
35
|
+
* 同步范围(自定义同步模式持久化配置):只处理这些分区。
|
|
36
|
+
* 缺省 = 全部推荐分区。应用于 push / pull 等全部链路,供自动同步等后台流程复用
|
|
37
|
+
* 用户选择;手动请求仍可用 push(opts.sections) 覆盖。
|
|
38
|
+
*/
|
|
39
|
+
sections?: SectionId[];
|
|
40
|
+
}
|
|
41
|
+
export interface SyncPushOptions {
|
|
42
|
+
/** 覆盖自动生成的快照 id */
|
|
43
|
+
snapshotId?: string;
|
|
44
|
+
/** 仅同步指定分区(缺省 = 全部推荐分区)。
|
|
45
|
+
* 传入未知分区 → 忽略并告警(不静默吞掉)。 */
|
|
46
|
+
sections?: SectionId[];
|
|
47
|
+
}
|
|
48
|
+
export interface SyncPullOptions {
|
|
49
|
+
/** 指定远端快照 id(缺省 = 最新) */
|
|
50
|
+
snapshotId?: string;
|
|
51
|
+
/** 冲突全局策略(缺省 replace:勾选即同步,远端值直接覆盖本地,不做 diff/合并) */
|
|
52
|
+
strategy?: GlobalConflictStrategy;
|
|
53
|
+
}
|
|
54
|
+
export interface SyncPushReport {
|
|
55
|
+
ok: boolean;
|
|
56
|
+
snapshotId: string;
|
|
57
|
+
/** 实际进入同步的分区 */
|
|
58
|
+
sections: SectionId[];
|
|
59
|
+
/** 分区级告警(单项失败不拖垮整体,§34.17 语义) */
|
|
60
|
+
warnings: string[];
|
|
61
|
+
message?: string;
|
|
62
|
+
}
|
|
63
|
+
/** 差异报告中的单个变更项(PlanItem 摘要,不含敏感细节) */
|
|
64
|
+
export interface PullChange {
|
|
65
|
+
id: string;
|
|
66
|
+
adapter: SectionId;
|
|
67
|
+
kind: PlanItemKind;
|
|
68
|
+
description: string;
|
|
69
|
+
severity: PlanItem['severity'];
|
|
70
|
+
}
|
|
71
|
+
export interface SyncPullReport {
|
|
72
|
+
ok: boolean;
|
|
73
|
+
snapshotId: string;
|
|
74
|
+
changes: PullChange[];
|
|
75
|
+
/** 是否存在需要人工决策的项(Conflict / MissingSecret / MissingDependency / 路径问题)。 */
|
|
76
|
+
needsReview: boolean;
|
|
77
|
+
message?: string;
|
|
78
|
+
}
|
|
79
|
+
/** push 前只读预览 ——「将推送什么」的单分区摘要(不写远端、不落盘)。 */
|
|
80
|
+
export interface SyncPushPreviewSection {
|
|
81
|
+
/** 分区 id(将进入快照的分区) */
|
|
82
|
+
section: SectionId;
|
|
83
|
+
/** 分区内条目计数(adapter.export 的 counts 聚合;无计数时为 0) */
|
|
84
|
+
count: number;
|
|
85
|
+
/** 相对上次基线(sync-state)是否变化:true = 本次会更新该分区;false = 与基线一致 */
|
|
86
|
+
changed: boolean;
|
|
87
|
+
}
|
|
88
|
+
/** push 前预览结果(零写入)。 */
|
|
89
|
+
export interface SyncPushPreview {
|
|
90
|
+
ok: boolean;
|
|
91
|
+
/** 将推送的分区清单(含计数与变化标记) */
|
|
92
|
+
sections: SyncPushPreviewSection[];
|
|
93
|
+
/** 远端现有快照数(0 = 首次推送将创建首个基线) */
|
|
94
|
+
remoteSnapshotCount: number;
|
|
95
|
+
message?: string;
|
|
96
|
+
}
|
|
97
|
+
/** 一键 sync 预览结果(preview() 返回;临时 ZIP 由调用方持有并负责清理)。 */
|
|
98
|
+
export interface SyncPreviewResult {
|
|
99
|
+
ok: boolean;
|
|
100
|
+
/** 临时标准 ZIP 路径(apply-items 复用 executeImportPlan 需要;调用方清理) */
|
|
101
|
+
zipPath: string;
|
|
102
|
+
plan: ImportPlan | null;
|
|
103
|
+
analysis: ImportAnalysis | null;
|
|
104
|
+
snapshotId: string;
|
|
105
|
+
message?: string;
|
|
106
|
+
}
|
|
107
|
+
/** applyItems 报告(§3.4 ApplyItemsResponse 的服务端形态) */
|
|
108
|
+
export interface ApplyItemsReport {
|
|
109
|
+
ok: boolean;
|
|
110
|
+
/** 实际写入的分区 id 列表(去重) */
|
|
111
|
+
applied: string[];
|
|
112
|
+
/** 未采纳的 itemId 列表 */
|
|
113
|
+
skipped?: string[];
|
|
114
|
+
/** 应用前快照 id(UI 一键回滚用;失败时仍透传以便排查) */
|
|
115
|
+
restoreId: string;
|
|
116
|
+
/** 任一失败是否整体回滚 */
|
|
117
|
+
rolledBack: boolean;
|
|
118
|
+
warnings: string[];
|
|
119
|
+
failed: {
|
|
120
|
+
itemId: string;
|
|
121
|
+
message?: string;
|
|
122
|
+
}[];
|
|
123
|
+
/** 透传 executeImportPlan 结果 */
|
|
124
|
+
result: ImportResult | null;
|
|
125
|
+
needsRestart?: boolean;
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* 远端快照保留数量上限:每次 push 上传成功后对远端裁剪,
|
|
129
|
+
* 保留最新 N 个(按 createdAt 升序的最末 N 个,含刚 push 的),更旧的逐个删除。
|
|
130
|
+
* 只按数量裁剪,不按时间窗口。删除失败只告警(进 push 的 warnings),不上抛阻断主流程。
|
|
131
|
+
*/
|
|
132
|
+
export declare const MAX_REMOTE_SNAPSHOTS = 10;
|
|
133
|
+
export declare class SyncEngine {
|
|
134
|
+
private readonly ctx;
|
|
135
|
+
private readonly transport;
|
|
136
|
+
private readonly stateDir;
|
|
137
|
+
private readonly adapters;
|
|
138
|
+
private readonly importer;
|
|
139
|
+
private readonly now;
|
|
140
|
+
private readonly snapshotIdFn;
|
|
141
|
+
private readonly transportRef;
|
|
142
|
+
private readonly localSnapshotsDir;
|
|
143
|
+
private readonly rollbackSnapshotsDir;
|
|
144
|
+
private readonly zipDir;
|
|
145
|
+
private readonly exporterVersion;
|
|
146
|
+
private readonly fsx;
|
|
147
|
+
private readonly msg;
|
|
148
|
+
private readonly sections;
|
|
149
|
+
constructor(opts: SyncEngineOptions);
|
|
150
|
+
/** 参与同步的分区全集:构造注入 sections(同步范围)时按注入范围过滤 ——
|
|
151
|
+
* 自动同步等后台流程 push/pull 全链路复用用户选择;手动请求仍可用 push(opts.sections) 覆盖。 */
|
|
152
|
+
private syncAdapters;
|
|
153
|
+
/** 未显式传 sections 时的默认范围。
|
|
154
|
+
*
|
|
155
|
+
* - 构造注入 sections(高级模式:用户已在 UI 勾选并持久化)→ 该选择**就是**默认范围,
|
|
156
|
+
* 不再按 defaultIncluded 收窄(否则高级模式勾选的 sessions/pluginFiles 会被悄悄剔除);
|
|
157
|
+
* - 未注入(默认模式「快速导出」)→ 只取推荐分区 defaultIncluded。
|
|
158
|
+
*
|
|
159
|
+
* defaultIncluded=false 的分区(sessions 含历史会话明文、pluginFiles)语义是「用户显式
|
|
160
|
+
* 勾选才同步」;把它们并进默认模式的范围会绕过 UI 勾选与「将同步 N 个推荐分区」计数,
|
|
161
|
+
* 把敏感内容静默推到远端通道。 */
|
|
162
|
+
private defaultTargets;
|
|
163
|
+
/**
|
|
164
|
+
* push 候选 adapter:
|
|
165
|
+
* - sections 缺省/空 → 全部推荐分区(defaultIncluded);
|
|
166
|
+
* - sections 显式给出 → 从**全部已挂载分区**取命中项(显式勾选可触达 defaultIncluded=false
|
|
167
|
+
* 的分区,这正是「默认关闭」的含义);未知分区 → 警告跳过(不静默,用户能看见自己勾了哪个无效项)。
|
|
168
|
+
*/
|
|
169
|
+
private pushTargets;
|
|
170
|
+
/**
|
|
171
|
+
* 快照读取准备(download 后、使用前):
|
|
172
|
+
* 加密快照(manifest.encrypted)→ 明确拒绝:同步通道已不再产生加密快照,
|
|
173
|
+
* 旧加密快照无法用当前引擎读取(拒绝静默当明文处理)。
|
|
174
|
+
*/
|
|
175
|
+
private prepareSnapshot;
|
|
176
|
+
/**
|
|
177
|
+
* push:导出分区(真实值)→ 组装快照 → 本地散文件副本 → transport.upload → 更新 sync-state。
|
|
178
|
+
* 单项分区导出失败只告警跳过(§34.17),全部失败才整体失败。
|
|
179
|
+
* opts.sections:指定仅同步这些分区(自定义模式);缺省 = 全部推荐分区。
|
|
180
|
+
*/
|
|
181
|
+
push(opts?: SyncPushOptions): Promise<SyncPushReport>;
|
|
182
|
+
/**
|
|
183
|
+
* push 前只读预览「将推送什么」—— 零写入、零远端变更:
|
|
184
|
+
* - 导出目标分区(与 push 同口径:sections 过滤);
|
|
185
|
+
* - 逐分区相对上次基线(sync-state.sections hash)的 changed 标记;
|
|
186
|
+
* - 远端现有快照数(list 只读;首次推送 = 0)。
|
|
187
|
+
* 任何失败都不写任何内容;预览只是 push 的「确认前说明书」。
|
|
188
|
+
*/
|
|
189
|
+
previewPush(opts?: SyncPushOptions): Promise<SyncPushPreview>;
|
|
190
|
+
/**
|
|
191
|
+
* 裁剪远端快照:调用 transport.list() 获取全部快照(按 createdAt 升序),
|
|
192
|
+
* 保留最新 MAX_REMOTE_SNAPSHOTS 个(含刚 push 的 pushedId),对更旧的逐个调用 transport.delete()。
|
|
193
|
+
* 只按数量裁剪,不按时间窗口。
|
|
194
|
+
*
|
|
195
|
+
* 失败语义:list/delete 失败均只 push 进 warnings,不上抛 —— 裁剪是「尽力而为」的后台整理,
|
|
196
|
+
* 绝不影响 push 主流程的成功与否(§34.17 分区级告警同款语义)。
|
|
197
|
+
*/
|
|
198
|
+
private pruneRemoteSnapshots;
|
|
199
|
+
/**
|
|
200
|
+
* pull:拉取远端最新(或指定)快照 → 转临时标准 ZIP →
|
|
201
|
+
* 复用 Importer 预览流程(analyzeImport/createImportPlan)产出差异报告。
|
|
202
|
+
* 绝不直接写配置、绝不执行导入;执行由上层按用户确认后走 Importer.executeImportPlan。
|
|
203
|
+
*/
|
|
204
|
+
pull(opts?: SyncPullOptions): Promise<SyncPullReport>;
|
|
205
|
+
/** 列出远端已有快照(按 createdAt 升序)—— 供「选择历史快照」下拉。 */
|
|
206
|
+
listSnapshots(): Promise<SyncSnapshotMeta[]>;
|
|
207
|
+
/**
|
|
208
|
+
* 远端是否出现比本地共同祖先(sync-state.lastSnapshotId)更新的快照(§3.2「检测到远端新快照」)。
|
|
209
|
+
* - 远端为空 → false(无物可拉);
|
|
210
|
+
* - 本地从未同步(lastSnapshotId='')且远端非空 → true(首次可拉);
|
|
211
|
+
* - 否则比较远端最新快照 id 与 lastSnapshotId。
|
|
212
|
+
* 只读远端列表(transport.list),不做下载/合并。
|
|
213
|
+
*/
|
|
214
|
+
hasNewRemoteSnapshot(): Promise<boolean>;
|
|
215
|
+
/**
|
|
216
|
+
* 本地配置当前内容与上次基线(sync-state.sections hash)相比是否有变化(§3.1 上传「看变化」)。
|
|
217
|
+
* - 从未同步(sync-state.sections 为空)→ true;
|
|
218
|
+
* - 任一分区当前导出 hash ≠ 基线 hash → true;
|
|
219
|
+
* - 全部一致 → false(无本地改动,不上传)。
|
|
220
|
+
* 只读本地导出 + sync-state,不写任何东西、不碰远端。
|
|
221
|
+
*/
|
|
222
|
+
hasLocalChanges(): Promise<boolean>;
|
|
223
|
+
/**
|
|
224
|
+
* 一键同步预览:拉取远端(最新或指定历史快照)→ 转临时 ZIP → Importer 分析出计划。
|
|
225
|
+
* 与 pull 的区别:临时 ZIP **不清理**(由调用方 / 会话持有,供 apply-items 复用),
|
|
226
|
+
* 并返回完整 plan/analysis/snapshotId 供会话登记。
|
|
227
|
+
* 调用方负责在会话消费或取消后清理 zipPath 所在目录。
|
|
228
|
+
*/
|
|
229
|
+
preview(opts?: SyncPullOptions): Promise<SyncPreviewResult>;
|
|
230
|
+
/**
|
|
231
|
+
* 记录基线:写本地快照副本 + 更新 sync-state
|
|
232
|
+
* (lastSnapshotId、每分区 hash/updatedAt、lastSyncAt、transport)。
|
|
233
|
+
* 通常由 push() 在上传成功后调用,也可被上层(apply-items 完成后)显式调用。
|
|
234
|
+
*/
|
|
235
|
+
recordBaseline(snapshotId: string, sections: SyncSnapshot['sections'], nowIso?: string): Promise<void>;
|
|
236
|
+
/**
|
|
237
|
+
* applyItems:按用户对差异项的逐项决策执行导入(§3.4/§5.3)。
|
|
238
|
+
*
|
|
239
|
+
* 接收「会话级临时 ZIP + 子计划」,直接执行
|
|
240
|
+
* (backup.createSnapshot 兜底 → importer.executeImportPlan → 成功 recordBaseline / 失败 rollback)。
|
|
241
|
+
*
|
|
242
|
+
* @param zipPath 会话级临时标准 ZIP(由 sync 预览生成,包含采纳项的 payload)
|
|
243
|
+
* @param subPlan 子计划(仅含采纳项的 ImportPlan;globalStrategy/pathMappings/needsRestart 沿用会话 plan)
|
|
244
|
+
* @param opts 执行选项(onItem 进度回调)
|
|
245
|
+
* @returns ApplyItemsReport(ok/applied/restoreId/rolledBack/warnings/result)
|
|
246
|
+
*/
|
|
247
|
+
applyItems(zipPath: string, subPlan: ImportPlan, opts?: {
|
|
248
|
+
onItem?: (info: PlanItemProgress) => void;
|
|
249
|
+
/** Phase 4 生产 journal↔snapshot 绑定(deferred;透传给 Importer.executeImportPlan) */
|
|
250
|
+
snapshotBinding?: TransactionSnapshotContext;
|
|
251
|
+
/**
|
|
252
|
+
* 本次应用的**远端快照 id**(preview() 返回值)。
|
|
253
|
+
*
|
|
254
|
+
* 必须传:基线(sync-state.lastSnapshotId)要与「刚刚应用的远端快照」对齐,
|
|
255
|
+
* 否则 `hasNewRemoteSnapshot()` 会把同一个远端快照一直判定为「新的」,
|
|
256
|
+
* 自动同步每轮都会重复拉取同一个快照(空转)。
|
|
257
|
+
* 缺省(不传)时才回退到本地新生成的 id —— 仅适用于「本地产生的快照」语义。
|
|
258
|
+
*/
|
|
259
|
+
snapshotId?: string;
|
|
260
|
+
}): Promise<ApplyItemsReport>;
|
|
261
|
+
/** 散文件快照 → 标准导出 ZIP(临时目录,用完即删):buildManifest + checksums + 平铺分区 */
|
|
262
|
+
private snapshotToZip;
|
|
263
|
+
}
|
|
264
|
+
/**
|
|
265
|
+
* 快照是否实际携带凭据值:复用 exporter 的敏感字段扫描器(单一真源,不另造字段表)。
|
|
266
|
+
* 用于如实标注 manifest.security.containsSecrets —— 标注必须与内容一致,
|
|
267
|
+
* 否则「含明文却标注 false」会让下游按「无秘密」处理。
|
|
268
|
+
*/
|
|
269
|
+
export declare function sectionsCarrySecrets(sections: Partial<Record<SectionId, SectionData>>): boolean;
|