@wendongfly/myhi 1.0.120 → 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 +1 -1
- package/dist/chat.html +0 -218
- package/dist/index.html +2 -7
- package/dist/index.js +30 -30
- package/dist/package.json +1 -1
- package/package.json +1 -1
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
|
|
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("[90m[会话已连接][0m\r\n");else if(e.subtype==="interrupted")process.stderr.write("\r\n[33m[已中断][0m\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[36m[工具] ${t.name}[0m\r\n`);if(t.input){const e=typeof t.input==="string"?t.input:JSON.stringify(t.input,null,2);process.stdout.write("[90m"+e.slice(0,500)+"[0m\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[36m[工具] ${e.tool_name||e.name||"?"}[0m\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("[90m"+t.slice(0,1e3)+"[0m\r\n")}break;case"result":process.stdout.write(`\r\n[32m[完成][0m ${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(`[34m> ${t.content}[0m\r\n`);else if(t.type==="result")process.stdout.write(`[32m[完成][0m\r\n`)}process.stdout.write("\r\n")}));e.on("agent:busy",(e=>{if(e)process.stderr.write("[90m[思考中...][0m\r\n")}));e.on("agent:error",(e=>{process.stderr.write(`[31m[错误] ${e.message}[0m\r\n`)}));const s=(0,i.createInterface)({input:process.stdin,output:process.stdout,prompt:"[34m> [0m"});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("[33m[正在处理中,请等待][0m\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">🛡️</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
|
|
688
|
-
{ icon: '
|
|
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 || '未知错误'));
|