dsh-stall-guard 0.1.0 → 0.1.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/README.md +7 -3
- package/client.js +57 -12
- package/lib/config.js +7 -0
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,31 @@
|
|
|
5
5
|
格式参考 [Keep a Changelog](https://keepachangelog.com/zh-CN/1.1.0/),
|
|
6
6
|
版本号遵循 [语义化版本](https://semver.org/lang/zh-CN/)。
|
|
7
7
|
|
|
8
|
+
## [0.1.2] - 2026-10-21
|
|
9
|
+
|
|
10
|
+
### 修复
|
|
11
|
+
|
|
12
|
+
- **点气泡会打开面板**:0.1.1 里气泡的点击改成了「关闭提示」(`acknowledge`),
|
|
13
|
+
但这个改动晚于 0.1.1 的发布,所以 npm 上那版仍是旧的。本版包含该修复。
|
|
14
|
+
现在点气泡 = 关掉通知,点鱼图标 = 打开设置。
|
|
15
|
+
|
|
16
|
+
## [0.1.1] - 2026-10-21
|
|
17
|
+
|
|
18
|
+
### 新增
|
|
19
|
+
|
|
20
|
+
- **气泡文字可关闭**:`supervisor.bubble`。关掉后图标仍会跳动提示有异常,但不显示文字。
|
|
21
|
+
旧配置文件没有这个键,默认显示气泡。
|
|
22
|
+
|
|
23
|
+
### 修复
|
|
24
|
+
|
|
25
|
+
- **点气泡打不开面板**:气泡原先在 `<button>` 内部且 `pointer-events:none`,
|
|
26
|
+
点击会穿透到背后的标题栏(表现为点出「下载 Session 日志」菜单)。
|
|
27
|
+
现在气泡是按钮的兄弟元素,有自己的点击与键盘激活。
|
|
28
|
+
- **气泡被挤成竖排**:宽度依赖内容,在窄的标题栏槽里被压成每行 3–4 字。
|
|
29
|
+
改为固定 200px(视口更窄时收窄),最多 4 行后截断。
|
|
30
|
+
- **过时提示文案**:`scenesHint` 仍写着「能自己决定要不要用模型写提示」,
|
|
31
|
+
但模型功能已删除。改为只描述超时。
|
|
32
|
+
|
|
8
33
|
## [0.1.0] - 2026-10-20
|
|
9
34
|
|
|
10
35
|
首个版本。核心用途:**下载卡住时自动掐掉,并把换源提示写给智能体看**。
|
package/README.md
CHANGED
|
@@ -128,8 +128,12 @@ $env:DSH_PROFILE # 桌面版一般是 desktop
|
|
|
128
128
|
|
|
129
129
|
### 显示开关
|
|
130
130
|
|
|
131
|
-
|
|
132
|
-
|
|
131
|
+
| 开关 | 作用 |
|
|
132
|
+
|---|---|
|
|
133
|
+
| 显示监督员 | 关掉后不再画形象,但**守卫照常工作**。右下角留一个半透明小点,点它开回面板 |
|
|
134
|
+
| 卡顿时显示文字气泡 | 关掉仍会**跳动**提示有异常,但不显示那句文字 |
|
|
135
|
+
|
|
136
|
+
点图标或点气泡都能打开面板。
|
|
133
137
|
|
|
134
138
|
### 图标外观(仅本浏览器)
|
|
135
139
|
|
|
@@ -258,7 +262,7 @@ $env:DSH_PROFILE # 桌面版一般是 desktop
|
|
|
258
262
|
## 开发
|
|
259
263
|
|
|
260
264
|
```bash
|
|
261
|
-
node --test test/*.test.mjs #
|
|
265
|
+
node --test test/*.test.mjs # 182 个用例
|
|
262
266
|
```
|
|
263
267
|
|
|
264
268
|
## 许可
|
package/client.js
CHANGED
|
@@ -189,7 +189,7 @@ window.__ModuleLoader__.load({
|
|
|
189
189
|
autoSwitch: 'Auto-switch fetch/search on timeout',
|
|
190
190
|
sectionPresets: 'Preset text (sent to the agent)',
|
|
191
191
|
sectionScenes: 'Each feature, configured separately',
|
|
192
|
-
scenesHint: 'Turn one off without affecting the others. Each has its own timeout
|
|
192
|
+
scenesHint: 'Turn one off without affecting the others. Each has its own timeout.',
|
|
193
193
|
sceneSeconds: 'Timeout (s)',
|
|
194
194
|
presetsHint: 'Write the exact text the agent reads when a stall is reported. Placeholders are filled in automatically; an empty box uses the built-in wording.',
|
|
195
195
|
presetSceneDownload: 'Download',
|
|
@@ -207,6 +207,9 @@ window.__ModuleLoader__.load({
|
|
|
207
207
|
sectionSupervisor: 'Supervisor',
|
|
208
208
|
showSupervisor: 'Show the supervisor',
|
|
209
209
|
showSupervisorHint: 'Off hides the figure but keeps the guard running; a small tab stays in the corner so you can switch it back.',
|
|
210
|
+
showBubble: 'Show the text bubble on a stall',
|
|
211
|
+
showBubbleHint: 'Off keeps the bouncing animation but hides the sentence. Click the bubble to dismiss it; click the figure for settings.',
|
|
212
|
+
dismissBubble: 'Dismiss this notice',
|
|
210
213
|
supervisorHiddenHint: 'Supervisor hidden — click to open settings',
|
|
211
214
|
appearanceLabel: 'Icon appearance (this browser only)',
|
|
212
215
|
iconSize: 'Size',
|
|
@@ -279,7 +282,7 @@ window.__ModuleLoader__.load({
|
|
|
279
282
|
discardNo: '\u7ee7\u7eed\u7f16\u8f91',
|
|
280
283
|
sectionPresets: '\u9884\u8bbe\u6587\u5b57\uff08\u53d1\u7ed9\u667a\u80fd\u4f53\uff09',
|
|
281
284
|
sectionScenes: '\u6bcf\u4e2a\u529f\u80fd\u72ec\u7acb\u914d\u7f6e',
|
|
282
|
-
scenesHint: '\u5173\u6389\u4e00\u4e2a\u4e0d\u5f71\u54cd\u5176\u5b83\u3002\u6bcf\u4e2a\u7c7b\u578b\u6709\u81ea\u5df1\u7684\u8d85\u65f6\
|
|
285
|
+
scenesHint: '\u5173\u6389\u4e00\u4e2a\u4e0d\u5f71\u54cd\u5176\u5b83\u3002\u6bcf\u4e2a\u7c7b\u578b\u6709\u81ea\u5df1\u7684\u8d85\u65f6\u3002',
|
|
283
286
|
sceneSeconds: '\u8d85\u65f6\uff08\u79d2\uff09',
|
|
284
287
|
presetsHint: '\u76f4\u63a5\u5199\u4f60\u8981\u53d1\u7ed9\u667a\u80fd\u4f53\u7684\u6587\u5b57\uff0c\u5360\u4f4d\u7b26\u4f1a\u81ea\u52a8\u66ff\u6362\uff1b\u7a7a\u7740 = \u7528\u5185\u7f6e\u6587\u6848\u3002',
|
|
285
288
|
presetSceneDownload: '\u4e0b\u8f7d',
|
|
@@ -296,6 +299,9 @@ window.__ModuleLoader__.load({
|
|
|
296
299
|
downloadSeconds: '\u4e0b\u8f7d\u8d85\u65f6\uff08\u79d2\uff09',
|
|
297
300
|
showSupervisor: '\u663e\u793a\u76d1\u7763\u5458',
|
|
298
301
|
showSupervisorHint: '\u5173\u6389\u540e\u4e0d\u518d\u663e\u793a\u5f62\u8c61\uff0c\u4f46\u5b88\u536b\u4ecd\u5728\u8fd0\u884c\uff1b\u53f3\u4e0b\u89d2\u4f1a\u7559\u4e00\u4e2a\u5c0f\u70b9\uff0c\u70b9\u5b83\u53ef\u4ee5\u518d\u6253\u5f00\u3002',
|
|
302
|
+
showBubble: '\u5361\u987f\u65f6\u663e\u793a\u6587\u5b57\u6c14\u6ce1',
|
|
303
|
+
showBubbleHint: '\u5173\u6389\u4ecd\u4f1a\u8df3\u52a8\uff0c\u4f46\u4e0d\u663e\u793a\u6587\u5b57\u3002\u70b9\u6c14\u6ce1\u53ef\u4ee5\u5173\u6389\u5b83\uff1b\u70b9\u56fe\u6807\u6253\u5f00\u8bbe\u7f6e\u3002',
|
|
304
|
+
dismissBubble: '\u5173\u6389\u8fd9\u6761\u63d0\u793a',
|
|
299
305
|
supervisorHiddenHint: '\u76d1\u7763\u5458\u5df2\u9690\u85cf \u2014 \u70b9\u51fb\u6253\u5f00\u8bbe\u7f6e',
|
|
300
306
|
appearanceLabel: '\u56fe\u6807\u5916\u89c2\uff08\u4ec5\u672c\u6d4f\u89c8\u5668\uff09',
|
|
301
307
|
iconSize: '\u5927\u5c0f',
|
|
@@ -496,6 +502,7 @@ window.__ModuleLoader__.load({
|
|
|
496
502
|
presets: normalizePresets(body.presets),
|
|
497
503
|
supervisor: {
|
|
498
504
|
show: sup.show !== false,
|
|
505
|
+
bubble: sup.bubble !== false,
|
|
499
506
|
display: pick(sup.display, DISPLAYS, 'avatar'),
|
|
500
507
|
},
|
|
501
508
|
};
|
|
@@ -858,6 +865,7 @@ window.__ModuleLoader__.load({
|
|
|
858
865
|
},
|
|
859
866
|
supervisor: {
|
|
860
867
|
show: source.supervisor.show !== false,
|
|
868
|
+
bubble: source.supervisor.bubble !== false,
|
|
861
869
|
display: source.supervisor.display,
|
|
862
870
|
},
|
|
863
871
|
};
|
|
@@ -905,6 +913,7 @@ window.__ModuleLoader__.load({
|
|
|
905
913
|
},
|
|
906
914
|
supervisor: {
|
|
907
915
|
show: draft.supervisor.show !== false,
|
|
916
|
+
bubble: draft.supervisor.bubble !== false,
|
|
908
917
|
display: pick(draft.supervisor.display, DISPLAYS, 'avatar'),
|
|
909
918
|
},
|
|
910
919
|
};
|
|
@@ -920,7 +929,7 @@ window.__ModuleLoader__.load({
|
|
|
920
929
|
*/
|
|
921
930
|
const CSS = [
|
|
922
931
|
/* ---- supervisor seat ---- */
|
|
923
|
-
'.dsg-seat{position:relative;display:inline-flex;flex:none;align-items:center;justify-content:center;pointer-events:auto}',
|
|
932
|
+
'.dsg-seat{position:relative;display:inline-flex;flex:none;align-items:center;justify-content:center;pointer-events:auto;overflow:visible}',
|
|
924
933
|
'.dsg-orb{box-sizing:border-box;display:inline-flex;position:relative;align-items:center;justify-content:center;margin:0;padding:0;border:none;background:transparent;font-family:inherit;cursor:pointer;overflow:visible;border-radius:var(--dsw-radius-sm,6px);pointer-events:auto}',
|
|
925
934
|
'.dsg-orb:focus-visible{outline:2px solid var(--dsw-alias-brand-primary,#4d6bfe);outline-offset:2px}',
|
|
926
935
|
'.dsg-orb[data-display="dot"]{width:22px;height:22px}',
|
|
@@ -937,11 +946,14 @@ window.__ModuleLoader__.load({
|
|
|
937
946
|
'.dsg-orb[data-mode="idle"] .dsg-avatar{opacity:.72;animation:none}',
|
|
938
947
|
'.dsg-orb[data-mode="watching"] .dsg-avatar{animation:dsg-sway 3.2s ease-in-out infinite}',
|
|
939
948
|
'.dsg-orb[data-mode="alert"] .dsg-avatar{animation:dsg-bounce-big .85s ease-in-out infinite}',
|
|
940
|
-
'.dsg-bubble{position:absolute;right:0;bottom:calc(100% + 7px);z-index:5;box-sizing:border-box;
|
|
949
|
+
'.dsg-bubble{position:absolute;right:0;bottom:calc(100% + 7px);z-index:5;box-sizing:border-box;width:200px;max-width:calc(100vw - 40px);padding:4px 8px;border:1px solid var(--dsw-alias-border-l1,var(--dsh-border,rgba(255,255,255,.12)));border-radius:var(--dsw-radius-sm,8px);background:var(--dsw-alias-bg-overlay,var(--dsh-surface,rgba(20,22,28,.96)));box-shadow:var(--dsw-elevation-soft,0 4px 18px rgba(0,0,0,.2));color:var(--dsw-alias-label-primary,var(--dsh-text-primary,#e6e6e6));font-size:11px;line-height:15px;text-align:left;white-space:normal;overflow-wrap:break-word;word-break:normal;max-height:120px;overflow:hidden;pointer-events:auto;cursor:pointer;animation:dsg-bubble-in .16s ease-out both}',
|
|
941
950
|
/* The header sits at the very top of the frame, so a bubble above the head
|
|
942
951
|
* would be clipped by the viewport; the seat measures once and flips it
|
|
943
952
|
* below when there is no room. */
|
|
944
953
|
'.dsg-bubble[data-place="below"]{bottom:auto;top:calc(100% + 7px)}',
|
|
954
|
+
'.dsg-bubble:hover{border-color:var(--dsw-alias-brand-primary,#4d6bfe)}',
|
|
955
|
+
/* Hidden state: the bubble is not rendered at all, so there is nothing to dim. */
|
|
956
|
+
|
|
945
957
|
/* ---- overlay panel ---- */
|
|
946
958
|
'.dsg-panel{position:fixed;top:56px;right:14px;left:auto;z-index:70;box-sizing:border-box;width:min(430px,calc(100vw - 40px));max-height:min(74vh,660px);overflow:hidden;pointer-events:auto;display:flex;flex-direction:column;gap:0;padding:12px;border:1px solid var(--dsw-alias-border-l1,var(--dsh-border,rgba(255,255,255,.12)));border-radius:var(--dsw-radius-panel,12px);background:var(--dsw-alias-bg-overlay,var(--dsh-surface,rgba(20,22,28,.96)));box-shadow:var(--dsw-elevation-soft,0 8px 28px rgba(0,0,0,.22));color:var(--dsw-alias-label-primary,var(--dsh-text-primary,#e6e6e6));font:inherit;font-size:12px;line-height:18px}',
|
|
947
959
|
'.dsg-head{display:flex;align-items:center;gap:8px;flex-wrap:wrap;flex:0 0 auto;padding-bottom:8px}',
|
|
@@ -1207,7 +1219,8 @@ window.__ModuleLoader__.load({
|
|
|
1207
1219
|
const mode = snapshot.state === null ? 'idle' : snapshot.state.supervisor.mode;
|
|
1208
1220
|
const display = config === null ? 'avatar' : config.supervisor.display;
|
|
1209
1221
|
const message = snapshot.state === null ? '' : snapshot.state.supervisor.message;
|
|
1210
|
-
const
|
|
1222
|
+
const bubbleOn = config === null ? true : config.supervisor.bubble !== false;
|
|
1223
|
+
const showBubble = !supervisorOff && bubbleOn && display === 'avatar' && mode === 'alert' && message !== '';
|
|
1211
1224
|
|
|
1212
1225
|
/* The seat can live in the top bar or float anywhere; a bubble above the head
|
|
1213
1226
|
* would be cut off by the viewport when it sits near the top, so measure once
|
|
@@ -1297,14 +1310,34 @@ window.__ModuleLoader__.load({
|
|
|
1297
1310
|
display === 'dot' ? h('span', { className: 'dsg-dot' }) : null,
|
|
1298
1311
|
display === 'icon' ? h(ShieldGlyph, { size: Math.round(size * 0.64) }) : null,
|
|
1299
1312
|
display === 'avatar' ? h(WhaleGlyph, { size: Math.round(size * 0.82), mode }) : null,
|
|
1300
|
-
showBubble
|
|
1301
|
-
? h('span', {
|
|
1302
|
-
className: 'dsg-bubble',
|
|
1303
|
-
'data-place': bubbleBelow ? 'below' : 'above',
|
|
1304
|
-
role: 'status',
|
|
1305
|
-
}, message)
|
|
1306
|
-
: null,
|
|
1307
1313
|
),
|
|
1314
|
+
/* The bubble is a SIBLING of the button, not a child: interactive content inside a
|
|
1315
|
+
* button is invalid and browsers route its clicks unpredictably, and content painted
|
|
1316
|
+
* outside the button box is not part of its hit area. */
|
|
1317
|
+
showBubble
|
|
1318
|
+
? h('span', {
|
|
1319
|
+
className: 'dsg-bubble',
|
|
1320
|
+
'data-place': bubbleBelow ? 'below' : 'above',
|
|
1321
|
+
role: 'button',
|
|
1322
|
+
tabIndex: 0,
|
|
1323
|
+
title: t('dismissBubble'),
|
|
1324
|
+
'aria-label': t('dismissBubble'),
|
|
1325
|
+
/* Pressing the notice dismisses it; it does not navigate. Opening the settings
|
|
1326
|
+
* panel stays on the figure, so the two affordances cannot be confused. */
|
|
1327
|
+
onClick: (event) => {
|
|
1328
|
+
event.stopPropagation();
|
|
1329
|
+
event.preventDefault();
|
|
1330
|
+
void store.acknowledge();
|
|
1331
|
+
},
|
|
1332
|
+
onKeyDown: (event) => {
|
|
1333
|
+
if (event.key === 'Enter' || event.key === ' ') {
|
|
1334
|
+
event.preventDefault();
|
|
1335
|
+
event.stopPropagation();
|
|
1336
|
+
void store.acknowledge();
|
|
1337
|
+
}
|
|
1338
|
+
},
|
|
1339
|
+
}, message)
|
|
1340
|
+
: null,
|
|
1308
1341
|
);
|
|
1309
1342
|
}
|
|
1310
1343
|
|
|
@@ -1701,6 +1734,18 @@ window.__ModuleLoader__.load({
|
|
|
1701
1734
|
h('span', null, t('showSupervisor')),
|
|
1702
1735
|
),
|
|
1703
1736
|
h('p', { className: 'dsg-status' }, t('showSupervisorHint')),
|
|
1737
|
+
h('label', { className: 'dsg-check' },
|
|
1738
|
+
h('input', {
|
|
1739
|
+
type: 'checkbox',
|
|
1740
|
+
checked: draft.supervisor.bubble !== false,
|
|
1741
|
+
onChange: (event) => {
|
|
1742
|
+
const checked = event.target.checked;
|
|
1743
|
+
edit((next) => { next.supervisor = Object.assign({}, next.supervisor, { bubble: checked }); });
|
|
1744
|
+
},
|
|
1745
|
+
}),
|
|
1746
|
+
h('span', null, t('showBubble')),
|
|
1747
|
+
),
|
|
1748
|
+
h('p', { className: 'dsg-status' }, t('showBubbleHint')),
|
|
1704
1749
|
h('div', { className: 'dsg-label' }, t('displayLabel')),
|
|
1705
1750
|
h('div', { className: 'dsg-choices' },
|
|
1706
1751
|
DISPLAYS.map((value) => h('label', {
|
package/lib/config.js
CHANGED
|
@@ -48,6 +48,11 @@ export function defaults() {
|
|
|
48
48
|
supervisor: {
|
|
49
49
|
/** Whether the supervisor is drawn at all. Off still serves the API and the panel. */
|
|
50
50
|
show: true,
|
|
51
|
+
/**
|
|
52
|
+
* Whether the alert bubble shows its sentence. Off keeps the animation only, so a
|
|
53
|
+
* user who finds the text intrusive still sees that something is wrong.
|
|
54
|
+
*/
|
|
55
|
+
bubble: true,
|
|
51
56
|
display: DEFAULT_DISPLAY,
|
|
52
57
|
},
|
|
53
58
|
};
|
|
@@ -113,6 +118,7 @@ export function validateConfig(raw) {
|
|
|
113
118
|
const rawSupervisor = input.supervisor && typeof input.supervisor === 'object' ? input.supervisor : {};
|
|
114
119
|
|
|
115
120
|
const show = rawSupervisor.show === undefined ? base.supervisor.show : Boolean(rawSupervisor.show);
|
|
121
|
+
const bubble = rawSupervisor.bubble === undefined ? base.supervisor.bubble : Boolean(rawSupervisor.bubble);
|
|
116
122
|
|
|
117
123
|
let display = base.supervisor.display;
|
|
118
124
|
if (rawSupervisor.display !== undefined) {
|
|
@@ -142,6 +148,7 @@ export function validateConfig(raw) {
|
|
|
142
148
|
presets: presetResult.presets,
|
|
143
149
|
supervisor: {
|
|
144
150
|
show,
|
|
151
|
+
bubble,
|
|
145
152
|
display,
|
|
146
153
|
},
|
|
147
154
|
},
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-stall-guard",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2",
|
|
4
4
|
"description": "DSH 卡顿守卫:下载类命令(git clone / pnpm install / pip / curl)超时后自动中止,并把「换个下载源重试」的提示写进工具结果给智能体看,解决 GitHub 在国内网络不稳导致智能体一直干等的问题。另可按工具类型自定义超时秒数,并附一个只在卡顿时唤醒的 DeepSeek 监督员。",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"dsh",
|