@luchenglong/cli-bridge 0.2.4 → 0.2.6
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/CHANGELOG.md +73 -0
- package/docs/HANDOVER.md +201 -51
- package/dynamic/agy.host.js +1 -1
- package/dynamic/codebuddy.host.js +1332 -925
- package/dynamic/codebuddy.host.template.mjs +1 -0
- package/dynamic/mimo.host.js +314 -548
- package/dynamic/mimo.host.template.mjs +43 -30
- package/dynamic/opencode.host.js +32 -22
- package/home-plugin/cli-bridge-indicator/lib/preset-declarations.mjs +11 -14
- package/mcp/cli-bridge-mcp.mjs +1 -1
- package/mcp/providers/codebuddy.mjs +427 -398
- package/mcp/providers/mimo.mjs +37 -68
- package/mcp/providers/opencode.mjs +28 -1
- package/package.json +3 -1
- package/preset/codebuddy-first/cli-bridge.mjs +12 -0
- package/preset/mimo-first/cli-bridge.mjs +32 -50
- package/providers/codebuddy-catalog.mjs +605 -0
- package/providers/codebuddy-models-tool.mjs +106 -0
- package/providers/codebuddy.mjs +636 -926
- package/providers/mimo.mjs +271 -517
- package/providers/opencode.mjs +31 -21
- package/scripts/build.mjs +262 -235
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
// providers/codebuddy-models-tool.mjs — codebuddy_models 工具定义(纯逻辑)。
|
|
2
|
+
//
|
|
3
|
+
// 从 codebuddy-catalog.mjs 再拆出:两个文件都要留在 600 行红线内(规则 §26)。
|
|
4
|
+
// scripts/build.mjs 会把 providers/codebuddy-*.mjs 全部内联进 dynamic 沙箱的同一个
|
|
5
|
+
// 作用域,所以这里仍可引用 catalog 的函数(沙箱内天然可见);ESM 形态走本文件的 import。
|
|
6
|
+
// ⚠️ 注释里绝不写出口令标记字面量(build 全局替换会劈开注释 → 生成物语法崩)。
|
|
7
|
+
|
|
8
|
+
import { currentPreferredBackend, probeAccountModels, fetchHelpModelsSync } from './codebuddy-catalog.mjs'
|
|
9
|
+
|
|
10
|
+
// ── codebuddy_models 工具定义(搬自主文件;纯逻辑,依赖经 ctx 注入)────────────
|
|
11
|
+
// ctx = { env, deps, resolveBackend, modelCatalogDetailed, getAccountCatalog }
|
|
12
|
+
export function codebuddyModelsTool(ctx) {
|
|
13
|
+
const c = ctx || {}
|
|
14
|
+
const deps = c.deps || {}
|
|
15
|
+
const env = c.env || {}
|
|
16
|
+
return {
|
|
17
|
+
name: 'codebuddy_models',
|
|
18
|
+
description: 'List the models the local codebuddy CLI can use for a given backend, read LIVE from the CLI. Primary source: an account probe (runs the CLI with a deliberately invalid model id, which makes the server print "Currently supported models for your account") — this is the authoritative licensed list for THIS account. If that face is not logged in, it falls back to the roster the installed CLI declares in --help and says so in the source field. Credit multipliers (x0.00 = free) come from the product descriptor as metadata. Returns ok:false with an actionable error when no live roster can be read — it never presents a frozen static table as if it were live.',
|
|
19
|
+
inputSchema: {
|
|
20
|
+
type: 'object', additionalProperties: false, required: [],
|
|
21
|
+
properties: {
|
|
22
|
+
backend: { type: 'string', enum: c.BACKEND_ENUM, description: 'Which product face to list (default: the preferred backend).' },
|
|
23
|
+
freeOnly: { type: 'boolean', description: 'Return only x0.00 (free) models.' },
|
|
24
|
+
authorizedOnly: { type: 'boolean', description: 'Return only ids the current account is licensed for (harvested from a prior run).' }
|
|
25
|
+
}
|
|
26
|
+
},
|
|
27
|
+
render: (args, value) => {
|
|
28
|
+
const v = value || {}
|
|
29
|
+
if (!v.ok) return [{ type: 'text', text: 'codebuddy_models failed: ' + String(v.error || 'unknown') }]
|
|
30
|
+
const list = Array.isArray(v.models) ? v.models : []
|
|
31
|
+
if (!list.length) return [{ type: 'text', text: 'codebuddy_models: no models matched (backend=' + (v.backend || '?') + ')' }]
|
|
32
|
+
const lines = ['codebuddy models [' + v.backend + '] (' + list.length + '):']
|
|
33
|
+
for (const m of list.slice(0, 40)) {
|
|
34
|
+
const flags = []
|
|
35
|
+
if (m.authorized) flags.push('authorized')
|
|
36
|
+
if (m.credits === 0) flags.push('free')
|
|
37
|
+
lines.push(' ' + m.id + (m.creditLabel ? ' ' + m.creditLabel : '') + (flags.length ? ' [' + flags.join(',') + ']' : ''))
|
|
38
|
+
}
|
|
39
|
+
if (list.length > 40) lines.push(' … and ' + (list.length - 40) + ' more')
|
|
40
|
+
const srcLabel = v.source === 'account-live'
|
|
41
|
+
? 'account-live (authoritative for this account)'
|
|
42
|
+
: 'cli-help (installed CLI roster, not the licensed subset)'
|
|
43
|
+
lines.push('source: ' + srcLabel)
|
|
44
|
+
if (v.note) lines.push('note: ' + v.note)
|
|
45
|
+
return [{ type: 'text', text: lines.join('\n') }]
|
|
46
|
+
},
|
|
47
|
+
async logic(args) {
|
|
48
|
+
const a = args || {}
|
|
49
|
+
const preferred = currentPreferredBackend() || (deps.settings && deps.settings.preferredBackend) || null
|
|
50
|
+
const backend = c.normalizeBackend(a.backend) || c.resolveBackend({}, null, preferred)
|
|
51
|
+
|
|
52
|
+
// ① 账号实时(最权威,对照 workbuddy-auth 的 agents[cli] 白名单语义)。
|
|
53
|
+
// 该面未登录/无 token 时探测会失败 —— 失败就**明确降级到 CLI 名册并标注**,
|
|
54
|
+
// 绝不把静态表冒充成账号可用清单。
|
|
55
|
+
const extraEnv = (c.faceEnv && c.faceEnv(backend)) || {}
|
|
56
|
+
const probe = await probeAccountModels(env, { envExtra: extraEnv, timeoutMs: a.timeoutMs })
|
|
57
|
+
let ids = null
|
|
58
|
+
let source = null
|
|
59
|
+
let note = ''
|
|
60
|
+
if (probe.ok && probe.ids.length) {
|
|
61
|
+
ids = probe.ids
|
|
62
|
+
source = 'account-live'
|
|
63
|
+
} else {
|
|
64
|
+
// ② CLI 声明的 roster(零网络,随 CLI 版本更新)。
|
|
65
|
+
const help = fetchHelpModelsSync(env, { timeoutMs: a.timeoutMs })
|
|
66
|
+
if (help.ok && help.ids.length) {
|
|
67
|
+
ids = help.ids
|
|
68
|
+
source = 'cli-help'
|
|
69
|
+
note = 'account probe unavailable (' + String(probe.error || 'not logged in for this face') +
|
|
70
|
+
'); showing the roster the installed CLI declares. It reflects the CLI version, not the licensed subset of this account.'
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
if (!ids || !ids.length) {
|
|
74
|
+
return {
|
|
75
|
+
ok: false, backend,
|
|
76
|
+
error: 'no live model roster could be read for backend "' + backend + '". ' +
|
|
77
|
+
'Account probe: ' + String(probe.error || 'failed') + '. ' +
|
|
78
|
+
'Install/fix the codebuddy CLI (CODEBUDDY_BIN) and make sure this face is logged in, then retry.'
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// ③ 倍率/授权元数据(来自描述文件;仅作标注,不参与「是否可用」判定)。
|
|
83
|
+
let detail = []
|
|
84
|
+
try { detail = c.modelCatalogDetailed(backend, { env, accountModels: ids }) || [] } catch (e) { detail = [] }
|
|
85
|
+
const byId = new Map(detail.map((x) => [x.id, x]))
|
|
86
|
+
let list = ids.map((id) => {
|
|
87
|
+
const d = byId.get(id)
|
|
88
|
+
return {
|
|
89
|
+
id,
|
|
90
|
+
authorized: d ? !!d.authorized : (source === 'account-live'),
|
|
91
|
+
credits: d && typeof d.credits === 'number' ? d.credits : null,
|
|
92
|
+
creditLabel: (d && (d.creditLabel || d.label)) || '',
|
|
93
|
+
label: (d && d.label) || id
|
|
94
|
+
}
|
|
95
|
+
})
|
|
96
|
+
if (a.authorizedOnly) list = list.filter((m) => m.authorized)
|
|
97
|
+
if (a.freeOnly) list = list.filter((m) => m.credits === 0)
|
|
98
|
+
return {
|
|
99
|
+
ok: true, backend, models: list, count: list.length,
|
|
100
|
+
source, note: note || undefined,
|
|
101
|
+
probedAt: Date.now()
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|