@linkaicli/linkai-cli 1.0.19 → 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.
- package/AGENTS.md +1 -1
- package/GUIDE.md +15 -0
- package/README.md +41 -48
- package/THIRD_PARTY_NOTICES.txt +1 -1
- package/cli-manifest.json +63 -0
- package/clis/linkai/commands.js +3 -3
- package/clis/linkai/core.js +6 -6
- package/clis/linkai/dictionary.js +1 -1
- package/dist/bundle/core.mjs +232 -231
- package/dist/bundle/oplog.mjs +1 -1
- package/dist/bundle/skills.mjs +1 -1
- package/knowledge/report-builder/references/pitfalls.md +6 -7
- package/llms.txt +15 -16
- package/package.json +12 -6
- package/skills/agent-prompt.md +1 -0
- package/skills/linkai-ask/SKILL.md +87 -0
- package/skills/linkai-report-builder/SKILL.md +2 -1
- package/skills/linkai-usage/SKILL.md +4 -1
package/clis/linkai/core.js
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
var ze=Object.defineProperty;var o=(t,e)=>ze(t,"name",{value:e,configurable:!0});import h from"node:fs";import x from"node:path";import le from"node:os";import U from"node:crypto";import{authorizeTenantSession as Ge,loginWithOauth as Ke,refreshOauthSession as Te,revokeOauthSession as H}from"./oauth.js";import{validateFillCardPayload as We}from"./fill-card.js";import{checkCardPayload as ke,checkFilterPayload as Ne}from"./card-rules.js";import*as k from"@linkaicli/linkai-cli/oplog";import*as Oe from"./collab.js";import _e from"./templates.js";const j="linkai",b="00000000-0000-0000-0000-000000000000",He="https://bi.linkmore.com/",Je="https://bi-api.linkmore.com",qe=/UND_ERR_CONNECT_TIMEOUT|ECONNREFUSED|ENOTFOUND|EAI_AGAIN|Connect Timeout/,Ve=/EPIPE|ECONNRESET|socket hang up|UND_ERR_SOCKET|other side closed/i,Qe=new Set(["GET","HEAD"]);class Ye extends Error{static{o(this,"NetworkError")}constructor(e,n){super(e,n),this.name="NetworkError"}}async function Ze(t,e,n=3){for(let r=1;;r++)try{return await fetch(t,e)}catch(a){if(e?.signal?.aborted)throw a;const i=`${a?.cause?.code||""} ${a?.cause?.message||""} ${a?.message||""}`,l=qe.test(i)||Qe.has(String(e?.method||"GET").toUpperCase())&&Ve.test(i);if(r>=n||!l)throw new Ye(`无法连接领谋 BI 服务器,请检查网络后重试(${i.trim().replace(/\s+/g," ").slice(0,120)})`,{cause:a});await new Promise(s=>setTimeout(s,1500*r))}}o(Ze,"fetchRetry");function ce(){const t=process.env.LINKAI_SITE_CONFIG_DIR||x.join(le.homedir(),".linkai","sites");return h.mkdirSync(t,{recursive:!0,mode:448}),Xe(t),t}o(ce,"configDir");let Ce=!1;function Xe(t){if(!Ce){Ce=!0;for(const[e,n]of[["lmbi.json",`${j}.json`],["lmbi.session.json",`${j}.session.json`]]){const r=x.join(t,e),a=x.join(t,n);try{h.existsSync(r)&&!h.existsSync(a)&&h.renameSync(r,a)}catch{}}if(!process.env.LINKAI_ARTIFACTS_DIR){const e=x.join(le.homedir(),".linkai","artifacts","lmbi"),n=et();try{h.existsSync(e)&&!h.existsSync(n)&&h.renameSync(e,n)}catch{}}}}o(Xe,"migrateLegacyFiles");const de=o(()=>x.join(ce(),`${j}.json`),"configFile");function et(...t){return x.join(process.env.LINKAI_ARTIFACTS_DIR||x.join(le.homedir(),".linkai","artifacts",j),...t)}o(et,"artifactsDir");function I(){const t=pe();if("username"in t||"password"in t||"tenantConfirmed"in t){const{username:e,password:n,tenantConfirmed:r,...a}=t;return he(a),$e(a)}return $e(t)}o(I,"readConfig");function $e(t){const e=new URL(t.url||He);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||Je).replace(/\/$/,"")}}o($e,"configFrom");const Y=o(()=>x.join(ce(),`${j}.session.json`),"sessionFile"),tt="请用户执行 linkai login,在浏览器完成领谋授权";function L(){try{const t=JSON.parse(h.readFileSync(Y(),"utf8"));return t.authMode==="oauth"&&t.token&&t.refreshToken&&t.tenantId?t:null}catch{return null}}o(L,"readSession");const J=o(()=>x.join(ce(),`${j}.cache.json`),"shortCacheFile");async function ue(t,e,n){const r=L()?.tenantId;if(!r||process.env.LINKAI_NO_CACHE==="1")return n();let a={};try{a=JSON.parse(h.readFileSync(J(),"utf8"))||{}}catch{}a.tenantId!==r&&(a={tenantId:r,entries:{}});const i=a.entries?.[t];if(i&&Date.now()-i.at>=0&&Date.now()-i.at<e)return i.value;const l=await n();try{try{a=JSON.parse(h.readFileSync(J(),"utf8"))||{}}catch{a={}}a.tenantId!==r&&(a={tenantId:r,entries:{}}),a.entries??={},a.entries[t]={at:Date.now(),value:l};const s=`${J()}.${process.pid}.tmp`;h.writeFileSync(s,JSON.stringify(a),{mode:384}),h.renameSync(s,J())}catch{}return l}o(ue,"shortCache");function Ht(){try{h.rmSync(J(),{force:!0})}catch{}}o(Ht,"clearShortCache");function Z(t){if(t?.authMode!=="oauth"||!t.token||!t.refreshToken||!t.tenantId||!t.agentId)throw new Error("只允许保存完整的 OAuth 会话");const e=Y(),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 h.writeFileSync(n,JSON.stringify(r),{mode:384}),h.renameSync(n,e),O=r,z=null,G=null,r}o(Z,"saveSession");function nt(){try{h.rmSync(Y())}catch{}O=null,z=null,G=null}o(nt,"clearSession");function D(t){try{return JSON.parse(Buffer.from(String(t).split(".")[1],"base64").toString())}catch{return{}}}o(D,"tokenPayload");function q(t){const e=D(t);return e.exp?(e.exp*1e3-Date.now())/36e5:-1}o(q,"tokenHoursLeft");function fe(t){const e=D(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(fe,"spaceOf");class E extends Error{static{o(this,"LoginRequiredError")}constructor(e){super(`${e}。${tt}`),this.name="LoginRequiredError"}}function pe(){try{return JSON.parse(h.readFileSync(de(),"utf8"))||{}}catch{return{}}}o(pe,"readConfigFile");function he(t){const e=de(),n=`${e}.${process.pid}.tmp`;h.writeFileSync(n,JSON.stringify(t,null,2),{mode:384}),h.renameSync(n,e)}o(he,"writeConfigFile");function me(){const{username:t,password:e,tenantId:n,tenantConfirmed:r,...a}=pe();if(Object.keys(a).length)he(a);else try{h.rmSync(de())}catch{}}o(me,"forgetCredentials");function rt(){me();const t=pe();if(typeof t.oauthAgentId=="string"&&/^[A-Za-z0-9._:-]{8,128}$/.test(t.oauthAgentId))return t.oauthAgentId;const e=U.randomUUID().replace(/-/g,"");return he({...t,oauthAgentId:e}),e}o(rt,"oauthAgentId");async function Jt({tenant:t,switchAccount:e=!1,timeoutSeconds:n=300,browser:r}={}){const a=L();return Ke(I(),{agentId:rt(),tenant:t,switchAccount:e,timeoutSeconds:n,browser:r,onAuthorized:o(async i=>{if(e&&a){const s=D(a.token).UserId,u=D(i.token).UserId;if(s&&u&&s===u)throw await H(I(),i).catch(()=>{}),new Error("授权后仍是原领谋账号;请在授权页切换账号后重试")}if(t)try{const s=await Fe(i),u=String(t).trim(),c=s.filter(f=>f.tenantId===u||f.code===u||f.name===u),d=c.length?c:s.filter(f=>f.name.includes(u));if(d.length!==1||d[0].tenantId!==i.tenantId)throw new Error(`授权的租户不是「${u}」,请在领谋授权页选择目标租户后重试`)}catch(s){throw await H(I(),i).catch(()=>{}),s}me();const l=Z(i);return a?.refreshToken&&await H(I(),a).catch(()=>{}),{session:l,tenantName:fe(l).tenantName||l.tenantId}},"onAuthorized")})}o(Jt,"oauthLogin");async function qt(){const t=L();if(!t?.refreshToken)return!1;try{return await H(I(),t)}catch{return!1}}o(qt,"oauthLogout");let O=null,X=null;async function Ae(t){const e=`${Y()}.refresh.lock`,n=Date.now()+3e4;let r;for(;r===void 0;)try{r=h.openSync(e,"wx",384),h.writeFileSync(r,String(process.pid))}catch(a){if(a.code!=="EEXIST")throw a;try{const i=Number(h.readFileSync(e,"utf8"));if(Number.isInteger(i)&&i>0){let l=!0;try{process.kill(i,0)}catch(s){s.code==="ESRCH"&&(l=!1)}if(!l){h.rmSync(e);continue}}}catch{}if(Date.now()>=n)throw new Error("等待 OAuth 会话操作超时,请稍后重试");await new Promise(i=>setTimeout(i,150))}try{return await t()}finally{h.closeSync(r),h.rmSync(e)}}o(Ae,"withSessionLock");async function at(t){return Ae(async()=>{const e=L();if(!e?.refreshToken)throw new E("OAuth 会话已在本机退出");return e.refreshToken!==t.refreshToken&&q(e.token)>.05?(O=e,e):Z(await Te(I(),e))})}o(at,"refreshOauthLocked");async function M({refresh:t=!1}={}){if(me(),O&&!t&&q(O.token)>.05)return O;const e=O||L();if(e&&!t&&q(e.token)>.05)return O=e,e;if(e?.authMode==="oauth"&&e.refreshToken)return X||(X=(async()=>{try{return await at(e)}catch(n){if(["invalid_grant","invalid_client","oauth_exchange_unknown"].includes(n.code)){const r=L();if(r&&r.refreshToken!==e.refreshToken&&q(r.token)>.05)return O=r,r;throw nt(),new E(n.code==="oauth_exchange_unknown"?"OAuth 续期结果无法确认":"OAuth 授权已失效")}throw n}})().finally(()=>{X=null})),X;throw new E(e?"登录已过期":"尚未登录领谋 BI")}o(M,"ensureSession");async function Fe(t){t=t||await M();const e=D(t.token),n=await V(I(),t,"GET",`/api/app/account/user-bind-tenant-list/${e.UserId||e.sub||e.userId}`);if(n.code!==200)throw new E(`读取租户列表失败(${n.message||n.code})`);return(n.data||[]).map(r=>({tenantId:r.tenantId,name:r.spaceName||r.tenantName||"",code:r.tenantCode||""})).filter(r=>!ot(r))}o(Fe,"tenantList");function ot(t){return t.tenantId===b||t.code==="0000"||t.name==="全域租户"||t.name==="全域"}o(ot,"isPlatformTenant");async function Vt(t,e){if(t?.authMode!=="oauth")throw new E("当前没有 OAuth 授权");return Ae(async()=>{let n=L();if(!n)throw new E("当前没有 OAuth 授权");if(D(n.token).UserId!==D(t.token).UserId)throw new Error("领谋账号已切换,请重新读取当前租户");q(n.token)<=.05&&(n=Z(await Te(I(),n)));const r=await Fe(n),a=String(e).trim(),i=r.filter(c=>c.tenantId===a||c.code===a||c.name===a),l=i.length?i: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 s=l[0];if(s.tenantId===n.tenantId)return{session:n,tenant:s,switched:!1};const u=await Ge(I(),n,s.tenantId);return Z(u),Q=null,await H(I(),n).catch(()=>{}),{session:u,tenant:s,switched:!0}})}o(Vt,"switchTenant");let we="";function Qt(t){we=String(t||"")}o(Qt,"setClientVersion");function Le(t){const[e,n=""]=String(t).split("?"),r=n?[...new URLSearchParams(n).keys()]:[];return{path:e,query:r.length?r:void 0}}o(Le,"logPath");function it(t){if(!t)return null;const e=D(t.token);return{userId:String(e.UserId||e.sub||e.userId||""),tenantId:t.tenantId,tenantCode:fe(t).tenantCode}}o(it,"identityOf");function B(t,e,n={}){k.emit("guard",{check:t,pass:e,...n})}o(B,"logGuard");async function V(t,e,n,r,a,{log:i=!0,signal:l}={}){const s=Date.now(),u=a!==void 0?JSON.stringify(a):void 0;i&&k.setIdentity(it(e));let c;try{c=await Ze(t.apiBase+r,{method:n,headers:{"X-LinkAI-Run-Id":k.currentRunId(),...we?{"X-LinkAI-Version":we}:{},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 i&&k.emit("api.call",{method:n,...Le(r),ok:!1,network:!0,ms:Date.now()-s,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(i){const m=c.status<400&&f.code===200;k.emit("api.call",{method:n,...Le(r),status:c.status,code:f.code??null,ok:m,ms:Date.now()-s,reqBytes:u?Buffer.byteLength(u):0,resBytes:Buffer.byteLength(d),...m?{}:{message:String(f.message||"").slice(0,300)}})}return f}o(V,"rawFetch");const De="/api/app/linkai-oplog/events";k.setTransport(async(t,e)=>{if(!L())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 V(I(),r,"POST",De,{events:t},{log:!1,signal:e});return n(a)&&(r=await M({refresh:!0}),a=await V(I(),r,"POST",De,{events:t},{log:!1,signal:e})),a.__status<400&&a.code===200?(k.saveServerConfig(a.data||{}),!0):!1}catch{return!1}});const st=[/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 Yt(t){let e=String(t);try{for(let r=0;r<3;r++)e=decodeURIComponent(e)}catch{}const n=st.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(Yt,"assertApiAllowed");async function _(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{We(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=ke(t,e,n)}catch(f){const m=/update-new/i.test(e)&&n?.id&&n.id!==b?n.id:null,g=m&&f.errors?await Se(m).catch(()=>null):null;if(!g)throw f;c=ke(t,e,n,{previous:g})}for(const f of c)k.addNotice(`卡片保存:${f}。`,"card-rules");let d;try{d=Ne(t,e,n)}catch(f){const m=n?.userPageSettingId&&Number(n.filterSourceType)!==2?n.userPageSettingId:null,g=m&&f.errors?(await Ct(m).catch(()=>null))?.filters:null;if(!g)throw f;d=Ne(t,e,n,{previous:g})}for(const f of d)k.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 P("CONFIG_INVALID",c.message)}const i=kt(t,e,n);if(i){Oe.assertWritable([Oe.normalizeObject(`card:${i}`)]);try{await Nt(i),B("tenant-ownership",!0,{target:i,path:e.split("?")[0]})}catch(c){throw c instanceof Ue&&B("tenant-ownership",!1,{target:i,path:e.split("?")[0]}),c}}let l=await M(),s=await V(a,l,t,e,n);const u=o(c=>c.__status===401||c.code===401||c.__status===406||c.code===406,"expired");if(u(s)){if(!l.refreshToken)throw new E("OAuth 授权已失效");if(l=await M({refresh:!0}),s=await V(a,l,t,e,n),u(s))throw new E("自动续登后仍被拒绝")}if(delete s.__status,r)return s;if(s.code!==200)throw new ve(t,e,s);return s.data}o(_,"api");class P extends Error{static{o(this,"KindError")}constructor(e,n){super(n),this.name="KindError",this.kind=e}}const lt=[["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 Pe(t){return String(t||"").split(/\s*STACK TRACE:/)[0].replace(/\s*\w*Exception:\s*/g," ").replace(/\s+/g," ").trim()}o(Pe,"cleanApiMessage");class ve extends Error{static{o(this,"ApiError")}constructor(e,n,r={}){const a=Pe(r.message),i=Pe(r.error&&r.error.details||""),l=i&&!a.includes(i)&&!i.includes(a)?`${a} ${i}`:i.length>a.length?i: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=lt.find(([,s])=>s.test(l))?.[0]||null}}const S=o((t,e)=>_("POST",t,e),"post"),v=o(t=>_("GET",t),"get");let z=null;async function ee(){if(z)return z;const t=I();let e=process.env.LM_DATASOURCE||t.dataSourceId||"",n=[];if(!e){if(n=await ue("dataSources",30*6e4,async()=>{const a=await v("/index-block/index-block-data-source");return(Array.isArray(a)?a:a.items||[]).map(i=>({id:i.id,name:i.name}))}),!n.length)throw new Error("当前租户没有可用的数据源,请联系领谋开通");e=n[0].id}const r=await ue("rootFolders",30*6e4,async()=>{const[a,i,l]=await Promise.all([v("/index-block/folders?type=0"),v("/index-block/folders?type=1"),v("/index-block/folders?type=3")]);return{planFolder:a[0]?.id||b,cardFolder:i[0]?.id||b,dsFolder:l[0]?.id||b}});return z={dataSourceId:e,dataSources:n,...r},z}o(ee,"ctx");function ct(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*)?",i=[];for(const l of e.matchAll(new RegExp(`\\b(?:FROM|JOIN)\\s+${a}((?:\\s*,\\s*${a})*)`,"gi"))){i.push(l[1]);for(const s of(l[2]||"").matchAll(new RegExp(`,\\s*${a}`,"gi")))i.push(s[1])}return[...new Set(i)].filter(l=>!n.has(l.toLowerCase()))}o(ct,"sqlTables");let G=null;async function dt(){if(G)return G;const t=await ee(),e=await ue(`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(dt,"allowedTables");function ut(t){const e=t.split(`
|
|
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
|
|
3
|
-
${
|
|
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
|
+
`).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
|
+
${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
|
-
`+u.trim()}),a=a.replace(/\u0001(\d+)\u0001/g,(l,
|
|
7
|
-
`}o(ut,"formatSql");function ge(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(ge,"stripTrailingSemicolon");const ft=/^(information_schema|mysql|performance_schema|sys)\./i;function pt(t){const e=String(t).replace(/'(?:[^'\\]|\\.)*'/g,"''").replace(/\/\*[\s\S]*?\*\//g," ").replace(/--[^\n]*/g," ").trim();if(!/^(SELECT|WITH)\b/i.test(e))throw new P("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 P("SQL_READONLY","Rejected: SELECT … INTO OUTFILE/DUMPFILE is not allowed");ge(t)}o(pt,"assertReadOnlySql");function ht(t){try{pt(t)}catch(e){throw B("sql-readonly",!1,{sqlSha256:k.sha256(String(t)),chars:String(t).length}),e}}o(ht,"precheckSql");async function mt(t){ht(t),t=ge(t);const e=ct(t),n=t.replace(/'(?:[^'\\]|\\.)*'/g,"''").replace(/`/g,""),r=[...new Set([...e.filter(i=>ft.test(i)),...[...n.matchAll(/\b(information_schema|mysql|performance_schema|sys)\s*\.\s*\w+/gi)].map(i=>i[0])])];if(r.length)throw new P("SQL_TABLE_NOT_ALLOWED",`Rejected: system schema access is not allowed (${r.join(", ")})`);if(e.length){const i=await dt(),l=e.filter(s=>!i.has(s.replace(/^[\w]+\./,"").toLowerCase()));if(l.length)throw new P("SQL_TABLE_NOT_ALLOWED",`Rejected: not in the space's table list (${l.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).`)}const a=await ee();return S("/index-block/get-field-info-by-data-set-sql",{dataSourceId:a.dataSourceId,sql:t,mode:2})}o(mt,"checkSql");const wt=99,gt=2,yt=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:U.randomUUID(),tableFieldName:e.field_name,aliasCnName:e.field_name,fieldComment:"",argumentType:r?0:2,calculationMode:r?gt:wt,tableFieldType:r?"decimal":e.field_type,tableFieldTypeName:e.type_name,sortNum:n,sqlType:0,isCustomize:!1}}),"fieldColumns");async function Zt({id:t,name:e,sql:n,remark:r,noFormat:a=!1,current:i=null}){const l=await ee(),s=t&&t!==b?i||await bt(t):null;!a&&process.env.LM_NO_FMT!=="1"&&(n=ut(n)),n=ge(n);const u=await mt(n);if(!u?.length)throw new P("SQL_INVALID","运行 SQL 没有返回任何字段,无法生成映射字段");const c=Date.now(),d=[{id:U.randomUUID(),tableName:`sql_name_${c}`,tableComment:`sql_comment_${c}`,sortNum:0,fieldColumns:yt(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 ve?new P("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:s?.parentId||l.dsFolder,tag:s?.tag??"",remark:r??s?.remark??"",isPrivate:s?.isPrivate??!1,joinOns:[],tables:d})}o(Zt,"dsSave");async function St({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}}),i=a.items||[];n.push(...i.map(s=>({name:s.name,dsId:s.objId,folderId:s.id,...t?{editTime:s.editTime||null,addUser:s.addUser||null}:{}})));const l=Number(a.totalCount??a.total??0);if(i.length<500||l&&n.length>=l)break}return n}o(St,"dsList");const bt=o(t=>v(`/index-block-data-argument/data-set-detail/${t}`),"dsDetail"),It=o(t=>v(`/index-block-data-argument/data-set-fields/${t}`),"dsFields");async function Xt(t){return(await v(`/index-block/data-set-quoted-infos/${t}`)||[]).map(n=>({name:n.name,cardId:n.indexBlockId}))}o(Xt,"dsRefs");function en(){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(en,"kinds");const xt={dim:{prefix:"waxis",axis:"",argumentType:2},meas:{prefix:"yaxis",axis:"y1",argumentType:1},series:{prefix:"waxis_type",axis:"",argumentType:3}};function te({field:t,col:e,axis:n="",argumentType:r=2,tableFieldType:a="string",title:i,orderByType:l=0,i:s=0}){return{id:U.randomUUID(),format:"default",axis:n,field:t,title:i??e,sortNum:s,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:U.randomUUID(),isCustomizeFill:!1,isInnerFill:!1,fieldDesc:""}}o(te,"baseDto");const Et=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 ne({dsId:t,name:e,remark:n,chartParentType:r,chartType:a,dtos:i,chartSetting:l,limitCount:s=500,id:u}){const c=await ee(),d=u&&u!==b?await Se(u):null,f={indexBlockDataId:t,isMulData:!1,indexBlockShowOrderFields:i.map(Et),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:s,indexBlockDataSettingDtos:i,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(ne,"saveCard");async function ye(t){const e=await It(t);return{fields:e,meta:Object.fromEntries(e.map(n=>[n.tableFieldComment,n]))}}o(ye,"datasetMeta");async function tn({dsId:t,name:e,kind:n,dim:r,meas:a,series:i,id:l}){const s=_e[n];if(!s)throw new Error(`Chart kind "${n}" is not in the v2 chart registry; run: linkai kinds`);if(s._disabled)throw new Error(`Chart kind "${n}" (${s._name}) is disabled: ${s._disabled}`);const{fields:u,meta:c}=await ye(t),d=o((C,$,W,p)=>{const w=c[W];if(!w)throw new Error(`Dataset has no column "${W}". Available: ${u.map(A=>A.tableFieldComment).join(" / ")}`);const y=xt[C];return te({field:y.prefix+$,col:W,axis:y.axis,argumentType:y.argumentType,tableFieldType:w.tableFieldType,i:p})},"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 g=f.map((C,$)=>d("dim",$+1,C,$));return m.forEach((C,$)=>g.push(d("meas",$+1,C,g.length))),i&&g.push(d("series",1,i,g.length)),ne({dsId:t,name:e,chartParentType:s.chartParentType,chartType:s.chartType,dtos:g,chartSetting:s.chartSetting,limitCount:0,id:l})}o(tn,"cardChart");async function nn({dsId:t,name:e,col:n,subtitle:r,color:a="#E8611C",id:i}){const l=[te({field:"yaxis1",col:n,axis:"y1",argumentType:1,tableFieldType:"decimal",title:r||n})];return ne({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:i})}o(nn,"cardMeter");async function rn({dsId:t,spec:e,id:n}){const{meta:r}=await ye(t),a={},i=[];let l=0;const s=o(p=>{if(a[p])return a[p];const w=r[p];if(!w)throw new Error(`Dataset has no column "${p}". Available: ${Object.keys(r).join(" / ")}`);const y=`waxis${++l}`;return i.push(te({field:y,col:p,argumentType:w.argumentType,tableFieldType:w.tableFieldType,i:l-1})),a[p]=y,y},"reg"),u=o(()=>U.randomUUID(),"u"),c=o(p=>({fontSize:12,fill:"#1F2329",...p}),"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((p,w,y)=>({id:"column_"+u(),field:[p],fixed:!1,enableCustomRender:!1,areas:[{id:u(),width:null,rows:[{id:u(),itemGap:10,items:[f()]}]}],name:w,contentAlign:"left",spanRows:!1,hidden:y,isPivot:!1,childPivotFields:[],topValueField:!1}),"plainColumn"),g=[];for(const p of e.columns||[]){if(p.plain){g.push(m(s(p.plain),p.header,!1));continue}const w=[],y=[],A=o(T=>{const[N,F=""]=T.split(":"),Ie=s(N);w.push(Ie);const se=F==="image",xe=d[se?"":F]||d[""],Ee={id:u(),type:se?"image":"labelValue",field:Ie,layout:"left",arrangement:"horizontal",fieldStyle:xe,valueStyle:xe,showLabel:!1,iconPosition:"left",labelValueGap:4,showColon:!1};return se&&(Ee.imgWidth=p.imgWidth||112),Ee},"mkItem");p.image&&y.push({id:u(),width:(p.imgWidth||112)+16,rows:[{id:u(),itemGap:10,items:[A(`${p.image}:image`)]}]}),y.push({id:u(),width:null,rows:(p.rows||[]).map(T=>({id:u(),itemGap:10,items:(Array.isArray(T)?T:[T]).map(A)}))}),g.push({id:"column_"+u(),field:w,fixed:!1,enableCustomRender:!0,areas:y,name:p.header,contentAlign:"left",spanRows:!1,hidden:!1,isPivot:!1,childPivotFields:[],topValueField:!1})}for(const p of e.hidden||[])g.push(m(s(p),p,!0));const C=o(p=>(e[p]||[]).flatMap(w=>{const y=a[w.dep]||s(w.dep),A=a[w.col]||s(w.col),T=[];for(const[N,F]of Object.entries(w.eq||{}))T.push({field:A,color:F,depField:y,operator:"==",value:isNaN(+N)?N:+N});for(const[N,F]of Object.entries(w.gte||{}))T.push({field:A,color:F,depField:y,operator:">=",value:+N});for(const[N,F]of Object.entries(w.ne||{}))T.push({field:A,color:F,depField:y,operator:"!=",value:N});return T}),"conds"),$=e.rowGrey?[{field:"",color:"#F7F8FA",depField:a[e.rowGrey.dep]||s(e.rowGrey.dep),operator:">=",value:e.rowGrey.gte}]:[];e.order&&s(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(p=>({key:g.find(w=>w.name===p.header).field[0],width:p.width||200})),conditionStyle:{value:{conditions:C("pills")},arrow:{conditions:[]},fontColor:{conditions:C("fontColor")},rowMark:{conditions:$},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:g}}};return ne({dsId:t,name:e.name,remark:e.remark,chartParentType:"table",chartType:"agtable",dtos:i,chartSetting:W,limitCount:e.limitCount||2e3,id:n})}o(rn,"cardTable");async function an({dsId:t,spec:e,id:n}){const{meta:r}=await ye(t),a={},i=[];let l=0;const s=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 i.push(te({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:s(e.image),main:s(e.main),assist:s(e.assist),status:s(e.status),tags:(e.tags||[]).map(s),desc:(e.desc||[]).map(s),list:(e.list||[]).map(s),progress:s(e.progress),taskBar:{textFields:(e.taskBarText||[]).map(s)},fieldMap:{startDate:s(e.start),endDate:s(e.end),plannedStart:s(e.plannedStart),plannedEnd:s(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&&s(e.order),ne({dsId:t,name:e.name,remark:e.remark,chartParentType:"table",chartType:"gantt",dtos:i,chartSetting:u,id:n})}o(an,"cardGantt");async function Re(){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 i=Number(r.totalCount??r.total??0);if(a.length<500||i&&e.length>=i)break}return e}o(Re,"cardList");class Ue extends Error{static{o(this,"TenantMismatchError")}constructor(e,n){super(`卡片 ${e} 不属于当前租户${n?`「${n}」`:""},已拒绝写入(写进去的数据会落在当前租户名下)`),this.name="TenantMismatchError",this.cardId=e}}const Tt=/\/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 Q=null;function kt(t,e,n){if(String(t).toUpperCase()==="GET"||!Tt.test(String(e))||!n||typeof n!="object"||Array.isArray(n))return"";const r=o((...a)=>{for(const i of a){const l=Object.keys(n).find(s=>s.toLowerCase()===i);if(l&&n[l])return String(n[l])}return""},"pick");return/update-new/i.test(e)?r("id"):r("usedindexblockid")}o(kt,"tenantGuardTarget");async function Nt(t){if(!t)return;const e=String(t).toLowerCase(),n=o(async()=>{Q=new Set((await Re()).map(r=>String(r.cardId).toLowerCase()))},"load");if(Q?Q.has(e)||await n():await n(),!Q.has(e))throw new Ue(t,fe(await M()).tenantName)}o(Nt,"assertCardInTenant");const Se=o(t=>S("/index-block/get-new-index-block-detail",{id:t}),"cardDetail");async function on(t,e){let n;if(e==="clear")n=[];else if(Array.isArray(e))n=e;else{const i=(await Se(t)).indexBlockDataSettingDtos||[];n=String(e).split(",").map(l=>l.trim()).filter(Boolean).map(l=>{const[s,u="asc"]=l.split(":"),c=i.find(d=>d.field===s)||i.find(d=>d.comment===s);if(!c)throw new Error(`Card has no field "${s}". Available: ${i.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((i,l)=>({type:1,sortFields:[],...i,sortNum:l}));await _("PUT","/page-setting/page-card-index-block-sort-field",{usedIndexBlockId:t,sortField:r});const a=await Ot({cardId:t,pageSize:1});return{cardId:t,requested:r.map(i=>`${i.field} ${i.type===2?"manual":i.sort}`),sqlHasOrderBy:/\bORDER\s+BY\s+(?:W\.)?(?:waxis|yaxis|waxis_type|order)\w*/i.test(a.sql||""),queryCode:a.code}}o(on,"cardSort");async function Ot({cardId:t,pageSize:e=20,filters:n=[]}){const r=await _("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(Ot,"queryCard");async function _t(){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(_t,"planList");const Ct=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:i=20}){const l=await S("/page-setting/new-page-card",{indexBlockId:e,sourceType:0,x:+n,y:+r,width:+a,height:+i,mobileX:0,mobileY:+r,mobileWidth:4,mobileHeight:+i,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 ln(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(ln,"planFilter");async function $t(){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($t,"reportList");const cn=o(t=>S("/page-setting/get-new-page-card-report",{indexBlockId:t,isSubWarn:!1}),"reportDetail");async function dn(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 $t()).filter(r=>r.name.startsWith(t)).map(r=>r.folderId)),await n("plans",(await _t()).filter(r=>r.name.startsWith(t)).map(r=>r.folderId)),await n("cards",(await Re()).filter(r=>r.name.startsWith(t)).map(r=>r.folderId)),await n("datasets",(await St()).filter(r=>r.name.startsWith(t)).map(r=>r.folderId)),{prefix:t,removed:e}}o(dn,"cleanPrefix");function un(t){return`(() => { const s = ${JSON.stringify({t:t.token,c:t.tenantId,a:t.agentId})};
|
|
6
|
+
`+u.trim()}),a=a.replace(/\u0001(\d+)\u0001/g,(l,i)=>r[+i]),a.trim()+`
|
|
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(
|
|
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};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
var
|
|
1
|
+
var D=Object.defineProperty;var n=(e,t)=>D(e,"name",{value:t,configurable:!0});import S from"node:fs";import y from"node:path";import L from"node:os";const b=2*3600*1e3,k=n(()=>y.join(L.homedir(),".linkai","cache","dict-reads.json"),"file");function m(){try{return JSON.parse(S.readFileSync(k(),"utf8"))||{}}catch{return{}}}n(m,"readState");function w(e){try{S.mkdirSync(y.dirname(k()),{recursive:!0,mode:448}),S.writeFileSync(k(),JSON.stringify(e),{mode:384})}catch{}}n(w,"writeState");function O({tenantId:e,source:t,flowId:s=null,op:a,now:r=Date.now()}){if(!e||!t)return;const o=m();o.reads??={},o.reads[e]??={},o.reads[e][String(t).toLowerCase()]={readAt:r,flowId:s,op:a},w(o)}n(O,"recordRead");function C({tenantId:e,flowId:t=null,ref:s,now:a=Date.now()}){if(!e||!s)return;const r=m();r.refs??={};const o=(r.refs[e]||[]).filter(d=>d.ref!==s);o.push({ref:String(s).slice(0,200),flowId:t,at:a}),r.refs[e]=o.slice(-100),w(r)}n(C,"recordRef");function T({tenantId:e,flowId:t=null,now:s=Date.now()}){return(m().refs?.[e]||[]).filter(r=>t&&r.flowId===t||s-r.at<=b).slice(-50).map(r=>r.ref)}n(T,"recentRefs");function _(e,t=Date.now()){w({reads:{},resetAt:t,resetReason:e})}n(_,"resetReads");function x(e){if(!e)return null;const t=Date.parse(`${String(e).trim().replace(" ","T")}+08:00`);return Number.isFinite(t)?t:null}n(x,"parseServerTime");const A=n(e=>new Date(e+8*3600*1e3).toISOString().slice(0,19).replace("T"," "),"fmt");async function j({tenantId:e,source:t,flowId:s,fetchSources:a,fetchDocs:r,now:o=Date.now()}){let d;try{d=await a()}catch(i){return{ok:!0,skipped:`数据字典服务暂不可用(${String(i?.message||i).slice(0,80)}),本次未校验`}}const f=(d||[]).find(i=>String(i.source).toLowerCase()===String(t).toLowerCase());if(!f||f.metadataReady===!1)return{ok:!0,skipped:"当前数据集所用数据源没有可用的数据字典,本次未校验"};let g=[];try{g=await r()||[]}catch{}const h=[x(f.lastSuccessAt),...g.map(i=>x(i.updatedAt))].filter(Number.isFinite),c=h.length?Math.max(...h):null,u=m().reads?.[e]?.[String(t).toLowerCase()],p={source:t,sourceName:f.name??null,dictUpdatedAt:c?A(c):null};if(!u)return{...p,ok:!1,reason:"当前租户还没有读取数据字典(切换租户或重新登录后必须重新读取)"};const l=A(u.readAt);return c&&u.readAt<c?{...p,readAt:l,ok:!1,reason:`数据字典在 ${A(c)} 更新过,晚于上次读取(${l}),必须重新读取`}:u.flowId!==s&&o-u.readAt>b?{...p,readAt:l,ok:!1,reason:`上次读取数据字典是 ${l},不在当前任务内且已超过 2 小时,必须重新读取`}:{...p,readAt:l,ok:!0}}n(j,"dictionaryStatus");function M(e){return`先读取当前租户的最新数据字典:linkai kb-sources 确认数据源,再按需 linkai kb-search --source ${e} --question "<业务问题>"、kb-object / kb-docs / kb-doc 核对本次 SQL 用到的视图、字段与口径,然后重新执行。不能沿用其它租户或旧的数据字典结论。`}n(M,"readHint");export{b as MAX_AGE_OUTSIDE_FLOW_MS,j as dictionaryStatus,x as parseServerTime,M as readHint,T as recentRefs,O as recordRead,C as recordRef,_ as resetReads};
|