dsh-deepseek-import 0.1.2 → 0.2.1
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 +230 -230
- package/README.zh.md +277 -264
- package/cordis.patch.yml +4 -0
- package/dsh-import.cmd +65 -65
- package/dsh-import.mjs +2 -1
- package/icon.svg +6 -0
- package/index.js +105 -0
- package/locale/en.json +6 -0
- package/locale/zh.json +6 -0
- package/package.json +25 -2
- package/repair-session.mjs +219 -219
package/repair-session.mjs
CHANGED
|
@@ -1,220 +1,220 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
/**
|
|
3
|
-
* repair-session.mjs — 给导入的会话补上 v4 要求的「受保护 system 头」
|
|
4
|
-
*
|
|
5
|
-
* 背景:DSH 会话格式 v4 要求 surface 的第 0 个节点是 system/message。
|
|
6
|
-
* 早期的导入器没写这一条,会话能打开,但一旦继续对话、运行时追加自己的
|
|
7
|
-
* system/message,就会报 `system/message requires a protected first surface head`。
|
|
8
|
-
*
|
|
9
|
-
* 用法:
|
|
10
|
-
* node repair-session.mjs --audit # 体检所有会话
|
|
11
|
-
* node repair-session.mjs <session-dir> --dry-run
|
|
12
|
-
* node repair-session.mjs <session-dir>
|
|
13
|
-
* node repair-session.mjs --all [--dry-run] # 批量修所有需要修的
|
|
14
|
-
*/
|
|
15
|
-
|
|
16
|
-
import fs from 'node:fs';
|
|
17
|
-
import os from 'node:os';
|
|
18
|
-
import path from 'node:path';
|
|
19
|
-
import zlib from 'node:zlib';
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* repair-session.mjs — 给导入的会话补上 v4 要求的「受保护 system 头」
|
|
4
|
+
*
|
|
5
|
+
* 背景:DSH 会话格式 v4 要求 surface 的第 0 个节点是 system/message。
|
|
6
|
+
* 早期的导入器没写这一条,会话能打开,但一旦继续对话、运行时追加自己的
|
|
7
|
+
* system/message,就会报 `system/message requires a protected first surface head`。
|
|
8
|
+
*
|
|
9
|
+
* 用法:
|
|
10
|
+
* node repair-session.mjs --audit # 体检所有会话
|
|
11
|
+
* node repair-session.mjs <session-dir> --dry-run
|
|
12
|
+
* node repair-session.mjs <session-dir>
|
|
13
|
+
* node repair-session.mjs --all [--dry-run] # 批量修所有需要修的
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
import fs from 'node:fs';
|
|
17
|
+
import os from 'node:os';
|
|
18
|
+
import path from 'node:path';
|
|
19
|
+
import zlib from 'node:zlib';
|
|
20
20
|
import crypto from 'node:crypto';
|
|
21
|
-
import { fileURLToPath } from 'node:url';
|
|
22
|
-
|
|
23
|
-
const DSH_HOME = process.env.DSH_HOME || path.join(os.homedir(), '.dsh');
|
|
24
|
-
const SESSIONS = path.join(DSH_HOME, 'sessions');
|
|
25
|
-
const CACHE = path.join(DSH_HOME, 'storages', 'session_projcache', 'sessions');
|
|
26
|
-
|
|
27
|
-
const NOTE =
|
|
28
|
-
'本会话的历史来自 DeepSeek 网页对话的导入,以下消息为用户与助手的原始记录,顺序与原文一致。';
|
|
29
|
-
|
|
30
|
-
const args = process.argv.slice(2);
|
|
31
|
-
const dryRun = args.includes('--dry-run');
|
|
32
|
-
const audit = args.includes('--audit');
|
|
33
|
-
const all = args.includes('--all');
|
|
34
|
-
const targets = args.filter((a) => !a.startsWith('--'));
|
|
35
|
-
|
|
36
|
-
function readFrames(file) {
|
|
37
|
-
const buf = fs.readFileSync(file);
|
|
38
|
-
const magic = Buffer.from([0x28, 0xb5, 0x2f, 0xfd]);
|
|
39
|
-
const offsets = [];
|
|
40
|
-
for (let i = -1; (i = buf.indexOf(magic, i + 1)) !== -1;) offsets.push(i);
|
|
41
|
-
const texts = [];
|
|
42
|
-
for (let k = 0; k < offsets.length; k++) {
|
|
43
|
-
texts.push(zlib.zstdDecompressSync(buf.subarray(offsets[k], offsets[k + 1] ?? buf.length)).toString('utf8'));
|
|
44
|
-
}
|
|
45
|
-
return texts;
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
function writeFrames(file, texts) {
|
|
49
|
-
const encoded = Buffer.concat(texts.map((text) => zlib.zstdCompressSync(Buffer.from(text, 'utf8'))));
|
|
50
|
-
fs.writeFileSync(file, encoded);
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
function sessionFiles() {
|
|
54
|
-
const out = [];
|
|
55
|
-
if (!fs.existsSync(SESSIONS)) return out;
|
|
56
|
-
for (const project of fs.readdirSync(SESSIONS)) {
|
|
57
|
-
const dir = path.join(SESSIONS, project);
|
|
58
|
-
if (!fs.statSync(dir).isDirectory()) continue;
|
|
59
|
-
for (const entry of fs.readdirSync(dir)) {
|
|
60
|
-
if (!entry.startsWith('session-')) continue;
|
|
61
|
-
const file = path.join(dir, entry, 'session.v4.jsonl.zstd');
|
|
62
|
-
if (fs.existsSync(file)) out.push({ id: entry, file });
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
return out;
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
/** 返回 { status, header, events, firstSurfaceIndex, systemIndexes } */
|
|
69
|
-
function inspect(file) {
|
|
70
|
-
const texts = readFrames(file);
|
|
71
|
-
const rows = texts.join('').split('\n').filter(Boolean).map((line) => JSON.parse(line));
|
|
72
|
-
const header = rows[0];
|
|
73
|
-
const events = rows.slice(1);
|
|
74
|
-
const systemIndexes = events.map((e, i) => (e.type === 'system/message' ? i : -1)).filter((i) => i >= 0);
|
|
75
|
-
const surfaceTypes = new Set(['user/message', 'assistant/message', 'system/message', 'tool/result', 'developer/message']);
|
|
76
|
-
const firstSurfaceIndex = events.findIndex((e) => surfaceTypes.has(e.type));
|
|
77
|
-
return { texts, header, events, systemIndexes, firstSurfaceIndex };
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
function makeSystemEvent(turn, step) {
|
|
81
|
-
return {
|
|
82
|
-
type: 'system/message',
|
|
83
|
-
seq: 0,
|
|
84
|
-
time: Date.now(),
|
|
85
|
-
data: {
|
|
86
|
-
turn,
|
|
87
|
-
step,
|
|
88
|
-
message: {
|
|
89
|
-
role: 'system',
|
|
90
|
-
content: [{ type: 'text', text: NOTE }],
|
|
91
|
-
source: { kind: 'system-prompt' },
|
|
92
|
-
id: crypto.randomUUID(),
|
|
93
|
-
},
|
|
94
|
-
},
|
|
95
|
-
surfaceOp: 'append',
|
|
96
|
-
};
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
function planRepair({ events, systemIndexes, firstSurfaceIndex }) {
|
|
100
|
-
if (firstSurfaceIndex < 0) return { action: 'skip', reason: '没有 surface 事件' };
|
|
101
|
-
if (systemIndexes.length && systemIndexes[0] === firstSurfaceIndex) {
|
|
102
|
-
return { action: 'ok' };
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
const copy = events.map((e) => ({ ...e }));
|
|
106
|
-
if (systemIndexes.length) {
|
|
107
|
-
// 已有 system 但在后面:把它挪到 surface 最前面
|
|
108
|
-
const source = copy[systemIndexes[0]];
|
|
109
|
-
copy.splice(systemIndexes[0], 1);
|
|
110
|
-
const target = firstSurfaceIndex;
|
|
111
|
-
copy.splice(target, 0, {
|
|
112
|
-
...source,
|
|
113
|
-
data: { ...source.data, turn: 1, step: 1 },
|
|
114
|
-
surfaceOp: 'append',
|
|
115
|
-
});
|
|
116
|
-
return { action: 'move', events: copy };
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
// 完全没有 system:插到第一个 surface 事件之前
|
|
120
|
-
copy.splice(firstSurfaceIndex, 0, makeSystemEvent(1, 1));
|
|
121
|
-
return { action: 'insert', events: copy };
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
function resequence(events) {
|
|
125
|
-
const mapping = new Map();
|
|
126
|
-
events.forEach((event, index) => mapping.set(event.seq, index));
|
|
127
|
-
const next = events.map((event, index) => ({ ...event, seq: index }));
|
|
128
|
-
// 改写对 seq 的引用(session/title 的 messageSeqs)
|
|
129
|
-
for (const event of next) {
|
|
130
|
-
if (event.type === 'session/title' && Array.isArray(event.data?.messageSeqs)) {
|
|
131
|
-
event.data = {
|
|
132
|
-
...event.data,
|
|
133
|
-
messageSeqs: event.data.messageSeqs.map((seq) => mapping.get(seq) ?? seq),
|
|
134
|
-
};
|
|
135
|
-
}
|
|
136
|
-
}
|
|
137
|
-
return next;
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
function sessionDirOf(file) {
|
|
141
|
-
return path.dirname(file);
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
function repair(file, plan) {
|
|
145
|
-
const { texts, header } = inspect(file);
|
|
146
|
-
const events = resequence(plan.events);
|
|
147
|
-
const lines = [JSON.stringify(header), ...events.map((e) => JSON.stringify(e))];
|
|
148
|
-
writeFrames(file, lines.map((line) => `${line}\n`));
|
|
149
|
-
|
|
150
|
-
const id = path.basename(sessionDirOf(file));
|
|
151
|
-
const cached = path.join(CACHE, `${id}.json`);
|
|
152
|
-
let cacheNote = '';
|
|
153
|
-
if (fs.existsSync(cached)) {
|
|
154
|
-
fs.rmSync(cached, { force: true });
|
|
155
|
-
cacheNote = ',投影缓存已清除';
|
|
156
|
-
}
|
|
157
|
-
return `${plan.action === 'insert' ? '已插入 system 头' : '已把 system 头移到最前'}(${events.length} 条事件${cacheNote})`;
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
export function auditSessions({ dryRun = false, apply = false } = {}) {
|
|
161
|
-
const list = sessionFiles();
|
|
162
|
-
const problems = [];
|
|
163
|
-
for (const item of list) {
|
|
164
|
-
let info;
|
|
165
|
-
try {
|
|
166
|
-
info = inspect(item.file);
|
|
167
|
-
} catch (error) {
|
|
168
|
-
problems.push({ ...item, status: `读取失败:${error.message}` });
|
|
169
|
-
continue;
|
|
170
|
-
}
|
|
171
|
-
const plan = planRepair(info);
|
|
172
|
-
if (plan.action === 'ok') continue;
|
|
173
|
-
problems.push({ ...item, status: plan.action, plan });
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
console.log(`会话总数 ${list.length},需要处理 ${problems.length}`);
|
|
177
|
-
for (const item of problems) {
|
|
178
|
-
console.log(` ${item.id} ${item.status}`);
|
|
179
|
-
if (dryRun || !apply || !item.plan) continue;
|
|
180
|
-
console.log(` → ${repair(item.file, item.plan)}`);
|
|
181
|
-
}
|
|
182
|
-
if (dryRun) console.log('\n[dry-run] 没有修改任何文件');
|
|
183
|
-
return problems;
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
export function repairSessions(paths, { dryRun = false } = {}) {
|
|
187
|
-
for (const target of paths) {
|
|
188
|
-
const dir = target.endsWith('session.v4.jsonl.zstd') ? path.dirname(target) : target;
|
|
189
|
-
const file = path.join(dir, 'session.v4.jsonl.zstd');
|
|
190
|
-
if (!fs.existsSync(file)) {
|
|
191
|
-
console.error(`找不到 ${file}`);
|
|
192
|
-
continue;
|
|
193
|
-
}
|
|
194
|
-
const info = inspect(file);
|
|
195
|
-
const plan = planRepair(info);
|
|
196
|
-
console.log(`${path.basename(dir)}: ${plan.action}`);
|
|
197
|
-
if (plan.action === 'ok' || plan.action === 'skip' || dryRun) continue;
|
|
198
|
-
console.log(' → ' + repair(file, plan));
|
|
199
|
-
}
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
// ---------------------------------------------------------------- 入口
|
|
203
|
-
|
|
204
|
-
const invokedDirectly =
|
|
205
|
-
process.argv[1] && path.resolve(process.argv[1]) === path.resolve(fileURLToPath(import.meta.url));
|
|
206
|
-
|
|
207
|
-
if (invokedDirectly) {
|
|
208
|
-
if (audit || all) {
|
|
209
|
-
auditSessions({ dryRun, apply: all });
|
|
210
|
-
process.exit(0);
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
if (!targets.length) {
|
|
214
|
-
console.error('用法:node repair-session.mjs --audit | <session-dir> [--dry-run] | --all');
|
|
215
|
-
process.exit(1);
|
|
216
|
-
}
|
|
217
|
-
|
|
218
|
-
repairSessions(targets, { dryRun });
|
|
219
|
-
}
|
|
220
|
-
|
|
21
|
+
import { fileURLToPath } from 'node:url';
|
|
22
|
+
|
|
23
|
+
const DSH_HOME = process.env.DSH_HOME || path.join(os.homedir(), '.dsh');
|
|
24
|
+
const SESSIONS = path.join(DSH_HOME, 'sessions');
|
|
25
|
+
const CACHE = path.join(DSH_HOME, 'storages', 'session_projcache', 'sessions');
|
|
26
|
+
|
|
27
|
+
const NOTE =
|
|
28
|
+
'本会话的历史来自 DeepSeek 网页对话的导入,以下消息为用户与助手的原始记录,顺序与原文一致。';
|
|
29
|
+
|
|
30
|
+
const args = process.argv.slice(2);
|
|
31
|
+
const dryRun = args.includes('--dry-run');
|
|
32
|
+
const audit = args.includes('--audit');
|
|
33
|
+
const all = args.includes('--all');
|
|
34
|
+
const targets = args.filter((a) => !a.startsWith('--'));
|
|
35
|
+
|
|
36
|
+
function readFrames(file) {
|
|
37
|
+
const buf = fs.readFileSync(file);
|
|
38
|
+
const magic = Buffer.from([0x28, 0xb5, 0x2f, 0xfd]);
|
|
39
|
+
const offsets = [];
|
|
40
|
+
for (let i = -1; (i = buf.indexOf(magic, i + 1)) !== -1;) offsets.push(i);
|
|
41
|
+
const texts = [];
|
|
42
|
+
for (let k = 0; k < offsets.length; k++) {
|
|
43
|
+
texts.push(zlib.zstdDecompressSync(buf.subarray(offsets[k], offsets[k + 1] ?? buf.length)).toString('utf8'));
|
|
44
|
+
}
|
|
45
|
+
return texts;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function writeFrames(file, texts) {
|
|
49
|
+
const encoded = Buffer.concat(texts.map((text) => zlib.zstdCompressSync(Buffer.from(text, 'utf8'))));
|
|
50
|
+
fs.writeFileSync(file, encoded);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function sessionFiles() {
|
|
54
|
+
const out = [];
|
|
55
|
+
if (!fs.existsSync(SESSIONS)) return out;
|
|
56
|
+
for (const project of fs.readdirSync(SESSIONS)) {
|
|
57
|
+
const dir = path.join(SESSIONS, project);
|
|
58
|
+
if (!fs.statSync(dir).isDirectory()) continue;
|
|
59
|
+
for (const entry of fs.readdirSync(dir)) {
|
|
60
|
+
if (!entry.startsWith('session-')) continue;
|
|
61
|
+
const file = path.join(dir, entry, 'session.v4.jsonl.zstd');
|
|
62
|
+
if (fs.existsSync(file)) out.push({ id: entry, file });
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
return out;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/** 返回 { status, header, events, firstSurfaceIndex, systemIndexes } */
|
|
69
|
+
function inspect(file) {
|
|
70
|
+
const texts = readFrames(file);
|
|
71
|
+
const rows = texts.join('').split('\n').filter(Boolean).map((line) => JSON.parse(line));
|
|
72
|
+
const header = rows[0];
|
|
73
|
+
const events = rows.slice(1);
|
|
74
|
+
const systemIndexes = events.map((e, i) => (e.type === 'system/message' ? i : -1)).filter((i) => i >= 0);
|
|
75
|
+
const surfaceTypes = new Set(['user/message', 'assistant/message', 'system/message', 'tool/result', 'developer/message']);
|
|
76
|
+
const firstSurfaceIndex = events.findIndex((e) => surfaceTypes.has(e.type));
|
|
77
|
+
return { texts, header, events, systemIndexes, firstSurfaceIndex };
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
function makeSystemEvent(turn, step) {
|
|
81
|
+
return {
|
|
82
|
+
type: 'system/message',
|
|
83
|
+
seq: 0,
|
|
84
|
+
time: Date.now(),
|
|
85
|
+
data: {
|
|
86
|
+
turn,
|
|
87
|
+
step,
|
|
88
|
+
message: {
|
|
89
|
+
role: 'system',
|
|
90
|
+
content: [{ type: 'text', text: NOTE }],
|
|
91
|
+
source: { kind: 'system-prompt' },
|
|
92
|
+
id: crypto.randomUUID(),
|
|
93
|
+
},
|
|
94
|
+
},
|
|
95
|
+
surfaceOp: 'append',
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
function planRepair({ events, systemIndexes, firstSurfaceIndex }) {
|
|
100
|
+
if (firstSurfaceIndex < 0) return { action: 'skip', reason: '没有 surface 事件' };
|
|
101
|
+
if (systemIndexes.length && systemIndexes[0] === firstSurfaceIndex) {
|
|
102
|
+
return { action: 'ok' };
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
const copy = events.map((e) => ({ ...e }));
|
|
106
|
+
if (systemIndexes.length) {
|
|
107
|
+
// 已有 system 但在后面:把它挪到 surface 最前面
|
|
108
|
+
const source = copy[systemIndexes[0]];
|
|
109
|
+
copy.splice(systemIndexes[0], 1);
|
|
110
|
+
const target = firstSurfaceIndex;
|
|
111
|
+
copy.splice(target, 0, {
|
|
112
|
+
...source,
|
|
113
|
+
data: { ...source.data, turn: 1, step: 1 },
|
|
114
|
+
surfaceOp: 'append',
|
|
115
|
+
});
|
|
116
|
+
return { action: 'move', events: copy };
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// 完全没有 system:插到第一个 surface 事件之前
|
|
120
|
+
copy.splice(firstSurfaceIndex, 0, makeSystemEvent(1, 1));
|
|
121
|
+
return { action: 'insert', events: copy };
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
function resequence(events) {
|
|
125
|
+
const mapping = new Map();
|
|
126
|
+
events.forEach((event, index) => mapping.set(event.seq, index));
|
|
127
|
+
const next = events.map((event, index) => ({ ...event, seq: index }));
|
|
128
|
+
// 改写对 seq 的引用(session/title 的 messageSeqs)
|
|
129
|
+
for (const event of next) {
|
|
130
|
+
if (event.type === 'session/title' && Array.isArray(event.data?.messageSeqs)) {
|
|
131
|
+
event.data = {
|
|
132
|
+
...event.data,
|
|
133
|
+
messageSeqs: event.data.messageSeqs.map((seq) => mapping.get(seq) ?? seq),
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
return next;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
function sessionDirOf(file) {
|
|
141
|
+
return path.dirname(file);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
function repair(file, plan) {
|
|
145
|
+
const { texts, header } = inspect(file);
|
|
146
|
+
const events = resequence(plan.events);
|
|
147
|
+
const lines = [JSON.stringify(header), ...events.map((e) => JSON.stringify(e))];
|
|
148
|
+
writeFrames(file, lines.map((line) => `${line}\n`));
|
|
149
|
+
|
|
150
|
+
const id = path.basename(sessionDirOf(file));
|
|
151
|
+
const cached = path.join(CACHE, `${id}.json`);
|
|
152
|
+
let cacheNote = '';
|
|
153
|
+
if (fs.existsSync(cached)) {
|
|
154
|
+
fs.rmSync(cached, { force: true });
|
|
155
|
+
cacheNote = ',投影缓存已清除';
|
|
156
|
+
}
|
|
157
|
+
return `${plan.action === 'insert' ? '已插入 system 头' : '已把 system 头移到最前'}(${events.length} 条事件${cacheNote})`;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
export function auditSessions({ dryRun = false, apply = false } = {}) {
|
|
161
|
+
const list = sessionFiles();
|
|
162
|
+
const problems = [];
|
|
163
|
+
for (const item of list) {
|
|
164
|
+
let info;
|
|
165
|
+
try {
|
|
166
|
+
info = inspect(item.file);
|
|
167
|
+
} catch (error) {
|
|
168
|
+
problems.push({ ...item, status: `读取失败:${error.message}` });
|
|
169
|
+
continue;
|
|
170
|
+
}
|
|
171
|
+
const plan = planRepair(info);
|
|
172
|
+
if (plan.action === 'ok') continue;
|
|
173
|
+
problems.push({ ...item, status: plan.action, plan });
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
console.log(`会话总数 ${list.length},需要处理 ${problems.length}`);
|
|
177
|
+
for (const item of problems) {
|
|
178
|
+
console.log(` ${item.id} ${item.status}`);
|
|
179
|
+
if (dryRun || !apply || !item.plan) continue;
|
|
180
|
+
console.log(` → ${repair(item.file, item.plan)}`);
|
|
181
|
+
}
|
|
182
|
+
if (dryRun) console.log('\n[dry-run] 没有修改任何文件');
|
|
183
|
+
return problems;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
export function repairSessions(paths, { dryRun = false } = {}) {
|
|
187
|
+
for (const target of paths) {
|
|
188
|
+
const dir = target.endsWith('session.v4.jsonl.zstd') ? path.dirname(target) : target;
|
|
189
|
+
const file = path.join(dir, 'session.v4.jsonl.zstd');
|
|
190
|
+
if (!fs.existsSync(file)) {
|
|
191
|
+
console.error(`找不到 ${file}`);
|
|
192
|
+
continue;
|
|
193
|
+
}
|
|
194
|
+
const info = inspect(file);
|
|
195
|
+
const plan = planRepair(info);
|
|
196
|
+
console.log(`${path.basename(dir)}: ${plan.action}`);
|
|
197
|
+
if (plan.action === 'ok' || plan.action === 'skip' || dryRun) continue;
|
|
198
|
+
console.log(' → ' + repair(file, plan));
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
// ---------------------------------------------------------------- 入口
|
|
203
|
+
|
|
204
|
+
const invokedDirectly =
|
|
205
|
+
process.argv[1] && path.resolve(process.argv[1]) === path.resolve(fileURLToPath(import.meta.url));
|
|
206
|
+
|
|
207
|
+
if (invokedDirectly) {
|
|
208
|
+
if (audit || all) {
|
|
209
|
+
auditSessions({ dryRun, apply: all });
|
|
210
|
+
process.exit(0);
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
if (!targets.length) {
|
|
214
|
+
console.error('用法:node repair-session.mjs --audit | <session-dir> [--dry-run] | --all');
|
|
215
|
+
process.exit(1);
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
repairSessions(targets, { dryRun });
|
|
219
|
+
}
|
|
220
|
+
|