@wendongfly/myhi 1.0.85 → 1.0.86

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
@@ -339,9 +339,10 @@ myhi — 基于 Web 的终端共享工具
339
339
  myhi exclusive off 关闭独占模式(多人同时使用,默认)
340
340
 
341
341
  远程连接:
342
- myhi attach 列出活跃会话,交互式选择后附加
343
- myhi attach <id> 直接附加到指定会话
344
- myhi attach --new 创建新会话并附加
342
+ myhi attach 列出活跃会话,交互式选择后附加
343
+ myhi attach <id> 直接附加到指定会话
344
+ myhi attach --new 创建新会话并附加
345
+ myhi attach --password <密码> 用指定用户身份连接(看到该用户的会话)
345
346
  myhi kick 列出在线用户,交互式选择后踢出
346
347
 
347
348
  快捷键(attach 模式):
package/dist/attach.js CHANGED
@@ -1,2 +1,2 @@
1
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:a}=c("socket.io-client");const p=process.env.MYHI_SERVER||"http://localhost:12300";const m=(0,r.readFileSync)((0,o.join)((0,n.homedir)(),".myhi","token"),"utf8").trim();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[myhi] 已附加到 "${s.title}" (${t}) 模式=${r?"agent":"pty"}\r\n`);process.stderr.write("[myhi] 按 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;case"control_request":process.stdout.write(`\r\n[权限请求] ${e.request?.tool_name||"?"}\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("[myhi] 已分离\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[myhi] 已分离\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[myhi] 获取控制权失败: ${e}\r\n`)}));e.on("control-changed",(({holder:t,holderName:s})=>{if(t&&t!==e.id){process.stderr.write(`\r\n[myhi] ${s||"其他用户"} 已获取控制权,当前为只读\r\n`)}else if(!t){process.stderr.write("\r\n[myhi] 控制权已释放\r\n")}}));e.on("kicked",(({reason:t})=>{process.stderr.write(`\r\n[myhi] ${t||"你已被管理员踢出"}\r\n`);cleanup(e);process.exit(1)}));e.on("session-exit",(({code:t})=>{process.stderr.write(`\r\n[myhi] 会话已退出 (code ${t})\r\n`);cleanup(e);process.exit(0)}));e.on("error",(({message:t})=>{process.stderr.write(`\r\n[myhi] 错误: ${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("[myhi] 没有活跃的会话。\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("[myhi] 无效的选择。\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(`[myhi] 创建失败: ${t?.error||"未知错误"}\n`);cleanup(e);process.exit(1)}process.stdout.write(`[myhi] 已创建会话 "${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 u=a(p,{transports:["websocket"],auth:{token:m}});u.on("connect_error",(e=>{process.stderr.write(`[myhi] 连接失败: ${e.message}\n`);process.exit(1)}));u.on("connect",(async()=>{const e=process.argv[2];if(e==="--new"){const e=process.argv[3];const t=process.argv[4]||undefined;const s=e?await createAndAttach(u,{title:e,initCmd:t}):await promptNew(u);attach(u,s)}else{const t=e||await pickSession(u);attach(u,t)}}));for(const e of["SIGINT","SIGTERM"]){process.on(e,(()=>{cleanup(u);process.exit(0)}))}process.on("exit",(()=>{try{process.stdin.setRawMode(false)}catch{}}));
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.MYHI_SERVER||"http://localhost:12300";let m=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]){m=u[e+1];u.splice(e,2);e--}}let d;if(m){d={password:m}}else{try{d={token:(0,r.readFileSync)((0,o.join)((0,n.homedir)(),".myhi","token"),"utf8").trim()}}catch{console.error("[myhi] 未找到 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[myhi] 已附加到 "${s.title}" (${t}) 模式=${r?"agent":"pty"}\r\n`);process.stderr.write("[myhi] 按 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;case"control_request":process.stdout.write(`\r\n[权限请求] ${e.request?.tool_name||"?"}\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("[myhi] 已分离\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[myhi] 已分离\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[myhi] 获取控制权失败: ${e}\r\n`)}));e.on("control-changed",(({holder:t,holderName:s})=>{if(t&&t!==e.id){process.stderr.write(`\r\n[myhi] ${s||"其他用户"} 已获取控制权,当前为只读\r\n`)}else if(!t){process.stderr.write("\r\n[myhi] 控制权已释放\r\n")}}));e.on("kicked",(({reason:t})=>{process.stderr.write(`\r\n[myhi] ${t||"你已被管理员踢出"}\r\n`);cleanup(e);process.exit(1)}));e.on("session-exit",(({code:t})=>{process.stderr.write(`\r\n[myhi] 会话已退出 (code ${t})\r\n`);cleanup(e);process.exit(0)}));e.on("error",(({message:t})=>{process.stderr.write(`\r\n[myhi] 错误: ${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("[myhi] 没有活跃的会话。\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("[myhi] 无效的选择。\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(`[myhi] 创建失败: ${t?.error||"未知错误"}\n`);cleanup(e);process.exit(1)}process.stdout.write(`[myhi] 已创建会话 "${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 l=p(a,{transports:["websocket","polling"],auth:d});l.on("connect_error",(e=>{process.stderr.write(`[myhi] 连接失败: ${e.message}\n`);process.exit(1)}));l.on("connect",(async()=>{const e=u[0];if(e==="--new"){const e=u[1];const t=u[2]||undefined;const s=e?await createAndAttach(l,{title:e,initCmd:t}):await promptNew(l);attach(l,s)}else{const t=e||await pickSession(l);attach(l,t)}}));for(const e of["SIGINT","SIGTERM"]){process.on(e,(()=>{cleanup(l);process.exit(0)}))}process.on("exit",(()=>{try{process.stdin.setRawMode(false)}catch{}}));