@linkaicli/linkai-cli 1.0.21 → 1.0.23

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.
@@ -37,7 +37,7 @@ complete -c linkai -f -a '(
37
37
  `,{mode:384})}catch{}}function QN(t,e,r){let n=Ar();return n.n+=1,{schema:xN,eventId:Ld(),type:t,ts:new Date().toISOString(),runId:n.runId,sessionId:n.sessionId,runSeq:n.runSeq,n:n.n,installId:n.installId,cli:{version:st,node:process.versions.node,os:`${process.platform}-${process.arch}`},caller:n.caller,identity:n.identity,flow:r??n.flow,data:e}}function Hr(t,e={},r){if(hl())return;let n=Ar();if(t==="command.start"&&typeof e.command=="string"&&(n.command=e.command,n.access=typeof e.access=="string"?e.access:null),t==="build.stage"&&n.flow&&typeof e.stage=="string"&&e.stage!=="validate")try{cN(n.flow.id,e.stage,e.ok!==!1)}catch{}let i=QN(t,e,r);P6(JSON.stringify(i)),bN()}function V6(t){let e=Ar(),r=JSON.stringify(e.identity)!==JSON.stringify(t);e.identity=t,r&&t&&Hr("identity",{...t})}function q6(t){Ar().transport=t,bN()}function bN(){let t=Ar();!t.transport||t.flushTimer||Od()||Ud()||(t.flushTimer=setTimeout(()=>{t.flushTimer=null,pC()},b6),t.flushTimer.unref?.())}function pC({force:t=!1}={}){let e=Ar();if(!e.transport||!t&&Ud())return Promise.resolve();if(e.flushing)return e.flushing;let r=new AbortController;return e.abort=r,e.flushing=(async()=>{try{let n=is(),i=Date.now(),s=[];for(let c of Ze.existsSync(n)?Ze.readdirSync(n):[]){if(!c.startsWith("sending-"))continue;let l=Xt.join(n,c);try{i-Ze.statSync(l).mtimeMs>D6&&s.push(l)}catch{}}let a=Xt.join(n,`sending-${process.pid}-${i}.jsonl`);try{Ze.renameSync(BA(),a),s.push(a)}catch{}for(let c of s){let l;try{l=Ze.readFileSync(c,"utf8").split(`
38
38
  `).filter(Boolean)}catch{continue}let u=l.map(h=>{try{return JSON.parse(h)}catch{return null}}).filter(h=>h!==null).map(h=>h.identity||h.runId!==e.runId||!e.identity?h:{...h,identity:e.identity}),A=-1;for(let h=0;h<u.length;h+=IN){let g=!1;try{g=!r.signal.aborted&&await e.transport(u.slice(h,h+IN),r.signal)}catch{g=!1}if(!g){A=h;break}}if(r.signal.aborted||N6(A<0),A>=0){let h=u.slice(A).map(g=>JSON.stringify(g)).join(`
39
39
  `);try{Ze.appendFileSync(BA(),h+`
40
- `,{mode:384})}catch{}}try{Ze.rmSync(c,{force:!0})}catch{}if(A>=0)break}}finally{e.flushing=null,e.abort=null}})(),e.flushing}function Y6(t){let e=Ar();e.n>0||(e.argv=t,hl()||W6(),Hr("run.start",{argv:gC(t),cwdHash:To(process.cwd()).slice(0,16),pid:process.pid,worker:process.env.LINKAI_COLLAB_WORKER||null,processUptimeMs:Math.round(process.uptime()*1e3),parentRunId:process.env.LINKAI_PARENT_RUN_ID||null}),process.env.LINKAI_PARENT_RUN_ID=e.runId,process.once("exit",r=>{if(!e.ended){e.ended=!0;let n=kN(e,r);Hr("run.end",{exitCode:r,durationMs:Date.now()-e.startedAt,...n,abrupt:!0}),DN(e,n)}}))}function W6(){let t=Ar();try{let e=G6(),r=process.env.LINKAI_COLLAB_WORKER||null;t.flowKey=nN({installId:t.installId,agent:t.caller.agent,cwdHash:To(process.cwd()).slice(0,16),worker:r,toolSessionId:t.caller.toolSessionId??null});let n=vd(process.env.LINKAI_AI_MODEL),i=aN({key:t.flowKey,agent:t.caller.agent,worker:r,idleMs:e,explicitId:process.env.LINKAI_FLOW||null,board:rN(r),runId:t.runId,pid:process.pid,model:n});for(let s of i.stalled)Hr("flow.stalled",{status:s.status,reason:s.reason,source:s.source,goal:s.goal,type:s.type,plan:s.plan,lastActivity:new Date(s.lastActivity).toISOString(),lastCommand:s.lastCommand,lastErrorCode:s.lastErrorCode,build:s.build,openRuns:Object.keys(s.runsOpen).length,idleMinutes:e/6e4},{id:s.id,attach:"detected"});t.flow={id:i.id,attach:i.attach},t.caller.model=i.model?{name:i.model,source:"self-reported"}:null}catch{}}function DN(t,e){if(t.flow)try{nC(t.flow.id,t.runId,{command:t.command??gC(t.argv).command,ok:typeof e.ok=="boolean"?e.ok:null,errorCode:typeof e.errorCode=="string"?e.errorCode:null,outcomeUnknown:e.outcomeUnknown===!0})}catch{}}function mC(){let t=Ar();if(!t.flow||!t.command||J6.test(t.command)||t.access!=="write"&&t.command!=="linkai/status")return null;try{if(iC(t.flow.id)?.source!=="auto")return null}catch{return null}return j6(t.flow.id)?'\u5F53\u524D\u4EFB\u52A1\u5C1A\u672A\u5F00\u59CB\u5DE5\u4F5C\u6D41\u3002\u6BCF\u4E2A\u4E1A\u52A1\u4EFB\u52A1\u8BF7\u5148\u6267\u884C linkai flow-start --goal "<\u7528\u6237\u76EE\u6807>" --type create|modify|import|query --model "<\u4F60\u7684\u6A21\u578B\u4E0E\u7248\u672C>"\uFF0C\u4E4B\u540E\u7684\u547D\u4EE4\u81EA\u52A8\u5F52\u5165\uFF1B\u5B8C\u6210\u540E linkai flow-end --status delivered\uFF08\u653E\u5F03\u6216\u5931\u8D25\u7528 abandoned / failed\uFF09\u3002':null}function j6(t,e=Date.now()){let r=Xt.join(is(),"hints.json");try{let n={};try{n=JSON.parse(Ze.readFileSync(r,"utf8"))||{}}catch{}let i=n.flowNotice?.[t]??0;if(e-i>=0&&e-i<$6)return!1;let s=Object.entries(n.flowNotice||{}).sort((a,c)=>c[1]-a[1]).slice(0,19);return n.flowNotice=Object.fromEntries([...s,[t,e]]),Ze.writeFileSync(r,JSON.stringify(n),{mode:384}),!0}catch{return!0}}function SN(){return Ar().caller.model?.name??null}function z6(){return Ar().flow}function K6(t){let e=Ar();if(!ZE.includes(t.type))throw new Error(`\u5DE5\u4F5C\u6D41\u7C7B\u578B\u53EA\u80FD\u662F ${ZE.join(" / ")}`);if(!String(t.goal||"").trim())throw new Error("\u9700\u8981 --goal\uFF08\u8FD9\u4E2A\u5DE5\u4F5C\u6D41\u8981\u5B8C\u6210\u4EC0\u4E48\uFF09");let r=process.env.LINKAI_COLLAB_WORKER||null,n=e.flow,i=lN({id:t.id,adoptId:n?.id??null,source:t.source??"explicit",goal:String(t.goal).trim().slice(0,500),type:t.type,plan:t.plan??null,key:e.flowKey,agent:e.caller.agent,worker:r}),s=n?.id===i.id;if(n&&n.id!==i.id){try{nC(n.id,e.runId,{})}catch{}try{sN(i.id,e.runId,process.pid)}catch{}}e.flow={id:i.id,attach:t.source==="board"?"board":"flag"};let a=vd(t.model)??vd(process.env.LINKAI_AI_MODEL)??(n?iC(n.id)?.model:null)??null;return a&&(AN(i.id,a),e.caller.model={name:a,source:"self-reported"}),Hr("flow.start",{goal:i.goal,type:i.type,plan:i.plan,source:i.source,model:a,previousFlow:n?.id!==i.id?n?.id??null:null,adopted:s}),i}function X6(t){let e=Ar(),r=t.id||e.flow?.id;if(!r)throw new Error("\u6CA1\u6709\u5F53\u524D\u5DE5\u4F5C\u6D41\uFF1B\u7528 --flow <id> \u6307\u5B9A");if(!eC.includes(t.status))throw new Error(`\u7ED3\u675F\u72B6\u6001\u53EA\u80FD\u662F ${eC.join(" / ")}`);let n=uN(r,t.status,t.result??null);return Hr("flow.end",{status:n.status,result:n.result,goal:n.goal,type:n.type,plan:n.plan,source:n.source,runs:n.runs,durationMs:(n.endedAt??Date.now())-n.startedAt},{id:n.id,attach:e.flow?.id===n.id?e.flow.attach:"flag"}),n}function n$(t){let e=Ar();e.marks[t]=Date.now()-e.startedAt}function EC(){let t=Ar();return{...t.marks,total:Date.now()-t.startedAt,sinceProcessStart:Math.round(process.uptime()*1e3)}}function kN(t,e){if(t.outcome)return t.outcome;let r=t.argv.some(n=>i$.has(n));return{ok:Number(e??0)===0,...r?{kind:"help"}:{}}}function Hd(t){Ar().outcome=t}function uC(){try{return Ze.existsSync(BA())&&Ze.statSync(BA()).size>0?!0:Ze.readdirSync(is()).some(t=>t.startsWith("sending-"))}catch{return!1}}function s$(){try{let{pid:t,at:e}=JSON.parse(Ze.readFileSync(lC(),"utf8"));return Date.now()-e>BN+5e3?!1:(process.kill(t,0),!0)}catch{return!1}}function NN(t=process.argv[1]){if(Od()||hl()||!t||Ud()||!uC()||s$())return!1;try{return x6(process.execPath,[t,"__oplog-upload"],{detached:!0,stdio:"ignore",windowsHide:!0,env:{...process.env,LINKAI_OPLOG_UPLOADER:"1"}}).unref(),!0}catch{return!1}}async function o$(t=BN){let e=Ar();if(!e.transport)return;try{hC(),Ze.writeFileSync(lC(),JSON.stringify({pid:process.pid,at:Date.now()}),{mode:384})}catch{}let r=Date.now()+t,n=setTimeout(()=>e.abort?.abort(),t);try{for(;Date.now()<r&&uC()&&!Ud();)await pC(),uC()&&await new Promise(i=>setTimeout(i,500))}finally{clearTimeout(n);try{Ze.rmSync(lC(),{force:!0})}catch{}}}async function a$(t=300){let e=Ar();if(e.ended)return!1;e.ended=!0,e.flushTimer&&(clearTimeout(e.flushTimer),e.flushTimer=null);let r=kN(e,process.exitCode);Hr("run.end",{exitCode:process.exitCode??0,durationMs:Date.now()-e.startedAt,...r}),DN(e,r),e.flushTimer&&(clearTimeout(e.flushTimer),e.flushTimer=null);let n=!1;if(e.flushing){let i=e.flushing,s;await Promise.race([i,new Promise(a=>{s=setTimeout(()=>{n=!0,e.abort?.abort(),a()},t)})]),clearTimeout(s),await i.catch(()=>{})}return NN(),n}function c$(){let t=globalThis.__linkai_oplog__;t?.flushTimer&&clearTimeout(t.flushTimer),globalThis.__linkai_oplog__=void 0}var xN,w6,Q6,IN,b6,D6,S6,k6,Md,BA,Od,hl,BN,lC,To,T6,R6,v6,O6,yN,G6,J6,$6,Z6,e$,t$,r$,i$,fl=re(()=>{"use strict";ei();fN();CN();xA();xN="linkai.oplog/1",w6=1800*1e3,Q6=20*1024*1024,IN=200,b6=300,D6=60*1e3,S6=60*1e3,k6=3600*1e3,Md=o(()=>Xt.join(is(),"upload-state.json"),"backoffFile");o(Ud,"inBackoff");o(N6,"recordUpload");o(is,"oplogDir");BA=o(()=>Xt.join(is(),"spool.jsonl"),"spoolFile"),Od=o(()=>!!process.env.VITEST,"testMode"),hl=o(()=>process.env.LINKAI_OPLOG_UPLOADER==="1","uploaderMode"),BN=30*1e3,lC=o(()=>Xt.join(is(),"uploader.lock"),"lockFile");o(hC,"ensureDir");o(fC,"readJson");o(_6,"installId");To=o(t=>B6("sha256").update(t).digest("hex"),"sha256"),T6=[[/WORKBUDDY/i,"workbuddy"],[/^CLAUDECODE$|^CLAUDE_CODE_/,"claude"],[/^CODEX_/,"codex"],[/CODEBUDDY/i,"codebuddy"],[/^CURSOR_/,"cursor"]],R6=[[/claude/i,"claude"],[/codex/i,"codex"],[/workbuddy/i,"workbuddy"],[/codebuddy/i,"codebuddy"],[/cursor/i,"cursor"],[/deepseek/i,"deepseek"]];o(wN,"detectCaller");v6=tC;o(F6,"nextSession");o(M6,"nextSessionUnlocked");o(Ar,"state");o(L6,"currentRunId");o(U6,"currentCaller");O6=/pass(word)?|token|secret|cookie|credential|authorization|api[-_]?key/i,yN=/^(file|spec|body|sql|input|output|dir|image|path|config)$/i;o(H6,"fileInfo");o(dC,"sanitizeArgs");o(gC,"describeArgv");o(P6,"appendSpool");o(QN,"buildEvent");o(Hr,"emit");o(V6,"setIdentity");o(q6,"setTransport");o(bN,"scheduleFlush");o(pC,"flush");o(Y6,"startRun");G6=o(()=>rC()*60*1e3,"flowIdleMs");o(W6,"attachFlow");o(DN,"flowRunEnded");J6=/^linkai\/(flow-|collab-|login$|logout$)/;o(mC,"flowNotice");$6=10*6e4;o(j6,"flowNoticeDue");o(SN,"currentModel");o(z6,"currentFlow");o(K6,"startFlow");o(X6,"endFlow");Z6=tN,e$=sC,t$=hN,r$=rC;o(n$,"mark");o(EC,"startupMarks");i$=new Set(["--help","-h","help"]);o(kN,"finalOutcome");o(Hd,"setOutcome");o(uC,"pendingUpload");o(s$,"uploaderRunning");o(NN,"spawnUploader");o(o$,"runUploader");o(a$,"endRun");o(c$,"_resetForTests")});var wA={};At(wA,{emitFailure:()=>ce,emitResult:()=>Ae,resultShape:()=>Vd});function _N(t){for(let e of t)Hr("notice",{kind:e.kind,text:e.text.slice(0,200),chars:e.text.length});return t.length?t.map(e=>e.text).join(" "):null}function l$(){let t=mC();return _N([...Fd(),...t?[{text:t,kind:"flow-start"}]:[]])}function Vd(t){return Array.isArray(t)?{kind:"array",rows:t.length}:t&&typeof t=="object"?{kind:"object",keys:Object.keys(t).slice(0,30)}:{kind:t==null?"null":typeof t}}function Ae(t){let e=l$(),r=JSON.stringify({ok:!0,data:t??null,...e?{notice:e}:{}});Hd({ok:!0,outputBytes:Buffer.byteLength(r),result:Vd(t)}),console.log(r)}function ce(t,e){let r=t,n=r?.ok===!1&&r.error&&typeof r.error=="object"?r:void 0;n&&(t=n.error);let i=JE(t);if(n?.trace&&(i.trace=n.trace),t&&typeof t=="object"){let l=t;for(let u of["code","message","hint","candidates","matches_n","details","expected","actual","available","available_keys","download"])["code","message","hint"].includes(u)&&typeof l[u]!="string"||l[u]!==void 0&&(i.error[u]=l[u])}["command_result_unknown","command_lost","result_evicted","outcome_unknown","detached_mid_command","cdp_timeout"].includes(i.error.code)&&(i.error.outcome="unknown");let s=i.error;i.error.help&&(s.hint??=i.error.help,delete i.error.help),(i.error.code==="usage_error"||i.error.code.startsWith("commander."))&&(i.error.exitCode=2),e!==void 0&&(i.error.exitCode=e),i.error.code==="UNKNOWN"&&i.error.exitCode===2&&(i.error.code="usage_error");let a=_N(Fd()),c=JSON.stringify(a?{...i,notice:a}:i);Hd({ok:!1,errorCode:i.error.code,errorExitCode:i.error.exitCode||1,message:String(i.error.message||"").slice(0,500),outcomeUnknown:s.outcome==="unknown"||void 0}),console.log(c),process.exitCode=i.error.exitCode||1}var fn=re(()=>{"use strict";Ct();fl();xA();o(_N,"deliver");o(l$,"notices");o(Vd,"resultShape");o(Ae,"emitResult");o(ce,"emitFailure")});var bA={};At(bA,{GATE_EXEMPT:()=>GN,PACKAGE_NAME:()=>yC,UpgradeRequiredError:()=>Gd,autoUpgradeEnabled:()=>HN,belowMinVersion:()=>p$,checkAndUpgrade:()=>qN,checkDue:()=>YN,compareVersions:()=>gl,enforceMinVersion:()=>B$,fetchLatest:()=>xC,installPrefix:()=>Wd,npmCommand:()=>VN,readState:()=>Hs,reexec:()=>E$,registryUrl:()=>QA,spawnBackgroundUpgrade:()=>g$,takeResult:()=>m$,verifyIntegrity:()=>PN});import{spawn as u$,spawnSync as FN}from"node:child_process";import{createHash as TN}from"node:crypto";import*as Zt from"node:fs";import*as MN from"node:os";import*as Pr from"node:path";function Hs(){try{return JSON.parse(Zt.readFileSync(CC(),"utf8"))||{}}catch{return{}}}function Ro(t){let e={...Hs(),...t};try{Zt.mkdirSync(dl(),{recursive:!0,mode:448});let r=`${CC()}.${process.pid}.tmp`;Zt.writeFileSync(r,JSON.stringify(e),{mode:384}),Zt.renameSync(r,CC())}catch{}return e}function gl(t,e){let r=o(s=>String(s).replace(/^v/,"").split("-")[0].split(".").map(a=>Number(a)||0),"p"),[n,i]=[r(t),r(e)];for(let s=0;s<3;s++)if((n[s]??0)!==(i[s]??0))return(n[s]??0)-(i[s]??0);return 0}function QA(t=process.env,e=mA){if(t.LINKAI_REGISTRY)return t.LINKAI_REGISTRY.replace(/\/+$/,"");try{let r=JSON.parse(Zt.readFileSync(Pr.join(e,"package.json"),"utf8"));return String(r.publishConfig?.registry||"").replace(/\/+$/,"")}catch{return""}}function Wd(t=mA,e=process.platform){let r=(e==="win32"?Pr.win32:Pr.posix).resolve(t).split(/[\\/]+/),n=r.length;if(n<4||r[n-1]!=="linkai-cli"||r[n-2]!=="@linkaicli"||r[n-3]!=="node_modules"||r.includes("_npx")||r.includes(".npm"))return null;let i=e==="win32"?"\\":"/",s=r.slice(0,n-3);if(e!=="win32"){if(s[s.length-1]!=="lib")return null;s.pop()}return s.join(i)||i}function HN(t=process.env){return t.LINKAI_NO_AUTO_UPGRADE==="1"||t.CI||t.CONTINUOUS_INTEGRATION||t.VITEST?!1:!!QA(t)&&Wd()!==null}async function xC(t=QA(),e=UN){let r={Accept:"application/json"};process.env.LINKAI_REGISTRY_TOKEN&&(r.Authorization=`Bearer ${process.env.LINKAI_REGISTRY_TOKEN}`);let n=await fetch(`${t}/${yC.replace("/","%2f")}`,{headers:r,signal:AbortSignal.timeout(e)});if(!n.ok)throw new Error(`\u7248\u672C\u67E5\u8BE2\u5931\u8D25\uFF1AHTTP ${n.status}`);let i=await n.json(),s=i["dist-tags"]?.latest,a=s?i.versions?.[s]:void 0;if(!s||!a?.dist?.tarball)throw new Error("\u6E90\u4E0A\u6CA1\u6709\u53EF\u7528\u7684\u7248\u672C\u4FE1\u606F");return{version:s,tarball:a.dist.tarball,integrity:a.dist.integrity,shasum:a.dist.shasum,minVersion:a.linkaiUpgrade?.minVersion||null}}function PN(t,e){let r=String(e.integrity||"").split(/\s+/).find(n=>n.startsWith("sha512-"));return r?`sha512-${TN("sha512").update(t).digest("base64")}`===r:e.shasum?TN("sha1").update(t).digest("hex")===e.shasum:!1}async function RN(t){let e={};process.env.LINKAI_REGISTRY_TOKEN&&(e.Authorization=`Bearer ${process.env.LINKAI_REGISTRY_TOKEN}`);let r=await fetch(t.tarball,{headers:e,signal:AbortSignal.timeout(ON)});if(!r.ok)throw new Error(`\u4E0B\u8F7D\u5931\u8D25\uFF1AHTTP ${r.status}`);let n=Buffer.from(await r.arrayBuffer());if(!PN(n,t))throw new Error("\u5B89\u88C5\u5305\u6821\u9A8C\u5931\u8D25\uFF08integrity \u4E0D\u5339\u914D\uFF09\uFF0C\u672A\u5B89\u88C5");Zt.mkdirSync(dl(),{recursive:!0,mode:448});let i=Pr.join(dl(),`linkai-cli-${t.version}.tgz`);return Zt.writeFileSync(i,n,{mode:384}),i}function VN(t=process.execPath,e=process.platform){let r=Pr.dirname(t),i=(e==="win32"?[Pr.join(r,"node_modules","npm","bin","npm-cli.js")]:[Pr.join(r,"..","lib","node_modules","npm","bin","npm-cli.js"),Pr.join(r,"node_modules","npm","bin","npm-cli.js")]).find(s=>Zt.existsSync(s));return i?{cmd:t,args:[i],shell:!1}:{cmd:e==="win32"?"npm.cmd":"npm",args:[],shell:e==="win32"}}function vN(t,e){let r=VN(),n=FN(r.cmd,[...r.args,"install","-g",t,"--prefix",e,"--no-audit","--no-fund","--loglevel=error"],{encoding:"utf8",timeout:ON,shell:r.shell,windowsHide:!0,env:{...process.env,npm_config_update_notifier:"false"}});if(n.status!==0)throw new Error(`npm \u5B89\u88C5\u5931\u8D25\uFF1A${String(n.stderr||n.error?.message||n.status).trim().slice(-300)}`)}function IC(t){try{return JSON.parse(Zt.readFileSync(Pr.join(t,"package.json"),"utf8")).version||null}catch{return null}}function d$(){try{Zt.mkdirSync(dl(),{recursive:!0,mode:448});try{let t=Zt.statSync(Yd());if(Date.now()-t.mtimeMs<f$)return!1;Zt.rmSync(Yd(),{force:!0})}catch{}return Zt.writeFileSync(Yd(),String(process.pid),{flag:"wx"}),!0}catch{return!1}}async function qN({force:t=!1,pkgDir:e=mA,current:r=st,registry:n=QA()}={}){let i=Wd(e);if(!n||!i||!d$())return null;let s=Date.now(),a;try{Ro({lastAttempt:s}),a=await xC(n);let u=Hs();if(Ro({lastCheck:Date.now(),failures:0,latest:a.version,minVersion:a.minVersion}),gl(a.version,r)<=0||!t&&u.skip?.version===a.version&&Date.now()<u.skip.until)return qd(),null}catch{let u=(Hs().failures||0)+1;return Ro({failures:u}),qd(),null}let c=r,l;try{let u=await RN(a);vN(u,i);let A=IC(e);if(A!==a.version)throw new Error(`\u5B89\u88C5\u540E\u7248\u672C\u662F ${A??"\u672A\u77E5"}\uFF0C\u4E0D\u662F ${a.version}`);l={ok:!0,from:c,to:a.version,at:Date.now(),ms:Date.now()-s,prefix:i};try{Zt.rmSync(u,{force:!0})}catch{}}catch(u){let A=!1;if(IC(e)!==c)try{let g=(await fetch(`${n}/${yC.replace("/","%2f")}`,{signal:AbortSignal.timeout(UN)}).then(d=>d.json())).versions?.[c]?.dist;g?.tarball&&(vN(await RN({version:c,tarball:g.tarball,integrity:g.integrity,shasum:g.shasum,minVersion:null}),i),A=!0)}catch{}l={ok:!1,from:c,to:a.version,at:Date.now(),ms:Date.now()-s,error:String(u.message).slice(0,300),rolledBack:A,prefix:i},Ro({skip:{version:a.version,until:Date.now()+LN}})}return Ro({result:l}),qd(),l}function YN(t=Hs(),e=Date.now()){if(e-(t.lastCheck||0)<A$)return!1;let r=Math.min(LN,h$*2**Math.max(0,(t.failures||0)-1));return!t.failures||e-(t.lastAttempt||0)>=r}function g$(t=process.argv[1]){if(!t||!HN()||process.env.LINKAI_UPGRADER==="1")return!1;let e=Hs();if(!(e.latest&&gl(e.latest,st)>0&&!(e.skip?.version===e.latest&&Date.now()<e.skip.until))&&!YN(e))return!1;try{return u$(process.execPath,[t,"__self-upgrade"],{detached:!0,stdio:"ignore",windowsHide:!0,env:{...process.env,LINKAI_UPGRADER:"1"}}).unref(),!0}catch{return!1}}function p$(t=Hs()){return!!(t.minVersion&&gl(t.minVersion,st)>0)}function m$(){let t=Hs();return t.result?(Ro({result:null}),t.result):null}function E$(t,e){return FN(process.execPath,[t,...e],{stdio:"inherit",env:{...process.env,LINKAI_REEXEC:"1"}}).status??1}async function B$(t,{pkgDir:e=mA,current:r=st,registry:n=QA(),env:i=process.env,now:s=Date.now()}={}){if(!t.length||t[0].startsWith("__")||GN.has(t[0])||i.LINKAI_SKIP_UPGRADE==="1"||i.LINKAI_REEXEC==="1"||i.LINKAI_NO_AUTO_UPGRADE==="1"||i.CI||i.CONTINUOUS_INTEGRATION||i.VITEST||!n||Wd(e)===null)return"run";let a=Hs(),c=s-(a.lastCheck||0)>=C$,l=!a.failures||s-(a.lastAttempt||0)>=I$;if(c&&l)try{let h=await xC(n,y$);a=Ro({lastCheck:Date.now(),failures:0,latest:h.version,minVersion:h.minVersion})}catch{return Ro({lastAttempt:Date.now(),failures:(a.failures||0)+1}),"run"}let u=a.minVersion;if(!u||gl(u,r)<=0)return"run";let A=await qN({force:!0,pkgDir:e,current:r,registry:n});if(A?.ok)return"reexec";if(!A){let h=Date.now()+x$;for(;Date.now()<h;){let g=IC(e);if(g&&gl(g,u)>=0)return"reexec";await new Promise(d=>setTimeout(d,1e3))}}throw new Gd(u,r,A?.error||"\u7B49\u5F85\u5176\u5B83\u8FDB\u7A0B\u5B8C\u6210\u5347\u7EA7\u8D85\u65F6")}var yC,A$,h$,LN,f$,UN,ON,dl,CC,Yd,qd,C$,I$,y$,x$,GN,Gd,DA=re(()=>{"use strict";ei();yC="@linkaicli/linkai-cli",A$=4*36e5,h$=30*6e4,LN=24*36e5,f$=10*6e4,UN=15e3,ON=18e4,dl=o(()=>Pr.join(MN.homedir(),".linkai","upgrade"),"dir"),CC=o(()=>Pr.join(dl(),"state.json"),"stateFile"),Yd=o(()=>Pr.join(dl(),"lock"),"lockFile");o(Hs,"readState");o(Ro,"writeState");o(gl,"compareVersions");o(QA,"registryUrl");o(Wd,"installPrefix");o(HN,"autoUpgradeEnabled");o(xC,"fetchLatest");o(PN,"verifyIntegrity");o(RN,"download");o(VN,"npmCommand");o(vN,"npmInstall");o(IC,"installedVersion");o(d$,"acquireLock");qd=o(()=>{try{Zt.rmSync(Yd(),{force:!0})}catch{}},"releaseLock");o(qN,"checkAndUpgrade");o(YN,"checkDue");o(g$,"spawnBackgroundUpgrade");o(p$,"belowMinVersion");o(m$,"takeResult");o(E$,"reexec");C$=10*6e4,I$=10*6e4,y$=5e3,x$=6e4,GN=new Set(["-V","--version","-h","--help","help","doctor","daemon"]),Gd=class extends Error{static{o(this,"UpgradeRequiredError")}minVersion;current;reason;constructor(e,r,n){super(`LinkAI-CLI ${r} \u4F4E\u4E8E\u8981\u6C42\u7684\u6700\u4F4E\u7248\u672C ${e}\uFF0C\u81EA\u52A8\u5347\u7EA7\u5931\u8D25\uFF1A${n}`),this.minVersion=e,this.current=r,this.reason=n,this.name="UpgradeRequiredError"}};o(B$,"enforceMinVersion")});function w$(t){return t&&t.__esModule&&Object.prototype.hasOwnProperty.call(t,"default")?t.default:t}function SA(){if(WN)return vo;WN=1;function t(a){return typeof a>"u"||a===null}o(t,"isNothing");function e(a){return typeof a=="object"&&a!==null}o(e,"isObject");function r(a){return Array.isArray(a)?a:t(a)?[]:[a]}o(r,"toArray");function n(a,c){if(c){let l=Object.keys(c);for(let u=0,A=l.length;u<A;u+=1){let h=l[u];a[h]=c[h]}}return a}o(n,"extend");function i(a,c){let l="";for(let u=0;u<c;u+=1)l+=a;return l}o(i,"repeat");function s(a){return a===0&&Number.NEGATIVE_INFINITY===1/a}return o(s,"isNegativeZero"),vo.isNothing=t,vo.isObject=e,vo.toArray=r,vo.repeat=i,vo.isNegativeZero=s,vo.extend=n,vo}function kA(){if(JN)return BC;JN=1;function t(r,n){let i="",s=r.reason||"(unknown reason)";return r.mark?(r.mark.name&&(i+='in "'+r.mark.name+'" '),i+="("+(r.mark.line+1)+":"+(r.mark.column+1)+")",!n&&r.mark.snippet&&(i+=`
40
+ `,{mode:384})}catch{}}try{Ze.rmSync(c,{force:!0})}catch{}if(A>=0)break}}finally{e.flushing=null,e.abort=null}})(),e.flushing}function Y6(t){let e=Ar();e.n>0||(e.argv=t,hl()||W6(),Hr("run.start",{argv:gC(t),cwdHash:To(process.cwd()).slice(0,16),pid:process.pid,worker:process.env.LINKAI_COLLAB_WORKER||null,processUptimeMs:Math.round(process.uptime()*1e3),parentRunId:process.env.LINKAI_PARENT_RUN_ID||null}),process.env.LINKAI_PARENT_RUN_ID=e.runId,process.once("exit",r=>{if(!e.ended){e.ended=!0;let n=kN(e,r);Hr("run.end",{exitCode:r,durationMs:Date.now()-e.startedAt,...n,abrupt:!0}),DN(e,n)}}))}function W6(){let t=Ar();try{let e=G6(),r=process.env.LINKAI_COLLAB_WORKER||null;t.flowKey=nN({installId:t.installId,agent:t.caller.agent,cwdHash:To(process.cwd()).slice(0,16),worker:r,toolSessionId:t.caller.toolSessionId??null});let n=vd(process.env.LINKAI_AI_MODEL),i=aN({key:t.flowKey,agent:t.caller.agent,worker:r,idleMs:e,explicitId:process.env.LINKAI_FLOW||null,board:rN(r),runId:t.runId,pid:process.pid,model:n});for(let s of i.stalled)Hr("flow.stalled",{status:s.status,reason:s.reason,source:s.source,goal:s.goal,type:s.type,plan:s.plan,lastActivity:new Date(s.lastActivity).toISOString(),lastCommand:s.lastCommand,lastErrorCode:s.lastErrorCode,build:s.build,openRuns:Object.keys(s.runsOpen).length,idleMinutes:e/6e4},{id:s.id,attach:"detected"});t.flow={id:i.id,attach:i.attach},t.caller.model=i.model?{name:i.model,source:"self-reported"}:null}catch{}}function DN(t,e){if(t.flow)try{nC(t.flow.id,t.runId,{command:t.command??gC(t.argv).command,ok:typeof e.ok=="boolean"?e.ok:null,errorCode:typeof e.errorCode=="string"?e.errorCode:null,outcomeUnknown:e.outcomeUnknown===!0})}catch{}}function mC(){let t=Ar();if(!t.flow||!t.command||J6.test(t.command)||t.access!=="write"&&t.command!=="linkai/status")return null;try{if(iC(t.flow.id)?.source!=="auto")return null}catch{return null}return j6(t.flow.id)?'\u5F53\u524D\u4EFB\u52A1\u5C1A\u672A\u5F00\u59CB\u5DE5\u4F5C\u6D41\uFF1A\u73B0\u5728\u5C31\u6267\u884C linkai flow-start --goal "<\u7528\u6237\u76EE\u6807\uFF0C\u4E00\u53E5\u8BDD>" --type create|modify|import|query [--plan <planId>\uFF08\u4FEE\u6539\u5DF2\u6709\u65B9\u6848\u65F6\u5FC5\u5E26\uFF09] --model "<\u4F60\u7684\u6A21\u578B\u4E0E\u7248\u672C>"\uFF0C\u6B64\u524D\u7684\u547D\u4EE4\u4F1A\u4E00\u8D77\u5F52\u5165\uFF1B\u5B8C\u6210\u540E linkai flow-end --status delivered\uFF08\u653E\u5F03\u6216\u5931\u8D25\u7528 abandoned / failed\uFF09\u3002\u4E0D\u5F00\u5DE5\u4F5C\u6D41\uFF0C\u9886\u8C0B\u7F51\u9875\u4E0A\u770B\u4E0D\u51FA\u8FD9\u4E2A\u4EFB\u52A1\u7684\u76EE\u6807\u548C\u65B9\u6848\u3002':null}function j6(t,e=Date.now()){let r=Xt.join(is(),"hints.json");try{let n={};try{n=JSON.parse(Ze.readFileSync(r,"utf8"))||{}}catch{}let i=n.flowNotice?.[t]??0;if(e-i>=0&&e-i<$6)return!1;let s=Object.entries(n.flowNotice||{}).sort((a,c)=>c[1]-a[1]).slice(0,19);return n.flowNotice=Object.fromEntries([...s,[t,e]]),Ze.writeFileSync(r,JSON.stringify(n),{mode:384}),!0}catch{return!0}}function SN(){return Ar().caller.model?.name??null}function z6(){return Ar().flow}function K6(t){let e=Ar();if(!ZE.includes(t.type))throw new Error(`\u5DE5\u4F5C\u6D41\u7C7B\u578B\u53EA\u80FD\u662F ${ZE.join(" / ")}`);if(!String(t.goal||"").trim())throw new Error("\u9700\u8981 --goal\uFF08\u8FD9\u4E2A\u5DE5\u4F5C\u6D41\u8981\u5B8C\u6210\u4EC0\u4E48\uFF09");let r=process.env.LINKAI_COLLAB_WORKER||null,n=e.flow,i=lN({id:t.id,adoptId:n?.id??null,source:t.source??"explicit",goal:String(t.goal).trim().slice(0,500),type:t.type,plan:t.plan??null,key:e.flowKey,agent:e.caller.agent,worker:r}),s=n?.id===i.id;if(n&&n.id!==i.id){try{nC(n.id,e.runId,{})}catch{}try{sN(i.id,e.runId,process.pid)}catch{}}e.flow={id:i.id,attach:t.source==="board"?"board":"flag"};let a=vd(t.model)??vd(process.env.LINKAI_AI_MODEL)??(n?iC(n.id)?.model:null)??null;return a&&(AN(i.id,a),e.caller.model={name:a,source:"self-reported"}),Hr("flow.start",{goal:i.goal,type:i.type,plan:i.plan,source:i.source,model:a,previousFlow:n?.id!==i.id?n?.id??null:null,adopted:s}),i}function X6(t){let e=Ar(),r=t.id||e.flow?.id;if(!r)throw new Error("\u6CA1\u6709\u5F53\u524D\u5DE5\u4F5C\u6D41\uFF1B\u7528 --flow <id> \u6307\u5B9A");if(!eC.includes(t.status))throw new Error(`\u7ED3\u675F\u72B6\u6001\u53EA\u80FD\u662F ${eC.join(" / ")}`);let n=uN(r,t.status,t.result??null);return Hr("flow.end",{status:n.status,result:n.result,goal:n.goal,type:n.type,plan:n.plan,source:n.source,runs:n.runs,durationMs:(n.endedAt??Date.now())-n.startedAt},{id:n.id,attach:e.flow?.id===n.id?e.flow.attach:"flag"}),n}function n$(t){let e=Ar();e.marks[t]=Date.now()-e.startedAt}function EC(){let t=Ar();return{...t.marks,total:Date.now()-t.startedAt,sinceProcessStart:Math.round(process.uptime()*1e3)}}function kN(t,e){if(t.outcome)return t.outcome;let r=t.argv.some(n=>i$.has(n));return{ok:Number(e??0)===0,...r?{kind:"help"}:{}}}function Hd(t){Ar().outcome=t}function uC(){try{return Ze.existsSync(BA())&&Ze.statSync(BA()).size>0?!0:Ze.readdirSync(is()).some(t=>t.startsWith("sending-"))}catch{return!1}}function s$(){try{let{pid:t,at:e}=JSON.parse(Ze.readFileSync(lC(),"utf8"));return Date.now()-e>BN+5e3?!1:(process.kill(t,0),!0)}catch{return!1}}function NN(t=process.argv[1]){if(Od()||hl()||!t||Ud()||!uC()||s$())return!1;try{return x6(process.execPath,[t,"__oplog-upload"],{detached:!0,stdio:"ignore",windowsHide:!0,env:{...process.env,LINKAI_OPLOG_UPLOADER:"1"}}).unref(),!0}catch{return!1}}async function o$(t=BN){let e=Ar();if(!e.transport)return;try{hC(),Ze.writeFileSync(lC(),JSON.stringify({pid:process.pid,at:Date.now()}),{mode:384})}catch{}let r=Date.now()+t,n=setTimeout(()=>e.abort?.abort(),t);try{for(;Date.now()<r&&uC()&&!Ud();)await pC(),uC()&&await new Promise(i=>setTimeout(i,500))}finally{clearTimeout(n);try{Ze.rmSync(lC(),{force:!0})}catch{}}}async function a$(t=300){let e=Ar();if(e.ended)return!1;e.ended=!0,e.flushTimer&&(clearTimeout(e.flushTimer),e.flushTimer=null);let r=kN(e,process.exitCode);Hr("run.end",{exitCode:process.exitCode??0,durationMs:Date.now()-e.startedAt,...r}),DN(e,r),e.flushTimer&&(clearTimeout(e.flushTimer),e.flushTimer=null);let n=!1;if(e.flushing){let i=e.flushing,s;await Promise.race([i,new Promise(a=>{s=setTimeout(()=>{n=!0,e.abort?.abort(),a()},t)})]),clearTimeout(s),await i.catch(()=>{})}return NN(),n}function c$(){let t=globalThis.__linkai_oplog__;t?.flushTimer&&clearTimeout(t.flushTimer),globalThis.__linkai_oplog__=void 0}var xN,w6,Q6,IN,b6,D6,S6,k6,Md,BA,Od,hl,BN,lC,To,T6,R6,v6,O6,yN,G6,J6,$6,Z6,e$,t$,r$,i$,fl=re(()=>{"use strict";ei();fN();CN();xA();xN="linkai.oplog/1",w6=1800*1e3,Q6=20*1024*1024,IN=200,b6=300,D6=60*1e3,S6=60*1e3,k6=3600*1e3,Md=o(()=>Xt.join(is(),"upload-state.json"),"backoffFile");o(Ud,"inBackoff");o(N6,"recordUpload");o(is,"oplogDir");BA=o(()=>Xt.join(is(),"spool.jsonl"),"spoolFile"),Od=o(()=>!!process.env.VITEST,"testMode"),hl=o(()=>process.env.LINKAI_OPLOG_UPLOADER==="1","uploaderMode"),BN=30*1e3,lC=o(()=>Xt.join(is(),"uploader.lock"),"lockFile");o(hC,"ensureDir");o(fC,"readJson");o(_6,"installId");To=o(t=>B6("sha256").update(t).digest("hex"),"sha256"),T6=[[/WORKBUDDY/i,"workbuddy"],[/^CLAUDECODE$|^CLAUDE_CODE_/,"claude"],[/^CODEX_/,"codex"],[/CODEBUDDY/i,"codebuddy"],[/^CURSOR_/,"cursor"]],R6=[[/claude/i,"claude"],[/codex/i,"codex"],[/workbuddy/i,"workbuddy"],[/codebuddy/i,"codebuddy"],[/cursor/i,"cursor"],[/deepseek/i,"deepseek"]];o(wN,"detectCaller");v6=tC;o(F6,"nextSession");o(M6,"nextSessionUnlocked");o(Ar,"state");o(L6,"currentRunId");o(U6,"currentCaller");O6=/pass(word)?|token|secret|cookie|credential|authorization|api[-_]?key/i,yN=/^(file|spec|body|sql|input|output|dir|image|path|config)$/i;o(H6,"fileInfo");o(dC,"sanitizeArgs");o(gC,"describeArgv");o(P6,"appendSpool");o(QN,"buildEvent");o(Hr,"emit");o(V6,"setIdentity");o(q6,"setTransport");o(bN,"scheduleFlush");o(pC,"flush");o(Y6,"startRun");G6=o(()=>rC()*60*1e3,"flowIdleMs");o(W6,"attachFlow");o(DN,"flowRunEnded");J6=/^linkai\/(flow-|collab-|login$|logout$)/;o(mC,"flowNotice");$6=10*6e4;o(j6,"flowNoticeDue");o(SN,"currentModel");o(z6,"currentFlow");o(K6,"startFlow");o(X6,"endFlow");Z6=tN,e$=sC,t$=hN,r$=rC;o(n$,"mark");o(EC,"startupMarks");i$=new Set(["--help","-h","help"]);o(kN,"finalOutcome");o(Hd,"setOutcome");o(uC,"pendingUpload");o(s$,"uploaderRunning");o(NN,"spawnUploader");o(o$,"runUploader");o(a$,"endRun");o(c$,"_resetForTests")});var wA={};At(wA,{emitFailure:()=>ce,emitResult:()=>Ae,resultShape:()=>Vd});function _N(t){for(let e of t)Hr("notice",{kind:e.kind,text:e.text.slice(0,200),chars:e.text.length});return t.length?t.map(e=>e.text).join(" "):null}function l$(){let t=mC();return _N([...Fd(),...t?[{text:t,kind:"flow-start"}]:[]])}function Vd(t){return Array.isArray(t)?{kind:"array",rows:t.length}:t&&typeof t=="object"?{kind:"object",keys:Object.keys(t).slice(0,30)}:{kind:t==null?"null":typeof t}}function Ae(t){let e=l$(),r=JSON.stringify({ok:!0,data:t??null,...e?{notice:e}:{}});Hd({ok:!0,outputBytes:Buffer.byteLength(r),result:Vd(t)}),console.log(r)}function ce(t,e){let r=t,n=r?.ok===!1&&r.error&&typeof r.error=="object"?r:void 0;n&&(t=n.error);let i=JE(t);if(n?.trace&&(i.trace=n.trace),t&&typeof t=="object"){let l=t;for(let u of["code","message","hint","candidates","matches_n","details","expected","actual","available","available_keys","download"])["code","message","hint"].includes(u)&&typeof l[u]!="string"||l[u]!==void 0&&(i.error[u]=l[u])}["command_result_unknown","command_lost","result_evicted","outcome_unknown","detached_mid_command","cdp_timeout"].includes(i.error.code)&&(i.error.outcome="unknown");let s=i.error;i.error.help&&(s.hint??=i.error.help,delete i.error.help),(i.error.code==="usage_error"||i.error.code.startsWith("commander."))&&(i.error.exitCode=2),e!==void 0&&(i.error.exitCode=e),i.error.code==="UNKNOWN"&&i.error.exitCode===2&&(i.error.code="usage_error");let a=_N(Fd()),c=JSON.stringify(a?{...i,notice:a}:i);Hd({ok:!1,errorCode:i.error.code,errorExitCode:i.error.exitCode||1,message:String(i.error.message||"").slice(0,500),outcomeUnknown:s.outcome==="unknown"||void 0}),console.log(c),process.exitCode=i.error.exitCode||1}var fn=re(()=>{"use strict";Ct();fl();xA();o(_N,"deliver");o(l$,"notices");o(Vd,"resultShape");o(Ae,"emitResult");o(ce,"emitFailure")});var bA={};At(bA,{GATE_EXEMPT:()=>GN,PACKAGE_NAME:()=>yC,UpgradeRequiredError:()=>Gd,autoUpgradeEnabled:()=>HN,belowMinVersion:()=>p$,checkAndUpgrade:()=>qN,checkDue:()=>YN,compareVersions:()=>gl,enforceMinVersion:()=>B$,fetchLatest:()=>xC,installPrefix:()=>Wd,npmCommand:()=>VN,readState:()=>Hs,reexec:()=>E$,registryUrl:()=>QA,spawnBackgroundUpgrade:()=>g$,takeResult:()=>m$,verifyIntegrity:()=>PN});import{spawn as u$,spawnSync as FN}from"node:child_process";import{createHash as TN}from"node:crypto";import*as Zt from"node:fs";import*as MN from"node:os";import*as Pr from"node:path";function Hs(){try{return JSON.parse(Zt.readFileSync(CC(),"utf8"))||{}}catch{return{}}}function Ro(t){let e={...Hs(),...t};try{Zt.mkdirSync(dl(),{recursive:!0,mode:448});let r=`${CC()}.${process.pid}.tmp`;Zt.writeFileSync(r,JSON.stringify(e),{mode:384}),Zt.renameSync(r,CC())}catch{}return e}function gl(t,e){let r=o(s=>String(s).replace(/^v/,"").split("-")[0].split(".").map(a=>Number(a)||0),"p"),[n,i]=[r(t),r(e)];for(let s=0;s<3;s++)if((n[s]??0)!==(i[s]??0))return(n[s]??0)-(i[s]??0);return 0}function QA(t=process.env,e=mA){if(t.LINKAI_REGISTRY)return t.LINKAI_REGISTRY.replace(/\/+$/,"");try{let r=JSON.parse(Zt.readFileSync(Pr.join(e,"package.json"),"utf8"));return String(r.publishConfig?.registry||"").replace(/\/+$/,"")}catch{return""}}function Wd(t=mA,e=process.platform){let r=(e==="win32"?Pr.win32:Pr.posix).resolve(t).split(/[\\/]+/),n=r.length;if(n<4||r[n-1]!=="linkai-cli"||r[n-2]!=="@linkaicli"||r[n-3]!=="node_modules"||r.includes("_npx")||r.includes(".npm"))return null;let i=e==="win32"?"\\":"/",s=r.slice(0,n-3);if(e!=="win32"){if(s[s.length-1]!=="lib")return null;s.pop()}return s.join(i)||i}function HN(t=process.env){return t.LINKAI_NO_AUTO_UPGRADE==="1"||t.CI||t.CONTINUOUS_INTEGRATION||t.VITEST?!1:!!QA(t)&&Wd()!==null}async function xC(t=QA(),e=UN){let r={Accept:"application/json"};process.env.LINKAI_REGISTRY_TOKEN&&(r.Authorization=`Bearer ${process.env.LINKAI_REGISTRY_TOKEN}`);let n=await fetch(`${t}/${yC.replace("/","%2f")}`,{headers:r,signal:AbortSignal.timeout(e)});if(!n.ok)throw new Error(`\u7248\u672C\u67E5\u8BE2\u5931\u8D25\uFF1AHTTP ${n.status}`);let i=await n.json(),s=i["dist-tags"]?.latest,a=s?i.versions?.[s]:void 0;if(!s||!a?.dist?.tarball)throw new Error("\u6E90\u4E0A\u6CA1\u6709\u53EF\u7528\u7684\u7248\u672C\u4FE1\u606F");return{version:s,tarball:a.dist.tarball,integrity:a.dist.integrity,shasum:a.dist.shasum,minVersion:a.linkaiUpgrade?.minVersion||null}}function PN(t,e){let r=String(e.integrity||"").split(/\s+/).find(n=>n.startsWith("sha512-"));return r?`sha512-${TN("sha512").update(t).digest("base64")}`===r:e.shasum?TN("sha1").update(t).digest("hex")===e.shasum:!1}async function RN(t){let e={};process.env.LINKAI_REGISTRY_TOKEN&&(e.Authorization=`Bearer ${process.env.LINKAI_REGISTRY_TOKEN}`);let r=await fetch(t.tarball,{headers:e,signal:AbortSignal.timeout(ON)});if(!r.ok)throw new Error(`\u4E0B\u8F7D\u5931\u8D25\uFF1AHTTP ${r.status}`);let n=Buffer.from(await r.arrayBuffer());if(!PN(n,t))throw new Error("\u5B89\u88C5\u5305\u6821\u9A8C\u5931\u8D25\uFF08integrity \u4E0D\u5339\u914D\uFF09\uFF0C\u672A\u5B89\u88C5");Zt.mkdirSync(dl(),{recursive:!0,mode:448});let i=Pr.join(dl(),`linkai-cli-${t.version}.tgz`);return Zt.writeFileSync(i,n,{mode:384}),i}function VN(t=process.execPath,e=process.platform){let r=Pr.dirname(t),i=(e==="win32"?[Pr.join(r,"node_modules","npm","bin","npm-cli.js")]:[Pr.join(r,"..","lib","node_modules","npm","bin","npm-cli.js"),Pr.join(r,"node_modules","npm","bin","npm-cli.js")]).find(s=>Zt.existsSync(s));return i?{cmd:t,args:[i],shell:!1}:{cmd:e==="win32"?"npm.cmd":"npm",args:[],shell:e==="win32"}}function vN(t,e){let r=VN(),n=FN(r.cmd,[...r.args,"install","-g",t,"--prefix",e,"--no-audit","--no-fund","--loglevel=error"],{encoding:"utf8",timeout:ON,shell:r.shell,windowsHide:!0,env:{...process.env,npm_config_update_notifier:"false"}});if(n.status!==0)throw new Error(`npm \u5B89\u88C5\u5931\u8D25\uFF1A${String(n.stderr||n.error?.message||n.status).trim().slice(-300)}`)}function IC(t){try{return JSON.parse(Zt.readFileSync(Pr.join(t,"package.json"),"utf8")).version||null}catch{return null}}function d$(){try{Zt.mkdirSync(dl(),{recursive:!0,mode:448});try{let t=Zt.statSync(Yd());if(Date.now()-t.mtimeMs<f$)return!1;Zt.rmSync(Yd(),{force:!0})}catch{}return Zt.writeFileSync(Yd(),String(process.pid),{flag:"wx"}),!0}catch{return!1}}async function qN({force:t=!1,pkgDir:e=mA,current:r=st,registry:n=QA()}={}){let i=Wd(e);if(!n||!i||!d$())return null;let s=Date.now(),a;try{Ro({lastAttempt:s}),a=await xC(n);let u=Hs();if(Ro({lastCheck:Date.now(),failures:0,latest:a.version,minVersion:a.minVersion}),gl(a.version,r)<=0||!t&&u.skip?.version===a.version&&Date.now()<u.skip.until)return qd(),null}catch{let u=(Hs().failures||0)+1;return Ro({failures:u}),qd(),null}let c=r,l;try{let u=await RN(a);vN(u,i);let A=IC(e);if(A!==a.version)throw new Error(`\u5B89\u88C5\u540E\u7248\u672C\u662F ${A??"\u672A\u77E5"}\uFF0C\u4E0D\u662F ${a.version}`);l={ok:!0,from:c,to:a.version,at:Date.now(),ms:Date.now()-s,prefix:i};try{Zt.rmSync(u,{force:!0})}catch{}}catch(u){let A=!1;if(IC(e)!==c)try{let g=(await fetch(`${n}/${yC.replace("/","%2f")}`,{signal:AbortSignal.timeout(UN)}).then(d=>d.json())).versions?.[c]?.dist;g?.tarball&&(vN(await RN({version:c,tarball:g.tarball,integrity:g.integrity,shasum:g.shasum,minVersion:null}),i),A=!0)}catch{}l={ok:!1,from:c,to:a.version,at:Date.now(),ms:Date.now()-s,error:String(u.message).slice(0,300),rolledBack:A,prefix:i},Ro({skip:{version:a.version,until:Date.now()+LN}})}return Ro({result:l}),qd(),l}function YN(t=Hs(),e=Date.now()){if(e-(t.lastCheck||0)<A$)return!1;let r=Math.min(LN,h$*2**Math.max(0,(t.failures||0)-1));return!t.failures||e-(t.lastAttempt||0)>=r}function g$(t=process.argv[1]){if(!t||!HN()||process.env.LINKAI_UPGRADER==="1")return!1;let e=Hs();if(!(e.latest&&gl(e.latest,st)>0&&!(e.skip?.version===e.latest&&Date.now()<e.skip.until))&&!YN(e))return!1;try{return u$(process.execPath,[t,"__self-upgrade"],{detached:!0,stdio:"ignore",windowsHide:!0,env:{...process.env,LINKAI_UPGRADER:"1"}}).unref(),!0}catch{return!1}}function p$(t=Hs()){return!!(t.minVersion&&gl(t.minVersion,st)>0)}function m$(){let t=Hs();return t.result?(Ro({result:null}),t.result):null}function E$(t,e){return FN(process.execPath,[t,...e],{stdio:"inherit",env:{...process.env,LINKAI_REEXEC:"1"}}).status??1}async function B$(t,{pkgDir:e=mA,current:r=st,registry:n=QA(),env:i=process.env,now:s=Date.now()}={}){if(!t.length||t[0].startsWith("__")||GN.has(t[0])||i.LINKAI_SKIP_UPGRADE==="1"||i.LINKAI_REEXEC==="1"||i.LINKAI_NO_AUTO_UPGRADE==="1"||i.CI||i.CONTINUOUS_INTEGRATION||i.VITEST||!n||Wd(e)===null)return"run";let a=Hs(),c=s-(a.lastCheck||0)>=C$,l=!a.failures||s-(a.lastAttempt||0)>=I$;if(c&&l)try{let h=await xC(n,y$);a=Ro({lastCheck:Date.now(),failures:0,latest:h.version,minVersion:h.minVersion})}catch{return Ro({lastAttempt:Date.now(),failures:(a.failures||0)+1}),"run"}let u=a.minVersion;if(!u||gl(u,r)<=0)return"run";let A=await qN({force:!0,pkgDir:e,current:r,registry:n});if(A?.ok)return"reexec";if(!A){let h=Date.now()+x$;for(;Date.now()<h;){let g=IC(e);if(g&&gl(g,u)>=0)return"reexec";await new Promise(d=>setTimeout(d,1e3))}}throw new Gd(u,r,A?.error||"\u7B49\u5F85\u5176\u5B83\u8FDB\u7A0B\u5B8C\u6210\u5347\u7EA7\u8D85\u65F6")}var yC,A$,h$,LN,f$,UN,ON,dl,CC,Yd,qd,C$,I$,y$,x$,GN,Gd,DA=re(()=>{"use strict";ei();yC="@linkaicli/linkai-cli",A$=4*36e5,h$=30*6e4,LN=24*36e5,f$=10*6e4,UN=15e3,ON=18e4,dl=o(()=>Pr.join(MN.homedir(),".linkai","upgrade"),"dir"),CC=o(()=>Pr.join(dl(),"state.json"),"stateFile"),Yd=o(()=>Pr.join(dl(),"lock"),"lockFile");o(Hs,"readState");o(Ro,"writeState");o(gl,"compareVersions");o(QA,"registryUrl");o(Wd,"installPrefix");o(HN,"autoUpgradeEnabled");o(xC,"fetchLatest");o(PN,"verifyIntegrity");o(RN,"download");o(VN,"npmCommand");o(vN,"npmInstall");o(IC,"installedVersion");o(d$,"acquireLock");qd=o(()=>{try{Zt.rmSync(Yd(),{force:!0})}catch{}},"releaseLock");o(qN,"checkAndUpgrade");o(YN,"checkDue");o(g$,"spawnBackgroundUpgrade");o(p$,"belowMinVersion");o(m$,"takeResult");o(E$,"reexec");C$=10*6e4,I$=10*6e4,y$=5e3,x$=6e4,GN=new Set(["-V","--version","-h","--help","help","doctor","daemon"]),Gd=class extends Error{static{o(this,"UpgradeRequiredError")}minVersion;current;reason;constructor(e,r,n){super(`LinkAI-CLI ${r} \u4F4E\u4E8E\u8981\u6C42\u7684\u6700\u4F4E\u7248\u672C ${e}\uFF0C\u81EA\u52A8\u5347\u7EA7\u5931\u8D25\uFF1A${n}`),this.minVersion=e,this.current=r,this.reason=n,this.name="UpgradeRequiredError"}};o(B$,"enforceMinVersion")});function w$(t){return t&&t.__esModule&&Object.prototype.hasOwnProperty.call(t,"default")?t.default:t}function SA(){if(WN)return vo;WN=1;function t(a){return typeof a>"u"||a===null}o(t,"isNothing");function e(a){return typeof a=="object"&&a!==null}o(e,"isObject");function r(a){return Array.isArray(a)?a:t(a)?[]:[a]}o(r,"toArray");function n(a,c){if(c){let l=Object.keys(c);for(let u=0,A=l.length;u<A;u+=1){let h=l[u];a[h]=c[h]}}return a}o(n,"extend");function i(a,c){let l="";for(let u=0;u<c;u+=1)l+=a;return l}o(i,"repeat");function s(a){return a===0&&Number.NEGATIVE_INFINITY===1/a}return o(s,"isNegativeZero"),vo.isNothing=t,vo.isObject=e,vo.toArray=r,vo.repeat=i,vo.isNegativeZero=s,vo.extend=n,vo}function kA(){if(JN)return BC;JN=1;function t(r,n){let i="",s=r.reason||"(unknown reason)";return r.mark?(r.mark.name&&(i+='in "'+r.mark.name+'" '),i+="("+(r.mark.line+1)+":"+(r.mark.column+1)+")",!n&&r.mark.snippet&&(i+=`
41
41
 
42
42
  `+r.mark.snippet),s+" "+i):s}o(t,"formatError");function e(r,n){Error.call(this),this.name="YAMLException",this.reason=r,this.mark=n,this.message=t(this,!1),Error.captureStackTrace?Error.captureStackTrace(this,this.constructor):this.stack=new Error().stack||""}return o(e,"YAMLException2"),e.prototype=Object.create(Error.prototype),e.prototype.constructor=e,e.prototype.toString=o(function(n){return this.name+": "+t(this,n)},"toString"),BC=e,BC}function Q$(){if($N)return wC;$N=1;let t=SA();function e(i,s,a,c,l){let u="",A="",h=Math.floor(l/2)-1;return c-s>h&&(u=" ... ",s=c-h+u.length),a-c>h&&(A=" ...",a=c+h-A.length),{str:u+i.slice(s,a).replace(/\t/g,"\u2192")+A,pos:c-s+u.length}}o(e,"getLine");function r(i,s){return t.repeat(" ",s-i.length)+i}o(r,"padStart");function n(i,s){if(s=Object.create(s||null),!i.buffer)return null;s.maxLength||(s.maxLength=79),typeof s.indent!="number"&&(s.indent=1),typeof s.linesBefore!="number"&&(s.linesBefore=3),typeof s.linesAfter!="number"&&(s.linesAfter=2);let a=/\r?\n|\r|\0/g,c=[0],l=[],u,A=-1;for(;u=a.exec(i.buffer);)l.push(u.index),c.push(u.index+u[0].length),i.position<=u.index&&A<0&&(A=c.length-2);A<0&&(A=c.length-1);let h="",g=Math.min(i.line+s.linesAfter,l.length).toString().length,d=s.maxLength-(s.indent+g+3);for(let p=1;p<=s.linesBefore&&!(A-p<0);p++){let y=e(i.buffer,c[A-p],l[A-p],i.position-(c[A]-c[A-p]),d);h=t.repeat(" ",s.indent)+r((i.line-p+1).toString(),g)+" | "+y.str+`
43
43
  `+h}let I=e(i.buffer,c[A],l[A],i.position,d);h+=t.repeat(" ",s.indent)+r((i.line+1).toString(),g)+" | "+I.str+`
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@linkaicli/linkai-cli",
3
- "version": "1.0.21",
3
+ "version": "1.0.23",
4
4
  "publishConfig": {
5
5
  "registry": "https://registry.npmjs.org/",
6
6
  "access": "public"
@@ -42,6 +42,7 @@
42
42
  "templates/**",
43
43
  "cli-manifest.json",
44
44
  "README.md",
45
+ "MANUAL.md",
45
46
  "GUIDE.md",
46
47
  "AGENTS.md",
47
48
  "llms.txt",
@@ -111,6 +112,6 @@
111
112
  },
112
113
  "homepage": "https://bi.linkmore.com",
113
114
  "linkaiUpgrade": {
114
- "minVersion": "1.0.21"
115
+ "minVersion": "1.0.23"
115
116
  }
116
117
  }
@@ -35,4 +35,5 @@ BI 公共知识库是强制执行约束,不是可选参考。没有先读取 S
35
35
  - 数据字典必须先用 `linkai kb-sources` 选择与数据集一致的 source,再按需执行 `kb-search`、`kb-object`、`kb-docs`、`kb-doc`;404/409/503 不得回退本地占位内容。
36
36
  - 数据字典由服务器下发、会不定时更新:**切换租户或重新登录后必须重新读取新租户的数据字典**,不能沿用其它租户或旧的结论;服务端更新过(重新探库或口径文档更新)也要重读。CLI 在 `ds-create` / `ds-update` / `build --execute`(有新数据集)前强制校验,未读或已过期返回 `DICTIONARY_REQUIRED`;`build` 预览与 `sql-check` 会提前提示。
37
37
  - 方案完成后必须完整呈现“<方案名称>方案知识库”:版本、方案地址、方案内容、解决的问题,以及每张业务卡片各自的数据集、数据来源、粒度和取数逻辑/规则。不要输出完整 SQL,也不要展示服务器上传信息。新建方案读取 build 结果的 `planKnowledge.content`;修改方案执行 `linkai plan-knowledge --plan <planId> --mode update --file <知识文件>`。
38
+ - 用户要领谋方案 / 看板的截图时执行 `linkai plan-shot --plan "<方案名称或 id>"`(整页、等全部卡片取完数;同名多个时返回候选让用户选),把返回的 PNG 文件交给用户,并说明没有数据的卡片。
38
39
  - 用户问到浏览器能力或工作原理时,先执行 linkai kb --file knowledge/browser/README.md,以该文为准回答,不补充文中没有的内容。
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: linkai-browser
3
- version: 2026.10.31
3
+ version: 2026.11.07
4
4
  description: 用 LinkAI 操作 Chrome 浏览器:打开页面、读取页面状态、点击输入、截图、查看网络请求,以及管理标签页、标签组和窗口。适用于网页操作、截图验收、浏览器能力相关问题。领谋 BI 报表搭建请用 linkai-report-builder。
5
5
  allowed-tools: Bash(linkai:*), Read, Edit, Write
6
6
  ---
@@ -12,6 +12,19 @@ allowed-tools: Bash(linkai:*), Read, Edit, Write
12
12
 
13
13
  查找命令:`linkai list <关键字>`;查看参数:`linkai browser <命令> --help -f json`;连接有问题时执行 `linkai doctor`。
14
14
 
15
+ ## 领谋分析方案截图(用户说「要 xx 方案 / 看板的截图」)
16
+
17
+ 直接用一条命令,不要自己用 `browser open` + `screenshot` 拼:
18
+
19
+ ```sh
20
+ linkai plan-shot --plan "<方案名称或 id>" -f json
21
+ ```
22
+
23
+ - 按名称找方案(多个同名或只有相近名称时返回 `PLAN_AMBIGUOUS` 和候选,列给用户选,再用 `--plan <planId>`;找不到 `PLAN_NOT_FOUND`)。
24
+ - 自动同步登录、打开方案、等全部卡片(包括需要滚动才加载的)取完数,把整个方案截成一张 PNG,返回 `output`(文件路径)、`cards`、`emptyCards`(没有数据的卡片名)、`failedCards`、`warnings`。
25
+ - 截图期间 LinkAI 自己的 Chrome 窗口会到前台(窗口在后台时 Chrome 不绘制图表),不动用户自己的标签页;截完可以继续用。
26
+ - 把文件交给用户(能展示图片就直接展示),有 `emptyCards` / `failedCards` / `warnings` 时一并说明。方案很大时加 `--timeout 240`;需要更宽加 `--width 1920`。
27
+
15
28
  ## 会话与返回结果
16
29
 
17
30
  页面类命令都要带 `--session <名称>`,同一任务沿用同一个名称,不同名称互相隔离。需要指定 Chrome 用户时加 `--profile <名称>`。
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: linkai-data-workflow
3
- version: 2026.11.01
3
+ version: 2026.11.06
4
4
  description: 处理领谋DI/BI的Excel或CSV源数据导入、预算与实际更新、填报数据维护,以及根据源文件新建或扩展分析方案。用户说“店铺预算导进去”“这月数据更新到经营看板”“这些底表做个分析”“重新上传看看数据对不对”时使用,无须提CLI。纯本地表格整理且未涉及领谋、租户或已有分析方案时不接管。
5
5
  ---
6
6
 
@@ -16,7 +16,7 @@ description: 处理领谋DI/BI的Excel或CSV源数据导入、预算与实际更
16
16
 
17
17
  ## 开始工作
18
18
 
19
- 0. 先开工作流:导入/更新数据 `linkai flow-start --goal "<用户目标>" --type import [--plan <planId>] --model "<你的模型与版本>"`,只读核对用 `--type query`,新建或扩展方案用 `create` / `modify`;完成后 `linkai flow-end --status delivered|abandoned|failed`。`status` 返回 `unfinishedFlows` 时先按其 `next` 续做或结束。
19
+ 0. **第一条命令先开工作流**:导入/更新数据 `linkai flow-start --goal "<用户目标,一句话>" --type import --plan <planId> --model "<你的模型与版本>"`(已有方案必须带 `--plan`),只读核对用 `--type query`,新建或扩展方案用 `create` / `modify`;**结束时一定** `linkai flow-end --status delivered|abandoned|failed`。不开工作流,领谋网页「CLI 记录」里看不出这个任务的目标和方案;中途想起来也要补,补开后此前的命令会一起归入。`status` 返回 `unfinishedFlows` 时先按其 `next` 续做或结束。
20
20
  1. 执行 `linkai status`,核对环境、租户及目标。检查当前 `--help`,不要猜命令参数。未登录时按 `linkai login --help` 引导用户完成授权,不读取登录文件或索要密码。CLI不可用时明确缺少哪项能力,不让客户挑选接入技术。
21
21
  - **租户必须与方案规范一致**:规范写明了目标租户(名称/编码)时,`linkai status` 的 `tenantName`/租户编码必须与之相同。不一致时先按 `linkai login --help` 切换到规范指定的租户(如 `linkai login --tenant <租户名>`),切换后再 `status` 确认;无法切换就停止并告诉用户。**不能因为“当前租户下也能读到目标卡片”就认定租户正确或文档过时**——卡片可以跨租户读取,但填报数据按登录租户写入,租户不对会把数据写进另一家客户名下。CLI 写卡片/填报/导入前会校验目标卡片属于当前租户,返回 `TENANT_MISMATCH` 时按提示切换租户,不要绕过。
22
22
  2. 执行 `linkai kb-sources`,从当前租户可读源中定位候选source。数据字典由服务器下发、会不定时更新:**切换租户或重新登录后必须重新读取新租户的数据字典**,不能沿用其它租户或旧的结论;服务端更新过(重新探库或口径文档更新)也要重读。CLI 在 `ds-create` / `ds-update` / `build --execute`(有新数据集)前强制校验,未读或已过期返回 `DICTIONARY_REQUIRED`;`build` 预览与 `sql-check` 会提前提示。 公共source下也可能有租户文档,按文档scope及目标对象核对,不能仅凭source名称“公共”排除。
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: linkai-report-builder
3
- version: 2026.11.04
3
+ version: 2026.11.07
4
4
  description: 领谋 BI(LMBI,bi.linkmore.com)自动搭建报表:数据集、卡片(74 种图表,含 AG 表格、甘特图、指标卡)、看板(分析方案)、数据报表、筛选器与排序,按搭建报表知识库和数据字典知识库执行;新建看板优先用 linkai build 声明式搭建,完成后交付完整方案知识库。用户要新建或修改领谋 BI 的报表、看板、分析方案、卡片、指标块、数据集、筛选器、表格样式,或提到 bi.linkmore.com 时使用。
5
5
  ---
6
6
 
@@ -12,12 +12,14 @@ description: 领谋 BI(LMBI,bi.linkmore.com)自动搭建报表:数据集
12
12
 
13
13
  > **多会话 / 子代理并行**:要开子代理或多个会话处理同一个方案时,先读 `linkai skills read linkai-collab -f plain`,用任务板分工、写共享对象前认领;收到 `COLLAB_CONFLICT` 不要绕过。
14
14
 
15
- > **工作流(每个业务任务都要)**:接到任务先 `linkai flow-start --goal "<用户目标>" --type <create|modify|import|query> [--plan <planId>] --model "<你的模型与版本>"`;
15
+ > **工作流(每个业务任务都要,第一条命令就做)**:接到任务的第一条命令是 `linkai flow-start --goal "<用户目标,一句话>" --type <create|modify|import|query> [--plan <planId>] --model "<你的模型与版本>"`;
16
+ > **修改已有方案时必须带 `--plan <planId>`**。不开工作流,领谋网页的「CLI 记录」里这个任务没有目标、关联不到方案,看不出在做什么
17
+ > (2026-11-05 正式数据:没开工作流的 93 个任务只有 3 个正常交付,88 个空闲 30 分钟后被自动关闭)。中途才想起来也要补:补开后此前的命令会一起归入。
16
18
  > `--model` 填你(AI)当前使用的具体模型与版本(如 `Claude Opus 5.5`、`gpt-5.6`),中途换了模型就在下一条命令上加全局参数 `--ai-model "<新模型>"`。
17
- > 交付后 `linkai flow-end --status delivered [--plan <planId>] [--knowledge-version vN]`,放弃或失败用 `--status abandoned|failed --note <原因>`。
19
+ > **结束时一定 `flow-end`**:交付后 `linkai flow-end --status delivered [--plan <planId>] [--knowledge-version vN]`,放弃或失败用 `--status abandoned|failed --note <原因>`。
18
20
  > `linkai status` 返回 `unfinishedFlows` 时先按其中的 `next` 处理:继续做就在命令上加 `--flow <id>`,不做了就 `flow-end --flow <id> --status abandoned`。
19
21
 
20
- 所有操作只用 `linkai <命令>`(纯接口,浏览器只用于 `shot`)。动手前读 `knowledge/report-builder/SOP.md`:
22
+ 所有操作只用 `linkai <命令>`(纯接口,浏览器只用于截图 `plan-shot` / `shot`)。动手前读 `knowledge/report-builder/SOP.md`:
21
23
  它规定固定顺序、哪些动作需要用户额外授权、失败与回滚。命令输出统一为 `{"ok":…, "data":…}` 的 JSON。
22
24
 
23
25
  **BI 公共知识库是强制执行约束,不是参考建议。** 未读取 SOP 和当前任务对应篇章前不得搭建;不得凭模型经验自由发明接口、字段角色、图表配置、筛选器结构或操作步骤。知识库不可用、缺失或没有覆盖需求时,停止写操作并把缺口告诉用户。
@@ -43,7 +45,7 @@ description: 领谋 BI(LMBI,bi.linkmore.com)自动搭建报表:数据集
43
45
  4. **不得自由搭建**:公共 BI 知识库没有明确支持的结构、参数或步骤,不得自行猜测写入;先停止并说明缺少哪篇规则或哪项产品能力。
44
46
  5. **SQL 由 AI 编写**:根据用户需求、当前租户数据字典、`ds-tables`/`ds-fields` 和确认后的业务口径编写 SQL 文件。CLI 只校验、保存、编排和验收,不会自行完成 SQL;模版 SQL 仅为格式示例。
45
47
  数据字典由服务器下发、会不定时更新:**切换租户或重新登录后必须重新读取新租户的数据字典**,不能沿用其它租户或旧的结论;服务端更新过(重新探库或口径文档更新)也要重读。CLI 在 `ds-create` / `ds-update` / `build --execute`(有新数据集)前强制校验,未读或已过期返回 `DICTIONARY_REQUIRED`;`build` 预览与 `sql-check` 会提前提示。复制已有方案到另一个租户也一样:先读新租户的数据字典,核对视图、字段与口径是否一致。
46
- 6. **先确认新建或修改**:用户没有明确说明时先询问,不能凭同名方案猜测。新方案 `operation: create`、知识库从 v1 开始;修改原方案必须定位精确 planId,先 `plan-knowledge-get` 读取现有知识,修改完成后同步更新知识库和版本;历史方案无知识库时自动补建 v1。
48
+ 6. **先确认新建或修改**(修改前先 `plan-knowledge-get`:返回 `readOnly: true` 的是领谋下发的**系统分析方案**,只能读不能改,停下来告诉用户「如需按本租户调整,请在领谋网页上把它另存为本租户的分析方案,再对新方案操作」,不要重试、不要建副本):用户没有明确说明时先询问,不能凭同名方案猜测。新方案 `operation: create`、知识库从 v1 开始;修改原方案必须定位精确 planId,先 `plan-knowledge-get` 读取现有知识,修改完成后同步更新知识库和版本;历史方案无知识库时自动补建 v1。
47
49
 
48
50
  ## 登录与租户
49
51
 
@@ -97,7 +99,8 @@ linkai card-sort --card <cardId> --spec "优先级:asc,ID:asc" # 持
97
99
  linkai query --card <cardId> [--page-size 50] [--output rows.json] [--filters filters.json]
98
100
  linkai plan-create --name "名称" | plan-add --plan <planId> --card <cardId> --x 2 --y 0 --w 20 --h 21 | plan-filter --plan <planId> --file filters.json
99
101
  linkai report-publish --card <cardId> --name "名称" | report-card --card <cardId> | reset-filter --page-card <userPageCardId>
100
- linkai shot --url "<报表/看板 URL>" # 截图到 ~/.linkai/artifacts/linkai/shots/
102
+ linkai plan-shot --plan "<方案名称或 id>" # 方案整页截图(等全部卡片取完数),返回 PNG 路径与空卡片
103
+ linkai shot --url "<报表/看板 URL>" # 任意页面首屏截图
101
104
  linkai clean --prefix zz-任务- # 报表→看板→卡片→数据集 按依赖序清理
102
105
  linkai api-get --path <路径> | api --method POST --path <路径> --body body.json # 未封装接口:先查 references/api-catalog.md
103
106
  ```
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: linkai-usage
3
- version: 2026.11.07
3
+ version: 2026.11.08
4
4
  description: LinkAI-CLI 通用用法:查找可用命令、查看参数、理解 JSON 返回与错误码、处理登录与后台服务问题。不确定该用哪个 linkai 命令时使用;领谋 BI 报表搭建请用 linkai-report-builder,网页操作请用 linkai-browser。
5
5
  allowed-tools: Bash(linkai:*), Read
6
6
  ---
@@ -25,6 +25,7 @@ linkai build --help -f json # 查看某个命令的参数
25
25
  - 成功:`{"ok":true,"data":…}`;失败:退出码非 0,`{"ok":false,"error":{"code","message","hint"}}`。日志在 stderr。
26
26
  - 按 `error.code` 和 `hint` 处理问题。常见错误码:
27
27
  - `AUTH_REQUIRED`:未登录或登录失效,请用户执行 `linkai login` 并在领谋网页完成 OAuth 授权;不要索要密码。
28
+ - **工作流**:每个业务任务(搭建、修改、导入)的第一条命令是 `linkai flow-start --goal "<一句话目标>" --type … [--plan <planId>]`,结束时 `linkai flow-end --status delivered|abandoned|failed`;命令返回里提示「尚未开始工作流」时立刻补上。
28
29
  - `status` 返回 `unfinishedFlows`:有未完成或中断的工作流(`reason`:process-killed 进程被强制结束 / build-incomplete 搭建未完成 / outcome-unknown 写操作结果未知 / idle 长时间无活动)。按 `next` 处理:续做加 `--flow <id>`,不做了 `linkai flow-end --flow <id> --status abandoned`;`linkai flow-status` 查看全部。
29
30
  - `DICTIONARY_REQUIRED`:写 SQL 前没有读过当前租户的最新数据字典(切换了租户、重新登录,或服务端更新过数据字典)。按提示执行 `kb-sources`、`kb-search` / `kb-object` / `kb-doc` 核对后再重做,不要沿用旧结论。
30
31
  - `SQL_INVALID`:服务端解析 SQL 失败(字段 / 表不存在或语法错误)。用 `kb-object` / `ds-tables` 核对字段名后改 SQL,`sql-check` 通过前不要执行 `ds-create` / `ds-update`。
@@ -36,7 +37,8 @@ linkai build --help -f json # 查看某个命令的参数
36
37
  - `FILE_NOT_FOUND`:输入文件不存在(相对路径按当前目录解析)。确认文件已写出、路径正确,不要原样重复执行。
37
38
  - `SKILLS_INSTALL_FAILED`:技能目录写不进去(Windows 上常见于目录被占用或无权限)。关闭 AI 工具后重试,或改用 `linkai skills install <工具> --mode rules`。
38
39
  - `CONFIG_INVALID`:卡片或筛选条件违反领谋界面的保存规则(与在界面点保存时的校验一致)。按 message 逐条修正后再保存,规则见 references/card.md「保存前校验」、filters.md §8。
39
- - `READ_ONLY`:系统报表 / 系统方案(资源包下发到本租户)只读,本租户改不了。停下告诉用户到来源租户修改后重新下发,不要换接口反复尝试。
40
+ - `READ_ONLY` / `SYSTEM_PLAN_READONLY`:领谋下发的**系统分析方案** / 系统报表只读,CLI 不能改它的卡片、筛选器、排序、删除,也不能写它的方案知识(可以读)。`plan-knowledge-get` 返回 `readOnly: true` 时也是这种情况。停下告诉用户:如需按本租户调整,请在领谋网页上把它另存为本租户的分析方案,再对新方案操作;不要重试、不要换接口、不要自己建副本。
41
+ - `CLI_FORBIDDEN`:这个接口不允许 LinkAI CLI 调用(领谋平台维护的禁止列表)。把 message 原样告诉用户,不要重试、不要换路径再试;问数用 `linkai ask`,口径补充或纠正请用户在领谋网页上维护。
40
42
  - `BAD_REQUEST`:请求体结构不对(多见于 `api` 透传)。查 `references/api-catalog.md` 或回读一条现有数据照抄结构,优先用已封装的命令。
41
43
  - `KNOWLEDGE_INVALID`:方案知识文件不完整(缺卡片段落、缺 cardId、修改方案缺 changeSummary 等)。按 message 补齐后重新执行 `plan-knowledge`。
42
44
  - `UPGRADE_REQUIRED`:当前版本低于要求的最低版本且自动升级失败,命令没有执行。把 message 和 hint 里的手动升级步骤转告用户,等用户升级完成后再继续,不要尝试绕过。