@wendongfly/myhi 1.0.24 → 1.0.26

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/myhi.js CHANGED
@@ -103,6 +103,59 @@ if (cmd === 'attach') {
103
103
  console.log('[myhi] 日志文件不存在');
104
104
  }
105
105
 
106
+ } else if (cmd === 'user') {
107
+ const { loadUsers, listUsers, addUser, removeUser } = await import('../src/roles.js');
108
+ loadUsers();
109
+ const sub = args[args.indexOf('user') + 1] || process.argv[3];
110
+ if (sub === 'list' || sub === 'ls') {
111
+ const users = listUsers();
112
+ if (users.length === 0) {
113
+ console.log('[myhi] 还没有用户,使用 myhi user add <密码> <名称> <目录> 添加');
114
+ } else {
115
+ console.log('\n 密码\t\t名称\t\t目录');
116
+ console.log(' ────\t\t────\t\t────');
117
+ for (const u of users) {
118
+ console.log(` ${u.password}\t\t${u.name}\t\t${u.dir}`);
119
+ }
120
+ console.log();
121
+ }
122
+ } else if (sub === 'add') {
123
+ const pwd = process.argv[4];
124
+ const name = process.argv[5];
125
+ const dir = process.argv[6];
126
+ if (!pwd || !name || !dir) {
127
+ console.log('用法: myhi user add <密码> <名称> <目录>');
128
+ console.log('示例: myhi user add 1234 张三 D:\\project\\app1');
129
+ process.exit(1);
130
+ }
131
+ addUser(pwd, name, dir);
132
+ console.log(`[myhi] 已添加用户 "${name}" (目录: ${dir})`);
133
+ } else if (sub === 'remove' || sub === 'rm') {
134
+ const pwd = process.argv[4];
135
+ if (!pwd) {
136
+ console.log('用法: myhi user remove <密码>');
137
+ process.exit(1);
138
+ }
139
+ if (removeUser(pwd)) {
140
+ console.log(`[myhi] 已删除用户`);
141
+ } else {
142
+ console.log('[myhi] 未找到该密码对应的用户');
143
+ }
144
+ } else {
145
+ console.log(`
146
+ 用户管理命令:
147
+ myhi user list 列出所有用户
148
+ myhi user add <密码> <名称> <目录> 添加用户
149
+ myhi user remove <密码> 删除用户
150
+
151
+ 示例:
152
+ myhi user add 1234 张三 D:\\project\\app1
153
+ myhi user add 5678 李四 D:\\project\\app2
154
+ myhi user list
155
+ myhi user remove 1234
156
+ `);
157
+ }
158
+
106
159
  } else if (cmd === 'help' || cmd === '--help' || cmd === '-h') {
107
160
  console.log(`
108
161
  myhi — 基于 Web 的终端共享工具
@@ -118,6 +171,11 @@ myhi — 基于 Web 的终端共享工具
118
171
  myhi status 查看后台运行状态
119
172
  myhi log [N] 查看后台日志(最后 N 行,默认 50)
120
173
 
174
+ 用户管理:
175
+ myhi user list 列出所有用户
176
+ myhi user add <密码> <名称> <目录> 添加用户
177
+ myhi user remove <密码> 删除用户
178
+
121
179
  远程连接:
122
180
  myhi attach 列出活跃会话,交互式选择后附加
123
181
  myhi attach <id> 直接附加到指定会话
@@ -139,6 +197,7 @@ myhi — 基于 Web 的终端共享工具
139
197
  配置目录:~/.myhi/
140
198
  token 认证令牌(自动生成)
141
199
  password 登录密码(自动生成,可手动修改)
200
+ users.json 独占用户配置(myhi user 命令管理)
142
201
  roles.json 多用户角色配置(可选)
143
202
  sessions.json 会话持久化数据
144
203
  daemon.pid 后台进程 PID
@@ -150,6 +209,7 @@ myhi — 基于 Web 的终端共享工具
150
209
  myhi -p 8080 -d # 8080 端口后台启动
151
210
  MYHI_CWD=/project myhi # 指定会话工作目录
152
211
  myhi attach # 从另一台机器连接到会话
212
+ myhi user add 1234 张三 D:\\project # 添加用户
153
213
  `);
154
214
 
155
215
  } else if (cmd === '-d' || cmd === '--daemon' || cmd === 'daemon') {
package/dist/chat.html CHANGED
@@ -215,6 +215,9 @@
215
215
  <button class="top-btn" onclick="goBack()" title="返回">
216
216
  <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="M15 18l-6-6 6-6"/></svg>
217
217
  </button>
218
+ <button class="top-btn" id="logout-btn" onclick="doLogout()" title="退出" style="display:none;color:#f0883e">
219
+ <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="M9 21H5a2 2 0 01-2-2V5a2 2 0 012-2h4"/><polyline points="16 17 21 12 16 7"/><line x1="21" y1="12" x2="9" y2="12"/></svg>
220
+ </button>
218
221
  </div>
219
222
 
220
223
  <div id="status-bar">
@@ -328,6 +331,15 @@
328
331
  const ansi = new AnsiUp();
329
332
  ansi.use_classes = false;
330
333
 
334
+ // 加载用户信息(独占模式显示退出按钮)
335
+ fetch('/api/me').then(r => r.json()).then(data => {
336
+ if (data.exclusive) document.getElementById('logout-btn').style.display = '';
337
+ }).catch(() => {});
338
+ window.doLogout = () => {
339
+ if (!confirm('确定退出登录?当前所有会话将被关闭。')) return;
340
+ fetch('/logout', { method: 'POST' }).then(() => { location.href = '/login'; });
341
+ };
342
+
331
343
  // ── 状态 ──────────────────────────────────────
332
344
  const SESSION_ID = location.pathname.split('/').pop();
333
345
  const chatArea = document.getElementById('chat-area');
package/dist/index.html CHANGED
@@ -413,7 +413,11 @@
413
413
  <div id="app">
414
414
  <div id="header">
415
415
  <div class="logo">my<span>hi</span></div>
416
- <div id="conn-dot"></div>
416
+ <div style="display:flex;align-items:center;gap:0.6rem">
417
+ <span id="user-name" style="font-size:0.8rem;color:var(--muted)"></span>
418
+ <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>
419
+ <div id="conn-dot"></div>
420
+ </div>
417
421
  </div>
418
422
 
419
423
  <div id="list">
@@ -504,6 +508,30 @@
504
508
  const connDot = document.getElementById('conn-dot');
505
509
  const list = document.getElementById('list');
506
510
 
511
+ // 加载当前用户信息(独占模式显示用户名、退出按钮、锁定目录)
512
+ let _userDir = null;
513
+ fetch('/api/me').then(r => r.json()).then(data => {
514
+ if (data.exclusive && data.name) {
515
+ document.getElementById('user-name').textContent = data.name;
516
+ document.getElementById('logout-btn').style.display = '';
517
+ }
518
+ if (data.dir) {
519
+ _userDir = data.dir;
520
+ const cwdInput = document.getElementById('inp-cwd');
521
+ cwdInput.value = data.dir;
522
+ cwdInput.readOnly = true;
523
+ cwdInput.style.opacity = '0.6';
524
+ // 隐藏目录选择按钮和最近目录
525
+ cwdInput.parentElement.querySelector('button').style.display = 'none';
526
+ document.getElementById('recent-dirs').style.display = 'none';
527
+ }
528
+ }).catch(() => {});
529
+
530
+ function doLogout() {
531
+ if (!confirm('确定退出登录?当前所有会话将被关闭。')) return;
532
+ fetch('/logout', { method: 'POST' }).then(() => { location.href = '/login'; });
533
+ }
534
+
507
535
  // HTML 转义,防止 XSS
508
536
  function esc(s) {
509
537
  const d = document.createElement('div');