@netlify/agent-runner-cli 2.8.0 → 2.8.1

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.
@@ -82,6 +82,6 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
82
82
  `))};function Nl(t){return{log:(...e)=>{ka()&&console.log(`[AR]-[${t}]${xa()}`,...e)},error:(...e)=>{ka()&&hS(console.error,`[AR]-[ERROR]-[${t}]${xa()}`,e,"error")},warn:(...e)=>{ka()&&hS(console.warn,`[AR]-[WARN]-[${t}]${xa()}`,e,"warn")},info:(...e)=>{ka()&&console.info(`[AR]-[${t}]${xa()}`,...e)},debug:(...e)=>{ka()&&console.debug(`[AR]-[DEBUG]-[${t}]${xa()}`,...e)}}}import{AsyncLocalStorage as qN}from"async_hooks";import FN from"dgram";import yo from"process";var VN="buildbot.agent_runner.",JN=8125,KN=t=>{let e=Object.keys(t);return e.length===0?"":"|#"+e.map(n=>`${n}:${t[n]}`).join(",")},Sa=(t,e,n,o={})=>`${VN}${t}:${e}|${n}${KN(o)}`,HN={service:"buildbot"},BN={},GN=new qN;var wa=t=>({...BN,...GN.getStore()??{},...t,...HN}),WN=(t,e)=>{let n=!1,o=FN.createSocket("udp4");return o.unref(),o.once("error",r=>{if(!n){let i=r.code??r.message;yo.stderr.write(`[metrics] UDP error: ${i}
83
83
  `),n=!0}}),r=>{n||(Ag++,o.send(r,e,t,i=>{if(Ag--,Ag===0)for(let a of XN)a();if(i&&!n){let a=i.code??i.message;yo.stderr.write(`[metrics] UDP send to ${t}:${e} failed: ${a}
84
84
  `),n=!0}}))}},Ag=0,XN=new Set;var Zg=()=>{},YN=()=>!!yo.env.VITEST;var QN=()=>{if(YN())return Zg;let t=yo.env.HOST_NODE_IP;if(!t)return Zg;let e=yo.env.DD_AGENT_PORT,n=e===void 0?JN:Number(e);return!Number.isInteger(n)||n<=0||n>65535?(yo.stderr.write(`[metrics] DD_AGENT_PORT="${e}" is not a valid port; metrics disabled
85
- `),Zg):WN(t,n)},e1=(t=QN())=>({inc(e,n=1,o={}){t(Sa(e,n,"c",wa(o)))},gauge(e,n,o={}){t(Sa(e,n,"g",wa(o)))},histogram(e,n,o={}){t(Sa(e,n,"h",wa(o)))},timing(e,n,o={}){t(Sa(e,n,"ms",wa(o)))},distribution(e,n,o={}){t(Sa(e,n,"d",wa(o)))}}),za=e1();import{spawn as BL}from"child_process";import{existsSync as WL,mkdirSync as XL,writeFileSync as YL}from"fs";import{connect as eq}from"net";import t1 from"path";import nq from"process";var iq=Nl("step-preview"),aq=t1.join("apps","netlify-react-ui","public","agent-run-preview.json"),r1=9001,n1="components-agentrunners-rendersteps--live",sq=`http://localhost:${r1}/?path=/story/${n1}`;var Dl=class extends Error{constructor(n,o,r){super(n);this.statusCode=o;this.userMessage=r;this.name="CreditLimitExceededError"}},Al=class extends Error{constructor(n,o,r){super(n);this.statusCode=o;this.userMessage=r;this.name="ApiResourceNotFoundError"}},Ia=class extends Error{constructor(n,o){super(n);this.statusCode=o;this.name="ApiRequestError"}};var Ug=Ta.env.NETLIFY_API_URL,Cg=Ta.env.NETLIFY_API_TOKEN,$o=Nl("api"),o1=()=>Ta.env.NETLIFY_LOCAL_MODE==="true",i1=new Set(["agent_runners","sessions","sites","accounts","branch","dev_servers"]),a1=new Set(["api","v1","agent_runners","sessions","sites","accounts","classify","diff","result","upload_urls","native_session","upload","update_usage","connections","tokens","ai-gateway","providers","agent-runners","token","database","branch","dev_servers","config","setup_requests"]),s1=t=>t.split("/").map((e,n,o)=>e===""?e:i1.has(o[n-1])?":id":a1.has(e)?e:":id").join("/"),c1=async(t,e={})=>{if(!Ug||!Cg)throw new Error("No API URL or token");let n=new URL(t,Ug),o={...e,headers:{...e.headers,Authorization:`Bearer ${Cg}`}};Ta.env.AGENT_RUNNERS_DEBUG==="true"&&(o.headers["x-nf-debug-logging"]="true"),e.json&&(o.headers||={},o.headers["Content-Type"]="application/json",o.body=JSON.stringify(e.json));let r=e.method??"GET",i=s1(n.pathname),a=Date.now(),s;try{s=await fetch(n,o)}catch(d){throw $o.error(ba({event:"api.request",method:r,path:n.pathname,resource:i,outcome:"network_error",error:d.message,duration_ms:Date.now()-a})),za.inc("api.request",1,{method:r,resource:i}),za.inc("api.request_failed",1,{status:"network_error",method:r,resource:i}),d}let c=s.ok&&s.status<=299;Ta.env.AGENT_RUNNERS_DEBUG==="true"&&($o.log(`Response headers for ${n}:`),s.headers.forEach((d,p)=>{$o.log(` ${p}: ${d}`)}));let u=ba({event:"api.request",method:r,path:n.pathname,resource:i,status:s.status,request_id:s.headers.get("x-request-id")||s.headers.get("x-nf-request-id"),duration_ms:Date.now()-a});if(za.inc("api.request",1,{method:r,resource:i}),c?$o.log(u):($o.error(u),za.inc("api.request_failed",1,{status:s.status,method:r,resource:i})),e.raw){if(!c)throw new Ia(`API request failed: ${s.status} ${s.statusText}`,s.status);return s}let l=await(s.headers.get("content-type")?.includes("application/json")?s.json():s.text());if(!c){let d=typeof l=="string"?l:JSON.stringify(l);throw s.status===404?new Al(`API request failed: 404 - ${d}`,404,"The site associated with this agent run no longer exists."):s.status===503&&e.gracefulOn503&&d.toLowerCase().includes("usage exceeded")?new Dl(`API request failed: 503 - ${d}`,503,"Credit limit reached. Please add more credits to continue using Agent Runners."):new Ia(`API request failed: ${s.status} - ${d}`,s.status)}return l},gS=t=>{$o.log("Setting details for api",{apiUrl:t?.constants?.NETLIFY_API_HOST,token:!!t?.constants?.NETLIFY_API_TOKEN}),t?.constants?.NETLIFY_API_HOST&&(Ug=`https://${t.constants.NETLIFY_API_HOST}`),t?.constants?.NETLIFY_API_TOKEN&&(Cg=t.constants.NETLIFY_API_TOKEN)};var u1=(t,e)=>o1()?{}:{"X-Nf-Agent-Runner-Id":t,"X-Nf-Agent-Runner-Session-Id":e};var vS=(t,e,n,o)=>c1(`/api/v1/sites/${t}/setup_requests`,{method:"POST",json:e,headers:u1(n,o)});var yS='Ask the user clarifying questions, preferably one to three, and pause until they answer. Use this instead of guessing when a decision materially changes the work. A question either offers two or three predefined options (one answer, or several with multiSelect) or omits options entirely so the user answers in their own words. The UI adds an "Other" choice and a free-text field for extra context to every question that has options, so an answer can name a choice you never offered or carry context beyond the label the user picked. The process pauses as soon as you call it; do not call it more than once or continue working after calling it.',l1=t=>Array.isArray(t)&&t.length===0?void 0:t,_S=t=>t.meta({maxItems:3}),d1=t=>{if(typeof t!="string")return t;try{return JSON.parse(t)}catch{return t}},p1=t=>t.nonoptional(),Mg={questions:p1(me.preprocess(d1,_S(me.array(me.object({question:me.string().describe("The question as the user reads it"),multiSelect:me.boolean().optional().describe("True when several options can apply at once. Leave off for mutually exclusive choices."),options:me.preprocess(l1,_S(me.array(me.object({label:me.string(),description:me.string().optional()})).min(2).optional())).describe('Two or three predefined answers. Omit entirely to ask an open question the user answers in their own words. The UI adds its own "Other" choice and a free-text field for extra context, so never add an option like "Other" or "None of these" yourself.')})).min(1)))).describe("One to three questions preferred, all asked in a single call")},Eq=me.object(Mg);import{renameSync as f1,writeFileSync as m1}from"fs";import Zl from"process";var Lg=(t,e)=>{let n=`${t}.partial`;m1(n,JSON.stringify(e)),f1(n,t)},qg=()=>new Promise(()=>{}),$S=()=>{let t=()=>Zl.exit(0);Zl.stdin.on("close",t),Zl.stdin.on("end",t)},bS=()=>{console.log=console.error,console.info=console.error,console.debug=console.error},Pa=(t,e=Zl.argv)=>{let n=e.indexOf(t);return n===-1?void 0:e[n+1]};var kr="netlify_interactions";var Ea="request_project_setup",xS="--questions-file",Fg="netlify_connections",Vg="request_service_authorization";var kS="--runner-id",SS="--session-id";var wS="--setup-file",Jg=(t,e)=>`mcp__${t}__${e}`,Kg=(t,e)=>`mcp_${t}_${e}`,Hg=(t,e)=>`${t}_${e}`,Dq=Jg(kr,"ask"),Aq=Kg(kr,"ask"),Zq=Hg(kr,"ask"),Uq=Jg(Fg,Vg),Cq=Kg(Fg,Vg),Mq=Hg(Fg,Vg),Lq=Jg(kr,Ea),qq=Kg(kr,Ea),Fq=Hg(kr,Ea);var h1=["production","deploy-preview","branch-deploy","dev"],g1=["exist","change"],v1=(t,e)=>me.array(me.object({name:t,required:me.boolean().default(!0).describe("Set false only if the task is genuinely complete without it.")})).min(1).describe(e),_1=me.string().min(1).describe("One sentence, shown to the user next to the field: what you are building and why it needs this."),y1=me.object({kind:me.literal("env_var"),reason:_1,key:me.string().min(1).regex(/^[A-Za-z_][A-Za-z0-9_]*$/).describe("The variable name exactly as the code reads it, e.g. STRIPE_SECRET_KEY."),secret:me.boolean().default(!0).describe("True for anything that must not be shown again once set: API keys, tokens, passwords."),mode:me.enum(g1).default("exist").describe('"exist" if any configured value will do. "change" if a value is set but is wrong for this task and must be replaced'),targets:v1(me.enum(h1),"The deploy contexts the task actually needs this in. Do not list contexts the task does not touch.")}),$1=me.discriminatedUnion("kind",[y1]),Bg={items:me.array($1).min(1).max(10).describe("Everything this project is missing for the task. Send them in one call, not one call per item")},Kq=me.object(Bg);var zS='Ask the user to configure something this project needs. Environment variables only, for now. Check what is already configured first with the `netlify` CLI, then raise only what is genuinely missing or wrong: `mode: "exist"` when any configured value will do, `mode: "change"` when a value is set but is wrong for this task. If nothing needs setting, this returns straight away and you continue. If something does, your run pauses while the user supplies it and then resumes automatically, so do not design around the gap or leave the work unfinished. Never end a run by telling the user to go and set something up themselves: raise it here instead, while the run can still act on it. Do NOT use this for values you can derive, generate, or read from the repo, or for a variable the code does not read. For a service that is not connected, use `request_service_authorization`.',b1=t=>{if(!t.some(n=>n.name==="production"&&n.required))return t;let e=t.find(n=>n.name==="deploy-preview");return e?t.map(n=>n===e?{...n,required:!0}:n):[...t,{name:"deploy-preview",required:!0}]},IS=(t,{interactionRefId:e,runnerId:n,sessionId:o})=>({requester_kind:"agent_run",agent_runner_id:n,agent_runner_session_id:o,interaction_ref_id:e,items:t.items.map(({kind:r,reason:i,key:a,secret:s,mode:c,targets:u})=>({kind:r,reason:i,config:{key:a,secret:s,mode:c},targets:b1(u.map(({name:l,required:d})=>({name:l,required:d})))}))});var Gg=new Ol({name:kr,version:"1.0.0"}),TS=Pa(xS),PS=Pa(wS),ES=Pa(kS),OS=Pa(SS),jS=bo.env.NETLIFY_SITE_ID||bo.env.SITE_ID,S1=(t,e)=>{bo.stderr.write(`[interactions-mcp] ${t}${e?` ${JSON.stringify(e)}`:""}
85
+ `),Zg):WN(t,n)},e1=(t=QN())=>({inc(e,n=1,o={}){t(Sa(e,n,"c",wa(o)))},gauge(e,n,o={}){t(Sa(e,n,"g",wa(o)))},histogram(e,n,o={}){t(Sa(e,n,"h",wa(o)))},timing(e,n,o={}){t(Sa(e,n,"ms",wa(o)))},distribution(e,n,o={}){t(Sa(e,n,"d",wa(o)))}}),za=e1();import{spawn as BL}from"child_process";import{existsSync as WL,mkdirSync as XL,writeFileSync as YL}from"fs";import{connect as eq}from"net";import t1 from"path";import nq from"process";var iq=Nl("step-preview"),aq=t1.join("apps","netlify-react-ui","public","agent-run-preview.json"),r1=9001,n1="components-agentrunners-rendersteps--live",sq=`http://localhost:${r1}/?path=/story/${n1}`;var Dl=class extends Error{constructor(n,o,r){super(n);this.statusCode=o;this.userMessage=r;this.name="CreditLimitExceededError"}},Al=class extends Error{constructor(n,o,r){super(n);this.statusCode=o;this.userMessage=r;this.name="ApiResourceNotFoundError"}},Ia=class extends Error{constructor(n,o){super(n);this.statusCode=o;this.name="ApiRequestError"}};var Ug=Ta.env.NETLIFY_API_URL,Cg=Ta.env.NETLIFY_API_TOKEN,$o=Nl("api"),o1=()=>Ta.env.NETLIFY_LOCAL_MODE==="true",i1=new Set(["agent_runners","sessions","sites","accounts","branch","dev_servers"]),a1=new Set(["api","v1","agent_runners","sessions","sites","accounts","classify","diff","result","upload_urls","native_session","upload","update_usage","connections","tokens","ai-gateway","providers","agent-runners","token","database","branch","dev_servers","config","setup_requests"]),s1=t=>t.split("/").map((e,n,o)=>e===""?e:i1.has(o[n-1])?":id":a1.has(e)?e:":id").join("/"),c1=async(t,e={})=>{if(!Ug||!Cg)throw new Error("No API URL or token");let n=new URL(t,Ug),o={...e,headers:{...e.headers,Authorization:`Bearer ${Cg}`}};Ta.env.AGENT_RUNNERS_DEBUG==="true"&&(o.headers["x-nf-debug-logging"]="true"),e.json&&(o.headers||={},o.headers["Content-Type"]="application/json",o.body=JSON.stringify(e.json));let r=e.method??"GET",i=s1(n.pathname),a=Date.now(),s;try{s=await fetch(n,o)}catch(d){throw $o.error(ba({event:"api.request",method:r,path:n.pathname,resource:i,outcome:"network_error",error:d.message,duration_ms:Date.now()-a})),za.inc("api.request",1,{method:r,resource:i}),za.inc("api.request_failed",1,{status:"network_error",method:r,resource:i}),d}let c=s.ok&&s.status<=299;Ta.env.AGENT_RUNNERS_DEBUG==="true"&&($o.log(`Response headers for ${n}:`),s.headers.forEach((d,p)=>{$o.log(` ${p}: ${d}`)}));let u=ba({event:"api.request",method:r,path:n.pathname,resource:i,status:s.status,request_id:s.headers.get("x-request-id")||s.headers.get("x-nf-request-id"),duration_ms:Date.now()-a});if(za.inc("api.request",1,{method:r,resource:i}),c?$o.log(u):($o.error(u),za.inc("api.request_failed",1,{status:s.status,method:r,resource:i})),e.raw){if(!c)throw new Ia(`API request failed: ${s.status} ${s.statusText}`,s.status);return s}let l=await(s.headers.get("content-type")?.includes("application/json")?s.json():s.text());if(!c){let d=typeof l=="string"?l:JSON.stringify(l);throw s.status===404?new Al(`API request failed: 404 - ${d}`,404,"The site associated with this agent run no longer exists."):s.status===503&&e.gracefulOn503&&d.toLowerCase().includes("usage exceeded")?new Dl(`API request failed: 503 - ${d}`,503,"Credit limit reached. Please add more credits to continue using Agent Runners."):new Ia(`API request failed: ${s.status} - ${d}`,s.status)}return l},gS=t=>{$o.log("Setting details for api",{apiUrl:t?.constants?.NETLIFY_API_HOST,token:!!t?.constants?.NETLIFY_API_TOKEN}),t?.constants?.NETLIFY_API_HOST&&(Ug=`https://${t.constants.NETLIFY_API_HOST}`),t?.constants?.NETLIFY_API_TOKEN&&(Cg=t.constants.NETLIFY_API_TOKEN)};var u1=(t,e)=>o1()?{}:{"X-Nf-Agent-Runner-Id":t,"X-Nf-Agent-Runner-Session-Id":e};var vS=(t,e,n,o)=>c1(`/api/v1/sites/${t}/setup_requests`,{method:"POST",json:e,headers:u1(n,o)});var yS='Ask the user clarifying questions, preferably one to three, and pause until they answer. Use this instead of guessing when a decision materially changes the work. A question either offers two or three predefined options (one answer, or several with multiSelect) or omits options entirely so the user answers in their own words. The UI adds an "Other" choice and a free-text field for extra context to every question that has options, so an answer can name a choice you never offered or carry context beyond the label the user picked. The process pauses as soon as you call it; do not call it more than once or continue working after calling it.',l1=t=>{if(t!==null){if(!Array.isArray(t))return t;if(!(t.length<2))return t.map(e=>typeof e=="string"?{label:e}:e)}},_S=t=>t.meta({maxItems:3}),d1=t=>{if(typeof t!="string")return t;try{return JSON.parse(t)}catch{return t}},p1=t=>t.nonoptional(),Mg={questions:p1(me.preprocess(d1,_S(me.array(me.object({question:me.string().describe("The question as the user reads it"),multiSelect:me.boolean().optional().describe("True when several options can apply at once. Leave off for mutually exclusive choices."),options:me.preprocess(l1,_S(me.array(me.object({label:me.string(),description:me.string().optional()})).min(2).optional())).describe('Two or three predefined answers. Omit entirely to ask an open question the user answers in their own words. The UI adds its own "Other" choice and a free-text field for extra context, so never add an option like "Other" or "None of these" yourself.')})).min(1)))).describe("One to three questions preferred, all asked in a single call")},Eq=me.object(Mg);import{renameSync as f1,writeFileSync as m1}from"fs";import Zl from"process";var Lg=(t,e)=>{let n=`${t}.partial`;m1(n,JSON.stringify(e)),f1(n,t)},qg=()=>new Promise(()=>{}),$S=()=>{let t=()=>Zl.exit(0);Zl.stdin.on("close",t),Zl.stdin.on("end",t)},bS=()=>{console.log=console.error,console.info=console.error,console.debug=console.error},Pa=(t,e=Zl.argv)=>{let n=e.indexOf(t);return n===-1?void 0:e[n+1]};var kr="netlify_interactions";var Ea="request_project_setup",xS="--questions-file",Fg="netlify_connections",Vg="request_service_authorization";var kS="--runner-id",SS="--session-id";var wS="--setup-file",Jg=(t,e)=>`mcp__${t}__${e}`,Kg=(t,e)=>`mcp_${t}_${e}`,Hg=(t,e)=>`${t}_${e}`,Dq=Jg(kr,"ask"),Aq=Kg(kr,"ask"),Zq=Hg(kr,"ask"),Uq=Jg(Fg,Vg),Cq=Kg(Fg,Vg),Mq=Hg(Fg,Vg),Lq=Jg(kr,Ea),qq=Kg(kr,Ea),Fq=Hg(kr,Ea);var h1=["production","deploy-preview","branch-deploy","dev"],g1=["exist","change"],v1=(t,e)=>me.array(me.object({name:t,required:me.boolean().default(!0).describe("Set false only if the task is genuinely complete without it.")})).min(1).describe(e),_1=me.string().min(1).describe("One sentence, shown to the user next to the field: what you are building and why it needs this."),y1=me.object({kind:me.literal("env_var"),reason:_1,key:me.string().min(1).regex(/^[A-Za-z_][A-Za-z0-9_]*$/).describe("The variable name exactly as the code reads it, e.g. STRIPE_SECRET_KEY."),secret:me.boolean().default(!0).describe("True for anything that must not be shown again once set: API keys, tokens, passwords."),mode:me.enum(g1).default("exist").describe('"exist" if any configured value will do. "change" if a value is set but is wrong for this task and must be replaced'),targets:v1(me.enum(h1),"The deploy contexts the task actually needs this in. Do not list contexts the task does not touch.")}),$1=me.discriminatedUnion("kind",[y1]),Bg={items:me.array($1).min(1).max(10).describe("Everything this project is missing for the task. Send them in one call, not one call per item")},Kq=me.object(Bg);var zS='Ask the user to configure something this project needs. Environment variables only, for now. Check what is already configured first with the `netlify` CLI, then raise only what is genuinely missing or wrong: `mode: "exist"` when any configured value will do, `mode: "change"` when a value is set but is wrong for this task. If nothing needs setting, this returns straight away and you continue. If something does, your run pauses while the user supplies it and then resumes automatically, so do not design around the gap or leave the work unfinished. Never end a run by telling the user to go and set something up themselves: raise it here instead, while the run can still act on it. Do NOT use this for values you can derive, generate, or read from the repo, or for a variable the code does not read. For a service that is not connected, use `request_service_authorization`.',b1=t=>{if(!t.some(n=>n.name==="production"&&n.required))return t;let e=t.find(n=>n.name==="deploy-preview");return e?t.map(n=>n===e?{...n,required:!0}:n):[...t,{name:"deploy-preview",required:!0}]},IS=(t,{interactionRefId:e,runnerId:n,sessionId:o})=>({requester_kind:"agent_run",agent_runner_id:n,agent_runner_session_id:o,interaction_ref_id:e,items:t.items.map(({kind:r,reason:i,key:a,secret:s,mode:c,targets:u})=>({kind:r,reason:i,config:{key:a,secret:s,mode:c},targets:b1(u.map(({name:l,required:d})=>({name:l,required:d})))}))});var Gg=new Ol({name:kr,version:"1.0.0"}),TS=Pa(xS),PS=Pa(wS),ES=Pa(kS),OS=Pa(SS),jS=bo.env.NETLIFY_SITE_ID||bo.env.SITE_ID,S1=(t,e)=>{bo.stderr.write(`[interactions-mcp] ${t}${e?` ${JSON.stringify(e)}`:""}
86
86
  `)};bS();gS({constants:{NETLIFY_API_HOST:bo.env.NETLIFY_API_HOST||"api.netlify.com",NETLIFY_API_TOKEN:bo.env.NETLIFY_AUTH_TOKEN||bo.env.NETLIFY_API_TOKEN}});Gg.registerTool("ask",{description:yS,inputSchema:Mg},t=>(TS&&Lg(TS,t),qg()));var Ul=(t,e=!1)=>({content:[{type:"text",text:t}],...e?{isError:!0}:{}});PS&&Gg.registerTool(Ea,{description:zS,inputSchema:Bg},async t=>{if(!jS)return Ul("No project id available, so this project cannot be checked.",!0);let e=k1(),n;try{n=await vS(jS,IS(t,{interactionRefId:e,runnerId:ES,sessionId:OS}),ES,OS)}catch(r){return Ul(`Could not raise the setup request: ${r?.message}`,!0)}if(S1("setup request created",{id:n?.id,satisfied:n?.satisfied,items:t.items.length}),n?.satisfied)return Ul("This project is already configured for everything you asked about. Continue.");let o=String(n?.id??"");return o?(Lg(PS,{refId:e,setupRequestId:o}),qg()):Ul("Could not raise the setup request: the API returned no request id.",!0)});$S();await Gg.connect(new Rl);
87
87
  //# sourceMappingURL=interactions-mcp-server.js.map
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@netlify/agent-runner-cli",
3
3
  "type": "module",
4
- "version": "2.8.0",
4
+ "version": "2.8.1",
5
5
  "description": "CLI tool for running Netlify agents",
6
6
  "main": "./dist/index.js",
7
7
  "types": "./dist/index.d.ts",
@@ -66,9 +66,11 @@
66
66
  "@commitlint/config-conventional": "^20.0.0",
67
67
  "@eslint/compat": "^2.0.0",
68
68
  "@eslint/js": "^10.0.1",
69
+ "@grpc/grpc-js": "1.14.5",
69
70
  "@netlify/axis": "^1.21.0",
70
71
  "@netlify/eslint-config-node": "^7.0.1",
71
72
  "@netlify/skills": "1.7.0",
73
+ "@opentelemetry/sdk-trace-base": "2.9.0",
72
74
  "@types/node": "^24.5.0",
73
75
  "@typescript-eslint/eslint-plugin": "^8.0.0",
74
76
  "@typescript-eslint/parser": "^8.0.0",
@@ -97,6 +99,7 @@
97
99
  "@openai/codex": "0.162.0",
98
100
  "@opentelemetry/api": "^1.9.0",
99
101
  "@opentelemetry/exporter-trace-otlp-grpc": "0.218.0",
102
+ "@opentelemetry/otlp-transformer": "0.218.0",
100
103
  "execa": "^9.6.1",
101
104
  "fastify": "5.12.5",
102
105
  "minimist": "^1.2.8",