dsh-custom-mode 2.2.5 → 2.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/README.i18n.yaml +2 -2
- package/README.md +76 -57
- package/README.zh.md +76 -57
- package/assistants.mjs +8 -2
- package/atomic.mjs +156 -56
- package/client.js +653 -227
- package/composition.mjs +5 -4
- package/index.mjs +86 -47
- package/locales.mjs +138 -20
- package/package.json +4 -4
- package/preset/prompt-reader.mjs +35 -2
- package/preset/prompt-tool.mjs +1 -0
- package/preset/prompt.md +0 -2
- package/preset-backend/declarative.mjs +43 -15
- package/seed.mjs +5 -1
package/composition.mjs
CHANGED
|
@@ -132,9 +132,10 @@ export const ROW_META = {
|
|
|
132
132
|
'tool-cordis': { label: 'Cordis 运行时工具', note: '可读写 harness 运行时' },
|
|
133
133
|
'tool-plugin-manager': {
|
|
134
134
|
label: '插件管理(安装 / 启停)',
|
|
135
|
-
note: '
|
|
135
|
+
note: '供模型安装或启停插件;出厂状态由当前宿主的基础模式声明决定',
|
|
136
136
|
},
|
|
137
137
|
'tool-presentation': { label: 'PTC 工具呈现' },
|
|
138
|
+
'tool-executor-ptc': { label: 'PTC 程序执行器', note: '通过 run_code 执行程序并调用工具,同时向模型提供工具 SDK' },
|
|
138
139
|
present: { label: '交付文件(present)' },
|
|
139
140
|
'custom-prompt-tool': { label: 'custom_prompt 工具', note: '关掉后无法用对话改提示词(设置页仍可用)' },
|
|
140
141
|
'persistent-shell': { label: '持久 Shell' },
|
|
@@ -241,9 +242,9 @@ export function readBaseComposition(modeId) {
|
|
|
241
242
|
* First writer, in `BASE_MODE_IDS` order (`standard` → `ptc` → `minimal` → `cordis`). The order is not
|
|
242
243
|
* cosmetic: `standard` is the mode this product's own preset is derived from, so a row declared in more
|
|
243
244
|
* than one mode keeps **standard's text and standard's shipped default**. The visible consequence is
|
|
244
|
-
* deliberate
|
|
245
|
-
*
|
|
246
|
-
*
|
|
245
|
+
* deliberate: the union follows this host's `standard` declaration, including any condition on
|
|
246
|
+
* `tool-plugin-manager`. Its shipped state varies by host version; do not hard-code it as disabled.
|
|
247
|
+
* Enabling a row merely because another mode does so would change the selected baseline.
|
|
247
248
|
*
|
|
248
249
|
* ## Nothing is cached
|
|
249
250
|
*
|
package/index.mjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Host half: the 「自定义模式」 settings page — an ASSISTANT MANAGER.
|
|
3
3
|
*
|
|
4
|
-
* It registers
|
|
4
|
+
* It registers eight exact routes on the platform's shared `/api` channel; the browser half calls
|
|
5
5
|
* them with `fetch`:
|
|
6
6
|
*
|
|
7
7
|
* GET /api/custom-mode — every assistant this feature manages.
|
|
@@ -20,6 +20,7 @@
|
|
|
20
20
|
* POST /api/custom-mode/reorder — { id, direction }: move one assistant up/down in the
|
|
21
21
|
* picker order by writing `order` into each `preset.yml`.
|
|
22
22
|
* POST /api/custom-mode/repair — { id }: turn off the rows this dsh line cannot resolve.
|
|
23
|
+
* POST /api/custom-mode/sync — {}: retry registry synchronization without repeating a write.
|
|
23
24
|
*
|
|
24
25
|
* **Why `/api` and not the raw `webServer` table**: the carrier that owns the `/api` channel applies the
|
|
25
26
|
* platform's trust and authentication policy — loopback/`trustedHosts` Host check, `Sec-Fetch-Site`,
|
|
@@ -31,7 +32,7 @@
|
|
|
31
32
|
* route answered 401; a plain form post needs no preflight, so CORS would not have helped either). The
|
|
32
33
|
* registered paths are absolute *including* `/api`, which is what the platform's own packages pass.
|
|
33
34
|
*
|
|
34
|
-
* **Why exact routes rather than one prefix**: the Fetch registry matches exact paths.
|
|
35
|
+
* **Why exact routes rather than one prefix**: the Fetch registry matches exact paths. Eight registrations
|
|
35
36
|
* cost nothing and each declares the methods it owns, so the method table doubles as the guarantee that a
|
|
36
37
|
* prefetched `GET …/create` cannot create anything — that method is simply not registered for that path.
|
|
37
38
|
*
|
|
@@ -101,6 +102,7 @@ const DELETE_PATH = ROUTE_PATH + '/delete'
|
|
|
101
102
|
const REORDER_PATH = ROUTE_PATH + '/reorder'
|
|
102
103
|
const REPAIR_PATH = ROUTE_PATH + '/repair'
|
|
103
104
|
const HISTORY_PATH = ROUTE_PATH + '/history'
|
|
105
|
+
const SYNC_PATH = ROUTE_PATH + '/sync'
|
|
104
106
|
|
|
105
107
|
/** Which verbs each endpoint answers. `undefined` for a path means 404. */
|
|
106
108
|
const METHODS = {
|
|
@@ -111,6 +113,7 @@ const METHODS = {
|
|
|
111
113
|
[DELETE_PATH]: ['POST'],
|
|
112
114
|
[REORDER_PATH]: ['POST'],
|
|
113
115
|
[REPAIR_PATH]: ['POST'],
|
|
116
|
+
[SYNC_PATH]: ['POST'],
|
|
114
117
|
}
|
|
115
118
|
|
|
116
119
|
/** 只告警一次:避免每个请求都刷同一行日志。 */
|
|
@@ -154,6 +157,7 @@ const VARIABLE_NAME = /^[a-z][a-z0-9_]*$/
|
|
|
154
157
|
* Mirrors `checkPromptText` in the preset's `prompt-tool.mjs`; the duplication is
|
|
155
158
|
* deliberate so neither side depends on the other's install location.
|
|
156
159
|
*/
|
|
160
|
+
// cwd is supplied by the old loop, or by prompt-reader's scoped workingDirectory compatibility provider.
|
|
157
161
|
const KNOWN_VARIABLES = ['model', 'cwd', 'provider']
|
|
158
162
|
|
|
159
163
|
/**
|
|
@@ -372,7 +376,8 @@ export function brokenRowIds(message) {
|
|
|
372
376
|
export function readState(rows, id, options = {}) {
|
|
373
377
|
const directory = assistantDir(rows, id)
|
|
374
378
|
if (directory === undefined) return unknownAssistant(id)
|
|
375
|
-
restoreBackups(directory)
|
|
379
|
+
const recovery = restoreBackups(directory)
|
|
380
|
+
if (recovery.ok !== true) return { ok: false, code: 'recoveryRequired', params: { path: directory, detail: recovery.detail }, error: '助手文件需要恢复:' + recovery.detail }
|
|
376
381
|
const composition = compositionFile(directory)
|
|
377
382
|
if (!existsSync(composition)) return { ok: false, code: 'compositionMissing', params: { path: composition }, error: '找不到组成文件:' + composition }
|
|
378
383
|
const text = readFileSync(composition, 'utf8')
|
|
@@ -463,7 +468,7 @@ export function readState(rows, id, options = {}) {
|
|
|
463
468
|
}
|
|
464
469
|
|
|
465
470
|
/**
|
|
466
|
-
* The list payload:
|
|
471
|
+
* The list payload: managed assistants, their root, and the running plugin version.
|
|
467
472
|
*
|
|
468
473
|
* @param {Array<object>} rows - the current roster.
|
|
469
474
|
*/
|
|
@@ -473,7 +478,7 @@ export function readList(rows) {
|
|
|
473
478
|
const list = assistantsFromRoster(rows).map((item) =>
|
|
474
479
|
typeof item.broken === 'string' ? { ...item, brokenRows: brokenRowIds(item.broken) } : item,
|
|
475
480
|
)
|
|
476
|
-
return { ok: true, assistants: list, postures: publicPostures(), root: userPresetRoot(rows) }
|
|
481
|
+
return { ok: true, assistants: list, postures: publicPostures(), root: userPresetRoot(rows), version: PLUGIN_VERSION }
|
|
477
482
|
}
|
|
478
483
|
|
|
479
484
|
/**
|
|
@@ -1217,6 +1222,9 @@ export function apply(ctx) {
|
|
|
1217
1222
|
backendVerdict.id === BACKEND_DECLARATIVE
|
|
1218
1223
|
? createDeclarativeBackend({ scope, log: console.log, warn: console.error })
|
|
1219
1224
|
: null
|
|
1225
|
+
if (declarative !== null) {
|
|
1226
|
+
scope.effect(() => () => declarative.disposeAll(), 'custom-mode.presets')
|
|
1227
|
+
}
|
|
1220
1228
|
|
|
1221
1229
|
/**
|
|
1222
1230
|
* 删除入口,按后端选。新线上服务**没有** `remove()`(唯一手段是 `register()` 的 disposer),
|
|
@@ -1299,7 +1307,7 @@ export function apply(ctx) {
|
|
|
1299
1307
|
console.error('custom-mode: 取回出厂组成时出错(已忽略): ' + describe(error))
|
|
1300
1308
|
}
|
|
1301
1309
|
}
|
|
1302
|
-
if (declarative === null) return
|
|
1310
|
+
if (declarative === null) return { ok: true, failures: [] }
|
|
1303
1311
|
try {
|
|
1304
1312
|
const rows = await rawRoster()
|
|
1305
1313
|
const targets = []
|
|
@@ -1343,8 +1351,10 @@ export function apply(ctx) {
|
|
|
1343
1351
|
if (failed.length > 0) {
|
|
1344
1352
|
console.error(`custom-mode: ${failed.length} 个助手的 preset 未能挂载 —— 见上面的具体原因。`)
|
|
1345
1353
|
}
|
|
1354
|
+
return { ok: failed.length === 0, failures: failed }
|
|
1346
1355
|
} catch (error) {
|
|
1347
|
-
console.error('custom-mode: 同步 preset
|
|
1356
|
+
console.error('custom-mode: 同步 preset 到注册表失败: ' + describe(error))
|
|
1357
|
+
return { ok: false, code: 'syncFailed', failures: [{ id: '', error: describe(error) }] }
|
|
1348
1358
|
}
|
|
1349
1359
|
}
|
|
1350
1360
|
|
|
@@ -1356,23 +1366,39 @@ export function apply(ctx) {
|
|
|
1356
1366
|
* 写操作之后的重同步由 `resyncPresets` 负责,不该让每个请求都等一遍。
|
|
1357
1367
|
*/
|
|
1358
1368
|
let startupSync = null
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
1369
|
+
let latestSync = { ok: true, failures: [] }
|
|
1370
|
+
const synchronize = async () => {
|
|
1371
|
+
const outcome = await doSync()
|
|
1372
|
+
latestSync = outcome
|
|
1373
|
+
return outcome
|
|
1374
|
+
}
|
|
1375
|
+
// Public synchronization evidence contains only bounded preset identifiers and
|
|
1376
|
+
// typed codes. Raw registration errors stay in host logs.
|
|
1377
|
+
const registrationSync = (assistantId) => {
|
|
1378
|
+
const failures = assistantId === undefined ? latestSync.failures
|
|
1379
|
+
: latestSync.failures.filter(({ id }) => typeof id !== 'string' || id === '' || id === assistantId)
|
|
1380
|
+
if (latestSync.ok === true || failures.length === 0) return { ok: true, code: 'synchronized', ids: [] }
|
|
1381
|
+
return {
|
|
1382
|
+
ok: false,
|
|
1383
|
+
code: 'presetRegistrationSyncFailed',
|
|
1384
|
+
ids: [...new Set(failures.map(({ id }) => id)
|
|
1385
|
+
.filter((id) => typeof id === 'string' && /^[A-Za-z0-9_.-]{1,128}$/.test(id)))].slice(0, 32),
|
|
1386
|
+
restartSuggested: failures.some(({ code }) => code === 'registrationConflict' || code === 'registrationCleanupFailed'),
|
|
1364
1387
|
}
|
|
1388
|
+
}
|
|
1389
|
+
const roster = async () => {
|
|
1390
|
+
if (startupSync !== null) await startupSync
|
|
1365
1391
|
return rawRoster()
|
|
1366
1392
|
}
|
|
1367
1393
|
|
|
1368
|
-
/**
|
|
1369
|
-
const resyncPresets =
|
|
1370
|
-
startupSync =
|
|
1371
|
-
|
|
1394
|
+
/** Called inside the write queue; readers also await the current synchronization. */
|
|
1395
|
+
const resyncPresets = () => {
|
|
1396
|
+
startupSync = synchronize()
|
|
1397
|
+
return startupSync
|
|
1372
1398
|
}
|
|
1373
1399
|
|
|
1374
1400
|
// 启动即同步:新线不是"写盘即生效",第一次进来必须先注册。
|
|
1375
|
-
startupSync =
|
|
1401
|
+
startupSync = synchronize()
|
|
1376
1402
|
|
|
1377
1403
|
/**
|
|
1378
1404
|
* One Fetch-shaped handler for the whole surface.
|
|
@@ -1391,16 +1417,30 @@ export function apply(ctx) {
|
|
|
1391
1417
|
const json = (value, status = 200) =>
|
|
1392
1418
|
Response.json(value, { status, headers: { 'cache-control': 'no-store' } })
|
|
1393
1419
|
|
|
1420
|
+
const syncFailureParams = (outcome) => ({
|
|
1421
|
+
ids: outcome.failures.map((failure) => failure.id).filter(Boolean).join(', '),
|
|
1422
|
+
detail: outcome.failures.map((failure) => failure.error ?? failure.code ?? 'unknown').join('; '),
|
|
1423
|
+
})
|
|
1424
|
+
|
|
1394
1425
|
const handle = async (request, pathname) => {
|
|
1395
1426
|
try {
|
|
1396
1427
|
const url = new URL(request.url)
|
|
1397
1428
|
if (request.method === 'GET' && pathname === ROUTE_PATH) {
|
|
1398
1429
|
await ensureShipped()
|
|
1399
|
-
|
|
1430
|
+
const rows = await roster()
|
|
1431
|
+
return json({ ...readList(rows), registrationSync: registrationSync() })
|
|
1400
1432
|
}
|
|
1401
1433
|
if (request.method === 'GET' && pathname === STATE_PATH) {
|
|
1402
1434
|
await ensureShipped()
|
|
1403
|
-
|
|
1435
|
+
const state = readState(await roster(), url.searchParams.get('id') ?? '', { factoryPrompt: packagedPrompt() })
|
|
1436
|
+
const synchronization = registrationSync(state.id)
|
|
1437
|
+
return json({
|
|
1438
|
+
...state,
|
|
1439
|
+
registrationSync: synchronization,
|
|
1440
|
+
...(state.ok === true && synchronization.ok === false
|
|
1441
|
+
? { warnings: [...state.warnings, synchronization.restartSuggested ? 'presetRegistrationRestartSuggested' : 'presetRegistrationSyncFailed'] }
|
|
1442
|
+
: {}),
|
|
1443
|
+
})
|
|
1404
1444
|
}
|
|
1405
1445
|
|
|
1406
1446
|
if (request.method === 'GET' && pathname === HISTORY_PATH) {
|
|
@@ -1436,26 +1476,32 @@ export function apply(ctx) {
|
|
|
1436
1476
|
return json({ ok: false, code: 'badJson', error: '请求体不是合法 JSON' }, 400)
|
|
1437
1477
|
}
|
|
1438
1478
|
await ensureShipped()
|
|
1439
|
-
|
|
1440
|
-
//
|
|
1441
|
-
|
|
1442
|
-
const
|
|
1443
|
-
|
|
1444
|
-
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
-
|
|
1448
|
-
|
|
1449
|
-
|
|
1450
|
-
|
|
1451
|
-
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
const
|
|
1455
|
-
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
|
|
1479
|
+
// One queue owns the entire mutation, INCLUDING its registry projection. Releasing this lock
|
|
1480
|
+
// before sync allowed a later save to race an older registration and leave disk B / runtime A.
|
|
1481
|
+
return await serializedWrite('preset-write', async () => {
|
|
1482
|
+
const rows = await roster()
|
|
1483
|
+
if (pathname === SYNC_PATH) {
|
|
1484
|
+
const synced = await resyncPresets()
|
|
1485
|
+
if (synced.ok === true) return json({ ok: true, code: 'synchronized', synchronized: true })
|
|
1486
|
+
return json({ ok: false, code: 'syncFailed', persisted: false, synchronized: false, params: syncFailureParams(synced) }, 503)
|
|
1487
|
+
}
|
|
1488
|
+
const result = pathname === STATE_PATH ? saveState(rows, parsed)
|
|
1489
|
+
: pathname === CREATE_PATH ? createAssistant(rows, parsed)
|
|
1490
|
+
: pathname === REPAIR_PATH ? repairComposition(rows, parsed)
|
|
1491
|
+
: pathname === REORDER_PATH ? reorderAssistant(rows, parsed)
|
|
1492
|
+
: await deleteAssistant(rows, parsed, presetRemover())
|
|
1493
|
+
if (result.ok !== true) return json(result, 400)
|
|
1494
|
+
const synced = await resyncPresets()
|
|
1495
|
+
if (synced.ok !== true) {
|
|
1496
|
+
return json({
|
|
1497
|
+
...result, ok: false, code: 'savedButSyncFailed', operationCode: result.code,
|
|
1498
|
+
persisted: true, synchronized: false, params: { ...result.params, ...syncFailureParams(synced) },
|
|
1499
|
+
error: '文件已写入,但预设同步失败;请只重试生效,不要重复原操作。',
|
|
1500
|
+
note: '文件已写入,但预设同步失败;请只重试生效,不要重复原操作。',
|
|
1501
|
+
}, 503)
|
|
1502
|
+
}
|
|
1503
|
+
return json({ ...result, persisted: true, synchronized: true })
|
|
1504
|
+
})
|
|
1459
1505
|
} catch (error) {
|
|
1460
1506
|
// 兜底也要带 code:这是**意料之外**的异常,页面拿到的是 Node 的原始错误串,方向反着也一样糟
|
|
1461
1507
|
// ——中文界面里会蹦出一句英文。带 code 后页面用自己的词典渲染,原始串只作为 `detail` 参数。
|
|
@@ -1472,14 +1518,7 @@ export function apply(ctx) {
|
|
|
1472
1518
|
path: API_PREFIX + pathname,
|
|
1473
1519
|
methods: [...methods],
|
|
1474
1520
|
requestBody: 'buffered',
|
|
1475
|
-
fetch:
|
|
1476
|
-
const response = await handle(request, pathname)
|
|
1477
|
-
// 写成功之后,把注册表同步到磁盘的新状态。
|
|
1478
|
-
// 旧线上 `declarative` 是 null,这里恒为空操作;新线上没有它,用户在设置页
|
|
1479
|
-
// 新建/改名/删除的助手就不会出现在选择器里 —— 是本插件最容易被漏掉的一步。
|
|
1480
|
-
if (declarative !== null && request.method !== 'GET' && response.ok) await resyncPresets()
|
|
1481
|
-
return response
|
|
1482
|
-
},
|
|
1521
|
+
fetch: (request) => handle(request, pathname),
|
|
1483
1522
|
}),
|
|
1484
1523
|
'custom-mode.route' + pathname,
|
|
1485
1524
|
)
|
package/locales.mjs
CHANGED
|
@@ -16,17 +16,72 @@
|
|
|
16
16
|
|
|
17
17
|
/** Simplified Chinese dictionary — the key source of truth. */
|
|
18
18
|
export const zh = {
|
|
19
|
+
'update.heading': '插件更新',
|
|
20
|
+
'update.orphanHint': '以下助手已不在当前列表,但本页仍保留其未保存草稿,因此暂不能离页。',
|
|
21
|
+
'update.orphanName': '{name}(ID:{id})',
|
|
22
|
+
'update.orphanView': '查看并保留草稿',
|
|
23
|
+
'update.orphanHide': '收起草稿',
|
|
24
|
+
'update.orphanCopy': '下方包含完整提示词、名称、描述、模式和覆盖项,可选中复制到本地文件保留。放弃仅移除本页内存草稿,不修改磁盘文件。',
|
|
25
|
+
'update.orphanDiscard': '放弃此草稿…',
|
|
26
|
+
'update.orphanConfirm': '确认放弃“{name}”(ID:{id})的未保存草稿?请先复制保留需要的内容。放弃后无法从本页恢复;磁盘文件不会改动。',
|
|
27
|
+
'update.orphanAcknowledge': '我已保留需要的内容,确认放弃后无法从本页恢复。',
|
|
28
|
+
'update.orphanChanged': '草稿或助手列表已变化,未放弃任何内容。请重新查看并确认。',
|
|
29
|
+
'update.orphanDiscarded': '已放弃所确认的本页草稿;磁盘文件未改动。',
|
|
30
|
+
'update.running': '运行中的插件版本:{version}',
|
|
31
|
+
'update.unknown': '未知',
|
|
32
|
+
'update.check': '检查更新',
|
|
33
|
+
'update.checking': '正在检查…',
|
|
34
|
+
'update.inPlugins': '在插件管理中更新',
|
|
35
|
+
'update.details': '打开插件详情',
|
|
36
|
+
'update.guide': '更新指引',
|
|
37
|
+
'update.idle': '仅在点击检查时查询宿主配置的包仓库;不会自动安装。',
|
|
38
|
+
'update.unavailable': '当前连接未提供原生版本检查能力。可打开插件详情或查看更新指引。',
|
|
39
|
+
'update.permission': '此操作缺少宿主授权,或权限服务暂不可用。',
|
|
40
|
+
'update.updatePermission': '可以查看版本,但当前连接没有更新权限。',
|
|
41
|
+
'update.available': '可用更新:{version}。安装由官方插件管理处理。',
|
|
42
|
+
'update.current': '此次检查未返回更高版本。',
|
|
43
|
+
'update.filtered': '发现较新版本,但没有符合官方默认条件的稳定兼容版本。',
|
|
44
|
+
'update.partial': '结果未覆盖全部新版本,暂未找到可推荐的更新。',
|
|
45
|
+
'update.omitted': '另有 {count} 个较新版本未列出。',
|
|
46
|
+
'update.disk': '磁盘安装版本:{version};它可能尚未在运行中加载。',
|
|
47
|
+
'update.managed': '此安装无法使用原生包仓库更新,请通过原来源或插件详情管理;不会自动切换来源。',
|
|
48
|
+
'update.notFound': '宿主插件列表中没有找到此插件,无法确认更新。',
|
|
49
|
+
'update.network': '宿主查询包仓库失败,请稍后重试;尚不能确认更新状态。',
|
|
50
|
+
'update.refused': '宿主未提供可用的版本列表,尚不能确认更新状态。',
|
|
51
|
+
'update.failed': '未能完成更新检查,请重试或打开插件详情查看。',
|
|
52
|
+
'update.finishOperation': '请等待当前操作完成后再离开此页。',
|
|
53
|
+
'update.finishRecovery': '请先完成重试读取或重试生效,再离开此页。',
|
|
54
|
+
'update.saveDrafts': '请先保存或主动放弃所有助手草稿及未提交的新建输入,再离开此页。',
|
|
55
|
+
'update.navigationFailed': '未能打开插件详情;草稿已保留,可稍后重试。',
|
|
56
|
+
'create.nextStep': '创建后可在上方继续编辑并保存;使用时,在新会话的模式菜单选择它。',
|
|
57
|
+
'btn.retryRead': '重试读取',
|
|
58
|
+
'btn.retrySync': '重试生效',
|
|
59
|
+
'api.savedButSyncFailed': '文件已保存,但这些助手尚未生效:{ids}。请点「重试生效」,无需重复原操作。',
|
|
60
|
+
'api.syncFailed': '这些助手仍未生效:{ids}。请重试;仍失败时查看宿主日志。',
|
|
61
|
+
'api.presetRegistrationSyncFailed': '尚未确认已保存的助手配置在宿主注册表中生效,请处理已有告警或查看宿主日志。',
|
|
62
|
+
'api.synchronized': '配置已生效。新会话请从模式菜单选择助手。',
|
|
63
|
+
'api.recoveryRequired': '助手文件需要恢复:{path}。请保留目录中的备份,并查看宿主日志中的恢复说明。',
|
|
64
|
+
'msg.completedReadFailed': '{result} 页面刷新失败。请点「重试读取」,无需重复原操作。',
|
|
65
|
+
'msg.synchronized': '配置已生效。',
|
|
66
|
+
'msg.syncFailed': '重新应用配置失败,请重试。',
|
|
67
|
+
'msg.loaded': '已载入到编辑器。尚未保存。',
|
|
68
|
+
'status.reading': '正在读取…',
|
|
69
|
+
'status.saving': '正在保存…',
|
|
70
|
+
'status.creating': '正在创建助手…',
|
|
71
|
+
'status.deleting': '正在删除助手…',
|
|
72
|
+
'status.reordering': '正在保存顺序…',
|
|
73
|
+
'status.repairing': '正在修复…',
|
|
74
|
+
'status.importing': '正在读取导入文件…',
|
|
75
|
+
'status.synchronizing': '正在应用配置…',
|
|
19
76
|
nav: '自定义模式',
|
|
20
77
|
|
|
21
78
|
// ── 助手管理 ──────────────────────────────────────────────────────────────
|
|
22
79
|
'assistant.heading': '助手',
|
|
23
|
-
'assistant.hint': '
|
|
24
|
-
'posture.hint': '
|
|
80
|
+
'assistant.hint': '选择要编辑的助手。每位助手独立保存系统提示词、基础模式和插件开关。',
|
|
81
|
+
'posture.hint': '选择起步姿态并命名,再点「新增助手」。',
|
|
25
82
|
'tree.posture': '起步',
|
|
26
|
-
'tree.base': '
|
|
27
|
-
'tree.
|
|
28
|
-
'tree.baseSkip': '这一支点不了。建好后到「当前助手」里改。',
|
|
29
|
-
'tree.baseLocked': '只有官方标准模式是亮的。其余四档是暗的,新建时点不了。',
|
|
83
|
+
'tree.base': '初始基础模式',
|
|
84
|
+
'tree.baseLocked': '新建助手从官方标准模式起步。建好后,可在「当前助手」调整基础模式。',
|
|
30
85
|
'posture.develop.label': '开发',
|
|
31
86
|
'posture.develop.note': '官方标准模式,工具按出厂。',
|
|
32
87
|
'posture.write.label': '写作',
|
|
@@ -34,7 +89,7 @@ export const zh = {
|
|
|
34
89
|
'posture.chat.label': '聊天',
|
|
35
90
|
'posture.chat.note': '官方标准模式。文件和终端关闭。',
|
|
36
91
|
'editor.heading': '当前助手',
|
|
37
|
-
'editor.hint': '
|
|
92
|
+
'editor.hint': '正在编辑「{name}」。这里的选择只决定编辑对象;新会话请从模式菜单选择助手。',
|
|
38
93
|
'assistant.empty': '还没有助手,用下面的输入框新建一个。',
|
|
39
94
|
'assistant.loadingList': '正在读取助手列表…',
|
|
40
95
|
'assistant.newPlaceholder': '新助手的名字(例如:写作助手)',
|
|
@@ -57,7 +112,7 @@ export const zh = {
|
|
|
57
112
|
'btn.resetHint': '把编辑器换回此助手新建时保存的提示词。只改草稿,保存后才写入。',
|
|
58
113
|
'btn.delete': '删除',
|
|
59
114
|
'btn.cancel': '取消',
|
|
60
|
-
'msg.created': '
|
|
115
|
+
'msg.created': '已创建。可继续编辑;使用时请在新会话的模式菜单选择它。',
|
|
61
116
|
'msg.duplicated': '已复制。两份配置彼此独立。',
|
|
62
117
|
'msg.deleted': '已删除。',
|
|
63
118
|
'msg.createFailed': '创建失败',
|
|
@@ -97,7 +152,7 @@ export const zh = {
|
|
|
97
152
|
'api.saved': '已保存({name},{mode})。下一步组装时会重读已保存的提示词。',
|
|
98
153
|
'api.conflict': '磁盘上的提示词已经变了,这次没有写入。先重新读取,再决定要不要覆盖。',
|
|
99
154
|
'api.savedWithExclusiveRows': '已保存({name},{mode})。已关闭不能同时启用的行:{rows}。',
|
|
100
|
-
'api.created': '已创建「{name}
|
|
155
|
+
'api.created': '已创建「{name}」。可继续编辑;使用时请在新会话的模式菜单选择它。',
|
|
101
156
|
'api.savedPrompt': '已保存系统提示词({name})。下一步组装时会重读这个文件。',
|
|
102
157
|
'api.duplicated': '已从「{from}」复制。两份互不影响。',
|
|
103
158
|
'api.deleted': '已删除「{name}」。正在使用它的会话不受影响。',
|
|
@@ -133,6 +188,8 @@ export const zh = {
|
|
|
133
188
|
'warn.approvalGateMissing': '当前版本没有审批事件。会话内改写提示词不会请求确认。',
|
|
134
189
|
'warn.unresolvableRows': '有的行在本机无法启动,此模式不会出现在新会话菜单里。点「按本线修复」只关闭那些行。',
|
|
135
190
|
'warn.presetBroken': '此模式无法启动,不会出现在新会话菜单里。点「按本线修复」。原因见下一行。',
|
|
191
|
+
'warn.presetRegistrationSyncFailed': '尚未确认这位助手已保存的配置在宿主注册表中生效。请先处理已有告警或查看宿主日志;无需重复保存。',
|
|
192
|
+
'warn.presetRegistrationRestartSuggested': '尚未确认这位助手已保存的配置在宿主注册表中生效。如刚更新或恢复插件,建议重启 DeepSeek Harness 后重新检查;无需重复保存。',
|
|
136
193
|
'warn.badPromptVariable': '提示词里有不可用的变量。改掉后再保存,否则此模式的请求会失败。',
|
|
137
194
|
'warn.duplicateRowIds': '组成文件里有重复的行 id。开关只作用于第一处。',
|
|
138
195
|
'warn.predicateFailed': '有一行的平台条件无法计算,已按关闭处理。',
|
|
@@ -186,7 +243,7 @@ export const zh = {
|
|
|
186
243
|
'tag.followPlatform': '随平台',
|
|
187
244
|
|
|
188
245
|
'btn.save': '保存',
|
|
189
|
-
'btn.saving': '
|
|
246
|
+
'btn.saving': '保存中…',
|
|
190
247
|
'btn.reload': '重新读取',
|
|
191
248
|
'btn.reloadDiscard': '放弃修改并重新读取',
|
|
192
249
|
'btn.reloadConfirm': '放弃未保存的修改并重新读取?',
|
|
@@ -250,6 +307,8 @@ export const zh = {
|
|
|
250
307
|
'row.tool-plugin-manager.label': '插件管理(安装 / 启停)',
|
|
251
308
|
'row.tool-plugin-manager.note': '供模型安装或开关插件。',
|
|
252
309
|
'row.tool-presentation.label': 'PTC 工具呈现',
|
|
310
|
+
'row.tool-executor-ptc.label': 'PTC 程序执行器',
|
|
311
|
+
'row.tool-executor-ptc.note': '通过 run_code 执行程序并调用工具,同时向模型提供工具 SDK。',
|
|
253
312
|
'row.present.label': '交付文件(present)',
|
|
254
313
|
'row.command-goal.label': '目标命令',
|
|
255
314
|
'row.tool-goal.label': '目标',
|
|
@@ -283,17 +342,72 @@ export const zh = {
|
|
|
283
342
|
|
|
284
343
|
/** English dictionary; the parity test keeps its key set equal to {@link zh}. */
|
|
285
344
|
export const en = {
|
|
345
|
+
'update.heading': 'Plugin updates',
|
|
346
|
+
'update.orphanHint': 'These assistants left the current list, but this page still holds their unsaved drafts. Resolve them before leaving.',
|
|
347
|
+
'update.orphanName': '{name} (ID: {id})',
|
|
348
|
+
'update.orphanView': 'View and keep draft',
|
|
349
|
+
'update.orphanHide': 'Hide draft',
|
|
350
|
+
'update.orphanCopy': 'The text below includes the full prompt, name, description, mode and overrides. Select and copy it to a local file to keep it. Discarding only removes this page\'s in-memory draft; files on disk are unchanged.',
|
|
351
|
+
'update.orphanDiscard': 'Discard this draft…',
|
|
352
|
+
'update.orphanConfirm': 'Discard the unsaved draft for "{name}" (ID: {id})? First copy any content you need to keep. This page cannot recover it after discarding; files on disk will remain unchanged.',
|
|
353
|
+
'update.orphanAcknowledge': 'I have kept the content I need and understand this page cannot recover the discarded draft.',
|
|
354
|
+
'update.orphanChanged': 'The draft or assistant list changed. Nothing was discarded. Review it again before confirming.',
|
|
355
|
+
'update.orphanDiscarded': 'The confirmed draft was discarded from this page; files on disk are unchanged.',
|
|
356
|
+
'update.running': 'Running plugin version: {version}',
|
|
357
|
+
'update.unknown': 'unknown',
|
|
358
|
+
'update.check': 'Check for updates',
|
|
359
|
+
'update.checking': 'Checking…',
|
|
360
|
+
'update.inPlugins': 'Update in Plugins',
|
|
361
|
+
'update.details': 'Open plugin details',
|
|
362
|
+
'update.guide': 'Update guide',
|
|
363
|
+
'update.idle': 'Only a click checks the registries configured by the host; installation is never automatic.',
|
|
364
|
+
'update.unavailable': 'This connection does not offer native version checks. Open plugin details or the update guide.',
|
|
365
|
+
'update.permission': 'Host permission is missing for this action, or the access service is unavailable.',
|
|
366
|
+
'update.updatePermission': 'You can view versions, but this connection does not have update permission.',
|
|
367
|
+
'update.available': 'Update available: {version}. Official plugin management handles installation.',
|
|
368
|
+
'update.current': 'This check returned no newer releases.',
|
|
369
|
+
'update.filtered': 'Newer releases exist, but none meet the official default conditions for a stable compatible version.',
|
|
370
|
+
'update.partial': 'The result omits some newer releases; no recommended update was found.',
|
|
371
|
+
'update.omitted': '{count} additional newer releases were omitted.',
|
|
372
|
+
'update.disk': 'Installed on disk: {version}; this version may not be loaded in the running host yet.',
|
|
373
|
+
'update.managed': 'Manage this installation through its original source or plugin details; it cannot use this native registry update path. The source will not be changed automatically.',
|
|
374
|
+
'update.notFound': 'The host did not list this plugin, so its update status could not be determined.',
|
|
375
|
+
'update.network': 'The host could not query the registry. Try again later; update status is unknown.',
|
|
376
|
+
'update.refused': 'The host did not provide a usable version list; update status is unknown.',
|
|
377
|
+
'update.failed': 'The update check could not finish. Retry or open plugin details.',
|
|
378
|
+
'update.finishOperation': 'Wait for the current operation to finish before leaving this page.',
|
|
379
|
+
'update.finishRecovery': 'Complete Retry refresh or Retry activation before leaving this page.',
|
|
380
|
+
'update.saveDrafts': 'Save or explicitly discard all assistant drafts and unsubmitted new-assistant input before leaving this page.',
|
|
381
|
+
'update.navigationFailed': 'Plugin details could not open. Your drafts are preserved; try again later.',
|
|
382
|
+
'create.nextStep': 'After creating, edit and save above. To use the assistant, choose it in the mode menu for a new session.',
|
|
383
|
+
'btn.retryRead': 'Retry refresh',
|
|
384
|
+
'btn.retrySync': 'Retry activation',
|
|
385
|
+
'api.savedButSyncFailed': 'Files are saved, but these assistants are not active yet: {ids}. Select Retry activation; you do not need to repeat the original action.',
|
|
386
|
+
'api.syncFailed': 'These assistants are still not active: {ids}. Retry, then check the host log if the problem continues.',
|
|
387
|
+
'api.presetRegistrationSyncFailed': 'The saved assistant configuration has not been confirmed in the host registry. Address existing warnings or check the host log.',
|
|
388
|
+
'api.synchronized': 'Configuration is active. Choose an assistant in the mode menu for a new session.',
|
|
389
|
+
'api.recoveryRequired': 'Assistant files need recovery: {path}. Keep the backups in that directory and check the host log for recovery details.',
|
|
390
|
+
'msg.completedReadFailed': '{result} The page could not refresh. Select Retry refresh; do not repeat the original action.',
|
|
391
|
+
'msg.synchronized': 'Configuration is active.',
|
|
392
|
+
'msg.syncFailed': 'Could not apply the configuration. Please retry.',
|
|
393
|
+
'msg.loaded': 'Loaded into the editor. Not saved yet.',
|
|
394
|
+
'status.reading': 'Reading…',
|
|
395
|
+
'status.saving': 'Saving…',
|
|
396
|
+
'status.creating': 'Creating assistant…',
|
|
397
|
+
'status.deleting': 'Deleting assistant…',
|
|
398
|
+
'status.reordering': 'Saving order…',
|
|
399
|
+
'status.repairing': 'Repairing…',
|
|
400
|
+
'status.importing': 'Reading import file…',
|
|
401
|
+
'status.synchronizing': 'Applying configuration…',
|
|
286
402
|
nav: 'Custom mode',
|
|
287
403
|
|
|
288
404
|
// ── assistant management ──────────────────────────────────────────────────
|
|
289
405
|
'assistant.heading': 'Assistants',
|
|
290
|
-
'assistant.hint': '
|
|
291
|
-
'posture.hint': '
|
|
406
|
+
'assistant.hint': 'Choose an assistant to edit. Each keeps its own system prompt, base mode and plugin switches.',
|
|
407
|
+
'posture.hint': 'Choose a starting posture and a name, then select New assistant.',
|
|
292
408
|
'tree.posture': 'Starter',
|
|
293
|
-
'tree.base': '
|
|
294
|
-
'tree.
|
|
295
|
-
'tree.baseSkip': 'Cannot be chosen on this branch. Change it later under This assistant.',
|
|
296
|
-
'tree.baseLocked': 'Only the official Standard mode is lit. The other four are dim and cannot be chosen when creating.',
|
|
409
|
+
'tree.base': 'Starting base mode',
|
|
410
|
+
'tree.baseLocked': 'New assistants start on the official Standard mode. You can change the base mode under This assistant after creating one.',
|
|
297
411
|
'posture.develop.label': 'Coding',
|
|
298
412
|
'posture.develop.note': 'Official Standard mode, tools as shipped.',
|
|
299
413
|
'posture.write.label': 'Writing',
|
|
@@ -301,7 +415,7 @@ export const en = {
|
|
|
301
415
|
'posture.chat.label': 'Chat',
|
|
302
416
|
'posture.chat.note': 'Official Standard mode. Files and terminal off.',
|
|
303
417
|
'editor.heading': 'This assistant',
|
|
304
|
-
'editor.hint': 'This
|
|
418
|
+
'editor.hint': 'Editing {name}. This selection controls what you edit here; choose an assistant in the mode menu when starting a new session.',
|
|
305
419
|
'assistant.empty': 'No assistants yet — create one with the field below.',
|
|
306
420
|
'assistant.loadingList': 'Loading assistants…',
|
|
307
421
|
'assistant.newPlaceholder': 'Name of the new assistant (e.g. Writing assistant)',
|
|
@@ -322,7 +436,7 @@ export const en = {
|
|
|
322
436
|
'btn.resetHint': 'Restores the prompt saved when this assistant was created. The editor changes. Save writes it.',
|
|
323
437
|
'btn.delete': 'Delete',
|
|
324
438
|
'btn.cancel': 'Cancel',
|
|
325
|
-
'msg.created': 'Created.
|
|
439
|
+
'msg.created': 'Created. Continue editing here, then choose it in the mode menu for a new session.',
|
|
326
440
|
'msg.duplicated': 'Duplicated. The two are independent from here on.',
|
|
327
441
|
'msg.deleted': 'Deleted.',
|
|
328
442
|
'msg.createFailed': 'Could not create',
|
|
@@ -362,7 +476,7 @@ export const en = {
|
|
|
362
476
|
'api.saved': 'Saved ({name}, {mode}). The next model step rereads the saved prompt.',
|
|
363
477
|
'api.conflict': 'The prompt on disk has changed, so this save was not written. Reload, then decide whether to overwrite.',
|
|
364
478
|
'api.savedWithExclusiveRows': 'Saved ({name}, {mode}). Turned off rows that cannot be on together: {rows}.',
|
|
365
|
-
'api.created': 'Created {name}.
|
|
479
|
+
'api.created': 'Created {name}. Continue editing here, then choose it in the mode menu for a new session.',
|
|
366
480
|
'api.savedPrompt': 'System prompt saved ({name}). The next model step rereads this file.',
|
|
367
481
|
'api.duplicated': 'Copied from {from}. The two are independent from now on.',
|
|
368
482
|
'api.deleted': 'Deleted {name}. Sessions already using it are unchanged.',
|
|
@@ -401,6 +515,8 @@ export const en = {
|
|
|
401
515
|
'warn.approvalGateMissing': 'This version has no approval event. In-session prompt edits are not confirmed.',
|
|
402
516
|
'warn.unresolvableRows': 'Some rows cannot start here, so this mode is absent from the new-session menu. Fix for this line turns off only those rows.',
|
|
403
517
|
'warn.presetBroken': 'This mode cannot start, so it will not appear for a new session. Use Fix for this line. The reason is on the next line.',
|
|
518
|
+
'warn.presetRegistrationSyncFailed': 'This assistant\'s saved configuration has not been confirmed in the host registry. Address existing warnings or check the host log; you do not need to save again.',
|
|
519
|
+
'warn.presetRegistrationRestartSuggested': 'This assistant\'s saved configuration has not been confirmed in the host registry. If the plugin was just updated or restored, consider restarting DeepSeek Harness, then check again; you do not need to save again.',
|
|
404
520
|
'warn.badPromptVariable': 'The prompt uses a variable that is not available. Fix it before saving.',
|
|
405
521
|
'warn.duplicateRowIds': 'A row id is repeated. A switch applies only to the first one.',
|
|
406
522
|
'warn.predicateFailed': 'A platform condition could not be evaluated. That row is treated as off.',
|
|
@@ -451,7 +567,7 @@ export const en = {
|
|
|
451
567
|
'tag.followPlatform': 'Platform',
|
|
452
568
|
|
|
453
569
|
'btn.save': 'Save',
|
|
454
|
-
'btn.saving': '
|
|
570
|
+
'btn.saving': 'Saving…',
|
|
455
571
|
'btn.reload': 'Reload',
|
|
456
572
|
'btn.reloadDiscard': 'Discard edits and reload',
|
|
457
573
|
'btn.reloadConfirm': 'Discard unsaved edits and reload?',
|
|
@@ -513,6 +629,8 @@ export const en = {
|
|
|
513
629
|
'row.tool-plugin-manager.label': 'Plugin manager (install / toggle)',
|
|
514
630
|
'row.tool-plugin-manager.note': 'Lets the model install or toggle plugins.',
|
|
515
631
|
'row.tool-presentation.label': 'PTC tool presentation',
|
|
632
|
+
'row.tool-executor-ptc.label': 'PTC program executor',
|
|
633
|
+
'row.tool-executor-ptc.note': 'Runs programs and calls tools through run_code, and provides the tool SDK to the model.',
|
|
516
634
|
'row.present.label': 'Deliverables (present)',
|
|
517
635
|
'row.command-goal.label': 'Goal command',
|
|
518
636
|
'row.tool-goal.label': 'Goals',
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-custom-mode",
|
|
3
|
-
"version": "2.
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "2.3.0",
|
|
4
|
+
"description": "Customize DeepSeek Harness assistants: edit system prompts, base modes, and plugin switches.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"author": "bowluna <ai@bowluna.com>",
|
|
@@ -80,7 +80,7 @@
|
|
|
80
80
|
"market-check": "node tools/market-check.mjs"
|
|
81
81
|
},
|
|
82
82
|
"peerDependencies": {
|
|
83
|
-
"@deepseek-ai/dsh": ">=0.1.5-rc.2 <0.2.0-0 || >=0.1.6-alpha.1 <0.2.0-0 || >=0.1.7-alpha.1 <0.2.0-0 || >=0.2.0-0 <0.3.0-0 || >=0.2.1-alpha.1 <0.3.0-0"
|
|
83
|
+
"@deepseek-ai/dsh": ">=0.1.5-rc.2 <0.2.0-0 || >=0.1.6-alpha.1 <0.2.0-0 || >=0.1.7-alpha.1 <0.2.0-0 || >=0.2.0-0 <0.3.0-0 || >=0.2.1-alpha.1 <0.3.0-0 || >=0.2.2-alpha.2 <0.3.0-0"
|
|
84
84
|
},
|
|
85
85
|
"peerDependenciesMeta": {
|
|
86
86
|
"@deepseek-ai/dsh": {
|
|
@@ -89,7 +89,7 @@
|
|
|
89
89
|
},
|
|
90
90
|
"engines": {
|
|
91
91
|
"node": ">=20",
|
|
92
|
-
"dsh": ">=0.1.5-rc.2 <0.2.0-0 || >=0.1.6-alpha.1 <0.2.0-0 || >=0.1.7-alpha.1 <0.2.0-0 || >=0.2.0-0 <0.3.0-0 || >=0.2.1-alpha.1 <0.3.0-0"
|
|
92
|
+
"dsh": ">=0.1.5-rc.2 <0.2.0-0 || >=0.1.6-alpha.1 <0.2.0-0 || >=0.1.7-alpha.1 <0.2.0-0 || >=0.2.0-0 <0.3.0-0 || >=0.2.1-alpha.1 <0.3.0-0 || >=0.2.2-alpha.2 <0.3.0-0"
|
|
93
93
|
},
|
|
94
94
|
"publishConfig": {
|
|
95
95
|
"access": "public",
|