cs-bots 0.0.0-stage → 0.1.1
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/LICENSE +21 -0
- package/README.md +164 -2
- package/cordis.patch.agent-team.yml +31 -0
- package/cordis.patch.yml +30 -0
- package/install.sh +218 -0
- package/package.json +57 -4
- package/src/api.js +706 -0
- package/src/board.js +1056 -0
- package/src/botprofile.js +331 -0
- package/src/client.js +2089 -0
- package/src/compat.js +196 -0
- package/src/dirbrowse.js +555 -0
- package/src/dispatcher.js +651 -0
- package/src/index.js +311 -0
- package/src/kernel.js +480 -0
- package/src/library.js +740 -0
- package/src/memory.js +1905 -0
- package/src/registry.js +34 -0
- package/src/room.js +1550 -0
- package/src/roster.js +1119 -0
- package/src/settings.js +107 -0
- package/src/store.js +243 -0
- package/src/tools.js +469 -0
package/src/compat.js
ADDED
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* cs-bots /compat —— 宿主兼容性探测(**防止插件把 DSH 启动搞崩**)。
|
|
3
|
+
*
|
|
4
|
+
* 为什么必须有这一段(来自真实教训):
|
|
5
|
+
* 宿主插件在 `apply()` 里抛错 → **DSH 启动失败** → 用户必须「禁用插件才能打开」。
|
|
6
|
+
* 我们已在 rc.2 上踩到两个同类 bug(ctx 禁止任意属性赋值 / 未 inject 就取服务)。
|
|
7
|
+
* rc1 与 rc5 的 API 面与 rc.2 **不完全相同**,所以必须在**运行时探测**,
|
|
8
|
+
* 而不是假定宿主有某个能力。
|
|
9
|
+
*
|
|
10
|
+
* 三条纪律:
|
|
11
|
+
* 1. **探测本身绝不抛**:任何一次探测失败都只是把该能力记为 "missing"。
|
|
12
|
+
* 2. **报告可见**:探测结果写日志,并暴露给 /status(用户能自助诊断)。
|
|
13
|
+
* 3. **不阻断**:缺能力 → 对应功能降级,**不影响其它功能,更不影响 DSH 启动**。
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* 从 dsh 安装目录读版本号(**绝不抛**)。
|
|
18
|
+
*
|
|
19
|
+
* 为什么要读它:主管环境是 rc1(可升 rc5),我们必须能报出真实版本,
|
|
20
|
+
* 否则「兼容性差异」只能靠猜。
|
|
21
|
+
* @returns {string | undefined}
|
|
22
|
+
*/
|
|
23
|
+
function readDshVersion() {
|
|
24
|
+
try {
|
|
25
|
+
return safeReadVersion()
|
|
26
|
+
} catch {
|
|
27
|
+
return undefined
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function safeReadVersion() {
|
|
32
|
+
// 动态引入 node 内置(避免在浏览器/受限环境报错)
|
|
33
|
+
const { createRequire } = requireNode('node:module')
|
|
34
|
+
const { existsSync, readFileSync } = requireNode('node:fs')
|
|
35
|
+
if (!createRequire || !readFileSync) return undefined
|
|
36
|
+
|
|
37
|
+
const anchors = []
|
|
38
|
+
try {
|
|
39
|
+
anchors.push(new URL('../../package.json', import.meta.url).pathname) // 同级/上级
|
|
40
|
+
} catch {}
|
|
41
|
+
anchors.push('/usr/lib/node_modules/@deepseek-ai/dsh/package.json')
|
|
42
|
+
anchors.push('/usr/local/lib/node_modules/@deepseek-ai/dsh/package.json')
|
|
43
|
+
for (const dir of String(process.env.PATH ?? '').split(':')) {
|
|
44
|
+
if (!dir) continue
|
|
45
|
+
anchors.push(`${dir}/../lib/node_modules/@deepseek-ai/dsh/package.json`)
|
|
46
|
+
anchors.push(`${dir}/../node_modules/@deepseek-ai/dsh/package.json`)
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
for (const file of anchors) {
|
|
50
|
+
try {
|
|
51
|
+
if (!file || (file.startsWith('/') && !existsSync(file))) continue
|
|
52
|
+
const pkg = JSON.parse(readFileSync(file, 'utf8'))
|
|
53
|
+
if (pkg?.name === '@deepseek-ai/dsh' && typeof pkg.version === 'string') return pkg.version
|
|
54
|
+
} catch {
|
|
55
|
+
/* 单个锚点失败不影响其它 */
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
// 最后试 require.resolve(宿主内运行时通常能解析到)
|
|
59
|
+
try {
|
|
60
|
+
const req = createRequire(anchors.find((a) => a && (!a.startsWith('/') || existsSync(a))) ?? import.meta.url)
|
|
61
|
+
const pkg = req('@deepseek-ai/dsh/package.json')
|
|
62
|
+
if (typeof pkg?.version === 'string') return pkg.version
|
|
63
|
+
} catch {}
|
|
64
|
+
return undefined
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/** 惰性取 node 内置模块(失败返回 undefined,绝不抛)。 */
|
|
68
|
+
function requireNode(spec) {
|
|
69
|
+
try {
|
|
70
|
+
// 顶层已用 ESM;这里用 process.getBuiltinModule(Node 22+)或 globalThis.require 兜底
|
|
71
|
+
if (typeof process.getBuiltinModule === 'function') return process.getBuiltinModule(spec) ?? {}
|
|
72
|
+
return {}
|
|
73
|
+
} catch {
|
|
74
|
+
return {}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* 尝试求值,失败返回 undefined(**绝不抛**)。
|
|
80
|
+
* @template T
|
|
81
|
+
* @param {() => T} fn
|
|
82
|
+
* @returns {T | undefined}
|
|
83
|
+
*/
|
|
84
|
+
function safe(fn) {
|
|
85
|
+
try {
|
|
86
|
+
return fn()
|
|
87
|
+
} catch {
|
|
88
|
+
return undefined
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* 探测宿主能力矩阵。
|
|
94
|
+
*
|
|
95
|
+
* @param {object} ctx 宿主上下文
|
|
96
|
+
* @param {{ warn?: Function }} [deps]
|
|
97
|
+
* @returns {{
|
|
98
|
+
* dshVersion: string | undefined,
|
|
99
|
+
* node: string,
|
|
100
|
+
* available: Record<string, boolean>,
|
|
101
|
+
* missing: string[],
|
|
102
|
+
* notes: string[],
|
|
103
|
+
* }}
|
|
104
|
+
*/
|
|
105
|
+
export function probeHost(ctx, { warn } = {}) {
|
|
106
|
+
const available = {}
|
|
107
|
+
const notes = []
|
|
108
|
+
|
|
109
|
+
/** 判一项能力:谓词通过即 true。 */
|
|
110
|
+
const check = (name, predicate) => {
|
|
111
|
+
const ok = safe(() => predicate() === true) === true
|
|
112
|
+
available[name] = ok
|
|
113
|
+
return ok
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// ── 上下文基础能力 ──────────────────────────────────────────────────────────
|
|
117
|
+
check('ctx.reflect.get', () => typeof ctx?.reflect?.get === 'function')
|
|
118
|
+
check('ctx.get', () => typeof ctx?.get === 'function')
|
|
119
|
+
check('ctx.on', () => typeof ctx?.on === 'function')
|
|
120
|
+
check('ctx.effect', () => typeof ctx?.effect === 'function')
|
|
121
|
+
|
|
122
|
+
// `ctx.reflect.get` 是「不声明 inject 也能读服务」的唯一通路;
|
|
123
|
+
// 缺它时只能退到 ctx.get / ctx[key],而这两条在真实 ctx 上会抛。
|
|
124
|
+
if (!available['ctx.reflect.get']) {
|
|
125
|
+
notes.push('缺 ctx.reflect.get → 服务读取可能全部失败(插件会降级为"空壳但能启动")')
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
// ── 服务:tools ────────────────────────────────────────────────────────────
|
|
129
|
+
const tools = safe(() => ctx?.reflect?.get?.('tools', false)) ?? safe(() => ctx?.tools)
|
|
130
|
+
check('service.tools', () => tools !== undefined)
|
|
131
|
+
check('tools.register', () => typeof tools?.register === 'function')
|
|
132
|
+
check('tools.guard', () => typeof tools?.guard === 'function')
|
|
133
|
+
check('tools.restrict', () => typeof tools?.restrict === 'function')
|
|
134
|
+
check('tools.schemas', () => typeof tools?.schemas === 'function')
|
|
135
|
+
|
|
136
|
+
// ── 服务:webServer(页面数据面)────────────────────────────────────────────
|
|
137
|
+
const webServer = safe(() => ctx?.reflect?.get?.('webServer', false))
|
|
138
|
+
check('service.webServer', () => webServer !== undefined)
|
|
139
|
+
check('webServer.register', () => typeof webServer?.register === 'function')
|
|
140
|
+
|
|
141
|
+
// ── 服务:agents(Bot 独立目录的唯一路径)──────────────────────────────────
|
|
142
|
+
const agents = safe(() => ctx?.reflect?.get?.('agents', false))
|
|
143
|
+
check('service.agents', () => agents !== undefined)
|
|
144
|
+
check('agents.create', () => typeof agents?.create === 'function')
|
|
145
|
+
check('agents.get', () => typeof agents?.get === 'function')
|
|
146
|
+
check('agents.list', () => typeof agents?.list === 'function')
|
|
147
|
+
check('agents.resume', () => typeof agents?.resume === 'function')
|
|
148
|
+
|
|
149
|
+
// ── 可选增强(缺了只影响官方 Team 相关,不影响本平台自有能力)─────────────
|
|
150
|
+
check('service.agentTeams', () => safe(() => ctx?.reflect?.get?.('agentTeams', false)) !== undefined)
|
|
151
|
+
|
|
152
|
+
// ── 版本信息(拿不到就留空,不猜)──────────────────────────────────────────
|
|
153
|
+
// 依次尝试:环境变量 → profileContext → 从 dsh 安装目录读 package.json
|
|
154
|
+
const dshVersion =
|
|
155
|
+
safe(() => process.env.DSH_VERSION) ??
|
|
156
|
+
safe(() => ctx?.reflect?.get?.('profileContext', false)?.dshVersion) ??
|
|
157
|
+
readDshVersion() ??
|
|
158
|
+
undefined
|
|
159
|
+
|
|
160
|
+
const node = safe(() => process.version) ?? 'unknown'
|
|
161
|
+
|
|
162
|
+
const missing = Object.entries(available)
|
|
163
|
+
.filter(([, ok]) => !ok)
|
|
164
|
+
.map(([name]) => name)
|
|
165
|
+
|
|
166
|
+
// 只对「影响核心功能」的缺失发 warn(可选增强不吵)
|
|
167
|
+
const critical = ['tools.register', 'webServer.register', 'agents.create']
|
|
168
|
+
const criticalMissing = missing.filter((m) => critical.includes(m))
|
|
169
|
+
if (criticalMissing.length && typeof warn === 'function') {
|
|
170
|
+
warn(
|
|
171
|
+
`[cs-bots] 宿主缺少关键能力:${criticalMissing.join(', ')} → 相关功能将降级(插件仍会正常启动)`,
|
|
172
|
+
)
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
return {
|
|
176
|
+
dshVersion: typeof dshVersion === 'string' ? dshVersion : undefined,
|
|
177
|
+
node,
|
|
178
|
+
available,
|
|
179
|
+
missing,
|
|
180
|
+
notes,
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
/**
|
|
185
|
+
* 把探测结果整理成一行可读摘要(写日志/显示在 UI 用)。
|
|
186
|
+
* @param {ReturnType<typeof probeHost>} report
|
|
187
|
+
* @returns {string}
|
|
188
|
+
*/
|
|
189
|
+
export function formatReport(report) {
|
|
190
|
+
const ok = Object.entries(report.available).filter(([, v]) => v).length
|
|
191
|
+
const total = Object.keys(report.available).length
|
|
192
|
+
const parts = [`能力 ${ok}/${total}`, `node=${report.node}`]
|
|
193
|
+
if (report.dshVersion) parts.push(`dsh=${report.dshVersion}`)
|
|
194
|
+
if (report.missing.length) parts.push(`缺: ${report.missing.join(', ')}`)
|
|
195
|
+
return parts.join(' · ')
|
|
196
|
+
}
|