@netlify/agent-runner-cli 1.69.3 → 1.71.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,478 @@
1
+ ---
2
+ name: netlify-serverless
3
+ description: Create and deploy Netlify Functions including standard, background, and scheduled functions. Use when implementing API endpoints, server-side logic, background processing, or scheduled tasks.
4
+ ---
5
+
6
+ # Netlify Functions
7
+
8
+ Netlify Functions are serverless API endpoints and background workers that deploy alongside your site. They use
9
+ standard Web API Request/Response objects with zero configuration required.
10
+
11
+ ## Quick Start
12
+
13
+ Create a function file in the `netlify/functions/` directory:
14
+
15
+ ```typescript
16
+ // netlify/functions/hello.mts
17
+ export default async (req: Request) => {
18
+ const name = new URL(req.url).searchParams.get('name') || 'world'
19
+ return new Response(`Hello, ${name}!`)
20
+ }
21
+ ```
22
+
23
+ ```javascript
24
+ // netlify/functions/hello.mjs
25
+ export default async (req) => {
26
+ const name = new URL(req.url).searchParams.get('name') || 'world'
27
+ return new Response(`Hello, ${name}!`)
28
+ }
29
+ ```
30
+
31
+ Deploy and invoke at `/.netlify/functions/hello?name=Netlify`.
32
+
33
+ ALWAYS use TypeScript (`.mts`) if other functions in the project are TypeScript or if there are no existing functions.
34
+ ONLY use vanilla JavaScript if there are other `.js` files in the functions directory. DO NOT put global logic outside
35
+ of the exported function unless wrapped in a function definition. ALWAYS use the latest format of a function structure.
36
+
37
+ Serverless functions use Node.js and should attempt to use built-in methods where possible.
38
+
39
+ When adding new npm modules, ensure `node_modules` is in the `.gitignore`. If using TypeScript, ensure types are
40
+ installed from `npm install @netlify/functions`.
41
+
42
+ ## Function Structure & Placement
43
+
44
+ **Default directory:** `netlify/functions/` (relative to site base directory)
45
+
46
+ **Naming conventions:**
47
+ - Use `.mts` (TypeScript, recommended) or `.mjs` (JavaScript) extensions. Naming files with `.mts` enables modern ES module syntax
48
+ - Filename becomes the function name and default URL path
49
+ - One default export per file
50
+
51
+ **Valid function paths:**
52
+ - `netlify/functions/hello.mts`
53
+ - `netlify/functions/hello/index.mts`
54
+ - `netlify/functions/hello/hello.mts`
55
+
56
+ **Custom directory** via `netlify.toml`:
57
+
58
+ ```toml
59
+ [functions]
60
+ directory = "my_functions"
61
+ ```
62
+
63
+ ## Handler Signature
64
+
65
+ ```typescript
66
+ export default async (req: Request, context: Context) => Response | void
67
+ ```
68
+
69
+ The handler receives a standard [Web API Request](https://developer.mozilla.org/en-US/docs/Web/API/Request) and a
70
+ Netlify-specific `Context` object. Return a standard
71
+ [Web API Response](https://developer.mozilla.org/en-US/docs/Web/API/Response), or return nothing for a 204 response.
72
+
73
+ ```typescript
74
+ import type { Context } from '@netlify/functions'
75
+
76
+ export default async (req: Request, context: Context) => {
77
+ const body = await req.json()
78
+ return Response.json({ received: body })
79
+ }
80
+ ```
81
+
82
+ ## In-Code Config & Routing
83
+
84
+ Prefer to use in-code configuration via exporting a `config` object. Prefer to provide a friendly path using the config
85
+ object. ONLY serverless functions use `/.netlify/functions/{function_name}` path by default. If you set a specific path
86
+ via config or netlify.toml, it will only be available at that new path.
87
+
88
+ Export a `config` object to configure routing and behavior:
89
+
90
+ ```typescript
91
+ import type { Config } from '@netlify/functions'
92
+
93
+ export default async (req: Request, context: Context) => {
94
+ const { name } = context.params
95
+ return Response.json({ pet: name })
96
+ }
97
+
98
+ export const config: Config = {
99
+ path: '/api/pets/:name',
100
+ }
101
+ ```
102
+
103
+ **Config properties:**
104
+
105
+ | Property | Type | Description |
106
+ |----------|------|-------------|
107
+ | `path` | `string \| string[]` | Custom URL path(s) with route parameters |
108
+ | `excludedPath` | `string \| string[]` | Paths to exclude from matching |
109
+ | `preferStatic` | `boolean` | If `true`, static file takes precedence over function |
110
+ | `method` | `string \| string[]` | HTTP method(s) to match (`GET`, `POST`, etc.) |
111
+ | `schedule` | `string` | Cron expression for scheduled functions |
112
+
113
+ `path` and `excludedPath` support substring patterns or the URLPattern syntax from the web platform.
114
+
115
+ **Route parameters:**
116
+
117
+ ```typescript
118
+ // :param - named parameter
119
+ export const config: Config = { path: '/api/users/:id' }
120
+ // context.params.id = '123' for /api/users/123
121
+
122
+ // * - wildcard
123
+ export const config: Config = { path: '/api/*' }
124
+ // Matches /api/anything/here
125
+ ```
126
+
127
+ **Multiple paths:**
128
+
129
+ ```typescript
130
+ export const config: Config = {
131
+ path: ['/api/pets/:name', '/api/animals/:name'],
132
+ }
133
+ ```
134
+
135
+ ## Context Object
136
+
137
+ The `context` parameter provides Netlify-specific metadata:
138
+
139
+ ### `context.ip`
140
+
141
+ Client IP address as a string.
142
+
143
+ ### `context.geo`
144
+
145
+ Geolocation data for the client:
146
+
147
+ ```typescript
148
+ context.geo.city // string - city name
149
+ context.geo.country // { code: string, name: string } - ISO 3166
150
+ context.geo.latitude // number
151
+ context.geo.longitude // number
152
+ context.geo.subdivision // { code: string, name: string } - ISO 3166
153
+ context.geo.timezone // string - IANA timezone
154
+ context.geo.postalCode // string
155
+ ```
156
+
157
+ ### `context.site`
158
+
159
+ Site metadata:
160
+
161
+ ```typescript
162
+ context.site.id // string - unique site ID
163
+ context.site.name // string - site subdomain name
164
+ context.site.url // string - primary site URL
165
+ ```
166
+
167
+ ### `context.deploy`
168
+
169
+ Deploy metadata:
170
+
171
+ ```typescript
172
+ context.deploy.context // string - deploy context (e.g., 'production', 'deploy-preview')
173
+ context.deploy.id // string - unique deploy ID
174
+ context.deploy.published // boolean - is this the published deploy?
175
+ ```
176
+
177
+ ### `context.account`
178
+
179
+ ```typescript
180
+ context.account.id // string - team/account ID
181
+ ```
182
+
183
+ ### `context.requestId`
184
+
185
+ Unique request identifier string.
186
+
187
+ ### `context.params`
188
+
189
+ Route parameters from `config.path`:
190
+
191
+ ```typescript
192
+ // For path '/api/users/:id' and request '/api/users/42':
193
+ context.params.id // '42'
194
+ ```
195
+
196
+ ### `context.cookies`
197
+
198
+ Simplified cookie interface:
199
+
200
+ ```typescript
201
+ // Read
202
+ const session = context.cookies.get('session')
203
+
204
+ // Set
205
+ context.cookies.set({ name: 'session', value: 'abc123', path: '/', httpOnly: true })
206
+
207
+ // Delete
208
+ context.cookies.delete('session')
209
+ ```
210
+
211
+ ### `context.server`
212
+
213
+ ```typescript
214
+ context.server.region // string - e.g., 'us-east-1'
215
+ ```
216
+
217
+ ### `context.waitUntil(promise)`
218
+
219
+ Extends execution after the response is sent. Use for analytics, logging, or cleanup tasks.
220
+
221
+ ```typescript
222
+ export default async (req: Request, context: Context) => {
223
+ context.waitUntil(
224
+ fetch('https://analytics.example.com/track', {
225
+ method: 'POST',
226
+ body: JSON.stringify({ path: new URL(req.url).pathname }),
227
+ }),
228
+ )
229
+ return new Response('OK')
230
+ }
231
+ ```
232
+
233
+ ## Global `Netlify` Object
234
+
235
+ Available in all function contexts:
236
+
237
+ ### `Netlify.env`
238
+
239
+ Access environment variables:
240
+
241
+ ```typescript
242
+ const apiKey = Netlify.env.get('API_KEY') // string | undefined
243
+ const exists = Netlify.env.has('API_KEY') // boolean
244
+ const all = Netlify.env.toObject() // Record<string, string>
245
+
246
+ // Scoped to current invocation only
247
+ Netlify.env.set('TEMP_VAR', 'value')
248
+ Netlify.env.delete('TEMP_VAR')
249
+ ```
250
+
251
+ ALWAYS use `Netlify.env.get()` instead of `process.env` when reading environment variables in Netlify Functions. It
252
+ provides consistent behavior across all function contexts.
253
+
254
+ ### `Netlify.context`
255
+
256
+ Same as the `context` parameter in the handler. Available from outside the handler scope (e.g., imported modules).
257
+ Returns `null` outside a function invocation.
258
+
259
+ ## Background Functions
260
+
261
+ Background functions operate the same as standard serverless functions and are syntactically the same. They run for up
262
+ to 15 minutes without blocking the client. The client receives an immediate 202 response.
263
+
264
+ **Naming convention:** Append `-background` to the filename.
265
+
266
+ ```typescript
267
+ // netlify/functions/process-data-background.mts
268
+ import type { Context } from '@netlify/functions'
269
+
270
+ export default async (req: Request, context: Context) => {
271
+ const data = await req.json()
272
+
273
+ // Long-running work (up to 15 minutes)
274
+ for (const item of data.items) {
275
+ await processItem(item)
276
+ }
277
+
278
+ // Return value is ignored - client already received 202
279
+ }
280
+ ```
281
+
282
+ ```javascript
283
+ // netlify/functions/process-data-background.mjs
284
+ export default async (req, context) => {
285
+ const data = await req.json()
286
+
287
+ for (const item of data.items) {
288
+ await processItem(item)
289
+ }
290
+ }
291
+ ```
292
+
293
+ **Key behavior:**
294
+ - Client receives `202 Accepted` immediately
295
+ - Function continues executing for up to 15 minutes
296
+ - Response body is ignored
297
+ - No streaming support
298
+ - Cannot use `config.path` or `config.schedule`
299
+ - Retry logic: first retry after 1 minute, second retry after 2 minutes on failure
300
+ - Any data background functions produce should be stored in Netlify Blobs or a database
301
+
302
+ **Valid background function paths:**
303
+ - `netlify/functions/hello-background.mts`
304
+ - `netlify/functions/hello-background/index.mts`
305
+
306
+ Invoke like any function: `POST /.netlify/functions/process-data-background`
307
+
308
+ ## Scheduled Functions
309
+
310
+ Run functions on a cron schedule. Use for periodic tasks like data sync, cleanup, or report generation.
311
+
312
+ ```typescript
313
+ // netlify/functions/daily-cleanup.mts
314
+ import type { Config } from '@netlify/functions'
315
+
316
+ export default async (req: Request) => {
317
+ const { next_run } = await req.json()
318
+ console.log('Running cleanup. Next run:', next_run)
319
+
320
+ await performCleanup()
321
+ }
322
+
323
+ export const config: Config = {
324
+ schedule: '@daily',
325
+ }
326
+ ```
327
+
328
+ ```javascript
329
+ // netlify/functions/daily-cleanup.mjs
330
+ export default async (req) => {
331
+ const { next_run } = await req.json()
332
+ console.log('Running cleanup. Next run:', next_run)
333
+
334
+ await performCleanup()
335
+ }
336
+
337
+ export const config = {
338
+ schedule: '@daily',
339
+ }
340
+ ```
341
+
342
+ The minimum interval is 1 minute. Scheduled functions do not return response bodies.
343
+
344
+ **Cron syntax** (UTC timezone):
345
+
346
+ ```
347
+ ┌───────────── minute (0-59)
348
+ │ ┌───────────── hour (0-23)
349
+ │ │ ┌───────────── day of month (1-31)
350
+ │ │ │ ┌───────────── month (1-12)
351
+ │ │ │ │ ┌───────────── day of week (0-6, Sunday=0)
352
+ │ │ │ │ │
353
+ * * * * *
354
+ ```
355
+
356
+ **Supported extensions:**
357
+
358
+ | Extension | Equivalent | Description |
359
+ |-----------|------------|-------------|
360
+ | `@yearly` | `0 0 1 1 *` | January 1st at midnight |
361
+ | `@monthly` | `0 0 1 * *` | 1st of each month at midnight |
362
+ | `@weekly` | `0 0 * * 0` | Sunday at midnight |
363
+ | `@daily` | `0 0 * * *` | Every day at midnight |
364
+ | `@hourly` | `0 * * * *` | Every hour at minute 0 |
365
+
366
+ Cron syntax supports extensions defined in the RFC except for `@reboot` and `@annually`.
367
+
368
+ **Alternative:** Configure schedule in `netlify.toml` instead of in-code config. ONLY do this for consistency or if
369
+ explicitly asked to keep all schedules in one place:
370
+
371
+ ```toml
372
+ [functions."daily-cleanup"]
373
+ schedule = "@daily"
374
+ ```
375
+
376
+ **Key behavior:**
377
+ - Scheduled functions only run on **published deploys** (not deploy previews or branch deploys)
378
+ - Request body is `{ "next_run": "<ISO-8601 timestamp>" }`
379
+ - 30-second execution time limit (use background functions for longer tasks)
380
+ - No direct URL invocation in production
381
+
382
+ **Local testing:**
383
+
384
+ ```bash
385
+ netlify functions:invoke daily-cleanup
386
+ ```
387
+
388
+ ## Response Streaming
389
+
390
+ Synchronous functions can stream responses for large payloads (up to 20 MB):
391
+
392
+ ```typescript
393
+ export default async (req: Request) => {
394
+ const stream = new ReadableStream({
395
+ start(controller) {
396
+ controller.enqueue(new TextEncoder().encode('Hello '))
397
+ controller.enqueue(new TextEncoder().encode('World'))
398
+ controller.close()
399
+ },
400
+ })
401
+
402
+ return new Response(stream, {
403
+ headers: { 'Content-Type': 'text/plain' },
404
+ })
405
+ }
406
+ ```
407
+
408
+ ## Limits
409
+
410
+ | Resource | Limit |
411
+ |----------|-------|
412
+ | Memory | 1024 MB |
413
+ | Synchronous execution time | 60 seconds |
414
+ | Scheduled execution time | 30 seconds |
415
+ | Background execution time | 15 minutes |
416
+ | Buffered request/response payload | 6 MB |
417
+ | Background function payload | 256 KB |
418
+ | Streaming response payload | 20 MB |
419
+ | Functions per site | Unlimited |
420
+
421
+ **Default deployment region:** `us-east-2` (configurable on Pro/Enterprise plans)
422
+
423
+ ## Common Errors & Solutions
424
+
425
+ ### "Function not found" (404)
426
+
427
+ **Cause:** Function file not in the correct directory or wrong extension.
428
+
429
+ **Fix:**
430
+
431
+ 1. Place function in `netlify/functions/` (or configured directory)
432
+ 2. Use `.mts` or `.mjs` extension (not `.ts` or `.js`)
433
+ 3. Ensure the file has a default export
434
+
435
+ ### "Function timeout" (502 or 504)
436
+
437
+ **Cause:** Function exceeds execution time limit.
438
+
439
+ **Fix:**
440
+
441
+ 1. Synchronous functions: optimize to complete within 60 seconds
442
+ 2. For long-running tasks, use background functions (`-background` suffix, 15-min limit)
443
+ 3. For periodic tasks, use scheduled functions
444
+
445
+ ### "Payload too large" (413)
446
+
447
+ **Cause:** Request or response exceeds 6 MB limit.
448
+
449
+ **Fix:**
450
+
451
+ 1. Reduce payload size
452
+ 2. For large file handling, use Netlify Blobs for storage and pass references instead of raw data
453
+ 3. Use streaming responses for large outputs (up to 20 MB)
454
+
455
+ ### "CORS errors" in browser
456
+
457
+ **Cause:** Missing CORS headers on function response. NEVER add CORS headers unless explicitly requested by the user.
458
+
459
+ **Fix** (only when user asks for CORS support):
460
+
461
+ ```typescript
462
+ return new Response(JSON.stringify(data), {
463
+ headers: {
464
+ 'Content-Type': 'application/json',
465
+ 'Access-Control-Allow-Origin': '*',
466
+ },
467
+ })
468
+ ```
469
+
470
+ ### Environment variable is undefined
471
+
472
+ **Cause:** Variable not available in the function context.
473
+
474
+ **Fix:**
475
+
476
+ 1. Use `Netlify.env.get('VAR_NAME')` instead of `process.env.VAR_NAME`
477
+ 2. Ensure the variable is set in Netlify UI with correct scopes (Runtime)
478
+ 3. Deploy to Netlify or use the Vite plugin for env var injection
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@netlify/agent-runner-cli",
3
3
  "type": "module",
4
- "version": "1.69.3",
4
+ "version": "1.71.0",
5
5
  "description": "CLI tool for running Netlify agents",
6
6
  "main": "./dist/index.js",
7
7
  "types": "./dist/index.d.ts",