@wendongfly/myhi 1.3.53 → 1.3.55

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
@@ -355,7 +355,8 @@ Content-Length: `+C+`\r
355
355
  `).filter(Boolean).slice(-20);for(const m of h)try{const p=JSON.parse(m);if(p.type==="human"&&p.message?.content){const f=typeof p.message.content=="string"?p.message.content:p.message.content.find((i=>i.type==="text"))?.text||"";f&&this._history.push({type:"user",content:f,timestamp:p.timestamp})}else p.type==="assistant"&&p.message?.content?this._history.push({type:"assistant",message:p.message,timestamp:p.timestamp}):p.type==="result"&&this._history.push({type:"result",total_cost_usd:p.costUSD||p.total_cost_usd,timestamp:p.timestamp})}catch{}return}}catch(l){console.warn("[agent] \u52A0\u8F7D\u6062\u590D\u4F1A\u8BDD\u5386\u53F2\u5931\u8D25:",l.message)}}get isBusy(){return this._busy}getRecentHistory(l=20){return this._history.slice(-l)}getUsage(){return{...this._usage}}addViewer(l,o){this._viewers.add(l),o&&this._viewerNames.set(l,o)}removeViewer(l){this._viewers.delete(l),this._viewerNames.delete(l)}get viewerCount(){return new Set(this._viewerNames.values()).size||this._viewers.size}takeControl(l,o){this.controlHolder=l,this.controlHolderName=o||null,this.lastInputTime=Date.now()}releaseControl(){this.controlHolder=null,this.controlHolderName=null,this.lastInputTime=null}isController(l){return this.controlHolder===l}setPermissionMode(l){this.permissionMode=l,this._proc&&(this.kill(),this.alive=!0)}toJSON(){return{id:this.id,title:this.title,cwd:this.cwd,createdAt:this.createdAt,mode:"agent",alive:this.alive,viewers:this.viewerCount,controlHolder:this.controlHolder,controlHolderName:this.controlHolderName,permissionMode:this.permissionMode,owner:this.owner,claudeSessionId:this.claudeSessionId,busy:this._busy,usage:this._usage}}}let _claudeSessionsCache=null,_claudeSessionsCacheTime=0;const CLAUDE_SESSIONS_CACHE_TTL=3e4;function listLocalClaudeSessions(w){const l=Date.now();if(_claudeSessionsCache&&l-_claudeSessionsCacheTime<CLAUDE_SESSIONS_CACHE_TTL){if(!w)return _claudeSessionsCache;const m=(0,external_path_.resolve)((0,external_path_.normalize)(w)).replace(/[\\/]+$/,"");return _claudeSessionsCache.filter((p=>(0,external_path_.resolve)((0,external_path_.normalize)(p.projectPath)).replace(/[\\/]+$/,"").startsWith(m)))}const o=[],g=w?(0,external_path_.resolve)((0,external_path_.normalize)(w)).replace(/[\\/]+$/,""):null;try{const m=(0,external_path_.join)((0,external_os_.homedir)(),".claude","projects");if(!(0,external_fs_.existsSync)(m))return o;for(const p of(0,external_fs_.readdirSync)(m,{withFileTypes:!0})){if(!p.isDirectory())continue;const f=(0,external_path_.join)(m,p.name);try{for(const i of(0,external_fs_.readdirSync)(f)){if(!i.endsWith(".jsonl"))continue;const r=i.replace(".jsonl",""),s=(0,external_path_.join)(f,i);try{const t=(0,external_fs_.readFileSync)(s,"utf8").split(`
356
356
  `).filter(Boolean);let e=null,c=null,a=0,n="";for(const u of t)try{const v=JSON.parse(u);if(e||(e=v),c=v,a++,!n&&v.type==="human"&&v.message?.content){const b=v.message.content;if(typeof b=="string")n=b.slice(0,120);else if(Array.isArray(b)){const _=b.find((k=>k.type==="text"));_&&(n=_.text?.slice(0,120)||"")}}}catch{}let d=p.name;if(process.platform==="win32"&&/^[A-Za-z]--/.test(d)?d=d[0]+":\\"+d.slice(3).replace(/-/g,"\\"):d.startsWith("-")&&(d=d.replace(/-/g,"/")),g&&!(0,external_path_.resolve)((0,external_path_.normalize)(d)).replace(/[\\/]+$/,"").startsWith(g))continue;o.push({sessionId:r,projectDir:p.name,projectPath:d,messageCount:a,summary:n,createdAt:e?.timestamp||null,updatedAt:c?.timestamp||null})}catch{}}}catch{}}}catch{}o.sort(((m,p)=>new Date(p.updatedAt||0).getTime()-new Date(m.updatedAt||0).getTime()));const h=o.slice(0,50);return _claudeSessionsCache=h,_claudeSessionsCacheTime=Date.now(),h.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(w){try{(0,external_fs_.mkdirSync)(CONFIG_DIR,{recursive:!0});const l=SESSIONS_FILE+".tmp";(0,external_fs_.writeFileSync)(l,JSON.stringify(w,null,2),{mode:384}),(0,external_fs_.renameSync)(l,SESSIONS_FILE)}catch(l){console.error("[sessions] \u6301\u4E45\u5316\u5199\u5165\u5931\u8D25:",l.message)}}class Session extends external_events_.EventEmitter{constructor(l,o={}){super(),this.id=l,this.createdAt=new Date().toISOString(),this.title=o.title||DEFAULT_SHELL,this.cwd=o.cwd||process.env.MYHI_CWD||process.cwd(),this.initCmd=o.initCmd||null,this.owner=o.owner||null,this.cols=o.cols||120,this.rows=o.rows||30,this._viewers=new Set,this._viewerNames=new Map,this.controlHolder=null,this.controlHolderName=null,this.lastInputTime=null;const g={...process.env};if(delete g.CLAUDECODE,delete g.CLAUDE_CODE_ENTRYPOINT,delete g.CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC,o.userDir){const m=(0,external_path_.join)(o.userDir,".gitconfig");(0,external_fs_.existsSync)(m)&&(g.GIT_CONFIG_GLOBAL=m)}this._pty=external_node_pty_namespaceObject.spawn(DEFAULT_SHELL,[],{name:"xterm-256color",cols:this.cols,rows:this.rows,cwd:this.cwd,env:g,useConpty:!1}),this._scrollback="";const h=100*1024;if(this._pty.onData((m=>{this.emit("data",m),this._scrollback+=m,this._scrollback.length>h&&(this._scrollback=this._scrollback.slice(this._scrollback.length-h))})),this._pty.onExit((({exitCode:m})=>{this.exitCode=m,this.emit("exit",m)})),o.initCmd){const m=o.initCmd;let p=!1;const f=i=>{!p&&/[$>#\]]\s*$/.test(i.trimEnd())&&(p=!0,this._pty.off("data",f),setTimeout((()=>this._pty.write(m+"\r")),120))};this._pty.onData(f),setTimeout((()=>{p||(p=!0,this._pty.write(m+"\r"))}),3e3)}}write(l){l!=null&&this._pty.write(l)}resize(l,o){this.cols=l,this.rows=o,this._pty.resize(l,o)}kill(){try{this._pty.kill()}catch{}}addViewer(l,o){this._viewers.add(l),o&&this._viewerNames.set(l,o)}removeViewer(l){this._viewers.delete(l),this._viewerNames.delete(l)}get viewerCount(){return new Set(this._viewerNames.values()).size||this._viewers.size}takeControl(l,o){this.controlHolder=l,this.controlHolderName=o||null,this.lastInputTime=Date.now()}releaseControl(){this.controlHolder=null,this.controlHolderName=null,this.lastInputTime=null}isController(l){return this.controlHolder===l}toJSON(){return{id:this.id,title:this.title,cwd:this.cwd,createdAt:this.createdAt,cols:this.cols,rows:this.rows,viewers:this.viewerCount,alive:this.exitCode===void 0,controlHolder:this.controlHolder,controlHolderName:this.controlHolderName,owner:this.owner}}}class SessionManager{constructor(){this._sessions=new Map,this._agentSessions=new Map;for(const l of loadSavedConfigs())try{if(l.mode==="agent"){if(l.claudeSessionId){const o=(0,external_crypto_.randomUUID)(),g=new AgentSession(o,{...l,resumeSessionId:l.claudeSessionId});g.on("session-id",(()=>this._persist())),this._agentSessions.set(o,g),console.log(`[\u4F1A\u8BDD] \u6062\u590D Agent "${l.title}" (claude: ${l.claudeSessionId.slice(0,8)}...)`)}}else this._spawn((0,external_crypto_.randomUUID)(),l,!1)}catch(o){console.warn(`[\u4F1A\u8BDD] \u6062\u590D "${l.title}" \u5931\u8D25:`,o.message)}}_spawn(l,o,g=!0){const h=new Session(l,o);return this._sessions.set(l,h),h.on("exit",(()=>{setTimeout((()=>this._sessions.delete(l)),300*1e3)})),g&&this._persist(),h}create(l={}){return this._spawn((0,external_crypto_.randomUUID)(),l,!0)}createAgent(l={}){const o=(0,external_crypto_.randomUUID)(),g=new AgentSession(o,l);return this._agentSessions.set(o,g),g.on("session-id",(()=>this._persist())),this._persist(),g}get(l){return this._sessions.get(l)||this._agentSessions.get(l)}list(l,o=!1){let g=[...this._sessions.values()].map((m=>({...m.toJSON(),mode:"pty"}))),h=[...this._agentSessions.values()].map((m=>m.toJSON()));return l&&!o&&(g=g.filter((m=>!m.owner||m.owner===l)),h=h.filter((m=>!m.owner||m.owner===l))),[...g,...h]}listSessions(){return[...this._sessions.values(),...this._agentSessions.values()]}kill(l){const o=this._sessions.get(l);if(o){o.kill(),this._sessions.delete(l),this._persist();return}const g=this._agentSessions.get(l);g&&(g.kill(),this._agentSessions.delete(l),this._persist())}persist(){this._persist()}_persist(){const l=[...this._sessions.values()].filter((g=>g.exitCode===void 0)).map((g=>({mode:"pty",title:g.title,cwd:g.cwd,initCmd:g.initCmd||void 0,owner:g.owner||void 0}))),o=[...this._agentSessions.values()].filter((g=>g.alive)).map((g=>({mode:"agent",title:g.title,cwd:g.cwd,owner:g.owner||void 0,userDir:g.userDir||void 0,claudeSessionId:g.claudeSessionId||void 0})));writeSavedConfigs([...l,...o])}}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(w){console.warn("[\u89D2\u8272] \u52A0\u8F7D roles.json \u5931\u8D25:",w.message)}return rolesConfig=null,null}function lookupPassword(w){if(!rolesConfig?.passwords)return null;const l=rolesConfig.passwords[w];return l?{name:l.name||"\u7528\u6237",role:l.role||"viewer"}:null}function hasPermission(w,l){return(ROLE_LEVELS[w]||0)>=(ROLE_LEVELS[l]||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(w){console.warn("[\u7528\u6237] \u52A0\u8F7D users.json \u5931\u8D25:",w.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(w){usersConfig||(usersConfig={users:{}}),usersConfig.exclusive=!!w,_writeUsers()}function lookupUser(w){if(!usersConfig?.users)return null;const l=usersConfig.users[w];return l?{name:l.name||"\u7528\u6237",dir:l.dir}:null}function listUsers(){return usersConfig?.users?Object.entries(usersConfig.users).map((([w,l])=>({password:w.slice(0,2)+"*".repeat(w.length-2),name:l.name,dir:l.dir}))):[]}function addUser(w,l,o){usersConfig||(usersConfig={users:{}}),usersConfig.users||(usersConfig.users={}),usersConfig.users[w]={name:l,dir:o},_writeUsers()}function removeUser(w){return!usersConfig?.users||!usersConfig.users[w]?!1:(delete usersConfig.users[w],_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 HTTPS_PORT=parseInt(process.env.HTTPS_PORT,10)||0,httpsServer=null;function getOrCreateCert(){const w=(0,external_path_.join)(configDir,"cert.pem"),l=(0,external_path_.join)(configDir,"key.pem");if((0,external_fs_.existsSync)(w)&&(0,external_fs_.existsSync)(l))try{return{cert:(0,external_fs_.readFileSync)(w),key:(0,external_fs_.readFileSync)(l)}}catch{}try{return(0,external_child_process_namespaceObject.execSync)(`openssl req -x509 -newkey rsa:2048 -keyout "${l}" -out "${w}" -days 3650 -nodes -subj "/CN=myhi"`,{stdio:"pipe",timeout:1e4}),{cert:(0,external_fs_.readFileSync)(w),key:(0,external_fs_.readFileSync)(l)}}catch(o){return console.warn(`[myhi] \u751F\u6210 HTTPS \u8BC1\u4E66\u5931\u8D25\uFF08openssl \u4E0D\u53EF\u7528\uFF09: ${o.message?.split(`
357
357
  `)[0]||o}`),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(w,l){let o=!1;try{process.kill(w,0),o=!0}catch{}if(!o)return!0;console.log(`[myhi] \u68C0\u6D4B\u5230\u5DF2\u8FD0\u884C\u7684${l} (PID ${w})\uFF0C\u6B63\u5728\u505C\u6B62...`);try{process.platform==="win32"?(0,external_child_process_namespaceObject.execSync)(`taskkill /PID ${w} /F /T`,{stdio:"pipe",timeout:5e3}):process.kill(w,"SIGTERM")}catch{}const g=Date.now()+5e3;for(;Date.now()<g;){try{process.kill(w,0)}catch{return!0}const m=Date.now()+200;for(;Date.now()<m;);}let h=!1;try{process.kill(w,0),h=!0}catch{}return!h}(function w(){try{const l=parseInt((0,external_fs_.readFileSync)(daemonPidFile,"utf8").trim(),10);if(l&&l!==process.pid&&l!==parseInt(process.env.MYHI_DAEMON_PID||"0",10)){let o=!1;try{process.kill(l,0),o=!0}catch{}if(o&&!IS_DAEMON_CHILD){console.log("[myhi] \u68C0\u6D4B\u5230\u540E\u53F0 daemon \u5728\u8FD0\u884C\uFF0C\u5148\u505C\u6B62..."),killOldProcess(l,"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 l=parseInt((0,external_fs_.readFileSync)(pidFile,"utf8").trim(),10);l&&l!==process.pid&&(killOldProcess(l,"\u5B9E\u4F8B")||(console.error(`[myhi] \u65E0\u6CD5\u505C\u6B62\u65E7\u8FDB\u7A0B (PID ${l})\uFF0C\u8BF7\u624B\u52A8\u6267\u884C\uFF1A`),console.error(" myhi stop"),console.error(` \u6216: ${process.platform==="win32"?`taskkill /PID ${l} /F`:`kill -9 ${l}`}`),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 w of manager.listSessions())try{w.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",(w=>{console.error("[myhi] \u672A\u6355\u83B7\u5F02\u5E38:",w.message),gracefulShutdown(),process.exit(1)})),process.on("unhandledRejection",(w=>{console.error("[myhi] \u672A\u5904\u7406\u7684 Promise \u62D2\u7EDD:",w)}));function readOrCreate(w,l){try{if((0,external_fs_.existsSync)(w))return(0,external_fs_.readFileSync)(w,"utf8").trim()}catch{}const o=l();return(0,external_fs_.writeFileSync)(w,o,{mode:384}),o}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 w=Date.now();for(const[l,o]of userSessions)o.permanent||(!o.lastUsed||w-o.lastUsed>SESSION_MAX_AGE)&&userSessions.delete(l)}),3600*1e3);function getTailscaleIP(){try{return(0,external_child_process_namespaceObject.execSync)("tailscale ip -4",{timeout:3e3,stdio:"pipe"}).toString().trim().split(`
358
- `)[0]}catch{}try{if(process.platform==="win32"){const w=(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(w)return w[1]}else try{const w=(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(w)return w[1]}catch{const w=(0,external_child_process_namespaceObject.execSync)("route -n get default 2>/dev/null || route -n 2>/dev/null",{timeout:3e3,stdio:"pipe"}).toString(),l=w.match(/interface:\s*(\S+)/i)||w.match(/0\.0\.0\.0\s+(\d+\.\d+\.\d+\.\d+)/);if(l){const o=(0,external_os_.networkInterfaces)()[l[1]];if(o){const g=o.find((h=>h.family==="IPv4"&&!h.internal));if(g)return g.address}if(/^\d+\.\d+\.\d+\.\d+$/.test(l[1]))return l[1]}}}catch{}for(const w of Object.values((0,external_os_.networkInterfaces)()))for(const l of w)if(l.family==="IPv4"&&!l.internal)return l.address;return"localhost"}function parseCookies(w=""){const l={};for(const o of w.split(";")){const g=o.indexOf("=");g>0&&(l[o.slice(0,g).trim()]=o.slice(g+1).trim())}return l}const SESSION_COOKIE="myhi_sid",COOKIE_OPTS_BASE="HttpOnly; SameSite=Strict; Path=/; Max-Age=31536000";function cookieOpts(w){return w?.secure||w?.headers?.["x-forwarded-proto"]==="https"?COOKIE_OPTS_BASE+"; Secure":COOKIE_OPTS_BASE}function setSessionCookie(w,l,o){w.setHeader("Set-Cookie",`${SESSION_COOKIE}=${l||TOKEN}; ${cookieOpts(o)}`)}function getSessionToken(w){return parseCookies(w)[SESSION_COOKIE]||null}function hasValidSession(w){const l=getSessionToken(w);if(l&&userSessions.has(l)){const o=userSessions.get(l);return o&&(o.lastUsed=Date.now()),!0}return!1}function getUserInfo(w){const l=getSessionToken(w);return l?userSessions.get(l):null}const app=express();app.set("trust proxy",!0),app.use(((w,l,o)=>{l.setHeader("Referrer-Policy","no-referrer"),l.setHeader("X-Content-Type-Options","nosniff"),o()}));const httpServer=(0,external_http_.createServer)(app),_tlsOpts=process.env.MYHI_NO_HTTPS?null:getOrCreateCert();_tlsOpts&&(httpsServer=(0,external_https_.createServer)(_tlsOpts,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:(w,l)=>{if(!w||_corsAllowed.test(w))return l(null,!0);l(new Error("CORS \u4E0D\u5141\u8BB8\u6B64\u6765\u6E90"),!1)},credentials:!0},transports:["websocket","polling"],pingTimeout:6e4,pingInterval:25e3,upgradeTimeout:3e4,maxHttpBufferSize:5e6});io.attach(httpServer),httpsServer&&io.attach(httpsServer);const manager=new SessionManager,AUTO_ATTACH=process.env.MYHI_AUTO_ATTACH!=="0",_autoSpawned=new Set;function spawnLocalTerminal(w,l){if(!AUTO_ATTACH||_autoSpawned.has(w)||!process.env.DISPLAY&&process.platform==="linux")return;_autoSpawned.add(w);const o=__nccwpck_require__.ab+"attach.js",g=process.execPath,h=l||"myhi";if(process.platform==="win32"){const m=`"${g}" "${o}" ${w}`,p=(0,external_child_process_namespaceObject.spawn)("wt.exe",["-w","0","new-tab","--title",h,"--","cmd","/k",m],{detached:!0,stdio:"ignore"});p.unref(),p.on("error",(f=>{console.warn("[attach] wt.exe \u5931\u8D25\uFF0C\u56DE\u9000\u5230 cmd:",f.message),(0,external_child_process_namespaceObject.spawn)("cmd.exe",["/c","start",`"${h}"`,"cmd","/k",m],{detached:!0,stdio:"ignore"}).unref()}))}else if(process.platform==="darwin"){const m=`tell application "Terminal" to do script "${g} ${o} ${w}"`;(0,external_child_process_namespaceObject.spawn)("osascript",["-e",m],{detached:!0,stdio:"ignore"}).unref()}else{const m=`${g} "${o}" ${w}`;for(const[p,f]of[["gnome-terminal",["--","bash","-c",`${m}; exec bash`]],["xterm",["-title",h,"-e",m]],["konsole",["--new-tab","-e",m]]]){const i=(0,external_child_process_namespaceObject.spawn)(p,f,{detached:!0,stdio:"ignore"});i.unref(),i.on("error",(()=>{}));break}}}const uploadDir=(0,external_path_.join)(configDir,"uploads");(0,external_fs_.mkdirSync)(uploadDir,{recursive:!0});function checkAuth(w,l,o){const g=w.query.token;if(g&&(g===TOKEN||userSessions.has(g))){const h=userSessions.get(g);if(h?.onetime){userSessions.delete(g);const f=(0,external_crypto_.randomBytes)(16).toString("hex");userSessions.set(f,{role:h.role,name:h.name,lastUsed:Date.now()}),setSessionCookie(l,f,w)}else setSessionCookie(l,g===TOKEN?TOKEN:g,w);const m=Object.keys(w.query).filter((f=>f!=="token")),p=w.path+(m.length?"?"+new URLSearchParams(Object.fromEntries(Object.entries(w.query).filter((([f])=>f!=="token")))):"");return l.redirect(p)}if(hasValidSession(w.headers.cookie)){if(isExclusiveMode()&&activeUser){const h=getSessionToken(w.headers.cookie);if(h!==TOKEN){const m=userSessions.get(h);if(!m||m.name!==activeUser.name)return l.setHeader("Set-Cookie",`${SESSION_COOKIE}=; HttpOnly; SameSite=Strict; Path=/; Max-Age=0`),l.redirect("/login")}}return o()}l.redirect("/login")}const _require=(0,external_module_namespaceObject.createRequire)(import.meta.url);function pkgDirSafe(w){try{return(0,external_path_.dirname)(_require.resolve(`${w}/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:(w,l)=>{l.endsWith(".js")&&w.setHeader("Content-Type","application/javascript; charset=utf-8")}})),app.get("/manifest.json",((w,l)=>l.sendFile((0,external_path_.join)(server_dirname,"manifest.json")))),app.get("/icon.png",((w,l)=>l.sendFile((0,external_path_.join)(server_dirname,"icon.png")))),app.get("/icon.svg",((w,l)=>l.sendFile((0,external_path_.join)(server_dirname,"icon.svg"))));const _loginAttempts=new Map,LOGIN_MAX_ATTEMPTS=5,LOGIN_LOCKOUT_MS=300*1e3;function checkLoginRate(w){const l=Date.now(),o=_loginAttempts.get(w);return!o||l>o.resetAt?!0:o.count<LOGIN_MAX_ATTEMPTS}function recordLoginFailure(w){const l=Date.now(),o=_loginAttempts.get(w);!o||l>o.resetAt?_loginAttempts.set(w,{count:1,resetAt:l+LOGIN_LOCKOUT_MS}):o.count++}function clearLoginFailures(w){_loginAttempts.delete(w)}setInterval((()=>{const w=Date.now();for(const[l,o]of _loginAttempts)w>o.resetAt&&_loginAttempts.delete(l)}),600*1e3),app.get("/login",((w,l)=>{l.sendFile((0,external_path_.join)(server_dirname,"login.html"))})),app.post("/login",express.urlencoded({extended:!1}),((w,l)=>{const o=w.ip;if(!checkLoginRate(o))return l.redirect("/login?error=locked");loadRoles(),loadUsers();const g=w.body?.password,h=w.query.from,m=h&&h.startsWith("/")&&!h.startsWith("//")?h:"/";if(hasUsers()){const f=lookupUser(g);if(!f)return recordLoginFailure(o),l.redirect("/login?error=1");if(isExclusiveMode()&&activeUser&&activeUser.name!==f.name)return l.redirect("/login?error=occupied&user="+encodeURIComponent(activeUser.name));clearLoginFailures(o);const i=(0,external_crypto_.randomBytes)(16).toString("hex");return userSessions.set(i,{role:"operator",name:f.name,dir:f.dir,lastUsed:Date.now()}),isExclusiveMode()&&(activeUser?activeUser.tokens.add(i):activeUser={name:f.name,dir:f.dir,tokens:new Set([i]),loginAt:Date.now()}),setSessionCookie(l,i,w),console.log(`[\u767B\u5F55] ${f.name} \u767B\u5F55\uFF08${isExclusiveMode()?"\u72EC\u5360":"\u5171\u4EAB"}\u6A21\u5F0F\uFF09`),l.redirect(m)}const p=lookupPassword(g);if(p){clearLoginFailures(o);const f=(0,external_crypto_.randomBytes)(16).toString("hex");return userSessions.set(f,{role:p.role,name:p.name,lastUsed:Date.now()}),setSessionCookie(l,f,w),l.redirect(m)}if(g===PASSWORD){clearLoginFailures(o);const f=(0,external_crypto_.randomBytes)(16).toString("hex");return userSessions.set(f,{role:"admin",name:"\u7BA1\u7406\u5458",lastUsed:Date.now()}),setSessionCookie(l,f,w),l.redirect(m)}recordLoginFailure(o),l.redirect("/login?error=1")})),app.post("/logout",((w,l)=>{const o=getSessionToken(w.headers.cookie);o&&(activeUser&&activeUser.tokens.has(o)&&(activeUser.tokens.delete(o),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(o)),l.setHeader("Set-Cookie",`${SESSION_COOKIE}=; HttpOnly; SameSite=Strict; Path=/; Max-Age=0`),l.redirect("/login")})),app.get("/api/status",((w,l)=>{if(isExclusiveMode()&&activeUser)return l.json({occupied:!0,userName:activeUser.name,releasing:!!_exclusiveReleaseTimer});if(hasUsers()&&!isExclusiveMode()){const o=new Set;for(const[,g]of io.sockets.sockets){if(!g.data.userName||g.data.userName==="\u7BA1\u7406\u5458")continue;const h=g.handshake.headers.cookie;h&&!hasValidSession(h)||o.add(g.data.userName)}return l.json({occupied:!1,onlineUsers:[...o]})}l.json({occupied:!1})})),app.get("/admin",((w,l)=>l.sendFile((0,external_path_.join)(server_dirname,"admin.html")))),app.get("/",checkAuth,((w,l)=>l.sendFile((0,external_path_.join)(server_dirname,"index.html")))),app.get("/terminal/:id",checkAuth,((w,l)=>l.sendFile((0,external_path_.join)(server_dirname,"chat.html")))),app.get("/terminal-raw/:id",checkAuth,((w,l)=>l.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,((w,l)=>{const o=getUserInfo(w.headers.cookie);l.json(manager.list(o?.name,o?.role==="admin"))})),app.get("/api/usage",((w,l)=>{try{const o=manager.listSessions().filter((h=>h.mode==="agent"&&h.alive)).map((h=>({id:h.id,title:h.title,owner:h.owner,usage:h._usage})));let g=null;for(const h of o)if(h.usage?.rateLimitInfo){g=h.usage.rateLimitInfo;break}l.json({rateLimit:g,activeSessions:o})}catch(o){l.status(500).json({error:o.message})}})),app.post("/api/sessions",checkAuth,express.json(),((w,l)=>{const o=getUserInfo(w.headers.cookie),g=manager.create({...w.body,owner:o?.name,userDir:o?.dir});l.status(201).json(g.toJSON())})),app.delete("/api/sessions/:id",checkAuth,((w,l)=>{manager.kill(w.params.id),l.status(204).end()})),app.get("/api/claude-sessions",checkAuth,((w,l)=>{const o=getUserInfo(w.headers.cookie);l.json(listLocalClaudeSessions(o?.dir))})),app.get("/api/dirs",checkAuth,((w,l)=>{const o=getUserInfo(w.headers.cookie);if(!o||o.role!=="admin")return l.status(403).json({error:"\u6CA1\u6709\u76EE\u5F55\u6D4F\u89C8\u6743\u9650"});const g=o.dir?(0,external_path_.resolve)((0,external_path_.normalize)(o.dir)):null,h=process.platform==="win32"?"\\":"/";let m=w.query.path||g||process.env.MYHI_CWD||(0,external_os_.homedir)(),p=(0,external_path_.resolve)((0,external_path_.normalize)(m)).replace(/[/\\]+$/,"");process.platform==="win32"&&/^[A-Za-z]:$/.test(p)&&(p+="\\"),g&&!p.startsWith(g)&&(p=g);try{const f=(0,external_fs_.readdirSync)(p,{withFileTypes:!0}).filter((s=>s.isDirectory()&&s.name!==".git")).map((s=>({name:s.name,path:p.replace(/[/\\]+$/,"")+h+s.name}))).sort(((s,t)=>s.name.localeCompare(t.name))),i=(0,external_path_.join)(p,".."),r=!g||(0,external_path_.resolve)((0,external_path_.normalize)(i)).startsWith(g);l.json({current:p,parent:r&&i!==p?i:null,dirs:f})}catch(f){l.status(400).json({error:f.message})}}));function safePath(w,l){const o=(0,external_path_.resolve)((0,external_path_.normalize)(w)),g=(0,external_path_.resolve)((0,external_path_.normalize)((0,external_path_.join)(w,l||"")));if(g!==o&&!g.startsWith(o+external_path_.sep))return null;try{const h=(0,external_fs_.realpathSync)(o),m=(0,external_fs_.existsSync)(g)?(0,external_fs_.realpathSync)(g):g;if(m!==h&&!m.startsWith(h+external_path_.sep))return null}catch{}return g}function walkFiles(w,l,o=[],g=0){if(g>20)return o;let h;try{h=(0,external_fs_.readdirSync)(w,{withFileTypes:!0})}catch{return o}for(const m of h){if(m.name===".git")continue;const p=(0,external_path_.join)(w,m.name),f=l?l+"/"+m.name:m.name;try{const i=(0,external_fs_.lstatSync)(p);if(i.isSymbolicLink())continue;i.isDirectory()?walkFiles(p,f,o,g+1):i.isFile()&&o.push({name:f,size:i.size,mtime:i.mtimeMs})}catch{}}return o}function fileHash(w){return new Promise(((l,o)=>{const g=(0,external_crypto_.createHash)("sha256"),h=(0,external_fs_.createReadStream)(w);h.on("data",(m=>g.update(m))),h.on("end",(()=>l(g.digest("hex")))),h.on("error",o)}))}app.get("/api/files/:sessionId",checkAuth,((w,l)=>{const o=manager.get(w.params.sessionId);if(!o)return l.status(404).json({error:"\u4F1A\u8BDD\u4E0D\u5B58\u5728"});const g=w.query.path||"",h=safePath(o.cwd,g);if(!h)return l.status(403).json({error:"\u8DEF\u5F84\u8D8A\u754C"});try{if(!(0,external_fs_.statSync)(h).isDirectory())return l.status(400).json({error:"\u4E0D\u662F\u76EE\u5F55"});const m=(0,external_fs_.readdirSync)(h,{withFileTypes:!0}).filter((i=>i.name!==".git")).map((i=>{const r=(0,external_path_.join)(h,i.name);try{const s=(0,external_fs_.lstatSync)(r);return s.isSymbolicLink()?null:{name:i.name,isDir:s.isDirectory(),size:s.isFile()?s.size:void 0,mtime:s.mtimeMs}}catch{return null}})).filter(Boolean).sort(((i,r)=>r.isDir-i.isDir||i.name.localeCompare(r.name))),p=g?g.replace(/\\/g,"/"):"",f=p.includes("/")?p.slice(0,p.lastIndexOf("/")):p?"":null;l.json({ok:!0,cwd:o.cwd,path:p,parent:f,entries:m})}catch(m){l.status(500).json({error:m.message})}})),app.get("/api/files/:sessionId/download",checkAuth,((w,l)=>{const o=manager.get(w.params.sessionId);if(!o)return l.status(404).json({error:"\u4F1A\u8BDD\u4E0D\u5B58\u5728"});const g=w.query.path;if(!g)return l.status(400).json({error:"\u7F3A\u5C11 path \u53C2\u6570"});const h=safePath(o.cwd,g);if(!h)return l.status(403).json({error:"\u8DEF\u5F84\u8D8A\u754C"});try{const m=(0,external_fs_.statSync)(h);if(!m.isFile())return l.status(400).json({error:"\u4E0D\u662F\u6587\u4EF6"});const p=g.split("/").pop();l.setHeader("Content-Disposition",`attachment; filename*=UTF-8''${encodeURIComponent(p)}`),l.setHeader("Content-Length",m.size),(0,external_fs_.createReadStream)(h).pipe(l)}catch{l.status(404).json({error:"\u6587\u4EF6\u4E0D\u5B58\u5728"})}})),app.post("/api/files/:sessionId/upload",checkAuth,((w,l)=>{const o=manager.get(w.params.sessionId);if(!o)return l.status(404).json({error:"\u4F1A\u8BDD\u4E0D\u5B58\u5728"});const g=(w.query.path||"").replace(/\\/g,"/"),h=safePath(o.cwd,g);if(!h)return l.status(403).json({error:"\u8DEF\u5F84\u8D8A\u754C"});(0,external_fs_.mkdirSync)(h,{recursive:!0}),multer({storage:multer.diskStorage({destination:(m,p,f)=>{p.originalname=Buffer.from(p.originalname,"latin1").toString("utf8");const i=p.originalname.includes("/")?p.originalname.slice(0,p.originalname.lastIndexOf("/")):"",r=i?(0,external_path_.join)(h,i):h,s=(0,external_path_.resolve)((0,external_path_.normalize)(r)),t=(0,external_path_.resolve)((0,external_path_.normalize)(o.cwd));if(s!==t&&!s.startsWith(t+external_path_.sep))return f(new Error("\u8DEF\u5F84\u8D8A\u754C"));(0,external_fs_.mkdirSync)(r,{recursive:!0}),f(null,r)},filename:(m,p,f)=>{const i=p.originalname.includes("/")?p.originalname.slice(p.originalname.lastIndexOf("/")+1):p.originalname;f(null,i)}}),limits:{fileSize:500*1024*1024}}).array("files",100)(w,l,(m=>{if(m)return l.status(400).json({error:m.message});if(!w.files?.length)return l.status(400).json({error:"\u6CA1\u6709\u6587\u4EF6"});const p=w.files.map((f=>({name:f.originalname,size:f.size,path:(0,external_path_.resolve)(f.path).replace(/\\/g,"/")})));io.emit("files:changed",{sessionId:o.id}),l.json({ok:!0,files:p})}))})),app.delete("/api/files/:sessionId",checkAuth,((w,l)=>{const o=manager.get(w.params.sessionId);if(!o)return l.status(404).json({error:"\u4F1A\u8BDD\u4E0D\u5B58\u5728"});const g=w.query.path;if(!g)return l.status(400).json({error:"\u7F3A\u5C11 path \u53C2\u6570"});const h=safePath(o.cwd,g);if(!h)return l.status(403).json({error:"\u8DEF\u5F84\u8D8A\u754C"});if((0,external_path_.resolve)((0,external_path_.normalize)(h))===(0,external_path_.resolve)((0,external_path_.normalize)(o.cwd)))return l.status(403).json({error:"\u4E0D\u80FD\u5220\u9664\u4F1A\u8BDD\u6839\u76EE\u5F55"});try{(0,external_fs_.statSync)(h).isDirectory()?(0,external_fs_.rmSync)(h,{recursive:!0}):(0,external_fs_.unlinkSync)(h),io.emit("files:changed",{sessionId:o.id}),l.json({ok:!0})}catch(m){l.status(500).json({error:m.message})}})),app.get("/api/files/:sessionId/manifest",checkAuth,(async(w,l)=>{const o=manager.get(w.params.sessionId);if(!o)return l.status(404).json({error:"\u4F1A\u8BDD\u4E0D\u5B58\u5728"});const g=w.query.path||"",h=safePath(o.cwd,g);if(!h)return l.status(403).json({error:"\u8DEF\u5F84\u8D8A\u754C"});try{const m=walkFiles(h,""),p=[];for(const f of m){if(f.size>=50*1024*1024){p.push(f);continue}try{const i=await fileHash((0,external_path_.join)(h,f.name));p.push({...f,hash:i})}catch{p.push(f)}}l.json({ok:!0,cwd:o.cwd,path:g,files:p})}catch(m){l.status(500).json({error:m.message})}})),app.get("/api/me",checkAuth,((w,l)=>{const o=getUserInfo(w.headers.cookie);l.json({name:o?.name||"\u7528\u6237",role:o?.role||"admin",exclusive:isExclusiveMode(),hasUsers:hasUsers(),dir:o?.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,((w,l)=>{const o=loadGateway();l.json({baseUrl:o.baseUrl||"",apiKey:o.apiKey?o.apiKey.length>12?o.apiKey.slice(0,6)+"***"+o.apiKey.slice(-4):"***":"",hasKey:!!o.apiKey})})),app.post("/api/gateway",checkAdminAuth,express.json(),((w,l)=>{const{baseUrl:o,apiKey:g}=w.body||{},h=loadGateway(),m={baseUrl:typeof o=="string"?o.trim():h.baseUrl,apiKey:!g||g.includes("***")?h.apiKey:g.trim()};try{(0,external_fs_.mkdirSync)(configDir,{recursive:!0}),(0,external_fs_.writeFileSync)(gatewayFile,JSON.stringify(m,null,2),{mode:384}),l.json({ok:!0})}catch(p){l.status(500).json({error:p.message})}}));function getPkgVersion(){for(const w of["../package.json","./package.json","package.json"])try{const l=JSON.parse((0,external_fs_.readFileSync)((0,external_path_.join)(server_dirname,w),"utf8")).version;if(l&&l!=="0.0.0")return l}catch{}return"0.0.0"}const PKG_VERSION=getPkgVersion();let _latestVersion=null,_lastVersionCheck=0;const VERSION_CHECK_INTERVAL=600*1e3;async function checkLatestVersion(){const w=Date.now();if(_latestVersion&&w-_lastVersionCheck<VERSION_CHECK_INTERVAL)return _latestVersion;try{const l=await fetch("https://registry.npmjs.org/@wendongfly/myhi/latest",{headers:{Accept:"application/json"},signal:AbortSignal.timeout(8e3)});l.ok&&(_latestVersion=(await l.json()).version,_lastVersionCheck=w)}catch{}return _latestVersion}app.get("/api/version",(async(w,l)=>{const o=getPkgVersion(),g=await checkLatestVersion();l.json({current:o,latest:g||o,updateAvailable:g&&g!==o})}));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.post("/api/update",checkAuth,(async(w,l)=>{try{await execAsync(getNpmUpgradeCmd(),{encoding:"utf8",timeout:12e4}),console.log("[myhi] \u5347\u7EA7\u5B8C\u6210"),l.json({ok:!0,message:"\u5347\u7EA7\u5B8C\u6210\uFF0C\u670D\u52A1\u5C06\u81EA\u52A8\u91CD\u542F..."}),setTimeout((()=>restartService()),500)}catch(o){const g=(o.stderr||o.stdout||o.message||"").split(`
358
+ `)[0]}catch{}try{if(process.platform==="win32"){const w=(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(w)return w[1]}else try{const w=(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(w)return w[1]}catch{const w=(0,external_child_process_namespaceObject.execSync)("route -n get default 2>/dev/null || route -n 2>/dev/null",{timeout:3e3,stdio:"pipe"}).toString(),l=w.match(/interface:\s*(\S+)/i)||w.match(/0\.0\.0\.0\s+(\d+\.\d+\.\d+\.\d+)/);if(l){const o=(0,external_os_.networkInterfaces)()[l[1]];if(o){const g=o.find((h=>h.family==="IPv4"&&!h.internal));if(g)return g.address}if(/^\d+\.\d+\.\d+\.\d+$/.test(l[1]))return l[1]}}}catch{}for(const w of Object.values((0,external_os_.networkInterfaces)()))for(const l of w)if(l.family==="IPv4"&&!l.internal)return l.address;return"localhost"}function parseCookies(w=""){const l={};for(const o of w.split(";")){const g=o.indexOf("=");g>0&&(l[o.slice(0,g).trim()]=o.slice(g+1).trim())}return l}const SESSION_COOKIE="myhi_sid",COOKIE_OPTS_BASE="HttpOnly; SameSite=Strict; Path=/; Max-Age=31536000";function cookieOpts(w){return w?.secure||w?.headers?.["x-forwarded-proto"]==="https"?COOKIE_OPTS_BASE+"; Secure":COOKIE_OPTS_BASE}function setSessionCookie(w,l,o){w.setHeader("Set-Cookie",`${SESSION_COOKIE}=${l||TOKEN}; ${cookieOpts(o)}`)}function getSessionToken(w){return parseCookies(w)[SESSION_COOKIE]||null}function hasValidSession(w){const l=getSessionToken(w);if(l&&userSessions.has(l)){const o=userSessions.get(l);return o&&(o.lastUsed=Date.now()),!0}return!1}function getUserInfo(w){const l=getSessionToken(w);return l?userSessions.get(l):null}const app=express();app.set("trust proxy",!0),app.use(((w,l,o)=>{l.setHeader("Referrer-Policy","no-referrer"),l.setHeader("X-Content-Type-Options","nosniff"),o()}));const httpServer=(0,external_http_.createServer)(app),_tlsOpts=process.env.MYHI_NO_HTTPS?null:getOrCreateCert();_tlsOpts&&(httpsServer=(0,external_https_.createServer)(_tlsOpts,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:(w,l)=>{if(!w||_corsAllowed.test(w))return l(null,!0);l(new Error("CORS \u4E0D\u5141\u8BB8\u6B64\u6765\u6E90"),!1)},credentials:!0},transports:["websocket","polling"],pingTimeout:6e4,pingInterval:25e3,upgradeTimeout:3e4,maxHttpBufferSize:5e6});io.attach(httpServer),httpsServer&&io.attach(httpsServer);const manager=new SessionManager,AUTO_ATTACH=process.env.MYHI_AUTO_ATTACH!=="0",_autoSpawned=new Set;function spawnLocalTerminal(w,l){if(!AUTO_ATTACH||_autoSpawned.has(w)||!process.env.DISPLAY&&process.platform==="linux")return;_autoSpawned.add(w);const o=__nccwpck_require__.ab+"attach.js",g=process.execPath,h=l||"myhi";if(process.platform==="win32"){const m=`"${g}" "${o}" ${w}`,p=(0,external_child_process_namespaceObject.spawn)("wt.exe",["-w","0","new-tab","--title",h,"--","cmd","/k",m],{detached:!0,stdio:"ignore"});p.unref(),p.on("error",(f=>{console.warn("[attach] wt.exe \u5931\u8D25\uFF0C\u56DE\u9000\u5230 cmd:",f.message),(0,external_child_process_namespaceObject.spawn)("cmd.exe",["/c","start",`"${h}"`,"cmd","/k",m],{detached:!0,stdio:"ignore"}).unref()}))}else if(process.platform==="darwin"){const m=`tell application "Terminal" to do script "${g} ${o} ${w}"`;(0,external_child_process_namespaceObject.spawn)("osascript",["-e",m],{detached:!0,stdio:"ignore"}).unref()}else{const m=`${g} "${o}" ${w}`;for(const[p,f]of[["gnome-terminal",["--","bash","-c",`${m}; exec bash`]],["xterm",["-title",h,"-e",m]],["konsole",["--new-tab","-e",m]]]){const i=(0,external_child_process_namespaceObject.spawn)(p,f,{detached:!0,stdio:"ignore"});i.unref(),i.on("error",(()=>{}));break}}}const uploadDir=(0,external_path_.join)(configDir,"uploads");(0,external_fs_.mkdirSync)(uploadDir,{recursive:!0});function checkAuth(w,l,o){const g=w.query.token;if(g&&(g===TOKEN||userSessions.has(g))){const h=userSessions.get(g);if(h?.onetime){userSessions.delete(g);const f=(0,external_crypto_.randomBytes)(16).toString("hex");userSessions.set(f,{role:h.role,name:h.name,lastUsed:Date.now()}),setSessionCookie(l,f,w)}else setSessionCookie(l,g===TOKEN?TOKEN:g,w);const m=Object.keys(w.query).filter((f=>f!=="token")),p=w.path+(m.length?"?"+new URLSearchParams(Object.fromEntries(Object.entries(w.query).filter((([f])=>f!=="token")))):"");return l.redirect(p)}if(hasValidSession(w.headers.cookie)){if(isExclusiveMode()&&activeUser){const h=getSessionToken(w.headers.cookie);if(h!==TOKEN){const m=userSessions.get(h);if(!m||m.name!==activeUser.name)return l.setHeader("Set-Cookie",`${SESSION_COOKIE}=; HttpOnly; SameSite=Strict; Path=/; Max-Age=0`),l.redirect("/login")}}return o()}l.redirect("/login")}const _require=(0,external_module_namespaceObject.createRequire)(import.meta.url);function pkgDirSafe(w){try{return(0,external_path_.dirname)(_require.resolve(`${w}/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:(w,l)=>{l.endsWith(".js")&&w.setHeader("Content-Type","application/javascript; charset=utf-8")}})),app.get("/manifest.json",((w,l)=>l.sendFile((0,external_path_.join)(server_dirname,"manifest.json")))),app.get("/icon.png",((w,l)=>l.sendFile((0,external_path_.join)(server_dirname,"icon.png")))),app.get("/icon.svg",((w,l)=>l.sendFile((0,external_path_.join)(server_dirname,"icon.svg"))));const _loginAttempts=new Map,LOGIN_MAX_ATTEMPTS=5,LOGIN_LOCKOUT_MS=300*1e3;function checkLoginRate(w){const l=Date.now(),o=_loginAttempts.get(w);return!o||l>o.resetAt?!0:o.count<LOGIN_MAX_ATTEMPTS}function recordLoginFailure(w){const l=Date.now(),o=_loginAttempts.get(w);!o||l>o.resetAt?_loginAttempts.set(w,{count:1,resetAt:l+LOGIN_LOCKOUT_MS}):o.count++}function clearLoginFailures(w){_loginAttempts.delete(w)}setInterval((()=>{const w=Date.now();for(const[l,o]of _loginAttempts)w>o.resetAt&&_loginAttempts.delete(l)}),600*1e3),app.get("/login",((w,l)=>{l.sendFile((0,external_path_.join)(server_dirname,"login.html"))})),app.post("/login",express.urlencoded({extended:!1}),((w,l)=>{const o=w.ip;if(!checkLoginRate(o))return l.redirect("/login?error=locked");loadRoles(),loadUsers();const g=w.body?.password,h=w.query.from,m=h&&h.startsWith("/")&&!h.startsWith("//")?h:"/";if(hasUsers()){const f=lookupUser(g);if(!f)return recordLoginFailure(o),l.redirect("/login?error=1");if(isExclusiveMode()&&activeUser&&activeUser.name!==f.name)return l.redirect("/login?error=occupied&user="+encodeURIComponent(activeUser.name));clearLoginFailures(o);const i=(0,external_crypto_.randomBytes)(16).toString("hex");return userSessions.set(i,{role:"operator",name:f.name,dir:f.dir,lastUsed:Date.now()}),isExclusiveMode()&&(activeUser?activeUser.tokens.add(i):activeUser={name:f.name,dir:f.dir,tokens:new Set([i]),loginAt:Date.now()}),setSessionCookie(l,i,w),console.log(`[\u767B\u5F55] ${f.name} \u767B\u5F55\uFF08${isExclusiveMode()?"\u72EC\u5360":"\u5171\u4EAB"}\u6A21\u5F0F\uFF09`),l.redirect(m)}const p=lookupPassword(g);if(p){clearLoginFailures(o);const f=(0,external_crypto_.randomBytes)(16).toString("hex");return userSessions.set(f,{role:p.role,name:p.name,lastUsed:Date.now()}),setSessionCookie(l,f,w),l.redirect(m)}if(g===PASSWORD){clearLoginFailures(o);const f=(0,external_crypto_.randomBytes)(16).toString("hex");return userSessions.set(f,{role:"admin",name:"\u7BA1\u7406\u5458",lastUsed:Date.now()}),setSessionCookie(l,f,w),l.redirect(m)}recordLoginFailure(o),l.redirect("/login?error=1")})),app.post("/logout",((w,l)=>{const o=getSessionToken(w.headers.cookie);o&&(activeUser&&activeUser.tokens.has(o)&&(activeUser.tokens.delete(o),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(o)),l.setHeader("Set-Cookie",`${SESSION_COOKIE}=; HttpOnly; SameSite=Strict; Path=/; Max-Age=0`),l.redirect("/login")})),app.get("/api/status",((w,l)=>{if(isExclusiveMode()&&activeUser)return l.json({occupied:!0,userName:activeUser.name,releasing:!!_exclusiveReleaseTimer});if(hasUsers()&&!isExclusiveMode()){const o=new Set;for(const[,g]of io.sockets.sockets){if(!g.data.userName||g.data.userName==="\u7BA1\u7406\u5458")continue;const h=g.handshake.headers.cookie;h&&!hasValidSession(h)||o.add(g.data.userName)}return l.json({occupied:!1,onlineUsers:[...o]})}l.json({occupied:!1})})),app.get("/admin",((w,l)=>l.sendFile((0,external_path_.join)(server_dirname,"admin.html")))),app.get("/",checkAuth,((w,l)=>l.sendFile((0,external_path_.join)(server_dirname,"index.html")))),app.get("/terminal/:id",checkAuth,((w,l)=>l.sendFile((0,external_path_.join)(server_dirname,"chat.html")))),app.get("/terminal-raw/:id",checkAuth,((w,l)=>l.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,((w,l)=>{const o=getUserInfo(w.headers.cookie);l.json(manager.list(o?.name,o?.role==="admin"))})),app.get("/api/usage",((w,l)=>{try{const o=manager.listSessions().filter((h=>h.mode==="agent"&&h.alive)).map((h=>({id:h.id,title:h.title,owner:h.owner,usage:h._usage})));let g=null;for(const h of o)if(h.usage?.rateLimitInfo){g=h.usage.rateLimitInfo;break}l.json({rateLimit:g,activeSessions:o})}catch(o){l.status(500).json({error:o.message})}})),app.post("/api/sessions",checkAuth,express.json(),((w,l)=>{const o=getUserInfo(w.headers.cookie),g=manager.create({...w.body,owner:o?.name,userDir:o?.dir});l.status(201).json(g.toJSON())})),app.delete("/api/sessions/:id",checkAuth,((w,l)=>{manager.kill(w.params.id),l.status(204).end()})),app.get("/api/claude-sessions",checkAuth,((w,l)=>{const o=getUserInfo(w.headers.cookie);l.json(listLocalClaudeSessions(o?.dir))})),app.get("/api/agent/history/:sessionId",checkAuth,((w,l)=>{const o=manager.get(w.params.sessionId);if(!o||o.mode!=="agent")return l.status(404).json({ok:!1,error:"\u4F1A\u8BDD\u4E0D\u5B58\u5728"});const g=o.claudeSessionId;if(!g)return l.json({ok:!0,messages:[],total:0,hasMore:!1});const h=(0,external_path_.join)((0,external_os_.homedir)(),".claude","projects");let m=null;try{if((0,external_fs_.existsSync)(h))for(const c of(0,external_fs_.readdirSync)(h,{withFileTypes:!0})){if(!c.isDirectory())continue;const a=(0,external_path_.join)(h,c.name,g+".jsonl");if((0,external_fs_.existsSync)(a)){m=a;break}}}catch{}if(!m)return l.json({ok:!0,messages:[],total:0,hasMore:!1});let p;try{p=(0,external_fs_.readFileSync)(m,"utf8").split(`
359
+ `).filter(Boolean)}catch(c){return l.status(500).json({ok:!1,error:c.message})}const f=Math.max(0,parseInt(w.query.offset,10)||0),i=Math.min(200,Math.max(1,parseInt(w.query.limit,10)||50)),r=p.length,s=r-f,t=Math.max(0,s-i),e=[];for(let c=t;c<s;c++)try{const a=JSON.parse(p[c]);if(a.type==="human"&&a.message?.content){const n=typeof a.message.content=="string"?a.message.content:a.message.content.find((d=>d.type==="text"))?.text||"";n&&e.push({type:"user",content:n,timestamp:a.timestamp})}else a.type==="assistant"&&a.message?.content?e.push({type:"assistant",message:a.message,timestamp:a.timestamp}):a.type==="result"&&e.push({type:"result",total_cost_usd:a.costUSD||a.total_cost_usd,timestamp:a.timestamp})}catch{}l.json({ok:!0,messages:e,total:r,hasMore:t>0})})),app.get("/api/dirs",checkAuth,((w,l)=>{const o=getUserInfo(w.headers.cookie);if(!o||o.role!=="admin")return l.status(403).json({error:"\u6CA1\u6709\u76EE\u5F55\u6D4F\u89C8\u6743\u9650"});const g=o.dir?(0,external_path_.resolve)((0,external_path_.normalize)(o.dir)):null,h=process.platform==="win32"?"\\":"/";let m=w.query.path||g||process.env.MYHI_CWD||(0,external_os_.homedir)(),p=(0,external_path_.resolve)((0,external_path_.normalize)(m)).replace(/[/\\]+$/,"");process.platform==="win32"&&/^[A-Za-z]:$/.test(p)&&(p+="\\"),g&&!p.startsWith(g)&&(p=g);try{const f=(0,external_fs_.readdirSync)(p,{withFileTypes:!0}).filter((s=>s.isDirectory()&&s.name!==".git")).map((s=>({name:s.name,path:p.replace(/[/\\]+$/,"")+h+s.name}))).sort(((s,t)=>s.name.localeCompare(t.name))),i=(0,external_path_.join)(p,".."),r=!g||(0,external_path_.resolve)((0,external_path_.normalize)(i)).startsWith(g);l.json({current:p,parent:r&&i!==p?i:null,dirs:f})}catch(f){l.status(400).json({error:f.message})}}));function safePath(w,l){const o=(0,external_path_.resolve)((0,external_path_.normalize)(w)),g=(0,external_path_.resolve)((0,external_path_.normalize)((0,external_path_.join)(w,l||"")));if(g!==o&&!g.startsWith(o+external_path_.sep))return null;try{const h=(0,external_fs_.realpathSync)(o),m=(0,external_fs_.existsSync)(g)?(0,external_fs_.realpathSync)(g):g;if(m!==h&&!m.startsWith(h+external_path_.sep))return null}catch{}return g}function walkFiles(w,l,o=[],g=0){if(g>20)return o;let h;try{h=(0,external_fs_.readdirSync)(w,{withFileTypes:!0})}catch{return o}for(const m of h){if(m.name===".git")continue;const p=(0,external_path_.join)(w,m.name),f=l?l+"/"+m.name:m.name;try{const i=(0,external_fs_.lstatSync)(p);if(i.isSymbolicLink())continue;i.isDirectory()?walkFiles(p,f,o,g+1):i.isFile()&&o.push({name:f,size:i.size,mtime:i.mtimeMs})}catch{}}return o}function fileHash(w){return new Promise(((l,o)=>{const g=(0,external_crypto_.createHash)("sha256"),h=(0,external_fs_.createReadStream)(w);h.on("data",(m=>g.update(m))),h.on("end",(()=>l(g.digest("hex")))),h.on("error",o)}))}app.get("/api/files/:sessionId",checkAuth,((w,l)=>{const o=manager.get(w.params.sessionId);if(!o)return l.status(404).json({error:"\u4F1A\u8BDD\u4E0D\u5B58\u5728"});const g=w.query.path||"",h=safePath(o.cwd,g);if(!h)return l.status(403).json({error:"\u8DEF\u5F84\u8D8A\u754C"});try{if(!(0,external_fs_.statSync)(h).isDirectory())return l.status(400).json({error:"\u4E0D\u662F\u76EE\u5F55"});const m=(0,external_fs_.readdirSync)(h,{withFileTypes:!0}).filter((i=>i.name!==".git")).map((i=>{const r=(0,external_path_.join)(h,i.name);try{const s=(0,external_fs_.lstatSync)(r);return s.isSymbolicLink()?null:{name:i.name,isDir:s.isDirectory(),size:s.isFile()?s.size:void 0,mtime:s.mtimeMs}}catch{return null}})).filter(Boolean).sort(((i,r)=>r.isDir-i.isDir||i.name.localeCompare(r.name))),p=g?g.replace(/\\/g,"/"):"",f=p.includes("/")?p.slice(0,p.lastIndexOf("/")):p?"":null;l.json({ok:!0,cwd:o.cwd,path:p,parent:f,entries:m})}catch(m){l.status(500).json({error:m.message})}})),app.get("/api/files/:sessionId/download",checkAuth,((w,l)=>{const o=manager.get(w.params.sessionId);if(!o)return l.status(404).json({error:"\u4F1A\u8BDD\u4E0D\u5B58\u5728"});const g=w.query.path;if(!g)return l.status(400).json({error:"\u7F3A\u5C11 path \u53C2\u6570"});const h=safePath(o.cwd,g);if(!h)return l.status(403).json({error:"\u8DEF\u5F84\u8D8A\u754C"});try{const m=(0,external_fs_.statSync)(h);if(!m.isFile())return l.status(400).json({error:"\u4E0D\u662F\u6587\u4EF6"});const p=g.split("/").pop();l.setHeader("Content-Disposition",`attachment; filename*=UTF-8''${encodeURIComponent(p)}`),l.setHeader("Content-Length",m.size),(0,external_fs_.createReadStream)(h).pipe(l)}catch{l.status(404).json({error:"\u6587\u4EF6\u4E0D\u5B58\u5728"})}})),app.post("/api/files/:sessionId/upload",checkAuth,((w,l)=>{const o=manager.get(w.params.sessionId);if(!o)return l.status(404).json({error:"\u4F1A\u8BDD\u4E0D\u5B58\u5728"});const g=(w.query.path||"").replace(/\\/g,"/"),h=safePath(o.cwd,g);if(!h)return l.status(403).json({error:"\u8DEF\u5F84\u8D8A\u754C"});(0,external_fs_.mkdirSync)(h,{recursive:!0}),multer({storage:multer.diskStorage({destination:(m,p,f)=>{p.originalname=Buffer.from(p.originalname,"latin1").toString("utf8");const i=p.originalname.includes("/")?p.originalname.slice(0,p.originalname.lastIndexOf("/")):"",r=i?(0,external_path_.join)(h,i):h,s=(0,external_path_.resolve)((0,external_path_.normalize)(r)),t=(0,external_path_.resolve)((0,external_path_.normalize)(o.cwd));if(s!==t&&!s.startsWith(t+external_path_.sep))return f(new Error("\u8DEF\u5F84\u8D8A\u754C"));(0,external_fs_.mkdirSync)(r,{recursive:!0}),f(null,r)},filename:(m,p,f)=>{const i=p.originalname.includes("/")?p.originalname.slice(p.originalname.lastIndexOf("/")+1):p.originalname;f(null,i)}}),limits:{fileSize:500*1024*1024}}).array("files",100)(w,l,(m=>{if(m)return l.status(400).json({error:m.message});if(!w.files?.length)return l.status(400).json({error:"\u6CA1\u6709\u6587\u4EF6"});const p=w.files.map((f=>({name:f.originalname,size:f.size,path:(0,external_path_.resolve)(f.path).replace(/\\/g,"/")})));io.emit("files:changed",{sessionId:o.id}),l.json({ok:!0,files:p})}))})),app.delete("/api/files/:sessionId",checkAuth,((w,l)=>{const o=manager.get(w.params.sessionId);if(!o)return l.status(404).json({error:"\u4F1A\u8BDD\u4E0D\u5B58\u5728"});const g=w.query.path;if(!g)return l.status(400).json({error:"\u7F3A\u5C11 path \u53C2\u6570"});const h=safePath(o.cwd,g);if(!h)return l.status(403).json({error:"\u8DEF\u5F84\u8D8A\u754C"});if((0,external_path_.resolve)((0,external_path_.normalize)(h))===(0,external_path_.resolve)((0,external_path_.normalize)(o.cwd)))return l.status(403).json({error:"\u4E0D\u80FD\u5220\u9664\u4F1A\u8BDD\u6839\u76EE\u5F55"});try{(0,external_fs_.statSync)(h).isDirectory()?(0,external_fs_.rmSync)(h,{recursive:!0}):(0,external_fs_.unlinkSync)(h),io.emit("files:changed",{sessionId:o.id}),l.json({ok:!0})}catch(m){l.status(500).json({error:m.message})}})),app.get("/api/files/:sessionId/manifest",checkAuth,(async(w,l)=>{const o=manager.get(w.params.sessionId);if(!o)return l.status(404).json({error:"\u4F1A\u8BDD\u4E0D\u5B58\u5728"});const g=w.query.path||"",h=safePath(o.cwd,g);if(!h)return l.status(403).json({error:"\u8DEF\u5F84\u8D8A\u754C"});try{const m=walkFiles(h,""),p=[];for(const f of m){if(f.size>=50*1024*1024){p.push(f);continue}try{const i=await fileHash((0,external_path_.join)(h,f.name));p.push({...f,hash:i})}catch{p.push(f)}}l.json({ok:!0,cwd:o.cwd,path:g,files:p})}catch(m){l.status(500).json({error:m.message})}})),app.get("/api/me",checkAuth,((w,l)=>{const o=getUserInfo(w.headers.cookie);l.json({name:o?.name||"\u7528\u6237",role:o?.role||"admin",exclusive:isExclusiveMode(),hasUsers:hasUsers(),dir:o?.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,((w,l)=>{const o=loadGateway();l.json({baseUrl:o.baseUrl||"",apiKey:o.apiKey?o.apiKey.length>12?o.apiKey.slice(0,6)+"***"+o.apiKey.slice(-4):"***":"",hasKey:!!o.apiKey})})),app.post("/api/gateway",checkAdminAuth,express.json(),((w,l)=>{const{baseUrl:o,apiKey:g}=w.body||{},h=loadGateway(),m={baseUrl:typeof o=="string"?o.trim():h.baseUrl,apiKey:!g||g.includes("***")?h.apiKey:g.trim()};try{(0,external_fs_.mkdirSync)(configDir,{recursive:!0}),(0,external_fs_.writeFileSync)(gatewayFile,JSON.stringify(m,null,2),{mode:384}),l.json({ok:!0})}catch(p){l.status(500).json({error:p.message})}}));function getPkgVersion(){for(const w of["../package.json","./package.json","package.json"])try{const l=JSON.parse((0,external_fs_.readFileSync)((0,external_path_.join)(server_dirname,w),"utf8")).version;if(l&&l!=="0.0.0")return l}catch{}return"0.0.0"}const PKG_VERSION=getPkgVersion();let _latestVersion=null,_lastVersionCheck=0;const VERSION_CHECK_INTERVAL=600*1e3;async function checkLatestVersion(){const w=Date.now();if(_latestVersion&&w-_lastVersionCheck<VERSION_CHECK_INTERVAL)return _latestVersion;try{const l=await fetch("https://registry.npmjs.org/@wendongfly/myhi/latest",{headers:{Accept:"application/json"},signal:AbortSignal.timeout(8e3)});l.ok&&(_latestVersion=(await l.json()).version,_lastVersionCheck=w)}catch{}return _latestVersion}app.get("/api/version",(async(w,l)=>{const o=getPkgVersion(),g=await checkLatestVersion();l.json({current:o,latest:g||o,updateAvailable:g&&g!==o})}));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.post("/api/update",checkAuth,(async(w,l)=>{try{await execAsync(getNpmUpgradeCmd(),{encoding:"utf8",timeout:12e4}),console.log("[myhi] \u5347\u7EA7\u5B8C\u6210"),l.json({ok:!0,message:"\u5347\u7EA7\u5B8C\u6210\uFF0C\u670D\u52A1\u5C06\u81EA\u52A8\u91CD\u542F..."}),setTimeout((()=>restartService()),500)}catch(o){const g=(o.stderr||o.stdout||o.message||"").split(`
359
360
  `).filter((h=>h.trim())).slice(-5).join(`
360
361
  `);console.error("[myhi] \u5347\u7EA7\u5931\u8D25:",g),l.status(500).json({ok:!1,error:`npm install \u5931\u8D25\uFF1A
361
362
  `+g+`
@@ -367,7 +368,7 @@ Content-Length: `+C+`\r
367
368
  name = ${h}
368
369
  email = ${h}@myhi
369
370
  `;(0,external_fs_.writeFileSync)(v,b,{mode:384});try{await e(["remote","remove","origin"])}catch{}await e(["remote","add","origin",g])}if(await e(["add","-A"]),!await e(["status","--porcelain"]))return l.json({ok:!0,message:"\u6CA1\u6709\u9700\u8981\u63D0\u4EA4\u7684\u53D8\u66F4",pushed:!1});await e(["commit","-m",p]);const c=f||"main";try{await e(["push","-u","origin",c])}catch(n){if(n.message.includes("src refspec")||n.message.includes("does not match"))await e(["checkout","-b",c]),await e(["push","-u","origin",c]);else throw n}const a=await e(["log","--oneline","-1"]);l.json({ok:!0,message:`\u5DF2\u63D0\u4EA4\u5E76\u63A8\u9001: ${a}`,pushed:!0})}catch(c){l.json({ok:!1,error:c.message})}})),app.get("/api/git/info",checkAuth,((w,l)=>{const o=w.query.sessionId,g=o?manager.get(o):null;if(!g)return l.json({});const h=g.cwd||process.cwd();try{const m=(0,external_child_process_namespaceObject.execSync)("git remote get-url origin",{cwd:h,encoding:"utf8",timeout:5e3}).trim(),p=(0,external_child_process_namespaceObject.execSync)("git branch --show-current",{cwd:h,encoding:"utf8",timeout:5e3}).trim();let f="";const i=(0,external_path_.join)(h,".gitconfig");if((0,external_fs_.existsSync)(i)){const r=(0,external_fs_.readFileSync)(i,"utf8").match(/name\s*=\s*(.+)/);r&&(f=r[1].trim())}l.json({url:m,branch:p||"main",username:f})}catch{l.json({})}}));function skillFilePath(w,l){const o=l.split(":");return(0,external_path_.join)(w,...o.slice(0,-1),o[o.length-1]+".md")}app.get("/api/skills",checkAuth,((w,l)=>{const o=w.query.sessionId,h=(o?manager.get(o):null)?.cwd||process.cwd(),m=[],p=w.query.detail==="1";function f(i,r,s=""){if((0,external_fs_.existsSync)(i))try{for(const t of(0,external_fs_.readdirSync)(i,{withFileTypes:!0})){if(t.isDirectory()){f((0,external_path_.join)(i,t.name),r,s?`${s}:${t.name}`:t.name);continue}if(!t.name.endsWith(".md"))continue;const e=t.name.replace(/\.md$/,""),c=s?`${s}:${e}`:e,a=(0,external_path_.join)(i,t.name);try{const n=(0,external_fs_.readFileSync)(a,"utf8"),u=(n.split(`
370
- `).find((b=>b.trim()))||"").replace(/^#+\s*/,"").trim().slice(0,60),v={name:c,desc:u,source:r};p&&(v.content=n),m.push(v)}catch{}}}catch{}}f((0,external_path_.join)(h,".claude","commands"),"project"),f((0,external_path_.join)((0,external_os_.homedir)(),".claude","commands"),"user"),l.json({skills:m})})),app.post("/api/skills",checkAuth,express.json(),((w,l)=>{const{name:o,content:g,scope:h}=w.body||{};if(!o||!g)return l.status(400).json({error:"\u7F3A\u5C11 name \u6216 content"});if(!/^[\w:.-]+$/.test(o))return l.status(400).json({error:"\u540D\u79F0\u53EA\u80FD\u5305\u542B\u5B57\u6BCD\u3001\u6570\u5B57\u3001-\u3001_\u3001.\u3001:"});const m=w.query.sessionId,f=(m?manager.get(m):null)?.cwd||process.cwd(),i=h==="project"?(0,external_path_.join)(f,".claude","commands"):(0,external_path_.join)((0,external_os_.homedir)(),".claude","commands"),r=skillFilePath(i,o);try{(0,external_fs_.mkdirSync)((0,external_path_.dirname)(r),{recursive:!0}),(0,external_fs_.writeFileSync)(r,g,"utf8"),l.json({ok:!0,path:r})}catch(s){l.status(500).json({error:s.message})}})),app.delete("/api/skills",checkAuth,express.json(),((w,l)=>{const{name:o,scope:g}=w.body||{};if(!o)return l.status(400).json({error:"\u7F3A\u5C11 name"});const h=w.query.sessionId,p=(h?manager.get(h):null)?.cwd||process.cwd(),f=g==="project"?(0,external_path_.join)(p,".claude","commands"):(0,external_path_.join)((0,external_os_.homedir)(),".claude","commands"),i=skillFilePath(f,o);try{(0,external_fs_.existsSync)(i)&&(0,external_fs_.unlinkSync)(i),l.json({ok:!0})}catch(r){l.status(500).json({error:r.message})}})),app.post("/api/skills/scan-git",checkAuth,express.json(),((w,l)=>{const{url:o,username:g,password:h}=w.body||{};if(!o)return l.status(400).json({error:"\u7F3A\u5C11 Git \u4ED3\u5E93\u5730\u5740"});let m=o;if(g&&h&&/^https?:\/\//.test(o))try{const t=new URL(o);t.username=encodeURIComponent(g),t.password=encodeURIComponent(h),m=t.toString()}catch{}const p=(0,external_path_.join)((0,external_os_.tmpdir)(),"myhi-skill-"+Date.now());try{(0,external_child_process_namespaceObject.execSync)(`git clone --depth 1 "${m}" "${p}"`,{timeout:6e4,stdio:"pipe",encoding:"utf8",env:{...process.env,GIT_TERMINAL_PROMPT:"0"}})}catch(t){const e=(t.stderr||"").trim(),c=(t.stdout||"").trim(),d=(e||c||t.message||"\u672A\u77E5\u9519\u8BEF").split(`
371
+ `).find((b=>b.trim()))||"").replace(/^#+\s*/,"").trim().slice(0,60),v={name:c,desc:u,source:r};p&&(v.content=n),m.push(v)}catch{}}}catch{}}f((0,external_path_.join)(h,".claude","commands"),"project"),f((0,external_path_.join)((0,external_os_.homedir)(),".claude","commands"),"user"),f(__nccwpck_require__.ab+"builtin-skills","builtin"),l.json({skills:m})})),app.get("/api/skills/content",checkAuth,((w,l)=>{const{name:o,sessionId:g}=w.query;if(!o)return l.status(400).json({error:"\u7F3A\u5C11 name"});const m=(g?manager.get(g):null)?.cwd||process.cwd(),p=[{dir:(0,external_path_.join)(m,".claude","commands"),source:"project"},{dir:(0,external_path_.join)((0,external_os_.homedir)(),".claude","commands"),source:"user"},{dir:__nccwpck_require__.ab+"builtin-skills",source:"builtin"}];for(const{dir:f,source:i}of p){const r=skillFilePath(f,o);if((0,external_fs_.existsSync)(r))try{return l.json({content:(0,external_fs_.readFileSync)(r,"utf8"),source:i})}catch{}}l.status(404).json({error:"\u6280\u80FD\u4E0D\u5B58\u5728"})})),app.post("/api/skills",checkAuth,express.json(),((w,l)=>{const{name:o,content:g,scope:h}=w.body||{};if(!o||!g)return l.status(400).json({error:"\u7F3A\u5C11 name \u6216 content"});if(!/^[\w:.-]+$/.test(o))return l.status(400).json({error:"\u540D\u79F0\u53EA\u80FD\u5305\u542B\u5B57\u6BCD\u3001\u6570\u5B57\u3001-\u3001_\u3001.\u3001:"});const m=w.query.sessionId,f=(m?manager.get(m):null)?.cwd||process.cwd(),i=h==="project"?(0,external_path_.join)(f,".claude","commands"):(0,external_path_.join)((0,external_os_.homedir)(),".claude","commands"),r=skillFilePath(i,o);try{(0,external_fs_.mkdirSync)((0,external_path_.dirname)(r),{recursive:!0}),(0,external_fs_.writeFileSync)(r,g,"utf8"),l.json({ok:!0,path:r})}catch(s){l.status(500).json({error:s.message})}})),app.delete("/api/skills",checkAuth,express.json(),((w,l)=>{const{name:o,scope:g}=w.body||{};if(!o)return l.status(400).json({error:"\u7F3A\u5C11 name"});const h=w.query.sessionId,p=(h?manager.get(h):null)?.cwd||process.cwd(),f=g==="project"?(0,external_path_.join)(p,".claude","commands"):(0,external_path_.join)((0,external_os_.homedir)(),".claude","commands"),i=skillFilePath(f,o);try{(0,external_fs_.existsSync)(i)&&(0,external_fs_.unlinkSync)(i),l.json({ok:!0})}catch(r){l.status(500).json({error:r.message})}})),app.post("/api/skills/scan-git",checkAuth,express.json(),((w,l)=>{const{url:o,username:g,password:h}=w.body||{};if(!o)return l.status(400).json({error:"\u7F3A\u5C11 Git \u4ED3\u5E93\u5730\u5740"});let m=o;if(g&&h&&/^https?:\/\//.test(o))try{const t=new URL(o);t.username=encodeURIComponent(g),t.password=encodeURIComponent(h),m=t.toString()}catch{}const p=(0,external_path_.join)((0,external_os_.tmpdir)(),"myhi-skill-"+Date.now());try{(0,external_child_process_namespaceObject.execSync)(`git clone --depth 1 "${m}" "${p}"`,{timeout:6e4,stdio:"pipe",encoding:"utf8",env:{...process.env,GIT_TERMINAL_PROMPT:"0"}})}catch(t){const e=(t.stderr||"").trim(),c=(t.stdout||"").trim(),d=(e||c||t.message||"\u672A\u77E5\u9519\u8BEF").split(`
371
372
  `).filter((u=>u.trim())).slice(-3).join(" ").slice(0,200);return l.status(400).json({error:"\u514B\u9686\u5931\u8D25: "+d})}const f=[];function i(t,e=""){if((0,external_fs_.existsSync)(t))try{for(const c of(0,external_fs_.readdirSync)(t,{withFileTypes:!0})){if(c.name.startsWith("."))continue;if(c.isDirectory()){i((0,external_path_.join)(t,c.name),e?`${e}:${c.name}`:c.name);continue}if(!c.name.endsWith(".md")||["README.MD","CHANGELOG.MD","LICENSE.MD","CONTRIBUTING.MD"].includes(c.name.toUpperCase()))continue;const a=c.name.replace(/\.md$/,""),n=e?`${e}:${a}`:a,d=(0,external_path_.join)(t,c.name);try{const u=(0,external_fs_.readFileSync)(d,"utf8"),b=(u.split(`
372
373
  `).find((_=>_.trim()))||"").replace(/^#+\s*/,"").trim().slice(0,80);f.push({name:n,desc:b,content:u})}catch{}}}catch{}}const r=(0,external_path_.join)(p,"commands"),s=(0,external_path_.join)(p,".claude","commands");(0,external_fs_.existsSync)(r)?i(r):(0,external_fs_.existsSync)(s)?i(s):i(p);try{(0,external_child_process_namespaceObject.execSync)(process.platform==="win32"?`rmdir /s /q ${JSON.stringify(p)}`:`rm -rf ${JSON.stringify(p)}`,{timeout:1e4,stdio:"pipe"})}catch{}if(!f.length)return l.json({skills:[],message:"\u4ED3\u5E93\u4E2D\u672A\u627E\u5230\u6280\u80FD\u6587\u4EF6(.md)"});l.json({skills:f})})),app.get("/api/memory",checkAuth,((w,l)=>{const o=w.query.sessionId,g=o?manager.get(o):null;if(!g)return l.json({files:[]});const h=g.cwd||process.cwd(),m=[],p=(0,external_path_.join)(h,"CLAUDE.md");if((0,external_fs_.existsSync)(p))try{m.push({name:"CLAUDE.md",content:(0,external_fs_.readFileSync)(p,"utf8")})}catch{}const f=(0,external_path_.join)(h,".claude","settings.json");if((0,external_fs_.existsSync)(f))try{m.push({name:".claude/settings.json",content:(0,external_fs_.readFileSync)(f,"utf8")})}catch{}const i=(0,external_path_.join)((0,external_os_.homedir)(),".claude","CLAUDE.md");if((0,external_fs_.existsSync)(i))try{m.push({name:"~/.claude/CLAUDE.md",content:(0,external_fs_.readFileSync)(i,"utf8")})}catch{}const r=[(0,external_path_.join)((0,external_os_.homedir)(),".claude","memory")],s=(0,external_path_.join)((0,external_os_.homedir)(),".claude","projects");if((0,external_fs_.existsSync)(s))try{for(const t of(0,external_fs_.readdirSync)(s)){const e=(0,external_path_.join)(s,t,"memory");if((0,external_fs_.existsSync)(e)){const c=(0,external_path_.join)(s,t,"CLAUDE.md");if((0,external_fs_.existsSync)(c))try{m.push({name:`.claude/projects/${t}/CLAUDE.md`,content:(0,external_fs_.readFileSync)(c,"utf8")})}catch{}try{for(const a of(0,external_fs_.readdirSync)(e)){if(!a.endsWith(".md"))continue;const n=(0,external_path_.join)(e,a);try{m.push({name:`memory/${a}`,content:(0,external_fs_.readFileSync)(n,"utf8")})}catch{}}}catch{}}}}catch{}for(const t of r)if((0,external_fs_.existsSync)(t))try{for(const e of(0,external_fs_.readdirSync)(t)){if(!e.endsWith(".md"))continue;const c=(0,external_path_.join)(t,e);try{m.push({name:`~/.claude/memory/${e}`,content:(0,external_fs_.readFileSync)(c,"utf8")})}catch{}}}catch{}l.json({files:m})}));const _adminTokens=new Set,SERVER_START_TIME=Date.now();function checkAdminAuth(w,l,o){const g=w.headers.authorization;if(g&&g.startsWith("Bearer ")&&_adminTokens.has(g.slice(7)))return o();l.status(401).json({error:"\u672A\u6388\u6743"})}app.post("/api/admin/login",express.json(),((w,l)=>{const{password:o}=w.body||{};if(o===PASSWORD){const g=(0,external_crypto_.randomBytes)(16).toString("hex");return _adminTokens.add(g),l.json({ok:!0,token:g})}l.json({ok:!1,error:"\u5BC6\u7801\u9519\u8BEF"})})),app.get("/api/admin/status",checkAdminAuth,(async(w,l)=>{const o=await checkLatestVersion();loadUsers();const g=[],h=new Set;for(const[,p]of io.sockets.sockets)!p.data.userName||h.has(p.data.userName)||(h.add(p.data.userName),g.push({name:p.data.userName,role:p.data.role||"viewer"}));const m=manager.listSessions().filter((p=>p.alive!==!1)).map((p=>({id:p.id,title:p.title,mode:p.mode||"pty",owner:p.owner,viewers:p.viewerCount||0})));l.json({version:{current:getPkgVersion(),latest:o||getPkgVersion(),updateAvailable:o&&o!==getPkgVersion()},onlineUsers:g,sessions:m,users:listUsers(),uptime:Math.floor((Date.now()-SERVER_START_TIME)/1e3)})})),app.post("/api/admin/upgrade",checkAdminAuth,(async(w,l)=>{try{await execAsync(getNpmUpgradeCmd(),{encoding:"utf8",timeout:12e4}),console.log("[myhi] \u5347\u7EA7\u5B8C\u6210"),l.json({ok:!0,message:"\u5347\u7EA7\u5B8C\u6210\uFF0C\u670D\u52A1\u5C06\u81EA\u52A8\u91CD\u542F..."}),setTimeout((()=>restartService()),500)}catch(o){const g=(o.stderr||o.stdout||o.message||"").split(`
373
374
  `).filter((h=>h.trim())).slice(-5).join(`
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wendongfly/myhi",
3
- "version": "1.3.53",
3
+ "version": "1.3.55",
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",