@wendongfly/zihi 1.1.11 → 1.1.13

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/chat.html CHANGED
@@ -1812,6 +1812,7 @@
1812
1812
  list.innerHTML = '<div class="fp-empty">加载中...</div>';
1813
1813
  try {
1814
1814
  var r = await fetch('/api/files/list?path=' + encodeURIComponent(fpCurrentPath) + '&' + fpQ);
1815
+ if (!r.ok && r.status === 401) { location.href = '/login'; return; }
1815
1816
  var data = await r.json();
1816
1817
  if (data.error) { list.innerHTML = '<div class="fp-empty">' + escHtml(data.error) + '</div>'; return; }
1817
1818
  document.getElementById('fp-path').textContent = data.current || '';
@@ -1847,6 +1848,7 @@
1847
1848
  headers: { 'Content-Type': 'application/json' },
1848
1849
  body: JSON.stringify({ path: dirPath, sessionId: SESSION_ID }),
1849
1850
  });
1851
+ if (!r.ok && r.status === 401) { location.href = '/login'; return; }
1850
1852
  var data = await r.json();
1851
1853
  if (data.ok) fpLoadFiles();
1852
1854
  else alert('创建失败: ' + (data.error || '未知错误'));
@@ -1861,6 +1863,7 @@
1861
1863
  headers: { 'Content-Type': 'application/json' },
1862
1864
  body: JSON.stringify({ path: path, sessionId: SESSION_ID }),
1863
1865
  });
1866
+ if (!r.ok && r.status === 401) { location.href = '/login'; return; }
1864
1867
  var data = await r.json();
1865
1868
  if (data.ok) fpLoadFiles();
1866
1869
  else alert('删除失败: ' + (data.error || '未知错误'));
@@ -1892,6 +1895,7 @@
1892
1895
  var form = new FormData();
1893
1896
  for (var i = 0; i < files.length; i++) form.append('files', files[i], files[i].webkitRelativePath || files[i].name);
1894
1897
  var r = await fetch('/api/files/upload?path=' + encodeURIComponent(fpCurrentPath) + '&' + fpQ, { method: 'POST', body: form });
1898
+ if (!r.ok && r.status === 401) { location.href = '/login'; return; }
1895
1899
  var data = await r.json();
1896
1900
  if (data.ok) {
1897
1901
  progress.textContent = '已上传 ' + data.files.length + ' 个文件';
package/dist/index.html CHANGED
@@ -357,7 +357,7 @@
357
357
  border: none; font-size: 0.9rem; font-weight: 600; cursor: pointer;
358
358
  }
359
359
  .dir-btn.primary { background: var(--accent); color: #fff; }
360
- .dir-btn.cancel { background: var(--bg); color: var(--text); }
360
+ .dir-btn.cancel { background: var(--bg); color: var(--muted); border: 1px solid var(--border); }
361
361
 
362
362
  /* ── Kill confirm ── */
363
363
  #kill-menu {
@@ -490,7 +490,7 @@
490
490
  <div id="dir-list"></div>
491
491
  <div id="dir-actions">
492
492
  <button class="dir-btn cancel" onclick="closeDirPicker()">取消</button>
493
- <button class="dir-btn" onclick="createSubDir()" style="background:#21262d;border-color:#30363d">新建文件夹</button>
493
+ <button class="dir-btn" onclick="createSubDir()" style="background:#1f6feb;color:#fff;border:none">新建文件夹</button>
494
494
  <button class="dir-btn primary" onclick="selectDir()">选择此目录</button>
495
495
  </div>
496
496
  </div>
@@ -927,19 +927,15 @@
927
927
  function createSubDir() {
928
928
  var name = prompt('新建文件夹名称:');
929
929
  if (!name || !name.trim()) return;
930
- // 在当前目录下创建子文件夹
930
+ // 在当前目录下创建子文件夹(通过 socket 的 mkdir 事件)
931
931
  var newPath = _dirCurrent.replace(/[\\/]+$/, '') + '/' + name.trim();
932
- fetch('/api/files/mkdir', {
933
- method: 'POST',
934
- headers: { 'Content-Type': 'application/json' },
935
- body: JSON.stringify({ path: newPath }),
936
- }).then(function(r) { return r.json(); }).then(function(data) {
937
- if (data.ok || data.error?.includes('已存在')) {
932
+ socket.emit('mkdir', newPath, function(data) {
933
+ if (data.ok) {
938
934
  loadDirs(_dirCurrent); // 刷新列表
939
935
  } else {
940
936
  alert('创建失败: ' + (data.error || '未知错误'));
941
937
  }
942
- }).catch(function(e) { alert('创建失败: ' + e.message); });
938
+ });
943
939
  }
944
940
  function escH(s) { return s.replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;'); }
945
941
 
package/dist/index.js CHANGED
@@ -465,7 +465,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
465
465
  `).filter(Boolean).slice(-20);for(const x of h)try{const $=JSON.parse(x);if($.type==="human"&&$.message?.content){const b=typeof $.message.content=="string"?$.message.content:$.message.content.find((w=>w.type==="text"))?.text||"";b&&this._history.push({type:"user",content:b,timestamp:$.timestamp})}else $.type==="assistant"&&$.message?.content?this._history.push({type:"assistant",message:$.message,timestamp:$.timestamp}):$.type==="result"&&this._history.push({type:"result",total_cost_usd:$.costUSD||$.total_cost_usd,timestamp:$.timestamp})}catch{}return}}catch(t){console.warn("[agent] \u52A0\u8F7D\u6062\u590D\u4F1A\u8BDD\u5386\u53F2\u5931\u8D25:",t.message)}}get isBusy(){return this._busy}addViewer(t,u){this._viewers.add(t),u&&this._viewerNames.set(t,u)}removeViewer(t){this._viewers.delete(t),this._viewerNames.delete(t)}get viewerCount(){return new Set(this._viewerNames.values()).size||this._viewers.size}takeControl(t,u){this.controlHolder=t,this.controlHolderName=u||null,this.lastInputTime=Date.now()}releaseControl(){this.controlHolder=null,this.controlHolderName=null,this.lastInputTime=null}isController(t){return this.controlHolder===t}toJSON(){return{id:this.id,title:this.title,cwd:this.cwd,createdAt:this.createdAt,mode:"agent",alive:this.alive,viewers:this.viewerCount,controlHolder:this.controlHolder,controlHolderName:this.controlHolderName,permissionMode:this.permissionMode,owner:this.owner,claudeSessionId:this.claudeSessionId,busy:this._busy,usage:this._usage}}}let _claudeSessionsCache=null,_claudeSessionsCacheTime=0;const CLAUDE_SESSIONS_CACHE_TTL=3e4;function listLocalClaudeSessions(r){const t=Date.now();if(_claudeSessionsCache&&t-_claudeSessionsCacheTime<CLAUDE_SESSIONS_CACHE_TTL){if(!r)return _claudeSessionsCache;const h=(0,external_path_.resolve)((0,external_path_.normalize)(r)).replace(/[\\/]+$/,"");return _claudeSessionsCache.filter((x=>(0,external_path_.resolve)((0,external_path_.normalize)(x.projectPath)).replace(/[\\/]+$/,"").startsWith(h)))}const u=[],d=r?(0,external_path_.resolve)((0,external_path_.normalize)(r)).replace(/[\\/]+$/,""):null;try{const h=(0,external_path_.join)((0,external_os_.homedir)(),".claude","projects");if(!(0,external_fs_.existsSync)(h))return u;for(const x of(0,external_fs_.readdirSync)(h,{withFileTypes:!0})){if(!x.isDirectory())continue;const $=(0,external_path_.join)(h,x.name);try{for(const b of(0,external_fs_.readdirSync)($)){if(!b.endsWith(".jsonl"))continue;const w=b.replace(".jsonl",""),j=(0,external_path_.join)($,b);try{const N=(0,external_fs_.readFileSync)(j,"utf8").split(`
466
466
  `).filter(Boolean);let I=null,U=null,z=0,O="";for(const L of N)try{const W=JSON.parse(L);if(I||(I=W),U=W,z++,!O&&W.type==="human"&&W.message?.content){const H=W.message.content;if(typeof H=="string")O=H.slice(0,120);else if(Array.isArray(H)){const V=H.find((J=>J.type==="text"));V&&(O=V.text?.slice(0,120)||"")}}}catch{}let B=x.name;if(process.platform==="win32"&&/^[A-Za-z]--/.test(B)?B=B[0]+":\\"+B.slice(3).replace(/-/g,"\\"):B.startsWith("-")&&(B=B.replace(/-/g,"/")),d&&!(0,external_path_.resolve)((0,external_path_.normalize)(B)).replace(/[\\/]+$/,"").startsWith(d))continue;u.push({sessionId:w,projectDir:x.name,projectPath:B,messageCount:z,summary:O,createdAt:I?.timestamp||null,updatedAt:U?.timestamp||null})}catch{}}}catch{}}}catch{}u.sort(((h,x)=>new Date(x.updatedAt||0).getTime()-new Date(h.updatedAt||0).getTime()));const p=u.slice(0,50);return _claudeSessionsCache=p,_claudeSessionsCacheTime=Date.now(),p.slice(0,20)}const CONFIG_DIR=(0,external_path_.join)((0,external_os_.homedir)(),".zihi"),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(r){try{(0,external_fs_.mkdirSync)(CONFIG_DIR,{recursive:!0});const t=SESSIONS_FILE+".tmp";(0,external_fs_.writeFileSync)(t,JSON.stringify(r,null,2),{mode:384}),(0,external_fs_.renameSync)(t,SESSIONS_FILE)}catch(t){console.error("[sessions] \u6301\u4E45\u5316\u5199\u5165\u5931\u8D25:",t.message)}}class SessionManager{constructor(){this._agentSessions=new Map;for(const t of loadSavedConfigs())try{if(t.claudeSessionId){const u=(0,external_crypto_.randomUUID)(),d=new AgentSession(u,{...t,resumeSessionId:t.claudeSessionId});d.on("session-id",(()=>this._persist())),this._agentSessions.set(u,d),console.log(`[\u4F1A\u8BDD] \u6062\u590D Agent "${t.title}" (claude: ${t.claudeSessionId.slice(0,8)}...)`)}}catch(u){console.warn(`[\u4F1A\u8BDD] \u6062\u590D "${t.title}" \u5931\u8D25:`,u.message)}}createAgent(t={}){const u=(0,external_crypto_.randomUUID)(),d=new AgentSession(u,t);return this._agentSessions.set(u,d),d.on("session-id",(()=>this._persist())),this._persist(),d}get(t){return this._agentSessions.get(t)}list(t,u=!1){let d=[...this._agentSessions.values()].map((p=>p.toJSON()));return t&&!u&&(d=d.filter((p=>!p.owner||p.owner===t))),d}listSessions(){return[...this._agentSessions.values()]}kill(t){const u=this._agentSessions.get(t);u&&(u.kill(),this._agentSessions.delete(t),this._persist())}persist(){this._persist()}_persist(){const t=[...this._agentSessions.values()].filter((u=>u.alive)).map((u=>({mode:"agent",title:u.title,cwd:u.cwd,permissionMode:u.permissionMode||void 0,owner:u.owner||void 0,userDir:u.userDir||void 0,claudeSessionId:u.claudeSessionId||void 0})));writeSavedConfigs(t)}}const roles_CONFIG_DIR=(0,external_path_.join)((0,external_os_.homedir)(),".zihi"),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(r){console.warn("[\u89D2\u8272] \u52A0\u8F7D roles.json \u5931\u8D25:",r.message)}return rolesConfig=null,null}function lookupPassword(r){if(!rolesConfig?.passwords)return null;const t=rolesConfig.passwords[r];return t?{name:t.name||"\u7528\u6237",role:t.role||"viewer"}:null}function hasPermission(r,t){return(ROLE_LEVELS[r]||0)>=(ROLE_LEVELS[t]||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(r){console.warn("[\u7528\u6237] \u52A0\u8F7D users.json \u5931\u8D25:",r.message)}return usersConfig=null,null}function hasUsers(){return usersConfig!==null&&usersConfig.users&&Object.keys(usersConfig.users).length>0}function isExclusiveMode(){return hasUsers()&&usersConfig.exclusive===!0}function setExclusiveMode(r){usersConfig||(usersConfig={users:{}}),usersConfig.exclusive=!!r,_writeUsers()}function lookupUser(r){if(!usersConfig?.users)return null;const t=usersConfig.users[r];return t?{name:t.name||"\u7528\u6237",dir:t.dir}:null}function listUsers(){return usersConfig?.users?Object.entries(usersConfig.users).map((([r,t])=>({password:r.slice(0,2)+"*".repeat(r.length-2),name:t.name,dir:t.dir}))):[]}function addUser(r,t,u){usersConfig||(usersConfig={users:{}}),usersConfig.users||(usersConfig.users={}),usersConfig.users[r]={name:t,dir:u},_writeUsers()}function removeUser(r){return!usersConfig?.users||!usersConfig.users[r]?!1:(delete usersConfig.users[r],_writeUsers(),!0)}function _writeUsers(){(0,external_fs_.mkdirSync)(roles_CONFIG_DIR,{recursive:!0}),(0,external_fs_.writeFileSync)(USERS_FILE,JSON.stringify(usersConfig,null,2),{mode:384})}const server_dirname=(0,external_path_.dirname)((0,external_url_.fileURLToPath)(import.meta.url));let PORT=parseInt(process.env.PORT,10)||12345;const HOST=process.env.HOST||"0.0.0.0",PORT_EXPLICIT=!!process.env.PORT,DEFAULT_ROOT=(0,external_path_.resolve)((0,external_path_.normalize)(process.env.ZIHI_CWD||process.cwd()));function getEffectiveRoot(r){return r?(0,external_path_.resolve)((0,external_path_.normalize)(r)):DEFAULT_ROOT}const configDir=(0,external_path_.join)((0,external_os_.homedir)(),".zihi");(0,external_fs_.mkdirSync)(configDir,{recursive:!0});const pidFile=(0,external_path_.join)(configDir,"daemon.pid"),IS_DAEMON_CHILD=!!process.env.ZIHI_DAEMON;(function r(){if(!IS_DAEMON_CHILD){try{const t=parseInt((0,external_fs_.readFileSync)(pidFile,"utf8").trim(),10);if(t&&t!==process.pid){let u=!1;try{process.kill(t,0),u=!0}catch{}if(u){console.log(`[zihi] \u68C0\u6D4B\u5230\u5DF2\u8FD0\u884C\u7684\u5B9E\u4F8B (PID ${t})\uFF0C\u6B63\u5728\u505C\u6B62...`);try{process.platform==="win32"?(0,external_child_process_namespaceObject.execSync)(`taskkill /PID ${t} /F /T`,{stdio:"pipe",timeout:5e3}):process.kill(t,"SIGTERM")}catch(h){console.warn(`[zihi] \u505C\u6B62\u65E7\u8FDB\u7A0B\u5931\u8D25: ${h.message?.split(`
467
467
  `)[0]||h}`)}const d=Date.now()+5e3;for(;Date.now()<d;){try{process.kill(t,0)}catch{break}const h=Date.now()+200;for(;Date.now()<h;);}let p=!1;try{process.kill(t,0),p=!0}catch{}p&&(console.error(`[zihi] \u65E0\u6CD5\u505C\u6B62\u65E7\u8FDB\u7A0B (PID ${t})\uFF0C\u8BF7\u624B\u52A8\u6267\u884C\uFF1A`),console.error(" zihi stop"),console.error(` \u6216: ${process.platform==="win32"?`taskkill /PID ${t} /F`:`kill -9 ${t}`}`),process.exit(1))}}}catch{}IS_DAEMON_CHILD||(0,external_fs_.writeFileSync)(pidFile,String(process.pid))}})();function cleanupPid(){try{parseInt((0,external_fs_.readFileSync)(pidFile,"utf8").trim(),10)===process.pid&&(0,external_fs_.unlinkSync)(pidFile)}catch{}}function gracefulShutdown(){try{if(typeof manager<"u"){try{manager.persist()}catch{}for(const r of manager.listSessions())try{r.kill()}catch{}}}catch{}cleanupPid()}process.on("exit",cleanupPid),process.on("SIGTERM",(()=>{gracefulShutdown(),process.exit(0)})),process.on("SIGINT",(()=>{gracefulShutdown(),process.exit(0)})),process.on("uncaughtException",(r=>{console.error("[zihi] \u672A\u6355\u83B7\u5F02\u5E38:",r.message),gracefulShutdown(),process.exit(1)})),process.on("unhandledRejection",(r=>{console.error("[zihi] \u672A\u5904\u7406\u7684 Promise \u62D2\u7EDD:",r)}));function readOrCreate(r,t){try{if((0,external_fs_.existsSync)(r))return(0,external_fs_.readFileSync)(r,"utf8").trim()}catch{}const u=t();return(0,external_fs_.writeFileSync)(r,u,{mode:384}),u}const TOKEN=readOrCreate((0,external_path_.join)(configDir,"token"),(()=>(0,external_crypto_.randomBytes)(16).toString("hex")));let PASSWORD=readOrCreate((0,external_path_.join)(configDir,"password"),(()=>String(Math.floor(1e3+Math.random()*9e3))));loadRoles(),loadUsers();let activeUser=null;const userSessions=new Map;userSessions.set(TOKEN,{role:"admin",name:"\u7BA1\u7406\u5458",permanent:!0});const SESSION_MAX_AGE=10080*60*1e3;setInterval((()=>{const r=Date.now();for(const[t,u]of userSessions)u.permanent||(!u.lastUsed||r-u.lastUsed>SESSION_MAX_AGE)&&userSessions.delete(t)}),3600*1e3);function getTailscaleIP(){try{return(0,external_child_process_namespaceObject.execSync)("tailscale ip -4",{timeout:3e3,stdio:"pipe"}).toString().trim().split(`
468
- `)[0]}catch{}try{if(process.platform==="win32"){const t=(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(t)return t[1]}else try{const t=(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(t)return t[1]}catch{const r=(0,external_child_process_namespaceObject.execSync)("route -n get default 2>/dev/null || route -n 2>/dev/null",{timeout:3e3,stdio:"pipe"}).toString(),t=r.match(/interface:\s*(\S+)/i)||r.match(/0\.0\.0\.0\s+(\d+\.\d+\.\d+\.\d+)/);if(t){const u=(0,external_os_.networkInterfaces)()[t[1]];if(u){const d=u.find((p=>p.family==="IPv4"&&!p.internal));if(d)return d.address}if(/^\d+\.\d+\.\d+\.\d+$/.test(t[1]))return t[1]}}}catch{}for(const r of Object.values((0,external_os_.networkInterfaces)()))for(const t of r)if(t.family==="IPv4"&&!t.internal)return t.address;return"localhost"}function parseCookies(r=""){const t={};for(const u of r.split(";")){const d=u.indexOf("=");d>0&&(t[u.slice(0,d).trim()]=u.slice(d+1).trim())}return t}const SESSION_COOKIE="zihi_sid",COOKIE_OPTS="HttpOnly; SameSite=Lax; Path=/; Max-Age=31536000";function setSessionCookie(r,t){r.setHeader("Set-Cookie",`${SESSION_COOKIE}=${t||TOKEN}; ${COOKIE_OPTS}`)}function getSessionToken(r){return parseCookies(r)[SESSION_COOKIE]||null}function hasValidSession(r){const t=getSessionToken(r);if(t&&userSessions.has(t)){const u=userSessions.get(t);return u&&(u.lastUsed=Date.now()),!0}return!1}function getUserInfo(r){const t=getSessionToken(r);return t?userSessions.get(t):null}const MIME_MAP={".html":"text/html",".css":"text/css",".js":"text/javascript",".mjs":"text/javascript",".json":"application/json",".xml":"application/xml",".txt":"text/plain",".md":"text/plain",".csv":"text/csv",".log":"text/plain",".sh":"text/plain",".py":"text/plain",".ts":"text/plain",".jsx":"text/plain",".tsx":"text/plain",".yml":"text/plain",".yaml":"text/plain",".png":"image/png",".jpg":"image/jpeg",".jpeg":"image/jpeg",".gif":"image/gif",".svg":"image/svg+xml",".webp":"image/webp",".ico":"image/x-icon",".bmp":"image/bmp",".mp3":"audio/mpeg",".wav":"audio/wav",".ogg":"audio/ogg",".flac":"audio/flac",".m4a":"audio/mp4",".aac":"audio/aac",".wma":"audio/x-ms-wma",".mp4":"video/mp4",".webm":"video/webm",".mkv":"video/x-matroska",".avi":"video/x-msvideo",".mov":"video/quicktime",".flv":"video/x-flv",".pdf":"application/pdf",".zip":"application/zip",".gz":"application/gzip",".tar":"application/x-tar",".7z":"application/x-7z-compressed",".rar":"application/x-rar-compressed",".doc":"application/msword",".docx":"application/vnd.openxmlformats-officedocument.wordprocessingml.document",".xls":"application/vnd.ms-excel",".xlsx":"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",".ppt":"application/vnd.ms-powerpoint",".pptx":"application/vnd.openxmlformats-officedocument.presentationml.presentation",".woff":"font/woff",".woff2":"font/woff2",".ttf":"font/ttf",".eot":"application/vnd.ms-fontobject"};function getMime(r){return MIME_MAP[(0,external_path_.extname)(r).toLowerCase()]||"application/octet-stream"}function sanitizeError(r){return(r||"\u64CD\u4F5C\u5931\u8D25").replace(/[<>"'&]/g,(t=>({"<":"&lt;",">":"&gt;",'"':"&quot;","'":"&#39;","&":"&amp;"})[t]))}function resolveUserFilePath(r,t){const u=getUserInfo(r.headers.cookie),d=r.query.sessionId||r.body?.sessionId,p=d?manager.get(d):null,h=p?.cwd?(0,external_path_.resolve)((0,external_path_.normalize)(p.cwd)):null,x=getEffectiveRoot(u?.dir),$=h&&h.startsWith(x)?h:x;let b=t?(0,external_path_.resolve)((0,external_path_.normalize)($),(0,external_path_.normalize)(t)):$;if(process.platform==="win32"&&/^[A-Za-z]:$/.test(b)&&(b+="\\"),!b.startsWith($))return{error:"\u8BBF\u95EE\u88AB\u62D2\u7EDD\uFF1A\u8DEF\u5F84\u8D85\u51FA\u5141\u8BB8\u8303\u56F4",status:403};try{if(!(0,external_fs_.realpathSync)(b).startsWith($))return{error:"\u8BBF\u95EE\u88AB\u62D2\u7EDD\uFF1A\u7B26\u53F7\u94FE\u63A5\u6307\u5411\u5141\u8BB8\u8303\u56F4\u5916",status:403}}catch{}return{resolved:b,userRoot:$,info:u}}const app=express();app.set("trust proxy","loopback"),app.use(((r,t,u)=>{t.setHeader("Referrer-Policy","no-referrer"),t.setHeader("X-Content-Type-Options","nosniff"),u()}));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:(r,t)=>{if(!r||_corsAllowed.test(r))return t(null,!0);t(new Error("CORS \u4E0D\u5141\u8BB8\u6B64\u6765\u6E90"),!1)},credentials:!0},transports:["polling","websocket"],pingTimeout:6e4,pingInterval:25e3,upgradeTimeout:3e4,allowUpgrades:!0,maxHttpBufferSize:5e6});io.attach(httpServer);const manager=new SessionManager,uploadDir=(0,external_path_.join)(configDir,"uploads");(0,external_fs_.mkdirSync)(uploadDir,{recursive:!0});function checkAuth(r,t,u){const d=r.query.token;if(d&&userSessions.has(d)){const p=userSessions.get(d);if(p?.onetime){userSessions.delete(d);const x=(0,external_crypto_.randomBytes)(16).toString("hex");userSessions.set(x,{role:p.role,name:p.name,lastUsed:Date.now()}),setSessionCookie(t,x)}else setSessionCookie(t,d);const h=r.path+(Object.keys(r.query).filter((x=>x!=="token")).length?"?"+new URLSearchParams(Object.fromEntries(Object.entries(r.query).filter((([x])=>x!=="token")))):"");return t.redirect(h)}if(hasValidSession(r.headers.cookie)){if(isExclusiveMode()&&activeUser){const p=getSessionToken(r.headers.cookie);if(p!==TOKEN){const h=userSessions.get(p);if(!h||h.name!==activeUser.name)return t.setHeader("Set-Cookie",`${SESSION_COOKIE}=; HttpOnly; SameSite=Strict; Path=/; Max-Age=0`),t.redirect("/login")}}return u()}t.redirect("/login")}const server_require=(0,external_module_namespaceObject.createRequire)(import.meta.url);function pkgDirSafe(r){try{return(0,external_path_.dirname)(server_require.resolve(`${r}/package.json`))}catch{return null}}const libDir=(0,external_path_.join)(server_dirname,"lib"),staticOpts={maxAge:864e5};app.use("/lib/xterm",express.static(pkgDirSafe("@xterm/xterm")||(0,external_path_.join)(libDir,"xterm"),staticOpts)),app.use("/lib/xterm-fit",express.static(pkgDirSafe("@xterm/addon-fit")||(0,external_path_.join)(libDir,"xterm-fit"),staticOpts)),app.use("/lib/xterm-links",express.static(pkgDirSafe("@xterm/addon-web-links")||(0,external_path_.join)(libDir,"xterm-links"),staticOpts));const _loginAttempts=new Map,LOGIN_MAX_ATTEMPTS=5,LOGIN_LOCKOUT_MS=300*1e3;function checkLoginRate(r){const t=Date.now(),u=_loginAttempts.get(r);return!u||t>u.resetAt?!0:u.count<LOGIN_MAX_ATTEMPTS}function recordLoginFailure(r){const t=Date.now(),u=_loginAttempts.get(r);!u||t>u.resetAt?_loginAttempts.set(r,{count:1,resetAt:t+LOGIN_LOCKOUT_MS}):u.count++}function clearLoginFailures(r){_loginAttempts.delete(r)}setInterval((()=>{const r=Date.now();for(const[t,u]of _loginAttempts)r>u.resetAt&&_loginAttempts.delete(t)}),600*1e3),app.get("/login",((r,t)=>{t.sendFile(__nccwpck_require__.ab+"login.html")})),app.post("/login",express.urlencoded({extended:!1}),((r,t)=>{const u=r.ip;if(!checkLoginRate(u))return t.redirect("/login?error=locked");loadRoles(),loadUsers();const d=r.body?.password,p=r.query.from,h=p&&p.startsWith("/")&&!p.startsWith("//")?p:"/";if(hasUsers()){const $=lookupUser(d);if(!$)return recordLoginFailure(u),t.redirect("/login?error=1");if(isExclusiveMode()&&activeUser&&activeUser.name!==$.name)return t.redirect("/login?error=occupied&user="+encodeURIComponent(activeUser.name));clearLoginFailures(u);const b=(0,external_crypto_.randomBytes)(16).toString("hex");return userSessions.set(b,{role:"operator",name:$.name,dir:$.dir,lastUsed:Date.now()}),isExclusiveMode()&&(activeUser?activeUser.tokens.add(b):activeUser={name:$.name,dir:$.dir,tokens:new Set([b]),loginAt:Date.now()}),setSessionCookie(t,b),console.log(`[\u767B\u5F55] ${$.name} \u767B\u5F55\uFF08${isExclusiveMode()?"\u72EC\u5360":"\u5171\u4EAB"}\u6A21\u5F0F\uFF09`),t.redirect(h)}const x=lookupPassword(d);if(x){clearLoginFailures(u);const $=(0,external_crypto_.randomBytes)(16).toString("hex");return userSessions.set($,{role:x.role,name:x.name,lastUsed:Date.now()}),setSessionCookie(t,$),t.redirect(h)}if(d===PASSWORD){clearLoginFailures(u);const $=(0,external_crypto_.randomBytes)(16).toString("hex");return userSessions.set($,{role:"admin",name:"\u7BA1\u7406\u5458",lastUsed:Date.now()}),setSessionCookie(t,$),t.redirect(h)}recordLoginFailure(u),t.redirect("/login?error=1")})),app.post("/logout",((r,t)=>{const u=getSessionToken(r.headers.cookie);u&&(activeUser&&activeUser.tokens.has(u)&&(activeUser.tokens.delete(u),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(u)),t.setHeader("Set-Cookie",`${SESSION_COOKIE}=; HttpOnly; SameSite=Strict; Path=/; Max-Age=0`),t.redirect("/login")})),app.get("/api/status",((r,t)=>{if(isExclusiveMode()&&activeUser)return t.json({occupied:!0,userName:activeUser.name,releasing:!!_exclusiveReleaseTimer});if(hasUsers()&&!isExclusiveMode()){const u=new Set;for(const[,d]of io.sockets.sockets){if(!d.data.userName||d.data.userName==="\u7BA1\u7406\u5458")continue;const p=d.handshake.headers.cookie;p&&!hasValidSession(p)||u.add(d.data.userName)}return t.json({occupied:!1,onlineUsers:[...u]})}t.json({occupied:!1})})),app.get("/admin",((r,t)=>t.sendFile(__nccwpck_require__.ab+"admin.html"))),app.get("/sw.js",((r,t)=>{t.setHeader("Service-Worker-Allowed","/"),t.sendFile(__nccwpck_require__.ab+"sw.js")})),app.get("/",checkAuth,((r,t)=>t.sendFile(__nccwpck_require__.ab+"index.html"))),app.get("/terminal/:id",checkAuth,((r,t)=>t.sendFile(__nccwpck_require__.ab+"chat.html"))),app.get("/files",checkAuth,((r,t)=>t.sendFile(__nccwpck_require__.ab+"files.html"))),app.use("/lib",express.static(__nccwpck_require__.ab+"lib",staticOpts)),app.get("/api/sessions",checkAuth,((r,t)=>{const u=getUserInfo(r.headers.cookie);t.json(manager.list(u?.name,u?.role==="admin"))})),app.get("/api/usage",checkAuth,((r,t)=>{try{const u=manager.listSessions().filter((p=>p.mode==="agent"&&p.alive)).map((p=>({id:p.id,title:p.title,owner:p.owner,usage:p._usage})));let d=null;for(const p of u)if(p.usage?.rateLimitInfo){d=p.usage.rateLimitInfo;break}t.json({rateLimit:d,activeSessions:u})}catch(u){t.status(500).json({error:u.message})}})),app.post("/api/sessions",checkAuth,express.json(),((r,t)=>{const u=getUserInfo(r.headers.cookie),d=manager.createAgent({...r.body,owner:u?.name,userDir:u?.dir});t.status(201).json(d.toJSON())})),app.delete("/api/sessions/:id",checkAuth,((r,t)=>{manager.kill(r.params.id),t.status(204).end()})),app.get("/api/claude-sessions",checkAuth,((r,t)=>{const u=getUserInfo(r.headers.cookie);t.json(listLocalClaudeSessions(u?.dir))})),app.get("/api/dirs",checkAuth,((r,t)=>{const u=getUserInfo(r.headers.cookie);if(!u||u.role!=="admin")return t.status(403).json({error:"\u6CA1\u6709\u76EE\u5F55\u6D4F\u89C8\u6743\u9650"});const d=getEffectiveRoot(u.dir),p=process.platform==="win32"?"\\":"/";let h=r.query.path||d,x=(0,external_path_.resolve)((0,external_path_.normalize)(h)).replace(/[/\\]+$/,"");process.platform==="win32"&&/^[A-Za-z]:$/.test(x)&&(x+="\\"),x.startsWith(d)||(x=d);try{const $=(0,external_fs_.readdirSync)(x,{withFileTypes:!0}).filter((j=>j.isDirectory()&&!j.name.startsWith("."))).map((j=>({name:j.name,path:x.replace(/[/\\]+$/,"")+p+j.name}))).sort(((j,N)=>j.name.localeCompare(N.name))),b=(0,external_path_.join)(x,".."),w=(0,external_path_.resolve)((0,external_path_.normalize)(b)).startsWith(d);t.json({current:x,parent:w&&b!==x?b:null,dirs:$})}catch($){t.status(400).json({error:sanitizeError($.message)})}})),app.get("/api/me",checkAuth,((r,t)=>{const u=getUserInfo(r.headers.cookie);t.json({name:u?.name||"\u7528\u6237",exclusive:isExclusiveMode(),hasUsers:hasUsers(),dir:u?.dir||null})}));function getPkgVersion(){for(const r of["../package.json","./package.json","package.json"])try{const t=JSON.parse((0,external_fs_.readFileSync)((0,external_path_.join)(server_dirname,r),"utf8")).version;if(t&&t!=="0.0.0")return t}catch{}return"0.0.0"}const PKG_VERSION=getPkgVersion();let _latestVersion=null,_lastVersionCheck=0;const VERSION_CHECK_INTERVAL=600*1e3;async function checkLatestVersion(){const r=Date.now();if(_latestVersion&&r-_lastVersionCheck<VERSION_CHECK_INTERVAL)return _latestVersion;try{const t=await fetch("https://registry.npmjs.org/@wendongfly/zihi/latest",{headers:{Accept:"application/json"},signal:AbortSignal.timeout(8e3)});t.ok&&(_latestVersion=(await t.json()).version,_lastVersionCheck=r)}catch{}return _latestVersion}app.get("/api/version",(async(r,t)=>{const u=getPkgVersion(),d=await checkLatestVersion();t.json({current:u,latest:d||u,updateAvailable:d&&d!==u})})),app.post("/api/git/push",checkAuth,express.json(),(async(r,t)=>{const{sessionId:u,url:d,username:p,password:h,message:x,branch:$}=r.body||{};if(!u||!x)return t.status(400).json({ok:!1,error:"\u7F3A\u5C11\u53C2\u6570"});const b=manager.get(u);if(!b)return t.status(404).json({ok:!1,error:"\u4F1A\u8BDD\u4E0D\u5B58\u5728"});const w=b.cwd||process.cwd(),j=process.platform==="win32",N="git";function I(U,z={}){return new Promise(((O,B)=>{const L={...process.env,GIT_TERMINAL_PROMPT:"0"},W=(0,external_path_.join)(w,".gitconfig");(0,external_fs_.existsSync)(W)&&(L.GIT_CONFIG_GLOBAL=W);const H=(0,external_child_process_namespaceObject.spawn)(N,U,{cwd:w,env:L,timeout:6e4});let V="",J="";H.stdout.on("data",(G=>V+=G)),H.stderr.on("data",(G=>J+=G)),H.on("close",(G=>G===0?O(V.trim()):B(new Error(J.trim()||`exit ${G}`)))),H.on("error",B)}))}try{try{await I(["rev-parse","--git-dir"])}catch{await I(["init"])}if(d&&p&&h){const B=new URL(d);B.username=encodeURIComponent(p),B.password=encodeURIComponent(h);const L=B.toString(),W=(0,external_path_.join)(w,".git-credentials");(0,external_fs_.writeFileSync)(W,L+`
468
+ `)[0]}catch{}try{if(process.platform==="win32"){const t=(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(t)return t[1]}else try{const t=(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(t)return t[1]}catch{const r=(0,external_child_process_namespaceObject.execSync)("route -n get default 2>/dev/null || route -n 2>/dev/null",{timeout:3e3,stdio:"pipe"}).toString(),t=r.match(/interface:\s*(\S+)/i)||r.match(/0\.0\.0\.0\s+(\d+\.\d+\.\d+\.\d+)/);if(t){const u=(0,external_os_.networkInterfaces)()[t[1]];if(u){const d=u.find((p=>p.family==="IPv4"&&!p.internal));if(d)return d.address}if(/^\d+\.\d+\.\d+\.\d+$/.test(t[1]))return t[1]}}}catch{}for(const r of Object.values((0,external_os_.networkInterfaces)()))for(const t of r)if(t.family==="IPv4"&&!t.internal)return t.address;return"localhost"}function parseCookies(r=""){const t={};for(const u of r.split(";")){const d=u.indexOf("=");d>0&&(t[u.slice(0,d).trim()]=u.slice(d+1).trim())}return t}const SESSION_COOKIE="zihi_sid",COOKIE_OPTS="HttpOnly; SameSite=Lax; Path=/; Max-Age=31536000";function setSessionCookie(r,t){r.setHeader("Set-Cookie",`${SESSION_COOKIE}=${t||TOKEN}; ${COOKIE_OPTS}`)}function getSessionToken(r){return parseCookies(r)[SESSION_COOKIE]||null}function hasValidSession(r){const t=getSessionToken(r);if(t&&userSessions.has(t)){const u=userSessions.get(t);return u&&(u.lastUsed=Date.now()),!0}return!1}function getUserInfo(r){const t=getSessionToken(r);return t?userSessions.get(t):null}const MIME_MAP={".html":"text/html",".css":"text/css",".js":"text/javascript",".mjs":"text/javascript",".json":"application/json",".xml":"application/xml",".txt":"text/plain",".md":"text/plain",".csv":"text/csv",".log":"text/plain",".sh":"text/plain",".py":"text/plain",".ts":"text/plain",".jsx":"text/plain",".tsx":"text/plain",".yml":"text/plain",".yaml":"text/plain",".png":"image/png",".jpg":"image/jpeg",".jpeg":"image/jpeg",".gif":"image/gif",".svg":"image/svg+xml",".webp":"image/webp",".ico":"image/x-icon",".bmp":"image/bmp",".mp3":"audio/mpeg",".wav":"audio/wav",".ogg":"audio/ogg",".flac":"audio/flac",".m4a":"audio/mp4",".aac":"audio/aac",".wma":"audio/x-ms-wma",".mp4":"video/mp4",".webm":"video/webm",".mkv":"video/x-matroska",".avi":"video/x-msvideo",".mov":"video/quicktime",".flv":"video/x-flv",".pdf":"application/pdf",".zip":"application/zip",".gz":"application/gzip",".tar":"application/x-tar",".7z":"application/x-7z-compressed",".rar":"application/x-rar-compressed",".doc":"application/msword",".docx":"application/vnd.openxmlformats-officedocument.wordprocessingml.document",".xls":"application/vnd.ms-excel",".xlsx":"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",".ppt":"application/vnd.ms-powerpoint",".pptx":"application/vnd.openxmlformats-officedocument.presentationml.presentation",".woff":"font/woff",".woff2":"font/woff2",".ttf":"font/ttf",".eot":"application/vnd.ms-fontobject"};function getMime(r){return MIME_MAP[(0,external_path_.extname)(r).toLowerCase()]||"application/octet-stream"}function sanitizeError(r){return(r||"\u64CD\u4F5C\u5931\u8D25").replace(/[<>"'&]/g,(t=>({"<":"&lt;",">":"&gt;",'"':"&quot;","'":"&#39;","&":"&amp;"})[t]))}function resolveUserFilePath(r,t){const u=getUserInfo(r.headers.cookie),d=r.query.sessionId||r.body?.sessionId,p=d?manager.get(d):null,h=p?.cwd?(0,external_path_.resolve)((0,external_path_.normalize)(p.cwd)):null,x=getEffectiveRoot(u?.dir),$=h&&h.startsWith(x)?h:x;let b=t?(0,external_path_.resolve)((0,external_path_.normalize)($),(0,external_path_.normalize)(t)):$;if(process.platform==="win32"&&/^[A-Za-z]:$/.test(b)&&(b+="\\"),!b.startsWith($))return{error:"\u8BBF\u95EE\u88AB\u62D2\u7EDD\uFF1A\u8DEF\u5F84\u8D85\u51FA\u5141\u8BB8\u8303\u56F4",status:403};try{if(!(0,external_fs_.realpathSync)(b).startsWith($))return{error:"\u8BBF\u95EE\u88AB\u62D2\u7EDD\uFF1A\u7B26\u53F7\u94FE\u63A5\u6307\u5411\u5141\u8BB8\u8303\u56F4\u5916",status:403}}catch{}return{resolved:b,userRoot:$,info:u}}const app=express();app.set("trust proxy","loopback"),app.use(((r,t,u)=>{t.setHeader("Referrer-Policy","no-referrer"),t.setHeader("X-Content-Type-Options","nosniff"),u()}));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:(r,t)=>{if(!r||_corsAllowed.test(r))return t(null,!0);t(new Error("CORS \u4E0D\u5141\u8BB8\u6B64\u6765\u6E90"),!1)},credentials:!0},transports:["polling","websocket"],pingTimeout:6e4,pingInterval:25e3,upgradeTimeout:3e4,allowUpgrades:!0,maxHttpBufferSize:5e6});io.attach(httpServer);const manager=new SessionManager,uploadDir=(0,external_path_.join)(configDir,"uploads");(0,external_fs_.mkdirSync)(uploadDir,{recursive:!0});function checkAuth(r,t,u){const d=r.query.token;if(d&&userSessions.has(d)){const p=userSessions.get(d);if(p?.onetime){userSessions.delete(d);const x=(0,external_crypto_.randomBytes)(16).toString("hex");userSessions.set(x,{role:p.role,name:p.name,lastUsed:Date.now()}),setSessionCookie(t,x)}else setSessionCookie(t,d);const h=r.path+(Object.keys(r.query).filter((x=>x!=="token")).length?"?"+new URLSearchParams(Object.fromEntries(Object.entries(r.query).filter((([x])=>x!=="token")))):"");return t.redirect(h)}if(hasValidSession(r.headers.cookie)){if(isExclusiveMode()&&activeUser){const p=getSessionToken(r.headers.cookie);if(p!==TOKEN){const h=userSessions.get(p);if(!h||h.name!==activeUser.name)return t.setHeader("Set-Cookie",`${SESSION_COOKIE}=; HttpOnly; SameSite=Strict; Path=/; Max-Age=0`),r.path.startsWith("/api/")?t.status(401).json({error:"\u4F1A\u8BDD\u5DF2\u8FC7\u671F"}):t.redirect("/login")}}return u()}if(r.path.startsWith("/api/"))return t.status(401).json({error:"\u672A\u767B\u5F55"});t.redirect("/login")}const server_require=(0,external_module_namespaceObject.createRequire)(import.meta.url);function pkgDirSafe(r){try{return(0,external_path_.dirname)(server_require.resolve(`${r}/package.json`))}catch{return null}}const libDir=(0,external_path_.join)(server_dirname,"lib"),staticOpts={maxAge:864e5};app.use("/lib/xterm",express.static(pkgDirSafe("@xterm/xterm")||(0,external_path_.join)(libDir,"xterm"),staticOpts)),app.use("/lib/xterm-fit",express.static(pkgDirSafe("@xterm/addon-fit")||(0,external_path_.join)(libDir,"xterm-fit"),staticOpts)),app.use("/lib/xterm-links",express.static(pkgDirSafe("@xterm/addon-web-links")||(0,external_path_.join)(libDir,"xterm-links"),staticOpts));const _loginAttempts=new Map,LOGIN_MAX_ATTEMPTS=5,LOGIN_LOCKOUT_MS=300*1e3;function checkLoginRate(r){const t=Date.now(),u=_loginAttempts.get(r);return!u||t>u.resetAt?!0:u.count<LOGIN_MAX_ATTEMPTS}function recordLoginFailure(r){const t=Date.now(),u=_loginAttempts.get(r);!u||t>u.resetAt?_loginAttempts.set(r,{count:1,resetAt:t+LOGIN_LOCKOUT_MS}):u.count++}function clearLoginFailures(r){_loginAttempts.delete(r)}setInterval((()=>{const r=Date.now();for(const[t,u]of _loginAttempts)r>u.resetAt&&_loginAttempts.delete(t)}),600*1e3),app.get("/login",((r,t)=>{t.sendFile(__nccwpck_require__.ab+"login.html")})),app.post("/login",express.urlencoded({extended:!1}),((r,t)=>{const u=r.ip;if(!checkLoginRate(u))return t.redirect("/login?error=locked");loadRoles(),loadUsers();const d=r.body?.password,p=r.query.from,h=p&&p.startsWith("/")&&!p.startsWith("//")?p:"/";if(hasUsers()){const $=lookupUser(d);if(!$)return recordLoginFailure(u),t.redirect("/login?error=1");if(isExclusiveMode()&&activeUser&&activeUser.name!==$.name)return t.redirect("/login?error=occupied&user="+encodeURIComponent(activeUser.name));clearLoginFailures(u);const b=(0,external_crypto_.randomBytes)(16).toString("hex");return userSessions.set(b,{role:"operator",name:$.name,dir:$.dir,lastUsed:Date.now()}),isExclusiveMode()&&(activeUser?activeUser.tokens.add(b):activeUser={name:$.name,dir:$.dir,tokens:new Set([b]),loginAt:Date.now()}),setSessionCookie(t,b),console.log(`[\u767B\u5F55] ${$.name} \u767B\u5F55\uFF08${isExclusiveMode()?"\u72EC\u5360":"\u5171\u4EAB"}\u6A21\u5F0F\uFF09`),t.redirect(h)}const x=lookupPassword(d);if(x){clearLoginFailures(u);const $=(0,external_crypto_.randomBytes)(16).toString("hex");return userSessions.set($,{role:x.role,name:x.name,lastUsed:Date.now()}),setSessionCookie(t,$),t.redirect(h)}if(d===PASSWORD){clearLoginFailures(u);const $=(0,external_crypto_.randomBytes)(16).toString("hex");return userSessions.set($,{role:"admin",name:"\u7BA1\u7406\u5458",lastUsed:Date.now()}),setSessionCookie(t,$),t.redirect(h)}recordLoginFailure(u),t.redirect("/login?error=1")})),app.post("/logout",((r,t)=>{const u=getSessionToken(r.headers.cookie);u&&(activeUser&&activeUser.tokens.has(u)&&(activeUser.tokens.delete(u),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(u)),t.setHeader("Set-Cookie",`${SESSION_COOKIE}=; HttpOnly; SameSite=Strict; Path=/; Max-Age=0`),t.redirect("/login")})),app.get("/api/status",((r,t)=>{if(isExclusiveMode()&&activeUser)return t.json({occupied:!0,userName:activeUser.name,releasing:!!_exclusiveReleaseTimer});if(hasUsers()&&!isExclusiveMode()){const u=new Set;for(const[,d]of io.sockets.sockets){if(!d.data.userName||d.data.userName==="\u7BA1\u7406\u5458")continue;const p=d.handshake.headers.cookie;p&&!hasValidSession(p)||u.add(d.data.userName)}return t.json({occupied:!1,onlineUsers:[...u]})}t.json({occupied:!1})})),app.get("/admin",((r,t)=>t.sendFile(__nccwpck_require__.ab+"admin.html"))),app.get("/sw.js",((r,t)=>{t.setHeader("Service-Worker-Allowed","/"),t.sendFile(__nccwpck_require__.ab+"sw.js")})),app.get("/",checkAuth,((r,t)=>t.sendFile(__nccwpck_require__.ab+"index.html"))),app.get("/terminal/:id",checkAuth,((r,t)=>t.sendFile(__nccwpck_require__.ab+"chat.html"))),app.get("/files",checkAuth,((r,t)=>t.sendFile(__nccwpck_require__.ab+"files.html"))),app.use("/lib",express.static(__nccwpck_require__.ab+"lib",staticOpts)),app.get("/api/sessions",checkAuth,((r,t)=>{const u=getUserInfo(r.headers.cookie);t.json(manager.list(u?.name,u?.role==="admin"))})),app.get("/api/usage",checkAuth,((r,t)=>{try{const u=manager.listSessions().filter((p=>p.mode==="agent"&&p.alive)).map((p=>({id:p.id,title:p.title,owner:p.owner,usage:p._usage})));let d=null;for(const p of u)if(p.usage?.rateLimitInfo){d=p.usage.rateLimitInfo;break}t.json({rateLimit:d,activeSessions:u})}catch(u){t.status(500).json({error:u.message})}})),app.post("/api/sessions",checkAuth,express.json(),((r,t)=>{const u=getUserInfo(r.headers.cookie),d=manager.createAgent({...r.body,owner:u?.name,userDir:u?.dir});t.status(201).json(d.toJSON())})),app.delete("/api/sessions/:id",checkAuth,((r,t)=>{manager.kill(r.params.id),t.status(204).end()})),app.get("/api/claude-sessions",checkAuth,((r,t)=>{const u=getUserInfo(r.headers.cookie);t.json(listLocalClaudeSessions(u?.dir))})),app.get("/api/dirs",checkAuth,((r,t)=>{const u=getUserInfo(r.headers.cookie);if(!u||u.role!=="admin")return t.status(403).json({error:"\u6CA1\u6709\u76EE\u5F55\u6D4F\u89C8\u6743\u9650"});const d=getEffectiveRoot(u.dir),p=process.platform==="win32"?"\\":"/";let h=r.query.path||d,x=(0,external_path_.resolve)((0,external_path_.normalize)(h)).replace(/[/\\]+$/,"");process.platform==="win32"&&/^[A-Za-z]:$/.test(x)&&(x+="\\"),x.startsWith(d)||(x=d);try{const $=(0,external_fs_.readdirSync)(x,{withFileTypes:!0}).filter((j=>j.isDirectory()&&!j.name.startsWith("."))).map((j=>({name:j.name,path:x.replace(/[/\\]+$/,"")+p+j.name}))).sort(((j,N)=>j.name.localeCompare(N.name))),b=(0,external_path_.join)(x,".."),w=(0,external_path_.resolve)((0,external_path_.normalize)(b)).startsWith(d);t.json({current:x,parent:w&&b!==x?b:null,dirs:$})}catch($){t.status(400).json({error:sanitizeError($.message)})}})),app.get("/api/me",checkAuth,((r,t)=>{const u=getUserInfo(r.headers.cookie);t.json({name:u?.name||"\u7528\u6237",exclusive:isExclusiveMode(),hasUsers:hasUsers(),dir:u?.dir||null})}));function getPkgVersion(){for(const r of["../package.json","./package.json","package.json"])try{const t=JSON.parse((0,external_fs_.readFileSync)((0,external_path_.join)(server_dirname,r),"utf8")).version;if(t&&t!=="0.0.0")return t}catch{}return"0.0.0"}const PKG_VERSION=getPkgVersion();let _latestVersion=null,_lastVersionCheck=0;const VERSION_CHECK_INTERVAL=600*1e3;async function checkLatestVersion(){const r=Date.now();if(_latestVersion&&r-_lastVersionCheck<VERSION_CHECK_INTERVAL)return _latestVersion;try{const t=await fetch("https://registry.npmjs.org/@wendongfly/zihi/latest",{headers:{Accept:"application/json"},signal:AbortSignal.timeout(8e3)});t.ok&&(_latestVersion=(await t.json()).version,_lastVersionCheck=r)}catch{}return _latestVersion}app.get("/api/version",(async(r,t)=>{const u=getPkgVersion(),d=await checkLatestVersion();t.json({current:u,latest:d||u,updateAvailable:d&&d!==u})})),app.post("/api/git/push",checkAuth,express.json(),(async(r,t)=>{const{sessionId:u,url:d,username:p,password:h,message:x,branch:$}=r.body||{};if(!u||!x)return t.status(400).json({ok:!1,error:"\u7F3A\u5C11\u53C2\u6570"});const b=manager.get(u);if(!b)return t.status(404).json({ok:!1,error:"\u4F1A\u8BDD\u4E0D\u5B58\u5728"});const w=b.cwd||process.cwd(),j=process.platform==="win32",N="git";function I(U,z={}){return new Promise(((O,B)=>{const L={...process.env,GIT_TERMINAL_PROMPT:"0"},W=(0,external_path_.join)(w,".gitconfig");(0,external_fs_.existsSync)(W)&&(L.GIT_CONFIG_GLOBAL=W);const H=(0,external_child_process_namespaceObject.spawn)(N,U,{cwd:w,env:L,timeout:6e4});let V="",J="";H.stdout.on("data",(G=>V+=G)),H.stderr.on("data",(G=>J+=G)),H.on("close",(G=>G===0?O(V.trim()):B(new Error(J.trim()||`exit ${G}`)))),H.on("error",B)}))}try{try{await I(["rev-parse","--git-dir"])}catch{await I(["init"])}if(d&&p&&h){const B=new URL(d);B.username=encodeURIComponent(p),B.password=encodeURIComponent(h);const L=B.toString(),W=(0,external_path_.join)(w,".git-credentials");(0,external_fs_.writeFileSync)(W,L+`
469
469
  `,{mode:384});const H=(0,external_path_.join)(w,".gitconfig"),J=`[credential]
470
470
  helper = store --file ${W.replace(/\\/g,"/")}
471
471
  [user]
@@ -475,7 +475,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
475
475
  helper = store --file ${x.replace(/\\/g,"/")}
476
476
  [user]
477
477
  name = ${d}
478
- `,{mode:384})}catch{}console.log(`[\u7BA1\u7406] \u6DFB\u52A0\u7528\u6237 ${d} (\u76EE\u5F55: ${p})`),t.json({ok:!0})})),app.delete("/api/admin/user",checkAdminAuth,express.json(),((r,t)=>{const{name:u}=r.body||{};if(!u)return t.json({ok:!1,error:"\u8BF7\u6307\u5B9A\u7528\u6237\u540D\u79F0"});if(loadUsers(),!listUsers().find((h=>h.name===u)))return t.json({ok:!1,error:"\u7528\u6237\u4E0D\u5B58\u5728"});try{const h=(0,external_path_.join)(configDir,"users.json"),x=JSON.parse((0,external_fs_.readFileSync)(h,"utf8"));for(const[$,b]of Object.entries(x.users||{}))if(b.name===u){delete x.users[$];break}(0,external_fs_.writeFileSync)(h,JSON.stringify(x,null,2),{mode:384}),loadUsers(),console.log(`[\u7BA1\u7406] \u5220\u9664\u7528\u6237 ${u}`),t.json({ok:!0})}catch(h){t.json({ok:!1,error:h.message})}})),app.post("/api/admin/user/password",checkAdminAuth,express.json(),((r,t)=>{const{name:u,password:d}=r.body||{};if(!u||!d)return t.json({ok:!1,error:"\u540D\u79F0\u548C\u65B0\u5BC6\u7801\u5FC5\u586B"});try{const p=(0,external_path_.join)(configDir,"users.json"),h=JSON.parse((0,external_fs_.readFileSync)(p,"utf8"));let x=null;for(const[$,b]of Object.entries(h.users||{}))if(b.name===u){x=b,delete h.users[$];break}if(!x)return t.json({ok:!1,error:"\u7528\u6237\u4E0D\u5B58\u5728"});h.users[d]=x,(0,external_fs_.writeFileSync)(p,JSON.stringify(h,null,2),{mode:384}),loadUsers(),console.log(`[\u7BA1\u7406] \u4FEE\u6539\u7528\u6237 ${u} \u7684\u5BC6\u7801`),t.json({ok:!0})}catch(p){t.json({ok:!1,error:p.message})}})),app.post("/api/admin/password",checkAdminAuth,express.json(),((r,t)=>{const{password:u}=r.body||{};if(!u||u.length<4)return t.json({ok:!1,error:"\u5BC6\u7801\u81F3\u5C114\u4F4D"});try{(0,external_fs_.writeFileSync)((0,external_path_.join)(configDir,"password"),u,{mode:384}),PASSWORD=u,t.json({ok:!0}),console.log("[\u7BA1\u7406] \u7BA1\u7406\u5BC6\u7801\u5DF2\u4FEE\u6539")}catch(d){t.json({ok:!1,error:d.message})}})),app.post("/upload",checkAuth,((r,t)=>{const u=r.query.sessionId,d=u?manager.get(u):null,p=d?.cwd?(0,external_path_.join)(d.cwd,"upload"):uploadDir;(0,external_fs_.mkdirSync)(p,{recursive:!0}),multer({storage:multer.diskStorage({destination:p,filename:(x,$,b)=>{const w=$.originalname.match(/\.[^.]+$/)?.[0]||".jpg";b(null,`${Date.now()}-${(0,external_crypto_.randomBytes)(3).toString("hex")}${w}`)}}),limits:{fileSize:20*1024*1024},fileFilter:(x,$,b)=>b(null,$.mimetype.startsWith("image/")),defParamCharset:"utf-8"}).single("image")(r,t,(x=>{if(x)return t.status(400).json({error:x.message});if(!r.file)return t.status(400).json({error:"\u6CA1\u6709\u56FE\u7247"});t.json({path:(0,external_path_.resolve)(r.file.path).replace(/\\/g,"/")})}))})),app.get("/api/files/list",checkAuth,((r,t)=>{const u=resolveUserFilePath(r,r.query.path);if(u.error)return t.status(u.status).json({error:u.error});try{let x=function(I,U){const z=(0,external_fs_.readdirSync)(I,{withFileTypes:!0}).filter((B=>p||!B.name.startsWith("."))),O=[];for(const B of z)try{const L=(0,external_path_.join)(I,B.name),W=U?U+"/"+B.name:B.name,H=(0,external_fs_.statSync)(L);B.isDirectory()?h?O.push(...x(L,W)):O.push({name:W,type:"dir",size:H.size,mtime:H.mtime}):O.push({name:W,type:"file",size:H.size,mtime:H.mtime})}catch{}return O};if(!(0,external_fs_.statSync)(u.resolved).isDirectory())return t.status(400).json({error:"\u4E0D\u662F\u76EE\u5F55"});const p=r.query.showHidden==="1",h=r.query.recursive==="1",$=x(u.resolved,"").sort(((I,U)=>I.type===U.type?I.name.localeCompare(U.name):I.type==="dir"?-1:1)),b=(0,external_path_.join)(u.resolved,".."),w=(0,external_path_.resolve)((0,external_path_.normalize)(b)),j=w.startsWith(u.userRoot)&&w!==u.resolved,N=u.resolved.startsWith(u.userRoot)?u.resolved.slice(u.userRoot.length).replace(/^[/\\]+/,""):"";t.json({current:u.resolved.replace(/\\/g,"/"),currentRel:N.replace(/\\/g,"/"),parent:j?w.replace(/\\/g,"/"):null,root:u.userRoot.replace(/\\/g,"/"),entries:$})}catch(d){t.status(400).json({error:sanitizeError(d.message)})}})),app.post("/api/files/upload",checkAuth,((r,t)=>{const u=resolveUserFilePath(r,r.query.path);if(u.error)return t.status(u.status).json({error:u.error});(0,external_fs_.mkdirSync)(u.resolved,{recursive:!0}),multer({storage:multer.diskStorage({destination:(p,h,x)=>{const $=h.originalname||"",b=$.lastIndexOf("/"),w=b>0?$.slice(0,b):"",j=w?(0,external_path_.join)(u.resolved,w):u.resolved;if(!(0,external_path_.resolve)(j).startsWith(u.resolved))return x(new Error("\u8DEF\u5F84\u4E0D\u5408\u6CD5"));(0,external_fs_.mkdirSync)(j,{recursive:!0}),x(null,j)},filename:(p,h,x)=>{const $=h.originalname||"",b=$.lastIndexOf("/"),w=b>0?$.slice(b+1):(0,external_path_.basename)($);x(null,w||`${Date.now()}.bin`)}}),limits:{fileSize:500*1024*1024},defParamCharset:"utf-8"}).array("files",100)(r,t,(p=>{if(p)return t.status(400).json({error:p.message});if(!r.files?.length)return t.status(400).json({error:"\u6CA1\u6709\u6587\u4EF6"});const h={ok:!0,files:r.files.map(($=>({name:$.filename,path:(0,external_path_.resolve)($.path).replace(/\\/g,"/"),size:$.size})))};t.json(h);const x=r.query.sessionId;x&&io.to(x).emit("files:changed",{sessionId:x})}))})),app.get("/api/files/download",checkAuth,((r,t)=>{if(!r.query.path)return t.status(400).json({error:"\u7F3A\u5C11 path \u53C2\u6570"});const u=resolveUserFilePath(r,r.query.path);if(u.error)return t.status(u.status).json({error:u.error});try{const d=(0,external_fs_.statSync)(u.resolved);if(!d.isFile())return t.status(400).json({error:"\u4E0D\u662F\u6587\u4EF6"});const p=(0,external_path_.basename)(u.resolved);t.setHeader("Content-Type",getMime(p)),t.setHeader("Content-Length",d.size),t.setHeader("Content-Disposition",`attachment; filename*=UTF-8''${encodeURIComponent(p)}`),(0,external_fs_.createReadStream)(u.resolved).pipe(t)}catch(d){t.status(404).json({error:sanitizeError(d.message)})}})),app.get("/api/files/preview",checkAuth,((r,t)=>{if(!r.query.path)return t.status(400).json({error:"\u7F3A\u5C11 path \u53C2\u6570"});const u=resolveUserFilePath(r,r.query.path);if(u.error)return t.status(u.status).json({error:u.error});try{const d=(0,external_fs_.statSync)(u.resolved);if(!d.isFile())return t.status(400).json({error:"\u4E0D\u662F\u6587\u4EF6"});const p=(0,external_path_.basename)(u.resolved),h=getMime(p);t.setHeader("Content-Type",h),t.setHeader("Content-Length",d.size),t.setHeader("Content-Disposition",`inline; filename*=UTF-8''${encodeURIComponent(p)}`),(0,external_fs_.createReadStream)(u.resolved).pipe(t)}catch(d){t.status(404).json({error:sanitizeError(d.message)})}})),app.post("/api/files/mkdir",checkAuth,express.json(),((r,t)=>{const{path:u}=r.body||{};if(!u)return t.status(400).json({error:"\u7F3A\u5C11 path"});const d=resolveUserFilePath(r,u);if(d.error)return t.status(d.status).json({error:d.error});try{(0,external_fs_.mkdirSync)(d.resolved,{recursive:!0}),t.json({ok:!0,path:d.resolved.replace(/\\/g,"/")})}catch(p){t.status(400).json({error:sanitizeError(p.message)})}})),app.delete("/api/files/delete",checkAuth,express.json(),((r,t)=>{const u=getUserInfo(r.headers.cookie),d=r.body?.sessionId||r.query.sessionId,p=d?manager.get(d):null;if(!(p&&u&&(u.role==="admin"||p.owner===u.name))&&(!u||u.role!=="admin"))return t.status(403).json({error:"\u6CA1\u6709\u5220\u9664\u6743\u9650"});const{path:x}=r.body||{};if(!x)return t.status(400).json({error:"\u7F3A\u5C11 path"});const $=resolveUserFilePath(r,x);if($.error)return t.status($.status).json({error:$.error});if($.resolved===$.userRoot)return t.status(403).json({error:"\u4E0D\u5141\u8BB8\u5220\u9664\u6839\u76EE\u5F55"});try{(0,external_fs_.statSync)($.resolved).isDirectory()?(0,external_fs_.rmSync)($.resolved,{recursive:!1}):(0,external_fs_.unlinkSync)($.resolved),t.json({ok:!0});const w=r.body?.sessionId||r.query.sessionId;w&&io.to(w).emit("files:changed",{sessionId:w})}catch(b){t.status(400).json({error:sanitizeError(b.message)})}})),app.get("/qr/:sessionId",checkAuth,(async(r,t)=>{const u=getTailscaleIP(),d=(0,external_crypto_.randomBytes)(16).toString("hex");userSessions.set(d,{role:"operator",name:"\u626B\u7801\u7528\u6237",onetime:!0,lastUsed:Date.now()});const p=`http://${u}:${PORT}/terminal/${r.params.sessionId}?token=${d}`;try{const h=await lib.toString(p,{type:"svg"});t.setHeader("Content-Type","image/svg+xml"),t.send(h)}catch(h){t.status(500).send(h.message)}})),io.use(((r,t)=>{const u=r.handshake.headers.cookie;if(hasValidSession(u)){const h=getUserInfo(u);return r.data.role=h?.role||"admin",r.data.userName=h?.name||"\u7528\u6237",r.data.dir=h?.dir||null,t()}const d=r.handshake.auth?.password;if(d){loadUsers();const h=lookupUser(d);if(h)return r.data.role="operator",r.data.userName=h.name,r.data.dir=h.dir,t()}if((r.handshake.auth?.token||r.handshake.query?.token)===TOKEN)return r.data.role="admin",r.data.userName="\u7BA1\u7406\u5458",t();t(new Error("\u672A\u6388\u6743"))}));const EXCLUSIVE_RELEASE_DELAY=120*1e3;let _exclusiveReleaseTimer=null;function checkExclusiveRelease(){if(!isExclusiveMode()||!activeUser)return;let r=!1;for(const[,t]of io.sockets.sockets)if(t.data.userName===activeUser.name){r=!0;break}if(r){_exclusiveReleaseTimer&&(clearTimeout(_exclusiveReleaseTimer),_exclusiveReleaseTimer=null);return}_exclusiveReleaseTimer||(_exclusiveReleaseTimer=setTimeout((()=>{if(_exclusiveReleaseTimer=null,!!activeUser){for(const[,t]of io.sockets.sockets)if(t.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 t of activeUser.tokens)userSessions.delete(t);activeUser=null}}),EXCLUSIVE_RELEASE_DELAY))}let _broadcastTimer=null;function broadcastSessions(){_broadcastTimer||(_broadcastTimer=setTimeout((()=>{_broadcastTimer=null;for(const[,r]of io.sockets.sockets)r.emit("sessions",manager.list(r.data.userName,r.data.role==="admin"))}),100))}io.on("connection",(r=>{const t=r.handshake.address?.replace("::ffff:","")||"?",u=r.handshake.headers?.referer||"?";console.log(`[\u8FDE\u63A5] ${r.data.userName}(${r.data.role}) \u4ECE ${t} \u63A5\u5165 id=${r.id} \u9875\u9762=${u}`),_exclusiveReleaseTimer&&activeUser&&r.data.userName===activeUser.name&&(clearTimeout(_exclusiveReleaseTimer),_exclusiveReleaseTimer=null);let d=null,p=null,h=null,x=null,$=null;function b(){d&&(d.isController(r.id)&&(d.releaseControl(),io.emit("control-changed",{sessionId:d.id,holder:null,holderName:null})),d.removeViewer(r.id),p&&d.off("data",p),h&&d.off("agent:message",h),x&&d.off("agent:busy",x),$&&d.off("agent:error",$),p=null,h=null,x=null,$=null)}r.on("join",(w=>{const j=manager.get(w);if(!j){r.emit("error",{message:`\u4F1A\u8BDD ${w} \u672A\u627E\u5230`});return}if(r.data.role!=="admin"&&j.owner&&j.owner!==r.data.userName){r.emit("error",{message:"\u6CA1\u6709\u52A0\u5165\u8BE5\u4F1A\u8BDD\u7684\u6743\u9650"});return}b(),d=j,d.addViewer(r.id,r.data.userName),r.join(w),h=N=>r.emit("agent:message",N),d.on("agent:message",h),x=N=>r.emit("agent:busy",N),d.on("agent:busy",x),$=N=>r.emit("agent:error",N),d.on("agent:error",$),console.log(`[\u52A0\u5165] ${r.data.userName} \u52A0\u5165\u4F1A\u8BDD "${j.title}" (${w})`),r.emit("joined",{...j.toJSON(),role:r.data.role}),j._history?.length&&r.emit("agent:history",j._history),broadcastSessions()})),r.on("disconnect",(w=>{console.log(`[\u65AD\u5F00] ${r.data.userName} \u79BB\u5F00\u4F1A\u8BDD "${d?.title||"?"}" id=${r.id} \u539F\u56E0=${w}`),b(),broadcastSessions(),checkExclusiveRelease(),manager.persist()})),r.on("agent:query",(async({prompt:w}={})=>{if(!d||d.mode!=="agent"){r.emit("agent:error",{message:"\u5F53\u524D\u4E0D\u662F Agent \u6A21\u5F0F\u4F1A\u8BDD"});return}if(!d.isController(r.id)){r.emit("control-denied",{reason:"\u4F60\u6CA1\u6709\u5F53\u524D\u4F1A\u8BDD\u7684\u63A7\u5236\u6743"});return}if(d.isBusy){r.emit("agent:error",{message:"\u6B63\u5728\u5904\u7406\u4E2D\uFF0C\u8BF7\u7B49\u5F85\u5B8C\u6210"});return}try{await d.query(w)}catch(j){console.error("[agent:query] \u9519\u8BEF:",j.message);try{r.connected&&r.emit("agent:error",{message:j.message})}catch{}}})),r.on("agent:interrupt",(()=>{if(!(!d||d.mode!=="agent")){if(!d.isController(r.id)){r.emit("control-denied",{reason:"\u4F60\u6CA1\u6709\u5F53\u524D\u4F1A\u8BDD\u7684\u63A7\u5236\u6743"});return}d.interrupt()}})),r.on("agent:permission",(({requestId:w,allow:j,toolName:N,approveAll:I}={})=>{if(!(!d||d.mode!=="agent")){if(!d.isController(r.id)){r.emit("control-denied",{reason:"\u4F60\u6CA1\u6709\u5F53\u524D\u4F1A\u8BDD\u7684\u63A7\u5236\u6743"});return}d.respondPermission(w,j,N,I)}})),r.on("set-mode",(({sessionId:w,mode:j}={})=>{const N=w?manager.get(w):d;if(N){if(r.data.role!=="admin"&&N.owner&&N.owner!==r.data.userName){r.emit("error",{message:"\u6CA1\u6709\u66F4\u6539\u6A21\u5F0F\u7684\u6743\u9650"});return}N.setPermissionMode&&(N.setPermissionMode(j),console.log(`[\u6A21\u5F0F] ${r.data.userName} \u5207\u6362\u4F1A\u8BDD "${N.title}" \u4E3A ${j}`)),io.emit("mode-changed",{sessionId:N.id,mode:j}),manager.persist()}})),r.on("create-agent",((w,j)=>{if(!hasPermission(r.data.role,"operator")){typeof j=="function"&&j({ok:!1,error:"\u6CA1\u6709\u521B\u5EFA\u4F1A\u8BDD\u7684\u6743\u9650"});return}{const U=getEffectiveRoot(r.data.dir),z=w?.cwd?(0,external_path_.resolve)((0,external_path_.normalize)(w.cwd)):null;if(z&&z!==U&&!z.startsWith(U+external_path_.sep)){typeof j=="function"&&j({ok:!1,error:"\u4E0D\u80FD\u5728\u5141\u8BB8\u76EE\u5F55\u4E4B\u5916\u521B\u5EFA\u4F1A\u8BDD"});return}z||(w={...w,cwd:U})}const N=w.cwd,I=(0,external_path_.join)(N,".zihi-sessions",`${Date.now()}-${(0,external_crypto_.randomUUID)().slice(0,8)}`);try{(0,external_fs_.mkdirSync)(I,{recursive:!0})}catch(U){typeof j=="function"&&j({ok:!1,error:`\u521B\u5EFA\u9694\u79BB\u76EE\u5F55\u5931\u8D25: ${U.message}`});return}w={...w,cwd:I};try{const U=manager.createAgent({...w,owner:r.data.userName,userDir:r.data.dir});console.log(`[\u4F1A\u8BDD] ${r.data.userName} \u521B\u5EFA Agent \u4F1A\u8BDD "${U.title}" (${U.id})`),broadcastSessions(),typeof j=="function"&&j({ok:!0,session:U.toJSON()})}catch(U){typeof j=="function"&&j({ok:!1,error:U.message})}})),r.on("take-control",(({sessionId:w}={})=>{const j=w?manager.get(w):d;if(j){if(!hasPermission(r.data.role,"operator")){r.emit("control-denied",{reason:"\u4F60\u7684\u89D2\u8272\u6CA1\u6709\u63A7\u5236\u6743\u9650"});return}if(j.controlHolder&&j.controlHolder!==r.id){const N=j.controlHolderName===r.data.userName;if(r.data.role!=="admin"&&!N){r.emit("control-denied",{reason:"\u5176\u4ED6\u7528\u6237\u6B63\u5728\u63A7\u5236\u4E2D\uFF0C\u8BF7\u7B49\u5F85\u91CA\u653E"});return}}j.takeControl(r.id,r.data.userName),console.log(`[\u63A7\u5236] ${r.data.userName} \u83B7\u53D6\u4F1A\u8BDD "${j.title}" \u7684\u63A7\u5236\u6743`),io.emit("control-changed",{sessionId:j.id,holder:r.id,holderName:r.data.userName})}})),r.on("release-control",(({sessionId:w}={})=>{const j=w?manager.get(w):d;j&&j.isController(r.id)&&(console.log(`[\u63A7\u5236] ${r.data.userName} \u91CA\u653E\u4F1A\u8BDD "${j.title}" \u7684\u63A7\u5236\u6743`),j.releaseControl(),io.emit("control-changed",{sessionId:j.id,holder:null,holderName:null}))})),r.on("rename",(({sessionId:w,title:j}={})=>{const N=w?manager.get(w):d;if(!(!N||!j)){if(r.data.role!=="admin"&&N.owner&&N.owner!==r.data.userName){r.emit("error",{message:"\u6CA1\u6709\u91CD\u547D\u540D\u4F1A\u8BDD\u7684\u6743\u9650"});return}N.title=j,io.emit("session-renamed",{sessionId:N.id,title:j}),broadcastSessions(),manager.persist()}})),r.on("resize",(({cols:w,rows:j})=>d?.resize(w,j))),r.on("list",(()=>r.emit("sessions",manager.list(r.data.userName,r.data.role==="admin")))),r.on("dirs",((w,j)=>{if(typeof j!="function")return;const N=getEffectiveRoot(r.data.dir),I=process.platform==="win32"?"\\":"/";let U=(w||N).replace(/[/\\]+$/,"")||N;process.platform==="win32"&&/^[A-Za-z]:$/.test(U)&&(U+="\\");const z=(0,external_path_.resolve)((0,external_path_.normalize)(U));z!==N&&!z.startsWith(N+I)&&(U=N);try{const O=(0,external_fs_.readdirSync)(U,{withFileTypes:!0}).filter((H=>H.isDirectory()&&!H.name.startsWith("."))).map((H=>({name:H.name,path:U.replace(/[/\\]+$/,"")+I+H.name}))).sort(((H,V)=>H.name.localeCompare(V.name))),B=(0,external_path_.join)(U,".."),L=(0,external_path_.resolve)((0,external_path_.normalize)(B)),W=L===N||L.startsWith(N+I);j({ok:!0,current:U,parent:W&&B!==U?B:null,dirs:O})}catch(O){j({ok:!1,error:O.message})}})),r.on("kill",(w=>{if(!hasPermission(r.data.role,"operator")){r.emit("error",{message:"\u6CA1\u6709\u5220\u9664\u4F1A\u8BDD\u7684\u6743\u9650"});return}const j=manager.get(w);if(j&&r.data.role!=="admin"&&j.owner&&j.owner!==r.data.userName){r.emit("error",{message:"\u6CA1\u6709\u5220\u9664\u8BE5\u4F1A\u8BDD\u7684\u6743\u9650"});return}console.log(`[\u4F1A\u8BDD] ${r.data.userName} \u5220\u9664\u4F1A\u8BDD ${w}`),manager.kill(w),broadcastSessions()})),r.on("kick-user",(({socketId:w,sessionId:j}={},N)=>{if(!hasPermission(r.data.role,"admin")){typeof N=="function"&&N({ok:!1,error:"\u6CA1\u6709\u8E22\u51FA\u7528\u6237\u7684\u6743\u9650"});return}const I=io.sockets.sockets.get(w);if(!I){typeof N=="function"&&N({ok:!1,error:"\u76EE\u6807\u7528\u6237\u4E0D\u5728\u7EBF"});return}const U=I.data.userName||"\u672A\u77E5";console.log(`[\u8E22\u51FA] ${r.data.userName} \u8E22\u51FA\u4E86\u7528\u6237 ${U} (${w})`),I.emit("kicked",{reason:`\u4F60\u5DF2\u88AB\u7BA1\u7406\u5458 ${r.data.userName} \u8E22\u51FA`}),I.disconnect(!0),typeof N=="function"&&N({ok:!0,name:U})})),r.on("list-viewers",(({sessionId:w}={},j)=>{if(typeof j!="function")return;const N=w?manager.get(w):d;if(!N){j({ok:!1,error:"\u4F1A\u8BDD\u4E0D\u5B58\u5728"});return}const I=[];for(const U of N._viewers){const z=io.sockets.sockets.get(U);z&&I.push({socketId:U,userName:z.data.userName||"\u672A\u77E5",role:z.data.role||"viewer",isController:N.controlHolder===U})}j({ok:!0,viewers:I})}))}));const CONTROL_TIMEOUT=300*1e3;setInterval((()=>{for(const r of manager.listSessions()){r.controlHolder&&(!io.sockets.sockets.get(r.controlHolder)||r.lastInputTime&&Date.now()-r.lastInputTime>CONTROL_TIMEOUT)&&(r.releaseControl(),io.emit("control-changed",{sessionId:r.id,holder:null,holderName:null}));for(const t of r._viewers)io.sockets.sockets.has(t)||r.removeViewer(t)}}),60*1e3);function showStartupInfo(){const r=httpServer.address().port;PORT=r;const t=getTailscaleIP(),u=`http://${t}:${r}/admin`;console.log(`
478
+ `,{mode:384})}catch{}console.log(`[\u7BA1\u7406] \u6DFB\u52A0\u7528\u6237 ${d} (\u76EE\u5F55: ${p})`),t.json({ok:!0})})),app.delete("/api/admin/user",checkAdminAuth,express.json(),((r,t)=>{const{name:u}=r.body||{};if(!u)return t.json({ok:!1,error:"\u8BF7\u6307\u5B9A\u7528\u6237\u540D\u79F0"});if(loadUsers(),!listUsers().find((h=>h.name===u)))return t.json({ok:!1,error:"\u7528\u6237\u4E0D\u5B58\u5728"});try{const h=(0,external_path_.join)(configDir,"users.json"),x=JSON.parse((0,external_fs_.readFileSync)(h,"utf8"));for(const[$,b]of Object.entries(x.users||{}))if(b.name===u){delete x.users[$];break}(0,external_fs_.writeFileSync)(h,JSON.stringify(x,null,2),{mode:384}),loadUsers(),console.log(`[\u7BA1\u7406] \u5220\u9664\u7528\u6237 ${u}`),t.json({ok:!0})}catch(h){t.json({ok:!1,error:h.message})}})),app.post("/api/admin/user/password",checkAdminAuth,express.json(),((r,t)=>{const{name:u,password:d}=r.body||{};if(!u||!d)return t.json({ok:!1,error:"\u540D\u79F0\u548C\u65B0\u5BC6\u7801\u5FC5\u586B"});try{const p=(0,external_path_.join)(configDir,"users.json"),h=JSON.parse((0,external_fs_.readFileSync)(p,"utf8"));let x=null;for(const[$,b]of Object.entries(h.users||{}))if(b.name===u){x=b,delete h.users[$];break}if(!x)return t.json({ok:!1,error:"\u7528\u6237\u4E0D\u5B58\u5728"});h.users[d]=x,(0,external_fs_.writeFileSync)(p,JSON.stringify(h,null,2),{mode:384}),loadUsers(),console.log(`[\u7BA1\u7406] \u4FEE\u6539\u7528\u6237 ${u} \u7684\u5BC6\u7801`),t.json({ok:!0})}catch(p){t.json({ok:!1,error:p.message})}})),app.post("/api/admin/password",checkAdminAuth,express.json(),((r,t)=>{const{password:u}=r.body||{};if(!u||u.length<4)return t.json({ok:!1,error:"\u5BC6\u7801\u81F3\u5C114\u4F4D"});try{(0,external_fs_.writeFileSync)((0,external_path_.join)(configDir,"password"),u,{mode:384}),PASSWORD=u,t.json({ok:!0}),console.log("[\u7BA1\u7406] \u7BA1\u7406\u5BC6\u7801\u5DF2\u4FEE\u6539")}catch(d){t.json({ok:!1,error:d.message})}})),app.post("/upload",checkAuth,((r,t)=>{const u=r.query.sessionId,d=u?manager.get(u):null,p=d?.cwd?(0,external_path_.join)(d.cwd,"upload"):uploadDir;(0,external_fs_.mkdirSync)(p,{recursive:!0}),multer({storage:multer.diskStorage({destination:p,filename:(x,$,b)=>{const w=$.originalname.match(/\.[^.]+$/)?.[0]||".jpg";b(null,`${Date.now()}-${(0,external_crypto_.randomBytes)(3).toString("hex")}${w}`)}}),limits:{fileSize:20*1024*1024},fileFilter:(x,$,b)=>b(null,$.mimetype.startsWith("image/")),defParamCharset:"utf-8"}).single("image")(r,t,(x=>{if(x)return t.status(400).json({error:x.message});if(!r.file)return t.status(400).json({error:"\u6CA1\u6709\u56FE\u7247"});t.json({path:(0,external_path_.resolve)(r.file.path).replace(/\\/g,"/")})}))})),app.get("/api/files/list",checkAuth,((r,t)=>{const u=resolveUserFilePath(r,r.query.path);if(u.error)return t.status(u.status).json({error:u.error});try{let x=function(I,U){const z=(0,external_fs_.readdirSync)(I,{withFileTypes:!0}).filter((B=>p||!B.name.startsWith("."))),O=[];for(const B of z)try{const L=(0,external_path_.join)(I,B.name),W=U?U+"/"+B.name:B.name,H=(0,external_fs_.statSync)(L);B.isDirectory()?h?O.push(...x(L,W)):O.push({name:W,type:"dir",size:H.size,mtime:H.mtime}):O.push({name:W,type:"file",size:H.size,mtime:H.mtime})}catch{}return O};if(!(0,external_fs_.statSync)(u.resolved).isDirectory())return t.status(400).json({error:"\u4E0D\u662F\u76EE\u5F55"});const p=r.query.showHidden==="1",h=r.query.recursive==="1",$=x(u.resolved,"").sort(((I,U)=>I.type===U.type?I.name.localeCompare(U.name):I.type==="dir"?-1:1)),b=(0,external_path_.join)(u.resolved,".."),w=(0,external_path_.resolve)((0,external_path_.normalize)(b)),j=w.startsWith(u.userRoot)&&w!==u.resolved,N=u.resolved.startsWith(u.userRoot)?u.resolved.slice(u.userRoot.length).replace(/^[/\\]+/,""):"";t.json({current:u.resolved.replace(/\\/g,"/"),currentRel:N.replace(/\\/g,"/"),parent:j?w.replace(/\\/g,"/"):null,root:u.userRoot.replace(/\\/g,"/"),entries:$})}catch(d){t.status(400).json({error:sanitizeError(d.message)})}})),app.post("/api/files/upload",checkAuth,((r,t)=>{const u=resolveUserFilePath(r,r.query.path);if(u.error)return t.status(u.status).json({error:u.error});(0,external_fs_.mkdirSync)(u.resolved,{recursive:!0}),multer({storage:multer.diskStorage({destination:(p,h,x)=>{const $=h.originalname||"",b=$.lastIndexOf("/"),w=b>0?$.slice(0,b):"",j=w?(0,external_path_.join)(u.resolved,w):u.resolved;if(!(0,external_path_.resolve)(j).startsWith(u.resolved))return x(new Error("\u8DEF\u5F84\u4E0D\u5408\u6CD5"));(0,external_fs_.mkdirSync)(j,{recursive:!0}),x(null,j)},filename:(p,h,x)=>{const $=h.originalname||"",b=$.lastIndexOf("/"),w=b>0?$.slice(b+1):(0,external_path_.basename)($);x(null,w||`${Date.now()}.bin`)}}),limits:{fileSize:500*1024*1024},defParamCharset:"utf-8"}).array("files",100)(r,t,(p=>{if(p)return t.status(400).json({error:p.message});if(!r.files?.length)return t.status(400).json({error:"\u6CA1\u6709\u6587\u4EF6"});const h={ok:!0,files:r.files.map(($=>({name:$.filename,path:(0,external_path_.resolve)($.path).replace(/\\/g,"/"),size:$.size})))};t.json(h);const x=r.query.sessionId;x&&io.to(x).emit("files:changed",{sessionId:x})}))})),app.get("/api/files/download",checkAuth,((r,t)=>{if(!r.query.path)return t.status(400).json({error:"\u7F3A\u5C11 path \u53C2\u6570"});const u=resolveUserFilePath(r,r.query.path);if(u.error)return t.status(u.status).json({error:u.error});try{const d=(0,external_fs_.statSync)(u.resolved);if(!d.isFile())return t.status(400).json({error:"\u4E0D\u662F\u6587\u4EF6"});const p=(0,external_path_.basename)(u.resolved);t.setHeader("Content-Type",getMime(p)),t.setHeader("Content-Length",d.size),t.setHeader("Content-Disposition",`attachment; filename*=UTF-8''${encodeURIComponent(p)}`),(0,external_fs_.createReadStream)(u.resolved).pipe(t)}catch(d){t.status(404).json({error:sanitizeError(d.message)})}})),app.get("/api/files/preview",checkAuth,((r,t)=>{if(!r.query.path)return t.status(400).json({error:"\u7F3A\u5C11 path \u53C2\u6570"});const u=resolveUserFilePath(r,r.query.path);if(u.error)return t.status(u.status).json({error:u.error});try{const d=(0,external_fs_.statSync)(u.resolved);if(!d.isFile())return t.status(400).json({error:"\u4E0D\u662F\u6587\u4EF6"});const p=(0,external_path_.basename)(u.resolved),h=getMime(p);t.setHeader("Content-Type",h),t.setHeader("Content-Length",d.size),t.setHeader("Content-Disposition",`inline; filename*=UTF-8''${encodeURIComponent(p)}`),(0,external_fs_.createReadStream)(u.resolved).pipe(t)}catch(d){t.status(404).json({error:sanitizeError(d.message)})}})),app.post("/api/files/mkdir",checkAuth,express.json(),((r,t)=>{const{path:u}=r.body||{};if(!u)return t.status(400).json({error:"\u7F3A\u5C11 path"});const d=resolveUserFilePath(r,u);if(d.error)return t.status(d.status).json({error:d.error});try{(0,external_fs_.mkdirSync)(d.resolved,{recursive:!0}),t.json({ok:!0,path:d.resolved.replace(/\\/g,"/")})}catch(p){t.status(400).json({error:sanitizeError(p.message)})}})),app.delete("/api/files/delete",checkAuth,express.json(),((r,t)=>{const u=getUserInfo(r.headers.cookie),d=r.body?.sessionId||r.query.sessionId,p=d?manager.get(d):null;if(!(p&&u&&(u.role==="admin"||p.owner===u.name))&&(!u||u.role!=="admin"))return t.status(403).json({error:"\u6CA1\u6709\u5220\u9664\u6743\u9650"});const{path:x}=r.body||{};if(!x)return t.status(400).json({error:"\u7F3A\u5C11 path"});const $=resolveUserFilePath(r,x);if($.error)return t.status($.status).json({error:$.error});if($.resolved===$.userRoot)return t.status(403).json({error:"\u4E0D\u5141\u8BB8\u5220\u9664\u6839\u76EE\u5F55"});try{(0,external_fs_.statSync)($.resolved).isDirectory()?(0,external_fs_.rmSync)($.resolved,{recursive:!1}):(0,external_fs_.unlinkSync)($.resolved),t.json({ok:!0});const w=r.body?.sessionId||r.query.sessionId;w&&io.to(w).emit("files:changed",{sessionId:w})}catch(b){t.status(400).json({error:sanitizeError(b.message)})}})),app.get("/qr/:sessionId",checkAuth,(async(r,t)=>{const u=getTailscaleIP(),d=(0,external_crypto_.randomBytes)(16).toString("hex");userSessions.set(d,{role:"operator",name:"\u626B\u7801\u7528\u6237",onetime:!0,lastUsed:Date.now()});const p=`http://${u}:${PORT}/terminal/${r.params.sessionId}?token=${d}`;try{const h=await lib.toString(p,{type:"svg"});t.setHeader("Content-Type","image/svg+xml"),t.send(h)}catch(h){t.status(500).send(h.message)}})),io.use(((r,t)=>{const u=r.handshake.headers.cookie;if(hasValidSession(u)){const h=getUserInfo(u);return r.data.role=h?.role||"admin",r.data.userName=h?.name||"\u7528\u6237",r.data.dir=h?.dir||null,t()}const d=r.handshake.auth?.password;if(d){loadUsers();const h=lookupUser(d);if(h)return r.data.role="operator",r.data.userName=h.name,r.data.dir=h.dir,t()}if((r.handshake.auth?.token||r.handshake.query?.token)===TOKEN)return r.data.role="admin",r.data.userName="\u7BA1\u7406\u5458",t();t(new Error("\u672A\u6388\u6743"))}));const EXCLUSIVE_RELEASE_DELAY=120*1e3;let _exclusiveReleaseTimer=null;function checkExclusiveRelease(){if(!isExclusiveMode()||!activeUser)return;let r=!1;for(const[,t]of io.sockets.sockets)if(t.data.userName===activeUser.name){r=!0;break}if(r){_exclusiveReleaseTimer&&(clearTimeout(_exclusiveReleaseTimer),_exclusiveReleaseTimer=null);return}_exclusiveReleaseTimer||(_exclusiveReleaseTimer=setTimeout((()=>{if(_exclusiveReleaseTimer=null,!!activeUser){for(const[,t]of io.sockets.sockets)if(t.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 t of activeUser.tokens)userSessions.delete(t);activeUser=null}}),EXCLUSIVE_RELEASE_DELAY))}let _broadcastTimer=null;function broadcastSessions(){_broadcastTimer||(_broadcastTimer=setTimeout((()=>{_broadcastTimer=null;for(const[,r]of io.sockets.sockets)r.emit("sessions",manager.list(r.data.userName,r.data.role==="admin"))}),100))}io.on("connection",(r=>{const t=r.handshake.address?.replace("::ffff:","")||"?",u=r.handshake.headers?.referer||"?";console.log(`[\u8FDE\u63A5] ${r.data.userName}(${r.data.role}) \u4ECE ${t} \u63A5\u5165 id=${r.id} \u9875\u9762=${u}`),_exclusiveReleaseTimer&&activeUser&&r.data.userName===activeUser.name&&(clearTimeout(_exclusiveReleaseTimer),_exclusiveReleaseTimer=null);let d=null,p=null,h=null,x=null,$=null;function b(){d&&(d.isController(r.id)&&(d.releaseControl(),io.emit("control-changed",{sessionId:d.id,holder:null,holderName:null})),d.removeViewer(r.id),p&&d.off("data",p),h&&d.off("agent:message",h),x&&d.off("agent:busy",x),$&&d.off("agent:error",$),p=null,h=null,x=null,$=null)}r.on("join",(w=>{const j=manager.get(w);if(!j){r.emit("error",{message:`\u4F1A\u8BDD ${w} \u672A\u627E\u5230`});return}if(r.data.role!=="admin"&&j.owner&&j.owner!==r.data.userName){r.emit("error",{message:"\u6CA1\u6709\u52A0\u5165\u8BE5\u4F1A\u8BDD\u7684\u6743\u9650"});return}b(),d=j,d.addViewer(r.id,r.data.userName),r.join(w),h=N=>r.emit("agent:message",N),d.on("agent:message",h),x=N=>r.emit("agent:busy",N),d.on("agent:busy",x),$=N=>r.emit("agent:error",N),d.on("agent:error",$),console.log(`[\u52A0\u5165] ${r.data.userName} \u52A0\u5165\u4F1A\u8BDD "${j.title}" (${w})`),r.emit("joined",{...j.toJSON(),role:r.data.role}),j._history?.length&&r.emit("agent:history",j._history),broadcastSessions()})),r.on("disconnect",(w=>{console.log(`[\u65AD\u5F00] ${r.data.userName} \u79BB\u5F00\u4F1A\u8BDD "${d?.title||"?"}" id=${r.id} \u539F\u56E0=${w}`),b(),broadcastSessions(),checkExclusiveRelease(),manager.persist()})),r.on("agent:query",(async({prompt:w}={})=>{if(!d||d.mode!=="agent"){r.emit("agent:error",{message:"\u5F53\u524D\u4E0D\u662F Agent \u6A21\u5F0F\u4F1A\u8BDD"});return}if(!d.isController(r.id)){r.emit("control-denied",{reason:"\u4F60\u6CA1\u6709\u5F53\u524D\u4F1A\u8BDD\u7684\u63A7\u5236\u6743"});return}if(d.isBusy){r.emit("agent:error",{message:"\u6B63\u5728\u5904\u7406\u4E2D\uFF0C\u8BF7\u7B49\u5F85\u5B8C\u6210"});return}try{await d.query(w)}catch(j){console.error("[agent:query] \u9519\u8BEF:",j.message);try{r.connected&&r.emit("agent:error",{message:j.message})}catch{}}})),r.on("agent:interrupt",(()=>{if(!(!d||d.mode!=="agent")){if(!d.isController(r.id)){r.emit("control-denied",{reason:"\u4F60\u6CA1\u6709\u5F53\u524D\u4F1A\u8BDD\u7684\u63A7\u5236\u6743"});return}d.interrupt()}})),r.on("agent:permission",(({requestId:w,allow:j,toolName:N,approveAll:I}={})=>{if(!(!d||d.mode!=="agent")){if(!d.isController(r.id)){r.emit("control-denied",{reason:"\u4F60\u6CA1\u6709\u5F53\u524D\u4F1A\u8BDD\u7684\u63A7\u5236\u6743"});return}d.respondPermission(w,j,N,I)}})),r.on("set-mode",(({sessionId:w,mode:j}={})=>{const N=w?manager.get(w):d;if(N){if(r.data.role!=="admin"&&N.owner&&N.owner!==r.data.userName){r.emit("error",{message:"\u6CA1\u6709\u66F4\u6539\u6A21\u5F0F\u7684\u6743\u9650"});return}N.setPermissionMode&&(N.setPermissionMode(j),console.log(`[\u6A21\u5F0F] ${r.data.userName} \u5207\u6362\u4F1A\u8BDD "${N.title}" \u4E3A ${j}`)),io.emit("mode-changed",{sessionId:N.id,mode:j}),manager.persist()}})),r.on("create-agent",((w,j)=>{if(!hasPermission(r.data.role,"operator")){typeof j=="function"&&j({ok:!1,error:"\u6CA1\u6709\u521B\u5EFA\u4F1A\u8BDD\u7684\u6743\u9650"});return}{const U=getEffectiveRoot(r.data.dir);let z=null;if(w?.cwd&&(z=(0,external_path_.resolve)((0,external_path_.normalize)(U),(0,external_path_.normalize)(w.cwd))),z&&z!==U&&!z.startsWith(U+external_path_.sep)){typeof j=="function"&&j({ok:!1,error:"\u4E0D\u80FD\u5728\u5141\u8BB8\u76EE\u5F55\u4E4B\u5916\u521B\u5EFA\u4F1A\u8BDD"});return}w={...w,cwd:z||U}}const N=w.cwd,I=(0,external_path_.join)(N,".zihi-sessions",`${Date.now()}-${(0,external_crypto_.randomUUID)().slice(0,8)}`);try{(0,external_fs_.mkdirSync)(I,{recursive:!0})}catch(U){typeof j=="function"&&j({ok:!1,error:`\u521B\u5EFA\u9694\u79BB\u76EE\u5F55\u5931\u8D25: ${U.message}`});return}w={...w,cwd:I};try{const U=manager.createAgent({...w,owner:r.data.userName,userDir:r.data.dir});console.log(`[\u4F1A\u8BDD] ${r.data.userName} \u521B\u5EFA Agent \u4F1A\u8BDD "${U.title}" (${U.id})`),broadcastSessions(),typeof j=="function"&&j({ok:!0,session:U.toJSON()})}catch(U){typeof j=="function"&&j({ok:!1,error:U.message})}})),r.on("take-control",(({sessionId:w}={})=>{const j=w?manager.get(w):d;if(j){if(!hasPermission(r.data.role,"operator")){r.emit("control-denied",{reason:"\u4F60\u7684\u89D2\u8272\u6CA1\u6709\u63A7\u5236\u6743\u9650"});return}if(j.controlHolder&&j.controlHolder!==r.id){const N=j.controlHolderName===r.data.userName;if(r.data.role!=="admin"&&!N){r.emit("control-denied",{reason:"\u5176\u4ED6\u7528\u6237\u6B63\u5728\u63A7\u5236\u4E2D\uFF0C\u8BF7\u7B49\u5F85\u91CA\u653E"});return}}j.takeControl(r.id,r.data.userName),console.log(`[\u63A7\u5236] ${r.data.userName} \u83B7\u53D6\u4F1A\u8BDD "${j.title}" \u7684\u63A7\u5236\u6743`),io.emit("control-changed",{sessionId:j.id,holder:r.id,holderName:r.data.userName})}})),r.on("release-control",(({sessionId:w}={})=>{const j=w?manager.get(w):d;j&&j.isController(r.id)&&(console.log(`[\u63A7\u5236] ${r.data.userName} \u91CA\u653E\u4F1A\u8BDD "${j.title}" \u7684\u63A7\u5236\u6743`),j.releaseControl(),io.emit("control-changed",{sessionId:j.id,holder:null,holderName:null}))})),r.on("rename",(({sessionId:w,title:j}={})=>{const N=w?manager.get(w):d;if(!(!N||!j)){if(r.data.role!=="admin"&&N.owner&&N.owner!==r.data.userName){r.emit("error",{message:"\u6CA1\u6709\u91CD\u547D\u540D\u4F1A\u8BDD\u7684\u6743\u9650"});return}N.title=j,io.emit("session-renamed",{sessionId:N.id,title:j}),broadcastSessions(),manager.persist()}})),r.on("resize",(({cols:w,rows:j})=>d?.resize(w,j))),r.on("list",(()=>r.emit("sessions",manager.list(r.data.userName,r.data.role==="admin")))),r.on("dirs",((w,j)=>{if(typeof j!="function")return;const N=getEffectiveRoot(r.data.dir),I=process.platform==="win32"?"\\":"/";let U=(w||N).replace(/[/\\]+$/,"")||N;process.platform==="win32"&&/^[A-Za-z]:$/.test(U)&&(U+="\\");const z=(0,external_path_.resolve)((0,external_path_.normalize)(U));z!==N&&!z.startsWith(N+I)&&(U=N);try{const O=(0,external_fs_.readdirSync)(U,{withFileTypes:!0}).filter((H=>H.isDirectory()&&!H.name.startsWith("."))).map((H=>({name:H.name,path:U.replace(/[/\\]+$/,"")+I+H.name}))).sort(((H,V)=>H.name.localeCompare(V.name))),B=(0,external_path_.join)(U,".."),L=(0,external_path_.resolve)((0,external_path_.normalize)(B)),W=L===N||L.startsWith(N+I);j({ok:!0,current:U,parent:W&&B!==U?B:null,dirs:O})}catch(O){j({ok:!1,error:O.message})}})),r.on("mkdir",((w,j)=>{if(typeof j!="function")return;if(!hasPermission(r.data.role,"operator"))return j({ok:!1,error:"\u6CA1\u6709\u521B\u5EFA\u6587\u4EF6\u5939\u7684\u6743\u9650"});const N=getEffectiveRoot(r.data.dir),I=(0,external_path_.resolve)((0,external_path_.normalize)(w));if(!I.startsWith(N+external_path_.sep)&&I!==N)return j({ok:!1,error:"\u4E0D\u80FD\u5728\u5141\u8BB8\u76EE\u5F55\u4E4B\u5916\u521B\u5EFA\u6587\u4EF6\u5939"});try{(0,external_fs_.mkdirSync)(I,{recursive:!0}),j({ok:!0})}catch(U){j({ok:!1,error:U.message})}})),r.on("kill",(w=>{if(!hasPermission(r.data.role,"operator")){r.emit("error",{message:"\u6CA1\u6709\u5220\u9664\u4F1A\u8BDD\u7684\u6743\u9650"});return}const j=manager.get(w);if(j&&r.data.role!=="admin"&&j.owner&&j.owner!==r.data.userName){r.emit("error",{message:"\u6CA1\u6709\u5220\u9664\u8BE5\u4F1A\u8BDD\u7684\u6743\u9650"});return}console.log(`[\u4F1A\u8BDD] ${r.data.userName} \u5220\u9664\u4F1A\u8BDD ${w}`),manager.kill(w),broadcastSessions()})),r.on("kick-user",(({socketId:w,sessionId:j}={},N)=>{if(!hasPermission(r.data.role,"admin")){typeof N=="function"&&N({ok:!1,error:"\u6CA1\u6709\u8E22\u51FA\u7528\u6237\u7684\u6743\u9650"});return}const I=io.sockets.sockets.get(w);if(!I){typeof N=="function"&&N({ok:!1,error:"\u76EE\u6807\u7528\u6237\u4E0D\u5728\u7EBF"});return}const U=I.data.userName||"\u672A\u77E5";console.log(`[\u8E22\u51FA] ${r.data.userName} \u8E22\u51FA\u4E86\u7528\u6237 ${U} (${w})`),I.emit("kicked",{reason:`\u4F60\u5DF2\u88AB\u7BA1\u7406\u5458 ${r.data.userName} \u8E22\u51FA`}),I.disconnect(!0),typeof N=="function"&&N({ok:!0,name:U})})),r.on("list-viewers",(({sessionId:w}={},j)=>{if(typeof j!="function")return;const N=w?manager.get(w):d;if(!N){j({ok:!1,error:"\u4F1A\u8BDD\u4E0D\u5B58\u5728"});return}const I=[];for(const U of N._viewers){const z=io.sockets.sockets.get(U);z&&I.push({socketId:U,userName:z.data.userName||"\u672A\u77E5",role:z.data.role||"viewer",isController:N.controlHolder===U})}j({ok:!0,viewers:I})}))}));const CONTROL_TIMEOUT=300*1e3;setInterval((()=>{for(const r of manager.listSessions()){r.controlHolder&&(!io.sockets.sockets.get(r.controlHolder)||r.lastInputTime&&Date.now()-r.lastInputTime>CONTROL_TIMEOUT)&&(r.releaseControl(),io.emit("control-changed",{sessionId:r.id,holder:null,holderName:null}));for(const t of r._viewers)io.sockets.sockets.has(t)||r.removeViewer(t)}}),60*1e3);function showStartupInfo(){const r=httpServer.address().port;PORT=r;const t=getTailscaleIP(),u=`http://${t}:${r}/admin`;console.log(`
479
479
  \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(" zihi \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(`
480
480
  \u5730\u5740: http://${t}:${r}`),console.log(` \u5BC6\u7801: ${PASSWORD} (\u7F16\u8F91 ~/.zihi/password \u53EF\u4FEE\u6539)`),console.log(`
481
481
  \u626B\u63CF\u4E8C\u7EF4\u7801\u5728\u624B\u673A\u4E0A\u6253\u5F00:
package/dist/package.json CHANGED
@@ -1 +1 @@
1
- {"type":"module","version":"1.1.10"}
1
+ {"type":"module","version":"1.1.13"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wendongfly/zihi",
3
- "version": "1.1.11",
3
+ "version": "1.1.13",
4
4
  "description": "AI Agent terminal sharing with interactive permission approval via Claude Agent SDK",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",