dsh-model-headers 0.2.0 → 0.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/lib/client.js CHANGED
@@ -4,7 +4,7 @@
4
4
  * 设置页 section「模型请求头」:
5
5
  * - 规则卡片列表(双列):启停开关 / 模型通配符 / 头摘要 / 编辑 / 删除(二次确认)
6
6
  * - 添加·编辑弹框:模型下拉(combobox:点选 settings.yaml 现有模型 / 推导通配符 / 手输任意)+ 头键值行(值可插入 ${sessionId} 变量)
7
- * - 通信:connection RPC 通道 '/model-headers',信封 {ok, value | error{message}}
7
+ * - 通信:dsh v0.1.5 的 /api 通道 POST /api/model-headers/*,信封 {ok, value | error{message}}
8
8
  * - 挂载点:settings.section 槽位(order 100,排在「技能与MCP」之后)。
9
9
  */
10
10
  window.__ModuleLoader__.load({
@@ -17,13 +17,13 @@ window.__ModuleLoader__.load({
17
17
 
18
18
  const INJECT = ["slots", "connection"];
19
19
 
20
- /** connection RPC 通道(与 host 半一致)。 */
21
- const RPC_CHANNEL = "/model-headers";
20
+ /** dsh v0.1.5:浏览器侧统一走 /api 通道,endpoint 带插件前缀。 */
21
+ const RPC_PREFIX = "model-headers";
22
22
 
23
23
  let hostCtx = null;
24
24
 
25
25
  async function callApi(endpoint, body = {}) {
26
- const result = await hostCtx.connection.rpc.call(RPC_CHANNEL, endpoint, body);
26
+ const result = await hostCtx.connection.rpc.call("/api", `${RPC_PREFIX}/${endpoint}`, body);
27
27
  if (result.ok) return result.value;
28
28
  throw new Error(result.error?.message || `rpc ${endpoint} failed`);
29
29
  }
package/lib/index.js CHANGED
@@ -16,8 +16,9 @@
16
16
  * dsh 原生请求头(x-client-request-id / session_id)在此之前已就位,作为
17
17
  * sessionId 取值来源。
18
18
  *
19
- * 管理:connection RPC 通道 '/model-headers'(loopback),配置文件 fs.watch
20
- * 热加载。每次实际注入追加一行到 ~/.dsh/model-headers.log 供核验(>5MB 截断)。
19
+ * 管理:dsh v0.1.5 传输模型下注册精确 POST 路由 /api/model-headers/*,
20
+ * 配置文件 fs.watch 热加载。每次实际注入追加一行到
21
+ * ~/.dsh/model-headers.log 供核验(>5MB 截断)。
21
22
  */
22
23
 
23
24
  import { watch as fsWatch } from 'node:fs'
@@ -27,6 +28,8 @@ import path from 'node:path'
27
28
  import { randomUUID } from 'node:crypto'
28
29
 
29
30
  export const name = 'dsh-model-headers'
31
+ // 保持空 inject:静态声明只影响自身 fiber 启动门槛,headless 无 webServer 会让
32
+ // 插件永不装配(自愈/热加载失效)。RPC 的服务等待改用 apply 内 ctx.inject 回调。
30
33
  export const inject = []
31
34
  export { ensureCacheRetention, listConfiguredModels }
32
35
 
@@ -35,7 +38,7 @@ const CONFIG_FILE = () => path.join(DSH_HOME(), 'model-headers.json')
35
38
  const LOG_FILE = () => path.join(DSH_HOME(), 'model-headers.log')
36
39
  const SETTINGS_FILE = () => path.join(DSH_HOME(), 'settings.yaml')
37
40
  const LOG_MAX_BYTES = 5 * 1024 * 1024
38
- const RPC_CHANNEL = '/model-headers'
41
+ const RPC_PREFIX = 'model-headers' // 挂在 /api/<prefix>/<endpoint> 下的精确 POST 路由
39
42
  const GUARD = Symbol.for('dsh.model-headers.installed')
40
43
 
41
44
  const e2str = (e) => String(e?.message || e).slice(0, 160)
@@ -415,35 +418,45 @@ export function apply(ctx) {
415
418
  await loadRules()
416
419
  await ensureCacheRetention()
417
420
  await watchConfig()
418
- console.log(`[dsh-model-headers] host ready: ${rules.length} rule(s), rpc '${RPC_CHANNEL}'`)
421
+ console.log(`[dsh-model-headers] host ready: ${rules.length} rule(s)`)
419
422
  })()
420
423
 
421
- // connection 是 web profile 的服务,headless 没有 → 可选获取,取不到就跳过 RPC
422
- //(fetch 钩子与配置文件热加载不依赖它)。ctx.get 绕过 inject 要求直读服务店。
423
- const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms))
424
- void (async () => {
425
- let connection
426
- for (let attempt = 0; attempt < 10; attempt++) {
427
- connection = ctx.get?.('connection')
428
- if (connection?.rpc?.handle) break
429
- connection = undefined
430
- await sleep(1000)
431
- }
432
- if (!connection?.rpc?.handle) {
433
- console.log('[dsh-model-headers] 无 connection 服务(headless):RPC 关闭,注入钩子照常工作')
434
- return
424
+ // ---- RPC(dsh v0.1.5 新传输模型)----
425
+ // 浏览器侧固定 POST /api/<endpoint>;插件用 connection.fetch.register
426
+ // 注册精确 POST 路由(fetchRoutes 优先于网关 interceptor)。register 内部
427
+ // 经 owner.webServer.register 落路由,故回调 fiber 链上需同时有
428
+ // connection 与 webServer。信封:client-request {rpcId,method,payload} →
429
+ // server-response {rpcId,result:{ok,value|error}}。
430
+ const okResponse = (rpcId, result) => new Response(
431
+ JSON.stringify({ type: 'server-response', rpcId, result }),
432
+ { status: 200, headers: { 'content-type': 'application/json' } },
433
+ )
434
+ // fetch.register 内部为 owner.effect(() => owner.webServer.register(route)),
435
+ // 解析的是本回调 fiber 作用域链上的 webServer——只 inject connection 会抛
436
+ // "cannot get property webServer without inject"。web profile 两者就绪即注册;
437
+ // headless 无 webServer 时回调静默等待(无需 HTTP 路由),插件本体仍正常装配。
438
+ ctx.inject(['connection', 'webServer'], (svc) => {
439
+ for (const [name, action] of Object.entries(endpoints)) {
440
+ const path = `/api/${RPC_PREFIX}/${name}`
441
+ svc.connection.fetch.register({
442
+ path,
443
+ methods: ['POST'],
444
+ requestBody: 'buffered',
445
+ fetch: async (request) => {
446
+ let rpcId = ''
447
+ try {
448
+ const msg = await request.json()
449
+ rpcId = typeof msg?.rpcId === 'string' ? msg.rpcId : ''
450
+ const value = await action.run(msg?.payload ?? {})
451
+ return okResponse(rpcId, rpcOk(value))
452
+ } catch (e) {
453
+ return okResponse(rpcId, rpcFail(e2str(e)))
454
+ }
455
+ },
456
+ })
435
457
  }
436
- connection.rpc.handle(RPC_CHANNEL, async (endpoint, payload) => {
437
- try {
438
- const action = endpoints[endpoint]
439
- if (action === undefined) return rpcFail(`unknown endpoint "${endpoint}"`)
440
- return rpcOk(await action.run(payload ?? {}))
441
- } catch (e) {
442
- return rpcFail(e2str(e))
443
- }
444
- }, { authority: 'loopback' })
445
- console.log(`[dsh-model-headers] rpc '${RPC_CHANNEL}' ready`)
446
- })()
458
+ console.log(`[dsh-model-headers] rpc routes ready under /api/${RPC_PREFIX} (${Object.keys(endpoints).length} endpoints)`)
459
+ })
447
460
 
448
461
  // 文件监听随插件卸载关闭;fetch 钩子恢复原状(配合热重载)。
449
462
  ctx.effect(() => () => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-model-headers",
3
- "version": "0.2.0",
3
+ "version": "0.2.2",
4
4
  "description": "Per-model custom request headers: inject headers into LLM requests matched by model-name wildcard; values support fixed strings and the ${sessionId} variable. Managed from a settings-page UI.",
5
5
  "license": "BSD-3-Clause",
6
6
  "homepage": "https://github.com/LvanLa/deepseek-harness-ex/tree/master/plugins/dsh-model-headers",