@gehennawu/dsh-service 1.6.2 → 1.7.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.en.md +17 -7
- package/README.md +17 -7
- package/backup-integrity.js +1 -1
- package/client.js +1 -1
- package/index.js +188 -16
- package/package.json +1 -1
- package/quota-adapters.js +214 -2
package/index.js
CHANGED
|
@@ -24,11 +24,13 @@ import {
|
|
|
24
24
|
cliproxyProjectFor as cliproxyProjectForAdapter,
|
|
25
25
|
createQuotaAdapterCatalog,
|
|
26
26
|
fetchCliproxyUsage as fetchCliproxyAdapterUsage,
|
|
27
|
+
fetchCommandCodeQuota as fetchCommandCodeQuotaAdapter,
|
|
27
28
|
fetchStepFunStepPlanUsage as fetchStepFunStepPlanAdapterUsage,
|
|
28
29
|
fetchXiaomiTokenPlanUsage as fetchXiaomiTokenPlanAdapterUsage,
|
|
29
30
|
normalizeAntigravityModels as normalizeAntigravityModelsAdapter,
|
|
30
31
|
normalizeAntigravityQuotaSummary as normalizeAntigravityQuotaSummaryAdapter,
|
|
31
32
|
normalizeCodexRateLimit as normalizeCodexRateLimitAdapter,
|
|
33
|
+
normalizeCommandCodeQuota as normalizeCommandCodeQuotaAdapter,
|
|
32
34
|
normalizeDeepseekBalance as normalizeDeepseekBalanceAdapter,
|
|
33
35
|
normalizeGeminiBuckets as normalizeGeminiBucketsAdapter,
|
|
34
36
|
normalizeKimiBalance as normalizeKimiBalanceAdapter,
|
|
@@ -213,6 +215,14 @@ const SUBAGENT_DISPATCH_PAGE_MAX = 400
|
|
|
213
215
|
const QUOTA_UNUSABLE_ERROR_RE = /^(credential-missing|credential-rejected|credentials-unavailable|no-base-url|no-subscription|host-not-pinned|mgmt-disabled|transport-unavailable|bad-payload)/i
|
|
214
216
|
const QUOTA_UNUSABLE_STATUS_RE = /^(?:http-status|upstream-status):4\d\d$/i
|
|
215
217
|
|
|
218
|
+
// 统一配置文件(v1.6.4):收敛各功能独立配置,避免文件碎片化;修改/清除单项配置不影响其他字段。
|
|
219
|
+
const UNIFIED_CONFIG_FILE = 'dsh-service-config.json'
|
|
220
|
+
const UNIFIED_CONFIG_VERSION = 1
|
|
221
|
+
const MAX_UNIFIED_CONFIG_BYTES = 128 * 1024
|
|
222
|
+
const CONFIG_ALLOWED_SECTIONS = Object.freeze(['settingsNav'])
|
|
223
|
+
const NAV_ITEM_ID_RE = /^[A-Za-z0-9._-]{1,64}$/
|
|
224
|
+
const MAX_NAV_ITEMS = 64
|
|
225
|
+
|
|
216
226
|
// 升级目标白名单:命令与包名全来自宿主常量,浏览器不传任何输入;
|
|
217
227
|
// TARGET_RE 与 dsh-market 同源,只放行「包名@版本」字符集。
|
|
218
228
|
const TARGET_RE = /^[A-Za-z0-9@:./_#+~^=-]+$/
|
|
@@ -799,7 +809,7 @@ async function createBackupAttempt(ctx, dshHome, backupDir, name, validateArchiv
|
|
|
799
809
|
try {
|
|
800
810
|
// 进度:复制阶段按真实字节上报;打包/校验阶段上报归档体积(大小 ≈ 源字节,zstd 数据近不可压缩)。
|
|
801
811
|
const sessionsSource = join(dshHome, 'sessions')
|
|
802
|
-
const configNames = ['settings.yaml', 'cordis.patch.yml', 'AGENTS.md']
|
|
812
|
+
const configNames = ['settings.yaml', 'cordis.patch.yml', 'AGENTS.md', 'dsh-service-config.json']
|
|
803
813
|
let configBytes = 0
|
|
804
814
|
for (const file of configNames) configBytes += await sumBackupTree(join(dshHome, file))
|
|
805
815
|
let profilesBytes = 0
|
|
@@ -1429,6 +1439,20 @@ async function fetchStepFunStepPlanUsage({ credential, signal }) {
|
|
|
1429
1439
|
return fetchStepFunStepPlanAdapterUsage({ credential, signal, requestJson: requestQuotaJson })
|
|
1430
1440
|
}
|
|
1431
1441
|
|
|
1442
|
+
// ─── Command Code(command-goat)账号额度查询 ────────────────────────────────
|
|
1443
|
+
// 查询面完全固定(api.commandcode.ai 的 alpha 面,与 settings baseURL 无关):whoami 取 orgId →
|
|
1444
|
+
// credits(三源余额 + 五小时/周窗 used/cap)→ subscriptions(套餐与计费周期)→ usage/summary
|
|
1445
|
+
// (周期花费)。认证与推理同 key(Bearer <用户 key>),凭据线索走 settings apiKeyEnv 在前、
|
|
1446
|
+
// COMMAND_CODE_API_KEY 殿后。归一化:余额/花费是文本窗、窗口是百分比窗(宿主下发绝对数,
|
|
1447
|
+
// 客户端做缩写),全部由 Adapter 持有——index.js 只做装配。
|
|
1448
|
+
function normalizeCommandCodeQuota(payload) {
|
|
1449
|
+
return normalizeCommandCodeQuotaAdapter(payload)
|
|
1450
|
+
}
|
|
1451
|
+
|
|
1452
|
+
async function fetchCommandCodeQuota({ credential, signal }) {
|
|
1453
|
+
return fetchCommandCodeQuotaAdapter({ credential, signal, requestJson: requestQuotaJson })
|
|
1454
|
+
}
|
|
1455
|
+
|
|
1432
1456
|
|
|
1433
1457
|
// ─── CLIProxyAPI(cliproxy)管理面编排 ──────────────────────────────────────
|
|
1434
1458
|
// 上游官方额度端点注册表(经 CPA api-call 代调,header 的 $TOKEN$ 由 CPA 替换为对应账号凭据)。
|
|
@@ -2913,8 +2937,10 @@ function buildSkillRoots(ctx, dshHome) {
|
|
|
2913
2937
|
return roots
|
|
2914
2938
|
}
|
|
2915
2939
|
|
|
2916
|
-
/** 全量扫描:一层深度发现目录 bundle 与扁平 .md,逐文件评估并标注同名遮蔽。
|
|
2917
|
-
|
|
2940
|
+
/** 全量扫描:一层深度发现目录 bundle 与扁平 .md,逐文件评估并标注同名遮蔽。
|
|
2941
|
+
* fileCache 只跳过「读文件 + 解析」这一步:目录发现、realpath 去重、权限与 stat
|
|
2942
|
+
* 每次都重跑,缓存命中判据是 stat 的 size+mtimeMs 指纹(先记录后复用)。 */
|
|
2943
|
+
async function scanSkillEntries(ctx, dshHome, fileCache) {
|
|
2918
2944
|
const roots = []
|
|
2919
2945
|
const entries = []
|
|
2920
2946
|
// 同一物理目录可能被两条根规则同时命中(典型:HOME 本身注册为工作区时,
|
|
@@ -2954,14 +2980,26 @@ async function scanSkillEntries(ctx, dshHome) {
|
|
|
2954
2980
|
try {
|
|
2955
2981
|
const info = await stat(path)
|
|
2956
2982
|
if (info.size > MAX_SKILL_FILE_BYTES) { entries.push({ ...base, invalid: 'too-large' }); continue }
|
|
2983
|
+
const cached = fileCache?.get(path)
|
|
2984
|
+
if (cached !== undefined && cached.size === info.size && cached.mtimeMs === info.mtimeMs) {
|
|
2985
|
+
entries.push({ ...base, ...cached.evaluated, bodyHash: cached.bodyHash, bytes: info.size })
|
|
2986
|
+
continue
|
|
2987
|
+
}
|
|
2957
2988
|
const raw = await readFile(path, 'utf8')
|
|
2958
2989
|
const evaluated = evaluateSkillFile(raw)
|
|
2959
|
-
|
|
2990
|
+
const bodyHash = bodyHashOf(raw, locateSkillFrontmatter(raw)?.bodyStart ?? 0)
|
|
2991
|
+
fileCache?.set(path, { size: info.size, mtimeMs: info.mtimeMs, evaluated, bodyHash })
|
|
2992
|
+
entries.push({ ...base, ...evaluated, bodyHash, bytes: info.size })
|
|
2960
2993
|
} catch (error) {
|
|
2961
2994
|
entries.push({ ...base, invalid: 'read-error:' + (error?.code || error?.message || String(error)).slice(0, 80) })
|
|
2962
2995
|
}
|
|
2963
2996
|
}
|
|
2964
2997
|
}
|
|
2998
|
+
// 本轮未再出现的路径从缓存剔除:技能被删除/改名后缓存不残留悬挂条目。
|
|
2999
|
+
if (fileCache !== undefined) {
|
|
3000
|
+
const seen = new Set(entries.map((entry) => entry.path))
|
|
3001
|
+
for (const key of [...fileCache.keys()]) if (!seen.has(key)) fileCache.delete(key)
|
|
3002
|
+
}
|
|
2965
3003
|
const winners = new Map()
|
|
2966
3004
|
for (const entry of entries) {
|
|
2967
3005
|
if (entry.invalid !== undefined) continue
|
|
@@ -3042,16 +3080,20 @@ function publicSkillEntry(entry, index) {
|
|
|
3042
3080
|
}
|
|
3043
3081
|
|
|
3044
3082
|
/** 变更类动作共用通道:重扫定位签名 ID(浏览器零路径输入),校验后执行手术。 */
|
|
3045
|
-
async function mutateSkillEntryById(ctx, dshHome, index, id, allowInvalid, mutate) {
|
|
3083
|
+
async function mutateSkillEntryById(ctx, dshHome, index, id, allowInvalid, mutate, fileCache) {
|
|
3046
3084
|
if (typeof id !== 'string' || id === '') return { ok: false, error: 'unknown-skill' }
|
|
3047
|
-
const { entries } = await scanSkillEntries(ctx, dshHome)
|
|
3085
|
+
const { entries } = await scanSkillEntries(ctx, dshHome, fileCache)
|
|
3048
3086
|
const entry = entries.find((candidate) => candidate.id === id)
|
|
3049
3087
|
if (entry === undefined) return { ok: false, error: 'unknown-skill' }
|
|
3050
3088
|
if (entry.invalid !== undefined && !(allowInvalid === true && entry.invalid.startsWith('legacy-invocation-key:'))) return { ok: false, error: 'invalid-skill', detail: entry.invalid }
|
|
3051
3089
|
if (entry.writable !== true) return { ok: false, error: 'read-only-source' }
|
|
3052
3090
|
const raw = await readFile(entry.path, 'utf8')
|
|
3053
3091
|
const outcome = mutate(raw, entry)
|
|
3054
|
-
if (outcome.text !== raw)
|
|
3092
|
+
if (outcome.text !== raw) {
|
|
3093
|
+
await writeFile(entry.path, outcome.text, 'utf8')
|
|
3094
|
+
// 宿主自身写入主动失效缓存:不依赖 mtime 指纹在极端情况下(同毫秒同尺寸)的判断。
|
|
3095
|
+
fileCache?.delete(entry.path)
|
|
3096
|
+
}
|
|
3055
3097
|
const evaluated = evaluateSkillFile(outcome.text)
|
|
3056
3098
|
const freshLocated = locateSkillFrontmatter(outcome.text)
|
|
3057
3099
|
const fresh = { ...entry, ...evaluated, bodyHash: bodyHashOf(outcome.text, freshLocated?.bodyStart ?? 0) }
|
|
@@ -3342,6 +3384,84 @@ async function saveSubagentRoute(dshHome, config) {
|
|
|
3342
3384
|
}
|
|
3343
3385
|
}
|
|
3344
3386
|
|
|
3387
|
+
function sanitizeSettingsNavConfig(value) {
|
|
3388
|
+
if (value === null || value === undefined) return null
|
|
3389
|
+
if (typeof value !== 'object' || Array.isArray(value)) return null
|
|
3390
|
+
|
|
3391
|
+
let order = null
|
|
3392
|
+
if (Array.isArray(value.order)) {
|
|
3393
|
+
order = value.order
|
|
3394
|
+
.filter((id) => typeof id === 'string' && NAV_ITEM_ID_RE.test(id.trim()))
|
|
3395
|
+
.map((id) => id.trim())
|
|
3396
|
+
.slice(0, MAX_NAV_ITEMS)
|
|
3397
|
+
}
|
|
3398
|
+
|
|
3399
|
+
let hidden = []
|
|
3400
|
+
if (Array.isArray(value.hidden)) {
|
|
3401
|
+
hidden = value.hidden
|
|
3402
|
+
.filter((id) => typeof id === 'string' && id.trim() !== 'dsh-service' && NAV_ITEM_ID_RE.test(id.trim()))
|
|
3403
|
+
.map((id) => id.trim())
|
|
3404
|
+
.slice(0, MAX_NAV_ITEMS)
|
|
3405
|
+
}
|
|
3406
|
+
|
|
3407
|
+
return {
|
|
3408
|
+
order,
|
|
3409
|
+
hidden,
|
|
3410
|
+
}
|
|
3411
|
+
}
|
|
3412
|
+
|
|
3413
|
+
async function loadUnifiedConfig(dshHome) {
|
|
3414
|
+
try {
|
|
3415
|
+
const target = join(dshHome, UNIFIED_CONFIG_FILE)
|
|
3416
|
+
const info = await stat(target)
|
|
3417
|
+
if (info.size > MAX_UNIFIED_CONFIG_BYTES) return { version: UNIFIED_CONFIG_VERSION }
|
|
3418
|
+
const parsed = JSON.parse(await readFile(target, 'utf8'))
|
|
3419
|
+
if (parsed && typeof parsed === 'object' && !Array.isArray(parsed)) {
|
|
3420
|
+
return parsed
|
|
3421
|
+
}
|
|
3422
|
+
return { version: UNIFIED_CONFIG_VERSION }
|
|
3423
|
+
} catch (_) {
|
|
3424
|
+
return { version: UNIFIED_CONFIG_VERSION }
|
|
3425
|
+
}
|
|
3426
|
+
}
|
|
3427
|
+
|
|
3428
|
+
async function saveUnifiedConfig(dshHome, config) {
|
|
3429
|
+
await mkdir(dshHome, { recursive: true })
|
|
3430
|
+
const target = join(dshHome, UNIFIED_CONFIG_FILE)
|
|
3431
|
+
const temporary = `${target}.${randomUUID()}.tmp`
|
|
3432
|
+
try {
|
|
3433
|
+
const payload = JSON.stringify(config, null, 2)
|
|
3434
|
+
await writeFile(temporary, payload, { mode: 0o600 })
|
|
3435
|
+
await rename(temporary, target)
|
|
3436
|
+
} finally {
|
|
3437
|
+
await rm(temporary, { force: true })
|
|
3438
|
+
}
|
|
3439
|
+
}
|
|
3440
|
+
|
|
3441
|
+
// 统一配置写串行化(按 dshHome 分链):读-改-写整个文件必须排队,否则两台设备
|
|
3442
|
+
// 同时首次迁移(并发 config-set)会在读旧快照后互相覆盖,丢失先写区块。
|
|
3443
|
+
const unifiedConfigWriteChains = new Map()
|
|
3444
|
+
function updateUnifiedConfigSection(dshHome, section, value) {
|
|
3445
|
+
const previous = unifiedConfigWriteChains.get(dshHome) ?? Promise.resolve()
|
|
3446
|
+
const result = previous.then(async () => {
|
|
3447
|
+
const current = await loadUnifiedConfig(dshHome)
|
|
3448
|
+
const updated = Object.assign({}, current, { version: UNIFIED_CONFIG_VERSION })
|
|
3449
|
+
if (value === null || value === undefined) {
|
|
3450
|
+
delete updated[section]
|
|
3451
|
+
} else {
|
|
3452
|
+
updated[section] = value
|
|
3453
|
+
}
|
|
3454
|
+
await saveUnifiedConfig(dshHome, updated)
|
|
3455
|
+
return updated
|
|
3456
|
+
})
|
|
3457
|
+
const tail = result.then(() => undefined, () => undefined)
|
|
3458
|
+
unifiedConfigWriteChains.set(dshHome, tail)
|
|
3459
|
+
void tail.then(() => {
|
|
3460
|
+
if (unifiedConfigWriteChains.get(dshHome) === tail) unifiedConfigWriteChains.delete(dshHome)
|
|
3461
|
+
})
|
|
3462
|
+
return result
|
|
3463
|
+
}
|
|
3464
|
+
|
|
3345
3465
|
// 额度态「不可服务」判定(子代理回退候选过滤用):lastError 命中配置/凭据/上游 4xx 码集,
|
|
3346
3466
|
// 或任一显示窗口 percent≥100(已用尽)。无数据 / 刷新中 / 瞬态错误视为可用——fail-open,
|
|
3347
3467
|
// 不因额度数据缺席或一次网络抖动误伤正常渠道。
|
|
@@ -3828,6 +3948,11 @@ async function listSessionsForManage(ctx, dshHome, scope = 'all', titleCache = n
|
|
|
3828
3948
|
for (const record of recordsInScope) {
|
|
3829
3949
|
const id = record.header.id
|
|
3830
3950
|
if (deletedSet.has(id)) continue
|
|
3951
|
+
// 子代理识别(官方 SessionHeader 字段):origin='subagent' 是官方产品分类
|
|
3952
|
+
// (dsh-subagent 创建时写入,官方自身也按此圈定);delegationDepth>0 兜底
|
|
3953
|
+
// 老日志缺 origin 的子代理;parentSession 只是 fork 血统(普通 fork 也写),不作判据。
|
|
3954
|
+
const subagent = record.header.origin === 'subagent'
|
|
3955
|
+
|| (typeof record.header.delegationDepth === 'number' && record.header.delegationDepth > 0)
|
|
3831
3956
|
items.push({
|
|
3832
3957
|
id,
|
|
3833
3958
|
title: titles.get(id) ?? (titleCache !== null ? titleCache.get(id)?.title : undefined) ?? '',
|
|
@@ -3836,6 +3961,7 @@ async function listSessionsForManage(ctx, dshHome, scope = 'all', titleCache = n
|
|
|
3836
3961
|
live: record.live === true,
|
|
3837
3962
|
persisted: record.persisted === true,
|
|
3838
3963
|
archived: archivedSet.has(id),
|
|
3964
|
+
subagent,
|
|
3839
3965
|
})
|
|
3840
3966
|
}
|
|
3841
3967
|
items.sort((a, b) => (b.createdAt ?? 0) - (a.createdAt ?? 0))
|
|
@@ -4428,6 +4554,9 @@ function apply(ctx) {
|
|
|
4428
4554
|
const quotaThrottle = createQuotaThrottle()
|
|
4429
4555
|
// 技能管理(v0.22):侧车索引缓存 + 批量补全状态。批量随 Fiber 销毁中止。
|
|
4430
4556
|
let skillsIndexPromise = loadSkillsIndex(dshHome)
|
|
4557
|
+
// 技能文件解析缓存:键=绝对路径,值=stat 指纹(size+mtimeMs)+ 评估结果。
|
|
4558
|
+
// 列表/定位类接口每次都重跑目录发现与 stat,只跳过未变文件的读盘与解析。
|
|
4559
|
+
const skillFileCache = new Map()
|
|
4431
4560
|
let skillsBatch = null
|
|
4432
4561
|
// 单条补全的运行日志环形缓冲:客户端在「生成中」期间轮询展示。
|
|
4433
4562
|
// 条目是结构化 {at, code, params},本地化文案由客户端词典渲染。
|
|
@@ -4470,6 +4599,7 @@ function apply(ctx) {
|
|
|
4470
4599
|
try { call.abort(new Error('batch-cancelled')) } catch (_) {}
|
|
4471
4600
|
}
|
|
4472
4601
|
skillsActiveControllers.clear()
|
|
4602
|
+
skillFileCache.clear()
|
|
4473
4603
|
}, 'dsh-service skills batch teardown')
|
|
4474
4604
|
// ── 子代理路由(v0.27):三态配置 + subagents seam ─────────────────────
|
|
4475
4605
|
// 内存态即 seam 读取的事实源:保存端点落盘成功后原地替换,派生路径零读盘。
|
|
@@ -5087,7 +5217,7 @@ function apply(ctx) {
|
|
|
5087
5217
|
'skills-list': { feature: 'skillManager', handle: async (payload, rpcEndpoint) => {
|
|
5088
5218
|
try {
|
|
5089
5219
|
const index = await skillsIndexPromise
|
|
5090
|
-
const { roots, entries } = await scanSkillEntries(ctx, dshHome)
|
|
5220
|
+
const { roots, entries } = await scanSkillEntries(ctx, dshHome, skillFileCache)
|
|
5091
5221
|
return {
|
|
5092
5222
|
ok: true,
|
|
5093
5223
|
value: {
|
|
@@ -5117,7 +5247,7 @@ function apply(ctx) {
|
|
|
5117
5247
|
if (typeof payload?.enable !== 'boolean') return { ok: false, error: 'invalid-enable' }
|
|
5118
5248
|
try {
|
|
5119
5249
|
const index = await skillsIndexPromise
|
|
5120
|
-
const outcome = await mutateSkillEntryById(ctx, dshHome, index, payload?.id, false, (raw) => ({ text: setSkillInvocationKey(raw, field, payload.enable) }))
|
|
5250
|
+
const outcome = await mutateSkillEntryById(ctx, dshHome, index, payload?.id, false, (raw) => ({ text: setSkillInvocationKey(raw, field, payload.enable) }), skillFileCache)
|
|
5121
5251
|
if (!outcome.ok) return { ok: false, error: outcome.error, ...(outcome.detail !== undefined ? { detail: outcome.detail } : {}) }
|
|
5122
5252
|
return { ok: true, value: { entry: outcome.entry } }
|
|
5123
5253
|
} catch (error) {
|
|
@@ -5131,7 +5261,7 @@ function apply(ctx) {
|
|
|
5131
5261
|
const outcome = await mutateSkillEntryById(ctx, dshHome, index, payload?.id, true, (raw) => {
|
|
5132
5262
|
const fixed = fixLegacySkillInvocationKeys(raw)
|
|
5133
5263
|
return { text: fixed.text }
|
|
5134
|
-
})
|
|
5264
|
+
}, skillFileCache)
|
|
5135
5265
|
if (!outcome.ok) return { ok: false, error: outcome.error, ...(outcome.detail !== undefined ? { detail: outcome.detail } : {}) }
|
|
5136
5266
|
return { ok: true, value: { entry: outcome.entry } }
|
|
5137
5267
|
} catch (error) {
|
|
@@ -5150,7 +5280,7 @@ function apply(ctx) {
|
|
|
5150
5280
|
const whitelist = await listSkillModels(llm, ctx.get('agentDefaultModel'))
|
|
5151
5281
|
if (!whitelist.models.some((item) => item.provider === provider && item.id === model)) return { ok: false, error: 'invalid-model-route' }
|
|
5152
5282
|
const index = await skillsIndexPromise
|
|
5153
|
-
const { entries } = await scanSkillEntries(ctx, dshHome)
|
|
5283
|
+
const { entries } = await scanSkillEntries(ctx, dshHome, skillFileCache)
|
|
5154
5284
|
const entry = entries.find((candidate) => candidate.id === payload?.id)
|
|
5155
5285
|
if (entry === undefined) return { ok: false, error: 'unknown-skill' }
|
|
5156
5286
|
if (entry.invalid !== undefined) return { ok: false, error: 'invalid-skill', detail: entry.invalid }
|
|
@@ -5181,7 +5311,7 @@ function apply(ctx) {
|
|
|
5181
5311
|
if (description === '') return { ok: false, error: 'invalid-description' }
|
|
5182
5312
|
try {
|
|
5183
5313
|
// 注释只进插件侧车索引,绝不写回技能文件;因此不要求条目可写,只要求能被签名 ID 定位。
|
|
5184
|
-
const { entries } = await scanSkillEntries(ctx, dshHome)
|
|
5314
|
+
const { entries } = await scanSkillEntries(ctx, dshHome, skillFileCache)
|
|
5185
5315
|
const entry = entries.find((candidate) => candidate.id === payload?.id)
|
|
5186
5316
|
if (entry === undefined) return { ok: false, error: 'unknown-skill' }
|
|
5187
5317
|
const index = await serializeSkillsIndexWrite((current) => {
|
|
@@ -5201,7 +5331,7 @@ function apply(ctx) {
|
|
|
5201
5331
|
} },
|
|
5202
5332
|
'skills-note-clear': { feature: 'skillManager', audit: true, handle: async (payload, rpcEndpoint) => {
|
|
5203
5333
|
try {
|
|
5204
|
-
const { entries } = await scanSkillEntries(ctx, dshHome)
|
|
5334
|
+
const { entries } = await scanSkillEntries(ctx, dshHome, skillFileCache)
|
|
5205
5335
|
const entry = entries.find((candidate) => candidate.id === payload?.id)
|
|
5206
5336
|
if (entry === undefined) return { ok: false, error: 'unknown-skill' }
|
|
5207
5337
|
const index = await serializeSkillsIndexWrite((current) => {
|
|
@@ -5227,7 +5357,7 @@ function apply(ctx) {
|
|
|
5227
5357
|
const whitelist = await listSkillModels(llm, ctx.get('agentDefaultModel'))
|
|
5228
5358
|
if (!whitelist.models.some((item) => item.provider === provider && item.id === model)) return { ok: false, error: 'invalid-model-route' }
|
|
5229
5359
|
const index = await skillsIndexPromise
|
|
5230
|
-
const { entries } = await scanSkillEntries(ctx, dshHome)
|
|
5360
|
+
const { entries } = await scanSkillEntries(ctx, dshHome, skillFileCache)
|
|
5231
5361
|
const { candidates, annotated, skipped } = selectSkillBatchCandidates(entries, index)
|
|
5232
5362
|
const planId = randomUUID()
|
|
5233
5363
|
// 已注释条目也进计划(单列待客户端确认),体积估算含两者——确认后整批运行。
|
|
@@ -5260,7 +5390,7 @@ function apply(ctx) {
|
|
|
5260
5390
|
// 扫描一次建立 id→条目映射;逐条只重读目标文件校验新鲜度,不再每条全量重扫五类根。
|
|
5261
5391
|
let byId = new Map()
|
|
5262
5392
|
try {
|
|
5263
|
-
const { entries } = await scanSkillEntries(ctx, dshHome)
|
|
5393
|
+
const { entries } = await scanSkillEntries(ctx, dshHome, skillFileCache)
|
|
5264
5394
|
byId = new Map(entries.map((entry) => [entry.id, entry]))
|
|
5265
5395
|
} catch (_) {}
|
|
5266
5396
|
for (let cursor = 0; cursor < skillsBatch.items.length; cursor += 1) {
|
|
@@ -5525,7 +5655,7 @@ function apply(ctx) {
|
|
|
5525
5655
|
return {
|
|
5526
5656
|
ok: true,
|
|
5527
5657
|
value: {
|
|
5528
|
-
|
|
5658
|
+
// 只下发客户端真正消费的实例 id;重启反馈文案由客户端词典渲染(双语约束)。
|
|
5529
5659
|
instanceId,
|
|
5530
5660
|
},
|
|
5531
5661
|
}
|
|
@@ -5947,6 +6077,36 @@ function apply(ctx) {
|
|
|
5947
6077
|
return fileEditorFailure(fileEditorErrorCode(error))
|
|
5948
6078
|
}
|
|
5949
6079
|
} },
|
|
6080
|
+
'config-get': { handle: async (payload) => {
|
|
6081
|
+
try {
|
|
6082
|
+
const config = await loadUnifiedConfig(dshHome)
|
|
6083
|
+
const section = typeof payload?.section === 'string' ? payload.section : undefined
|
|
6084
|
+
if (section !== undefined) {
|
|
6085
|
+
if (!CONFIG_ALLOWED_SECTIONS.includes(section)) return { ok: false, error: 'invalid-section' }
|
|
6086
|
+
return { ok: true, value: config[section] ?? null }
|
|
6087
|
+
}
|
|
6088
|
+
return { ok: true, value: config }
|
|
6089
|
+
} catch (error) {
|
|
6090
|
+
return rpcTechnicalFailure(error)
|
|
6091
|
+
}
|
|
6092
|
+
} },
|
|
6093
|
+
'config-set': { audit: true, handle: async (payload) => {
|
|
6094
|
+
const section = typeof payload?.section === 'string' ? payload.section : ''
|
|
6095
|
+
if (!CONFIG_ALLOWED_SECTIONS.includes(section)) return { ok: false, error: 'invalid-section' }
|
|
6096
|
+
try {
|
|
6097
|
+
let sanitized = null
|
|
6098
|
+
if (section === 'settingsNav') {
|
|
6099
|
+
sanitized = sanitizeSettingsNavConfig(payload?.value)
|
|
6100
|
+
if (payload?.value !== null && payload?.value !== undefined && sanitized === null) {
|
|
6101
|
+
return { ok: false, error: 'invalid-section-value' }
|
|
6102
|
+
}
|
|
6103
|
+
}
|
|
6104
|
+
const updated = await updateUnifiedConfigSection(dshHome, section, sanitized)
|
|
6105
|
+
return { ok: true, value: updated[section] ?? null }
|
|
6106
|
+
} catch (error) {
|
|
6107
|
+
return rpcTechnicalFailure(error)
|
|
6108
|
+
}
|
|
6109
|
+
} },
|
|
5950
6110
|
}
|
|
5951
6111
|
const dispatchRpc = createRpcDispatcher({ endpoints: rpcEndpoints, featureEnabled, logger: ctx.logger })
|
|
5952
6112
|
try {
|
|
@@ -5977,6 +6137,7 @@ export {
|
|
|
5977
6137
|
evaluateSkillFile,
|
|
5978
6138
|
extractSkillDraftJson,
|
|
5979
6139
|
fetchCliproxyUsage,
|
|
6140
|
+
fetchCommandCodeQuota,
|
|
5980
6141
|
fetchProviderUsage,
|
|
5981
6142
|
fetchStepFunStepPlanUsage,
|
|
5982
6143
|
fetchXiaomiTokenPlanUsage,
|
|
@@ -5995,6 +6156,7 @@ export {
|
|
|
5995
6156
|
normalizeAntigravityModels,
|
|
5996
6157
|
normalizeAntigravityQuotaSummary,
|
|
5997
6158
|
normalizeCodexRateLimit,
|
|
6159
|
+
normalizeCommandCodeQuota,
|
|
5998
6160
|
normalizeDeepseekBalance,
|
|
5999
6161
|
normalizeGeminiBuckets,
|
|
6000
6162
|
normalizeKimiBalance,
|
|
@@ -6035,6 +6197,10 @@ export {
|
|
|
6035
6197
|
unwrapCliproxyApiCallEnvelope,
|
|
6036
6198
|
unwrapXiaomiConsoleEnvelope,
|
|
6037
6199
|
viewSessionPage,
|
|
6200
|
+
loadUnifiedConfig,
|
|
6201
|
+
saveUnifiedConfig,
|
|
6202
|
+
updateUnifiedConfigSection,
|
|
6203
|
+
sanitizeSettingsNavConfig,
|
|
6038
6204
|
}
|
|
6039
6205
|
export default {
|
|
6040
6206
|
SKILL_SOURCE_RANK,
|
|
@@ -6051,6 +6217,7 @@ export default {
|
|
|
6051
6217
|
evaluateSkillFile,
|
|
6052
6218
|
extractSkillDraftJson,
|
|
6053
6219
|
fetchCliproxyUsage,
|
|
6220
|
+
fetchCommandCodeQuota,
|
|
6054
6221
|
fetchProviderUsage,
|
|
6055
6222
|
fetchStepFunStepPlanUsage,
|
|
6056
6223
|
fetchXiaomiTokenPlanUsage,
|
|
@@ -6069,6 +6236,7 @@ export default {
|
|
|
6069
6236
|
normalizeAntigravityModels,
|
|
6070
6237
|
normalizeAntigravityQuotaSummary,
|
|
6071
6238
|
normalizeCodexRateLimit,
|
|
6239
|
+
normalizeCommandCodeQuota,
|
|
6072
6240
|
normalizeDeepseekBalance,
|
|
6073
6241
|
normalizeGeminiBuckets,
|
|
6074
6242
|
normalizeKimiBalance,
|
|
@@ -6109,4 +6277,8 @@ export default {
|
|
|
6109
6277
|
unwrapCliproxyApiCallEnvelope,
|
|
6110
6278
|
unwrapXiaomiConsoleEnvelope,
|
|
6111
6279
|
viewSessionPage,
|
|
6280
|
+
loadUnifiedConfig,
|
|
6281
|
+
saveUnifiedConfig,
|
|
6282
|
+
updateUnifiedConfigSection,
|
|
6283
|
+
sanitizeSettingsNavConfig,
|
|
6112
6284
|
}
|