mirror of
https://github.com/modelstudioai/cli.git
synced 2026-09-14 19:49:23 +08:00
fix(settings): 优化 bailian-kb 设置路由的配置管理逻辑
- 更新 package.json 版本至 0.1.3,完善导出和文件配置 - 引入 SettingsProvider 用于支持路径级 unset 操作 - 将 GET 和 POST 请求共用一个 /bailian-kb/settings 路由,避免重复注册 - POST 接口支持批量更新和删除配置字段,删除操作使用
This commit is contained in:
@@ -1,17 +1,24 @@
|
||||
{
|
||||
"name": "@ali/bailian-kb-dsh",
|
||||
"version": "0.1.1",
|
||||
"version": "0.1.3",
|
||||
"description": "Bailian knowledge-base tools for DeepSeek Harness: kb_search and kb_chat over the DashScope RAG API, plus the kscli management skill.",
|
||||
"type": "module",
|
||||
"main": "lib/index.js",
|
||||
"types": "lib/index.d.ts",
|
||||
"exports": {
|
||||
".": { "types": "./lib/index.d.ts", "default": "./lib/index.js" },
|
||||
"./client": { "default": "./lib/web/client.js" },
|
||||
".": {
|
||||
"types": "./lib/index.d.ts",
|
||||
"default": "./lib/index.js"
|
||||
},
|
||||
"./client": {
|
||||
"default": "./lib/web/client.js"
|
||||
},
|
||||
"./package.json": "./package.json"
|
||||
},
|
||||
"dsh": {
|
||||
"bundle": { "patch": "./cordis.patch.yml" },
|
||||
"bundle": {
|
||||
"patch": "./cordis.patch.yml"
|
||||
},
|
||||
"client": {
|
||||
"inject": [
|
||||
"@deepseek-ai/dsh-client-connection",
|
||||
@@ -23,8 +30,14 @@
|
||||
"platform": "web"
|
||||
}
|
||||
},
|
||||
"files": ["lib", "skills", "cordis.patch.yml"],
|
||||
"scripts": { "build": "tsc -b && tsdown" },
|
||||
"files": [
|
||||
"lib",
|
||||
"skills",
|
||||
"cordis.patch.yml"
|
||||
],
|
||||
"scripts": {
|
||||
"build": "tsc -b && tsdown"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@deepseek-ai/cordis": "^4.0.1",
|
||||
"@deepseek-ai/dsh-tools": "^0.1.0-rc.6",
|
||||
|
||||
@@ -8,7 +8,7 @@ import type { Context } from '@deepseek-ai/cordis'
|
||||
import type { IncomingMessage, ServerResponse } from 'node:http'
|
||||
import z from '@deepseek-ai/schemastery'
|
||||
import { credentialRef } from '@deepseek-ai/dsh-credentials'
|
||||
import { settingsNamespace, type SettingsRegisterOptions, type SettingsScope } from '@deepseek-ai/dsh-settings'
|
||||
import { settingsNamespace, SettingsProvider, type SettingsRegisterOptions, type SettingsScope } from '@deepseek-ai/dsh-settings'
|
||||
import { KbClient } from './client.js'
|
||||
import { registerSkill } from './skill.js'
|
||||
import { createKbTools } from './tools.js'
|
||||
@@ -40,6 +40,16 @@ const CREDENTIAL_SEEDS = [
|
||||
['defaultChatAgentId', 'BAILIAN_DEFAULT_CHAT_AGENT_ID'],
|
||||
] as const
|
||||
|
||||
/**
|
||||
* Every {@link Config} field the bridge route accepts. A static allowlist,
|
||||
* NOT `key in current()`: optional fields with no default and no base
|
||||
* (the default service ids) vanish from the resolved config once cleared,
|
||||
* and a membership test against it would silently drop their next write.
|
||||
*/
|
||||
const CONFIG_FIELDS = new Set<string>([
|
||||
'workspaceId', 'endpointHost', 'defaultRetrieveAgentId', 'defaultChatAgentId', 'agentVersion', 'chatTimeoutMs',
|
||||
])
|
||||
|
||||
/**
|
||||
* One-time migration: before this section existed, the workspace and
|
||||
* default-service ids lived only as credentials, which the wire never echoes.
|
||||
@@ -111,7 +121,10 @@ export function apply(ctx: Context, config: Config): void {
|
||||
// browser) and the scope handle the credential migration writes through.
|
||||
let current: () => Config = () => config
|
||||
let scope: SettingsScope<Config> | undefined
|
||||
/** The settings provider, captured for `mutate` (path-level unset) access. */
|
||||
let settings: SettingsProvider | undefined
|
||||
ctx.inject(['settings'], (sctx) => {
|
||||
settings = sctx.settings
|
||||
// `expose` is the wire opt-in the harness documents as deferred work; the
|
||||
// assertion keeps this compiling against pristine upstream types, which do
|
||||
// not declare it yet. Until upstream lands it the option is ignored and
|
||||
@@ -174,21 +187,20 @@ export function apply(ctx: Context, config: Config): void {
|
||||
// Bridge routes let the browser settings page read and write the resolved
|
||||
// section without riding the settings wire (which requires an apiproxy
|
||||
// allowlist entry the composition does not grant out-of-tree namespaces).
|
||||
// GET and POST share one exact-route registration: the webServer map keys
|
||||
// on (kind, path), so two registrations for the same path throw
|
||||
// "duplicate route" and the second handler silently replaces the first.
|
||||
ctx.inject(['webServer'], (wctx) => {
|
||||
wctx.effect(() => wctx.webServer.register({
|
||||
kind: 'exact',
|
||||
path: '/bailian-kb/settings',
|
||||
handler: (_req: IncomingMessage, res: ServerResponse) => {
|
||||
sendJson(res, 200, current())
|
||||
},
|
||||
}), 'tool-bailian-kb: settings GET route')
|
||||
|
||||
wctx.effect(() => wctx.webServer.register({
|
||||
kind: 'exact',
|
||||
path: '/bailian-kb/settings',
|
||||
handler: async (req: IncomingMessage, res: ServerResponse) => {
|
||||
if (req.method === 'GET' || req.method === 'HEAD') {
|
||||
sendJson(res, 200, current())
|
||||
return
|
||||
}
|
||||
if (req.method !== 'POST') {
|
||||
sendJson(res, 405, { error: 'use POST' })
|
||||
sendJson(res, 405, { error: 'use GET or POST' })
|
||||
return
|
||||
}
|
||||
if (!scope) {
|
||||
@@ -209,27 +221,30 @@ export function apply(ctx: Context, config: Config): void {
|
||||
const patch: Record<string, unknown> = {}
|
||||
const removals = new Set<string>()
|
||||
for (const [key, value] of Object.entries(body as Record<string, unknown>)) {
|
||||
if (!(key in current())) continue
|
||||
if (!CONFIG_FIELDS.has(key)) continue
|
||||
if (value === null) { removals.add(key); continue }
|
||||
patch[key] = value
|
||||
}
|
||||
try {
|
||||
if (removals.size > 0) {
|
||||
// Remove fields from the user section by replacing it wholesale
|
||||
// with the current resolved config minus the removed keys.
|
||||
// Absent keys re-inherit the entry base and schema defaults.
|
||||
const next = { ...current(), ...patch }
|
||||
for (const key of removals) delete (next as Record<string, unknown>)[key]
|
||||
await scope.replace(next)
|
||||
} else if (Object.keys(patch).length > 0) {
|
||||
await scope.update(patch)
|
||||
// Apply non-removal patches first (scope.update merges into the user
|
||||
// layer without disturbing other fields).
|
||||
if (Object.keys(patch).length > 0) await scope.update(patch)
|
||||
// Remove fields via path-level unset ops: this deletes the key from
|
||||
// the user layer so it re-inherits the entry base and schema defaults.
|
||||
// Using scope.replace() with the resolved config would bake defaults
|
||||
// (endpointHost, chatTimeoutMs) and entry values into the user layer,
|
||||
// shadowing future entry changes and polluting the stored document.
|
||||
if (removals.size > 0 && settings) {
|
||||
for (const key of removals) {
|
||||
await settings.mutate(SETTINGS_NS, [{ op: 'unset', path: [key] }])
|
||||
}
|
||||
}
|
||||
sendJson(res, 200, scope.get())
|
||||
} catch (err) {
|
||||
sendJson(res, 500, { error: err instanceof Error ? err.message : 'settings write failed' })
|
||||
}
|
||||
},
|
||||
}), 'tool-bailian-kb: settings POST route')
|
||||
}), 'tool-bailian-kb: settings bridge route')
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user