@huicai/cli 2026.721.0 → 2026.804.0
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/README.md +11 -4
- package/package.json +2 -2
- package/src/advisor.js +1 -1
- package/src/agent.js +1 -1
- package/src/api.js +1 -1
- package/src/assets-import.js +1 -0
- package/src/autofix.js +1 -1
- package/src/automation.js +1 -1
- package/src/commands.js +1 -1
- package/src/compaction.js +1 -1
- package/src/config.js +1 -1
- package/src/context-report.js +1 -1
- package/src/context.js +1 -1
- package/src/decisions.js +1 -0
- package/src/executors.js +1 -1
- package/src/expert-catalog.js +1 -0
- package/src/experts-store.js +1 -0
- package/src/gitflow.js +1 -1
- package/src/mcp.js +1 -1
- package/src/pyruntime.js +1 -1
- package/src/repl.js +1 -1
- package/src/schedule-freq.js +1 -0
- package/src/schedule.js +1 -1
- package/src/session.js +1 -1
- package/src/skills.js +1 -1
- package/src/squad.js +1 -1
- package/src/subagent.js +1 -1
- package/src/title.js +1 -0
- package/src/tools.js +1 -1
- package/src/transcript.js +1 -1
- package/src/tui/app.js +1 -1
package/src/transcript.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{streamChat as t}from"./api.js";import{DEFAULT_MODEL as e}from"./config.js";const n=/^(agent|workflow|datahuman|squad)-/;export function textOf(t){return"string"==typeof t?t:Array.isArray(t)?t.map(t=>"text"===t?.type?t.text:"image_url"===t?.type?"[图片]":"").join("\n"):""}export function toMarkdown(t){const e=t||{},n=["# HuiCai 对话记录","",`- 会话:${e.id||""}`,`- 模型:${e.model||""}`,`- 导出时间:${(new Date).toLocaleString()}`,"","---",""];for(const t of e.messages||[])if("system"!==t.role)if("user"===t.role)n.push("### 🧑 用户","",textOf(t.content).trim()||"(空)","");else if("assistant"===t.role){const e=textOf(t.content).trim();e&&n.push("### 🤖 助手","",e,"");for(const e of t.tool_calls||[]){const t=String(e.function?.arguments||"").replace(/\s+/g," ").slice(0,200);n.push(`> 🔧 \`${e.function?.name}(${t})\``,"")}}else if("tool"===t.role){const e=String(t.content||"").slice(0,1500);n.push("> 工具结果:","","```",e,"```","")}return n.join("\n")}export async function distillMemory(o,s,r=""){const i=n.test(String(s.model||""))?s.compactModel||e:s.model,l=(o||[]).filter(t=>"system"!==t.role).map(t=>`[${t.role}] ${textOf(t.content).replace(/\s+/g," ").slice(0,600)}`).join("\n").slice(0,16e3);if(!l.trim())return[];const c=(r?"【已有记忆(勿重复)】\n"+r+"\n\n":"")+"【本次记录】\n"+l,a=((await t({baseUrl:s.baseUrl,apiKey:s.apiKey,model:i,messages:[{role:"system",content:"你是项目知识提炼器。从下面这段对话/执行记录中,提炼出**值得跨会话长期记住**的稳定事实/约定/结论/踩坑(如构建与测试命令、架构决定、代码约定、易错点、外部依赖要点)。要求:每条一行,以「- 」开头,简洁具体;只输出真正长期有用的,不要一次性细节、不要大段代码;若与「已有记忆」重复则跳过;最多 5 条;没有则输出「(无)」。"},{role:"user",content:c}]})).content||"").trim();return!a||/^[((]无[))]$/.test(a)?[]:a.split("\n").map(t=>t.replace(/^[-*]\s*/,"").trim()).filter(t=>t&&!/^[((]无[))]$/.test(t)).slice(0,5)}export async function recapConversation(o,s){const r=n.test(String(s.model||""))?s.compactModel||e:s.model,i=(o||[]).filter(t=>"system"!==t.role).map(t=>`[${t.role}] ${textOf(t.content).replace(/\s+/g," ").slice(0,500)}`).join("\n").slice(0,12e3);if(!i.trim())return"(对话尚无内容)";return((await t({baseUrl:s.baseUrl,apiKey:s.apiKey,model:r,messages:[{role:"system",content:"用一句话(不超过40个汉字)概括这段对话到目前为止在做什么、进展如何。只输出这一句话,不要引号、不要多余说明。"},{role:"user",content:i}]})).content||"").trim().split("\n")[0]||"(无法生成小结)"}
|
|
1
|
+
import{streamChat as t}from"./api.js";import{DEFAULT_MODEL as e}from"./config.js";const n=/^(agent|workflow|datahuman|squad)-/;export function textOf(t){return"string"==typeof t?t:Array.isArray(t)?t.map(t=>"text"===t?.type?t.text:"image_url"===t?.type?"[图片]":"").join("\n"):""}export function toMarkdown(t){const e=t||{},n=["# HuiCai 对话记录","",`- 会话:${e.id||""}`,`- 模型:${e.model||""}`,`- 导出时间:${(new Date).toLocaleString()}`,"","---",""];for(const t of e.messages||[])if("system"!==t.role)if("user"===t.role)n.push("### 🧑 用户","",textOf(t.content).trim()||"(空)","");else if("assistant"===t.role){const e=textOf(t.content).trim();e&&n.push("### 🤖 助手","",e,"");for(const e of t.tool_calls||[]){const t=String(e.function?.arguments||"").replace(/\s+/g," ").slice(0,200);n.push(`> 🔧 \`${e.function?.name}(${t})\``,"")}}else if("tool"===t.role){const e=String(t.content||"").slice(0,1500);n.push("> 工具结果:","","```",e,"```","")}return n.join("\n")}export async function distillMemory(o,s,r=""){const i=n.test(String(s.model||""))?s.compactModel||e:s.model,l=(o||[]).filter(t=>"system"!==t.role).map(t=>`[${t.role}] ${textOf(t.content).replace(/\s+/g," ").slice(0,600)}`).join("\n").slice(0,16e3);if(!l.trim())return[];const c=(r?"【已有记忆(勿重复)】\n"+r+"\n\n":"")+"【本次记录】\n"+l,a=((await t({baseUrl:s.baseUrl,apiKey:s.apiKey,model:i,client:s.client,aux:!0,messages:[{role:"system",content:"你是项目知识提炼器。从下面这段对话/执行记录中,提炼出**值得跨会话长期记住**的稳定事实/约定/结论/踩坑(如构建与测试命令、架构决定、代码约定、易错点、外部依赖要点)。要求:每条一行,以「- 」开头,简洁具体;只输出真正长期有用的,不要一次性细节、不要大段代码;若与「已有记忆」重复则跳过;最多 5 条;没有则输出「(无)」。"},{role:"user",content:c}]})).content||"").trim();return!a||/^[((]无[))]$/.test(a)?[]:a.split("\n").map(t=>t.replace(/^[-*]\s*/,"").trim()).filter(t=>t&&!/^[((]无[))]$/.test(t)).slice(0,5)}export async function recapConversation(o,s){const r=n.test(String(s.model||""))?s.compactModel||e:s.model,i=(o||[]).filter(t=>"system"!==t.role).map(t=>`[${t.role}] ${textOf(t.content).replace(/\s+/g," ").slice(0,500)}`).join("\n").slice(0,12e3);if(!i.trim())return"(对话尚无内容)";return((await t({baseUrl:s.baseUrl,apiKey:s.apiKey,model:r,client:s.client,aux:!0,messages:[{role:"system",content:"用一句话(不超过40个汉字)概括这段对话到目前为止在做什么、进展如何。只输出这一句话,不要引号、不要多余说明。"},{role:"user",content:i}]})).content||"").trim().split("\n")[0]||"(无法生成小结)"}
|
package/src/tui/app.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import r,{useState as t,useRef as e,useReducer as n,useEffect as o,useMemo as c}from"react";import{execSync as s}from"node:child_process";import{render as l,Box as u,Text as i,Static as a,useInput as m,useApp as p}from"ink";import{runTurn as f}from"../agent.js";import{fetchUsage as d}from"../api.js";import{toolLabel as g}from"../render.js";import{renderMarkdown as h,looksLikeMarkdown as w}from"../markdown.js";import{renderWelcome as x}from"../banner.js";import{createSession as y,saveSession as b}from"../session.js";import{createCheckpoints as v}from"../checkpoints.js";import{buildExtraTools as k}from"../repl.js";import{buildSystemPrompt as T}from"../context.js";import{closeMcp as $}from"../mcp.js";import{runSlash as j,listCommands as E}from"../commands.js";import{usageStatus as M,fmtReset as P}from"../usage.js";import{expandAtMentions as A,buildUserContent as S,runBangCommand as I,loadHistory as D,appendHistory as L}from"../input.js";import{loadAllowRules as _,loadDenyRules as C,matchAllowRules as U,suggestRule as O,addProjectAllowRule as R,riskWarning as W}from"../settings.js";import{PLAN_DENY_REASON as F,SANDBOX_DENY_REASON as K,sandboxBlocked as Y,AUTOMATION_TOOLS as q,automationActionLabel as B,automationDetail as G,PERMISSION_MODES as J,MODE_ICON as N,MODE_LABEL as Q}from"../permissions.js";import{runVerifyLoop as X}from"../autofix.js";import{runAutoFlow as z}from"../autorun.js";const H=r.createElement,V=10;function Z(r,t=12){const e=String(r).split("\n");return e.length<=t?r:e.slice(0,t).join("\n")+`\n…(共 ${e.length} 行)`}function rr(r){return(r=Number(r)||0)>=1e3?(r/1e3).toFixed(1)+"k":String(r)}const tr=["⠋","⠙","⠹","⠸","⠼","⠴","⠦","⠧","⠇","⠏"];export function App({config:r,cwd:l,session:x,extraTools:nr,mcp:or,yolo:cr,initialPrompt:sr,version:lr,onExit:ur}){const ir=p(),[ar,mr]=t(l),pr=e(nr),fr=e([]),dr=e(0),gr=e(""),hr=e(x),wr=e(null),xr=e(null),yr=e(new Set),br=e(_(ar)),vr=e(C(ar)),kr=e(!1),Tr=e(!1),$r=e(!1),jr=e(v()),Er=e(!!cr),Mr=e(!1),Pr=e("idle"),Ar=e([]),Sr=e({list:[]}),Ir=e(D(ar)),Dr=e(null),Lr=e(""),[,_r]=n(r=>r+1,0),[Cr,Ur]=n(r=>r+1,0),[Or,Rr]=t(0),[Wr,Fr]=t("idle"),[Kr,Yr]=t({text:"",cursor:0}),[qr,Br]=t(null),[Gr,Jr]=t({prompt:0,completion:0}),[Nr,Qr]=t(0),[Xr,zr]=t([]),[Hr,Vr]=t(0),[Zr,rt]=t(cr?"auto":"manual"),tt=()=>kr.current?"plan":Tr.current?"sandbox":Er.current?"auto":$r.current?"acceptEdits":"manual",[et,nt]=t(""),ot=e(null),[ct,st]=t(0),[lt,ut]=t(null),it=e(null),[at,mt]=t(0),[pt,ft]=t(!1),dt=c(()=>E(ar),[ar]),gt=(r,t)=>{fr.current.push({id:dr.current++,kind:r,text:t}),Ur()},ht=()=>{if(gr.current){const t=gr.current,e=!1!==r.markdown&&w(t);gt("assistant",e?h(t):t),gr.current=""}},wt=r=>{Pr.current=r?"busy":"idle",Fr(Pr.current)};o(()=>{if("busy"!==Wr)return;const r=setInterval(()=>st(r=>(r+1)%tr.length),80);return()=>clearInterval(r)},[Wr]);const xt=async()=>{if(r.apiKey)try{const t=await d(r.baseUrl,r.apiKey);it.current=t,ut(t)}catch{}};o(()=>{xt();const r=setInterval(()=>{xt()},18e4);return()=>clearInterval(r)},[]),o(()=>{if(!r.statusLine)return void nt("");const t=()=>{try{const t=s(r.statusLine,{cwd:ar,timeout:3e3,stdio:["ignore","pipe","ignore"]}).toString().split("\n")[0].slice(0,80);nt(t)}catch{nt("")}};t();const e=setInterval(t,5e3);return()=>clearInterval(e)},[r.statusLine]),o(()=>()=>{ot.current&&clearInterval(ot.current)},[]);const yt=e(""),bt={onDelta:r=>{yt.current&&(yt.current=""),gr.current+=r,_r()},onReasoning:r=>{yt.current=(yt.current+r).slice(-400),_r()},onToolStart:(r,t)=>{yt.current="",ht(),gt("tool",g(r,t))},onToolResult:(r,t)=>gt("toolresult",function(r,t=200){const e=String(r).split("\n")[0];return e.length>t?e.slice(0,t)+"…":e}(t)),onFileDiff:r=>gt("diff",r),onSkip:(r,t)=>gt("skip",g(r,t)+" — 已跳过"),onInfo:r=>{ht(),gt("info",r)},onTodo:r=>zr(r),onTurnEnd:()=>{yt.current="",ht()}},vt={request:(r,t,e,n)=>{if(vr.current.length&&U(vr.current,r,t))return Promise.resolve({allow:!1,reason:"该操作被拒绝规则阻止(/permissions 查看或移除)。"});if(kr.current&&!n)return Promise.resolve({allow:!1,reason:F});if(Tr.current&&Y(r))return Promise.resolve({allow:!1,reason:K});if(n||Er.current||yr.current.has(r)||U(br.current,r,t))return Promise.resolve({allow:!0});if($r.current&&("write_file"===r||"edit_file"===r))return Promise.resolve({allow:!0});const o=q.has(r);return new Promise(e=>{xr.current={name:r,args:t,resolve:e,consent:o},Br({label:o?B(r,t):g(r,t),detail:o?G(r,t):"",name:r,args:t,consent:o,risk:W(r,t)})})},isYolo:()=>Er.current,setYolo:r=>{Er.current=!!r,rt(tt())},setPlanMode:r=>{kr.current=!!r,rt(tt())},isPlanMode:()=>kr.current,setSandbox:r=>{Tr.current=!!r,rt(tt())},isSandbox:()=>Tr.current,setAcceptEdits:r=>{$r.current=!!r,rt(tt())},isAcceptEdits:()=>$r.current,setMode:r=>{Er.current="auto"===r,$r.current="acceptEdits"===r,kr.current="plan"===r,Tr.current="sandbox"===r,rt(r)},getMode:()=>tt(),reloadRules:()=>{br.current=_(ar),vr.current=C(ar)}},kt=()=>{const r=Ar.current.shift();return Vr(Ar.current.length),null==r?null:r},Tt=async t=>{const e=M(it.current);if(e&&"exhausted"===e.level)return void gt("info",`✗ Token 用量窗口(${e.label})已用尽,暂无法继续。${P(e.resetAtEpochMs)}`);const{text:n,images:o}=await A(t,ar,r),c=jr.current.snapshot({session:hr.current,label:t});hr.current.messages.push({role:"user",content:S(n,o)}),wt(!0),wr.current=new AbortController,hr.current.compaction||(hr.current.compaction={});const s=await f({config:r,messages:hr.current.messages,permissions:vt,cwd:ar,extraTools:pr.current,events:bt,signal:wr.current.signal,todoState:Sr.current,compaction:hr.current.compaction,pullQueued:kt,fileJournal:c});return wr.current=null,ht(),s?.usage&&Jr(r=>({prompt:r.prompt+s.usage.prompt,completion:r.completion+s.usage.completion})),s?.lastPromptTokens&&(Qr(s.lastPromptTokens),hr.current.lastPromptTokens=s.lastPromptTokens),b(hr.current),(s?.aborted||s?.error)&&Ar.current.length&&(Ar.current=[],Vr(0),gt("info","已清空排队消息")),wt(!1),xt(),s},$t=()=>{ur?.(),ir.exit()},jt=async t=>{const e=t.trim();if(Yr({text:"",cursor:0}),Dr.current=null,e){if(L(ar,e),Ir.current[Ir.current.length-1]!==e&&Ir.current.push(e),e.startsWith("/"))return(async t=>{const e={version:lr,config:r,cwd:ar,usage:Gr,permissions:vt,extraTools:pr.current,session:hr.current,checkpoints:jr.current,print:r=>gt("info",String(r))},n=await j(t,e);if("exit"===n.action)return $t();if("clear"===n.action&&(hr.current=y({cwd:ar,model:r.model,messages:[hr.current.messages[0]]}),Sr.current.list=[],jr.current.list.length=0,zr([]),Qr(0),fr.current=[],gr.current="",Rr(r=>r+1),gt("info","已开启新会话(旧会话仍可 --resume 恢复)")),"refresh"===n.action&&(Sr.current.list=[],zr([]),Qr(0)),"autofix"===n.action&&await X({command:n.command,maxIters:n.maxIters,cwd:ar,runTurnOnce:r=>Tt(r),emit:r=>gt("info",r)}),"autorun"===n.action&&await z({goal:n.goal,config:r,permissions:vt,cwd:ar,runTurnOnce:r=>Tt(r),emit:r=>gt("info",r)}),"cd"===n.action){br.current=_(n.cwd),vr.current=C(n.cwd),$(or?.clients);const t=await k(r,n.cwd);pr.current=t.extraTools,or&&(or.clients=t.clients),hr.current=y({cwd:n.cwd,model:r.model,messages:[{role:"system",content:T(n.cwd,{memory:!1!==r.memoryEnabled})}]}),r.sessionId=hr.current.id,Sr.current.list=[],jr.current.list.length=0,zr([]),Qr(0),fr.current=[],gr.current="",Rr(r=>r+1),mr(n.cwd),gt("info","已切换到 "+n.cwd+"(新会话,旧会话已保存)")}"loop"===n.action&&(ot.current&&(clearInterval(ot.current),ot.current=null),n.stop?gt("info","已停止循环。"):(gt("info",`已开始循环:每 ${n.seconds}s 运行「${n.prompt}」(/loop off 停止)`),ot.current=setInterval(()=>{"busy"!==Pr.current&&(gt("user",n.prompt),Tt(n.prompt))},1e3*n.seconds))),n.prompt&&("busy"===Pr.current?(Ar.current.push(n.prompt),Vr(Ar.current.length)):(gt("user",n.prompt),await Tt(n.prompt)))})(e);if(e.startsWith("!")&&e.length>1)return(async r=>{if("busy"===Pr.current)return void gt("info","正在处理上一条,! 命令请稍候再试");gt("user","! "+r);const{output:t,record:e}=await I(r,ar);gt("toolresult",Z(t)),hr.current.messages.push({role:"user",content:e}),b(hr.current)})(e.slice(1).trim());if("busy"===Pr.current)return gt("user",e),Ar.current.push(e),void Vr(Ar.current.length);gt("user",e),await Tt(e)}};o(()=>{!Mr.current&&sr&&(Mr.current=!0,jt(sr))},[]);const Et=/^\/(\S*)$/.exec(Kr.text),Mt=Et?Et[1]:null,Pt=null==Mt?[]:dt.filter(r=>r.name.toLowerCase().startsWith(Mt.toLowerCase())),At=Pt.length>0&&!pt,St=Pt.length<=V?0:Math.max(0,Math.min(at-Math.floor(5),Pt.length-V)),It=Pt.slice(St,St+V);o(()=>{mt(0),ft(!1)},[Mt]),m((t,e)=>{if(xr.current){const r=xr.current,n=t=>{xr.current=null,Br(null),r.resolve(t)};if("y"===t||"1"===t)return n({allow:!0});if("a"===t||"2"===t)return yr.current.add(r.name),n({allow:!0});if("3"===t)return n({allow:!1,reason:"用户选择了「否」,该操作未执行。"});if("A"===t){yr.current.add(r.name);const t=O(r.name,r.args);try{R(ar,t),br.current=_(ar),gt("info",`已写入允许规则:${t} → .huicai/settings.json`)}catch(r){gt("info",`规则写入失败(本会话仍生效):${r.message}`)}return n({allow:!0})}return"n"===t||e.escape||e.return?n({allow:!1,reason:"用户拒绝了该操作。"}):void 0}if(e.tab&&e.shift){const t=J[(J.indexOf(vt.getMode())+1)%J.length];return vt.setMode(n=t),r.planMode="plan"===n,r.sandbox="sandbox"===n,void gt("info",`对话模式 → ${N[n]} ${Q[n]}`+("auto"===n?"(已开放所有权限,任何命令免审批)":""))}var n;if(At){const r=()=>Pt[Math.min(at,Pt.length-1)];if(e.upArrow)return mt(r=>(r-1+Pt.length)%Pt.length);if(e.downArrow)return mt(r=>(r+1)%Pt.length);if(e.pageUp)return mt(r=>Math.max(0,r-V));if(e.pageDown)return mt(r=>Math.min(Pt.length-1,r+V));if(e.tab){const t=r().name;return Yr({text:"/"+t+" ",cursor:t.length+2})}if(e.return)return void jt("/"+r().name);if(e.escape)return ft(!0)}if(e.escape)"busy"===Pr.current&&wr.current&&(wr.current.abort(),gt("info","已请求中断(Esc)"));else if(e.ctrl&&"c"===t)"busy"===Pr.current&&wr.current?(wr.current.abort(),gt("info","已请求中断")):$t();else if(e.return)jt(Kr.text);else{if(e.upArrow)return(()=>{const r=Ir.current;if(!r.length)return;null==Dr.current?(Lr.current=Kr.text,Dr.current=r.length-1):Dr.current>0&&Dr.current--;const t=r[Dr.current]??"";Yr({text:t,cursor:t.length})})();if(e.downArrow)return(()=>{if(null==Dr.current)return;const r=Ir.current;if(Dr.current<r.length-1){Dr.current++;const t=r[Dr.current];Yr({text:t,cursor:t.length})}else Dr.current=null,Yr({text:Lr.current,cursor:Lr.current.length})})();if(e.leftArrow)return Yr(r=>({...r,cursor:Math.max(0,r.cursor-1)}));if(e.rightArrow)return Yr(r=>({...r,cursor:Math.min(r.text.length,r.cursor+1)}));if(e.ctrl&&"a"===t)return Yr(r=>({...r,cursor:0}));if(e.ctrl&&"e"===t)return Yr(r=>({...r,cursor:r.text.length}));if(e.ctrl&&"u"===t)return Yr(r=>({text:r.text.slice(r.cursor),cursor:0}));if(e.ctrl&&"k"===t)return Yr(r=>({text:r.text.slice(0,r.cursor),cursor:r.cursor}));if(e.ctrl&&"w"===t)return Yr(r=>{const t=r.text.slice(0,r.cursor).replace(/\S+\s*$/,"");return{text:t+r.text.slice(r.cursor),cursor:t.length}});if(e.backspace||e.delete)return Dr.current=null,Yr(r=>r.cursor>0?{text:r.text.slice(0,r.cursor-1)+r.text.slice(r.cursor),cursor:r.cursor-1}:r);if(t&&!e.ctrl&&!e.meta){Dr.current=null;const r=t.replace(/\r\n?/g,"\n");Yr(t=>({text:t.text.slice(0,t.cursor)+r+t.text.slice(t.cursor),cursor:t.cursor+r.length}))}}});const Dt=c(()=>fr.current.slice(),[Cr]),Lt=ar.split("/").filter(Boolean).pop()||ar,_t=Kr.text.slice(0,Kr.cursor),Ct=Kr.text[Kr.cursor]??" ",Ut=Kr.text.slice(Kr.cursor+1),Ot=Math.max(8,process.stdout.columns||80),Rt=M(lt),Wt=Rt?"exhausted"===Rt.level?`⛔ ${Rt.label}用量已用尽${P(Rt.resetAtEpochMs)?" · "+P(Rt.resetAtEpochMs):""}`:`⚠ ${Rt.label}用量 ${Math.round(100*Rt.pct)}%(${rr(Rt.used)}/${rr(Rt.limit)})`:"";return H(u,{flexDirection:"column"},H(a,{key:Or,items:Dt},r=>H(er,{key:r.id,item:r})),yt.current?H(u,null,H(i,{color:"gray",italic:!0},"✦ 思考 "+yt.current.replace(/\n/g," ").slice(-160))):null,gr.current?H(u,{marginTop:0},H(i,null,gr.current)):null,Xr.length?H(u,{flexDirection:"column",paddingLeft:2,marginTop:1},...Xr.map((r,t)=>H(i,{key:t,color:"completed"===r.status?"green":"in_progress"===r.status?"cyan":"gray",dimColor:"completed"===r.status,bold:"in_progress"===r.status},("completed"===r.status?"☑ ":"in_progress"===r.status?"◐ ":"☐ ")+r.content))):null,"busy"!==Wr||gr.current?null:H(u,null,H(i,{color:"cyan"},tr[ct]+" 思考中…"),H(i,{color:"gray"},"(Esc 中断 · 输入回车可排队补充)")),qr?H(u,{flexDirection:"column",borderStyle:"round",borderColor:"yellow",paddingX:1,marginTop:1},H(i,{color:"yellow",bold:!0},qr.consent?"● 是否继续?"+qr.label:"● 需要授权:"+qr.label),qr.risk?H(i,{color:"red",bold:!0},"⚠ "+qr.risk):null,qr.consent&&qr.detail?H(i,{color:"gray"},Z(qr.detail,6)):null,"run_bash"===qr.name?H(i,{color:"gray"},"$ "+(qr.args.command||"")):null,"fetch_url"===qr.name?H(i,{color:"gray"},"GET "+(qr.args.url||"")):null,H(i,null,qr.consent?"[1] 是 · [2] 是,并允许在当前对话中后续相同操作不再询问 · [3/Esc] 否":"[y] 允许一次 · [a] 本会话 · [A] 永久(写入项目设置) · [n/Esc] 拒绝")):null,At?H(u,{flexDirection:"column",marginTop:1},H(i,{color:"gray"},Pt.length>V?` 斜杠命令 ${St+1}-${St+It.length}/${Pt.length} · ↑↓ 选择 · PgUp/PgDn 翻页 · Tab 补全 · ⏎ 执行 · Esc 关闭`:" 斜杠命令 · ↑↓ 选择 · Tab 补全 · ⏎ 执行 · Esc 关闭"),...It.map((r,t)=>{const e=St+t;return H(u,{key:r.name},H(i,{color:"cyan",bold:e===at},(e===at?" ❯ ":" ")+"/"+r.name+(r.args?" "+r.args:"")),H(i,{color:"gray"}," "+String(r.desc||"").replace(/\s+/g," ").slice(0,46)))})):null,H(u,{marginTop:At?0:1},H(i,{color:"blue"},"─".repeat(Ot))),H(u,null,H(i,{color:"busy"===Wr?"yellow":"cyan"},"busy"===Wr?"⧗ ":"› "),H(i,null,_t),H(i,{inverse:!0},Ct),H(i,null,Ut)),H(i,{color:"blue"},"─".repeat(Ot)),H(u,{justifyContent:"space-between"},H(i,{color:"gray"},(et?et+" · ":"")+`${Lt} · ${r.model} · ${N[Zr]} ${Q[Zr]}`+(Hr?` · 排队 ${Hr}`:"")+" · ⇧Tab 模式 · /help"),Wt?H(i,{color:"exhausted"===Rt.level?"red":"yellow",bold:!0},Wt):H(i,null,"")))}function er({item:r}){const{kind:t,text:e}=r;if("user"===t)return H(u,{marginTop:1},H(i,{color:"cyan",bold:!0},"› "),H(i,{bold:!0},e));if("assistant"===t)return H(u,null,H(i,null,e));if("diff"===t)return H(u,{marginLeft:2},H(i,null,e));if("tool"===t)return H(i,{color:"cyan"}," ⏺ "+e);if("toolresult"===t)return H(i,{color:"gray"}," "+e);if("skip"===t)return H(i,{color:"gray"}," ⊘ "+e);if("info"===t){const r=/✗|失败|错误|error/i.test(e);return H(i,{color:r?"red":"yellow"},(r?" ✗ ":" · ")+e.replace(/^✗\s*/,""))}return H(i,null,e)}export async function runInk({config:r,cwd:t,session:e,yolo:n,initialPrompt:o,version:c}){process.stdout.write(x({model:r.model,cwd:t,version:c,platform:/^(agent|workflow|datahuman)-/.test(String(r.model||"")),yolo:n,consult:r.consult?r.consultLabel||r.consult:null,ink:!0})+"\n");const{extraTools:s,clients:u}=await k(r,t),i={clients:u},{waitUntilExit:a}=l(H(App,{config:r,cwd:t,session:e,extraTools:s,mcp:i,yolo:n,initialPrompt:o,version:c,onExit:null}));await a(),$(i.clients)}
|
|
1
|
+
import r,{useState as t,useRef as e,useReducer as n,useEffect as o,useMemo as l}from"react";import{execSync as c}from"node:child_process";import{render as s,Box as u,Text as i,Static as a,useInput as m,useApp as p}from"ink";import{runTurn as g}from"../agent.js";import{fetchUsage as f}from"../api.js";import{toolLabel as d}from"../render.js";import{renderMarkdown as h,looksLikeMarkdown as x}from"../markdown.js";import{renderWelcome as w}from"../banner.js";import{createSession as y,saveSession as b}from"../session.js";import{ensureSessionTitle as k}from"../title.js";import{createCheckpoints as v}from"../checkpoints.js";import{buildExtraTools as $}from"../repl.js";import{buildSystemPrompt as M}from"../context.js";import{closeMcp as T}from"../mcp.js";import{runSlash as E,listCommands as j}from"../commands.js";import{usageStatus as P,fmtReset as A}from"../usage.js";import{expandAtMentions as S,buildUserContent as D,runBangCommand as I,loadHistory as L,appendHistory as C}from"../input.js";import{listWorkspaceFiles as U}from"../executors.js";import{loadAllowRules as _,loadDenyRules as W,matchAllowRules as O,suggestRule as R,addProjectAllowRule as F,riskWarning as K}from"../settings.js";import{PLAN_DENY_REASON as Y,SANDBOX_DENY_REASON as q,sandboxBlocked as B,AUTOMATION_TOOLS as G,automationActionLabel as J,automationDetail as N,PERMISSION_MODES as Q,MODE_ICON as X,MODE_LABEL as z}from"../permissions.js";import{runVerifyLoop as H}from"../autofix.js";import{runAutoFlow as V}from"../autorun.js";const Z=r.createElement,rr=10;function tr(r,t=12){const e=String(r).split("\n");return e.length<=t?r:e.slice(0,t).join("\n")+`\n…(共 ${e.length} 行)`}function er(r){return(r=Number(r)||0)>=1e3?(r/1e3).toFixed(1)+"k":String(r)}const nr=["⠋","⠙","⠹","⠸","⠼","⠴","⠦","⠧","⠇","⠏"];export function App({config:r,cwd:s,session:w,extraTools:lr,mcp:cr,yolo:sr,initialPrompt:ur,version:ir,onExit:ar}){const mr=p(),[pr,gr]=t(s),fr=e(lr),dr=e([]),hr=e(0),xr=e(""),wr=e(w),yr=e(null),br=e(null),kr=e(new Set),vr=e(_(pr)),$r=e(W(pr)),Mr=e(!1),Tr=e(!1),Er=e(!1),jr=e(v()),Pr=e(!!sr),Ar=e(!1),Sr=e("idle"),Dr=e([]),Ir=e({list:[]}),Lr=e(L(pr)),Cr=e(null),Ur=e(""),[,_r]=n(r=>r+1,0),[Wr,Or]=n(r=>r+1,0),[Rr,Fr]=t(0),[Kr,Yr]=t("idle"),[qr,Br]=t({text:"",cursor:0}),[Gr,Jr]=t(null),[Nr,Qr]=t({prompt:0,completion:0}),[Xr,zr]=t(0),[Hr,Vr]=t([]),[Zr,rt]=t(0),[tt,et]=t(sr?"auto":"manual"),nt=()=>Mr.current?"plan":Tr.current?"sandbox":Pr.current?"auto":Er.current?"acceptEdits":"manual",[ot,lt]=t(""),ct=e(null),[st,ut]=t(0),[it,at]=t(null),mt=e(null),[pt,gt]=t(0),[ft,dt]=t(!1),ht=l(()=>j(pr),[pr]),[xt,wt]=t(0),[yt,bt]=t(!1),kt=l(()=>{try{return U(pr)}catch{return[]}},[pr]),[vt,$t]=t(!1),[Mt,Tt]=t(0),Et=e(0),jt=(r,t)=>{dr.current.push({id:hr.current++,kind:r,text:t}),Or()},Pt=()=>{if(xr.current){const t=xr.current,e=!1!==r.markdown&&x(t);jt("assistant",e?h(t):t),xr.current=""}},At=r=>{Sr.current=r?"busy":"idle",Yr(Sr.current)};o(()=>{if("busy"!==Kr)return;const r=setInterval(()=>ut(r=>(r+1)%nr.length),80);return()=>clearInterval(r)},[Kr]);const St=async()=>{if(r.apiKey)try{const t=await f(r.baseUrl,r.apiKey);mt.current=t,at(t)}catch{}};o(()=>{St();const r=setInterval(()=>{St()},18e4);return()=>clearInterval(r)},[]),o(()=>{if(!r.statusLine)return void lt("");const t=()=>{try{const t=c(r.statusLine,{cwd:pr,timeout:3e3,stdio:["ignore","pipe","ignore"]}).toString().split("\n")[0].slice(0,80);lt(t)}catch{lt("")}};t();const e=setInterval(t,5e3);return()=>clearInterval(e)},[r.statusLine]),o(()=>()=>{ct.current&&clearInterval(ct.current)},[]);const Dt=e(""),It={onDelta:r=>{Dt.current&&(Dt.current=""),xr.current+=r,_r()},onReasoning:r=>{Dt.current=(Dt.current+r).slice(-400),_r()},onToolStart:(r,t)=>{Dt.current="",Pt(),jt("tool",d(r,t))},onToolResult:(r,t)=>jt("toolresult",function(r,t=200){const e=String(r).split("\n")[0];return e.length>t?e.slice(0,t)+"…":e}(t)),onFileDiff:r=>jt("diff",r),onSkip:(r,t)=>jt("skip",d(r,t)+" — 已跳过"),onInfo:r=>{Pt(),jt("info",r)},onTodo:r=>Vr(r),onDecision:r=>{Pt();const t=(r.alternatives||[]).length?` · ${r.alternatives.length} 备选`:"",e="draft"===r.status?"(待确认草稿)":"";jt("info",`◆ 已记录决策:${r.title}${t}${e}`)},onTurnEnd:()=>{Dt.current="",Pt()}},Lt={request:(r,t,e,n)=>{if($r.current.length&&O($r.current,r,t))return Promise.resolve({allow:!1,reason:"该操作被拒绝规则阻止(/permissions 查看或移除)。"});if(Mr.current&&!n)return Promise.resolve({allow:!1,reason:Y});if(Tr.current&&B(r))return Promise.resolve({allow:!1,reason:q});if(n||Pr.current||kr.current.has(r)||O(vr.current,r,t))return Promise.resolve({allow:!0});if(Er.current&&("write_file"===r||"edit_file"===r))return Promise.resolve({allow:!0});const o=G.has(r);return new Promise(e=>{br.current={name:r,args:t,resolve:e,consent:o},Jr({label:o?J(r,t):d(r,t),detail:o?N(r,t):"",name:r,args:t,consent:o,risk:K(r,t)})})},isYolo:()=>Pr.current,setYolo:r=>{Pr.current=!!r,et(nt())},setPlanMode:r=>{Mr.current=!!r,et(nt())},isPlanMode:()=>Mr.current,setSandbox:r=>{Tr.current=!!r,et(nt())},isSandbox:()=>Tr.current,setAcceptEdits:r=>{Er.current=!!r,et(nt())},isAcceptEdits:()=>Er.current,setMode:r=>{Pr.current="auto"===r,Er.current="acceptEdits"===r,Mr.current="plan"===r,Tr.current="sandbox"===r,et(r)},getMode:()=>nt(),reloadRules:()=>{vr.current=_(pr),$r.current=W(pr)}},Ct=()=>{const r=Dr.current.shift();return rt(Dr.current.length),null==r?null:r},Ut=()=>{k(wr.current,r).then(r=>{r&&b(wr.current)}).catch(()=>{})},_t=async t=>{const e=P(mt.current);if(e&&"exhausted"===e.level)return void jt("info",`✗ Token 用量窗口(${e.label})已用尽,暂无法继续。${A(e.resetAtEpochMs)}`);const{text:n,images:o}=await S(t,pr,r),l=jr.current.snapshot({session:wr.current,label:t});wr.current.messages.push({role:"user",content:D(n,o)}),Ut(),At(!0),yr.current=new AbortController,wr.current.compaction||(wr.current.compaction={});const c=await g({config:r,messages:wr.current.messages,permissions:Lt,cwd:pr,extraTools:fr.current,events:It,signal:yr.current.signal,todoState:Ir.current,compaction:wr.current.compaction,pullQueued:Ct,fileJournal:l});return yr.current=null,Pt(),c?.usage&&Qr(r=>({prompt:r.prompt+c.usage.prompt,completion:r.completion+c.usage.completion})),c?.lastPromptTokens&&(zr(c.lastPromptTokens),wr.current.lastPromptTokens=c.lastPromptTokens),b(wr.current),Ut(),(c?.aborted||c?.error)&&Dr.current.length&&(Dr.current=[],rt(0),jt("info","已清空排队消息")),At(!1),St(),c},Wt=()=>{ar?.(),mr.exit()},Ot=async t=>{const e=t.trim();if(Br({text:"",cursor:0}),Cr.current=null,e){if(C(pr,e),Lr.current[Lr.current.length-1]!==e&&Lr.current.push(e),e.startsWith("/"))return(async t=>{const e={version:ir,config:r,cwd:pr,usage:Nr,permissions:Lt,extraTools:fr.current,session:wr.current,checkpoints:jr.current,print:r=>jt("info",String(r))},n=await E(t,e);if("exit"===n.action)return Wt();if("clear"===n.action&&(wr.current=y({cwd:pr,model:r.model,messages:[wr.current.messages[0]]}),Ir.current.list=[],jr.current.list.length=0,Vr([]),zr(0),dr.current=[],xr.current="",Fr(r=>r+1),jt("info","已开启新会话(旧会话仍可 --resume 恢复)")),"refresh"===n.action&&(Ir.current.list=[],Vr([]),zr(0)),"autofix"===n.action&&await H({command:n.command,maxIters:n.maxIters,cwd:pr,runTurnOnce:r=>_t(r),emit:r=>jt("info",r)}),"autorun"===n.action&&await V({goal:n.goal,config:r,permissions:Lt,cwd:pr,runTurnOnce:r=>_t(r),emit:r=>jt("info",r)}),"cd"===n.action){vr.current=_(n.cwd),$r.current=W(n.cwd),T(cr?.clients);const t=await $(r,n.cwd);fr.current=t.extraTools,cr&&(cr.clients=t.clients),wr.current=y({cwd:n.cwd,model:r.model,messages:[{role:"system",content:M(n.cwd,{memory:!1!==r.memoryEnabled})}]}),r.sessionId=wr.current.id,Ir.current.list=[],jr.current.list.length=0,Vr([]),zr(0),dr.current=[],xr.current="",Fr(r=>r+1),gr(n.cwd),jt("info","已切换到 "+n.cwd+"(新会话,旧会话已保存)")}"loop"===n.action&&(ct.current&&(clearInterval(ct.current),ct.current=null),n.stop?jt("info","已停止循环。"):(jt("info",`已开始循环:每 ${n.seconds}s 运行「${n.prompt}」(/loop off 停止)`),ct.current=setInterval(()=>{"busy"!==Sr.current&&(jt("user",n.prompt),_t(n.prompt))},1e3*n.seconds))),n.prompt&&("busy"===Sr.current?(Dr.current.push(n.prompt),rt(Dr.current.length)):(jt("user",n.prompt),await _t(n.prompt)))})(e);if(e.startsWith("!")&&e.length>1)return(async r=>{if("busy"===Sr.current)return void jt("info","正在处理上一条,! 命令请稍候再试");jt("user","! "+r);const{output:t,record:e}=await I(r,pr);jt("toolresult",tr(t)),wr.current.messages.push({role:"user",content:e}),b(wr.current)})(e.slice(1).trim());if("busy"===Sr.current)return jt("user",e),Dr.current.push(e),void rt(Dr.current.length);jt("user",e),await _t(e)}};o(()=>{!Ar.current&&ur&&(Ar.current=!0,Ot(ur))},[]);const Rt=/^\/(\S*)$/.exec(qr.text),Ft=Rt?Rt[1]:null,Kt=null==Ft?[]:ht.filter(r=>r.name.toLowerCase().startsWith(Ft.toLowerCase())),Yt=Kt.length>0&&!ft,qt=Kt.length<=rr?0:Math.max(0,Math.min(pt-Math.floor(5),Kt.length-rr)),Bt=Kt.slice(qt,qt+rr);o(()=>{gt(0),dt(!1)},[Ft]);const Gt=null==Ft?/(?:^|\s)@([^\s@]*)$/.exec(qr.text):null,Jt=Gt?Gt[1]:null,Nt=null==Jt?[]:(()=>{const r=Jt.toLowerCase(),t=[];for(const e of kt){const n=e.toLowerCase(),o=n.split("/").pop();let l=-1;if(r?o.startsWith(r)?l=3:o.includes(r)?l=2:n.includes(r)&&(l=1):l=0,l>=0&&t.push({f:e,rank:l}),!r&&t.length>=50)break}return t.sort((r,t)=>t.rank-r.rank||r.f.length-t.f.length||r.f.localeCompare(t.f)),t.slice(0,50).map(r=>r.f)})(),Qt=Nt.length>0&&!yt,Xt=Nt.length<=rr?0:Math.max(0,Math.min(xt-Math.floor(5),Nt.length-rr)),zt=Nt.slice(Xt,Xt+rr);o(()=>{wt(0),bt(!1)},[Jt]);const Ht=l(()=>{const r=new Set,t=[];for(let e=Lr.current.length-1;e>=0&&t.length<50;e--){const n=Lr.current[e];n&&!r.has(n)&&(r.add(n),t.push(n))}return t},[vt]),Vt=Ht.length<=rr?0:Math.max(0,Math.min(Mt-Math.floor(5),Ht.length-rr)),Zt=Ht.slice(Vt,Vt+rr);m((t,e)=>{if(br.current){const r=br.current,n=t=>{br.current=null,Jr(null),r.resolve(t)};if("y"===t||"1"===t)return n({allow:!0});if("a"===t||"2"===t)return kr.current.add(r.name),n({allow:!0});if("3"===t)return n({allow:!1,reason:"用户选择了「否」,该操作未执行。"});if("A"===t){kr.current.add(r.name);const t=R(r.name,r.args);try{F(pr,t),vr.current=_(pr),jt("info",`已写入允许规则:${t} → .huicai/settings.json`)}catch(r){jt("info",`规则写入失败(本会话仍生效):${r.message}`)}return n({allow:!0})}return"n"===t||e.escape||e.return?n({allow:!1,reason:"用户拒绝了该操作。"}):void 0}if(vt){if(!Ht.length)return void $t(!1);if(e.upArrow)return Tt(r=>(r-1+Ht.length)%Ht.length);if(e.downArrow)return Tt(r=>(r+1)%Ht.length);if(e.pageUp)return Tt(r=>Math.max(0,r-rr));if(e.pageDown)return Tt(r=>Math.min(Ht.length-1,r+rr));if(e.return){const r=Ht[Math.min(Mt,Ht.length-1)]||"";return $t(!1),Cr.current=null,Br({text:r,cursor:r.length})}return e.escape?$t(!1):void 0}if(e.tab&&e.shift){const t=Q[(Q.indexOf(Lt.getMode())+1)%Q.length];return Lt.setMode(n=t),r.planMode="plan"===n,r.sandbox="sandbox"===n,void jt("info",`对话模式 → ${X[n]} ${z[n]}`+("auto"===n?"(已开放所有权限,任何命令免审批)":""))}var n;if(Yt){const r=()=>Kt[Math.min(pt,Kt.length-1)];if(e.upArrow)return gt(r=>(r-1+Kt.length)%Kt.length);if(e.downArrow)return gt(r=>(r+1)%Kt.length);if(e.pageUp)return gt(r=>Math.max(0,r-rr));if(e.pageDown)return gt(r=>Math.min(Kt.length-1,r+rr));if(e.tab){const t=r().name;return Br({text:"/"+t+" ",cursor:t.length+2})}if(e.return)return void Ot("/"+r().name);if(e.escape)return dt(!0)}if(Qt){const r=()=>Nt[Math.min(xt,Nt.length-1)];if(e.upArrow)return wt(r=>(r-1+Nt.length)%Nt.length);if(e.downArrow)return wt(r=>(r+1)%Nt.length);if(e.pageUp)return wt(r=>Math.max(0,r-rr));if(e.pageDown)return wt(r=>Math.min(Nt.length-1,r+rr));if(e.tab||e.return){const t=r(),e=qr.text.length-(Jt?Jt.length:0)-1,n=qr.text.slice(0,e+1)+t+" ";return Br({text:n,cursor:n.length})}if(e.escape)return bt(!0)}if(e.escape){if("busy"===Sr.current&&yr.current)return yr.current.abort(),void jt("info","已请求中断(Esc)");const r=Date.now();return r-Et.current<600&&Lr.current.length?(Et.current=0,Tt(0),void $t(!0)):void(Et.current=r)}if(e.ctrl&&"c"===t)"busy"===Sr.current&&yr.current?(yr.current.abort(),jt("info","已请求中断")):Wt();else if(e.return)Ot(qr.text);else{if(e.upArrow)return(()=>{const r=Lr.current;if(!r.length)return;null==Cr.current?(Ur.current=qr.text,Cr.current=r.length-1):Cr.current>0&&Cr.current--;const t=r[Cr.current]??"";Br({text:t,cursor:t.length})})();if(e.downArrow)return(()=>{if(null==Cr.current)return;const r=Lr.current;if(Cr.current<r.length-1){Cr.current++;const t=r[Cr.current];Br({text:t,cursor:t.length})}else Cr.current=null,Br({text:Ur.current,cursor:Ur.current.length})})();if(e.leftArrow)return Br(r=>({...r,cursor:Math.max(0,r.cursor-1)}));if(e.rightArrow)return Br(r=>({...r,cursor:Math.min(r.text.length,r.cursor+1)}));if(e.ctrl&&"a"===t)return Br(r=>({...r,cursor:0}));if(e.ctrl&&"e"===t)return Br(r=>({...r,cursor:r.text.length}));if(e.ctrl&&"u"===t)return Br(r=>({text:r.text.slice(r.cursor),cursor:0}));if(e.ctrl&&"k"===t)return Br(r=>({text:r.text.slice(0,r.cursor),cursor:r.cursor}));if(e.ctrl&&"w"===t)return Br(r=>{const t=r.text.slice(0,r.cursor).replace(/\S+\s*$/,"");return{text:t+r.text.slice(r.cursor),cursor:t.length}});if(e.backspace||e.delete)return Cr.current=null,Br(r=>r.cursor>0?{text:r.text.slice(0,r.cursor-1)+r.text.slice(r.cursor),cursor:r.cursor-1}:r);if(t&&!e.ctrl&&!e.meta){Cr.current=null;const r=t.replace(/\r\n?/g,"\n");Br(t=>({text:t.text.slice(0,t.cursor)+r+t.text.slice(t.cursor),cursor:t.cursor+r.length}))}}});const re=l(()=>dr.current.slice(),[Wr]),te=pr.split("/").filter(Boolean).pop()||pr,ee=qr.text.slice(0,qr.cursor),ne=qr.text[qr.cursor]??" ",oe=qr.text.slice(qr.cursor+1),le=Math.max(8,process.stdout.columns||80),ce=P(it),se=ce?"exhausted"===ce.level?`⛔ ${ce.label}用量已用尽${A(ce.resetAtEpochMs)?" · "+A(ce.resetAtEpochMs):""}`:`⚠ ${ce.label}用量 ${Math.round(100*ce.pct)}%(${er(ce.used)}/${er(ce.limit)})`:"";return Z(u,{flexDirection:"column"},Z(a,{key:Rr,items:re},r=>Z(or,{key:r.id,item:r})),Dt.current?Z(u,null,Z(i,{color:"gray",italic:!0},"✦ 思考 "+Dt.current.replace(/\n/g," ").slice(-160))):null,xr.current?Z(u,{marginTop:0},Z(i,null,xr.current)):null,Hr.length?Z(u,{flexDirection:"column",paddingLeft:2,marginTop:1},...Hr.map((r,t)=>Z(i,{key:t,color:"completed"===r.status?"green":"in_progress"===r.status?"cyan":"gray",dimColor:"completed"===r.status,bold:"in_progress"===r.status},("completed"===r.status?"☑ ":"in_progress"===r.status?"◐ ":"☐ ")+r.content))):null,"busy"!==Kr||xr.current?null:Z(u,null,Z(i,{color:"cyan"},nr[st]+" 思考中…"),Z(i,{color:"gray"},"(Esc 中断 · 输入回车可排队补充)")),Gr?Z(u,{flexDirection:"column",borderStyle:"round",borderColor:"yellow",paddingX:1,marginTop:1},Z(i,{color:"yellow",bold:!0},Gr.consent?"● 是否继续?"+Gr.label:"● 需要授权:"+Gr.label),Gr.risk?Z(i,{color:"red",bold:!0},"⚠ "+Gr.risk):null,Gr.consent&&Gr.detail?Z(i,{color:"gray"},tr(Gr.detail,6)):null,"run_bash"===Gr.name?Z(i,{color:"gray"},"$ "+(Gr.args.command||"")):null,"fetch_url"===Gr.name?Z(i,{color:"gray"},"GET "+(Gr.args.url||"")):null,Z(u,{marginTop:1}),...Gr.consent?[Z(u,{key:"c1"},Z(i,{color:"green",bold:!0}," [1] "),Z(i,null,"是,继续")),Z(u,{key:"c2"},Z(i,{color:"cyan",bold:!0}," [2] "),Z(i,null,"是,并在本次对话中后续相同操作不再询问")),Z(u,{key:"c3"},Z(i,{color:"gray",bold:!0}," [3] "),Z(i,{color:"gray"},"否(或按 Esc / 回车)"))]:[Z(u,{key:"a1"},Z(i,{color:"green",bold:!0}," [y] "),Z(i,null,"允许一次")),Z(u,{key:"a2"},Z(i,{color:"cyan",bold:!0}," [a] "),Z(i,null,"本次对话都允许")),Z(u,{key:"a3"},Z(i,{color:"blue",bold:!0}," [A] "),Z(i,null,"永久允许(写入项目设置)")),Z(u,{key:"a4"},Z(i,{color:"gray",bold:!0}," [n] "),Z(i,{color:"gray"},"拒绝(或按 Esc / 回车)"))]):null,Yt?Z(u,{flexDirection:"column",marginTop:1},Z(i,{color:"gray"},Kt.length>rr?` 斜杠命令 ${qt+1}-${qt+Bt.length}/${Kt.length} · ↑↓ 选择 · PgUp/PgDn 翻页 · Tab 补全 · ⏎ 执行 · Esc 关闭`:" 斜杠命令 · ↑↓ 选择 · Tab 补全 · ⏎ 执行 · Esc 关闭"),...Bt.map((r,t)=>{const e=qt+t;return Z(u,{key:r.name},Z(i,{color:"cyan",bold:e===pt},(e===pt?" ❯ ":" ")+"/"+r.name+(r.args?" "+r.args:"")),Z(i,{color:"gray"}," "+String(r.desc||"").replace(/\s+/g," ").slice(0,46)))})):null,Qt?Z(u,{flexDirection:"column",marginTop:1},Z(i,{color:"gray"},Nt.length>rr?` @ 引用文件 ${Xt+1}-${Xt+zt.length}/${Nt.length} · ↑↓ 选择 · PgUp/PgDn 翻页 · Tab/⏎ 插入 · Esc 关闭`:" @ 引用文件 · ↑↓ 选择 · Tab/⏎ 插入 · Esc 关闭"),...zt.map((r,t)=>{const e=Xt+t;return Z(u,{key:r},Z(i,{color:"cyan",bold:e===xt},(e===xt?" ❯ ":" ")+"@"+r))})):null,vt&&Ht.length?Z(u,{flexDirection:"column",marginTop:1},Z(i,{color:"gray"},Ht.length>rr?` 历史记录 ${Vt+1}-${Vt+Zt.length}/${Ht.length} · ↑↓ 选择 · PgUp/PgDn 翻页 · ⏎ 填入 · Esc 关闭`:" 历史记录 · ↑↓ 选择 · ⏎ 填入 · Esc 关闭"),...Zt.map((r,t)=>{const e=Vt+t;return Z(u,{key:e},Z(i,{color:"cyan",bold:e===Mt},(e===Mt?" ❯ ":" ")+r.replace(/\s+/g," ").slice(0,le-6)))})):null,Z(u,{marginTop:Yt||Qt||vt?0:1},Z(i,{color:"blue"},"─".repeat(le))),Z(u,null,Z(i,{color:"busy"===Kr?"yellow":"cyan"},"busy"===Kr?"⧗ ":"› "),Z(i,null,ee),Z(i,{inverse:!0},ne),Z(i,null,oe)),Z(i,{color:"blue"},"─".repeat(le)),Z(u,{justifyContent:"space-between"},Z(i,{color:"gray"},(ot?ot+" · ":"")+`${te} · ${r.model} · ${X[tt]} ${z[tt]}`+(Zr?` · 排队 ${Zr}`:"")+" · ⇧Tab 模式 · /help"),se?Z(i,{color:"exhausted"===ce.level?"red":"yellow",bold:!0},se):Z(i,null,"")))}function or({item:r}){const{kind:t,text:e}=r;if("user"===t)return Z(u,{marginTop:1},Z(i,{color:"cyan",bold:!0},"› "),Z(i,{bold:!0},e));if("assistant"===t)return Z(u,null,Z(i,null,e));if("diff"===t)return Z(u,{marginLeft:2},Z(i,null,e));if("tool"===t)return Z(i,{color:"cyan"}," ⏺ "+e);if("toolresult"===t)return Z(i,{color:"gray"}," "+e);if("skip"===t)return Z(i,{color:"gray"}," ⊘ "+e);if("info"===t){const r=/✗|失败|错误|error/i.test(e);return Z(i,{color:r?"red":"yellow"},(r?" ✗ ":" · ")+e.replace(/^✗\s*/,""))}return Z(i,null,e)}export async function runInk({config:r,cwd:t,session:e,yolo:n,initialPrompt:o,version:l}){process.stdout.write(w({model:r.model,cwd:t,version:l,platform:/^(agent|workflow|datahuman)-/.test(String(r.model||"")),yolo:n,consult:r.consult?r.consultLabel||r.consult:null,ink:!0})+"\n");const{extraTools:c,clients:u}=await $(r,t),i={clients:u},{waitUntilExit:a}=s(Z(App,{config:r,cwd:t,session:e,extraTools:c,mcp:i,yolo:n,initialPrompt:o,version:l,onExit:null}));await a(),T(i.clients)}
|