@wendongfly/zihi 1.1.20 → 1.1.21
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 +3 -2
- package/dist/index.js +1 -1
- package/dist/index.min.js +1 -1
- package/dist/package.json +1 -1
- package/package.json +1 -1
package/dist/index.min.js
CHANGED
|
@@ -390,7 +390,7 @@ Content-Length: `+j+`\r
|
|
|
390
390
|
helper = store --file ${m.replace(/\\/g,"/")}
|
|
391
391
|
[user]
|
|
392
392
|
name = ${f}
|
|
393
|
-
`,{mode:384})}catch{}console.log(`[\u7BA1\u7406] \u6DFB\u52A0\u7528\u6237 ${f} (\u76EE\u5F55: ${v})`),d.json({ok:!0})})),app.delete("/api/admin/user",checkAdminAuth,express.json(),((w,d)=>{const{name:c}=w.body||{};if(!c)return d.json({ok:!1,error:"\u8BF7\u6307\u5B9A\u7528\u6237\u540D\u79F0"});if(loadUsers(),!listUsers().find((f=>f.name===c)))return d.json({ok:!1,error:"\u7528\u6237\u4E0D\u5B58\u5728"});try{const f=(0,external_path_.join)(configDir,"users.json"),v=JSON.parse((0,external_fs_.readFileSync)(f,"utf8"));for(const[g,m]of Object.entries(v.users||{}))if(m.name===c){delete v.users[g];break}(0,external_fs_.writeFileSync)(f,JSON.stringify(v,null,2),{mode:384}),loadUsers(),console.log(`[\u7BA1\u7406] \u5220\u9664\u7528\u6237 ${c}`),d.json({ok:!0})}catch(f){d.json({ok:!1,error:f.message})}})),app.post("/api/admin/user/password",checkAdminAuth,express.json(),((w,d)=>{const{name:c,password:f}=w.body||{};if(!c||!f)return d.json({ok:!1,error:"\u540D\u79F0\u548C\u65B0\u5BC6\u7801\u5FC5\u586B"});try{const v=(0,external_path_.join)(configDir,"users.json"),g=JSON.parse((0,external_fs_.readFileSync)(v,"utf8"));let m=null;for(const[u,o]of Object.entries(g.users||{}))if(o.name===c){m=o,delete g.users[u];break}if(!m)return d.json({ok:!1,error:"\u7528\u6237\u4E0D\u5B58\u5728"});g.users[f]=m,(0,external_fs_.writeFileSync)(v,JSON.stringify(g,null,2),{mode:384}),loadUsers(),console.log(`[\u7BA1\u7406] \u4FEE\u6539\u7528\u6237 ${c} \u7684\u5BC6\u7801`),d.json({ok:!0})}catch(v){d.json({ok:!1,error:v.message})}})),app.post("/api/admin/password",checkAdminAuth,express.json(),((w,d)=>{const{password:c}=w.body||{};if(!c||c.length<4)return d.json({ok:!1,error:"\u5BC6\u7801\u81F3\u5C114\u4F4D"});try{(0,external_fs_.writeFileSync)((0,external_path_.join)(configDir,"password"),c,{mode:384}),PASSWORD=c,d.json({ok:!0}),console.log("[\u7BA1\u7406] \u7BA1\u7406\u5BC6\u7801\u5DF2\u4FEE\u6539")}catch(f){d.json({ok:!1,error:f.message})}})),app.post("/upload",checkAuth,((w,d)=>{const c=w.query.sessionId,f=c?manager.get(c):null,v=f?.cwd?(0,external_path_.join)(f.cwd,"upload"):uploadDir;(0,external_fs_.mkdirSync)(v,{recursive:!0}),multer({storage:multer.diskStorage({destination:v,filename:(g,m,u)=>{const o=m.originalname.match(/\.[^.]+$/)?.[0]||".jpg";u(null,`${Date.now()}-${(0,external_crypto_.randomBytes)(3).toString("hex")}${o}`)}}),limits:{fileSize:20*1024*1024},fileFilter:(g,m,u)=>u(null,m.mimetype.startsWith("image/"))}).single("image")(w,d,(g=>{if(g)return d.status(400).json({error:g.message});if(!w.file)return d.status(400).json({error:"\u6CA1\u6709\u56FE\u7247"});d.json({path:(0,external_path_.resolve)(w.file.path).replace(/\\/g,"/")})}))}));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(w){return MIME_MAP[(0,external_path_.extname)(w).toLowerCase()]||"application/octet-stream"}function sanitizeError(w){return(w||"\u64CD\u4F5C\u5931\u8D25").replace(/[<>"'&]/g,(d=>({"<":"<",">":">",'"':""","'":"'","&":"&"})[d]))}function resolveUserFilePath(w,d){const c=getUserInfo(w.headers.cookie),f=w.query.sessionId||w.body?.sessionId,v=f?manager.get(f):null,g=v?.cwd?(0,external_path_.resolve)((0,external_path_.normalize)(v.cwd)):null,m=getEffectiveRoot(c?.dir),u=g&&g.startsWith(m)?g:m;let o=d?(0,external_path_.resolve)((0,external_path_.normalize)(u),(0,external_path_.normalize)(d)):u;if(process.platform==="win32"&&/^[A-Za-z]:$/.test(o)&&(o+="\\"),!o.startsWith(u))return{error:"\u8BBF\u95EE\u88AB\u62D2\u7EDD\uFF1A\u8DEF\u5F84\u8D85\u51FA\u5141\u8BB8\u8303\u56F4",status:403};try{if(!(0,external_fs_.realpathSync)(o).startsWith(u))return{error:"\u8BBF\u95EE\u88AB\u62D2\u7EDD\uFF1A\u7B26\u53F7\u94FE\u63A5\u6307\u5411\u5141\u8BB8\u8303\u56F4\u5916",status:403}}catch{}return{resolved:o,userRoot:u,info:c}}app.get("/api/files/list",checkAuth,((w,d)=>{const c=resolveUserFilePath(w,w.query.path);if(c.error)return d.status(c.status).json({error:c.error});try{let g=function(s,i){const r=(0,external_fs_.readdirSync)(s,{withFileTypes:!0}).filter((e=>f||!e.name.startsWith("."))),n=[];for(const e of r){const l=(0,external_path_.join)(s,e.name),p=i?i+"/"+e.name:e.name;let h=0,b=null;try{const _=(0,external_fs_.statSync)(l);h=_.size,b=_.mtime}catch{}if(e.isDirectory())if(v)try{n.push(...g(l,p))}catch{}else n.push({name:p,type:"dir",size:h,mtime:b});else n.push({name:p,type:"file",size:h,mtime:b})}return n};if(!(0,external_fs_.statSync)(c.resolved).isDirectory())return d.status(400).json({error:"\u4E0D\u662F\u76EE\u5F55"});const f=w.query.showHidden==="1",v=w.query.recursive==="1",m=g(c.resolved,"").sort(((s,i)=>s.type===i.type?s.name.localeCompare(i.name):s.type==="dir"?-1:1)),u=(0,external_path_.join)(c.resolved,".."),o=(0,external_path_.resolve)((0,external_path_.normalize)(u)),a=o.startsWith(c.userRoot)&&o!==c.resolved,t=c.resolved.startsWith(c.userRoot)?c.resolved.slice(c.userRoot.length).replace(/^[/\\]+/,""):"";d.json({current:c.resolved.replace(/\\/g,"/"),currentRel:t.replace(/\\/g,"/"),parent:a?o.replace(/\\/g,"/"):null,root:c.userRoot.replace(/\\/g,"/"),entries:m})}catch(f){d.status(400).json({error:sanitizeError(f.message)})}})),app.post("/api/files/upload",checkAuth,((w,d)=>{const c=resolveUserFilePath(w,w.query.path);if(c.error)return d.status(c.status).json({error:c.error});(0,external_fs_.mkdirSync)(c.resolved,{recursive:!0}),multer({storage:multer.diskStorage({destination:(f,v,g)=>{const m=v.originalname||"",u=m.lastIndexOf("/"),o=u>0?m.slice(0,u):"",a=o?(0,external_path_.join)(c.resolved,o):c.resolved;if(!(0,external_path_.resolve)(a).startsWith(c.resolved))return g(new Error("\u8DEF\u5F84\u4E0D\u5408\u6CD5"));(0,external_fs_.mkdirSync)(a,{recursive:!0}),g(null,a)},filename:(f,v,g)=>{const m=v.originalname||"",u=m.lastIndexOf("/"),o=u>0?m.slice(u+1):(0,external_path_.basename)(m);g(null,o||`${Date.now()}.bin`)}}),limits:{fileSize:500*1024*1024},defParamCharset:"utf-8"}).array("files",100)(w,d,(f=>{if(f)return d.status(400).json({error:f.message});if(!w.files?.length)return d.status(400).json({error:"\u6CA1\u6709\u6587\u4EF6"});const v={ok:!0,files:w.files.map((m=>({name:m.filename,path:(0,external_path_.resolve)(m.path).replace(/\\/g,"/"),size:m.size})))};d.json(v);const g=w.query.sessionId;g&&io.to(g).emit("files:changed",{sessionId:g})}))})),app.get("/api/files/download",checkAuth,((w,d)=>{if(!w.query.path)return d.status(400).json({error:"\u7F3A\u5C11 path \u53C2\u6570"});const c=resolveUserFilePath(w,w.query.path);if(c.error)return d.status(c.status).json({error:c.error});try{const f=(0,external_fs_.statSync)(c.resolved);if(!f.isFile())return d.status(400).json({error:"\u4E0D\u662F\u6587\u4EF6"});const v=(0,external_path_.basename)(c.resolved);d.setHeader("Content-Type",getMime(v)),d.setHeader("Content-Length",f.size),d.setHeader("Content-Disposition",`attachment; filename*=UTF-8''${encodeURIComponent(v)}`),(0,external_fs_.createReadStream)(c.resolved).pipe(d)}catch(f){d.status(404).json({error:sanitizeError(f.message)})}})),app.get("/api/files/preview",checkAuth,((w,d)=>{if(!w.query.path)return d.status(400).json({error:"\u7F3A\u5C11 path \u53C2\u6570"});const c=resolveUserFilePath(w,w.query.path);if(c.error)return d.status(c.status).json({error:c.error});try{const f=(0,external_fs_.statSync)(c.resolved);if(!f.isFile())return d.status(400).json({error:"\u4E0D\u662F\u6587\u4EF6"});const v=(0,external_path_.basename)(c.resolved),g=getMime(v);d.setHeader("Content-Type",g),d.setHeader("Content-Length",f.size),d.setHeader("Content-Disposition",`inline; filename*=UTF-8''${encodeURIComponent(v)}`),(0,external_fs_.createReadStream)(c.resolved).pipe(d)}catch(f){d.status(404).json({error:sanitizeError(f.message)})}})),app.post("/api/files/mkdir",checkAuth,express.json(),((w,d)=>{const{path:c}=w.body||{};if(!c)return d.status(400).json({error:"\u7F3A\u5C11 path"});const f=resolveUserFilePath(w,c);if(f.error)return d.status(f.status).json({error:f.error});try{(0,external_fs_.mkdirSync)(f.resolved,{recursive:!0}),d.json({ok:!0,path:f.resolved.replace(/\\/g,"/")})}catch(v){d.status(400).json({error:sanitizeError(v.message)})}})),app.delete("/api/files/delete",checkAuth,express.json(),((w,d)=>{const c=getUserInfo(w.headers.cookie),f=w.body?.sessionId||w.query.sessionId,v=f?manager.get(f):null;if(!(v&&c&&(c.role==="admin"||v.owner===c.name))&&(!c||c.role!=="admin"))return d.status(403).json({error:"\u6CA1\u6709\u5220\u9664\u6743\u9650"});const{path:m}=w.body||{};if(!m)return d.status(400).json({error:"\u7F3A\u5C11 path"});const u=resolveUserFilePath(w,m);if(u.error)return d.status(u.status).json({error:u.error});if(u.resolved===u.userRoot)return d.status(403).json({error:"\u4E0D\u5141\u8BB8\u5220\u9664\u6839\u76EE\u5F55"});try{(0,external_fs_.statSync)(u.resolved).isDirectory()?(0,external_fs_.rmSync)(u.resolved,{recursive:!1}):(0,external_fs_.unlinkSync)(u.resolved),d.json({ok:!0});const o=w.body?.sessionId||w.query.sessionId;o&&io.to(o).emit("files:changed",{sessionId:o})}catch(o){d.status(400).json({error:sanitizeError(o.message)})}})),app.get("/qr/:sessionId",checkAuth,(async(w,d)=>{const c=getTailscaleIP(),f=(0,external_crypto_.randomBytes)(16).toString("hex");userSessions.set(f,{role:"operator",name:"\u626B\u7801\u7528\u6237",onetime:!0,lastUsed:Date.now()});const v=`http://${c}:${PORT}/terminal/${w.params.sessionId}?token=${f}`;try{const g=await lib.toString(v,{type:"svg"});d.setHeader("Content-Type","image/svg+xml"),d.send(g)}catch(g){d.status(500).send(g.message)}})),io.use(((w,d)=>{const c=w.handshake.headers.cookie;if(hasValidSession(c)){const v=getUserInfo(c);return w.data.role=v?.role||"admin",w.data.userName=v?.name||"\u7528\u6237",w.data.dir=v?.dir||null,d()}const f=w.handshake.auth?.password;if(f){loadUsers();const v=lookupUser(f);if(v)return w.data.role="operator",w.data.userName=v.name,w.data.dir=v.dir,d()}if((w.handshake.auth?.token||w.handshake.query?.token)===TOKEN)return w.data.role="admin",w.data.userName="\u7BA1\u7406\u5458",d();d(new Error("\u672A\u6388\u6743"))}));const EXCLUSIVE_RELEASE_DELAY=120*1e3;let _exclusiveReleaseTimer=null;function checkExclusiveRelease(){if(!isExclusiveMode()||!activeUser)return;let w=!1;for(const[,d]of io.sockets.sockets)if(d.data.userName===activeUser.name){w=!0;break}if(w){_exclusiveReleaseTimer&&(clearTimeout(_exclusiveReleaseTimer),_exclusiveReleaseTimer=null);return}_exclusiveReleaseTimer||(_exclusiveReleaseTimer=setTimeout((()=>{if(_exclusiveReleaseTimer=null,!!activeUser){for(const[,d]of io.sockets.sockets)if(d.data.userName===activeUser.name)return;console.log(`[\u72EC\u5360] ${activeUser.name} \u5DF2\u79BB\u7EBF\u8D85\u65F6\uFF0C\u91CA\u653E\u767B\u5F55\u72B6\u6001\uFF08\u4F1A\u8BDD\u4FDD\u7559\uFF09`);for(const d of activeUser.tokens)userSessions.delete(d);activeUser=null}}),EXCLUSIVE_RELEASE_DELAY))}let _broadcastTimer=null;function broadcastSessions(){_broadcastTimer||(_broadcastTimer=setTimeout((()=>{_broadcastTimer=null;for(const[,w]of io.sockets.sockets)w.emit("sessions",manager.list(w.data.userName,w.data.role==="admin"))}),100))}io.on("connection",(w=>{const d=w.handshake.address?.replace("::ffff:","")||"?";console.log(`[\u8FDE\u63A5] ${w.data.userName}(${w.data.role}) \u4ECE ${d} \u63A5\u5165 id=${w.id}`),_exclusiveReleaseTimer&&activeUser&&w.data.userName===activeUser.name&&(clearTimeout(_exclusiveReleaseTimer),_exclusiveReleaseTimer=null);let c=null,f=null,v=null,g=null,m=null;function u(){c&&(c.isController(w.id)&&(c.releaseControl(),io.emit("control-changed",{sessionId:c.id,holder:null,holderName:null})),c.removeViewer(w.id),f&&c.off("data",f),v&&c.off("agent:message",v),g&&c.off("agent:busy",g),m&&c.off("agent:error",m),f=null,v=null,g=null,m=null)}w.on("join",(o=>{const a=manager.get(o);if(!a){w.emit("error",{message:`\u4F1A\u8BDD ${o} \u672A\u627E\u5230`});return}if(u(),c=a,c.addViewer(w.id,w.data.userName),a.mode==="agent"?(v=t=>w.emit("agent:message",t),c.on("agent:message",v),g=t=>w.emit("agent:busy",t),c.on("agent:busy",g),m=t=>w.emit("agent:error",t),c.on("agent:error",m)):(f=t=>w.emit("output",t),c.on("data",f),c.once("exit",(t=>{w.emit("session-exit",{code:t}),f&&c?.off("data",f)})),spawnLocalTerminal(o,a.title)),console.log(`[\u52A0\u5165] ${w.data.userName} \u52A0\u5165\u4F1A\u8BDD "${a.title}" (${o}) \u6A21\u5F0F=${a.mode||"pty"}`),w.emit("joined",{...a.toJSON(),role:w.data.role}),a.mode==="agent")a._history?.length&&w.emit("agent:history",a._history);else if(a._scrollback?.length){const t=a._scrollback;if(t.length<=4096)w.emit("output",t);else{let s=0;(function i(){s>=t.length||(w.emit("output",t.slice(s,s+4096)),s+=4096,setImmediate(i))})()}}broadcastSessions()})),w.on("disconnect",(()=>{console.log(`[\u65AD\u5F00] ${w.data.userName} \u79BB\u5F00\u4F1A\u8BDD "${c?.title||"?"}" id=${w.id}`),u(),broadcastSessions(),checkExclusiveRelease(),manager.persist()})),w.on("agent:query",(async({prompt:o}={})=>{if(!c||c.mode!=="agent"){w.emit("agent:error",{message:"\u5F53\u524D\u4E0D\u662F Agent \u6A21\u5F0F\u4F1A\u8BDD"});return}if(!c.isController(w.id)){w.emit("control-denied",{reason:"\u4F60\u6CA1\u6709\u5F53\u524D\u4F1A\u8BDD\u7684\u63A7\u5236\u6743"});return}if(c.isBusy){w.emit("agent:error",{message:"\u6B63\u5728\u5904\u7406\u4E2D\uFF0C\u8BF7\u7B49\u5F85\u5B8C\u6210"});return}try{await c.query(o)}catch(a){console.error("[agent:query] \u9519\u8BEF:",a.message);try{w.connected&&w.emit("agent:error",{message:a.message})}catch{}}})),w.on("agent:interrupt",(()=>{if(!(!c||c.mode!=="agent")){if(!c.isController(w.id)){w.emit("control-denied",{reason:"\u4F60\u6CA1\u6709\u5F53\u524D\u4F1A\u8BDD\u7684\u63A7\u5236\u6743"});return}c.interrupt()}})),w.on("agent:clear",(()=>{if(!(!c||c.mode!=="agent")){if(!c.isController(w.id)){w.emit("control-denied",{reason:"\u4F60\u6CA1\u6709\u5F53\u524D\u4F1A\u8BDD\u7684\u63A7\u5236\u6743"});return}typeof c.clear=="function"&&(c.clear(),console.log(`[\u6E05\u9664] ${w.data.userName} \u6E05\u9664\u4F1A\u8BDD "${c.title}" \u7684\u5BF9\u8BDD\u4E0A\u4E0B\u6587`))}})),w.on("agent:permission",(({requestId:o,allow:a,toolName:t,approveAll:s}={})=>{if(!(!c||c.mode!=="agent")){if(!c.isController(w.id)){w.emit("control-denied",{reason:"\u4F60\u6CA1\u6709\u5F53\u524D\u4F1A\u8BDD\u7684\u63A7\u5236\u6743"});return}c.respondPermission(o,a,t,s)}})),w.on("set-mode",(({sessionId:o,mode:a}={})=>{const t=o?manager.get(o):c;if(t){if(w.data.role!=="admin"&&t.owner&&t.owner!==w.data.userName){w.emit("error",{message:"\u6CA1\u6709\u66F4\u6539\u6A21\u5F0F\u7684\u6743\u9650"});return}t.mode==="agent"&&typeof t.setPermissionMode=="function"&&(t.setPermissionMode(a),console.log(`[\u6A21\u5F0F] ${w.data.userName} \u66F4\u6539\u4F1A\u8BDD "${t.title}" \u4E3A ${a}`),io.emit("mode-changed",{sessionId:t.id,mode:a}),manager.persist())}})),w.on("create-agent",((o,a)=>{if(!hasPermission(w.data.role,"operator")){typeof a=="function"&&a({ok:!1,error:"\u6CA1\u6709\u521B\u5EFA\u4F1A\u8BDD\u7684\u6743\u9650"});return}{const i=getEffectiveRoot(w.data.dir);let r=null;if(o?.cwd&&(r=(0,external_path_.resolve)((0,external_path_.normalize)(i),(0,external_path_.normalize)(o.cwd))),r&&r!==i&&!r.startsWith(i+external_path_.sep)){typeof a=="function"&&a({ok:!1,error:"\u4E0D\u80FD\u5728\u5141\u8BB8\u76EE\u5F55\u4E4B\u5916\u521B\u5EFA\u4F1A\u8BDD"});return}o={...o,cwd:r||i}}const t=o.cwd,s=(0,external_path_.join)(t,".zihi-sessions",`${Date.now()}-${(0,external_crypto_.randomUUID)().slice(0,8)}`);try{(0,external_fs_.mkdirSync)(s,{recursive:!0})}catch(i){typeof a=="function"&&a({ok:!1,error:`\u521B\u5EFA\u9694\u79BB\u76EE\u5F55\u5931\u8D25: ${i.message}`});return}o={...o,cwd:s};try{const i=manager.createAgent({...o,owner:w.data.userName,userDir:w.data.dir});console.log(`[\u4F1A\u8BDD] ${w.data.userName} \u521B\u5EFA Agent \u4F1A\u8BDD "${i.title}" (${i.id})`),broadcastSessions(),typeof a=="function"&&a({ok:!0,session:i.toJSON()})}catch(i){typeof a=="function"&&a({ok:!1,error:i.message})}})),w.on("input",(o=>{if(!(o==null||!c)&&c.mode!=="agent"){if(!c.isController(w.id)){w.emit("control-denied",{reason:"\u4F60\u6CA1\u6709\u5F53\u524D\u4F1A\u8BDD\u7684\u63A7\u5236\u6743"});return}c.lastInputTime=Date.now(),c.write(o)}})),w.on("take-control",(({sessionId:o}={})=>{const a=o?manager.get(o):c;if(a){if(!hasPermission(w.data.role,"operator")){w.emit("control-denied",{reason:"\u4F60\u7684\u89D2\u8272\u6CA1\u6709\u63A7\u5236\u6743\u9650"});return}if(a.controlHolder&&a.controlHolder!==w.id){const t=a.controlHolderName===w.data.userName;if(w.data.role!=="admin"&&!t){w.emit("control-denied",{reason:"\u5176\u4ED6\u7528\u6237\u6B63\u5728\u63A7\u5236\u4E2D\uFF0C\u8BF7\u7B49\u5F85\u91CA\u653E"});return}}a.takeControl(w.id,w.data.userName),console.log(`[\u63A7\u5236] ${w.data.userName} \u83B7\u53D6\u4F1A\u8BDD "${a.title}" \u7684\u63A7\u5236\u6743`),io.emit("control-changed",{sessionId:a.id,holder:w.id,holderName:w.data.userName})}})),w.on("release-control",(({sessionId:o}={})=>{const a=o?manager.get(o):c;!a||!a.isController(w.id)||(console.log(`[\u63A7\u5236] ${w.data.userName} \u91CA\u653E\u4F1A\u8BDD "${a.title}" \u7684\u63A7\u5236\u6743`),a.releaseControl(),io.emit("control-changed",{sessionId:a.id,holder:null,holderName:null}))})),w.on("rename",(({sessionId:o,title:a}={})=>{const t=o?manager.get(o):c;!t||!a||(t.title=a,io.emit("session-renamed",{sessionId:t.id,title:a}),broadcastSessions(),manager.persist())})),w.on("resize",(({cols:o,rows:a})=>c?.resize(o,a))),w.on("list",(()=>w.emit("sessions",manager.list(w.data.userName,w.data.role==="admin")))),w.on("dirs",((o,a)=>{if(typeof a!="function")return;const t=getEffectiveRoot(w.data.dir),s=process.platform==="win32"?"\\":"/";let i=(o||t).replace(/[/\\]+$/,"")||t;process.platform==="win32"&&/^[A-Za-z]:$/.test(i)&&(i+="\\");const r=(0,external_path_.resolve)((0,external_path_.normalize)(i));r!==t&&!r.startsWith(t+external_path_.sep)&&(i=t);try{const n=(0,external_fs_.readdirSync)(i,{withFileTypes:!0}).filter((h=>h.isDirectory()&&!h.name.startsWith("."))).map((h=>({name:h.name,path:i.replace(/[/\\]+$/,"")+s+h.name}))).sort(((h,b)=>h.name.localeCompare(b.name))),e=(0,external_path_.join)(i,".."),l=(0,external_path_.resolve)((0,external_path_.normalize)(e)),p=l===t||l.startsWith(t+external_path_.sep);a({ok:!0,current:i,parent:p&&e!==i?e:null,dirs:n})}catch(n){a({ok:!1,error:n.message})}})),w.on("create",((o,a)=>{if(!hasPermission(w.data.role,"operator")){typeof a=="function"&&a({ok:!1,error:"\u6CA1\u6709\u521B\u5EFA\u4F1A\u8BDD\u7684\u6743\u9650"});return}{const s=getEffectiveRoot(w.data.dir),i=o?.cwd?(0,external_path_.resolve)((0,external_path_.normalize)(o.cwd)):null;if(i&&i!==s&&!i.startsWith(s+external_path_.sep)){typeof a=="function"&&a({ok:!1,error:"\u4E0D\u80FD\u5728\u5141\u8BB8\u76EE\u5F55\u4E4B\u5916\u521B\u5EFA\u4F1A\u8BDD"});return}i||(o={...o,cwd:s})}const t=o.cwd;if(!(0,external_fs_.existsSync)(t))try{(0,external_fs_.mkdirSync)(t,{recursive:!0}),console.log(`[\u521B\u5EFA] \u81EA\u52A8\u521B\u5EFA\u76EE\u5F55: ${t}`)}catch(s){typeof a=="function"&&a({ok:!1,error:`\u521B\u5EFA\u76EE\u5F55\u5931\u8D25: ${s.message}`});return}try{const s=manager.create({...o,owner:w.data.userName,userDir:w.data.dir});console.log(`[\u4F1A\u8BDD] ${w.data.userName} \u521B\u5EFA PTY \u4F1A\u8BDD "${s.title}" (${s.id})`),broadcastSessions(),typeof a=="function"&&a({ok:!0,session:s.toJSON()})}catch(s){console.error("[\u521B\u5EFA] PTY \u542F\u52A8\u5931\u8D25:",s.message),typeof a=="function"&&a({ok:!1,error:s.message})}})),w.on("mkdir",((o,a)=>{if(typeof a!="function")return;if(!hasPermission(w.data.role,"operator"))return a({ok:!1,error:"\u6CA1\u6709\u521B\u5EFA\u6587\u4EF6\u5939\u7684\u6743\u9650"});const t=getEffectiveRoot(w.data.dir),s=(0,external_path_.resolve)((0,external_path_.normalize)(o));if(!s.startsWith(t+external_path_.sep)&&s!==t)return a({ok:!1,error:"\u4E0D\u80FD\u5728\u5141\u8BB8\u76EE\u5F55\u4E4B\u5916\u521B\u5EFA\u6587\u4EF6\u5939"});try{(0,external_fs_.mkdirSync)(s,{recursive:!0}),a({ok:!0})}catch(i){a({ok:!1,error:i.message})}})),w.on("kill",(o=>{if(!hasPermission(w.data.role,"operator")){w.emit("error",{message:"\u6CA1\u6709\u5220\u9664\u4F1A\u8BDD\u7684\u6743\u9650"});return}const a=manager.get(o);if(a&&w.data.role!=="admin"&&a.owner&&a.owner!==w.data.userName){w.emit("error",{message:"\u6CA1\u6709\u5220\u9664\u8BE5\u4F1A\u8BDD\u7684\u6743\u9650"});return}console.log(`[\u4F1A\u8BDD] ${w.data.userName} \u5220\u9664\u4F1A\u8BDD ${o}`),manager.kill(o),broadcastSessions()})),w.on("kick-user",(({socketId:o,sessionId:a}={},t)=>{if(!hasPermission(w.data.role,"admin")){typeof t=="function"&&t({ok:!1,error:"\u6CA1\u6709\u8E22\u51FA\u7528\u6237\u7684\u6743\u9650"});return}const s=io.sockets.sockets.get(o);if(!s){typeof t=="function"&&t({ok:!1,error:"\u76EE\u6807\u7528\u6237\u4E0D\u5728\u7EBF"});return}const i=s.data.userName||"\u672A\u77E5";console.log(`[\u8E22\u51FA] ${w.data.userName} \u8E22\u51FA\u4E86\u7528\u6237 ${i} (${o})`),s.emit("kicked",{reason:`\u4F60\u5DF2\u88AB\u7BA1\u7406\u5458 ${w.data.userName} \u8E22\u51FA`}),s.disconnect(!0),typeof t=="function"&&t({ok:!0,name:i})})),w.on("list-viewers",(({sessionId:o}={},a)=>{if(typeof a!="function")return;const t=o?manager.get(o):c;if(!t){a({ok:!1,error:"\u4F1A\u8BDD\u4E0D\u5B58\u5728"});return}const s=[];for(const i of t._viewers){const r=io.sockets.sockets.get(i);r&&s.push({socketId:i,userName:r.data.userName||"\u672A\u77E5",role:r.data.role||"viewer",isController:t.controlHolder===i})}a({ok:!0,viewers:s})}))}));const CONTROL_TIMEOUT=300*1e3;setInterval((()=>{for(const w of manager.listSessions()){w.controlHolder&&(!io.sockets.sockets.get(w.controlHolder)||w.lastInputTime&&Date.now()-w.lastInputTime>CONTROL_TIMEOUT)&&(w.releaseControl(),io.emit("control-changed",{sessionId:w.id,holder:null,holderName:null}));for(const d of w._viewers)io.sockets.sockets.has(d)||w.removeViewer(d)}}),60*1e3);function showStartupInfo(){const w=httpServer.address().port;PORT=w,writePidInfo({port:w});const d=getTailscaleIP(),c=`http://${d}:${w}/admin`;console.log(`
|
|
393
|
+
`,{mode:384})}catch{}console.log(`[\u7BA1\u7406] \u6DFB\u52A0\u7528\u6237 ${f} (\u76EE\u5F55: ${v})`),d.json({ok:!0})})),app.delete("/api/admin/user",checkAdminAuth,express.json(),((w,d)=>{const{name:c}=w.body||{};if(!c)return d.json({ok:!1,error:"\u8BF7\u6307\u5B9A\u7528\u6237\u540D\u79F0"});if(loadUsers(),!listUsers().find((f=>f.name===c)))return d.json({ok:!1,error:"\u7528\u6237\u4E0D\u5B58\u5728"});try{const f=(0,external_path_.join)(configDir,"users.json"),v=JSON.parse((0,external_fs_.readFileSync)(f,"utf8"));for(const[g,m]of Object.entries(v.users||{}))if(m.name===c){delete v.users[g];break}(0,external_fs_.writeFileSync)(f,JSON.stringify(v,null,2),{mode:384}),loadUsers(),console.log(`[\u7BA1\u7406] \u5220\u9664\u7528\u6237 ${c}`),d.json({ok:!0})}catch(f){d.json({ok:!1,error:f.message})}})),app.post("/api/admin/user/password",checkAdminAuth,express.json(),((w,d)=>{const{name:c,password:f}=w.body||{};if(!c||!f)return d.json({ok:!1,error:"\u540D\u79F0\u548C\u65B0\u5BC6\u7801\u5FC5\u586B"});try{const v=(0,external_path_.join)(configDir,"users.json"),g=JSON.parse((0,external_fs_.readFileSync)(v,"utf8"));let m=null;for(const[u,o]of Object.entries(g.users||{}))if(o.name===c){m=o,delete g.users[u];break}if(!m)return d.json({ok:!1,error:"\u7528\u6237\u4E0D\u5B58\u5728"});g.users[f]=m,(0,external_fs_.writeFileSync)(v,JSON.stringify(g,null,2),{mode:384}),loadUsers(),console.log(`[\u7BA1\u7406] \u4FEE\u6539\u7528\u6237 ${c} \u7684\u5BC6\u7801`),d.json({ok:!0})}catch(v){d.json({ok:!1,error:v.message})}})),app.post("/api/admin/password",checkAdminAuth,express.json(),((w,d)=>{const{password:c}=w.body||{};if(!c||c.length<4)return d.json({ok:!1,error:"\u5BC6\u7801\u81F3\u5C114\u4F4D"});try{(0,external_fs_.writeFileSync)((0,external_path_.join)(configDir,"password"),c,{mode:384}),PASSWORD=c,d.json({ok:!0}),console.log("[\u7BA1\u7406] \u7BA1\u7406\u5BC6\u7801\u5DF2\u4FEE\u6539")}catch(f){d.json({ok:!1,error:f.message})}})),app.post("/upload",checkAuth,((w,d)=>{const c=w.query.sessionId,f=c?manager.get(c):null,v=f?.cwd?(0,external_path_.join)(f.cwd,"upload"):uploadDir;(0,external_fs_.mkdirSync)(v,{recursive:!0}),multer({storage:multer.diskStorage({destination:v,filename:(g,m,u)=>{const o=m.originalname.match(/\.[^.]+$/)?.[0]||".jpg";u(null,`${Date.now()}-${(0,external_crypto_.randomBytes)(3).toString("hex")}${o}`)}}),limits:{fileSize:20*1024*1024},fileFilter:(g,m,u)=>u(null,m.mimetype.startsWith("image/"))}).single("image")(w,d,(g=>{if(g)return d.status(400).json({error:g.message});if(!w.file)return d.status(400).json({error:"\u6CA1\u6709\u56FE\u7247"});d.json({path:(0,external_path_.resolve)(w.file.path).replace(/\\/g,"/")})}))}));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(w){return MIME_MAP[(0,external_path_.extname)(w).toLowerCase()]||"application/octet-stream"}function sanitizeError(w){return(w||"\u64CD\u4F5C\u5931\u8D25").replace(/[<>"'&]/g,(d=>({"<":"<",">":">",'"':""","'":"'","&":"&"})[d]))}function resolveUserFilePath(w,d){const c=getUserInfo(w.headers.cookie),f=w.query.sessionId||w.body?.sessionId,v=f?manager.get(f):null,g=v?.cwd?(0,external_path_.resolve)((0,external_path_.normalize)(v.cwd)):null,m=getEffectiveRoot(c?.dir),u=g&&g.startsWith(m)?g:m;let o=d?(0,external_path_.resolve)((0,external_path_.normalize)(u),(0,external_path_.normalize)(d)):u;if(process.platform==="win32"&&/^[A-Za-z]:$/.test(o)&&(o+="\\"),!o.startsWith(u))return{error:"\u8BBF\u95EE\u88AB\u62D2\u7EDD\uFF1A\u8DEF\u5F84\u8D85\u51FA\u5141\u8BB8\u8303\u56F4",status:403};try{if(!(0,external_fs_.realpathSync)(o).startsWith(u))return{error:"\u8BBF\u95EE\u88AB\u62D2\u7EDD\uFF1A\u7B26\u53F7\u94FE\u63A5\u6307\u5411\u5141\u8BB8\u8303\u56F4\u5916",status:403}}catch{}return{resolved:o,userRoot:u,info:c}}app.get("/api/files/list",checkAuth,((w,d)=>{const c=resolveUserFilePath(w,w.query.path);if(c.error)return d.status(c.status).json({error:c.error});try{let g=function(s,i){const r=(0,external_fs_.readdirSync)(s,{withFileTypes:!0}).filter((e=>f||!e.name.startsWith("."))),n=[];for(const e of r){const l=(0,external_path_.join)(s,e.name),p=i?i+"/"+e.name:e.name;let h=0,b=null;try{const _=(0,external_fs_.statSync)(l);h=_.size,b=_.mtime}catch{}if(e.isDirectory())if(v)try{n.push(...g(l,p))}catch{}else n.push({name:p,type:"dir",size:h,mtime:b});else n.push({name:p,type:"file",size:h,mtime:b})}return n};if(!(0,external_fs_.statSync)(c.resolved).isDirectory())return d.status(400).json({error:"\u4E0D\u662F\u76EE\u5F55"});const f=w.query.showHidden==="1",v=w.query.recursive==="1",m=g(c.resolved,"").sort(((s,i)=>s.type===i.type?s.name.localeCompare(i.name):s.type==="dir"?-1:1)),u=(0,external_path_.join)(c.resolved,".."),o=(0,external_path_.resolve)((0,external_path_.normalize)(u)),a=o.startsWith(c.userRoot)&&o!==c.resolved,t=c.resolved.startsWith(c.userRoot)?c.resolved.slice(c.userRoot.length).replace(/^[/\\]+/,""):"";d.json({current:c.resolved.replace(/\\/g,"/"),currentRel:t.replace(/\\/g,"/"),parent:a?o.replace(/\\/g,"/"):null,root:c.userRoot.replace(/\\/g,"/"),entries:m})}catch(f){d.status(400).json({error:sanitizeError(f.message)})}})),app.post("/api/files/upload",checkAuth,((w,d)=>{const c=resolveUserFilePath(w,w.query.path);if(c.error)return d.status(c.status).json({error:c.error});(0,external_fs_.mkdirSync)(c.resolved,{recursive:!0}),multer({storage:multer.diskStorage({destination:(f,v,g)=>{const m=v.originalname||"",u=m.lastIndexOf("/"),o=u>0?m.slice(0,u):"",a=o?(0,external_path_.join)(c.resolved,o):c.resolved;if(!(0,external_path_.resolve)(a).startsWith(c.resolved))return g(new Error("\u8DEF\u5F84\u4E0D\u5408\u6CD5"));(0,external_fs_.mkdirSync)(a,{recursive:!0}),g(null,a)},filename:(f,v,g)=>{const m=v.originalname||"",u=m.lastIndexOf("/"),o=u>0?m.slice(u+1):(0,external_path_.basename)(m);g(null,o||`${Date.now()}.bin`)}}),limits:{fileSize:500*1024*1024},defParamCharset:"utf-8"}).array("files",100)(w,d,(f=>{if(f)return d.status(400).json({error:f.message});if(!w.files?.length)return d.status(400).json({error:"\u6CA1\u6709\u6587\u4EF6"});const v={ok:!0,files:w.files.map((m=>({name:m.filename,path:(0,external_path_.resolve)(m.path).replace(/\\/g,"/"),size:m.size})))};d.json(v);const g=w.query.sessionId;g&&io.to(g).emit("files:changed",{sessionId:g})}))})),app.get("/api/files/download",checkAuth,((w,d)=>{if(!w.query.path)return d.status(400).json({error:"\u7F3A\u5C11 path \u53C2\u6570"});const c=resolveUserFilePath(w,w.query.path);if(c.error)return d.status(c.status).json({error:c.error});try{const f=(0,external_fs_.statSync)(c.resolved);if(!f.isFile())return d.status(400).json({error:"\u4E0D\u662F\u6587\u4EF6"});const v=(0,external_path_.basename)(c.resolved);d.setHeader("Content-Type",getMime(v)),d.setHeader("Content-Length",f.size),d.setHeader("Content-Disposition",`attachment; filename*=UTF-8''${encodeURIComponent(v)}`),(0,external_fs_.createReadStream)(c.resolved).pipe(d)}catch(f){d.status(404).json({error:sanitizeError(f.message)})}})),app.get("/api/files/preview",checkAuth,((w,d)=>{if(!w.query.path)return d.status(400).json({error:"\u7F3A\u5C11 path \u53C2\u6570"});const c=resolveUserFilePath(w,w.query.path);if(c.error)return d.status(c.status).json({error:c.error});try{const f=(0,external_fs_.statSync)(c.resolved);if(!f.isFile())return d.status(400).json({error:"\u4E0D\u662F\u6587\u4EF6"});const v=(0,external_path_.basename)(c.resolved),g=getMime(v);d.setHeader("Content-Type",g),d.setHeader("Content-Length",f.size),d.setHeader("Content-Disposition",`inline; filename*=UTF-8''${encodeURIComponent(v)}`),(0,external_fs_.createReadStream)(c.resolved).pipe(d)}catch(f){d.status(404).json({error:sanitizeError(f.message)})}})),app.post("/api/files/mkdir",checkAuth,express.json(),((w,d)=>{const{path:c}=w.body||{};if(!c)return d.status(400).json({error:"\u7F3A\u5C11 path"});const f=resolveUserFilePath(w,c);if(f.error)return d.status(f.status).json({error:f.error});try{(0,external_fs_.mkdirSync)(f.resolved,{recursive:!0}),d.json({ok:!0,path:f.resolved.replace(/\\/g,"/")})}catch(v){d.status(400).json({error:sanitizeError(v.message)})}})),app.delete("/api/files/delete",checkAuth,express.json(),((w,d)=>{const c=getUserInfo(w.headers.cookie),f=w.body?.sessionId||w.query.sessionId,v=f?manager.get(f):null;if(!(v&&c&&(c.role==="admin"||v.owner===c.name))&&(!c||c.role!=="admin"))return d.status(403).json({error:"\u6CA1\u6709\u5220\u9664\u6743\u9650"});const{path:m}=w.body||{};if(!m)return d.status(400).json({error:"\u7F3A\u5C11 path"});const u=resolveUserFilePath(w,m);if(u.error)return d.status(u.status).json({error:u.error});if(u.resolved===u.userRoot)return d.status(403).json({error:"\u4E0D\u5141\u8BB8\u5220\u9664\u6839\u76EE\u5F55"});try{(0,external_fs_.statSync)(u.resolved).isDirectory()?(0,external_fs_.rmSync)(u.resolved,{recursive:!1}):(0,external_fs_.unlinkSync)(u.resolved),d.json({ok:!0});const o=w.body?.sessionId||w.query.sessionId;o&&io.to(o).emit("files:changed",{sessionId:o})}catch(o){d.status(400).json({error:sanitizeError(o.message)})}})),app.get("/qr/:sessionId",checkAuth,(async(w,d)=>{const c=getTailscaleIP(),f=(0,external_crypto_.randomBytes)(16).toString("hex");userSessions.set(f,{role:"operator",name:"\u626B\u7801\u7528\u6237",onetime:!0,lastUsed:Date.now()});const v=`http://${c}:${PORT}/terminal/${w.params.sessionId}?token=${f}`;try{const g=await lib.toString(v,{type:"svg"});d.setHeader("Content-Type","image/svg+xml"),d.send(g)}catch(g){d.status(500).send(g.message)}})),io.use(((w,d)=>{const c=w.handshake.headers.cookie;if(hasValidSession(c)){const v=getUserInfo(c);return w.data.role=v?.role||"admin",w.data.userName=v?.name||"\u7528\u6237",w.data.dir=v?.dir||null,d()}const f=w.handshake.auth?.password;if(f){loadUsers();const v=lookupUser(f);if(v)return w.data.role="operator",w.data.userName=v.name,w.data.dir=v.dir,d()}if((w.handshake.auth?.token||w.handshake.query?.token)===TOKEN)return w.data.role="admin",w.data.userName="\u7BA1\u7406\u5458",d();d(new Error("\u672A\u6388\u6743"))}));const EXCLUSIVE_RELEASE_DELAY=120*1e3;let _exclusiveReleaseTimer=null;function checkExclusiveRelease(){if(!isExclusiveMode()||!activeUser)return;let w=!1;for(const[,d]of io.sockets.sockets)if(d.data.userName===activeUser.name){w=!0;break}if(w){_exclusiveReleaseTimer&&(clearTimeout(_exclusiveReleaseTimer),_exclusiveReleaseTimer=null);return}_exclusiveReleaseTimer||(_exclusiveReleaseTimer=setTimeout((()=>{if(_exclusiveReleaseTimer=null,!!activeUser){for(const[,d]of io.sockets.sockets)if(d.data.userName===activeUser.name)return;console.log(`[\u72EC\u5360] ${activeUser.name} \u5DF2\u79BB\u7EBF\u8D85\u65F6\uFF0C\u91CA\u653E\u767B\u5F55\u72B6\u6001\uFF08\u4F1A\u8BDD\u4FDD\u7559\uFF09`);for(const d of activeUser.tokens)userSessions.delete(d);activeUser=null}}),EXCLUSIVE_RELEASE_DELAY))}let _broadcastTimer=null;function broadcastSessions(){_broadcastTimer||(_broadcastTimer=setTimeout((()=>{_broadcastTimer=null;for(const[,w]of io.sockets.sockets)w.emit("sessions",manager.list(w.data.userName,w.data.role==="admin"))}),100))}io.on("connection",(w=>{const d=w.handshake.address?.replace("::ffff:","")||"?";console.log(`[\u8FDE\u63A5] ${w.data.userName}(${w.data.role}) \u4ECE ${d} \u63A5\u5165 id=${w.id}`),_exclusiveReleaseTimer&&activeUser&&w.data.userName===activeUser.name&&(clearTimeout(_exclusiveReleaseTimer),_exclusiveReleaseTimer=null);let c=null,f=null,v=null,g=null,m=null;function u(){c&&(c.isController(w.id)&&(c.releaseControl(),io.emit("control-changed",{sessionId:c.id,holder:null,holderName:null})),c.removeViewer(w.id),f&&c.off("data",f),v&&c.off("agent:message",v),g&&c.off("agent:busy",g),m&&c.off("agent:error",m),f=null,v=null,g=null,m=null)}w.on("join",(o=>{const a=manager.get(o);if(!a){w.emit("error",{message:`\u4F1A\u8BDD ${o} \u672A\u627E\u5230`});return}if(u(),c=a,c.addViewer(w.id,w.data.userName),a.mode==="agent"?(v=t=>w.emit("agent:message",t),c.on("agent:message",v),g=t=>w.emit("agent:busy",t),c.on("agent:busy",g),m=t=>w.emit("agent:error",t),c.on("agent:error",m)):(f=t=>w.emit("output",t),c.on("data",f),c.once("exit",(t=>{w.emit("session-exit",{code:t}),f&&c?.off("data",f)})),spawnLocalTerminal(o,a.title)),console.log(`[\u52A0\u5165] ${w.data.userName} \u52A0\u5165\u4F1A\u8BDD "${a.title}" (${o}) \u6A21\u5F0F=${a.mode||"pty"}`),w.emit("joined",{...a.toJSON(),role:w.data.role}),a.mode==="agent")a._history?.length&&w.emit("agent:history",a._history);else if(a._scrollback?.length){const t=a._scrollback;if(t.length<=4096)w.emit("output",t);else{let s=0;(function i(){s>=t.length||(w.emit("output",t.slice(s,s+4096)),s+=4096,setImmediate(i))})()}}broadcastSessions()})),w.on("disconnect",(()=>{console.log(`[\u65AD\u5F00] ${w.data.userName} \u79BB\u5F00\u4F1A\u8BDD "${c?.title||"?"}" id=${w.id}`),u(),broadcastSessions(),checkExclusiveRelease(),manager.persist()})),w.on("agent:query",(async({prompt:o}={})=>{if(!c||c.mode!=="agent"){w.emit("agent:error",{message:"\u5F53\u524D\u4E0D\u662F Agent \u6A21\u5F0F\u4F1A\u8BDD"});return}if(!c.isController(w.id)){w.emit("control-denied",{reason:"\u4F60\u6CA1\u6709\u5F53\u524D\u4F1A\u8BDD\u7684\u63A7\u5236\u6743"});return}if(c.isBusy){w.emit("agent:error",{message:"\u6B63\u5728\u5904\u7406\u4E2D\uFF0C\u8BF7\u7B49\u5F85\u5B8C\u6210"});return}try{await c.query(o)}catch(a){console.error("[agent:query] \u9519\u8BEF:",a.message);try{w.connected&&w.emit("agent:error",{message:a.message})}catch{}}})),w.on("agent:interrupt",(()=>{if(!(!c||c.mode!=="agent")){if(!c.isController(w.id)){w.emit("control-denied",{reason:"\u4F60\u6CA1\u6709\u5F53\u524D\u4F1A\u8BDD\u7684\u63A7\u5236\u6743"});return}c.interrupt()}})),w.on("agent:clear",(()=>{if(!(!c||c.mode!=="agent")){if(!c.isController(w.id)){w.emit("control-denied",{reason:"\u4F60\u6CA1\u6709\u5F53\u524D\u4F1A\u8BDD\u7684\u63A7\u5236\u6743"});return}typeof c.clear=="function"&&(c.clear(),console.log(`[\u6E05\u9664] ${w.data.userName} \u6E05\u9664\u4F1A\u8BDD "${c.title}" \u7684\u5BF9\u8BDD\u4E0A\u4E0B\u6587`))}})),w.on("agent:permission",(({requestId:o,allow:a,toolName:t,approveAll:s}={})=>{if(!c||c.mode!=="agent")return;const i=c.owner&&c.owner===w.data.userName,r=w.data.role==="admin",n=c.isController(w.id);if(!i&&!r&&!n){w.emit("control-denied",{reason:"\u53EA\u6709\u4F1A\u8BDD\u6240\u6709\u8005\u53EF\u4EE5\u5BA1\u6279\u6743\u9650"});return}c.respondPermission(o,a,t,s)})),w.on("set-mode",(({sessionId:o,mode:a}={})=>{const t=o?manager.get(o):c;if(t){if(w.data.role!=="admin"&&t.owner&&t.owner!==w.data.userName){w.emit("error",{message:"\u6CA1\u6709\u66F4\u6539\u6A21\u5F0F\u7684\u6743\u9650"});return}t.mode==="agent"&&typeof t.setPermissionMode=="function"&&(t.setPermissionMode(a),console.log(`[\u6A21\u5F0F] ${w.data.userName} \u66F4\u6539\u4F1A\u8BDD "${t.title}" \u4E3A ${a}`),io.emit("mode-changed",{sessionId:t.id,mode:a}),manager.persist())}})),w.on("create-agent",((o,a)=>{if(!hasPermission(w.data.role,"operator")){typeof a=="function"&&a({ok:!1,error:"\u6CA1\u6709\u521B\u5EFA\u4F1A\u8BDD\u7684\u6743\u9650"});return}{const i=getEffectiveRoot(w.data.dir);let r=null;if(o?.cwd&&(r=(0,external_path_.resolve)((0,external_path_.normalize)(i),(0,external_path_.normalize)(o.cwd))),r&&r!==i&&!r.startsWith(i+external_path_.sep)){typeof a=="function"&&a({ok:!1,error:"\u4E0D\u80FD\u5728\u5141\u8BB8\u76EE\u5F55\u4E4B\u5916\u521B\u5EFA\u4F1A\u8BDD"});return}o={...o,cwd:r||i}}const t=o.cwd,s=(0,external_path_.join)(t,".zihi-sessions",`${Date.now()}-${(0,external_crypto_.randomUUID)().slice(0,8)}`);try{(0,external_fs_.mkdirSync)(s,{recursive:!0})}catch(i){typeof a=="function"&&a({ok:!1,error:`\u521B\u5EFA\u9694\u79BB\u76EE\u5F55\u5931\u8D25: ${i.message}`});return}o={...o,cwd:s};try{const i=manager.createAgent({...o,owner:w.data.userName,userDir:w.data.dir});console.log(`[\u4F1A\u8BDD] ${w.data.userName} \u521B\u5EFA Agent \u4F1A\u8BDD "${i.title}" (${i.id})`),broadcastSessions(),typeof a=="function"&&a({ok:!0,session:i.toJSON()})}catch(i){typeof a=="function"&&a({ok:!1,error:i.message})}})),w.on("input",(o=>{if(!(o==null||!c)&&c.mode!=="agent"){if(!c.isController(w.id)){w.emit("control-denied",{reason:"\u4F60\u6CA1\u6709\u5F53\u524D\u4F1A\u8BDD\u7684\u63A7\u5236\u6743"});return}c.lastInputTime=Date.now(),c.write(o)}})),w.on("take-control",(({sessionId:o}={})=>{const a=o?manager.get(o):c;if(a){if(!hasPermission(w.data.role,"operator")){w.emit("control-denied",{reason:"\u4F60\u7684\u89D2\u8272\u6CA1\u6709\u63A7\u5236\u6743\u9650"});return}if(a.controlHolder&&a.controlHolder!==w.id){const t=a.controlHolderName===w.data.userName;if(w.data.role!=="admin"&&!t){w.emit("control-denied",{reason:"\u5176\u4ED6\u7528\u6237\u6B63\u5728\u63A7\u5236\u4E2D\uFF0C\u8BF7\u7B49\u5F85\u91CA\u653E"});return}}a.takeControl(w.id,w.data.userName),console.log(`[\u63A7\u5236] ${w.data.userName} \u83B7\u53D6\u4F1A\u8BDD "${a.title}" \u7684\u63A7\u5236\u6743`),io.emit("control-changed",{sessionId:a.id,holder:w.id,holderName:w.data.userName})}})),w.on("release-control",(({sessionId:o}={})=>{const a=o?manager.get(o):c;!a||!a.isController(w.id)||(console.log(`[\u63A7\u5236] ${w.data.userName} \u91CA\u653E\u4F1A\u8BDD "${a.title}" \u7684\u63A7\u5236\u6743`),a.releaseControl(),io.emit("control-changed",{sessionId:a.id,holder:null,holderName:null}))})),w.on("rename",(({sessionId:o,title:a}={})=>{const t=o?manager.get(o):c;!t||!a||(t.title=a,io.emit("session-renamed",{sessionId:t.id,title:a}),broadcastSessions(),manager.persist())})),w.on("resize",(({cols:o,rows:a})=>c?.resize(o,a))),w.on("list",(()=>w.emit("sessions",manager.list(w.data.userName,w.data.role==="admin")))),w.on("dirs",((o,a)=>{if(typeof a!="function")return;const t=getEffectiveRoot(w.data.dir),s=process.platform==="win32"?"\\":"/";let i=(o||t).replace(/[/\\]+$/,"")||t;process.platform==="win32"&&/^[A-Za-z]:$/.test(i)&&(i+="\\");const r=(0,external_path_.resolve)((0,external_path_.normalize)(i));r!==t&&!r.startsWith(t+external_path_.sep)&&(i=t);try{const n=(0,external_fs_.readdirSync)(i,{withFileTypes:!0}).filter((h=>h.isDirectory()&&!h.name.startsWith("."))).map((h=>({name:h.name,path:i.replace(/[/\\]+$/,"")+s+h.name}))).sort(((h,b)=>h.name.localeCompare(b.name))),e=(0,external_path_.join)(i,".."),l=(0,external_path_.resolve)((0,external_path_.normalize)(e)),p=l===t||l.startsWith(t+external_path_.sep);a({ok:!0,current:i,parent:p&&e!==i?e:null,dirs:n})}catch(n){a({ok:!1,error:n.message})}})),w.on("create",((o,a)=>{if(!hasPermission(w.data.role,"operator")){typeof a=="function"&&a({ok:!1,error:"\u6CA1\u6709\u521B\u5EFA\u4F1A\u8BDD\u7684\u6743\u9650"});return}{const s=getEffectiveRoot(w.data.dir),i=o?.cwd?(0,external_path_.resolve)((0,external_path_.normalize)(o.cwd)):null;if(i&&i!==s&&!i.startsWith(s+external_path_.sep)){typeof a=="function"&&a({ok:!1,error:"\u4E0D\u80FD\u5728\u5141\u8BB8\u76EE\u5F55\u4E4B\u5916\u521B\u5EFA\u4F1A\u8BDD"});return}i||(o={...o,cwd:s})}const t=o.cwd;if(!(0,external_fs_.existsSync)(t))try{(0,external_fs_.mkdirSync)(t,{recursive:!0}),console.log(`[\u521B\u5EFA] \u81EA\u52A8\u521B\u5EFA\u76EE\u5F55: ${t}`)}catch(s){typeof a=="function"&&a({ok:!1,error:`\u521B\u5EFA\u76EE\u5F55\u5931\u8D25: ${s.message}`});return}try{const s=manager.create({...o,owner:w.data.userName,userDir:w.data.dir});console.log(`[\u4F1A\u8BDD] ${w.data.userName} \u521B\u5EFA PTY \u4F1A\u8BDD "${s.title}" (${s.id})`),broadcastSessions(),typeof a=="function"&&a({ok:!0,session:s.toJSON()})}catch(s){console.error("[\u521B\u5EFA] PTY \u542F\u52A8\u5931\u8D25:",s.message),typeof a=="function"&&a({ok:!1,error:s.message})}})),w.on("mkdir",((o,a)=>{if(typeof a!="function")return;if(!hasPermission(w.data.role,"operator"))return a({ok:!1,error:"\u6CA1\u6709\u521B\u5EFA\u6587\u4EF6\u5939\u7684\u6743\u9650"});const t=getEffectiveRoot(w.data.dir),s=(0,external_path_.resolve)((0,external_path_.normalize)(o));if(!s.startsWith(t+external_path_.sep)&&s!==t)return a({ok:!1,error:"\u4E0D\u80FD\u5728\u5141\u8BB8\u76EE\u5F55\u4E4B\u5916\u521B\u5EFA\u6587\u4EF6\u5939"});try{(0,external_fs_.mkdirSync)(s,{recursive:!0}),a({ok:!0})}catch(i){a({ok:!1,error:i.message})}})),w.on("kill",(o=>{if(!hasPermission(w.data.role,"operator")){w.emit("error",{message:"\u6CA1\u6709\u5220\u9664\u4F1A\u8BDD\u7684\u6743\u9650"});return}const a=manager.get(o);if(a&&w.data.role!=="admin"&&a.owner&&a.owner!==w.data.userName){w.emit("error",{message:"\u6CA1\u6709\u5220\u9664\u8BE5\u4F1A\u8BDD\u7684\u6743\u9650"});return}console.log(`[\u4F1A\u8BDD] ${w.data.userName} \u5220\u9664\u4F1A\u8BDD ${o}`),manager.kill(o),broadcastSessions()})),w.on("kick-user",(({socketId:o,sessionId:a}={},t)=>{if(!hasPermission(w.data.role,"admin")){typeof t=="function"&&t({ok:!1,error:"\u6CA1\u6709\u8E22\u51FA\u7528\u6237\u7684\u6743\u9650"});return}const s=io.sockets.sockets.get(o);if(!s){typeof t=="function"&&t({ok:!1,error:"\u76EE\u6807\u7528\u6237\u4E0D\u5728\u7EBF"});return}const i=s.data.userName||"\u672A\u77E5";console.log(`[\u8E22\u51FA] ${w.data.userName} \u8E22\u51FA\u4E86\u7528\u6237 ${i} (${o})`),s.emit("kicked",{reason:`\u4F60\u5DF2\u88AB\u7BA1\u7406\u5458 ${w.data.userName} \u8E22\u51FA`}),s.disconnect(!0),typeof t=="function"&&t({ok:!0,name:i})})),w.on("list-viewers",(({sessionId:o}={},a)=>{if(typeof a!="function")return;const t=o?manager.get(o):c;if(!t){a({ok:!1,error:"\u4F1A\u8BDD\u4E0D\u5B58\u5728"});return}const s=[];for(const i of t._viewers){const r=io.sockets.sockets.get(i);r&&s.push({socketId:i,userName:r.data.userName||"\u672A\u77E5",role:r.data.role||"viewer",isController:t.controlHolder===i})}a({ok:!0,viewers:s})}))}));const CONTROL_TIMEOUT=300*1e3;setInterval((()=>{for(const w of manager.listSessions()){w.controlHolder&&(!io.sockets.sockets.get(w.controlHolder)||w.lastInputTime&&Date.now()-w.lastInputTime>CONTROL_TIMEOUT)&&(w.releaseControl(),io.emit("control-changed",{sessionId:w.id,holder:null,holderName:null}));for(const d of w._viewers)io.sockets.sockets.has(d)||w.removeViewer(d)}}),60*1e3);function showStartupInfo(){const w=httpServer.address().port;PORT=w,writePidInfo({port:w});const d=getTailscaleIP(),c=`http://${d}:${w}/admin`;console.log(`
|
|
394
394
|
\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(`
|
|
395
395
|
\u5730\u5740: http://${d}:${w}`),console.log(` \u5BC6\u7801: ${PASSWORD} (\u7F16\u8F91 ~/.zihi/password \u53EF\u4FEE\u6539)`),console.log(`
|
|
396
396
|
\u626B\u63CF\u4E8C\u7EF4\u7801\u5728\u624B\u673A\u4E0A\u6253\u5F00:
|
package/dist/package.json
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"name":"@wendongfly/zihi","version":"1.1.
|
|
1
|
+
{"name":"@wendongfly/zihi","version":"1.1.20","description":"AI Agent terminal sharing with interactive permission approval via Claude Agent SDK","type":"module","main":"dist/index.js","bin":{"zihi":"bin/zihi.js"},"files":["bin/","dist/"],"scripts":{"start":"node dist/index.js","dev":"node --watch src/server.js","build":"node scripts/build.js"},"keywords":["ai","agent","claude","terminal","sharing","permission","web"],"author":"wendongfly","license":"MIT","dependencies":{"@anthropic-ai/claude-agent-sdk":"latest","@xterm/addon-fit":"^0.11.0","@xterm/addon-web-links":"^0.12.0","@xterm/xterm":"^6.0.0","express":"^5.2.1","multer":"^2.1.1","qrcode":"^1.5.4","qrcode-terminal":"^0.12.0","socket.io":"^4.8.3","socket.io-client":"^4.8.3"},"devDependencies":{"@vercel/ncc":"^0.38.4","esbuild":"^0.28.0","playwright":"^1.52.0"}}
|