@heixui/dsh-session-suite 0.0.0-stage → 0.3.0
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/LICENSE +21 -0
- package/README.md +381 -2
- package/README.zh.md +178 -0
- package/THIRD-PARTY.md +64 -0
- package/client.js +2974 -0
- package/cordis.patch.yml +77 -0
- package/icon.svg +19 -0
- package/index.js +211 -0
- package/lib/api.js +387 -0
- package/lib/checkpoints.js +774 -0
- package/lib/data/jieba-dict.txt +109750 -0
- package/lib/factkeys.js +212 -0
- package/lib/markdown.js +252 -0
- package/lib/memory-engine.js +1323 -0
- package/lib/memory-host.js +640 -0
- package/lib/memory.js +224 -0
- package/lib/retrieval.js +217 -0
- package/lib/service.js +1273 -0
- package/lib/share.js +641 -0
- package/lib/step-tag-filter.js +199 -0
- package/lib/tags.js +140 -0
- package/lib/util.js +261 -0
- package/lib/wordlist.js +183 -0
- package/locale/en.json +87 -0
- package/locale/zh.json +87 -0
- package/package.json +75 -4
package/lib/wordlist.js
ADDED
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Dictionary-backed word segmentation, deliberately free of I/O.
|
|
3
|
+
*
|
|
4
|
+
* The engine already has two lexical routes, bigrams and unigrams. Both are
|
|
5
|
+
* character-level, which gives them one shared error profile: neither knows
|
|
6
|
+
* where a word ends. `车` is a query term on its own, but the bigram and unigram
|
|
7
|
+
* routes emit their pieces for `车站` as well, so a memory about 车站 answers a
|
|
8
|
+
* question about 车. No amount of rank fusion between two routes that share a
|
|
9
|
+
* blind spot can fix that.
|
|
10
|
+
*
|
|
11
|
+
* This module adds a third route that *does* know word boundaries: forward
|
|
12
|
+
* maximum matching against a general-purpose Chinese word list (the jieba small
|
|
13
|
+
* dictionary, MIT, stored at lib/data/jieba-dict.txt). It is a pure function
|
|
14
|
+
* module — no file reads here, the caller loads the list once and passes it in —
|
|
15
|
+
* so the recall path stays offline and testable.
|
|
16
|
+
*
|
|
17
|
+
* The route is deliberately not "better" than the others, just different, which
|
|
18
|
+
* is the whole point of fusing by rank: it fails on out-of-vocabulary terms
|
|
19
|
+
* (names, code identifiers, new slang) where the character routes still work.
|
|
20
|
+
* Its failure mode and theirs do not overlap.
|
|
21
|
+
*/
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Safety bound on the longest candidate scored at each position, in UTF-16 code
|
|
25
|
+
* units. The real bound is the longest word actually present (15 units in the
|
|
26
|
+
* shipped dictionary), so this only exists to stop a pathological word list
|
|
27
|
+
* from turning matching into quadratic work. It is above the shipped maximum on
|
|
28
|
+
* purpose: a lower cap such as 8 would make every entry longer than 8
|
|
29
|
+
* characters, e.g. 中华人民共和国全国人民代表大会, unmatchable.
|
|
30
|
+
*/
|
|
31
|
+
const MAX_WORD_LENGTH = 16
|
|
32
|
+
|
|
33
|
+
/** A chunk made only of ASCII is a word, exactly as lib/retrieval.js treats it. */
|
|
34
|
+
const ASCII_ONLY = /^[\x00-\x7f]+$/
|
|
35
|
+
|
|
36
|
+
/** Same normalisation as lib/retrieval.js's tokens(): lowercase, punctuation to space. */
|
|
37
|
+
const SEPARATORS = /[\s\p{P}\p{S}]+/gu
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Build the lookup structure the segmenter queries.
|
|
41
|
+
*
|
|
42
|
+
* Accepts an array of lines (what `readFileSync(...).split('\n')` gives) or the
|
|
43
|
+
* whole text; blank lines are skipped, so a word list with a trailing newline or
|
|
44
|
+
* a stray blank line is not a problem. Words are kept verbatim apart from
|
|
45
|
+
* trimming — case folding happens at match time, because the dictionary is
|
|
46
|
+
* Chinese and lowercasing 300k entries buys nothing.
|
|
47
|
+
*
|
|
48
|
+
* @param {string[] | string} lines word list, one word per line
|
|
49
|
+
* @returns {{ words: Set<string>, size: number, longest: number, maxLength: number }} opaque word list
|
|
50
|
+
*/
|
|
51
|
+
export function loadWordList(lines) {
|
|
52
|
+
const source = typeof lines === 'string' ? lines.split(/\r?\n/) : lines ?? []
|
|
53
|
+
const words = new Set()
|
|
54
|
+
let longest = 0
|
|
55
|
+
for (const line of source) {
|
|
56
|
+
if (line == null) continue
|
|
57
|
+
const word = String(line).trim()
|
|
58
|
+
if (!word || word.startsWith('#')) continue
|
|
59
|
+
words.add(word)
|
|
60
|
+
if (word.length > longest) longest = word.length
|
|
61
|
+
}
|
|
62
|
+
return Object.freeze({
|
|
63
|
+
words,
|
|
64
|
+
size: words.size,
|
|
65
|
+
longest,
|
|
66
|
+
maxLength: Math.min(longest || 1, MAX_WORD_LENGTH),
|
|
67
|
+
})
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Whether a word list carries any words.
|
|
72
|
+
*
|
|
73
|
+
* Everything else in this module treats a null, undefined, empty or malformed
|
|
74
|
+
* word list as "not loaded" and degrades instead of throwing: recall must never
|
|
75
|
+
* fail because a data file is missing.
|
|
76
|
+
*
|
|
77
|
+
* @param {object} [wordlist] value returned by loadWordList()
|
|
78
|
+
* @returns {boolean} true when segmentation can use a dictionary
|
|
79
|
+
*/
|
|
80
|
+
export function isLoaded(wordlist) {
|
|
81
|
+
return Boolean(wordlist && wordlist.words instanceof Set && wordlist.words.size > 0)
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Number of words in a word list, 0 when it is absent or empty.
|
|
86
|
+
*
|
|
87
|
+
* @param {object} [wordlist] value returned by loadWordList()
|
|
88
|
+
* @returns {number} entry count
|
|
89
|
+
*/
|
|
90
|
+
export function wordListSize(wordlist) {
|
|
91
|
+
return isLoaded(wordlist) ? wordlist.size : 0
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/** @param {string} char single code point @returns {boolean} true for ASCII */
|
|
95
|
+
function isAsciiChar(char) {
|
|
96
|
+
return char.charCodeAt(0) < 0x80
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Forward maximum matching segmentation.
|
|
101
|
+
*
|
|
102
|
+
* At each position the longest candidate that is in the dictionary wins; when
|
|
103
|
+
* nothing matches beyond the first character, that single character is emitted
|
|
104
|
+
* and the scan moves on, so the function is total over any input. Longest rather
|
|
105
|
+
* than shortest is what makes `车站` one token instead of `车` + `站`, which is
|
|
106
|
+
* the cross-word mismatch this route exists to remove.
|
|
107
|
+
*
|
|
108
|
+
* ASCII runs inside mixed text are emitted as one word each, so `checkpoint快照`
|
|
109
|
+
* yields `checkpoint` and the Chinese tokens rather than one letter per token.
|
|
110
|
+
*
|
|
111
|
+
* Degradation: with no word list loaded every CJK character becomes its own
|
|
112
|
+
* token. That is deliberately *not* an empty result — the route then contributes
|
|
113
|
+
* the same loose signal the unigram route already provides and simply adds
|
|
114
|
+
* nothing new, which is safer for recall than silently returning nothing.
|
|
115
|
+
*
|
|
116
|
+
* @param {string} text input
|
|
117
|
+
* @param {object} [wordlist] value returned by loadWordList()
|
|
118
|
+
* @returns {string[]} tokens in order, duplicates preserved
|
|
119
|
+
*/
|
|
120
|
+
export function segment(text, wordlist) {
|
|
121
|
+
const out = []
|
|
122
|
+
const words = isLoaded(wordlist) ? wordlist.words : null
|
|
123
|
+
const maxLength = words ? wordlist.maxLength : 1
|
|
124
|
+
const normalized = String(text ?? '').toLowerCase().replace(SEPARATORS, ' ')
|
|
125
|
+
|
|
126
|
+
for (const chunk of normalized.split(' ')) {
|
|
127
|
+
if (!chunk) continue
|
|
128
|
+
if (ASCII_ONLY.test(chunk)) {
|
|
129
|
+
out.push(chunk)
|
|
130
|
+
continue
|
|
131
|
+
}
|
|
132
|
+
const chars = [...chunk]
|
|
133
|
+
let index = 0
|
|
134
|
+
while (index < chars.length) {
|
|
135
|
+
if (isAsciiChar(chars[index])) {
|
|
136
|
+
let end = index + 1
|
|
137
|
+
while (end < chars.length && isAsciiChar(chars[end])) end += 1
|
|
138
|
+
out.push(chars.slice(index, end).join(''))
|
|
139
|
+
index = end
|
|
140
|
+
continue
|
|
141
|
+
}
|
|
142
|
+
if (!words) {
|
|
143
|
+
out.push(chars[index])
|
|
144
|
+
index += 1
|
|
145
|
+
continue
|
|
146
|
+
}
|
|
147
|
+
const remaining = chars.length - index
|
|
148
|
+
let length = Math.min(maxLength, remaining)
|
|
149
|
+
let token = ''
|
|
150
|
+
for (; length > 1; length -= 1) {
|
|
151
|
+
const candidate = chars.slice(index, index + length).join('')
|
|
152
|
+
if (words.has(candidate)) {
|
|
153
|
+
token = candidate
|
|
154
|
+
break
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
if (!token) {
|
|
158
|
+
token = chars[index]
|
|
159
|
+
length = 1
|
|
160
|
+
}
|
|
161
|
+
out.push(token)
|
|
162
|
+
index += length
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
return out
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
/**
|
|
169
|
+
* The token list the retrieval layer feeds to BM25.
|
|
170
|
+
*
|
|
171
|
+
* This is the same cut as `segment()`; the two names exist because the retrieval
|
|
172
|
+
* code asks for "tokens" while the segmentation itself is the primitive. Either
|
|
173
|
+
* can be used, but this one is the documented entry point: it mirrors
|
|
174
|
+
* lib/retrieval.js's `tokens()` / `unigrams()` shape, and it keeps duplicates
|
|
175
|
+
* because BM25 consumes term frequency rather than a set.
|
|
176
|
+
*
|
|
177
|
+
* @param {string} text input
|
|
178
|
+
* @param {object} [wordlist] value returned by loadWordList()
|
|
179
|
+
* @returns {string[]} tokens in order, duplicates preserved
|
|
180
|
+
*/
|
|
181
|
+
export function wordTokens(text, wordlist) {
|
|
182
|
+
return segment(text, wordlist)
|
|
183
|
+
}
|
package/locale/en.json
ADDED
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
{
|
|
2
|
+
"meta": {
|
|
3
|
+
"title": "Session Suite",
|
|
4
|
+
"description": "Rewind a session to any message with its files and memory, delete sessions permanently, and export or share conversations."
|
|
5
|
+
},
|
|
6
|
+
"menu": {
|
|
7
|
+
"delete": "Delete session",
|
|
8
|
+
"share": "Share session",
|
|
9
|
+
"shareMarkdown": "Download as Markdown",
|
|
10
|
+
"shareText": "Copy as plain text",
|
|
11
|
+
"shareJson": "Download as JSON",
|
|
12
|
+
"shareCopied": "Conversation copied to the clipboard",
|
|
13
|
+
"shareFailed": "Could not export this session",
|
|
14
|
+
"shareEmpty": "This session has no messages to share yet",
|
|
15
|
+
"snapshots": "Session snapshots"
|
|
16
|
+
},
|
|
17
|
+
"message": {
|
|
18
|
+
"rewind": "Rewind to this step",
|
|
19
|
+
"rewindTitle": "Rewind to this step?",
|
|
20
|
+
"rewindBody": "This restores the workspace files and session memory saved for this step, forks the conversation up to this message, and archives the current session. An undo snapshot is kept for 10 minutes.",
|
|
21
|
+
"rewindConfirm": "Rewind",
|
|
22
|
+
"cancel": "Cancel",
|
|
23
|
+
"rewinding": "Rewinding…",
|
|
24
|
+
"rewindDone": "Rewound to this step",
|
|
25
|
+
"rewindFailed": "Could not rewind this session",
|
|
26
|
+
"undo": "Undo this rewind",
|
|
27
|
+
"undoDone": "Rewind undone",
|
|
28
|
+
"delete": "Delete message",
|
|
29
|
+
"deleteTitle": "Delete this message?",
|
|
30
|
+
"deleteBody": "This rewinds the conversation to the message before it, so the message and everything after it leave the conversation. Workspace files and memory are restored to that point too.",
|
|
31
|
+
"deleteConfirm": "Delete",
|
|
32
|
+
"deleteFailed": "Could not delete this message"
|
|
33
|
+
},
|
|
34
|
+
"dialog": {
|
|
35
|
+
"deleteSessionTitle": "Delete session",
|
|
36
|
+
"deleteSessionBody": "The session and everything inside it will be removed from local storage, including its conversation log, snapshots, and memory. This cannot be undone.",
|
|
37
|
+
"deleteSessionOnlyBody": "This is the only message in the session. Deleting it also deletes the session.",
|
|
38
|
+
"delete": "Delete",
|
|
39
|
+
"cancel": "Cancel",
|
|
40
|
+
"deleting": "Deleting…",
|
|
41
|
+
"deleteDone": "Session deleted",
|
|
42
|
+
"deleteFailed": "Could not delete the session",
|
|
43
|
+
"sessionRunning": "This session is running. Stop it before deleting."
|
|
44
|
+
},
|
|
45
|
+
"settings": {
|
|
46
|
+
"title": "Session snapshots",
|
|
47
|
+
"description": "Workspace checkpoints captured for the current session, with disk usage, locking, and restore controls.",
|
|
48
|
+
"empty": "No snapshots yet. A checkpoint is captured at the start of each turn.",
|
|
49
|
+
"capture": "Capture now",
|
|
50
|
+
"capturing": "Capturing…",
|
|
51
|
+
"restore": "Restore files",
|
|
52
|
+
"restoreTitle": "Restore workspace files?",
|
|
53
|
+
"restoreBody": "Only the workspace files recorded in this snapshot are rewritten. The conversation is not changed.",
|
|
54
|
+
"restoreDone": "Workspace restored",
|
|
55
|
+
"restoreFailed": "Could not restore this snapshot",
|
|
56
|
+
"remove": "Delete snapshot",
|
|
57
|
+
"lock": "Lock",
|
|
58
|
+
"unlock": "Unlock",
|
|
59
|
+
"locked": "Locked",
|
|
60
|
+
"budget": "Keeps the newest {keep} automatic snapshots",
|
|
61
|
+
"usage": "Snapshots use {size}",
|
|
62
|
+
"files": "{count} files",
|
|
63
|
+
"turn": "Turn {turn}",
|
|
64
|
+
"temporary": "Temporary",
|
|
65
|
+
"undoBundles": "Undo bundles",
|
|
66
|
+
"undoBundle": "Rollback at turn {turn}",
|
|
67
|
+
"undoRestore": "Undo rollback",
|
|
68
|
+
"undoHint": "Available for 10 minutes after a rollback.",
|
|
69
|
+
"memory": "Session memory",
|
|
70
|
+
"memoryEmpty": "Nothing remembered in this session yet.",
|
|
71
|
+
"memoryAdd": "Add memory",
|
|
72
|
+
"memoryPlaceholder": "A durable fact, rule, or preference",
|
|
73
|
+
"memorySave": "Save",
|
|
74
|
+
"memoryRemove": "Remove",
|
|
75
|
+
"memoryHint": "Entries added after a rollback point are removed when you rewind.",
|
|
76
|
+
"loading": "Loading snapshots…"
|
|
77
|
+
},
|
|
78
|
+
"toast": {
|
|
79
|
+
"captured": "Snapshot captured",
|
|
80
|
+
"removed": "Snapshot deleted",
|
|
81
|
+
"locked": "Snapshot locked",
|
|
82
|
+
"unlocked": "Snapshot unlocked",
|
|
83
|
+
"memoryAdded": "Memory saved",
|
|
84
|
+
"memoryRemoved": "Memory removed",
|
|
85
|
+
"failed": "Something went wrong"
|
|
86
|
+
}
|
|
87
|
+
}
|
package/locale/zh.json
ADDED
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
{
|
|
2
|
+
"meta": {
|
|
3
|
+
"title": "会话套件",
|
|
4
|
+
"description": "把会话回滚到任意一条消息,同时恢复工作区文件与会话记忆;支持永久删除会话、导出与分享对话。"
|
|
5
|
+
},
|
|
6
|
+
"menu": {
|
|
7
|
+
"delete": "删除任务",
|
|
8
|
+
"share": "分享会话",
|
|
9
|
+
"shareMarkdown": "下载为 Markdown",
|
|
10
|
+
"shareText": "复制为纯文本",
|
|
11
|
+
"shareJson": "下载为 JSON",
|
|
12
|
+
"shareCopied": "对话已复制到剪贴板",
|
|
13
|
+
"shareFailed": "会话导出失败",
|
|
14
|
+
"shareEmpty": "这个会话还没有可分享的消息",
|
|
15
|
+
"snapshots": "会话快照"
|
|
16
|
+
},
|
|
17
|
+
"message": {
|
|
18
|
+
"rewind": "回滚到此步骤",
|
|
19
|
+
"rewindTitle": "确定要回滚到此步骤并重新开始吗?",
|
|
20
|
+
"rewindBody": "此操作会把工作区文件和会话记忆恢复到这一步,并把对话分叉到这条消息,同时归档当前会话。撤销快照会保留 10 分钟。",
|
|
21
|
+
"rewindConfirm": "确认回滚",
|
|
22
|
+
"cancel": "取消",
|
|
23
|
+
"rewinding": "正在回滚…",
|
|
24
|
+
"rewindDone": "已回滚到此步骤",
|
|
25
|
+
"rewindFailed": "回滚失败",
|
|
26
|
+
"undo": "撤销本次回滚",
|
|
27
|
+
"undoDone": "已撤销回滚",
|
|
28
|
+
"delete": "删除消息",
|
|
29
|
+
"deleteTitle": "确定要删除这条消息吗?",
|
|
30
|
+
"deleteBody": "删除后,对话会回滚到这条消息之前,这条消息及其之后的内容都不会再出现在会话里,同时工作区文件与会话记忆也会恢复到那一刻。",
|
|
31
|
+
"deleteConfirm": "删除",
|
|
32
|
+
"deleteFailed": "删除消息失败"
|
|
33
|
+
},
|
|
34
|
+
"dialog": {
|
|
35
|
+
"deleteSessionTitle": "删除任务",
|
|
36
|
+
"deleteSessionBody": "删除后,这个会话及其所有内容将从本地移除,包括聊天记录、快照和会话记忆,且无法恢复。",
|
|
37
|
+
"deleteSessionOnlyBody": "这是当前会话中唯一的消息,删除后会同时删除该会话。",
|
|
38
|
+
"delete": "删除",
|
|
39
|
+
"cancel": "取消",
|
|
40
|
+
"deleting": "正在删除…",
|
|
41
|
+
"deleteDone": "会话已删除",
|
|
42
|
+
"deleteFailed": "删除会话失败",
|
|
43
|
+
"sessionRunning": "这个会话正在运行,请先停止再删除。"
|
|
44
|
+
},
|
|
45
|
+
"settings": {
|
|
46
|
+
"title": "会话快照",
|
|
47
|
+
"description": "当前会话的工作区检查点:查看占用空间、锁定或删除快照,也可以直接恢复文件。",
|
|
48
|
+
"empty": "还没有快照。每一轮对话开始时都会自动记录一次。",
|
|
49
|
+
"capture": "立即创建快照",
|
|
50
|
+
"capturing": "正在创建…",
|
|
51
|
+
"restore": "恢复文件",
|
|
52
|
+
"restoreTitle": "恢复工作区文件?",
|
|
53
|
+
"restoreBody": "只会把这份快照里记录的文件写回工作区,不会改变对话内容。",
|
|
54
|
+
"restoreDone": "工作区已恢复",
|
|
55
|
+
"restoreFailed": "恢复快照失败",
|
|
56
|
+
"remove": "删除快照",
|
|
57
|
+
"lock": "锁定",
|
|
58
|
+
"unlock": "解锁",
|
|
59
|
+
"locked": "已锁定",
|
|
60
|
+
"budget": "每个会话保留最近 {keep} 个自动快照",
|
|
61
|
+
"usage": "快照占用 {size}",
|
|
62
|
+
"files": "{count} 个文件",
|
|
63
|
+
"turn": "第 {turn} 轮",
|
|
64
|
+
"temporary": "临时",
|
|
65
|
+
"undoBundles": "撤销包",
|
|
66
|
+
"undoBundle": "第 {turn} 轮的回滚",
|
|
67
|
+
"undoRestore": "撤销回滚",
|
|
68
|
+
"undoHint": "回滚后 10 分钟内可撤销。",
|
|
69
|
+
"memory": "会话记忆",
|
|
70
|
+
"memoryEmpty": "这个会话还没有记录记忆。",
|
|
71
|
+
"memoryAdd": "新增记忆",
|
|
72
|
+
"memoryPlaceholder": "写入一条长期有效的事实、规则或偏好",
|
|
73
|
+
"memorySave": "保存",
|
|
74
|
+
"memoryRemove": "删除",
|
|
75
|
+
"memoryHint": "回滚时,晚于回滚点的记忆会一并被移除。",
|
|
76
|
+
"loading": "正在读取快照…"
|
|
77
|
+
},
|
|
78
|
+
"toast": {
|
|
79
|
+
"captured": "已创建快照",
|
|
80
|
+
"removed": "已删除快照",
|
|
81
|
+
"locked": "已锁定快照",
|
|
82
|
+
"unlocked": "已解锁快照",
|
|
83
|
+
"memoryAdded": "记忆已保存",
|
|
84
|
+
"memoryRemoved": "记忆已删除",
|
|
85
|
+
"failed": "操作失败"
|
|
86
|
+
}
|
|
87
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,77 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@heixui/dsh-session-suite",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"
|
|
5
|
-
"
|
|
6
|
-
|
|
3
|
+
"version": "0.3.0",
|
|
4
|
+
"description": "Session checkpoints for DSH: rewind a conversation with its files and memory, permanent session deletion, and shareable exports",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./index.js",
|
|
7
|
+
"license": "MIT",
|
|
8
|
+
"author": {
|
|
9
|
+
"name": "heixiua",
|
|
10
|
+
"url": "https://github.com/heixiua"
|
|
11
|
+
},
|
|
12
|
+
"contributors": [
|
|
13
|
+
{
|
|
14
|
+
"name": "heixiua",
|
|
15
|
+
"url": "https://github.com/heixiua"
|
|
16
|
+
}
|
|
17
|
+
],
|
|
18
|
+
"keywords": [
|
|
19
|
+
"dsh",
|
|
20
|
+
"cordis",
|
|
21
|
+
"plugin",
|
|
22
|
+
"session",
|
|
23
|
+
"checkpoint",
|
|
24
|
+
"rollback",
|
|
25
|
+
"rewind",
|
|
26
|
+
"memory"
|
|
27
|
+
],
|
|
28
|
+
"repository": {
|
|
29
|
+
"type": "git",
|
|
30
|
+
"url": "git+https://github.com/heixiua/dsh-session-suite.git"
|
|
31
|
+
},
|
|
32
|
+
"homepage": "https://github.com/heixiua/dsh-session-suite#readme",
|
|
33
|
+
"bugs": {
|
|
34
|
+
"url": "https://github.com/heixiua/dsh-session-suite/issues"
|
|
35
|
+
},
|
|
36
|
+
"engines": {
|
|
37
|
+
"node": ">=22"
|
|
38
|
+
},
|
|
39
|
+
"exports": {
|
|
40
|
+
".": "./index.js",
|
|
41
|
+
"./client": "./client.js",
|
|
42
|
+
"./locale/*.json": "./locale/*.json",
|
|
43
|
+
"./icon": "./icon.svg",
|
|
44
|
+
"./package.json": "./package.json"
|
|
45
|
+
},
|
|
46
|
+
"files": [
|
|
47
|
+
"index.js",
|
|
48
|
+
"client.js",
|
|
49
|
+
"lib/*.js",
|
|
50
|
+
"lib/data/*.txt",
|
|
51
|
+
"cordis.patch.yml",
|
|
52
|
+
"locale/*.json",
|
|
53
|
+
"icon.svg",
|
|
54
|
+
"README.md",
|
|
55
|
+
"LICENSE",
|
|
56
|
+
"THIRD-PARTY.md"
|
|
57
|
+
],
|
|
58
|
+
"dsh": {
|
|
59
|
+
"bundle": {
|
|
60
|
+
"patch": "./cordis.patch.yml"
|
|
61
|
+
},
|
|
62
|
+
"client": {
|
|
63
|
+
"platform": "web",
|
|
64
|
+
"immediately": true,
|
|
65
|
+
"inject": [
|
|
66
|
+
"@deepseek-ai/dsh-client-locale",
|
|
67
|
+
"@deepseek-ai/dsh-client-ui-workspace",
|
|
68
|
+
"@deepseek-ai/dsh-client-ui-chat",
|
|
69
|
+
"@deepseek-ai/dsh-client-ui-settings"
|
|
70
|
+
]
|
|
71
|
+
}
|
|
72
|
+
},
|
|
73
|
+
"publishConfig": {
|
|
74
|
+
"access": "public",
|
|
75
|
+
"registry": "https://registry.npmjs.org/"
|
|
76
|
+
}
|
|
77
|
+
}
|