dsh-model 0.22.3 → 0.23.0
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.en.md +1 -1
- package/README.md +1 -1
- package/dist/daemon/core.js +4 -1
- package/dist/integrations/workbuddy.js +24 -0
- package/package.json +1 -1
- package/plugin/client.js +14 -6
package/README.en.md
CHANGED
|
@@ -48,7 +48,7 @@ On a dsh-vps server, use `sudo dsh-model setup`. On Windows, run it in a normal
|
|
|
48
48
|
`setup` is safe to re-run. It:
|
|
49
49
|
|
|
50
50
|
1. downloads and verifies the engine and runs it on `127.0.0.1:8317/v1`, with key auth enforced;
|
|
51
|
-
2.
|
|
51
|
+
2. starts dsh-model's daemon (the management page, check-in and sign-ins run on it). Setup asks for no keys: for a free source we suggest **SiliconFlow** (reachable from China) or **Gemini** (if you can reach Google), added from the management page. WorkBuddy is used for check-in only; its models are not connected to dsh by default;
|
|
52
52
|
3. installs the management page into dsh and syncs the models into dsh;
|
|
53
53
|
4. on a VPS, also opens a public address `https://<dsh's domain>:9443/v1` (or `<IP>.sslip.io` when there is no domain).
|
|
54
54
|
|
package/README.md
CHANGED
|
@@ -48,7 +48,7 @@ dsh-model setup
|
|
|
48
48
|
`setup` 可以重复执行,它会依次:
|
|
49
49
|
|
|
50
50
|
1. 下载并校验引擎,在本机 `127.0.0.1:8317/v1` 上运行,强制 key 鉴权;
|
|
51
|
-
2.
|
|
51
|
+
2. 启动 dsh-model 的守护进程(管理页、签到、登录都靠它)。安装时不问任何 key:免费来源推荐**硅基流动**(国内直连)或 **Gemini**(能上外网),装好后在管理页里填 key。WorkBuddy 只用来签到,模型默认不接进 dsh;
|
|
52
52
|
3. 把管理页装进 dsh,把模型同步进 dsh;
|
|
53
53
|
4. 在 VPS 上,额外开通对外地址 `https://<dsh 的域名>:9443/v1`(没有域名时用 `<IP>.sslip.io`)。
|
|
54
54
|
|
package/dist/daemon/core.js
CHANGED
|
@@ -30,7 +30,7 @@ import { Mgmt } from '../engine/mgmt.js';
|
|
|
30
30
|
import { DshModelError, isDshModelError } from '../errors.js';
|
|
31
31
|
import { L } from '../i18n.js';
|
|
32
32
|
import { refreshOpencodeModels, removeOpencodeKey, saveOpencodeKey, validateKeyShape, verifyOpencodeKey } from '../integrations/opencode.js';
|
|
33
|
-
import { riskNotice as workbuddyRiskNotice } from '../integrations/workbuddy.js';
|
|
33
|
+
import { blockedNotice as workbuddyBlockedNotice, riskNotice as workbuddyRiskNotice } from '../integrations/workbuddy.js';
|
|
34
34
|
import { DSH_KEY_NAME, addKey, findActive, loadKeys, revokeKey, rotateKey, saveKeys } from '../keys.js';
|
|
35
35
|
import { applyKeyChange, dshCandidates, dshKey, loadAll, saveAll, syncAll } from '../ops.js';
|
|
36
36
|
import { defaultPick, pickedFor } from '../dsh/pick.js';
|
|
@@ -301,6 +301,9 @@ export class Daemon {
|
|
|
301
301
|
return { enabled: false, riskNotice: upstreamRiskNotice(getUpstream(def.id)) };
|
|
302
302
|
return { enabled: false, login: await this.startLogin(def) };
|
|
303
303
|
}
|
|
304
|
+
// WorkBuddy 会拦 dsh 的对话:从关着打开时先提醒一次
|
|
305
|
+
if (def.kind === 'workbuddy' && !state.enabled && !opts.acceptRisk)
|
|
306
|
+
return { enabled: false, riskNotice: workbuddyBlockedNotice() };
|
|
304
307
|
if (def.kind === 'engine') {
|
|
305
308
|
const m = await this.mgmt();
|
|
306
309
|
for (const c of credsFor(await m.credentials(), def))
|
|
@@ -37,7 +37,31 @@ async function waitCatalogs(ctx, since, expected, timeoutMs = 45_000) {
|
|
|
37
37
|
return cats;
|
|
38
38
|
}
|
|
39
39
|
/** 检测 App → 首次打印说明 → bridge 配置与服务 → 等目录。返回目录(调用方随后 syncAll) */
|
|
40
|
+
export const WORKBUDDY_SOURCES = ['workbuddy', 'workbuddy-ai'];
|
|
41
|
+
/** WorkBuddy 会拦 dsh 发出的对话时的提醒(新装默认关、从页面打开前都显示) */
|
|
42
|
+
export function blockedNotice() {
|
|
43
|
+
return L('WorkBuddy 现在会认出 dsh 发出的对话并拦下(「请求被拦截」),在 dsh 里基本用不了;「测一下」能通也不代表能用。dsh-model 不会去绕开这个限制,继续调用有封号风险。签到不受影响。仍要把它的模型接进 dsh 吗?', 'WorkBuddy now recognises conversations sent by dsh and blocks them ("请求被拦截"), so it barely works in dsh; a passing Test does not mean it works. dsh-model will not try to get around this, and continuing risks the account. Check-in is not affected. Connect its models to dsh anyway?');
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* 只签到(设计 §14.18):第一次装时把 WorkBuddy 的模型关掉,签到照常(签到不看来源开关)。
|
|
47
|
+
* 以前装过的(已有 riskNoticeAt)保持原样;只做一次,之后用户自己打开的不改回去。
|
|
48
|
+
*/
|
|
49
|
+
export function applyWorkbuddyDefault(all) {
|
|
50
|
+
if (all.config.workbuddyDefault)
|
|
51
|
+
return;
|
|
52
|
+
if (all.config.bridge?.riskNoticeAt) {
|
|
53
|
+
all.config.workbuddyDefault = 'kept';
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
const off = new Set(all.config.disabledSources ?? []);
|
|
57
|
+
for (const id of WORKBUDDY_SOURCES)
|
|
58
|
+
off.add(id);
|
|
59
|
+
all.config.disabledSources = [...off];
|
|
60
|
+
all.config.workbuddyDefault = 'checkin-only';
|
|
61
|
+
info(L('WorkBuddy 只用来签到:它现在会拦 dsh 的对话,所以模型默认不接进 dsh(要用可在「来源」里打开)', 'WorkBuddy is used for check-in only: it now blocks conversations from dsh, so its models are not connected by default (turn it on under Sources if you want)'));
|
|
62
|
+
}
|
|
40
63
|
export async function enableWorkbuddy(ctx, all) {
|
|
64
|
+
applyWorkbuddyDefault(all);
|
|
41
65
|
const variants = await availableVariants(ctx.paths.home);
|
|
42
66
|
if (!variants.length) {
|
|
43
67
|
skip(ctx.platform === 'darwin'
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-model",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.23.0",
|
|
4
4
|
"description": "One OpenAI-compatible address for all your models: subscriptions (Codex, Kimi…), free tiers (WorkBuddy, Gemini, Groq, OpenRouter), local models (Ollama, LM Studio), daily check-in credits, auto failover. Works with DeepSeek Harness (dsh) and any OpenAI-compatible tool. 一个地址用上你所有的模型。",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"dsh-plugin",
|
package/plugin/client.js
CHANGED
|
@@ -1089,9 +1089,15 @@ window.__ModuleLoader__.load({
|
|
|
1089
1089
|
/** 刚装好、接入的来源少于两个时显示:先接一个免费来源 → 模型已经挑好 → 去 dsh 里用 */
|
|
1090
1090
|
function Guide({ sources, onConnect }) {
|
|
1091
1091
|
const [done, setDone] = useState(readGuideDone)
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1092
|
+
// WorkBuddy 只签到(它拦 dsh 的对话),不算"已接入"
|
|
1093
|
+
const connected = sources.filter((s) => s.loggedIn && s.enabled && s.kind !== 'workbuddy')
|
|
1094
|
+
if (done || connected.length >= 1) return null
|
|
1095
|
+
// 只推两家:国内直连的硅基流动、能上外网的 Gemini
|
|
1096
|
+
const PICKS = [
|
|
1097
|
+
{ id: 'siliconflow', why: () => L('国内直连,不用代理;实名认证后部分模型免费', 'Reachable from China without a proxy; some models are free after identity verification') },
|
|
1098
|
+
{ id: 'gemini', why: () => L('能上外网就选它:免费模型里质量最好,每天有次数上限', 'If you can reach Google: the best free models, with a daily limit') },
|
|
1099
|
+
]
|
|
1100
|
+
const picks = PICKS.map((p) => ({ ...p, s: sources.find((s) => s.id === p.id) })).filter((p) => p.s)
|
|
1095
1101
|
const hide = () => {
|
|
1096
1102
|
setDone(true)
|
|
1097
1103
|
try { window.localStorage.setItem(GUIDE_KEY, '1') } catch { /* 无痕模式:只在本次生效 */ }
|
|
@@ -1106,9 +1112,11 @@ window.__ModuleLoader__.load({
|
|
|
1106
1112
|
h('button', { type: 'button', style: S.btn, onClick: hide }, L('我知道了,不再显示', 'Got it, hide this'))),
|
|
1107
1113
|
step(1, L('接入一个免费来源', 'Connect a free source'),
|
|
1108
1114
|
h('div', null,
|
|
1109
|
-
h('div', { style: S.meta }, L('
|
|
1110
|
-
h('div', { style: {
|
|
1111
|
-
|
|
1115
|
+
h('div', { style: S.meta }, L('接一个就能用:去对方网站领一把免费 key,点下面的按钮粘贴进来。', 'One is enough: get a free key from their website, then click below and paste it.')),
|
|
1116
|
+
picks.map((p) => h('div', { key: p.id, style: { ...S.line, marginTop: 6, flexWrap: 'wrap' } },
|
|
1117
|
+
h('button', { type: 'button', style: S.btn, onClick: () => onConnect(p.s) }, p.s.label),
|
|
1118
|
+
h('span', { style: { ...S.meta, ...S.grow } }, p.why()),
|
|
1119
|
+
p.s.subscribeUrl ? h('a', { href: p.s.subscribeUrl, target: '_blank', rel: 'noopener noreferrer', style: { color: C.accent, fontSize: 12 } }, L('去领 key ↗', 'Get a key ↗')) : null)))),
|
|
1112
1120
|
step(2, L('模型已经替你挑好', 'Models are picked for you'),
|
|
1113
1121
|
h('div', { style: S.meta }, L('每个来源默认挑 5 个放进 dsh(免费的优先)。想换就在下面「来源」里点「选模型」。', 'Each source puts 5 suggested models into dsh (free ones first). Change them with "Models" under Sources below.'))),
|
|
1114
1122
|
step(3, L('去用', 'Use it'),
|