mingdao-harness 0.6.0 → 0.6.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.
@@ -9,6 +9,21 @@
9
9
 
10
10
  ---
11
11
 
12
+ ## 〇.0 最近一次发布复盘(v0.6.0,2026-09-12)
13
+
14
+ 本次发布**我自己犯了两个错**,都记在这里:
15
+
16
+ 1. **升版本时漏了 `desktop/package.json`** —— 被 smoke 里那条「桌面版版本必须与根一致」的
17
+ 静态护栏当场拦下(v0.4.6 审计加的版本漂移防护)。护栏起了作用,说明它值得保留。
18
+ 2. **更该记的是操作失误**:我把 `node test/smoke.js | tail -1` 串进了 `&&` 链——
19
+ **管道取的是 `tail` 的退出码**,于是 smoke 明明失败、链条照跑,我对着坏提交打了 tag,
20
+ 随后不得不删 tag 重打。教训:**判成败要看 `$?`,不要用管道尾部命令代替**;
21
+ 写成 `cmd > out 2>&1; echo $?` 或 `set -o pipefail`。
22
+
23
+ 另外新增了 `MingDao-Harness-Site/scripts/update-downloads.mjs`:下载区的机械字段(版本/链接/体积/
24
+ sha256/apt 命令/平台标签)自动更新,替代手工逐个替换;数据源取 GitHub Release 的 `digest`,
25
+ 保证 GitHub / 服务器 / 官网三方一致。
26
+
12
27
  ## 〇、铁律(三条,先记住)
13
28
 
14
29
  1. **未自检不发版**:全门禁必须绿(见 §1)。
@@ -148,6 +163,51 @@ mingdao audit # 原审计日志行为不变
148
163
 
149
164
  > 验收通过 = 明确回复确认。**确认之前不创建 tag、不推 tag、不建 Release。**
150
165
 
166
+ ### 3.0 本轮(v0.6.0)暴露的两个**流程缺口**——已补进清单
167
+
168
+ v0.6.0 上线后负责人发现两个问题,都是**流程漏了一步**,不是代码缺陷:
169
+
170
+ **① 桌面版无法在线更新** —— 我把自动更新的 feed 收割到了 `/downloads/`,
171
+ 但桌面版读的是官网 **`/updates/`**(`desktop/main.js` 里写明 `feed: 官网 /updates`)。
172
+ `/updates/*.yml` 还停在 0.4.5,于是应用永远认为「已是最新」。
173
+ 两者格式也不同:`/downloads/` 用 GitHub 直链(CI 生成),`/updates/` 用**官网直链**(发布流程生成)。
174
+
175
+ ```bash
176
+ # 已在官网仓库提供生成器,**别再手写**(手写过两次,两次都错):
177
+ ssh mingdao-server 'bash /tmp/gen-update-feeds.sh <版本> <downloads 目录> <updates 目录> https://harness.mingdao.ai/downloads'
178
+ # 源文件:MingDao-Harness-Site/scripts/gen-update-feeds.sh(scp 到服务器再跑)
179
+ #
180
+ # /updates/latest.yml → Windows NSIS exe
181
+ # /updates/latest-linux.yml → Linux AppImage(deb 不支持 electron-updater)
182
+ # /updates/latest-mac.yml → macOS **两个 zip**(arm64 + x64 合并成一份)
183
+ ```
184
+
185
+ > ⚠ **macOS 必须是 zip,不能是 dmg**。v0.6.0 上线后用户报
186
+ > 「`ZIP file not provided`」,根因就是 mac feed 指向了 dmg——electron-updater 在 macOS 上
187
+ > 要下载 **zip** 解包替换 .app,dmg 只能手动安装。**0.4.5 的 feed 也是 dmg,所以这个错误一直存在**
188
+ > (该步由「服务器侧发布流程」生成,长期没人写对)。相应地在**收割步骤**里也要把两个
189
+ > `MingDao.Harness-<版本>-{arm64-,}mac.zip` 一并放进 downloads,否则 feed 指向的文件不存在。
190
+
191
+ ```bash
192
+ # 验收(三个平台各自取 feed → 下载 → 核对字节 sha512 与 size):
193
+ for f in latest.yml latest-linux.yml latest-mac.yml; do
194
+ curl -s https://harness.mingdao.ai/updates/$f | head -1 # 必须是新版本
195
+ done
196
+ # 注意:从 HTTP 头取 content-length 要用 `tr -d '\r'` 去掉 CR,否则 "$size" = "$act" 会假失败(我踩过)
197
+ ```
198
+ **验收**:三份 `/updates/*.yml` 的 `version` 都是新版本,且 `curl https://harness.mingdao.ai/updates/latest.yml | head -1` 即为新版本。
199
+
200
+ **② 代码只推了 tag、没推分支** —— gitee/gitcode 的 `main` 仍停在旧提交(只有 tag 是新的),
201
+ 用户在镜像上看到的是过期代码;同时 Gitee 的「最新版」标注也因此对不上。
202
+
203
+ ```bash
204
+ # 发布时必须把分支与 tag 一起推(tag 触发 CI 构建,分支供人阅读/克隆)
205
+ git push <mirror> main:main
206
+ git push <mirror> refs/tags/v<版本>:refs/tags/v<版本>
207
+ ```
208
+ **验收**:三平台的 `git ls-remote <url> refs/heads/main` 与本地 `main` 同 SHA;
209
+ `git ls-remote <url> 'refs/tags/v<版本>^{}'` 解析到的提交也一致。
210
+
151
211
  ## 三、发布(拿到确认后)
152
212
 
153
213
  ### 3.1 打 tag 并推送(触发桌面版构建与 GitHub Release)
@@ -157,4 +157,23 @@
157
157
  | 3 v0.5.1 | ❌ **已取消**(负责人 2026-09-11:不发 v0.5.1,确认后直接发完整 v0.6.0) |
158
158
  | 4 v0.6.0(C1–C4) | 🚧 C1–C4 均已开发完成;待负责人验收后发布 |
159
159
 
160
- > **当前唯一阻塞**:官网部署所需的**服务器私钥 + 主机地址**。除官网外,v0.4.6 与 v0.5.0 的四平台发布已全部完成。
160
+ > **状态(2026-09-12 更新):全部完成。** 负责人提供了私钥与主机(`8.210.103.117`),
161
+ > 并决定「官网直接发布 v0.6.0」(不再补发 v0.4.6 / v0.5.0 的官网内容)。v0.6.0 已四平台 + 官网发布完成。
162
+
163
+ ## 六、v0.6.0 发布结果(2026-09-12)
164
+
165
+ | 渠道 | 结果 |
166
+ | --- | --- |
167
+ | GitHub Release | ✅ tag `v0.6.0`;9 个附件(5 安装包 + 2 mac zip + 2 自动更新 feed);正文已按新政策(附件长期保留) |
168
+ | Gitee / GitCode Release | ✅ 均已创建;正文 6112 字符含官网直链;**无安装包附件**(符合政策) |
169
+ | npm | ✅ 已发布,`latest` = 0.6.0;实测 `--version` / `ledger list` / `net policy` 可用 |
170
+ | 官网 | ✅ 线上即 v0.6.0;5 个下载链接全部 200、体积正确、sha256 与 GitHub 产物**逐项一致**;无 0.4.5 残留 |
171
+ | 桌面自动更新 | ✅ `latest.yml` / `latest-linux.yml` 指向 v0.6.0 |
172
+ | 三平台 tag | ✅ 同一 tag 对象 `3b1cc0f1aa` → 同一提交 `a9167d1` |
173
+ | CI | ✅ 10 绿 + 1 有意跳过(`normalize-release-notes` 仅在手动触发时运行) |
174
+
175
+ **三方一致性交叉核对**(GitHub digest / 服务器实际文件 / 官网页面):5/5 安装包完全一致。
176
+ 收割在服务器侧直接拉 GitHub(~10MB/s),无需绕经本机。
177
+
178
+ **遗留提醒**:服务器 `downloads/` 已 15G(9 个历史版本 × 约 630M),磁盘 80%。
179
+ 清理旧版本是负责人的决定,**未擅自删除**。
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mingdao-harness",
3
- "version": "0.6.0",
3
+ "version": "0.6.1",
4
4
  "description": "MingDao Harness —— 开源智能体框架(Agent Harness)。零依赖、开箱即用,针对 DeepSeek-V4 系列优化,开放主流模型接入。",
5
5
  "type": "module",
6
6
  "bin": {
@@ -109,6 +109,30 @@ export async function fetchProviderModels(/** @type {any} */ cfg, /** @type {any
109
109
  }
110
110
  }
111
111
 
112
+ /**
113
+ * 该模型名是否出现在**已动态拉取的**服务商名单里(v0.6.0 修复)。
114
+ *
115
+ * 为什么需要它:设置界面用 `availableModels()`(动态优先,未在静态表里的标「(线上最新)」),
116
+ * 而切换模型的 `/api/config` 只认静态 `MODELS` 表——于是出现「应用自己列出来的模型却选不了」:
117
+ * DeepSeek 官方把 `deepseek-v4-flash` 改名为 `deepseek-flash` 后,界面能拉到、一点就报
118
+ * 「未知模型」并弹回旧模型。
119
+ *
120
+ * 判据用「服务商自己返回的名单」:它仍是有界集合(`/models` 结果、已按 isChatModel 过滤、
121
+ * 上限 200),因此既不放过任意字符串(v0.4.7 加这条校验就是为了拦 `{model:12345}`),
122
+ * 又能让**厂家改名/上新**这一类正常演进立刻可用,不必等内核发版。
123
+ * @param {any} name
124
+ */
125
+ export function isDiscoveredModel(/** @type {any} */ name) {
126
+ const target = String(name || '').trim();
127
+ if (!target) return false;
128
+ const cache = loadCache();
129
+ for (const entry of Object.values(cache || {})) {
130
+ const models = /** @type {any} */ (entry)?.models;
131
+ if (Array.isArray(models) && models.includes(target)) return true;
132
+ }
133
+ return false;
134
+ }
135
+
112
136
  // 合并可用模型列表:只含已设置 Key 的服务商;动态名单优先、预设回退;自定义模型恒在。
113
137
  export async function availableModels(/** @type {any} */ cfg, /** @type {any} */ currentModel) {
114
138
  const out = [];
package/src/models.js CHANGED
@@ -8,7 +8,8 @@ export const PROVIDERS = {
8
8
  kind: 'openai-compatible',
9
9
  baseUrl: 'https://api.deepseek.com/v1',
10
10
  envKey: 'DEEPSEEK_API_KEY',
11
- models: ['deepseek-v4-flash', 'deepseek-v4-pro', 'deepseek-v4-flash-vision-exp'],
11
+ // 与官方 `GET /v1/models` 的实际返回保持一致(拉取失败时的回退名单也应是真实可用的)
12
+ models: ['deepseek-flash', 'deepseek-v4-pro'],
12
13
  note: 'DeepSeek-V4 正式版(2026-08-17 起商用):1M 上下文(单次最大输出 384K),峰谷定价(高峰=北京工作日 9:00–12:00、14:00–18:00,闲时价=高峰一半),支持工具调用 / Responses API / Anthropic 兼容接口。',
13
14
  },
14
15
  openai: {
@@ -97,9 +98,30 @@ export const MODELS = {
97
98
  peak: { input: 9, output: 27, cacheHit: 0.3 },
98
99
  },
99
100
  },
100
- 'deepseek-v4-flash': {
101
+ // v0.6.0:DeepSeek 官方把 `deepseek-v4-flash` **改名**为 `deepseek-flash`
102
+ // (依据:`GET /v1/models` 现只返回 `deepseek-flash` 与 `deepseek-v4-pro` 两个,
103
+ // 而本项目静态表里还是旧名 → 设置界面能拉到、聊天界面选中却被判「未知模型」)。
104
+ // 上限/价格沿用 v4-flash 的数值(同一个模型换了名字);若官方后续公布不同规格,
105
+ // 只需改这一处——**价格必须在这里显式给出**,否则费用会变成「无法估算」而不是真实计费。
106
+ 'deepseek-flash': {
101
107
  label: '极速响应 · 日常问答与轻量任务',
102
108
  provider: 'deepseek',
109
+ contextWindow: 1000000,
110
+ maxOutputCeiling: 384000,
111
+ budgetTokens: 128000,
112
+ maxOutputTokens: 8192,
113
+ temperature: 0.6,
114
+ supportsReasoning: false,
115
+ pricing: {
116
+ offpeak: { input: 1.5, output: 4.5, cacheHit: 0.05 },
117
+ peak: { input: 3, output: 9, cacheHit: 0.1 },
118
+ },
119
+ },
120
+ // 旧名保留:**老配置与历史会话里写的是这个字符串**,删掉会让它们直接 404。
121
+ // 但官方已不再在 /v1/models 里返回它,故标签里标明原因,避免用户以为是内核丢了三方模型。
122
+ 'deepseek-v4-flash': {
123
+ label: '极速响应 · 日常问答与轻量任务(旧名;官方已改名为 deepseek-flash,建议切换)',
124
+ provider: 'deepseek',
103
125
  contextWindow: 1000000, // 官方上下文 1M
104
126
  maxOutputCeiling: 384000,
105
127
  budgetTokens: 128000,
@@ -4,7 +4,7 @@ import fs from 'node:fs';
4
4
  import path from 'node:path';
5
5
  import { loadConfig, saveConfig } from '../../../config.js';
6
6
  import { setStoredKey, removeStoredKey, getStoredKey, maskKey } from '../../../credentials.js';
7
- import { availableModels, fetchProviderModels, providerHasKey } from '../../../model-discovery.js';
7
+ import { availableModels, fetchProviderModels, providerHasKey, isDiscoveredModel } from '../../../model-discovery.js';
8
8
  import { createProvider, resolveProviderConfig } from '../../../providers/index.js';
9
9
  import { MODELS, PROVIDERS, modelPreset } from '../../../models.js';
10
10
  import { detectSandbox } from '../../../tools/bash.js';
@@ -80,7 +80,12 @@ export async function handle({ req, res, method, p, url }, deps, shared) {
80
80
  const known =
81
81
  Object.prototype.hasOwnProperty.call(cfg.customModels || {}, target) ||
82
82
  Object.prototype.hasOwnProperty.call(MODELS, target) ||
83
- Boolean(modelPreset(target));
83
+ Boolean(modelPreset(target)) ||
84
+ // v0.6.0 修复:设置界面显示的是**动态拉取**的名单,切换时却只认静态表,
85
+ // 于是「应用自己列出来的模型选不了」。DeepSeek 官方把 deepseek-v4-flash
86
+ // 改名成 deepseek-flash 后,用户一点就报「未知模型」并弹回旧模型——
87
+ // 厂家改名/上新不该等内核发版才能用。判据仍是有界集合(服务商 /models 的返回)。
88
+ isDiscoveredModel(target);
84
89
  if (!known) {
85
90
  return json(res, 400, { error: `未知模型 "${target}"——请先在 ⚙ 设置里选择内置模型,或添加自定义模型后再切换。` });
86
91
  }