nstbrowser-ai-agent 0.0.2 → 0.0.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/README.md CHANGED
@@ -4,6 +4,58 @@ Headless browser automation CLI for AI agents. Fast Rust CLI with Node.js fallba
4
4
 
5
5
  ## Installation
6
6
 
7
+ ## Prerequisites
8
+
9
+ Before using nstbrowser-ai-agent, ensure you have the following:
10
+
11
+ ### 1. Nstbrowser Client
12
+
13
+ **Nstbrowser client must be installed and running.**
14
+
15
+ - Download from: https://www.nstbrowser.io/
16
+ - Install the client application
17
+ - Launch the Nstbrowser client
18
+
19
+ ### 2. Nstbrowser Service
20
+
21
+ The Nstbrowser API service must be accessible:
22
+
23
+ - Default endpoint: `http://127.0.0.1:8848`
24
+ - Verify service is running:
25
+ ```bash
26
+ curl http://127.0.0.1:8848/api/v2/profiles
27
+ ```
28
+ - Expected response: JSON with profile data or empty list
29
+
30
+ ### 3. API Key
31
+
32
+ Obtain your API key from the Nstbrowser dashboard and configure it:
33
+
34
+ **Method 1: Config File (Recommended)**
35
+ ```bash
36
+ nstbrowser-ai-agent config set key YOUR_API_KEY
37
+ ```
38
+
39
+ **Method 2: Environment Variable**
40
+ ```bash
41
+ export NST_API_KEY="YOUR_API_KEY"
42
+ ```
43
+
44
+ ### 4. Verify Setup
45
+
46
+ Test your configuration:
47
+
48
+ ```bash
49
+ # Check CLI version
50
+ nstbrowser-ai-agent --version
51
+
52
+ # List profiles (verifies API connection)
53
+ nstbrowser-ai-agent profile list
54
+ ```
55
+
56
+ If you see your profiles or an empty list, your environment is configured correctly.
57
+
58
+
7
59
  ### npm (Recommended)
8
60
 
9
61
  Install globally via npm to get the native Rust binary for maximum performance:
@@ -86,6 +138,144 @@ nstbrowser-ai-agent install --with-deps
86
138
  # or manually: npx playwright install-deps chromium
87
139
  ```
88
140
 
141
+ ## Quick Start Examples
142
+
143
+ ### Using Temporary Browser (Fastest)
144
+
145
+ For quick tests or one-time tasks:
146
+
147
+ ```bash
148
+ # Start temporary browser
149
+ nstbrowser-ai-agent browser start-once
150
+
151
+ # Open a website
152
+ nstbrowser-ai-agent open https://example.com
153
+
154
+ # Take a snapshot
155
+ nstbrowser-ai-agent snapshot -i
156
+
157
+ # Close browser (auto-cleanup)
158
+ nstbrowser-ai-agent close
159
+ ```
160
+
161
+ **Note:** Temporary browsers don't save session state.
162
+
163
+ ### Using Profile (Recommended)
164
+
165
+ For tasks requiring persistent sessions:
166
+
167
+ ```bash
168
+ # List available profiles
169
+ nstbrowser-ai-agent profile list
170
+
171
+ # Create a new profile (if needed)
172
+ nstbrowser-ai-agent profile create my-profile
173
+
174
+ # Set default profile
175
+ export NST_PROFILE="my-profile"
176
+
177
+ # Open browser (auto-starts with profile)
178
+ nstbrowser-ai-agent open https://example.com
179
+
180
+ # Interact with page
181
+ nstbrowser-ai-agent snapshot -i
182
+ nstbrowser-ai-agent click @e1
183
+
184
+ # Close browser (session saved to profile)
185
+ nstbrowser-ai-agent close
186
+ ```
187
+
188
+ ### Profile Specification for Browser Actions
189
+
190
+ **All browser actions** (open, click, fill, type, etc.) support specifying a profile name or ID. The CLI will automatically handle profile resolution and browser startup.
191
+
192
+ #### Profile Resolution Priority
193
+
194
+ When you specify a profile for a browser action, the system follows these rules:
195
+
196
+ 1. **Check running browsers** - First, it looks for a browser already running with the specified name or ID
197
+ - If multiple browsers match a name, the earliest started browser is used
198
+ - Profile IDs always uniquely match a single browser
199
+
200
+ 2. **Start browser if not running** - If the profile exists but isn't running, the browser is automatically started
201
+
202
+ 3. **Create profile if name doesn't exist** - If you specify a profile **name** that doesn't exist, a new profile is automatically created
203
+ - This makes it easy to create profiles on-the-fly
204
+
205
+ 4. **Error if ID doesn't exist** - If you specify a profile **ID** that doesn't exist, an error is thrown
206
+ - Profile IDs are expected to be exact matches
207
+
208
+ 5. **Use once browser if no profile specified** - If no profile is specified:
209
+ - Uses an existing "once" (temporary) browser if one is running
210
+ - Otherwise creates a new temporary browser
211
+ - Temporary browsers don't persist session data
212
+
213
+ #### Specifying Profiles
214
+
215
+ You can specify profiles in three ways:
216
+
217
+ **1. Environment Variables (Global Default)**
218
+ ```bash
219
+ export NST_PROFILE="my-profile" # Use profile name
220
+ # OR
221
+ export NST_PROFILE_ID="abc-123-def" # Use profile ID
222
+
223
+ # All browser actions will use this profile
224
+ nstbrowser-ai-agent open https://example.com
225
+ nstbrowser-ai-agent click "#button"
226
+ ```
227
+
228
+ **2. Command-Line Flags (Per-Action)**
229
+ ```bash
230
+ # By profile name
231
+ nstbrowser-ai-agent open https://example.com --nst-profile "my-profile"
232
+ nstbrowser-ai-agent click "#button" --nst-profile "my-profile"
233
+
234
+ # By profile ID
235
+ nstbrowser-ai-agent open https://example.com --nst-profile-id "abc-123-def"
236
+ nstbrowser-ai-agent fill "#email" "test@test.com" --nst-profile-id "abc-123-def"
237
+ ```
238
+
239
+ **3. Mixed Approach**
240
+ ```bash
241
+ # Set default profile
242
+ export NST_PROFILE="default-profile"
243
+
244
+ # Most commands use the default
245
+ nstbrowser-ai-agent open https://example.com
246
+
247
+ # Override for specific commands
248
+ nstbrowser-ai-agent click "#button" --nst-profile "other-profile"
249
+ ```
250
+
251
+ #### Examples
252
+
253
+ **Auto-create profile on first use:**
254
+ ```bash
255
+ # This will create "test-profile" if it doesn't exist
256
+ nstbrowser-ai-agent open https://example.com --nst-profile "test-profile"
257
+ nstbrowser-ai-agent click "#login" --nst-profile "test-profile"
258
+ ```
259
+
260
+ **Use existing profile by ID:**
261
+ ```bash
262
+ # List profiles to find ID
263
+ nstbrowser-ai-agent profile list
264
+
265
+ # Use specific profile ID
266
+ nstbrowser-ai-agent open https://example.com --nst-profile-id "abc-123-def"
267
+ ```
268
+
269
+ **Reuse running browser:**
270
+ ```bash
271
+ # Start a browser with a profile
272
+ nstbrowser-ai-agent open https://example.com --nst-profile "my-profile"
273
+
274
+ # Later commands automatically connect to the same running browser
275
+ nstbrowser-ai-agent click "#button" --nst-profile "my-profile"
276
+ # No restart needed - uses existing browser!
277
+ ```
278
+
89
279
  ## Default Provider
90
280
 
91
281
  By default, nstbrowser-ai-agent uses **Nstbrowser** as the browser provider. This means you don't need to specify `-p nst` every time - it's automatic.
@@ -112,29 +302,6 @@ nstbrowser-ai-agent browser list # List running browsers
112
302
  nstbrowser-ai-agent browser start profile-id # Start browser with profile
113
303
  ```
114
304
 
115
- ### Using Local Browser Mode
116
-
117
- If you want to use a local browser instead of Nstbrowser, use the `--local` flag:
118
-
119
- ```bash
120
- # Use local browser (no API key needed)
121
- nstbrowser-ai-agent --local open example.com
122
- nstbrowser-ai-agent --headed open example.com # Visual browser (also uses local)
123
- ```
124
-
125
- ### Provider Selection Logic
126
-
127
- The provider is selected automatically based on the following priority (highest to lowest):
128
-
129
- 1. Explicit `--provider` flag
130
- 2. `--local` flag (uses local browser)
131
- 3. `--headed` flag (implies local)
132
- 4. `--cdp` flag (implies local)
133
- 5. `--auto-connect` flag (implies local)
134
- 6. `NST_API_KEY` environment variable (uses nst)
135
- 7. **Default: nst (Nstbrowser)**
136
-
137
- This means if you have `NST_API_KEY` set, Nstbrowser will be used automatically. To override this, use `--local` or any other flag that implies local mode.
138
305
 
139
306
  ### Traditional Selectors (also supported)
140
307
 
@@ -173,7 +340,6 @@ nstbrowser-ai-agent screenshot --annotate # Annotated screenshot with numbered
173
340
  nstbrowser-ai-agent pdf <path> # Save as PDF
174
341
  nstbrowser-ai-agent snapshot # Accessibility tree with refs (best for AI)
175
342
  nstbrowser-ai-agent eval <js> # Run JavaScript (-b for base64, --stdin for piped input)
176
- nstbrowser-ai-agent connect <port> # Connect to browser via CDP
177
343
  nstbrowser-ai-agent close # Close browser (aliases: quit, exit)
178
344
  ```
179
345
 
@@ -478,7 +644,6 @@ nstbrowser-ai-agent --session-name secure open example.com
478
644
  | `NSTBROWSER_AI_AGENT_ENCRYPTION_KEY` | 64-char hex key for AES-256-GCM encryption |
479
645
  | `NSTBROWSER_AI_AGENT_STATE_EXPIRE_DAYS` | Auto-delete states older than N days (default: 30) |
480
646
  | `NSTBROWSER_AI_AGENT_PROVIDER` | Browser provider (default: nst) |
481
- | `NSTBROWSER_AI_AGENT_LOCAL` | Use local browser instead of Nstbrowser |
482
647
  | `NST_API_KEY` | Nstbrowser API key (required for nst provider, default provider) |
483
648
  | `NST_HOST` | Nstbrowser API host (default: localhost) |
484
649
  | `NST_PORT` | Nstbrowser API port (default: 8848) |
@@ -503,7 +668,6 @@ nstbrowser-ai-agent includes security features for safe AI agent deployments. Al
503
668
  | `NSTBROWSER_AI_AGENT_CONFIRM_ACTIONS` | Action categories requiring confirmation |
504
669
  | `NSTBROWSER_AI_AGENT_CONFIRM_INTERACTIVE` | Enable interactive confirmation prompts |
505
670
  | `NSTBROWSER_AI_AGENT_PROVIDER` | Browser provider (default: nst) |
506
- | `NSTBROWSER_AI_AGENT_LOCAL` | Use local browser instead of Nstbrowser |
507
671
  | `NST_API_KEY` | Nstbrowser API key (required for nst provider, default provider) |
508
672
  | `NST_HOST` | Nstbrowser API host (default: localhost) |
509
673
  | `NST_PORT` | Nstbrowser API port (default: 8848) |
@@ -573,13 +737,9 @@ This is useful for multimodal AI models that can reason about visual layout, unl
573
737
  | `--ignore-https-errors` | Ignore HTTPS certificate errors (useful for self-signed certs) |
574
738
  | `--allow-file-access` | Allow file:// URLs to access local files (Chromium only) |
575
739
  | `-p, --provider <name>` | Browser provider: `nst` (default), `local` (or `NSTBROWSER_AI_AGENT_PROVIDER` env) |
576
- | `--local` | Use local browser instead of Nstbrowser (or `NSTBROWSER_AI_AGENT_LOCAL` env) |
577
740
  | `--json` | JSON output (for agents) |
578
741
  | `--full, -f` | Full page screenshot |
579
742
  | `--annotate` | Annotated screenshot with numbered element labels (or `NSTBROWSER_AI_AGENT_ANNOTATE` env) |
580
- | `--headed` | Show browser window (not headless, implies local mode) |
581
- | `--cdp <port\|url>` | Connect via Chrome DevTools Protocol (port or WebSocket URL, implies local mode) |
582
- | `--auto-connect` | Auto-discover and connect to running Chrome (implies local mode) (or `NSTBROWSER_AI_AGENT_AUTO_CONNECT` env) |
583
743
  | `--color-scheme <scheme>` | Color scheme: `dark`, `light`, `no-preference` (or `NSTBROWSER_AI_AGENT_COLOR_SCHEME` env) |
584
744
  | `--download-path <path>` | Default download directory (or `NSTBROWSER_AI_AGENT_DOWNLOAD_PATH` env) |
585
745
  | `--content-boundaries` | Wrap page output in boundary markers for LLM safety (or `NSTBROWSER_AI_AGENT_CONTENT_BOUNDARIES` env) |
@@ -876,59 +1036,6 @@ The `--allow-file-access` flag adds Chromium flags (`--allow-file-access-from-fi
876
1036
 
877
1037
  **Note:** This flag only works with Chromium. For security, it's disabled by default.
878
1038
 
879
- ## CDP Mode
880
-
881
- Connect to an existing browser via Chrome DevTools Protocol:
882
-
883
- ```bash
884
- # Start Chrome with: google-chrome --remote-debugging-port=9222
885
-
886
- # Connect once, then run commands without --cdp
887
- nstbrowser-ai-agent connect 9222
888
- nstbrowser-ai-agent snapshot
889
- nstbrowser-ai-agent tab
890
- nstbrowser-ai-agent close
891
-
892
- # Or pass --cdp on each command
893
- nstbrowser-ai-agent --cdp 9222 snapshot
894
-
895
- # Connect to remote browser via WebSocket URL
896
- nstbrowser-ai-agent --cdp "wss://your-browser-service.com/cdp?token=..." snapshot
897
- ```
898
-
899
- The `--cdp` flag accepts either:
900
- - A port number (e.g., `9222`) for local connections via `http://localhost:{port}`
901
- - A full WebSocket URL (e.g., `wss://...` or `ws://...`) for remote browser services
902
-
903
- This enables control of:
904
- - Electron apps
905
- - Chrome/Chromium instances with remote debugging
906
- - WebView2 applications
907
- - Any browser exposing a CDP endpoint
908
-
909
- ### Auto-Connect
910
-
911
- Use `--auto-connect` to automatically discover and connect to a running Chrome instance without specifying a port:
912
-
913
- ```bash
914
- # Auto-discover running Chrome with remote debugging
915
- nstbrowser-ai-agent --auto-connect open example.com
916
- nstbrowser-ai-agent --auto-connect snapshot
917
-
918
- # Or via environment variable
919
- NSTBROWSER_AI_AGENT_AUTO_CONNECT=1 nstbrowser-ai-agent snapshot
920
- ```
921
-
922
- Auto-connect discovers Chrome by:
923
- 1. Reading Chrome's `DevToolsActivePort` file from the default user data directory
924
- 2. Falling back to probing common debugging ports (9222, 9229)
925
-
926
- This is useful when:
927
- - Chrome 144+ has remote debugging enabled via `chrome://inspect/#remote-debugging` (which uses a dynamic port)
928
- - You want a zero-configuration connection to your existing browser
929
- - You don't want to track which port Chrome is using
930
-
931
- ## Streaming (Browser Preview)
932
1039
 
933
1040
  Stream the browser viewport via WebSocket for live preview or "pair browsing" where a human can watch and interact alongside an AI agent.
934
1041
 
@@ -1137,10 +1244,11 @@ For more consistent results, add to your project or global instructions file:
1137
1244
  Use `nstbrowser-ai-agent` for web automation. Run `nstbrowser-ai-agent --help` for all commands.
1138
1245
 
1139
1246
  Core workflow:
1140
- 1. `nstbrowser-ai-agent open <url>` - Navigate to page
1141
- 2. `nstbrowser-ai-agent snapshot -i` - Get interactive elements with refs (@e1, @e2)
1142
- 3. `nstbrowser-ai-agent click @e1` / `fill @e2 "text"` - Interact using refs
1143
- 4. Re-snapshot after page changes
1247
+ 1. Set profile: `export NST_PROFILE="my-profile"` or use `browser start-once` for temporary browser
1248
+ 2. `nstbrowser-ai-agent open <url>` - Navigate to page
1249
+ 3. `nstbrowser-ai-agent snapshot -i` - Get interactive elements with refs (@e1, @e2)
1250
+ 4. `nstbrowser-ai-agent click @e1` / `fill @e2 "text"` - Interact using refs
1251
+ 5. Re-snapshot after page changes
1144
1252
  ```
1145
1253
 
1146
1254
  ## Nstbrowser Integration
@@ -1245,7 +1353,6 @@ You can still use `--profile-id` for explicit ID specification if preferred, but
1245
1353
  # With default NST provider (NST_API_KEY set), no 'nst' prefix needed:
1246
1354
  nstbrowser-ai-agent browser list # List running instances
1247
1355
  nstbrowser-ai-agent browser start profile-123 # Start browser for profile
1248
- nstbrowser-ai-agent browser start-batch p1 p2 p3 # Start multiple browsers
1249
1356
  nstbrowser-ai-agent browser start-once # Start temporary browser
1250
1357
  nstbrowser-ai-agent browser stop profile-123 # Stop browser instance
1251
1358
  nstbrowser-ai-agent browser stop-all # Stop all instances
@@ -1259,7 +1366,6 @@ nstbrowser-ai-agent browser connect-once # Connect to temp browser
1259
1366
  # Traditional explicit syntax still works:
1260
1367
  nstbrowser-ai-agent nst browser list
1261
1368
  nstbrowser-ai-agent nst browser start profile-123
1262
- nstbrowser-ai-agent nst browser start-batch p1 p2 p3
1263
1369
  nstbrowser-ai-agent nst browser stop profile-123
1264
1370
  nstbrowser-ai-agent nst browser stop-all
1265
1371
  ```
Binary file
Binary file
Binary file
@@ -1 +1 @@
1
- {"version":3,"file":"actions.d.ts","sourceRoot":"","sources":["../src/actions.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,cAAc,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAqBpE,OAAO,KAAK,EACV,OAAO,EACP,QAAQ,EAsIT,MAAM,YAAY,CAAC;AAQpB;;;GAGG;AACH,wBAAgB,0BAA0B,CACxC,QAAQ,EAAE,CAAC,CAAC,KAAK,EAAE,eAAe,KAAK,IAAI,CAAC,GAAG,IAAI,GAClD,IAAI,CAEN;AAQD;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,GAAG,KAAK,CAqDzE;AAKD,wBAAgB,gBAAgB,IAAI,IAAI,CAuBvC;AAED;;GAEG;AACH,wBAAsB,cAAc,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,cAAc,GAAG,OAAO,CAAC,QAAQ,CAAC,CA6CjG"}
1
+ {"version":3,"file":"actions.d.ts","sourceRoot":"","sources":["../src/actions.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,cAAc,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAwBpE,OAAO,KAAK,EACV,OAAO,EACP,QAAQ,EAsIT,MAAM,YAAY,CAAC;AAQpB;;;GAGG;AACH,wBAAgB,0BAA0B,CACxC,QAAQ,EAAE,CAAC,CAAC,KAAK,EAAE,eAAe,KAAK,IAAI,CAAC,GAAG,IAAI,GAClD,IAAI,CAEN;AAQD;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,GAAG,KAAK,CAqDzE;AAKD,wBAAgB,gBAAgB,IAAI,IAAI,CAuBvC;AAED;;GAEG;AACH,wBAAsB,cAAc,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,cAAc,GAAG,OAAO,CAAC,QAAQ,CAAC,CA2DjG"}
package/dist/actions.js CHANGED
@@ -2,6 +2,9 @@ import * as fs from 'fs';
2
2
  import * as path from 'path';
3
3
  import { mkdirSync } from 'node:fs';
4
4
  import { getAppDir } from './daemon.js';
5
+ import { resolveBrowserProfile, extractProfileOptions } from './browser-profile-resolver.js';
6
+ import { NstbrowserClient } from './nstbrowser-client.js';
7
+ import { loadNstConfig } from './config-loader.js';
5
8
  import { checkPolicy, describeAction, getActionCategory, loadPolicyFile, initPolicyReloader, reloadPolicyIfChanged, } from './action-policy.js';
6
9
  import { requestConfirmation, getAndRemovePending } from './confirmation.js';
7
10
  import { getAuthProfile, updateLastLogin } from './auth-vault.js';
@@ -111,6 +114,17 @@ export async function executeCommand(command, browser) {
111
114
  confirmation_id: confirmationId,
112
115
  });
113
116
  }
117
+ // === Profile-Aware Browser Action Handling ===
118
+ // Check if this is a browser action command that needs profile resolution
119
+ // Skip launch command as it already handles its own profile resolution
120
+ if (command.action !== 'launch' &&
121
+ command.action !== 'state_list' &&
122
+ command.action !== 'state_show' &&
123
+ command.action !== 'state_clean' &&
124
+ command.action !== 'state_clear' &&
125
+ command.action !== 'state_rename') {
126
+ await ensureBrowserWithProfile(command, browser);
127
+ }
114
128
  return await dispatchAction(command, browser);
115
129
  }
116
130
  catch (error) {
@@ -118,6 +132,68 @@ export async function executeCommand(command, browser) {
118
132
  return errorResponse(command.id, message);
119
133
  }
120
134
  }
135
+ /**
136
+ * Ensure browser is launched with the correct profile before executing an action
137
+ * Implements the unified profile resolution logic for all browser actions
138
+ */
139
+ async function ensureBrowserWithProfile(command, browser) {
140
+ const cmdWithProfile = command;
141
+ // Check if command has profile specifications or environment variables are set
142
+ const hasProfileSpec = !!cmdWithProfile.nstProfileName ||
143
+ !!cmdWithProfile.nstProfileId ||
144
+ !!process.env.NST_PROFILE ||
145
+ !!process.env.NST_PROFILE_ID;
146
+ // Only proceed with profile resolution if:
147
+ // 1. Profile is explicitly specified in the command OR
148
+ // 2. Environment variables are set OR
149
+ // 3. We're using NST provider (default or explicit)
150
+ const provider = process.env.NSTBROWSER_AI_AGENT_PROVIDER;
151
+ const isNstProvider = !provider || provider === 'nst'; // Default is NST
152
+ if (!hasProfileSpec && !isNstProvider) {
153
+ // Not NST provider and no profile specified, skip profile resolution
154
+ return;
155
+ }
156
+ // If browser is not launched or needs reconnection, launch with profile
157
+ if (!browser.isLaunched()) {
158
+ const config = loadNstConfig();
159
+ if (!config) {
160
+ // NST not configured, skip profile resolution (will use local browser)
161
+ if (process.env.NSTBROWSER_AI_AGENT_DEBUG === '1') {
162
+ console.error('[DEBUG] NST not configured, skipping profile resolution');
163
+ }
164
+ return;
165
+ }
166
+ // Launch browser with profile resolution
167
+ const client = new NstbrowserClient(config.host, config.port, config.apiKey);
168
+ try {
169
+ const profileOptions = extractProfileOptions(cmdWithProfile, config.host, config.port, config.apiKey);
170
+ const resolved = await resolveBrowserProfile(client, profileOptions);
171
+ if (process.env.NSTBROWSER_AI_AGENT_DEBUG === '1') {
172
+ console.error('[DEBUG] Resolved profile for action:', {
173
+ action: command.action,
174
+ profileId: resolved.profileId,
175
+ profileName: resolved.profileName,
176
+ isOnce: resolved.isOnce,
177
+ wasCreated: resolved.wasCreated,
178
+ });
179
+ }
180
+ // Launch browser with the resolved profile
181
+ await browser.launch({
182
+ id: command.id,
183
+ action: 'launch',
184
+ provider: 'nst',
185
+ nstProfileId: resolved.profileId,
186
+ nstProfileName: resolved.profileName,
187
+ });
188
+ }
189
+ catch (error) {
190
+ if (process.env.NSTBROWSER_AI_AGENT_DEBUG === '1') {
191
+ console.error('[DEBUG] Profile resolution failed:', error);
192
+ }
193
+ throw error;
194
+ }
195
+ }
196
+ }
121
197
  /**
122
198
  * Dispatch a command to its handler after policy checks have passed.
123
199
  */