@wendongfly/myhi 1.3.81 → 1.3.85

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/index.html CHANGED
@@ -418,6 +418,7 @@
418
418
  <div class="logo">my<span>hi</span></div>
419
419
  <div style="display:flex;align-items:center;gap:0.6rem">
420
420
  <span id="user-name" style="font-size:0.8rem;color:var(--muted)"></span>
421
+ <button id="doctor-btn" onclick="openDoctor()" style="background:none;border:1px solid var(--border);color:var(--muted);font-size:0.75rem;padding:0.3rem 0.6rem;border-radius:6px;cursor:pointer;width:auto">🩺 体检</button>
421
422
  <button id="usage-btn" onclick="showUsage()" style="background:none;border:1px solid var(--border);color:var(--muted);font-size:0.75rem;padding:0.3rem 0.6rem;border-radius:6px;cursor:pointer;width:auto">用量</button>
422
423
  <button id="logout-btn" onclick="doLogout()" style="display:none;background:none;border:1px solid var(--border);color:var(--muted);font-size:0.75rem;padding:0.3rem 0.6rem;border-radius:6px;cursor:pointer;width:auto">退出</button>
423
424
  <div id="conn-dot"></div>
@@ -495,6 +496,20 @@
495
496
  </div>
496
497
  </div>
497
498
 
499
+ <!-- 会话体检 sheet -->
500
+ <div id="doctor-menu" style="position:fixed;inset:0;z-index:60;display:none">
501
+ <div onclick="closeDoctor()" style="position:absolute;inset:0;background:rgba(0,0,0,0.55)"></div>
502
+ <div style="position:absolute;left:0;right:0;bottom:0;background:var(--surface);border-radius:20px 20px 0 0;width:100%;max-width:640px;margin:0 auto;padding:0 1.25rem 1.25rem;max-height:80vh;display:flex;flex-direction:column">
503
+ <div class="sheet-handle"></div>
504
+ <div style="display:flex;align-items:center;justify-content:space-between;margin-bottom:0.5rem">
505
+ <div class="sheet-title" style="margin:0">🩺 会话体检</div>
506
+ <button onclick="refreshDoctor()" style="background:none;border:1px solid var(--border);color:var(--muted);font-size:0.72rem;padding:0.25rem 0.55rem;border-radius:6px;cursor:pointer">刷新</button>
507
+ </div>
508
+ <div style="font-size:0.72rem;color:var(--muted);margin-bottom:0.5rem">「中毒」= 该会话恢复后实际非 bypass,写/删会被拦且无法运行时提权,需重开。</div>
509
+ <div id="doctor-list" style="flex:1;overflow-y:auto;scrollbar-width:thin"></div>
510
+ </div>
511
+ </div>
512
+
498
513
  <!-- 导入已有会话 -->
499
514
  <div id="import-menu">
500
515
  <div id="import-backdrop" onclick="closeImportSheet()" style="position:absolute;inset:0;background:rgba(0,0,0,0.55)"></div>
@@ -692,6 +707,79 @@
692
707
  window.location.href = `/terminal/${id}`;
693
708
  }
694
709
 
710
+ // ── 会话体检 ────────────────────────────────────────────────
711
+ window.openDoctor = function() {
712
+ document.getElementById('doctor-menu').style.display = 'block';
713
+ refreshDoctor();
714
+ };
715
+ window.closeDoctor = function() {
716
+ document.getElementById('doctor-menu').style.display = 'none';
717
+ };
718
+ window.refreshDoctor = async function() {
719
+ const box = document.getElementById('doctor-list');
720
+ box.innerHTML = '<div style="color:var(--muted);font-size:0.8rem;padding:1rem 0.2rem">加载中…</div>';
721
+ try {
722
+ const res = await fetch('/api/sessions/health', { credentials: 'same-origin' });
723
+ const data = await res.json();
724
+ renderDoctor(data.sessions || []);
725
+ } catch (e) {
726
+ box.innerHTML = '<div style="color:var(--red);font-size:0.8rem;padding:1rem 0.2rem">加载失败:' + esc(e.message) + '</div>';
727
+ }
728
+ };
729
+ function pill(text, color, bg) {
730
+ return `<span style="font-size:0.65rem;padding:0.1rem 0.4rem;border-radius:5px;color:${color};background:${bg};white-space:nowrap">${text}</span>`;
731
+ }
732
+ function renderDoctor(sessions) {
733
+ const box = document.getElementById('doctor-list');
734
+ if (!sessions.length) {
735
+ box.innerHTML = '<div style="color:var(--muted);font-size:0.8rem;padding:1rem 0.2rem">没有 agent 会话</div>';
736
+ return;
737
+ }
738
+ // 中毒的排前面
739
+ sessions.sort((a, b) => (b.health?.poisoned ? 1 : 0) - (a.health?.poisoned ? 1 : 0));
740
+ box.innerHTML = sessions.map(s => {
741
+ const h = s.health || {};
742
+ const poisoned = h.poisoned;
743
+ const badges = [];
744
+ if (poisoned) badges.push(pill('⚠ 中毒 · ' + esc(h.persistedMode || '非bypass'), '#fff', '#a11'));
745
+ else if (h.persistedMode) badges.push(pill(esc(h.persistedMode), 'var(--muted)', 'var(--bg)'));
746
+ badges.push(pill(s.alive ? '运行中' : '已停', s.alive ? '#3fb950' : 'var(--muted)', 'var(--bg)'));
747
+ if (s.busy) badges.push(pill('忙', '#f0883e', 'var(--bg)'));
748
+ if (h.transcriptKB) badges.push(pill(h.transcriptKB + 'KB', 'var(--muted)', 'var(--bg)'));
749
+ if (h.imageCount) badges.push(pill('🖼 ' + h.imageCount, 'var(--muted)', 'var(--bg)'));
750
+ const border = poisoned ? '#a11' : 'var(--border)';
751
+ return `
752
+ <div style="border:1px solid ${border};border-radius:10px;padding:0.6rem 0.7rem;margin-bottom:0.5rem">
753
+ <div style="display:flex;align-items:center;gap:0.4rem;flex-wrap:wrap;margin-bottom:0.35rem">
754
+ <span style="font-weight:600;font-size:0.85rem">${esc(s.title || '(无标题)')}</span>
755
+ ${badges.join('')}
756
+ </div>
757
+ <div style="font-size:0.68rem;color:var(--muted);margin-bottom:0.5rem;word-break:break-all">${esc(s.cwd || '')}</div>
758
+ <div style="display:flex;gap:0.4rem">
759
+ <button onclick="openSession('${esc(s.id)}')" style="flex:1;background:var(--bg);border:1px solid var(--border);color:var(--text);font-size:0.72rem;padding:0.35rem;border-radius:6px;cursor:pointer">打开</button>
760
+ <button onclick="doctorReset('${esc(s.id)}','${esc(s.title || '')}')" style="flex:1;background:${poisoned ? '#1c3a2b' : 'var(--bg)'};border:1px solid ${poisoned ? '#3fb950' : 'var(--border)'};color:${poisoned ? '#3fb950' : 'var(--text)'};font-size:0.72rem;padding:0.35rem;border-radius:6px;cursor:pointer">重开为干净会话</button>
761
+ <button onclick="doctorDelete('${esc(s.id)}','${esc(s.title || '')}')" style="background:#3d1515;border:none;color:var(--red);font-size:0.72rem;padding:0.35rem 0.6rem;border-radius:6px;cursor:pointer">删除</button>
762
+ </div>
763
+ </div>`;
764
+ }).join('');
765
+ }
766
+ window.doctorReset = async function(id, title) {
767
+ if (!confirm(`重开「${title}」为干净会话?\n会断开旧对话上下文(磁盘上的文件不受影响),下次提问开启全新会话。`)) return;
768
+ try {
769
+ const res = await fetch(`/api/sessions/${id}/reset`, { method: 'POST', credentials: 'same-origin' });
770
+ if (!res.ok) throw new Error((await res.json().catch(() => ({}))).error || res.status);
771
+ refreshDoctor();
772
+ } catch (e) { alert('重开失败:' + e.message); }
773
+ };
774
+ window.doctorDelete = async function(id, title) {
775
+ if (!confirm(`删除会话「${title}」?此操作不可恢复。`)) return;
776
+ try {
777
+ const res = await fetch(`/api/sessions/${id}`, { method: 'DELETE', credentials: 'same-origin' });
778
+ if (!res.ok && res.status !== 204) throw new Error(res.status);
779
+ refreshDoctor();
780
+ } catch (e) { alert('删除失败:' + e.message); }
781
+ };
782
+
695
783
  // ── Presets ───────────────────────────────────────────────────
696
784
  const PRESETS = [
697
785
  // Agent 模式