@leviyuan/lodestar 0.12.7 → 0.12.8
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/lodestar.js +1 -1
- package/package.json +1 -1
package/dist/lodestar.js
CHANGED
|
@@ -336,7 +336,7 @@ ${bz(E6(x.error??"unknown error"))}`;let Z=[];if(Z.push(kz(x)?"dirty":"clean"),x
|
|
|
336
336
|
`),rL0()}var Wh=S0(()=>{_8();C8();FN()});var UN={};JZ(UN,{runGlmSetupCommand:()=>eL0,onTokenSourceEnable:()=>tL0});async function tL0(x,Z){let Q=Q8(Z);if(!Q){await W0(x.chatId,`❌ 未知 token source: ${Z}`);return}if(Q.enabled){await W0(x.chatId,`${Q.display} 已启用,发 \`model\` 选择。`);return}if(Q.kind==="glm-coding-plan")await W0(x.chatId,`启用 ${Q.display}:发送
|
|
337
337
|
\`\`\`
|
|
338
338
|
glm-setup <base_url> <token>
|
|
339
|
-
\`\`\``);else if(Q.kind==="codex-subscription")await W0(x.chatId,`${Q.display} 需要 ChatGPT 登录:在服务器执行 \`codex login\`,完成后重启 daemon(\`systemctl --user restart feishu-daemon\`)或重发 \`model\` 刷新。`)}async function eL0(x,Z){let Q=Z.trim().split(/\s+/);if(Q.length<2){await W0(x.chatId,"用法:`glm-setup <base_url> <token>`");return}let[J,X]=Q;try{zh("glm",{agent:"claude",base_url:J,auth_token:X});let z=Q8("glm");if(z)await z.refreshModels();await W0(x.chatId,`✅ ${z?.display??"GLM Coding Plan"} 已启用(base_url=${J})。发 \`model\` 重新选择。`)}catch(z){await W0(x.chatId,`❌ 启用失败: ${z?.message??z}`)}}var LN=S0(()=>{h1();$6();Wh()});async function Kh(x,Z,Q=""){let J=Z.trim().match(/^(?:wt|worktree)(?:\s+(.+))?$/i);if(J){if(x.startingAgy||x.runningAgy)return await W0(x.chatId,"⏳ agy 任务正在执行;请等待完成,或发送 stop 打断后再使用 wt。"),!0;return await x.runWorktreeCommand((J[1]??"").trim(),Q),!0}if(Z.trim().match(/^btw$/i)){if(x.startingAgy||x.runningAgy)return await W0(x.chatId,"⏳ agy 任务正在执行;请等待完成,或发送 stop 打断后再使用 btw。"),!0;return await x.runBtwCommand(Q),!0}if(Z.trim().match(/^bye$/i))return await x.runByeCommand(),!0;if(Z.trim().match(/^(?:fk|fork)$/i)){if(x.startingAgy||x.runningAgy)return await W0(x.chatId,"⏳ agy 任务正在执行;请等待完成,或发送 stop 打断后再使用 fk。"),!0;return await x.showForkList(),!0}if(Z.trim().match(/^(?:bk|back)$/i)){if(x.startingAgy||x.runningAgy)return await W0(x.chatId,"⏳ agy 任务正在执行;请等待完成,或发送 stop 打断后再使用 bk。"),!0;if(x.isRunning())await x.stop("回滚前终止",{announce:!1});return await x.showBackList(),!0}let X=Z.trim().match(/^agy(?:\s+([\s\S]+))?$/i);if(X)return await x.runAgyCommand((X[1]??"").trim()),!0;if(Z.trim().toLowerCase()==="task")return await x.showTasklistPanel(),!0;let z=Z.trim().match(/^glm-setup\s+([\s\S]+)$/i);if(z){let{runGlmSetupCommand:G}=await Promise.resolve().then(() => (LN(),UN));return await G(x,z[1].trim()),!0}let Y=aL0.get(Z.trim().toLowerCase());if(!Y)return!1;if((x.startingAgy||x.runningAgy)&&!["stop","kill","restart","hi","model"].includes(Y))return await W0(x.chatId,`⏳ agy 任务正在执行;请等待完成,或发送 stop 打断后再执行 ${Y}。`),!0;switch(Y){case"model":return await x.showModelPanel(),!0;case"compact":return await x.runCompactCommand(),!0;case"hi":{let K=!x.isRunning(),H=x.backendLabel(),W=K?await x.openStatusCard("hi",x.withModel(`\uD83D\uDE80 启动 ${H}`)):null,L=x.withModel(`\uD83D\uDE80 启动 ${H}`);if(!(K?await x.start({announce:!W,onStatus:(D)=>{L=D,x.setStatusCard(W,D)}}):!0))return await x.closeStatusCard(W,L.startsWith("❌")?L:"❌ 启动失败"),!0;if(W)return await x.replaceStatusCardWithConsole(W,x.withModel(x.withWorktreeInstructionNotice(`✅ ${x.backendLabel()} 已就绪`))),!0;if(K)await x.closeStatusCard(W,x.withModel(x.withWorktreeInstructionNotice(`✅ ${x.backendLabel()} 已就绪`)))}return await x.showConsole(),!0;case"stop":if(x.runningAgy)return await x.stopAgyTask("\uD83D\uDED1 agy 已打断"),!0;if(x.clearStaleIdleQueueState("stop"),x.clearMultiMsgBuffer("stop command"),!x.currentTurn&&x.pendingUserMessageCount===0&&x.pendingMidTurnMsgs.length===0){let K=await x.openStatusCard("stop","⚪ 当前没有正在执行的 turn","grey");if(K)await x.closeStatusCard(K,"⚪ 无正在执行的 turn");else await W0(x.chatId,"⚪ 当前没有正在执行的 turn");return!0}k(`session "${x.sessionName}": stop command — interrupt + drop count=${x.pendingUserMessageCount} midBuffer=${x.pendingMidTurnMsgs.length}`);let G=new Set;for(let[K,H]of[...x.pendingReactionIds.entries(),...x.currentBatchReactionIds.entries()]){if(H)P8(K,H);kx(K,"CrossMark"),G.add(K)}for(let K of x.pendingMidTurnMsgs)if(K.msgId&&!G.has(K.msgId))kx(K.msgId,"CrossMark");return x.pendingUserMessageCount=0,x.pendingMidTurnMsgs=[],x.pendingTurnInputs=[],x.lastUserOpenId="",x.pendingReactionIds=new Map,x.currentBatchReactionIds=new Map,x.userInterrupted=!0,x.interrupt(),await x.closeTurnCard("\uD83D\uDED1 打断"),!0;case"kill":{if(x.runningAgy)await x.stopAgyTask("\uD83D\uDED1 agy 已终止");let K=x.isRunning(),H=x.backendLabel(x.proc?.provider??x.currentProvider()),W=K?`\uD83D\uDED1 停止 ${H}`:"⚪ session 当前未运行",L=await x.openStatusCard("kill",W,K?"red":"grey");await x.stop("已终止",{announce:!L,onStatus:(N)=>{x.setStatusCard(L,N)}}),await x.closeStatusCard(L,K?`✅ ${H} 已终止`:`⚪ ${H} 未运行`)}return!0;case"restart":if(!x.isRunning())return await x.showResumeList(),!0;{let K=x.lastSessionId?x.lastSessionId.slice(0,8):"",H=x.backendLabel(x.proc?.provider??x.currentProvider()),W=x.isRunning()?x.withModel(`\uD83D\uDD01 重启 ${H}`):K?x.withModel(`\uD83D\uDD01 恢复上一会话 thread=${K}…`):x.withModel(`\uD83D\uDD01 启动 ${H}`),L=await x.openStatusCard("restart",W);if(x.runningAgy)x.setStatusCard(L,"\uD83D\uDED1 restart 前终止 agy"),await x.stopAgyTask("\uD83D\uDED1 restart 前已终止 agy");let N=W,D=await x.restart(!0,{announce:!L,onStatus:(v)=>{N=v,x.setStatusCard(L,v)}}),O=D?N.startsWith("✅")?N:x.withWorktreeInstructionNotice(K?"✅ 已恢复上一会话":`✅ ${x.backendLabel()} 已就绪`):N.startsWith("❌")?N:"❌ 重启失败";await x.closeStatusCard(L,D?x.withModel(O):O)}return!0;case"clear":if(!x.isRunning()){x.status="stopped",x.opts.onLifecycleChange?.();let K=await x.openStatusCard("clear","⚪ session 当前未运行","grey");if(K)await x.closeStatusCard(K,`⚪ ${x.backendLabel()} 未运行,clear 无效`);else await W0(x.chatId,`⚪ session "${x.sessionName}" 当前未运行,clear 无效;用 \`hi\` 启动或 \`restart\` 恢复上一会话`);return!0}{let K=await x.openStatusCard("clear","\uD83E\uDDF9 清空并启动新会话","orange"),H="\uD83E\uDDF9 清空并启动新会话",W=await x.restart(!1,{announce:!K,onStatus:(L)=>{H=L,x.setStatusCard(K,L)}});await x.closeStatusCard(K,W?x.withModel(x.withWorktreeInstructionNotice("✅ 已清空并启动新会话")):H.startsWith("❌")?H:"❌ 清空失败")}return!0}}var aL0;var Vh=S0(()=>{h1();K1();aL0=new Map([["hi","hi"],["stop","stop"],["st","stop"],["kill","kill"],["kl","kill"],["restart","restart"],["rs","restart"],["clear","clear"],["cl","clear"],["compact","compact"],["cm","compact"],["model","model"],["md","model"]])});function QN0(x,Z){let Q=!1,J=null,X=()=>{},z=()=>{},Y=x.sessionId,G=()=>{if(J)clearTimeout(J),J=null;x.off("context_compacted",W),x.off("exit",L),x.off("error",N)},K=(O)=>{if(Q)return;Q=!0,G(),X(O)},H=(O)=>{if(Q)return;Q=!0,G(),z(O)},W=(O)=>{if(O.phase==="start")return;if(O.threadId&&Y&&O.threadId!==Y)return;K(O)},L=()=>H(Error("codex app-server exited before context compaction completed")),N=(O)=>H(O instanceof Error?O:Error(String(O))),D=new Promise((O,v)=>{X=O,z=v,J=setTimeout(()=>{H(Error(`context compaction completion timed out after ${Z/1000}s`))},Z)});return x.on("context_compacted",W),x.once("exit",L),x.once("error",N),{promise:D,cancel(){if(Q)return;Q=!0,G()}}}function JN0(x,Z){let Q=[],J=new Set,X=(G,K)=>{let H=K.threadId??Z;if(H&&G.threadId&&G.threadId!==H)return!1;if(K.turnId)return G.turnId===K.turnId;return!0},z=(G)=>{for(let K=Q.length-1;K>=0;K--){let H=Q[K];if(H&&X(H,G))return H}return null},Y=(G)=>{if(Z&&G.threadId&&G.threadId!==Z)return;let K={usage:G.usage,contextWindow:G.contextWindow,threadId:G.threadId,turnId:G.turnId};Q.push(K);for(let H of[...J])if(X(K,H.notice))H.finish(K)};return x.on("token_usage",Y),{waitFor(G,K){let H=z(G);if(H)return Promise.resolve(H);return new Promise((W)=>{let L=null,N={notice:G,finish:(D)=>{if(L)clearTimeout(L);J.delete(N),W(D)}};L=setTimeout(()=>N.finish(null),K),J.add(N)})},cancel(){x.off("token_usage",Y);for(let G of[...J])G.finish(null)}}}function XN0(x){if(!x)return" · \uD83E\uDDE0 MISS";let Z=Jz(x.usage);if(Z==null)return" · \uD83E\uDDE0 MISS";let Q=Xz(x.contextWindow);return` · \uD83E\uDDE0 ${bu(Z,Q)} (${zz(Z,Q)})`}async function Hh(x){x.clearStaleIdleQueueState("compact");let Z=!x.currentTurn&&x.pendingUserMessageCount===0&&x.pendingMidTurnMsgs.length===0&&!x.openingTurn;if(!x.isRunning()||!x.proc){x.status="stopped",x.opts.onLifecycleChange?.();let H=await x.openStatusCard("compact","⚪ session 当前未运行","grey");if(H)await x.closeStatusCard(H,"⚪ 后端未运行,compact 无效");else await W0(x.chatId,`⚪ session "${x.sessionName}" 当前未运行,compact 无效;用 \`hi\` 启动或 \`restart\` 恢复上一会话`);return}if(!Z){let H=await x.openStatusCard("compact","⚠️ 当前 turn 正在执行","grey");if(H)await x.closeStatusCard(H,"⚠️ 先 stop 当前 turn,再 compact");else await W0(x.chatId,"⚠️ 当前 turn 正在执行,先 `stop` 后再 `compact`。");return}if(x.manualContextCompactionPending){let H=await x.openStatusCard("compact","⏳ 上下文压缩已在进行中","grey");if(H)await x.closeStatusCard(H,"⏳ 上下文压缩已在进行中");else await W0(x.chatId,"⏳ 上下文压缩已在进行中。");return}let Q=x.proc,J=Q.sessionId?Q.sessionId.slice(0,8):"",X=x.withModel(J?`\uD83E\uDDE0 压缩上下文 thread=${J}…`:"\uD83E\uDDE0 压缩上下文…"),z=await x.openStatusCard("compact",X,"orange"),Y=async(H)=>{if(z)await x.closeStatusCard(z,H);else await W0(x.chatId,H)},G=QN0(Q,YN0),K=JN0(Q,Q.sessionId);x.manualContextCompactionPending=!0;try{x.setStatusCard(z,x.withModel("\uD83E\uDDE0 发起上下文压缩")),await Q.compactThread(),x.setStatusCard(z,x.withModel("⏳ 等待压缩完成事件"));let H=await G.promise,W=await K.waitFor(H,ZN0),L=H.threadId?` thread=${H.threadId.slice(0,8)}…`:"";await Y(x.withModel(`✅ 上下文已压缩${L}${XN0(W)}`))}catch(H){G.cancel(),await Y(`❌ 上下文压缩失败: ${$1(H)}`)}finally{K.cancel(),x.manualContextCompactionPending=!1}}var YN0=120000,ZN0=1500;var Gh=S0(()=>{h1()});import{randomUUID as WN0}from"node:crypto";function KN0(x){let Z=x.currentTokenSource(),Q=x.currentModelLabel();return aQ().map((J)=>({provider:J.agent,sourceId:J.id,display:J.display+(Z?.id===J.id&&Q?` · ${Q}`:""),enabled:J.enabled,modelCount:J.models.length,selected:Z?.id===J.id}))}function VN0(x,Z){let Q=x.currentModelLabel(),J=x.currentEffortLabel(),X=x.currentTokenSource()?.id===Z.id;return Z.models.map((z)=>{let Y=X&&Q===z.model;return{provider:Z.agent,sourceId:Z.id,model:z.model,displayName:z.display,description:Z.display,enabled:!0,isDefault:!1,selected:Y,efforts:z.efforts.map((G)=>({effort:G,description:"",isDefault:G===z.defaultEffort,selected:Y&&J===G}))}})}async function $h(x){let Z=WN0(),Q=KN0(x);if(x.modelPanels.set(Z,{models:[]}),!await M1(x.chatId,BL({sessionName:x.sessionName,panelId:Z,currentDisplay:x.currentTokenSource()?.display??x.currentModelLabel(),providers:Q})))x.modelPanels.delete(Z),await R1(x.chatId,"❌ 模型面板发送失败")}async function Fh(x,Z,Q=""){let J=Z.trim(),X=Q8(J);if(!X)return{ok:!1,message:`未知账号: ${J}`};if(!X.enabled)return{ok:!1,message:`${X.display} 未配置,请先点「启用」`};let z=Q.trim(),Y=VN0(x,X);return x.modelPanels.set(z,{models:Y}),{ok:!0,message:"",card:vL({sessionName:x.sessionName,panelId:z,currentModel:x.currentModelLabel(),currentEffort:x.currentEffortLabel(),models:Y})}}function HN0(x,Z){return Z?.provider==="claude"||Z?.provider==="codex"?Z.provider:u7(x)}function GN0(x,Z){if(x.currentTurn)return"当前 turn 不变,后续新 turn 使用。";if(x.proc?.isAlive()&&x.proc.provider===Z)return Z==="codex"?"Codex 需重启进程生效(发 restart)。":"下一轮开始使用。";return`下次启动 ${A4(Z)} 时使用。`}async function Uh(x,Z,Q="",J="",X=null){let z=Z.trim();if(!z)return await W0(x.chatId,`❌ ${"模型为空"}`),{ok:!1,message:"模型为空"};let Y=HN0(z,X),G=x.modelPanels.get(Q.trim())?.models.find((K)=>K.model===z&&(K.provider??"codex")===Y);if(!G)return{ok:!1,message:"模型不在当前选项中,请重新发送 model"};if(G.enabled===!1)return{ok:!1,message:`${G.displayName} 未配置,请先点「启用」`};if(G.efforts.length===0)return{ok:!1,message:"模型未返回 effort"};return{ok:!0,message:"",card:ML({sessionName:x.sessionName,panelId:Q.trim(),currentModel:x.currentModelLabel(),currentEffort:x.currentEffortLabel(),model:G})}}async function Lh(x,Z,Q,J="",X="",z=""){let Y=Z.trim(),G=Q.trim();if(!Y)return{ok:!1,message:"模型为空"};let K=J.trim(),H=x.modelPanels.get(K),W=z==="claude"||z==="codex"?z:H?.models.find((c)=>c.model===Y)?.provider??u7(Y);if(W==="claude"){if(!G6(G))return{ok:!1,message:"Claude reasoning effort 无效"}}else if(!a8(G))return{ok:!1,message:"Codex reasoning effort 无效"};let L=G,N=H?.models.find((c)=>c.model===Y&&(c.provider??"codex")===W);if(!N||!N.efforts.some((c)=>c.effort===L))return{ok:!1,message:`${A4(W)} · ${Y}/${L} 不在选项中`};let D=!!N.sourceId&&x.currentTokenSource()?.id!==N.sourceId;if(x.currentProvider()===W&&!D&&x.currentModelLabel()===Y&&x.currentEffortLabel()===L)return x.modelPanels.delete(K),{ok:!0,message:`当前已是 ${A4(W)} · ${Y} / ${L}`,card:cz({sessionName:x.sessionName,provider:W,model:Y,effort:L,scope:"当前已是此设置,无需变更。"})};if(x.proc?.isAlive()&&x.proc.provider!==W&&(x.currentTurn||x.openingTurn||x.pendingUserMessageCount>0||x.pendingMidTurnMsgs.length>0))return{ok:!1,message:`当前 ${x.backendLabel(x.proc.provider)} turn 正在执行或排队;请等结束或 stop 后再切换到 ${A4(W)}`};let B=x.currentModelLabel()!==Y||D,j=!!(x.currentTurn||x.openingTurn||x.pendingUserMessageCount>0||x.pendingMidTurnMsgs.length>0);if(W==="claude"&&x.proc?.isAlive()&&x.proc.provider==="claude"&&B&&j)return{ok:!1,message:"当前 Claude turn 正在执行或排队;Claude 模型 profile 通过 env 生效,请等结束或 stop 后再切换"};try{if(x.proc?.isAlive()&&x.proc.provider===W){let T=N.sourceId?Q8(N.sourceId)?.resolveSpawnModel(Y)??Y:Y;await Ly(x.proc.setModelSettings(T,L),20000,"thread/settings/update")}await x.applyModelSelection(W,Y,L,N.sourceId);let c=GN0(x,W);return x.modelPanels.delete(K),{ok:!0,message:`已选择 ${A4(W)} · ${Y} / ${L}`,card:cz({sessionName:x.sessionName,provider:W,model:Y,effort:L,scope:c})}}catch(c){let T=`模型切换失败: ${$1(c)}`;return k(`session "${x.sessionName}": set model settings failed: ${$1(c)}`),await W0(x.chatId,`❌ ${T}`),{ok:!1,message:T}}}var Nh=S0(()=>{$6();w7();r9();y8();h1();K1()});function g6(x,Z,Q=!1){let J=x.worktreeProjectName();return gL({projectName:J,tasklistName:EL(J),binding:$4(J),notice:Z,confirmDelete:Q})}async function Dh(x){if(!await M1(x.chatId,g6(x)))await R1(x.chatId,"❌ task 面板发送失败")}async function Oh(x){let Z=x.worktreeProjectName();try{let Q=$4(Z),J=await Af(Z,x.chatId),X=Q?"已启用":`已启用 ${J.name}`;return{ok:!0,message:X,card:g6(x,{type:"success",content:`✅ ${X}`})}}catch(Q){let J=`启用失败: ${$1(Q)}`;return k(`session "${x.sessionName}": tasklist enable failed: ${$1(Q)}`),{ok:!1,message:J,card:g6(x,{type:"error",content:`❌ ${J}`})}}}function Bh(x,Z){let Q=x.worktreeProjectName(),J=$4(Q),X=Z.trim();if(!J)return{ok:!1,message:"未启用",card:g6(x,{type:"error",content:"❌ 未启用"})};if(J.guid!==X)return{ok:!1,message:"清单绑定已变化",card:g6(x,{type:"error",content:"❌ 清单绑定已变化,请重新打开 task 面板"})};return{ok:!0,message:"请再次确认删除",card:g6(x,{type:"error",content:"⚠️ 删除会删除所有清单内任务"},!0)}}async function vh(x,Z){let Q=x.worktreeProjectName(),J=Z.trim();try{let z=`已删除 ${(await Ef(Q,J)).name}`;return{ok:!0,message:z,card:g6(x,{type:"success",content:`✅ ${z}`})}}catch(X){let z=`删除失败: ${$1(X)}`;return k(`session "${x.sessionName}": tasklist delete failed: ${$1(X)}`),{ok:!1,message:z,card:g6(x,{type:"error",content:`❌ ${z}`})}}}var Mh=S0(()=>{y8();h1();K1();uz()});var Rh,Sh,Ch,jh;var Th=S0(()=>{Rh=["- 以 `[file: /abs/path]` 开头的文本表示该路径上挂着一个文件,相关时去读它。","- 在回复任意位置,独占一行写 `[[send: /abs/path]]` 即可把该文件作为单独一条消息送出。只在用户主动要文件、或你要交付最终产物时才发。"],Sh=["- 判断是否该发文件:导出附件/数据集、用户明确要文件或下载物时,应先写到本机绝对路径,再用独占一行的 `[[send: /abs/path]]` 发给用户;短总结、普通说明、无需留存的正文答复不要为了发文件而发文件。","- 使用图片生成工具生成图片后,不要再补充说明文字或 `[[send: ...]]`;Lodestar 会根据工具返回的本机图片路径自动发给用户。若你用脚本或文件编辑生成图片,按上一条用 `[[send: /abs/path]]` 发出。","- 每次调用 Bash / shell 命令时,第一行都必须写 shell 注释 `# desc: <一句中文说明>`,再写真正命令。这个注释只给 Lodestar 卡片做摘要,不要依赖它改变命令行为。"],Ch=[...Rh,'- 当你有问题需要澄清时,输出单独一行 `[[askusr: {...}]]`。JSON 必须单行、合法、紧凑。格式是 `{ "questions": [{ "question": "完整问题1", "options": ["问题1的可选方案A","问题1的可选方案B"] }, { "question": "完整问题2", "options": [...] }] }`。每个问题都必须提供 `options`,且每题至少 2 个选项;',"- 一旦输出 `[[askusr: ...]]`,立刻停止继续推理和继续回答,等待用户先回答。需要澄清时,应一次性把当前继续执行所需的所有问题都放进这一个 askusr 标记里,不要拆成多轮连续追问。",...Sh].join(`
|
|
339
|
+
\`\`\``);else if(Q.kind==="codex-subscription")await W0(x.chatId,`${Q.display} 需要 ChatGPT 登录:在服务器执行 \`codex login\`,完成后重启 daemon(\`systemctl --user restart feishu-daemon\`)或重发 \`model\` 刷新。`)}async function eL0(x,Z){let Q=Z.trim().split(/\s+/);if(Q.length<2){await W0(x.chatId,"用法:`glm-setup <base_url> <token>`");return}let[J,X]=Q;try{zh("glm",{agent:"claude",base_url:J,auth_token:X});let z=Q8("glm");if(z)await z.refreshModels();await W0(x.chatId,`✅ ${z?.display??"GLM Coding Plan"} 已启用(base_url=${J})。发 \`model\` 重新选择。`)}catch(z){await W0(x.chatId,`❌ 启用失败: ${z?.message??z}`)}}var LN=S0(()=>{h1();$6();Wh()});async function Kh(x,Z,Q=""){let J=Z.trim().match(/^(?:wt|worktree)(?:\s+(.+))?$/i);if(J){if(x.startingAgy||x.runningAgy)return await W0(x.chatId,"⏳ agy 任务正在执行;请等待完成,或发送 stop 打断后再使用 wt。"),!0;return await x.runWorktreeCommand((J[1]??"").trim(),Q),!0}if(Z.trim().match(/^btw$/i)){if(x.startingAgy||x.runningAgy)return await W0(x.chatId,"⏳ agy 任务正在执行;请等待完成,或发送 stop 打断后再使用 btw。"),!0;return await x.runBtwCommand(Q),!0}if(Z.trim().match(/^bye$/i))return await x.runByeCommand(),!0;if(Z.trim().match(/^(?:fk|fork)$/i)){if(x.startingAgy||x.runningAgy)return await W0(x.chatId,"⏳ agy 任务正在执行;请等待完成,或发送 stop 打断后再使用 fk。"),!0;return await x.showForkList(),!0}if(Z.trim().match(/^(?:bk|back)$/i)){if(x.startingAgy||x.runningAgy)return await W0(x.chatId,"⏳ agy 任务正在执行;请等待完成,或发送 stop 打断后再使用 bk。"),!0;if(x.isRunning())await x.stop("回滚前终止",{announce:!1});return await x.showBackList(),!0}let X=Z.trim().match(/^agy(?:\s+([\s\S]+))?$/i);if(X)return await x.runAgyCommand((X[1]??"").trim()),!0;if(Z.trim().toLowerCase()==="task")return await x.showTasklistPanel(),!0;let z=Z.trim().match(/^glm-setup\s+([\s\S]+)$/i);if(z){let{runGlmSetupCommand:G}=await Promise.resolve().then(() => (LN(),UN));return await G(x,z[1].trim()),!0}let Y=aL0.get(Z.trim().toLowerCase());if(!Y)return!1;if((x.startingAgy||x.runningAgy)&&!["stop","kill","restart","hi","model"].includes(Y))return await W0(x.chatId,`⏳ agy 任务正在执行;请等待完成,或发送 stop 打断后再执行 ${Y}。`),!0;switch(Y){case"model":return await x.showModelPanel(),!0;case"compact":return await x.runCompactCommand(),!0;case"hi":{let K=!x.isRunning(),H=x.backendLabel(),W=K?await x.openStatusCard("hi",x.withModel(`\uD83D\uDE80 启动 ${H}`)):null,L=x.withModel(`\uD83D\uDE80 启动 ${H}`);if(!(K?await x.start({announce:!W,onStatus:(D)=>{L=D,x.setStatusCard(W,D)}}):!0))return await x.closeStatusCard(W,L.startsWith("❌")?L:"❌ 启动失败"),!0;if(W)return await x.replaceStatusCardWithConsole(W,x.withModel(x.withWorktreeInstructionNotice(`✅ ${x.backendLabel()} 已就绪`))),!0;if(K)await x.closeStatusCard(W,x.withModel(x.withWorktreeInstructionNotice(`✅ ${x.backendLabel()} 已就绪`)))}return await x.showConsole(),!0;case"stop":if(x.runningAgy)return await x.stopAgyTask("\uD83D\uDED1 agy 已打断"),!0;if(x.clearStaleIdleQueueState("stop"),x.clearMultiMsgBuffer("stop command"),!x.currentTurn&&x.pendingUserMessageCount===0&&x.pendingMidTurnMsgs.length===0){let K=await x.openStatusCard("stop","⚪ 当前没有正在执行的 turn","grey");if(K)await x.closeStatusCard(K,"⚪ 无正在执行的 turn");else await W0(x.chatId,"⚪ 当前没有正在执行的 turn");return!0}k(`session "${x.sessionName}": stop command — interrupt + drop count=${x.pendingUserMessageCount} midBuffer=${x.pendingMidTurnMsgs.length}`);let G=new Set;for(let[K,H]of[...x.pendingReactionIds.entries(),...x.currentBatchReactionIds.entries()]){if(H)P8(K,H);kx(K,"CrossMark"),G.add(K)}for(let K of x.pendingMidTurnMsgs)if(K.msgId&&!G.has(K.msgId))kx(K.msgId,"CrossMark");return x.pendingUserMessageCount=0,x.pendingMidTurnMsgs=[],x.pendingTurnInputs=[],x.lastUserOpenId="",x.pendingReactionIds=new Map,x.currentBatchReactionIds=new Map,x.userInterrupted=!0,x.interrupt(),await x.closeTurnCard("\uD83D\uDED1 打断"),!0;case"kill":{if(x.runningAgy)await x.stopAgyTask("\uD83D\uDED1 agy 已终止");let K=x.isRunning(),H=x.backendLabel(x.proc?.provider??x.currentProvider()),W=K?`\uD83D\uDED1 停止 ${H}`:"⚪ session 当前未运行",L=await x.openStatusCard("kill",W,K?"red":"grey");await x.stop("已终止",{announce:!L,onStatus:(N)=>{x.setStatusCard(L,N)}}),await x.closeStatusCard(L,K?`✅ ${H} 已终止`:`⚪ ${H} 未运行`)}return!0;case"restart":if(!x.isRunning()&&x.selectedProvider!=="codex")return await x.showResumeList(),!0;{let K=x.lastSessionId?x.lastSessionId.slice(0,8):"",H=x.backendLabel(x.proc?.provider??x.currentProvider()),W=x.isRunning()?x.withModel(`\uD83D\uDD01 重启 ${H}`):K?x.withModel(`\uD83D\uDD01 恢复上一会话 thread=${K}…`):x.withModel(`\uD83D\uDD01 启动 ${H}`),L=await x.openStatusCard("restart",W);if(x.runningAgy)x.setStatusCard(L,"\uD83D\uDED1 restart 前终止 agy"),await x.stopAgyTask("\uD83D\uDED1 restart 前已终止 agy");let N=W,D=await x.restart(!0,{announce:!L,onStatus:(v)=>{N=v,x.setStatusCard(L,v)}}),O=D?N.startsWith("✅")?N:x.withWorktreeInstructionNotice(K?"✅ 已恢复上一会话":`✅ ${x.backendLabel()} 已就绪`):N.startsWith("❌")?N:"❌ 重启失败";await x.closeStatusCard(L,D?x.withModel(O):O)}return!0;case"clear":if(!x.isRunning()){x.status="stopped",x.opts.onLifecycleChange?.();let K=await x.openStatusCard("clear","⚪ session 当前未运行","grey");if(K)await x.closeStatusCard(K,`⚪ ${x.backendLabel()} 未运行,clear 无效`);else await W0(x.chatId,`⚪ session "${x.sessionName}" 当前未运行,clear 无效;用 \`hi\` 启动或 \`restart\` 恢复上一会话`);return!0}{let K=await x.openStatusCard("clear","\uD83E\uDDF9 清空并启动新会话","orange"),H="\uD83E\uDDF9 清空并启动新会话",W=await x.restart(!1,{announce:!K,onStatus:(L)=>{H=L,x.setStatusCard(K,L)}});await x.closeStatusCard(K,W?x.withModel(x.withWorktreeInstructionNotice("✅ 已清空并启动新会话")):H.startsWith("❌")?H:"❌ 清空失败")}return!0}}var aL0;var Vh=S0(()=>{h1();K1();aL0=new Map([["hi","hi"],["stop","stop"],["st","stop"],["kill","kill"],["kl","kill"],["restart","restart"],["rs","restart"],["clear","clear"],["cl","clear"],["compact","compact"],["cm","compact"],["model","model"],["md","model"]])});function QN0(x,Z){let Q=!1,J=null,X=()=>{},z=()=>{},Y=x.sessionId,G=()=>{if(J)clearTimeout(J),J=null;x.off("context_compacted",W),x.off("exit",L),x.off("error",N)},K=(O)=>{if(Q)return;Q=!0,G(),X(O)},H=(O)=>{if(Q)return;Q=!0,G(),z(O)},W=(O)=>{if(O.phase==="start")return;if(O.threadId&&Y&&O.threadId!==Y)return;K(O)},L=()=>H(Error("codex app-server exited before context compaction completed")),N=(O)=>H(O instanceof Error?O:Error(String(O))),D=new Promise((O,v)=>{X=O,z=v,J=setTimeout(()=>{H(Error(`context compaction completion timed out after ${Z/1000}s`))},Z)});return x.on("context_compacted",W),x.once("exit",L),x.once("error",N),{promise:D,cancel(){if(Q)return;Q=!0,G()}}}function JN0(x,Z){let Q=[],J=new Set,X=(G,K)=>{let H=K.threadId??Z;if(H&&G.threadId&&G.threadId!==H)return!1;if(K.turnId)return G.turnId===K.turnId;return!0},z=(G)=>{for(let K=Q.length-1;K>=0;K--){let H=Q[K];if(H&&X(H,G))return H}return null},Y=(G)=>{if(Z&&G.threadId&&G.threadId!==Z)return;let K={usage:G.usage,contextWindow:G.contextWindow,threadId:G.threadId,turnId:G.turnId};Q.push(K);for(let H of[...J])if(X(K,H.notice))H.finish(K)};return x.on("token_usage",Y),{waitFor(G,K){let H=z(G);if(H)return Promise.resolve(H);return new Promise((W)=>{let L=null,N={notice:G,finish:(D)=>{if(L)clearTimeout(L);J.delete(N),W(D)}};L=setTimeout(()=>N.finish(null),K),J.add(N)})},cancel(){x.off("token_usage",Y);for(let G of[...J])G.finish(null)}}}function XN0(x){if(!x)return" · \uD83E\uDDE0 MISS";let Z=Jz(x.usage);if(Z==null)return" · \uD83E\uDDE0 MISS";let Q=Xz(x.contextWindow);return` · \uD83E\uDDE0 ${bu(Z,Q)} (${zz(Z,Q)})`}async function Hh(x){x.clearStaleIdleQueueState("compact");let Z=!x.currentTurn&&x.pendingUserMessageCount===0&&x.pendingMidTurnMsgs.length===0&&!x.openingTurn;if(!x.isRunning()||!x.proc){x.status="stopped",x.opts.onLifecycleChange?.();let H=await x.openStatusCard("compact","⚪ session 当前未运行","grey");if(H)await x.closeStatusCard(H,"⚪ 后端未运行,compact 无效");else await W0(x.chatId,`⚪ session "${x.sessionName}" 当前未运行,compact 无效;用 \`hi\` 启动或 \`restart\` 恢复上一会话`);return}if(!Z){let H=await x.openStatusCard("compact","⚠️ 当前 turn 正在执行","grey");if(H)await x.closeStatusCard(H,"⚠️ 先 stop 当前 turn,再 compact");else await W0(x.chatId,"⚠️ 当前 turn 正在执行,先 `stop` 后再 `compact`。");return}if(x.manualContextCompactionPending){let H=await x.openStatusCard("compact","⏳ 上下文压缩已在进行中","grey");if(H)await x.closeStatusCard(H,"⏳ 上下文压缩已在进行中");else await W0(x.chatId,"⏳ 上下文压缩已在进行中。");return}let Q=x.proc,J=Q.sessionId?Q.sessionId.slice(0,8):"",X=x.withModel(J?`\uD83E\uDDE0 压缩上下文 thread=${J}…`:"\uD83E\uDDE0 压缩上下文…"),z=await x.openStatusCard("compact",X,"orange"),Y=async(H)=>{if(z)await x.closeStatusCard(z,H);else await W0(x.chatId,H)},G=QN0(Q,YN0),K=JN0(Q,Q.sessionId);x.manualContextCompactionPending=!0;try{x.setStatusCard(z,x.withModel("\uD83E\uDDE0 发起上下文压缩")),await Q.compactThread(),x.setStatusCard(z,x.withModel("⏳ 等待压缩完成事件"));let H=await G.promise,W=await K.waitFor(H,ZN0),L=H.threadId?` thread=${H.threadId.slice(0,8)}…`:"";await Y(x.withModel(`✅ 上下文已压缩${L}${XN0(W)}`))}catch(H){G.cancel(),await Y(`❌ 上下文压缩失败: ${$1(H)}`)}finally{K.cancel(),x.manualContextCompactionPending=!1}}var YN0=120000,ZN0=1500;var Gh=S0(()=>{h1()});import{randomUUID as WN0}from"node:crypto";function KN0(x){let Z=x.currentTokenSource(),Q=x.currentModelLabel();return aQ().map((J)=>({provider:J.agent,sourceId:J.id,display:J.display+(Z?.id===J.id&&Q?` · ${Q}`:""),enabled:J.enabled,modelCount:J.models.length,selected:Z?.id===J.id}))}function VN0(x,Z){let Q=x.currentModelLabel(),J=x.currentEffortLabel(),X=x.currentTokenSource()?.id===Z.id;return Z.models.map((z)=>{let Y=X&&Q===z.model;return{provider:Z.agent,sourceId:Z.id,model:z.model,displayName:z.display,description:Z.display,enabled:!0,isDefault:!1,selected:Y,efforts:z.efforts.map((G)=>({effort:G,description:"",isDefault:G===z.defaultEffort,selected:Y&&J===G}))}})}async function $h(x){let Z=WN0(),Q=KN0(x);if(x.modelPanels.set(Z,{models:[]}),!await M1(x.chatId,BL({sessionName:x.sessionName,panelId:Z,currentDisplay:x.currentTokenSource()?.display??x.currentModelLabel(),providers:Q})))x.modelPanels.delete(Z),await R1(x.chatId,"❌ 模型面板发送失败")}async function Fh(x,Z,Q=""){let J=Z.trim(),X=Q8(J);if(!X)return{ok:!1,message:`未知账号: ${J}`};if(!X.enabled)return{ok:!1,message:`${X.display} 未配置,请先点「启用」`};let z=Q.trim(),Y=VN0(x,X);return x.modelPanels.set(z,{models:Y}),{ok:!0,message:"",card:vL({sessionName:x.sessionName,panelId:z,currentModel:x.currentModelLabel(),currentEffort:x.currentEffortLabel(),models:Y})}}function HN0(x,Z){return Z?.provider==="claude"||Z?.provider==="codex"?Z.provider:u7(x)}function GN0(x,Z){if(x.currentTurn)return"当前 turn 不变,后续新 turn 使用。";if(x.proc?.isAlive()&&x.proc.provider===Z)return Z==="codex"?"Codex 需重启进程生效(发 restart)。":"下一轮开始使用。";return`下次启动 ${A4(Z)} 时使用。`}async function Uh(x,Z,Q="",J="",X=null){let z=Z.trim();if(!z)return await W0(x.chatId,`❌ ${"模型为空"}`),{ok:!1,message:"模型为空"};let Y=HN0(z,X),G=x.modelPanels.get(Q.trim())?.models.find((K)=>K.model===z&&(K.provider??"codex")===Y);if(!G)return{ok:!1,message:"模型不在当前选项中,请重新发送 model"};if(G.enabled===!1)return{ok:!1,message:`${G.displayName} 未配置,请先点「启用」`};if(G.efforts.length===0)return{ok:!1,message:"模型未返回 effort"};return{ok:!0,message:"",card:ML({sessionName:x.sessionName,panelId:Q.trim(),currentModel:x.currentModelLabel(),currentEffort:x.currentEffortLabel(),model:G})}}async function Lh(x,Z,Q,J="",X="",z=""){let Y=Z.trim(),G=Q.trim();if(!Y)return{ok:!1,message:"模型为空"};let K=J.trim(),H=x.modelPanels.get(K),W=z==="claude"||z==="codex"?z:H?.models.find((c)=>c.model===Y)?.provider??u7(Y);if(W==="claude"){if(!G6(G))return{ok:!1,message:"Claude reasoning effort 无效"}}else if(!a8(G))return{ok:!1,message:"Codex reasoning effort 无效"};let L=G,N=H?.models.find((c)=>c.model===Y&&(c.provider??"codex")===W);if(!N||!N.efforts.some((c)=>c.effort===L))return{ok:!1,message:`${A4(W)} · ${Y}/${L} 不在选项中`};let D=!!N.sourceId&&x.currentTokenSource()?.id!==N.sourceId;if(x.currentProvider()===W&&!D&&x.currentModelLabel()===Y&&x.currentEffortLabel()===L)return x.modelPanels.delete(K),{ok:!0,message:`当前已是 ${A4(W)} · ${Y} / ${L}`,card:cz({sessionName:x.sessionName,provider:W,model:Y,effort:L,scope:"当前已是此设置,无需变更。"})};if(x.proc?.isAlive()&&x.proc.provider!==W&&(x.currentTurn||x.openingTurn||x.pendingUserMessageCount>0||x.pendingMidTurnMsgs.length>0))return{ok:!1,message:`当前 ${x.backendLabel(x.proc.provider)} turn 正在执行或排队;请等结束或 stop 后再切换到 ${A4(W)}`};let B=x.currentModelLabel()!==Y||D,j=!!(x.currentTurn||x.openingTurn||x.pendingUserMessageCount>0||x.pendingMidTurnMsgs.length>0);if(W==="claude"&&x.proc?.isAlive()&&x.proc.provider==="claude"&&B&&j)return{ok:!1,message:"当前 Claude turn 正在执行或排队;Claude 模型 profile 通过 env 生效,请等结束或 stop 后再切换"};try{if(x.proc?.isAlive()&&x.proc.provider===W){let T=N.sourceId?Q8(N.sourceId)?.resolveSpawnModel(Y)??Y:Y;await Ly(x.proc.setModelSettings(T,L),20000,"thread/settings/update")}await x.applyModelSelection(W,Y,L,N.sourceId);let c=GN0(x,W);return x.modelPanels.delete(K),{ok:!0,message:`已选择 ${A4(W)} · ${Y} / ${L}`,card:cz({sessionName:x.sessionName,provider:W,model:Y,effort:L,scope:c})}}catch(c){let T=`模型切换失败: ${$1(c)}`;return k(`session "${x.sessionName}": set model settings failed: ${$1(c)}`),await W0(x.chatId,`❌ ${T}`),{ok:!1,message:T}}}var Nh=S0(()=>{$6();w7();r9();y8();h1();K1()});function g6(x,Z,Q=!1){let J=x.worktreeProjectName();return gL({projectName:J,tasklistName:EL(J),binding:$4(J),notice:Z,confirmDelete:Q})}async function Dh(x){if(!await M1(x.chatId,g6(x)))await R1(x.chatId,"❌ task 面板发送失败")}async function Oh(x){let Z=x.worktreeProjectName();try{let Q=$4(Z),J=await Af(Z,x.chatId),X=Q?"已启用":`已启用 ${J.name}`;return{ok:!0,message:X,card:g6(x,{type:"success",content:`✅ ${X}`})}}catch(Q){let J=`启用失败: ${$1(Q)}`;return k(`session "${x.sessionName}": tasklist enable failed: ${$1(Q)}`),{ok:!1,message:J,card:g6(x,{type:"error",content:`❌ ${J}`})}}}function Bh(x,Z){let Q=x.worktreeProjectName(),J=$4(Q),X=Z.trim();if(!J)return{ok:!1,message:"未启用",card:g6(x,{type:"error",content:"❌ 未启用"})};if(J.guid!==X)return{ok:!1,message:"清单绑定已变化",card:g6(x,{type:"error",content:"❌ 清单绑定已变化,请重新打开 task 面板"})};return{ok:!0,message:"请再次确认删除",card:g6(x,{type:"error",content:"⚠️ 删除会删除所有清单内任务"},!0)}}async function vh(x,Z){let Q=x.worktreeProjectName(),J=Z.trim();try{let z=`已删除 ${(await Ef(Q,J)).name}`;return{ok:!0,message:z,card:g6(x,{type:"success",content:`✅ ${z}`})}}catch(X){let z=`删除失败: ${$1(X)}`;return k(`session "${x.sessionName}": tasklist delete failed: ${$1(X)}`),{ok:!1,message:z,card:g6(x,{type:"error",content:`❌ ${z}`})}}}var Mh=S0(()=>{y8();h1();K1();uz()});var Rh,Sh,Ch,jh;var Th=S0(()=>{Rh=["- 以 `[file: /abs/path]` 开头的文本表示该路径上挂着一个文件,相关时去读它。","- 在回复任意位置,独占一行写 `[[send: /abs/path]]` 即可把该文件作为单独一条消息送出。只在用户主动要文件、或你要交付最终产物时才发。"],Sh=["- 判断是否该发文件:导出附件/数据集、用户明确要文件或下载物时,应先写到本机绝对路径,再用独占一行的 `[[send: /abs/path]]` 发给用户;短总结、普通说明、无需留存的正文答复不要为了发文件而发文件。","- 使用图片生成工具生成图片后,不要再补充说明文字或 `[[send: ...]]`;Lodestar 会根据工具返回的本机图片路径自动发给用户。若你用脚本或文件编辑生成图片,按上一条用 `[[send: /abs/path]]` 发出。","- 每次调用 Bash / shell 命令时,第一行都必须写 shell 注释 `# desc: <一句中文说明>`,再写真正命令。这个注释只给 Lodestar 卡片做摘要,不要依赖它改变命令行为。"],Ch=[...Rh,'- 当你有问题需要澄清时,输出单独一行 `[[askusr: {...}]]`。JSON 必须单行、合法、紧凑。格式是 `{ "questions": [{ "question": "完整问题1", "options": ["问题1的可选方案A","问题1的可选方案B"] }, { "question": "完整问题2", "options": [...] }] }`。每个问题都必须提供 `options`,且每题至少 2 个选项;',"- 一旦输出 `[[askusr: ...]]`,立刻停止继续推理和继续回答,等待用户先回答。需要澄清时,应一次性把当前继续执行所需的所有问题都放进这一个 askusr 标记里,不要拆成多轮连续追问。",...Sh].join(`
|
|
340
340
|
`),jh=[...Rh,"- 当你有问题需要澄清时,使用 Claude Code 自带的 AskUserQuestion 工具向用户提问;不要输出 Codex 专属的 host-marker 文本协议。",...Sh].join(`
|
|
341
341
|
`)});import{execFileSync as UN0}from"node:child_process";import{existsSync as _3,readdirSync as LN0,readFileSync as NN0,realpathSync as Ih,statSync as DN0}from"node:fs";import{dirname as ON0,join as qh,resolve as tz}from"node:path";function n3(x){let Z=x.trim();if(!BN0.test(Z))return null;return Z}function pY(x){return x.startsWith(vN0)}function Ah(x){return x.match(/^(.+)\[[^\[\]]+\]$/)?.[1]??x}function P5(x,Z){return`${x}[${Z}]`}function Eh(x){return`${g5}${x}`}function ez(x,Z,Q){return qh(ON0(x),P5(Z,Q))}function bh(x,Z,Q){let J=MN0(x,Z,Q);if(!J)return null;let X=NN0(J.path,"utf8").trim();if(!X)return null;return{path:J.path,content:X,slug:J.slug}}function kh(x,Z){DN(x);let Q=Gx(x,["for-each-ref","--format=%(refname:short)",`refs/heads/${g5}`]).split(`
|
|
342
342
|
`).map((z)=>z.trim()).filter(Boolean).filter((z)=>z.startsWith(g5)),J=ON(x),X=[];for(let z of Q){let Y=z.slice(g5.length);if(!n3(Y))continue;if(pY(Y))continue;let G=P5(Z,Y),K=ez(x,Z,Y),H=J.get(z)??null,W=H??K,L=!!H&&_3(H),N=uh(x,z,L),D={slug:Y,chatName:G,branch:z,state:N,expectedPath:K,worktreePath:L?H:null,mounted:L,dirtyCount:null,statusLine:null,error:null};if(H&&_3(H))try{let v=Gx(H,["status","--porcelain=v1","--branch"]).split(`
|