@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.
- package/bin/daemon.js +23 -0
- package/bin/zihi.js +603 -0
- package/dist/admin.html +297 -0
- package/dist/attach.js +2 -0
- package/dist/chat.html +2254 -0
- package/dist/client-dist/socket.io.esm.min.js +7 -0
- package/dist/client-dist/socket.io.js +4955 -0
- package/dist/client-dist/socket.io.min.js +7 -0
- package/dist/client-dist/socket.io.msgpack.min.js +7 -0
- package/dist/files.html +722 -0
- package/dist/icon.png +0 -0
- package/dist/icon.svg +4 -0
- package/dist/index.html +976 -0
- package/dist/index.js +485 -0
- package/dist/lib/ansi_up.js +431 -0
- package/dist/lib/xterm/LICENSE +21 -0
- package/dist/lib/xterm/README.md +230 -0
- package/dist/lib/xterm/css/xterm.css +209 -0
- package/dist/lib/xterm/lib/xterm.js +2 -0
- package/dist/lib/xterm/lib/xterm.js.map +1 -0
- package/dist/lib/xterm/package.json +100 -0
- package/dist/lib/xterm/src/browser/AccessibilityManager.ts +300 -0
- package/dist/lib/xterm/src/browser/Clipboard.ts +93 -0
- package/dist/lib/xterm/src/browser/ColorContrastCache.ts +34 -0
- package/dist/lib/xterm/src/browser/Lifecycle.ts +33 -0
- package/dist/lib/xterm/src/browser/Linkifier2.ts +416 -0
- package/dist/lib/xterm/src/browser/LocalizableStrings.ts +12 -0
- package/dist/lib/xterm/src/browser/OscLinkProvider.ts +128 -0
- package/dist/lib/xterm/src/browser/RenderDebouncer.ts +83 -0
- package/dist/lib/xterm/src/browser/ScreenDprMonitor.ts +72 -0
- package/dist/lib/xterm/src/browser/Terminal.ts +1305 -0
- package/dist/lib/xterm/src/browser/TimeBasedDebouncer.ts +86 -0
- package/dist/lib/xterm/src/browser/Types.d.ts +181 -0
- package/dist/lib/xterm/src/browser/Viewport.ts +401 -0
- package/dist/lib/xterm/src/browser/decorations/BufferDecorationRenderer.ts +134 -0
- package/dist/lib/xterm/src/browser/decorations/ColorZoneStore.ts +117 -0
- package/dist/lib/xterm/src/browser/decorations/OverviewRulerRenderer.ts +219 -0
- package/dist/lib/xterm/src/browser/input/CompositionHelper.ts +246 -0
- package/dist/lib/xterm/src/browser/input/Mouse.ts +54 -0
- package/dist/lib/xterm/src/browser/input/MoveToCell.ts +249 -0
- package/dist/lib/xterm/src/browser/public/Terminal.ts +260 -0
- package/dist/lib/xterm/src/browser/renderer/dom/DomRenderer.ts +506 -0
- package/dist/lib/xterm/src/browser/renderer/dom/DomRendererRowFactory.ts +522 -0
- package/dist/lib/xterm/src/browser/renderer/dom/WidthCache.ts +157 -0
- package/dist/lib/xterm/src/browser/renderer/shared/CellColorResolver.ts +137 -0
- package/dist/lib/xterm/src/browser/renderer/shared/CharAtlasCache.ts +96 -0
- package/dist/lib/xterm/src/browser/renderer/shared/CharAtlasUtils.ts +75 -0
- package/dist/lib/xterm/src/browser/renderer/shared/Constants.ts +14 -0
- package/dist/lib/xterm/src/browser/renderer/shared/CursorBlinkStateManager.ts +146 -0
- package/dist/lib/xterm/src/browser/renderer/shared/CustomGlyphs.ts +687 -0
- package/dist/lib/xterm/src/browser/renderer/shared/DevicePixelObserver.ts +41 -0
- package/dist/lib/xterm/src/browser/renderer/shared/README.md +1 -0
- package/dist/lib/xterm/src/browser/renderer/shared/RendererUtils.ts +58 -0
- package/dist/lib/xterm/src/browser/renderer/shared/SelectionRenderModel.ts +91 -0
- package/dist/lib/xterm/src/browser/renderer/shared/TextureAtlas.ts +1082 -0
- package/dist/lib/xterm/src/browser/renderer/shared/Types.d.ts +173 -0
- package/dist/lib/xterm/src/browser/selection/SelectionModel.ts +144 -0
- package/dist/lib/xterm/src/browser/selection/Types.d.ts +15 -0
- package/dist/lib/xterm/src/browser/services/CharSizeService.ts +102 -0
- package/dist/lib/xterm/src/browser/services/CharacterJoinerService.ts +339 -0
- package/dist/lib/xterm/src/browser/services/CoreBrowserService.ts +33 -0
- package/dist/lib/xterm/src/browser/services/MouseService.ts +46 -0
- package/dist/lib/xterm/src/browser/services/RenderService.ts +284 -0
- package/dist/lib/xterm/src/browser/services/SelectionService.ts +1029 -0
- package/dist/lib/xterm/src/browser/services/Services.ts +138 -0
- package/dist/lib/xterm/src/browser/services/ThemeService.ts +237 -0
- package/dist/lib/xterm/src/common/CircularList.ts +241 -0
- package/dist/lib/xterm/src/common/Clone.ts +23 -0
- package/dist/lib/xterm/src/common/Color.ts +356 -0
- package/dist/lib/xterm/src/common/CoreTerminal.ts +284 -0
- package/dist/lib/xterm/src/common/EventEmitter.ts +73 -0
- package/dist/lib/xterm/src/common/InputHandler.ts +3443 -0
- package/dist/lib/xterm/src/common/Lifecycle.ts +108 -0
- package/dist/lib/xterm/src/common/MultiKeyMap.ts +42 -0
- package/dist/lib/xterm/src/common/Platform.ts +43 -0
- package/dist/lib/xterm/src/common/SortedList.ts +118 -0
- package/dist/lib/xterm/src/common/TaskQueue.ts +166 -0
- package/dist/lib/xterm/src/common/TypedArrayUtils.ts +17 -0
- package/dist/lib/xterm/src/common/Types.d.ts +553 -0
- package/dist/lib/xterm/src/common/WindowsMode.ts +27 -0
- package/dist/lib/xterm/src/common/buffer/AttributeData.ts +196 -0
- package/dist/lib/xterm/src/common/buffer/Buffer.ts +654 -0
- package/dist/lib/xterm/src/common/buffer/BufferLine.ts +520 -0
- package/dist/lib/xterm/src/common/buffer/BufferRange.ts +13 -0
- package/dist/lib/xterm/src/common/buffer/BufferReflow.ts +223 -0
- package/dist/lib/xterm/src/common/buffer/BufferSet.ts +134 -0
- package/dist/lib/xterm/src/common/buffer/CellData.ts +94 -0
- package/dist/lib/xterm/src/common/buffer/Constants.ts +149 -0
- package/dist/lib/xterm/src/common/buffer/Marker.ts +43 -0
- package/dist/lib/xterm/src/common/buffer/Types.d.ts +52 -0
- package/dist/lib/xterm/src/common/data/Charsets.ts +256 -0
- package/dist/lib/xterm/src/common/data/EscapeSequences.ts +153 -0
- package/dist/lib/xterm/src/common/input/Keyboard.ts +398 -0
- package/dist/lib/xterm/src/common/input/TextDecoder.ts +346 -0
- package/dist/lib/xterm/src/common/input/UnicodeV6.ts +132 -0
- package/dist/lib/xterm/src/common/input/WriteBuffer.ts +246 -0
- package/dist/lib/xterm/src/common/input/XParseColor.ts +80 -0
- package/dist/lib/xterm/src/common/parser/Constants.ts +58 -0
- package/dist/lib/xterm/src/common/parser/DcsParser.ts +192 -0
- package/dist/lib/xterm/src/common/parser/EscapeSequenceParser.ts +792 -0
- package/dist/lib/xterm/src/common/parser/OscParser.ts +238 -0
- package/dist/lib/xterm/src/common/parser/Params.ts +229 -0
- package/dist/lib/xterm/src/common/parser/Types.d.ts +274 -0
- package/dist/lib/xterm/src/common/public/AddonManager.ts +53 -0
- package/dist/lib/xterm/src/common/public/BufferApiView.ts +35 -0
- package/dist/lib/xterm/src/common/public/BufferLineApiView.ts +29 -0
- package/dist/lib/xterm/src/common/public/BufferNamespaceApi.ts +36 -0
- package/dist/lib/xterm/src/common/public/ParserApi.ts +37 -0
- package/dist/lib/xterm/src/common/public/UnicodeApi.ts +27 -0
- package/dist/lib/xterm/src/common/services/BufferService.ts +151 -0
- package/dist/lib/xterm/src/common/services/CharsetService.ts +34 -0
- package/dist/lib/xterm/src/common/services/CoreMouseService.ts +318 -0
- package/dist/lib/xterm/src/common/services/CoreService.ts +87 -0
- package/dist/lib/xterm/src/common/services/DecorationService.ts +140 -0
- package/dist/lib/xterm/src/common/services/InstantiationService.ts +85 -0
- package/dist/lib/xterm/src/common/services/LogService.ts +124 -0
- package/dist/lib/xterm/src/common/services/OptionsService.ts +201 -0
- package/dist/lib/xterm/src/common/services/OscLinkService.ts +115 -0
- package/dist/lib/xterm/src/common/services/ServiceRegistry.ts +49 -0
- package/dist/lib/xterm/src/common/services/Services.ts +342 -0
- package/dist/lib/xterm/src/common/services/UnicodeService.ts +86 -0
- package/dist/lib/xterm/src/headless/Terminal.ts +136 -0
- package/dist/lib/xterm/src/headless/public/Terminal.ts +195 -0
- package/dist/lib/xterm/typings/xterm.d.ts +1844 -0
- package/dist/lib/xterm-fit/LICENSE +19 -0
- package/dist/lib/xterm-fit/README.md +24 -0
- package/dist/lib/xterm-fit/lib/xterm-addon-fit.js +2 -0
- package/dist/lib/xterm-fit/lib/xterm-addon-fit.js.map +1 -0
- package/dist/lib/xterm-fit/package.json +26 -0
- package/dist/lib/xterm-fit/src/FitAddon.ts +89 -0
- package/dist/lib/xterm-fit/typings/xterm-addon-fit.d.ts +55 -0
- package/dist/lib/xterm-links/LICENSE +19 -0
- package/dist/lib/xterm-links/README.md +21 -0
- package/dist/lib/xterm-links/lib/xterm-addon-web-links.js +2 -0
- package/dist/lib/xterm-links/lib/xterm-addon-web-links.js.map +1 -0
- package/dist/lib/xterm-links/package.json +26 -0
- package/dist/lib/xterm-links/src/WebLinkProvider.ts +198 -0
- package/dist/lib/xterm-links/src/WebLinksAddon.ts +57 -0
- package/dist/lib/xterm-links/typings/xterm-addon-web-links.d.ts +53 -0
- package/dist/login.html +163 -0
- package/dist/manifest.json +12 -0
- package/dist/package.json +1 -0
- package/dist/sw.js +127 -0
- package/dist/sync.html +816 -0
- package/package.json +47 -0
package/dist/admin.html
ADDED
|
@@ -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,'&').replace(/</g,'<').replace(/>/g,'>').replace(/"/g,'"').replace(/'/g,'''); }
|
|
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("[90m[会话已连接][0m\r\n");else if(e.subtype==="interrupted")process.stderr.write("\r\n[33m[已中断][0m\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[36m[工具] ${t.name}[0m\r\n`);if(t.input){const e=typeof t.input==="string"?t.input:JSON.stringify(t.input,null,2);process.stdout.write("[90m"+e.slice(0,500)+"[0m\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[36m[工具] ${e.tool_name||e.name||"?"}[0m\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("[90m"+t.slice(0,1e3)+"[0m\r\n")}break;case"result":process.stdout.write(`\r\n[32m[完成][0m ${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(`[34m> ${t.content}[0m\r\n`);else if(t.type==="result")process.stdout.write(`[32m[完成][0m\r\n`)}process.stdout.write("\r\n")}));e.on("agent:busy",(e=>{if(e)process.stderr.write("[90m[思考中...][0m\r\n")}));e.on("agent:error",(e=>{process.stderr.write(`[31m[错误] ${e.message}[0m\r\n`)}));const s=(0,i.createInterface)({input:process.stdin,output:process.stdout,prompt:"[34m> [0m"});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("[33m[正在处理中,请等待][0m\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{}}));
|