@netlify/agent-runner-cli 1.90.2 → 1.90.3

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/bin-local.js CHANGED
@@ -5,7 +5,7 @@ import U from"process";import Kr from"path";import Jr from"fs";import Ci from"mi
5
5
  ${s}
6
6
  </extracted_error_chunk>`).join(`
7
7
 
8
- `);return o.length>e.length*.8?e:o}import{execSync as Vn}from"child_process";import Rr from"fs/promises";import zn from"path";import ye from"process";import{getTracer as Xn}from"@netlify/otel";import ke from"process";var Q=class extends Error{constructor(r,i,n,o=!1){super(r);this.statusCode=i;this.userMessage=n;this.isCreditLimitExceeded=o;this.name="GracefulShutdownError"}},je=e=>e instanceof Q;var Ye=ke.env.NETLIFY_API_URL,qe=ke.env.NETLIFY_API_TOKEN,K=x("api"),_e=()=>ke.env.NETLIFY_LOCAL_MODE==="true",ce=async(e,t={})=>{if(!Ye||!qe)throw new Error("No API URL or token");let r=new URL(e,Ye),i={...t,headers:{...t.headers,Authorization:`Bearer ${qe}`}};ke.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(t.json));let n=await fetch(r,i),o=n.ok&&n.status<=299;if(ke.env.AGENT_RUNNERS_DEBUG==="true")K.log(`Response headers for ${r}:`),n.headers.forEach((a,c)=>{K.log(` ${c}: ${a}`)});else{let a=n.headers.get("x-request-id")||n.headers.get("x-nf-request-id");K.log(`Request ID for ${r}: ${a||"N/A"}`)}if(o||K.error(`Got status ${n.status} for request ${r}`),t.raw){if(!o)throw new Error(`API request failed: ${n.status} ${n.statusText}`);return n}let s=await(n.headers.get("content-type")?.includes("application/json")?n.json():n.text());if(!o){let a=typeof s=="string"?s:JSON.stringify(s);throw n.status===404?new Q(`API request failed: 404 - ${a}`,404,"The site associated with this agent run no longer exists."):n.status===503&&t.gracefulOn503&&a.toLowerCase().includes("usage exceeded")?new Q(`API request failed: 503 - ${a}`,503,"Credit limit reached. Please add more credits to continue using Agent Runners.",!0):new Error(`API request failed: ${n.status} - ${a}`)}return s},Mt=e=>{K.log("Setting details for api",{apiUrl:e?.constants?.NETLIFY_API_HOST,token:!!e?.constants?.NETLIFY_API_TOKEN}),e?.constants?.NETLIFY_API_HOST&&(Ye=`https://${e.constants.NETLIFY_API_HOST}`),e?.constants?.NETLIFY_API_TOKEN&&(qe=e.constants.NETLIFY_API_TOKEN)},Ut=()=>({apiUrl:Ye,token:qe}),Pe=async(e,t)=>_e()?(K.log("Mock API: updateRunner called",{runnerId:e,data:t}),{id:e,...t}):ce(`/api/v1/agent_runners/${e}`,{method:"PUT",json:t}),J=async(e,t,r)=>_e()?(K.log("Mock API: updateRunnerSession called",JSON.stringify({runnerId:e,sessionId:t,data:r},null,2)),{id:e,sessionId:t,...r}):ce(`/api/v1/agent_runners/${e}/sessions/${t}`,{method:"PUT",json:r});var Gt=async e=>_e()?(K.log("Mock API: getSite called",{siteId:e}),{id:e,published_deploy:{id:"id"}}):ce(`/api/v1/sites/${e}`),jt=async(e,t)=>_e()?(K.log("Mock API: getRunnerSession called",{runnerId:e,sessionId:t}),{id:t,runnerId:e,state:"running"}):ce(`/api/v1/agent_runners/${e}/sessions/${t}`),Yt=(e,t,r)=>ce(`/api/v1/accounts/${e}/ai-gateway/token`,{headers:{"X-Nf-Agent-Runner-Id":t,"X-Nf-Agent-Runner-Session-Id":r},gracefulOn503:!0}),qt=(e,t,r)=>ce(`/api/v1/sites/${e}/ai-gateway/token`,{headers:{"X-Nf-Agent-Runner-Id":t,"X-Nf-Agent-Runner-Session-Id":r},gracefulOn503:!0}),Bt=async(e,t)=>_e()?(K.log("Mock API: getDiffUploadUrls called",{runnerId:e,sessionId:t}),{result:{upload_url:"https://s3.mock.com/mock-upload-url-result",s3_key:"mock-s3-key-result"},cumulative:{upload_url:"https://s3.mock.com/mock-upload-url-cumulative",s3_key:"mock-s3-key-cumulative"}}):ce(`/api/v1/agent_runners/${e}/sessions/${t}/diff/upload_urls`,{method:"POST"}),Ht=async(e,t)=>_e()?(K.log("Mock API: updateSessionUsage called",{runnerId:e,sessionId:t}),{id:t,runnerId:e,usage:0}):ce(`/api/v1/agent_runners/${e}/sessions/${t}/update_usage`,{method:"POST"}),lt=async(e,t)=>{K.log(`Uploading diff to S3: ${e.substring(0,50)}...`);let r=await fetch(e,{method:"PUT",body:t,headers:{"Content-Type":"text/plain"}});if(!r.ok)throw new Error(`S3 upload failed with status ${r.status}`);return r};var Ee=x("ai_gateway"),ct=null;var Be=async()=>{if(ct)return ct;Ee.log("Fetching available AI gateway providers");let e=await fetch(`${Ut().apiUrl}/api/v1/ai-gateway/providers`);if(!e.ok)throw new Error(`Failed to fetch AI gateway providers: ${e.statusText}`);let t=await e.json();return ct=t,Ee.log("Cached AI gateway providers",{providerCount:Object.keys(t.providers).length}),t},rn=async(e,t)=>{let i=(await Be()).providers[e];if(!i)return Ee.log(`Provider '${e}' not found`),!1;let n=i.models.includes(t);return Ee.log(`Model validation for ${e}/${t}`,{isAvailable:n}),n},Wt=async({config:e})=>{let t,r,i,n,o=!e.site?.published_deploy;if(!(o?e.accountId:e.siteId))throw new Error(`No entity id for ${o?"account":"site"}`);let a=async()=>{clearTimeout(i),Ee.log("Requesting AI gateway information");let l=await(o?Yt(e.accountId,e.id,e.sessionId):qt(e.siteId,e.id,e.sessionId));if({token:t,url:n}=l,r=l.expires_at?l.expires_at*1e3:void 0,Ee.log("Got AI gateway information",{token:!!t,expiresAt:r,url:n}),r){let d=r-Date.now()-6e4;d>0&&(i=setTimeout(()=>{a()},d))}};return await Promise.all([a(),Be()]),{get url(){return n},get token(){return t},isModelAvailableForProvider:rn}};import te from"process";import oe from"path";import He from"fs";import{fileURLToPath as un}from"url";import{createRequire as dn}from"module";import{execa as pn,execaCommand as lo}from"execa";import{Transform as nn}from"stream";function on(){let e=process.env.NETLIFY_SENSITIVE_ENV_KEYS;return e?e.split(",").map(t=>t.trim()).filter(Boolean):[]}function sn(e){let t=e.toLowerCase();return t==="true"||t==="false"?!0:e.trim().length<4}function an(){let t=on().map(r=>process.env[r]).filter(r=>!(!r||sn(r)));return[...new Set(t)].sort((r,i)=>i.length-r.length)}function ie(e){if(typeof e!="string")return e;let t=an();if(t.length===0)return e;let r=e;return t.forEach(i=>{let n=new RegExp(ln(i),"g");r=r.replace(n,"******")}),r}function ln(e){return e.replace(/[.*+?^${}()|[\]\\]/g,"\\$&")}var xe=class extends nn{constructor(t={}){super({...t,objectMode:!1})}_transform(t,r,i){let n=t.toString(),o=ie(n);i(null,o)}};function Kt(){if(!(process.env.NETLIFY_MASK_LOGS!=="false"))return;let t=process.stdout.write.bind(process.stdout),r=process.stderr.write.bind(process.stderr);process.stdout.write=function(i,n,o){let s=typeof i=="string"?ie(i):i;return typeof n=="function"?t(s,n):t(s,n,o)},process.stderr.write=function(i,n,o){let s=typeof i=="string"?ie(i):i;return typeof n=="function"?r(s,n):r(s,n,o)}}var Ne=null,Jt=e=>(Ne&&Ne.destroy(),Ne=new ue({totalAllowedTime:e}),Ne),Vt=()=>Ne;var ue=class{constructor({totalAllowedTime:t}){this.withStageTimer=async(t,r,i)=>{if(this.isTimeExpired())throw new Error(`${t} stage did not complete in the allowed time. Time has already expired.`);let n=this.onTimesUp(()=>{throw new Error(`${t} stage did not complete in the allowed time.`)}),o=null,s=null;i!==void 0&&(s=new Promise((a,c)=>{o=setTimeout(()=>{c(new Error(`${t} stage exceeded its maximum duration of ${i}ms`))},i)}));try{return s?await Promise.race([r(),s]):await r()}finally{n(),o&&clearTimeout(o)}};this.startTime=Date.now(),this.totalAllowedTime=t,this.globalTimeoutId=null,this.subscribers=[],this.hasTimedOut=!1,this.setupGlobalTimeout()}getElapsedTime(){return Date.now()-this.startTime}getRemainingTime(){let t=this.getElapsedTime(),r=this.totalAllowedTime-t;return Math.max(0,r)}isTimeExpired(){return this.getRemainingTime()===0||this.hasTimedOut}setupGlobalTimeout(){this.globalTimeoutId&&clearTimeout(this.globalTimeoutId),this.globalTimeoutId=setTimeout(()=>{this.notifyTimeUp()},this.totalAllowedTime)}notifyTimeUp(){this.hasTimedOut=!0;for(let t=this.subscribers.length-1;t>=0;t--)try{this.subscribers[t]()}catch(r){console.error("TimeKeeper: Error in time up callback:",r)}}onTimesUp(t){if(this.subscribers.push(t),this.hasTimedOut)try{t()}catch(r){console.error("TimeKeeper: Error in time up callback:",r)}return()=>{let r=this.subscribers.indexOf(t);r>-1&&this.subscribers.splice(r,1)}}off(t){let r=this.subscribers.indexOf(t);r>-1&&this.subscribers.splice(r,1)}clearSubscribers(){this.subscribers.length=0}getSubscriberCount(){return this.subscribers.length}destroy(){this.globalTimeoutId&&(clearTimeout(this.globalTimeoutId),this.globalTimeoutId=null),this.clearSubscribers()}static{this.timeUnits={seconds:t=>t*1e3,minutes:t=>t*60*1e3,hours:t=>t*60*60*1e3}}};var zt="netlify-agent-runner-context.md",ut="task-history",ee=".netlify",me="results.md",dt="assets";var Xt="free";var ge=1800*1e3,y={Environment:"environment",UserMessage:"user-message",AgentMessage:"agent-message",Task:"task",RunCommand:"run-command",Explore:"explore",Plan:"plan",FileRead:"file-read",FileWrite:"file-write",Notebook:"notebook",Web:"web",Todo:"todo",Reasoning:"reasoning",Skill:"skill",Memorize:"memorize",Deployment:"deployment",SiteGeneration:"site-generation"};var Zt={name:"@netlify/agent-runner-cli",type:"module",version:"1.90.2",description:"CLI tool for running Netlify agents",main:"./dist/index.js",types:"./dist/index.d.ts",exports:"./dist/index.js",bin:{"agent-runner-cli":"./dist/bin.js","agent-runner-cli-local":"./dist/bin-local.js"},files:["dist/**/*.js","dist/**/*.d.ts","dist/skills/**","patches","scripts"],scripts:{build:"tsup",dev:"tsup --watch",prepare:"husky install node_modules/@netlify/eslint-config-node/.husky/",prepublishOnly:"npm ci && npm test",prepack:"npm run build",test:"run-s build format test:dev",format:"run-s build format:check-fix:*","format:ci":"run-s build format:check:*","format:check-fix:lint":"run-e format:check:lint format:fix:lint","format:check:lint":"cross-env-shell eslint $npm_package_config_eslint","format:fix:lint":"cross-env-shell eslint --fix $npm_package_config_eslint","format:check-fix:prettier":"run-e format:check:prettier format:fix:prettier","format:check:prettier":"cross-env-shell prettier --check $npm_package_config_prettier","format:fix:prettier":"cross-env-shell prettier --write $npm_package_config_prettier","test:dev":"run-s build test:dev:*","test:ci":"run-s build test:ci:*","test:dev:vitest":"LOG=0 vitest --exclude '**/integration/**'","test:ci:vitest":"LOG=0 c8 -r lcovonly -r text -r json vitest --exclude '**/integration/**'","test:integration":"vitest run test/integration/","test:integration:codex":"vitest run test/integration/codex.test.ts","test:integration:claude":"vitest run test/integration/claude.test.ts","test:integration:gemini":"vitest run test/integration/gemini.test.ts","test:integration:create-stage":"vitest run test/integration/create.test.ts","test:integration:skill-invocation":"vitest run test/integration/skill-invocation.test.ts","check:types":"tsc --noEmit",postinstall:"node scripts/postinstall.js"},config:{eslint:'--cache --format=codeframe --max-warnings=0 "{src,scripts,test,.github}/**/*.{js,ts,md,html}"',prettier:'--ignore-path .gitignore --loglevel=warn "{src,scripts,test,.github}/**/*.{js,ts,md,yml,json,html}" "*.{js,ts,yml,json,html}" ".*.{js,ts,yml,json,html}" "!**/package-lock.json" "!package-lock.json" "!src/skills/**/*.md"'},keywords:[],license:"MIT",repository:"netlify/agent-runner-cli",bugs:{url:"https://github.com/netlify/agent-runner-cli/issues"},author:"Netlify Inc.",directories:{test:"test"},devDependencies:{"@commitlint/cli":"^20.0.0","@commitlint/config-conventional":"^20.0.0","@eslint/compat":"^2.0.0","@eslint/js":"^9.35.0","@netlify/eslint-config-node":"^7.0.1","@types/node":"^24.5.0","@typescript-eslint/eslint-plugin":"^8.0.0","@typescript-eslint/parser":"^8.0.0","@vitest/eslint-plugin":"^1.6.6",c8:"^10.0.0","eslint-config-prettier":"^10.1.8","eslint-plugin-n":"^17.0.0",husky:"^9.0.0","patch-package":"^8.0.0",tsup:"^8.5.0",typescript:"^5.0.0","typescript-eslint":"^8.44.0",vitest:"^4.0.16"},dependencies:{"@anthropic-ai/claude-code":"2.1.63","@anthropic-ai/sdk":"0.78.0","@google/gemini-cli":"0.31.0","@netlify/otel":"^5.1.2","@openai/codex":"0.110.0","@opentelemetry/exporter-trace-otlp-grpc":"0.57.2",execa:"^9.6.1",kaddidlehopper:"^0.7.2",minimist:"^1.2.8",openai:"6.26.0"}};var mn=un(import.meta.url),gn=oe.dirname(mn),fn=dn(import.meta.url),Te=x("shell"),pt=new Set,hn={preferLocal:!0},A=(e,t,r)=>{let[i,n]=yn(t,r),o={...hn,...n},s=pn(e,i,o);wn(s,o),En(s);let a=r?.idleTimeout;return a&&a>0&&_n(s,a),s};var yn=function(e,t){return Array.isArray(e)?[e,t]:typeof e=="object"&&e!==null?[[],e]:[[],void 0]},wn=(e,t)=>{if(t.stdio!==void 0||t.stdout!==void 0||t.stderr!==void 0)return;if(te.env.NETLIFY_MASK_LOGS!=="false"){e.all?.pipe(new xe).pipe(te.stdout),e.stdout?.pipe(new xe).pipe(te.stdout),e.stderr?.pipe(new xe).pipe(te.stderr);return}e.stdout?.pipe(te.stdout),e.stderr?.pipe(te.stderr)},mt=(e,t="SIGTERM")=>{try{return e.pid&&!e.killed?(te.kill(-e.pid,t),Te.log(`Killed process ${e.pid} with signal ${t}`),!0):!1}catch(r){return Te.error("Error killing process:",r),!1}},Qt=e=>mt(e,"SIGKILL"),_n=(e,t)=>{let r=null,i=()=>{Te.log(`Process ${e.pid} killed due to idle timeout (no output for ${t}ms)`),mt(e,"SIGTERM"),setTimeout(()=>{e.pid&&!e.killed&&(Te.log(`Force killing idle process ${e.pid}`),Qt(e))},5e3)},n=()=>{r&&clearTimeout(r),r=setTimeout(i,t)};n(),e.stdout?.on("data",n),e.stderr?.on("data",n);let o=()=>{r&&(clearTimeout(r),r=null)};e.on("exit",o),e.on("error",o)},En=e=>{pt.add(e);let t=Vt();if(t){let r=t.onTimesUp(()=>{Te.log(`Global timer expired, killing process ${e.pid}`),mt(e,"SIGTERM"),setTimeout(()=>{e.pid&&!e.killed&&(Te.log(`Force killing process ${e.pid} after timeout`),Qt(e))},5e3)});e.on("exit",()=>{pt.delete(e),r()}),e.on("error",()=>{pt.delete(e),r()})}};function We(e,t){return!!se(e,t)}function se(e,t){if(!te.env.NETLIFY_LOCAL_MODE)try{let n=fn.resolve(Zt.name),o=oe.dirname(n);for(;o!==oe.dirname(o);){let s=oe.dirname(o);if(oe.basename(s)==="node_modules"){let a=oe.join(s,".bin",t);if(He.existsSync(a))return a;break}o=s}}catch(n){console.error("Could not resolve package.json",n)}if(te.env.NODE_PATH){let n=oe.join(te.env.NODE_PATH,".bin",t);if(He.existsSync(n))return n}let r=oe.join(e,"node_modules",".bin",t);if(He.existsSync(r))return r;let i=oe.join(gn,"..","node_modules",".bin",t);if(He.existsSync(i))return i}var xn=x("utils"),Tn=e=>new Promise(t=>{setTimeout(t,e)}),Ke=(e,t=3e3)=>{let r=!1,i=null,n=[],o=null,s=(...a)=>{if(r)return i=a,new Promise(d=>{n.push(d)});r=!0;let c,l=new Promise(d=>{c=d});return o=(async()=>{await Promise.resolve();let d=await e(...a);for(c(d);;){if(await Tn(t),!i)return r=!1,o=null,d;let m=i,u=n;i=null,n=[],d=await e(...m),u.forEach(w=>{w(d)})}})(),l};return s.flush=async()=>{if((r||i)&&o)return await o,s.flush()},s},Ie=(e,t,r=!1)=>{let i=null,n=null,o=null,s=function(...a){n=a,o=this;let c=r&&!i;clearTimeout(i),i=setTimeout(()=>{i=null,r||(e.apply(o,n),n=null,o=null)},t),c&&(e.apply(o,n),n=null,o=null)};return s.cancel=()=>{clearTimeout(i),i=null,n=null,o=null},s.flush=()=>{if(i){clearTimeout(i);let a=n,c=o;i=null,n=null,o=null,e.apply(c,a)}},s},er=(e,t=!0,r)=>{if(e)try{return JSON.parse(e)}catch(i){t&&(r?.error?r.error("Could not parse JSON",i):xn.error("Could not parse JSON",i))}},gt=e=>e.charAt(0).toUpperCase()+e.slice(1),de=e=>e.split("-").map(t=>t.length===2?t.toUpperCase():gt(t)).join(" ");function fe(e,t){t&&e.log(`Skill invoked: ${t}`)}var tr=e=>Object.fromEntries(Object.entries(e).filter(([,t])=>t!==void 0)),rr=(e,t)=>{let n=".netlify.app",o="agent-";if(!t)return`${o}${e.slice(0,6)}`;let a=`--${t}${n}`;if(a.length>55)return"";let c=60-a.length;if(c<=0)return"";if(c>=o.length+6){let l=Math.min(c-o.length,e.length);return`${o}${e.slice(0,l)}`}return e.slice(0,c)};var In=50*1024,ft=(e,t=In)=>{if(!e||typeof e!="string"||e.length<=t)return e;let i=e.startsWith("```")?"\n... [truncated]\n```":"... [truncated]";return e.slice(0,t)+i};import{Buffer as nr}from"buffer";import Sn from"path";var ir=x("repo"),sr=async({config:e,isRetry:t,cwd:r=process.cwd()})=>{ir.info("Getting runner diffs");let i=await Rn(r),{hasChanges:n}=i,{status:o}=i;if(!n)return{hasChanges:!1};if(!t){let _=An(o);await Cn(_,r)}ir.info("Changes after processing"),await yt(r);let s=await wt(o,r);if(await ht(s,r),n=await bn(r),!n)return{hasChanges:!1,ignored:s};process.env.NETLIFY_INTERNAL_GIT="1";try{await A("git",["commit","-m","Agent runner"],{cwd:r})}finally{process.env.NETLIFY_INTERNAL_GIT="0"}let a={stdio:["ignore","pipe","pipe"],cwd:r},c=await A("git",["diff",e.runSha,"HEAD"],a),l=String(c.stdout??"");if(n=!!l,!n)return await or(r),{hasChanges:!1,ignored:s};let d=await A("git",["diff",e.runSha,"HEAD","--binary"],a),m=String(d.stdout??""),u,w;if(e.sha){let _=await A("git",["diff",e.sha,"HEAD"],a);u=String(_.stdout??"");let S=await A("git",["diff",e.sha,"HEAD","--binary"],a),h=String(S.stdout??"");u!==h&&(w=nr.from(h).toString("base64"))}await or(r);let E={hasChanges:!0,diff:l,resultDiff:u,ignored:s};return l!==m&&(E.diffBinary=nr.from(m).toString("base64")),w&&(E.resultDiffBinary=w),E},or=async(e=process.cwd())=>{process.env.NETLIFY_LOCAL_MODE&&await A("git",["reset","--soft","HEAD~1"],{cwd:e})},ht=async(e=[],t=process.cwd())=>{process.env.NETLIFY_INTERNAL_GIT="1";try{await A("git",["add",".",...e],{cwd:t})}finally{process.env.NETLIFY_INTERNAL_GIT="0"}},yt=async(e=process.cwd())=>{let t=await A("git",["status","-s"],{cwd:e});return String(t.stdout??"")},ar=/.. (.+)?\.log$/,vn=[ar],Rn=async(e=process.cwd())=>{let t=await yt(e);return{hasChanges:(t.trim().length===0?[]:t.split(`
8
+ `);return o.length>e.length*.8?e:o}import{execSync as Vn}from"child_process";import Rr from"fs/promises";import zn from"path";import ye from"process";import{getTracer as Xn}from"@netlify/otel";import ke from"process";var Q=class extends Error{constructor(r,i,n,o=!1){super(r);this.statusCode=i;this.userMessage=n;this.isCreditLimitExceeded=o;this.name="GracefulShutdownError"}},je=e=>e instanceof Q;var Ye=ke.env.NETLIFY_API_URL,qe=ke.env.NETLIFY_API_TOKEN,K=x("api"),_e=()=>ke.env.NETLIFY_LOCAL_MODE==="true",ce=async(e,t={})=>{if(!Ye||!qe)throw new Error("No API URL or token");let r=new URL(e,Ye),i={...t,headers:{...t.headers,Authorization:`Bearer ${qe}`}};ke.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(t.json));let n=await fetch(r,i),o=n.ok&&n.status<=299;if(ke.env.AGENT_RUNNERS_DEBUG==="true")K.log(`Response headers for ${r}:`),n.headers.forEach((a,c)=>{K.log(` ${c}: ${a}`)});else{let a=n.headers.get("x-request-id")||n.headers.get("x-nf-request-id");K.log(`Request ID for ${r}: ${a||"N/A"}`)}if(o||K.error(`Got status ${n.status} for request ${r}`),t.raw){if(!o)throw new Error(`API request failed: ${n.status} ${n.statusText}`);return n}let s=await(n.headers.get("content-type")?.includes("application/json")?n.json():n.text());if(!o){let a=typeof s=="string"?s:JSON.stringify(s);throw n.status===404?new Q(`API request failed: 404 - ${a}`,404,"The site associated with this agent run no longer exists."):n.status===503&&t.gracefulOn503&&a.toLowerCase().includes("usage exceeded")?new Q(`API request failed: 503 - ${a}`,503,"Credit limit reached. Please add more credits to continue using Agent Runners.",!0):new Error(`API request failed: ${n.status} - ${a}`)}return s},Mt=e=>{K.log("Setting details for api",{apiUrl:e?.constants?.NETLIFY_API_HOST,token:!!e?.constants?.NETLIFY_API_TOKEN}),e?.constants?.NETLIFY_API_HOST&&(Ye=`https://${e.constants.NETLIFY_API_HOST}`),e?.constants?.NETLIFY_API_TOKEN&&(qe=e.constants.NETLIFY_API_TOKEN)},Ut=()=>({apiUrl:Ye,token:qe}),Pe=async(e,t)=>_e()?(K.log("Mock API: updateRunner called",{runnerId:e,data:t}),{id:e,...t}):ce(`/api/v1/agent_runners/${e}`,{method:"PUT",json:t}),J=async(e,t,r)=>_e()?(K.log("Mock API: updateRunnerSession called",JSON.stringify({runnerId:e,sessionId:t,data:r},null,2)),{id:e,sessionId:t,...r}):ce(`/api/v1/agent_runners/${e}/sessions/${t}`,{method:"PUT",json:r});var Gt=async e=>_e()?(K.log("Mock API: getSite called",{siteId:e}),{id:e,published_deploy:{id:"id"}}):ce(`/api/v1/sites/${e}`),jt=async(e,t)=>_e()?(K.log("Mock API: getRunnerSession called",{runnerId:e,sessionId:t}),{id:t,runnerId:e,state:"running"}):ce(`/api/v1/agent_runners/${e}/sessions/${t}`),Yt=(e,t,r)=>ce(`/api/v1/accounts/${e}/ai-gateway/token`,{headers:{"X-Nf-Agent-Runner-Id":t,"X-Nf-Agent-Runner-Session-Id":r},gracefulOn503:!0}),qt=(e,t,r)=>ce(`/api/v1/sites/${e}/ai-gateway/token`,{headers:{"X-Nf-Agent-Runner-Id":t,"X-Nf-Agent-Runner-Session-Id":r},gracefulOn503:!0}),Bt=async(e,t)=>_e()?(K.log("Mock API: getDiffUploadUrls called",{runnerId:e,sessionId:t}),{result:{upload_url:"https://s3.mock.com/mock-upload-url-result",s3_key:"mock-s3-key-result"},cumulative:{upload_url:"https://s3.mock.com/mock-upload-url-cumulative",s3_key:"mock-s3-key-cumulative"}}):ce(`/api/v1/agent_runners/${e}/sessions/${t}/diff/upload_urls`,{method:"POST"}),Ht=async(e,t)=>_e()?(K.log("Mock API: updateSessionUsage called",{runnerId:e,sessionId:t}),{id:t,runnerId:e,usage:0}):ce(`/api/v1/agent_runners/${e}/sessions/${t}/update_usage`,{method:"POST"}),lt=async(e,t)=>{K.log(`Uploading diff to S3: ${e.substring(0,50)}...`);let r=await fetch(e,{method:"PUT",body:t,headers:{"Content-Type":"text/plain"}});if(!r.ok)throw new Error(`S3 upload failed with status ${r.status}`);return r};var Ee=x("ai_gateway"),ct=null;var Be=async()=>{if(ct)return ct;Ee.log("Fetching available AI gateway providers");let e=await fetch(`${Ut().apiUrl}/api/v1/ai-gateway/providers`);if(!e.ok)throw new Error(`Failed to fetch AI gateway providers: ${e.statusText}`);let t=await e.json();return ct=t,Ee.log("Cached AI gateway providers",{providerCount:Object.keys(t.providers).length}),t},rn=async(e,t)=>{let i=(await Be()).providers[e];if(!i)return Ee.log(`Provider '${e}' not found`),!1;let n=i.models.includes(t);return Ee.log(`Model validation for ${e}/${t}`,{isAvailable:n}),n},Wt=async({config:e})=>{let t,r,i,n,o=!e.site?.published_deploy;if(!(o?e.accountId:e.siteId))throw new Error(`No entity id for ${o?"account":"site"}`);let a=async()=>{clearTimeout(i),Ee.log("Requesting AI gateway information");let l=await(o?Yt(e.accountId,e.id,e.sessionId):qt(e.siteId,e.id,e.sessionId));if({token:t,url:n}=l,r=l.expires_at?l.expires_at*1e3:void 0,Ee.log("Got AI gateway information",{token:!!t,expiresAt:r,url:n}),r){let d=r-Date.now()-6e4;d>0&&(i=setTimeout(()=>{a()},d))}};return await Promise.all([a(),Be()]),{get url(){return n},get token(){return t},isModelAvailableForProvider:rn}};import te from"process";import oe from"path";import He from"fs";import{fileURLToPath as un}from"url";import{createRequire as dn}from"module";import{execa as pn,execaCommand as lo}from"execa";import{Transform as nn}from"stream";function on(){let e=process.env.NETLIFY_SENSITIVE_ENV_KEYS;return e?e.split(",").map(t=>t.trim()).filter(Boolean):[]}function sn(e){let t=e.toLowerCase();return t==="true"||t==="false"?!0:e.trim().length<4}function an(){let t=on().map(r=>process.env[r]).filter(r=>!(!r||sn(r)));return[...new Set(t)].sort((r,i)=>i.length-r.length)}function ie(e){if(typeof e!="string")return e;let t=an();if(t.length===0)return e;let r=e;return t.forEach(i=>{let n=new RegExp(ln(i),"g");r=r.replace(n,"******")}),r}function ln(e){return e.replace(/[.*+?^${}()|[\]\\]/g,"\\$&")}var xe=class extends nn{constructor(t={}){super({...t,objectMode:!1})}_transform(t,r,i){let n=t.toString(),o=ie(n);i(null,o)}};function Kt(){if(!(process.env.NETLIFY_MASK_LOGS!=="false"))return;let t=process.stdout.write.bind(process.stdout),r=process.stderr.write.bind(process.stderr);process.stdout.write=function(i,n,o){let s=typeof i=="string"?ie(i):i;return typeof n=="function"?t(s,n):t(s,n,o)},process.stderr.write=function(i,n,o){let s=typeof i=="string"?ie(i):i;return typeof n=="function"?r(s,n):r(s,n,o)}}var Ne=null,Jt=e=>(Ne&&Ne.destroy(),Ne=new ue({totalAllowedTime:e}),Ne),Vt=()=>Ne;var ue=class{constructor({totalAllowedTime:t}){this.withStageTimer=async(t,r,i)=>{if(this.isTimeExpired())throw new Error(`${t} stage did not complete in the allowed time. Time has already expired.`);let n=this.onTimesUp(()=>{throw new Error(`${t} stage did not complete in the allowed time.`)}),o=null,s=null;i!==void 0&&(s=new Promise((a,c)=>{o=setTimeout(()=>{c(new Error(`${t} stage exceeded its maximum duration of ${i}ms`))},i)}));try{return s?await Promise.race([r(),s]):await r()}finally{n(),o&&clearTimeout(o)}};this.startTime=Date.now(),this.totalAllowedTime=t,this.globalTimeoutId=null,this.subscribers=[],this.hasTimedOut=!1,this.setupGlobalTimeout()}getElapsedTime(){return Date.now()-this.startTime}getRemainingTime(){let t=this.getElapsedTime(),r=this.totalAllowedTime-t;return Math.max(0,r)}isTimeExpired(){return this.getRemainingTime()===0||this.hasTimedOut}setupGlobalTimeout(){this.globalTimeoutId&&clearTimeout(this.globalTimeoutId),this.globalTimeoutId=setTimeout(()=>{this.notifyTimeUp()},this.totalAllowedTime)}notifyTimeUp(){this.hasTimedOut=!0;for(let t=this.subscribers.length-1;t>=0;t--)try{this.subscribers[t]()}catch(r){console.error("TimeKeeper: Error in time up callback:",r)}}onTimesUp(t){if(this.subscribers.push(t),this.hasTimedOut)try{t()}catch(r){console.error("TimeKeeper: Error in time up callback:",r)}return()=>{let r=this.subscribers.indexOf(t);r>-1&&this.subscribers.splice(r,1)}}off(t){let r=this.subscribers.indexOf(t);r>-1&&this.subscribers.splice(r,1)}clearSubscribers(){this.subscribers.length=0}getSubscriberCount(){return this.subscribers.length}destroy(){this.globalTimeoutId&&(clearTimeout(this.globalTimeoutId),this.globalTimeoutId=null),this.clearSubscribers()}static{this.timeUnits={seconds:t=>t*1e3,minutes:t=>t*60*1e3,hours:t=>t*60*60*1e3}}};var zt="netlify-agent-runner-context.md",ut="task-history",ee=".netlify",me="results.md",dt="assets";var Xt="free";var ge=1800*1e3,y={Environment:"environment",UserMessage:"user-message",AgentMessage:"agent-message",Task:"task",RunCommand:"run-command",Explore:"explore",Plan:"plan",FileRead:"file-read",FileWrite:"file-write",Notebook:"notebook",Web:"web",Todo:"todo",Reasoning:"reasoning",Skill:"skill",Memorize:"memorize",Deployment:"deployment",SiteGeneration:"site-generation"};var Zt={name:"@netlify/agent-runner-cli",type:"module",version:"1.90.3",description:"CLI tool for running Netlify agents",main:"./dist/index.js",types:"./dist/index.d.ts",exports:"./dist/index.js",bin:{"agent-runner-cli":"./dist/bin.js","agent-runner-cli-local":"./dist/bin-local.js"},files:["dist/**/*.js","dist/**/*.d.ts","dist/skills/**","patches","scripts"],scripts:{build:"tsup",dev:"tsup --watch",prepare:"husky install node_modules/@netlify/eslint-config-node/.husky/",prepublishOnly:"npm ci && npm test",prepack:"npm run build",test:"run-s build format test:dev",format:"run-s build format:check-fix:*","format:ci":"run-s build format:check:*","format:check-fix:lint":"run-e format:check:lint format:fix:lint","format:check:lint":"cross-env-shell eslint $npm_package_config_eslint","format:fix:lint":"cross-env-shell eslint --fix $npm_package_config_eslint","format:check-fix:prettier":"run-e format:check:prettier format:fix:prettier","format:check:prettier":"cross-env-shell prettier --check $npm_package_config_prettier","format:fix:prettier":"cross-env-shell prettier --write $npm_package_config_prettier","test:dev":"run-s build test:dev:*","test:ci":"run-s build test:ci:*","test:dev:vitest":"LOG=0 vitest --exclude '**/integration/**'","test:ci:vitest":"LOG=0 c8 -r lcovonly -r text -r json vitest --exclude '**/integration/**'","test:integration":"vitest run test/integration/","test:integration:codex":"vitest run test/integration/codex.test.ts","test:integration:claude":"vitest run test/integration/claude.test.ts","test:integration:gemini":"vitest run test/integration/gemini.test.ts","test:integration:create-stage":"vitest run test/integration/create.test.ts","test:integration:skill-invocation":"vitest run test/integration/skill-invocation.test.ts","check:types":"tsc --noEmit",postinstall:"node scripts/postinstall.js"},config:{eslint:'--cache --format=codeframe --max-warnings=0 "{src,scripts,test,.github}/**/*.{js,ts,md,html}"',prettier:'--ignore-path .gitignore --loglevel=warn "{src,scripts,test,.github}/**/*.{js,ts,md,yml,json,html}" "*.{js,ts,yml,json,html}" ".*.{js,ts,yml,json,html}" "!**/package-lock.json" "!package-lock.json" "!src/skills/**/*.md"'},keywords:[],license:"MIT",repository:"netlify/agent-runner-cli",bugs:{url:"https://github.com/netlify/agent-runner-cli/issues"},author:"Netlify Inc.",directories:{test:"test"},devDependencies:{"@commitlint/cli":"^20.0.0","@commitlint/config-conventional":"^20.0.0","@eslint/compat":"^2.0.0","@eslint/js":"^9.35.0","@netlify/eslint-config-node":"^7.0.1","@types/node":"^24.5.0","@typescript-eslint/eslint-plugin":"^8.0.0","@typescript-eslint/parser":"^8.0.0","@vitest/eslint-plugin":"^1.6.6",c8:"^10.0.0","eslint-config-prettier":"^10.1.8","eslint-plugin-n":"^17.0.0",husky:"^9.0.0","patch-package":"^8.0.0",tsup:"^8.5.0",typescript:"^5.0.0","typescript-eslint":"^8.44.0",vitest:"^4.0.16"},dependencies:{"@anthropic-ai/claude-code":"2.1.63","@anthropic-ai/sdk":"0.78.0","@google/gemini-cli":"0.31.0","@netlify/otel":"^5.1.2","@openai/codex":"0.110.0","@opentelemetry/exporter-trace-otlp-grpc":"0.57.2",execa:"^9.6.1",kaddidlehopper:"^0.7.2",minimist:"^1.2.8",openai:"6.26.0"}};var mn=un(import.meta.url),gn=oe.dirname(mn),fn=dn(import.meta.url),Te=x("shell"),pt=new Set,hn={preferLocal:!0},A=(e,t,r)=>{let[i,n]=yn(t,r),o={...hn,...n},s=pn(e,i,o);wn(s,o),En(s);let a=r?.idleTimeout;return a&&a>0&&_n(s,a),s};var yn=function(e,t){return Array.isArray(e)?[e,t]:typeof e=="object"&&e!==null?[[],e]:[[],void 0]},wn=(e,t)=>{if(t.stdio!==void 0||t.stdout!==void 0||t.stderr!==void 0)return;if(te.env.NETLIFY_MASK_LOGS!=="false"){e.all?.pipe(new xe).pipe(te.stdout),e.stdout?.pipe(new xe).pipe(te.stdout),e.stderr?.pipe(new xe).pipe(te.stderr);return}e.stdout?.pipe(te.stdout),e.stderr?.pipe(te.stderr)},mt=(e,t="SIGTERM")=>{try{return e.pid&&!e.killed?(te.kill(-e.pid,t),Te.log(`Killed process ${e.pid} with signal ${t}`),!0):!1}catch(r){return Te.error("Error killing process:",r),!1}},Qt=e=>mt(e,"SIGKILL"),_n=(e,t)=>{let r=null,i=()=>{Te.log(`Process ${e.pid} killed due to idle timeout (no output for ${t}ms)`),mt(e,"SIGTERM"),setTimeout(()=>{e.pid&&!e.killed&&(Te.log(`Force killing idle process ${e.pid}`),Qt(e))},5e3)},n=()=>{r&&clearTimeout(r),r=setTimeout(i,t)};n(),e.stdout?.on("data",n),e.stderr?.on("data",n);let o=()=>{r&&(clearTimeout(r),r=null)};e.on("exit",o),e.on("error",o)},En=e=>{pt.add(e);let t=Vt();if(t){let r=t.onTimesUp(()=>{Te.log(`Global timer expired, killing process ${e.pid}`),mt(e,"SIGTERM"),setTimeout(()=>{e.pid&&!e.killed&&(Te.log(`Force killing process ${e.pid} after timeout`),Qt(e))},5e3)});e.on("exit",()=>{pt.delete(e),r()}),e.on("error",()=>{pt.delete(e),r()})}};function We(e,t){return!!se(e,t)}function se(e,t){if(!te.env.NETLIFY_LOCAL_MODE)try{let n=fn.resolve(Zt.name),o=oe.dirname(n);for(;o!==oe.dirname(o);){let s=oe.dirname(o);if(oe.basename(s)==="node_modules"){let a=oe.join(s,".bin",t);if(He.existsSync(a))return a;break}o=s}}catch(n){console.error("Could not resolve package.json",n)}if(te.env.NODE_PATH){let n=oe.join(te.env.NODE_PATH,".bin",t);if(He.existsSync(n))return n}let r=oe.join(e,"node_modules",".bin",t);if(He.existsSync(r))return r;let i=oe.join(gn,"..","node_modules",".bin",t);if(He.existsSync(i))return i}var xn=x("utils"),Tn=e=>new Promise(t=>{setTimeout(t,e)}),Ke=(e,t=3e3)=>{let r=!1,i=null,n=[],o=null,s=(...a)=>{if(r)return i=a,new Promise(d=>{n.push(d)});r=!0;let c,l=new Promise(d=>{c=d});return o=(async()=>{await Promise.resolve();let d=await e(...a);for(c(d);;){if(await Tn(t),!i)return r=!1,o=null,d;let m=i,u=n;i=null,n=[],d=await e(...m),u.forEach(w=>{w(d)})}})(),l};return s.flush=async()=>{if((r||i)&&o)return await o,s.flush()},s},Ie=(e,t,r=!1)=>{let i=null,n=null,o=null,s=function(...a){n=a,o=this;let c=r&&!i;clearTimeout(i),i=setTimeout(()=>{i=null,r||(e.apply(o,n),n=null,o=null)},t),c&&(e.apply(o,n),n=null,o=null)};return s.cancel=()=>{clearTimeout(i),i=null,n=null,o=null},s.flush=()=>{if(i){clearTimeout(i);let a=n,c=o;i=null,n=null,o=null,e.apply(c,a)}},s},er=(e,t=!0,r)=>{if(e)try{return JSON.parse(e)}catch(i){t&&(r?.error?r.error("Could not parse JSON",i):xn.error("Could not parse JSON",i))}},gt=e=>e.charAt(0).toUpperCase()+e.slice(1),de=e=>e.split("-").map(t=>t.length===2?t.toUpperCase():gt(t)).join(" ");function fe(e,t){t&&e.log(`Skill invoked: ${t}`)}var tr=e=>Object.fromEntries(Object.entries(e).filter(([,t])=>t!==void 0)),rr=(e,t)=>{let n=".netlify.app",o="agent-";if(!t)return`${o}${e.slice(0,6)}`;let a=`--${t}${n}`;if(a.length>55)return"";let c=60-a.length;if(c<=0)return"";if(c>=o.length+6){let l=Math.min(c-o.length,e.length);return`${o}${e.slice(0,l)}`}return e.slice(0,c)};var In=50*1024,ft=(e,t=In)=>{if(!e||typeof e!="string"||e.length<=t)return e;let i=e.startsWith("```")?"\n... [truncated]\n```":"... [truncated]";return e.slice(0,t)+i};import{Buffer as nr}from"buffer";import Sn from"path";var ir=x("repo"),sr=async({config:e,isRetry:t,cwd:r=process.cwd()})=>{ir.info("Getting runner diffs");let i=await Rn(r),{hasChanges:n}=i,{status:o}=i;if(!n)return{hasChanges:!1};if(!t){let _=An(o);await Cn(_,r)}ir.info("Changes after processing"),await yt(r);let s=await wt(o,r);if(await ht(s,r),n=await bn(r),!n)return{hasChanges:!1,ignored:s};process.env.NETLIFY_INTERNAL_GIT="1";try{await A("git",["commit","-m","Agent runner"],{cwd:r})}finally{process.env.NETLIFY_INTERNAL_GIT="0"}let a={stdio:["ignore","pipe","pipe"],cwd:r},c=await A("git",["diff",e.runSha,"HEAD"],a),l=String(c.stdout??"");if(n=!!l,!n)return await or(r),{hasChanges:!1,ignored:s};let d=await A("git",["diff",e.runSha,"HEAD","--binary"],a),m=String(d.stdout??""),u,w;if(e.sha){let _=await A("git",["diff",e.sha,"HEAD"],a);u=String(_.stdout??"");let S=await A("git",["diff",e.sha,"HEAD","--binary"],a),h=String(S.stdout??"");u!==h&&(w=nr.from(h).toString("base64"))}await or(r);let E={hasChanges:!0,diff:l,resultDiff:u,ignored:s};return l!==m&&(E.diffBinary=nr.from(m).toString("base64")),w&&(E.resultDiffBinary=w),E},or=async(e=process.cwd())=>{process.env.NETLIFY_LOCAL_MODE&&await A("git",["reset","--soft","HEAD~1"],{cwd:e})},ht=async(e=[],t=process.cwd())=>{process.env.NETLIFY_INTERNAL_GIT="1";try{await A("git",["add",".",...e],{cwd:t})}finally{process.env.NETLIFY_INTERNAL_GIT="0"}},yt=async(e=process.cwd())=>{let t=await A("git",["status","-s"],{cwd:e});return String(t.stdout??"")},ar=/.. (.+)?\.log$/,vn=[ar],Rn=async(e=process.cwd())=>{let t=await yt(e);return{hasChanges:(t.trim().length===0?[]:t.split(`
9
9
  `).filter(n=>vn.some(s=>s instanceof RegExp?s.test(n):n===s)?!1:n[1]?.trim()!=="")).length!==0,status:t}},bn=async(e=process.cwd())=>{try{return await A("git",["diff","--staged","--quiet"],{cwd:e}),!1}catch{return!0}},$e=async(e=process.cwd())=>{let{stdout:t}=await A("git",["rev-parse","HEAD"],{cwd:e});return String(t??"").trim()},lr=async(e=process.cwd())=>{let{stdout:t}=await A("git",["rev-list","--max-parents=0","HEAD"],{cwd:e});return String(t??"").trim()},wt=async(e,t=process.cwd())=>{e||=await yt(t);let r=[".netlify","node_modules","dist",".next","out",".nuxt",".output",".cache",".turbo",".parcel-cache","coverage",".nyc_output","storybook-static","public/build","CLAUDE.local.md"],i=[];return e.split(`
10
10
  `).forEach(n=>{r.forEach(s=>{let a=n===`?? ${s}`,c=n.startsWith(`?? ${s}/`)||n.startsWith(`?? ${s}${Sn.sep}`);(a||c)&&i.push(`:!${s}`)});let o=n.match(ar)?.[1];o&&i.push(`:!${o}.log`)}),i},_t=async(e=process.cwd())=>{await A("git",["reset","--hard","HEAD"],{cwd:e})},An=e=>{let t=e.split(`
11
11
  `).reduce((r,i)=>{if(!i)return r;let[n,o,,...s]=i,a=s.join(""),c=n.trim(),l=o.trim();return r[a]?r[a].change=l:r[a]={filePath:a,stage:c,change:l},r},{});return Object.values(t)},Cn=async(e,t=process.cwd())=>{let r=e.filter(i=>i.stage&&!i.change).map(i=>i.filePath);r.length!==0&&await A("git",["restore","--staged","--worktree","--pathspec-from-file=-"],{cwd:t,input:r.join(`
package/dist/bin.js CHANGED
@@ -5,7 +5,7 @@ import Ft from"process";import qo from"minimist";import{createRequire as No}from
5
5
  ${s}
6
6
  </extracted_error_chunk>`).join(`
7
7
 
8
- `);return i.length>e.length*.8?e:i}import{execSync as eo}from"child_process";import Nr from"fs/promises";import to from"path";import fe from"process";import{getTracer as ro}from"@netlify/otel";import be from"process";var X=class extends Error{constructor(r,o,n,i=!1){super(r);this.statusCode=o;this.userMessage=n;this.isCreditLimitExceeded=i;this.name="GracefulShutdownError"}},Ge=e=>e instanceof X;var je=be.env.NETLIFY_API_URL,Ye=be.env.NETLIFY_API_TOKEN,W=x("api"),ye=()=>be.env.NETLIFY_LOCAL_MODE==="true",se=async(e,t={})=>{if(!je||!Ye)throw new Error("No API URL or token");let r=new URL(e,je),o={...t,headers:{...t.headers,Authorization:`Bearer ${Ye}`}};be.env.AGENT_RUNNERS_DEBUG==="true"&&(o.headers["x-nf-debug-logging"]="true"),t.json&&(o.headers||={},o.headers["Content-Type"]="application/json",o.body=JSON.stringify(t.json));let n=await fetch(r,o),i=n.ok&&n.status<=299;if(be.env.AGENT_RUNNERS_DEBUG==="true")W.log(`Response headers for ${r}:`),n.headers.forEach((a,c)=>{W.log(` ${c}: ${a}`)});else{let a=n.headers.get("x-request-id")||n.headers.get("x-nf-request-id");W.log(`Request ID for ${r}: ${a||"N/A"}`)}if(i||W.error(`Got status ${n.status} for request ${r}`),t.raw){if(!i)throw new Error(`API request failed: ${n.status} ${n.statusText}`);return n}let s=await(n.headers.get("content-type")?.includes("application/json")?n.json():n.text());if(!i){let a=typeof s=="string"?s:JSON.stringify(s);throw n.status===404?new X(`API request failed: 404 - ${a}`,404,"The site associated with this agent run no longer exists."):n.status===503&&t.gracefulOn503&&a.toLowerCase().includes("usage exceeded")?new X(`API request failed: 503 - ${a}`,503,"Credit limit reached. Please add more credits to continue using Agent Runners.",!0):new Error(`API request failed: ${n.status} - ${a}`)}return s},jt=e=>{W.log("Setting details for api",{apiUrl:e?.constants?.NETLIFY_API_HOST,token:!!e?.constants?.NETLIFY_API_TOKEN}),e?.constants?.NETLIFY_API_HOST&&(je=`https://${e.constants.NETLIFY_API_HOST}`),e?.constants?.NETLIFY_API_TOKEN&&(Ye=e.constants.NETLIFY_API_TOKEN)},Yt=()=>({apiUrl:je,token:Ye}),Pe=async(e,t)=>ye()?(W.log("Mock API: updateRunner called",{runnerId:e,data:t}),{id:e,...t}):se(`/api/v1/agent_runners/${e}`,{method:"PUT",json:t}),K=async(e,t,r)=>ye()?(W.log("Mock API: updateRunnerSession called",JSON.stringify({runnerId:e,sessionId:t,data:r},null,2)),{id:e,sessionId:t,...r}):se(`/api/v1/agent_runners/${e}/sessions/${t}`,{method:"PUT",json:r});var qt=async e=>ye()?(W.log("Mock API: getSite called",{siteId:e}),{id:e,published_deploy:{id:"id"}}):se(`/api/v1/sites/${e}`),Bt=async(e,t)=>ye()?(W.log("Mock API: getRunnerSession called",{runnerId:e,sessionId:t}),{id:t,runnerId:e,state:"running"}):se(`/api/v1/agent_runners/${e}/sessions/${t}`),Ht=(e,t,r)=>se(`/api/v1/accounts/${e}/ai-gateway/token`,{headers:{"X-Nf-Agent-Runner-Id":t,"X-Nf-Agent-Runner-Session-Id":r},gracefulOn503:!0}),Wt=(e,t,r)=>se(`/api/v1/sites/${e}/ai-gateway/token`,{headers:{"X-Nf-Agent-Runner-Id":t,"X-Nf-Agent-Runner-Session-Id":r},gracefulOn503:!0}),Kt=async(e,t)=>ye()?(W.log("Mock API: getDiffUploadUrls called",{runnerId:e,sessionId:t}),{result:{upload_url:"https://s3.mock.com/mock-upload-url-result",s3_key:"mock-s3-key-result"},cumulative:{upload_url:"https://s3.mock.com/mock-upload-url-cumulative",s3_key:"mock-s3-key-cumulative"}}):se(`/api/v1/agent_runners/${e}/sessions/${t}/diff/upload_urls`,{method:"POST"}),Vt=async(e,t)=>ye()?(W.log("Mock API: updateSessionUsage called",{runnerId:e,sessionId:t}),{id:t,runnerId:e,usage:0}):se(`/api/v1/agent_runners/${e}/sessions/${t}/update_usage`,{method:"POST"}),at=async(e,t)=>{W.log(`Uploading diff to S3: ${e.substring(0,50)}...`);let r=await fetch(e,{method:"PUT",body:t,headers:{"Content-Type":"text/plain"}});if(!r.ok)throw new Error(`S3 upload failed with status ${r.status}`);return r};var _e=x("ai_gateway"),lt=null;var qe=async()=>{if(lt)return lt;_e.log("Fetching available AI gateway providers");let e=await fetch(`${Yt().apiUrl}/api/v1/ai-gateway/providers`);if(!e.ok)throw new Error(`Failed to fetch AI gateway providers: ${e.statusText}`);let t=await e.json();return lt=t,_e.log("Cached AI gateway providers",{providerCount:Object.keys(t.providers).length}),t},ln=async(e,t)=>{let o=(await qe()).providers[e];if(!o)return _e.log(`Provider '${e}' not found`),!1;let n=o.models.includes(t);return _e.log(`Model validation for ${e}/${t}`,{isAvailable:n}),n},Jt=async({config:e})=>{let t,r,o,n,i=!e.site?.published_deploy;if(!(i?e.accountId:e.siteId))throw new Error(`No entity id for ${i?"account":"site"}`);let a=async()=>{clearTimeout(o),_e.log("Requesting AI gateway information");let l=await(i?Ht(e.accountId,e.id,e.sessionId):Wt(e.siteId,e.id,e.sessionId));if({token:t,url:n}=l,r=l.expires_at?l.expires_at*1e3:void 0,_e.log("Got AI gateway information",{token:!!t,expiresAt:r,url:n}),r){let d=r-Date.now()-6e4;d>0&&(o=setTimeout(()=>{a()},d))}};return await Promise.all([a(),qe()]),{get url(){return n},get token(){return t},isModelAvailableForProvider:ln}};import Q from"process";import ne from"path";import Be from"fs";import{fileURLToPath as fn}from"url";import{createRequire as hn}from"module";import{execa as yn,execaCommand as xi}from"execa";import{Transform as cn}from"stream";function un(){let e=process.env.NETLIFY_SENSITIVE_ENV_KEYS;return e?e.split(",").map(t=>t.trim()).filter(Boolean):[]}function dn(e){let t=e.toLowerCase();return t==="true"||t==="false"?!0:e.trim().length<4}function pn(){let t=un().map(r=>process.env[r]).filter(r=>!(!r||dn(r)));return[...new Set(t)].sort((r,o)=>o.length-r.length)}function re(e){if(typeof e!="string")return e;let t=pn();if(t.length===0)return e;let r=e;return t.forEach(o=>{let n=new RegExp(mn(o),"g");r=r.replace(n,"******")}),r}function mn(e){return e.replace(/[.*+?^${}()|[\]\\]/g,"\\$&")}var Ee=class extends cn{constructor(t={}){super({...t,objectMode:!1})}_transform(t,r,o){let n=t.toString(),i=re(n);o(null,i)}};function zt(){if(!(process.env.NETLIFY_MASK_LOGS!=="false"))return;let t=process.stdout.write.bind(process.stdout),r=process.stderr.write.bind(process.stderr);process.stdout.write=function(o,n,i){let s=typeof o=="string"?re(o):o;return typeof n=="function"?t(s,n):t(s,n,i)},process.stderr.write=function(o,n,i){let s=typeof o=="string"?re(o):o;return typeof n=="function"?r(s,n):r(s,n,i)}}var ke=null,Xt=e=>(ke&&ke.destroy(),ke=new ae({totalAllowedTime:e}),ke),Zt=()=>ke;var ae=class{constructor({totalAllowedTime:t}){this.withStageTimer=async(t,r,o)=>{if(this.isTimeExpired())throw new Error(`${t} stage did not complete in the allowed time. Time has already expired.`);let n=this.onTimesUp(()=>{throw new Error(`${t} stage did not complete in the allowed time.`)}),i=null,s=null;o!==void 0&&(s=new Promise((a,c)=>{i=setTimeout(()=>{c(new Error(`${t} stage exceeded its maximum duration of ${o}ms`))},o)}));try{return s?await Promise.race([r(),s]):await r()}finally{n(),i&&clearTimeout(i)}};this.startTime=Date.now(),this.totalAllowedTime=t,this.globalTimeoutId=null,this.subscribers=[],this.hasTimedOut=!1,this.setupGlobalTimeout()}getElapsedTime(){return Date.now()-this.startTime}getRemainingTime(){let t=this.getElapsedTime(),r=this.totalAllowedTime-t;return Math.max(0,r)}isTimeExpired(){return this.getRemainingTime()===0||this.hasTimedOut}setupGlobalTimeout(){this.globalTimeoutId&&clearTimeout(this.globalTimeoutId),this.globalTimeoutId=setTimeout(()=>{this.notifyTimeUp()},this.totalAllowedTime)}notifyTimeUp(){this.hasTimedOut=!0;for(let t=this.subscribers.length-1;t>=0;t--)try{this.subscribers[t]()}catch(r){console.error("TimeKeeper: Error in time up callback:",r)}}onTimesUp(t){if(this.subscribers.push(t),this.hasTimedOut)try{t()}catch(r){console.error("TimeKeeper: Error in time up callback:",r)}return()=>{let r=this.subscribers.indexOf(t);r>-1&&this.subscribers.splice(r,1)}}off(t){let r=this.subscribers.indexOf(t);r>-1&&this.subscribers.splice(r,1)}clearSubscribers(){this.subscribers.length=0}getSubscriberCount(){return this.subscribers.length}destroy(){this.globalTimeoutId&&(clearTimeout(this.globalTimeoutId),this.globalTimeoutId=null),this.clearSubscribers()}static{this.timeUnits={seconds:t=>t*1e3,minutes:t=>t*60*1e3,hours:t=>t*60*60*1e3}}};var Qt="netlify-agent-runner-context.md",ct="task-history",Z=".netlify",de="results.md",ut="assets",dt="other",pt="personal";var mt="enterprise",Ne="free",er=[pt,"pro",mt,Ne],tr=["normal","redeploy","create","ask","dtn-prod-iteration","rebase"],rr="The production deploy has changed since you started working. Please reapply the changes to the current codebase, resolving any conflicts that arise. Use the attached diff file if present, otherwise review the previous session context to reproduce the changes.",pe=1800*1e3,_={Environment:"environment",UserMessage:"user-message",AgentMessage:"agent-message",Task:"task",RunCommand:"run-command",Explore:"explore",Plan:"plan",FileRead:"file-read",FileWrite:"file-write",Notebook:"notebook",Web:"web",Todo:"todo",Reasoning:"reasoning",Skill:"skill",Memorize:"memorize",Deployment:"deployment",SiteGeneration:"site-generation"};var nr={name:"@netlify/agent-runner-cli",type:"module",version:"1.90.2",description:"CLI tool for running Netlify agents",main:"./dist/index.js",types:"./dist/index.d.ts",exports:"./dist/index.js",bin:{"agent-runner-cli":"./dist/bin.js","agent-runner-cli-local":"./dist/bin-local.js"},files:["dist/**/*.js","dist/**/*.d.ts","dist/skills/**","patches","scripts"],scripts:{build:"tsup",dev:"tsup --watch",prepare:"husky install node_modules/@netlify/eslint-config-node/.husky/",prepublishOnly:"npm ci && npm test",prepack:"npm run build",test:"run-s build format test:dev",format:"run-s build format:check-fix:*","format:ci":"run-s build format:check:*","format:check-fix:lint":"run-e format:check:lint format:fix:lint","format:check:lint":"cross-env-shell eslint $npm_package_config_eslint","format:fix:lint":"cross-env-shell eslint --fix $npm_package_config_eslint","format:check-fix:prettier":"run-e format:check:prettier format:fix:prettier","format:check:prettier":"cross-env-shell prettier --check $npm_package_config_prettier","format:fix:prettier":"cross-env-shell prettier --write $npm_package_config_prettier","test:dev":"run-s build test:dev:*","test:ci":"run-s build test:ci:*","test:dev:vitest":"LOG=0 vitest --exclude '**/integration/**'","test:ci:vitest":"LOG=0 c8 -r lcovonly -r text -r json vitest --exclude '**/integration/**'","test:integration":"vitest run test/integration/","test:integration:codex":"vitest run test/integration/codex.test.ts","test:integration:claude":"vitest run test/integration/claude.test.ts","test:integration:gemini":"vitest run test/integration/gemini.test.ts","test:integration:create-stage":"vitest run test/integration/create.test.ts","test:integration:skill-invocation":"vitest run test/integration/skill-invocation.test.ts","check:types":"tsc --noEmit",postinstall:"node scripts/postinstall.js"},config:{eslint:'--cache --format=codeframe --max-warnings=0 "{src,scripts,test,.github}/**/*.{js,ts,md,html}"',prettier:'--ignore-path .gitignore --loglevel=warn "{src,scripts,test,.github}/**/*.{js,ts,md,yml,json,html}" "*.{js,ts,yml,json,html}" ".*.{js,ts,yml,json,html}" "!**/package-lock.json" "!package-lock.json" "!src/skills/**/*.md"'},keywords:[],license:"MIT",repository:"netlify/agent-runner-cli",bugs:{url:"https://github.com/netlify/agent-runner-cli/issues"},author:"Netlify Inc.",directories:{test:"test"},devDependencies:{"@commitlint/cli":"^20.0.0","@commitlint/config-conventional":"^20.0.0","@eslint/compat":"^2.0.0","@eslint/js":"^9.35.0","@netlify/eslint-config-node":"^7.0.1","@types/node":"^24.5.0","@typescript-eslint/eslint-plugin":"^8.0.0","@typescript-eslint/parser":"^8.0.0","@vitest/eslint-plugin":"^1.6.6",c8:"^10.0.0","eslint-config-prettier":"^10.1.8","eslint-plugin-n":"^17.0.0",husky:"^9.0.0","patch-package":"^8.0.0",tsup:"^8.5.0",typescript:"^5.0.0","typescript-eslint":"^8.44.0",vitest:"^4.0.16"},dependencies:{"@anthropic-ai/claude-code":"2.1.63","@anthropic-ai/sdk":"0.78.0","@google/gemini-cli":"0.31.0","@netlify/otel":"^5.1.2","@openai/codex":"0.110.0","@opentelemetry/exporter-trace-otlp-grpc":"0.57.2",execa:"^9.6.1",kaddidlehopper:"^0.7.2",minimist:"^1.2.8",openai:"6.26.0"}};var _n=fn(import.meta.url),En=ne.dirname(_n),wn=hn(import.meta.url),we=x("shell"),gt=new Set,xn={preferLocal:!0},N=(e,t,r)=>{let[o,n]=Tn(t,r),i={...xn,...n},s=yn(e,o,i);In(s,i),Sn(s);let a=r?.idleTimeout;return a&&a>0&&vn(s,a),s};var Tn=function(e,t){return Array.isArray(e)?[e,t]:typeof e=="object"&&e!==null?[[],e]:[[],void 0]},In=(e,t)=>{if(t.stdio!==void 0||t.stdout!==void 0||t.stderr!==void 0)return;if(Q.env.NETLIFY_MASK_LOGS!=="false"){e.all?.pipe(new Ee).pipe(Q.stdout),e.stdout?.pipe(new Ee).pipe(Q.stdout),e.stderr?.pipe(new Ee).pipe(Q.stderr);return}e.stdout?.pipe(Q.stdout),e.stderr?.pipe(Q.stderr)},ft=(e,t="SIGTERM")=>{try{return e.pid&&!e.killed?(Q.kill(-e.pid,t),we.log(`Killed process ${e.pid} with signal ${t}`),!0):!1}catch(r){return we.error("Error killing process:",r),!1}},or=e=>ft(e,"SIGKILL"),vn=(e,t)=>{let r=null,o=()=>{we.log(`Process ${e.pid} killed due to idle timeout (no output for ${t}ms)`),ft(e,"SIGTERM"),setTimeout(()=>{e.pid&&!e.killed&&(we.log(`Force killing idle process ${e.pid}`),or(e))},5e3)},n=()=>{r&&clearTimeout(r),r=setTimeout(o,t)};n(),e.stdout?.on("data",n),e.stderr?.on("data",n);let i=()=>{r&&(clearTimeout(r),r=null)};e.on("exit",i),e.on("error",i)},Sn=e=>{gt.add(e);let t=Zt();if(t){let r=t.onTimesUp(()=>{we.log(`Global timer expired, killing process ${e.pid}`),ft(e,"SIGTERM"),setTimeout(()=>{e.pid&&!e.killed&&(we.log(`Force killing process ${e.pid} after timeout`),or(e))},5e3)});e.on("exit",()=>{gt.delete(e),r()}),e.on("error",()=>{gt.delete(e),r()})}};function le(e,t){if(!Q.env.NETLIFY_LOCAL_MODE)try{let n=wn.resolve(nr.name),i=ne.dirname(n);for(;i!==ne.dirname(i);){let s=ne.dirname(i);if(ne.basename(s)==="node_modules"){let a=ne.join(s,".bin",t);if(Be.existsSync(a))return a;break}i=s}}catch(n){console.error("Could not resolve package.json",n)}if(Q.env.NODE_PATH){let n=ne.join(Q.env.NODE_PATH,".bin",t);if(Be.existsSync(n))return n}let r=ne.join(e,"node_modules",".bin",t);if(Be.existsSync(r))return r;let o=ne.join(En,"..","node_modules",".bin",t);if(Be.existsSync(o))return o}var ir=x("utils"),Rn=e=>new Promise(t=>{setTimeout(t,e)}),He=(e,t=3e3)=>{let r=!1,o=null,n=[],i=null,s=(...a)=>{if(r)return o=a,new Promise(d=>{n.push(d)});r=!0;let c,l=new Promise(d=>{c=d});return i=(async()=>{await Promise.resolve();let d=await e(...a);for(c(d);;){if(await Rn(t),!o)return r=!1,i=null,d;let p=o,u=n;o=null,n=[],d=await e(...p),u.forEach(y=>{y(d)})}})(),l};return s.flush=async()=>{if((r||o)&&i)return await i,s.flush()},s},xe=(e,t,r=!1)=>{let o=null,n=null,i=null,s=function(...a){n=a,i=this;let c=r&&!o;clearTimeout(o),o=setTimeout(()=>{o=null,r||(e.apply(i,n),n=null,i=null)},t),c&&(e.apply(i,n),n=null,i=null)};return s.cancel=()=>{clearTimeout(o),o=null,n=null,i=null},s.flush=()=>{if(o){clearTimeout(o);let a=n,c=i;o=null,n=null,i=null,e.apply(c,a)}},s},We=(e,t=!0,r)=>{if(e)try{return JSON.parse(e)}catch(o){t&&(r?.error?r.error("Could not parse JSON",o):ir.error("Could not parse JSON",o))}},ht=e=>e.charAt(0).toUpperCase()+e.slice(1),ce=e=>e.split("-").map(t=>t.length===2?t.toUpperCase():ht(t)).join(" ");function me(e,t){t&&e.log(`Skill invoked: ${t}`)}var sr=e=>Object.fromEntries(Object.entries(e).filter(([,t])=>t!==void 0)),ar=(e,t)=>{let n=".netlify.app",i="agent-";if(!t)return`${i}${e.slice(0,6)}`;let a=`--${t}${n}`;if(a.length>55)return"";let c=60-a.length;if(c<=0)return"";if(c>=i.length+6){let l=Math.min(c-i.length,e.length);return`${i}${e.slice(0,l)}`}return e.slice(0,c)},An=e=>!e||typeof e!="object"||Array.isArray(e)||Object.keys(e).length===0?!1:!!er.some(t=>t in e),lr=()=>{let e={},t={codex:process.env.NETLIFY_FF_AGENT_RUNNER_CODEX_VERSION,claude:process.env.NETLIFY_FF_AGENT_RUNNER_CLAUDE_VERSION,gemini:process.env.NETLIFY_FF_AGENT_RUNNER_GEMINI_VERSION};return Object.entries(t).forEach(([r,o])=>{if(o){let n=`NETLIFY_FF_AGENT_RUNNER_${r.toUpperCase()}_VERSION`;try{let i=JSON.parse(o);An(i)&&(e[r]=i)}catch(i){let a=i instanceof SyntaxError?"Invalid JSON":i.message;ir.error(`Could not parse ${r} model version override from ${n}: ${a}`)}}}),e},Cn=50*1024,yt=(e,t=Cn)=>{if(!e||typeof e!="string"||e.length<=t)return e;let o=e.startsWith("```")?"\n... [truncated]\n```":"... [truncated]";return e.slice(0,t)+o};import{Buffer as cr}from"buffer";import bn from"path";var ur=x("repo"),pr=async({config:e,isRetry:t,cwd:r=process.cwd()})=>{ur.info("Getting runner diffs");let o=await kn(r),{hasChanges:n}=o,{status:i}=o;if(!n)return{hasChanges:!1};if(!t){let E=On(i);await $n(E,r)}ur.info("Changes after processing"),await Et(r);let s=await xt(i,r);if(await _t(s,r),n=await Nn(r),!n)return{hasChanges:!1,ignored:s};process.env.NETLIFY_INTERNAL_GIT="1";try{await N("git",["commit","-m","Agent runner"],{cwd:r})}finally{process.env.NETLIFY_INTERNAL_GIT="0"}let a={stdio:["ignore","pipe","pipe"],cwd:r},c=await N("git",["diff",e.runSha,"HEAD"],a),l=String(c.stdout??"");if(n=!!l,!n)return await dr(r),{hasChanges:!1,ignored:s};let d=await N("git",["diff",e.runSha,"HEAD","--binary"],a),p=String(d.stdout??""),u,y;if(e.sha){let E=await N("git",["diff",e.sha,"HEAD"],a);u=String(E.stdout??"");let I=await N("git",["diff",e.sha,"HEAD","--binary"],a),f=String(I.stdout??"");u!==f&&(y=cr.from(f).toString("base64"))}await dr(r);let w={hasChanges:!0,diff:l,resultDiff:u,ignored:s};return l!==p&&(w.diffBinary=cr.from(p).toString("base64")),y&&(w.resultDiffBinary=y),w},dr=async(e=process.cwd())=>{process.env.NETLIFY_LOCAL_MODE&&await N("git",["reset","--soft","HEAD~1"],{cwd:e})},_t=async(e=[],t=process.cwd())=>{process.env.NETLIFY_INTERNAL_GIT="1";try{await N("git",["add",".",...e],{cwd:t})}finally{process.env.NETLIFY_INTERNAL_GIT="0"}},Et=async(e=process.cwd())=>{let t=await N("git",["status","-s"],{cwd:e});return String(t.stdout??"")},mr=/.. (.+)?\.log$/,Pn=[mr],kn=async(e=process.cwd())=>{let t=await Et(e);return{hasChanges:(t.trim().length===0?[]:t.split(`
8
+ `);return i.length>e.length*.8?e:i}import{execSync as eo}from"child_process";import Nr from"fs/promises";import to from"path";import fe from"process";import{getTracer as ro}from"@netlify/otel";import be from"process";var X=class extends Error{constructor(r,o,n,i=!1){super(r);this.statusCode=o;this.userMessage=n;this.isCreditLimitExceeded=i;this.name="GracefulShutdownError"}},Ge=e=>e instanceof X;var je=be.env.NETLIFY_API_URL,Ye=be.env.NETLIFY_API_TOKEN,W=x("api"),ye=()=>be.env.NETLIFY_LOCAL_MODE==="true",se=async(e,t={})=>{if(!je||!Ye)throw new Error("No API URL or token");let r=new URL(e,je),o={...t,headers:{...t.headers,Authorization:`Bearer ${Ye}`}};be.env.AGENT_RUNNERS_DEBUG==="true"&&(o.headers["x-nf-debug-logging"]="true"),t.json&&(o.headers||={},o.headers["Content-Type"]="application/json",o.body=JSON.stringify(t.json));let n=await fetch(r,o),i=n.ok&&n.status<=299;if(be.env.AGENT_RUNNERS_DEBUG==="true")W.log(`Response headers for ${r}:`),n.headers.forEach((a,c)=>{W.log(` ${c}: ${a}`)});else{let a=n.headers.get("x-request-id")||n.headers.get("x-nf-request-id");W.log(`Request ID for ${r}: ${a||"N/A"}`)}if(i||W.error(`Got status ${n.status} for request ${r}`),t.raw){if(!i)throw new Error(`API request failed: ${n.status} ${n.statusText}`);return n}let s=await(n.headers.get("content-type")?.includes("application/json")?n.json():n.text());if(!i){let a=typeof s=="string"?s:JSON.stringify(s);throw n.status===404?new X(`API request failed: 404 - ${a}`,404,"The site associated with this agent run no longer exists."):n.status===503&&t.gracefulOn503&&a.toLowerCase().includes("usage exceeded")?new X(`API request failed: 503 - ${a}`,503,"Credit limit reached. Please add more credits to continue using Agent Runners.",!0):new Error(`API request failed: ${n.status} - ${a}`)}return s},jt=e=>{W.log("Setting details for api",{apiUrl:e?.constants?.NETLIFY_API_HOST,token:!!e?.constants?.NETLIFY_API_TOKEN}),e?.constants?.NETLIFY_API_HOST&&(je=`https://${e.constants.NETLIFY_API_HOST}`),e?.constants?.NETLIFY_API_TOKEN&&(Ye=e.constants.NETLIFY_API_TOKEN)},Yt=()=>({apiUrl:je,token:Ye}),Pe=async(e,t)=>ye()?(W.log("Mock API: updateRunner called",{runnerId:e,data:t}),{id:e,...t}):se(`/api/v1/agent_runners/${e}`,{method:"PUT",json:t}),K=async(e,t,r)=>ye()?(W.log("Mock API: updateRunnerSession called",JSON.stringify({runnerId:e,sessionId:t,data:r},null,2)),{id:e,sessionId:t,...r}):se(`/api/v1/agent_runners/${e}/sessions/${t}`,{method:"PUT",json:r});var qt=async e=>ye()?(W.log("Mock API: getSite called",{siteId:e}),{id:e,published_deploy:{id:"id"}}):se(`/api/v1/sites/${e}`),Bt=async(e,t)=>ye()?(W.log("Mock API: getRunnerSession called",{runnerId:e,sessionId:t}),{id:t,runnerId:e,state:"running"}):se(`/api/v1/agent_runners/${e}/sessions/${t}`),Ht=(e,t,r)=>se(`/api/v1/accounts/${e}/ai-gateway/token`,{headers:{"X-Nf-Agent-Runner-Id":t,"X-Nf-Agent-Runner-Session-Id":r},gracefulOn503:!0}),Wt=(e,t,r)=>se(`/api/v1/sites/${e}/ai-gateway/token`,{headers:{"X-Nf-Agent-Runner-Id":t,"X-Nf-Agent-Runner-Session-Id":r},gracefulOn503:!0}),Kt=async(e,t)=>ye()?(W.log("Mock API: getDiffUploadUrls called",{runnerId:e,sessionId:t}),{result:{upload_url:"https://s3.mock.com/mock-upload-url-result",s3_key:"mock-s3-key-result"},cumulative:{upload_url:"https://s3.mock.com/mock-upload-url-cumulative",s3_key:"mock-s3-key-cumulative"}}):se(`/api/v1/agent_runners/${e}/sessions/${t}/diff/upload_urls`,{method:"POST"}),Vt=async(e,t)=>ye()?(W.log("Mock API: updateSessionUsage called",{runnerId:e,sessionId:t}),{id:t,runnerId:e,usage:0}):se(`/api/v1/agent_runners/${e}/sessions/${t}/update_usage`,{method:"POST"}),at=async(e,t)=>{W.log(`Uploading diff to S3: ${e.substring(0,50)}...`);let r=await fetch(e,{method:"PUT",body:t,headers:{"Content-Type":"text/plain"}});if(!r.ok)throw new Error(`S3 upload failed with status ${r.status}`);return r};var _e=x("ai_gateway"),lt=null;var qe=async()=>{if(lt)return lt;_e.log("Fetching available AI gateway providers");let e=await fetch(`${Yt().apiUrl}/api/v1/ai-gateway/providers`);if(!e.ok)throw new Error(`Failed to fetch AI gateway providers: ${e.statusText}`);let t=await e.json();return lt=t,_e.log("Cached AI gateway providers",{providerCount:Object.keys(t.providers).length}),t},ln=async(e,t)=>{let o=(await qe()).providers[e];if(!o)return _e.log(`Provider '${e}' not found`),!1;let n=o.models.includes(t);return _e.log(`Model validation for ${e}/${t}`,{isAvailable:n}),n},Jt=async({config:e})=>{let t,r,o,n,i=!e.site?.published_deploy;if(!(i?e.accountId:e.siteId))throw new Error(`No entity id for ${i?"account":"site"}`);let a=async()=>{clearTimeout(o),_e.log("Requesting AI gateway information");let l=await(i?Ht(e.accountId,e.id,e.sessionId):Wt(e.siteId,e.id,e.sessionId));if({token:t,url:n}=l,r=l.expires_at?l.expires_at*1e3:void 0,_e.log("Got AI gateway information",{token:!!t,expiresAt:r,url:n}),r){let d=r-Date.now()-6e4;d>0&&(o=setTimeout(()=>{a()},d))}};return await Promise.all([a(),qe()]),{get url(){return n},get token(){return t},isModelAvailableForProvider:ln}};import Q from"process";import ne from"path";import Be from"fs";import{fileURLToPath as fn}from"url";import{createRequire as hn}from"module";import{execa as yn,execaCommand as xi}from"execa";import{Transform as cn}from"stream";function un(){let e=process.env.NETLIFY_SENSITIVE_ENV_KEYS;return e?e.split(",").map(t=>t.trim()).filter(Boolean):[]}function dn(e){let t=e.toLowerCase();return t==="true"||t==="false"?!0:e.trim().length<4}function pn(){let t=un().map(r=>process.env[r]).filter(r=>!(!r||dn(r)));return[...new Set(t)].sort((r,o)=>o.length-r.length)}function re(e){if(typeof e!="string")return e;let t=pn();if(t.length===0)return e;let r=e;return t.forEach(o=>{let n=new RegExp(mn(o),"g");r=r.replace(n,"******")}),r}function mn(e){return e.replace(/[.*+?^${}()|[\]\\]/g,"\\$&")}var Ee=class extends cn{constructor(t={}){super({...t,objectMode:!1})}_transform(t,r,o){let n=t.toString(),i=re(n);o(null,i)}};function zt(){if(!(process.env.NETLIFY_MASK_LOGS!=="false"))return;let t=process.stdout.write.bind(process.stdout),r=process.stderr.write.bind(process.stderr);process.stdout.write=function(o,n,i){let s=typeof o=="string"?re(o):o;return typeof n=="function"?t(s,n):t(s,n,i)},process.stderr.write=function(o,n,i){let s=typeof o=="string"?re(o):o;return typeof n=="function"?r(s,n):r(s,n,i)}}var ke=null,Xt=e=>(ke&&ke.destroy(),ke=new ae({totalAllowedTime:e}),ke),Zt=()=>ke;var ae=class{constructor({totalAllowedTime:t}){this.withStageTimer=async(t,r,o)=>{if(this.isTimeExpired())throw new Error(`${t} stage did not complete in the allowed time. Time has already expired.`);let n=this.onTimesUp(()=>{throw new Error(`${t} stage did not complete in the allowed time.`)}),i=null,s=null;o!==void 0&&(s=new Promise((a,c)=>{i=setTimeout(()=>{c(new Error(`${t} stage exceeded its maximum duration of ${o}ms`))},o)}));try{return s?await Promise.race([r(),s]):await r()}finally{n(),i&&clearTimeout(i)}};this.startTime=Date.now(),this.totalAllowedTime=t,this.globalTimeoutId=null,this.subscribers=[],this.hasTimedOut=!1,this.setupGlobalTimeout()}getElapsedTime(){return Date.now()-this.startTime}getRemainingTime(){let t=this.getElapsedTime(),r=this.totalAllowedTime-t;return Math.max(0,r)}isTimeExpired(){return this.getRemainingTime()===0||this.hasTimedOut}setupGlobalTimeout(){this.globalTimeoutId&&clearTimeout(this.globalTimeoutId),this.globalTimeoutId=setTimeout(()=>{this.notifyTimeUp()},this.totalAllowedTime)}notifyTimeUp(){this.hasTimedOut=!0;for(let t=this.subscribers.length-1;t>=0;t--)try{this.subscribers[t]()}catch(r){console.error("TimeKeeper: Error in time up callback:",r)}}onTimesUp(t){if(this.subscribers.push(t),this.hasTimedOut)try{t()}catch(r){console.error("TimeKeeper: Error in time up callback:",r)}return()=>{let r=this.subscribers.indexOf(t);r>-1&&this.subscribers.splice(r,1)}}off(t){let r=this.subscribers.indexOf(t);r>-1&&this.subscribers.splice(r,1)}clearSubscribers(){this.subscribers.length=0}getSubscriberCount(){return this.subscribers.length}destroy(){this.globalTimeoutId&&(clearTimeout(this.globalTimeoutId),this.globalTimeoutId=null),this.clearSubscribers()}static{this.timeUnits={seconds:t=>t*1e3,minutes:t=>t*60*1e3,hours:t=>t*60*60*1e3}}};var Qt="netlify-agent-runner-context.md",ct="task-history",Z=".netlify",de="results.md",ut="assets",dt="other",pt="personal";var mt="enterprise",Ne="free",er=[pt,"pro",mt,Ne],tr=["normal","redeploy","create","ask","dtn-prod-iteration","rebase"],rr="The production deploy has changed since you started working. Please reapply the changes to the current codebase, resolving any conflicts that arise. Use the attached diff file if present, otherwise review the previous session context to reproduce the changes.",pe=1800*1e3,_={Environment:"environment",UserMessage:"user-message",AgentMessage:"agent-message",Task:"task",RunCommand:"run-command",Explore:"explore",Plan:"plan",FileRead:"file-read",FileWrite:"file-write",Notebook:"notebook",Web:"web",Todo:"todo",Reasoning:"reasoning",Skill:"skill",Memorize:"memorize",Deployment:"deployment",SiteGeneration:"site-generation"};var nr={name:"@netlify/agent-runner-cli",type:"module",version:"1.90.3",description:"CLI tool for running Netlify agents",main:"./dist/index.js",types:"./dist/index.d.ts",exports:"./dist/index.js",bin:{"agent-runner-cli":"./dist/bin.js","agent-runner-cli-local":"./dist/bin-local.js"},files:["dist/**/*.js","dist/**/*.d.ts","dist/skills/**","patches","scripts"],scripts:{build:"tsup",dev:"tsup --watch",prepare:"husky install node_modules/@netlify/eslint-config-node/.husky/",prepublishOnly:"npm ci && npm test",prepack:"npm run build",test:"run-s build format test:dev",format:"run-s build format:check-fix:*","format:ci":"run-s build format:check:*","format:check-fix:lint":"run-e format:check:lint format:fix:lint","format:check:lint":"cross-env-shell eslint $npm_package_config_eslint","format:fix:lint":"cross-env-shell eslint --fix $npm_package_config_eslint","format:check-fix:prettier":"run-e format:check:prettier format:fix:prettier","format:check:prettier":"cross-env-shell prettier --check $npm_package_config_prettier","format:fix:prettier":"cross-env-shell prettier --write $npm_package_config_prettier","test:dev":"run-s build test:dev:*","test:ci":"run-s build test:ci:*","test:dev:vitest":"LOG=0 vitest --exclude '**/integration/**'","test:ci:vitest":"LOG=0 c8 -r lcovonly -r text -r json vitest --exclude '**/integration/**'","test:integration":"vitest run test/integration/","test:integration:codex":"vitest run test/integration/codex.test.ts","test:integration:claude":"vitest run test/integration/claude.test.ts","test:integration:gemini":"vitest run test/integration/gemini.test.ts","test:integration:create-stage":"vitest run test/integration/create.test.ts","test:integration:skill-invocation":"vitest run test/integration/skill-invocation.test.ts","check:types":"tsc --noEmit",postinstall:"node scripts/postinstall.js"},config:{eslint:'--cache --format=codeframe --max-warnings=0 "{src,scripts,test,.github}/**/*.{js,ts,md,html}"',prettier:'--ignore-path .gitignore --loglevel=warn "{src,scripts,test,.github}/**/*.{js,ts,md,yml,json,html}" "*.{js,ts,yml,json,html}" ".*.{js,ts,yml,json,html}" "!**/package-lock.json" "!package-lock.json" "!src/skills/**/*.md"'},keywords:[],license:"MIT",repository:"netlify/agent-runner-cli",bugs:{url:"https://github.com/netlify/agent-runner-cli/issues"},author:"Netlify Inc.",directories:{test:"test"},devDependencies:{"@commitlint/cli":"^20.0.0","@commitlint/config-conventional":"^20.0.0","@eslint/compat":"^2.0.0","@eslint/js":"^9.35.0","@netlify/eslint-config-node":"^7.0.1","@types/node":"^24.5.0","@typescript-eslint/eslint-plugin":"^8.0.0","@typescript-eslint/parser":"^8.0.0","@vitest/eslint-plugin":"^1.6.6",c8:"^10.0.0","eslint-config-prettier":"^10.1.8","eslint-plugin-n":"^17.0.0",husky:"^9.0.0","patch-package":"^8.0.0",tsup:"^8.5.0",typescript:"^5.0.0","typescript-eslint":"^8.44.0",vitest:"^4.0.16"},dependencies:{"@anthropic-ai/claude-code":"2.1.63","@anthropic-ai/sdk":"0.78.0","@google/gemini-cli":"0.31.0","@netlify/otel":"^5.1.2","@openai/codex":"0.110.0","@opentelemetry/exporter-trace-otlp-grpc":"0.57.2",execa:"^9.6.1",kaddidlehopper:"^0.7.2",minimist:"^1.2.8",openai:"6.26.0"}};var _n=fn(import.meta.url),En=ne.dirname(_n),wn=hn(import.meta.url),we=x("shell"),gt=new Set,xn={preferLocal:!0},N=(e,t,r)=>{let[o,n]=Tn(t,r),i={...xn,...n},s=yn(e,o,i);In(s,i),Sn(s);let a=r?.idleTimeout;return a&&a>0&&vn(s,a),s};var Tn=function(e,t){return Array.isArray(e)?[e,t]:typeof e=="object"&&e!==null?[[],e]:[[],void 0]},In=(e,t)=>{if(t.stdio!==void 0||t.stdout!==void 0||t.stderr!==void 0)return;if(Q.env.NETLIFY_MASK_LOGS!=="false"){e.all?.pipe(new Ee).pipe(Q.stdout),e.stdout?.pipe(new Ee).pipe(Q.stdout),e.stderr?.pipe(new Ee).pipe(Q.stderr);return}e.stdout?.pipe(Q.stdout),e.stderr?.pipe(Q.stderr)},ft=(e,t="SIGTERM")=>{try{return e.pid&&!e.killed?(Q.kill(-e.pid,t),we.log(`Killed process ${e.pid} with signal ${t}`),!0):!1}catch(r){return we.error("Error killing process:",r),!1}},or=e=>ft(e,"SIGKILL"),vn=(e,t)=>{let r=null,o=()=>{we.log(`Process ${e.pid} killed due to idle timeout (no output for ${t}ms)`),ft(e,"SIGTERM"),setTimeout(()=>{e.pid&&!e.killed&&(we.log(`Force killing idle process ${e.pid}`),or(e))},5e3)},n=()=>{r&&clearTimeout(r),r=setTimeout(o,t)};n(),e.stdout?.on("data",n),e.stderr?.on("data",n);let i=()=>{r&&(clearTimeout(r),r=null)};e.on("exit",i),e.on("error",i)},Sn=e=>{gt.add(e);let t=Zt();if(t){let r=t.onTimesUp(()=>{we.log(`Global timer expired, killing process ${e.pid}`),ft(e,"SIGTERM"),setTimeout(()=>{e.pid&&!e.killed&&(we.log(`Force killing process ${e.pid} after timeout`),or(e))},5e3)});e.on("exit",()=>{gt.delete(e),r()}),e.on("error",()=>{gt.delete(e),r()})}};function le(e,t){if(!Q.env.NETLIFY_LOCAL_MODE)try{let n=wn.resolve(nr.name),i=ne.dirname(n);for(;i!==ne.dirname(i);){let s=ne.dirname(i);if(ne.basename(s)==="node_modules"){let a=ne.join(s,".bin",t);if(Be.existsSync(a))return a;break}i=s}}catch(n){console.error("Could not resolve package.json",n)}if(Q.env.NODE_PATH){let n=ne.join(Q.env.NODE_PATH,".bin",t);if(Be.existsSync(n))return n}let r=ne.join(e,"node_modules",".bin",t);if(Be.existsSync(r))return r;let o=ne.join(En,"..","node_modules",".bin",t);if(Be.existsSync(o))return o}var ir=x("utils"),Rn=e=>new Promise(t=>{setTimeout(t,e)}),He=(e,t=3e3)=>{let r=!1,o=null,n=[],i=null,s=(...a)=>{if(r)return o=a,new Promise(d=>{n.push(d)});r=!0;let c,l=new Promise(d=>{c=d});return i=(async()=>{await Promise.resolve();let d=await e(...a);for(c(d);;){if(await Rn(t),!o)return r=!1,i=null,d;let p=o,u=n;o=null,n=[],d=await e(...p),u.forEach(y=>{y(d)})}})(),l};return s.flush=async()=>{if((r||o)&&i)return await i,s.flush()},s},xe=(e,t,r=!1)=>{let o=null,n=null,i=null,s=function(...a){n=a,i=this;let c=r&&!o;clearTimeout(o),o=setTimeout(()=>{o=null,r||(e.apply(i,n),n=null,i=null)},t),c&&(e.apply(i,n),n=null,i=null)};return s.cancel=()=>{clearTimeout(o),o=null,n=null,i=null},s.flush=()=>{if(o){clearTimeout(o);let a=n,c=i;o=null,n=null,i=null,e.apply(c,a)}},s},We=(e,t=!0,r)=>{if(e)try{return JSON.parse(e)}catch(o){t&&(r?.error?r.error("Could not parse JSON",o):ir.error("Could not parse JSON",o))}},ht=e=>e.charAt(0).toUpperCase()+e.slice(1),ce=e=>e.split("-").map(t=>t.length===2?t.toUpperCase():ht(t)).join(" ");function me(e,t){t&&e.log(`Skill invoked: ${t}`)}var sr=e=>Object.fromEntries(Object.entries(e).filter(([,t])=>t!==void 0)),ar=(e,t)=>{let n=".netlify.app",i="agent-";if(!t)return`${i}${e.slice(0,6)}`;let a=`--${t}${n}`;if(a.length>55)return"";let c=60-a.length;if(c<=0)return"";if(c>=i.length+6){let l=Math.min(c-i.length,e.length);return`${i}${e.slice(0,l)}`}return e.slice(0,c)},An=e=>!e||typeof e!="object"||Array.isArray(e)||Object.keys(e).length===0?!1:!!er.some(t=>t in e),lr=()=>{let e={},t={codex:process.env.NETLIFY_FF_AGENT_RUNNER_CODEX_VERSION,claude:process.env.NETLIFY_FF_AGENT_RUNNER_CLAUDE_VERSION,gemini:process.env.NETLIFY_FF_AGENT_RUNNER_GEMINI_VERSION};return Object.entries(t).forEach(([r,o])=>{if(o){let n=`NETLIFY_FF_AGENT_RUNNER_${r.toUpperCase()}_VERSION`;try{let i=JSON.parse(o);An(i)&&(e[r]=i)}catch(i){let a=i instanceof SyntaxError?"Invalid JSON":i.message;ir.error(`Could not parse ${r} model version override from ${n}: ${a}`)}}}),e},Cn=50*1024,yt=(e,t=Cn)=>{if(!e||typeof e!="string"||e.length<=t)return e;let o=e.startsWith("```")?"\n... [truncated]\n```":"... [truncated]";return e.slice(0,t)+o};import{Buffer as cr}from"buffer";import bn from"path";var ur=x("repo"),pr=async({config:e,isRetry:t,cwd:r=process.cwd()})=>{ur.info("Getting runner diffs");let o=await kn(r),{hasChanges:n}=o,{status:i}=o;if(!n)return{hasChanges:!1};if(!t){let E=On(i);await $n(E,r)}ur.info("Changes after processing"),await Et(r);let s=await xt(i,r);if(await _t(s,r),n=await Nn(r),!n)return{hasChanges:!1,ignored:s};process.env.NETLIFY_INTERNAL_GIT="1";try{await N("git",["commit","-m","Agent runner"],{cwd:r})}finally{process.env.NETLIFY_INTERNAL_GIT="0"}let a={stdio:["ignore","pipe","pipe"],cwd:r},c=await N("git",["diff",e.runSha,"HEAD"],a),l=String(c.stdout??"");if(n=!!l,!n)return await dr(r),{hasChanges:!1,ignored:s};let d=await N("git",["diff",e.runSha,"HEAD","--binary"],a),p=String(d.stdout??""),u,y;if(e.sha){let E=await N("git",["diff",e.sha,"HEAD"],a);u=String(E.stdout??"");let I=await N("git",["diff",e.sha,"HEAD","--binary"],a),f=String(I.stdout??"");u!==f&&(y=cr.from(f).toString("base64"))}await dr(r);let w={hasChanges:!0,diff:l,resultDiff:u,ignored:s};return l!==p&&(w.diffBinary=cr.from(p).toString("base64")),y&&(w.resultDiffBinary=y),w},dr=async(e=process.cwd())=>{process.env.NETLIFY_LOCAL_MODE&&await N("git",["reset","--soft","HEAD~1"],{cwd:e})},_t=async(e=[],t=process.cwd())=>{process.env.NETLIFY_INTERNAL_GIT="1";try{await N("git",["add",".",...e],{cwd:t})}finally{process.env.NETLIFY_INTERNAL_GIT="0"}},Et=async(e=process.cwd())=>{let t=await N("git",["status","-s"],{cwd:e});return String(t.stdout??"")},mr=/.. (.+)?\.log$/,Pn=[mr],kn=async(e=process.cwd())=>{let t=await Et(e);return{hasChanges:(t.trim().length===0?[]:t.split(`
9
9
  `).filter(n=>Pn.some(s=>s instanceof RegExp?s.test(n):n===s)?!1:n[1]?.trim()!=="")).length!==0,status:t}},Nn=async(e=process.cwd())=>{try{return await N("git",["diff","--staged","--quiet"],{cwd:e}),!1}catch{return!0}},wt=async(e=process.cwd())=>{let{stdout:t}=await N("git",["rev-parse","HEAD"],{cwd:e});return String(t??"").trim()},gr=async(e=process.cwd())=>{let{stdout:t}=await N("git",["rev-list","--max-parents=0","HEAD"],{cwd:e});return String(t??"").trim()},xt=async(e,t=process.cwd())=>{e||=await Et(t);let r=[".netlify","node_modules","dist",".next","out",".nuxt",".output",".cache",".turbo",".parcel-cache","coverage",".nyc_output","storybook-static","public/build","CLAUDE.local.md"],o=[];return e.split(`
10
10
  `).forEach(n=>{r.forEach(s=>{let a=n===`?? ${s}`,c=n.startsWith(`?? ${s}/`)||n.startsWith(`?? ${s}${bn.sep}`);(a||c)&&o.push(`:!${s}`)});let i=n.match(mr)?.[1];i&&o.push(`:!${i}.log`)}),o},Tt=async(e=process.cwd())=>{await N("git",["reset","--hard","HEAD"],{cwd:e})},On=e=>{let t=e.split(`
11
11
  `).reduce((r,o)=>{if(!o)return r;let[n,i,,...s]=o,a=s.join(""),c=n.trim(),l=i.trim();return r[a]?r[a].change=l:r[a]={filePath:a,stage:c,change:l},r},{});return Object.values(t)},$n=async(e,t=process.cwd())=>{let r=e.filter(o=>o.stage&&!o.change).map(o=>o.filePath);r.length!==0&&await N("git",["restore","--staged","--worktree","--pathspec-from-file=-"],{cwd:t,input:r.join(`
package/dist/index.js CHANGED
@@ -4,7 +4,7 @@ import{createRequire as hi}from"module";import{createTracerProvider as Gr}from"@
4
4
  ${s}
5
5
  </extracted_error_chunk>`).join(`
6
6
 
7
- `);return o.length>e.length*.8?e:o}import{execSync as Un}from"child_process";import Er from"fs/promises";import Gn from"path";import ge from"process";import{getTracer as jn}from"@netlify/otel";import be from"process";var z=class extends Error{constructor(r,i,n,o=!1){super(r);this.statusCode=i;this.userMessage=n;this.isCreditLimitExceeded=o;this.name="GracefulShutdownError"}},Le=e=>e instanceof z;var Me=be.env.NETLIFY_API_URL,Ue=be.env.NETLIFY_API_TOKEN,W=x("api"),he=()=>be.env.NETLIFY_LOCAL_MODE==="true",oe=async(e,t={})=>{if(!Me||!Ue)throw new Error("No API URL or token");let r=new URL(e,Me),i={...t,headers:{...t.headers,Authorization:`Bearer ${Ue}`}};be.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(t.json));let n=await fetch(r,i),o=n.ok&&n.status<=299;if(be.env.AGENT_RUNNERS_DEBUG==="true")W.log(`Response headers for ${r}:`),n.headers.forEach((a,c)=>{W.log(` ${c}: ${a}`)});else{let a=n.headers.get("x-request-id")||n.headers.get("x-nf-request-id");W.log(`Request ID for ${r}: ${a||"N/A"}`)}if(o||W.error(`Got status ${n.status} for request ${r}`),t.raw){if(!o)throw new Error(`API request failed: ${n.status} ${n.statusText}`);return n}let s=await(n.headers.get("content-type")?.includes("application/json")?n.json():n.text());if(!o){let a=typeof s=="string"?s:JSON.stringify(s);throw n.status===404?new z(`API request failed: 404 - ${a}`,404,"The site associated with this agent run no longer exists."):n.status===503&&t.gracefulOn503&&a.toLowerCase().includes("usage exceeded")?new z(`API request failed: 503 - ${a}`,503,"Credit limit reached. Please add more credits to continue using Agent Runners.",!0):new Error(`API request failed: ${n.status} - ${a}`)}return s},Nt=e=>{W.log("Setting details for api",{apiUrl:e?.constants?.NETLIFY_API_HOST,token:!!e?.constants?.NETLIFY_API_TOKEN}),e?.constants?.NETLIFY_API_HOST&&(Me=`https://${e.constants.NETLIFY_API_HOST}`),e?.constants?.NETLIFY_API_TOKEN&&(Ue=e.constants.NETLIFY_API_TOKEN)},$t=()=>({apiUrl:Me,token:Ue}),Ae=async(e,t)=>he()?(W.log("Mock API: updateRunner called",{runnerId:e,data:t}),{id:e,...t}):oe(`/api/v1/agent_runners/${e}`,{method:"PUT",json:t}),H=async(e,t,r)=>he()?(W.log("Mock API: updateRunnerSession called",JSON.stringify({runnerId:e,sessionId:t,data:r},null,2)),{id:e,sessionId:t,...r}):oe(`/api/v1/agent_runners/${e}/sessions/${t}`,{method:"PUT",json:r});var Ot=async e=>he()?(W.log("Mock API: getSite called",{siteId:e}),{id:e,published_deploy:{id:"id"}}):oe(`/api/v1/sites/${e}`),Ft=async(e,t)=>he()?(W.log("Mock API: getRunnerSession called",{runnerId:e,sessionId:t}),{id:t,runnerId:e,state:"running"}):oe(`/api/v1/agent_runners/${e}/sessions/${t}`),Dt=(e,t,r)=>oe(`/api/v1/accounts/${e}/ai-gateway/token`,{headers:{"X-Nf-Agent-Runner-Id":t,"X-Nf-Agent-Runner-Session-Id":r},gracefulOn503:!0}),Lt=(e,t,r)=>oe(`/api/v1/sites/${e}/ai-gateway/token`,{headers:{"X-Nf-Agent-Runner-Id":t,"X-Nf-Agent-Runner-Session-Id":r},gracefulOn503:!0}),Mt=async(e,t)=>he()?(W.log("Mock API: getDiffUploadUrls called",{runnerId:e,sessionId:t}),{result:{upload_url:"https://s3.mock.com/mock-upload-url-result",s3_key:"mock-s3-key-result"},cumulative:{upload_url:"https://s3.mock.com/mock-upload-url-cumulative",s3_key:"mock-s3-key-cumulative"}}):oe(`/api/v1/agent_runners/${e}/sessions/${t}/diff/upload_urls`,{method:"POST"}),Ut=async(e,t)=>he()?(W.log("Mock API: updateSessionUsage called",{runnerId:e,sessionId:t}),{id:t,runnerId:e,usage:0}):oe(`/api/v1/agent_runners/${e}/sessions/${t}/update_usage`,{method:"POST"}),nt=async(e,t)=>{W.log(`Uploading diff to S3: ${e.substring(0,50)}...`);let r=await fetch(e,{method:"PUT",body:t,headers:{"Content-Type":"text/plain"}});if(!r.ok)throw new Error(`S3 upload failed with status ${r.status}`);return r};var ye=x("ai_gateway"),it=null;var Ge=async()=>{if(it)return it;ye.log("Fetching available AI gateway providers");let e=await fetch(`${$t().apiUrl}/api/v1/ai-gateway/providers`);if(!e.ok)throw new Error(`Failed to fetch AI gateway providers: ${e.statusText}`);let t=await e.json();return it=t,ye.log("Cached AI gateway providers",{providerCount:Object.keys(t.providers).length}),t},Hr=async(e,t)=>{let i=(await Ge()).providers[e];if(!i)return ye.log(`Provider '${e}' not found`),!1;let n=i.models.includes(t);return ye.log(`Model validation for ${e}/${t}`,{isAvailable:n}),n},Gt=async({config:e})=>{let t,r,i,n,o=!e.site?.published_deploy;if(!(o?e.accountId:e.siteId))throw new Error(`No entity id for ${o?"account":"site"}`);let a=async()=>{clearTimeout(i),ye.log("Requesting AI gateway information");let l=await(o?Dt(e.accountId,e.id,e.sessionId):Lt(e.siteId,e.id,e.sessionId));if({token:t,url:n}=l,r=l.expires_at?l.expires_at*1e3:void 0,ye.log("Got AI gateway information",{token:!!t,expiresAt:r,url:n}),r){let d=r-Date.now()-6e4;d>0&&(i=setTimeout(()=>{a()},d))}};return await Promise.all([a(),Ge()]),{get url(){return n},get token(){return t},isModelAvailableForProvider:Hr}};import Z from"process";import re from"path";import je from"fs";import{fileURLToPath as Qr}from"url";import{createRequire as en}from"module";import{execa as tn,execaCommand as zi}from"execa";import{Transform as Kr}from"stream";function Jr(){let e=process.env.NETLIFY_SENSITIVE_ENV_KEYS;return e?e.split(",").map(t=>t.trim()).filter(Boolean):[]}function Vr(e){let t=e.toLowerCase();return t==="true"||t==="false"?!0:e.trim().length<4}function zr(){let t=Jr().map(r=>process.env[r]).filter(r=>!(!r||Vr(r)));return[...new Set(t)].sort((r,i)=>i.length-r.length)}function te(e){if(typeof e!="string")return e;let t=zr();if(t.length===0)return e;let r=e;return t.forEach(i=>{let n=new RegExp(Xr(i),"g");r=r.replace(n,"******")}),r}function Xr(e){return e.replace(/[.*+?^${}()|[\]\\]/g,"\\$&")}var we=class extends Kr{constructor(t={}){super({...t,objectMode:!1})}_transform(t,r,i){let n=t.toString(),o=te(n);i(null,o)}};function jt(){if(!(process.env.NETLIFY_MASK_LOGS!=="false"))return;let t=process.stdout.write.bind(process.stdout),r=process.stderr.write.bind(process.stderr);process.stdout.write=function(i,n,o){let s=typeof i=="string"?te(i):i;return typeof n=="function"?t(s,n):t(s,n,o)},process.stderr.write=function(i,n,o){let s=typeof i=="string"?te(i):i;return typeof n=="function"?r(s,n):r(s,n,o)}}var Ce=null,Yt=e=>(Ce&&Ce.destroy(),Ce=new se({totalAllowedTime:e}),Ce),qt=()=>Ce;var se=class{constructor({totalAllowedTime:t}){this.withStageTimer=async(t,r,i)=>{if(this.isTimeExpired())throw new Error(`${t} stage did not complete in the allowed time. Time has already expired.`);let n=this.onTimesUp(()=>{throw new Error(`${t} stage did not complete in the allowed time.`)}),o=null,s=null;i!==void 0&&(s=new Promise((a,c)=>{o=setTimeout(()=>{c(new Error(`${t} stage exceeded its maximum duration of ${i}ms`))},i)}));try{return s?await Promise.race([r(),s]):await r()}finally{n(),o&&clearTimeout(o)}};this.startTime=Date.now(),this.totalAllowedTime=t,this.globalTimeoutId=null,this.subscribers=[],this.hasTimedOut=!1,this.setupGlobalTimeout()}getElapsedTime(){return Date.now()-this.startTime}getRemainingTime(){let t=this.getElapsedTime(),r=this.totalAllowedTime-t;return Math.max(0,r)}isTimeExpired(){return this.getRemainingTime()===0||this.hasTimedOut}setupGlobalTimeout(){this.globalTimeoutId&&clearTimeout(this.globalTimeoutId),this.globalTimeoutId=setTimeout(()=>{this.notifyTimeUp()},this.totalAllowedTime)}notifyTimeUp(){this.hasTimedOut=!0;for(let t=this.subscribers.length-1;t>=0;t--)try{this.subscribers[t]()}catch(r){console.error("TimeKeeper: Error in time up callback:",r)}}onTimesUp(t){if(this.subscribers.push(t),this.hasTimedOut)try{t()}catch(r){console.error("TimeKeeper: Error in time up callback:",r)}return()=>{let r=this.subscribers.indexOf(t);r>-1&&this.subscribers.splice(r,1)}}off(t){let r=this.subscribers.indexOf(t);r>-1&&this.subscribers.splice(r,1)}clearSubscribers(){this.subscribers.length=0}getSubscriberCount(){return this.subscribers.length}destroy(){this.globalTimeoutId&&(clearTimeout(this.globalTimeoutId),this.globalTimeoutId=null),this.clearSubscribers()}static{this.timeUnits={seconds:t=>t*1e3,minutes:t=>t*60*1e3,hours:t=>t*60*60*1e3}}};var Bt="netlify-agent-runner-context.md",ot="task-history",X=".netlify",ue="results.md",st="assets";var Wt="free";var de=1800*1e3,y={Environment:"environment",UserMessage:"user-message",AgentMessage:"agent-message",Task:"task",RunCommand:"run-command",Explore:"explore",Plan:"plan",FileRead:"file-read",FileWrite:"file-write",Notebook:"notebook",Web:"web",Todo:"todo",Reasoning:"reasoning",Skill:"skill",Memorize:"memorize",Deployment:"deployment",SiteGeneration:"site-generation"};var Ht={name:"@netlify/agent-runner-cli",type:"module",version:"1.90.2",description:"CLI tool for running Netlify agents",main:"./dist/index.js",types:"./dist/index.d.ts",exports:"./dist/index.js",bin:{"agent-runner-cli":"./dist/bin.js","agent-runner-cli-local":"./dist/bin-local.js"},files:["dist/**/*.js","dist/**/*.d.ts","dist/skills/**","patches","scripts"],scripts:{build:"tsup",dev:"tsup --watch",prepare:"husky install node_modules/@netlify/eslint-config-node/.husky/",prepublishOnly:"npm ci && npm test",prepack:"npm run build",test:"run-s build format test:dev",format:"run-s build format:check-fix:*","format:ci":"run-s build format:check:*","format:check-fix:lint":"run-e format:check:lint format:fix:lint","format:check:lint":"cross-env-shell eslint $npm_package_config_eslint","format:fix:lint":"cross-env-shell eslint --fix $npm_package_config_eslint","format:check-fix:prettier":"run-e format:check:prettier format:fix:prettier","format:check:prettier":"cross-env-shell prettier --check $npm_package_config_prettier","format:fix:prettier":"cross-env-shell prettier --write $npm_package_config_prettier","test:dev":"run-s build test:dev:*","test:ci":"run-s build test:ci:*","test:dev:vitest":"LOG=0 vitest --exclude '**/integration/**'","test:ci:vitest":"LOG=0 c8 -r lcovonly -r text -r json vitest --exclude '**/integration/**'","test:integration":"vitest run test/integration/","test:integration:codex":"vitest run test/integration/codex.test.ts","test:integration:claude":"vitest run test/integration/claude.test.ts","test:integration:gemini":"vitest run test/integration/gemini.test.ts","test:integration:create-stage":"vitest run test/integration/create.test.ts","test:integration:skill-invocation":"vitest run test/integration/skill-invocation.test.ts","check:types":"tsc --noEmit",postinstall:"node scripts/postinstall.js"},config:{eslint:'--cache --format=codeframe --max-warnings=0 "{src,scripts,test,.github}/**/*.{js,ts,md,html}"',prettier:'--ignore-path .gitignore --loglevel=warn "{src,scripts,test,.github}/**/*.{js,ts,md,yml,json,html}" "*.{js,ts,yml,json,html}" ".*.{js,ts,yml,json,html}" "!**/package-lock.json" "!package-lock.json" "!src/skills/**/*.md"'},keywords:[],license:"MIT",repository:"netlify/agent-runner-cli",bugs:{url:"https://github.com/netlify/agent-runner-cli/issues"},author:"Netlify Inc.",directories:{test:"test"},devDependencies:{"@commitlint/cli":"^20.0.0","@commitlint/config-conventional":"^20.0.0","@eslint/compat":"^2.0.0","@eslint/js":"^9.35.0","@netlify/eslint-config-node":"^7.0.1","@types/node":"^24.5.0","@typescript-eslint/eslint-plugin":"^8.0.0","@typescript-eslint/parser":"^8.0.0","@vitest/eslint-plugin":"^1.6.6",c8:"^10.0.0","eslint-config-prettier":"^10.1.8","eslint-plugin-n":"^17.0.0",husky:"^9.0.0","patch-package":"^8.0.0",tsup:"^8.5.0",typescript:"^5.0.0","typescript-eslint":"^8.44.0",vitest:"^4.0.16"},dependencies:{"@anthropic-ai/claude-code":"2.1.63","@anthropic-ai/sdk":"0.78.0","@google/gemini-cli":"0.31.0","@netlify/otel":"^5.1.2","@openai/codex":"0.110.0","@opentelemetry/exporter-trace-otlp-grpc":"0.57.2",execa:"^9.6.1",kaddidlehopper:"^0.7.2",minimist:"^1.2.8",openai:"6.26.0"}};var rn=Qr(import.meta.url),nn=re.dirname(rn),on=en(import.meta.url),_e=x("shell"),at=new Set,sn={preferLocal:!0},N=(e,t,r)=>{let[i,n]=an(t,r),o={...sn,...n},s=tn(e,i,o);ln(s,o),un(s);let a=r?.idleTimeout;return a&&a>0&&cn(s,a),s};var an=function(e,t){return Array.isArray(e)?[e,t]:typeof e=="object"&&e!==null?[[],e]:[[],void 0]},ln=(e,t)=>{if(t.stdio!==void 0||t.stdout!==void 0||t.stderr!==void 0)return;if(Z.env.NETLIFY_MASK_LOGS!=="false"){e.all?.pipe(new we).pipe(Z.stdout),e.stdout?.pipe(new we).pipe(Z.stdout),e.stderr?.pipe(new we).pipe(Z.stderr);return}e.stdout?.pipe(Z.stdout),e.stderr?.pipe(Z.stderr)},lt=(e,t="SIGTERM")=>{try{return e.pid&&!e.killed?(Z.kill(-e.pid,t),_e.log(`Killed process ${e.pid} with signal ${t}`),!0):!1}catch(r){return _e.error("Error killing process:",r),!1}},Kt=e=>lt(e,"SIGKILL"),cn=(e,t)=>{let r=null,i=()=>{_e.log(`Process ${e.pid} killed due to idle timeout (no output for ${t}ms)`),lt(e,"SIGTERM"),setTimeout(()=>{e.pid&&!e.killed&&(_e.log(`Force killing idle process ${e.pid}`),Kt(e))},5e3)},n=()=>{r&&clearTimeout(r),r=setTimeout(i,t)};n(),e.stdout?.on("data",n),e.stderr?.on("data",n);let o=()=>{r&&(clearTimeout(r),r=null)};e.on("exit",o),e.on("error",o)},un=e=>{at.add(e);let t=qt();if(t){let r=t.onTimesUp(()=>{_e.log(`Global timer expired, killing process ${e.pid}`),lt(e,"SIGTERM"),setTimeout(()=>{e.pid&&!e.killed&&(_e.log(`Force killing process ${e.pid} after timeout`),Kt(e))},5e3)});e.on("exit",()=>{at.delete(e),r()}),e.on("error",()=>{at.delete(e),r()})}};function ae(e,t){if(!Z.env.NETLIFY_LOCAL_MODE)try{let n=on.resolve(Ht.name),o=re.dirname(n);for(;o!==re.dirname(o);){let s=re.dirname(o);if(re.basename(s)==="node_modules"){let a=re.join(s,".bin",t);if(je.existsSync(a))return a;break}o=s}}catch(n){console.error("Could not resolve package.json",n)}if(Z.env.NODE_PATH){let n=re.join(Z.env.NODE_PATH,".bin",t);if(je.existsSync(n))return n}let r=re.join(e,"node_modules",".bin",t);if(je.existsSync(r))return r;let i=re.join(nn,"..","node_modules",".bin",t);if(je.existsSync(i))return i}var dn=x("utils"),pn=e=>new Promise(t=>{setTimeout(t,e)}),Ye=(e,t=3e3)=>{let r=!1,i=null,n=[],o=null,s=(...a)=>{if(r)return i=a,new Promise(d=>{n.push(d)});r=!0;let c,l=new Promise(d=>{c=d});return o=(async()=>{await Promise.resolve();let d=await e(...a);for(c(d);;){if(await pn(t),!i)return r=!1,o=null,d;let m=i,u=n;i=null,n=[],d=await e(...m),u.forEach(w=>{w(d)})}})(),l};return s.flush=async()=>{if((r||i)&&o)return await o,s.flush()},s},Ee=(e,t,r=!1)=>{let i=null,n=null,o=null,s=function(...a){n=a,o=this;let c=r&&!i;clearTimeout(i),i=setTimeout(()=>{i=null,r||(e.apply(o,n),n=null,o=null)},t),c&&(e.apply(o,n),n=null,o=null)};return s.cancel=()=>{clearTimeout(i),i=null,n=null,o=null},s.flush=()=>{if(i){clearTimeout(i);let a=n,c=o;i=null,n=null,o=null,e.apply(c,a)}},s},Jt=(e,t=!0,r)=>{if(e)try{return JSON.parse(e)}catch(i){t&&(r?.error?r.error("Could not parse JSON",i):dn.error("Could not parse JSON",i))}},ct=e=>e.charAt(0).toUpperCase()+e.slice(1),le=e=>e.split("-").map(t=>t.length===2?t.toUpperCase():ct(t)).join(" ");function pe(e,t){t&&e.log(`Skill invoked: ${t}`)}var Vt=e=>Object.fromEntries(Object.entries(e).filter(([,t])=>t!==void 0)),zt=(e,t)=>{let n=".netlify.app",o="agent-";if(!t)return`${o}${e.slice(0,6)}`;let a=`--${t}${n}`;if(a.length>55)return"";let c=60-a.length;if(c<=0)return"";if(c>=o.length+6){let l=Math.min(c-o.length,e.length);return`${o}${e.slice(0,l)}`}return e.slice(0,c)};var mn=50*1024,ut=(e,t=mn)=>{if(!e||typeof e!="string"||e.length<=t)return e;let i=e.startsWith("```")?"\n... [truncated]\n```":"... [truncated]";return e.slice(0,t)+i};import{Buffer as Xt}from"buffer";import gn from"path";var Zt=x("repo"),er=async({config:e,isRetry:t,cwd:r=process.cwd()})=>{Zt.info("Getting runner diffs");let i=await hn(r),{hasChanges:n}=i,{status:o}=i;if(!n)return{hasChanges:!1};if(!t){let _=wn(o);await _n(_,r)}Zt.info("Changes after processing"),await pt(r);let s=await gt(o,r);if(await dt(s,r),n=await yn(r),!n)return{hasChanges:!1,ignored:s};process.env.NETLIFY_INTERNAL_GIT="1";try{await N("git",["commit","-m","Agent runner"],{cwd:r})}finally{process.env.NETLIFY_INTERNAL_GIT="0"}let a={stdio:["ignore","pipe","pipe"],cwd:r},c=await N("git",["diff",e.runSha,"HEAD"],a),l=String(c.stdout??"");if(n=!!l,!n)return await Qt(r),{hasChanges:!1,ignored:s};let d=await N("git",["diff",e.runSha,"HEAD","--binary"],a),m=String(d.stdout??""),u,w;if(e.sha){let _=await N("git",["diff",e.sha,"HEAD"],a);u=String(_.stdout??"");let I=await N("git",["diff",e.sha,"HEAD","--binary"],a),h=String(I.stdout??"");u!==h&&(w=Xt.from(h).toString("base64"))}await Qt(r);let E={hasChanges:!0,diff:l,resultDiff:u,ignored:s};return l!==m&&(E.diffBinary=Xt.from(m).toString("base64")),w&&(E.resultDiffBinary=w),E},Qt=async(e=process.cwd())=>{process.env.NETLIFY_LOCAL_MODE&&await N("git",["reset","--soft","HEAD~1"],{cwd:e})},dt=async(e=[],t=process.cwd())=>{process.env.NETLIFY_INTERNAL_GIT="1";try{await N("git",["add",".",...e],{cwd:t})}finally{process.env.NETLIFY_INTERNAL_GIT="0"}},pt=async(e=process.cwd())=>{let t=await N("git",["status","-s"],{cwd:e});return String(t.stdout??"")},tr=/.. (.+)?\.log$/,fn=[tr],hn=async(e=process.cwd())=>{let t=await pt(e);return{hasChanges:(t.trim().length===0?[]:t.split(`
7
+ `);return o.length>e.length*.8?e:o}import{execSync as Un}from"child_process";import Er from"fs/promises";import Gn from"path";import ge from"process";import{getTracer as jn}from"@netlify/otel";import be from"process";var z=class extends Error{constructor(r,i,n,o=!1){super(r);this.statusCode=i;this.userMessage=n;this.isCreditLimitExceeded=o;this.name="GracefulShutdownError"}},Le=e=>e instanceof z;var Me=be.env.NETLIFY_API_URL,Ue=be.env.NETLIFY_API_TOKEN,W=x("api"),he=()=>be.env.NETLIFY_LOCAL_MODE==="true",oe=async(e,t={})=>{if(!Me||!Ue)throw new Error("No API URL or token");let r=new URL(e,Me),i={...t,headers:{...t.headers,Authorization:`Bearer ${Ue}`}};be.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(t.json));let n=await fetch(r,i),o=n.ok&&n.status<=299;if(be.env.AGENT_RUNNERS_DEBUG==="true")W.log(`Response headers for ${r}:`),n.headers.forEach((a,c)=>{W.log(` ${c}: ${a}`)});else{let a=n.headers.get("x-request-id")||n.headers.get("x-nf-request-id");W.log(`Request ID for ${r}: ${a||"N/A"}`)}if(o||W.error(`Got status ${n.status} for request ${r}`),t.raw){if(!o)throw new Error(`API request failed: ${n.status} ${n.statusText}`);return n}let s=await(n.headers.get("content-type")?.includes("application/json")?n.json():n.text());if(!o){let a=typeof s=="string"?s:JSON.stringify(s);throw n.status===404?new z(`API request failed: 404 - ${a}`,404,"The site associated with this agent run no longer exists."):n.status===503&&t.gracefulOn503&&a.toLowerCase().includes("usage exceeded")?new z(`API request failed: 503 - ${a}`,503,"Credit limit reached. Please add more credits to continue using Agent Runners.",!0):new Error(`API request failed: ${n.status} - ${a}`)}return s},Nt=e=>{W.log("Setting details for api",{apiUrl:e?.constants?.NETLIFY_API_HOST,token:!!e?.constants?.NETLIFY_API_TOKEN}),e?.constants?.NETLIFY_API_HOST&&(Me=`https://${e.constants.NETLIFY_API_HOST}`),e?.constants?.NETLIFY_API_TOKEN&&(Ue=e.constants.NETLIFY_API_TOKEN)},$t=()=>({apiUrl:Me,token:Ue}),Ae=async(e,t)=>he()?(W.log("Mock API: updateRunner called",{runnerId:e,data:t}),{id:e,...t}):oe(`/api/v1/agent_runners/${e}`,{method:"PUT",json:t}),H=async(e,t,r)=>he()?(W.log("Mock API: updateRunnerSession called",JSON.stringify({runnerId:e,sessionId:t,data:r},null,2)),{id:e,sessionId:t,...r}):oe(`/api/v1/agent_runners/${e}/sessions/${t}`,{method:"PUT",json:r});var Ot=async e=>he()?(W.log("Mock API: getSite called",{siteId:e}),{id:e,published_deploy:{id:"id"}}):oe(`/api/v1/sites/${e}`),Ft=async(e,t)=>he()?(W.log("Mock API: getRunnerSession called",{runnerId:e,sessionId:t}),{id:t,runnerId:e,state:"running"}):oe(`/api/v1/agent_runners/${e}/sessions/${t}`),Dt=(e,t,r)=>oe(`/api/v1/accounts/${e}/ai-gateway/token`,{headers:{"X-Nf-Agent-Runner-Id":t,"X-Nf-Agent-Runner-Session-Id":r},gracefulOn503:!0}),Lt=(e,t,r)=>oe(`/api/v1/sites/${e}/ai-gateway/token`,{headers:{"X-Nf-Agent-Runner-Id":t,"X-Nf-Agent-Runner-Session-Id":r},gracefulOn503:!0}),Mt=async(e,t)=>he()?(W.log("Mock API: getDiffUploadUrls called",{runnerId:e,sessionId:t}),{result:{upload_url:"https://s3.mock.com/mock-upload-url-result",s3_key:"mock-s3-key-result"},cumulative:{upload_url:"https://s3.mock.com/mock-upload-url-cumulative",s3_key:"mock-s3-key-cumulative"}}):oe(`/api/v1/agent_runners/${e}/sessions/${t}/diff/upload_urls`,{method:"POST"}),Ut=async(e,t)=>he()?(W.log("Mock API: updateSessionUsage called",{runnerId:e,sessionId:t}),{id:t,runnerId:e,usage:0}):oe(`/api/v1/agent_runners/${e}/sessions/${t}/update_usage`,{method:"POST"}),nt=async(e,t)=>{W.log(`Uploading diff to S3: ${e.substring(0,50)}...`);let r=await fetch(e,{method:"PUT",body:t,headers:{"Content-Type":"text/plain"}});if(!r.ok)throw new Error(`S3 upload failed with status ${r.status}`);return r};var ye=x("ai_gateway"),it=null;var Ge=async()=>{if(it)return it;ye.log("Fetching available AI gateway providers");let e=await fetch(`${$t().apiUrl}/api/v1/ai-gateway/providers`);if(!e.ok)throw new Error(`Failed to fetch AI gateway providers: ${e.statusText}`);let t=await e.json();return it=t,ye.log("Cached AI gateway providers",{providerCount:Object.keys(t.providers).length}),t},Hr=async(e,t)=>{let i=(await Ge()).providers[e];if(!i)return ye.log(`Provider '${e}' not found`),!1;let n=i.models.includes(t);return ye.log(`Model validation for ${e}/${t}`,{isAvailable:n}),n},Gt=async({config:e})=>{let t,r,i,n,o=!e.site?.published_deploy;if(!(o?e.accountId:e.siteId))throw new Error(`No entity id for ${o?"account":"site"}`);let a=async()=>{clearTimeout(i),ye.log("Requesting AI gateway information");let l=await(o?Dt(e.accountId,e.id,e.sessionId):Lt(e.siteId,e.id,e.sessionId));if({token:t,url:n}=l,r=l.expires_at?l.expires_at*1e3:void 0,ye.log("Got AI gateway information",{token:!!t,expiresAt:r,url:n}),r){let d=r-Date.now()-6e4;d>0&&(i=setTimeout(()=>{a()},d))}};return await Promise.all([a(),Ge()]),{get url(){return n},get token(){return t},isModelAvailableForProvider:Hr}};import Z from"process";import re from"path";import je from"fs";import{fileURLToPath as Qr}from"url";import{createRequire as en}from"module";import{execa as tn,execaCommand as zi}from"execa";import{Transform as Kr}from"stream";function Jr(){let e=process.env.NETLIFY_SENSITIVE_ENV_KEYS;return e?e.split(",").map(t=>t.trim()).filter(Boolean):[]}function Vr(e){let t=e.toLowerCase();return t==="true"||t==="false"?!0:e.trim().length<4}function zr(){let t=Jr().map(r=>process.env[r]).filter(r=>!(!r||Vr(r)));return[...new Set(t)].sort((r,i)=>i.length-r.length)}function te(e){if(typeof e!="string")return e;let t=zr();if(t.length===0)return e;let r=e;return t.forEach(i=>{let n=new RegExp(Xr(i),"g");r=r.replace(n,"******")}),r}function Xr(e){return e.replace(/[.*+?^${}()|[\]\\]/g,"\\$&")}var we=class extends Kr{constructor(t={}){super({...t,objectMode:!1})}_transform(t,r,i){let n=t.toString(),o=te(n);i(null,o)}};function jt(){if(!(process.env.NETLIFY_MASK_LOGS!=="false"))return;let t=process.stdout.write.bind(process.stdout),r=process.stderr.write.bind(process.stderr);process.stdout.write=function(i,n,o){let s=typeof i=="string"?te(i):i;return typeof n=="function"?t(s,n):t(s,n,o)},process.stderr.write=function(i,n,o){let s=typeof i=="string"?te(i):i;return typeof n=="function"?r(s,n):r(s,n,o)}}var Ce=null,Yt=e=>(Ce&&Ce.destroy(),Ce=new se({totalAllowedTime:e}),Ce),qt=()=>Ce;var se=class{constructor({totalAllowedTime:t}){this.withStageTimer=async(t,r,i)=>{if(this.isTimeExpired())throw new Error(`${t} stage did not complete in the allowed time. Time has already expired.`);let n=this.onTimesUp(()=>{throw new Error(`${t} stage did not complete in the allowed time.`)}),o=null,s=null;i!==void 0&&(s=new Promise((a,c)=>{o=setTimeout(()=>{c(new Error(`${t} stage exceeded its maximum duration of ${i}ms`))},i)}));try{return s?await Promise.race([r(),s]):await r()}finally{n(),o&&clearTimeout(o)}};this.startTime=Date.now(),this.totalAllowedTime=t,this.globalTimeoutId=null,this.subscribers=[],this.hasTimedOut=!1,this.setupGlobalTimeout()}getElapsedTime(){return Date.now()-this.startTime}getRemainingTime(){let t=this.getElapsedTime(),r=this.totalAllowedTime-t;return Math.max(0,r)}isTimeExpired(){return this.getRemainingTime()===0||this.hasTimedOut}setupGlobalTimeout(){this.globalTimeoutId&&clearTimeout(this.globalTimeoutId),this.globalTimeoutId=setTimeout(()=>{this.notifyTimeUp()},this.totalAllowedTime)}notifyTimeUp(){this.hasTimedOut=!0;for(let t=this.subscribers.length-1;t>=0;t--)try{this.subscribers[t]()}catch(r){console.error("TimeKeeper: Error in time up callback:",r)}}onTimesUp(t){if(this.subscribers.push(t),this.hasTimedOut)try{t()}catch(r){console.error("TimeKeeper: Error in time up callback:",r)}return()=>{let r=this.subscribers.indexOf(t);r>-1&&this.subscribers.splice(r,1)}}off(t){let r=this.subscribers.indexOf(t);r>-1&&this.subscribers.splice(r,1)}clearSubscribers(){this.subscribers.length=0}getSubscriberCount(){return this.subscribers.length}destroy(){this.globalTimeoutId&&(clearTimeout(this.globalTimeoutId),this.globalTimeoutId=null),this.clearSubscribers()}static{this.timeUnits={seconds:t=>t*1e3,minutes:t=>t*60*1e3,hours:t=>t*60*60*1e3}}};var Bt="netlify-agent-runner-context.md",ot="task-history",X=".netlify",ue="results.md",st="assets";var Wt="free";var de=1800*1e3,y={Environment:"environment",UserMessage:"user-message",AgentMessage:"agent-message",Task:"task",RunCommand:"run-command",Explore:"explore",Plan:"plan",FileRead:"file-read",FileWrite:"file-write",Notebook:"notebook",Web:"web",Todo:"todo",Reasoning:"reasoning",Skill:"skill",Memorize:"memorize",Deployment:"deployment",SiteGeneration:"site-generation"};var Ht={name:"@netlify/agent-runner-cli",type:"module",version:"1.90.3",description:"CLI tool for running Netlify agents",main:"./dist/index.js",types:"./dist/index.d.ts",exports:"./dist/index.js",bin:{"agent-runner-cli":"./dist/bin.js","agent-runner-cli-local":"./dist/bin-local.js"},files:["dist/**/*.js","dist/**/*.d.ts","dist/skills/**","patches","scripts"],scripts:{build:"tsup",dev:"tsup --watch",prepare:"husky install node_modules/@netlify/eslint-config-node/.husky/",prepublishOnly:"npm ci && npm test",prepack:"npm run build",test:"run-s build format test:dev",format:"run-s build format:check-fix:*","format:ci":"run-s build format:check:*","format:check-fix:lint":"run-e format:check:lint format:fix:lint","format:check:lint":"cross-env-shell eslint $npm_package_config_eslint","format:fix:lint":"cross-env-shell eslint --fix $npm_package_config_eslint","format:check-fix:prettier":"run-e format:check:prettier format:fix:prettier","format:check:prettier":"cross-env-shell prettier --check $npm_package_config_prettier","format:fix:prettier":"cross-env-shell prettier --write $npm_package_config_prettier","test:dev":"run-s build test:dev:*","test:ci":"run-s build test:ci:*","test:dev:vitest":"LOG=0 vitest --exclude '**/integration/**'","test:ci:vitest":"LOG=0 c8 -r lcovonly -r text -r json vitest --exclude '**/integration/**'","test:integration":"vitest run test/integration/","test:integration:codex":"vitest run test/integration/codex.test.ts","test:integration:claude":"vitest run test/integration/claude.test.ts","test:integration:gemini":"vitest run test/integration/gemini.test.ts","test:integration:create-stage":"vitest run test/integration/create.test.ts","test:integration:skill-invocation":"vitest run test/integration/skill-invocation.test.ts","check:types":"tsc --noEmit",postinstall:"node scripts/postinstall.js"},config:{eslint:'--cache --format=codeframe --max-warnings=0 "{src,scripts,test,.github}/**/*.{js,ts,md,html}"',prettier:'--ignore-path .gitignore --loglevel=warn "{src,scripts,test,.github}/**/*.{js,ts,md,yml,json,html}" "*.{js,ts,yml,json,html}" ".*.{js,ts,yml,json,html}" "!**/package-lock.json" "!package-lock.json" "!src/skills/**/*.md"'},keywords:[],license:"MIT",repository:"netlify/agent-runner-cli",bugs:{url:"https://github.com/netlify/agent-runner-cli/issues"},author:"Netlify Inc.",directories:{test:"test"},devDependencies:{"@commitlint/cli":"^20.0.0","@commitlint/config-conventional":"^20.0.0","@eslint/compat":"^2.0.0","@eslint/js":"^9.35.0","@netlify/eslint-config-node":"^7.0.1","@types/node":"^24.5.0","@typescript-eslint/eslint-plugin":"^8.0.0","@typescript-eslint/parser":"^8.0.0","@vitest/eslint-plugin":"^1.6.6",c8:"^10.0.0","eslint-config-prettier":"^10.1.8","eslint-plugin-n":"^17.0.0",husky:"^9.0.0","patch-package":"^8.0.0",tsup:"^8.5.0",typescript:"^5.0.0","typescript-eslint":"^8.44.0",vitest:"^4.0.16"},dependencies:{"@anthropic-ai/claude-code":"2.1.63","@anthropic-ai/sdk":"0.78.0","@google/gemini-cli":"0.31.0","@netlify/otel":"^5.1.2","@openai/codex":"0.110.0","@opentelemetry/exporter-trace-otlp-grpc":"0.57.2",execa:"^9.6.1",kaddidlehopper:"^0.7.2",minimist:"^1.2.8",openai:"6.26.0"}};var rn=Qr(import.meta.url),nn=re.dirname(rn),on=en(import.meta.url),_e=x("shell"),at=new Set,sn={preferLocal:!0},N=(e,t,r)=>{let[i,n]=an(t,r),o={...sn,...n},s=tn(e,i,o);ln(s,o),un(s);let a=r?.idleTimeout;return a&&a>0&&cn(s,a),s};var an=function(e,t){return Array.isArray(e)?[e,t]:typeof e=="object"&&e!==null?[[],e]:[[],void 0]},ln=(e,t)=>{if(t.stdio!==void 0||t.stdout!==void 0||t.stderr!==void 0)return;if(Z.env.NETLIFY_MASK_LOGS!=="false"){e.all?.pipe(new we).pipe(Z.stdout),e.stdout?.pipe(new we).pipe(Z.stdout),e.stderr?.pipe(new we).pipe(Z.stderr);return}e.stdout?.pipe(Z.stdout),e.stderr?.pipe(Z.stderr)},lt=(e,t="SIGTERM")=>{try{return e.pid&&!e.killed?(Z.kill(-e.pid,t),_e.log(`Killed process ${e.pid} with signal ${t}`),!0):!1}catch(r){return _e.error("Error killing process:",r),!1}},Kt=e=>lt(e,"SIGKILL"),cn=(e,t)=>{let r=null,i=()=>{_e.log(`Process ${e.pid} killed due to idle timeout (no output for ${t}ms)`),lt(e,"SIGTERM"),setTimeout(()=>{e.pid&&!e.killed&&(_e.log(`Force killing idle process ${e.pid}`),Kt(e))},5e3)},n=()=>{r&&clearTimeout(r),r=setTimeout(i,t)};n(),e.stdout?.on("data",n),e.stderr?.on("data",n);let o=()=>{r&&(clearTimeout(r),r=null)};e.on("exit",o),e.on("error",o)},un=e=>{at.add(e);let t=qt();if(t){let r=t.onTimesUp(()=>{_e.log(`Global timer expired, killing process ${e.pid}`),lt(e,"SIGTERM"),setTimeout(()=>{e.pid&&!e.killed&&(_e.log(`Force killing process ${e.pid} after timeout`),Kt(e))},5e3)});e.on("exit",()=>{at.delete(e),r()}),e.on("error",()=>{at.delete(e),r()})}};function ae(e,t){if(!Z.env.NETLIFY_LOCAL_MODE)try{let n=on.resolve(Ht.name),o=re.dirname(n);for(;o!==re.dirname(o);){let s=re.dirname(o);if(re.basename(s)==="node_modules"){let a=re.join(s,".bin",t);if(je.existsSync(a))return a;break}o=s}}catch(n){console.error("Could not resolve package.json",n)}if(Z.env.NODE_PATH){let n=re.join(Z.env.NODE_PATH,".bin",t);if(je.existsSync(n))return n}let r=re.join(e,"node_modules",".bin",t);if(je.existsSync(r))return r;let i=re.join(nn,"..","node_modules",".bin",t);if(je.existsSync(i))return i}var dn=x("utils"),pn=e=>new Promise(t=>{setTimeout(t,e)}),Ye=(e,t=3e3)=>{let r=!1,i=null,n=[],o=null,s=(...a)=>{if(r)return i=a,new Promise(d=>{n.push(d)});r=!0;let c,l=new Promise(d=>{c=d});return o=(async()=>{await Promise.resolve();let d=await e(...a);for(c(d);;){if(await pn(t),!i)return r=!1,o=null,d;let m=i,u=n;i=null,n=[],d=await e(...m),u.forEach(w=>{w(d)})}})(),l};return s.flush=async()=>{if((r||i)&&o)return await o,s.flush()},s},Ee=(e,t,r=!1)=>{let i=null,n=null,o=null,s=function(...a){n=a,o=this;let c=r&&!i;clearTimeout(i),i=setTimeout(()=>{i=null,r||(e.apply(o,n),n=null,o=null)},t),c&&(e.apply(o,n),n=null,o=null)};return s.cancel=()=>{clearTimeout(i),i=null,n=null,o=null},s.flush=()=>{if(i){clearTimeout(i);let a=n,c=o;i=null,n=null,o=null,e.apply(c,a)}},s},Jt=(e,t=!0,r)=>{if(e)try{return JSON.parse(e)}catch(i){t&&(r?.error?r.error("Could not parse JSON",i):dn.error("Could not parse JSON",i))}},ct=e=>e.charAt(0).toUpperCase()+e.slice(1),le=e=>e.split("-").map(t=>t.length===2?t.toUpperCase():ct(t)).join(" ");function pe(e,t){t&&e.log(`Skill invoked: ${t}`)}var Vt=e=>Object.fromEntries(Object.entries(e).filter(([,t])=>t!==void 0)),zt=(e,t)=>{let n=".netlify.app",o="agent-";if(!t)return`${o}${e.slice(0,6)}`;let a=`--${t}${n}`;if(a.length>55)return"";let c=60-a.length;if(c<=0)return"";if(c>=o.length+6){let l=Math.min(c-o.length,e.length);return`${o}${e.slice(0,l)}`}return e.slice(0,c)};var mn=50*1024,ut=(e,t=mn)=>{if(!e||typeof e!="string"||e.length<=t)return e;let i=e.startsWith("```")?"\n... [truncated]\n```":"... [truncated]";return e.slice(0,t)+i};import{Buffer as Xt}from"buffer";import gn from"path";var Zt=x("repo"),er=async({config:e,isRetry:t,cwd:r=process.cwd()})=>{Zt.info("Getting runner diffs");let i=await hn(r),{hasChanges:n}=i,{status:o}=i;if(!n)return{hasChanges:!1};if(!t){let _=wn(o);await _n(_,r)}Zt.info("Changes after processing"),await pt(r);let s=await gt(o,r);if(await dt(s,r),n=await yn(r),!n)return{hasChanges:!1,ignored:s};process.env.NETLIFY_INTERNAL_GIT="1";try{await N("git",["commit","-m","Agent runner"],{cwd:r})}finally{process.env.NETLIFY_INTERNAL_GIT="0"}let a={stdio:["ignore","pipe","pipe"],cwd:r},c=await N("git",["diff",e.runSha,"HEAD"],a),l=String(c.stdout??"");if(n=!!l,!n)return await Qt(r),{hasChanges:!1,ignored:s};let d=await N("git",["diff",e.runSha,"HEAD","--binary"],a),m=String(d.stdout??""),u,w;if(e.sha){let _=await N("git",["diff",e.sha,"HEAD"],a);u=String(_.stdout??"");let I=await N("git",["diff",e.sha,"HEAD","--binary"],a),h=String(I.stdout??"");u!==h&&(w=Xt.from(h).toString("base64"))}await Qt(r);let E={hasChanges:!0,diff:l,resultDiff:u,ignored:s};return l!==m&&(E.diffBinary=Xt.from(m).toString("base64")),w&&(E.resultDiffBinary=w),E},Qt=async(e=process.cwd())=>{process.env.NETLIFY_LOCAL_MODE&&await N("git",["reset","--soft","HEAD~1"],{cwd:e})},dt=async(e=[],t=process.cwd())=>{process.env.NETLIFY_INTERNAL_GIT="1";try{await N("git",["add",".",...e],{cwd:t})}finally{process.env.NETLIFY_INTERNAL_GIT="0"}},pt=async(e=process.cwd())=>{let t=await N("git",["status","-s"],{cwd:e});return String(t.stdout??"")},tr=/.. (.+)?\.log$/,fn=[tr],hn=async(e=process.cwd())=>{let t=await pt(e);return{hasChanges:(t.trim().length===0?[]:t.split(`
8
8
  `).filter(n=>fn.some(s=>s instanceof RegExp?s.test(n):n===s)?!1:n[1]?.trim()!=="")).length!==0,status:t}},yn=async(e=process.cwd())=>{try{return await N("git",["diff","--staged","--quiet"],{cwd:e}),!1}catch{return!0}},mt=async(e=process.cwd())=>{let{stdout:t}=await N("git",["rev-parse","HEAD"],{cwd:e});return String(t??"").trim()},rr=async(e=process.cwd())=>{let{stdout:t}=await N("git",["rev-list","--max-parents=0","HEAD"],{cwd:e});return String(t??"").trim()},gt=async(e,t=process.cwd())=>{e||=await pt(t);let r=[".netlify","node_modules","dist",".next","out",".nuxt",".output",".cache",".turbo",".parcel-cache","coverage",".nyc_output","storybook-static","public/build","CLAUDE.local.md"],i=[];return e.split(`
9
9
  `).forEach(n=>{r.forEach(s=>{let a=n===`?? ${s}`,c=n.startsWith(`?? ${s}/`)||n.startsWith(`?? ${s}${gn.sep}`);(a||c)&&i.push(`:!${s}`)});let o=n.match(tr)?.[1];o&&i.push(`:!${o}.log`)}),i},ft=async(e=process.cwd())=>{await N("git",["reset","--hard","HEAD"],{cwd:e})},wn=e=>{let t=e.split(`
10
10
  `).reduce((r,i)=>{if(!i)return r;let[n,o,,...s]=i,a=s.join(""),c=n.trim(),l=o.trim();return r[a]?r[a].change=l:r[a]={filePath:a,stage:c,change:l},r},{});return Object.values(t)},_n=async(e,t=process.cwd())=>{let r=e.filter(i=>i.stage&&!i.change).map(i=>i.filePath);r.length!==0&&await N("git",["restore","--staged","--worktree","--pathspec-from-file=-"],{cwd:t,input:r.join(`
@@ -49,7 +49,9 @@ form.addEventListener('submit', async (e) => {
49
49
  })
50
50
  ```
51
51
 
52
- > **SSR apps (Next.js, TanStack Start, SvelteKit, Remix, Nuxt):** POST to `/__forms.html` instead of `/`. SSR frameworks intercept POSTs to `/`, silently preventing Netlify from processing the submission. See [JavaScript Frameworks](#javascript-frameworks-ssr--client-rendered-apps) below.
52
+ > **SSR/SPA apps (React, Vue, TanStack Start, Next.js, SvelteKit, Remix, Nuxt):** You MUST create a static HTML skeleton
53
+ > file for build-time form detection. Without it, Netlify cannot detect the form during build and submissions will
54
+ > silently fail. See [JavaScript Frameworks](#javascript-frameworks-ssr--client-rendered-apps) below.
53
55
 
54
56
  **Critical AJAX requirements:**
55
57
 
@@ -58,34 +60,43 @@ form.addEventListener('submit', async (e) => {
58
60
  ```html
59
61
  <input type="hidden" name="form-name" value="contact" />
60
62
  ```
61
- 3. For SSR/SPA apps, POST to `/__forms.html` instead of `/` (see JavaScript Frameworks section)
62
63
 
63
64
  ## JavaScript Frameworks (SSR & Client-Rendered Apps)
64
65
 
65
- Netlify's build bot cannot detect forms rendered client-side. For any SSR or SPA framework (React, Vue, TanStack Start, Next.js, SvelteKit, Remix, Nuxt), you MUST:
66
+ Netlify's build bot cannot detect forms rendered client-side. For any SSR or SPA framework (React, Vue, TanStack Start,
67
+ Next.js, SvelteKit, Remix, Nuxt), you MUST:
66
68
 
67
- 1. Create `public/__forms.html` containing a static HTML skeleton of each form for build-time detection
68
- 2. Submit via AJAX with `e.preventDefault()` full-page POST will not work
69
- 3. POST to `/__forms.html` (not `/`) — SSR frameworks intercept POSTs to `/`, silently preventing Netlify from processing submissions
69
+ 1. **Create a static HTML skeleton** in `public/` for build-time form detection — this is the critical step. Without it,
70
+ Netlify never registers the form and submissions will silently fail or 404.
71
+ 2. Submit via AJAX with `e.preventDefault()`full-page POST will not work in SPAs
72
+ 3. Include a hidden `form-name` field matching the form's `name` attribute
70
73
 
71
74
  ### Static Form Skeleton
72
75
 
73
- Create `public/__forms.html`:
76
+ Create a static HTML file (e.g. `public/__forms.html`) containing every form your app uses. The file name does not
77
+ matter — Netlify scans all HTML files in the build output:
74
78
 
75
79
  ```html
76
80
  <!-- public/__forms.html — only for Netlify's build bot detection -->
77
81
  <html>
78
82
  <body>
79
- <form name="contact" data-netlify="true" hidden>
83
+ <form name="contact" data-netlify="true" netlify-honeypot="bot-field" hidden>
80
84
  <input type="hidden" name="form-name" value="contact" />
81
85
  <input name="name" />
82
86
  <input name="email" />
83
87
  <textarea name="message"></textarea>
88
+ <input name="bot-field" />
84
89
  </form>
85
90
  </body>
86
91
  </html>
87
92
  ```
88
93
 
94
+ **Rules:**
95
+
96
+ - The form `name` attribute must exactly match the `form-name` value in your React/Vue component
97
+ - Include every field your component submits — Netlify validates field names against the registered form
98
+ - Add `netlify-honeypot="bot-field"` and a `bot-field` input for spam protection
99
+
89
100
  ### React Example
90
101
 
91
102
  ```jsx
@@ -94,7 +105,7 @@ function ContactForm() {
94
105
  e.preventDefault()
95
106
  const formData = new FormData(e.target)
96
107
 
97
- await fetch('/__forms.html', {
108
+ await fetch('/', {
98
109
  method: 'POST',
99
110
  headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
100
111
  body: new URLSearchParams(formData).toString(),
@@ -102,9 +113,14 @@ function ContactForm() {
102
113
  }
103
114
 
104
115
  return (
105
- <form name="contact" method="POST" onSubmit={handleSubmit}>
116
+ <form name="contact" method="POST" data-netlify="true" netlify-honeypot="bot-field" onSubmit={handleSubmit}>
106
117
  {/* Hidden field required for AJAX */}
107
118
  <input type="hidden" name="form-name" value="contact" />
119
+ <p style={{ display: 'none' }}>
120
+ <label>
121
+ Don't fill this out: <input name="bot-field" />
122
+ </label>
123
+ </p>
108
124
  <input name="name" required />
109
125
  <input name="email" type="email" required />
110
126
  <textarea name="message" />
@@ -203,7 +219,8 @@ The `action` path must:
203
219
  - Be relative to site root
204
220
  - Point to an existing page
205
221
 
206
- > **Pretty URLs:** Netlify serves `thank-you.html` at `/thank-you` by default. Use `action="/thank-you"`, not `action="/thank-you.html"` — the `.html` path returns 404.
222
+ > **Pretty URLs:** Netlify serves `thank-you.html` at `/thank-you` by default. Use `action="/thank-you"`, not
223
+ > `action="/thank-you.html"` — the `.html` path returns 404.
207
224
 
208
225
  ## Notifications
209
226
 
@@ -293,17 +310,13 @@ export default async (req: Request, context: Context) => {
293
310
 
294
311
  ### Form succeeds but no submissions appear (SSR apps)
295
312
 
296
- **Cause:** POST to `/` is intercepted by the SSR handler (Next.js, TanStack Start, SvelteKit, Remix, Nuxt). The SSR handler returns 200, so `fetch()` reports success, but Netlify never processes the submission.
313
+ **Cause:** The form was not detected during build. Without a static HTML skeleton, Netlify doesn't register the form, so
314
+ submissions are passed through to the SSR handler (Next.js, TanStack Start, SvelteKit, Remix, Nuxt). The SSR handler
315
+ returns 200, so `fetch()` reports success, but Netlify never processes the submission.
297
316
 
298
- **Fix:** POST to `/__forms.html` instead of `/`:
299
-
300
- ```javascript
301
- await fetch('/__forms.html', {
302
- method: 'POST',
303
- headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
304
- body: new URLSearchParams(formData).toString(),
305
- })
306
- ```
317
+ **Fix:** Create a static HTML skeleton in `public/` (see
318
+ [JavaScript Frameworks](#javascript-frameworks-ssr--client-rendered-apps) section). Once the form is properly registered
319
+ at build time, Netlify intercepts form POSTs at the CDN level before they reach the SSR handler.
307
320
 
308
321
  ### File upload fails
309
322
 
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@netlify/agent-runner-cli",
3
3
  "type": "module",
4
- "version": "1.90.2",
4
+ "version": "1.90.3",
5
5
  "description": "CLI tool for running Netlify agents",
6
6
  "main": "./dist/index.js",
7
7
  "types": "./dist/index.d.ts",