@wendongfly/myhi 1.0.98 → 1.0.100
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/bin/daemon.js +1 -1
- package/dist/index.js +1 -1
- package/dist/package.json +1 -1
- package/package.json +1 -1
package/bin/daemon.js
CHANGED
|
@@ -88,7 +88,7 @@ async function doUpgrade() {
|
|
|
88
88
|
log('收到升级请求,执行 npm install...');
|
|
89
89
|
try {
|
|
90
90
|
const cmd = process.platform === 'win32'
|
|
91
|
-
? 'npm install -g @wendongfly/myhi@latest'
|
|
91
|
+
? 'npm.cmd install -g @wendongfly/myhi@latest'
|
|
92
92
|
: 'sudo npm install -g @wendongfly/myhi@latest 2>&1 || npm install -g @wendongfly/myhi@latest 2>&1';
|
|
93
93
|
const output = execSync(cmd, { timeout: 120000, encoding: 'utf8' });
|
|
94
94
|
log('升级完成: ' + output.trim().split('\n').pop());
|
package/dist/index.js
CHANGED
|
@@ -359,7 +359,7 @@ Content-Length: `+T+`\r
|
|
|
359
359
|
`).filter(Boolean).slice(-20);for(const l of h)try{const d=JSON.parse(l);if(d.type==="human"&&d.message?.content){const a=typeof d.message.content=="string"?d.message.content:d.message.content.find(n=>n.type==="text")?.text||"";a&&this._history.push({type:"user",content:a,timestamp:d.timestamp})}else d.type==="assistant"&&d.message?.content?this._history.push({type:"assistant",message:d.message,timestamp:d.timestamp}):d.type==="result"&&this._history.push({type:"result",total_cost_usd:d.costUSD||d.total_cost_usd,timestamp:d.timestamp})}catch{}return}}catch(p){console.warn("[agent] \u52A0\u8F7D\u6062\u590D\u4F1A\u8BDD\u5386\u53F2\u5931\u8D25:",p.message)}}get isBusy(){return this._busy}addViewer(p,o){this._viewers.add(p),o&&this._viewerNames.set(p,o)}removeViewer(p){this._viewers.delete(p),this._viewerNames.delete(p)}get viewerCount(){return new Set(this._viewerNames.values()).size||this._viewers.size}takeControl(p,o){this.controlHolder=p,this.controlHolderName=o||null,this.lastInputTime=Date.now()}releaseControl(){this.controlHolder=null,this.controlHolderName=null,this.lastInputTime=null}isController(p){return this.controlHolder===p}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(_){const p=Date.now();if(_claudeSessionsCache&&p-_claudeSessionsCacheTime<CLAUDE_SESSIONS_CACHE_TTL){if(!_)return _claudeSessionsCache;const h=(0,external_path_.resolve)((0,external_path_.normalize)(_)).replace(/[\\/]+$/,"");return _claudeSessionsCache.filter(l=>(0,external_path_.resolve)((0,external_path_.normalize)(l.projectPath)).replace(/[\\/]+$/,"").startsWith(h))}const o=[],v=_?(0,external_path_.resolve)((0,external_path_.normalize)(_)).replace(/[\\/]+$/,""):null;try{const h=(0,external_path_.join)((0,external_os_.homedir)(),".claude","projects");if(!(0,external_fs_.existsSync)(h))return o;for(const l of(0,external_fs_.readdirSync)(h,{withFileTypes:!0})){if(!l.isDirectory())continue;const d=(0,external_path_.join)(h,l.name);try{for(const a of(0,external_fs_.readdirSync)(d)){if(!a.endsWith(".jsonl"))continue;const n=a.replace(".jsonl",""),r=(0,external_path_.join)(d,a);try{const t=(0,external_fs_.readFileSync)(r,"utf8").split(`
|
|
360
360
|
`).filter(Boolean);let e=null,c=null,s=0,i="";for(const u of t)try{const g=JSON.parse(u);if(e||(e=g),c=g,s++,!i&&g.type==="human"&&g.message?.content){const b=g.message.content;if(typeof b=="string")i=b.slice(0,120);else if(Array.isArray(b)){const w=b.find(E=>E.type==="text");w&&(i=w.text?.slice(0,120)||"")}}}catch{}let f=l.name;if(process.platform==="win32"&&/^[A-Za-z]--/.test(f)?f=f[0]+":\\"+f.slice(3).replace(/-/g,"\\"):f.startsWith("-")&&(f=f.replace(/-/g,"/")),v&&!(0,external_path_.resolve)((0,external_path_.normalize)(f)).replace(/[\\/]+$/,"").startsWith(v))continue;o.push({sessionId:n,projectDir:l.name,projectPath:f,messageCount:s,summary:i,createdAt:e?.timestamp||null,updatedAt:c?.timestamp||null})}catch{}}}catch{}}}catch{}o.sort((h,l)=>new Date(l.updatedAt||0).getTime()-new Date(h.updatedAt||0).getTime());const m=o.slice(0,50);return _claudeSessionsCache=m,_claudeSessionsCacheTime=Date.now(),m.slice(0,20)}const DEFAULT_SHELL=process.platform==="win32"?process.env.SHELL||"powershell.exe":process.env.SHELL||"/bin/bash",CONFIG_DIR=(0,external_path_.join)((0,external_os_.homedir)(),".myhi"),SESSIONS_FILE=(0,external_path_.join)(CONFIG_DIR,"sessions.json");function loadSavedConfigs(){try{return JSON.parse((0,external_fs_.readFileSync)(SESSIONS_FILE,"utf8"))}catch{return[]}}function writeSavedConfigs(_){try{(0,external_fs_.mkdirSync)(CONFIG_DIR,{recursive:!0});const p=SESSIONS_FILE+".tmp";(0,external_fs_.writeFileSync)(p,JSON.stringify(_,null,2),{mode:384}),(0,external_fs_.renameSync)(p,SESSIONS_FILE)}catch(p){console.error("[sessions] \u6301\u4E45\u5316\u5199\u5165\u5931\u8D25:",p.message)}}class Session extends external_events_.EventEmitter{constructor(p,o={}){super(),this.id=p,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.permissionMode=o.permissionMode||"default",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 v={...process.env};if(delete v.CLAUDECODE,delete v.CLAUDE_CODE_ENTRYPOINT,delete v.CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC,o.userDir){const h=(0,external_path_.join)(o.userDir,".gitconfig");(0,external_fs_.existsSync)(h)&&(v.GIT_CONFIG_GLOBAL=h)}this._pty=external_node_pty_namespaceObject.spawn(DEFAULT_SHELL,[],{name:"xterm-256color",cols:this.cols,rows:this.rows,cwd:this.cwd,env:v,useConpty:!1}),this._scrollback="";const m=100*1024;if(this._pty.onData(h=>{this.emit("data",h),this._scrollback+=h,this._scrollback.length>m&&(this._scrollback=this._scrollback.slice(this._scrollback.length-m))}),this._pty.onExit(({exitCode:h})=>{this.exitCode=h,this.emit("exit",h)}),o.initCmd){const h=o.initCmd;let l=!1;const d=a=>{!l&&/[$>#\]]\s*$/.test(a.trimEnd())&&(l=!0,this._pty.off("data",d),setTimeout(()=>this._pty.write(h+"\r"),120))};this._pty.onData(d),setTimeout(()=>{l||(l=!0,this._pty.write(h+"\r"))},3e3)}}write(p){p!=null&&this._pty.write(p)}resize(p,o){this.cols=p,this.rows=o,this._pty.resize(p,o)}kill(){try{this._pty.kill()}catch{}}addViewer(p,o){this._viewers.add(p),o&&this._viewerNames.set(p,o)}removeViewer(p){this._viewers.delete(p),this._viewerNames.delete(p)}get viewerCount(){return new Set(this._viewerNames.values()).size||this._viewers.size}takeControl(p,o){this.controlHolder=p,this.controlHolderName=o||null,this.lastInputTime=Date.now()}releaseControl(){this.controlHolder=null,this.controlHolderName=null,this.lastInputTime=null}isController(p){return this.controlHolder===p}toJSON(){return{id:this.id,title:this.title,cwd:this.cwd,createdAt:this.createdAt,cols:this.cols,rows:this.rows,viewers:this.viewerCount,alive:this.exitCode===void 0,controlHolder:this.controlHolder,controlHolderName:this.controlHolderName,permissionMode:this.permissionMode,owner:this.owner}}}class SessionManager{constructor(){this._sessions=new Map,this._agentSessions=new Map;for(const p of loadSavedConfigs())try{if(p.mode==="agent"){if(p.claudeSessionId){const o=(0,external_crypto_.randomUUID)(),v=new AgentSession(o,{...p,resumeSessionId:p.claudeSessionId});v.on("session-id",()=>this._persist()),this._agentSessions.set(o,v),console.log(`[\u4F1A\u8BDD] \u6062\u590D Agent "${p.title}" (claude: ${p.claudeSessionId.slice(0,8)}...)`)}}else this._spawn((0,external_crypto_.randomUUID)(),p,!1)}catch(o){console.warn(`[\u4F1A\u8BDD] \u6062\u590D "${p.title}" \u5931\u8D25:`,o.message)}}_spawn(p,o,v=!0){const m=new Session(p,o);return this._sessions.set(p,m),m.on("exit",()=>{setTimeout(()=>this._sessions.delete(p),300*1e3)}),v&&this._persist(),m}create(p={}){return this._spawn((0,external_crypto_.randomUUID)(),p,!0)}createAgent(p={}){const o=(0,external_crypto_.randomUUID)(),v=new AgentSession(o,p);return this._agentSessions.set(o,v),v.on("session-id",()=>this._persist()),this._persist(),v}get(p){return this._sessions.get(p)||this._agentSessions.get(p)}list(p,o=!1){let v=[...this._sessions.values()].map(h=>({...h.toJSON(),mode:"pty"})),m=[...this._agentSessions.values()].map(h=>h.toJSON());return p&&!o&&(v=v.filter(h=>!h.owner||h.owner===p),m=m.filter(h=>!h.owner||h.owner===p)),[...v,...m]}listSessions(){return[...this._sessions.values(),...this._agentSessions.values()]}kill(p){const o=this._sessions.get(p);if(o){o.kill(),this._sessions.delete(p),this._persist();return}const v=this._agentSessions.get(p);v&&(v.kill(),this._agentSessions.delete(p),this._persist())}_persist(){const p=[...this._sessions.values()].filter(v=>v.exitCode===void 0).map(v=>({mode:"pty",title:v.title,cwd:v.cwd,initCmd:v.initCmd||void 0,permissionMode:v.permissionMode||void 0,owner:v.owner||void 0})),o=[...this._agentSessions.values()].filter(v=>v.alive).map(v=>({mode:"agent",title:v.title,cwd:v.cwd,permissionMode:v.permissionMode||void 0,owner:v.owner||void 0,userDir:v.userDir||void 0,claudeSessionId:v.claudeSessionId||void 0}));writeSavedConfigs([...p,...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;function loadRoles(){try{if((0,external_fs_.existsSync)(ROLES_FILE))return rolesConfig=JSON.parse((0,external_fs_.readFileSync)(ROLES_FILE,"utf8")),rolesConfig}catch(_){console.warn("[\u89D2\u8272] \u52A0\u8F7D roles.json \u5931\u8D25:",_.message)}return rolesConfig=null,null}function lookupPassword(_){if(!rolesConfig?.passwords)return null;const p=rolesConfig.passwords[_];return p?{name:p.name||"\u7528\u6237",role:p.role||"viewer"}:null}function hasPermission(_,p){return(ROLE_LEVELS[_]||0)>=(ROLE_LEVELS[p]||0)}function isMultiUserMode(){return rolesConfig!==null&&rolesConfig.passwords&&Object.keys(rolesConfig.passwords).length>0}function getDefaultRole(){return rolesConfig?.defaultRole||"admin"}let usersConfig=null;function loadUsers(){try{if((0,external_fs_.existsSync)(USERS_FILE))return usersConfig=JSON.parse((0,external_fs_.readFileSync)(USERS_FILE,"utf8")),usersConfig}catch(_){console.warn("[\u7528\u6237] \u52A0\u8F7D users.json \u5931\u8D25:",_.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(_){usersConfig||(usersConfig={users:{}}),usersConfig.exclusive=!!_,_writeUsers()}function lookupUser(_){if(!usersConfig?.users)return null;const p=usersConfig.users[_];return p?{name:p.name||"\u7528\u6237",dir:p.dir}:null}function listUsers(){return usersConfig?.users?Object.entries(usersConfig.users).map(([_,p])=>({password:_.slice(0,2)+"*".repeat(_.length-2),name:p.name,dir:p.dir})):[]}function addUser(_,p,o){usersConfig||(usersConfig={users:{}}),usersConfig.users||(usersConfig.users={}),usersConfig.users[_]={name:p,dir:o},_writeUsers()}function removeUser(_){return!usersConfig?.users||!usersConfig.users[_]?!1:(delete usersConfig.users[_],_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 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});const pidFile=(0,external_path_.join)(configDir,"daemon.pid"),IS_DAEMON_CHILD=!!process.env.MYHI_DAEMON;(function _(){if(!IS_DAEMON_CHILD){try{const p=parseInt((0,external_fs_.readFileSync)(pidFile,"utf8").trim(),10);if(p&&p!==process.pid){let o=!1;try{process.kill(p,0),o=!0}catch{}if(o){console.log(`[myhi] \u68C0\u6D4B\u5230\u5DF2\u8FD0\u884C\u7684\u5B9E\u4F8B (PID ${p})\uFF0C\u6B63\u5728\u505C\u6B62...`);try{process.platform==="win32"?(0,external_child_process_namespaceObject.execSync)(`taskkill /PID ${p} /F /T`,{stdio:"pipe",timeout:5e3}):process.kill(p,"SIGTERM")}catch(h){console.warn(`[myhi] \u505C\u6B62\u65E7\u8FDB\u7A0B\u5931\u8D25: ${h.message?.split(`
|
|
361
361
|
`)[0]||h}`)}const v=Date.now()+5e3;for(;Date.now()<v;){try{process.kill(p,0)}catch{break}const h=Date.now()+200;for(;Date.now()<h;);}let m=!1;try{process.kill(p,0),m=!0}catch{}m&&(console.error(`[myhi] \u65E0\u6CD5\u505C\u6B62\u65E7\u8FDB\u7A0B (PID ${p})\uFF0C\u8BF7\u624B\u52A8\u6267\u884C\uFF1A`),console.error(" myhi stop"),console.error(` \u6216: ${process.platform==="win32"?`taskkill /PID ${p} /F`:`kill -9 ${p}`}`),process.exit(1))}}}catch{}IS_DAEMON_CHILD||(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")for(const _ of manager.listSessions())try{_.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",_=>{console.error("[myhi] \u672A\u6355\u83B7\u5F02\u5E38:",_.message),gracefulShutdown(),process.exit(1)}),process.on("unhandledRejection",_=>{console.error("[myhi] \u672A\u5904\u7406\u7684 Promise \u62D2\u7EDD:",_)});function readOrCreate(_,p){try{if((0,external_fs_.existsSync)(_))return(0,external_fs_.readFileSync)(_,"utf8").trim()}catch{}const o=p();return(0,external_fs_.writeFileSync)(_,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 _=Date.now();for(const[p,o]of userSessions)o.permanent||(!o.lastUsed||_-o.lastUsed>SESSION_MAX_AGE)&&userSessions.delete(p)},3600*1e3);function getTailscaleIP(){try{return(0,external_child_process_namespaceObject.execSync)("tailscale ip -4",{timeout:3e3,stdio:"pipe"}).toString().trim().split(`
|
|
362
|
-
`)[0]}catch{}try{if(process.platform==="win32"){const p=(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(p)return p[1]}else try{const p=(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(p)return p[1]}catch{const _=(0,external_child_process_namespaceObject.execSync)("route -n get default 2>/dev/null || route -n 2>/dev/null",{timeout:3e3,stdio:"pipe"}).toString(),p=_.match(/interface:\s*(\S+)/i)||_.match(/0\.0\.0\.0\s+(\d+\.\d+\.\d+\.\d+)/);if(p){const o=(0,external_os_.networkInterfaces)()[p[1]];if(o){const v=o.find(m=>m.family==="IPv4"&&!m.internal);if(v)return v.address}if(/^\d+\.\d+\.\d+\.\d+$/.test(p[1]))return p[1]}}}catch{}for(const _ of Object.values((0,external_os_.networkInterfaces)()))for(const p of _)if(p.family==="IPv4"&&!p.internal)return p.address;return"localhost"}function parseCookies(_=""){const p={};for(const o of _.split(";")){const v=o.indexOf("=");v>0&&(p[o.slice(0,v).trim()]=o.slice(v+1).trim())}return p}const SESSION_COOKIE="myhi_sid",COOKIE_OPTS="HttpOnly; SameSite=Strict; Path=/; Max-Age=31536000";function setSessionCookie(_,p){_.setHeader("Set-Cookie",`${SESSION_COOKIE}=${p||TOKEN}; ${COOKIE_OPTS}`)}function getSessionToken(_){return parseCookies(_)[SESSION_COOKIE]||null}function hasValidSession(_){const p=getSessionToken(_);if(p&&userSessions.has(p)){const o=userSessions.get(p);return o&&(o.lastUsed=Date.now()),!0}return!1}function getUserInfo(_){const p=getSessionToken(_);return p?userSessions.get(p):null}const app=express();app.set("trust proxy",!0),app.use((_,p,o)=>{p.setHeader("Referrer-Policy","no-referrer"),p.setHeader("X-Content-Type-Options","nosniff"),o()});const httpServer=(0,external_http_.createServer)(app),_corsAllowed=/^https?:\/\/(localhost|127\.0\.0\.1|\[::1\]|10\.\d+\.\d+\.\d+|172\.(1[6-9]|2\d|3[01])\.\d+\.\d+|192\.168\.\d+\.\d+|100\.(6[4-9]|[7-9]\d|1[01]\d|12[0-7])\.\d+\.\d+)(:\d+)?$/,io=new Server({cors:{origin:(_,p)=>{if(!_||_corsAllowed.test(_))return p(null,!0);p(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);const manager=new SessionManager,AUTO_ATTACH=process.env.MYHI_AUTO_ATTACH!=="0",_autoSpawned=new Set;function spawnLocalTerminal(_,p){if(!AUTO_ATTACH||_autoSpawned.has(_)||!process.env.DISPLAY&&process.platform==="linux")return;_autoSpawned.add(_);const o=__nccwpck_require__.ab+"attach.js",v=process.execPath,m=p||"myhi";if(process.platform==="win32"){const h=`"${v}" "${o}" ${_}`,l=(0,external_child_process_namespaceObject.spawn)("wt.exe",["-w","0","new-tab","--title",m,"--","cmd","/k",h],{detached:!0,stdio:"ignore"});l.unref(),l.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",`"${m}"`,"cmd","/k",h],{detached:!0,stdio:"ignore"}).unref()})}else if(process.platform==="darwin"){const h=`tell application "Terminal" to do script "${v} ${o} ${_}"`;(0,external_child_process_namespaceObject.spawn)("osascript",["-e",h],{detached:!0,stdio:"ignore"}).unref()}else{const h=`${v} "${o}" ${_}`;for(const[l,d]of[["gnome-terminal",["--","bash","-c",`${h}; exec bash`]],["xterm",["-title",m,"-e",h]],["konsole",["--new-tab","-e",h]]]){const a=(0,external_child_process_namespaceObject.spawn)(l,d,{detached:!0,stdio:"ignore"});a.unref(),a.on("error",()=>{});break}}}const uploadDir=(0,external_path_.join)(configDir,"uploads");(0,external_fs_.mkdirSync)(uploadDir,{recursive:!0});function checkAuth(_,p,o){const v=_.query.token;if(v&&(v===TOKEN||userSessions.has(v))){const m=userSessions.get(v);if(m?.onetime){userSessions.delete(v);const l=(0,external_crypto_.randomBytes)(16).toString("hex");userSessions.set(l,{role:m.role,name:m.name,lastUsed:Date.now()}),setSessionCookie(p,l)}else setSessionCookie(p,v===TOKEN?TOKEN:v);const h=_.path+(Object.keys(_.query).filter(l=>l!=="token").length?"?"+new URLSearchParams(Object.fromEntries(Object.entries(_.query).filter(([l])=>l!=="token"))):"");return p.redirect(h)}if(hasValidSession(_.headers.cookie)){if(isExclusiveMode()&&activeUser){const m=getSessionToken(_.headers.cookie);if(m!==TOKEN){const h=userSessions.get(m);if(!h||h.name!==activeUser.name)return p.setHeader("Set-Cookie",`${SESSION_COOKIE}=; HttpOnly; SameSite=Strict; Path=/; Max-Age=0`),p.redirect("/login")}}return o()}p.redirect("/login")}const _require=(0,external_module_namespaceObject.createRequire)(import.meta.url);function pkgDirSafe(_){try{return(0,external_path_.dirname)(_require.resolve(`${_}/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));const _loginAttempts=new Map,LOGIN_MAX_ATTEMPTS=5,LOGIN_LOCKOUT_MS=300*1e3;function checkLoginRate(_){const p=Date.now(),o=_loginAttempts.get(_);return!o||p>o.resetAt?!0:o.count<LOGIN_MAX_ATTEMPTS}function recordLoginFailure(_){const p=Date.now(),o=_loginAttempts.get(_);!o||p>o.resetAt?_loginAttempts.set(_,{count:1,resetAt:p+LOGIN_LOCKOUT_MS}):o.count++}function clearLoginFailures(_){_loginAttempts.delete(_)}setInterval(()=>{const _=Date.now();for(const[p,o]of _loginAttempts)_>o.resetAt&&_loginAttempts.delete(p)},600*1e3),app.get("/login",(_,p)=>{p.sendFile(__nccwpck_require__.ab+"login.html")}),app.post("/login",express.urlencoded({extended:!1}),(_,p)=>{const o=_.ip;if(!checkLoginRate(o))return p.redirect("/login?error=locked");loadRoles(),loadUsers();const v=_.body?.password,m=_.query.from,h=m&&m.startsWith("/")&&!m.startsWith("//")?m:"/";if(hasUsers()){const d=lookupUser(v);if(!d)return recordLoginFailure(o),p.redirect("/login?error=1");if(isExclusiveMode()&&activeUser&&activeUser.name!==d.name)return p.redirect("/login?error=occupied&user="+encodeURIComponent(activeUser.name));clearLoginFailures(o);const a=(0,external_crypto_.randomBytes)(16).toString("hex");return userSessions.set(a,{role:"admin",name:d.name,dir:d.dir,lastUsed:Date.now()}),isExclusiveMode()&&(activeUser?activeUser.tokens.add(a):activeUser={name:d.name,dir:d.dir,tokens:new Set([a]),loginAt:Date.now()}),setSessionCookie(p,a),console.log(`[\u767B\u5F55] ${d.name} \u767B\u5F55\uFF08${isExclusiveMode()?"\u72EC\u5360":"\u5171\u4EAB"}\u6A21\u5F0F\uFF09`),p.redirect(h)}const l=lookupPassword(v);if(l){clearLoginFailures(o);const d=(0,external_crypto_.randomBytes)(16).toString("hex");return userSessions.set(d,{role:l.role,name:l.name,lastUsed:Date.now()}),setSessionCookie(p,d),p.redirect(h)}if(v===PASSWORD){clearLoginFailures(o);const d=(0,external_crypto_.randomBytes)(16).toString("hex");return userSessions.set(d,{role:"admin",name:"\u7BA1\u7406\u5458",lastUsed:Date.now()}),setSessionCookie(p,d),p.redirect(h)}recordLoginFailure(o),p.redirect("/login?error=1")}),app.post("/logout",(_,p)=>{const o=getSessionToken(_.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)),p.setHeader("Set-Cookie",`${SESSION_COOKIE}=; HttpOnly; SameSite=Strict; Path=/; Max-Age=0`),p.redirect("/login")}),app.get("/api/status",(_,p)=>{if(isExclusiveMode()&&activeUser)return p.json({occupied:!0,userName:activeUser.name,releasing:!!_exclusiveReleaseTimer});if(hasUsers()&&!isExclusiveMode()){const o=new Set;for(const[,v]of io.sockets.sockets){if(!v.data.userName||v.data.userName==="\u7BA1\u7406\u5458")continue;const m=v.handshake.headers.cookie;m&&!hasValidSession(m)||o.add(v.data.userName)}return p.json({occupied:!1,onlineUsers:[...o]})}p.json({occupied:!1})}),app.get("/admin",(_,p)=>p.sendFile(__nccwpck_require__.ab+"admin.html")),app.get("/",checkAuth,(_,p)=>p.sendFile(__nccwpck_require__.ab+"index.html")),app.get("/terminal/:id",checkAuth,(_,p)=>p.sendFile(__nccwpck_require__.ab+"chat.html")),app.get("/terminal-raw/:id",checkAuth,(_,p)=>p.sendFile(__nccwpck_require__.ab+"terminal.html")),app.use("/lib",express.static(__nccwpck_require__.ab+"lib",staticOpts)),app.get("/api/sessions",checkAuth,(_,p)=>{const o=getUserInfo(_.headers.cookie);p.json(manager.list(o?.name,o?.role==="admin"))}),app.get("/api/usage",(_,p)=>{try{const o=manager.listSessions().filter(m=>m.mode==="agent"&&m.alive).map(m=>({id:m.id,title:m.title,owner:m.owner,usage:m._usage}));let v=null;for(const m of o)if(m.usage?.rateLimitInfo){v=m.usage.rateLimitInfo;break}p.json({rateLimit:v,activeSessions:o})}catch(o){p.status(500).json({error:o.message})}}),app.post("/api/sessions",checkAuth,express.json(),(_,p)=>{const o=getUserInfo(_.headers.cookie),v=manager.create({..._.body,owner:o?.name,userDir:o?.dir});p.status(201).json(v.toJSON())}),app.delete("/api/sessions/:id",checkAuth,(_,p)=>{manager.kill(_.params.id),p.status(204).end()}),app.get("/api/claude-sessions",checkAuth,(_,p)=>{const o=getUserInfo(_.headers.cookie);p.json(listLocalClaudeSessions(o?.dir))}),app.get("/api/dirs",checkAuth,(_,p)=>{const o=getUserInfo(_.headers.cookie);if(!o||o.role!=="admin")return p.status(403).json({error:"\u6CA1\u6709\u76EE\u5F55\u6D4F\u89C8\u6743\u9650"});const v=o.dir?(0,external_path_.resolve)((0,external_path_.normalize)(o.dir)):null,m=process.platform==="win32"?"\\":"/";let h=_.query.path||v||process.env.MYHI_CWD||(0,external_os_.homedir)(),l=(0,external_path_.resolve)((0,external_path_.normalize)(h)).replace(/[/\\]+$/,"");process.platform==="win32"&&/^[A-Za-z]:$/.test(l)&&(l+="\\"),v&&!l.startsWith(v)&&(l=v);try{const d=(0,external_fs_.readdirSync)(l,{withFileTypes:!0}).filter(r=>r.isDirectory()&&!r.name.startsWith(".")).map(r=>({name:r.name,path:l.replace(/[/\\]+$/,"")+m+r.name})).sort((r,t)=>r.name.localeCompare(t.name)),a=(0,external_path_.join)(l,".."),n=!v||(0,external_path_.resolve)((0,external_path_.normalize)(a)).startsWith(v);p.json({current:l,parent:n&&a!==l?a:null,dirs:d})}catch(d){p.status(400).json({error:d.message})}}),app.get("/api/me",checkAuth,(_,p)=>{const o=getUserInfo(_.headers.cookie);p.json({name:o?.name||"\u7528\u6237",exclusive:isExclusiveMode(),hasUsers:hasUsers(),dir:o?.dir||null})});const PKG_VERSION=(()=>{try{return JSON.parse((0,external_fs_.readFileSync)(__nccwpck_require__.ab+"package.json","utf8")).version}catch{return"0.0.0"}})();let _latestVersion=null,_lastVersionCheck=0;const VERSION_CHECK_INTERVAL=600*1e3;async function checkLatestVersion(){const _=Date.now();if(_latestVersion&&_-_lastVersionCheck<VERSION_CHECK_INTERVAL)return _latestVersion;try{const p=await fetch("https://registry.npmjs.org/@wendongfly/myhi/latest",{headers:{Accept:"application/json"},signal:AbortSignal.timeout(8e3)});p.ok&&(_latestVersion=(await p.json()).version,_lastVersionCheck=_)}catch{}return _latestVersion}app.get("/api/version",async(_,p)=>{const o=await checkLatestVersion();p.json({current:PKG_VERSION,latest:o||PKG_VERSION,updateAvailable:o&&o!==PKG_VERSION})});const _adminTokens=new Set,SERVER_START_TIME=Date.now();function checkAdminAuth(_,p,o){const v=_.headers.authorization;if(v&&v.startsWith("Bearer ")&&_adminTokens.has(v.slice(7)))return o();p.status(401).json({error:"\u672A\u6388\u6743"})}app.post("/api/admin/login",express.json(),(_,p)=>{const{password:o}=_.body||{};if(o===PASSWORD){const v=(0,external_crypto_.randomBytes)(16).toString("hex");return _adminTokens.add(v),p.json({ok:!0,token:v})}p.json({ok:!1,error:"\u5BC6\u7801\u9519\u8BEF"})}),app.get("/api/admin/status",checkAdminAuth,async(_,p)=>{const o=await checkLatestVersion();loadUsers();const v=[],m=new Set;for(const[,l]of io.sockets.sockets)!l.data.userName||m.has(l.data.userName)||(m.add(l.data.userName),v.push({name:l.data.userName,role:l.data.role||"viewer"}));const h=manager.listSessions().filter(l=>l.alive!==!1).map(l=>({id:l.id,title:l.title,mode:l.mode||"pty",owner:l.owner,viewers:l.viewerCount||0}));p.json({version:{current:PKG_VERSION,latest:o||PKG_VERSION,updateAvailable:o&&o!==PKG_VERSION},onlineUsers:v,sessions:h,users:listUsers(),uptime:Math.floor((Date.now()-SERVER_START_TIME)/1e3)})}),app.post("/api/admin/upgrade",checkAdminAuth,(_,p)=>{process.send?(process.send({type:"upgrade"}),p.json({ok:!0,message:"\u5347\u7EA7\u4E2D\uFF0C\u670D\u52A1\u5C06\u81EA\u52A8\u91CD\u542F..."})):(p.json({ok:!0,message:"\u5347\u7EA7\u4E2D..."}),setTimeout(()=>{try{(0,external_child_process_namespaceObject.execSync)("npm install -g @wendongfly/myhi@latest",{stdio:"inherit",timeout:12e4}),process.exit(0)}catch(o){console.error("[myhi] \u5347\u7EA7\u5931\u8D25:",o.message)}},500))}),app.post("/api/admin/restart",checkAdminAuth,(_,p)=>{p.json({ok:!0}),setTimeout(()=>{process.send?process.send({type:"restart"}):process.exit(0)},300)}),app.post("/api/admin/user",checkAdminAuth,express.json(),(_,p)=>{const{password:o,name:v,dir:m}=_.body||{};if(!o||!v||!m)return p.json({ok:!1,error:"\u5BC6\u7801\u3001\u540D\u79F0\u3001\u76EE\u5F55\u90FD\u5FC5\u586B"});loadUsers(),addUser(o,v,m);try{(0,external_fs_.mkdirSync)(m,{recursive:!0});const h=(0,external_path_.join)(m,".gitconfig"),l=(0,external_path_.join)(m,".git-credentials");(0,external_fs_.existsSync)(h)||(0,external_fs_.writeFileSync)(h,`[credential]
|
|
362
|
+
`)[0]}catch{}try{if(process.platform==="win32"){const p=(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(p)return p[1]}else try{const p=(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(p)return p[1]}catch{const _=(0,external_child_process_namespaceObject.execSync)("route -n get default 2>/dev/null || route -n 2>/dev/null",{timeout:3e3,stdio:"pipe"}).toString(),p=_.match(/interface:\s*(\S+)/i)||_.match(/0\.0\.0\.0\s+(\d+\.\d+\.\d+\.\d+)/);if(p){const o=(0,external_os_.networkInterfaces)()[p[1]];if(o){const v=o.find(m=>m.family==="IPv4"&&!m.internal);if(v)return v.address}if(/^\d+\.\d+\.\d+\.\d+$/.test(p[1]))return p[1]}}}catch{}for(const _ of Object.values((0,external_os_.networkInterfaces)()))for(const p of _)if(p.family==="IPv4"&&!p.internal)return p.address;return"localhost"}function parseCookies(_=""){const p={};for(const o of _.split(";")){const v=o.indexOf("=");v>0&&(p[o.slice(0,v).trim()]=o.slice(v+1).trim())}return p}const SESSION_COOKIE="myhi_sid",COOKIE_OPTS="HttpOnly; SameSite=Strict; Path=/; Max-Age=31536000";function setSessionCookie(_,p){_.setHeader("Set-Cookie",`${SESSION_COOKIE}=${p||TOKEN}; ${COOKIE_OPTS}`)}function getSessionToken(_){return parseCookies(_)[SESSION_COOKIE]||null}function hasValidSession(_){const p=getSessionToken(_);if(p&&userSessions.has(p)){const o=userSessions.get(p);return o&&(o.lastUsed=Date.now()),!0}return!1}function getUserInfo(_){const p=getSessionToken(_);return p?userSessions.get(p):null}const app=express();app.set("trust proxy",!0),app.use((_,p,o)=>{p.setHeader("Referrer-Policy","no-referrer"),p.setHeader("X-Content-Type-Options","nosniff"),o()});const httpServer=(0,external_http_.createServer)(app),_corsAllowed=/^https?:\/\/(localhost|127\.0\.0\.1|\[::1\]|10\.\d+\.\d+\.\d+|172\.(1[6-9]|2\d|3[01])\.\d+\.\d+|192\.168\.\d+\.\d+|100\.(6[4-9]|[7-9]\d|1[01]\d|12[0-7])\.\d+\.\d+)(:\d+)?$/,io=new Server({cors:{origin:(_,p)=>{if(!_||_corsAllowed.test(_))return p(null,!0);p(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);const manager=new SessionManager,AUTO_ATTACH=process.env.MYHI_AUTO_ATTACH!=="0",_autoSpawned=new Set;function spawnLocalTerminal(_,p){if(!AUTO_ATTACH||_autoSpawned.has(_)||!process.env.DISPLAY&&process.platform==="linux")return;_autoSpawned.add(_);const o=__nccwpck_require__.ab+"attach.js",v=process.execPath,m=p||"myhi";if(process.platform==="win32"){const h=`"${v}" "${o}" ${_}`,l=(0,external_child_process_namespaceObject.spawn)("wt.exe",["-w","0","new-tab","--title",m,"--","cmd","/k",h],{detached:!0,stdio:"ignore"});l.unref(),l.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",`"${m}"`,"cmd","/k",h],{detached:!0,stdio:"ignore"}).unref()})}else if(process.platform==="darwin"){const h=`tell application "Terminal" to do script "${v} ${o} ${_}"`;(0,external_child_process_namespaceObject.spawn)("osascript",["-e",h],{detached:!0,stdio:"ignore"}).unref()}else{const h=`${v} "${o}" ${_}`;for(const[l,d]of[["gnome-terminal",["--","bash","-c",`${h}; exec bash`]],["xterm",["-title",m,"-e",h]],["konsole",["--new-tab","-e",h]]]){const a=(0,external_child_process_namespaceObject.spawn)(l,d,{detached:!0,stdio:"ignore"});a.unref(),a.on("error",()=>{});break}}}const uploadDir=(0,external_path_.join)(configDir,"uploads");(0,external_fs_.mkdirSync)(uploadDir,{recursive:!0});function checkAuth(_,p,o){const v=_.query.token;if(v&&(v===TOKEN||userSessions.has(v))){const m=userSessions.get(v);if(m?.onetime){userSessions.delete(v);const l=(0,external_crypto_.randomBytes)(16).toString("hex");userSessions.set(l,{role:m.role,name:m.name,lastUsed:Date.now()}),setSessionCookie(p,l)}else setSessionCookie(p,v===TOKEN?TOKEN:v);const h=_.path+(Object.keys(_.query).filter(l=>l!=="token").length?"?"+new URLSearchParams(Object.fromEntries(Object.entries(_.query).filter(([l])=>l!=="token"))):"");return p.redirect(h)}if(hasValidSession(_.headers.cookie)){if(isExclusiveMode()&&activeUser){const m=getSessionToken(_.headers.cookie);if(m!==TOKEN){const h=userSessions.get(m);if(!h||h.name!==activeUser.name)return p.setHeader("Set-Cookie",`${SESSION_COOKIE}=; HttpOnly; SameSite=Strict; Path=/; Max-Age=0`),p.redirect("/login")}}return o()}p.redirect("/login")}const _require=(0,external_module_namespaceObject.createRequire)(import.meta.url);function pkgDirSafe(_){try{return(0,external_path_.dirname)(_require.resolve(`${_}/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));const _loginAttempts=new Map,LOGIN_MAX_ATTEMPTS=5,LOGIN_LOCKOUT_MS=300*1e3;function checkLoginRate(_){const p=Date.now(),o=_loginAttempts.get(_);return!o||p>o.resetAt?!0:o.count<LOGIN_MAX_ATTEMPTS}function recordLoginFailure(_){const p=Date.now(),o=_loginAttempts.get(_);!o||p>o.resetAt?_loginAttempts.set(_,{count:1,resetAt:p+LOGIN_LOCKOUT_MS}):o.count++}function clearLoginFailures(_){_loginAttempts.delete(_)}setInterval(()=>{const _=Date.now();for(const[p,o]of _loginAttempts)_>o.resetAt&&_loginAttempts.delete(p)},600*1e3),app.get("/login",(_,p)=>{p.sendFile(__nccwpck_require__.ab+"login.html")}),app.post("/login",express.urlencoded({extended:!1}),(_,p)=>{const o=_.ip;if(!checkLoginRate(o))return p.redirect("/login?error=locked");loadRoles(),loadUsers();const v=_.body?.password,m=_.query.from,h=m&&m.startsWith("/")&&!m.startsWith("//")?m:"/";if(hasUsers()){const d=lookupUser(v);if(!d)return recordLoginFailure(o),p.redirect("/login?error=1");if(isExclusiveMode()&&activeUser&&activeUser.name!==d.name)return p.redirect("/login?error=occupied&user="+encodeURIComponent(activeUser.name));clearLoginFailures(o);const a=(0,external_crypto_.randomBytes)(16).toString("hex");return userSessions.set(a,{role:"admin",name:d.name,dir:d.dir,lastUsed:Date.now()}),isExclusiveMode()&&(activeUser?activeUser.tokens.add(a):activeUser={name:d.name,dir:d.dir,tokens:new Set([a]),loginAt:Date.now()}),setSessionCookie(p,a),console.log(`[\u767B\u5F55] ${d.name} \u767B\u5F55\uFF08${isExclusiveMode()?"\u72EC\u5360":"\u5171\u4EAB"}\u6A21\u5F0F\uFF09`),p.redirect(h)}const l=lookupPassword(v);if(l){clearLoginFailures(o);const d=(0,external_crypto_.randomBytes)(16).toString("hex");return userSessions.set(d,{role:l.role,name:l.name,lastUsed:Date.now()}),setSessionCookie(p,d),p.redirect(h)}if(v===PASSWORD){clearLoginFailures(o);const d=(0,external_crypto_.randomBytes)(16).toString("hex");return userSessions.set(d,{role:"admin",name:"\u7BA1\u7406\u5458",lastUsed:Date.now()}),setSessionCookie(p,d),p.redirect(h)}recordLoginFailure(o),p.redirect("/login?error=1")}),app.post("/logout",(_,p)=>{const o=getSessionToken(_.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)),p.setHeader("Set-Cookie",`${SESSION_COOKIE}=; HttpOnly; SameSite=Strict; Path=/; Max-Age=0`),p.redirect("/login")}),app.get("/api/status",(_,p)=>{if(isExclusiveMode()&&activeUser)return p.json({occupied:!0,userName:activeUser.name,releasing:!!_exclusiveReleaseTimer});if(hasUsers()&&!isExclusiveMode()){const o=new Set;for(const[,v]of io.sockets.sockets){if(!v.data.userName||v.data.userName==="\u7BA1\u7406\u5458")continue;const m=v.handshake.headers.cookie;m&&!hasValidSession(m)||o.add(v.data.userName)}return p.json({occupied:!1,onlineUsers:[...o]})}p.json({occupied:!1})}),app.get("/admin",(_,p)=>p.sendFile(__nccwpck_require__.ab+"admin.html")),app.get("/",checkAuth,(_,p)=>p.sendFile(__nccwpck_require__.ab+"index.html")),app.get("/terminal/:id",checkAuth,(_,p)=>p.sendFile(__nccwpck_require__.ab+"chat.html")),app.get("/terminal-raw/:id",checkAuth,(_,p)=>p.sendFile(__nccwpck_require__.ab+"terminal.html")),app.use("/lib",express.static(__nccwpck_require__.ab+"lib",staticOpts)),app.get("/api/sessions",checkAuth,(_,p)=>{const o=getUserInfo(_.headers.cookie);p.json(manager.list(o?.name,o?.role==="admin"))}),app.get("/api/usage",(_,p)=>{try{const o=manager.listSessions().filter(m=>m.mode==="agent"&&m.alive).map(m=>({id:m.id,title:m.title,owner:m.owner,usage:m._usage}));let v=null;for(const m of o)if(m.usage?.rateLimitInfo){v=m.usage.rateLimitInfo;break}p.json({rateLimit:v,activeSessions:o})}catch(o){p.status(500).json({error:o.message})}}),app.post("/api/sessions",checkAuth,express.json(),(_,p)=>{const o=getUserInfo(_.headers.cookie),v=manager.create({..._.body,owner:o?.name,userDir:o?.dir});p.status(201).json(v.toJSON())}),app.delete("/api/sessions/:id",checkAuth,(_,p)=>{manager.kill(_.params.id),p.status(204).end()}),app.get("/api/claude-sessions",checkAuth,(_,p)=>{const o=getUserInfo(_.headers.cookie);p.json(listLocalClaudeSessions(o?.dir))}),app.get("/api/dirs",checkAuth,(_,p)=>{const o=getUserInfo(_.headers.cookie);if(!o||o.role!=="admin")return p.status(403).json({error:"\u6CA1\u6709\u76EE\u5F55\u6D4F\u89C8\u6743\u9650"});const v=o.dir?(0,external_path_.resolve)((0,external_path_.normalize)(o.dir)):null,m=process.platform==="win32"?"\\":"/";let h=_.query.path||v||process.env.MYHI_CWD||(0,external_os_.homedir)(),l=(0,external_path_.resolve)((0,external_path_.normalize)(h)).replace(/[/\\]+$/,"");process.platform==="win32"&&/^[A-Za-z]:$/.test(l)&&(l+="\\"),v&&!l.startsWith(v)&&(l=v);try{const d=(0,external_fs_.readdirSync)(l,{withFileTypes:!0}).filter(r=>r.isDirectory()&&!r.name.startsWith(".")).map(r=>({name:r.name,path:l.replace(/[/\\]+$/,"")+m+r.name})).sort((r,t)=>r.name.localeCompare(t.name)),a=(0,external_path_.join)(l,".."),n=!v||(0,external_path_.resolve)((0,external_path_.normalize)(a)).startsWith(v);p.json({current:l,parent:n&&a!==l?a:null,dirs:d})}catch(d){p.status(400).json({error:d.message})}}),app.get("/api/me",checkAuth,(_,p)=>{const o=getUserInfo(_.headers.cookie);p.json({name:o?.name||"\u7528\u6237",exclusive:isExclusiveMode(),hasUsers:hasUsers(),dir:o?.dir||null})});const PKG_VERSION=(()=>{try{return JSON.parse((0,external_fs_.readFileSync)(__nccwpck_require__.ab+"package.json","utf8")).version}catch{return"0.0.0"}})();let _latestVersion=null,_lastVersionCheck=0;const VERSION_CHECK_INTERVAL=600*1e3;async function checkLatestVersion(){const _=Date.now();if(_latestVersion&&_-_lastVersionCheck<VERSION_CHECK_INTERVAL)return _latestVersion;try{const p=await fetch("https://registry.npmjs.org/@wendongfly/myhi/latest",{headers:{Accept:"application/json"},signal:AbortSignal.timeout(8e3)});p.ok&&(_latestVersion=(await p.json()).version,_lastVersionCheck=_)}catch{}return _latestVersion}app.get("/api/version",async(_,p)=>{const o=await checkLatestVersion();p.json({current:PKG_VERSION,latest:o||PKG_VERSION,updateAvailable:o&&o!==PKG_VERSION})});const _adminTokens=new Set,SERVER_START_TIME=Date.now();function checkAdminAuth(_,p,o){const v=_.headers.authorization;if(v&&v.startsWith("Bearer ")&&_adminTokens.has(v.slice(7)))return o();p.status(401).json({error:"\u672A\u6388\u6743"})}app.post("/api/admin/login",express.json(),(_,p)=>{const{password:o}=_.body||{};if(o===PASSWORD){const v=(0,external_crypto_.randomBytes)(16).toString("hex");return _adminTokens.add(v),p.json({ok:!0,token:v})}p.json({ok:!1,error:"\u5BC6\u7801\u9519\u8BEF"})}),app.get("/api/admin/status",checkAdminAuth,async(_,p)=>{const o=await checkLatestVersion();loadUsers();const v=[],m=new Set;for(const[,l]of io.sockets.sockets)!l.data.userName||m.has(l.data.userName)||(m.add(l.data.userName),v.push({name:l.data.userName,role:l.data.role||"viewer"}));const h=manager.listSessions().filter(l=>l.alive!==!1).map(l=>({id:l.id,title:l.title,mode:l.mode||"pty",owner:l.owner,viewers:l.viewerCount||0}));p.json({version:{current:PKG_VERSION,latest:o||PKG_VERSION,updateAvailable:o&&o!==PKG_VERSION},onlineUsers:v,sessions:h,users:listUsers(),uptime:Math.floor((Date.now()-SERVER_START_TIME)/1e3)})}),app.post("/api/admin/upgrade",checkAdminAuth,(_,p)=>{process.send?(process.send({type:"upgrade"}),p.json({ok:!0,message:"\u5347\u7EA7\u4E2D\uFF0C\u670D\u52A1\u5C06\u81EA\u52A8\u91CD\u542F..."})):(p.json({ok:!0,message:"\u5347\u7EA7\u4E2D..."}),setTimeout(()=>{try{const o=process.platform==="win32"?"npm.cmd install -g @wendongfly/myhi@latest":"sudo npm install -g @wendongfly/myhi@latest 2>&1 || npm install -g @wendongfly/myhi@latest";(0,external_child_process_namespaceObject.execSync)(o,{encoding:"utf8",timeout:12e4}),process.exit(0)}catch(o){console.error("[myhi] \u5347\u7EA7\u5931\u8D25:",o.message)}},500))}),app.post("/api/admin/restart",checkAdminAuth,(_,p)=>{p.json({ok:!0}),setTimeout(()=>{process.send?process.send({type:"restart"}):process.exit(0)},300)}),app.post("/api/admin/user",checkAdminAuth,express.json(),(_,p)=>{const{password:o,name:v,dir:m}=_.body||{};if(!o||!v||!m)return p.json({ok:!1,error:"\u5BC6\u7801\u3001\u540D\u79F0\u3001\u76EE\u5F55\u90FD\u5FC5\u586B"});loadUsers(),addUser(o,v,m);try{(0,external_fs_.mkdirSync)(m,{recursive:!0});const h=(0,external_path_.join)(m,".gitconfig"),l=(0,external_path_.join)(m,".git-credentials");(0,external_fs_.existsSync)(h)||(0,external_fs_.writeFileSync)(h,`[credential]
|
|
363
363
|
helper = store --file ${l.replace(/\\/g,"/")}
|
|
364
364
|
[user]
|
|
365
365
|
name = ${v}
|
package/dist/package.json
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"name":"@wendongfly/myhi","version":"1.0.
|
|
1
|
+
{"name":"@wendongfly/myhi","version":"1.0.99","description":"Web-based terminal sharing with chat UI — control your terminal from phone via LAN/Tailscale","type":"module","main":"dist/index.js","bin":{"myhi":"bin/myhi.js"},"files":["bin/","dist/"],"scripts":{"start":"node dist/index.js","dev":"node --watch src/server.js","build":"node scripts/build.js","postinstall":"node -e \"try{require('fs').chmodSync(require('path').join(__dirname,'bin','myhi.js'),0o755)}catch(e){}\""},"keywords":["terminal","web-terminal","claude","claude-code","tailscale","pty","remote-terminal","chat-ui"],"author":"wendongfly","license":"MIT","repository":{"type":"git","url":""},"engines":{"node":">=18.0.0"},"dependencies":{"node-pty":"^1.0.0","socket.io-client":"^4.8.3"},"devDependencies":{"@vercel/ncc":"^0.38.4","esbuild":"^0.27.4","express":"^4.18.2","multer":"^2.1.1","qrcode":"^1.5.4","qrcode-terminal":"^0.12.0","socket.io":"^4.8.1","socket.io-client":"^4.8.3","terser":"^5.46.1","xterm":"^4.19.0","xterm-addon-fit":"^0.5.0","xterm-addon-web-links":"^0.6.0"}}
|