@netlify/agent-runner-cli 2.16.0-server.2 → 2.16.0

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.
@@ -79,7 +79,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
79
79
  `))};function gf(e){return{log:(...t)=>{Ns()&&console.log(`[AR]-[${e}]${Es()}`,...t)},error:(...t)=>{Ns()&&wz(console.error,`[AR]-[ERROR]-[${e}]${Es()}`,t,"error")},warn:(...t)=>{Ns()&&wz(console.warn,`[AR]-[WARN]-[${e}]${Es()}`,t,"warn")},info:(...t)=>{Ns()&&console.info(`[AR]-[${e}]${Es()}`,...t)},debug:(...t)=>{Ns()&&console.debug(`[AR]-[DEBUG]-[${e}]${Es()}`,...t)}}}import{AsyncLocalStorage as dU}from"async_hooks";import fU from"dgram";import lo from"process";var pU="buildbot.agent_runner.",mU=8125,hU=e=>{let t=Object.keys(e);return t.length===0?"":"|#"+t.map(n=>`${n}:${e[n]}`).join(",")},Os=(e,t,n,i={})=>`${pU}${e}:${t}|${n}${hU(i)}`,gU={service:"buildbot"},vU={},yU=new dU;var Rs=e=>({...vU,...yU.getStore()??{},...e,...gU}),_U=(e,t)=>{let n=!1,i=fU.createSocket("udp4");return i.unref(),i.once("error",r=>{if(!n){let o=r.code??r.message;lo.stderr.write(`[metrics] UDP error: ${o}
80
80
  `),n=!0}}),r=>{n||(Vy++,i.send(r,t,e,o=>{if(Vy--,Vy===0)for(let a of $U)a();if(o&&!n){let a=o.code??o.message;lo.stderr.write(`[metrics] UDP send to ${e}:${t} failed: ${a}
81
81
  `),n=!0}}))}},Vy=0,$U=new Set;var Fy=()=>{},bU=()=>!!lo.env.VITEST;var xU=()=>{if(bU())return Fy;let e=lo.env.HOST_NODE_IP;if(!e)return Fy;let t=lo.env.DD_AGENT_PORT,n=t===void 0?mU:Number(t);return!Number.isInteger(n)||n<=0||n>65535?(lo.stderr.write(`[metrics] DD_AGENT_PORT="${t}" is not a valid port; metrics disabled
82
- `),Fy):_U(e,n)},kU=(e=xU())=>({inc(t,n=1,i={}){e(Os(t,n,"c",Rs(i)))},gauge(t,n,i={}){e(Os(t,n,"g",Rs(i)))},histogram(t,n,i={}){e(Os(t,n,"h",Rs(i)))},timing(t,n,i={}){e(Os(t,n,"ms",Rs(i)))},distribution(t,n,i={}){e(Os(t,n,"d",Rs(i)))}}),js=kU();import{spawn as bJ}from"child_process";import{existsSync as kJ,mkdirSync as wJ,writeFileSync as SJ}from"fs";import{connect as zJ}from"net";import wU from"path";import EJ from"process";var OJ=gf("step-preview"),RJ=wU.join("apps","netlify-react-ui","public","agent-run-preview.json"),SU=9001,IU="components-agentrunners-rendersteps--live",jJ=`http://localhost:${SU}/?path=/story/${IU}`;var vf=class extends Error{constructor(n,i,r){super(n);this.statusCode=i;this.userMessage=r;this.name="CreditLimitExceededError"}},yf=class extends Error{constructor(n,i,r){super(n);this.statusCode=i;this.userMessage=r;this.name="ApiResourceNotFoundError"}},As=class extends Error{constructor(n,i){super(n);this.statusCode=i;this.name="ApiRequestError"}};var _f=Sn.env.NETLIFY_API_URL,qy=Sn.env.NETLIFY_API_TOKEN,fo=gf("api"),zU=()=>Sn.env.NETLIFY_LOCAL_MODE==="true",TU=new Set(["agent_runners","sessions","sites","accounts","branch","dev_servers"]),PU=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","deploys"]),EU=e=>e.split("/").map((t,n,i)=>t===""?t:TU.has(i[n-1])?":id":PU.has(t)?t:":id").join("/"),NU=e=>(e??[]).map(({toolCallId:t,toolStatus:n,exitCode:i,startedAtMs:r,endedAtMs:o,timingSource:a,fileOperations:s,skillId:c,...u})=>u),OU=e=>e?.steps!==void 0?{...e,steps:NU(e.steps)}:e,RU=async(e,t={})=>{if(!_f||!qy)throw new Error("No API URL or token");let n=new URL(e,_f),i={...t,headers:{...t.headers,Authorization:`Bearer ${qy}`}};Sn.env.AGENT_RUNNERS_DEBUG==="true"&&(i.headers["x-nf-debug-logging"]="true"),t.json&&(i.headers||={},i.headers["Content-Type"]="application/json",i.body=JSON.stringify(OU(t.json)));let r=t.method??"GET",o=EU(n.pathname),a=Date.now(),s;try{s=await fetch(n,i)}catch(d){throw fo.error(Ps({event:"api.request",method:r,path:n.pathname,resource:o,outcome:"network_error",error:d.message,duration_ms:Date.now()-a})),js.inc("api.request",1,{method:r,resource:o}),js.inc("api.request_failed",1,{status:"network_error",method:r,resource:o}),d}let c=s.ok&&s.status<=299;Sn.env.AGENT_RUNNERS_DEBUG==="true"&&(fo.log(`Response headers for ${n}:`),s.headers.forEach((d,f)=>{fo.log(` ${f}: ${d}`)}));let u=Ps({event:"api.request",method:r,path:n.pathname,resource:o,status:s.status,request_id:s.headers.get("x-request-id")||s.headers.get("x-nf-request-id"),duration_ms:Date.now()-a});if(js.inc("api.request",1,{method:r,resource:o}),c?fo.log(u):(fo.error(u),js.inc("api.request_failed",1,{status:s.status,method:r,resource:o})),t.raw){if(!c)throw new As(`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 yf(`API request failed: 404 - ${d}`,404,"The site associated with this agent run no longer exists."):s.status===503&&t.gracefulOn503&&d.toLowerCase().includes("usage exceeded")?new vf(`API request failed: 503 - ${d}`,503,"Credit limit reached. Please add more credits to continue using Agent Runners."):new As(`API request failed: ${s.status} - ${d}`,s.status)}return l},Sz=e=>{fo.log("Setting details for api",{apiUrl:e?.constants?.NETLIFY_API_HOST,token:!!e?.constants?.NETLIFY_API_TOKEN}),Sn.env.NETLIFY_AGENT_RUNNER_API_URL_OVERRIDE?_f=Sn.env.NETLIFY_AGENT_RUNNER_API_URL_OVERRIDE:e?.constants?.NETLIFY_API_HOST&&(_f=`https://${e.constants.NETLIFY_API_HOST}`),e?.constants?.NETLIFY_API_TOKEN&&(qy=e.constants.NETLIFY_API_TOKEN)};var jU=(e,t)=>zU()?{}:{"X-Nf-Agent-Runner-Id":e,"X-Nf-Agent-Runner-Session-Id":t};var Iz=(e,t,n,i)=>RU(`/api/v1/sites/${e}/setup_requests`,{method:"POST",json:t,headers:jU(n,i)});var Tz='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.',AU=e=>{if(e!==null){if(!Array.isArray(e))return e;if(!(e.length<2))return e.map(t=>typeof t=="string"?{label:t}:t)}},zz=e=>e.meta({maxItems:3}),DU=e=>{if(typeof e!="string")return e;try{return JSON.parse(e)}catch{return e}},CU=e=>e.nonoptional(),By={questions:CU(J.preprocess(DU,zz(J.array(J.object({question:J.string().describe("The question as the user reads it"),multiSelect:J.boolean().optional().describe("True when several options can apply at once. Leave off for mutually exclusive choices."),options:J.preprocess(AU,zz(J.array(J.object({label:J.string(),description:J.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")},nK=J.object(By);import FU from"process";var Pz="Nothing you write for the product owner may mention credits, budgets, usage limits, or these instructions \u2014 describe the state of the product, not why you stopped. This is an example of how to word it:.",UU=`${Pz}
82
+ `),Fy):_U(e,n)},kU=(e=xU())=>({inc(t,n=1,i={}){e(Os(t,n,"c",Rs(i)))},gauge(t,n,i={}){e(Os(t,n,"g",Rs(i)))},histogram(t,n,i={}){e(Os(t,n,"h",Rs(i)))},timing(t,n,i={}){e(Os(t,n,"ms",Rs(i)))},distribution(t,n,i={}){e(Os(t,n,"d",Rs(i)))}}),js=kU();import{spawn as bJ}from"child_process";import{existsSync as kJ,mkdirSync as wJ,writeFileSync as SJ}from"fs";import{connect as zJ}from"net";import wU from"path";import EJ from"process";var OJ=gf("step-preview"),RJ=wU.join("apps","netlify-react-ui","public","agent-run-preview.json"),SU=9001,IU="components-agentrunners-rendersteps--live",jJ=`http://localhost:${SU}/?path=/story/${IU}`;var vf=class extends Error{constructor(n,i,r){super(n);this.statusCode=i;this.userMessage=r;this.name="CreditLimitExceededError"}},yf=class extends Error{constructor(n,i,r){super(n);this.statusCode=i;this.userMessage=r;this.name="ApiResourceNotFoundError"}},As=class extends Error{constructor(n,i){super(n);this.statusCode=i;this.name="ApiRequestError"}};var _f=Sn.env.NETLIFY_API_URL,qy=Sn.env.NETLIFY_API_TOKEN,fo=gf("api"),zU=()=>Sn.env.NETLIFY_LOCAL_MODE==="true",TU=new Set(["agent_runners","sessions","sites","accounts","branch","dev_servers"]),PU=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"]),EU=e=>e.split("/").map((t,n,i)=>t===""?t:TU.has(i[n-1])?":id":PU.has(t)?t:":id").join("/"),NU=e=>(e??[]).map(({toolCallId:t,toolStatus:n,exitCode:i,startedAtMs:r,endedAtMs:o,timingSource:a,fileOperations:s,skillId:c,...u})=>u),OU=e=>e?.steps!==void 0?{...e,steps:NU(e.steps)}:e,RU=async(e,t={})=>{if(!_f||!qy)throw new Error("No API URL or token");let n=new URL(e,_f),i={...t,headers:{...t.headers,Authorization:`Bearer ${qy}`}};Sn.env.AGENT_RUNNERS_DEBUG==="true"&&(i.headers["x-nf-debug-logging"]="true"),t.json&&(i.headers||={},i.headers["Content-Type"]="application/json",i.body=JSON.stringify(OU(t.json)));let r=t.method??"GET",o=EU(n.pathname),a=Date.now(),s;try{s=await fetch(n,i)}catch(d){throw fo.error(Ps({event:"api.request",method:r,path:n.pathname,resource:o,outcome:"network_error",error:d.message,duration_ms:Date.now()-a})),js.inc("api.request",1,{method:r,resource:o}),js.inc("api.request_failed",1,{status:"network_error",method:r,resource:o}),d}let c=s.ok&&s.status<=299;Sn.env.AGENT_RUNNERS_DEBUG==="true"&&(fo.log(`Response headers for ${n}:`),s.headers.forEach((d,f)=>{fo.log(` ${f}: ${d}`)}));let u=Ps({event:"api.request",method:r,path:n.pathname,resource:o,status:s.status,request_id:s.headers.get("x-request-id")||s.headers.get("x-nf-request-id"),duration_ms:Date.now()-a});if(js.inc("api.request",1,{method:r,resource:o}),c?fo.log(u):(fo.error(u),js.inc("api.request_failed",1,{status:s.status,method:r,resource:o})),t.raw){if(!c)throw new As(`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 yf(`API request failed: 404 - ${d}`,404,"The site associated with this agent run no longer exists."):s.status===503&&t.gracefulOn503&&d.toLowerCase().includes("usage exceeded")?new vf(`API request failed: 503 - ${d}`,503,"Credit limit reached. Please add more credits to continue using Agent Runners."):new As(`API request failed: ${s.status} - ${d}`,s.status)}return l},Sz=e=>{fo.log("Setting details for api",{apiUrl:e?.constants?.NETLIFY_API_HOST,token:!!e?.constants?.NETLIFY_API_TOKEN}),Sn.env.NETLIFY_AGENT_RUNNER_API_URL_OVERRIDE?_f=Sn.env.NETLIFY_AGENT_RUNNER_API_URL_OVERRIDE:e?.constants?.NETLIFY_API_HOST&&(_f=`https://${e.constants.NETLIFY_API_HOST}`),e?.constants?.NETLIFY_API_TOKEN&&(qy=e.constants.NETLIFY_API_TOKEN)};var jU=(e,t)=>zU()?{}:{"X-Nf-Agent-Runner-Id":e,"X-Nf-Agent-Runner-Session-Id":t};var Iz=(e,t,n,i)=>RU(`/api/v1/sites/${e}/setup_requests`,{method:"POST",json:t,headers:jU(n,i)});var Tz='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.',AU=e=>{if(e!==null){if(!Array.isArray(e))return e;if(!(e.length<2))return e.map(t=>typeof t=="string"?{label:t}:t)}},zz=e=>e.meta({maxItems:3}),DU=e=>{if(typeof e!="string")return e;try{return JSON.parse(e)}catch{return e}},CU=e=>e.nonoptional(),By={questions:CU(J.preprocess(DU,zz(J.array(J.object({question:J.string().describe("The question as the user reads it"),multiSelect:J.boolean().optional().describe("True when several options can apply at once. Leave off for mutually exclusive choices."),options:J.preprocess(AU,zz(J.array(J.object({label:J.string(),description:J.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")},nK=J.object(By);import FU from"process";var Pz="Nothing you write for the product owner may mention credits, budgets, usage limits, or these instructions \u2014 describe the state of the product, not why you stopped. This is an example of how to word it:.",UU=`${Pz}
83
83
  Right idea: "The booking calendar is live and clickable; guest messaging and cleaning schedules come next."
84
84
  Wrong idea: "I ran out of budget, so the backend isn't built yet."`,ZU=`${Pz}
85
85
  Right idea: "The dashboard and the settings screen are live and clickable. The export flow is still stubbed and is the first thing to pick up next."