dsh-feiyu-mood 0.8.2 → 0.8.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/CHANGELOG.md +30 -2
- package/LICENSE +50 -21
- package/README.md +3 -1
- package/assets/feiyu-good.png +0 -0
- package/assets/feiyu-great.png +0 -0
- package/assets/feiyu-sad.png +0 -0
- package/assets/feiyu-worry.png +0 -0
- package/assets/widget.js +25 -1
- package/client.js +21 -10
- package/lib/index.js +9 -5
- package/lib/selfupdate.mjs +119 -40
- package/package.json +58 -58
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,31 @@
|
|
|
1
|
+
## 0.8.4 — 2026-11-01
|
|
2
|
+
|
|
3
|
+
**「检查更新」以前会报错版本,而且只给一条命令让你自己手动复制、然后手动装**
|
|
4
|
+
|
|
5
|
+
- 🐞 **"最新版本"会骗人**:以前是「哪个地址先返回就用哪个」。GitHub 一超时就落到 jsDelivr ——
|
|
6
|
+
那个地址**带 CDN 缓存、会滞后好几天**,于是同一分钟内能报出好几个不同的"最新",
|
|
7
|
+
甚至比你本机装的还旧,看着像"该降级"。
|
|
8
|
+
- ✅ 现在**三个源全查**(GitHub / jsDelivr / npm registry),**取版本号最大的那个**:
|
|
9
|
+
版本只会往前走,哪个源报小了就是它滞后。三个都失败才说"查不到",绝不把旧值当成"最新"。
|
|
10
|
+
- ✨ **检测到新版可以直接装**:那一块多了一个「一键更新到 vX」按钮,点它走 DSH 自己的插件管理装最新版,
|
|
11
|
+
装完会提示你重启(或重载)一次。以前只有一个「复制命令」,得你自己去终端敲两行。
|
|
12
|
+
- 🧹 **界面上那些命令行全删了**:以前那两行 `dsh plugin --profile web remove/add` 和「复制命令」按钮不再显示 —— 很多人看不懂,还得跑来问。现在有新版就只有「一键更新到 vX」一个按钮。
|
|
13
|
+
- ⚠ **本地开发副本(link: 进来的那种)不给一键装** —— 那会把正在改的目录换成仓库里的版本;
|
|
14
|
+
这种会明确写出来,让你手动更新。
|
|
15
|
+
|
|
16
|
+
## 0.8.3 — 2026-10-31
|
|
17
|
+
|
|
18
|
+
**外面那个挂件开始跟着心情换形象(五档)**
|
|
19
|
+
|
|
20
|
+
- ✨ 以前挂件永远是同一张大肥鱼(也就是面板里「一般」那一档)。现在挂件本身按心情分五档换图:
|
|
21
|
+
**1 欢喜 / 2 开心 / 3 一般 / 4 心疼 / 5 难过** —— 阈值和面板里那张脸完全同一套
|
|
22
|
+
(≥80 / ≥62 / ≥42 / ≥25),两边不会对不上。
|
|
23
|
+
- 新增四张形象图 `feiyu-great.png`、`feiyu-good.png`、`feiyu-worry.png`、`feiyu-sad.png`
|
|
24
|
+
(1254×1254,和原来那张同画布、同角色尺寸、同落位,换档不会一大一小、也不会跳位置)。
|
|
25
|
+
**3 档继续用原来的 `feiyu.png`**,文件名不动 —— 老备份、老缓存都对得上。
|
|
26
|
+
- 五张在挂件初始化时各预加载一次,切档不会空一帧。
|
|
27
|
+
- 收起态(小鲸鱼那个形状)也跟着换 —— 它和展开态是同一张图。
|
|
28
|
+
|
|
1
29
|
## 0.8.2 — 2026-10-30
|
|
2
30
|
|
|
3
31
|
**挂饰拖过之后,刷新会回到默认角落**
|
|
@@ -77,7 +105,7 @@
|
|
|
77
105
|
**收起态那个心情数字会自己变了**
|
|
78
106
|
|
|
79
107
|
- 🐞 右下角挂饰收起来以后,小圆里那个心情数字只有「点开面板」才会刷新一次,
|
|
80
|
-
|
|
108
|
+
收起状态就一直停在旧值(在外面只显示数字的那个不会实时更新)。
|
|
81
109
|
- ✅ 现在收起态也每 20 秒拉一次状态;页面重新可见时也补一次。
|
|
82
110
|
- 自测:语法检查 + 25 项单测全过。
|
|
83
111
|
## 0.7.5 — 2026-10-27
|
|
@@ -132,7 +160,7 @@
|
|
|
132
160
|
|
|
133
161
|
**自动触发的对话也记心情了(QQ 里发的、骰子投中的搭话)**
|
|
134
162
|
|
|
135
|
-
- 🐛
|
|
163
|
+
- 🐛 老 bug:在 QQ 里跟它说话、或者「主动搭话」自己冒出来的那一轮,心情**不记录**。
|
|
136
164
|
- 🔍 真因:找「这一轮是谁在说话」时,最后一句写的是 `来源不是 user 就跳过` ——
|
|
137
165
|
于是 QQ 桥转进来的话(`source.kind = 'qq'`)、鱼搭子挂件说的话(`'fishbuddy'`)**全被当成“别的插件的消息”丢掉**,
|
|
138
166
|
这一轮找不到输入 → 直接不判心情。
|
package/LICENSE
CHANGED
|
@@ -1,21 +1,50 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
1
|
+
Yuuyuko-uu 插件许可 1.2
|
|
2
|
+
|
|
3
|
+
版权所有 (c) 2026 Yuuyuko-uu
|
|
4
|
+
|
|
5
|
+
──────────────────────────────────────────────────────────────
|
|
6
|
+
|
|
7
|
+
【为什么不用 MIT】
|
|
8
|
+
|
|
9
|
+
2026 年 10 月,有人把我这几个插件(代码、README,还有我做的图)
|
|
10
|
+
改名、去掉署名,在视频里说成「自研」发布。
|
|
11
|
+
|
|
12
|
+
他没有违反 MIT —— MIT 只要求保留版权声明,不管「这是谁做的」。
|
|
13
|
+
|
|
14
|
+
所以换成了这份许可。它只多要求一件事:
|
|
15
|
+
你拿走的时候,说一声这是谁做的。
|
|
16
|
+
|
|
17
|
+
安装、修改、商用,全都允许 —— 和 MIT 一样宽。
|
|
18
|
+
|
|
19
|
+
──────────────────────────────────────────────────────────────
|
|
20
|
+
|
|
21
|
+
一、允许,不需要另外问我
|
|
22
|
+
|
|
23
|
+
1. 安装到自己的机器上使用,长期用,商用也行。
|
|
24
|
+
2. 读源码、改源码、改成自己需要的版本(改给自己用,不必公开)。
|
|
25
|
+
3. 把你的改动发给我(issue / PR);我采纳后,按仓库惯例记你的贡献。
|
|
26
|
+
4. 在文章、视频、教程里介绍、演示、截图、引用本项目 —— 请附上原仓库地址。
|
|
27
|
+
|
|
28
|
+
二、允许,但必须做到
|
|
29
|
+
|
|
30
|
+
5. 再分发时(把代码或其实质性部分放进你自己的仓库、整合包、安装包,
|
|
31
|
+
或者换一个包名发布),必须同时做到:
|
|
32
|
+
a) 完整保留本文件,不删、不改、不缩略;
|
|
33
|
+
b) 在 README 的第一屏写明:本项目派生自 <原仓库地址>,原作者 Yuuyuko-uu;
|
|
34
|
+
c) 说明你改了哪些部分。
|
|
35
|
+
6. 公开的衍生作品必须继续使用本许可(或更严格的许可),不得改用更宽松的许可。
|
|
36
|
+
|
|
37
|
+
三、禁止
|
|
38
|
+
|
|
39
|
+
7. 不得把本项目的全部或实质性部分声称为你自己原创 ——
|
|
40
|
+
包括在 README、发布说明、视频、插件市场条目里,
|
|
41
|
+
把它写成「自研」「原创」「我写的」。
|
|
42
|
+
8. 不得删除、替换、隐藏本项目的作者署名,
|
|
43
|
+
包括 LICENSE、NOTICE、README、package.json 和源码注释里的。
|
|
44
|
+
|
|
45
|
+
四、其他
|
|
46
|
+
|
|
47
|
+
9. 本项目按「现状」提供,不附带任何明示或默示的担保。
|
|
48
|
+
10. 违反第二、三条中任何一条,本许可立即终止;你还应当删除已发布的衍生作品。
|
|
49
|
+
从违约那一刻起,你就不再拥有使用、复制、分发本项目的任何授权。
|
|
50
|
+
11. 上面没写的情况想用,直接来问我,一般都会答应。
|
package/README.md
CHANGED
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
package/assets/widget.js
CHANGED
|
@@ -101,6 +101,28 @@
|
|
|
101
101
|
widget.appendChild(bodyImg)
|
|
102
102
|
widget.title = '心情(点开看,按住拖动换位置)'
|
|
103
103
|
|
|
104
|
+
// ⭐ 2026-10-31:**外面这个小挂件跟着心情换形象**(五档,和面板里那张脸同一套阈值)
|
|
105
|
+
// 1 大笑 / 2 微笑 / 3 普通(原图 feiyu.png)/ 4 嘟嘴 / 5 哭
|
|
106
|
+
// ⚠ 3 档仍然用 feiyu.png 那个路径,别改成 feiyu-ok.png —— 老图不动,免得缓存和备份对不上
|
|
107
|
+
function bodyFor(v) {
|
|
108
|
+
if (v >= 80) return B + '/feiyu-great.png'
|
|
109
|
+
if (v >= 62) return B + '/feiyu-good.png'
|
|
110
|
+
if (v >= 42) return B + '/feiyu.png'
|
|
111
|
+
if (v >= 25) return B + '/feiyu-worry.png'
|
|
112
|
+
return B + '/feiyu-sad.png'
|
|
113
|
+
}
|
|
114
|
+
var 当前形象 = B + '/feiyu.png'
|
|
115
|
+
function 换形象(v) {
|
|
116
|
+
if (v == null || v === '') return
|
|
117
|
+
var src = bodyFor(Number(v))
|
|
118
|
+
if (src === 当前形象) return
|
|
119
|
+
当前形象 = src
|
|
120
|
+
bodyImg.src = src
|
|
121
|
+
}
|
|
122
|
+
// 五张先各自拉一遍,换档的时候不会空一下(本地端口,几十毫秒)
|
|
123
|
+
;[B + '/feiyu-great.png', B + '/feiyu-good.png', B + '/feiyu.png', B + '/feiyu-worry.png', B + '/feiyu-sad.png']
|
|
124
|
+
.forEach(function (u) { var i = new Image(); i.src = u })
|
|
125
|
+
|
|
104
126
|
// ⭐ 2026-10-23(反馈第二版):分清两件事,别混在一起 ——
|
|
105
127
|
// · **外面那个东西**(大肥鱼形象 / 小圆):点一下只负责**开关面板**,不改形态
|
|
106
128
|
// · **面板右上角那个钮**:只负责**切换外面显示的形态**(大 ↔ 小)
|
|
@@ -274,6 +296,8 @@
|
|
|
274
296
|
function renderAll(d) {
|
|
275
297
|
// 收起态那个小圆里显示的心情数字
|
|
276
298
|
try { colNum.textContent = d && d.mood != null ? String(d.mood) : '--' } catch (e) {}
|
|
299
|
+
// ⭐ 2026-10-31:外面的形象跟着档位换(1 大笑 … 5 哭),面板开着也照换
|
|
300
|
+
try { 换形象(d && d.mood) } catch (e) {}
|
|
277
301
|
if (page === 'log') renderLog(d)
|
|
278
302
|
else renderMain(d)
|
|
279
303
|
}
|
|
@@ -440,7 +464,7 @@
|
|
|
440
464
|
保持屏内() // 形态定下来之后再夹一次(收起态 64px、大图能到 288px,合法范围不一样)
|
|
441
465
|
load()
|
|
442
466
|
|
|
443
|
-
// ⭐ 0.7.6
|
|
467
|
+
// ⭐ 0.7.6(收起态那个数字以前不刷新,得点一下才变):
|
|
444
468
|
// 以前只有「点开面板」才 load() 一次,收起来的时候就再也不拉了 —— 数字一直是旧的。
|
|
445
469
|
// 现在收起态也每 20 秒拉一次(就一个 GET,不花 token);页面重新可见时也补一次。
|
|
446
470
|
setInterval(load, 20000)
|
package/client.js
CHANGED
|
@@ -149,7 +149,7 @@ window.__ModuleLoader__.load({
|
|
|
149
149
|
return () => clearInterval(t)
|
|
150
150
|
}, [load])
|
|
151
151
|
|
|
152
|
-
// ⭐ 0.7.5
|
|
152
|
+
// ⭐ 0.7.5(最小化的时候心情以前不刷新,得点一下才变):
|
|
153
153
|
// 页面在后台时浏览器会把定时器节流到一分钟以上,切回来那一瞬间还显示旧值。
|
|
154
154
|
// 所以「重新可见 / 重新获得焦点」时立刻拉一次 —— 不用她点任何东西。
|
|
155
155
|
React.useEffect(() => {
|
|
@@ -186,7 +186,19 @@ window.__ModuleLoader__.load({
|
|
|
186
186
|
}
|
|
187
187
|
setUpBusy('')
|
|
188
188
|
}
|
|
189
|
-
|
|
189
|
+
|
|
190
|
+
// ⭐ 2026-11-01:查到新版就**一键装**(宿主那边的 pluginManager.installBundle)
|
|
191
|
+
const 一键更新 = async () => {
|
|
192
|
+
setUpBusy('1')
|
|
193
|
+
try {
|
|
194
|
+
const r = await postJson(API + '/install', {})
|
|
195
|
+
if (r && r.ok) flash('开始更新了 —— 装完按提示重载一次')
|
|
196
|
+
else flash('更新失败:' + ((r && r.error) || '不知道原因'))
|
|
197
|
+
} catch (e) {
|
|
198
|
+
flash('更新失败:' + (e && e.message))
|
|
199
|
+
}
|
|
200
|
+
setUpBusy('')
|
|
201
|
+
}
|
|
190
202
|
|
|
191
203
|
/* ⭐ 一份心情可以绑好几个会话(或一个工作区):这里照这份清单画
|
|
192
204
|
(没配置过就当成「第一份」;id 尽量沿用宿主给的 st.key,别凭空造一个) */
|
|
@@ -271,15 +283,14 @@ window.__ModuleLoader__.load({
|
|
|
271
283
|
upBusy === '1' ? '查…' : '检查更新'))
|
|
272
284
|
const 新版块 = (up && up.hasUpdate)
|
|
273
285
|
? h('div', { 'data-fm-sec': '' },
|
|
274
|
-
h('div', { 'data-fm-note': '' },
|
|
275
|
-
|
|
286
|
+
h('div', { 'data-fm-note': '' }, up.canInstall
|
|
287
|
+
? '仓库里已经有 v' + up.latest + ' 了,点下面这个按钮直接装。'
|
|
288
|
+
: '仓库里已经有 v' + up.latest + ' 了。这是本地开发副本(link: 进来的),请手动更新。'),
|
|
276
289
|
h('div', { 'data-fm-row': '' },
|
|
277
|
-
|
|
278
|
-
'data-fm-btn': '',
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
.catch(() => flash('复制不了,手动选中吧')),
|
|
282
|
-
}, '复制命令'),
|
|
290
|
+
up.canInstall
|
|
291
|
+
? h('button', { 'data-fm-btn': '', disabled: upBusy === '1', onClick: 一键更新 },
|
|
292
|
+
upBusy === '1' ? '装…' : '一键更新到 v' + up.latest)
|
|
293
|
+
: null,
|
|
283
294
|
up.repoUrl
|
|
284
295
|
? h('a', {
|
|
285
296
|
'data-fm-btn': '', href: up.repoUrl, target: '_blank', rel: 'noreferrer',
|
package/lib/index.js
CHANGED
|
@@ -145,7 +145,7 @@ function stripBridgePrefix(s) {
|
|
|
145
145
|
if (最后 < 0) return t
|
|
146
146
|
let 后 = t.slice(最后 + 长度).replace(/^[))\]】\s]*/, '')
|
|
147
147
|
// ⭐ 2026-10-23(反馈):标记后面常常还跟着一小段「谁发的」说明,例如
|
|
148
|
-
//
|
|
148
|
+
// 【主人私信】主人(mid 10000)刚给你发私信:<原话> …
|
|
149
149
|
// 原来只切到标记,这段说明会一起喂给判断模型。冒号前那截够短、又明显是说明,就再切一刀。
|
|
150
150
|
const 冒号 = 后.slice(0, 80).search(/[::]/)
|
|
151
151
|
if (冒号 > 0 && /mid\s*\d+|私信|消息/i.test(后.slice(0, 冒号))) 后 = 后.slice(冒号 + 1)
|
|
@@ -483,7 +483,7 @@ export function 规整模式(v) {
|
|
|
483
483
|
* `~` + 4 位大写十六进制(按 UTF-16 码元);
|
|
484
484
|
* · 去掉开头连续的 `-`(空了就是 `root`),截到 251 个字符,两头各包一个 `-`。
|
|
485
485
|
* 例如:
|
|
486
|
-
*
|
|
486
|
+
* D:\my-workspace\dsh → --D-my-workspace-dsh--
|
|
487
487
|
* D:\shing_of_kai\SourceCode\ShaoZhu'sDSHPlugins
|
|
488
488
|
* → --D-shing_of_kai-SourceCode-ShaoZhu~0027sDSHPlugins--
|
|
489
489
|
* ⚠️ 单引号、`~` 这些以前被简化成 `-`,跟 DSH 算出来的目录名对不上 —— 同一个工作区会裂成两项。
|
|
@@ -968,7 +968,7 @@ const 注入上下文 = new Set(['runtime-context', 'time-context', 'system-remi
|
|
|
968
968
|
const 外部触发 = new Set(['bili-duty-scan'])
|
|
969
969
|
// ⭐ 你自己的主动搭话(「可以去找她聊天了」)—— 这不是干活,判成干活就全错了
|
|
970
970
|
const 自主搭话 = new Set(['speak-up'])
|
|
971
|
-
// ⭐ 2026-10-25
|
|
971
|
+
// ⭐ 2026-10-25(QQ 来的话、骰子投中的搭话,心情都不记录):
|
|
972
972
|
// 以前最后一句是 `if (src && src !== 'user') continue` —— 只要来源不是 user,
|
|
973
973
|
// 全当「别的插件消息」跳过。于是 QQ 桥(source.kind='qq')和鱼搭子挂件('fishbuddy')
|
|
974
974
|
// 转进来的话根本不算「她在说话」→ 找不到输入 → 这一轮不判 → 心情不记。
|
|
@@ -1266,7 +1266,7 @@ async function judge(ctx, agent, userText, reply, mode, o) {
|
|
|
1266
1266
|
let user
|
|
1267
1267
|
if (mode === 'work') {
|
|
1268
1268
|
sys += 干活轮补充(o)
|
|
1269
|
-
// ⭐
|
|
1269
|
+
// ⭐ 主人说的话摆最前面、闹钟塞到最后一行括号里 —— 顺序就是权重,别让模型拿闹钟当理由
|
|
1270
1270
|
user = '⚠️ 这一轮**不是' + o.owner + '在跟你说话**,是定时任务把你叫起来干活的。\n\n'
|
|
1271
1271
|
+ '**你这轮说的话(唯一判据,重点看这个):**\n' + reply.slice(0, 1200)
|
|
1272
1272
|
+ '\n\n(下面这条只是闹钟,跟心情无关,别拿它当理由:「' + userText.slice(0, 100) + '」)'
|
|
@@ -1832,7 +1832,11 @@ export function apply(ctx) {
|
|
|
1832
1832
|
|
|
1833
1833
|
// 5.5) 图片(形象图 + 五档表情)
|
|
1834
1834
|
try {
|
|
1835
|
-
const IMAGE_FILES = [
|
|
1835
|
+
const IMAGE_FILES = [
|
|
1836
|
+
'feiyu.png', // 3 档(普通)—— 也是原来的形象图,别删,老版本还在用这个路径
|
|
1837
|
+
'feiyu-great.png', 'feiyu-good.png', 'feiyu-worry.png', 'feiyu-sad.png',
|
|
1838
|
+
'face-great.png', 'face-good.png', 'face-ok.png', 'face-worry.png', 'face-sad.png',
|
|
1839
|
+
]
|
|
1836
1840
|
for (const f of IMAGE_FILES) {
|
|
1837
1841
|
ctx.effect(() => ctx.webServer.register({
|
|
1838
1842
|
kind: 'exact',
|
package/lib/selfupdate.mjs
CHANGED
|
@@ -1,17 +1,22 @@
|
|
|
1
|
-
// 插件自查更新 ——
|
|
1
|
+
// 插件自查更新 —— 查的是**这个插件自己的仓库**,并且能一键装
|
|
2
2
|
//
|
|
3
|
-
//
|
|
3
|
+
// 每个插件在自己设置页放一个「检查更新」:点一下去自己的仓库 / npm 看最新版本。
|
|
4
4
|
//
|
|
5
5
|
// 原则:
|
|
6
6
|
// · 仓库地址从包里自己的 package.json 读(`repository`),不在代码里写死
|
|
7
|
-
// ·
|
|
8
|
-
//
|
|
7
|
+
// · **多源全查、取最大的那个版本** —— 以前是"谁先返回用谁",GitHub 一超时就落到
|
|
8
|
+
// jsDelivr 那个**带 CDN 缓存**的源上,于是同一分钟能报出两个不同的"最新",甚至比本机还旧
|
|
9
|
+
// · 查不到要如实说"查不到",绝不能显示成"已是最新"
|
|
9
10
|
// · 结果缓存 6 小时;点按钮(refresh)就绕过缓存
|
|
11
|
+
// · 「一键更新」走宿主自己的 pluginManager.installBundle —— 装完由宿主决定要不要重载;
|
|
12
|
+
// **本地开发副本(link:)不给装**,那会把她正在改的目录换成仓库里的版本
|
|
10
13
|
//
|
|
11
|
-
// ⚠
|
|
12
|
-
//
|
|
14
|
+
// ⚠ 这个文件是**复制多份**到各个插件里的同一份实现,只在这一个地方改了要同步给其它插件。
|
|
15
|
+
//
|
|
16
|
+
// 2026-11-01:多源取最大 + 一键更新(install)+ 装完把结果原样带回界面。
|
|
13
17
|
|
|
14
|
-
import { readFileSync } from 'node:fs'
|
|
18
|
+
import { readFileSync, readdirSync, existsSync } from 'node:fs'
|
|
19
|
+
import { homedir } from 'node:os'
|
|
15
20
|
import { join } from 'node:path'
|
|
16
21
|
|
|
17
22
|
const TTL = 6 * 3600 * 1000
|
|
@@ -39,17 +44,23 @@ export function cmpVer(a, b) {
|
|
|
39
44
|
return x.pre > y.pre ? 1 : -1
|
|
40
45
|
}
|
|
41
46
|
|
|
42
|
-
|
|
47
|
+
// 三个源都要查:GitHub raw(真值)· jsDelivr(有缓存,可能滞后)· npm registry(发过就有)
|
|
48
|
+
function sources(meta, repo) {
|
|
49
|
+
const out = []
|
|
43
50
|
const s = String(repo || '').trim()
|
|
44
51
|
const m = /github\.com[/:]([^/]+)\/([^/#?]+)/i.exec(s)
|
|
45
|
-
if (
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
`https://
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
52
|
+
if (m) {
|
|
53
|
+
const owner = m[1]
|
|
54
|
+
const name = m[2].replace(/\.git$/i, '')
|
|
55
|
+
out.push({ url: `https://raw.githubusercontent.com/${owner}/${name}/HEAD/package.json`, from: 'GitHub' })
|
|
56
|
+
out.push({ url: `https://cdn.jsdelivr.net/gh/${owner}/${name}@main/package.json`, from: 'jsDelivr' })
|
|
57
|
+
}
|
|
58
|
+
const pkg = String((meta && meta.name) || '').trim()
|
|
59
|
+
if (pkg) {
|
|
60
|
+
const enc = pkg.startsWith('@') ? '@' + pkg.slice(1).replace('/', '%2F') : encodeURIComponent(pkg)
|
|
61
|
+
out.push({ url: `https://registry.npmjs.org/${enc}/latest`, from: 'npm' })
|
|
62
|
+
}
|
|
63
|
+
return out
|
|
53
64
|
}
|
|
54
65
|
|
|
55
66
|
export function createSelfUpdate({ packageRoot, diag }) {
|
|
@@ -73,30 +84,40 @@ export function createSelfUpdate({ packageRoot, diag }) {
|
|
|
73
84
|
.replace(/\.git$/, '')
|
|
74
85
|
.replace(/#readme$/i, '')
|
|
75
86
|
|
|
87
|
+
// 装在自己的 node_modules 里 = 正常安装;不在 = 本地 link 的开发副本
|
|
88
|
+
const 是开发副本 = !/[\\/]node_modules[\\/]/.test(String(packageRoot))
|
|
89
|
+
|
|
76
90
|
let cache = null
|
|
77
91
|
|
|
78
92
|
async function fetchLatest() {
|
|
79
|
-
const
|
|
80
|
-
if (!
|
|
81
|
-
|
|
82
|
-
for (const url of urls) {
|
|
93
|
+
const list = sources(meta, repo)
|
|
94
|
+
if (!list.length) return { latest: null, error: '包里没写仓库地址,也不像在 npm 上发过,没法查' }
|
|
95
|
+
const 结果 = await Promise.all(list.map(async (s) => {
|
|
83
96
|
try {
|
|
84
|
-
const res = await fetch(url, {
|
|
97
|
+
const res = await fetch(s.url, {
|
|
85
98
|
signal: AbortSignal.timeout(TIMEOUT),
|
|
86
99
|
headers: { accept: 'application/json', 'user-agent': 'dsh-plugin-selfupdate' },
|
|
87
100
|
})
|
|
88
|
-
if (!res.ok) {
|
|
101
|
+
if (!res.ok) return { err: s.from + ' HTTP ' + res.status }
|
|
89
102
|
const j = await res.json()
|
|
90
|
-
|
|
91
|
-
return {
|
|
92
|
-
|
|
93
|
-
from: url.includes('jsdelivr') ? 'jsDelivr(有缓存,可能滞后)' : 'GitHub',
|
|
94
|
-
}
|
|
103
|
+
const v = String((j && j.version) || '')
|
|
104
|
+
if (!v) return { err: s.from + ' 没有 version' }
|
|
105
|
+
return { v, from: s.from }
|
|
95
106
|
} catch (e) {
|
|
96
|
-
|
|
107
|
+
return { err: s.from + ' ' + String((e && e.message) || e) }
|
|
97
108
|
}
|
|
109
|
+
}))
|
|
110
|
+
// ⭐ 取**最大**的那个:版本只会往前走,任何一个源报小了都只是它滞后
|
|
111
|
+
let best = null
|
|
112
|
+
let bestFrom = null
|
|
113
|
+
for (const r of 结果) {
|
|
114
|
+
if (!r.v) continue
|
|
115
|
+
if (best === null || cmpVer(r.v, best) > 0) { best = r.v; bestFrom = r.from }
|
|
98
116
|
}
|
|
99
|
-
|
|
117
|
+
if (best === null) {
|
|
118
|
+
return { latest: null, error: 结果.map((r) => r.err).filter(Boolean).join(' · ') || '查询失败' }
|
|
119
|
+
}
|
|
120
|
+
return { latest: best, from: bestFrom }
|
|
100
121
|
}
|
|
101
122
|
|
|
102
123
|
async function check(force) {
|
|
@@ -113,32 +134,90 @@ export function createSelfUpdate({ packageRoot, diag }) {
|
|
|
113
134
|
error: r.error || null,
|
|
114
135
|
from: r.from || null,
|
|
115
136
|
repoUrl: repoUrl || null,
|
|
137
|
+
devCopy: 是开发副本,
|
|
138
|
+
canInstall: !是开发副本,
|
|
116
139
|
checkedAt: Date.now(),
|
|
117
140
|
}
|
|
118
|
-
log('查更新:当前 ' + current + ' / 最新 ' + (r.latest || '查不到') + (r.error ? '(' + r.error + ')' : ''))
|
|
141
|
+
log('查更新:当前 ' + current + ' / 最新 ' + (r.latest || '查不到') + (r.from ? '(' + r.from + ')' : '') + (r.error ? '(' + r.error + ')' : ''))
|
|
119
142
|
return cache
|
|
120
143
|
}
|
|
121
144
|
|
|
145
|
+
// 这个插件是以什么身份装的?从 profile 的 package.json 里把它那行依赖原样取出来 ——
|
|
146
|
+
// 用它当 spec 才是"同源更新",不会把 github 装的换成 npm 版。
|
|
147
|
+
function 取安装来源() {
|
|
148
|
+
const HOME = process.env.DSH_HOME || join(homedir(), '.dsh')
|
|
149
|
+
const name = String(meta.name || '')
|
|
150
|
+
if (!name) return ''
|
|
151
|
+
let profiles = []
|
|
152
|
+
try { profiles = readdirSync(join(HOME, 'profiles')) } catch (e) { return '' }
|
|
153
|
+
for (const p of profiles) {
|
|
154
|
+
const f = join(HOME, 'profiles', p, 'package.json')
|
|
155
|
+
if (!existsSync(f)) continue
|
|
156
|
+
try {
|
|
157
|
+
const j = JSON.parse(readFileSync(f, 'utf8'))
|
|
158
|
+
const dep = String((j.dependencies && j.dependencies[name]) || '')
|
|
159
|
+
if (/^(github:|git\+|https?:)/i.test(dep)) return dep
|
|
160
|
+
} catch (e) { /* 读不了就下一个 */ }
|
|
161
|
+
}
|
|
162
|
+
// 兜底:从 package.json 的仓库地址拼
|
|
163
|
+
const m = /github\.com[/:]([^/]+)\/([^/#?]+)/i.exec(repoUrl)
|
|
164
|
+
if (m) return 'github:' + m[1] + '/' + m[2].replace(/\.git$/i, '')
|
|
165
|
+
return ''
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
async function install(ctx) {
|
|
169
|
+
if (是开发副本) {
|
|
170
|
+
return { ok: false, error: '这是本地开发副本(link: 进来的),一键更新会把你正在改的目录换成仓库里的版本 —— 要更新请在仓库里 git pull' }
|
|
171
|
+
}
|
|
172
|
+
const pm = ctx && ctx.pluginManager
|
|
173
|
+
if (!pm || typeof pm.installBundle !== 'function') {
|
|
174
|
+
return { ok: false, error: '这版 DSH 没有插件管理接口,装不了;请手动更新' }
|
|
175
|
+
}
|
|
176
|
+
const spec = 取安装来源() || (meta.name ? String(meta.name) : '')
|
|
177
|
+
if (!spec) return { ok: false, error: '不知道该从哪儿装(既没有仓库地址,也没记录安装来源)' }
|
|
178
|
+
try {
|
|
179
|
+
log('一键更新:开始装 ' + spec)
|
|
180
|
+
const r = await pm.installBundle(spec)
|
|
181
|
+
log('一键更新:' + JSON.stringify(r && (r.applied ?? r.application ?? r.reason ?? r)))
|
|
182
|
+
return { ok: true, spec, result: r || null }
|
|
183
|
+
} catch (e) {
|
|
184
|
+
log('一键更新失败:' + String((e && e.message) || e))
|
|
185
|
+
return { ok: false, spec, error: String((e && e.message) || e) }
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
|
|
122
189
|
function register(ctx, route) {
|
|
190
|
+
const send = (res, code, obj) => {
|
|
191
|
+
res.writeHead(code, { 'content-type': 'application/json; charset=utf-8', 'cache-control': 'no-store' })
|
|
192
|
+
res.end(JSON.stringify(obj))
|
|
193
|
+
}
|
|
123
194
|
try {
|
|
124
195
|
ctx.effect(() => ctx.webServer.register({
|
|
125
196
|
kind: 'exact',
|
|
126
197
|
path: route + '/update',
|
|
127
198
|
handler: async (req, res) => {
|
|
128
|
-
const send = (code, obj) => {
|
|
129
|
-
res.writeHead(code, {
|
|
130
|
-
'content-type': 'application/json; charset=utf-8',
|
|
131
|
-
'cache-control': 'no-store',
|
|
132
|
-
})
|
|
133
|
-
res.end(JSON.stringify(obj))
|
|
134
|
-
}
|
|
135
199
|
try {
|
|
136
200
|
const url = new URL(req.url, 'http://127.0.0.1')
|
|
137
201
|
const force = url.searchParams.get('refresh') === '1'
|
|
138
|
-
send(200, { ok: true, ...(await check(force)) })
|
|
202
|
+
send(res, 200, { ok: true, ...(await check(force)) })
|
|
139
203
|
} catch (e) {
|
|
140
204
|
log('查更新路由失败:' + (e && e.message))
|
|
141
|
-
send(200, { ok: false, current, latest: null, error: String((e && e.message) || e) })
|
|
205
|
+
send(res, 200, { ok: false, current, latest: null, error: String((e && e.message) || e) })
|
|
206
|
+
}
|
|
207
|
+
},
|
|
208
|
+
}))
|
|
209
|
+
// ⭐ 一键更新:POST。装完的结果原样带回去(可能要重启才生效)
|
|
210
|
+
ctx.effect(() => ctx.webServer.register({
|
|
211
|
+
kind: 'exact',
|
|
212
|
+
path: route + '/install',
|
|
213
|
+
handler: async (req, res) => {
|
|
214
|
+
if (req.method !== 'POST') { send(res, 405, { ok: false, error: '只接受 POST' }); return }
|
|
215
|
+
try {
|
|
216
|
+
const r = await install(ctx)
|
|
217
|
+
send(res, 200, r)
|
|
218
|
+
} catch (e) {
|
|
219
|
+
log('一键更新路由失败:' + (e && e.message))
|
|
220
|
+
send(res, 200, { ok: false, error: String((e && e.message) || e) })
|
|
142
221
|
}
|
|
143
222
|
},
|
|
144
223
|
}))
|
|
@@ -147,5 +226,5 @@ export function createSelfUpdate({ packageRoot, diag }) {
|
|
|
147
226
|
}
|
|
148
227
|
}
|
|
149
228
|
|
|
150
|
-
return { current, repo, repoUrl, check, register }
|
|
229
|
+
return { current, repo, repoUrl, check, install, register }
|
|
151
230
|
}
|
package/package.json
CHANGED
|
@@ -1,58 +1,58 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "dsh-feiyu-mood",
|
|
3
|
-
"version": "0.8.
|
|
4
|
-
"description": "给 AI 伙伴一个会自己判断、你看得见的心情:每轮聊完自动判断涨跌,右下角挂饰点开看数值 + 情绪 + 时间线;心情怎么告诉她(不告诉 / 只说心情 / 说她刚才那件事)和可信度门槛都能在设置里改。",
|
|
5
|
-
"license": "
|
|
6
|
-
"author": "Yuuyuko-uu (https://github.com/Yuuyuko-uu)",
|
|
7
|
-
"type": "module",
|
|
8
|
-
"scripts": {
|
|
9
|
-
"test": "node --test"
|
|
10
|
-
},
|
|
11
|
-
"keywords": [
|
|
12
|
-
"deepseek-harness",
|
|
13
|
-
"dsh-plugin",
|
|
14
|
-
"mood",
|
|
15
|
-
"ai-companion"
|
|
16
|
-
],
|
|
17
|
-
"repository": {
|
|
18
|
-
"type": "git",
|
|
19
|
-
"url": "git+https://github.com/Yuuyuko-uu/dsh-feiyu-mood.git"
|
|
20
|
-
},
|
|
21
|
-
"homepage": "https://github.com/Yuuyuko-uu/dsh-feiyu-mood#readme",
|
|
22
|
-
"bugs": {
|
|
23
|
-
"url": "https://github.com/Yuuyuko-uu/dsh-feiyu-mood/issues"
|
|
24
|
-
},
|
|
25
|
-
"main": "lib/index.js",
|
|
26
|
-
"exports": {
|
|
27
|
-
".": "./lib/index.js",
|
|
28
|
-
"./client": "./client.js",
|
|
29
|
-
"./cordis.patch.yml": "./cordis.patch.yml",
|
|
30
|
-
"./package.json": "./package.json"
|
|
31
|
-
},
|
|
32
|
-
"files": [
|
|
33
|
-
"lib",
|
|
34
|
-
"assets",
|
|
35
|
-
"client.js",
|
|
36
|
-
"cordis.patch.yml",
|
|
37
|
-
"README.md",
|
|
38
|
-
"CHANGELOG.md",
|
|
39
|
-
"LICENSE"
|
|
40
|
-
],
|
|
41
|
-
"dsh": {
|
|
42
|
-
"bundle": {
|
|
43
|
-
"patch": "./cordis.patch.yml"
|
|
44
|
-
},
|
|
45
|
-
"client": {
|
|
46
|
-
"platform": "web",
|
|
47
|
-
"inject": [
|
|
48
|
-
"@deepseek-ai/dsh-client-runtime",
|
|
49
|
-
"@deepseek-ai/dsh-client-ui-settings",
|
|
50
|
-
"@deepseek-ai/dsh-client-ui-settings-plugins"
|
|
51
|
-
]
|
|
52
|
-
}
|
|
53
|
-
},
|
|
54
|
-
"publishConfig": {
|
|
55
|
-
"access": "public",
|
|
56
|
-
"registry": "https://registry.npmjs.org/"
|
|
57
|
-
}
|
|
58
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "dsh-feiyu-mood",
|
|
3
|
+
"version": "0.8.4",
|
|
4
|
+
"description": "给 AI 伙伴一个会自己判断、你看得见的心情:每轮聊完自动判断涨跌,右下角挂饰点开看数值 + 情绪 + 时间线;心情怎么告诉她(不告诉 / 只说心情 / 说她刚才那件事)和可信度门槛都能在设置里改。",
|
|
5
|
+
"license": "SEE LICENSE IN LICENSE",
|
|
6
|
+
"author": "Yuuyuko-uu (https://github.com/Yuuyuko-uu)",
|
|
7
|
+
"type": "module",
|
|
8
|
+
"scripts": {
|
|
9
|
+
"test": "node --test"
|
|
10
|
+
},
|
|
11
|
+
"keywords": [
|
|
12
|
+
"deepseek-harness",
|
|
13
|
+
"dsh-plugin",
|
|
14
|
+
"mood",
|
|
15
|
+
"ai-companion"
|
|
16
|
+
],
|
|
17
|
+
"repository": {
|
|
18
|
+
"type": "git",
|
|
19
|
+
"url": "git+https://github.com/Yuuyuko-uu/dsh-feiyu-mood.git"
|
|
20
|
+
},
|
|
21
|
+
"homepage": "https://github.com/Yuuyuko-uu/dsh-feiyu-mood#readme",
|
|
22
|
+
"bugs": {
|
|
23
|
+
"url": "https://github.com/Yuuyuko-uu/dsh-feiyu-mood/issues"
|
|
24
|
+
},
|
|
25
|
+
"main": "lib/index.js",
|
|
26
|
+
"exports": {
|
|
27
|
+
".": "./lib/index.js",
|
|
28
|
+
"./client": "./client.js",
|
|
29
|
+
"./cordis.patch.yml": "./cordis.patch.yml",
|
|
30
|
+
"./package.json": "./package.json"
|
|
31
|
+
},
|
|
32
|
+
"files": [
|
|
33
|
+
"lib",
|
|
34
|
+
"assets",
|
|
35
|
+
"client.js",
|
|
36
|
+
"cordis.patch.yml",
|
|
37
|
+
"README.md",
|
|
38
|
+
"CHANGELOG.md",
|
|
39
|
+
"LICENSE"
|
|
40
|
+
],
|
|
41
|
+
"dsh": {
|
|
42
|
+
"bundle": {
|
|
43
|
+
"patch": "./cordis.patch.yml"
|
|
44
|
+
},
|
|
45
|
+
"client": {
|
|
46
|
+
"platform": "web",
|
|
47
|
+
"inject": [
|
|
48
|
+
"@deepseek-ai/dsh-client-runtime",
|
|
49
|
+
"@deepseek-ai/dsh-client-ui-settings",
|
|
50
|
+
"@deepseek-ai/dsh-client-ui-settings-plugins"
|
|
51
|
+
]
|
|
52
|
+
}
|
|
53
|
+
},
|
|
54
|
+
"publishConfig": {
|
|
55
|
+
"access": "public",
|
|
56
|
+
"registry": "https://registry.npmjs.org/"
|
|
57
|
+
}
|
|
58
|
+
}
|