@zibby/skills 2.0.17 → 2.0.19

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/larkDocs.js CHANGED
@@ -1,3 +1,3 @@
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,`
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 he,resolve as ke}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"}),Fe=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 K}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 K(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 K(w.LARK);return{appId:e?.appId,appSecret:e?.appSecret,host:R(e?.host)}}}var O={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=O[t]||O.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 x(t,e={},n={}){let r=n.kind||"api",o=(O[r]||O.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=he(me(import.meta.url)),e=ke(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 D(){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 x(`${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 D(),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 x(`${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 Oe(t,e){return String(t||"").includes("feishu")?`https://feishu.cn/wiki/${e}`:`https://www.larksuite.com/wiki/${e}`}function xe(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],...xe(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 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};
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 D(),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 x(`${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 De(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 $e({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 De(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=[],k=0,h=c||null,f=!1,S=0,_=!1;for(let b=0;b<U;b++){let $=new URLSearchParams({page_size:String(Math.min(we,s))});h&&$.set("page_token",h);let{data:T}=await m("POST",`/open-apis/bitable/v1/apps/${encodeURIComponent(t)}/tables/${encodeURIComponent(e)}/records/search?${$.toString()}`,u);S=Number(T?.total)||S,f=!!T?.has_more,h=T?.page_token||null;for(let N of Array.isArray(T?.items)?T.items:[]){let j={};for(let[F,oe]of Object.entries(N?.fields||{}))j[F]=Ue(oe,l.get(F));let C={recordId:String(N?.record_id||""),...i?Ce(N):{},fields:j},q=JSON.stringify(C).length;if(p.length&&k+q>Se){_=!0;break}if(k+=q,p.push(C),p.length>=s)break}if(_||p.length>=s||!f||!h)break}return{columns:d,records:p,total:S,hasMore:f||_,nextPageToken:h||"",truncated:_,..._&&!h?{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.\n`createdTime` and `lastModifiedTime` are DIFFERENT QUESTIONS and must never stand in for one another: a row edited yesterday was not added yesterday. Rows APPEARING in a read you did not see before is not evidence they were created \u2014 a shorter earlier read (see `truncated`) explains it just as well. Answer "what was added" from `createdTime` ONLY, and if you did not ask for row metadata, say the read cannot tell rather than reasoning from row counts. A Base read is a SNAPSHOT OF NOW: every field holds only its CURRENT value, and `createdTime` says when the ROW appeared, never when it acquired that value. So a question about a PAST STATE of a mutable field has no answerable form here \u2014 only a PROXY (rows whose value is X today and whose `createdTime` precedes the date) that is wrong in BOTH directions: rows given the value after that date inflate it, and rows that held it then but were since changed away or deleted are absent from the read entirely and deflate it. Offer the proxy labelled as a proxy with both directions stated \u2014 never as the answer.\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 k=A(e);if(k&&k.trim()){let h=v(k);h.length&&await P(p,h)}return JSON.stringify({ok:!0,documentId:p,title:n,wikiSpaceId:r,wikiNodeToken:u.node_token||"",url:u.node_token?Oe(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),k=Number(e?.height);Number.isFinite(p)&&p>0&&(u.width=Math.round(p)),Number.isFinite(k)&&k>0&&(u.height=Math.round(k));let{host:h}=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(h,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 $e({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,k=Array.isArray(e?.sort)&&e.sort.length?e.sort:null,h=!!e?.includeRowMeta,f=await qe({appToken:a,tableId:i,viewId:s,fieldNames:c,filter:p,sort:k,includeRowMeta:h,maxRecords:l,pageToken:u}),{host:S}=await D(),_="";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, and a row count cannot substitute for them. createdTime answers WHAT WAS ADDED; lastModifiedTime answers WHAT WAS EDITED \u2014 a row edited yesterday was not added yesterday."},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,Oe as wikiWebUrl};
package/dist/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zibby/skills",
3
- "version": "2.0.17",
3
+ "version": "2.0.19",
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.17",
120
+ "@zibby/agent-workflow": "2.0.19",
121
121
  "@zibby/bin-oxlint": "^1.73.0",
122
122
  "@zibby/bin-semgrep": "^1.169.0",
123
- "@zibby/skill-ids": "2.0.17",
123
+ "@zibby/skill-ids": "2.0.19",
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.17",
138
+ "@zibby/mcp-browser": "2.0.19",
139
139
  "@zibby/mcp-memory": "*"
140
140
  },
141
141
  "devDependencies": {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zibby/skills",
3
- "version": "2.0.17",
3
+ "version": "2.0.19",
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.17",
120
+ "@zibby/agent-workflow": "2.0.19",
121
121
  "@zibby/bin-oxlint": "^1.73.0",
122
122
  "@zibby/bin-semgrep": "^1.169.0",
123
- "@zibby/skill-ids": "2.0.17",
123
+ "@zibby/skill-ids": "2.0.19",
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.17",
138
+ "@zibby/mcp-browser": "2.0.19",
139
139
  "@zibby/mcp-memory": "*"
140
140
  },
141
141
  "devDependencies": {