@wendongfly/zihi 1.0.0

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.
Files changed (145) hide show
  1. package/bin/daemon.js +23 -0
  2. package/bin/zihi.js +603 -0
  3. package/dist/admin.html +297 -0
  4. package/dist/attach.js +2 -0
  5. package/dist/chat.html +2254 -0
  6. package/dist/client-dist/socket.io.esm.min.js +7 -0
  7. package/dist/client-dist/socket.io.js +4955 -0
  8. package/dist/client-dist/socket.io.min.js +7 -0
  9. package/dist/client-dist/socket.io.msgpack.min.js +7 -0
  10. package/dist/files.html +722 -0
  11. package/dist/icon.png +0 -0
  12. package/dist/icon.svg +4 -0
  13. package/dist/index.html +976 -0
  14. package/dist/index.js +485 -0
  15. package/dist/lib/ansi_up.js +431 -0
  16. package/dist/lib/xterm/LICENSE +21 -0
  17. package/dist/lib/xterm/README.md +230 -0
  18. package/dist/lib/xterm/css/xterm.css +209 -0
  19. package/dist/lib/xterm/lib/xterm.js +2 -0
  20. package/dist/lib/xterm/lib/xterm.js.map +1 -0
  21. package/dist/lib/xterm/package.json +100 -0
  22. package/dist/lib/xterm/src/browser/AccessibilityManager.ts +300 -0
  23. package/dist/lib/xterm/src/browser/Clipboard.ts +93 -0
  24. package/dist/lib/xterm/src/browser/ColorContrastCache.ts +34 -0
  25. package/dist/lib/xterm/src/browser/Lifecycle.ts +33 -0
  26. package/dist/lib/xterm/src/browser/Linkifier2.ts +416 -0
  27. package/dist/lib/xterm/src/browser/LocalizableStrings.ts +12 -0
  28. package/dist/lib/xterm/src/browser/OscLinkProvider.ts +128 -0
  29. package/dist/lib/xterm/src/browser/RenderDebouncer.ts +83 -0
  30. package/dist/lib/xterm/src/browser/ScreenDprMonitor.ts +72 -0
  31. package/dist/lib/xterm/src/browser/Terminal.ts +1305 -0
  32. package/dist/lib/xterm/src/browser/TimeBasedDebouncer.ts +86 -0
  33. package/dist/lib/xterm/src/browser/Types.d.ts +181 -0
  34. package/dist/lib/xterm/src/browser/Viewport.ts +401 -0
  35. package/dist/lib/xterm/src/browser/decorations/BufferDecorationRenderer.ts +134 -0
  36. package/dist/lib/xterm/src/browser/decorations/ColorZoneStore.ts +117 -0
  37. package/dist/lib/xterm/src/browser/decorations/OverviewRulerRenderer.ts +219 -0
  38. package/dist/lib/xterm/src/browser/input/CompositionHelper.ts +246 -0
  39. package/dist/lib/xterm/src/browser/input/Mouse.ts +54 -0
  40. package/dist/lib/xterm/src/browser/input/MoveToCell.ts +249 -0
  41. package/dist/lib/xterm/src/browser/public/Terminal.ts +260 -0
  42. package/dist/lib/xterm/src/browser/renderer/dom/DomRenderer.ts +506 -0
  43. package/dist/lib/xterm/src/browser/renderer/dom/DomRendererRowFactory.ts +522 -0
  44. package/dist/lib/xterm/src/browser/renderer/dom/WidthCache.ts +157 -0
  45. package/dist/lib/xterm/src/browser/renderer/shared/CellColorResolver.ts +137 -0
  46. package/dist/lib/xterm/src/browser/renderer/shared/CharAtlasCache.ts +96 -0
  47. package/dist/lib/xterm/src/browser/renderer/shared/CharAtlasUtils.ts +75 -0
  48. package/dist/lib/xterm/src/browser/renderer/shared/Constants.ts +14 -0
  49. package/dist/lib/xterm/src/browser/renderer/shared/CursorBlinkStateManager.ts +146 -0
  50. package/dist/lib/xterm/src/browser/renderer/shared/CustomGlyphs.ts +687 -0
  51. package/dist/lib/xterm/src/browser/renderer/shared/DevicePixelObserver.ts +41 -0
  52. package/dist/lib/xterm/src/browser/renderer/shared/README.md +1 -0
  53. package/dist/lib/xterm/src/browser/renderer/shared/RendererUtils.ts +58 -0
  54. package/dist/lib/xterm/src/browser/renderer/shared/SelectionRenderModel.ts +91 -0
  55. package/dist/lib/xterm/src/browser/renderer/shared/TextureAtlas.ts +1082 -0
  56. package/dist/lib/xterm/src/browser/renderer/shared/Types.d.ts +173 -0
  57. package/dist/lib/xterm/src/browser/selection/SelectionModel.ts +144 -0
  58. package/dist/lib/xterm/src/browser/selection/Types.d.ts +15 -0
  59. package/dist/lib/xterm/src/browser/services/CharSizeService.ts +102 -0
  60. package/dist/lib/xterm/src/browser/services/CharacterJoinerService.ts +339 -0
  61. package/dist/lib/xterm/src/browser/services/CoreBrowserService.ts +33 -0
  62. package/dist/lib/xterm/src/browser/services/MouseService.ts +46 -0
  63. package/dist/lib/xterm/src/browser/services/RenderService.ts +284 -0
  64. package/dist/lib/xterm/src/browser/services/SelectionService.ts +1029 -0
  65. package/dist/lib/xterm/src/browser/services/Services.ts +138 -0
  66. package/dist/lib/xterm/src/browser/services/ThemeService.ts +237 -0
  67. package/dist/lib/xterm/src/common/CircularList.ts +241 -0
  68. package/dist/lib/xterm/src/common/Clone.ts +23 -0
  69. package/dist/lib/xterm/src/common/Color.ts +356 -0
  70. package/dist/lib/xterm/src/common/CoreTerminal.ts +284 -0
  71. package/dist/lib/xterm/src/common/EventEmitter.ts +73 -0
  72. package/dist/lib/xterm/src/common/InputHandler.ts +3443 -0
  73. package/dist/lib/xterm/src/common/Lifecycle.ts +108 -0
  74. package/dist/lib/xterm/src/common/MultiKeyMap.ts +42 -0
  75. package/dist/lib/xterm/src/common/Platform.ts +43 -0
  76. package/dist/lib/xterm/src/common/SortedList.ts +118 -0
  77. package/dist/lib/xterm/src/common/TaskQueue.ts +166 -0
  78. package/dist/lib/xterm/src/common/TypedArrayUtils.ts +17 -0
  79. package/dist/lib/xterm/src/common/Types.d.ts +553 -0
  80. package/dist/lib/xterm/src/common/WindowsMode.ts +27 -0
  81. package/dist/lib/xterm/src/common/buffer/AttributeData.ts +196 -0
  82. package/dist/lib/xterm/src/common/buffer/Buffer.ts +654 -0
  83. package/dist/lib/xterm/src/common/buffer/BufferLine.ts +520 -0
  84. package/dist/lib/xterm/src/common/buffer/BufferRange.ts +13 -0
  85. package/dist/lib/xterm/src/common/buffer/BufferReflow.ts +223 -0
  86. package/dist/lib/xterm/src/common/buffer/BufferSet.ts +134 -0
  87. package/dist/lib/xterm/src/common/buffer/CellData.ts +94 -0
  88. package/dist/lib/xterm/src/common/buffer/Constants.ts +149 -0
  89. package/dist/lib/xterm/src/common/buffer/Marker.ts +43 -0
  90. package/dist/lib/xterm/src/common/buffer/Types.d.ts +52 -0
  91. package/dist/lib/xterm/src/common/data/Charsets.ts +256 -0
  92. package/dist/lib/xterm/src/common/data/EscapeSequences.ts +153 -0
  93. package/dist/lib/xterm/src/common/input/Keyboard.ts +398 -0
  94. package/dist/lib/xterm/src/common/input/TextDecoder.ts +346 -0
  95. package/dist/lib/xterm/src/common/input/UnicodeV6.ts +132 -0
  96. package/dist/lib/xterm/src/common/input/WriteBuffer.ts +246 -0
  97. package/dist/lib/xterm/src/common/input/XParseColor.ts +80 -0
  98. package/dist/lib/xterm/src/common/parser/Constants.ts +58 -0
  99. package/dist/lib/xterm/src/common/parser/DcsParser.ts +192 -0
  100. package/dist/lib/xterm/src/common/parser/EscapeSequenceParser.ts +792 -0
  101. package/dist/lib/xterm/src/common/parser/OscParser.ts +238 -0
  102. package/dist/lib/xterm/src/common/parser/Params.ts +229 -0
  103. package/dist/lib/xterm/src/common/parser/Types.d.ts +274 -0
  104. package/dist/lib/xterm/src/common/public/AddonManager.ts +53 -0
  105. package/dist/lib/xterm/src/common/public/BufferApiView.ts +35 -0
  106. package/dist/lib/xterm/src/common/public/BufferLineApiView.ts +29 -0
  107. package/dist/lib/xterm/src/common/public/BufferNamespaceApi.ts +36 -0
  108. package/dist/lib/xterm/src/common/public/ParserApi.ts +37 -0
  109. package/dist/lib/xterm/src/common/public/UnicodeApi.ts +27 -0
  110. package/dist/lib/xterm/src/common/services/BufferService.ts +151 -0
  111. package/dist/lib/xterm/src/common/services/CharsetService.ts +34 -0
  112. package/dist/lib/xterm/src/common/services/CoreMouseService.ts +318 -0
  113. package/dist/lib/xterm/src/common/services/CoreService.ts +87 -0
  114. package/dist/lib/xterm/src/common/services/DecorationService.ts +140 -0
  115. package/dist/lib/xterm/src/common/services/InstantiationService.ts +85 -0
  116. package/dist/lib/xterm/src/common/services/LogService.ts +124 -0
  117. package/dist/lib/xterm/src/common/services/OptionsService.ts +201 -0
  118. package/dist/lib/xterm/src/common/services/OscLinkService.ts +115 -0
  119. package/dist/lib/xterm/src/common/services/ServiceRegistry.ts +49 -0
  120. package/dist/lib/xterm/src/common/services/Services.ts +342 -0
  121. package/dist/lib/xterm/src/common/services/UnicodeService.ts +86 -0
  122. package/dist/lib/xterm/src/headless/Terminal.ts +136 -0
  123. package/dist/lib/xterm/src/headless/public/Terminal.ts +195 -0
  124. package/dist/lib/xterm/typings/xterm.d.ts +1844 -0
  125. package/dist/lib/xterm-fit/LICENSE +19 -0
  126. package/dist/lib/xterm-fit/README.md +24 -0
  127. package/dist/lib/xterm-fit/lib/xterm-addon-fit.js +2 -0
  128. package/dist/lib/xterm-fit/lib/xterm-addon-fit.js.map +1 -0
  129. package/dist/lib/xterm-fit/package.json +26 -0
  130. package/dist/lib/xterm-fit/src/FitAddon.ts +89 -0
  131. package/dist/lib/xterm-fit/typings/xterm-addon-fit.d.ts +55 -0
  132. package/dist/lib/xterm-links/LICENSE +19 -0
  133. package/dist/lib/xterm-links/README.md +21 -0
  134. package/dist/lib/xterm-links/lib/xterm-addon-web-links.js +2 -0
  135. package/dist/lib/xterm-links/lib/xterm-addon-web-links.js.map +1 -0
  136. package/dist/lib/xterm-links/package.json +26 -0
  137. package/dist/lib/xterm-links/src/WebLinkProvider.ts +198 -0
  138. package/dist/lib/xterm-links/src/WebLinksAddon.ts +57 -0
  139. package/dist/lib/xterm-links/typings/xterm-addon-web-links.d.ts +53 -0
  140. package/dist/login.html +163 -0
  141. package/dist/manifest.json +12 -0
  142. package/dist/package.json +1 -0
  143. package/dist/sw.js +127 -0
  144. package/dist/sync.html +816 -0
  145. package/package.json +47 -0
@@ -0,0 +1,297 @@
1
+ <!DOCTYPE html>
2
+ <html lang="zh-CN">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>weHi 管理</title>
7
+ <style>
8
+ * { margin: 0; padding: 0; box-sizing: border-box; }
9
+ body { font-family: -apple-system, system-ui, sans-serif; background: #0d1117; color: #e6edf3; min-height: 100vh; }
10
+ .login-wrap { display: flex; align-items: center; justify-content: center; min-height: 100vh; padding: 1rem; }
11
+ .login-box { background: #161b22; border: 1px solid rgba(255,255,255,0.1); border-radius: 16px; padding: 2rem; width: 100%; max-width: 360px; }
12
+ .login-box h2 { text-align: center; margin-bottom: 1.5rem; font-size: 1.2rem; }
13
+ .login-box input { width: 100%; padding: 0.8rem; background: #0d1117; border: 1px solid rgba(255,255,255,0.15); border-radius: 10px; color: #e6edf3; font-size: 1rem; margin-bottom: 1rem; }
14
+ .login-box button { width: 100%; padding: 0.8rem; background: #f97316; color: #fff; border: none; border-radius: 10px; font-size: 1rem; font-weight: 600; cursor: pointer; }
15
+ .login-box .err { color: #f85149; font-size: 0.85rem; text-align: center; margin-bottom: 0.5rem; min-height: 1.2em; }
16
+ .wrap { max-width: 640px; margin: 0 auto; padding: 1rem; }
17
+ .header { display: flex; align-items: center; justify-content: space-between; padding: 1rem 0; border-bottom: 1px solid rgba(255,255,255,0.08); margin-bottom: 1.5rem; }
18
+ .header h1 { font-size: 1.1rem; }
19
+ .header .logout { background: none; border: 1px solid rgba(255,255,255,0.15); color: #8b949e; padding: 0.3rem 0.8rem; border-radius: 8px; cursor: pointer; font-size: 0.8rem; }
20
+ .ver-card { background: #161b22; border: 1px solid rgba(255,255,255,0.1); border-radius: 14px; padding: 1.2rem 1.5rem; margin-bottom: 1.2rem; }
21
+ .ver-big { font-size: 2rem; font-weight: 800; color: #f97316; }
22
+ .ver-sub { font-size: 0.85rem; color: #8b949e; margin-top: 0.5rem; }
23
+ .ver-sub b { color: #3fb950; }
24
+ .actions { display: flex; gap: 0.8rem; margin-bottom: 1.5rem; }
25
+ .actions button { flex: 1; padding: 0.8rem; border: none; border-radius: 10px; font-size: 0.9rem; font-weight: 600; cursor: pointer; }
26
+ .btn-up { background: #f97316; color: #fff; }
27
+ .btn-up:disabled { background: #333; color: #666; cursor: not-allowed; }
28
+ .btn-up.glow { background: #3fb950; animation: pulse 2s infinite; }
29
+ .btn-re { background: rgba(255,255,255,0.08); color: #e6edf3; border: 1px solid rgba(255,255,255,0.15) !important; }
30
+ @keyframes pulse { 0%,100%{opacity:1} 50%{opacity:0.7} }
31
+ .card { background: #161b22; border: 1px solid rgba(255,255,255,0.1); border-radius: 14px; padding: 1rem 1.2rem; margin-bottom: 1rem; }
32
+ .card h3 { font-size: 0.9rem; font-weight: 700; margin-bottom: 0.8rem; }
33
+ .row { font-size: 0.82rem; color: #b1bac4; padding: 0.5rem 0; line-height: 1.5; display: flex; justify-content: space-between; align-items: center; }
34
+ .row + .row { border-top: 1px solid rgba(255,255,255,0.04); }
35
+ .row-actions { display: flex; gap: 0.4rem; }
36
+ .row-actions button { background: none; border: 1px solid rgba(255,255,255,0.12); color: #8b949e; padding: 0.2rem 0.5rem; border-radius: 6px; cursor: pointer; font-size: 0.75rem; }
37
+ .row-actions button:hover { color: #e6edf3; border-color: rgba(255,255,255,0.3); }
38
+ .row-actions .del { color: #f85149; border-color: rgba(248,81,73,0.3); }
39
+ .inp { padding: 0.5rem 0.7rem; background: #0d1117; border: 1px solid rgba(255,255,255,0.15); border-radius: 8px; color: #e6edf3; font-size: 0.85rem; width: 100%; }
40
+ .inp-row { display: flex; gap: 0.5rem; align-items: center; margin-bottom: 0.5rem; }
41
+ .inp-row .inp { flex: 1; }
42
+ .btn-sm { padding: 0.5rem 1rem; background: #f97316; color: #fff; border: none; border-radius: 8px; cursor: pointer; font-size: 0.85rem; white-space: nowrap; }
43
+ .msg { font-size: 0.8rem; margin-top: 0.3rem; min-height: 1.2em; }
44
+ .msg-ok { color: #3fb950; }
45
+ .msg-err { color: #f85149; }
46
+ .status-msg { text-align: center; padding: 1rem; color: #8b949e; font-size: 0.85rem; }
47
+ .uptime { font-size: 0.78rem; color: #6e7681; text-align: center; margin-top: 1rem; }
48
+ </style>
49
+ </head>
50
+ <body>
51
+
52
+ <div class="login-wrap" id="pg-login">
53
+ <div class="login-box">
54
+ <h2>weHi 管理</h2>
55
+ <div class="err" id="login-err"></div>
56
+ <input type="password" id="admin-pwd" placeholder="输入管理密码" autofocus>
57
+ <button onclick="doLogin()">登录</button>
58
+ </div>
59
+ </div>
60
+
61
+ <div class="wrap" id="pg-admin" style="display:none">
62
+ <div class="header">
63
+ <h1>weHi 管理</h1>
64
+ <button class="logout" onclick="doLogout()">退出</button>
65
+ </div>
66
+
67
+ <!-- 版本 + 升级 -->
68
+ <div class="ver-card">
69
+ <div style="font-size:0.8rem;color:#8b949e">当前版本</div>
70
+ <div class="ver-big" id="v-cur">--</div>
71
+ <div class="ver-sub" id="v-latest"></div>
72
+ </div>
73
+ <div class="actions">
74
+ <button class="btn-up" id="btn-up" onclick="doUpgrade()" disabled>已是最新</button>
75
+ <button class="btn-re" onclick="doRestart()">重启</button>
76
+ </div>
77
+ <div id="op-msg" class="status-msg" style="display:none"></div>
78
+
79
+ <!-- 在线用户 -->
80
+ <div class="card">
81
+ <h3>在线用户</h3>
82
+ <div id="online-users"><div class="row" style="color:#6e7681">加载中...</div></div>
83
+ </div>
84
+
85
+ <!-- 活跃会话 -->
86
+ <div class="card">
87
+ <h3>活跃会话</h3>
88
+ <div id="active-sessions"><div class="row" style="color:#6e7681">加载中...</div></div>
89
+ </div>
90
+
91
+ <!-- 用户管理 -->
92
+ <div class="card">
93
+ <h3>用户管理</h3>
94
+ <div id="user-list"></div>
95
+ <div style="border-top:1px solid rgba(255,255,255,0.08);margin-top:0.5rem;padding-top:0.8rem">
96
+ <div style="font-size:0.82rem;color:#8b949e;margin-bottom:0.5rem">添加用户</div>
97
+ <div class="inp-row">
98
+ <input class="inp" id="u-pwd" placeholder="密码">
99
+ <input class="inp" id="u-name" placeholder="名称">
100
+ </div>
101
+ <div class="inp-row">
102
+ <input class="inp" id="u-dir" placeholder="工作目录">
103
+ <button class="btn-sm" onclick="addUser()">添加</button>
104
+ </div>
105
+ <div class="msg" id="u-msg"></div>
106
+ </div>
107
+ </div>
108
+
109
+ <!-- 修改管理密码 -->
110
+ <div class="card">
111
+ <h3>修改管理密码</h3>
112
+ <div class="inp-row">
113
+ <input type="password" class="inp" id="new-pwd" placeholder="新密码">
114
+ <button class="btn-sm" onclick="changePwd()">保存</button>
115
+ </div>
116
+ <div class="msg" id="pwd-msg"></div>
117
+ </div>
118
+
119
+ <div class="uptime" id="uptime"></div>
120
+ </div>
121
+
122
+ <script>
123
+ let T = sessionStorage.getItem('zihi_admin');
124
+ const H = () => ({ 'Authorization': 'Bearer ' + T, 'Content-Type': 'application/json' });
125
+
126
+ document.getElementById('admin-pwd').addEventListener('keydown', e => { if (e.key === 'Enter') doLogin(); });
127
+
128
+ async function doLogin() {
129
+ const pwd = document.getElementById('admin-pwd').value;
130
+ if (!pwd) return;
131
+ try {
132
+ const r = await fetch('/api/admin/login', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ password: pwd }) });
133
+ const d = await r.json();
134
+ if (d.ok) { T = d.token; sessionStorage.setItem('zihi_admin', T); showAdmin(); }
135
+ else document.getElementById('login-err').textContent = d.error || '密码错误';
136
+ } catch { document.getElementById('login-err').textContent = '连接失败'; }
137
+ }
138
+
139
+ function doLogout() {
140
+ sessionStorage.removeItem('zihi_admin'); T = null;
141
+ document.getElementById('pg-admin').style.display = 'none';
142
+ document.getElementById('pg-login').style.display = '';
143
+ }
144
+
145
+ async function showAdmin() {
146
+ document.getElementById('pg-login').style.display = 'none';
147
+ document.getElementById('pg-admin').style.display = '';
148
+ await refresh();
149
+ setInterval(refresh, 10000);
150
+ }
151
+
152
+ async function refresh() {
153
+ try {
154
+ const r = await fetch('/api/admin/status', { headers: H() });
155
+ if (r.status === 401) { doLogout(); return; }
156
+ const d = await r.json();
157
+
158
+ // 版本
159
+ document.getElementById('v-cur').textContent = 'v' + d.version.current;
160
+ const btn = document.getElementById('btn-up');
161
+ if (d.version.updateAvailable) {
162
+ document.getElementById('v-latest').innerHTML = '最新版本: <b>v' + d.version.latest + '</b>';
163
+ btn.disabled = false; btn.classList.add('glow'); btn.textContent = '升级到 v' + d.version.latest;
164
+ } else {
165
+ document.getElementById('v-latest').innerHTML = '<span style="color:#3fb950">已是最新版本</span>';
166
+ btn.disabled = true; btn.classList.remove('glow'); btn.textContent = '已是最新';
167
+ }
168
+
169
+ // 在线用户
170
+ const ouEl = document.getElementById('online-users');
171
+ if (!d.onlineUsers?.length) {
172
+ ouEl.innerHTML = '<div class="row" style="color:#6e7681">无在线用户</div>';
173
+ } else {
174
+ ouEl.innerHTML = d.onlineUsers.map(u =>
175
+ `<div class="row"><span>${esc(u.name)}</span><span style="color:#6e7681">${esc(u.role)}</span></div>`
176
+ ).join('');
177
+ }
178
+
179
+ // 活跃会话
180
+ const asEl = document.getElementById('active-sessions');
181
+ if (!d.sessions?.length) {
182
+ asEl.innerHTML = '<div class="row" style="color:#6e7681">无活跃会话</div>';
183
+ } else {
184
+ asEl.innerHTML = d.sessions.map(s => {
185
+ const tag = s.mode === 'agent'
186
+ ? '<span style="display:inline-block;font-size:0.7rem;padding:0.1rem 0.4rem;border-radius:4px;background:rgba(124,58,237,0.2);color:#a78bfa">Agent</span>'
187
+ : '<span style="display:inline-block;font-size:0.7rem;padding:0.1rem 0.4rem;border-radius:4px;background:rgba(63,185,80,0.2);color:#3fb950">PTY</span>';
188
+ return `<div class="row"><span>${tag} ${esc(s.title)} <span style="color:#6e7681">${esc(s.owner||'')}</span></span><span style="color:#6e7681">${s.viewers}人</span></div>`;
189
+ }).join('');
190
+ }
191
+
192
+ // 用户列表
193
+ renderUsers(d.users || []);
194
+
195
+ // 运行时长
196
+ if (d.uptime) {
197
+ const h = Math.floor(d.uptime / 3600), m = Math.floor((d.uptime % 3600) / 60);
198
+ document.getElementById('uptime').textContent = `服务运行 ${h}小时${m}分钟`;
199
+ }
200
+ } catch {}
201
+ }
202
+
203
+ function renderUsers(users) {
204
+ const el = document.getElementById('user-list');
205
+ if (!users.length) { el.innerHTML = '<div class="row" style="color:#6e7681">暂无用户</div>'; return; }
206
+ el.innerHTML = users.map(u => `
207
+ <div class="row">
208
+ <span><b style="color:#e6edf3">${esc(u.name)}</b> <span style="color:#6e7681;font-size:0.75rem">${esc(u.password)}</span> <span style="color:#6e7681;font-size:0.72rem">${esc(u.dir)}</span></span>
209
+ <div class="row-actions">
210
+ <button onclick="editUserPwd('${esc(u.name)}')">改密</button>
211
+ <button class="del" onclick="delUser('${esc(u.name)}')">删除</button>
212
+ </div>
213
+ </div>
214
+ `).join('');
215
+ }
216
+
217
+ function esc(s) { return String(s||'').replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;').replace(/"/g,'&quot;').replace(/'/g,'&#39;'); }
218
+
219
+ async function addUser() {
220
+ const pwd = document.getElementById('u-pwd').value.trim();
221
+ const name = document.getElementById('u-name').value.trim();
222
+ const dir = document.getElementById('u-dir').value.trim();
223
+ const msg = document.getElementById('u-msg');
224
+ if (!pwd || !name || !dir) { msg.className = 'msg msg-err'; msg.textContent = '密码、名称、目录都必填'; return; }
225
+ try {
226
+ const r = await fetch('/api/admin/user', { method: 'POST', headers: H(), body: JSON.stringify({ password: pwd, name, dir }) });
227
+ const d = await r.json();
228
+ if (d.ok) { msg.className = 'msg msg-ok'; msg.textContent = '已添加'; document.getElementById('u-pwd').value = ''; document.getElementById('u-name').value = ''; document.getElementById('u-dir').value = ''; refresh(); }
229
+ else { msg.className = 'msg msg-err'; msg.textContent = d.error || '失败'; }
230
+ } catch { msg.className = 'msg msg-err'; msg.textContent = '连接失败'; }
231
+ }
232
+
233
+ async function editUserPwd(name) {
234
+ const newPwd = prompt(`修改 ${name} 的密码:`);
235
+ if (!newPwd) return;
236
+ try {
237
+ const r = await fetch('/api/admin/user/password', { method: 'POST', headers: H(), body: JSON.stringify({ name, password: newPwd }) });
238
+ const d = await r.json();
239
+ if (d.ok) { alert('密码已修改'); refresh(); }
240
+ else alert(d.error || '失败');
241
+ } catch { alert('连接失败'); }
242
+ }
243
+
244
+ async function delUser(name) {
245
+ if (!confirm(`确定删除用户 ${name}?`)) return;
246
+ try {
247
+ const r = await fetch('/api/admin/user', { method: 'DELETE', headers: H(), body: JSON.stringify({ name }) });
248
+ const d = await r.json();
249
+ if (d.ok) refresh();
250
+ else alert(d.error || '失败');
251
+ } catch { alert('连接失败'); }
252
+ }
253
+
254
+ async function changePwd() {
255
+ const pwd = document.getElementById('new-pwd').value.trim();
256
+ const msg = document.getElementById('pwd-msg');
257
+ if (!pwd || pwd.length < 4) { msg.className = 'msg msg-err'; msg.textContent = '密码至少4位'; return; }
258
+ try {
259
+ const r = await fetch('/api/admin/password', { method: 'POST', headers: H(), body: JSON.stringify({ password: pwd }) });
260
+ const d = await r.json();
261
+ if (d.ok) { msg.className = 'msg msg-ok'; msg.textContent = '已修改'; document.getElementById('new-pwd').value = ''; }
262
+ else { msg.className = 'msg msg-err'; msg.textContent = d.error || '失败'; }
263
+ } catch { msg.className = 'msg msg-err'; msg.textContent = '连接失败'; }
264
+ }
265
+
266
+ async function doUpgrade() {
267
+ const btn = document.getElementById('btn-up'), msg = document.getElementById('op-msg');
268
+ btn.disabled = true; btn.textContent = '升级中...'; msg.textContent = '正在下载安装...'; msg.style.display = '';
269
+ try {
270
+ const r = await fetch('/api/admin/upgrade', { method: 'POST', headers: H() });
271
+ const d = await r.json();
272
+ if (d.ok) { msg.textContent = '升级完成,重启中...'; waitReload(); }
273
+ else { msg.textContent = '失败: ' + (d.error||''); btn.disabled = false; btn.textContent = '重试'; }
274
+ } catch { msg.textContent = '连接失败'; btn.disabled = false; btn.textContent = '重试'; }
275
+ }
276
+
277
+ async function doRestart() {
278
+ if (!confirm('确定重启?')) return;
279
+ const msg = document.getElementById('op-msg');
280
+ msg.textContent = '重启中...'; msg.style.display = '';
281
+ try { await fetch('/api/admin/restart', { method: 'POST', headers: H() }); } catch {}
282
+ waitReload();
283
+ }
284
+
285
+ async function waitReload() {
286
+ await new Promise(r => setTimeout(r, 3000));
287
+ for (let i = 0; i < 30; i++) {
288
+ try { const r = await fetch('/api/version'); if (r.ok) { location.reload(); return; } } catch {}
289
+ await new Promise(r => setTimeout(r, 2000));
290
+ }
291
+ document.getElementById('op-msg').textContent = '重启超时,请手动刷新';
292
+ }
293
+
294
+ if (T) showAdmin();
295
+ </script>
296
+ </body>
297
+ </html>
package/dist/attach.js ADDED
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ import{createRequire as e}from"module";if(typeof __nccwpck_require__!=="undefined")__nccwpck_require__.ab=new URL(".",import.meta.url).pathname.slice(import.meta.url.match(/^file:\/\/\/\w:/)?1:0,-1)+"/";var t={};const s=e(import.meta.url)("module");const r=e(import.meta.url)("fs");const o=e(import.meta.url)("path");const n=e(import.meta.url)("os");const i=e(import.meta.url)("readline");const c=(0,s.createRequire)(import.meta.url);const{io:p}=c("socket.io-client");const a=process.env.ZIHI_SERVER||"http://localhost:12300";let d=null;const u=process.argv.slice(2);for(let e=0;e<u.length;e++){if((u[e]==="--password"||u[e]==="-pw")&&u[e+1]){d=u[e+1];u.splice(e,2);e--}}let l;if(d){l={password:d}}else{try{l={token:(0,r.readFileSync)((0,o.join)((0,n.homedir)(),".zihi","token"),"utf8").trim()}}catch{console.error("[zihi] 未找到 token,请使用 --password <密码> 或先启动服务器");process.exit(1)}}function cleanup(e){try{process.stdin.setRawMode(false)}catch{}process.stdin.pause();e.disconnect()}function attach(e,t){e.emit("join",t);e.on("joined",(s=>{const r=s.mode==="agent";process.stderr.write(`\r\n[zihi] 已附加到 "${s.title}" (${t}) 模式=${r?"agent":"pty"}\r\n`);process.stderr.write("[zihi] 按 Ctrl+] 分离\r\n\r\n");e.emit("take-control",{sessionId:t});if(r){let t="";e.on("agent:message",(e=>{if(!e)return;switch(e.type){case"system":if(e.subtype==="init")process.stderr.write("[会话已连接]\r\n");else if(e.subtype==="interrupted")process.stderr.write("\r\n[已中断]\r\n");break;case"assistant":if(e.message?.content){for(const t of e.message.content){if(t.type==="text")process.stdout.write(t.text+"\r\n");else if(t.type==="tool_use"){process.stdout.write(`\r\n[工具] ${t.name}\r\n`);if(t.input){const e=typeof t.input==="string"?t.input:JSON.stringify(t.input,null,2);process.stdout.write(""+e.slice(0,500)+"\r\n")}}}}break;case"content_block_delta":if(e.delta?.text){process.stdout.write(e.delta.text);t+=e.delta.text}break;case"content_block_stop":if(t){process.stdout.write("\r\n");t=""}break;case"tool_use":process.stdout.write(`\r\n[工具] ${e.tool_name||e.name||"?"}\r\n`);break;case"tool_result":if(e.content){const t=typeof e.content==="string"?e.content:Array.isArray(e.content)?e.content.map((e=>e.text||"")).join(""):"";if(t)process.stdout.write(""+t.slice(0,1e3)+"\r\n")}break;case"result":process.stdout.write(`\r\n[完成] ${e.duration_ms?(e.duration_ms/1e3).toFixed(1)+"s":""} ${e.total_cost_usd?"$"+e.total_cost_usd.toFixed(4):""}\r\n\r\n`);break}}));e.on("agent:history",(e=>{for(const t of e){if(t.type==="user")process.stdout.write(`> ${t.content}\r\n`);else if(t.type==="result")process.stdout.write(`[完成]\r\n`)}process.stdout.write("\r\n")}));e.on("agent:busy",(e=>{if(e)process.stderr.write("[思考中...]\r\n")}));e.on("agent:error",(e=>{process.stderr.write(`[错误] ${e.message}\r\n`)}));const s=(0,i.createInterface)({input:process.stdin,output:process.stdout,prompt:"> "});let r=false;e.on("agent:busy",(e=>{r=e;if(!e)s.prompt()}));s.prompt();s.on("line",(t=>{const o=t.trim();if(!o){if(!r)s.prompt();return}if(o==="/quit"||o==="/exit"||o==="/q"){process.stderr.write("[zihi] 已分离\r\n");cleanup(e);process.exit(0)}if(r){process.stderr.write("[正在处理中,请等待]\r\n");return}e.emit("agent:query",{prompt:o})}));s.on("close",(()=>{cleanup(e);process.exit(0)}))}else{try{process.stdin.setRawMode(true)}catch{}process.stdin.resume();process.stdin.setEncoding("binary");process.stdin.on("data",(t=>{if(t===""){process.stderr.write("\r\n[zihi] 已分离\r\n");cleanup(e);process.exit(0)}e.emit("input",t)}));e.on("output",(e=>{process.stdout.write(e,"binary")}));process.stdout.on("resize",(()=>{e.emit("resize",{cols:process.stdout.columns||80,rows:process.stdout.rows||24})}))}}));e.on("control-denied",(({reason:e})=>{process.stderr.write(`\r\n[zihi] 获取控制权失败: ${e}\r\n`)}));e.on("control-changed",(({holder:t,holderName:s})=>{if(t&&t!==e.id){process.stderr.write(`\r\n[zihi] ${s||"其他用户"} 已获取控制权,当前为只读\r\n`)}else if(!t){process.stderr.write("\r\n[zihi] 控制权已释放\r\n")}}));e.on("kicked",(({reason:t})=>{process.stderr.write(`\r\n[zihi] ${t||"你已被管理员踢出"}\r\n`);cleanup(e);process.exit(1)}));e.on("session-exit",(({code:t})=>{process.stderr.write(`\r\n[zihi] 会话已退出 (code ${t})\r\n`);cleanup(e);process.exit(0)}));e.on("error",(({message:t})=>{process.stderr.write(`\r\n[zihi] 错误: ${t}\r\n`);cleanup(e);process.exit(1)}))}async function pickSession(e){return new Promise(((t,s)=>{e.emit("list");e.once("sessions",(s=>{const r=s.filter((e=>e.alive));if(!r.length){process.stderr.write("[zihi] 没有活跃的会话。\n");cleanup(e);process.exit(0)}process.stdout.write("\n活跃会话:\n");r.forEach(((e,t)=>{const s=e.viewers>0?` (${e.viewers} 人在线)`:"";const r=e.mode==="agent"?" [Agent]":" [PTY]";process.stdout.write(` [${t+1}] ${e.title}${r}${s} — ${e.id}\n`)}));process.stdout.write("\n");const o=(0,i.createInterface)({input:process.stdin,output:process.stdout});o.question("选择会话编号: ",(s=>{o.close();const n=parseInt(s,10)-1;if(n<0||n>=r.length){process.stderr.write("[zihi] 无效的选择。\n");cleanup(e);process.exit(1)}t(r[n].id)}))}))}))}async function createAndAttach(e,t){return new Promise(((s,r)=>{e.emit("create",t,(t=>{if(!t?.ok){process.stderr.write(`[zihi] 创建失败: ${t?.error||"未知错误"}\n`);cleanup(e);process.exit(1)}process.stdout.write(`[zihi] 已创建会话 "${t.session.title}" — ${t.session.id}\n`);s(t.session.id)}))}))}async function promptNew(e){const t=(0,i.createInterface)({input:process.stdin,output:process.stdout});const ask=e=>new Promise((s=>t.question(e,s)));const s=(await ask("会话名称 [shell]: ")).trim()||"shell";const r=(await ask("启动命令(可选): ")).trim()||undefined;t.close();return createAndAttach(e,{title:s,initCmd:r})}const m=p(a,{transports:["websocket","polling"],auth:l});m.on("connect_error",(e=>{process.stderr.write(`[zihi] 连接失败: ${e.message}\n`);process.exit(1)}));m.on("connect",(async()=>{const e=u[0];if(e==="--new"){const e=u[1];const t=u[2]||undefined;const s=e?await createAndAttach(m,{title:e,initCmd:t}):await promptNew(m);attach(m,s)}else{const t=e||await pickSession(m);attach(m,t)}}));for(const e of["SIGINT","SIGTERM"]){process.on(e,(()=>{cleanup(m);process.exit(0)}))}process.on("exit",(()=>{try{process.stdin.setRawMode(false)}catch{}}));