@wendongfly/myhi 1.3.88 → 1.3.90

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/admin.html CHANGED
@@ -76,6 +76,17 @@
76
76
  </div>
77
77
  <div id="op-msg" class="status-msg" style="display:none"></div>
78
78
 
79
+ <!-- Claude Code 版本 + 升级 -->
80
+ <div class="ver-card">
81
+ <div style="font-size:0.8rem;color:#8b949e">Claude Code 版本</div>
82
+ <div class="ver-big" id="cl-cur">--</div>
83
+ <div class="ver-sub" id="cl-latest"></div>
84
+ </div>
85
+ <div class="actions">
86
+ <button class="btn-up" id="btn-cl-up" onclick="doClaudeUpgrade()" disabled>已是最新</button>
87
+ </div>
88
+ <div id="cl-msg" class="status-msg" style="display:none"></div>
89
+
79
90
  <!-- 在线用户 -->
80
91
  <div class="card">
81
92
  <h3>在线用户</h3>
@@ -219,6 +230,26 @@ async function refresh() {
219
230
  btn.disabled = true; btn.classList.remove('glow'); btn.textContent = '已是最新';
220
231
  }
221
232
 
233
+ // Claude Code 版本(独立端点)
234
+ try {
235
+ const cr = await fetch('/api/claude/version', { headers: H() });
236
+ if (cr.ok) {
237
+ const cd = await cr.json();
238
+ document.getElementById('cl-cur').textContent = cd.current ? ('v' + cd.current) : '未检测到';
239
+ const clBtn = document.getElementById('btn-cl-up');
240
+ if (cd.upgrade?.running) {
241
+ document.getElementById('cl-latest').innerHTML = '<span style="color:#58a6ff">升级中...</span>';
242
+ clBtn.disabled = true; clBtn.textContent = '升级中...';
243
+ } else if (cd.updateAvailable) {
244
+ document.getElementById('cl-latest').innerHTML = '最新版本: <b>v' + cd.latest + '</b>';
245
+ clBtn.disabled = false; clBtn.classList.add('glow'); clBtn.textContent = '升级到 v' + cd.latest;
246
+ } else {
247
+ document.getElementById('cl-latest').innerHTML = '<span style="color:#3fb950">已是最新版本</span>';
248
+ clBtn.disabled = true; clBtn.classList.remove('glow'); clBtn.textContent = '已是最新';
249
+ }
250
+ }
251
+ } catch {}
252
+
222
253
  // 在线用户
223
254
  const ouEl = document.getElementById('online-users');
224
255
  if (!d.onlineUsers?.length) {
@@ -327,6 +358,28 @@ async function doUpgrade() {
327
358
  } catch { msg.textContent = '连接失败'; btn.disabled = false; btn.textContent = '重试'; }
328
359
  }
329
360
 
361
+ async function doClaudeUpgrade() {
362
+ const btn = document.getElementById('btn-cl-up'), msg = document.getElementById('cl-msg');
363
+ if (!confirm('升级 Claude Code?\nmyhi 会用 npm --include=optional 装全平台二进制,避免占位符坑。\n不影响正在跑的会话,升级后新查询生效。')) return;
364
+ btn.disabled = true; btn.textContent = '升级中...'; msg.className = 'status-msg'; msg.textContent = '正在下载安装(~260MB,可能几分钟)...'; msg.style.display = '';
365
+ try {
366
+ const r = await fetch('/api/claude/update', { method: 'POST', headers: H() });
367
+ const d = await r.json();
368
+ if (!d.ok) { msg.textContent = '失败: ' + (d.error||''); btn.disabled = false; btn.textContent = '重试'; return; }
369
+ const poll = setInterval(async () => {
370
+ try {
371
+ const sr = await fetch('/api/claude/update-status', { headers: H() });
372
+ const s = await sr.json();
373
+ if (!s.running) {
374
+ clearInterval(poll);
375
+ if (s.ok) { msg.textContent = '已升级到 v' + s.version; setTimeout(refresh, 500); }
376
+ else { msg.textContent = '失败: ' + (s.error||''); btn.disabled = false; btn.textContent = '重试'; }
377
+ }
378
+ } catch {}
379
+ }, 4000);
380
+ } catch { msg.textContent = '连接失败'; btn.disabled = false; btn.textContent = '重试'; }
381
+ }
382
+
330
383
  async function doRestart() {
331
384
  if (!confirm('确定重启?')) return;
332
385
  const msg = document.getElementById('op-msg');
package/dist/chat.html CHANGED
@@ -291,6 +291,7 @@
291
291
  <span id="control-badge" class="readonly" style="display:none">只读</span>
292
292
  <span id="viewer-count" class="sb-item"></span>
293
293
  <span id="update-badge" style="display:none;color:#d29922;cursor:pointer" onclick="doUpdate()">⬆ 有更新</span>
294
+ <span id="claude-update-badge" style="display:none;color:#58a6ff;cursor:pointer" onclick="doClaudeUpdate()">⬆ claude</span>
294
295
  </div>
295
296
 
296
297
  <div id="chat-area"></div>
@@ -885,10 +886,22 @@
885
886
  scrollToBottom();
886
887
  }
887
888
 
889
+ // 连续重复的同一条状态不再一行行堆,折叠成「— xxx — ×N」。
890
+ // 任何会反复触发的状态(自动重启、上传失败、控制权变更…)都不会再刷屏。
888
891
  function addStatusMessage(text) {
889
892
  endStream(); endToolGroup();
893
+ const last = chatArea.lastElementChild;
894
+ if (last && last.classList.contains('msg-status') && last.dataset.statusText === text) {
895
+ const n = (parseInt(last.dataset.statusCount, 10) || 1) + 1;
896
+ last.dataset.statusCount = String(n);
897
+ last.textContent = '— ' + text + ' — ×' + n;
898
+ scrollToBottom();
899
+ return;
900
+ }
890
901
  const msg = document.createElement('div');
891
902
  msg.className = 'msg msg-status';
903
+ msg.dataset.statusText = text;
904
+ msg.dataset.statusCount = '1';
892
905
  msg.textContent = '— ' + text + ' —';
893
906
  chatArea.appendChild(msg);
894
907
  trimMessages();
@@ -1496,11 +1509,17 @@
1496
1509
  socket.on('error', ({ message }) => { showOverlay('错误: ' + message, true); });
1497
1510
 
1498
1511
  // 控制权事件
1512
+ // 只有「别人抢走了」才值得往聊天区写一行。自己拿到 / 自己释放属于日常动作,
1513
+ // 右上角徽标(控制中 / 获取控制)已经把状态说清楚了,再刷消息就是噪音——
1514
+ // 之前正是这两条把长会话刷成了满屏「你已获取控制权 / 控制权已释放」。
1515
+ let lastControlHolder = null;
1499
1516
  socket.on('control-changed', ({ sessionId, holder, holderName }) => {
1500
1517
  if (sessionId !== SESSION_ID) return;
1501
1518
  updateControlUI(holder === socket.id, holderName);
1502
- if (holder) addStatusMessage(holder === socket.id ? '你已获取控制权' : `${holderName || '其他用户'} 获取了控制权`);
1503
- else addStatusMessage('控制权已释放');
1519
+ if (holder && holder !== socket.id && holder !== lastControlHolder) {
1520
+ addStatusMessage(`${holderName || '其他用户'} 获取了控制权`);
1521
+ }
1522
+ lastControlHolder = holder || null;
1504
1523
  });
1505
1524
  socket.on('control-denied', ({ reason }) => addStatusMessage('无法获取控制: ' + reason));
1506
1525
  socket.on('kicked', ({ reason }) => { addStatusMessage(reason || '你已被管理员踢出'); setTimeout(() => location.href = '/', 2000); });
@@ -3541,6 +3560,53 @@
3541
3560
  badge.style.color = '#d29922';
3542
3561
  }
3543
3562
  };
3563
+
3564
+ // ── Claude Code 版本检查 & 升级 ──────────────────────
3565
+ let _claudeInfo = null;
3566
+ (async function checkClaudeUpdate() {
3567
+ try {
3568
+ const resp = await fetch('/api/claude/version');
3569
+ if (resp.ok) {
3570
+ _claudeInfo = await resp.json();
3571
+ const b = document.getElementById('claude-update-badge');
3572
+ if (_claudeInfo.upgrade?.running) {
3573
+ b.textContent = '⬆ claude 升级中...'; b.style.color = '#8b949e'; b.style.display = '';
3574
+ } else if (_claudeInfo.updateAvailable) {
3575
+ b.textContent = `⬆ claude ${_claudeInfo.latest}`; b.style.color = '#58a6ff'; b.style.display = '';
3576
+ } else {
3577
+ b.style.display = 'none';
3578
+ }
3579
+ }
3580
+ } catch {}
3581
+ setTimeout(checkClaudeUpdate, 10 * 60 * 1000);
3582
+ })();
3583
+
3584
+ window.doClaudeUpdate = async function() {
3585
+ if (!_claudeInfo?.updateAvailable) return;
3586
+ if (!confirm(`升级 Claude Code?\n当前: ${_claudeInfo.current}\n最新: ${_claudeInfo.latest}\n\nmyhi 会用 npm --include=optional 装全平台二进制,避免占位符坑。不影响正在跑的会话,升级后新查询生效。`)) return;
3587
+ const b = document.getElementById('claude-update-badge');
3588
+ b.textContent = '⬆ claude 升级中...'; b.style.color = '#8b949e';
3589
+ try {
3590
+ const resp = await fetch('/api/claude/update', { method: 'POST' });
3591
+ const data = await resp.json();
3592
+ if (!data.ok) { addStatusMessage('claude 升级失败:' + (data.error || '未知')); b.textContent = '⬆ claude 失败'; b.style.color = '#f85149'; return; }
3593
+ addStatusMessage(data.message || 'claude 升级中...');
3594
+ const poll = setInterval(async () => {
3595
+ try {
3596
+ const r = await fetch('/api/claude/update-status');
3597
+ const s = await r.json();
3598
+ if (!s.running) {
3599
+ clearInterval(poll);
3600
+ if (s.ok) { addStatusMessage('Claude Code 已升级到 ' + s.version); b.style.display = 'none'; }
3601
+ else { addStatusMessage('claude 升级失败:\n' + (s.error || '未知')); b.textContent = '⬆ claude 失败'; b.style.color = '#f85149'; }
3602
+ }
3603
+ } catch {}
3604
+ }, 4000);
3605
+ } catch (e) {
3606
+ addStatusMessage('claude 升级失败: ' + e.message);
3607
+ b.textContent = `⬆ claude ${_claudeInfo.latest}`; b.style.color = '#58a6ff';
3608
+ }
3609
+ };
3544
3610
  </script>
3545
3611
  </body>
3546
3612
  </html>