@frontman-ai/nextjs 0.1.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.
package/README.md ADDED
@@ -0,0 +1,471 @@
1
+ # @frontman-ai/nextjs
2
+
3
+ Next.js integration for Frontman - provides development tools and observability for Next.js applications.
4
+
5
+ ## Installation
6
+
7
+ ### Quick Install (Recommended)
8
+
9
+ The fastest way to install Frontman is using our CLI installer:
10
+
11
+ ```bash
12
+ # Using curl (server host is auto-detected from the URL)
13
+ curl https://api.frontman.sh/install/nextjs | bash
14
+
15
+ # For local development
16
+ curl http://frontman.local:4000/install/nextjs | bash
17
+
18
+ # Or using npx directly
19
+ npx @frontman-ai/nextjs install --server api.frontman.sh
20
+ ```
21
+
22
+ The installer will:
23
+ - Detect your Next.js version (15 or 16+)
24
+ - Create the appropriate middleware/proxy file
25
+ - Set up OpenTelemetry instrumentation
26
+ - Configure everything to connect to your Frontman server
27
+
28
+ ### CLI Options
29
+
30
+ ```bash
31
+ npx @frontman-ai/nextjs install [options]
32
+
33
+ Options:
34
+ --server <host> Frontman server host (required)
35
+ --prefix <path> Target directory (default: current directory)
36
+ --dry-run Preview changes without writing files
37
+ --skip-deps Skip dependency installation
38
+ --help Show help message
39
+ ```
40
+
41
+ ### Manual Installation
42
+
43
+ If you prefer to set things up manually or need to integrate with an existing configuration:
44
+
45
+ ```bash
46
+ npm install @frontman-ai/nextjs
47
+ ```
48
+
49
+ Then follow the [Manual Setup](#manual-setup) instructions below.
50
+
51
+ ## Quick Start
52
+
53
+ After running the installer, you're ready to go! Start your Next.js dev server:
54
+
55
+ ```bash
56
+ npm run dev
57
+ ```
58
+
59
+ Then open your browser to `http://localhost:3000/__frontman` to access the Frontman UI.
60
+
61
+ ## Manual Setup
62
+
63
+ ### Next.js 15 (middleware.ts)
64
+
65
+ Create `middleware.ts` in your project root:
66
+
67
+ ```typescript
68
+ import { createMiddleware } from '@frontman-ai/nextjs';
69
+ import { NextRequest, NextResponse } from 'next/server';
70
+
71
+ const frontman = createMiddleware({
72
+ host: 'api.frontman.sh', // or 'frontman.local:4000' for local development
73
+ });
74
+
75
+ export async function middleware(req: NextRequest) {
76
+ const response = await frontman(req);
77
+ if (response) return response;
78
+ return NextResponse.next();
79
+ }
80
+
81
+ export const config = {
82
+ matcher: ['/__frontman/:path*'],
83
+ };
84
+ ```
85
+
86
+ ### Next.js 16+ (proxy.ts)
87
+
88
+ Create `proxy.ts` in your project root:
89
+
90
+ ```typescript
91
+ import { createMiddleware } from '@frontman-ai/nextjs';
92
+ import { NextRequest, NextResponse } from 'next/server';
93
+
94
+ const frontman = createMiddleware({
95
+ host: 'api.frontman.sh', // or 'frontman.local:4000' for local development
96
+ });
97
+
98
+ export function proxy(req: NextRequest): NextResponse | Promise<NextResponse> {
99
+ if (req.nextUrl.pathname.startsWith('/__frontman')) {
100
+ return frontman(req) || NextResponse.next();
101
+ }
102
+ return NextResponse.next();
103
+ }
104
+ ```
105
+
106
+ ### OpenTelemetry Setup (Recommended)
107
+
108
+ Create `instrumentation.ts` in your project root (or `src/` if you use that directory):
109
+
110
+ ```typescript
111
+ export async function register() {
112
+ if (process.env.NEXT_RUNTIME === 'nodejs') {
113
+ const { NodeSDK } = await import('@opentelemetry/sdk-node');
114
+ const { setup } = await import('@frontman-ai/nextjs/Instrumentation');
115
+ const [logProcessor, spanProcessor] = setup();
116
+ new NodeSDK({
117
+ logRecordProcessors: [logProcessor],
118
+ spanProcessors: [spanProcessor],
119
+ }).start();
120
+ }
121
+ }
122
+ ```
123
+
124
+ **That's it!** Frontman will now:
125
+ - Capture console logs, build output, and errors
126
+ - Track Next.js HTTP requests, API routes, and rendering
127
+ - Make all logs available via the Frontman UI at `/__frontman`
128
+
129
+ ## Adding to Existing Files
130
+
131
+ If you already have `middleware.ts`, `proxy.ts`, or `instrumentation.ts` files, the installer will show you manual integration steps. Here's how to add Frontman to existing files:
132
+
133
+ ### Existing middleware.ts
134
+
135
+ ```typescript
136
+ import { createMiddleware } from '@frontman-ai/nextjs';
137
+ // ... your other imports
138
+
139
+ const frontman = createMiddleware({
140
+ host: 'api.frontman.sh', // or 'frontman.local:4000' for local development
141
+ });
142
+
143
+ export async function middleware(req: NextRequest) {
144
+ // Add Frontman handler first
145
+ const response = await frontman(req);
146
+ if (response) return response;
147
+
148
+ // ... your existing middleware logic
149
+ return NextResponse.next();
150
+ }
151
+
152
+ export const config = {
153
+ // Add Frontman matcher alongside your existing matchers
154
+ matcher: ['/__frontman/:path*', '/your-other-routes/:path*'],
155
+ };
156
+ ```
157
+
158
+ ### Existing instrumentation.ts
159
+
160
+ ```typescript
161
+ export async function register() {
162
+ if (process.env.NEXT_RUNTIME === 'nodejs') {
163
+ const { setup } = await import('@frontman-ai/nextjs/Instrumentation');
164
+ const [logProcessor, spanProcessor] = setup();
165
+
166
+ // Add to your existing NodeSDK configuration:
167
+ new NodeSDK({
168
+ // ... your existing config
169
+ logRecordProcessors: [logProcessor, ...yourExistingLogProcessors],
170
+ spanProcessors: [spanProcessor, ...yourExistingSpanProcessors],
171
+ }).start();
172
+ }
173
+ }
174
+ ```
175
+
176
+ ## What Gets Captured
177
+
178
+ ### Automatic Console Patching (Node.js only)
179
+ LogCapture automatically initializes when the module is imported and patches:
180
+ - All console methods: `console.log()`, `console.error()`, `console.warn()`, `console.info()`, `console.debug()`
181
+ - `process.stdout.write()` for build output (webpack/turbopack compilation messages)
182
+ - `process.on('uncaughtException')` for unhandled errors
183
+ - `process.on('unhandledRejection')` for unhandled promise rejections
184
+
185
+ **Browser environments are automatically skipped** - no console patching occurs in the browser.
186
+
187
+ ### Via OpenTelemetry Spans (Optional)
188
+ When you set up `instrumentation.ts`:
189
+ - HTTP requests (`BaseServer.handleRequest`)
190
+ - Route rendering (`AppRender.getBodyResult`)
191
+ - API route execution (`AppRouteRouteHandlers.runHandler`)
192
+ - Request method, path, status code, duration
193
+
194
+ ### Storage & Cross-Context Sharing
195
+ All captured data is stored in a **circular buffer** (1024 entries by default) using a `globalThis` singleton pattern. This ensures logs are shared across Next.js/Turbopack execution contexts:
196
+ - Instrumentation context (startup)
197
+ - Page render context
198
+ - API route context
199
+ - Middleware context (Edge runtime - read-only)
200
+
201
+ The buffer persists for the lifetime of the Node.js process and is accessible through the Frontman UI and `get_logs` tool.
202
+
203
+ ## Configuration Options
204
+
205
+ ### Middleware Options
206
+
207
+ ```typescript
208
+ createMiddleware({
209
+ host: string, // Frontman server host (required) - the client UI connects here via WebSocket
210
+ basePath: string, // Base path for Frontman routes (default: "__frontman")
211
+ serverName: string, // Server name (default: "frontman-nextjs")
212
+ serverVersion: string, // Server version (default: package version)
213
+ clientUrl: string, // Custom client bundle URL
214
+ clientCssUrl: string, // Custom client CSS URL
215
+ entrypointUrl: string, // Custom entrypoint URL
216
+ isLightTheme: boolean, // Use light theme (default: false)
217
+ projectRoot: string, // Project root directory (default: process.cwd())
218
+ })
219
+ ```
220
+
221
+ ### Understanding the `host` Option
222
+
223
+ The `host` option specifies the Frontman server that the client UI will connect to for AI capabilities. When you visit `/__frontman` in your Next.js app:
224
+
225
+ 1. The middleware serves the Frontman UI HTML
226
+ 2. The UI loads the client JavaScript with `?host=<your-host>`
227
+ 3. The client establishes a WebSocket connection to `wss://<host>/socket`
228
+ 4. AI interactions and tool calls flow through this connection
229
+
230
+ The middleware itself doesn't connect to the Frontman server - it only passes the host to the client.
231
+
232
+ **Examples:**
233
+ - Production: `host: 'api.frontman.sh'` → client connects to `wss://api.frontman.sh/socket`
234
+ - Local dev: `host: 'frontman.local:4000'` → client connects to `wss://frontman.local:4000/socket`
235
+
236
+ ## Supported Next.js Versions
237
+
238
+ | Version | Middleware File | Status |
239
+ |---------|----------------|--------|
240
+ | Next.js 15.x | `middleware.ts` | Fully supported |
241
+ | Next.js 16.x | `proxy.ts` | Fully supported |
242
+
243
+ Both versions have built-in OpenTelemetry support with no additional configuration required.
244
+
245
+ ## Architecture
246
+
247
+ ```
248
+ Next.js App (Turbopack/Webpack)
249
+
250
+ ├─> Module Import (first context - instrumentation)
251
+ │ └─> LogCapture auto-initializes at module level
252
+ │ ├─> Creates globalThis.__FRONTMAN_INSTANCE__
253
+ │ ├─> Patches console.log/warn/error/info/debug
254
+ │ ├─> Intercepts process.stdout.write
255
+ │ └─> Listens to uncaughtException/unhandledRejection
256
+
257
+ ├─> Module Import (second context - page render)
258
+ │ └─> LogCapture reuses existing globalThis.__FRONTMAN_INSTANCE__
259
+ │ └─> Same buffer, no re-patching (guarded by __FRONTMAN_CONSOLE_PATCHED__ flag)
260
+
261
+ ├─> instrumentation.ts (startup) - OPTIONAL
262
+ │ └─> setup() returns OTEL processors that write to same buffer
263
+
264
+ ├─> middleware.ts / proxy.ts (per-request)
265
+ │ └─> Serves Frontman UI at /__frontman
266
+ │ └─> Connects to Frontman server for AI tools
267
+
268
+ └─> OpenTelemetry SDK (optional)
269
+ ├─> LogRecordProcessor → globalThis.__FRONTMAN_INSTANCE__.buffer
270
+ └─> SpanProcessor → globalThis.__FRONTMAN_INSTANCE__.buffer
271
+ ```
272
+
273
+ ### Key Technical Details
274
+
275
+ **Cross-Context Buffer Sharing**
276
+ - Next.js 15+ with Turbopack runs code in multiple isolated contexts
277
+ - `globalThis.__FRONTMAN_INSTANCE__` stores the singleton buffer instance
278
+ - All contexts read/write to the same circular buffer
279
+ - Console patching happens only once (protected by `__FRONTMAN_CONSOLE_PATCHED__` flag)
280
+
281
+ **Circular Buffer**
282
+ - Fixed capacity: 1024 entries (configurable)
283
+ - Oldest entries automatically evicted when full
284
+ - Entries include: timestamp, level, message, attributes, consoleMethod
285
+ - Thread-safe for concurrent writes from different contexts
286
+
287
+ ## Advanced Usage
288
+
289
+ ### Custom OTEL Configuration
290
+
291
+ If you need more control over OpenTelemetry setup:
292
+
293
+ ```typescript
294
+ import { setup } from '@frontman-ai/nextjs/Instrumentation';
295
+ import { NodeSDK } from '@opentelemetry/sdk-node';
296
+ import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-http';
297
+
298
+ export async function register() {
299
+ if (process.env.NEXT_RUNTIME === 'nodejs') {
300
+ const [logProcessor, spanProcessor] = setup();
301
+
302
+ new NodeSDK({
303
+ serviceName: 'my-app',
304
+ resource: resourceFromAttributes({
305
+ 'service.version': '1.0.0',
306
+ }),
307
+ traceExporter: new OTLPTraceExporter({
308
+ url: process.env.OTEL_EXPORTER_OTLP_ENDPOINT,
309
+ }),
310
+ logRecordProcessors: [logProcessor],
311
+ spanProcessors: [spanProcessor],
312
+ }).start();
313
+ }
314
+ }
315
+ ```
316
+
317
+ ### Without OpenTelemetry
318
+
319
+ Frontman works without OpenTelemetry! If you only set up middleware (skip `instrumentation.ts`):
320
+ - Console logs are still captured (auto-initialized at module import)
321
+ - Build output is tracked
322
+ - Errors are logged
323
+ - Frontman UI available at `/__frontman`
324
+ - HTTP spans are not captured (requires OTEL)
325
+
326
+ LogCapture auto-initializes when the module is imported, so console patching happens automatically in Node.js environments - no explicit initialization needed.
327
+
328
+ ### Custom LogCapture Configuration
329
+
330
+ You can customize the buffer size and stdout patterns:
331
+
332
+ ```typescript
333
+ import { initialize } from '@frontman-ai/nextjs/LogCapture';
334
+
335
+ // Call this BEFORE any console.log() calls (e.g., in instrumentation.ts)
336
+ initialize({
337
+ bufferCapacity: 2048, // Default: 1024
338
+ stdoutPatterns: ['webpack', 'turbopack', 'Compiled', 'Failed', 'custom-pattern'],
339
+ });
340
+ ```
341
+
342
+ **Note:** Configuration only takes effect on the first call. Subsequent calls are ignored because the singleton instance is already created.
343
+
344
+ ## Troubleshooting
345
+
346
+ ### Logs not being captured
347
+
348
+ **Check 1: Verify module is imported**
349
+ LogCapture only initializes when the module is imported in a Node.js context. Make sure either:
350
+ - You have `instrumentation.ts` that imports from `@frontman-ai/nextjs/Instrumentation`
351
+ - OR you have `middleware.ts` that imports from `@frontman-ai/nextjs`
352
+
353
+ **Check 2: Verify Node.js runtime**
354
+ LogCapture doesn't run in browser or Edge runtime. Check your environment:
355
+ ```javascript
356
+ console.log('Runtime:', process.env.NEXT_RUNTIME); // Should be 'nodejs'
357
+ ```
358
+
359
+ **Check 3: Verify buffer contents**
360
+ Query the buffer directly to see if logs are present:
361
+ ```typescript
362
+ import { getLogs } from '@frontman-ai/nextjs/LogCapture';
363
+
364
+ const allLogs = getLogs();
365
+ console.log('Buffer contains', allLogs.length, 'logs');
366
+ ```
367
+
368
+ **Check 4: Multiple contexts**
369
+ In Next.js 15+, code may run in different contexts. Verify all contexts share the same buffer:
370
+ ```javascript
371
+ console.log('Instance:', globalThis.__FRONTMAN_INSTANCE__);
372
+ console.log('Buffer size:', globalThis.__FRONTMAN_INSTANCE__?.buffer.contents.items.length);
373
+ ```
374
+
375
+ ### Console logs appear twice
376
+
377
+ This is normal behavior - LogCapture captures logs AND calls the original console method so logs still appear in your terminal/browser console.
378
+
379
+ ### Build output not captured
380
+
381
+ By default, only these patterns are captured from `process.stdout`:
382
+ - "webpack"
383
+ - "turbopack"
384
+ - "Compiled"
385
+ - "Failed"
386
+
387
+ To capture additional patterns, use custom configuration (see above).
388
+
389
+ ### Installer shows "manual modification required"
390
+
391
+ This happens when you have existing middleware, proxy, or instrumentation files that don't already include Frontman. The installer won't overwrite your existing code. Follow the [Adding to Existing Files](#adding-to-existing-files) instructions to manually integrate Frontman.
392
+
393
+ ## API
394
+
395
+ ### `createMiddleware(options)`
396
+
397
+ Creates a Next.js middleware handler that serves the Frontman UI and connects to your Frontman server.
398
+
399
+ ```typescript
400
+ import { createMiddleware } from '@frontman-ai/nextjs';
401
+
402
+ const middleware = createMiddleware({
403
+ host: string, // Frontman server host (required)
404
+ basePath: string, // Base path (default: "__frontman")
405
+ serverName: string, // Server name (default: "frontman-nextjs")
406
+ serverVersion: string, // Version (default: package version)
407
+ projectRoot: string, // Project root (default: process.cwd())
408
+ });
409
+ ```
410
+
411
+ **Returns:** `(request: NextRequest) => Promise<NextResponse | undefined>`
412
+
413
+ ### `setup()`
414
+
415
+ Initializes LogCapture (console patching, error handlers) and returns OTEL processors for use with OpenTelemetry SDK.
416
+
417
+ ```typescript
418
+ import { setup } from '@frontman-ai/nextjs/Instrumentation';
419
+
420
+ const [logProcessor, spanProcessor] = setup();
421
+ ```
422
+
423
+ **Returns:** `[LogRecordProcessor, SpanProcessor]`
424
+
425
+ ### `initialize(config?)`
426
+
427
+ Manually initialize LogCapture with custom configuration. Usually not needed since auto-initialization happens at module import.
428
+
429
+ ```typescript
430
+ import { initialize } from '@frontman-ai/nextjs/LogCapture';
431
+
432
+ initialize({
433
+ bufferCapacity: number, // Buffer size (default: 1024)
434
+ stdoutPatterns: string[], // Patterns to capture from stdout
435
+ });
436
+ ```
437
+
438
+ **Returns:** `void`
439
+
440
+ ### `getLogs(options?)`
441
+
442
+ Query the log buffer with optional filters.
443
+
444
+ ```typescript
445
+ import { getLogs } from '@frontman-ai/nextjs/LogCapture';
446
+
447
+ const logs = getLogs({
448
+ pattern: string, // Regex pattern to match messages (case-insensitive)
449
+ level: 'console' | 'build' | 'error', // Filter by log level
450
+ since: number, // Unix timestamp - only logs after this time
451
+ tail: number, // Limit to last N logs
452
+ });
453
+ ```
454
+
455
+ **Returns:** `LogEntry[]`
456
+
457
+ **LogEntry type:**
458
+ ```typescript
459
+ type LogEntry = {
460
+ timestamp: string; // ISO 8601 timestamp
461
+ level: 'console' | 'build' | 'error'; // Log level
462
+ message: string; // Log message (ANSI codes stripped)
463
+ attributes?: Record<string, any>; // Additional attributes
464
+ resource?: Record<string, any>; // Resource info
465
+ consoleMethod?: 'log' | 'info' | 'warn' | 'error' | 'debug'; // Original console method
466
+ };
467
+ ```
468
+
469
+ ## License
470
+
471
+ MIT