@netlify/agent-runner-cli 1.88.0-alpha.1 → 1.88.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.
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: netlify-identity
3
- description: Add user authentication with Netlify Identity. Use when implementing signups, logins, password recovery, role-based access control, OAuth providers, or protecting Netlify Functions with user tokens.
3
+ description: Add user authentication with Netlify Identity. Use when implementing signups, logins, password recovery, role-based access control, OAuth providers, protecting Netlify Functions, or server-side auth in SSR frameworks.
4
4
  ---
5
5
 
6
6
  # Netlify Identity
@@ -8,190 +8,139 @@ description: Add user authentication with Netlify Identity. Use when implementin
8
8
  Netlify Identity is a user management service for signups, logins, password recovery, user metadata, and role-based
9
9
  access control. It is built on [GoTrue](https://github.com/netlify/gotrue) and issues JSON Web Tokens (JWTs).
10
10
 
11
- There are two integration paths:
11
+ `@netlify/identity` is the recommended library for integrating with Netlify Identity. It provides a unified, headless
12
+ API that works in both browser and server contexts (Netlify Functions, Edge Functions, SSR frameworks). You do not need
13
+ `gotrue-js` or `netlify-identity-widget` separately.
12
14
 
13
- - **Widget** (`netlify-identity-widget`)Drop-in UI modal for login, signup, and password recovery. Zero framework
14
- dependencies. Best when you want auth working quickly without building custom forms.
15
- - **gotrue-js**Programmatic client for full control over authentication flows. Best when you need custom UI or
16
- headless auth logic.
15
+ - **`@netlify/identity`**Default choice. Headless TypeScript API for browser and server. Full control over auth UI.
16
+ Works with SSR frameworks (Next.js, Remix, Astro, SvelteKit, TanStack Start).
17
+ - **`netlify-identity-widget`**Browser-only drop-in UI modal for login, signup, and password recovery. Zero custom
18
+ code needed. **Does not work server-side** — no SSR, no Netlify Functions, no Edge Functions. Use only when the user
19
+ wants a pre-built auth modal with no custom design.
17
20
 
18
- Both communicate with the Identity endpoint at `/.netlify/identity` on your site.
19
-
20
- ## Quick Start
21
-
22
- Add the widget CDN script and a menu element for instant auth:
23
-
24
- ```html
25
- <script type="text/javascript" src="https://identity.netlify.com/v1/netlify-identity-widget.js"></script>
26
- <div data-netlify-identity-menu></div>
27
- ```
28
-
29
- Handle logins:
21
+ ## Setup
30
22
 
31
- ```javascript
32
- netlifyIdentity.on('login', async (user) => {
33
- const jwt = await netlifyIdentity.refresh()
34
- await fetch('/.netlify/functions/protected', {
35
- headers: { Authorization: `Bearer ${jwt}` },
36
- })
37
- })
23
+ ```bash
24
+ npm install @netlify/identity
38
25
  ```
39
26
 
40
- ### When to Use Widget vs gotrue-js
41
-
42
- - **Prefer the widget** when the user wants auth working quickly, doesn't have custom UI requirements, or is building a
43
- simple site. It requires minimal code and automatically sets the `nf_jwt` cookie needed for role-based redirects.
44
- - **Use gotrue-js** when the user needs custom login/signup forms, headless auth, or programmatic control over the auth
45
- flow. Pass `setCookie: true` if you need role-based redirects (see below).
46
- - **Never mix both** in the same page unless accessing `netlifyIdentity.gotrue` for low-level operations while using the
47
- widget for UI.
48
-
49
- ## Setup
50
-
51
27
  Identity is automatically enabled when the deploy includes Identity code. Default settings:
52
28
 
53
29
  - **Registration** — Open (anyone can sign up). Change to Invite only in **Project configuration > Identity** if needed.
54
- - **Autoconfirm** — Off (new signups require email confirmation). Enable in **Project configuration > Identity** to skip confirmation during development.
30
+ - **Autoconfirm** — Off (new signups require email confirmation). Enable in **Project configuration > Identity** to skip
31
+ confirmation during development.
55
32
 
56
- ## Widget (Drop-in UI)
33
+ For local development, use `netlify dev` so the Identity endpoint is available.
57
34
 
58
- ### CDN Script Tag
35
+ ## Quick Start
59
36
 
60
- Include the widget script and add HTML attributes for automatic controls:
37
+ Log in from the browser:
61
38
 
62
- ```html
63
- <script type="text/javascript" src="https://identity.netlify.com/v1/netlify-identity-widget.js"></script>
39
+ ```typescript
40
+ import { login, getUser } from '@netlify/identity'
64
41
 
65
- <!-- Login/Signup menu (shows username + logout when logged in) -->
66
- <div data-netlify-identity-menu></div>
42
+ const user = await login('jane@example.com', 'password123')
43
+ console.log(`Hello, ${user.name}`)
67
44
 
68
- <!-- Simple button that opens the modal -->
69
- <div data-netlify-identity-button>Login with Netlify Identity</div>
45
+ // Later, check auth state
46
+ const currentUser = await getUser()
70
47
  ```
71
48
 
72
- The widget attaches itself to `window.netlifyIdentity` automatically.
73
-
74
- ### npm Module
75
-
76
- ```bash
77
- npm install netlify-identity-widget
78
- ```
49
+ Protect a Netlify Function:
79
50
 
80
- ```javascript
81
- import netlifyIdentity from 'netlify-identity-widget'
82
-
83
- netlifyIdentity.init({
84
- container: '#netlify-modal', // defaults to document.body
85
- locale: 'en', // language code (en, fr, es, pt, de, etc.)
86
- // APIUrl: 'https://your-site.netlify.app/.netlify/identity', // only for non-Netlify hosts
87
- })
51
+ ```typescript
52
+ // netlify/functions/protected.mts
53
+ import { getUser } from '@netlify/identity'
54
+ import type { Context } from '@netlify/functions'
55
+
56
+ export default async (req: Request, context: Context) => {
57
+ const user = await getUser()
58
+ if (!user) return new Response('Unauthorized', { status: 401 })
59
+ return Response.json({ id: user.id, email: user.email })
60
+ }
88
61
  ```
89
62
 
90
- Set `APIUrl` only when the app is served from a different domain than the Identity endpoint (Cordova, Electron, or
91
- custom domains).
63
+ ALWAYS use `@netlify/identity` for new Identity integrations. ONLY use `netlify-identity-widget` if the user explicitly
64
+ asks for a drop-in UI modal. ALWAYS use TypeScript (`.mts`) for functions that use Identity.
92
65
 
93
- ### Events
66
+ ## Full API Reference
94
67
 
95
- ```javascript
96
- netlifyIdentity.on('init', (user) => console.log('init', user))
97
- netlifyIdentity.on('login', (user) => console.log('login', user))
98
- netlifyIdentity.on('logout', () => console.log('Logged out'))
99
- netlifyIdentity.on('error', (err) => console.error('Error', err))
68
+ For the complete API reference — all function signatures, type definitions, OAuth helpers, admin operations, session
69
+ management, auth events, and framework-specific integration examples — read the package README:
100
70
 
101
- netlifyIdentity.off('login') // unbind all login handlers
102
- netlifyIdentity.off('login', handler) // unbind a specific handler
103
71
  ```
104
-
105
- ### Key Methods
106
-
107
- ```javascript
108
- netlifyIdentity.open() // open modal (default tab)
109
- netlifyIdentity.open('login') // open to login tab
110
- netlifyIdentity.open('signup') // open to signup tab
111
- netlifyIdentity.open('signup', { full_name: 'Jane' }) // pre-fill signup metadata
112
- netlifyIdentity.close()
113
- netlifyIdentity.logout()
114
- const user = netlifyIdentity.currentUser() // null if not logged in
115
- const jwt = await netlifyIdentity.refresh() // refresh the JWT
116
- const gotrueClient = netlifyIdentity.gotrue // underlying GoTrue client
72
+ node_modules/@netlify/identity/README.md
117
73
  ```
118
74
 
119
- ## gotrue-js (Programmatic Client)
75
+ The README is shipped with the npm package and is always in sync with the installed version.
120
76
 
121
- ### Install
77
+ ## SSR Integration Patterns
122
78
 
123
- ```bash
124
- npm install gotrue-js
125
- ```
79
+ For SSR frameworks, the recommended pattern is:
126
80
 
127
- ### Initialize
81
+ - **Browser-side** for auth mutations: `login()`, `signup()`, `logout()`, `oauthLogin()`
82
+ - **Server-side** for reading auth state: `getUser()`, `getSettings()`, `getIdentityConfig()`
128
83
 
129
- ```typescript
130
- import GoTrue from 'gotrue-js'
84
+ Browser-side auth mutations set the `nf_jwt` cookie and localStorage, and emit `onAuthChange` events. The
85
+ server reads the cookie on the next request.
131
86
 
132
- const auth = new GoTrue({
133
- APIUrl: 'https://<your-site>.netlify.app/.netlify/identity',
134
- setCookie: false,
135
- })
136
- ```
87
+ The library also supports server-side mutations (`login()`, `signup()`, `logout()` inside Netlify Functions), but these
88
+ require the Netlify Functions runtime to set cookies. After a server-side mutation, use a full page navigation so the
89
+ browser sends the new cookie.
137
90
 
138
- `setCookie: true` tells the server to set HttpOnly cookies, which protects tokens from XSS but is required if you need
139
- role-based redirects without the widget (the CDN reads an `nf_jwt` cookie to evaluate redirect conditions). Use `false`
140
- when you manage tokens entirely in JavaScript and don't need CDN-level access control.
91
+ ALWAYS use `window.location.href` (not framework router navigation) after server-side auth mutations in Next.js,
92
+ TanStack Start, and SvelteKit. Remix `redirect()` is safe because Remix actions return real HTTP responses.
141
93
 
142
- ### Core Methods
94
+ ## Identity Event Functions
143
95
 
144
- ```javascript
145
- // Signup (sends confirmation email if autoconfirm is off)
146
- const response = await auth.signup(email, password)
147
- // Pass user_metadata during signup: auth.signup(email, password, { full_name: 'Jane' })
96
+ Special serverless functions that trigger on Identity lifecycle events. These use the **legacy named `handler` export**
97
+ (not the modern default export) because they receive `event.body` containing the user payload.
148
98
 
149
- // Confirm email (token from confirmation email URL fragment)
150
- const user = await auth.confirm(token, true) // remember = persist session
99
+ ALWAYS use the legacy named `handler` export (not default export) for Identity event functions. The filename MUST match
100
+ the event name exactly (e.g., `netlify/functions/identity-signup.mts`).
151
101
 
152
- // Login
153
- const user = await auth.login(email, password, true) // remember = persist session
102
+ **Event names:** `identity-validate`, `identity-signup`, `identity-login`
154
103
 
155
- // Current user (from localStorage)
156
- const user = auth.currentUser()
104
+ - `identity-signup` — fires when a new user signs up (email/password or OAuth)
105
+ - `identity-login` fires on each login
106
+ - `identity-validate` — fires during signup before the user is created; return a non-200 status to reject
157
107
 
158
- // Get fresh JWT (auto-refreshes if expired; pass true to force)
159
- const token = await user.jwt()
108
+ ### Example: Assign Default Role on Signup
160
109
 
161
- // Update user
162
- await user.update({ email: 'new@example.com', password: 'newpassword' })
163
- await user.update({ data: { full_name: 'Jane Doe' } }) // update user_metadata
110
+ ```typescript
111
+ // netlify/functions/identity-signup.mts
112
+ import type { Handler, HandlerEvent, HandlerContext } from '@netlify/functions'
164
113
 
165
- // Logout
166
- await user.logout()
114
+ const handler: Handler = async (event: HandlerEvent, context: HandlerContext) => {
115
+ const { user } = JSON.parse(event.body || '{}')
167
116
 
168
- // Password recovery
169
- await auth.requestPasswordRecovery(email) // sends recovery email
170
- const user = await auth.recover(recoveryToken, true) // remember = persist session
117
+ return {
118
+ statusCode: 200,
119
+ body: JSON.stringify({
120
+ app_metadata: {
121
+ ...user.app_metadata,
122
+ roles: ['member'],
123
+ },
124
+ }),
125
+ }
126
+ }
171
127
 
172
- // Check server settings (signup enabled? autoconfirm? OAuth providers?)
173
- const settings = await auth.settings()
128
+ export { handler }
174
129
  ```
175
130
 
176
- ### Error Handling
177
-
178
- gotrue-js exports `JSONHTTPError`, `TextHTTPError`, and `HTTPError` for granular error catching. Use `error.json`,
179
- `error.data`, or `error.status` respectively.
180
-
181
- ## Token Handling
131
+ The response body replaces `app_metadata` and/or `user_metadata` on the user record — include all fields you want to
132
+ keep, not just new ones.
182
133
 
183
- - Access tokens expire after **1 hour**
184
- - Both `user.jwt()` (gotrue-js) and `netlifyIdentity.refresh()` (widget) auto-refresh using the refresh token
185
- - Always call `jwt()` or `refresh()` before authenticated requests — do not cache tokens
186
- - Send the token as `Authorization: Bearer <token>`
134
+ For bulk user management or role changes outside lifecycle events, use the `admin` API instead of Identity event
135
+ functions.
187
136
 
188
137
  ## Roles and Authorization
189
138
 
190
139
  ### Metadata Types
191
140
 
192
- - **`app_metadata.roles`** — Server-controlled. Only settable via the Netlify UI, admin API, or server-side code
193
- (Identity event functions, protected functions with admin token). Do not allow users to set their own roles.
194
- - **`user_metadata`** — User-controlled. Users can update this via `user.update({ data: { ... } })`.
141
+ - **`app_metadata.roles`** — Server-controlled. Only settable via the Netlify UI, admin API, or Identity event functions.
142
+ Do not allow users to set their own roles.
143
+ - **`user_metadata`** — User-controlled. Users can update this via `updateUser({ data: { ... } })`.
195
144
 
196
145
  ### Role-Based Redirects
197
146
 
@@ -213,117 +162,72 @@ Use `netlify.toml` to restrict paths by role:
213
162
  Rules are evaluated top-to-bottom. The first redirect matches users with the `admin` role; everyone else falls through
214
163
  to the second rule and is redirected away.
215
164
 
216
- **How it works:** The widget sets an `nf_jwt` cookie that the CDN reads to evaluate `conditions = { Role = [...] }`.
217
- Without this cookie, role-based redirects will not work. If using gotrue-js instead of the widget, pass
218
- `setCookie: true` when initializing GoTrue so the server sets this cookie.
165
+ **How it works:** The `nf_jwt` cookie is read by the CDN to evaluate `conditions = { Role = [...] }`. Without this
166
+ cookie, role-based redirects will not work.
219
167
 
220
- ## Identity Event Functions
168
+ ## Common Errors & Solutions
221
169
 
222
- Special serverless functions that trigger on Identity lifecycle events. These use the **legacy named `handler` export**
223
- (not the modern default export) because they receive `event.body` containing the user payload.
170
+ ### "Signups not allowed for this instance" (403)
224
171
 
225
- **Event names:** `identity-validate`, `identity-signup`, `identity-login`
172
+ **Cause:** Registration is set to Invite only.
226
173
 
227
- - `identity-signup` — fires when a new user signs up (email/password or OAuth)
228
- - `identity-login` — fires on each login
229
- - `identity-validate` — fires during signup before the user is created; return a non-200 status to reject the signup
174
+ **Fix:**
230
175
 
231
- The filename must match the event name exactly (e.g. `netlify/functions/identity-signup.mts`).
176
+ 1. Change to Open in **Project configuration > Identity**
177
+ 2. Or invite users from the Identity tab in the Netlify UI
232
178
 
233
- ### Example: Assign Default Role on Signup
179
+ ### "Token expired" / 401 on API calls
234
180
 
235
- ```typescript
236
- // netlify/functions/identity-signup.mts
237
- import type { Handler, HandlerEvent, HandlerContext } from '@netlify/functions'
181
+ **Cause:** Stale access token.
238
182
 
239
- const handler: Handler = async (event: HandlerEvent, context: HandlerContext) => {
240
- const { user } = JSON.parse(event.body || '{}')
183
+ **Fix:**
241
184
 
242
- return {
243
- statusCode: 200,
244
- body: JSON.stringify({
245
- app_metadata: {
246
- ...user.app_metadata,
247
- roles: ['member'],
248
- },
249
- }),
250
- }
251
- }
185
+ 1. Always use `getUser()` before authenticated requests
186
+ 2. In the browser, the library auto-refreshes tokens
187
+ 3. On the server, call `refreshSession()` in middleware to handle near-expiry tokens
252
188
 
253
- export { handler }
254
- ```
189
+ ### Identity event function not triggering
255
190
 
256
- The response body replaces `app_metadata` and/or `user_metadata` on the user record include all fields you want to
257
- keep, not just new ones.
191
+ **Cause:** Filename or export format does not match expected convention.
258
192
 
259
- ## Protected Functions
193
+ **Fix:**
260
194
 
261
- Functions that verify the calling user's identity. Use the legacy `handler` export to access `context.clientContext`.
195
+ 1. Verify filename matches exactly: `identity-signup`, `identity-validate`, or `identity-login`
196
+ 2. Place in `netlify/functions/` with `.mts` or `.mjs` extension
197
+ 3. Use named `handler` export (not default export)
262
198
 
263
- ```typescript
264
- // netlify/functions/protected-action.mts
265
- import type { Handler, HandlerEvent, HandlerContext } from '@netlify/functions'
199
+ ### `MissingIdentityError`
266
200
 
267
- const handler: Handler = async (event: HandlerEvent, context: HandlerContext) => {
268
- const { identity, user } = context.clientContext || {}
201
+ **Cause:** Identity is not configured in the current environment.
269
202
 
270
- if (!user) {
271
- return { statusCode: 401, body: JSON.stringify({ error: 'Unauthorized' }) }
272
- }
203
+ **Fix:**
273
204
 
274
- // user.sub user ID
275
- // user.email email address
276
- // user.app_metadata.roles — assigned roles
277
- // user.user_metadata — user-controlled data
278
-
279
- // Use identity.token (admin token) to call the Identity admin API:
280
- const adminAuthHeader = `Bearer ${identity.token}`
281
- const response = await fetch(`${identity.url}/admin/users/${user.sub}`, {
282
- method: 'PUT',
283
- headers: { Authorization: adminAuthHeader },
284
- body: JSON.stringify({ app_metadata: { roles: ['editor'] } }),
285
- })
286
-
287
- const data = await response.json()
288
- return { statusCode: 200, body: JSON.stringify(data) }
289
- }
205
+ 1. Ensure Identity is enabled on the project
206
+ 2. Use `netlify dev` for local development so the Identity endpoint is available
290
207
 
291
- export { handler }
292
- ```
208
+ ### `AuthError` on server — missing Netlify runtime
293
209
 
294
- **Note:** Operations using `identity.token` (admin token) do **not** work locally with `netlify dev`. Deploy to Netlify
295
- to test server-side admin operations.
210
+ **Cause:** Server-side `login()`, `signup()`, or `logout()` require the Netlify Functions runtime to set cookies.
296
211
 
297
- ## External OAuth Providers
212
+ **Fix:**
298
213
 
299
- Netlify Identity supports Google, GitHub, GitLab, and BitBucket as built-in OAuth providers.
214
+ 1. Deploy to Netlify to use server-side auth mutations
215
+ 2. Or use `netlify dev` for local development
300
216
 
301
- **Enable in Netlify UI:** Project configuration > Identity > Registration > External providers
217
+ ### "User not found" after OAuth login
302
218
 
303
- ### Widget
219
+ **Cause:** OAuth provider is not enabled for the project.
304
220
 
305
- Once enabled in the UI, the widget automatically shows OAuth provider buttons. No additional code needed.
221
+ **Fix:**
306
222
 
307
- ### gotrue-js
223
+ 1. Enable the provider in **Project configuration > Identity > External providers**
224
+ 2. Users are created automatically on first OAuth login
308
225
 
309
- ```javascript
310
- const url = auth.loginExternalUrl('github')
311
- // Redirect the user to this URL to start the OAuth flow
312
- window.location.href = url
313
- ```
226
+ ### Account operations fail after server-side login
314
227
 
315
- Available provider names: `'google'`, `'github'`, `'gitlab'`, `'bitbucket'`
228
+ **Cause:** Browser-side session is not bootstrapped from server-set cookies.
316
229
 
317
- ## Common Errors & Solutions
230
+ **Fix:**
318
231
 
319
- - **"Signups not allowed for this instance" (403)** Registration is set to Invite only. Change to Open in Project
320
- configuration > Identity, or invite users from the Identity tab.
321
- - **"Token expired" / 401 on API calls** — Stale access token. Always call `user.jwt()` or `netlifyIdentity.refresh()`
322
- before authenticated requests.
323
- - **Identity event function not triggering** — Verify filename matches exactly (`identity-signup`, `identity-validate`,
324
- or `identity-login`), is in `netlify/functions/`, and uses `.mts`/`.mjs`.
325
- - **`clientContext` is undefined** — Use named `handler` export (not `export default`), send `Authorization: Bearer`
326
- header, and verify token is fresh.
327
- - **Admin methods not working locally** — `identity.token` is unavailable in local dev. Deploy to Netlify to test.
328
- - **"User not found" after OAuth login** — Enable the OAuth provider in Project configuration > Identity > External
329
- providers. Users are created on first OAuth login.
232
+ 1. Call `hydrateSession()` on page load to bridge server-set cookies to the browser session
233
+ 2. Then use `updateUser()`, `verifyEmailChange()`, or other account operations
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@netlify/agent-runner-cli",
3
3
  "type": "module",
4
- "version": "1.88.0-alpha.1",
4
+ "version": "1.88.0",
5
5
  "description": "CLI tool for running Netlify agents",
6
6
  "main": "./dist/index.js",
7
7
  "types": "./dist/index.d.ts",
@@ -42,6 +42,7 @@
42
42
  "test:integration:gemini": "vitest run test/integration/gemini.test.ts",
43
43
  "test:integration:create-stage": "vitest run test/integration/create.test.ts",
44
44
  "test:integration:skill-invocation": "vitest run test/integration/skill-invocation.test.ts",
45
+ "check:types": "tsc --noEmit",
45
46
  "postinstall": "node scripts/postinstall.js"
46
47
  },
47
48
  "config": {
@@ -82,7 +83,7 @@
82
83
  "@anthropic-ai/claude-code": "2.1.63",
83
84
  "@anthropic-ai/sdk": "0.78.0",
84
85
  "@google/gemini-cli": "0.31.0",
85
- "@netlify/otel": "^5.1.1",
86
+ "@netlify/otel": "^5.1.2",
86
87
  "@openai/codex": "0.110.0",
87
88
  "@opentelemetry/exporter-trace-otlp-grpc": "^0.212.0",
88
89
  "execa": "^9.6.1",