@wendongfly/myhi 1.3.139 → 1.3.141

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
@@ -381,7 +381,7 @@ Content-Length: `+S+`\r
381
381
  `).filter(Boolean);let r=null,p=null,f=0,s="";for(const m of t)try{const b=JSON.parse(m);if(r||(r=b),p=b,f++,!s&&b.type==="human"||b.type==="user"&&b.message?.content){const _=b.message.content;if(typeof _=="string")s=_.slice(0,120);else if(Array.isArray(_)){const g=_.find((w=>w.type==="text"));g&&(s=g.text?.slice(0,120)||"")}}}catch{}let v=u.name;if(process.platform==="win32"&&/^[A-Za-z]--/.test(v)?v=v[0]+":\\"+v.slice(3).replace(/-/g,"\\"):v.startsWith("-")&&(v=v.replace(/-/g,"/")),n&&!(0,external_path_.resolve)((0,external_path_.normalize)(v)).replace(/[\\/]+$/,"").startsWith(n))continue;l.push({sessionId:i,projectDir:u.name,projectPath:v,messageCount:f,summary:s,createdAt:r?.timestamp||null,updatedAt:p?.timestamp||null})}catch{}}}catch{}}}catch{}l.sort(((o,u)=>new Date(u.updatedAt||0).getTime()-new Date(o.updatedAt||0).getTime()));const e=l.slice(0,50);return _claudeSessionsCache=e,_claudeSessionsCacheTime=Date.now(),e.slice(0,20)}const DEFAULT_SHELL=process.platform==="win32"?process.env.SHELL||"powershell.exe":process.env.SHELL||"/bin/bash",sessions_CONFIG_DIR=(0,external_path_.join)((0,external_os_.homedir)(),".myhi"),SESSIONS_FILE=(0,external_path_.join)(sessions_CONFIG_DIR,"sessions.json"),savePath=null;function loadSavedConfigs(){try{return JSON.parse((0,external_fs_.readFileSync)(SESSIONS_FILE,"utf8"))}catch{return[]}}function writeSavedConfigs(C){try{(0,external_fs_.mkdirSync)(sessions_CONFIG_DIR,{recursive:!0});const h=SESSIONS_FILE+".tmp";(0,external_fs_.writeFileSync)(h,JSON.stringify(C,null,2),{mode:384}),(0,external_fs_.renameSync)(h,SESSIONS_FILE)}catch(h){console.error("[sessions] \u6301\u4E45\u5316\u5199\u5165\u5931\u8D25:",h.message)}}class Session extends external_events_.EventEmitter{constructor(h,l={}){super(),this.id=h,this.createdAt=l.createdAt||new Date().toISOString(),this.title=l.title||DEFAULT_SHELL,this.cwd=l.cwd||process.env.MYHI_CWD||process.cwd(),this.tags=Array.isArray(l.tags)?l.tags:[],this.initCmd=l.initCmd||null,this.owner=l.owner||null,this.cols=l.cols||120,this.rows=l.rows||30,this._lastActivityAt=l.lastActivityAt||0,this._viewers=new Set,this._viewerNames=new Map,this.controlHolder=null,this.controlHolderName=null,this.lastInputTime=null;const n={...process.env};if(delete n.CLAUDECODE,delete n.CLAUDE_CODE_ENTRYPOINT,delete n.CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC,l.userDir){const o=(0,external_path_.join)(l.userDir,".gitconfig");(0,external_fs_.existsSync)(o)&&(n.GIT_CONFIG_GLOBAL=o)}this._pty=external_node_pty_namespaceObject.spawn(DEFAULT_SHELL,[],{name:"xterm-256color",cols:this.cols,rows:this.rows,cwd:this.cwd,env:n,useConpty:!1}),this._scrollback="";const e=100*1024;if(this._pty.onData((o=>{this.emit("data",o),this._scrollback+=o,this._scrollback.length>e&&(this._scrollback=this._scrollback.slice(this._scrollback.length-e))})),this._pty.onExit((({exitCode:o})=>{this.exitCode=o,this.emit("exit",o)})),l.initCmd){const o=l.initCmd;let u=!1;const d=c=>{!u&&/[$>#\]]\s*$/.test(c.trimEnd())&&(u=!0,this._pty.off("data",d),setTimeout((()=>this._pty.write(o+"\r")),120))};this._pty.onData(d),setTimeout((()=>{u||(u=!0,this._pty.write(o+"\r"))}),3e3)}}write(h){h!=null&&(this._lastActivityAt=Date.now(),this._pty.write(h))}resize(h,l){this.cols=h,this.rows=l,this._pty.resize(h,l)}kill(){try{this._pty.kill()}catch{}}addViewer(h,l){this._viewers.add(h),l&&this._viewerNames.set(h,l)}removeViewer(h){this._viewers.delete(h),this._viewerNames.delete(h)}get viewerCount(){return new Set(this._viewerNames.values()).size||this._viewers.size}takeControl(h,l){this.controlHolder=h,this.controlHolderName=l||null,this.lastInputTime=Date.now()}releaseControl(){this.controlHolder=null,this.controlHolderName=null,this.lastInputTime=null}isController(h){return this.controlHolder===h}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 h of loadSavedConfigs())try{if(h.mode==="agent"){if(h.claudeSessionId){const l=(0,external_crypto_.randomUUID)(),n=new AgentSession(l,{...h,resumeSessionId:h.claudeSessionId});n.on("session-id",(()=>this._persist())),this._agentSessions.set(l,n),console.log(`[\u4F1A\u8BDD] \u6062\u590D Agent "${h.title}" (claude: ${h.claudeSessionId.slice(0,8)}...)`)}}else this._spawn((0,external_crypto_.randomUUID)(),h,!1)}catch(l){console.warn(`[\u4F1A\u8BDD] \u6062\u590D "${h.title}" \u5931\u8D25:`,l.message)}}_spawn(h,l,n=!0){const e=new Session(h,l);return this._sessions.set(h,e),e.on("exit",(()=>{setTimeout((()=>this._sessions.delete(h)),300*1e3)})),n&&this._persist(),e}create(h={}){return this._spawn((0,external_crypto_.randomUUID)(),h,!0)}createAgent(h={}){const l=(0,external_crypto_.randomUUID)(),n=new AgentSession(l,h);return this._agentSessions.set(l,n),n.on("session-id",(()=>this._persist())),this._persist(),n}get(h){return this._sessions.get(h)||this._agentSessions.get(h)}list(h,l=!1){let n=[...this._sessions.values()].map((o=>({...o.toJSON(),mode:"pty"}))),e=[...this._agentSessions.values()].map((o=>o.toJSON()));return h&&!l&&(n=n.filter((o=>!o.owner||o.owner===h)),e=e.filter((o=>!o.owner||o.owner===h))),[...n,...e]}listSessions(){return[...this._sessions.values(),...this._agentSessions.values()]}kill(h){const l=this._sessions.get(h);if(l){l.kill(),this._sessions.delete(h),this._persist();return}const n=this._agentSessions.get(h);n&&(n.kill(),this._agentSessions.delete(h),this._persist())}persist(){this._persist()}_persist(){const h=[...this._sessions.values()].filter((n=>n.exitCode===void 0)).map((n=>({mode:"pty",title:n.title,cwd:n.cwd,tags:n.tags?.length?n.tags:void 0,createdAt:n.createdAt||void 0,lastActivityAt:n._lastActivityAt||void 0,initCmd:n.initCmd||void 0,owner:n.owner||void 0}))),l=[...this._agentSessions.values()].filter((n=>n.alive)).map((n=>({mode:"agent",title:n.title,firstUserText:n.firstUserText||void 0,cwd:n.cwd,tags:n.tags?.length?n.tags:void 0,createdAt:n.createdAt||void 0,lastActivityAt:n._lastActivityAt||void 0,owner:n.owner||void 0,userDir:n.userDir||void 0,claudeSessionId:n.claudeSessionId||void 0})));writeSavedConfigs([...h,...l])}}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(C){console.warn("[\u89D2\u8272] \u52A0\u8F7D roles.json \u5931\u8D25:",C.message)}return rolesConfig=null,null}function lookupPassword(C){if(!rolesConfig?.passwords)return null;const h=rolesConfig.passwords[C];return h?{name:h.name||"\u7528\u6237",role:h.role||"viewer"}:null}function hasPermission(C,h){return(ROLE_LEVELS[C]||0)>=(ROLE_LEVELS[h]||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(C){console.warn("[\u7528\u6237] \u52A0\u8F7D users.json \u5931\u8D25:",C.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(C){usersConfig||(usersConfig={users:{}}),usersConfig.exclusive=!!C,_writeUsers()}function lookupUser(C){if(!usersConfig?.users)return null;const h=usersConfig.users[C];return h?{name:h.name||"\u7528\u6237",dir:h.dir}:null}function listUsers(){return usersConfig?.users?Object.entries(usersConfig.users).map((([C,h])=>({password:C.slice(0,2)+"*".repeat(C.length-2),name:h.name,dir:h.dir}))):[]}function addUser(C,h,l){usersConfig||(usersConfig={users:{}}),usersConfig.users||(usersConfig.users={}),usersConfig.users[C]={name:h,dir:l},_writeUsers()}function removeUser(C){return!usersConfig?.users||!usersConfig.users[C]?!1:(delete usersConfig.users[C],_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,server_configDir=(0,external_path_.join)((0,external_os_.homedir)(),".myhi");(0,external_fs_.mkdirSync)(server_configDir,{recursive:!0});let httpsServer=null,httpServer=null;function getOrCreateCert(){const C=(0,external_path_.join)(server_configDir,"cert.pem"),h=(0,external_path_.join)(server_configDir,"key.pem");if((0,external_fs_.existsSync)(C)&&(0,external_fs_.existsSync)(h))try{return{cert:(0,external_fs_.readFileSync)(C),key:(0,external_fs_.readFileSync)(h)}}catch{}try{const l=selfsigned.generate([{name:"commonName",value:"myhi"}],{days:3650,keySize:2048});return(0,external_fs_.writeFileSync)(C,l.cert,"utf8"),(0,external_fs_.writeFileSync)(h,l.private,"utf8"),console.log("[myhi] \u81EA\u7B7E\u540D HTTPS \u8BC1\u4E66\u5DF2\u751F\u6210"),{cert:l.cert,key:l.private}}catch{}try{return(0,external_child_process_namespaceObject.execSync)(`openssl req -x509 -newkey rsa:2048 -keyout "${h}" -out "${C}" -days 3650 -nodes -subj "/CN=myhi"`,{stdio:"pipe",timeout:1e4}),{cert:(0,external_fs_.readFileSync)(C),key:(0,external_fs_.readFileSync)(h)}}catch(l){return console.warn(`[myhi] \u751F\u6210 HTTPS \u8BC1\u4E66\u5931\u8D25: ${l.message?.split(`
382
382
  `)[0]||l}`),null}}const pidFile=(0,external_path_.join)(server_configDir,"server.pid"),daemonPidFile=(0,external_path_.join)(server_configDir,"daemon.pid"),IS_DAEMON_CHILD=!!process.env.MYHI_DAEMON;function killOldProcess(C,h){let l=!1;try{process.kill(C,0),l=!0}catch{}if(!l)return!0;console.log(`[myhi] \u68C0\u6D4B\u5230\u5DF2\u8FD0\u884C\u7684${h} (PID ${C})\uFF0C\u6B63\u5728\u505C\u6B62...`);try{process.platform==="win32"?(0,external_child_process_namespaceObject.execSync)(`taskkill /PID ${C} /F /T`,{stdio:"pipe",timeout:5e3}):process.kill(C,"SIGTERM")}catch{}const n=Date.now()+5e3;for(;Date.now()<n;){try{process.kill(C,0)}catch{return!0}const o=Date.now()+200;for(;Date.now()<o;);}let e=!1;try{process.kill(C,0),e=!0}catch{}return!e}(function C(){try{const h=parseInt((0,external_fs_.readFileSync)(daemonPidFile,"utf8").trim(),10);if(h&&h!==process.pid&&h!==parseInt(process.env.MYHI_DAEMON_PID||"0",10)){let l=!1;try{process.kill(h,0),l=!0}catch{}if(l&&!IS_DAEMON_CHILD){console.log("[myhi] \u68C0\u6D4B\u5230\u540E\u53F0 daemon \u5728\u8FD0\u884C\uFF0C\u5148\u505C\u6B62..."),killOldProcess(h,"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 h=parseInt((0,external_fs_.readFileSync)(pidFile,"utf8").trim(),10);h&&h!==process.pid&&(killOldProcess(h,"\u5B9E\u4F8B")||(console.error(`[myhi] \u65E0\u6CD5\u505C\u6B62\u65E7\u8FDB\u7A0B (PID ${h})\uFF0C\u8BF7\u624B\u52A8\u6267\u884C\uFF1A`),console.error(" myhi stop"),console.error(` \u6216: ${process.platform==="win32"?`taskkill /PID ${h} /F`:`kill -9 ${h}`}`),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 C of manager.listSessions())try{C.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",(C=>{console.error("[myhi] \u672A\u6355\u83B7\u5F02\u5E38:",C.message),gracefulShutdown(),process.exit(1)})),process.on("unhandledRejection",(C=>{console.error("[myhi] \u672A\u5904\u7406\u7684 Promise \u62D2\u7EDD:",C)}));function readOrCreate(C,h){try{if((0,external_fs_.existsSync)(C))return(0,external_fs_.readFileSync)(C,"utf8").trim()}catch{}const l=h();return(0,external_fs_.writeFileSync)(C,l,{mode:384}),l}const TOKEN=readOrCreate((0,external_path_.join)(server_configDir,"token"),(()=>(0,external_crypto_.randomBytes)(16).toString("hex")));let PASSWORD=readOrCreate((0,external_path_.join)(server_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 C=Date.now();for(const[h,l]of userSessions)l.permanent||(!l.lastUsed||C-l.lastUsed>SESSION_MAX_AGE)&&userSessions.delete(h)}),3600*1e3);function getTailscaleIP(){try{return(0,external_child_process_namespaceObject.execSync)("tailscale ip -4",{timeout:3e3,stdio:"pipe"}).toString().trim().split(`
383
383
  `)[0]}catch{}try{if(process.platform==="win32"){const C=(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(C)return C[1]}else try{const C=(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(C)return C[1]}catch{const C=(0,external_child_process_namespaceObject.execSync)("route -n get default 2>/dev/null || route -n 2>/dev/null",{timeout:3e3,stdio:"pipe"}).toString(),h=C.match(/interface:\s*(\S+)/i)||C.match(/0\.0\.0\.0\s+(\d+\.\d+\.\d+\.\d+)/);if(h){const l=(0,external_os_.networkInterfaces)()[h[1]];if(l){const n=l.find((e=>e.family==="IPv4"&&!e.internal));if(n)return n.address}if(/^\d+\.\d+\.\d+\.\d+$/.test(h[1]))return h[1]}}}catch{}for(const C of Object.values((0,external_os_.networkInterfaces)()))for(const h of C)if(h.family==="IPv4"&&!h.internal)return h.address;return"localhost"}function parseCookies(C=""){const h={};for(const l of C.split(";")){const n=l.indexOf("=");n>0&&(h[l.slice(0,n).trim()]=l.slice(n+1).trim())}return h}const SESSION_COOKIE="myhi_sid",COOKIE_OPTS_BASE="HttpOnly; SameSite=Strict; Path=/; Max-Age=31536000";function cookieOpts(C){return C?.secure||C?.headers?.["x-forwarded-proto"]==="https"?COOKIE_OPTS_BASE+"; Secure":COOKIE_OPTS_BASE}function setSessionCookie(C,h,l){C.setHeader("Set-Cookie",`${SESSION_COOKIE}=${h||TOKEN}; ${cookieOpts(l)}`)}function getSessionToken(C){return parseCookies(C)[SESSION_COOKIE]||null}function hasValidSession(C){const h=getSessionToken(C);if(h&&userSessions.has(h)){const l=userSessions.get(h);return l&&(l.lastUsed=Date.now()),!0}return!1}function getUserInfo(C){const h=getSessionToken(C);return h?userSessions.get(h):null}const app=express();app.set("trust proxy",!0),app.use(((C,h,l)=>{h.setHeader("Referrer-Policy","no-referrer"),h.setHeader("X-Content-Type-Options","nosniff"),l()}));function _httpOnlyFromConfig(){try{return!!JSON.parse((0,external_fs_.readFileSync)((0,external_path_.join)(server_configDir,"config.json"),"utf8")).httpOnly}catch{return!1}}const _tlsOpts=process.env.MYHI_NO_HTTPS||_httpOnlyFromConfig()?null:getOrCreateCert();_tlsOpts?(httpsServer=(0,external_https_.createServer)(_tlsOpts,app),httpServer=(0,external_http_.createServer)(((C,h)=>{const l=(C.headers.host||"").replace(/:\d+$/,"");h.writeHead(301,{Location:`https://${l}:${PORT}${C.url}`}),h.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:(C,h)=>{if(!C||_corsAllowed.test(C))return h(null,!0);h(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(C,h){if(!AUTO_ATTACH||_autoSpawned.has(C)||!process.env.DISPLAY&&process.platform==="linux")return;_autoSpawned.add(C);const l=__nccwpck_require__.ab+"attach.js",n=process.execPath,e=h||"myhi";if(process.platform==="win32"){const o=`"${n}" "${l}" ${C}`,u=(0,external_child_process_namespaceObject.spawn)("wt.exe",["-w","0","new-tab","--title",e,"--","cmd","/k",o],{detached:!0,stdio:"ignore"});u.unref(),u.on("error",(d=>{console.warn("[attach] wt.exe \u5931\u8D25\uFF0C\u56DE\u9000\u5230 cmd:",d.message),(0,external_child_process_namespaceObject.spawn)("cmd.exe",["/c","start",`"${e}"`,"cmd","/k",o],{detached:!0,stdio:"ignore"}).unref()}))}else if(process.platform==="darwin"){const o=`tell application "Terminal" to do script "${n} ${l} ${C}"`;(0,external_child_process_namespaceObject.spawn)("osascript",["-e",o],{detached:!0,stdio:"ignore"}).unref()}else{const o=`${n} "${l}" ${C}`;for(const[u,d]of[["gnome-terminal",["--","bash","-c",`${o}; exec bash`]],["xterm",["-title",e,"-e",o]],["konsole",["--new-tab","-e",o]]]){const c=(0,external_child_process_namespaceObject.spawn)(u,d,{detached:!0,stdio:"ignore"});c.unref(),c.on("error",(()=>{}));break}}}const uploadDir=(0,external_path_.join)(server_configDir,"uploads");(0,external_fs_.mkdirSync)(uploadDir,{recursive:!0});function checkAuth(C,h,l){const n=C.query.token;if(n&&(n===TOKEN||userSessions.has(n))){const e=userSessions.get(n);if(e?.onetime){userSessions.delete(n);const d=(0,external_crypto_.randomBytes)(16).toString("hex");userSessions.set(d,{role:e.role,name:e.name,lastUsed:Date.now()}),setSessionCookie(h,d,C)}else setSessionCookie(h,n===TOKEN?TOKEN:n,C);const o=Object.keys(C.query).filter((d=>d!=="token")),u=C.path+(o.length?"?"+new URLSearchParams(Object.fromEntries(Object.entries(C.query).filter((([d])=>d!=="token")))):"");return h.redirect(u)}if(hasValidSession(C.headers.cookie)){if(isExclusiveMode()&&activeUser){const e=getSessionToken(C.headers.cookie);if(e!==TOKEN){const o=userSessions.get(e);if(!o||o.name!==activeUser.name)return h.setHeader("Set-Cookie",`${SESSION_COOKIE}=; HttpOnly; SameSite=Strict; Path=/; Max-Age=0`),h.redirect("/login")}}return l()}h.redirect("/login")}const _require=(0,external_module_namespaceObject.createRequire)(import.meta.url);function pkgDirSafe(C){try{return(0,external_path_.dirname)(_require.resolve(`${C}/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:(C,h)=>{h.endsWith(".js")&&C.setHeader("Content-Type","application/javascript; charset=utf-8")}})),app.get("/manifest.json",((C,h)=>h.sendFile((0,external_path_.join)(server_dirname,"manifest.json")))),app.get("/icon.png",((C,h)=>h.sendFile((0,external_path_.join)(server_dirname,"icon.png")))),app.get("/icon.svg",((C,h)=>h.sendFile((0,external_path_.join)(server_dirname,"icon.svg"))));const _loginAttempts=new Map,LOGIN_MAX_ATTEMPTS=5,LOGIN_LOCKOUT_MS=300*1e3;function checkLoginRate(C){const h=Date.now(),l=_loginAttempts.get(C);return!l||h>l.resetAt?!0:l.count<LOGIN_MAX_ATTEMPTS}function recordLoginFailure(C){const h=Date.now(),l=_loginAttempts.get(C);!l||h>l.resetAt?_loginAttempts.set(C,{count:1,resetAt:h+LOGIN_LOCKOUT_MS}):l.count++}function clearLoginFailures(C){_loginAttempts.delete(C)}setInterval((()=>{const C=Date.now();for(const[h,l]of _loginAttempts)C>l.resetAt&&_loginAttempts.delete(h)}),600*1e3),app.get("/login",((C,h)=>{h.sendFile((0,external_path_.join)(server_dirname,"login.html"))})),app.post("/login",express.urlencoded({extended:!1}),((C,h)=>{const l=C.ip;if(!checkLoginRate(l))return h.redirect("/login?error=locked");loadRoles(),loadUsers();const n=C.body?.password,e=C.query.from,o=e&&e.startsWith("/")&&!e.startsWith("//")?e:"/";if(hasUsers()){const d=lookupUser(n);if(!d)return recordLoginFailure(l),h.redirect("/login?error=1");if(isExclusiveMode()&&activeUser&&activeUser.name!==d.name)return h.redirect("/login?error=occupied&user="+encodeURIComponent(activeUser.name));clearLoginFailures(l);const c=(0,external_crypto_.randomBytes)(16).toString("hex");return userSessions.set(c,{role:"operator",name:d.name,dir:d.dir,lastUsed:Date.now()}),isExclusiveMode()&&(activeUser?activeUser.tokens.add(c):activeUser={name:d.name,dir:d.dir,tokens:new Set([c]),loginAt:Date.now()}),setSessionCookie(h,c,C),console.log(`[\u767B\u5F55] ${d.name} \u767B\u5F55\uFF08${isExclusiveMode()?"\u72EC\u5360":"\u5171\u4EAB"}\u6A21\u5F0F\uFF09`),h.redirect(o)}const u=lookupPassword(n);if(u){clearLoginFailures(l);const d=(0,external_crypto_.randomBytes)(16).toString("hex");return userSessions.set(d,{role:u.role,name:u.name,lastUsed:Date.now()}),setSessionCookie(h,d,C),h.redirect(o)}if(n===PASSWORD){clearLoginFailures(l);const d=(0,external_crypto_.randomBytes)(16).toString("hex");return userSessions.set(d,{role:"admin",name:"\u7BA1\u7406\u5458",lastUsed:Date.now()}),setSessionCookie(h,d,C),h.redirect(o)}recordLoginFailure(l),h.redirect("/login?error=1")})),app.post("/logout",((C,h)=>{const l=getSessionToken(C.headers.cookie);l&&(activeUser&&activeUser.tokens.has(l)&&(activeUser.tokens.delete(l),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(l)),h.setHeader("Set-Cookie",`${SESSION_COOKIE}=; HttpOnly; SameSite=Strict; Path=/; Max-Age=0`),h.redirect("/login")})),app.get("/api/status",((C,h)=>{if(isExclusiveMode()&&activeUser)return h.json({occupied:!0,userName:activeUser.name,releasing:!!_exclusiveReleaseTimer});if(hasUsers()&&!isExclusiveMode()){const l=new Set;for(const[,n]of io.sockets.sockets){if(!n.data.userName||n.data.userName==="\u7BA1\u7406\u5458")continue;const e=n.handshake.headers.cookie;e&&!hasValidSession(e)||l.add(n.data.userName)}return h.json({occupied:!1,onlineUsers:[...l]})}h.json({occupied:!1})})),app.get("/admin",((C,h)=>h.sendFile((0,external_path_.join)(server_dirname,"admin.html")))),app.get("/",checkAuth,((C,h)=>h.sendFile((0,external_path_.join)(server_dirname,"index.html")))),app.get("/terminal/:id",checkAuth,((C,h)=>h.sendFile((0,external_path_.join)(server_dirname,"chat.html")))),app.get("/grid",checkAuth,((C,h)=>h.sendFile((0,external_path_.join)(server_dirname,"grid.html")))),app.get("/terminal-raw/:id",checkAuth,((C,h)=>h.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,((C,h)=>{const l=getUserInfo(C.headers.cookie);h.json(manager.list(l?.name,l?.role==="admin"))})),app.get("/api/usage",((C,h)=>{try{const l=manager.listSessions().filter((e=>e.mode==="agent"&&e.alive)).map((e=>({id:e.id,title:e.title,owner:e.owner,usage:e._usage})));let n=null;for(const e of l)if(e.usage?.rateLimitInfo){n=e.usage.rateLimitInfo;break}h.json({rateLimit:n,activeSessions:l})}catch(l){h.status(500).json({error:l.message})}})),app.post("/api/sessions",checkAuth,express.json(),((C,h)=>{const l=getUserInfo(C.headers.cookie),n=manager.create({...C.body,owner:l?.name,userDir:l?.dir});h.status(201).json(n.toJSON())})),app.delete("/api/sessions/:id",checkAuth,((C,h)=>{manager.kill(C.params.id),h.status(204).end()})),app.get("/api/sessions/health",checkAuth,((C,h)=>{const l=getUserInfo(C.headers.cookie),n=manager.listSessions().filter((e=>e.mode==="agent")).filter((e=>!e.owner||!l?.name||e.owner===l.name)).map((e=>({...e.toJSON(),health:getSessionHealth(e.claudeSessionId)})));h.json({ok:!0,sessions:n})})),app.post("/api/sessions/:id/reset",checkAuth,((C,h)=>{const l=manager.get(C.params.id);if(!l||l.mode!=="agent"||typeof l.reset!="function")return h.status(404).json({ok:!1,error:"agent \u4F1A\u8BDD\u4E0D\u5B58\u5728"});l.reset(),l.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(),h.json({ok:!0})})),app.get("/api/claude-sessions",checkAuth,((C,h)=>{const l=getUserInfo(C.headers.cookie);h.json(listLocalClaudeSessions(l?.dir))})),app.get("/api/agent/history/:sessionId",checkAuth,((C,h)=>{const l=manager.get(C.params.sessionId);if(!l||l.mode!=="agent")return h.status(404).json({ok:!1,error:"\u4F1A\u8BDD\u4E0D\u5B58\u5728"});const n=l.claudeSessionId;if(!n)return h.json({ok:!0,messages:[],total:0,hasMore:!1});const e=(0,external_path_.join)((0,external_os_.homedir)(),".claude","projects");let o=null;try{if((0,external_fs_.existsSync)(e))for(const p of(0,external_fs_.readdirSync)(e,{withFileTypes:!0})){if(!p.isDirectory())continue;const f=(0,external_path_.join)(e,p.name,n+".jsonl");if((0,external_fs_.existsSync)(f)){o=f;break}}}catch{}if(!o)return h.json({ok:!0,messages:[],total:0,hasMore:!1});let u;try{u=(0,external_fs_.readFileSync)(o,"utf8").split(`
384
- `).filter(Boolean)}catch(p){return h.status(500).json({ok:!1,error:p.message})}const d=Math.max(0,parseInt(C.query.offset,10)||0),c=Math.min(200,Math.max(1,parseInt(C.query.limit,10)||50)),i=u.length,a=i-d,t=Math.max(0,a-c),r=[];for(let p=t;p<a;p++)try{const f=JSON.parse(u[p]);if((f.type==="human"||f.type==="user")&&f.message?.content){const s=typeof f.message.content=="string"?f.message.content:f.message.content.find((v=>v.type==="text"))?.text||"";s&&r.push({type:"user",content:s,timestamp:f.timestamp})}else f.type==="assistant"&&f.message?.content?r.push({type:"assistant",message:f.message,timestamp:f.timestamp}):f.type==="result"&&r.push({type:"result",total_cost_usd:f.costUSD||f.total_cost_usd,timestamp:f.timestamp})}catch{}h.json({ok:!0,messages:r,total:i,hasMore:t>0})})),app.get("/api/dirs",checkAuth,((C,h)=>{const l=getUserInfo(C.headers.cookie);if(!l||l.role!=="admin")return h.status(403).json({error:"\u6CA1\u6709\u76EE\u5F55\u6D4F\u89C8\u6743\u9650"});const n=l.dir?(0,external_path_.resolve)((0,external_path_.normalize)(l.dir)):null,e=process.platform==="win32"?"\\":"/";let o=C.query.path||n||process.env.MYHI_CWD||(0,external_os_.homedir)(),u=(0,external_path_.resolve)((0,external_path_.normalize)(o)).replace(/[/\\]+$/,"");process.platform==="win32"&&/^[A-Za-z]:$/.test(u)&&(u+="\\"),n&&!u.startsWith(n)&&(u=n);try{const d=(0,external_fs_.readdirSync)(u,{withFileTypes:!0}).filter((a=>a.isDirectory()&&a.name!==".git")).map((a=>({name:a.name,path:u.replace(/[/\\]+$/,"")+e+a.name}))).sort(((a,t)=>a.name.localeCompare(t.name))),c=(0,external_path_.join)(u,".."),i=!n||(0,external_path_.resolve)((0,external_path_.normalize)(c)).startsWith(n);h.json({current:u,parent:i&&c!==u?c:null,dirs:d})}catch(d){h.status(400).json({error:d.message})}}));function safePath(C,h){const l=(0,external_path_.resolve)((0,external_path_.normalize)(C)),n=(0,external_path_.resolve)((0,external_path_.normalize)((0,external_path_.join)(C,h||"")));if(n!==l&&!n.startsWith(l+external_path_.sep))return null;try{const e=(0,external_fs_.realpathSync)(l),o=(0,external_fs_.existsSync)(n)?(0,external_fs_.realpathSync)(n):n;if(o!==e&&!o.startsWith(e+external_path_.sep))return null}catch{}return n}function walkFiles(C,h,l=[],n=0){if(n>20)return l;let e;try{e=(0,external_fs_.readdirSync)(C,{withFileTypes:!0})}catch{return l}for(const o of e){if(o.name===".git")continue;const u=(0,external_path_.join)(C,o.name),d=h?h+"/"+o.name:o.name;try{const c=(0,external_fs_.lstatSync)(u);if(c.isSymbolicLink())continue;c.isDirectory()?walkFiles(u,d,l,n+1):c.isFile()&&l.push({name:d,size:c.size,mtime:c.mtimeMs})}catch{}}return l}function fileHash(C){return new Promise(((h,l)=>{const n=(0,external_crypto_.createHash)("sha256"),e=(0,external_fs_.createReadStream)(C);e.on("data",(o=>n.update(o))),e.on("end",(()=>h(n.digest("hex")))),e.on("error",l)}))}app.get("/api/files/:sessionId",checkAuth,((C,h)=>{const l=manager.get(C.params.sessionId);if(!l)return h.status(404).json({error:"\u4F1A\u8BDD\u4E0D\u5B58\u5728"});const n=C.query.path||"",e=safePath(l.cwd,n);if(!e)return h.status(403).json({error:"\u8DEF\u5F84\u8D8A\u754C"});try{if(!(0,external_fs_.statSync)(e).isDirectory())return h.status(400).json({error:"\u4E0D\u662F\u76EE\u5F55"});const o=(0,external_fs_.readdirSync)(e,{withFileTypes:!0}).filter((c=>c.name!==".git")).map((c=>{const i=(0,external_path_.join)(e,c.name);try{const a=(0,external_fs_.lstatSync)(i);return a.isSymbolicLink()?null:{name:c.name,isDir:a.isDirectory(),size:a.isFile()?a.size:void 0,mtime:a.mtimeMs}}catch{return null}})).filter(Boolean).sort(((c,i)=>i.isDir-c.isDir||c.name.localeCompare(i.name))),u=n?n.replace(/\\/g,"/"):"",d=u.includes("/")?u.slice(0,u.lastIndexOf("/")):u?"":null;h.json({ok:!0,cwd:l.cwd,path:u,parent:d,entries:o})}catch(o){h.status(500).json({error:o.message})}})),app.get("/api/files/:sessionId/download",checkAuth,((C,h)=>{const l=manager.get(C.params.sessionId);if(!l)return h.status(404).json({error:"\u4F1A\u8BDD\u4E0D\u5B58\u5728"});const n=C.query.path;if(!n)return h.status(400).json({error:"\u7F3A\u5C11 path \u53C2\u6570"});const e=safePath(l.cwd,n);if(!e)return h.status(403).json({error:"\u8DEF\u5F84\u8D8A\u754C"});try{const o=(0,external_fs_.statSync)(e);if(!o.isFile())return h.status(400).json({error:"\u4E0D\u662F\u6587\u4EF6"});const u=n.split("/").pop();h.setHeader("Content-Disposition",`attachment; filename*=UTF-8''${encodeURIComponent(u)}`),h.setHeader("Content-Length",o.size),(0,external_fs_.createReadStream)(e).pipe(h)}catch{h.status(404).json({error:"\u6587\u4EF6\u4E0D\u5B58\u5728"})}})),app.post("/api/files/:sessionId/upload",checkAuth,((C,h)=>{const l=manager.get(C.params.sessionId);if(!l)return h.status(404).json({error:"\u4F1A\u8BDD\u4E0D\u5B58\u5728"});const n=(C.query.path||"").replace(/\\/g,"/"),e=safePath(l.cwd,n);if(!e)return h.status(403).json({error:"\u8DEF\u5F84\u8D8A\u754C"});(0,external_fs_.mkdirSync)(e,{recursive:!0}),multer({storage:multer.diskStorage({destination:(o,u,d)=>{u.originalname=Buffer.from(u.originalname,"latin1").toString("utf8");const c=u.originalname.includes("/")?u.originalname.slice(0,u.originalname.lastIndexOf("/")):"",i=c?(0,external_path_.join)(e,c):e,a=(0,external_path_.resolve)((0,external_path_.normalize)(i)),t=(0,external_path_.resolve)((0,external_path_.normalize)(l.cwd));if(a!==t&&!a.startsWith(t+external_path_.sep))return d(new Error("\u8DEF\u5F84\u8D8A\u754C"));(0,external_fs_.mkdirSync)(i,{recursive:!0}),d(null,i)},filename:(o,u,d)=>{const c=u.originalname.includes("/")?u.originalname.slice(u.originalname.lastIndexOf("/")+1):u.originalname;d(null,c)}}),limits:{fileSize:500*1024*1024}}).array("files",100)(C,h,(o=>{if(o)return h.status(400).json({error:o.message});if(!C.files?.length)return h.status(400).json({error:"\u6CA1\u6709\u6587\u4EF6"});const u=C.files.map((d=>({name:d.originalname,size:d.size,path:(0,external_path_.resolve)(d.path).replace(/\\/g,"/")})));io.emit("files:changed",{sessionId:l.id}),h.json({ok:!0,files:u})}))})),app.delete("/api/files/:sessionId",checkAuth,((C,h)=>{const l=manager.get(C.params.sessionId);if(!l)return h.status(404).json({error:"\u4F1A\u8BDD\u4E0D\u5B58\u5728"});const n=C.query.path;if(!n)return h.status(400).json({error:"\u7F3A\u5C11 path \u53C2\u6570"});const e=safePath(l.cwd,n);if(!e)return h.status(403).json({error:"\u8DEF\u5F84\u8D8A\u754C"});if((0,external_path_.resolve)((0,external_path_.normalize)(e))===(0,external_path_.resolve)((0,external_path_.normalize)(l.cwd)))return h.status(403).json({error:"\u4E0D\u80FD\u5220\u9664\u4F1A\u8BDD\u6839\u76EE\u5F55"});try{(0,external_fs_.statSync)(e).isDirectory()?(0,external_fs_.rmSync)(e,{recursive:!0}):(0,external_fs_.unlinkSync)(e),io.emit("files:changed",{sessionId:l.id}),h.json({ok:!0})}catch(o){h.status(500).json({error:o.message})}})),app.get("/api/files/:sessionId/manifest",checkAuth,(async(C,h)=>{const l=manager.get(C.params.sessionId);if(!l)return h.status(404).json({error:"\u4F1A\u8BDD\u4E0D\u5B58\u5728"});const n=C.query.path||"",e=safePath(l.cwd,n);if(!e)return h.status(403).json({error:"\u8DEF\u5F84\u8D8A\u754C"});try{const o=walkFiles(e,""),u=[];for(const d of o){if(d.size>=50*1024*1024){u.push(d);continue}try{const c=await fileHash((0,external_path_.join)(e,d.name));u.push({...d,hash:c})}catch{u.push(d)}}h.json({ok:!0,cwd:l.cwd,path:n,files:u})}catch(o){h.status(500).json({error:o.message})}})),app.get("/api/me",checkAuth,((C,h)=>{const l=getUserInfo(C.headers.cookie);h.json({name:l?.name||"\u7528\u6237",role:l?.role||"admin",exclusive:isExclusiveMode(),hasUsers:hasUsers(),dir:l?.dir||null})}));const gatewayFile=(0,external_path_.join)(server_configDir,"gateway.json");function loadGateway(){try{return JSON.parse((0,external_fs_.readFileSync)(gatewayFile,"utf8"))}catch{return{baseUrl:"",apiKey:""}}}app.get("/api/gateway",checkAdminAuth,((C,h)=>{const l=loadGateway();h.json({baseUrl:l.baseUrl||"",apiKey:l.apiKey?l.apiKey.length>12?l.apiKey.slice(0,6)+"***"+l.apiKey.slice(-4):"***":"",hasKey:!!l.apiKey})})),app.post("/api/gateway",checkAdminAuth,express.json(),((C,h)=>{const{baseUrl:l,apiKey:n}=C.body||{},e=loadGateway(),o={baseUrl:typeof l=="string"?l.trim():e.baseUrl,apiKey:!n||n.includes("***")?e.apiKey:n.trim()};try{(0,external_fs_.mkdirSync)(server_configDir,{recursive:!0}),(0,external_fs_.writeFileSync)(gatewayFile,JSON.stringify(o,null,2),{mode:384}),h.json({ok:!0})}catch(u){h.status(500).json({error:u.message})}})),app.get("/api/secrets",checkAdminAuth,((C,h)=>{try{h.json(secretsOverview())}catch(l){h.status(500).json({error:l.message})}})),app.post("/api/secrets",checkAdminAuth,express.json(),((C,h)=>{const{name:l,value:n,type:e,scope:o,cwd:u}=C.body||{},d=upsertSecret({name:l,value:n,type:e,scope:o,cwd:u});if(d.error)return h.status(400).json(d);h.json(d)})),app.delete("/api/secrets",checkAdminAuth,express.json(),((C,h)=>{const{name:l,scope:n,cwd:e}=C.body||{};if(!l)return h.status(400).json({error:"\u7F3A\u5C11 name"});try{h.json(deleteSecret({name:l,scope:n,cwd:e}))}catch(o){h.status(500).json({error:o.message})}})),app.get("/api/session/:id/secrets",checkAuth,((C,h)=>{const l=manager.get(C.params.id);if(!l)return h.status(404).json({error:"\u4F1A\u8BDD\u4E0D\u5B58\u5728"});h.json({cwd:l.cwd,project:listSecrets("project",l.cwd),global:listSecrets("global")})})),app.post("/api/session/:id/secrets",checkAuth,express.json(),((C,h)=>{const l=manager.get(C.params.id);if(!l)return h.status(404).json({error:"\u4F1A\u8BDD\u4E0D\u5B58\u5728"});const{name:n,value:e,type:o}=C.body||{},u=upsertSecret({name:n,value:e,type:o,scope:"project",cwd:l.cwd});if(u.error)return h.status(400).json(u);h.json(u)})),app.delete("/api/session/:id/secrets",checkAuth,express.json(),((C,h)=>{const l=manager.get(C.params.id);if(!l)return h.status(404).json({error:"\u4F1A\u8BDD\u4E0D\u5B58\u5728"});const{name:n}=C.body||{};if(!n)return h.status(400).json({error:"\u7F3A\u5C11 name"});h.json(deleteSecret({name:n,scope:"project",cwd:l.cwd}))})),app.post("/api/session/:id/secrets/bulk",checkAuth,express.json(),((C,h)=>{const l=manager.get(C.params.id);if(!l)return h.status(404).json({error:"\u4F1A\u8BDD\u4E0D\u5B58\u5728"});const n=upsertManyProject(l.cwd,(C.body||{}).text||"");if(n.error)return h.status(400).json(n);h.json(n)})),app.get("/api/session/:id/guard",checkAuth,((C,h)=>{const l=manager.get(C.params.id);if(!l)return h.status(404).json({error:"\u4F1A\u8BDD\u4E0D\u5B58\u5728"});const n=getProjectGuard(l.cwd);h.json({cwd:l.cwd,enabled:n.enabled,level:n.level,entities:n.entities})})),app.put("/api/session/:id/guard",checkAuth,express.json(),((C,h)=>{const l=manager.get(C.params.id);if(!l)return h.status(404).json({error:"\u4F1A\u8BDD\u4E0D\u5B58\u5728"});const{enabled:n,level:e}=C.body||{},o=setProjectGuard(l.cwd,{enabled:n,level:e});if(o.error)return h.status(400).json(o);h.json(o)})),app.post("/api/session/:id/guard/entity",checkAuth,express.json(),((C,h)=>{const l=manager.get(C.params.id);if(!l)return h.status(404).json({error:"\u4F1A\u8BDD\u4E0D\u5B58\u5728"});const{name:n,type:e}=C.body||{},o=upsertEntity(l.cwd,{name:n,type:e});if(o.error)return h.status(400).json(o);h.json(o)})),app.delete("/api/session/:id/guard/entity",checkAuth,express.json(),((C,h)=>{const l=manager.get(C.params.id);if(!l)return h.status(404).json({error:"\u4F1A\u8BDD\u4E0D\u5B58\u5728"});const{name:n}=C.body||{};if(!n)return h.status(400).json({error:"\u7F3A\u5C11 name"});h.json(deleteEntity(l.cwd,n))})),app.post("/api/session/:id/guard/bulk",checkAuth,express.json(),((C,h)=>{const l=manager.get(C.params.id);if(!l)return h.status(404).json({error:"\u4F1A\u8BDD\u4E0D\u5B58\u5728"});const n=upsertManyEntities(l.cwd,(C.body||{}).text||"");if(n.error)return h.status(400).json(n);h.json(n)})),app.get("/api/session/:id/guard/audit",checkAuth,((C,h)=>{const l=manager.get(C.params.id);if(!l)return h.status(404).json({error:"\u4F1A\u8BDD\u4E0D\u5B58\u5728"});const n=l.claudeSessionId||C.params.id;h.json({entries:loadAudit(n)})})),app.get("/api/session/:id/followup",checkAuth,((C,h)=>{const l=manager.get(C.params.id);if(!l||typeof l.followUpState!="function")return h.status(404).json({error:"\u4F1A\u8BDD\u4E0D\u5B58\u5728\u6216\u975E AI \u4F1A\u8BDD"});h.json(l.followUpState())})),app.put("/api/session/:id/followup",checkAuth,express.json(),((C,h)=>{const l=manager.get(C.params.id);if(!l||typeof l.setFollowUp!="function")return h.status(404).json({error:"\u4F1A\u8BDD\u4E0D\u5B58\u5728\u6216\u975E AI \u4F1A\u8BDD"});const{enabled:n,intervalMinutes:e}=C.body||{};h.json(l.setFollowUp({enabled:n,intervalMinutes:e}))})),app.get("/api/inbox",checkAuth,((C,h)=>{const l=getUserInfo(C.headers.cookie),{sessionId:n}=C.query;try{try{rebindDrafts(manager.listSessions())}catch{}const e=listDrafts({sessionId:n||void 0,owner:l?.name,includeAll:l?.role==="admin"}),o=new Set(manager.listSessions().map((u=>u.id)));h.json({drafts:e.map((u=>({...u,sessionAlive:o.has(u.sessionId)}))),counts:countBySession()})}catch(e){h.status(500).json({error:e.message})}})),app.post("/api/inbox",checkAuth,express.json({limit:"1mb"}),((C,h)=>{const l=getUserInfo(C.headers.cookie),n=C.body||{},e=n.sessionId?manager.get(n.sessionId):null;try{const o=addDraft({...n,owner:n.owner||l?.name||null},e);h.json({ok:!0,draft:o})}catch(o){h.status(400).json({error:o.message})}})),app.put("/api/inbox/:id",checkAuth,express.json({limit:"1mb"}),((C,h)=>{const l=C.body||{},n=l.sessionId?manager.get(l.sessionId):null,e=updateDraft(C.params.id,l,n);if(!e)return h.status(404).json({error:"\u8349\u7A3F\u4E0D\u5B58\u5728"});h.json({ok:!0,draft:e})})),app.delete("/api/inbox/:id",checkAuth,((C,h)=>{if(!removeDraft(C.params.id))return h.status(404).json({error:"\u8349\u7A3F\u4E0D\u5B58\u5728"});h.json({ok:!0})}));function _spawnOpen(C,h){try{const l=(0,external_child_process_namespaceObject.spawn)(C,h,{detached:!0,stdio:"ignore",shell:process.platform==="win32"});return l.on("error",(()=>{})),l.unref(),!0}catch{return!1}}app.post("/api/session/:id/open-folder",checkAuth,((C,h)=>{const l=manager.get(C.params.id);if(!l?.cwd)return h.status(404).json({error:"\u4F1A\u8BDD\u6216\u76EE\u5F55\u4E0D\u5B58\u5728"});const n=l.cwd,e=process.platform==="win32"?_spawnOpen("explorer.exe",[n]):process.platform==="darwin"?_spawnOpen("open",[n]):_spawnOpen("xdg-open",[n]);h.json({ok:e,cwd:n})})),app.post("/api/session/:id/open-editor",checkAuth,((C,h)=>{const l=manager.get(C.params.id);if(!l?.cwd)return h.status(404).json({error:"\u4F1A\u8BDD\u6216\u76EE\u5F55\u4E0D\u5B58\u5728"});const n=_spawnOpen("code",[l.cwd]);h.json({ok:n})}));function getPkgVersion(){for(const C of["../package.json","./package.json","package.json"])try{const h=JSON.parse((0,external_fs_.readFileSync)((0,external_path_.join)(server_dirname,C),"utf8")).version;if(h&&h!=="0.0.0")return h}catch{}return"0.0.0"}const PKG_VERSION=getPkgVersion();let _latestVersion=null,_lastVersionCheck=0;const VERSION_CHECK_INTERVAL=600*1e3;async function checkLatestVersion(){const C=Date.now();if(_latestVersion&&C-_lastVersionCheck<VERSION_CHECK_INTERVAL)return _latestVersion;try{const h=await fetch("https://registry.npmjs.org/@wendongfly/myhi/latest",{headers:{Accept:"application/json"},signal:AbortSignal.timeout(8e3)});h.ok&&(_latestVersion=(await h.json()).version,_lastVersionCheck=C)}catch{}return _latestVersion}app.get("/api/version",(async(C,h)=>{const l=getPkgVersion(),n=await checkLatestVersion();h.json({current:l,latest:n||l,updateAvailable:n&&n!==l})}));function isSystemd(){return process.platform==="linux"&&(process.env.INVOCATION_ID||process.env.JOURNAL_STREAM)}function getNpmUpgradeCmd(){const C="--registry https://registry.npmjs.org";return process.platform==="win32"?`npm.cmd install -g @wendongfly/myhi@latest ${C}`:`npm install -g @wendongfly/myhi@latest ${C}`}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,((C,h)=>{const l=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""}})();h.json({asrUrl:l||""})})),app.post("/api/server-config",checkAuth,express.json(),((C,h)=>{const{asrUrl:l}=C.body||{},n=(0,external_path_.join)((0,external_os_.homedir)(),".myhi","asr.json");try{const e=(l||"").trim();(0,external_fs_.writeFileSync)(n,JSON.stringify({url:e},null,2),"utf8"),h.json({ok:!0})}catch(e){h.status(500).json({error:e.message})}})),app.post("/api/restart",checkAuth,((C,h)=>{h.json({ok:!0}),setTimeout((()=>restartService()),300)})),app.post("/api/update",checkAuth,(async(C,h)=>{try{await execAsync(getNpmUpgradeCmd(),{encoding:"utf8",timeout:12e4}),console.log("[myhi] \u5347\u7EA7\u5B8C\u6210"),h.json({ok:!0,message:"\u5347\u7EA7\u5B8C\u6210\uFF0C\u670D\u52A1\u5C06\u81EA\u52A8\u91CD\u542F..."}),setTimeout((()=>restartService()),500)}catch(l){const n=(l.stderr||l.stdout||l.message||"").split(`
384
+ `).filter(Boolean)}catch(p){return h.status(500).json({ok:!1,error:p.message})}const d=Math.max(0,parseInt(C.query.offset,10)||0),c=Math.min(200,Math.max(1,parseInt(C.query.limit,10)||50)),i=u.length,a=i-d,t=Math.max(0,a-c),r=[];for(let p=t;p<a;p++)try{const f=JSON.parse(u[p]);if((f.type==="human"||f.type==="user")&&f.message?.content){const s=typeof f.message.content=="string"?f.message.content:f.message.content.find((v=>v.type==="text"))?.text||"";s&&r.push({type:"user",content:s,timestamp:f.timestamp})}else f.type==="assistant"&&f.message?.content?r.push({type:"assistant",message:f.message,timestamp:f.timestamp}):f.type==="result"&&r.push({type:"result",total_cost_usd:f.costUSD||f.total_cost_usd,timestamp:f.timestamp})}catch{}h.json({ok:!0,messages:r,total:i,hasMore:t>0})})),app.get("/api/dirs",checkAuth,((C,h)=>{const l=getUserInfo(C.headers.cookie);if(!l||l.role!=="admin")return h.status(403).json({error:"\u6CA1\u6709\u76EE\u5F55\u6D4F\u89C8\u6743\u9650"});const n=l.dir?(0,external_path_.resolve)((0,external_path_.normalize)(l.dir)):null,e=process.platform==="win32"?"\\":"/";let o=C.query.path||n||process.env.MYHI_CWD||(0,external_os_.homedir)(),u=(0,external_path_.resolve)((0,external_path_.normalize)(o)).replace(/[/\\]+$/,"");process.platform==="win32"&&/^[A-Za-z]:$/.test(u)&&(u+="\\"),n&&!u.startsWith(n)&&(u=n);try{const d=(0,external_fs_.readdirSync)(u,{withFileTypes:!0}).filter((a=>a.isDirectory()&&a.name!==".git")).map((a=>({name:a.name,path:u.replace(/[/\\]+$/,"")+e+a.name}))).sort(((a,t)=>a.name.localeCompare(t.name))),c=(0,external_path_.join)(u,".."),i=!n||(0,external_path_.resolve)((0,external_path_.normalize)(c)).startsWith(n);h.json({current:u,parent:i&&c!==u?c:null,dirs:d})}catch(d){h.status(400).json({error:d.message})}}));function safePath(C,h){const l=(0,external_path_.resolve)((0,external_path_.normalize)(C)),n=(0,external_path_.resolve)((0,external_path_.normalize)((0,external_path_.join)(C,h||"")));if(n!==l&&!n.startsWith(l+external_path_.sep))return null;try{const e=(0,external_fs_.realpathSync)(l),o=(0,external_fs_.existsSync)(n)?(0,external_fs_.realpathSync)(n):n;if(o!==e&&!o.startsWith(e+external_path_.sep))return null}catch{}return n}function walkFiles(C,h,l=[],n=0){if(n>20)return l;let e;try{e=(0,external_fs_.readdirSync)(C,{withFileTypes:!0})}catch{return l}for(const o of e){if(o.name===".git")continue;const u=(0,external_path_.join)(C,o.name),d=h?h+"/"+o.name:o.name;try{const c=(0,external_fs_.lstatSync)(u);if(c.isSymbolicLink())continue;c.isDirectory()?walkFiles(u,d,l,n+1):c.isFile()&&l.push({name:d,size:c.size,mtime:c.mtimeMs})}catch{}}return l}function fileHash(C){return new Promise(((h,l)=>{const n=(0,external_crypto_.createHash)("sha256"),e=(0,external_fs_.createReadStream)(C);e.on("data",(o=>n.update(o))),e.on("end",(()=>h(n.digest("hex")))),e.on("error",l)}))}app.get("/api/files/:sessionId",checkAuth,((C,h)=>{const l=manager.get(C.params.sessionId);if(!l)return h.status(404).json({error:"\u4F1A\u8BDD\u4E0D\u5B58\u5728"});const n=C.query.path||"",e=safePath(l.cwd,n);if(!e)return h.status(403).json({error:"\u8DEF\u5F84\u8D8A\u754C"});try{if(!(0,external_fs_.statSync)(e).isDirectory())return h.status(400).json({error:"\u4E0D\u662F\u76EE\u5F55"});const o=(0,external_fs_.readdirSync)(e,{withFileTypes:!0}).filter((c=>c.name!==".git")).map((c=>{const i=(0,external_path_.join)(e,c.name);try{const a=(0,external_fs_.lstatSync)(i);return a.isSymbolicLink()?null:{name:c.name,isDir:a.isDirectory(),size:a.isFile()?a.size:void 0,mtime:a.mtimeMs}}catch{return null}})).filter(Boolean).sort(((c,i)=>i.isDir-c.isDir||c.name.localeCompare(i.name))),u=n?n.replace(/\\/g,"/"):"",d=u.includes("/")?u.slice(0,u.lastIndexOf("/")):u?"":null;h.json({ok:!0,cwd:l.cwd,path:u,parent:d,entries:o})}catch(o){h.status(500).json({error:o.message})}})),app.get("/api/files/:sessionId/download",checkAuth,((C,h)=>{const l=manager.get(C.params.sessionId);if(!l)return h.status(404).json({error:"\u4F1A\u8BDD\u4E0D\u5B58\u5728"});const n=C.query.path;if(!n)return h.status(400).json({error:"\u7F3A\u5C11 path \u53C2\u6570"});const e=safePath(l.cwd,n);if(!e)return h.status(403).json({error:"\u8DEF\u5F84\u8D8A\u754C"});try{const o=(0,external_fs_.statSync)(e);if(!o.isFile())return h.status(400).json({error:"\u4E0D\u662F\u6587\u4EF6"});const u=n.split("/").pop();h.setHeader("Content-Disposition",`attachment; filename*=UTF-8''${encodeURIComponent(u)}`),h.setHeader("Content-Length",o.size),(0,external_fs_.createReadStream)(e).pipe(h)}catch{h.status(404).json({error:"\u6587\u4EF6\u4E0D\u5B58\u5728"})}})),app.post("/api/files/:sessionId/upload",checkAuth,((C,h)=>{const l=manager.get(C.params.sessionId);if(!l)return h.status(404).json({error:"\u4F1A\u8BDD\u4E0D\u5B58\u5728"});const n=(C.query.path||"").replace(/\\/g,"/"),e=safePath(l.cwd,n);if(!e)return h.status(403).json({error:"\u8DEF\u5F84\u8D8A\u754C"});(0,external_fs_.mkdirSync)(e,{recursive:!0}),multer({storage:multer.diskStorage({destination:(o,u,d)=>{u.originalname=Buffer.from(u.originalname,"latin1").toString("utf8");const c=u.originalname.includes("/")?u.originalname.slice(0,u.originalname.lastIndexOf("/")):"",i=c?(0,external_path_.join)(e,c):e,a=(0,external_path_.resolve)((0,external_path_.normalize)(i)),t=(0,external_path_.resolve)((0,external_path_.normalize)(l.cwd));if(a!==t&&!a.startsWith(t+external_path_.sep))return d(new Error("\u8DEF\u5F84\u8D8A\u754C"));(0,external_fs_.mkdirSync)(i,{recursive:!0}),d(null,i)},filename:(o,u,d)=>{const c=u.originalname.includes("/")?u.originalname.slice(u.originalname.lastIndexOf("/")+1):u.originalname;d(null,c)}}),limits:{fileSize:500*1024*1024}}).array("files",100)(C,h,(o=>{if(o)return h.status(400).json({error:o.message});if(!C.files?.length)return h.status(400).json({error:"\u6CA1\u6709\u6587\u4EF6"});const u=C.files.map((d=>({name:d.originalname,size:d.size,path:(0,external_path_.resolve)(d.path).replace(/\\/g,"/")})));io.emit("files:changed",{sessionId:l.id}),h.json({ok:!0,files:u})}))})),app.delete("/api/files/:sessionId",checkAuth,((C,h)=>{const l=manager.get(C.params.sessionId);if(!l)return h.status(404).json({error:"\u4F1A\u8BDD\u4E0D\u5B58\u5728"});const n=C.query.path;if(!n)return h.status(400).json({error:"\u7F3A\u5C11 path \u53C2\u6570"});const e=safePath(l.cwd,n);if(!e)return h.status(403).json({error:"\u8DEF\u5F84\u8D8A\u754C"});if((0,external_path_.resolve)((0,external_path_.normalize)(e))===(0,external_path_.resolve)((0,external_path_.normalize)(l.cwd)))return h.status(403).json({error:"\u4E0D\u80FD\u5220\u9664\u4F1A\u8BDD\u6839\u76EE\u5F55"});try{(0,external_fs_.statSync)(e).isDirectory()?(0,external_fs_.rmSync)(e,{recursive:!0}):(0,external_fs_.unlinkSync)(e),io.emit("files:changed",{sessionId:l.id}),h.json({ok:!0})}catch(o){h.status(500).json({error:o.message})}})),app.get("/api/files/:sessionId/manifest",checkAuth,(async(C,h)=>{const l=manager.get(C.params.sessionId);if(!l)return h.status(404).json({error:"\u4F1A\u8BDD\u4E0D\u5B58\u5728"});const n=C.query.path||"",e=safePath(l.cwd,n);if(!e)return h.status(403).json({error:"\u8DEF\u5F84\u8D8A\u754C"});try{const o=walkFiles(e,""),u=[];for(const d of o){if(d.size>=50*1024*1024){u.push(d);continue}try{const c=await fileHash((0,external_path_.join)(e,d.name));u.push({...d,hash:c})}catch{u.push(d)}}h.json({ok:!0,cwd:l.cwd,path:n,files:u})}catch(o){h.status(500).json({error:o.message})}})),app.get("/api/me",checkAuth,((C,h)=>{const l=getUserInfo(C.headers.cookie);h.json({name:l?.name||"\u7528\u6237",role:l?.role||"admin",exclusive:isExclusiveMode(),hasUsers:hasUsers(),dir:l?.dir||null})}));const gatewayFile=(0,external_path_.join)(server_configDir,"gateway.json");function loadGateway(){try{return JSON.parse((0,external_fs_.readFileSync)(gatewayFile,"utf8"))}catch{return{baseUrl:"",apiKey:""}}}app.get("/api/gateway",checkAdminAuth,((C,h)=>{const l=loadGateway();h.json({baseUrl:l.baseUrl||"",apiKey:l.apiKey?l.apiKey.length>12?l.apiKey.slice(0,6)+"***"+l.apiKey.slice(-4):"***":"",hasKey:!!l.apiKey})})),app.post("/api/gateway",checkAdminAuth,express.json(),((C,h)=>{const{baseUrl:l,apiKey:n}=C.body||{},e=loadGateway(),o={baseUrl:typeof l=="string"?l.trim():e.baseUrl,apiKey:!n||n.includes("***")?e.apiKey:n.trim()};try{(0,external_fs_.mkdirSync)(server_configDir,{recursive:!0}),(0,external_fs_.writeFileSync)(gatewayFile,JSON.stringify(o,null,2),{mode:384}),h.json({ok:!0})}catch(u){h.status(500).json({error:u.message})}})),app.get("/api/secrets",checkAdminAuth,((C,h)=>{try{h.json(secretsOverview())}catch(l){h.status(500).json({error:l.message})}})),app.post("/api/secrets",checkAdminAuth,express.json(),((C,h)=>{const{name:l,value:n,type:e,scope:o,cwd:u}=C.body||{},d=upsertSecret({name:l,value:n,type:e,scope:o,cwd:u});if(d.error)return h.status(400).json(d);h.json(d)})),app.delete("/api/secrets",checkAdminAuth,express.json(),((C,h)=>{const{name:l,scope:n,cwd:e}=C.body||{};if(!l)return h.status(400).json({error:"\u7F3A\u5C11 name"});try{h.json(deleteSecret({name:l,scope:n,cwd:e}))}catch(o){h.status(500).json({error:o.message})}})),app.get("/api/session/:id/secrets",checkAuth,((C,h)=>{const l=manager.get(C.params.id);if(!l)return h.status(404).json({error:"\u4F1A\u8BDD\u4E0D\u5B58\u5728"});h.json({cwd:l.cwd,project:listSecrets("project",l.cwd),global:listSecrets("global")})})),app.post("/api/session/:id/secrets",checkAuth,express.json(),((C,h)=>{const l=manager.get(C.params.id);if(!l)return h.status(404).json({error:"\u4F1A\u8BDD\u4E0D\u5B58\u5728"});const{name:n,value:e,type:o}=C.body||{},u=upsertSecret({name:n,value:e,type:o,scope:"project",cwd:l.cwd});if(u.error)return h.status(400).json(u);h.json(u)})),app.delete("/api/session/:id/secrets",checkAuth,express.json(),((C,h)=>{const l=manager.get(C.params.id);if(!l)return h.status(404).json({error:"\u4F1A\u8BDD\u4E0D\u5B58\u5728"});const{name:n}=C.body||{};if(!n)return h.status(400).json({error:"\u7F3A\u5C11 name"});h.json(deleteSecret({name:n,scope:"project",cwd:l.cwd}))})),app.post("/api/session/:id/secrets/bulk",checkAuth,express.json(),((C,h)=>{const l=manager.get(C.params.id);if(!l)return h.status(404).json({error:"\u4F1A\u8BDD\u4E0D\u5B58\u5728"});const n=upsertManyProject(l.cwd,(C.body||{}).text||"");if(n.error)return h.status(400).json(n);h.json(n)})),app.get("/api/session/:id/guard",checkAuth,((C,h)=>{const l=manager.get(C.params.id);if(!l)return h.status(404).json({error:"\u4F1A\u8BDD\u4E0D\u5B58\u5728"});const n=getProjectGuard(l.cwd);h.json({cwd:l.cwd,enabled:n.enabled,level:n.level,entities:n.entities})})),app.put("/api/session/:id/guard",checkAuth,express.json(),((C,h)=>{const l=manager.get(C.params.id);if(!l)return h.status(404).json({error:"\u4F1A\u8BDD\u4E0D\u5B58\u5728"});const{enabled:n,level:e}=C.body||{},o=setProjectGuard(l.cwd,{enabled:n,level:e});if(o.error)return h.status(400).json(o);h.json(o)})),app.post("/api/session/:id/guard/entity",checkAuth,express.json(),((C,h)=>{const l=manager.get(C.params.id);if(!l)return h.status(404).json({error:"\u4F1A\u8BDD\u4E0D\u5B58\u5728"});const{name:n,type:e}=C.body||{},o=upsertEntity(l.cwd,{name:n,type:e});if(o.error)return h.status(400).json(o);h.json(o)})),app.delete("/api/session/:id/guard/entity",checkAuth,express.json(),((C,h)=>{const l=manager.get(C.params.id);if(!l)return h.status(404).json({error:"\u4F1A\u8BDD\u4E0D\u5B58\u5728"});const{name:n}=C.body||{};if(!n)return h.status(400).json({error:"\u7F3A\u5C11 name"});h.json(deleteEntity(l.cwd,n))})),app.post("/api/session/:id/guard/bulk",checkAuth,express.json(),((C,h)=>{const l=manager.get(C.params.id);if(!l)return h.status(404).json({error:"\u4F1A\u8BDD\u4E0D\u5B58\u5728"});const n=upsertManyEntities(l.cwd,(C.body||{}).text||"");if(n.error)return h.status(400).json(n);h.json(n)})),app.get("/api/session/:id/guard/audit",checkAuth,((C,h)=>{const l=manager.get(C.params.id);if(!l)return h.status(404).json({error:"\u4F1A\u8BDD\u4E0D\u5B58\u5728"});const n=l.claudeSessionId||C.params.id;h.json({entries:loadAudit(n)})})),app.get("/api/session/:id/followup",checkAuth,((C,h)=>{const l=manager.get(C.params.id);if(!l||typeof l.followUpState!="function")return h.status(404).json({error:"\u4F1A\u8BDD\u4E0D\u5B58\u5728\u6216\u975E AI \u4F1A\u8BDD"});h.json(l.followUpState())})),app.put("/api/session/:id/followup",checkAuth,express.json(),((C,h)=>{const l=manager.get(C.params.id);if(!l||typeof l.setFollowUp!="function")return h.status(404).json({error:"\u4F1A\u8BDD\u4E0D\u5B58\u5728\u6216\u975E AI \u4F1A\u8BDD"});const{enabled:n,intervalMinutes:e}=C.body||{};h.json(l.setFollowUp({enabled:n,intervalMinutes:e}))}));function maybeKickRelay(C){try{if(!C?.autoSend)return;const h=manager.get(C.sessionId);h&&typeof h._maybeRelayDraft=="function"&&h.alive&&!h.isBusy&&h._maybeRelayDraft()}catch{}}app.get("/api/inbox",checkAuth,((C,h)=>{const l=getUserInfo(C.headers.cookie),{sessionId:n}=C.query;try{try{rebindDrafts(manager.listSessions())}catch{}const e=listDrafts({sessionId:n||void 0,owner:l?.name,includeAll:l?.role==="admin"}),o=new Set(manager.listSessions().map((u=>u.id)));h.json({drafts:e.map((u=>({...u,sessionAlive:o.has(u.sessionId)}))),counts:countBySession()})}catch(e){h.status(500).json({error:e.message})}})),app.post("/api/inbox",checkAuth,express.json({limit:"1mb"}),((C,h)=>{const l=getUserInfo(C.headers.cookie),n=C.body||{},e=n.sessionId?manager.get(n.sessionId):null;try{const o=addDraft({...n,owner:n.owner||l?.name||null},e);maybeKickRelay(o),h.json({ok:!0,draft:o})}catch(o){h.status(400).json({error:o.message})}})),app.put("/api/inbox/:id",checkAuth,express.json({limit:"1mb"}),((C,h)=>{const l=C.body||{},n=l.sessionId?manager.get(l.sessionId):null,e=updateDraft(C.params.id,l,n);if(!e)return h.status(404).json({error:"\u8349\u7A3F\u4E0D\u5B58\u5728"});maybeKickRelay(e),h.json({ok:!0,draft:e})})),app.delete("/api/inbox/:id",checkAuth,((C,h)=>{if(!removeDraft(C.params.id))return h.status(404).json({error:"\u8349\u7A3F\u4E0D\u5B58\u5728"});h.json({ok:!0})}));function _spawnOpen(C,h){try{const l=(0,external_child_process_namespaceObject.spawn)(C,h,{detached:!0,stdio:"ignore",shell:process.platform==="win32"});return l.on("error",(()=>{})),l.unref(),!0}catch{return!1}}app.post("/api/session/:id/open-folder",checkAuth,((C,h)=>{const l=manager.get(C.params.id);if(!l?.cwd)return h.status(404).json({error:"\u4F1A\u8BDD\u6216\u76EE\u5F55\u4E0D\u5B58\u5728"});const n=l.cwd,e=process.platform==="win32"?_spawnOpen("explorer.exe",[n]):process.platform==="darwin"?_spawnOpen("open",[n]):_spawnOpen("xdg-open",[n]);h.json({ok:e,cwd:n})})),app.post("/api/session/:id/open-editor",checkAuth,((C,h)=>{const l=manager.get(C.params.id);if(!l?.cwd)return h.status(404).json({error:"\u4F1A\u8BDD\u6216\u76EE\u5F55\u4E0D\u5B58\u5728"});const n=_spawnOpen("code",[l.cwd]);h.json({ok:n})}));function getPkgVersion(){for(const C of["../package.json","./package.json","package.json"])try{const h=JSON.parse((0,external_fs_.readFileSync)((0,external_path_.join)(server_dirname,C),"utf8")).version;if(h&&h!=="0.0.0")return h}catch{}return"0.0.0"}const PKG_VERSION=getPkgVersion();let _latestVersion=null,_lastVersionCheck=0;const VERSION_CHECK_INTERVAL=600*1e3;async function checkLatestVersion(){const C=Date.now();if(_latestVersion&&C-_lastVersionCheck<VERSION_CHECK_INTERVAL)return _latestVersion;try{const h=await fetch("https://registry.npmjs.org/@wendongfly/myhi/latest",{headers:{Accept:"application/json"},signal:AbortSignal.timeout(8e3)});h.ok&&(_latestVersion=(await h.json()).version,_lastVersionCheck=C)}catch{}return _latestVersion}app.get("/api/version",(async(C,h)=>{const l=getPkgVersion(),n=await checkLatestVersion();h.json({current:l,latest:n||l,updateAvailable:n&&n!==l})}));function isSystemd(){return process.platform==="linux"&&(process.env.INVOCATION_ID||process.env.JOURNAL_STREAM)}function getNpmUpgradeCmd(){const C="--registry https://registry.npmjs.org";return process.platform==="win32"?`npm.cmd install -g @wendongfly/myhi@latest ${C}`:`npm install -g @wendongfly/myhi@latest ${C}`}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,((C,h)=>{const l=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""}})();h.json({asrUrl:l||""})})),app.post("/api/server-config",checkAuth,express.json(),((C,h)=>{const{asrUrl:l}=C.body||{},n=(0,external_path_.join)((0,external_os_.homedir)(),".myhi","asr.json");try{const e=(l||"").trim();(0,external_fs_.writeFileSync)(n,JSON.stringify({url:e},null,2),"utf8"),h.json({ok:!0})}catch(e){h.status(500).json({error:e.message})}})),app.post("/api/restart",checkAuth,((C,h)=>{h.json({ok:!0}),setTimeout((()=>restartService()),300)})),app.post("/api/update",checkAuth,(async(C,h)=>{try{await execAsync(getNpmUpgradeCmd(),{encoding:"utf8",timeout:12e4}),console.log("[myhi] \u5347\u7EA7\u5B8C\u6210"),h.json({ok:!0,message:"\u5347\u7EA7\u5B8C\u6210\uFF0C\u670D\u52A1\u5C06\u81EA\u52A8\u91CD\u542F..."}),setTimeout((()=>restartService()),500)}catch(l){const n=(l.stderr||l.stdout||l.message||"").split(`
385
385
  `).filter((e=>e.trim())).slice(-5).join(`
386
386
  `);console.error("[myhi] \u5347\u7EA7\u5931\u8D25:",n),h.status(500).json({ok:!1,error:`npm install \u5931\u8D25\uFF1A
387
387
  `+n+`
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wendongfly/myhi",
3
- "version": "1.3.139",
3
+ "version": "1.3.141",
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",