@wendongfly/myhi 1.0.99 → 1.0.101

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 CHANGED
@@ -88,7 +88,7 @@ async function doUpgrade() {
88
88
  log('收到升级请求,执行 npm install...');
89
89
  try {
90
90
  const cmd = process.platform === 'win32'
91
- ? 'cmd /c 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/admin.html CHANGED
@@ -76,6 +76,18 @@
76
76
  </div>
77
77
  <div id="op-msg" class="status-msg" style="display:none"></div>
78
78
 
79
+ <!-- 在线用户 -->
80
+ <div class="card">
81
+ <h3>在线用户</h3>
82
+ <div id="online-users"><div class="row" style="color:#6e7681">加载中...</div></div>
83
+ </div>
84
+
85
+ <!-- 活跃会话 -->
86
+ <div class="card">
87
+ <h3>活跃会话</h3>
88
+ <div id="active-sessions"><div class="row" style="color:#6e7681">加载中...</div></div>
89
+ </div>
90
+
79
91
  <!-- 用户管理 -->
80
92
  <div class="card">
81
93
  <h3>用户管理</h3>
@@ -154,6 +166,29 @@ async function refresh() {
154
166
  btn.disabled = true; btn.classList.remove('glow'); btn.textContent = '已是最新';
155
167
  }
156
168
 
169
+ // 在线用户
170
+ const ouEl = document.getElementById('online-users');
171
+ if (!d.onlineUsers?.length) {
172
+ ouEl.innerHTML = '<div class="row" style="color:#6e7681">无在线用户</div>';
173
+ } else {
174
+ ouEl.innerHTML = d.onlineUsers.map(u =>
175
+ `<div class="row"><span>${esc(u.name)}</span><span style="color:#6e7681">${esc(u.role)}</span></div>`
176
+ ).join('');
177
+ }
178
+
179
+ // 活跃会话
180
+ const asEl = document.getElementById('active-sessions');
181
+ if (!d.sessions?.length) {
182
+ asEl.innerHTML = '<div class="row" style="color:#6e7681">无活跃会话</div>';
183
+ } else {
184
+ asEl.innerHTML = d.sessions.map(s => {
185
+ const tag = s.mode === 'agent'
186
+ ? '<span style="display:inline-block;font-size:0.7rem;padding:0.1rem 0.4rem;border-radius:4px;background:rgba(124,58,237,0.2);color:#a78bfa">Agent</span>'
187
+ : '<span style="display:inline-block;font-size:0.7rem;padding:0.1rem 0.4rem;border-radius:4px;background:rgba(63,185,80,0.2);color:#3fb950">PTY</span>';
188
+ return `<div class="row"><span>${tag} ${esc(s.title)} <span style="color:#6e7681">${esc(s.owner||'')}</span></span><span style="color:#6e7681">${s.viewers}人</span></div>`;
189
+ }).join('');
190
+ }
191
+
157
192
  // 用户列表
158
193
  renderUsers(d.users || []);
159
194
 
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{const o=process.platform==="win32"?"cmd /c npm 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,{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.98","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"}}
1
+ {"name":"@wendongfly/myhi","version":"1.0.100","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"}}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wendongfly/myhi",
3
- "version": "1.0.99",
3
+ "version": "1.0.101",
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",