dsh-anima 0.0.0-stage → 2.2.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/CHANGELOG.md +1009 -0
- package/LICENSE +21 -0
- package/README.md +1015 -2
- package/README_EN.md +1000 -0
- package/assets/icon.svg +30 -0
- package/client/index.mjs +6295 -0
- package/cordis.patch.yml +3 -0
- package/index.mjs +3097 -0
- package/lib/audit.mjs +126 -0
- package/lib/config.mjs +617 -0
- package/lib/decay.mjs +131 -0
- package/lib/import.mjs +408 -0
- package/lib/injection.mjs +280 -0
- package/lib/memory-audit.mjs +262 -0
- package/lib/memory.mjs +703 -0
- package/lib/patrol.mjs +231 -0
- package/lib/presets.mjs +310 -0
- package/lib/prompt.mjs +416 -0
- package/lib/quality.mjs +718 -0
- package/lib/retrospect.mjs +296 -0
- package/lib/sessions.mjs +174 -0
- package/lib/store.mjs +755 -0
- package/lib/wording.mjs +139 -0
- package/lib/workspace.mjs +168 -0
- package/package.json +72 -4
package/lib/wording.mjs
ADDED
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
// lib/wording.mjs — 档位文案表 + 示例表(纯数据,零依赖)
|
|
2
|
+
//
|
|
3
|
+
// 定位(用户 m06593 拍板「全字段自由文本 + 原档位文案降级为一键填入」):
|
|
4
|
+
// 人设字段**全部改成自由文本**,原来的档位(专业严谨 / 幽默风趣 / 表格增强 …)
|
|
5
|
+
// 不再是「只能选这几个」的枚举,而是一段**可以一键填入、随后随便改**的文案模板。
|
|
6
|
+
// 于是文案表从「查表渲染」的 lib/prompt.mjs 搬到这里,由另外两个消费者共用:
|
|
7
|
+
// - lib/config.mjs 的迁移路径:旧配置里存的是档位键('professional'),
|
|
8
|
+
// 自动翻译成文案。用户升级后注入提示词的内容一字不变 —— 这是刻意的,
|
|
9
|
+
// 否则「升级插件」会等于「偷偷把用户的风格改回默认」。
|
|
10
|
+
// - host 下发给设置页:每个「一键填入」按钮该填什么。
|
|
11
|
+
//
|
|
12
|
+
// 为什么表不留在这两个消费者各自那边:同一份文案在三处各写一遍,改一处漏两处,
|
|
13
|
+
// 而漏掉的那处正是提示词里实际生效的那份。
|
|
14
|
+
//
|
|
15
|
+
// 文件里的两张表,区别只在「有没有标准答案」:
|
|
16
|
+
// - WORDING_TABLES:风格字段。改回枚举是说得通的,所以每个档位就是一句定稿的话。
|
|
17
|
+
// - FIELD_EXAMPLES:身份与背景字段。**没有**标准答案(谁也不能替用户写他是谁),
|
|
18
|
+
// 所以这里给的是**示例**——一段可以直接改成自己情况的现成文字(用户 m08898
|
|
19
|
+
// 要求「每个文本都要有例子或者是一些预设」)。示例的作用是消除「空输入框恐惧」:
|
|
20
|
+
// 面对一个空 textarea,多数人第一反应是跳过,于是这个字段永远空着。
|
|
21
|
+
//
|
|
22
|
+
// 一个已知取舍(不是遗漏):注入进提示词的文案是中文的(含 en 档的 'Reply in English.'),
|
|
23
|
+
// 英文界面的用户点「一键填入」会得到中文文案。这与改造前完全一致 —— 语言档一向是
|
|
24
|
+
// 「用哪种语言回复」,而不是「插件界面文案用哪种语言」;两者混在一起反而会让
|
|
25
|
+
// 中文用户选 en 之后收到英文的解释段。
|
|
26
|
+
|
|
27
|
+
/** 风格档位:键 → 注入提示词的完整文案。 */
|
|
28
|
+
export const STYLE_WORDING = {
|
|
29
|
+
professional: '专业严谨、注重逻辑和准确性;结论先行,术语准确,避免口语化与情绪化表达。',
|
|
30
|
+
casual: '轻松自然,像朋友之间聊天;语气随和,可以用口语,但技术内容仍要准确。',
|
|
31
|
+
humorous: '幽默风趣;在保证准确的前提下允许俏皮的比喻与调侃,让回答读起来不枯燥。',
|
|
32
|
+
roast: '毒舌吐槽;直白指出问题、不留情面,但始终对事不对人,不攻击用户本人。',
|
|
33
|
+
efficient: '高效干练;走最短路径给答案,能省的字都省,不重复用户已经说过的话。'
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* 三项特质:键 → 文案。
|
|
38
|
+
* default 刻意映射为**空串**而不是某句「保持默认」—— 提示词组装会把空串整行丢掉,
|
|
39
|
+
* 于是「默认」真的等于「不提这件事」,模型按自己的判断来,而不是被一句
|
|
40
|
+
* 「请按默认方式处理标题」占用注意力。
|
|
41
|
+
*/
|
|
42
|
+
export const TRAIT_WORDING = {
|
|
43
|
+
headingLists: {
|
|
44
|
+
default: '',
|
|
45
|
+
more: '多用标题与列表组织内容,让结构一目了然。',
|
|
46
|
+
less: '少用标题与列表,以连贯的段落文字为主。'
|
|
47
|
+
},
|
|
48
|
+
emoji: {
|
|
49
|
+
default: '',
|
|
50
|
+
more: '可以适度使用表情符号增添语气。',
|
|
51
|
+
less: '尽量不使用表情符号。'
|
|
52
|
+
},
|
|
53
|
+
tables: {
|
|
54
|
+
default: '',
|
|
55
|
+
more: '需要对比、多字段或结构化信息时,优先使用表格。',
|
|
56
|
+
less: '避免使用表格,改用列表或段落文本。'
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export const REPLY_LENGTH_WORDING = {
|
|
61
|
+
concise: '简洁;直击要点,不展开背景与推理过程。',
|
|
62
|
+
normal: '适中;不额外约束长度。',
|
|
63
|
+
detailed: '详尽;充分展开背景、步骤与推理过程。'
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export const LANGUAGE_WORDING = {
|
|
67
|
+
zh: '使用简体中文回复。',
|
|
68
|
+
en: 'Reply in English.'
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* 字段 → 档位表。键的顺序即设置页「一键填入」按钮的排列顺序
|
|
73
|
+
* (对象字面量的键序在 JS 里是有保证的,不必另立一份顺序表 —— 两份必然写岔)。
|
|
74
|
+
*/
|
|
75
|
+
export const WORDING_TABLES = {
|
|
76
|
+
style: STYLE_WORDING,
|
|
77
|
+
headingLists: TRAIT_WORDING.headingLists,
|
|
78
|
+
emoji: TRAIT_WORDING.emoji,
|
|
79
|
+
tables: TRAIT_WORDING.tables,
|
|
80
|
+
replyLength: REPLY_LENGTH_WORDING,
|
|
81
|
+
language: LANGUAGE_WORDING
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* 身份与背景字段的示例(用户 m08898:「每个文本都要有例子或者是一些预设」)。
|
|
86
|
+
*
|
|
87
|
+
* 与 WORDING_TABLES 的分工:那边是「档位」,点一下就定稿;这边是「示例」,
|
|
88
|
+
* 点一下**填进去还要改**(把「三年前端」改成你自己的年限)。因此文案刻意写成
|
|
89
|
+
* **明显属于别人的第一人称**,而不是一段通用套话 —— 通用套话会被直接保存,
|
|
90
|
+
* 然后长期误导模型;一看就知道要改的示例才会被改。
|
|
91
|
+
*
|
|
92
|
+
* 数组而非单条:同一字段给多个角度,用户挑一个顺眼的改。键序即按钮排列序。
|
|
93
|
+
* 空数组表示「这个字段不给示例」——不是字段不存在,是示例帮不上忙(见 agentName)。
|
|
94
|
+
*/
|
|
95
|
+
export const FIELD_EXAMPLES = {
|
|
96
|
+
// 名字不宜给示例:按钮填一个示例名进去,用户很可能忘了改,
|
|
97
|
+
// 结果 AI 用了别人起的名。这一项靠 placeholder 提示格式即可。
|
|
98
|
+
agentName: [],
|
|
99
|
+
agentRole: [
|
|
100
|
+
'长期 AI 助手,服务对象是学生与研究者。习惯先给结论再解释依据,遇到不确定的事实会先查证而不是猜。',
|
|
101
|
+
'资深全栈工程师。优先给出能跑的最小方案,再谈扩展;改动会说明影响面与回滚方式。',
|
|
102
|
+
'写作搭子。先问清读者是谁、要什么效果,再动笔;改稿时说明每一处为什么改。'
|
|
103
|
+
],
|
|
104
|
+
addressUserAs: ['老大', '老师', '直接叫我的名字就行'],
|
|
105
|
+
userOccupation: ['研究生', '前端开发', '产品经理', '中学教师'],
|
|
106
|
+
userBio: [
|
|
107
|
+
'研究生在读,方向是自然语言处理。日常要读论文、跑实验、写代码,对名词的准确性比较敏感。',
|
|
108
|
+
'三年前端开发,主要写 React。看得懂后端代码但不想深入,解释时可以少讲原理多讲怎么用。',
|
|
109
|
+
'非技术岗,用 AI 处理文档和数据表格。术语请展开讲,代码能跑就行。'
|
|
110
|
+
],
|
|
111
|
+
customInstructions: [
|
|
112
|
+
'回答里不要用「首先/其次/最后」这类模板词。\n涉及具体版本号、命令、参数时,先确认再写,不确定就说不确定。',
|
|
113
|
+
'给方案时最多给三个,并说明你推荐哪个、为什么。\n改动我已有的文件前,先说清改哪里、改成什么。',
|
|
114
|
+
'每次涉及事实陈述,标出信息来源是记忆、推理还是检索结果。\n我不想看客套话,直接进正题。'
|
|
115
|
+
],
|
|
116
|
+
// 记忆区的示例。键名不是人设字段,但走同一条下发通道(客户端按字段名取表),
|
|
117
|
+
// 与其为它单开一条路径,不如共用 —— 两张表都只是「一段可改的现成文字」。
|
|
118
|
+
memory: [
|
|
119
|
+
'回答先给结论,再展开依据。',
|
|
120
|
+
'改动我已有文件之前,先说清改哪里、为什么改。',
|
|
121
|
+
'涉及版本号与命令时不要凭印象写,先确认。'
|
|
122
|
+
]
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* 把「可能是档位键」的值翻译成文案。
|
|
127
|
+
*
|
|
128
|
+
* @param {string} key 人设字段名(style / headingLists / …)
|
|
129
|
+
* @param {unknown} value 配置里的原值
|
|
130
|
+
* @returns {string|null} 命中档位键时返回文案(**可能是空串**,如 default);
|
|
131
|
+
* 不是本字段的档位键、或本字段没有档位表时返回 null —— 调用方据此判断
|
|
132
|
+
* 「这是旧版存的键」还是「这是用户自己写的句子」。
|
|
133
|
+
* 注意不能用 `if (result)` 判命中:空串是合法翻译结果。
|
|
134
|
+
*/
|
|
135
|
+
export function translateWording(key, value) {
|
|
136
|
+
const table = WORDING_TABLES[key]
|
|
137
|
+
if (!table || typeof value !== 'string') return null
|
|
138
|
+
return Object.prototype.hasOwnProperty.call(table, value) ? table[value] : null
|
|
139
|
+
}
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
// lib/workspace.mjs — 工作区两级覆盖的读写与解析
|
|
2
|
+
//
|
|
3
|
+
// 存储位置:<workspace>/.dsh-anima/override.json
|
|
4
|
+
// - 跟项目走、可 git、可拷给别人(拍板 ⑦)
|
|
5
|
+
// - 风险:会进用户的 git,第一次 commit 就可能把私人语气推到远程。
|
|
6
|
+
// 因此本模块额外维护 .gitignore,默认忽略整个目录(可关)。
|
|
7
|
+
//
|
|
8
|
+
// 解析链:会话 cwd → 向上找最近的 override.json → sanitizeOverrides → mergePersona
|
|
9
|
+
|
|
10
|
+
import { mkdir, readFile, rename, writeFile } from 'node:fs/promises'
|
|
11
|
+
import { readFileSync, statSync } from 'node:fs'
|
|
12
|
+
import { dirname, join, resolve, sep } from 'node:path'
|
|
13
|
+
import { sanitizeOverrides } from './config.mjs'
|
|
14
|
+
|
|
15
|
+
const OVERRIDE_FILE = 'override.json'
|
|
16
|
+
|
|
17
|
+
export function overrideDirFor(workspacePath, dirName) {
|
|
18
|
+
return join(workspacePath, dirName || '.dsh-anima')
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export function overrideFileFor(workspacePath, dirName) {
|
|
22
|
+
return join(overrideDirFor(workspacePath, dirName), OVERRIDE_FILE)
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
async function readJsonIfExists(path) {
|
|
26
|
+
try {
|
|
27
|
+
return JSON.parse(await readFile(path, 'utf8'))
|
|
28
|
+
} catch (err) {
|
|
29
|
+
if (err && err.code === 'ENOENT') return null
|
|
30
|
+
if (err instanceof SyntaxError) {
|
|
31
|
+
const e = new Error(`覆盖文件不是合法 JSON:${path}`)
|
|
32
|
+
e.code = 'INVALID_JSON'
|
|
33
|
+
throw e
|
|
34
|
+
}
|
|
35
|
+
throw err
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
async function writeJsonAtomic(path, value) {
|
|
40
|
+
await mkdir(dirname(path), { recursive: true })
|
|
41
|
+
const tmp = `${path}.tmp-${process.pid}-${Date.now()}`
|
|
42
|
+
await writeFile(tmp, JSON.stringify(value, null, 2), 'utf8')
|
|
43
|
+
await rename(tmp, path)
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* 确保工作区目录不需要把覆盖文件提交进 git。
|
|
48
|
+
* 在覆盖目录内写一个 .gitignore(内容 `*`),该规则只作用于本目录。
|
|
49
|
+
* 失败不抛错 —— gitignore 只是保险,不该阻断保存。
|
|
50
|
+
*/
|
|
51
|
+
export async function ensureIgnored(workspacePath, dirName) {
|
|
52
|
+
try {
|
|
53
|
+
const gitignore = join(overrideDirFor(workspacePath, dirName), '.gitignore')
|
|
54
|
+
await mkdir(dirname(gitignore), { recursive: true })
|
|
55
|
+
await readFile(gitignore, 'utf8')
|
|
56
|
+
} catch (err) {
|
|
57
|
+
if (err && err.code === 'ENOENT') {
|
|
58
|
+
try {
|
|
59
|
+
await writeFile(join(overrideDirFor(workspacePath, dirName), '.gitignore'), '*\n', 'utf8')
|
|
60
|
+
} catch { /* 忽略 */ }
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* 读取某工作区的覆盖。
|
|
67
|
+
* @returns {{overrides: object, path: string, exists: boolean, errors: Record<string,string>}}
|
|
68
|
+
*/
|
|
69
|
+
export async function readOverrides(workspacePath, dirName) {
|
|
70
|
+
const path = overrideFileFor(workspacePath, dirName)
|
|
71
|
+
let raw = null
|
|
72
|
+
try {
|
|
73
|
+
raw = await readJsonIfExists(path)
|
|
74
|
+
} catch (err) {
|
|
75
|
+
if (err.code === 'INVALID_JSON') {
|
|
76
|
+
return { overrides: {}, path, exists: true, errors: { _: err.message } }
|
|
77
|
+
}
|
|
78
|
+
throw err
|
|
79
|
+
}
|
|
80
|
+
if (raw === null) return { overrides: {}, path, exists: false, errors: {} }
|
|
81
|
+
const { overrides, errors } = sanitizeOverrides(raw)
|
|
82
|
+
return { overrides, path, exists: true, errors }
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/** 写入某工作区的覆盖;空对象表示清除覆盖(写空文件而不是删文件,便于用户看到)。 */
|
|
86
|
+
export async function writeOverrides(workspacePath, dirName, overrides) {
|
|
87
|
+
const path = overrideFileFor(workspacePath, dirName)
|
|
88
|
+
await writeJsonAtomic(path, { overrides })
|
|
89
|
+
invalidateSyncCache(path)
|
|
90
|
+
return path
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/** 删除覆盖文件;不存在返回 false。 */
|
|
94
|
+
export async function removeOverrides(workspacePath, dirName) {
|
|
95
|
+
const path = overrideFileFor(workspacePath, dirName)
|
|
96
|
+
try {
|
|
97
|
+
const { rm } = await import('node:fs/promises')
|
|
98
|
+
await rm(path)
|
|
99
|
+
invalidateSyncCache(path)
|
|
100
|
+
return true
|
|
101
|
+
} catch (err) {
|
|
102
|
+
if (err && err.code === 'ENOENT') return false
|
|
103
|
+
throw err
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* 从会话 cwd 向上寻找最近的覆盖文件(同步版)。
|
|
109
|
+
*
|
|
110
|
+
* **为什么需要同步版**:`systemPrompt.section()` 的 `text` 为函数时,
|
|
111
|
+
* dsh-system-prompt 在装配期是**同步**调用 `section.text(context)`(见
|
|
112
|
+
* dsh-system-prompt\lib\index.js:342),拿不到 Promise。而 section 必须在
|
|
113
|
+
* 每一轮装配期重新求值才能反映「此时此会话的工作区」,所以这条读取路径
|
|
114
|
+
* 只能是同步的。
|
|
115
|
+
*
|
|
116
|
+
* 用 mtime 做缓存键:文件没变就不重复解析,避免每个模型 step 都读盘。
|
|
117
|
+
*
|
|
118
|
+
* @returns {{workspacePath: string, overrides: object, path: string, errors: object}|null}
|
|
119
|
+
*/
|
|
120
|
+
const syncCache = new Map() // path -> {mtimeMs, size, result}
|
|
121
|
+
|
|
122
|
+
export function resolveOverridesFromCwdSync(cwd, dirName) {
|
|
123
|
+
if (!cwd || typeof cwd !== 'string') return null
|
|
124
|
+
const name = dirName || '.dsh-anima'
|
|
125
|
+
let current = resolve(cwd)
|
|
126
|
+
const root = resolve(sep)
|
|
127
|
+
|
|
128
|
+
for (let depth = 0; depth < 32; depth += 1) {
|
|
129
|
+
const candidate = join(current, name, OVERRIDE_FILE)
|
|
130
|
+
let stat = null
|
|
131
|
+
try {
|
|
132
|
+
stat = statSync(candidate)
|
|
133
|
+
} catch {
|
|
134
|
+
stat = null
|
|
135
|
+
}
|
|
136
|
+
if (stat && stat.isFile()) {
|
|
137
|
+
const cached = syncCache.get(candidate)
|
|
138
|
+
if (cached && cached.mtimeMs === stat.mtimeMs && cached.size === stat.size) {
|
|
139
|
+
return cached.result === null ? null : { ...cached.result, workspacePath: current }
|
|
140
|
+
}
|
|
141
|
+
let result
|
|
142
|
+
try {
|
|
143
|
+
const raw = JSON.parse(readFileSync(candidate, 'utf8'))
|
|
144
|
+
const { overrides, errors } = sanitizeOverrides(raw)
|
|
145
|
+
result = { overrides, path: candidate, errors }
|
|
146
|
+
} catch (err) {
|
|
147
|
+
result = {
|
|
148
|
+
overrides: {},
|
|
149
|
+
path: candidate,
|
|
150
|
+
errors: { _: err instanceof SyntaxError ? `覆盖文件不是合法 JSON:${candidate}` : String(err?.message || err) }
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
syncCache.set(candidate, { mtimeMs: stat.mtimeMs, size: stat.size, result })
|
|
154
|
+
return { ...result, workspacePath: current }
|
|
155
|
+
}
|
|
156
|
+
if (current === root) break
|
|
157
|
+
const parent = dirname(current)
|
|
158
|
+
if (parent === current) break
|
|
159
|
+
current = parent
|
|
160
|
+
}
|
|
161
|
+
return null
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/** 写入后使同步缓存失效(否则同一 mtime 精度内的连续写入会读到旧值)。 */
|
|
165
|
+
export function invalidateSyncCache(path) {
|
|
166
|
+
if (path) syncCache.delete(path)
|
|
167
|
+
else syncCache.clear()
|
|
168
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,74 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-anima",
|
|
3
|
-
"version": "
|
|
4
|
-
"
|
|
5
|
-
"
|
|
6
|
-
|
|
3
|
+
"version": "2.2.2",
|
|
4
|
+
"description": "DSH 助手人格与外挂记忆层:AI 自身的名字与角色、按工作区两级覆盖、带待审缓冲区的长期记忆",
|
|
5
|
+
"icon": "./assets/icon.svg",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"main": "./index.mjs",
|
|
8
|
+
"license": "MIT",
|
|
9
|
+
"scripts": {
|
|
10
|
+
"verify": "node scripts/verify-core.mjs",
|
|
11
|
+
"smoke": "node scripts/smoke-host.mjs && node scripts/smoke-client.mjs",
|
|
12
|
+
"acceptance": "node scripts/acceptance.mjs",
|
|
13
|
+
"review": "node scripts/review.mjs",
|
|
14
|
+
"check": "node scripts/verify-core.mjs && node scripts/smoke-host.mjs && node scripts/smoke-client.mjs && node scripts/check-client-i18n.mjs"
|
|
15
|
+
},
|
|
16
|
+
"keywords": [
|
|
17
|
+
"dsh",
|
|
18
|
+
"dsh-plugin",
|
|
19
|
+
"deepseek-harness",
|
|
20
|
+
"persona",
|
|
21
|
+
"memory",
|
|
22
|
+
"prompt"
|
|
23
|
+
],
|
|
24
|
+
"repository": {
|
|
25
|
+
"type": "git",
|
|
26
|
+
"url": "git+https://github.com/Melody-cx/dsh-anima.git"
|
|
27
|
+
},
|
|
28
|
+
"homepage": "https://github.com/Melody-cx/dsh-anima#readme",
|
|
29
|
+
"bugs": {
|
|
30
|
+
"url": "https://github.com/Melody-cx/dsh-anima/issues"
|
|
31
|
+
},
|
|
32
|
+
"exports": {
|
|
33
|
+
".": "./index.mjs",
|
|
34
|
+
"./client": "./client/index.mjs",
|
|
35
|
+
"./cordis.patch.yml": "./cordis.patch.yml",
|
|
36
|
+
"./package.json": "./package.json"
|
|
37
|
+
},
|
|
38
|
+
"files": [
|
|
39
|
+
"index.mjs",
|
|
40
|
+
"lib",
|
|
41
|
+
"client",
|
|
42
|
+
"assets",
|
|
43
|
+
"cordis.patch.yml",
|
|
44
|
+
"LICENSE",
|
|
45
|
+
"README.md",
|
|
46
|
+
"README_EN.md",
|
|
47
|
+
"CHANGELOG.md"
|
|
48
|
+
],
|
|
49
|
+
"dsh": {
|
|
50
|
+
"bundle": {
|
|
51
|
+
"patch": "./cordis.patch.yml"
|
|
52
|
+
},
|
|
53
|
+
"client": {
|
|
54
|
+
"platform": "web",
|
|
55
|
+
"inject": [
|
|
56
|
+
"@deepseek-ai/dsh-client-locale",
|
|
57
|
+
"@deepseek-ai/dsh-client-ui-conversation"
|
|
58
|
+
]
|
|
59
|
+
}
|
|
60
|
+
},
|
|
61
|
+
"peerDependencies": {
|
|
62
|
+
"@deepseek-ai/cordis": "^4.0.1 || ^4.0.5-alpha.1",
|
|
63
|
+
"@deepseek-ai/dsh-llm": ">=0.1.1-rc.2 <0.2.0 || >=0.2.0-0 <0.3.0-0",
|
|
64
|
+
"@deepseek-ai/dsh-storage-domain": ">=0.1.6-0 <0.2.0 || >=0.2.0-0 <0.3.0-0",
|
|
65
|
+
"@deepseek-ai/dsh-tools": ">=0.1.0-rc.6 <0.2.0 || >=0.2.0-0 <0.3.0-0"
|
|
66
|
+
},
|
|
67
|
+
"dependencies": {
|
|
68
|
+
"zod": "^4.4.3"
|
|
69
|
+
},
|
|
70
|
+
"engines": {
|
|
71
|
+
"node": "^22.19.0 || >=24.0.0",
|
|
72
|
+
"dsh": ">=0.1.6-0 <0.2.0 || >=0.2.0-0 <0.3.0-0"
|
|
73
|
+
}
|
|
74
|
+
}
|