@linkaicli/linkai-cli 1.0.20 → 1.0.21

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.
@@ -1,12 +1,12 @@
1
- var We=Object.defineProperty;var o=(t,e)=>We(t,"name",{value:e,configurable:!0});import p from"node:fs";import x from"node:path";import ue from"node:os";import j from"node:crypto";import{authorizeTenantSession as He,loginWithOauth as Je,refreshOauthSession as Ce,revokeOauthSession as H}from"./oauth.js";import{validateFillCardPayload as Ve}from"./fill-card.js";import{checkCardPayload as Ne,checkFilterPayload as Oe}from"./card-rules.js";import*as C from"@linkaicli/linkai-cli/oplog";import*as _e from"./collab.js";import $e from"./templates.js";const P="linkai",b="00000000-0000-0000-0000-000000000000",qe="https://bi.linkmore.com/",Qe="https://bi-api.linkmore.com",Ye=/UND_ERR_CONNECT_TIMEOUT|ECONNREFUSED|ENOTFOUND|EAI_AGAIN|Connect Timeout/,Ze=/EPIPE|ECONNRESET|socket hang up|UND_ERR_SOCKET|other side closed/i,Xe=new Set(["GET","HEAD"]);class et extends Error{static{o(this,"NetworkError")}constructor(e,n){super(e,n),this.name="NetworkError"}}async function tt(t,e,n=3){for(let r=1;;r++)try{return await fetch(t,e)}catch(a){if(e?.signal?.aborted)throw a;const s=`${a?.cause?.code||""} ${a?.cause?.message||""} ${a?.message||""}`,l=Ye.test(s)||Xe.has(String(e?.method||"GET").toUpperCase())&&Ze.test(s);if(r>=n||!l)throw new et(`无法连接领谋 BI 服务器,请检查网络后重试(${s.trim().replace(/\s+/g," ").slice(0,120)})`,{cause:a});await new Promise(i=>setTimeout(i,1500*r))}}o(tt,"fetchRetry");function X(){const t=process.env.LINKAI_SITE_CONFIG_DIR||x.join(ue.homedir(),".linkai","sites");return p.mkdirSync(t,{recursive:!0,mode:448}),nt(t),t}o(X,"configDir");let Ae=!1;function nt(t){if(!Ae){Ae=!0;for(const[e,n]of[["lmbi.json",`${P}.json`],["lmbi.session.json",`${P}.session.json`]]){const r=x.join(t,e),a=x.join(t,n);try{p.existsSync(r)&&!p.existsSync(a)&&p.renameSync(r,a)}catch{}}if(!process.env.LINKAI_ARTIFACTS_DIR){const e=x.join(ue.homedir(),".linkai","artifacts","lmbi"),n=rt();try{p.existsSync(e)&&!p.existsSync(n)&&p.renameSync(e,n)}catch{}}}}o(nt,"migrateLegacyFiles");const fe=o(()=>x.join(X(),`${P}.json`),"configFile");function rt(...t){return x.join(process.env.LINKAI_ARTIFACTS_DIR||x.join(ue.homedir(),".linkai","artifacts",P),...t)}o(rt,"artifactsDir");function I(){const t=he();if("username"in t||"password"in t||"tenantConfirmed"in t){const{username:e,password:n,tenantConfirmed:r,...a}=t;return me(a),Le(a)}return Le(t)}o(I,"readConfig");function Le(t){const e=new URL(t.url||qe);if(!["http:","https:"].includes(e.protocol))throw new Error("LMBI url must use http:// or https://");return{...t,url:e.href,origin:e.origin,apiBase:(t.apiBase||Qe).replace(/\/$/,"")}}o(Le,"configFrom");const ee=o(()=>x.join(X(),`${P}.session.json`),"sessionFile"),at="请用户执行 linkai login,在浏览器完成领谋授权";function _(){try{const t=JSON.parse(p.readFileSync(ee(),"utf8"));return t.authMode==="oauth"&&t.token&&t.refreshToken&&t.tenantId?t:null}catch{return null}}o(_,"readSession");const J=o(()=>x.join(X(),`${P}.cache.json`),"shortCacheFile");async function V(t,e,n){const r=_()?.tenantId;if(!r||process.env.LINKAI_NO_CACHE==="1")return n();let a={};try{a=JSON.parse(p.readFileSync(J(),"utf8"))||{}}catch{}a.tenantId!==r&&(a={tenantId:r,entries:{}});const s=a.entries?.[t];if(s&&Date.now()-s.at>=0&&Date.now()-s.at<e)return s.value;const l=await n();try{try{a=JSON.parse(p.readFileSync(J(),"utf8"))||{}}catch{a={}}a.tenantId!==r&&(a={tenantId:r,entries:{}}),a.entries??={},a.entries[t]={at:Date.now(),value:l};const i=`${J()}.${process.pid}.tmp`;p.writeFileSync(i,JSON.stringify(a),{mode:384}),p.renameSync(i,J())}catch{}return l}o(V,"shortCache");function nn(){try{p.rmSync(J(),{force:!0})}catch{}}o(nn,"clearShortCache");function te(t){if(t?.authMode!=="oauth"||!t.token||!t.refreshToken||!t.tenantId||!t.agentId)throw new Error("只允许保存完整的 OAuth 会话");const e=ee(),n=`${e}.${process.pid}.tmp`,r={token:t.token,tenantId:t.tenantId,agentId:t.agentId||"",...t.authMode==="oauth"&&t.refreshToken?{authMode:"oauth",refreshToken:t.refreshToken}:{},savedAt:new Date().toISOString()};return p.writeFileSync(n,JSON.stringify(r),{mode:384}),p.renameSync(n,e),$=r,z=null,G=null,r}o(te,"saveSession");function ot(){try{p.rmSync(ee())}catch{}$=null,z=null,G=null}o(ot,"clearSession");function v(t){try{return JSON.parse(Buffer.from(String(t).split(".")[1],"base64").toString())}catch{return{}}}o(v,"tokenPayload");function q(t){const e=v(t);return e.exp?(e.exp*1e3-Date.now())/36e5:-1}o(q,"tokenHoursLeft");function pe(t){const e=v(t?.token);let n={};try{n=JSON.parse(e.TenantInfo||"[]")[0]||{}}catch{}return{tenantId:t?.tenantId,tenantName:n.tenantName||"",tenantCode:n.tenantCode||"",user:e.UserName||""}}o(pe,"spaceOf");class k extends Error{static{o(this,"LoginRequiredError")}constructor(e){super(`${e}。${at}`),this.name="LoginRequiredError"}}function he(){try{return JSON.parse(p.readFileSync(fe(),"utf8"))||{}}catch{return{}}}o(he,"readConfigFile");function me(t){const e=fe(),n=`${e}.${process.pid}.tmp`;p.writeFileSync(n,JSON.stringify(t,null,2),{mode:384}),p.renameSync(n,e)}o(me,"writeConfigFile");function we(){const{username:t,password:e,tenantId:n,tenantConfirmed:r,...a}=he();if(Object.keys(a).length)me(a);else try{p.rmSync(fe())}catch{}}o(we,"forgetCredentials");function it(){we();const t=he();if(typeof t.oauthAgentId=="string"&&/^[A-Za-z0-9._:-]{8,128}$/.test(t.oauthAgentId))return t.oauthAgentId;const e=j.randomUUID().replace(/-/g,"");return me({...t,oauthAgentId:e}),e}o(it,"oauthAgentId");async function rn({tenant:t,switchAccount:e=!1,timeoutSeconds:n=300,browser:r}={}){const a=_();return Je(I(),{agentId:it(),tenant:t,switchAccount:e,timeoutSeconds:n,browser:r,onAuthorized:o(async s=>{if(e&&a){const i=v(a.token).UserId,u=v(s.token).UserId;if(i&&u&&i===u)throw await H(I(),s).catch(()=>{}),new Error("授权后仍是原领谋账号;请在授权页切换账号后重试")}if(t)try{const i=await De(s),u=String(t).trim(),c=i.filter(f=>f.tenantId===u||f.code===u||f.name===u),d=c.length?c:i.filter(f=>f.name.includes(u));if(d.length!==1||d[0].tenantId!==s.tenantId)throw new Error(`授权的租户不是「${u}」,请在领谋授权页选择目标租户后重试`)}catch(i){throw await H(I(),s).catch(()=>{}),i}we();const l=te(s);return a?.refreshToken&&await H(I(),a).catch(()=>{}),{session:l,tenantName:pe(l).tenantName||l.tenantId}},"onAuthorized")})}o(rn,"oauthLogin");async function an(){const t=_();if(!t?.refreshToken)return!1;try{return await H(I(),t)}catch{return!1}}o(an,"oauthLogout");let $=null,ne=null;async function Fe(t){const e=`${ee()}.refresh.lock`,n=Date.now()+3e4;let r;for(;r===void 0;)try{r=p.openSync(e,"wx",384),p.writeFileSync(r,String(process.pid))}catch(a){if(a.code!=="EEXIST")throw a;try{const s=Number(p.readFileSync(e,"utf8"));if(Number.isInteger(s)&&s>0){let l=!0;try{process.kill(s,0)}catch(i){i.code==="ESRCH"&&(l=!1)}if(!l){p.rmSync(e);continue}}}catch{}if(Date.now()>=n)throw new Error("等待 OAuth 会话操作超时,请稍后重试");await new Promise(s=>setTimeout(s,150))}try{return await t()}finally{p.closeSync(r),p.rmSync(e)}}o(Fe,"withSessionLock");async function st(t){return Fe(async()=>{const e=_();if(!e?.refreshToken)throw new k("OAuth 会话已在本机退出");return e.refreshToken!==t.refreshToken&&q(e.token)>.05?($=e,e):te(await Ce(I(),e))})}o(st,"refreshOauthLocked");async function M({refresh:t=!1}={}){if(we(),$&&!t&&q($.token)>.05)return $;const e=$||_();if(e&&!t&&q(e.token)>.05)return $=e,e;if(e?.authMode==="oauth"&&e.refreshToken)return ne||(ne=(async()=>{try{return await st(e)}catch(n){if(["invalid_grant","invalid_client","oauth_exchange_unknown"].includes(n.code)){const r=_();if(r&&r.refreshToken!==e.refreshToken&&q(r.token)>.05)return $=r,r;throw ot(),new k(n.code==="oauth_exchange_unknown"?"OAuth 续期结果无法确认":"OAuth 授权已失效")}throw n}})().finally(()=>{ne=null})),ne;throw new k(e?"登录已过期":"尚未登录领谋 BI")}o(M,"ensureSession");async function De(t){t=t||await M();const e=v(t.token),n=await Q(I(),t,"GET",`/api/app/account/user-bind-tenant-list/${e.UserId||e.sub||e.userId}`);if(n.code!==200)throw new k(`读取租户列表失败(${n.message||n.code})`);return(n.data||[]).map(r=>({tenantId:r.tenantId,name:r.spaceName||r.tenantName||"",code:r.tenantCode||""})).filter(r=>!lt(r))}o(De,"tenantList");function lt(t){return t.tenantId===b||t.code==="0000"||t.name==="全域租户"||t.name==="全域"}o(lt,"isPlatformTenant");async function on(t,e){if(t?.authMode!=="oauth")throw new k("当前没有 OAuth 授权");return Fe(async()=>{let n=_();if(!n)throw new k("当前没有 OAuth 授权");if(v(n.token).UserId!==v(t.token).UserId)throw new Error("领谋账号已切换,请重新读取当前租户");q(n.token)<=.05&&(n=te(await Ce(I(),n)));const r=await De(n),a=String(e).trim(),s=r.filter(c=>c.tenantId===a||c.code===a||c.name===a),l=s.length?s:r.filter(c=>c.name.includes(a));if(l.length!==1)throw new Error(`租户「${a}」${l.length?"匹配到多个":"不存在"};可选:${r.map(c=>`${c.name}(${c.tenantId})`).join("、")}`);const i=l[0];if(i.tenantId===n.tenantId)return{session:n,tenant:i,switched:!1};const u=await He(I(),n,i.tenantId);return te(u),Y=null,await H(I(),n).catch(()=>{}),{session:u,tenant:i,switched:!0}})}o(on,"switchTenant");let ge="";function sn(t){ge=String(t||"")}o(sn,"setClientVersion");function ve(t){const[e,n=""]=String(t).split("?"),r=n?[...new URLSearchParams(n).keys()]:[];return{path:e,query:r.length?r:void 0}}o(ve,"logPath");function ct(t){if(!t)return null;const e=v(t.token);return{userId:String(e.UserId||e.sub||e.userId||""),tenantId:t.tenantId,tenantCode:pe(t).tenantCode}}o(ct,"identityOf");function B(t,e,n={}){C.emit("guard",{check:t,pass:e,...n})}o(B,"logGuard");async function Q(t,e,n,r,a,{log:s=!0,signal:l}={}){const i=Date.now(),u=a!==void 0?JSON.stringify(a):void 0;s&&C.setIdentity(ct(e));let c;try{c=await tt(t.apiBase+r,{method:n,headers:{"X-LinkAI-Run-Id":C.currentRunId(),...ge?{"X-LinkAI-Version":ge}:{},Authorization:"Bearer "+e.token,Tenantid:e.tenantId,Agentid:e.agentId||"","Content-Type":"application/json"},...u!==void 0?{body:u}:{},...l?{signal:l}:{}})}catch(m){throw s&&C.emit("api.call",{method:n,...ve(r),ok:!1,network:!0,ms:Date.now()-i,reqBytes:u?Buffer.byteLength(u):0,message:String(m.message).slice(0,300)}),m}const d=await c.text();let f;try{f=JSON.parse(d),f.__status=c.status}catch{f={code:c.status,message:`HTTP ${c.status} non-JSON: ${d.slice(0,200)}`,__status:c.status}}if(s){const m=c.status<400&&f.code===200;C.emit("api.call",{method:n,...ve(r),status:c.status,code:f.code??null,ok:m,ms:Date.now()-i,reqBytes:u?Buffer.byteLength(u):0,resBytes:Buffer.byteLength(d),...m?{}:{message:String(f.message||"").slice(0,300)}})}return f}o(Q,"rawFetch");const Pe="/api/app/linkai-oplog/events";C.setTransport(async(t,e)=>{if(!_())return!1;const n=o(r=>r.__status===401||r.code===401||r.__status===406||r.code===406,"expired");try{let r=await M(),a=await Q(I(),r,"POST",Pe,{events:t},{log:!1,signal:e});return n(a)&&(r=await M({refresh:!0}),a=await Q(I(),r,"POST",Pe,{events:t},{log:!1,signal:e})),a.__status<400&&a.code===200?(C.saveServerConfig(a.data||{}),!0):!1}catch{return!1}});const dt=[/data-?source/i,/sql/i,/table-field/i,/table-info/i,/er-table/i,/field-info-by/i,/data-info-by/i,/index-block-data-argument/i,/copy-index-block-data/i,/\/export\/import/i,/task-sync/i,/data-warehouse/i,/authority-variable/i,/\/test\//i,/package/i,/subscribe/i,/distribute/i];function ln(t){let e=String(t);try{for(let r=0;r<3;r++)e=decodeURIComponent(e)}catch{}const n=dt.find(r=>r.test(e)||r.test(String(t)));if(n)throw B("api-blocklist",!1,{path:String(t).split("?")[0],rule:String(n)}),new Error(`Rejected: ${t} touches data sources / SQL / database structure, which this CLI does not allow. Use ds-create / ds-update for datasets; database changes must go through the LinkMore data team.`)}o(ln,"assertApiAllowed");async function N(t,e,n,{raw:r=!1}={}){const a=I();e=String(e).trim(),e.startsWith("/")||(e="/"+e),e.startsWith("/api/")||(e="/api/app"+e);try{Ve(t,e,n)}catch(c){throw B("fill-card-structure",!1,{path:e.split("?")[0],message:c.message.slice(0,300)}),c}try{let c;try{c=Ne(t,e,n)}catch(f){const m=/update-new/i.test(e)&&n?.id&&n.id!==b?n.id:null,w=m&&f.errors?await Ie(m).catch(()=>null):null;if(!w)throw f;c=Ne(t,e,n,{previous:w})}for(const f of c)C.addNotice(`卡片保存:${f}。`,"card-rules");let d;try{d=Oe(t,e,n)}catch(f){const m=n?.userPageSettingId&&Number(n.filterSourceType)!==2?n.userPageSettingId:null,w=m&&f.errors?(await Ft(m).catch(()=>null))?.filters:null;if(!w)throw f;d=Oe(t,e,n,{previous:w})}for(const f of d)C.addNotice(`筛选条件:${f}。`,"filter-rules")}catch(c){throw B(/filter/.test(e)?"filter-rules":"card-rules",!1,{path:e.split("?")[0],message:c.message.slice(0,300)}),new A("CONFIG_INVALID",c.message)}const s=_t(t,e,n);if(s){_e.assertWritable([_e.normalizeObject(`card:${s}`)]);try{await $t(s),B("tenant-ownership",!0,{target:s,path:e.split("?")[0]})}catch(c){throw c instanceof je&&B("tenant-ownership",!1,{target:s,path:e.split("?")[0]}),c}}let l=await M(),i=await Q(a,l,t,e,n);const u=o(c=>c.__status===401||c.code===401||c.__status===406||c.code===406,"expired");if(u(i)){if(!l.refreshToken)throw new k("OAuth 授权已失效");if(l=await M({refresh:!0}),i=await Q(a,l,t,e,n),u(i))throw new k("自动续登后仍被拒绝")}if(delete i.__status,r)return i;if(i.code!==200)throw new ye(t,e,i);return i.data}o(N,"api");class A extends Error{static{o(this,"KindError")}constructor(e,n){super(n),this.name="KindError",this.kind=e}}const ut=[["SQL_INVALID",/cannot be resolved|analyzing error|syntax|语法错误|Unknown column|doesn't exist/i],["IN_USE",/被.{0,40}引用.{0,20}请先删除/],["DUPLICATE_NAME",/名称不能重复|名称已存在|已存在同名/],["READ_ONLY",/系统报表不能更改|系统方案或大屏不能|系统.{0,6}(只读|不能(更改|修改|新增|删除))/],["NOT_FOUND",/已被删除|不存在|Id有误|未获取到该信息|未找到该数据/],["FORBIDDEN",/私有|暂无权限|没有权限/],["BAD_REQUEST",/ModelState is not valid|Cannot deserialize/]];function Re(t){return String(t||"").split(/\s*STACK TRACE:/)[0].replace(/\s*\w*Exception:\s*/g," ").replace(/\s+/g," ").trim()}o(Re,"cleanApiMessage");class ye extends Error{static{o(this,"ApiError")}constructor(e,n,r={}){const a=Re(r.message),s=Re(r.error&&r.error.details||""),l=s&&!a.includes(s)&&!s.includes(a)?`${a} ${s}`:s.length>a.length?s:a;super(`LMBI API error [${e} ${n.split("?")[0]}]: ${l.slice(0,400)}`),this.name="ApiError",this.path=n.split("?")[0],this.status=r.code,this.kind=ut.find(([,i])=>i.test(l))?.[0]||null}}const S=o((t,e)=>N("POST",t,e),"post"),R=o(t=>N("GET",t),"get");let z=null;async function re(){if(z)return z;const t=I();let e=process.env.LM_DATASOURCE||t.dataSourceId||"",n=[];if(!e){if(n=await V("dataSources",30*6e4,async()=>{const a=await R("/index-block/index-block-data-source");return(Array.isArray(a)?a:a.items||[]).map(s=>({id:s.id,name:s.name}))}),!n.length)throw new Error("当前租户没有可用的数据源,请联系领谋开通");e=n[0].id}const r=await V("rootFolders",30*6e4,async()=>{const[a,s,l]=await Promise.all([R("/index-block/folders?type=0"),R("/index-block/folders?type=1"),R("/index-block/folders?type=3")]);return{planFolder:a[0]?.id||b,cardFolder:s[0]?.id||b,dsFolder:l[0]?.id||b}});return z={dataSourceId:e,dataSources:n,...r},z}o(re,"ctx");function ft(t){const e=t.replace(/\/\*[\s\S]*?\*\//g," ").replace(/--[^\n]*/g," ").replace(/'(?:[^'\\]|\\.)*'/g,"''").replace(/`/g,""),n=new Set([...e.matchAll(/(?:\bWITH(?:\s+RECURSIVE)?|,)\s*([A-Za-z_]\w*)\s*(?:\([^()]*\))?\s*AS\s*(?:NOT\s+)?(?:MATERIALIZED\s*)?\(/gi)].map(l=>l[1].toLowerCase())),a="([A-Za-z_][\\w.]*)(?:\\s+(?:AS\\s+)?(?!(?:WHERE|GROUP|ORDER|HAVING|LIMIT|UNION|JOIN|LEFT|RIGHT|INNER|CROSS|FULL|ON)\\b)[A-Za-z_]\\w*)?",s=[];for(const l of e.matchAll(new RegExp(`\\b(?:FROM|JOIN)\\s+${a}((?:\\s*,\\s*${a})*)`,"gi"))){s.push(l[1]);for(const i of(l[2]||"").matchAll(new RegExp(`,\\s*${a}`,"gi")))s.push(i[1])}return[...new Set(s)].filter(l=>!n.has(l.toLowerCase()))}o(ft,"sqlTables");let G=null;async function pt(){if(G)return G;const t=await re(),e=await V(`tables:${t.dataSourceId}`,10*6e4,async()=>[...new Set((await S(`/index-block/get-data-source-table-field/${t.dataSourceId}`)).map(n=>(n.table_name||"").toLowerCase()))]);return G=new Set(e),G}o(pt,"allowedTables");function ht(t){const e=t.split(`
1
+ var Je=Object.defineProperty;var o=(t,e)=>Je(t,"name",{value:e,configurable:!0});import p from"node:fs";import I from"node:path";import fe from"node:os";import j from"node:crypto";import{authorizeTenantSession as Ve,loginWithOauth as qe,refreshOauthSession as Ne,revokeOauthSession as J}from"./oauth.js";import{validateFillCardPayload as Qe}from"./fill-card.js";import{checkCardPayload as Ce,checkFilterPayload as Oe}from"./card-rules.js";import*as C from"@linkaicli/linkai-cli/oplog";import*as _e from"./collab.js";import Ae from"./templates.js";const v="linkai",b="00000000-0000-0000-0000-000000000000",Ye="https://bi.linkmore.com/",Xe="https://bi-api.linkmore.com",Ze=/UND_ERR_CONNECT_TIMEOUT|ECONNREFUSED|ENOTFOUND|EAI_AGAIN|Connect Timeout/,et=/EPIPE|ECONNRESET|socket hang up|UND_ERR_SOCKET|other side closed/i,tt=new Set(["GET","HEAD"]);class nt extends Error{static{o(this,"NetworkError")}constructor(e,n){super(e,n),this.name="NetworkError"}}async function rt(t,e,n=3){for(let r=1;;r++)try{return await fetch(t,e)}catch(a){if(e?.signal?.aborted)throw a;const s=`${a?.cause?.code||""} ${a?.cause?.message||""} ${a?.message||""}`,l=Ze.test(s)||tt.has(String(e?.method||"GET").toUpperCase())&&et.test(s);if(r>=n||!l)throw new nt(`无法连接领谋 BI 服务器,请检查网络后重试(${s.trim().replace(/\s+/g," ").slice(0,120)})`,{cause:a});await new Promise(i=>setTimeout(i,1500*r))}}o(rt,"fetchRetry");function V(){const t=process.env.LINKAI_SITE_CONFIG_DIR||I.join(fe.homedir(),".linkai","sites");return p.mkdirSync(t,{recursive:!0,mode:448}),at(t),t}o(V,"configDir");let $e=!1;function at(t){if(!$e){$e=!0;for(const[e,n]of[["lmbi.json",`${v}.json`],["lmbi.session.json",`${v}.session.json`]]){const r=I.join(t,e),a=I.join(t,n);try{p.existsSync(r)&&!p.existsSync(a)&&p.renameSync(r,a)}catch{}}if(!process.env.LINKAI_ARTIFACTS_DIR){const e=I.join(fe.homedir(),".linkai","artifacts","lmbi"),n=ot();try{p.existsSync(e)&&!p.existsSync(n)&&p.renameSync(e,n)}catch{}}}}o(at,"migrateLegacyFiles");const pe=o(()=>I.join(V(),`${v}.json`),"configFile");function ot(...t){return I.join(process.env.LINKAI_ARTIFACTS_DIR||I.join(fe.homedir(),".linkai","artifacts",v),...t)}o(ot,"artifactsDir");function x(){const t=me();if("username"in t||"password"in t||"tenantConfirmed"in t){const{username:e,password:n,tenantConfirmed:r,...a}=t;return we(a),Le(a)}return Le(t)}o(x,"readConfig");function Le(t){const e=new URL(t.url||Ye);if(!["http:","https:"].includes(e.protocol))throw new Error("LMBI url must use http:// or https://");return{...t,url:e.href,origin:e.origin,apiBase:(t.apiBase||Xe).replace(/\/$/,"")}}o(Le,"configFrom");const ee=o(()=>I.join(V(),`${v}.session.json`),"sessionFile"),it="请用户执行 linkai login,在浏览器完成领谋授权";function A(){try{const t=JSON.parse(p.readFileSync(ee(),"utf8"));return t.authMode==="oauth"&&t.token&&t.refreshToken&&t.tenantId?t:null}catch{return null}}o(A,"readSession");const q=o(()=>I.join(V(),`${v}.cache.json`),"shortCacheFile");async function Q(t,e,n){const r=A()?.tenantId;if(!r||process.env.LINKAI_NO_CACHE==="1")return n();let a={};try{a=JSON.parse(p.readFileSync(q(),"utf8"))||{}}catch{}a.tenantId!==r&&(a={tenantId:r,entries:{}});const s=a.entries?.[t];if(s&&Date.now()-s.at>=0&&Date.now()-s.at<e)return s.value;const l=await n();try{try{a=JSON.parse(p.readFileSync(q(),"utf8"))||{}}catch{a={}}a.tenantId!==r&&(a={tenantId:r,entries:{}}),a.entries??={},a.entries[t]={at:Date.now(),value:l};const i=`${q()}.${process.pid}.tmp`;p.writeFileSync(i,JSON.stringify(a),{mode:384}),p.renameSync(i,q())}catch{}return l}o(Q,"shortCache");function on(){try{p.rmSync(q(),{force:!0})}catch{}}o(on,"clearShortCache");function te(t){if(t?.authMode!=="oauth"||!t.token||!t.refreshToken||!t.tenantId||!t.agentId)throw new Error("只允许保存完整的 OAuth 会话");const e=ee(),n=`${e}.${process.pid}.tmp`,r={token:t.token,tenantId:t.tenantId,agentId:t.agentId||"",...t.authMode==="oauth"&&t.refreshToken?{authMode:"oauth",refreshToken:t.refreshToken}:{},savedAt:new Date().toISOString()};return p.writeFileSync(n,JSON.stringify(r),{mode:384}),p.renameSync(n,e),$=r,z=null,G=null,r}o(te,"saveSession");function st(){try{p.rmSync(ee())}catch{}$=null,z=null,G=null}o(st,"clearSession");function P(t){try{return JSON.parse(Buffer.from(String(t).split(".")[1],"base64").toString())}catch{return{}}}o(P,"tokenPayload");function Y(t){const e=P(t);return e.exp?(e.exp*1e3-Date.now())/36e5:-1}o(Y,"tokenHoursLeft");function he(t){const e=P(t?.token);let n={};try{n=JSON.parse(e.TenantInfo||"[]")[0]||{}}catch{}return{tenantId:t?.tenantId,tenantName:n.tenantName||"",tenantCode:n.tenantCode||"",user:e.UserName||""}}o(he,"spaceOf");class k extends Error{static{o(this,"LoginRequiredError")}constructor(e){super(`${e}。${it}`),this.name="LoginRequiredError"}}function me(){try{return JSON.parse(p.readFileSync(pe(),"utf8"))||{}}catch{return{}}}o(me,"readConfigFile");function we(t){const e=pe(),n=`${e}.${process.pid}.tmp`;p.writeFileSync(n,JSON.stringify(t,null,2),{mode:384}),p.renameSync(n,e)}o(we,"writeConfigFile");function ge(){const{username:t,password:e,tenantId:n,tenantConfirmed:r,...a}=me();if(Object.keys(a).length)we(a);else try{p.rmSync(pe())}catch{}}o(ge,"forgetCredentials");function lt(){ge();const t=me();if(typeof t.oauthAgentId=="string"&&/^[A-Za-z0-9._:-]{8,128}$/.test(t.oauthAgentId))return t.oauthAgentId;const e=j.randomUUID().replace(/-/g,"");return we({...t,oauthAgentId:e}),e}o(lt,"oauthAgentId");async function sn({tenant:t,switchAccount:e=!1,timeoutSeconds:n=300,browser:r}={}){const a=A();return qe(x(),{agentId:lt(),tenant:t,switchAccount:e,timeoutSeconds:n,browser:r,onAuthorized:o(async s=>{if(e&&a){const i=P(a.token).UserId,u=P(s.token).UserId;if(i&&u&&i===u)throw await J(x(),s).catch(()=>{}),new Error("授权后仍是原领谋账号;请在授权页切换账号后重试")}if(t)try{const i=await De(s),u=String(t).trim(),c=i.filter(f=>f.tenantId===u||f.code===u||f.name===u),d=c.length?c:i.filter(f=>f.name.includes(u));if(d.length!==1||d[0].tenantId!==s.tenantId)throw new Error(`授权的租户不是「${u}」,请在领谋授权页选择目标租户后重试`)}catch(i){throw await J(x(),s).catch(()=>{}),i}ge();const l=te(s);return a?.refreshToken&&await J(x(),a).catch(()=>{}),{session:l,tenantName:he(l).tenantName||l.tenantId}},"onAuthorized")})}o(sn,"oauthLogin");async function ln(){const t=A();if(!t?.refreshToken)return!1;try{return await J(x(),t)}catch{return!1}}o(ln,"oauthLogout");let $=null,ne=null;async function Fe(t){const e=`${ee()}.refresh.lock`,n=Date.now()+3e4;let r;for(;r===void 0;)try{r=p.openSync(e,"wx",384),p.writeFileSync(r,String(process.pid))}catch(a){if(a.code!=="EEXIST")throw a;try{const s=Number(p.readFileSync(e,"utf8"));if(Number.isInteger(s)&&s>0){let l=!0;try{process.kill(s,0)}catch(i){i.code==="ESRCH"&&(l=!1)}if(!l){p.rmSync(e);continue}}}catch{}if(Date.now()>=n)throw new Error("等待 OAuth 会话操作超时,请稍后重试");await new Promise(s=>setTimeout(s,150))}try{return await t()}finally{p.closeSync(r),p.rmSync(e)}}o(Fe,"withSessionLock");async function ct(t){return Fe(async()=>{const e=A();if(!e?.refreshToken)throw new k("OAuth 会话已在本机退出");return e.refreshToken!==t.refreshToken&&Y(e.token)>.05?($=e,e):te(await Ne(x(),e))})}o(ct,"refreshOauthLocked");async function M({refresh:t=!1}={}){if(ge(),$&&!t&&Y($.token)>.05)return $;const e=$||A();if(e&&!t&&Y(e.token)>.05)return $=e,e;if(e?.authMode==="oauth"&&e.refreshToken)return ne||(ne=(async()=>{try{return await ct(e)}catch(n){if(["invalid_grant","invalid_client","oauth_exchange_unknown"].includes(n.code)){const r=A();if(r&&r.refreshToken!==e.refreshToken&&Y(r.token)>.05)return $=r,r;throw st(),new k(n.code==="oauth_exchange_unknown"?"OAuth 续期结果无法确认":"OAuth 授权已失效")}throw n}})().finally(()=>{ne=null})),ne;throw new k(e?"登录已过期":"尚未登录领谋 BI")}o(M,"ensureSession");async function De(t){t=t||await M();const e=P(t.token),n=await X(x(),t,"GET",`/api/app/account/user-bind-tenant-list/${e.UserId||e.sub||e.userId}`);if(n.code!==200)throw new k(`读取租户列表失败(${n.message||n.code})`);return(n.data||[]).map(r=>({tenantId:r.tenantId,name:r.spaceName||r.tenantName||"",code:r.tenantCode||""})).filter(r=>!dt(r))}o(De,"tenantList");function dt(t){return t.tenantId===b||t.code==="0000"||t.name==="全域租户"||t.name==="全域"}o(dt,"isPlatformTenant");async function cn(t,e){if(t?.authMode!=="oauth")throw new k("当前没有 OAuth 授权");return Fe(async()=>{let n=A();if(!n)throw new k("当前没有 OAuth 授权");if(P(n.token).UserId!==P(t.token).UserId)throw new Error("领谋账号已切换,请重新读取当前租户");Y(n.token)<=.05&&(n=te(await Ne(x(),n)));const r=await De(n),a=String(e).trim(),s=r.filter(c=>c.tenantId===a||c.code===a||c.name===a),l=s.length?s:r.filter(c=>c.name.includes(a));if(l.length!==1)throw new Error(`租户「${a}」${l.length?"匹配到多个":"不存在"};可选:${r.map(c=>`${c.name}(${c.tenantId})`).join("、")}`);const i=l[0];if(i.tenantId===n.tenantId)return{session:n,tenant:i,switched:!1};const u=await Ve(x(),n,i.tenantId);return te(u),Z=null,await J(x(),n).catch(()=>{}),{session:u,tenant:i,switched:!0}})}o(cn,"switchTenant");let ye="";function dn(t){ye=String(t||"")}o(dn,"setClientVersion");function ve(t){const[e,n=""]=String(t).split("?"),r=n?[...new URLSearchParams(n).keys()]:[];return{path:e,query:r.length?r:void 0}}o(ve,"logPath");function ut(t){if(!t)return null;const e=P(t.token);return{userId:String(e.UserId||e.sub||e.userId||""),tenantId:t.tenantId,tenantCode:he(t).tenantCode}}o(ut,"identityOf");function B(t,e,n={}){C.emit("guard",{check:t,pass:e,...n})}o(B,"logGuard");async function X(t,e,n,r,a,{log:s=!0,signal:l}={}){const i=Date.now(),u=a!==void 0?JSON.stringify(a):void 0;s&&C.setIdentity(ut(e));let c;try{c=await rt(t.apiBase+r,{method:n,headers:{"X-LinkAI-Run-Id":C.currentRunId(),...ye?{"X-LinkAI-Version":ye}:{},Authorization:"Bearer "+e.token,Tenantid:e.tenantId,Agentid:e.agentId||"","Content-Type":"application/json"},...u!==void 0?{body:u}:{},...l?{signal:l}:{}})}catch(m){throw s&&C.emit("api.call",{method:n,...ve(r),ok:!1,network:!0,ms:Date.now()-i,reqBytes:u?Buffer.byteLength(u):0,message:String(m.message).slice(0,300)}),m}const d=await c.text();let f;try{f=JSON.parse(d),f.__status=c.status}catch{f={code:c.status,message:`HTTP ${c.status} non-JSON: ${d.slice(0,200)}`,__status:c.status}}if(s){const m=c.status<400&&f.code===200;C.emit("api.call",{method:n,...ve(r),status:c.status,code:f.code??null,ok:m,ms:Date.now()-i,reqBytes:u?Buffer.byteLength(u):0,resBytes:Buffer.byteLength(d),...m?{}:{message:String(f.message||"").slice(0,300)}})}return f}o(X,"rawFetch");const Pe="/api/app/linkai-oplog/events";C.setTransport(async(t,e)=>{if(!A())return!1;const n=o(r=>r.__status===401||r.code===401||r.__status===406||r.code===406,"expired");try{let r=await M(),a=await X(x(),r,"POST",Pe,{events:t},{log:!1,signal:e});return n(a)&&(r=await M({refresh:!0}),a=await X(x(),r,"POST",Pe,{events:t},{log:!1,signal:e})),a.__status<400&&a.code===200?(C.saveServerConfig(a.data||{}),!0):!1}catch{return!1}});const ft=[/data-?source/i,/sql/i,/table-field/i,/table-info/i,/er-table/i,/field-info-by/i,/data-info-by/i,/index-block-data-argument/i,/copy-index-block-data/i,/\/export\/import/i,/task-sync/i,/data-warehouse/i,/authority-variable/i,/\/test\//i,/package/i,/subscribe/i,/distribute/i];function un(t){let e=String(t);try{for(let r=0;r<3;r++)e=decodeURIComponent(e)}catch{}const n=ft.find(r=>r.test(e)||r.test(String(t)));if(n)throw B("api-blocklist",!1,{path:String(t).split("?")[0],rule:String(n)}),new Error(`Rejected: ${t} touches data sources / SQL / database structure, which this CLI does not allow. Use ds-create / ds-update for datasets; database changes must go through the LinkMore data team.`)}o(un,"assertApiAllowed");async function E(t,e,n,{raw:r=!1}={}){const a=x();e=String(e).trim(),e.startsWith("/")||(e="/"+e),e.startsWith("/api/")||(e="/api/app"+e);try{Qe(t,e,n)}catch(c){throw B("fill-card-structure",!1,{path:e.split("?")[0],message:c.message.slice(0,300)}),c}try{let c;try{c=Ce(t,e,n)}catch(f){const m=/update-new/i.test(e)&&n?.id&&n.id!==b?n.id:null,w=m&&f.errors?await Ie(m).catch(()=>null):null;if(!w)throw f;c=Ce(t,e,n,{previous:w})}for(const f of c)C.addNotice(`卡片保存:${f}。`,"card-rules");let d;try{d=Oe(t,e,n)}catch(f){const m=n?.userPageSettingId&&Number(n.filterSourceType)!==2?n.userPageSettingId:null,w=m&&f.errors?(await Pt(m).catch(()=>null))?.filters:null;if(!w)throw f;d=Oe(t,e,n,{previous:w})}for(const f of d)C.addNotice(`筛选条件:${f}。`,"filter-rules")}catch(c){throw B(/filter/.test(e)?"filter-rules":"card-rules",!1,{path:e.split("?")[0],message:c.message.slice(0,300)}),new O("CONFIG_INVALID",c.message)}const s=Lt(t,e,n);if(s){_e.assertWritable([_e.normalizeObject(`card:${s}`)]);try{await Ft(s),B("tenant-ownership",!0,{target:s,path:e.split("?")[0]})}catch(c){throw c instanceof Be&&B("tenant-ownership",!1,{target:s,path:e.split("?")[0]}),c}}let l=await M(),i=await X(a,l,t,e,n);const u=o(c=>c.__status===401||c.code===401||c.__status===406||c.code===406,"expired");if(u(i)){if(!l.refreshToken)throw new k("OAuth 授权已失效");if(l=await M({refresh:!0}),i=await X(a,l,t,e,n),u(i))throw new k("自动续登后仍被拒绝")}if(delete i.__status,r)return i;if(i.code!==200)throw new re(t,e,i);return i.data}o(E,"api");class O extends Error{static{o(this,"KindError")}constructor(e,n){super(n),this.name="KindError",this.kind=e}}const pt=[["SQL_INVALID",/cannot be resolved|analyzing error|syntax|语法错误|Unknown column|doesn't exist/i],["IN_USE",/被.{0,40}引用.{0,20}请先删除/],["DUPLICATE_NAME",/名称不能重复|名称已存在|已存在同名/],["READ_ONLY",/系统报表不能更改|系统方案或大屏不能|系统.{0,6}(只读|不能(更改|修改|新增|删除))/],["NOT_FOUND",/已被删除|不存在|Id有误|未获取到该信息|未找到该数据/],["FORBIDDEN",/私有|暂无权限|没有权限/],["BAD_REQUEST",/ModelState is not valid|Cannot deserialize/]];function Re(t){return String(t||"").split(/\s*STACK TRACE:/)[0].replace(/\s*\w*Exception:\s*/g," ").replace(/\s+/g," ").trim()}o(Re,"cleanApiMessage");class re extends Error{static{o(this,"ApiError")}constructor(e,n,r={}){const a=Re(r.message),s=Re(r.error&&r.error.details||""),l=s&&!a.includes(s)&&!s.includes(a)?`${a} ${s}`:s.length>a.length?s:a;super(`LMBI API error [${e} ${n.split("?")[0]}]: ${l.slice(0,400)}`),this.name="ApiError",this.path=n.split("?")[0],this.status=r.code,this.kind=pt.find(([,i])=>i.test(l))?.[0]||null}}const S=o((t,e)=>E("POST",t,e),"post"),R=o(t=>E("GET",t),"get");let z=null;async function ae(){if(z)return z;const t=x();let e=process.env.LM_DATASOURCE||t.dataSourceId||"",n=[];if(!e){if(n=await Q("dataSources",30*6e4,async()=>{const a=await R("/index-block/index-block-data-source");return(Array.isArray(a)?a:a.items||[]).map(s=>({id:s.id,name:s.name}))}),!n.length)throw new Error("当前租户没有可用的数据源,请联系领谋开通");e=n[0].id}const r=await Q("rootFolders",30*6e4,async()=>{const[a,s,l]=await Promise.all([R("/index-block/folders?type=0"),R("/index-block/folders?type=1"),R("/index-block/folders?type=3")]);return{planFolder:a[0]?.id||b,cardFolder:s[0]?.id||b,dsFolder:l[0]?.id||b}});return z={dataSourceId:e,dataSources:n,...r},z}o(ae,"ctx");function ht(t){const e=t.replace(/\/\*[\s\S]*?\*\//g," ").replace(/--[^\n]*/g," ").replace(/'(?:[^'\\]|\\.)*'/g,"''").replace(/`/g,""),n=new Set([...e.matchAll(/(?:\bWITH(?:\s+RECURSIVE)?|,)\s*([A-Za-z_]\w*)\s*(?:\([^()]*\))?\s*AS\s*(?:NOT\s+)?(?:MATERIALIZED\s*)?\(/gi)].map(l=>l[1].toLowerCase())),a="([A-Za-z_][\\w.]*)(?:\\s+(?:AS\\s+)?(?!(?:WHERE|GROUP|ORDER|HAVING|LIMIT|UNION|JOIN|LEFT|RIGHT|INNER|CROSS|FULL|ON)\\b)[A-Za-z_]\\w*)?",s=[];for(const l of e.matchAll(new RegExp(`\\b(?:FROM|JOIN)\\s+${a}((?:\\s*,\\s*${a})*)`,"gi"))){s.push(l[1]);for(const i of(l[2]||"").matchAll(new RegExp(`,\\s*${a}`,"gi")))s.push(i[1])}return[...new Set(s)].filter(l=>!n.has(l.toLowerCase()))}o(ht,"sqlTables");let G=null;async function mt(){if(G)return G;const t=await ae(),e=await Q(`tables:${t.dataSourceId}`,10*6e4,async()=>[...new Set((await S(`/index-block/get-data-source-table-field/${t.dataSourceId}`)).map(n=>(n.table_name||"").toLowerCase()))]);return G=new Set(e),G}o(mt,"allowedTables");function wt(t){const e=t.split(`
2
2
  `).filter(l=>l.trim()),n=t.length/Math.max(e.length,1);if(e.length>3&&n<100)return t;const r=[];let a=t.replace(/'(?:[^'\\]|\\.)*'/g,l=>`${r.push(l)-1}`);a=" "+a.replace(/\s+/g," ")+" ";const s=["SELECT","FROM","LEFT JOIN","RIGHT JOIN","INNER JOIN","JOIN","WHERE","GROUP BY","HAVING","ORDER BY","LIMIT","UNION ALL","UNION"];for(const l of s){const i=l.replace(" ","");a=a.replace(new RegExp(`\\s${l.replace(" ","\\s+")}\\s`,"gi"),`
3
3
  ${i} `)}return a=a.replace(/\u0002/g," "),a=a.replace(/\nSELECT ([^\n]*)/g,(l,i)=>{let u="",c=0;for(const d of i)d==="("&&c++,d===")"&&c--,u+=d,d===","&&c===0&&(u+=`
4
4
  `);return`
5
5
  SELECT
6
6
  `+u.trim()}),a=a.replace(/\u0001(\d+)\u0001/g,(l,i)=>r[+i]),a.trim()+`
7
- `}o(ht,"formatSql");function Se(t){const e=t.replace(/'(?:[^'\\]|\\.)*'/g,a=>" ".repeat(a.length)).replace(/\/\*[\s\S]*?\*\//g,a=>" ".repeat(a.length)).replace(/--[^\n]*/g,a=>" ".repeat(a.length)),n=[...e.matchAll(/;/g)].map(a=>a.index);if(!n.length)return t;const r=n[n.length-1];if(e.slice(r+1).trim()||n.length>1)throw new Error('Dataset SQL must be a single SELECT statement (found ";" inside)');return t.slice(0,r)+t.slice(r+1)}o(Se,"stripTrailingSemicolon");const mt=/^(information_schema|mysql|performance_schema|sys)\./i;function wt(t){const e=String(t).replace(/'(?:[^'\\]|\\.)*'/g,"''").replace(/\/\*[\s\S]*?\*\//g," ").replace(/--[^\n]*/g," ").trim();if(!/^(SELECT|WITH)\b/i.test(e))throw new A("SQL_READONLY","Rejected: dataset SQL must be a single read-only SELECT (or WITH … SELECT) query");if(/\bINTO\s+(OUTFILE|DUMPFILE)\b/i.test(e))throw new A("SQL_READONLY","Rejected: SELECT … INTO OUTFILE/DUMPFILE is not allowed");Se(t)}o(wt,"assertReadOnlySql");function gt(t){try{wt(t)}catch(e){throw B("sql-readonly",!1,{sqlSha256:C.sha256(String(t)),chars:String(t).length}),e}}o(gt,"precheckSql");async function yt(t){gt(t),t=Se(t);const e=ft(t),n=t.replace(/'(?:[^'\\]|\\.)*'/g,"''").replace(/`/g,""),r=[...new Set([...e.filter(a=>mt.test(a)),...[...n.matchAll(/\b(information_schema|mysql|performance_schema|sys)\s*\.\s*\w+/gi)].map(a=>a[0])])];if(r.length)throw new A("SQL_TABLE_NOT_ALLOWED",`Rejected: system schema access is not allowed (${r.join(", ")})`);if(e.length){const a=await pt(),s=e.filter(l=>!a.has(l.replace(/^[\w]+\./,"").toLowerCase()));if(s.length)throw new A("SQL_TABLE_NOT_ALLOWED",`Rejected: not in the space's table list (${s.join(", ")}). Only tenant views listed by \`linkai ds-tables\` can be used; to use another view, ask the LinkMore data team to expose it (this CLI cannot operate the database).`)}return t}o(yt,"localCheckSql");async function St(t){t=await yt(t);const e=await re();return S("/index-block/get-field-info-by-data-set-sql",{dataSourceId:e.dataSourceId,sql:t,mode:2})}o(St,"checkSql");function cn(t){const e=String(t||"").replace(/\/\*[\s\S]*?\*\//g," ").replace(/--[^\n]*/g," ").replace(/'(?:[^'\\]|\\.|'')*'/g,"''");return/\boffset\b/i.test(e)||/\blimit\s+[^\s,;)]+\s*,/i.test(e)}o(cn,"hasOffset");async function dn(t){const e=await N("POST","/linkai-ask/query",t,{raw:!0});if(Z(e))throw new A("ASK_UNAVAILABLE","问数服务暂未开通(服务端接口尚未上线)");if(e.code!==200)throw new ye("POST","/api/app/linkai-ask/query",e);return e.data}o(dn,"askQuery");const bt=99,It=2,xt=o(t=>[...t].sort((e,n)=>Number(e.sort_num)-Number(n.sort_num)).map((e,n)=>{const r=["int","decimal"].includes(e.field_type);return{id:j.randomUUID(),tableFieldName:e.field_name,aliasCnName:e.field_name,fieldComment:"",argumentType:r?0:2,calculationMode:r?It:bt,tableFieldType:r?"decimal":e.field_type,tableFieldTypeName:e.type_name,sortNum:n,sqlType:0,isCustomize:!1}}),"fieldColumns");async function un({id:t,name:e,sql:n,remark:r,noFormat:a=!1,current:s=null}){const l=await re(),i=t&&t!==b?s||await Et(t):null;!a&&process.env.LM_NO_FMT!=="1"&&(n=ht(n)),n=Se(n);const u=await St(n);if(!u?.length)throw new A("SQL_INVALID","运行 SQL 没有返回任何字段,无法生成映射字段");const c=Date.now(),d=[{id:j.randomUUID(),tableName:`sql_name_${c}`,tableComment:`sql_comment_${c}`,sortNum:0,fieldColumns:xt(u)}];try{await S("/index-block/get-data-set",{tables:d,joinOns:[],mode:2,dataSourceId:l.dataSourceId,isNewDataSet:!0,filters:[],sortFields:[],sql:n})}catch(f){throw f instanceof ye?new A("SQL_INVALID",`数据集取数验证失败,未保存:${f.message}`):f}return S("/index-block-data-argument/add-or-update",{indexBlockDataId:t||b,isNewDataSet:!0,mode:2,dataSourceId:l.dataSourceId,sql:n,name:e,parentId:i?.parentId||l.dsFolder,tag:i?.tag??"",remark:r??i?.remark??"",isPrivate:i?.isPrivate??!1,joinOns:[],tables:d})}o(un,"dsSave");async function kt({withMeta:t=!1}={}){const n=[];for(let r=1;r<=200;r++){const a=await S("/index-block/get-index-block-data-folder-bussiness-info",{page:{pageIndex:r,pageSize:500}}),s=a.items||[];n.push(...s.map(i=>({name:i.name,dsId:i.objId,folderId:i.id,...t?{editTime:i.editTime||null,addUser:i.addUser||null}:{}})));const l=Number(a.totalCount??a.total??0);if(s.length<500||l&&n.length>=l)break}return n}o(kt,"dsList");const Et=o(t=>R(`/index-block-data-argument/data-set-detail/${t}`),"dsDetail"),Tt=o(t=>R(`/index-block-data-argument/data-set-fields/${t}`),"dsFields");async function fn(t){return(await R(`/index-block/data-set-quoted-infos/${t}`)||[]).map(n=>({name:n.name,cardId:n.indexBlockId}))}o(fn,"dsRefs");function pn(){return Object.entries($e).sort(([t],[e])=>t.localeCompare(e)).map(([t,e])=>({kind:t,name:e._name||"",status:e._disabled?"disabled":e._minimal?"minimal":"full",tip:e._tip||""}))}o(pn,"kinds");const Ct={dim:{prefix:"waxis",axis:"",argumentType:2},meas:{prefix:"yaxis",axis:"y1",argumentType:1},series:{prefix:"waxis_type",axis:"",argumentType:3}};function ae({field:t,col:e,axis:n="",argumentType:r=2,tableFieldType:a="string",title:s,orderByType:l=0,i=0}){return{id:j.randomUUID(),format:"default",axis:n,field:t,title:s??e,sortNum:i,calculationMode:0,seniorMode:0,periods:0,periodType:0,sqlText:"",comment:e,argumentType:r,orderByType:l,tableFieldId:b,tableFieldGroupId:b,periodsWithTableFieldId:b,decimal:0,thousandSeparator:!0,unit:"",tableFieldType:a,isCustomize:!1,isAggregation:!1,isUseSnow:!1,parent:null,nullZero:!1,isVirtual:!1,isFill:!1,uniqueId:j.randomUUID(),isCustomizeFill:!1,isInnerFill:!1,fieldDesc:""}}o(ae,"baseDto");const Nt=o(t=>({fieldExpression:"",aliasName:t.title,sortNum:t.sortNum,argumentType:t.argumentType,orderByType:t.orderByType,sqlText:"",tableFieldId:b,tableFieldName:null,tableFieldComment:t.comment,tableFieldType:t.tableFieldType,isCustomize:!1,fieldExpressionDesc:"",isAggregation:!1,isVirtual:!1,isFill:!1,uniqueId:t.uniqueId,isCustomizeFill:!1,isInnerFill:!1,isNeedTransferJson:!1,jsonFatherId:b,children:null}),"orderField");async function oe({dsId:t,name:e,remark:n,chartParentType:r,chartType:a,dtos:s,chartSetting:l,limitCount:i=500,id:u}){const c=await re(),d=u&&u!==b?await Ie(u):null,f={indexBlockDataId:t,isMulData:!1,indexBlockShowOrderFields:s.map(Nt),filters:[],isFreeFill:!1,id:u||b,indexBlockType:0,name:e,tag:d?.tag??"",remark:n??d?.remark??"",parentId:d?.parentId||c.cardFolder,isPrivate:d?.isPrivate??!1,chartParentType:r,chartType:a,limitCount:i,indexBlockDataSettingDtos:s,chartSetting:JSON.stringify(l)};let m=u;return(!m||m===b)&&(m=await S("/index-block/new",f)),f.id=m,await S("/index-block/update-new",f),{cardId:m,name:e,chartType:`${r}/${a}`}}o(oe,"saveCard");async function be(t){const e=await Tt(t);return{fields:e,meta:Object.fromEntries(e.map(n=>[n.tableFieldComment,n]))}}o(be,"datasetMeta");async function hn({dsId:t,name:e,kind:n,dim:r,meas:a,series:s,id:l}){const i=$e[n];if(!i)throw new Error(`Chart kind "${n}" is not in the v2 chart registry; run: linkai kinds`);if(i._disabled)throw new Error(`Chart kind "${n}" (${i._name}) is disabled: ${i._disabled}`);const{fields:u,meta:c}=await be(t),d=o((E,L,W,h)=>{const g=c[W];if(!g)throw new Error(`Dataset has no column "${W}". Available: ${u.map(F=>F.tableFieldComment).join(" / ")}`);const y=Ct[E];return ae({field:y.prefix+L,col:W,axis:y.axis,argumentType:y.argumentType,tableFieldType:g.tableFieldType,i:h})},"dto"),f=String(r||"").split(",").filter(Boolean),m=String(a||"").split(",").filter(Boolean);if(!m.length)throw new Error("At least one measure column is required");const w=f.map((E,L)=>d("dim",L+1,E,L));return m.forEach((E,L)=>w.push(d("meas",L+1,E,w.length))),s&&w.push(d("series",1,s,w.length)),oe({dsId:t,name:e,chartParentType:i.chartParentType,chartType:i.chartType,dtos:w,chartSetting:i.chartSetting,limitCount:0,id:l})}o(hn,"cardChart");async function mn({dsId:t,name:e,col:n,subtitle:r,color:a="#E8611C",id:s}){const l=[ae({field:"yaxis1",col:n,axis:"y1",argumentType:1,tableFieldType:"decimal",title:r||n})];return oe({dsId:t,name:e,chartParentType:"meter",chartType:"list",dtos:l,chartSetting:{id:"list",parentId:"meter",config:{group:{type:"value",groups:[]},content:{type:"main",position:"left",bgColor:"lucency",icon:{show:!1,img:"",postion:"left"},labelText:{fontSize:12,fill:"rgb(153,153,153)"},valueText:{fontSize:14,fontWeight:800,fill:"rgba(0,0,0,0.85)"},labelTextMain:{fontSize:12,fill:"rgb(153,153,153)",fontStyle:"italic"},valueTextMain:{fontSize:30,fontWeight:800,fill:a},textPadding:0}}},id:s})}o(mn,"cardMeter");async function wn({dsId:t,spec:e,id:n}){const{meta:r}=await be(t),a={},s=[];let l=0;const i=o(h=>{if(a[h])return a[h];const g=r[h];if(!g)throw new Error(`Dataset has no column "${h}". Available: ${Object.keys(r).join(" / ")}`);const y=`waxis${++l}`;return s.push(ae({field:y,col:h,argumentType:g.argumentType,tableFieldType:g.tableFieldType,i:l-1})),a[h]=y,y},"reg"),u=o(()=>j.randomUUID(),"u"),c=o(h=>({fontSize:12,fill:"#1F2329",...h}),"st"),d={b:c({fontSize:15,fontWeight:600,lineHeight:22}),h:c({fontSize:13,fontWeight:600,lineHeight:20}),grey:c({fill:"#8F959E",lineHeight:18}),"":c({lineHeight:18})},f=o(()=>({id:u(),type:"labelValue",field:"",layout:"left",arrangement:"horizontal",fieldStyle:d[""],valueStyle:d[""],showLabel:!1,iconPosition:"left",labelValueGap:4,showColon:!1}),"emptyItem"),m=o((h,g,y)=>({id:"column_"+u(),field:[h],fixed:!1,enableCustomRender:!1,areas:[{id:u(),width:null,rows:[{id:u(),itemGap:10,items:[f()]}]}],name:g,contentAlign:"left",spanRows:!1,hidden:y,isPivot:!1,childPivotFields:[],topValueField:!1}),"plainColumn"),w=[];for(const h of e.columns||[]){if(h.plain){w.push(m(i(h.plain),h.header,!1));continue}const g=[],y=[],F=o(T=>{const[O,D=""]=T.split(":"),ke=i(O);g.push(ke);const de=D==="image",Ee=d[de?"":D]||d[""],Te={id:u(),type:de?"image":"labelValue",field:ke,layout:"left",arrangement:"horizontal",fieldStyle:Ee,valueStyle:Ee,showLabel:!1,iconPosition:"left",labelValueGap:4,showColon:!1};return de&&(Te.imgWidth=h.imgWidth||112),Te},"mkItem");h.image&&y.push({id:u(),width:(h.imgWidth||112)+16,rows:[{id:u(),itemGap:10,items:[F(`${h.image}:image`)]}]}),y.push({id:u(),width:null,rows:(h.rows||[]).map(T=>({id:u(),itemGap:10,items:(Array.isArray(T)?T:[T]).map(F)}))}),w.push({id:"column_"+u(),field:g,fixed:!1,enableCustomRender:!0,areas:y,name:h.header,contentAlign:"left",spanRows:!1,hidden:!1,isPivot:!1,childPivotFields:[],topValueField:!1})}for(const h of e.hidden||[])w.push(m(i(h),h,!0));const E=o(h=>(e[h]||[]).flatMap(g=>{const y=a[g.dep]||i(g.dep),F=a[g.col]||i(g.col),T=[];for(const[O,D]of Object.entries(g.eq||{}))T.push({field:F,color:D,depField:y,operator:"==",value:isNaN(+O)?O:+O});for(const[O,D]of Object.entries(g.gte||{}))T.push({field:F,color:D,depField:y,operator:">=",value:+O});for(const[O,D]of Object.entries(g.ne||{}))T.push({field:F,color:D,depField:y,operator:"!=",value:O});return T}),"conds"),L=e.rowGrey?[{field:"",color:"#F7F8FA",depField:a[e.rowGrey.dep]||i(e.rowGrey.dep),operator:">=",value:e.rowGrey.gte}]:[];e.order&&i(e.order);const W={id:"agtable",parentId:"table",config:{sheetType:"table",showPagination:!0,pagination:{pageSize:e.pageSize||20},fitTableWidth:e.fitTableWidth!==!1,showHeader:!0,headerHeight:36,rowHeight:e.rowHeight||150,imgHeight:e.imgHeight||120,showLineNumber:!1,lineNumberTextAlign:"left",actionColTextAlign:"left",cellTextAlign:"left",cellBorder:{visible:!0,color:"#EDEFF2",width:1},total:{show:!1,labelColumn:"",labelText:"总计",rowtotalType:"all"},agColumnWidths:(e.columns||[]).map(h=>({key:w.find(g=>g.name===h.header).field[0],width:h.width||200})),conditionStyle:{value:{conditions:E("pills")},arrow:{conditions:[]},fontColor:{conditions:E("fontColor")},rowMark:{conditions:L},cellMark:{conditions:[]}},bgMark:{visible:!1,fields:[]},mobileViewFields:[],report:{visible:!1,fillField:{fields:[],fillFieldProperties:[]}},syncReport:{visible:!1,syncConfig:{webHookUrl:"",webHookId:"",relations:[],fields:[]}},wd:{rows:[],values:[],columns:w}}};return oe({dsId:t,name:e.name,remark:e.remark,chartParentType:"table",chartType:"agtable",dtos:s,chartSetting:W,limitCount:e.limitCount||2e3,id:n})}o(wn,"cardTable");async function gn({dsId:t,spec:e,id:n}){const{meta:r}=await be(t),a={},s=[];let l=0;const i=o(c=>{if(!c)return"";if(a[c])return a[c];const d=r[c];if(!d)throw new Error(`Dataset has no column "${c}". Available: ${Object.keys(r).join(" / ")}`);const f=`waxis${++l}`;return s.push(ae({field:f,col:c,argumentType:d.argumentType,tableFieldType:d.tableFieldType,i:l-1})),a[c]=f,f},"reg"),u={id:"gantt",parentId:"table",config:{ganttRenderConfig:{showIcon:!0,style:{image:i(e.image),main:i(e.main),assist:i(e.assist),status:i(e.status),tags:(e.tags||[]).map(i),desc:(e.desc||[]).map(i),list:(e.list||[]).map(i),progress:i(e.progress),taskBar:{textFields:(e.taskBarText||[]).map(i)},fieldMap:{startDate:i(e.start),endDate:i(e.end),plannedStart:i(e.plannedStart),plannedEnd:i(e.plannedEnd)}},dynamic:{main:[],levels:{}}},conditionStyle:{value:{conditions:[]},arrow:{conditions:[]},fontColor:{conditions:[]}},ganttGridTitle:e.title||"任务",ganttGridMinWidth:452,projectRowHeight:e.rowHeight||202,defaultZoomLevel:e.zoom||"day",pagination:{pageSize:e.pageSize||20},showPagination:!1}};return e.order&&i(e.order),oe({dsId:t,name:e.name,remark:e.remark,chartParentType:"table",chartType:"gantt",dtos:s,chartSetting:u,id:n})}o(gn,"cardGantt");async function Ue(){const e=[];for(let n=1;n<=200;n++){const r=await S("/index-block/get-index-block-folder-bussiness-info",{page:{pageIndex:n,pageSize:500}}),a=r.items||[];e.push(...a.map(l=>({name:l.name,cardId:l.objId,folderId:l.id})));const s=Number(r.totalCount??r.total??0);if(a.length<500||s&&e.length>=s)break}return e}o(Ue,"cardList");class je extends Error{static{o(this,"TenantMismatchError")}constructor(e,n){super(`卡片 ${e} 不属于当前租户${n?`「${n}」`:""},已拒绝写入(写进去的数据会落在当前租户名下)`),this.name="TenantMismatchError",this.cardId=e}}const Ot=/\/index-block\/(?:update-new|new-fill-field|add-fill-field|fill-field|add-fill-field-by-s-c-m|new-import-fill-data|import-fill-data)(?:\?|$)/i;let Y=null;function _t(t,e,n){if(String(t).toUpperCase()==="GET"||!Ot.test(String(e))||!n||typeof n!="object"||Array.isArray(n))return"";const r=o((...a)=>{for(const s of a){const l=Object.keys(n).find(i=>i.toLowerCase()===s);if(l&&n[l])return String(n[l])}return""},"pick");return/update-new/i.test(e)?r("id"):r("usedindexblockid")}o(_t,"tenantGuardTarget");async function $t(t){if(!t)return;const e=String(t).toLowerCase(),n=o(async()=>{Y=new Set((await Ue()).map(r=>String(r.cardId).toLowerCase()))},"load");if(Y?Y.has(e)||await n():await n(),!Y.has(e))throw new je(t,pe(await M()).tenantName)}o($t,"assertCardInTenant");const Ie=o(t=>S("/index-block/get-new-index-block-detail",{id:t}),"cardDetail");async function yn(t,e){let n;if(e==="clear")n=[];else if(Array.isArray(e))n=e;else{const s=(await Ie(t)).indexBlockDataSettingDtos||[];n=String(e).split(",").map(l=>l.trim()).filter(Boolean).map(l=>{const[i,u="asc"]=l.split(":"),c=s.find(d=>d.field===i)||s.find(d=>d.comment===i);if(!c)throw new Error(`Card has no field "${i}". Available: ${s.map(d=>`${d.field}=${d.comment}`).join(" / ")}`);if(!["asc","desc"].includes(u.toLowerCase()))throw new Error(`Sort direction must be asc or desc: ${l}`);return{field:c.field,type:1,sort:u.toLowerCase(),sortFields:[]}})}const r=n.map((s,l)=>({type:1,sortFields:[],...s,sortNum:l}));await N("PUT","/page-setting/page-card-index-block-sort-field",{usedIndexBlockId:t,sortField:r});const a=await At({cardId:t,pageSize:1});return{cardId:t,requested:r.map(s=>`${s.field} ${s.type===2?"manual":s.sort}`),sqlHasOrderBy:/\bORDER\s+BY\s+(?:W\.)?(?:waxis|yaxis|waxis_type|order)\w*/i.test(a.sql||""),queryCode:a.code}}o(yn,"cardSort");async function At({cardId:t,pageSize:e=20,filters:n=[]}){const r=await N("POST","/page-setting/get-new-index-block-data",{usedIndexBlockId:t,filters:n,page:{pageIndex:1,pageSize:+e}},{raw:!0}),a=Array.isArray(r.data)?r.data:[];return{code:r.code,message:r.code===200?"":r.message,total:r.dataCount,rows:a.length,columns:a[0]?Object.keys(a[0]):[],sql:r.extendData?.sql||"",data:a}}o(At,"queryCard");async function Lt(){return((await S("/index-block/get-page-setting-folder-bussiness-info",{isOnlySeeSelf:!1,page:{pageIndex:1,pageSize:500}})).items||[]).map(e=>({name:e.name,planId:e.objId,folderId:e.id}))}o(Lt,"planList");const Ft=o((t,e=!0)=>S(`/page-setting/get-new-page-plan/${t}?isEdit=${e}`,{}),"planDetail");async function Sn({planId:t,cardId:e,x:n=0,y:r=0,w:a=24,h:s=20}){const l=await S("/page-setting/new-page-card",{indexBlockId:e,sourceType:0,x:+n,y:+r,width:+a,height:+s,mobileX:0,mobileY:+r,mobileWidth:4,mobileHeight:+s,cardType:1,bindFilterIds:[],userPageSettingId:t});return{pageCardId:l.id,x:l.x,y:l.y,width:l.width,height:l.height}}o(Sn,"planAdd");async function bn(t,e){if(e.isTenantSystem!==!0)throw new Error("Plan filters must be saved with isTenantSystem:true (tenant template); see references/filters.md");await S("/page-setting/batch-add-or-update-filter",e);const n=await S("/page-setting/reset-user-filter",{userPageSettingId:t,filterSourceType:4});return{planId:t,filters:(n||[]).map(r=>r.labelName)}}o(bn,"planFilter");async function Dt(){return((await S("/index-block/get-index-block-report-folder-bussiness-info",{})).items||[]).map(e=>({name:e.name,cardId:e.indexBlockId,reportId:e.objId,folderId:e.id}))}o(Dt,"reportList");const In=o(t=>S("/page-setting/get-new-page-card-report",{indexBlockId:t,isSubWarn:!1}),"reportDetail");async function xn(t){if(!t||!t.startsWith("zz-"))throw new Error('clean only accepts prefixes starting with "zz-"');const e={},n=o(async(r,a)=>{e[r]=a.length,a.length&&await S("/index-block/delete-folder-bussiness",a)},"del");return await n("reports",(await Dt()).filter(r=>r.name.startsWith(t)).map(r=>r.folderId)),await n("plans",(await Lt()).filter(r=>r.name.startsWith(t)).map(r=>r.folderId)),await n("cards",(await Ue()).filter(r=>r.name.startsWith(t)).map(r=>r.folderId)),await n("datasets",(await kt()).filter(r=>r.name.startsWith(t)).map(r=>r.folderId)),{prefix:t,removed:e}}o(xn,"cleanPrefix");function kn(t){return`(() => { const s = ${JSON.stringify({t:t.token,c:t.tenantId,a:t.agentId})};
7
+ `}o(wt,"formatSql");function Se(t){const e=t.replace(/'(?:[^'\\]|\\.)*'/g,a=>" ".repeat(a.length)).replace(/\/\*[\s\S]*?\*\//g,a=>" ".repeat(a.length)).replace(/--[^\n]*/g,a=>" ".repeat(a.length)),n=[...e.matchAll(/;/g)].map(a=>a.index);if(!n.length)return t;const r=n[n.length-1];if(e.slice(r+1).trim()||n.length>1)throw new Error('Dataset SQL must be a single SELECT statement (found ";" inside)');return t.slice(0,r)+t.slice(r+1)}o(Se,"stripTrailingSemicolon");const gt=/^(information_schema|mysql|performance_schema|sys)\./i;function yt(t){const e=String(t).replace(/'(?:[^'\\]|\\.)*'/g,"''").replace(/\/\*[\s\S]*?\*\//g," ").replace(/--[^\n]*/g," ").trim();if(!/^(SELECT|WITH)\b/i.test(e))throw new O("SQL_READONLY","Rejected: dataset SQL must be a single read-only SELECT (or WITH … SELECT) query");if(/\bINTO\s+(OUTFILE|DUMPFILE)\b/i.test(e))throw new O("SQL_READONLY","Rejected: SELECT … INTO OUTFILE/DUMPFILE is not allowed");Se(t)}o(yt,"assertReadOnlySql");function St(t){try{yt(t)}catch(e){throw B("sql-readonly",!1,{sqlSha256:C.sha256(String(t)),chars:String(t).length}),e}}o(St,"precheckSql");async function bt(t){St(t),t=Se(t);const e=ht(t),n=t.replace(/'(?:[^'\\]|\\.)*'/g,"''").replace(/`/g,""),r=[...new Set([...e.filter(a=>gt.test(a)),...[...n.matchAll(/\b(information_schema|mysql|performance_schema|sys)\s*\.\s*\w+/gi)].map(a=>a[0])])];if(r.length)throw new O("SQL_TABLE_NOT_ALLOWED",`Rejected: system schema access is not allowed (${r.join(", ")})`);if(e.length){const a=await mt(),s=e.filter(l=>!a.has(l.replace(/^[\w]+\./,"").toLowerCase()));if(s.length)throw new O("SQL_TABLE_NOT_ALLOWED",`Rejected: not in the space's table list (${s.join(", ")}). Only tenant views listed by \`linkai ds-tables\` can be used; to use another view, ask the LinkMore data team to expose it (this CLI cannot operate the database).`)}return t}o(bt,"localCheckSql");async function It(t){t=await bt(t);const e=await ae();return S("/index-block/get-field-info-by-data-set-sql",{dataSourceId:e.dataSourceId,sql:t,mode:2})}o(It,"checkSql");function fn(t){const e=String(t||"").replace(/\/\*[\s\S]*?\*\//g," ").replace(/--[^\n]*/g," ").replace(/'(?:[^'\\]|\\.|'')*'/g,"''");return/\boffset\b/i.test(e)||/\blimit\s+[^\s,;)]+\s*,/i.test(e)}o(fn,"hasOffset");const xt=2*36e5,Ue=o(()=>I.join(V(),`${v}.ask-xiaoman.json`),"xiaomanFile");function je(){try{const t=JSON.parse(p.readFileSync(Ue(),"utf8"));return Array.isArray(t)?t.filter(e=>Date.now()-e.at<xt):[]}catch{return[]}}o(je,"readXiaoman");function pn({askId:t,status:e,question:n}){try{const r=[{askId:t,status:e,question:String(n).slice(0,200),at:Date.now()},...je().filter(a=>a.askId!==t)].slice(0,50);p.writeFileSync(Ue(),JSON.stringify(r),{mode:384})}catch{}}o(pn,"recordXiaomanAttempt");function hn(t){return je().find(e=>e.askId===t)??null}o(hn,"xiaomanAttempt");async function mn(t){const e=await E("POST","/linkai-ask/ask",t,{raw:!0});if(K(e))throw new O("ASK_UNAVAILABLE","问数服务暂未开通(服务端接口尚未上线)");if(e.code!==200)throw new re("POST","/api/app/linkai-ask/ask",e);return e.data}o(mn,"askQuestion");async function wn(t){const e=await E("POST","/linkai-ask/query",t,{raw:!0});if(K(e))throw new O("ASK_UNAVAILABLE","问数服务暂未开通(服务端接口尚未上线)");if(e.code!==200)throw new re("POST","/api/app/linkai-ask/query",e);return e.data}o(wn,"askQuery");const kt=99,Et=2,Tt=o(t=>[...t].sort((e,n)=>Number(e.sort_num)-Number(n.sort_num)).map((e,n)=>{const r=["int","decimal"].includes(e.field_type);return{id:j.randomUUID(),tableFieldName:e.field_name,aliasCnName:e.field_name,fieldComment:"",argumentType:r?0:2,calculationMode:r?Et:kt,tableFieldType:r?"decimal":e.field_type,tableFieldTypeName:e.type_name,sortNum:n,sqlType:0,isCustomize:!1}}),"fieldColumns");async function gn({id:t,name:e,sql:n,remark:r,noFormat:a=!1,current:s=null}){const l=await ae(),i=t&&t!==b?s||await Ct(t):null;!a&&process.env.LM_NO_FMT!=="1"&&(n=wt(n)),n=Se(n);const u=await It(n);if(!u?.length)throw new O("SQL_INVALID","运行 SQL 没有返回任何字段,无法生成映射字段");const c=Date.now(),d=[{id:j.randomUUID(),tableName:`sql_name_${c}`,tableComment:`sql_comment_${c}`,sortNum:0,fieldColumns:Tt(u)}];try{await S("/index-block/get-data-set",{tables:d,joinOns:[],mode:2,dataSourceId:l.dataSourceId,isNewDataSet:!0,filters:[],sortFields:[],sql:n})}catch(f){throw f instanceof re?new O("SQL_INVALID",`数据集取数验证失败,未保存:${f.message}`):f}return S("/index-block-data-argument/add-or-update",{indexBlockDataId:t||b,isNewDataSet:!0,mode:2,dataSourceId:l.dataSourceId,sql:n,name:e,parentId:i?.parentId||l.dsFolder,tag:i?.tag??"",remark:r??i?.remark??"",isPrivate:i?.isPrivate??!1,joinOns:[],tables:d})}o(gn,"dsSave");async function Nt({withMeta:t=!1}={}){const n=[];for(let r=1;r<=200;r++){const a=await S("/index-block/get-index-block-data-folder-bussiness-info",{page:{pageIndex:r,pageSize:500}}),s=a.items||[];n.push(...s.map(i=>({name:i.name,dsId:i.objId,folderId:i.id,...t?{editTime:i.editTime||null,addUser:i.addUser||null}:{}})));const l=Number(a.totalCount??a.total??0);if(s.length<500||l&&n.length>=l)break}return n}o(Nt,"dsList");const Ct=o(t=>R(`/index-block-data-argument/data-set-detail/${t}`),"dsDetail"),Ot=o(t=>R(`/index-block-data-argument/data-set-fields/${t}`),"dsFields");async function yn(t){return(await R(`/index-block/data-set-quoted-infos/${t}`)||[]).map(n=>({name:n.name,cardId:n.indexBlockId}))}o(yn,"dsRefs");function Sn(){return Object.entries(Ae).sort(([t],[e])=>t.localeCompare(e)).map(([t,e])=>({kind:t,name:e._name||"",status:e._disabled?"disabled":e._minimal?"minimal":"full",tip:e._tip||""}))}o(Sn,"kinds");const _t={dim:{prefix:"waxis",axis:"",argumentType:2},meas:{prefix:"yaxis",axis:"y1",argumentType:1},series:{prefix:"waxis_type",axis:"",argumentType:3}};function oe({field:t,col:e,axis:n="",argumentType:r=2,tableFieldType:a="string",title:s,orderByType:l=0,i=0}){return{id:j.randomUUID(),format:"default",axis:n,field:t,title:s??e,sortNum:i,calculationMode:0,seniorMode:0,periods:0,periodType:0,sqlText:"",comment:e,argumentType:r,orderByType:l,tableFieldId:b,tableFieldGroupId:b,periodsWithTableFieldId:b,decimal:0,thousandSeparator:!0,unit:"",tableFieldType:a,isCustomize:!1,isAggregation:!1,isUseSnow:!1,parent:null,nullZero:!1,isVirtual:!1,isFill:!1,uniqueId:j.randomUUID(),isCustomizeFill:!1,isInnerFill:!1,fieldDesc:""}}o(oe,"baseDto");const At=o(t=>({fieldExpression:"",aliasName:t.title,sortNum:t.sortNum,argumentType:t.argumentType,orderByType:t.orderByType,sqlText:"",tableFieldId:b,tableFieldName:null,tableFieldComment:t.comment,tableFieldType:t.tableFieldType,isCustomize:!1,fieldExpressionDesc:"",isAggregation:!1,isVirtual:!1,isFill:!1,uniqueId:t.uniqueId,isCustomizeFill:!1,isInnerFill:!1,isNeedTransferJson:!1,jsonFatherId:b,children:null}),"orderField");async function ie({dsId:t,name:e,remark:n,chartParentType:r,chartType:a,dtos:s,chartSetting:l,limitCount:i=500,id:u}){const c=await ae(),d=u&&u!==b?await Ie(u):null,f={indexBlockDataId:t,isMulData:!1,indexBlockShowOrderFields:s.map(At),filters:[],isFreeFill:!1,id:u||b,indexBlockType:0,name:e,tag:d?.tag??"",remark:n??d?.remark??"",parentId:d?.parentId||c.cardFolder,isPrivate:d?.isPrivate??!1,chartParentType:r,chartType:a,limitCount:i,indexBlockDataSettingDtos:s,chartSetting:JSON.stringify(l)};let m=u;return(!m||m===b)&&(m=await S("/index-block/new",f)),f.id=m,await S("/index-block/update-new",f),{cardId:m,name:e,chartType:`${r}/${a}`}}o(ie,"saveCard");async function be(t){const e=await Ot(t);return{fields:e,meta:Object.fromEntries(e.map(n=>[n.tableFieldComment,n]))}}o(be,"datasetMeta");async function bn({dsId:t,name:e,kind:n,dim:r,meas:a,series:s,id:l}){const i=Ae[n];if(!i)throw new Error(`Chart kind "${n}" is not in the v2 chart registry; run: linkai kinds`);if(i._disabled)throw new Error(`Chart kind "${n}" (${i._name}) is disabled: ${i._disabled}`);const{fields:u,meta:c}=await be(t),d=o((T,L,H,h)=>{const g=c[H];if(!g)throw new Error(`Dataset has no column "${H}". Available: ${u.map(F=>F.tableFieldComment).join(" / ")}`);const y=_t[T];return oe({field:y.prefix+L,col:H,axis:y.axis,argumentType:y.argumentType,tableFieldType:g.tableFieldType,i:h})},"dto"),f=String(r||"").split(",").filter(Boolean),m=String(a||"").split(",").filter(Boolean);if(!m.length)throw new Error("At least one measure column is required");const w=f.map((T,L)=>d("dim",L+1,T,L));return m.forEach((T,L)=>w.push(d("meas",L+1,T,w.length))),s&&w.push(d("series",1,s,w.length)),ie({dsId:t,name:e,chartParentType:i.chartParentType,chartType:i.chartType,dtos:w,chartSetting:i.chartSetting,limitCount:0,id:l})}o(bn,"cardChart");async function In({dsId:t,name:e,col:n,subtitle:r,color:a="#E8611C",id:s}){const l=[oe({field:"yaxis1",col:n,axis:"y1",argumentType:1,tableFieldType:"decimal",title:r||n})];return ie({dsId:t,name:e,chartParentType:"meter",chartType:"list",dtos:l,chartSetting:{id:"list",parentId:"meter",config:{group:{type:"value",groups:[]},content:{type:"main",position:"left",bgColor:"lucency",icon:{show:!1,img:"",postion:"left"},labelText:{fontSize:12,fill:"rgb(153,153,153)"},valueText:{fontSize:14,fontWeight:800,fill:"rgba(0,0,0,0.85)"},labelTextMain:{fontSize:12,fill:"rgb(153,153,153)",fontStyle:"italic"},valueTextMain:{fontSize:30,fontWeight:800,fill:a},textPadding:0}}},id:s})}o(In,"cardMeter");async function xn({dsId:t,spec:e,id:n}){const{meta:r}=await be(t),a={},s=[];let l=0;const i=o(h=>{if(a[h])return a[h];const g=r[h];if(!g)throw new Error(`Dataset has no column "${h}". Available: ${Object.keys(r).join(" / ")}`);const y=`waxis${++l}`;return s.push(oe({field:y,col:h,argumentType:g.argumentType,tableFieldType:g.tableFieldType,i:l-1})),a[h]=y,y},"reg"),u=o(()=>j.randomUUID(),"u"),c=o(h=>({fontSize:12,fill:"#1F2329",...h}),"st"),d={b:c({fontSize:15,fontWeight:600,lineHeight:22}),h:c({fontSize:13,fontWeight:600,lineHeight:20}),grey:c({fill:"#8F959E",lineHeight:18}),"":c({lineHeight:18})},f=o(()=>({id:u(),type:"labelValue",field:"",layout:"left",arrangement:"horizontal",fieldStyle:d[""],valueStyle:d[""],showLabel:!1,iconPosition:"left",labelValueGap:4,showColon:!1}),"emptyItem"),m=o((h,g,y)=>({id:"column_"+u(),field:[h],fixed:!1,enableCustomRender:!1,areas:[{id:u(),width:null,rows:[{id:u(),itemGap:10,items:[f()]}]}],name:g,contentAlign:"left",spanRows:!1,hidden:y,isPivot:!1,childPivotFields:[],topValueField:!1}),"plainColumn"),w=[];for(const h of e.columns||[]){if(h.plain){w.push(m(i(h.plain),h.header,!1));continue}const g=[],y=[],F=o(N=>{const[_,D=""]=N.split(":"),ke=i(_);g.push(ke);const ue=D==="image",Ee=d[ue?"":D]||d[""],Te={id:u(),type:ue?"image":"labelValue",field:ke,layout:"left",arrangement:"horizontal",fieldStyle:Ee,valueStyle:Ee,showLabel:!1,iconPosition:"left",labelValueGap:4,showColon:!1};return ue&&(Te.imgWidth=h.imgWidth||112),Te},"mkItem");h.image&&y.push({id:u(),width:(h.imgWidth||112)+16,rows:[{id:u(),itemGap:10,items:[F(`${h.image}:image`)]}]}),y.push({id:u(),width:null,rows:(h.rows||[]).map(N=>({id:u(),itemGap:10,items:(Array.isArray(N)?N:[N]).map(F)}))}),w.push({id:"column_"+u(),field:g,fixed:!1,enableCustomRender:!0,areas:y,name:h.header,contentAlign:"left",spanRows:!1,hidden:!1,isPivot:!1,childPivotFields:[],topValueField:!1})}for(const h of e.hidden||[])w.push(m(i(h),h,!0));const T=o(h=>(e[h]||[]).flatMap(g=>{const y=a[g.dep]||i(g.dep),F=a[g.col]||i(g.col),N=[];for(const[_,D]of Object.entries(g.eq||{}))N.push({field:F,color:D,depField:y,operator:"==",value:isNaN(+_)?_:+_});for(const[_,D]of Object.entries(g.gte||{}))N.push({field:F,color:D,depField:y,operator:">=",value:+_});for(const[_,D]of Object.entries(g.ne||{}))N.push({field:F,color:D,depField:y,operator:"!=",value:_});return N}),"conds"),L=e.rowGrey?[{field:"",color:"#F7F8FA",depField:a[e.rowGrey.dep]||i(e.rowGrey.dep),operator:">=",value:e.rowGrey.gte}]:[];e.order&&i(e.order);const H={id:"agtable",parentId:"table",config:{sheetType:"table",showPagination:!0,pagination:{pageSize:e.pageSize||20},fitTableWidth:e.fitTableWidth!==!1,showHeader:!0,headerHeight:36,rowHeight:e.rowHeight||150,imgHeight:e.imgHeight||120,showLineNumber:!1,lineNumberTextAlign:"left",actionColTextAlign:"left",cellTextAlign:"left",cellBorder:{visible:!0,color:"#EDEFF2",width:1},total:{show:!1,labelColumn:"",labelText:"总计",rowtotalType:"all"},agColumnWidths:(e.columns||[]).map(h=>({key:w.find(g=>g.name===h.header).field[0],width:h.width||200})),conditionStyle:{value:{conditions:T("pills")},arrow:{conditions:[]},fontColor:{conditions:T("fontColor")},rowMark:{conditions:L},cellMark:{conditions:[]}},bgMark:{visible:!1,fields:[]},mobileViewFields:[],report:{visible:!1,fillField:{fields:[],fillFieldProperties:[]}},syncReport:{visible:!1,syncConfig:{webHookUrl:"",webHookId:"",relations:[],fields:[]}},wd:{rows:[],values:[],columns:w}}};return ie({dsId:t,name:e.name,remark:e.remark,chartParentType:"table",chartType:"agtable",dtos:s,chartSetting:H,limitCount:e.limitCount||2e3,id:n})}o(xn,"cardTable");async function kn({dsId:t,spec:e,id:n}){const{meta:r}=await be(t),a={},s=[];let l=0;const i=o(c=>{if(!c)return"";if(a[c])return a[c];const d=r[c];if(!d)throw new Error(`Dataset has no column "${c}". Available: ${Object.keys(r).join(" / ")}`);const f=`waxis${++l}`;return s.push(oe({field:f,col:c,argumentType:d.argumentType,tableFieldType:d.tableFieldType,i:l-1})),a[c]=f,f},"reg"),u={id:"gantt",parentId:"table",config:{ganttRenderConfig:{showIcon:!0,style:{image:i(e.image),main:i(e.main),assist:i(e.assist),status:i(e.status),tags:(e.tags||[]).map(i),desc:(e.desc||[]).map(i),list:(e.list||[]).map(i),progress:i(e.progress),taskBar:{textFields:(e.taskBarText||[]).map(i)},fieldMap:{startDate:i(e.start),endDate:i(e.end),plannedStart:i(e.plannedStart),plannedEnd:i(e.plannedEnd)}},dynamic:{main:[],levels:{}}},conditionStyle:{value:{conditions:[]},arrow:{conditions:[]},fontColor:{conditions:[]}},ganttGridTitle:e.title||"任务",ganttGridMinWidth:452,projectRowHeight:e.rowHeight||202,defaultZoomLevel:e.zoom||"day",pagination:{pageSize:e.pageSize||20},showPagination:!1}};return e.order&&i(e.order),ie({dsId:t,name:e.name,remark:e.remark,chartParentType:"table",chartType:"gantt",dtos:s,chartSetting:u,id:n})}o(kn,"cardGantt");async function Me(){const e=[];for(let n=1;n<=200;n++){const r=await S("/index-block/get-index-block-folder-bussiness-info",{page:{pageIndex:n,pageSize:500}}),a=r.items||[];e.push(...a.map(l=>({name:l.name,cardId:l.objId,folderId:l.id})));const s=Number(r.totalCount??r.total??0);if(a.length<500||s&&e.length>=s)break}return e}o(Me,"cardList");class Be extends Error{static{o(this,"TenantMismatchError")}constructor(e,n){super(`卡片 ${e} 不属于当前租户${n?`「${n}」`:""},已拒绝写入(写进去的数据会落在当前租户名下)`),this.name="TenantMismatchError",this.cardId=e}}const $t=/\/index-block\/(?:update-new|new-fill-field|add-fill-field|fill-field|add-fill-field-by-s-c-m|new-import-fill-data|import-fill-data)(?:\?|$)/i;let Z=null;function Lt(t,e,n){if(String(t).toUpperCase()==="GET"||!$t.test(String(e))||!n||typeof n!="object"||Array.isArray(n))return"";const r=o((...a)=>{for(const s of a){const l=Object.keys(n).find(i=>i.toLowerCase()===s);if(l&&n[l])return String(n[l])}return""},"pick");return/update-new/i.test(e)?r("id"):r("usedindexblockid")}o(Lt,"tenantGuardTarget");async function Ft(t){if(!t)return;const e=String(t).toLowerCase(),n=o(async()=>{Z=new Set((await Me()).map(r=>String(r.cardId).toLowerCase()))},"load");if(Z?Z.has(e)||await n():await n(),!Z.has(e))throw new Be(t,he(await M()).tenantName)}o(Ft,"assertCardInTenant");const Ie=o(t=>S("/index-block/get-new-index-block-detail",{id:t}),"cardDetail");async function En(t,e){let n;if(e==="clear")n=[];else if(Array.isArray(e))n=e;else{const s=(await Ie(t)).indexBlockDataSettingDtos||[];n=String(e).split(",").map(l=>l.trim()).filter(Boolean).map(l=>{const[i,u="asc"]=l.split(":"),c=s.find(d=>d.field===i)||s.find(d=>d.comment===i);if(!c)throw new Error(`Card has no field "${i}". Available: ${s.map(d=>`${d.field}=${d.comment}`).join(" / ")}`);if(!["asc","desc"].includes(u.toLowerCase()))throw new Error(`Sort direction must be asc or desc: ${l}`);return{field:c.field,type:1,sort:u.toLowerCase(),sortFields:[]}})}const r=n.map((s,l)=>({type:1,sortFields:[],...s,sortNum:l}));await E("PUT","/page-setting/page-card-index-block-sort-field",{usedIndexBlockId:t,sortField:r});const a=await Dt({cardId:t,pageSize:1});return{cardId:t,requested:r.map(s=>`${s.field} ${s.type===2?"manual":s.sort}`),sqlHasOrderBy:/\bORDER\s+BY\s+(?:W\.)?(?:waxis|yaxis|waxis_type|order)\w*/i.test(a.sql||""),queryCode:a.code}}o(En,"cardSort");async function Dt({cardId:t,pageSize:e=20,filters:n=[]}){const r=await E("POST","/page-setting/get-new-index-block-data",{usedIndexBlockId:t,filters:n,page:{pageIndex:1,pageSize:+e}},{raw:!0}),a=Array.isArray(r.data)?r.data:[];return{code:r.code,message:r.code===200?"":r.message,total:r.dataCount,rows:a.length,columns:a[0]?Object.keys(a[0]):[],sql:r.extendData?.sql||"",data:a}}o(Dt,"queryCard");async function vt(){return((await S("/index-block/get-page-setting-folder-bussiness-info",{isOnlySeeSelf:!1,page:{pageIndex:1,pageSize:500}})).items||[]).map(e=>({name:e.name,planId:e.objId,folderId:e.id}))}o(vt,"planList");const Pt=o((t,e=!0)=>S(`/page-setting/get-new-page-plan/${t}?isEdit=${e}`,{}),"planDetail");async function Tn({planId:t,cardId:e,x:n=0,y:r=0,w:a=24,h:s=20}){const l=await S("/page-setting/new-page-card",{indexBlockId:e,sourceType:0,x:+n,y:+r,width:+a,height:+s,mobileX:0,mobileY:+r,mobileWidth:4,mobileHeight:+s,cardType:1,bindFilterIds:[],userPageSettingId:t});return{pageCardId:l.id,x:l.x,y:l.y,width:l.width,height:l.height}}o(Tn,"planAdd");async function Nn(t,e){if(e.isTenantSystem!==!0)throw new Error("Plan filters must be saved with isTenantSystem:true (tenant template); see references/filters.md");await S("/page-setting/batch-add-or-update-filter",e);const n=await S("/page-setting/reset-user-filter",{userPageSettingId:t,filterSourceType:4});return{planId:t,filters:(n||[]).map(r=>r.labelName)}}o(Nn,"planFilter");async function Rt(){return((await S("/index-block/get-index-block-report-folder-bussiness-info",{})).items||[]).map(e=>({name:e.name,cardId:e.indexBlockId,reportId:e.objId,folderId:e.id}))}o(Rt,"reportList");const Cn=o(t=>S("/page-setting/get-new-page-card-report",{indexBlockId:t,isSubWarn:!1}),"reportDetail");async function On(t){if(!t||!t.startsWith("zz-"))throw new Error('clean only accepts prefixes starting with "zz-"');const e={},n=o(async(r,a)=>{e[r]=a.length,a.length&&await S("/index-block/delete-folder-bussiness",a)},"del");return await n("reports",(await Rt()).filter(r=>r.name.startsWith(t)).map(r=>r.folderId)),await n("plans",(await vt()).filter(r=>r.name.startsWith(t)).map(r=>r.folderId)),await n("cards",(await Me()).filter(r=>r.name.startsWith(t)).map(r=>r.folderId)),await n("datasets",(await Nt()).filter(r=>r.name.startsWith(t)).map(r=>r.folderId)),{prefix:t,removed:e}}o(On,"cleanPrefix");function _n(t){return`(() => { const s = ${JSON.stringify({t:t.token,c:t.tenantId,a:t.agentId})};
8
8
  localStorage['Access-Token'] = JSON.stringify(s.t);
9
9
  localStorage['agentId'] = JSON.stringify(s.a);
10
10
  localStorage['LM_COMPANY'] = JSON.stringify([{ id: s.c, name: '-' }]);
11
11
  sessionStorage['LM_COMPANY_SELECT'] = JSON.stringify(s.c);
12
- return true; })()`}o(kn,"browserSessionScript");function En(t,e){return t=x.resolve(String(t)),p.mkdirSync(x.dirname(t),{recursive:!0}),p.writeFileSync(t,typeof e=="string"?e:JSON.stringify(e,null,1)),t}o(En,"writeJsonFile");function Tn(t){const e=x.resolve(String(t).trim());if(!p.existsSync(e))throw new A("FILE_NOT_FOUND",`文件不存在:${e}(相对路径按当前目录 ${process.cwd()} 解析)`);return p.readFileSync(e,"utf8")}o(Tn,"readInputFile");const xe="/linkai-kb",ie=Object.freeze({"bi-builder":{pathPrefix:"report-builder",scope:"public",enabled:!0,fallback:"bundled"},"data-dictionary":{pathPrefix:"data-dictionary",scope:"tenant",enabled:!0,fallback:"none"},"integration-flow":{pathPrefix:"integration-flow",scope:"tenant",enabled:!1,fallback:"none"},user:{pathPrefix:"user",scope:"user",enabled:!1,fallback:"none"}});class Me extends Error{static{o(this,"KbUnavailableError")}constructor(){super("知识库服务暂不可用"),this.name="KbUnavailableError"}}class vt extends Error{static{o(this,"KbDocumentNotFoundError")}constructor(e,n){super(`知识库文档不存在:${e}/${n}`),this.name="KbDocumentNotFoundError"}}const Z=o(t=>(t.code===404||t.__status===404)&&/non-JSON/.test(String(t.message||"")),"endpointMissing");function Pt(t){const e=String(t||"").trim().replace(/\\/g,"/").replace(/^\.?\//,"").replace(/^knowledge\//,"");if(!e||e.split("/").some(n=>n===".."||n==="")||!e.endsWith(".md"))throw new Error(`知识库路径不合法:${t}(例:knowledge/report-builder/SOP.md)`);return e}o(Pt,"normalizeKbPath");function Rt(t,e){const n=Pt(t);let r=e?String(e):"",a=n;const s=Object.entries(ie).find(([,i])=>n===i.pathPrefix||n.startsWith(`${i.pathPrefix}/`));r||(r=s?.[0]||"bi-builder",s&&(a=n.slice(s[1].pathPrefix.length).replace(/^\//,"")));const l=ie[r];if(!l)throw new Error(`未知知识库:${r}(支持:${Object.keys(ie).join(", ")})`);if(!l.enabled)throw new Error(`知识库 ${r} 已预留,当前版本尚未启用`);if(s&&s[0]!==r)throw new Error(`知识库路径 ${n} 不属于 ${r}`);if((n===l.pathPrefix||n.startsWith(`${l.pathPrefix}/`))&&(a=n.slice(l.pathPrefix.length).replace(/^\//,"")),!a||a.split("/").some(i=>i===".."||i==="")||!a.endsWith(".md"))throw new Error(`知识库路径不合法:${t}`);return{library:r,path:a,canonicalPath:`${l.pathPrefix}/${a}`,...l}}o(Rt,"kbRequest");async function Cn(t="bi-builder"){const e=ie[t];if(!e)throw new Error(`未知知识库:${t}`);if(!e.enabled)throw new Error(`知识库 ${t} 已预留,当前版本尚未启用`);if(t==="data-dictionary")throw new Error("数据字典使用结构化只读接口;请执行 linkai kb-sources,再按需使用 kb-search / kb-catalog / kb-object / kb-docs / kb-doc");const n=await N("GET",`${xe}/catalog?library=${encodeURIComponent(t)}`,void 0,{raw:!0});if(Z(n))throw new Me;if(n.code!==200)throw new Error(`读取知识库目录失败:${n.message||n.code}`);return{...n.data||{docs:[]},library:t,scope:e.scope}}o(Cn,"kbCatalog");async function Nn(t,e){const n=Rt(t,e);if(n.library==="data-dictionary")throw new Error("数据字典不再按 Markdown 文件路径读取;请使用 kb-search / kb-object / kb-docs / kb-doc");const r=await N("GET",`${xe}/doc?library=${encodeURIComponent(n.library)}&path=${encodeURIComponent(n.path)}`,void 0,{raw:!0});if(Z(r))throw new Me;if(r.code===404||r.__status===404)throw new vt(n.library,n.path);if(r.code!==200)throw new Error(r.message||`读取知识库失败:${n.library}/${n.path}`);return{...r.data,library:n.library,scope:n.scope,path:n.canonicalPath}}o(Nn,"kbDoc");const Ut="/ai-kb";class U extends Error{static{o(this,"AiKbError")}constructor(e,n){super(n||`数据字典服务返回错误:${e}`),this.name="AiKbError",this.code=Number(e)||500}}function se(t){const e=new URLSearchParams;for(const[n,r]of Object.entries(t))r!=null&&r!==""&&e.set(n,String(r));return e.toString()}o(se,"query");async function K(t,e,n){let r;try{r=await N(t,`${Ut}/${e}`,n,{raw:!0})}catch(a){throw a instanceof k?a:new U(503,`数据字典服务暂不可用:${a.message||a}`)}if(Z(r))throw new U(503,"数据字典接口尚未部署或当前网关不可用");if(r.code!==200)throw new U(r.code,r.message);return r.data}o(K,"aiKbRequest");const jt=o(()=>K("GET","sources"),"aiKbSources");function On({source:t,keyword:e,pageIndex:n=1,pageSize:r=30}){return K("GET",`catalog?${se({source:t,keyword:e,pageIndex:n,pageSize:r})}`)}o(On,"aiKbCatalog");function _n({source:t,db:e,object:n}){return K("GET",`object?${se({source:t,db:e,object:n})}`)}o(_n,"aiKbObject");function Mt({source:t,category:e,keyword:n,pageIndex:r=1,pageSize:a=30}){return K("GET",`docs?${se({source:t,category:e,keyword:n,pageIndex:r,pageSize:a})}`)}o(Mt,"aiKbDocs");function Bt({source:t,doc:e,offset:n=0,limit:r=2e4,expectedVersion:a}){return K("GET",`doc?${se({source:t,doc:e,offset:n,limit:r,expectedVersion:a})}`)}o(Bt,"aiKbDocPage");async function $n({source:t,doc:e,limit:n=2e4,maxRestarts:r=3}){for(let a=0;a<=r;a++){let s=0,l,i;const u=[];let c=0;try{for(;;){const d=await Bt({source:t,doc:e,offset:s,limit:n,expectedVersion:l});if(c++,i||=d,l||=d.contentVersion,d.contentVersion!==l)throw new U(409,"文档内容版本在分页读取过程中发生变化");if(u.push(d.content||""),d.nextOffset===null||d.nextOffset===void 0)return{...i,content:u.join(""),contentVersion:l,offset:0,nextOffset:null,totalChars:d.totalChars,truncated:!1,pages:c};const f=Number(d.nextOffset);if(!Number.isInteger(f)||f<=s)throw new U(503,"数据字典服务返回了无效的 nextOffset");s=f}}catch(d){if(!(d instanceof U)||d.code!==409||a===r)throw d}}throw new U(409,"文档持续变化,无法完成一致性读取")}o($n,"aiKbDoc");const Be=2*6e4,ze=20*1024*1024;let le=null;const An=o(()=>le,"kbCacheHit"),zt=o(()=>V("dict:sources",Be,async()=>(await jt()).sources||[]),"dictSourcesCached"),Gt=o(t=>V(`dict:docs:${String(t).toLowerCase()}`,Be,async()=>{const e=await Mt({source:t,pageIndex:1,pageSize:20});return{items:(e?.items||[]).map(n=>({updatedAt:n.updatedAt})),total:e?.totalCount??null}}),"dictDocsHead");async function Kt(t){const[e,n]=await Promise.all([zt(),Gt(t)]),r=e.find(s=>String(s.source).toLowerCase()===String(t).toLowerCase());if(!r)return null;const a=(n.items||[]).map(s=>String(s.updatedAt||"")).sort().pop()||"";return[r.lastSuccessAt||"",r.revision??"",a,n.total??""].join("|")}o(Kt,"dictVersion");const Wt=o(t=>x.join(X(),`${P}.kbcache-${String(t).replace(/[^\w-]/g,"")}.json`),"kbCacheFile");async function Ln(t,e,n){const r=_()?.tenantId;let a=null;if(r&&process.env.LINKAI_NO_CACHE!=="1")try{a=await Kt(t)}catch{a=null}if(!a)return le=!1,n();const s=Wt(r),l=`${String(t).toLowerCase()}|${e}`;let i={};try{i=JSON.parse(p.readFileSync(s,"utf8"))||{}}catch{}const u=i[l];if(u&&u.v===a)return le=!0,u.value;const c=await n();le=!1;try{try{i=JSON.parse(p.readFileSync(s,"utf8"))||{}}catch{i={}}for(const[m,w]of Object.entries(i))w?.v!==a&&m.startsWith(`${String(t).toLowerCase()}|`)&&delete i[m];i[l]={v:a,at:Date.now(),value:c};let d=JSON.stringify(i);if(d.length>ze){const m=Object.keys(i).sort((w,E)=>(i[w].at||0)-(i[E].at||0));for(;m.length>1&&d.length>ze;)delete i[m.shift()],d=JSON.stringify(i)}const f=`${s}.${process.pid}.tmp`;p.writeFileSync(f,d,{mode:384}),p.renameSync(f,s)}catch{}return c}o(Ln,"kbCached");const Fn=o(t=>String(t||"").trim().replace(/\s+/g," ").toLowerCase(),"normalizeQuestion");function Dn({source:t,question:e,topTables:n=5,topDocs:r=3}){return K("POST","search",{source:t,question:e,topTables:n,topDocs:r})}o(Dn,"aiKbSearch");const Ge=`${xe}/plan-knowledge`;class ce extends Error{static{o(this,"PlanKnowledgeUnavailableError")}constructor(){super("方案知识库服务暂不可用"),this.name="PlanKnowledgeUnavailableError"}}class Ht extends Error{static{o(this,"PlanKnowledgeConflictError")}constructor(e="方案知识库版本冲突,请重新读取当前版本后再更新"){super(e),this.name="PlanKnowledgeConflictError"}}const Ke=o(t=>Z(t)||t.code===429||Number(t.code)>=500,"planKnowledgeUnavailable");async function vn(t){let e;try{e=await N("GET",`${Ge}?planId=${encodeURIComponent(t)}`,void 0,{raw:!0})}catch(n){throw n instanceof k?n:new ce}if(Ke(e))throw new ce;if(e.code===404)return null;if(e.code!==200)throw new Error(e.message||`读取方案知识库失败:${t}`);return e.data||null}o(vn,"planKnowledgeGet");async function Pn(t){let e;try{e=await N("POST",Ge,t,{raw:!0})}catch(r){throw r instanceof k?r:new ce}if(Ke(e))throw new ce;if(e.code===409)throw new Ht(e.message||void 0);if(e.code!==200)throw new Error(e.message||`保存方案知识库失败:${t.planId}`);const n=e.data||{};if(Number(n.version)!==Number(t.version))throw new Error(`方案知识库版本异常:请求 v${t.version},服务器返回 v${n.version||"?"}`);return n}o(Pn,"planKnowledgePut");export{U as AiKbError,ye as ApiError,Be as DICT_VERSION_TTL_MS,ie as KB_LIBRARIES,vt as KbDocumentNotFoundError,Me as KbUnavailableError,A as KindError,k as LoginRequiredError,et as NetworkError,Ht as PlanKnowledgeConflictError,ce as PlanKnowledgeUnavailableError,P as SITE_ID,je as TenantMismatchError,b as Z,On as aiKbCatalog,$n as aiKbDoc,Bt as aiKbDocPage,Mt as aiKbDocs,_n as aiKbObject,Dn as aiKbSearch,jt as aiKbSources,pt as allowedTables,N as api,rt as artifactsDir,dn as askQuery,ln as assertApiAllowed,$t as assertCardInTenant,wt as assertReadOnlySql,kn as browserSessionScript,hn as cardChart,Ie as cardDetail,gn as cardGantt,Ue as cardList,mn as cardMeter,yn as cardSort,wn as cardTable,St as checkSql,Re as cleanApiMessage,xn as cleanPrefix,ot as clearSession,nn as clearShortCache,X as configDir,fe as configFile,re as ctx,Gt as dictDocsHead,zt as dictSourcesCached,Kt as dictVersion,Et as dsDetail,Tt as dsFields,kt as dsList,fn as dsRefs,un as dsSave,M as ensureSession,xt as fieldColumns,we as forgetCredentials,ht as formatSql,R as get,cn as hasOffset,lt as isPlatformTenant,An as kbCacheHit,Ln as kbCached,Cn as kbCatalog,Nn as kbDoc,Rt as kbRequest,pn as kinds,yt as localCheckSql,B as logGuard,Pt as normalizeKbPath,Fn as normalizeQuestion,rn as oauthLogin,an as oauthLogout,Sn as planAdd,Ft as planDetail,bn as planFilter,vn as planKnowledgeGet,Pn as planKnowledgePut,Lt as planList,S as post,gt as precheckSql,At as queryCard,I as readConfig,Tn as readInputFile,_ as readSession,In as reportDetail,Dt as reportList,te as saveSession,sn as setClientVersion,V as shortCache,pe as spaceOf,ft as sqlTables,Se as stripTrailingSemicolon,on as switchTenant,_t as tenantGuardTarget,De as tenantList,v as tokenPayload,En as writeJsonFile};
12
+ return true; })()`}o(_n,"browserSessionScript");function An(t,e){return t=I.resolve(String(t)),p.mkdirSync(I.dirname(t),{recursive:!0}),p.writeFileSync(t,typeof e=="string"?e:JSON.stringify(e,null,1)),t}o(An,"writeJsonFile");function $n(t){const e=I.resolve(String(t).trim());if(!p.existsSync(e))throw new O("FILE_NOT_FOUND",`文件不存在:${e}(相对路径按当前目录 ${process.cwd()} 解析)`);return p.readFileSync(e,"utf8")}o($n,"readInputFile");const xe="/linkai-kb",se=Object.freeze({"bi-builder":{pathPrefix:"report-builder",scope:"public",enabled:!0,fallback:"bundled"},"data-dictionary":{pathPrefix:"data-dictionary",scope:"tenant",enabled:!0,fallback:"none"},"integration-flow":{pathPrefix:"integration-flow",scope:"tenant",enabled:!1,fallback:"none"},user:{pathPrefix:"user",scope:"user",enabled:!1,fallback:"none"}});class ze extends Error{static{o(this,"KbUnavailableError")}constructor(){super("知识库服务暂不可用"),this.name="KbUnavailableError"}}class Ut extends Error{static{o(this,"KbDocumentNotFoundError")}constructor(e,n){super(`知识库文档不存在:${e}/${n}`),this.name="KbDocumentNotFoundError"}}const K=o(t=>(t.code===404||t.__status===404)&&/non-JSON/.test(String(t.message||"")),"endpointMissing");function jt(t){const e=String(t||"").trim().replace(/\\/g,"/").replace(/^\.?\//,"").replace(/^knowledge\//,"");if(!e||e.split("/").some(n=>n===".."||n==="")||!e.endsWith(".md"))throw new Error(`知识库路径不合法:${t}(例:knowledge/report-builder/SOP.md)`);return e}o(jt,"normalizeKbPath");function Mt(t,e){const n=jt(t);let r=e?String(e):"",a=n;const s=Object.entries(se).find(([,i])=>n===i.pathPrefix||n.startsWith(`${i.pathPrefix}/`));r||(r=s?.[0]||"bi-builder",s&&(a=n.slice(s[1].pathPrefix.length).replace(/^\//,"")));const l=se[r];if(!l)throw new Error(`未知知识库:${r}(支持:${Object.keys(se).join(", ")})`);if(!l.enabled)throw new Error(`知识库 ${r} 已预留,当前版本尚未启用`);if(s&&s[0]!==r)throw new Error(`知识库路径 ${n} 不属于 ${r}`);if((n===l.pathPrefix||n.startsWith(`${l.pathPrefix}/`))&&(a=n.slice(l.pathPrefix.length).replace(/^\//,"")),!a||a.split("/").some(i=>i===".."||i==="")||!a.endsWith(".md"))throw new Error(`知识库路径不合法:${t}`);return{library:r,path:a,canonicalPath:`${l.pathPrefix}/${a}`,...l}}o(Mt,"kbRequest");async function Ln(t="bi-builder"){const e=se[t];if(!e)throw new Error(`未知知识库:${t}`);if(!e.enabled)throw new Error(`知识库 ${t} 已预留,当前版本尚未启用`);if(t==="data-dictionary")throw new Error("数据字典使用结构化只读接口;请执行 linkai kb-sources,再按需使用 kb-search / kb-catalog / kb-object / kb-docs / kb-doc");const n=await E("GET",`${xe}/catalog?library=${encodeURIComponent(t)}`,void 0,{raw:!0});if(K(n))throw new ze;if(n.code!==200)throw new Error(`读取知识库目录失败:${n.message||n.code}`);return{...n.data||{docs:[]},library:t,scope:e.scope}}o(Ln,"kbCatalog");async function Fn(t,e){const n=Mt(t,e);if(n.library==="data-dictionary")throw new Error("数据字典不再按 Markdown 文件路径读取;请使用 kb-search / kb-object / kb-docs / kb-doc");const r=await E("GET",`${xe}/doc?library=${encodeURIComponent(n.library)}&path=${encodeURIComponent(n.path)}`,void 0,{raw:!0});if(K(r))throw new ze;if(r.code===404||r.__status===404)throw new Ut(n.library,n.path);if(r.code!==200)throw new Error(r.message||`读取知识库失败:${n.library}/${n.path}`);return{...r.data,library:n.library,scope:n.scope,path:n.canonicalPath}}o(Fn,"kbDoc");const Bt="/ai-kb";class U extends Error{static{o(this,"AiKbError")}constructor(e,n){super(n||`数据字典服务返回错误:${e}`),this.name="AiKbError",this.code=Number(e)||500}}function le(t){const e=new URLSearchParams;for(const[n,r]of Object.entries(t))r!=null&&r!==""&&e.set(n,String(r));return e.toString()}o(le,"query");async function W(t,e,n){let r;try{r=await E(t,`${Bt}/${e}`,n,{raw:!0})}catch(a){throw a instanceof k?a:new U(503,`数据字典服务暂不可用:${a.message||a}`)}if(K(r))throw new U(503,"数据字典接口尚未部署或当前网关不可用");if(r.code!==200)throw new U(r.code,r.message);return r.data}o(W,"aiKbRequest");const zt=o(()=>W("GET","sources"),"aiKbSources");function Dn({source:t,keyword:e,pageIndex:n=1,pageSize:r=30}){return W("GET",`catalog?${le({source:t,keyword:e,pageIndex:n,pageSize:r})}`)}o(Dn,"aiKbCatalog");function vn({source:t,db:e,object:n}){return W("GET",`object?${le({source:t,db:e,object:n})}`)}o(vn,"aiKbObject");function Gt({source:t,category:e,keyword:n,pageIndex:r=1,pageSize:a=30}){return W("GET",`docs?${le({source:t,category:e,keyword:n,pageIndex:r,pageSize:a})}`)}o(Gt,"aiKbDocs");function Kt({source:t,doc:e,offset:n=0,limit:r=2e4,expectedVersion:a}){return W("GET",`doc?${le({source:t,doc:e,offset:n,limit:r,expectedVersion:a})}`)}o(Kt,"aiKbDocPage");async function Pn({source:t,doc:e,limit:n=2e4,maxRestarts:r=3}){for(let a=0;a<=r;a++){let s=0,l,i;const u=[];let c=0;try{for(;;){const d=await Kt({source:t,doc:e,offset:s,limit:n,expectedVersion:l});if(c++,i||=d,l||=d.contentVersion,d.contentVersion!==l)throw new U(409,"文档内容版本在分页读取过程中发生变化");if(u.push(d.content||""),d.nextOffset===null||d.nextOffset===void 0)return{...i,content:u.join(""),contentVersion:l,offset:0,nextOffset:null,totalChars:d.totalChars,truncated:!1,pages:c};const f=Number(d.nextOffset);if(!Number.isInteger(f)||f<=s)throw new U(503,"数据字典服务返回了无效的 nextOffset");s=f}}catch(d){if(!(d instanceof U)||d.code!==409||a===r)throw d}}throw new U(409,"文档持续变化,无法完成一致性读取")}o(Pn,"aiKbDoc");const Ge=2*6e4,Ke=20*1024*1024;let ce=null;const Rn=o(()=>ce,"kbCacheHit"),Wt=o(()=>Q("dict:sources",Ge,async()=>(await zt()).sources||[]),"dictSourcesCached"),Ht=o(t=>Q(`dict:docs:${String(t).toLowerCase()}`,Ge,async()=>{const e=await Gt({source:t,pageIndex:1,pageSize:20});return{items:(e?.items||[]).map(n=>({updatedAt:n.updatedAt})),total:e?.totalCount??null}}),"dictDocsHead");async function Jt(t){const[e,n]=await Promise.all([Wt(),Ht(t)]),r=e.find(s=>String(s.source).toLowerCase()===String(t).toLowerCase());if(!r)return null;const a=(n.items||[]).map(s=>String(s.updatedAt||"")).sort().pop()||"";return[r.lastSuccessAt||"",r.revision??"",a,n.total??""].join("|")}o(Jt,"dictVersion");const Vt=o(t=>I.join(V(),`${v}.kbcache-${String(t).replace(/[^\w-]/g,"")}.json`),"kbCacheFile");async function Un(t,e,n){const r=A()?.tenantId;let a=null;if(r&&process.env.LINKAI_NO_CACHE!=="1")try{a=await Jt(t)}catch{a=null}if(!a)return ce=!1,n();const s=Vt(r),l=`${String(t).toLowerCase()}|${e}`;let i={};try{i=JSON.parse(p.readFileSync(s,"utf8"))||{}}catch{}const u=i[l];if(u&&u.v===a)return ce=!0,u.value;const c=await n();ce=!1;try{try{i=JSON.parse(p.readFileSync(s,"utf8"))||{}}catch{i={}}for(const[m,w]of Object.entries(i))w?.v!==a&&m.startsWith(`${String(t).toLowerCase()}|`)&&delete i[m];i[l]={v:a,at:Date.now(),value:c};let d=JSON.stringify(i);if(d.length>Ke){const m=Object.keys(i).sort((w,T)=>(i[w].at||0)-(i[T].at||0));for(;m.length>1&&d.length>Ke;)delete i[m.shift()],d=JSON.stringify(i)}const f=`${s}.${process.pid}.tmp`;p.writeFileSync(f,d,{mode:384}),p.renameSync(f,s)}catch{}return c}o(Un,"kbCached");const jn=o(t=>String(t||"").trim().replace(/\s+/g," ").toLowerCase(),"normalizeQuestion");function Mn({source:t,question:e,topTables:n=5,topDocs:r=3}){return W("POST","search",{source:t,question:e,topTables:n,topDocs:r})}o(Mn,"aiKbSearch");const We=`${xe}/plan-knowledge`;class de extends Error{static{o(this,"PlanKnowledgeUnavailableError")}constructor(){super("方案知识库服务暂不可用"),this.name="PlanKnowledgeUnavailableError"}}class qt extends Error{static{o(this,"PlanKnowledgeConflictError")}constructor(e="方案知识库版本冲突,请重新读取当前版本后再更新"){super(e),this.name="PlanKnowledgeConflictError"}}const He=o(t=>K(t)||t.code===429||Number(t.code)>=500,"planKnowledgeUnavailable");async function Bn(t){let e;try{e=await E("GET",`${We}?planId=${encodeURIComponent(t)}`,void 0,{raw:!0})}catch(n){throw n instanceof k?n:new de}if(He(e))throw new de;if(e.code===404)return null;if(e.code!==200)throw new Error(e.message||`读取方案知识库失败:${t}`);return e.data||null}o(Bn,"planKnowledgeGet");async function zn(t){let e;try{e=await E("POST",We,t,{raw:!0})}catch(r){throw r instanceof k?r:new de}if(He(e))throw new de;if(e.code===409)throw new qt(e.message||void 0);if(e.code!==200)throw new Error(e.message||`保存方案知识库失败:${t.planId}`);const n=e.data||{};if(Number(n.version)!==Number(t.version))throw new Error(`方案知识库版本异常:请求 v${t.version},服务器返回 v${n.version||"?"}`);return n}o(zn,"planKnowledgePut");export{U as AiKbError,re as ApiError,Ge as DICT_VERSION_TTL_MS,se as KB_LIBRARIES,Ut as KbDocumentNotFoundError,ze as KbUnavailableError,O as KindError,k as LoginRequiredError,nt as NetworkError,qt as PlanKnowledgeConflictError,de as PlanKnowledgeUnavailableError,v as SITE_ID,Be as TenantMismatchError,b as Z,Dn as aiKbCatalog,Pn as aiKbDoc,Kt as aiKbDocPage,Gt as aiKbDocs,vn as aiKbObject,Mn as aiKbSearch,zt as aiKbSources,mt as allowedTables,E as api,ot as artifactsDir,wn as askQuery,mn as askQuestion,un as assertApiAllowed,Ft as assertCardInTenant,yt as assertReadOnlySql,_n as browserSessionScript,bn as cardChart,Ie as cardDetail,kn as cardGantt,Me as cardList,In as cardMeter,En as cardSort,xn as cardTable,It as checkSql,Re as cleanApiMessage,On as cleanPrefix,st as clearSession,on as clearShortCache,V as configDir,pe as configFile,ae as ctx,Ht as dictDocsHead,Wt as dictSourcesCached,Jt as dictVersion,Ct as dsDetail,Ot as dsFields,Nt as dsList,yn as dsRefs,gn as dsSave,M as ensureSession,Tt as fieldColumns,ge as forgetCredentials,wt as formatSql,R as get,fn as hasOffset,dt as isPlatformTenant,Rn as kbCacheHit,Un as kbCached,Ln as kbCatalog,Fn as kbDoc,Mt as kbRequest,Sn as kinds,bt as localCheckSql,B as logGuard,jt as normalizeKbPath,jn as normalizeQuestion,sn as oauthLogin,ln as oauthLogout,Tn as planAdd,Pt as planDetail,Nn as planFilter,Bn as planKnowledgeGet,zn as planKnowledgePut,vt as planList,S as post,St as precheckSql,Dt as queryCard,x as readConfig,$n as readInputFile,A as readSession,pn as recordXiaomanAttempt,Cn as reportDetail,Rt as reportList,te as saveSession,dn as setClientVersion,Q as shortCache,he as spaceOf,ht as sqlTables,Se as stripTrailingSemicolon,cn as switchTenant,Lt as tenantGuardTarget,De as tenantList,P as tokenPayload,An as writeJsonFile,hn as xiaomanAttempt};