@zibby/skills 2.0.4 → 2.0.6

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.
Files changed (55) hide show
  1. package/dist/artifact.js +7 -7
  2. package/dist/chat-notify.js +4 -4
  3. package/dist/chatProgress.js +4 -4
  4. package/dist/datasetStore.js +2 -2
  5. package/dist/discord.js +3 -3
  6. package/dist/figma.js +2 -2
  7. package/dist/git-write.js +9 -9
  8. package/dist/github.js +5 -5
  9. package/dist/gitlab.js +5 -5
  10. package/dist/googleDocs.js +9 -9
  11. package/dist/hubspot.js +1 -1
  12. package/dist/index.js +179 -181
  13. package/dist/jira.d.ts +36 -1
  14. package/dist/jira.js +22 -20
  15. package/dist/kvMemory.js +2 -2
  16. package/dist/lark.js +2 -2
  17. package/dist/larkAttendance.js +1 -1
  18. package/dist/larkDocs.js +3 -3
  19. package/dist/lib/http-deadline.d.ts +117 -0
  20. package/dist/lib/http-deadline.js +1 -0
  21. package/dist/lib/markup.d.ts +158 -0
  22. package/dist/lib/markup.js +22 -0
  23. package/dist/linear.js +15 -15
  24. package/dist/linkedin.js +2 -2
  25. package/dist/llm-billing.js +1 -1
  26. package/dist/notion.js +7 -7
  27. package/dist/opendesign.js +2 -2
  28. package/dist/package.json +6 -5
  29. package/dist/report.d.ts +42 -42
  30. package/dist/review.js +4 -4
  31. package/dist/reviewMemoryIo.js +1 -1
  32. package/dist/sentry.js +2 -2
  33. package/dist/skill-installer.js +3 -3
  34. package/dist/slack.js +2 -2
  35. package/dist/trackers/github-adapter.js +5 -5
  36. package/dist/trackers/index.js +46 -44
  37. package/dist/trackers/jira-adapter.js +24 -22
  38. package/dist/trackers/linear-adapter.js +19 -19
  39. package/dist/trackers/plane-adapter.js +1 -1
  40. package/dist/triggerAgent.js +1 -1
  41. package/dist/vikunja.d.ts +8 -1
  42. package/dist/vikunja.js +22 -7
  43. package/docs/templates/catalog-metadata.md +101 -0
  44. package/docs/templates/composition.md +97 -0
  45. package/docs/templates/deploy-time-config.md +228 -0
  46. package/docs/templates/index.md +138 -0
  47. package/docs/templates/models.md +157 -0
  48. package/docs/templates/node-declarations.md +149 -0
  49. package/docs/templates/remote-mcp.md +105 -0
  50. package/docs/templates/requires.md +183 -0
  51. package/docs/templates/sidecars.md +163 -0
  52. package/docs/templates/stores.md +113 -0
  53. package/docs/templates/surfaces.md +184 -0
  54. package/docs/templates/update-behavior.md +108 -0
  55. package/package.json +6 -5
package/dist/larkDocs.js CHANGED
@@ -1,3 +1,3 @@
1
- import{existsSync as E,statSync as C,readFileSync as U}from"fs";import{fileURLToPath as J}from"url";import{basename as K,dirname as F,resolve as B}from"path";var h=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"}),ne=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 b}from"@zibby/core/backend-client.js";var $="https://open.larksuite.com",T=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"})]),O=Object.freeze(T.flatMap(t=>[t.appId,t.appSecret,t.host]));function S(t){let e=String(t||"").trim().replace(/\/+$/,"");return e?/^https?:\/\//i.test(e)?e:`https://${e}`:$}function j(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:S(process.env[t.host])}}async function v(){for(let t of T){let e=j(t);if(e)return e}try{let t=await b(h.LARK_DOCS);return{appId:t?.appId,appSecret:t?.appSecret,host:S(t?.host)}}catch(t){if(!/unknown provider/i.test(String(t?.message||"")))throw t;let e=await b(h.LARK);return{appId:e?.appId,appSecret:e?.appSecret,host:S(e?.host)}}}function G(){if(process.env.MCP_SKILL_PATH)return process.env.MCP_SKILL_PATH;let t=F(J(import.meta.url)),e=B(t,"..","bin","mcp-skill.mjs");return E(e)?e:null}var N=2e4,P=50,q=50,M=20,z=10*1024*1024,H=6e3*1e3,y=null,Y=O;async function D(){let{appId:t,appSecret:e,host:n}=await v();if(y&&y.appId===t&&y.expiresAt>Date.now())return{token:y.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 y={token:o.tenant_access_token,expiresAt:Date.now()+H,appId:t},{token:o.tenant_access_token,host:n}}async function l(t,e,n){let{token:i,host:o}=await D(),r={method:t,headers:{Authorization:`Bearer ${i}`,"Content-Type":"application/json; charset=utf-8"}};t!=="GET"&&n!==void 0&&(r.body=JSON.stringify(n));let a=await(await fetch(`${o}${e}`,r)).json();if(a.code!==0)throw new Error(`Lark Docx API ${e} error: ${a.msg||a.code}`);return{data:a.data||{},host:o}}function g(t,e){return String(t||"").includes("feishu")?`https://feishu.cn/docx/${e}`:`https://www.larksuite.com/docx/${e}`}function V(t,e){return String(t||"").includes("feishu")?`https://feishu.cn/wiki/${e}`:`https://www.larksuite.com/wiki/${e}`}function f(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 _(t){let e=typeof t=="string"?f(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)}`),i=n?.node||{};if(i.obj_type!=="docx"||!i.obj_token)throw new Error(`Wiki node is not a docx document (obj_type=${i.obj_type||"unknown"})`);return i.obj_token}function L(t){let e=String(t??"").replace(/\r\n/g,`
2
- `),n=[];for(let i of e.split(`
3
- `)){let o=i.replace(/\s+$/,"");if(!o.trim())continue;let r=/^(#{1,3})\s+(.*)$/.exec(o),s=/^\s*[-*]\s+(.*)$/.exec(o),a=/^\s*\d+[.)]\s+(.*)$/.exec(o),c,d,p;if(r){let u=r[1].length;c=`heading${u}`,d=2+u,p=r[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 I(t){let e=typeof t?.markdown=="string"?t.markdown:null,n=typeof t?.text=="string"?t.text:null;return e??n}async function x(t,e){let n;for(let i=0;i<e.length;i+=P){let o=e.slice(i,i+P);n=(await l("POST",`/open-apis/docx/v1/documents/${t}/blocks/${t}/children?document_revision_id=-1`,{children:o})).host}return n}function W(t){let e=typeof t=="string"?t.trim():"";if(!e)throw new Error("imagePath is required");if(!E(e)||!C(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=U(e);if(n.length>z)throw new Error(`image is ${(n.length/(1024*1024)).toFixed(1)}MB \u2014 max 10MB`);return n}async function X({fileName:t,parentNode:e,bytes:n}){let{token:i,host:o}=await D(),r=new FormData;r.set("file_name",t),r.set("parent_type","docx_image"),r.set("parent_node",e),r.set("size",String(n.length)),r.set("file",new Blob([n]),t);let a=await(await fetch(`${o}/open-apis/drive/v1/medias/upload_all`,{method:"POST",headers:{Authorization:`Bearer ${i}`},body:r})).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 Z="docx",Q=50;function R(t){return[{type:"text_run",text_run:{text:String(t??"")}}]}function ee(t){return Array.isArray(t)?t.map(e=>e?.text_run?.text??e?.docs_link?.url??(e?.person?`@${e.person.user_id||""}`:"")).join(""):""}function te(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:ee(n?.content?.elements),createTime:n?.create_time||""}))}}function A(t){return typeof t?.fileType=="string"&&t.fileType.trim()?t.fileType.trim():Z}var ue={id:"lark-docs",callsBackend:!0,serverName:"larkdocs",allowedTools:["mcp__larkdocs__*"],requiresIntegration:[h.LARK_DOCS,h.LARK],description:"Lark / Feishu Docs \u2014 read, create, append, and insert images into Lark documents (docx).",envKeys:["PROJECT_API_TOKEN","ZIBBY_ACCOUNT_API_URL","ZIBBY_ENV",...Y],promptFragment:'## Lark Docs\nYou can read, create, and append Lark/Feishu documents (docx), and read/post/reply to their comments. This runs on the connected Lark Docs app (the chat app is used when no separate docs app is connected). Each `larkdoc_*` / `larkwiki_*` tool documents its own params and return shape in its tool description \u2014 they are not restated here.\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=G();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,alwaysLoad:!0}},async handleToolCall(t,e){try{switch(t){case"larkdoc_get":{let n=e?.documentId||e?.url||e?.id,i=f(n);if(!i)return JSON.stringify({ok:!1,error:"A valid Lark doc id or URL is required"});let o=await _(i),r="";try{r=(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>N&&(c=c.slice(0,N),d=!0),JSON.stringify({ok:!0,documentId:o,title:r,url:g(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 i=typeof e?.wikiSpaceId=="string"&&e.wikiSpaceId.trim()?e.wikiSpaceId.trim():null;if(i){let c=typeof e?.parentNodeToken=="string"&&e.parentNodeToken.trim()?e.parentNodeToken.trim():null,{data:d,host:p}=await l("POST",`/open-apis/wiki/v2/spaces/${encodeURIComponent(i)}/nodes`,{obj_type:"docx",node_type:"origin",title:n,...c?{parent_node_token:c}:{}}),u=d?.node||{},m=u.obj_token;if(!m)return JSON.stringify({ok:!1,error:"Lark wiki node create returned no obj_token"});let k=I(e);if(k&&k.trim()){let w=L(k);w.length&&await x(m,w)}return JSON.stringify({ok:!0,documentId:m,title:n,wikiSpaceId:i,wikiNodeToken:u.node_token||"",url:u.node_token?V(p,u.node_token):g(p,m)})}let{data:o,host:r}=await l("POST","/open-apis/docx/v1/documents",{title:n,...e?.folderToken?{folder_token:String(e.folderToken)}:{}}),s=o?.document?.document_id;if(!s)return JSON.stringify({ok:!1,error:"Lark Docs create returned no document_id"});let a=I(e);if(a&&a.trim()){let c=L(a);c.length&&await x(s,c)}return JSON.stringify({ok:!0,documentId:s,title:n,url:g(r,s)})}case"larkdoc_append":{let n=e?.documentId||e?.url||e?.id,i=f(n);if(!i)return JSON.stringify({ok:!1,error:"A valid Lark doc id or URL is required"});let o=I(e);if(!o||!o.trim())return JSON.stringify({ok:!1,error:"markdown or text content is required"});let r=await _(i),s=L(o);if(!s.length)return JSON.stringify({ok:!1,error:"no non-empty content to append"});let a=await x(r,s);return JSON.stringify({ok:!0,documentId:r,url:g(a,r)})}case"larkdoc_insert_image":{let n=e?.documentId||e?.url||e?.id,i=f(n);if(!i)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 _(i),r=e.imagePath.trim(),s=W(r),a=K(r),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 X({fileName:a,parentNode:d,bytes:s}),u={token:p},m=Number(e?.width),k=Number(e?.height);Number.isFinite(m)&&m>0&&(u.width=Math.round(m)),Number.isFinite(k)&&k>0&&(u.height=Math.round(k));let{host:w}=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:g(w,o)})}case"larkwiki_list_spaces":{let n=[],i=null;for(let o=0;o<M;o++){let r=new URLSearchParams({page_size:String(q)});i&&r.set("page_token",i);let{data:s}=await l("GET",`/open-apis/wiki/v2/spaces?${r.toString()}`),a=Array.isArray(s?.items)?s.items:[];for(let c of a)n.push({spaceId:String(c?.space_id||""),name:String(c?.name||"")});if(!s?.has_more||!s?.page_token)break;i=s.page_token}return JSON.stringify({ok:!0,count:n.length,spaces:n})}case"larkdoc_list_comments":{let n=e?.documentId||e?.url||e?.id||e?.fileToken,i=f(n);if(!i)return JSON.stringify({ok:!1,error:"A valid Lark doc id or URL is required"});let o=await _(i),r=A(e),s=new URLSearchParams({file_type:r,page_size:String(Q)}),{data:a}=await l("GET",`/open-apis/drive/v1/files/${o}/comments?${s.toString()}`),d=(Array.isArray(a?.items)?a.items:[]).map(te);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,i=f(n);if(!i)return JSON.stringify({ok:!1,error:"A valid Lark doc id or URL is required"});let o=I({text:e?.text??e?.body});if(!o||!o.trim())return JSON.stringify({ok:!1,error:"text is required"});let r=await _(i),s=A(e),{data:a}=await l("POST",`/open-apis/drive/v1/files/${r}/comments?file_type=${encodeURIComponent(s)}`,{reply_list:{replies:[{content:{elements:R(o)}}]}});return JSON.stringify({ok:!0,documentId:r,commentId:a?.comment_id||""})}case"larkdoc_reply_comment":{let n=e?.documentId||e?.url||e?.id||e?.fileToken,i=f(n);if(!i)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 r=I({text:e?.text??e?.body});if(!r||!r.trim())return JSON.stringify({ok:!1,error:"text is required"});let s=await _(i),a=A(e),{data:c}=await l("POST",`/open-apis/drive/v1/files/${s}/comments/${encodeURIComponent(o)}/replies?file_type=${encodeURIComponent(a)}`,{content:{elements:R(r)}});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). 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:"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 me(){y=null}export{Y as LARK_DOCS_APP_ENV_KEYS,me as _resetLarkDocsTokenCache,g as docWebUrl,ue as larkDocsSkill,L as markdownToLarkBlocks,f as parseLarkDocRef,V as wikiWebUrl};
1
+ import{existsSync as M,statSync as G,readFileSync as q}from"fs";import{fileURLToPath as z}from"url";import{basename as H,dirname as Y,resolve as W}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"}),de=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 A}from"@zibby/core/backend-client.js";var $="https://open.larksuite.com",E=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"})]),v=Object.freeze(E.flatMap(t=>[t.appId,t.appSecret,t.host]));function b(t){let e=String(t||"").trim().replace(/\/+$/,"");return e?/^https?:\/\//i.test(e)?e:`https://${e}`:$}function K(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:b(process.env[t.host])}}async function N(){for(let t of E){let e=K(t);if(e)return e}try{let t=await A(_.LARK_DOCS);return{appId:t?.appId,appSecret:t?.appSecret,host:b(t?.host)}}catch(t){if(!/unknown provider/i.test(String(t?.message||"")))throw t;let e=await A(_.LARK);return{appId:e?.appId,appSecret:e?.appSecret,host:b(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 j(t,e,n=process.env){let o=Number(n?.[t]);return Number.isFinite(o)&&o>0?Math.min(6e5,Math.max(1e3,Math.floor(o))):e}function C(t="api",e=process.env){let n=L[t]||L.api;return j(n.knob,n.fallback,e)}function F(t){return t?.name==="TimeoutError"||t?.name==="AbortError"}function J(t){try{return new URL(String(t?.url??t)).host||"unknown host"}catch{return"unknown host"}}function B(t,e){if(!t)return e;let n=new AbortController,o=a=>{n.signal.aborted||n.abort(a)},r=()=>o(t.reason),i=()=>o(e.reason);return n.signal.addEventListener("abort",()=>{t.removeEventListener("abort",r),e.removeEventListener("abort",i)},{once:!0}),t.aborted?o(t.reason):e.aborted?o(e.reason):(t.addEventListener("abort",r,{once:!0}),e.addEventListener("abort",i,{once:!0})),n.signal}async function w(t,e={},n={}){let o=n.kind||"api",r=(L[o]||L.api).knob,i=n.timeoutMs?Math.min(6e5,Math.max(1e3,Math.floor(n.timeoutMs))):C(o),a=e?.signal;if(a?.aborted)throw a.reason??new DOMException("This operation was aborted","AbortError");let s=AbortSignal.timeout(i),c=B(a,s);try{return await fetch(t,{...e,signal:c})}catch(d){throw s.aborted&&F(d)?new Error(`${n.what||"request"} TIMED OUT after ${i}ms against ${J(t)} (${r})`):d}}function V(){if(process.env.MCP_SKILL_PATH)return process.env.MCP_SKILL_PATH;let t=Y(z(import.meta.url)),e=W(t,"..","bin","mcp-skill.mjs");return M(e)?e:null}var R=2e4,P=50,X=50,Z=20,Q=10*1024*1024,ee=6e3*1e3,y=null,te=v;async function U(){let{appId:t,appSecret:e,host:n}=await N();if(y&&y.appId===t&&y.expiresAt>Date.now())return{token:y.token,host:n};let r=await(await w(`${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(r.code!==0)throw new Error(`Lark tenant_access_token failed: ${r.msg||r.code}`);return y={token:r.tenant_access_token,expiresAt:Date.now()+ee,appId:t},{token:r.tenant_access_token,host:n}}async function l(t,e,n){let{token:o,host:r}=await U(),i={method:t,headers:{Authorization:`Bearer ${o}`,"Content-Type":"application/json; charset=utf-8"}};t!=="GET"&&n!==void 0&&(i.body=JSON.stringify(n));let s=await(await w(`${r}${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:r}}function g(t,e){return String(t||"").includes("feishu")?`https://feishu.cn/docx/${e}`:`https://www.larksuite.com/docx/${e}`}function ne(t,e){return String(t||"").includes("feishu")?`https://feishu.cn/wiki/${e}`:`https://www.larksuite.com/wiki/${e}`}function f(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 h(t){let e=typeof t=="string"?f(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)}`),o=n?.node||{};if(o.obj_type!=="docx"||!o.obj_token)throw new Error(`Wiki node is not a docx document (obj_type=${o.obj_type||"unknown"})`);return o.obj_token}function T(t){let e=String(t??"").replace(/\r\n/g,`
2
+ `),n=[];for(let o of e.split(`
3
+ `)){let r=o.replace(/\s+$/,"");if(!r.trim())continue;let i=/^(#{1,3})\s+(.*)$/.exec(r),a=/^\s*[-*]\s+(.*)$/.exec(r),s=/^\s*\d+[.)]\s+(.*)$/.exec(r),c,d,p;if(i){let u=i[1].length;c=`heading${u}`,d=2+u,p=i[2]}else a?(c="bullet",d=12,p=a[1]):s?(c="ordered",d=13,p=s[1]):(c="text",d=2,p=r);n.push({block_type:d,[c]:{elements:[{text_run:{content:p}}],style:{}}})}return n}function I(t){let e=typeof t?.markdown=="string"?t.markdown:null,n=typeof t?.text=="string"?t.text:null;return e??n}async function x(t,e){let n;for(let o=0;o<e.length;o+=P){let r=e.slice(o,o+P);n=(await l("POST",`/open-apis/docx/v1/documents/${t}/blocks/${t}/children?document_revision_id=-1`,{children:r})).host}return n}function oe(t){let e=typeof t=="string"?t.trim():"";if(!e)throw new Error("imagePath is required");if(!M(e)||!G(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=q(e);if(n.length>Q)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:o,host:r}=await U(),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 w(`${r}/open-apis/drive/v1/medias/upload_all`,{method:"POST",headers:{Authorization:`Bearer ${o}`},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 ie="docx",ae=50;function D(t){return[{type:"text_run",text_run:{text:String(t??"")}}]}function se(t){return Array.isArray(t)?t.map(e=>e?.text_run?.text??e?.docs_link?.url??(e?.person?`@${e.person.user_id||""}`:"")).join(""):""}function ce(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:se(n?.content?.elements),createTime:n?.create_time||""}))}}function O(t){return typeof t?.fileType=="string"&&t.fileType.trim()?t.fileType.trim():ie}var Se={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).",envKeys:["PROJECT_API_TOKEN","ZIBBY_ACCOUNT_API_URL","ZIBBY_ENV",...te],promptFragment:'## Lark Docs\nYou can read, create, and append Lark/Feishu documents (docx), and read/post/reply to their comments. This runs on the connected Lark Docs app (the chat app is used when no separate docs app is connected). Each `larkdoc_*` / `larkwiki_*` tool documents its own params and return shape in its tool description \u2014 they are not restated here.\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=V();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,alwaysLoad:!0}},async handleToolCall(t,e){try{switch(t){case"larkdoc_get":{let n=e?.documentId||e?.url||e?.id,o=f(n);if(!o)return JSON.stringify({ok:!1,error:"A valid Lark doc id or URL is required"});let r=await h(o),i="";try{i=(await l("GET",`/open-apis/docx/v1/documents/${r}`)).data?.document?.title||""}catch{}let{data:a,host:s}=await l("GET",`/open-apis/docx/v1/documents/${r}/raw_content?lang=0`),c=String(a?.content||""),d=!1;return c.length>R&&(c=c.slice(0,R),d=!0),JSON.stringify({ok:!0,documentId:r,title:i,url:g(s,r),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 o=typeof e?.wikiSpaceId=="string"&&e.wikiSpaceId.trim()?e.wikiSpaceId.trim():null;if(o){let c=typeof e?.parentNodeToken=="string"&&e.parentNodeToken.trim()?e.parentNodeToken.trim():null,{data:d,host:p}=await l("POST",`/open-apis/wiki/v2/spaces/${encodeURIComponent(o)}/nodes`,{obj_type:"docx",node_type:"origin",title:n,...c?{parent_node_token:c}:{}}),u=d?.node||{},m=u.obj_token;if(!m)return JSON.stringify({ok:!1,error:"Lark wiki node create returned no obj_token"});let k=I(e);if(k&&k.trim()){let S=T(k);S.length&&await x(m,S)}return JSON.stringify({ok:!0,documentId:m,title:n,wikiSpaceId:o,wikiNodeToken:u.node_token||"",url:u.node_token?ne(p,u.node_token):g(p,m)})}let{data:r,host:i}=await l("POST","/open-apis/docx/v1/documents",{title:n,...e?.folderToken?{folder_token:String(e.folderToken)}:{}}),a=r?.document?.document_id;if(!a)return JSON.stringify({ok:!1,error:"Lark Docs create returned no document_id"});let s=I(e);if(s&&s.trim()){let c=T(s);c.length&&await x(a,c)}return JSON.stringify({ok:!0,documentId:a,title:n,url:g(i,a)})}case"larkdoc_append":{let n=e?.documentId||e?.url||e?.id,o=f(n);if(!o)return JSON.stringify({ok:!1,error:"A valid Lark doc id or URL is required"});let r=I(e);if(!r||!r.trim())return JSON.stringify({ok:!1,error:"markdown or text content is required"});let i=await h(o),a=T(r);if(!a.length)return JSON.stringify({ok:!1,error:"no non-empty content to append"});let s=await x(i,a);return JSON.stringify({ok:!0,documentId:i,url:g(s,i)})}case"larkdoc_insert_image":{let n=e?.documentId||e?.url||e?.id,o=f(n);if(!o)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 r=await h(o),i=e.imagePath.trim(),a=oe(i),s=H(i),d=(await l("POST",`/open-apis/docx/v1/documents/${r}/blocks/${r}/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 re({fileName:s,parentNode:d,bytes:a}),u={token:p},m=Number(e?.width),k=Number(e?.height);Number.isFinite(m)&&m>0&&(u.width=Math.round(m)),Number.isFinite(k)&&k>0&&(u.height=Math.round(k));let{host:S}=await l("PATCH",`/open-apis/docx/v1/documents/${r}/blocks/${d}?document_revision_id=-1`,{replace_image:u});return JSON.stringify({ok:!0,documentId:r,blockId:d,fileToken:p,url:g(S,r)})}case"larkwiki_list_spaces":{let n=[],o=null;for(let r=0;r<Z;r++){let i=new URLSearchParams({page_size:String(X)});o&&i.set("page_token",o);let{data:a}=await l("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;o=a.page_token}return JSON.stringify({ok:!0,count:n.length,spaces:n})}case"larkdoc_list_comments":{let n=e?.documentId||e?.url||e?.id||e?.fileToken,o=f(n);if(!o)return JSON.stringify({ok:!1,error:"A valid Lark doc id or URL is required"});let r=await h(o),i=O(e),a=new URLSearchParams({file_type:i,page_size:String(ae)}),{data:s}=await l("GET",`/open-apis/drive/v1/files/${r}/comments?${a.toString()}`),d=(Array.isArray(s?.items)?s.items:[]).map(ce);return JSON.stringify({ok:!0,documentId:r,count:d.length,comments:d})}case"larkdoc_add_comment":{let n=e?.documentId||e?.url||e?.id||e?.fileToken,o=f(n);if(!o)return JSON.stringify({ok:!1,error:"A valid Lark doc id or URL is required"});let r=I({text:e?.text??e?.body});if(!r||!r.trim())return JSON.stringify({ok:!1,error:"text is required"});let i=await h(o),a=O(e),{data:s}=await l("POST",`/open-apis/drive/v1/files/${i}/comments?file_type=${encodeURIComponent(a)}`,{reply_list:{replies:[{content:{elements:D(r)}}]}});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,o=f(n);if(!o)return JSON.stringify({ok:!1,error:"A valid Lark doc id or URL is required"});let r=e?.commentId||e?.comment_id;if(!r)return JSON.stringify({ok:!1,error:"commentId is required"});let i=I({text:e?.text??e?.body});if(!i||!i.trim())return JSON.stringify({ok:!1,error:"text is required"});let a=await h(o),s=O(e),{data:c}=await l("POST",`/open-apis/drive/v1/files/${a}/comments/${encodeURIComponent(r)}/replies?file_type=${encodeURIComponent(s)}`,{content:{elements:D(i)}});return JSON.stringify({ok:!0,documentId:a,commentId:String(r),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:"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 Le(){y=null}export{te as LARK_DOCS_APP_ENV_KEYS,Le as _resetLarkDocsTokenCache,g as docWebUrl,Se as larkDocsSkill,T as markdownToLarkBlocks,f as parseLarkDocRef,ne as wikiWebUrl};
@@ -0,0 +1,117 @@
1
+ /**
2
+ * Every skill's outbound HTTP door, bounded from ONE declaration.
3
+ * ============================================================================
4
+ *
5
+ * WHY THIS FILE EXISTS. Node's global `fetch` has NO default timeout, and A
6
+ * HANG IS NOT A THROW: a connection that is accepted and then never answered
7
+ * is not an error any `catch` can see — it is a process that stops. Every
8
+ * provider module in this package is already written for failure (a non-2xx
9
+ * becomes a worded error the agent can read and route around), and NONE of
10
+ * that code can run for the one failure mode that actually costs a run.
11
+ *
12
+ * MEASURED, not hypothetical: board-runner run 4b49371e (2026-08-24) sat 7m33s
13
+ * inside the identical unbounded shape until the container watchdog killed it,
14
+ * and a tick that had already done all of its work recorded nothing. The class
15
+ * has since been closed one layer at a time — workflow-templates' `lib/kb.js`
16
+ * (d7e3184), `lib/platform-api.js` (7a355cc), `_shared/tracker.js` (539483e),
17
+ * `@zibby/core`'s `backend-client.js` (6677c77), `@zibby/agent-workflow`'s
18
+ * dispatch path (1ef5cca), and the runner's own seven doors (d0d857a, d32ba9c).
19
+ *
20
+ * THIS IS THE LAST LAYER, and the widest: ~45 call sites across ~25 provider
21
+ * modules. That width is exactly why it is a HELPER and not 45 hand-written
22
+ * `AbortSignal.timeout(...)` calls. Forty-five copies of a clamp, a
23
+ * `TimeoutError` check and a budget is the TWO-PLACES shape multiplied — a set
24
+ * that must agree, with nothing to scream when it drifts. One declaration, N
25
+ * consumers, and `__tests__/skills-http-doors-bounded.test.ts` reads the SOURCE
26
+ * of every provider module so that call site #46 fails the suite the day it is
27
+ * WRITTEN rather than the day it hangs somebody's run.
28
+ *
29
+ * ⚠️ A SKILL'S HANG IS CHEAPER THAN THE RUNNER'S, WHICH IS WHY IT WAS DEFERRED
30
+ * AND WHY IT IS NOT FREE. The runner's doors bracket the whole run; a skill's
31
+ * door costs "only" one tool call. But a tool call the model is waiting on is
32
+ * a run that is waiting on it, and the container watchdog does not care which
33
+ * layer stopped. The difference is the BUDGET, not whether there is one.
34
+ */
35
+ export declare const SKILL_API_TIMEOUT_MS = 30000;
36
+ export declare const SKILL_TRANSFER_TIMEOUT_MS = 120000;
37
+ export declare const SKILL_JOB_TIMEOUT_MS = 300000;
38
+ export declare const TIMEOUT_FLOOR_MS = 1000;
39
+ export declare const TIMEOUT_CEILING_MS = 600000;
40
+ export type DoorKind = 'api' | 'transfer' | 'job';
41
+ /**
42
+ * Read a budget from the environment, or fall back. Clamped to
43
+ * [TIMEOUT_FLOOR_MS, TIMEOUT_CEILING_MS]; anything unparseable or non-positive
44
+ * (`0`, `-1`, `''`, `'soon'`) falls back rather than disabling the bound.
45
+ */
46
+ export declare function timeoutMsFrom(knob: string, fallback: number, env?: any): number;
47
+ /** The effective budget for a call kind, after the env override and the clamp. */
48
+ export declare function timeoutMsForKind(kind?: DoorKind, env?: any): number;
49
+ /**
50
+ * `AbortSignal.timeout` aborts with a `TimeoutError` DOMException (undici
51
+ * rejects the fetch — and any in-flight body read — with that same reason); a
52
+ * caller-cancelled signal aborts with `AbortError`. Both mean "WE stopped
53
+ * waiting"; NEITHER means "the far end said no", which is why every call site
54
+ * has to branch on this before deciding whether to reword an error or rethrow
55
+ * it UNCHANGED.
56
+ */
57
+ export declare function isTimeoutError(err: any): boolean;
58
+ /**
59
+ * The host a URL is aimed at, for the error message. Never the path, never the
60
+ * query: a presigned S3 URL carries its signature in the query string and a
61
+ * provider URL carries ids the model will happily paste into a comment, so a
62
+ * timeout message that echoed the full URL would be a credential leak wearing
63
+ * a diagnostic's clothes (invariant #4). The host is what a human needs in
64
+ * order to tell "GitHub is down" from "our control plane is down" from "this
65
+ * box has no egress", and it is all they need.
66
+ */
67
+ export declare function hostOf(url: any): string;
68
+ export interface DoorOptions {
69
+ /** Which budget applies. Defaults to 'api'. */
70
+ kind?: DoorKind;
71
+ /**
72
+ * What the caller was DOING, in the words a human reading a run log needs:
73
+ * "GitHub GET /repos/x/y/pulls", "Lark media upload". CLAUDE.md's rule is
74
+ * that error text must describe THIS failure, not a different era's — a bare
75
+ * "fetch timed out" makes every one of ~45 doors look identical in a log.
76
+ */
77
+ what?: string;
78
+ /** An explicit budget, for the rare call that is none of the three kinds. */
79
+ timeoutMs?: number;
80
+ }
81
+ /**
82
+ * THE ONE DOOR. A `fetch` that cannot hang, whose timeout says WHAT timed out
83
+ * and AGAINST WHICH HOST, and which passes a caller's own `signal` straight
84
+ * through.
85
+ *
86
+ * Deliberately a drop-in for `fetch(url, init)`: it returns the same `Response`
87
+ * and rethrows every non-timeout error as the SAME OBJECT, so a provider
88
+ * module's existing `!res.ok` handling, its status branches and its error
89
+ * wording are all untouched. The only observable difference is that the hang
90
+ * became a throw — which is precisely what every one of those `catch` blocks
91
+ * was already written for and could never reach.
92
+ *
93
+ * ⚠️ THE SIGNAL COVERS THE BODY READ TOO, and that is load-bearing rather than
94
+ * incidental: a response whose HEADERS arrive and whose BODY then stalls is the
95
+ * same hang wearing a 200, and bounding only the first half would leave the
96
+ * door open in the shape hardest to notice. undici rejects an in-flight
97
+ * `res.json()` / `res.text()` with the request's abort reason, so callers get
98
+ * the same `TimeoutError` — see `describeTimeout` for turning that half into
99
+ * words at the call site.
100
+ */
101
+ export declare function fetchWithDeadline(url: any, init?: any, opts?: DoorOptions): Promise<Response>;
102
+ /**
103
+ * The BODY half, in words. `fetchWithDeadline` cannot wrap `res.json()` for the
104
+ * caller — the caller decides whether it wants json, text, bytes or nothing —
105
+ * so a module that reads a body under the same signal wraps the read in this to
106
+ * get a message in the same voice:
107
+ *
108
+ * catch (err) { throw new Error(describeTimeout(err, { what, url, kind, body: true })); }
109
+ *
110
+ * Returns `null` when the error is NOT a timeout, so the caller can rethrow the
111
+ * original object unchanged rather than flattening a real transport error into
112
+ * a string.
113
+ */
114
+ export declare function describeTimeout(err: any, { what, url, kind, timeoutMs, body }: DoorOptions & {
115
+ url?: any;
116
+ body?: boolean;
117
+ }): string | null;
@@ -0,0 +1 @@
1
+ var S=3e4,E=12e4,f=3e5,O=1e3,d=6e5,s={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 l(n,t,o=process.env){let r=Number(o?.[n]);return Number.isFinite(r)&&r>0?Math.min(6e5,Math.max(1e3,Math.floor(r))):t}function M(n="api",t=process.env){let o=s[n]||s.api;return l(o.knob,o.fallback,t)}function _(n){return n?.name==="TimeoutError"||n?.name==="AbortError"}function T(n){try{return new URL(String(n?.url??n)).host||"unknown host"}catch{return"unknown host"}}function I(n,t){if(!n)return t;let o=new AbortController,r=a=>{o.signal.aborted||o.abort(a)},i=()=>r(n.reason),e=()=>r(t.reason);return o.signal.addEventListener("abort",()=>{n.removeEventListener("abort",i),t.removeEventListener("abort",e)},{once:!0}),n.aborted?r(n.reason):t.aborted?r(t.reason):(n.addEventListener("abort",i,{once:!0}),t.addEventListener("abort",e,{once:!0})),o.signal}async function m(n,t={},o={}){let r=o.kind||"api",i=(s[r]||s.api).knob,e=o.timeoutMs?Math.min(6e5,Math.max(1e3,Math.floor(o.timeoutMs))):M(r),a=t?.signal;if(a?.aborted)throw a.reason??new DOMException("This operation was aborted","AbortError");let b=AbortSignal.timeout(e),u=I(a,b);try{return await fetch(n,{...t,signal:u})}catch(c){throw b.aborted&&_(c)?new Error(`${o.what||"request"} TIMED OUT after ${e}ms against ${T(n)} (${i})`):c}}function L(n,{what:t,url:o,kind:r="api",timeoutMs:i,body:e=!1}){if(!_(n))return null;let a=(s[r]||s.api).knob,b=i||M(r);return`${t||"request"}${e?" body read":""} TIMED OUT after ${b}ms against ${T(o)} (${a})`}export{S as SKILL_API_TIMEOUT_MS,f as SKILL_JOB_TIMEOUT_MS,E as SKILL_TRANSFER_TIMEOUT_MS,d as TIMEOUT_CEILING_MS,O as TIMEOUT_FLOOR_MS,L as describeTimeout,m as fetchWithDeadline,T as hostOf,_ as isTimeoutError,M as timeoutMsForKind,l as timeoutMsFrom};
@@ -0,0 +1,158 @@
1
+ /**
2
+ * markup.ts — ONE markup grammar for every board write and every board read.
3
+ * ============================================================================
4
+ *
5
+ * Every Jira description/comment used to be posted as ONE ADF paragraph per
6
+ * line of raw text, so the Markdown the fleet's writers already emit (`###`,
7
+ * `**bold**`, `` `code` ``, `- ` lists, `1.` lists, `---`) rendered as literal
8
+ * punctuation. Vikunja received the same raw text and its Tiptap editor showed
9
+ * it verbatim. This module is the fix, and it is the ONLY place the grammar
10
+ * lives:
11
+ *
12
+ * Markdown ──parseMarkup──▶ Block[] ──markupToAdf──▶ ADF doc (Jira)
13
+ * ──markupToHtml──▶ HTML (Vikunja)
14
+ * ADF doc ──adfToBlocks──▶ Block[] ──blocksToText──▶ Markdown (readers)
15
+ * HTML ──htmlToBlocks─▶ Block[] ──blocksToText──▶ Markdown (readers)
16
+ *
17
+ * WHY THE GRAMMAR IS DELIBERATELY *NOT* CommonMark — three properties the
18
+ * fleet's own parsers depend on, each of which CommonMark would break:
19
+ *
20
+ * 1. A LINE IS A BLOCK. A single newline ends a paragraph; blank lines are
21
+ * dropped, never rendered as empty paragraphs. The readers therefore hand
22
+ * back one line per block, joined by `\n` — byte-for-byte what the old
23
+ * one-paragraph-per-line writer produced, so `parseQuestion` (line 2 of a
24
+ * comment) and every `Key: value` field the fleet reads off a body keep
25
+ * working. Nothing ever emits a `hardBreak`.
26
+ * 2. BLOCK SYNTAX STARTS AT COLUMN 0. A line that begins with whitespace is
27
+ * a paragraph, whatever follows the whitespace. `draft.js sanitizeBlock`
28
+ * and `create-node neutralizeChildProse` neutralise a hostile heading or
29
+ * field line by PREFIXING A SPACE (lossless, visible); CommonMark allows
30
+ * up to three leading spaces before `###`, which would re-open exactly the
31
+ * injection those sanitizers close. So a leading space is inert here.
32
+ * 3. THE MACHINE LINE STAYS PLAIN. `[MAGNUM] verdict exec=… outcome=…
33
+ * pr=https://…` must come back off the board as the identical first line.
34
+ * `[x]` without a following `(url)` is text; `_` and `*` open emphasis
35
+ * only at a word boundary (so `worker=my_worker exec=a_b` has no italics);
36
+ * a bare URL autolinks only when preceded by whitespace or `(` — so
37
+ * `pr=https://…` is text while `**PR:** https://…` is a link. A link whose
38
+ * text equals its href reads back as the bare href, never `[u](u)`.
39
+ *
40
+ * SUPPORTED: paragraphs, `#`–`######` headings, `**bold**`/`__bold__`,
41
+ * `*em*`/`_em_`, `~~strike~~`, `` `code` ``, fenced code blocks, `- `/`* `
42
+ * bullets, `1.`/`1)` ordered lists, `- [ ]`/`- [x]` task items, `[text](url)`
43
+ * + bare URLs, `---` rules, `> ` blockquotes, GitHub-style alert panels
44
+ * (`> [!NOTE]` / `[!TIP]` / `[!IMPORTANT]` / `[!WARNING]` / `[!CAUTION]` →
45
+ * ADF panel info / success / note / warning / error), and simple GFM tables.
46
+ * Anything else is text. NOTHING HERE THROWS: every entry point catches and
47
+ * degrades to the plain one-paragraph-per-line shape, because a render failure
48
+ * that lost a comment would be strictly worse than the ugliness it fixes.
49
+ *
50
+ * The panel syntax is GitHub's alert extension (a real, documented dialect),
51
+ * chosen over an invented one so the same Markdown reads correctly on GitHub,
52
+ * GitLab, Linear and in a plain editor.
53
+ */
54
+ export type Inline = {
55
+ t: 'text';
56
+ v: string;
57
+ } | {
58
+ t: 'br';
59
+ } | {
60
+ t: 'code';
61
+ v: string;
62
+ } | {
63
+ t: 'strong';
64
+ c: Inline[];
65
+ } | {
66
+ t: 'em';
67
+ c: Inline[];
68
+ } | {
69
+ t: 'strike';
70
+ c: Inline[];
71
+ } | {
72
+ t: 'link';
73
+ href: string;
74
+ c: Inline[];
75
+ };
76
+ export type PanelKind = 'info' | 'success' | 'note' | 'warning' | 'error';
77
+ export type Block = {
78
+ t: 'paragraph';
79
+ c: Inline[];
80
+ } | {
81
+ t: 'heading';
82
+ level: number;
83
+ c: Inline[];
84
+ } | {
85
+ t: 'bullet';
86
+ items: Block[][];
87
+ } | {
88
+ t: 'ordered';
89
+ start: number;
90
+ items: Block[][];
91
+ } | {
92
+ t: 'task';
93
+ items: Array<{
94
+ checked: boolean;
95
+ c: Inline[];
96
+ }>;
97
+ } | {
98
+ t: 'code';
99
+ lang: string;
100
+ v: string;
101
+ } | {
102
+ t: 'quote';
103
+ c: Block[];
104
+ } | {
105
+ t: 'panel';
106
+ kind: PanelKind;
107
+ c: Block[];
108
+ } | {
109
+ t: 'rule';
110
+ } | {
111
+ t: 'table';
112
+ header: Inline[][] | null;
113
+ rows: Inline[][][];
114
+ };
115
+ /** Markdown text → blocks. Line-oriented; never throws. */
116
+ export declare function parseMarkup(source: unknown): Block[];
117
+ export declare function parseInline(source: unknown): Inline[];
118
+ export type AdfNode = {
119
+ type: string;
120
+ version?: number;
121
+ attrs?: Record<string, unknown>;
122
+ content?: AdfNode[];
123
+ text?: string;
124
+ marks?: Array<{
125
+ type: string;
126
+ attrs?: Record<string, unknown>;
127
+ }>;
128
+ };
129
+ /**
130
+ * The shape every board write used before this module existed — one paragraph
131
+ * per line, no marks. It is the FALLBACK of `markupToAdf`, and what a caller
132
+ * retries with when the API rejects the rich document. Exported so the two
133
+ * cannot drift.
134
+ */
135
+ export declare function plainTextToAdf(text: unknown): AdfNode;
136
+ /** Markdown → an ADF `doc`. Never throws; degrades to `plainTextToAdf`. */
137
+ export declare function markupToAdf(source: unknown): AdfNode;
138
+ /** Markdown → HTML for Tiptap-backed trackers (Vikunja). Never throws. */
139
+ export declare function markupToHtml(source: unknown): string;
140
+ /**
141
+ * Blocks → Markdown, ONE LINE PER BLOCK (property 1). Every block ends in `\n`;
142
+ * nested blocks (quotes, panels) prefix `> `; list items are `- `/`1. `/`- [ ] `.
143
+ * This is what every reader hands the fleet's parsers.
144
+ */
145
+ export declare function blocksToText(blocks: Block[]): string;
146
+ /** An ADF doc (or its `content` array) → blocks. Never throws. */
147
+ export declare function adfToBlocks(adf: unknown): Block[];
148
+ /**
149
+ * An ADF doc → the Markdown the fleet's parsers read. One line per block,
150
+ * trimmed. A doc the old writer produced (one plain paragraph per line) comes
151
+ * back byte-for-byte as the text that was written, blank lines included as
152
+ * empty paragraphs → empty lines.
153
+ */
154
+ export declare function adfToMarkup(adf: unknown): string;
155
+ /** HTML (Tiptap/Vikunja) → blocks. Never throws. Plain text without tags is read as Markdown. */
156
+ export declare function htmlToBlocks(html: unknown): Block[];
157
+ /** HTML → the Markdown the fleet's parsers read (one line per block, trimmed). */
158
+ export declare function htmlToMarkup(html: unknown): string;
@@ -0,0 +1,22 @@
1
+ var rt={NOTE:"info",TIP:"success",IMPORTANT:"note",WARNING:"warning",CAUTION:"error"},j={info:"NOTE",success:"TIP",note:"IMPORTANT",warning:"WARNING",error:"CAUTION"},ot=2e5,nt=6,ct=/^```([A-Za-z0-9_+.-]{0,32})\s*$/,it=/^(#{1,6})[ \t]+(.*?)\s*$/,at=/^(?:-{3,}|\*{3,}|_{3,})\s*$/,z=/^>(?: ?(.*))?$/,W=/^[-*][ \t]+\[([ xX])\][ \t]+(.*)$/,X=/^[-*][ \t]+(.*)$/,F=/^(\d{1,3})[.)][ \t]+(.*)$/,Z=/^\|.*\|\s*$/,lt=/^\|?\s*:?-{1,}:?\s*(?:\|\s*:?-{1,}:?\s*)*\|?\s*$/,pt=/^\[!(NOTE|TIP|IMPORTANT|WARNING|CAUTION)\]\s*$/i;function Q(s){let t=s.trim().replace(/^\|/,"").replace(/\|$/,""),e=[],n="";for(let r=0;r<t.length;r++){let o=t[r];if(o==="\\"&&t[r+1]==="|"){n+="|",r++;continue}if(o==="|"){e.push(n.trim()),n="";continue}n+=o}return e.push(n.trim()),e}function P(s){let e=String(s??"").slice(0,ot).replace(/\r\n?/g,`
2
+ `).split(`
3
+ `);return K(e,0)}function K(s,t){let e=[],n=0;for(;n<s.length;){let r=s[n];if(r.trim()===""){n++;continue}if(/^[ \t]/.test(r)){e.push({t:"paragraph",c:k(r)}),n++;continue}let o;if(o=ct.exec(r)){let a=o[1]||"",i=[];for(n++;n<s.length&&!/^```\s*$/.test(s[n]);)i.push(s[n]),n++;n++,e.push({t:"code",lang:a,v:i.join(`
4
+ `)});continue}if(at.test(r)){e.push({t:"rule"}),n++;continue}if(o=it.exec(r)){let a=Math.min(nt,o[1].length);e.push({t:"heading",level:a,c:k(o[2])}),n++;continue}if(z.test(r)&&t<3){let a=[];for(;n<s.length&&(o=z.exec(s[n]));)a.push(o[1]??""),n++;let i=a.findIndex(l=>l.trim()!==""),h=i>=0?pt.exec(a[i].trim()):null;if(h){let l=rt[h[1].toUpperCase()];e.push({t:"panel",kind:l,c:K(a.slice(i+1),t+1)})}else e.push({t:"quote",c:K(a,t+1)});continue}if(W.test(r)){let a=[];for(;n<s.length&&(o=W.exec(s[n]));)a.push({checked:o[1]!==" ",c:k(o[2])}),n++;e.push({t:"task",items:a});continue}if(X.test(r)){let a=[];for(;n<s.length&&!W.test(s[n])&&(o=X.exec(s[n]));)a.push([{t:"paragraph",c:k(o[1])}]),n++;e.push({t:"bullet",items:a});continue}if(o=F.exec(r)){let a=Math.max(1,parseInt(o[1],10)||1),i=[];for(;n<s.length&&(o=F.exec(s[n]));)i.push([{t:"paragraph",c:k(o[2])}]),n++;e.push({t:"ordered",start:a,items:i});continue}if(Z.test(r)&&n+1<s.length&&lt.test(s[n+1])){let a=Q(r).map(k);n+=2;let i=[];for(;n<s.length&&Z.test(s[n]);)i.push(Q(s[n]).map(k)),n++;e.push({t:"table",header:a,rows:i});continue}e.push({t:"paragraph",c:k(r)}),n++}return e}var ut=/^https?:\/\/[^\s<>()[\]]+/,ht=/^\[([^\]\n]{1,300})\]\((https?:\/\/[^\s)]{1,2000})\)/,ft=/[.,;:!?'"]+$/;function dt(s,t){return t<=0?!0:/[\s([{"'“‘>:—–-]/.test(s[t-1])}function kt(s,t){return t>=s.length?!0:/[\s)\]}"'”’.,;:!?—–-]/.test(s[t])}function gt(s,t,e){let n=t;for(;n<s.length;){let r=s.indexOf(e,n);if(r<0)return-1;let o=s[r-1],a=s[r+e.length],i=e.length===1&&(o===e||a===e);if(r>t&&o&&!/\s/.test(o)&&!i&&kt(s,r+e.length))return r;n=r+1}return-1}function k(s){let t=String(s??""),e=[],n="",r=()=>{n&&(e.push({t:"text",v:n}),n="")},o=0;for(;o<t.length;){let a=t[o];if(a==="`"){let i=1;for(;t[o+i]==="`";)i++;let h="`".repeat(i),l=t.indexOf(h,o+i);if(l>o+i-1&&l!==-1){let c=t.slice(o+i,l);if(c.trim()){r(),e.push({t:"code",v:c.length>1&&c.startsWith(" ")&&c.endsWith(" ")?c.slice(1,-1):c}),o=l+i;continue}}n+=h,o+=i;continue}if(a==="["){let i=ht.exec(t.slice(o));if(i){r(),e.push({t:"link",href:i[2],c:k(i[1])}),o+=i[0].length;continue}}if((a==="*"||a==="_"||a==="~")&&dt(t,o)){let i=t[o+1]===a;if(a==="~"&&!i){n+=a,o++;continue}let h=i?a+a:a,l=o+h.length;if(l<t.length&&!/\s/.test(t[l])){let c=gt(t,l,h);if(c>l){r();let p=k(t.slice(l,c));e.push(a==="~"?{t:"strike",c:p}:i?{t:"strong",c:p}:{t:"em",c:p}),o=c+h.length;continue}}}if(a==="h"&&(o===0||/[\s(<]/.test(t[o-1]))){let i=ut.exec(t.slice(o));if(i){let h=i[0],l=ft.exec(h);if(l&&(h=h.slice(0,-l[0].length)),h.length>8){r(),e.push({t:"link",href:h,c:[{t:"text",v:h}]}),o+=h.length;continue}}}n+=a,o++}return r(),e}function R(s,t=[]){let e=[];for(let n of s)switch(n.t){case"text":n.v&&e.push({type:"text",text:n.v,...t.length?{marks:t}:{}});break;case"br":e.push({type:"hardBreak"});break;case"code":e.push({type:"text",text:n.v,marks:[{type:"code"},...t.filter(r=>r.type==="link")]});break;case"strong":e.push(...R(n.c,O(t,{type:"strong"})));break;case"em":e.push(...R(n.c,O(t,{type:"em"})));break;case"strike":e.push(...R(n.c,O(t,{type:"strike"})));break;case"link":e.push(...R(n.c,O(t,{type:"link",attrs:{href:n.href}})));break}return e}function O(s,t){return s.some(e=>e.type===t.type)?s:[...s,t]}var D=0;function J(){return D=(D+1)%1e6,`m${Date.now().toString(36)}-${D}`}var L=s=>{let t=R(s);return t.length?{type:"paragraph",content:t}:{type:"paragraph"}};function N(s,t){let e=[];for(let n of s)switch(n.t){case"paragraph":e.push(L(n.c));break;case"heading":if(t==="quote"||t==="item")e.push(L([{t:"strong",c:n.c}]));else{let r=R(n.c);r.length&&e.push({type:"heading",attrs:{level:n.level},content:r})}break;case"bullet":e.push({type:"bulletList",content:n.items.map(r=>({type:"listItem",content:C(N(r,"item"))}))});break;case"ordered":e.push({type:"orderedList",attrs:{order:n.start},content:n.items.map(r=>({type:"listItem",content:C(N(r,"item"))}))});break;case"task":t==="quote"||t==="item"?e.push({type:"bulletList",content:n.items.map(r=>({type:"listItem",content:[L([{t:"text",v:r.checked?"\u2611 ":"\u2610 "},...r.c])]}))}):e.push({type:"taskList",attrs:{localId:J()},content:n.items.map(r=>({type:"taskItem",attrs:{localId:J(),state:r.checked?"DONE":"TODO"},content:mt(R(r.c))}))});break;case"code":e.push({type:"codeBlock",attrs:n.lang?{language:n.lang}:{},...n.v?{content:[{type:"text",text:n.v}]}:{}});break;case"quote":t==="quote"||t==="item"?e.push(...N(n.c,t)):e.push({type:"blockquote",content:C(N(n.c,"quote"))});break;case"panel":t!=="doc"?e.push(...N(n.c,t)):e.push({type:"panel",attrs:{panelType:n.kind},content:C(N(n.c,"panel"))});break;case"rule":if(t==="quote"||t==="item")break;e.push({type:"rule"});break;case"table":{if(t!=="doc"){for(let o of[...n.header?[n.header]:[],...n.rows])e.push(L(bt(o)));break}let r=[];n.header&&r.push({type:"tableRow",content:n.header.map(o=>({type:"tableHeader",attrs:{},content:[L(o)]}))});for(let o of n.rows)r.push({type:"tableRow",content:o.map(a=>({type:"tableCell",attrs:{},content:[L(a)]}))});r.length&&e.push({type:"table",attrs:{isNumberColumnEnabled:!1,layout:"default"},content:r});break}}return e}function C(s){return s.length?s:[{type:"paragraph"}]}function mt(s){return s.length?s:[{type:"text",text:" "}]}function bt(s){let t=[];return s.forEach((e,n)=>{n&&t.push({t:"text",v:" \xB7 "}),t.push(...e)}),t}function xt(s){return{type:"doc",version:1,content:String(s??"").split(`
5
+ `).map(t=>({type:"paragraph",...t?{content:[{type:"text",text:t}]}:{}}))}}function Nt(s){try{let t=N(P(s),"doc");return{type:"doc",version:1,content:t.length?t:[{type:"paragraph"}]}}catch{return xt(s)}}function B(s){return s.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;")}function I(s){let t="";for(let e of s)switch(e.t){case"text":t+=B(e.v);break;case"br":t+="<br>";break;case"code":t+=`<code>${B(e.v)}</code>`;break;case"strong":t+=`<strong>${I(e.c)}</strong>`;break;case"em":t+=`<em>${I(e.c)}</em>`;break;case"strike":t+=`<s>${I(e.c)}</s>`;break;case"link":t+=`<a href="${B(e.href)}" target="_blank" rel="noopener noreferrer">${I(e.c)}</a>`;break}return t}function yt(s){return s.length&&s[0].t==="text"&&/^ /.test(s[0].v)?[{t:"text",v:`\xA0${s[0].v.slice(1)}`},...s.slice(1)]:s}function q(s){let t="";for(let e of s)switch(e.t){case"paragraph":t+=`<p>${I(yt(e.c))}</p>`;break;case"heading":t+=`<h${e.level}>${I(e.c)}</h${e.level}>`;break;case"bullet":t+=`<ul>${e.items.map(n=>`<li>${q(n)}</li>`).join("")}</ul>`;break;case"ordered":t+=`<ol${e.start!==1?` start="${e.start}"`:""}>${e.items.map(n=>`<li>${q(n)}</li>`).join("")}</ol>`;break;case"task":t+=`<ul data-type="taskList">${e.items.map(n=>`<li data-type="taskItem" data-checked="${n.checked?"true":"false"}"><label><input type="checkbox"${n.checked?' checked="checked"':""}><span></span></label><div><p>${I(n.c)}</p></div></li>`).join("")}</ul>`;break;case"code":t+=`<pre><code${e.lang?` class="language-${B(e.lang)}"`:""}>${B(e.v)}</code></pre>`;break;case"quote":t+=`<blockquote>${q(e.c)}</blockquote>`;break;case"panel":t+=`<blockquote><p><strong>${j[e.kind][0]}${j[e.kind].slice(1).toLowerCase()}</strong></p>${q(e.c)}</blockquote>`;break;case"rule":t+="<hr>";break;case"table":{let n="<table><tbody>";e.header&&(n+=`<tr>${e.header.map(r=>`<th><p>${I(r)}</p></th>`).join("")}</tr>`);for(let r of e.rows)n+=`<tr>${r.map(o=>`<td><p>${I(o)}</p></td>`).join("")}</tr>`;t+=`${n}</tbody></table>`;break}}return t}function Et(s){try{return q(P(s))||`<p>${B(String(s??""))}</p>`}catch{return String(s??"").split(`
6
+ `).map(t=>`<p>${B(t)}</p>`).join("")}}function A(s){let t="";for(let e of s)switch(e.t){case"text":t+=e.v;break;case"br":t+=`
7
+ `;break;case"code":t+=`\`${e.v}\``;break;case"strong":t+=`**${A(e.c)}**`;break;case"em":t+=`_${A(e.c)}_`;break;case"strike":t+=`~~${A(e.c)}~~`;break;case"link":{let n=A(e.c);t+=!n||n===e.href?e.href:`[${n}](${e.href})`;break}}return t}function $(s){let t="";for(let e of s)switch(e.t){case"paragraph":t+=`${A(e.c)}
8
+ `;break;case"heading":t+=`${"#".repeat(Math.max(1,Math.min(nt,e.level)))} ${A(e.c)}
9
+ `;break;case"bullet":for(let n of e.items)t+=`- ${$(n).trim()}
10
+ `;break;case"ordered":e.items.forEach((n,r)=>{t+=`${e.start+r}. ${$(n).trim()}
11
+ `});break;case"task":for(let n of e.items)t+=`- [${n.checked?"x":" "}] ${A(n.c)}
12
+ `;break;case"code":t+=`\`\`\`${e.lang}
13
+ ${e.v}
14
+ \`\`\`
15
+ `;break;case"quote":t+=V($(e.c));break;case"panel":t+=`> [!${j[e.kind]}]
16
+ ${V($(e.c))}`;break;case"rule":t+=`---
17
+ `;break;case"table":{let n=r=>`| ${r.map(o=>A(o).replace(/\|/g,"\\|")).join(" | ")} |
18
+ `;e.header&&(t+=n(e.header)+`|${e.header.map(()=>" --- |").join("")}
19
+ `);for(let r of e.rows)t+=n(r);break}}return t}function st(s){return s.replace(/^\n+/,"").replace(/\s+$/,"")}function V(s){return s.replace(/\n$/,"").split(`
20
+ `).map(t=>t?`> ${t}`:">").join(`
21
+ `)+`
22
+ `}function _(s){let t=[];for(let e of s||[])if(!(!e||typeof e!="object"))switch(e.type){case"text":{let n={t:"text",v:String(e.text??"")},r=Array.isArray(e.marks)?e.marks:[];r.some(o=>o?.type==="code")&&(n={t:"code",v:String(e.text??"")});for(let o of r)!o||o.type==="code"||(o.type==="strong"?n={t:"strong",c:[n]}:o.type==="em"?n={t:"em",c:[n]}:o.type==="strike"?n={t:"strike",c:[n]}:o.type==="link"&&o.attrs?.href&&(n={t:"link",href:String(o.attrs.href),c:[n]}));t.push(n);break}case"hardBreak":t.push({t:"br"});break;case"mention":t.push({t:"text",v:String(e.attrs?.text||(e.attrs?.id?`@${e.attrs.id}`:"@someone"))});break;case"emoji":t.push({t:"text",v:String(e.attrs?.text||e.attrs?.shortName||"")});break;case"status":t.push({t:"text",v:String(e.attrs?.text||"")});break;case"date":t.push({t:"text",v:e.attrs?.timestamp?new Date(Number(e.attrs.timestamp)).toISOString().slice(0,10):""});break;case"inlineCard":{let n=String(e.attrs?.url||"");n&&t.push({t:"link",href:n,c:[{t:"text",v:n}]});break}default:Array.isArray(e.content)?t.push(..._(e.content)):typeof e.text=="string"&&t.push({t:"text",v:e.text})}return t}function y(s){let t=[];for(let e of s||[])if(!(!e||typeof e!="object"))switch(e.type){case"paragraph":t.push({t:"paragraph",c:_(e.content)});break;case"heading":t.push({t:"heading",level:Number(e.attrs?.level)||1,c:_(e.content)});break;case"bulletList":t.push({t:"bullet",items:(e.content||[]).map(n=>y(n?.content))});break;case"orderedList":t.push({t:"ordered",start:Number(e.attrs?.order)||1,items:(e.content||[]).map(n=>y(n?.content))});break;case"taskList":t.push({t:"task",items:(e.content||[]).map(n=>({checked:n?.attrs?.state==="DONE",c:_(n?.content)}))});break;case"decisionList":t.push({t:"bullet",items:(e.content||[]).map(n=>[{t:"paragraph",c:_(n?.content)}])});break;case"codeBlock":t.push({t:"code",lang:String(e.attrs?.language||""),v:(e.content||[]).map(n=>String(n?.text??"")).join("")});break;case"blockquote":t.push({t:"quote",c:y(e.content)});break;case"panel":{let n=String(e.attrs?.panelType||"info");t.push({t:"panel",kind:n in j?n:"info",c:y(e.content)});break}case"rule":t.push({t:"rule"});break;case"table":{let n=(e.content||[]).filter(i=>i?.type==="tableRow"),r=i=>(i.content||[]).map(h=>{let l=y(h?.content);return k($(l).trim().replace(/\n+/g," "))}),o=i=>(i.content||[]).length>0&&(i.content||[]).every(h=>h?.type==="tableHeader"),a=n.length&&o(n[0])?r(n[0]):null;t.push({t:"table",header:a,rows:n.slice(a?1:0).map(r)});break}case"mediaSingle":case"mediaGroup":case"media":case"mediaInline":t.push({t:"paragraph",c:[{t:"text",v:"[attachment]"}]});break;case"expand":case"nestedExpand":{let n=String(e.attrs?.title||"").trim();n&&t.push({t:"paragraph",c:[{t:"strong",c:[{t:"text",v:n}]}]}),t.push(...y(e.content));break}default:{let n=Array.isArray(e.content)?e.content:[];n.some(r=>r?.type==="text"||r?.type==="hardBreak")?t.push({t:"paragraph",c:_(n)}):n.length?t.push(...y(n)):typeof e.text=="string"&&e.text&&t.push({t:"paragraph",c:[{t:"text",v:e.text}]})}}return t}function $t(s){try{if(Array.isArray(s))return y(s);if(s&&typeof s=="object"){let t=s;return t.type==="doc"||!t.type&&Array.isArray(t.content)?y(t.content):y([t])}if(typeof s=="string")return P(s)}catch{}return[]}function Rt(s){try{return st($($t(s)))}catch{return""}}var It={amp:"&",lt:"<",gt:">",quot:'"',apos:"'",nbsp:"\xA0",hellip:"\u2026",mdash:"\u2014",ndash:"\u2013",lsquo:"\u2018",rsquo:"\u2019",ldquo:"\u201C",rdquo:"\u201D",copy:"\xA9",reg:"\xAE",trade:"\u2122"};function Y(s){return s.replace(/&(#x[0-9a-f]+|#\d+|[a-z]+);/gi,(t,e)=>{if(e[0]==="#"){let r=e[1]==="x"||e[1]==="X"?parseInt(e.slice(2),16):parseInt(e.slice(1),10);return Number.isFinite(r)&&r>0&&r<1114112?String.fromCodePoint(r):t}let n=It[e.toLowerCase()];return n===void 0?t:n})}function At(s){let t=[],e=/<!--[\s\S]*?-->|<\/([A-Za-z][\w:-]*)\s*>|<([A-Za-z][\w:-]*)((?:\s+[^\s=>/]+(?:\s*=\s*(?:"[^"]*"|'[^']*'|[^\s>]+))?)*)\s*(\/?)>|([^<]+)|(<)/g,n;for(;n=e.exec(s);)if(n[1])t.push({kind:"close",name:n[1].toLowerCase()});else if(n[2]){let r={},o=/([^\s=>/]+)(?:\s*=\s*(?:"([^"]*)"|'([^']*)'|([^\s>]+)))?/g,a;for(;a=o.exec(n[3]||"");)r[a[1].toLowerCase()]=Y(a[2]??a[3]??a[4]??"");let i=n[2].toLowerCase();t.push({kind:"open",name:i,attrs:r,selfClose:!!n[4]||i==="br"||i==="hr"||i==="img"||i==="input"})}else n[5]!==void 0?t.push({kind:"text",v:Y(n[5])}):n[6]&&t.push({kind:"text",v:"<"});return t}var tt=new Set(["p","div","h1","h2","h3","h4","h5","h6","ul","ol","li","blockquote","pre","hr","table","thead","tbody","tfoot","tr","td","th","section","article","header","footer","details","summary","figure","figcaption"]),Tt=new Set(["script","style","label","input","button","svg","head","title"]);function E(s){let t=[],e=0,n=[],r=[],o=l=>{let c=vt(n);n=[],c.length&&t.push(l?l(c):{t:"paragraph",c})},a=l=>{if(!l)return;let c={t:"text",v:l};for(let p=r.length-1;p>=0;p--){let u=r[p];u==="strong"?c={t:"strong",c:[c]}:u==="em"?c={t:"em",c:[c]}:u==="strike"?c={t:"strike",c:[c]}:c={t:"link",href:u.link,c:[c]}}n.push(c)},i=l=>{let c=[],p=1;for(;e<s.length;){let u=s[e++];if(u.kind==="open"&&u.name===l&&!u.selfClose&&p++,u.kind==="close"&&u.name===l&&(p--,p===0))break;c.push(u)}return c},h=l=>l.filter(c=>c.kind==="text").map(c=>c.v).join("");for(;e<s.length;){let l=s[e++];if(l.kind==="text"){a(l.v.replace(/\s+/g," "));continue}if(l.kind==="close"){if(l.name==="strong"||l.name==="b")H(r,"strong");else if(l.name==="em"||l.name==="i")H(r,"em");else if(l.name==="s"||l.name==="del"||l.name==="strike")H(r,"strike");else if(l.name==="a"){let p=r.map(u=>typeof u=="object").lastIndexOf(!0);p>=0&&r.splice(p,1)}else tt.has(l.name)&&o();continue}let c=l.name;if(Tt.has(c)){l.selfClose||i(c);continue}if(c==="br"){n.push({t:"br"});continue}if(c==="hr"){o(),t.push({t:"rule"});continue}if(c==="strong"||c==="b"){r.push("strong");continue}if(c==="em"||c==="i"){r.push("em");continue}if(c==="s"||c==="del"||c==="strike"){r.push("strike");continue}if(c==="a"){l.attrs.href?r.push({link:l.attrs.href}):r.push({link:""});continue}if(c==="code"){let p=h(i("code"));p&&n.push({t:"code",v:p});continue}if(c==="pre"){o();let p=i("pre"),u=p.find(x=>x.kind==="open"&&x.name==="code"),b=u&&/language-([\w+.-]+)/.exec(u.attrs.class||"")?.[1]||"";t.push({t:"code",lang:b,v:h(p).replace(/\n$/,"")});continue}if(/^h[1-6]$/.test(c)){o();let p=Number(c[1]),u=E(i(c));t.push({t:"heading",level:p,c:k($(u).trim().replace(/\n+/g," "))});continue}if(c==="blockquote"){o(),t.push({t:"quote",c:E(i(c))});continue}if(c==="ul"||c==="ol"){o();let p=i(c),u=(l.attrs["data-type"]||"")==="tasklist",b=[],x=[],T=0;for(;T<p.length;){let f=p[T++];if(f.kind==="open"&&f.name==="li"){let m=[],v=1;for(;T<p.length;){let d=p[T++];if(d.kind==="open"&&d.name==="li"&&!d.selfClose&&v++,d.kind==="close"&&d.name==="li"&&(v--,v===0))break;m.push(d)}b.push(m);let w=(f.attrs["data-checked"]||"")==="true"||m.some(d=>d.kind==="open"&&d.name==="input"&&"checked"in d.attrs);x.push(w)}}u||b.some(f=>f.some(m=>m.kind==="open"&&m.name==="input"&&(m.attrs.type||"")==="checkbox"))?t.push({t:"task",items:b.map((f,m)=>({checked:x[m],c:k($(E(f)).trim().replace(/\n+/g," "))}))}):c==="ul"?t.push({t:"bullet",items:b.map(f=>et(E(f)))}):t.push({t:"ordered",start:Math.max(1,parseInt(l.attrs.start||"1",10)||1),items:b.map(f=>et(E(f)))});continue}if(c==="table"){o();let p=i("table"),u=[],b=[],x=0;for(;x<p.length;){let M=p[x++];if(M.kind==="open"&&M.name==="tr"){let f=[],m=!0,v=1,w=[];for(;x<p.length;){let g=p[x++];if(g.kind==="open"&&g.name==="tr"&&!g.selfClose&&v++,g.kind==="close"&&g.name==="tr"&&(v--,v===0))break;w.push(g)}let d=0;for(;d<w.length;){let g=w[d++];if(g.kind==="open"&&(g.name==="td"||g.name==="th")){g.name!=="th"&&(m=!1);let G=[],U=1;for(;d<w.length;){let S=w[d++];if(S.kind==="open"&&S.name===g.name&&!S.selfClose&&U++,S.kind==="close"&&S.name===g.name&&(U--,U===0))break;G.push(S)}f.push(k($(E(G)).trim().replace(/\n+/g," ")))}}f.length&&(u.push(f),b.push(m))}}let T=u.length&&b[0]?u[0]:null;t.push({t:"table",header:T,rows:u.slice(T?1:0)});continue}if(tt.has(c)){o();continue}}return o(),t}function H(s,t){let e=s.lastIndexOf(t);e>=0&&s.splice(e,1)}function et(s){return s.length?s:[{t:"paragraph",c:[]}]}function vt(s){let t=[...s];for(;t.length&&t[0].t==="text"&&!t[0].v.trim();)t.shift();for(;t.length&&t[t.length-1].t==="text"&&!t[t.length-1].v.trim();)t.pop();t.length&&t[0].t==="text"&&(t[0]={t:"text",v:t[0].v.replace(/^\s+/," ")});let e=t.length-1;return e>=0&&t[e].t==="text"&&(t[e]={t:"text",v:t[e].v.replace(/\s+$/,"")}),t}function wt(s){try{let t=String(s??"");return/<[a-z][\s\S]*>/i.test(t)?E(At(t)):P(t)}catch{return[]}}function Bt(s){try{return st($(wt(s)))}catch{return""}}export{$t as adfToBlocks,Rt as adfToMarkup,$ as blocksToText,wt as htmlToBlocks,Bt as htmlToMarkup,Nt as markupToAdf,Et as markupToHtml,k as parseInline,P as parseMarkup,xt as plainTextToAdf};