dsh-config-manager 0.1.78 → 0.1.80
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/lib/adapters/credentials.d.ts +6 -2
- package/lib/adapters/credentials.js +19 -3
- package/lib/adapters/file-collection.js +2 -1
- package/lib/adapters/mcp.d.ts +48 -2
- package/lib/adapters/mcp.js +265 -3
- package/lib/adapters/messages.d.ts +3 -0
- package/lib/adapters/messages.js +7 -0
- package/lib/adapters/plugin-files.js +2 -1
- package/lib/adapters/plugins.js +105 -20
- package/lib/adapters/pnpm-workspace.d.ts +32 -0
- package/lib/adapters/pnpm-workspace.js +113 -2
- package/lib/adapters/prompts.d.ts +6 -2
- package/lib/adapters/prompts.js +17 -2
- package/lib/adapters/providers.d.ts +19 -2
- package/lib/adapters/providers.js +59 -3
- package/lib/adapters/settings.d.ts +6 -2
- package/lib/adapters/settings.js +13 -2
- package/lib/adapters/skills.d.ts +25 -1
- package/lib/adapters/skills.js +60 -0
- package/lib/adapters/ui.d.ts +6 -2
- package/lib/adapters/ui.js +13 -2
- package/lib/adapters/units.d.ts +18 -0
- package/lib/adapters/units.js +39 -0
- package/lib/adapters/workspaces.js +2 -1
- package/lib/client.d.ts +401 -112
- package/lib/client.js +2899 -1525
- package/lib/core/backup.js +20 -0
- package/lib/core/mcp-connector-store.d.ts +159 -0
- package/lib/core/mcp-connector-store.js +445 -0
- package/lib/core/messages-core.d.ts +2 -0
- package/lib/core/messages-core.js +4 -0
- package/lib/core/messages.d.ts +20 -0
- package/lib/core/messages.js +44 -0
- package/lib/core/phase3-host.js +18 -1
- package/lib/core/plugin-cli.d.ts +22 -2
- package/lib/core/plugin-cli.js +22 -3
- package/lib/core/recovery-orchestrator.d.ts +3 -1
- package/lib/core/recovery-orchestrator.js +75 -16
- package/lib/core/rollback-activity.d.ts +89 -0
- package/lib/core/rollback-activity.js +350 -0
- package/lib/core/rollback-ledger.d.ts +0 -9
- package/lib/core/rollback-ledger.js +37 -0
- package/lib/core/rollback-merge.d.ts +22 -2
- package/lib/core/rollback-merge.js +63 -13
- package/lib/core/rollback-point.d.ts +93 -0
- package/lib/core/rollback-point.js +420 -101
- package/lib/core/rollback-undo-journal.d.ts +123 -0
- package/lib/core/rollback-undo-journal.js +517 -0
- package/lib/core/self-update.js +2 -4
- package/lib/core/text-fidelity.d.ts +89 -0
- package/lib/core/text-fidelity.js +176 -0
- package/lib/core/types.d.ts +12 -3
- package/lib/core/update-check.js +2 -4
- package/lib/core/validator.d.ts +9 -0
- package/lib/core/validator.js +12 -0
- package/lib/foreign/bundle.js +42 -3
- package/lib/foreign/claude-code.js +358 -26
- package/lib/foreign/claude-sessions.d.ts +51 -8
- package/lib/foreign/claude-sessions.js +111 -13
- package/lib/foreign/read-claude-code.d.ts +33 -4
- package/lib/foreign/read-claude-code.js +446 -65
- package/lib/foreign/read-claude-plugins.d.ts +36 -0
- package/lib/foreign/read-claude-plugins.js +403 -0
- package/lib/foreign/registry.js +25 -2
- package/lib/foreign/truth-table.js +6 -4
- package/lib/foreign/types.d.ts +170 -2
- package/lib/index.d.ts +14 -5
- package/lib/index.js +60 -16
- package/lib/market/git-file-writer.js +7 -1
- package/lib/profiles/dsh-profile-launcher.d.ts +20 -0
- package/lib/profiles/dsh-profile-launcher.js +58 -1
- package/lib/profiles/dsh-profile-shared.d.ts +8 -2
- package/lib/profiles/dsh-profile-shared.js +8 -2
- package/lib/routes/foreign.js +8 -1
- package/lib/routes/index.d.ts +1 -1
- package/lib/routes/kit.d.ts +2 -2
- package/lib/routes/sync.js +173 -9
- package/lib/schema/manifest.d.ts +6 -1
- package/lib/schema/manifest.js +55 -0
- package/lib/schema/types.d.ts +47 -2
- package/lib/sync/ancestor.d.ts +22 -0
- package/lib/sync/ancestor.js +66 -2
- package/lib/sync/autosync-config.d.ts +73 -0
- package/lib/sync/autosync-config.js +89 -0
- package/lib/sync/autosync-scheduler.d.ts +80 -6
- package/lib/sync/autosync-scheduler.js +263 -40
- package/lib/sync/gist/gist-transport.d.ts +18 -1
- package/lib/sync/gist/gist-transport.js +42 -3
- package/lib/sync/gist/messages.d.ts +2 -0
- package/lib/sync/gist/messages.js +5 -0
- package/lib/sync/git/git-transport.d.ts +42 -3
- package/lib/sync/git/git-transport.js +146 -3
- package/lib/sync/merge.d.ts +47 -14
- package/lib/sync/merge.js +242 -222
- package/lib/sync/messages.d.ts +8 -0
- package/lib/sync/messages.js +19 -0
- package/lib/sync/risk.d.ts +19 -2
- package/lib/sync/risk.js +10 -3
- package/lib/sync/s3/messages.d.ts +6 -0
- package/lib/sync/s3/messages.js +13 -0
- package/lib/sync/s3/s3-transport.d.ts +9 -1
- package/lib/sync/s3/s3-transport.js +78 -6
- package/lib/sync/s3/sigv4.js +11 -2
- package/lib/sync/sync-engine.d.ts +212 -8
- package/lib/sync/sync-engine.js +514 -58
- package/lib/sync/sync-history.d.ts +11 -0
- package/lib/sync/sync-history.js +20 -0
- package/lib/sync/sync-state.d.ts +43 -0
- package/lib/sync/sync-state.js +44 -0
- package/lib/sync/sync-watcher.d.ts +76 -0
- package/lib/sync/sync-watcher.js +202 -0
- package/lib/sync/text-merge.d.ts +85 -0
- package/lib/sync/text-merge.js +513 -0
- package/lib/sync/transport.d.ts +48 -0
- package/lib/sync/transport.js +12 -0
- package/lib/sync/watch-scope.d.ts +38 -0
- package/lib/sync/watch-scope.js +95 -0
- package/lib/sync/webdav/webdav-transport.d.ts +16 -2
- package/lib/sync/webdav/webdav-transport.js +128 -11
- package/lib/ui/foreign-view.js +10 -0
- package/lib/ui/i18n.d.ts +9 -0
- package/lib/ui/i18n.js +22 -0
- package/lib/ui/rollback-view.d.ts +12 -0
- package/lib/ui/rollback-view.js +2 -0
- package/lib/ui/sync-settings-view.d.ts +47 -0
- package/lib/ui/sync-settings-view.js +24 -0
- package/lib/ui/types.d.ts +21 -0
- package/lib/utils/env-lock.d.ts +1 -0
- package/lib/utils/env-lock.js +7 -4
- package/package.json +6 -6
- package/src/adapters/credentials.ts +22 -4
- package/src/adapters/file-collection.ts +2 -1
- package/src/adapters/mcp.test.ts +235 -1
- package/src/adapters/mcp.ts +276 -5
- package/src/adapters/messages.ts +7 -0
- package/src/adapters/plugin-files.ts +2 -1
- package/src/adapters/plugins.test.ts +176 -2
- package/src/adapters/plugins.ts +107 -21
- package/src/adapters/pnpm-workspace.test.ts +89 -1
- package/src/adapters/pnpm-workspace.ts +127 -2
- package/src/adapters/prompts.ts +20 -3
- package/src/adapters/providers.ts +62 -4
- package/src/adapters/settings.ts +16 -3
- package/src/adapters/skills.test.ts +141 -0
- package/src/adapters/skills.ts +58 -2
- package/src/adapters/ui.ts +16 -3
- package/src/adapters/units-entries.test.ts +190 -0
- package/src/adapters/units.ts +50 -0
- package/src/adapters/workspaces.ts +2 -1
- package/src/client/about/PluginUpdateDialog.tsx +4 -2
- package/src/client/about/ReleaseNotesDialog.tsx +4 -2
- package/src/client/chat/RollbackReviewDialog.tsx +3 -2
- package/src/client/common/Skeleton.tsx +32 -0
- package/src/client/common/api-base.test.ts +36 -0
- package/src/client/common/http.test.ts +12 -1
- package/src/client/common/http.ts +8 -2
- package/src/client/common/routes.ts +46 -3
- package/src/client/config-manager.module.css +321 -11
- package/src/client/home/HomePanel.tsx +14 -6
- package/src/client/import/ForeignImportView.tsx +3 -1
- package/src/client/import/import-wizard-steps.tsx +2 -1
- package/src/client/library/ArtifactRow.tsx +12 -2
- package/src/client/library/LibraryActions.tsx +6 -3
- package/src/client/library/LibraryPanel.tsx +21 -3
- package/src/client/library/RestorePlanView.tsx +4 -2
- package/src/client/locales.ts +3 -0
- package/src/client/market/market-api.test.ts +3 -2
- package/src/client/recovery/RecoveryPanel.tsx +54 -7
- package/src/client/recovery/RollbackCard.tsx +7 -2
- package/src/client/recovery/RollbackDiffPane.tsx +3 -2
- package/src/client/recovery/recovery-locales.ts +20 -2
- package/src/client/recovery/recovery-view.test.ts +39 -1
- package/src/client/recovery/recovery-view.ts +23 -1
- package/src/client/run-store.test.ts +1 -1
- package/src/client/run-store.ts +4 -0
- package/src/client/sync/AutosyncCard.tsx +49 -6
- package/src/client/sync/MergeModal.tsx +220 -0
- package/src/client/sync/MergePreviewView.tsx +306 -0
- package/src/client/sync/SyncActivityBar.tsx +31 -0
- package/src/client/sync/SyncConfirmView.tsx +2 -1
- package/src/client/sync/SyncPanel.tsx +130 -87
- package/src/client/sync/SyncProbeRow.tsx +162 -0
- package/src/client/sync/SyncSettingsView.test.ts +27 -2
- package/src/client/sync/WorkDirCard.tsx +44 -6
- package/src/client/sync/history-model.ts +4 -1
- package/src/client/sync/merge-view.test.ts +248 -0
- package/src/client/sync/merge-view.ts +398 -0
- package/src/client/sync/probe-view.test.ts +90 -0
- package/src/client/sync/probe-view.ts +63 -0
- package/src/client/sync/remote-device-view.test.ts +35 -0
- package/src/client/sync/remote-device-view.ts +25 -0
- package/src/client/sync/sync-api.test.ts +5 -3
- package/src/client/sync/sync-api.ts +212 -2
- package/src/client/sync/sync-entry.test.ts +78 -0
- package/src/client/sync/sync-entry.ts +53 -0
- package/src/client/sync/sync-locales.ts +234 -2
- package/src/client/sync/sync-view.test.ts +18 -1
- package/src/client/sync/sync-view.ts +20 -0
- package/src/core/backup.test.ts +41 -0
- package/src/core/backup.ts +20 -0
- package/src/core/mcp-connector-store.test.ts +265 -0
- package/src/core/mcp-connector-store.ts +516 -0
- package/src/core/messages-core.ts +4 -0
- package/src/core/messages.ts +46 -0
- package/src/core/phase3-host.ts +18 -1
- package/src/core/phase3-production-integration.test.ts +4 -0
- package/src/core/plugin-cli.test.ts +26 -0
- package/src/core/plugin-cli.ts +30 -4
- package/src/core/recovery-orchestrator.test.ts +104 -5
- package/src/core/recovery-orchestrator.ts +74 -17
- package/src/core/rollback-activity.test.ts +311 -0
- package/src/core/rollback-activity.ts +367 -0
- package/src/core/rollback-cross-session.test.ts +694 -0
- package/src/core/rollback-interrupted.test.ts +137 -0
- package/src/core/rollback-ledger.ts +32 -0
- package/src/core/rollback-merge.test.ts +130 -0
- package/src/core/rollback-merge.ts +78 -14
- package/src/core/rollback-point.ts +468 -91
- package/src/core/rollback-undo-journal.test.ts +508 -0
- package/src/core/rollback-undo-journal.ts +571 -0
- package/src/core/self-update.ts +2 -5
- package/src/core/text-fidelity.test.ts +189 -0
- package/src/core/text-fidelity.ts +197 -0
- package/src/core/types.ts +11 -3
- package/src/core/update-check.ts +2 -5
- package/src/core/validator.ts +14 -0
- package/src/foreign/bundle-origin.test.ts +265 -0
- package/src/foreign/bundle.ts +45 -5
- package/src/foreign/claude-code.test.ts +268 -0
- package/src/foreign/claude-code.ts +406 -25
- package/src/foreign/claude-plugins.test.ts +234 -0
- package/src/foreign/claude-sessions.test.ts +141 -0
- package/src/foreign/claude-sessions.ts +179 -23
- package/src/foreign/file-budget.test.ts +4 -1
- package/src/foreign/read-claude-code.test.ts +320 -0
- package/src/foreign/read-claude-code.ts +490 -71
- package/src/foreign/read-claude-plugins.ts +465 -0
- package/src/foreign/registry.ts +25 -2
- package/src/foreign/session-ir.test.ts +65 -0
- package/src/foreign/truth-table.test.ts +81 -0
- package/src/foreign/truth-table.ts +6 -3
- package/src/foreign/types.ts +162 -2
- package/src/index.facade.test.ts +26 -4
- package/src/index.ts +76 -19
- package/src/market/git-file-writer.test.ts +4 -0
- package/src/market/git-file-writer.ts +7 -1
- package/src/profiles/dsh-profile-launcher.test.ts +68 -3
- package/src/profiles/dsh-profile-launcher.ts +56 -1
- package/src/profiles/dsh-profile-shared.ts +8 -2
- package/src/routes/foreign.ts +8 -1
- package/src/routes/index.ts +1 -1
- package/src/routes/kit.ts +2 -2
- package/src/routes/sync.ts +176 -9
- package/src/schema/manifest-schema.test.ts +87 -2
- package/src/schema/manifest.ts +57 -1
- package/src/schema/types.ts +45 -2
- package/src/sync/ancestor.ts +76 -2
- package/src/sync/autosync-config.test.ts +73 -1
- package/src/sync/autosync-config.ts +124 -0
- package/src/sync/autosync-risk-tier.test.ts +38 -2
- package/src/sync/autosync-scheduler.test.ts +264 -12
- package/src/sync/autosync-scheduler.ts +296 -44
- package/src/sync/gist/gist-conditional-write.test.ts +203 -0
- package/src/sync/gist/gist-transport.ts +53 -5
- package/src/sync/gist/messages.ts +5 -0
- package/src/sync/git/git-probe.test.ts +80 -0
- package/src/sync/git/git-transport.test.ts +185 -0
- package/src/sync/git/git-transport.ts +178 -5
- package/src/sync/merge.test.ts +188 -55
- package/src/sync/merge.ts +282 -224
- package/src/sync/messages.ts +19 -0
- package/src/sync/risk.ts +29 -5
- package/src/sync/s3/messages.ts +13 -0
- package/src/sync/s3/s3-conditional-write.test.ts +330 -0
- package/src/sync/s3/s3-transport.test.ts +2 -1
- package/src/sync/s3/s3-transport.ts +87 -9
- package/src/sync/s3/sigv4.test.ts +4 -1
- package/src/sync/s3/sigv4.ts +11 -2
- package/src/sync/sync-device-identity.test.ts +267 -0
- package/src/sync/sync-engine.test.ts +410 -19
- package/src/sync/sync-engine.ts +602 -57
- package/src/sync/sync-history.test.ts +16 -1
- package/src/sync/sync-history.ts +26 -0
- package/src/sync/sync-state.test.ts +64 -0
- package/src/sync/sync-state.ts +79 -0
- package/src/sync/sync-watcher.test.ts +197 -0
- package/src/sync/sync-watcher.ts +246 -0
- package/src/sync/text-merge.test.ts +358 -0
- package/src/sync/text-merge.ts +554 -0
- package/src/sync/transport.ts +57 -0
- package/src/sync/watch-scope.test.ts +122 -0
- package/src/sync/watch-scope.ts +120 -0
- package/src/sync/webdav/webdav-conditional-write.test.ts +360 -0
- package/src/sync/webdav/webdav-transport.ts +137 -14
- package/src/ui/foreign-view.test.ts +34 -0
- package/src/ui/foreign-view.ts +10 -0
- package/src/ui/i18n.ts +22 -0
- package/src/ui/rollback-view.ts +14 -0
- package/src/ui/sync-settings-view.test.ts +28 -1
- package/src/ui/sync-settings-view.ts +43 -0
- package/src/ui/types.ts +22 -0
- package/src/utils/env-lock.ts +8 -5
|
@@ -1,46 +1,283 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Claude Code(~/.claude)→ DSH bundle 分区的**纯**翻译层(v1
|
|
2
|
+
* Claude Code(~/.claude)→ DSH bundle 分区的**纯**翻译层(v1;2026-10-18 补齐项目级 / 插件 / 诊断)。
|
|
3
3
|
*
|
|
4
4
|
* 重构后本文件只保留 Claude 特有的部分,公共口径全部走共享内核(kernel.ts):
|
|
5
5
|
* - 路径/名字安全、SKILL.md frontmatter 校验、MCP 映射与凭据剥离、skill 装配、会话归集
|
|
6
6
|
* → 内核(Hermes 等其它来源共用同一份实现,口径不可能分叉);
|
|
7
|
-
* -
|
|
8
|
-
*
|
|
7
|
+
* - 本文件只剩:指令合并、~/.claude.json 与项目/插件三处 MCP 的装配、settings 的只读诊断、
|
|
8
|
+
* commands / agents / hooks / plugins 的如实报码、以及溯源块(origin)。
|
|
9
9
|
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
10
|
+
* **既有行为逐字保留**(claude-code.test.ts 的 t0..t7 一行未改):全局 CLAUDE.md 单来源时
|
|
11
|
+
* 原样落一份 AGENTS.md(不报 instructions-merged);mcp / skills / sessions / workspaces 的
|
|
12
|
+
* 装配口径不变;跳过码只增不改。
|
|
13
|
+
*
|
|
14
|
+
* 本层**零 fs**:项目根、插件清单、settings、rules 全都是读盘层已经读好的纯数据(见
|
|
15
|
+
* read-claude-code.ts / read-claude-plugins.ts),因此全部可在单测里构造。
|
|
12
16
|
*
|
|
13
|
-
* v1 范围:mcp / skills / agentInstructions / sessions+workspaces。
|
|
14
17
|
* 明确不搬、并**可见地**记进 skipped(绝不静默):
|
|
15
|
-
* - settings.json 的 hooks
|
|
16
|
-
* - settings.json 的
|
|
17
|
-
* -
|
|
18
|
-
* -
|
|
18
|
+
* - settings(.local).json 的 hooks(事件名 + 条目数明细,DSH 无对等结构)
|
|
19
|
+
* - settings(.local).json 的 permissions(allow / ask / deny 只数条数,值一律不读)
|
|
20
|
+
* - settings(.local).json 其余顶层键(只记键名,值一律不读)
|
|
21
|
+
* - settings 的 env(凭据:只报名字与条数,值不读)
|
|
22
|
+
* - commands/*.md、agents/*.md(与 DSH 的 prompts / subagent 不是同一语义)
|
|
23
|
+
* - 带 frontmatter 的 .claude/rules/*.md(作用域合并会丢语义,只报告、不进合并正文)
|
|
24
|
+
* - 插件本体(hooks / commands / agents 的执行面与运行时 API,**绝不复制其代码**)
|
|
25
|
+
* - ~/.claude.json 的 projects / sessions / history 中的机器状态(与源机路径强绑定)
|
|
19
26
|
*/
|
|
20
27
|
import { defaultSecretScanner } from '../core/exporter.js';
|
|
21
28
|
import { isRecord } from '../utils/guards.js';
|
|
22
29
|
import { transcodeClaudeSession } from './claude-sessions.js';
|
|
23
|
-
import { collectSessionSections, collectSkills, instructionsSection, mcpSectionFromEntries, serverEntriesOf, } from './kernel.js';
|
|
30
|
+
import { collectSessionSections, collectSkills, instructionsSection, mcpSectionFromEntries, serverEntriesOf, splitFrontmatter, } from './kernel.js';
|
|
24
31
|
/* 公共判据/工具**从共享内核再导出**:v1 的既有 import 路径与名字保持不变(外部只 import 这一层) */
|
|
25
32
|
export { frontmatterProblem, isSafeRelPath, isSafeUnitName, mcpEntryOf, splitFrontmatter, stripUserInfo, } from './kernel.js';
|
|
26
|
-
|
|
27
|
-
|
|
33
|
+
const NL = String.fromCharCode(10);
|
|
34
|
+
/** settings 未迁移键名在 detail 里的上限(超出只记 count,绝不把整份键名铺开) */
|
|
35
|
+
const MAX_SETTINGS_KEY_NAMES = 20;
|
|
36
|
+
/** hooks 明细:事件名 + **该事件的条目数**(只看数组长度,绝不读值) */
|
|
37
|
+
function hookDetailOf(hooks) {
|
|
38
|
+
const parts = [];
|
|
39
|
+
for (const [event, value] of Object.entries(hooks)) {
|
|
40
|
+
parts.push(event + '=' + String(Array.isArray(value) ? value.length : 0));
|
|
41
|
+
}
|
|
42
|
+
return parts.join(',');
|
|
43
|
+
}
|
|
44
|
+
/** hooks 条目总数(数组长度之和;非数组按 0,不猜) */
|
|
45
|
+
function hookEntriesOf(hooks) {
|
|
46
|
+
let total = 0;
|
|
47
|
+
for (const value of Object.values(hooks)) {
|
|
48
|
+
if (Array.isArray(value))
|
|
49
|
+
total += value.length;
|
|
50
|
+
}
|
|
51
|
+
return total;
|
|
52
|
+
}
|
|
53
|
+
/** permissions 明细:只数 allow / ask / deny 的条数,值一律不读 */
|
|
54
|
+
function permissionDetailOf(permissions) {
|
|
55
|
+
const countOf = (key) => {
|
|
56
|
+
const value = permissions[key];
|
|
57
|
+
return Array.isArray(value) ? value.length : 0;
|
|
58
|
+
};
|
|
59
|
+
return 'allow=' + String(countOf('allow')) + ',ask=' + String(countOf('ask')) + ',deny=' + String(countOf('deny'));
|
|
60
|
+
}
|
|
61
|
+
/** permissions 规则总条数(allow + ask + deny) */
|
|
62
|
+
function permissionEntriesOf(permissions) {
|
|
63
|
+
let total = 0;
|
|
64
|
+
for (const key of ['allow', 'ask', 'deny']) {
|
|
65
|
+
const value = permissions[key];
|
|
66
|
+
if (Array.isArray(value))
|
|
67
|
+
total += value.length;
|
|
68
|
+
}
|
|
69
|
+
return total;
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* settings.json / settings.local.json 的只读诊断。
|
|
73
|
+
*
|
|
74
|
+
* 只报码、绝不静默,也**绝不读值**:
|
|
75
|
+
* - hooks → unsupported-hooks(detail = 事件名=条目数,count = 事件数)
|
|
76
|
+
* - permissions 对象存在 → unsupported-permissions(detail = allow/ask/deny 条数)
|
|
77
|
+
* - 其余顶层键 → unsupported-settings-keys(detail = 键名逗号拼接,上限 20;超出记 count)
|
|
78
|
+
* - env 里的凭据名 → credentials-not-migrated(只记名,值不读)
|
|
79
|
+
*/
|
|
80
|
+
function collectSettingsFindings(settings, scope, skipped, refs, tally) {
|
|
28
81
|
if (!isRecord(settings))
|
|
29
82
|
return;
|
|
83
|
+
const tag = (skip) => {
|
|
84
|
+
const out = { code: skip.code };
|
|
85
|
+
if (skip.origin !== undefined)
|
|
86
|
+
out.origin = skip.origin;
|
|
87
|
+
if (skip.detail !== undefined)
|
|
88
|
+
out.detail = skip.detail;
|
|
89
|
+
if (skip.count !== undefined)
|
|
90
|
+
out.count = skip.count;
|
|
91
|
+
if (scope.origin !== undefined)
|
|
92
|
+
out.origin = scope.origin;
|
|
93
|
+
if (scope.detailPrefix !== undefined && out.detail !== undefined) {
|
|
94
|
+
out.detail = scope.detailPrefix + ' ' + out.detail;
|
|
95
|
+
}
|
|
96
|
+
return out;
|
|
97
|
+
};
|
|
30
98
|
const hooks = settings['hooks'];
|
|
31
99
|
if (isRecord(hooks) && Object.keys(hooks).length > 0) {
|
|
32
|
-
|
|
100
|
+
tally.hooks += hookEntriesOf(hooks);
|
|
101
|
+
skipped.push(tag({ code: 'unsupported-hooks', detail: hookDetailOf(hooks), count: Object.keys(hooks).length }));
|
|
102
|
+
}
|
|
103
|
+
const permissions = settings['permissions'];
|
|
104
|
+
if (isRecord(permissions)) {
|
|
105
|
+
tally.permissions += permissionEntriesOf(permissions);
|
|
106
|
+
skipped.push(tag({ code: 'unsupported-permissions', detail: permissionDetailOf(permissions) }));
|
|
33
107
|
}
|
|
34
108
|
const env = settings['env'];
|
|
35
109
|
if (isRecord(env) && Object.keys(env).length > 0) {
|
|
36
110
|
// 只问「哪些名字像凭据」——值一律不读、不落盘、不回传
|
|
37
111
|
const { hits } = defaultSecretScanner().scanAndRedact(env);
|
|
38
112
|
if (hits.length > 0) {
|
|
39
|
-
skipped.push({ code: 'credentials-not-migrated', count: hits.length });
|
|
40
|
-
for (const
|
|
41
|
-
refs.push('
|
|
113
|
+
skipped.push(tag({ code: 'credentials-not-migrated', count: hits.length }));
|
|
114
|
+
for (const hit of hits)
|
|
115
|
+
refs.push(scope.refPrefix + ':' + hit.field);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
const otherKeys = Object.keys(settings).filter((key) => key !== 'hooks' && key !== 'env' && key !== 'permissions');
|
|
119
|
+
if (otherKeys.length > 0) {
|
|
120
|
+
const names = otherKeys.slice(0, MAX_SETTINGS_KEY_NAMES).join(',');
|
|
121
|
+
skipped.push(tag(otherKeys.length > MAX_SETTINGS_KEY_NAMES
|
|
122
|
+
? { code: 'unsupported-settings-keys', detail: names, count: otherKeys.length }
|
|
123
|
+
: { code: 'unsupported-settings-keys', detail: names }));
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
/* ---------------- MCP 取值(三处载体形态各不相同,取值只有这一份实现) ---------------- */
|
|
127
|
+
/** 「名字 → 定义」映射的条目(非映射 → 空清单);项目级 projectMcp 本身就是这个映射 */
|
|
128
|
+
function mappingEntries(value) {
|
|
129
|
+
if (!isRecord(value))
|
|
130
|
+
return [];
|
|
131
|
+
return Object.entries(value);
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* `.mcp.json` 的条目:Claude Code 标准形态是 `{ mcpServers: {…} }`;
|
|
135
|
+
* 插件目录里也见过裸映射 `{ "<server>": {…} }`(read-claude-plugins 的实测布局)。
|
|
136
|
+
* 有 mcpServers 键就**只认它**(即使为空),否则才按裸映射读 —— 绝不把 "mcpServers" 当成 server 名。
|
|
137
|
+
*/
|
|
138
|
+
function mcpFileEntries(value) {
|
|
139
|
+
if (!isRecord(value))
|
|
140
|
+
return [];
|
|
141
|
+
if (Object.prototype.hasOwnProperty.call(value, 'mcpServers'))
|
|
142
|
+
return serverEntriesOf(value, 'mcpServers');
|
|
143
|
+
return Object.entries(value);
|
|
144
|
+
}
|
|
145
|
+
/**
|
|
146
|
+
* 指令来源按**固定顺序**收集:全局 CLAUDE.md → 每个项目的 CLAUDE.md / CLAUDE.local.md →
|
|
147
|
+
* 该项目不带 frontmatter 的 rules/*.md。带 frontmatter 的 rule **不进正文**,只报
|
|
148
|
+
* unsupported-rules(它的作用域语义在 DSH 没有承载位,合并进去等于悄悄扩大生效范围)。
|
|
149
|
+
*/
|
|
150
|
+
function instructionSourcesOf(input, projects, skipped) {
|
|
151
|
+
const out = [];
|
|
152
|
+
if (typeof input.memory === 'string' && input.memory.trim() !== '') {
|
|
153
|
+
out.push({ label: 'claude-md', text: input.memory });
|
|
154
|
+
}
|
|
155
|
+
for (let i = 0; i < projects.length; i++) {
|
|
156
|
+
const project = projects[i];
|
|
157
|
+
if (project === undefined)
|
|
158
|
+
continue;
|
|
159
|
+
const n = i + 1;
|
|
160
|
+
if (typeof project.memory === 'string' && project.memory.trim() !== '') {
|
|
161
|
+
out.push({ label: 'project:' + String(n), text: project.memory });
|
|
162
|
+
}
|
|
163
|
+
if (typeof project.memoryLocal === 'string' && project.memoryLocal.trim() !== '') {
|
|
164
|
+
out.push({ label: 'project-local:' + String(n), text: project.memoryLocal });
|
|
165
|
+
}
|
|
166
|
+
for (const rule of project.rules ?? []) {
|
|
167
|
+
if (splitFrontmatter(rule.text) !== null) {
|
|
168
|
+
skipped.push({ code: 'unsupported-rules', origin: rule.name });
|
|
169
|
+
continue;
|
|
170
|
+
}
|
|
171
|
+
if (rule.text.trim() !== '') {
|
|
172
|
+
out.push({ label: 'rule:' + String(n) + ':' + rule.name, text: rule.text });
|
|
173
|
+
}
|
|
42
174
|
}
|
|
43
175
|
}
|
|
176
|
+
return out;
|
|
177
|
+
}
|
|
178
|
+
/**
|
|
179
|
+
* 单一来源 → 原文逐字返回(既有行为),**不报码**;多来源 → 合并并报
|
|
180
|
+
* instructions-merged(detail = 来源标签串,如 "claude-md+project:1")。
|
|
181
|
+
*/
|
|
182
|
+
function mergeInstructions(sources, skipped) {
|
|
183
|
+
const first = sources[0];
|
|
184
|
+
if (first === undefined)
|
|
185
|
+
return null;
|
|
186
|
+
if (sources.length === 1)
|
|
187
|
+
return first.text;
|
|
188
|
+
skipped.push({ code: 'instructions-merged', detail: sources.map((s) => s.label).join('+') });
|
|
189
|
+
return sources.map((s) => s.text.trimEnd()).join(NL + NL) + NL;
|
|
190
|
+
}
|
|
191
|
+
/* ---------------- 溯源块(进 bundle 的 manifest.foreign) ---------------- */
|
|
192
|
+
/**
|
|
193
|
+
* 全局位置的标签前缀:位置被 CLAUDE_CONFIG_DIR 覆盖时用真实配置目录(绝对路径)——
|
|
194
|
+
* 它是「用户自己机器的来源位置」,只进溯源元数据,**绝不进任何分区载荷**;未覆盖时用 .claude。
|
|
195
|
+
*/
|
|
196
|
+
function globalLocationBase(input) {
|
|
197
|
+
if (input.configDirOverridden === true && typeof input.configDir === 'string' && input.configDir.trim() !== '') {
|
|
198
|
+
return input.configDir;
|
|
199
|
+
}
|
|
200
|
+
return '.claude';
|
|
201
|
+
}
|
|
202
|
+
/** skipped → {code, count} 聚合(按首次出现顺序;同码的 count 相加) */
|
|
203
|
+
function aggregateSkipped(skipped) {
|
|
204
|
+
const byCode = new Map();
|
|
205
|
+
for (const skip of skipped) {
|
|
206
|
+
byCode.set(skip.code, (byCode.get(skip.code) ?? 0) + (skip.count ?? 1));
|
|
207
|
+
}
|
|
208
|
+
return [...byCode.entries()].map(([code, count]) => ({ code, count }));
|
|
209
|
+
}
|
|
210
|
+
/**
|
|
211
|
+
* 装配溯源块。三条纪律:只放标识与计数;绝对路径只允许出现在 projects[](配置目录覆盖时
|
|
212
|
+
* 的 locations 也是用户自己机器的来源位置);输入没给的字段**省略**(缺字段 = 不知道)。
|
|
213
|
+
* 一个信号都没有时返回 undefined(旧行为:不写 origin)。
|
|
214
|
+
*/
|
|
215
|
+
function buildOrigin(input, projects, plugins, skipped, credentialRefs) {
|
|
216
|
+
const locations = [];
|
|
217
|
+
if (input.claudeJson !== undefined)
|
|
218
|
+
locations.push('.claude.json');
|
|
219
|
+
const base = globalLocationBase(input);
|
|
220
|
+
const globalBits = [];
|
|
221
|
+
if (input.settings !== undefined)
|
|
222
|
+
globalBits.push(base + '/settings.json');
|
|
223
|
+
if (typeof input.memory === 'string' && input.memory.trim() !== '')
|
|
224
|
+
globalBits.push(base + '/CLAUDE.md');
|
|
225
|
+
if ((input.skills?.length ?? 0) > 0)
|
|
226
|
+
globalBits.push(base + '/skills');
|
|
227
|
+
if ((input.commandCount ?? 0) > 0)
|
|
228
|
+
globalBits.push(base + '/commands');
|
|
229
|
+
if ((input.sessions?.length ?? 0) > 0)
|
|
230
|
+
globalBits.push(base + '/projects');
|
|
231
|
+
if ((input.marketplaces?.length ?? 0) > 0)
|
|
232
|
+
globalBits.push(base + '/plugins/known_marketplaces.json');
|
|
233
|
+
if (plugins.length > 0)
|
|
234
|
+
globalBits.push(base + '/plugins');
|
|
235
|
+
if (globalBits.length > 0) {
|
|
236
|
+
locations.push(base);
|
|
237
|
+
locations.push(...globalBits);
|
|
238
|
+
}
|
|
239
|
+
const roots = [];
|
|
240
|
+
for (const project of projects) {
|
|
241
|
+
if (project.root.trim() !== '')
|
|
242
|
+
roots.push(project.root);
|
|
243
|
+
}
|
|
244
|
+
const pluginMetas = [];
|
|
245
|
+
for (const plugin of plugins) {
|
|
246
|
+
const meta = { name: plugin.name };
|
|
247
|
+
if (plugin.version !== undefined)
|
|
248
|
+
meta.version = plugin.version;
|
|
249
|
+
if (plugin.marketplace !== undefined)
|
|
250
|
+
meta.marketplace = plugin.marketplace;
|
|
251
|
+
if (plugin.repo !== undefined)
|
|
252
|
+
meta.repo = plugin.repo;
|
|
253
|
+
if (plugin.ref !== undefined)
|
|
254
|
+
meta.ref = plugin.ref;
|
|
255
|
+
if (plugin.components.length > 0)
|
|
256
|
+
meta.components = [...plugin.components];
|
|
257
|
+
pluginMetas.push(meta);
|
|
258
|
+
}
|
|
259
|
+
const skipSummary = aggregateSkipped(skipped);
|
|
260
|
+
const hasSignal = locations.length > 0
|
|
261
|
+
|| roots.length > 0
|
|
262
|
+
|| pluginMetas.length > 0
|
|
263
|
+
|| (input.sourceVersion !== undefined && input.sourceVersion !== '')
|
|
264
|
+
|| skipSummary.length > 0
|
|
265
|
+
|| credentialRefs.length > 0;
|
|
266
|
+
if (!hasSignal)
|
|
267
|
+
return undefined;
|
|
268
|
+
const origin = {
|
|
269
|
+
source: 'claude-code',
|
|
270
|
+
locations,
|
|
271
|
+
skipped: skipSummary,
|
|
272
|
+
credentialRefs: [...credentialRefs],
|
|
273
|
+
};
|
|
274
|
+
if (input.sourceVersion !== undefined && input.sourceVersion !== '')
|
|
275
|
+
origin.sourceVersion = input.sourceVersion;
|
|
276
|
+
if (roots.length > 0)
|
|
277
|
+
origin.projects = roots;
|
|
278
|
+
if (pluginMetas.length > 0)
|
|
279
|
+
origin.plugins = pluginMetas;
|
|
280
|
+
return origin;
|
|
44
281
|
}
|
|
45
282
|
/* ---------------- 入口 ---------------- */
|
|
46
283
|
export function convertClaudeCode(input) {
|
|
@@ -48,21 +285,56 @@ export function convertClaudeCode(input) {
|
|
|
48
285
|
const sections = [];
|
|
49
286
|
const credentialRefs = [];
|
|
50
287
|
const counts = {};
|
|
51
|
-
const
|
|
288
|
+
const projects = input.projects ?? [];
|
|
289
|
+
const plugins = input.plugins ?? [];
|
|
290
|
+
const tally = { hooks: 0, permissions: 0 };
|
|
291
|
+
/* ---- MCP:全局 ~/.claude.json ∪ 项目 .mcp.json ∪ 项目 projectMcp ∪ 插件 .mcp.json ---- */
|
|
292
|
+
// 同名先到先得:第一条定义进包,其余只报 mcp-server-id-conflict(origin = server 名)
|
|
293
|
+
const mcpEntries = [];
|
|
294
|
+
const seenMcp = new Set();
|
|
295
|
+
const addMcpEntries = (entries) => {
|
|
296
|
+
for (const entry of entries) {
|
|
297
|
+
if (seenMcp.has(entry[0])) {
|
|
298
|
+
skipped.push({ code: 'mcp-server-id-conflict', origin: entry[0] });
|
|
299
|
+
continue;
|
|
300
|
+
}
|
|
301
|
+
seenMcp.add(entry[0]);
|
|
302
|
+
mcpEntries.push([entry[0], entry[1]]);
|
|
303
|
+
}
|
|
304
|
+
};
|
|
305
|
+
addMcpEntries(serverEntriesOf(input.claudeJson, 'mcpServers'));
|
|
306
|
+
for (const project of projects) {
|
|
307
|
+
addMcpEntries(mcpFileEntries(project.mcpJson));
|
|
308
|
+
addMcpEntries(mappingEntries(project.projectMcp));
|
|
309
|
+
}
|
|
310
|
+
for (const plugin of plugins)
|
|
311
|
+
addMcpEntries(mcpFileEntries(plugin.mcpJson));
|
|
312
|
+
const mcp = mcpSectionFromEntries(mcpEntries, credentialRefs, skipped);
|
|
52
313
|
if (mcp !== null) {
|
|
53
314
|
sections.push({ sectionId: 'mcp', data: mcp });
|
|
54
315
|
counts['mcp.servers'] = mcp.servers.length;
|
|
55
316
|
}
|
|
317
|
+
/* ---- skills:全局 ∪ 项目级 ∪ 插件(压平一层;同名先到先得由内核统一判) ---- */
|
|
318
|
+
const skillUnits = [
|
|
319
|
+
...(input.skills ?? []),
|
|
320
|
+
...projects.flatMap((project) => project.skills ?? []),
|
|
321
|
+
...plugins.flatMap((plugin) => plugin.skills ?? []),
|
|
322
|
+
];
|
|
56
323
|
const skillFiles = [];
|
|
57
|
-
collectSkills(
|
|
324
|
+
collectSkills(skillUnits, skillFiles, skipped);
|
|
58
325
|
if (skillFiles.length > 0) {
|
|
59
326
|
sections.push({ sectionId: 'skills', files: skillFiles });
|
|
60
327
|
counts['skills.files'] = skillFiles.length;
|
|
61
328
|
}
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
329
|
+
/* ---- 指令合并:全局 + 项目 memory/memoryLocal + 无 frontmatter 的 rules ---- */
|
|
330
|
+
const instructionSources = instructionSourcesOf(input, projects, skipped);
|
|
331
|
+
const mergedInstructions = mergeInstructions(instructionSources, skipped);
|
|
332
|
+
if (mergedInstructions !== null) {
|
|
333
|
+
const instructions = instructionsSection(mergedInstructions);
|
|
334
|
+
if (instructions !== null) {
|
|
335
|
+
sections.push(instructions);
|
|
336
|
+
counts['agentInstructions.files'] = 1;
|
|
337
|
+
}
|
|
66
338
|
}
|
|
67
339
|
// 内核在 formatVersion === undefined 时**一条都不转**并整批报码;这里的 -1 是不可达分支的兜底
|
|
68
340
|
// (转码器会把它判成 session-format-unsupported),刻意不写 0/3 之类的"最近版本",避免猜。
|
|
@@ -70,16 +342,76 @@ export function convertClaudeCode(input) {
|
|
|
70
342
|
collectSessionSections({
|
|
71
343
|
files: input.sessions ?? [],
|
|
72
344
|
targetFormatVersion: input.targetSessionFormatVersion,
|
|
73
|
-
|
|
345
|
+
// TranscodeResult.findings 是**逐会话**的如实报码(session-id-mismatch 等)。
|
|
346
|
+
// 内核只读 result.skip 与 session.info.ignored,**不读 findings** ⇒ 必须在这里并入,
|
|
347
|
+
// 否则这条码永远到不了用户面前(审计:findings 是一个「写好了但没接线」的字段)。
|
|
348
|
+
transcode: (file) => {
|
|
349
|
+
const result = transcodeClaudeSession(file, { formatVersion });
|
|
350
|
+
for (const finding of result.findings)
|
|
351
|
+
skipped.push(finding);
|
|
352
|
+
return result;
|
|
353
|
+
},
|
|
74
354
|
workspaceIdPrefix: 'claude-code',
|
|
75
355
|
sections,
|
|
76
356
|
skipped,
|
|
77
357
|
counts,
|
|
78
358
|
});
|
|
79
|
-
|
|
359
|
+
/* ---- settings 诊断:全局 + 项目级(settings / settingsLocal)同一套判据 ---- */
|
|
360
|
+
collectSettingsFindings(input.settings, { refPrefix: 'settings.env' }, skipped, credentialRefs, tally);
|
|
361
|
+
for (let i = 0; i < projects.length; i++) {
|
|
362
|
+
const project = projects[i];
|
|
363
|
+
if (project === undefined)
|
|
364
|
+
continue;
|
|
365
|
+
const n = String(i + 1);
|
|
366
|
+
collectSettingsFindings(project.settings, { origin: 'project:' + n + '/settings.json', detailPrefix: 'project:' + n, refPrefix: 'project:' + n + ':settings.env' }, skipped, credentialRefs, tally);
|
|
367
|
+
collectSettingsFindings(project.settingsLocal, { origin: 'project:' + n + '/settings.local.json', detailPrefix: 'project:' + n, refPrefix: 'project:' + n + ':settings.local.env' }, skipped, credentialRefs, tally);
|
|
368
|
+
const projectCommands = project.commandCount ?? 0;
|
|
369
|
+
if (projectCommands > 0)
|
|
370
|
+
skipped.push({ code: 'unsupported-commands', origin: 'project:' + n, count: projectCommands });
|
|
371
|
+
for (const agent of project.agents ?? [])
|
|
372
|
+
skipped.push({ code: 'unsupported-agents', origin: agent.name });
|
|
373
|
+
for (const finding of project.readFindings ?? [])
|
|
374
|
+
skipped.push(finding);
|
|
375
|
+
}
|
|
376
|
+
/* ---- 插件:本体不迁移(一条一码 + 组件明细),能从插件无损复用的只有 skills 与 .mcp.json ---- */
|
|
377
|
+
for (const plugin of plugins) {
|
|
378
|
+
skipped.push({ code: 'unsupported-plugins', origin: plugin.name, detail: plugin.components.join(',') });
|
|
379
|
+
const pluginCommands = plugin.commandCount ?? 0;
|
|
380
|
+
if (pluginCommands > 0)
|
|
381
|
+
skipped.push({ code: 'unsupported-commands', origin: plugin.name, count: pluginCommands });
|
|
382
|
+
const pluginHooks = plugin.hookFiles ?? 0;
|
|
383
|
+
if (pluginHooks > 0)
|
|
384
|
+
skipped.push({ code: 'unsupported-hooks', origin: plugin.name, count: pluginHooks });
|
|
385
|
+
for (const agent of plugin.agents ?? [])
|
|
386
|
+
skipped.push({ code: 'unsupported-agents', origin: agent });
|
|
387
|
+
for (const finding of plugin.readFindings ?? [])
|
|
388
|
+
skipped.push(finding);
|
|
389
|
+
}
|
|
80
390
|
const commandCount = input.commandCount ?? 0;
|
|
81
391
|
if (commandCount > 0)
|
|
82
392
|
skipped.push({ code: 'unsupported-commands', count: commandCount });
|
|
83
|
-
|
|
393
|
+
/* ---- counts:既有键逐字保留;新键只在真的有内容时出现(0 不占位) ---- */
|
|
394
|
+
const agentCount = projects.reduce((total, project) => total + (project.agents?.length ?? 0), 0)
|
|
395
|
+
+ plugins.reduce((total, plugin) => total + (plugin.agents?.length ?? 0), 0);
|
|
396
|
+
const allCommands = commandCount
|
|
397
|
+
+ projects.reduce((total, project) => total + (project.commandCount ?? 0), 0)
|
|
398
|
+
+ plugins.reduce((total, plugin) => total + (plugin.commandCount ?? 0), 0);
|
|
399
|
+
const allHooks = tally.hooks + plugins.reduce((total, plugin) => total + (plugin.hookFiles ?? 0), 0);
|
|
400
|
+
if (projects.length > 0)
|
|
401
|
+
counts['projects'] = projects.length;
|
|
402
|
+
if (plugins.length > 0)
|
|
403
|
+
counts['plugins'] = plugins.length;
|
|
404
|
+
if (agentCount > 0)
|
|
405
|
+
counts['agents'] = agentCount;
|
|
406
|
+
if (allCommands > 0)
|
|
407
|
+
counts['commands'] = allCommands;
|
|
408
|
+
if (allHooks > 0)
|
|
409
|
+
counts['hooks'] = allHooks;
|
|
410
|
+
if (tally.permissions > 0)
|
|
411
|
+
counts['permissions'] = tally.permissions;
|
|
412
|
+
const origin = buildOrigin(input, projects, plugins, skipped, credentialRefs);
|
|
413
|
+
return origin === undefined
|
|
414
|
+
? { source: 'claude-code', sections, skipped, credentialRefs, counts }
|
|
415
|
+
: { source: 'claude-code', sections, skipped, credentialRefs, counts, origin };
|
|
84
416
|
}
|
|
85
417
|
//# sourceMappingURL=claude-code.js.map
|
|
@@ -26,9 +26,23 @@
|
|
|
26
26
|
* 本文件**刻意不出现在 file-budget 的 import 白名单里**:它 import 了 `node:fs` 的**类型**用于
|
|
27
27
|
* 构造只读句柄的回调(`ClaudeReadHandle`,追加能力通道),这是**零运行期 I/O** 的类型依赖。
|
|
28
28
|
* 守卫按运行时性质放行(见 file-budget.test.ts 的白名单说明)。
|
|
29
|
+
*
|
|
30
|
+
* 会话保真修复(2026-10-18,task-4;四条都在**本文件内**收口,共享合成器 session-ir.ts 一字未改):
|
|
31
|
+
* ① **幂等时间**:墙钟回落只有一个确定性来源(`resolveWallClock`:显式 `options.now` → 记录自身
|
|
32
|
+
* 最早时间戳 → `input.createdAtMs`),只有全都缺失才回落 `Date.now()` 并在
|
|
33
|
+
* `info.counts.timestampsFallback` **如实记账**。旧实现 parse 与 synthesize 各调一次
|
|
34
|
+
* `Date.now()` ⇒ 同一来源两次转码字节必然不同 ⇒ DSH 侧「同内容 → Skip」永不成立。
|
|
35
|
+
* ② **isSidechain**:真实 Claude 子代理记录(`type=user|assistant` + `isSidechain:true`)**一律
|
|
36
|
+
* 不进 IR**,整类计报 `isSidechain`(旧实现零命中 → 子代理正文静默混进父对话)。
|
|
37
|
+
* ③ **sessionId 不一致**:`input.recordSessionId` 存在且 ≠ `input.id` → 非致命发现
|
|
38
|
+
* `session-id-mismatch`(会话 id 仍以文件名 stem 为准,见 `TranscodeResult.findings`)。
|
|
39
|
+
* ④ **推理块**(Claude Code 的 `thinking` / `redacted_thinking`):共享 IR 没有 reasoning
|
|
40
|
+
* 承载位(docs/spec/known-gaps.md F-01)→ 计报 `thinking`,**绝不伪装成正文**;非 Claude 原生的
|
|
41
|
+
* `{type:'reasoning',text:…}` 刻意保持既有口径(理由与切换方式见 `REASONING_BLOCK_TYPES`)。
|
|
29
42
|
*/
|
|
30
43
|
import type { Stats } from 'node:fs';
|
|
31
44
|
import type { DshSessionHeader, DshSessionRow, IrParseStats, IrSession } from './session-ir.ts';
|
|
45
|
+
import type { ClaudeSessionInput, ForeignSkip } from './types.ts';
|
|
32
46
|
export type { DshSessionHeader, DshSessionRow } from './session-ir.ts';
|
|
33
47
|
/**
|
|
34
48
|
* 本模块已实现行式的 DSH 会话格式版本(其它版本一律拒绝,不猜)。
|
|
@@ -40,12 +54,14 @@ export type { DshSessionHeader, DshSessionRow } from './session-ir.ts';
|
|
|
40
54
|
* 桌面端 v4 目标下每一条会话都被 `session-format-unsupported` 拒掉,产出的包里没有 sessions 分区)。
|
|
41
55
|
*/
|
|
42
56
|
export declare const SUPPORTED_DSH_SESSION_FORMAT_VERSIONS: readonly number[];
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
}
|
|
57
|
+
/**
|
|
58
|
+
* 待转码的 Claude 会话文件。
|
|
59
|
+
*
|
|
60
|
+
* **等价于 `types.ts` 的 `ClaudeSessionInput`**(2026-10-18 起不再重复声明):读盘层额外给的
|
|
61
|
+
* `recordSessionId`(≠ 文件名 stem 时报 session-id-mismatch)与 `createdAtMs`(确定性 createdAt
|
|
62
|
+
* 回落值,见 `resolveWallClock`)因此天然可达本层,而 `{ id, text }` 的老调用方一字不用改。
|
|
63
|
+
*/
|
|
64
|
+
export type ClaudeSessionFile = ClaudeSessionInput;
|
|
49
65
|
/**
|
|
50
66
|
* **只读**的文件读取面(additive,2026-10):**内部**用它支持「大文件只读头尾」的追加通道。
|
|
51
67
|
*
|
|
@@ -69,8 +85,20 @@ export interface ClaudeSessionInfo {
|
|
|
69
85
|
assistantMessages: number;
|
|
70
86
|
toolCalls: number;
|
|
71
87
|
toolResults: number;
|
|
72
|
-
/**
|
|
88
|
+
/**
|
|
89
|
+
* 未迁移的记录类型 / 内容块 → 条数(attachment / queue-operation / system / isSidechain /
|
|
90
|
+
* thinking / unparsable …)。**每个键在下游都会被翻成 `unsupported-session-record` 的 detail**
|
|
91
|
+
* (kernel.collectSessionSections),所以这里只放「这条内容本版不迁移」这一件事。
|
|
92
|
+
*/
|
|
73
93
|
ignored: Record<string, number>;
|
|
94
|
+
/**
|
|
95
|
+
* 结算类记账(**刻意不进 `ignored`**:那里的键会被翻成 `unsupported-session-record`,语义不符)。
|
|
96
|
+
*
|
|
97
|
+
* 目前只有 `timestampsFallback`(0/1):记录里与 `input.createdAtMs` **都没有**可用时间戳 ⇒
|
|
98
|
+
* 本次转码只能回落墙钟 `Date.now()`,**同一来源两次转码的字节可能不同**(DSH 侧「同内容 → Skip」
|
|
99
|
+
* 因此不成立、重复导入会判 Conflict)。这是必须如实报出来的不确定性,不是错误。
|
|
100
|
+
*/
|
|
101
|
+
counts: Record<string, number>;
|
|
74
102
|
}
|
|
75
103
|
export interface TranscodedSession {
|
|
76
104
|
id: string;
|
|
@@ -83,7 +111,12 @@ export interface TranscodedSession {
|
|
|
83
111
|
export interface TranscodeOptions {
|
|
84
112
|
/** 目标机 DSH 的 SESSION_FORMAT_VERSION(必填) */
|
|
85
113
|
formatVersion: number;
|
|
86
|
-
/**
|
|
114
|
+
/**
|
|
115
|
+
* 墙钟回落的**最高优先级**注入点(测试 / 调用方显式给定)。
|
|
116
|
+
*
|
|
117
|
+
* 缺省时按 `resolveWallClock` 的回落链取:记录自身最早时间戳 → `input.createdAtMs` →
|
|
118
|
+
* `Date.now()`(最后一级会在返回值里记 `timestampsFallback`)。非法值(非安全整数)视同没给。
|
|
119
|
+
*/
|
|
87
120
|
now?: number;
|
|
88
121
|
}
|
|
89
122
|
export interface TranscodeSkip {
|
|
@@ -93,6 +126,16 @@ export interface TranscodeSkip {
|
|
|
93
126
|
export interface TranscodeResult {
|
|
94
127
|
session?: TranscodedSession;
|
|
95
128
|
skip?: TranscodeSkip;
|
|
129
|
+
/**
|
|
130
|
+
* **非致命**发现(会话照样产出):目前只有 `session-id-mismatch`(记录里的 sessionId 与文件名
|
|
131
|
+
* stem 不一致)。会话 id 仍以文件名 stem 为准,只有「不一致」这件事必须可见。
|
|
132
|
+
*
|
|
133
|
+
* 为什么放在结果上而不是 `info.ignored`:`ignored` 的键在下游一律被翻成
|
|
134
|
+
* `unsupported-session-record`(码固定),而本条要用契约里冻结的 `session-id-mismatch`。
|
|
135
|
+
* **调用方负责原样带出**(`skipped.push(...result.findings)`)—— 现有
|
|
136
|
+
* `kernel.collectSessionSections` 尚未读本字段(不在本次改动范围)。空数组 = 没有发现。
|
|
137
|
+
*/
|
|
138
|
+
findings: ForeignSkip[];
|
|
96
139
|
}
|
|
97
140
|
/**
|
|
98
141
|
* Claude 会话原文 → IR(**纯函数,不读盘**)。
|