@huicai/cli 2026.711.24616 → 2026.711.24617
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/LICENSE +5 -5
- package/package.json +2 -2
- package/src/tui/app.js +1 -1
package/LICENSE
CHANGED
|
@@ -4,25 +4,25 @@ HuiCai AI — 专有软件许可协议 / Proprietary Software License
|
|
|
4
4
|
Copyright © 2026 HuiCai. All rights reserved.
|
|
5
5
|
|
|
6
6
|
本软件(包括 HuiCai AI 命令行工具、VS Code 扩展、JetBrains 插件及其捆绑的
|
|
7
|
-
核心代码、资源与文档,下称"本软件"
|
|
7
|
+
核心代码、资源与文档,下称"本软件")为HuiCai及其许可方的专有财产,受著作权法
|
|
8
8
|
及国际条约保护。本软件按许可授权使用,而非出售。
|
|
9
9
|
|
|
10
|
-
1.
|
|
10
|
+
1. 授权。在您遵守本协议的前提下,HuiCai授予您一项有限的、非独占的、不可转让、
|
|
11
11
|
不可再许可的权利,以目标代码形式安装并使用本软件,用于您的内部业务用途。
|
|
12
12
|
|
|
13
|
-
2.
|
|
13
|
+
2. 限制。除适用法律明确允许或经HuiCai事先书面同意外,您不得:
|
|
14
14
|
(a) 复制、分发、出售、出租、出借、再许可或以其他方式向第三方提供本软件;
|
|
15
15
|
(b) 修改、翻译、改编本软件,或基于本软件创作衍生作品;
|
|
16
16
|
(c) 反向工程、反编译、反汇编本软件,或以其他方式尝试获取其源代码或内部结构
|
|
17
17
|
(适用法律明确不可排除者除外);
|
|
18
18
|
(d) 去除或更改本软件中的任何专有声明、标识或标记。
|
|
19
19
|
|
|
20
|
-
3.
|
|
20
|
+
3. 所有权。本软件的一切权利、所有权及知识产权均归HuiCai及其许可方所有。本协议
|
|
21
21
|
未明确授予的权利均予保留。
|
|
22
22
|
|
|
23
23
|
4. 无担保。本软件按"现状"提供,不附带任何明示或默示担保。
|
|
24
24
|
|
|
25
|
-
5.
|
|
25
|
+
5. 责任限制。在法律允许的最大范围内,HuiCai不对因使用或无法使用本软件而产生的
|
|
26
26
|
任何间接、附带、特殊或后果性损害承担责任。
|
|
27
27
|
|
|
28
28
|
6. 终止。若您违反本协议,本授权自动终止;届时您须停止使用并销毁本软件的全部
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@huicai/cli",
|
|
3
|
-
"version": "2026.711.
|
|
4
|
-
"versionDisplay": "2026.07.11.
|
|
3
|
+
"version": "2026.711.24617",
|
|
4
|
+
"versionDisplay": "2026.07.11.24617",
|
|
5
5
|
"description": "在终端运行的智能体 CLI:以 HuiCai_AI 平台的 OpenAI 兼容 /v1 为模型后端,在用户本机执行文件/命令工具。",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"bin": {
|
package/src/tui/app.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import r,{useState as e,useRef as t,useReducer as n,useEffect as o,useMemo as s}from"react";import{execSync as c}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 g}from"../api.js";import{toolLabel as d}from"../render.js";import{renderMarkdown as h,looksLikeMarkdown as x}from"../markdown.js";import{renderWelcome as y}from"../banner.js";import{createSession as w,saveSession as b}from"../session.js";import{createCheckpoints as v}from"../checkpoints.js";import{buildExtraTools as k}from"../repl.js";import{closeMcp as T}from"../mcp.js";import{runSlash as $,listCommands as j}from"../commands.js";import{usageStatus as S,fmtReset as A}from"../usage.js";import{expandAtMentions as I,buildUserContent as P,runBangCommand as E,loadHistory as M,appendHistory as L}from"../input.js";import{loadAllowRules as _,loadDenyRules as C,matchAllowRules as D,suggestRule as R,addProjectAllowRule as U,riskWarning as W}from"../settings.js";import{PLAN_DENY_REASON as F,SANDBOX_DENY_REASON as K}from"../permissions.js";import{runVerifyLoop as Y}from"../autofix.js";const q=r.createElement;function B(r){return(r=Number(r)||0)>=1e3?(r/1e3).toFixed(1)+"k":String(r)}const G=["⠋","⠙","⠹","⠸","⠼","⠴","⠦","⠧","⠇","⠏"];export function App({config:r,cwd:l,session:y,extraTools:k,yolo:T,initialPrompt:N,version:O,onExit:Q}){const X=p(),z=t([]),H=t(0),V=t(""),Z=t(y),rr=t(null),er=t(null),tr=t(new Set),nr=t(_(l)),or=t(C(l)),sr=t(!1),cr=t(!1),lr=t(v()),ur=t(!!T),ir=t(!1),ar=t("idle"),mr=t([]),pr=t({list:[]}),fr=t(M(l)),gr=t(null),dr=t(""),[,hr]=n(r=>r+1,0),[xr,yr]=n(r=>r+1,0),[wr,br]=e(0),[vr,kr]=e("idle"),[Tr,$r]=e({text:"",cursor:0}),[jr,Sr]=e(null),[Ar,Ir]=e({prompt:0,completion:0}),[Pr,Er]=e(0),[Mr,Lr]=e([]),[_r,Cr]=e(0),[Dr,Rr]=e(!1),[Ur,Wr]=e(""),Fr=t(null),[Kr,Yr]=e(0),[qr,Br]=e(null),Gr=t(null),[Jr,Nr]=e(0),[Or,Qr]=e(!1),Xr=s(()=>j(l),[l]),zr=(r,e)=>{z.current.push({id:H.current++,kind:r,text:e}),yr()},Hr=()=>{if(V.current){const e=V.current,t=!1!==r.markdown&&x(e);zr("assistant",t?h(e):e),V.current=""}},Vr=r=>{ar.current=r?"busy":"idle",kr(ar.current)};o(()=>{if("busy"!==vr)return;const r=setInterval(()=>Yr(r=>(r+1)%G.length),80);return()=>clearInterval(r)},[vr]);const Zr=async()=>{if(r.apiKey)try{const e=await g(r.baseUrl,r.apiKey);Gr.current=e,Br(e)}catch{}};o(()=>{Zr();const r=setInterval(()=>{Zr()},18e4);return()=>clearInterval(r)},[]),o(()=>{if(!r.statusLine)return void Wr("");const e=()=>{try{const e=c(r.statusLine,{cwd:l,timeout:3e3,stdio:["ignore","pipe","ignore"]}).toString().split("\n")[0].slice(0,80);Wr(e)}catch{Wr("")}};e();const t=setInterval(e,5e3);return()=>clearInterval(t)},[r.statusLine]),o(()=>()=>{Fr.current&&clearInterval(Fr.current)},[]);const re=t(""),ee={onDelta:r=>{re.current&&(re.current=""),V.current+=r,hr()},onReasoning:r=>{re.current=(re.current+r).slice(-400),hr()},onToolStart:(r,e)=>{re.current="",Hr(),zr("tool",d(r,e))},onToolResult:(r,e)=>zr("toolresult",function(r,e=200){const t=String(r).split("\n")[0];return t.length>e?t.slice(0,e)+"…":t}(e)),onFileDiff:r=>zr("diff",r),onSkip:(r,e)=>zr("skip",d(r,e)+" — 已跳过"),onInfo:r=>{Hr(),zr("info",r)},onTodo:r=>Lr(r),onTurnEnd:()=>{re.current="",Hr()}},te={request:(r,e,t,n)=>or.current.length&&D(or.current,r,e)?Promise.resolve({allow:!1,reason:"该操作被拒绝规则阻止(/permissions 查看或移除)。"}):sr.current&&!n?Promise.resolve({allow:!1,reason:F}):!cr.current||"run_bash"!==r&&"fetch_url"!==r?n||ur.current||tr.current.has(r)||D(nr.current,r,e)?Promise.resolve({allow:!0}):new Promise(t=>{er.current={name:r,args:e,resolve:t},Sr({label:d(r,e),name:r,args:e,risk:W(r,e)})}):Promise.resolve({allow:!1,reason:K}),isYolo:()=>ur.current,setYolo:r=>{ur.current=!!r},setPlanMode:r=>{sr.current=!!r,Rr(!!r)},isPlanMode:()=>sr.current,setSandbox:r=>{cr.current=!!r},isSandbox:()=>cr.current,reloadRules:()=>{nr.current=_(l),or.current=C(l)}},ne=()=>{const r=mr.current.shift();return Cr(mr.current.length),null==r?null:r},oe=async e=>{const t=S(Gr.current);if(t&&"exhausted"===t.level)return void zr("info",`✗ Token 用量窗口(${t.label})已用尽,暂无法继续。${A(t.resetAtEpochMs)}`);const{text:n,images:o}=await I(e,l,r),s=lr.current.snapshot({session:Z.current,label:e});Z.current.messages.push({role:"user",content:P(n,o)}),Vr(!0),rr.current=new AbortController,Z.current.compaction||(Z.current.compaction={});const c=await f({config:r,messages:Z.current.messages,permissions:te,cwd:l,extraTools:k,events:ee,signal:rr.current.signal,todoState:pr.current,compaction:Z.current.compaction,pullQueued:ne,fileJournal:s});return rr.current=null,Hr(),c?.usage&&Ir(r=>({prompt:r.prompt+c.usage.prompt,completion:r.completion+c.usage.completion})),c?.lastPromptTokens&&(Er(c.lastPromptTokens),Z.current.lastPromptTokens=c.lastPromptTokens),b(Z.current),(c?.aborted||c?.error)&&mr.current.length&&(mr.current=[],Cr(0),zr("info","已清空排队消息")),Vr(!1),Zr(),c},se=()=>{Q?.(),X.exit()},ce=async r=>{if("busy"===ar.current)return void zr("info","正在处理上一条,! 命令请稍候再试");zr("user","! "+r);const{output:e,record:t}=await E(r,l);zr("toolresult",function(r,e=12){const t=String(r).split("\n");return t.length<=e?r:t.slice(0,e).join("\n")+`\n…(共 ${t.length} 行)`}(e)),Z.current.messages.push({role:"user",content:t}),b(Z.current)},le=async e=>{const t=e.trim();if($r({text:"",cursor:0}),gr.current=null,t){if(L(l,t),fr.current[fr.current.length-1]!==t&&fr.current.push(t),t.startsWith("/"))return(async e=>{const t={version:O,config:r,cwd:l,usage:Ar,permissions:te,extraTools:k,session:Z.current,checkpoints:lr.current,print:r=>zr("info",String(r))},n=await $(e,t);if("exit"===n.action)return se();"clear"===n.action&&(Z.current=w({cwd:l,model:r.model,messages:[Z.current.messages[0]]}),pr.current.list=[],lr.current.list.length=0,Lr([]),Er(0),z.current=[],V.current="",br(r=>r+1),zr("info","已开启新会话(旧会话仍可 --resume 恢复)")),"refresh"===n.action&&(pr.current.list=[],Lr([]),Er(0)),"autofix"===n.action&&await Y({command:n.command,maxIters:n.maxIters,cwd:l,runTurnOnce:r=>oe(r),emit:r=>zr("info",r)}),"cd"===n.action&&zr("info","富 TUI 暂不支持 /cd 切换目录;请用 huicai --plain(普通模式)使用,或退出后到目标目录重开。"),"loop"===n.action&&(Fr.current&&(clearInterval(Fr.current),Fr.current=null),n.stop?zr("info","已停止循环。"):(zr("info",`已开始循环:每 ${n.seconds}s 运行「${n.prompt}」(/loop off 停止)`),Fr.current=setInterval(()=>{"busy"!==ar.current&&(zr("user",n.prompt),oe(n.prompt))},1e3*n.seconds))),n.prompt&&("busy"===ar.current?(mr.current.push(n.prompt),Cr(mr.current.length)):(zr("user",n.prompt),await oe(n.prompt)))})(t);if(t.startsWith("!")&&t.length>1)return ce(t.slice(1).trim());if("busy"===ar.current)return zr("user",t),mr.current.push(t),void Cr(mr.current.length);zr("user",t),await oe(t)}};o(()=>{!ir.current&&N&&(ir.current=!0,le(N))},[]);const ue=/^\/(\S*)$/.exec(Tr.text),ie=ue?ue[1]:null,ae=null==ie?[]:Xr.filter(r=>r.name.toLowerCase().startsWith(ie.toLowerCase())).slice(0,8),me=ae.length>0&&!Or;o(()=>{Nr(0),Qr(!1)},[ie]),m((r,e)=>{if(er.current){const t=er.current,n=r=>{er.current=null,Sr(null),t.resolve(r)};if("y"===r)return n({allow:!0});if("a"===r)return tr.current.add(t.name),n({allow:!0});if("A"===r){tr.current.add(t.name);const r=R(t.name,t.args);try{U(l,r),nr.current=_(l),zr("info",`已写入允许规则:${r} → .huicai/settings.json`)}catch(r){zr("info",`规则写入失败(本会话仍生效):${r.message}`)}return n({allow:!0})}return"n"===r||e.escape||e.return?n({allow:!1,reason:"用户拒绝了该操作。"}):void 0}if(me){const r=()=>ae[Math.min(Jr,ae.length-1)];if(e.upArrow)return Nr(r=>(r-1+ae.length)%ae.length);if(e.downArrow)return Nr(r=>(r+1)%ae.length);if(e.tab){const e=r().name;return $r({text:"/"+e+" ",cursor:e.length+2})}if(e.return)return void le("/"+r().name);if(e.escape)return Qr(!0)}if(e.escape)"busy"===ar.current&&rr.current&&(rr.current.abort(),zr("info","已请求中断(Esc)"));else if(e.ctrl&&"c"===r)"busy"===ar.current&&rr.current?(rr.current.abort(),zr("info","已请求中断")):se();else if(e.return)le(Tr.text);else{if(e.upArrow)return(()=>{const r=fr.current;if(!r.length)return;null==gr.current?(dr.current=Tr.text,gr.current=r.length-1):gr.current>0&&gr.current--;const e=r[gr.current]??"";$r({text:e,cursor:e.length})})();if(e.downArrow)return(()=>{if(null==gr.current)return;const r=fr.current;if(gr.current<r.length-1){gr.current++;const e=r[gr.current];$r({text:e,cursor:e.length})}else gr.current=null,$r({text:dr.current,cursor:dr.current.length})})();if(e.leftArrow)return $r(r=>({...r,cursor:Math.max(0,r.cursor-1)}));if(e.rightArrow)return $r(r=>({...r,cursor:Math.min(r.text.length,r.cursor+1)}));if(e.ctrl&&"a"===r)return $r(r=>({...r,cursor:0}));if(e.ctrl&&"e"===r)return $r(r=>({...r,cursor:r.text.length}));if(e.ctrl&&"u"===r)return $r(r=>({text:r.text.slice(r.cursor),cursor:0}));if(e.ctrl&&"k"===r)return $r(r=>({text:r.text.slice(0,r.cursor),cursor:r.cursor}));if(e.ctrl&&"w"===r)return $r(r=>{const e=r.text.slice(0,r.cursor).replace(/\S+\s*$/,"");return{text:e+r.text.slice(r.cursor),cursor:e.length}});if(e.backspace||e.delete)return gr.current=null,$r(r=>r.cursor>0?{text:r.text.slice(0,r.cursor-1)+r.text.slice(r.cursor),cursor:r.cursor-1}:r);if(r&&!e.ctrl&&!e.meta){gr.current=null;const e=r.replace(/\r\n?/g,"\n");$r(r=>({text:r.text.slice(0,r.cursor)+e+r.text.slice(r.cursor),cursor:r.cursor+e.length}))}}});const pe=s(()=>z.current.slice(),[xr]),fe=l.split("/").filter(Boolean).pop()||l,ge=Tr.text.slice(0,Tr.cursor),de=Tr.text[Tr.cursor]??" ",he=Tr.text.slice(Tr.cursor+1),xe=Math.max(8,process.stdout.columns||80),ye=S(qr),we=ye?"exhausted"===ye.level?`⛔ ${ye.label}用量已用尽${A(ye.resetAtEpochMs)?" · "+A(ye.resetAtEpochMs):""}`:`⚠ ${ye.label}用量 ${Math.round(100*ye.pct)}%(${B(ye.used)}/${B(ye.limit)})`:"";return q(u,{flexDirection:"column"},q(a,{key:wr,items:pe},r=>q(J,{key:r.id,item:r})),re.current?q(u,null,q(i,{color:"gray",italic:!0},"✦ 思考 "+re.current.replace(/\n/g," ").slice(-160))):null,V.current?q(u,{marginTop:0},q(i,null,V.current)):null,Mr.length?q(u,{flexDirection:"column",paddingLeft:2,marginTop:1},...Mr.map((r,e)=>q(i,{key:e,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"!==vr||V.current?null:q(u,null,q(i,{color:"cyan"},G[Kr]+" 思考中…"),q(i,{color:"gray"},"(Esc 中断 · 输入回车可排队补充)")),jr?q(u,{flexDirection:"column",borderStyle:"round",borderColor:"yellow",paddingX:1,marginTop:1},q(i,{color:"yellow",bold:!0},"● 需要授权:"+jr.label),jr.risk?q(i,{color:"red",bold:!0},"⚠ "+jr.risk):null,"run_bash"===jr.name?q(i,{color:"gray"},"$ "+(jr.args.command||"")):null,"fetch_url"===jr.name?q(i,{color:"gray"},"GET "+(jr.args.url||"")):null,q(i,null,"[y] 允许一次 · [a] 本会话 · [A] 永久(写入项目设置) · [n/Esc] 拒绝")):null,me?q(u,{flexDirection:"column",marginTop:1},q(i,{color:"gray"}," 斜杠命令 · ↑↓ 选择 · Tab 补全 · ⏎ 执行 · Esc 关闭"),...ae.map((r,e)=>q(u,{key:r.name},q(i,{color:"cyan",bold:e===Jr},(e===Jr?" ❯ ":" ")+"/"+r.name+(r.args?" "+r.args:"")),q(i,{color:"gray"}," "+String(r.desc||"").replace(/\s+/g," ").slice(0,46))))):null,q(u,{marginTop:me?0:1},q(i,{color:"blue"},"─".repeat(xe))),q(u,null,q(i,{color:"busy"===vr?"yellow":"cyan"},"busy"===vr?"⧗ ":"› "),q(i,null,ge),q(i,{inverse:!0},de),q(i,null,he)),q(i,{color:"blue"},"─".repeat(xe)),q(u,{justifyContent:"space-between"},q(i,{color:"gray"},(Ur?Ur+" · ":"")+`${fe} · ${r.model}${ur.current?" · ⚠ yolo":""}${Dr?" · 📋 计划":""}`+(_r?` · 排队 ${_r}`:"")+" · /help"),we?q(i,{color:"exhausted"===ye.level?"red":"yellow",bold:!0},we):q(i,null,"")))}function J({item:r}){const{kind:e,text:t}=r;if("user"===e)return q(u,{marginTop:1},q(i,{color:"cyan",bold:!0},"› "),q(i,{bold:!0},t));if("assistant"===e)return q(u,null,q(i,null,t));if("diff"===e)return q(u,{marginLeft:2},q(i,null,t));if("tool"===e)return q(i,{color:"cyan"}," ⏺ "+t);if("toolresult"===e)return q(i,{color:"gray"}," "+t);if("skip"===e)return q(i,{color:"gray"}," ⊘ "+t);if("info"===e){const r=/✗|失败|错误|error/i.test(t);return q(i,{color:r?"red":"yellow"},(r?" ✗ ":" · ")+t.replace(/^✗\s*/,""))}return q(i,null,t)}export async function runInk({config:r,cwd:e,session:t,yolo:n,initialPrompt:o,version:s}){process.stdout.write(y({model:r.model,cwd:e,version:s,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 k(r,e),{waitUntilExit:i}=l(q(App,{config:r,cwd:e,session:t,extraTools:c,yolo:n,initialPrompt:o,version:s,onExit:()=>T(u)}));await i(),T(u)}
|
|
1
|
+
import r,{useState as t,useRef as e,useReducer as n,useEffect as o,useMemo as s}from"react";import{execSync as c}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 g}from"../api.js";import{toolLabel as d}from"../render.js";import{renderMarkdown as h,looksLikeMarkdown as x}from"../markdown.js";import{renderWelcome as y}from"../banner.js";import{createSession as w,saveSession as b}from"../session.js";import{createCheckpoints as v}from"../checkpoints.js";import{buildExtraTools as k}from"../repl.js";import{closeMcp as T}from"../mcp.js";import{runSlash as $,listCommands as j}from"../commands.js";import{usageStatus as P,fmtReset as S}from"../usage.js";import{expandAtMentions as M,buildUserContent as A,runBangCommand as E,loadHistory as I,appendHistory as D}from"../input.js";import{loadAllowRules as L,loadDenyRules as _,matchAllowRules as C,suggestRule as U,addProjectAllowRule as R,riskWarning as W}from"../settings.js";import{PLAN_DENY_REASON as F,SANDBOX_DENY_REASON as K}from"../permissions.js";import{runVerifyLoop as Y}from"../autofix.js";const q=r.createElement,B=10;function G(r){return(r=Number(r)||0)>=1e3?(r/1e3).toFixed(1)+"k":String(r)}const J=["⠋","⠙","⠹","⠸","⠼","⠴","⠦","⠧","⠇","⠏"];export function App({config:r,cwd:l,session:y,extraTools:k,yolo:T,initialPrompt:O,version:Q,onExit:X}){const z=p(),H=e([]),V=e(0),Z=e(""),rr=e(y),tr=e(null),er=e(null),nr=e(new Set),or=e(L(l)),sr=e(_(l)),cr=e(!1),lr=e(!1),ur=e(v()),ir=e(!!T),ar=e(!1),mr=e("idle"),pr=e([]),fr=e({list:[]}),gr=e(I(l)),dr=e(null),hr=e(""),[,xr]=n(r=>r+1,0),[yr,wr]=n(r=>r+1,0),[br,vr]=t(0),[kr,Tr]=t("idle"),[$r,jr]=t({text:"",cursor:0}),[Pr,Sr]=t(null),[Mr,Ar]=t({prompt:0,completion:0}),[Er,Ir]=t(0),[Dr,Lr]=t([]),[_r,Cr]=t(0),[Ur,Rr]=t(!1),[Wr,Fr]=t(""),Kr=e(null),[Yr,qr]=t(0),[Br,Gr]=t(null),Jr=e(null),[Nr,Or]=t(0),[Qr,Xr]=t(!1),zr=s(()=>j(l),[l]),Hr=(r,t)=>{H.current.push({id:V.current++,kind:r,text:t}),wr()},Vr=()=>{if(Z.current){const t=Z.current,e=!1!==r.markdown&&x(t);Hr("assistant",e?h(t):t),Z.current=""}},Zr=r=>{mr.current=r?"busy":"idle",Tr(mr.current)};o(()=>{if("busy"!==kr)return;const r=setInterval(()=>qr(r=>(r+1)%J.length),80);return()=>clearInterval(r)},[kr]);const rt=async()=>{if(r.apiKey)try{const t=await g(r.baseUrl,r.apiKey);Jr.current=t,Gr(t)}catch{}};o(()=>{rt();const r=setInterval(()=>{rt()},18e4);return()=>clearInterval(r)},[]),o(()=>{if(!r.statusLine)return void Fr("");const t=()=>{try{const t=c(r.statusLine,{cwd:l,timeout:3e3,stdio:["ignore","pipe","ignore"]}).toString().split("\n")[0].slice(0,80);Fr(t)}catch{Fr("")}};t();const e=setInterval(t,5e3);return()=>clearInterval(e)},[r.statusLine]),o(()=>()=>{Kr.current&&clearInterval(Kr.current)},[]);const tt=e(""),et={onDelta:r=>{tt.current&&(tt.current=""),Z.current+=r,xr()},onReasoning:r=>{tt.current=(tt.current+r).slice(-400),xr()},onToolStart:(r,t)=>{tt.current="",Vr(),Hr("tool",d(r,t))},onToolResult:(r,t)=>Hr("toolresult",function(r,t=200){const e=String(r).split("\n")[0];return e.length>t?e.slice(0,t)+"…":e}(t)),onFileDiff:r=>Hr("diff",r),onSkip:(r,t)=>Hr("skip",d(r,t)+" — 已跳过"),onInfo:r=>{Vr(),Hr("info",r)},onTodo:r=>Lr(r),onTurnEnd:()=>{tt.current="",Vr()}},nt={request:(r,t,e,n)=>sr.current.length&&C(sr.current,r,t)?Promise.resolve({allow:!1,reason:"该操作被拒绝规则阻止(/permissions 查看或移除)。"}):cr.current&&!n?Promise.resolve({allow:!1,reason:F}):!lr.current||"run_bash"!==r&&"fetch_url"!==r?n||ir.current||nr.current.has(r)||C(or.current,r,t)?Promise.resolve({allow:!0}):new Promise(e=>{er.current={name:r,args:t,resolve:e},Sr({label:d(r,t),name:r,args:t,risk:W(r,t)})}):Promise.resolve({allow:!1,reason:K}),isYolo:()=>ir.current,setYolo:r=>{ir.current=!!r},setPlanMode:r=>{cr.current=!!r,Rr(!!r)},isPlanMode:()=>cr.current,setSandbox:r=>{lr.current=!!r},isSandbox:()=>lr.current,reloadRules:()=>{or.current=L(l),sr.current=_(l)}},ot=()=>{const r=pr.current.shift();return Cr(pr.current.length),null==r?null:r},st=async t=>{const e=P(Jr.current);if(e&&"exhausted"===e.level)return void Hr("info",`✗ Token 用量窗口(${e.label})已用尽,暂无法继续。${S(e.resetAtEpochMs)}`);const{text:n,images:o}=await M(t,l,r),s=ur.current.snapshot({session:rr.current,label:t});rr.current.messages.push({role:"user",content:A(n,o)}),Zr(!0),tr.current=new AbortController,rr.current.compaction||(rr.current.compaction={});const c=await f({config:r,messages:rr.current.messages,permissions:nt,cwd:l,extraTools:k,events:et,signal:tr.current.signal,todoState:fr.current,compaction:rr.current.compaction,pullQueued:ot,fileJournal:s});return tr.current=null,Vr(),c?.usage&&Ar(r=>({prompt:r.prompt+c.usage.prompt,completion:r.completion+c.usage.completion})),c?.lastPromptTokens&&(Ir(c.lastPromptTokens),rr.current.lastPromptTokens=c.lastPromptTokens),b(rr.current),(c?.aborted||c?.error)&&pr.current.length&&(pr.current=[],Cr(0),Hr("info","已清空排队消息")),Zr(!1),rt(),c},ct=()=>{X?.(),z.exit()},lt=async r=>{if("busy"===mr.current)return void Hr("info","正在处理上一条,! 命令请稍候再试");Hr("user","! "+r);const{output:t,record:e}=await E(r,l);Hr("toolresult",function(r,t=12){const e=String(r).split("\n");return e.length<=t?r:e.slice(0,t).join("\n")+`\n…(共 ${e.length} 行)`}(t)),rr.current.messages.push({role:"user",content:e}),b(rr.current)},ut=async t=>{const e=t.trim();if(jr({text:"",cursor:0}),dr.current=null,e){if(D(l,e),gr.current[gr.current.length-1]!==e&&gr.current.push(e),e.startsWith("/"))return(async t=>{const e={version:Q,config:r,cwd:l,usage:Mr,permissions:nt,extraTools:k,session:rr.current,checkpoints:ur.current,print:r=>Hr("info",String(r))},n=await $(t,e);if("exit"===n.action)return ct();"clear"===n.action&&(rr.current=w({cwd:l,model:r.model,messages:[rr.current.messages[0]]}),fr.current.list=[],ur.current.list.length=0,Lr([]),Ir(0),H.current=[],Z.current="",vr(r=>r+1),Hr("info","已开启新会话(旧会话仍可 --resume 恢复)")),"refresh"===n.action&&(fr.current.list=[],Lr([]),Ir(0)),"autofix"===n.action&&await Y({command:n.command,maxIters:n.maxIters,cwd:l,runTurnOnce:r=>st(r),emit:r=>Hr("info",r)}),"cd"===n.action&&Hr("info","富 TUI 暂不支持 /cd 切换目录;请用 huicai --plain(普通模式)使用,或退出后到目标目录重开。"),"loop"===n.action&&(Kr.current&&(clearInterval(Kr.current),Kr.current=null),n.stop?Hr("info","已停止循环。"):(Hr("info",`已开始循环:每 ${n.seconds}s 运行「${n.prompt}」(/loop off 停止)`),Kr.current=setInterval(()=>{"busy"!==mr.current&&(Hr("user",n.prompt),st(n.prompt))},1e3*n.seconds))),n.prompt&&("busy"===mr.current?(pr.current.push(n.prompt),Cr(pr.current.length)):(Hr("user",n.prompt),await st(n.prompt)))})(e);if(e.startsWith("!")&&e.length>1)return lt(e.slice(1).trim());if("busy"===mr.current)return Hr("user",e),pr.current.push(e),void Cr(pr.current.length);Hr("user",e),await st(e)}};o(()=>{!ar.current&&O&&(ar.current=!0,ut(O))},[]);const it=/^\/(\S*)$/.exec($r.text),at=it?it[1]:null,mt=null==at?[]:zr.filter(r=>r.name.toLowerCase().startsWith(at.toLowerCase())),pt=mt.length>0&&!Qr,ft=mt.length<=B?0:Math.max(0,Math.min(Nr-Math.floor(5),mt.length-B)),gt=mt.slice(ft,ft+B);o(()=>{Or(0),Xr(!1)},[at]),m((r,t)=>{if(er.current){const e=er.current,n=r=>{er.current=null,Sr(null),e.resolve(r)};if("y"===r)return n({allow:!0});if("a"===r)return nr.current.add(e.name),n({allow:!0});if("A"===r){nr.current.add(e.name);const r=U(e.name,e.args);try{R(l,r),or.current=L(l),Hr("info",`已写入允许规则:${r} → .huicai/settings.json`)}catch(r){Hr("info",`规则写入失败(本会话仍生效):${r.message}`)}return n({allow:!0})}return"n"===r||t.escape||t.return?n({allow:!1,reason:"用户拒绝了该操作。"}):void 0}if(pt){const r=()=>mt[Math.min(Nr,mt.length-1)];if(t.upArrow)return Or(r=>(r-1+mt.length)%mt.length);if(t.downArrow)return Or(r=>(r+1)%mt.length);if(t.pageUp)return Or(r=>Math.max(0,r-B));if(t.pageDown)return Or(r=>Math.min(mt.length-1,r+B));if(t.tab){const t=r().name;return jr({text:"/"+t+" ",cursor:t.length+2})}if(t.return)return void ut("/"+r().name);if(t.escape)return Xr(!0)}if(t.escape)"busy"===mr.current&&tr.current&&(tr.current.abort(),Hr("info","已请求中断(Esc)"));else if(t.ctrl&&"c"===r)"busy"===mr.current&&tr.current?(tr.current.abort(),Hr("info","已请求中断")):ct();else if(t.return)ut($r.text);else{if(t.upArrow)return(()=>{const r=gr.current;if(!r.length)return;null==dr.current?(hr.current=$r.text,dr.current=r.length-1):dr.current>0&&dr.current--;const t=r[dr.current]??"";jr({text:t,cursor:t.length})})();if(t.downArrow)return(()=>{if(null==dr.current)return;const r=gr.current;if(dr.current<r.length-1){dr.current++;const t=r[dr.current];jr({text:t,cursor:t.length})}else dr.current=null,jr({text:hr.current,cursor:hr.current.length})})();if(t.leftArrow)return jr(r=>({...r,cursor:Math.max(0,r.cursor-1)}));if(t.rightArrow)return jr(r=>({...r,cursor:Math.min(r.text.length,r.cursor+1)}));if(t.ctrl&&"a"===r)return jr(r=>({...r,cursor:0}));if(t.ctrl&&"e"===r)return jr(r=>({...r,cursor:r.text.length}));if(t.ctrl&&"u"===r)return jr(r=>({text:r.text.slice(r.cursor),cursor:0}));if(t.ctrl&&"k"===r)return jr(r=>({text:r.text.slice(0,r.cursor),cursor:r.cursor}));if(t.ctrl&&"w"===r)return jr(r=>{const t=r.text.slice(0,r.cursor).replace(/\S+\s*$/,"");return{text:t+r.text.slice(r.cursor),cursor:t.length}});if(t.backspace||t.delete)return dr.current=null,jr(r=>r.cursor>0?{text:r.text.slice(0,r.cursor-1)+r.text.slice(r.cursor),cursor:r.cursor-1}:r);if(r&&!t.ctrl&&!t.meta){dr.current=null;const t=r.replace(/\r\n?/g,"\n");jr(r=>({text:r.text.slice(0,r.cursor)+t+r.text.slice(r.cursor),cursor:r.cursor+t.length}))}}});const dt=s(()=>H.current.slice(),[yr]),ht=l.split("/").filter(Boolean).pop()||l,xt=$r.text.slice(0,$r.cursor),yt=$r.text[$r.cursor]??" ",wt=$r.text.slice($r.cursor+1),bt=Math.max(8,process.stdout.columns||80),vt=P(Br),kt=vt?"exhausted"===vt.level?`⛔ ${vt.label}用量已用尽${S(vt.resetAtEpochMs)?" · "+S(vt.resetAtEpochMs):""}`:`⚠ ${vt.label}用量 ${Math.round(100*vt.pct)}%(${G(vt.used)}/${G(vt.limit)})`:"";return q(u,{flexDirection:"column"},q(a,{key:br,items:dt},r=>q(N,{key:r.id,item:r})),tt.current?q(u,null,q(i,{color:"gray",italic:!0},"✦ 思考 "+tt.current.replace(/\n/g," ").slice(-160))):null,Z.current?q(u,{marginTop:0},q(i,null,Z.current)):null,Dr.length?q(u,{flexDirection:"column",paddingLeft:2,marginTop:1},...Dr.map((r,t)=>q(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||Z.current?null:q(u,null,q(i,{color:"cyan"},J[Yr]+" 思考中…"),q(i,{color:"gray"},"(Esc 中断 · 输入回车可排队补充)")),Pr?q(u,{flexDirection:"column",borderStyle:"round",borderColor:"yellow",paddingX:1,marginTop:1},q(i,{color:"yellow",bold:!0},"● 需要授权:"+Pr.label),Pr.risk?q(i,{color:"red",bold:!0},"⚠ "+Pr.risk):null,"run_bash"===Pr.name?q(i,{color:"gray"},"$ "+(Pr.args.command||"")):null,"fetch_url"===Pr.name?q(i,{color:"gray"},"GET "+(Pr.args.url||"")):null,q(i,null,"[y] 允许一次 · [a] 本会话 · [A] 永久(写入项目设置) · [n/Esc] 拒绝")):null,pt?q(u,{flexDirection:"column",marginTop:1},q(i,{color:"gray"},mt.length>B?` 斜杠命令 ${ft+1}-${ft+gt.length}/${mt.length} · ↑↓ 选择 · PgUp/PgDn 翻页 · Tab 补全 · ⏎ 执行 · Esc 关闭`:" 斜杠命令 · ↑↓ 选择 · Tab 补全 · ⏎ 执行 · Esc 关闭"),...gt.map((r,t)=>{const e=ft+t;return q(u,{key:r.name},q(i,{color:"cyan",bold:e===Nr},(e===Nr?" ❯ ":" ")+"/"+r.name+(r.args?" "+r.args:"")),q(i,{color:"gray"}," "+String(r.desc||"").replace(/\s+/g," ").slice(0,46)))})):null,q(u,{marginTop:pt?0:1},q(i,{color:"blue"},"─".repeat(bt))),q(u,null,q(i,{color:"busy"===kr?"yellow":"cyan"},"busy"===kr?"⧗ ":"› "),q(i,null,xt),q(i,{inverse:!0},yt),q(i,null,wt)),q(i,{color:"blue"},"─".repeat(bt)),q(u,{justifyContent:"space-between"},q(i,{color:"gray"},(Wr?Wr+" · ":"")+`${ht} · ${r.model}${ir.current?" · ⚠ yolo":""}${Ur?" · 📋 计划":""}`+(_r?` · 排队 ${_r}`:"")+" · /help"),kt?q(i,{color:"exhausted"===vt.level?"red":"yellow",bold:!0},kt):q(i,null,"")))}function N({item:r}){const{kind:t,text:e}=r;if("user"===t)return q(u,{marginTop:1},q(i,{color:"cyan",bold:!0},"› "),q(i,{bold:!0},e));if("assistant"===t)return q(u,null,q(i,null,e));if("diff"===t)return q(u,{marginLeft:2},q(i,null,e));if("tool"===t)return q(i,{color:"cyan"}," ⏺ "+e);if("toolresult"===t)return q(i,{color:"gray"}," "+e);if("skip"===t)return q(i,{color:"gray"}," ⊘ "+e);if("info"===t){const r=/✗|失败|错误|error/i.test(e);return q(i,{color:r?"red":"yellow"},(r?" ✗ ":" · ")+e.replace(/^✗\s*/,""))}return q(i,null,e)}export async function runInk({config:r,cwd:t,session:e,yolo:n,initialPrompt:o,version:s}){process.stdout.write(y({model:r.model,cwd:t,version:s,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 k(r,t),{waitUntilExit:i}=l(q(App,{config:r,cwd:t,session:e,extraTools:c,yolo:n,initialPrompt:o,version:s,onExit:()=>T(u)}));await i(),T(u)}
|