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,625 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 会话类来源的**共享装配层**(档 B 24 个来源共用;纯函数,零 I/O)。
|
|
3
|
+
*
|
|
4
|
+
* 三段式的第三段与第二段在这里收口(第一段「源格式 → 归一记录」在各来源自己的 `<id>.ts` 里):
|
|
5
|
+
*
|
|
6
|
+
* 源原文 --(各来源 parse)--> TranscriptRecord[] --(本模块)--> IR --(synthesizeDshRows)--> DSH 行
|
|
7
|
+
* --(encodeDshSessionLog)--> 字节 --(kernel.collectSessionSections)--> sessions+workspaces 分区
|
|
8
|
+
*
|
|
9
|
+
* 为什么必须收口:这四步里有三处**宿主硬不变量**(DSH codec 强校验 seq / surfaceOp / 工具闭合)与
|
|
10
|
+
* 一处**契约硬约束**(同一 DSH 会话 id 只允许出现一次、必须连 workspaces 一起产出)。
|
|
11
|
+
* 24 个来源各写一遍 = 24 个地方可能写错,且错法是「导入全绿但对话不出现」。
|
|
12
|
+
*
|
|
13
|
+
* 与 `claude-sessions.ts` 的关系:**字节出口只有一份实现**(本模块复用它的 `encodeDshSessionLog`
|
|
14
|
+
* 与 `dshSessionLogName`),Claude 自己的解析路径**一字未改**(逐字节回归仍然钉住它)。
|
|
15
|
+
*/
|
|
16
|
+
import { projectKeyOf } from '../core/session-select.ts';
|
|
17
|
+
import { dshSessionLogName, encodeDshSessionLog, SUPPORTED_DSH_SESSION_FORMAT_VERSIONS } from './claude-sessions.ts';
|
|
18
|
+
import { collectSessionSections } from './kernel.ts';
|
|
19
|
+
import { foreignSourceLabelKey } from './source-modules.ts';
|
|
20
|
+
import { probeConfiguredPaths } from './session-read.ts';
|
|
21
|
+
import { envValue } from './platform-paths.ts';
|
|
22
|
+
import type { ForeignSource } from './registry.ts';
|
|
23
|
+
import {
|
|
24
|
+
irBump,
|
|
25
|
+
irEarlier,
|
|
26
|
+
irFallbackMessageId,
|
|
27
|
+
irMessage,
|
|
28
|
+
irSafeTime,
|
|
29
|
+
irTextBlock,
|
|
30
|
+
irTextOfBlocks,
|
|
31
|
+
irToolCallBlock,
|
|
32
|
+
irToolResultBlock,
|
|
33
|
+
isSafeIrId,
|
|
34
|
+
synthesizeDshRows,
|
|
35
|
+
} from './session-ir.ts';
|
|
36
|
+
import type {
|
|
37
|
+
DshSessionHeader,
|
|
38
|
+
IrBlock,
|
|
39
|
+
IrMessage,
|
|
40
|
+
IrTimeMs,
|
|
41
|
+
SynthesisStats,
|
|
42
|
+
} from './session-ir.ts';
|
|
43
|
+
import { isRecord } from '../utils/guards.ts';
|
|
44
|
+
import type { ForeignEvidenceKind, ForeignImportResult, ForeignLimitOverrides, ForeignSectionOut, ForeignSkip, ForeignSkipCode, ForeignSourceId } from './types.ts';
|
|
45
|
+
|
|
46
|
+
/* ---------------- ① 归一记录(各来源只负责产出这个) ---------------- */
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* 一条**已归一**的源记录。
|
|
50
|
+
*
|
|
51
|
+
* 刻意不含任何「DSH 行式」知识(turn/step/seq 全在合成器里)—— 各来源只需要回答
|
|
52
|
+
* 「这条是用户还是助手、有哪些内容块、什么时间、哪个 id、哪个模型」。
|
|
53
|
+
*/
|
|
54
|
+
export interface TranscriptRecord {
|
|
55
|
+
readonly role: 'user' | 'assistant';
|
|
56
|
+
readonly blocks: readonly IrBlock[];
|
|
57
|
+
readonly time?: IrTimeMs | undefined;
|
|
58
|
+
readonly id?: string | undefined;
|
|
59
|
+
readonly model?: string | undefined;
|
|
60
|
+
readonly usage?: unknown;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/** 一次解析的结果(记账字段与竞品 `parseJsonlLines` 的 {recs,skipped,skippedLines} 同形) */
|
|
64
|
+
export interface ParsedTranscript {
|
|
65
|
+
readonly records: readonly TranscriptRecord[];
|
|
66
|
+
/** cwd(源里最权威的那个;缺失 = 调用方转成 session-missing-cwd,绝不猜) */
|
|
67
|
+
readonly cwd?: string | undefined;
|
|
68
|
+
readonly createdAt?: IrTimeMs | undefined;
|
|
69
|
+
/** 源给的标题(空串 = 没有) */
|
|
70
|
+
readonly title: string;
|
|
71
|
+
/** 源记录的原始条数(含被忽略的) */
|
|
72
|
+
readonly raw: number;
|
|
73
|
+
/** 解析不出来的条数/坏行数 */
|
|
74
|
+
readonly bad: number;
|
|
75
|
+
/** 未迁移的记录类型 → 条数(**逐类计数,绝不静默**) */
|
|
76
|
+
readonly ignored: Record<string, number>;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/** 一条待转码的会话草稿(IR 之上的「源侧元数据」) */
|
|
80
|
+
export interface SessionDraft {
|
|
81
|
+
readonly id: string;
|
|
82
|
+
readonly cwd: string | undefined;
|
|
83
|
+
readonly createdAt: IrTimeMs | undefined;
|
|
84
|
+
readonly title: string;
|
|
85
|
+
readonly messages: readonly IrMessage[];
|
|
86
|
+
/** 源侧记录的原始条数(含被忽略的) */
|
|
87
|
+
readonly records: number;
|
|
88
|
+
readonly provider: string;
|
|
89
|
+
/** 未迁移类型 → 条数(转成 unsupported-session-record 逐条报码) */
|
|
90
|
+
readonly ignored?: Readonly<Record<string, number>> | undefined;
|
|
91
|
+
/** 解析不出来的行数(进 ignored.unparsable) */
|
|
92
|
+
readonly unparsable?: number | undefined;
|
|
93
|
+
/** 「本轮内容块全空」的取值(Claude 取 [],其余来源同样取 []) */
|
|
94
|
+
readonly emptyBlocks?: readonly IrBlock[] | undefined;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/** 归一记录 → SessionDraft(各来源统一走这一处,不自己拼 IrMessage) */
|
|
98
|
+
export function draftFromTranscript(
|
|
99
|
+
id: string,
|
|
100
|
+
parsed: ParsedTranscript,
|
|
101
|
+
provider: string,
|
|
102
|
+
): SessionDraft {
|
|
103
|
+
const messages: IrMessage[] = [];
|
|
104
|
+
let index = 0;
|
|
105
|
+
for (const rec of parsed.records) {
|
|
106
|
+
const passthrough: Record<string, unknown> = {};
|
|
107
|
+
if (rec.time !== undefined) passthrough['time'] = rec.time;
|
|
108
|
+
if (rec.usage !== undefined) passthrough['usage'] = rec.usage;
|
|
109
|
+
if (rec.role === 'assistant') passthrough['source'] = { model: rec.model ?? '' };
|
|
110
|
+
const messageId = rec.id !== undefined && isSafeIrId(rec.id) ? rec.id : irFallbackMessageId(index);
|
|
111
|
+
messages.push(irMessage(messageId, rec.role, rec.blocks, passthrough));
|
|
112
|
+
index += 1;
|
|
113
|
+
}
|
|
114
|
+
return {
|
|
115
|
+
id,
|
|
116
|
+
cwd: parsed.cwd,
|
|
117
|
+
createdAt: parsed.createdAt,
|
|
118
|
+
title: parsed.title,
|
|
119
|
+
messages,
|
|
120
|
+
records: parsed.raw,
|
|
121
|
+
provider,
|
|
122
|
+
ignored: parsed.ignored,
|
|
123
|
+
unparsable: parsed.bad,
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/* ---------------- ② 草稿 → 字节(唯一转码出口) ---------------- */
|
|
128
|
+
|
|
129
|
+
export interface DraftTranscodeOptions {
|
|
130
|
+
/** 目标机 DSH 的 SESSION_FORMAT_VERSION(必填;不支持即 session-format-unsupported,绝不猜) */
|
|
131
|
+
readonly formatVersion: number;
|
|
132
|
+
/** 时间兜底(源里没有可解析时间时用) */
|
|
133
|
+
readonly now?: number | undefined;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
export interface DraftTranscodeResult {
|
|
137
|
+
readonly session?: {
|
|
138
|
+
readonly id: string;
|
|
139
|
+
readonly cwd: string;
|
|
140
|
+
readonly relativePath: string;
|
|
141
|
+
readonly data: Uint8Array;
|
|
142
|
+
readonly info: { readonly ignored: Record<string, number> };
|
|
143
|
+
};
|
|
144
|
+
readonly skip?: { readonly code: ForeignSkipCode; readonly detail?: string };
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* 骨架的三段判定 + 合成 + 编码(**所有会话类来源的唯一出口**)。
|
|
149
|
+
*
|
|
150
|
+
* 判定顺序与 `transcodeClaudeSession` 逐条对齐(版本 → id → 空 → 缺 cwd → 解析不出 → 合成空):
|
|
151
|
+
* 顺序变了,用户看到的 skip 码就会变(同一个坏文件从 session-missing-cwd 变成 session-empty)。
|
|
152
|
+
*/
|
|
153
|
+
export function transcodeSessionDraft(draft: SessionDraft, options: DraftTranscodeOptions): DraftTranscodeResult {
|
|
154
|
+
if (!SUPPORTED_DSH_SESSION_FORMAT_VERSIONS.includes(options.formatVersion)) {
|
|
155
|
+
return { skip: { code: 'session-format-unsupported', detail: String(options.formatVersion) } };
|
|
156
|
+
}
|
|
157
|
+
if (!isSafeIrId(draft.id)) return { skip: { code: 'session-unsafe-id', detail: draft.id } };
|
|
158
|
+
if (draft.records === 0) return { skip: { code: 'session-empty' } };
|
|
159
|
+
if (draft.cwd === undefined || draft.cwd === '') return { skip: { code: 'session-missing-cwd', detail: draft.id } };
|
|
160
|
+
if (draft.messages.length === 0 && (draft.unparsable ?? 0) > 0) {
|
|
161
|
+
return { skip: { code: 'session-unparsable', detail: draft.id } };
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
const now = options.now ?? Date.now();
|
|
165
|
+
const stats: SynthesisStats = { ignored: {}, userMessages: 0, assistantMessages: 0, toolCalls: 0, toolResults: 0 };
|
|
166
|
+
if ((draft.unparsable ?? 0) > 0) irBump(stats.ignored, 'unparsable', draft.unparsable ?? 0);
|
|
167
|
+
for (const [kind, count] of Object.entries(draft.ignored ?? {})) {
|
|
168
|
+
if (count > 0) irBump(stats.ignored, kind, count);
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
const createdAt = draft.createdAt ?? now;
|
|
172
|
+
const rows = synthesizeDshRows(
|
|
173
|
+
{ id: draft.id, cwd: draft.cwd, createdAt, records: draft.records, messages: draft.messages },
|
|
174
|
+
{
|
|
175
|
+
title: draft.title,
|
|
176
|
+
titleSource: { kind: 'fallback' },
|
|
177
|
+
emptyBlocks: draft.emptyBlocks ?? [],
|
|
178
|
+
provider: draft.provider,
|
|
179
|
+
reasoningEffort: 'medium',
|
|
180
|
+
maxTokens: 8192,
|
|
181
|
+
},
|
|
182
|
+
stats,
|
|
183
|
+
now,
|
|
184
|
+
);
|
|
185
|
+
if (rows.length === 0) return { skip: { code: 'session-empty' } };
|
|
186
|
+
|
|
187
|
+
const header: DshSessionHeader = {
|
|
188
|
+
type: 'session',
|
|
189
|
+
version: options.formatVersion,
|
|
190
|
+
id: draft.id,
|
|
191
|
+
createdAt,
|
|
192
|
+
cwd: draft.cwd,
|
|
193
|
+
isSeeded: false,
|
|
194
|
+
delegationDepth: 0,
|
|
195
|
+
};
|
|
196
|
+
return {
|
|
197
|
+
session: {
|
|
198
|
+
id: draft.id,
|
|
199
|
+
cwd: draft.cwd,
|
|
200
|
+
relativePath: projectKeyOf(draft.cwd) + '/' + draft.id + '/' + dshSessionLogName(options.formatVersion),
|
|
201
|
+
data: encodeDshSessionLog(header, rows),
|
|
202
|
+
info: { ignored: stats.ignored },
|
|
203
|
+
},
|
|
204
|
+
};
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
/* ---------------- ③ 装配:草稿 → ForeignImportResult ---------------- */
|
|
208
|
+
|
|
209
|
+
export interface SessionSourceOptions<TFile> {
|
|
210
|
+
/** 待转码的会话(已读盘 / 已解析成草稿的输入形态) */
|
|
211
|
+
readonly files: readonly TFile[];
|
|
212
|
+
/** 目标机格式版本(缺省 = 一条都不转,整批报 session-format-version-unknown) */
|
|
213
|
+
readonly targetFormatVersion: number | undefined;
|
|
214
|
+
/** workspaces 记录 id 的前缀(契约 §8.3:<sourceId>:<projectKey>) */
|
|
215
|
+
readonly workspaceIdPrefix: string;
|
|
216
|
+
/** 单个文件 → 草稿(或一个 skip 码) */
|
|
217
|
+
readonly draftOf: (file: TFile) => SessionDraft | { readonly skip: { readonly code: ForeignSkipCode; readonly detail?: string } };
|
|
218
|
+
/** 读盘层的发现(原样带出,调用方无需二次合并) */
|
|
219
|
+
readonly readFindings?: readonly ForeignSkip[] | undefined;
|
|
220
|
+
/** 额外的计数(如「发现 N 个会话目录」) */
|
|
221
|
+
readonly extraCounts?: Readonly<Record<string, number>> | undefined;
|
|
222
|
+
/** 额外的未迁移项(如「旧版 JSONL 刻意不读」) */
|
|
223
|
+
readonly extraSkips?: readonly ForeignSkip[] | undefined;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
/**
|
|
227
|
+
* 会话类来源的**统一出口**:草稿 → sessions + workspaces 分区 → ForeignImportResult。
|
|
228
|
+
*
|
|
229
|
+
* 走 `kernel.collectSessionSections`(与 Claude 同一份实现)而不是自己拼,理由有三:
|
|
230
|
+
* ① 冲突语义(同 DSH 会话 id 只允许出现一次)只有一处实现;
|
|
231
|
+
* ② workspaces 必须与会话同源产出(只给会话不给工作区 = 目标机上一条对话都看不见);
|
|
232
|
+
* ③ 「未迁移记录逐类计数」只有一处实现。
|
|
233
|
+
*/
|
|
234
|
+
export function buildSessionSourceResult<TFile extends { id: string }>(
|
|
235
|
+
source: ForeignSourceId,
|
|
236
|
+
opts: SessionSourceOptions<TFile>,
|
|
237
|
+
): ForeignImportResult {
|
|
238
|
+
const sections: ForeignSectionOut[] = [];
|
|
239
|
+
const skipped: ForeignSkip[] = [...(opts.readFindings ?? []), ...(opts.extraSkips ?? [])];
|
|
240
|
+
const counts: Record<string, number> = { ...(opts.extraCounts ?? {}) };
|
|
241
|
+
const formatVersion = opts.targetFormatVersion ?? -1;
|
|
242
|
+
collectSessionSections<TFile>({
|
|
243
|
+
files: opts.files,
|
|
244
|
+
targetFormatVersion: opts.targetFormatVersion,
|
|
245
|
+
transcode: (file) => {
|
|
246
|
+
const draft = opts.draftOf(file);
|
|
247
|
+
if ('skip' in draft) return { skip: draft.skip };
|
|
248
|
+
return transcodeSessionDraft(draft, { formatVersion });
|
|
249
|
+
},
|
|
250
|
+
workspaceIdPrefix: opts.workspaceIdPrefix,
|
|
251
|
+
sections,
|
|
252
|
+
skipped,
|
|
253
|
+
counts,
|
|
254
|
+
});
|
|
255
|
+
return { source, sections, skipped, credentialRefs: [], counts };
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
/* ---------------- ④ 通用内容块 / 记录映射(各来源共用的兜底口径) ---------------- */
|
|
259
|
+
|
|
260
|
+
/** 文本投影:字符串原样;数组逐项拼;对象取 text/content/message;其余 → ''(绝不 JSON.stringify 进正文) */
|
|
261
|
+
export function flattenText(value: unknown, unknown?: Record<string, number>, key = 'block'): string {
|
|
262
|
+
if (typeof value === 'string') return value;
|
|
263
|
+
if (Array.isArray(value)) {
|
|
264
|
+
const parts: string[] = [];
|
|
265
|
+
for (const item of value) parts.push(flattenText(item, unknown, key));
|
|
266
|
+
return parts.join(String.fromCharCode(10));
|
|
267
|
+
}
|
|
268
|
+
if (isRecord(value)) {
|
|
269
|
+
for (const candidate of ['text', 'content', 'message']) {
|
|
270
|
+
if (value[candidate] !== undefined) return flattenText(value[candidate], unknown, key);
|
|
271
|
+
}
|
|
272
|
+
if (unknown !== undefined) irBump(unknown, key + ':unmapped');
|
|
273
|
+
return '';
|
|
274
|
+
}
|
|
275
|
+
return '';
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
const TOOL_CALL_TYPES = ['tool_use', 'tool_call', 'function_call', 'tool-call', 'tool-invocation'];
|
|
279
|
+
const TOOL_RESULT_TYPES = ['tool_result', 'tool_response', 'function_call_output', 'tool-result'];
|
|
280
|
+
const TEXT_TYPES = ['text', 'input_text', 'output_text', 'message'];
|
|
281
|
+
|
|
282
|
+
function firstString(rec: Record<string, unknown>, keys: readonly string[]): string | undefined {
|
|
283
|
+
for (const key of keys) {
|
|
284
|
+
const v = rec[key];
|
|
285
|
+
if (typeof v === 'string' && v !== '') return v;
|
|
286
|
+
}
|
|
287
|
+
return undefined;
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
/** 工具入参:字符串若是 JSON 就解析(源侧两种形态并存),否则原样字符串 */
|
|
291
|
+
function toolInputOf(raw: unknown): unknown {
|
|
292
|
+
if (typeof raw === 'string') {
|
|
293
|
+
const trimmed = raw.trim();
|
|
294
|
+
if (trimmed.startsWith('{') || trimmed.startsWith('[')) {
|
|
295
|
+
try {
|
|
296
|
+
return JSON.parse(trimmed);
|
|
297
|
+
} catch {
|
|
298
|
+
return raw;
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
return raw;
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
/**
|
|
306
|
+
* 通用内容块映射(绝大多数来源的 `content` / `parts` 都能被它覆盖)。
|
|
307
|
+
*
|
|
308
|
+
* 覆盖四种形态:字符串、块数组(text / tool_use / tool_result)、单块对象、
|
|
309
|
+
* 以及把整段对话描述成 `{type:'text',text}` 的形态。**不认识的块逐类计数**(绝不静默丢)。
|
|
310
|
+
*/
|
|
311
|
+
export function genericBlocksOf(content: unknown, unknown?: Record<string, number>, key = 'block'): IrBlock[] {
|
|
312
|
+
if (typeof content === 'string') return [irTextBlock(content)];
|
|
313
|
+
if (Array.isArray(content)) {
|
|
314
|
+
const out: IrBlock[] = [];
|
|
315
|
+
for (const item of content) out.push(...genericBlocksOf(item, unknown, key));
|
|
316
|
+
return out;
|
|
317
|
+
}
|
|
318
|
+
if (!isRecord(content)) return [];
|
|
319
|
+
const type = typeof content['type'] === 'string' ? (content['type'] as string) : '';
|
|
320
|
+
const lower = type.toLowerCase();
|
|
321
|
+
if (TOOL_CALL_TYPES.includes(lower)) {
|
|
322
|
+
const name = firstString(content, ['name', 'tool', 'toolName', 'tool_name']) ?? '';
|
|
323
|
+
const id = firstString(content, ['id', 'tool_call_id', 'callId', 'call_id']) ?? '';
|
|
324
|
+
const input = content['input'] ?? content['arguments'] ?? content['args'] ?? content['parameters'];
|
|
325
|
+
return [irToolCallBlock(id, name, toolInputOf(input))];
|
|
326
|
+
}
|
|
327
|
+
if (TOOL_RESULT_TYPES.includes(lower)) {
|
|
328
|
+
const id = firstString(content, ['tool_use_id', 'tool_call_id', 'call_id', 'id']) ?? '';
|
|
329
|
+
const body = content['content'] ?? content['output'] ?? content['result'] ?? content['text'];
|
|
330
|
+
const text = flattenText(body, unknown, key);
|
|
331
|
+
const isError = content['is_error'] === true || content['isError'] === true || content['error'] === true;
|
|
332
|
+
return [irToolResultBlock(id, text, isError)];
|
|
333
|
+
}
|
|
334
|
+
if (lower === '' || TEXT_TYPES.includes(lower)) {
|
|
335
|
+
const text = content['text'] !== undefined ? content['text'] : content['content'];
|
|
336
|
+
if (typeof text === 'string') return [irTextBlock(text)];
|
|
337
|
+
if (Array.isArray(text)) return genericBlocksOf(text, unknown, key);
|
|
338
|
+
if (typeof content['content'] === 'string') return [irTextBlock(content['content'] as string)];
|
|
339
|
+
}
|
|
340
|
+
if (unknown !== undefined) irBump(unknown, key + ':' + (lower === '' ? 'unmapped' : lower));
|
|
341
|
+
return [];
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
/** 通用 JSONL 形态的字段真值(各来源可覆盖其中任意几项) */
|
|
345
|
+
export interface TranscriptShape {
|
|
346
|
+
/** 角色字段候选(按序取第一个非空字符串) */
|
|
347
|
+
readonly roleKeys: readonly string[];
|
|
348
|
+
/** 内容字段候选;命中的值可以是字符串 / 数组 / 对象 */
|
|
349
|
+
readonly contentKeys: readonly string[];
|
|
350
|
+
/** 记录类型字段(用于「未迁移类型」计数) */
|
|
351
|
+
readonly typeKey: string;
|
|
352
|
+
/** 角色取值(大小写不敏感)→ user */
|
|
353
|
+
readonly userValues: readonly string[];
|
|
354
|
+
/** 角色取值 → assistant */
|
|
355
|
+
readonly assistantValues: readonly string[];
|
|
356
|
+
readonly timeKeys: readonly string[];
|
|
357
|
+
readonly idKeys: readonly string[];
|
|
358
|
+
readonly cwdKeys: readonly string[];
|
|
359
|
+
readonly titleKeys: readonly string[];
|
|
360
|
+
readonly modelKeys: readonly string[];
|
|
361
|
+
/** 额外的「整条记录就是一段文本」字段(如 {type:'summary',summary:'…'} 不当消息) */
|
|
362
|
+
readonly textKeys: readonly string[];
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
/** 主形态(Claude / Codex / 多数 JSONL 工具的共同结构:{type|role, message|content,…}) */
|
|
366
|
+
export const GENERIC_TRANSCRIPT_SHAPE: TranscriptShape = {
|
|
367
|
+
roleKeys: ['role', 'type', 'kind', 'sender'],
|
|
368
|
+
contentKeys: ['message', 'content', 'parts', 'text', 'data'],
|
|
369
|
+
typeKey: 'type',
|
|
370
|
+
userValues: ['user', 'human', 'user_message', 'user-message', 'prompt'],
|
|
371
|
+
assistantValues: ['assistant', 'ai', 'model', 'gemini', 'assistant_message', 'assistant-message', 'completion'],
|
|
372
|
+
timeKeys: ['timestamp', 'time', 'createdAt', 'created_at', 'ts', 'date'],
|
|
373
|
+
idKeys: ['id', 'uuid', 'messageId', 'message_id'],
|
|
374
|
+
cwdKeys: ['cwd', 'workdir', 'working_directory', 'workingDir', 'directory'],
|
|
375
|
+
titleKeys: ['title', 'summary', 'name'],
|
|
376
|
+
modelKeys: ['model', 'modelId', 'model_id'],
|
|
377
|
+
textKeys: [],
|
|
378
|
+
};
|
|
379
|
+
|
|
380
|
+
/** 取第一条可用的内容字段值(message 是对象时下钻一层到它的 content) */
|
|
381
|
+
function contentOf(rec: Record<string, unknown>, shape: TranscriptShape): unknown {
|
|
382
|
+
for (const key of shape.contentKeys) {
|
|
383
|
+
const raw = rec[key];
|
|
384
|
+
if (raw === undefined || raw === null) continue;
|
|
385
|
+
if (isRecord(raw)) {
|
|
386
|
+
for (const inner of ['content', 'text', 'parts', 'blocks']) {
|
|
387
|
+
if (raw[inner] !== undefined) return raw[inner];
|
|
388
|
+
}
|
|
389
|
+
return raw;
|
|
390
|
+
}
|
|
391
|
+
return raw;
|
|
392
|
+
}
|
|
393
|
+
return undefined;
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
/** 在**一个**记录里按 roleKeys 找角色(找不到返回 undefined) */
|
|
397
|
+
function roleIn(rec: Record<string, unknown>, shape: TranscriptShape): string | undefined {
|
|
398
|
+
for (const key of shape.roleKeys) {
|
|
399
|
+
const raw = rec[key];
|
|
400
|
+
if (typeof raw !== 'string' || raw === '') continue;
|
|
401
|
+
const lower = raw.toLowerCase();
|
|
402
|
+
if (shape.userValues.includes(lower)) return 'user';
|
|
403
|
+
if (shape.assistantValues.includes(lower)) return 'assistant';
|
|
404
|
+
return 'unsupported:' + lower;
|
|
405
|
+
}
|
|
406
|
+
return undefined;
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
/**
|
|
410
|
+
* 角色归一:命中 userValues → user,命中 assistantValues → assistant,其余 undefined(调用方计数)。
|
|
411
|
+
*
|
|
412
|
+
* **一层包装的回退**(2026-10-06):事件流形态的源把角色放在 `{type:'message', message:{role,…}}` 的
|
|
413
|
+
* 内层对象里(Pi / OpenClaw 等)。顶层**完全取不到**角色时才下钻一层 —— 顶层能取到就照旧,
|
|
414
|
+
* 既有 24 个来源的判定因此逐字不变(回归由各自单测钉住)。
|
|
415
|
+
*/
|
|
416
|
+
function roleOf(rec: Record<string, unknown>, shape: TranscriptShape): string | undefined {
|
|
417
|
+
const direct = roleIn(rec, shape);
|
|
418
|
+
if (direct !== undefined) return direct;
|
|
419
|
+
const inner = rec['message'];
|
|
420
|
+
if (isRecord(inner)) return roleIn(inner, shape);
|
|
421
|
+
return undefined;
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
/**
|
|
425
|
+
* 通用 JSONL 解析:**一行一条 JSON 对象**,按 shape 映射成归一记录。
|
|
426
|
+
*
|
|
427
|
+
* 三条纪律(与竞品 `parseJsonlLines` 的记账口径同源,但**明细不设 200 上限**):
|
|
428
|
+
* ① 坏行不抛,只计入 `bad`;② 未迁移记录类型逐类计入 `ignored`;③ cwd 取**第一个**有值的记录。
|
|
429
|
+
*/
|
|
430
|
+
export function parseGenericJsonl(text: string, shape: TranscriptShape = GENERIC_TRANSCRIPT_SHAPE): ParsedTranscript {
|
|
431
|
+
const records: TranscriptRecord[] = [];
|
|
432
|
+
const ignored: Record<string, number> = {};
|
|
433
|
+
let raw = 0;
|
|
434
|
+
let bad = 0;
|
|
435
|
+
let cwd: string | undefined;
|
|
436
|
+
let createdAt: number | undefined;
|
|
437
|
+
let title = '';
|
|
438
|
+
for (const line of text.split(String.fromCharCode(10))) {
|
|
439
|
+
if (line.trim() === '') continue;
|
|
440
|
+
let parsed: unknown;
|
|
441
|
+
try {
|
|
442
|
+
parsed = JSON.parse(line);
|
|
443
|
+
} catch {
|
|
444
|
+
bad += 1;
|
|
445
|
+
continue;
|
|
446
|
+
}
|
|
447
|
+
if (!isRecord(parsed)) {
|
|
448
|
+
bad += 1;
|
|
449
|
+
continue;
|
|
450
|
+
}
|
|
451
|
+
raw += 1;
|
|
452
|
+
if (cwd === undefined) cwd = firstString(parsed, shape.cwdKeys);
|
|
453
|
+
const typeValue = parsed[shape.typeKey];
|
|
454
|
+
const typeName = typeof typeValue === 'string' && typeValue !== '' ? typeValue : 'unknown';
|
|
455
|
+
if (title === '') {
|
|
456
|
+
const candidate = firstString(parsed, shape.titleKeys);
|
|
457
|
+
if (candidate !== undefined && shape.titleKeys.indexOf(shape.typeKey) < 0) title = candidate;
|
|
458
|
+
}
|
|
459
|
+
const time = firstTime(parsed, shape.timeKeys);
|
|
460
|
+
createdAt = irEarlier(createdAt, time);
|
|
461
|
+
const role = roleOf(parsed, shape);
|
|
462
|
+
if (role === undefined || role.startsWith('unsupported:')) {
|
|
463
|
+
irBump(ignored, role === undefined ? typeName : role.slice('unsupported:'.length));
|
|
464
|
+
continue;
|
|
465
|
+
}
|
|
466
|
+
if (shape.textKeys.length > 0) {
|
|
467
|
+
let handled = false;
|
|
468
|
+
for (const key of shape.textKeys) {
|
|
469
|
+
const v = parsed[key];
|
|
470
|
+
if (typeof v === 'string' && v !== '') {
|
|
471
|
+
records.push({ role: role as 'user' | 'assistant', blocks: [irTextBlock(v)], time });
|
|
472
|
+
handled = true;
|
|
473
|
+
break;
|
|
474
|
+
}
|
|
475
|
+
}
|
|
476
|
+
if (handled) continue;
|
|
477
|
+
}
|
|
478
|
+
const blocks = genericBlocksOf(contentOf(parsed, shape), ignored);
|
|
479
|
+
if (blocks.length === 0) {
|
|
480
|
+
irBump(ignored, typeName + ':no-content');
|
|
481
|
+
continue;
|
|
482
|
+
}
|
|
483
|
+
records.push({
|
|
484
|
+
role: role as 'user' | 'assistant',
|
|
485
|
+
blocks,
|
|
486
|
+
time,
|
|
487
|
+
id: firstString(parsed, shape.idKeys),
|
|
488
|
+
model: firstString(parsed, shape.modelKeys),
|
|
489
|
+
usage: isRecord(parsed['usage']) ? parsed['usage'] : undefined,
|
|
490
|
+
});
|
|
491
|
+
}
|
|
492
|
+
if (title === '') title = firstUserText(records);
|
|
493
|
+
return { records, cwd, createdAt, title, raw, bad, ignored };
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
function firstTime(rec: Record<string, unknown>, keys: readonly string[]): IrTimeMs | undefined {
|
|
497
|
+
for (const key of keys) {
|
|
498
|
+
const t = irSafeTime(rec[key]);
|
|
499
|
+
if (t !== undefined) return t;
|
|
500
|
+
}
|
|
501
|
+
return undefined;
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
/** 首条用户文本前 80 字(折叠空白)——标题兜底口径与 Claude 一致 */
|
|
505
|
+
export function firstUserText(records: readonly TranscriptRecord[]): string {
|
|
506
|
+
for (const rec of records) {
|
|
507
|
+
if (rec.role !== 'user') continue;
|
|
508
|
+
const text = irTextOfBlocks(rec.blocks);
|
|
509
|
+
if (text.trim() !== '') return text.replace(/\s+/g, ' ').trim().slice(0, 80);
|
|
510
|
+
}
|
|
511
|
+
return '';
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
/** 取源给的会话标题字段(空串表示没有) */
|
|
515
|
+
export function titleFromRecord(rec: unknown, keys: readonly string[]): string {
|
|
516
|
+
if (!isRecord(rec)) return '';
|
|
517
|
+
const v = firstString(rec, keys);
|
|
518
|
+
return v === undefined ? '' : v.slice(0, 200);
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
/** 归一记录的 id 兜底(源没给合法 id 时用会话内序号铸稳定伪 uuid) */
|
|
522
|
+
export function recordIdOf(raw: string | undefined, index: number): string {
|
|
523
|
+
return raw !== undefined && isSafeIrId(raw) ? raw : irFallbackMessageId(index);
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
/* ---------------- ⑤ 来源装配(detect/build/probePaths 只写一遍) ---------------- */
|
|
527
|
+
|
|
528
|
+
/** 探测上下文(纯函数:homeDir / env / platform,**不读盘**) */
|
|
529
|
+
export interface RootProbeOptions {
|
|
530
|
+
readonly homeDir: string;
|
|
531
|
+
readonly env: Readonly<Record<string, string | undefined>>;
|
|
532
|
+
readonly platform: string;
|
|
533
|
+
/** 显式项目目录(crush 的每项目一库 / chatgpt 的显式导出文件都靠它,缺省不猜) */
|
|
534
|
+
readonly projectDir?: string | undefined;
|
|
535
|
+
/**
|
|
536
|
+
* 可选上限覆盖(t36):宿主路由 / CLI 将来放开上限时的通道。
|
|
537
|
+
* 缺省 = 各读器自己的选项与默认值**逐字不变**;读器用 resolveLimit(覆盖, 显式, 默认) 取值。
|
|
538
|
+
*/
|
|
539
|
+
readonly limits?: ForeignLimitOverrides | undefined;
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
/** 读盘上下文(read-*.ts 的唯一入口形状) */
|
|
543
|
+
export interface SessionSourceHostOptions extends RootProbeOptions {
|
|
544
|
+
readonly targetFormatVersion?: number | undefined;
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
/** 一次读盘的结果(**不含**任何分区装配语义 —— 那是 buildSessionSourceResult 的事) */
|
|
548
|
+
export interface SessionReadOutcome<TFile extends { id: string }> {
|
|
549
|
+
readonly files: readonly TFile[];
|
|
550
|
+
/** 读盘层的发现(0 字节 / 超限 / 只读打开失败 …) */
|
|
551
|
+
readonly readFindings?: readonly ForeignSkip[] | undefined;
|
|
552
|
+
/** 结构性提示(如「旧式 JSONL 刻意不读」「本版不迁移」) */
|
|
553
|
+
readonly extraSkips?: readonly ForeignSkip[] | undefined;
|
|
554
|
+
readonly extraCounts?: Readonly<Record<string, number>> | undefined;
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
/** 会话类来源的装配输入 */
|
|
558
|
+
export interface SessionSourceWiring<TFile extends { id: string }> {
|
|
559
|
+
readonly id: ForeignSourceId;
|
|
560
|
+
readonly evidence: ForeignEvidenceKind;
|
|
561
|
+
/** 目标平台下的**静态探测位置**(真值表;运行时再在它下面动态枚举) */
|
|
562
|
+
readonly probePaths: (opts: RootProbeOptions) => readonly string[];
|
|
563
|
+
/** 命中即报 source-location-overridden 的环境变量(只报键名,绝不报值) */
|
|
564
|
+
readonly probeEnvKeys?: readonly string[] | undefined;
|
|
565
|
+
/** 探测期的附加提示(如 chatgpt 的「无自动根」) */
|
|
566
|
+
readonly probeSkips?: ((opts: RootProbeOptions, probed: readonly string[]) => readonly ForeignSkip[]) | undefined;
|
|
567
|
+
readonly read: (opts: SessionSourceHostOptions) => Promise<SessionReadOutcome<TFile>>;
|
|
568
|
+
readonly draftOf: (file: TFile) => SessionDraft | { readonly skip: { readonly code: ForeignSkipCode; readonly detail?: string } };
|
|
569
|
+
readonly workspaceIdPrefix?: string | undefined;
|
|
570
|
+
}
|
|
571
|
+
|
|
572
|
+
/**
|
|
573
|
+
* 把一个会话来源装配成 `ForeignSource`。
|
|
574
|
+
*
|
|
575
|
+
* **detect 只 stat 真值表里的位置**(绝不读内容、绝不枚举会话)—— 界面对 30 个来源各调一次
|
|
576
|
+
* detect,任何一处读全文都会把面板拖慢;真正的枚举发生在 build。
|
|
577
|
+
*
|
|
578
|
+
* platform 的兜底只在**这一处**:`ctx.platform ?? process.platform`(装配层碰运行平台是允许的,
|
|
579
|
+
* 真值表函数本身一律显式收参)。
|
|
580
|
+
*/
|
|
581
|
+
export function sessionSourceOf<TFile extends { id: string }>(
|
|
582
|
+
wiring: SessionSourceWiring<TFile>,
|
|
583
|
+
): ForeignSource {
|
|
584
|
+
return {
|
|
585
|
+
id: wiring.id,
|
|
586
|
+
labelKey: foreignSourceLabelKey(wiring.id),
|
|
587
|
+
evidence: wiring.evidence,
|
|
588
|
+
probePaths: wiring.probePaths,
|
|
589
|
+
async detect(ctx) {
|
|
590
|
+
const opts: RootProbeOptions = {
|
|
591
|
+
homeDir: ctx.homeDir,
|
|
592
|
+
env: ctx.env,
|
|
593
|
+
platform: ctx.platform ?? process.platform,
|
|
594
|
+
...(ctx.projectDir !== undefined ? { projectDir: ctx.projectDir } : {}),
|
|
595
|
+
};
|
|
596
|
+
const probed = await probeConfiguredPaths(wiring.probePaths(opts), ctx.homeDir);
|
|
597
|
+
const skipped: ForeignSkip[] = [...probed.skipped];
|
|
598
|
+
for (const key of wiring.probeEnvKeys ?? []) {
|
|
599
|
+
if (envValue(ctx.env, key) !== undefined) skipped.push({ code: 'source-location-overridden', origin: key });
|
|
600
|
+
}
|
|
601
|
+
skipped.push(...(wiring.probeSkips?.(opts, probed.paths) ?? []));
|
|
602
|
+
return { found: probed.paths.length > 0, paths: [...probed.paths], skipped };
|
|
603
|
+
},
|
|
604
|
+
async build(ctx) {
|
|
605
|
+
const read = await wiring.read({
|
|
606
|
+
homeDir: ctx.homeDir,
|
|
607
|
+
env: ctx.env,
|
|
608
|
+
platform: ctx.platform ?? process.platform,
|
|
609
|
+
...(ctx.projectDir !== undefined ? { projectDir: ctx.projectDir } : {}),
|
|
610
|
+
targetFormatVersion: ctx.targetSessionFormatVersion,
|
|
611
|
+
// t36:上下文里的可选上限**原样**透传(不再固定只传 5 个字段);不给就一个键都不加
|
|
612
|
+
...(ctx.limits !== undefined ? { limits: ctx.limits } : {}),
|
|
613
|
+
});
|
|
614
|
+
return buildSessionSourceResult<TFile>(wiring.id, {
|
|
615
|
+
files: read.files,
|
|
616
|
+
targetFormatVersion: ctx.targetSessionFormatVersion,
|
|
617
|
+
workspaceIdPrefix: wiring.workspaceIdPrefix ?? wiring.id,
|
|
618
|
+
draftOf: wiring.draftOf,
|
|
619
|
+
readFindings: read.readFindings,
|
|
620
|
+
extraSkips: read.extraSkips,
|
|
621
|
+
extraCounts: read.extraCounts,
|
|
622
|
+
});
|
|
623
|
+
},
|
|
624
|
+
};
|
|
625
|
+
}
|