@wendongfly/myhi 1.3.9 → 1.3.10
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/dist/index.js +1 -1
- package/dist/index.min.js +1 -1
- package/package.json +1 -1
package/dist/index.min.js
CHANGED
|
@@ -367,7 +367,7 @@ Content-Length: `+C+`\r
|
|
|
367
367
|
helper = store --file ${l.replace(/\\/g,"/")}
|
|
368
368
|
[user]
|
|
369
369
|
name = ${g}
|
|
370
|
-
`,{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(),((w,p)=>{const{name:o}=w.body||{};if(!o)return p.json({ok:!1,error:"\u8BF7\u6307\u5B9A\u7528\u6237\u540D\u79F0"});if(loadUsers(),!listUsers().find((g=>g.name===o)))return p.json({ok:!1,error:"\u7528\u6237\u4E0D\u5B58\u5728"});try{const g=(0,external_path_.join)(configDir,"users.json"),m=JSON.parse((0,external_fs_.readFileSync)(g,"utf8"));for(const[h,l]of Object.entries(m.users||{}))if(l.name===o){delete m.users[h];break}(0,external_fs_.writeFileSync)(g,JSON.stringify(m,null,2),{mode:384}),loadUsers(),console.log(`[\u7BA1\u7406] \u5220\u9664\u7528\u6237 ${o}`),p.json({ok:!0})}catch(g){p.json({ok:!1,error:g.message})}})),app.post("/api/admin/user/password",checkAdminAuth,express.json(),((w,p)=>{const{name:o,password:g}=w.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[f,i]of Object.entries(h.users||{}))if(i.name===o){l=i,delete h.users[f];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(),((w,p)=>{const{password:o}=w.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,((w,p)=>{const o=w.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:(h,l,f)=>{const i=l.originalname.match(/\.[^.]+$/)?.[0]||".jpg";f(null,`${Date.now()}-${(0,external_crypto_.randomBytes)(3).toString("hex")}${i}`)}}),limits:{fileSize:20*1024*1024},fileFilter:(h,l,f)=>f(null,l.mimetype.startsWith("image/"))}).single("image")(w,p,(h=>{if(h)return p.status(400).json({error:h.message});if(!w.file)return p.status(400).json({error:"\u6CA1\u6709\u56FE\u7247"});p.json({path:(0,external_path_.resolve)(w.file.path).replace(/\\/g,"/")})}))})),app.get("/qr/:sessionId",checkAuth,(async(w,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/${w.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(((w,p)=>{const o=w.handshake.headers.cookie;if(hasValidSession(o)){const m=getUserInfo(o);return w.data.role=m?.role||"admin",w.data.userName=m?.name||"\u7528\u6237",w.data.dir=m?.dir||null,p()}const g=w.handshake.auth?.password;if(g){loadUsers();const m=lookupUser(g);if(m)return w.data.role="operator",w.data.userName=m.name,w.data.dir=m.dir,p()}if((w.handshake.auth?.token||w.handshake.query?.token)===TOKEN)return w.data.role="admin",w.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 w=!1;for(const[,p]of io.sockets.sockets)if(p.data.userName===activeUser.name){w=!0;break}if(w){_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[,w]of io.sockets.sockets)w.emit("sessions",manager.list(w.data.userName,w.data.role==="admin"))}),100))}io.on("connection",(w=>{const p=w.handshake.address?.replace("::ffff:","")||"?";console.log(`[\u8FDE\u63A5] ${w.data.userName}(${w.data.role}) \u4ECE ${p} \u63A5\u5165 id=${w.id}`),_exclusiveReleaseTimer&&activeUser&&w.data.userName===activeUser.name&&(clearTimeout(_exclusiveReleaseTimer),_exclusiveReleaseTimer=null);let o=null,g=null,m=null,h=null,l=null;function f(){o&&(o.isController(w.id)&&(o.releaseControl(),io.emit("control-changed",{sessionId:o.id,holder:null,holderName:null})),o.removeViewer(w.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)}w.on("join",(i=>{const n=manager.get(i);if(!n){w.emit("error",{message:`\u4F1A\u8BDD ${i} \u672A\u627E\u5230`});return}if(f(),o=n,o.addViewer(w.id,w.data.userName),n.mode==="agent"?(m=s=>w.emit("agent:message",s),o.on("agent:message",m),h=s=>w.emit("agent:busy",s),o.on("agent:busy",h),l=s=>w.emit("agent:error",s),o.on("agent:error",l)):(g=s=>w.emit("output",s),o.on("data",g),o.once("exit",(s=>{w.emit("session-exit",{code:s}),g&&o?.off("data",g)})),spawnLocalTerminal(i,n.title)),console.log(`[\u52A0\u5165] ${w.data.userName} \u52A0\u5165\u4F1A\u8BDD "${n.title}" (${i}) \u6A21\u5F0F=${n.mode||"pty"}`),w.emit("joined",{...n.toJSON(),role:w.data.role}),n.mode==="agent")n._history?.length&&w.emit("agent:history",n._history);else if(n._scrollback?.length){const s=n._scrollback;if(s.length<=4096)w.emit("output",s);else{let r=0;(function e(){r>=s.length||(w.emit("output",s.slice(r,r+4096)),r+=4096,setImmediate(e))})()}}broadcastSessions()})),w.on("disconnect",(()=>{console.log(`[\u65AD\u5F00] ${w.data.userName} \u79BB\u5F00\u4F1A\u8BDD "${o?.title||"?"}" id=${w.id}`),f(),broadcastSessions(),checkExclusiveRelease(),manager.persist()})),w.on("agent:query",(async({prompt:i}={})=>{if(!o||o.mode!=="agent"){w.emit("agent:error",{message:"\u5F53\u524D\u4E0D\u662F Agent \u6A21\u5F0F\u4F1A\u8BDD"});return}if(!o.isController(w.id)){w.emit("control-denied",{reason:"\u4F60\u6CA1\u6709\u5F53\u524D\u4F1A\u8BDD\u7684\u63A7\u5236\u6743"});return}if(o.isBusy){w.emit("agent:error",{message:"\u6B63\u5728\u5904\u7406\u4E2D\uFF0C\u8BF7\u7B49\u5F85\u5B8C\u6210"});return}try{await o.query(i)}catch(n){console.error("[agent:query] \u9519\u8BEF:",n.message);try{w.connected&&w.emit("agent:error",{message:n.message})}catch{}}})),w.on("agent:interrupt",(()=>{!o||o.mode!=="agent"||o.interrupt()})),w.on("create-agent",((i,n)=>{if(!hasPermission(w.data.role,"operator")){typeof n=="function"&&n({ok:!1,error:"\u6CA1\u6709\u521B\u5EFA\u4F1A\u8BDD\u7684\u6743\u9650"});return}if(w.data.dir){const r=(0,external_path_.resolve)((0,external_path_.normalize)(w.data.dir)),e=i?.cwd?(0,external_path_.resolve)((0,external_path_.normalize)(i.cwd)):null;if(e&&e!==r&&!e.startsWith(r+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||(i={...i,cwd:r})}const s=i?.cwd||process.env.MYHI_CWD||process.cwd();if(!(0,external_fs_.existsSync)(s))try{(0,external_fs_.mkdirSync)(s,{recursive:!0}),console.log(`[\u521B\u5EFA] \u81EA\u52A8\u521B\u5EFA\u76EE\u5F55: ${s}`)}catch(r){typeof n=="function"&&n({ok:!1,error:`\u521B\u5EFA\u76EE\u5F55\u5931\u8D25: ${r.message}`});return}try{const r=manager.createAgent({...i,owner:w.data.userName,userDir:w.data.dir});console.log(`[\u4F1A\u8BDD] ${w.data.userName} \u521B\u5EFA Agent \u4F1A\u8BDD "${r.title}" (${r.id})`),broadcastSessions(),typeof n=="function"&&n({ok:!0,session:r.toJSON()})}catch(r){typeof n=="function"&&n({ok:!1,error:r.message})}})),w.on("input",(i=>{if(!(i==null||!o)&&o.mode!=="agent"){if(!o.isController(w.id)){w.emit("control-denied",{reason:"\u4F60\u6CA1\u6709\u5F53\u524D\u4F1A\u8BDD\u7684\u63A7\u5236\u6743"});return}o.lastInputTime=Date.now(),o.write(i)}})),w.on("take-control",(({sessionId:i}={})=>{const n=i?manager.get(i):o;if(n){if(!hasPermission(w.data.role,"operator")){w.emit("control-denied",{reason:"\u4F60\u7684\u89D2\u8272\u6CA1\u6709\u63A7\u5236\u6743\u9650"});return}if(n.controlHolder&&n.controlHolder!==w.id){const s=n.controlHolderName===w.data.userName;if(w.data.role!=="admin"&&!s){w.emit("control-denied",{reason:"\u5176\u4ED6\u7528\u6237\u6B63\u5728\u63A7\u5236\u4E2D\uFF0C\u8BF7\u7B49\u5F85\u91CA\u653E"});return}}n.takeControl(w.id,w.data.userName),console.log(`[\u63A7\u5236] ${w.data.userName} \u83B7\u53D6\u4F1A\u8BDD "${n.title}" \u7684\u63A7\u5236\u6743`),io.emit("control-changed",{sessionId:n.id,holder:w.id,holderName:w.data.userName})}})),w.on("release-control",(({sessionId:i}={})=>{const n=i?manager.get(i):o;!n||!n.isController(w.id)||(console.log(`[\u63A7\u5236] ${w.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}))})),w.on("rename",(({sessionId:i,title:n}={})=>{const s=i?manager.get(i):o;!s||!n||(s.title=n,io.emit("session-renamed",{sessionId:s.id,title:n}),broadcastSessions(),manager.persist())})),w.on("resize",(({cols:i,rows:n})=>o?.resize(i,n))),w.on("list",(()=>w.emit("sessions",manager.list(w.data.userName,w.data.role==="admin")))),w.on("dirs",((i,n)=>{if(typeof n!="function")return;const s=w.data.dir?(0,external_path_.resolve)((0,external_path_.normalize)(w.data.dir)):null,r=process.platform==="win32"?"\\":"/";let e=(i||s||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));s&&c!==s&&!c.startsWith(s+r)&&(e=s);try{const a=(0,external_fs_.readdirSync)(e,{withFileTypes:!0}).filter((v=>v.isDirectory()&&v.name!==".git")).map((v=>({name:v.name,path:e.replace(/[/\\]+$/,"")+r+v.name}))).sort(((v,b)=>v.name.localeCompare(b.name))),t=(0,external_path_.join)(e,".."),d=(0,external_path_.resolve)((0,external_path_.normalize)(t)),u=!s||d===s||d.startsWith(s+r);n({ok:!0,current:e,parent:u&&t!==e?t:null,dirs:a})}catch(a){n({ok:!1,error:a.message})}})),w.on("create",((i,n)=>{if(!hasPermission(w.data.role,"operator")){typeof n=="function"&&n({ok:!1,error:"\u6CA1\u6709\u521B\u5EFA\u4F1A\u8BDD\u7684\u6743\u9650"});return}if(w.data.dir){const r=(0,external_path_.resolve)((0,external_path_.normalize)(w.data.dir)),e=i?.cwd?(0,external_path_.resolve)((0,external_path_.normalize)(i.cwd)):null;if(e&&e!==r&&!e.startsWith(r+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||(i={...i,cwd:r})}const s=i?.cwd||process.env.MYHI_CWD||process.cwd();if(!(0,external_fs_.existsSync)(s))try{(0,external_fs_.mkdirSync)(s,{recursive:!0}),console.log(`[\u521B\u5EFA] \u81EA\u52A8\u521B\u5EFA\u76EE\u5F55: ${s}`)}catch(r){typeof n=="function"&&n({ok:!1,error:`\u521B\u5EFA\u76EE\u5F55\u5931\u8D25: ${r.message}`});return}try{const r=manager.create({...i,owner:w.data.userName,userDir:w.data.dir});console.log(`[\u4F1A\u8BDD] ${w.data.userName} \u521B\u5EFA PTY \u4F1A\u8BDD "${r.title}" (${r.id})`),broadcastSessions(),typeof n=="function"&&n({ok:!0,session:r.toJSON()})}catch(r){console.error("[\u521B\u5EFA] PTY \u542F\u52A8\u5931\u8D25:",r.message),typeof n=="function"&&n({ok:!1,error:r.message})}})),w.on("kill",(i=>{if(!hasPermission(w.data.role,"operator")){w.emit("error",{message:"\u6CA1\u6709\u5220\u9664\u4F1A\u8BDD\u7684\u6743\u9650"});return}console.log(`[\u4F1A\u8BDD] ${w.data.userName} \u5220\u9664\u4F1A\u8BDD ${i}`),manager.kill(i),_autoSpawned.delete(i),broadcastSessions()})),w.on("kick-user",(({socketId:i,sessionId:n}={},s)=>{if(!hasPermission(w.data.role,"admin")){typeof s=="function"&&s({ok:!1,error:"\u6CA1\u6709\u8E22\u51FA\u7528\u6237\u7684\u6743\u9650"});return}const r=io.sockets.sockets.get(i);if(!r){typeof s=="function"&&s({ok:!1,error:"\u76EE\u6807\u7528\u6237\u4E0D\u5728\u7EBF"});return}const e=r.data.userName||"\u672A\u77E5";console.log(`[\u8E22\u51FA] ${w.data.userName} \u8E22\u51FA\u4E86\u7528\u6237 ${e} (${i})`),r.emit("kicked",{reason:`\u4F60\u5DF2\u88AB\u7BA1\u7406\u5458 ${w.data.userName} \u8E22\u51FA`}),r.disconnect(!0),typeof s=="function"&&s({ok:!0,name:e})})),w.on("list-viewers",(({sessionId:i}={},n)=>{if(typeof n!="function")return;const s=i?manager.get(i):o;if(!s){n({ok:!1,error:"\u4F1A\u8BDD\u4E0D\u5B58\u5728"});return}const r=[];for(const e of s._viewers){const c=io.sockets.sockets.get(e);c&&r.push({socketId:e,userName:c.data.userName||"\u672A\u77E5",role:c.data.role||"viewer",isController:s.controlHolder===e})}n({ok:!0,viewers:r})}))}));const CONTROL_TIMEOUT=300*1e3;setInterval((()=>{for(const w of manager.listSessions()){w.controlHolder&&(!io.sockets.sockets.get(w.controlHolder)||w.lastInputTime&&Date.now()-w.lastInputTime>CONTROL_TIMEOUT)&&(w.releaseControl(),io.emit("control-changed",{sessionId:w.id,holder:null,holderName:null}));for(const p of w._viewers)io.sockets.sockets.has(p)||w.removeViewer(p)}}),60*1e3);function showStartupInfo(){const w=httpServer.address().port;PORT=w;const p=getTailscaleIP(),o=`http://${p}:${w}/admin`;console.log(`
|
|
370
|
+
`,{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(),((w,p)=>{const{name:o}=w.body||{};if(!o)return p.json({ok:!1,error:"\u8BF7\u6307\u5B9A\u7528\u6237\u540D\u79F0"});if(loadUsers(),!listUsers().find((g=>g.name===o)))return p.json({ok:!1,error:"\u7528\u6237\u4E0D\u5B58\u5728"});try{const g=(0,external_path_.join)(configDir,"users.json"),m=JSON.parse((0,external_fs_.readFileSync)(g,"utf8"));for(const[h,l]of Object.entries(m.users||{}))if(l.name===o){delete m.users[h];break}(0,external_fs_.writeFileSync)(g,JSON.stringify(m,null,2),{mode:384}),loadUsers(),console.log(`[\u7BA1\u7406] \u5220\u9664\u7528\u6237 ${o}`),p.json({ok:!0})}catch(g){p.json({ok:!1,error:g.message})}})),app.post("/api/admin/user/password",checkAdminAuth,express.json(),((w,p)=>{const{name:o,password:g}=w.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[f,i]of Object.entries(h.users||{}))if(i.name===o){l=i,delete h.users[f];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(),((w,p)=>{const{password:o}=w.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,((w,p)=>{const o=w.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:(h,l,f)=>{const i=l.originalname.match(/\.[^.]+$/)?.[0]||".jpg";f(null,`${Date.now()}-${(0,external_crypto_.randomBytes)(3).toString("hex")}${i}`)}}),limits:{fileSize:20*1024*1024},fileFilter:(h,l,f)=>f(null,l.mimetype.startsWith("image/"))}).single("image")(w,p,(h=>{if(h)return p.status(400).json({error:h.message});if(!w.file)return p.status(400).json({error:"\u6CA1\u6709\u56FE\u7247"});p.json({path:(0,external_path_.resolve)(w.file.path).replace(/\\/g,"/")})}))})),app.get("/qr/:sessionId",checkAuth,(async(w,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/${w.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(((w,p)=>{const o=w.handshake.headers.cookie;if(hasValidSession(o)){const m=getUserInfo(o);return w.data.role=m?.role||"admin",w.data.userName=m?.name||"\u7528\u6237",w.data.dir=m?.dir||null,p()}const g=w.handshake.auth?.password;if(g){loadUsers();const m=lookupUser(g);if(m)return w.data.role="operator",w.data.userName=m.name,w.data.dir=m.dir,p()}if((w.handshake.auth?.token||w.handshake.query?.token)===TOKEN)return w.data.role="admin",w.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 w=!1;for(const[,p]of io.sockets.sockets)if(p.data.userName===activeUser.name){w=!0;break}if(w){_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[,w]of io.sockets.sockets)w.emit("sessions",manager.list(w.data.userName,w.data.role==="admin"))}),100))}io.on("connection",(w=>{const p=w.handshake.address?.replace("::ffff:","")||"?";console.log(`[\u8FDE\u63A5] ${w.data.userName}(${w.data.role}) \u4ECE ${p} \u63A5\u5165 id=${w.id}`),_exclusiveReleaseTimer&&activeUser&&w.data.userName===activeUser.name&&(clearTimeout(_exclusiveReleaseTimer),_exclusiveReleaseTimer=null);let o=null,g=null,m=null,h=null,l=null;function f(){o&&(o.isController(w.id)&&(o.releaseControl(),io.emit("control-changed",{sessionId:o.id,holder:null,holderName:null})),o.removeViewer(w.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)}w.on("join",(i=>{const n=manager.get(i);if(!n){w.emit("error",{message:`\u4F1A\u8BDD ${i} \u672A\u627E\u5230`});return}if(f(),o=n,o.addViewer(w.id,w.data.userName),n.mode==="agent"?(m=s=>w.emit("agent:message",s),o.on("agent:message",m),h=s=>w.emit("agent:busy",s),o.on("agent:busy",h),l=s=>w.emit("agent:error",s),o.on("agent:error",l)):(g=s=>w.emit("output",s),o.on("data",g),o.once("exit",(s=>{w.emit("session-exit",{code:s}),g&&o?.off("data",g)})),spawnLocalTerminal(i,n.title)),console.log(`[\u52A0\u5165] ${w.data.userName} \u52A0\u5165\u4F1A\u8BDD "${n.title}" (${i}) \u6A21\u5F0F=${n.mode||"pty"}`),w.emit("joined",{...n.toJSON(),role:w.data.role}),n.mode==="agent")n._history?.length&&w.emit("agent:history",n._history);else if(n._scrollback?.length){const s=n._scrollback;if(s.length<=4096)w.emit("output",s);else{let r=0;(function e(){r>=s.length||(w.emit("output",s.slice(r,r+4096)),r+=4096,setImmediate(e))})()}}broadcastSessions()})),w.on("disconnect",(()=>{console.log(`[\u65AD\u5F00] ${w.data.userName} \u79BB\u5F00\u4F1A\u8BDD "${o?.title||"?"}" id=${w.id}`),f(),broadcastSessions(),checkExclusiveRelease(),manager.persist()})),w.on("agent:query",(async({prompt:i}={})=>{if(!o||o.mode!=="agent"){w.emit("agent:error",{message:"\u5F53\u524D\u4E0D\u662F Agent \u6A21\u5F0F\u4F1A\u8BDD"});return}if(!o.isController(w.id)){w.emit("control-denied",{reason:"\u4F60\u6CA1\u6709\u5F53\u524D\u4F1A\u8BDD\u7684\u63A7\u5236\u6743"});return}if(o.isBusy){w.emit("agent:error",{message:"\u6B63\u5728\u5904\u7406\u4E2D\uFF0C\u8BF7\u7B49\u5F85\u5B8C\u6210"});return}try{await o.query(i)}catch(n){console.error("[agent:query] \u9519\u8BEF:",n.message);try{w.connected&&w.emit("agent:error",{message:n.message})}catch{}}})),w.on("agent:interrupt",(()=>{!o||o.mode!=="agent"||o.interrupt()})),w.on("create-agent",((i,n)=>{if(!hasPermission(w.data.role,"operator")){typeof n=="function"&&n({ok:!1,error:"\u6CA1\u6709\u521B\u5EFA\u4F1A\u8BDD\u7684\u6743\u9650"});return}if(w.data.dir){const r=(0,external_path_.resolve)((0,external_path_.normalize)(w.data.dir)),e=i?.cwd?(0,external_path_.resolve)((0,external_path_.normalize)(i.cwd)):null;if(e&&e!==r&&!e.startsWith(r+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||(i={...i,cwd:r})}const s=i?.cwd||process.env.MYHI_CWD||process.cwd();if(!(0,external_fs_.existsSync)(s))try{(0,external_fs_.mkdirSync)(s,{recursive:!0}),console.log(`[\u521B\u5EFA] \u81EA\u52A8\u521B\u5EFA\u76EE\u5F55: ${s}`)}catch(r){typeof n=="function"&&n({ok:!1,error:`\u521B\u5EFA\u76EE\u5F55\u5931\u8D25: ${r.message}`});return}try{const r=manager.createAgent({...i,owner:w.data.userName,userDir:w.data.dir});console.log(`[\u4F1A\u8BDD] ${w.data.userName} \u521B\u5EFA Agent \u4F1A\u8BDD "${r.title}" (${r.id})`),broadcastSessions(),typeof n=="function"&&n({ok:!0,session:r.toJSON()})}catch(r){typeof n=="function"&&n({ok:!1,error:r.message})}})),w.on("input",(i=>{if(!(i==null||!o)&&o.mode!=="agent"){if(!o.isController(w.id)){w.emit("control-denied",{reason:"\u4F60\u6CA1\u6709\u5F53\u524D\u4F1A\u8BDD\u7684\u63A7\u5236\u6743"});return}o.lastInputTime=Date.now(),o.write(i)}})),w.on("take-control",(({sessionId:i}={})=>{const n=i?manager.get(i):o;if(n){if(!hasPermission(w.data.role,"operator")){w.emit("control-denied",{reason:"\u4F60\u7684\u89D2\u8272\u6CA1\u6709\u63A7\u5236\u6743\u9650"});return}if(n.controlHolder&&n.controlHolder!==w.id){const s=n.controlHolderName===w.data.userName;if(w.data.role!=="admin"&&!s){w.emit("control-denied",{reason:"\u5176\u4ED6\u7528\u6237\u6B63\u5728\u63A7\u5236\u4E2D\uFF0C\u8BF7\u7B49\u5F85\u91CA\u653E"});return}}n.takeControl(w.id,w.data.userName),console.log(`[\u63A7\u5236] ${w.data.userName} \u83B7\u53D6\u4F1A\u8BDD "${n.title}" \u7684\u63A7\u5236\u6743`),io.emit("control-changed",{sessionId:n.id,holder:w.id,holderName:w.data.userName})}})),w.on("release-control",(({sessionId:i}={})=>{const n=i?manager.get(i):o;!n||!n.isController(w.id)||(console.log(`[\u63A7\u5236] ${w.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}))})),w.on("set-mode",(({sessionId:i,mode:n}={})=>{const s=i?manager.get(i):o;s&&(s.mode==="agent"&&s.setPermissionMode?(s.setPermissionMode(n),console.log(`[\u6A21\u5F0F] ${w.data.userName} \u5207\u6362\u4F1A\u8BDD "${s.title}" \u4E3A ${n}`)):s.permissionMode=n,io.emit("mode-changed",{sessionId:s.id,mode:n}),manager.persist())})),w.on("rename",(({sessionId:i,title:n}={})=>{const s=i?manager.get(i):o;!s||!n||(s.title=n,io.emit("session-renamed",{sessionId:s.id,title:n}),broadcastSessions(),manager.persist())})),w.on("resize",(({cols:i,rows:n})=>o?.resize(i,n))),w.on("list",(()=>w.emit("sessions",manager.list(w.data.userName,w.data.role==="admin")))),w.on("dirs",((i,n)=>{if(typeof n!="function")return;const s=w.data.dir?(0,external_path_.resolve)((0,external_path_.normalize)(w.data.dir)):null,r=process.platform==="win32"?"\\":"/";let e=(i||s||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));s&&c!==s&&!c.startsWith(s+r)&&(e=s);try{const a=(0,external_fs_.readdirSync)(e,{withFileTypes:!0}).filter((v=>v.isDirectory()&&v.name!==".git")).map((v=>({name:v.name,path:e.replace(/[/\\]+$/,"")+r+v.name}))).sort(((v,b)=>v.name.localeCompare(b.name))),t=(0,external_path_.join)(e,".."),d=(0,external_path_.resolve)((0,external_path_.normalize)(t)),u=!s||d===s||d.startsWith(s+r);n({ok:!0,current:e,parent:u&&t!==e?t:null,dirs:a})}catch(a){n({ok:!1,error:a.message})}})),w.on("create",((i,n)=>{if(!hasPermission(w.data.role,"operator")){typeof n=="function"&&n({ok:!1,error:"\u6CA1\u6709\u521B\u5EFA\u4F1A\u8BDD\u7684\u6743\u9650"});return}if(w.data.dir){const r=(0,external_path_.resolve)((0,external_path_.normalize)(w.data.dir)),e=i?.cwd?(0,external_path_.resolve)((0,external_path_.normalize)(i.cwd)):null;if(e&&e!==r&&!e.startsWith(r+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||(i={...i,cwd:r})}const s=i?.cwd||process.env.MYHI_CWD||process.cwd();if(!(0,external_fs_.existsSync)(s))try{(0,external_fs_.mkdirSync)(s,{recursive:!0}),console.log(`[\u521B\u5EFA] \u81EA\u52A8\u521B\u5EFA\u76EE\u5F55: ${s}`)}catch(r){typeof n=="function"&&n({ok:!1,error:`\u521B\u5EFA\u76EE\u5F55\u5931\u8D25: ${r.message}`});return}try{const r=manager.create({...i,owner:w.data.userName,userDir:w.data.dir});console.log(`[\u4F1A\u8BDD] ${w.data.userName} \u521B\u5EFA PTY \u4F1A\u8BDD "${r.title}" (${r.id})`),broadcastSessions(),typeof n=="function"&&n({ok:!0,session:r.toJSON()})}catch(r){console.error("[\u521B\u5EFA] PTY \u542F\u52A8\u5931\u8D25:",r.message),typeof n=="function"&&n({ok:!1,error:r.message})}})),w.on("kill",(i=>{if(!hasPermission(w.data.role,"operator")){w.emit("error",{message:"\u6CA1\u6709\u5220\u9664\u4F1A\u8BDD\u7684\u6743\u9650"});return}console.log(`[\u4F1A\u8BDD] ${w.data.userName} \u5220\u9664\u4F1A\u8BDD ${i}`),manager.kill(i),_autoSpawned.delete(i),broadcastSessions()})),w.on("kick-user",(({socketId:i,sessionId:n}={},s)=>{if(!hasPermission(w.data.role,"admin")){typeof s=="function"&&s({ok:!1,error:"\u6CA1\u6709\u8E22\u51FA\u7528\u6237\u7684\u6743\u9650"});return}const r=io.sockets.sockets.get(i);if(!r){typeof s=="function"&&s({ok:!1,error:"\u76EE\u6807\u7528\u6237\u4E0D\u5728\u7EBF"});return}const e=r.data.userName||"\u672A\u77E5";console.log(`[\u8E22\u51FA] ${w.data.userName} \u8E22\u51FA\u4E86\u7528\u6237 ${e} (${i})`),r.emit("kicked",{reason:`\u4F60\u5DF2\u88AB\u7BA1\u7406\u5458 ${w.data.userName} \u8E22\u51FA`}),r.disconnect(!0),typeof s=="function"&&s({ok:!0,name:e})})),w.on("list-viewers",(({sessionId:i}={},n)=>{if(typeof n!="function")return;const s=i?manager.get(i):o;if(!s){n({ok:!1,error:"\u4F1A\u8BDD\u4E0D\u5B58\u5728"});return}const r=[];for(const e of s._viewers){const c=io.sockets.sockets.get(e);c&&r.push({socketId:e,userName:c.data.userName||"\u672A\u77E5",role:c.data.role||"viewer",isController:s.controlHolder===e})}n({ok:!0,viewers:r})}))}));const CONTROL_TIMEOUT=300*1e3;setInterval((()=>{for(const w of manager.listSessions()){w.controlHolder&&(!io.sockets.sockets.get(w.controlHolder)||w.lastInputTime&&Date.now()-w.lastInputTime>CONTROL_TIMEOUT)&&(w.releaseControl(),io.emit("control-changed",{sessionId:w.id,holder:null,holderName:null}));for(const p of w._viewers)io.sockets.sockets.has(p)||w.removeViewer(p)}}),60*1e3);function showStartupInfo(){const w=httpServer.address().port;PORT=w;const p=getTailscaleIP(),o=`http://${p}:${w}/admin`;console.log(`
|
|
371
371
|
\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(`
|
|
372
372
|
\u5730\u5740: http://${p}:${w}`),httpsServer&&console.log(` HTTPS: https://${p}:${HTTPS_PORT}`),console.log(` \u5BC6\u7801: ${PASSWORD} (\u7F16\u8F91 ~/.myhi/password \u53EF\u4FEE\u6539)`),console.log(`
|
|
373
373
|
\u626B\u63CF\u4E8C\u7EF4\u7801\u5728\u624B\u673A\u4E0A\u6253\u5F00:
|