@netlify/agent-runner-cli 1.102.0-test.0 → 1.103.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.
@@ -47,7 +47,7 @@ import Anthropic from '@anthropic-ai/sdk'
47
47
  const anthropic = new Anthropic()
48
48
 
49
49
  const message = await anthropic.messages.create({
50
- model: 'claude-opus-4-6',
50
+ model: 'claude-opus-4-7',
51
51
  max_tokens: 1024,
52
52
  messages: [{ role: 'user', content: 'Hello!' }],
53
53
  })
@@ -118,7 +118,7 @@ import Anthropic from '@anthropic-ai/sdk'
118
118
  const anthropic = new Anthropic()
119
119
 
120
120
  const stream = await anthropic.messages.create({
121
- model: 'claude-opus-4-6',
121
+ model: 'claude-opus-4-7',
122
122
  max_tokens: 1024,
123
123
  messages: [{ role: 'user', content: 'Tell me a story.' }],
124
124
  stream: true,
@@ -218,7 +218,7 @@ export default async (req: Request, context: Context) => {
218
218
  const { prompt } = await req.json()
219
219
 
220
220
  const message = await anthropic.messages.create({
221
- model: 'claude-opus-4-6',
221
+ model: 'claude-opus-4-7',
222
222
  max_tokens: 1024,
223
223
  messages: [{ role: 'user', content: prompt }],
224
224
  })
@@ -309,7 +309,7 @@ const response = await fetch(`${process.env.NETLIFY_AI_GATEWAY_BASE_URL}/anthrop
309
309
  'anthropic-version': '2023-06-01',
310
310
  },
311
311
  body: JSON.stringify({
312
- model: 'claude-opus-4-6',
312
+ model: 'claude-opus-4-7',
313
313
  max_tokens: 1024,
314
314
  messages: [{ role: 'user', content: 'Hello!' }],
315
315
  }),
@@ -329,7 +329,7 @@ const response = await fetch(`${process.env.ANTHROPIC_BASE_URL}/v1/messages`, {
329
329
  'anthropic-version': '2023-06-01',
330
330
  },
331
331
  body: JSON.stringify({
332
- model: 'claude-opus-4-6',
332
+ model: 'claude-opus-4-7',
333
333
  max_tokens: 1024,
334
334
  messages: [{ role: 'user', content: 'Hello!' }],
335
335
  }),
@@ -0,0 +1,421 @@
1
+ ---
2
+ name: netlify-blobs
3
+ description: Store and retrieve files and other unstructured data using Netlify Blobs object storage. Use for uploaded files, images, video, pre-rendered content, cached responses, or large JSON documents that do not need relational querying. For any database-like use cases (records, collections, queryable application state), use Netlify Database instead.
4
+ ---
5
+
6
+ # Netlify Blobs
7
+
8
+ Netlify Blobs is a zero-configuration object store optimized for frequent reads and infrequent writes. It provides
9
+ persistent storage scoped to your site with automatic provisioning and access control. The API will be the same across
10
+ all compute types (serverless functions, edge functions, build plugins, etc.).
11
+
12
+ Requires Fetch API support (Node.js 18+ recommended).
13
+
14
+ ## When to use Netlify Blobs
15
+
16
+ Use Netlify Blobs for **unstructured object data** — things that are naturally a file or a self-contained blob:
17
+
18
+ - Uploaded files (user uploads, documents, attachments)
19
+ - Images, video, audio, and other binary assets
20
+ - Pre-rendered pages, HTML snippets, or build artifacts tied to a deploy
21
+ - Cached responses from upstream APIs or expensive computations
22
+ - Large JSON documents that are read and written as a whole (e.g. a prerender manifest, a cached API payload)
23
+
24
+ ## When NOT to use Netlify Blobs
25
+
26
+ For ANY database-like use cases, use **Netlify Database** (see the `netlify-database` skill) — not Blobs.
27
+
28
+ That includes:
29
+
30
+ - Records, rows, or collections of entities (users, posts, orders, sessions, comments, …)
31
+ - Data you need to query, filter, sort, join, or count
32
+ - Data with relationships between different kinds of entities (users who own posts, posts with comments, …)
33
+ - Application state that evolves over time and needs to be read and updated field-by-field
34
+ - Anything a developer would naturally model as a table with columns
35
+
36
+ Blobs is an **object store**, not a database. Treating it as one — for example, storing one blob per user keyed by
37
+ user ID — works for trivial cases but breaks down as soon as you need to query or relate data. Netlify Database is
38
+ the correct primitive for those use cases, is equally zero-configuration, and handles relational data, SQL queries,
39
+ transactions, and migrations out of the box.
40
+
41
+ ## Quick Start
42
+
43
+ Install the package and use `getStore` to start storing data:
44
+
45
+ ```bash
46
+ npm install @netlify/blobs
47
+ ```
48
+
49
+ ```typescript
50
+ import { getStore } from '@netlify/blobs'
51
+
52
+ const store = getStore('my-store')
53
+
54
+ // Write
55
+ await store.set('greeting', 'Hello, world!')
56
+ await store.setJSON('config', { theme: 'dark', lang: 'en' })
57
+
58
+ // Read
59
+ const greeting = await store.get('greeting', { type: 'text' })
60
+ const config = await store.get('config', { type: 'json' })
61
+
62
+ // Delete
63
+ await store.delete('greeting')
64
+ ```
65
+
66
+ ## API Reference
67
+
68
+ ### Global Functions
69
+
70
+ #### `getStore(name: string | StoreOptions): Store`
71
+
72
+ Returns a store instance for a site-level namespace. Data persists across deploys.
73
+
74
+ ```typescript
75
+ import { getStore } from '@netlify/blobs'
76
+
77
+ // Simple usage
78
+ const store = getStore('my-store')
79
+
80
+ // With options
81
+ const store = getStore({
82
+ name: 'my-store',
83
+ consistency: 'strong',
84
+ })
85
+ ```
86
+
87
+ **`StoreOptions`:**
88
+
89
+ | Property | Type | Description |
90
+ |----------|------|-------------|
91
+ | `name` | `string` | Store namespace (required) |
92
+ | `consistency` | `'strong' \| 'eventual'` | Consistency model (default: `'eventual'`) |
93
+
94
+ ONLY add the options argument if the user needs strong consistency.
95
+
96
+ #### `getDeployStore(options?: DeployStoreOptions): Store`
97
+
98
+ Returns a store scoped to a specific deploy. Data is isolated per deploy and immutable after deploy finishes.
99
+
100
+ ```typescript
101
+ import { getDeployStore } from '@netlify/blobs'
102
+
103
+ const store = getDeployStore()
104
+ ```
105
+
106
+ Use deploy stores for data that should be tied to a specific deploy snapshot (e.g., pre-rendered page data, build
107
+ artifacts). ONLY add the options argument if the user needs strong consistency.
108
+
109
+ #### `listStores(options?: ListStoresOptions): Promise<{ stores: string[] }>`
110
+
111
+ Lists all site-level store names. Supports pagination for sites with many stores.
112
+
113
+ ```typescript
114
+ import { listStores } from '@netlify/blobs'
115
+
116
+ const { stores } = await listStores()
117
+ console.log(stores) // ['my-store', 'uploads', ...]
118
+
119
+ // Paginated iteration
120
+ for await (const page of listStores({ paginate: true })) {
121
+ console.log(page.stores)
122
+ }
123
+ ```
124
+
125
+ DO NOT pass options unless paginating.
126
+
127
+ ### Store Methods
128
+
129
+ THESE ARE THE ONLY STORE METHODS. DO NOT MAKE UP NEW ONES.
130
+
131
+ #### `set(key: string, value: string | ArrayBuffer | Blob, options?: SetOptions): Promise<void>`
132
+
133
+ Stores a value. Supports strings, binary data (ArrayBuffer), and Blob objects.
134
+
135
+ ```typescript
136
+ await store.set('my-key', 'my-value')
137
+ await store.set('binary-key', new Uint8Array([1, 2, 3]))
138
+ await store.set('with-meta', 'value', {
139
+ metadata: { contentType: 'text/plain', author: 'system' },
140
+ })
141
+ ```
142
+
143
+ **`SetOptions`:**
144
+
145
+ | Property | Type | Description |
146
+ |----------|------|-------------|
147
+ | `metadata` | `Record<string, string>` | Custom metadata (max 64 KB total) |
148
+
149
+ NEVER add metadata unless the user explicitly instructs you to.
150
+
151
+ #### `setJSON(key: string, value: any, options?: SetOptions): Promise<void>`
152
+
153
+ Stores a JSON-serializable value. Automatically serializes with `JSON.stringify`.
154
+
155
+ ```typescript
156
+ await store.setJSON('prerender-manifest', { routes: ['/', '/about'], generatedAt: Date.now() })
157
+ await store.setJSON('cache/weather-api', { city: 'Lisbon', temperature: 22, fetchedAt: Date.now() })
158
+ ```
159
+
160
+ NEVER add metadata unless the user explicitly instructs you to.
161
+
162
+ #### `get(key: string, options?: GetOptions): Promise<string | Blob | object | ArrayBuffer | null>`
163
+
164
+ Retrieves a value by key. Returns `null` if the key does not exist.
165
+
166
+ ```typescript
167
+ // As text (default)
168
+ const text = await store.get('my-key', { type: 'text' })
169
+
170
+ // As JSON
171
+ const data = await store.get('config', { type: 'json' })
172
+
173
+ // As ArrayBuffer
174
+ const binary = await store.get('binary-key', { type: 'arrayBuffer' })
175
+
176
+ // As Blob
177
+ const blob = await store.get('image', { type: 'blob' })
178
+
179
+ // As ReadableStream
180
+ const stream = await store.get('large-file', { type: 'stream' })
181
+ ```
182
+
183
+ **`GetOptions.type`:** `'text'` | `'json'` | `'arrayBuffer'` | `'blob'` | `'stream'`
184
+
185
+ ALWAYS use `store.get('key', { type: 'json' })` instead of `JSON.parse(await store.get('key'))`. NEVER add the options
186
+ argument unless you need a specific type.
187
+
188
+ #### `getWithMetadata(key: string, options?: GetOptions): Promise<{ data, metadata } | null>`
189
+
190
+ Retrieves a value along with its custom metadata.
191
+
192
+ ```typescript
193
+ const result = await store.getWithMetadata('my-key', { type: 'text' })
194
+ if (result) {
195
+ console.log(result.data) // the value
196
+ console.log(result.metadata) // { contentType: '...', ... }
197
+ }
198
+ ```
199
+
200
+ NEVER add the second getOpts arg unless you need an explicit type or have an etag to check against. AVOID adding it
201
+ unless it's reliably available but IF an etag is provided, it will only return the blob if the etag is different than
202
+ what's stored.
203
+
204
+ #### `getMetadata(key: string): Promise<{ metadata: Record<string, string> } | null>`
205
+
206
+ Retrieves only the metadata for a key, without downloading the value. Useful for checking existence or reading metadata
207
+ on large blobs.
208
+
209
+ ```typescript
210
+ const result = await store.getMetadata('my-key')
211
+ if (result) {
212
+ console.log(result.metadata)
213
+ }
214
+ ```
215
+
216
+ NEVER add the second getOpts arg unless you need an explicit type or have an etag to check against. AVOID adding it
217
+ unless it's reliably available but IF an etag is provided, it will only return the blob if the etag is different than
218
+ what's stored.
219
+
220
+ #### `list(options?: ListOptions): Promise<{ blobs: ListEntry[], directories: string[] }>`
221
+
222
+ Lists entries in the store. By default retrieves all pages. Use `paginate: true` for an `AsyncIterable`.
223
+
224
+ ```typescript
225
+ // List all
226
+ const { blobs } = await store.list()
227
+
228
+ // With prefix filter
229
+ const { blobs } = await store.list({ prefix: 'uploads/' })
230
+
231
+ // With hierarchical browsing
232
+ const { blobs, directories } = await store.list({
233
+ prefix: 'uploads/',
234
+ directories: true,
235
+ })
236
+
237
+ // Paginated iteration
238
+ for await (const page of store.list({ paginate: true })) {
239
+ for (const blob of page.blobs) {
240
+ console.log(blob.key)
241
+ }
242
+ }
243
+ ```
244
+
245
+ **`ListOptions`:**
246
+
247
+ | Property | Type | Description |
248
+ |----------|------|-------------|
249
+ | `prefix` | `string` | Filter keys by prefix |
250
+ | `directories` | `boolean` | Group by `/` delimiter |
251
+ | `paginate` | `boolean` | Return `AsyncIterable` for page-by-page iteration |
252
+ | `cursor` | `string` | Pagination cursor from previous response |
253
+ | `limit` | `number` | Max entries per page |
254
+
255
+ **`ListEntry`:**
256
+
257
+ | Property | Type | Description |
258
+ |----------|------|-------------|
259
+ | `key` | `string` | Blob key |
260
+ | `etag` | `string` | Content hash |
261
+
262
+ #### `delete(key: string): Promise<void>`
263
+
264
+ Deletes a single blob. No error if the key does not exist.
265
+
266
+ ```typescript
267
+ await store.delete('my-key')
268
+ ```
269
+
270
+ ## Storage Scopes
271
+
272
+ ### Site-Level Stores (`getStore`)
273
+
274
+ Data persists across all deploys and is shared by all functions and builds for the site. Use for uploaded files,
275
+ cached responses, and other unstructured objects that should survive redeployments. (For structured application
276
+ state — records, collections, anything relational — use Netlify Database instead.)
277
+
278
+ ```typescript
279
+ import { getStore } from '@netlify/blobs'
280
+
281
+ const store = getStore('user-uploads')
282
+ ```
283
+
284
+ ### Deploy-Specific Stores (`getDeployStore`)
285
+
286
+ Data is scoped to a single deploy. Use for build-generated data, pre-rendered content, or data that should be
287
+ immutable after deploy. Build plugins and file-based uploads must write to deploy-specific stores.
288
+
289
+ ```typescript
290
+ import { getDeployStore } from '@netlify/blobs'
291
+
292
+ const store = getDeployStore()
293
+ ```
294
+
295
+ **Production isolation pattern:** Use a global store in production and deploy store otherwise to prevent non-production
296
+ data from polluting global stores:
297
+
298
+ ```typescript
299
+ import { getStore, getDeployStore } from '@netlify/blobs'
300
+
301
+ function getBlobStore(...storeOptions) {
302
+ if (Netlify.context?.deploy.context === 'production') {
303
+ return getStore(...storeOptions)
304
+ }
305
+ return getDeployStore(...storeOptions)
306
+ }
307
+
308
+ const store = getBlobStore('app-data')
309
+ ```
310
+
311
+ ## Consistency Models
312
+
313
+ | Model | Default | Propagation | Use Case |
314
+ |-------|---------|-------------|----------|
315
+ | `eventual` | Yes | Up to 60 seconds | High-read workloads, caching |
316
+ | `strong` | No | Immediate | Critical data, counters |
317
+
318
+ Last-write-wins: concurrent writes to the same key result in the final write persisting. Add object-locking mechanisms
319
+ if you need concurrency guarantees.
320
+
321
+ ```typescript
322
+ // Strong consistency when you need immediate read-after-write
323
+ const store = getStore({ name: 'counters', consistency: 'strong' })
324
+ ```
325
+
326
+ ## File-Based Uploads
327
+
328
+ Deploy blobs by placing files in the `.netlify/blobs/deploy/` directory at build time. These are written to a
329
+ deploy-specific store automatically.
330
+
331
+ ```
332
+ .netlify/blobs/deploy/
333
+ my-key.txt # Value stored with key "my-key.txt"
334
+ $my-key.txt.json # Optional metadata for "my-key.txt"
335
+ nested/path/data.json # Key is "nested/path/data.json"
336
+ ```
337
+
338
+ **Metadata pattern:** Create a `$filename.json` sibling file:
339
+
340
+ ```json
341
+ {
342
+ "contentType": "text/plain",
343
+ "author": "build-pipeline"
344
+ }
345
+ ```
346
+
347
+ Read file-based blobs in functions using `getDeployStore`:
348
+
349
+ ```typescript
350
+ import { getDeployStore } from '@netlify/blobs'
351
+
352
+ const store = getDeployStore()
353
+ const data = await store.get('my-key.txt', { type: 'text' })
354
+ ```
355
+
356
+ ## Limits
357
+
358
+ | Resource | Limit |
359
+ |----------|-------|
360
+ | Store name | 64 bytes |
361
+ | Key | 600 bytes |
362
+ | Object size | 5 GB |
363
+ | Metadata per object | 64 KB |
364
+ | Stores per site | Unlimited |
365
+ | Objects per store | Unlimited |
366
+
367
+ ## Common Errors & Solutions
368
+
369
+ ### "Store not found" or empty reads
370
+
371
+ **Cause:** Store name mismatch or reading from wrong scope.
372
+
373
+ **Fix:**
374
+
375
+ 1. Verify the store name matches exactly (case-sensitive)
376
+ 2. Use `listStores()` to confirm the store exists
377
+ 3. Ensure you're using `getStore` for site-level data and `getDeployStore` for deploy-scoped data
378
+
379
+ ### "The environment has not been configured to use Netlify Blobs"
380
+
381
+ **Cause:** Missing Blobs environment configuration in local development.
382
+
383
+ **Fix:**
384
+
385
+ 1. Install the framework plugin (`@netlify/vite-plugin`, `@netlify/nuxt`, or `@netlify/vite-plugin-tanstack-start`)
386
+ 2. Deploy to Netlify or use the Vite plugin for local env configuration
387
+ 3. Ensure the site has at least one production deploy
388
+
389
+ This does NOT apply to legacy V1 functions which require manual siteID/token configuration.
390
+
391
+ ### "Unauthorized" or permission errors
392
+
393
+ **Cause:** Function is not running in a Netlify environment.
394
+
395
+ **Fix:**
396
+
397
+ 1. Deploy to Netlify
398
+ 2. Ensure the site has at least one production deploy
399
+ 3. Confirm the function is deployed to Netlify (not a third-party host)
400
+
401
+ ### Stale reads after writing
402
+
403
+ **Cause:** Default eventual consistency model (up to 60s propagation).
404
+
405
+ **Fix:**
406
+
407
+ 1. Use strong consistency if immediate reads are required:
408
+ ```typescript
409
+ const store = getStore({ name: 'my-store', consistency: 'strong' })
410
+ ```
411
+ 2. Note that strong consistency has higher latency
412
+
413
+ ### Large object upload timeouts
414
+
415
+ **Cause:** Object exceeds function timeout or network limits.
416
+
417
+ **Fix:**
418
+
419
+ 1. Verify object is under 5 GB
420
+ 2. For large uploads, use file-based uploads via `.netlify/blobs/deploy/` at build time
421
+ 3. For function uploads, ensure function timeout is sufficient (background functions have 15-min timeout)
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@netlify/agent-runner-cli",
3
3
  "type": "module",
4
- "version": "1.102.0-test.0",
4
+ "version": "1.103.0",
5
5
  "description": "CLI tool for running Netlify agents",
6
6
  "main": "./dist/index.js",
7
7
  "types": "./dist/index.d.ts",
@@ -81,8 +81,8 @@
81
81
  "vitest": "^4.0.16"
82
82
  },
83
83
  "dependencies": {
84
- "@anthropic-ai/claude-code": "2.1.87",
85
- "@anthropic-ai/sdk": "0.78.0",
84
+ "@anthropic-ai/claude-code": "2.1.111",
85
+ "@anthropic-ai/sdk": "0.90.0",
86
86
  "@google/gemini-cli": "0.31.0",
87
87
  "@netlify/otel": "^5.1.5",
88
88
  "@netlify/ts-cli": "^1.0.4",