dsh-deepseek-import 0.1.1 → 0.1.2
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 +3 -1
- package/README.zh.md +3 -1
- package/dsh-import.mjs +1 -1
- package/edge-extension/content.js +22 -2
- package/edge-extension/merge.js +18 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -54,7 +54,7 @@ Double-click **`dsh-import.cmd`** in the workspace (or drop JSON files onto it).
|
|
|
54
54
|
|
|
55
55
|
- Lists your DSH workspaces; pick one by number
|
|
56
56
|
- Each conversation becomes one session: title, order and body (including code blocks and tables) are preserved
|
|
57
|
-
- **Attachments** (images, txt, pdf, docx…)
|
|
57
|
+
- **Attachments** (images, txt, pdf, docx…): saved under `<workspace>/.dsh-imported/<sessionId>/` with their original names **only when the data source carries them**; anything that cannot be downloaded degrades to `[附件未下载:name (size) 原链接]`. DeepSeek's IndexedDB cache copy usually carries no attachments
|
|
58
58
|
- **Thinking** goes to `thinking.md` in the same folder (never injected into the context)
|
|
59
59
|
- **Edited / regenerated versions**: only the selected branch stays in the body, all versions go to `revisions.md`
|
|
60
60
|
- **Interrupted outputs** keep only a numbered placeholder (`[中断输出 #27:未产生完整正文…]`); add `--keep-interrupted` to keep the partial text
|
|
@@ -188,6 +188,8 @@ Open the conversation and click **Capture chat**; then check the "来源" (sourc
|
|
|
188
188
|
|
|
189
189
|
## Known limitations
|
|
190
190
|
|
|
191
|
+
- **Citations and attachments depend on the data source**: DeepSeek's IndexedDB cache copy keeps the body and thinking only; the `references` table and `files` field are usually absent. `references.md` and the attachment folder are therefore created **only when the source carries them** — their absence is a normal result, not a failure.
|
|
192
|
+
|
|
191
193
|
- **Thinking is archived, not inlined**: DSH reasoning blocks carry provider signatures; faking them would poison later requests. Read `thinking.md`, or force inline with `--thinking`.
|
|
192
194
|
- **Attachments are not added to DSH's attachment store**: they are written to disk and referenced by path. When no download URL is available only the name and size survive.
|
|
193
195
|
- **Web-search citations**: the cache record has no `references` table, so `[reference:N]` markers stay as plain text with no links.
|
package/README.zh.md
CHANGED
|
@@ -57,7 +57,7 @@ DeepSeek 的长对话是**分页加载**的,每次只能看到一屏。扩展
|
|
|
57
57
|
|
|
58
58
|
- 会列出 DSH 里的工作区,选序号决定导入到哪个工作区
|
|
59
59
|
- 每个对话重建为一个会话,标题、顺序、正文(含代码块/表格等 Markdown)原样保留
|
|
60
|
-
- **附件**(图片、txt、pdf、docx
|
|
60
|
+
- **附件**(图片、txt、pdf、docx…):**仅当数据源带附件时**才落盘到 `<工作区>/.dsh-imported/<sessionId>/`,保留原文件名;下载不到时退化成 `[附件未下载:文件名(大小) 原链接]`,由你本机匹配。DeepSeek 的 IndexedDB 缓存副本通常不含附件
|
|
61
61
|
- **思考过程**写进同目录的 `thinking.md`(旁挂存档,不进入对话上下文)
|
|
62
62
|
- **编辑/重新生成留下的多个版本**:正文只保留当前分支,全部版本写进同目录的 `revisions.md`
|
|
63
63
|
- 改动 `workspace.json` 前自动备份为 `workspace.json.bak-<时间>`
|
|
@@ -225,6 +225,8 @@ node check-sessions.mjs
|
|
|
225
225
|
|
|
226
226
|
## 已知限制
|
|
227
227
|
|
|
228
|
+
- **引用与附件取决于数据源**:DeepSeek 的 IndexedDB 缓存副本只保留正文和思考,`references` 表与 `files` 字段通常不在里面。因此 `references.md` 和附件目录**只在数据源带这两类数据时才会生成**,没有生成是正常结果,不是失败。想找回图片只能回原网页另存。
|
|
229
|
+
|
|
228
230
|
- **思考过程旁挂**:写进 `thinking.md`,不进上下文。DSH 的 reasoning 块带 provider 签名,硬塞正文会污染后续请求;想看时直接翻文件,或加 `--thinking` 强制内联。
|
|
229
231
|
- **附件不进 DSH 附件库**:DSH 的图片是内容寻址附件(`~/.dsh/attachments/v1/objects/...`),伪造引用有损坏历史的风险;这里改为落盘 + 路径引用,模型可以用工具读。拿不到下载地址时只留文件名与大小。
|
|
230
232
|
- **正文完整性**:历史接口 `/api/v0/chat/history_messages` 返回的正文可能偏短;回答生成时的 `/api/v0/chat/completion` 是 `text/event-stream` 补丁流(`THINK` 片段=思考,其余片段=正文),扩展会边读边拼,生成完即写入捕获。所以**新回答**是完整的;很久以前的老回答以历史接口返回的内容为准。
|
package/dsh-import.mjs
CHANGED
|
@@ -424,7 +424,7 @@ function sidecarDir(workspacePath, sessionId) {
|
|
|
424
424
|
const ignore = path.join(root, '.gitignore');
|
|
425
425
|
if (!fs.existsSync(ignore)) {
|
|
426
426
|
try {
|
|
427
|
-
fs.writeFileSync(ignore, '# DSH 导入存档(思考 /
|
|
427
|
+
fs.writeFileSync(ignore, '# DSH 导入存档(思考 / 全文 / 多版本;数据源带引用或附件时另存)\n*\n');
|
|
428
428
|
} catch {
|
|
429
429
|
/* 没有写权限就算了 */
|
|
430
430
|
}
|
|
@@ -437,11 +437,31 @@
|
|
|
437
437
|
try {
|
|
438
438
|
const cached = await readCachedHistory();
|
|
439
439
|
if (cached.ok && cached.messages?.length) {
|
|
440
|
-
|
|
440
|
+
// 缓存里没有附件(DeepSeek 不把 files 写进 IndexedDB)。
|
|
441
|
+
// 当前屏幕上已渲染的消息能拿到 <img>,按「角色+正文完全相同」贴回去,
|
|
442
|
+
// 只做并集、不新增消息,避免重复。
|
|
443
|
+
let withFiles = cached.messages;
|
|
444
|
+
try {
|
|
445
|
+
const domMessages = domExtract().messages || [];
|
|
446
|
+
const byKey = new Map(withFiles.map((m) => [`${m.role}|${String(m.content || '').trim()}`, m]));
|
|
447
|
+
let attached = 0;
|
|
448
|
+
for (const domMessage of domMessages) {
|
|
449
|
+
if (!(domMessage.files || []).length) continue;
|
|
450
|
+
const target = byKey.get(`${domMessage.role}|${String(domMessage.content || '').trim()}`);
|
|
451
|
+
if (!target) continue;
|
|
452
|
+
target.files = [...(target.files || []), ...domMessage.files];
|
|
453
|
+
attached += domMessage.files.length;
|
|
454
|
+
}
|
|
455
|
+
if (attached) withFiles = [...withFiles];
|
|
456
|
+
} catch {
|
|
457
|
+
/* DOM 读不到就维持原样 */
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
pushCapture(withFiles);
|
|
441
461
|
return {
|
|
442
462
|
ok: true,
|
|
443
463
|
source: 'cache',
|
|
444
|
-
messages:
|
|
464
|
+
messages: withFiles,
|
|
445
465
|
rounds: 0,
|
|
446
466
|
captures: 1,
|
|
447
467
|
delta: 0,
|
package/edge-extension/merge.js
CHANGED
|
@@ -16,8 +16,25 @@
|
|
|
16
16
|
return (message?.content || '').length + (message?.thinking || '').length;
|
|
17
17
|
}
|
|
18
18
|
|
|
19
|
+
/** 同一消息的两份副本:正文取更全的,附件取并集 */
|
|
20
|
+
function unionFiles(a, b) {
|
|
21
|
+
const merged = [];
|
|
22
|
+
const seen = new Set();
|
|
23
|
+
for (const file of [...(a || []), ...(b || [])]) {
|
|
24
|
+
const key = file?.dataUrl ? String(file.dataUrl).slice(0, 64) : file?.url || file?.name;
|
|
25
|
+
if (!key || seen.has(key)) continue;
|
|
26
|
+
seen.add(key);
|
|
27
|
+
merged.push(file);
|
|
28
|
+
}
|
|
29
|
+
return merged;
|
|
30
|
+
}
|
|
31
|
+
|
|
19
32
|
function richer(a, b) {
|
|
20
|
-
|
|
33
|
+
if (!a) return b;
|
|
34
|
+
if (!b) return a;
|
|
35
|
+
const winner = score(b) > score(a) ? b : a;
|
|
36
|
+
const files = unionFiles(a.files, b.files);
|
|
37
|
+
return files.length ? { ...winner, files } : winner;
|
|
21
38
|
}
|
|
22
39
|
|
|
23
40
|
function keyOf(message) {
|