@netlify/agent-runner-cli 1.94.0-netlifydb.3 → 1.94.0-netlifydb.4
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 +8 -8
- package/dist/bin.js +2 -2
- package/dist/index.js +2 -2
- package/package.json +1 -1
package/dist/bin-local.js
CHANGED
|
@@ -5,7 +5,7 @@ import G from"process";import en from"path";import tn from"fs";import Mi 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 ri}from"child_process";import Pr from"fs/promises";import ni from"path";import _e from"process";import{getTracer as ii}from"@netlify/otel";import Oe from"process";var ne=class extends Error{constructor(r,i,n,o=!1){super(r);this.statusCode=i;this.userMessage=n;this.isCreditLimitExceeded=o;this.name="GracefulShutdownError"}},We=e=>e instanceof ne;var Ke=Oe.env.NETLIFY_API_URL,Ve=Oe.env.NETLIFY_API_TOKEN,W=I("api"),xe=()=>Oe.env.NETLIFY_LOCAL_MODE==="true",ue=async(e,t={})=>{if(!Ke||!Ve)throw new Error("No API URL or token");let r=new URL(e,Ke),i={...t,headers:{...t.headers,Authorization:`Bearer ${Ve}`}};Oe.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(Oe.env.AGENT_RUNNERS_DEBUG==="true")W.log(`Response headers for ${r}:`),n.headers.forEach((a,l)=>{W.log(` ${l}: ${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 ne(`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 ne(`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},Kt=e=>{W.log("Setting details for api",{apiUrl:e?.constants?.NETLIFY_API_HOST,token:!!e?.constants?.NETLIFY_API_TOKEN}),e?.constants?.NETLIFY_API_HOST&&(Ke=`https://${e.constants.NETLIFY_API_HOST}`),e?.constants?.NETLIFY_API_TOKEN&&(Ve=e.constants.NETLIFY_API_TOKEN)},Vt=()=>({apiUrl:Ke,token:Ve}),Fe=async(e,t)=>xe()?(W.log("Mock API: updateRunner called",{runnerId:e,data:t}),{id:e,...t}):ue(`/api/v1/agent_runners/${e}`,{method:"PUT",json:t}),K=async(e,t,r)=>xe()?(W.log("Mock API: updateRunnerSession called",JSON.stringify({runnerId:e,sessionId:t,data:r},null,2)),{id:e,sessionId:t,...r}):ue(`/api/v1/agent_runners/${e}/sessions/${t}`,{method:"PUT",json:r});var Jt=async e=>xe()?(W.log("Mock API: getSite called",{siteId:e}),{id:e,published_deploy:{id:"id"}}):ue(`/api/v1/sites/${e}`),zt=async(e,t)=>xe()?(W.log("Mock API: getRunnerSession called",{runnerId:e,sessionId:t}),{id:t,runnerId:e,state:"running"}):ue(`/api/v1/agent_runners/${e}/sessions/${t}`),Xt=(e,t,r)=>ue(`/api/v1/accounts/${e}/ai-gateway/token`,{headers:{"X-Nf-Agent-Runner-Id":t,"X-Nf-Agent-Runner-Session-Id":r},gracefulOn503:!0}),Zt=(e,t,r)=>ue(`/api/v1/sites/${e}/ai-gateway/token`,{headers:{"X-Nf-Agent-Runner-Id":t,"X-Nf-Agent-Runner-Session-Id":r},gracefulOn503:!0}),Qt=async(e,t)=>xe()?(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"}}):ue(`/api/v1/agent_runners/${e}/sessions/${t}/diff/upload_urls`,{method:"POST"}),er=async(e,t)=>xe()?(W.log("Mock API: updateSessionUsage called",{runnerId:e,sessionId:t}),{id:t,runnerId:e,usage:0}):ue(`/api/v1/agent_runners/${e}/sessions/${t}/update_usage`,{method:"POST"}),mt=async(e,t,{maxRetries:r=3,baseDelayMs:i=500}={})=>{W.log(`Uploading diff to S3: ${e.substring(0,50)}...`);for(let n=1;n<=r;n++)try{let o=await fetch(e,{method:"PUT",body:t,headers:{"Content-Type":"text/plain"}});if(!o.ok)throw new Error(`S3 upload failed with status ${o.status}`);return o}catch(o){if(n===r)throw o;let s=i*2**(n-1);W.warn(`S3 upload attempt ${n}/${r} failed: ${o.message}. Retrying in ${s}ms...`),await new Promise(a=>setTimeout(a,s))}};var Ie=I("ai_gateway"),gt=null;var Je=async()=>{if(gt)return gt;Ie.log("Fetching available AI gateway providers");let e=await fetch(`${Vt().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 gt=t,Ie.log("Cached AI gateway providers",{providerCount:Object.keys(t.providers).length}),t},un=async(e,t)=>{let i=(await Je()).providers[e];if(!i)return Ie.log(`Provider '${e}' not found`),!1;let n=i.models.includes(t);return Ie.log(`Model validation for ${e}/${t}`,{isAvailable:n}),n},tr=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),Ie.log("Requesting AI gateway information");let c=await(o?Xt(e.accountId,e.id,e.sessionId):Zt(e.siteId,e.id,e.sessionId));if({token:t,url:n}=c,r=c.expires_at?c.expires_at*1e3:void 0,Ie.log("Got AI gateway information",{token:!!t,expiresAt:r,url:n}),r){let u=r-Date.now()-6e4;u>0&&(i=setTimeout(()=>{a()},u))}};return await Promise.all([a(),Je()]),{get url(){return n},get token(){return t},isModelAvailableForProvider:un}};import oe from"process";import ae from"path";import Ze from"fs";import{fileURLToPath as wn}from"url";import{createRequire as _n}from"module";import{execa as En,execaCommand as wo}from"execa";import{Transform as dn}from"stream";function pn(){let e=process.env.NETLIFY_SENSITIVE_ENV_KEYS;return e?e.split(",").map(t=>t.trim()).filter(Boolean):[]}function mn(e){let t=e.toLowerCase();return t==="true"||t==="false"?!0:e.trim().length<4}function gn(){let t=pn().map(r=>process.env[r]).filter(r=>!(!r||mn(r)));return[...new Set(t)].sort((r,i)=>i.length-r.length)}function se(e){if(typeof e!="string")return e;let t=gn();if(t.length===0)return e;let r=e;return t.forEach(i=>{let n=new RegExp(fn(i),"g");r=r.replace(n,"******")}),r}function fn(e){return e.replace(/[.*+?^${}()|[\]\\]/g,"\\$&")}var Se=class extends dn{constructor(t={}){super({...t,objectMode:!1})}_transform(t,r,i){let n=t.toString(),o=se(n);i(null,o)}};function rr(){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"?se(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"?se(i):i;return typeof n=="function"?r(s,n):r(s,n,o)}}var Le=null,nr=e=>(Le&&Le.destroy(),Le=new de({totalAllowedTime:e}),Le),ir=()=>Le;var de=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,l)=>{o=setTimeout(()=>{l(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 or="netlify-agent-runner-context.md",ft="task-history",ie=".netlify",fe="results.md",ht="assets";var sr="free";var he=1800*1e3,hn=["\\.claude","\\.gemini","\\.codex","\\.agents"],ze=new RegExp(`(^|/)(${hn.join("|")})/skills/`),Xe=/(^|\/)((CLAUDE|AGENTS|GEMINI)(\.local)?\.md)$/,f={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 ar={name:"@netlify/agent-runner-cli",type:"module",version:"1.94.0-netlifydb.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.81","@anthropic-ai/sdk":"0.78.0","@google/gemini-cli":"0.31.0","@netlify/otel":"^5.1.5","@netlify/ts-cli":"^1.0.4","@openai/codex":"0.115.0","@opentelemetry/exporter-trace-otlp-grpc":"0.57.2",execa:"^9.6.1",minimist:"^1.2.8",openai:"6.26.0"}};var Tn=wn(import.meta.url),xn=ae.dirname(Tn),In=_n(import.meta.url),ve=I("shell"),yt=new Set,Sn={preferLocal:!0},N=(e,t,r)=>{let[i,n]=vn(t,r),o={...Sn,...n},s=En(e,i,o);bn(s,o),An(s);let a=r?.idleTimeout;return a&&a>0&&Rn(s,a),s};var vn=function(e,t){return Array.isArray(e)?[e,t]:typeof e=="object"&&e!==null?[[],e]:[[],void 0]},bn=(e,t)=>{if(t.stdio!==void 0||t.stdout!==void 0||t.stderr!==void 0)return;if(oe.env.NETLIFY_MASK_LOGS!=="false"){e.all?.pipe(new Se).pipe(oe.stdout),e.stdout?.pipe(new Se).pipe(oe.stdout),e.stderr?.pipe(new Se).pipe(oe.stderr);return}e.stdout?.pipe(oe.stdout),e.stderr?.pipe(oe.stderr)},wt=(e,t="SIGTERM")=>{try{return e.pid&&!e.killed?(oe.kill(-e.pid,t),ve.log(`Killed process ${e.pid} with signal ${t}`),!0):!1}catch(r){return ve.error("Error killing process:",r),!1}},lr=e=>wt(e,"SIGKILL"),Rn=(e,t)=>{let r=null,i=()=>{ve.log(`Process ${e.pid} killed due to idle timeout (no output for ${t}ms)`),wt(e,"SIGTERM"),setTimeout(()=>{e.pid&&!e.killed&&(ve.log(`Force killing idle process ${e.pid}`),lr(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)},An=e=>{yt.add(e);let t=ir();if(t){let r=t.onTimesUp(()=>{ve.log(`Global timer expired, killing process ${e.pid}`),wt(e,"SIGTERM"),setTimeout(()=>{e.pid&&!e.killed&&(ve.log(`Force killing process ${e.pid} after timeout`),lr(e))},5e3)});e.on("exit",()=>{yt.delete(e),r()}),e.on("error",()=>{yt.delete(e),r()})}};function Qe(e,t){return!!le(e,t)}function le(e,t){if(!oe.env.NETLIFY_LOCAL_MODE)try{let n=In.resolve(ar.name),o=ae.dirname(n);for(;o!==ae.dirname(o);){let s=ae.dirname(o);if(ae.basename(s)==="node_modules"){let a=ae.join(s,".bin",t);if(Ze.existsSync(a))return a;break}o=s}}catch(n){console.error("Could not resolve package.json",n)}if(oe.env.NODE_PATH){let n=ae.join(oe.env.NODE_PATH,".bin",t);if(Ze.existsSync(n))return n}let r=ae.join(e,"node_modules",".bin",t);if(Ze.existsSync(r))return r;let i=ae.join(xn,"..","node_modules",".bin",t);if(Ze.existsSync(i))return i}var Cn=I("utils"),kn=e=>new Promise(t=>{setTimeout(t,e)}),et=(e,t=3e3)=>{let r=!1,i=null,n=[],o=null,s=(...a)=>{if(r)return i=a,new Promise(u=>{n.push(u)});r=!0;let l,c=new Promise(u=>{l=u});return o=(async()=>{await Promise.resolve();let u=await e(...a);for(l(u);;){if(await kn(t),!i)return r=!1,o=null,u;let d=i,p=n;i=null,n=[],u=await e(...d),p.forEach(g=>{g(u)})}})(),c};return s.flush=async()=>{if((r||i)&&o)return await o,s.flush()},s},be=(e,t,r=!1)=>{let i=null,n=null,o=null,s=function(...a){n=a,o=this;let l=r&&!i;clearTimeout(i),i=setTimeout(()=>{i=null,r||(e.apply(o,n),n=null,o=null)},t),l&&(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,l=o;i=null,n=null,o=null,e.apply(l,a)}},s},cr=(e,t=!0,r)=>{if(e)try{return JSON.parse(e)}catch(i){t&&(r?.error?r.error("Could not parse JSON",i):Cn.error("Could not parse JSON",i))}},_t=e=>e.charAt(0).toUpperCase()+e.slice(1),pe=e=>e.split("-").map(t=>t.length===2?t.toUpperCase():_t(t)).join(" ");function ye(e,t){t&&e.log(`Skill invoked: ${t}`)}var ur=e=>Object.fromEntries(Object.entries(e).filter(([,t])=>t!==void 0)),dr=(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 l=60-a.length;if(l<=0)return"";if(l>=o.length+6){let c=Math.min(l-o.length,e.length);return`${o}${e.slice(0,c)}`}return e.slice(0,l)};var Pn=1e4,Et=(e,t=Pn)=>{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 pr}from"buffer";import Nn from"path";var mr=I("repo"),fr=async({config:e,isRetry:t,cwd:r=process.cwd()})=>{mr.info("Getting runner diffs");let i=await On(r),{hasChanges:n}=i,{status:o}=i;if(!n)return{hasChanges:!1};if(!t){let _=Ln(o);await Dn(_,r)}mr.info("Changes after processing"),await xt(r);let s=await It(o,r);if(await Tt(s,r),n=await Fn(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},l=await N("git",["diff",e.runSha,"HEAD"],a),c=String(l.stdout??"");if(n=!!c,!n)return await gr(r),{hasChanges:!1,ignored:s};let u=await N("git",["diff",e.runSha,"HEAD","--binary"],a),d=String(u.stdout??""),p,g;if(e.sha){let _=await N("git",["diff",e.sha,"HEAD"],a);p=String(_.stdout??"");let x=await N("git",["diff",e.sha,"HEAD","--binary"],a),w=String(x.stdout??"");p!==w&&(g=pr.from(w).toString("base64"))}await gr(r);let T={hasChanges:!0,diff:c,resultDiff:p,ignored:s};return c!==d&&(T.diffBinary=pr.from(d).toString("base64")),g&&(T.resultDiffBinary=g),T},gr=async(e=process.cwd())=>{process.env.NETLIFY_LOCAL_MODE&&await N("git",["reset","--soft","HEAD~1"],{cwd:e})},Tt=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"}},xt=async(e=process.cwd())=>{let t=await N("git",["status","-s"],{cwd:e});return String(t.stdout??"")},hr=/.. (.+)?\.log$/,$n=[hr],On=async(e=process.cwd())=>{let t=await xt(e);return{hasChanges:(t.trim().length===0?[]:t.split(`
|
|
8
|
+
`);return o.length>e.length*.8?e:o}import{execSync as ri}from"child_process";import Pr from"fs/promises";import ni from"path";import _e from"process";import{getTracer as ii}from"@netlify/otel";import Oe from"process";var ne=class extends Error{constructor(r,i,n,o=!1){super(r);this.statusCode=i;this.userMessage=n;this.isCreditLimitExceeded=o;this.name="GracefulShutdownError"}},We=e=>e instanceof ne;var Ke=Oe.env.NETLIFY_API_URL,Ve=Oe.env.NETLIFY_API_TOKEN,W=I("api"),xe=()=>Oe.env.NETLIFY_LOCAL_MODE==="true",ue=async(e,t={})=>{if(!Ke||!Ve)throw new Error("No API URL or token");let r=new URL(e,Ke),i={...t,headers:{...t.headers,Authorization:`Bearer ${Ve}`}};Oe.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(Oe.env.AGENT_RUNNERS_DEBUG==="true")W.log(`Response headers for ${r}:`),n.headers.forEach((a,l)=>{W.log(` ${l}: ${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 ne(`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 ne(`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},Kt=e=>{W.log("Setting details for api",{apiUrl:e?.constants?.NETLIFY_API_HOST,token:!!e?.constants?.NETLIFY_API_TOKEN}),e?.constants?.NETLIFY_API_HOST&&(Ke=`https://${e.constants.NETLIFY_API_HOST}`),e?.constants?.NETLIFY_API_TOKEN&&(Ve=e.constants.NETLIFY_API_TOKEN)},Vt=()=>({apiUrl:Ke,token:Ve}),Fe=async(e,t)=>xe()?(W.log("Mock API: updateRunner called",{runnerId:e,data:t}),{id:e,...t}):ue(`/api/v1/agent_runners/${e}`,{method:"PUT",json:t}),K=async(e,t,r)=>xe()?(W.log("Mock API: updateRunnerSession called",JSON.stringify({runnerId:e,sessionId:t,data:r},null,2)),{id:e,sessionId:t,...r}):ue(`/api/v1/agent_runners/${e}/sessions/${t}`,{method:"PUT",json:r});var Jt=async e=>xe()?(W.log("Mock API: getSite called",{siteId:e}),{id:e,published_deploy:{id:"id"}}):ue(`/api/v1/sites/${e}`),zt=async(e,t)=>xe()?(W.log("Mock API: getRunnerSession called",{runnerId:e,sessionId:t}),{id:t,runnerId:e,state:"running"}):ue(`/api/v1/agent_runners/${e}/sessions/${t}`),Xt=(e,t,r)=>ue(`/api/v1/accounts/${e}/ai-gateway/token`,{headers:{"X-Nf-Agent-Runner-Id":t,"X-Nf-Agent-Runner-Session-Id":r},gracefulOn503:!0}),Zt=(e,t,r)=>ue(`/api/v1/sites/${e}/ai-gateway/token`,{headers:{"X-Nf-Agent-Runner-Id":t,"X-Nf-Agent-Runner-Session-Id":r},gracefulOn503:!0}),Qt=async(e,t)=>xe()?(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"}}):ue(`/api/v1/agent_runners/${e}/sessions/${t}/diff/upload_urls`,{method:"POST"}),er=async(e,t)=>xe()?(W.log("Mock API: updateSessionUsage called",{runnerId:e,sessionId:t}),{id:t,runnerId:e,usage:0}):ue(`/api/v1/agent_runners/${e}/sessions/${t}/update_usage`,{method:"POST"}),mt=async(e,t,{maxRetries:r=3,baseDelayMs:i=500}={})=>{W.log(`Uploading diff to S3: ${e.substring(0,50)}...`);for(let n=1;n<=r;n++)try{let o=await fetch(e,{method:"PUT",body:t,headers:{"Content-Type":"text/plain"}});if(!o.ok)throw new Error(`S3 upload failed with status ${o.status}`);return o}catch(o){if(n===r)throw o;let s=i*2**(n-1);W.warn(`S3 upload attempt ${n}/${r} failed: ${o.message}. Retrying in ${s}ms...`),await new Promise(a=>setTimeout(a,s))}};var Ie=I("ai_gateway"),gt=null;var Je=async()=>{if(gt)return gt;Ie.log("Fetching available AI gateway providers");let e=await fetch(`${Vt().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 gt=t,Ie.log("Cached AI gateway providers",{providerCount:Object.keys(t.providers).length}),t},un=async(e,t)=>{let i=(await Je()).providers[e];if(!i)return Ie.log(`Provider '${e}' not found`),!1;let n=i.models.includes(t);return Ie.log(`Model validation for ${e}/${t}`,{isAvailable:n}),n},tr=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),Ie.log("Requesting AI gateway information");let c=await(o?Xt(e.accountId,e.id,e.sessionId):Zt(e.siteId,e.id,e.sessionId));if({token:t,url:n}=c,r=c.expires_at?c.expires_at*1e3:void 0,Ie.log("Got AI gateway information",{token:!!t,expiresAt:r,url:n}),r){let u=r-Date.now()-6e4;u>0&&(i=setTimeout(()=>{a()},u))}};return await Promise.all([a(),Je()]),{get url(){return n},get token(){return t},isModelAvailableForProvider:un}};import oe from"process";import ae from"path";import Ze from"fs";import{fileURLToPath as wn}from"url";import{createRequire as _n}from"module";import{execa as En,execaCommand as wo}from"execa";import{Transform as dn}from"stream";function pn(){let e=process.env.NETLIFY_SENSITIVE_ENV_KEYS;return e?e.split(",").map(t=>t.trim()).filter(Boolean):[]}function mn(e){let t=e.toLowerCase();return t==="true"||t==="false"?!0:e.trim().length<4}function gn(){let t=pn().map(r=>process.env[r]).filter(r=>!(!r||mn(r)));return[...new Set(t)].sort((r,i)=>i.length-r.length)}function se(e){if(typeof e!="string")return e;let t=gn();if(t.length===0)return e;let r=e;return t.forEach(i=>{let n=new RegExp(fn(i),"g");r=r.replace(n,"******")}),r}function fn(e){return e.replace(/[.*+?^${}()|[\]\\]/g,"\\$&")}var Se=class extends dn{constructor(t={}){super({...t,objectMode:!1})}_transform(t,r,i){let n=t.toString(),o=se(n);i(null,o)}};function rr(){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"?se(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"?se(i):i;return typeof n=="function"?r(s,n):r(s,n,o)}}var Le=null,nr=e=>(Le&&Le.destroy(),Le=new de({totalAllowedTime:e}),Le),ir=()=>Le;var de=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,l)=>{o=setTimeout(()=>{l(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 or="netlify-agent-runner-context.md",ft="task-history",ie=".netlify",fe="results.md",ht="assets";var sr="free";var he=1800*1e3,hn=["\\.claude","\\.gemini","\\.codex","\\.agents"],ze=new RegExp(`(^|/)(${hn.join("|")})/skills/`),Xe=/(^|\/)((CLAUDE|AGENTS|GEMINI)(\.local)?\.md)$/,f={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 ar={name:"@netlify/agent-runner-cli",type:"module",version:"1.94.0-netlifydb.4",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.81","@anthropic-ai/sdk":"0.78.0","@google/gemini-cli":"0.31.0","@netlify/otel":"^5.1.5","@netlify/ts-cli":"^1.0.4","@openai/codex":"0.115.0","@opentelemetry/exporter-trace-otlp-grpc":"0.57.2",execa:"^9.6.1",minimist:"^1.2.8",openai:"6.26.0"}};var Tn=wn(import.meta.url),xn=ae.dirname(Tn),In=_n(import.meta.url),ve=I("shell"),yt=new Set,Sn={preferLocal:!0},N=(e,t,r)=>{let[i,n]=vn(t,r),o={...Sn,...n},s=En(e,i,o);bn(s,o),An(s);let a=r?.idleTimeout;return a&&a>0&&Rn(s,a),s};var vn=function(e,t){return Array.isArray(e)?[e,t]:typeof e=="object"&&e!==null?[[],e]:[[],void 0]},bn=(e,t)=>{if(t.stdio!==void 0||t.stdout!==void 0||t.stderr!==void 0)return;if(oe.env.NETLIFY_MASK_LOGS!=="false"){e.all?.pipe(new Se).pipe(oe.stdout),e.stdout?.pipe(new Se).pipe(oe.stdout),e.stderr?.pipe(new Se).pipe(oe.stderr);return}e.stdout?.pipe(oe.stdout),e.stderr?.pipe(oe.stderr)},wt=(e,t="SIGTERM")=>{try{return e.pid&&!e.killed?(oe.kill(-e.pid,t),ve.log(`Killed process ${e.pid} with signal ${t}`),!0):!1}catch(r){return ve.error("Error killing process:",r),!1}},lr=e=>wt(e,"SIGKILL"),Rn=(e,t)=>{let r=null,i=()=>{ve.log(`Process ${e.pid} killed due to idle timeout (no output for ${t}ms)`),wt(e,"SIGTERM"),setTimeout(()=>{e.pid&&!e.killed&&(ve.log(`Force killing idle process ${e.pid}`),lr(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)},An=e=>{yt.add(e);let t=ir();if(t){let r=t.onTimesUp(()=>{ve.log(`Global timer expired, killing process ${e.pid}`),wt(e,"SIGTERM"),setTimeout(()=>{e.pid&&!e.killed&&(ve.log(`Force killing process ${e.pid} after timeout`),lr(e))},5e3)});e.on("exit",()=>{yt.delete(e),r()}),e.on("error",()=>{yt.delete(e),r()})}};function Qe(e,t){return!!le(e,t)}function le(e,t){if(!oe.env.NETLIFY_LOCAL_MODE)try{let n=In.resolve(ar.name),o=ae.dirname(n);for(;o!==ae.dirname(o);){let s=ae.dirname(o);if(ae.basename(s)==="node_modules"){let a=ae.join(s,".bin",t);if(Ze.existsSync(a))return a;break}o=s}}catch(n){console.error("Could not resolve package.json",n)}if(oe.env.NODE_PATH){let n=ae.join(oe.env.NODE_PATH,".bin",t);if(Ze.existsSync(n))return n}let r=ae.join(e,"node_modules",".bin",t);if(Ze.existsSync(r))return r;let i=ae.join(xn,"..","node_modules",".bin",t);if(Ze.existsSync(i))return i}var Cn=I("utils"),kn=e=>new Promise(t=>{setTimeout(t,e)}),et=(e,t=3e3)=>{let r=!1,i=null,n=[],o=null,s=(...a)=>{if(r)return i=a,new Promise(u=>{n.push(u)});r=!0;let l,c=new Promise(u=>{l=u});return o=(async()=>{await Promise.resolve();let u=await e(...a);for(l(u);;){if(await kn(t),!i)return r=!1,o=null,u;let d=i,p=n;i=null,n=[],u=await e(...d),p.forEach(g=>{g(u)})}})(),c};return s.flush=async()=>{if((r||i)&&o)return await o,s.flush()},s},be=(e,t,r=!1)=>{let i=null,n=null,o=null,s=function(...a){n=a,o=this;let l=r&&!i;clearTimeout(i),i=setTimeout(()=>{i=null,r||(e.apply(o,n),n=null,o=null)},t),l&&(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,l=o;i=null,n=null,o=null,e.apply(l,a)}},s},cr=(e,t=!0,r)=>{if(e)try{return JSON.parse(e)}catch(i){t&&(r?.error?r.error("Could not parse JSON",i):Cn.error("Could not parse JSON",i))}},_t=e=>e.charAt(0).toUpperCase()+e.slice(1),pe=e=>e.split("-").map(t=>t.length===2?t.toUpperCase():_t(t)).join(" ");function ye(e,t){t&&e.log(`Skill invoked: ${t}`)}var ur=e=>Object.fromEntries(Object.entries(e).filter(([,t])=>t!==void 0)),dr=(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 l=60-a.length;if(l<=0)return"";if(l>=o.length+6){let c=Math.min(l-o.length,e.length);return`${o}${e.slice(0,c)}`}return e.slice(0,l)};var Pn=1e4,Et=(e,t=Pn)=>{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 pr}from"buffer";import Nn from"path";var mr=I("repo"),fr=async({config:e,isRetry:t,cwd:r=process.cwd()})=>{mr.info("Getting runner diffs");let i=await On(r),{hasChanges:n}=i,{status:o}=i;if(!n)return{hasChanges:!1};if(!t){let _=Ln(o);await Dn(_,r)}mr.info("Changes after processing"),await xt(r);let s=await It(o,r);if(await Tt(s,r),n=await Fn(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},l=await N("git",["diff",e.runSha,"HEAD"],a),c=String(l.stdout??"");if(n=!!c,!n)return await gr(r),{hasChanges:!1,ignored:s};let u=await N("git",["diff",e.runSha,"HEAD","--binary"],a),d=String(u.stdout??""),p,g;if(e.sha){let _=await N("git",["diff",e.sha,"HEAD"],a);p=String(_.stdout??"");let x=await N("git",["diff",e.sha,"HEAD","--binary"],a),w=String(x.stdout??"");p!==w&&(g=pr.from(w).toString("base64"))}await gr(r);let T={hasChanges:!0,diff:c,resultDiff:p,ignored:s};return c!==d&&(T.diffBinary=pr.from(d).toString("base64")),g&&(T.resultDiffBinary=g),T},gr=async(e=process.cwd())=>{process.env.NETLIFY_LOCAL_MODE&&await N("git",["reset","--soft","HEAD~1"],{cwd:e})},Tt=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"}},xt=async(e=process.cwd())=>{let t=await N("git",["status","-s"],{cwd:e});return String(t.stdout??"")},hr=/.. (.+)?\.log$/,$n=[hr],On=async(e=process.cwd())=>{let t=await xt(e);return{hasChanges:(t.trim().length===0?[]:t.split(`
|
|
9
9
|
`).filter(n=>$n.some(s=>s instanceof RegExp?s.test(n):n===s)?!1:n[1]?.trim()!=="")).length!==0,status:t}},Fn=async(e=process.cwd())=>{try{return await N("git",["diff","--staged","--quiet"],{cwd:e}),!1}catch{return!0}},De=async(e=process.cwd())=>{let{stdout:t}=await N("git",["rev-parse","HEAD"],{cwd:e});return String(t??"").trim()},yr=async(e=process.cwd())=>{let{stdout:t}=await N("git",["rev-list","--max-parents=0","HEAD"],{cwd:e});return String(t??"").trim()},It=async(e,t=process.cwd())=>{e||=await xt(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}`,l=n.startsWith(`?? ${s}/`)||n.startsWith(`?? ${s}${Nn.sep}`);(a||l)&&i.push(`:!${s}`)});let o=n.match(hr)?.[1];o&&i.push(`:!${o}.log`)}),i},St=async(e=process.cwd())=>{await N("git",["reset","--hard","HEAD"],{cwd:e})},Ln=e=>{let t=e.split(`
|
|
11
11
|
`).reduce((r,i)=>{if(!i)return r;let[n,o,,...s]=i,a=s.join(""),l=n.trim(),c=o.trim();return r[a]?r[a].change=c:r[a]={filePath:a,stage:l,change:c},r},{});return Object.values(t)},Dn=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(`
|
|
@@ -60,12 +60,12 @@ case "$1" in
|
|
|
60
60
|
exec ${e} "$@"
|
|
61
61
|
;;
|
|
62
62
|
esac
|
|
63
|
-
`;try{await Pr.mkdir(t,{recursive:!0}),await Pr.writeFile(r,i,{mode:493}),_e.env.PATH=`${t}:${_e.env.PATH}`,_e.env.NETLIFY_INTERNAL_GIT="0",Ye.info("Installed git wrapper to block add/commit commands")}catch(n){Ye.warn("Failed to install git wrapper",{error:n?.message||n})}},si=({apiToken:e,config:t})=>({constants:{NETLIFY_API_HOST:_e.env.NETLIFY_API_HOST||"api.netlify.com",NETLIFY_API_TOKEN:e||_e.env.NETLIFY_API_TOKEN,SITE_ID:t.siteId,FUNCTIONS_DIST:_e.env.FUNCTIONS_DIST||"netlify/functions"},utils:{run:N}});import{getTracer as Lt}from"@netlify/otel";import ai from"crypto";import V from"fs/promises";import Ot from"os";import C from"path";import Q from"process";import{fileURLToPath as li}from"url";var
|
|
63
|
+
`;try{await Pr.mkdir(t,{recursive:!0}),await Pr.writeFile(r,i,{mode:493}),_e.env.PATH=`${t}:${_e.env.PATH}`,_e.env.NETLIFY_INTERNAL_GIT="0",Ye.info("Installed git wrapper to block add/commit commands")}catch(n){Ye.warn("Failed to install git wrapper",{error:n?.message||n})}},si=({apiToken:e,config:t})=>({constants:{NETLIFY_API_HOST:_e.env.NETLIFY_API_HOST||"api.netlify.com",NETLIFY_API_TOKEN:e||_e.env.NETLIFY_API_TOKEN,SITE_ID:t.siteId,FUNCTIONS_DIST:_e.env.FUNCTIONS_DIST||"netlify/functions"},utils:{run:N}});import{getTracer as Lt}from"@netlify/otel";import ai from"crypto";import V from"fs/promises";import Ot from"os";import C from"path";import Q from"process";import{fileURLToPath as li}from"url";var X=I("context"),ci=li(import.meta.url),ui=C.dirname(ci),di={claude:C.join(Ot.homedir(),".claude","skills"),gemini:C.join(Ot.homedir(),".agents","skills"),codex:C.join(Ot.homedir(),".agents","skills")},$r=C.join(ui,"skills"),Ft=null;var Or="NETLIFY_FF_AGENT_RUNNER_SKILL_",pi=()=>{let e=[];for(let[t,r]of Object.entries(Q.env))t.startsWith(Or)&&r&&e.push(t.slice(Or.length).toLowerCase());return X.log(`Active skill variations: ${e.length?e.join(", "):"none"}`),e},Fr=e=>{let t=e.match(/^(.+)@([^.]+)(.*)$/);return t?{baseName:`${t[1]}${t[3]}`,variation:t[2]}:{baseName:e,variation:null}},mi=async(e,t=[])=>{try{let r=await V.readdir(e);for(let i of r){let{baseName:n,variation:o}=Fr(i);if(o&&n==="SKILL.md"&&t.includes(o))return C.join(e,i)}}catch{}return C.join(e,"SKILL.md")},gi=async(e,{targetDir:t}={})=>{let r=t||di[e];if(!r)return X.warn(`Unknown runner: ${e}, skipping skills setup`),[];if(Ft)return Ft;let i=[],n=pi();try{let o=await V.readdir($r);for(let s of o){let a=C.join($r,s);if(!(await V.stat(a)).isDirectory())continue;let{baseName:c,variation:u}=Fr(s);if(u&&!n.includes(u))continue;let d=C.join(r,c);await V.mkdir(d,{recursive:!0});let p=await mi(a,n),g=C.join(d,"SKILL.md");try{await V.copyFile(p,g),i.push(c),u&&X.log(`Installed skill variation: ${c} (variation: ${u})`),C.basename(p)!=="SKILL.md"&&X.log(`Using skill variation for ${c}: ${C.basename(p)}`)}catch(T){X.warn(`Failed to copy skill ${c}:`,T.message)}}}catch(o){X.warn("Failed to setup agent skills:",o.message)}if(i.includes("netlify-ai-gateway"))try{let o=await Je(),s=C.join(r,"netlify-ai-gateway","SKILL.md"),a=await V.readFile(s,"utf-8");if(a.includes("<!-- AVAILABLE_MODELS -->")){let l=Object.entries(o.providers).map(([c,u])=>`### ${c}
|
|
64
64
|
|
|
65
65
|
${u.models.map(d=>`- \`${d}\``).join(`
|
|
66
66
|
`)}`).join(`
|
|
67
67
|
|
|
68
|
-
`);a=a.replace("<!-- AVAILABLE_MODELS -->",l),await V.writeFile(s,a,"utf-8"),
|
|
68
|
+
`);a=a.replace("<!-- AVAILABLE_MODELS -->",l),await V.writeFile(s,a,"utf-8"),X.log("Injected dynamic model list into AI Gateway skill",{modelList:l})}}catch(o){X.warn("Failed to inject dynamic model list into AI Gateway skill:",o.message)}return i.length>0&&X.log(`Installed ${i.length} skills for ${e}: ${i.join(", ")}`,{runner:e,skills:i,targetDir:r}),Ft=i,i},fi=e=>{let t=e?.constants||{};return{siteId:t.SITE_ID,accountId:Q.env.NETLIFY_TEAM_ID,userId:Q.env.NETLIFY_AGENT_RUNNER_USER_ID,siteSlug:Q.env.SITE_NAME,apiHost:t.NETLIFY_API_HOST,functionsDir:t.FUNCTIONS_DIST}},hi=10,yi=async e=>{let{name:t,ext:r}=C.parse(e),i=e,n=C.join(Q.cwd(),ie,i),o=0;for(;await wi(n);){if(o>=hi)throw new Error("Failed to generate context file");i=`${t}-${ai.randomUUID().slice(0,5)}${r}`,n=C.join(Q.cwd(),ie,i),o+=1}return i},wi=async e=>{try{return await V.access(e),!0}catch{return!1}},Lr=async({cliPath:e,netlify:t,config:r,buildErrorContext:i,additionalContext:n})=>{let o=fi(t),s=await yi(or),a=C.join(Q.cwd(),ie);await V.mkdir(a,{recursive:!0});let l=C.join(ie,s),c=C.join(Q.cwd(),l),u=C.join(Q.cwd(),ie,fe);try{await V.unlink(u),X.log(`Deleted old results file: ${u}`)}catch{}let d=i?`You've already made changes to complete the above request. However, the build is currently failing after your changes.
|
|
69
69
|
Your task is to analyze and fix the build errors.
|
|
70
70
|
Don't apply techniques of reverting changes. Apply fixes related to errors.
|
|
71
71
|
Don't try to run build by yourself. Just fix the errors.
|
|
@@ -88,7 +88,7 @@ ${w.request}
|
|
|
88
88
|
## Response - what the agent replied with after its work
|
|
89
89
|
|
|
90
90
|
${w.response}
|
|
91
|
-
`;return await V.writeFile(P,Y,"utf-8"),
|
|
91
|
+
`;return await V.writeFile(P,Y,"utf-8"),X.log(`Created history file: ${L}`),L}));g+=`
|
|
92
92
|
<session_history_context>
|
|
93
93
|
History of prior work on this task.
|
|
94
94
|
You MUST review ALL of the files below as context to understand the context of previous attempts. Use this information to continue the discussion appropriately.
|
|
@@ -154,7 +154,7 @@ You're an AI agent designed to assist with tasks related to a Netlify project. P
|
|
|
154
154
|
</extra_context>
|
|
155
155
|
|
|
156
156
|
${g}
|
|
157
|
-
`;return await V.writeFile(c,T,"utf-8"),
|
|
157
|
+
`;return await V.writeFile(c,T,"utf-8"),X.log(`Generated agent context document at: ${c}`),T.length>5e5&&(T=`
|
|
158
158
|
You're an AI agent designed to assist with tasks related to a Netlify project. Please review, understand, and use the context provided to complete the user's request as needed.
|
|
159
159
|
|
|
160
160
|
<request>
|
|
@@ -200,7 +200,7 @@ ${JSON.stringify(e,null,2)}`,$i=async({config:e,aiGateway:t,templates:r,prompt:i
|
|
|
200
200
|
|
|
201
201
|
\`\`\`
|
|
202
202
|
${w}
|
|
203
|
-
\`\`\``,U.info("Generated project structure for agent context"))}catch(w){U.warn("Failed to generate project structure",w.message)}let x=performance.now()-n;return i?.setAttributes({"create.framework":p?.framework,"create.template":p?.template,"create.duration.ms":x,"create.status":"success"}),{...p??{template:"",newPrompt:"",packageManager:"",framework:""},additionalContext:_}});var Be=I("usage_tracker"),Oi=4e3,Vr=(e,t,r)=>{let i=!1,n=!1,o=!1,a=et(async()=>{try{let u=await er(e,t);Be.log("Usage update response",{usage:u?.usage}),r!=null&&u?.usage?.total_credits_cost!=null&&u.usage.total_credits_cost>=r&&(Be.log("Credit limit exceeded",{totalCreditsCost:u.usage.total_credits_cost,enforcedCreditsRemaining:r}),o=!0),u?.credit_limit_exceeded&&(Be.log("Credit limit exceeded (flagged by API)"),o=!0)}catch(u){Be.warn("Failed to update usage",{error:u?.message||u})}},Oi);return{onAgentOutput:()=>{if(o)throw new ne("AI credit usage exceeded enforced limit.",503,"Credit limit reached. Check credit limits to continue using Agent Runners.",!0);n||(i=!0,a())},stop:async()=>{n||(n=!0,i&&(Be.log("Sending final usage update"),a(),await a.flush()))}}};var Di=Fi(import.meta.url),Xr=Di("../package.json"),Ee=I("pipeline_index"),ut=3,Zr=async({config:e,apiToken:t,cliPath:r="netlify",cwd:i,filter:n,tracing:o={}})=>{let s,a,{withStageTimer:l}=nr(de.timeUnits.hours(4)),c=await Ht(Xr.version,e.id,o);try{await zr(Jr(),"run-pipeline",{},c,async()=>{let{aiGateway:u,context:d,persistSteps:p,runner:g,sha:T}=await l("init",()=>Nr({config:e,apiToken:t,cliPath:r,cwd:i,filter:n,runnerVersion:Xr.version}),de.timeUnits.minutes(10));s=g.clean,a=Vr(e.id,e.sessionId,e.enforcedAICreditsRemaining);let _,x=Object.assign(async y=>{try{a?.onAgentOutput()}catch(h){We(h)?_??=h:Ee.warn("Unexpected error in onAgentOutput",{error:h?.message||h});return}return p(y)},{flush:p.flush.bind(p)});if(e.sha=T,e.mode==="redeploy"){let y=await l("deploy",()=>lt({cliPath:r,config:e,context:d,result:"Redeploy completed",filter:n,isRetry:!1}));y.deployError&&Ee.warn(`Redeploy deploy failed: ${y.deployError}`);let{diff:h,resultDiff:A,previewInfo:b,diffBinary:F,resultDiffBinary:ce,isProdDeploy:te,hasNetlifyForm:re,hasNetlifyIdentity:Te}=y;await a?.stop(),await l("cleanup",()=>Ut({config:e,diff:h,result:"Redeploy completed",duration:0,resultDiff:A,diffBinary:F,resultDiffBinary:ce,previewInfo:b,isProdDeploy:te,hasNetlifyForm:re,hasNetlifyIdentity:Te}),de.timeUnits.minutes(10)),process.env.NETLIFY_LOCAL_MODE||(await s?.(),await St());return}let w;e.mode==="create"&&(w=(await l("create",()=>Kr({config:e,aiGateway:u,cwd:i}))).additionalContext);let{runnerResult:S}=await l("inference",()=>ot({cliPath:r,config:e,context:d,runner:g.runner,persistSteps:x,aiGateway:u,additionalContext:w,cwd:i}));if(_)throw _;let R=await l("deploy",()=>lt({cliPath:r,config:e,context:d,result:S.result,filter:n,isRetry:!1})),k=S,P=[];if(R.hasChanges&&R.deployError){P.push(Wt(R.deployError));let y=1,h=!1;for(;y<=ut&&!R.previewInfo&&!h;)Ee.log(`Deploy attempt had errors. Retrying. ${y}/${ut}`),await zr(Jr(),"deploy-stage",async A=>{A?.setAttributes({"stage.attempt":y});let b;try{b=(await l(`inference-retry-${y}`,()=>ot({cliPath:r,config:e,context:d,runner:g.runner,persistSteps:x,aiGateway:u,buildErrors:P,priorAgentSessionId:S.agentSessionId}))).runnerResult}catch(F){if(We(F))throw F;Ee.warn(`Inference retry ${y} failed, stopping deploy retries:`,F),h=!0;return}if(_)throw _;k={...b,steps:[...k.steps||[],...b.steps||[]],duration:(k.duration||0)+(b.duration||0)},R=await l(`deploy-retry-${y}`,()=>lt({cliPath:r,config:e,context:d,result:b.result,filter:n,isRetry:!0})),R.deployError&&P.push(R.deployError),y++});y>ut&&!R.previewInfo&&console.warn(`Deploy validation failed after ${ut} attempts`)}let{diff:L,resultDiff:Y,previewInfo:O,diffBinary:D,resultDiffBinary:ee,isProdDeploy:v,hasNetlifyForm:m,hasNetlifyIdentity:E}=R;await a?.stop(),await l("cleanup",()=>Ut({config:e,diff:L,result:k.result,duration:k.duration,resultDiff:Y,diffBinary:D,resultDiffBinary:ee,previewInfo:O,isProdDeploy:v,hasNetlifyForm:m,hasNetlifyIdentity:E}),de.timeUnits.minutes(10)),process.env.NETLIFY_LOCAL_MODE||(await s?.(),await St())})}catch(u){if(We(u)){Ee.info("Agent run terminated gracefully",{statusCode:u.statusCode,reason:u.message}),await a?.stop(),await s?.();try{await K(e.id,e.sessionId,{result:u.userMessage,state:u.isCreditLimitExceeded?"cancelled":"error",...u.isCreditLimitExceeded&&{credit_limit_exceeded:!0}})}catch{Ee.info("Could not update session (site may have been deleted)")}return}Ee.error("Got error while running pipeline",u),await a?.stop(),await s?.();let d=u instanceof Error&&u.message;throw await K(e.id,e.sessionId,{result:d||"Encountered error when running agent",state:"error"}),u}finally{await Li()}};import Qr from"crypto";var j=I("bin_local"),
|
|
203
|
+
\`\`\``,U.info("Generated project structure for agent context"))}catch(w){U.warn("Failed to generate project structure",w.message)}let x=performance.now()-n;return i?.setAttributes({"create.framework":p?.framework,"create.template":p?.template,"create.duration.ms":x,"create.status":"success"}),{...p??{template:"",newPrompt:"",packageManager:"",framework:""},additionalContext:_}});var Be=I("usage_tracker"),Oi=4e3,Vr=(e,t,r)=>{let i=!1,n=!1,o=!1,a=et(async()=>{try{let u=await er(e,t);Be.log("Usage update response",{usage:u?.usage}),r!=null&&u?.usage?.total_credits_cost!=null&&u.usage.total_credits_cost>=r&&(Be.log("Credit limit exceeded",{totalCreditsCost:u.usage.total_credits_cost,enforcedCreditsRemaining:r}),o=!0),u?.credit_limit_exceeded&&(Be.log("Credit limit exceeded (flagged by API)"),o=!0)}catch(u){Be.warn("Failed to update usage",{error:u?.message||u})}},Oi);return{onAgentOutput:()=>{if(o)throw new ne("AI credit usage exceeded enforced limit.",503,"Credit limit reached. Check credit limits to continue using Agent Runners.",!0);n||(i=!0,a())},stop:async()=>{n||(n=!0,i&&(Be.log("Sending final usage update"),a(),await a.flush()))}}};var Di=Fi(import.meta.url),Xr=Di("../package.json"),Ee=I("pipeline_index"),ut=3,Zr=async({config:e,apiToken:t,cliPath:r="netlify",cwd:i,filter:n,tracing:o={}})=>{let s,a,{withStageTimer:l}=nr(de.timeUnits.hours(4)),c=await Ht(Xr.version,e.id,o);try{await zr(Jr(),"run-pipeline",{},c,async()=>{let{aiGateway:u,context:d,persistSteps:p,runner:g,sha:T}=await l("init",()=>Nr({config:e,apiToken:t,cliPath:r,cwd:i,filter:n,runnerVersion:Xr.version}),de.timeUnits.minutes(10));s=g.clean,a=Vr(e.id,e.sessionId,e.enforcedAICreditsRemaining);let _,x=Object.assign(async y=>{try{a?.onAgentOutput()}catch(h){We(h)?_??=h:Ee.warn("Unexpected error in onAgentOutput",{error:h?.message||h});return}return p(y)},{flush:p.flush.bind(p)});if(e.sha=T,e.mode==="redeploy"){let y=await l("deploy",()=>lt({cliPath:r,config:e,context:d,result:"Redeploy completed",filter:n,isRetry:!1}));y.deployError&&Ee.warn(`Redeploy deploy failed: ${y.deployError}`);let{diff:h,resultDiff:A,previewInfo:b,diffBinary:F,resultDiffBinary:ce,isProdDeploy:te,hasNetlifyForm:re,hasNetlifyIdentity:Te}=y;await a?.stop(),await l("cleanup",()=>Ut({config:e,diff:h,result:"Redeploy completed",duration:0,resultDiff:A,diffBinary:F,resultDiffBinary:ce,previewInfo:b,isProdDeploy:te,hasNetlifyForm:re,hasNetlifyIdentity:Te}),de.timeUnits.minutes(10)),process.env.NETLIFY_LOCAL_MODE||(await s?.(),await St());return}let w;e.mode==="create"&&(w=(await l("create",()=>Kr({config:e,aiGateway:u,cwd:i}))).additionalContext);let{runnerResult:S}=await l("inference",()=>ot({cliPath:r,config:e,context:d,runner:g.runner,persistSteps:x,aiGateway:u,additionalContext:w,cwd:i}));if(_)throw _;let R=await l("deploy",()=>lt({cliPath:r,config:e,context:d,result:S.result,filter:n,isRetry:!1})),k=S,P=[];if(R.hasChanges&&R.deployError){P.push(Wt(R.deployError));let y=1,h=!1;for(;y<=ut&&!R.previewInfo&&!h;)Ee.log(`Deploy attempt had errors. Retrying. ${y}/${ut}`),await zr(Jr(),"deploy-stage",async A=>{A?.setAttributes({"stage.attempt":y});let b;try{b=(await l(`inference-retry-${y}`,()=>ot({cliPath:r,config:e,context:d,runner:g.runner,persistSteps:x,aiGateway:u,buildErrors:P,priorAgentSessionId:S.agentSessionId}))).runnerResult}catch(F){if(We(F))throw F;Ee.warn(`Inference retry ${y} failed, stopping deploy retries:`,F),h=!0;return}if(_)throw _;k={...b,steps:[...k.steps||[],...b.steps||[]],duration:(k.duration||0)+(b.duration||0)},R=await l(`deploy-retry-${y}`,()=>lt({cliPath:r,config:e,context:d,result:b.result,filter:n,isRetry:!0})),R.deployError&&P.push(R.deployError),y++});y>ut&&!R.previewInfo&&console.warn(`Deploy validation failed after ${ut} attempts`)}let{diff:L,resultDiff:Y,previewInfo:O,diffBinary:D,resultDiffBinary:ee,isProdDeploy:v,hasNetlifyForm:m,hasNetlifyIdentity:E}=R;await a?.stop(),await l("cleanup",()=>Ut({config:e,diff:L,result:k.result,duration:k.duration,resultDiff:Y,diffBinary:D,resultDiffBinary:ee,previewInfo:O,isProdDeploy:v,hasNetlifyForm:m,hasNetlifyIdentity:E}),de.timeUnits.minutes(10)),process.env.NETLIFY_LOCAL_MODE||(await s?.(),await St())})}catch(u){if(We(u)){Ee.info("Agent run terminated gracefully",{statusCode:u.statusCode,reason:u.message}),await a?.stop(),await s?.();try{await K(e.id,e.sessionId,{result:u.userMessage,state:u.isCreditLimitExceeded?"cancelled":"error",...u.isCreditLimitExceeded&&{credit_limit_exceeded:!0}})}catch{Ee.info("Could not update session (site may have been deleted)")}return}Ee.error("Got error while running pipeline",u),await a?.stop(),await s?.();let d=u instanceof Error&&u.message;throw await K(e.id,e.sessionId,{result:d||"Encountered error when running agent",state:"error"}),u}finally{await Li()}};import Qr from"crypto";var j=I("bin_local"),Z=Mi(G.argv.slice(2),{string:["cwd","cli-path","filter","prompt","runner","model","netlify-api-token"],boolean:["verbose","help"],alias:{h:"help",v:"verbose"}}),jt=()=>{console.log(`
|
|
204
204
|
agent-runner-cli-local - Run Netlify agent runner locally without API connections
|
|
205
205
|
|
|
206
206
|
USAGE:
|
|
@@ -233,6 +233,6 @@ NOTE:
|
|
|
233
233
|
This local mode mocks all Netlify API calls. The agent will run through
|
|
234
234
|
the full pipeline including inference and deployment, but API calls will
|
|
235
235
|
be logged instead of executed.
|
|
236
|
-
`)};
|
|
237
|
-
To link this directory to a Netlify site, run:`),j.error(" netlify link"),G.exit(1)}let i=`local-${Qr.randomBytes(8).toString("hex")}`,n=`session-${Qr.randomBytes(8).toString("hex")}`,o=
|
|
236
|
+
`)};Z.help&&(jt(),G.exit(0));Z.prompt||(j.error("Error: --prompt is required"),jt(),G.exit(1));Z["netlify-api-token"]||(j.error("Error: --netlify-api-token is required - generate a PAT from your Netlify user settings"),jt(),G.exit(1));try{let e=Z.cwd||G.cwd(),t=en.join(e,".netlify","netlify-agent-runner-context*");tn.rmSync(t,{recursive:!0,force:!0});let r;try{r=await Ui(e)}catch(u){j.error(u.message),j.error(`
|
|
237
|
+
To link this directory to a Netlify site, run:`),j.error(" netlify link"),G.exit(1)}let i=`local-${Qr.randomBytes(8).toString("hex")}`,n=`session-${Qr.randomBytes(8).toString("hex")}`,o=Z.runner||"claude",s=Z.mode||"normal",a=!!Z.staging,l="local-team-id";j.log("Starting agent runner in local mode",{runnerId:i,sessionId:n,siteId:r,cwd:e,runner:o,mode:s,isStaging:a});let c={id:i,sessionId:n,prompt:Z.prompt,runner:o,model:Z.model,accountType:"free",mode:s,sessionHistoryContext:[],siteContext:[],hasRepo:!0,useGateway:!0,sha:void 0,runSha:await De(e),modelVersionOverrides:{},accountId:l,siteId:r};G.env.NETLIFY_LOCAL_MODE="true",G.env.NETLIFY_API_HOST=a?"api-staging.netlify.com":"api.netlify.com",G.env.NETLIFY_API_TOKEN=Z["netlify-api-token"],G.env.SITE_ID=r,G.env.NETLIFY_TEAM_ID=l,G.env.NETLIFY_AGENT_RUNNER_USER_ID="local-user-id",G.env.SITE_NAME="local-site",o==="claude"?Qe(e,"claude")||(j.log("Claude CLI not found, installing..."),await Gt(e,"@anthropic-ai/claude-code")):o==="gemini"?Qe(e,"gemini")||(j.log("Gemini CLI not found, installing..."),await Gt(e,"@google/gemini-cli")):o==="codex"?Qe(e,"codex")||(j.log("Codex CLI not found, installing..."),await Gt(e,"@openai/codex")):(j.error(`Unknown runner: ${o}`),G.exit(1)),await Zr({config:c,cwd:e,cliPath:Z["cli-path"],filter:Z.filter,tracing:{exporterUrl:void 0,traceparent:void 0}}),j.info("Finished agent (local mode)"),G.exit(0)}catch(e){j.error("Error running agent pipeline (local mode):",e),G.exit(1)}function Gt(e,t){return new Promise((r,i)=>{N("npm",["install",t,"--no-save"],{cwd:e}).then(({stdout:n})=>{j.log(`${t} installed: ${n}`),r()}).catch(n=>{j.error(`Error installing ${t}: ${n.stderr||n.message}`),i(n)})})}async function Ui(e){let t=en.join(e,".netlify","state.json");try{let r=await tn.readFileSync(t,"utf-8"),i=JSON.parse(r);if(!i.siteId)throw new Error(`No siteId found in ${t}. Please link this directory to a Netlify site using 'netlify link'.`);return j.log(`Found site ID from state file: ${i.siteId}`),i.siteId}catch(r){throw r.code==="ENOENT"?new Error(`No .netlify/state.json found in ${e}. Please link this directory to a Netlify site using 'netlify link'.`):r}}
|
|
238
238
|
//# sourceMappingURL=bin-local.js.map
|
package/dist/bin.js
CHANGED
|
@@ -5,7 +5,7 @@ import Bt from"process";import Xi from"minimist";import{createRequire as Gi}from
|
|
|
5
5
|
${s}
|
|
6
6
|
</extracted_error_chunk>`).join(`
|
|
7
7
|
|
|
8
|
-
`);return o.length>e.length*.8?e:o}import{execSync as ai}from"child_process";import Dr from"fs/promises";import li from"path";import ye from"process";import{getTracer as ci}from"@netlify/otel";import Oe from"process";var te=class extends Error{constructor(r,i,n,o=!1){super(r);this.statusCode=i;this.userMessage=n;this.isCreditLimitExceeded=o;this.name="GracefulShutdownError"}},He=e=>e instanceof te;var We=Oe.env.NETLIFY_API_URL,Ke=Oe.env.NETLIFY_API_TOKEN,H=S("api"),we=()=>Oe.env.NETLIFY_LOCAL_MODE==="true",ae=async(e,t={})=>{if(!We||!Ke)throw new Error("No API URL or token");let r=new URL(e,We),i={...t,headers:{...t.headers,Authorization:`Bearer ${Ke}`}};Oe.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(Oe.env.AGENT_RUNNERS_DEBUG==="true")H.log(`Response headers for ${r}:`),n.headers.forEach((a,c)=>{H.log(` ${c}: ${a}`)});else{let a=n.headers.get("x-request-id")||n.headers.get("x-nf-request-id");H.log(`Request ID for ${r}: ${a||"N/A"}`)}if(o||H.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 te(`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 te(`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},zt=e=>{H.log("Setting details for api",{apiUrl:e?.constants?.NETLIFY_API_HOST,token:!!e?.constants?.NETLIFY_API_TOKEN}),e?.constants?.NETLIFY_API_HOST&&(We=`https://${e.constants.NETLIFY_API_HOST}`),e?.constants?.NETLIFY_API_TOKEN&&(Ke=e.constants.NETLIFY_API_TOKEN)},Xt=()=>({apiUrl:We,token:Ke}),$e=async(e,t)=>we()?(H.log("Mock API: updateRunner called",{runnerId:e,data:t}),{id:e,...t}):ae(`/api/v1/agent_runners/${e}`,{method:"PUT",json:t}),W=async(e,t,r)=>we()?(H.log("Mock API: updateRunnerSession called",JSON.stringify({runnerId:e,sessionId:t,data:r},null,2)),{id:e,sessionId:t,...r}):ae(`/api/v1/agent_runners/${e}/sessions/${t}`,{method:"PUT",json:r});var Zt=async e=>we()?(H.log("Mock API: getSite called",{siteId:e}),{id:e,published_deploy:{id:"id"}}):ae(`/api/v1/sites/${e}`),Qt=async(e,t)=>we()?(H.log("Mock API: getRunnerSession called",{runnerId:e,sessionId:t}),{id:t,runnerId:e,state:"running"}):ae(`/api/v1/agent_runners/${e}/sessions/${t}`),er=(e,t,r)=>ae(`/api/v1/accounts/${e}/ai-gateway/token`,{headers:{"X-Nf-Agent-Runner-Id":t,"X-Nf-Agent-Runner-Session-Id":r},gracefulOn503:!0}),tr=(e,t,r)=>ae(`/api/v1/sites/${e}/ai-gateway/token`,{headers:{"X-Nf-Agent-Runner-Id":t,"X-Nf-Agent-Runner-Session-Id":r},gracefulOn503:!0}),rr=async(e,t)=>we()?(H.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"}}):ae(`/api/v1/agent_runners/${e}/sessions/${t}/diff/upload_urls`,{method:"POST"}),nr=async(e,t)=>we()?(H.log("Mock API: updateSessionUsage called",{runnerId:e,sessionId:t}),{id:t,runnerId:e,usage:0}):ae(`/api/v1/agent_runners/${e}/sessions/${t}/update_usage`,{method:"POST"}),pt=async(e,t,{maxRetries:r=3,baseDelayMs:i=500}={})=>{H.log(`Uploading diff to S3: ${e.substring(0,50)}...`);for(let n=1;n<=r;n++)try{let o=await fetch(e,{method:"PUT",body:t,headers:{"Content-Type":"text/plain"}});if(!o.ok)throw new Error(`S3 upload failed with status ${o.status}`);return o}catch(o){if(n===r)throw o;let s=i*2**(n-1);H.warn(`S3 upload attempt ${n}/${r} failed: ${o.message}. Retrying in ${s}ms...`),await new Promise(a=>setTimeout(a,s))}};var Te=S("ai_gateway"),mt=null;var Ve=async()=>{if(mt)return mt;Te.log("Fetching available AI gateway providers");let e=await fetch(`${Xt().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 mt=t,Te.log("Cached AI gateway providers",{providerCount:Object.keys(t.providers).length}),t},fn=async(e,t)=>{let i=(await Ve()).providers[e];if(!i)return Te.log(`Provider '${e}' not found`),!1;let n=i.models.includes(t);return Te.log(`Model validation for ${e}/${t}`,{isAvailable:n}),n},ir=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),Te.log("Requesting AI gateway information");let l=await(o?er(e.accountId,e.id,e.sessionId):tr(e.siteId,e.id,e.sessionId));if({token:t,url:n}=l,r=l.expires_at?l.expires_at*1e3:void 0,Te.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(),Ve()]),{get url(){return n},get token(){return t},isModelAvailableForProvider:fn}};import ne from"process";import oe from"path";import Xe from"fs";import{fileURLToPath as In}from"url";import{createRequire as Sn}from"module";import{execa as vn,execaCommand as No}from"execa";import{Transform as hn}from"stream";function yn(){let e=process.env.NETLIFY_SENSITIVE_ENV_KEYS;return e?e.split(",").map(t=>t.trim()).filter(Boolean):[]}function _n(e){let t=e.toLowerCase();return t==="true"||t==="false"?!0:e.trim().length<4}function En(){let t=yn().map(r=>process.env[r]).filter(r=>!(!r||_n(r)));return[...new Set(t)].sort((r,i)=>i.length-r.length)}function ie(e){if(typeof e!="string")return e;let t=En();if(t.length===0)return e;let r=e;return t.forEach(i=>{let n=new RegExp(wn(i),"g");r=r.replace(n,"******")}),r}function wn(e){return e.replace(/[.*+?^${}()|[\]\\]/g,"\\$&")}var xe=class extends hn{constructor(t={}){super({...t,objectMode:!1})}_transform(t,r,i){let n=t.toString(),o=ie(n);i(null,o)}};function or(){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 Fe=null,sr=e=>(Fe&&Fe.destroy(),Fe=new le({totalAllowedTime:e}),Fe),ar=()=>Fe;var le=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 lr="netlify-agent-runner-context.md",gt="task-history",re=".netlify",me="results.md",ft="assets",ht="other",yt="personal";var _t="enterprise",Le="free",cr=[yt,"pro",_t,Le],ur=["normal","redeploy","create","ask","dtn-prod-iteration","rebase"],dr="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.",ge=1800*1e3,Tn=["\\.claude","\\.gemini","\\.codex","\\.agents"],Je=new RegExp(`(^|/)(${Tn.join("|")})/skills/`),ze=/(^|\/)((CLAUDE|AGENTS|GEMINI)(\.local)?\.md)$/,f={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 pr={name:"@netlify/agent-runner-cli",type:"module",version:"1.94.0-netlifydb.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.81","@anthropic-ai/sdk":"0.78.0","@google/gemini-cli":"0.31.0","@netlify/otel":"^5.1.5","@netlify/ts-cli":"^1.0.4","@openai/codex":"0.115.0","@opentelemetry/exporter-trace-otlp-grpc":"0.57.2",execa:"^9.6.1",minimist:"^1.2.8",openai:"6.26.0"}};var Rn=In(import.meta.url),An=oe.dirname(Rn),bn=Sn(import.meta.url),Ie=S("shell"),Et=new Set,Cn={preferLocal:!0},O=(e,t,r)=>{let[i,n]=Nn(t,r),o={...Cn,...n},s=vn(e,i,o);Pn(s,o),On(s);let a=r?.idleTimeout;return a&&a>0&&kn(s,a),s};var Nn=function(e,t){return Array.isArray(e)?[e,t]:typeof e=="object"&&e!==null?[[],e]:[[],void 0]},Pn=(e,t)=>{if(t.stdio!==void 0||t.stdout!==void 0||t.stderr!==void 0)return;if(ne.env.NETLIFY_MASK_LOGS!=="false"){e.all?.pipe(new xe).pipe(ne.stdout),e.stdout?.pipe(new xe).pipe(ne.stdout),e.stderr?.pipe(new xe).pipe(ne.stderr);return}e.stdout?.pipe(ne.stdout),e.stderr?.pipe(ne.stderr)},wt=(e,t="SIGTERM")=>{try{return e.pid&&!e.killed?(ne.kill(-e.pid,t),Ie.log(`Killed process ${e.pid} with signal ${t}`),!0):!1}catch(r){return Ie.error("Error killing process:",r),!1}},mr=e=>wt(e,"SIGKILL"),kn=(e,t)=>{let r=null,i=()=>{Ie.log(`Process ${e.pid} killed due to idle timeout (no output for ${t}ms)`),wt(e,"SIGTERM"),setTimeout(()=>{e.pid&&!e.killed&&(Ie.log(`Force killing idle process ${e.pid}`),mr(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)},On=e=>{Et.add(e);let t=ar();if(t){let r=t.onTimesUp(()=>{Ie.log(`Global timer expired, killing process ${e.pid}`),wt(e,"SIGTERM"),setTimeout(()=>{e.pid&&!e.killed&&(Ie.log(`Force killing process ${e.pid} after timeout`),mr(e))},5e3)});e.on("exit",()=>{Et.delete(e),r()}),e.on("error",()=>{Et.delete(e),r()})}};function ce(e,t){if(!ne.env.NETLIFY_LOCAL_MODE)try{let n=bn.resolve(pr.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(Xe.existsSync(a))return a;break}o=s}}catch(n){console.error("Could not resolve package.json",n)}if(ne.env.NODE_PATH){let n=oe.join(ne.env.NODE_PATH,".bin",t);if(Xe.existsSync(n))return n}let r=oe.join(e,"node_modules",".bin",t);if(Xe.existsSync(r))return r;let i=oe.join(An,"..","node_modules",".bin",t);if(Xe.existsSync(i))return i}var gr=S("utils"),$n=e=>new Promise(t=>{setTimeout(t,e)}),Ze=(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 $n(t),!i)return r=!1,o=null,d;let u=i,p=n;i=null,n=[],d=await e(...u),p.forEach(g=>{g(d)})}})(),l};return s.flush=async()=>{if((r||i)&&o)return await o,s.flush()},s},Se=(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},Qe=(e,t=!0,r)=>{if(e)try{return JSON.parse(e)}catch(i){t&&(r?.error?r.error("Could not parse JSON",i):gr.error("Could not parse JSON",i))}},Tt=e=>e.charAt(0).toUpperCase()+e.slice(1),ue=e=>e.split("-").map(t=>t.length===2?t.toUpperCase():Tt(t)).join(" ");function fe(e,t){t&&e.log(`Skill invoked: ${t}`)}var fr=e=>Object.fromEntries(Object.entries(e).filter(([,t])=>t!==void 0)),hr=(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)},Fn=e=>!e||typeof e!="object"||Array.isArray(e)||Object.keys(e).length===0?!1:!!cr.some(t=>t in e),yr=()=>{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,i])=>{if(i){let n=`NETLIFY_FF_AGENT_RUNNER_${r.toUpperCase()}_VERSION`;try{let o=JSON.parse(i);Fn(o)&&(e[r]=o)}catch(o){let a=o instanceof SyntaxError?"Invalid JSON":o.message;gr.error(`Could not parse ${r} model version override from ${n}: ${a}`)}}}),e},Ln=1e4,xt=(e,t=Ln)=>{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 _r}from"buffer";import Dn from"path";var Er=S("repo"),Tr=async({config:e,isRetry:t,cwd:r=process.cwd()})=>{Er.info("Getting runner diffs");let i=await Un(r),{hasChanges:n}=i,{status:o}=i;if(!n)return{hasChanges:!1};if(!t){let E=jn(o);await Yn(E,r)}Er.info("Changes after processing"),await St(r);let s=await Rt(o,r);if(await It(s,r),n=await Gn(r),!n)return{hasChanges:!1,ignored:s};process.env.NETLIFY_INTERNAL_GIT="1";try{await O("git",["commit","-m","Agent runner"],{cwd:r})}finally{process.env.NETLIFY_INTERNAL_GIT="0"}let a={stdio:["ignore","pipe","pipe"],cwd:r},c=await O("git",["diff",e.runSha,"HEAD"],a),l=String(c.stdout??"");if(n=!!l,!n)return await wr(r),{hasChanges:!1,ignored:s};let d=await O("git",["diff",e.runSha,"HEAD","--binary"],a),u=String(d.stdout??""),p,g;if(e.sha){let E=await O("git",["diff",e.sha,"HEAD"],a);p=String(E.stdout??"");let x=await O("git",["diff",e.sha,"HEAD","--binary"],a),y=String(x.stdout??"");p!==y&&(g=_r.from(y).toString("base64"))}await wr(r);let T={hasChanges:!0,diff:l,resultDiff:p,ignored:s};return l!==u&&(T.diffBinary=_r.from(u).toString("base64")),g&&(T.resultDiffBinary=g),T},wr=async(e=process.cwd())=>{process.env.NETLIFY_LOCAL_MODE&&await O("git",["reset","--soft","HEAD~1"],{cwd:e})},It=async(e=[],t=process.cwd())=>{process.env.NETLIFY_INTERNAL_GIT="1";try{await O("git",["add",".",...e],{cwd:t})}finally{process.env.NETLIFY_INTERNAL_GIT="0"}},St=async(e=process.cwd())=>{let t=await O("git",["status","-s"],{cwd:e});return String(t.stdout??"")},xr=/.. (.+)?\.log$/,Mn=[xr],Un=async(e=process.cwd())=>{let t=await St(e);return{hasChanges:(t.trim().length===0?[]:t.split(`
|
|
8
|
+
`);return o.length>e.length*.8?e:o}import{execSync as ai}from"child_process";import Dr from"fs/promises";import li from"path";import ye from"process";import{getTracer as ci}from"@netlify/otel";import Oe from"process";var te=class extends Error{constructor(r,i,n,o=!1){super(r);this.statusCode=i;this.userMessage=n;this.isCreditLimitExceeded=o;this.name="GracefulShutdownError"}},He=e=>e instanceof te;var We=Oe.env.NETLIFY_API_URL,Ke=Oe.env.NETLIFY_API_TOKEN,H=S("api"),we=()=>Oe.env.NETLIFY_LOCAL_MODE==="true",ae=async(e,t={})=>{if(!We||!Ke)throw new Error("No API URL or token");let r=new URL(e,We),i={...t,headers:{...t.headers,Authorization:`Bearer ${Ke}`}};Oe.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(Oe.env.AGENT_RUNNERS_DEBUG==="true")H.log(`Response headers for ${r}:`),n.headers.forEach((a,c)=>{H.log(` ${c}: ${a}`)});else{let a=n.headers.get("x-request-id")||n.headers.get("x-nf-request-id");H.log(`Request ID for ${r}: ${a||"N/A"}`)}if(o||H.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 te(`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 te(`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},zt=e=>{H.log("Setting details for api",{apiUrl:e?.constants?.NETLIFY_API_HOST,token:!!e?.constants?.NETLIFY_API_TOKEN}),e?.constants?.NETLIFY_API_HOST&&(We=`https://${e.constants.NETLIFY_API_HOST}`),e?.constants?.NETLIFY_API_TOKEN&&(Ke=e.constants.NETLIFY_API_TOKEN)},Xt=()=>({apiUrl:We,token:Ke}),$e=async(e,t)=>we()?(H.log("Mock API: updateRunner called",{runnerId:e,data:t}),{id:e,...t}):ae(`/api/v1/agent_runners/${e}`,{method:"PUT",json:t}),W=async(e,t,r)=>we()?(H.log("Mock API: updateRunnerSession called",JSON.stringify({runnerId:e,sessionId:t,data:r},null,2)),{id:e,sessionId:t,...r}):ae(`/api/v1/agent_runners/${e}/sessions/${t}`,{method:"PUT",json:r});var Zt=async e=>we()?(H.log("Mock API: getSite called",{siteId:e}),{id:e,published_deploy:{id:"id"}}):ae(`/api/v1/sites/${e}`),Qt=async(e,t)=>we()?(H.log("Mock API: getRunnerSession called",{runnerId:e,sessionId:t}),{id:t,runnerId:e,state:"running"}):ae(`/api/v1/agent_runners/${e}/sessions/${t}`),er=(e,t,r)=>ae(`/api/v1/accounts/${e}/ai-gateway/token`,{headers:{"X-Nf-Agent-Runner-Id":t,"X-Nf-Agent-Runner-Session-Id":r},gracefulOn503:!0}),tr=(e,t,r)=>ae(`/api/v1/sites/${e}/ai-gateway/token`,{headers:{"X-Nf-Agent-Runner-Id":t,"X-Nf-Agent-Runner-Session-Id":r},gracefulOn503:!0}),rr=async(e,t)=>we()?(H.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"}}):ae(`/api/v1/agent_runners/${e}/sessions/${t}/diff/upload_urls`,{method:"POST"}),nr=async(e,t)=>we()?(H.log("Mock API: updateSessionUsage called",{runnerId:e,sessionId:t}),{id:t,runnerId:e,usage:0}):ae(`/api/v1/agent_runners/${e}/sessions/${t}/update_usage`,{method:"POST"}),pt=async(e,t,{maxRetries:r=3,baseDelayMs:i=500}={})=>{H.log(`Uploading diff to S3: ${e.substring(0,50)}...`);for(let n=1;n<=r;n++)try{let o=await fetch(e,{method:"PUT",body:t,headers:{"Content-Type":"text/plain"}});if(!o.ok)throw new Error(`S3 upload failed with status ${o.status}`);return o}catch(o){if(n===r)throw o;let s=i*2**(n-1);H.warn(`S3 upload attempt ${n}/${r} failed: ${o.message}. Retrying in ${s}ms...`),await new Promise(a=>setTimeout(a,s))}};var Te=S("ai_gateway"),mt=null;var Ve=async()=>{if(mt)return mt;Te.log("Fetching available AI gateway providers");let e=await fetch(`${Xt().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 mt=t,Te.log("Cached AI gateway providers",{providerCount:Object.keys(t.providers).length}),t},fn=async(e,t)=>{let i=(await Ve()).providers[e];if(!i)return Te.log(`Provider '${e}' not found`),!1;let n=i.models.includes(t);return Te.log(`Model validation for ${e}/${t}`,{isAvailable:n}),n},ir=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),Te.log("Requesting AI gateway information");let l=await(o?er(e.accountId,e.id,e.sessionId):tr(e.siteId,e.id,e.sessionId));if({token:t,url:n}=l,r=l.expires_at?l.expires_at*1e3:void 0,Te.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(),Ve()]),{get url(){return n},get token(){return t},isModelAvailableForProvider:fn}};import ne from"process";import oe from"path";import Xe from"fs";import{fileURLToPath as In}from"url";import{createRequire as Sn}from"module";import{execa as vn,execaCommand as No}from"execa";import{Transform as hn}from"stream";function yn(){let e=process.env.NETLIFY_SENSITIVE_ENV_KEYS;return e?e.split(",").map(t=>t.trim()).filter(Boolean):[]}function _n(e){let t=e.toLowerCase();return t==="true"||t==="false"?!0:e.trim().length<4}function En(){let t=yn().map(r=>process.env[r]).filter(r=>!(!r||_n(r)));return[...new Set(t)].sort((r,i)=>i.length-r.length)}function ie(e){if(typeof e!="string")return e;let t=En();if(t.length===0)return e;let r=e;return t.forEach(i=>{let n=new RegExp(wn(i),"g");r=r.replace(n,"******")}),r}function wn(e){return e.replace(/[.*+?^${}()|[\]\\]/g,"\\$&")}var xe=class extends hn{constructor(t={}){super({...t,objectMode:!1})}_transform(t,r,i){let n=t.toString(),o=ie(n);i(null,o)}};function or(){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 Fe=null,sr=e=>(Fe&&Fe.destroy(),Fe=new le({totalAllowedTime:e}),Fe),ar=()=>Fe;var le=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 lr="netlify-agent-runner-context.md",gt="task-history",re=".netlify",me="results.md",ft="assets",ht="other",yt="personal";var _t="enterprise",Le="free",cr=[yt,"pro",_t,Le],ur=["normal","redeploy","create","ask","dtn-prod-iteration","rebase"],dr="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.",ge=1800*1e3,Tn=["\\.claude","\\.gemini","\\.codex","\\.agents"],Je=new RegExp(`(^|/)(${Tn.join("|")})/skills/`),ze=/(^|\/)((CLAUDE|AGENTS|GEMINI)(\.local)?\.md)$/,f={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 pr={name:"@netlify/agent-runner-cli",type:"module",version:"1.94.0-netlifydb.4",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.81","@anthropic-ai/sdk":"0.78.0","@google/gemini-cli":"0.31.0","@netlify/otel":"^5.1.5","@netlify/ts-cli":"^1.0.4","@openai/codex":"0.115.0","@opentelemetry/exporter-trace-otlp-grpc":"0.57.2",execa:"^9.6.1",minimist:"^1.2.8",openai:"6.26.0"}};var Rn=In(import.meta.url),An=oe.dirname(Rn),bn=Sn(import.meta.url),Ie=S("shell"),Et=new Set,Cn={preferLocal:!0},O=(e,t,r)=>{let[i,n]=Nn(t,r),o={...Cn,...n},s=vn(e,i,o);Pn(s,o),On(s);let a=r?.idleTimeout;return a&&a>0&&kn(s,a),s};var Nn=function(e,t){return Array.isArray(e)?[e,t]:typeof e=="object"&&e!==null?[[],e]:[[],void 0]},Pn=(e,t)=>{if(t.stdio!==void 0||t.stdout!==void 0||t.stderr!==void 0)return;if(ne.env.NETLIFY_MASK_LOGS!=="false"){e.all?.pipe(new xe).pipe(ne.stdout),e.stdout?.pipe(new xe).pipe(ne.stdout),e.stderr?.pipe(new xe).pipe(ne.stderr);return}e.stdout?.pipe(ne.stdout),e.stderr?.pipe(ne.stderr)},wt=(e,t="SIGTERM")=>{try{return e.pid&&!e.killed?(ne.kill(-e.pid,t),Ie.log(`Killed process ${e.pid} with signal ${t}`),!0):!1}catch(r){return Ie.error("Error killing process:",r),!1}},mr=e=>wt(e,"SIGKILL"),kn=(e,t)=>{let r=null,i=()=>{Ie.log(`Process ${e.pid} killed due to idle timeout (no output for ${t}ms)`),wt(e,"SIGTERM"),setTimeout(()=>{e.pid&&!e.killed&&(Ie.log(`Force killing idle process ${e.pid}`),mr(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)},On=e=>{Et.add(e);let t=ar();if(t){let r=t.onTimesUp(()=>{Ie.log(`Global timer expired, killing process ${e.pid}`),wt(e,"SIGTERM"),setTimeout(()=>{e.pid&&!e.killed&&(Ie.log(`Force killing process ${e.pid} after timeout`),mr(e))},5e3)});e.on("exit",()=>{Et.delete(e),r()}),e.on("error",()=>{Et.delete(e),r()})}};function ce(e,t){if(!ne.env.NETLIFY_LOCAL_MODE)try{let n=bn.resolve(pr.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(Xe.existsSync(a))return a;break}o=s}}catch(n){console.error("Could not resolve package.json",n)}if(ne.env.NODE_PATH){let n=oe.join(ne.env.NODE_PATH,".bin",t);if(Xe.existsSync(n))return n}let r=oe.join(e,"node_modules",".bin",t);if(Xe.existsSync(r))return r;let i=oe.join(An,"..","node_modules",".bin",t);if(Xe.existsSync(i))return i}var gr=S("utils"),$n=e=>new Promise(t=>{setTimeout(t,e)}),Ze=(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 $n(t),!i)return r=!1,o=null,d;let u=i,p=n;i=null,n=[],d=await e(...u),p.forEach(g=>{g(d)})}})(),l};return s.flush=async()=>{if((r||i)&&o)return await o,s.flush()},s},Se=(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},Qe=(e,t=!0,r)=>{if(e)try{return JSON.parse(e)}catch(i){t&&(r?.error?r.error("Could not parse JSON",i):gr.error("Could not parse JSON",i))}},Tt=e=>e.charAt(0).toUpperCase()+e.slice(1),ue=e=>e.split("-").map(t=>t.length===2?t.toUpperCase():Tt(t)).join(" ");function fe(e,t){t&&e.log(`Skill invoked: ${t}`)}var fr=e=>Object.fromEntries(Object.entries(e).filter(([,t])=>t!==void 0)),hr=(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)},Fn=e=>!e||typeof e!="object"||Array.isArray(e)||Object.keys(e).length===0?!1:!!cr.some(t=>t in e),yr=()=>{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,i])=>{if(i){let n=`NETLIFY_FF_AGENT_RUNNER_${r.toUpperCase()}_VERSION`;try{let o=JSON.parse(i);Fn(o)&&(e[r]=o)}catch(o){let a=o instanceof SyntaxError?"Invalid JSON":o.message;gr.error(`Could not parse ${r} model version override from ${n}: ${a}`)}}}),e},Ln=1e4,xt=(e,t=Ln)=>{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 _r}from"buffer";import Dn from"path";var Er=S("repo"),Tr=async({config:e,isRetry:t,cwd:r=process.cwd()})=>{Er.info("Getting runner diffs");let i=await Un(r),{hasChanges:n}=i,{status:o}=i;if(!n)return{hasChanges:!1};if(!t){let E=jn(o);await Yn(E,r)}Er.info("Changes after processing"),await St(r);let s=await Rt(o,r);if(await It(s,r),n=await Gn(r),!n)return{hasChanges:!1,ignored:s};process.env.NETLIFY_INTERNAL_GIT="1";try{await O("git",["commit","-m","Agent runner"],{cwd:r})}finally{process.env.NETLIFY_INTERNAL_GIT="0"}let a={stdio:["ignore","pipe","pipe"],cwd:r},c=await O("git",["diff",e.runSha,"HEAD"],a),l=String(c.stdout??"");if(n=!!l,!n)return await wr(r),{hasChanges:!1,ignored:s};let d=await O("git",["diff",e.runSha,"HEAD","--binary"],a),u=String(d.stdout??""),p,g;if(e.sha){let E=await O("git",["diff",e.sha,"HEAD"],a);p=String(E.stdout??"");let x=await O("git",["diff",e.sha,"HEAD","--binary"],a),y=String(x.stdout??"");p!==y&&(g=_r.from(y).toString("base64"))}await wr(r);let T={hasChanges:!0,diff:l,resultDiff:p,ignored:s};return l!==u&&(T.diffBinary=_r.from(u).toString("base64")),g&&(T.resultDiffBinary=g),T},wr=async(e=process.cwd())=>{process.env.NETLIFY_LOCAL_MODE&&await O("git",["reset","--soft","HEAD~1"],{cwd:e})},It=async(e=[],t=process.cwd())=>{process.env.NETLIFY_INTERNAL_GIT="1";try{await O("git",["add",".",...e],{cwd:t})}finally{process.env.NETLIFY_INTERNAL_GIT="0"}},St=async(e=process.cwd())=>{let t=await O("git",["status","-s"],{cwd:e});return String(t.stdout??"")},xr=/.. (.+)?\.log$/,Mn=[xr],Un=async(e=process.cwd())=>{let t=await St(e);return{hasChanges:(t.trim().length===0?[]:t.split(`
|
|
9
9
|
`).filter(n=>Mn.some(s=>s instanceof RegExp?s.test(n):n===s)?!1:n[1]?.trim()!=="")).length!==0,status:t}},Gn=async(e=process.cwd())=>{try{return await O("git",["diff","--staged","--quiet"],{cwd:e}),!1}catch{return!0}},vt=async(e=process.cwd())=>{let{stdout:t}=await O("git",["rev-parse","HEAD"],{cwd:e});return String(t??"").trim()},Ir=async(e=process.cwd())=>{let{stdout:t}=await O("git",["rev-list","--max-parents=0","HEAD"],{cwd:e});return String(t??"").trim()},Rt=async(e,t=process.cwd())=>{e||=await St(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}${Dn.sep}`);(a||c)&&i.push(`:!${s}`)});let o=n.match(xr)?.[1];o&&i.push(`:!${o}.log`)}),i},At=async(e=process.cwd())=>{await O("git",["reset","--hard","HEAD"],{cwd:e})},jn=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)},Yn=async(e,t=process.cwd())=>{let r=e.filter(i=>i.stage&&!i.change).map(i=>i.filePath);r.length!==0&&await O("git",["restore","--staged","--worktree","--pathspec-from-file=-"],{cwd:t,input:r.join(`
|
|
@@ -60,7 +60,7 @@ case "$1" in
|
|
|
60
60
|
exec ${e} "$@"
|
|
61
61
|
;;
|
|
62
62
|
esac
|
|
63
|
-
`;try{await Dr.mkdir(t,{recursive:!0}),await Dr.writeFile(r,i,{mode:493}),ye.env.PATH=`${t}:${ye.env.PATH}`,ye.env.NETLIFY_INTERNAL_GIT="0",je.info("Installed git wrapper to block add/commit commands")}catch(n){je.warn("Failed to install git wrapper",{error:n?.message||n})}},di=({apiToken:e,config:t})=>({constants:{NETLIFY_API_HOST:ye.env.NETLIFY_API_HOST||"api.netlify.com",NETLIFY_API_TOKEN:e||ye.env.NETLIFY_API_TOKEN,SITE_ID:t.siteId,FUNCTIONS_DIST:ye.env.FUNCTIONS_DIST||"netlify/functions"},utils:{run:O}});import{getTracer as Ut}from"@netlify/otel";import pi from"crypto";import K from"fs/promises";import Dt from"os";import C from"path";import X from"process";import{fileURLToPath as mi}from"url";var z=S("context"),gi=mi(import.meta.url),fi=C.dirname(gi),hi={claude:C.join(Dt.homedir(),".claude","skills"),gemini:C.join(Dt.homedir(),".agents","skills"),codex:C.join(Dt.homedir(),".agents","skills")},Ur=C.join(fi,"skills"),Mt=null;var Gr="NETLIFY_FF_AGENT_RUNNER_SKILL_",yi=()=>{let e=[];for(let[t,r]of Object.entries(X.env))t.startsWith(Gr)&&r&&e.push(t.slice(Gr.length).toLowerCase());return e},jr=e=>{let t=e.match(/^(.+)@([^.]+)(.*)$/);return t?{baseName:`${t[1]}${t[3]}`,variation:t[2]}:{baseName:e,variation:null}},_i=async(e,t=[])=>{try{let r=await K.readdir(e);for(let i of r){let{baseName:n,variation:o}=jr(i);if(o&&n==="SKILL.md"&&t.includes(o))return C.join(e,i)}}catch{}return C.join(e,"SKILL.md")},Ei=async(e,{targetDir:t}={})=>{let r=t||hi[e];if(!r)return z.warn(`Unknown runner: ${e}, skipping skills setup`),[];if(Mt)return Mt;let i=[],n=yi();try{let o=await K.readdir(Ur);for(let s of o){let a=C.join(Ur,s);if(!(await K.stat(a)).isDirectory())continue;let{baseName:l,variation:d}=jr(s);if(d&&!n.includes(d))continue;let u=C.join(r,l);await K.mkdir(u,{recursive:!0});let p=await _i(a,n),g=C.join(u,"SKILL.md");try{await K.copyFile(p,g),i.push(l),d&&z.log(`Installed skill variation: ${l} (variation: ${d})`),C.basename(p)!=="SKILL.md"&&z.log(`Using skill variation for ${l}: ${C.basename(p)}`)}catch(T){z.warn(`Failed to copy skill ${l}:`,T.message)}}}catch(o){z.warn("Failed to setup agent skills:",o.message)}if(i.includes("netlify-ai-gateway"))try{let o=await Ve(),s=C.join(r,"netlify-ai-gateway","SKILL.md"),a=await K.readFile(s,"utf-8");if(a.includes("<!-- AVAILABLE_MODELS -->")){let c=Object.entries(o.providers).map(([l,d])=>`### ${l}
|
|
63
|
+
`;try{await Dr.mkdir(t,{recursive:!0}),await Dr.writeFile(r,i,{mode:493}),ye.env.PATH=`${t}:${ye.env.PATH}`,ye.env.NETLIFY_INTERNAL_GIT="0",je.info("Installed git wrapper to block add/commit commands")}catch(n){je.warn("Failed to install git wrapper",{error:n?.message||n})}},di=({apiToken:e,config:t})=>({constants:{NETLIFY_API_HOST:ye.env.NETLIFY_API_HOST||"api.netlify.com",NETLIFY_API_TOKEN:e||ye.env.NETLIFY_API_TOKEN,SITE_ID:t.siteId,FUNCTIONS_DIST:ye.env.FUNCTIONS_DIST||"netlify/functions"},utils:{run:O}});import{getTracer as Ut}from"@netlify/otel";import pi from"crypto";import K from"fs/promises";import Dt from"os";import C from"path";import X from"process";import{fileURLToPath as mi}from"url";var z=S("context"),gi=mi(import.meta.url),fi=C.dirname(gi),hi={claude:C.join(Dt.homedir(),".claude","skills"),gemini:C.join(Dt.homedir(),".agents","skills"),codex:C.join(Dt.homedir(),".agents","skills")},Ur=C.join(fi,"skills"),Mt=null;var Gr="NETLIFY_FF_AGENT_RUNNER_SKILL_",yi=()=>{let e=[];for(let[t,r]of Object.entries(X.env))t.startsWith(Gr)&&r&&e.push(t.slice(Gr.length).toLowerCase());return z.log(`Active skill variations: ${e.length?e.join(", "):"none"}`),e},jr=e=>{let t=e.match(/^(.+)@([^.]+)(.*)$/);return t?{baseName:`${t[1]}${t[3]}`,variation:t[2]}:{baseName:e,variation:null}},_i=async(e,t=[])=>{try{let r=await K.readdir(e);for(let i of r){let{baseName:n,variation:o}=jr(i);if(o&&n==="SKILL.md"&&t.includes(o))return C.join(e,i)}}catch{}return C.join(e,"SKILL.md")},Ei=async(e,{targetDir:t}={})=>{let r=t||hi[e];if(!r)return z.warn(`Unknown runner: ${e}, skipping skills setup`),[];if(Mt)return Mt;let i=[],n=yi();try{let o=await K.readdir(Ur);for(let s of o){let a=C.join(Ur,s);if(!(await K.stat(a)).isDirectory())continue;let{baseName:l,variation:d}=jr(s);if(d&&!n.includes(d))continue;let u=C.join(r,l);await K.mkdir(u,{recursive:!0});let p=await _i(a,n),g=C.join(u,"SKILL.md");try{await K.copyFile(p,g),i.push(l),d&&z.log(`Installed skill variation: ${l} (variation: ${d})`),C.basename(p)!=="SKILL.md"&&z.log(`Using skill variation for ${l}: ${C.basename(p)}`)}catch(T){z.warn(`Failed to copy skill ${l}:`,T.message)}}}catch(o){z.warn("Failed to setup agent skills:",o.message)}if(i.includes("netlify-ai-gateway"))try{let o=await Ve(),s=C.join(r,"netlify-ai-gateway","SKILL.md"),a=await K.readFile(s,"utf-8");if(a.includes("<!-- AVAILABLE_MODELS -->")){let c=Object.entries(o.providers).map(([l,d])=>`### ${l}
|
|
64
64
|
|
|
65
65
|
${d.models.map(u=>`- \`${u}\``).join(`
|
|
66
66
|
`)}`).join(`
|
package/dist/index.js
CHANGED
|
@@ -4,7 +4,7 @@ import{createRequire as vi}from"module";import{createTracerProvider as Kr}from"@
|
|
|
4
4
|
${s}
|
|
5
5
|
</extracted_error_chunk>`).join(`
|
|
6
6
|
|
|
7
|
-
`);return o.length>e.length*.8?e:o}import{execSync as Wn}from"child_process";import vr from"fs/promises";import Kn from"path";import he from"process";import{getTracer as Vn}from"@netlify/otel";import Pe from"process";var ee=class extends Error{constructor(r,i,n,o=!1){super(r);this.statusCode=i;this.userMessage=n;this.isCreditLimitExceeded=o;this.name="GracefulShutdownError"}},Ye=e=>e instanceof ee;var qe=Pe.env.NETLIFY_API_URL,Be=Pe.env.NETLIFY_API_TOKEN,B=I("api"),_e=()=>Pe.env.NETLIFY_LOCAL_MODE==="true",se=async(e,t={})=>{if(!qe||!Be)throw new Error("No API URL or token");let r=new URL(e,qe),i={...t,headers:{...t.headers,Authorization:`Bearer ${Be}`}};Pe.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(Pe.env.AGENT_RUNNERS_DEBUG==="true")B.log(`Response headers for ${r}:`),n.headers.forEach((a,c)=>{B.log(` ${c}: ${a}`)});else{let a=n.headers.get("x-request-id")||n.headers.get("x-nf-request-id");B.log(`Request ID for ${r}: ${a||"N/A"}`)}if(o||B.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 ee(`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 ee(`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=>{B.log("Setting details for api",{apiUrl:e?.constants?.NETLIFY_API_HOST,token:!!e?.constants?.NETLIFY_API_TOKEN}),e?.constants?.NETLIFY_API_HOST&&(qe=`https://${e.constants.NETLIFY_API_HOST}`),e?.constants?.NETLIFY_API_TOKEN&&(Be=e.constants.NETLIFY_API_TOKEN)},Yt=()=>({apiUrl:qe,token:Be}),Ne=async(e,t)=>_e()?(B.log("Mock API: updateRunner called",{runnerId:e,data:t}),{id:e,...t}):se(`/api/v1/agent_runners/${e}`,{method:"PUT",json:t}),H=async(e,t,r)=>_e()?(B.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=>_e()?(B.log("Mock API: getSite called",{siteId:e}),{id:e,published_deploy:{id:"id"}}):se(`/api/v1/sites/${e}`),Bt=async(e,t)=>_e()?(B.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)=>_e()?(B.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)=>_e()?(B.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"}),lt=async(e,t,{maxRetries:r=3,baseDelayMs:i=500}={})=>{B.log(`Uploading diff to S3: ${e.substring(0,50)}...`);for(let n=1;n<=r;n++)try{let o=await fetch(e,{method:"PUT",body:t,headers:{"Content-Type":"text/plain"}});if(!o.ok)throw new Error(`S3 upload failed with status ${o.status}`);return o}catch(o){if(n===r)throw o;let s=i*2**(n-1);B.warn(`S3 upload attempt ${n}/${r} failed: ${o.message}. Retrying in ${s}ms...`),await new Promise(a=>setTimeout(a,s))}};var Ee=I("ai_gateway"),ct=null;var He=async()=>{if(ct)return ct;Ee.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 ct=t,Ee.log("Cached AI gateway providers",{providerCount:Object.keys(t.providers).length}),t},Qr=async(e,t)=>{let i=(await He()).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},Jt=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?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,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(),He()]),{get url(){return n},get token(){return t},isModelAvailableForProvider:Qr}};import re from"process";import ie from"path";import Ve from"fs";import{fileURLToPath as ln}from"url";import{createRequire as cn}from"module";import{execa as un,execaCommand as oo}from"execa";import{Transform as en}from"stream";function tn(){let e=process.env.NETLIFY_SENSITIVE_ENV_KEYS;return e?e.split(",").map(t=>t.trim()).filter(Boolean):[]}function rn(e){let t=e.toLowerCase();return t==="true"||t==="false"?!0:e.trim().length<4}function nn(){let t=tn().map(r=>process.env[r]).filter(r=>!(!r||rn(r)));return[...new Set(t)].sort((r,i)=>i.length-r.length)}function ne(e){if(typeof e!="string")return e;let t=nn();if(t.length===0)return e;let r=e;return t.forEach(i=>{let n=new RegExp(on(i),"g");r=r.replace(n,"******")}),r}function on(e){return e.replace(/[.*+?^${}()|[\]\\]/g,"\\$&")}var Te=class extends en{constructor(t={}){super({...t,objectMode:!1})}_transform(t,r,i){let n=t.toString(),o=ne(n);i(null,o)}};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(i,n,o){let s=typeof i=="string"?ne(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"?ne(i):i;return typeof n=="function"?r(s,n):r(s,n,o)}}var $e=null,Xt=e=>($e&&$e.destroy(),$e=new ae({totalAllowedTime:e}),$e),Zt=()=>$e;var ae=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 Qt="netlify-agent-runner-context.md",ut="task-history",te=".netlify",pe="results.md",dt="assets";var er="free";var me=1800*1e3,sn=["\\.claude","\\.gemini","\\.codex","\\.agents"],We=new RegExp(`(^|/)(${sn.join("|")})/skills/`),Ke=/(^|\/)((CLAUDE|AGENTS|GEMINI)(\.local)?\.md)$/,f={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 tr={name:"@netlify/agent-runner-cli",type:"module",version:"1.94.0-netlifydb.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.81","@anthropic-ai/sdk":"0.78.0","@google/gemini-cli":"0.31.0","@netlify/otel":"^5.1.5","@netlify/ts-cli":"^1.0.4","@openai/codex":"0.115.0","@opentelemetry/exporter-trace-otlp-grpc":"0.57.2",execa:"^9.6.1",minimist:"^1.2.8",openai:"6.26.0"}};var dn=ln(import.meta.url),pn=ie.dirname(dn),mn=cn(import.meta.url),xe=I("shell"),pt=new Set,gn={preferLocal:!0},$=(e,t,r)=>{let[i,n]=fn(t,r),o={...gn,...n},s=un(e,i,o);hn(s,o),wn(s);let a=r?.idleTimeout;return a&&a>0&&yn(s,a),s};var fn=function(e,t){return Array.isArray(e)?[e,t]:typeof e=="object"&&e!==null?[[],e]:[[],void 0]},hn=(e,t)=>{if(t.stdio!==void 0||t.stdout!==void 0||t.stderr!==void 0)return;if(re.env.NETLIFY_MASK_LOGS!=="false"){e.all?.pipe(new Te).pipe(re.stdout),e.stdout?.pipe(new Te).pipe(re.stdout),e.stderr?.pipe(new Te).pipe(re.stderr);return}e.stdout?.pipe(re.stdout),e.stderr?.pipe(re.stderr)},mt=(e,t="SIGTERM")=>{try{return e.pid&&!e.killed?(re.kill(-e.pid,t),xe.log(`Killed process ${e.pid} with signal ${t}`),!0):!1}catch(r){return xe.error("Error killing process:",r),!1}},rr=e=>mt(e,"SIGKILL"),yn=(e,t)=>{let r=null,i=()=>{xe.log(`Process ${e.pid} killed due to idle timeout (no output for ${t}ms)`),mt(e,"SIGTERM"),setTimeout(()=>{e.pid&&!e.killed&&(xe.log(`Force killing idle process ${e.pid}`),rr(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)},wn=e=>{pt.add(e);let t=Zt();if(t){let r=t.onTimesUp(()=>{xe.log(`Global timer expired, killing process ${e.pid}`),mt(e,"SIGTERM"),setTimeout(()=>{e.pid&&!e.killed&&(xe.log(`Force killing process ${e.pid} after timeout`),rr(e))},5e3)});e.on("exit",()=>{pt.delete(e),r()}),e.on("error",()=>{pt.delete(e),r()})}};function le(e,t){if(!re.env.NETLIFY_LOCAL_MODE)try{let n=mn.resolve(tr.name),o=ie.dirname(n);for(;o!==ie.dirname(o);){let s=ie.dirname(o);if(ie.basename(s)==="node_modules"){let a=ie.join(s,".bin",t);if(Ve.existsSync(a))return a;break}o=s}}catch(n){console.error("Could not resolve package.json",n)}if(re.env.NODE_PATH){let n=ie.join(re.env.NODE_PATH,".bin",t);if(Ve.existsSync(n))return n}let r=ie.join(e,"node_modules",".bin",t);if(Ve.existsSync(r))return r;let i=ie.join(pn,"..","node_modules",".bin",t);if(Ve.existsSync(i))return i}var _n=I("utils"),En=e=>new Promise(t=>{setTimeout(t,e)}),Je=(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 En(t),!i)return r=!1,o=null,d;let u=i,p=n;i=null,n=[],d=await e(...u),p.forEach(g=>{g(d)})}})(),l};return s.flush=async()=>{if((r||i)&&o)return await o,s.flush()},s},Se=(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},nr=(e,t=!0,r)=>{if(e)try{return JSON.parse(e)}catch(i){t&&(r?.error?r.error("Could not parse JSON",i):_n.error("Could not parse JSON",i))}},gt=e=>e.charAt(0).toUpperCase()+e.slice(1),ce=e=>e.split("-").map(t=>t.length===2?t.toUpperCase():gt(t)).join(" ");function ge(e,t){t&&e.log(`Skill invoked: ${t}`)}var ir=e=>Object.fromEntries(Object.entries(e).filter(([,t])=>t!==void 0)),or=(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 Tn=1e4,ft=(e,t=Tn)=>{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 sr}from"buffer";import xn from"path";var ar=I("repo"),cr=async({config:e,isRetry:t,cwd:r=process.cwd()})=>{ar.info("Getting runner diffs");let i=await In(r),{hasChanges:n}=i,{status:o}=i;if(!n)return{hasChanges:!1};if(!t){let _=bn(o);await Rn(_,r)}ar.info("Changes after processing"),await yt(r);let s=await _t(o,r);if(await ht(s,r),n=await vn(r),!n)return{hasChanges:!1,ignored:s};process.env.NETLIFY_INTERNAL_GIT="1";try{await $("git",["commit","-m","Agent runner"],{cwd:r})}finally{process.env.NETLIFY_INTERNAL_GIT="0"}let a={stdio:["ignore","pipe","pipe"],cwd:r},c=await $("git",["diff",e.runSha,"HEAD"],a),l=String(c.stdout??"");if(n=!!l,!n)return await lr(r),{hasChanges:!1,ignored:s};let d=await $("git",["diff",e.runSha,"HEAD","--binary"],a),u=String(d.stdout??""),p,g;if(e.sha){let _=await $("git",["diff",e.sha,"HEAD"],a);p=String(_.stdout??"");let x=await $("git",["diff",e.sha,"HEAD","--binary"],a),w=String(x.stdout??"");p!==w&&(g=sr.from(w).toString("base64"))}await lr(r);let T={hasChanges:!0,diff:l,resultDiff:p,ignored:s};return l!==u&&(T.diffBinary=sr.from(u).toString("base64")),g&&(T.resultDiffBinary=g),T},lr=async(e=process.cwd())=>{process.env.NETLIFY_LOCAL_MODE&&await $("git",["reset","--soft","HEAD~1"],{cwd:e})},ht=async(e=[],t=process.cwd())=>{process.env.NETLIFY_INTERNAL_GIT="1";try{await $("git",["add",".",...e],{cwd:t})}finally{process.env.NETLIFY_INTERNAL_GIT="0"}},yt=async(e=process.cwd())=>{let t=await $("git",["status","-s"],{cwd:e});return String(t.stdout??"")},ur=/.. (.+)?\.log$/,Sn=[ur],In=async(e=process.cwd())=>{let t=await yt(e);return{hasChanges:(t.trim().length===0?[]:t.split(`
|
|
7
|
+
`);return o.length>e.length*.8?e:o}import{execSync as Wn}from"child_process";import vr from"fs/promises";import Kn from"path";import he from"process";import{getTracer as Vn}from"@netlify/otel";import Pe from"process";var ee=class extends Error{constructor(r,i,n,o=!1){super(r);this.statusCode=i;this.userMessage=n;this.isCreditLimitExceeded=o;this.name="GracefulShutdownError"}},Ye=e=>e instanceof ee;var qe=Pe.env.NETLIFY_API_URL,Be=Pe.env.NETLIFY_API_TOKEN,B=I("api"),_e=()=>Pe.env.NETLIFY_LOCAL_MODE==="true",se=async(e,t={})=>{if(!qe||!Be)throw new Error("No API URL or token");let r=new URL(e,qe),i={...t,headers:{...t.headers,Authorization:`Bearer ${Be}`}};Pe.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(Pe.env.AGENT_RUNNERS_DEBUG==="true")B.log(`Response headers for ${r}:`),n.headers.forEach((a,c)=>{B.log(` ${c}: ${a}`)});else{let a=n.headers.get("x-request-id")||n.headers.get("x-nf-request-id");B.log(`Request ID for ${r}: ${a||"N/A"}`)}if(o||B.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 ee(`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 ee(`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=>{B.log("Setting details for api",{apiUrl:e?.constants?.NETLIFY_API_HOST,token:!!e?.constants?.NETLIFY_API_TOKEN}),e?.constants?.NETLIFY_API_HOST&&(qe=`https://${e.constants.NETLIFY_API_HOST}`),e?.constants?.NETLIFY_API_TOKEN&&(Be=e.constants.NETLIFY_API_TOKEN)},Yt=()=>({apiUrl:qe,token:Be}),Ne=async(e,t)=>_e()?(B.log("Mock API: updateRunner called",{runnerId:e,data:t}),{id:e,...t}):se(`/api/v1/agent_runners/${e}`,{method:"PUT",json:t}),H=async(e,t,r)=>_e()?(B.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=>_e()?(B.log("Mock API: getSite called",{siteId:e}),{id:e,published_deploy:{id:"id"}}):se(`/api/v1/sites/${e}`),Bt=async(e,t)=>_e()?(B.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)=>_e()?(B.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)=>_e()?(B.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"}),lt=async(e,t,{maxRetries:r=3,baseDelayMs:i=500}={})=>{B.log(`Uploading diff to S3: ${e.substring(0,50)}...`);for(let n=1;n<=r;n++)try{let o=await fetch(e,{method:"PUT",body:t,headers:{"Content-Type":"text/plain"}});if(!o.ok)throw new Error(`S3 upload failed with status ${o.status}`);return o}catch(o){if(n===r)throw o;let s=i*2**(n-1);B.warn(`S3 upload attempt ${n}/${r} failed: ${o.message}. Retrying in ${s}ms...`),await new Promise(a=>setTimeout(a,s))}};var Ee=I("ai_gateway"),ct=null;var He=async()=>{if(ct)return ct;Ee.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 ct=t,Ee.log("Cached AI gateway providers",{providerCount:Object.keys(t.providers).length}),t},Qr=async(e,t)=>{let i=(await He()).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},Jt=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?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,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(),He()]),{get url(){return n},get token(){return t},isModelAvailableForProvider:Qr}};import re from"process";import ie from"path";import Ve from"fs";import{fileURLToPath as ln}from"url";import{createRequire as cn}from"module";import{execa as un,execaCommand as oo}from"execa";import{Transform as en}from"stream";function tn(){let e=process.env.NETLIFY_SENSITIVE_ENV_KEYS;return e?e.split(",").map(t=>t.trim()).filter(Boolean):[]}function rn(e){let t=e.toLowerCase();return t==="true"||t==="false"?!0:e.trim().length<4}function nn(){let t=tn().map(r=>process.env[r]).filter(r=>!(!r||rn(r)));return[...new Set(t)].sort((r,i)=>i.length-r.length)}function ne(e){if(typeof e!="string")return e;let t=nn();if(t.length===0)return e;let r=e;return t.forEach(i=>{let n=new RegExp(on(i),"g");r=r.replace(n,"******")}),r}function on(e){return e.replace(/[.*+?^${}()|[\]\\]/g,"\\$&")}var Te=class extends en{constructor(t={}){super({...t,objectMode:!1})}_transform(t,r,i){let n=t.toString(),o=ne(n);i(null,o)}};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(i,n,o){let s=typeof i=="string"?ne(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"?ne(i):i;return typeof n=="function"?r(s,n):r(s,n,o)}}var $e=null,Xt=e=>($e&&$e.destroy(),$e=new ae({totalAllowedTime:e}),$e),Zt=()=>$e;var ae=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 Qt="netlify-agent-runner-context.md",ut="task-history",te=".netlify",pe="results.md",dt="assets";var er="free";var me=1800*1e3,sn=["\\.claude","\\.gemini","\\.codex","\\.agents"],We=new RegExp(`(^|/)(${sn.join("|")})/skills/`),Ke=/(^|\/)((CLAUDE|AGENTS|GEMINI)(\.local)?\.md)$/,f={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 tr={name:"@netlify/agent-runner-cli",type:"module",version:"1.94.0-netlifydb.4",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.81","@anthropic-ai/sdk":"0.78.0","@google/gemini-cli":"0.31.0","@netlify/otel":"^5.1.5","@netlify/ts-cli":"^1.0.4","@openai/codex":"0.115.0","@opentelemetry/exporter-trace-otlp-grpc":"0.57.2",execa:"^9.6.1",minimist:"^1.2.8",openai:"6.26.0"}};var dn=ln(import.meta.url),pn=ie.dirname(dn),mn=cn(import.meta.url),xe=I("shell"),pt=new Set,gn={preferLocal:!0},$=(e,t,r)=>{let[i,n]=fn(t,r),o={...gn,...n},s=un(e,i,o);hn(s,o),wn(s);let a=r?.idleTimeout;return a&&a>0&&yn(s,a),s};var fn=function(e,t){return Array.isArray(e)?[e,t]:typeof e=="object"&&e!==null?[[],e]:[[],void 0]},hn=(e,t)=>{if(t.stdio!==void 0||t.stdout!==void 0||t.stderr!==void 0)return;if(re.env.NETLIFY_MASK_LOGS!=="false"){e.all?.pipe(new Te).pipe(re.stdout),e.stdout?.pipe(new Te).pipe(re.stdout),e.stderr?.pipe(new Te).pipe(re.stderr);return}e.stdout?.pipe(re.stdout),e.stderr?.pipe(re.stderr)},mt=(e,t="SIGTERM")=>{try{return e.pid&&!e.killed?(re.kill(-e.pid,t),xe.log(`Killed process ${e.pid} with signal ${t}`),!0):!1}catch(r){return xe.error("Error killing process:",r),!1}},rr=e=>mt(e,"SIGKILL"),yn=(e,t)=>{let r=null,i=()=>{xe.log(`Process ${e.pid} killed due to idle timeout (no output for ${t}ms)`),mt(e,"SIGTERM"),setTimeout(()=>{e.pid&&!e.killed&&(xe.log(`Force killing idle process ${e.pid}`),rr(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)},wn=e=>{pt.add(e);let t=Zt();if(t){let r=t.onTimesUp(()=>{xe.log(`Global timer expired, killing process ${e.pid}`),mt(e,"SIGTERM"),setTimeout(()=>{e.pid&&!e.killed&&(xe.log(`Force killing process ${e.pid} after timeout`),rr(e))},5e3)});e.on("exit",()=>{pt.delete(e),r()}),e.on("error",()=>{pt.delete(e),r()})}};function le(e,t){if(!re.env.NETLIFY_LOCAL_MODE)try{let n=mn.resolve(tr.name),o=ie.dirname(n);for(;o!==ie.dirname(o);){let s=ie.dirname(o);if(ie.basename(s)==="node_modules"){let a=ie.join(s,".bin",t);if(Ve.existsSync(a))return a;break}o=s}}catch(n){console.error("Could not resolve package.json",n)}if(re.env.NODE_PATH){let n=ie.join(re.env.NODE_PATH,".bin",t);if(Ve.existsSync(n))return n}let r=ie.join(e,"node_modules",".bin",t);if(Ve.existsSync(r))return r;let i=ie.join(pn,"..","node_modules",".bin",t);if(Ve.existsSync(i))return i}var _n=I("utils"),En=e=>new Promise(t=>{setTimeout(t,e)}),Je=(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 En(t),!i)return r=!1,o=null,d;let u=i,p=n;i=null,n=[],d=await e(...u),p.forEach(g=>{g(d)})}})(),l};return s.flush=async()=>{if((r||i)&&o)return await o,s.flush()},s},Se=(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},nr=(e,t=!0,r)=>{if(e)try{return JSON.parse(e)}catch(i){t&&(r?.error?r.error("Could not parse JSON",i):_n.error("Could not parse JSON",i))}},gt=e=>e.charAt(0).toUpperCase()+e.slice(1),ce=e=>e.split("-").map(t=>t.length===2?t.toUpperCase():gt(t)).join(" ");function ge(e,t){t&&e.log(`Skill invoked: ${t}`)}var ir=e=>Object.fromEntries(Object.entries(e).filter(([,t])=>t!==void 0)),or=(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 Tn=1e4,ft=(e,t=Tn)=>{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 sr}from"buffer";import xn from"path";var ar=I("repo"),cr=async({config:e,isRetry:t,cwd:r=process.cwd()})=>{ar.info("Getting runner diffs");let i=await In(r),{hasChanges:n}=i,{status:o}=i;if(!n)return{hasChanges:!1};if(!t){let _=bn(o);await Rn(_,r)}ar.info("Changes after processing"),await yt(r);let s=await _t(o,r);if(await ht(s,r),n=await vn(r),!n)return{hasChanges:!1,ignored:s};process.env.NETLIFY_INTERNAL_GIT="1";try{await $("git",["commit","-m","Agent runner"],{cwd:r})}finally{process.env.NETLIFY_INTERNAL_GIT="0"}let a={stdio:["ignore","pipe","pipe"],cwd:r},c=await $("git",["diff",e.runSha,"HEAD"],a),l=String(c.stdout??"");if(n=!!l,!n)return await lr(r),{hasChanges:!1,ignored:s};let d=await $("git",["diff",e.runSha,"HEAD","--binary"],a),u=String(d.stdout??""),p,g;if(e.sha){let _=await $("git",["diff",e.sha,"HEAD"],a);p=String(_.stdout??"");let x=await $("git",["diff",e.sha,"HEAD","--binary"],a),w=String(x.stdout??"");p!==w&&(g=sr.from(w).toString("base64"))}await lr(r);let T={hasChanges:!0,diff:l,resultDiff:p,ignored:s};return l!==u&&(T.diffBinary=sr.from(u).toString("base64")),g&&(T.resultDiffBinary=g),T},lr=async(e=process.cwd())=>{process.env.NETLIFY_LOCAL_MODE&&await $("git",["reset","--soft","HEAD~1"],{cwd:e})},ht=async(e=[],t=process.cwd())=>{process.env.NETLIFY_INTERNAL_GIT="1";try{await $("git",["add",".",...e],{cwd:t})}finally{process.env.NETLIFY_INTERNAL_GIT="0"}},yt=async(e=process.cwd())=>{let t=await $("git",["status","-s"],{cwd:e});return String(t.stdout??"")},ur=/.. (.+)?\.log$/,Sn=[ur],In=async(e=process.cwd())=>{let t=await yt(e);return{hasChanges:(t.trim().length===0?[]:t.split(`
|
|
8
8
|
`).filter(n=>Sn.some(s=>s instanceof RegExp?s.test(n):n===s)?!1:n[1]?.trim()!=="")).length!==0,status:t}},vn=async(e=process.cwd())=>{try{return await $("git",["diff","--staged","--quiet"],{cwd:e}),!1}catch{return!0}},wt=async(e=process.cwd())=>{let{stdout:t}=await $("git",["rev-parse","HEAD"],{cwd:e});return String(t??"").trim()},dr=async(e=process.cwd())=>{let{stdout:t}=await $("git",["rev-list","--max-parents=0","HEAD"],{cwd:e});return String(t??"").trim()},_t=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(`
|
|
9
9
|
`).forEach(n=>{r.forEach(s=>{let a=n===`?? ${s}`,c=n.startsWith(`?? ${s}/`)||n.startsWith(`?? ${s}${xn.sep}`);(a||c)&&i.push(`:!${s}`)});let o=n.match(ur)?.[1];o&&i.push(`:!${o}.log`)}),i},Et=async(e=process.cwd())=>{await $("git",["reset","--hard","HEAD"],{cwd:e})},bn=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)},Rn=async(e,t=process.cwd())=>{let r=e.filter(i=>i.stage&&!i.change).map(i=>i.filePath);r.length!==0&&await $("git",["restore","--staged","--worktree","--pathspec-from-file=-"],{cwd:t,input:r.join(`
|
|
@@ -59,7 +59,7 @@ case "$1" in
|
|
|
59
59
|
exec ${e} "$@"
|
|
60
60
|
;;
|
|
61
61
|
esac
|
|
62
|
-
`;try{await vr.mkdir(t,{recursive:!0}),await vr.writeFile(r,i,{mode:493}),he.env.PATH=`${t}:${he.env.PATH}`,he.env.NETLIFY_INTERNAL_GIT="0",Me.info("Installed git wrapper to block add/commit commands")}catch(n){Me.warn("Failed to install git wrapper",{error:n?.message||n})}},zn=({apiToken:e,config:t})=>({constants:{NETLIFY_API_HOST:he.env.NETLIFY_API_HOST||"api.netlify.com",NETLIFY_API_TOKEN:e||he.env.NETLIFY_API_TOKEN,SITE_ID:t.siteId,FUNCTIONS_DIST:he.env.FUNCTIONS_DIST||"netlify/functions"},utils:{run:$}});import{getTracer as Nt}from"@netlify/otel";import Xn from"crypto";import W from"fs/promises";import kt from"os";import C from"path";import z from"process";import{fileURLToPath as Zn}from"url";var J=I("context"),Qn=Zn(import.meta.url),ei=C.dirname(Qn),ti={claude:C.join(kt.homedir(),".claude","skills"),gemini:C.join(kt.homedir(),".agents","skills"),codex:C.join(kt.homedir(),".agents","skills")},Rr=C.join(ei,"skills"),Pt=null;var Ar="NETLIFY_FF_AGENT_RUNNER_SKILL_",ri=()=>{let e=[];for(let[t,r]of Object.entries(z.env))t.startsWith(Ar)&&r&&e.push(t.slice(Ar.length).toLowerCase());return e},Cr=e=>{let t=e.match(/^(.+)@([^.]+)(.*)$/);return t?{baseName:`${t[1]}${t[3]}`,variation:t[2]}:{baseName:e,variation:null}},ni=async(e,t=[])=>{try{let r=await W.readdir(e);for(let i of r){let{baseName:n,variation:o}=Cr(i);if(o&&n==="SKILL.md"&&t.includes(o))return C.join(e,i)}}catch{}return C.join(e,"SKILL.md")},ii=async(e,{targetDir:t}={})=>{let r=t||ti[e];if(!r)return J.warn(`Unknown runner: ${e}, skipping skills setup`),[];if(Pt)return Pt;let i=[],n=ri();try{let o=await W.readdir(Rr);for(let s of o){let a=C.join(Rr,s);if(!(await W.stat(a)).isDirectory())continue;let{baseName:l,variation:d}=Cr(s);if(d&&!n.includes(d))continue;let u=C.join(r,l);await W.mkdir(u,{recursive:!0});let p=await ni(a,n),g=C.join(u,"SKILL.md");try{await W.copyFile(p,g),i.push(l),d&&J.log(`Installed skill variation: ${l} (variation: ${d})`),C.basename(p)!=="SKILL.md"&&J.log(`Using skill variation for ${l}: ${C.basename(p)}`)}catch(T){J.warn(`Failed to copy skill ${l}:`,T.message)}}}catch(o){J.warn("Failed to setup agent skills:",o.message)}if(i.includes("netlify-ai-gateway"))try{let o=await He(),s=C.join(r,"netlify-ai-gateway","SKILL.md"),a=await W.readFile(s,"utf-8");if(a.includes("<!-- AVAILABLE_MODELS -->")){let c=Object.entries(o.providers).map(([l,d])=>`### ${l}
|
|
62
|
+
`;try{await vr.mkdir(t,{recursive:!0}),await vr.writeFile(r,i,{mode:493}),he.env.PATH=`${t}:${he.env.PATH}`,he.env.NETLIFY_INTERNAL_GIT="0",Me.info("Installed git wrapper to block add/commit commands")}catch(n){Me.warn("Failed to install git wrapper",{error:n?.message||n})}},zn=({apiToken:e,config:t})=>({constants:{NETLIFY_API_HOST:he.env.NETLIFY_API_HOST||"api.netlify.com",NETLIFY_API_TOKEN:e||he.env.NETLIFY_API_TOKEN,SITE_ID:t.siteId,FUNCTIONS_DIST:he.env.FUNCTIONS_DIST||"netlify/functions"},utils:{run:$}});import{getTracer as Nt}from"@netlify/otel";import Xn from"crypto";import W from"fs/promises";import kt from"os";import C from"path";import z from"process";import{fileURLToPath as Zn}from"url";var J=I("context"),Qn=Zn(import.meta.url),ei=C.dirname(Qn),ti={claude:C.join(kt.homedir(),".claude","skills"),gemini:C.join(kt.homedir(),".agents","skills"),codex:C.join(kt.homedir(),".agents","skills")},Rr=C.join(ei,"skills"),Pt=null;var Ar="NETLIFY_FF_AGENT_RUNNER_SKILL_",ri=()=>{let e=[];for(let[t,r]of Object.entries(z.env))t.startsWith(Ar)&&r&&e.push(t.slice(Ar.length).toLowerCase());return J.log(`Active skill variations: ${e.length?e.join(", "):"none"}`),e},Cr=e=>{let t=e.match(/^(.+)@([^.]+)(.*)$/);return t?{baseName:`${t[1]}${t[3]}`,variation:t[2]}:{baseName:e,variation:null}},ni=async(e,t=[])=>{try{let r=await W.readdir(e);for(let i of r){let{baseName:n,variation:o}=Cr(i);if(o&&n==="SKILL.md"&&t.includes(o))return C.join(e,i)}}catch{}return C.join(e,"SKILL.md")},ii=async(e,{targetDir:t}={})=>{let r=t||ti[e];if(!r)return J.warn(`Unknown runner: ${e}, skipping skills setup`),[];if(Pt)return Pt;let i=[],n=ri();try{let o=await W.readdir(Rr);for(let s of o){let a=C.join(Rr,s);if(!(await W.stat(a)).isDirectory())continue;let{baseName:l,variation:d}=Cr(s);if(d&&!n.includes(d))continue;let u=C.join(r,l);await W.mkdir(u,{recursive:!0});let p=await ni(a,n),g=C.join(u,"SKILL.md");try{await W.copyFile(p,g),i.push(l),d&&J.log(`Installed skill variation: ${l} (variation: ${d})`),C.basename(p)!=="SKILL.md"&&J.log(`Using skill variation for ${l}: ${C.basename(p)}`)}catch(T){J.warn(`Failed to copy skill ${l}:`,T.message)}}}catch(o){J.warn("Failed to setup agent skills:",o.message)}if(i.includes("netlify-ai-gateway"))try{let o=await He(),s=C.join(r,"netlify-ai-gateway","SKILL.md"),a=await W.readFile(s,"utf-8");if(a.includes("<!-- AVAILABLE_MODELS -->")){let c=Object.entries(o.providers).map(([l,d])=>`### ${l}
|
|
63
63
|
|
|
64
64
|
${d.models.map(u=>`- \`${u}\``).join(`
|
|
65
65
|
`)}`).join(`
|
package/package.json
CHANGED