dsh-custom-mode 1.9.14 → 1.9.15
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/client.js +2 -0
- package/index.mjs +30 -7
- package/locales.mjs +2 -0
- package/package.json +1 -1
- package/preset-backend/declarative.mjs +27 -2
package/client.js
CHANGED
|
@@ -148,6 +148,7 @@ try {
|
|
|
148
148
|
"api.metaWriteFailed": "写入 preset.yml 失败:{detail}",
|
|
149
149
|
"api.promptReadFailed": "读取提示词失败:{detail}",
|
|
150
150
|
"api.noRemoveApi": "当前 DSH 版本没有 agentPresets.remove(),无法删除。",
|
|
151
|
+
"api.noDirectory": "删除失败:本机找不到这个助手的目录,页面上的列表可能是旧的。刷新一次再试。",
|
|
151
152
|
"api.deleteFailed": "删除失败:{detail}",
|
|
152
153
|
"api.versionMissing": "找不到这个版本(历史可能已被上限裁剪)。",
|
|
153
154
|
"api.badJson": "请求体不是合法 JSON",
|
|
@@ -361,6 +362,7 @@ try {
|
|
|
361
362
|
"api.metaWriteFailed": "Writing preset.yml failed: {detail}",
|
|
362
363
|
"api.promptReadFailed": "Reading the prompt failed: {detail}",
|
|
363
364
|
"api.noRemoveApi": "This DSH build has no agentPresets.remove(), so deleting is unavailable.",
|
|
365
|
+
"api.noDirectory": "Delete failed: this assistant's directory is gone from disk, so the list on screen is stale. Refresh and try again.",
|
|
364
366
|
"api.deleteFailed": "Delete failed: {detail}",
|
|
365
367
|
"api.versionMissing": "That version is gone (the history is capped).",
|
|
366
368
|
"api.badJson": "The request body is not valid JSON",
|
package/index.mjs
CHANGED
|
@@ -733,11 +733,14 @@ export function repairComposition(rows, input) {
|
|
|
733
733
|
}
|
|
734
734
|
}
|
|
735
735
|
|
|
736
|
-
export async function deleteAssistant(rows, input,
|
|
736
|
+
export async function deleteAssistant(rows, input, remover) {
|
|
737
737
|
const id = input !== null && typeof input === 'object' && typeof input.id === 'string' ? input.id : ''
|
|
738
|
-
|
|
739
|
-
if (
|
|
740
|
-
|
|
738
|
+
const directory = assistantDir(rows, id)
|
|
739
|
+
if (directory === undefined) return unknownAssistant(id)
|
|
740
|
+
// `remover` 是**按后端选出来的**删除入口(见 {@link presetRemover}):旧线是平台的
|
|
741
|
+
// `agentPresets.remove()`,新线是声明式后端的 dispose + 删目录。两边都没有时才是真的做不到。
|
|
742
|
+
if (remover === undefined || remover === null || typeof remover.remove !== 'function') {
|
|
743
|
+
return { ok: false, code: 'noRemoveApi', error: '当前 DSH 版本没有可用的删除入口(agentPresets.remove() 与声明式后端都不可用),无法删除。' }
|
|
741
744
|
}
|
|
742
745
|
// 文案用**显示名**,不用内部目录 id(复制/删除的状态行曾把 id 暴露给用户,审阅点名)。
|
|
743
746
|
const displayName = (() => {
|
|
@@ -745,9 +748,17 @@ export async function deleteAssistant(rows, input, agentPresets) {
|
|
|
745
748
|
return typeof row?.name === 'string' && row.name.trim() !== '' ? row.name : id
|
|
746
749
|
})()
|
|
747
750
|
try {
|
|
748
|
-
await
|
|
751
|
+
const outcome = await remover.remove(id, directory)
|
|
752
|
+
if (outcome !== null && typeof outcome === 'object' && outcome.ok === false) {
|
|
753
|
+
// 字面量 code(不用三元表达式):test/editor-route.test.mjs 会扫 `code:` 后面的字符串字面量,
|
|
754
|
+
// 要求每个都在词典里中英各一条 —— 三元里的 'string' 会被它当成一个 code。
|
|
755
|
+
if (outcome.code === 'noDirectory') {
|
|
756
|
+
return { ok: false, code: 'noDirectory', params: { path: directory }, error: '删除失败:找不到「' + displayName + '」的目录。' }
|
|
757
|
+
}
|
|
758
|
+
return { ok: false, code: 'deleteFailed', params: { detail: describe(outcome.code ?? 'unknown'), path: directory }, error: '删除失败:' + describe(outcome.code ?? 'unknown') }
|
|
759
|
+
}
|
|
749
760
|
} catch (error) {
|
|
750
|
-
return { ok: false, code: 'deleteFailed', params: { detail: describe(error) }, error: '删除失败:' + describe(error) }
|
|
761
|
+
return { ok: false, code: 'deleteFailed', params: { detail: describe(error), path: directory }, error: '删除失败:' + describe(error) }
|
|
751
762
|
}
|
|
752
763
|
return { ok: true, id, code: 'deleted', params: { name: displayName }, note: '已删除「' + displayName + '」。正在使用它的会话不受影响;新建会话时不再出现。' }
|
|
753
764
|
}
|
|
@@ -912,6 +923,18 @@ export function apply(ctx) {
|
|
|
912
923
|
? createDeclarativeBackend({ scope, log: console.log, warn: console.error })
|
|
913
924
|
: null
|
|
914
925
|
|
|
926
|
+
/**
|
|
927
|
+
* 删除入口,按后端选。新线上服务**没有** `remove()`(唯一手段是 `register()` 的 disposer),
|
|
928
|
+
* 所以由声明式后端自己 dispose + 删目录;旧线继续用平台的文件系统语义。
|
|
929
|
+
*
|
|
930
|
+
* 这里返回 `undefined` 只发生在"两套都没有"的线上,那时才允许报 `noRemoveApi`。
|
|
931
|
+
*/
|
|
932
|
+
const presetRemover = () => {
|
|
933
|
+
if (declarative !== null) return { remove: (id, dir) => declarative.remove(id, dir) }
|
|
934
|
+
if (typeof scope.agentPresets?.remove === 'function') return { remove: (id) => scope.agentPresets.remove(id) }
|
|
935
|
+
return undefined
|
|
936
|
+
}
|
|
937
|
+
|
|
915
938
|
/** 原始 roster:只读、无副作用。同步逻辑走它,避免与 {@link roster} 互相递归。 */
|
|
916
939
|
const rawRoster = async () => {
|
|
917
940
|
const rows = await scope.agentPresets.list()
|
|
@@ -1134,7 +1157,7 @@ export function apply(ctx) {
|
|
|
1134
1157
|
const result = await serializedWrite('tree', async () => reorderAssistant(await roster(), parsed))
|
|
1135
1158
|
return json(result, result.ok === true ? 200 : 400)
|
|
1136
1159
|
}
|
|
1137
|
-
const result = await serializedWrite('delete:' + targetId, async () => deleteAssistant(await roster(), parsed,
|
|
1160
|
+
const result = await serializedWrite('delete:' + targetId, async () => deleteAssistant(await roster(), parsed, presetRemover()))
|
|
1138
1161
|
return json(result, result.ok === true ? 200 : 400)
|
|
1139
1162
|
} catch (error) {
|
|
1140
1163
|
// 兜底也要带 code:这是**意料之外**的异常,页面拿到的是 Node 的原始错误串,方向反着也一样糟
|
package/locales.mjs
CHANGED
|
@@ -108,6 +108,7 @@ export const zh = {
|
|
|
108
108
|
'api.metaWriteFailed': '写入 preset.yml 失败:{detail}',
|
|
109
109
|
'api.promptReadFailed': '读取提示词失败:{detail}',
|
|
110
110
|
'api.noRemoveApi': '当前 DSH 版本没有 agentPresets.remove(),无法删除。',
|
|
111
|
+
'api.noDirectory': '删除失败:本机找不到这个助手的目录,页面上的列表可能是旧的。刷新一次再试。',
|
|
111
112
|
'api.deleteFailed': '删除失败:{detail}',
|
|
112
113
|
'api.versionMissing': '找不到这个版本(历史可能已被上限裁剪)。',
|
|
113
114
|
'api.badJson': '请求体不是合法 JSON',
|
|
@@ -339,6 +340,7 @@ export const en = {
|
|
|
339
340
|
'api.metaWriteFailed': 'Writing preset.yml failed: {detail}',
|
|
340
341
|
'api.promptReadFailed': 'Reading the prompt failed: {detail}',
|
|
341
342
|
'api.noRemoveApi': 'This DSH build has no agentPresets.remove(), so deleting is unavailable.',
|
|
343
|
+
'api.noDirectory': "Delete failed: this assistant's directory is gone from disk, so the list on screen is stale. Refresh and try again.",
|
|
342
344
|
'api.deleteFailed': 'Delete failed: {detail}',
|
|
343
345
|
'api.versionMissing': 'That version is gone (the history is capped).',
|
|
344
346
|
'api.badJson': 'The request body is not valid JSON',
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-custom-mode",
|
|
3
|
-
"version": "1.9.
|
|
3
|
+
"version": "1.9.15",
|
|
4
4
|
"description": "Custom modes and custom prompts for DeepSeek Harness (dsh): edit a mode's system prompt on the settings page (it takes effect on the next model step), pick its base mode, switch plugin rows one by one, and keep several assistants side by side — coding, chat or roleplay (RP) personas (multi-mode / multi-agent).",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -12,7 +12,8 @@
|
|
|
12
12
|
* 相对路径;旧线上它相对 preset 目录解析,而 `register()` 在 profile 的 baseUrl 下挂载。
|
|
13
13
|
* 实测:相对路径 → `broken: … never started`;绝对 `file://` URL → 正常 mount。
|
|
14
14
|
* ⇒ `toPluginRows()` 必须把相对说明符改写成绝对 URL。
|
|
15
|
-
* 2. **`register()` 的返回值是 disposer**,注销唯一手段;服务上没有 `remove()
|
|
15
|
+
* 2. **`register()` 的返回值是 disposer**,注销唯一手段;服务上没有 `remove()` —— 所以"删除助手"
|
|
16
|
+
* 由本后端的 {@link remove} 自己做(dispose + 删目录),宿主那半不再要求平台提供 `remove()`。
|
|
16
17
|
* 3. **同一个 id 重复注册会抛 `Duplicate agent preset`**
|
|
17
18
|
* ⇒ 重新挂载前必须先 dispose 旧的。
|
|
18
19
|
*
|
|
@@ -22,7 +23,7 @@
|
|
|
22
23
|
* `agent.cordis.yml` 仍然写盘 —— 它是用户可见、可迁移、可手改的真相,也是旧线唯一的输入。
|
|
23
24
|
*/
|
|
24
25
|
|
|
25
|
-
import { existsSync, readFileSync } from 'node:fs'
|
|
26
|
+
import { existsSync, readFileSync, rmSync } from 'node:fs'
|
|
26
27
|
import { join } from 'node:path'
|
|
27
28
|
import { pathToFileURL } from 'node:url'
|
|
28
29
|
import { parseComposition } from './parse-composition.mjs'
|
|
@@ -163,6 +164,29 @@ export function createDeclarativeBackend({ scope, log = console.log, warn = cons
|
|
|
163
164
|
for (const id of [...mounted.keys()]) await unmount(id)
|
|
164
165
|
}
|
|
165
166
|
|
|
167
|
+
/**
|
|
168
|
+
* 删除一个助手:**先注销,再删目录**。
|
|
169
|
+
*
|
|
170
|
+
* 这条线上服务没有 `remove()`(唯一手段是 `register()` 返回的 disposer),所以"删除"必须由本插件
|
|
171
|
+
* 自己完成 —— 而这正是设置页在 0.1.7 上**根本删不掉助手**的原因:宿主那半只认 `agentPresets.remove()`,
|
|
172
|
+
* 拿不到就回一个类型化的 `noRemoveApi`,页面弹完确认框只显示"删除失败"。
|
|
173
|
+
*
|
|
174
|
+
* 顺序不能反:先注销,选择器立刻不再提供它;再删目录。删目录失败时**如实抛出**(调用方转成
|
|
175
|
+
* `deleteFailed` 并把路径带给用户),而不是报告"已删除"却把目录留在磁盘上 —— 那样下次同步
|
|
176
|
+
* 会把它重新挂回来,用户看到的是"删了又回来了"。
|
|
177
|
+
*
|
|
178
|
+
* @param {string} id - assistant id.
|
|
179
|
+
* @param {string} [dir] - its directory; when omitted, the one remembered at mount time is used.
|
|
180
|
+
*/
|
|
181
|
+
async function remove(id, dir) {
|
|
182
|
+
const remembered = mounted.get(id)?.dir
|
|
183
|
+
await unmount(id)
|
|
184
|
+
const target = typeof dir === 'string' && dir !== '' ? dir : remembered
|
|
185
|
+
if (typeof target !== 'string' || target === '') return { ok: false, code: 'noDirectory', id }
|
|
186
|
+
rmSync(target, { recursive: true, force: true })
|
|
187
|
+
return { ok: true, id, dir: target }
|
|
188
|
+
}
|
|
189
|
+
|
|
166
190
|
/**
|
|
167
191
|
* Fetch the shipped base compositions the host declares, as entry-list YAML.
|
|
168
192
|
*
|
|
@@ -205,6 +229,7 @@ export function createDeclarativeBackend({ scope, log = console.log, warn = cons
|
|
|
205
229
|
sync,
|
|
206
230
|
mountOne,
|
|
207
231
|
unmount,
|
|
232
|
+
remove,
|
|
208
233
|
disposeAll,
|
|
209
234
|
fetchBaseCompositions,
|
|
210
235
|
mountedIds: () => [...mounted.keys()],
|