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
|
@@ -22,7 +22,7 @@ import type { RecoveryPort } from '../../ui/types.ts'
|
|
|
22
22
|
import type { RecoveryPreview, RecoveryStatus, RecoveryVerifyResult } from '../../ui/types.ts'
|
|
23
23
|
import type { TranslateNS } from '../client-types.ts'
|
|
24
24
|
import { Badge, Banner, Button, Card, Empty, SectionTitle, Spinner } from '../common/ui.tsx'
|
|
25
|
-
import { DiskUsageCard } from '../
|
|
25
|
+
import { DiskUsageCard } from '../environment/maintenance/DiskUsageCard.tsx'
|
|
26
26
|
import { SkeletonList } from '../common/Skeleton.tsx'
|
|
27
27
|
import { ConfirmDialog } from '../common/ConfirmDialog.tsx'
|
|
28
28
|
import { Modal } from '../common/Modal.tsx'
|
|
@@ -36,15 +36,38 @@ import {
|
|
|
36
36
|
sessionHealthRows,
|
|
37
37
|
sessionHealthSummaryView,
|
|
38
38
|
sessionHealthTruncated,
|
|
39
|
+
sessionRepairBatchEntry,
|
|
39
40
|
sessionRepairCommands,
|
|
40
41
|
sessionRepairEntries,
|
|
41
42
|
sessionRepairReasonKey,
|
|
43
|
+
sessionRepairVerifyKey,
|
|
42
44
|
type SessionHealthResponse,
|
|
45
|
+
type SessionRepairBatchEntryView,
|
|
43
46
|
type SessionRepairEntryView,
|
|
44
47
|
type SessionRepairResult,
|
|
45
48
|
} from '../../ui/session-inventory-view.ts'
|
|
49
|
+
import {
|
|
50
|
+
sessionLayoutApplyItems,
|
|
51
|
+
sessionLayoutCandidates,
|
|
52
|
+
sessionLayoutKeepState,
|
|
53
|
+
sessionLayoutOverallReasonKey,
|
|
54
|
+
sessionLayoutPlanDisplayRows,
|
|
55
|
+
sessionLayoutPlanSkippedCount,
|
|
56
|
+
sessionLayoutPlanSubmittable,
|
|
57
|
+
sessionLayoutResultCounts,
|
|
58
|
+
sessionLayoutSectionState,
|
|
59
|
+
type SessionLayoutApplyView,
|
|
60
|
+
type SessionLayoutPlanView,
|
|
61
|
+
type SessionLayoutPort,
|
|
62
|
+
} from '../../ui/session-layout-view.ts'
|
|
63
|
+
import {
|
|
64
|
+
sessionExportEntryState,
|
|
65
|
+
sessionExportReasonText,
|
|
66
|
+
type SessionExportProbe,
|
|
67
|
+
} from '../session-export/session-export-view.ts'
|
|
46
68
|
import { runStore, type RecoveryStoreSlice } from '../run-store.ts'
|
|
47
69
|
import type { CrashReport, IncidentApi, RescueStatus } from './incident-api.ts'
|
|
70
|
+
import { IncompleteCopiesSection } from './IncompleteCopiesSection.tsx'
|
|
48
71
|
import {
|
|
49
72
|
isSnapshotTrusted, isVerdictAttention, isVerdictSuccess, toRecoveryPreviewView,
|
|
50
73
|
toRecoveryView,
|
|
@@ -53,12 +76,29 @@ import {
|
|
|
53
76
|
formatRecoveryTime,
|
|
54
77
|
rescueHintKey,
|
|
55
78
|
} from './recovery-view.ts'
|
|
79
|
+
import { redact } from '../../security/redaction.ts'
|
|
56
80
|
import css from '../config-manager.module.css'
|
|
57
81
|
|
|
82
|
+
/**
|
|
83
|
+
* 宿主/错误文本渲染前统一过 redact(AGENTS.md §UI 硬性规则 7;client-F4)。
|
|
84
|
+
* 单列成一行:plan-text-redaction.test.ts 的「按渲染点」登记表需要一个唯一锚点。
|
|
85
|
+
*/
|
|
86
|
+
const redactErrorText = (err: unknown): string => redact(err instanceof Error ? err.message : String(err))
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* E2:布局归位端口 = `RecoveryPort` 的**可选**扩展(面板只用 `layoutRepairSessions`)。
|
|
90
|
+
*
|
|
91
|
+
* 为什么是 Partial:`src/ui/types.ts` 的 RecoveryPort 契约文件不在本次改动范围内,而注入的实现
|
|
92
|
+
* (`src/client/recovery/recovery-api.ts` 的 `RecoveryApi`)一定带这个方法。可选形态让所有既有
|
|
93
|
+
* 调用点(按 RecoveryPort 传值)原样可编译;运行时若真缺失,面板走 `sessions.layout.apiMissing`
|
|
94
|
+
* 如实提示并且**一条也不执行**,绝不假装有能力。
|
|
95
|
+
*/
|
|
96
|
+
type LayoutRecoveryApi = RecoveryPort & Partial<SessionLayoutPort>
|
|
97
|
+
|
|
58
98
|
export interface RecoveryPanelProps {
|
|
59
99
|
/** 事故处置(崩溃归因 + 救援模式)API;与 recoveryApi 同属「事故恢复」子 tab */
|
|
60
100
|
incidentApi: IncidentApi
|
|
61
|
-
recoveryApi:
|
|
101
|
+
recoveryApi: LayoutRecoveryApi
|
|
62
102
|
t: TranslateNS<'config-manager-recovery'>
|
|
63
103
|
/**
|
|
64
104
|
* **主字典**(config-manager)翻译器 —— 仅供 CopyButton 这类「共用原语」使用:
|
|
@@ -216,7 +256,7 @@ function incidentStateLabel(t: TranslateNS<'config-manager-recovery'>, state: st
|
|
|
216
256
|
*/
|
|
217
257
|
const EMPTY_COPY_T: TranslateNS<'config-manager'> = ((key: string) => key) as TranslateNS<'config-manager'>
|
|
218
258
|
|
|
219
|
-
function SessionHealthCard({ recoveryApi, t, copyT }: Pick<RecoveryPanelProps, 'recoveryApi' | 't' | 'copyT'>) {
|
|
259
|
+
function SessionHealthCard({ recoveryApi, t, copyT, diskApi }: Pick<RecoveryPanelProps, 'recoveryApi' | 't' | 'copyT' | 'diskApi'>) {
|
|
220
260
|
/** 剪贴板反馈的翻译器(config-manager 命名空间;缺省退到 zhUiT 兜底)。 */
|
|
221
261
|
const clipboardT = copyT ?? EMPTY_COPY_T
|
|
222
262
|
/** 体检结果住**卡片**里:弹窗每次打开重新扫一遍(scanVersion 变化即重扫),关掉不留一屏陈旧数据。 */
|
|
@@ -226,6 +266,25 @@ function SessionHealthCard({ recoveryApi, t, copyT }: Pick<RecoveryPanelProps, '
|
|
|
226
266
|
const [open, setOpen] = useState(false)
|
|
227
267
|
const [scanVersion, setScanVersion] = useState(0)
|
|
228
268
|
const scan = (): void => { setScanVersion((v) => v + 1) }
|
|
269
|
+
/**
|
|
270
|
+
* F-2:官方 session.export 端的可用性探测(一次;打开弹窗时顺带探)。
|
|
271
|
+
*
|
|
272
|
+
* 为什么**只在打开弹窗时**探:探测要走一次宿主自请求,用户没进这个界面时毫无意义;
|
|
273
|
+
* 而进程内缓存(setState 在卡片上)保证同一次对话里只探一次 —— 后端能力不会中途变化。
|
|
274
|
+
* 探测失败(网络/宿主异常)**不写 error**:它是「下载入口的可用性未知」,不是体检失败。
|
|
275
|
+
*/
|
|
276
|
+
const [zipProbe, setZipProbe] = useState<SessionExportProbe | null>(null)
|
|
277
|
+
const zipChecked = useRef(false)
|
|
278
|
+
useEffect(() => {
|
|
279
|
+
if (!open || zipChecked.current) return
|
|
280
|
+
zipChecked.current = true
|
|
281
|
+
let alive = true
|
|
282
|
+
diskApi.sessionExportProbe()
|
|
283
|
+
.then((probe) => { if (alive) setZipProbe(probe) })
|
|
284
|
+
// 探测本身失败(宿主不可达等)→ 落到保守三态:**绝不**改判成不可用而把入口藏掉。
|
|
285
|
+
.catch(() => { if (alive) setZipProbe({ availability: 'unknown', reason: 'network-error', status: 0, path: '', checkedAt: new Date().toISOString() }) })
|
|
286
|
+
return () => { alive = false }
|
|
287
|
+
}, [open, diskApi])
|
|
229
288
|
|
|
230
289
|
useEffect(() => {
|
|
231
290
|
if (!open) return
|
|
@@ -279,6 +338,8 @@ function SessionHealthCard({ recoveryApi, t, copyT }: Pick<RecoveryPanelProps, '
|
|
|
279
338
|
loading={loading}
|
|
280
339
|
error={error}
|
|
281
340
|
onScan={scan}
|
|
341
|
+
zipProbe={zipProbe}
|
|
342
|
+
api={diskApi}
|
|
282
343
|
/>
|
|
283
344
|
)}
|
|
284
345
|
</Card>
|
|
@@ -292,35 +353,261 @@ function SessionHealthCard({ recoveryApi, t, copyT }: Pick<RecoveryPanelProps, '
|
|
|
292
353
|
* 交互约定:打开即扫(scanVersion 变化重扫);修复一律先 dry-run 预览(零写入),有损计划必须再点
|
|
293
354
|
* 「截断并修复」才执行;回滚走 ConfirmDialog(danger)。
|
|
294
355
|
*/
|
|
295
|
-
/** 批量修复里的一条结果(成功或被拒;原因是机器可读码,界面映射成文案)。 */
|
|
296
|
-
interface BatchRepairResult {
|
|
297
|
-
unitId: string
|
|
298
|
-
sessionId: string
|
|
299
|
-
ok: boolean
|
|
300
|
-
/** 被拒原因(机器可读;未知一律走 unknown 文案,绝不渲染裸枚举) */
|
|
301
|
-
reason?: string
|
|
302
|
-
/** 被拒是「需要逐个确认的有损修复」 */
|
|
303
|
-
lossy?: boolean
|
|
304
|
-
/** 成功时丢弃的行数 */
|
|
305
|
-
droppedRows?: number
|
|
306
|
-
/** 传输层失败(HTTP/网络)时的原文(已由 ErrorBanner/http 层脱敏) */
|
|
307
|
-
transportError?: string
|
|
308
|
-
}
|
|
309
|
-
|
|
310
356
|
interface SessionHealthDialogProps {
|
|
311
357
|
open: boolean
|
|
312
358
|
onClose: () => void
|
|
313
|
-
recoveryApi:
|
|
359
|
+
recoveryApi: LayoutRecoveryApi
|
|
314
360
|
t: TranslateNS<'config-manager-recovery'>
|
|
315
361
|
clipboardT: TranslateNS<'config-manager'>
|
|
316
362
|
response: SessionHealthResponse | null
|
|
317
363
|
loading: boolean
|
|
318
364
|
error: string | null
|
|
319
365
|
onScan: () => void
|
|
366
|
+
/** F-2:官方 session.export 的可用性(null = 还没探出来 → 入口显示加载态,不误判禁用)。 */
|
|
367
|
+
zipProbe: SessionExportProbe | null
|
|
368
|
+
/** 供「下载原始日志 (ZIP)」拼下载地址(同一份 api 实例,别新起一个)。 */
|
|
369
|
+
api: import('../api.ts').ConfigManagerApi
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
/**
|
|
373
|
+
* E2:面板「会话布局归位 / 重复 id 隔离」写入口(宿主 E1:`POST /recovery/sessions/layout`)。
|
|
374
|
+
*
|
|
375
|
+
* 与字节级修复(/repair)的分工:那条改日志**字节**,这条改**位置** —— 把会话目录搬回首帧 cwd
|
|
376
|
+
* 对应的 projectKey 段,并把重复 id 的其它副本移进隔离目录(可人工找回)。
|
|
377
|
+
*
|
|
378
|
+
* 安全口径(键 `sessions.layout.runtimeNote`,必须让用户看见):本入口在 **DSH 运行时**执行,
|
|
379
|
+
* 与离线 CLI `dcm sessions repair --fix` 的「须先停 DSH」不同 —— 靠门前置(SAFE MODE / mutation lock /
|
|
380
|
+
* 逐目标无 session.lock + 不在静止期)+ 索引刷新必须成功 + 失败逐条回滚兜底;宿主没给索引刷新端口时
|
|
381
|
+
* **一条也不执行**(`reindex-unavailable`,绝不显示成成功)。
|
|
382
|
+
*
|
|
383
|
+
* 硬约束:**本组件不做任何业务判断** —— 候选筛选 / keep 完整性 / 可否提交 / 逐条状态与计数全在
|
|
384
|
+
* `ui/session-layout-view.ts`(含「未执行 / 已回滚不得算成功」的判定),组件只装配与渲染。
|
|
385
|
+
*/
|
|
386
|
+
interface SessionLayoutSectionProps {
|
|
387
|
+
recoveryApi: LayoutRecoveryApi
|
|
388
|
+
t: TranslateNS<'config-manager-recovery'>
|
|
389
|
+
response: SessionHealthResponse | null
|
|
390
|
+
/** 执行后重扫列表(结果已落盘,列表必须跟着刷新) */
|
|
391
|
+
onScan: () => void
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
function SessionLayoutSection({ recoveryApi, t, response, onScan }: SessionLayoutSectionProps) {
|
|
395
|
+
const [plan, setPlan] = useState<SessionLayoutPlanView | null>(null)
|
|
396
|
+
const [applied, setApplied] = useState<SessionLayoutApplyView | null>(null)
|
|
397
|
+
/** sessionId → 用户选定保留的副本 unitId(提交判定在 sessionLayoutKeepState) */
|
|
398
|
+
const [keep, setKeep] = useState<Record<string, string>>({})
|
|
399
|
+
const [busy, setBusy] = useState<'preview' | 'apply' | null>(null)
|
|
400
|
+
/** 客户端/传输层失败(宿主的拒绝走结果里的机器码,不进这里) */
|
|
401
|
+
const [failure, setFailure] = useState<string | null>(null)
|
|
402
|
+
|
|
403
|
+
const candidates = sessionLayoutCandidates(response)
|
|
404
|
+
const keepState = sessionLayoutKeepState(plan, keep)
|
|
405
|
+
const submittable = sessionLayoutPlanSubmittable(plan, keepState)
|
|
406
|
+
const planRows = sessionLayoutPlanDisplayRows(plan)
|
|
407
|
+
const skippedCount = sessionLayoutPlanSkippedCount(plan)
|
|
408
|
+
const planReasonKey = sessionLayoutOverallReasonKey(plan?.reason)
|
|
409
|
+
const appliedReasonKey = sessionLayoutOverallReasonKey(applied?.reason)
|
|
410
|
+
const appliedItems = sessionLayoutApplyItems(applied)
|
|
411
|
+
const counts = sessionLayoutResultCounts(appliedItems)
|
|
412
|
+
|
|
413
|
+
const preview = (): void => {
|
|
414
|
+
if (busy !== null) return
|
|
415
|
+
setBusy('preview')
|
|
416
|
+
setFailure(null)
|
|
417
|
+
const request = recoveryApi.layoutRepairSessions?.(false)
|
|
418
|
+
if (request === undefined) {
|
|
419
|
+
setFailure(t('sessions.layout.apiMissing'))
|
|
420
|
+
setBusy(null)
|
|
421
|
+
return
|
|
422
|
+
}
|
|
423
|
+
request
|
|
424
|
+
.then((next) => {
|
|
425
|
+
if ('results' in next) { setApplied(next); return }
|
|
426
|
+
setPlan(next)
|
|
427
|
+
setKeep({})
|
|
428
|
+
setApplied(null)
|
|
429
|
+
})
|
|
430
|
+
.catch((err) => { setFailure(redactErrorText(err)) })
|
|
431
|
+
.finally(() => { setBusy(null) })
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
const apply = (): void => {
|
|
435
|
+
if (busy !== null || !submittable) return
|
|
436
|
+
setBusy('apply')
|
|
437
|
+
setFailure(null)
|
|
438
|
+
const request = recoveryApi.layoutRepairSessions?.(true, keep)
|
|
439
|
+
if (request === undefined) {
|
|
440
|
+
setFailure(t('sessions.layout.apiMissing'))
|
|
441
|
+
setBusy(null)
|
|
442
|
+
return
|
|
443
|
+
}
|
|
444
|
+
request
|
|
445
|
+
.then((next) => {
|
|
446
|
+
if ('results' in next) {
|
|
447
|
+
setApplied(next)
|
|
448
|
+
// 结果已落盘(含逐条回滚):重扫列表是唯一权威,结果视图保留到用户关闭
|
|
449
|
+
onScan()
|
|
450
|
+
return
|
|
451
|
+
}
|
|
452
|
+
setPlan(next)
|
|
453
|
+
setKeep({})
|
|
454
|
+
setApplied(null)
|
|
455
|
+
})
|
|
456
|
+
.catch((err) => { setFailure(redactErrorText(err)) })
|
|
457
|
+
.finally(() => { setBusy(null) })
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
// 扫过之后入口**始终可见**(否则本机没有这一档的用户根本找不到它);只有「还没扫过且手上没计划/结果」才隐藏。
|
|
461
|
+
const sectionState = sessionLayoutSectionState({ response, plan, applied })
|
|
462
|
+
if (sectionState === 'hidden') return null
|
|
463
|
+
|
|
464
|
+
return (
|
|
465
|
+
<div className={css.card}>
|
|
466
|
+
<div className={css.groupLabel}>{t('sessions.layout.title')}</div>
|
|
467
|
+
<div className={css.hint}>{t('sessions.layout.desc')}</div>
|
|
468
|
+
{/* 口径:本入口跑在 DSH 运行时 —— 与离线 CLI 的「须先停 DSH」不同,必须让用户看见 */}
|
|
469
|
+
<div className={css.hint}>{t('sessions.layout.runtimeNote')}</div>
|
|
470
|
+
<div className={css.hint}>{t('sessions.layout.quarantineNote')}</div>
|
|
471
|
+
{sectionState === 'empty' && <div className={css.hint}>{t('sessions.layout.noCandidates')}</div>}
|
|
472
|
+
{candidates.length > 0 && (
|
|
473
|
+
<div className={css.snapshotList + ' ' + css.reportScroll}>
|
|
474
|
+
{candidates.map((candidate) => (
|
|
475
|
+
<div key={candidate.unitId} className={css.snapshotRow}>
|
|
476
|
+
<span className={css.snapshotRowMain} title={candidate.unitId}>{candidate.sessionId}</span>
|
|
477
|
+
<span className={css.snapshotRowFacts}>
|
|
478
|
+
{candidate.issueCodes.join(' · ')} · {candidate.projectKey}
|
|
479
|
+
</span>
|
|
480
|
+
</div>
|
|
481
|
+
))}
|
|
482
|
+
</div>
|
|
483
|
+
)}
|
|
484
|
+
<div className={css.actionRow}>
|
|
485
|
+
<Button variant="ghost" loading={busy === 'preview'} disabled={busy !== null} onClick={preview}>
|
|
486
|
+
{busy === 'preview' ? t('sessions.layout.previewing') : t('sessions.layout.preview')}
|
|
487
|
+
</Button>
|
|
488
|
+
</div>
|
|
489
|
+
{failure !== null && <Banner kind="error">{failure}</Banner>}
|
|
490
|
+
{applied === null && plan !== null && (
|
|
491
|
+
<>
|
|
492
|
+
<div className={css.groupLabel}>{t('sessions.layout.planTitle')}</div>
|
|
493
|
+
{/* 计划只读:确认前零写入 */}
|
|
494
|
+
<div className={css.hint}>{t('sessions.layout.planReadOnly')}</div>
|
|
495
|
+
{planReasonKey !== null && <Banner kind="warn">{t(planReasonKey)}</Banner>}
|
|
496
|
+
<div className={css.hint}>
|
|
497
|
+
{t('sessions.layout.summary', { move: String(plan.summary.move), quarantine: String(plan.summary.quarantine) })}
|
|
498
|
+
</div>
|
|
499
|
+
{planRows.length === 0 && <div className={css.hint}>{t('sessions.layout.planEmpty')}</div>}
|
|
500
|
+
{planRows.length > 0 && (
|
|
501
|
+
<div className={css.snapshotList + ' ' + css.reportScroll}>
|
|
502
|
+
{planRows.map((row) => (
|
|
503
|
+
<div key={row.unitId} className={css.snapshotRow}>
|
|
504
|
+
<span className={css.snapshotRowBadges}>
|
|
505
|
+
<Badge kind="info">{t(row.kindKey)}</Badge>
|
|
506
|
+
{row.kind === 'skip' && <Badge kind="warn">{t('sessions.layout.onlyReported')}</Badge>}
|
|
507
|
+
</span>
|
|
508
|
+
<span className={css.snapshotRowIssues} title={row.unitId}>{row.sessionId}</span>
|
|
509
|
+
<span className={css.snapshotRowFacts}>
|
|
510
|
+
{row.toProjectKey !== null ? row.fromProjectKey + ' → ' + row.toProjectKey : row.fromProjectKey}
|
|
511
|
+
{' · '}
|
|
512
|
+
{t(row.reasonKey)}
|
|
513
|
+
</span>
|
|
514
|
+
</div>
|
|
515
|
+
))}
|
|
516
|
+
</div>
|
|
517
|
+
)}
|
|
518
|
+
{skippedCount > 0 && (
|
|
519
|
+
<div className={css.hint}>{t('sessions.layout.planSkipped', { count: String(skippedCount) })}</div>
|
|
520
|
+
)}
|
|
521
|
+
{/* 重复 id:必须选定保留哪一份才可提交(判定在 ui 纯函数层) */}
|
|
522
|
+
{plan.needsKeep.length > 0 && (
|
|
523
|
+
<>
|
|
524
|
+
<div className={css.groupLabel}>{t('sessions.layout.keepTitle')}</div>
|
|
525
|
+
<div className={css.hint}>{t('sessions.layout.keepHint')}</div>
|
|
526
|
+
{plan.needsKeep.map((sessionId) => (
|
|
527
|
+
<div key={sessionId} className={css.conflictChoices} role="radiogroup" aria-label={sessionId}>
|
|
528
|
+
{(keepState.candidates[sessionId] ?? []).map((unitId) => {
|
|
529
|
+
const selected = keep[sessionId] === unitId
|
|
530
|
+
return (
|
|
531
|
+
<label key={unitId} className={css.choiceCard} data-selected={selected ? '' : undefined}>
|
|
532
|
+
<input
|
|
533
|
+
type="radio"
|
|
534
|
+
name={'layout-keep-' + sessionId}
|
|
535
|
+
checked={selected}
|
|
536
|
+
onChange={() => { setKeep((prev) => ({ ...prev, [sessionId]: unitId })) }}
|
|
537
|
+
/>
|
|
538
|
+
<span className={css.choiceTitle}>{unitId}</span>
|
|
539
|
+
</label>
|
|
540
|
+
)
|
|
541
|
+
})}
|
|
542
|
+
</div>
|
|
543
|
+
))}
|
|
544
|
+
{!keepState.complete && (
|
|
545
|
+
<div className={css.hint}>{t('sessions.layout.keepMissing', { count: String(keepState.missing.length) })}</div>
|
|
546
|
+
)}
|
|
547
|
+
</>
|
|
548
|
+
)}
|
|
549
|
+
<div className={css.actionRow}>
|
|
550
|
+
{/* 未选定 keep / 计划不可用 ⇒ 不可提交(判定在 ui 纯函数层) */}
|
|
551
|
+
<Button variant="primary" loading={busy === 'apply'} disabled={busy !== null || !submittable} onClick={apply}>
|
|
552
|
+
{busy === 'apply' ? t('sessions.layout.applying') : t('sessions.layout.apply')}
|
|
553
|
+
</Button>
|
|
554
|
+
</div>
|
|
555
|
+
</>
|
|
556
|
+
)}
|
|
557
|
+
{applied !== null && (
|
|
558
|
+
<>
|
|
559
|
+
<div className={css.groupLabel}>{t('sessions.layout.resultTitle')}</div>
|
|
560
|
+
{/* 整体未执行(如 reindex-unavailable)**不是**成功:单独说明,且下面不会有任何成功条目 */}
|
|
561
|
+
{appliedReasonKey !== null && <Banner kind="warn">{t(appliedReasonKey)}</Banner>}
|
|
562
|
+
{appliedItems.length > 0 && (
|
|
563
|
+
<div className={css.hint}>
|
|
564
|
+
{t('sessions.layout.done', {
|
|
565
|
+
moved: String(counts.moved),
|
|
566
|
+
quarantined: String(counts.quarantined),
|
|
567
|
+
failed: String(counts.failedTotal),
|
|
568
|
+
skipped: String(counts.skipped),
|
|
569
|
+
})}
|
|
570
|
+
</div>
|
|
571
|
+
)}
|
|
572
|
+
{appliedItems.length > 0 && (
|
|
573
|
+
<div className={css.snapshotList + ' ' + css.reportScroll}>
|
|
574
|
+
{appliedItems.map((item, index) => (
|
|
575
|
+
<div key={item.unitId + '#' + String(index)} className={css.snapshotRow}>
|
|
576
|
+
<span className={css.snapshotRowBadges}>
|
|
577
|
+
<Badge kind={item.badgeKind}>{t(item.statusKey)}</Badge>
|
|
578
|
+
</span>
|
|
579
|
+
<span className={css.snapshotRowIssues} title={item.unitId}>{item.sessionId}</span>
|
|
580
|
+
<span className={css.snapshotRowFacts}>
|
|
581
|
+
{item.reasonKey !== null ? t(item.reasonKey) : ''}
|
|
582
|
+
{item.movedUnitId !== null ? ' → ' + item.movedUnitId : ''}
|
|
583
|
+
{item.quarantineDir !== null ? ' → ' + item.quarantineDir : ''}
|
|
584
|
+
</span>
|
|
585
|
+
</div>
|
|
586
|
+
))}
|
|
587
|
+
</div>
|
|
588
|
+
)}
|
|
589
|
+
<div className={css.hint}>{t('sessions.layout.refreshHint')}</div>
|
|
590
|
+
<div className={css.actionRow}>
|
|
591
|
+
<Button variant="ghost" onClick={() => { setApplied(null); setPlan(null); setKeep({}) }}>
|
|
592
|
+
{t('sessions.layout.close')}
|
|
593
|
+
</Button>
|
|
594
|
+
</div>
|
|
595
|
+
</>
|
|
596
|
+
)}
|
|
597
|
+
</div>
|
|
598
|
+
)
|
|
320
599
|
}
|
|
321
600
|
|
|
322
601
|
function SessionHealthDialog(props: SessionHealthDialogProps) {
|
|
323
|
-
const { open, onClose, recoveryApi, t, clipboardT, response, loading, error, onScan } = props
|
|
602
|
+
const { open, onClose, recoveryApi, t, clipboardT, response, loading, error, onScan, zipProbe, api } = props
|
|
603
|
+
/**
|
|
604
|
+
* F-2:「下载原始日志 (ZIP)」入口的三态判定(**唯一**判定点在 ui/ 纯函数里,组件只映射)。
|
|
605
|
+
*
|
|
606
|
+
* 为什么不在组件里判 `status !== 501`(竞品做法):那条判据在本机 DSH 上**已过时** ——
|
|
607
|
+
* 服务缺失返的是 500 而不是 501,照抄会让入口几乎恒显示。判定与理由见 session-export-view.ts。
|
|
608
|
+
*/
|
|
609
|
+
const zipState = sessionExportEntryState(zipProbe)
|
|
610
|
+
const zipHint = sessionExportReasonText(zipState, t)
|
|
324
611
|
const phase = sessionHealthPhase({ loading, error, response })
|
|
325
612
|
/**
|
|
326
613
|
* 本地的「已修复」行集合:批量/单条修复成功后**就地更新界面**,不再整屏重扫。
|
|
@@ -341,8 +628,38 @@ function SessionHealthDialog(props: SessionHealthDialogProps) {
|
|
|
341
628
|
const repairEntries = sessionRepairEntries(response)
|
|
342
629
|
/** 正在预览 / 应用 / 回滚的 unitId(进行中态住组件内;弹窗不跨页签) */
|
|
343
630
|
const [busy, setBusy] = useState<string | null>(null)
|
|
631
|
+
/**
|
|
632
|
+
* F-2:正在下载原始日志的会话 id(进行中态住组件内 —— 弹窗不跨页签,无需进 runStore)。
|
|
633
|
+
*
|
|
634
|
+
* 为什么必须有:ZIP 可达数百 MB,点下去到浏览器开始落盘之间可能好几秒,
|
|
635
|
+
* 没有任何反馈会被当成「点了没反应」而反复点击。
|
|
636
|
+
*/
|
|
637
|
+
const [zipBusy, setZipBusy] = useState<string | null>(null)
|
|
638
|
+
/** 触发一次 ZIP 下载;**失败必须显式报告**(绝不静默)。 */
|
|
639
|
+
const downloadZip = (sessionId: string): void => {
|
|
640
|
+
if (zipBusy !== null) return
|
|
641
|
+
setZipBusy(sessionId)
|
|
642
|
+
api.downloadSessionExport(sessionId)
|
|
643
|
+
.then((result) => { toast.ok(t('sessions.zip.done', { name: result.filename })) })
|
|
644
|
+
// 错误文本走 toast(与本节其它动作同一口径;http 层已脱敏)。
|
|
645
|
+
.catch((err) => { toast.error(t('sessions.zip.failed') + ': ' + redactErrorText(err)) })
|
|
646
|
+
.finally(() => { setZipBusy(null) })
|
|
647
|
+
}
|
|
344
648
|
/** 预览得到的修复计划(应用前必须显式确认;ok=false 时也在这里显示拒绝原因) */
|
|
345
649
|
const [plan, setPlan] = useState<{ unitId: string; result: SessionRepairResult } | null>(null)
|
|
650
|
+
/**
|
|
651
|
+
* 应用成功后的结果(含写后真 codec 复验结论)。
|
|
652
|
+
*
|
|
653
|
+
* 为什么必须有:修复「写完了」与「DSH 现在能读它」是两件事 —— 没有复验结论时(如 unavailable)
|
|
654
|
+
* 把这次修复当成普通成功呈现,正是本轮要消灭的谎报。行已从列表移出,所以结果留在这里。
|
|
655
|
+
*/
|
|
656
|
+
const [applied, setApplied] = useState<{ unitId: string; result: SessionRepairResult } | null>(null)
|
|
657
|
+
/**
|
|
658
|
+
* 复验三态 / 危险态的字典键(**判定全部在 ui 纯函数层**,组件只装配 —— 见 sessionRepairVerifyKey)。
|
|
659
|
+
* null = 没有复验结论(预览路径)⇒ 不渲染这一行,绝不臆造「已验证」。
|
|
660
|
+
*/
|
|
661
|
+
const planVerifyKey = plan !== null ? sessionRepairVerifyKey(plan.result.verify, plan.result.rolledBack) : null
|
|
662
|
+
const appliedVerifyKey = applied !== null ? sessionRepairVerifyKey(applied.result.verify, applied.result.rolledBack) : null
|
|
346
663
|
/** 待确认的回滚目标(ConfirmDialog,danger) */
|
|
347
664
|
const [rollbackTarget, setRollbackTarget] = useState<SessionRepairEntryView | null>(null)
|
|
348
665
|
/**
|
|
@@ -352,14 +669,15 @@ function SessionHealthDialog(props: SessionHealthDialogProps) {
|
|
|
352
669
|
* 「哪几条成了、哪几条为什么没成」,绝不只给一个计数。
|
|
353
670
|
*/
|
|
354
671
|
const [batchBusy, setBatchBusy] = useState(false)
|
|
355
|
-
const [batch, setBatch] = useState<{ ok: number; failed: number; results:
|
|
672
|
+
const [batch, setBatch] = useState<{ ok: number; failed: number; results: SessionRepairBatchEntryView[] } | null>(null)
|
|
356
673
|
|
|
357
674
|
const startRepair = (unitId: string): void => {
|
|
358
675
|
setBusy(unitId)
|
|
359
676
|
setPlan(null)
|
|
677
|
+
setApplied(null)
|
|
360
678
|
recoveryApi.repairSession(unitId, false)
|
|
361
679
|
.then((result) => { setPlan({ unitId, result }); setBusy(null) })
|
|
362
|
-
.catch((err) => { toast.error(
|
|
680
|
+
.catch((err) => { toast.error(redactErrorText(err)); setBusy(null) })
|
|
363
681
|
}
|
|
364
682
|
|
|
365
683
|
/**
|
|
@@ -376,7 +694,7 @@ function SessionHealthDialog(props: SessionHealthDialogProps) {
|
|
|
376
694
|
setBatch(null)
|
|
377
695
|
let ok = 0
|
|
378
696
|
let failed = 0
|
|
379
|
-
const results:
|
|
697
|
+
const results: SessionRepairBatchEntryView[] = []
|
|
380
698
|
const done: string[] = []
|
|
381
699
|
for (const unitId of targets) {
|
|
382
700
|
setBusy(unitId)
|
|
@@ -384,27 +702,53 @@ function SessionHealthDialog(props: SessionHealthDialogProps) {
|
|
|
384
702
|
const plan = await recoveryApi.repairSession(unitId, false)
|
|
385
703
|
if (!plan.ok) {
|
|
386
704
|
failed += 1
|
|
387
|
-
results.push({
|
|
705
|
+
results.push(sessionRepairBatchEntry({
|
|
706
|
+
unitId,
|
|
707
|
+
sessionId: plan.sessionId ?? unitId,
|
|
708
|
+
ok: false,
|
|
709
|
+
...(plan.reason !== undefined ? { reason: plan.reason } : {}),
|
|
710
|
+
}))
|
|
388
711
|
continue
|
|
389
712
|
}
|
|
390
713
|
if (plan.lossy === true) {
|
|
391
714
|
// 有损(截断)绝不批量代做 —— 逐条确认是硬约束,这里如实说明「需要你逐条确认」
|
|
392
715
|
failed += 1
|
|
393
|
-
results.push({ unitId, sessionId: plan.sessionId ?? unitId, ok: false, reason: 'lossy-required', lossy: true })
|
|
716
|
+
results.push(sessionRepairBatchEntry({ unitId, sessionId: plan.sessionId ?? unitId, ok: false, reason: 'lossy-required', lossy: true }))
|
|
394
717
|
continue
|
|
395
718
|
}
|
|
396
719
|
const applied = await recoveryApi.repairSession(unitId, true, plan.expect)
|
|
397
720
|
if (applied.ok) {
|
|
398
721
|
ok += 1
|
|
399
722
|
done.push(unitId)
|
|
400
|
-
results.push({
|
|
723
|
+
results.push(sessionRepairBatchEntry({
|
|
724
|
+
unitId,
|
|
725
|
+
sessionId: applied.sessionId ?? unitId,
|
|
726
|
+
ok: true,
|
|
727
|
+
droppedRows: applied.droppedRows ?? 0,
|
|
728
|
+
...(applied.verify !== undefined ? { verify: applied.verify } : {}),
|
|
729
|
+
...(applied.rolledBack !== undefined ? { rolledBack: applied.rolledBack } : {}),
|
|
730
|
+
}))
|
|
401
731
|
} else {
|
|
402
732
|
failed += 1
|
|
403
|
-
|
|
733
|
+
// A2-F1:失败分支与成功分支**同源**搬运 —— verify-failed 必带 verify,
|
|
734
|
+
// rolledBack===false(自动回滚未成功)是危险态,必须在批量视图里可见。
|
|
735
|
+
results.push(sessionRepairBatchEntry({
|
|
736
|
+
unitId,
|
|
737
|
+
sessionId: applied.sessionId ?? unitId,
|
|
738
|
+
ok: false,
|
|
739
|
+
...(applied.reason !== undefined ? { reason: applied.reason } : {}),
|
|
740
|
+
...(applied.verify !== undefined ? { verify: applied.verify } : {}),
|
|
741
|
+
...(applied.rolledBack !== undefined ? { rolledBack: applied.rolledBack } : {}),
|
|
742
|
+
}))
|
|
404
743
|
}
|
|
405
744
|
} catch (err) {
|
|
406
745
|
failed += 1
|
|
407
|
-
results.push({
|
|
746
|
+
results.push(sessionRepairBatchEntry({
|
|
747
|
+
unitId,
|
|
748
|
+
sessionId: unitId,
|
|
749
|
+
ok: false,
|
|
750
|
+
transportError: err instanceof Error ? err.message : String(err),
|
|
751
|
+
}))
|
|
408
752
|
}
|
|
409
753
|
}
|
|
410
754
|
setBusy(null)
|
|
@@ -425,6 +769,8 @@ function SessionHealthDialog(props: SessionHealthDialogProps) {
|
|
|
425
769
|
if (next.ok) {
|
|
426
770
|
toast.ok(t('sessions.repair.applied', { rows: String(next.droppedRows ?? 0) }))
|
|
427
771
|
setPlan(null)
|
|
772
|
+
// 结果(含复验三态)留在面板上:绝不把「写完了」说成「已可加载」
|
|
773
|
+
setApplied({ unitId, result: next })
|
|
428
774
|
setRepairedUnits((prev) => new Set([...prev, unitId]))
|
|
429
775
|
} else {
|
|
430
776
|
// 应用期被拒(指纹变化 / 校验不过 / 写失败):**就地给出真实原因**,不假装成功
|
|
@@ -432,7 +778,7 @@ function SessionHealthDialog(props: SessionHealthDialogProps) {
|
|
|
432
778
|
}
|
|
433
779
|
setBusy(null)
|
|
434
780
|
})
|
|
435
|
-
.catch((err) => { toast.error(
|
|
781
|
+
.catch((err) => { toast.error(redactErrorText(err)); setBusy(null) })
|
|
436
782
|
}
|
|
437
783
|
|
|
438
784
|
const doRollback = async (): Promise<void> => {
|
|
@@ -450,7 +796,7 @@ function SessionHealthDialog(props: SessionHealthDialogProps) {
|
|
|
450
796
|
setRollbackTarget(null)
|
|
451
797
|
}
|
|
452
798
|
} catch (err) {
|
|
453
|
-
toast.error(
|
|
799
|
+
toast.error(redactErrorText(err))
|
|
454
800
|
setRollbackTarget(null)
|
|
455
801
|
} finally {
|
|
456
802
|
setBusy(null)
|
|
@@ -468,6 +814,15 @@ function SessionHealthDialog(props: SessionHealthDialogProps) {
|
|
|
468
814
|
</Button>
|
|
469
815
|
</div>
|
|
470
816
|
|
|
817
|
+
{/*
|
|
818
|
+
F-2:官方 session.export 通道的**可用性说明**(逐行入口见下方列表)。
|
|
819
|
+
ZIP 由 DSH 自己打包(含子会话与附件);体检/修复动的是本机文件,两者不是一回事。
|
|
820
|
+
不可用时界面给出**可读原因**,绝不静默消失(消失会让用户以为插件根本没做这个功能)。
|
|
821
|
+
*/}
|
|
822
|
+
{zipHint !== null && (
|
|
823
|
+
<Banner kind="warn">{zipHint}</Banner>
|
|
824
|
+
)}
|
|
825
|
+
|
|
471
826
|
{phase === 'error' && <Banner kind="error">{error ?? t('common.unknownError')}</Banner>}
|
|
472
827
|
|
|
473
828
|
{phase === 'ready' && response !== null && (
|
|
@@ -496,6 +851,14 @@ function SessionHealthDialog(props: SessionHealthDialogProps) {
|
|
|
496
851
|
<Badge kind="info">{t('sessions.targetFormat', { version: String(summary.targetFormatVersion) })}</Badge>
|
|
497
852
|
)}
|
|
498
853
|
</div>
|
|
854
|
+
{/* E2:本档(blocksStartup 的 location-mismatch / duplicate-id)的**应用内写入口**。
|
|
855
|
+
只在有候选行或手上有计划/结果时渲染;判定与键映射全在 ui/session-layout-view.ts。 */}
|
|
856
|
+
<SessionLayoutSection
|
|
857
|
+
recoveryApi={recoveryApi}
|
|
858
|
+
t={t}
|
|
859
|
+
response={response}
|
|
860
|
+
onScan={onScan}
|
|
861
|
+
/>
|
|
499
862
|
<div className={css.hint}>
|
|
500
863
|
{summary.repairable > 0
|
|
501
864
|
? t('sessions.repair.available', { count: String(summary.repairable) })
|
|
@@ -524,25 +887,31 @@ function SessionHealthDialog(props: SessionHealthDialogProps) {
|
|
|
524
887
|
{t('sessions.repair.allDone', { ok: String(batch.ok), failed: String(batch.failed) })}
|
|
525
888
|
</span>
|
|
526
889
|
{/* 逐条留痕:修好了几条、剩下几条为什么没修成 —— 绝不只给一个计数 */}
|
|
527
|
-
{batch.results.map((item) =>
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
890
|
+
{batch.results.map((item) => {
|
|
891
|
+
// 一键修复的成功项同样要说出复验结论(三态判定在 ui 纯函数层,组件只查字典)
|
|
892
|
+
// 与单条修复路径同一个纯函数、同一套 locale 键(失败项也必须给出复验结论)
|
|
893
|
+
const verifyKey = sessionRepairVerifyKey(item.verify, item.rolledBack)
|
|
894
|
+
return (
|
|
895
|
+
<div key={item.unitId} className={css.snapshotRowMeta}>
|
|
896
|
+
<span className={css.snapshotRowBadges}>
|
|
897
|
+
<Badge kind={item.ok ? 'ok' : 'warn'}>
|
|
898
|
+
{item.ok ? t('sessions.repair.batch.ok') : t('sessions.repair.batch.failed')}
|
|
899
|
+
</Badge>
|
|
900
|
+
</span>
|
|
901
|
+
<span className={css.snapshotRowIssues} title={item.unitId}>{item.sessionId}</span>
|
|
902
|
+
<span className={css.snapshotRowFacts}>
|
|
903
|
+
{item.ok
|
|
904
|
+
? t('sessions.repair.batch.dropped', { rows: String(item.droppedRows ?? 0) })
|
|
905
|
+
: item.transportError !== undefined
|
|
906
|
+
? t('sessions.repair.batch.transportError', { message: item.transportError })
|
|
907
|
+
: item.lossy === true
|
|
908
|
+
? t('sessions.repair.reason.lossyRequired')
|
|
909
|
+
: t(sessionRepairReasonKey(item.reason))}
|
|
910
|
+
{verifyKey !== null && <> · {t(verifyKey)}</>}
|
|
911
|
+
</span>
|
|
912
|
+
</div>
|
|
913
|
+
)
|
|
914
|
+
})}
|
|
546
915
|
</div>
|
|
547
916
|
</div>
|
|
548
917
|
)}
|
|
@@ -591,6 +960,20 @@ function SessionHealthDialog(props: SessionHealthDialogProps) {
|
|
|
591
960
|
</Button>
|
|
592
961
|
</div>
|
|
593
962
|
)}
|
|
963
|
+
{/* F-2:下载该会话的原始日志 ZIP(**只读**;由 DSH 自己打包,含子会话与附件)。
|
|
964
|
+
不可用时**禁用并给出原因**(不静默消失)—— 三态判定在 session-export-view.ts。 */}
|
|
965
|
+
<div className={css.actionRow}>
|
|
966
|
+
<Button
|
|
967
|
+
variant="ghost"
|
|
968
|
+
size="sm"
|
|
969
|
+
disabled={!zipState.enabled || zipBusy !== null}
|
|
970
|
+
loading={zipBusy === row.sessionId}
|
|
971
|
+
title={zipHint ?? t('sessions.zip.hint')}
|
|
972
|
+
onClick={() => { downloadZip(row.sessionId) }}
|
|
973
|
+
>
|
|
974
|
+
{t('sessions.zip.action')}
|
|
975
|
+
</Button>
|
|
976
|
+
</div>
|
|
594
977
|
</div>
|
|
595
978
|
))}
|
|
596
979
|
</div>
|
|
@@ -630,6 +1013,9 @@ function SessionHealthDialog(props: SessionHealthDialogProps) {
|
|
|
630
1013
|
<div className={css.hint}>{t('sessions.repair.rejectedNext')}</div>
|
|
631
1014
|
</>
|
|
632
1015
|
)}
|
|
1016
|
+
{/* 写后真 codec 复验结论(现役读盘可读 / 迁移链可还原 / 未验证 / 回滚失败)——
|
|
1017
|
+
key 由 ui 纯函数判定,组件不写三态逻辑 */}
|
|
1018
|
+
{planVerifyKey !== null && <div className={css.hint}>{t(planVerifyKey)}</div>}
|
|
633
1019
|
<div className={css.actionRow}>
|
|
634
1020
|
{plan.result.ok && (
|
|
635
1021
|
<Button variant="primary" loading={busy !== null} onClick={applyRepair}>
|
|
@@ -642,6 +1028,23 @@ function SessionHealthDialog(props: SessionHealthDialogProps) {
|
|
|
642
1028
|
</div>
|
|
643
1029
|
</div>
|
|
644
1030
|
)}
|
|
1031
|
+
{/* 应用成功后的结果(含真 codec 复验结论):绝不把「写完了」说成「已可加载」 */}
|
|
1032
|
+
{applied !== null && (
|
|
1033
|
+
<div className={css.snapshotRow}>
|
|
1034
|
+
<div className={css.snapshotRowMain}>
|
|
1035
|
+
<span title={applied.unitId}>{applied.result.sessionId ?? applied.unitId}</span>
|
|
1036
|
+
</div>
|
|
1037
|
+
<div className={css.hint}>
|
|
1038
|
+
{t('sessions.repair.applied', { rows: String(applied.result.droppedRows ?? 0) })}
|
|
1039
|
+
</div>
|
|
1040
|
+
{appliedVerifyKey !== null && <div className={css.hint}>{t(appliedVerifyKey)}</div>}
|
|
1041
|
+
<div className={css.actionRow}>
|
|
1042
|
+
<Button variant="ghost" onClick={() => { setApplied(null) }}>
|
|
1043
|
+
{t('common.close')}
|
|
1044
|
+
</Button>
|
|
1045
|
+
</div>
|
|
1046
|
+
</div>
|
|
1047
|
+
)}
|
|
645
1048
|
{/* 应用内不动的问题类别:离线出路 */}
|
|
646
1049
|
<div className={css.groupLabel}>{t('sessions.repair.title')}</div>
|
|
647
1050
|
<div className={css.hint}>{t('sessions.repair.desc')}</div>
|
|
@@ -781,13 +1184,13 @@ export function RecoveryPanel(props: RecoveryPanelProps) {
|
|
|
781
1184
|
incidentApi.rescueOn().then(
|
|
782
1185
|
(res) => {
|
|
783
1186
|
setRescueBusy(false)
|
|
784
|
-
if (!res.ok) { toast.error(res.message ?? t('common.unknownError')); return }
|
|
1187
|
+
if (!res.ok) { toast.error(redact(res.message ?? t('common.unknownError'))); return }
|
|
785
1188
|
toast.ok(t('recovery.rescue.on'))
|
|
786
1189
|
loadIncident()
|
|
787
1190
|
},
|
|
788
1191
|
(err) => {
|
|
789
1192
|
setRescueBusy(false)
|
|
790
|
-
toast.error(
|
|
1193
|
+
toast.error(redactErrorText(err))
|
|
791
1194
|
},
|
|
792
1195
|
)
|
|
793
1196
|
}
|
|
@@ -798,13 +1201,13 @@ export function RecoveryPanel(props: RecoveryPanelProps) {
|
|
|
798
1201
|
incidentApi.rescueOff().then(
|
|
799
1202
|
(res) => {
|
|
800
1203
|
setRescueBusy(false)
|
|
801
|
-
if (!res.ok) { toast.error(res.message ?? t('common.unknownError')); return }
|
|
1204
|
+
if (!res.ok) { toast.error(redact(res.message ?? t('common.unknownError'))); return }
|
|
802
1205
|
toast.ok(t('recovery.rescue.off'))
|
|
803
1206
|
loadIncident()
|
|
804
1207
|
},
|
|
805
1208
|
(err) => {
|
|
806
1209
|
setRescueBusy(false)
|
|
807
|
-
toast.error(
|
|
1210
|
+
toast.error(redactErrorText(err))
|
|
808
1211
|
},
|
|
809
1212
|
)
|
|
810
1213
|
}
|
|
@@ -845,7 +1248,7 @@ export function RecoveryPanel(props: RecoveryPanelProps) {
|
|
|
845
1248
|
patch({ running: false })
|
|
846
1249
|
runStore.stopRunWatch('recovery')
|
|
847
1250
|
// 确认弹窗已关闭 → 用 Toast 送达(写 panel state 将无渲染点)
|
|
848
|
-
toast.error(
|
|
1251
|
+
toast.error(redactErrorText(err))
|
|
849
1252
|
return null
|
|
850
1253
|
},
|
|
851
1254
|
).then((verifyResult) => {
|
|
@@ -872,7 +1275,7 @@ export function RecoveryPanel(props: RecoveryPanelProps) {
|
|
|
872
1275
|
(err) => {
|
|
873
1276
|
patch({ running: false })
|
|
874
1277
|
runStore.stopRunWatch('recovery')
|
|
875
|
-
toast.error(
|
|
1278
|
+
toast.error(redactErrorText(err))
|
|
876
1279
|
return null
|
|
877
1280
|
},
|
|
878
1281
|
).then((verifyResult) => {
|
|
@@ -899,7 +1302,7 @@ export function RecoveryPanel(props: RecoveryPanelProps) {
|
|
|
899
1302
|
},
|
|
900
1303
|
(err) => {
|
|
901
1304
|
patch({ running: false })
|
|
902
|
-
toast.error(
|
|
1305
|
+
toast.error(redactErrorText(err))
|
|
903
1306
|
},
|
|
904
1307
|
)
|
|
905
1308
|
}
|
|
@@ -919,7 +1322,7 @@ export function RecoveryPanel(props: RecoveryPanelProps) {
|
|
|
919
1322
|
},
|
|
920
1323
|
(err) => {
|
|
921
1324
|
setLockBusy(false)
|
|
922
|
-
toast.error(
|
|
1325
|
+
toast.error(redactErrorText(err))
|
|
923
1326
|
},
|
|
924
1327
|
)
|
|
925
1328
|
}
|
|
@@ -942,7 +1345,7 @@ export function RecoveryPanel(props: RecoveryPanelProps) {
|
|
|
942
1345
|
},
|
|
943
1346
|
(err) => {
|
|
944
1347
|
setSafeModeBusy(false)
|
|
945
|
-
toast.error(
|
|
1348
|
+
toast.error(redactErrorText(err))
|
|
946
1349
|
},
|
|
947
1350
|
)
|
|
948
1351
|
}
|
|
@@ -998,13 +1401,37 @@ export function RecoveryPanel(props: RecoveryPanelProps) {
|
|
|
998
1401
|
</Card>
|
|
999
1402
|
)}
|
|
1000
1403
|
|
|
1404
|
+
{/* t54:中断的档案复制残留(cross-F3)—— 独立形态 + 只给删除;删除走既有 POST /profiles/delete,
|
|
1405
|
+
被 SAFE MODE 拦下(423 mutation-locked)时给原因与本页出口(safe-mode/clear)。 */}
|
|
1406
|
+
{view !== null && view.incompleteCopies.length > 0 && (
|
|
1407
|
+
<IncompleteCopiesSection
|
|
1408
|
+
rows={view.incompleteCopies}
|
|
1409
|
+
t={t}
|
|
1410
|
+
diskApi={props.diskApi}
|
|
1411
|
+
recoveryApi={props.recoveryApi}
|
|
1412
|
+
onChanged={load}
|
|
1413
|
+
/>
|
|
1414
|
+
)}
|
|
1415
|
+
|
|
1416
|
+
{/* t89:残留**枚举失败** —— 与上面「有残留」和「暂无残留」都不同形:显式告知「读不到 ≠ 没有」,
|
|
1417
|
+
并给唯一的可行动作(重试 = 重拉 /recovery/status)。判定在 recovery-view.ts 的纯函数里,这里只装配。 */}
|
|
1418
|
+
{view !== null && view.incompleteCopiesNotice === 'unreadable' && (
|
|
1419
|
+
<Card>
|
|
1420
|
+
<SectionTitle title={t('recovery.incomplete.unreadable.title')} />
|
|
1421
|
+
<Banner kind="warn">{t('recovery.incomplete.unreadable.hint')}</Banner>
|
|
1422
|
+
<div className={css.actionRow}>
|
|
1423
|
+
<Button onClick={load}>{t('common.retry')}</Button>
|
|
1424
|
+
</div>
|
|
1425
|
+
</Card>
|
|
1426
|
+
)}
|
|
1427
|
+
|
|
1001
1428
|
{/* 磁盘占用与清理(m-disk-usage):§9 固定的第 2 块。
|
|
1002
1429
|
三条硬边界一字不动(候选集只有可重建区 + 显式勾选的过期导出;snapshots/sync 永不在候选集;
|
|
1003
1430
|
界面数字由渲染行现算)—— 实现全在 DiskUsageCard 与 ui/disk-usage-view.ts 里。 */}
|
|
1004
1431
|
<DiskUsageCard api={props.diskApi} infoHintLabel={props.infoHintLabel ?? props.t('common.infoHint')} />
|
|
1005
1432
|
|
|
1006
1433
|
{/* T5:会话体检(只读)—— 与「我的对话去哪了」是同一件事,故与事故恢复同屏 */}
|
|
1007
|
-
<SessionHealthCard recoveryApi={recoveryApi} t={t} copyT={props.copyT} />
|
|
1434
|
+
<SessionHealthCard recoveryApi={recoveryApi} t={t} copyT={props.copyT} diskApi={props.diskApi} />
|
|
1008
1435
|
|
|
1009
1436
|
{/* SAFE MODE / recovery-required 状态提示(正常态不渲染任何横幅——无事项即静默) */}
|
|
1010
1437
|
{view?.recoveryRequired === true && (
|