@zibby/skills 2.0.15 → 2.0.17

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.
@@ -34,7 +34,13 @@
34
34
  * GET /open-apis/bitable/v1/apps/{app}/tables?page_size=100 → { items:[{table_id,name}] }
35
35
  * GET /open-apis/bitable/v1/apps/{app}/tables/{t}/fields → { items:[{field_name,type}] }
36
36
  * POST /open-apis/bitable/v1/apps/{app}/tables/{t}/records/search?page_size=500
37
- * body { view_id?, field_names? } → { items:[{record_id,fields}], has_more, page_token, total }
37
+ * body { view_id?, field_names?, filter?, sort?, automatic_fields? }
38
+ * → { items:[{record_id,fields,created_time?,created_by?,
39
+ * last_modified_time?,last_modified_by?}], has_more, page_token, total }
40
+ * `filter`/`sort` are evaluated by LARK (so `total` counts the MATCHES,
41
+ * not the table), and `automatic_fields` is what makes the four
42
+ * who/when properties present — they are row METADATA on the item, not
43
+ * cells, so they arrive whether or not the table declares such columns.
38
44
  * A Base is NOT a docx: a /wiki/ node fronting one resolves to obj_type
39
45
  * 'bitable', which the docx path rejects by design (see resolveDocumentId).
40
46
  *
@@ -87,5 +93,34 @@ export declare function markdownToLarkBlocks(markdown: any): any[];
87
93
  * Derive the human-facing Base web URL (same region logic as docWebUrl).
88
94
  */
89
95
  export declare function baseWebUrl(host: any, appToken: any, tableId?: any, viewId?: any): string;
96
+ /**
97
+ * Build the records/search request body from the caller's arguments, in
98
+ * Lark's snake_case shape, validating every column name and operator against
99
+ * the columns this table actually has.
100
+ *
101
+ * Exported for tests: this is the whole surface where a caller's question
102
+ * ("which rows changed yesterday?") becomes something LARK evaluates instead
103
+ * of something the model re-derives after pulling the table.
104
+ */
105
+ export declare function buildBitableSearchBody({ columns, viewId, fieldNames, filter, sort, includeRowMeta }: {
106
+ columns: any;
107
+ viewId: any;
108
+ fieldNames: any;
109
+ filter: any;
110
+ sort: any;
111
+ includeRowMeta: any;
112
+ }): any;
113
+ /**
114
+ * Flatten one record's row METADATA — created/last-modified, who and when.
115
+ * These are properties of the ITEM, not cells, so they exist for every row
116
+ * whether or not the table declares "Created time"-style columns.
117
+ *
118
+ * Lark stamps these record-level times in SECONDS while a date CELL is
119
+ * MILLISECONDS. Normalising on magnitude rather than trusting one unit keeps
120
+ * a unit change from silently rendering every row as 1970 (or year 55000).
121
+ *
122
+ * Exported for tests.
123
+ */
124
+ export declare function bitableRowMeta(item: any): any;
90
125
  export declare const larkDocsSkill: any;
91
126
  export declare function _resetLarkDocsTokenCache(): void;
package/dist/larkDocs.js CHANGED
@@ -1,3 +1,3 @@
1
- import{existsSync as H,statSync as re,readFileSync as ie}from"fs";import{fileURLToPath as ae}from"url";import{basename as se,dirname as ce,resolve as de}from"path";var _=Object.freeze({SENTRY:"sentry",JIRA:"jira",GITHUB:"github",GITLAB:"gitlab",SLACK:"slack",LARK:"lark",LARK_DOCS:"lark_docs",OPENAI_BILLING:"openai_billing",ANTHROPIC_BILLING:"anthropic_billing",CURSOR_ADMIN:"cursor_admin",NOTION:"notion",GOOGLE:"google",PLANE:"plane",LINEAR:"linear",VIKUNJA:"vikunja",FIGMA:"figma",HUBSPOT:"hubspot",OPEN_DESIGN:"open_design",LINKEDIN_PERSONAL:"linkedin_personal",LINKEDIN_BUSINESS:"linkedin_business",DISCORD:"discord"}),Pe=Object.freeze({sentry:{id:"sentry",name:"Sentry",connectPath:"/integrations?provider=sentry"},jira:{id:"jira",name:"Jira",connectPath:"/integrations?provider=jira"},github:{id:"github",name:"GitHub",connectPath:"/integrations?provider=github"},gitlab:{id:"gitlab",name:"GitLab",connectPath:"/integrations?provider=gitlab"},slack:{id:"slack",name:"Slack",connectPath:"/integrations?provider=slack"},lark:{id:"lark",name:"Lark Chat",connectPath:"/integrations?provider=lark"},lark_docs:{id:"lark_docs",name:"Lark App",connectPath:"/integrations?provider=lark_docs"},openai_billing:{id:"openai_billing",name:"OpenAI Admin",connectPath:"/integrations?provider=openai_billing"},anthropic_billing:{id:"anthropic_billing",name:"Anthropic Admin",connectPath:"/integrations?provider=anthropic_billing"},cursor_admin:{id:"cursor_admin",name:"Cursor Admin",connectPath:"/integrations?provider=cursor_admin"},notion:{id:"notion",name:"Notion",connectPath:"/integrations?provider=notion"},google:{id:"google",name:"Google Docs",connectPath:"/integrations?provider=google"},plane:{id:"plane",name:"Plane",connectPath:"/integrations?provider=plane"},linear:{id:"linear",name:"Linear",connectPath:"/integrations?provider=linear"},vikunja:{id:"vikunja",name:"Vikunja",connectPath:"/integrations?provider=vikunja"},figma:{id:"figma",name:"Figma",connectPath:"/integrations?provider=figma"},hubspot:{id:"hubspot",name:"HubSpot",connectPath:"/integrations?provider=hubspot"},open_design:{id:"open_design",name:"OpenDesign",connectPath:"/integrations?provider=open_design"},linkedin_personal:{id:"linkedin_personal",name:"LinkedIn (Personal)",connectPath:"/integrations?provider=linkedin_personal"},linkedin_business:{id:"linkedin_business",name:"LinkedIn (Business)",connectPath:"/integrations?provider=linkedin_business"},discord:{id:"discord",name:"Discord",connectPath:"/integrations?provider=discord"}});import{resolveIntegrationToken as D}from"@zibby/core/backend-client.js";var V="https://open.larksuite.com",j=Object.freeze([Object.freeze({appId:"LARK_DOCS_APP_ID",appSecret:"LARK_DOCS_APP_SECRET",host:"LARK_DOCS_HOST"}),Object.freeze({appId:"LARK_APP_ID",appSecret:"LARK_APP_SECRET",host:"LARK_HOST"})]),C=Object.freeze(j.flatMap(t=>[t.appId,t.appSecret,t.host]));function A(t){let e=String(t||"").trim().replace(/\/+$/,"");return e?/^https?:\/\//i.test(e)?e:`https://${e}`:V}function X(t){let e=String(process.env[t.appId]||"").trim(),n=String(process.env[t.appSecret]||"").trim();return!e||!n?null:{appId:e,appSecret:n,host:A(process.env[t.host])}}async function K(){for(let t of j){let e=X(t);if(e)return e}try{let t=await D(_.LARK_DOCS);return{appId:t?.appId,appSecret:t?.appSecret,host:A(t?.host)}}catch(t){if(!/unknown provider/i.test(String(t?.message||"")))throw t;let e=await D(_.LARK);return{appId:e?.appId,appSecret:e?.appSecret,host:A(e?.host)}}}var L={api:{knob:"SKILL_API_TIMEOUT_MS",fallback:3e4},transfer:{knob:"SKILL_TRANSFER_TIMEOUT_MS",fallback:12e4},job:{knob:"SKILL_JOB_TIMEOUT_MS",fallback:3e5}};function Q(t,e,n=process.env){let r=Number(n?.[t]);return Number.isFinite(r)&&r>0?Math.min(6e5,Math.max(1e3,Math.floor(r))):e}function ee(t="api",e=process.env){let n=L[t]||L.api;return Q(n.knob,n.fallback,e)}function te(t){return t?.name==="TimeoutError"||t?.name==="AbortError"}function ne(t){try{return new URL(String(t?.url??t)).host||"unknown host"}catch{return"unknown host"}}function oe(t,e){if(!t)return e;let n=new AbortController,r=a=>{n.signal.aborted||n.abort(a)},o=()=>r(t.reason),i=()=>r(e.reason);return n.signal.addEventListener("abort",()=>{t.removeEventListener("abort",o),e.removeEventListener("abort",i)},{once:!0}),t.aborted?r(t.reason):e.aborted?r(e.reason):(t.addEventListener("abort",o,{once:!0}),e.addEventListener("abort",i,{once:!0})),n.signal}async function T(t,e={},n={}){let r=n.kind||"api",o=(L[r]||L.api).knob,i=n.timeoutMs?Math.min(6e5,Math.max(1e3,Math.floor(n.timeoutMs))):ee(r),a=e?.signal;if(a?.aborted)throw a.reason??new DOMException("This operation was aborted","AbortError");let s=AbortSignal.timeout(i),c=oe(a,s);try{return await fetch(t,{...e,signal:c})}catch(d){throw s.aborted&&te(d)?new Error(`${n.what||"request"} TIMED OUT after ${i}ms against ${ne(t)} (${o})`):d}}function le(){if(process.env.MCP_SKILL_PATH)return process.env.MCP_SKILL_PATH;let t=ce(ae(import.meta.url)),e=de(t,"..","bin","mcp-skill.mjs");return H(e)?e:null}var F=2e4,J=50,pe=50,ue=20,me=500,ke=200,fe=1e3,Y=100,v=20,he=3e4,_e=new Set([5,1001,1002]),be=10*1024*1024,ye=6e3*1e3,y=null,ge=C;async function N(){let{appId:t,appSecret:e,host:n}=await K();if(y&&y.appId===t&&y.expiresAt>Date.now())return{token:y.token,host:n};let o=await(await T(`${n}/open-apis/auth/v3/tenant_access_token/internal`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({app_id:t,app_secret:e})},{kind:"api",what:"Lark tenant_access_token"})).json();if(o.code!==0)throw new Error(`Lark tenant_access_token failed: ${o.msg||o.code}`);return y={token:o.tenant_access_token,expiresAt:Date.now()+ye,appId:t},{token:o.tenant_access_token,host:n}}async function u(t,e,n){let{token:r,host:o}=await N(),i={method:t,headers:{Authorization:`Bearer ${r}`,"Content-Type":"application/json; charset=utf-8"}};t!=="GET"&&n!==void 0&&(i.body=JSON.stringify(n));let s=await(await T(`${o}${e}`,i,{kind:"api",what:`Lark Docx ${t} ${e}`})).json();if(s.code!==0)throw new Error(`Lark Docx API ${e} error: ${s.msg||s.code}`);return{data:s.data||{},host:o}}function w(t,e){return String(t||"").includes("feishu")?`https://feishu.cn/docx/${e}`:`https://www.larksuite.com/docx/${e}`}function Ie(t,e){return String(t||"").includes("feishu")?`https://feishu.cn/wiki/${e}`:`https://www.larksuite.com/wiki/${e}`}function we(t){let e=t.indexOf("?");if(e<0)return{};let n=t.slice(e+1).split("#")[0],r=new URLSearchParams(n),o={},i=r.get("table"),a=r.get("view");return i&&/^tbl[A-Za-z0-9]+$/.test(i)&&(o.tableId=i),a&&/^vew[A-Za-z0-9]+$/.test(a)&&(o.viewId=a),o}function h(t){if(!t||typeof t!="string")return null;let e=t.trim();if(!e)return null;let n=e.match(/\/(docx|wiki|base)\/([A-Za-z0-9]+)/);return n?{type:n[1],token:n[2],...we(e)}:/^[A-Za-z0-9]{10,}$/.test(e)?{type:"docx",token:e}:null}async function b(t){let e=typeof t=="string"?h(t):t;if(!e)throw new Error("A valid Lark doc id or URL is required");if(e.type==="docx")return e.token;let{data:n}=await u("GET",`/open-apis/wiki/v2/spaces/get_node?token=${encodeURIComponent(e.token)}`),r=n?.node||{};if(r.obj_type!=="docx"||!r.obj_token){let o=r.obj_type||"unknown";throw o==="bitable"?new Error("This Lark wiki link is a Base / \u591A\u7EF4\u8868\u683C (obj_type=bitable), not a document \u2014 read it with larkbitable_read_records (larkbitable_list_tables lists its tables)"):new Error(`Wiki node is not a docx document (obj_type=${o})`)}return r.obj_token}function x(t){let e=String(t??"").replace(/\r\n/g,`
1
+ import{existsSync as ee,statSync as pe,readFileSync as ue}from"fs";import{fileURLToPath as me}from"url";import{basename as fe,dirname as ke,resolve as he}from"path";var w=Object.freeze({SENTRY:"sentry",JIRA:"jira",GITHUB:"github",GITLAB:"gitlab",SLACK:"slack",LARK:"lark",LARK_DOCS:"lark_docs",OPENAI_BILLING:"openai_billing",ANTHROPIC_BILLING:"anthropic_billing",CURSOR_ADMIN:"cursor_admin",NOTION:"notion",GOOGLE:"google",PLANE:"plane",LINEAR:"linear",VIKUNJA:"vikunja",FIGMA:"figma",HUBSPOT:"hubspot",OPEN_DESIGN:"open_design",LINKEDIN_PERSONAL:"linkedin_personal",LINKEDIN_BUSINESS:"linkedin_business",DISCORD:"discord"}),Ke=Object.freeze({sentry:{id:"sentry",name:"Sentry",connectPath:"/integrations?provider=sentry"},jira:{id:"jira",name:"Jira",connectPath:"/integrations?provider=jira"},github:{id:"github",name:"GitHub",connectPath:"/integrations?provider=github"},gitlab:{id:"gitlab",name:"GitLab",connectPath:"/integrations?provider=gitlab"},slack:{id:"slack",name:"Slack",connectPath:"/integrations?provider=slack"},lark:{id:"lark",name:"Lark Chat",connectPath:"/integrations?provider=lark"},lark_docs:{id:"lark_docs",name:"Lark App",connectPath:"/integrations?provider=lark_docs"},openai_billing:{id:"openai_billing",name:"OpenAI Admin",connectPath:"/integrations?provider=openai_billing"},anthropic_billing:{id:"anthropic_billing",name:"Anthropic Admin",connectPath:"/integrations?provider=anthropic_billing"},cursor_admin:{id:"cursor_admin",name:"Cursor Admin",connectPath:"/integrations?provider=cursor_admin"},notion:{id:"notion",name:"Notion",connectPath:"/integrations?provider=notion"},google:{id:"google",name:"Google Docs",connectPath:"/integrations?provider=google"},plane:{id:"plane",name:"Plane",connectPath:"/integrations?provider=plane"},linear:{id:"linear",name:"Linear",connectPath:"/integrations?provider=linear"},vikunja:{id:"vikunja",name:"Vikunja",connectPath:"/integrations?provider=vikunja"},figma:{id:"figma",name:"Figma",connectPath:"/integrations?provider=figma"},hubspot:{id:"hubspot",name:"HubSpot",connectPath:"/integrations?provider=hubspot"},open_design:{id:"open_design",name:"OpenDesign",connectPath:"/integrations?provider=open_design"},linkedin_personal:{id:"linkedin_personal",name:"LinkedIn (Personal)",connectPath:"/integrations?provider=linkedin_personal"},linkedin_business:{id:"linkedin_business",name:"LinkedIn (Business)",connectPath:"/integrations?provider=linkedin_business"},discord:{id:"discord",name:"Discord",connectPath:"/integrations?provider=discord"}});import{resolveIntegrationToken as F}from"@zibby/core/backend-client.js";var re="https://open.larksuite.com",J=Object.freeze([Object.freeze({appId:"LARK_DOCS_APP_ID",appSecret:"LARK_DOCS_APP_SECRET",host:"LARK_DOCS_HOST"}),Object.freeze({appId:"LARK_APP_ID",appSecret:"LARK_APP_SECRET",host:"LARK_HOST"})]),G=Object.freeze(J.flatMap(t=>[t.appId,t.appSecret,t.host]));function R(t){let e=String(t||"").trim().replace(/\/+$/,"");return e?/^https?:\/\//i.test(e)?e:`https://${e}`:re}function ie(t){let e=String(process.env[t.appId]||"").trim(),n=String(process.env[t.appSecret]||"").trim();return!e||!n?null:{appId:e,appSecret:n,host:R(process.env[t.host])}}async function z(){for(let t of J){let e=ie(t);if(e)return e}try{let t=await F(w.LARK_DOCS);return{appId:t?.appId,appSecret:t?.appSecret,host:R(t?.host)}}catch(t){if(!/unknown provider/i.test(String(t?.message||"")))throw t;let e=await F(w.LARK);return{appId:e?.appId,appSecret:e?.appSecret,host:R(e?.host)}}}var x={api:{knob:"SKILL_API_TIMEOUT_MS",fallback:3e4},transfer:{knob:"SKILL_TRANSFER_TIMEOUT_MS",fallback:12e4},job:{knob:"SKILL_JOB_TIMEOUT_MS",fallback:3e5}};function ae(t,e,n=process.env){let r=Number(n?.[t]);return Number.isFinite(r)&&r>0?Math.min(6e5,Math.max(1e3,Math.floor(r))):e}function se(t="api",e=process.env){let n=x[t]||x.api;return ae(n.knob,n.fallback,e)}function ce(t){return t?.name==="TimeoutError"||t?.name==="AbortError"}function de(t){try{return new URL(String(t?.url??t)).host||"unknown host"}catch{return"unknown host"}}function le(t,e){if(!t)return e;let n=new AbortController,r=i=>{n.signal.aborted||n.abort(i)},o=()=>r(t.reason),a=()=>r(e.reason);return n.signal.addEventListener("abort",()=>{t.removeEventListener("abort",o),e.removeEventListener("abort",a)},{once:!0}),t.aborted?r(t.reason):e.aborted?r(e.reason):(t.addEventListener("abort",o,{once:!0}),e.addEventListener("abort",a,{once:!0})),n.signal}async function O(t,e={},n={}){let r=n.kind||"api",o=(x[r]||x.api).knob,a=n.timeoutMs?Math.min(6e5,Math.max(1e3,Math.floor(n.timeoutMs))):se(r),i=e?.signal;if(i?.aborted)throw i.reason??new DOMException("This operation was aborted","AbortError");let s=AbortSignal.timeout(a),c=le(i,s);try{return await fetch(t,{...e,signal:c})}catch(d){throw s.aborted&&ce(d)?new Error(`${n.what||"request"} TIMED OUT after ${a}ms against ${de(t)} (${o})`):d}}function ye(){if(process.env.MCP_SKILL_PATH)return process.env.MCP_SKILL_PATH;let t=ke(me(import.meta.url)),e=he(t,"..","bin","mcp-skill.mjs");return ee(e)?e:null}var H=2e4,W=50,_e=50,be=20,we=500,ge=200,Ie=1e3,te=100,U=20,Se=3e4,Te=new Set([5,1001,1002]),Le=10*1024*1024,Ae=6e3*1e3,I=null,Ee=G;async function $(){let{appId:t,appSecret:e,host:n}=await z();if(I&&I.appId===t&&I.expiresAt>Date.now())return{token:I.token,host:n};let o=await(await O(`${n}/open-apis/auth/v3/tenant_access_token/internal`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({app_id:t,app_secret:e})},{kind:"api",what:"Lark tenant_access_token"})).json();if(o.code!==0)throw new Error(`Lark tenant_access_token failed: ${o.msg||o.code}`);return I={token:o.tenant_access_token,expiresAt:Date.now()+Ae,appId:t},{token:o.tenant_access_token,host:n}}async function m(t,e,n){let{token:r,host:o}=await $(),a={method:t,headers:{Authorization:`Bearer ${r}`,"Content-Type":"application/json; charset=utf-8"}};t!=="GET"&&n!==void 0&&(a.body=JSON.stringify(n));let s=await(await O(`${o}${e}`,a,{kind:"api",what:`Lark Docx ${t} ${e}`})).json();if(s.code!==0)throw new Error(`Lark Docx API ${e} error: ${s.msg||s.code}`);return{data:s.data||{},host:o}}function L(t,e){return String(t||"").includes("feishu")?`https://feishu.cn/docx/${e}`:`https://www.larksuite.com/docx/${e}`}function xe(t,e){return String(t||"").includes("feishu")?`https://feishu.cn/wiki/${e}`:`https://www.larksuite.com/wiki/${e}`}function Oe(t){let e=t.indexOf("?");if(e<0)return{};let n=t.slice(e+1).split("#")[0],r=new URLSearchParams(n),o={},a=r.get("table"),i=r.get("view");return a&&/^tbl[A-Za-z0-9]+$/.test(a)&&(o.tableId=a),i&&/^vew[A-Za-z0-9]+$/.test(i)&&(o.viewId=i),o}function y(t){if(!t||typeof t!="string")return null;let e=t.trim();if(!e)return null;let n=e.match(/\/(docx|wiki|base)\/([A-Za-z0-9]+)/);return n?{type:n[1],token:n[2],...Oe(e)}:/^[A-Za-z0-9]{10,}$/.test(e)?{type:"docx",token:e}:null}async function g(t){let e=typeof t=="string"?y(t):t;if(!e)throw new Error("A valid Lark doc id or URL is required");if(e.type==="docx")return e.token;let{data:n}=await m("GET",`/open-apis/wiki/v2/spaces/get_node?token=${encodeURIComponent(e.token)}`),r=n?.node||{};if(r.obj_type!=="docx"||!r.obj_token){let o=r.obj_type||"unknown";throw o==="bitable"?new Error("This Lark wiki link is a Base / \u591A\u7EF4\u8868\u683C (obj_type=bitable), not a document \u2014 read it with larkbitable_read_records (larkbitable_list_tables lists its tables)"):new Error(`Wiki node is not a docx document (obj_type=${o})`)}return r.obj_token}function v(t){let e=String(t??"").replace(/\r\n/g,`
2
2
  `),n=[];for(let r of e.split(`
3
- `)){let o=r.replace(/\s+$/,"");if(!o.trim())continue;let i=/^(#{1,3})\s+(.*)$/.exec(o),a=/^\s*[-*]\s+(.*)$/.exec(o),s=/^\s*\d+[.)]\s+(.*)$/.exec(o),c,d,l;if(i){let m=i[1].length;c=`heading${m}`,d=2+m,l=i[2]}else a?(c="bullet",d=12,l=a[1]):s?(c="ordered",d=13,l=s[1]):(c="text",d=2,l=o);n.push({block_type:d,[c]:{elements:[{text_run:{content:l}}],style:{}}})}return n}function S(t){let e=typeof t?.markdown=="string"?t.markdown:null,n=typeof t?.text=="string"?t.text:null;return e??n}async function O(t,e){let n;for(let r=0;r<e.length;r+=J){let o=e.slice(r,r+J);n=(await u("POST",`/open-apis/docx/v1/documents/${t}/blocks/${t}/children?document_revision_id=-1`,{children:o})).host}return n}function Se(t){let e=typeof t=="string"?t.trim():"";if(!e)throw new Error("imagePath is required");if(!H(e)||!re(e).isFile())throw new Error(`imagePath not found (or not a file): ${e}`);if(!/\.(png|jpe?g)$/i.test(e))throw new Error("imagePath must be a .png or .jpg/.jpeg file");let n=ie(e);if(n.length>be)throw new Error(`image is ${(n.length/(1024*1024)).toFixed(1)}MB \u2014 max 10MB`);return n}async function Le({fileName:t,parentNode:e,bytes:n}){let{token:r,host:o}=await N(),i=new FormData;i.set("file_name",t),i.set("parent_type","docx_image"),i.set("parent_node",e),i.set("size",String(n.length)),i.set("file",new Blob([n]),t);let s=await(await T(`${o}/open-apis/drive/v1/medias/upload_all`,{method:"POST",headers:{Authorization:`Bearer ${r}`},body:i},{kind:"transfer",what:"Lark Docx image upload"})).json();if(s.code!==0)throw new Error(`Lark media upload_all error: ${s.msg||s.code}`);let c=s.data?.file_token;if(!c)throw new Error("Lark media upload_all returned no file_token");return c}var Te="docx",Ae=50;function q(t){return[{type:"text_run",text_run:{text:String(t??"")}}]}function xe(t){return Array.isArray(t)?t.map(e=>e?.text_run?.text??e?.docs_link?.url??(e?.person?`@${e.person.user_id||""}`:"")).join(""):""}function Oe(t){let e=Array.isArray(t?.reply_list?.replies)?t.reply_list.replies:[];return{commentId:t?.comment_id||"",resolved:!!t?.is_solved,replies:e.map(n=>({replyId:n?.reply_id||"",author:n?.user_id||"",text:xe(n?.content?.elements),createTime:n?.create_time||""}))}}function E(t){return typeof t?.fileType=="string"&&t.fileType.trim()?t.fileType.trim():Te}function G(t,e,n,r){let i=String(t||"").includes("feishu")?"https://feishu.cn":"https://www.larksuite.com",a=new URLSearchParams;n&&a.set("table",n),r&&a.set("view",r);let s=a.toString();return`${i}/base/${e}${s?`?${s}`:""}`}async function z(t){let e=typeof t=="string"?h(t):t;if(!e)throw new Error("A valid Lark Base id or URL is required");let n={tableId:e.tableId||null,viewId:e.viewId||null};if(e.type!=="wiki")return{appToken:e.token,...n};let{data:r}=await u("GET",`/open-apis/wiki/v2/spaces/get_node?token=${encodeURIComponent(e.token)}`),o=r?.node||{};if(o.obj_type!=="bitable"||!o.obj_token)throw o.obj_type==="docx"?new Error("This Lark wiki link is a document (obj_type=docx), not a Base / \u591A\u7EF4\u8868\u683C \u2014 read it with larkdoc_get"):new Error(`Wiki node is not a Base / \u591A\u7EF4\u8868\u683C (obj_type=${o.obj_type||"unknown"})`);return{appToken:o.obj_token,...n}}function R(t){if(t==null)return"";if(typeof t=="string")return t;if(typeof t=="number"||typeof t=="boolean")return String(t);if(Array.isArray(t))return t.map(R).filter(e=>e!=="").join(", ");if(typeof t=="object"){let e=t;return typeof e.text=="string"&&e.text?e.text:typeof e.name=="string"&&e.name?e.name:typeof e.en_name=="string"&&e.en_name?e.en_name:typeof e.full_address=="string"&&e.full_address?e.full_address:typeof e.link=="string"&&e.link?e.link:e.value!==void 0?R(e.value):JSON.stringify(e)}return String(t)}function Ee(t,e){if(_e.has(Number(e))){let n=Number(Array.isArray(t)?t[0]:t);if(Number.isFinite(n)&&n>0)return new Date(n).toISOString()}return R(t)}async function W(t){let e=[],n=null;for(let r=0;r<v;r++){let o=new URLSearchParams({page_size:String(Y)});n&&o.set("page_token",n);let{data:i}=await u("GET",`/open-apis/bitable/v1/apps/${encodeURIComponent(t)}/tables?${o.toString()}`);for(let a of Array.isArray(i?.items)?i.items:[])e.push({tableId:String(a?.table_id||""),name:String(a?.name||"")});if(!i?.has_more||!i?.page_token)break;n=i.page_token}return e}async function Re(t,e){let n=[],r=null;for(let o=0;o<v;o++){let i=new URLSearchParams({page_size:String(Y)});r&&i.set("page_token",r);let{data:a}=await u("GET",`/open-apis/bitable/v1/apps/${encodeURIComponent(t)}/tables/${encodeURIComponent(e)}/fields?${i.toString()}`);for(let s of Array.isArray(a?.items)?a.items:[])n.push({name:String(s?.field_name||""),type:Number(s?.type)||0});if(!a?.has_more||!a?.page_token)break;r=a.page_token}return n}async function ve({appToken:t,tableId:e}){if(e)return e;let n=await W(t);if(n.length===1)return n[0].tableId;if(!n.length)throw new Error("This Lark Base has no tables");let r=n.map(o=>`${o.name} (${o.tableId})`).join("; ");throw new Error(`tableId is required \u2014 this Base has ${n.length} tables: ${r}`)}async function Ne({appToken:t,tableId:e,viewId:n,fieldNames:r,maxRecords:o,pageToken:i}){let a=await Re(t,e),s=new Map(a.map(f=>[f.name,f.type])),c=[],d=0,l=i||null,m=!1,p=0,k=!1;for(let f=0;f<v;f++){let g=new URLSearchParams({page_size:String(Math.min(me,o))});l&&g.set("page_token",l);let{data:I}=await u("POST",`/open-apis/bitable/v1/apps/${encodeURIComponent(t)}/tables/${encodeURIComponent(e)}/records/search?${g.toString()}`,{...n?{view_id:n}:{},...r?.length?{field_names:r}:{}});p=Number(I?.total)||p,m=!!I?.has_more,l=I?.page_token||null;for(let P of Array.isArray(I?.items)?I.items:[]){let U={};for(let[M,Z]of Object.entries(P?.fields||{}))U[M]=Ee(Z,s.get(M));let B={recordId:String(P?.record_id||""),fields:U},$=JSON.stringify(B).length;if(c.length&&d+$>he){k=!0;break}if(d+=$,c.push(B),c.length>=o)break}if(k||c.length>=o||!m||!l)break}return{columns:a,records:c,total:p,hasMore:(m||k)&&!!l,nextPageToken:l||"",truncated:k}}var Ge={id:"lark-docs",callsBackend:!0,serverName:"larkdocs",allowedTools:["mcp__larkdocs__*"],requiresIntegration:[_.LARK_DOCS,_.LARK],description:"Lark / Feishu Docs \u2014 read, create, append, and insert images into Lark documents (docx), and read Lark Bases (\u591A\u7EF4\u8868\u683C / bitable).",envKeys:["PROJECT_API_TOKEN","ZIBBY_ACCOUNT_API_URL","ZIBBY_ENV",...ge],promptFragment:'## Lark Docs\nYou can read, create, and append Lark/Feishu documents (docx), read/post/reply to their comments, and READ Lark Bases (\u591A\u7EF4\u8868\u683C / bitable). This runs on the connected Lark Docs app (the chat app is used when no separate docs app is connected). Each `larkdoc_*` / `larkwiki_*` / `larkbitable_*` tool documents its own params and return shape in its tool description \u2014 they are not restated here.\nA Lark link is not always a document: a `/base/\u2026` link, or a `/wiki/\u2026` link carrying `?table=tbl\u2026`, is a Base \u2014 read it with `larkbitable_read_records`, NOT `larkdoc_get` (which refuses it, since a Base has no document body). Base reads need the `bitable:app:readonly` scope on the connected app.\nThese tools return { ok:false, error } on failure \u2014 treat an unavailable Lark connection as "cannot read/deliver to Lark Docs" and continue rather than blocking the task.',resolve(){let t=le();if(!t)return null;let e={};for(let n of this.envKeys)process.env[n]&&(e[n]=process.env[n]);return{type:"stdio",command:"node",args:[t,"../dist/larkDocs.js","larkDocsSkill"],env:e,description:this.description}},async handleToolCall(t,e){try{switch(t){case"larkdoc_get":{let n=e?.documentId||e?.url||e?.id,r=h(n);if(!r)return JSON.stringify({ok:!1,error:"A valid Lark doc id or URL is required"});let o=await b(r),i="";try{i=(await u("GET",`/open-apis/docx/v1/documents/${o}`)).data?.document?.title||""}catch{}let{data:a,host:s}=await u("GET",`/open-apis/docx/v1/documents/${o}/raw_content?lang=0`),c=String(a?.content||""),d=!1;return c.length>F&&(c=c.slice(0,F),d=!0),JSON.stringify({ok:!0,documentId:o,title:i,url:w(s,o),text:c,...d?{truncated:!0}:{}})}case"larkdoc_create":{let n=typeof e?.title=="string"&&e.title.trim()?e.title.trim():null;if(!n)return JSON.stringify({ok:!1,error:"title is required"});let r=typeof e?.wikiSpaceId=="string"&&e.wikiSpaceId.trim()?e.wikiSpaceId.trim():null;if(r){let c=typeof e?.parentNodeToken=="string"&&e.parentNodeToken.trim()?e.parentNodeToken.trim():null,{data:d,host:l}=await u("POST",`/open-apis/wiki/v2/spaces/${encodeURIComponent(r)}/nodes`,{obj_type:"docx",node_type:"origin",title:n,...c?{parent_node_token:c}:{}}),m=d?.node||{},p=m.obj_token;if(!p)return JSON.stringify({ok:!1,error:"Lark wiki node create returned no obj_token"});let k=S(e);if(k&&k.trim()){let f=x(k);f.length&&await O(p,f)}return JSON.stringify({ok:!0,documentId:p,title:n,wikiSpaceId:r,wikiNodeToken:m.node_token||"",url:m.node_token?Ie(l,m.node_token):w(l,p)})}let{data:o,host:i}=await u("POST","/open-apis/docx/v1/documents",{title:n,...e?.folderToken?{folder_token:String(e.folderToken)}:{}}),a=o?.document?.document_id;if(!a)return JSON.stringify({ok:!1,error:"Lark Docs create returned no document_id"});let s=S(e);if(s&&s.trim()){let c=x(s);c.length&&await O(a,c)}return JSON.stringify({ok:!0,documentId:a,title:n,url:w(i,a)})}case"larkdoc_append":{let n=e?.documentId||e?.url||e?.id,r=h(n);if(!r)return JSON.stringify({ok:!1,error:"A valid Lark doc id or URL is required"});let o=S(e);if(!o||!o.trim())return JSON.stringify({ok:!1,error:"markdown or text content is required"});let i=await b(r),a=x(o);if(!a.length)return JSON.stringify({ok:!1,error:"no non-empty content to append"});let s=await O(i,a);return JSON.stringify({ok:!0,documentId:i,url:w(s,i)})}case"larkdoc_insert_image":{let n=e?.documentId||e?.url||e?.id,r=h(n);if(!r)return JSON.stringify({ok:!1,error:"A valid Lark doc id or URL is required"});if(!e?.imagePath||typeof e.imagePath!="string"||!e.imagePath.trim())return JSON.stringify({ok:!1,error:"imagePath is required"});let o=await b(r),i=e.imagePath.trim(),a=Se(i),s=se(i),d=(await u("POST",`/open-apis/docx/v1/documents/${o}/blocks/${o}/children?document_revision_id=-1`,{children:[{block_type:27,image:{}}]})).data?.children?.[0]?.block_id;if(!d)return JSON.stringify({ok:!1,error:"Lark image block create returned no block_id"});let l=await Le({fileName:s,parentNode:d,bytes:a}),m={token:l},p=Number(e?.width),k=Number(e?.height);Number.isFinite(p)&&p>0&&(m.width=Math.round(p)),Number.isFinite(k)&&k>0&&(m.height=Math.round(k));let{host:f}=await u("PATCH",`/open-apis/docx/v1/documents/${o}/blocks/${d}?document_revision_id=-1`,{replace_image:m});return JSON.stringify({ok:!0,documentId:o,blockId:d,fileToken:l,url:w(f,o)})}case"larkwiki_list_spaces":{let n=[],r=null;for(let o=0;o<ue;o++){let i=new URLSearchParams({page_size:String(pe)});r&&i.set("page_token",r);let{data:a}=await u("GET",`/open-apis/wiki/v2/spaces?${i.toString()}`),s=Array.isArray(a?.items)?a.items:[];for(let c of s)n.push({spaceId:String(c?.space_id||""),name:String(c?.name||"")});if(!a?.has_more||!a?.page_token)break;r=a.page_token}return JSON.stringify({ok:!0,count:n.length,spaces:n})}case"larkbitable_list_tables":{let n=e?.baseId||e?.url||e?.documentId||e?.id,r=h(n);if(!r)return JSON.stringify({ok:!1,error:"A valid Lark Base id or URL is required"});let{appToken:o,tableId:i,viewId:a}=await z(r),s=await W(o),c="",d="";try{let l=await u("GET",`/open-apis/bitable/v1/apps/${encodeURIComponent(o)}`);c=l.data?.app?.name||"",d=l.host}catch{}return JSON.stringify({ok:!0,appToken:o,name:c,...d?{url:G(d,o,i,a)}:{},...i?{linkedTableId:i}:{},...a?{linkedViewId:a}:{},count:s.length,tables:s})}case"larkbitable_read_records":{let n=e?.baseId||e?.url||e?.documentId||e?.id,r=h(n);if(!r)return JSON.stringify({ok:!1,error:"A valid Lark Base id or URL is required"});let o=await z(r),i=o.appToken,a=await ve({appToken:i,tableId:typeof e?.tableId=="string"&&e.tableId.trim()?e.tableId.trim():o.tableId}),s=typeof e?.viewId=="string"&&e.viewId.trim()?e.viewId.trim():o.viewId,c=Array.isArray(e?.fieldNames)?e.fieldNames.map(g=>String(g)).filter(Boolean):null,d=Number(e?.maxRecords),l=Number.isFinite(d)&&d>0?Math.min(Math.round(d),fe):ke,m=typeof e?.pageToken=="string"&&e.pageToken.trim()?e.pageToken.trim():null,p=await Ne({appToken:i,tableId:a,viewId:s,fieldNames:c,maxRecords:l,pageToken:m}),{host:k}=await N(),f="";try{f=(await u("GET",`/open-apis/bitable/v1/apps/${encodeURIComponent(i)}`)).data?.app?.name||""}catch{}return JSON.stringify({ok:!0,appToken:i,name:f,tableId:a,...s?{viewId:s}:{},url:G(k,i,a,s),total:p.total,count:p.records.length,columns:p.columns,records:p.records,hasMore:p.hasMore,...p.nextPageToken?{nextPageToken:p.nextPageToken}:{},...p.truncated?{truncated:!0}:{}})}case"larkdoc_list_comments":{let n=e?.documentId||e?.url||e?.id||e?.fileToken,r=h(n);if(!r)return JSON.stringify({ok:!1,error:"A valid Lark doc id or URL is required"});let o=await b(r),i=E(e),a=new URLSearchParams({file_type:i,page_size:String(Ae)}),{data:s}=await u("GET",`/open-apis/drive/v1/files/${o}/comments?${a.toString()}`),d=(Array.isArray(s?.items)?s.items:[]).map(Oe);return JSON.stringify({ok:!0,documentId:o,count:d.length,comments:d})}case"larkdoc_add_comment":{let n=e?.documentId||e?.url||e?.id||e?.fileToken,r=h(n);if(!r)return JSON.stringify({ok:!1,error:"A valid Lark doc id or URL is required"});let o=S({text:e?.text??e?.body});if(!o||!o.trim())return JSON.stringify({ok:!1,error:"text is required"});let i=await b(r),a=E(e),{data:s}=await u("POST",`/open-apis/drive/v1/files/${i}/comments?file_type=${encodeURIComponent(a)}`,{reply_list:{replies:[{content:{elements:q(o)}}]}});return JSON.stringify({ok:!0,documentId:i,commentId:s?.comment_id||""})}case"larkdoc_reply_comment":{let n=e?.documentId||e?.url||e?.id||e?.fileToken,r=h(n);if(!r)return JSON.stringify({ok:!1,error:"A valid Lark doc id or URL is required"});let o=e?.commentId||e?.comment_id;if(!o)return JSON.stringify({ok:!1,error:"commentId is required"});let i=S({text:e?.text??e?.body});if(!i||!i.trim())return JSON.stringify({ok:!1,error:"text is required"});let a=await b(r),s=E(e),{data:c}=await u("POST",`/open-apis/drive/v1/files/${a}/comments/${encodeURIComponent(o)}/replies?file_type=${encodeURIComponent(s)}`,{content:{elements:q(i)}});return JSON.stringify({ok:!0,documentId:a,commentId:String(o),replyId:c?.reply_id||""})}default:return JSON.stringify({ok:!1,error:`Unknown tool: ${t}`})}}catch(n){return JSON.stringify({ok:!1,error:n.message})}},tools:[{name:"larkdoc_get",description:"Read a Lark/Feishu document (docx) as plain text, for use as reference context. Accepts a raw doc id OR a full Lark doc URL (a /docx/ or /wiki/ link \u2014 wiki links are resolved to their backing docx). Returns { ok, documentId, title, url, text }. Text is truncated to ~20k chars.",input_schema:{type:"object",properties:{documentId:{type:"string",description:"Lark doc id (docx token) OR a full Lark/Feishu doc URL (/docx/\u2026 or /wiki/\u2026)."}},required:["documentId"]}},{name:"larkdoc_create",description:"Create a new Lark/Feishu document (docx) with a title and optional content (markdown: #/##/### headings, - bullets, 1. ordered; or plain text). Pass wikiSpaceId (from larkwiki_list_spaces) to create the doc INSIDE a wiki space instead of as a standalone doc \u2014 optionally nested under parentNodeToken. Returns { ok, documentId, url } (plus wikiNodeToken when created in a wiki) \u2014 share the url with the user.",input_schema:{type:"object",properties:{title:{type:"string",description:"Document title."},markdown:{type:"string",description:"Document body as markdown (preferred)."},text:{type:"string",description:"Document body as plain text (used when markdown is absent)."},folderToken:{type:"string",description:"Optional Lark drive folder token to create the doc in. Absent = the app root. Ignored when wikiSpaceId is set."},wikiSpaceId:{type:"string",description:"Optional wiki space id (from larkwiki_list_spaces) \u2014 create the doc as a node inside this wiki space."},parentNodeToken:{type:"string",description:"Optional wiki node token to nest the new doc under (only with wikiSpaceId). Absent = the space root."}},required:["title"]}},{name:"larkwiki_list_spaces",description:"List the Lark/Feishu wiki spaces visible to the connected app. Returns { ok, count, spaces:[{ spaceId, name }] }. Use to discover the wikiSpaceId for larkdoc_create.",input_schema:{type:"object",properties:{}}},{name:"larkdoc_append",description:"Append markdown/text content to the END of an existing Lark/Feishu document (docx). Accepts a documentId or a full Lark doc URL. Returns { ok, documentId, url }.",input_schema:{type:"object",properties:{documentId:{type:"string",description:"Lark doc id (docx token) OR a full Lark/Feishu doc URL."},markdown:{type:"string",description:"Content to append, as markdown (preferred)."},text:{type:"string",description:"Content to append, as plain text (used when markdown is absent)."}},required:["documentId"]}},{name:"larkdoc_insert_image",description:"Append a LOCAL image file (png/jpg, max 10MB) to the END of an existing Lark/Feishu document (docx). Accepts a documentId or full doc URL plus a local file path. Optional width/height in pixels. Returns { ok, documentId, blockId, fileToken, url }.",input_schema:{type:"object",properties:{documentId:{type:"string",description:"Lark doc id (docx token) OR a full Lark/Feishu doc URL."},imagePath:{type:"string",description:"Local filesystem path to a .png or .jpg/.jpeg image (max 10MB)."},width:{type:"number",description:"Optional display width in pixels."},height:{type:"number",description:"Optional display height in pixels."}},required:["documentId","imagePath"]}},{name:"larkbitable_list_tables",description:"List the tables in a Lark/Feishu Base (\u591A\u7EF4\u8868\u683C / bitable). Accepts a Base URL (a /base/\u2026 link, or a /wiki/\u2026 link whose node IS a Base) or a raw app token. Returns { ok, appToken, name, tables:[{ tableId, name }] } plus linkedTableId/linkedViewId when the URL pinned them. A Base is NOT a document \u2014 larkdoc_get cannot read one; use this and larkbitable_read_records instead.",input_schema:{type:"object",properties:{baseId:{type:"string",description:"Lark Base app token OR a full Base/wiki URL (/base/\u2026 or /wiki/\u2026)."}},required:["baseId"]}},{name:"larkbitable_read_records",description:"Read the rows of one table in a Lark/Feishu Base (\u591A\u7EF4\u8868\u683C / bitable) as flat text. Accepts a Base URL (/base/\u2026 or a /wiki/\u2026 link fronting a Base) or app token; the table and view are taken from the URL's table=/view= when present, and tableId may be omitted when the Base has exactly one table. Optionally restrict to viewId or fieldNames. Returns { ok, appToken, name, tableId, total, count, columns:[{name,type}], records:[{ recordId, fields:{ <column>: <text> } }], hasMore, nextPageToken }. Every cell is a STRING (dates as ISO). Defaults to 200 records; pass maxRecords (max 1000) and pageToken to page through a bigger table.",input_schema:{type:"object",properties:{baseId:{type:"string",description:"Lark Base app token OR a full Base/wiki URL (/base/\u2026 or /wiki/\u2026)."},tableId:{type:"string",description:"Table id (tbl\u2026). Optional when the URL carries table= or the Base has exactly one table; larkbitable_list_tables lists them."},viewId:{type:"string",description:"Optional view id (vew\u2026) \u2014 restricts the read to that view's rows and order."},fieldNames:{type:"array",items:{type:"string"},description:"Optional column names to return. Absent = every column."},maxRecords:{type:"number",description:"Max records to return (default 200, hard cap 1000)."},pageToken:{type:"string",description:"Resume token from a previous call's nextPageToken."}},required:["baseId"]}},{name:"larkdoc_list_comments",description:"List the comment threads on a Lark/Feishu document. Accepts a documentId or full doc URL. Returns { ok, comments:[{ commentId, replies:[{ replyId, author, text }] }] }. Use to read the thread you are replying to.",input_schema:{type:"object",properties:{documentId:{type:"string",description:"Lark doc id (docx token) OR a full Lark/Feishu doc URL."},fileType:{type:"string",description:"Drive file type \u2014 defaults to 'docx'. Only change for non-docx files (doc/sheet/bitable/file/slides)."}},required:["documentId"]}},{name:"larkdoc_add_comment",description:"Post a NEW top-level comment on a Lark/Feishu document. Accepts a documentId or full doc URL plus text. Returns { ok, documentId, commentId }.",input_schema:{type:"object",properties:{documentId:{type:"string",description:"Lark doc id (docx token) OR a full Lark/Feishu doc URL."},text:{type:"string",description:"The comment body (plain text)."},fileType:{type:"string",description:"Drive file type \u2014 defaults to 'docx'."}},required:["documentId","text"]}},{name:"larkdoc_reply_comment",description:"Reply INSIDE an existing comment thread on a Lark/Feishu document. Pass { documentId, commentId, text }. Use to answer a user who @mentioned Zibby in a doc comment (reply in the same commentId). Returns { ok, documentId, commentId, replyId }.",input_schema:{type:"object",properties:{documentId:{type:"string",description:"Lark doc id (docx token) OR a full Lark/Feishu doc URL."},commentId:{type:"string",description:"The comment_id of the thread to reply into (from larkdoc_list_comments or the webhook event)."},text:{type:"string",description:"The reply body (plain text)."},fileType:{type:"string",description:"Drive file type \u2014 defaults to 'docx'."}},required:["documentId","commentId","text"]}}]};function ze(){y=null}export{ge as LARK_DOCS_APP_ENV_KEYS,ze as _resetLarkDocsTokenCache,G as baseWebUrl,w as docWebUrl,Ge as larkDocsSkill,x as markdownToLarkBlocks,h as parseLarkDocRef,Ie as wikiWebUrl};
3
+ `)){let o=r.replace(/\s+$/,"");if(!o.trim())continue;let a=/^(#{1,3})\s+(.*)$/.exec(o),i=/^\s*[-*]\s+(.*)$/.exec(o),s=/^\s*\d+[.)]\s+(.*)$/.exec(o),c,d,l;if(a){let u=a[1].length;c=`heading${u}`,d=2+u,l=a[2]}else i?(c="bullet",d=12,l=i[1]):s?(c="ordered",d=13,l=s[1]):(c="text",d=2,l=o);n.push({block_type:d,[c]:{elements:[{text_run:{content:l}}],style:{}}})}return n}function A(t){let e=typeof t?.markdown=="string"?t.markdown:null,n=typeof t?.text=="string"?t.text:null;return e??n}async function P(t,e){let n;for(let r=0;r<e.length;r+=W){let o=e.slice(r,r+W);n=(await m("POST",`/open-apis/docx/v1/documents/${t}/blocks/${t}/children?document_revision_id=-1`,{children:o})).host}return n}function Ne(t){let e=typeof t=="string"?t.trim():"";if(!e)throw new Error("imagePath is required");if(!ee(e)||!pe(e).isFile())throw new Error(`imagePath not found (or not a file): ${e}`);if(!/\.(png|jpe?g)$/i.test(e))throw new Error("imagePath must be a .png or .jpg/.jpeg file");let n=ue(e);if(n.length>Le)throw new Error(`image is ${(n.length/(1024*1024)).toFixed(1)}MB \u2014 max 10MB`);return n}async function Re({fileName:t,parentNode:e,bytes:n}){let{token:r,host:o}=await $(),a=new FormData;a.set("file_name",t),a.set("parent_type","docx_image"),a.set("parent_node",e),a.set("size",String(n.length)),a.set("file",new Blob([n]),t);let s=await(await O(`${o}/open-apis/drive/v1/medias/upload_all`,{method:"POST",headers:{Authorization:`Bearer ${r}`},body:a},{kind:"transfer",what:"Lark Docx image upload"})).json();if(s.code!==0)throw new Error(`Lark media upload_all error: ${s.msg||s.code}`);let c=s.data?.file_token;if(!c)throw new Error("Lark media upload_all returned no file_token");return c}var ve="docx",Pe=50;function Y(t){return[{type:"text_run",text_run:{text:String(t??"")}}]}function Be(t){return Array.isArray(t)?t.map(e=>e?.text_run?.text??e?.docs_link?.url??(e?.person?`@${e.person.user_id||""}`:"")).join(""):""}function Me(t){let e=Array.isArray(t?.reply_list?.replies)?t.reply_list.replies:[];return{commentId:t?.comment_id||"",resolved:!!t?.is_solved,replies:e.map(n=>({replyId:n?.reply_id||"",author:n?.user_id||"",text:Be(n?.content?.elements),createTime:n?.create_time||""}))}}function B(t){return typeof t?.fileType=="string"&&t.fileType.trim()?t.fileType.trim():ve}function V(t,e,n,r){let a=String(t||"").includes("feishu")?"https://feishu.cn":"https://www.larksuite.com",i=new URLSearchParams;n&&i.set("table",n),r&&i.set("view",r);let s=i.toString();return`${a}/base/${e}${s?`?${s}`:""}`}async function X(t){let e=typeof t=="string"?y(t):t;if(!e)throw new Error("A valid Lark Base id or URL is required");let n={tableId:e.tableId||null,viewId:e.viewId||null};if(e.type!=="wiki")return{appToken:e.token,...n};let{data:r}=await m("GET",`/open-apis/wiki/v2/spaces/get_node?token=${encodeURIComponent(e.token)}`),o=r?.node||{};if(o.obj_type!=="bitable"||!o.obj_token)throw o.obj_type==="docx"?new Error("This Lark wiki link is a document (obj_type=docx), not a Base / \u591A\u7EF4\u8868\u683C \u2014 read it with larkdoc_get"):new Error(`Wiki node is not a Base / \u591A\u7EF4\u8868\u683C (obj_type=${o.obj_type||"unknown"})`);return{appToken:o.obj_token,...n}}function E(t){if(t==null)return"";if(typeof t=="string")return t;if(typeof t=="number"||typeof t=="boolean")return String(t);if(Array.isArray(t))return t.map(E).filter(e=>e!=="").join(", ");if(typeof t=="object"){let e=t;return typeof e.text=="string"&&e.text?e.text:typeof e.name=="string"&&e.name?e.name:typeof e.en_name=="string"&&e.en_name?e.en_name:typeof e.full_address=="string"&&e.full_address?e.full_address:typeof e.link=="string"&&e.link?e.link:e.value!==void 0?E(e.value):JSON.stringify(e)}return String(t)}function Ue(t,e){if(Te.has(Number(e))){let n=Number(Array.isArray(t)?t[0]:t);if(Number.isFinite(n)&&n>0)return new Date(n).toISOString()}return E(t)}async function ne(t){let e=[],n=null;for(let r=0;r<U;r++){let o=new URLSearchParams({page_size:String(te)});n&&o.set("page_token",n);let{data:a}=await m("GET",`/open-apis/bitable/v1/apps/${encodeURIComponent(t)}/tables?${o.toString()}`);for(let i of Array.isArray(a?.items)?a.items:[])e.push({tableId:String(i?.table_id||""),name:String(i?.name||"")});if(!a?.has_more||!a?.page_token)break;n=a.page_token}return e}async function $e(t,e){let n=[],r=null;for(let o=0;o<U;o++){let a=new URLSearchParams({page_size:String(te)});r&&a.set("page_token",r);let{data:i}=await m("GET",`/open-apis/bitable/v1/apps/${encodeURIComponent(t)}/tables/${encodeURIComponent(e)}/fields?${a.toString()}`);for(let s of Array.isArray(i?.items)?i.items:[])n.push({name:String(s?.field_name||""),type:Number(s?.type)||0});if(!i?.has_more||!i?.page_token)break;r=i.page_token}return n}async function De({appToken:t,tableId:e}){if(e)return e;let n=await ne(t);if(n.length===1)return n[0].tableId;if(!n.length)throw new Error("This Lark Base has no tables");let r=n.map(o=>`${o.name} (${o.tableId})`).join("; ");throw new Error(`tableId is required \u2014 this Base has ${n.length} tables: ${r}`)}var Z=new Set(["is","isNot","contains","doesNotContain","isEmpty","isNotEmpty","isGreater","isGreaterEqual","isLess","isLessEqual"]),Q=new Set(["isEmpty","isNotEmpty"]);function M(t,e,n){let r=new Set(t.map(i=>i.name)),o=e.filter(i=>!r.has(i));if(!o.length)return;let a=o.flatMap(i=>t.filter(s=>s.name.trim()===String(i).trim()).map(s=>JSON.stringify(s.name)));throw new Error(`unknown column(s) in ${n}: ${o.map(i=>JSON.stringify(i)).join(", ")}`+(a.length?` \u2014 did you mean ${a.join(", ")}? (names can carry spaces)`:"")+`. This table's columns: ${t.map(i=>JSON.stringify(i.name)).join(", ")}`)}function je({columns:t,viewId:e,fieldNames:n,filter:r,sort:o,includeRowMeta:a}){let i={};if(e&&(i.view_id=e),n?.length&&(M(t,n,"fieldNames"),i.field_names=n),r){let s=Array.isArray(r.conditions)?r.conditions:[];if(!s.length)throw new Error("filter.conditions must be a non-empty array");M(t,s.map(c=>c?.fieldName??c?.field_name),"filter"),i.filter={conjunction:r.conjunction==="or"?"or":"and",conditions:s.map(c=>{let d=String(c?.operator||"is");if(!Z.has(d))throw new Error(`unknown filter operator ${JSON.stringify(d)} \u2014 Lark accepts: ${[...Z].join(", ")}`);let l=c?.value,u=l==null?[]:(Array.isArray(l)?l:[l]).map(p=>String(p));if(!Q.has(d)&&!u.length)throw new Error(`filter operator ${JSON.stringify(d)} requires a value`);return{field_name:String(c?.fieldName??c?.field_name),operator:d,...Q.has(d)?{}:{value:u}}})}}return o?.length&&(M(t,o.map(s=>s?.fieldName??s?.field_name),"sort"),i.sort=o.map(s=>({field_name:String(s?.fieldName??s?.field_name),desc:!!s?.desc}))),a&&(i.automatic_fields=!0),i}function Ce(t){let e={},n=s=>{let c=Number(s);return!Number.isFinite(c)||c<=0?"":new Date(c<1e11?c*1e3:c).toISOString()},r=n(t?.created_time),o=n(t?.last_modified_time),a=E(t?.created_by),i=E(t?.last_modified_by);return r&&(e.createdTime=r),a&&(e.createdBy=a),o&&(e.lastModifiedTime=o),i&&(e.lastModifiedBy=i),e}async function qe({appToken:t,tableId:e,viewId:n,fieldNames:r,filter:o,sort:a,includeRowMeta:i,maxRecords:s,pageToken:c}){let d=await $e(t,e),l=new Map(d.map(b=>[b.name,b.type])),u=je({columns:d,viewId:n,fieldNames:r,filter:o,sort:a,includeRowMeta:i}),p=[],h=0,k=c||null,f=!1,S=0,_=!1;for(let b=0;b<U;b++){let D=new URLSearchParams({page_size:String(Math.min(we,s))});k&&D.set("page_token",k);let{data:T}=await m("POST",`/open-apis/bitable/v1/apps/${encodeURIComponent(t)}/tables/${encodeURIComponent(e)}/records/search?${D.toString()}`,u);S=Number(T?.total)||S,f=!!T?.has_more,k=T?.page_token||null;for(let N of Array.isArray(T?.items)?T.items:[]){let j={};for(let[K,oe]of Object.entries(N?.fields||{}))j[K]=Ue(oe,l.get(K));let C={recordId:String(N?.record_id||""),...i?Ce(N):{},fields:j},q=JSON.stringify(C).length;if(p.length&&h+q>Se){_=!0;break}if(h+=q,p.push(C),p.length>=s)break}if(_||p.length>=s||!f||!k)break}return{columns:d,records:p,total:S,hasMore:f||_,nextPageToken:k||"",truncated:_,..._&&!k?{note:"stopped at the character budget, and these rows have no resume token \u2014 re-read with fieldNames set to the few columns you need (see columns[])."}:{}}}var et={id:"lark-docs",callsBackend:!0,serverName:"larkdocs",allowedTools:["mcp__larkdocs__*"],requiresIntegration:[w.LARK_DOCS,w.LARK],description:"Lark / Feishu Docs \u2014 read, create, append, and insert images into Lark documents (docx), and read Lark Bases (\u591A\u7EF4\u8868\u683C / bitable).",envKeys:["PROJECT_API_TOKEN","ZIBBY_ACCOUNT_API_URL","ZIBBY_ENV",...Ee],promptFragment:'## Lark Docs\nYou can read, create, and append Lark/Feishu documents (docx), read/post/reply to their comments, and READ Lark Bases (\u591A\u7EF4\u8868\u683C / bitable). This runs on the connected Lark Docs app (the chat app is used when no separate docs app is connected). Each `larkdoc_*` / `larkwiki_*` / `larkbitable_*` tool documents its own params and return shape in its tool description \u2014 they are not restated here.\nA Lark link is not always a document: a `/base/\u2026` link, or a `/wiki/\u2026` link carrying `?table=tbl\u2026`, is a Base \u2014 read it with `larkbitable_read_records`, NOT `larkdoc_get` (which refuses it, since a Base has no document body). Base reads need the `bitable:app:readonly` scope on the connected app.\nA Base question about CHANGE \u2014 what moved since yesterday, who edited a row, which rows are stale \u2014 is answered by `larkbitable_read_records` with `includeRowMeta:true` (per-row created/last-modified time and person) and a `filter`/`sort` on that timestamp, NOT by reading the whole table twice and diffing. Lark exposes no per-cell edit history to any API, so a field\'s PREVIOUS value cannot be fetched \u2014 say so rather than inferring it from snapshots.\nThese tools return { ok:false, error } on failure \u2014 treat an unavailable Lark connection as "cannot read/deliver to Lark Docs" and continue rather than blocking the task.',resolve(){let t=ye();if(!t)return null;let e={};for(let n of this.envKeys)process.env[n]&&(e[n]=process.env[n]);return{type:"stdio",command:"node",args:[t,"../dist/larkDocs.js","larkDocsSkill"],env:e,description:this.description}},async handleToolCall(t,e){try{switch(t){case"larkdoc_get":{let n=e?.documentId||e?.url||e?.id,r=y(n);if(!r)return JSON.stringify({ok:!1,error:"A valid Lark doc id or URL is required"});let o=await g(r),a="";try{a=(await m("GET",`/open-apis/docx/v1/documents/${o}`)).data?.document?.title||""}catch{}let{data:i,host:s}=await m("GET",`/open-apis/docx/v1/documents/${o}/raw_content?lang=0`),c=String(i?.content||""),d=!1;return c.length>H&&(c=c.slice(0,H),d=!0),JSON.stringify({ok:!0,documentId:o,title:a,url:L(s,o),text:c,...d?{truncated:!0}:{}})}case"larkdoc_create":{let n=typeof e?.title=="string"&&e.title.trim()?e.title.trim():null;if(!n)return JSON.stringify({ok:!1,error:"title is required"});let r=typeof e?.wikiSpaceId=="string"&&e.wikiSpaceId.trim()?e.wikiSpaceId.trim():null;if(r){let c=typeof e?.parentNodeToken=="string"&&e.parentNodeToken.trim()?e.parentNodeToken.trim():null,{data:d,host:l}=await m("POST",`/open-apis/wiki/v2/spaces/${encodeURIComponent(r)}/nodes`,{obj_type:"docx",node_type:"origin",title:n,...c?{parent_node_token:c}:{}}),u=d?.node||{},p=u.obj_token;if(!p)return JSON.stringify({ok:!1,error:"Lark wiki node create returned no obj_token"});let h=A(e);if(h&&h.trim()){let k=v(h);k.length&&await P(p,k)}return JSON.stringify({ok:!0,documentId:p,title:n,wikiSpaceId:r,wikiNodeToken:u.node_token||"",url:u.node_token?xe(l,u.node_token):L(l,p)})}let{data:o,host:a}=await m("POST","/open-apis/docx/v1/documents",{title:n,...e?.folderToken?{folder_token:String(e.folderToken)}:{}}),i=o?.document?.document_id;if(!i)return JSON.stringify({ok:!1,error:"Lark Docs create returned no document_id"});let s=A(e);if(s&&s.trim()){let c=v(s);c.length&&await P(i,c)}return JSON.stringify({ok:!0,documentId:i,title:n,url:L(a,i)})}case"larkdoc_append":{let n=e?.documentId||e?.url||e?.id,r=y(n);if(!r)return JSON.stringify({ok:!1,error:"A valid Lark doc id or URL is required"});let o=A(e);if(!o||!o.trim())return JSON.stringify({ok:!1,error:"markdown or text content is required"});let a=await g(r),i=v(o);if(!i.length)return JSON.stringify({ok:!1,error:"no non-empty content to append"});let s=await P(a,i);return JSON.stringify({ok:!0,documentId:a,url:L(s,a)})}case"larkdoc_insert_image":{let n=e?.documentId||e?.url||e?.id,r=y(n);if(!r)return JSON.stringify({ok:!1,error:"A valid Lark doc id or URL is required"});if(!e?.imagePath||typeof e.imagePath!="string"||!e.imagePath.trim())return JSON.stringify({ok:!1,error:"imagePath is required"});let o=await g(r),a=e.imagePath.trim(),i=Ne(a),s=fe(a),d=(await m("POST",`/open-apis/docx/v1/documents/${o}/blocks/${o}/children?document_revision_id=-1`,{children:[{block_type:27,image:{}}]})).data?.children?.[0]?.block_id;if(!d)return JSON.stringify({ok:!1,error:"Lark image block create returned no block_id"});let l=await Re({fileName:s,parentNode:d,bytes:i}),u={token:l},p=Number(e?.width),h=Number(e?.height);Number.isFinite(p)&&p>0&&(u.width=Math.round(p)),Number.isFinite(h)&&h>0&&(u.height=Math.round(h));let{host:k}=await m("PATCH",`/open-apis/docx/v1/documents/${o}/blocks/${d}?document_revision_id=-1`,{replace_image:u});return JSON.stringify({ok:!0,documentId:o,blockId:d,fileToken:l,url:L(k,o)})}case"larkwiki_list_spaces":{let n=[],r=null;for(let o=0;o<be;o++){let a=new URLSearchParams({page_size:String(_e)});r&&a.set("page_token",r);let{data:i}=await m("GET",`/open-apis/wiki/v2/spaces?${a.toString()}`),s=Array.isArray(i?.items)?i.items:[];for(let c of s)n.push({spaceId:String(c?.space_id||""),name:String(c?.name||"")});if(!i?.has_more||!i?.page_token)break;r=i.page_token}return JSON.stringify({ok:!0,count:n.length,spaces:n})}case"larkbitable_list_tables":{let n=e?.baseId||e?.url||e?.documentId||e?.id,r=y(n);if(!r)return JSON.stringify({ok:!1,error:"A valid Lark Base id or URL is required"});let{appToken:o,tableId:a,viewId:i}=await X(r),s=await ne(o),c="",d="";try{let l=await m("GET",`/open-apis/bitable/v1/apps/${encodeURIComponent(o)}`);c=l.data?.app?.name||"",d=l.host}catch{}return JSON.stringify({ok:!0,appToken:o,name:c,...d?{url:V(d,o,a,i)}:{},...a?{linkedTableId:a}:{},...i?{linkedViewId:i}:{},count:s.length,tables:s})}case"larkbitable_read_records":{let n=e?.baseId||e?.url||e?.documentId||e?.id,r=y(n);if(!r)return JSON.stringify({ok:!1,error:"A valid Lark Base id or URL is required"});let o=await X(r),a=o.appToken,i=await De({appToken:a,tableId:typeof e?.tableId=="string"&&e.tableId.trim()?e.tableId.trim():o.tableId}),s=typeof e?.viewId=="string"&&e.viewId.trim()?e.viewId.trim():o.viewId,c=Array.isArray(e?.fieldNames)?e.fieldNames.map(b=>String(b)).filter(Boolean):null,d=Number(e?.maxRecords),l=Number.isFinite(d)&&d>0?Math.min(Math.round(d),Ie):ge,u=typeof e?.pageToken=="string"&&e.pageToken.trim()?e.pageToken.trim():null,p=e?.filter&&typeof e.filter=="object"?e.filter:null,h=Array.isArray(e?.sort)&&e.sort.length?e.sort:null,k=!!e?.includeRowMeta,f=await qe({appToken:a,tableId:i,viewId:s,fieldNames:c,filter:p,sort:h,includeRowMeta:k,maxRecords:l,pageToken:u}),{host:S}=await $(),_="";try{_=(await m("GET",`/open-apis/bitable/v1/apps/${encodeURIComponent(a)}`)).data?.app?.name||""}catch{}return JSON.stringify({ok:!0,appToken:a,name:_,tableId:i,...s?{viewId:s}:{},url:V(S,a,i,s),total:f.total,...p?{filtered:!0}:{},count:f.records.length,columns:f.columns,records:f.records,hasMore:f.hasMore,...f.nextPageToken?{nextPageToken:f.nextPageToken}:{},...f.truncated?{truncated:!0}:{},...f.note?{note:f.note}:{}})}case"larkdoc_list_comments":{let n=e?.documentId||e?.url||e?.id||e?.fileToken,r=y(n);if(!r)return JSON.stringify({ok:!1,error:"A valid Lark doc id or URL is required"});let o=await g(r),a=B(e),i=new URLSearchParams({file_type:a,page_size:String(Pe)}),{data:s}=await m("GET",`/open-apis/drive/v1/files/${o}/comments?${i.toString()}`),d=(Array.isArray(s?.items)?s.items:[]).map(Me);return JSON.stringify({ok:!0,documentId:o,count:d.length,comments:d})}case"larkdoc_add_comment":{let n=e?.documentId||e?.url||e?.id||e?.fileToken,r=y(n);if(!r)return JSON.stringify({ok:!1,error:"A valid Lark doc id or URL is required"});let o=A({text:e?.text??e?.body});if(!o||!o.trim())return JSON.stringify({ok:!1,error:"text is required"});let a=await g(r),i=B(e),{data:s}=await m("POST",`/open-apis/drive/v1/files/${a}/comments?file_type=${encodeURIComponent(i)}`,{reply_list:{replies:[{content:{elements:Y(o)}}]}});return JSON.stringify({ok:!0,documentId:a,commentId:s?.comment_id||""})}case"larkdoc_reply_comment":{let n=e?.documentId||e?.url||e?.id||e?.fileToken,r=y(n);if(!r)return JSON.stringify({ok:!1,error:"A valid Lark doc id or URL is required"});let o=e?.commentId||e?.comment_id;if(!o)return JSON.stringify({ok:!1,error:"commentId is required"});let a=A({text:e?.text??e?.body});if(!a||!a.trim())return JSON.stringify({ok:!1,error:"text is required"});let i=await g(r),s=B(e),{data:c}=await m("POST",`/open-apis/drive/v1/files/${i}/comments/${encodeURIComponent(o)}/replies?file_type=${encodeURIComponent(s)}`,{content:{elements:Y(a)}});return JSON.stringify({ok:!0,documentId:i,commentId:String(o),replyId:c?.reply_id||""})}default:return JSON.stringify({ok:!1,error:`Unknown tool: ${t}`})}}catch(n){return JSON.stringify({ok:!1,error:n.message})}},tools:[{name:"larkdoc_get",description:"Read a Lark/Feishu document (docx) as plain text, for use as reference context. Accepts a raw doc id OR a full Lark doc URL (a /docx/ or /wiki/ link \u2014 wiki links are resolved to their backing docx). Returns { ok, documentId, title, url, text }. Text is truncated to ~20k chars.",input_schema:{type:"object",properties:{documentId:{type:"string",description:"Lark doc id (docx token) OR a full Lark/Feishu doc URL (/docx/\u2026 or /wiki/\u2026)."}},required:["documentId"]}},{name:"larkdoc_create",description:"Create a new Lark/Feishu document (docx) with a title and optional content (markdown: #/##/### headings, - bullets, 1. ordered; or plain text). Pass wikiSpaceId (from larkwiki_list_spaces) to create the doc INSIDE a wiki space instead of as a standalone doc \u2014 optionally nested under parentNodeToken. Returns { ok, documentId, url } (plus wikiNodeToken when created in a wiki) \u2014 share the url with the user.",input_schema:{type:"object",properties:{title:{type:"string",description:"Document title."},markdown:{type:"string",description:"Document body as markdown (preferred)."},text:{type:"string",description:"Document body as plain text (used when markdown is absent)."},folderToken:{type:"string",description:"Optional Lark drive folder token to create the doc in. Absent = the app root. Ignored when wikiSpaceId is set."},wikiSpaceId:{type:"string",description:"Optional wiki space id (from larkwiki_list_spaces) \u2014 create the doc as a node inside this wiki space."},parentNodeToken:{type:"string",description:"Optional wiki node token to nest the new doc under (only with wikiSpaceId). Absent = the space root."}},required:["title"]}},{name:"larkwiki_list_spaces",description:"List the Lark/Feishu wiki spaces visible to the connected app. Returns { ok, count, spaces:[{ spaceId, name }] }. Use to discover the wikiSpaceId for larkdoc_create.",input_schema:{type:"object",properties:{}}},{name:"larkdoc_append",description:"Append markdown/text content to the END of an existing Lark/Feishu document (docx). Accepts a documentId or a full Lark doc URL. Returns { ok, documentId, url }.",input_schema:{type:"object",properties:{documentId:{type:"string",description:"Lark doc id (docx token) OR a full Lark/Feishu doc URL."},markdown:{type:"string",description:"Content to append, as markdown (preferred)."},text:{type:"string",description:"Content to append, as plain text (used when markdown is absent)."}},required:["documentId"]}},{name:"larkdoc_insert_image",description:"Append a LOCAL image file (png/jpg, max 10MB) to the END of an existing Lark/Feishu document (docx). Accepts a documentId or full doc URL plus a local file path. Optional width/height in pixels. Returns { ok, documentId, blockId, fileToken, url }.",input_schema:{type:"object",properties:{documentId:{type:"string",description:"Lark doc id (docx token) OR a full Lark/Feishu doc URL."},imagePath:{type:"string",description:"Local filesystem path to a .png or .jpg/.jpeg image (max 10MB)."},width:{type:"number",description:"Optional display width in pixels."},height:{type:"number",description:"Optional display height in pixels."}},required:["documentId","imagePath"]}},{name:"larkbitable_list_tables",description:"List the tables in a Lark/Feishu Base (\u591A\u7EF4\u8868\u683C / bitable). Accepts a Base URL (a /base/\u2026 link, or a /wiki/\u2026 link whose node IS a Base) or a raw app token. Returns { ok, appToken, name, tables:[{ tableId, name }] } plus linkedTableId/linkedViewId when the URL pinned them. A Base is NOT a document \u2014 larkdoc_get cannot read one; use this and larkbitable_read_records instead.",input_schema:{type:"object",properties:{baseId:{type:"string",description:"Lark Base app token OR a full Base/wiki URL (/base/\u2026 or /wiki/\u2026)."}},required:["baseId"]}},{name:"larkbitable_read_records",description:"Read the rows of one table in a Lark/Feishu Base (\u591A\u7EF4\u8868\u683C / bitable) as flat text. Accepts a Base URL (/base/\u2026 or a /wiki/\u2026 link fronting a Base) or app token; the table and view are taken from the URL's table=/view= when present, and tableId may be omitted when the Base has exactly one table. Optionally restrict to viewId or fieldNames. Returns { ok, appToken, name, tableId, total, count, columns:[{name,type}], records:[{ recordId, fields:{ <column>: <text> } }], hasMore, nextPageToken }. Every cell is a STRING (dates as ISO). Defaults to 200 records; pass maxRecords (max 1000) and pageToken to page through a bigger table. A WIDE table (dozens of columns) hits a character budget before the row cap: the reply then carries truncated:true, hasMore:true and a note, and the fix is fieldNames, not paging. Column names are matched EXACTLY and real ones can carry a leading space or a ' (1)' suffix \u2014 an unknown name is refused with this table's actual column list, so read columns[] (or one small call) before naming any. TO ANSWER 'what changed, when, by whom': pass includeRowMeta:true and each record also carries createdTime/createdBy/lastModifiedTime/lastModifiedBy (ISO + display name) \u2014 row METADATA that exists even when the table declares no such column. This is the ONLY way to see a row's edit time; Lark exposes NO per-cell edit history to any API, so a field's previous VALUE is unavailable (only the Base's own web UI record history has it). Filter and sort SERVER-SIDE rather than pulling the table and counting: filter = { conjunction:'and'|'or', conditions:[{ fieldName, operator, value }] } with operator one of is/isNot/contains/doesNotContain/isEmpty/isNotEmpty/isGreater/isGreaterEqual/isLess/isLessEqual, and sort = [{ fieldName, desc }]. With a filter the reply carries filtered:true and `total` is the MATCH count, so one call with maxRecords:1 answers 'how many rows are X'. Date/datetime columns compare against value ['ExactDate','<epoch ms>'] (Lark also accepts 'Today', 'Yesterday', 'CurrentWeek'); everything else takes plain strings.",input_schema:{type:"object",properties:{baseId:{type:"string",description:"Lark Base app token OR a full Base/wiki URL (/base/\u2026 or /wiki/\u2026)."},tableId:{type:"string",description:"Table id (tbl\u2026). Optional when the URL carries table= or the Base has exactly one table; larkbitable_list_tables lists them."},viewId:{type:"string",description:"Optional view id (vew\u2026) \u2014 restricts the read to that view's rows and order."},fieldNames:{type:"array",items:{type:"string"},description:"Optional column names to return. Absent = every column."},includeRowMeta:{type:"boolean",description:"Also return each row's createdTime/createdBy/lastModifiedTime/lastModifiedBy. Use for any 'what changed / who edited / when' question \u2014 without it those are absent."},filter:{type:"object",description:"Server-side filter, evaluated by Lark. { conjunction:'and'|'or', conditions:[{ fieldName, operator, value }] }.",properties:{conjunction:{type:"string",description:"'and' (default) or 'or'."},conditions:{type:"array",items:{type:"object",properties:{fieldName:{type:"string",description:"Exact column name (see columns[])."},operator:{type:"string",description:"is | isNot | contains | doesNotContain | isEmpty | isNotEmpty | isGreater | isGreaterEqual | isLess | isLessEqual."},value:{description:"String, number, or array of them. Omit for isEmpty/isNotEmpty. Date columns take ['ExactDate','<epoch ms>'] or 'Today'/'Yesterday'/'CurrentWeek'."}},required:["fieldName","operator"]}}},required:["conditions"]},sort:{type:"array",description:'Server-side sort, e.g. [{ fieldName: "\u6700\u540E\u66F4\u65B0\u65F6\u95F4", desc: true }].',items:{type:"object",properties:{fieldName:{type:"string",description:"Exact column name."},desc:{type:"boolean",description:"Descending when true."}},required:["fieldName"]}},maxRecords:{type:"number",description:"Max records to return (default 200, hard cap 1000)."},pageToken:{type:"string",description:"Resume token from a previous call's nextPageToken."}},required:["baseId"]}},{name:"larkdoc_list_comments",description:"List the comment threads on a Lark/Feishu document. Accepts a documentId or full doc URL. Returns { ok, comments:[{ commentId, replies:[{ replyId, author, text }] }] }. Use to read the thread you are replying to.",input_schema:{type:"object",properties:{documentId:{type:"string",description:"Lark doc id (docx token) OR a full Lark/Feishu doc URL."},fileType:{type:"string",description:"Drive file type \u2014 defaults to 'docx'. Only change for non-docx files (doc/sheet/bitable/file/slides)."}},required:["documentId"]}},{name:"larkdoc_add_comment",description:"Post a NEW top-level comment on a Lark/Feishu document. Accepts a documentId or full doc URL plus text. Returns { ok, documentId, commentId }.",input_schema:{type:"object",properties:{documentId:{type:"string",description:"Lark doc id (docx token) OR a full Lark/Feishu doc URL."},text:{type:"string",description:"The comment body (plain text)."},fileType:{type:"string",description:"Drive file type \u2014 defaults to 'docx'."}},required:["documentId","text"]}},{name:"larkdoc_reply_comment",description:"Reply INSIDE an existing comment thread on a Lark/Feishu document. Pass { documentId, commentId, text }. Use to answer a user who @mentioned Zibby in a doc comment (reply in the same commentId). Returns { ok, documentId, commentId, replyId }.",input_schema:{type:"object",properties:{documentId:{type:"string",description:"Lark doc id (docx token) OR a full Lark/Feishu doc URL."},commentId:{type:"string",description:"The comment_id of the thread to reply into (from larkdoc_list_comments or the webhook event)."},text:{type:"string",description:"The reply body (plain text)."},fileType:{type:"string",description:"Drive file type \u2014 defaults to 'docx'."}},required:["documentId","commentId","text"]}}]};function tt(){I=null}export{Ee as LARK_DOCS_APP_ENV_KEYS,tt as _resetLarkDocsTokenCache,V as baseWebUrl,Ce as bitableRowMeta,je as buildBitableSearchBody,L as docWebUrl,et as larkDocsSkill,v as markdownToLarkBlocks,y as parseLarkDocRef,xe as wikiWebUrl};
package/dist/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zibby/skills",
3
- "version": "2.0.15",
3
+ "version": "2.0.17",
4
4
  "description": "Built-in skill definitions for the Zibby agent-workflow framework",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -117,10 +117,10 @@
117
117
  "dependencies": {
118
118
  "@modelcontextprotocol/sdk": "^1.29.0",
119
119
  "@resvg/resvg-js": "^2.6.2",
120
- "@zibby/agent-workflow": "2.0.15",
120
+ "@zibby/agent-workflow": "2.0.17",
121
121
  "@zibby/bin-oxlint": "^1.73.0",
122
122
  "@zibby/bin-semgrep": "^1.169.0",
123
- "@zibby/skill-ids": "2.0.15",
123
+ "@zibby/skill-ids": "2.0.17",
124
124
  "echarts": "^6.1.0",
125
125
  "mem0ai": "npm:@zibby/mem0ai@^3.0.5",
126
126
  "zod": "^3.23.0 || ^4.0.0"
@@ -135,7 +135,7 @@
135
135
  }
136
136
  },
137
137
  "optionalDependencies": {
138
- "@zibby/mcp-browser": "2.0.15",
138
+ "@zibby/mcp-browser": "2.0.17",
139
139
  "@zibby/mcp-memory": "*"
140
140
  },
141
141
  "devDependencies": {
package/dist/report.d.ts CHANGED
@@ -34,301 +34,76 @@ export declare const reportObjectSchema: z.ZodObject<{
34
34
  primary: z.ZodString;
35
35
  delta: z.ZodOptional<z.ZodObject<{
36
36
  value: z.ZodString;
37
- direction: z.ZodOptional<z.ZodEnum<["up", "down", "flat"]>>;
38
- severity: z.ZodOptional<z.ZodEnum<["ok", "info", "warn", "critical"]>>;
39
- }, "strip", z.ZodTypeAny, {
40
- severity?: "warn" | "info" | "critical" | "ok";
41
- value?: string;
42
- direction?: "flat" | "up" | "down";
43
- }, {
44
- severity?: "warn" | "info" | "critical" | "ok";
45
- value?: string;
46
- direction?: "flat" | "up" | "down";
47
- }>>;
37
+ direction: z.ZodOptional<z.ZodEnum<{
38
+ flat: "flat";
39
+ up: "up";
40
+ down: "down";
41
+ }>>;
42
+ severity: z.ZodOptional<z.ZodEnum<{
43
+ warn: "warn";
44
+ info: "info";
45
+ critical: "critical";
46
+ ok: "ok";
47
+ }>>;
48
+ }, z.core.$strip>>;
48
49
  summary: z.ZodOptional<z.ZodString>;
49
- }, "strip", z.ZodTypeAny, {
50
- summary?: string;
51
- primary?: string;
52
- delta?: {
53
- severity?: "warn" | "info" | "critical" | "ok";
54
- value?: string;
55
- direction?: "flat" | "up" | "down";
56
- };
57
- }, {
58
- summary?: string;
59
- primary?: string;
60
- delta?: {
61
- severity?: "warn" | "info" | "critical" | "ok";
62
- value?: string;
63
- direction?: "flat" | "up" | "down";
64
- };
65
- }>;
66
- sections: z.ZodEffects<z.ZodDefault<z.ZodArray<z.ZodDiscriminatedUnion<"kind", [z.ZodObject<{
50
+ }, z.core.$strip>;
51
+ sections: z.ZodDefault<z.ZodArray<z.ZodDiscriminatedUnion<[z.ZodObject<{
67
52
  kind: z.ZodLiteral<"trend">;
68
53
  title: z.ZodOptional<z.ZodString>;
69
- labels: z.ZodArray<z.ZodString, "many">;
70
- values: z.ZodArray<z.ZodNumber, "many">;
71
- highlight: z.ZodOptional<z.ZodDefault<z.ZodEnum<["last", "max", "min", "none"]>>>;
72
- severity: z.ZodOptional<z.ZodEnum<["ok", "info", "warn", "critical"]>>;
73
- }, "strip", z.ZodTypeAny, {
74
- values?: number[];
75
- kind?: "trend";
76
- title?: string;
77
- severity?: "warn" | "info" | "critical" | "ok";
78
- labels?: string[];
79
- highlight?: "none" | "last" | "max" | "min";
80
- }, {
81
- values?: number[];
82
- kind?: "trend";
83
- title?: string;
84
- severity?: "warn" | "info" | "critical" | "ok";
85
- labels?: string[];
86
- highlight?: "none" | "last" | "max" | "min";
87
- }>, z.ZodObject<{
54
+ labels: z.ZodArray<z.ZodString>;
55
+ values: z.ZodArray<z.ZodNumber>;
56
+ highlight: z.ZodOptional<z.ZodDefault<z.ZodEnum<{
57
+ none: "none";
58
+ last: "last";
59
+ max: "max";
60
+ min: "min";
61
+ }>>>;
62
+ severity: z.ZodOptional<z.ZodEnum<{
63
+ warn: "warn";
64
+ info: "info";
65
+ critical: "critical";
66
+ ok: "ok";
67
+ }>>;
68
+ }, z.core.$strip>, z.ZodObject<{
88
69
  kind: z.ZodLiteral<"table">;
89
70
  title: z.ZodOptional<z.ZodString>;
90
- headers: z.ZodArray<z.ZodString, "many">;
91
- rows: z.ZodArray<z.ZodArray<z.ZodUnion<[z.ZodString, z.ZodNumber]>, "many">, "many">;
92
- }, "strip", z.ZodTypeAny, {
93
- kind?: "table";
94
- title?: string;
95
- headers?: string[];
96
- rows?: (string | number)[][];
97
- }, {
98
- kind?: "table";
99
- title?: string;
100
- headers?: string[];
101
- rows?: (string | number)[][];
102
- }>, z.ZodObject<{
71
+ headers: z.ZodArray<z.ZodString>;
72
+ rows: z.ZodArray<z.ZodArray<z.ZodUnion<readonly [z.ZodString, z.ZodNumber]>>>;
73
+ }, z.core.$strip>, z.ZodObject<{
103
74
  kind: z.ZodLiteral<"callouts">;
104
75
  title: z.ZodOptional<z.ZodString>;
105
- tone: z.ZodOptional<z.ZodDefault<z.ZodEnum<["ok", "info", "warn", "critical"]>>>;
106
- items: z.ZodArray<z.ZodString, "many">;
107
- }, "strip", z.ZodTypeAny, {
108
- kind?: "callouts";
109
- title?: string;
110
- items?: string[];
111
- tone?: "warn" | "info" | "critical" | "ok";
112
- }, {
113
- kind?: "callouts";
114
- title?: string;
115
- items?: string[];
116
- tone?: "warn" | "info" | "critical" | "ok";
117
- }>, z.ZodObject<{
76
+ tone: z.ZodOptional<z.ZodDefault<z.ZodEnum<{
77
+ warn: "warn";
78
+ info: "info";
79
+ critical: "critical";
80
+ ok: "ok";
81
+ }>>>;
82
+ items: z.ZodArray<z.ZodString>;
83
+ }, z.core.$strip>, z.ZodObject<{
118
84
  kind: z.ZodLiteral<"breakdown">;
119
85
  title: z.ZodOptional<z.ZodString>;
120
86
  rows: z.ZodArray<z.ZodObject<{
121
87
  label: z.ZodString;
122
88
  value: z.ZodString;
123
89
  sub: z.ZodOptional<z.ZodString>;
124
- severity: z.ZodOptional<z.ZodEnum<["ok", "info", "warn", "critical"]>>;
125
- }, "strip", z.ZodTypeAny, {
126
- label?: string;
127
- sub?: string;
128
- severity?: "warn" | "info" | "critical" | "ok";
129
- value?: string;
130
- }, {
131
- label?: string;
132
- sub?: string;
133
- severity?: "warn" | "info" | "critical" | "ok";
134
- value?: string;
135
- }>, "many">;
136
- }, "strip", z.ZodTypeAny, {
137
- kind?: "breakdown";
138
- title?: string;
139
- rows?: {
140
- label?: string;
141
- sub?: string;
142
- severity?: "warn" | "info" | "critical" | "ok";
143
- value?: string;
144
- }[];
145
- }, {
146
- kind?: "breakdown";
147
- title?: string;
148
- rows?: {
149
- label?: string;
150
- sub?: string;
151
- severity?: "warn" | "info" | "critical" | "ok";
152
- value?: string;
153
- }[];
154
- }>, z.ZodObject<{
90
+ severity: z.ZodOptional<z.ZodEnum<{
91
+ warn: "warn";
92
+ info: "info";
93
+ critical: "critical";
94
+ ok: "ok";
95
+ }>>;
96
+ }, z.core.$strip>>;
97
+ }, z.core.$strip>, z.ZodObject<{
155
98
  kind: z.ZodLiteral<"paragraph">;
156
99
  title: z.ZodOptional<z.ZodString>;
157
100
  text: z.ZodString;
158
- }, "strip", z.ZodTypeAny, {
159
- kind?: "paragraph";
160
- text?: string;
161
- title?: string;
162
- }, {
163
- kind?: "paragraph";
164
- text?: string;
165
- title?: string;
166
- }>]>, "many">>, ({
167
- values?: number[];
168
- kind?: "trend";
169
- title?: string;
170
- severity?: "warn" | "info" | "critical" | "ok";
171
- labels?: string[];
172
- highlight?: "none" | "last" | "max" | "min";
173
- } | {
174
- kind?: "table";
175
- title?: string;
176
- headers?: string[];
177
- rows?: (string | number)[][];
178
- } | {
179
- kind?: "callouts";
180
- title?: string;
181
- items?: string[];
182
- tone?: "warn" | "info" | "critical" | "ok";
183
- } | {
184
- kind?: "breakdown";
185
- title?: string;
186
- rows?: {
187
- label?: string;
188
- sub?: string;
189
- severity?: "warn" | "info" | "critical" | "ok";
190
- value?: string;
191
- }[];
192
- } | {
193
- kind?: "paragraph";
194
- text?: string;
195
- title?: string;
196
- })[], ({
197
- values?: number[];
198
- kind?: "trend";
199
- title?: string;
200
- severity?: "warn" | "info" | "critical" | "ok";
201
- labels?: string[];
202
- highlight?: "none" | "last" | "max" | "min";
203
- } | {
204
- kind?: "table";
205
- title?: string;
206
- headers?: string[];
207
- rows?: (string | number)[][];
208
- } | {
209
- kind?: "callouts";
210
- title?: string;
211
- items?: string[];
212
- tone?: "warn" | "info" | "critical" | "ok";
213
- } | {
214
- kind?: "breakdown";
215
- title?: string;
216
- rows?: {
217
- label?: string;
218
- sub?: string;
219
- severity?: "warn" | "info" | "critical" | "ok";
220
- value?: string;
221
- }[];
222
- } | {
223
- kind?: "paragraph";
224
- text?: string;
225
- title?: string;
226
- })[]>;
101
+ }, z.core.$strip>], "kind">>>;
227
102
  footer: z.ZodOptional<z.ZodObject<{
228
103
  viewUrl: z.ZodOptional<z.ZodString>;
229
104
  rerunUrl: z.ZodOptional<z.ZodString>;
230
- }, "strip", z.ZodTypeAny, {
231
- viewUrl?: string;
232
- rerunUrl?: string;
233
- }, {
234
- viewUrl?: string;
235
- rerunUrl?: string;
236
- }>>;
237
- }, "strip", z.ZodTypeAny, {
238
- title?: string;
239
- footer?: {
240
- viewUrl?: string;
241
- rerunUrl?: string;
242
- };
243
- subtitle?: string;
244
- headline?: {
245
- summary?: string;
246
- primary?: string;
247
- delta?: {
248
- severity?: "warn" | "info" | "critical" | "ok";
249
- value?: string;
250
- direction?: "flat" | "up" | "down";
251
- };
252
- };
253
- sections?: ({
254
- values?: number[];
255
- kind?: "trend";
256
- title?: string;
257
- severity?: "warn" | "info" | "critical" | "ok";
258
- labels?: string[];
259
- highlight?: "none" | "last" | "max" | "min";
260
- } | {
261
- kind?: "table";
262
- title?: string;
263
- headers?: string[];
264
- rows?: (string | number)[][];
265
- } | {
266
- kind?: "callouts";
267
- title?: string;
268
- items?: string[];
269
- tone?: "warn" | "info" | "critical" | "ok";
270
- } | {
271
- kind?: "breakdown";
272
- title?: string;
273
- rows?: {
274
- label?: string;
275
- sub?: string;
276
- severity?: "warn" | "info" | "critical" | "ok";
277
- value?: string;
278
- }[];
279
- } | {
280
- kind?: "paragraph";
281
- text?: string;
282
- title?: string;
283
- })[];
284
- }, {
285
- title?: string;
286
- footer?: {
287
- viewUrl?: string;
288
- rerunUrl?: string;
289
- };
290
- subtitle?: string;
291
- headline?: {
292
- summary?: string;
293
- primary?: string;
294
- delta?: {
295
- severity?: "warn" | "info" | "critical" | "ok";
296
- value?: string;
297
- direction?: "flat" | "up" | "down";
298
- };
299
- };
300
- sections?: ({
301
- values?: number[];
302
- kind?: "trend";
303
- title?: string;
304
- severity?: "warn" | "info" | "critical" | "ok";
305
- labels?: string[];
306
- highlight?: "none" | "last" | "max" | "min";
307
- } | {
308
- kind?: "table";
309
- title?: string;
310
- headers?: string[];
311
- rows?: (string | number)[][];
312
- } | {
313
- kind?: "callouts";
314
- title?: string;
315
- items?: string[];
316
- tone?: "warn" | "info" | "critical" | "ok";
317
- } | {
318
- kind?: "breakdown";
319
- title?: string;
320
- rows?: {
321
- label?: string;
322
- sub?: string;
323
- severity?: "warn" | "info" | "critical" | "ok";
324
- value?: string;
325
- }[];
326
- } | {
327
- kind?: "paragraph";
328
- text?: string;
329
- title?: string;
330
- })[];
331
- }>;
105
+ }, z.core.$strip>>;
106
+ }, z.core.$strip>;
332
107
  /**
333
108
  * Convert a report-object → Slack Block-Kit `blocks` array.
334
109
  *
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zibby/skills",
3
- "version": "2.0.15",
3
+ "version": "2.0.17",
4
4
  "description": "Built-in skill definitions for the Zibby agent-workflow framework",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -117,10 +117,10 @@
117
117
  "dependencies": {
118
118
  "@modelcontextprotocol/sdk": "^1.29.0",
119
119
  "@resvg/resvg-js": "^2.6.2",
120
- "@zibby/agent-workflow": "2.0.15",
120
+ "@zibby/agent-workflow": "2.0.17",
121
121
  "@zibby/bin-oxlint": "^1.73.0",
122
122
  "@zibby/bin-semgrep": "^1.169.0",
123
- "@zibby/skill-ids": "2.0.15",
123
+ "@zibby/skill-ids": "2.0.17",
124
124
  "echarts": "^6.1.0",
125
125
  "mem0ai": "npm:@zibby/mem0ai@^3.0.5",
126
126
  "zod": "^3.23.0 || ^4.0.0"
@@ -135,7 +135,7 @@
135
135
  }
136
136
  },
137
137
  "optionalDependencies": {
138
- "@zibby/mcp-browser": "2.0.15",
138
+ "@zibby/mcp-browser": "2.0.17",
139
139
  "@zibby/mcp-memory": "*"
140
140
  },
141
141
  "devDependencies": {