@wendongfly/zihi 1.1.6 → 1.1.7

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
@@ -224,6 +224,7 @@
224
224
  .fp-item .fp-name { flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
225
225
  .fp-item .fp-size { color: #8b949e; font-size: 0.7rem; white-space: nowrap; }
226
226
  .fp-item .fp-dl { color: #58a6ff; font-size: 0.7rem; text-decoration: none; padding: 0.2rem 0.4rem; }
227
+ .fp-item .fp-del { color: #f85149; font-size: 0.7rem; cursor: pointer; padding: 0.2rem 0.4rem; background: none; border: none; }
227
228
  .fp-empty { text-align: center; color: #8b949e; padding: 2rem 0.75rem; font-size: 0.85rem; }
228
229
  .fp-drop-zone {
229
230
  margin: 0.5rem 0.75rem; padding: 1.5rem; border: 2px dashed #30363d; border-radius: 8px;
@@ -459,13 +460,13 @@
459
460
  <div class="fp-header">
460
461
  <h3 id="fp-title">文件管理</h3>
461
462
  <button class="fp-btn" onclick="fpUploadClick()">上传</button>
462
- <button class="fp-btn" onclick="fpStartSync()">同步</button>
463
+ <button class="fp-btn" onclick="fpMkdir()">新建</button>
463
464
  <button class="fp-btn" onclick="closeFilePanel()">关闭</button>
464
465
  </div>
465
466
  <div class="fp-sync-bar" id="fp-sync-bar" style="display:none">
466
467
  <span class="sync-dir" id="fp-sync-dir"></span>
467
468
  <button class="fp-btn" onclick="fpPickDir()">换目录</button>
468
- <button class="fp-btn" onclick="fpExitSync()">退出同步</button>
469
+ <button class="fp-btn" onclick="fpExitSync()">关闭同步</button>
469
470
  </div>
470
471
  <div class="fp-path" id="fp-path"></div>
471
472
  <div class="fp-list" id="fp-list"></div>
@@ -1821,10 +1822,11 @@
1821
1822
  for (var i = 0; i < data.entries.length; i++) {
1822
1823
  var e = data.entries[i];
1823
1824
  var full = data.current + '/' + e.name;
1825
+ var eFull = escHtml(full).replace(/'/g, "\\'");
1824
1826
  if (e.type === 'dir') {
1825
- html += '<div class="fp-item" onclick="fpNav(\'' + escHtml(full).replace(/'/g, "\\'") + '\')"><span class="fp-icon">📁</span><span class="fp-name">' + escHtml(e.name) + '</span></div>';
1827
+ html += '<div class="fp-item"><span class="fp-icon" onclick="fpNav(\'' + eFull + '\')">📁</span><span class="fp-name" onclick="fpNav(\'' + eFull + '\')">' + escHtml(e.name) + '</span><button class="fp-del" onclick="event.stopPropagation();fpDelete(\'' + eFull + '\')">删除</button></div>';
1826
1828
  } else {
1827
- html += '<div class="fp-item"><span class="fp-icon">📄</span><span class="fp-name">' + escHtml(e.name) + '</span><span class="fp-size">' + fpFormatSize(e.size) + '</span><a class="fp-dl" href="/api/files/download?path=' + encodeURIComponent(full) + '&' + fpQ + '" download title="下载">下载</a></div>';
1829
+ html += '<div class="fp-item"><span class="fp-icon">📄</span><span class="fp-name">' + escHtml(e.name) + '</span><span class="fp-size">' + fpFormatSize(e.size) + '</span><a class="fp-dl" href="/api/files/download?path=' + encodeURIComponent(full) + '&' + fpQ + '" download title="下载">下载</a><button class="fp-del" onclick="event.stopPropagation();fpDelete(\'' + eFull + '\')">删除</button></div>';
1828
1830
  }
1829
1831
  }
1830
1832
  list.innerHTML = html || '<div class="fp-empty">空目录</div>';
@@ -1835,6 +1837,36 @@
1835
1837
 
1836
1838
  window.fpNav = function(path) { fpCurrentPath = path; if (syncMode) fpComputeDiff(); else fpLoadFiles(); };
1837
1839
 
1840
+ window.fpMkdir = async function() {
1841
+ var name = prompt('文件夹名称:');
1842
+ if (!name) return;
1843
+ var dirPath = fpCurrentPath ? fpCurrentPath + '/' + name : name;
1844
+ try {
1845
+ var r = await fetch('/api/files/mkdir', {
1846
+ method: 'POST',
1847
+ headers: { 'Content-Type': 'application/json' },
1848
+ body: JSON.stringify({ path: dirPath, sessionId: SESSION_ID }),
1849
+ });
1850
+ var data = await r.json();
1851
+ if (data.ok) fpLoadFiles();
1852
+ else alert('创建失败: ' + (data.error || '未知错误'));
1853
+ } catch (e) { alert('创建失败: ' + e.message); }
1854
+ };
1855
+
1856
+ window.fpDelete = async function(path) {
1857
+ if (!confirm('确定删除?')) return;
1858
+ try {
1859
+ var r = await fetch('/api/files/delete', {
1860
+ method: 'DELETE',
1861
+ headers: { 'Content-Type': 'application/json' },
1862
+ body: JSON.stringify({ path: path, sessionId: SESSION_ID }),
1863
+ });
1864
+ var data = await r.json();
1865
+ if (data.ok) fpLoadFiles();
1866
+ else alert('删除失败: ' + (data.error || '未知错误'));
1867
+ } catch (e) { alert('删除失败: ' + e.message); }
1868
+ };
1869
+
1838
1870
  function fpFormatSize(bytes) {
1839
1871
  if (bytes == null) return '';
1840
1872
  if (bytes < 1024) return bytes + ' B';
package/dist/index.js CHANGED
@@ -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);if(!u||u.role!=="admin")return t.status(403).json({error:"\u4EC5\u7BA1\u7406\u5458\u53EF\u5220\u9664"});const{path:d}=r.body||{};if(!d)return t.status(400).json({error:"\u7F3A\u5C11 path"});const p=resolveUserFilePath(r,d);if(p.error)return t.status(p.status).json({error:p.error});if(p.resolved===p.userRoot)return t.status(403).json({error:"\u4E0D\u5141\u8BB8\u5220\u9664\u6839\u76EE\u5F55"});try{(0,external_fs_.statSync)(p.resolved).isDirectory()?(0,external_fs_.rmSync)(p.resolved,{recursive:!1}):(0,external_fs_.unlinkSync)(p.resolved),t.json({ok:!0});const x=r.body?.sessionId||r.query.sessionId;x&&io.to(x).emit("files:changed",{sessionId:x})}catch(h){t.status(400).json({error:sanitizeError(h.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),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(`
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.5"}
1
+ {"type":"module","version":"1.1.6"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wendongfly/zihi",
3
- "version": "1.1.6",
3
+ "version": "1.1.7",
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",