@wendongfly/myhi 1.0.121 → 1.0.122

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/attach.js CHANGED
@@ -1,2 +1,2 @@
1
1
  #!/usr/bin/env node
2
- import{createRequire as e}from"module";if(typeof __nccwpck_require__!=="undefined")__nccwpck_require__.ab=new URL(".",import.meta.url).pathname.slice(import.meta.url.match(/^file:\/\/\/\w:/)?1:0,-1)+"/";var t={};const s=e(import.meta.url)("module");const r=e(import.meta.url)("fs");const o=e(import.meta.url)("path");const n=e(import.meta.url)("os");const i=e(import.meta.url)("readline");const c=(0,s.createRequire)(import.meta.url);const{io:p}=c("socket.io-client");const a=process.env.MYHI_SERVER||"http://localhost:12300";let m=null;const u=process.argv.slice(2);for(let e=0;e<u.length;e++){if((u[e]==="--password"||u[e]==="-pw")&&u[e+1]){m=u[e+1];u.splice(e,2);e--}}let d;if(m){d={password:m}}else{try{d={token:(0,r.readFileSync)((0,o.join)((0,n.homedir)(),".myhi","token"),"utf8").trim()}}catch{console.error("[myhi] 未找到 token,请使用 --password <密码> 或先启动服务器");process.exit(1)}}function cleanup(e){try{process.stdin.setRawMode(false)}catch{}process.stdin.pause();e.disconnect()}function attach(e,t){e.emit("join",t);e.on("joined",(s=>{const r=s.mode==="agent";process.stderr.write(`\r\n[myhi] 已附加到 "${s.title}" (${t}) 模式=${r?"agent":"pty"}\r\n`);process.stderr.write("[myhi] 按 Ctrl+] 分离\r\n\r\n");e.emit("take-control",{sessionId:t});if(r){let t="";e.on("agent:message",(e=>{if(!e)return;switch(e.type){case"system":if(e.subtype==="init")process.stderr.write("[会话已连接]\r\n");else if(e.subtype==="interrupted")process.stderr.write("\r\n[已中断]\r\n");break;case"assistant":if(e.message?.content){for(const t of e.message.content){if(t.type==="text")process.stdout.write(t.text+"\r\n");else if(t.type==="tool_use"){process.stdout.write(`\r\n[工具] ${t.name}\r\n`);if(t.input){const e=typeof t.input==="string"?t.input:JSON.stringify(t.input,null,2);process.stdout.write(""+e.slice(0,500)+"\r\n")}}}}break;case"content_block_delta":if(e.delta?.text){process.stdout.write(e.delta.text);t+=e.delta.text}break;case"content_block_stop":if(t){process.stdout.write("\r\n");t=""}break;case"tool_use":process.stdout.write(`\r\n[工具] ${e.tool_name||e.name||"?"}\r\n`);break;case"tool_result":if(e.content){const t=typeof e.content==="string"?e.content:Array.isArray(e.content)?e.content.map((e=>e.text||"")).join(""):"";if(t)process.stdout.write(""+t.slice(0,1e3)+"\r\n")}break;case"result":process.stdout.write(`\r\n[完成] ${e.duration_ms?(e.duration_ms/1e3).toFixed(1)+"s":""} ${e.total_cost_usd?"$"+e.total_cost_usd.toFixed(4):""}\r\n\r\n`);break;case"control_request":process.stdout.write(`\r\n[权限请求] ${e.request?.tool_name||"?"}\r\n`);break}}));e.on("agent:history",(e=>{for(const t of e){if(t.type==="user")process.stdout.write(`> ${t.content}\r\n`);else if(t.type==="result")process.stdout.write(`[完成]\r\n`)}process.stdout.write("\r\n")}));e.on("agent:busy",(e=>{if(e)process.stderr.write("[思考中...]\r\n")}));e.on("agent:error",(e=>{process.stderr.write(`[错误] ${e.message}\r\n`)}));const s=(0,i.createInterface)({input:process.stdin,output:process.stdout,prompt:"> "});let r=false;e.on("agent:busy",(e=>{r=e;if(!e)s.prompt()}));s.prompt();s.on("line",(t=>{const o=t.trim();if(!o){if(!r)s.prompt();return}if(o==="/quit"||o==="/exit"||o==="/q"){process.stderr.write("[myhi] 已分离\r\n");cleanup(e);process.exit(0)}if(r){process.stderr.write("[正在处理中,请等待]\r\n");return}e.emit("agent:query",{prompt:o})}));s.on("close",(()=>{cleanup(e);process.exit(0)}))}else{try{process.stdin.setRawMode(true)}catch{}process.stdin.resume();process.stdin.setEncoding("binary");process.stdin.on("data",(t=>{if(t===""){process.stderr.write("\r\n[myhi] 已分离\r\n");cleanup(e);process.exit(0)}e.emit("input",t)}));e.on("output",(e=>{process.stdout.write(e,"binary")}));process.stdout.on("resize",(()=>{e.emit("resize",{cols:process.stdout.columns||80,rows:process.stdout.rows||24})}))}}));e.on("control-denied",(({reason:e})=>{process.stderr.write(`\r\n[myhi] 获取控制权失败: ${e}\r\n`)}));e.on("control-changed",(({holder:t,holderName:s})=>{if(t&&t!==e.id){process.stderr.write(`\r\n[myhi] ${s||"其他用户"} 已获取控制权,当前为只读\r\n`)}else if(!t){process.stderr.write("\r\n[myhi] 控制权已释放\r\n")}}));e.on("kicked",(({reason:t})=>{process.stderr.write(`\r\n[myhi] ${t||"你已被管理员踢出"}\r\n`);cleanup(e);process.exit(1)}));e.on("session-exit",(({code:t})=>{process.stderr.write(`\r\n[myhi] 会话已退出 (code ${t})\r\n`);cleanup(e);process.exit(0)}));e.on("error",(({message:t})=>{process.stderr.write(`\r\n[myhi] 错误: ${t}\r\n`);cleanup(e);process.exit(1)}))}async function pickSession(e){return new Promise(((t,s)=>{e.emit("list");e.once("sessions",(s=>{const r=s.filter((e=>e.alive));if(!r.length){process.stderr.write("[myhi] 没有活跃的会话。\n");cleanup(e);process.exit(0)}process.stdout.write("\n活跃会话:\n");r.forEach(((e,t)=>{const s=e.viewers>0?` (${e.viewers} 人在线)`:"";const r=e.mode==="agent"?" [Agent]":" [PTY]";process.stdout.write(` [${t+1}] ${e.title}${r}${s} — ${e.id}\n`)}));process.stdout.write("\n");const o=(0,i.createInterface)({input:process.stdin,output:process.stdout});o.question("选择会话编号: ",(s=>{o.close();const n=parseInt(s,10)-1;if(n<0||n>=r.length){process.stderr.write("[myhi] 无效的选择。\n");cleanup(e);process.exit(1)}t(r[n].id)}))}))}))}async function createAndAttach(e,t){return new Promise(((s,r)=>{e.emit("create",t,(t=>{if(!t?.ok){process.stderr.write(`[myhi] 创建失败: ${t?.error||"未知错误"}\n`);cleanup(e);process.exit(1)}process.stdout.write(`[myhi] 已创建会话 "${t.session.title}" — ${t.session.id}\n`);s(t.session.id)}))}))}async function promptNew(e){const t=(0,i.createInterface)({input:process.stdin,output:process.stdout});const ask=e=>new Promise((s=>t.question(e,s)));const s=(await ask("会话名称 [shell]: ")).trim()||"shell";const r=(await ask("启动命令(可选): ")).trim()||undefined;t.close();return createAndAttach(e,{title:s,initCmd:r})}const l=p(a,{transports:["websocket","polling"],auth:d});l.on("connect_error",(e=>{process.stderr.write(`[myhi] 连接失败: ${e.message}\n`);process.exit(1)}));l.on("connect",(async()=>{const e=u[0];if(e==="--new"){const e=u[1];const t=u[2]||undefined;const s=e?await createAndAttach(l,{title:e,initCmd:t}):await promptNew(l);attach(l,s)}else{const t=e||await pickSession(l);attach(l,t)}}));for(const e of["SIGINT","SIGTERM"]){process.on(e,(()=>{cleanup(l);process.exit(0)}))}process.on("exit",(()=>{try{process.stdin.setRawMode(false)}catch{}}));
2
+ import{createRequire as e}from"module";if(typeof __nccwpck_require__!=="undefined")__nccwpck_require__.ab=new URL(".",import.meta.url).pathname.slice(import.meta.url.match(/^file:\/\/\/\w:/)?1:0,-1)+"/";var t={};const s=e(import.meta.url)("module");const r=e(import.meta.url)("fs");const o=e(import.meta.url)("path");const n=e(import.meta.url)("os");const i=e(import.meta.url)("readline");const c=(0,s.createRequire)(import.meta.url);const{io:p}=c("socket.io-client");const a=process.env.MYHI_SERVER||"http://localhost:12300";let m=null;const d=process.argv.slice(2);for(let e=0;e<d.length;e++){if((d[e]==="--password"||d[e]==="-pw")&&d[e+1]){m=d[e+1];d.splice(e,2);e--}}let u;if(m){u={password:m}}else{try{u={token:(0,r.readFileSync)((0,o.join)((0,n.homedir)(),".myhi","token"),"utf8").trim()}}catch{console.error("[myhi] 未找到 token,请使用 --password <密码> 或先启动服务器");process.exit(1)}}function cleanup(e){try{process.stdin.setRawMode(false)}catch{}process.stdin.pause();e.disconnect()}function attach(e,t){e.emit("join",t);e.on("joined",(s=>{const r=s.mode==="agent";process.stderr.write(`\r\n[myhi] 已附加到 "${s.title}" (${t}) 模式=${r?"agent":"pty"}\r\n`);process.stderr.write("[myhi] 按 Ctrl+] 分离\r\n\r\n");e.emit("take-control",{sessionId:t});if(r){let t="";e.on("agent:message",(e=>{if(!e)return;switch(e.type){case"system":if(e.subtype==="init")process.stderr.write("[会话已连接]\r\n");else if(e.subtype==="interrupted")process.stderr.write("\r\n[已中断]\r\n");break;case"assistant":if(e.message?.content){for(const t of e.message.content){if(t.type==="text")process.stdout.write(t.text+"\r\n");else if(t.type==="tool_use"){process.stdout.write(`\r\n[工具] ${t.name}\r\n`);if(t.input){const e=typeof t.input==="string"?t.input:JSON.stringify(t.input,null,2);process.stdout.write(""+e.slice(0,500)+"\r\n")}}}}break;case"content_block_delta":if(e.delta?.text){process.stdout.write(e.delta.text);t+=e.delta.text}break;case"content_block_stop":if(t){process.stdout.write("\r\n");t=""}break;case"tool_use":process.stdout.write(`\r\n[工具] ${e.tool_name||e.name||"?"}\r\n`);break;case"tool_result":if(e.content){const t=typeof e.content==="string"?e.content:Array.isArray(e.content)?e.content.map((e=>e.text||"")).join(""):"";if(t)process.stdout.write(""+t.slice(0,1e3)+"\r\n")}break;case"result":process.stdout.write(`\r\n[完成] ${e.duration_ms?(e.duration_ms/1e3).toFixed(1)+"s":""} ${e.total_cost_usd?"$"+e.total_cost_usd.toFixed(4):""}\r\n\r\n`);break}}));e.on("agent:history",(e=>{for(const t of e){if(t.type==="user")process.stdout.write(`> ${t.content}\r\n`);else if(t.type==="result")process.stdout.write(`[完成]\r\n`)}process.stdout.write("\r\n")}));e.on("agent:busy",(e=>{if(e)process.stderr.write("[思考中...]\r\n")}));e.on("agent:error",(e=>{process.stderr.write(`[错误] ${e.message}\r\n`)}));const s=(0,i.createInterface)({input:process.stdin,output:process.stdout,prompt:"> "});let r=false;e.on("agent:busy",(e=>{r=e;if(!e)s.prompt()}));s.prompt();s.on("line",(t=>{const o=t.trim();if(!o){if(!r)s.prompt();return}if(o==="/quit"||o==="/exit"||o==="/q"){process.stderr.write("[myhi] 已分离\r\n");cleanup(e);process.exit(0)}if(r){process.stderr.write("[正在处理中,请等待]\r\n");return}e.emit("agent:query",{prompt:o})}));s.on("close",(()=>{cleanup(e);process.exit(0)}))}else{try{process.stdin.setRawMode(true)}catch{}process.stdin.resume();process.stdin.setEncoding("binary");process.stdin.on("data",(t=>{if(t===""){process.stderr.write("\r\n[myhi] 已分离\r\n");cleanup(e);process.exit(0)}e.emit("input",t)}));e.on("output",(e=>{process.stdout.write(e,"binary")}));process.stdout.on("resize",(()=>{e.emit("resize",{cols:process.stdout.columns||80,rows:process.stdout.rows||24})}))}}));e.on("control-denied",(({reason:e})=>{process.stderr.write(`\r\n[myhi] 获取控制权失败: ${e}\r\n`)}));e.on("control-changed",(({holder:t,holderName:s})=>{if(t&&t!==e.id){process.stderr.write(`\r\n[myhi] ${s||"其他用户"} 已获取控制权,当前为只读\r\n`)}else if(!t){process.stderr.write("\r\n[myhi] 控制权已释放\r\n")}}));e.on("kicked",(({reason:t})=>{process.stderr.write(`\r\n[myhi] ${t||"你已被管理员踢出"}\r\n`);cleanup(e);process.exit(1)}));e.on("session-exit",(({code:t})=>{process.stderr.write(`\r\n[myhi] 会话已退出 (code ${t})\r\n`);cleanup(e);process.exit(0)}));e.on("error",(({message:t})=>{process.stderr.write(`\r\n[myhi] 错误: ${t}\r\n`);cleanup(e);process.exit(1)}))}async function pickSession(e){return new Promise(((t,s)=>{e.emit("list");e.once("sessions",(s=>{const r=s.filter((e=>e.alive));if(!r.length){process.stderr.write("[myhi] 没有活跃的会话。\n");cleanup(e);process.exit(0)}process.stdout.write("\n活跃会话:\n");r.forEach(((e,t)=>{const s=e.viewers>0?` (${e.viewers} 人在线)`:"";const r=e.mode==="agent"?" [Agent]":" [PTY]";process.stdout.write(` [${t+1}] ${e.title}${r}${s} — ${e.id}\n`)}));process.stdout.write("\n");const o=(0,i.createInterface)({input:process.stdin,output:process.stdout});o.question("选择会话编号: ",(s=>{o.close();const n=parseInt(s,10)-1;if(n<0||n>=r.length){process.stderr.write("[myhi] 无效的选择。\n");cleanup(e);process.exit(1)}t(r[n].id)}))}))}))}async function createAndAttach(e,t){return new Promise(((s,r)=>{e.emit("create",t,(t=>{if(!t?.ok){process.stderr.write(`[myhi] 创建失败: ${t?.error||"未知错误"}\n`);cleanup(e);process.exit(1)}process.stdout.write(`[myhi] 已创建会话 "${t.session.title}" — ${t.session.id}\n`);s(t.session.id)}))}))}async function promptNew(e){const t=(0,i.createInterface)({input:process.stdin,output:process.stdout});const ask=e=>new Promise((s=>t.question(e,s)));const s=(await ask("会话名称 [shell]: ")).trim()||"shell";const r=(await ask("启动命令(可选): ")).trim()||undefined;t.close();return createAndAttach(e,{title:s,initCmd:r})}const l=p(a,{transports:["websocket","polling"],auth:u});l.on("connect_error",(e=>{process.stderr.write(`[myhi] 连接失败: ${e.message}\n`);process.exit(1)}));l.on("connect",(async()=>{const e=d[0];if(e==="--new"){const e=d[1];const t=d[2]||undefined;const s=e?await createAndAttach(l,{title:e,initCmd:t}):await promptNew(l);attach(l,s)}else{const t=e||await pickSession(l);attach(l,t)}}));for(const e of["SIGINT","SIGTERM"]){process.on(e,(()=>{cleanup(l);process.exit(0)}))}process.on("exit",(()=>{try{process.stdin.setRawMode(false)}catch{}}));
package/dist/chat.html CHANGED
@@ -34,8 +34,6 @@
34
34
  .sb-dot.working { background: #58a6ff; animation: pulse 1s infinite; }
35
35
  .sb-dot.waiting { background: #f0883e; animation: pulse 0.6s infinite; }
36
36
  @keyframes pulse { 0%,100% { opacity: 1; } 50% { opacity: 0.4; } }
37
- #mode-badge { padding: 0.15rem 0.5rem; border-radius: 10px; background: #21262d; border: 1px solid #30363d; cursor: pointer; user-select: none; }
38
- #mode-badge:hover { background: #30363d; }
39
37
  #control-badge { padding: 0.15rem 0.5rem; border-radius: 10px; cursor: pointer; user-select: none; border: 1px solid; }
40
38
  #control-badge.controlling { background: #0d3321; color: #3fb950; border-color: #238636; }
41
39
  #control-badge.readonly { background: #21262d; color: #8b949e; border-color: #30363d; }
@@ -84,34 +82,6 @@
84
82
  .tool-body { display: none; background: #161b22; border: 1px solid #21262d; border-radius: 6px; padding: 0.5rem; margin-top: 0.2rem; font-family: 'SF Mono', 'Consolas', monospace; font-size: 0.75rem; line-height: 1.3; overflow-x: auto; white-space: pre-wrap; word-break: break-all; max-height: 300px; overflow-y: auto; }
85
83
  .tool-body.open { display: block; }
86
84
 
87
- /* 权限请求卡片 */
88
- .msg-permission { background: #1c1500; border: 1px solid #d29922; border-radius: 8px; padding: 0.65rem 0.75rem; font-size: 0.82rem; }
89
- .msg-permission .perm-title { color: #d29922; font-weight: 600; margin-bottom: 0.3rem; }
90
- .msg-permission .perm-detail { color: #e6edf3; font-family: 'SF Mono', 'Consolas', monospace; font-size: 0.78rem; margin-bottom: 0.5rem; white-space: pre-wrap; }
91
- .perm-actions { display: flex; gap: 0.4rem; flex-wrap: wrap; }
92
- .perm-actions button { padding: 0.35rem 0.8rem; border-radius: 6px; font-size: 0.78rem; cursor: pointer; border: none; }
93
- .btn-allow { background: #238636; color: #fff; }
94
- .btn-allow:hover { background: #2ea043; }
95
- .btn-deny { background: #21262d; color: #f85149; border: 1px solid #30363d !important; }
96
-
97
- /* 权限弹窗 modal */
98
- #perm-modal { display: none; position: fixed; inset: 0; z-index: 90; align-items: flex-end; justify-content: center; }
99
- #perm-modal.open { display: flex; }
100
- #perm-modal-backdrop { position: absolute; inset: 0; background: rgba(0,0,0,0.55); }
101
- #perm-modal-box { position: relative; background: #161b22; border-radius: 16px 16px 0 0; width: 100%; max-width: 480px; padding: 1rem 1rem; padding-bottom: max(1rem, env(safe-area-inset-bottom)); z-index: 1; animation: slideUp 0.25s ease; border-top: 3px solid #d29922; }
102
- @keyframes slideUp { from { transform: translateY(100%); } to { transform: translateY(0); } }
103
- #perm-modal-box .pm-icon { text-align: center; font-size: 1.6rem; margin-bottom: 0.4rem; }
104
- #perm-modal-box .pm-title { text-align: center; font-size: 0.95rem; font-weight: 600; color: #d29922; margin-bottom: 0.6rem; }
105
- #perm-modal-box .pm-tool { text-align: center; font-size: 0.78rem; color: #8b949e; margin-bottom: 0.3rem; }
106
- #perm-modal-box .pm-detail { background: #0d1117; border: 1px solid #21262d; border-radius: 8px; padding: 0.6rem 0.75rem; font-family: 'SF Mono', 'Consolas', monospace; font-size: 0.78rem; line-height: 1.4; color: #e6edf3; white-space: pre-wrap; word-break: break-all; max-height: 200px; overflow-y: auto; margin-bottom: 0.75rem; }
107
- #perm-modal-box .pm-actions { display: flex; gap: 0.5rem; }
108
- #perm-modal-box .pm-actions button { flex: 1; padding: 0.6rem; border-radius: 10px; font-size: 0.88rem; font-weight: 600; cursor: pointer; border: none; transition: all 0.15s; }
109
- #perm-modal-box .pm-actions button:active { transform: scale(0.96); }
110
- #perm-modal-box .pm-btn-allow { background: #238636; color: #fff; }
111
- #perm-modal-box .pm-btn-allow:hover { background: #2ea043; }
112
- #perm-modal-box .pm-btn-deny { background: #21262d; color: #f85149; border: 1px solid #30363d; }
113
- #perm-modal-box .pm-btn-deny:hover { background: #30363d; }
114
- #perm-modal-box .pm-queue { text-align: center; font-size: 0.7rem; color: #8b949e; margin-top: 0.5rem; }
115
85
 
116
86
  /* Diff 视图 */
117
87
  .msg-diff { font-family: 'SF Mono', 'Consolas', monospace; font-size: 0.78rem; background: #161b22; border: 1px solid #30363d; border-radius: 8px; padding: 0.5rem 0.6rem; overflow-x: auto; line-height: 1.35; }
@@ -253,7 +223,6 @@
253
223
 
254
224
  <div id="status-bar">
255
225
  <span class="sb-item" id="work-status"><span class="sb-dot idle"></span> 空闲</span>
256
- <span id="mode-badge" onclick="switchMode()">默认</span>
257
226
  <span id="control-badge" class="readonly" style="display:none">只读</span>
258
227
  <span id="viewer-count" class="sb-item"></span>
259
228
  <span id="update-badge" style="display:none;color:#d29922;cursor:pointer" onclick="location.href='/'">⬆ 有更新</span>
@@ -409,22 +378,6 @@
409
378
  </div>
410
379
  </div>
411
380
 
412
- <!-- 权限授权弹窗 -->
413
- <div id="perm-modal">
414
- <div id="perm-modal-backdrop"></div>
415
- <div id="perm-modal-box">
416
- <div class="pm-icon">&#x1F6E1;&#xFE0F;</div>
417
- <div class="pm-title" id="pm-title">请求权限</div>
418
- <div class="pm-tool" id="pm-tool"></div>
419
- <div class="pm-detail" id="pm-detail"></div>
420
- <div class="pm-actions">
421
- <button class="pm-btn-deny" id="pm-btn-deny">拒绝</button>
422
- <button class="pm-btn-allow" id="pm-btn-allow">允许</button>
423
- </div>
424
- <div class="pm-queue" id="pm-queue" style="display:none"></div>
425
- </div>
426
- </div>
427
-
428
381
  <div id="status-overlay">连接中...</div>
429
382
 
430
383
  <script type="module">
@@ -509,7 +462,6 @@
509
462
  let isController = false;
510
463
  let myRole = 'viewer';
511
464
  let claudeSession = false; // 是否为 Claude Code 会话
512
- let currentMode = 'default'; // 当前权限模式
513
465
  let workState = 'idle';
514
466
 
515
467
  // 设置
@@ -603,8 +555,6 @@
603
555
  }
604
556
 
605
557
  // 权限请求
606
- if (/allow|deny|approve|reject|permission|Do you want to|允许|拒绝/i.test(clean) && clean.length < 500) return { type: 'permission', detail: clean };
607
-
608
558
  // Diff
609
559
  const lines = clean.split('\n');
610
560
  const hasDiffMarkers = lines.some(l => /^[+-](?![-+]{2})/.test(l)) && lines.some(l => /^@@/.test(l) || /^(---|[\+]{3})\s/.test(l));
@@ -809,21 +759,6 @@
809
759
  // 非工具消息时结束工具组
810
760
  function endToolGroup() { _toolGroupEl = null; _toolGroupCount = 0; }
811
761
 
812
- function addPermissionMessage(raw, detail) {
813
- removeThinking();
814
- const msg = document.createElement('div');
815
- msg.className = 'msg msg-permission';
816
- msg.innerHTML = `<div class="perm-title">需要权限确认</div><div class="perm-detail">${escHtml(detail)}</div>
817
- <div class="perm-actions">
818
- <button class="btn-allow" onclick="respondPermission('y', this)">允许</button>
819
- <button class="btn-deny" onclick="respondPermission('n', this)">拒绝</button>
820
- </div>`;
821
- chatArea.appendChild(msg);
822
- trimMessages();
823
- scrollToBottom();
824
- setWorkState('waiting');
825
- }
826
-
827
762
  function addDiffMessage(raw) {
828
763
  removeThinking();
829
764
  const lines = stripAnsi(raw).split('\n');
@@ -959,93 +894,6 @@
959
894
  chatArea.appendChild(msg);
960
895
  }
961
896
 
962
- // 权限响应
963
- window.respondPermission = function(answer, btn) {
964
- if (!isController) { addStatusMessage('你没有控制权,无法操作'); return; }
965
- socket.emit('input', answer + '\r');
966
- const actions = btn.closest('.perm-actions');
967
- if (actions) actions.innerHTML = answer === 'y' ? '<span style="color:#3fb950">已允许</span>' : '<span style="color:#f85149">已拒绝</span>';
968
- };
969
-
970
- // ── 权限弹窗队列管理 ──────────────────────────────
971
- const permQueue = []; // { reqId, toolName, detail }
972
- let permModalOpen = false;
973
-
974
- function showPermModal(item) {
975
- const modal = document.getElementById('perm-modal');
976
- document.getElementById('pm-title').textContent = `${item.toolName} 请求权限`;
977
- document.getElementById('pm-tool').textContent = item.toolLabel || '';
978
- document.getElementById('pm-detail').textContent = item.detail;
979
- const queueEl = document.getElementById('pm-queue');
980
- if (permQueue.length > 0) {
981
- queueEl.textContent = `还有 ${permQueue.length} 个权限请求等待处理`;
982
- queueEl.style.display = '';
983
- } else {
984
- queueEl.style.display = 'none';
985
- }
986
- // 绑定按钮
987
- document.getElementById('pm-btn-allow').onclick = () => respondPermModal(item.reqId, true);
988
- document.getElementById('pm-btn-deny').onclick = () => respondPermModal(item.reqId, false);
989
- modal.classList.add('open');
990
- permModalOpen = true;
991
- // 振动提醒(移动端)
992
- if (navigator.vibrate) navigator.vibrate(100);
993
- }
994
-
995
- function closePermModal() {
996
- document.getElementById('perm-modal').classList.remove('open');
997
- permModalOpen = false;
998
- }
999
-
1000
- function respondPermModal(requestId, allow) {
1001
- if (!isController) { addStatusMessage('你没有控制权,无法操作'); return; }
1002
- socket.emit('agent:permission', { requestId, allow });
1003
- // 更新内联卡片
1004
- const inlineActions = document.querySelector(`.perm-actions[data-reqid="${requestId}"]`);
1005
- if (inlineActions) inlineActions.innerHTML = allow ? '<span style="color:#3fb950">✓ 已允许</span>' : '<span style="color:#f85149">✗ 已拒绝</span>';
1006
- closePermModal();
1007
- setWorkState('working');
1008
- // 处理队列中的下一个
1009
- if (permQueue.length > 0) {
1010
- const next = permQueue.shift();
1011
- setTimeout(() => showPermModal(next), 200);
1012
- }
1013
- }
1014
-
1015
- function enqueuePermission(reqId, toolName, detail, toolLabel) {
1016
- const item = { reqId, toolName, detail, toolLabel };
1017
- if (!permModalOpen) {
1018
- showPermModal(item);
1019
- } else {
1020
- permQueue.push(item);
1021
- // 更新队列提示
1022
- const queueEl = document.getElementById('pm-queue');
1023
- queueEl.textContent = `还有 ${permQueue.length} 个权限请求等待处理`;
1024
- queueEl.style.display = '';
1025
- }
1026
- }
1027
-
1028
- // backdrop 点击不关闭弹窗(防止误触),但可以拒绝
1029
- document.getElementById('perm-modal-backdrop').addEventListener('click', () => {
1030
- // 不自动关闭,必须明确选择
1031
- });
1032
-
1033
- window.respondAgentPermission = function(requestId, allow, btn) {
1034
- if (!isController) { addStatusMessage('你没有控制权,无法操作'); return; }
1035
- socket.emit('agent:permission', { requestId, allow });
1036
- const actions = btn.closest('.perm-actions');
1037
- if (actions) actions.innerHTML = allow ? '<span style="color:#3fb950">✓ 已允许</span>' : '<span style="color:#f85149">✗ 已拒绝</span>';
1038
- setWorkState('working');
1039
- // 如果弹窗正在显示同一个请求,也关闭它
1040
- if (permModalOpen) {
1041
- closePermModal();
1042
- if (permQueue.length > 0) {
1043
- const next = permQueue.shift();
1044
- setTimeout(() => showPermModal(next), 200);
1045
- }
1046
- }
1047
- };
1048
-
1049
897
  // ── 输出处理 ──────────────────────────────────
1050
898
  function flushOutput() {
1051
899
  if (!outputBuffer) return;
@@ -1069,7 +917,6 @@
1069
917
  switch (result.type) {
1070
918
  case 'thinking': showThinking(result.content); break;
1071
919
  case 'tool': endStream(); addToolMessage(raw, result.name); break;
1072
- case 'permission': endStream(); addPermissionMessage(raw, result.detail); break;
1073
920
  case 'diff': endStream(); addDiffMessage(raw); break;
1074
921
  case 'error': endStream(); addErrorMessage(raw); break;
1075
922
  case 'prompt': endStream(); removeThinking(); setWorkState('idle'); break;
@@ -1105,19 +952,6 @@
1105
952
  }
1106
953
  }
1107
954
 
1108
- // ── 权限模式 ──────────────────────────────────
1109
- const MODES = [
1110
- { id: 'default', label: '默认' },
1111
- { id: 'acceptEdits', label: '自动编辑' },
1112
- { id: 'plan', label: '计划模式' },
1113
- { id: 'bypass', label: '跳过权限' },
1114
- ];
1115
-
1116
- function updateModeUI() {
1117
- const m = MODES.find(x => x.id === currentMode) || MODES[0];
1118
- document.getElementById('mode-badge').textContent = m.label;
1119
- }
1120
-
1121
955
  function updateShortcutBar() {
1122
956
  if (!currentSession) { shortcutBar.style.display = 'none'; return; }
1123
957
  const isAgent = currentSession.mode === 'agent';
@@ -1130,25 +964,6 @@
1130
964
  document.getElementById('sk-claude-agent').style.display = isAgent ? 'flex' : 'none';
1131
965
  }
1132
966
 
1133
- window.switchMode = function() {
1134
- if (!isController) return;
1135
- if (currentSession?.mode === 'agent') {
1136
- // Agent 模式:仅更新模式设置(下次查询生效)
1137
- const idx = MODES.findIndex(x => x.id === currentMode);
1138
- currentMode = MODES[(idx + 1) % MODES.length].id;
1139
- updateModeUI();
1140
- socket.emit('set-mode', { sessionId: SESSION_ID, mode: currentMode });
1141
- addStatusMessage(`权限模式已切换为:${MODES.find(x => x.id === currentMode)?.label}`);
1142
- } else {
1143
- // PTY 模式:发送 Shift+Tab 到 Claude Code CLI
1144
- socket.emit('input', '\x1b[Z');
1145
- const idx = MODES.findIndex(x => x.id === currentMode);
1146
- currentMode = MODES[(idx + 1) % MODES.length].id;
1147
- updateModeUI();
1148
- socket.emit('set-mode', { sessionId: SESSION_ID, mode: currentMode });
1149
- }
1150
- };
1151
-
1152
967
  // ── Socket.IO ──────────────────────────────────
1153
968
  const socket = io({
1154
969
  transports: ['websocket', 'polling'],
@@ -1191,8 +1006,6 @@
1191
1006
  updateControlUI(session.controlHolder === socket.id, session.controlHolderName);
1192
1007
 
1193
1008
  claudeSession = isClaudeSession(session) || session.mode === 'agent';
1194
- currentMode = session.permissionMode || 'default';
1195
- updateModeUI();
1196
1009
  updateShortcutBar();
1197
1010
 
1198
1011
  // 更新输入框占位符
@@ -1230,7 +1043,6 @@
1230
1043
  });
1231
1044
  socket.on('control-denied', ({ reason }) => addStatusMessage('无法获取控制: ' + reason));
1232
1045
  socket.on('kicked', ({ reason }) => { addStatusMessage(reason || '你已被管理员踢出'); setTimeout(() => location.href = '/', 2000); });
1233
- socket.on('mode-changed', ({ sessionId, mode }) => { if (sessionId === SESSION_ID) { currentMode = mode; updateModeUI(); } });
1234
1046
 
1235
1047
  // ── Agent 模式事件 ──────────────────────────────
1236
1048
  socket.on('agent:message', (msg) => {
@@ -1263,36 +1075,6 @@
1263
1075
  }
1264
1076
  setWorkState('working');
1265
1077
  break;
1266
- case 'control_request':
1267
- removeThinking();
1268
- if (msg.request) {
1269
- const toolName = msg.request.tool_name || '未知工具';
1270
- const input = msg.request.input || {};
1271
- const detail = toolName === 'Bash' ? (input.command || JSON.stringify(input))
1272
- : toolName === 'Edit' ? `${input.file_path || ''}\n${input.old_string ? '替换: ' + input.old_string.slice(0, 120) : ''}`
1273
- : toolName === 'Write' ? `${input.file_path || ''}`
1274
- : toolName === 'Read' ? `${input.file_path || ''}`
1275
- : JSON.stringify(input, null, 2);
1276
- const reqId = msg.request_id || msg.request?.request_id || msg.uuid;
1277
- // 工具类型标签
1278
- const toolLabels = { Bash: '执行命令', Edit: '编辑文件', Write: '写入文件', Read: '读取文件', Glob: '搜索文件', Grep: '搜索内容' };
1279
- const toolLabel = toolLabels[toolName] || toolName;
1280
- // 内联卡片
1281
- const el = document.createElement('div');
1282
- el.className = 'msg msg-permission';
1283
- el.innerHTML = `<div class="perm-title">${escHtml(toolName)} 请求权限</div><div class="perm-detail">${escHtml(detail)}</div>
1284
- <div class="perm-actions" data-reqid="${escHtml(reqId)}">
1285
- <button class="btn-allow" onclick="respondAgentPermission('${escHtml(reqId)}', true, this)">允许</button>
1286
- <button class="btn-deny" onclick="respondAgentPermission('${escHtml(reqId)}', false, this)">拒绝</button>
1287
- </div>`;
1288
- chatArea.appendChild(el);
1289
- trimMessages();
1290
- scrollToBottom();
1291
- setWorkState('waiting');
1292
- // 弹窗授权
1293
- enqueuePermission(reqId, toolName, detail, toolLabel);
1294
- }
1295
- break;
1296
1078
  case 'result':
1297
1079
  removeThinking();
1298
1080
  setWorkState('idle');
package/dist/index.html CHANGED
@@ -684,11 +684,8 @@
684
684
 
685
685
  // ── Presets ───────────────────────────────────────────────────
686
686
  const PRESETS = [
687
- // Agent 模式(使用 Claude SDK)
688
- { icon: '🛡️', label: '交互审批', title: 'claude', type: 'agent', cmd: 'claude', permissionMode: 'default' },
689
- { icon: '✏️', label: '自动编辑', title: 'claude', type: 'agent', cmd: 'claude --permission-mode acceptEdits', permissionMode: 'acceptEdits' },
690
- { icon: '📋', label: '计划模式', title: 'claude', type: 'agent', cmd: 'claude --permission-mode plan', permissionMode: 'plan' },
691
- { icon: '⚡', label: '跳过权限', title: 'claude', type: 'agent', cmd: 'claude --dangerously-skip-permissions', permissionMode: 'bypassPermissions' },
687
+ // Agent 模式
688
+ { icon: '🤖', label: 'Claude', title: 'claude', type: 'agent', cmd: 'claude' },
692
689
  // PTY 模式(终端)
693
690
  { icon: '✨', label: 'Gemini', title: 'gemini', type: 'pty', cmd: 'gemini' },
694
691
  { icon: '🐚', label: 'Shell', title: 'shell', type: 'pty', cmd: '' },
@@ -750,7 +747,6 @@
750
747
  socket.emit('create-agent', {
751
748
  title,
752
749
  cwd,
753
- permissionMode: preset.permissionMode,
754
750
  }, (res) => {
755
751
  if (res?.ok) openSession(res.session.id);
756
752
  else alert('创建失败: ' + (res?.error || '未知错误'));
@@ -938,7 +934,6 @@
938
934
  title,
939
935
  cwd,
940
936
  resumeSessionId: session.sessionId,
941
- permissionMode: 'default',
942
937
  }, (res) => {
943
938
  if (res?.ok) openSession(res.session.id);
944
939
  else alert('导入失败: ' + (res?.error || '未知错误'));