@wendongfly/zihi 1.1.14 → 1.1.15

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/zihi.js CHANGED
@@ -134,6 +134,18 @@ function printRunningInfo(info, opts = {}) {
134
134
  }
135
135
 
136
136
  async function stopDaemon() {
137
+ // Linux 下优先使用 systemd
138
+ if (hasSystemdService()) {
139
+ try {
140
+ execSync('sudo systemctl stop zihi', { stdio: 'inherit' });
141
+ console.log('[zihi] 服务已停止');
142
+ return true;
143
+ } catch (e) {
144
+ console.error('[zihi] systemctl 停止失败:', e.message);
145
+ return false;
146
+ }
147
+ }
148
+
137
149
  const info = readPidInfo();
138
150
  if (!info || !info.pid) { console.log('[zihi] 没有正在运行的后台进程'); return false; }
139
151
  if (!isPidAlive(info.pid)) {
@@ -154,7 +166,31 @@ async function stopDaemon() {
154
166
  return true;
155
167
  }
156
168
 
169
+ /** 检查是否已安装 systemd 服务 */
170
+ function hasSystemdService() {
171
+ if (process.platform !== 'linux') return false;
172
+ try {
173
+ execSync('systemctl cat zihi.service', { stdio: 'ignore', timeout: 2000 });
174
+ return true;
175
+ } catch { return false; }
176
+ }
177
+
157
178
  async function startDaemon() {
179
+ // Linux 下优先使用 systemd
180
+ if (hasSystemdService()) {
181
+ console.log('[zihi] 检测到 systemd 服务,通过 systemctl 启动');
182
+ try {
183
+ execSync('sudo systemctl restart zihi', { stdio: 'inherit' });
184
+ console.log('[zihi] 服务已启动');
185
+ console.log(' 查看状态: sudo systemctl status zihi');
186
+ console.log(' 查看日志: sudo journalctl -u zihi -f');
187
+ process.exit(0);
188
+ } catch (e) {
189
+ console.error('[zihi] systemctl 启动失败:', e.message);
190
+ process.exit(1);
191
+ }
192
+ }
193
+
158
194
  // 启动前唯一性校验
159
195
  const running = await verifyRunning();
160
196
  if (running) {
@@ -248,6 +284,66 @@ if (cmd === 'attach') {
248
284
  console.log('[zihi] 日志文件不存在');
249
285
  }
250
286
 
287
+ } else if (cmd === 'install' || cmd === 'service') {
288
+ // Linux systemd 服务安装
289
+ if (process.platform !== 'linux') {
290
+ console.log('[zihi] systemd 服务安装仅支持 Linux');
291
+ process.exit(1);
292
+ }
293
+ const servicePath = '/etc/systemd/system/zihi.service';
294
+ const user = process.env.SUDO_USER || process.env.USER || 'root';
295
+ const nodeBin = process.execPath;
296
+ const zihiBin = join(__dirname, 'zihi.js');
297
+ const port = process.env.PORT || '12345';
298
+ const unit = `[Unit]
299
+ Description=ZiHi - AI Agent Terminal Sharing
300
+ After=network.target
301
+
302
+ [Service]
303
+ Type=simple
304
+ User=${user}
305
+ Environment=PORT=${port}
306
+ Environment=ZIHI_DAEMON=1
307
+ ExecStart=${nodeBin} ${join(__dirname, 'daemon.js')}
308
+ Restart=on-failure
309
+ RestartSec=5
310
+ StandardOutput=append:${join(homedir(), '.zihi/daemon.log')}
311
+ StandardError=append:${join(homedir(), '.zihi/daemon.log')}
312
+
313
+ [Install]
314
+ WantedBy=multi-user.target
315
+ `;
316
+ try {
317
+ execSync(`sudo tee ${servicePath}`, { input: unit, stdio: ['pipe', 'ignore', 'inherit'] });
318
+ execSync('sudo systemctl daemon-reload', { stdio: 'inherit' });
319
+ execSync('sudo systemctl enable zihi', { stdio: 'inherit' });
320
+ execSync('sudo systemctl restart zihi', { stdio: 'inherit' });
321
+ console.log('\n[zihi] systemd 服务已安装并启动');
322
+ console.log(' 查看状态: sudo systemctl status zihi');
323
+ console.log(' 查看日志: sudo journalctl -u zihi -f');
324
+ console.log(' 停止服务: sudo systemctl stop zihi');
325
+ console.log(' 卸载服务: zihi uninstall');
326
+ } catch (e) {
327
+ console.error('[zihi] 安装失败:', e.message);
328
+ process.exit(1);
329
+ }
330
+
331
+ } else if (cmd === 'uninstall') {
332
+ if (process.platform !== 'linux') {
333
+ console.log('[zihi] systemd 服务卸载仅支持 Linux');
334
+ process.exit(1);
335
+ }
336
+ try {
337
+ execSync('sudo systemctl stop zihi 2>/dev/null || true', { stdio: 'inherit' });
338
+ execSync('sudo systemctl disable zihi 2>/dev/null || true', { stdio: 'inherit' });
339
+ execSync('sudo rm -f /etc/systemd/system/zihi.service', { stdio: 'inherit' });
340
+ execSync('sudo systemctl daemon-reload', { stdio: 'inherit' });
341
+ console.log('[zihi] systemd 服务已卸载');
342
+ } catch (e) {
343
+ console.error('[zihi] 卸载失败:', e.message);
344
+ process.exit(1);
345
+ }
346
+
251
347
  } else if (cmd === 'exclusive') {
252
348
  // 独占模式开关
253
349
  const { existsSync } = await import('fs');
package/dist/admin.html CHANGED
@@ -106,6 +106,19 @@
106
106
  </div>
107
107
  </div>
108
108
 
109
+ <!-- Claude Code -->
110
+ <div class="card">
111
+ <h3>Claude Code</h3>
112
+ <div class="row">
113
+ <span id="claude-status">检测中...</span>
114
+ <div class="row-actions">
115
+ <button onclick="checkClaude()">检测</button>
116
+ <button onclick="installClaude()" class="btn-install">安装/更新</button>
117
+ </div>
118
+ </div>
119
+ <pre id="claude-log" style="display:none;font-size:0.72rem;background:#0d1117;border:1px solid rgba(255,255,255,0.1);border-radius:8px;padding:0.6rem;margin-top:0.5rem;max-height:240px;overflow:auto;white-space:pre-wrap;word-break:break-all;color:#b1bac4"></pre>
120
+ </div>
121
+
109
122
  <!-- 修改管理密码 -->
110
123
  <div class="card">
111
124
  <h3>修改管理密码</h3>
@@ -146,6 +159,7 @@ async function showAdmin() {
146
159
  document.getElementById('pg-login').style.display = 'none';
147
160
  document.getElementById('pg-admin').style.display = '';
148
161
  await refresh();
162
+ checkClaude();
149
163
  setInterval(refresh, 10000);
150
164
  }
151
165
 
@@ -274,6 +288,43 @@ async function doUpgrade() {
274
288
  } catch { msg.textContent = '连接失败'; btn.disabled = false; btn.textContent = '重试'; }
275
289
  }
276
290
 
291
+ async function checkClaude() {
292
+ const st = document.getElementById('claude-status');
293
+ st.textContent = '检测中...';
294
+ try {
295
+ const r = await fetch('/api/admin/claude-status', { headers: H() });
296
+ const d = await r.json();
297
+ if (d.installed) {
298
+ st.innerHTML = '<span style="color:#3fb950">✓ 已安装</span> <span style="color:#8b949e;font-size:0.78rem">' + (d.version || '') + '</span>';
299
+ } else {
300
+ st.innerHTML = '<span style="color:#f85149">✗ 未安装</span>';
301
+ }
302
+ } catch {
303
+ st.textContent = '检测失败';
304
+ }
305
+ }
306
+
307
+ async function installClaude() {
308
+ if (!confirm('确定安装/更新 Claude Code?(可能需要几分钟)')) return;
309
+ var log = document.getElementById('claude-log');
310
+ log.style.display = 'block';
311
+ log.textContent = '开始安装...\n';
312
+ try {
313
+ var r = await fetch('/api/admin/install-claude', { method: 'POST', headers: H() });
314
+ var reader = r.body.getReader();
315
+ var decoder = new TextDecoder();
316
+ while (true) {
317
+ var chunk = await reader.read();
318
+ if (chunk.done) break;
319
+ log.textContent += decoder.decode(chunk.value);
320
+ log.scrollTop = log.scrollHeight;
321
+ }
322
+ checkClaude();
323
+ } catch (e) {
324
+ log.textContent += '\n[连接失败] ' + e.message;
325
+ }
326
+ }
327
+
277
328
  async function doRestart() {
278
329
  if (!confirm('确定重启?')) return;
279
330
  const msg = document.getElementById('op-msg');