dsh-model 0.21.3 → 0.21.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/dist/commands/key.js +7 -7
- package/dist/daemon/core.js +6 -5
- package/dist/ops.js +32 -0
- package/package.json +1 -1
package/dist/commands/key.js
CHANGED
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
import { requireRootInVps } from '../context.js';
|
|
3
3
|
import { DshModelError } from '../errors.js';
|
|
4
4
|
import { L } from '../i18n.js';
|
|
5
|
-
import { DSH_KEY_NAME, addKey, revokeKey, rotateKey, saveKeys } from '../keys.js';
|
|
6
|
-
import {
|
|
5
|
+
import { DSH_KEY_NAME, addKey, findActive, revokeKey, rotateKey, saveKeys } from '../keys.js';
|
|
6
|
+
import { applyKeyChange, dshKey, loadAll, syncModels } from '../ops.js';
|
|
7
7
|
import { withLock } from '../state.js';
|
|
8
8
|
import { baseUrl } from '../engine/client.js';
|
|
9
9
|
import { bold, info, isJsonMode, ok, printJson, table, warn } from '../util/output.js';
|
|
@@ -20,7 +20,7 @@ export async function key(ctx, sub, name) {
|
|
|
20
20
|
if (sub === 'add') {
|
|
21
21
|
const entry = addKey(all.keys, name);
|
|
22
22
|
await saveKeys(ctx, all.keys);
|
|
23
|
-
await
|
|
23
|
+
await applyKeyChange(ctx, all, { accepts: entry.key });
|
|
24
24
|
if (isJsonMode())
|
|
25
25
|
printJson({ ok: true, name, key: entry.key, baseURL: `${baseUrl(all.config.port)}/v1` });
|
|
26
26
|
ok(L(`已新增 key:${name}(只显示这一次,请妥善保存)`, `Key added: ${name} (shown only once; keep it safe)`));
|
|
@@ -32,19 +32,19 @@ export async function key(ctx, sub, name) {
|
|
|
32
32
|
if (name === DSH_KEY_NAME) {
|
|
33
33
|
throw new DshModelError('cannot_revoke_dsh', L('dsh 用的 key 不能吊销,只能轮换', 'The dsh key cannot be revoked, only rotated'), L('dsh-model key rotate dsh', 'dsh-model key rotate dsh'));
|
|
34
34
|
}
|
|
35
|
+
const old = findActive(all.keys, name)?.key;
|
|
35
36
|
revokeKey(all.keys, name);
|
|
36
37
|
await saveKeys(ctx, all.keys);
|
|
37
|
-
await
|
|
38
|
+
await applyKeyChange(ctx, all, { rejects: old });
|
|
38
39
|
ok(L(`已吊销 key:${name}`, `Key revoked: ${name}`));
|
|
39
40
|
return 0;
|
|
40
41
|
}
|
|
41
42
|
if (sub === 'rotate') {
|
|
42
43
|
const entry = rotateKey(all.keys, name);
|
|
43
44
|
await saveKeys(ctx, all.keys);
|
|
44
|
-
|
|
45
|
+
// 等新 key 在引擎里生效后再改 dsh
|
|
46
|
+
await applyKeyChange(ctx, all, { accepts: entry.key });
|
|
45
47
|
if (name === DSH_KEY_NAME) {
|
|
46
|
-
// 引擎热重载约 1s:等新 key 生效后再改 dsh
|
|
47
|
-
await new Promise((r) => setTimeout(r, 1500));
|
|
48
48
|
await syncModels(ctx, all, { quiet: true });
|
|
49
49
|
ok(L('已轮换 dsh 用的 key,并同步到 dsh', 'Rotated the dsh key and updated dsh'));
|
|
50
50
|
}
|
package/dist/daemon/core.js
CHANGED
|
@@ -31,8 +31,8 @@ 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
33
|
import { riskNotice as workbuddyRiskNotice } from '../integrations/workbuddy.js';
|
|
34
|
-
import { DSH_KEY_NAME, addKey, loadKeys, revokeKey, rotateKey, saveKeys } from '../keys.js';
|
|
35
|
-
import {
|
|
34
|
+
import { DSH_KEY_NAME, addKey, findActive, loadKeys, revokeKey, rotateKey, saveKeys } from '../keys.js';
|
|
35
|
+
import { applyKeyChange, dshCandidates, dshKey, loadAll, saveAll, syncAll } from '../ops.js';
|
|
36
36
|
import { defaultPick, pickedFor } from '../dsh/pick.js';
|
|
37
37
|
import { loadSecrets, saveSecrets } from '../secrets.js';
|
|
38
38
|
import { SOURCES, credsFor, findSource, paymentRequired } from '../sources.js';
|
|
@@ -929,7 +929,7 @@ export class Daemon {
|
|
|
929
929
|
const all = await loadAll(this.ctx);
|
|
930
930
|
const entry = addKey(all.keys, name);
|
|
931
931
|
await saveKeys(this.ctx, all.keys);
|
|
932
|
-
await
|
|
932
|
+
await applyKeyChange(this.ctx, all, { accepts: entry.key });
|
|
933
933
|
await this.reloadKeyNames();
|
|
934
934
|
return { name: entry.name, key: entry.key };
|
|
935
935
|
});
|
|
@@ -939,9 +939,10 @@ export class Daemon {
|
|
|
939
939
|
throw new DshModelError('cannot_revoke_dsh', L('dsh 用的 key 不能吊销,只能轮换', 'The dsh key cannot be revoked, only rotated'));
|
|
940
940
|
await withLock(this.ctx, async () => {
|
|
941
941
|
const all = await loadAll(this.ctx);
|
|
942
|
+
const old = findActive(all.keys, name)?.key;
|
|
942
943
|
revokeKey(all.keys, name);
|
|
943
944
|
await saveKeys(this.ctx, all.keys);
|
|
944
|
-
await
|
|
945
|
+
await applyKeyChange(this.ctx, all, { rejects: old });
|
|
945
946
|
});
|
|
946
947
|
await this.reloadKeyNames();
|
|
947
948
|
}
|
|
@@ -950,7 +951,7 @@ export class Daemon {
|
|
|
950
951
|
const all = await loadAll(this.ctx);
|
|
951
952
|
const e = rotateKey(all.keys, name);
|
|
952
953
|
await saveKeys(this.ctx, all.keys);
|
|
953
|
-
await
|
|
954
|
+
await applyKeyChange(this.ctx, all, { accepts: e.key });
|
|
954
955
|
return e;
|
|
955
956
|
});
|
|
956
957
|
await this.reloadKeyNames();
|
package/dist/ops.js
CHANGED
|
@@ -12,6 +12,7 @@ import { DSH_KEY_NAME, findActive, loadKeys } from './keys.js';
|
|
|
12
12
|
import { serviceFor } from './service/index.js';
|
|
13
13
|
import { loadConfig, loadState, saveConfig, saveState } from './state.js';
|
|
14
14
|
import { ok, skip, warn } from './util/output.js';
|
|
15
|
+
import { readText, writeInPlace } from './util/fs.js';
|
|
15
16
|
export async function loadAll(ctx) {
|
|
16
17
|
const [config, state, keys] = await Promise.all([loadConfig(ctx), loadState(ctx), loadKeys(ctx)]);
|
|
17
18
|
return { config, state, keys };
|
|
@@ -34,6 +35,37 @@ export function dshKey(keys) {
|
|
|
34
35
|
export async function applyEngineConfig(ctx, all) {
|
|
35
36
|
return writeEngineConfig(ctx, all.config, all.keys);
|
|
36
37
|
}
|
|
38
|
+
/**
|
|
39
|
+
* 改 key 后确认引擎真的换上了:热重载偶尔会漏掉一次变化(CI 上 macOS、Windows 都见过等 15 秒仍没生效)。
|
|
40
|
+
* 没生效就原样再写一次配置文件,还不行就重启引擎。引擎没在运行时只写文件。
|
|
41
|
+
*/
|
|
42
|
+
export async function applyKeyChange(ctx, all, probe) {
|
|
43
|
+
await writeEngineConfig(ctx, all.config, all.keys);
|
|
44
|
+
const port = all.config.port;
|
|
45
|
+
if (!(await waitHealthy(port, 1000)))
|
|
46
|
+
return;
|
|
47
|
+
const works = (key) => listModels(port, key).then(() => true, () => false);
|
|
48
|
+
const effective = async () => (!probe.accepts || (await works(probe.accepts))) && (!probe.rejects || !(await works(probe.rejects)));
|
|
49
|
+
const wait = async (ms) => {
|
|
50
|
+
const deadline = Date.now() + ms;
|
|
51
|
+
while (Date.now() < deadline) {
|
|
52
|
+
if (await effective())
|
|
53
|
+
return true;
|
|
54
|
+
await new Promise((r) => setTimeout(r, 250));
|
|
55
|
+
}
|
|
56
|
+
return effective();
|
|
57
|
+
};
|
|
58
|
+
if (await wait(5000))
|
|
59
|
+
return;
|
|
60
|
+
const text = await readText(ctx.paths.engineYaml);
|
|
61
|
+
if (text !== null)
|
|
62
|
+
await writeInPlace(ctx.paths.engineYaml, text, { owner: ctx.owner });
|
|
63
|
+
if (await wait(5000))
|
|
64
|
+
return;
|
|
65
|
+
if ((await restartEngineBestEffort(ctx, all)) && (await wait(5000)))
|
|
66
|
+
return;
|
|
67
|
+
warn(L('引擎还没换上这次的 key 改动;稍后执行 dsh-model repair', 'The engine has not picked up this key change yet; run dsh-model repair later'));
|
|
68
|
+
}
|
|
37
69
|
/**
|
|
38
70
|
* 兜底重启:热重载没生效时用。vps 模式下 bridge 以 dsh 用户运行,没有权限重启系统服务——
|
|
39
71
|
* 这时只记警告不失败(实测:bridge 写完配置后 systemctl restart 被拒)。
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-model",
|
|
3
|
-
"version": "0.21.
|
|
3
|
+
"version": "0.21.4",
|
|
4
4
|
"description": "Many model sources behind one authenticated OpenAI-compatible endpoint for DeepSeek Harness (dsh) and your tools: WorkBuddy, OpenCode Zen and your CLI subscriptions (Codex, Kimi…), with a management page inside dsh. 多家模型汇成一个 OpenAI 兼容地址,dsh 和其他软件都能直接用。",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"dsh-plugin",
|