dsh-cnb-assist 1.0.3 → 1.0.4
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.md +15 -2
- package/client.js +3 -3
- package/cordis.patch.yml +3 -1
- package/index.js +1 -1
- package/package.json +9 -15
- package/selfcheck-identity.mjs +108 -0
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# CNB 反代辅助
|
|
2
2
|
|
|
3
|
-
DeepSeek Harness(DSH)插件。把 CNB 的额度、AI 用量和运行中容器的反代地址放进 IDE 侧栏,
|
|
3
|
+
DeepSeek Harness(DSH)插件。把 CNB 的额度、AI 用量和运行中容器的反代地址放进 IDE 侧栏,
|
|
4
4
|
并提供「一键写回模型地址」和「四步重启容器」。
|
|
5
5
|
|
|
6
6
|
- **令牌不出 Host 进程** —— 浏览器那半只拿到算好的数字。
|
|
@@ -17,7 +17,7 @@ DeepSeek Harness(DSH)插件。把 CNB 的额度、AI 用量和运行中容
|
|
|
17
17
|
plugin_manager → install_bundle → target: dsh-cnb-assist
|
|
18
18
|
```
|
|
19
19
|
|
|
20
|
-
要固定版本就写 `dsh-cnb-assist@1.0.
|
|
20
|
+
要固定版本就写 `dsh-cnb-assist@1.0.4`。
|
|
21
21
|
|
|
22
22
|
离线安装也行,把 `dsh-cnb-assist-*.tgz` 拷到目标机器后填该文件的绝对路径。
|
|
23
23
|
安装器认**目录**和 **`.tgz`/`.tar.gz`**,`.zip` 不被接受。
|
|
@@ -59,6 +59,17 @@ plugin_manager → install_bundle → target: dsh-cnb-assist
|
|
|
59
59
|
容器那一行点「设为模型地址」,插件把 `https://<business_id>-9001.cnb.run` 写进
|
|
60
60
|
设置里那个 provider 的 `baseURL`。写前会留 `.bak`,只替换地址字符串本身。
|
|
61
61
|
|
|
62
|
+
### 反代地址从哪来
|
|
63
|
+
|
|
64
|
+
优先读容器构建日志里平台注入的模板行:
|
|
65
|
+
|
|
66
|
+
```
|
|
67
|
+
PROXY_URI=https://<business_id>-{{port}}.cnb.run
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
把 `{{port}}` 换成 `9001` 后作为地址显示。日志里取不到时,才用容器的 `business_id`
|
|
71
|
+
直接拼 `https://<business_id>-9001.cnb.run`。两条路都走,有哪个显示哪个。
|
|
72
|
+
|
|
62
73
|
---
|
|
63
74
|
|
|
64
75
|
## 面板之外
|
|
@@ -85,6 +96,8 @@ plugin_manager → install_bundle → target: dsh-cnb-assist
|
|
|
85
96
|
|
|
86
97
|
**重启后地址变了** —— 正常。重启会重建容器,`business_id` 变,地址跟着变。
|
|
87
98
|
|
|
99
|
+
**地址里出现 `{{port}}`** —— 升级到 1.0.4 及以上。
|
|
100
|
+
|
|
88
101
|
**改完配置没生效** —— `cordis.patch.yml` 属于 Host 层,要重启 dsh,刷新页面不够。
|
|
89
102
|
|
|
90
103
|
---
|
package/client.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* dsh-cnb-assist — Client half.
|
|
3
3
|
*
|
|
4
4
|
* 三个落点,按有没有装 dsh-better-sidebar 自动选:
|
|
5
5
|
* 1. better-sidebar 在 → 右侧栏页签「CNB」(页签 = 一个组件,数据经 host.call 从 Host 半身取)
|
|
@@ -10,13 +10,13 @@
|
|
|
10
10
|
* 样式只用 --dsw-alias-* 主题令牌,亮色/暗色跟随宿主。
|
|
11
11
|
*/
|
|
12
12
|
window.__ModuleLoader__.load({
|
|
13
|
-
id: '
|
|
13
|
+
id: 'dsh-cnb-assist',
|
|
14
14
|
factory(require) {
|
|
15
15
|
const React = require('react')
|
|
16
16
|
const h = React.createElement
|
|
17
17
|
const { useState, useEffect, useMemo, useRef, useSyncExternalStore, createContext, useContext } = React
|
|
18
18
|
|
|
19
|
-
const PLUGIN_ID = '
|
|
19
|
+
const PLUGIN_ID = 'dsh-cnb-assist'
|
|
20
20
|
const ROUTE = 'cnb-assist/api'
|
|
21
21
|
const ASSIST_KEY = 'dsh-cnb-assist'
|
|
22
22
|
/** 本插件在宿主 locale 注册表里的命名空间(也是 slot 项的 locale 标记)。 */
|
package/cordis.patch.yml
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
# CNB 反代辅助(DSH 插件)
|
|
2
2
|
#
|
|
3
3
|
# 一行 Host 半身:提供 cnbAssist 服务、/cnb-assist/api 路由、cnb_assist 工具。
|
|
4
|
-
# 浏览器半身由 package.json 的 dsh.client
|
|
4
|
+
# 浏览器半身由 package.json 的 dsh.client 自动加载:
|
|
5
|
+
# · 装了 dsh-better-sidebar → 右侧栏页签「CNB」
|
|
6
|
+
# · 没装 → 侧栏底部一个按钮 + 居中浮层面板
|
|
5
7
|
- insert:
|
|
6
8
|
- id: cnb-assist
|
|
7
9
|
name: 'dsh-cnb-assist'
|
package/index.js
CHANGED
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-cnb-assist",
|
|
3
|
-
"version": "1.0.
|
|
4
|
-
"description": "CNB
|
|
3
|
+
"version": "1.0.4",
|
|
4
|
+
"description": "CNB 反代辅助:额度、本月 AI 用量、运行中容器反代地址,一键写回 DSH 模型配置",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "index.js",
|
|
7
7
|
"exports": {
|
|
@@ -18,26 +18,16 @@
|
|
|
18
18
|
"locale",
|
|
19
19
|
"README.md",
|
|
20
20
|
"selfcheck.mjs",
|
|
21
|
-
"selfcheck-relay.mjs"
|
|
21
|
+
"selfcheck-relay.mjs",
|
|
22
|
+
"selfcheck-identity.mjs"
|
|
22
23
|
],
|
|
23
24
|
"icon": "./icon.svg",
|
|
24
25
|
"keywords": [
|
|
25
26
|
"dsh",
|
|
26
27
|
"dsh-plugin",
|
|
27
|
-
"
|
|
28
|
-
"deepseek-harness",
|
|
29
|
-
"cnb",
|
|
30
|
-
"cnb-cool"
|
|
28
|
+
"cnb"
|
|
31
29
|
],
|
|
32
30
|
"license": "MIT",
|
|
33
|
-
"repository": {
|
|
34
|
-
"type": "git",
|
|
35
|
-
"url": "git+https://github.com/lonelymud/dsh-cnb-assist.git"
|
|
36
|
-
},
|
|
37
|
-
"homepage": "https://cnb.cool",
|
|
38
|
-
"engines": {
|
|
39
|
-
"node": ">=20"
|
|
40
|
-
},
|
|
41
31
|
"dsh": {
|
|
42
32
|
"bundle": {
|
|
43
33
|
"patch": "./cordis.patch.yml"
|
|
@@ -50,5 +40,9 @@
|
|
|
50
40
|
"@deepseek-ai/dsh-client-locale"
|
|
51
41
|
]
|
|
52
42
|
}
|
|
43
|
+
},
|
|
44
|
+
"scripts": {
|
|
45
|
+
"test": "node selfcheck-identity.mjs && node selfcheck.mjs && node selfcheck-relay.mjs",
|
|
46
|
+
"prepublishOnly": "node selfcheck-identity.mjs && node selfcheck.mjs && node selfcheck-relay.mjs"
|
|
53
47
|
}
|
|
54
48
|
}
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 发布前一致性门禁。
|
|
3
|
+
*
|
|
4
|
+
* 背景:1.0.3 发出去后 dsh 启动崩溃,根因是 client.js 里的
|
|
5
|
+
* `__ModuleLoader__.load({ id })` 还写着 `@local/dsh-cnb-assist`,
|
|
6
|
+
* 而包名已经是 `dsh-cnb-assist`。客户端按 @local/... 注册,设置表单按新包名查,
|
|
7
|
+
* 两边对不上直接把宿主带崩。
|
|
8
|
+
*
|
|
9
|
+
* 这个脚本把这些"必须处处一致"的标识全部对一遍,任何一个跑偏就非零退出,
|
|
10
|
+
* 让 npm publish 在 commit 之前就失败。
|
|
11
|
+
*/
|
|
12
|
+
import assert from 'node:assert/strict'
|
|
13
|
+
import { readFileSync } from 'node:fs'
|
|
14
|
+
import { fileURLToPath } from 'node:url'
|
|
15
|
+
import { dirname, join } from 'node:path'
|
|
16
|
+
|
|
17
|
+
const here = dirname(fileURLToPath(import.meta.url))
|
|
18
|
+
const read = f => readFileSync(join(here, f), 'utf8')
|
|
19
|
+
|
|
20
|
+
const pkg = JSON.parse(read('package.json'))
|
|
21
|
+
const clientJs = read('client.js')
|
|
22
|
+
const indexJs = read('index.js')
|
|
23
|
+
const patchYml = read('cordis.patch.yml')
|
|
24
|
+
|
|
25
|
+
let pass = 0
|
|
26
|
+
function ok(name) {
|
|
27
|
+
pass += 1
|
|
28
|
+
console.log('PASS ', name)
|
|
29
|
+
}
|
|
30
|
+
function eq(name, actual, expected) {
|
|
31
|
+
assert.equal(actual, expected, `${name}\n actual : ${actual}\n expected: ${expected}`)
|
|
32
|
+
pass += 1
|
|
33
|
+
console.log('PASS ', name)
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const PLUGIN = pkg.name
|
|
37
|
+
|
|
38
|
+
// ── 1. 包名必须是公开可发布的形态
|
|
39
|
+
ok('包名非空')
|
|
40
|
+
assert.ok(PLUGIN && PLUGIN.length > 0)
|
|
41
|
+
ok('包名不是 @local/ 私有 scope')
|
|
42
|
+
assert.ok(!PLUGIN.startsWith('@local/'), `包名仍是私有 scope:${PLUGIN}`)
|
|
43
|
+
ok('没有 private:true')
|
|
44
|
+
assert.notEqual(pkg.private, true)
|
|
45
|
+
|
|
46
|
+
// ── 2. client.js 的加载 id 必须与包名一致
|
|
47
|
+
const loadId = /__ModuleLoader__\.load\(\{\s*\n\s*id:\s*'([^']+)'/.exec(clientJs)?.[1]
|
|
48
|
+
eq('client.js __ModuleLoader__ id 与包名一致', loadId, PLUGIN)
|
|
49
|
+
|
|
50
|
+
const pluginId = /const PLUGIN_ID = '([^']+)'/.exec(clientJs)?.[1]
|
|
51
|
+
eq('client.js PLUGIN_ID 与包名一致', pluginId, PLUGIN)
|
|
52
|
+
|
|
53
|
+
// ── 3. host 半身导出名必须与包名一致
|
|
54
|
+
const hostName = /export const name = '([^']+)'/.exec(indexJs)?.[1]
|
|
55
|
+
eq('index.js export name 与包名一致', hostName, PLUGIN)
|
|
56
|
+
|
|
57
|
+
// ── 4. bundle patch 里 insert 的 name 必须与包名一致
|
|
58
|
+
const patchName = /^\s*name:\s*'([^']+)'/m.exec(patchYml)?.[1]
|
|
59
|
+
eq('cordis.patch.yml insert name 与包名一致', patchName, PLUGIN)
|
|
60
|
+
|
|
61
|
+
// ── 5. 任何文件都不该再出现 @local/
|
|
62
|
+
const files = ['client.js', 'index.js', 'cordis.patch.yml', 'package.json', 'README.md',
|
|
63
|
+
'locale/zh.json', 'locale/en.json', 'selfcheck.mjs', 'selfcheck-relay.mjs']
|
|
64
|
+
const offenders = []
|
|
65
|
+
for (const f of files) {
|
|
66
|
+
let text
|
|
67
|
+
try { text = read(f) } catch { continue }
|
|
68
|
+
const lines = text.split('\n')
|
|
69
|
+
lines.forEach((line, i) => {
|
|
70
|
+
if (line.includes('@local/')) offenders.push(`${f}:${i + 1} ${line.trim()}`)
|
|
71
|
+
})
|
|
72
|
+
}
|
|
73
|
+
if (offenders.length > 0) {
|
|
74
|
+
console.log('\n发现 @local/ 残留:')
|
|
75
|
+
for (const o of offenders) console.log(' ' + o)
|
|
76
|
+
}
|
|
77
|
+
eq('全仓库无 @local/ 残留', offenders.length, 0)
|
|
78
|
+
|
|
79
|
+
// ── 6. files 清单里每一项都要真实存在(locale 作为目录)
|
|
80
|
+
const { existsSync } = await import('node:fs')
|
|
81
|
+
for (const entry of pkg.files ?? []) {
|
|
82
|
+
ok(`files 清单存在:${entry}`)
|
|
83
|
+
assert.ok(existsSync(join(here, entry)), `files 里列了不存在的条目:${entry}`)
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// ── 7. 两个必需文件必须在清单里
|
|
87
|
+
ok('index.js 在 files 里')
|
|
88
|
+
assert.ok((pkg.files ?? []).includes('index.js'))
|
|
89
|
+
ok('client.js 在 files 里')
|
|
90
|
+
assert.ok((pkg.files ?? []).includes('client.js'))
|
|
91
|
+
ok('cordis.patch.yml 在 files 里')
|
|
92
|
+
assert.ok((pkg.files ?? []).includes('cordis.patch.yml'))
|
|
93
|
+
|
|
94
|
+
// ── 8. dsh 清单字段完整
|
|
95
|
+
eq('dsh.bundle.patch 指向 cordis.patch.yml', pkg.dsh?.bundle?.patch, './cordis.patch.yml')
|
|
96
|
+
eq('dsh.client.platform 是 web', pkg.dsh?.client?.platform, 'web')
|
|
97
|
+
ok('dsh.client.immediately 为 true')
|
|
98
|
+
assert.equal(pkg.dsh?.client?.immediately, true)
|
|
99
|
+
ok('dsh.client.inject 含 ui-slots')
|
|
100
|
+
assert.ok(pkg.dsh?.client?.inject?.includes('@deepseek-ai/dsh-client-ui-slots'))
|
|
101
|
+
ok('dsh.client.inject 含 locale')
|
|
102
|
+
assert.ok(pkg.dsh?.client?.inject?.includes('@deepseek-ai/dsh-client-locale'))
|
|
103
|
+
|
|
104
|
+
// ── 9. 版本号形态
|
|
105
|
+
ok('version 是 x.y.z')
|
|
106
|
+
assert.match(pkg.version, /^\d+\.\d+\.\d+$/)
|
|
107
|
+
|
|
108
|
+
console.log(`\nall ${pass} consistency checks passed (package: ${PLUGIN}@${pkg.version})`)
|