@wendongfly/myhi 1.3.188 → 1.3.190
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/bin/daemon.js +196 -170
- package/bin/install-check.js +25 -0
- package/dist/admin.html +30 -7
- package/dist/chat.html +82 -33
- package/dist/index.html +37 -6
- package/dist/index.js +1 -1
- package/dist/index.min.js +10 -15
- package/dist/lib/machine.js +8 -1
- package/dist/terminal.html +1 -1
- package/package.json +1 -1
package/dist/index.min.js
CHANGED
|
@@ -381,21 +381,21 @@ Content-Length: `+C+`\r
|
|
|
381
381
|
`).map((t=>t.trim())).filter(Boolean),a=i.filter((t=>!u.some((n=>n.test(t)))));i.length>a.length&&console.log(`[agent:stderr][benign] \u541E\u6389 ${i.length-a.length} \u6761\u826F\u6027\u63D0\u793A`),a.length&&(console.error("[agent:stderr]",a.join(`
|
|
382
382
|
`)),this.emit("agent:error",{message:a.join(`
|
|
383
383
|
`)}),this._checkDeathLoop({type:"stderr",_raw:a.join(`
|
|
384
|
-
`)}))})),this._proc.on("close",(d=>{if(!this._interrupted&&this._buffer.trim())try{const o=JSON.parse(this._buffer.trim());this._handleMessage(o)}catch{}this._keepAliveTimer&&(clearInterval(this._keepAliveTimer),this._keepAliveTimer=null),this._stopStallWatchdog(),this._proc=null,this._buffer="",console.log(`[agent] \u8FDB\u7A0B\u9000\u51FA (code ${d}), \u4F1A\u8BDD: ${this.claudeSessionId||"?"}`),this._busy&&(this._busy=!1,this._interrupted=!1,this.emit("agent:busy",!1),this._pendingReject&&(this._pendingReject(new Error(`Claude \u8FDB\u7A0B\u9000\u51FA (code ${d})`)),this._pendingResolve=null,this._pendingReject=null)),this._flushPendingSteer(),this.alive&&d!==0&&d!==null&&this.emit("agent:error",{message:`Claude \u8FDB\u7A0B\u5F02\u5E38\u9000\u51FA (code ${d})\uFF0C\u4E0B\u6B21\u67E5\u8BE2\u5C06\u81EA\u52A8\u91CD\u542F`})})),this._proc.on("error",(d=>{this._keepAliveTimer&&(clearInterval(this._keepAliveTimer),this._keepAliveTimer=null),this._stopStallWatchdog(),this._proc=null,this._busy=!1,this.emit("agent:busy",!1),this.emit("agent:error",{message:`\u542F\u52A8 Claude \u5931\u8D25: ${d.message}`}),this._pendingReject&&(this._pendingReject(d),this._pendingResolve=null,this._pendingReject=null)})),this._keepAliveTimer=setInterval((()=>{this._writeStdin({type:"keep_alive"})}),3e4)}async query(p,{internal:l=!1}={}){if(this._busy){this.emit("agent:error",{message:"\u6B63\u5728\u5904\u7406\u4E2D\uFF0C\u8BF7\u7B49\u5F85\u5B8C\u6210"});return}if(l||(this._followCount=0,this._followStopSignal=!1,this._cancelFollowUp()),p==="/clear"){this.reset(),this.emit("agent:message",{type:"system",subtype:"info",message:"\u5DF2\u6E05\u9664 Claude \u4E0A\u4E0B\u6587\uFF0C\u4E0B\u6B21\u63D0\u95EE\u5C06\u5F00\u542F\u5168\u65B0\u5BF9\u8BDD"});return}const r=p.match(/^\/resume\s+(\S+)/);if(r){if(this._proc){try{this._proc.kill("SIGTERM")}catch{}this._proc=null}this.claudeSessionId=r[1],this._history=[],this._buffer="",this._loadResumedHistory(),this.emit("agent:message",{type:"system",subtype:"info",message:`\u5DF2\u5207\u6362\u5230\u4F1A\u8BDD ${r[1].slice(0,8)}...\uFF0C\u4E0B\u6B21\u63D0\u95EE\u5C06\u6062\u590D\u8BE5\u5BF9\u8BDD`});return}const e=p.match(/^\/model\s+(\S+)/);if(e){if(this.modelId=e[1],this._proc){try{this._proc.kill("SIGTERM")}catch{}this._proc=null}this.emit("agent:message",{type:"system",subtype:"info",message:`\u6A21\u578B\u5DF2\u5207\u6362\u4E3A ${e[1]}`});return}if(p==="/plan"||p.startsWith("/plan ")){if(this.permissionMode==="plan"){if(this.permissionMode=normalizePermissionMode(this._prePlanMode),this._prePlanMode=null,this._proc){try{this._proc.kill("SIGTERM")}catch{}this._proc=null}this.emit("agent:message",{type:"system",subtype:"info",message:"\u5DF2\u9000\u51FA\u8BA1\u5212\u6A21\u5F0F"})}else{if(this._prePlanMode=this.permissionMode,this.permissionMode="plan",this._proc){try{this._proc.kill("SIGTERM")}catch{}this._proc=null}this.emit("agent:message",{type:"system",subtype:"info",message:"\u5DF2\u8FDB\u5165\u8BA1\u5212\u6A21\u5F0F\uFF08\u53EA\u89C4\u5212\u4E0D\u6267\u884C\uFF09"});const d=p.slice(5).trim();if(d){setTimeout((()=>this.query(d)),500);return}}return}if(p.startsWith("/rename")){this.emit("agent:message",{type:"system",subtype:"info",message:"\u4F1A\u8BDD\u5DF2\u547D\u540D"});return}this._busy=!0,this._interrupted=!1,this.emit("agent:busy",!0),this._loopSigCounts.clear(),this._loopPoisonHits=0,this._loopTurnToolCalls=0,this._loopTripped=!1,l||(this._loopAutoRecover=0),this._redactor=buildRedactor(),this._pseudoMap||(this._pseudoMap=loadMap(this.claudeSessionId||this.id)),this._pseudo=buildPseudonymizer(this.cwd,this._pseudoMap),this._lastActivityAt=Date.now(),this._awaitingTool=!1,this._startStallWatchdog();const s=redactDeep(p,this._redactor);return this._history.push({type:"user",content:s,timestamp:Date.now()}),!this.firstUserText&&typeof s=="string"&&!s.startsWith("/")&&(this.firstUserText=s.trim().slice(0,160)),this._proc||this._spawnProcess(),new Promise(((u,d)=>{this._pendingResolve=u,this._pendingReject=d,p.startsWith("/")&&(this._slashTimeout=setTimeout((()=>{this._busy&&(this._busy=!1,this._interrupted=!1,this.emit("agent:busy",!1),this.emit("agent:message",{type:"system",subtype:"info",message:`\u547D\u4EE4 ${p} \u5DF2\u53D1\u9001`}),this._pendingResolve&&(this._pendingResolve(0),this._pendingResolve=null,this._pendingReject=null))}),3e4));let o=p;if(this._pseudo?.enabled){const a=this._pseudo.pseudonymize(p);o=a.text;const t=this.claudeSessionId||this.id;saveMap(t,this._pseudoMap),a.count>0&&(appendAudit(t,{ts:Date.now(),level:this._pseudo.level,maskedCount:a.count,byType:a.byType,sentToModel:a.text}),this.emit("agent:message",{type:"system",subtype:"info",message:`\u{1F6E1} \u9690\u79C1\u62A4\u680F\uFF1A\u672C\u8F6E\u5DF2\u8131\u654F ${a.count} \u5904\u654F\u611F\u4FE1\u606F\uFF0C\u6A21\u578B\u53EA\u770B\u5230\u5360\u4F4D\u7B26\uFF08\u6620\u5C04\u8868\u7559\u672C\u673A\u3001\u53EF\u9006\u8FD8\u539F\uFF09\u3002`}))}this._writeStdin({type:"user",message:{role:"user",content:o}})||(this._busy=!1,this.emit("agent:busy",!1),this._pendingResolve=null,this._pendingReject=null,d(new Error("Claude \u8FDB\u7A0B\u4E0D\u53EF\u5199")))}))}_handleMessage(p){const l=!!this.claudeSessionId;if(this._lastActivityAt=Date.now(),p.type==="assistant"&&Array.isArray(p.message?.content)?p.message.content.some((o=>o.type==="tool_use"))&&(this._awaitingTool=!0):p.type==="user"&&(this._awaitingTool=!1),p.session_id&&!this.claudeSessionId&&(this.claudeSessionId=p.session_id),p.type==="system"&&p.subtype==="init"&&p.session_id&&(this.claudeSessionId=p.session_id),!l&&this.claudeSessionId&&this.emit("session-id",this.claudeSessionId),p.type==="system"&&p.subtype==="init"&&this.permissionMode==="bypassPermissions"&&p.permissionMode&&p.permissionMode!=="bypassPermissions"){const o=p.permissionMode;this.reset(),this.emit("agent:message",{type:"system",subtype:"warning",message:`\u26A0\uFE0F \u8FD9\u4E2A\u4F1A\u8BDD\u88AB --resume \u5E26\u56DE\u4E86\u300C${o}\u300D\u6A21\u5F0F\uFF08\u975E bypass\uFF0C\u4F1A\u62E6\u5199/\u5220\u4E14\u65E0\u6CD5\u8FD0\u884C\u65F6\u63D0\u6743\uFF0C\u70B9\u300C\u9000\u51FA\u8BA1\u5212\u300D\u4E5F\u9003\u4E0D\u6389\uFF09\u3002\u5DF2\u81EA\u52A8\u65AD\u5F00\u65E7\u4F1A\u8BDD\u94FE\u3001\u91CD\u5F00\u4E3A\u5E72\u51C0\u7684 bypass \u4F1A\u8BDD\u2014\u2014\u8BF7\u91CD\u53D1\u4F60\u521A\u624D\u7684\u6307\u4EE4\uFF0C\u8FD9\u6B21\u4F1A\u76F4\u63A5\u6267\u884C\u3002`});return}const r=this._pseudo?.enabled?redactDeep(p,this._pseudo.restore):p,e=redactDeep(r,this._redactor),s=stripHeavyPayload({...e,timestamp:Date.now()}),u=JSON.stringify(s);this._followEnabled&&u.includes("[\u8DDF\u8FDB\u5B8C\u6210]")&&(this._followStopSignal=!0),u.length>10240&&(s._truncated=!0),this._history.push(s);const d=500;if(this._history.length>d&&(this._history=this._history.slice(-d)),this._trackImagePressure(u),this._checkDeathLoop(p,u),p.type==="rate_limit_event"&&p.rate_limit_info&&(this._usage.rateLimitInfo=p.rate_limit_info),p.type==="result"){const o=this._usageBase;if(this._procQueryCount=(this._procQueryCount||0)+1,this._usage.queryCount=o.queryCount+this._procQueryCount,p.total_cost_usd&&(this._usage.totalCostUSD=o.totalCostUSD+p.total_cost_usd),p.usage&&(this._usage.totalInputTokens=o.totalInputTokens+(p.usage.input_tokens||0),this._usage.totalOutputTokens=o.totalOutputTokens+(p.usage.output_tokens||0),this._usage.totalCacheReadTokens=o.totalCacheReadTokens+(p.usage.cache_read_input_tokens||0),this._usage.totalCacheCreationTokens=o.totalCacheCreationTokens+(p.usage.cache_creation_input_tokens||0)),p.modelUsage)for(const[i,a]of Object.entries(p.modelUsage)){const t=o.modelUsage[i]||{inputTokens:0,outputTokens:0,costUSD:0};this._usage.modelUsage[i]={inputTokens:t.inputTokens+(a.inputTokens||0),outputTokens:t.outputTokens+(a.outputTokens||0),costUSD:t.costUSD+(a.costUSD||0)}}}{let o=p.type==="assistant"?p.message?.usage:null;if(p.type==="result"&&(o=this._readTranscriptCtx()||o),o){const i=(o.input_tokens||0)+(o.cache_read_input_tokens||0)+(o.cache_creation_input_tokens||0);(i>this._contextTokens||this._contextTokens>0&&i<this._contextTokens*.5)&&(this._contextTokens=i)}}if(p.type==="result"&&(e.ctxTokens=this._contextTokens),this.emit("agent:message",e),p.type==="result"){this._slashTimeout&&(clearTimeout(this._slashTimeout),this._slashTimeout=null),this._stopStallWatchdog();const o=!this._interrupted&&!p.is_error;this._busy=!1,this._interrupted=!1,this.emit("agent:busy",!1),this._pendingResolve&&(this._pendingResolve(0),this._pendingResolve=null,this._pendingReject=null),this._flushPendingSteer(),o&&this._maybeRelayDraft(),this._maybeScheduleFollowUp()}}_trackImagePressure(p){const l=(p.match(/"type":"image"/g)||[]).length;l&&(this._imageBlockCount+=l,this._imageBlockCount>=this._imageNudgeAt&&(this._imageNudgeAt+=IMAGE_NUDGE_STEP,this.emit("agent:message",{type:"system",subtype:"warning",message:`\u2139\uFE0F \u672C\u4F1A\u8BDD\u5DF2\u7D2F\u79EF\u7EA6 ${this._imageBlockCount} \u5F20\u56FE\u7247\u3002\u56FE\u7247\u8D8A\u591A\u8D8A\u5BB9\u6613\u89E6\u53D1 API \u56FE\u7247\u5904\u7406\u9519\u8BEF\u5E76\u62D6\u6162/\u6D88\u8017\u989D\u5EA6\uFF0C\u5EFA\u8BAE\u7528 /clear \u91CD\u5F00\u4E00\u4E2A\u5E72\u51C0\u4F1A\u8BDD\uFF0C\u6216\u51CF\u5C11\u540E\u7EED\u622A\u56FE\u3002`})))}_checkDeathLoop(p,l){if(this._loopTripped)return;let r=!1;try{const e=l||JSON.stringify(p);if(LOOP_POISON_PHRASES.some((s=>e.includes(s)))){const s=p.type==="assistant",u=e.includes('"type":"image"'),d=e.length<=POISON_MAX_NOTICE_LEN;r=!s&&(u||d)}}catch{}if(r&&(this._loopPoisonHits++,this._loopPoisonHits>=LOOP_MAX_POISON_HITS)){this._tripLoopBreaker("\u8BE5\u56FE\u7247/\u5185\u5BB9 API \u53CD\u590D\u65E0\u6CD5\u5904\u7406\u5E76\u5DF2\u88AB\u79FB\u9664\uFF0C\u8BF7\u4E0D\u8981\u518D\u7528 Read \u91CD\u65B0\u8BFB\u53D6\u5B83\u2014\u2014\u5B83\u5DF2\u4E0D\u53EF\u7528\u3002\u8BF7\u57FA\u4E8E\u73B0\u6709\u6587\u5B57\u4FE1\u606F\u7EE7\u7EED\uFF1B\u82E5\u786E\u5B9E\u9700\u8981\u8BE5\u56FE\uFF0C\u8BF7\u63D0\u793A\u7528\u6237\u91CD\u65B0\u4E0A\u4F20\u4E00\u5F20\u66F4\u5C0F\u7684\u622A\u56FE\u3002");return}if(p.type==="assistant"&&Array.isArray(p.message?.content)){const e=!!p.parent_tool_use_id,s=p.parent_tool_use_id||"main";for(const u of p.message.content){if(u.type!=="tool_use")continue;if(!e&&(this._loopTurnToolCalls++,this._loopTurnToolCalls>LOOP_MAX_TURN_TOOLCALLS)){this._tripLoopBreaker(`\u672C\u8F6E\u5DE5\u5177\u8C03\u7528\u5DF2\u8FBE ${this._loopTurnToolCalls} \u6B21\u5E76\u63D2\u5165\u68C0\u67E5\u70B9\u3002\u82E5\u4ECD\u5728\u6B63\u5E38\u63A8\u8FDB\uFF0C\u8BF7\u6982\u8FF0\u5F53\u524D\u8FDB\u5C55\u540E\u7EE7\u7EED\uFF1B\u82E5\u5728\u91CD\u590D\u540C\u7C7B\u64CD\u4F5C\u800C\u6CA1\u6709\u8FDB\u5C55\uFF0C\u8BF7\u6362\u4E00\u79CD\u601D\u8DEF\uFF0C\u6216\u5148\u5411\u7528\u6237\u6C47\u62A5\u518D\u7B49\u5F85\u6307\u793A\u3002`);return}const d=`${s}:${u.name}:${this._hashInput(u.input)}`,o=(this._loopSigCounts.get(d)||0)+1;if(this._loopSigCounts.set(d,o),o>=LOOP_MAX_IDENTICAL_CALLS){this._tripLoopBreaker(`\u5DE5\u5177\u300C${u.name}\u300D\u5DF2\u7528\u5B8C\u5168\u76F8\u540C\u7684\u53C2\u6570\u8C03\u7528 ${o} \u6B21\uFF0C\u7591\u4F3C\u9677\u5165\u6B7B\u5FAA\u73AF\u3002\u8BF7\u505C\u6B62\u91CD\u590D\u8BE5\u8C03\u7528\uFF0C\u6539\u7528\u4E0D\u540C\u53C2\u6570/\u4E0D\u540C\u5DE5\u5177\uFF0C\u6216\u8DF3\u8FC7\u8FD9\u4E00\u6B65\u7EE7\u7EED\u3002`);return}}}}_hashInput(p){let l;try{l=JSON.stringify(stripHeavyPayload(p))}catch{l=String(p)}l.length>4096&&(l=l.slice(0,4096));let r=5381;for(let e=0;e<l.length;e++)r=(r<<5)+r+l.charCodeAt(e)|0;return r.toString(36)}_tripLoopBreaker(p){if(this._loopTripped=!0,console.warn("[agent] \u{1F50C} \u6B7B\u5FAA\u73AF\u7194\u65AD:",p),this._loopAutoRecover>=LOOP_MAX_AUTO_RECOVER){this._pendingSteer=null,this.emit("agent:message",{type:"system",subtype:"warning",message:"\u26A0\uFE0F \u591A\u6B21\u81EA\u52A8\u7EA0\u504F\u4ECD\u672A\u8DF3\u51FA\u5FAA\u73AF\uFF0C\u5DF2\u505C\u6B62\u4EE5\u4FDD\u62A4 session \u989D\u5EA6\u3002\u8BF7 /clear \u91CD\u5F00\u6216\u6362\u4E2A\u95EE\u6CD5\u3002"}),this.interrupt();return}this._loopAutoRecover++,this._pendingSteer=p,this.emit("agent:message",{type:"system",subtype:"warning",message:`\u26A0\uFE0F \u7194\u65AD\u4FDD\u62A4\uFF08\u7B2C ${this._loopAutoRecover} \u6B21\u81EA\u52A8\u7EA0\u504F\uFF09\uFF1A${p}`}),this.interrupt()}_flushPendingSteer(){if(!this._pendingSteer)return;const p=this._pendingSteer;this._pendingSteer=null,setTimeout((()=>{!this._busy&&this.alive&&this.query(p,{internal:!0}).catch((()=>{}))}),600)}setFollowUp({enabled:p,intervalMinutes:l}={}){if(typeof p=="boolean"&&(this._followEnabled=p),l!=null){const r=Math.max(1,Math.min(180,Number(l)||15));this._followIntervalMs=Math.round(r*60*1e3)}return this._followEnabled?this._busy||this._maybeScheduleFollowUp():(this._cancelFollowUp(),this._followCount=0),this.emit("agent:message",{type:"system",subtype:"info",message:this._followEnabled?`\u{1F504} \u81EA\u52A8\u8DDF\u8FDB\u5DF2\u5F00\u542F\uFF1A\u6BCF ${Math.round(this._followIntervalMs/6e4)} \u5206\u949F\u81EA\u52A8\u67E5\u4E00\u6B21\u8FDB\u5EA6\u5E76\u6C47\u62A5\uFF08\u6700\u591A ${this._followMax} \u6B21\uFF1BAI \u5224\u65AD\u5168\u90E8\u5B8C\u6210\u4F1A\u81EA\u52A8\u505C\uFF09\u3002`:"\u{1F504} \u81EA\u52A8\u8DDF\u8FDB\u5DF2\u5173\u95ED\u3002"}),this.followUpState()}followUpState(){return{enabled:this._followEnabled,intervalMinutes:Math.round(this._followIntervalMs/6e4),max:this._followMax,count:this._followCount}}_cancelFollowUp(){this._followTimer&&(clearTimeout(this._followTimer),this._followTimer=null)}_cancelRelay(){this._relayTimer&&(clearTimeout(this._relayTimer),this._relayTimer=null)}_maybeRelayDraft(){!this.alive||this._relayTimer||(this._relayTimer=setTimeout((()=>{if(this._relayTimer=null,this._busy||!this.alive)return;let p=null;try{p=nextAutoSendDraft(this.id)}catch{return}if(!p)return;try{removeDraft(p.id)}catch{}const l=buildDraftPrompt(p);l.trim()&&(this.emit("agent:message",{type:"system",subtype:"info",message:"\u{1F4E5} \u81EA\u52A8\u63A5\u529B\uFF1A\u4E0A\u4E00\u8F6E\u5DF2\u5B8C\u6210\uFF0C\u53D1\u51FA\u4E0B\u9762\u8FD9\u6761\u8349\u7A3F"}),this.emit("agent:message",{type:"user",content:draftDisplayText(p)||l,timestamp:Date.now()}),this.emit("draft-relayed",{id:p.id,sessionId:this.id}),this.lastInputTime=Date.now(),this.query(l).catch((()=>{})))}),RELAY_DELAY_MS),this._relayTimer.unref&&this._relayTimer.unref())}_maybeScheduleFollowUp(){if(this._cancelFollowUp(),!(!this._followEnabled||!this.alive)){if(this._followStopSignal){this._followStopSignal=!1,this._followCount=0,this.emit("agent:message",{type:"system",subtype:"info",message:"\u{1F504} AI \u5DF2\u786E\u8BA4\u4EFB\u52A1\u5168\u90E8\u5B8C\u6210\uFF0C\u81EA\u52A8\u8DDF\u8FDB\u505C\u6B62\u3002"});return}if(this._followCount>=this._followMax){this.emit("agent:message",{type:"system",subtype:"info",message:`\u{1F504} \u81EA\u52A8\u8DDF\u8FDB\u5DF2\u8FDE\u7EED ${this._followMax} \u6B21\uFF0C\u6682\u505C\uFF08\u9632\u7A7A\u8F6C\u70E7\u989D\u5EA6\uFF09\u3002\u9700\u8981\u7EE7\u7EED\u53EF\u624B\u52A8\u95EE\u4E00\u53E5\u6216\u91CD\u5F00\u3002`});return}this._followTimer=setTimeout((()=>{this._followTimer=null,!(this._busy||!this.alive||!this._followEnabled)&&(this._followCount++,this.emit("agent:message",{type:"system",subtype:"info",message:`\u{1F504} \u81EA\u52A8\u8DDF\u8FDB\uFF08\u7B2C ${this._followCount}/${this._followMax} \u6B21\uFF09\uFF1A\u8BA9 AI \u67E5\u770B\u8FDB\u5EA6\u2026`}),this.query(FOLLOW_UP_PROMPT,{internal:!0}).catch((()=>{})))}),this._followIntervalMs)}}_startStallWatchdog(){this._stopStallWatchdog(),this._watchdogTimer=setInterval((()=>{if(!this._busy){this._stopStallWatchdog();return}if(this._awaitingTool)return;const p=Date.now()-this._lastActivityAt;if(p>=STALL_WATCHDOG_MS){const l=Math.round(p/1e3);console.warn(`[agent] \u23F1 \u505C\u6446\u770B\u95E8\u72D7\u89E6\u53D1\uFF1A\u7B49\u5F85\u6A21\u578B\u54CD\u5E94\u5DF2 ${l}s \u65E0\u4EFB\u4F55\u6D88\u606F\uFF0C\u5F3A\u5236\u4E2D\u65AD`),this.emit("agent:message",{type:"system",subtype:"warning",message:`\u23F1 \u68C0\u6D4B\u5230\u4F1A\u8BDD\u505C\u6446\uFF08\u7B49\u5F85\u6A21\u578B\u54CD\u5E94 ${l}s \u65E0\u54CD\u5E94\uFF0C\u7591\u4F3C API \u4F20\u8F93\u5C42\u91CD\u8BD5\u5361\u4F4F\uFF09\uFF0C\u5DF2\u5F3A\u5236\u4E2D\u65AD\u672C\u56DE\u5408\u3002\u8BF7\u91CD\u8BD5\u6216\u7A0D\u540E\u518D\u8BD5\u3002`}),this._stopStallWatchdog(),this.interrupt()}}),STALL_CHECK_INTERVAL_MS),this._watchdogTimer.unref&&this._watchdogTimer.unref()}_stopStallWatchdog(){this._watchdogTimer&&(clearInterval(this._watchdogTimer),this._watchdogTimer=null)}respondPermission(p,l){return this._writeStdin({type:"control_response",request_id:p,allow:l})}_hardKillTree(p){if(p)try{if(process.platform==="win32")(0,external_child_process_namespaceObject.execSync)(`taskkill /PID ${p} /F /T`,{stdio:"pipe",timeout:5e3});else{try{process.kill(p,"SIGTERM")}catch{}setTimeout((()=>{try{process.kill(p,0)}catch{return}try{process.kill(p,"SIGKILL")}catch{}}),1e3)}}catch{}}interrupt(){if(!this._proc||!this._busy)return;this._interrupted=!0;const p=this._proc.pid,l=this._proc,e=this._writeStdin({type:"control_request",request_id:`interrupt-${Date.now()}-${Math.random().toString(36).slice(2,8)}`,request:{subtype:"interrupt"}})?1500:0;setTimeout((()=>{this._busy&&this._proc===l&&(console.log(`[agent] graceful interrupt \u8D85\u65F6\uFF0C\u5F3A\u5236\u7ED3\u675F\u8FDB\u7A0B\u6811 PID ${p}`),this._hardKillTree(p))}),e),this.emit("agent:message",{type:"system",subtype:"interrupted",message:"\u67E5\u8BE2\u5DF2\u4E2D\u65AD"})}kill(){if(this.alive=!1,this._cancelRelay(),this._cancelFollowUp(),this._keepAliveTimer&&(clearInterval(this._keepAliveTimer),this._keepAliveTimer=null),this._proc&&(this._hardKillTree(this._proc.pid),this._proc=null),this._pendingReject){try{this._pendingReject(new Error("\u4F1A\u8BDD\u5DF2\u7EC8\u6B62"))}catch{}this._pendingResolve=null,this._pendingReject=null}this._busy=!1}_readTranscriptCtx(){if(!this.claudeSessionId)return null;try{const p=(0,external_path_.join)((0,external_os_.homedir)(),".claude","projects");if(!(0,external_fs_.existsSync)(p))return null;for(const l of(0,external_fs_.readdirSync)(p,{withFileTypes:!0})){if(!l.isDirectory())continue;const r=(0,external_path_.join)(p,l.name,this.claudeSessionId+".jsonl");if(!(0,external_fs_.existsSync)(r))continue;const e=(0,external_fs_.statSync)(r).size,s=Math.min(e,65536),u=Buffer.alloc(s),d=(0,external_fs_.openSync)(r,"r");try{(0,external_fs_.readSync)(d,u,0,s,e-s)}finally{(0,external_fs_.closeSync)(d)}const o=u.toString("utf8").split(`
|
|
384
|
+
`)}))})),this._proc.on("close",(d=>{if(!this._interrupted&&this._buffer.trim())try{const o=JSON.parse(this._buffer.trim());this._handleMessage(o)}catch{}this._keepAliveTimer&&(clearInterval(this._keepAliveTimer),this._keepAliveTimer=null),this._stopStallWatchdog(),this._proc=null,this._buffer="",console.log(`[agent] \u8FDB\u7A0B\u9000\u51FA (code ${d}), \u4F1A\u8BDD: ${this.claudeSessionId||"?"}`),this._busy&&(this._busy=!1,this._interrupted=!1,this.emit("agent:busy",!1),this._pendingReject&&(this._pendingReject(new Error(`Claude \u8FDB\u7A0B\u9000\u51FA (code ${d})`)),this._pendingResolve=null,this._pendingReject=null)),this._flushPendingSteer(),this.alive&&d!==0&&d!==null&&this.emit("agent:error",{message:`Claude \u8FDB\u7A0B\u5F02\u5E38\u9000\u51FA (code ${d})\uFF0C\u4E0B\u6B21\u67E5\u8BE2\u5C06\u81EA\u52A8\u91CD\u542F`})})),this._proc.on("error",(d=>{this._keepAliveTimer&&(clearInterval(this._keepAliveTimer),this._keepAliveTimer=null),this._stopStallWatchdog(),this._proc=null,this._busy=!1,this.emit("agent:busy",!1),this.emit("agent:error",{message:`\u542F\u52A8 Claude \u5931\u8D25: ${d.message}`}),this._pendingReject&&(this._pendingReject(d),this._pendingResolve=null,this._pendingReject=null)})),this._keepAliveTimer=setInterval((()=>{this._writeStdin({type:"keep_alive"})}),3e4)}async query(p,{internal:l=!1,from:r=null}={}){if(this._busy){this.emit("agent:error",{message:"\u6B63\u5728\u5904\u7406\u4E2D\uFF0C\u8BF7\u7B49\u5F85\u5B8C\u6210"});return}if(l||(this._followCount=0,this._followStopSignal=!1,this._cancelFollowUp()),p==="/clear"){this.reset(),this.emit("agent:message",{type:"system",subtype:"info",message:"\u5DF2\u6E05\u9664 Claude \u4E0A\u4E0B\u6587\uFF0C\u4E0B\u6B21\u63D0\u95EE\u5C06\u5F00\u542F\u5168\u65B0\u5BF9\u8BDD"});return}const e=p.match(/^\/resume\s+(\S+)/);if(e){if(this._proc){try{this._proc.kill("SIGTERM")}catch{}this._proc=null}this.claudeSessionId=e[1],this._history=[],this._buffer="",this._loadResumedHistory(),this.emit("agent:message",{type:"system",subtype:"info",message:`\u5DF2\u5207\u6362\u5230\u4F1A\u8BDD ${e[1].slice(0,8)}...\uFF0C\u4E0B\u6B21\u63D0\u95EE\u5C06\u6062\u590D\u8BE5\u5BF9\u8BDD`});return}const s=p.match(/^\/model\s+(\S+)/);if(s){if(this.modelId=s[1],this._proc){try{this._proc.kill("SIGTERM")}catch{}this._proc=null}this.emit("agent:message",{type:"system",subtype:"info",message:`\u6A21\u578B\u5DF2\u5207\u6362\u4E3A ${s[1]}`});return}if(p==="/plan"||p.startsWith("/plan ")){if(this.permissionMode==="plan"){if(this.permissionMode=normalizePermissionMode(this._prePlanMode),this._prePlanMode=null,this._proc){try{this._proc.kill("SIGTERM")}catch{}this._proc=null}this.emit("agent:message",{type:"system",subtype:"info",message:"\u5DF2\u9000\u51FA\u8BA1\u5212\u6A21\u5F0F"})}else{if(this._prePlanMode=this.permissionMode,this.permissionMode="plan",this._proc){try{this._proc.kill("SIGTERM")}catch{}this._proc=null}this.emit("agent:message",{type:"system",subtype:"info",message:"\u5DF2\u8FDB\u5165\u8BA1\u5212\u6A21\u5F0F\uFF08\u53EA\u89C4\u5212\u4E0D\u6267\u884C\uFF09"});const o=p.slice(5).trim();if(o){setTimeout((()=>this.query(o)),500);return}}return}if(p.startsWith("/rename")){this.emit("agent:message",{type:"system",subtype:"info",message:"\u4F1A\u8BDD\u5DF2\u547D\u540D"});return}this._busy=!0,this._interrupted=!1,this.emit("agent:busy",!0),this._loopSigCounts.clear(),this._loopPoisonHits=0,this._loopTurnToolCalls=0,this._loopTripped=!1,l||(this._loopAutoRecover=0),this._redactor=buildRedactor(),this._pseudoMap||(this._pseudoMap=loadMap(this.claudeSessionId||this.id)),this._pseudo=buildPseudonymizer(this.cwd,this._pseudoMap),this._lastActivityAt=Date.now(),this._awaitingTool=!1,this._startStallWatchdog();const u=redactDeep(p,this._redactor);return this._history.push({type:"user",content:u,timestamp:Date.now()}),r&&this.emit("agent:message",{type:"user",content:u,timestamp:Date.now(),from:r}),!this.firstUserText&&typeof u=="string"&&!u.startsWith("/")&&(this.firstUserText=u.trim().slice(0,160)),this._proc||this._spawnProcess(),new Promise(((d,o)=>{this._pendingResolve=d,this._pendingReject=o,p.startsWith("/")&&(this._slashTimeout=setTimeout((()=>{this._busy&&(this._busy=!1,this._interrupted=!1,this.emit("agent:busy",!1),this.emit("agent:message",{type:"system",subtype:"info",message:`\u547D\u4EE4 ${p} \u5DF2\u53D1\u9001`}),this._pendingResolve&&(this._pendingResolve(0),this._pendingResolve=null,this._pendingReject=null))}),3e4));let i=p;if(this._pseudo?.enabled){const t=this._pseudo.pseudonymize(p);i=t.text;const n=this.claudeSessionId||this.id;saveMap(n,this._pseudoMap),t.count>0&&(appendAudit(n,{ts:Date.now(),level:this._pseudo.level,maskedCount:t.count,byType:t.byType,sentToModel:t.text}),this.emit("agent:message",{type:"system",subtype:"info",message:`\u{1F6E1} \u9690\u79C1\u62A4\u680F\uFF1A\u672C\u8F6E\u5DF2\u8131\u654F ${t.count} \u5904\u654F\u611F\u4FE1\u606F\uFF0C\u6A21\u578B\u53EA\u770B\u5230\u5360\u4F4D\u7B26\uFF08\u6620\u5C04\u8868\u7559\u672C\u673A\u3001\u53EF\u9006\u8FD8\u539F\uFF09\u3002`}))}this._writeStdin({type:"user",message:{role:"user",content:i}})||(this._busy=!1,this.emit("agent:busy",!1),this._pendingResolve=null,this._pendingReject=null,o(new Error("Claude \u8FDB\u7A0B\u4E0D\u53EF\u5199")))}))}_handleMessage(p){const l=!!this.claudeSessionId;if(this._lastActivityAt=Date.now(),p.type==="assistant"&&Array.isArray(p.message?.content)?p.message.content.some((o=>o.type==="tool_use"))&&(this._awaitingTool=!0):p.type==="user"&&(this._awaitingTool=!1),p.session_id&&!this.claudeSessionId&&(this.claudeSessionId=p.session_id),p.type==="system"&&p.subtype==="init"&&p.session_id&&(this.claudeSessionId=p.session_id),!l&&this.claudeSessionId&&this.emit("session-id",this.claudeSessionId),p.type==="system"&&p.subtype==="init"&&this.permissionMode==="bypassPermissions"&&p.permissionMode&&p.permissionMode!=="bypassPermissions"){const o=p.permissionMode;this.reset(),this.emit("agent:message",{type:"system",subtype:"warning",message:`\u26A0\uFE0F \u8FD9\u4E2A\u4F1A\u8BDD\u88AB --resume \u5E26\u56DE\u4E86\u300C${o}\u300D\u6A21\u5F0F\uFF08\u975E bypass\uFF0C\u4F1A\u62E6\u5199/\u5220\u4E14\u65E0\u6CD5\u8FD0\u884C\u65F6\u63D0\u6743\uFF0C\u70B9\u300C\u9000\u51FA\u8BA1\u5212\u300D\u4E5F\u9003\u4E0D\u6389\uFF09\u3002\u5DF2\u81EA\u52A8\u65AD\u5F00\u65E7\u4F1A\u8BDD\u94FE\u3001\u91CD\u5F00\u4E3A\u5E72\u51C0\u7684 bypass \u4F1A\u8BDD\u2014\u2014\u8BF7\u91CD\u53D1\u4F60\u521A\u624D\u7684\u6307\u4EE4\uFF0C\u8FD9\u6B21\u4F1A\u76F4\u63A5\u6267\u884C\u3002`});return}const r=this._pseudo?.enabled?redactDeep(p,this._pseudo.restore):p,e=redactDeep(r,this._redactor),s=stripHeavyPayload({...e,timestamp:Date.now()}),u=JSON.stringify(s);this._followEnabled&&u.includes("[\u8DDF\u8FDB\u5B8C\u6210]")&&(this._followStopSignal=!0),u.length>10240&&(s._truncated=!0),this._history.push(s);const d=500;if(this._history.length>d&&(this._history=this._history.slice(-d)),this._trackImagePressure(u),this._checkDeathLoop(p,u),p.type==="rate_limit_event"&&p.rate_limit_info&&(this._usage.rateLimitInfo=p.rate_limit_info),p.type==="result"){const o=this._usageBase;if(this._procQueryCount=(this._procQueryCount||0)+1,this._usage.queryCount=o.queryCount+this._procQueryCount,p.total_cost_usd&&(this._usage.totalCostUSD=o.totalCostUSD+p.total_cost_usd),p.usage&&(this._usage.totalInputTokens=o.totalInputTokens+(p.usage.input_tokens||0),this._usage.totalOutputTokens=o.totalOutputTokens+(p.usage.output_tokens||0),this._usage.totalCacheReadTokens=o.totalCacheReadTokens+(p.usage.cache_read_input_tokens||0),this._usage.totalCacheCreationTokens=o.totalCacheCreationTokens+(p.usage.cache_creation_input_tokens||0)),p.modelUsage)for(const[i,a]of Object.entries(p.modelUsage)){const t=o.modelUsage[i]||{inputTokens:0,outputTokens:0,costUSD:0};this._usage.modelUsage[i]={inputTokens:t.inputTokens+(a.inputTokens||0),outputTokens:t.outputTokens+(a.outputTokens||0),costUSD:t.costUSD+(a.costUSD||0)}}}{let o=p.type==="assistant"?p.message?.usage:null;if(p.type==="result"&&(o=this._readTranscriptCtx()||o),o){const i=(o.input_tokens||0)+(o.cache_read_input_tokens||0)+(o.cache_creation_input_tokens||0);(i>this._contextTokens||this._contextTokens>0&&i<this._contextTokens*.5)&&(this._contextTokens=i)}}if(p.type==="result"&&(e.ctxTokens=this._contextTokens),this.emit("agent:message",e),p.type==="result"){this._slashTimeout&&(clearTimeout(this._slashTimeout),this._slashTimeout=null),this._stopStallWatchdog();const o=!this._interrupted&&!p.is_error;this._busy=!1,this._interrupted=!1,this.emit("agent:busy",!1),this._pendingResolve&&(this._pendingResolve(0),this._pendingResolve=null,this._pendingReject=null),this._flushPendingSteer(),o&&this._maybeRelayDraft(),this._maybeScheduleFollowUp()}}_trackImagePressure(p){const l=(p.match(/"type":"image"/g)||[]).length;l&&(this._imageBlockCount+=l,this._imageBlockCount>=this._imageNudgeAt&&(this._imageNudgeAt+=IMAGE_NUDGE_STEP,this.emit("agent:message",{type:"system",subtype:"warning",message:`\u2139\uFE0F \u672C\u4F1A\u8BDD\u5DF2\u7D2F\u79EF\u7EA6 ${this._imageBlockCount} \u5F20\u56FE\u7247\u3002\u56FE\u7247\u8D8A\u591A\u8D8A\u5BB9\u6613\u89E6\u53D1 API \u56FE\u7247\u5904\u7406\u9519\u8BEF\u5E76\u62D6\u6162/\u6D88\u8017\u989D\u5EA6\uFF0C\u5EFA\u8BAE\u7528 /clear \u91CD\u5F00\u4E00\u4E2A\u5E72\u51C0\u4F1A\u8BDD\uFF0C\u6216\u51CF\u5C11\u540E\u7EED\u622A\u56FE\u3002`})))}_checkDeathLoop(p,l){if(this._loopTripped)return;let r=!1;try{const e=l||JSON.stringify(p);if(LOOP_POISON_PHRASES.some((s=>e.includes(s)))){const s=p.type==="assistant",u=e.includes('"type":"image"'),d=e.length<=POISON_MAX_NOTICE_LEN;r=!s&&(u||d)}}catch{}if(r&&(this._loopPoisonHits++,this._loopPoisonHits>=LOOP_MAX_POISON_HITS)){this._tripLoopBreaker("\u8BE5\u56FE\u7247/\u5185\u5BB9 API \u53CD\u590D\u65E0\u6CD5\u5904\u7406\u5E76\u5DF2\u88AB\u79FB\u9664\uFF0C\u8BF7\u4E0D\u8981\u518D\u7528 Read \u91CD\u65B0\u8BFB\u53D6\u5B83\u2014\u2014\u5B83\u5DF2\u4E0D\u53EF\u7528\u3002\u8BF7\u57FA\u4E8E\u73B0\u6709\u6587\u5B57\u4FE1\u606F\u7EE7\u7EED\uFF1B\u82E5\u786E\u5B9E\u9700\u8981\u8BE5\u56FE\uFF0C\u8BF7\u63D0\u793A\u7528\u6237\u91CD\u65B0\u4E0A\u4F20\u4E00\u5F20\u66F4\u5C0F\u7684\u622A\u56FE\u3002");return}if(p.type==="assistant"&&Array.isArray(p.message?.content)){const e=!!p.parent_tool_use_id,s=p.parent_tool_use_id||"main";for(const u of p.message.content){if(u.type!=="tool_use")continue;if(!e&&(this._loopTurnToolCalls++,this._loopTurnToolCalls>LOOP_MAX_TURN_TOOLCALLS)){this._tripLoopBreaker(`\u672C\u8F6E\u5DE5\u5177\u8C03\u7528\u5DF2\u8FBE ${this._loopTurnToolCalls} \u6B21\u5E76\u63D2\u5165\u68C0\u67E5\u70B9\u3002\u82E5\u4ECD\u5728\u6B63\u5E38\u63A8\u8FDB\uFF0C\u8BF7\u6982\u8FF0\u5F53\u524D\u8FDB\u5C55\u540E\u7EE7\u7EED\uFF1B\u82E5\u5728\u91CD\u590D\u540C\u7C7B\u64CD\u4F5C\u800C\u6CA1\u6709\u8FDB\u5C55\uFF0C\u8BF7\u6362\u4E00\u79CD\u601D\u8DEF\uFF0C\u6216\u5148\u5411\u7528\u6237\u6C47\u62A5\u518D\u7B49\u5F85\u6307\u793A\u3002`);return}const d=`${s}:${u.name}:${this._hashInput(u.input)}`,o=(this._loopSigCounts.get(d)||0)+1;if(this._loopSigCounts.set(d,o),o>=LOOP_MAX_IDENTICAL_CALLS){this._tripLoopBreaker(`\u5DE5\u5177\u300C${u.name}\u300D\u5DF2\u7528\u5B8C\u5168\u76F8\u540C\u7684\u53C2\u6570\u8C03\u7528 ${o} \u6B21\uFF0C\u7591\u4F3C\u9677\u5165\u6B7B\u5FAA\u73AF\u3002\u8BF7\u505C\u6B62\u91CD\u590D\u8BE5\u8C03\u7528\uFF0C\u6539\u7528\u4E0D\u540C\u53C2\u6570/\u4E0D\u540C\u5DE5\u5177\uFF0C\u6216\u8DF3\u8FC7\u8FD9\u4E00\u6B65\u7EE7\u7EED\u3002`);return}}}}_hashInput(p){let l;try{l=JSON.stringify(stripHeavyPayload(p))}catch{l=String(p)}l.length>4096&&(l=l.slice(0,4096));let r=5381;for(let e=0;e<l.length;e++)r=(r<<5)+r+l.charCodeAt(e)|0;return r.toString(36)}_tripLoopBreaker(p){if(this._loopTripped=!0,console.warn("[agent] \u{1F50C} \u6B7B\u5FAA\u73AF\u7194\u65AD:",p),this._loopAutoRecover>=LOOP_MAX_AUTO_RECOVER){this._pendingSteer=null,this.emit("agent:message",{type:"system",subtype:"warning",message:"\u26A0\uFE0F \u591A\u6B21\u81EA\u52A8\u7EA0\u504F\u4ECD\u672A\u8DF3\u51FA\u5FAA\u73AF\uFF0C\u5DF2\u505C\u6B62\u4EE5\u4FDD\u62A4 session \u989D\u5EA6\u3002\u8BF7 /clear \u91CD\u5F00\u6216\u6362\u4E2A\u95EE\u6CD5\u3002"}),this.interrupt();return}this._loopAutoRecover++,this._pendingSteer=p,this.emit("agent:message",{type:"system",subtype:"warning",message:`\u26A0\uFE0F \u7194\u65AD\u4FDD\u62A4\uFF08\u7B2C ${this._loopAutoRecover} \u6B21\u81EA\u52A8\u7EA0\u504F\uFF09\uFF1A${p}`}),this.interrupt()}_flushPendingSteer(){if(!this._pendingSteer)return;const p=this._pendingSteer;this._pendingSteer=null,setTimeout((()=>{!this._busy&&this.alive&&this.query(p,{internal:!0}).catch((()=>{}))}),600)}setFollowUp({enabled:p,intervalMinutes:l}={}){if(typeof p=="boolean"&&(this._followEnabled=p),l!=null){const r=Math.max(1,Math.min(180,Number(l)||15));this._followIntervalMs=Math.round(r*60*1e3)}return this._followEnabled?this._busy||this._maybeScheduleFollowUp():(this._cancelFollowUp(),this._followCount=0),this.emit("agent:message",{type:"system",subtype:"info",message:this._followEnabled?`\u{1F504} \u81EA\u52A8\u8DDF\u8FDB\u5DF2\u5F00\u542F\uFF1A\u6BCF ${Math.round(this._followIntervalMs/6e4)} \u5206\u949F\u81EA\u52A8\u67E5\u4E00\u6B21\u8FDB\u5EA6\u5E76\u6C47\u62A5\uFF08\u6700\u591A ${this._followMax} \u6B21\uFF1BAI \u5224\u65AD\u5168\u90E8\u5B8C\u6210\u4F1A\u81EA\u52A8\u505C\uFF09\u3002`:"\u{1F504} \u81EA\u52A8\u8DDF\u8FDB\u5DF2\u5173\u95ED\u3002"}),this.followUpState()}followUpState(){return{enabled:this._followEnabled,intervalMinutes:Math.round(this._followIntervalMs/6e4),max:this._followMax,count:this._followCount}}_cancelFollowUp(){this._followTimer&&(clearTimeout(this._followTimer),this._followTimer=null)}_cancelRelay(){this._relayTimer&&(clearTimeout(this._relayTimer),this._relayTimer=null)}_maybeRelayDraft(){!this.alive||this._relayTimer||(this._relayTimer=setTimeout((()=>{if(this._relayTimer=null,this._busy||!this.alive)return;let p=null;try{p=nextAutoSendDraft(this.id)}catch{return}if(!p)return;try{removeDraft(p.id)}catch{}const l=buildDraftPrompt(p);l.trim()&&(this.emit("agent:message",{type:"system",subtype:"info",message:"\u{1F4E5} \u81EA\u52A8\u63A5\u529B\uFF1A\u4E0A\u4E00\u8F6E\u5DF2\u5B8C\u6210\uFF0C\u53D1\u51FA\u4E0B\u9762\u8FD9\u6761\u8349\u7A3F"}),this.emit("agent:message",{type:"user",content:draftDisplayText(p)||l,timestamp:Date.now()}),this.emit("draft-relayed",{id:p.id,sessionId:this.id}),this.lastInputTime=Date.now(),this.query(l).catch((()=>{})))}),RELAY_DELAY_MS),this._relayTimer.unref&&this._relayTimer.unref())}_maybeScheduleFollowUp(){if(this._cancelFollowUp(),!(!this._followEnabled||!this.alive)){if(this._followStopSignal){this._followStopSignal=!1,this._followCount=0,this.emit("agent:message",{type:"system",subtype:"info",message:"\u{1F504} AI \u5DF2\u786E\u8BA4\u4EFB\u52A1\u5168\u90E8\u5B8C\u6210\uFF0C\u81EA\u52A8\u8DDF\u8FDB\u505C\u6B62\u3002"});return}if(this._followCount>=this._followMax){this.emit("agent:message",{type:"system",subtype:"info",message:`\u{1F504} \u81EA\u52A8\u8DDF\u8FDB\u5DF2\u8FDE\u7EED ${this._followMax} \u6B21\uFF0C\u6682\u505C\uFF08\u9632\u7A7A\u8F6C\u70E7\u989D\u5EA6\uFF09\u3002\u9700\u8981\u7EE7\u7EED\u53EF\u624B\u52A8\u95EE\u4E00\u53E5\u6216\u91CD\u5F00\u3002`});return}this._followTimer=setTimeout((()=>{this._followTimer=null,!(this._busy||!this.alive||!this._followEnabled)&&(this._followCount++,this.emit("agent:message",{type:"system",subtype:"info",message:`\u{1F504} \u81EA\u52A8\u8DDF\u8FDB\uFF08\u7B2C ${this._followCount}/${this._followMax} \u6B21\uFF09\uFF1A\u8BA9 AI \u67E5\u770B\u8FDB\u5EA6\u2026`}),this.query(FOLLOW_UP_PROMPT,{internal:!0}).catch((()=>{})))}),this._followIntervalMs)}}_startStallWatchdog(){this._stopStallWatchdog(),this._watchdogTimer=setInterval((()=>{if(!this._busy){this._stopStallWatchdog();return}if(this._awaitingTool)return;const p=Date.now()-this._lastActivityAt;if(p>=STALL_WATCHDOG_MS){const l=Math.round(p/1e3);console.warn(`[agent] \u23F1 \u505C\u6446\u770B\u95E8\u72D7\u89E6\u53D1\uFF1A\u7B49\u5F85\u6A21\u578B\u54CD\u5E94\u5DF2 ${l}s \u65E0\u4EFB\u4F55\u6D88\u606F\uFF0C\u5F3A\u5236\u4E2D\u65AD`),this.emit("agent:message",{type:"system",subtype:"warning",message:`\u23F1 \u68C0\u6D4B\u5230\u4F1A\u8BDD\u505C\u6446\uFF08\u7B49\u5F85\u6A21\u578B\u54CD\u5E94 ${l}s \u65E0\u54CD\u5E94\uFF0C\u7591\u4F3C API \u4F20\u8F93\u5C42\u91CD\u8BD5\u5361\u4F4F\uFF09\uFF0C\u5DF2\u5F3A\u5236\u4E2D\u65AD\u672C\u56DE\u5408\u3002\u8BF7\u91CD\u8BD5\u6216\u7A0D\u540E\u518D\u8BD5\u3002`}),this._stopStallWatchdog(),this.interrupt()}}),STALL_CHECK_INTERVAL_MS),this._watchdogTimer.unref&&this._watchdogTimer.unref()}_stopStallWatchdog(){this._watchdogTimer&&(clearInterval(this._watchdogTimer),this._watchdogTimer=null)}respondPermission(p,l){return this._writeStdin({type:"control_response",request_id:p,allow:l})}_hardKillTree(p){if(p)try{if(process.platform==="win32")(0,external_child_process_namespaceObject.execSync)(`taskkill /PID ${p} /F /T`,{stdio:"pipe",timeout:5e3});else{try{process.kill(p,"SIGTERM")}catch{}setTimeout((()=>{try{process.kill(p,0)}catch{return}try{process.kill(p,"SIGKILL")}catch{}}),1e3)}}catch{}}interrupt(){if(!this._proc||!this._busy)return;this._interrupted=!0;const p=this._proc.pid,l=this._proc,e=this._writeStdin({type:"control_request",request_id:`interrupt-${Date.now()}-${Math.random().toString(36).slice(2,8)}`,request:{subtype:"interrupt"}})?1500:0;setTimeout((()=>{this._busy&&this._proc===l&&(console.log(`[agent] graceful interrupt \u8D85\u65F6\uFF0C\u5F3A\u5236\u7ED3\u675F\u8FDB\u7A0B\u6811 PID ${p}`),this._hardKillTree(p))}),e),this.emit("agent:message",{type:"system",subtype:"interrupted",message:"\u67E5\u8BE2\u5DF2\u4E2D\u65AD"})}kill(){if(this.alive=!1,this._cancelRelay(),this._cancelFollowUp(),this._keepAliveTimer&&(clearInterval(this._keepAliveTimer),this._keepAliveTimer=null),this._proc&&(this._hardKillTree(this._proc.pid),this._proc=null),this._pendingReject){try{this._pendingReject(new Error("\u4F1A\u8BDD\u5DF2\u7EC8\u6B62"))}catch{}this._pendingResolve=null,this._pendingReject=null}this._busy=!1}_readTranscriptCtx(){if(!this.claudeSessionId)return null;try{const p=(0,external_path_.join)((0,external_os_.homedir)(),".claude","projects");if(!(0,external_fs_.existsSync)(p))return null;for(const l of(0,external_fs_.readdirSync)(p,{withFileTypes:!0})){if(!l.isDirectory())continue;const r=(0,external_path_.join)(p,l.name,this.claudeSessionId+".jsonl");if(!(0,external_fs_.existsSync)(r))continue;const e=(0,external_fs_.statSync)(r).size,s=Math.min(e,65536),u=Buffer.alloc(s),d=(0,external_fs_.openSync)(r,"r");try{(0,external_fs_.readSync)(d,u,0,s,e-s)}finally{(0,external_fs_.closeSync)(d)}const o=u.toString("utf8").split(`
|
|
385
385
|
`).filter(Boolean);for(let i=o.length-1;i>=0;i--)try{const a=JSON.parse(o[i]),t=a.type==="assistant"?a.message?.usage:null;if(t&&(t.input_tokens||0)+(t.cache_read_input_tokens||0)+(t.cache_creation_input_tokens||0)>0)return t}catch{}}}catch{}return null}_loadResumedHistory(){try{const p=(0,external_path_.join)((0,external_os_.homedir)(),".claude","projects");if(!(0,external_fs_.existsSync)(p))return;for(const l of(0,external_fs_.readdirSync)(p,{withFileTypes:!0})){if(!l.isDirectory())continue;const r=(0,external_path_.join)(p,l.name,this.claudeSessionId+".jsonl");if(!(0,external_fs_.existsSync)(r))continue;const e=(0,external_fs_.readFileSync)(r,"utf8").split(`
|
|
386
386
|
`).filter(Boolean),s=e.slice(-200);if(!this.firstUserText)for(const u of e.slice(0,40))try{const d=JSON.parse(u);if(d.type!=="user"&&d.type!=="human")continue;const o=d.message?.content,i=(typeof o=="string"?o:o?.find?.((a=>a.type==="text"))?.text||"").trim();if(!i||i.startsWith("/")||/^(Caveat:|<command-name>|<system-reminder>|<local-command)/.test(i))continue;this.firstUserText=i.slice(0,160);break}catch{}for(const u of s)try{const d=JSON.parse(u);if(d.isSidechain)continue;if(d.type==="human"||d.type==="user"&&d.message?.content){const o=typeof d.message.content=="string"?d.message.content:d.message.content.find((i=>i.type==="text"))?.text||"";o&&this._history.push({type:"user",content:o,timestamp:d.timestamp})}else if(d.type==="assistant"&&d.message?.content){this._history.push({type:"assistant",message:d.message,timestamp:d.timestamp});const o=d.message.usage;if(o){const i=(o.input_tokens||0)+(o.cache_read_input_tokens||0)+(o.cache_creation_input_tokens||0);this._contextTokens=i}}else d.type==="result"&&this._history.push({type:"result",total_cost_usd:d.costUSD||d.total_cost_usd,timestamp:d.timestamp})}catch{}try{const u=(0,external_fs_.statSync)(r).mtimeMs;u>0&&(this._lastActivityAt=Math.max(this._lastActivityAt,u))}catch{}return}}catch(p){console.warn("[agent] \u52A0\u8F7D\u6062\u590D\u4F1A\u8BDD\u5386\u53F2\u5931\u8D25:",p.message)}}get isBusy(){return this._busy}getRecentHistory(p=20){return this._history.slice(-p)}getUsage(){return{...this._usage}}addViewer(p,l){this._viewers.add(p),l&&this._viewerNames.set(p,l)}removeViewer(p){this._viewers.delete(p),this._viewerNames.delete(p)}get viewerCount(){return new Set(this._viewerNames.values()).size||this._viewers.size}takeControl(p,l){this.controlHolder=p,this.controlHolderName=l||null,this.lastInputTime=Date.now()}releaseControl(){this.controlHolder=null,this.controlHolderName=null,this.lastInputTime=null}isController(p){return this.controlHolder===p}setPermissionMode(p){this.permissionMode=normalizePermissionMode(p),this._proc&&(this.kill(),this.alive=!0)}reset(){this._proc&&(this._hardKillTree(this._proc.pid),this._proc=null),this.claudeSessionId=null,this._history=[],this._contextTokens=0,this._buffer="",this._imageBlockCount=0,this._imageNudgeAt=IMAGE_NUDGE_STEP,this._cancelFollowUp(),this._followCount=0,this._followStopSignal=!1,this._cancelRelay(),this.permissionMode="bypassPermissions"}toJSON(){return{id:this.id,title:this.title,firstUserText:this.firstUserText||"",cwd:this.cwd,tags:this.tags,createdAt:this.createdAt,lastActivityAt:this._lastActivityAt||null,mode:"agent",alive:this.alive,viewers:this.viewerCount,controlHolder:this.controlHolder,controlHolderName:this.controlHolderName,permissionMode:this.permissionMode,owner:this.owner,claudeSessionId:this.claudeSessionId,busy:this._busy,usage:this._usage,contextTokens:this._contextTokens,followUp:this.followUpState()}}}function getSessionHealth(E){const p={found:!1,persistedMode:null,poisoned:!1,messageCount:0,transcriptKB:0,imageCount:0,updatedAt:null};if(!E)return p;try{const l=(0,external_path_.join)((0,external_os_.homedir)(),".claude","projects");if(!(0,external_fs_.existsSync)(l))return p;for(const r of(0,external_fs_.readdirSync)(l,{withFileTypes:!0})){if(!r.isDirectory())continue;const e=(0,external_path_.join)(l,r.name,E+".jsonl");if(!(0,external_fs_.existsSync)(e))continue;const s=(0,external_fs_.readFileSync)(e,"utf8"),u=s.split(`
|
|
387
387
|
`).filter(Boolean);let d=null,o=null,i=0;for(const a of u){const t=a.match(/"type":"image"/g);if(t&&(i+=t.length),a.includes("permissionMode"))try{const n=JSON.parse(a);n.type==="user"&&n.permissionMode&&(d=n.permissionMode)}catch{}}try{const a=JSON.parse(u[u.length-1]);a.timestamp&&(o=a.timestamp)}catch{}return{found:!0,persistedMode:d,poisoned:!!(d&&d!=="bypassPermissions"),messageCount:u.length,transcriptKB:Math.round(s.length/1024),imageCount:i,updatedAt:o}}}catch{}return p}let _claudeSessionsCache=null,_claudeSessionsCacheTime=0;const CLAUDE_SESSIONS_CACHE_TTL=3e4;function listLocalClaudeSessions(E){const p=Date.now();if(_claudeSessionsCache&&p-_claudeSessionsCacheTime<CLAUDE_SESSIONS_CACHE_TTL){if(!E)return _claudeSessionsCache;const s=(0,external_path_.resolve)((0,external_path_.normalize)(E)).replace(/[\\/]+$/,"");return _claudeSessionsCache.filter((u=>(0,external_path_.resolve)((0,external_path_.normalize)(u.projectPath)).replace(/[\\/]+$/,"").startsWith(s)))}const l=[],r=E?(0,external_path_.resolve)((0,external_path_.normalize)(E)).replace(/[\\/]+$/,""):null;try{const s=(0,external_path_.join)((0,external_os_.homedir)(),".claude","projects");if(!(0,external_fs_.existsSync)(s))return l;for(const u of(0,external_fs_.readdirSync)(s,{withFileTypes:!0})){if(!u.isDirectory())continue;const d=(0,external_path_.join)(s,u.name);try{for(const o of(0,external_fs_.readdirSync)(d)){if(!o.endsWith(".jsonl"))continue;const i=o.replace(".jsonl",""),a=(0,external_path_.join)(d,o);try{const t=(0,external_fs_.readFileSync)(a,"utf8").split(`
|
|
388
388
|
`).filter(Boolean);let n=null,f=null,h=0,c="";for(const m of t)try{const b=JSON.parse(m);if(n||(n=b),f=b,h++,!c&&b.type==="human"||b.type==="user"&&b.message?.content){const _=b.message.content;if(typeof _=="string")c=_.slice(0,120);else if(Array.isArray(_)){const g=_.find((w=>w.type==="text"));g&&(c=g.text?.slice(0,120)||"")}}}catch{}let v=u.name;if(process.platform==="win32"&&/^[A-Za-z]--/.test(v)?v=v[0]+":\\"+v.slice(3).replace(/-/g,"\\"):v.startsWith("-")&&(v=v.replace(/-/g,"/")),r&&!(0,external_path_.resolve)((0,external_path_.normalize)(v)).replace(/[\\/]+$/,"").startsWith(r))continue;l.push({sessionId:i,projectDir:u.name,projectPath:v,messageCount:h,summary:c,createdAt:n?.timestamp||null,updatedAt:f?.timestamp||null})}catch{}}}catch{}}}catch{}l.sort(((s,u)=>new Date(u.updatedAt||0).getTime()-new Date(s.updatedAt||0).getTime()));const e=l.slice(0,50);return _claudeSessionsCache=e,_claudeSessionsCacheTime=Date.now(),e.slice(0,20)}const DEFAULT_SHELL=process.platform==="win32"?process.env.SHELL||"powershell.exe":process.env.SHELL||"/bin/bash",sessions_CONFIG_DIR=(0,external_path_.join)((0,external_os_.homedir)(),".myhi"),SESSIONS_FILE=(0,external_path_.join)(sessions_CONFIG_DIR,"sessions.json"),savePath=null;function loadSavedConfigs(){let E;try{E=(0,external_fs_.readFileSync)(SESSIONS_FILE,"utf8")}catch{return[]}try{const p=JSON.parse(E);if(!Array.isArray(p))throw new Error("\u5185\u5BB9\u4E0D\u662F\u6570\u7EC4");return p}catch{const p=new Date().toISOString().replace(/[:.]/g,"-");try{(0,external_fs_.copyFileSync)(SESSIONS_FILE,SESSIONS_FILE+".corrupt-"+p)}catch{}console.error(`[sessions] \u26A0 sessions.json \u635F\u574F\uFF08\u65AD\u7535\u6495\u88C2\u5199/\u88AB\u6539\u574F\uFF09\uFF0C\u5DF2\u7559\u8BC1 sessions.json.corrupt-${p}`);try{const l=JSON.parse((0,external_fs_.readFileSync)(SESSIONS_FILE+".bak","utf8"));if(Array.isArray(l)&&l.length)return console.log(`[sessions] \u2714 \u5DF2\u4ECE .bak \u6062\u590D ${l.length} \u6761\u4F1A\u8BDD\u914D\u7F6E`),l}catch{}return console.error("[sessions] \u26A0 \u65E0 .bak \u53EF\u6062\u590D\uFF0C\u672C\u6B21\u542F\u52A8\u4F1A\u8BDD\u5217\u8868\u4E3A\u7A7A\uFF08\u78C1\u76D8\u8BC1\u636E\u5DF2\u7559\u6863\uFF09"),[]}}function writeSavedConfigs(E){try{(0,external_fs_.mkdirSync)(sessions_CONFIG_DIR,{recursive:!0});try{const l=(0,external_fs_.readFileSync)(SESSIONS_FILE,"utf8").trim();(!E||!E.length)&&l&&l!=="[]"&&console.error(`[sessions] \u26A0 \u5373\u5C06\u7528\u7A7A\u5217\u8868\u8986\u76D6 ${l.length} \u5B57\u8282\u7684\u5B58\u91CF\u4F1A\u8BDD\u6570\u636E\uFF08\u82E5\u975E\u672C\u4EBA\u6E05\u7A7A\u4F1A\u8BDD\uFF0C\u7ACB\u5373\u505C\u6B62 myhi \u5E76\u627E\u56DE .bak\uFF09`)}catch{}try{(0,external_fs_.copyFileSync)(SESSIONS_FILE,SESSIONS_FILE+".bak")}catch{}const p=SESSIONS_FILE+".tmp";(0,external_fs_.writeFileSync)(p,JSON.stringify(E,null,2),{mode:384});try{(0,external_fs_.renameSync)(p,SESSIONS_FILE)}catch{(0,external_fs_.copyFileSync)(p,SESSIONS_FILE);try{(0,external_fs_.rmSync)(p,{force:!0})}catch{}}}catch(p){console.error("[sessions] \u6301\u4E45\u5316\u5199\u5165\u5931\u8D25:",p.message)}}class Session extends external_events_.EventEmitter{constructor(p,l={}){super(),this.id=p,this.createdAt=l.createdAt||new Date().toISOString(),this.title=l.title||DEFAULT_SHELL,this.cwd=l.cwd||process.env.MYHI_CWD||process.cwd(),this.tags=Array.isArray(l.tags)?l.tags:[],this.initCmd=l.initCmd||null,this.owner=l.owner||null,this.cols=l.cols||120,this.rows=l.rows||30,this._lastActivityAt=l.lastActivityAt||0,this._viewers=new Set,this._viewerNames=new Map,this.controlHolder=null,this.controlHolderName=null,this.lastInputTime=null;const r={...process.env};if(delete r.CLAUDECODE,delete r.CLAUDE_CODE_ENTRYPOINT,delete r.CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC,l.userDir){const s=(0,external_path_.join)(l.userDir,".gitconfig");(0,external_fs_.existsSync)(s)&&(r.GIT_CONFIG_GLOBAL=s)}this._pty=external_node_pty_namespaceObject.spawn(DEFAULT_SHELL,[],{name:"xterm-256color",cols:this.cols,rows:this.rows,cwd:this.cwd,env:r,useConpty:!1}),this._scrollback="";const e=100*1024;if(this._pty.onData((s=>{this.emit("data",s),this._scrollback+=s,this._scrollback.length>e&&(this._scrollback=this._scrollback.slice(this._scrollback.length-e))})),this._pty.onExit((({exitCode:s})=>{this.exitCode=s,this.emit("exit",s)})),l.initCmd){const s=l.initCmd;let u=!1;const d=o=>{!u&&/[$>#\]]\s*$/.test(o.trimEnd())&&(u=!0,this._pty.off("data",d),setTimeout((()=>this._pty.write(s+"\r")),120))};this._pty.onData(d),setTimeout((()=>{u||(u=!0,this._pty.write(s+"\r"))}),3e3)}}write(p){p!=null&&(this._lastActivityAt=Date.now(),this._pty.write(p))}resize(p,l){this.cols=p,this.rows=l,this._pty.resize(p,l)}kill(){try{this._pty.kill()}catch{}}addViewer(p,l){this._viewers.add(p),l&&this._viewerNames.set(p,l)}removeViewer(p){this._viewers.delete(p),this._viewerNames.delete(p)}get viewerCount(){return new Set(this._viewerNames.values()).size||this._viewers.size}takeControl(p,l){this.controlHolder=p,this.controlHolderName=l||null,this.lastInputTime=Date.now()}releaseControl(){this.controlHolder=null,this.controlHolderName=null,this.lastInputTime=null}isController(p){return this.controlHolder===p}toJSON(){return{id:this.id,title:this.title,cwd:this.cwd,tags:this.tags,createdAt:this.createdAt,lastActivityAt:this._lastActivityAt||null,cols:this.cols,rows:this.rows,viewers:this.viewerCount,alive:this.exitCode===void 0,controlHolder:this.controlHolder,controlHolderName:this.controlHolderName,owner:this.owner}}}class SessionManager{constructor(){this._sessions=new Map,this._agentSessions=new Map;const p=loadSavedConfigs();let l=0;for(const r of p)try{if(r.mode==="agent"){const e=(0,external_crypto_.randomUUID)(),s=new AgentSession(e,{...r,resumeSessionId:r.claudeSessionId});s.on("session-id",(()=>this._persist())),this._agentSessions.set(e,s),l++,console.log(r.claudeSessionId?`[\u4F1A\u8BDD] \u6062\u590D Agent "${r.title}" (claude: ${r.claudeSessionId.slice(0,8)}...)`:`[\u4F1A\u8BDD] \u6062\u590D Agent "${r.title}" (\u65B0\u4E0A\u4E0B\u6587\u2014\u2014\u6B64\u524D\u5DF2\u6E05\u9664/\u672A\u53D1\u8D77\u8FC7\u67E5\u8BE2)`)}else this._spawn((0,external_crypto_.randomUUID)(),r,!1),l++}catch(e){console.warn(`[\u4F1A\u8BDD] \u6062\u590D "${r.title}" \u5931\u8D25:`,e.message)}p.length&&!l&&console.error(`[\u4F1A\u8BDD] \u26A0 \u78C1\u76D8\u4E0A\u6709 ${p.length} \u6761\u4F1A\u8BDD\u914D\u7F6E\u4F46\u4E00\u6761\u90FD\u6CA1\u6062\u590D\u6210\u529F \u2014\u2014 \u68C0\u67E5\u4E0A\u65B9\u65E5\u5FD7\uFF1Bsessions.json.bak \u91CC\u6709\u4E00\u4EFD\u597D\u6570\u636E`)}_spawn(p,l,r=!0){const e=new Session(p,l);return this._sessions.set(p,e),e.on("exit",(()=>{setTimeout((()=>this._sessions.delete(p)),300*1e3)})),r&&this._persist(),e}create(p={}){return this._spawn((0,external_crypto_.randomUUID)(),p,!0)}createAgent(p={}){const l=(0,external_crypto_.randomUUID)(),r=new AgentSession(l,p);return this._agentSessions.set(l,r),r.on("session-id",(()=>this._persist())),this._persist(),r}get(p){return this._sessions.get(p)||this._agentSessions.get(p)}list(p,l=!1){let r=[...this._sessions.values()].map((s=>({...s.toJSON(),mode:"pty"}))),e=[...this._agentSessions.values()].map((s=>s.toJSON()));return p&&!l&&(r=r.filter((s=>!s.owner||s.owner===p)),e=e.filter((s=>!s.owner||s.owner===p))),[...r,...e]}listSessions(){return[...this._sessions.values(),...this._agentSessions.values()]}kill(p){const l=this._sessions.get(p);if(l){l.kill(),this._sessions.delete(p),this._persist();return}const r=this._agentSessions.get(p);r&&(r.kill(),this._agentSessions.delete(p),this._persist())}persist(){this._persist()}_persist(){const p=[...this._sessions.values()].filter((r=>r.exitCode===void 0)).map((r=>({mode:"pty",title:r.title,cwd:r.cwd,tags:r.tags?.length?r.tags:void 0,createdAt:r.createdAt||void 0,lastActivityAt:r._lastActivityAt||void 0,initCmd:r.initCmd||void 0,owner:r.owner||void 0}))),l=[...this._agentSessions.values()].filter((r=>r.alive)).map((r=>({mode:"agent",title:r.title,firstUserText:r.firstUserText||void 0,cwd:r.cwd,tags:r.tags?.length?r.tags:void 0,createdAt:r.createdAt||void 0,lastActivityAt:r._lastActivityAt||void 0,owner:r.owner||void 0,userDir:r.userDir||void 0,claudeSessionId:r.claudeSessionId||void 0})));writeSavedConfigs([...p,...l])}}const roles_CONFIG_DIR=(0,external_path_.join)((0,external_os_.homedir)(),".myhi"),ROLES_FILE=(0,external_path_.join)(roles_CONFIG_DIR,"roles.json"),USERS_FILE=(0,external_path_.join)(roles_CONFIG_DIR,"users.json"),ROLE_LEVELS={admin:3,operator:2,viewer:1};let rolesConfig=null,usersConfig=null;function loadRoles(){try{if((0,external_fs_.existsSync)(ROLES_FILE))return rolesConfig=JSON.parse((0,external_fs_.readFileSync)(ROLES_FILE,"utf8")),rolesConfig}catch(E){console.warn("[\u89D2\u8272] \u52A0\u8F7D roles.json \u5931\u8D25:",E.message)}return rolesConfig=null,null}function lookupPassword(E){if(!rolesConfig?.passwords)return null;const p=rolesConfig.passwords[E];return p?{name:p.name||"\u7528\u6237",role:p.role||"viewer"}:null}function hasPermission(E,p){return(ROLE_LEVELS[E]||0)>=(ROLE_LEVELS[p]||0)}function isMultiUserMode(){return rolesConfig!==null&&rolesConfig.passwords&&Object.keys(rolesConfig.passwords).length>0}function getDefaultRole(){return rolesConfig?.defaultRole||"admin"}function loadUsers(){try{if((0,external_fs_.existsSync)(USERS_FILE))return usersConfig=JSON.parse((0,external_fs_.readFileSync)(USERS_FILE,"utf8")),usersConfig}catch(E){console.warn("[\u7528\u6237] \u52A0\u8F7D users.json \u5931\u8D25:",E.message)}return usersConfig=null,null}function hasUsers(){return usersConfig!==null&&usersConfig.users&&Object.keys(usersConfig.users).length>0}function isExclusiveMode(){return hasUsers()&&usersConfig.exclusive===!0}function setExclusiveMode(E){usersConfig||(usersConfig={users:{}}),usersConfig.exclusive=!!E,_writeUsers()}function lookupUser(E){if(!usersConfig?.users)return null;const p=usersConfig.users[E];return p?{name:p.name||"\u7528\u6237",dir:p.dir}:null}function listUsers(){return usersConfig?.users?Object.entries(usersConfig.users).map((([E,p])=>({password:E.slice(0,2)+"*".repeat(E.length-2),name:p.name,dir:p.dir}))):[]}function addUser(E,p,l){usersConfig||(usersConfig={users:{}}),usersConfig.users||(usersConfig.users={}),usersConfig.users[E]={name:p,dir:l},_writeUsers()}function removeUser(E){return!usersConfig?.users||!usersConfig.users[E]?!1:(delete usersConfig.users[E],_writeUsers(),!0)}function changeUserPassword(E,p,l){if(!usersConfig?.users||!Object.keys(usersConfig.users).length)return{ok:!1,error:"\u672A\u914D\u7F6E\u6210\u5458\u7528\u6237"};const r=usersConfig.users,e=r[E];if(!e||l&&e.name!==l)return{ok:!1,error:"\u539F\u5BC6\u7801\u4E0D\u6B63\u786E"};if(E===p)return{ok:!1,error:"\u65B0\u5BC6\u7801\u4E0D\u80FD\u4E0E\u539F\u5BC6\u7801\u76F8\u540C"};for(const[s,u]of Object.entries(r))if(s!==E&&s===p)return{ok:!1,error:`\u65B0\u5BC6\u7801\u4E0E\u7528\u6237\u300C${u.name||"\u672A\u547D\u540D"}\u300D\u7684\u5BC6\u7801\u91CD\u590D\uFF0C\u8BF7\u6362\u4E00\u4E2A`};return delete r[E],r[p]=e,_writeUsers(),{ok:!0}}function _writeUsers(){(0,external_fs_.mkdirSync)(roles_CONFIG_DIR,{recursive:!0}),(0,external_fs_.writeFileSync)(USERS_FILE,JSON.stringify(usersConfig,null,2),{mode:384})}const transcript_MAX_TEXT=8e3,MAX_INPUT_STR=16e3;function findTranscriptPath(E){if(!E)return null;const p=(0,external_path_.join)((0,external_os_.homedir)(),".claude","projects");if(!(0,external_fs_.existsSync)(p))return null;try{for(const l of(0,external_fs_.readdirSync)(p,{withFileTypes:!0})){if(!l.isDirectory())continue;const r=(0,external_path_.join)(p,l.name,E+".jsonl");if((0,external_fs_.existsSync)(r))return r}}catch{}return null}function clipText(E,p=transcript_MAX_TEXT){return E.length>p?E.slice(0,p)+`
|
|
389
389
|
\u2026[\u5DF2\u622A\u65AD\uFF0C\u5171 ${E.length} \u5B57\u7B26]`:E}function clipStrings(E,p=MAX_INPUT_STR){if(E==null||typeof E!="object")return typeof E=="string"&&E.length>p?clipText(E,p):E;if(Array.isArray(E))return E.map((r=>clipStrings(r,p)));const l={};for(const[r,e]of Object.entries(E))l[r]=clipStrings(e,p);return l}function parseTranscriptLine(E){let p;try{p=JSON.parse(E)}catch{return null}if(!p||p.isSidechain)return null;if(p.type==="human"||p.type==="user"){const l=p.message?.content;if(typeof l=="string")return l.trim()?{type:"user",content:clipText(l),timestamp:p.timestamp}:null;if(Array.isArray(l)){const r=l.find((s=>s.type==="tool_result"));if(r){let s="";return typeof r.content=="string"?s=r.content:Array.isArray(r.content)&&(s=r.content.map((u=>u?.type==="text"?u.text||"":u?.type==="image"?"[\u56FE\u7247\u7ED3\u679C]":"")).filter(Boolean).join(`
|
|
390
390
|
`)),{type:"tool_result",id:r.tool_use_id||"",text:clipText(s),timestamp:p.timestamp}}const e=l.find((s=>s.type==="text"))?.text||"";return e?{type:"user",content:clipText(e),timestamp:p.timestamp}:null}return null}if(p.type==="assistant"&&p.message?.content){const l=stripHeavyPayload(p.message);if(Array.isArray(l.content))for(const r of l.content)r?.type==="thinking"&&typeof r.thinking=="string"&&(r.thinking=clipText(r.thinking,4e3)),r?.type==="tool_use"&&r.input&&(r.input=clipStrings(r.input));return{type:"assistant",message:l,timestamp:p.timestamp}}return p.type==="result"?{type:"result",total_cost_usd:p.costUSD||p.total_cost_usd,timestamp:p.timestamp}:null}function readTranscript(E,{offset:p=0,limit:l=0}={}){const r=findTranscriptPath(E);if(!r)return{ok:!1,messages:[],total:0,hasMore:!1};let e;try{e=(0,external_fs_.readFileSync)(r,"utf8").split(`
|
|
391
|
-
`).filter(Boolean)}catch{return{ok:!1,messages:[],total:0,hasMore:!1}}const s=[];for(const i of e){const a=parseTranscriptLine(i);a&&s.push(a)}const u=s.length,d=Math.max(0,u-Math.max(0,p)),o=Math.max(0,l>0?d-l:0);return{ok:!0,messages:s.slice(o,d),total:u,hasMore:o>0}}const execAsync=(0,external_util_.promisify)(external_child_process_namespaceObject.exec),server_dirname=(0,external_path_.dirname)((0,external_url_.fileURLToPath)(import.meta.url));let PORT=parseInt(process.env.PORT,10)||12300;const PORT_EXPLICIT=!!process.env.PORT,server_configDir=(0,external_path_.join)((0,external_os_.homedir)(),".myhi");(0,external_fs_.mkdirSync)(server_configDir,{recursive:!0});let httpsServer=null,httpServer=null;function getOrCreateCert(){const E=(0,external_path_.join)(server_configDir,"cert.pem"),p=(0,external_path_.join)(server_configDir,"key.pem");if((0,external_fs_.existsSync)(E)&&(0,external_fs_.existsSync)(p))try{return{cert:(0,external_fs_.readFileSync)(E),key:(0,external_fs_.readFileSync)(p)}}catch{}try{const l=selfsigned.generate([{name:"commonName",value:"myhi"}],{days:3650,keySize:2048});return(0,external_fs_.writeFileSync)(E,l.cert,"utf8"),(0,external_fs_.writeFileSync)(p,l.private,"utf8"),console.log("[myhi] \u81EA\u7B7E\u540D HTTPS \u8BC1\u4E66\u5DF2\u751F\u6210"),{cert:l.cert,key:l.private}}catch{}try{return(0,external_child_process_namespaceObject.execSync)(`openssl req -x509 -newkey rsa:2048 -keyout "${p}" -out "${E}" -days 3650 -nodes -subj "/CN=myhi"`,{stdio:"pipe",timeout:1e4}),{cert:(0,external_fs_.readFileSync)(E),key:(0,external_fs_.readFileSync)(p)}}catch(l){return console.warn(`[myhi] \u751F\u6210 HTTPS \u8BC1\u4E66\u5931\u8D25: ${l.message?.split(`
|
|
392
|
-
`)[0]||l}`),null}}const pidFile=(0,external_path_.join)(server_configDir,"server.pid"),daemonPidFile=(0,external_path_.join)(server_configDir,"daemon.pid"),IS_DAEMON_CHILD=!!process.env.MYHI_DAEMON;function killOldProcess(E,p){let l=!1;try{process.kill(E,0),l=!0}catch{}if(!l)return!0;console.log(`[myhi] \u68C0\u6D4B\u5230\u5DF2\u8FD0\u884C\u7684${p} (PID ${E})\uFF0C\u6B63\u5728\u505C\u6B62...`);try{process.platform==="win32"?(0,external_child_process_namespaceObject.execSync)(`taskkill /PID ${E} /F /T`,{stdio:"pipe",timeout:5e3}):process.kill(E,"SIGTERM")}catch{}const r=Date.now()+5e3;for(;Date.now()<r;){try{process.kill(E,0)}catch{return!0}const s=Date.now()+200;for(;Date.now()<s;);}let e=!1;try{process.kill(E,0),e=!0}catch{}return!e}(function E(){try{const p=parseInt((0,external_fs_.readFileSync)(daemonPidFile,"utf8").trim(),10);if(p&&p!==process.pid&&p!==parseInt(process.env.MYHI_DAEMON_PID||"0",10)){let l=!1;try{process.kill(p,0),l=!0}catch{}if(l&&!IS_DAEMON_CHILD){console.log("[myhi] \u68C0\u6D4B\u5230\u540E\u53F0 daemon \u5728\u8FD0\u884C\uFF0C\u5148\u505C\u6B62..."),killOldProcess(p,"daemon")||(console.error("[myhi] \u65E0\u6CD5\u505C\u6B62 daemon\uFF0C\u8BF7\u5148\u8FD0\u884C: myhi stop"),process.exit(1));try{(0,external_fs_.unlinkSync)(daemonPidFile)}catch{}}}}catch{}if(!IS_DAEMON_CHILD)try{const p=parseInt((0,external_fs_.readFileSync)(pidFile,"utf8").trim(),10);p&&p!==process.pid&&(killOldProcess(p,"\u5B9E\u4F8B")||(console.error(`[myhi] \u65E0\u6CD5\u505C\u6B62\u65E7\u8FDB\u7A0B (PID ${p})\uFF0C\u8BF7\u624B\u52A8\u6267\u884C\uFF1A`),console.error(" myhi stop"),console.error(` \u6216: ${process.platform==="win32"?`taskkill /PID ${p} /F`:`kill -9 ${p}`}`),process.exit(1)))}catch{}(0,external_fs_.writeFileSync)(pidFile,String(process.pid))})();function cleanupPid(){try{parseInt((0,external_fs_.readFileSync)(pidFile,"utf8").trim(),10)===process.pid&&(0,external_fs_.unlinkSync)(pidFile)}catch{}}function gracefulShutdown(){try{if(typeof manager<"u"){try{manager.persist()}catch{}for(const E of manager.listSessions())try{E.kill()}catch{}}}catch{}cleanupPid()}process.on("exit",cleanupPid),process.on("SIGTERM",(()=>{gracefulShutdown(),process.exit(0)})),process.on("SIGINT",(()=>{gracefulShutdown(),process.exit(0)}));const RECOVERABLE_ERRNO=new Set(["ECONNRESET","EPIPE","ECONNABORTED","ETIMEDOUT","ENOENT","EBUSY","EACCES","EPERM","EISDIR","ERR_STREAM_PREMATURE_CLOSE","ERR_STREAM_DESTROYED","ERR_HTTP_HEADERS_SENT"]);process.on("uncaughtException",(E=>{if(RECOVERABLE_ERRNO.has(E?.code)){console.error(`[myhi] \u5DF2\u5FFD\u7565\u53EF\u6062\u590D\u5F02\u5E38(${E.code}):`,E.message);return}console.error("[myhi] \u672A\u6355\u83B7\u5F02\u5E38:",E.message),gracefulShutdown(),process.exit(1)})),process.on("unhandledRejection",(E=>{console.error("[myhi] \u672A\u5904\u7406\u7684 Promise \u62D2\u7EDD:",E)}));function readOrCreate(E,p){try{if((0,external_fs_.existsSync)(E))return(0,external_fs_.readFileSync)(E,"utf8").trim()}catch{}const l=p();return(0,external_fs_.writeFileSync)(E,l,{mode:384}),l}const TOKEN=readOrCreate((0,external_path_.join)(server_configDir,"token"),(()=>(0,external_crypto_.randomBytes)(16).toString("hex")));let PASSWORD=readOrCreate((0,external_path_.join)(server_configDir,"password"),(()=>String(Math.floor(1e3+Math.random()*9e3))));loadRoles(),loadUsers();let activeUser=null;const userSessions=new Map;userSessions.set(TOKEN,{role:"admin",name:"\u7BA1\u7406\u5458",permanent:!0});const SESSION_MAX_AGE=10080*60*1e3;setInterval((()=>{const E=Date.now();for(const[p,l]of userSessions)l.permanent||(!l.lastUsed||E-l.lastUsed>SESSION_MAX_AGE)&&userSessions.delete(p)}),3600*1e3);function getTailscaleIP(){try{return(0,external_child_process_namespaceObject.execSync)("tailscale ip -4",{timeout:3e3,stdio:"pipe"}).toString().trim().split(`
|
|
393
|
-
`)[0]}catch{}try{if(process.platform==="win32"){const E=(0,external_child_process_namespaceObject.execSync)("route print 0.0.0.0",{timeout:3e3,stdio:"pipe"}).toString().match(/0\.0\.0\.0\s+0\.0\.0\.0\s+\S+\s+(\d+\.\d+\.\d+\.\d+)/);if(E)return E[1]}else try{const E=(0,external_child_process_namespaceObject.execSync)("ip route get 1.1.1.1",{timeout:3e3,stdio:"pipe"}).toString().match(/src\s+(\d+\.\d+\.\d+\.\d+)/);if(E)return E[1]}catch{const E=(0,external_child_process_namespaceObject.execSync)("route -n get default 2>/dev/null || route -n 2>/dev/null",{timeout:3e3,stdio:"pipe"}).toString(),p=E.match(/interface:\s*(\S+)/i)||E.match(/0\.0\.0\.0\s+(\d+\.\d+\.\d+\.\d+)/);if(p){const l=(0,external_os_.networkInterfaces)()[p[1]];if(l){const r=l.find((e=>e.family==="IPv4"&&!e.internal));if(r)return r.address}if(/^\d+\.\d+\.\d+\.\d+$/.test(p[1]))return p[1]}}}catch{}for(const E of Object.values((0,external_os_.networkInterfaces)()))for(const p of E)if(p.family==="IPv4"&&!p.internal)return p.address;return"localhost"}function parseCookies(E=""){const p={};for(const l of E.split(";")){const r=l.indexOf("=");r>0&&(p[l.slice(0,r).trim()]=l.slice(r+1).trim())}return p}const SESSION_COOKIE="myhi_sid",COOKIE_OPTS_BASE="HttpOnly; SameSite=Strict; Path=/; Max-Age=31536000";function cookieOpts(E){return E?.secure||E?.headers?.["x-forwarded-proto"]==="https"?COOKIE_OPTS_BASE+"; Secure":COOKIE_OPTS_BASE}function setSessionCookie(E,p,l){E.setHeader("Set-Cookie",`${SESSION_COOKIE}=${p||TOKEN}; ${cookieOpts(l)}`)}function getSessionToken(E){return parseCookies(E)[SESSION_COOKIE]||null}function hasValidSession(E){const p=getSessionToken(E);if(p&&userSessions.has(p)){const l=userSessions.get(p);return l&&(l.lastUsed=Date.now()),!0}return!1}function getUserInfo(E){const p=getSessionToken(E);return p?userSessions.get(p):null}const app=express();app.set("trust proxy",!0),app.use(((E,p,l)=>{p.setHeader("Referrer-Policy","no-referrer"),p.setHeader("X-Content-Type-Options","nosniff"),l()}));function _readConfig(){try{return JSON.parse((0,external_fs_.readFileSync)((0,external_path_.join)(server_configDir,"config.json"),"utf8").replace(/^\uFEFF/,""))||{}}catch{return{}}}function _configString(E){const p=_readConfig()[E];return typeof p=="string"&&p?p:null}const HOST=process.env.HOST||_configString("host")||"0.0.0.0",_tlsOpts=process.env.MYHI_NO_HTTPS||_readConfig().httpOnly?null:getOrCreateCert();_tlsOpts?(httpsServer=(0,external_https_.createServer)(_tlsOpts,app),httpServer=(0,external_http_.createServer)(((E,p)=>{const l=(E.headers.host||"").replace(/:\d+$/,"");p.writeHead(301,{Location:`https://${l}:${PORT}${E.url}`}),p.end()}))):(console.warn("[myhi] TLS \u4E0D\u53EF\u7528\uFF0C\u4EE5 HTTP \u6A21\u5F0F\u8FD0\u884C\uFF08\u9EA6\u514B\u98CE/\u6444\u50CF\u5934\u7B49\u6D4F\u89C8\u5668\u5B89\u5168\u529F\u80FD\u53D7\u9650\uFF09"),httpServer=(0,external_http_.createServer)(app));for(const E of[httpsServer,httpServer])E&&(E.keepAliveTimeout=72e3,E.headersTimeout=76e3);const _corsAllowed=/^https?:\/\/(localhost|127\.0\.0\.1|\[::1\]|10\.\d+\.\d+\.\d+|172\.(1[6-9]|2\d|3[01])\.\d+\.\d+|192\.168\.\d+\.\d+|100\.(6[4-9]|[7-9]\d|1[01]\d|12[0-7])\.\d+\.\d+)(:\d+)?$/,io=new Server({serveClient:!1,cors:{origin:(E,p)=>{if(!E||_corsAllowed.test(E))return p(null,!0);p(new Error("CORS \u4E0D\u5141\u8BB8\u6B64\u6765\u6E90"),!1)},credentials:!0},transports:["websocket","polling"],pingTimeout:6e4,pingInterval:25e3,upgradeTimeout:3e4,maxHttpBufferSize:5e6});httpsServer?io.attach(httpsServer):io.attach(httpServer);const manager=new SessionManager,AUTO_ATTACH=process.env.MYHI_AUTO_ATTACH!=="0",_autoSpawned=new Set;function spawnLocalTerminal(E,p){if(!AUTO_ATTACH||_autoSpawned.has(E)||!process.env.DISPLAY&&process.platform==="linux")return;_autoSpawned.add(E);const l=__nccwpck_require__.ab+"attach.js",r=process.execPath,e=p||"myhi";if(process.platform==="win32"){const s=`"${r}" "${l}" ${E}`,u=(0,external_child_process_namespaceObject.spawn)("wt.exe",["-w","0","new-tab","--title",e,"--","cmd","/k",s],{detached:!0,stdio:"ignore"});u.unref(),u.on("error",(d=>{console.warn("[attach] wt.exe \u5931\u8D25\uFF0C\u56DE\u9000\u5230 cmd:",d.message),(0,external_child_process_namespaceObject.spawn)("cmd.exe",["/c","start",`"${e}"`,"cmd","/k",s],{detached:!0,stdio:"ignore"}).unref()}))}else if(process.platform==="darwin"){const s=`tell application "Terminal" to do script "${r} ${l} ${E}"`;(0,external_child_process_namespaceObject.spawn)("osascript",["-e",s],{detached:!0,stdio:"ignore"}).unref()}else{const s=`${r} "${l}" ${E}`;for(const[u,d]of[["gnome-terminal",["--","bash","-c",`${s}; exec bash`]],["xterm",["-title",e,"-e",s]],["konsole",["--new-tab","-e",s]]]){const o=(0,external_child_process_namespaceObject.spawn)(u,d,{detached:!0,stdio:"ignore"});o.unref(),o.on("error",(()=>{}));break}}}const uploadDir=(0,external_path_.join)(server_configDir,"uploads");(0,external_fs_.mkdirSync)(uploadDir,{recursive:!0});function checkAuth(E,p,l){const r=E.query.token;if(r&&(r===TOKEN||userSessions.has(r))){const e=userSessions.get(r);if(e?.onetime){userSessions.delete(r);const d=(0,external_crypto_.randomBytes)(16).toString("hex");userSessions.set(d,{role:e.role,name:e.name,lastUsed:Date.now()}),setSessionCookie(p,d,E)}else setSessionCookie(p,r===TOKEN?TOKEN:r,E);const s=Object.keys(E.query).filter((d=>d!=="token")),u=E.path+(s.length?"?"+new URLSearchParams(Object.fromEntries(Object.entries(E.query).filter((([d])=>d!=="token")))):"");return p.redirect(u)}if(hasValidSession(E.headers.cookie)){if(isExclusiveMode()&&activeUser){const e=getSessionToken(E.headers.cookie);if(e!==TOKEN){const s=userSessions.get(e);if(!s||s.name!==activeUser.name)return p.setHeader("Set-Cookie",`${SESSION_COOKIE}=; HttpOnly; SameSite=Strict; Path=/; Max-Age=0`),p.redirect("/login")}}return l()}p.redirect("/login")}const _require=(0,external_module_namespaceObject.createRequire)(import.meta.url);function pkgDirSafe(E){try{return(0,external_path_.dirname)(_require.resolve(`${E}/package.json`))}catch{return null}}const libDir=(0,external_path_.join)(server_dirname,"lib"),staticOpts={maxAge:864e5};app.use("/lib/xterm",express.static(pkgDirSafe("xterm")||(0,external_path_.join)(libDir,"xterm"),staticOpts)),app.use("/lib/xterm-fit",express.static(pkgDirSafe("xterm-addon-fit")||(0,external_path_.join)(libDir,"xterm-fit"),staticOpts)),app.use("/lib/xterm-links",express.static(pkgDirSafe("xterm-addon-web-links")||(0,external_path_.join)(libDir,"xterm-links"),staticOpts)),app.use("/sio-client",express.static((0,external_path_.join)(server_dirname,"client-dist"),{maxAge:"1h",index:!1,setHeaders:(E,p)=>{p.endsWith(".js")&&E.setHeader("Content-Type","application/javascript; charset=utf-8")}})),app.get("/manifest.json",((E,p)=>p.sendFile((0,external_path_.join)(server_dirname,"manifest.json")))),app.get("/icon.png",((E,p)=>p.sendFile((0,external_path_.join)(server_dirname,"icon.png")))),app.get("/icon.svg",((E,p)=>p.sendFile((0,external_path_.join)(server_dirname,"icon.svg"))));const _loginAttempts=new Map,LOGIN_MAX_ATTEMPTS=5,LOGIN_LOCKOUT_MS=300*1e3;function checkLoginRate(E){const p=Date.now(),l=_loginAttempts.get(E);return!l||p>l.resetAt?!0:l.count<LOGIN_MAX_ATTEMPTS}function recordLoginFailure(E){const p=Date.now(),l=_loginAttempts.get(E);!l||p>l.resetAt?_loginAttempts.set(E,{count:1,resetAt:p+LOGIN_LOCKOUT_MS}):l.count++}function clearLoginFailures(E){_loginAttempts.delete(E)}setInterval((()=>{const E=Date.now();for(const[p,l]of _loginAttempts)E>l.resetAt&&_loginAttempts.delete(p)}),600*1e3),app.get("/login",((E,p)=>{p.sendFile((0,external_path_.join)(server_dirname,"login.html"))})),app.post("/login",express.urlencoded({extended:!1}),((E,p)=>{const l=E.ip;if(!checkLoginRate(l))return p.redirect("/login?error=locked");loadRoles(),loadUsers();const r=E.body?.password,e=E.query.from,s=e&&e.startsWith("/")&&!e.startsWith("//")?e:"/";if(hasUsers()){const d=lookupUser(r);if(!d)return recordLoginFailure(l),p.redirect("/login?error=1");if(isExclusiveMode()&&activeUser&&activeUser.name!==d.name)return p.redirect("/login?error=occupied&user="+encodeURIComponent(activeUser.name));clearLoginFailures(l);const o=(0,external_crypto_.randomBytes)(16).toString("hex");return userSessions.set(o,{role:"operator",name:d.name,dir:d.dir,lastUsed:Date.now()}),isExclusiveMode()&&(activeUser?activeUser.tokens.add(o):activeUser={name:d.name,dir:d.dir,tokens:new Set([o]),loginAt:Date.now()}),setSessionCookie(p,o,E),console.log(`[\u767B\u5F55] ${d.name} \u767B\u5F55\uFF08${isExclusiveMode()?"\u72EC\u5360":"\u5171\u4EAB"}\u6A21\u5F0F\uFF09`),p.redirect(s)}const u=lookupPassword(r);if(u){clearLoginFailures(l);const d=(0,external_crypto_.randomBytes)(16).toString("hex");return userSessions.set(d,{role:u.role,name:u.name,lastUsed:Date.now()}),setSessionCookie(p,d,E),p.redirect(s)}if(r===PASSWORD){clearLoginFailures(l);const d=(0,external_crypto_.randomBytes)(16).toString("hex");return userSessions.set(d,{role:"admin",name:"\u7BA1\u7406\u5458",lastUsed:Date.now()}),setSessionCookie(p,d,E),p.redirect(s)}recordLoginFailure(l),p.redirect("/login?error=1")})),app.post("/logout",((E,p)=>{const l=getSessionToken(E.headers.cookie);l&&(activeUser&&activeUser.tokens.has(l)&&(activeUser.tokens.delete(l),activeUser.tokens.size===0?(console.log(`[\u9000\u51FA] ${activeUser.name} \u5168\u90E8\u8BBE\u5907\u9000\u51FA\uFF0C\u91CA\u653E\u767B\u5F55\u72B6\u6001\uFF08\u4F1A\u8BDD\u4FDD\u7559\uFF09`),activeUser=null):console.log(`[\u9000\u51FA] ${activeUser.name} \u4E00\u4E2A\u8BBE\u5907\u9000\u51FA\uFF08\u5269\u4F59 ${activeUser.tokens.size} \u4E2A\uFF09`)),userSessions.delete(l)),p.setHeader("Set-Cookie",`${SESSION_COOKIE}=; HttpOnly; SameSite=Strict; Path=/; Max-Age=0`),p.redirect("/login")})),app.get("/api/status",((E,p)=>{if(isExclusiveMode()&&activeUser)return p.json({occupied:!0,userName:activeUser.name,releasing:!!_exclusiveReleaseTimer});if(hasUsers()&&!isExclusiveMode()){const l=new Set;for(const[,r]of io.sockets.sockets){if(!r.data.userName||r.data.userName==="\u7BA1\u7406\u5458")continue;const e=r.handshake.headers.cookie;e&&!hasValidSession(e)||l.add(r.data.userName)}return p.json({occupied:!1,onlineUsers:[...l]})}p.json({occupied:!1})})),app.get("/admin",((E,p)=>p.sendFile((0,external_path_.join)(server_dirname,"admin.html")))),app.get("/",checkAuth,((E,p)=>p.sendFile((0,external_path_.join)(server_dirname,"index.html")))),app.get("/terminal/:id",checkAuth,((E,p)=>p.sendFile((0,external_path_.join)(server_dirname,"chat.html")))),app.get("/grid",checkAuth,((E,p)=>p.sendFile((0,external_path_.join)(server_dirname,"grid.html")))),app.get("/terminal-raw/:id",checkAuth,((E,p)=>p.sendFile((0,external_path_.join)(server_dirname,"terminal.html")))),app.use("/lib",express.static((0,external_path_.join)(server_dirname,"lib"),staticOpts)),app.get("/api/sessions",checkAuth,((E,p)=>{const l=getUserInfo(E.headers.cookie);p.json(manager.list(l?.name,l?.role==="admin"))})),app.get("/api/usage",((E,p)=>{try{const l=manager.listSessions().filter((e=>e.mode==="agent"&&e.alive)).map((e=>({id:e.id,title:e.title,owner:e.owner,usage:e._usage})));let r=null;for(const e of l)if(e.usage?.rateLimitInfo){r=e.usage.rateLimitInfo;break}p.json({rateLimit:r,activeSessions:l})}catch(l){p.status(500).json({error:l.message})}})),app.post("/api/sessions",checkAuth,express.json(),((E,p)=>{const l=getUserInfo(E.headers.cookie),r=manager.create({...E.body,owner:l?.name,userDir:l?.dir});p.status(201).json(r.toJSON())})),app.delete("/api/sessions/:id",checkAuth,((E,p)=>{manager.kill(E.params.id),p.status(204).end()})),app.get("/api/sessions/health",checkAuth,((E,p)=>{const l=getUserInfo(E.headers.cookie),r=manager.listSessions().filter((e=>e.mode==="agent")).filter((e=>!e.owner||!l?.name||e.owner===l.name)).map((e=>({...e.toJSON(),health:getSessionHealth(e.claudeSessionId)})));p.json({ok:!0,sessions:r})})),app.post("/api/sessions/:id/reset",checkAuth,((E,p)=>{const l=manager.get(E.params.id);if(!l||l.mode!=="agent"||typeof l.reset!="function")return p.status(404).json({ok:!1,error:"agent \u4F1A\u8BDD\u4E0D\u5B58\u5728"});l.reset(),l.emit("agent:message",{type:"system",subtype:"info",message:"\u5DF2\u91CD\u5F00\u4E3A\u5E72\u51C0\u4F1A\u8BDD\uFF0C\u4E0B\u6B21\u63D0\u95EE\u5F00\u542F\u5168\u65B0\u5BF9\u8BDD\uFF08\u65E7\u4E0A\u4E0B\u6587\u5DF2\u65AD\u5F00\uFF09"}),broadcastSessions(),p.json({ok:!0})})),app.get("/api/claude-sessions",checkAuth,((E,p)=>{const l=getUserInfo(E.headers.cookie);p.json(listLocalClaudeSessions(l?.dir))})),app.get("/api/agent/history/:sessionId",checkAuth,((E,p)=>{const l=manager.get(E.params.sessionId);if(!l||l.mode!=="agent")return p.status(404).json({ok:!1,error:"\u4F1A\u8BDD\u4E0D\u5B58\u5728"});const r=l.claudeSessionId;if(!r)return p.json({ok:!0,messages:[],total:0,hasMore:!1});const e=Math.max(0,parseInt(E.query.offset,10)||0),s=Math.min(200,Math.max(1,parseInt(E.query.limit,10)||50)),u=readTranscript(r,{offset:e,limit:s});p.json({ok:!0,messages:u.messages,total:u.total,hasMore:u.hasMore})})),app.get("/api/dirs",checkAuth,((E,p)=>{const l=getUserInfo(E.headers.cookie);if(!l||l.role!=="admin")return p.status(403).json({error:"\u6CA1\u6709\u76EE\u5F55\u6D4F\u89C8\u6743\u9650"});const r=l.dir?(0,external_path_.resolve)((0,external_path_.normalize)(l.dir)):null,e=process.platform==="win32"?"\\":"/";let s=E.query.path||r||process.env.MYHI_CWD||(0,external_os_.homedir)(),u=(0,external_path_.resolve)((0,external_path_.normalize)(s)).replace(/[/\\]+$/,"");process.platform==="win32"&&/^[A-Za-z]:$/.test(u)&&(u+="\\"),r&&!u.startsWith(r)&&(u=r);try{const d=(0,external_fs_.readdirSync)(u,{withFileTypes:!0}).filter((a=>a.isDirectory()&&a.name!==".git")).map((a=>({name:a.name,path:u.replace(/[/\\]+$/,"")+e+a.name}))).sort(((a,t)=>a.name.localeCompare(t.name))),o=(0,external_path_.join)(u,".."),i=!r||(0,external_path_.resolve)((0,external_path_.normalize)(o)).startsWith(r);p.json({current:u,parent:i&&o!==u?o:null,dirs:d,drives:process.platform==="win32"&&!r?"CDEFGHIJKLMNOPQRSTUVWXYZ".split("").map((a=>a+":\\")).filter((a=>{try{return(0,external_fs_.existsSync)(a)}catch{return!1}})):[]})}catch(d){p.status(400).json({error:d.message})}}));function safePath(E,p){const l=(0,external_path_.resolve)((0,external_path_.normalize)(E)),r=(0,external_path_.resolve)((0,external_path_.normalize)((0,external_path_.join)(E,p||"")));if(r!==l&&!r.startsWith(l+external_path_.sep))return null;try{const e=(0,external_fs_.realpathSync)(l),s=(0,external_fs_.existsSync)(r)?(0,external_fs_.realpathSync)(r):r;if(s!==e&&!s.startsWith(e+external_path_.sep))return null}catch{}return r}function walkFiles(E,p,l=[],r=0,e=[],s=!1,u=1/0){if(r>20||l.length>=u)return l;let d;try{d=(0,external_fs_.readdirSync)(E,{withFileTypes:!0})}catch{return l}for(const o of d){if(o.name===".git"||e.includes(o.name))continue;if(l.length>=u)return l;const i=(0,external_path_.join)(E,o.name),a=p?p+"/"+o.name:o.name;try{const t=(0,external_fs_.lstatSync)(i);if(t.isSymbolicLink())continue;t.isDirectory()?(s&&l.push({name:a,dir:!0,mtime:t.mtimeMs}),walkFiles(i,a,l,r+1,e,s,u)):t.isFile()&&l.push({name:a,size:t.size,mtime:t.mtimeMs})}catch{}}return l}function fileHash(E){return new Promise(((p,l)=>{const r=(0,external_crypto_.createHash)("sha256"),e=(0,external_fs_.createReadStream)(E);e.on("data",(s=>r.update(s))),e.on("end",(()=>p(r.digest("hex")))),e.on("error",l)}))}app.get("/api/files/:sessionId",checkAuth,((E,p)=>{const l=manager.get(E.params.sessionId);if(!l)return p.status(404).json({error:"\u4F1A\u8BDD\u4E0D\u5B58\u5728"});const r=E.query.path||"",e=safePath(l.cwd,r);if(!e)return p.status(403).json({error:"\u8DEF\u5F84\u8D8A\u754C"});try{if(!(0,external_fs_.statSync)(e).isDirectory())return p.status(400).json({error:"\u4E0D\u662F\u76EE\u5F55"});const s=(0,external_fs_.readdirSync)(e,{withFileTypes:!0}).filter((o=>o.name!==".git")).map((o=>{const i=(0,external_path_.join)(e,o.name);try{const a=(0,external_fs_.lstatSync)(i);return a.isSymbolicLink()?null:{name:o.name,isDir:a.isDirectory(),size:a.isFile()?a.size:void 0,mtime:a.mtimeMs}}catch{return null}})).filter(Boolean).sort(((o,i)=>i.isDir-o.isDir||o.name.localeCompare(i.name))),u=r?r.replace(/\\/g,"/"):"",d=u.includes("/")?u.slice(0,u.lastIndexOf("/")):u?"":null;p.json({ok:!0,cwd:l.cwd,path:u,parent:d,entries:s})}catch(s){p.status(500).json({error:s.message})}})),app.get("/api/files/:sessionId/search",checkAuth,((E,p)=>{const l=manager.get(E.params.sessionId);if(!l)return p.status(404).json({error:"\u4F1A\u8BDD\u4E0D\u5B58\u5728"});const r=String(E.query.q||"").trim().toLowerCase();try{let e=walkFiles(l.cwd,"",[],0,["node_modules"],!0).map((u=>({name:u.name,dir:!!u.dir,mtime:u.mtime})));r&&(e=e.filter((u=>u.name.toLowerCase().includes(r))));const s=u=>{if(!r)return 0;const d=u.name.slice(u.name.lastIndexOf("/")+1).toLowerCase();return d.startsWith(r)?0:d.includes(r)?1:2};e.sort(((u,d)=>s(u)-s(d)||(d.mtime||0)-(u.mtime||0))),p.json({ok:!0,files:e.slice(0,50).map((u=>({name:u.name,dir:!!u.dir})))})}catch(e){p.status(500).json({error:e.message})}})),app.get("/api/files/:sessionId/at-ls",checkAuth,((E,p)=>{const l=manager.get(E.params.sessionId);if(!l)return p.status(404).json({error:"\u4F1A\u8BDD\u4E0D\u5B58\u5728"});const r=process.platform==="win32",e=String(E.query.dir||"").replace(/\\/g,"/"),s=String(E.query.q||"").toLowerCase(),u=o=>o.replace(/\\/g,"/");if(r&&e==="/"){const o="CDEFGHIJKLMNOPQRSTUVWXYZ".split("").map((i=>i+":")).filter((i=>{try{return(0,external_fs_.existsSync)(i+"\\")}catch{return!1}})).filter((i=>!s||i.toLowerCase().startsWith(s))).map((i=>({label:i,dir:!0})));return p.json({ok:!0,win:r,abs:"/",rel:!1,parent:null,entries:o})}let d;e?e==="~"||e.startsWith("~/")?d=(0,external_path_.join)((0,external_os_.homedir)(),e.slice(1)):r&&/^[A-Za-z]:/.test(e)?d=e.length===2?e+"/":e:e.startsWith("/")?d=e:d=(0,external_path_.join)(l.cwd,e):d=l.cwd,d=(0,external_path_.resolve)((0,external_path_.normalize)(d));try{const o=(0,external_fs_.readdirSync)(d,{withFileTypes:!0}).filter((n=>n.name!==".git")).map((n=>{try{const f=(0,external_fs_.lstatSync)((0,external_path_.join)(d,n.name));return f.isSymbolicLink()||!(f.isDirectory()||f.isFile())?null:{label:n.name,dir:f.isDirectory()}}catch{return null}})).filter(Boolean),i=n=>s?n.label.toLowerCase().startsWith(s)?0:1:0,a=o.filter((n=>!s||n.label.toLowerCase().includes(s))).filter((n=>s.startsWith(".")||!n.label.startsWith("."))).sort(((n,f)=>i(n)-i(f)||f.dir-n.dir||n.label.localeCompare(f.label,"zh"))).slice(0,300),t=(0,external_path_.dirname)(d);p.json({ok:!0,win:r,abs:u(d),rel:!e||!/^([/~]|[A-Za-z]:)/.test(e),parent:t!==d?u(t):r?"/":null,entries:a})}catch(o){p.status(o.code==="ENOENT"||o.code==="ENOTDIR"?404:500).json({error:o.code==="ENOENT"?"\u76EE\u5F55\u4E0D\u5B58\u5728":o.code==="ENOTDIR"?"\u4E0D\u662F\u76EE\u5F55":o.message})}})),app.get("/api/files/:sessionId/download",checkAuth,((E,p)=>{const l=manager.get(E.params.sessionId);if(!l)return p.status(404).json({error:"\u4F1A\u8BDD\u4E0D\u5B58\u5728"});const r=E.query.path;if(!r)return p.status(400).json({error:"\u7F3A\u5C11 path \u53C2\u6570"});const e=safePath(l.cwd,r);if(!e)return p.status(403).json({error:"\u8DEF\u5F84\u8D8A\u754C"});let s;try{s=(0,external_fs_.statSync)(e)}catch{return p.status(404).json({error:"\u6587\u4EF6\u4E0D\u5B58\u5728"})}if(!s.isFile())return p.status(400).json({error:"\u4E0D\u662F\u6587\u4EF6"});const u=r.split("/").pop();p.setHeader("Content-Disposition",`attachment; filename*=UTF-8''${encodeURIComponent(u)}`),p.sendFile(e,{dotfiles:"allow",acceptRanges:!0},(d=>{if(!d)return;const o=d.code||"";["ECONNABORTED","EPIPE","ECONNRESET"].includes(o)||p.writableEnded||(console.warn("[files] \u4E0B\u8F7D\u5931\u8D25:",r,o||d.message),p.headersSent?p.destroy():p.status(500).json({error:"\u8BFB\u53D6\u6587\u4EF6\u5931\u8D25: "+(o||d.message)}))}))})),app.post("/api/files/:sessionId/upload",checkAuth,((E,p)=>{const l=manager.get(E.params.sessionId);if(!l)return p.status(404).json({error:"\u4F1A\u8BDD\u4E0D\u5B58\u5728"});const r=(E.query.path||"").replace(/\\/g,"/"),e=safePath(l.cwd,r);if(!e)return p.status(403).json({error:"\u8DEF\u5F84\u8D8A\u754C"});(0,external_fs_.mkdirSync)(e,{recursive:!0}),multer({storage:multer.diskStorage({destination:(s,u,d)=>{u.originalname=Buffer.from(u.originalname,"latin1").toString("utf8");const o=u.originalname.includes("/")?u.originalname.slice(0,u.originalname.lastIndexOf("/")):"",i=o?(0,external_path_.join)(e,o):e,a=(0,external_path_.resolve)((0,external_path_.normalize)(i)),t=(0,external_path_.resolve)((0,external_path_.normalize)(l.cwd));if(a!==t&&!a.startsWith(t+external_path_.sep))return d(new Error("\u8DEF\u5F84\u8D8A\u754C"));(0,external_fs_.mkdirSync)(i,{recursive:!0}),d(null,i)},filename:(s,u,d)=>{const o=u.originalname.includes("/")?u.originalname.slice(u.originalname.lastIndexOf("/")+1):u.originalname;d(null,o)}}),limits:{fileSize:500*1024*1024}}).array("files",100)(E,p,(s=>{if(s)return p.status(400).json({error:s.message});if(!E.files?.length)return p.status(400).json({error:"\u6CA1\u6709\u6587\u4EF6"});const u=E.files.map((d=>({name:d.originalname,size:d.size,path:(0,external_path_.resolve)(d.path).replace(/\\/g,"/")})));io.emit("files:changed",{sessionId:l.id}),p.json({ok:!0,files:u})}))})),app.delete("/api/files/:sessionId",checkAuth,((E,p)=>{const l=manager.get(E.params.sessionId);if(!l)return p.status(404).json({error:"\u4F1A\u8BDD\u4E0D\u5B58\u5728"});const r=E.query.path;if(!r)return p.status(400).json({error:"\u7F3A\u5C11 path \u53C2\u6570"});const e=safePath(l.cwd,r);if(!e)return p.status(403).json({error:"\u8DEF\u5F84\u8D8A\u754C"});if((0,external_path_.resolve)((0,external_path_.normalize)(e))===(0,external_path_.resolve)((0,external_path_.normalize)(l.cwd)))return p.status(403).json({error:"\u4E0D\u80FD\u5220\u9664\u4F1A\u8BDD\u6839\u76EE\u5F55"});try{(0,external_fs_.statSync)(e).isDirectory()?(0,external_fs_.rmSync)(e,{recursive:!0}):(0,external_fs_.unlinkSync)(e),io.emit("files:changed",{sessionId:l.id}),p.json({ok:!0})}catch(s){p.status(500).json({error:s.message})}})),app.get("/api/files/:sessionId/manifest",checkAuth,(async(E,p)=>{const l=manager.get(E.params.sessionId);if(!l)return p.status(404).json({error:"\u4F1A\u8BDD\u4E0D\u5B58\u5728"});const r=E.query.path||"",e=safePath(l.cwd,r);if(!e)return p.status(403).json({error:"\u8DEF\u5F84\u8D8A\u754C"});try{const s=walkFiles(e,""),u=[];for(const d of s){if(d.size>=50*1024*1024){u.push(d);continue}try{const o=await fileHash((0,external_path_.join)(e,d.name));u.push({...d,hash:o})}catch{u.push(d)}}p.json({ok:!0,cwd:l.cwd,path:r,files:u})}catch(s){p.status(500).json({error:s.message})}}));const EDITABLE_EXTS=new Set(["md","markdown","txt","log","json","jsonc","json5","yml","yaml","toml","ini","cfg","conf","env","properties","editorconfig","csv","tsv","xml","svg","html","htm","css","scss","less","js","mjs","cjs","jsx","ts","mts","cts","tsx","vue","svelte","py","pyi","sh","bash","zsh","ps1","psm1","bat","cmd","sql","java","kt","kts","groovy","gradle","c","h","cpp","hpp","cc","cxx","cs","go","rs","rb","php","swift","lua","dart","proto","graphql","cmake","dockerfile","gitignore"]),EDIT_MAX_BYTES=2*1024*1024;function normRelPath(E){return String(E||"").replace(/\\/g,"/").replace(/^\/+/,"")}function editableTarget(E,p){const l=normRelPath(p);if(!l)return{error:"\u7F3A\u5C11 path",code:400};const r=safePath(E.cwd,l);if(!r)return{error:"\u8DEF\u5F84\u8D8A\u754C",code:403};const e=(l.split("/").pop().match(/\.([A-Za-z0-9]+)$/)||[])[1]?.toLowerCase()||"";return EDITABLE_EXTS.has(e)?{rel:l,target:r}:{error:"\u8BE5\u7C7B\u578B\u6587\u4EF6\u4E0D\u652F\u6301\u5728\u7EBF\u7F16\u8F91\uFF0C\u8BF7\u7528\u4E0B\u8F7D",code:415}}function looksBinary(E){const p=Math.min(E.length,8192);for(let l=0;l<p;l++)if(E[l]===0)return!0;return!1}function findBySuffix(E,p){const l=walkFiles(E,"",[],0,["node_modules"],!1,2e4).filter((e=>e.name.endsWith("/"+p)));if(!l.length)return null;const r=e=>e.name.split("/").length;return l.sort(((e,s)=>r(e)-r(s)||s.mtime-e.mtime)),l[0].name}app.get("/api/files/:sessionId/content",checkAuth,((E,p)=>{const l=manager.get(E.params.sessionId);if(!l)return p.status(404).json({error:"\u4F1A\u8BDD\u4E0D\u5B58\u5728"});let r=editableTarget(l,E.query.path);if(r.error)return p.status(r.code).json({error:r.error});if(!(0,external_fs_.existsSync)(r.target)&&!(0,external_path_.isAbsolute)(r.rel)){const u=findBySuffix(l.cwd,r.rel);u&&(r=editableTarget(l,u))}let e;try{e=(0,external_fs_.statSync)(r.target)}catch{return p.status(404).json({error:"\u6587\u4EF6\u4E0D\u5B58\u5728"})}if(!e.isFile())return p.status(400).json({error:"\u4E0D\u662F\u6587\u4EF6"});if(e.size>EDIT_MAX_BYTES)return p.status(413).json({error:"\u6587\u4EF6\u8D85\u8FC7 2MB\uFF0C\u4EC5\u652F\u6301\u4E0B\u8F7D"});let s;try{s=(0,external_fs_.readFileSync)(r.target)}catch(u){return p.status(500).json({error:"\u8BFB\u53D6\u5931\u8D25: "+(u.code||u.message)})}if(looksBinary(s))return p.status(415).json({error:"\u4E8C\u8FDB\u5236\u6587\u4EF6\uFF0C\u4EC5\u652F\u6301\u4E0B\u8F7D"});p.json({ok:!0,path:r.rel,name:r.rel.split("/").pop(),content:s.toString("utf8"),mtime:e.mtimeMs,size:e.size,md:/\.(md|markdown)$/i.test(r.rel)})})),app.put("/api/files/:sessionId/content",checkAuth,express.json({limit:"4mb"}),((E,p)=>{const l=manager.get(E.params.sessionId);if(!l)return p.status(404).json({error:"\u4F1A\u8BDD\u4E0D\u5B58\u5728"});const{content:r,baseMtime:e,force:s}=E.body||{};if(typeof r!="string")return p.status(400).json({error:"\u7F3A\u5C11\u5185\u5BB9"});if(Buffer.byteLength(r,"utf8")>EDIT_MAX_BYTES)return p.status(413).json({error:"\u5185\u5BB9\u8D85\u8FC7 2MB"});const u=editableTarget(l,E.body.path);if(u.error)return p.status(u.code).json({error:u.error});let d;try{d=(0,external_fs_.statSync)(u.target)}catch{return p.status(404).json({error:"\u6587\u4EF6\u5DF2\u88AB\u5220\u9664\uFF0C\u65E0\u6CD5\u4FDD\u5B58"})}if(!d.isFile())return p.status(400).json({error:"\u4E0D\u662F\u6587\u4EF6"});if(!s&&typeof e=="number"&&Math.abs(d.mtimeMs-e)>300)return p.status(409).json({error:"conflict",mtime:d.mtimeMs});const o=u.target+".myhi-edit-"+Date.now();try{try{(0,external_fs_.writeFileSync)(o,r,"utf8"),(0,external_fs_.renameSync)(o,u.target)}catch{try{(0,external_fs_.rmSync)(o,{force:!0})}catch{}(0,external_fs_.writeFileSync)(u.target,r,"utf8")}}catch(a){const t=["EBUSY","EPERM","EACCES"].includes(a.code);return console.warn("[files] \u4FDD\u5B58\u5931\u8D25:",u.rel,a.code||a.message),p.status(500).json({error:t?"\u6587\u4EF6\u88AB\u5176\u5B83\u7A0B\u5E8F\u5360\u7528\uFF08AI \u53EF\u80FD\u6B63\u5728\u5199\u5165\uFF09\uFF0C\u7A0D\u540E\u518D\u8BD5":"\u4FDD\u5B58\u5931\u8D25: "+(a.code||a.message)})}let i=Date.now();try{i=(0,external_fs_.statSync)(u.target).mtimeMs}catch{}io.emit("files:changed",{sessionId:l.id}),p.json({ok:!0,mtime:i})})),app.get("/api/me",checkAuth,((E,p)=>{const l=getUserInfo(E.headers.cookie);p.json({name:l?.name||"\u7528\u6237",role:l?.role||"admin",exclusive:isExclusiveMode(),hasUsers:hasUsers(),dir:l?.dir||null,defaultRoot:l?.dir||process.env.MYHI_CWD||process.cwd(),desktop:hasDesktop(),skMode:(process.env.MYHI_SK_MODE||_configString("skMode")||"expert").toLowerCase()==="normal"?"normal":"expert"})}));const gatewayFile=(0,external_path_.join)(server_configDir,"gateway.json");function loadGateway(){try{return JSON.parse((0,external_fs_.readFileSync)(gatewayFile,"utf8").replace(/^\uFEFF/,""))}catch{return{baseUrl:"",apiKey:""}}}app.get("/api/gateway",checkAdminAuth,((E,p)=>{const l=loadGateway();p.json({baseUrl:l.baseUrl||"",apiKey:l.apiKey?l.apiKey.length>12?l.apiKey.slice(0,6)+"***"+l.apiKey.slice(-4):"***":"",hasKey:!!l.apiKey})})),app.post("/api/gateway",checkAdminAuth,express.json(),((E,p)=>{const{baseUrl:l,apiKey:r}=E.body||{},e=loadGateway(),s={baseUrl:typeof l=="string"?l.trim():e.baseUrl,apiKey:!r||r.includes("***")?e.apiKey:r.trim()};try{(0,external_fs_.mkdirSync)(server_configDir,{recursive:!0}),(0,external_fs_.writeFileSync)(gatewayFile,JSON.stringify(s,null,2),{mode:384}),p.json({ok:!0})}catch(u){p.status(500).json({error:u.message})}})),app.get("/api/secrets",checkAdminAuth,((E,p)=>{try{p.json(secretsOverview())}catch(l){p.status(500).json({error:l.message})}})),app.post("/api/secrets",checkAdminAuth,express.json(),((E,p)=>{const{name:l,value:r,type:e,scope:s,cwd:u}=E.body||{},d=upsertSecret({name:l,value:r,type:e,scope:s,cwd:u});if(d.error)return p.status(400).json(d);p.json(d)})),app.delete("/api/secrets",checkAdminAuth,express.json(),((E,p)=>{const{name:l,scope:r,cwd:e}=E.body||{};if(!l)return p.status(400).json({error:"\u7F3A\u5C11 name"});try{p.json(deleteSecret({name:l,scope:r,cwd:e}))}catch(s){p.status(500).json({error:s.message})}})),app.get("/api/session/:id/secrets",checkAuth,((E,p)=>{const l=manager.get(E.params.id);if(!l)return p.status(404).json({error:"\u4F1A\u8BDD\u4E0D\u5B58\u5728"});p.json({cwd:l.cwd,project:listSecrets("project",l.cwd),global:listSecrets("global")})})),app.post("/api/session/:id/secrets",checkAuth,express.json(),((E,p)=>{const l=manager.get(E.params.id);if(!l)return p.status(404).json({error:"\u4F1A\u8BDD\u4E0D\u5B58\u5728"});const{name:r,value:e,type:s}=E.body||{},u=upsertSecret({name:r,value:e,type:s,scope:"project",cwd:l.cwd});if(u.error)return p.status(400).json(u);p.json(u)})),app.delete("/api/session/:id/secrets",checkAuth,express.json(),((E,p)=>{const l=manager.get(E.params.id);if(!l)return p.status(404).json({error:"\u4F1A\u8BDD\u4E0D\u5B58\u5728"});const{name:r}=E.body||{};if(!r)return p.status(400).json({error:"\u7F3A\u5C11 name"});p.json(deleteSecret({name:r,scope:"project",cwd:l.cwd}))})),app.post("/api/session/:id/secrets/bulk",checkAuth,express.json(),((E,p)=>{const l=manager.get(E.params.id);if(!l)return p.status(404).json({error:"\u4F1A\u8BDD\u4E0D\u5B58\u5728"});const r=upsertManyProject(l.cwd,(E.body||{}).text||"");if(r.error)return p.status(400).json(r);p.json(r)})),app.get("/api/session/:id/guard",checkAuth,((E,p)=>{const l=manager.get(E.params.id);if(!l)return p.status(404).json({error:"\u4F1A\u8BDD\u4E0D\u5B58\u5728"});const r=getProjectGuard(l.cwd);p.json({cwd:l.cwd,enabled:r.enabled,level:r.level,entities:r.entities})})),app.put("/api/session/:id/guard",checkAuth,express.json(),((E,p)=>{const l=manager.get(E.params.id);if(!l)return p.status(404).json({error:"\u4F1A\u8BDD\u4E0D\u5B58\u5728"});const{enabled:r,level:e}=E.body||{},s=setProjectGuard(l.cwd,{enabled:r,level:e});if(s.error)return p.status(400).json(s);p.json(s)})),app.post("/api/session/:id/guard/entity",checkAuth,express.json(),((E,p)=>{const l=manager.get(E.params.id);if(!l)return p.status(404).json({error:"\u4F1A\u8BDD\u4E0D\u5B58\u5728"});const{name:r,type:e}=E.body||{},s=upsertEntity(l.cwd,{name:r,type:e});if(s.error)return p.status(400).json(s);p.json(s)})),app.delete("/api/session/:id/guard/entity",checkAuth,express.json(),((E,p)=>{const l=manager.get(E.params.id);if(!l)return p.status(404).json({error:"\u4F1A\u8BDD\u4E0D\u5B58\u5728"});const{name:r}=E.body||{};if(!r)return p.status(400).json({error:"\u7F3A\u5C11 name"});p.json(deleteEntity(l.cwd,r))})),app.post("/api/session/:id/guard/bulk",checkAuth,express.json(),((E,p)=>{const l=manager.get(E.params.id);if(!l)return p.status(404).json({error:"\u4F1A\u8BDD\u4E0D\u5B58\u5728"});const r=upsertManyEntities(l.cwd,(E.body||{}).text||"");if(r.error)return p.status(400).json(r);p.json(r)})),app.get("/api/session/:id/guard/audit",checkAuth,((E,p)=>{const l=manager.get(E.params.id);if(!l)return p.status(404).json({error:"\u4F1A\u8BDD\u4E0D\u5B58\u5728"});const r=l.claudeSessionId||E.params.id;p.json({entries:loadAudit(r)})})),app.get("/api/session/:id/followup",checkAuth,((E,p)=>{const l=manager.get(E.params.id);if(!l||typeof l.followUpState!="function")return p.status(404).json({error:"\u4F1A\u8BDD\u4E0D\u5B58\u5728\u6216\u975E AI \u4F1A\u8BDD"});p.json(l.followUpState())})),app.put("/api/session/:id/followup",checkAuth,express.json(),((E,p)=>{const l=manager.get(E.params.id);if(!l||typeof l.setFollowUp!="function")return p.status(404).json({error:"\u4F1A\u8BDD\u4E0D\u5B58\u5728\u6216\u975E AI \u4F1A\u8BDD"});const{enabled:r,intervalMinutes:e}=E.body||{};p.json(l.setFollowUp({enabled:r,intervalMinutes:e}))}));function maybeKickRelay(E){try{if(!E?.autoSend)return;const p=manager.get(E.sessionId);p&&typeof p._maybeRelayDraft=="function"&&p.alive&&!p.isBusy&&p._maybeRelayDraft()}catch{}}app.get("/api/inbox",checkAuth,((E,p)=>{const l=getUserInfo(E.headers.cookie),{sessionId:r}=E.query;try{try{rebindDrafts(manager.listSessions())}catch{}const e=new Set(manager.list(l?.name,l?.role==="admin").map((o=>o.id))),s=listDrafts({sessionId:r||void 0}).filter((o=>e.has(o.sessionId)||o.owner&&o.owner===l?.name)),u={};for(const o of s)u[o.sessionId]=(u[o.sessionId]||0)+1;const d=new Set(manager.listSessions().map((o=>o.id)));p.json({drafts:s.map((o=>({...o,sessionAlive:d.has(o.sessionId)}))),counts:u})}catch(e){p.status(500).json({error:e.message})}})),app.post("/api/inbox",checkAuth,express.json({limit:"1mb"}),((E,p)=>{const l=getUserInfo(E.headers.cookie),r=E.body||{},e=r.sessionId?manager.get(r.sessionId):null;try{const s=addDraft({...r,owner:r.owner||l?.name||null},e);maybeKickRelay(s),p.json({ok:!0,draft:s})}catch(s){p.status(400).json({error:s.message})}})),app.put("/api/inbox/:id",checkAuth,express.json({limit:"1mb"}),((E,p)=>{const l=E.body||{},r=l.sessionId?manager.get(l.sessionId):null,e=updateDraft(E.params.id,l,r);if(!e)return p.status(404).json({error:"\u8349\u7A3F\u4E0D\u5B58\u5728"});maybeKickRelay(e),p.json({ok:!0,draft:e})})),app.delete("/api/inbox/:id",checkAuth,((E,p)=>{if(!removeDraft(E.params.id))return p.status(404).json({error:"\u8349\u7A3F\u4E0D\u5B58\u5728"});p.json({ok:!0})}));function _spawnOpen(E,p,l=process.platform==="win32"){return new Promise((r=>{try{const e=(0,external_child_process_namespaceObject.spawn)(E,p,{detached:!0,stdio:"ignore",shell:l});e.on("error",(s=>{console.warn("[open-folder]",E,"\u5931\u8D25:",s.message),r(!1)})),e.unref(),setTimeout((()=>r(!0)),300)}catch(e){console.warn("[open-folder] spawn \u5F02\u5E38:",e.message),r(!1)}}))}let _winSessionId;process.platform==="win32"&&(0,external_child_process_namespaceObject.execFile)("tasklist",["/FI",`PID eq ${process.pid}`,"/FO","CSV","/NH"],{windowsHide:!0,timeout:5e3},((E,p)=>{const l=!E&&String(p).match(/^"[^"]*","\d+","[^"]*","(\d+)"/m);_winSessionId=l?Number(l[1]):null,_winSessionId===0&&console.log("[open-folder] myhi \u8FD0\u884C\u5728\u540E\u53F0\u4F1A\u8BDD\uFF08Session 0\uFF09\uFF0C\u300C\u6253\u5F00\u6587\u4EF6\u5939\u300D\u5DF2\u505C\u7528")}));function hasDesktop(){return process.platform==="linux"?!!(process.env.DISPLAY||process.env.WAYLAND_DISPLAY):process.platform==="win32"?_winSessionId!==0:!0}const NO_DESKTOP_MSG="myhi \u5F53\u524D\u4EE5\u540E\u53F0\u670D\u52A1\u65B9\u5F0F\u8FD0\u884C\uFF08\u6CA1\u6709\u684C\u9762\uFF09\uFF0C\u65E0\u6CD5\u5728\u672C\u673A\u5F39\u51FA\u6587\u4EF6\u5939\u7A97\u53E3\uFF0C\u8BF7\u7528\u300C\u6587\u4EF6\u300D\u9762\u677F\u6D4F\u89C8/\u4E0B\u8F7D";app.post("/api/session/:id/open-folder",checkAuth,(async(E,p)=>{const l=manager.get(E.params.id);if(!l?.cwd)return p.status(404).json({error:"\u4F1A\u8BDD\u6216\u76EE\u5F55\u4E0D\u5B58\u5728"});if(!(0,external_fs_.existsSync)(l.cwd))return p.status(400).json({error:"\u76EE\u5F55\u4E0D\u5B58\u5728\uFF08\u53EF\u80FD\u88AB\u79FB\u52A8/\u5220\u9664\uFF09: "+l.cwd});if(!hasDesktop())return p.json({ok:!1,cwd:l.cwd,error:NO_DESKTOP_MSG});let r=l.cwd;if(process.platform==="win32"){r=r.replace(/\//g,"\\").replace(/(?<!^[A-Za-z]:)\\+$/,"");let s=await _spawnOpen("explorer.exe",[r],!1);s||(s=await _spawnOpen("cmd.exe",["/c","start","",r]));const u=r.split("\\").filter(Boolean).pop()||"";if(u){const d=`$f='${u.replace(/'/g,"''")}'; 1..12 | ForEach-Object { Start-Sleep -Milliseconds 350; if ((New-Object -ComObject WScript.Shell).AppActivate($f)) { break } }`;_spawnOpen("powershell.exe",["-NoProfile","-ExecutionPolicy","Bypass","-Command",d],!1)}return console.log("[open-folder]",r,s?"\u5DF2\u62C9\u8D77":"\u5931\u8D25"),p.json({ok:s,cwd:r})}const e=process.platform==="darwin"?await _spawnOpen("open",[r]):await _spawnOpen("xdg-open",[r]);p.json({ok:e,cwd:r})}));function getPkgVersion(){for(const E of["../package.json","./package.json","package.json"])try{const p=JSON.parse((0,external_fs_.readFileSync)((0,external_path_.join)(server_dirname,E),"utf8")).version;if(p&&p!=="0.0.0")return p}catch{}return"0.0.0"}const PKG_VERSION=getPkgVersion();let _latestVersion=null,_lastVersionCheck=0;const VERSION_CHECK_INTERVAL=600*1e3;async function checkLatestVersion(){const E=Date.now();if(_latestVersion&&E-_lastVersionCheck<VERSION_CHECK_INTERVAL)return _latestVersion;try{const p=await fetch("https://registry.npmjs.org/@wendongfly/myhi/latest",{headers:{Accept:"application/json"},signal:AbortSignal.timeout(8e3)});p.ok&&(_latestVersion=(await p.json()).version,_lastVersionCheck=E)}catch{}return _latestVersion}app.get("/api/version",(async(E,p)=>{const l=getPkgVersion(),r=await checkLatestVersion();p.json({current:l,latest:r||l,updateAvailable:r&&r!==l})}));function isSystemd(){return process.platform==="linux"&&(process.env.INVOCATION_ID||process.env.JOURNAL_STREAM)}function getNpmUpgradeCmd(){const E="--registry https://registry.npmjs.org";return process.platform==="win32"?`npm.cmd install -g @wendongfly/myhi@latest ${E}`:`npm install -g @wendongfly/myhi@latest ${E}`}function restartService(){if(process.send)process.send({type:"restart"});else if(isSystemd())try{(0,external_child_process_namespaceObject.spawn)("sudo",["systemctl","restart","myhi.service"],{detached:!0,stdio:"ignore"}).unref()}catch{}else process.exit(0)}app.get("/api/server-config",checkAuth,((E,p)=>{const l=process.env.ASR_URL||(()=>{try{return JSON.parse((0,external_fs_.readFileSync)((0,external_path_.join)((0,external_os_.homedir)(),".myhi","asr.json"),"utf8")).url}catch{return""}})();p.json({asrUrl:l||""})})),app.post("/api/server-config",checkAuth,express.json(),((E,p)=>{const{asrUrl:l}=E.body||{},r=(0,external_path_.join)((0,external_os_.homedir)(),".myhi","asr.json");try{const e=(l||"").trim();(0,external_fs_.writeFileSync)(r,JSON.stringify({url:e},null,2),"utf8"),p.json({ok:!0})}catch(e){p.status(500).json({error:e.message})}}));const shortHostname=()=>((0,external_os_.hostname)()||"").split(".")[0];function machineName(){return(process.env.MYHI_NAME||_configString("name")||shortHostname()||"myhi").trim()}app.get("/api/machine-name",checkAuth,((E,p)=>{p.json({name:machineName(),hostname:shortHostname(),fromEnv:!!process.env.MYHI_NAME})})),app.post("/api/machine-name",checkAuth,express.json(),((E,p)=>{const l=getUserInfo(E.headers.cookie);if(l&&l.role!=="admin")return p.status(403).json({error:"\u4EC5\u7BA1\u7406\u5458\u53EF\u4FEE\u6539\u673A\u5668\u540D"});const r=String(E.body?.name??"").trim().slice(0,32),e=(0,external_path_.join)(server_configDir,"config.json");try{const s=_readConfig();r?s.name=r:delete s.name,(0,external_fs_.writeFileSync)(e,JSON.stringify(s,null,2),"utf8"),p.json({ok:!0,name:machineName()})}catch(s){p.status(500).json({error:s.message})}})),app.post("/api/restart",checkAuth,((E,p)=>{p.json({ok:!0}),setTimeout((()=>restartService()),300)})),app.post("/api/update",checkAuth,(async(E,p)=>{try{await execAsync(getNpmUpgradeCmd(),{encoding:"utf8",timeout:12e4}),console.log("[myhi] \u5347\u7EA7\u5B8C\u6210"),p.json({ok:!0,message:"\u5347\u7EA7\u5B8C\u6210\uFF0C\u670D\u52A1\u5C06\u81EA\u52A8\u91CD\u542F..."}),setTimeout((()=>restartService()),500)}catch(l){const r=(l.stderr||l.stdout||l.message||"").split(`
|
|
394
|
-
`).filter((
|
|
395
|
-
`);
|
|
396
|
-
`+
|
|
391
|
+
`).filter(Boolean)}catch{return{ok:!1,messages:[],total:0,hasMore:!1}}const s=[];for(const i of e){const a=parseTranscriptLine(i);a&&s.push(a)}const u=s.length,d=Math.max(0,u-Math.max(0,p)),o=Math.max(0,l>0?d-l:0);return{ok:!0,messages:s.slice(o,d),total:u,hasMore:o>0}}const REQUIRED_FILES=["package.json","bin/myhi.js","bin/daemon.js","dist/index.js","dist/lib","dist/login.html","dist/index.html","dist/chat.html","dist/admin.html","dist/grid.html","dist/terminal.html"];function checkInstall(E,p){const l=REQUIRED_FILES.filter((s=>!(0,external_fs_.existsSync)((0,external_path_.join)(E,s))));let r=null;try{r=JSON.parse((0,external_fs_.readFileSync)((0,external_path_.join)(E,"package.json"),"utf8")).version}catch{}let e=null;return l.length?e=`\u5B89\u88C5\u4E0D\u5B8C\u6574\uFF0C\u7F3A\u5C11 ${l.join("\u3001")}\uFF08\u53EF\u80FD\u88AB\u6740\u6BD2\u8F6F\u4EF6\u62E6\u622A\u6216\u5B89\u88C5\u88AB\u4E2D\u65AD\uFF09`:p&&r!==p&&(e=`\u5B89\u88C5\u540E\u7248\u672C\u662F ${r||"\u672A\u77E5"}\uFF0C\u4E0D\u662F\u671F\u671B\u7684 ${p}\uFF08npm \u53EF\u80FD\u88C5\u5230\u4E86\u522B\u7684\u76EE\u5F55\u6216\u955C\u50CF\u672A\u540C\u6B65\uFF09`),{ok:!e,version:r,missing:l,error:e}}function upgradeStatusFile(E){return(0,external_path_.join)(E,"upgrade-status.json")}const execAsync=(0,external_util_.promisify)(external_child_process_namespaceObject.exec),server_dirname=(0,external_path_.dirname)((0,external_url_.fileURLToPath)(import.meta.url));let PORT=parseInt(process.env.PORT,10)||12300;const PORT_EXPLICIT=!!process.env.PORT,server_configDir=(0,external_path_.join)((0,external_os_.homedir)(),".myhi");(0,external_fs_.mkdirSync)(server_configDir,{recursive:!0});let httpsServer=null,httpServer=null;function getOrCreateCert(){const E=(0,external_path_.join)(server_configDir,"cert.pem"),p=(0,external_path_.join)(server_configDir,"key.pem");if((0,external_fs_.existsSync)(E)&&(0,external_fs_.existsSync)(p))try{return{cert:(0,external_fs_.readFileSync)(E),key:(0,external_fs_.readFileSync)(p)}}catch{}try{const l=selfsigned.generate([{name:"commonName",value:"myhi"}],{days:3650,keySize:2048});return(0,external_fs_.writeFileSync)(E,l.cert,"utf8"),(0,external_fs_.writeFileSync)(p,l.private,"utf8"),console.log("[myhi] \u81EA\u7B7E\u540D HTTPS \u8BC1\u4E66\u5DF2\u751F\u6210"),{cert:l.cert,key:l.private}}catch{}try{return(0,external_child_process_namespaceObject.execSync)(`openssl req -x509 -newkey rsa:2048 -keyout "${p}" -out "${E}" -days 3650 -nodes -subj "/CN=myhi"`,{stdio:"pipe",timeout:1e4}),{cert:(0,external_fs_.readFileSync)(E),key:(0,external_fs_.readFileSync)(p)}}catch(l){return console.warn(`[myhi] \u751F\u6210 HTTPS \u8BC1\u4E66\u5931\u8D25: ${l.message?.split(`
|
|
392
|
+
`)[0]||l}`),null}}const pidFile=(0,external_path_.join)(server_configDir,"server.pid"),daemonPidFile=(0,external_path_.join)(server_configDir,"daemon.pid"),IS_DAEMON_CHILD=!!process.env.MYHI_DAEMON;function killOldProcess(E,p){let l=!1;try{process.kill(E,0),l=!0}catch{}if(!l)return!0;console.log(`[myhi] \u68C0\u6D4B\u5230\u5DF2\u8FD0\u884C\u7684${p} (PID ${E})\uFF0C\u6B63\u5728\u505C\u6B62...`);try{process.platform==="win32"?(0,external_child_process_namespaceObject.execSync)(`taskkill /PID ${E} /F /T`,{stdio:"pipe",timeout:5e3}):process.kill(E,"SIGTERM")}catch{}const r=Date.now()+5e3;for(;Date.now()<r;){try{process.kill(E,0)}catch{return!0}const s=Date.now()+200;for(;Date.now()<s;);}let e=!1;try{process.kill(E,0),e=!0}catch{}return!e}(function E(){try{const p=parseInt((0,external_fs_.readFileSync)(daemonPidFile,"utf8").trim(),10);if(p&&p!==process.pid&&p!==parseInt(process.env.MYHI_DAEMON_PID||"0",10)){let l=!1;try{process.kill(p,0),l=!0}catch{}if(l&&!IS_DAEMON_CHILD){console.log("[myhi] \u68C0\u6D4B\u5230\u540E\u53F0 daemon \u5728\u8FD0\u884C\uFF0C\u5148\u505C\u6B62..."),killOldProcess(p,"daemon")||(console.error("[myhi] \u65E0\u6CD5\u505C\u6B62 daemon\uFF0C\u8BF7\u5148\u8FD0\u884C: myhi stop"),process.exit(1));try{(0,external_fs_.unlinkSync)(daemonPidFile)}catch{}}}}catch{}if(!IS_DAEMON_CHILD)try{const p=parseInt((0,external_fs_.readFileSync)(pidFile,"utf8").trim(),10);p&&p!==process.pid&&(killOldProcess(p,"\u5B9E\u4F8B")||(console.error(`[myhi] \u65E0\u6CD5\u505C\u6B62\u65E7\u8FDB\u7A0B (PID ${p})\uFF0C\u8BF7\u624B\u52A8\u6267\u884C\uFF1A`),console.error(" myhi stop"),console.error(` \u6216: ${process.platform==="win32"?`taskkill /PID ${p} /F`:`kill -9 ${p}`}`),process.exit(1)))}catch{}(0,external_fs_.writeFileSync)(pidFile,String(process.pid))})();function cleanupPid(){try{parseInt((0,external_fs_.readFileSync)(pidFile,"utf8").trim(),10)===process.pid&&(0,external_fs_.unlinkSync)(pidFile)}catch{}}function gracefulShutdown(){try{if(typeof manager<"u"){try{manager.persist()}catch{}for(const E of manager.listSessions())try{E.kill()}catch{}}}catch{}cleanupPid()}process.on("exit",cleanupPid),process.on("SIGTERM",(()=>{gracefulShutdown(),process.exit(0)})),process.on("message",(E=>{E?.type==="shutdown"&&(gracefulShutdown(),process.exit(0))})),process.on("SIGINT",(()=>{gracefulShutdown(),process.exit(0)}));const RECOVERABLE_ERRNO=new Set(["ECONNRESET","EPIPE","ECONNABORTED","ETIMEDOUT","ENOENT","EBUSY","EACCES","EPERM","EISDIR","ERR_STREAM_PREMATURE_CLOSE","ERR_STREAM_DESTROYED","ERR_HTTP_HEADERS_SENT"]);process.on("uncaughtException",(E=>{if(RECOVERABLE_ERRNO.has(E?.code)){console.error(`[myhi] \u5DF2\u5FFD\u7565\u53EF\u6062\u590D\u5F02\u5E38(${E.code}):`,E.message);return}console.error("[myhi] \u672A\u6355\u83B7\u5F02\u5E38:",E.message),gracefulShutdown(),process.exit(1)})),process.on("unhandledRejection",(E=>{console.error("[myhi] \u672A\u5904\u7406\u7684 Promise \u62D2\u7EDD:",E)}));function readOrCreate(E,p){try{if((0,external_fs_.existsSync)(E))return(0,external_fs_.readFileSync)(E,"utf8").trim()}catch{}const l=p();return(0,external_fs_.writeFileSync)(E,l,{mode:384}),l}const TOKEN=readOrCreate((0,external_path_.join)(server_configDir,"token"),(()=>(0,external_crypto_.randomBytes)(16).toString("hex")));let PASSWORD=readOrCreate((0,external_path_.join)(server_configDir,"password"),(()=>String(Math.floor(1e3+Math.random()*9e3))));loadRoles(),loadUsers();let activeUser=null;const userSessions=new Map;userSessions.set(TOKEN,{role:"admin",name:"\u7BA1\u7406\u5458",permanent:!0});const SESSION_MAX_AGE=10080*60*1e3;setInterval((()=>{const E=Date.now();for(const[p,l]of userSessions)l.permanent||(!l.lastUsed||E-l.lastUsed>SESSION_MAX_AGE)&&userSessions.delete(p)}),3600*1e3);function getTailscaleIP(){try{return(0,external_child_process_namespaceObject.execSync)("tailscale ip -4",{timeout:3e3,stdio:"pipe"}).toString().trim().split(`
|
|
393
|
+
`)[0]}catch{}try{if(process.platform==="win32"){const E=(0,external_child_process_namespaceObject.execSync)("route print 0.0.0.0",{timeout:3e3,stdio:"pipe"}).toString().match(/0\.0\.0\.0\s+0\.0\.0\.0\s+\S+\s+(\d+\.\d+\.\d+\.\d+)/);if(E)return E[1]}else try{const E=(0,external_child_process_namespaceObject.execSync)("ip route get 1.1.1.1",{timeout:3e3,stdio:"pipe"}).toString().match(/src\s+(\d+\.\d+\.\d+\.\d+)/);if(E)return E[1]}catch{const E=(0,external_child_process_namespaceObject.execSync)("route -n get default 2>/dev/null || route -n 2>/dev/null",{timeout:3e3,stdio:"pipe"}).toString(),p=E.match(/interface:\s*(\S+)/i)||E.match(/0\.0\.0\.0\s+(\d+\.\d+\.\d+\.\d+)/);if(p){const l=(0,external_os_.networkInterfaces)()[p[1]];if(l){const r=l.find((e=>e.family==="IPv4"&&!e.internal));if(r)return r.address}if(/^\d+\.\d+\.\d+\.\d+$/.test(p[1]))return p[1]}}}catch{}for(const E of Object.values((0,external_os_.networkInterfaces)()))for(const p of E)if(p.family==="IPv4"&&!p.internal)return p.address;return"localhost"}function parseCookies(E=""){const p={};for(const l of E.split(";")){const r=l.indexOf("=");r>0&&(p[l.slice(0,r).trim()]=l.slice(r+1).trim())}return p}const SESSION_COOKIE="myhi_sid",COOKIE_OPTS_BASE="HttpOnly; SameSite=Strict; Path=/; Max-Age=31536000";function cookieOpts(E){return E?.secure||E?.headers?.["x-forwarded-proto"]==="https"?COOKIE_OPTS_BASE+"; Secure":COOKIE_OPTS_BASE}function setSessionCookie(E,p,l){E.setHeader("Set-Cookie",`${SESSION_COOKIE}=${p||TOKEN}; ${cookieOpts(l)}`)}function getSessionToken(E){return parseCookies(E)[SESSION_COOKIE]||null}function hasValidSession(E){const p=getSessionToken(E);if(p&&userSessions.has(p)){const l=userSessions.get(p);return l&&(l.lastUsed=Date.now()),!0}return!1}function getUserInfo(E){const p=getSessionToken(E);return p?userSessions.get(p):null}const app=express();app.set("trust proxy",!0),app.use(((E,p,l)=>{p.setHeader("Referrer-Policy","no-referrer"),p.setHeader("X-Content-Type-Options","nosniff"),l()}));function _readConfig(){try{return JSON.parse((0,external_fs_.readFileSync)((0,external_path_.join)(server_configDir,"config.json"),"utf8").replace(/^\uFEFF/,""))||{}}catch{return{}}}function _configString(E){const p=_readConfig()[E];return typeof p=="string"&&p?p:null}const HOST=process.env.HOST||_configString("host")||"0.0.0.0",_tlsOpts=process.env.MYHI_NO_HTTPS||_readConfig().httpOnly?null:getOrCreateCert();_tlsOpts?(httpsServer=(0,external_https_.createServer)(_tlsOpts,app),httpServer=(0,external_http_.createServer)(((E,p)=>{const l=(E.headers.host||"").replace(/:\d+$/,"");p.writeHead(301,{Location:`https://${l}:${PORT}${E.url}`}),p.end()}))):(console.warn("[myhi] TLS \u4E0D\u53EF\u7528\uFF0C\u4EE5 HTTP \u6A21\u5F0F\u8FD0\u884C\uFF08\u9EA6\u514B\u98CE/\u6444\u50CF\u5934\u7B49\u6D4F\u89C8\u5668\u5B89\u5168\u529F\u80FD\u53D7\u9650\uFF09"),httpServer=(0,external_http_.createServer)(app));for(const E of[httpsServer,httpServer])E&&(E.keepAliveTimeout=72e3,E.headersTimeout=76e3);const _corsAllowed=/^https?:\/\/(localhost|127\.0\.0\.1|\[::1\]|10\.\d+\.\d+\.\d+|172\.(1[6-9]|2\d|3[01])\.\d+\.\d+|192\.168\.\d+\.\d+|100\.(6[4-9]|[7-9]\d|1[01]\d|12[0-7])\.\d+\.\d+)(:\d+)?$/,io=new Server({serveClient:!1,cors:{origin:(E,p)=>{if(!E||_corsAllowed.test(E))return p(null,!0);p(new Error("CORS \u4E0D\u5141\u8BB8\u6B64\u6765\u6E90"),!1)},credentials:!0},transports:["websocket","polling"],pingTimeout:6e4,pingInterval:25e3,upgradeTimeout:3e4,maxHttpBufferSize:5e6});httpsServer?io.attach(httpsServer):io.attach(httpServer);const manager=new SessionManager,AUTO_ATTACH=process.env.MYHI_AUTO_ATTACH!=="0",_autoSpawned=new Set;function spawnLocalTerminal(E,p){if(!AUTO_ATTACH||_autoSpawned.has(E)||!process.env.DISPLAY&&process.platform==="linux")return;_autoSpawned.add(E);const l=__nccwpck_require__.ab+"attach.js",r=process.execPath,e=p||"myhi";if(process.platform==="win32"){const s=`"${r}" "${l}" ${E}`,u=(0,external_child_process_namespaceObject.spawn)("wt.exe",["-w","0","new-tab","--title",e,"--","cmd","/k",s],{detached:!0,stdio:"ignore"});u.unref(),u.on("error",(d=>{console.warn("[attach] wt.exe \u5931\u8D25\uFF0C\u56DE\u9000\u5230 cmd:",d.message),(0,external_child_process_namespaceObject.spawn)("cmd.exe",["/c","start",`"${e}"`,"cmd","/k",s],{detached:!0,stdio:"ignore"}).unref()}))}else if(process.platform==="darwin"){const s=`tell application "Terminal" to do script "${r} ${l} ${E}"`;(0,external_child_process_namespaceObject.spawn)("osascript",["-e",s],{detached:!0,stdio:"ignore"}).unref()}else{const s=`${r} "${l}" ${E}`;for(const[u,d]of[["gnome-terminal",["--","bash","-c",`${s}; exec bash`]],["xterm",["-title",e,"-e",s]],["konsole",["--new-tab","-e",s]]]){const o=(0,external_child_process_namespaceObject.spawn)(u,d,{detached:!0,stdio:"ignore"});o.unref(),o.on("error",(()=>{}));break}}}const uploadDir=(0,external_path_.join)(server_configDir,"uploads");(0,external_fs_.mkdirSync)(uploadDir,{recursive:!0});function checkAuth(E,p,l){const r=E.query.token;if(r&&(r===TOKEN||userSessions.has(r))){const e=userSessions.get(r);if(e?.onetime){userSessions.delete(r);const d=(0,external_crypto_.randomBytes)(16).toString("hex");userSessions.set(d,{role:e.role,name:e.name,lastUsed:Date.now()}),setSessionCookie(p,d,E)}else setSessionCookie(p,r===TOKEN?TOKEN:r,E);const s=Object.keys(E.query).filter((d=>d!=="token")),u=E.path+(s.length?"?"+new URLSearchParams(Object.fromEntries(Object.entries(E.query).filter((([d])=>d!=="token")))):"");return p.redirect(u)}if(hasValidSession(E.headers.cookie)){if(isExclusiveMode()&&activeUser){const e=getSessionToken(E.headers.cookie);if(e!==TOKEN){const s=userSessions.get(e);if(!s||s.name!==activeUser.name)return p.setHeader("Set-Cookie",`${SESSION_COOKIE}=; HttpOnly; SameSite=Strict; Path=/; Max-Age=0`),p.redirect("/login")}}return l()}p.redirect("/login")}const _require=(0,external_module_namespaceObject.createRequire)(import.meta.url);function pkgDirSafe(E){try{return(0,external_path_.dirname)(_require.resolve(`${E}/package.json`))}catch{return null}}const libDir=(0,external_path_.join)(server_dirname,"lib"),staticOpts={maxAge:864e5};app.use("/lib/xterm",express.static(pkgDirSafe("xterm")||(0,external_path_.join)(libDir,"xterm"),staticOpts)),app.use("/lib/xterm-fit",express.static(pkgDirSafe("xterm-addon-fit")||(0,external_path_.join)(libDir,"xterm-fit"),staticOpts)),app.use("/lib/xterm-links",express.static(pkgDirSafe("xterm-addon-web-links")||(0,external_path_.join)(libDir,"xterm-links"),staticOpts)),app.use("/sio-client",express.static((0,external_path_.join)(server_dirname,"client-dist"),{maxAge:"1h",index:!1,setHeaders:(E,p)=>{p.endsWith(".js")&&E.setHeader("Content-Type","application/javascript; charset=utf-8")}})),app.get("/manifest.json",((E,p)=>p.sendFile((0,external_path_.join)(server_dirname,"manifest.json")))),app.get("/icon.png",((E,p)=>p.sendFile((0,external_path_.join)(server_dirname,"icon.png")))),app.get("/icon.svg",((E,p)=>p.sendFile((0,external_path_.join)(server_dirname,"icon.svg"))));const _loginAttempts=new Map,LOGIN_MAX_ATTEMPTS=5,LOGIN_LOCKOUT_MS=300*1e3;function checkLoginRate(E){const p=Date.now(),l=_loginAttempts.get(E);return!l||p>l.resetAt?!0:l.count<LOGIN_MAX_ATTEMPTS}function recordLoginFailure(E){const p=Date.now(),l=_loginAttempts.get(E);!l||p>l.resetAt?_loginAttempts.set(E,{count:1,resetAt:p+LOGIN_LOCKOUT_MS}):l.count++}function clearLoginFailures(E){_loginAttempts.delete(E)}setInterval((()=>{const E=Date.now();for(const[p,l]of _loginAttempts)E>l.resetAt&&_loginAttempts.delete(p)}),600*1e3),app.get("/login",((E,p)=>{p.sendFile((0,external_path_.join)(server_dirname,"login.html"))})),app.post("/login",express.urlencoded({extended:!1}),((E,p)=>{const l=E.ip;if(!checkLoginRate(l))return p.redirect("/login?error=locked");loadRoles(),loadUsers();const r=E.body?.password,e=E.query.from,s=e&&e.startsWith("/")&&!e.startsWith("//")?e:"/";if(hasUsers()){const d=lookupUser(r);if(!d)return recordLoginFailure(l),p.redirect("/login?error=1");if(isExclusiveMode()&&activeUser&&activeUser.name!==d.name)return p.redirect("/login?error=occupied&user="+encodeURIComponent(activeUser.name));clearLoginFailures(l);const o=(0,external_crypto_.randomBytes)(16).toString("hex");return userSessions.set(o,{role:"operator",name:d.name,dir:d.dir,lastUsed:Date.now()}),isExclusiveMode()&&(activeUser?activeUser.tokens.add(o):activeUser={name:d.name,dir:d.dir,tokens:new Set([o]),loginAt:Date.now()}),setSessionCookie(p,o,E),console.log(`[\u767B\u5F55] ${d.name} \u767B\u5F55\uFF08${isExclusiveMode()?"\u72EC\u5360":"\u5171\u4EAB"}\u6A21\u5F0F\uFF09`),p.redirect(s)}const u=lookupPassword(r);if(u){clearLoginFailures(l);const d=(0,external_crypto_.randomBytes)(16).toString("hex");return userSessions.set(d,{role:u.role,name:u.name,lastUsed:Date.now()}),setSessionCookie(p,d,E),p.redirect(s)}if(r===PASSWORD){clearLoginFailures(l);const d=(0,external_crypto_.randomBytes)(16).toString("hex");return userSessions.set(d,{role:"admin",name:"\u7BA1\u7406\u5458",lastUsed:Date.now()}),setSessionCookie(p,d,E),p.redirect(s)}recordLoginFailure(l),p.redirect("/login?error=1")})),app.post("/logout",((E,p)=>{const l=getSessionToken(E.headers.cookie);l&&(activeUser&&activeUser.tokens.has(l)&&(activeUser.tokens.delete(l),activeUser.tokens.size===0?(console.log(`[\u9000\u51FA] ${activeUser.name} \u5168\u90E8\u8BBE\u5907\u9000\u51FA\uFF0C\u91CA\u653E\u767B\u5F55\u72B6\u6001\uFF08\u4F1A\u8BDD\u4FDD\u7559\uFF09`),activeUser=null):console.log(`[\u9000\u51FA] ${activeUser.name} \u4E00\u4E2A\u8BBE\u5907\u9000\u51FA\uFF08\u5269\u4F59 ${activeUser.tokens.size} \u4E2A\uFF09`)),userSessions.delete(l)),p.setHeader("Set-Cookie",`${SESSION_COOKIE}=; HttpOnly; SameSite=Strict; Path=/; Max-Age=0`),p.redirect("/login")})),app.get("/api/status",((E,p)=>{if(isExclusiveMode()&&activeUser)return p.json({occupied:!0,userName:activeUser.name,releasing:!!_exclusiveReleaseTimer});if(hasUsers()&&!isExclusiveMode()){const l=new Set;for(const[,r]of io.sockets.sockets){if(!r.data.userName||r.data.userName==="\u7BA1\u7406\u5458")continue;const e=r.handshake.headers.cookie;e&&!hasValidSession(e)||l.add(r.data.userName)}return p.json({occupied:!1,onlineUsers:[...l]})}p.json({occupied:!1})})),app.get("/admin",((E,p)=>p.sendFile((0,external_path_.join)(server_dirname,"admin.html")))),app.get("/",checkAuth,((E,p)=>p.sendFile((0,external_path_.join)(server_dirname,"index.html")))),app.get("/terminal/:id",checkAuth,((E,p)=>p.sendFile((0,external_path_.join)(server_dirname,"chat.html")))),app.get("/grid",checkAuth,((E,p)=>p.sendFile((0,external_path_.join)(server_dirname,"grid.html")))),app.get("/terminal-raw/:id",checkAuth,((E,p)=>p.sendFile((0,external_path_.join)(server_dirname,"terminal.html")))),app.use("/lib",express.static((0,external_path_.join)(server_dirname,"lib"),staticOpts)),app.get("/api/sessions",checkAuth,((E,p)=>{const l=getUserInfo(E.headers.cookie);p.json(manager.list(l?.name,l?.role==="admin"))})),app.get("/api/usage",((E,p)=>{try{const l=manager.listSessions().filter((e=>e.mode==="agent"&&e.alive)).map((e=>({id:e.id,title:e.title,owner:e.owner,usage:e._usage})));let r=null;for(const e of l)if(e.usage?.rateLimitInfo){r=e.usage.rateLimitInfo;break}p.json({rateLimit:r,activeSessions:l})}catch(l){p.status(500).json({error:l.message})}})),app.post("/api/sessions",checkAuth,express.json(),((E,p)=>{const l=getUserInfo(E.headers.cookie),r=manager.create({...E.body,owner:l?.name,userDir:l?.dir});p.status(201).json(r.toJSON())})),app.delete("/api/sessions/:id",checkAuth,((E,p)=>{manager.kill(E.params.id),p.status(204).end()})),app.get("/api/sessions/health",checkAuth,((E,p)=>{const l=getUserInfo(E.headers.cookie),r=manager.listSessions().filter((e=>e.mode==="agent")).filter((e=>!e.owner||!l?.name||e.owner===l.name)).map((e=>({...e.toJSON(),health:getSessionHealth(e.claudeSessionId)})));p.json({ok:!0,sessions:r})})),app.post("/api/sessions/:id/reset",checkAuth,((E,p)=>{const l=manager.get(E.params.id);if(!l||l.mode!=="agent"||typeof l.reset!="function")return p.status(404).json({ok:!1,error:"agent \u4F1A\u8BDD\u4E0D\u5B58\u5728"});l.reset(),l.emit("agent:message",{type:"system",subtype:"info",message:"\u5DF2\u91CD\u5F00\u4E3A\u5E72\u51C0\u4F1A\u8BDD\uFF0C\u4E0B\u6B21\u63D0\u95EE\u5F00\u542F\u5168\u65B0\u5BF9\u8BDD\uFF08\u65E7\u4E0A\u4E0B\u6587\u5DF2\u65AD\u5F00\uFF09"}),broadcastSessions(),p.json({ok:!0})})),app.get("/api/claude-sessions",checkAuth,((E,p)=>{const l=getUserInfo(E.headers.cookie);p.json(listLocalClaudeSessions(l?.dir))})),app.get("/api/agent/history/:sessionId",checkAuth,((E,p)=>{const l=manager.get(E.params.sessionId);if(!l||l.mode!=="agent")return p.status(404).json({ok:!1,error:"\u4F1A\u8BDD\u4E0D\u5B58\u5728"});const r=l.claudeSessionId;if(!r)return p.json({ok:!0,messages:[],total:0,hasMore:!1});const e=Math.max(0,parseInt(E.query.offset,10)||0),s=Math.min(200,Math.max(1,parseInt(E.query.limit,10)||50)),u=readTranscript(r,{offset:e,limit:s});p.json({ok:!0,messages:u.messages,total:u.total,hasMore:u.hasMore})})),app.get("/api/dirs",checkAuth,((E,p)=>{const l=getUserInfo(E.headers.cookie);if(!l||l.role!=="admin")return p.status(403).json({error:"\u6CA1\u6709\u76EE\u5F55\u6D4F\u89C8\u6743\u9650"});const r=l.dir?(0,external_path_.resolve)((0,external_path_.normalize)(l.dir)):null,e=process.platform==="win32"?"\\":"/";let s=E.query.path||r||process.env.MYHI_CWD||(0,external_os_.homedir)(),u=(0,external_path_.resolve)((0,external_path_.normalize)(s)).replace(/[/\\]+$/,"");process.platform==="win32"&&/^[A-Za-z]:$/.test(u)&&(u+="\\"),r&&!u.startsWith(r)&&(u=r);try{const d=(0,external_fs_.readdirSync)(u,{withFileTypes:!0}).filter((a=>a.isDirectory()&&a.name!==".git")).map((a=>({name:a.name,path:u.replace(/[/\\]+$/,"")+e+a.name}))).sort(((a,t)=>a.name.localeCompare(t.name))),o=(0,external_path_.join)(u,".."),i=!r||(0,external_path_.resolve)((0,external_path_.normalize)(o)).startsWith(r);p.json({current:u,parent:i&&o!==u?o:null,dirs:d,drives:process.platform==="win32"&&!r?"CDEFGHIJKLMNOPQRSTUVWXYZ".split("").map((a=>a+":\\")).filter((a=>{try{return(0,external_fs_.existsSync)(a)}catch{return!1}})):[]})}catch(d){p.status(400).json({error:d.message})}}));function safePath(E,p){const l=(0,external_path_.resolve)((0,external_path_.normalize)(E)),r=(0,external_path_.resolve)((0,external_path_.normalize)((0,external_path_.join)(E,p||"")));if(r!==l&&!r.startsWith(l+external_path_.sep))return null;try{const e=(0,external_fs_.realpathSync)(l),s=(0,external_fs_.existsSync)(r)?(0,external_fs_.realpathSync)(r):r;if(s!==e&&!s.startsWith(e+external_path_.sep))return null}catch{}return r}function walkFiles(E,p,l=[],r=0,e=[],s=!1,u=1/0){if(r>20||l.length>=u)return l;let d;try{d=(0,external_fs_.readdirSync)(E,{withFileTypes:!0})}catch{return l}for(const o of d){if(o.name===".git"||e.includes(o.name))continue;if(l.length>=u)return l;const i=(0,external_path_.join)(E,o.name),a=p?p+"/"+o.name:o.name;try{const t=(0,external_fs_.lstatSync)(i);if(t.isSymbolicLink())continue;t.isDirectory()?(s&&l.push({name:a,dir:!0,mtime:t.mtimeMs}),walkFiles(i,a,l,r+1,e,s,u)):t.isFile()&&l.push({name:a,size:t.size,mtime:t.mtimeMs})}catch{}}return l}function fileHash(E){return new Promise(((p,l)=>{const r=(0,external_crypto_.createHash)("sha256"),e=(0,external_fs_.createReadStream)(E);e.on("data",(s=>r.update(s))),e.on("end",(()=>p(r.digest("hex")))),e.on("error",l)}))}app.get("/api/files/:sessionId",checkAuth,((E,p)=>{const l=manager.get(E.params.sessionId);if(!l)return p.status(404).json({error:"\u4F1A\u8BDD\u4E0D\u5B58\u5728"});const r=E.query.path||"",e=safePath(l.cwd,r);if(!e)return p.status(403).json({error:"\u8DEF\u5F84\u8D8A\u754C"});try{if(!(0,external_fs_.statSync)(e).isDirectory())return p.status(400).json({error:"\u4E0D\u662F\u76EE\u5F55"});const s=(0,external_fs_.readdirSync)(e,{withFileTypes:!0}).filter((o=>o.name!==".git")).map((o=>{const i=(0,external_path_.join)(e,o.name);try{const a=(0,external_fs_.lstatSync)(i);return a.isSymbolicLink()?null:{name:o.name,isDir:a.isDirectory(),size:a.isFile()?a.size:void 0,mtime:a.mtimeMs}}catch{return null}})).filter(Boolean).sort(((o,i)=>i.isDir-o.isDir||o.name.localeCompare(i.name))),u=r?r.replace(/\\/g,"/"):"",d=u.includes("/")?u.slice(0,u.lastIndexOf("/")):u?"":null;p.json({ok:!0,cwd:l.cwd,path:u,parent:d,entries:s})}catch(s){p.status(500).json({error:s.message})}})),app.get("/api/files/:sessionId/search",checkAuth,((E,p)=>{const l=manager.get(E.params.sessionId);if(!l)return p.status(404).json({error:"\u4F1A\u8BDD\u4E0D\u5B58\u5728"});const r=String(E.query.q||"").trim().toLowerCase();try{let e=walkFiles(l.cwd,"",[],0,["node_modules"],!0).map((u=>({name:u.name,dir:!!u.dir,mtime:u.mtime})));r&&(e=e.filter((u=>u.name.toLowerCase().includes(r))));const s=u=>{if(!r)return 0;const d=u.name.slice(u.name.lastIndexOf("/")+1).toLowerCase();return d.startsWith(r)?0:d.includes(r)?1:2};e.sort(((u,d)=>s(u)-s(d)||(d.mtime||0)-(u.mtime||0))),p.json({ok:!0,files:e.slice(0,50).map((u=>({name:u.name,dir:!!u.dir})))})}catch(e){p.status(500).json({error:e.message})}})),app.get("/api/files/:sessionId/at-ls",checkAuth,((E,p)=>{const l=manager.get(E.params.sessionId);if(!l)return p.status(404).json({error:"\u4F1A\u8BDD\u4E0D\u5B58\u5728"});const r=process.platform==="win32",e=String(E.query.dir||"").replace(/\\/g,"/"),s=String(E.query.q||"").toLowerCase(),u=o=>o.replace(/\\/g,"/");if(r&&e==="/"){const o="CDEFGHIJKLMNOPQRSTUVWXYZ".split("").map((i=>i+":")).filter((i=>{try{return(0,external_fs_.existsSync)(i+"\\")}catch{return!1}})).filter((i=>!s||i.toLowerCase().startsWith(s))).map((i=>({label:i,dir:!0})));return p.json({ok:!0,win:r,abs:"/",rel:!1,parent:null,entries:o})}let d;e?e==="~"||e.startsWith("~/")?d=(0,external_path_.join)((0,external_os_.homedir)(),e.slice(1)):r&&/^[A-Za-z]:/.test(e)?d=e.length===2?e+"/":e:e.startsWith("/")?d=e:d=(0,external_path_.join)(l.cwd,e):d=l.cwd,d=(0,external_path_.resolve)((0,external_path_.normalize)(d));try{const o=(0,external_fs_.readdirSync)(d,{withFileTypes:!0}).filter((n=>n.name!==".git")).map((n=>{try{const f=(0,external_fs_.lstatSync)((0,external_path_.join)(d,n.name));return f.isSymbolicLink()||!(f.isDirectory()||f.isFile())?null:{label:n.name,dir:f.isDirectory()}}catch{return null}})).filter(Boolean),i=n=>s?n.label.toLowerCase().startsWith(s)?0:1:0,a=o.filter((n=>!s||n.label.toLowerCase().includes(s))).filter((n=>s.startsWith(".")||!n.label.startsWith("."))).sort(((n,f)=>i(n)-i(f)||f.dir-n.dir||n.label.localeCompare(f.label,"zh"))).slice(0,300),t=(0,external_path_.dirname)(d);p.json({ok:!0,win:r,abs:u(d),rel:!e||!/^([/~]|[A-Za-z]:)/.test(e),parent:t!==d?u(t):r?"/":null,entries:a})}catch(o){p.status(o.code==="ENOENT"||o.code==="ENOTDIR"?404:500).json({error:o.code==="ENOENT"?"\u76EE\u5F55\u4E0D\u5B58\u5728":o.code==="ENOTDIR"?"\u4E0D\u662F\u76EE\u5F55":o.message})}})),app.get("/api/files/:sessionId/download",checkAuth,((E,p)=>{const l=manager.get(E.params.sessionId);if(!l)return p.status(404).json({error:"\u4F1A\u8BDD\u4E0D\u5B58\u5728"});const r=E.query.path;if(!r)return p.status(400).json({error:"\u7F3A\u5C11 path \u53C2\u6570"});const e=safePath(l.cwd,r);if(!e)return p.status(403).json({error:"\u8DEF\u5F84\u8D8A\u754C"});let s;try{s=(0,external_fs_.statSync)(e)}catch{return p.status(404).json({error:"\u6587\u4EF6\u4E0D\u5B58\u5728"})}if(!s.isFile())return p.status(400).json({error:"\u4E0D\u662F\u6587\u4EF6"});const u=r.split("/").pop();p.setHeader("Content-Disposition",`attachment; filename*=UTF-8''${encodeURIComponent(u)}`),p.sendFile(e,{dotfiles:"allow",acceptRanges:!0},(d=>{if(!d)return;const o=d.code||"";["ECONNABORTED","EPIPE","ECONNRESET"].includes(o)||p.writableEnded||(console.warn("[files] \u4E0B\u8F7D\u5931\u8D25:",r,o||d.message),p.headersSent?p.destroy():p.status(500).json({error:"\u8BFB\u53D6\u6587\u4EF6\u5931\u8D25: "+(o||d.message)}))}))})),app.post("/api/files/:sessionId/upload",checkAuth,((E,p)=>{const l=manager.get(E.params.sessionId);if(!l)return p.status(404).json({error:"\u4F1A\u8BDD\u4E0D\u5B58\u5728"});const r=(E.query.path||"").replace(/\\/g,"/"),e=safePath(l.cwd,r);if(!e)return p.status(403).json({error:"\u8DEF\u5F84\u8D8A\u754C"});(0,external_fs_.mkdirSync)(e,{recursive:!0}),multer({storage:multer.diskStorage({destination:(s,u,d)=>{u.originalname=Buffer.from(u.originalname,"latin1").toString("utf8");const o=u.originalname.includes("/")?u.originalname.slice(0,u.originalname.lastIndexOf("/")):"",i=o?(0,external_path_.join)(e,o):e,a=(0,external_path_.resolve)((0,external_path_.normalize)(i)),t=(0,external_path_.resolve)((0,external_path_.normalize)(l.cwd));if(a!==t&&!a.startsWith(t+external_path_.sep))return d(new Error("\u8DEF\u5F84\u8D8A\u754C"));(0,external_fs_.mkdirSync)(i,{recursive:!0}),d(null,i)},filename:(s,u,d)=>{const o=u.originalname.includes("/")?u.originalname.slice(u.originalname.lastIndexOf("/")+1):u.originalname;d(null,o)}}),limits:{fileSize:500*1024*1024}}).array("files",100)(E,p,(s=>{if(s)return p.status(400).json({error:s.message});if(!E.files?.length)return p.status(400).json({error:"\u6CA1\u6709\u6587\u4EF6"});const u=E.files.map((d=>({name:d.originalname,size:d.size,path:(0,external_path_.resolve)(d.path).replace(/\\/g,"/")})));io.emit("files:changed",{sessionId:l.id}),p.json({ok:!0,files:u})}))})),app.delete("/api/files/:sessionId",checkAuth,((E,p)=>{const l=manager.get(E.params.sessionId);if(!l)return p.status(404).json({error:"\u4F1A\u8BDD\u4E0D\u5B58\u5728"});const r=E.query.path;if(!r)return p.status(400).json({error:"\u7F3A\u5C11 path \u53C2\u6570"});const e=safePath(l.cwd,r);if(!e)return p.status(403).json({error:"\u8DEF\u5F84\u8D8A\u754C"});if((0,external_path_.resolve)((0,external_path_.normalize)(e))===(0,external_path_.resolve)((0,external_path_.normalize)(l.cwd)))return p.status(403).json({error:"\u4E0D\u80FD\u5220\u9664\u4F1A\u8BDD\u6839\u76EE\u5F55"});try{(0,external_fs_.statSync)(e).isDirectory()?(0,external_fs_.rmSync)(e,{recursive:!0}):(0,external_fs_.unlinkSync)(e),io.emit("files:changed",{sessionId:l.id}),p.json({ok:!0})}catch(s){p.status(500).json({error:s.message})}})),app.get("/api/files/:sessionId/manifest",checkAuth,(async(E,p)=>{const l=manager.get(E.params.sessionId);if(!l)return p.status(404).json({error:"\u4F1A\u8BDD\u4E0D\u5B58\u5728"});const r=E.query.path||"",e=safePath(l.cwd,r);if(!e)return p.status(403).json({error:"\u8DEF\u5F84\u8D8A\u754C"});try{const s=walkFiles(e,""),u=[];for(const d of s){if(d.size>=50*1024*1024){u.push(d);continue}try{const o=await fileHash((0,external_path_.join)(e,d.name));u.push({...d,hash:o})}catch{u.push(d)}}p.json({ok:!0,cwd:l.cwd,path:r,files:u})}catch(s){p.status(500).json({error:s.message})}}));const EDITABLE_EXTS=new Set(["md","markdown","txt","log","json","jsonc","json5","yml","yaml","toml","ini","cfg","conf","env","properties","editorconfig","csv","tsv","xml","svg","html","htm","css","scss","less","js","mjs","cjs","jsx","ts","mts","cts","tsx","vue","svelte","py","pyi","sh","bash","zsh","ps1","psm1","bat","cmd","sql","java","kt","kts","groovy","gradle","c","h","cpp","hpp","cc","cxx","cs","go","rs","rb","php","swift","lua","dart","proto","graphql","cmake","dockerfile","gitignore"]),EDIT_MAX_BYTES=2*1024*1024;function normRelPath(E){return String(E||"").replace(/\\/g,"/").replace(/^\/+/,"")}function editableTarget(E,p){const l=normRelPath(p);if(!l)return{error:"\u7F3A\u5C11 path",code:400};const r=safePath(E.cwd,l);if(!r)return{error:"\u8DEF\u5F84\u8D8A\u754C",code:403};const e=(l.split("/").pop().match(/\.([A-Za-z0-9]+)$/)||[])[1]?.toLowerCase()||"";return EDITABLE_EXTS.has(e)?{rel:l,target:r}:{error:"\u8BE5\u7C7B\u578B\u6587\u4EF6\u4E0D\u652F\u6301\u5728\u7EBF\u7F16\u8F91\uFF0C\u8BF7\u7528\u4E0B\u8F7D",code:415}}function looksBinary(E){const p=Math.min(E.length,8192);for(let l=0;l<p;l++)if(E[l]===0)return!0;return!1}function findBySuffix(E,p){const l=walkFiles(E,"",[],0,["node_modules"],!1,2e4).filter((e=>e.name.endsWith("/"+p)));if(!l.length)return null;const r=e=>e.name.split("/").length;return l.sort(((e,s)=>r(e)-r(s)||s.mtime-e.mtime)),l[0].name}app.get("/api/files/:sessionId/content",checkAuth,((E,p)=>{const l=manager.get(E.params.sessionId);if(!l)return p.status(404).json({error:"\u4F1A\u8BDD\u4E0D\u5B58\u5728"});let r=editableTarget(l,E.query.path);if(r.error)return p.status(r.code).json({error:r.error});if(!(0,external_fs_.existsSync)(r.target)&&!(0,external_path_.isAbsolute)(r.rel)){const u=findBySuffix(l.cwd,r.rel);u&&(r=editableTarget(l,u))}let e;try{e=(0,external_fs_.statSync)(r.target)}catch{return p.status(404).json({error:"\u6587\u4EF6\u4E0D\u5B58\u5728"})}if(!e.isFile())return p.status(400).json({error:"\u4E0D\u662F\u6587\u4EF6"});if(e.size>EDIT_MAX_BYTES)return p.status(413).json({error:"\u6587\u4EF6\u8D85\u8FC7 2MB\uFF0C\u4EC5\u652F\u6301\u4E0B\u8F7D"});let s;try{s=(0,external_fs_.readFileSync)(r.target)}catch(u){return p.status(500).json({error:"\u8BFB\u53D6\u5931\u8D25: "+(u.code||u.message)})}if(looksBinary(s))return p.status(415).json({error:"\u4E8C\u8FDB\u5236\u6587\u4EF6\uFF0C\u4EC5\u652F\u6301\u4E0B\u8F7D"});p.json({ok:!0,path:r.rel,name:r.rel.split("/").pop(),content:s.toString("utf8"),mtime:e.mtimeMs,size:e.size,md:/\.(md|markdown)$/i.test(r.rel)})})),app.put("/api/files/:sessionId/content",checkAuth,express.json({limit:"4mb"}),((E,p)=>{const l=manager.get(E.params.sessionId);if(!l)return p.status(404).json({error:"\u4F1A\u8BDD\u4E0D\u5B58\u5728"});const{content:r,baseMtime:e,force:s}=E.body||{};if(typeof r!="string")return p.status(400).json({error:"\u7F3A\u5C11\u5185\u5BB9"});if(Buffer.byteLength(r,"utf8")>EDIT_MAX_BYTES)return p.status(413).json({error:"\u5185\u5BB9\u8D85\u8FC7 2MB"});const u=editableTarget(l,E.body.path);if(u.error)return p.status(u.code).json({error:u.error});let d;try{d=(0,external_fs_.statSync)(u.target)}catch{return p.status(404).json({error:"\u6587\u4EF6\u5DF2\u88AB\u5220\u9664\uFF0C\u65E0\u6CD5\u4FDD\u5B58"})}if(!d.isFile())return p.status(400).json({error:"\u4E0D\u662F\u6587\u4EF6"});if(!s&&typeof e=="number"&&Math.abs(d.mtimeMs-e)>300)return p.status(409).json({error:"conflict",mtime:d.mtimeMs});const o=u.target+".myhi-edit-"+Date.now();try{try{(0,external_fs_.writeFileSync)(o,r,"utf8"),(0,external_fs_.renameSync)(o,u.target)}catch{try{(0,external_fs_.rmSync)(o,{force:!0})}catch{}(0,external_fs_.writeFileSync)(u.target,r,"utf8")}}catch(a){const t=["EBUSY","EPERM","EACCES"].includes(a.code);return console.warn("[files] \u4FDD\u5B58\u5931\u8D25:",u.rel,a.code||a.message),p.status(500).json({error:t?"\u6587\u4EF6\u88AB\u5176\u5B83\u7A0B\u5E8F\u5360\u7528\uFF08AI \u53EF\u80FD\u6B63\u5728\u5199\u5165\uFF09\uFF0C\u7A0D\u540E\u518D\u8BD5":"\u4FDD\u5B58\u5931\u8D25: "+(a.code||a.message)})}let i=Date.now();try{i=(0,external_fs_.statSync)(u.target).mtimeMs}catch{}io.emit("files:changed",{sessionId:l.id}),p.json({ok:!0,mtime:i})})),app.get("/api/me",checkAuth,((E,p)=>{const l=getUserInfo(E.headers.cookie);p.json({name:l?.name||"\u7528\u6237",role:l?.role||"admin",exclusive:isExclusiveMode(),hasUsers:hasUsers(),dir:l?.dir||null,defaultRoot:l?.dir||process.env.MYHI_CWD||process.cwd(),desktop:hasDesktop(),skMode:(process.env.MYHI_SK_MODE||_configString("skMode")||"expert").toLowerCase()==="normal"?"normal":"expert"})}));const gatewayFile=(0,external_path_.join)(server_configDir,"gateway.json");function loadGateway(){try{return JSON.parse((0,external_fs_.readFileSync)(gatewayFile,"utf8").replace(/^\uFEFF/,""))}catch{return{baseUrl:"",apiKey:""}}}app.get("/api/gateway",checkAdminAuth,((E,p)=>{const l=loadGateway();p.json({baseUrl:l.baseUrl||"",apiKey:l.apiKey?l.apiKey.length>12?l.apiKey.slice(0,6)+"***"+l.apiKey.slice(-4):"***":"",hasKey:!!l.apiKey})})),app.post("/api/gateway",checkAdminAuth,express.json(),((E,p)=>{const{baseUrl:l,apiKey:r}=E.body||{},e=loadGateway(),s={baseUrl:typeof l=="string"?l.trim():e.baseUrl,apiKey:!r||r.includes("***")?e.apiKey:r.trim()};try{(0,external_fs_.mkdirSync)(server_configDir,{recursive:!0}),(0,external_fs_.writeFileSync)(gatewayFile,JSON.stringify(s,null,2),{mode:384}),p.json({ok:!0})}catch(u){p.status(500).json({error:u.message})}})),app.get("/api/secrets",checkAdminAuth,((E,p)=>{try{p.json(secretsOverview())}catch(l){p.status(500).json({error:l.message})}})),app.post("/api/secrets",checkAdminAuth,express.json(),((E,p)=>{const{name:l,value:r,type:e,scope:s,cwd:u}=E.body||{},d=upsertSecret({name:l,value:r,type:e,scope:s,cwd:u});if(d.error)return p.status(400).json(d);p.json(d)})),app.delete("/api/secrets",checkAdminAuth,express.json(),((E,p)=>{const{name:l,scope:r,cwd:e}=E.body||{};if(!l)return p.status(400).json({error:"\u7F3A\u5C11 name"});try{p.json(deleteSecret({name:l,scope:r,cwd:e}))}catch(s){p.status(500).json({error:s.message})}})),app.get("/api/session/:id/secrets",checkAuth,((E,p)=>{const l=manager.get(E.params.id);if(!l)return p.status(404).json({error:"\u4F1A\u8BDD\u4E0D\u5B58\u5728"});p.json({cwd:l.cwd,project:listSecrets("project",l.cwd),global:listSecrets("global")})})),app.post("/api/session/:id/secrets",checkAuth,express.json(),((E,p)=>{const l=manager.get(E.params.id);if(!l)return p.status(404).json({error:"\u4F1A\u8BDD\u4E0D\u5B58\u5728"});const{name:r,value:e,type:s}=E.body||{},u=upsertSecret({name:r,value:e,type:s,scope:"project",cwd:l.cwd});if(u.error)return p.status(400).json(u);p.json(u)})),app.delete("/api/session/:id/secrets",checkAuth,express.json(),((E,p)=>{const l=manager.get(E.params.id);if(!l)return p.status(404).json({error:"\u4F1A\u8BDD\u4E0D\u5B58\u5728"});const{name:r}=E.body||{};if(!r)return p.status(400).json({error:"\u7F3A\u5C11 name"});p.json(deleteSecret({name:r,scope:"project",cwd:l.cwd}))})),app.post("/api/session/:id/secrets/bulk",checkAuth,express.json(),((E,p)=>{const l=manager.get(E.params.id);if(!l)return p.status(404).json({error:"\u4F1A\u8BDD\u4E0D\u5B58\u5728"});const r=upsertManyProject(l.cwd,(E.body||{}).text||"");if(r.error)return p.status(400).json(r);p.json(r)})),app.get("/api/session/:id/guard",checkAuth,((E,p)=>{const l=manager.get(E.params.id);if(!l)return p.status(404).json({error:"\u4F1A\u8BDD\u4E0D\u5B58\u5728"});const r=getProjectGuard(l.cwd);p.json({cwd:l.cwd,enabled:r.enabled,level:r.level,entities:r.entities})})),app.put("/api/session/:id/guard",checkAuth,express.json(),((E,p)=>{const l=manager.get(E.params.id);if(!l)return p.status(404).json({error:"\u4F1A\u8BDD\u4E0D\u5B58\u5728"});const{enabled:r,level:e}=E.body||{},s=setProjectGuard(l.cwd,{enabled:r,level:e});if(s.error)return p.status(400).json(s);p.json(s)})),app.post("/api/session/:id/guard/entity",checkAuth,express.json(),((E,p)=>{const l=manager.get(E.params.id);if(!l)return p.status(404).json({error:"\u4F1A\u8BDD\u4E0D\u5B58\u5728"});const{name:r,type:e}=E.body||{},s=upsertEntity(l.cwd,{name:r,type:e});if(s.error)return p.status(400).json(s);p.json(s)})),app.delete("/api/session/:id/guard/entity",checkAuth,express.json(),((E,p)=>{const l=manager.get(E.params.id);if(!l)return p.status(404).json({error:"\u4F1A\u8BDD\u4E0D\u5B58\u5728"});const{name:r}=E.body||{};if(!r)return p.status(400).json({error:"\u7F3A\u5C11 name"});p.json(deleteEntity(l.cwd,r))})),app.post("/api/session/:id/guard/bulk",checkAuth,express.json(),((E,p)=>{const l=manager.get(E.params.id);if(!l)return p.status(404).json({error:"\u4F1A\u8BDD\u4E0D\u5B58\u5728"});const r=upsertManyEntities(l.cwd,(E.body||{}).text||"");if(r.error)return p.status(400).json(r);p.json(r)})),app.get("/api/session/:id/guard/audit",checkAuth,((E,p)=>{const l=manager.get(E.params.id);if(!l)return p.status(404).json({error:"\u4F1A\u8BDD\u4E0D\u5B58\u5728"});const r=l.claudeSessionId||E.params.id;p.json({entries:loadAudit(r)})})),app.get("/api/session/:id/followup",checkAuth,((E,p)=>{const l=manager.get(E.params.id);if(!l||typeof l.followUpState!="function")return p.status(404).json({error:"\u4F1A\u8BDD\u4E0D\u5B58\u5728\u6216\u975E AI \u4F1A\u8BDD"});p.json(l.followUpState())})),app.put("/api/session/:id/followup",checkAuth,express.json(),((E,p)=>{const l=manager.get(E.params.id);if(!l||typeof l.setFollowUp!="function")return p.status(404).json({error:"\u4F1A\u8BDD\u4E0D\u5B58\u5728\u6216\u975E AI \u4F1A\u8BDD"});const{enabled:r,intervalMinutes:e}=E.body||{};p.json(l.setFollowUp({enabled:r,intervalMinutes:e}))}));function maybeKickRelay(E){try{if(!E?.autoSend)return;const p=manager.get(E.sessionId);p&&typeof p._maybeRelayDraft=="function"&&p.alive&&!p.isBusy&&p._maybeRelayDraft()}catch{}}app.get("/api/inbox",checkAuth,((E,p)=>{const l=getUserInfo(E.headers.cookie),{sessionId:r}=E.query;try{try{rebindDrafts(manager.listSessions())}catch{}const e=new Set(manager.list(l?.name,l?.role==="admin").map((o=>o.id))),s=listDrafts({sessionId:r||void 0}).filter((o=>e.has(o.sessionId)||o.owner&&o.owner===l?.name)),u={};for(const o of s)u[o.sessionId]=(u[o.sessionId]||0)+1;const d=new Set(manager.listSessions().map((o=>o.id)));p.json({drafts:s.map((o=>({...o,sessionAlive:d.has(o.sessionId)}))),counts:u})}catch(e){p.status(500).json({error:e.message})}})),app.post("/api/inbox",checkAuth,express.json({limit:"1mb"}),((E,p)=>{const l=getUserInfo(E.headers.cookie),r=E.body||{},e=r.sessionId?manager.get(r.sessionId):null;try{const s=addDraft({...r,owner:r.owner||l?.name||null},e);maybeKickRelay(s),p.json({ok:!0,draft:s})}catch(s){p.status(400).json({error:s.message})}})),app.put("/api/inbox/:id",checkAuth,express.json({limit:"1mb"}),((E,p)=>{const l=E.body||{},r=l.sessionId?manager.get(l.sessionId):null,e=updateDraft(E.params.id,l,r);if(!e)return p.status(404).json({error:"\u8349\u7A3F\u4E0D\u5B58\u5728"});maybeKickRelay(e),p.json({ok:!0,draft:e})})),app.delete("/api/inbox/:id",checkAuth,((E,p)=>{if(!removeDraft(E.params.id))return p.status(404).json({error:"\u8349\u7A3F\u4E0D\u5B58\u5728"});p.json({ok:!0})}));function _spawnOpen(E,p,l=process.platform==="win32"){return new Promise((r=>{try{const e=(0,external_child_process_namespaceObject.spawn)(E,p,{detached:!0,stdio:"ignore",shell:l});e.on("error",(s=>{console.warn("[open-folder]",E,"\u5931\u8D25:",s.message),r(!1)})),e.unref(),setTimeout((()=>r(!0)),300)}catch(e){console.warn("[open-folder] spawn \u5F02\u5E38:",e.message),r(!1)}}))}let _winSessionId;process.platform==="win32"&&(0,external_child_process_namespaceObject.execFile)("tasklist",["/FI",`PID eq ${process.pid}`,"/FO","CSV","/NH"],{windowsHide:!0,timeout:5e3},((E,p)=>{const l=!E&&String(p).match(/^"[^"]*","\d+","[^"]*","(\d+)"/m);_winSessionId=l?Number(l[1]):null,_winSessionId===0&&console.log("[open-folder] myhi \u8FD0\u884C\u5728\u540E\u53F0\u4F1A\u8BDD\uFF08Session 0\uFF09\uFF0C\u300C\u6253\u5F00\u6587\u4EF6\u5939\u300D\u5DF2\u505C\u7528")}));function hasDesktop(){return process.platform==="linux"?!!(process.env.DISPLAY||process.env.WAYLAND_DISPLAY):process.platform==="win32"?_winSessionId!==0:!0}const NO_DESKTOP_MSG="myhi \u5F53\u524D\u4EE5\u540E\u53F0\u670D\u52A1\u65B9\u5F0F\u8FD0\u884C\uFF08\u6CA1\u6709\u684C\u9762\uFF09\uFF0C\u65E0\u6CD5\u5728\u672C\u673A\u5F39\u51FA\u6587\u4EF6\u5939\u7A97\u53E3\uFF0C\u8BF7\u7528\u300C\u6587\u4EF6\u300D\u9762\u677F\u6D4F\u89C8/\u4E0B\u8F7D";app.post("/api/session/:id/open-folder",checkAuth,(async(E,p)=>{const l=manager.get(E.params.id);if(!l?.cwd)return p.status(404).json({error:"\u4F1A\u8BDD\u6216\u76EE\u5F55\u4E0D\u5B58\u5728"});if(!(0,external_fs_.existsSync)(l.cwd))return p.status(400).json({error:"\u76EE\u5F55\u4E0D\u5B58\u5728\uFF08\u53EF\u80FD\u88AB\u79FB\u52A8/\u5220\u9664\uFF09: "+l.cwd});if(!hasDesktop())return p.json({ok:!1,cwd:l.cwd,error:NO_DESKTOP_MSG});let r=l.cwd;if(process.platform==="win32"){r=r.replace(/\//g,"\\").replace(/(?<!^[A-Za-z]:)\\+$/,"");let s=await _spawnOpen("explorer.exe",[r],!1);s||(s=await _spawnOpen("cmd.exe",["/c","start","",r]));const u=r.split("\\").filter(Boolean).pop()||"";if(u){const d=`$f='${u.replace(/'/g,"''")}'; 1..12 | ForEach-Object { Start-Sleep -Milliseconds 350; if ((New-Object -ComObject WScript.Shell).AppActivate($f)) { break } }`;_spawnOpen("powershell.exe",["-NoProfile","-ExecutionPolicy","Bypass","-Command",d],!1)}return console.log("[open-folder]",r,s?"\u5DF2\u62C9\u8D77":"\u5931\u8D25"),p.json({ok:s,cwd:r})}const e=process.platform==="darwin"?await _spawnOpen("open",[r]):await _spawnOpen("xdg-open",[r]);p.json({ok:e,cwd:r})}));function getPkgVersion(){for(const E of["../package.json","./package.json","package.json"])try{const p=JSON.parse((0,external_fs_.readFileSync)((0,external_path_.join)(server_dirname,E),"utf8")).version;if(p&&p!=="0.0.0")return p}catch{}return"0.0.0"}const PKG_VERSION=getPkgVersion();let _latestVersion=null,_lastVersionCheck=0;const VERSION_CHECK_INTERVAL=600*1e3;async function checkLatestVersion(){const E=Date.now();if(_latestVersion&&E-_lastVersionCheck<VERSION_CHECK_INTERVAL)return _latestVersion;try{const p=await fetch("https://registry.npmjs.org/@wendongfly/myhi/latest",{headers:{Accept:"application/json"},signal:AbortSignal.timeout(8e3)});p.ok&&(_latestVersion=(await p.json()).version,_lastVersionCheck=E)}catch{}return _latestVersion}app.get("/api/version",(async(E,p)=>{const l=PKG_VERSION,r=await checkLatestVersion();p.json({current:l,latest:r||l,updateAvailable:r&&r!==l,upgrade:upgradeState()})}));function isSystemd(){return process.platform==="linux"&&process.ppid===1&&!!(process.env.INVOCATION_ID||process.env.JOURNAL_STREAM)}function getNpmUpgradeCmd(){const E="--registry https://registry.npmjs.org";return process.platform==="win32"?`npm.cmd install -g @wendongfly/myhi@latest ${E}`:`npm install -g @wendongfly/myhi@latest ${E}`}function restartService(){if(process.send)process.send({type:"restart"});else if(isSystemd())try{(0,external_child_process_namespaceObject.spawn)("sudo",["systemctl","restart","myhi.service"],{detached:!0,stdio:"ignore"}).unref()}catch{}else{try{const E=[PKG_ROOT,"bin","daemon.js"].join(external_path_.sep),p=(0,external_fs_.openSync)((0,external_path_.join)(server_configDir,"daemon.log"),"a");(0,external_child_process_namespaceObject.spawn)(process.execPath,[E,...process.argv.slice(2)],{detached:!0,stdio:["ignore",p,p],windowsHide:!0,env:{...process.env,MYHI_WAIT_PID:String(process.pid)}}).unref(),console.log("[myhi] \u5DF2\u8F6C\u4E3A\u540E\u53F0\u8FD0\u884C\uFF08daemon\uFF09\uFF0C\u67E5\u770B\u65E5\u5FD7\uFF1Amyhi log")}catch(E){console.error("[myhi] \u62C9\u8D77 daemon \u5931\u8D25\uFF0C\u8BF7\u624B\u52A8\u91CD\u65B0\u8FD0\u884C myhi\uFF1A",E.message)}gracefulShutdown(),setTimeout((()=>process.exit(0)),300)}}app.get("/api/server-config",checkAuth,((E,p)=>{const l=process.env.ASR_URL||(()=>{try{return JSON.parse((0,external_fs_.readFileSync)((0,external_path_.join)((0,external_os_.homedir)(),".myhi","asr.json"),"utf8")).url}catch{return""}})();p.json({asrUrl:l||""})})),app.post("/api/server-config",checkAuth,express.json(),((E,p)=>{const{asrUrl:l}=E.body||{},r=(0,external_path_.join)((0,external_os_.homedir)(),".myhi","asr.json");try{const e=(l||"").trim();(0,external_fs_.writeFileSync)(r,JSON.stringify({url:e},null,2),"utf8"),p.json({ok:!0})}catch(e){p.status(500).json({error:e.message})}}));const shortHostname=()=>((0,external_os_.hostname)()||"").split(".")[0];function machineName(){return(process.env.MYHI_NAME||_configString("name")||shortHostname()||"myhi").trim()}app.get("/api/machine-name",checkAuth,((E,p)=>{p.json({name:machineName(),hostname:shortHostname(),fromEnv:!!process.env.MYHI_NAME})})),app.post("/api/machine-name",checkAuth,express.json(),((E,p)=>{const l=getUserInfo(E.headers.cookie);if(l&&l.role!=="admin")return p.status(403).json({error:"\u4EC5\u7BA1\u7406\u5458\u53EF\u4FEE\u6539\u673A\u5668\u540D"});const r=String(E.body?.name??"").trim().slice(0,32),e=(0,external_path_.join)(server_configDir,"config.json");try{const s=_readConfig();r?s.name=r:delete s.name,(0,external_fs_.writeFileSync)(e,JSON.stringify(s,null,2),"utf8"),p.json({ok:!0,name:machineName()})}catch(s){p.status(500).json({error:s.message})}})),app.post("/api/restart",checkAuth,((E,p)=>{p.json({ok:!0}),setTimeout((()=>restartService()),300)}));const PKG_ROOT=(0,external_path_.join)(server_dirname,".."),DAEMON_CAPS=(process.env.MYHI_DAEMON_CAPS||"").split(",");let _upgrade={running:!1};function readLastUpgrade(){try{return JSON.parse((0,external_fs_.readFileSync)(upgradeStatusFile(server_configDir),"utf8"))}catch{return null}}function upgradeState(){if(_upgrade.running)return{running:!0,startedAt:_upgrade.startedAt,target:_upgrade.target};const E=_upgrade.result||readLastUpgrade();return E&&Date.now()-(E.at||0)<1800*1e3?{running:!1,last:E}:{running:!1}}function busyAgentTitles(){try{return manager.listSessions().filter((E=>E.mode==="agent"&&E.isBusy)).map((E=>E.title||E.id))}catch{return[]}}function recordUpgrade(E){E.at=Date.now(),_upgrade={running:!1,result:E};try{(0,external_fs_.writeFileSync)(upgradeStatusFile(server_configDir),JSON.stringify(E,null,2))}catch{}console.log(E.ok?`[myhi] \u5347\u7EA7\u6210\u529F\uFF1A${E.from} \u2192 ${E.to}`:`[myhi] \u5347\u7EA7\u5931\u8D25\uFF1A${E.error}`)}async function handleUpgrade(E,p){if(_upgrade.running)return p.status(409).json({ok:!1,error:"\u5347\u7EA7\u5DF2\u5728\u8FDB\u884C\u4E2D\uFF0C\u8BF7\u7A0D\u5019"});const l=busyAgentTitles();if(l.length&&!E.body?.force)return p.status(409).json({ok:!1,busy:l,error:`\u6709 ${l.length} \u4E2A\u4EFB\u52A1\u6B63\u5728\u6267\u884C\uFF1A${l.join("\u3001")}\u3002\u5347\u7EA7\u4F1A\u91CD\u542F\u670D\u52A1\u3001\u6253\u65AD\u5B83\u4EEC\u8FD9\u4E00\u8F6E\u3002`});const r=PKG_VERSION,e=await checkLatestVersion();if(e&&e===r)return p.json({ok:!1,error:`\u5DF2\u7ECF\u662F\u6700\u65B0\u7248\u672C ${r}`});if(process.platform==="win32"&&process.send&&DAEMON_CAPS.includes("upgrade-v2")){_upgrade={running:!0,startedAt:Date.now(),target:e},p.json({ok:!0,mode:"daemon",from:r,target:e,message:"\u6B63\u5728\u505C\u6B62\u670D\u52A1\u5E76\u5B89\u88C5\u65B0\u7248\u672C\uFF0C\u7EA6 1~3 \u5206\u949F\uFF0C\u671F\u95F4\u9875\u9762\u4F1A\u65AD\u5F00\u2026"}),setTimeout((()=>process.send({type:"upgrade",target:e})),300);return}_upgrade={running:!0,startedAt:Date.now(),target:e},p.json({ok:!0,mode:"server",from:r,target:e,message:"\u6B63\u5728\u4E0B\u8F7D\u5B89\u88C5\u65B0\u7248\u672C\u2026"});try{await execAsync(getNpmUpgradeCmd(),{encoding:"utf8",timeout:6e5,windowsHide:!0})}catch(u){const d=(u.stderr||u.stdout||u.message||"").split(`
|
|
394
|
+
`).filter((o=>o.trim())).slice(-5).join(`
|
|
395
|
+
`);return recordUpgrade({ok:!1,from:r,to:e,error:`npm install \u5931\u8D25\uFF1A
|
|
396
|
+
`+d+`
|
|
397
397
|
|
|
398
|
-
\
|
|
398
|
+
\u53EF\u767B\u5F55\u670D\u52A1\u5668\u624B\u52A8\u6267\u884C npm i -g @wendongfly/myhi@latest \u540E\u91CD\u542F`})}const s=checkInstall(PKG_ROOT,e);if(!s.ok)return recordUpgrade({ok:!1,from:r,to:e,error:s.error+"\u3002\u670D\u52A1\u672A\u91CD\u542F\uFF0C\u4ECD\u5728\u8FD0\u884C\u65E7\u7248\u672C\uFF1B\u8BF7\u91CD\u65B0\u5B89\u88C5\u540E\u518D\u91CD\u542F\u3002"});recordUpgrade({ok:!0,from:r,to:s.version}),_upgrade={running:!0,startedAt:Date.now(),target:e},setTimeout((()=>restartService()),500),setTimeout((()=>recordUpgrade({ok:!1,from:r,to:s.version,error:"\u65B0\u7248\u672C\u5DF2\u5B89\u88C5\uFF0C\u4F46\u670D\u52A1\u6CA1\u80FD\u81EA\u52A8\u91CD\u542F\uFF0C\u8BF7\u624B\u52A8\u91CD\u542F myhi \u751F\u6548"})),12e4).unref()}app.post("/api/update",checkAuth,express.json(),handleUpgrade);let _latestClaude=null,_lastClaudeCheck=0;async function checkLatestClaudeVersion(){const E=Date.now();if(_latestClaude&&E-_lastClaudeCheck<VERSION_CHECK_INTERVAL)return _latestClaude;try{const p=await fetch("https://registry.npmjs.org/@anthropic-ai/claude-code/latest",{headers:{Accept:"application/json"},signal:AbortSignal.timeout(8e3)});p.ok&&(_latestClaude=(await p.json()).version,_lastClaudeCheck=E)}catch{}return _latestClaude}function getClaudeUpgradeCmd(){return(process.platform==="win32"?"npm.cmd ":"npm ")+"install -g @anthropic-ai/claude-code@latest --include=optional"}let _claudeUpgrade={running:!1,ok:null,error:null,version:null,startedAt:0};app.get("/api/claude/version",(async(E,p)=>{const[l,r]=await Promise.all([getClaudeVersion(),checkLatestClaudeVersion()]),e=l?.version||null;p.json({current:e,latest:r||e,updateAvailable:!!(r&&e&&r!==e),upgrade:_claudeUpgrade.running?{running:!0}:void 0})})),app.get("/api/claude/update-status",((E,p)=>{p.json(_claudeUpgrade)})),app.post("/api/claude/update",checkAuth,(async(E,p)=>{if(_claudeUpgrade.running)return p.json({ok:!0,started:!1,message:"\u5347\u7EA7\u5DF2\u5728\u8FDB\u884C\u4E2D"});_claudeUpgrade={running:!0,ok:null,error:null,version:null,startedAt:Date.now()},p.json({ok:!0,started:!0,message:"claude \u5347\u7EA7\u5DF2\u5F00\u59CB\uFF0C\u53EF\u80FD\u9700\u8981\u51E0\u5206\u949F..."}),(async()=>{try{await execAsync(getClaudeUpgradeCmd(),{encoding:"utf8",timeout:900*1e3}),resetClaudeLauncher();const l=await getClaudeVersion();if(!l)throw new Error("\u5347\u7EA7\u540E claude --version \u8DD1\u4E0D\u8D77\u6765\uFF08\u53EF\u80FD\u5E73\u53F0\u4E8C\u8FDB\u5236\u6CA1\u88C5\u597D\uFF0C\u8BF7 SSH \u624B\u52A8 npm i -g @anthropic-ai/claude-code@latest --include=optional\uFF09");_claudeUpgrade={running:!1,ok:!0,error:null,version:l.version,startedAt:_claudeUpgrade.startedAt},console.log("[myhi] claude \u5347\u7EA7\u5B8C\u6210 ->",l.version)}catch(l){const r=(l.stderr||l.stdout||l.message||"").split(`
|
|
399
399
|
`).filter((e=>e.trim())).slice(-6).join(`
|
|
400
400
|
`);_claudeUpgrade={running:!1,ok:!1,error:r,version:null,startedAt:_claudeUpgrade.startedAt},console.error("[myhi] claude \u5347\u7EA7\u5931\u8D25:",r)}})()})),app.post("/api/git/push",checkAuth,express.json(),(async(E,p)=>{const{sessionId:l,url:r,username:e,password:s,message:u,branch:d}=E.body||{};if(!l||!u)return p.status(400).json({ok:!1,error:"\u7F3A\u5C11\u53C2\u6570"});const o=manager.get(l);if(!o)return p.status(404).json({ok:!1,error:"\u4F1A\u8BDD\u4E0D\u5B58\u5728"});const i=o.cwd||process.cwd(),a=process.platform==="win32",t="git";function n(f,h={}){return new Promise(((c,v)=>{const m={...process.env,GIT_TERMINAL_PROMPT:"0"},b=(0,external_path_.join)(i,".gitconfig");(0,external_fs_.existsSync)(b)&&(m.GIT_CONFIG_GLOBAL=b);const _=(0,external_child_process_namespaceObject.spawn)(t,f,{cwd:i,env:m,timeout:6e4});let g="",w="";_.stdout.on("data",(k=>g+=k)),_.stderr.on("data",(k=>w+=k)),_.on("close",(k=>k===0?c(g.trim()):v(new Error(w.trim()||`exit ${k}`)))),_.on("error",v)}))}try{try{await n(["rev-parse","--git-dir"])}catch{await n(["init"])}if(r&&e&&s){const c=new URL(r);c.username=encodeURIComponent(e),c.password=encodeURIComponent(s);const v=c.toString(),m=(0,external_path_.join)(i,".git-credentials");(0,external_fs_.writeFileSync)(m,v+`
|
|
401
401
|
`,{mode:384});const b=(0,external_path_.join)(i,".gitconfig"),_=`[credential]
|
|
@@ -406,12 +406,7 @@ Content-Length: `+C+`\r
|
|
|
406
406
|
`;(0,external_fs_.writeFileSync)(b,_,{mode:384});try{await n(["remote","remove","origin"])}catch{}await n(["remote","add","origin",r])}if(await n(["add","-A"]),!await n(["status","--porcelain"]))return p.json({ok:!0,message:"\u6CA1\u6709\u9700\u8981\u63D0\u4EA4\u7684\u53D8\u66F4",pushed:!1});await n(["commit","-m",u]);const f=d||"main";try{await n(["push","-u","origin",f])}catch(c){if(c.message.includes("src refspec")||c.message.includes("does not match"))await n(["checkout","-b",f]),await n(["push","-u","origin",f]);else throw c}const h=await n(["log","--oneline","-1"]);p.json({ok:!0,message:`\u5DF2\u63D0\u4EA4\u5E76\u63A8\u9001: ${h}`,pushed:!0})}catch(f){p.json({ok:!1,error:f.message})}})),app.get("/api/git/info",checkAuth,((E,p)=>{const l=E.query.sessionId,r=l?manager.get(l):null;if(!r)return p.json({});const e=r.cwd||process.cwd();try{const s=(0,external_child_process_namespaceObject.execSync)("git remote get-url origin",{cwd:e,encoding:"utf8",timeout:5e3}).trim(),u=(0,external_child_process_namespaceObject.execSync)("git branch --show-current",{cwd:e,encoding:"utf8",timeout:5e3}).trim();let d="";const o=(0,external_path_.join)(e,".gitconfig");if((0,external_fs_.existsSync)(o)){const i=(0,external_fs_.readFileSync)(o,"utf8").match(/name\s*=\s*(.+)/);i&&(d=i[1].trim())}p.json({url:s,branch:u||"main",username:d})}catch{p.json({})}}));function skillFilePath(E,p){const l=p.split(":");return(0,external_path_.join)(E,...l.slice(0,-1),l[l.length-1]+".md")}function taskCwdFromTitle(E,p){const l=p?(0,external_path_.resolve)((0,external_path_.normalize)(p)):process.env.MYHI_CWD||process.cwd(),r=String(E||"").trim().replace(/[\\/:*?"<>|]+/g,"_").replace(/^\.+/,"").slice(0,80).trim();return r?(0,external_path_.join)(l,r):l}app.get("/api/skills",checkAuth,((E,p)=>{const l=E.query.sessionId,e=(l?manager.get(l):null)?.cwd||process.cwd(),s=[],u=E.query.detail==="1";function d(o,i,a=""){if((0,external_fs_.existsSync)(o))try{for(const t of(0,external_fs_.readdirSync)(o,{withFileTypes:!0})){if(t.isDirectory()){d((0,external_path_.join)(o,t.name),i,a?`${a}:${t.name}`:t.name);continue}if(!t.name.endsWith(".md"))continue;const n=t.name.replace(/\.md$/,""),f=a?`${a}:${n}`:n,h=(0,external_path_.join)(o,t.name);try{const c=(0,external_fs_.readFileSync)(h,"utf8"),m=(c.split(`
|
|
407
407
|
`).find((_=>_.trim()))||"").replace(/^#+\s*/,"").trim().slice(0,60),b={name:f,desc:m,source:i};u&&(b.content=c),s.push(b)}catch{}}}catch{}}d((0,external_path_.join)(e,".claude","commands"),"project"),d((0,external_path_.join)((0,external_os_.homedir)(),".claude","commands"),"user"),d(__nccwpck_require__.ab+"builtin-skills","builtin"),p.json({skills:s})})),app.get("/api/skills/content",checkAuth,((E,p)=>{const{name:l,sessionId:r}=E.query;if(!l)return p.status(400).json({error:"\u7F3A\u5C11 name"});const s=(r?manager.get(r):null)?.cwd||process.cwd(),u=[{dir:(0,external_path_.join)(s,".claude","commands"),source:"project"},{dir:(0,external_path_.join)((0,external_os_.homedir)(),".claude","commands"),source:"user"},{dir:__nccwpck_require__.ab+"builtin-skills",source:"builtin"}];for(const{dir:d,source:o}of u){const i=skillFilePath(d,l);if((0,external_fs_.existsSync)(i))try{return p.json({content:(0,external_fs_.readFileSync)(i,"utf8"),source:o})}catch{}}p.status(404).json({error:"\u6280\u80FD\u4E0D\u5B58\u5728"})})),app.post("/api/skills",checkAuth,express.json(),((E,p)=>{const{name:l,content:r,scope:e}=E.body||{};if(!l||!r)return p.status(400).json({error:"\u7F3A\u5C11 name \u6216 content"});if(!/^[\w:.-]+$/.test(l))return p.status(400).json({error:"\u540D\u79F0\u53EA\u80FD\u5305\u542B\u5B57\u6BCD\u3001\u6570\u5B57\u3001-\u3001_\u3001.\u3001:"});const s=E.query.sessionId,d=(s?manager.get(s):null)?.cwd||process.cwd(),o=e==="project"?(0,external_path_.join)(d,".claude","commands"):(0,external_path_.join)((0,external_os_.homedir)(),".claude","commands"),i=skillFilePath(o,l);try{(0,external_fs_.mkdirSync)((0,external_path_.dirname)(i),{recursive:!0}),(0,external_fs_.writeFileSync)(i,r,"utf8"),p.json({ok:!0,path:i})}catch(a){p.status(500).json({error:a.message})}})),app.delete("/api/skills",checkAuth,express.json(),((E,p)=>{const{name:l,scope:r}=E.body||{};if(!l)return p.status(400).json({error:"\u7F3A\u5C11 name"});const e=E.query.sessionId,u=(e?manager.get(e):null)?.cwd||process.cwd(),d=r==="project"?(0,external_path_.join)(u,".claude","commands"):(0,external_path_.join)((0,external_os_.homedir)(),".claude","commands"),o=skillFilePath(d,l);try{(0,external_fs_.existsSync)(o)&&(0,external_fs_.unlinkSync)(o),p.json({ok:!0})}catch(i){p.status(500).json({error:i.message})}})),app.post("/api/skills/scan-git",checkAuth,express.json(),((E,p)=>{const{url:l,username:r,password:e}=E.body||{};if(!l)return p.status(400).json({error:"\u7F3A\u5C11 Git \u4ED3\u5E93\u5730\u5740"});let s=l;if(r&&e&&/^https?:\/\//.test(l))try{const t=new URL(l);t.username=encodeURIComponent(r),t.password=encodeURIComponent(e),s=t.toString()}catch{}const u=(0,external_path_.join)((0,external_os_.tmpdir)(),"myhi-skill-"+Date.now());try{(0,external_child_process_namespaceObject.execSync)(`git clone --depth 1 "${s}" "${u}"`,{timeout:6e4,stdio:"pipe",encoding:"utf8",env:{...process.env,GIT_TERMINAL_PROMPT:"0"}})}catch(t){const n=(t.stderr||"").trim(),f=(t.stdout||"").trim(),v=(n||f||t.message||"\u672A\u77E5\u9519\u8BEF").split(`
|
|
408
408
|
`).filter((m=>m.trim())).slice(-3).join(" ").slice(0,200);return p.status(400).json({error:"\u514B\u9686\u5931\u8D25: "+v})}const d=[];function o(t,n=""){if((0,external_fs_.existsSync)(t))try{for(const f of(0,external_fs_.readdirSync)(t,{withFileTypes:!0})){if(f.name.startsWith("."))continue;if(f.isDirectory()){o((0,external_path_.join)(t,f.name),n?`${n}:${f.name}`:f.name);continue}if(!f.name.endsWith(".md")||["README.MD","CHANGELOG.MD","LICENSE.MD","CONTRIBUTING.MD"].includes(f.name.toUpperCase()))continue;const h=f.name.replace(/\.md$/,""),c=n?`${n}:${h}`:h,v=(0,external_path_.join)(t,f.name);try{const m=(0,external_fs_.readFileSync)(v,"utf8"),_=(m.split(`
|
|
409
|
-
`).find((g=>g.trim()))||"").replace(/^#+\s*/,"").trim().slice(0,80);d.push({name:c,desc:_,content:m})}catch{}}}catch{}}const i=(0,external_path_.join)(u,"commands"),a=(0,external_path_.join)(u,".claude","commands");(0,external_fs_.existsSync)(i)?o(i):(0,external_fs_.existsSync)(a)?o(a):o(u);try{(0,external_child_process_namespaceObject.execSync)(process.platform==="win32"?`rmdir /s /q ${JSON.stringify(u)}`:`rm -rf ${JSON.stringify(u)}`,{timeout:1e4,stdio:"pipe"})}catch{}if(!d.length)return p.json({skills:[],message:"\u4ED3\u5E93\u4E2D\u672A\u627E\u5230\u6280\u80FD\u6587\u4EF6(.md)"});p.json({skills:d})})),app.get("/api/memory",checkAuth,((E,p)=>{const l=E.query.sessionId,r=E.query.scope==="all"?"all":"project",e=l?manager.get(l):null;if(!e)return p.json({files:[],scope:r});const s=e.cwd||process.cwd(),u=String(s).replace(/[^a-zA-Z0-9]/g,"-"),d=[],o=(0,external_path_.join)(s,"CLAUDE.md");if((0,external_fs_.existsSync)(o))try{d.push({name:"CLAUDE.md",content:(0,external_fs_.readFileSync)(o,"utf8")})}catch{}const i=(0,external_path_.join)(s,".claude","settings.json");if((0,external_fs_.existsSync)(i))try{d.push({name:".claude/settings.json",content:(0,external_fs_.readFileSync)(i,"utf8")})}catch{}if(r==="all"){const n=(0,external_path_.join)((0,external_os_.homedir)(),".claude","CLAUDE.md");if((0,external_fs_.existsSync)(n))try{d.push({name:"~/.claude/CLAUDE.md",content:(0,external_fs_.readFileSync)(n,"utf8")})}catch{}}const a=[(0,external_path_.join)((0,external_os_.homedir)(),".claude","memory")],t=(0,external_path_.join)((0,external_os_.homedir)(),".claude","projects");if((0,external_fs_.existsSync)(t))try{for(const n of(0,external_fs_.readdirSync)(t)){if(r!=="all"&&n!==u)continue;const f=(0,external_path_.join)(t,n,"memory");if((0,external_fs_.existsSync)(f)){const h=(0,external_path_.join)(t,n,"CLAUDE.md");if((0,external_fs_.existsSync)(h))try{d.push({name:`.claude/projects/${n}/CLAUDE.md`,content:(0,external_fs_.readFileSync)(h,"utf8")})}catch{}try{for(const c of(0,external_fs_.readdirSync)(f)){if(!c.endsWith(".md"))continue;const v=(0,external_path_.join)(f,c);try{d.push({name:`memory/${c}`,content:(0,external_fs_.readFileSync)(v,"utf8")})}catch{}}}catch{}}}}catch{}if(r==="all"){for(const n of a)if((0,external_fs_.existsSync)(n))try{for(const f of(0,external_fs_.readdirSync)(n)){if(!f.endsWith(".md"))continue;const h=(0,external_path_.join)(n,f);try{d.push({name:`~/.claude/memory/${f}`,content:(0,external_fs_.readFileSync)(h,"utf8")})}catch{}}}catch{}}p.json({files:d,scope:r})}));const _SAFE_MD=/^[\w.\- ]+\.md$/;function memoryTarget(E,p){const l=(0,external_os_.homedir)();if(typeof p!="string"||p.includes(".."))return null;if(p.startsWith("memory/")){const e=p.slice(7);if(!_SAFE_MD.test(e))return null;const s=String(E).replace(/[^a-zA-Z0-9]/g,"-");return{path:(0,external_path_.join)(l,".claude","projects",s,"memory",e),deletable:!0}}if(p.startsWith("~/.claude/memory/")){const e=p.slice(17);return _SAFE_MD.test(e)?{path:(0,external_path_.join)(l,".claude","memory",e),deletable:!0}:null}if(p==="~/.claude/CLAUDE.md")return{path:(0,external_path_.join)(l,".claude","CLAUDE.md"),deletable:!1};if(p==="CLAUDE.md")return{path:(0,external_path_.join)(E,"CLAUDE.md"),deletable:!1};if(p===".claude/settings.json")return{path:(0,external_path_.join)(E,".claude","settings.json"),deletable:!1};const r=p.match(/^\.claude\/projects\/([\w.\-]+)\/CLAUDE\.md$/);return r?{path:(0,external_path_.join)(l,".claude","projects",r[1],"CLAUDE.md"),deletable:!1}:null}app.post("/api/memory/save",checkAuth,express.json(),((E,p)=>{const{sessionId:l,name:r,content:e}=E.body||{},s=l?manager.get(l):null,u=memoryTarget(s?.cwd||process.cwd(),r);if(!u)return p.status(400).json({error:"\u4E0D\u652F\u6301\u7F16\u8F91\u8BE5\u6587\u4EF6"});if(typeof e!="string")return p.status(400).json({error:"\u7F3A\u5C11\u5185\u5BB9"});try{(0,external_fs_.mkdirSync)((0,external_path_.dirname)(u.path),{recursive:!0}),(0,external_fs_.writeFileSync)(u.path,e,"utf8"),p.json({ok:!0})}catch(d){p.status(500).json({error:d.message})}})),app.post("/api/memory/delete",checkAuth,express.json(),((E,p)=>{const{sessionId:l,name:r}=E.body||{},e=l?manager.get(l):null,s=memoryTarget(e?.cwd||process.cwd(),r);if(!s||!s.deletable)return p.status(400).json({error:"\u8BE5\u6587\u4EF6\u4E0D\u5141\u8BB8\u5220\u9664\uFF08\u53EA\u5141\u8BB8\u5220 memory/*.md\uFF09"});try{(0,external_fs_.existsSync)(s.path)&&(0,external_fs_.unlinkSync)(s.path),p.json({ok:!0})}catch(u){p.status(500).json({error:u.message})}}));const _adminTokens=new Set,SERVER_START_TIME=Date.now();function checkAdminAuth(E,p,l){const r=E.headers.authorization;if(r&&r.startsWith("Bearer ")&&_adminTokens.has(r.slice(7)))return l();p.status(401).json({error:"\u672A\u6388\u6743"})}app.post("/api/admin/login",express.json(),((E,p)=>{const{password:l}=E.body||{};if(l===PASSWORD){const r=(0,external_crypto_.randomBytes)(16).toString("hex");return _adminTokens.add(r),p.json({ok:!0,token:r})}p.json({ok:!1,error:"\u5BC6\u7801\u9519\u8BEF"})})),app.get("/api/admin/status",checkAdminAuth,(async(E,p)=>{const l=await checkLatestVersion();loadUsers();const r=[],e=new Set;for(const[,u]of io.sockets.sockets)!u.data.userName||e.has(u.data.userName)||(e.add(u.data.userName),r.push({name:u.data.userName,role:u.data.role||"viewer"}));const s=manager.listSessions().filter((u=>u.alive!==!1)).map((u=>({id:u.id,title:u.title,mode:u.mode||"pty",owner:u.owner,viewers:u.viewerCount||0})));p.json({version:{current:
|
|
410
|
-
`).filter((e=>e.trim())).slice(-5).join(`
|
|
411
|
-
`);console.error("[myhi] \u5347\u7EA7\u5931\u8D25:",r),p.status(500).json({ok:!1,error:`npm install \u5931\u8D25\uFF1A
|
|
412
|
-
`+r+`
|
|
413
|
-
|
|
414
|
-
\u8BF7\u767B\u5F55\u670D\u52A1\u5668\u624B\u52A8\u6267\u884C sudo npm i -g @wendongfly/myhi@latest \u540E\u518D\u91CD\u542F`})}})),app.post("/api/admin/restart",checkAdminAuth,((E,p)=>{p.json({ok:!0}),setTimeout((()=>restartService()),300)})),app.post("/api/user/password",checkAuth,express.json(),((E,p)=>{const l=getUserInfo(E.headers.cookie),{oldPassword:r,newPassword:e}=E.body||{};if(!r||!e)return p.json({ok:!1,error:"\u539F\u5BC6\u7801\u548C\u65B0\u5BC6\u7801\u90FD\u5FC5\u586B"});if(e.length<4)return p.json({ok:!1,error:"\u65B0\u5BC6\u7801\u81F3\u5C11 4 \u4F4D"});if(loadUsers(),!hasUsers())return p.json({ok:!1,error:"\u672C\u673A\u672A\u914D\u7F6E\u6210\u5458\u7528\u6237\uFF08\u5355\u7BA1\u7406\u5458\u6A21\u5F0F\u65E0\u81EA\u52A9\u6539\u5BC6\uFF09"});if(e===PASSWORD)return p.json({ok:!1,error:"\u65B0\u5BC6\u7801\u4E0D\u80FD\u4E0E\u7BA1\u7406\u5BC6\u7801\u91CD\u590D"});const s=changeUserPassword(r,e,l?.name);s.ok&&console.log(`[\u7528\u6237] ${l?.name} \u4FEE\u6539\u4E86\u81EA\u5DF1\u7684\u5BC6\u7801`),p.json(s)})),app.post("/api/admin/user",checkAdminAuth,express.json(),((E,p)=>{const{password:l,name:r,dir:e}=E.body||{};if(!l||!r||!e)return p.json({ok:!1,error:"\u5BC6\u7801\u3001\u540D\u79F0\u3001\u76EE\u5F55\u90FD\u5FC5\u586B"});loadUsers(),addUser(l,r,e);try{(0,external_fs_.mkdirSync)(e,{recursive:!0});const s=(0,external_path_.join)(e,".gitconfig"),u=(0,external_path_.join)(e,".git-credentials");(0,external_fs_.existsSync)(s)||(0,external_fs_.writeFileSync)(s,`[credential]
|
|
409
|
+
`).find((g=>g.trim()))||"").replace(/^#+\s*/,"").trim().slice(0,80);d.push({name:c,desc:_,content:m})}catch{}}}catch{}}const i=(0,external_path_.join)(u,"commands"),a=(0,external_path_.join)(u,".claude","commands");(0,external_fs_.existsSync)(i)?o(i):(0,external_fs_.existsSync)(a)?o(a):o(u);try{(0,external_child_process_namespaceObject.execSync)(process.platform==="win32"?`rmdir /s /q ${JSON.stringify(u)}`:`rm -rf ${JSON.stringify(u)}`,{timeout:1e4,stdio:"pipe"})}catch{}if(!d.length)return p.json({skills:[],message:"\u4ED3\u5E93\u4E2D\u672A\u627E\u5230\u6280\u80FD\u6587\u4EF6(.md)"});p.json({skills:d})})),app.get("/api/memory",checkAuth,((E,p)=>{const l=E.query.sessionId,r=E.query.scope==="all"?"all":"project",e=l?manager.get(l):null;if(!e)return p.json({files:[],scope:r});const s=e.cwd||process.cwd(),u=String(s).replace(/[^a-zA-Z0-9]/g,"-"),d=[],o=(0,external_path_.join)(s,"CLAUDE.md");if((0,external_fs_.existsSync)(o))try{d.push({name:"CLAUDE.md",content:(0,external_fs_.readFileSync)(o,"utf8")})}catch{}const i=(0,external_path_.join)(s,".claude","settings.json");if((0,external_fs_.existsSync)(i))try{d.push({name:".claude/settings.json",content:(0,external_fs_.readFileSync)(i,"utf8")})}catch{}if(r==="all"){const n=(0,external_path_.join)((0,external_os_.homedir)(),".claude","CLAUDE.md");if((0,external_fs_.existsSync)(n))try{d.push({name:"~/.claude/CLAUDE.md",content:(0,external_fs_.readFileSync)(n,"utf8")})}catch{}}const a=[(0,external_path_.join)((0,external_os_.homedir)(),".claude","memory")],t=(0,external_path_.join)((0,external_os_.homedir)(),".claude","projects");if((0,external_fs_.existsSync)(t))try{for(const n of(0,external_fs_.readdirSync)(t)){if(r!=="all"&&n!==u)continue;const f=(0,external_path_.join)(t,n,"memory");if((0,external_fs_.existsSync)(f)){const h=(0,external_path_.join)(t,n,"CLAUDE.md");if((0,external_fs_.existsSync)(h))try{d.push({name:`.claude/projects/${n}/CLAUDE.md`,content:(0,external_fs_.readFileSync)(h,"utf8")})}catch{}try{for(const c of(0,external_fs_.readdirSync)(f)){if(!c.endsWith(".md"))continue;const v=(0,external_path_.join)(f,c);try{d.push({name:`memory/${c}`,content:(0,external_fs_.readFileSync)(v,"utf8")})}catch{}}}catch{}}}}catch{}if(r==="all"){for(const n of a)if((0,external_fs_.existsSync)(n))try{for(const f of(0,external_fs_.readdirSync)(n)){if(!f.endsWith(".md"))continue;const h=(0,external_path_.join)(n,f);try{d.push({name:`~/.claude/memory/${f}`,content:(0,external_fs_.readFileSync)(h,"utf8")})}catch{}}}catch{}}p.json({files:d,scope:r})}));const _SAFE_MD=/^[\w.\- ]+\.md$/;function memoryTarget(E,p){const l=(0,external_os_.homedir)();if(typeof p!="string"||p.includes(".."))return null;if(p.startsWith("memory/")){const e=p.slice(7);if(!_SAFE_MD.test(e))return null;const s=String(E).replace(/[^a-zA-Z0-9]/g,"-");return{path:(0,external_path_.join)(l,".claude","projects",s,"memory",e),deletable:!0}}if(p.startsWith("~/.claude/memory/")){const e=p.slice(17);return _SAFE_MD.test(e)?{path:(0,external_path_.join)(l,".claude","memory",e),deletable:!0}:null}if(p==="~/.claude/CLAUDE.md")return{path:(0,external_path_.join)(l,".claude","CLAUDE.md"),deletable:!1};if(p==="CLAUDE.md")return{path:(0,external_path_.join)(E,"CLAUDE.md"),deletable:!1};if(p===".claude/settings.json")return{path:(0,external_path_.join)(E,".claude","settings.json"),deletable:!1};const r=p.match(/^\.claude\/projects\/([\w.\-]+)\/CLAUDE\.md$/);return r?{path:(0,external_path_.join)(l,".claude","projects",r[1],"CLAUDE.md"),deletable:!1}:null}app.post("/api/memory/save",checkAuth,express.json(),((E,p)=>{const{sessionId:l,name:r,content:e}=E.body||{},s=l?manager.get(l):null,u=memoryTarget(s?.cwd||process.cwd(),r);if(!u)return p.status(400).json({error:"\u4E0D\u652F\u6301\u7F16\u8F91\u8BE5\u6587\u4EF6"});if(typeof e!="string")return p.status(400).json({error:"\u7F3A\u5C11\u5185\u5BB9"});try{(0,external_fs_.mkdirSync)((0,external_path_.dirname)(u.path),{recursive:!0}),(0,external_fs_.writeFileSync)(u.path,e,"utf8"),p.json({ok:!0})}catch(d){p.status(500).json({error:d.message})}})),app.post("/api/memory/delete",checkAuth,express.json(),((E,p)=>{const{sessionId:l,name:r}=E.body||{},e=l?manager.get(l):null,s=memoryTarget(e?.cwd||process.cwd(),r);if(!s||!s.deletable)return p.status(400).json({error:"\u8BE5\u6587\u4EF6\u4E0D\u5141\u8BB8\u5220\u9664\uFF08\u53EA\u5141\u8BB8\u5220 memory/*.md\uFF09"});try{(0,external_fs_.existsSync)(s.path)&&(0,external_fs_.unlinkSync)(s.path),p.json({ok:!0})}catch(u){p.status(500).json({error:u.message})}}));const _adminTokens=new Set,SERVER_START_TIME=Date.now();function checkAdminAuth(E,p,l){const r=E.headers.authorization;if(r&&r.startsWith("Bearer ")&&_adminTokens.has(r.slice(7)))return l();p.status(401).json({error:"\u672A\u6388\u6743"})}app.post("/api/admin/login",express.json(),((E,p)=>{const{password:l}=E.body||{};if(l===PASSWORD){const r=(0,external_crypto_.randomBytes)(16).toString("hex");return _adminTokens.add(r),p.json({ok:!0,token:r})}p.json({ok:!1,error:"\u5BC6\u7801\u9519\u8BEF"})})),app.get("/api/admin/status",checkAdminAuth,(async(E,p)=>{const l=await checkLatestVersion();loadUsers();const r=[],e=new Set;for(const[,u]of io.sockets.sockets)!u.data.userName||e.has(u.data.userName)||(e.add(u.data.userName),r.push({name:u.data.userName,role:u.data.role||"viewer"}));const s=manager.listSessions().filter((u=>u.alive!==!1)).map((u=>({id:u.id,title:u.title,mode:u.mode||"pty",owner:u.owner,viewers:u.viewerCount||0})));p.json({version:{current:PKG_VERSION,latest:l||PKG_VERSION,updateAvailable:l&&l!==PKG_VERSION},onlineUsers:r,sessions:s,users:listUsers(),uptime:Math.floor((Date.now()-SERVER_START_TIME)/1e3)})})),app.post("/api/admin/upgrade",checkAdminAuth,express.json(),handleUpgrade),app.post("/api/admin/restart",checkAdminAuth,((E,p)=>{p.json({ok:!0}),setTimeout((()=>restartService()),300)})),app.post("/api/user/password",checkAuth,express.json(),((E,p)=>{const l=getUserInfo(E.headers.cookie),{oldPassword:r,newPassword:e}=E.body||{};if(!r||!e)return p.json({ok:!1,error:"\u539F\u5BC6\u7801\u548C\u65B0\u5BC6\u7801\u90FD\u5FC5\u586B"});if(e.length<4)return p.json({ok:!1,error:"\u65B0\u5BC6\u7801\u81F3\u5C11 4 \u4F4D"});if(loadUsers(),!hasUsers())return p.json({ok:!1,error:"\u672C\u673A\u672A\u914D\u7F6E\u6210\u5458\u7528\u6237\uFF08\u5355\u7BA1\u7406\u5458\u6A21\u5F0F\u65E0\u81EA\u52A9\u6539\u5BC6\uFF09"});if(e===PASSWORD)return p.json({ok:!1,error:"\u65B0\u5BC6\u7801\u4E0D\u80FD\u4E0E\u7BA1\u7406\u5BC6\u7801\u91CD\u590D"});const s=changeUserPassword(r,e,l?.name);s.ok&&console.log(`[\u7528\u6237] ${l?.name} \u4FEE\u6539\u4E86\u81EA\u5DF1\u7684\u5BC6\u7801`),p.json(s)})),app.post("/api/admin/user",checkAdminAuth,express.json(),((E,p)=>{const{password:l,name:r,dir:e}=E.body||{};if(!l||!r||!e)return p.json({ok:!1,error:"\u5BC6\u7801\u3001\u540D\u79F0\u3001\u76EE\u5F55\u90FD\u5FC5\u586B"});loadUsers(),addUser(l,r,e);try{(0,external_fs_.mkdirSync)(e,{recursive:!0});const s=(0,external_path_.join)(e,".gitconfig"),u=(0,external_path_.join)(e,".git-credentials");(0,external_fs_.existsSync)(s)||(0,external_fs_.writeFileSync)(s,`[credential]
|
|
415
410
|
helper = store --file ${u.replace(/\\/g,"/")}
|
|
416
411
|
[user]
|
|
417
412
|
name = ${r}
|
|
@@ -427,7 +422,7 @@ ${l}
|
|
|
427
422
|
3. \u8BED\u8A00\u81EA\u7136\u6D41\u7545\uFF0C\u4E0D\u9700\u8981\u5206\u6761\u5217\u9879\uFF0C\u4FDD\u6301\u8FDE\u8D2F\u6BB5\u843D
|
|
428
423
|
4. \u4FDD\u7559\u6240\u6709\u5177\u4F53\u7684\u6570\u5B57\u3001\u540D\u79F0\u7B49\u5173\u952E\u4FE1\u606F
|
|
429
424
|
|
|
430
|
-
\u53EA\u8F93\u51FA\u6574\u7406\u540E\u7684\u6587\u5B57\uFF0C\u4E0D\u9700\u8981\u4EFB\u4F55\u89E3\u91CA\u6216\u6807\u9898\u3002\u5FC5\u987B\u7528\u4E2D\u6587\u8F93\u51FA\u3002`;try{const e=await new Promise(((s,u)=>{let d="",o="";const i=process.platform==="win32"?(0,external_fs_.existsSync)((0,external_path_.join)((0,external_os_.homedir)(),"AppData","Roaming","npm","claude.cmd"))?(0,external_path_.join)((0,external_os_.homedir)(),"AppData","Roaming","npm","claude.cmd"):(0,external_path_.join)((0,external_os_.homedir)(),".claude","local","claude.cmd"):"claude",a=process.platform==="win32"?process.env.ComSpec||"cmd.exe":i,t=process.platform==="win32"?["/c",i,"--print"]:["--print"],n=(0,external_child_process_namespaceObject.spawn)(a,t,{stdio:["pipe","pipe","pipe"],env:{...process.env,NO_COLOR:"1"},timeout:9e4});n.stdin.write(r,"utf8"),n.stdin.end(),n.stdout.on("data",(f=>{d+=f.toString()})),n.stderr.on("data",(f=>{o+=f.toString()})),n.on("close",(f=>{f!==0&&!d.trim()?u(new Error(o||`exit ${f}`)):s(d.trim())})),n.on("error",u)}));p.json({requirement:e})}catch(e){p.status(500).json({error:e.message})}})),app.get("/qr/:sessionId",checkAuth,(async(E,p)=>{const l=getTailscaleIP(),r=(0,external_crypto_.randomBytes)(16).toString("hex");userSessions.set(r,{role:"admin",name:"\u626B\u7801\u7528\u6237",onetime:!0,lastUsed:Date.now()});const s=`${httpsServer?"https":"http"}://${l}:${PORT}/terminal/${E.params.sessionId}?token=${r}`;try{const u=await lib.toString(s,{type:"svg"});p.setHeader("Content-Type","image/svg+xml"),p.send(u)}catch(u){p.status(500).send(u.message)}})),io.use(((E,p)=>{const l=E.handshake.headers.cookie;if(hasValidSession(l)){const e=getUserInfo(l);return E.data.role=e?.role||"admin",E.data.userName=e?.name||"\u7528\u6237",E.data.dir=e?.dir||null,p()}const r=E.handshake.auth?.password;if(r){loadUsers();const e=lookupUser(r);if(e)return E.data.role="operator",E.data.userName=e.name,E.data.dir=e.dir,p()}if((E.handshake.auth?.token||E.handshake.query?.token)===TOKEN)return E.data.role="admin",E.data.userName="\u7BA1\u7406\u5458",p();p(new Error("\u672A\u6388\u6743"))}));const EXCLUSIVE_RELEASE_DELAY=120*1e3;let _exclusiveReleaseTimer=null;function checkExclusiveRelease(){if(!isExclusiveMode()||!activeUser)return;let E=!1;for(const[,p]of io.sockets.sockets)if(p.data.userName===activeUser.name){E=!0;break}if(E){_exclusiveReleaseTimer&&(clearTimeout(_exclusiveReleaseTimer),_exclusiveReleaseTimer=null);return}_exclusiveReleaseTimer||(_exclusiveReleaseTimer=setTimeout((()=>{if(_exclusiveReleaseTimer=null,!!activeUser){for(const[,p]of io.sockets.sockets)if(p.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 p of activeUser.tokens)userSessions.delete(p);activeUser=null}}),EXCLUSIVE_RELEASE_DELAY))}let _broadcastTimer=null;function broadcastSessions(){_broadcastTimer||(_broadcastTimer=setTimeout((()=>{_broadcastTimer=null;for(const[,E]of io.sockets.sockets)E.emit("sessions",manager.list(E.data.userName,E.data.role==="admin"))}),100))}io.on("connection",(E=>{const p=E.handshake.address?.replace("::ffff:","")||"?";console.log(`[\u8FDE\u63A5] ${E.data.userName}(${E.data.role}) \u4ECE ${p} \u63A5\u5165 id=${E.id}`),_exclusiveReleaseTimer&&activeUser&&E.data.userName===activeUser.name&&(clearTimeout(_exclusiveReleaseTimer),_exclusiveReleaseTimer=null);let l=null,r=null,e=null,s=null,u=null,d=null;function o(){l&&(l.isController(E.id)&&(l.releaseControl(),io.emit("control-changed",{sessionId:l.id,holder:null,holderName:null})),l.removeViewer(E.id),r&&l.off("data",r),e&&l.off("agent:message",e),s&&l.off("agent:busy",s),u&&l.off("agent:error",u),d&&l.off("draft-relayed",d),r=null,e=null,s=null,u=null,d=null)}E.on("join",(i=>{const a=manager.get(i);if(!a){E.emit("error",{message:`\u4F1A\u8BDD ${i} \u672A\u627E\u5230`});return}const t=l?.id===i;if(o(),l=a,l.addViewer(E.id,E.data.userName),a.mode==="agent"?(e=n=>E.emit("agent:message",n),l.on("agent:message",e),s=n=>E.emit("agent:busy",n),l.on("agent:busy",s),u=n=>E.emit("agent:error",n),l.on("agent:error",u),d=n=>E.emit("draft-relayed",n),l.on("draft-relayed",d)):(r=n=>E.emit("output",n),l.on("data",r),l.once("exit",(n=>{E.emit("session-exit",{code:n}),r&&l?.off("data",r)})),spawnLocalTerminal(i,a.title)),console.log(`[\u52A0\u5165] ${E.data.userName} \u52A0\u5165\u4F1A\u8BDD "${a.title}" (${i}) \u6A21\u5F0F=${a.mode||"pty"}${t?" [\u91CD\u65B0\u8BA2\u9605]":""}`),E.emit("joined",{...a.toJSON(),role:E.data.role}),!t){if(a.mode==="agent"){let n=null;try{const f=readTranscript(a.claudeSessionId,{offset:0,limit:500});f.ok&&f.messages.length&&(n=f.messages)}catch{}!n?.length&&a._history?.length&&(n=a._history),n?.length&&E.emit("agent:history",n)}else if(a._scrollback?.length){const n=a._scrollback;if(n.length<=4096)E.emit("output",n);else{let f=0;(function h(){f>=n.length||(E.emit("output",n.slice(f,f+4096)),f+=4096,setImmediate(h))})()}}}broadcastSessions()})),E.on("disconnect",(()=>{console.log(`[\u65AD\u5F00] ${E.data.userName} \u79BB\u5F00\u4F1A\u8BDD "${l?.title||"?"}" id=${E.id}`),o(),broadcastSessions(),checkExclusiveRelease(),manager.persist()})),E.on("agent:query",(async({prompt:i}={})=>{if(!l||l.mode!=="agent"){E.emit("agent:error",{message:"\u5F53\u524D\u4E0D\u662F Agent \u6A21\u5F0F\u4F1A\u8BDD"});return}if(!l.isController(E.id)){E.emit("control-denied",{reason:"\u4F60\u6CA1\u6709\u5F53\u524D\u4F1A\u8BDD\u7684\u63A7\u5236\u6743"});return}if(l.isBusy){E.emit("agent:error",{message:"\u6B63\u5728\u5904\u7406\u4E2D\uFF0C\u8BF7\u7B49\u5F85\u5B8C\u6210"});return}l.lastInputTime=Date.now();try{await l.query(i)}catch(a){console.error("[agent:query] \u9519\u8BEF:",a.message);try{E.connected&&E.emit("agent:error",{message:a.message})}catch{}}})),E.on("agent:interrupt",(()=>{!l||l.mode!=="agent"||(l.isController(E.id)&&(l.lastInputTime=Date.now()),l.interrupt())})),E.on("create-agent",((i,a)=>{if(!hasPermission(E.data.role,"operator")){typeof a=="function"&&a({ok:!1,error:"\u6CA1\u6709\u521B\u5EFA\u4F1A\u8BDD\u7684\u6743\u9650"});return}if(!i?.cwd&&i?.nestByTitle&&(i={...i,cwd:taskCwdFromTitle(i?.title,E.data.dir)}),E.data.dir){const n=(0,external_path_.resolve)((0,external_path_.normalize)(E.data.dir)),f=i?.cwd?(0,external_path_.resolve)((0,external_path_.normalize)(i.cwd)):null;if(f&&f!==n&&!f.startsWith(n+external_path_.sep)){typeof a=="function"&&a({ok:!1,error:"\u4E0D\u80FD\u5728\u7ED1\u5B9A\u76EE\u5F55\u4E4B\u5916\u521B\u5EFA\u4F1A\u8BDD"});return}f||(i={...i,cwd:n})}const t=i?.cwd||process.env.MYHI_CWD||process.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(n){typeof a=="function"&&a({ok:!1,error:`\u521B\u5EFA\u76EE\u5F55\u5931\u8D25: ${n.message}`});return}try{const n=manager.createAgent({...i,owner:E.data.userName,userDir:E.data.dir});console.log(`[\u4F1A\u8BDD] ${E.data.userName} \u521B\u5EFA Agent \u4F1A\u8BDD "${n.title}" (${n.id})`),broadcastSessions(),typeof a=="function"&&a({ok:!0,session:n.toJSON()})}catch(n){typeof a=="function"&&a({ok:!1,error:n.message})}})),E.on("input",(i=>{if(!(i==null||!l)&&l.mode!=="agent"){if(!l.isController(E.id)){E.emit("control-denied",{reason:"\u4F60\u6CA1\u6709\u5F53\u524D\u4F1A\u8BDD\u7684\u63A7\u5236\u6743"});return}l.lastInputTime=Date.now(),l.write(i)}})),E.on("take-control",(({sessionId:i}={})=>{const a=i?manager.get(i):l;if(a){if(!hasPermission(E.data.role,"operator")){E.emit("control-denied",{reason:"\u4F60\u7684\u89D2\u8272\u6CA1\u6709\u63A7\u5236\u6743\u9650"});return}if(a.controlHolder&&a.controlHolder!==E.id){const t=a.controlHolderName===E.data.userName;if(E.data.role!=="admin"&&!t){E.emit("control-denied",{reason:"\u5176\u4ED6\u7528\u6237\u6B63\u5728\u63A7\u5236\u4E2D\uFF0C\u8BF7\u7B49\u5F85\u91CA\u653E"});return}}a.takeControl(E.id,E.data.userName),console.log(`[\u63A7\u5236] ${E.data.userName} \u83B7\u53D6\u4F1A\u8BDD "${a.title}" \u7684\u63A7\u5236\u6743`),io.emit("control-changed",{sessionId:a.id,holder:E.id,holderName:E.data.userName})}})),E.on("release-control",(({sessionId:i}={})=>{const a=i?manager.get(i):l;!a||!a.isController(E.id)||(console.log(`[\u63A7\u5236] ${E.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}))})),E.on("set-mode",(({sessionId:i,mode:a}={})=>{const t=i?manager.get(i):l;if(t){if(t.mode==="agent"&&t.setPermissionMode){t.setPermissionMode(a);const n=t.permissionMode;n!==a&&console.log(`[\u6A21\u5F0F] set-mode \u6536\u5230\u65E0\u6548\u6A21\u5F0F "${a}"\uFF0C\u5DF2\u5F52\u4E00\u5316\u4E3A ${n}`),console.log(`[\u6A21\u5F0F] ${E.data.userName} \u5207\u6362\u4F1A\u8BDD "${t.title}" \u4E3A ${n}`),io.emit("mode-changed",{sessionId:t.id,mode:n})}else t.permissionMode=a,io.emit("mode-changed",{sessionId:t.id,mode:a});manager.persist()}})),E.on("rename",(({sessionId:i,title:a}={})=>{const t=i?manager.get(i):l;if(!(!t||!a)){t.title=a;try{syncSessionTitle(t.id,a)}catch{}io.emit("session-renamed",{sessionId:t.id,title:a}),broadcastSessions(),manager.persist()}})),E.on("set-tags",(({sessionId:i,tags:a}={})=>{const t=i?manager.get(i):l;!t||!Array.isArray(a)||(t.tags=[...new Set(a.map((n=>String(n).trim())).filter(Boolean).map((n=>n.slice(0,24))))].slice(0,12),io.emit("session-tagged",{sessionId:t.id,tags:t.tags}),broadcastSessions(),manager.persist())})),E.on("resize",(({cols:i,rows:a})=>l?.resize(i,a))),E.on("list",(()=>E.emit("sessions",manager.list(E.data.userName,E.data.role==="admin")))),E.on("dirs",((i,a)=>{if(typeof a!="function")return;const t=E.data.dir?(0,external_path_.resolve)((0,external_path_.normalize)(E.data.dir)):null,n=process.platform==="win32"?"\\":"/";let f=(i||t||process.env.MYHI_CWD||(0,external_os_.homedir)()).replace(/[/\\]+$/,"")||(0,external_os_.homedir)();process.platform==="win32"&&/^[A-Za-z]:$/.test(f)&&(f+="\\");const h=(0,external_path_.resolve)((0,external_path_.normalize)(f));t&&h!==t&&!h.startsWith(t+n)&&(f=t);try{const c=(0,external_fs_.readdirSync)(f,{withFileTypes:!0}).filter((_=>_.isDirectory()&&_.name!==".git")).map((_=>({name:_.name,path:f.replace(/[/\\]+$/,"")+n+_.name}))).sort(((_,g)=>_.name.localeCompare(g.name))),v=(0,external_path_.join)(f,".."),m=(0,external_path_.resolve)((0,external_path_.normalize)(v)),b=!t||m===t||m.startsWith(t+n);a({ok:!0,current:f,parent:b&&v!==f?v:null,dirs:c})}catch(c){a({ok:!1,error:c.message})}})),E.on("create",((i,a)=>{if(!hasPermission(E.data.role,"operator")){typeof a=="function"&&a({ok:!1,error:"\u6CA1\u6709\u521B\u5EFA\u4F1A\u8BDD\u7684\u6743\u9650"});return}if(!i?.cwd&&i?.nestByTitle&&(i={...i,cwd:taskCwdFromTitle(i?.title,E.data.dir)}),E.data.dir){const n=(0,external_path_.resolve)((0,external_path_.normalize)(E.data.dir)),f=i?.cwd?(0,external_path_.resolve)((0,external_path_.normalize)(i.cwd)):null;if(f&&f!==n&&!f.startsWith(n+external_path_.sep)){typeof a=="function"&&a({ok:!1,error:"\u4E0D\u80FD\u5728\u7ED1\u5B9A\u76EE\u5F55\u4E4B\u5916\u521B\u5EFA\u4F1A\u8BDD"});return}f||(i={...i,cwd:n})}const t=i?.cwd||process.env.MYHI_CWD||process.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(n){typeof a=="function"&&a({ok:!1,error:`\u521B\u5EFA\u76EE\u5F55\u5931\u8D25: ${n.message}`});return}try{const n=manager.create({...i,owner:E.data.userName,userDir:E.data.dir});console.log(`[\u4F1A\u8BDD] ${E.data.userName} \u521B\u5EFA PTY \u4F1A\u8BDD "${n.title}" (${n.id})`),broadcastSessions(),typeof a=="function"&&a({ok:!0,session:n.toJSON()})}catch(n){console.error("[\u521B\u5EFA] PTY \u542F\u52A8\u5931\u8D25:",n.message),typeof a=="function"&&a({ok:!1,error:n.message})}})),E.on("kill",(i=>{if(!hasPermission(E.data.role,"operator")){E.emit("error",{message:"\u6CA1\u6709\u5220\u9664\u4F1A\u8BDD\u7684\u6743\u9650"});return}console.log(`[\u4F1A\u8BDD] ${E.data.userName} \u5220\u9664\u4F1A\u8BDD ${i}`),manager.kill(i),_autoSpawned.delete(i),broadcastSessions()})),E.on("kick-user",(({socketId:i,sessionId:a}={},t)=>{if(!hasPermission(E.data.role,"admin")){typeof t=="function"&&t({ok:!1,error:"\u6CA1\u6709\u8E22\u51FA\u7528\u6237\u7684\u6743\u9650"});return}const n=io.sockets.sockets.get(i);if(!n){typeof t=="function"&&t({ok:!1,error:"\u76EE\u6807\u7528\u6237\u4E0D\u5728\u7EBF"});return}const f=n.data.userName||"\u672A\u77E5";console.log(`[\u8E22\u51FA] ${E.data.userName} \u8E22\u51FA\u4E86\u7528\u6237 ${f} (${i})`),n.emit("kicked",{reason:`\u4F60\u5DF2\u88AB\u7BA1\u7406\u5458 ${E.data.userName} \u8E22\u51FA`}),n.disconnect(!0),typeof t=="function"&&t({ok:!0,name:f})})),E.on("list-viewers",(({sessionId:i}={},a)=>{if(typeof a!="function")return;const t=i?manager.get(i):l;if(!t){a({ok:!1,error:"\u4F1A\u8BDD\u4E0D\u5B58\u5728"});return}const n=[];for(const f of t._viewers){const h=io.sockets.sockets.get(f);h&&n.push({socketId:f,userName:h.data.userName||"\u672A\u77E5",role:h.data.role||"viewer",isController:t.controlHolder===f})}a({ok:!0,viewers:n})}))}));const CONTROL_TIMEOUT=(Number(process.env.MYHI_CONTROL_TIMEOUT)||300)*1e3,CONTROL_SWEEP=Math.min(60*1e3,CONTROL_TIMEOUT);function shouldReleaseIdle(E){if(!E.controlHolder)return!1;if(!io.sockets.sockets.get(E.controlHolder))return!0;if(!E.lastInputTime||Date.now()-E.lastInputTime<=CONTROL_TIMEOUT||E.isBusy)return!1;for(const p of E._viewers||[])if(p!==E.controlHolder&&io.sockets.sockets.has(p))return!0;return!1}setInterval((()=>{for(const E of manager.listSessions()){shouldReleaseIdle(E)&&(E.releaseControl(),io.emit("control-changed",{sessionId:E.id,holder:null,holderName:null}));for(const p of E._viewers)io.sockets.sockets.has(p)||E.removeViewer(p)}}),CONTROL_SWEEP);function showStartupInfo(){PORT=(httpsServer||httpServer).address()?.port||PORT;const p=getTailscaleIP(),r=`${httpsServer?"https":"http"}://${p}:${PORT}`;console.log(`
|
|
425
|
+
\u53EA\u8F93\u51FA\u6574\u7406\u540E\u7684\u6587\u5B57\uFF0C\u4E0D\u9700\u8981\u4EFB\u4F55\u89E3\u91CA\u6216\u6807\u9898\u3002\u5FC5\u987B\u7528\u4E2D\u6587\u8F93\u51FA\u3002`;try{const e=await new Promise(((s,u)=>{let d="",o="";const i=process.platform==="win32"?(0,external_fs_.existsSync)((0,external_path_.join)((0,external_os_.homedir)(),"AppData","Roaming","npm","claude.cmd"))?(0,external_path_.join)((0,external_os_.homedir)(),"AppData","Roaming","npm","claude.cmd"):(0,external_path_.join)((0,external_os_.homedir)(),".claude","local","claude.cmd"):"claude",a=process.platform==="win32"?process.env.ComSpec||"cmd.exe":i,t=process.platform==="win32"?["/c",i,"--print"]:["--print"],n=(0,external_child_process_namespaceObject.spawn)(a,t,{stdio:["pipe","pipe","pipe"],env:{...process.env,NO_COLOR:"1"},timeout:9e4});n.stdin.write(r,"utf8"),n.stdin.end(),n.stdout.on("data",(f=>{d+=f.toString()})),n.stderr.on("data",(f=>{o+=f.toString()})),n.on("close",(f=>{f!==0&&!d.trim()?u(new Error(o||`exit ${f}`)):s(d.trim())})),n.on("error",u)}));p.json({requirement:e})}catch(e){p.status(500).json({error:e.message})}})),app.get("/qr/:sessionId",checkAuth,(async(E,p)=>{const l=getTailscaleIP(),r=(0,external_crypto_.randomBytes)(16).toString("hex");userSessions.set(r,{role:"admin",name:"\u626B\u7801\u7528\u6237",onetime:!0,lastUsed:Date.now()});const s=`${httpsServer?"https":"http"}://${l}:${PORT}/terminal/${E.params.sessionId}?token=${r}`;try{const u=await lib.toString(s,{type:"svg"});p.setHeader("Content-Type","image/svg+xml"),p.send(u)}catch(u){p.status(500).send(u.message)}})),io.use(((E,p)=>{const l=E.handshake.headers.cookie;if(hasValidSession(l)){const e=getUserInfo(l);return E.data.role=e?.role||"admin",E.data.userName=e?.name||"\u7528\u6237",E.data.dir=e?.dir||null,p()}const r=E.handshake.auth?.password;if(r){loadUsers();const e=lookupUser(r);if(e)return E.data.role="operator",E.data.userName=e.name,E.data.dir=e.dir,p()}if((E.handshake.auth?.token||E.handshake.query?.token)===TOKEN)return E.data.role="admin",E.data.userName="\u7BA1\u7406\u5458",p();p(new Error("\u672A\u6388\u6743"))}));const EXCLUSIVE_RELEASE_DELAY=120*1e3;let _exclusiveReleaseTimer=null;function checkExclusiveRelease(){if(!isExclusiveMode()||!activeUser)return;let E=!1;for(const[,p]of io.sockets.sockets)if(p.data.userName===activeUser.name){E=!0;break}if(E){_exclusiveReleaseTimer&&(clearTimeout(_exclusiveReleaseTimer),_exclusiveReleaseTimer=null);return}_exclusiveReleaseTimer||(_exclusiveReleaseTimer=setTimeout((()=>{if(_exclusiveReleaseTimer=null,!!activeUser){for(const[,p]of io.sockets.sockets)if(p.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 p of activeUser.tokens)userSessions.delete(p);activeUser=null}}),EXCLUSIVE_RELEASE_DELAY))}let _broadcastTimer=null;function broadcastSessions(){_broadcastTimer||(_broadcastTimer=setTimeout((()=>{_broadcastTimer=null;for(const[,E]of io.sockets.sockets)E.emit("sessions",manager.list(E.data.userName,E.data.role==="admin"))}),100))}io.on("connection",(E=>{const p=E.handshake.address?.replace("::ffff:","")||"?";console.log(`[\u8FDE\u63A5] ${E.data.userName}(${E.data.role}) \u4ECE ${p} \u63A5\u5165 id=${E.id}`),_exclusiveReleaseTimer&&activeUser&&E.data.userName===activeUser.name&&(clearTimeout(_exclusiveReleaseTimer),_exclusiveReleaseTimer=null);let l=null,r=null,e=null,s=null,u=null,d=null;function o(){l&&(l.isController(E.id)&&(l.releaseControl(),io.emit("control-changed",{sessionId:l.id,holder:null,holderName:null})),l.removeViewer(E.id),r&&l.off("data",r),e&&l.off("agent:message",e),s&&l.off("agent:busy",s),u&&l.off("agent:error",u),d&&l.off("draft-relayed",d),r=null,e=null,s=null,u=null,d=null)}E.on("join",(i=>{const a=manager.get(i);if(!a){E.emit("error",{message:`\u4F1A\u8BDD ${i} \u672A\u627E\u5230`});return}const t=l?.id===i;if(o(),l=a,l.addViewer(E.id,E.data.userName),a.mode==="agent"?(e=n=>E.emit("agent:message",n),l.on("agent:message",e),s=n=>E.emit("agent:busy",n),l.on("agent:busy",s),u=n=>E.emit("agent:error",n),l.on("agent:error",u),d=n=>E.emit("draft-relayed",n),l.on("draft-relayed",d)):(r=n=>E.emit("output",n),l.on("data",r),l.once("exit",(n=>{E.emit("session-exit",{code:n}),r&&l?.off("data",r)})),spawnLocalTerminal(i,a.title)),console.log(`[\u52A0\u5165] ${E.data.userName} \u52A0\u5165\u4F1A\u8BDD "${a.title}" (${i}) \u6A21\u5F0F=${a.mode||"pty"}${t?" [\u91CD\u65B0\u8BA2\u9605]":""}`),E.emit("joined",{...a.toJSON(),role:E.data.role}),!t){if(a.mode==="agent"){let n=null;try{const f=readTranscript(a.claudeSessionId,{offset:0,limit:500});f.ok&&f.messages.length&&(n=f.messages)}catch{}!n?.length&&a._history?.length&&(n=a._history),n?.length&&E.emit("agent:history",n)}else if(a._scrollback?.length){const n=a._scrollback;if(n.length<=4096)E.emit("output",n);else{let f=0;(function h(){f>=n.length||(E.emit("output",n.slice(f,f+4096)),f+=4096,setImmediate(h))})()}}}broadcastSessions()})),E.on("disconnect",(()=>{console.log(`[\u65AD\u5F00] ${E.data.userName} \u79BB\u5F00\u4F1A\u8BDD "${l?.title||"?"}" id=${E.id}`),o(),broadcastSessions(),checkExclusiveRelease(),manager.persist()})),E.on("agent:query",(async({prompt:i}={})=>{if(!l||l.mode!=="agent"){E.emit("agent:error",{message:"\u5F53\u524D\u4E0D\u662F Agent \u6A21\u5F0F\u4F1A\u8BDD"});return}if(!l.isController(E.id)){E.emit("control-denied",{reason:"\u4F60\u6CA1\u6709\u5F53\u524D\u4F1A\u8BDD\u7684\u63A7\u5236\u6743"});return}if(l.isBusy){E.emit("agent:error",{message:"\u6B63\u5728\u5904\u7406\u4E2D\uFF0C\u8BF7\u7B49\u5F85\u5B8C\u6210"});return}l.lastInputTime=Date.now();try{await l.query(i,{from:E.id})}catch(a){console.error("[agent:query] \u9519\u8BEF:",a.message);try{E.connected&&E.emit("agent:error",{message:a.message})}catch{}}})),E.on("agent:interrupt",(()=>{!l||l.mode!=="agent"||(l.isController(E.id)&&(l.lastInputTime=Date.now()),l.interrupt())})),E.on("create-agent",((i,a)=>{if(!hasPermission(E.data.role,"operator")){typeof a=="function"&&a({ok:!1,error:"\u6CA1\u6709\u521B\u5EFA\u4F1A\u8BDD\u7684\u6743\u9650"});return}if(!i?.cwd&&i?.nestByTitle&&(i={...i,cwd:taskCwdFromTitle(i?.title,E.data.dir)}),E.data.dir){const n=(0,external_path_.resolve)((0,external_path_.normalize)(E.data.dir)),f=i?.cwd?(0,external_path_.resolve)((0,external_path_.normalize)(i.cwd)):null;if(f&&f!==n&&!f.startsWith(n+external_path_.sep)){typeof a=="function"&&a({ok:!1,error:"\u4E0D\u80FD\u5728\u7ED1\u5B9A\u76EE\u5F55\u4E4B\u5916\u521B\u5EFA\u4F1A\u8BDD"});return}f||(i={...i,cwd:n})}const t=i?.cwd||process.env.MYHI_CWD||process.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(n){typeof a=="function"&&a({ok:!1,error:`\u521B\u5EFA\u76EE\u5F55\u5931\u8D25: ${n.message}`});return}try{const n=manager.createAgent({...i,owner:E.data.userName,userDir:E.data.dir});console.log(`[\u4F1A\u8BDD] ${E.data.userName} \u521B\u5EFA Agent \u4F1A\u8BDD "${n.title}" (${n.id})`),broadcastSessions(),typeof a=="function"&&a({ok:!0,session:n.toJSON()})}catch(n){typeof a=="function"&&a({ok:!1,error:n.message})}})),E.on("input",(i=>{if(!(i==null||!l)&&l.mode!=="agent"){if(!l.isController(E.id)){E.emit("control-denied",{reason:"\u4F60\u6CA1\u6709\u5F53\u524D\u4F1A\u8BDD\u7684\u63A7\u5236\u6743"});return}l.lastInputTime=Date.now(),l.write(i)}})),E.on("take-control",(({sessionId:i}={})=>{const a=i?manager.get(i):l;if(a){if(!hasPermission(E.data.role,"operator")){E.emit("control-denied",{reason:"\u4F60\u7684\u89D2\u8272\u6CA1\u6709\u63A7\u5236\u6743\u9650"});return}if(a.controlHolder&&a.controlHolder!==E.id){const t=a.controlHolderName===E.data.userName;if(E.data.role!=="admin"&&!t){E.emit("control-denied",{reason:"\u5176\u4ED6\u7528\u6237\u6B63\u5728\u63A7\u5236\u4E2D\uFF0C\u8BF7\u7B49\u5F85\u91CA\u653E"});return}}a.takeControl(E.id,E.data.userName),console.log(`[\u63A7\u5236] ${E.data.userName} \u83B7\u53D6\u4F1A\u8BDD "${a.title}" \u7684\u63A7\u5236\u6743`),io.emit("control-changed",{sessionId:a.id,holder:E.id,holderName:E.data.userName})}})),E.on("release-control",(({sessionId:i}={})=>{const a=i?manager.get(i):l;!a||!a.isController(E.id)||(console.log(`[\u63A7\u5236] ${E.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}))})),E.on("set-mode",(({sessionId:i,mode:a}={})=>{const t=i?manager.get(i):l;if(t){if(t.mode==="agent"&&t.setPermissionMode){t.setPermissionMode(a);const n=t.permissionMode;n!==a&&console.log(`[\u6A21\u5F0F] set-mode \u6536\u5230\u65E0\u6548\u6A21\u5F0F "${a}"\uFF0C\u5DF2\u5F52\u4E00\u5316\u4E3A ${n}`),console.log(`[\u6A21\u5F0F] ${E.data.userName} \u5207\u6362\u4F1A\u8BDD "${t.title}" \u4E3A ${n}`),io.emit("mode-changed",{sessionId:t.id,mode:n})}else t.permissionMode=a,io.emit("mode-changed",{sessionId:t.id,mode:a});manager.persist()}})),E.on("rename",(({sessionId:i,title:a}={})=>{const t=i?manager.get(i):l;if(!(!t||!a)){t.title=a;try{syncSessionTitle(t.id,a)}catch{}io.emit("session-renamed",{sessionId:t.id,title:a}),broadcastSessions(),manager.persist()}})),E.on("set-tags",(({sessionId:i,tags:a}={})=>{const t=i?manager.get(i):l;!t||!Array.isArray(a)||(t.tags=[...new Set(a.map((n=>String(n).trim())).filter(Boolean).map((n=>n.slice(0,24))))].slice(0,12),io.emit("session-tagged",{sessionId:t.id,tags:t.tags}),broadcastSessions(),manager.persist())})),E.on("resize",(({cols:i,rows:a})=>l?.resize(i,a))),E.on("list",(()=>E.emit("sessions",manager.list(E.data.userName,E.data.role==="admin")))),E.on("dirs",((i,a)=>{if(typeof a!="function")return;const t=E.data.dir?(0,external_path_.resolve)((0,external_path_.normalize)(E.data.dir)):null,n=process.platform==="win32"?"\\":"/";let f=(i||t||process.env.MYHI_CWD||(0,external_os_.homedir)()).replace(/[/\\]+$/,"")||(0,external_os_.homedir)();process.platform==="win32"&&/^[A-Za-z]:$/.test(f)&&(f+="\\");const h=(0,external_path_.resolve)((0,external_path_.normalize)(f));t&&h!==t&&!h.startsWith(t+n)&&(f=t);try{const c=(0,external_fs_.readdirSync)(f,{withFileTypes:!0}).filter((_=>_.isDirectory()&&_.name!==".git")).map((_=>({name:_.name,path:f.replace(/[/\\]+$/,"")+n+_.name}))).sort(((_,g)=>_.name.localeCompare(g.name))),v=(0,external_path_.join)(f,".."),m=(0,external_path_.resolve)((0,external_path_.normalize)(v)),b=!t||m===t||m.startsWith(t+n);a({ok:!0,current:f,parent:b&&v!==f?v:null,dirs:c})}catch(c){a({ok:!1,error:c.message})}})),E.on("create",((i,a)=>{if(!hasPermission(E.data.role,"operator")){typeof a=="function"&&a({ok:!1,error:"\u6CA1\u6709\u521B\u5EFA\u4F1A\u8BDD\u7684\u6743\u9650"});return}if(!i?.cwd&&i?.nestByTitle&&(i={...i,cwd:taskCwdFromTitle(i?.title,E.data.dir)}),E.data.dir){const n=(0,external_path_.resolve)((0,external_path_.normalize)(E.data.dir)),f=i?.cwd?(0,external_path_.resolve)((0,external_path_.normalize)(i.cwd)):null;if(f&&f!==n&&!f.startsWith(n+external_path_.sep)){typeof a=="function"&&a({ok:!1,error:"\u4E0D\u80FD\u5728\u7ED1\u5B9A\u76EE\u5F55\u4E4B\u5916\u521B\u5EFA\u4F1A\u8BDD"});return}f||(i={...i,cwd:n})}const t=i?.cwd||process.env.MYHI_CWD||process.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(n){typeof a=="function"&&a({ok:!1,error:`\u521B\u5EFA\u76EE\u5F55\u5931\u8D25: ${n.message}`});return}try{const n=manager.create({...i,owner:E.data.userName,userDir:E.data.dir});console.log(`[\u4F1A\u8BDD] ${E.data.userName} \u521B\u5EFA PTY \u4F1A\u8BDD "${n.title}" (${n.id})`),broadcastSessions(),typeof a=="function"&&a({ok:!0,session:n.toJSON()})}catch(n){console.error("[\u521B\u5EFA] PTY \u542F\u52A8\u5931\u8D25:",n.message),typeof a=="function"&&a({ok:!1,error:n.message})}})),E.on("kill",(i=>{if(!hasPermission(E.data.role,"operator")){E.emit("error",{message:"\u6CA1\u6709\u5220\u9664\u4F1A\u8BDD\u7684\u6743\u9650"});return}console.log(`[\u4F1A\u8BDD] ${E.data.userName} \u5220\u9664\u4F1A\u8BDD ${i}`),manager.kill(i),_autoSpawned.delete(i),broadcastSessions()})),E.on("kick-user",(({socketId:i,sessionId:a}={},t)=>{if(!hasPermission(E.data.role,"admin")){typeof t=="function"&&t({ok:!1,error:"\u6CA1\u6709\u8E22\u51FA\u7528\u6237\u7684\u6743\u9650"});return}const n=io.sockets.sockets.get(i);if(!n){typeof t=="function"&&t({ok:!1,error:"\u76EE\u6807\u7528\u6237\u4E0D\u5728\u7EBF"});return}const f=n.data.userName||"\u672A\u77E5";console.log(`[\u8E22\u51FA] ${E.data.userName} \u8E22\u51FA\u4E86\u7528\u6237 ${f} (${i})`),n.emit("kicked",{reason:`\u4F60\u5DF2\u88AB\u7BA1\u7406\u5458 ${E.data.userName} \u8E22\u51FA`}),n.disconnect(!0),typeof t=="function"&&t({ok:!0,name:f})})),E.on("list-viewers",(({sessionId:i}={},a)=>{if(typeof a!="function")return;const t=i?manager.get(i):l;if(!t){a({ok:!1,error:"\u4F1A\u8BDD\u4E0D\u5B58\u5728"});return}const n=[];for(const f of t._viewers){const h=io.sockets.sockets.get(f);h&&n.push({socketId:f,userName:h.data.userName||"\u672A\u77E5",role:h.data.role||"viewer",isController:t.controlHolder===f})}a({ok:!0,viewers:n})}))}));const CONTROL_TIMEOUT=(Number(process.env.MYHI_CONTROL_TIMEOUT)||300)*1e3,CONTROL_SWEEP=Math.min(60*1e3,CONTROL_TIMEOUT);function shouldReleaseIdle(E){if(!E.controlHolder)return!1;if(!io.sockets.sockets.get(E.controlHolder))return!0;if(!E.lastInputTime||Date.now()-E.lastInputTime<=CONTROL_TIMEOUT||E.isBusy)return!1;for(const p of E._viewers||[])if(p!==E.controlHolder&&io.sockets.sockets.has(p))return!0;return!1}setInterval((()=>{for(const E of manager.listSessions()){shouldReleaseIdle(E)&&(E.releaseControl(),io.emit("control-changed",{sessionId:E.id,holder:null,holderName:null}));for(const p of E._viewers)io.sockets.sockets.has(p)||E.removeViewer(p)}}),CONTROL_SWEEP);function showStartupInfo(){PORT=(httpsServer||httpServer).address()?.port||PORT;const p=getTailscaleIP(),r=`${httpsServer?"https":"http"}://${p}:${PORT}`;console.log(`
|
|
431
426
|
\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501`),console.log(" myhi \u2014 \u57FA\u4E8E Tailscale \u7684 Web \u7EC8\u7AEF"),console.log("\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501"),console.log(`
|
|
432
427
|
\u5730\u5740: ${r}`),httpsServer&&console.log(` HTTP\u91CD\u5B9A\u5411: http://${p}:${PORT+1} \u2192 HTTPS`),console.log(` \u5BC6\u7801: ${PASSWORD} (\u7F16\u8F91 ~/.myhi/password \u53EF\u4FEE\u6539)`),console.log(`
|
|
433
428
|
\u626B\u63CF\u4E8C\u7EF4\u7801\u5728\u624B\u673A\u4E0A\u6253\u5F00:
|