@wendongfly/myhi 1.0.25 → 1.0.26
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.html +12 -1
- package/dist/index.js +1 -1
- package/package.json +1 -1
package/dist/index.html
CHANGED
|
@@ -508,12 +508,23 @@
|
|
|
508
508
|
const connDot = document.getElementById('conn-dot');
|
|
509
509
|
const list = document.getElementById('list');
|
|
510
510
|
|
|
511
|
-
//
|
|
511
|
+
// 加载当前用户信息(独占模式显示用户名、退出按钮、锁定目录)
|
|
512
|
+
let _userDir = null;
|
|
512
513
|
fetch('/api/me').then(r => r.json()).then(data => {
|
|
513
514
|
if (data.exclusive && data.name) {
|
|
514
515
|
document.getElementById('user-name').textContent = data.name;
|
|
515
516
|
document.getElementById('logout-btn').style.display = '';
|
|
516
517
|
}
|
|
518
|
+
if (data.dir) {
|
|
519
|
+
_userDir = data.dir;
|
|
520
|
+
const cwdInput = document.getElementById('inp-cwd');
|
|
521
|
+
cwdInput.value = data.dir;
|
|
522
|
+
cwdInput.readOnly = true;
|
|
523
|
+
cwdInput.style.opacity = '0.6';
|
|
524
|
+
// 隐藏目录选择按钮和最近目录
|
|
525
|
+
cwdInput.parentElement.querySelector('button').style.display = 'none';
|
|
526
|
+
document.getElementById('recent-dirs').style.display = 'none';
|
|
527
|
+
}
|
|
517
528
|
}).catch(() => {});
|
|
518
529
|
|
|
519
530
|
function doLogout() {
|
package/dist/index.js
CHANGED
|
@@ -356,7 +356,7 @@ Content-Length: `+T+`\r
|
|
|
356
356
|
`).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(t=>t.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"];return this.permissionMode==="bypassPermissions"?c.push("--dangerously-skip-permissions"):this.permissionMode!=="default"&&c.push("--permission-mode",this.permissionMode),this.claudeSessionId&&c.push("--resume",this.claudeSessionId),new Promise((b,g)=>{const m={...process.env};delete m.CLAUDECODE,delete m.CLAUDE_CODE_ENTRYPOINT,delete m.CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC;const u=process.platform==="win32"?process.env.ComSpec||"cmd.exe":"claude",f=process.platform==="win32"?["/c","claude.cmd",...c]:c;this._proc=(0,external_child_process_namespaceObject.spawn)(u,f,{cwd:this.cwd,env:m,stdio:["ignore","pipe","pipe"]});let a="";this._proc.stdout.on("data",t=>{a+=t.toString();const r=a.split(`
|
|
357
357
|
`);a=r.pop();for(const i of r)if(i.trim())try{const e=JSON.parse(i);this._handleMessage(e)}catch{}}),this._proc.stderr.on("data",t=>{const r=t.toString().trim();r&&(console.error("[agent:stderr]",r),!r.includes("Warning:")&&!r.includes("DeprecationWarning")&&this.emit("agent:error",{message:r}))}),this._proc.on("close",t=>{if(a.trim())try{const r=JSON.parse(a.trim());this._handleMessage(r)}catch{}this._proc=null,this._busy=!1,this.emit("agent:busy",!1),b(t)}),this._proc.on("error",t=>{this._proc=null,this._busy=!1,this.emit("agent:busy",!1),this.emit("agent:error",{message:`\u542F\u52A8 Claude \u5931\u8D25: ${t.message}`}),g(t)})})}_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._proc.kill("SIGTERM"),this._proc=null,this._busy=!1,this.emit("agent:busy",!1),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,claudeSessionId:this.claudeSessionId,busy:this._busy}}}function listLocalClaudeSessions(S){const d=[];try{const c=(0,external_path_.join)((0,external_os_.homedir)(),".claude","projects");if(!(0,external_fs_.existsSync)(c))return d;for(const b of(0,external_fs_.readdirSync)(c,{withFileTypes:!0})){if(!b.isDirectory())continue;const g=(0,external_path_.join)(c,b.name);try{for(const m of(0,external_fs_.readdirSync)(g)){if(!m.endsWith(".jsonl"))continue;const u=m.replace(".jsonl",""),f=(0,external_path_.join)(g,m);try{const a=(0,external_fs_.readFileSync)(f,"utf8").split(`
|
|
358
358
|
`).filter(Boolean);let t=null,r=null,i=0,e="";for(const s of a)try{const n=JSON.parse(s);if(t||(t=n),r=n,i++,!e&&n.type==="human"&&n.message?.content){const p=n.message.content;if(typeof p=="string")e=p.slice(0,120);else if(Array.isArray(p)){const l=p.find(h=>h.type==="text");l&&(e=l.text?.slice(0,120)||"")}}}catch{}let o=b.name;process.platform==="win32"&&/^[A-Za-z]--/.test(o)?o=o[0]+":\\"+o.slice(3).replace(/-/g,"\\"):o.startsWith("-")&&(o=o.replace(/-/g,"/")),d.push({sessionId:u,projectDir:b.name,projectPath:o,messageCount:i,summary:e,createdAt:t?.timestamp||null,updatedAt:r?.timestamp||null})}catch{}}}catch{}}}catch{}return d.sort((c,b)=>new Date(b.updatedAt||0).getTime()-new Date(c.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.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};delete b.CLAUDECODE,delete b.CLAUDE_CODE_ENTRYPOINT,delete b.CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC,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}}}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(){const d=[...this._sessions.values()].map(b=>({...b.toJSON(),mode:"pty"})),c=[...this._agentSessions.values()].map(b=>b.toJSON());return[...d,...c]}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}));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});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(`
|
|
359
|
-
`)[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!==activeUser.token&&g!==TOKEN)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");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={name:f.name,dir:f.dir,token: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);if(c){if(activeUser&&activeUser.token===c){console.log(`[\u9000\u51FA] ${activeUser.name} \u9000\u51FA\u767B\u5F55`);for(const b of manager.listSessions())manager.kill(b.id),_autoSpawned.delete(b.id);activeUser=null}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});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)=>d.json(manager.list())),app.post("/api/sessions",checkAuth,express.json(),(S,d)=>{const c=manager.create(S.body||{});d.status(201).json(c.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)=>{d.json(listLocalClaudeSessions())}),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,i)=>r.name.localeCompare(i.name)),a=(0,external_path_.join)(u,".."),t=!b||(0,external_path_.resolve)((0,external_path_.normalize)(a)).startsWith(b);d.json({current:u,parent:t&&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 t=f.originalname.match(/\.[^.]+$/)?.[0]||".jpg";a(null,`${Date.now()}-${(0,external_crypto_.randomBytes)(3).toString("hex")}${t}`)}}),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"))}),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}`);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 t=manager.get(a);if(!t){S.emit("error",{message:`\u4F1A\u8BDD ${a} \u672A\u627E\u5230`});return}f(),c=t,c.addViewer(S.id),t.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,t.title)),console.log(`[\u52A0\u5165] ${S.data.userName} \u52A0\u5165\u4F1A\u8BDD "${t.title}" (${a}) \u6A21\u5F0F=${t.mode||"pty"}`),S.emit("joined",{...t.toJSON(),role:S.data.role}),t.mode==="agent"?t._history?.length&&S.emit("agent:history",t._history):t._scrollback?.length&&S.emit("output",t._scrollback),io.emit("sessions",manager.list())}),S.on("disconnect",()=>{console.log(`[\u65AD\u5F00] ${S.data.userName} \u79BB\u5F00\u4F1A\u8BDD "${c?.title||"?"}" id=${S.id}`),f(),io.emit("sessions",manager.list())}),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(t){console.error("[agent:query] \u9519\u8BEF:",t.message),S.emit("agent:error",{message:t.message})}}),S.on("agent:interrupt",()=>{!c||c.mode!=="agent"||c.interrupt()}),S.on("create-agent",(a,t)=>{if(!hasPermission(S.data.role,"admin")){typeof t=="function"&&t({ok:!1,error:"\u6CA1\u6709\u521B\u5EFA\u4F1A\u8BDD\u7684\u6743\u9650"});return}if(S.data.dir){const r=(0,external_path_.resolve)((0,external_path_.normalize)(S.data.dir));if(a?.cwd){if(!(0,external_path_.resolve)((0,external_path_.normalize)(a.cwd)).startsWith(r)){typeof t=="function"&&t({ok:!1,error:"\u4E0D\u80FD\u5728\u7ED1\u5B9A\u76EE\u5F55\u4E4B\u5916\u521B\u5EFA\u4F1A\u8BDD"});return}}else a={...a,cwd:r}}try{const r=manager.createAgent(a||{});console.log(`[\u4F1A\u8BDD] ${S.data.userName} \u521B\u5EFA Agent \u4F1A\u8BDD "${r.title}" (${r.id})`),io.emit("sessions",manager.list()),typeof t=="function"&&t({ok:!0,session:r.toJSON()})}catch(r){typeof t=="function"&&t({ok:!1,error:r.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 t=a?manager.get(a):c;if(t){if(!hasPermission(S.data.role,"operator")){S.emit("control-denied",{reason:"\u4F60\u7684\u89D2\u8272\u6CA1\u6709\u63A7\u5236\u6743\u9650"});return}if(t.controlHolder&&t.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}t.takeControl(S.id,S.data.userName),console.log(`[\u63A7\u5236] ${S.data.userName} \u83B7\u53D6\u4F1A\u8BDD "${t.title}" \u7684\u63A7\u5236\u6743`),io.emit("control-changed",{sessionId:t.id,holder:S.id,holderName:S.data.userName})}}),S.on("release-control",({sessionId:a}={})=>{const t=a?manager.get(a):c;t&&t.isController(S.id)&&(console.log(`[\u63A7\u5236] ${S.data.userName} \u91CA\u653E\u4F1A\u8BDD "${t.title}" \u7684\u63A7\u5236\u6743`),t.releaseControl(),io.emit("control-changed",{sessionId:t.id,holder:null,holderName:null}))}),S.on("set-mode",({sessionId:a,mode:t}={})=>{const r=a?manager.get(a):c;r&&(r.permissionMode=t,io.emit("mode-changed",{sessionId:r.id,mode:t}))}),S.on("rename",({sessionId:a,title:t}={})=>{const r=a?manager.get(a):c;!r||!t||(r.title=t,io.emit("session-renamed",{sessionId:r.id,title:t}),io.emit("sessions",manager.list()))}),S.on("resize",({cols:a,rows:t})=>c?.resize(a,t)),S.on("list",()=>S.emit("sessions",manager.list())),S.on("dirs",(a,t)=>{if(typeof t!="function")return;const r=S.data.dir?(0,external_path_.resolve)((0,external_path_.normalize)(S.data.dir)):null,i=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+="\\"),r&&!(0,external_path_.resolve)((0,external_path_.normalize)(e)).startsWith(r)&&(e=r);try{const o=(0,external_fs_.readdirSync)(e,{withFileTypes:!0}).filter(p=>p.isDirectory()&&!p.name.startsWith(".")).map(p=>({name:p.name,path:e.replace(/[/\\]+$/,"")+i+p.name})).sort((p,l)=>p.name.localeCompare(l.name)),s=(0,external_path_.join)(e,".."),n=!r||(0,external_path_.resolve)((0,external_path_.normalize)(s)).startsWith(r);t({ok:!0,current:e,parent:n&&s!==e?s:null,dirs:o})}catch(o){t({ok:!1,error:o.message})}}),S.on("create",(a,t)=>{if(!hasPermission(S.data.role,"admin")){typeof t=="function"&&t({ok:!1,error:"\u6CA1\u6709\u521B\u5EFA\u4F1A\u8BDD\u7684\u6743\u9650"});return}if(S.data.dir){const r=(0,external_path_.resolve)((0,external_path_.normalize)(S.data.dir));if(a?.cwd){if(!(0,external_path_.resolve)((0,external_path_.normalize)(a.cwd)).startsWith(r)){typeof t=="function"&&t({ok:!1,error:"\u4E0D\u80FD\u5728\u7ED1\u5B9A\u76EE\u5F55\u4E4B\u5916\u521B\u5EFA\u4F1A\u8BDD"});return}}else a={...a,cwd:r}}try{const r=manager.create(a||{});console.log(`[\u4F1A\u8BDD] ${S.data.userName} \u521B\u5EFA PTY \u4F1A\u8BDD "${r.title}" (${r.id})`),io.emit("sessions",manager.list()),typeof t=="function"&&t({ok:!0,session:r.toJSON()})}catch(r){console.error("[\u521B\u5EFA] PTY \u542F\u52A8\u5931\u8D25:",r.message),typeof t=="function"&&t({ok:!1,error:r.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),io.emit("sessions",manager.list())})});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(`
|
|
359
|
+
`)[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!==activeUser.token&&g!==TOKEN)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");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={name:f.name,dir:f.dir,token: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);if(c){if(activeUser&&activeUser.token===c){console.log(`[\u9000\u51FA] ${activeUser.name} \u9000\u51FA\u767B\u5F55`);for(const b of manager.listSessions())manager.kill(b.id),_autoSpawned.delete(b.id);activeUser=null}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});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)=>d.json(manager.list())),app.post("/api/sessions",checkAuth,express.json(),(S,d)=>{const c=manager.create(S.body||{});d.status(201).json(c.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)=>{d.json(listLocalClaudeSessions())}),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,i)=>r.name.localeCompare(i.name)),a=(0,external_path_.join)(u,".."),t=!b||(0,external_path_.resolve)((0,external_path_.normalize)(a)).startsWith(b);d.json({current:u,parent:t&&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 t=f.originalname.match(/\.[^.]+$/)?.[0]||".jpg";a(null,`${Date.now()}-${(0,external_crypto_.randomBytes)(3).toString("hex")}${t}`)}}),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"))}),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}`);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 t=manager.get(a);if(!t){S.emit("error",{message:`\u4F1A\u8BDD ${a} \u672A\u627E\u5230`});return}f(),c=t,c.addViewer(S.id),t.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,t.title)),console.log(`[\u52A0\u5165] ${S.data.userName} \u52A0\u5165\u4F1A\u8BDD "${t.title}" (${a}) \u6A21\u5F0F=${t.mode||"pty"}`),S.emit("joined",{...t.toJSON(),role:S.data.role}),t.mode==="agent"?t._history?.length&&S.emit("agent:history",t._history):t._scrollback?.length&&S.emit("output",t._scrollback),io.emit("sessions",manager.list())}),S.on("disconnect",()=>{console.log(`[\u65AD\u5F00] ${S.data.userName} \u79BB\u5F00\u4F1A\u8BDD "${c?.title||"?"}" id=${S.id}`),f(),io.emit("sessions",manager.list())}),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(t){console.error("[agent:query] \u9519\u8BEF:",t.message),S.emit("agent:error",{message:t.message})}}),S.on("agent:interrupt",()=>{!c||c.mode!=="agent"||c.interrupt()}),S.on("create-agent",(a,t)=>{if(!hasPermission(S.data.role,"admin")){typeof t=="function"&&t({ok:!1,error:"\u6CA1\u6709\u521B\u5EFA\u4F1A\u8BDD\u7684\u6743\u9650"});return}S.data.dir&&(a={...a,cwd:(0,external_path_.resolve)((0,external_path_.normalize)(S.data.dir))});try{const r=manager.createAgent(a||{});console.log(`[\u4F1A\u8BDD] ${S.data.userName} \u521B\u5EFA Agent \u4F1A\u8BDD "${r.title}" (${r.id})`),io.emit("sessions",manager.list()),typeof t=="function"&&t({ok:!0,session:r.toJSON()})}catch(r){typeof t=="function"&&t({ok:!1,error:r.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 t=a?manager.get(a):c;if(t){if(!hasPermission(S.data.role,"operator")){S.emit("control-denied",{reason:"\u4F60\u7684\u89D2\u8272\u6CA1\u6709\u63A7\u5236\u6743\u9650"});return}if(t.controlHolder&&t.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}t.takeControl(S.id,S.data.userName),console.log(`[\u63A7\u5236] ${S.data.userName} \u83B7\u53D6\u4F1A\u8BDD "${t.title}" \u7684\u63A7\u5236\u6743`),io.emit("control-changed",{sessionId:t.id,holder:S.id,holderName:S.data.userName})}}),S.on("release-control",({sessionId:a}={})=>{const t=a?manager.get(a):c;t&&t.isController(S.id)&&(console.log(`[\u63A7\u5236] ${S.data.userName} \u91CA\u653E\u4F1A\u8BDD "${t.title}" \u7684\u63A7\u5236\u6743`),t.releaseControl(),io.emit("control-changed",{sessionId:t.id,holder:null,holderName:null}))}),S.on("set-mode",({sessionId:a,mode:t}={})=>{const r=a?manager.get(a):c;r&&(r.permissionMode=t,io.emit("mode-changed",{sessionId:r.id,mode:t}))}),S.on("rename",({sessionId:a,title:t}={})=>{const r=a?manager.get(a):c;!r||!t||(r.title=t,io.emit("session-renamed",{sessionId:r.id,title:t}),io.emit("sessions",manager.list()))}),S.on("resize",({cols:a,rows:t})=>c?.resize(a,t)),S.on("list",()=>S.emit("sessions",manager.list())),S.on("dirs",(a,t)=>{if(typeof t!="function")return;const r=S.data.dir?(0,external_path_.resolve)((0,external_path_.normalize)(S.data.dir)):null,i=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+="\\"),r&&!(0,external_path_.resolve)((0,external_path_.normalize)(e)).startsWith(r)&&(e=r);try{const o=(0,external_fs_.readdirSync)(e,{withFileTypes:!0}).filter(p=>p.isDirectory()&&!p.name.startsWith(".")).map(p=>({name:p.name,path:e.replace(/[/\\]+$/,"")+i+p.name})).sort((p,l)=>p.name.localeCompare(l.name)),s=(0,external_path_.join)(e,".."),n=!r||(0,external_path_.resolve)((0,external_path_.normalize)(s)).startsWith(r);t({ok:!0,current:e,parent:n&&s!==e?s:null,dirs:o})}catch(o){t({ok:!1,error:o.message})}}),S.on("create",(a,t)=>{if(!hasPermission(S.data.role,"admin")){typeof t=="function"&&t({ok:!1,error:"\u6CA1\u6709\u521B\u5EFA\u4F1A\u8BDD\u7684\u6743\u9650"});return}S.data.dir&&(a={...a,cwd:(0,external_path_.resolve)((0,external_path_.normalize)(S.data.dir))});try{const r=manager.create(a||{});console.log(`[\u4F1A\u8BDD] ${S.data.userName} \u521B\u5EFA PTY \u4F1A\u8BDD "${r.title}" (${r.id})`),io.emit("sessions",manager.list()),typeof t=="function"&&t({ok:!0,session:r.toJSON()})}catch(r){console.error("[\u521B\u5EFA] PTY \u542F\u52A8\u5931\u8D25:",r.message),typeof t=="function"&&t({ok:!1,error:r.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),io.emit("sessions",manager.list())})});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(`
|
|
360
360
|
\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(`
|
|
361
361
|
\u5730\u5740: http://${d}:${S}`),console.log(` \u5BC6\u7801: ${PASSWORD} (\u7F16\u8F91 ~/.myhi/password \u53EF\u4FEE\u6539)`),console.log(`
|
|
362
362
|
\u626B\u63CF\u4E8C\u7EF4\u7801\u5728\u624B\u673A\u4E0A\u6253\u5F00:
|