dsh-reference-anything 0.5.0 → 0.5.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 +11 -5
- package/cordis.patch.yml +2 -0
- package/lib/client.js +2 -2
- package/lib/host.js +4 -1
- package/package.json +1 -1
- package/release-notes/0.4.4/features.json +35 -35
- package/release-notes/0.5.0/features.json +65 -65
- package/release-notes/README.md +38 -36
- package/vendor/chat-import-reader/LICENSE.fzstd +20 -20
- package/vendor/chat-import-reader/availability-worker.mjs +114 -114
- package/vendor/chat-import-reader/parsers.mjs +11168 -11168
- package/vendor/chat-import-reader/scan-worker.mjs +7 -7
- package/vendor/chat-import-reader/scanners.mjs +4286 -4286
- package/vendor/chat-import-reader/upstream-sha256.json +101 -101
|
@@ -1,114 +1,114 @@
|
|
|
1
|
-
import { parentPort } from 'node:worker_threads'
|
|
2
|
-
import { readFile, readdir, stat } from 'node:fs/promises'
|
|
3
|
-
import { join, resolve } from 'node:path'
|
|
4
|
-
import { homedir } from 'node:os'
|
|
5
|
-
import { readConversation } from './parsers.mjs'
|
|
6
|
-
const channel = parentPort || { on: (...args) => process.on(...args), postMessage: value => process.send(value) }
|
|
7
|
-
if (!parentPort) process.on('disconnect', () => process.exit(0))
|
|
8
|
-
|
|
9
|
-
// Parsing stays off the picker thread. Only states and file fingerprints cross back.
|
|
10
|
-
channel.on('message', async ({ id, target }) => {
|
|
11
|
-
const dependencies = new Map()
|
|
12
|
-
const inspect = async path => {
|
|
13
|
-
try {
|
|
14
|
-
const s = await stat(path)
|
|
15
|
-
dependencies.set(path, `${s.size}:${s.mtimeMs}:${s.ctimeMs}`)
|
|
16
|
-
return { type: s.isDirectory() ? 'directory' : 'file', size: s.size, mtimeMs: s.mtimeMs }
|
|
17
|
-
} catch (error) {
|
|
18
|
-
if (error.code !== 'ENOENT') throw error
|
|
19
|
-
dependencies.set(path, 'missing')
|
|
20
|
-
return undefined
|
|
21
|
-
}
|
|
22
|
-
}
|
|
23
|
-
const fs = {
|
|
24
|
-
resolve: async path => ({ displayPath: resolve(path) }),
|
|
25
|
-
processPath: value => value.displayPath,
|
|
26
|
-
stat: async value => inspect(value.displayPath),
|
|
27
|
-
readText: async value => {
|
|
28
|
-
const info = await inspect(value.displayPath)
|
|
29
|
-
// shortcut: large sources stay unknown; use streaming probes before raising this background budget.
|
|
30
|
-
if (info?.size > 8 * 1024 * 1024) throw new Error('Background validation budget exceeded')
|
|
31
|
-
const raw = await readFile(value.displayPath, 'utf8')
|
|
32
|
-
// A tolerant converter may skip broken lines; that must never prove emptiness.
|
|
33
|
-
if (/\.jsonl$/i.test(value.displayPath)) for (const line of raw.split(/\r?\n/)) if (line.trim()) JSON.parse(line)
|
|
34
|
-
return raw
|
|
35
|
-
},
|
|
36
|
-
listDir: async value => {
|
|
37
|
-
await inspect(value.displayPath)
|
|
38
|
-
return (await readdir(value.displayPath, { withFileTypes: true })).map(e => ({
|
|
39
|
-
name: e.name, type: e.isDirectory() ? 'directory' : 'file', target: { displayPath: join(value.displayPath, e.name) },
|
|
40
|
-
}))
|
|
41
|
-
},
|
|
42
|
-
}
|
|
43
|
-
let state = 'unknown'
|
|
44
|
-
try {
|
|
45
|
-
const info = await inspect(target.path)
|
|
46
|
-
if (!info || (info.type === 'file' && info.size === 0)) state = 'empty'
|
|
47
|
-
else if (target.format === 'claude' && /\.json$/i.test(target.path)) {
|
|
48
|
-
const meta = JSON.parse(await fs.readText({ displayPath: target.path }))
|
|
49
|
-
if (meta.cliSessionId && !meta.messages) {
|
|
50
|
-
// Match the exact CLI identity; never substitute another chat in the project.
|
|
51
|
-
const root = join(homedir(), '.claude', 'projects')
|
|
52
|
-
let found = false
|
|
53
|
-
const walk = async dir => {
|
|
54
|
-
await inspect(dir)
|
|
55
|
-
for (const e of await readdir(dir, { withFileTypes: true })) {
|
|
56
|
-
if (e.isDirectory()) await walk(join(dir, e.name))
|
|
57
|
-
else if (e.name === `${meta.cliSessionId}.jsonl`) found = true
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
if (await inspect(root)) await walk(root)
|
|
61
|
-
state = found ? 'unknown' : 'empty'
|
|
62
|
-
}
|
|
63
|
-
} else {
|
|
64
|
-
if (info.type === 'directory' && ['grokbuild', 'vibe', 'kimi'].includes(target.format)) {
|
|
65
|
-
const names = target.format === 'grokbuild' ? ['chat_history.jsonl']
|
|
66
|
-
: target.format === 'vibe' ? ['messages.jsonl'] : ['wire.jsonl', 'agents/main/wire.jsonl']
|
|
67
|
-
const bodies = await Promise.all(names.map(name => inspect(join(target.path, name))))
|
|
68
|
-
if (bodies.every(body => !body || body.size === 0)) {
|
|
69
|
-
channel.postMessage({ id, state: 'empty', dependencies: [...dependencies] })
|
|
70
|
-
return
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
if (/\.(?:db|vscdb)$/i.test(target.path)) {
|
|
74
|
-
await inspect(target.path + '-wal')
|
|
75
|
-
await inspect(target.path + '-shm')
|
|
76
|
-
const schemas = {
|
|
77
|
-
opencode: ['session', 'message'], mimocode: ['session', 'message'], kilocode: ['session', 'message'],
|
|
78
|
-
teleagent: ['session', 'message'], zcode: ['session', 'message'],
|
|
79
|
-
hermes: ['sessions', 'messages'], goose: ['sessions', 'messages'], crush: ['sessions', 'messages'],
|
|
80
|
-
}
|
|
81
|
-
let schema = schemas[target.format]
|
|
82
|
-
if (schema) {
|
|
83
|
-
const { DatabaseSync } = await import('node:sqlite')
|
|
84
|
-
const db = new DatabaseSync(target.path, { readOnly: true })
|
|
85
|
-
try {
|
|
86
|
-
const tables = new Set(db.prepare("SELECT name FROM sqlite_master WHERE type='table'").all().map(r => r.name))
|
|
87
|
-
if (schema[0] === 'session' && tables.has('session_v2')) schema = ['session_v2', 'session_message']
|
|
88
|
-
if (tables.has(schema[0]) && tables.has(schema[1])
|
|
89
|
-
&& db.prepare(`SELECT 1 FROM ${schema[0]} WHERE id = ? LIMIT 1`).get(target.sessionId)
|
|
90
|
-
&& !db.prepare(`SELECT 1 FROM ${schema[1]} WHERE session_id = ? LIMIT 1`).get(target.sessionId)) {
|
|
91
|
-
channel.postMessage({ id, state: 'empty', dependencies: [...dependencies] })
|
|
92
|
-
return
|
|
93
|
-
}
|
|
94
|
-
} finally { db.close() }
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
|
-
if (target.format === 'chatgpt') {
|
|
98
|
-
const exported = JSON.parse(await fs.readText({ displayPath: target.path }))
|
|
99
|
-
const chat = Array.isArray(exported) ? exported.find(c => c.id === target.sessionId) : null
|
|
100
|
-
if (chat?.mapping && Object.values(chat.mapping).every(node => node && !node.message)) {
|
|
101
|
-
channel.postMessage({ id, state: 'empty', dependencies: [...dependencies] })
|
|
102
|
-
return
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
state = await readConversation(fs, { ...target, limit: 1, detail: 'full', probe: true })
|
|
106
|
-
if (state === 'unknown' && ['pi', 'codex', 'openclaw', 'hermes', 'dsh', 'dsh4'].includes(target.format) && info.type === 'file' && !/\.(db|vscdb|zstd)$/i.test(target.path)) {
|
|
107
|
-
const records = (await fs.readText({ displayPath: target.path })).split(/\r?\n/).filter(line => line.trim()).map(line => JSON.parse(line))
|
|
108
|
-
const allowed = target.format === 'codex' ? ['session_meta'] : ['session', 'session_info', 'session/title']
|
|
109
|
-
if (records.length && records.every(r => allowed.includes(r.type))) state = 'empty'
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
|
-
} catch { state = 'unknown' }
|
|
113
|
-
channel.postMessage({ id, state, dependencies: [...dependencies] })
|
|
114
|
-
})
|
|
1
|
+
import { parentPort } from 'node:worker_threads'
|
|
2
|
+
import { readFile, readdir, stat } from 'node:fs/promises'
|
|
3
|
+
import { join, resolve } from 'node:path'
|
|
4
|
+
import { homedir } from 'node:os'
|
|
5
|
+
import { readConversation } from './parsers.mjs'
|
|
6
|
+
const channel = parentPort || { on: (...args) => process.on(...args), postMessage: value => process.send(value) }
|
|
7
|
+
if (!parentPort) process.on('disconnect', () => process.exit(0))
|
|
8
|
+
|
|
9
|
+
// Parsing stays off the picker thread. Only states and file fingerprints cross back.
|
|
10
|
+
channel.on('message', async ({ id, target }) => {
|
|
11
|
+
const dependencies = new Map()
|
|
12
|
+
const inspect = async path => {
|
|
13
|
+
try {
|
|
14
|
+
const s = await stat(path)
|
|
15
|
+
dependencies.set(path, `${s.size}:${s.mtimeMs}:${s.ctimeMs}`)
|
|
16
|
+
return { type: s.isDirectory() ? 'directory' : 'file', size: s.size, mtimeMs: s.mtimeMs }
|
|
17
|
+
} catch (error) {
|
|
18
|
+
if (error.code !== 'ENOENT') throw error
|
|
19
|
+
dependencies.set(path, 'missing')
|
|
20
|
+
return undefined
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
const fs = {
|
|
24
|
+
resolve: async path => ({ displayPath: resolve(path) }),
|
|
25
|
+
processPath: value => value.displayPath,
|
|
26
|
+
stat: async value => inspect(value.displayPath),
|
|
27
|
+
readText: async value => {
|
|
28
|
+
const info = await inspect(value.displayPath)
|
|
29
|
+
// shortcut: large sources stay unknown; use streaming probes before raising this background budget.
|
|
30
|
+
if (info?.size > 8 * 1024 * 1024) throw new Error('Background validation budget exceeded')
|
|
31
|
+
const raw = await readFile(value.displayPath, 'utf8')
|
|
32
|
+
// A tolerant converter may skip broken lines; that must never prove emptiness.
|
|
33
|
+
if (/\.jsonl$/i.test(value.displayPath)) for (const line of raw.split(/\r?\n/)) if (line.trim()) JSON.parse(line)
|
|
34
|
+
return raw
|
|
35
|
+
},
|
|
36
|
+
listDir: async value => {
|
|
37
|
+
await inspect(value.displayPath)
|
|
38
|
+
return (await readdir(value.displayPath, { withFileTypes: true })).map(e => ({
|
|
39
|
+
name: e.name, type: e.isDirectory() ? 'directory' : 'file', target: { displayPath: join(value.displayPath, e.name) },
|
|
40
|
+
}))
|
|
41
|
+
},
|
|
42
|
+
}
|
|
43
|
+
let state = 'unknown'
|
|
44
|
+
try {
|
|
45
|
+
const info = await inspect(target.path)
|
|
46
|
+
if (!info || (info.type === 'file' && info.size === 0)) state = 'empty'
|
|
47
|
+
else if (target.format === 'claude' && /\.json$/i.test(target.path)) {
|
|
48
|
+
const meta = JSON.parse(await fs.readText({ displayPath: target.path }))
|
|
49
|
+
if (meta.cliSessionId && !meta.messages) {
|
|
50
|
+
// Match the exact CLI identity; never substitute another chat in the project.
|
|
51
|
+
const root = join(homedir(), '.claude', 'projects')
|
|
52
|
+
let found = false
|
|
53
|
+
const walk = async dir => {
|
|
54
|
+
await inspect(dir)
|
|
55
|
+
for (const e of await readdir(dir, { withFileTypes: true })) {
|
|
56
|
+
if (e.isDirectory()) await walk(join(dir, e.name))
|
|
57
|
+
else if (e.name === `${meta.cliSessionId}.jsonl`) found = true
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
if (await inspect(root)) await walk(root)
|
|
61
|
+
state = found ? 'unknown' : 'empty'
|
|
62
|
+
}
|
|
63
|
+
} else {
|
|
64
|
+
if (info.type === 'directory' && ['grokbuild', 'vibe', 'kimi'].includes(target.format)) {
|
|
65
|
+
const names = target.format === 'grokbuild' ? ['chat_history.jsonl']
|
|
66
|
+
: target.format === 'vibe' ? ['messages.jsonl'] : ['wire.jsonl', 'agents/main/wire.jsonl']
|
|
67
|
+
const bodies = await Promise.all(names.map(name => inspect(join(target.path, name))))
|
|
68
|
+
if (bodies.every(body => !body || body.size === 0)) {
|
|
69
|
+
channel.postMessage({ id, state: 'empty', dependencies: [...dependencies] })
|
|
70
|
+
return
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
if (/\.(?:db|vscdb)$/i.test(target.path)) {
|
|
74
|
+
await inspect(target.path + '-wal')
|
|
75
|
+
await inspect(target.path + '-shm')
|
|
76
|
+
const schemas = {
|
|
77
|
+
opencode: ['session', 'message'], mimocode: ['session', 'message'], kilocode: ['session', 'message'],
|
|
78
|
+
teleagent: ['session', 'message'], zcode: ['session', 'message'],
|
|
79
|
+
hermes: ['sessions', 'messages'], goose: ['sessions', 'messages'], crush: ['sessions', 'messages'],
|
|
80
|
+
}
|
|
81
|
+
let schema = schemas[target.format]
|
|
82
|
+
if (schema) {
|
|
83
|
+
const { DatabaseSync } = await import('node:sqlite')
|
|
84
|
+
const db = new DatabaseSync(target.path, { readOnly: true })
|
|
85
|
+
try {
|
|
86
|
+
const tables = new Set(db.prepare("SELECT name FROM sqlite_master WHERE type='table'").all().map(r => r.name))
|
|
87
|
+
if (schema[0] === 'session' && tables.has('session_v2')) schema = ['session_v2', 'session_message']
|
|
88
|
+
if (tables.has(schema[0]) && tables.has(schema[1])
|
|
89
|
+
&& db.prepare(`SELECT 1 FROM ${schema[0]} WHERE id = ? LIMIT 1`).get(target.sessionId)
|
|
90
|
+
&& !db.prepare(`SELECT 1 FROM ${schema[1]} WHERE session_id = ? LIMIT 1`).get(target.sessionId)) {
|
|
91
|
+
channel.postMessage({ id, state: 'empty', dependencies: [...dependencies] })
|
|
92
|
+
return
|
|
93
|
+
}
|
|
94
|
+
} finally { db.close() }
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
if (target.format === 'chatgpt') {
|
|
98
|
+
const exported = JSON.parse(await fs.readText({ displayPath: target.path }))
|
|
99
|
+
const chat = Array.isArray(exported) ? exported.find(c => c.id === target.sessionId) : null
|
|
100
|
+
if (chat?.mapping && Object.values(chat.mapping).every(node => node && !node.message)) {
|
|
101
|
+
channel.postMessage({ id, state: 'empty', dependencies: [...dependencies] })
|
|
102
|
+
return
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
state = await readConversation(fs, { ...target, limit: 1, detail: 'full', probe: true })
|
|
106
|
+
if (state === 'unknown' && ['pi', 'codex', 'openclaw', 'hermes', 'dsh', 'dsh4'].includes(target.format) && info.type === 'file' && !/\.(db|vscdb|zstd)$/i.test(target.path)) {
|
|
107
|
+
const records = (await fs.readText({ displayPath: target.path })).split(/\r?\n/).filter(line => line.trim()).map(line => JSON.parse(line))
|
|
108
|
+
const allowed = target.format === 'codex' ? ['session_meta'] : ['session', 'session_info', 'session/title']
|
|
109
|
+
if (records.length && records.every(r => allowed.includes(r.type))) state = 'empty'
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
} catch { state = 'unknown' }
|
|
113
|
+
channel.postMessage({ id, state, dependencies: [...dependencies] })
|
|
114
|
+
})
|