@netlify/agent-runner-cli 1.66.0 → 1.67.0-alpha.1

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,304 @@
1
+ ---
2
+ name: netlify-forms
3
+ description: Build and configure Netlify Forms for serverless form handling. Use when implementing contact forms, feedback forms, file uploads, or any form that collects user submissions without backend code.
4
+ ---
5
+
6
+ # Netlify Forms
7
+
8
+ Netlify Forms is a serverless form-handling service. The build system parses HTML at deploy time to detect forms and
9
+ automatically creates submission endpoints.
10
+
11
+ ## Quick Start
12
+
13
+ Add `data-netlify="true"` or `netlify` attribute to any `<form>` tag:
14
+
15
+ ```html
16
+ <form name="contact" method="POST" data-netlify="true">
17
+ <input type="text" name="name" required />
18
+ <input type="email" name="email" required />
19
+ <textarea name="message"></textarea>
20
+ <button type="submit">Send</button>
21
+ </form>
22
+ ```
23
+
24
+ **Key requirements:**
25
+
26
+ - The `name` attribute identifies the form in Netlify UI (must be unique per site)
27
+ - Method must be `POST`
28
+ - Netlify injects a hidden `form-name` field during build
29
+
30
+ ## AJAX / JavaScript Submission
31
+
32
+ Submit forms without page reload:
33
+
34
+ ```javascript
35
+ const form = document.getElementById('contact-form')
36
+
37
+ form.addEventListener('submit', async (e) => {
38
+ e.preventDefault()
39
+
40
+ const response = await fetch('/', {
41
+ method: 'POST',
42
+ headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
43
+ body: new URLSearchParams(new FormData(form)).toString(),
44
+ })
45
+
46
+ if (response.ok) {
47
+ // Handle success
48
+ }
49
+ })
50
+ ```
51
+
52
+ **Critical AJAX requirements:**
53
+
54
+ 1. Content-Type MUST be `application/x-www-form-urlencoded`
55
+ 2. Include hidden `form-name` field in your HTML:
56
+ ```html
57
+ <input type="hidden" name="form-name" value="contact" />
58
+ ```
59
+
60
+ ## JavaScript Frameworks (React, Vue, Next.js)
61
+
62
+ Netlify's build bot cannot detect forms rendered client-side. You MUST create a static HTML version.
63
+
64
+ ### Next.js Runtime v5
65
+
66
+ If using Netlify Forms with Next.js Runtime v5, you **must**:
67
+
68
+ 1. Extract form definitions to a dedicated static HTML file (e.g., `public/__forms.html`)
69
+ 2. Submit forms using AJAX — full-page navigation won't work
70
+
71
+ ### Solution: Hidden HTML Skeleton
72
+
73
+ Create `public/__forms.html` (or include in your static HTML):
74
+
75
+ ```html
76
+ <!-- This file is only for Netlify's build bot detection -->
77
+ <form name="contact" netlify hidden>
78
+ <input name="name" />
79
+ <input name="email" />
80
+ <textarea name="message"></textarea>
81
+ </form>
82
+ ```
83
+
84
+ ### React Example
85
+
86
+ ```jsx
87
+ function ContactForm() {
88
+ const handleSubmit = async (e) => {
89
+ e.preventDefault()
90
+ const formData = new FormData(e.target)
91
+
92
+ await fetch('/', {
93
+ method: 'POST',
94
+ headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
95
+ body: new URLSearchParams(formData).toString(),
96
+ })
97
+ }
98
+
99
+ return (
100
+ <form name="contact" method="POST" onSubmit={handleSubmit}>
101
+ {/* Hidden field required for AJAX */}
102
+ <input type="hidden" name="form-name" value="contact" />
103
+ <input name="name" required />
104
+ <input name="email" type="email" required />
105
+ <textarea name="message" />
106
+ <button type="submit">Send</button>
107
+ </form>
108
+ )
109
+ }
110
+ ```
111
+
112
+ ## File Uploads
113
+
114
+ ```html
115
+ <form name="upload" method="POST" data-netlify="true" enctype="multipart/form-data">
116
+ <input type="file" name="attachment" />
117
+ <button type="submit">Upload</button>
118
+ </form>
119
+ ```
120
+
121
+ **Limits:**
122
+
123
+ - Maximum request size: **8 MB**
124
+ - Timeout: **30 seconds**
125
+ - One file per input field (use multiple inputs for multiple files)
126
+
127
+ **Security:** For file uploads containing PII (personally identifiable information), use the
128
+ [Very Good Security (VGS)](https://www.netlify.com/integrations/very-good-security/) integration for additional
129
+ protection.
130
+
131
+ **AJAX file uploads:** Do NOT set Content-Type header - let browser set `multipart/form-data` with boundary:
132
+
133
+ ```javascript
134
+ // Correct - no Content-Type header
135
+ fetch('/', {
136
+ method: 'POST',
137
+ body: new FormData(form), // Browser sets correct Content-Type
138
+ })
139
+ ```
140
+
141
+ ## Spam Protection
142
+
143
+ ### Honeypot Field (Recommended)
144
+
145
+ ```html
146
+ <form name="contact" method="POST" data-netlify="true" netlify-honeypot="bot-field">
147
+ <!-- Hidden from humans, filled by bots -->
148
+ <p style="display:none">
149
+ <label>Don't fill this: <input name="bot-field" /></label>
150
+ </p>
151
+
152
+ <!-- Visible fields -->
153
+ <input name="name" required />
154
+ <button type="submit">Send</button>
155
+ </form>
156
+ ```
157
+
158
+ ### reCAPTCHA v2 (Netlify-provided)
159
+
160
+ ```html
161
+ <form name="contact" method="POST" data-netlify="true" data-netlify-recaptcha="true">
162
+ <input name="name" required />
163
+
164
+ <!-- Netlify injects reCAPTCHA here -->
165
+ <div data-netlify-recaptcha="true"></div>
166
+
167
+ <button type="submit">Send</button>
168
+ </form>
169
+ ```
170
+
171
+ Only one Netlify-provided reCAPTCHA per page. For multiple CAPTCHAs on one page, use custom reCAPTCHA.
172
+
173
+ ### Custom reCAPTCHA v2
174
+
175
+ Use your own reCAPTCHA 2 code with Netlify validation:
176
+
177
+ 1. Sign up for a [reCAPTCHA API key pair](http://www.google.com/recaptcha/admin) and add the reCAPTCHA script/widget to
178
+ your form.
179
+ 2. Set environment variables in Netlify (UI, CLI, or API):
180
+ - `SITE_RECAPTCHA_KEY` — your reCAPTCHA site key (scopes: Builds + Runtime)
181
+ - `SITE_RECAPTCHA_SECRET` — your reCAPTCHA secret key (scope: Runtime)
182
+ 3. Add `data-netlify-recaptcha="true"` to your `<form>` tag.
183
+
184
+ Netlify validates the `g-recaptcha-response` server-side on each submission.
185
+
186
+ **For AJAX with reCAPTCHA:** Include `g-recaptcha-response` in POST body (automatic if using `FormData()`).
187
+
188
+ ## Success Redirects
189
+
190
+ ```html
191
+ <!-- Redirect to custom thank-you page -->
192
+ <form name="contact" method="POST" data-netlify="true" action="/thank-you"></form>
193
+ ```
194
+
195
+ The `action` path must:
196
+
197
+ - Start with `/`
198
+ - Be relative to site root
199
+ - Point to an existing page
200
+
201
+ ## Notifications
202
+
203
+ ### Email Notifications
204
+
205
+ Configure in Netlify UI: **Project configuration > Notifications > Emails and webhooks > Form submission notifications**
206
+
207
+ - Include `<input name="email">` to set reply-to address automatically
208
+ - Custom subject line:
209
+ ```html
210
+ <input type="hidden" name="subject" value="New inquiry from %{formName}" />
211
+ ```
212
+ - Available variables: `%{formName}`, `%{siteName}`, `%{submissionId}`
213
+ - For forms created before May 5, 2023: remove `[Netlify]` prefix from subject by adding `data-remove-prefix`:
214
+ ```html
215
+ <input type="hidden" name="subject" data-remove-prefix value="Sales inquiry" />
216
+ ```
217
+
218
+ ### Webhooks
219
+
220
+ Configure in Netlify UI: **Project configuration > Notifications > Emails and webhooks > Form submission notifications**
221
+
222
+ Sends JSON payload on each verified submission.
223
+
224
+ ## Function Triggers
225
+
226
+ Trigger serverless functions on submissions:
227
+
228
+ ```typescript
229
+ // netlify/functions/submission-created.mts
230
+ import type { Context } from '@netlify/functions'
231
+
232
+ interface FormPayload {
233
+ form_name: string
234
+ data: Record<string, string>
235
+ created_at: string
236
+ }
237
+
238
+ export default async (req: Request, context: Context) => {
239
+ const { payload } = (await req.json()) as { payload: FormPayload }
240
+
241
+ console.log('Form:', payload.form_name)
242
+ console.log('Data:', payload.data)
243
+
244
+ // Process submission (send to CRM, Slack, etc.)
245
+
246
+ return new Response('OK')
247
+ }
248
+ ```
249
+
250
+ **Event name:** `submission-created` (filename must match)
251
+
252
+ ## Limits
253
+
254
+ | Feature | Free Tier | Paid Tier |
255
+ | ------------ | ----------- | ---------- |
256
+ | Submissions | 100/month | Metered |
257
+ | File Storage | 10 MB/month | Scalable |
258
+ | Request Size | 8 MB | 8 MB |
259
+ | Timeout | 30 seconds | 30 seconds |
260
+
261
+ ## Common Errors & Solutions
262
+
263
+ ### "404 on submit"
264
+
265
+ **Cause:** Build bot didn't detect the form. **Fix:**
266
+
267
+ 1. Ensure static HTML version exists for JS frameworks
268
+ 2. Verify `form-name` hidden input is present
269
+ 3. Check form has `name` attribute
270
+
271
+ ### Submissions not appearing
272
+
273
+ **Check:**
274
+
275
+ 1. Look in **Spam** folder in Netlify UI (Akismet filtering)
276
+ 2. Avoid test values like "test@test.com" or "asdf" — use real email and full sentences
277
+ 3. Verify form was included in the latest deploy
278
+
279
+ ### AJAX submission fails silently
280
+
281
+ **Ensure:**
282
+
283
+ 1. Content-Type is `application/x-www-form-urlencoded` (not JSON)
284
+ 2. `form-name` field is included in body
285
+ 3. Check browser Network tab for actual response
286
+
287
+ ### File upload fails
288
+
289
+ **Check:**
290
+
291
+ 1. Total request size under 8 MB
292
+ 2. Not setting Content-Type header manually
293
+ 3. Using `enctype="multipart/form-data"` on form
294
+
295
+ ## API Access
296
+
297
+ List submissions programmatically:
298
+
299
+ ```bash
300
+ curl -H "Authorization: Bearer $NETLIFY_AUTH_TOKEN" \
301
+ https://api.netlify.com/api/v1/forms/{form_id}/submissions
302
+ ```
303
+
304
+ Export available as CSV from Netlify UI.
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@netlify/agent-runner-cli",
3
3
  "type": "module",
4
- "version": "1.66.0",
4
+ "version": "1.67.0-alpha.1",
5
5
  "description": "CLI tool for running Netlify agents",
6
6
  "main": "./dist/index.js",
7
7
  "types": "./dist/index.d.ts",
@@ -13,6 +13,7 @@
13
13
  "files": [
14
14
  "dist/**/*.js",
15
15
  "dist/**/*.d.ts",
16
+ "dist/skills/**",
16
17
  "patches",
17
18
  "scripts"
18
19
  ],
@@ -43,7 +44,7 @@
43
44
  },
44
45
  "config": {
45
46
  "eslint": "--cache --format=codeframe --max-warnings=0 \"{src,scripts,test,.github}/**/*.{js,ts,md,html}\"",
46
- "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\""
47
+ "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\""
47
48
  },
48
49
  "keywords": [],
49
50
  "license": "MIT",
@@ -76,7 +77,7 @@
76
77
  "vitest": "^4.0.16"
77
78
  },
78
79
  "dependencies": {
79
- "@anthropic-ai/claude-code": "2.1.32",
80
+ "@anthropic-ai/claude-code": "2.1.37",
80
81
  "@anthropic-ai/sdk": "0.72.1",
81
82
  "@google/gemini-cli": "0.25.2",
82
83
  "@netlify/otel": "^5.1.1",