dsh-deepmoyu 2.7.0 → 2.7.2
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/CHANGELOG.md +25 -0
- package/SECURITY.md +4 -0
- package/client.js +89 -13
- package/docs/RELEASE_AUDIT.md +2 -2
- package/docs/RELEASING.md +15 -0
- package/docs/tool-evidence.json +1 -1
- package/index.js +296 -62
- package/lib/asr.py +22 -0
- package/lib/favops.py +38 -6
- package/lib/host-compat.mjs +123 -0
- package/lib/learn-review.mjs +11 -1
- package/lib/login.mjs +20 -3
- package/lib/mirror.mjs +15 -1
- package/lib/model-relay.mjs +6 -2
- package/lib/persona.mjs +3 -1
- package/lib/pet-session.mjs +9 -3
- package/lib/pv-session.mjs +16 -1
- package/lib/pv.mjs +18 -3
- package/lib/pv.py +142 -18
- package/lib/pv_cover.py +11 -1
- package/lib/pv_editorial.py +23 -2
- package/lib/pv_plan.py +20 -2
- package/lib/pv_verify.py +14 -0
- package/lib/state.mjs +6 -2
- package/lib/sticker-automation.mjs +16 -2
- package/lib/sticker.mjs +4 -1
- package/lib/uvfetch.mjs +38 -12
- package/package.json +1 -1
- package/presets/deepmoyu.patch.yml +6 -1
- package/vendor/bilibili-mcp/mcp_server.py +116 -32
- package/vendor/bilibili-mcp/runtime_guard.py +32 -6
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,30 @@
|
|
|
1
1
|
# 更新记录
|
|
2
2
|
|
|
3
|
+
## 2.7.2
|
|
4
|
+
|
|
5
|
+
本节对应当前源码,不能据此推断 npm 或插件市场已更新。
|
|
6
|
+
|
|
7
|
+
- **一轮 76 项全量审计后的集中修复**(P0×1、P1×5、P2×17、P3×48,69 项已修,其余为有意保留的设计边界/环境项),测试矩阵全绿:Node 429 / Python 90 / UI 1013 / browser 7 套 / isolation 103 / mode-exit 54 / release:check。
|
|
8
|
+
- 🔒 **P0 安全修复:动态配图白名单**。以前 `bili_moment` 把模型给的 `images` 原样透传给 MCP——本地路径会被 Python `Picture.from_file` 读**任意本地文件**公开发布,http 项被服务端无校验抓取(本机文件外泄通道)。现在只认①已审核表情库的图名(发送前快照出字节)②发送审核产出的哈希文件(文件名即 SHA-256,逐字节复验),其余一律拒绝。
|
|
9
|
+
- 🧾 **账本与统计补漏**(本轮重点复查过一遍):①说说配额的一切失败路径都退预占名额——含普通异常(MCP 未就绪等)和图片校验拒绝;「超 9 张」改为占名额**之前**校验,不再白烧当天份额(新增 2 条回归测试锁死);②五只发送工具成功后的簿记失败降级为回执里的 `bookkeepingWarning`,不再吞掉发送成功回执;③节奏(pace)改锁内读-算-写,并发撞码不再互相覆盖冷却;④被拒发送计入连发节奏(burst),配合守卫 `reject_send` 保留节奏事件;⑤`opus-new` 补记 JS 判重账本与口味本子,两本账不再漂移;⑥跨天归零只走 `lib/state.mjs` 那一份名单(此前手写清单漏过 `moments`,说说被永久静默)。
|
|
10
|
+
- 🔁 **unknown 不再是死局**:守卫新增 `release_unknown`,`bili_guard_status` 带 `release` 参数作人工解除入口;unknown 回执带 `slot_key` 并指引自助解除——「手工重试永远被拦」有了出路。pending/sent 依旧不可删。
|
|
11
|
+
- ✉️ **私信软上限对一切发送生效**:防刷屏闸不再看 `msg_seqno` 是否填写(以前不填 seqno 就绕过软上限);判重与收件箱复核语义不变。
|
|
12
|
+
- 🛡️ **稳定性**:表情判图加 21 秒独立墙钟,判图挂死不再卡住收集队列(fail-open 照老规矩收);视频会话 `stop` 不再覆盖已交付终态;插件卸载清单补齐 pv 渲染子进程、接力定时器、pvTick 的收尾;时段任务跨实例认领(owner 心跳 10 分钟过期接管);投稿上传加 900 秒上限并写明「直跑无账本闸」。
|
|
13
|
+
- 🎬 **视频流水线(Python)**:GSV 语速双向守卫(0.15–0.32 秒/有效字,防截断也防复读);GSV 静音 RMS 检查与 `torch/soundfile` 启动预检;edge-tts 加 120 秒超时兜底;空文本镜头渲染前拒绝;省略号/闭引号断句修复;字幕时长按字符权重分摊;ffmpeg 缺失给安装指引;收藏/验证边车钉 UTF-8;非数值 ID 原样保留;五个发送工具的守卫调用全部 fail-closed。
|
|
14
|
+
- 🖥️ **工作台体验**:设置保存改「单飞」——快速连点/乱序回退消失,结束后补发最新值;活动页 404 停轮询并给提示条;所有前端请求 15 秒超时;陪伴节奏改 diff 提交;学习页不再多余预取 `/update`。
|
|
15
|
+
- 🧹 **隔离与运维**:人工停止的审核批不再被自动重拉(batchKey 不变就跳过);登录二维码失败即删 + 24 小时旧文件清扫;升级前重判安装方式;uv 下载改手动跟跳且跳转目标限定官方域名清单;镜像日志剔除待审私信正文;权限读回缺失时标 `unverified`。
|
|
16
|
+
- CI 纳管 `test:isolation`;persona 隔离断言改稳定子串(不再钉整句文案)。
|
|
17
|
+
|
|
18
|
+
## 2.7.1
|
|
19
|
+
|
|
20
|
+
本节对应当前源码,不能据此推断 npm 或插件市场已更新。
|
|
21
|
+
|
|
22
|
+
- 修 `2026-11-10` 真机事故:DSH 升到 **0.12.1** 之后选「鲸鱼摸鱼模式」整个起不来,本轮直接 `unknown prompt variable "{{cwd}}" in section "deployment:persona-suffix"; registered variables: provider, model`。根因是 `presets/deepmoyu.patch.yml` 里 persona 的 `suffix` 写着 `Your working directory is {{cwd}}.`:`dsh-persona` 把它原样注册成 `deployment:persona-suffix` 段,而 `dsh-system-prompt` 的 `interpolate()` 遇到**没注册过的变量会直接 throw**,整轮提示词组装跟着失败。DSH 0.12.1 全局只注册 `provider` / `model` 两个变量(`dsh-agent-loop` 注册),`{{cwd}}` 是旧版本的遗物。现在 `suffix` 留空 —— 工作目录本来由 `dsh-working-directory` 以 runtime context(`working-directory:current`)注入,段里不用再说一遍。
|
|
23
|
+
- 插件自己注册的 `bili:dafeiyu` 提示词段加上 **`interpolate: false`**:这一段全是插件自己算出来的(人格 / soul / 陪伴档位 / 实时配额),本来就不该参与变量替换;关掉之后,以后哪怕文案里写出 `{{`,也炸不到整轮对话。同理 `text` 回调外面已经有一层 `try/catch` 兜成空串。
|
|
24
|
+
- 新增 `tests/prompt-variables.test.mjs`:扫 `cordis.patch.yml` 与 `presets/deepmoyu.patch.yml` 里所有 `{{...}}`,只允许宿主真正注册的 `provider` / `model`(白名单是 `HOST_VARIABLES`,DSH 再升版时改这一处);另外断言 persona 的 `suffix` 为空、插件注册的每个 `sp.section({` 都带 `interpolate: false`。以后再写回未知变量,`npm test` 就会红。
|
|
25
|
+
- 新增 `lib/host-compat.mjs`:插件启动时就地扫一遍自己那两个 patch 文件,发现宿主不认识的 `{{变量}}` 立刻往 `duty.log` 写一条能照着改的告警(注释行不算,免得自己告警自己)。**只写日志、不抛错、不阻断加载** —— 自检本身不能变成新的风险。以前是靠使用者选模式、看到一句英文堆栈才知道坏了。
|
|
26
|
+
- 顺带修了同一台机器上「profile 里那份 `preset-dafeiyu` 一直盖着插件这份」的老问题(不是代码,是本地 profile 的手写副本,已在备份后删除;仓库侧不受影响)。
|
|
27
|
+
|
|
3
28
|
## 2.7.0
|
|
4
29
|
|
|
5
30
|
本节对应当前源码,不能据此推断 npm 或插件市场已更新。
|
package/SECURITY.md
CHANGED
|
@@ -19,3 +19,7 @@
|
|
|
19
19
|
## 数据流与测试范围
|
|
20
20
|
|
|
21
21
|
详见 [数据与隐私](docs/PRIVACY.md) 和 [发布审查记录](docs/RELEASE_AUDIT.md)。本地隔离测试不会向真实 B站 账号发送内容。第三方平台、模型和语音服务的可用性、处理方式及限制由各服务决定。
|
|
22
|
+
|
|
23
|
+
## 关于「假密钥」字符串的说明
|
|
24
|
+
|
|
25
|
+
`prompts/duty.md` 与 `lib/mode.mjs` 中出现的 `sk-KfcCrazyDay4VMe50Quickly` 是**有意设计的假密钥**——人设里的一个梗(角色被问到 key 时按设定回这句),不是任何真实凭证,不指向任何服务,也不应被视为泄漏。发布检查 `scripts/release/check-secrets.mjs` 有意放行 `sk-KfcCrazyDay` 前缀(该文件第 20 行)。第三方密钥扫描器若命中此字符串,属误报,可忽略。
|
package/client.js
CHANGED
|
@@ -69,12 +69,21 @@ window.__ModuleLoader__.load({
|
|
|
69
69
|
error.sessionId = detail?.sessionId
|
|
70
70
|
throw error
|
|
71
71
|
}
|
|
72
|
-
|
|
72
|
+
/* ⏱ P3-38:所有 API 请求带 15s 超时 —— 后端卡死时请求不能永远挂着(挂住的 PATCH
|
|
73
|
+
* 会占住单飞挡板、挂住的轮询会一直 inFlight)。AbortSignal.timeout 在旧宿主上
|
|
74
|
+
* 可能不存在,缺了就退回无超时,行为跟以前一样;超时抛的是 DOMException
|
|
75
|
+
* (name=TimeoutError,没有 .status),各调用方的 catch 原样兜住,释放逻辑不受影响。 */
|
|
76
|
+
const FETCH_TIMEOUT_MS = 15000
|
|
77
|
+
const fetchSignal = () => {
|
|
78
|
+
try { return AbortSignal.timeout(FETCH_TIMEOUT_MS) } catch { return undefined }
|
|
79
|
+
}
|
|
80
|
+
const jget = async (p) => asJson(await fetch(API + p, { credentials: 'same-origin', signal: fetchSignal() }))
|
|
73
81
|
const jpost = async (p, body) => asJson(await fetch(API + p, {
|
|
74
82
|
method: 'POST',
|
|
75
83
|
credentials: 'same-origin',
|
|
76
84
|
headers: { 'content-type': 'application/json' },
|
|
77
85
|
body: JSON.stringify(body || {}),
|
|
86
|
+
signal: fetchSignal(),
|
|
78
87
|
}))
|
|
79
88
|
|
|
80
89
|
const CSS = `
|
|
@@ -1108,6 +1117,41 @@ window.__ModuleLoader__.load({
|
|
|
1108
1117
|
return [st, externalSet.current, fails]
|
|
1109
1118
|
}
|
|
1110
1119
|
|
|
1120
|
+
/* 🔐 P2-15 单飞 PATCH 包装:并发 PATCH 响应乱序会把本地 config 回写成旧值(审计 P2-15)——
|
|
1121
|
+
* 例如「制作模型」select 连改两下、两个 PATCH 同时在飞,先发的那笔**后**回来,
|
|
1122
|
+
* patch 成功路径里的 setSt({config: r.config}) 会把界面回写成上一笔的旧值。
|
|
1123
|
+
* 这里保证同一时刻只有一个 /config 在飞:
|
|
1124
|
+
* · 在飞期间新到的改动只记「待发」(pending 按键合并、同键取最新值),界面控件
|
|
1125
|
+
* 照常更新本地显示、调用方拿到 true 不回弹;
|
|
1126
|
+
* · 当前请求结束后若有积压,再补发一次(补发永远带最新值;补发期间又来新值会
|
|
1127
|
+
* 走上面的排队分支,不会出现并发)。
|
|
1128
|
+
* 失败提示仍由 patch 内部的 say() 负责,单飞层不做二次弹错。 */
|
|
1129
|
+
function useLatestPatch(patch) {
|
|
1130
|
+
const patchRef = React.useRef(patch)
|
|
1131
|
+
patchRef.current = patch
|
|
1132
|
+
const inFlight = React.useRef(false)
|
|
1133
|
+
const pending = React.useRef(null) // { p, what }:在飞期间积压的改动(按键合并)
|
|
1134
|
+
const send = async (p, what) => {
|
|
1135
|
+
if (inFlight.current) {
|
|
1136
|
+
const old = pending.current
|
|
1137
|
+
pending.current = old
|
|
1138
|
+
? { p: { ...old.p, ...p }, what: [old.what, what].filter(Boolean).join('、') }
|
|
1139
|
+
: { p: { ...p }, what }
|
|
1140
|
+
return true
|
|
1141
|
+
}
|
|
1142
|
+
inFlight.current = true
|
|
1143
|
+
try {
|
|
1144
|
+
return await patchRef.current(p, what)
|
|
1145
|
+
} finally {
|
|
1146
|
+
inFlight.current = false
|
|
1147
|
+
const next = pending.current
|
|
1148
|
+
pending.current = null
|
|
1149
|
+
if (next) send(next.p, next.what).catch(() => {})
|
|
1150
|
+
}
|
|
1151
|
+
}
|
|
1152
|
+
return send
|
|
1153
|
+
}
|
|
1154
|
+
|
|
1111
1155
|
/* 🗑 旧的全页设置 Panel 已退役(2026-10-27):它的配置项早已搬进 PetSettingsModal,
|
|
1112
1156
|
活动流水 / 出手节奏读数 / 判重账本行也一并搬了进来。回归断言改由 render-modal.cjs 盯弹窗本体。 */
|
|
1113
1157
|
|
|
@@ -1567,12 +1611,17 @@ window.__ModuleLoader__.load({
|
|
|
1567
1611
|
})
|
|
1568
1612
|
}
|
|
1569
1613
|
|
|
1570
|
-
function PetCompanionSettings({ st, busy, patch }) {
|
|
1614
|
+
function PetCompanionSettings({ st, busy, patch, say }) {
|
|
1571
1615
|
const cfg = st?.config || {}
|
|
1572
1616
|
const initial = () => ({ chatterFrequency: cfg.chatterFrequency || 'normal', notificationLevel: cfg.notificationLevel || 'normal',
|
|
1573
1617
|
focusMode: cfg.focusMode === true, quietHoursEnabled: cfg.quietHoursEnabled === true,
|
|
1574
1618
|
quietHoursStart: cfg.quietHoursStart || '23:00', quietHoursEnd: cfg.quietHoursEnd || '08:00', petReducedMotion: cfg.petReducedMotion === true })
|
|
1575
1619
|
const [draft, setDraft] = React.useState(initial)
|
|
1620
|
+
/* 📌 P3-39:挂载时的初始快照。保存只提交与快照**不同**的键 —— 没动过的键不再
|
|
1621
|
+
* 整包重发(以前每次都把七个键原样打回 /config,纯噪音写入);全没动就提示
|
|
1622
|
+
* 「没有改动」。 */
|
|
1623
|
+
const [baseline] = React.useState(initial)
|
|
1624
|
+
const tell = (text, tone) => { if (typeof say === 'function') say(String(text || ''), tone) }
|
|
1576
1625
|
const update = (key, value) => setDraft(old => ({ ...old, [key]: value }))
|
|
1577
1626
|
const toggle = (key, title, note) => ToggleRow({ label: title, note, on: draft[key], disabled: Boolean(busy), onChange: value => update(key, value) })
|
|
1578
1627
|
return h('div', { 'data-bd-companion-settings': '' },
|
|
@@ -1591,7 +1640,12 @@ window.__ModuleLoader__.load({
|
|
|
1591
1640
|
h('span', { 'data-bd-note': '' }, draft.quietHoursStart === draft.quietHoursEnd ? '起止时间相同,安静时段不生效。' : '按本地时间执行,支持跨午夜。')) : null,
|
|
1592
1641
|
toggle('petReducedMotion', '减少桌宠动效', '停用浮动、泡泡和事件动画,并遵循系统的减少动态效果设置。'),
|
|
1593
1642
|
h('div', { 'data-bd-row': '' }, h('span', { 'data-bd-note': '', 'data-bd-grow': '' }, '当前:' + (st?.companion?.label || '自然陪伴')),
|
|
1594
|
-
h('button', { 'data-bd-btn': '', 'data-primary': '', disabled: Boolean(busy), onClick: () =>
|
|
1643
|
+
h('button', { 'data-bd-btn': '', 'data-primary': '', disabled: Boolean(busy), onClick: () => {
|
|
1644
|
+
/* P3-39:只提交与初始快照不同的键,没有改动就不发请求 */
|
|
1645
|
+
const changed = Object.fromEntries(Object.entries(draft).filter(([key, value]) => value !== baseline[key]))
|
|
1646
|
+
if (!Object.keys(changed).length) { tell('陪伴节奏:没有改动'); return }
|
|
1647
|
+
patch(changed, '陪伴节奏')
|
|
1648
|
+
} }, '保存陪伴节奏')))
|
|
1595
1649
|
}
|
|
1596
1650
|
|
|
1597
1651
|
/* ⭐ 设置大弹窗(2026-10-26 桌宠化改造):从桌宠 ⚙️ 进来的居中弹窗。
|
|
@@ -2183,7 +2237,9 @@ function PetOtherSettings({ view = 'environment', st, setSt, busy, setBusy, say,
|
|
|
2183
2237
|
setUpdateError(r.error || '')
|
|
2184
2238
|
} catch (e) { if (updateAlive.current) setUpdateError(String(e && e.message || e)) }
|
|
2185
2239
|
}
|
|
2186
|
-
|
|
2240
|
+
/* ⭐ P3-36:updateCard 只在「账号与环境」页渲染(learning 页不挂这张卡),
|
|
2241
|
+
预取也只在该页跑 —— learning 页挂载时白跑一次 /update 徒增一次接口往返。 */
|
|
2242
|
+
React.useEffect(() => { if (view !== 'learning') pullUpdate() }, [view])
|
|
2187
2243
|
const updateCard = (() => {
|
|
2188
2244
|
const u = updateState || {}
|
|
2189
2245
|
const install = u.install || {}
|
|
@@ -2692,6 +2748,9 @@ function PetOtherSettings({ view = 'environment', st, setSt, busy, setBusy, say,
|
|
|
2692
2748
|
单独 2.5 秒增量轮询,跟 5 秒的 /status 分开,不然那条流会一顿一顿的。 */
|
|
2693
2749
|
const [acts, setActs] = React.useState([])
|
|
2694
2750
|
const [actLive, setActLive] = React.useState(null)
|
|
2751
|
+
/* ⚠️ P3-37:/activity 404(宿主还没加载这版接口)不是抖动 —— 停轮询 + 出提示条,
|
|
2752
|
+
不然每 2.5s 打一次必 404 的接口,网络面板和日志里全是噪音。 */
|
|
2753
|
+
const [actRouteMissing, setActRouteMissing] = React.useState(false)
|
|
2695
2754
|
const pvRunning = Boolean(pv && pv.running)
|
|
2696
2755
|
React.useEffect(() => {
|
|
2697
2756
|
if (tab !== 'video' || pvRouteMissing.current) return
|
|
@@ -2732,6 +2791,7 @@ function PetOtherSettings({ view = 'environment', st, setSt, busy, setBusy, say,
|
|
|
2732
2791
|
React.useEffect(() => {
|
|
2733
2792
|
if (tab !== 'logs') return
|
|
2734
2793
|
let dead = false
|
|
2794
|
+
let timer = null
|
|
2735
2795
|
let sinceUid = activityCursor.current
|
|
2736
2796
|
const pull = async () => {
|
|
2737
2797
|
if (dead || activityRequest.current) return
|
|
@@ -2760,12 +2820,20 @@ function PetOtherSettings({ view = 'environment', st, setSt, busy, setBusy, say,
|
|
|
2760
2820
|
return next.length > 200 ? next.slice(next.length - 200) : next
|
|
2761
2821
|
})
|
|
2762
2822
|
}
|
|
2763
|
-
} catch {
|
|
2823
|
+
} catch (e) {
|
|
2824
|
+
/* ⚠️ P3-37:404 不是抖动 —— 接口压根不存在(宿主较旧 / 没热加载),
|
|
2825
|
+
停掉这条轮询并提示用户重启;超时等其他失败仍按「抖一下」处理。 */
|
|
2826
|
+
if (e?.status === 404) {
|
|
2827
|
+
if (!dead) setActRouteMissing(true)
|
|
2828
|
+
if (timer) { clearInterval(timer); timer = null }
|
|
2829
|
+
return
|
|
2830
|
+
}
|
|
2831
|
+
}
|
|
2764
2832
|
finally { if (activityRequest.current === token) activityRequest.current = null }
|
|
2765
2833
|
}
|
|
2766
2834
|
pull()
|
|
2767
|
-
|
|
2768
|
-
return () => { dead = true; activityEpoch.current += 1; activityRequest.current = null; clearInterval(
|
|
2835
|
+
timer = setInterval(pull, 2500)
|
|
2836
|
+
return () => { dead = true; activityEpoch.current += 1; activityRequest.current = null; if (timer) clearInterval(timer) }
|
|
2769
2837
|
}, [tab])
|
|
2770
2838
|
|
|
2771
2839
|
const onClearAct = async () => {
|
|
@@ -2873,6 +2941,9 @@ function PetOtherSettings({ view = 'environment', st, setSt, busy, setBusy, say,
|
|
|
2873
2941
|
} catch (e) { say(String(e?.message ?? e), 'warn'); return false }
|
|
2874
2942
|
finally { setBusy('') }
|
|
2875
2943
|
}
|
|
2944
|
+
/* 🔐 P2-15:patch 的单飞包装(见 useLatestPatch 注释)。时间/下拉这类没有 disabled
|
|
2945
|
+
* 挡板的控件连改两下时会并发 PATCH,走它串行化。 */
|
|
2946
|
+
const patchLatest = useLatestPatch(patch)
|
|
2876
2947
|
const sw = (k) => Boolean(cfg && cfg[k])
|
|
2877
2948
|
|
|
2878
2949
|
const onLoginStart = async () => {
|
|
@@ -3111,7 +3182,9 @@ function PetOtherSettings({ view = 'environment', st, setSt, busy, setBusy, say,
|
|
|
3111
3182
|
const saveModel = async (value) => {
|
|
3112
3183
|
const previous = currentModel
|
|
3113
3184
|
setVideoModelId(value)
|
|
3114
|
-
|
|
3185
|
+
/* 🔐 P2-15:走单飞 —— 在飞期间再改,新值只记 pending(本地 select 已更新、
|
|
3186
|
+
* 不回弹),当前请求结束后补发;直接发送失败才回弹旧值。 */
|
|
3187
|
+
if (!(await patchLatest({ videoModelId: value }, '制作模型'))) setVideoModelId(previous)
|
|
3115
3188
|
}
|
|
3116
3189
|
const selectedSession = selected && (selected.sessionId || sessions.find((item) => item.productionId === selected.id)?.sessionId)
|
|
3117
3190
|
return h('div', { 'data-bd-pet-grid': '', 'data-bd-video-studio': '' },
|
|
@@ -3204,12 +3277,13 @@ function PetOtherSettings({ view = 'environment', st, setSt, busy, setBusy, say,
|
|
|
3204
3277
|
const value = e.target.value
|
|
3205
3278
|
if (!/^\d{2}:\d{2}$/.test(value)) return
|
|
3206
3279
|
setPv((old) => ({ ...(old || {}), at: value }))
|
|
3207
|
-
|
|
3280
|
+
/* 🔐 P2-15:单飞防并发 PATCH 乱序回写(见 useLatestPatch) */
|
|
3281
|
+
patchLatest({ pvAt: value }, '自动做视频的时间')
|
|
3208
3282
|
} }))),
|
|
3209
3283
|
h('section', { 'data-bd-sec': '', 'data-bd-span': '2' },
|
|
3210
3284
|
heading('film', '配音与本地环境'),
|
|
3211
3285
|
h('label', { 'data-bd-row': '' }, h('span', { 'data-bd-label': '', 'data-bd-grow': '' }, '配音音色'),
|
|
3212
|
-
h('select', { 'data-bd-input': '', style: { width: '230px' }, value: pv?.voice || 'zh-CN-XiaoyiNeural', onChange: (e) =>
|
|
3286
|
+
h('select', { 'data-bd-input': '', style: { width: '230px' }, value: pv?.voice || 'zh-CN-XiaoyiNeural', onChange: (e) => patchLatest({ pvVoice: e.target.value }, '配音音色') },
|
|
3213
3287
|
h('option', { value: 'zh-CN-XiaoyiNeural' }, '晓伊(默认 · 偏软)'), h('option', { value: 'zh-CN-XiaoxiaoNeural' }, '晓晓(更清亮)'), h('option', { value: 'zh-CN-XiaochenNeural' }, '晓辰(偏成熟)'))),
|
|
3214
3288
|
...[
|
|
3215
3289
|
['GPT-SoVITS 目录', 'gsvRepo', 'pvGsvRepo', 'D:\\...\\GPT-SoVITS'],
|
|
@@ -3464,9 +3538,9 @@ function PetOtherSettings({ view = 'environment', st, setSt, busy, setBusy, say,
|
|
|
3464
3538
|
})),
|
|
3465
3539
|
!scheduleAllDay ? h('div', { 'data-bd-row' : '' },
|
|
3466
3540
|
h('span', { 'data-bd-label': '', 'data-bd-grow': '' }, '开始时间'),
|
|
3467
|
-
h('input', { 'data-bd-input': '', type: 'time', value: scheduleStart || '18:00', onChange: (e) => { setScheduleStart(e.target.value);
|
|
3541
|
+
h('input', { 'data-bd-input': '', type: 'time', value: scheduleStart || '18:00', onChange: (e) => { setScheduleStart(e.target.value); patchLatest({ scheduleStart: e.target.value }, '定时开始时间') } }),
|
|
3468
3542
|
h('span', { 'data-bd-label': '' }, '结束时间'),
|
|
3469
|
-
h('input', { 'data-bd-input': '', type: 'time', value: scheduleEnd || '09:00', onChange: (e) => { setScheduleEnd(e.target.value);
|
|
3543
|
+
h('input', { 'data-bd-input': '', type: 'time', value: scheduleEnd || '09:00', onChange: (e) => { setScheduleEnd(e.target.value); patchLatest({ scheduleEnd: e.target.value }, '定时结束时间') } })) : null,
|
|
3470
3544
|
h('div', { 'data-bd-row' : '' },
|
|
3471
3545
|
h('span', { 'data-bd-badge': '', 'data-live': st.schedule && st.schedule.inWindow ? '' : undefined }, st.schedule && st.schedule.inWindow ? '当前在窗口内' : '当前不在窗口内'),
|
|
3472
3546
|
h('span', { 'data-bd-note': '', 'data-bd-grow': '' }, st.schedule && st.schedule.lastError ? '上次失败:' + st.schedule.lastError : st.schedule && st.schedule.lastStartedAt ? '上次启动:' + petFmtTime(st.schedule.lastStartedAt) : '尚未启动'),
|
|
@@ -3485,7 +3559,7 @@ function PetOtherSettings({ view = 'environment', st, setSt, busy, setBusy, say,
|
|
|
3485
3559
|
label: '闲聊提示', on: !(st && st.config && st.config.idleChatter === false), disabled: Boolean(busy),
|
|
3486
3560
|
note: '按已配置频率显示闲聊,频率由陪伴节奏控制。',
|
|
3487
3561
|
onChange: (v) => patch({ idleChatter: v }, '闲聊泡泡'),
|
|
3488
|
-
}), h(PetCompanionSettings, { st, busy, patch })),
|
|
3562
|
+
}), h(PetCompanionSettings, { st, busy, patch, say })),
|
|
3489
3563
|
() => h('div', { 'data-bd-sec': '' },
|
|
3490
3564
|
heading('spark', '角色与表达'),
|
|
3491
3565
|
h('div', { 'data-bd-note': '' }, '人格与陪伴设置仅用于鲸鱼摸鱼模式。留空使用默认设定。'),
|
|
@@ -3615,6 +3689,8 @@ function PetOtherSettings({ view = 'environment', st, setSt, busy, setBusy, say,
|
|
|
3615
3689
|
h('div', { 'data-bd-h': '', 'data-bd-grow': '' }, icon('pulse'), h('b', {}, '它刚才在干嘛'),
|
|
3616
3690
|
h('span', { 'data-bd-badge': '', 'data-live': fyLive ? '' : undefined }, fyLive ? '在刷' : '没在刷')),
|
|
3617
3691
|
h('button', { 'data-bd-btn': '', 'data-sm': '', onClick: onClearAct, disabled: Boolean(busy) }, '清屏')),
|
|
3692
|
+
/* ⚠️ P3-37:接口不存在时轮询已停,把话说清楚(文案对照图库页的 404 提示) */
|
|
3693
|
+
actRouteMissing ? h('div', { 'data-bd-msg': '', 'data-warn': '', role: 'status', 'data-bd-act-missing': '' }, '活动接口不存在——可能宿主版本较旧,重启 DSH 后重试。') : null,
|
|
3618
3694
|
fyLive && actLive && actLive.thinking
|
|
3619
3695
|
? h('div', { 'data-bd-thinking': '' },
|
|
3620
3696
|
h('i', { 'data-bd-dot': '' }),
|
package/docs/RELEASE_AUDIT.md
CHANGED
|
@@ -63,8 +63,8 @@
|
|
|
63
63
|
|
|
64
64
|
## 发布状态与复跑
|
|
65
65
|
|
|
66
|
-
当前版本是本地待发布2.
|
|
66
|
+
当前版本是本地待发布2.7.2(2026-11-12 审计修复批;2.7.0 / 2.7.1 已发布 npm 并打 GitHub 标签)。最终安装包与完整源码ZIP位于 `release/`,有逐文件验证报告和SHA-256;运行manifest删除开发命令是明确的发布投影,其他运行文件与源码逐字节核对。源码ZIP包含当前未提交的新文件,不混入历史工作区的旧包。
|
|
67
67
|
|
|
68
68
|
安装包清洁安装使用临时家目录与生产依赖,执行包内 `check` 自检。依赖代码、媒体和界面文件调整后需重新构建归档。维护者可以在源码根运行README/CONTRIBUTING列出的全部命令;输出可由 `QA_OUTPUT_DIR`、`RELEASE_TMPDIR` 和 `DSH_QA_TMPDIR` 指定位置。
|
|
69
69
|
|
|
70
|
-
|
|
70
|
+
市场所需条目、截图及客观描述已准备;年龄门禁(仓库须创建满一天)自 2026-10-29 起已满足,2.7.x 系列此前已按 [发布指南](RELEASING.md) 发布过 npm 与 GitHub 标签。**2.7.2 尚未发布**:npm发布、GitHub标签与 Release 必须在验收与打包全部通过后执行,不能由本地打包通过推断已发布或已收录。
|
package/docs/RELEASING.md
CHANGED
|
@@ -57,6 +57,15 @@ npm 运行包提供 `npm run check`;`npm test`、界面验收和发布命令
|
|
|
57
57
|
|
|
58
58
|
将经过审阅的源码提交到 `pyyxzi/dsh-deepmoyu`,确认 CI 结果后再创建对应版本标签和 Release。可以附加上一步的两个归档、校验和与审查记录。不要把 `release/` 归档作为源码提交进去。
|
|
59
59
|
|
|
60
|
+
本机推 GitHub 经常连不上(`Failed to connect to github.com port 443` / `Recv failure: Connection was reset`),但同一时刻 `https://github.com` 用浏览器/`Invoke-WebRequest` 是 200 —— 说明是 git 走 TLS/直连的路径被卡,不是网络整体断。两个参数按顺序试(2026-11-10 实测第二种可用):
|
|
61
|
+
|
|
62
|
+
```sh
|
|
63
|
+
git -c http.sslBackend=schannel push origin main
|
|
64
|
+
git -c http.proxy=http://127.0.0.1:7897 -c https.proxy=http://127.0.0.1:7897 push origin main
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
本机全局 `git config` 里有 `http.sslbackend=openssl`,会盖掉 schannel,所以每次都得在命令行显式传;7897 是本地代理端口,用之前先 `Test-NetConnection 127.0.0.1 -Port 7897` 确认在监听。推送"看似失败"时先 `git -c ... ls-remote origin refs/heads/main` 对一下远端 SHA 再决定要不要重推,不要盲目反复重试。
|
|
68
|
+
|
|
60
69
|
npm 发布需要该包的发布权限和 registry 要求的认证。确认账号、版本、归档和发布目标后,再执行:
|
|
61
70
|
|
|
62
71
|
```sh
|
|
@@ -69,6 +78,12 @@ npm publish ./release/dsh-deepmoyu-<版本>.tgz --registry=https://registry.npmj
|
|
|
69
78
|
- 本包在 npm 上的维护者是 `tzl666`,不是 GitHub 的 `pyyxzi`。`~/.npmrc` 里必须有 `//registry.npmjs.org/:_authToken=`,且 token 属于有发布权限的账号;`npm whoami` 返回 401 就是 token 过期,换好后重试。默认 registry 若指向 npmmirror,`whoami`、`publish` 都必须显式带 `--registry=https://registry.npmjs.org`。
|
|
70
79
|
- `npm publish` 有时在**上传成功之后**仍以 `You cannot publish over the previously published versions` 报错退出。不要据此判定失败,也不要用改版本号的方式绕过:先 `Invoke-RestMethod https://registry.npmjs.org/dsh-deepmoyu` 查 `versions` 与 `time.<版本>`,再下载 registry tarball 比对 SHA-256 与本地归档是否一致,以 registry 的实际内容为准。
|
|
71
80
|
|
|
81
|
+
发布后的可见性有两段延迟(2026-11-10 实测 2.7.1):
|
|
82
|
+
|
|
83
|
+
- `npm publish` 打出 `+ dsh-deepmoyu@<版本>` 且退出码 0,只是 registry 回了 `PUT 202`(已收下、正在处理)。元数据要先渲染完才可读,**头几分钟 `https://registry.npmjs.org/dsh-deepmoyu/<版本>` 仍会 404、`dist-tags.latest` 也还是上一版**,这不是失败。实测约 3 分钟后 `/<版本>` 可取、`latest` 才切过去。
|
|
84
|
+
- 排查时务必加缓存穿透参数(`?t=<ticks>` 加 `Cache-Control: no-cache`);不带参数可能读到 CDN 上的旧 packument,把"还在处理"误判成"没发上去"。
|
|
85
|
+
- 切换令牌可以直接用临时配置文件,不必改写 `~/.npmrc`:`npm publish ... --userconfig <临时文件> --registry=https://registry.npmjs.org`。临时文件用完即删,别把它留在仓库或家目录里。
|
|
86
|
+
|
|
72
87
|
版本号一经上传不可覆盖。`2.5.0` 只在 GitHub 上有标签,从未发布到 npm,因此 npm 上 `2.4.2` 之后直接是 `2.6.0`;如需补齐序列,必须另起一个未被占用的版本号重新构建。
|
|
73
88
|
|
|
74
89
|
发布完成后查询指定版本的 `dist.integrity`、`dist.shasum` 和仓库信息,下载 registry tarball 并核对内容;本地构建通过不能替代发布后检查。npm 版本不可覆盖,不应为发布失败临时篡改归档内容。
|
package/docs/tool-evidence.json
CHANGED