@wendongfly/myhi 1.0.1 → 1.0.2
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/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -356,7 +356,7 @@ Content-Length: `+T+`\r
|
|
|
356
356
|
`).filter(Boolean).slice(-20);for(const u of m)try{const f=JSON.parse(u);if(f.type==="human"&&f.message?.content){const r=typeof f.message.content=="string"?f.message.content:f.message.content.find(t=>t.type==="text")?.text||"";r&&this._history.push({type:"user",content:r,timestamp:f.timestamp})}else f.type==="assistant"&&f.message?.content?this._history.push({type:"assistant",message:f.message,timestamp:f.timestamp}):f.type==="result"&&this._history.push({type:"result",total_cost_usd:f.costUSD||f.total_cost_usd,timestamp:f.timestamp})}catch{}return}}catch(d){console.warn("[agent] \u52A0\u8F7D\u6062\u590D\u4F1A\u8BDD\u5386\u53F2\u5931\u8D25:",d.message)}}async query(d){if(this._busy){this.emit("agent:error",{message:"\u6B63\u5728\u5904\u7406\u4E2D\uFF0C\u8BF7\u7B49\u5F85\u5B8C\u6210"});return}this._busy=!0,this.emit("agent:busy",!0),this._history.push({type:"user",content:d,timestamp:Date.now()});const c=["-p",d,"--output-format","stream-json","--verbose"];return this.permissionMode==="bypassPermissions"?c.push("--dangerously-skip-permissions"):this.permissionMode!=="default"&&c.push("--permission-mode",this.permissionMode),this.claudeSessionId&&c.push("--resume",this.claudeSessionId),new Promise((b,g)=>{const m=process.platform==="win32"?"claude.cmd":"claude",u={...process.env};delete u.CLAUDECODE,delete u.CLAUDE_CODE_ENTRYPOINT,delete u.CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC,this._proc=(0,external_child_process_namespaceObject.spawn)(m,c,{cwd:this.cwd,env:u,stdio:["ignore","pipe","pipe"],shell:process.platform==="win32"});let f="";this._proc.stdout.on("data",r=>{f+=r.toString();const t=f.split(`
|
|
357
357
|
`);f=t.pop();for(const a of t)if(a.trim())try{const i=JSON.parse(a);this._handleMessage(i)}catch{}}),this._proc.stderr.on("data",r=>{const t=r.toString().trim();t&&(console.error("[agent:stderr]",t),!t.includes("Warning:")&&!t.includes("DeprecationWarning")&&this.emit("agent:error",{message:t}))}),this._proc.on("close",r=>{if(f.trim())try{const t=JSON.parse(f.trim());this._handleMessage(t)}catch{}this._proc=null,this._busy=!1,this.emit("agent:busy",!1),b(r)}),this._proc.on("error",r=>{this._proc=null,this._busy=!1,this.emit("agent:busy",!1),this.emit("agent:error",{message:`\u542F\u52A8 Claude \u5931\u8D25: ${r.message}`}),g(r)})})}_handleMessage(d){d.session_id&&!this.claudeSessionId&&(this.claudeSessionId=d.session_id),d.type==="system"&&d.subtype==="init"&&d.session_id&&(this.claudeSessionId=d.session_id),this._history.push({...d,timestamp:Date.now()}),this.emit("agent:message",d)}interrupt(){this._proc&&(this._proc.kill("SIGTERM"),this._proc=null,this._busy=!1,this.emit("agent:busy",!1),this.emit("agent:message",{type:"system",subtype:"interrupted",message:"\u67E5\u8BE2\u5DF2\u4E2D\u65AD"}))}kill(){this.interrupt(),this.alive=!1}get isBusy(){return this._busy}addViewer(d){this._viewers.add(d)}removeViewer(d){this._viewers.delete(d)}get viewerCount(){return this._viewers.size}takeControl(d,c){this.controlHolder=d,this.controlHolderName=c||null,this.lastInputTime=Date.now()}releaseControl(){this.controlHolder=null,this.controlHolderName=null,this.lastInputTime=null}isController(d){return this.controlHolder===d}toJSON(){return{id:this.id,title:this.title,cwd:this.cwd,createdAt:this.createdAt,mode:"agent",alive:this.alive,viewers:this.viewerCount,controlHolder:this.controlHolder,controlHolderName:this.controlHolderName,permissionMode:this.permissionMode,claudeSessionId:this.claudeSessionId,busy:this._busy}}}function listLocalClaudeSessions(S){const d=[];try{const c=(0,external_path_.join)((0,external_os_.homedir)(),".claude","projects");if(!(0,external_fs_.existsSync)(c))return d;for(const b of(0,external_fs_.readdirSync)(c,{withFileTypes:!0})){if(!b.isDirectory())continue;const g=(0,external_path_.join)(c,b.name);try{for(const m of(0,external_fs_.readdirSync)(g)){if(!m.endsWith(".jsonl"))continue;const u=m.replace(".jsonl",""),f=(0,external_path_.join)(g,m);try{const r=(0,external_fs_.readFileSync)(f,"utf8").split(`
|
|
358
358
|
`).filter(Boolean);let t=null,a=null,i=0,e="";for(const s of r)try{const n=JSON.parse(s);if(t||(t=n),a=n,i++,!e&&n.type==="human"&&n.message?.content){const p=n.message.content;if(typeof p=="string")e=p.slice(0,120);else if(Array.isArray(p)){const l=p.find(h=>h.type==="text");l&&(e=l.text?.slice(0,120)||"")}}}catch{}let o=b.name;process.platform==="win32"&&/^[A-Za-z]--/.test(o)?o=o[0]+":\\"+o.slice(3).replace(/-/g,"\\"):o.startsWith("-")&&(o=o.replace(/-/g,"/")),d.push({sessionId:u,projectDir:b.name,projectPath:o,messageCount:i,summary:e,createdAt:t?.timestamp||null,updatedAt:a?.timestamp||null})}catch{}}}catch{}}}catch{}return d.sort((c,b)=>new Date(b.updatedAt||0).getTime()-new Date(c.updatedAt||0).getTime()),d.slice(0,20)}const DEFAULT_SHELL=process.platform==="win32"?process.env.SHELL||"powershell.exe":process.env.SHELL||"/bin/bash",CONFIG_DIR=(0,external_path_.join)((0,external_os_.homedir)(),".myhi"),SESSIONS_FILE=(0,external_path_.join)(CONFIG_DIR,"sessions.json");function loadSavedConfigs(){try{return JSON.parse((0,external_fs_.readFileSync)(SESSIONS_FILE,"utf8"))}catch{return[]}}function writeSavedConfigs(S){try{(0,external_fs_.mkdirSync)(CONFIG_DIR,{recursive:!0});const d=SESSIONS_FILE+".tmp";(0,external_fs_.writeFileSync)(d,JSON.stringify(S,null,2),{mode:384}),(0,external_fs_.renameSync)(d,SESSIONS_FILE)}catch(d){console.error("[sessions] \u6301\u4E45\u5316\u5199\u5165\u5931\u8D25:",d.message)}}class Session extends external_events_.EventEmitter{constructor(d,c={}){super(),this.id=d,this.createdAt=new Date().toISOString(),this.title=c.title||DEFAULT_SHELL,this.cwd=c.cwd||process.env.MYHI_CWD||process.cwd(),this.initCmd=c.initCmd||null,this.permissionMode=c.permissionMode||"default",this.cols=c.cols||120,this.rows=c.rows||30,this._viewers=new Set,this.controlHolder=null,this.controlHolderName=null,this.lastInputTime=null;const b={...process.env};delete b.CLAUDECODE,delete b.CLAUDE_CODE_ENTRYPOINT,delete b.CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC,this._pty=external_node_pty_namespaceObject.spawn(DEFAULT_SHELL,[],{name:"xterm-256color",cols:this.cols,rows:this.rows,cwd:this.cwd,env:b,useConpty:!1}),this._scrollback="";const g=100*1024;if(this._pty.onData(m=>{this.emit("data",m),this._scrollback+=m,this._scrollback.length>g&&(this._scrollback=this._scrollback.slice(this._scrollback.length-g))}),this._pty.onExit(({exitCode:m})=>{this.exitCode=m,this.emit("exit",m)}),c.initCmd){const m=c.initCmd;let u=!1;const f=r=>{!u&&/[$>#\]]\s*$/.test(r.trimEnd())&&(u=!0,this._pty.off("data",f),setTimeout(()=>this._pty.write(m+"\r"),120))};this._pty.onData(f),setTimeout(()=>{u||(u=!0,this._pty.write(m+"\r"))},3e3)}}write(d){d!=null&&this._pty.write(d)}resize(d,c){this.cols=d,this.rows=c,this._pty.resize(d,c)}kill(){try{this._pty.kill()}catch{}}addViewer(d){this._viewers.add(d)}removeViewer(d){this._viewers.delete(d)}get viewerCount(){return this._viewers.size}takeControl(d,c){this.controlHolder=d,this.controlHolderName=c||null,this.lastInputTime=Date.now()}releaseControl(){this.controlHolder=null,this.controlHolderName=null,this.lastInputTime=null}isController(d){return this.controlHolder===d}toJSON(){return{id:this.id,title:this.title,cwd:this.cwd,createdAt:this.createdAt,cols:this.cols,rows:this.rows,viewers:this.viewerCount,alive:this.exitCode===void 0,controlHolder:this.controlHolder,controlHolderName:this.controlHolderName,permissionMode:this.permissionMode}}}class SessionManager{constructor(){this._sessions=new Map,this._agentSessions=new Map;for(const d of loadSavedConfigs())try{this._spawn((0,external_crypto_.randomUUID)(),d,!1)}catch(c){console.warn(`[\u4F1A\u8BDD] \u6062\u590D "${d.title}" \u5931\u8D25:`,c.message)}}_spawn(d,c,b=!0){const g=new Session(d,c);return this._sessions.set(d,g),g.on("exit",()=>{setTimeout(()=>this._sessions.delete(d),300*1e3)}),b&&this._persist(),g}create(d={}){return this._spawn((0,external_crypto_.randomUUID)(),d,!0)}createAgent(d={}){const c=(0,external_crypto_.randomUUID)(),b=new AgentSession(c,d);return this._agentSessions.set(c,b),b}get(d){return this._sessions.get(d)||this._agentSessions.get(d)}list(){const d=[...this._sessions.values()].map(b=>({...b.toJSON(),mode:"pty"})),c=[...this._agentSessions.values()].map(b=>b.toJSON());return[...d,...c]}listSessions(){return[...this._sessions.values(),...this._agentSessions.values()]}kill(d){const c=this._sessions.get(d);if(c){c.kill(),this._sessions.delete(d),this._persist();return}const b=this._agentSessions.get(d);b&&(b.kill(),this._agentSessions.delete(d))}_persist(){const d=[...this._sessions.values()].filter(c=>c.exitCode===void 0).map(c=>({title:c.title,cwd:c.cwd,initCmd:c.initCmd||void 0,permissionMode:c.permissionMode||void 0}));writeSavedConfigs(d)}}const roles_CONFIG_DIR=(0,external_path_.join)((0,external_os_.homedir)(),".myhi"),ROLES_FILE=(0,external_path_.join)(roles_CONFIG_DIR,"roles.json"),ROLE_LEVELS={admin:3,operator:2,viewer:1};let rolesConfig=null;function loadRoles(){try{if((0,external_fs_.existsSync)(ROLES_FILE))return rolesConfig=JSON.parse((0,external_fs_.readFileSync)(ROLES_FILE,"utf8")),rolesConfig}catch(S){console.warn("[\u89D2\u8272] \u52A0\u8F7D roles.json \u5931\u8D25:",S.message)}return rolesConfig=null,null}function lookupPassword(S){if(!rolesConfig?.passwords)return null;const d=rolesConfig.passwords[S];return d?{name:d.name||"\u7528\u6237",role:d.role||"viewer"}:null}function hasPermission(S,d){return(ROLE_LEVELS[S]||0)>=(ROLE_LEVELS[d]||0)}function isMultiUserMode(){return rolesConfig!==null&&rolesConfig.passwords&&Object.keys(rolesConfig.passwords).length>0}function getDefaultRole(){return rolesConfig?.defaultRole||"admin"}const server_dirname=(0,external_path_.dirname)((0,external_url_.fileURLToPath)(import.meta.url)),PORT=process.env.PORT||3e3,HTTPS_PORT=process.env.HTTPS_PORT||3443,HOST=process.env.HOST||"0.0.0.0",configDir=(0,external_path_.join)((0,external_os_.homedir)(),".myhi");(0,external_fs_.mkdirSync)(configDir,{recursive:!0});function readOrCreate(S,d){try{if((0,external_fs_.existsSync)(S))return(0,external_fs_.readFileSync)(S,"utf8").trim()}catch{}const c=d();return(0,external_fs_.writeFileSync)(S,c,{mode:384}),c}const TOKEN=readOrCreate((0,external_path_.join)(configDir,"token"),()=>(0,external_crypto_.randomBytes)(16).toString("hex")),PASSWORD=readOrCreate((0,external_path_.join)(configDir,"password"),()=>(0,external_crypto_.randomBytes)(16).toString("hex"));loadRoles();const userSessions=new Map;userSessions.set(TOKEN,{role:"admin",name:"\u7BA1\u7406\u5458"});function getTailscaleIP(){try{return(0,external_child_process_namespaceObject.execSync)("tailscale ip -4",{timeout:3e3}).toString().trim().split(`
|
|
359
|
-
`)[0]}catch{}for(const S of Object.values((0,external_os_.networkInterfaces)()))for(const d of S)if(d.family==="IPv4"&&!d.internal)return d.address;return"localhost"}function parseCookies(S=""){const d={};for(const c of S.split(";")){const b=c.indexOf("=");b>0&&(d[c.slice(0,b).trim()]=c.slice(b+1).trim())}return d}const SESSION_COOKIE="myhi_sid",isHTTPS=(0,external_fs_.existsSync)(certPath)&&(0,external_fs_.existsSync)(keyPath),COOKIE_OPTS=`HttpOnly; SameSite=Strict; Path=/; Max-Age=31536000${isHTTPS?"; Secure":""}`;function setSessionCookie(S,d){S.setHeader("Set-Cookie",`${SESSION_COOKIE}=${d||TOKEN}; ${COOKIE_OPTS}`)}function getSessionToken(S){return parseCookies(S)[SESSION_COOKIE]||null}function hasValidSession(S){const d=getSessionToken(S);return d&&userSessions.has(d)}function getUserInfo(S){const d=getSessionToken(S);return d?userSessions.get(d):null}const app=express();app.set("trust proxy",!0),app.use((S,d,c)=>{d.setHeader("Referrer-Policy","no-referrer"),d.setHeader("X-Content-Type-Options","nosniff"),c()});const httpServer=(0,external_http_.createServer)(app);let httpsServer=null;const certPath=(0,external_path_.join)(configDir,"certs","cert.pem"),keyPath=(0,external_path_.join)(configDir,"certs","key.pem");if((0,external_fs_.existsSync)(certPath)&&(0,external_fs_.existsSync)(keyPath))try{httpsServer=(0,external_https_.createServer)({cert:(0,external_fs_.readFileSync)(certPath),key:(0,external_fs_.readFileSync)(keyPath)},app)}catch(S){console.warn("[HTTPS] \u8BC1\u4E66\u52A0\u8F7D\u5931\u8D25:",S.message)}const _corsAllowed=/^https?:\/\/(localhost|127\.0\.0\.1|\[::1\]|10\.\d+\.\d+\.\d+|172\.(1[6-9]|2\d|3[01])\.\d+\.\d+|192\.168\.\d+\.\d+|100\.(6[4-9]|[7-9]\d|1[01]\d|12[0-7])\.\d+\.\d+)(:\d+)?$/,io=new Server({cors:{origin:(S,d)=>{if(!S||_corsAllowed.test(S))return d(null,!0);d(new Error("CORS \u4E0D\u5141\u8BB8\u6B64\u6765\u6E90"),!1)},credentials:!0},transports:["websocket"]});io.attach(httpServer),httpsServer&&io.attach(httpsServer);const manager=new SessionManager,AUTO_ATTACH=process.env.MYHI_AUTO_ATTACH!=="0",_autoSpawned=new Set;function spawnLocalTerminal(S,d){if(!AUTO_ATTACH||_autoSpawned.has(S))return;_autoSpawned.add(S);const c=(0,external_url_.fileURLToPath)(__nccwpck_require__.ab+"attach.js"),b=process.execPath,g=d||"myhi";if(process.platform==="win32"){const m=`"${b}" "${c}" ${S}`,u=(0,external_child_process_namespaceObject.spawn)("wt.exe",["-w","0","new-tab","--title",g,"--","cmd","/k",m],{detached:!0,stdio:"ignore"});u.unref(),u.on("error",f=>{console.warn("[attach] wt.exe \u5931\u8D25\uFF0C\u56DE\u9000\u5230 cmd:",f.message),(0,external_child_process_namespaceObject.spawn)("cmd.exe",["/c","start",`"${g}"`,"cmd","/k",m],{detached:!0,stdio:"ignore"}).unref()})}else if(process.platform==="darwin"){const m=`tell application "Terminal" to do script "${b} ${c} ${S}"`;(0,external_child_process_namespaceObject.spawn)("osascript",["-e",m],{detached:!0,stdio:"ignore"}).unref()}else{const m=`${b} "${c}" ${S}`;for(const[u,f]of[["gnome-terminal",["--","bash","-c",`${m}; exec bash`]],["xterm",["-title",g,"-e",m]],["konsole",["--new-tab","-e",m]]]){const r=(0,external_child_process_namespaceObject.spawn)(u,f,{detached:!0,stdio:"ignore"});r.unref(),r.on("error",()=>{});break}}}const uploadDir=(0,external_path_.join)(configDir,"uploads");(0,external_fs_.mkdirSync)(uploadDir,{recursive:!0});function checkAuth(S,d,c){const b=S.query.token;if(b&&(b===TOKEN||userSessions.has(b))){const g=userSessions.get(b);if(g?.onetime){userSessions.delete(b);const u=(0,external_crypto_.randomBytes)(16).toString("hex");userSessions.set(u,{role:g.role,name:g.name}),setSessionCookie(d,u)}else setSessionCookie(d,b===TOKEN?TOKEN:b);const m=S.path+(Object.keys(S.query).filter(u=>u!=="token").length?"?"+new URLSearchParams(Object.fromEntries(Object.entries(S.query).filter(([u])=>u!=="token"))):"");return d.redirect(m)}if(hasValidSession(S.headers.cookie))return c();d.redirect("/login")}const _require=(0,external_module_namespaceObject.createRequire)(import.meta.url);function pkgDir(S){return(0,external_path_.dirname)(_require.resolve(`${S}/package.json`))}app.use("/lib/xterm",express.static(pkgDir("xterm"))),app.use("/lib/xterm-fit",express.static(pkgDir("xterm-addon-fit"))),app.use("/lib/xterm-links",express.static(pkgDir("xterm-addon-web-links")));const _loginAttempts=new Map,LOGIN_MAX_ATTEMPTS=5,LOGIN_LOCKOUT_MS=300*1e3;function checkLoginRate(S){const d=Date.now(),c=_loginAttempts.get(S);return!c||d>c.resetAt?!0:c.count<LOGIN_MAX_ATTEMPTS}function recordLoginFailure(S){const d=Date.now(),c=_loginAttempts.get(S);!c||d>c.resetAt?_loginAttempts.set(S,{count:1,resetAt:d+LOGIN_LOCKOUT_MS}):c.count++}function clearLoginFailures(S){_loginAttempts.delete(S)}app.get("/login",(S,d)=>{d.sendFile(__nccwpck_require__.ab+"login.html")}),app.post("/login",express.urlencoded({extended:!1}),(S,d)=>{const c=S.ip;if(!checkLoginRate(c))return d.redirect("/login?error=locked");const b=S.body?.password,g=S.query.from,m=g&&g.startsWith("/")&&!g.startsWith("//")?g:"/",u=lookupPassword(b);if(u){clearLoginFailures(c);const f=(0,external_crypto_.randomBytes)(16).toString("hex");return userSessions.set(f,{role:u.role,name:u.name}),setSessionCookie(d,f),d.redirect(m)}if(b===PASSWORD){clearLoginFailures(c);const f=(0,external_crypto_.randomBytes)(16).toString("hex");return userSessions.set(f,{role:"admin",name:"\u7BA1\u7406\u5458"}),setSessionCookie(d,f),d.redirect(m)}recordLoginFailure(c),d.redirect("/login?error=1")}),app.get("/",checkAuth,(S,d)=>d.sendFile(__nccwpck_require__.ab+"index.html")),app.get("/terminal/:id",checkAuth,(S,d)=>d.sendFile(__nccwpck_require__.ab+"chat.html")),app.get("/terminal-raw/:id",checkAuth,(S,d)=>d.sendFile(__nccwpck_require__.ab+"terminal.html")),app.use("/lib",express.static(__nccwpck_require__.ab+"lib")),app.get("/api/sessions",checkAuth,(S,d)=>d.json(manager.list())),app.post("/api/sessions",checkAuth,express.json(),(S,d)=>{const c=manager.create(S.body||{});d.status(201).json(c.toJSON())}),app.delete("/api/sessions/:id",checkAuth,(S,d)=>{manager.kill(S.params.id),d.status(204).end()}),app.get("/api/claude-sessions",checkAuth,(S,d)=>{d.json(listLocalClaudeSessions())}),app.get("/api/dirs",checkAuth,(S,d)=>{const c=getUserInfo(S.headers.cookie);if(!c||c.role!=="admin")return d.status(403).json({error:"\u6CA1\u6709\u76EE\u5F55\u6D4F\u89C8\u6743\u9650"});const b=process.platform==="win32"?"\\":"/";let g=S.query.path||process.env.MYHI_CWD||(0,external_os_.homedir)(),m=(0,external_path_.resolve)((0,external_path_.normalize)(g)).replace(/[/\\]+$/,"");process.platform==="win32"&&/^[A-Za-z]:$/.test(m)&&(m+="\\");try{const u=(0,external_fs_.readdirSync)(m,{withFileTypes:!0}).filter(r=>r.isDirectory()&&!r.name.startsWith(".")).map(r=>({name:r.name,path:m.replace(/[/\\]+$/,"")+b+r.name})).sort((r,t)=>r.name.localeCompare(t.name)),f=(0,external_path_.join)(m,"..");d.json({current:m,parent:f!==m?f:null,dirs:u})}catch(u){d.status(400).json({error:u.message})}}),app.post("/upload",checkAuth,(S,d)=>{const c=S.query.sessionId,b=c?manager.get(c):null,g=b?.cwd?(0,external_path_.join)(b.cwd,"upload"):uploadDir;(0,external_fs_.mkdirSync)(g,{recursive:!0}),multer({storage:multer.diskStorage({destination:g,filename:(u,f,r)=>{const t=f.originalname.match(/\.[^.]+$/)?.[0]||".jpg";r(null,`${Date.now()}-${(0,external_crypto_.randomBytes)(3).toString("hex")}${t}`)}}),limits:{fileSize:20*1024*1024},fileFilter:(u,f,r)=>r(null,f.mimetype.startsWith("image/"))}).single("image")(S,d,u=>{if(u)return d.status(400).json({error:u.message});if(!S.file)return d.status(400).json({error:"\u6CA1\u6709\u56FE\u7247"});d.json({path:S.file.path})})}),app.get("/qr/:sessionId",checkAuth,async(S,d)=>{const c=getTailscaleIP(),b=(0,external_crypto_.randomBytes)(16).toString("hex");userSessions.set(b,{role:"admin",name:"\u626B\u7801\u7528\u6237",onetime:!0});const g=`http://${c}:${PORT}/terminal/${S.params.sessionId}?token=${b}`;try{const m=await lib.toString(g,{type:"svg"});d.setHeader("Content-Type","image/svg+xml"),d.send(m)}catch(m){d.status(500).send(m.message)}}),io.use((S,d)=>{const c=S.handshake.headers.cookie;if(hasValidSession(c)){const g=getUserInfo(c);return S.data.role=g?.role||"admin",S.data.userName=g?.name||"\u7528\u6237",d()}if((S.handshake.auth?.token||S.handshake.query?.token)===TOKEN)return S.data.role="admin",S.data.userName="\u7BA1\u7406\u5458",d();d(new Error("\u672A\u6388\u6743"))}),io.on("connection",S=>{const d=S.handshake.address?.replace("::ffff:","")||"?";console.log(`[\u8FDE\u63A5] ${S.data.userName}(${S.data.role}) \u4ECE ${d} \u63A5\u5165 id=${S.id}`);let c=null,b=null,g=null,m=null,u=null;function f(){c&&(c.isController(S.id)&&(c.releaseControl(),io.emit("control-changed",{sessionId:c.id,holder:null,holderName:null})),c.removeViewer(S.id),b&&c.off("data",b),g&&c.off("agent:message",g),m&&c.off("agent:busy",m),u&&c.off("agent:error",u),b=null,g=null,m=null,u=null)}S.on("join",r=>{const t=manager.get(r);if(!t){S.emit("error",{message:`\u4F1A\u8BDD ${r} \u672A\u627E\u5230`});return}f(),c=t,c.addViewer(S.id),t.mode==="agent"?(t._history?.length&&S.emit("agent:history",t._history),g=a=>S.emit("agent:message",a),c.on("agent:message",g),m=a=>S.emit("agent:busy",a),c.on("agent:busy",m),u=a=>S.emit("agent:error",a),c.on("agent:error",u)):(t._scrollback?.length&&S.emit("output",t._scrollback),b=a=>S.emit("output",a),c.on("data",b),c.once("exit",a=>{S.emit("session-exit",{code:a}),b&&c?.off("data",b)}),spawnLocalTerminal(r,t.title)),console.log(`[\u52A0\u5165] ${S.data.userName} \u52A0\u5165\u4F1A\u8BDD "${t.title}" (${r}) \u6A21\u5F0F=${t.mode||"pty"}`),S.emit("joined",{...t.toJSON(),role:S.data.role}),io.emit("sessions",manager.list()),S.on("disconnect",()=>{console.log(`[\u65AD\u5F00] ${S.data.userName} \u79BB\u5F00\u4F1A\u8BDD "${c?.title||"?"}" id=${S.id}`),f(),io.emit("sessions",manager.list())})}),S.on("agent:query",async({prompt:r}={})=>{if(!c||c.mode!=="agent"){S.emit("agent:error",{message:"\u5F53\u524D\u4E0D\u662F Agent \u6A21\u5F0F\u4F1A\u8BDD"});return}if(!c.isController(S.id)){S.emit("control-denied",{reason:"\u4F60\u6CA1\u6709\u5F53\u524D\u4F1A\u8BDD\u7684\u63A7\u5236\u6743"});return}if(c.isBusy){S.emit("agent:error",{message:"\u6B63\u5728\u5904\u7406\u4E2D\uFF0C\u8BF7\u7B49\u5F85\u5B8C\u6210"});return}try{await c.query(r)}catch(t){console.error("[agent:query] \u9519\u8BEF:",t.message),S.emit("agent:error",{message:t.message})}}),S.on("agent:interrupt",()=>{!c||c.mode!=="agent"||c.interrupt()}),S.on("create-agent",(r,t)=>{if(!hasPermission(S.data.role,"admin")){typeof t=="function"&&t({ok:!1,error:"\u6CA1\u6709\u521B\u5EFA\u4F1A\u8BDD\u7684\u6743\u9650"});return}try{const a=manager.createAgent(r||{});console.log(`[\u4F1A\u8BDD] ${S.data.userName} \u521B\u5EFA Agent \u4F1A\u8BDD "${a.title}" (${a.id})`),io.emit("sessions",manager.list()),typeof t=="function"&&t({ok:!0,session:a.toJSON()})}catch(a){typeof t=="function"&&t({ok:!1,error:a.message})}}),S.on("input",r=>{if(!(r==null||!c)&&c.mode!=="agent"){if(!c.isController(S.id)){S.emit("control-denied",{reason:"\u4F60\u6CA1\u6709\u5F53\u524D\u4F1A\u8BDD\u7684\u63A7\u5236\u6743"});return}c.lastInputTime=Date.now(),c.write(r)}}),S.on("take-control",({sessionId:r}={})=>{const t=r?manager.get(r):c;if(t){if(!hasPermission(S.data.role,"operator")){S.emit("control-denied",{reason:"\u4F60\u7684\u89D2\u8272\u6CA1\u6709\u63A7\u5236\u6743\u9650"});return}if(t.controlHolder&&t.controlHolder!==S.id&&S.data.role!=="admin"){S.emit("control-denied",{reason:"\u5176\u4ED6\u7528\u6237\u6B63\u5728\u63A7\u5236\u4E2D\uFF0C\u8BF7\u7B49\u5F85\u91CA\u653E"});return}t.takeControl(S.id,S.data.userName),console.log(`[\u63A7\u5236] ${S.data.userName} \u83B7\u53D6\u4F1A\u8BDD "${t.title}" \u7684\u63A7\u5236\u6743`),io.emit("control-changed",{sessionId:t.id,holder:S.id,holderName:S.data.userName})}}),S.on("release-control",({sessionId:r}={})=>{const t=r?manager.get(r):c;t&&t.isController(S.id)&&(console.log(`[\u63A7\u5236] ${S.data.userName} \u91CA\u653E\u4F1A\u8BDD "${t.title}" \u7684\u63A7\u5236\u6743`),t.releaseControl(),io.emit("control-changed",{sessionId:t.id,holder:null,holderName:null}))}),S.on("set-mode",({sessionId:r,mode:t}={})=>{const a=r?manager.get(r):c;a&&(a.permissionMode=t,io.emit("mode-changed",{sessionId:a.id,mode:t}))}),S.on("rename",({sessionId:r,title:t}={})=>{const a=r?manager.get(r):c;!a||!t||(a.title=t,io.emit("session-renamed",{sessionId:a.id,title:t}),io.emit("sessions",manager.list()))}),S.on("resize",({cols:r,rows:t})=>c?.resize(r,t)),S.on("list",()=>S.emit("sessions",manager.list())),S.on("dirs",(r,t)=>{if(typeof t!="function")return;const a=process.platform==="win32"?"\\":"/";let i=(r||process.env.MYHI_CWD||(0,external_os_.homedir)()).replace(/[/\\]+$/,"")||(0,external_os_.homedir)();process.platform==="win32"&&/^[A-Za-z]:$/.test(i)&&(i+="\\");try{const e=(0,external_fs_.readdirSync)(i,{withFileTypes:!0}).filter(s=>s.isDirectory()&&!s.name.startsWith(".")).map(s=>({name:s.name,path:i.replace(/[/\\]+$/,"")+a+s.name})).sort((s,n)=>s.name.localeCompare(n.name)),o=(0,external_path_.join)(i,"..");t({ok:!0,current:i,parent:o!==i?o:null,dirs:e})}catch(e){t({ok:!1,error:e.message})}}),S.on("create",(r,t)=>{if(!hasPermission(S.data.role,"admin")){typeof t=="function"&&t({ok:!1,error:"\u6CA1\u6709\u521B\u5EFA\u4F1A\u8BDD\u7684\u6743\u9650"});return}try{const a=manager.create(r||{});console.log(`[\u4F1A\u8BDD] ${S.data.userName} \u521B\u5EFA PTY \u4F1A\u8BDD "${a.title}" (${a.id})`),io.emit("sessions",manager.list()),typeof t=="function"&&t({ok:!0,session:a.toJSON()})}catch(a){console.error("[\u521B\u5EFA] PTY \u542F\u52A8\u5931\u8D25:",a.message),typeof t=="function"&&t({ok:!1,error:a.message})}}),S.on("kill",r=>{if(!hasPermission(S.data.role,"admin")){S.emit("error",{message:"\u6CA1\u6709\u5220\u9664\u4F1A\u8BDD\u7684\u6743\u9650"});return}console.log(`[\u4F1A\u8BDD] ${S.data.userName} \u5220\u9664\u4F1A\u8BDD ${r}`),manager.kill(r),io.emit("sessions",manager.list())})});const CONTROL_TIMEOUT=300*1e3;setInterval(()=>{for(const S of manager.listSessions())S.controlHolder&&S.lastInputTime&&Date.now()-S.lastInputTime>CONTROL_TIMEOUT&&(S.releaseControl(),io.emit("control-changed",{sessionId:S.id,holder:null,holderName:null}))},60*1e3),httpServer.listen(PORT,HOST,()=>{const S=getTailscaleIP(),d=(0,external_crypto_.randomBytes)(16).toString("hex");userSessions.set(d,{role:"admin",name:"\u626B\u7801\u7528\u6237",onetime:!0});const c=`http://${S}:${PORT}/?token=${d}`,b=httpsServer?`https://${S}:${HTTPS_PORT}/?token=${d}`:null,g=b||c;console.log(`
|
|
359
|
+
`)[0]}catch{}for(const S of Object.values((0,external_os_.networkInterfaces)()))for(const d of S)if(d.family==="IPv4"&&!d.internal)return d.address;return"localhost"}function parseCookies(S=""){const d={};for(const c of S.split(";")){const b=c.indexOf("=");b>0&&(d[c.slice(0,b).trim()]=c.slice(b+1).trim())}return d}const SESSION_COOKIE="myhi_sid",certPath=(0,external_path_.join)(configDir,"certs","cert.pem"),keyPath=(0,external_path_.join)(configDir,"certs","key.pem"),isHTTPS=(0,external_fs_.existsSync)(certPath)&&(0,external_fs_.existsSync)(keyPath),COOKIE_OPTS=`HttpOnly; SameSite=Strict; Path=/; Max-Age=31536000${isHTTPS?"; Secure":""}`;function setSessionCookie(S,d){S.setHeader("Set-Cookie",`${SESSION_COOKIE}=${d||TOKEN}; ${COOKIE_OPTS}`)}function getSessionToken(S){return parseCookies(S)[SESSION_COOKIE]||null}function hasValidSession(S){const d=getSessionToken(S);return d&&userSessions.has(d)}function getUserInfo(S){const d=getSessionToken(S);return d?userSessions.get(d):null}const app=express();app.set("trust proxy",!0),app.use((S,d,c)=>{d.setHeader("Referrer-Policy","no-referrer"),d.setHeader("X-Content-Type-Options","nosniff"),c()});const httpServer=(0,external_http_.createServer)(app);let httpsServer=null;if((0,external_fs_.existsSync)(certPath)&&(0,external_fs_.existsSync)(keyPath))try{httpsServer=(0,external_https_.createServer)({cert:(0,external_fs_.readFileSync)(certPath),key:(0,external_fs_.readFileSync)(keyPath)},app)}catch(S){console.warn("[HTTPS] \u8BC1\u4E66\u52A0\u8F7D\u5931\u8D25:",S.message)}const _corsAllowed=/^https?:\/\/(localhost|127\.0\.0\.1|\[::1\]|10\.\d+\.\d+\.\d+|172\.(1[6-9]|2\d|3[01])\.\d+\.\d+|192\.168\.\d+\.\d+|100\.(6[4-9]|[7-9]\d|1[01]\d|12[0-7])\.\d+\.\d+)(:\d+)?$/,io=new Server({cors:{origin:(S,d)=>{if(!S||_corsAllowed.test(S))return d(null,!0);d(new Error("CORS \u4E0D\u5141\u8BB8\u6B64\u6765\u6E90"),!1)},credentials:!0},transports:["websocket"]});io.attach(httpServer),httpsServer&&io.attach(httpsServer);const manager=new SessionManager,AUTO_ATTACH=process.env.MYHI_AUTO_ATTACH!=="0",_autoSpawned=new Set;function spawnLocalTerminal(S,d){if(!AUTO_ATTACH||_autoSpawned.has(S))return;_autoSpawned.add(S);const c=(0,external_url_.fileURLToPath)(__nccwpck_require__.ab+"attach.js"),b=process.execPath,g=d||"myhi";if(process.platform==="win32"){const m=`"${b}" "${c}" ${S}`,u=(0,external_child_process_namespaceObject.spawn)("wt.exe",["-w","0","new-tab","--title",g,"--","cmd","/k",m],{detached:!0,stdio:"ignore"});u.unref(),u.on("error",f=>{console.warn("[attach] wt.exe \u5931\u8D25\uFF0C\u56DE\u9000\u5230 cmd:",f.message),(0,external_child_process_namespaceObject.spawn)("cmd.exe",["/c","start",`"${g}"`,"cmd","/k",m],{detached:!0,stdio:"ignore"}).unref()})}else if(process.platform==="darwin"){const m=`tell application "Terminal" to do script "${b} ${c} ${S}"`;(0,external_child_process_namespaceObject.spawn)("osascript",["-e",m],{detached:!0,stdio:"ignore"}).unref()}else{const m=`${b} "${c}" ${S}`;for(const[u,f]of[["gnome-terminal",["--","bash","-c",`${m}; exec bash`]],["xterm",["-title",g,"-e",m]],["konsole",["--new-tab","-e",m]]]){const r=(0,external_child_process_namespaceObject.spawn)(u,f,{detached:!0,stdio:"ignore"});r.unref(),r.on("error",()=>{});break}}}const uploadDir=(0,external_path_.join)(configDir,"uploads");(0,external_fs_.mkdirSync)(uploadDir,{recursive:!0});function checkAuth(S,d,c){const b=S.query.token;if(b&&(b===TOKEN||userSessions.has(b))){const g=userSessions.get(b);if(g?.onetime){userSessions.delete(b);const u=(0,external_crypto_.randomBytes)(16).toString("hex");userSessions.set(u,{role:g.role,name:g.name}),setSessionCookie(d,u)}else setSessionCookie(d,b===TOKEN?TOKEN:b);const m=S.path+(Object.keys(S.query).filter(u=>u!=="token").length?"?"+new URLSearchParams(Object.fromEntries(Object.entries(S.query).filter(([u])=>u!=="token"))):"");return d.redirect(m)}if(hasValidSession(S.headers.cookie))return c();d.redirect("/login")}const _require=(0,external_module_namespaceObject.createRequire)(import.meta.url);function pkgDir(S){return(0,external_path_.dirname)(_require.resolve(`${S}/package.json`))}app.use("/lib/xterm",express.static(pkgDir("xterm"))),app.use("/lib/xterm-fit",express.static(pkgDir("xterm-addon-fit"))),app.use("/lib/xterm-links",express.static(pkgDir("xterm-addon-web-links")));const _loginAttempts=new Map,LOGIN_MAX_ATTEMPTS=5,LOGIN_LOCKOUT_MS=300*1e3;function checkLoginRate(S){const d=Date.now(),c=_loginAttempts.get(S);return!c||d>c.resetAt?!0:c.count<LOGIN_MAX_ATTEMPTS}function recordLoginFailure(S){const d=Date.now(),c=_loginAttempts.get(S);!c||d>c.resetAt?_loginAttempts.set(S,{count:1,resetAt:d+LOGIN_LOCKOUT_MS}):c.count++}function clearLoginFailures(S){_loginAttempts.delete(S)}app.get("/login",(S,d)=>{d.sendFile(__nccwpck_require__.ab+"login.html")}),app.post("/login",express.urlencoded({extended:!1}),(S,d)=>{const c=S.ip;if(!checkLoginRate(c))return d.redirect("/login?error=locked");const b=S.body?.password,g=S.query.from,m=g&&g.startsWith("/")&&!g.startsWith("//")?g:"/",u=lookupPassword(b);if(u){clearLoginFailures(c);const f=(0,external_crypto_.randomBytes)(16).toString("hex");return userSessions.set(f,{role:u.role,name:u.name}),setSessionCookie(d,f),d.redirect(m)}if(b===PASSWORD){clearLoginFailures(c);const f=(0,external_crypto_.randomBytes)(16).toString("hex");return userSessions.set(f,{role:"admin",name:"\u7BA1\u7406\u5458"}),setSessionCookie(d,f),d.redirect(m)}recordLoginFailure(c),d.redirect("/login?error=1")}),app.get("/",checkAuth,(S,d)=>d.sendFile(__nccwpck_require__.ab+"index.html")),app.get("/terminal/:id",checkAuth,(S,d)=>d.sendFile(__nccwpck_require__.ab+"chat.html")),app.get("/terminal-raw/:id",checkAuth,(S,d)=>d.sendFile(__nccwpck_require__.ab+"terminal.html")),app.use("/lib",express.static(__nccwpck_require__.ab+"lib")),app.get("/api/sessions",checkAuth,(S,d)=>d.json(manager.list())),app.post("/api/sessions",checkAuth,express.json(),(S,d)=>{const c=manager.create(S.body||{});d.status(201).json(c.toJSON())}),app.delete("/api/sessions/:id",checkAuth,(S,d)=>{manager.kill(S.params.id),d.status(204).end()}),app.get("/api/claude-sessions",checkAuth,(S,d)=>{d.json(listLocalClaudeSessions())}),app.get("/api/dirs",checkAuth,(S,d)=>{const c=getUserInfo(S.headers.cookie);if(!c||c.role!=="admin")return d.status(403).json({error:"\u6CA1\u6709\u76EE\u5F55\u6D4F\u89C8\u6743\u9650"});const b=process.platform==="win32"?"\\":"/";let g=S.query.path||process.env.MYHI_CWD||(0,external_os_.homedir)(),m=(0,external_path_.resolve)((0,external_path_.normalize)(g)).replace(/[/\\]+$/,"");process.platform==="win32"&&/^[A-Za-z]:$/.test(m)&&(m+="\\");try{const u=(0,external_fs_.readdirSync)(m,{withFileTypes:!0}).filter(r=>r.isDirectory()&&!r.name.startsWith(".")).map(r=>({name:r.name,path:m.replace(/[/\\]+$/,"")+b+r.name})).sort((r,t)=>r.name.localeCompare(t.name)),f=(0,external_path_.join)(m,"..");d.json({current:m,parent:f!==m?f:null,dirs:u})}catch(u){d.status(400).json({error:u.message})}}),app.post("/upload",checkAuth,(S,d)=>{const c=S.query.sessionId,b=c?manager.get(c):null,g=b?.cwd?(0,external_path_.join)(b.cwd,"upload"):uploadDir;(0,external_fs_.mkdirSync)(g,{recursive:!0}),multer({storage:multer.diskStorage({destination:g,filename:(u,f,r)=>{const t=f.originalname.match(/\.[^.]+$/)?.[0]||".jpg";r(null,`${Date.now()}-${(0,external_crypto_.randomBytes)(3).toString("hex")}${t}`)}}),limits:{fileSize:20*1024*1024},fileFilter:(u,f,r)=>r(null,f.mimetype.startsWith("image/"))}).single("image")(S,d,u=>{if(u)return d.status(400).json({error:u.message});if(!S.file)return d.status(400).json({error:"\u6CA1\u6709\u56FE\u7247"});d.json({path:S.file.path})})}),app.get("/qr/:sessionId",checkAuth,async(S,d)=>{const c=getTailscaleIP(),b=(0,external_crypto_.randomBytes)(16).toString("hex");userSessions.set(b,{role:"admin",name:"\u626B\u7801\u7528\u6237",onetime:!0});const g=`http://${c}:${PORT}/terminal/${S.params.sessionId}?token=${b}`;try{const m=await lib.toString(g,{type:"svg"});d.setHeader("Content-Type","image/svg+xml"),d.send(m)}catch(m){d.status(500).send(m.message)}}),io.use((S,d)=>{const c=S.handshake.headers.cookie;if(hasValidSession(c)){const g=getUserInfo(c);return S.data.role=g?.role||"admin",S.data.userName=g?.name||"\u7528\u6237",d()}if((S.handshake.auth?.token||S.handshake.query?.token)===TOKEN)return S.data.role="admin",S.data.userName="\u7BA1\u7406\u5458",d();d(new Error("\u672A\u6388\u6743"))}),io.on("connection",S=>{const d=S.handshake.address?.replace("::ffff:","")||"?";console.log(`[\u8FDE\u63A5] ${S.data.userName}(${S.data.role}) \u4ECE ${d} \u63A5\u5165 id=${S.id}`);let c=null,b=null,g=null,m=null,u=null;function f(){c&&(c.isController(S.id)&&(c.releaseControl(),io.emit("control-changed",{sessionId:c.id,holder:null,holderName:null})),c.removeViewer(S.id),b&&c.off("data",b),g&&c.off("agent:message",g),m&&c.off("agent:busy",m),u&&c.off("agent:error",u),b=null,g=null,m=null,u=null)}S.on("join",r=>{const t=manager.get(r);if(!t){S.emit("error",{message:`\u4F1A\u8BDD ${r} \u672A\u627E\u5230`});return}f(),c=t,c.addViewer(S.id),t.mode==="agent"?(t._history?.length&&S.emit("agent:history",t._history),g=a=>S.emit("agent:message",a),c.on("agent:message",g),m=a=>S.emit("agent:busy",a),c.on("agent:busy",m),u=a=>S.emit("agent:error",a),c.on("agent:error",u)):(t._scrollback?.length&&S.emit("output",t._scrollback),b=a=>S.emit("output",a),c.on("data",b),c.once("exit",a=>{S.emit("session-exit",{code:a}),b&&c?.off("data",b)}),spawnLocalTerminal(r,t.title)),console.log(`[\u52A0\u5165] ${S.data.userName} \u52A0\u5165\u4F1A\u8BDD "${t.title}" (${r}) \u6A21\u5F0F=${t.mode||"pty"}`),S.emit("joined",{...t.toJSON(),role:S.data.role}),io.emit("sessions",manager.list()),S.on("disconnect",()=>{console.log(`[\u65AD\u5F00] ${S.data.userName} \u79BB\u5F00\u4F1A\u8BDD "${c?.title||"?"}" id=${S.id}`),f(),io.emit("sessions",manager.list())})}),S.on("agent:query",async({prompt:r}={})=>{if(!c||c.mode!=="agent"){S.emit("agent:error",{message:"\u5F53\u524D\u4E0D\u662F Agent \u6A21\u5F0F\u4F1A\u8BDD"});return}if(!c.isController(S.id)){S.emit("control-denied",{reason:"\u4F60\u6CA1\u6709\u5F53\u524D\u4F1A\u8BDD\u7684\u63A7\u5236\u6743"});return}if(c.isBusy){S.emit("agent:error",{message:"\u6B63\u5728\u5904\u7406\u4E2D\uFF0C\u8BF7\u7B49\u5F85\u5B8C\u6210"});return}try{await c.query(r)}catch(t){console.error("[agent:query] \u9519\u8BEF:",t.message),S.emit("agent:error",{message:t.message})}}),S.on("agent:interrupt",()=>{!c||c.mode!=="agent"||c.interrupt()}),S.on("create-agent",(r,t)=>{if(!hasPermission(S.data.role,"admin")){typeof t=="function"&&t({ok:!1,error:"\u6CA1\u6709\u521B\u5EFA\u4F1A\u8BDD\u7684\u6743\u9650"});return}try{const a=manager.createAgent(r||{});console.log(`[\u4F1A\u8BDD] ${S.data.userName} \u521B\u5EFA Agent \u4F1A\u8BDD "${a.title}" (${a.id})`),io.emit("sessions",manager.list()),typeof t=="function"&&t({ok:!0,session:a.toJSON()})}catch(a){typeof t=="function"&&t({ok:!1,error:a.message})}}),S.on("input",r=>{if(!(r==null||!c)&&c.mode!=="agent"){if(!c.isController(S.id)){S.emit("control-denied",{reason:"\u4F60\u6CA1\u6709\u5F53\u524D\u4F1A\u8BDD\u7684\u63A7\u5236\u6743"});return}c.lastInputTime=Date.now(),c.write(r)}}),S.on("take-control",({sessionId:r}={})=>{const t=r?manager.get(r):c;if(t){if(!hasPermission(S.data.role,"operator")){S.emit("control-denied",{reason:"\u4F60\u7684\u89D2\u8272\u6CA1\u6709\u63A7\u5236\u6743\u9650"});return}if(t.controlHolder&&t.controlHolder!==S.id&&S.data.role!=="admin"){S.emit("control-denied",{reason:"\u5176\u4ED6\u7528\u6237\u6B63\u5728\u63A7\u5236\u4E2D\uFF0C\u8BF7\u7B49\u5F85\u91CA\u653E"});return}t.takeControl(S.id,S.data.userName),console.log(`[\u63A7\u5236] ${S.data.userName} \u83B7\u53D6\u4F1A\u8BDD "${t.title}" \u7684\u63A7\u5236\u6743`),io.emit("control-changed",{sessionId:t.id,holder:S.id,holderName:S.data.userName})}}),S.on("release-control",({sessionId:r}={})=>{const t=r?manager.get(r):c;t&&t.isController(S.id)&&(console.log(`[\u63A7\u5236] ${S.data.userName} \u91CA\u653E\u4F1A\u8BDD "${t.title}" \u7684\u63A7\u5236\u6743`),t.releaseControl(),io.emit("control-changed",{sessionId:t.id,holder:null,holderName:null}))}),S.on("set-mode",({sessionId:r,mode:t}={})=>{const a=r?manager.get(r):c;a&&(a.permissionMode=t,io.emit("mode-changed",{sessionId:a.id,mode:t}))}),S.on("rename",({sessionId:r,title:t}={})=>{const a=r?manager.get(r):c;!a||!t||(a.title=t,io.emit("session-renamed",{sessionId:a.id,title:t}),io.emit("sessions",manager.list()))}),S.on("resize",({cols:r,rows:t})=>c?.resize(r,t)),S.on("list",()=>S.emit("sessions",manager.list())),S.on("dirs",(r,t)=>{if(typeof t!="function")return;const a=process.platform==="win32"?"\\":"/";let i=(r||process.env.MYHI_CWD||(0,external_os_.homedir)()).replace(/[/\\]+$/,"")||(0,external_os_.homedir)();process.platform==="win32"&&/^[A-Za-z]:$/.test(i)&&(i+="\\");try{const e=(0,external_fs_.readdirSync)(i,{withFileTypes:!0}).filter(s=>s.isDirectory()&&!s.name.startsWith(".")).map(s=>({name:s.name,path:i.replace(/[/\\]+$/,"")+a+s.name})).sort((s,n)=>s.name.localeCompare(n.name)),o=(0,external_path_.join)(i,"..");t({ok:!0,current:i,parent:o!==i?o:null,dirs:e})}catch(e){t({ok:!1,error:e.message})}}),S.on("create",(r,t)=>{if(!hasPermission(S.data.role,"admin")){typeof t=="function"&&t({ok:!1,error:"\u6CA1\u6709\u521B\u5EFA\u4F1A\u8BDD\u7684\u6743\u9650"});return}try{const a=manager.create(r||{});console.log(`[\u4F1A\u8BDD] ${S.data.userName} \u521B\u5EFA PTY \u4F1A\u8BDD "${a.title}" (${a.id})`),io.emit("sessions",manager.list()),typeof t=="function"&&t({ok:!0,session:a.toJSON()})}catch(a){console.error("[\u521B\u5EFA] PTY \u542F\u52A8\u5931\u8D25:",a.message),typeof t=="function"&&t({ok:!1,error:a.message})}}),S.on("kill",r=>{if(!hasPermission(S.data.role,"admin")){S.emit("error",{message:"\u6CA1\u6709\u5220\u9664\u4F1A\u8BDD\u7684\u6743\u9650"});return}console.log(`[\u4F1A\u8BDD] ${S.data.userName} \u5220\u9664\u4F1A\u8BDD ${r}`),manager.kill(r),io.emit("sessions",manager.list())})});const CONTROL_TIMEOUT=300*1e3;setInterval(()=>{for(const S of manager.listSessions())S.controlHolder&&S.lastInputTime&&Date.now()-S.lastInputTime>CONTROL_TIMEOUT&&(S.releaseControl(),io.emit("control-changed",{sessionId:S.id,holder:null,holderName:null}))},60*1e3),httpServer.listen(PORT,HOST,()=>{const S=getTailscaleIP(),d=(0,external_crypto_.randomBytes)(16).toString("hex");userSessions.set(d,{role:"admin",name:"\u626B\u7801\u7528\u6237",onetime:!0});const c=`http://${S}:${PORT}/?token=${d}`,b=httpsServer?`https://${S}:${HTTPS_PORT}/?token=${d}`:null,g=b||c;console.log(`
|
|
360
360
|
\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(`
|
|
361
361
|
\u5730\u5740: http://${S}:${PORT}`),b&&console.log(` HTTPS: https://${S}:${HTTPS_PORT} (\u8BED\u97F3\u8BC6\u522B\u9700\u8981)`),console.log(` \u5BC6\u7801: ${PASSWORD} (\u7F16\u8F91 ~/.myhi/password \u53EF\u4FEE\u6539)`),console.log(`
|
|
362
362
|
\u626B\u63CF\u4E8C\u7EF4\u7801\u5728\u624B\u673A\u4E0A\u6253\u5F00\uFF08\u4E00\u6B21\u6027\u94FE\u63A5\uFF09:
|