@zibby/skills 0.1.56 → 0.1.57

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -336,6 +336,7 @@ Personal (your own profile) \u2014 PUBLISHES IMMEDIATELY:
336
336
 
337
337
  Notes:
338
338
  - Org-page posts are ALWAYS created as a DRAFT; personal-profile posts are ALWAYS published live \u2014 choose the tool accordingly.
339
+ - PUBLISHING IS OUTWARD-FACING AND IRREVERSIBLE. linkedin_publish_post posts live to a real audience the instant you call it \u2014 there is no undo and no draft state for member profiles. Only call it after the human has EXPLICITLY approved publishing THIS specific text in THIS conversation. If you wrote or edited the text, show it and get an explicit "yes, publish" first; if you are unsure, HOLD/draft rather than publish. The org draft tool (linkedin_create_draft_post) is the safe default when a human still needs to review.
339
340
  - If the relevant LinkedIn integration is not connected these tools return { ok:false, error }; treat that as "LinkedIn unavailable" and continue.`,resolve(){let s=Xi();return s?{type:"stdio",command:"node",args:[s,"../dist/linkedin.js","linkedinSkill"],env:{},description:this.description,alwaysLoad:!0}:{command:null,args:[],env:{},description:this.description}},async handleToolCall(s,t){try{switch(s){case"linkedin_list_organizations":{let{body:e}=await Re("/rest/organizationAcls?q=roleAssignee&role=ADMINISTRATOR&state=APPROVED",{},"linkedin_business"),r=Array.isArray(e.elements)?e.elements:[],i=[];for(let n of r){let o=n.organizationalTarget||n["organizationalTarget~"]||n.organization,a=Dt(o);if(!a)continue;let c="",d="";try{let l=(await Re(`/rest/organizations/${a}`,{},"linkedin_business")).body;c=rn(l),d=l.vanityName||""}catch{}i.push({id:a,urn:`urn:li:organization:${a}`,name:c,vanityName:d})}return JSON.stringify({ok:!0,count:i.length,organizations:i})}case"linkedin_create_draft_post":{let e=t?.organizationUrn||t?.organizationId||t?.organization||t?.orgId,r=Dt(e);if(!r)return JSON.stringify({ok:!1,error:"A valid organizationId or organizationUrn (urn:li:organization:{id}) is required"});let i=t?.text;if(typeof i!="string"||!i.trim())return JSON.stringify({ok:!1,error:"text (the post commentary) is required"});let n=t?.visibility?String(t.visibility).toUpperCase():"PUBLIC",o=`urn:li:organization:${r}`,c=await Re("/rest/posts",{method:"POST",body:{author:o,commentary:i,visibility:n,distribution:{feedDistribution:"MAIN_FEED",targetEntities:[],thirdPartyDistributionChannels:[]},lifecycleState:"DRAFT",isReshareDisabledByAuthor:!1}},"linkedin_business"),d=Oe(c.headers,"x-restli-id")||Oe(c.headers,"x-linkedin-id")||c.body?.id||null;return JSON.stringify({ok:!0,postUrn:d,author:o,lifecycleState:"DRAFT",visibility:n,status:c.status})}case"linkedin_publish_post":{let{memberId:e}=await Bt("linkedin_personal");if(!e)return JSON.stringify({ok:!1,error:"LinkedIn (personal) not connected or member id unavailable \u2014 reconnect LinkedIn Personal"});let r=t?.text;if(typeof r!="string"||!r.trim())return JSON.stringify({ok:!1,error:"text (the post body) is required"});let i=t?.visibility?String(t.visibility).toUpperCase():"PUBLIC",n=`urn:li:person:${e}`,a=await Re("/rest/posts",{method:"POST",body:{author:n,commentary:r,visibility:i,distribution:{feedDistribution:"MAIN_FEED",targetEntities:[],thirdPartyDistributionChannels:[]},lifecycleState:"PUBLISHED",isReshareDisabledByAuthor:!1}},"linkedin_personal"),c=Oe(a.headers,"x-restli-id")||Oe(a.headers,"x-linkedin-id")||a.body?.id||null;return JSON.stringify({ok:!0,postUrn:c,author:n,lifecycleState:"PUBLISHED",visibility:i,status:a.status})}default:return JSON.stringify({ok:!1,error:`Unknown tool: ${s}`})}}catch(e){return JSON.stringify({ok:!1,error:e.message})}},tools:[{name:"linkedin_list_organizations",description:"List the LinkedIn Organizations (company Pages) the authenticated member ADMINISTERS. Returns [{ id, urn, name, vanityName }]. Call this first to choose the author org for a draft post.",input_schema:{type:"object",properties:{}}},{name:"linkedin_create_draft_post",description:"Create a DRAFT post on a LinkedIn Organization (company Page). The post is created in DRAFT state (never published automatically) so a human can review and publish it in LinkedIn. Returns the created post URN.",input_schema:{type:"object",properties:{organizationId:{type:"string",description:'The numeric organization id (e.g. "12345"). Alternative to organizationUrn.'},organizationUrn:{type:"string",description:'The organization URN, e.g. "urn:li:organization:12345". Alternative to organizationId.'},text:{type:"string",description:"The post commentary (the body text of the post)."},visibility:{type:"string",enum:["PUBLIC"],description:"Post visibility. Defaults to PUBLIC."}},required:["text"]}},{name:"linkedin_publish_post",description:"PUBLISH a post to the authenticated member's OWN LinkedIn profile feed (personal). UNLIKE linkedin_create_draft_post (which only drafts on a company Page), this PUBLISHES the post IMMEDIATELY \u2014 LinkedIn has no DRAFT state for member profiles, so there is no human review step. Returns the created post URN. Requires the LinkedIn Personal integration connected.",input_schema:{type:"object",properties:{text:{type:"string",description:"The post body (the commentary text of the post)."},visibility:{type:"string",enum:["PUBLIC","CONNECTIONS"],description:"Post visibility: PUBLIC (anyone) or CONNECTIONS (your connections only). Defaults to PUBLIC."}},required:["text"]}}]};var Gt={id:"chat_notify",description:"Chat notification meta-skill \u2014 routes to whichever messaging integration (Slack OR Lark) the user has configured for this project.",envKeys:[...x.envKeys||[],...K.envKeys||[]],get serverName(){if(process.env.SLACK_CHANNEL)return x.serverName;if(process.env.LARK_RECEIVE_ID)return K.serverName;if(process.env.SLACK_BOT_TOKEN)return x.serverName},get allowedTools(){return process.env.SLACK_CHANNEL?x.allowedTools||[]:process.env.LARK_RECEIVE_ID?K.allowedTools||[]:process.env.SLACK_BOT_TOKEN?x.allowedTools||[]:[]},promptFragment:`## Chat notifications (Slack OR Lark \u2014 at least one connected)
340
341
  You can post chat messages via:
341
342
  - slack_post_message (channel, text[, blocks]) \u2014 Slack. The \`channel\` is REQUIRED on every call.
package/dist/linkedin.js CHANGED
@@ -1,4 +1,4 @@
1
- import{existsSync as y}from"fs";import{fileURLToPath as f}from"url";import{dirname as g,resolve as I}from"path";import{resolveIntegrationToken as m,clearTokenCache as k}from"@zibby/core/backend-client.js";function b(){if(process.env.MCP_SKILL_PATH)return process.env.MCP_SKILL_PATH;let t=g(f(import.meta.url)),e=I(t,"..","bin","mcp-skill.mjs");return y(e)?e:null}var L="202506",S="https://api.linkedin.com";function h(t){if(t==null)return null;let e=String(t).trim();if(!e)return null;let n=e.match(/urn:li:organization:(\d+)/i);return n?n[1]:/^\d+$/.test(e)?e:null}function P(t){if(!t)return"";if(t.localizedName)return String(t.localizedName);let e=t.name&&t.name.localized;if(e&&typeof e=="object"){let n=Object.values(e)[0];if(n)return String(n)}return""}function c(t,e){return t?typeof t.get=="function"?t.get(e):t[e]||t[e.toLowerCase()]||null:null}async function u(t,e={},n="linkedin_business"){let l=async()=>{let{token:i}=await m(n);if(typeof i!="string"||!i)throw new Error("LinkedIn is not connected: no access token available. Connect LinkedIn in Integrations.");let o=t.startsWith("https://")?t:`${S}${t}`,s=await fetch(o,{method:e.method||"GET",headers:{Authorization:`Bearer ${i}`,"LinkedIn-Version":L,"X-Restli-Protocol-Version":"2.0.0",Accept:"application/json",...e.body?{"Content-Type":"application/json"}:{},...e.headers},body:e.body?JSON.stringify(e.body):void 0});if(!s.ok){let d=await s.text().catch(()=>"");throw new Error(`LinkedIn API ${s.status}: ${d.slice(0,300)}`)}let r=await s.text().catch(()=>""),a={};if(r&&r.trim())try{a=JSON.parse(r)}catch{a={raw:r}}return{status:s.status,headers:s.headers,body:a}};try{return await l()}catch(i){let o=String(i?.message||i||"").toLowerCase();if(!(o.includes("token")||o.includes("401")||o.includes("unauthorized")))throw i;return k(n),l()}}var T={id:"linkedin",serverName:"linkedin",allowedTools:["mcp__linkedin__*"],envKeys:[],description:"LinkedIn \u2014 (business) list admin Organizations + create DRAFT posts on a company Page, and (personal) PUBLISH a post to your own member profile feed",promptFragment:`## LinkedIn (connected)
1
+ import{existsSync as m}from"fs";import{fileURLToPath as f}from"url";import{dirname as g,resolve as I}from"path";import{resolveIntegrationToken as y,clearTokenCache as b}from"@zibby/core/backend-client.js";function k(){if(process.env.MCP_SKILL_PATH)return process.env.MCP_SKILL_PATH;let t=g(f(import.meta.url)),e=I(t,"..","bin","mcp-skill.mjs");return m(e)?e:null}var L="202506",S="https://api.linkedin.com";function h(t){if(t==null)return null;let e=String(t).trim();if(!e)return null;let n=e.match(/urn:li:organization:(\d+)/i);return n?n[1]:/^\d+$/.test(e)?e:null}function _(t){if(!t)return"";if(t.localizedName)return String(t.localizedName);let e=t.name&&t.name.localized;if(e&&typeof e=="object"){let n=Object.values(e)[0];if(n)return String(n)}return""}function c(t,e){return t?typeof t.get=="function"?t.get(e):t[e]||t[e.toLowerCase()]||null:null}async function u(t,e={},n="linkedin_business"){let l=async()=>{let{token:i}=await y(n);if(typeof i!="string"||!i)throw new Error("LinkedIn is not connected: no access token available. Connect LinkedIn in Integrations.");let o=t.startsWith("https://")?t:`${S}${t}`,s=await fetch(o,{method:e.method||"GET",headers:{Authorization:`Bearer ${i}`,"LinkedIn-Version":L,"X-Restli-Protocol-Version":"2.0.0",Accept:"application/json",...e.body?{"Content-Type":"application/json"}:{},...e.headers},body:e.body?JSON.stringify(e.body):void 0});if(!s.ok){let d=await s.text().catch(()=>"");throw new Error(`LinkedIn API ${s.status}: ${d.slice(0,300)}`)}let r=await s.text().catch(()=>""),a={};if(r&&r.trim())try{a=JSON.parse(r)}catch{a={raw:r}}return{status:s.status,headers:s.headers,body:a}};try{return await l()}catch(i){let o=String(i?.message||i||"").toLowerCase();if(!(o.includes("token")||o.includes("401")||o.includes("unauthorized")))throw i;return b(n),l()}}var A={id:"linkedin",serverName:"linkedin",allowedTools:["mcp__linkedin__*"],envKeys:[],description:"LinkedIn \u2014 (business) list admin Organizations + create DRAFT posts on a company Page, and (personal) PUBLISH a post to your own member profile feed",promptFragment:`## LinkedIn (connected)
2
2
  You can post to LinkedIn two ways \u2014 an ORG company Page (business) and your own member PROFILE (personal). Each path needs its own LinkedIn integration connected.
3
3
 
4
4
  Business (company Page) \u2014 DRAFT only:
@@ -10,4 +10,5 @@ Personal (your own profile) \u2014 PUBLISHES IMMEDIATELY:
10
10
 
11
11
  Notes:
12
12
  - Org-page posts are ALWAYS created as a DRAFT; personal-profile posts are ALWAYS published live \u2014 choose the tool accordingly.
13
- - If the relevant LinkedIn integration is not connected these tools return { ok:false, error }; treat that as "LinkedIn unavailable" and continue.`,resolve(){let t=b();return t?{type:"stdio",command:"node",args:[t,"../dist/linkedin.js","linkedinSkill"],env:{},description:this.description,alwaysLoad:!0}:{command:null,args:[],env:{},description:this.description}},async handleToolCall(t,e){try{switch(t){case"linkedin_list_organizations":{let{body:n}=await u("/rest/organizationAcls?q=roleAssignee&role=ADMINISTRATOR&state=APPROVED",{},"linkedin_business"),l=Array.isArray(n.elements)?n.elements:[],i=[];for(let o of l){let s=o.organizationalTarget||o["organizationalTarget~"]||o.organization,r=h(s);if(!r)continue;let a="",d="";try{let p=(await u(`/rest/organizations/${r}`,{},"linkedin_business")).body;a=P(p),d=p.vanityName||""}catch{}i.push({id:r,urn:`urn:li:organization:${r}`,name:a,vanityName:d})}return JSON.stringify({ok:!0,count:i.length,organizations:i})}case"linkedin_create_draft_post":{let n=e?.organizationUrn||e?.organizationId||e?.organization||e?.orgId,l=h(n);if(!l)return JSON.stringify({ok:!1,error:"A valid organizationId or organizationUrn (urn:li:organization:{id}) is required"});let i=e?.text;if(typeof i!="string"||!i.trim())return JSON.stringify({ok:!1,error:"text (the post commentary) is required"});let o=e?.visibility?String(e.visibility).toUpperCase():"PUBLIC",s=`urn:li:organization:${l}`,a=await u("/rest/posts",{method:"POST",body:{author:s,commentary:i,visibility:o,distribution:{feedDistribution:"MAIN_FEED",targetEntities:[],thirdPartyDistributionChannels:[]},lifecycleState:"DRAFT",isReshareDisabledByAuthor:!1}},"linkedin_business"),d=c(a.headers,"x-restli-id")||c(a.headers,"x-linkedin-id")||a.body?.id||null;return JSON.stringify({ok:!0,postUrn:d,author:s,lifecycleState:"DRAFT",visibility:o,status:a.status})}case"linkedin_publish_post":{let{memberId:n}=await m("linkedin_personal");if(!n)return JSON.stringify({ok:!1,error:"LinkedIn (personal) not connected or member id unavailable \u2014 reconnect LinkedIn Personal"});let l=e?.text;if(typeof l!="string"||!l.trim())return JSON.stringify({ok:!1,error:"text (the post body) is required"});let i=e?.visibility?String(e.visibility).toUpperCase():"PUBLIC",o=`urn:li:person:${n}`,r=await u("/rest/posts",{method:"POST",body:{author:o,commentary:l,visibility:i,distribution:{feedDistribution:"MAIN_FEED",targetEntities:[],thirdPartyDistributionChannels:[]},lifecycleState:"PUBLISHED",isReshareDisabledByAuthor:!1}},"linkedin_personal"),a=c(r.headers,"x-restli-id")||c(r.headers,"x-linkedin-id")||r.body?.id||null;return JSON.stringify({ok:!0,postUrn:a,author:o,lifecycleState:"PUBLISHED",visibility:i,status:r.status})}default:return JSON.stringify({ok:!1,error:`Unknown tool: ${t}`})}}catch(n){return JSON.stringify({ok:!1,error:n.message})}},tools:[{name:"linkedin_list_organizations",description:"List the LinkedIn Organizations (company Pages) the authenticated member ADMINISTERS. Returns [{ id, urn, name, vanityName }]. Call this first to choose the author org for a draft post.",input_schema:{type:"object",properties:{}}},{name:"linkedin_create_draft_post",description:"Create a DRAFT post on a LinkedIn Organization (company Page). The post is created in DRAFT state (never published automatically) so a human can review and publish it in LinkedIn. Returns the created post URN.",input_schema:{type:"object",properties:{organizationId:{type:"string",description:'The numeric organization id (e.g. "12345"). Alternative to organizationUrn.'},organizationUrn:{type:"string",description:'The organization URN, e.g. "urn:li:organization:12345". Alternative to organizationId.'},text:{type:"string",description:"The post commentary (the body text of the post)."},visibility:{type:"string",enum:["PUBLIC"],description:"Post visibility. Defaults to PUBLIC."}},required:["text"]}},{name:"linkedin_publish_post",description:"PUBLISH a post to the authenticated member's OWN LinkedIn profile feed (personal). UNLIKE linkedin_create_draft_post (which only drafts on a company Page), this PUBLISHES the post IMMEDIATELY \u2014 LinkedIn has no DRAFT state for member profiles, so there is no human review step. Returns the created post URN. Requires the LinkedIn Personal integration connected.",input_schema:{type:"object",properties:{text:{type:"string",description:"The post body (the commentary text of the post)."},visibility:{type:"string",enum:["PUBLIC","CONNECTIONS"],description:"Post visibility: PUBLIC (anyone) or CONNECTIONS (your connections only). Defaults to PUBLIC."}},required:["text"]}}]};export{u as linkedinApi,T as linkedinSkill,h as parseOrgId};
13
+ - PUBLISHING IS OUTWARD-FACING AND IRREVERSIBLE. linkedin_publish_post posts live to a real audience the instant you call it \u2014 there is no undo and no draft state for member profiles. Only call it after the human has EXPLICITLY approved publishing THIS specific text in THIS conversation. If you wrote or edited the text, show it and get an explicit "yes, publish" first; if you are unsure, HOLD/draft rather than publish. The org draft tool (linkedin_create_draft_post) is the safe default when a human still needs to review.
14
+ - If the relevant LinkedIn integration is not connected these tools return { ok:false, error }; treat that as "LinkedIn unavailable" and continue.`,resolve(){let t=k();return t?{type:"stdio",command:"node",args:[t,"../dist/linkedin.js","linkedinSkill"],env:{},description:this.description,alwaysLoad:!0}:{command:null,args:[],env:{},description:this.description}},async handleToolCall(t,e){try{switch(t){case"linkedin_list_organizations":{let{body:n}=await u("/rest/organizationAcls?q=roleAssignee&role=ADMINISTRATOR&state=APPROVED",{},"linkedin_business"),l=Array.isArray(n.elements)?n.elements:[],i=[];for(let o of l){let s=o.organizationalTarget||o["organizationalTarget~"]||o.organization,r=h(s);if(!r)continue;let a="",d="";try{let p=(await u(`/rest/organizations/${r}`,{},"linkedin_business")).body;a=_(p),d=p.vanityName||""}catch{}i.push({id:r,urn:`urn:li:organization:${r}`,name:a,vanityName:d})}return JSON.stringify({ok:!0,count:i.length,organizations:i})}case"linkedin_create_draft_post":{let n=e?.organizationUrn||e?.organizationId||e?.organization||e?.orgId,l=h(n);if(!l)return JSON.stringify({ok:!1,error:"A valid organizationId or organizationUrn (urn:li:organization:{id}) is required"});let i=e?.text;if(typeof i!="string"||!i.trim())return JSON.stringify({ok:!1,error:"text (the post commentary) is required"});let o=e?.visibility?String(e.visibility).toUpperCase():"PUBLIC",s=`urn:li:organization:${l}`,a=await u("/rest/posts",{method:"POST",body:{author:s,commentary:i,visibility:o,distribution:{feedDistribution:"MAIN_FEED",targetEntities:[],thirdPartyDistributionChannels:[]},lifecycleState:"DRAFT",isReshareDisabledByAuthor:!1}},"linkedin_business"),d=c(a.headers,"x-restli-id")||c(a.headers,"x-linkedin-id")||a.body?.id||null;return JSON.stringify({ok:!0,postUrn:d,author:s,lifecycleState:"DRAFT",visibility:o,status:a.status})}case"linkedin_publish_post":{let{memberId:n}=await y("linkedin_personal");if(!n)return JSON.stringify({ok:!1,error:"LinkedIn (personal) not connected or member id unavailable \u2014 reconnect LinkedIn Personal"});let l=e?.text;if(typeof l!="string"||!l.trim())return JSON.stringify({ok:!1,error:"text (the post body) is required"});let i=e?.visibility?String(e.visibility).toUpperCase():"PUBLIC",o=`urn:li:person:${n}`,r=await u("/rest/posts",{method:"POST",body:{author:o,commentary:l,visibility:i,distribution:{feedDistribution:"MAIN_FEED",targetEntities:[],thirdPartyDistributionChannels:[]},lifecycleState:"PUBLISHED",isReshareDisabledByAuthor:!1}},"linkedin_personal"),a=c(r.headers,"x-restli-id")||c(r.headers,"x-linkedin-id")||r.body?.id||null;return JSON.stringify({ok:!0,postUrn:a,author:o,lifecycleState:"PUBLISHED",visibility:i,status:r.status})}default:return JSON.stringify({ok:!1,error:`Unknown tool: ${t}`})}}catch(n){return JSON.stringify({ok:!1,error:n.message})}},tools:[{name:"linkedin_list_organizations",description:"List the LinkedIn Organizations (company Pages) the authenticated member ADMINISTERS. Returns [{ id, urn, name, vanityName }]. Call this first to choose the author org for a draft post.",input_schema:{type:"object",properties:{}}},{name:"linkedin_create_draft_post",description:"Create a DRAFT post on a LinkedIn Organization (company Page). The post is created in DRAFT state (never published automatically) so a human can review and publish it in LinkedIn. Returns the created post URN.",input_schema:{type:"object",properties:{organizationId:{type:"string",description:'The numeric organization id (e.g. "12345"). Alternative to organizationUrn.'},organizationUrn:{type:"string",description:'The organization URN, e.g. "urn:li:organization:12345". Alternative to organizationId.'},text:{type:"string",description:"The post commentary (the body text of the post)."},visibility:{type:"string",enum:["PUBLIC"],description:"Post visibility. Defaults to PUBLIC."}},required:["text"]}},{name:"linkedin_publish_post",description:"PUBLISH a post to the authenticated member's OWN LinkedIn profile feed (personal). UNLIKE linkedin_create_draft_post (which only drafts on a company Page), this PUBLISHES the post IMMEDIATELY \u2014 LinkedIn has no DRAFT state for member profiles, so there is no human review step. Returns the created post URN. Requires the LinkedIn Personal integration connected.",input_schema:{type:"object",properties:{text:{type:"string",description:"The post body (the commentary text of the post)."},visibility:{type:"string",enum:["PUBLIC","CONNECTIONS"],description:"Post visibility: PUBLIC (anyone) or CONNECTIONS (your connections only). Defaults to PUBLIC."}},required:["text"]}}]};export{u as linkedinApi,A as linkedinSkill,h as parseOrgId};
package/dist/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zibby/skills",
3
- "version": "0.1.56",
3
+ "version": "0.1.57",
4
4
  "description": "Built-in skill definitions for the Zibby agent-workflow framework",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zibby/skills",
3
- "version": "0.1.56",
3
+ "version": "0.1.57",
4
4
  "description": "Built-in skill definitions for the Zibby agent-workflow framework",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",