@wendongfly/myhi 1.3.95 → 1.3.97

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.min.js CHANGED
@@ -379,7 +379,7 @@ Content-Length: `+C+`\r
379
379
  `).filter(Boolean);let r=null,u=null,p=0,s="";for(const h of e)try{const g=JSON.parse(h);if(r||(r=g),u=g,p++,!s&&g.type==="human"||g.type==="user"&&g.message?.content){const b=g.message.content;if(typeof b=="string")s=b.slice(0,120);else if(Array.isArray(b)){const d=b.find((w=>w.type==="text"));d&&(s=d.text?.slice(0,120)||"")}}}catch{}let v=l.name;if(process.platform==="win32"&&/^[A-Za-z]--/.test(v)?v=v[0]+":\\"+v.slice(3).replace(/-/g,"\\"):v.startsWith("-")&&(v=v.replace(/-/g,"/")),a&&!(0,external_path_.resolve)((0,external_path_.normalize)(v)).replace(/[\\/]+$/,"").startsWith(a))continue;f.push({sessionId:i,projectDir:l.name,projectPath:v,messageCount:p,summary:s,createdAt:r?.timestamp||null,updatedAt:u?.timestamp||null})}catch{}}}catch{}}}catch{}f.sort(((c,l)=>new Date(l.updatedAt||0).getTime()-new Date(c.updatedAt||0).getTime()));const t=f.slice(0,50);return _claudeSessionsCache=t,_claudeSessionsCacheTime=Date.now(),t.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"),savePath=null;function loadSavedConfigs(){try{return JSON.parse((0,external_fs_.readFileSync)(SESSIONS_FILE,"utf8"))}catch{return[]}}function writeSavedConfigs(T){try{(0,external_fs_.mkdirSync)(CONFIG_DIR,{recursive:!0});const _=SESSIONS_FILE+".tmp";(0,external_fs_.writeFileSync)(_,JSON.stringify(T,null,2),{mode:384}),(0,external_fs_.renameSync)(_,SESSIONS_FILE)}catch(_){console.error("[sessions] \u6301\u4E45\u5316\u5199\u5165\u5931\u8D25:",_.message)}}class Session extends external_events_.EventEmitter{constructor(_,f={}){super(),this.id=_,this.createdAt=f.createdAt||new Date().toISOString(),this.title=f.title||DEFAULT_SHELL,this.cwd=f.cwd||process.env.MYHI_CWD||process.cwd(),this.tags=Array.isArray(f.tags)?f.tags:[],this.initCmd=f.initCmd||null,this.owner=f.owner||null,this.cols=f.cols||120,this.rows=f.rows||30,this._lastActivityAt=f.lastActivityAt||0,this._viewers=new Set,this._viewerNames=new Map,this.controlHolder=null,this.controlHolderName=null,this.lastInputTime=null;const a={...process.env};if(delete a.CLAUDECODE,delete a.CLAUDE_CODE_ENTRYPOINT,delete a.CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC,f.userDir){const c=(0,external_path_.join)(f.userDir,".gitconfig");(0,external_fs_.existsSync)(c)&&(a.GIT_CONFIG_GLOBAL=c)}this._pty=external_node_pty_namespaceObject.spawn(DEFAULT_SHELL,[],{name:"xterm-256color",cols:this.cols,rows:this.rows,cwd:this.cwd,env:a,useConpty:!1}),this._scrollback="";const t=100*1024;if(this._pty.onData((c=>{this.emit("data",c),this._scrollback+=c,this._scrollback.length>t&&(this._scrollback=this._scrollback.slice(this._scrollback.length-t))})),this._pty.onExit((({exitCode:c})=>{this.exitCode=c,this.emit("exit",c)})),f.initCmd){const c=f.initCmd;let l=!1;const m=o=>{!l&&/[$>#\]]\s*$/.test(o.trimEnd())&&(l=!0,this._pty.off("data",m),setTimeout((()=>this._pty.write(c+"\r")),120))};this._pty.onData(m),setTimeout((()=>{l||(l=!0,this._pty.write(c+"\r"))}),3e3)}}write(_){_!=null&&(this._lastActivityAt=Date.now(),this._pty.write(_))}resize(_,f){this.cols=_,this.rows=f,this._pty.resize(_,f)}kill(){try{this._pty.kill()}catch{}}addViewer(_,f){this._viewers.add(_),f&&this._viewerNames.set(_,f)}removeViewer(_){this._viewers.delete(_),this._viewerNames.delete(_)}get viewerCount(){return new Set(this._viewerNames.values()).size||this._viewers.size}takeControl(_,f){this.controlHolder=_,this.controlHolderName=f||null,this.lastInputTime=Date.now()}releaseControl(){this.controlHolder=null,this.controlHolderName=null,this.lastInputTime=null}isController(_){return this.controlHolder===_}toJSON(){return{id:this.id,title:this.title,cwd:this.cwd,tags:this.tags,createdAt:this.createdAt,lastActivityAt:this._lastActivityAt||null,cols:this.cols,rows:this.rows,viewers:this.viewerCount,alive:this.exitCode===void 0,controlHolder:this.controlHolder,controlHolderName:this.controlHolderName,owner:this.owner}}}class SessionManager{constructor(){this._sessions=new Map,this._agentSessions=new Map;for(const _ of loadSavedConfigs())try{if(_.mode==="agent"){if(_.claudeSessionId){const f=(0,external_crypto_.randomUUID)(),a=new AgentSession(f,{..._,resumeSessionId:_.claudeSessionId});a.on("session-id",(()=>this._persist())),this._agentSessions.set(f,a),console.log(`[\u4F1A\u8BDD] \u6062\u590D Agent "${_.title}" (claude: ${_.claudeSessionId.slice(0,8)}...)`)}}else this._spawn((0,external_crypto_.randomUUID)(),_,!1)}catch(f){console.warn(`[\u4F1A\u8BDD] \u6062\u590D "${_.title}" \u5931\u8D25:`,f.message)}}_spawn(_,f,a=!0){const t=new Session(_,f);return this._sessions.set(_,t),t.on("exit",(()=>{setTimeout((()=>this._sessions.delete(_)),300*1e3)})),a&&this._persist(),t}create(_={}){return this._spawn((0,external_crypto_.randomUUID)(),_,!0)}createAgent(_={}){const f=(0,external_crypto_.randomUUID)(),a=new AgentSession(f,_);return this._agentSessions.set(f,a),a.on("session-id",(()=>this._persist())),this._persist(),a}get(_){return this._sessions.get(_)||this._agentSessions.get(_)}list(_,f=!1){let a=[...this._sessions.values()].map((c=>({...c.toJSON(),mode:"pty"}))),t=[...this._agentSessions.values()].map((c=>c.toJSON()));return _&&!f&&(a=a.filter((c=>!c.owner||c.owner===_)),t=t.filter((c=>!c.owner||c.owner===_))),[...a,...t]}listSessions(){return[...this._sessions.values(),...this._agentSessions.values()]}kill(_){const f=this._sessions.get(_);if(f){f.kill(),this._sessions.delete(_),this._persist();return}const a=this._agentSessions.get(_);a&&(a.kill(),this._agentSessions.delete(_),this._persist())}persist(){this._persist()}_persist(){const _=[...this._sessions.values()].filter((a=>a.exitCode===void 0)).map((a=>({mode:"pty",title:a.title,cwd:a.cwd,tags:a.tags?.length?a.tags:void 0,createdAt:a.createdAt||void 0,lastActivityAt:a._lastActivityAt||void 0,initCmd:a.initCmd||void 0,owner:a.owner||void 0}))),f=[...this._agentSessions.values()].filter((a=>a.alive)).map((a=>({mode:"agent",title:a.title,cwd:a.cwd,tags:a.tags?.length?a.tags:void 0,createdAt:a.createdAt||void 0,lastActivityAt:a._lastActivityAt||void 0,owner:a.owner||void 0,userDir:a.userDir||void 0,claudeSessionId:a.claudeSessionId||void 0})));writeSavedConfigs([..._,...f])}}const roles_CONFIG_DIR=(0,external_path_.join)((0,external_os_.homedir)(),".myhi"),ROLES_FILE=(0,external_path_.join)(roles_CONFIG_DIR,"roles.json"),USERS_FILE=(0,external_path_.join)(roles_CONFIG_DIR,"users.json"),ROLE_LEVELS={admin:3,operator:2,viewer:1};let rolesConfig=null,usersConfig=null;function loadRoles(){try{if((0,external_fs_.existsSync)(ROLES_FILE))return rolesConfig=JSON.parse((0,external_fs_.readFileSync)(ROLES_FILE,"utf8")),rolesConfig}catch(T){console.warn("[\u89D2\u8272] \u52A0\u8F7D roles.json \u5931\u8D25:",T.message)}return rolesConfig=null,null}function lookupPassword(T){if(!rolesConfig?.passwords)return null;const _=rolesConfig.passwords[T];return _?{name:_.name||"\u7528\u6237",role:_.role||"viewer"}:null}function hasPermission(T,_){return(ROLE_LEVELS[T]||0)>=(ROLE_LEVELS[_]||0)}function isMultiUserMode(){return rolesConfig!==null&&rolesConfig.passwords&&Object.keys(rolesConfig.passwords).length>0}function getDefaultRole(){return rolesConfig?.defaultRole||"admin"}function loadUsers(){try{if((0,external_fs_.existsSync)(USERS_FILE))return usersConfig=JSON.parse((0,external_fs_.readFileSync)(USERS_FILE,"utf8")),usersConfig}catch(T){console.warn("[\u7528\u6237] \u52A0\u8F7D users.json \u5931\u8D25:",T.message)}return usersConfig=null,null}function hasUsers(){return usersConfig!==null&&usersConfig.users&&Object.keys(usersConfig.users).length>0}function isExclusiveMode(){return hasUsers()&&usersConfig.exclusive===!0}function setExclusiveMode(T){usersConfig||(usersConfig={users:{}}),usersConfig.exclusive=!!T,_writeUsers()}function lookupUser(T){if(!usersConfig?.users)return null;const _=usersConfig.users[T];return _?{name:_.name||"\u7528\u6237",dir:_.dir}:null}function listUsers(){return usersConfig?.users?Object.entries(usersConfig.users).map((([T,_])=>({password:T.slice(0,2)+"*".repeat(T.length-2),name:_.name,dir:_.dir}))):[]}function addUser(T,_,f){usersConfig||(usersConfig={users:{}}),usersConfig.users||(usersConfig.users={}),usersConfig.users[T]={name:_,dir:f},_writeUsers()}function removeUser(T){return!usersConfig?.users||!usersConfig.users[T]?!1:(delete usersConfig.users[T],_writeUsers(),!0)}function _writeUsers(){(0,external_fs_.mkdirSync)(roles_CONFIG_DIR,{recursive:!0}),(0,external_fs_.writeFileSync)(USERS_FILE,JSON.stringify(usersConfig,null,2),{mode:384})}const execAsync=(0,external_util_.promisify)(external_child_process_namespaceObject.exec),server_dirname=(0,external_path_.dirname)((0,external_url_.fileURLToPath)(import.meta.url));let PORT=parseInt(process.env.PORT,10)||12300;const HOST=process.env.HOST||"0.0.0.0",PORT_EXPLICIT=!!process.env.PORT,configDir=(0,external_path_.join)((0,external_os_.homedir)(),".myhi");(0,external_fs_.mkdirSync)(configDir,{recursive:!0});let httpsServer=null,httpServer=null;function getOrCreateCert(){const T=(0,external_path_.join)(configDir,"cert.pem"),_=(0,external_path_.join)(configDir,"key.pem");if((0,external_fs_.existsSync)(T)&&(0,external_fs_.existsSync)(_))try{return{cert:(0,external_fs_.readFileSync)(T),key:(0,external_fs_.readFileSync)(_)}}catch{}try{const f=selfsigned.generate([{name:"commonName",value:"myhi"}],{days:3650,keySize:2048});return(0,external_fs_.writeFileSync)(T,f.cert,"utf8"),(0,external_fs_.writeFileSync)(_,f.private,"utf8"),console.log("[myhi] \u81EA\u7B7E\u540D HTTPS \u8BC1\u4E66\u5DF2\u751F\u6210"),{cert:f.cert,key:f.private}}catch{}try{return(0,external_child_process_namespaceObject.execSync)(`openssl req -x509 -newkey rsa:2048 -keyout "${_}" -out "${T}" -days 3650 -nodes -subj "/CN=myhi"`,{stdio:"pipe",timeout:1e4}),{cert:(0,external_fs_.readFileSync)(T),key:(0,external_fs_.readFileSync)(_)}}catch(f){return console.warn(`[myhi] \u751F\u6210 HTTPS \u8BC1\u4E66\u5931\u8D25: ${f.message?.split(`
380
380
  `)[0]||f}`),null}}const pidFile=(0,external_path_.join)(configDir,"server.pid"),daemonPidFile=(0,external_path_.join)(configDir,"daemon.pid"),IS_DAEMON_CHILD=!!process.env.MYHI_DAEMON;function killOldProcess(T,_){let f=!1;try{process.kill(T,0),f=!0}catch{}if(!f)return!0;console.log(`[myhi] \u68C0\u6D4B\u5230\u5DF2\u8FD0\u884C\u7684${_} (PID ${T})\uFF0C\u6B63\u5728\u505C\u6B62...`);try{process.platform==="win32"?(0,external_child_process_namespaceObject.execSync)(`taskkill /PID ${T} /F /T`,{stdio:"pipe",timeout:5e3}):process.kill(T,"SIGTERM")}catch{}const a=Date.now()+5e3;for(;Date.now()<a;){try{process.kill(T,0)}catch{return!0}const c=Date.now()+200;for(;Date.now()<c;);}let t=!1;try{process.kill(T,0),t=!0}catch{}return!t}(function T(){try{const _=parseInt((0,external_fs_.readFileSync)(daemonPidFile,"utf8").trim(),10);if(_&&_!==process.pid&&_!==parseInt(process.env.MYHI_DAEMON_PID||"0",10)){let f=!1;try{process.kill(_,0),f=!0}catch{}if(f&&!IS_DAEMON_CHILD){console.log("[myhi] \u68C0\u6D4B\u5230\u540E\u53F0 daemon \u5728\u8FD0\u884C\uFF0C\u5148\u505C\u6B62..."),killOldProcess(_,"daemon")||(console.error("[myhi] \u65E0\u6CD5\u505C\u6B62 daemon\uFF0C\u8BF7\u5148\u8FD0\u884C: myhi stop"),process.exit(1));try{(0,external_fs_.unlinkSync)(daemonPidFile)}catch{}}}}catch{}if(!IS_DAEMON_CHILD)try{const _=parseInt((0,external_fs_.readFileSync)(pidFile,"utf8").trim(),10);_&&_!==process.pid&&(killOldProcess(_,"\u5B9E\u4F8B")||(console.error(`[myhi] \u65E0\u6CD5\u505C\u6B62\u65E7\u8FDB\u7A0B (PID ${_})\uFF0C\u8BF7\u624B\u52A8\u6267\u884C\uFF1A`),console.error(" myhi stop"),console.error(` \u6216: ${process.platform==="win32"?`taskkill /PID ${_} /F`:`kill -9 ${_}`}`),process.exit(1)))}catch{}(0,external_fs_.writeFileSync)(pidFile,String(process.pid))})();function cleanupPid(){try{parseInt((0,external_fs_.readFileSync)(pidFile,"utf8").trim(),10)===process.pid&&(0,external_fs_.unlinkSync)(pidFile)}catch{}}function gracefulShutdown(){try{if(typeof manager<"u"){try{manager.persist()}catch{}for(const T of manager.listSessions())try{T.kill()}catch{}}}catch{}cleanupPid()}process.on("exit",cleanupPid),process.on("SIGTERM",(()=>{gracefulShutdown(),process.exit(0)})),process.on("SIGINT",(()=>{gracefulShutdown(),process.exit(0)})),process.on("uncaughtException",(T=>{console.error("[myhi] \u672A\u6355\u83B7\u5F02\u5E38:",T.message),gracefulShutdown(),process.exit(1)})),process.on("unhandledRejection",(T=>{console.error("[myhi] \u672A\u5904\u7406\u7684 Promise \u62D2\u7EDD:",T)}));function readOrCreate(T,_){try{if((0,external_fs_.existsSync)(T))return(0,external_fs_.readFileSync)(T,"utf8").trim()}catch{}const f=_();return(0,external_fs_.writeFileSync)(T,f,{mode:384}),f}const TOKEN=readOrCreate((0,external_path_.join)(configDir,"token"),(()=>(0,external_crypto_.randomBytes)(16).toString("hex")));let PASSWORD=readOrCreate((0,external_path_.join)(configDir,"password"),(()=>String(Math.floor(1e3+Math.random()*9e3))));loadRoles(),loadUsers();let activeUser=null;const userSessions=new Map;userSessions.set(TOKEN,{role:"admin",name:"\u7BA1\u7406\u5458",permanent:!0});const SESSION_MAX_AGE=10080*60*1e3;setInterval((()=>{const T=Date.now();for(const[_,f]of userSessions)f.permanent||(!f.lastUsed||T-f.lastUsed>SESSION_MAX_AGE)&&userSessions.delete(_)}),3600*1e3);function getTailscaleIP(){try{return(0,external_child_process_namespaceObject.execSync)("tailscale ip -4",{timeout:3e3,stdio:"pipe"}).toString().trim().split(`
381
381
  `)[0]}catch{}try{if(process.platform==="win32"){const T=(0,external_child_process_namespaceObject.execSync)("route print 0.0.0.0",{timeout:3e3,stdio:"pipe"}).toString().match(/0\.0\.0\.0\s+0\.0\.0\.0\s+\S+\s+(\d+\.\d+\.\d+\.\d+)/);if(T)return T[1]}else try{const T=(0,external_child_process_namespaceObject.execSync)("ip route get 1.1.1.1",{timeout:3e3,stdio:"pipe"}).toString().match(/src\s+(\d+\.\d+\.\d+\.\d+)/);if(T)return T[1]}catch{const T=(0,external_child_process_namespaceObject.execSync)("route -n get default 2>/dev/null || route -n 2>/dev/null",{timeout:3e3,stdio:"pipe"}).toString(),_=T.match(/interface:\s*(\S+)/i)||T.match(/0\.0\.0\.0\s+(\d+\.\d+\.\d+\.\d+)/);if(_){const f=(0,external_os_.networkInterfaces)()[_[1]];if(f){const a=f.find((t=>t.family==="IPv4"&&!t.internal));if(a)return a.address}if(/^\d+\.\d+\.\d+\.\d+$/.test(_[1]))return _[1]}}}catch{}for(const T of Object.values((0,external_os_.networkInterfaces)()))for(const _ of T)if(_.family==="IPv4"&&!_.internal)return _.address;return"localhost"}function parseCookies(T=""){const _={};for(const f of T.split(";")){const a=f.indexOf("=");a>0&&(_[f.slice(0,a).trim()]=f.slice(a+1).trim())}return _}const SESSION_COOKIE="myhi_sid",COOKIE_OPTS_BASE="HttpOnly; SameSite=Strict; Path=/; Max-Age=31536000";function cookieOpts(T){return T?.secure||T?.headers?.["x-forwarded-proto"]==="https"?COOKIE_OPTS_BASE+"; Secure":COOKIE_OPTS_BASE}function setSessionCookie(T,_,f){T.setHeader("Set-Cookie",`${SESSION_COOKIE}=${_||TOKEN}; ${cookieOpts(f)}`)}function getSessionToken(T){return parseCookies(T)[SESSION_COOKIE]||null}function hasValidSession(T){const _=getSessionToken(T);if(_&&userSessions.has(_)){const f=userSessions.get(_);return f&&(f.lastUsed=Date.now()),!0}return!1}function getUserInfo(T){const _=getSessionToken(T);return _?userSessions.get(_):null}const app=express();app.set("trust proxy",!0),app.use(((T,_,f)=>{_.setHeader("Referrer-Policy","no-referrer"),_.setHeader("X-Content-Type-Options","nosniff"),f()}));const _tlsOpts=process.env.MYHI_NO_HTTPS?null:getOrCreateCert();_tlsOpts?(httpsServer=(0,external_https_.createServer)(_tlsOpts,app),httpServer=(0,external_http_.createServer)(((T,_)=>{const f=(T.headers.host||"").replace(/:\d+$/,"");_.writeHead(301,{Location:`https://${f}:${PORT}${T.url}`}),_.end()}))):(console.warn("[myhi] TLS \u4E0D\u53EF\u7528\uFF0C\u4EE5 HTTP \u6A21\u5F0F\u8FD0\u884C\uFF08\u9EA6\u514B\u98CE/\u6444\u50CF\u5934\u7B49\u6D4F\u89C8\u5668\u5B89\u5168\u529F\u80FD\u53D7\u9650\uFF09"),httpServer=(0,external_http_.createServer)(app));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({serveClient:!1,cors:{origin:(T,_)=>{if(!T||_corsAllowed.test(T))return _(null,!0);_(new Error("CORS \u4E0D\u5141\u8BB8\u6B64\u6765\u6E90"),!1)},credentials:!0},transports:["websocket","polling"],pingTimeout:6e4,pingInterval:25e3,upgradeTimeout:3e4,maxHttpBufferSize:5e6});httpsServer?io.attach(httpsServer):io.attach(httpServer);const manager=new SessionManager,AUTO_ATTACH=process.env.MYHI_AUTO_ATTACH!=="0",_autoSpawned=new Set;function spawnLocalTerminal(T,_){if(!AUTO_ATTACH||_autoSpawned.has(T)||!process.env.DISPLAY&&process.platform==="linux")return;_autoSpawned.add(T);const f=__nccwpck_require__.ab+"attach.js",a=process.execPath,t=_||"myhi";if(process.platform==="win32"){const c=`"${a}" "${f}" ${T}`,l=(0,external_child_process_namespaceObject.spawn)("wt.exe",["-w","0","new-tab","--title",t,"--","cmd","/k",c],{detached:!0,stdio:"ignore"});l.unref(),l.on("error",(m=>{console.warn("[attach] wt.exe \u5931\u8D25\uFF0C\u56DE\u9000\u5230 cmd:",m.message),(0,external_child_process_namespaceObject.spawn)("cmd.exe",["/c","start",`"${t}"`,"cmd","/k",c],{detached:!0,stdio:"ignore"}).unref()}))}else if(process.platform==="darwin"){const c=`tell application "Terminal" to do script "${a} ${f} ${T}"`;(0,external_child_process_namespaceObject.spawn)("osascript",["-e",c],{detached:!0,stdio:"ignore"}).unref()}else{const c=`${a} "${f}" ${T}`;for(const[l,m]of[["gnome-terminal",["--","bash","-c",`${c}; exec bash`]],["xterm",["-title",t,"-e",c]],["konsole",["--new-tab","-e",c]]]){const o=(0,external_child_process_namespaceObject.spawn)(l,m,{detached:!0,stdio:"ignore"});o.unref(),o.on("error",(()=>{}));break}}}const uploadDir=(0,external_path_.join)(configDir,"uploads");(0,external_fs_.mkdirSync)(uploadDir,{recursive:!0});function checkAuth(T,_,f){const a=T.query.token;if(a&&(a===TOKEN||userSessions.has(a))){const t=userSessions.get(a);if(t?.onetime){userSessions.delete(a);const m=(0,external_crypto_.randomBytes)(16).toString("hex");userSessions.set(m,{role:t.role,name:t.name,lastUsed:Date.now()}),setSessionCookie(_,m,T)}else setSessionCookie(_,a===TOKEN?TOKEN:a,T);const c=Object.keys(T.query).filter((m=>m!=="token")),l=T.path+(c.length?"?"+new URLSearchParams(Object.fromEntries(Object.entries(T.query).filter((([m])=>m!=="token")))):"");return _.redirect(l)}if(hasValidSession(T.headers.cookie)){if(isExclusiveMode()&&activeUser){const t=getSessionToken(T.headers.cookie);if(t!==TOKEN){const c=userSessions.get(t);if(!c||c.name!==activeUser.name)return _.setHeader("Set-Cookie",`${SESSION_COOKIE}=; HttpOnly; SameSite=Strict; Path=/; Max-Age=0`),_.redirect("/login")}}return f()}_.redirect("/login")}const _require=(0,external_module_namespaceObject.createRequire)(import.meta.url);function pkgDirSafe(T){try{return(0,external_path_.dirname)(_require.resolve(`${T}/package.json`))}catch{return null}}const libDir=(0,external_path_.join)(server_dirname,"lib"),staticOpts={maxAge:864e5};app.use("/lib/xterm",express.static(pkgDirSafe("xterm")||(0,external_path_.join)(libDir,"xterm"),staticOpts)),app.use("/lib/xterm-fit",express.static(pkgDirSafe("xterm-addon-fit")||(0,external_path_.join)(libDir,"xterm-fit"),staticOpts)),app.use("/lib/xterm-links",express.static(pkgDirSafe("xterm-addon-web-links")||(0,external_path_.join)(libDir,"xterm-links"),staticOpts)),app.use("/sio-client",express.static((0,external_path_.join)(server_dirname,"client-dist"),{maxAge:"1h",index:!1,setHeaders:(T,_)=>{_.endsWith(".js")&&T.setHeader("Content-Type","application/javascript; charset=utf-8")}})),app.get("/manifest.json",((T,_)=>_.sendFile((0,external_path_.join)(server_dirname,"manifest.json")))),app.get("/icon.png",((T,_)=>_.sendFile((0,external_path_.join)(server_dirname,"icon.png")))),app.get("/icon.svg",((T,_)=>_.sendFile((0,external_path_.join)(server_dirname,"icon.svg"))));const _loginAttempts=new Map,LOGIN_MAX_ATTEMPTS=5,LOGIN_LOCKOUT_MS=300*1e3;function checkLoginRate(T){const _=Date.now(),f=_loginAttempts.get(T);return!f||_>f.resetAt?!0:f.count<LOGIN_MAX_ATTEMPTS}function recordLoginFailure(T){const _=Date.now(),f=_loginAttempts.get(T);!f||_>f.resetAt?_loginAttempts.set(T,{count:1,resetAt:_+LOGIN_LOCKOUT_MS}):f.count++}function clearLoginFailures(T){_loginAttempts.delete(T)}setInterval((()=>{const T=Date.now();for(const[_,f]of _loginAttempts)T>f.resetAt&&_loginAttempts.delete(_)}),600*1e3),app.get("/login",((T,_)=>{_.sendFile((0,external_path_.join)(server_dirname,"login.html"))})),app.post("/login",express.urlencoded({extended:!1}),((T,_)=>{const f=T.ip;if(!checkLoginRate(f))return _.redirect("/login?error=locked");loadRoles(),loadUsers();const a=T.body?.password,t=T.query.from,c=t&&t.startsWith("/")&&!t.startsWith("//")?t:"/";if(hasUsers()){const m=lookupUser(a);if(!m)return recordLoginFailure(f),_.redirect("/login?error=1");if(isExclusiveMode()&&activeUser&&activeUser.name!==m.name)return _.redirect("/login?error=occupied&user="+encodeURIComponent(activeUser.name));clearLoginFailures(f);const o=(0,external_crypto_.randomBytes)(16).toString("hex");return userSessions.set(o,{role:"operator",name:m.name,dir:m.dir,lastUsed:Date.now()}),isExclusiveMode()&&(activeUser?activeUser.tokens.add(o):activeUser={name:m.name,dir:m.dir,tokens:new Set([o]),loginAt:Date.now()}),setSessionCookie(_,o,T),console.log(`[\u767B\u5F55] ${m.name} \u767B\u5F55\uFF08${isExclusiveMode()?"\u72EC\u5360":"\u5171\u4EAB"}\u6A21\u5F0F\uFF09`),_.redirect(c)}const l=lookupPassword(a);if(l){clearLoginFailures(f);const m=(0,external_crypto_.randomBytes)(16).toString("hex");return userSessions.set(m,{role:l.role,name:l.name,lastUsed:Date.now()}),setSessionCookie(_,m,T),_.redirect(c)}if(a===PASSWORD){clearLoginFailures(f);const m=(0,external_crypto_.randomBytes)(16).toString("hex");return userSessions.set(m,{role:"admin",name:"\u7BA1\u7406\u5458",lastUsed:Date.now()}),setSessionCookie(_,m,T),_.redirect(c)}recordLoginFailure(f),_.redirect("/login?error=1")})),app.post("/logout",((T,_)=>{const f=getSessionToken(T.headers.cookie);f&&(activeUser&&activeUser.tokens.has(f)&&(activeUser.tokens.delete(f),activeUser.tokens.size===0?(console.log(`[\u9000\u51FA] ${activeUser.name} \u5168\u90E8\u8BBE\u5907\u9000\u51FA\uFF0C\u91CA\u653E\u767B\u5F55\u72B6\u6001\uFF08\u4F1A\u8BDD\u4FDD\u7559\uFF09`),activeUser=null):console.log(`[\u9000\u51FA] ${activeUser.name} \u4E00\u4E2A\u8BBE\u5907\u9000\u51FA\uFF08\u5269\u4F59 ${activeUser.tokens.size} \u4E2A\uFF09`)),userSessions.delete(f)),_.setHeader("Set-Cookie",`${SESSION_COOKIE}=; HttpOnly; SameSite=Strict; Path=/; Max-Age=0`),_.redirect("/login")})),app.get("/api/status",((T,_)=>{if(isExclusiveMode()&&activeUser)return _.json({occupied:!0,userName:activeUser.name,releasing:!!_exclusiveReleaseTimer});if(hasUsers()&&!isExclusiveMode()){const f=new Set;for(const[,a]of io.sockets.sockets){if(!a.data.userName||a.data.userName==="\u7BA1\u7406\u5458")continue;const t=a.handshake.headers.cookie;t&&!hasValidSession(t)||f.add(a.data.userName)}return _.json({occupied:!1,onlineUsers:[...f]})}_.json({occupied:!1})})),app.get("/admin",((T,_)=>_.sendFile((0,external_path_.join)(server_dirname,"admin.html")))),app.get("/",checkAuth,((T,_)=>_.sendFile((0,external_path_.join)(server_dirname,"index.html")))),app.get("/terminal/:id",checkAuth,((T,_)=>_.sendFile((0,external_path_.join)(server_dirname,"chat.html")))),app.get("/terminal-raw/:id",checkAuth,((T,_)=>_.sendFile((0,external_path_.join)(server_dirname,"terminal.html")))),app.use("/lib",express.static((0,external_path_.join)(server_dirname,"lib"),staticOpts)),app.get("/api/sessions",checkAuth,((T,_)=>{const f=getUserInfo(T.headers.cookie);_.json(manager.list(f?.name,f?.role==="admin"))})),app.get("/api/usage",((T,_)=>{try{const f=manager.listSessions().filter((t=>t.mode==="agent"&&t.alive)).map((t=>({id:t.id,title:t.title,owner:t.owner,usage:t._usage})));let a=null;for(const t of f)if(t.usage?.rateLimitInfo){a=t.usage.rateLimitInfo;break}_.json({rateLimit:a,activeSessions:f})}catch(f){_.status(500).json({error:f.message})}})),app.post("/api/sessions",checkAuth,express.json(),((T,_)=>{const f=getUserInfo(T.headers.cookie),a=manager.create({...T.body,owner:f?.name,userDir:f?.dir});_.status(201).json(a.toJSON())})),app.delete("/api/sessions/:id",checkAuth,((T,_)=>{manager.kill(T.params.id),_.status(204).end()})),app.get("/api/sessions/health",checkAuth,((T,_)=>{const f=getUserInfo(T.headers.cookie),a=manager.listSessions().filter((t=>t.mode==="agent")).filter((t=>!t.owner||!f?.name||t.owner===f.name)).map((t=>({...t.toJSON(),health:getSessionHealth(t.claudeSessionId)})));_.json({ok:!0,sessions:a})})),app.post("/api/sessions/:id/reset",checkAuth,((T,_)=>{const f=manager.get(T.params.id);if(!f||f.mode!=="agent"||typeof f.reset!="function")return _.status(404).json({ok:!1,error:"agent \u4F1A\u8BDD\u4E0D\u5B58\u5728"});f.reset(),f.emit("agent:message",{type:"system",subtype:"info",message:"\u5DF2\u91CD\u5F00\u4E3A\u5E72\u51C0\u4F1A\u8BDD\uFF0C\u4E0B\u6B21\u63D0\u95EE\u5F00\u542F\u5168\u65B0\u5BF9\u8BDD\uFF08\u65E7\u4E0A\u4E0B\u6587\u5DF2\u65AD\u5F00\uFF09"}),broadcastSessions(),_.json({ok:!0})})),app.get("/api/claude-sessions",checkAuth,((T,_)=>{const f=getUserInfo(T.headers.cookie);_.json(listLocalClaudeSessions(f?.dir))})),app.get("/api/agent/history/:sessionId",checkAuth,((T,_)=>{const f=manager.get(T.params.sessionId);if(!f||f.mode!=="agent")return _.status(404).json({ok:!1,error:"\u4F1A\u8BDD\u4E0D\u5B58\u5728"});const a=f.claudeSessionId;if(!a)return _.json({ok:!0,messages:[],total:0,hasMore:!1});const t=(0,external_path_.join)((0,external_os_.homedir)(),".claude","projects");let c=null;try{if((0,external_fs_.existsSync)(t))for(const u of(0,external_fs_.readdirSync)(t,{withFileTypes:!0})){if(!u.isDirectory())continue;const p=(0,external_path_.join)(t,u.name,a+".jsonl");if((0,external_fs_.existsSync)(p)){c=p;break}}}catch{}if(!c)return _.json({ok:!0,messages:[],total:0,hasMore:!1});let l;try{l=(0,external_fs_.readFileSync)(c,"utf8").split(`
382
- `).filter(Boolean)}catch(u){return _.status(500).json({ok:!1,error:u.message})}const m=Math.max(0,parseInt(T.query.offset,10)||0),o=Math.min(200,Math.max(1,parseInt(T.query.limit,10)||50)),i=l.length,n=i-m,e=Math.max(0,n-o),r=[];for(let u=e;u<n;u++)try{const p=JSON.parse(l[u]);if((p.type==="human"||p.type==="user")&&p.message?.content){const s=typeof p.message.content=="string"?p.message.content:p.message.content.find((v=>v.type==="text"))?.text||"";s&&r.push({type:"user",content:s,timestamp:p.timestamp})}else p.type==="assistant"&&p.message?.content?r.push({type:"assistant",message:p.message,timestamp:p.timestamp}):p.type==="result"&&r.push({type:"result",total_cost_usd:p.costUSD||p.total_cost_usd,timestamp:p.timestamp})}catch{}_.json({ok:!0,messages:r,total:i,hasMore:e>0})})),app.get("/api/dirs",checkAuth,((T,_)=>{const f=getUserInfo(T.headers.cookie);if(!f||f.role!=="admin")return _.status(403).json({error:"\u6CA1\u6709\u76EE\u5F55\u6D4F\u89C8\u6743\u9650"});const a=f.dir?(0,external_path_.resolve)((0,external_path_.normalize)(f.dir)):null,t=process.platform==="win32"?"\\":"/";let c=T.query.path||a||process.env.MYHI_CWD||(0,external_os_.homedir)(),l=(0,external_path_.resolve)((0,external_path_.normalize)(c)).replace(/[/\\]+$/,"");process.platform==="win32"&&/^[A-Za-z]:$/.test(l)&&(l+="\\"),a&&!l.startsWith(a)&&(l=a);try{const m=(0,external_fs_.readdirSync)(l,{withFileTypes:!0}).filter((n=>n.isDirectory()&&n.name!==".git")).map((n=>({name:n.name,path:l.replace(/[/\\]+$/,"")+t+n.name}))).sort(((n,e)=>n.name.localeCompare(e.name))),o=(0,external_path_.join)(l,".."),i=!a||(0,external_path_.resolve)((0,external_path_.normalize)(o)).startsWith(a);_.json({current:l,parent:i&&o!==l?o:null,dirs:m})}catch(m){_.status(400).json({error:m.message})}}));function safePath(T,_){const f=(0,external_path_.resolve)((0,external_path_.normalize)(T)),a=(0,external_path_.resolve)((0,external_path_.normalize)((0,external_path_.join)(T,_||"")));if(a!==f&&!a.startsWith(f+external_path_.sep))return null;try{const t=(0,external_fs_.realpathSync)(f),c=(0,external_fs_.existsSync)(a)?(0,external_fs_.realpathSync)(a):a;if(c!==t&&!c.startsWith(t+external_path_.sep))return null}catch{}return a}function walkFiles(T,_,f=[],a=0){if(a>20)return f;let t;try{t=(0,external_fs_.readdirSync)(T,{withFileTypes:!0})}catch{return f}for(const c of t){if(c.name===".git")continue;const l=(0,external_path_.join)(T,c.name),m=_?_+"/"+c.name:c.name;try{const o=(0,external_fs_.lstatSync)(l);if(o.isSymbolicLink())continue;o.isDirectory()?walkFiles(l,m,f,a+1):o.isFile()&&f.push({name:m,size:o.size,mtime:o.mtimeMs})}catch{}}return f}function fileHash(T){return new Promise(((_,f)=>{const a=(0,external_crypto_.createHash)("sha256"),t=(0,external_fs_.createReadStream)(T);t.on("data",(c=>a.update(c))),t.on("end",(()=>_(a.digest("hex")))),t.on("error",f)}))}app.get("/api/files/:sessionId",checkAuth,((T,_)=>{const f=manager.get(T.params.sessionId);if(!f)return _.status(404).json({error:"\u4F1A\u8BDD\u4E0D\u5B58\u5728"});const a=T.query.path||"",t=safePath(f.cwd,a);if(!t)return _.status(403).json({error:"\u8DEF\u5F84\u8D8A\u754C"});try{if(!(0,external_fs_.statSync)(t).isDirectory())return _.status(400).json({error:"\u4E0D\u662F\u76EE\u5F55"});const c=(0,external_fs_.readdirSync)(t,{withFileTypes:!0}).filter((o=>o.name!==".git")).map((o=>{const i=(0,external_path_.join)(t,o.name);try{const n=(0,external_fs_.lstatSync)(i);return n.isSymbolicLink()?null:{name:o.name,isDir:n.isDirectory(),size:n.isFile()?n.size:void 0,mtime:n.mtimeMs}}catch{return null}})).filter(Boolean).sort(((o,i)=>i.isDir-o.isDir||o.name.localeCompare(i.name))),l=a?a.replace(/\\/g,"/"):"",m=l.includes("/")?l.slice(0,l.lastIndexOf("/")):l?"":null;_.json({ok:!0,cwd:f.cwd,path:l,parent:m,entries:c})}catch(c){_.status(500).json({error:c.message})}})),app.get("/api/files/:sessionId/download",checkAuth,((T,_)=>{const f=manager.get(T.params.sessionId);if(!f)return _.status(404).json({error:"\u4F1A\u8BDD\u4E0D\u5B58\u5728"});const a=T.query.path;if(!a)return _.status(400).json({error:"\u7F3A\u5C11 path \u53C2\u6570"});const t=safePath(f.cwd,a);if(!t)return _.status(403).json({error:"\u8DEF\u5F84\u8D8A\u754C"});try{const c=(0,external_fs_.statSync)(t);if(!c.isFile())return _.status(400).json({error:"\u4E0D\u662F\u6587\u4EF6"});const l=a.split("/").pop();_.setHeader("Content-Disposition",`attachment; filename*=UTF-8''${encodeURIComponent(l)}`),_.setHeader("Content-Length",c.size),(0,external_fs_.createReadStream)(t).pipe(_)}catch{_.status(404).json({error:"\u6587\u4EF6\u4E0D\u5B58\u5728"})}})),app.post("/api/files/:sessionId/upload",checkAuth,((T,_)=>{const f=manager.get(T.params.sessionId);if(!f)return _.status(404).json({error:"\u4F1A\u8BDD\u4E0D\u5B58\u5728"});const a=(T.query.path||"").replace(/\\/g,"/"),t=safePath(f.cwd,a);if(!t)return _.status(403).json({error:"\u8DEF\u5F84\u8D8A\u754C"});(0,external_fs_.mkdirSync)(t,{recursive:!0}),multer({storage:multer.diskStorage({destination:(c,l,m)=>{l.originalname=Buffer.from(l.originalname,"latin1").toString("utf8");const o=l.originalname.includes("/")?l.originalname.slice(0,l.originalname.lastIndexOf("/")):"",i=o?(0,external_path_.join)(t,o):t,n=(0,external_path_.resolve)((0,external_path_.normalize)(i)),e=(0,external_path_.resolve)((0,external_path_.normalize)(f.cwd));if(n!==e&&!n.startsWith(e+external_path_.sep))return m(new Error("\u8DEF\u5F84\u8D8A\u754C"));(0,external_fs_.mkdirSync)(i,{recursive:!0}),m(null,i)},filename:(c,l,m)=>{const o=l.originalname.includes("/")?l.originalname.slice(l.originalname.lastIndexOf("/")+1):l.originalname;m(null,o)}}),limits:{fileSize:500*1024*1024}}).array("files",100)(T,_,(c=>{if(c)return _.status(400).json({error:c.message});if(!T.files?.length)return _.status(400).json({error:"\u6CA1\u6709\u6587\u4EF6"});const l=T.files.map((m=>({name:m.originalname,size:m.size,path:(0,external_path_.resolve)(m.path).replace(/\\/g,"/")})));io.emit("files:changed",{sessionId:f.id}),_.json({ok:!0,files:l})}))})),app.delete("/api/files/:sessionId",checkAuth,((T,_)=>{const f=manager.get(T.params.sessionId);if(!f)return _.status(404).json({error:"\u4F1A\u8BDD\u4E0D\u5B58\u5728"});const a=T.query.path;if(!a)return _.status(400).json({error:"\u7F3A\u5C11 path \u53C2\u6570"});const t=safePath(f.cwd,a);if(!t)return _.status(403).json({error:"\u8DEF\u5F84\u8D8A\u754C"});if((0,external_path_.resolve)((0,external_path_.normalize)(t))===(0,external_path_.resolve)((0,external_path_.normalize)(f.cwd)))return _.status(403).json({error:"\u4E0D\u80FD\u5220\u9664\u4F1A\u8BDD\u6839\u76EE\u5F55"});try{(0,external_fs_.statSync)(t).isDirectory()?(0,external_fs_.rmSync)(t,{recursive:!0}):(0,external_fs_.unlinkSync)(t),io.emit("files:changed",{sessionId:f.id}),_.json({ok:!0})}catch(c){_.status(500).json({error:c.message})}})),app.get("/api/files/:sessionId/manifest",checkAuth,(async(T,_)=>{const f=manager.get(T.params.sessionId);if(!f)return _.status(404).json({error:"\u4F1A\u8BDD\u4E0D\u5B58\u5728"});const a=T.query.path||"",t=safePath(f.cwd,a);if(!t)return _.status(403).json({error:"\u8DEF\u5F84\u8D8A\u754C"});try{const c=walkFiles(t,""),l=[];for(const m of c){if(m.size>=50*1024*1024){l.push(m);continue}try{const o=await fileHash((0,external_path_.join)(t,m.name));l.push({...m,hash:o})}catch{l.push(m)}}_.json({ok:!0,cwd:f.cwd,path:a,files:l})}catch(c){_.status(500).json({error:c.message})}})),app.get("/api/me",checkAuth,((T,_)=>{const f=getUserInfo(T.headers.cookie);_.json({name:f?.name||"\u7528\u6237",role:f?.role||"admin",exclusive:isExclusiveMode(),hasUsers:hasUsers(),dir:f?.dir||null})}));const gatewayFile=(0,external_path_.join)(configDir,"gateway.json");function loadGateway(){try{return JSON.parse((0,external_fs_.readFileSync)(gatewayFile,"utf8"))}catch{return{baseUrl:"",apiKey:""}}}app.get("/api/gateway",checkAdminAuth,((T,_)=>{const f=loadGateway();_.json({baseUrl:f.baseUrl||"",apiKey:f.apiKey?f.apiKey.length>12?f.apiKey.slice(0,6)+"***"+f.apiKey.slice(-4):"***":"",hasKey:!!f.apiKey})})),app.post("/api/gateway",checkAdminAuth,express.json(),((T,_)=>{const{baseUrl:f,apiKey:a}=T.body||{},t=loadGateway(),c={baseUrl:typeof f=="string"?f.trim():t.baseUrl,apiKey:!a||a.includes("***")?t.apiKey:a.trim()};try{(0,external_fs_.mkdirSync)(configDir,{recursive:!0}),(0,external_fs_.writeFileSync)(gatewayFile,JSON.stringify(c,null,2),{mode:384}),_.json({ok:!0})}catch(l){_.status(500).json({error:l.message})}}));function getPkgVersion(){for(const T of["../package.json","./package.json","package.json"])try{const _=JSON.parse((0,external_fs_.readFileSync)((0,external_path_.join)(server_dirname,T),"utf8")).version;if(_&&_!=="0.0.0")return _}catch{}return"0.0.0"}const PKG_VERSION=getPkgVersion();let _latestVersion=null,_lastVersionCheck=0;const VERSION_CHECK_INTERVAL=600*1e3;async function checkLatestVersion(){const T=Date.now();if(_latestVersion&&T-_lastVersionCheck<VERSION_CHECK_INTERVAL)return _latestVersion;try{const _=await fetch("https://registry.npmjs.org/@wendongfly/myhi/latest",{headers:{Accept:"application/json"},signal:AbortSignal.timeout(8e3)});_.ok&&(_latestVersion=(await _.json()).version,_lastVersionCheck=T)}catch{}return _latestVersion}app.get("/api/version",(async(T,_)=>{const f=getPkgVersion(),a=await checkLatestVersion();_.json({current:f,latest:a||f,updateAvailable:a&&a!==f})}));function isSystemd(){return process.platform==="linux"&&(process.env.INVOCATION_ID||process.env.JOURNAL_STREAM)}function getNpmUpgradeCmd(){return process.platform==="win32"?"npm.cmd install -g @wendongfly/myhi@latest":"npm install -g @wendongfly/myhi@latest"}function restartService(){if(process.send)process.send({type:"restart"});else if(isSystemd())try{(0,external_child_process_namespaceObject.spawn)("sudo",["systemctl","restart","myhi.service"],{detached:!0,stdio:"ignore"}).unref()}catch{}else process.exit(0)}app.get("/api/server-config",checkAuth,((T,_)=>{const f=process.env.ASR_URL||(()=>{try{return JSON.parse((0,external_fs_.readFileSync)((0,external_path_.join)((0,external_os_.homedir)(),".myhi","asr.json"),"utf8")).url}catch{return""}})();_.json({asrUrl:f||""})})),app.post("/api/server-config",checkAuth,express.json(),((T,_)=>{const{asrUrl:f}=T.body||{},a=(0,external_path_.join)((0,external_os_.homedir)(),".myhi","asr.json");try{const t=(f||"").trim();(0,external_fs_.writeFileSync)(a,JSON.stringify({url:t},null,2),"utf8"),_.json({ok:!0})}catch(t){_.status(500).json({error:t.message})}})),app.post("/api/restart",checkAuth,((T,_)=>{_.json({ok:!0}),setTimeout((()=>restartService()),300)})),app.post("/api/update",checkAuth,(async(T,_)=>{try{await execAsync(getNpmUpgradeCmd(),{encoding:"utf8",timeout:12e4}),console.log("[myhi] \u5347\u7EA7\u5B8C\u6210"),_.json({ok:!0,message:"\u5347\u7EA7\u5B8C\u6210\uFF0C\u670D\u52A1\u5C06\u81EA\u52A8\u91CD\u542F..."}),setTimeout((()=>restartService()),500)}catch(f){const a=(f.stderr||f.stdout||f.message||"").split(`
382
+ `).filter(Boolean)}catch(u){return _.status(500).json({ok:!1,error:u.message})}const m=Math.max(0,parseInt(T.query.offset,10)||0),o=Math.min(200,Math.max(1,parseInt(T.query.limit,10)||50)),i=l.length,n=i-m,e=Math.max(0,n-o),r=[];for(let u=e;u<n;u++)try{const p=JSON.parse(l[u]);if((p.type==="human"||p.type==="user")&&p.message?.content){const s=typeof p.message.content=="string"?p.message.content:p.message.content.find((v=>v.type==="text"))?.text||"";s&&r.push({type:"user",content:s,timestamp:p.timestamp})}else p.type==="assistant"&&p.message?.content?r.push({type:"assistant",message:p.message,timestamp:p.timestamp}):p.type==="result"&&r.push({type:"result",total_cost_usd:p.costUSD||p.total_cost_usd,timestamp:p.timestamp})}catch{}_.json({ok:!0,messages:r,total:i,hasMore:e>0})})),app.get("/api/dirs",checkAuth,((T,_)=>{const f=getUserInfo(T.headers.cookie);if(!f||f.role!=="admin")return _.status(403).json({error:"\u6CA1\u6709\u76EE\u5F55\u6D4F\u89C8\u6743\u9650"});const a=f.dir?(0,external_path_.resolve)((0,external_path_.normalize)(f.dir)):null,t=process.platform==="win32"?"\\":"/";let c=T.query.path||a||process.env.MYHI_CWD||(0,external_os_.homedir)(),l=(0,external_path_.resolve)((0,external_path_.normalize)(c)).replace(/[/\\]+$/,"");process.platform==="win32"&&/^[A-Za-z]:$/.test(l)&&(l+="\\"),a&&!l.startsWith(a)&&(l=a);try{const m=(0,external_fs_.readdirSync)(l,{withFileTypes:!0}).filter((n=>n.isDirectory()&&n.name!==".git")).map((n=>({name:n.name,path:l.replace(/[/\\]+$/,"")+t+n.name}))).sort(((n,e)=>n.name.localeCompare(e.name))),o=(0,external_path_.join)(l,".."),i=!a||(0,external_path_.resolve)((0,external_path_.normalize)(o)).startsWith(a);_.json({current:l,parent:i&&o!==l?o:null,dirs:m})}catch(m){_.status(400).json({error:m.message})}}));function safePath(T,_){const f=(0,external_path_.resolve)((0,external_path_.normalize)(T)),a=(0,external_path_.resolve)((0,external_path_.normalize)((0,external_path_.join)(T,_||"")));if(a!==f&&!a.startsWith(f+external_path_.sep))return null;try{const t=(0,external_fs_.realpathSync)(f),c=(0,external_fs_.existsSync)(a)?(0,external_fs_.realpathSync)(a):a;if(c!==t&&!c.startsWith(t+external_path_.sep))return null}catch{}return a}function walkFiles(T,_,f=[],a=0){if(a>20)return f;let t;try{t=(0,external_fs_.readdirSync)(T,{withFileTypes:!0})}catch{return f}for(const c of t){if(c.name===".git")continue;const l=(0,external_path_.join)(T,c.name),m=_?_+"/"+c.name:c.name;try{const o=(0,external_fs_.lstatSync)(l);if(o.isSymbolicLink())continue;o.isDirectory()?walkFiles(l,m,f,a+1):o.isFile()&&f.push({name:m,size:o.size,mtime:o.mtimeMs})}catch{}}return f}function fileHash(T){return new Promise(((_,f)=>{const a=(0,external_crypto_.createHash)("sha256"),t=(0,external_fs_.createReadStream)(T);t.on("data",(c=>a.update(c))),t.on("end",(()=>_(a.digest("hex")))),t.on("error",f)}))}app.get("/api/files/:sessionId",checkAuth,((T,_)=>{const f=manager.get(T.params.sessionId);if(!f)return _.status(404).json({error:"\u4F1A\u8BDD\u4E0D\u5B58\u5728"});const a=T.query.path||"",t=safePath(f.cwd,a);if(!t)return _.status(403).json({error:"\u8DEF\u5F84\u8D8A\u754C"});try{if(!(0,external_fs_.statSync)(t).isDirectory())return _.status(400).json({error:"\u4E0D\u662F\u76EE\u5F55"});const c=(0,external_fs_.readdirSync)(t,{withFileTypes:!0}).filter((o=>o.name!==".git")).map((o=>{const i=(0,external_path_.join)(t,o.name);try{const n=(0,external_fs_.lstatSync)(i);return n.isSymbolicLink()?null:{name:o.name,isDir:n.isDirectory(),size:n.isFile()?n.size:void 0,mtime:n.mtimeMs}}catch{return null}})).filter(Boolean).sort(((o,i)=>i.isDir-o.isDir||o.name.localeCompare(i.name))),l=a?a.replace(/\\/g,"/"):"",m=l.includes("/")?l.slice(0,l.lastIndexOf("/")):l?"":null;_.json({ok:!0,cwd:f.cwd,path:l,parent:m,entries:c})}catch(c){_.status(500).json({error:c.message})}})),app.get("/api/files/:sessionId/download",checkAuth,((T,_)=>{const f=manager.get(T.params.sessionId);if(!f)return _.status(404).json({error:"\u4F1A\u8BDD\u4E0D\u5B58\u5728"});const a=T.query.path;if(!a)return _.status(400).json({error:"\u7F3A\u5C11 path \u53C2\u6570"});const t=safePath(f.cwd,a);if(!t)return _.status(403).json({error:"\u8DEF\u5F84\u8D8A\u754C"});try{const c=(0,external_fs_.statSync)(t);if(!c.isFile())return _.status(400).json({error:"\u4E0D\u662F\u6587\u4EF6"});const l=a.split("/").pop();_.setHeader("Content-Disposition",`attachment; filename*=UTF-8''${encodeURIComponent(l)}`),_.setHeader("Content-Length",c.size),(0,external_fs_.createReadStream)(t).pipe(_)}catch{_.status(404).json({error:"\u6587\u4EF6\u4E0D\u5B58\u5728"})}})),app.post("/api/files/:sessionId/upload",checkAuth,((T,_)=>{const f=manager.get(T.params.sessionId);if(!f)return _.status(404).json({error:"\u4F1A\u8BDD\u4E0D\u5B58\u5728"});const a=(T.query.path||"").replace(/\\/g,"/"),t=safePath(f.cwd,a);if(!t)return _.status(403).json({error:"\u8DEF\u5F84\u8D8A\u754C"});(0,external_fs_.mkdirSync)(t,{recursive:!0}),multer({storage:multer.diskStorage({destination:(c,l,m)=>{l.originalname=Buffer.from(l.originalname,"latin1").toString("utf8");const o=l.originalname.includes("/")?l.originalname.slice(0,l.originalname.lastIndexOf("/")):"",i=o?(0,external_path_.join)(t,o):t,n=(0,external_path_.resolve)((0,external_path_.normalize)(i)),e=(0,external_path_.resolve)((0,external_path_.normalize)(f.cwd));if(n!==e&&!n.startsWith(e+external_path_.sep))return m(new Error("\u8DEF\u5F84\u8D8A\u754C"));(0,external_fs_.mkdirSync)(i,{recursive:!0}),m(null,i)},filename:(c,l,m)=>{const o=l.originalname.includes("/")?l.originalname.slice(l.originalname.lastIndexOf("/")+1):l.originalname;m(null,o)}}),limits:{fileSize:500*1024*1024}}).array("files",100)(T,_,(c=>{if(c)return _.status(400).json({error:c.message});if(!T.files?.length)return _.status(400).json({error:"\u6CA1\u6709\u6587\u4EF6"});const l=T.files.map((m=>({name:m.originalname,size:m.size,path:(0,external_path_.resolve)(m.path).replace(/\\/g,"/")})));io.emit("files:changed",{sessionId:f.id}),_.json({ok:!0,files:l})}))})),app.delete("/api/files/:sessionId",checkAuth,((T,_)=>{const f=manager.get(T.params.sessionId);if(!f)return _.status(404).json({error:"\u4F1A\u8BDD\u4E0D\u5B58\u5728"});const a=T.query.path;if(!a)return _.status(400).json({error:"\u7F3A\u5C11 path \u53C2\u6570"});const t=safePath(f.cwd,a);if(!t)return _.status(403).json({error:"\u8DEF\u5F84\u8D8A\u754C"});if((0,external_path_.resolve)((0,external_path_.normalize)(t))===(0,external_path_.resolve)((0,external_path_.normalize)(f.cwd)))return _.status(403).json({error:"\u4E0D\u80FD\u5220\u9664\u4F1A\u8BDD\u6839\u76EE\u5F55"});try{(0,external_fs_.statSync)(t).isDirectory()?(0,external_fs_.rmSync)(t,{recursive:!0}):(0,external_fs_.unlinkSync)(t),io.emit("files:changed",{sessionId:f.id}),_.json({ok:!0})}catch(c){_.status(500).json({error:c.message})}})),app.get("/api/files/:sessionId/manifest",checkAuth,(async(T,_)=>{const f=manager.get(T.params.sessionId);if(!f)return _.status(404).json({error:"\u4F1A\u8BDD\u4E0D\u5B58\u5728"});const a=T.query.path||"",t=safePath(f.cwd,a);if(!t)return _.status(403).json({error:"\u8DEF\u5F84\u8D8A\u754C"});try{const c=walkFiles(t,""),l=[];for(const m of c){if(m.size>=50*1024*1024){l.push(m);continue}try{const o=await fileHash((0,external_path_.join)(t,m.name));l.push({...m,hash:o})}catch{l.push(m)}}_.json({ok:!0,cwd:f.cwd,path:a,files:l})}catch(c){_.status(500).json({error:c.message})}})),app.get("/api/me",checkAuth,((T,_)=>{const f=getUserInfo(T.headers.cookie);_.json({name:f?.name||"\u7528\u6237",role:f?.role||"admin",exclusive:isExclusiveMode(),hasUsers:hasUsers(),dir:f?.dir||null})}));const gatewayFile=(0,external_path_.join)(configDir,"gateway.json");function loadGateway(){try{return JSON.parse((0,external_fs_.readFileSync)(gatewayFile,"utf8"))}catch{return{baseUrl:"",apiKey:""}}}app.get("/api/gateway",checkAdminAuth,((T,_)=>{const f=loadGateway();_.json({baseUrl:f.baseUrl||"",apiKey:f.apiKey?f.apiKey.length>12?f.apiKey.slice(0,6)+"***"+f.apiKey.slice(-4):"***":"",hasKey:!!f.apiKey})})),app.post("/api/gateway",checkAdminAuth,express.json(),((T,_)=>{const{baseUrl:f,apiKey:a}=T.body||{},t=loadGateway(),c={baseUrl:typeof f=="string"?f.trim():t.baseUrl,apiKey:!a||a.includes("***")?t.apiKey:a.trim()};try{(0,external_fs_.mkdirSync)(configDir,{recursive:!0}),(0,external_fs_.writeFileSync)(gatewayFile,JSON.stringify(c,null,2),{mode:384}),_.json({ok:!0})}catch(l){_.status(500).json({error:l.message})}}));function getPkgVersion(){for(const T of["../package.json","./package.json","package.json"])try{const _=JSON.parse((0,external_fs_.readFileSync)((0,external_path_.join)(server_dirname,T),"utf8")).version;if(_&&_!=="0.0.0")return _}catch{}return"0.0.0"}const PKG_VERSION=getPkgVersion();let _latestVersion=null,_lastVersionCheck=0;const VERSION_CHECK_INTERVAL=600*1e3;async function checkLatestVersion(){const T=Date.now();if(_latestVersion&&T-_lastVersionCheck<VERSION_CHECK_INTERVAL)return _latestVersion;try{const _=await fetch("https://registry.npmjs.org/@wendongfly/myhi/latest",{headers:{Accept:"application/json"},signal:AbortSignal.timeout(8e3)});_.ok&&(_latestVersion=(await _.json()).version,_lastVersionCheck=T)}catch{}return _latestVersion}app.get("/api/version",(async(T,_)=>{const f=getPkgVersion(),a=await checkLatestVersion();_.json({current:f,latest:a||f,updateAvailable:a&&a!==f})}));function isSystemd(){return process.platform==="linux"&&(process.env.INVOCATION_ID||process.env.JOURNAL_STREAM)}function getNpmUpgradeCmd(){const T="--registry https://registry.npmjs.org";return process.platform==="win32"?`npm.cmd install -g @wendongfly/myhi@latest ${T}`:`npm install -g @wendongfly/myhi@latest ${T}`}function restartService(){if(process.send)process.send({type:"restart"});else if(isSystemd())try{(0,external_child_process_namespaceObject.spawn)("sudo",["systemctl","restart","myhi.service"],{detached:!0,stdio:"ignore"}).unref()}catch{}else process.exit(0)}app.get("/api/server-config",checkAuth,((T,_)=>{const f=process.env.ASR_URL||(()=>{try{return JSON.parse((0,external_fs_.readFileSync)((0,external_path_.join)((0,external_os_.homedir)(),".myhi","asr.json"),"utf8")).url}catch{return""}})();_.json({asrUrl:f||""})})),app.post("/api/server-config",checkAuth,express.json(),((T,_)=>{const{asrUrl:f}=T.body||{},a=(0,external_path_.join)((0,external_os_.homedir)(),".myhi","asr.json");try{const t=(f||"").trim();(0,external_fs_.writeFileSync)(a,JSON.stringify({url:t},null,2),"utf8"),_.json({ok:!0})}catch(t){_.status(500).json({error:t.message})}})),app.post("/api/restart",checkAuth,((T,_)=>{_.json({ok:!0}),setTimeout((()=>restartService()),300)})),app.post("/api/update",checkAuth,(async(T,_)=>{try{await execAsync(getNpmUpgradeCmd(),{encoding:"utf8",timeout:12e4}),console.log("[myhi] \u5347\u7EA7\u5B8C\u6210"),_.json({ok:!0,message:"\u5347\u7EA7\u5B8C\u6210\uFF0C\u670D\u52A1\u5C06\u81EA\u52A8\u91CD\u542F..."}),setTimeout((()=>restartService()),500)}catch(f){const a=(f.stderr||f.stdout||f.message||"").split(`
383
383
  `).filter((t=>t.trim())).slice(-5).join(`
384
384
  `);console.error("[myhi] \u5347\u7EA7\u5931\u8D25:",a),_.status(500).json({ok:!1,error:`npm install \u5931\u8D25\uFF1A
385
385
  `+a+`
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wendongfly/myhi",
3
- "version": "1.3.95",
3
+ "version": "1.3.97",
4
4
  "description": "Web-based terminal sharing with chat UI — control your terminal from phone via LAN/Tailscale",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",