@wendongfly/myhi 1.0.112 → 1.0.114
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 +190 -0
- package/dist/index.js +1 -1
- package/dist/package.json +1 -1
- package/package.json +1 -1
package/bin/myhi.js
CHANGED
|
@@ -244,6 +244,191 @@ if (cmd === 'attach') {
|
|
|
244
244
|
`);
|
|
245
245
|
}
|
|
246
246
|
|
|
247
|
+
} else if (cmd === 'ssh') {
|
|
248
|
+
// SSH 账号管理:为 myhi 用户创建系统 SSH 账号,隔离目录权限
|
|
249
|
+
const { existsSync } = await import('fs');
|
|
250
|
+
const { platform } = await import('os');
|
|
251
|
+
const usersFile = join(configDir, 'users.json');
|
|
252
|
+
const isWindows = platform() === 'win32';
|
|
253
|
+
|
|
254
|
+
function loadUsersFile() {
|
|
255
|
+
try { if (existsSync(usersFile)) return JSON.parse(readFileSync(usersFile, 'utf8')); } catch {}
|
|
256
|
+
return { users: {} };
|
|
257
|
+
}
|
|
258
|
+
function saveUsersFile(data) {
|
|
259
|
+
mkdirSync(configDir, { recursive: true });
|
|
260
|
+
writeFileSync(usersFile, JSON.stringify(data, null, 2), { mode: 0o600 });
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
function run(cmd, opts = {}) {
|
|
264
|
+
try {
|
|
265
|
+
return execSync(cmd, { encoding: 'utf8', stdio: opts.silent ? 'pipe' : 'inherit', ...opts }).trim();
|
|
266
|
+
} catch (e) {
|
|
267
|
+
if (!opts.ignoreError) { console.error(`[myhi] 命令失败: ${cmd}\n${e.stderr || e.message}`); process.exit(1); }
|
|
268
|
+
return '';
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
function findUserByName(name) {
|
|
273
|
+
const data = loadUsersFile();
|
|
274
|
+
for (const [pwd, info] of Object.entries(data.users || {})) {
|
|
275
|
+
if (info.name === name) return { pwd, ...info };
|
|
276
|
+
}
|
|
277
|
+
return null;
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
const sub = args[args.indexOf('ssh') + 1] || process.argv[3];
|
|
281
|
+
|
|
282
|
+
if (sub === 'add' || sub === 'setup') {
|
|
283
|
+
const myName = process.argv[4];
|
|
284
|
+
const sshPass = process.argv[5];
|
|
285
|
+
if (!myName) {
|
|
286
|
+
console.log('用法: myhi ssh add <myhi用户名> [SSH密码]');
|
|
287
|
+
console.log('示例: myhi ssh add 张三 mypassword');
|
|
288
|
+
console.log('\n将为 myhi 用户创建系统 SSH 账号,绑定到该用户的项目目录');
|
|
289
|
+
process.exit(1);
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
const user = findUserByName(myName);
|
|
293
|
+
if (!user) { console.log(`[myhi] 未找到 myhi 用户 "${myName}",请先 myhi user add`); process.exit(1); }
|
|
294
|
+
|
|
295
|
+
const dir = user.dir;
|
|
296
|
+
// SSH 用户名:myhi-<name> (ASCII 化)
|
|
297
|
+
const sshUser = 'myhi-' + myName.replace(/[^a-zA-Z0-9_-]/g, () => Math.random().toString(36)[2]);
|
|
298
|
+
const sshPassword = sshPass || Math.random().toString(36).slice(2, 10) + 'A1!';
|
|
299
|
+
|
|
300
|
+
console.log(`[myhi] 创建 SSH 账号...`);
|
|
301
|
+
console.log(` myhi 用户: ${myName}`);
|
|
302
|
+
console.log(` 项目目录: ${dir}`);
|
|
303
|
+
console.log(` SSH 用户: ${sshUser}`);
|
|
304
|
+
|
|
305
|
+
if (isWindows) {
|
|
306
|
+
// ── Windows ──
|
|
307
|
+
// 创建本地用户
|
|
308
|
+
run(`net user "${sshUser}" "${sshPassword}" /add /y`, { silent: true, ignoreError: true });
|
|
309
|
+
run(`net user "${sshUser}" "${sshPassword}" /y`, { silent: true, ignoreError: true });
|
|
310
|
+
|
|
311
|
+
// 设置目录权限
|
|
312
|
+
mkdirSync(dir, { recursive: true });
|
|
313
|
+
// 上级目录:允许穿越(Traverse)但不允许列目录
|
|
314
|
+
const parent = join(dir, '..');
|
|
315
|
+
run(`icacls "${parent}" /grant "${sshUser}:(RC,S,X,RA,REA)"`, { silent: true, ignoreError: true });
|
|
316
|
+
// 用户自己的目录:完全控制
|
|
317
|
+
run(`icacls "${dir}" /grant "${sshUser}:(OI)(CI)F"`, { silent: true });
|
|
318
|
+
|
|
319
|
+
// 在用户 home 下创建 project 符号链接
|
|
320
|
+
const userHome = `C:\\Users\\${sshUser}`;
|
|
321
|
+
if (!existsSync(userHome)) mkdirSync(userHome, { recursive: true });
|
|
322
|
+
const link = join(userHome, 'project');
|
|
323
|
+
if (!existsSync(link)) {
|
|
324
|
+
run(`cmd /c mklink /D "${link}" "${dir}"`, { silent: true, ignoreError: true });
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
// 确保 OpenSSH Server 已启动
|
|
328
|
+
run('powershell -Command "Start-Service sshd 2>$null; Set-Service -Name sshd -StartupType Automatic 2>$null"', { silent: true, ignoreError: true });
|
|
329
|
+
|
|
330
|
+
} else {
|
|
331
|
+
// ── Linux / Ubuntu ──
|
|
332
|
+
// 创建系统用户,home 指向项目目录
|
|
333
|
+
run(`id "${sshUser}" 2>/dev/null || useradd -m -d "/home/${sshUser}" -s /bin/bash "${sshUser}"`, { silent: true, ignoreError: true });
|
|
334
|
+
run(`echo "${sshUser}:${sshPassword}" | chpasswd`, { silent: true });
|
|
335
|
+
|
|
336
|
+
mkdirSync(dir, { recursive: true });
|
|
337
|
+
|
|
338
|
+
// 在用户 home 下创建 project 符号链接
|
|
339
|
+
const homeDir = `/home/${sshUser}`;
|
|
340
|
+
const link = `${homeDir}/project`;
|
|
341
|
+
run(`ln -sfn "${dir}" "${link}"`, { silent: true, ignoreError: true });
|
|
342
|
+
run(`chown -h ${sshUser}:${sshUser} "${link}"`, { silent: true, ignoreError: true });
|
|
343
|
+
|
|
344
|
+
// 项目目录:设置为该用户所有
|
|
345
|
+
run(`chown -R ${sshUser}:${sshUser} "${dir}"`, { silent: true, ignoreError: true });
|
|
346
|
+
|
|
347
|
+
// 上级目录权限:移除 others 的读和执行
|
|
348
|
+
const parent = join(dir, '..');
|
|
349
|
+
run(`chmod 711 "${parent}"`, { silent: true, ignoreError: true });
|
|
350
|
+
// 用户自己的目录
|
|
351
|
+
run(`chmod 700 "${dir}"`, { silent: true, ignoreError: true });
|
|
352
|
+
|
|
353
|
+
// 确保 SSH 服务运行
|
|
354
|
+
run('systemctl enable ssh 2>/dev/null; systemctl start ssh 2>/dev/null || service ssh start 2>/dev/null', { silent: true, ignoreError: true });
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
// 保存 SSH 映射到 users.json
|
|
358
|
+
const data = loadUsersFile();
|
|
359
|
+
if (data.users[user.pwd]) {
|
|
360
|
+
data.users[user.pwd].sshUser = sshUser;
|
|
361
|
+
saveUsersFile(data);
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
console.log(`\n ✓ SSH 账号创建成功!`);
|
|
365
|
+
console.log(` ┌─────────────────────────────────`);
|
|
366
|
+
console.log(` │ SSH 用户名: ${sshUser}`);
|
|
367
|
+
console.log(` │ SSH 密码: ${sshPassword}`);
|
|
368
|
+
console.log(` │ 项目目录: ${dir}`);
|
|
369
|
+
console.log(` └─────────────────────────────────`);
|
|
370
|
+
console.log(`\n VS Code 连接方式:`);
|
|
371
|
+
console.log(` 1. 安装 Remote-SSH 扩展`);
|
|
372
|
+
console.log(` 2. 按 F1 → "Remote-SSH: Connect to Host"`);
|
|
373
|
+
console.log(` 3. 输入: ${sshUser}@<服务器IP>`);
|
|
374
|
+
console.log(` 4. 打开目录: ~/project`);
|
|
375
|
+
|
|
376
|
+
} else if (sub === 'list' || sub === 'ls') {
|
|
377
|
+
const data = loadUsersFile();
|
|
378
|
+
const entries = Object.entries(data.users || {}).filter(([, info]) => info.sshUser);
|
|
379
|
+
if (entries.length === 0) {
|
|
380
|
+
console.log('[myhi] 还没有 SSH 账号,使用 myhi ssh add <用户名> 创建');
|
|
381
|
+
} else {
|
|
382
|
+
console.log('\n myhi用户\tSSH账号\t\t\t目录');
|
|
383
|
+
console.log(' ────────\t───────\t\t\t────');
|
|
384
|
+
for (const [, info] of entries) {
|
|
385
|
+
console.log(` ${info.name}\t\t${info.sshUser}\t\t${info.dir}`);
|
|
386
|
+
}
|
|
387
|
+
console.log();
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
} else if (sub === 'remove' || sub === 'rm') {
|
|
391
|
+
const myName = process.argv[4];
|
|
392
|
+
if (!myName) { console.log('用法: myhi ssh remove <myhi用户名>'); process.exit(1); }
|
|
393
|
+
|
|
394
|
+
const user = findUserByName(myName);
|
|
395
|
+
if (!user || !user.sshUser) { console.log(`[myhi] 未找到 "${myName}" 的 SSH 账号`); process.exit(1); }
|
|
396
|
+
|
|
397
|
+
const sshUser = user.sshUser;
|
|
398
|
+
console.log(`[myhi] 删除 SSH 账号: ${sshUser}`);
|
|
399
|
+
|
|
400
|
+
if (isWindows) {
|
|
401
|
+
run(`net user "${sshUser}" /delete`, { silent: true, ignoreError: true });
|
|
402
|
+
} else {
|
|
403
|
+
run(`userdel "${sshUser}" 2>/dev/null`, { silent: true, ignoreError: true });
|
|
404
|
+
// 不删除 home,避免误删项目数据
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
const data = loadUsersFile();
|
|
408
|
+
if (data.users[user.pwd]) {
|
|
409
|
+
delete data.users[user.pwd].sshUser;
|
|
410
|
+
saveUsersFile(data);
|
|
411
|
+
}
|
|
412
|
+
console.log(`[myhi] 已删除 SSH 账号 "${sshUser}"(项目目录保留)`);
|
|
413
|
+
|
|
414
|
+
} else {
|
|
415
|
+
console.log(`
|
|
416
|
+
SSH 账号管理:为 myhi 用户创建独立的 SSH 开发账号
|
|
417
|
+
|
|
418
|
+
myhi ssh add <用户名> [密码] 创建 SSH 账号(绑定到 myhi 用户的项目目录)
|
|
419
|
+
myhi ssh list 列出所有 SSH 账号
|
|
420
|
+
myhi ssh remove <用户名> 删除 SSH 账号(保留项目目录)
|
|
421
|
+
|
|
422
|
+
示例:
|
|
423
|
+
myhi user add 1234 张三 /data/projects/zhangsan # 先添加 myhi 用户
|
|
424
|
+
myhi ssh add 张三 sshpass123 # 再创建 SSH 账号
|
|
425
|
+
myhi ssh list # 查看所有 SSH 账号
|
|
426
|
+
|
|
427
|
+
员工使用 VS Code Remote-SSH 连接后打开 ~/project 即可开发。
|
|
428
|
+
每个用户只能访问自己的项目目录,无法查看其他用户的文件。
|
|
429
|
+
`);
|
|
430
|
+
}
|
|
431
|
+
|
|
247
432
|
} else if (cmd === 'kick') {
|
|
248
433
|
// 踢出会话中的用户
|
|
249
434
|
const { createRequire } = await import('module');
|
|
@@ -365,6 +550,11 @@ myhi — 基于 Web 的终端共享工具
|
|
|
365
550
|
myhi exclusive on 开启独占模式(一次只能一个账号)
|
|
366
551
|
myhi exclusive off 关闭独占模式(多人同时使用,默认)
|
|
367
552
|
|
|
553
|
+
SSH 开发:
|
|
554
|
+
myhi ssh add <用户名> [密码] 为 myhi 用户创建 SSH 账号
|
|
555
|
+
myhi ssh list 列出所有 SSH 账号
|
|
556
|
+
myhi ssh remove <用户名> 删除 SSH 账号
|
|
557
|
+
|
|
368
558
|
远程连接:
|
|
369
559
|
myhi attach 列出活跃会话,交互式选择后附加
|
|
370
560
|
myhi attach <id> 直接附加到指定会话
|
package/dist/index.js
CHANGED
|
@@ -363,7 +363,7 @@ Content-Length: `+T+`\r
|
|
|
363
363
|
helper = store --file ${l.replace(/\\/g,"/")}
|
|
364
364
|
[user]
|
|
365
365
|
name = ${g}
|
|
366
|
-
`,{mode:384})}catch{}console.log(`[\u7BA1\u7406] \u6DFB\u52A0\u7528\u6237 ${g} (\u76EE\u5F55: ${m})`),p.json({ok:!0})}),app.delete("/api/admin/user",checkAdminAuth,express.json(),(_,p)=>{const{name:o}=_.body||{};if(!o)return p.json({ok:!1,error:"\u8BF7\u6307\u5B9A\u7528\u6237\u540D\u79F0"});if(loadUsers(),!listUsers().find(h=>h.name===o))return p.json({ok:!1,error:"\u7528\u6237\u4E0D\u5B58\u5728"});try{const h=(0,external_path_.join)(configDir,"users.json"),l=JSON.parse((0,external_fs_.readFileSync)(h,"utf8"));for(const[d,a]of Object.entries(l.users||{}))if(a.name===o){delete l.users[d];break}(0,external_fs_.writeFileSync)(h,JSON.stringify(l,null,2),{mode:384}),loadUsers(),console.log(`[\u7BA1\u7406] \u5220\u9664\u7528\u6237 ${o}`),p.json({ok:!0})}catch(h){p.json({ok:!1,error:h.message})}}),app.post("/api/admin/user/password",checkAdminAuth,express.json(),(_,p)=>{const{name:o,password:g}=_.body||{};if(!o||!g)return p.json({ok:!1,error:"\u540D\u79F0\u548C\u65B0\u5BC6\u7801\u5FC5\u586B"});try{const m=(0,external_path_.join)(configDir,"users.json"),h=JSON.parse((0,external_fs_.readFileSync)(m,"utf8"));let l=null;for(const[d,a]of Object.entries(h.users||{}))if(a.name===o){l=a,delete h.users[d];break}if(!l)return p.json({ok:!1,error:"\u7528\u6237\u4E0D\u5B58\u5728"});h.users[g]=l,(0,external_fs_.writeFileSync)(m,JSON.stringify(h,null,2),{mode:384}),loadUsers(),console.log(`[\u7BA1\u7406] \u4FEE\u6539\u7528\u6237 ${o} \u7684\u5BC6\u7801`),p.json({ok:!0})}catch(m){p.json({ok:!1,error:m.message})}}),app.post("/api/admin/password",checkAdminAuth,express.json(),(_,p)=>{const{password:o}=_.body||{};if(!o||o.length<4)return p.json({ok:!1,error:"\u5BC6\u7801\u81F3\u5C114\u4F4D"});try{(0,external_fs_.writeFileSync)((0,external_path_.join)(configDir,"password"),o,{mode:384}),PASSWORD=o,p.json({ok:!0}),console.log("[\u7BA1\u7406] \u7BA1\u7406\u5BC6\u7801\u5DF2\u4FEE\u6539")}catch(g){p.json({ok:!1,error:g.message})}}),app.post("/upload",checkAuth,(_,p)=>{const o=_.query.sessionId,g=o?manager.get(o):null,m=g?.cwd?(0,external_path_.join)(g.cwd,"upload"):uploadDir;(0,external_fs_.mkdirSync)(m,{recursive:!0}),multer({storage:multer.diskStorage({destination:m,filename:(l,d,a)=>{const n=d.originalname.match(/\.[^.]+$/)?.[0]||".jpg";a(null,`${Date.now()}-${(0,external_crypto_.randomBytes)(3).toString("hex")}${n}`)}}),limits:{fileSize:20*1024*1024},fileFilter:(l,d,a)=>a(null,d.mimetype.startsWith("image/"))}).single("image")(_,p,l=>{if(l)return p.status(400).json({error:l.message});if(!_.file)return p.status(400).json({error:"\u6CA1\u6709\u56FE\u7247"});p.json({path:(0,external_path_.resolve)(_.file.path).replace(/\\/g,"/")})})}),app.get("/qr/:sessionId",checkAuth,async(_,p)=>{const o=getTailscaleIP(),g=(0,external_crypto_.randomBytes)(16).toString("hex");userSessions.set(g,{role:"admin",name:"\u626B\u7801\u7528\u6237",onetime:!0,lastUsed:Date.now()});const m=`http://${o}:${PORT}/terminal/${_.params.sessionId}?token=${g}`;try{const h=await lib.toString(m,{type:"svg"});p.setHeader("Content-Type","image/svg+xml"),p.send(h)}catch(h){p.status(500).send(h.message)}}),io.use((_,p)=>{const o=_.handshake.headers.cookie;if(hasValidSession(o)){const h=getUserInfo(o);return _.data.role=h?.role||"admin",_.data.userName=h?.name||"\u7528\u6237",_.data.dir=h?.dir||null,p()}const g=_.handshake.auth?.password;if(g){loadUsers();const h=lookupUser(g);if(h)return _.data.role="operator",_.data.userName=h.name,_.data.dir=h.dir,p()}if((_.handshake.auth?.token||_.handshake.query?.token)===TOKEN)return _.data.role="admin",_.data.userName="\u7BA1\u7406\u5458",p();p(new Error("\u672A\u6388\u6743"))});const EXCLUSIVE_RELEASE_DELAY=120*1e3;let _exclusiveReleaseTimer=null;function checkExclusiveRelease(){if(!isExclusiveMode()||!activeUser)return;let _=!1;for(const[,p]of io.sockets.sockets)if(p.data.userName===activeUser.name){_=!0;break}if(_){_exclusiveReleaseTimer&&(clearTimeout(_exclusiveReleaseTimer),_exclusiveReleaseTimer=null);return}_exclusiveReleaseTimer||(_exclusiveReleaseTimer=setTimeout(()=>{if(_exclusiveReleaseTimer=null,!!activeUser){for(const[,p]of io.sockets.sockets)if(p.data.userName===activeUser.name)return;console.log(`[\u72EC\u5360] ${activeUser.name} \u5DF2\u79BB\u7EBF\u8D85\u65F6\uFF0C\u91CA\u653E\u767B\u5F55\u72B6\u6001\uFF08\u4F1A\u8BDD\u4FDD\u7559\uFF09`);for(const p of activeUser.tokens)userSessions.delete(p);activeUser=null}},EXCLUSIVE_RELEASE_DELAY))}let _broadcastTimer=null;function broadcastSessions(){_broadcastTimer||(_broadcastTimer=setTimeout(()=>{_broadcastTimer=null;for(const[,_]of io.sockets.sockets)_.emit("sessions",manager.list(_.data.userName,_.data.role==="admin"))},100))}io.on("connection",_=>{const p=_.handshake.address?.replace("::ffff:","")||"?";console.log(`[\u8FDE\u63A5] ${_.data.userName}(${_.data.role}) \u4ECE ${p} \u63A5\u5165 id=${_.id}`),_exclusiveReleaseTimer&&activeUser&&_.data.userName===activeUser.name&&(clearTimeout(_exclusiveReleaseTimer),_exclusiveReleaseTimer=null);let o=null,g=null,m=null,h=null,l=null;function d(){o&&(o.isController(_.id)&&(o.releaseControl(),io.emit("control-changed",{sessionId:o.id,holder:null,holderName:null})),o.removeViewer(_.id),g&&o.off("data",g),m&&o.off("agent:message",m),h&&o.off("agent:busy",h),l&&o.off("agent:error",l),g=null,m=null,h=null,l=null)}_.on("join",a=>{const n=manager.get(a);if(!n){_.emit("error",{message:`\u4F1A\u8BDD ${a} \u672A\u627E\u5230`});return}if(d(),o=n,o.addViewer(_.id,_.data.userName),n.mode==="agent"?(m=r=>_.emit("agent:message",r),o.on("agent:message",m),h=r=>_.emit("agent:busy",r),o.on("agent:busy",h),l=r=>_.emit("agent:error",r),o.on("agent:error",l)):(g=r=>_.emit("output",r),o.on("data",g),o.once("exit",r=>{_.emit("session-exit",{code:r}),g&&o?.off("data",g)}),spawnLocalTerminal(a,n.title)),console.log(`[\u52A0\u5165] ${_.data.userName} \u52A0\u5165\u4F1A\u8BDD "${n.title}" (${a}) \u6A21\u5F0F=${n.mode||"pty"}`),_.emit("joined",{...n.toJSON(),role:_.data.role}),n.mode==="agent")n._history?.length&&_.emit("agent:history",n._history);else if(n._scrollback?.length){const t=n._scrollback;if(t.length<=4096)_.emit("output",t);else{let e=0;(function c(){e>=t.length||(_.emit("output",t.slice(e,e+4096)),e+=4096,setImmediate(c))})()}}broadcastSessions()}),_.on("disconnect",()=>{console.log(`[\u65AD\u5F00] ${_.data.userName} \u79BB\u5F00\u4F1A\u8BDD "${o?.title||"?"}" id=${_.id}`),d(),broadcastSessions(),checkExclusiveRelease()}),_.on("agent:query",async({prompt:a}={})=>{if(!o||o.mode!=="agent"){_.emit("agent:error",{message:"\u5F53\u524D\u4E0D\u662F Agent \u6A21\u5F0F\u4F1A\u8BDD"});return}if(!o.isController(_.id)){_.emit("control-denied",{reason:"\u4F60\u6CA1\u6709\u5F53\u524D\u4F1A\u8BDD\u7684\u63A7\u5236\u6743"});return}if(o.isBusy){_.emit("agent:error",{message:"\u6B63\u5728\u5904\u7406\u4E2D\uFF0C\u8BF7\u7B49\u5F85\u5B8C\u6210"});return}try{await o.query(a)}catch(n){console.error("[agent:query] \u9519\u8BEF:",n.message);try{_.connected&&_.emit("agent:error",{message:n.message})}catch{}}}),_.on("agent:interrupt",()=>{!o||o.mode!=="agent"||o.interrupt()}),_.on("agent:permission",({requestId:a,allow:n}={})=>{if(!(!o||o.mode!=="agent")){if(!o.isController(_.id)){_.emit("control-denied",{reason:"\u4F60\u6CA1\u6709\u5F53\u524D\u4F1A\u8BDD\u7684\u63A7\u5236\u6743"});return}o.respondPermission(a,n)}}),_.on("create-agent",(a,n)=>{if(!hasPermission(_.data.role,"operator")){typeof n=="function"&&n({ok:!1,error:"\u6CA1\u6709\u521B\u5EFA\u4F1A\u8BDD\u7684\u6743\u9650"});return}if(_.data.dir){const t=(0,external_path_.resolve)((0,external_path_.normalize)(_.data.dir)),e=a?.cwd?(0,external_path_.resolve)((0,external_path_.normalize)(a.cwd)):null;if(e&&e!==t&&!e.startsWith(t+external_path_.sep)){typeof n=="function"&&n({ok:!1,error:"\u4E0D\u80FD\u5728\u7ED1\u5B9A\u76EE\u5F55\u4E4B\u5916\u521B\u5EFA\u4F1A\u8BDD"});return}e||(a={...a,cwd:t})}const r=a?.cwd||process.env.MYHI_CWD||process.cwd();if(!(0,external_fs_.existsSync)(r))try{(0,external_fs_.mkdirSync)(r,{recursive:!0}),console.log(`[\u521B\u5EFA] \u81EA\u52A8\u521B\u5EFA\u76EE\u5F55: ${r}`)}catch(t){typeof n=="function"&&n({ok:!1,error:`\u521B\u5EFA\u76EE\u5F55\u5931\u8D25: ${t.message}`});return}try{const t=manager.createAgent({...a,owner:_.data.userName,userDir:_.data.dir});console.log(`[\u4F1A\u8BDD] ${_.data.userName} \u521B\u5EFA Agent \u4F1A\u8BDD "${t.title}" (${t.id})`),broadcastSessions(),typeof n=="function"&&n({ok:!0,session:t.toJSON()})}catch(t){typeof n=="function"&&n({ok:!1,error:t.message})}}),_.on("input",a=>{if(!(a==null||!o)&&o.mode!=="agent"){if(!o.isController(_.id)){_.emit("control-denied",{reason:"\u4F60\u6CA1\u6709\u5F53\u524D\u4F1A\u8BDD\u7684\u63A7\u5236\u6743"});return}o.lastInputTime=Date.now(),o.write(a)}}),_.on("take-control",({sessionId:a}={})=>{const n=a?manager.get(a):o;if(n){if(!hasPermission(_.data.role,"operator")){_.emit("control-denied",{reason:"\u4F60\u7684\u89D2\u8272\u6CA1\u6709\u63A7\u5236\u6743\u9650"});return}if(n.controlHolder&&n.controlHolder!==_.id&&_.data.role!=="admin"){_.emit("control-denied",{reason:"\u5176\u4ED6\u7528\u6237\u6B63\u5728\u63A7\u5236\u4E2D\uFF0C\u8BF7\u7B49\u5F85\u91CA\u653E"});return}n.takeControl(_.id,_.data.userName),console.log(`[\u63A7\u5236] ${_.data.userName} \u83B7\u53D6\u4F1A\u8BDD "${n.title}" \u7684\u63A7\u5236\u6743`),io.emit("control-changed",{sessionId:n.id,holder:_.id,holderName:_.data.userName})}}),_.on("release-control",({sessionId:a}={})=>{const n=a?manager.get(a):o;n&&n.isController(_.id)&&(console.log(`[\u63A7\u5236] ${_.data.userName} \u91CA\u653E\u4F1A\u8BDD "${n.title}" \u7684\u63A7\u5236\u6743`),n.releaseControl(),io.emit("control-changed",{sessionId:n.id,holder:null,holderName:null}))}),_.on("set-mode",({sessionId:a,mode:n}={})=>{const r=a?manager.get(a):o;r&&(r.mode==="agent"&&r.setPermissionMode?(r.setPermissionMode(n),console.log(`[\u6A21\u5F0F] ${_.data.userName} \u5207\u6362\u4F1A\u8BDD "${r.title}" \u4E3A ${n}\uFF08Agent \u8FDB\u7A0B\u5C06\u91CD\u542F\uFF09`)):r.permissionMode=n,io.emit("mode-changed",{sessionId:r.id,mode:n}))}),_.on("rename",({sessionId:a,title:n}={})=>{const r=a?manager.get(a):o;!r||!n||(r.title=n,io.emit("session-renamed",{sessionId:r.id,title:n}),broadcastSessions())}),_.on("resize",({cols:a,rows:n})=>o?.resize(a,n)),_.on("list",()=>_.emit("sessions",manager.list(_.data.userName,_.data.role==="admin"))),_.on("dirs",(a,n)=>{if(typeof n!="function")return;const r=_.data.dir?(0,external_path_.resolve)((0,external_path_.normalize)(_.data.dir)):null,t=process.platform==="win32"?"\\":"/";let e=(a||r||process.env.MYHI_CWD||(0,external_os_.homedir)()).replace(/[/\\]+$/,"")||(0,external_os_.homedir)();process.platform==="win32"&&/^[A-Za-z]:$/.test(e)&&(e+="\\");const c=(0,external_path_.resolve)((0,external_path_.normalize)(e));r&&c!==r&&!c.startsWith(r+t)&&(e=r);try{const s=(0,external_fs_.readdirSync)(e,{withFileTypes:!0}).filter(v=>v.isDirectory()&&!v.name.startsWith(".")).map(v=>({name:v.name,path:e.replace(/[/\\]+$/,"")+t+v.name})).sort((v,b)=>v.name.localeCompare(b.name)),i=(0,external_path_.join)(e,".."),f=(0,external_path_.resolve)((0,external_path_.normalize)(i)),u=!r||f===r||f.startsWith(r+t);n({ok:!0,current:e,parent:u&&i!==e?i:null,dirs:s})}catch(s){n({ok:!1,error:s.message})}}),_.on("create",(a,n)=>{if(!hasPermission(_.data.role,"operator")){typeof n=="function"&&n({ok:!1,error:"\u6CA1\u6709\u521B\u5EFA\u4F1A\u8BDD\u7684\u6743\u9650"});return}if(_.data.dir){const t=(0,external_path_.resolve)((0,external_path_.normalize)(_.data.dir)),e=a?.cwd?(0,external_path_.resolve)((0,external_path_.normalize)(a.cwd)):null;if(e&&e!==t&&!e.startsWith(t+external_path_.sep)){typeof n=="function"&&n({ok:!1,error:"\u4E0D\u80FD\u5728\u7ED1\u5B9A\u76EE\u5F55\u4E4B\u5916\u521B\u5EFA\u4F1A\u8BDD"});return}e||(a={...a,cwd:t})}const r=a?.cwd||process.env.MYHI_CWD||process.cwd();if(!(0,external_fs_.existsSync)(r))try{(0,external_fs_.mkdirSync)(r,{recursive:!0}),console.log(`[\u521B\u5EFA] \u81EA\u52A8\u521B\u5EFA\u76EE\u5F55: ${r}`)}catch(t){typeof n=="function"&&n({ok:!1,error:`\u521B\u5EFA\u76EE\u5F55\u5931\u8D25: ${t.message}`});return}try{const t=manager.create({...a,owner:_.data.userName,userDir:_.data.dir});console.log(`[\u4F1A\u8BDD] ${_.data.userName} \u521B\u5EFA PTY \u4F1A\u8BDD "${t.title}" (${t.id})`),broadcastSessions(),typeof n=="function"&&n({ok:!0,session:t.toJSON()})}catch(t){console.error("[\u521B\u5EFA] PTY \u542F\u52A8\u5931\u8D25:",t.message),typeof n=="function"&&n({ok:!1,error:t.message})}}),_.on("kill",a=>{if(!hasPermission(_.data.role,"operator")){_.emit("error",{message:"\u6CA1\u6709\u5220\u9664\u4F1A\u8BDD\u7684\u6743\u9650"});return}console.log(`[\u4F1A\u8BDD] ${_.data.userName} \u5220\u9664\u4F1A\u8BDD ${a}`),manager.kill(a),_autoSpawned.delete(a),broadcastSessions()}),_.on("kick-user",({socketId:a,sessionId:n}={},r)=>{if(!hasPermission(_.data.role,"admin")){typeof r=="function"&&r({ok:!1,error:"\u6CA1\u6709\u8E22\u51FA\u7528\u6237\u7684\u6743\u9650"});return}const t=io.sockets.sockets.get(a);if(!t){typeof r=="function"&&r({ok:!1,error:"\u76EE\u6807\u7528\u6237\u4E0D\u5728\u7EBF"});return}const e=t.data.userName||"\u672A\u77E5";console.log(`[\u8E22\u51FA] ${_.data.userName} \u8E22\u51FA\u4E86\u7528\u6237 ${e} (${a})`),t.emit("kicked",{reason:`\u4F60\u5DF2\u88AB\u7BA1\u7406\u5458 ${_.data.userName} \u8E22\u51FA`}),t.disconnect(!0),typeof r=="function"&&r({ok:!0,name:e})}),_.on("list-viewers",({sessionId:a}={},n)=>{if(typeof n!="function")return;const r=a?manager.get(a):o;if(!r){n({ok:!1,error:"\u4F1A\u8BDD\u4E0D\u5B58\u5728"});return}const t=[];for(const e of r._viewers){const c=io.sockets.sockets.get(e);c&&t.push({socketId:e,userName:c.data.userName||"\u672A\u77E5",role:c.data.role||"viewer",isController:r.controlHolder===e})}n({ok:!0,viewers:t})})});const CONTROL_TIMEOUT=300*1e3;setInterval(()=>{for(const _ of manager.listSessions()){_.controlHolder&&(!io.sockets.sockets.get(_.controlHolder)||_.lastInputTime&&Date.now()-_.lastInputTime>CONTROL_TIMEOUT)&&(_.releaseControl(),io.emit("control-changed",{sessionId:_.id,holder:null,holderName:null}));for(const p of _._viewers)io.sockets.sockets.has(p)||_.removeViewer(p)}},60*1e3);function showStartupInfo(){const _=httpServer.address().port;PORT=_;const p=getTailscaleIP(),o=`http://${p}:${_}/admin`;console.log(`
|
|
366
|
+
`,{mode:384})}catch{}console.log(`[\u7BA1\u7406] \u6DFB\u52A0\u7528\u6237 ${g} (\u76EE\u5F55: ${m})`),p.json({ok:!0})}),app.delete("/api/admin/user",checkAdminAuth,express.json(),(_,p)=>{const{name:o}=_.body||{};if(!o)return p.json({ok:!1,error:"\u8BF7\u6307\u5B9A\u7528\u6237\u540D\u79F0"});if(loadUsers(),!listUsers().find(h=>h.name===o))return p.json({ok:!1,error:"\u7528\u6237\u4E0D\u5B58\u5728"});try{const h=(0,external_path_.join)(configDir,"users.json"),l=JSON.parse((0,external_fs_.readFileSync)(h,"utf8"));for(const[d,a]of Object.entries(l.users||{}))if(a.name===o){delete l.users[d];break}(0,external_fs_.writeFileSync)(h,JSON.stringify(l,null,2),{mode:384}),loadUsers(),console.log(`[\u7BA1\u7406] \u5220\u9664\u7528\u6237 ${o}`),p.json({ok:!0})}catch(h){p.json({ok:!1,error:h.message})}}),app.post("/api/admin/user/password",checkAdminAuth,express.json(),(_,p)=>{const{name:o,password:g}=_.body||{};if(!o||!g)return p.json({ok:!1,error:"\u540D\u79F0\u548C\u65B0\u5BC6\u7801\u5FC5\u586B"});try{const m=(0,external_path_.join)(configDir,"users.json"),h=JSON.parse((0,external_fs_.readFileSync)(m,"utf8"));let l=null;for(const[d,a]of Object.entries(h.users||{}))if(a.name===o){l=a,delete h.users[d];break}if(!l)return p.json({ok:!1,error:"\u7528\u6237\u4E0D\u5B58\u5728"});h.users[g]=l,(0,external_fs_.writeFileSync)(m,JSON.stringify(h,null,2),{mode:384}),loadUsers(),console.log(`[\u7BA1\u7406] \u4FEE\u6539\u7528\u6237 ${o} \u7684\u5BC6\u7801`),p.json({ok:!0})}catch(m){p.json({ok:!1,error:m.message})}}),app.post("/api/admin/password",checkAdminAuth,express.json(),(_,p)=>{const{password:o}=_.body||{};if(!o||o.length<4)return p.json({ok:!1,error:"\u5BC6\u7801\u81F3\u5C114\u4F4D"});try{(0,external_fs_.writeFileSync)((0,external_path_.join)(configDir,"password"),o,{mode:384}),PASSWORD=o,p.json({ok:!0}),console.log("[\u7BA1\u7406] \u7BA1\u7406\u5BC6\u7801\u5DF2\u4FEE\u6539")}catch(g){p.json({ok:!1,error:g.message})}}),app.post("/upload",checkAuth,(_,p)=>{const o=_.query.sessionId,g=o?manager.get(o):null,m=g?.cwd?(0,external_path_.join)(g.cwd,"upload"):uploadDir;(0,external_fs_.mkdirSync)(m,{recursive:!0}),multer({storage:multer.diskStorage({destination:m,filename:(l,d,a)=>{const n=d.originalname.match(/\.[^.]+$/)?.[0]||".jpg";a(null,`${Date.now()}-${(0,external_crypto_.randomBytes)(3).toString("hex")}${n}`)}}),limits:{fileSize:20*1024*1024},fileFilter:(l,d,a)=>a(null,d.mimetype.startsWith("image/"))}).single("image")(_,p,l=>{if(l)return p.status(400).json({error:l.message});if(!_.file)return p.status(400).json({error:"\u6CA1\u6709\u56FE\u7247"});p.json({path:(0,external_path_.resolve)(_.file.path).replace(/\\/g,"/")})})}),app.get("/qr/:sessionId",checkAuth,async(_,p)=>{const o=getTailscaleIP(),g=(0,external_crypto_.randomBytes)(16).toString("hex");userSessions.set(g,{role:"admin",name:"\u626B\u7801\u7528\u6237",onetime:!0,lastUsed:Date.now()});const m=`http://${o}:${PORT}/terminal/${_.params.sessionId}?token=${g}`;try{const h=await lib.toString(m,{type:"svg"});p.setHeader("Content-Type","image/svg+xml"),p.send(h)}catch(h){p.status(500).send(h.message)}}),io.use((_,p)=>{const o=_.handshake.headers.cookie;if(hasValidSession(o)){const h=getUserInfo(o);return _.data.role=h?.role||"admin",_.data.userName=h?.name||"\u7528\u6237",_.data.dir=h?.dir||null,p()}const g=_.handshake.auth?.password;if(g){loadUsers();const h=lookupUser(g);if(h)return _.data.role="operator",_.data.userName=h.name,_.data.dir=h.dir,p()}if((_.handshake.auth?.token||_.handshake.query?.token)===TOKEN)return _.data.role="admin",_.data.userName="\u7BA1\u7406\u5458",p();p(new Error("\u672A\u6388\u6743"))});const EXCLUSIVE_RELEASE_DELAY=120*1e3;let _exclusiveReleaseTimer=null;function checkExclusiveRelease(){if(!isExclusiveMode()||!activeUser)return;let _=!1;for(const[,p]of io.sockets.sockets)if(p.data.userName===activeUser.name){_=!0;break}if(_){_exclusiveReleaseTimer&&(clearTimeout(_exclusiveReleaseTimer),_exclusiveReleaseTimer=null);return}_exclusiveReleaseTimer||(_exclusiveReleaseTimer=setTimeout(()=>{if(_exclusiveReleaseTimer=null,!!activeUser){for(const[,p]of io.sockets.sockets)if(p.data.userName===activeUser.name)return;console.log(`[\u72EC\u5360] ${activeUser.name} \u5DF2\u79BB\u7EBF\u8D85\u65F6\uFF0C\u91CA\u653E\u767B\u5F55\u72B6\u6001\uFF08\u4F1A\u8BDD\u4FDD\u7559\uFF09`);for(const p of activeUser.tokens)userSessions.delete(p);activeUser=null}},EXCLUSIVE_RELEASE_DELAY))}let _broadcastTimer=null;function broadcastSessions(){_broadcastTimer||(_broadcastTimer=setTimeout(()=>{_broadcastTimer=null;for(const[,_]of io.sockets.sockets)_.emit("sessions",manager.list(_.data.userName,_.data.role==="admin"))},100))}io.on("connection",_=>{const p=_.handshake.address?.replace("::ffff:","")||"?";console.log(`[\u8FDE\u63A5] ${_.data.userName}(${_.data.role}) \u4ECE ${p} \u63A5\u5165 id=${_.id}`),_exclusiveReleaseTimer&&activeUser&&_.data.userName===activeUser.name&&(clearTimeout(_exclusiveReleaseTimer),_exclusiveReleaseTimer=null);let o=null,g=null,m=null,h=null,l=null;function d(){o&&(o.isController(_.id)&&(o.releaseControl(),io.emit("control-changed",{sessionId:o.id,holder:null,holderName:null})),o.removeViewer(_.id),g&&o.off("data",g),m&&o.off("agent:message",m),h&&o.off("agent:busy",h),l&&o.off("agent:error",l),g=null,m=null,h=null,l=null)}_.on("join",a=>{const n=manager.get(a);if(!n){_.emit("error",{message:`\u4F1A\u8BDD ${a} \u672A\u627E\u5230`});return}if(d(),o=n,o.addViewer(_.id,_.data.userName),n.mode==="agent"?(m=r=>_.emit("agent:message",r),o.on("agent:message",m),h=r=>_.emit("agent:busy",r),o.on("agent:busy",h),l=r=>_.emit("agent:error",r),o.on("agent:error",l)):(g=r=>_.emit("output",r),o.on("data",g),o.once("exit",r=>{_.emit("session-exit",{code:r}),g&&o?.off("data",g)}),spawnLocalTerminal(a,n.title)),console.log(`[\u52A0\u5165] ${_.data.userName} \u52A0\u5165\u4F1A\u8BDD "${n.title}" (${a}) \u6A21\u5F0F=${n.mode||"pty"}`),_.emit("joined",{...n.toJSON(),role:_.data.role}),n.mode==="agent")n._history?.length&&_.emit("agent:history",n._history);else if(n._scrollback?.length){const t=n._scrollback;if(t.length<=4096)_.emit("output",t);else{let e=0;(function c(){e>=t.length||(_.emit("output",t.slice(e,e+4096)),e+=4096,setImmediate(c))})()}}broadcastSessions()}),_.on("disconnect",()=>{console.log(`[\u65AD\u5F00] ${_.data.userName} \u79BB\u5F00\u4F1A\u8BDD "${o?.title||"?"}" id=${_.id}`),d(),broadcastSessions(),checkExclusiveRelease()}),_.on("agent:query",async({prompt:a}={})=>{if(!o||o.mode!=="agent"){_.emit("agent:error",{message:"\u5F53\u524D\u4E0D\u662F Agent \u6A21\u5F0F\u4F1A\u8BDD"});return}if(!o.isController(_.id)){_.emit("control-denied",{reason:"\u4F60\u6CA1\u6709\u5F53\u524D\u4F1A\u8BDD\u7684\u63A7\u5236\u6743"});return}if(o.isBusy){_.emit("agent:error",{message:"\u6B63\u5728\u5904\u7406\u4E2D\uFF0C\u8BF7\u7B49\u5F85\u5B8C\u6210"});return}try{await o.query(a)}catch(n){console.error("[agent:query] \u9519\u8BEF:",n.message);try{_.connected&&_.emit("agent:error",{message:n.message})}catch{}}}),_.on("agent:interrupt",()=>{!o||o.mode!=="agent"||o.interrupt()}),_.on("agent:permission",({requestId:a,allow:n}={})=>{if(!(!o||o.mode!=="agent")){if(!o.isController(_.id)){_.emit("control-denied",{reason:"\u4F60\u6CA1\u6709\u5F53\u524D\u4F1A\u8BDD\u7684\u63A7\u5236\u6743"});return}o.respondPermission(a,n)}}),_.on("create-agent",(a,n)=>{if(!hasPermission(_.data.role,"operator")){typeof n=="function"&&n({ok:!1,error:"\u6CA1\u6709\u521B\u5EFA\u4F1A\u8BDD\u7684\u6743\u9650"});return}if(_.data.dir){const t=(0,external_path_.resolve)((0,external_path_.normalize)(_.data.dir)),e=a?.cwd?(0,external_path_.resolve)((0,external_path_.normalize)(a.cwd)):null;if(e&&e!==t&&!e.startsWith(t+external_path_.sep)){typeof n=="function"&&n({ok:!1,error:"\u4E0D\u80FD\u5728\u7ED1\u5B9A\u76EE\u5F55\u4E4B\u5916\u521B\u5EFA\u4F1A\u8BDD"});return}e||(a={...a,cwd:t})}const r=a?.cwd||process.env.MYHI_CWD||process.cwd();if(!(0,external_fs_.existsSync)(r))try{(0,external_fs_.mkdirSync)(r,{recursive:!0}),console.log(`[\u521B\u5EFA] \u81EA\u52A8\u521B\u5EFA\u76EE\u5F55: ${r}`)}catch(t){typeof n=="function"&&n({ok:!1,error:`\u521B\u5EFA\u76EE\u5F55\u5931\u8D25: ${t.message}`});return}try{const t=manager.createAgent({...a,owner:_.data.userName,userDir:_.data.dir});console.log(`[\u4F1A\u8BDD] ${_.data.userName} \u521B\u5EFA Agent \u4F1A\u8BDD "${t.title}" (${t.id})`),broadcastSessions(),typeof n=="function"&&n({ok:!0,session:t.toJSON()})}catch(t){typeof n=="function"&&n({ok:!1,error:t.message})}}),_.on("input",a=>{if(!(a==null||!o)&&o.mode!=="agent"){if(!o.isController(_.id)){_.emit("control-denied",{reason:"\u4F60\u6CA1\u6709\u5F53\u524D\u4F1A\u8BDD\u7684\u63A7\u5236\u6743"});return}o.lastInputTime=Date.now(),o.write(a)}}),_.on("take-control",({sessionId:a}={})=>{const n=a?manager.get(a):o;if(n){if(!hasPermission(_.data.role,"operator")){_.emit("control-denied",{reason:"\u4F60\u7684\u89D2\u8272\u6CA1\u6709\u63A7\u5236\u6743\u9650"});return}if(n.controlHolder&&n.controlHolder!==_.id){const r=n.controlHolderName===_.data.userName;if(_.data.role!=="admin"&&!r){_.emit("control-denied",{reason:"\u5176\u4ED6\u7528\u6237\u6B63\u5728\u63A7\u5236\u4E2D\uFF0C\u8BF7\u7B49\u5F85\u91CA\u653E"});return}}n.takeControl(_.id,_.data.userName),console.log(`[\u63A7\u5236] ${_.data.userName} \u83B7\u53D6\u4F1A\u8BDD "${n.title}" \u7684\u63A7\u5236\u6743`),io.emit("control-changed",{sessionId:n.id,holder:_.id,holderName:_.data.userName})}}),_.on("release-control",({sessionId:a}={})=>{const n=a?manager.get(a):o;n&&n.isController(_.id)&&(console.log(`[\u63A7\u5236] ${_.data.userName} \u91CA\u653E\u4F1A\u8BDD "${n.title}" \u7684\u63A7\u5236\u6743`),n.releaseControl(),io.emit("control-changed",{sessionId:n.id,holder:null,holderName:null}))}),_.on("set-mode",({sessionId:a,mode:n}={})=>{const r=a?manager.get(a):o;r&&(r.mode==="agent"&&r.setPermissionMode?(r.setPermissionMode(n),console.log(`[\u6A21\u5F0F] ${_.data.userName} \u5207\u6362\u4F1A\u8BDD "${r.title}" \u4E3A ${n}\uFF08Agent \u8FDB\u7A0B\u5C06\u91CD\u542F\uFF09`)):r.permissionMode=n,io.emit("mode-changed",{sessionId:r.id,mode:n}))}),_.on("rename",({sessionId:a,title:n}={})=>{const r=a?manager.get(a):o;!r||!n||(r.title=n,io.emit("session-renamed",{sessionId:r.id,title:n}),broadcastSessions())}),_.on("resize",({cols:a,rows:n})=>o?.resize(a,n)),_.on("list",()=>_.emit("sessions",manager.list(_.data.userName,_.data.role==="admin"))),_.on("dirs",(a,n)=>{if(typeof n!="function")return;const r=_.data.dir?(0,external_path_.resolve)((0,external_path_.normalize)(_.data.dir)):null,t=process.platform==="win32"?"\\":"/";let e=(a||r||process.env.MYHI_CWD||(0,external_os_.homedir)()).replace(/[/\\]+$/,"")||(0,external_os_.homedir)();process.platform==="win32"&&/^[A-Za-z]:$/.test(e)&&(e+="\\");const c=(0,external_path_.resolve)((0,external_path_.normalize)(e));r&&c!==r&&!c.startsWith(r+t)&&(e=r);try{const s=(0,external_fs_.readdirSync)(e,{withFileTypes:!0}).filter(v=>v.isDirectory()&&!v.name.startsWith(".")).map(v=>({name:v.name,path:e.replace(/[/\\]+$/,"")+t+v.name})).sort((v,b)=>v.name.localeCompare(b.name)),i=(0,external_path_.join)(e,".."),f=(0,external_path_.resolve)((0,external_path_.normalize)(i)),u=!r||f===r||f.startsWith(r+t);n({ok:!0,current:e,parent:u&&i!==e?i:null,dirs:s})}catch(s){n({ok:!1,error:s.message})}}),_.on("create",(a,n)=>{if(!hasPermission(_.data.role,"operator")){typeof n=="function"&&n({ok:!1,error:"\u6CA1\u6709\u521B\u5EFA\u4F1A\u8BDD\u7684\u6743\u9650"});return}if(_.data.dir){const t=(0,external_path_.resolve)((0,external_path_.normalize)(_.data.dir)),e=a?.cwd?(0,external_path_.resolve)((0,external_path_.normalize)(a.cwd)):null;if(e&&e!==t&&!e.startsWith(t+external_path_.sep)){typeof n=="function"&&n({ok:!1,error:"\u4E0D\u80FD\u5728\u7ED1\u5B9A\u76EE\u5F55\u4E4B\u5916\u521B\u5EFA\u4F1A\u8BDD"});return}e||(a={...a,cwd:t})}const r=a?.cwd||process.env.MYHI_CWD||process.cwd();if(!(0,external_fs_.existsSync)(r))try{(0,external_fs_.mkdirSync)(r,{recursive:!0}),console.log(`[\u521B\u5EFA] \u81EA\u52A8\u521B\u5EFA\u76EE\u5F55: ${r}`)}catch(t){typeof n=="function"&&n({ok:!1,error:`\u521B\u5EFA\u76EE\u5F55\u5931\u8D25: ${t.message}`});return}try{const t=manager.create({...a,owner:_.data.userName,userDir:_.data.dir});console.log(`[\u4F1A\u8BDD] ${_.data.userName} \u521B\u5EFA PTY \u4F1A\u8BDD "${t.title}" (${t.id})`),broadcastSessions(),typeof n=="function"&&n({ok:!0,session:t.toJSON()})}catch(t){console.error("[\u521B\u5EFA] PTY \u542F\u52A8\u5931\u8D25:",t.message),typeof n=="function"&&n({ok:!1,error:t.message})}}),_.on("kill",a=>{if(!hasPermission(_.data.role,"operator")){_.emit("error",{message:"\u6CA1\u6709\u5220\u9664\u4F1A\u8BDD\u7684\u6743\u9650"});return}console.log(`[\u4F1A\u8BDD] ${_.data.userName} \u5220\u9664\u4F1A\u8BDD ${a}`),manager.kill(a),_autoSpawned.delete(a),broadcastSessions()}),_.on("kick-user",({socketId:a,sessionId:n}={},r)=>{if(!hasPermission(_.data.role,"admin")){typeof r=="function"&&r({ok:!1,error:"\u6CA1\u6709\u8E22\u51FA\u7528\u6237\u7684\u6743\u9650"});return}const t=io.sockets.sockets.get(a);if(!t){typeof r=="function"&&r({ok:!1,error:"\u76EE\u6807\u7528\u6237\u4E0D\u5728\u7EBF"});return}const e=t.data.userName||"\u672A\u77E5";console.log(`[\u8E22\u51FA] ${_.data.userName} \u8E22\u51FA\u4E86\u7528\u6237 ${e} (${a})`),t.emit("kicked",{reason:`\u4F60\u5DF2\u88AB\u7BA1\u7406\u5458 ${_.data.userName} \u8E22\u51FA`}),t.disconnect(!0),typeof r=="function"&&r({ok:!0,name:e})}),_.on("list-viewers",({sessionId:a}={},n)=>{if(typeof n!="function")return;const r=a?manager.get(a):o;if(!r){n({ok:!1,error:"\u4F1A\u8BDD\u4E0D\u5B58\u5728"});return}const t=[];for(const e of r._viewers){const c=io.sockets.sockets.get(e);c&&t.push({socketId:e,userName:c.data.userName||"\u672A\u77E5",role:c.data.role||"viewer",isController:r.controlHolder===e})}n({ok:!0,viewers:t})})});const CONTROL_TIMEOUT=300*1e3;setInterval(()=>{for(const _ of manager.listSessions()){_.controlHolder&&(!io.sockets.sockets.get(_.controlHolder)||_.lastInputTime&&Date.now()-_.lastInputTime>CONTROL_TIMEOUT)&&(_.releaseControl(),io.emit("control-changed",{sessionId:_.id,holder:null,holderName:null}));for(const p of _._viewers)io.sockets.sockets.has(p)||_.removeViewer(p)}},60*1e3);function showStartupInfo(){const _=httpServer.address().port;PORT=_;const p=getTailscaleIP(),o=`http://${p}:${_}/admin`;console.log(`
|
|
367
367
|
\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501`),console.log(" myhi \u2014 \u57FA\u4E8E Tailscale \u7684 Web \u7EC8\u7AEF"),console.log("\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501"),console.log(`
|
|
368
368
|
\u5730\u5740: http://${p}:${_}`),console.log(` \u5BC6\u7801: ${PASSWORD} (\u7F16\u8F91 ~/.myhi/password \u53EF\u4FEE\u6539)`),console.log(`
|
|
369
369
|
\u626B\u63CF\u4E8C\u7EF4\u7801\u5728\u624B\u673A\u4E0A\u6253\u5F00:
|
package/dist/package.json
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"type":"module","version":"1.0.
|
|
1
|
+
{"type":"module","version":"1.0.114"}
|
package/package.json
CHANGED