dsh-config-manager 0.1.69 → 0.1.71
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +59 -18
- package/README.zh-CN.md +29 -9
- package/lib/adapters/credentials.js +6 -3
- package/lib/adapters/file-collection.d.ts +21 -1
- package/lib/adapters/file-collection.js +32 -8
- package/lib/adapters/mcp.d.ts +12 -2
- package/lib/adapters/mcp.js +30 -13
- package/lib/adapters/plugin-files.d.ts +8 -0
- package/lib/adapters/plugin-files.js +36 -4
- package/lib/adapters/plugins.d.ts +28 -2
- package/lib/adapters/plugins.js +204 -55
- package/lib/adapters/prompts.d.ts +12 -2
- package/lib/adapters/prompts.js +34 -19
- package/lib/adapters/providers.js +5 -4
- package/lib/adapters/self.d.ts +20 -0
- package/lib/adapters/self.js +73 -0
- package/lib/adapters/sessions.d.ts +8 -0
- package/lib/adapters/sessions.js +47 -1
- package/lib/adapters/settings.d.ts +21 -3
- package/lib/adapters/settings.js +44 -7
- package/lib/adapters/skills.d.ts +36 -4
- package/lib/adapters/skills.js +222 -3
- package/lib/adapters/test-helpers.d.ts +31 -2
- package/lib/adapters/test-helpers.js +42 -5
- package/lib/adapters/workspaces.js +97 -5
- package/lib/cli/actions.d.ts +50 -3
- package/lib/cli/actions.js +103 -11
- package/lib/cli/help.d.ts +65 -0
- package/lib/cli/help.js +517 -0
- package/lib/cli/import-source.d.ts +32 -0
- package/lib/cli/import-source.js +178 -0
- package/lib/cli/index.d.ts +18 -2
- package/lib/cli/index.js +118 -90
- package/lib/cli/web/client-script.d.ts +8 -0
- package/lib/cli/web/client-script.js +11 -0
- package/lib/cli/web/http.d.ts +5 -0
- package/lib/cli/web/http.js +2 -1
- package/lib/cli/web/page.d.ts +16 -6
- package/lib/cli/web/page.js +102 -25
- package/lib/cli/web/routes.js +24 -5
- package/lib/cli/web/server.js +23 -2
- package/lib/client.d.ts +1019 -36
- package/lib/client.js +5522 -1564
- package/lib/core/analyzer.d.ts +15 -1
- package/lib/core/analyzer.js +114 -13
- package/lib/core/backup-plan.js +17 -1
- package/lib/core/backup.d.ts +8 -0
- package/lib/core/backup.js +28 -6
- package/lib/core/cache-cleaner.d.ts +10 -0
- package/lib/core/cache-cleaner.js +63 -22
- package/lib/core/checkpoint.d.ts +362 -0
- package/lib/core/checkpoint.js +1309 -0
- package/lib/core/journal.d.ts +37 -0
- package/lib/core/journal.js +69 -0
- package/lib/core/messages.d.ts +13 -0
- package/lib/core/messages.js +26 -0
- package/lib/core/model-tools.js +3 -0
- package/lib/core/patch-layers.d.ts +83 -0
- package/lib/core/patch-layers.js +110 -0
- package/lib/core/restore.d.ts +21 -0
- package/lib/core/restore.js +37 -3
- package/lib/core/rollback.js +6 -3
- package/lib/core/self-update.d.ts +52 -0
- package/lib/core/self-update.js +70 -0
- package/lib/core/session-health.d.ts +29 -5
- package/lib/core/session-health.js +28 -3
- package/lib/core/types.d.ts +126 -0
- package/lib/foreign/antigravity.d.ts +28 -0
- package/lib/foreign/antigravity.js +157 -0
- package/lib/foreign/bundle.d.ts +25 -0
- package/lib/foreign/bundle.js +79 -0
- package/lib/foreign/chatgpt.d.ts +6 -0
- package/lib/foreign/chatgpt.js +25 -0
- package/lib/foreign/claude-code.d.ts +3 -0
- package/lib/foreign/claude-code.js +85 -0
- package/lib/foreign/claude-sessions.d.ts +108 -0
- package/lib/foreign/claude-sessions.js +299 -0
- package/lib/foreign/cline.d.ts +6 -0
- package/lib/foreign/cline.js +27 -0
- package/lib/foreign/codex.d.ts +83 -0
- package/lib/foreign/codex.js +348 -0
- package/lib/foreign/continue.d.ts +9 -0
- package/lib/foreign/continue.js +65 -0
- package/lib/foreign/copilot.d.ts +39 -0
- package/lib/foreign/copilot.js +95 -0
- package/lib/foreign/crush.d.ts +11 -0
- package/lib/foreign/crush.js +57 -0
- package/lib/foreign/cursor.d.ts +87 -0
- package/lib/foreign/cursor.js +171 -0
- package/lib/foreign/dsh.d.ts +30 -0
- package/lib/foreign/dsh.js +101 -0
- package/lib/foreign/dsh4.d.ts +3 -0
- package/lib/foreign/dsh4.js +13 -0
- package/lib/foreign/gemini.d.ts +7 -0
- package/lib/foreign/gemini.js +44 -0
- package/lib/foreign/goose.d.ts +11 -0
- package/lib/foreign/goose.js +53 -0
- package/lib/foreign/grokbuild.d.ts +8 -0
- package/lib/foreign/grokbuild.js +25 -0
- package/lib/foreign/hermes.d.ts +2 -0
- package/lib/foreign/hermes.js +78 -0
- package/lib/foreign/kernel.d.ts +132 -0
- package/lib/foreign/kernel.js +569 -0
- package/lib/foreign/kilocode.d.ts +11 -0
- package/lib/foreign/kilocode.js +51 -0
- package/lib/foreign/kimi.d.ts +7 -0
- package/lib/foreign/kimi.js +47 -0
- package/lib/foreign/mimocode.d.ts +11 -0
- package/lib/foreign/mimocode.js +51 -0
- package/lib/foreign/openclaw.d.ts +7 -0
- package/lib/foreign/openclaw.js +46 -0
- package/lib/foreign/opencode.d.ts +11 -0
- package/lib/foreign/opencode.js +52 -0
- package/lib/foreign/pi.d.ts +7 -0
- package/lib/foreign/pi.js +48 -0
- package/lib/foreign/platform-paths.d.ts +66 -0
- package/lib/foreign/platform-paths.js +141 -0
- package/lib/foreign/qoder.d.ts +10 -0
- package/lib/foreign/qoder.js +49 -0
- package/lib/foreign/qwen.d.ts +7 -0
- package/lib/foreign/qwen.js +45 -0
- package/lib/foreign/read-antigravity.d.ts +57 -0
- package/lib/foreign/read-antigravity.js +504 -0
- package/lib/foreign/read-chatgpt.d.ts +20 -0
- package/lib/foreign/read-chatgpt.js +509 -0
- package/lib/foreign/read-claude-code.d.ts +24 -0
- package/lib/foreign/read-claude-code.js +234 -0
- package/lib/foreign/read-cline.d.ts +53 -0
- package/lib/foreign/read-cline.js +479 -0
- package/lib/foreign/read-codex.d.ts +75 -0
- package/lib/foreign/read-codex.js +842 -0
- package/lib/foreign/read-continue.d.ts +87 -0
- package/lib/foreign/read-continue.js +489 -0
- package/lib/foreign/read-copilot.d.ts +42 -0
- package/lib/foreign/read-copilot.js +267 -0
- package/lib/foreign/read-crush.d.ts +54 -0
- package/lib/foreign/read-crush.js +478 -0
- package/lib/foreign/read-cursor.d.ts +141 -0
- package/lib/foreign/read-cursor.js +705 -0
- package/lib/foreign/read-dsh.d.ts +43 -0
- package/lib/foreign/read-dsh.js +205 -0
- package/lib/foreign/read-dsh4.d.ts +14 -0
- package/lib/foreign/read-dsh4.js +27 -0
- package/lib/foreign/read-gemini.d.ts +55 -0
- package/lib/foreign/read-gemini.js +273 -0
- package/lib/foreign/read-goose.d.ts +25 -0
- package/lib/foreign/read-goose.js +322 -0
- package/lib/foreign/read-grokbuild.d.ts +34 -0
- package/lib/foreign/read-grokbuild.js +455 -0
- package/lib/foreign/read-hermes.d.ts +74 -0
- package/lib/foreign/read-hermes.js +690 -0
- package/lib/foreign/read-kilocode.d.ts +17 -0
- package/lib/foreign/read-kilocode.js +46 -0
- package/lib/foreign/read-kimi.d.ts +63 -0
- package/lib/foreign/read-kimi.js +719 -0
- package/lib/foreign/read-mimocode.d.ts +12 -0
- package/lib/foreign/read-mimocode.js +56 -0
- package/lib/foreign/read-openclaw.d.ts +51 -0
- package/lib/foreign/read-openclaw.js +445 -0
- package/lib/foreign/read-opencode.d.ts +150 -0
- package/lib/foreign/read-opencode.js +896 -0
- package/lib/foreign/read-pi.d.ts +56 -0
- package/lib/foreign/read-pi.js +411 -0
- package/lib/foreign/read-qoder.d.ts +54 -0
- package/lib/foreign/read-qoder.js +317 -0
- package/lib/foreign/read-qwen.d.ts +40 -0
- package/lib/foreign/read-qwen.js +326 -0
- package/lib/foreign/read-reasonix.d.ts +66 -0
- package/lib/foreign/read-reasonix.js +537 -0
- package/lib/foreign/read-teleagent.d.ts +15 -0
- package/lib/foreign/read-teleagent.js +68 -0
- package/lib/foreign/read-trae.d.ts +67 -0
- package/lib/foreign/read-trae.js +492 -0
- package/lib/foreign/read-vibe.d.ts +28 -0
- package/lib/foreign/read-vibe.js +364 -0
- package/lib/foreign/read-workbuddy.d.ts +49 -0
- package/lib/foreign/read-workbuddy.js +346 -0
- package/lib/foreign/read-zcode.d.ts +12 -0
- package/lib/foreign/read-zcode.js +38 -0
- package/lib/foreign/read-zed.d.ts +30 -0
- package/lib/foreign/read-zed.js +473 -0
- package/lib/foreign/reasonix.d.ts +6 -0
- package/lib/foreign/reasonix.js +20 -0
- package/lib/foreign/registry.d.ts +196 -0
- package/lib/foreign/registry.js +573 -0
- package/lib/foreign/session-ir.d.ts +186 -0
- package/lib/foreign/session-ir.js +427 -0
- package/lib/foreign/session-read.d.ts +102 -0
- package/lib/foreign/session-read.js +254 -0
- package/lib/foreign/session-source.d.ts +216 -0
- package/lib/foreign/session-source.js +453 -0
- package/lib/foreign/source-modules.d.ts +45 -0
- package/lib/foreign/source-modules.js +52 -0
- package/lib/foreign/sqlite.d.ts +61 -0
- package/lib/foreign/sqlite.js +404 -0
- package/lib/foreign/teleagent.d.ts +11 -0
- package/lib/foreign/teleagent.js +53 -0
- package/lib/foreign/trae.d.ts +6 -0
- package/lib/foreign/trae.js +20 -0
- package/lib/foreign/truth-table.d.ts +62 -0
- package/lib/foreign/truth-table.js +333 -0
- package/lib/foreign/types.d.ts +272 -0
- package/lib/foreign/types.js +2 -0
- package/lib/foreign/vibe.d.ts +6 -0
- package/lib/foreign/vibe.js +23 -0
- package/lib/foreign/workbuddy.d.ts +7 -0
- package/lib/foreign/workbuddy.js +45 -0
- package/lib/foreign/zcode.d.ts +11 -0
- package/lib/foreign/zcode.js +51 -0
- package/lib/foreign/zed.d.ts +11 -0
- package/lib/foreign/zed.js +53 -0
- package/lib/index.d.ts +154 -13
- package/lib/index.js +661 -84
- package/lib/market/github-repos.d.ts +28 -0
- package/lib/market/github-repos.js +43 -1
- package/lib/market/types.d.ts +1 -3
- package/lib/profiles/dsh-profile-manager.d.ts +54 -2
- package/lib/profiles/dsh-profile-manager.js +182 -11
- package/lib/profiles/dsh-profile-shared.d.ts +47 -0
- package/lib/profiles/dsh-profile-shared.js +15 -1
- package/lib/profiles/index.d.ts +1 -1
- package/lib/profiles/index.js +1 -1
- package/lib/routes/consult.js +51 -5
- package/lib/routes/foreign-context.d.ts +43 -0
- package/lib/routes/foreign-context.js +65 -0
- package/lib/routes/foreign.d.ts +11 -0
- package/lib/routes/foreign.js +208 -0
- package/lib/routes/import.d.ts +8 -0
- package/lib/routes/import.js +22 -1
- package/lib/routes/index.d.ts +2 -2
- package/lib/routes/index.js +4 -0
- package/lib/routes/kit.d.ts +4 -3
- package/lib/routes/market.js +0 -1
- package/lib/routes/prefs.d.ts +2 -1
- package/lib/routes/prefs.js +14 -2
- package/lib/routes/recovery.js +363 -3
- package/lib/routes/session-export.d.ts +117 -0
- package/lib/routes/session-export.js +270 -0
- package/lib/routes/snapshots.js +12 -2
- package/lib/routes/sync.js +82 -10
- package/lib/security/redaction.d.ts +14 -0
- package/lib/security/redaction.js +24 -9
- package/lib/security/secret-scanner.d.ts +27 -0
- package/lib/security/secret-scanner.js +54 -1
- package/lib/sync/autosync-scheduler.d.ts +13 -1
- package/lib/sync/autosync-scheduler.js +49 -26
- package/lib/sync/backup-files.js +6 -0
- package/lib/sync/gist/gist-transport.d.ts +91 -0
- package/lib/sync/gist/gist-transport.js +406 -0
- package/lib/sync/gist/messages.d.ts +22 -0
- package/lib/sync/gist/messages.js +46 -0
- package/lib/sync/s3/messages.d.ts +53 -0
- package/lib/sync/s3/messages.js +83 -0
- package/lib/sync/s3/s3-providers.d.ts +78 -0
- package/lib/sync/s3/s3-providers.js +156 -0
- package/lib/sync/s3/s3-transport.d.ts +126 -0
- package/lib/sync/s3/s3-transport.js +551 -0
- package/lib/sync/s3/sigv4.d.ts +107 -0
- package/lib/sync/s3/sigv4.js +196 -0
- package/lib/sync/sync-config.d.ts +250 -4
- package/lib/sync/sync-config.js +658 -57
- package/lib/sync/sync-engine.d.ts +1 -0
- package/lib/sync/sync-engine.js +4 -2
- package/lib/sync/sync-history.d.ts +1 -1
- package/lib/sync/webdav/webdav-transport.d.ts +31 -3
- package/lib/sync/webdav/webdav-transport.js +102 -18
- package/lib/ui/commands.d.ts +4 -3
- package/lib/ui/commands.js +41 -9
- package/lib/ui/disk-usage-view.d.ts +44 -0
- package/lib/ui/disk-usage-view.js +55 -1
- package/lib/ui/dsh-profiles-view.d.ts +53 -0
- package/lib/ui/dsh-profiles-view.js +56 -0
- package/lib/ui/foreign-view.d.ts +162 -0
- package/lib/ui/foreign-view.js +225 -0
- package/lib/ui/i18n.d.ts +77 -0
- package/lib/ui/i18n.js +164 -0
- package/lib/ui/session-inventory-view.d.ts +106 -0
- package/lib/ui/session-inventory-view.js +58 -0
- package/lib/ui/session-layout-view.d.ts +265 -0
- package/lib/ui/session-layout-view.js +271 -0
- package/lib/ui/sync-repository-picker.d.ts +98 -0
- package/lib/ui/sync-repository-picker.js +145 -0
- package/lib/ui/sync-settings-view.d.ts +143 -4
- package/lib/ui/sync-settings-view.js +231 -7
- package/lib/ui/types.d.ts +47 -0
- package/lib/utils/asar-read.d.ts +113 -0
- package/lib/utils/asar-read.js +523 -0
- package/lib/utils/env-lock.js +7 -2
- package/lib/utils/patch-yaml.d.ts +25 -0
- package/lib/utils/patch-yaml.js +62 -0
- package/lib/utils/paths.d.ts +10 -1
- package/lib/utils/paths.js +24 -3
- package/lib/utils/recursive-walk.js +12 -13
- package/lib/utils/session-format.d.ts +35 -2
- package/lib/utils/session-format.js +131 -5
- package/lib/utils/session-health-scan.d.ts +7 -1
- package/lib/utils/session-health-scan.js +369 -12
- package/lib/utils/session-layout-repair-service.d.ts +98 -0
- package/lib/utils/session-layout-repair-service.js +457 -0
- package/lib/utils/session-log-repair.d.ts +27 -0
- package/lib/utils/session-log-repair.js +233 -62
- package/lib/utils/session-repair-service.d.ts +32 -3
- package/lib/utils/session-repair-service.js +148 -9
- package/lib/utils/session-row-facts.d.ts +73 -0
- package/lib/utils/session-row-facts.js +182 -0
- package/lib/utils/session-verify.d.ts +118 -0
- package/lib/utils/session-verify.js +705 -0
- package/lib/utils/shared-constants.d.ts +59 -0
- package/lib/utils/shared-constants.js +71 -0
- package/package.json +1 -1
- package/src/adapters/credentials.test.ts +27 -0
- package/src/adapters/credentials.ts +6 -3
- package/src/adapters/file-collection.ts +33 -8
- package/src/adapters/files.test.ts +222 -0
- package/src/adapters/mcp.test.ts +43 -0
- package/src/adapters/mcp.ts +37 -14
- package/src/adapters/plugin-files.ts +34 -4
- package/src/adapters/plugins.test.ts +241 -0
- package/src/adapters/plugins.ts +201 -53
- package/src/adapters/prompts.test.ts +49 -0
- package/src/adapters/prompts.ts +41 -20
- package/src/adapters/providers.test.ts +16 -0
- package/src/adapters/providers.ts +4 -3
- package/src/adapters/self.test.ts +77 -0
- package/src/adapters/self.ts +75 -0
- package/src/adapters/sessions.test.ts +77 -0
- package/src/adapters/sessions.ts +47 -1
- package/src/adapters/settings.test.ts +78 -0
- package/src/adapters/settings.ts +42 -6
- package/src/adapters/skills.ts +221 -3
- package/src/adapters/test-helpers.ts +43 -5
- package/src/adapters/workspaces-dup-id.test.ts +144 -0
- package/src/adapters/workspaces.test.ts +10 -0
- package/src/adapters/workspaces.ts +97 -5
- package/src/cli/actions.test.ts +117 -2
- package/src/cli/actions.ts +132 -12
- package/src/cli/help.ts +570 -0
- package/src/cli/import-source.test.ts +193 -0
- package/src/cli/import-source.ts +229 -0
- package/src/cli/index.ts +129 -94
- package/src/cli/web/client-script.ts +12 -0
- package/src/cli/web/console-fixes.test.ts +36 -0
- package/src/cli/web/http.ts +7 -1
- package/src/cli/web/page.ts +108 -25
- package/src/cli/web/routes.ts +23 -4
- package/src/cli/web/script-anchor.test.ts +33 -0
- package/src/cli/web/server.ts +28 -2
- package/src/cli/web/web.test.ts +447 -4
- package/src/client/ConfigManagerSection.tsx +118 -63
- package/src/client/about/AboutPanel.tsx +42 -3
- package/src/client/about/PluginUpdateDialog.tsx +185 -0
- package/src/client/about/ReleaseBody.tsx +144 -0
- package/src/client/about/ReleaseNotesDialog.tsx +3 -130
- package/src/client/about/about-view.ts +7 -5
- package/src/client/about/plugin-update-view.test.ts +25 -0
- package/src/client/about/plugin-update-view.ts +26 -0
- package/src/client/about/release-notes-view.test.ts +33 -0
- package/src/client/about/release-notes-view.ts +34 -0
- package/src/client/api.ts +145 -1
- package/src/client/common/Icon.tsx +3 -0
- package/src/client/common/RunsCenter.tsx +8 -2
- package/src/client/common/Select.tsx +9 -2
- package/src/client/common/info-hint-guard.test.ts +63 -18
- package/src/client/common/plan-text-redaction.test.ts +193 -8
- package/src/client/common/routes.ts +15 -0
- package/src/client/config-manager.module.css +506 -354
- package/src/client/css-scale-guard.test.ts +122 -0
- package/src/client/css-token-guard.test.ts +82 -0
- package/src/client/environment/EnvironmentPanel.tsx +45 -16
- package/src/client/{snapshots → environment/maintenance}/DiskUsageCard.tsx +21 -17
- package/src/client/environment/profiles-incomplete-ui.test.ts +103 -0
- package/src/client/export/ExportView.tsx +9 -2
- package/src/client/{snapshots → home}/BackupScheduleCard.tsx +13 -4
- package/src/client/home/HomePanel.tsx +69 -157
- package/src/client/icon-layer-guard.test.ts +1 -1
- package/src/client/import/ForeignImportView.tsx +318 -0
- package/src/client/import/ImportWizardView.tsx +3 -1
- package/src/client/import/foreign-import-view-guard.test.ts +195 -0
- package/src/client/import/import-file-select.test.ts +6 -6
- package/src/client/import/import-wizard-steps.tsx +17 -1
- package/src/client/import/use-import-wizard-controller.ts +3 -3
- package/src/client/index.ts +1 -1
- package/src/client/library/LibraryActions.tsx +31 -34
- package/src/client/library/LibraryPanel.tsx +4 -1
- package/src/client/{snapshots → library}/RestorePlanView.tsx +1 -1
- package/src/client/{snapshots → library}/files-layout.test.ts +2 -2
- package/src/client/{snapshots → library}/report-dismiss.test.ts +26 -3
- package/src/client/library/task-panel-visibility.test.ts +25 -17
- package/src/client/locales.ts +278 -36
- package/src/client/lucide-icons.d.ts +1 -0
- package/src/client/market/MarketPanel.tsx +11 -2
- package/src/client/recovery/IncompleteCopiesSection.tsx +132 -0
- package/src/client/recovery/RecoveryPanel.tsx +486 -59
- package/src/client/recovery/recovery-api.ts +30 -0
- package/src/client/recovery/recovery-incomplete-copies.test.ts +151 -0
- package/src/client/recovery/recovery-locales.test.ts +88 -0
- package/src/client/recovery/recovery-locales.ts +321 -6
- package/src/client/recovery/recovery-panel-layout.test.ts +52 -0
- package/src/client/recovery/recovery-view.ts +92 -2
- package/src/client/run-store.test.ts +201 -89
- package/src/client/run-store.ts +114 -143
- package/src/client/session-export/session-export-view.test.ts +103 -0
- package/src/client/session-export/session-export-view.ts +118 -0
- package/src/client/sync/ChannelConfigDialog.tsx +219 -14
- package/src/client/sync/SyncChannelEntryCard.tsx +52 -4
- package/src/client/sync/SyncLogList.tsx +1 -1
- package/src/client/sync/SyncPanel.tsx +166 -102
- package/src/client/sync/SyncRepositoryPicker.tsx +216 -0
- package/src/client/sync/SyncSettingsView.test.ts +6 -1
- package/src/client/sync/history-model.test.ts +94 -57
- package/src/client/sync/history-model.ts +44 -25
- package/src/client/sync/sync-api.test.ts +56 -0
- package/src/client/sync/sync-api.ts +92 -0
- package/src/client/sync/sync-locales.ts +162 -2
- package/src/client/sync/sync-view.test.ts +3 -8
- package/src/client/sync/sync-view.ts +111 -21
- package/src/core/analyzer-cache.test.ts +47 -0
- package/src/core/analyzer.ts +115 -14
- package/src/core/backup-plan.test.ts +35 -0
- package/src/core/backup-plan.ts +19 -1
- package/src/core/backup.test.ts +37 -1
- package/src/core/backup.ts +27 -6
- package/src/core/cache-cleaner.test.ts +120 -1
- package/src/core/cache-cleaner.ts +59 -22
- package/src/core/checkpoint.test.ts +665 -0
- package/src/core/checkpoint.ts +1614 -0
- package/src/core/credential-plan-consistency.test.ts +93 -0
- package/src/core/incident-wiring.test.ts +6 -2
- package/src/core/journal.test.ts +70 -0
- package/src/core/journal.ts +68 -0
- package/src/core/messages.ts +26 -0
- package/src/core/model-tools.ts +3 -0
- package/src/core/patch-layers.test.ts +94 -0
- package/src/core/patch-layers.ts +150 -0
- package/src/core/restore.ts +59 -3
- package/src/core/rollback.ts +6 -3
- package/src/core/section-content-consistency.test.ts +185 -0
- package/src/core/self-update.test.ts +110 -0
- package/src/core/self-update.ts +118 -0
- package/src/core/session-health.test.ts +91 -1
- package/src/core/session-health.ts +56 -9
- package/src/core/types.ts +127 -1
- package/src/foreign/antigravity.test.ts +271 -0
- package/src/foreign/antigravity.ts +188 -0
- package/src/foreign/bundle.ts +108 -0
- package/src/foreign/chatgpt.test.ts +279 -0
- package/src/foreign/chatgpt.ts +30 -0
- package/src/foreign/claude-code.test.ts +234 -0
- package/src/foreign/claude-code.ts +106 -0
- package/src/foreign/claude-sessions-bytes.test.ts +227 -0
- package/src/foreign/claude-sessions.test.ts +180 -0
- package/src/foreign/claude-sessions.ts +461 -0
- package/src/foreign/cline.test.ts +274 -0
- package/src/foreign/cline.ts +32 -0
- package/src/foreign/codex.test.ts +585 -0
- package/src/foreign/codex.ts +409 -0
- package/src/foreign/continue.test.ts +365 -0
- package/src/foreign/continue.ts +73 -0
- package/src/foreign/copilot.test.ts +273 -0
- package/src/foreign/copilot.ts +133 -0
- package/src/foreign/crush.test.ts +234 -0
- package/src/foreign/crush.ts +66 -0
- package/src/foreign/cursor.test.ts +509 -0
- package/src/foreign/cursor.ts +259 -0
- package/src/foreign/dsh.test.ts +198 -0
- package/src/foreign/dsh.ts +117 -0
- package/src/foreign/dsh4.test.ts +127 -0
- package/src/foreign/dsh4.ts +15 -0
- package/src/foreign/file-budget.test.ts +256 -0
- package/src/foreign/fixtures/antigravity/README.md +18 -0
- package/src/foreign/fixtures/antigravity/basic/gemini/antigravity/mcp_config.json +13 -0
- package/src/foreign/fixtures/antigravity/basic/gemini/config/mcp_config.json +24 -0
- package/src/foreign/fixtures/antigravity/empty/gemini/antigravity/mcp_config.json +0 -0
- package/src/foreign/fixtures/antigravity/empty/gemini/antigravity/mcp_oauth_tokens.json +0 -0
- package/src/foreign/fixtures/antigravity/empty/gemini/config/mcp_config.json +0 -0
- package/src/foreign/fixtures/codex/README.md +31 -0
- package/src/foreign/fixtures/codex/basic/.agents/skills/hello/SKILL.md +6 -0
- package/src/foreign/fixtures/codex/basic/.agents/skills/wrapper/nested/SKILL.md +6 -0
- package/src/foreign/fixtures/codex/basic/.codex/AGENTS.md +4 -0
- package/src/foreign/fixtures/codex/basic/.codex/config.toml +28 -0
- package/src/foreign/fixtures/codex/malformed/.codex/config.toml +3 -0
- package/src/foreign/fixtures/codex/override/.codex/AGENTS.md +1 -0
- package/src/foreign/fixtures/codex/override/.codex/AGENTS.override.md +3 -0
- package/src/foreign/fixtures/codex/sessions/.codex/archived_sessions/rollout-2026-10-05T09-00-00-22222222-2222-4222-8222-222222222222.jsonl +2 -0
- package/src/foreign/fixtures/codex/sessions/.codex/sessions/2026/10/06/rollout-2026-10-06T10-00-00-11111111-1111-4111-8111-111111111111.jsonl +15 -0
- package/src/foreign/fixtures/codex/sessions/.codex/sessions/2026/10/06/rollout-2026-10-06T11-00-00-33333333-3333-4333-8333-333333333333.jsonl +2 -0
- package/src/foreign/fixtures/codex/sessions/.codex/sessions/2026/10/06/rollout-2026-10-06T11-30-00-55555555-5555-4555-8555-555555555555.jsonl +2 -0
- package/src/foreign/fixtures/codex/sessions/.codex/sessions/2026/10/06/rollout-2026-10-06T12-00-00-44444444-4444-4444-8444-444444444444.jsonl +2 -0
- package/src/foreign/fixtures/codex/sessions/.codex/sessions/notes.jsonl +1 -0
- package/src/foreign/fixtures/copilot/README.md +22 -0
- package/src/foreign/fixtures/copilot/basic/copilot-instructions.md +3 -0
- package/src/foreign/fixtures/copilot/basic/instructions/empty.instructions.md +1 -0
- package/src/foreign/fixtures/copilot/basic/instructions/repo.instructions.md +5 -0
- package/src/foreign/fixtures/copilot/basic/mcp-config.json +24 -0
- package/src/foreign/fixtures/copilot/basic/skills/hello/SKILL.md +6 -0
- package/src/foreign/fixtures/copilot/overridden/mcp-config.json +10 -0
- package/src/foreign/fixtures/cursor/README.md +17 -0
- package/src/foreign/fixtures/cursor/alt/.cursor/mcp.json +5 -0
- package/src/foreign/fixtures/cursor/basic/.cursor/mcp.json +18 -0
- package/src/foreign/fixtures/cursor/basic/.cursor/rules/agent.mdc +5 -0
- package/src/foreign/fixtures/cursor/basic/.cursor/rules/always.mdc +6 -0
- package/src/foreign/fixtures/cursor/basic/.cursor/rules/auto.mdc +7 -0
- package/src/foreign/fixtures/cursor/basic/.cursor/rules/manual.mdc +3 -0
- package/src/foreign/fixtures/cursor/basic/.cursor/skills/.hidden/SKILL.md +6 -0
- package/src/foreign/fixtures/cursor/basic/.cursor/skills/hello/SKILL.md +6 -0
- package/src/foreign/fixtures/cursor/basic/.cursor/skills/wrapper/nested/SKILL.md +6 -0
- package/src/foreign/fixtures/cursor/basic/.cursor/skills/wrapper/nested/notes.md +1 -0
- package/src/foreign/fixtures/cursor/basic/.cursorrules +1 -0
- package/src/foreign/fixtures/cursor/basic/project/.cursor/mcp.json +9 -0
- package/src/foreign/fixtures/cursor/basic/project/.cursor/rules/project-always.mdc +5 -0
- package/src/foreign/fixtures/cursor/basic/project/.cursor/skills/hello/SKILL.md +6 -0
- package/src/foreign/fixtures/cursor/basic/project/.cursorrules +1 -0
- package/src/foreign/fixtures/cursor/empty/.cursor/mcp.json +0 -0
- package/src/foreign/fixtures/cursor/malformed/.cursor/mcp.json +3 -0
- package/src/foreign/fixtures/cursor/malformed/.cursor/rules/broken.mdc +6 -0
- package/src/foreign/fixtures/cursor/shape/.cursor/mcp.json +3 -0
- package/src/foreign/fixtures/hermes/README.md +14 -0
- package/src/foreign/fixtures/hermes/basic/SOUL.md +3 -0
- package/src/foreign/fixtures/hermes/basic/config.yaml +48 -0
- package/src/foreign/fixtures/hermes/basic/dotenv.fixture +1 -0
- package/src/foreign/fixtures/hermes/basic/memories/MEMORY.md +1 -0
- package/src/foreign/fixtures/hermes/basic/memories/USER.md +1 -0
- package/src/foreign/fixtures/hermes/basic/skills/se-team-design/SKILL.md +6 -0
- package/src/foreign/fixtures/hermes/basic/skills/se-team-design/examples/usage.md +1 -0
- package/src/foreign/fixtures/hermes/basic/skills/software-development/dogfood/SKILL.md +6 -0
- package/src/foreign/fixtures/hermes/basic/skills/software-development/dogfood/notes.md +1 -0
- package/src/foreign/fixtures/hermes/basic/state.db +1 -0
- package/src/foreign/fixtures/hermes/empty/config.yaml +0 -0
- package/src/foreign/gemini.test.ts +291 -0
- package/src/foreign/gemini.ts +52 -0
- package/src/foreign/goose.test.ts +196 -0
- package/src/foreign/goose.ts +62 -0
- package/src/foreign/grokbuild.test.ts +246 -0
- package/src/foreign/grokbuild.ts +30 -0
- package/src/foreign/hermes.test.ts +545 -0
- package/src/foreign/hermes.ts +86 -0
- package/src/foreign/kernel-credentials.test.ts +88 -0
- package/src/foreign/kernel-workspaces.test.ts +88 -0
- package/src/foreign/kernel.ts +584 -0
- package/src/foreign/kilocode.test.ts +186 -0
- package/src/foreign/kilocode.ts +60 -0
- package/src/foreign/kimi.test.ts +259 -0
- package/src/foreign/kimi.ts +55 -0
- package/src/foreign/mcp-value-shape.test.ts +286 -0
- package/src/foreign/mimocode.test.ts +178 -0
- package/src/foreign/mimocode.ts +60 -0
- package/src/foreign/openclaw.test.ts +248 -0
- package/src/foreign/openclaw.ts +54 -0
- package/src/foreign/opencode.test.ts +376 -0
- package/src/foreign/opencode.ts +61 -0
- package/src/foreign/pi.test.ts +275 -0
- package/src/foreign/pi.ts +56 -0
- package/src/foreign/platform-paths.ts +156 -0
- package/src/foreign/qoder.test.ts +266 -0
- package/src/foreign/qoder.ts +57 -0
- package/src/foreign/qwen.test.ts +203 -0
- package/src/foreign/qwen.ts +53 -0
- package/src/foreign/read-antigravity.ts +569 -0
- package/src/foreign/read-chatgpt.ts +506 -0
- package/src/foreign/read-claude-code.ts +264 -0
- package/src/foreign/read-cline.ts +517 -0
- package/src/foreign/read-codex.ts +823 -0
- package/src/foreign/read-continue.ts +557 -0
- package/src/foreign/read-copilot.ts +304 -0
- package/src/foreign/read-crush.ts +503 -0
- package/src/foreign/read-cursor.ts +778 -0
- package/src/foreign/read-dsh.ts +215 -0
- package/src/foreign/read-dsh4.ts +35 -0
- package/src/foreign/read-gemini.ts +306 -0
- package/src/foreign/read-goose.ts +333 -0
- package/src/foreign/read-grokbuild.ts +475 -0
- package/src/foreign/read-hermes.ts +724 -0
- package/src/foreign/read-kilocode.ts +55 -0
- package/src/foreign/read-kimi.ts +768 -0
- package/src/foreign/read-mimocode.ts +66 -0
- package/src/foreign/read-openclaw.ts +505 -0
- package/src/foreign/read-opencode.ts +994 -0
- package/src/foreign/read-pi.ts +459 -0
- package/src/foreign/read-qoder.ts +351 -0
- package/src/foreign/read-qwen.ts +342 -0
- package/src/foreign/read-reasonix.ts +590 -0
- package/src/foreign/read-teleagent.ts +82 -0
- package/src/foreign/read-trae.ts +488 -0
- package/src/foreign/read-vibe.ts +353 -0
- package/src/foreign/read-workbuddy.ts +374 -0
- package/src/foreign/read-zcode.ts +48 -0
- package/src/foreign/read-zed.ts +479 -0
- package/src/foreign/reasonix.test.ts +336 -0
- package/src/foreign/reasonix.ts +25 -0
- package/src/foreign/registry.test.ts +377 -0
- package/src/foreign/registry.ts +750 -0
- package/src/foreign/session-ir.test.ts +189 -0
- package/src/foreign/session-ir.ts +614 -0
- package/src/foreign/session-read.ts +295 -0
- package/src/foreign/session-source.ts +625 -0
- package/src/foreign/skill-file-caps.test.ts +171 -0
- package/src/foreign/source-modules.ts +86 -0
- package/src/foreign/source-registry.test.ts +190 -0
- package/src/foreign/sqlite.test.ts +313 -0
- package/src/foreign/sqlite.ts +446 -0
- package/src/foreign/teleagent.test.ts +147 -0
- package/src/foreign/teleagent.ts +62 -0
- package/src/foreign/trae.test.ts +316 -0
- package/src/foreign/trae.ts +25 -0
- package/src/foreign/truncation-report.test.ts +238 -0
- package/src/foreign/truth-table.ts +453 -0
- package/src/foreign/types.ts +312 -0
- package/src/foreign/vibe.test.ts +209 -0
- package/src/foreign/vibe.ts +28 -0
- package/src/foreign/workbuddy.test.ts +207 -0
- package/src/foreign/workbuddy.ts +53 -0
- package/src/foreign/zcode.test.ts +224 -0
- package/src/foreign/zcode.ts +60 -0
- package/src/foreign/zed.test.ts +240 -0
- package/src/foreign/zed.ts +62 -0
- package/src/index.facade.test.ts +254 -16
- package/src/index.ts +704 -82
- package/src/market/github-repos.test.ts +98 -1
- package/src/market/github-repos.ts +63 -1
- package/src/market/types.ts +3 -3
- package/src/profiles/dsh-profile-incomplete-copy.test.ts +177 -0
- package/src/profiles/dsh-profile-manager.test.ts +37 -0
- package/src/profiles/dsh-profile-manager.ts +188 -13
- package/src/profiles/dsh-profile-shared.ts +55 -1
- package/src/profiles/index.ts +2 -1
- package/src/routes/consult.ts +53 -4
- package/src/routes/foreign-context.ts +89 -0
- package/src/routes/foreign.test.ts +443 -0
- package/src/routes/foreign.ts +226 -0
- package/src/routes/import.ts +23 -1
- package/src/routes/index.ts +6 -2
- package/src/routes/kit.ts +4 -3
- package/src/routes/market.ts +0 -1
- package/src/routes/prefs.ts +15 -1
- package/src/routes/recovery.test.ts +167 -0
- package/src/routes/recovery.ts +331 -1
- package/src/routes/session-export.ts +308 -0
- package/src/routes/snapshots.ts +12 -2
- package/src/routes/sync.ts +91 -7
- package/src/security/redaction.test.ts +149 -0
- package/src/security/redaction.ts +25 -9
- package/src/security/secret-scanner.test.ts +186 -0
- package/src/security/secret-scanner.ts +57 -1
- package/src/sync/autosync-risk-tier.test.ts +61 -0
- package/src/sync/autosync-scheduler.test.ts +48 -1
- package/src/sync/autosync-scheduler.ts +50 -26
- package/src/sync/backup-files.ts +5 -0
- package/src/sync/gist/gist-msgkeys-catalog.test.ts +33 -0
- package/src/sync/gist/gist-msgkeys.test.ts +71 -0
- package/src/sync/gist/gist-transport.test.ts +308 -0
- package/src/sync/gist/gist-transport.ts +550 -0
- package/src/sync/gist/messages.ts +49 -0
- package/src/sync/retention-prune-all-off.test.ts +50 -0
- package/src/sync/s3/cloud-config.test.ts +283 -0
- package/src/sync/s3/messages.ts +102 -0
- package/src/sync/s3/s3-delete-index-404.test.ts +170 -0
- package/src/sync/s3/s3-delete-index-unreadable.test.ts +141 -0
- package/src/sync/s3/s3-msgkeys.test.ts +101 -0
- package/src/sync/s3/s3-providers.ts +213 -0
- package/src/sync/s3/s3-transport.test.ts +394 -0
- package/src/sync/s3/s3-transport.ts +651 -0
- package/src/sync/s3/s3-upload-index-404.test.ts +171 -0
- package/src/sync/s3/sigv4.test.ts +219 -0
- package/src/sync/s3/sigv4.ts +283 -0
- package/src/sync/sync-config.test.ts +237 -5
- package/src/sync/sync-config.ts +841 -52
- package/src/sync/sync-engine-download-tombstone.test.ts +73 -0
- package/src/sync/sync-engine.ts +5 -3
- package/src/sync/sync-history.ts +1 -1
- package/src/sync/webdav/webdav-delete-index-404.test.ts +178 -0
- package/src/sync/webdav/webdav-delete-index-unreadable.test.ts +143 -0
- package/src/sync/webdav/webdav-msgkeys.test.ts +53 -0
- package/src/sync/webdav/webdav-transport.test.ts +9 -5
- package/src/sync/webdav/webdav-transport.ts +99 -17
- package/src/sync/webdav/webdav-upload-index-404.test.ts +177 -0
- package/src/ui/commands.test.ts +4 -1
- package/src/ui/commands.ts +46 -12
- package/src/ui/disk-usage-view.test.ts +63 -1
- package/src/ui/disk-usage-view.ts +93 -1
- package/src/ui/dsh-profiles-incomplete-view.test.ts +102 -0
- package/src/ui/dsh-profiles-view.ts +94 -0
- package/src/ui/foreign-view.test.ts +241 -0
- package/src/ui/foreign-view.ts +362 -0
- package/src/ui/i18n.ts +164 -0
- package/src/ui/mid-ellipsis.test.ts +5 -10
- package/src/ui/session-inventory-view.test.ts +142 -2
- package/src/ui/session-inventory-view.ts +127 -0
- package/src/ui/session-layout-view.test.ts +361 -0
- package/src/ui/session-layout-view.ts +444 -0
- package/src/ui/sync-repository-picker.test.ts +130 -0
- package/src/ui/sync-repository-picker.ts +180 -0
- package/src/ui/sync-settings-view.test.ts +166 -0
- package/src/ui/sync-settings-view.ts +319 -9
- package/src/ui/types.ts +42 -0
- package/src/utils/asar-read.test.ts +330 -0
- package/src/utils/asar-read.ts +606 -0
- package/src/utils/env-lock.test.ts +55 -0
- package/src/utils/env-lock.ts +6 -1
- package/src/utils/patch-yaml.test.ts +112 -0
- package/src/utils/patch-yaml.ts +72 -0
- package/src/utils/paths.test.ts +63 -0
- package/src/utils/paths.ts +25 -3
- package/src/utils/recursive-walk.test.ts +20 -0
- package/src/utils/recursive-walk.ts +12 -13
- package/src/utils/session-format.test.ts +113 -2
- package/src/utils/session-format.ts +127 -5
- package/src/utils/session-health-scan.test.ts +364 -1
- package/src/utils/session-health-scan.ts +434 -15
- package/src/utils/session-layout-repair-service.test.ts +362 -0
- package/src/utils/session-layout-repair-service.ts +568 -0
- package/src/utils/session-log-repair.test.ts +260 -1
- package/src/utils/session-log-repair.ts +294 -55
- package/src/utils/session-repair-service.test.ts +148 -1
- package/src/utils/session-repair-service.ts +165 -10
- package/src/utils/session-row-facts.test.ts +159 -0
- package/src/utils/session-row-facts.ts +187 -0
- package/src/utils/session-verify.test.ts +762 -0
- package/src/utils/session-verify.ts +784 -0
- package/src/utils/shared-constants.ts +82 -0
|
@@ -0,0 +1,705 @@
|
|
|
1
|
+
var __rewriteRelativeImportExtension = (this && this.__rewriteRelativeImportExtension) || function (path, preserveJsx) {
|
|
2
|
+
if (typeof path === "string" && /^\.\.?\//.test(path)) {
|
|
3
|
+
return path.replace(/\.(tsx)$|((?:\.d)?)((?:\.[^./]+?)?)\.([cm]?)ts$/i, function (m, tsx, d, ext, cm) {
|
|
4
|
+
return tsx ? preserveJsx ? ".jsx" : ".js" : d && (!ext || !cm) ? m : (d + ext + "." + cm.toLowerCase() + "js");
|
|
5
|
+
});
|
|
6
|
+
}
|
|
7
|
+
return path;
|
|
8
|
+
};
|
|
9
|
+
/**
|
|
10
|
+
* 真 codec 复验门(宿主/CLI 专用,node 侧)—— 把一份会话日志**字节**交给本机已装 DSH 的官方
|
|
11
|
+
* @deepseek-ai/dsh-session-format-catalog 走一遍官方读路径,回答的是
|
|
12
|
+
* 「**本机 DSH 真能加载它吗**」,而不是「它自身结构自洽吗」(后者见 session-log-repair.ts 的 sessionLogSelfCheck)。
|
|
13
|
+
*
|
|
14
|
+
* 分层纪律(与 utils/session-log.ts / utils/session-format.ts / utils/session-log-repair.ts 同款):
|
|
15
|
+
* · **只允许宿主侧 import**:src/client/** 与 src/core/** 禁止 import 本模块
|
|
16
|
+
* —— 它经 utils/zstd-frame.ts 间接依赖 node:zlib,core 必须与 DSH 存储格式 + node 内建模块解耦。
|
|
17
|
+
* · 本模块只读字节(绝不动文件系统),失败一律**返回结论**,绝不 throw 到调用方。
|
|
18
|
+
*
|
|
19
|
+
* ── 官方引证(@deepseek-ai/dsh-session-persistence-jsonl/lib/index.js,行号按已装包 142847 B / 3528 行)──
|
|
20
|
+
* 现役读盘路径 = parseHeaderRecord:
|
|
21
|
+
* L970 function parseHeaderRecord(record) { … }
|
|
22
|
+
* L971 首帧形状:非空 + 唯一换行符就是最后一个字节(本模块 extractFrames 逐字复刻)
|
|
23
|
+
* L974 JSON.parse 首行 → L978 必须是 JSON 对象
|
|
24
|
+
* L979 refuseForeignFormatVersion(parsed) // L964-968:version !== SESSION_FORMAT_VERSION 直接拒绝
|
|
25
|
+
* L981 isHeaderLine(parsed)
|
|
26
|
+
* L984-987 restore = sessionFormatCatalog.createRestore(parsed, { recovery: "strict", validation: "transformed" })
|
|
27
|
+
* 两点必须写清:
|
|
28
|
+
* ① 该 createRestore **只在 header.version === SESSION_FORMAT_VERSION(本机 4) 时可达** ——
|
|
29
|
+
* L979 的 refuseForeignFormatVersion 会先把「非本代」的 header 挡掉。所以我们判定 v4 日志用的
|
|
30
|
+
* { strict, transformed } 正是官方现役读盘口径。
|
|
31
|
+
* ② 对 **pre-v4** 日志,本门走的是**迁移链**(v0→v1→…→v4 后恢复),它**不等价于 DSH 读盘路径**:
|
|
32
|
+
* 官方的 resolveCurrentLog(L2850-2861)对 sourceVersion === SESSION_FORMAT_VERSION 返回现役日志、
|
|
33
|
+
* 对 < 本代**返回 undefined**(= 这份日志没有「现役代际」)、对 > 本代抛错;而官方的历史读取路径
|
|
34
|
+
* (L2222-2224)用的是 { recovery: "recoverable", validation: "current" },与现役口径**不同**。
|
|
35
|
+
* 所以结果里另有 equivalentToReadPath:仅当 header.version === catalog.currentVersion === 已装
|
|
36
|
+
* SESSION_FORMAT_VERSION 才为 true(「DSH 现在就能直接读」);pre-v4 迁移链成功时 verified:true
|
|
37
|
+
* 但 equivalentToReadPath:false(「迁移链能还原,但这不是 DSH 的现役读盘」)。
|
|
38
|
+
*
|
|
39
|
+
* ── catalog 解析顺序与资格审查(缺一即 unavailable,绝不猜)─────────────────
|
|
40
|
+
* ① **裸模块动态 import**(第一顺位):await import('@deepseek-ai/dsh-session-format-catalog') ——
|
|
41
|
+
* 已装插件自己依赖树里的那一份(开发树 / 档案 node_modules / app.asar 内均按 Node 解析规则命中);
|
|
42
|
+
* 再用 createRequire(import.meta.url).resolve('<pkg>/package.json') 反推它的 node_modules 根,供代际闸门用。
|
|
43
|
+
* ② **运行时锚点的 asar 容器**:<process.resourcesPath>/app.asar/dsh/node_modules(含 .unpacked 与解包 app 两种形态)
|
|
44
|
+
* —— 这是 profileContext.installAnchor 在服务层(拿不到 profileContext 的地方)的进程内等价物。
|
|
45
|
+
* 纯 node 下这条候选**落在 asar 容器里**,直接 existsSync/import 都不可能命中 ⇒ 交给
|
|
46
|
+
* `utils/asar-read.ts` 的最小只读读取器:把 catalog **及其依赖闭包**(真机 37 包 / 940 文件 / 8.4 MB)
|
|
47
|
+
* 取出到缓存目录(缺省 `<os.tmpdir()>/dsh-cm-asar-codec`,可用 `asarCacheDir` 注入)再 import;
|
|
48
|
+
* 命中缓存不重复解包,源 asar 全程只读(细节与拒绝条件见该模块文件头)。这种解析在内部/结果里
|
|
49
|
+
* 标 `via: "asar-extract"`(`detail` 写成 `runtime-anchor+asar-extract`,仍**不含绝对路径**)。
|
|
50
|
+
* ③ **显式安装根锚点**:环境变量 `DSH_CM_DSH_INSTALL` = DSH 安装根(含 `resources/app.asar` 的那一层)
|
|
51
|
+
* → `<root>/resources/app.asar/dsh/node_modules/…`(标签 `env-anchor`,排在运行时锚点之前)。
|
|
52
|
+
* **只在用户显式给出时使用**。它与其余锚点一样同时进**候选链**与**权威版本解析**
|
|
53
|
+
* (两条链共用 `anchorPlans()` 这一份计划表;早先只有权威侧看 env,纯 node 只设 env 会误判 unavailable —— B3/F1)。
|
|
54
|
+
* ④ **<home>/profiles/node_modules/@deepseek-ai/dsh-session-format-catalog**(以及 <home>/profiles/<profile>/… )
|
|
55
|
+
* —— 与 src/index.ts 的 dshPackageJsonCandidates 第 ②③ 条同源,每处再试 hoisted 与 pnpm 嵌套两种布局
|
|
56
|
+
* (deriveRoots 与 utils/session-format.ts 的 resolveSessionFormatVersion 逐字同款)。
|
|
57
|
+
* 代际闸门:候选 catalog 必须暴露 currentVersion,且必须等于**权威「已装版本」**——
|
|
58
|
+
* install-anchor(profileContext.installAnchor,最高优先)→ runtime-anchor(<resources>/app.asar|app 下的
|
|
59
|
+
* dsh/node_modules)→ profiles-tree(<home>/profiles/**),取第一个读得出来的 @deepseek-ai/dsh-session 常量
|
|
60
|
+
* (utils/session-format.ts 的 readSessionFormatVersionAt;顺序与 src/index.ts 的 dshPackageJsonCandidates 同源)。
|
|
61
|
+
* 调用方显式给了 dshPackageJsonCandidates 时,它**就是**权威清单(标签 anchor)。
|
|
62
|
+
* 读不到任一侧 → 换下一个候选;全部不匹配 → unavailable,detail 写清试过的来源与失败原因
|
|
63
|
+
* (generation-mismatch / no-installed-version(<来源>) 等)。
|
|
64
|
+
* API 闸门:包内没有 createSessionFormatCatalogWithChildren(只有静态 sessionFormatCatalog)时,
|
|
65
|
+
* **仅当** header.version === catalog.currentVersion 才可用(本代日志不过 v3→v4 边界、不需要 child 事实);
|
|
66
|
+
* 否则 unavailable(detail='children-required')。例:磁盘上那份 0.1.5-rc.2(currentVersion=3)对 v4 日志必须
|
|
67
|
+
* unavailable,而不是被当成「日志有错」。
|
|
68
|
+
* detail 只写**机器可读码 + 候选标签**(bare-module / runtime-anchor / profiles-tree / anchor / install-anchor),
|
|
69
|
+
* **绝不写绝对路径**(结果会经路由回传浏览器)。
|
|
70
|
+
* 缓存纪律(V2-F1 修复点):进程内缓存只存**与日志无关**的候选事实(catalog / currentVersion / viaChildren /
|
|
71
|
+
* 当时的权威版本),**绝不缓存「某个 headerVersion 下判定通过」这个结论** —— 依赖日志 header 的 API 闸门
|
|
72
|
+
* 与 equivalentToReadPath **每次调用都按本日志的 headerVersion 重算**;权威版本变了缓存条目即作废。
|
|
73
|
+
* 旧实现按根命中就直接返回(还丢掉 currentVersion),后果有二:同一份 v4 日志同进程第二次复验的
|
|
74
|
+
* equivalentToReadPath 由 true 翻 false(CLI 文案在「现役读盘可读 / 迁移链可还原」间翻转);以及
|
|
75
|
+
* 本机 0.1.5-rc.2 静态 catalog 被复用到 v4 单元 → createRestore 抛
|
|
76
|
+
* "stored Session uses newer format v4; this build writes v3" → 判 **decode-failed ⇒ 误回滚 + 不写台账**,
|
|
77
|
+
* 而新进程同输入是 unavailable(不回滚 + 写台账)。两条确定性回归测试钉住这两个后果。
|
|
78
|
+
* V2-F2(已修):旧实现用**被判定候选自己那棵树**的 SESSION_FORMAT_VERSION 当「已装版本」(自证),于是
|
|
79
|
+
* 磁盘上 0.1.5-rc.2 那棵树(currentVersion 3)把 v3 日志标成「现役读盘可读」,而真实运行的 DSH 是
|
|
80
|
+
* asar 里的 0.2.0(v4)—— 结论对运行环境不诚实。现在「已装版本」一律走上面的权威解析顺序
|
|
81
|
+
* (installAnchor 同源),候选自己那棵树只作为**被判定对象**;权威侧读不到 → unavailable(不猜)。
|
|
82
|
+
* 缺口⑤(本模块的接线,2026-10):**asar 抽取现在是产品路径** —— runtime-anchor 候选落在 app.asar 容器里
|
|
83
|
+
* 且直接 import 失败时,用 `utils/asar-read.ts` 的只读读取器把 catalog 及其依赖闭包取出到缓存目录再 import
|
|
84
|
+
* (见上 ②)。于是纯 node 的 CLI/离线救急台也能拿到**正在跑的 DSH** 那份真 codec,不再必然退化到
|
|
85
|
+
* profiles-tree 的旧代际静态 catalog;结果里的 `via: "asar-extract"` 让调用方与台账能看见这条路径。
|
|
86
|
+
* 仍然**不放宽**任何判据:asar 缺失 / 不可读 / 解不出 / 代际对不上 → 一律如实 unavailable
|
|
87
|
+
* (绝不伪造 verified,也绝不因此把日志判成 decode-failed)。
|
|
88
|
+
* 锚点的共同前提:必须由外部**显式**给出 —— 宿主注入 `installAnchor`、进程环境变量 `DSH_CM_DSH_INSTALL`、
|
|
89
|
+
* 或 Electron 的 `process.resourcesPath`。**「离线 CLI 自动发现安装位置」在本仓未实现**:
|
|
90
|
+
* 本模块不扫盘、不猜默认安装路径;纯 node 下三者都拿不到时如实判 unavailable(不伪造 verified)。
|
|
91
|
+
*
|
|
92
|
+
* ── recovery / validation(对着已安装包实测确定,不抄文档)────────────────────
|
|
93
|
+
* · recovery: 'strict' 写死。日志是**我们刚改写过的字节**,必须逐行严格成立;用 'recoverable' 会接受
|
|
94
|
+
* 「丢掉一行 + 丢掉其后未提交后缀」的残缺前缀(dsh-session-format/README.md 第 48 行明说),
|
|
95
|
+
* 那等于把「DSH 只能读到一半」判成通过。官方现役路径(L984-987)也是 strict。
|
|
96
|
+
* · validation: 'transformed' 为**判定门**(与官方现役路径逐字一致):catalog/lib/index.js L355/L357 显示
|
|
97
|
+
* 非 'current' 时取 options.restoreTransformedCurrent,而 L61-63 里 restoreTransformedCurrent 就是
|
|
98
|
+
* restoreReleasedV4Artifact(...);L56-59 的 restoreCurrent 才额外跑 validateInstalledCurrentSessionArtifact
|
|
99
|
+
* (已安装 build 的语义校验)。用 'current' 当判定门会把「已安装 build 的语义校验」当硬失败,
|
|
100
|
+
* 对「官方读盘路径能读」的日志造成**假回滚**,所以它只作结果里的 strong / strongDetail 增强字段。
|
|
101
|
+
* (旁证:dsh-session-format-v3-to-v4/README.md L280-281 表格把 validation:'transformed' 记为
|
|
102
|
+
* 「迁移后跑代际自有 V4 恢复、跳过已安装 Session 的通用校验」。)
|
|
103
|
+
*
|
|
104
|
+
* ── children(v3→v4 迁移的显式子会话事实)──────────────────────────────────
|
|
105
|
+
* 优先 createSessionFormatCatalogWithChildren(children):静态 sessionFormatCatalog 的 v3→v4 边在 createStage()
|
|
106
|
+
* 时无条件抛「V3 catalog migration requires explicit historical child facts, including an empty array for a
|
|
107
|
+
* parent without children」(catalog 实测;旁证 dsh-session-format-v3-to-v4/README.md 第 51 行)。
|
|
108
|
+
* 该行同时明确:**空数组 = 显式声明「这份父会话没有子会话」**,是合法绑定 —— 所以默认 []。
|
|
109
|
+
* 若 codec 仍抛 children 相关错误 → 判 unavailable(detail='children-required'):我们缺的是**事实**,
|
|
110
|
+
* 而不是日志有问题;服务层据此**不回滚、不宣称已验证**(诚实优于伪造)。
|
|
111
|
+
*/
|
|
112
|
+
import { existsSync } from 'node:fs';
|
|
113
|
+
import { createRequire } from 'node:module';
|
|
114
|
+
import { dirname, join } from 'node:path';
|
|
115
|
+
import { pathToFileURL } from 'node:url';
|
|
116
|
+
import { extractAsarPackages, extractedEntryPath, readAsarTextEntry } from './asar-read.js';
|
|
117
|
+
import { parseSessionFormatVersion, readPackageVersionAt, readSessionFormatVersionAt, sessionFormatRoots } from './session-format.js';
|
|
118
|
+
import { decodeZstdFrame, scanZstdFrames, zstdAvailable } from './zstd-frame.js';
|
|
119
|
+
export const SESSION_VERIFY_REASONS = ['unavailable', 'invalid-header', 'decode-failed', 'finish-failed'];
|
|
120
|
+
/** 解析台账里的旧字段用(台账记录来自 JSON 反序列化,形状不可信)。 */
|
|
121
|
+
export function isSessionVerifyReason(value) {
|
|
122
|
+
return typeof value === 'string' && SESSION_VERIFY_REASONS.includes(value);
|
|
123
|
+
}
|
|
124
|
+
/* ------------------------------------------------- 候选与布局(与 session-format.ts 同款) */
|
|
125
|
+
const DSH_PKG_REL = join('@deepseek-ai', 'dsh');
|
|
126
|
+
const DSH_SESSION_PKG_REL = join('@deepseek-ai', 'dsh-session');
|
|
127
|
+
const CATALOG_PKG = '@deepseek-ai/dsh-session-format-catalog';
|
|
128
|
+
const CATALOG_PKG_REL = join('@deepseek-ai', 'dsh-session-format-catalog');
|
|
129
|
+
const CATALOG_ENTRY_REL = join('lib', 'index.js');
|
|
130
|
+
/** 包内入口的 POSIX 相对路径(asar 内路径一律 POSIX);解包目录定位用。 */
|
|
131
|
+
const CATALOG_ENTRY_IN_NM = CATALOG_PKG_REL.split('\\').join('/') + '/' + CATALOG_ENTRY_REL.split('\\').join('/');
|
|
132
|
+
/** 显式安装根锚点(**只在用户显式给出时使用**,见文件头)。 */
|
|
133
|
+
const DSH_INSTALL_ENV = 'DSH_CM_DSH_INSTALL';
|
|
134
|
+
/**
|
|
135
|
+
* 候选 @deepseek-ai/dsh/package.json → 待探测的 node_modules 根。
|
|
136
|
+
*
|
|
137
|
+
* 与 utils/session-format.ts 的 `sessionFormatRoots` **同一份实现**(单一事实源):hoisted 同级 →
|
|
138
|
+
* dsh/node_modules 嵌套 → pnpm 隔离 store(issue #74)。这里解的是同树 dsh-session-format-catalog,
|
|
139
|
+
* 那里解的是同树 dsh-session 的格式常量 —— 布局推导只能有一处,否则同一个坑会换个入口复发。
|
|
140
|
+
*/
|
|
141
|
+
export function nodeModulesRootsFor(dshPackageJsonCandidates) {
|
|
142
|
+
const roots = [];
|
|
143
|
+
const seen = new Set();
|
|
144
|
+
for (const candidate of dshPackageJsonCandidates) {
|
|
145
|
+
const nodeModulesDir = join(dirname(candidate), '..', '..');
|
|
146
|
+
for (const root of sessionFormatRoots(nodeModulesDir, readPackageVersionAt(candidate))) {
|
|
147
|
+
if (seen.has(root))
|
|
148
|
+
continue;
|
|
149
|
+
seen.add(root);
|
|
150
|
+
roots.push(root);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
return roots;
|
|
154
|
+
}
|
|
155
|
+
/**
|
|
156
|
+
* 锚点候选清单(**公开视图**):env-anchor(`DSH_CM_DSH_INSTALL`)→ runtime-anchor(拉起本进程的 runtime)
|
|
157
|
+
* → profiles-tree(本机档案树)。与 `anchorPlans()` 的**同一份来源、同一顺序**。
|
|
158
|
+
*
|
|
159
|
+
* 调用关系(写清楚,避免「注释承诺 > 实际防线」):缺省路径**不调用本函数** —— `resolveCatalog` 与
|
|
160
|
+
* `resolveAuthoritativeVersion` 都直接吃 `anchorPlans()` 那张(标签 + 候选)计划表;本函数是同一份来源的
|
|
161
|
+
* 扁平清单,供**需要显式传 `dshPackageJsonCandidates` 的调用方与诊断**使用(显式传它等于把整条锚点链
|
|
162
|
+
* 换成这一份清单,标签统一为 `anchor`)。
|
|
163
|
+
*
|
|
164
|
+
* ① env-anchor:<DSH_CM_DSH_INSTALL>/resources/app.asar/dsh/node_modules/…(用户显式给出的安装根)。
|
|
165
|
+
* ② runtime-anchor:<process.resourcesPath>/app.asar|app.asar.unpacked|app 下的 dsh/node_modules
|
|
166
|
+
* —— profileContext.installAnchor 在服务层(拿不到 profileContext 的地方)的进程内等价物。
|
|
167
|
+
* ③ profiles-tree:与 src/index.ts 的 dshPackageJsonCandidates 第 ②③ 条逐字同款:<home>/profiles/<profile>/、
|
|
168
|
+
* <home>/profiles/、<home>/profiles/web/。
|
|
169
|
+
*
|
|
170
|
+
* 顺序就是真伪顺序:真机实测「profile hoisted 树」是 web 档案装出来的旧副本(本机 0.1.5-rc.2),
|
|
171
|
+
* 桌面端实际跑的是 app.asar 里的 0.2.0-rc.2 —— 所以锚点必须排在前面。
|
|
172
|
+
*/
|
|
173
|
+
export function defaultDshPackageJsonCandidates(homeDir, profile) {
|
|
174
|
+
return [...envInstallAnchorCandidates(), ...runtimeAnchorCandidates(), ...profileCandidates(homeDir, profile)];
|
|
175
|
+
}
|
|
176
|
+
/**
|
|
177
|
+
* 显式安装根锚点:`DSH_CM_DSH_INSTALL` = **DSH 安装根**(就是含 `resources/app.asar` 的那一层,
|
|
178
|
+
* 例如 `D:\Apps\DSH`)。只在用户显式给出时使用 —— **绝不**扫盘或猜默认安装路径。
|
|
179
|
+
*/
|
|
180
|
+
function envInstallAnchorCandidates() {
|
|
181
|
+
const root = process.env[DSH_INSTALL_ENV];
|
|
182
|
+
if (typeof root !== 'string' || root === '')
|
|
183
|
+
return [];
|
|
184
|
+
return anchorCandidatesAt(join(root, 'resources'));
|
|
185
|
+
}
|
|
186
|
+
/** 运行时锚点候选(Electron resources 下的 asar / 解包 app);非 Electron 进程为空。 */
|
|
187
|
+
function runtimeAnchorCandidates() {
|
|
188
|
+
const resources = process.resourcesPath;
|
|
189
|
+
if (typeof resources !== 'string' || resources === '')
|
|
190
|
+
return [];
|
|
191
|
+
return anchorCandidatesAt(resources);
|
|
192
|
+
}
|
|
193
|
+
/** 一个 resources 目录下的三种形态(打包 asar / asarUnpack / 解包 app)。 */
|
|
194
|
+
function anchorCandidatesAt(resources) {
|
|
195
|
+
const bases = [join('app.asar', 'dsh', 'node_modules'), join('app.asar.unpacked', 'dsh', 'node_modules'), join('app', 'dsh', 'node_modules')];
|
|
196
|
+
return bases.map((base) => join(resources, base, DSH_PKG_REL, 'package.json'));
|
|
197
|
+
}
|
|
198
|
+
/** 本机 profile 树候选(与 src/index.ts 的 dshPackageJsonCandidates ②③ 同源)。 */
|
|
199
|
+
function profileCandidates(homeDir, profile) {
|
|
200
|
+
const home = homeDir !== undefined && homeDir !== '' ? homeDir : process.env['DSH_HOME'];
|
|
201
|
+
if (home === undefined || home === '')
|
|
202
|
+
return [];
|
|
203
|
+
const out = [];
|
|
204
|
+
if (profile !== undefined && profile !== '')
|
|
205
|
+
out.push(join(home, 'profiles', profile, 'node_modules', DSH_PKG_REL, 'package.json'));
|
|
206
|
+
out.push(join(home, 'profiles', 'node_modules', DSH_PKG_REL, 'package.json'), join(home, 'profiles', 'web', 'node_modules', DSH_PKG_REL, 'package.json'));
|
|
207
|
+
return out;
|
|
208
|
+
}
|
|
209
|
+
/* ------------------------------------------------------------------- 进程内缓存 */
|
|
210
|
+
/** 已 import 的 catalog 模块(键 = 文件 URL;失败也缓存,避免反复探测)。 */
|
|
211
|
+
const moduleCache = new Map();
|
|
212
|
+
const catalogCache = new Map();
|
|
213
|
+
/** 清空进程内缓存(测试隔离用)。 */
|
|
214
|
+
export function clearSessionVerifyCache() {
|
|
215
|
+
moduleCache.clear();
|
|
216
|
+
catalogCache.clear();
|
|
217
|
+
}
|
|
218
|
+
async function importCatalogModule(file) {
|
|
219
|
+
if (moduleCache.has(file))
|
|
220
|
+
return moduleCache.get(file);
|
|
221
|
+
let mod;
|
|
222
|
+
try {
|
|
223
|
+
mod = await import(__rewriteRelativeImportExtension(pathToFileURL(file).href));
|
|
224
|
+
}
|
|
225
|
+
catch {
|
|
226
|
+
mod = undefined;
|
|
227
|
+
}
|
|
228
|
+
moduleCache.set(file, mod);
|
|
229
|
+
return mod;
|
|
230
|
+
}
|
|
231
|
+
/** 裸模块动态 import(第一顺位候选);失败返回 undefined。 */
|
|
232
|
+
async function importBareCatalog() {
|
|
233
|
+
const key = '\u0000bare\u0000' + CATALOG_PKG;
|
|
234
|
+
if (moduleCache.has(key))
|
|
235
|
+
return moduleCache.get(key);
|
|
236
|
+
let mod;
|
|
237
|
+
try {
|
|
238
|
+
mod = await import(__rewriteRelativeImportExtension(CATALOG_PKG));
|
|
239
|
+
}
|
|
240
|
+
catch {
|
|
241
|
+
mod = undefined;
|
|
242
|
+
}
|
|
243
|
+
moduleCache.set(key, mod);
|
|
244
|
+
return mod;
|
|
245
|
+
}
|
|
246
|
+
/** 裸模块自己的 node_modules 根(供代际闸门读同树的 dsh-session 常量);反推不到 → undefined(不猜)。 */
|
|
247
|
+
function bareCatalogRoot() {
|
|
248
|
+
try {
|
|
249
|
+
const require = createRequire(import.meta.url);
|
|
250
|
+
const pkgJson = require.resolve(CATALOG_PKG + '/package.json');
|
|
251
|
+
return join(dirname(pkgJson), '..', '..');
|
|
252
|
+
}
|
|
253
|
+
catch {
|
|
254
|
+
return undefined;
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
function isSessionVerifyCatalog(value) {
|
|
258
|
+
return typeof value === 'object' && value !== null && typeof value.createRestore === 'function';
|
|
259
|
+
}
|
|
260
|
+
/**
|
|
261
|
+
* 锚点计划(**标签 + 候选清单**)—— 权威「已装版本」解析与 catalog 候选规划**共用这一份**(B3/F1)。
|
|
262
|
+
*
|
|
263
|
+
* 顺序即真伪顺序:install-anchor(profileContext.installAnchor)→ env-anchor(显式 \`DSH_CM_DSH_INSTALL\`)
|
|
264
|
+
* → runtime-anchor(<resources>/app.asar|app)→ profiles-tree(<home>/profiles/**)。
|
|
265
|
+
* 调用方显式给了 dshPackageJsonCandidates 时,它**就是**唯一清单(标签 anchor)。
|
|
266
|
+
*
|
|
267
|
+
* 为什么必须共用:这两处一旦分叉,就会出现「权威版本按 env 锚点算出来了、catalog 候选却根本不看 env」
|
|
268
|
+
* —— 纯 node 只设 env 时永远 unavailable,而显式传 candidates 却能 verified(正是 B3/F1 的现场)。
|
|
269
|
+
*/
|
|
270
|
+
function anchorPlans(options) {
|
|
271
|
+
if (options.dshPackageJsonCandidates !== undefined)
|
|
272
|
+
return [{ label: 'anchor', candidates: [...options.dshPackageJsonCandidates] }];
|
|
273
|
+
const plans = [];
|
|
274
|
+
if (options.installAnchor !== undefined && options.installAnchor !== '')
|
|
275
|
+
plans.push({ label: 'install-anchor', candidates: [options.installAnchor] });
|
|
276
|
+
const env = envInstallAnchorCandidates();
|
|
277
|
+
if (env.length > 0)
|
|
278
|
+
plans.push({ label: 'env-anchor', candidates: env });
|
|
279
|
+
const runtime = runtimeAnchorCandidates();
|
|
280
|
+
if (runtime.length > 0)
|
|
281
|
+
plans.push({ label: 'runtime-anchor', candidates: runtime });
|
|
282
|
+
const profiles = profileCandidates(options.homeDir, options.profile);
|
|
283
|
+
if (profiles.length > 0)
|
|
284
|
+
plans.push({ label: 'profiles-tree', candidates: profiles });
|
|
285
|
+
return plans;
|
|
286
|
+
}
|
|
287
|
+
/**
|
|
288
|
+
* 权威「已装 DSH 格式版本」:按 [installAnchor → 运行时锚点(asar/app) → <home>/profiles/**] 顺序,取第一个
|
|
289
|
+
* 读得出来的 @deepseek-ai/dsh-session 常量(与 utils/session-format.ts 的 resolveSessionFormatVersion 同源同序)。
|
|
290
|
+
* 读不到 → undefined:**不猜**,调用方据此直接判 unavailable。
|
|
291
|
+
*
|
|
292
|
+
* V2-F2 修复点:**绝不拿被判定的那个 catalog 候选自己那棵树当权威**。旧实现用 readSessionFormatVersionAt(候选根)
|
|
293
|
+
* 自证,于是磁盘上 0.1.5-rc.2 那棵树(currentVersion 3)把 v3 日志标成「现役读盘可读」,而真实运行的 DSH 是 v4
|
|
294
|
+
* (asar 里的 0.2.0)—— 同一输入在新/旧进程给出不同代际结论,且对运行环境不诚实。
|
|
295
|
+
*/
|
|
296
|
+
async function resolveAuthoritativeVersion(options) {
|
|
297
|
+
const tried = [];
|
|
298
|
+
for (const plan of anchorPlans(options)) {
|
|
299
|
+
for (const root of nodeModulesRootsFor(plan.candidates)) {
|
|
300
|
+
// 磁盘树优先(解包 app / 档案树),asar 容器内的同树常量走只读读取器(缺口⑤)
|
|
301
|
+
const version = readSessionFormatVersionAt(root) ?? (await readSessionFormatVersionInAsar(root));
|
|
302
|
+
if (version !== undefined)
|
|
303
|
+
return { version, tried };
|
|
304
|
+
}
|
|
305
|
+
tried.push(plan.label);
|
|
306
|
+
}
|
|
307
|
+
return { tried };
|
|
308
|
+
}
|
|
309
|
+
/* ------------------------------------------------------------------ asar 容器 */
|
|
310
|
+
/**
|
|
311
|
+
* 把「可能位于 asar 容器内的路径」拆成 <容器路径> + <容器内相对前缀>(POSIX)。
|
|
312
|
+
* 只认以 `.asar` 结尾的路径段 —— `app.asar.unpacked` 不匹配(它的实体是普通目录)。
|
|
313
|
+
*/
|
|
314
|
+
export function splitAsarPath(p) {
|
|
315
|
+
const normalized = p.replace(/\\/g, '/');
|
|
316
|
+
const segments = normalized.split('/').filter((segment) => segment !== '');
|
|
317
|
+
const index = segments.findIndex((segment) => /\.asar$/i.test(segment));
|
|
318
|
+
if (index < 0)
|
|
319
|
+
return undefined;
|
|
320
|
+
const leading = normalized.startsWith('/') ? '/' : '';
|
|
321
|
+
return { asarPath: leading + segments.slice(0, index + 1).join('/'), innerPrefix: segments.slice(index + 1).join('/') };
|
|
322
|
+
}
|
|
323
|
+
/** 从 asar 里的 node_modules 根读同树 @deepseek-ai/dsh-session 的格式常量;读不到 → undefined(不猜)。 */
|
|
324
|
+
async function readSessionFormatVersionInAsar(root) {
|
|
325
|
+
const split = splitAsarPath(root);
|
|
326
|
+
if (split === undefined)
|
|
327
|
+
return undefined;
|
|
328
|
+
const rel = [...split.innerPrefix.split('/'), ...DSH_SESSION_PKG_REL.split('\\'), 'lib', 'index.js'].filter((part) => part !== '').join('/');
|
|
329
|
+
try {
|
|
330
|
+
const text = await readAsarTextEntry(split.asarPath, rel);
|
|
331
|
+
return text === undefined ? undefined : parseSessionFormatVersion(text);
|
|
332
|
+
}
|
|
333
|
+
catch {
|
|
334
|
+
return undefined;
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
function existsSafe(file) {
|
|
338
|
+
try {
|
|
339
|
+
return existsSync(file);
|
|
340
|
+
}
|
|
341
|
+
catch {
|
|
342
|
+
return false;
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
/**
|
|
346
|
+
* runtime-anchor 候选落在 asar 容器里时的产品路径:把 catalog **及其依赖闭包**取出到缓存目录再 import。
|
|
347
|
+
* 缓存被外部破坏(清单在、入口文件没了)时强制重解一次;一切失败都如实返回 undefined(判 unavailable)。
|
|
348
|
+
*/
|
|
349
|
+
async function extractCatalogFromAsar(split, options) {
|
|
350
|
+
const resolveFrom = split.innerPrefix === '' ? undefined : split.innerPrefix;
|
|
351
|
+
const run = (refresh) => extractAsarPackages({
|
|
352
|
+
asarPath: split.asarPath,
|
|
353
|
+
packages: [CATALOG_PKG],
|
|
354
|
+
...(resolveFrom !== undefined ? { resolveFrom } : {}),
|
|
355
|
+
...(options.asarCacheDir !== undefined ? { cacheDir: options.asarCacheDir } : {}),
|
|
356
|
+
...(refresh ? { refresh: true } : {}),
|
|
357
|
+
});
|
|
358
|
+
try {
|
|
359
|
+
for (const refresh of [false, true]) {
|
|
360
|
+
const result = await run(refresh);
|
|
361
|
+
if (!result.ok)
|
|
362
|
+
return undefined;
|
|
363
|
+
const entry = extractedEntryPath(result.dir, split.innerPrefix, CATALOG_ENTRY_IN_NM);
|
|
364
|
+
if (existsSafe(entry))
|
|
365
|
+
return entry;
|
|
366
|
+
}
|
|
367
|
+
return undefined;
|
|
368
|
+
}
|
|
369
|
+
catch {
|
|
370
|
+
// 读取器按纪律不 throw;这里只兜住不可预见的异常,一律如实判「取不出」
|
|
371
|
+
return undefined;
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
/** 装配一个候选模块(**与日志无关**的那一半判定)。 */
|
|
375
|
+
function assembleCandidate(mod, children) {
|
|
376
|
+
// 注意:官方 catalog 包**不导出模块级 createRestore**(导出的只有 createSessionFormatCatalogWithChildren /
|
|
377
|
+
// sessionFormatCatalog / historicalSessionFormatCatalog 等),createRestore 是**装配出的 catalog 对象**上的方法
|
|
378
|
+
// —— 所以这里只判「装配结果是不是一个带 createRestore 的 catalog」,别按模块级导出判(那会把真包判死)。
|
|
379
|
+
const record = mod;
|
|
380
|
+
const assemble = record['createSessionFormatCatalogWithChildren'];
|
|
381
|
+
const viaChildren = typeof assemble === 'function';
|
|
382
|
+
let catalog;
|
|
383
|
+
if (viaChildren) {
|
|
384
|
+
try {
|
|
385
|
+
catalog = assemble(children);
|
|
386
|
+
}
|
|
387
|
+
catch {
|
|
388
|
+
return { ok: false, code: 'assemble-failed' };
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
else {
|
|
392
|
+
catalog = record['sessionFormatCatalog'];
|
|
393
|
+
}
|
|
394
|
+
if (!isSessionVerifyCatalog(catalog))
|
|
395
|
+
return { ok: false, code: 'no-createRestore' };
|
|
396
|
+
const currentVersion = catalog.currentVersion;
|
|
397
|
+
if (typeof currentVersion !== 'number')
|
|
398
|
+
return { ok: false, code: 'no-current-version' };
|
|
399
|
+
return { ok: true, catalog, currentVersion, viaChildren };
|
|
400
|
+
}
|
|
401
|
+
/**
|
|
402
|
+
* **每次调用都必须重跑**的、依赖本日志 header 的闸门(V2-F1 修复点)。
|
|
403
|
+
*
|
|
404
|
+
* 静态 catalog(模块没导出 children 版装配函数)在 v3→v4 边界绑不了 child 事实,只有「这份日志本来就等于它的
|
|
405
|
+
* 现役代际」才是可信读盘,否则 unavailable(children-required)。**缓存命中也不例外** —— 旧实现命中缓存就直接
|
|
406
|
+
* 返回(还丢掉了 currentVersion),于是同一份 v4 日志在同进程第二次调用会由「现役可读」翻成「迁移链可还原」,
|
|
407
|
+
* 甚至把 v4 交给 v3 codec 判 decode-failed ⇒ 误回滚。
|
|
408
|
+
*/
|
|
409
|
+
function headerGateFailure(facts, headerVersion) {
|
|
410
|
+
if (facts.viaChildren)
|
|
411
|
+
return undefined;
|
|
412
|
+
return headerVersion === facts.currentVersion ? undefined : 'children-required';
|
|
413
|
+
}
|
|
414
|
+
/** 试一个候选根:缓存只提供「与日志无关的事实」,依赖 header 的闸门每次重跑。 */
|
|
415
|
+
function pickCandidate(mod, root, children, headerVersion, installedVersion, label, via) {
|
|
416
|
+
let facts;
|
|
417
|
+
if (children.length === 0) {
|
|
418
|
+
const cached = catalogCache.get(root);
|
|
419
|
+
// 权威版本变了(换锚点 / 装了别的)→ 缓存作废,重跑与日志无关的那一半
|
|
420
|
+
if (cached !== undefined && cached.installedVersion === installedVersion)
|
|
421
|
+
facts = cached;
|
|
422
|
+
}
|
|
423
|
+
if (facts === undefined) {
|
|
424
|
+
const verdict = assembleCandidate(mod, children);
|
|
425
|
+
if (!verdict.ok)
|
|
426
|
+
return { ok: false, code: verdict.code };
|
|
427
|
+
if (verdict.currentVersion !== installedVersion)
|
|
428
|
+
return { ok: false, code: 'generation-mismatch' };
|
|
429
|
+
facts = { catalog: verdict.catalog, currentVersion: verdict.currentVersion, viaChildren: verdict.viaChildren, installedVersion };
|
|
430
|
+
if (children.length === 0)
|
|
431
|
+
catalogCache.set(root, facts);
|
|
432
|
+
}
|
|
433
|
+
const gate = headerGateFailure(facts, headerVersion);
|
|
434
|
+
if (gate !== undefined)
|
|
435
|
+
return { ok: false, code: gate };
|
|
436
|
+
return { ok: true, resolution: { catalog: facts.catalog, currentVersion: facts.currentVersion, installedVersion, detail: label, ...(via !== undefined ? { via } : {}) } };
|
|
437
|
+
}
|
|
438
|
+
async function resolveCatalog(options, headerVersion) {
|
|
439
|
+
if (options.catalog === null)
|
|
440
|
+
return { detail: 'catalog-disabled' };
|
|
441
|
+
if (options.catalog !== undefined) {
|
|
442
|
+
// 注入 catalog = 调用方声明「这就是判定用的本机 codec」⇒ 它的 currentVersion 即权威版本(不再另找)
|
|
443
|
+
return {
|
|
444
|
+
catalog: options.catalog,
|
|
445
|
+
...(typeof options.catalog.currentVersion === 'number' ? { currentVersion: options.catalog.currentVersion } : {}),
|
|
446
|
+
detail: 'injected',
|
|
447
|
+
};
|
|
448
|
+
}
|
|
449
|
+
const children = options.children ?? [];
|
|
450
|
+
const notes = [];
|
|
451
|
+
const noted = new Set();
|
|
452
|
+
const note = (label, code) => {
|
|
453
|
+
const entry = label + ':' + code;
|
|
454
|
+
if (noted.has(entry))
|
|
455
|
+
return;
|
|
456
|
+
noted.add(entry);
|
|
457
|
+
notes.push(entry);
|
|
458
|
+
};
|
|
459
|
+
// 「已装版本」先**独立**解析(权威解析,与被判定的候选自己那棵树无关);读不到 → 不猜(detail 带上试过的来源)
|
|
460
|
+
const authority = await resolveAuthoritativeVersion(options);
|
|
461
|
+
if (authority.version === undefined) {
|
|
462
|
+
return { detail: authority.tried.length > 0 ? 'no-installed-version(' + authority.tried.join('+') + ')' : 'no-installed-version' };
|
|
463
|
+
}
|
|
464
|
+
const installedVersion = authority.version;
|
|
465
|
+
// ① 裸模块(第一顺位):已装插件自己依赖树里的那一份
|
|
466
|
+
const bare = await importBareCatalog();
|
|
467
|
+
if (bare === undefined) {
|
|
468
|
+
note('bare-module', 'import-failed');
|
|
469
|
+
}
|
|
470
|
+
else {
|
|
471
|
+
const root = bareCatalogRoot();
|
|
472
|
+
if (root === undefined) {
|
|
473
|
+
note('bare-module', 'no-version-anchor');
|
|
474
|
+
}
|
|
475
|
+
else {
|
|
476
|
+
const picked = pickCandidate(bare, root, children, headerVersion, installedVersion, 'bare-module');
|
|
477
|
+
if (picked.ok)
|
|
478
|
+
return picked.resolution;
|
|
479
|
+
note('bare-module', picked.code);
|
|
480
|
+
}
|
|
481
|
+
}
|
|
482
|
+
// ②③④ 候选链与权威版本解析**同源同序**(anchorPlans 是唯一那份计划表,B3/F1):
|
|
483
|
+
// 显式 candidates → anchor;否则 install-anchor → env-anchor → runtime-anchor → profiles-tree。
|
|
484
|
+
const plans = [];
|
|
485
|
+
const seenRoots = new Set();
|
|
486
|
+
const pushPlan = (label, root) => {
|
|
487
|
+
if (seenRoots.has(root))
|
|
488
|
+
return;
|
|
489
|
+
seenRoots.add(root);
|
|
490
|
+
plans.push({ label, root });
|
|
491
|
+
};
|
|
492
|
+
for (const plan of anchorPlans(options)) {
|
|
493
|
+
for (const root of nodeModulesRootsFor(plan.candidates))
|
|
494
|
+
pushPlan(plan.label, root);
|
|
495
|
+
}
|
|
496
|
+
for (const plan of plans) {
|
|
497
|
+
const direct = join(plan.root, CATALOG_PKG_REL, CATALOG_ENTRY_REL);
|
|
498
|
+
let entryFile = direct;
|
|
499
|
+
let label = plan.label;
|
|
500
|
+
let via;
|
|
501
|
+
if (!existsSafe(direct)) {
|
|
502
|
+
// 直接路径不存在:若候选落在 asar 容器里,走只读读取器取出闭包再从缓存目录 import(缺口⑤)
|
|
503
|
+
const split = splitAsarPath(plan.root);
|
|
504
|
+
if (split === undefined) {
|
|
505
|
+
note(plan.label, 'not-found');
|
|
506
|
+
continue;
|
|
507
|
+
}
|
|
508
|
+
const extracted = await extractCatalogFromAsar(split, options);
|
|
509
|
+
if (extracted === undefined) {
|
|
510
|
+
note(plan.label, 'asar-extract-failed');
|
|
511
|
+
continue;
|
|
512
|
+
}
|
|
513
|
+
entryFile = extracted;
|
|
514
|
+
label = plan.label + '+asar-extract';
|
|
515
|
+
via = 'asar-extract';
|
|
516
|
+
}
|
|
517
|
+
const mod = await importCatalogModule(entryFile);
|
|
518
|
+
if (mod === undefined) {
|
|
519
|
+
note(label, 'import-failed');
|
|
520
|
+
continue;
|
|
521
|
+
}
|
|
522
|
+
const picked = pickCandidate(mod, plan.root, children, headerVersion, installedVersion, label, via);
|
|
523
|
+
if (picked.ok)
|
|
524
|
+
return picked.resolution;
|
|
525
|
+
note(label, picked.code);
|
|
526
|
+
}
|
|
527
|
+
// 全部候选都不匹配时:只要任一候选卡在「静态 catalog 过不了 v3→v4 边界」,最终结论就是 children-required
|
|
528
|
+
if (notes.some((entry) => entry.endsWith(':children-required')))
|
|
529
|
+
return { detail: 'children-required' };
|
|
530
|
+
return { detail: notes.length > 0 ? notes.join('+') : 'no-candidate' };
|
|
531
|
+
}
|
|
532
|
+
/* ---------------------------------------------------------------- 字节 → header + 行 */
|
|
533
|
+
function errorText(error) {
|
|
534
|
+
if (error instanceof Error)
|
|
535
|
+
return error.message;
|
|
536
|
+
return String(error);
|
|
537
|
+
}
|
|
538
|
+
/** children-事实缺失类错误(catalog 实测文本:「V3 catalog migration requires explicit historical child facts…」)。 */
|
|
539
|
+
const CHILDREN_ERROR_RE = /(historical child|child facts|child evidence|childfacts|requires explicit)/i;
|
|
540
|
+
function isChildrenError(error) {
|
|
541
|
+
return CHILDREN_ERROR_RE.test(errorText(error));
|
|
542
|
+
}
|
|
543
|
+
/**
|
|
544
|
+
* 解帧 → 首帧**恰好一行** header(官方 parseHeaderRecord L971 / assertZstdHeaderFrame:非空且唯一的换行符
|
|
545
|
+
* 就是最后一个字节)→ 其余帧逐行 JSON.parse 成物理行对象。
|
|
546
|
+
*
|
|
547
|
+
* 归类:结构性不合法(帧扫描抛错 / 撕裂尾帧 / 首帧不是单行 header)→ invalid-header;
|
|
548
|
+
* 字节级解压失败或行不是 JSON 对象 → decode-failed。两者都是**确定性**结论(服务层据此自动回滚)。
|
|
549
|
+
*/
|
|
550
|
+
function extractFrames(bytes) {
|
|
551
|
+
if (!zstdAvailable())
|
|
552
|
+
return { ok: false, reason: 'unavailable', detail: 'no-zstd' };
|
|
553
|
+
let frames;
|
|
554
|
+
let torn = false;
|
|
555
|
+
try {
|
|
556
|
+
const scan = scanZstdFrames(bytes);
|
|
557
|
+
frames = scan.frames;
|
|
558
|
+
torn = scan.tornStart !== undefined;
|
|
559
|
+
}
|
|
560
|
+
catch {
|
|
561
|
+
return { ok: false, reason: 'invalid-header', detail: 'corrupt-container' };
|
|
562
|
+
}
|
|
563
|
+
if (frames.length === 0)
|
|
564
|
+
return { ok: false, reason: 'invalid-header', detail: 'no-frame' };
|
|
565
|
+
if (torn)
|
|
566
|
+
return { ok: false, reason: 'invalid-header', detail: 'torn-tail' };
|
|
567
|
+
const first = frames[0];
|
|
568
|
+
if (first === undefined)
|
|
569
|
+
return { ok: false, reason: 'invalid-header', detail: 'no-frame' };
|
|
570
|
+
let headerText;
|
|
571
|
+
try {
|
|
572
|
+
headerText = decodeZstdFrame(bytes.subarray(first.start, first.end)).toString('utf8');
|
|
573
|
+
}
|
|
574
|
+
catch (error) {
|
|
575
|
+
return { ok: false, reason: 'decode-failed', detail: 'header-frame: ' + errorText(error) };
|
|
576
|
+
}
|
|
577
|
+
if (headerText.length === 0 || headerText.indexOf(String.fromCharCode(10)) !== headerText.length - 1) {
|
|
578
|
+
return { ok: false, reason: 'invalid-header', detail: 'header-frame-not-one-line' };
|
|
579
|
+
}
|
|
580
|
+
let header;
|
|
581
|
+
try {
|
|
582
|
+
header = JSON.parse(headerText.slice(0, -1));
|
|
583
|
+
}
|
|
584
|
+
catch {
|
|
585
|
+
return { ok: false, reason: 'invalid-header', detail: 'header-not-json' };
|
|
586
|
+
}
|
|
587
|
+
if (header === null || typeof header !== 'object' || Array.isArray(header)) {
|
|
588
|
+
return { ok: false, reason: 'invalid-header', detail: 'header-not-object' };
|
|
589
|
+
}
|
|
590
|
+
const rows = [];
|
|
591
|
+
for (let i = 1; i < frames.length; i += 1) {
|
|
592
|
+
const frame = frames[i];
|
|
593
|
+
if (frame === undefined)
|
|
594
|
+
continue;
|
|
595
|
+
let text;
|
|
596
|
+
try {
|
|
597
|
+
text = decodeZstdFrame(bytes.subarray(frame.start, frame.end)).toString('utf8');
|
|
598
|
+
}
|
|
599
|
+
catch (error) {
|
|
600
|
+
return { ok: false, reason: 'decode-failed', detail: 'row-frame: ' + errorText(error) };
|
|
601
|
+
}
|
|
602
|
+
for (const line of text.split(String.fromCharCode(10))) {
|
|
603
|
+
if (line.trim() === '')
|
|
604
|
+
continue;
|
|
605
|
+
let row;
|
|
606
|
+
try {
|
|
607
|
+
row = JSON.parse(line);
|
|
608
|
+
}
|
|
609
|
+
catch {
|
|
610
|
+
return { ok: false, reason: 'decode-failed', detail: 'row-not-json' };
|
|
611
|
+
}
|
|
612
|
+
if (row === null || typeof row !== 'object' || Array.isArray(row)) {
|
|
613
|
+
return { ok: false, reason: 'decode-failed', detail: 'row-not-object' };
|
|
614
|
+
}
|
|
615
|
+
rows.push(row);
|
|
616
|
+
}
|
|
617
|
+
}
|
|
618
|
+
return { ok: true, header, rows };
|
|
619
|
+
}
|
|
620
|
+
/** 官方 finish() 产物里的 events 数组长度;产物形态不符(例如注入替身)时退回已解码行数。 */
|
|
621
|
+
function eventCountOf(artifact, fallback) {
|
|
622
|
+
if (typeof artifact === 'object' && artifact !== null) {
|
|
623
|
+
const events = artifact.events;
|
|
624
|
+
if (Array.isArray(events))
|
|
625
|
+
return events.length;
|
|
626
|
+
}
|
|
627
|
+
return fallback;
|
|
628
|
+
}
|
|
629
|
+
/**
|
|
630
|
+
* 走一遍官方读路径:createRestore → 逐行 decodeRow(**每行先 structuredClone**)→ finish()。
|
|
631
|
+
*
|
|
632
|
+
* 为什么每行都要 structuredClone:官方 restore 会**原地改写**入参(生态参考 mengjiemy/dsh-session-tools 的
|
|
633
|
+
* session-oracle.mjs 与 YouHui1/dsh-sessions-diagnosis 的 probeMigration 都这么做),而我们每次判定都要用
|
|
634
|
+
* 同一份行再跑一趟 strong 增强校验 —— 不隔离就会让增强校验吃到被上一趟改写过的行。
|
|
635
|
+
*/
|
|
636
|
+
function runRestore(catalog, header, rows, validation) {
|
|
637
|
+
let restore;
|
|
638
|
+
try {
|
|
639
|
+
restore = catalog.createRestore(structuredClone(header), { recovery: 'strict', validation });
|
|
640
|
+
}
|
|
641
|
+
catch (error) {
|
|
642
|
+
if (isChildrenError(error))
|
|
643
|
+
return { ok: false, reason: 'unavailable', detail: 'children-required' };
|
|
644
|
+
return { ok: false, reason: 'decode-failed', detail: 'createRestore: ' + errorText(error) };
|
|
645
|
+
}
|
|
646
|
+
try {
|
|
647
|
+
for (const row of rows)
|
|
648
|
+
restore.decodeRow(structuredClone(row));
|
|
649
|
+
}
|
|
650
|
+
catch (error) {
|
|
651
|
+
if (isChildrenError(error))
|
|
652
|
+
return { ok: false, reason: 'unavailable', detail: 'children-required' };
|
|
653
|
+
return { ok: false, reason: 'decode-failed', detail: 'decodeRow: ' + errorText(error) };
|
|
654
|
+
}
|
|
655
|
+
let artifact;
|
|
656
|
+
try {
|
|
657
|
+
artifact = restore.finish();
|
|
658
|
+
}
|
|
659
|
+
catch (error) {
|
|
660
|
+
if (isChildrenError(error))
|
|
661
|
+
return { ok: false, reason: 'unavailable', detail: 'children-required' };
|
|
662
|
+
return { ok: false, reason: 'finish-failed', detail: 'finish: ' + errorText(error) };
|
|
663
|
+
}
|
|
664
|
+
return { ok: true, events: eventCountOf(artifact, rows.length) };
|
|
665
|
+
}
|
|
666
|
+
/* ------------------------------------------------------------------------- 入口 */
|
|
667
|
+
/**
|
|
668
|
+
* 复验一份会话日志字节:**本机 DSH 的官方 codec 能不能把它读到底**。
|
|
669
|
+
*
|
|
670
|
+
* 判定口径固定 { recovery: 'strict', validation: 'transformed' }(= 官方现役读盘口径,选择 + 源码证据见文件头);
|
|
671
|
+
* validation: 'current' 只作为结果里的 strong / strongDetail 增强信息,**绝不影响判定**。
|
|
672
|
+
* 任何能力缺失(没有 zstd / 找不到可信 catalog / 代际对不上 / 缺子会话事实)→ unavailable:
|
|
673
|
+
* **绝不 throw,绝不宣称通过**。
|
|
674
|
+
*/
|
|
675
|
+
export async function verifySessionLogBytes(bytes, options = {}) {
|
|
676
|
+
const framed = extractFrames(bytes);
|
|
677
|
+
if (!framed.ok)
|
|
678
|
+
return { verified: false, reason: framed.reason, detail: framed.detail, equivalentToReadPath: false };
|
|
679
|
+
const headerVersion = framed.header.version;
|
|
680
|
+
let resolution;
|
|
681
|
+
try {
|
|
682
|
+
resolution = await resolveCatalog(options, headerVersion);
|
|
683
|
+
}
|
|
684
|
+
catch {
|
|
685
|
+
// 理论上到不了(resolveCatalog 内部全 try/catch);兜底仍判 unavailable,绝不 throw 给调用方
|
|
686
|
+
return { verified: false, reason: 'unavailable', detail: 'resolver-failed', equivalentToReadPath: false };
|
|
687
|
+
}
|
|
688
|
+
if (resolution.catalog === undefined)
|
|
689
|
+
return { verified: false, reason: 'unavailable', detail: resolution.detail, equivalentToReadPath: false };
|
|
690
|
+
// equivalentToReadPath:仅当 header.version === catalog.currentVersion === 已装 SESSION_FORMAT_VERSION
|
|
691
|
+
// (代际闸门已经保证后两者相等,所以这里等价于 header.version === currentVersion)
|
|
692
|
+
const equivalentToReadPath = typeof headerVersion === 'number'
|
|
693
|
+
&& resolution.currentVersion === headerVersion
|
|
694
|
+
&& (resolution.installedVersion === undefined || resolution.installedVersion === resolution.currentVersion);
|
|
695
|
+
const verdict = runRestore(resolution.catalog, framed.header, framed.rows, 'transformed');
|
|
696
|
+
if (!verdict.ok)
|
|
697
|
+
return { verified: false, reason: verdict.reason, detail: verdict.detail, equivalentToReadPath: false };
|
|
698
|
+
const via = resolution.via !== undefined ? { via: resolution.via } : {};
|
|
699
|
+
// 可选增强(不参与判定):跑完整安装侧校验,用来区分「能读且干净」与「能读但语义不完全干净」
|
|
700
|
+
const strong = runRestore(resolution.catalog, framed.header, framed.rows, 'current');
|
|
701
|
+
if (strong.ok)
|
|
702
|
+
return { verified: true, events: verdict.events, strong: true, equivalentToReadPath, ...via };
|
|
703
|
+
return { verified: true, events: verdict.events, strong: false, strongDetail: strong.detail, equivalentToReadPath, ...via };
|
|
704
|
+
}
|
|
705
|
+
//# sourceMappingURL=session-verify.js.map
|