@leviyuan/lodestar 0.11.7 → 0.11.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
|
@@ -411,4 +411,4 @@ ${E3(x.stdoutTail,4000)}`:""].filter(Boolean).join(`
|
|
|
411
411
|
[truncated ${Z.length-HN} chars]`}function _U0(x){return x.replace(/\[([^\]\n]+)\]\(((?!https?:\/\/|applink:\/\/)[^)]+)\)/g,"$1")}function nU0(x){return`AI-AUTO: ${(x.replace(/\s+/g," ").trim()||"task").slice(0,80)}`}function TY(x){return lU0(JSON.stringify(x,null,2),"json")}function lU0(x,Z=""){return"```"+Z+`
|
|
412
412
|
`+x.replace(/```/g,"`\\`\\`")+"\n```"}function E3(x,Z){return x.length<=Z?x:x.slice(x.length-Z)}function R5(x){return x instanceof Error?x.message:String(x)}var sf=30000,$U0=15000,vz=20000,HN=15000,Sz=3600000,ef=10800000,UU0=5000,NN,LN,of=null,GN=null,FN=!1;var Yy=k0(()=>{dU();M7();x1();Rx();Kz();VN();VN();NN=process.env.LODESTAR_TASK_CODEX_MODEL??"gpt-5.5",LN=process.env.LODESTAR_TASK_CODEX_EFFORT??"xhigh"});var ZN0={};import{chmodSync as dU0,existsSync as MN,mkdirSync as iU0,readFileSync as pU0,unlinkSync as Cz,writeFileSync as sU0}from"node:fs";import{dirname as oU0}from"node:path";function Qy(){let x=new Set;for(let Z of jz.values())if(Z.isRunning())x.add(Z.sessionName);for(let Z of cY)x.add(Z);return[...x]}function Rz(){e$(Qy())}function Jy(x,Z){let Q=jz.get(x);if(!Q)Q=new S5(Z,x,{onLifecycleChange:Rz}),jz.set(x,Q);return Q}async function rU0(){let x=[...new Set(cP())];if(x.length===0)return;cY=new Set(x),P(`revive: ${x.length} session(s) marked alive on shutdown: ${x.join(", ")}`);try{await Promise.all(x.map(async(Z)=>{let Q=CX(Z);if(!Q){P(`revive: no chatId binding for "${Z}", skip`),cY.delete(Z),Rz();return}let J=Jy(Q,Z);try{if(await J.restart(!0))P(`revive: spawned "${Z}" (chat ${Q.slice(0,8)}…)`);else P(`revive: "${Z}" did not start`)}catch(X){P(`revive: restart "${Z}" failed: ${X}`)}finally{cY.delete(Z),Rz()}}))}finally{cY.clear(),Rz()}}function tU0(x){let Z=[],Q=[],J=[],X=typeof x?.title==="string"?x.title.trim():"";if(X)J.push(`# ${X}`);let z=Array.isArray(x?.content)?x.content:[];for(let Y of z){if(!Array.isArray(Y))continue;let G=[];for(let H of Y){if(!H||typeof H!=="object")continue;switch(H.tag){case"text":{let K=String(H.text??""),N=Array.isArray(H.style)?H.style:[];if(N.includes("bold"))K=`**${K}**`;if(N.includes("italic"))K=`*${K}*`;if(N.includes("lineThrough")||N.includes("strikethrough"))K=`~~${K}~~`;G.push(K);break}case"a":{let K=String(H.href??""),N=String(H.text??K);G.push(`[${N}](${K})`);break}case"at":{let K=String(H.user_name??H.user_id??"");G.push(`@${K}`);break}case"code_inline":G.push(`\`${String(H.text??"")}\``);break;case"hr":G.push("---");break;case"img":if(H.image_key)Z.push(String(H.image_key));break;case"media":if(H.file_key)Q.push({key:String(H.file_key),name:H.file_name});break;case"emotion":break;default:if(typeof H.text==="string")G.push(H.text)}}let W=G.join("");if(W.trim())J.push(W)}return{markdown:J.join(`
|
|
413
413
|
|
|
414
|
-
`),imageKeys:Z,fileKeys:Q}}async function Xy(x){let Z=x?.message;if(!Z)return;let J=(x?.sender?.sender_id??x?.event?.sender?.sender_id??Z?.sender?.sender_id)?.open_id??"",X=Z.message_id;if(X&&T5.has(X))return;if(X){if(T5.add(X),T5.size>200){let B=[...T5];T5.clear();for(let j of B.slice(-100))T5.add(j)}}let z=Number(Z.create_time??0);if(z>0&&Date.now()-z>eU0){if(P(`drop stale message ${X} age=${Math.round((Date.now()-z)/1000)}s`),X)q8(X,"CrossMark");return}let Y=Z.chat_id,G={};try{G=JSON.parse(Z.content??"{}")}catch{}let W=Z.message_type==="text"?G.text??"":"";if(typeof W==="string"&&W.startsWith("[DEBUG]")){try{sU0(gY,JSON.stringify({chat_id:Y,sender_open_id:J,seeded_at:new Date().toISOString(),seeded_msg_id:X??""},null,2)),P(`debug: seeded inject context chat=${Y.slice(0,8)}… sender=${J.slice(0,8)}…`)}catch(j){P(`debug: seed context failed: ${j}`)}let B=W.slice(7);G.text=B,Z.content=JSON.stringify(G)}let H=q1.get(Y);if(!H)await jX(),H=q1.get(Y);if(!H)H=await EP(Y)??void 0;if(!H){P(`unknown chat ${Y}, dropping message`),await c0(Y,"❌ 无法识别群名。请确认:① 机器人已被拉进本群;② 群已设置名称(未命名群拿不到群名,无法映射项目目录)。设置后再发一条消息即可。");return}let K=IX(H);RX(K,Y);let N=Jy(Y,K),L={};try{L=JSON.parse(Z.content??"{}")}catch{}let D=Z.message_type,O="",v=[];if(D==="text")O=(L.text??"").trim();else if(D==="post"){let B=tU0(L);O=B.markdown.trim();for(let j of B.imageKeys){let c=await a2(Z.message_id,j,"image");if(c)v.push(c)}for(let j of B.fileKeys){let c=await a2(Z.message_id,j.key,"file",j.name);if(c)v.push(c)}}if(D==="text"&&O){if(await N.runCommand(O,J))return}if(D==="text"&&O&&N.hasPendingAsk()){await N.onAskMessageAnswer(O,J,X??"");return}if(D==="text"&&O&&N.hasPendingHostAsk()){await N.onHostAskMessageAnswer(O,J,X??"");return}if(D==="image"&&L.image_key){let B=await a2(Z.message_id,L.image_key,"image");if(B)v.push(B)}else if(D==="file"&&L.file_key){let B=await a2(Z.message_id,L.file_key,"file",L.file_name);if(B)v.push(B);if(!O)O=`(file: ${L.file_name})`}if(!O&&v.length===0){P(`drop empty message ${X} type=${D}`);return}await N.onUserMessage(O||"(empty)",v,J,X??"")}async function aU0(x){let Z=x?.action,Q=Z?.value;if(!Q?.kind)return;let J=x?.context?.open_chat_id??"",X=x?.operator?.open_id??"",z=jz.get(J);if(!z)return{toast:{type:"error",content:"会话不存在,请先发消息启动"}};switch(Q.kind){case"permission":return await z.onPermissionDecision(Q.request_id,Q.decision,X),{toast:{type:Q.decision==="deny"?"error":"success",content:"已处理"}};case"menu":return await z.onUserMessage(`(menu choice ${Q.choice+1})`),{toast:{type:"success",content:"OK"}};case"model_select":{let Y=await z.onModelSelect(String(Q.model??""),String(Q.panel_id??""),X,Q);return Y.card?g4(Y.card):{toast:{type:Y.ok?"success":"error",content:Y.message}}}case"model_effort_select":{let Y=await z.onModelEffortSelect(String(Q.model??""),String(Q.effort??""),String(Q.panel_id??""),X,String(Q.provider??""));return Y.card?g4(Y.card):{toast:{type:Y.ok?"success":"error",content:Y.message}}}case"ask":{if(Q.custom){let G=(Z?.form_value??Z?.input??{})?.custom_answer??Z?.input_value??"";return await z.onAskCustomAnswer(Q.tool_use_id,Q.question_idx??0,G,X),{toast:{type:G.trim()?"success":"error",content:G.trim()?"已回答":"请输入答案"}}}return await z.onAskAnswer(Q.tool_use_id,Q.question_idx??0,Q.option_idx,X),{toast:{type:"success",content:"已回答"}}}case"host_ask":{if(Q.custom){let W=(Z?.form_value??Z?.input??{})?.custom_answer??Z?.input_value??"",H=await z.onHostAskCustomAnswer(Q.tool_use_id,Q.question_idx??0,W,X);return H.card?g4(H.card):{toast:{type:H.ok?"success":"error",content:H.message}}}let Y=await z.onHostAskAnswer(Q.tool_use_id,Q.question_idx??0,Q.option_idx,X);return Y.card?g4(Y.card):{toast:{type:Y.ok?"success":"error",content:Y.message}}}case"worktree_disband":{let Y=await z.onWorktreeDisband(String(Q.slug??""));return g4(Y.card)}case"tasklist_enable":{let Y=await z.onTasklistEnable();return g4(Y.card)}case"tasklist_delete_prompt":{let Y=z.onTasklistDeletePrompt(String(Q.guid??""));return g4(Y.card)}case"tasklist_delete_confirm":{let Y=await z.onTasklistDeleteConfirm(String(Q.guid??""));return g4(Y.card)}case"agy_forward_codex":{let Y=z.beginAgyForwardToCodex(String(Q.result_id??""),X);return{toast:{type:Y.ok?"success":"error",content:Y.message}}}}return{toast:{type:"info",content:"unknown action"}}}function vN(x){return x.map((Z)=>typeof Z==="string"?Z:JSON.stringify(Z)).join(" ")}function xN0(){if(process.platform==="win32"){P("debug: inject socket skipped on Windows (dev-only feature)");return}if(typeof Bun>"u"){P("debug: inject socket skipped (Bun runtime only — npm/Node build does not include it)");return}try{if(MN(I6))Cz(I6)}catch{}try{Bun.serve({unix:I6,fetch:async(x)=>{if(x.method!=="POST")return new Response("use POST",{status:405});let Z={};try{Z=await x.json()}catch{return new Response("bad json",{status:400})}if(!MN(gY))return new Response("no debug context yet — send `[DEBUG]hi` from Feishu first",{status:412});let Q={};try{Q=JSON.parse(pU0(gY,"utf8"))}catch(G){return new Response(`ctx read failed: ${G}`,{status:500})}let J=String(Z.text??"");if(!J)return new Response("text required",{status:400});let X=`【自动化测试】:${J}`,z=await c0(Q.chat_id,X);if(!z)return new Response("sendText failed — see daemon log",{status:502});let Y={sender:{sender_id:{open_id:Q.sender_open_id}},message:{message_id:z,chat_id:Q.chat_id,message_type:"text",content:JSON.stringify({text:J}),create_time:String(Date.now())}};return P(`debug: inject text=${JSON.stringify(J).slice(0,80)} msg_id=${z}`),Xy(Y).catch((G)=>P(`debug: handleMessage rejected: ${G}`)),new Response(JSON.stringify({ok:!0,msg_id:z}),{headers:{"content-type":"application/json"}})}});try{dU0(I6,384)}catch{}P(`debug: inject socket listening at ${I6}`)}catch(x){P(`debug: socket bind failed: ${x}`)}}async function YN0(){P(`lodestar-daemon: pid ${process.pid} starting`),MP(),RP(),jP(),await jX(),setInterval(()=>{jX()},300000),af();let x,Z=Date.now(),Q=0,J=!1,X=null,z=1500,Y=90000,G=600000,W=3,H=null,K=0,N="",L=!1,D=(q)=>{P(`[ws] rebuild requested before WS init — ${q}`)},O=(q,u=0,h=!1)=>{let n=Date.now()+u;if(H){if(L||=h,n>=K){P(`[ws] rebuild already scheduled — ${q}`);return}clearTimeout(H)}K=n,N=q,L=h,H=setTimeout(()=>{let i=N,l=L;H=null,K=0,N="",L=!1,D(i,l)},u)},v={error:(q)=>P(`[ws-sdk error] ${vN(q)}`),warn:(q)=>{let u=vN(q);if(P(`[ws-sdk warn] ${u}`),u.includes("no pong/inbound"))O("ping-timeout: SDK liveness watchdog fired",2000,Date.now()-Z<G)},info:(q)=>P(`[ws-sdk] ${vN(q)}`),debug:(q)=>{},trace:(q)=>{}},B=new c5.EventDispatcher({}),j=()=>{if(Z=Date.now(),Q=0,X)clearTimeout(X),X=null};B.register({"im.message.receive_v1":async(q)=>{j(),Xy(q).catch((u)=>P(`handleMessage: ${u}`))}}),B.register({"card.action.trigger":async(q)=>{j();try{return await aU0(q)}catch(u){P(`handleCardAction: ${u}`)}}});let c=()=>new c5.WSClient({appId:ex.feishu.app_id,appSecret:ex.feishu.app_secret,loggerLevel:c5.LoggerLevel.info,logger:v,wsConfig:{pingTimeout:60},handshakeTimeoutMs:1e4,onReconnecting:()=>P("[ws] reconnecting — WS lost, SDK is retrying"),onReconnected:()=>k(),onError:(q)=>D(`SDK onError: ${q?.message??q}`)});D=(q,u=!1)=>{if(J){P(`[ws] rebuild skipped (already rebuilding) — ${q}`);return}if(H)clearTimeout(H),H=null,K=0;J=!0,Q++,P(`[ws] rebuild #${Q} (fresh WSClient) — ${q}`);let h=x;try{h?.close({force:!0})}catch(n){P(`[ws] rebuild: old close failed: ${n}`)}if(x=c(),x.start({eventDispatcher:B}),J=!1,u)I()};let I=()=>{if(X)clearTimeout(X);let q=Date.now();X=setTimeout(()=>{if(X=null,Z>=q){Q=0;return}if(Q>=W){P(`[ws] STILL deaf after ${W} fresh rebuilds — auto-heal exhausted, `+"leaving last client up (no process exit). Escape hatch: systemctl --user restart feishu-daemon"),Q=0;return}D(`verify: no event ${Math.round((Date.now()-q)/1000)}s after rebuild`,!0)},Y)},k=()=>{let q=Date.now()-Z<G;P("[ws] reconnected — swapping in a fresh WSClient (cluster-routing precaution; "+`recentlyActive=${q})`),Q=0,O("post-reconnect precaution",z,q)};x=c(),x.start({eventDispatcher:B}),P(`lodestar-daemon: WS started, watching ${q1.size} groups`);let w=15000,g=2,T=0;setInterval(()=>{let{state:q}=x.getConnectionStatus();if(q==="connected"){T=0;return}if(q==="failed"||q==="idle"){T=0,D(`watchdog: state=${q}`);return}if(T++,P(`[ws] watchdog: state=${q} (${T}/${g})`),T>=g)T=0,D(`watchdog: stuck in '${q}' ~${Math.round(w*g/1000)}s`)},w),xN0(),yf({bind:ex.notify.bind,port:ex.notify.port}),_f(),await rU0()}var c5,Zy=!1,Iz=()=>{if(Zy)return;Zy=!0;try{let x=Qy();if(e$(x),x.length>0)P(`alive marker: [${x.join(", ")}]`)}catch(x){P(`alive marker write failed: ${x}`)}try{Cz(d1)}catch{}try{Cz(I6)}catch{}},jz,cY,eU0=5000,T5;var zy=k0(()=>{ff();x1();hf();nf();Yy();t6();Rx();P4();uz();c5=IN(CV(),1);{let x=PY(d1);if(x.state==="exit")console.error(`lodestar-daemon: already running (pid ${x.pid})`),process.exit(1);if(MN(d1))try{Cz(d1)}catch{}}iU0(oU0(d1),{recursive:!0});AN(d1);process.on("exit",Iz);process.on("SIGTERM",()=>{P("SIGTERM"),Iz(),process.exit(0)});process.on("SIGINT",()=>{P("SIGINT"),Iz(),process.exit(0)});if(process.platform==="win32")process.on("SIGBREAK",()=>{P("SIGBREAK"),Iz(),process.exit(0)});process.on("unhandledRejection",(x)=>P(`unhandledRejection: ${x}`));process.on("uncaughtException",(x)=>P(`uncaughtException: ${x}`));jz=new Map,cY=new Set;T5=new Set;YN0().catch((x)=>{P(`boot fatal: ${x}`),process.exit(1)})});P4();uz();import{existsSync as Ky,unlinkSync as QN0}from"node:fs";var k8={reset:"\x1B[0m",bold:"\x1B[1m",yellow:"\x1B[33m",green:"\x1B[32m",red:"\x1B[31m"};async function JN0(){if(!Ky(l1)){if(process.stdin.isTTY){console.log(),console.log(`${k8.bold}${k8.yellow}Lodestar: 未找到配置, 启动安装向导...${k8.reset}`);let{runSetup:Z}=await Promise.resolve().then(() => (wN(),uN));await Z();return}console.error(`${k8.red}Lodestar: 未找到配置 ${l1}${k8.reset}`),console.error(`请在交互式 cmd / PowerShell 里运行 ${k8.green}lodestar-setup${k8.reset} 完成首次配置 (或直接运行 ${k8.green}lodestar-daemon${k8.reset} 也会触发同一个向导)。`),process.exit(1)}let x=PY(d1);if(x.state==="exit")console.error(`${k8.yellow}Lodestar: 已经有一个 daemon 在运行 (pid ${x.pid})${k8.reset}`),console.error(" • 想看日志:"+(process.platform==="win32"?" %LOCALAPPDATA%\\Lodestar\\daemon-YYYY-MM-DD.log":" ~/.local/share/lodestar/daemon-YYYY-MM-DD.log")),console.error(" • 想停: 任务管理器结束 pid 上面那个号 (Windows) / `kill "+x.pid+"` (Linux/macOS)"),console.error(` • 误判 / 已经死了: 手删 ${d1} 后再跑`),process.exit(1);if(Ky(d1))try{QN0(d1)}catch{}await Promise.resolve().then(() => (zy(),ZN0))}JN0().catch((x)=>{console.error("lodestar fatal:",x?.message??x),process.exit(1)});
|
|
414
|
+
`),imageKeys:Z,fileKeys:Q}}async function Xy(x){let Z=x?.message;if(!Z)return;let J=(x?.sender?.sender_id??x?.event?.sender?.sender_id??Z?.sender?.sender_id)?.open_id??"",X=Z.message_id;if(X&&T5.has(X))return;if(X){if(T5.add(X),T5.size>200){let B=[...T5];T5.clear();for(let j of B.slice(-100))T5.add(j)}}let z=Number(Z.create_time??0);if(z>0&&Date.now()-z>eU0){if(P(`drop stale message ${X} age=${Math.round((Date.now()-z)/1000)}s`),X)q8(X,"CrossMark");return}let Y=Z.chat_id,G={};try{G=JSON.parse(Z.content??"{}")}catch{}let W=Z.message_type==="text"?G.text??"":"";if(typeof W==="string"&&W.startsWith("[DEBUG]")){try{sU0(gY,JSON.stringify({chat_id:Y,sender_open_id:J,seeded_at:new Date().toISOString(),seeded_msg_id:X??""},null,2)),P(`debug: seeded inject context chat=${Y.slice(0,8)}… sender=${J.slice(0,8)}…`)}catch(j){P(`debug: seed context failed: ${j}`)}let B=W.slice(7);G.text=B,Z.content=JSON.stringify(G)}let H=q1.get(Y);if(!H)await jX(),H=q1.get(Y);if(!H)H=await EP(Y)??void 0;if(!H){P(`unknown chat ${Y}, dropping message`),await c0(Y,"❌ 无法识别群名。请确认:① 机器人已被拉进本群;② 群已设置名称(未命名群拿不到群名,无法映射项目目录)。设置后再发一条消息即可。");return}let K=IX(H);RX(K,Y);let N=Jy(Y,K),L={};try{L=JSON.parse(Z.content??"{}")}catch{}let D=Z.message_type,O="",v=[];if(D==="text")O=(L.text??"").trim();else if(D==="post"){let B=tU0(L);O=B.markdown.trim();for(let j of B.imageKeys){let c=await a2(Z.message_id,j,"image");if(c)v.push(c)}for(let j of B.fileKeys){let c=await a2(Z.message_id,j.key,"file",j.name);if(c)v.push(c)}}if(D==="text"&&O){if(await N.runCommand(O,J))return}if(D==="text"&&O&&N.hasPendingAsk()){await N.onAskMessageAnswer(O,J,X??"");return}if(D==="text"&&O&&N.hasPendingHostAsk()){await N.onHostAskMessageAnswer(O,J,X??"");return}if(D==="image"&&L.image_key){let B=await a2(Z.message_id,L.image_key,"image");if(B)v.push(B)}else if(D==="file"&&L.file_key){let B=await a2(Z.message_id,L.file_key,"file",L.file_name);if(B)v.push(B);if(!O)O=`(file: ${L.file_name})`}if(!O&&v.length===0){P(`drop empty message ${X} type=${D}`);return}await N.onUserMessage(O||"(empty)",v,J,X??"")}async function aU0(x){let Z=x?.action,Q=Z?.value;if(!Q?.kind)return;let J=x?.context?.open_chat_id??"",X=x?.operator?.open_id??"",z=jz.get(J);if(!z)return{toast:{type:"error",content:"会话不存在,请先发消息启动"}};switch(Q.kind){case"permission":return await z.onPermissionDecision(Q.request_id,Q.decision,X),{toast:{type:Q.decision==="deny"?"error":"success",content:"已处理"}};case"menu":return await z.onUserMessage(`(menu choice ${Q.choice+1})`),{toast:{type:"success",content:"OK"}};case"model_select":{let Y=await z.onModelSelect(String(Q.model??""),String(Q.panel_id??""),X,Q);return Y.card?g4(Y.card):{toast:{type:Y.ok?"success":"error",content:Y.message}}}case"model_effort_select":{let Y=await z.onModelEffortSelect(String(Q.model??""),String(Q.effort??""),String(Q.panel_id??""),X,String(Q.provider??""));return Y.card?g4(Y.card):{toast:{type:Y.ok?"success":"error",content:Y.message}}}case"ask":{if(Q.custom){let G=(Z?.form_value??Z?.input??{})?.custom_answer??Z?.input_value??"";return await z.onAskCustomAnswer(Q.tool_use_id,Q.question_idx??0,G,X),{toast:{type:G.trim()?"success":"error",content:G.trim()?"已回答":"请输入答案"}}}return await z.onAskAnswer(Q.tool_use_id,Q.question_idx??0,Q.option_idx,X),{toast:{type:"success",content:"已回答"}}}case"host_ask":{if(Q.custom){let W=(Z?.form_value??Z?.input??{})?.custom_answer??Z?.input_value??"",H=await z.onHostAskCustomAnswer(Q.tool_use_id,Q.question_idx??0,W,X);return H.card?g4(H.card):{toast:{type:H.ok?"success":"error",content:H.message}}}let Y=await z.onHostAskAnswer(Q.tool_use_id,Q.question_idx??0,Q.option_idx,X);return Y.card?g4(Y.card):{toast:{type:Y.ok?"success":"error",content:Y.message}}}case"worktree_disband":{let Y=await z.onWorktreeDisband(String(Q.slug??""));return g4(Y.card)}case"tasklist_enable":{let Y=await z.onTasklistEnable();return g4(Y.card)}case"tasklist_delete_prompt":{let Y=z.onTasklistDeletePrompt(String(Q.guid??""));return g4(Y.card)}case"tasklist_delete_confirm":{let Y=await z.onTasklistDeleteConfirm(String(Q.guid??""));return g4(Y.card)}case"agy_forward_codex":{let Y=z.beginAgyForwardToCodex(String(Q.result_id??""),X);return{toast:{type:Y.ok?"success":"error",content:Y.message}}}}return{toast:{type:"info",content:"unknown action"}}}function vN(x){return x.map((Z)=>typeof Z==="string"?Z:JSON.stringify(Z)).join(" ")}function xN0(){if(process.platform==="win32"){P("debug: inject socket skipped on Windows (dev-only feature)");return}if(typeof Bun>"u"){P("debug: inject socket skipped (Bun runtime only — npm/Node build does not include it)");return}try{if(MN(I6))Cz(I6)}catch{}try{Bun.serve({unix:I6,fetch:async(x)=>{if(x.method!=="POST")return new Response("use POST",{status:405});let Z={};try{Z=await x.json()}catch{return new Response("bad json",{status:400})}if(!MN(gY))return new Response("no debug context yet — send `[DEBUG]hi` from Feishu first",{status:412});let Q={};try{Q=JSON.parse(pU0(gY,"utf8"))}catch(G){return new Response(`ctx read failed: ${G}`,{status:500})}let J=String(Z.text??"");if(!J)return new Response("text required",{status:400});let X=`【自动化测试】:${J}`,z=await c0(Q.chat_id,X);if(!z)return new Response("sendText failed — see daemon log",{status:502});let Y={sender:{sender_id:{open_id:Q.sender_open_id}},message:{message_id:z,chat_id:Q.chat_id,message_type:"text",content:JSON.stringify({text:J}),create_time:String(Date.now())}};return P(`debug: inject text=${JSON.stringify(J).slice(0,80)} msg_id=${z}`),Xy(Y).catch((G)=>P(`debug: handleMessage rejected: ${G}`)),new Response(JSON.stringify({ok:!0,msg_id:z}),{headers:{"content-type":"application/json"}})}});try{dU0(I6,384)}catch{}P(`debug: inject socket listening at ${I6}`)}catch(x){P(`debug: socket bind failed: ${x}`)}}async function YN0(){P(`lodestar-daemon: pid ${process.pid} starting`),MP(),RP(),jP(),await jX(),setInterval(()=>{jX()},300000),af();let x,Z=Date.now(),Q=0,J=!1,X=null,z=1500,Y=90000,G=600000,W=3,H=null,K=0,N="",L=!1,D=(q)=>{P(`[ws] rebuild requested before WS init — ${q}`)},O=(q,u=0,h=!1)=>{let n=Date.now()+u;if(H){if(L||=h,n>=K){P(`[ws] rebuild already scheduled — ${q}`);return}clearTimeout(H)}K=n,N=q,L=h,H=setTimeout(()=>{let i=N,l=L;H=null,K=0,N="",L=!1,D(i,l)},u)},v={error:(q)=>P(`[ws-sdk error] ${vN(q)}`),warn:(q)=>{let u=vN(q);if(P(`[ws-sdk warn] ${u}`),u.includes("no pong/inbound"))O("ping-timeout: SDK liveness watchdog fired",2000,Date.now()-Z<G)},info:(q)=>P(`[ws-sdk] ${vN(q)}`),debug:(q)=>{},trace:(q)=>{}},B=new c5.EventDispatcher({}),j=()=>{if(Z=Date.now(),Q=0,X)clearTimeout(X),X=null};B.register({"im.message.receive_v1":async(q)=>{j(),Xy(q).catch((u)=>P(`handleMessage: ${u}`))}}),B.register({"card.action.trigger":async(q)=>{j();try{return await aU0(q)}catch(u){P(`handleCardAction: ${u}`)}}});let c=()=>new c5.WSClient({appId:ex.feishu.app_id,appSecret:ex.feishu.app_secret,loggerLevel:c5.LoggerLevel.info,logger:v,wsConfig:{pingTimeout:60},handshakeTimeoutMs:1e4,onReconnecting:()=>P("[ws] reconnecting — WS lost, SDK is retrying"),onReconnected:()=>k(),onError:(q)=>D(`SDK onError: ${q?.message??q}`)});D=(q,u=!1)=>{if(J){P(`[ws] rebuild skipped (already rebuilding) — ${q}`);return}if(H)clearTimeout(H),H=null,K=0;J=!0,Q++,P(`[ws] rebuild #${Q} (fresh WSClient) — ${q}`);let h=x;try{h?.close({force:!0})}catch(n){P(`[ws] rebuild: old close failed: ${n}`)}if(x=c(),x.start({eventDispatcher:B}),J=!1,u)I()};let I=()=>{if(X)clearTimeout(X);let q=Date.now();X=setTimeout(()=>{if(X=null,Z>=q){Q=0;return}if(Q>=W){P(`[ws] STILL deaf after ${W} fresh rebuilds — auto-heal exhausted, `+"leaving last client up (no process exit). Escape hatch: systemctl --user restart feishu-daemon"),Q=0;return}D(`verify: no event ${Math.round((Date.now()-q)/1000)}s after rebuild`,!0)},Y)},k=()=>{let q=Date.now()-Z<G;P("[ws] reconnected — swapping in a fresh WSClient (cluster-routing precaution; "+`recentlyActive=${q})`),Q=0,O("post-reconnect precaution",z,q)};x=c(),x.start({eventDispatcher:B}),P(`lodestar-daemon: WS started, watching ${q1.size} groups`);let w=15000,g=2,T=0;setInterval(()=>{let{state:q}=x.getConnectionStatus();if(q==="connected"){T=0;return}if(q==="failed"||q==="idle"){T=0,D(`watchdog: state=${q}`);return}if(T++,P(`[ws] watchdog: state=${q} (${T}/${g})`),T>=g)T=0,D(`watchdog: stuck in '${q}' ~${Math.round(w*g/1000)}s`)},w),xN0(),yf({bind:ex.notify.bind,port:ex.notify.port}),_f(),await rU0()}var c5,Zy=!1,Iz=()=>{if(Zy)return;Zy=!0;try{let x=Qy();if(e$(x),x.length>0)P(`alive marker: [${x.join(", ")}]`)}catch(x){P(`alive marker write failed: ${x}`)}try{Cz(d1)}catch{}try{Cz(I6)}catch{}},jz,cY,eU0=30000,T5;var zy=k0(()=>{ff();x1();hf();nf();Yy();t6();Rx();P4();uz();c5=IN(CV(),1);{let x=PY(d1);if(x.state==="exit")console.error(`lodestar-daemon: already running (pid ${x.pid})`),process.exit(1);if(MN(d1))try{Cz(d1)}catch{}}iU0(oU0(d1),{recursive:!0});AN(d1);process.on("exit",Iz);process.on("SIGTERM",()=>{P("SIGTERM"),Iz(),process.exit(0)});process.on("SIGINT",()=>{P("SIGINT"),Iz(),process.exit(0)});if(process.platform==="win32")process.on("SIGBREAK",()=>{P("SIGBREAK"),Iz(),process.exit(0)});process.on("unhandledRejection",(x)=>P(`unhandledRejection: ${x}`));process.on("uncaughtException",(x)=>P(`uncaughtException: ${x}`));jz=new Map,cY=new Set;T5=new Set;YN0().catch((x)=>{P(`boot fatal: ${x}`),process.exit(1)})});P4();uz();import{existsSync as Ky,unlinkSync as QN0}from"node:fs";var k8={reset:"\x1B[0m",bold:"\x1B[1m",yellow:"\x1B[33m",green:"\x1B[32m",red:"\x1B[31m"};async function JN0(){if(!Ky(l1)){if(process.stdin.isTTY){console.log(),console.log(`${k8.bold}${k8.yellow}Lodestar: 未找到配置, 启动安装向导...${k8.reset}`);let{runSetup:Z}=await Promise.resolve().then(() => (wN(),uN));await Z();return}console.error(`${k8.red}Lodestar: 未找到配置 ${l1}${k8.reset}`),console.error(`请在交互式 cmd / PowerShell 里运行 ${k8.green}lodestar-setup${k8.reset} 完成首次配置 (或直接运行 ${k8.green}lodestar-daemon${k8.reset} 也会触发同一个向导)。`),process.exit(1)}let x=PY(d1);if(x.state==="exit")console.error(`${k8.yellow}Lodestar: 已经有一个 daemon 在运行 (pid ${x.pid})${k8.reset}`),console.error(" • 想看日志:"+(process.platform==="win32"?" %LOCALAPPDATA%\\Lodestar\\daemon-YYYY-MM-DD.log":" ~/.local/share/lodestar/daemon-YYYY-MM-DD.log")),console.error(" • 想停: 任务管理器结束 pid 上面那个号 (Windows) / `kill "+x.pid+"` (Linux/macOS)"),console.error(` • 误判 / 已经死了: 手删 ${d1} 后再跑`),process.exit(1);if(Ky(d1))try{QN0(d1)}catch{}await Promise.resolve().then(() => (zy(),ZN0))}JN0().catch((x)=>{console.error("lodestar fatal:",x?.message??x),process.exit(1)});
|