@wendongfly/myhi 1.0.49 → 1.0.50
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -356,7 +356,7 @@ Content-Length: `+T+`\r
|
|
|
356
356
|
`)[0];if(c)return _claudePath=c,console.log("[agent] which \u627E\u5230 claude:",_claudePath),_claudePath}catch{}return console.warn("[agent] \u672A\u627E\u5230 claude \u53EF\u6267\u884C\u6587\u4EF6\uFF0C\u56DE\u9000\u5230 PATH \u67E5\u627E"),"claude"}class AgentSession extends external_events_.EventEmitter{constructor(d,c={}){super(),this.id=d,this.createdAt=new Date().toISOString(),this.title=c.title||"claude",this.cwd=c.cwd||process.env.MYHI_CWD||process.cwd(),this.permissionMode=c.permissionMode||"default",this.owner=c.owner||null,this.userDir=c.userDir||null,this.claudeSessionId=c.resumeSessionId||null,this.mode="agent",this.alive=!0,this._viewers=new Set,this.controlHolder=null,this.controlHolderName=null,this.lastInputTime=null,this._proc=null,this._busy=!1,this._history=[],this.claudeSessionId&&this._loadResumedHistory()}_loadResumedHistory(){try{const d=(0,external_path_.join)((0,external_os_.homedir)(),".claude","projects");if(!(0,external_fs_.existsSync)(d))return;for(const c of(0,external_fs_.readdirSync)(d,{withFileTypes:!0})){if(!c.isDirectory())continue;const b=(0,external_path_.join)(d,c.name,this.claudeSessionId+".jsonl");if(!(0,external_fs_.existsSync)(b))continue;const m=(0,external_fs_.readFileSync)(b,"utf8").split(`
|
|
357
357
|
`).filter(Boolean).slice(-20);for(const u of m)try{const f=JSON.parse(u);if(f.type==="human"&&f.message?.content){const a=typeof f.message.content=="string"?f.message.content:f.message.content.find(n=>n.type==="text")?.text||"";a&&this._history.push({type:"user",content:a,timestamp:f.timestamp})}else f.type==="assistant"&&f.message?.content?this._history.push({type:"assistant",message:f.message,timestamp:f.timestamp}):f.type==="result"&&this._history.push({type:"result",total_cost_usd:f.costUSD||f.total_cost_usd,timestamp:f.timestamp})}catch{}return}}catch(d){console.warn("[agent] \u52A0\u8F7D\u6062\u590D\u4F1A\u8BDD\u5386\u53F2\u5931\u8D25:",d.message)}}async query(d){if(this._busy){this.emit("agent:error",{message:"\u6B63\u5728\u5904\u7406\u4E2D\uFF0C\u8BF7\u7B49\u5F85\u5B8C\u6210"});return}this._busy=!0,this.emit("agent:busy",!0),this._history.push({type:"user",content:d,timestamp:Date.now()});const c=["-p",d,"--output-format","stream-json","--verbose"];if(this.permissionMode==="bypassPermissions")c.push("--dangerously-skip-permissions");else if(this.permissionMode==="plan")c.push("--permission-mode","plan");else{c.push("--permission-mode","acceptEdits");const b=["Bash(git *)","Bash(npm *)","Bash(npx *)","Bash(node *)","Bash(ls *)","Bash(cat *)","Bash(mkdir *)","Bash(cp *)","Bash(mv *)","Bash(rm *)","Bash(cd *)","Bash(pwd)","Bash(find *)","Bash(grep *)","Bash(curl *)","Bash(wget *)","Bash(pip *)","Bash(python *)","Bash(python3 *)","Bash(chmod *)","Bash(chown *)","Bash(tar *)","Bash(unzip *)","Bash(zip *)","Bash(sed *)","Bash(awk *)","Bash(head *)","Bash(tail *)","Bash(wc *)","Bash(sort *)","Bash(echo *)","Bash(which *)","Bash(apt *)","Bash(apt-get *)","Bash(docker *)","Bash(ssh *)","Bash(scp *)","Bash(rsync *)","Bash(touch *)","Bash(diff *)","Bash(env *)","Bash(export *)","Bash(source *)","Bash(cat *)","Bash(tee *)","Bash(xargs *)","Bash(du *)","Bash(df *)","Bash(ps *)","Bash(kill *)","Bash(systemctl *)","Bash(journalctl *)","Bash(sudo *)"];for(const g of b)c.push("--allowedTools",g)}return this.claudeSessionId&&c.push("--resume",this.claudeSessionId),new Promise((b,g)=>{const m={...process.env};if(delete m.CLAUDECODE,delete m.CLAUDE_CODE_ENTRYPOINT,delete m.CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC,this.userDir){const r=(0,external_path_.join)(this.userDir,".gitconfig");(0,external_fs_.existsSync)(r)&&(m.GIT_CONFIG_GLOBAL=r)}const u=findClaude(),f=process.platform==="win32"?process.env.ComSpec||"cmd.exe":u,a=process.platform==="win32"?["/c",u,...c]:c;this._proc=(0,external_child_process_namespaceObject.spawn)(f,a,{cwd:this.cwd,env:m,stdio:["ignore","pipe","pipe"]});let n="";this._proc.stdout.on("data",r=>{n+=r.toString();const t=n.split(`
|
|
358
358
|
`);n=t.pop();for(const e of t){const o=e.replace(/\r$/,"");if(o)try{const s=JSON.parse(o);this._handleMessage(s)}catch{}}}),this._proc.stderr.on("data",r=>{const t=r.toString().trim();t&&(console.error("[agent:stderr]",t),!t.includes("Warning:")&&!t.includes("DeprecationWarning")&&this.emit("agent:error",{message:t}))}),this._proc.on("close",r=>{if(!this._interrupted&&n.trim())try{const t=JSON.parse(n.trim());this._handleMessage(t)}catch{}this._proc=null,this._busy=!1,this._interrupted=!1,this.emit("agent:busy",!1),b(r)}),this._proc.on("error",r=>{this._proc=null,this._busy=!1,this.emit("agent:busy",!1),this.emit("agent:error",{message:`\u542F\u52A8 Claude \u5931\u8D25: ${r.message}`}),g(r)})})}_handleMessage(d){d.session_id&&!this.claudeSessionId&&(this.claudeSessionId=d.session_id),d.type==="system"&&d.subtype==="init"&&d.session_id&&(this.claudeSessionId=d.session_id),this._history.push({...d,timestamp:Date.now()});const c=500;this._history.length>c&&(this._history=this._history.slice(-c)),this.emit("agent:message",d)}interrupt(){this._proc&&(this._interrupted=!0,this._proc.kill("SIGTERM"),this.emit("agent:message",{type:"system",subtype:"interrupted",message:"\u67E5\u8BE2\u5DF2\u4E2D\u65AD"}))}kill(){this.interrupt(),this.alive=!1}get isBusy(){return this._busy}addViewer(d){this._viewers.add(d)}removeViewer(d){this._viewers.delete(d)}get viewerCount(){return this._viewers.size}takeControl(d,c){this.controlHolder=d,this.controlHolderName=c||null,this.lastInputTime=Date.now()}releaseControl(){this.controlHolder=null,this.controlHolderName=null,this.lastInputTime=null}isController(d){return this.controlHolder===d}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}}}function listLocalClaudeSessions(S){const d=[],c=S?(0,external_path_.resolve)((0,external_path_.normalize)(S)).replace(/[\\/]+$/,""):null;try{const b=(0,external_path_.join)((0,external_os_.homedir)(),".claude","projects");if(!(0,external_fs_.existsSync)(b))return d;for(const g of(0,external_fs_.readdirSync)(b,{withFileTypes:!0})){if(!g.isDirectory())continue;const m=(0,external_path_.join)(b,g.name);try{for(const u of(0,external_fs_.readdirSync)(m)){if(!u.endsWith(".jsonl"))continue;const f=u.replace(".jsonl",""),a=(0,external_path_.join)(m,u);try{const n=(0,external_fs_.readFileSync)(a,"utf8").split(`
|
|
359
|
-
`).filter(Boolean);let r=null,t=null,e=0,o="";for(const i of n)try{const p=JSON.parse(i);if(r||(r=p),t=p,e++,!o&&p.type==="human"&&p.message?.content){const l=p.message.content;if(typeof l=="string")o=l.slice(0,120);else if(Array.isArray(l)){const h=l.find(v=>v.type==="text");h&&(o=h.text?.slice(0,120)||"")}}}catch{}let s=g.name;if(process.platform==="win32"&&/^[A-Za-z]--/.test(s)?s=s[0]+":\\"+s.slice(3).replace(/-/g,"\\"):s.startsWith("-")&&(s=s.replace(/-/g,"/")),c&&!(0,external_path_.resolve)((0,external_path_.normalize)(s)).replace(/[\\/]+$/,"").startsWith(c))continue;d.push({sessionId:f,projectDir:g.name,projectPath:s,messageCount:e,summary:o,createdAt:r?.timestamp||null,updatedAt:t?.timestamp||null})}catch{}}}catch{}}}catch{}return d.sort((b,g)=>new Date(g.updatedAt||0).getTime()-new Date(b.updatedAt||0).getTime()),d.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(S){try{(0,external_fs_.mkdirSync)(CONFIG_DIR,{recursive:!0});const d=SESSIONS_FILE+".tmp";(0,external_fs_.writeFileSync)(d,JSON.stringify(S,null,2),{mode:384}),(0,external_fs_.renameSync)(d,SESSIONS_FILE)}catch(d){console.error("[sessions] \u6301\u4E45\u5316\u5199\u5165\u5931\u8D25:",d.message)}}class Session extends external_events_.EventEmitter{constructor(d,c={}){super(),this.id=d,this.createdAt=new Date().toISOString(),this.title=c.title||DEFAULT_SHELL,this.cwd=c.cwd||process.env.MYHI_CWD||process.cwd(),this.initCmd=c.initCmd||null,this.permissionMode=c.permissionMode||"default",this.owner=c.owner||null,this.cols=c.cols||120,this.rows=c.rows||30,this._viewers=new Set,this.controlHolder=null,this.controlHolderName=null,this.lastInputTime=null;const b={...process.env};if(delete b.CLAUDECODE,delete b.CLAUDE_CODE_ENTRYPOINT,delete b.CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC,c.userDir){const m=(0,external_path_.join)(c.userDir,".gitconfig");(0,external_fs_.existsSync)(m)&&(b.GIT_CONFIG_GLOBAL=m)}this._pty=external_node_pty_namespaceObject.spawn(DEFAULT_SHELL,[],{name:"xterm-256color",cols:this.cols,rows:this.rows,cwd:this.cwd,env:b,useConpty:!1}),this._scrollback="";const g=100*1024;if(this._pty.onData(m=>{this.emit("data",m),this._scrollback+=m,this._scrollback.length>g&&(this._scrollback=this._scrollback.slice(this._scrollback.length-g))}),this._pty.onExit(({exitCode:m})=>{this.exitCode=m,this.emit("exit",m)}),c.initCmd){const m=c.initCmd;let u=!1;const f=a=>{!u&&/[$>#\]]\s*$/.test(a.trimEnd())&&(u=!0,this._pty.off("data",f),setTimeout(()=>this._pty.write(m+"\r"),120))};this._pty.onData(f),setTimeout(()=>{u||(u=!0,this._pty.write(m+"\r"))},3e3)}}write(d){d!=null&&this._pty.write(d)}resize(d,c){this.cols=d,this.rows=c,this._pty.resize(d,c)}kill(){try{this._pty.kill()}catch{}}addViewer(d){this._viewers.add(d)}removeViewer(d){this._viewers.delete(d)}get viewerCount(){return this._viewers.size}takeControl(d,c){this.controlHolder=d,this.controlHolderName=c||null,this.lastInputTime=Date.now()}releaseControl(){this.controlHolder=null,this.controlHolderName=null,this.lastInputTime=null}isController(d){return this.controlHolder===d}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 d of loadSavedConfigs())try{this._spawn((0,external_crypto_.randomUUID)(),d,!1)}catch(c){console.warn(`[\u4F1A\u8BDD] \u6062\u590D "${d.title}" \u5931\u8D25:`,c.message)}}_spawn(d,c,b=!0){const g=new Session(d,c);return this._sessions.set(d,g),g.on("exit",()=>{setTimeout(()=>this._sessions.delete(d),300*1e3)}),b&&this._persist(),g}create(d={}){return this._spawn((0,external_crypto_.randomUUID)(),d,!0)}createAgent(d={}){const c=(0,external_crypto_.randomUUID)(),b=new AgentSession(c,d);return this._agentSessions.set(c,b),b}get(d){return this._sessions.get(d)||this._agentSessions.get(d)}list(d){let c=[...this._sessions.values()].map(g=>({...g.toJSON(),mode:"pty"})),b=[...this._agentSessions.values()].map(g=>g.toJSON());return d&&(c=c.filter(g
|
|
359
|
+
`).filter(Boolean);let r=null,t=null,e=0,o="";for(const i of n)try{const p=JSON.parse(i);if(r||(r=p),t=p,e++,!o&&p.type==="human"&&p.message?.content){const l=p.message.content;if(typeof l=="string")o=l.slice(0,120);else if(Array.isArray(l)){const h=l.find(v=>v.type==="text");h&&(o=h.text?.slice(0,120)||"")}}}catch{}let s=g.name;if(process.platform==="win32"&&/^[A-Za-z]--/.test(s)?s=s[0]+":\\"+s.slice(3).replace(/-/g,"\\"):s.startsWith("-")&&(s=s.replace(/-/g,"/")),c&&!(0,external_path_.resolve)((0,external_path_.normalize)(s)).replace(/[\\/]+$/,"").startsWith(c))continue;d.push({sessionId:f,projectDir:g.name,projectPath:s,messageCount:e,summary:o,createdAt:r?.timestamp||null,updatedAt:t?.timestamp||null})}catch{}}}catch{}}}catch{}return d.sort((b,g)=>new Date(g.updatedAt||0).getTime()-new Date(b.updatedAt||0).getTime()),d.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(S){try{(0,external_fs_.mkdirSync)(CONFIG_DIR,{recursive:!0});const d=SESSIONS_FILE+".tmp";(0,external_fs_.writeFileSync)(d,JSON.stringify(S,null,2),{mode:384}),(0,external_fs_.renameSync)(d,SESSIONS_FILE)}catch(d){console.error("[sessions] \u6301\u4E45\u5316\u5199\u5165\u5931\u8D25:",d.message)}}class Session extends external_events_.EventEmitter{constructor(d,c={}){super(),this.id=d,this.createdAt=new Date().toISOString(),this.title=c.title||DEFAULT_SHELL,this.cwd=c.cwd||process.env.MYHI_CWD||process.cwd(),this.initCmd=c.initCmd||null,this.permissionMode=c.permissionMode||"default",this.owner=c.owner||null,this.cols=c.cols||120,this.rows=c.rows||30,this._viewers=new Set,this.controlHolder=null,this.controlHolderName=null,this.lastInputTime=null;const b={...process.env};if(delete b.CLAUDECODE,delete b.CLAUDE_CODE_ENTRYPOINT,delete b.CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC,c.userDir){const m=(0,external_path_.join)(c.userDir,".gitconfig");(0,external_fs_.existsSync)(m)&&(b.GIT_CONFIG_GLOBAL=m)}this._pty=external_node_pty_namespaceObject.spawn(DEFAULT_SHELL,[],{name:"xterm-256color",cols:this.cols,rows:this.rows,cwd:this.cwd,env:b,useConpty:!1}),this._scrollback="";const g=100*1024;if(this._pty.onData(m=>{this.emit("data",m),this._scrollback+=m,this._scrollback.length>g&&(this._scrollback=this._scrollback.slice(this._scrollback.length-g))}),this._pty.onExit(({exitCode:m})=>{this.exitCode=m,this.emit("exit",m)}),c.initCmd){const m=c.initCmd;let u=!1;const f=a=>{!u&&/[$>#\]]\s*$/.test(a.trimEnd())&&(u=!0,this._pty.off("data",f),setTimeout(()=>this._pty.write(m+"\r"),120))};this._pty.onData(f),setTimeout(()=>{u||(u=!0,this._pty.write(m+"\r"))},3e3)}}write(d){d!=null&&this._pty.write(d)}resize(d,c){this.cols=d,this.rows=c,this._pty.resize(d,c)}kill(){try{this._pty.kill()}catch{}}addViewer(d){this._viewers.add(d)}removeViewer(d){this._viewers.delete(d)}get viewerCount(){return this._viewers.size}takeControl(d,c){this.controlHolder=d,this.controlHolderName=c||null,this.lastInputTime=Date.now()}releaseControl(){this.controlHolder=null,this.controlHolderName=null,this.lastInputTime=null}isController(d){return this.controlHolder===d}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 d of loadSavedConfigs())try{this._spawn((0,external_crypto_.randomUUID)(),d,!1)}catch(c){console.warn(`[\u4F1A\u8BDD] \u6062\u590D "${d.title}" \u5931\u8D25:`,c.message)}}_spawn(d,c,b=!0){const g=new Session(d,c);return this._sessions.set(d,g),g.on("exit",()=>{setTimeout(()=>this._sessions.delete(d),300*1e3)}),b&&this._persist(),g}create(d={}){return this._spawn((0,external_crypto_.randomUUID)(),d,!0)}createAgent(d={}){const c=(0,external_crypto_.randomUUID)(),b=new AgentSession(c,d);return this._agentSessions.set(c,b),b}get(d){return this._sessions.get(d)||this._agentSessions.get(d)}list(d){let c=[...this._sessions.values()].map(g=>({...g.toJSON(),mode:"pty"})),b=[...this._agentSessions.values()].map(g=>g.toJSON());return d&&(c=c.filter(g=>g.owner===d),b=b.filter(g=>g.owner===d)),[...c,...b]}listSessions(){return[...this._sessions.values(),...this._agentSessions.values()]}kill(d){const c=this._sessions.get(d);if(c){c.kill(),this._sessions.delete(d),this._persist();return}const b=this._agentSessions.get(d);b&&(b.kill(),this._agentSessions.delete(d))}_persist(){const d=[...this._sessions.values()].filter(c=>c.exitCode===void 0).map(c=>({title:c.title,cwd:c.cwd,initCmd:c.initCmd||void 0,permissionMode:c.permissionMode||void 0,owner:c.owner||void 0}));writeSavedConfigs(d)}}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(S){console.warn("[\u89D2\u8272] \u52A0\u8F7D roles.json \u5931\u8D25:",S.message)}return rolesConfig=null,null}function lookupPassword(S){if(!rolesConfig?.passwords)return null;const d=rolesConfig.passwords[S];return d?{name:d.name||"\u7528\u6237",role:d.role||"viewer"}:null}function hasPermission(S,d){return(ROLE_LEVELS[S]||0)>=(ROLE_LEVELS[d]||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(S){console.warn("[\u7528\u6237] \u52A0\u8F7D users.json \u5931\u8D25:",S.message)}return usersConfig=null,null}function isExclusiveMode(){return usersConfig!==null&&usersConfig.users&&Object.keys(usersConfig.users).length>0}function lookupUser(S){if(!usersConfig?.users)return null;const d=usersConfig.users[S];return d?{name:d.name||"\u7528\u6237",dir:d.dir}:null}function listUsers(){return usersConfig?.users?Object.entries(usersConfig.users).map(([S,d])=>({password:S.slice(0,2)+"*".repeat(S.length-2),name:d.name,dir:d.dir})):[]}function addUser(S,d,c){usersConfig||(usersConfig={users:{}}),usersConfig.users||(usersConfig.users={}),usersConfig.users[S]={name:d,dir:c},_writeUsers()}function removeUser(S){return!usersConfig?.users||!usersConfig.users[S]?!1:(delete usersConfig.users[S],_writeUsers(),!0)}function _writeUsers(){mkdirSync(roles_CONFIG_DIR,{recursive:!0}),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)||3e3;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");(function S(){try{const d=parseInt((0,external_fs_.readFileSync)(pidFile,"utf8").trim(),10);if(d&&d!==process.pid)try{process.kill(d,0),console.log(`[myhi] \u68C0\u6D4B\u5230\u5DF2\u8FD0\u884C\u7684\u5B9E\u4F8B (PID ${d})\uFF0C\u6B63\u5728\u505C\u6B62...`),process.kill(d,"SIGTERM")}catch{}}catch{}(0,external_fs_.writeFileSync)(pidFile,String(process.pid))})();function cleanupPid(){try{parseInt((0,external_fs_.readFileSync)(pidFile,"utf8").trim(),10)===process.pid&&(0,external_fs_.unlinkSync)(pidFile)}catch{}}process.on("exit",cleanupPid),process.on("SIGTERM",()=>{cleanupPid(),process.exit(0)}),process.on("SIGINT",()=>{cleanupPid(),process.exit(0)});function readOrCreate(S,d){try{if((0,external_fs_.existsSync)(S))return(0,external_fs_.readFileSync)(S,"utf8").trim()}catch{}const c=d();return(0,external_fs_.writeFileSync)(S,c,{mode:384}),c}const TOKEN=readOrCreate((0,external_path_.join)(configDir,"token"),()=>(0,external_crypto_.randomBytes)(16).toString("hex")),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 S=Date.now();for(const[d,c]of userSessions)c.permanent||(!c.lastUsed||S-c.lastUsed>SESSION_MAX_AGE)&&userSessions.delete(d)},3600*1e3);function getTailscaleIP(){try{return(0,external_child_process_namespaceObject.execSync)("tailscale ip -4",{timeout:3e3,stdio:"pipe"}).toString().trim().split(`
|
|
360
360
|
`)[0]}catch{}try{if(process.platform==="win32"){const d=(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(d)return d[1]}else try{const d=(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(d)return d[1]}catch{const S=(0,external_child_process_namespaceObject.execSync)("route -n get default 2>/dev/null || route -n 2>/dev/null",{timeout:3e3,stdio:"pipe"}).toString(),d=S.match(/interface:\s*(\S+)/i)||S.match(/0\.0\.0\.0\s+(\d+\.\d+\.\d+\.\d+)/);if(d){const c=(0,external_os_.networkInterfaces)()[d[1]];if(c){const b=c.find(g=>g.family==="IPv4"&&!g.internal);if(b)return b.address}if(/^\d+\.\d+\.\d+\.\d+$/.test(d[1]))return d[1]}}}catch{}for(const S of Object.values((0,external_os_.networkInterfaces)()))for(const d of S)if(d.family==="IPv4"&&!d.internal)return d.address;return"localhost"}function parseCookies(S=""){const d={};for(const c of S.split(";")){const b=c.indexOf("=");b>0&&(d[c.slice(0,b).trim()]=c.slice(b+1).trim())}return d}const SESSION_COOKIE="myhi_sid",COOKIE_OPTS="HttpOnly; SameSite=Strict; Path=/; Max-Age=31536000";function setSessionCookie(S,d){S.setHeader("Set-Cookie",`${SESSION_COOKIE}=${d||TOKEN}; ${COOKIE_OPTS}`)}function getSessionToken(S){return parseCookies(S)[SESSION_COOKIE]||null}function hasValidSession(S){const d=getSessionToken(S);if(d&&userSessions.has(d)){const c=userSessions.get(d);return c&&(c.lastUsed=Date.now()),!0}return!1}function getUserInfo(S){const d=getSessionToken(S);return d?userSessions.get(d):null}const app=express();app.set("trust proxy",!0),app.use((S,d,c)=>{d.setHeader("Referrer-Policy","no-referrer"),d.setHeader("X-Content-Type-Options","nosniff"),c()});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:(S,d)=>{if(!S||_corsAllowed.test(S))return d(null,!0);d(new Error("CORS \u4E0D\u5141\u8BB8\u6B64\u6765\u6E90"),!1)},credentials:!0},transports:["websocket"]});io.attach(httpServer);const manager=new SessionManager,AUTO_ATTACH=process.env.MYHI_AUTO_ATTACH!=="0",_autoSpawned=new Set;function spawnLocalTerminal(S,d){if(!AUTO_ATTACH||_autoSpawned.has(S))return;_autoSpawned.add(S);const c=__nccwpck_require__.ab+"attach.js",b=process.execPath,g=d||"myhi";if(process.platform==="win32"){const m=`"${b}" "${c}" ${S}`,u=(0,external_child_process_namespaceObject.spawn)("wt.exe",["-w","0","new-tab","--title",g,"--","cmd","/k",m],{detached:!0,stdio:"ignore"});u.unref(),u.on("error",f=>{console.warn("[attach] wt.exe \u5931\u8D25\uFF0C\u56DE\u9000\u5230 cmd:",f.message),(0,external_child_process_namespaceObject.spawn)("cmd.exe",["/c","start",`"${g}"`,"cmd","/k",m],{detached:!0,stdio:"ignore"}).unref()})}else if(process.platform==="darwin"){const m=`tell application "Terminal" to do script "${b} ${c} ${S}"`;(0,external_child_process_namespaceObject.spawn)("osascript",["-e",m],{detached:!0,stdio:"ignore"}).unref()}else{const m=`${b} "${c}" ${S}`;for(const[u,f]of[["gnome-terminal",["--","bash","-c",`${m}; exec bash`]],["xterm",["-title",g,"-e",m]],["konsole",["--new-tab","-e",m]]]){const a=(0,external_child_process_namespaceObject.spawn)(u,f,{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(S,d,c){const b=S.query.token;if(b&&(b===TOKEN||userSessions.has(b))){const g=userSessions.get(b);if(g?.onetime){userSessions.delete(b);const u=(0,external_crypto_.randomBytes)(16).toString("hex");userSessions.set(u,{role:g.role,name:g.name,lastUsed:Date.now()}),setSessionCookie(d,u)}else setSessionCookie(d,b===TOKEN?TOKEN:b);const m=S.path+(Object.keys(S.query).filter(u=>u!=="token").length?"?"+new URLSearchParams(Object.fromEntries(Object.entries(S.query).filter(([u])=>u!=="token"))):"");return d.redirect(m)}if(hasValidSession(S.headers.cookie)){if(isExclusiveMode()&&activeUser){const g=getSessionToken(S.headers.cookie);if(g!==TOKEN){const m=userSessions.get(g);if(!m||m.name!==activeUser.name)return d.setHeader("Set-Cookie",`${SESSION_COOKIE}=; HttpOnly; SameSite=Strict; Path=/; Max-Age=0`),d.redirect("/login")}}return c()}d.redirect("/login")}const _require=(0,external_module_namespaceObject.createRequire)(import.meta.url);function pkgDirSafe(S){try{return(0,external_path_.dirname)(_require.resolve(`${S}/package.json`))}catch{return null}}const libDir=(0,external_path_.join)(server_dirname,"lib");app.use("/lib/xterm",express.static(pkgDirSafe("xterm")||(0,external_path_.join)(libDir,"xterm"))),app.use("/lib/xterm-fit",express.static(pkgDirSafe("xterm-addon-fit")||(0,external_path_.join)(libDir,"xterm-fit"))),app.use("/lib/xterm-links",express.static(pkgDirSafe("xterm-addon-web-links")||(0,external_path_.join)(libDir,"xterm-links")));const _loginAttempts=new Map,LOGIN_MAX_ATTEMPTS=5,LOGIN_LOCKOUT_MS=300*1e3;function checkLoginRate(S){const d=Date.now(),c=_loginAttempts.get(S);return!c||d>c.resetAt?!0:c.count<LOGIN_MAX_ATTEMPTS}function recordLoginFailure(S){const d=Date.now(),c=_loginAttempts.get(S);!c||d>c.resetAt?_loginAttempts.set(S,{count:1,resetAt:d+LOGIN_LOCKOUT_MS}):c.count++}function clearLoginFailures(S){_loginAttempts.delete(S)}setInterval(()=>{const S=Date.now();for(const[d,c]of _loginAttempts)S>c.resetAt&&_loginAttempts.delete(d)},600*1e3),app.get("/login",(S,d)=>{d.sendFile(__nccwpck_require__.ab+"login.html")}),app.post("/login",express.urlencoded({extended:!1}),(S,d)=>{const c=S.ip;if(!checkLoginRate(c))return d.redirect("/login?error=locked");loadRoles(),loadUsers();const b=S.body?.password,g=S.query.from,m=g&&g.startsWith("/")&&!g.startsWith("//")?g:"/";if(isExclusiveMode()){const f=lookupUser(b);if(!f)return recordLoginFailure(c),d.redirect("/login?error=1");if(activeUser&&activeUser.name!==f.name)return d.redirect("/login?error=occupied&user="+encodeURIComponent(activeUser.name));clearLoginFailures(c);const a=(0,external_crypto_.randomBytes)(16).toString("hex");return userSessions.set(a,{role:"admin",name:f.name,dir:f.dir,lastUsed:Date.now()}),activeUser?activeUser.tokens.add(a):activeUser={name:f.name,dir:f.dir,tokens:new Set([a]),loginAt:Date.now()},setSessionCookie(d,a),console.log(`[\u767B\u5F55] ${f.name} \u767B\u5F55\uFF08\u72EC\u5360\u6A21\u5F0F\uFF09`),d.redirect(m)}const u=lookupPassword(b);if(u){clearLoginFailures(c);const f=(0,external_crypto_.randomBytes)(16).toString("hex");return userSessions.set(f,{role:u.role,name:u.name,lastUsed:Date.now()}),setSessionCookie(d,f),d.redirect(m)}if(b===PASSWORD){clearLoginFailures(c);const f=(0,external_crypto_.randomBytes)(16).toString("hex");return userSessions.set(f,{role:"admin",name:"\u7BA1\u7406\u5458",lastUsed:Date.now()}),setSessionCookie(d,f),d.redirect(m)}recordLoginFailure(c),d.redirect("/login?error=1")}),app.post("/logout",(S,d)=>{const c=getSessionToken(S.headers.cookie);c&&(activeUser&&activeUser.tokens.has(c)&&(activeUser.tokens.delete(c),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(c)),d.setHeader("Set-Cookie",`${SESSION_COOKIE}=; HttpOnly; SameSite=Strict; Path=/; Max-Age=0`),d.redirect("/login")}),app.get("/api/status",(S,d)=>{if(isExclusiveMode()&&activeUser)return d.json({occupied:!0,userName:activeUser.name,releasing:!!_exclusiveReleaseTimer});d.json({occupied:!1})}),app.get("/",checkAuth,(S,d)=>d.sendFile(__nccwpck_require__.ab+"index.html")),app.get("/terminal/:id",checkAuth,(S,d)=>d.sendFile(__nccwpck_require__.ab+"chat.html")),app.get("/terminal-raw/:id",checkAuth,(S,d)=>d.sendFile(__nccwpck_require__.ab+"terminal.html")),app.use("/lib",express.static(__nccwpck_require__.ab+"lib")),app.get("/api/sessions",checkAuth,(S,d)=>{const c=getUserInfo(S.headers.cookie);d.json(manager.list(c?.name))}),app.post("/api/sessions",checkAuth,express.json(),(S,d)=>{const c=getUserInfo(S.headers.cookie),b=manager.create({...S.body,owner:c?.name,userDir:c?.dir});d.status(201).json(b.toJSON())}),app.delete("/api/sessions/:id",checkAuth,(S,d)=>{manager.kill(S.params.id),d.status(204).end()}),app.get("/api/claude-sessions",checkAuth,(S,d)=>{const c=getUserInfo(S.headers.cookie);d.json(listLocalClaudeSessions(c?.dir))}),app.get("/api/dirs",checkAuth,(S,d)=>{const c=getUserInfo(S.headers.cookie);if(!c||c.role!=="admin")return d.status(403).json({error:"\u6CA1\u6709\u76EE\u5F55\u6D4F\u89C8\u6743\u9650"});const b=c.dir?(0,external_path_.resolve)((0,external_path_.normalize)(c.dir)):null,g=process.platform==="win32"?"\\":"/";let m=S.query.path||b||process.env.MYHI_CWD||(0,external_os_.homedir)(),u=(0,external_path_.resolve)((0,external_path_.normalize)(m)).replace(/[/\\]+$/,"");process.platform==="win32"&&/^[A-Za-z]:$/.test(u)&&(u+="\\"),b&&!u.startsWith(b)&&(u=b);try{const f=(0,external_fs_.readdirSync)(u,{withFileTypes:!0}).filter(r=>r.isDirectory()&&!r.name.startsWith(".")).map(r=>({name:r.name,path:u.replace(/[/\\]+$/,"")+g+r.name})).sort((r,t)=>r.name.localeCompare(t.name)),a=(0,external_path_.join)(u,".."),n=!b||(0,external_path_.resolve)((0,external_path_.normalize)(a)).startsWith(b);d.json({current:u,parent:n&&a!==u?a:null,dirs:f})}catch(f){d.status(400).json({error:f.message})}}),app.get("/api/me",checkAuth,(S,d)=>{const c=getUserInfo(S.headers.cookie);d.json({name:c?.name||"\u7528\u6237",exclusive:isExclusiveMode(),dir:c?.dir||null})}),app.post("/upload",checkAuth,(S,d)=>{const c=S.query.sessionId,b=c?manager.get(c):null,g=b?.cwd?(0,external_path_.join)(b.cwd,"upload"):uploadDir;(0,external_fs_.mkdirSync)(g,{recursive:!0}),multer({storage:multer.diskStorage({destination:g,filename:(u,f,a)=>{const n=f.originalname.match(/\.[^.]+$/)?.[0]||".jpg";a(null,`${Date.now()}-${(0,external_crypto_.randomBytes)(3).toString("hex")}${n}`)}}),limits:{fileSize:20*1024*1024},fileFilter:(u,f,a)=>a(null,f.mimetype.startsWith("image/"))}).single("image")(S,d,u=>{if(u)return d.status(400).json({error:u.message});if(!S.file)return d.status(400).json({error:"\u6CA1\u6709\u56FE\u7247"});d.json({path:(0,external_path_.resolve)(S.file.path).replace(/\\/g,"/")})})}),app.get("/qr/:sessionId",checkAuth,async(S,d)=>{const c=getTailscaleIP(),b=(0,external_crypto_.randomBytes)(16).toString("hex");userSessions.set(b,{role:"admin",name:"\u626B\u7801\u7528\u6237",onetime:!0,lastUsed:Date.now()});const g=`http://${c}:${PORT}/terminal/${S.params.sessionId}?token=${b}`;try{const m=await lib.toString(g,{type:"svg"});d.setHeader("Content-Type","image/svg+xml"),d.send(m)}catch(m){d.status(500).send(m.message)}}),io.use((S,d)=>{const c=S.handshake.headers.cookie;if(hasValidSession(c)){const g=getUserInfo(c);return S.data.role=g?.role||"admin",S.data.userName=g?.name||"\u7528\u6237",S.data.dir=g?.dir||null,d()}if((S.handshake.auth?.token||S.handshake.query?.token)===TOKEN)return S.data.role="admin",S.data.userName="\u7BA1\u7406\u5458",d();d(new Error("\u672A\u6388\u6743"))});const EXCLUSIVE_RELEASE_DELAY=120*1e3;let _exclusiveReleaseTimer=null;function checkExclusiveRelease(){if(!isExclusiveMode()||!activeUser)return;let S=!1;for(const[,d]of io.sockets.sockets)if(d.data.userName===activeUser.name){S=!0;break}if(S){_exclusiveReleaseTimer&&(clearTimeout(_exclusiveReleaseTimer),_exclusiveReleaseTimer=null);return}_exclusiveReleaseTimer||(_exclusiveReleaseTimer=setTimeout(()=>{if(_exclusiveReleaseTimer=null,!!activeUser){for(const[,d]of io.sockets.sockets)if(d.data.userName===activeUser.name)return;console.log(`[\u72EC\u5360] ${activeUser.name} \u5DF2\u79BB\u7EBF\u8D85\u65F6\uFF0C\u91CA\u653E\u767B\u5F55\u72B6\u6001\uFF08\u4F1A\u8BDD\u4FDD\u7559\uFF09`);for(const d of activeUser.tokens)userSessions.delete(d);activeUser=null}},EXCLUSIVE_RELEASE_DELAY))}function broadcastSessions(){for(const[,S]of io.sockets.sockets)S.emit("sessions",manager.list(S.data.userName))}io.on("connection",S=>{const d=S.handshake.address?.replace("::ffff:","")||"?";console.log(`[\u8FDE\u63A5] ${S.data.userName}(${S.data.role}) \u4ECE ${d} \u63A5\u5165 id=${S.id}`),_exclusiveReleaseTimer&&activeUser&&S.data.userName===activeUser.name&&(clearTimeout(_exclusiveReleaseTimer),_exclusiveReleaseTimer=null);let c=null,b=null,g=null,m=null,u=null;function f(){c&&(c.isController(S.id)&&(c.releaseControl(),io.emit("control-changed",{sessionId:c.id,holder:null,holderName:null})),c.removeViewer(S.id),b&&c.off("data",b),g&&c.off("agent:message",g),m&&c.off("agent:busy",m),u&&c.off("agent:error",u),b=null,g=null,m=null,u=null)}S.on("join",a=>{const n=manager.get(a);if(!n){S.emit("error",{message:`\u4F1A\u8BDD ${a} \u672A\u627E\u5230`});return}f(),c=n,c.addViewer(S.id),n.mode==="agent"?(g=r=>S.emit("agent:message",r),c.on("agent:message",g),m=r=>S.emit("agent:busy",r),c.on("agent:busy",m),u=r=>S.emit("agent:error",r),c.on("agent:error",u)):(b=r=>S.emit("output",r),c.on("data",b),c.once("exit",r=>{S.emit("session-exit",{code:r}),b&&c?.off("data",b)}),spawnLocalTerminal(a,n.title)),console.log(`[\u52A0\u5165] ${S.data.userName} \u52A0\u5165\u4F1A\u8BDD "${n.title}" (${a}) \u6A21\u5F0F=${n.mode||"pty"}`),S.emit("joined",{...n.toJSON(),role:S.data.role}),n.mode==="agent"?n._history?.length&&S.emit("agent:history",n._history):n._scrollback?.length&&S.emit("output",n._scrollback),broadcastSessions()}),S.on("disconnect",()=>{console.log(`[\u65AD\u5F00] ${S.data.userName} \u79BB\u5F00\u4F1A\u8BDD "${c?.title||"?"}" id=${S.id}`),f(),broadcastSessions(),checkExclusiveRelease()}),S.on("agent:query",async({prompt:a}={})=>{if(!c||c.mode!=="agent"){S.emit("agent:error",{message:"\u5F53\u524D\u4E0D\u662F Agent \u6A21\u5F0F\u4F1A\u8BDD"});return}if(!c.isController(S.id)){S.emit("control-denied",{reason:"\u4F60\u6CA1\u6709\u5F53\u524D\u4F1A\u8BDD\u7684\u63A7\u5236\u6743"});return}if(c.isBusy){S.emit("agent:error",{message:"\u6B63\u5728\u5904\u7406\u4E2D\uFF0C\u8BF7\u7B49\u5F85\u5B8C\u6210"});return}try{await c.query(a)}catch(n){console.error("[agent:query] \u9519\u8BEF:",n.message),S.emit("agent:error",{message:n.message})}}),S.on("agent:interrupt",()=>{!c||c.mode!=="agent"||c.interrupt()}),S.on("create-agent",(a,n)=>{if(!hasPermission(S.data.role,"admin")){typeof n=="function"&&n({ok:!1,error:"\u6CA1\u6709\u521B\u5EFA\u4F1A\u8BDD\u7684\u6743\u9650"});return}if(S.data.dir){const t=(0,external_path_.resolve)((0,external_path_.normalize)(S.data.dir)),e=a?.cwd?(0,external_path_.resolve)((0,external_path_.normalize)(a.cwd)):null;if(e&&e!==t&&!e.startsWith(t+external_path_.sep)){typeof n=="function"&&n({ok:!1,error:"\u4E0D\u80FD\u5728\u7ED1\u5B9A\u76EE\u5F55\u4E4B\u5916\u521B\u5EFA\u4F1A\u8BDD"});return}e||(a={...a,cwd:t})}const r=a?.cwd||process.env.MYHI_CWD||process.cwd();if(!(0,external_fs_.existsSync)(r))try{(0,external_fs_.mkdirSync)(r,{recursive:!0}),console.log(`[\u521B\u5EFA] \u81EA\u52A8\u521B\u5EFA\u76EE\u5F55: ${r}`)}catch(t){typeof n=="function"&&n({ok:!1,error:`\u521B\u5EFA\u76EE\u5F55\u5931\u8D25: ${t.message}`});return}try{const t=manager.createAgent({...a,owner:S.data.userName,userDir:S.data.dir});console.log(`[\u4F1A\u8BDD] ${S.data.userName} \u521B\u5EFA Agent \u4F1A\u8BDD "${t.title}" (${t.id})`),broadcastSessions(),typeof n=="function"&&n({ok:!0,session:t.toJSON()})}catch(t){typeof n=="function"&&n({ok:!1,error:t.message})}}),S.on("input",a=>{if(!(a==null||!c)&&c.mode!=="agent"){if(!c.isController(S.id)){S.emit("control-denied",{reason:"\u4F60\u6CA1\u6709\u5F53\u524D\u4F1A\u8BDD\u7684\u63A7\u5236\u6743"});return}c.lastInputTime=Date.now(),c.write(a)}}),S.on("take-control",({sessionId:a}={})=>{const n=a?manager.get(a):c;if(n){if(!hasPermission(S.data.role,"operator")){S.emit("control-denied",{reason:"\u4F60\u7684\u89D2\u8272\u6CA1\u6709\u63A7\u5236\u6743\u9650"});return}if(n.controlHolder&&n.controlHolder!==S.id&&S.data.role!=="admin"){S.emit("control-denied",{reason:"\u5176\u4ED6\u7528\u6237\u6B63\u5728\u63A7\u5236\u4E2D\uFF0C\u8BF7\u7B49\u5F85\u91CA\u653E"});return}n.takeControl(S.id,S.data.userName),console.log(`[\u63A7\u5236] ${S.data.userName} \u83B7\u53D6\u4F1A\u8BDD "${n.title}" \u7684\u63A7\u5236\u6743`),io.emit("control-changed",{sessionId:n.id,holder:S.id,holderName:S.data.userName})}}),S.on("release-control",({sessionId:a}={})=>{const n=a?manager.get(a):c;n&&n.isController(S.id)&&(console.log(`[\u63A7\u5236] ${S.data.userName} \u91CA\u653E\u4F1A\u8BDD "${n.title}" \u7684\u63A7\u5236\u6743`),n.releaseControl(),io.emit("control-changed",{sessionId:n.id,holder:null,holderName:null}))}),S.on("set-mode",({sessionId:a,mode:n}={})=>{const r=a?manager.get(a):c;r&&(r.permissionMode=n,io.emit("mode-changed",{sessionId:r.id,mode:n}))}),S.on("rename",({sessionId:a,title:n}={})=>{const r=a?manager.get(a):c;!r||!n||(r.title=n,io.emit("session-renamed",{sessionId:r.id,title:n}),broadcastSessions())}),S.on("resize",({cols:a,rows:n})=>c?.resize(a,n)),S.on("list",()=>S.emit("sessions",manager.list(S.data.userName))),S.on("dirs",(a,n)=>{if(typeof n!="function")return;const r=S.data.dir?(0,external_path_.resolve)((0,external_path_.normalize)(S.data.dir)):null,t=process.platform==="win32"?"\\":"/";let e=(a||r||process.env.MYHI_CWD||(0,external_os_.homedir)()).replace(/[/\\]+$/,"")||(0,external_os_.homedir)();process.platform==="win32"&&/^[A-Za-z]:$/.test(e)&&(e+="\\");const o=(0,external_path_.resolve)((0,external_path_.normalize)(e));r&&o!==r&&!o.startsWith(r+t)&&(e=r);try{const s=(0,external_fs_.readdirSync)(e,{withFileTypes:!0}).filter(h=>h.isDirectory()&&!h.name.startsWith(".")).map(h=>({name:h.name,path:e.replace(/[/\\]+$/,"")+t+h.name})).sort((h,v)=>h.name.localeCompare(v.name)),i=(0,external_path_.join)(e,".."),p=(0,external_path_.resolve)((0,external_path_.normalize)(i)),l=!r||p===r||p.startsWith(r+t);n({ok:!0,current:e,parent:l&&i!==e?i:null,dirs:s})}catch(s){n({ok:!1,error:s.message})}}),S.on("create",(a,n)=>{if(!hasPermission(S.data.role,"admin")){typeof n=="function"&&n({ok:!1,error:"\u6CA1\u6709\u521B\u5EFA\u4F1A\u8BDD\u7684\u6743\u9650"});return}if(S.data.dir){const t=(0,external_path_.resolve)((0,external_path_.normalize)(S.data.dir)),e=a?.cwd?(0,external_path_.resolve)((0,external_path_.normalize)(a.cwd)):null;if(e&&e!==t&&!e.startsWith(t+external_path_.sep)){typeof n=="function"&&n({ok:!1,error:"\u4E0D\u80FD\u5728\u7ED1\u5B9A\u76EE\u5F55\u4E4B\u5916\u521B\u5EFA\u4F1A\u8BDD"});return}e||(a={...a,cwd:t})}const r=a?.cwd||process.env.MYHI_CWD||process.cwd();if(!(0,external_fs_.existsSync)(r))try{(0,external_fs_.mkdirSync)(r,{recursive:!0}),console.log(`[\u521B\u5EFA] \u81EA\u52A8\u521B\u5EFA\u76EE\u5F55: ${r}`)}catch(t){typeof n=="function"&&n({ok:!1,error:`\u521B\u5EFA\u76EE\u5F55\u5931\u8D25: ${t.message}`});return}try{const t=manager.create({...a,owner:S.data.userName,userDir:S.data.dir});console.log(`[\u4F1A\u8BDD] ${S.data.userName} \u521B\u5EFA PTY \u4F1A\u8BDD "${t.title}" (${t.id})`),broadcastSessions(),typeof n=="function"&&n({ok:!0,session:t.toJSON()})}catch(t){console.error("[\u521B\u5EFA] PTY \u542F\u52A8\u5931\u8D25:",t.message),typeof n=="function"&&n({ok:!1,error:t.message})}}),S.on("kill",a=>{if(!hasPermission(S.data.role,"admin")){S.emit("error",{message:"\u6CA1\u6709\u5220\u9664\u4F1A\u8BDD\u7684\u6743\u9650"});return}console.log(`[\u4F1A\u8BDD] ${S.data.userName} \u5220\u9664\u4F1A\u8BDD ${a}`),manager.kill(a),_autoSpawned.delete(a),broadcastSessions()}),S.on("kick-user",({socketId:a,sessionId:n}={},r)=>{if(!hasPermission(S.data.role,"admin")){typeof r=="function"&&r({ok:!1,error:"\u6CA1\u6709\u8E22\u51FA\u7528\u6237\u7684\u6743\u9650"});return}const t=io.sockets.sockets.get(a);if(!t){typeof r=="function"&&r({ok:!1,error:"\u76EE\u6807\u7528\u6237\u4E0D\u5728\u7EBF"});return}const e=t.data.userName||"\u672A\u77E5";console.log(`[\u8E22\u51FA] ${S.data.userName} \u8E22\u51FA\u4E86\u7528\u6237 ${e} (${a})`),t.emit("kicked",{reason:`\u4F60\u5DF2\u88AB\u7BA1\u7406\u5458 ${S.data.userName} \u8E22\u51FA`}),t.disconnect(!0),typeof r=="function"&&r({ok:!0,name:e})}),S.on("list-viewers",({sessionId:a}={},n)=>{if(typeof n!="function")return;const r=a?manager.get(a):c;if(!r){n({ok:!1,error:"\u4F1A\u8BDD\u4E0D\u5B58\u5728"});return}const t=[];for(const e of r._viewers){const o=io.sockets.sockets.get(e);o&&t.push({socketId:e,userName:o.data.userName||"\u672A\u77E5",role:o.data.role||"viewer",isController:r.controlHolder===e})}n({ok:!0,viewers:t})})});const CONTROL_TIMEOUT=300*1e3;setInterval(()=>{for(const S of manager.listSessions())S.controlHolder&&S.lastInputTime&&Date.now()-S.lastInputTime>CONTROL_TIMEOUT&&(S.releaseControl(),io.emit("control-changed",{sessionId:S.id,holder:null,holderName:null}))},60*1e3);function showStartupInfo(){const S=httpServer.address().port;PORT=S;const d=getTailscaleIP(),c=`http://${d}:${S}/?token=${TOKEN}`;console.log(`
|
|
361
361
|
\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501`),console.log(" myhi \u2014 \u57FA\u4E8E Tailscale \u7684 Web \u7EC8\u7AEF"),console.log("\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501"),console.log(`
|
|
362
362
|
\u5730\u5740: http://${d}:${S}`),console.log(` \u5BC6\u7801: ${PASSWORD} (\u7F16\u8F91 ~/.myhi/password \u53EF\u4FEE\u6539)`),console.log(`
|