import{existsSync as v,statSync as A,readFileSync as R}from"fs";import{fileURLToPath as P}from"url";import{basename as E,dirname as U,resolve as $}from"path";import{resolveIntegrationToken as D}from"@zibby/core/backend-client.js";var x=Object.freeze({SENTRY:"sentry",JIRA:"jira",GITHUB:"github",GITLAB:"gitlab",SLACK:"slack",LARK:"lark",OPENAI_BILLING:"openai_billing",ANTHROPIC_BILLING:"anthropic_billing",CURSOR_ADMIN:"cursor_admin",NOTION:"notion",GOOGLE:"google",PLANE:"plane",LINEAR:"linear",FIGMA:"figma",HUBSPOT:"hubspot",OPEN_DESIGN:"open_design",LINKEDIN_PERSONAL:"linkedin_personal",LINKEDIN_BUSINESS:"linkedin_business",DISCORD:"discord"}),K=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",connectPath:"/integrations?provider=lark"},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"},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"}});function j(){if(process.env.MCP_SKILL_PATH)return process.env.MCP_SKILL_PATH;let t=U(P(import.meta.url)),e=$(t,"..","bin","mcp-skill.mjs");return v(e)?e:null}var L=2e4,w=50,C=10*1024*1024,J=6e3*1e3,h=null;async function O(){let{appId:t,appSecret:e,host:n}=await D("lark");if(h&&h.appId===t&&h.expiresAt>Date.now())return{token:h.token,host:n};let o=await(await fetch(`${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})})).json();if(o.code!==0)throw new Error(`Lark tenant_access_token failed: ${o.msg||o.code}`);return h={token:o.tenant_access_token,expiresAt:Date.now()+J,appId:t},{token:o.tenant_access_token,host:n}}async function l(t,e,n){let{token:r,host:o}=await O(),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 a=await(await fetch(`${o}${e}`,i)).json();if(a.code!==0)throw new Error(`Lark Docx API ${e} error: ${a.msg||a.code}`);return{data:a.data||{},host:o}}function f(t,e){return String(t||"").includes("feishu")?`https://feishu.cn/docx/${e}`:`https://www.larksuite.com/docx/${e}`}function m(t){if(!t||typeof t!="string")return null;let e=t.trim();if(!e)return null;let n=e.match(/\/(docx|wiki)\/([A-Za-z0-9]+)/);return n?{type:n[1],token:n[2]}:/^[A-Za-z0-9]{10,}$/.test(e)?{type:"docx",token:e}:null}async function k(t){let e=typeof t=="string"?m(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 l("GET",`/open-apis/wiki/v2/spaces/get_node?token=${encodeURIComponent(e.token)}`),r=n?.node||{};if(r.obj_type!=="docx"||!r.obj_token)throw new Error(`Wiki node is not a docx document (obj_type=${r.obj_type||"unknown"})`);return r.obj_token}function b(t){let e=String(t??"").replace(/\r\n/g,`
`),n=[];for(let r of e.split(`
`)){let o=r.replace(/\s+$/,"");if(!o.trim())continue;let i=/^(#{1,3})\s+(.*)$/.exec(o),s=/^\s*[-*]\s+(.*)$/.exec(o),a=/^\s*\d+[.)]\s+(.*)$/.exec(o),c,d,p;if(i){let u=i[1].length;c=`heading${u}`,d=2+u,p=i[2]}else s?(c="bullet",d=12,p=s[1]):a?(c="ordered",d=13,p=a[1]):(c="text",d=2,p=o);n.push({block_type:d,[c]:{elements:[{text_run:{content:p}}],style:{}}})}return n}function y(t){let e=typeof t?.markdown=="string"?t.markdown:null,n=typeof t?.text=="string"?t.text:null;return e??n}async function S(t,e){let n;for(let r=0;r<e.length;r+=w){let o=e.slice(r,r+w);n=(await l("POST",`/open-apis/docx/v1/documents/${t}/blocks/${t}/children?document_revision_id=-1`,{children:o})).host}return n}function F(t){let e=typeof t=="string"?t.trim():"";if(!e)throw new Error("imagePath is required");if(!v(e)||!A(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=R(e);if(n.length>C)throw new Error(`image is ${(n.length/(1024*1024)).toFixed(1)}MB \u2014 max 10MB`);return n}async function q({fileName:t,parentNode:e,bytes:n}){let{token:r,host:o}=await O(),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 a=await(await fetch(`${o}/open-apis/drive/v1/medias/upload_all`,{method:"POST",headers:{Authorization:`Bearer ${r}`},body:i})).json();if(a.code!==0)throw new Error(`Lark media upload_all error: ${a.msg||a.code}`);let c=a.data?.file_token;if(!c)throw new Error("Lark media upload_all returned no file_token");return c}var B="docx",G=50;function T(t){return[{type:"text_run",text_run:{text:String(t??"")}}]}function M(t){return Array.isArray(t)?t.map(e=>e?.text_run?.text??e?.docs_link?.url??(e?.person?`@${e.person.user_id||""}`:"")).join(""):""}function H(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:M(n?.content?.elements),createTime:n?.create_time||""}))}}function I(t){return typeof t?.fileType=="string"&&t.fileType.trim()?t.fileType.trim():B}var V={id:"lark-docs",serverName:"larkdocs",allowedTools:["mcp__larkdocs__*"],requiresIntegration:x.LARK,description:"Lark / Feishu Docs \u2014 read, create, append, and insert images into Lark documents (docx).",envKeys:[],promptFragment:`## Lark Docs (connected)
You can read, create, and append Lark/Feishu documents (docx). This reuses the same connected Lark app as messaging.
- larkdoc_get: pass a Lark doc id OR a full doc URL (a /docx/ or /wiki/ link); returns { ok, documentId, title, url, text } where text is the doc as plain text (truncated to ~20k chars). Use it as reference context.
- larkdoc_create: create a new doc from a title + markdown/text (#/##/### headings, - bullets, 1. ordered supported); returns { ok, documentId, url }. Share the url.
- larkdoc_append: append markdown/text to the end of an existing doc (pass the documentId or doc URL).
- larkdoc_insert_image: append a LOCAL image file (png/jpg, \u226410MB) to the end of a doc \u2014 pass { documentId, imagePath, width?, height? } (width/height in px, optional). Returns { ok, documentId, blockId, url }. Use this to deliver a rendered chart/report image into the doc.
- larkdoc_list_comments: list the comment threads on a doc (pass the documentId or doc URL); returns { ok, comments:[{ commentId, replies:[{ replyId, author, text }] }] }. Use to read the thread you are replying to.
- larkdoc_reply_comment: reply INSIDE an existing comment thread \u2014 pass { documentId, commentId, text }. Use this to answer a user who @mentioned Zibby in a doc comment (reply in the SAME commentId).
- larkdoc_add_comment: post a NEW top-level comment on a doc \u2014 pass { documentId, text }.
These 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=j();return t?{type:"stdio",command:"node",args:[t,"../dist/larkDocs.js","larkDocsSkill"],env:{},description:this.description,alwaysLoad:!0}:null},async handleToolCall(t,e){try{switch(t){case"larkdoc_get":{let n=e?.documentId||e?.url||e?.id,r=m(n);if(!r)return JSON.stringify({ok:!1,error:"A valid Lark doc id or URL is required"});let o=await k(r),i="";try{i=(await l("GET",`/open-apis/docx/v1/documents/${o}`)).data?.document?.title||""}catch{}let{data:s,host:a}=await l("GET",`/open-apis/docx/v1/documents/${o}/raw_content?lang=0`),c=String(s?.content||""),d=!1;return c.length>L&&(c=c.slice(0,L),d=!0),JSON.stringify({ok:!0,documentId:o,title:i,url:f(a,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{data:r,host:o}=await l("POST","/open-apis/docx/v1/documents",{title:n,...e?.folderToken?{folder_token:String(e.folderToken)}:{}}),i=r?.document?.document_id;if(!i)return JSON.stringify({ok:!1,error:"Lark Docs create returned no document_id"});let s=y(e);if(s&&s.trim()){let a=b(s);a.length&&await S(i,a)}return JSON.stringify({ok:!0,documentId:i,title:n,url:f(o,i)})}case"larkdoc_append":{let n=e?.documentId||e?.url||e?.id,r=m(n);if(!r)return JSON.stringify({ok:!1,error:"A valid Lark doc id or URL is required"});let o=y(e);if(!o||!o.trim())return JSON.stringify({ok:!1,error:"markdown or text content is required"});let i=await k(r),s=b(o);if(!s.length)return JSON.stringify({ok:!1,error:"no non-empty content to append"});let a=await S(i,s);return JSON.stringify({ok:!0,documentId:i,url:f(a,i)})}case"larkdoc_insert_image":{let n=e?.documentId||e?.url||e?.id,r=m(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 k(r),i=e.imagePath.trim(),s=F(i),a=E(i),d=(await l("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 p=await q({fileName:a,parentNode:d,bytes:s}),u={token:p},g=Number(e?.width),_=Number(e?.height);Number.isFinite(g)&&g>0&&(u.width=Math.round(g)),Number.isFinite(_)&&_>0&&(u.height=Math.round(_));let{host:N}=await l("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:p,url:f(N,o)})}case"larkdoc_list_comments":{let n=e?.documentId||e?.url||e?.id||e?.fileToken,r=m(n);if(!r)return JSON.stringify({ok:!1,error:"A valid Lark doc id or URL is required"});let o=await k(r),i=I(e),s=new URLSearchParams({file_type:i,page_size:String(G)}),{data:a}=await l("GET",`/open-apis/drive/v1/files/${o}/comments?${s.toString()}`),d=(Array.isArray(a?.items)?a.items:[]).map(H);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=m(n);if(!r)return JSON.stringify({ok:!1,error:"A valid Lark doc id or URL is required"});let o=y({text:e?.text??e?.body});if(!o||!o.trim())return JSON.stringify({ok:!1,error:"text is required"});let i=await k(r),s=I(e),{data:a}=await l("POST",`/open-apis/drive/v1/files/${i}/comments?file_type=${encodeURIComponent(s)}`,{reply_list:{replies:[{content:{elements:T(o)}}]}});return JSON.stringify({ok:!0,documentId:i,commentId:a?.comment_id||""})}case"larkdoc_reply_comment":{let n=e?.documentId||e?.url||e?.id||e?.fileToken,r=m(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=y({text:e?.text??e?.body});if(!i||!i.trim())return JSON.stringify({ok:!1,error:"text is required"});let s=await k(r),a=I(e),{data:c}=await l("POST",`/open-apis/drive/v1/files/${s}/comments/${encodeURIComponent(o)}/replies?file_type=${encodeURIComponent(a)}`,{content:{elements:T(i)}});return JSON.stringify({ok:!0,documentId:s,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). Returns { ok, documentId, url } \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."}},required:["title"]}},{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:"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 ee(){h=null}export{ee as _resetLarkDocsTokenCache,f as docWebUrl,V as larkDocsSkill,b as markdownToLarkBlocks,m as parseLarkDocRef};
