claude-dev-server 1.0.2 → 1.1.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.
package/README.md CHANGED
@@ -41,13 +41,10 @@ sudo apt install ttyd
41
41
 
42
42
  ```bash
43
43
  # 默认端口 3000
44
- npx claude-dev-server
44
+ npx claude-dev-server@latest
45
45
 
46
46
  # 指定端口
47
- npx claude-dev-server --port 4000
48
-
49
- # 指定 Claude 路径
50
- npx claude-dev-server --claude-path /path/to/claude
47
+ npx claude-dev-server@latest --port 4000
51
48
  ```
52
49
 
53
50
  就这么简单!工具会:
@@ -62,7 +59,6 @@ npx claude-dev-server --claude-path /path/to/claude
62
59
  | 选项 | 说明 | 默认值 |
63
60
  |------|------|--------|
64
61
  | `-p, --port <port>` | 代理服务器端口 | `3000` |
65
- | `--claude-path <path>` | Claude 可执行文件路径 | `claude` |
66
62
  | `-h, --help` | 显示帮助信息 | - |
67
63
 
68
64
  ## 使用方法
@@ -1,10 +1,11 @@
1
1
  import { spawn } from 'child_process';
2
2
  import { existsSync, readFileSync } from 'fs';
3
- import { dirname, join, resolve } from 'path';
3
+ import { dirname, join, relative, resolve } from 'path';
4
4
  import http from 'http';
5
5
  import { fileURLToPath } from 'url';
6
- import { WebSocketServer } from 'ws';
6
+ import { createConnection } from 'net';
7
7
  import { SourceMapConsumer } from 'source-map';
8
+ import { WebSocketServer } from 'ws';
8
9
  import { readFile } from 'fs/promises';
9
10
 
10
11
  // src/universal/index.ts
@@ -513,37 +514,168 @@ var CLIENT_SCRIPT = `
513
514
  }
514
515
 
515
516
  async function getSourceLocation(el) {
517
+ // Early check: detect Next.js by checking for _next/static chunks
518
+ const hasNextJsChunks = Array.from(document.querySelectorAll('script[src]'))
519
+ .some(script => script.getAttribute('src')?.includes('/_next/static/chunks/'))
520
+
521
+ if (hasNextJsChunks) {
522
+ console.log('[Claude Dev Server] Next.js Turbopack detected, using specialized lookup')
523
+
524
+ const pagePath = window.location.pathname
525
+ console.log('[Claude Dev Server] Looking up source for page:', pagePath)
526
+
527
+ try {
528
+ const params = new URLSearchParams({
529
+ page: pagePath,
530
+ framework: 'nextjs'
531
+ })
532
+ const lookupUrl = '/@sourcemap-lookup?' + params.toString()
533
+ console.log('[Claude Dev Server] Fetching:', lookupUrl)
534
+
535
+ const response = await fetch(lookupUrl)
536
+ console.log('[Claude Dev Server] Response status:', response.status)
537
+
538
+ if (response.ok) {
539
+ const result = await response.json()
540
+ console.log('[Claude Dev Server] Lookup result:', result)
541
+ if (result.file) {
542
+ const elClassName = el.className ? String(el.className) : ''
543
+ const selector = el.tagName.toLowerCase() +
544
+ (el.id ? '#' + el.id : '') +
545
+ (elClassName ? '.' + elClassName.split(' ').filter(c => c).join('.') : '')
546
+
547
+ return {
548
+ url: result.file,
549
+ line: result.line || undefined,
550
+ column: result.column || undefined,
551
+ selector: selector,
552
+ hint: 'File: ' + result.file
553
+ }
554
+ }
555
+ }
556
+ } catch (e) {
557
+ console.log('[Claude Dev Server] Next.js lookup failed:', e)
558
+ }
559
+ // If Next.js lookup failed, continue with normal flow
560
+ }
561
+
516
562
  let sourceFile = null
517
563
  let sourceLine = 1
518
564
  let sourceColumn = 1
519
565
 
520
566
  // Try to get original line number using source map
521
- async function getOriginalLine(generatedFile, generatedLine, generatedColumn) {
567
+ // Use server-side source map resolution with source-map library
568
+ async function resolveSourceMap(filePath, line, column) {
522
569
  try {
523
- // Find the script tag that corresponds to this file
524
- const scripts = Array.from(document.querySelectorAll('script[src]'))
525
- for (const script of scripts) {
526
- const src = script.getAttribute('src')
527
- if (src && (src.includes('/src/') || src.includes('/app.') || src.includes('/main.'))) {
528
- // Try to fetch and parse the source map
529
- const response = await fetch(src + '.map')
530
- if (response.ok) {
531
- const map = await response.json()
532
- // Use the source map to find the original position
533
- const consumer = await new (window.SourceMap || window.sourceMap).SourceMapConsumer(map)
534
- const original = consumer.originalPositionFor({
535
- line: generatedLine,
536
- column: generatedColumn
537
- })
538
- consumer.destroy()
539
- if (original.source && original.line !== null) {
540
- return { line: original.line, column: original.column || 1 }
541
- }
542
- }
570
+ console.log('[Claude Dev Server] Calling server source map resolution for:', filePath, line, column)
571
+
572
+ // Convert absolute file path to URL path for Vite
573
+ let urlPath = filePath
574
+ if (filePath.startsWith('/Users/')) {
575
+ // Convert /Users/.../src/App.jsx to /src/App.jsx
576
+ const parts = filePath.split('/')
577
+ const srcIndex = parts.indexOf('src')
578
+ if (srcIndex >= 0) {
579
+ urlPath = '/' + parts.slice(srcIndex).join('/')
543
580
  }
544
581
  }
582
+
583
+ console.log('[Claude Dev Server] Converted path to URL path:', urlPath)
584
+
585
+ // Use the server-side source map resolution with URL path
586
+ const params = new URLSearchParams({
587
+ file: urlPath,
588
+ line: String(line),
589
+ col: String(column)
590
+ })
591
+
592
+ const response = await fetch('/@sourcemap?' + params.toString())
593
+ if (!response.ok) {
594
+ console.log('[Claude Dev Server] Source map request failed:', response.status)
595
+ return null
596
+ }
597
+
598
+ const result = await response.json()
599
+ console.log('[Claude Dev Server] Source map result:', result)
600
+
601
+ if (result.error) {
602
+ console.log('[Claude Dev Server] Source map error:', result.error)
603
+ return null
604
+ }
605
+
606
+ if (result.file) {
607
+ return {
608
+ file: result.file,
609
+ line: result.line,
610
+ column: result.column
611
+ }
612
+ }
613
+
614
+ return null
545
615
  } catch (e) {
546
616
  console.log('[Claude Dev Server] Source map lookup failed:', e.message)
617
+ return null
618
+ }
619
+ }
620
+
621
+ // Try to extract jsxDEV source location from React Fiber
622
+ function extractJsxDevLocation(fiber) {
623
+ try {
624
+ // The jsxDEV location is stored in the element type's _debugInfo or _source
625
+ // React 18 stores it differently - let's check multiple locations
626
+ const elementType = fiber.elementType || fiber.type
627
+
628
+ // Check if this is a jsxDEV call by looking at the string representation
629
+ if (elementType && typeof elementType === 'function') {
630
+ const fnStr = elementType.toString()
631
+ // jsxDEV functions have a specific pattern
632
+ if (fnStr.includes('jsxDEV') || fnStr.includes('jsx')) {
633
+ console.log('[Claude Dev Server] Found jsxDEV element type')
634
+ }
635
+ }
636
+
637
+ // Try to get _debugSource which might have jsxDEV metadata
638
+ const debugSource = fiber._debugSource || fiber._debugInfo
639
+ if (debugSource) {
640
+ // For jsxDEV, check if there's a _source object with fileName/lineNumber
641
+ const source = debugSource._source || debugSource.source || debugSource
642
+ if (source && source.fileName && source.lineNumber !== undefined) {
643
+ console.log('[Claude Dev Server] Found _debugSource with source location:', source)
644
+ // NOTE: Don't use this directly! The lineNumber might be from transpiled code
645
+ // We'll let the source map resolver handle it
646
+ return null
647
+ }
648
+ }
649
+
650
+ // Check the memoizedState or other properties for source location
651
+ if (fiber.memoizedState) {
652
+ // React might store jsxDEV location in memoizedState
653
+ console.log('[Claude Dev Server] Checking memoizedState:', fiber.memoizedState)
654
+ }
655
+
656
+ // Check the memoizedProps or other properties for source location
657
+ if (fiber.memoizedProps && fiber.memoizedProps.__source) {
658
+ const source = fiber.memoizedProps.__source
659
+ console.log('[Claude Dev Server] Found __source in memoizedProps:', source)
660
+ return {
661
+ file: source.fileName,
662
+ line: source.lineNumber,
663
+ column: source.columnNumber || 1
664
+ }
665
+ }
666
+
667
+ // Check pendingProps for __source
668
+ if (fiber.pendingProps && fiber.pendingProps.__source) {
669
+ const source = fiber.pendingProps.__source
670
+ console.log('[Claude Dev Server] Found __source in pendingProps:', source)
671
+ return {
672
+ file: source.fileName,
673
+ line: source.lineNumber,
674
+ column: source.columnNumber || 1
675
+ }
676
+ }
677
+ } catch (e) {
678
+ console.log('[Claude Dev Server] Error extracting jsxDEV location:', e)
547
679
  }
548
680
  return null
549
681
  }
@@ -557,22 +689,42 @@ var CLIENT_SCRIPT = `
557
689
  const fiber = el[fiberKey]
558
690
  console.log('[Claude Dev Server] Found fiber at key:', fiberKey)
559
691
 
560
- // Try to get _debugSource from different locations
561
- const debugSource = fiber._debugSource || fiber.elementType?._debugSource || fiber.type?._debugSource || fiber.alternate?._debugSource
562
- if (debugSource && debugSource.fileName) {
563
- sourceFile = debugSource.fileName
564
- sourceLine = debugSource.lineNumber || 1
565
- sourceColumn = debugSource.columnNumber || 1
566
-
567
- // Try to use source map to get the original line number
568
- const original = await getOriginalLine(sourceFile, sourceLine, sourceColumn)
569
- if (original) {
570
- sourceLine = original.line
571
- sourceColumn = original.column
572
- console.log('[Claude Dev Server] Original line from source map:', sourceLine)
692
+ // Log fiber structure for debugging
693
+ console.log('[Claude Dev Server] Fiber structure:', {
694
+ _debugSource: fiber._debugSource,
695
+ elementType: fiber.elementType,
696
+ type: fiber.type,
697
+ memoizedProps: fiber.memoizedProps,
698
+ pendingProps: fiber.pendingProps
699
+ })
700
+
701
+ // Log _debugSource details
702
+ if (fiber._debugSource) {
703
+ console.log('[Claude Dev Server] _debugSource details:', JSON.stringify(fiber._debugSource, null, 2))
704
+ }
705
+
706
+ // For Next.js, try to get component name from elementType
707
+ if (!fiber._debugSource && fiber.elementType) {
708
+ const elementType = fiber.elementType
709
+ console.log('[Claude Dev Server] elementType:', elementType)
710
+ console.log('[Claude Dev Server] elementType.name:', elementType.name)
711
+ console.log('[Claude Dev Server] elementType.displayName:', elementType.displayName)
712
+
713
+ // Try to get the function string to find component name
714
+ if (typeof elementType === 'function') {
715
+ const fnStr = elementType.toString()
716
+ console.log('[Claude Dev Server] elementType function:', fnStr.substring(0, 200))
573
717
  }
718
+ }
574
719
 
575
- // Convert absolute path to relative path using project root
720
+ // First, try to extract jsxDEV source location
721
+ const jsxDevLocation = extractJsxDevLocation(fiber)
722
+ if (jsxDevLocation) {
723
+ sourceFile = jsxDevLocation.file
724
+ sourceLine = jsxDevLocation.line
725
+ sourceColumn = jsxDevLocation.column
726
+
727
+ // Convert absolute path to relative path
576
728
  if (sourceFile.startsWith('/')) {
577
729
  const projectRoot = window.__CLAUDE_PROJECT_ROOT__
578
730
  if (projectRoot && sourceFile.startsWith(projectRoot)) {
@@ -582,32 +734,100 @@ var CLIENT_SCRIPT = `
582
734
  }
583
735
  }
584
736
  }
585
- console.log('[Claude Dev Server] Found React source:', sourceFile, sourceLine)
737
+ console.log('[Claude Dev Server] Using jsxDEV location:', sourceFile, sourceLine)
586
738
  } else {
587
- // Try going up the fiber tree
588
- let currentFiber = fiber
589
- let depth = 0
590
- while (currentFiber && depth < 20) {
591
- const ds = currentFiber._debugSource || currentFiber.elementType?._debugSource || currentFiber.type?._debugSource
592
- if (ds && ds.fileName) {
593
- sourceFile = ds.fileName
594
- sourceLine = ds.lineNumber || 1
595
- sourceColumn = ds.columnNumber || 1
596
- // Convert absolute path to relative path using project root
597
- if (sourceFile.startsWith('/')) {
739
+ // Fall back to _debugSource
740
+ const debugSource = fiber._debugSource || fiber.elementType?._debugSource || fiber.type?._debugSource || fiber.alternate?._debugSource
741
+ if (debugSource && debugSource.fileName) {
742
+ sourceFile = debugSource.fileName
743
+ sourceLine = debugSource.lineNumber || 1
744
+ sourceColumn = debugSource.columnNumber || 1
745
+
746
+ // Convert relative path to absolute path for source map resolution
747
+ let absolutePath = sourceFile
748
+ if (!sourceFile.startsWith('/')) {
749
+ // Relative path - resolve relative to project root
750
+ const projectRoot = window.__CLAUDE_PROJECT_ROOT__
751
+ absolutePath = projectRoot + '/' + sourceFile
752
+ }
753
+
754
+ console.log('[Claude Dev Server] Resolving source map for:', absolutePath, 'at line:', sourceLine)
755
+
756
+ // Use server-side source map resolution
757
+ const original = await resolveSourceMap(absolutePath, sourceLine, sourceColumn)
758
+ if (original) {
759
+ sourceFile = original.file
760
+ sourceLine = original.line
761
+ sourceColumn = original.column
762
+ console.log('[Claude Dev Server] Original location from source map:', sourceFile, sourceLine)
763
+ } else {
764
+ // Source map resolution failed, use the original file
765
+ // Convert absolute path back to relative
766
+ if (absolutePath.startsWith('/')) {
598
767
  const projectRoot = window.__CLAUDE_PROJECT_ROOT__
599
- if (projectRoot && sourceFile.startsWith(projectRoot)) {
600
- sourceFile = sourceFile.substring(projectRoot.length + 1)
768
+ if (projectRoot && absolutePath.startsWith(projectRoot)) {
769
+ sourceFile = absolutePath.substring(projectRoot.length + 1)
601
770
  if (sourceFile.startsWith('/')) {
602
771
  sourceFile = sourceFile.substring(1)
603
772
  }
604
773
  }
605
774
  }
606
- console.log('[Claude Dev Server] Found React source at depth', depth, ':', sourceFile, sourceLine)
607
- break
608
775
  }
609
- currentFiber = currentFiber.return || currentFiber.alternate
610
- depth++
776
+ console.log('[Claude Dev Server] Final React source:', sourceFile, sourceLine)
777
+ } else {
778
+ // Try going up the fiber tree
779
+ let currentFiber = fiber
780
+ let depth = 0
781
+ while (currentFiber && depth < 20) {
782
+ const jsxDevLoc = extractJsxDevLocation(currentFiber)
783
+ if (jsxDevLoc) {
784
+ sourceFile = jsxDevLoc.file
785
+ sourceLine = jsxDevLoc.line
786
+ sourceColumn = jsxDevLoc.column
787
+
788
+ if (sourceFile.startsWith('/')) {
789
+ const projectRoot = window.__CLAUDE_PROJECT_ROOT__
790
+ if (projectRoot && sourceFile.startsWith(projectRoot)) {
791
+ sourceFile = sourceFile.substring(projectRoot.length + 1)
792
+ if (sourceFile.startsWith('/')) {
793
+ sourceFile = sourceFile.substring(1)
794
+ }
795
+ }
796
+ }
797
+ console.log('[Claude Dev Server] Found jsxDEV location at depth', depth, ':', sourceFile, sourceLine)
798
+ break
799
+ }
800
+
801
+ const ds = currentFiber._debugSource || currentFiber.elementType?._debugSource || currentFiber.type?._debugSource
802
+ if (ds && ds.fileName) {
803
+ sourceFile = ds.fileName
804
+ sourceLine = ds.lineNumber || 1
805
+ sourceColumn = ds.columnNumber || 1
806
+
807
+ // Use server-side source map resolution
808
+ const original = await resolveSourceMap(sourceFile, sourceLine, sourceColumn)
809
+ if (original) {
810
+ sourceFile = original.file
811
+ sourceLine = original.line
812
+ sourceColumn = original.column
813
+ } else {
814
+ // Convert absolute path to relative path using project root
815
+ if (sourceFile.startsWith('/')) {
816
+ const projectRoot = window.__CLAUDE_PROJECT_ROOT__
817
+ if (projectRoot && sourceFile.startsWith(projectRoot)) {
818
+ sourceFile = sourceFile.substring(projectRoot.length + 1)
819
+ if (sourceFile.startsWith('/')) {
820
+ sourceFile = sourceFile.substring(1)
821
+ }
822
+ }
823
+ }
824
+ }
825
+ console.log('[Claude Dev Server] Found React source at depth', depth, ':', sourceFile, sourceLine)
826
+ break
827
+ }
828
+ currentFiber = currentFiber.return || currentFiber.alternate
829
+ depth++
830
+ }
611
831
  }
612
832
  }
613
833
  }
@@ -775,7 +995,19 @@ var CLIENT_SCRIPT = `
775
995
  const filePath = location.url || location.file || 'unknown'
776
996
  const tagName = el.tagName ? el.tagName.toLowerCase() : ''
777
997
  const id = el.id ? '#' + el.id : ''
778
- const className = el.className ? '.' + String(el.className).split(' ').filter(c => c).join('.') : ''
998
+
999
+ // \u5BF9\u4E8E Tailwind CSS \u7B49\u5927\u91CF class \u7684\u60C5\u51B5\uFF0C\u53EA\u53D6\u524D 2-3 \u4E2A class
1000
+ let className = ''
1001
+ if (el.className) {
1002
+ const classes = String(el.className).split(' ').filter(c => c)
1003
+ // \u5982\u679C class \u592A\u591A\uFF08\u53EF\u80FD\u662F Tailwind\uFF09\uFF0C\u53EA\u53D6\u524D 2 \u4E2A
1004
+ if (classes.length > 5) {
1005
+ className = '.' + classes.slice(0, 2).join('.')
1006
+ } else {
1007
+ className = '.' + classes.join('.')
1008
+ }
1009
+ }
1010
+
779
1011
  const selector = tagName + id + className
780
1012
 
781
1013
  // \u83B7\u53D6\u5143\u7D20\u7684\u6587\u672C\u5185\u5BB9\uFF08\u5982\u679C\u662F\u6587\u672C\u8282\u70B9\u6216\u77ED\u6587\u672C\u5143\u7D20\uFF09
@@ -786,8 +1018,9 @@ var CLIENT_SCRIPT = `
786
1018
  textContent = el.childNodes[0].textContent ? el.childNodes[0].textContent.trim().substring(0, 50) : ''
787
1019
  }
788
1020
 
789
- // \u683C\u5F0F: @filename:line <selector> "text content"
790
- let prompt = \`@\${filePath}:\${location.line} <\${selector}>\`
1021
+ // \u683C\u5F0F: @filename:line <selector> "text content" \u6216 @filename <selector> "text content"
1022
+ const linePart = location.line ? \`:\${location.line}\` : ''
1023
+ let prompt = \`@\${filePath}\${linePart} <\${selector}>\`
791
1024
  if (textContent) {
792
1025
  prompt += \` "\${textContent}"\`
793
1026
  }
@@ -1075,6 +1308,175 @@ async function detectServerPort(childProcess, timeout) {
1075
1308
  childProcess.stdout?.on("data", onData);
1076
1309
  });
1077
1310
  }
1311
+ async function handleSourceMapRequest(projectRoot, filePath, line, column, targetPort) {
1312
+ try {
1313
+ let content;
1314
+ let fullPath = filePath;
1315
+ if (filePath.startsWith("/") && targetPort) {
1316
+ console.log("[Claude Dev Server] Fetching from Vite:", filePath);
1317
+ try {
1318
+ const response = await fetch(`http://localhost:${targetPort}${filePath}`);
1319
+ if (!response.ok) {
1320
+ console.log("[Claude Dev Server] Failed to fetch from Vite:", response.status);
1321
+ return { error: "Failed to fetch from Vite" };
1322
+ }
1323
+ content = await response.text();
1324
+ console.log("[Claude Dev Server] Fetched", content.length, "chars from Vite");
1325
+ } catch (e) {
1326
+ console.log("[Claude Dev Server] Fetch error:", e);
1327
+ return { error: "Fetch error: " + e.message };
1328
+ }
1329
+ } else {
1330
+ if (!filePath.startsWith("/")) {
1331
+ fullPath = join(projectRoot, filePath);
1332
+ }
1333
+ if (!existsSync(fullPath)) {
1334
+ console.log("[Claude Dev Server] File not found:", fullPath);
1335
+ return { error: "File not found" };
1336
+ }
1337
+ content = readFileSync(fullPath, "utf-8");
1338
+ console.log("[Claude Dev Server] Resolving source map for:", fullPath, "at line:", line);
1339
+ }
1340
+ let sourceMapUrl = null;
1341
+ const patterns = [
1342
+ /\/\/[@#]\s*sourceMappingURL=([^\s]+)/,
1343
+ /\/\*[@#]\s*sourceMappingURL=([^\s]+)\s*\*\//
1344
+ ];
1345
+ for (const pattern of patterns) {
1346
+ const match = content.match(pattern);
1347
+ if (match) {
1348
+ sourceMapUrl = match[1];
1349
+ console.log("[Claude Dev Server] Found sourceMappingURL:", sourceMapUrl.substring(0, 100) + "...");
1350
+ break;
1351
+ }
1352
+ }
1353
+ if (!sourceMapUrl) {
1354
+ console.log("[Claude Dev Server] No source map found in:", fullPath);
1355
+ return { file: relative(projectRoot, fullPath), line, column };
1356
+ }
1357
+ let sourceMapContent;
1358
+ let sourceMap;
1359
+ if (sourceMapUrl.startsWith("data:application/json;base64,") || sourceMapUrl.startsWith("data:application/json;charset=utf-8;base64,")) {
1360
+ console.log("[Claude Dev Server] Found inline source map");
1361
+ const base64Data = sourceMapUrl.split(",", 2)[1];
1362
+ sourceMapContent = Buffer.from(base64Data, "base64").toString("utf-8");
1363
+ try {
1364
+ sourceMap = JSON.parse(sourceMapContent);
1365
+ } catch (e) {
1366
+ console.log("[Claude Dev Server] Failed to parse inline source map:", e);
1367
+ return { file: relative(projectRoot, fullPath), line, column };
1368
+ }
1369
+ } else if (sourceMapUrl.startsWith("http://") || sourceMapUrl.startsWith("https://")) {
1370
+ console.log("[Claude Dev Server] Remote source map not supported:", sourceMapUrl);
1371
+ return { file: relative(projectRoot, fullPath), line, column };
1372
+ } else {
1373
+ let sourceMapPath;
1374
+ if (sourceMapUrl.startsWith("/")) {
1375
+ sourceMapPath = sourceMapUrl;
1376
+ } else {
1377
+ sourceMapPath = join(dirname(fullPath), sourceMapUrl);
1378
+ }
1379
+ console.log("[Claude Dev Server] Reading external source map:", sourceMapPath);
1380
+ if (!existsSync(sourceMapPath)) {
1381
+ console.log("[Claude Dev Server] Source map file not found:", sourceMapPath);
1382
+ return { file: relative(projectRoot, fullPath), line, column };
1383
+ }
1384
+ sourceMapContent = readFileSync(sourceMapPath, "utf-8");
1385
+ sourceMap = JSON.parse(sourceMapContent);
1386
+ }
1387
+ console.log("[Claude Dev Server] Source map loaded, sources:", sourceMap.sources?.length, "mappings:", sourceMap.mappings?.substring(0, 50) + "...");
1388
+ const consumer = await new SourceMapConsumer(sourceMap);
1389
+ const original = consumer.originalPositionFor({
1390
+ line,
1391
+ column
1392
+ });
1393
+ consumer.destroy();
1394
+ console.log("[Claude Dev Server] Source map result:", {
1395
+ generated: { line, column },
1396
+ original
1397
+ });
1398
+ if (original.source && original.line !== null) {
1399
+ let originalFile = original.source;
1400
+ if (originalFile.startsWith("webpack://")) {
1401
+ originalFile = originalFile.replace(/^webpack:\/\/[\/\\]?/, "");
1402
+ }
1403
+ if (!originalFile.startsWith("/")) {
1404
+ const possiblePath = join(projectRoot, originalFile);
1405
+ if (existsSync(possiblePath)) {
1406
+ originalFile = relative(projectRoot, possiblePath);
1407
+ }
1408
+ }
1409
+ console.log("[Claude Dev Server] Source map resolved:", {
1410
+ original: { file: originalFile, line: original.line, column: original.column },
1411
+ generated: { file: filePath, line, column }
1412
+ });
1413
+ return {
1414
+ file: originalFile,
1415
+ line: original.line,
1416
+ column: original.column || 1,
1417
+ original: {
1418
+ source: original.source,
1419
+ line: original.line,
1420
+ column: original.column,
1421
+ name: original.name
1422
+ }
1423
+ };
1424
+ }
1425
+ return { file: relative(projectRoot, fullPath), line, column };
1426
+ } catch (err) {
1427
+ console.error("[Claude Dev Server] Source map resolution error:", err);
1428
+ return { error: String(err) };
1429
+ }
1430
+ }
1431
+ async function handleTurbopackLookup(projectRoot, pagePath, targetPort) {
1432
+ try {
1433
+ console.log("[Claude Dev Server] Turbopack lookup for page:", pagePath);
1434
+ const pageRes = await fetch(`http://localhost:${targetPort}${pagePath}`);
1435
+ if (!pageRes.ok) {
1436
+ return { error: "Failed to fetch page" };
1437
+ }
1438
+ const html = await pageRes.text();
1439
+ const chunkUrls = [];
1440
+ const scriptMatches = html.matchAll(/<script[^>]*src="([^"]*\/_next\/static\/chunks\/[^"]*)"/g);
1441
+ for (const match of scriptMatches) {
1442
+ if (match[1]) {
1443
+ chunkUrls.push(match[1]);
1444
+ }
1445
+ }
1446
+ console.log("[Claude Dev Server] Found", chunkUrls.length, "chunk URLs");
1447
+ for (const chunkUrl of chunkUrls) {
1448
+ try {
1449
+ const fullUrl = chunkUrl.startsWith("http") ? chunkUrl : `http://localhost:${targetPort}${chunkUrl}`;
1450
+ const chunkRes = await fetch(fullUrl);
1451
+ if (!chunkRes.ok) continue;
1452
+ const chunkContent = await chunkRes.text();
1453
+ const modulePathRegex = /\[project\]([^\s"]+\.(tsx?|jsx?))/g;
1454
+ const matches = [...chunkContent.matchAll(modulePathRegex)];
1455
+ for (const match of matches) {
1456
+ if (match[1]) {
1457
+ const sourcePath = match[1];
1458
+ let relativePath = sourcePath.replace(/^\[project\]/, "");
1459
+ const normalizedPagePath = pagePath.replace(/^\/[^/]+/, "");
1460
+ if (relativePath.toLowerCase().includes(normalizedPagePath.toLowerCase()) || relativePath.toLowerCase().includes("login")) {
1461
+ console.log("[Claude Dev Server] Found source file:", relativePath);
1462
+ return {
1463
+ file: relativePath,
1464
+ line: void 0
1465
+ // Turbopack doesn't provide line numbers
1466
+ };
1467
+ }
1468
+ }
1469
+ }
1470
+ } catch (e) {
1471
+ console.log("[Claude Dev Server] Error fetching chunk:", chunkUrl, e);
1472
+ }
1473
+ }
1474
+ return { error: "Source file not found for page: " + pagePath };
1475
+ } catch (err) {
1476
+ console.error("[Claude Dev Server] Turbopack lookup error:", err);
1477
+ return { error: String(err) };
1478
+ }
1479
+ }
1078
1480
  function createProxyServer(targetPort, wsPort, projectRoot) {
1079
1481
  const moduleDir = dirname(fileURLToPath(import.meta.url));
1080
1482
  const assetsPath = join(moduleDir, "assets");
@@ -1089,12 +1491,45 @@ function createProxyServer(targetPort, wsPort, projectRoot) {
1089
1491
  console.error("[Claude Dev Server] Error:", e.message);
1090
1492
  throw new Error("ttyd assets not found. Please run `npm run build` first.");
1091
1493
  }
1092
- return http.createServer((req, res) => {
1494
+ const server = http.createServer((req, res) => {
1093
1495
  if (req.url === "/@claude-port") {
1094
1496
  res.setHeader("Content-Type", "application/json");
1095
1497
  res.end(JSON.stringify({ port: wsPort }));
1096
1498
  return;
1097
1499
  }
1500
+ if (req.url?.startsWith("/@sourcemap?")) {
1501
+ const url = new URL(req.url, `http://localhost:${wsPort}`);
1502
+ const file = url.searchParams.get("file");
1503
+ const line = url.searchParams.get("line");
1504
+ const column = url.searchParams.get("col");
1505
+ if (file && line && column) {
1506
+ handleSourceMapRequest(projectRoot, file, parseInt(line), parseInt(column || "1"), targetPort).then((result) => {
1507
+ res.setHeader("Content-Type", "application/json");
1508
+ res.end(JSON.stringify(result));
1509
+ }).catch((err) => {
1510
+ console.error("[Claude Dev Server] Source map error:", err);
1511
+ res.setHeader("Content-Type", "application/json");
1512
+ res.end(JSON.stringify({ error: err.message }));
1513
+ });
1514
+ return;
1515
+ }
1516
+ }
1517
+ if (req.url?.startsWith("/@sourcemap-lookup?")) {
1518
+ const url = new URL(req.url, `http://localhost:${wsPort}`);
1519
+ const page = url.searchParams.get("page");
1520
+ const framework = url.searchParams.get("framework");
1521
+ if (page && framework === "nextjs") {
1522
+ handleTurbopackLookup(projectRoot, page, targetPort).then((result) => {
1523
+ res.setHeader("Content-Type", "application/json");
1524
+ res.end(JSON.stringify(result));
1525
+ }).catch((err) => {
1526
+ console.error("[Claude Dev Server] Turbopack lookup error:", err);
1527
+ res.setHeader("Content-Type", "application/json");
1528
+ res.end(JSON.stringify({ error: err.message }));
1529
+ });
1530
+ return;
1531
+ }
1532
+ }
1098
1533
  if (req.url?.startsWith("/ttyd/")) {
1099
1534
  const urlPath = req.url.split("?")[0];
1100
1535
  if (urlPath === "/ttyd/index.html" || urlPath === "/ttyd/") {
@@ -1107,16 +1542,23 @@ function createProxyServer(targetPort, wsPort, projectRoot) {
1107
1542
  res.end(ttydBridgeJs);
1108
1543
  return;
1109
1544
  }
1545
+ if (urlPath === "/ttyd/token" || urlPath === "/ttyd/index.html/token") {
1546
+ res.setHeader("Content-Type", "application/json");
1547
+ res.end(JSON.stringify({ token: "" }));
1548
+ return;
1549
+ }
1110
1550
  res.statusCode = 404;
1111
1551
  res.end("Not found");
1112
1552
  return;
1113
1553
  }
1554
+ const proxyHeaders = { ...req.headers };
1555
+ delete proxyHeaders["accept-encoding"];
1114
1556
  const options = {
1115
1557
  hostname: "localhost",
1116
1558
  port: targetPort,
1117
1559
  path: req.url,
1118
1560
  method: req.method,
1119
- headers: req.headers
1561
+ headers: proxyHeaders
1120
1562
  };
1121
1563
  const proxyReq = http.request(options, (proxyRes) => {
1122
1564
  if (proxyRes.headers["content-type"]?.includes("text/html")) {
@@ -1145,6 +1587,60 @@ function createProxyServer(targetPort, wsPort, projectRoot) {
1145
1587
  });
1146
1588
  req.pipe(proxyReq);
1147
1589
  });
1590
+ server.on("upgrade", (req, socket, head) => {
1591
+ if (req.headers["upgrade"]?.toLowerCase() !== "websocket") {
1592
+ return;
1593
+ }
1594
+ console.log("[Claude Dev Server] WebSocket upgrade request:", req.url);
1595
+ const targetSocket = createConnection(targetPort, "localhost", () => {
1596
+ console.log("[Claude Dev Server] Connected to target WebSocket server");
1597
+ const upgradeRequest = [
1598
+ `${req.method} ${req.url} HTTP/1.1`,
1599
+ `Host: localhost:${targetPort}`,
1600
+ "Upgrade: websocket",
1601
+ "Connection: Upgrade",
1602
+ `Sec-WebSocket-Key: ${req.headers["sec-websocket-key"]}`,
1603
+ `Sec-WebSocket-Version: ${req.headers["sec-websocket-version"] || "13"}`
1604
+ ];
1605
+ if (req.headers["sec-websocket-protocol"]) {
1606
+ upgradeRequest.push(`Sec-WebSocket-Protocol: ${req.headers["sec-websocket-protocol"]}`);
1607
+ }
1608
+ if (req.headers["sec-websocket-extensions"]) {
1609
+ upgradeRequest.push(`Sec-WebSocket-Extensions: ${req.headers["sec-websocket-extensions"]}`);
1610
+ }
1611
+ targetSocket.write(upgradeRequest.join("\r\n") + "\r\n\r\n");
1612
+ if (head && head.length > 0) {
1613
+ targetSocket.write(head);
1614
+ }
1615
+ });
1616
+ targetSocket.on("data", (data) => {
1617
+ if (socket.writable) {
1618
+ socket.write(data);
1619
+ }
1620
+ });
1621
+ socket.on("data", (data) => {
1622
+ if (targetSocket.writable) {
1623
+ targetSocket.write(data);
1624
+ }
1625
+ });
1626
+ socket.on("close", () => {
1627
+ console.log("[Claude Dev Server] Client WebSocket closed");
1628
+ targetSocket.destroy();
1629
+ });
1630
+ targetSocket.on("close", () => {
1631
+ console.log("[Claude Dev Server] Target WebSocket closed");
1632
+ socket.end();
1633
+ });
1634
+ socket.on("error", (err) => {
1635
+ console.error("[Claude Dev Server] Client socket error:", err.message);
1636
+ targetSocket.destroy();
1637
+ });
1638
+ targetSocket.on("error", (err) => {
1639
+ console.error("[Claude Dev Server] Target socket error:", err.message);
1640
+ socket.end();
1641
+ });
1642
+ });
1643
+ return server;
1148
1644
  }
1149
1645
  function injectScripts(html, wsPort, projectRoot) {
1150
1646
  const projectRootScript = `<script>window.__CLAUDE_PROJECT_ROOT__ = ${JSON.stringify(projectRoot)}</script>`;
@@ -1162,5 +1658,5 @@ ${projectRootScript}
1162
1658
  }
1163
1659
 
1164
1660
  export { startUniversalServer };
1165
- //# sourceMappingURL=chunk-DOLSA776.js.map
1166
- //# sourceMappingURL=chunk-DOLSA776.js.map
1661
+ //# sourceMappingURL=chunk-3GUIIEFR.js.map
1662
+ //# sourceMappingURL=chunk-3GUIIEFR.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/server/pty.ts","../src/server/source-map.ts","../src/server/websocket.ts","../src/client/injection.js","../src/universal/index.ts"],"names":["resolve","__filename","spawn","SourceMapConsumer"],"mappings":"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AAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,CAAA;AAoOtB,IAAM,aAAA,GAAgB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;;AAAA;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","file":"chunk-3GUIIEFR.js","sourcesContent":["import { spawn } from 'child_process'\nimport { randomBytes } from 'crypto'\n\nexport interface PtyOptions {\n cwd: string\n claudePath: string\n args: string[]\n env?: Record<string, string>\n}\n\nexport function spawnClaudeCode(options: PtyOptions) {\n // Generate a random port for ttyd\n const port = 50000 + Math.floor(Math.random() * 10000)\n\n // Start ttyd with claude command\n const ttydProc = spawn('ttyd', [\n '--port', String(port),\n '--interface', '127.0.0.1',\n '--writable',\n options.claudePath,\n ...options.args,\n ], {\n cwd: options.cwd,\n env: {\n ...process.env,\n ...options.env,\n TERM: 'xterm-256color',\n FORCE_COLOR: '1',\n },\n })\n\n ttydProc.on('exit', (code) => {\n console.log(`[claude-dev-server] ttyd exited - code: ${code}`)\n })\n\n ttydProc.on('error', (err) => {\n console.error(`[claude-dev-server] ttyd error: ${err.message}`)\n })\n\n // Wait a bit for ttyd to start, then return the WebSocket URL\n return new Promise<{ wsUrl: string; process: typeof ttydProc; port: number }>((resolve, reject) => {\n ttydProc.stdout?.on('data', (data: Buffer) => {\n const msg = data.toString()\n console.log(`[ttyd] ${msg}`)\n })\n\n ttydProc.stderr?.on('data', (data: Buffer) => {\n const msg = data.toString()\n console.error(`[ttyd stderr] ${msg}`)\n })\n\n // Give ttyd a moment to start\n setTimeout(() => {\n resolve({\n wsUrl: `ws://127.0.0.1:${port}`,\n process: ttydProc,\n port,\n })\n }, 500)\n\n ttydProc.on('error', reject)\n })\n}\n","import { SourceMapConsumer } from 'source-map'\n\nexport interface SourcePosition {\n source: string\n line: number\n column: number\n name?: string\n}\n\nexport interface CodeLocation {\n file: string\n line: number\n column: number\n content?: string\n}\n\n// Store source maps by generated file path\nconst sourceMapCache = new Map<string, SourceMapConsumer>()\n\nexport async function parseSourceMap(sourceMapUrl: string, content: string): Promise<void> {\n try {\n const consumer = await new SourceMapConsumer(content)\n sourceMapCache.set(sourceMapUrl, consumer)\n } catch (err) {\n console.error('Failed to parse source map:', err)\n }\n}\n\nexport async function findOriginalPosition(\n generatedFile: string,\n line: number,\n column: number\n): Promise<SourcePosition | null> {\n const consumer = sourceMapCache.get(generatedFile)\n if (!consumer) {\n return null\n }\n\n try {\n const position = consumer.originalPositionFor({ line, column })\n if (position.source) {\n return {\n source: position.source,\n line: position.line || 1,\n column: position.column || 0,\n name: position.name,\n }\n }\n } catch (err) {\n console.error('Failed to find original position:', err)\n }\n\n return null\n}\n\nexport function generateVSCodeURI(location: CodeLocation): string {\n // Generate VS Code URI scheme for opening files\n // vscode://file/{path}:{line}:{col}\n const filePath = location.file.replace(/\\\\/g, '/')\n return `vscode://file/${filePath}:${location.line}:${location.column}`\n}\n\nexport function formatCodeContext(location: CodeLocation): string {\n return `\n📍 Code Location:\n File: ${location.file}\n Line: ${location.line}\n Column: ${location.column}\n`\n}\n\nexport function clearCache() {\n sourceMapCache.clear()\n}\n","import { WebSocketServer, WebSocket } from 'ws'\nimport { spawnClaudeCode } from './pty.js'\nimport { findOriginalPosition, parseSourceMap, CodeLocation, formatCodeContext } from './source-map.js'\nimport { readFile } from 'fs/promises'\nimport { resolve } from 'path'\n\nexport interface WebSocketServerOptions {\n port?: number\n projectRoot: string\n claudePath: string\n claudeArgs: string[]\n}\n\nexport function createWebSocketServer(options: WebSocketServerOptions) {\n let wss: WebSocketServer | null = null\n let port = options.port || 0\n let ttydInfo: { wsUrl: string; process: any; port: number } | null = null\n\n const start = async (): Promise<{ wsPort: number; ttydPort: number }> => {\n // Start ttyd first\n console.log('[claude-dev-server] Starting ttyd...')\n ttydInfo = await spawnClaudeCode({\n cwd: options.projectRoot,\n claudePath: options.claudePath,\n args: options.claudeArgs,\n })\n console.log(`[claude-dev-server] ttyd running at ${ttydInfo.wsUrl}`)\n\n // Also start a simple WebSocket server for inspect functionality\n return new Promise((resolve, reject) => {\n wss = new WebSocketServer({ port, host: '127.0.0.1' })\n\n wss.on('listening', () => {\n const address = wss!.address()\n if (address && typeof address === 'object') {\n port = address.port\n resolve({ wsPort: port, ttydPort: ttydInfo!.port })\n }\n })\n\n wss.on('error', (err) => {\n reject(err)\n })\n\n wss.on('connection', (ws: WebSocket) => {\n ws.on('message', async (message: Buffer) => {\n try {\n const msg = JSON.parse(message.toString())\n\n if (msg.type === 'inspect') {\n await handleInspect(msg, ws, options.projectRoot)\n } else if (msg.type === 'loadSourceMap') {\n await handleLoadSourceMap(msg, ws, options.projectRoot)\n }\n } catch (err) {\n // Ignore\n }\n })\n\n // Send ready message with ttyd URL\n ws.send(JSON.stringify({\n type: 'ready',\n ttydUrl: ttydInfo!.wsUrl\n }))\n })\n })\n }\n\n const stop = () => {\n ttydInfo?.process.kill()\n wss?.close()\n }\n\n return { start, stop }\n}\n\nasync function handleLoadSourceMap(\n msg: any,\n ws: WebSocket,\n projectRoot: string\n) {\n const { sourceMapUrl } = msg\n try {\n const mapPath = resolve(projectRoot, sourceMapUrl.replace(/^\\//, ''))\n const content = await readFile(mapPath, 'utf-8')\n await parseSourceMap(sourceMapUrl, content)\n ws.send(JSON.stringify({\n type: 'sourceMapLoaded',\n sourceMapUrl,\n success: true\n }))\n } catch (err) {\n ws.send(JSON.stringify({\n type: 'sourceMapLoaded',\n sourceMapUrl,\n success: false,\n error: (err as Error).message\n }))\n }\n}\n\nasync function handleInspect(\n msg: any,\n ws: WebSocket,\n projectRoot: string\n) {\n const { url, line, column, sourceMapUrl } = msg\n\n let location: CodeLocation | null = null\n\n if (sourceMapUrl) {\n const original = await findOriginalPosition(sourceMapUrl, line, column)\n if (original) {\n location = {\n file: resolve(projectRoot, original.source),\n line: original.line,\n column: original.column,\n }\n }\n }\n\n if (!location) {\n const match = url.match(/\\/@fs\\/(.+?)(?:\\?|$)|\\/@vite\\/(.+?)(?:\\?|$)/)\n if (match) {\n location = {\n file: decodeURIComponent(match[1] || match[2]),\n line,\n column,\n }\n }\n }\n\n ws.send(JSON.stringify({\n type: 'inspectResult',\n location: location ? {\n file: location.file,\n line: location.line,\n column: location.column,\n context: formatCodeContext(location)\n } : null\n }))\n}\n","// Client-side script - pure JavaScript (no TypeScript)\n// This file will be injected into the browser\n\nexport const CLIENT_STYLES = `\n<style id=\"claude-dev-server-styles\">\n.claude-dev-server-toggle {\n position: fixed;\n top: 20px;\n right: 20px;\n width: 44px;\n height: 44px;\n background: #1e1e1e;\n border: none;\n border-radius: 50%;\n box-shadow: 0 2px 12px rgba(0,0,0,0.3);\n cursor: pointer;\n z-index: 999999;\n display: flex;\n align-items: center;\n justify-content: center;\n font-size: 20px;\n transition: transform 0.2s, background 0.2s;\n}\n.claude-dev-server-toggle:hover {\n transform: scale(1.1);\n background: #2d2d2d;\n}\n.claude-dev-server-toggle.hidden {\n display: none;\n}\n.claude-dev-server-inspect-btn {\n position: fixed;\n top: 74px;\n right: 20px;\n width: 44px;\n height: 44px;\n background: #1e1e1e;\n border: none;\n border-radius: 50%;\n box-shadow: 0 2px 12px rgba(0,0,0,0.3);\n cursor: pointer;\n z-index: 999999;\n display: flex;\n align-items: center;\n justify-content: center;\n font-size: 16px;\n transition: transform 0.2s, background 0.2s;\n}\n.claude-dev-server-inspect-btn:hover {\n transform: scale(1.1);\n background: #2d2d2d;\n}\n.claude-dev-server-inspect-btn.active {\n background: #007acc;\n}\n.claude-dev-server-inspect-btn.hidden {\n display: none;\n}\n.claude-dev-server-toggle svg,\n.claude-dev-server-inspect-btn svg {\n width: 20px;\n height: 20px;\n flex-shrink: 0;\n}\n.claude-dev-server-panel {\n position: fixed;\n top: 0;\n right: 0;\n width: 650px;\n height: 100vh;\n background: #1e1e1e;\n color: #d4d4d4;\n font-family: 'SF Mono', 'Monaco', 'Cascadia Code', 'Consolas', monospace;\n font-size: 13px;\n box-shadow: -4px 0 20px rgba(0,0,0,0.3);\n transform: translateX(100%);\n transition: transform 0.2s ease;\n z-index: 999999;\n display: flex;\n flex-direction: column;\n}\n.claude-dev-server-panel.open {\n transform: translateX(0);\n}\n.claude-dev-server-header {\n padding: 8px 12px;\n background: #2d2d2d;\n border-bottom: 1px solid #3e3e3e;\n display: flex;\n justify-content: space-between;\n align-items: center;\n user-select: none;\n}\n.claude-dev-server-title {\n font-weight: 600;\n color: #fff;\n display: flex;\n align-items: center;\n gap: 8px;\n font-size: 13px;\n}\n.claude-dev-server-title::before {\n content: '🤖';\n font-size: 14px;\n}\n.claude-dev-server-actions {\n display: flex;\n gap: 6px;\n}\n.claude-dev-server-btn {\n background: #3e3e3e;\n border: none;\n color: #d4d4d4;\n cursor: pointer;\n font-family: inherit;\n font-size: 11px;\n padding: 4px 10px;\n border-radius: 3px;\n transition: background 0.15s;\n display: flex;\n align-items: center;\n gap: 4px;\n}\n.claude-dev-server-btn:hover {\n background: #4e4e4e;\n}\n.claude-dev-server-btn.active {\n background: #007acc;\n color: #fff;\n}\n.claude-dev-server-close {\n background: none;\n border: none;\n color: #858585;\n cursor: pointer;\n font-size: 18px;\n padding: 0;\n width: 20px;\n height: 20px;\n display: flex;\n align-items: center;\n justify-content: center;\n border-radius: 3px;\n transition: background 0.15s, color 0.15s;\n}\n.claude-dev-server-close:hover {\n background: #3e3e3e;\n color: #fff;\n}\n.claude-dev-server-terminal {\n flex: 1;\n overflow: hidden;\n position: relative;\n background: #000;\n}\n.claude-dev-server-terminal iframe {\n width: 100%;\n height: 100%;\n border: none;\n}\n.claude-dev-server-inspect-overlay {\n position: fixed;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n pointer-events: none;\n z-index: 999998;\n display: none;\n}\n.claude-dev-server-inspect-overlay.active {\n display: block;\n}\n.claude-dev-server-highlight {\n position: absolute;\n border: 2px solid #007acc;\n background: rgba(0, 122, 204, 0.1);\n pointer-events: none;\n transition: all 0.1s ease;\n}\n.claude-dev-server-highlight::after {\n content: attr(data-element);\n position: absolute;\n top: -20px;\n left: 0;\n background: #007acc;\n color: #fff;\n font-size: 10px;\n padding: 2px 4px;\n border-radius: 2px 2px 0 0;\n font-family: monospace;\n white-space: nowrap;\n}\n/* Responsive layout for portrait mode */\n@media (orientation: portrait) {\n .claude-dev-server-toggle {\n top: auto;\n bottom: 20px;\n right: 10px;\n width: 36px;\n height: 36px;\n }\n .claude-dev-server-inspect-btn {\n top: auto;\n bottom: 66px;\n right: 10px;\n width: 36px;\n height: 36px;\n }\n .claude-dev-server-panel {\n top: auto;\n bottom: 0;\n right: 0;\n left: 0;\n width: 100%;\n height: 80vh;\n transform: translateY(100%);\n box-shadow: 0 -4px 20px rgba(0,0,0,0.3);\n }\n .claude-dev-server-panel.open {\n transform: translateY(0);\n }\n .claude-dev-server-toggle svg,\n .claude-dev-server-inspect-btn svg {\n width: 16px;\n height: 16px;\n }\n}\n</style>\n`\n\nexport const CLIENT_SCRIPT = `\n(() => {\n let ws = null\n let panel = null\n let toggleBtn = null\n let inspectBtn = null // 悬浮 inspect 按钮\n let terminalContainer = null\n let terminalIframe = null\n let overlay = null\n let isOpen = false\n let isInspectMode = false\n let ttydWsUrl = null\n\n // Fetch the WebSocket port from the server\n async function getWsPort() {\n const res = await fetch('/@claude-port')\n const data = await res.json()\n return data.port\n }\n\n async function initWhenReady() {\n try {\n const port = await getWsPort()\n connect(port)\n } catch (err) {\n console.error('[Claude Dev Server] Failed to get port:', err)\n // Retry after 1 second\n setTimeout(initWhenReady, 1000)\n return\n }\n createToggleBtn()\n createInspectBtn()\n createOverlay()\n createPanel()\n }\n\n function createToggleBtn() {\n if (toggleBtn) return\n toggleBtn = document.createElement('button')\n toggleBtn.className = 'claude-dev-server-toggle'\n toggleBtn.innerHTML = '<svg width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" fill=\"none\"><path d=\"M11.376 24L10.776 23.544L10.44 22.8L10.776 21.312L11.16 19.392L11.472 17.856L11.76 15.96L11.928 15.336L11.904 15.288L11.784 15.312L10.344 17.28L8.16 20.232L6.432 22.056L6.024 22.224L5.304 21.864L5.376 21.192L5.784 20.616L8.16 17.568L9.6 15.672L10.536 14.592L10.512 14.448H10.464L4.128 18.576L3 18.72L2.496 18.264L2.568 17.52L2.808 17.28L4.704 15.96L9.432 13.32L9.504 13.08L9.432 12.96H9.192L8.4 12.912L5.712 12.84L3.384 12.744L1.104 12.624L0.528 12.504L0 11.784L0.048 11.424L0.528 11.112L1.224 11.16L2.736 11.28L5.016 11.424L6.672 11.52L9.12 11.784H9.504L9.552 11.616L9.432 11.52L9.336 11.424L6.96 9.84L4.416 8.16L3.072 7.176L2.352 6.672L1.992 6.216L1.848 5.208L2.496 4.488L3.384 4.56L3.6 4.608L4.488 5.304L6.384 6.768L8.88 8.616L9.24 8.904L9.408 8.808V8.736L9.24 8.472L7.896 6.024L6.456 3.528L5.808 2.496L5.64 1.872C5.576 1.656 5.544 1.416 5.544 1.152L6.288 0.144001L6.696 0L7.704 0.144001L8.112 0.504001L8.736 1.92L9.72 4.152L11.28 7.176L11.736 8.088L11.976 8.904L12.072 9.168H12.24V9.024L12.36 7.296L12.6 5.208L12.84 2.52L12.912 1.752L13.296 0.840001L14.04 0.360001L14.616 0.624001L15.096 1.32L15.024 1.752L14.76 3.6L14.184 6.504L13.824 8.472H14.04L14.28 8.208L15.264 6.912L16.92 4.848L17.64 4.032L18.504 3.12L19.056 2.688H20.088L20.832 3.816L20.496 4.992L19.44 6.336L18.552 7.464L17.28 9.168L16.512 10.536L16.584 10.632H16.752L19.608 10.008L21.168 9.744L22.992 9.432L23.832 9.816L23.928 10.2L23.592 11.016L21.624 11.496L19.32 11.952L15.888 12.768L15.84 12.792L15.888 12.864L17.424 13.008L18.096 13.056H19.728L22.752 13.272L23.544 13.8L24 14.424L23.928 14.928L22.704 15.528L21.072 15.144L17.232 14.232L15.936 13.92H15.744V14.016L16.848 15.096L18.84 16.896L21.36 19.224L21.48 19.8L21.168 20.28L20.832 20.232L18.624 18.552L17.76 17.808L15.84 16.2H15.72V16.368L16.152 17.016L18.504 20.544L18.624 21.624L18.456 21.96L17.832 22.176L17.184 22.056L15.792 20.136L14.376 17.952L13.224 16.008L13.104 16.104L12.408 23.352L12.096 23.712L11.376 24Z\" fill=\"#d97757\"/></svg>'\n toggleBtn.title = 'Open Claude Code (Cmd/Ctrl + \\`)'\n toggleBtn.setAttribute('data-dev-tool', 'true')\n toggleBtn.addEventListener('click', () => togglePanel(true))\n document.body.appendChild(toggleBtn)\n }\n\n function createInspectBtn() {\n if (inspectBtn) return\n inspectBtn = document.createElement('button')\n inspectBtn.className = 'claude-dev-server-inspect-btn'\n inspectBtn.innerHTML = '<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"20\" height=\"20\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"#fff\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M19 11V4a2 2 0 00-2-2H4a2 2 0 00-2 2v13a2 2 0 002 2h7\"/><path d=\"M12 12l4.166 10 1.48-4.355L22 16.166 12 12z\"/><path d=\"M18 18l3 3\"/></svg>'\n inspectBtn.title = 'Inspect Element (Cmd/Ctrl + Shift + I)'\n inspectBtn.setAttribute('data-dev-tool', 'true')\n inspectBtn.addEventListener('click', () => {\n if (isInspectMode) {\n disableInspectMode()\n } else {\n // 点击 Inspect 时先收起面板\n if (isOpen) {\n togglePanel(false)\n }\n enableInspectMode()\n }\n })\n document.body.appendChild(inspectBtn)\n }\n\n function createOverlay() {\n if (overlay) return\n overlay = document.createElement('div')\n overlay.className = 'claude-dev-server-inspect-overlay'\n document.body.appendChild(overlay)\n }\n\n function highlightElement(el) {\n if (!overlay) return\n overlay.innerHTML = ''\n const rect = el.getBoundingClientRect()\n const highlight = document.createElement('div')\n highlight.className = 'claude-dev-server-highlight'\n highlight.style.top = rect.top + 'px'\n highlight.style.left = rect.left + 'px'\n highlight.style.width = rect.width + 'px'\n highlight.style.height = rect.height + 'px'\n const className = el.className ? String(el.className) : ''\n highlight.dataset.element = el.tagName.toLowerCase() +\n (el.id ? '#' + el.id : '') +\n (className ? '.' + className.split(' ').join('.') : '')\n overlay.appendChild(highlight)\n }\n\n async function getSourceLocation(el) {\n // Early check: detect Next.js by checking for _next/static chunks\n const hasNextJsChunks = Array.from(document.querySelectorAll('script[src]'))\n .some(script => script.getAttribute('src')?.includes('/_next/static/chunks/'))\n\n if (hasNextJsChunks) {\n console.log('[Claude Dev Server] Next.js Turbopack detected, using specialized lookup')\n\n const pagePath = window.location.pathname\n console.log('[Claude Dev Server] Looking up source for page:', pagePath)\n\n try {\n const params = new URLSearchParams({\n page: pagePath,\n framework: 'nextjs'\n })\n const lookupUrl = '/@sourcemap-lookup?' + params.toString()\n console.log('[Claude Dev Server] Fetching:', lookupUrl)\n\n const response = await fetch(lookupUrl)\n console.log('[Claude Dev Server] Response status:', response.status)\n\n if (response.ok) {\n const result = await response.json()\n console.log('[Claude Dev Server] Lookup result:', result)\n if (result.file) {\n const elClassName = el.className ? String(el.className) : ''\n const selector = el.tagName.toLowerCase() +\n (el.id ? '#' + el.id : '') +\n (elClassName ? '.' + elClassName.split(' ').filter(c => c).join('.') : '')\n\n return {\n url: result.file,\n line: result.line || undefined,\n column: result.column || undefined,\n selector: selector,\n hint: 'File: ' + result.file\n }\n }\n }\n } catch (e) {\n console.log('[Claude Dev Server] Next.js lookup failed:', e)\n }\n // If Next.js lookup failed, continue with normal flow\n }\n\n let sourceFile = null\n let sourceLine = 1\n let sourceColumn = 1\n\n // Try to get original line number using source map\n // Use server-side source map resolution with source-map library\n async function resolveSourceMap(filePath, line, column) {\n try {\n console.log('[Claude Dev Server] Calling server source map resolution for:', filePath, line, column)\n\n // Convert absolute file path to URL path for Vite\n let urlPath = filePath\n if (filePath.startsWith('/Users/')) {\n // Convert /Users/.../src/App.jsx to /src/App.jsx\n const parts = filePath.split('/')\n const srcIndex = parts.indexOf('src')\n if (srcIndex >= 0) {\n urlPath = '/' + parts.slice(srcIndex).join('/')\n }\n }\n\n console.log('[Claude Dev Server] Converted path to URL path:', urlPath)\n\n // Use the server-side source map resolution with URL path\n const params = new URLSearchParams({\n file: urlPath,\n line: String(line),\n col: String(column)\n })\n\n const response = await fetch('/@sourcemap?' + params.toString())\n if (!response.ok) {\n console.log('[Claude Dev Server] Source map request failed:', response.status)\n return null\n }\n\n const result = await response.json()\n console.log('[Claude Dev Server] Source map result:', result)\n\n if (result.error) {\n console.log('[Claude Dev Server] Source map error:', result.error)\n return null\n }\n\n if (result.file) {\n return {\n file: result.file,\n line: result.line,\n column: result.column\n }\n }\n\n return null\n } catch (e) {\n console.log('[Claude Dev Server] Source map lookup failed:', e.message)\n return null\n }\n }\n\n // Try to extract jsxDEV source location from React Fiber\n function extractJsxDevLocation(fiber) {\n try {\n // The jsxDEV location is stored in the element type's _debugInfo or _source\n // React 18 stores it differently - let's check multiple locations\n const elementType = fiber.elementType || fiber.type\n\n // Check if this is a jsxDEV call by looking at the string representation\n if (elementType && typeof elementType === 'function') {\n const fnStr = elementType.toString()\n // jsxDEV functions have a specific pattern\n if (fnStr.includes('jsxDEV') || fnStr.includes('jsx')) {\n console.log('[Claude Dev Server] Found jsxDEV element type')\n }\n }\n\n // Try to get _debugSource which might have jsxDEV metadata\n const debugSource = fiber._debugSource || fiber._debugInfo\n if (debugSource) {\n // For jsxDEV, check if there's a _source object with fileName/lineNumber\n const source = debugSource._source || debugSource.source || debugSource\n if (source && source.fileName && source.lineNumber !== undefined) {\n console.log('[Claude Dev Server] Found _debugSource with source location:', source)\n // NOTE: Don't use this directly! The lineNumber might be from transpiled code\n // We'll let the source map resolver handle it\n return null\n }\n }\n\n // Check the memoizedState or other properties for source location\n if (fiber.memoizedState) {\n // React might store jsxDEV location in memoizedState\n console.log('[Claude Dev Server] Checking memoizedState:', fiber.memoizedState)\n }\n\n // Check the memoizedProps or other properties for source location\n if (fiber.memoizedProps && fiber.memoizedProps.__source) {\n const source = fiber.memoizedProps.__source\n console.log('[Claude Dev Server] Found __source in memoizedProps:', source)\n return {\n file: source.fileName,\n line: source.lineNumber,\n column: source.columnNumber || 1\n }\n }\n\n // Check pendingProps for __source\n if (fiber.pendingProps && fiber.pendingProps.__source) {\n const source = fiber.pendingProps.__source\n console.log('[Claude Dev Server] Found __source in pendingProps:', source)\n return {\n file: source.fileName,\n line: source.lineNumber,\n column: source.columnNumber || 1\n }\n }\n } catch (e) {\n console.log('[Claude Dev Server] Error extracting jsxDEV location:', e)\n }\n return null\n }\n\n // Try React DevTools - handle React 18's randomized suffix\n const elKeys = Object.keys(el)\n let fiberKey = elKeys.find(k => k === '__reactFiber__' || k === '__reactInternalInstance' || k.indexOf('__reactFiber') === 0)\n let propsKey = elKeys.find(k => k === '__reactProps__' || k.indexOf('__reactProps') === 0)\n\n if (fiberKey) {\n const fiber = el[fiberKey]\n console.log('[Claude Dev Server] Found fiber at key:', fiberKey)\n\n // Log fiber structure for debugging\n console.log('[Claude Dev Server] Fiber structure:', {\n _debugSource: fiber._debugSource,\n elementType: fiber.elementType,\n type: fiber.type,\n memoizedProps: fiber.memoizedProps,\n pendingProps: fiber.pendingProps\n })\n\n // Log _debugSource details\n if (fiber._debugSource) {\n console.log('[Claude Dev Server] _debugSource details:', JSON.stringify(fiber._debugSource, null, 2))\n }\n\n // For Next.js, try to get component name from elementType\n if (!fiber._debugSource && fiber.elementType) {\n const elementType = fiber.elementType\n console.log('[Claude Dev Server] elementType:', elementType)\n console.log('[Claude Dev Server] elementType.name:', elementType.name)\n console.log('[Claude Dev Server] elementType.displayName:', elementType.displayName)\n\n // Try to get the function string to find component name\n if (typeof elementType === 'function') {\n const fnStr = elementType.toString()\n console.log('[Claude Dev Server] elementType function:', fnStr.substring(0, 200))\n }\n }\n\n // First, try to extract jsxDEV source location\n const jsxDevLocation = extractJsxDevLocation(fiber)\n if (jsxDevLocation) {\n sourceFile = jsxDevLocation.file\n sourceLine = jsxDevLocation.line\n sourceColumn = jsxDevLocation.column\n\n // Convert absolute path to relative path\n if (sourceFile.startsWith('/')) {\n const projectRoot = window.__CLAUDE_PROJECT_ROOT__\n if (projectRoot && sourceFile.startsWith(projectRoot)) {\n sourceFile = sourceFile.substring(projectRoot.length + 1)\n if (sourceFile.startsWith('/')) {\n sourceFile = sourceFile.substring(1)\n }\n }\n }\n console.log('[Claude Dev Server] Using jsxDEV location:', sourceFile, sourceLine)\n } else {\n // Fall back to _debugSource\n const debugSource = fiber._debugSource || fiber.elementType?._debugSource || fiber.type?._debugSource || fiber.alternate?._debugSource\n if (debugSource && debugSource.fileName) {\n sourceFile = debugSource.fileName\n sourceLine = debugSource.lineNumber || 1\n sourceColumn = debugSource.columnNumber || 1\n\n // Convert relative path to absolute path for source map resolution\n let absolutePath = sourceFile\n if (!sourceFile.startsWith('/')) {\n // Relative path - resolve relative to project root\n const projectRoot = window.__CLAUDE_PROJECT_ROOT__\n absolutePath = projectRoot + '/' + sourceFile\n }\n\n console.log('[Claude Dev Server] Resolving source map for:', absolutePath, 'at line:', sourceLine)\n\n // Use server-side source map resolution\n const original = await resolveSourceMap(absolutePath, sourceLine, sourceColumn)\n if (original) {\n sourceFile = original.file\n sourceLine = original.line\n sourceColumn = original.column\n console.log('[Claude Dev Server] Original location from source map:', sourceFile, sourceLine)\n } else {\n // Source map resolution failed, use the original file\n // Convert absolute path back to relative\n if (absolutePath.startsWith('/')) {\n const projectRoot = window.__CLAUDE_PROJECT_ROOT__\n if (projectRoot && absolutePath.startsWith(projectRoot)) {\n sourceFile = absolutePath.substring(projectRoot.length + 1)\n if (sourceFile.startsWith('/')) {\n sourceFile = sourceFile.substring(1)\n }\n }\n }\n }\n console.log('[Claude Dev Server] Final React source:', sourceFile, sourceLine)\n } else {\n // Try going up the fiber tree\n let currentFiber = fiber\n let depth = 0\n while (currentFiber && depth < 20) {\n const jsxDevLoc = extractJsxDevLocation(currentFiber)\n if (jsxDevLoc) {\n sourceFile = jsxDevLoc.file\n sourceLine = jsxDevLoc.line\n sourceColumn = jsxDevLoc.column\n\n if (sourceFile.startsWith('/')) {\n const projectRoot = window.__CLAUDE_PROJECT_ROOT__\n if (projectRoot && sourceFile.startsWith(projectRoot)) {\n sourceFile = sourceFile.substring(projectRoot.length + 1)\n if (sourceFile.startsWith('/')) {\n sourceFile = sourceFile.substring(1)\n }\n }\n }\n console.log('[Claude Dev Server] Found jsxDEV location at depth', depth, ':', sourceFile, sourceLine)\n break\n }\n\n const ds = currentFiber._debugSource || currentFiber.elementType?._debugSource || currentFiber.type?._debugSource\n if (ds && ds.fileName) {\n sourceFile = ds.fileName\n sourceLine = ds.lineNumber || 1\n sourceColumn = ds.columnNumber || 1\n\n // Use server-side source map resolution\n const original = await resolveSourceMap(sourceFile, sourceLine, sourceColumn)\n if (original) {\n sourceFile = original.file\n sourceLine = original.line\n sourceColumn = original.column\n } else {\n // Convert absolute path to relative path using project root\n if (sourceFile.startsWith('/')) {\n const projectRoot = window.__CLAUDE_PROJECT_ROOT__\n if (projectRoot && sourceFile.startsWith(projectRoot)) {\n sourceFile = sourceFile.substring(projectRoot.length + 1)\n if (sourceFile.startsWith('/')) {\n sourceFile = sourceFile.substring(1)\n }\n }\n }\n }\n console.log('[Claude Dev Server] Found React source at depth', depth, ':', sourceFile, sourceLine)\n break\n }\n currentFiber = currentFiber.return || currentFiber.alternate\n depth++\n }\n }\n }\n }\n\n // Try Vue component\n if (!sourceFile) {\n const vueComponent = el.__vueParentComponent || el.__vnode\n if (vueComponent) {\n const type = vueComponent.type || vueComponent.component\n if (type && type.__file) {\n sourceFile = type.__file\n console.log('[Claude Dev Server] Found Vue source:', sourceFile)\n }\n }\n }\n\n // Try Vite's HMR source map\n if (!sourceFile) {\n // Look for data-vite-dev-id or similar attributes\n const viteId = el.getAttribute('data-vite-dev-id')\n if (viteId) {\n // Extract file path from viteId (remove query params if any)\n const queryIndex = viteId.indexOf('?')\n sourceFile = '/' + (queryIndex >= 0 ? viteId.substring(0, queryIndex) : viteId)\n console.log('[Claude Dev Server] Found Vite ID:', viteId)\n }\n }\n\n // Check for inline script\n if (!sourceFile) {\n const inlineScripts = document.querySelectorAll('script:not([src])')\n if (inlineScripts.length > 0) {\n const pathParts = window.location.pathname.split('/')\n const fileName = pathParts[pathParts.length - 1] || 'index.html'\n sourceFile = '/' + fileName\n\n const html = document.documentElement.outerHTML\n const elId = el.id ? el.id : ''\n const elClass = el.className ? el.className : ''\n let elPattern\n if (elId) {\n elPattern = 'id=\"' + elId + '\"'\n } else if (elClass) {\n elPattern = 'class=\"' + elClass.split(' ')[0] + '\"'\n } else {\n elPattern = el.tagName.toLowerCase()\n }\n\n const matchIndex = html.indexOf(elPattern)\n if (matchIndex !== -1) {\n const beforeElement = html.substring(0, matchIndex)\n sourceLine = beforeElement.split('\\\\n').length\n }\n }\n }\n\n // Find main script\n if (!sourceFile) {\n const scripts = document.querySelectorAll('script[src]')\n for (const script of scripts) {\n const src = script.getAttribute('src')\n if (src && !src.includes('cdn') && !src.includes('node_modules') &&\n (src.includes('/src/') || src.includes('/app.') || src.includes('/main.'))) {\n sourceFile = src\n break\n }\n }\n }\n\n // Fallback\n if (!sourceFile) {\n const pathParts = window.location.pathname.split('/')\n const fileName = pathParts[pathParts.length - 1] || 'index.html'\n sourceFile = '/' + fileName\n }\n\n const elClassName = el.className ? String(el.className) : ''\n const selector = el.tagName.toLowerCase() +\n (el.id ? '#' + el.id : '') +\n (elClassName ? '.' + elClassName.split(' ').filter(c => c).join('.') : '')\n\n return {\n url: sourceFile,\n line: sourceLine,\n column: sourceColumn,\n selector: selector,\n hint: sourceFile ? 'File: ' + sourceFile : 'Element: ' + selector\n }\n }\n\n function enableInspectMode() {\n isInspectMode = true\n overlay && overlay.classList.add('active')\n document.body.style.cursor = 'crosshair'\n\n // 更新悬浮 inspect 按钮状态\n if (inspectBtn) inspectBtn.classList.add('active')\n\n // 检查元素是否属于 dev tools\n function isDevToolElement(el) {\n if (!el) return false\n // 检查元素本身\n if (el.dataset && el.dataset.devTool === 'true') return true\n // 检查父元素\n let parent = el.parentElement\n while (parent) {\n if (parent.dataset && parent.dataset.devTool === 'true') return true\n parent = parent.parentElement\n }\n return false\n }\n\n const onMouseMove = async (e) => {\n if (!isInspectMode) return\n const el = document.elementFromPoint(e.clientX, e.clientY)\n // 忽略 dev tools 元素和 overlay\n if (el && el !== overlay && !isDevToolElement(el) && (!overlay || !overlay.contains(el))) {\n highlightElement(el)\n }\n }\n\n const onClick = async (e) => {\n if (!isInspectMode) return\n e.preventDefault()\n e.stopPropagation()\n\n const el = document.elementFromPoint(e.clientX, e.clientY)\n // 忽略 dev tools 元素和 overlay\n if (el && el !== overlay && !isDevToolElement(el) && (!overlay || !overlay.contains(el))) {\n const location = await getSourceLocation(el)\n if (location) {\n await inspectElement(location, el)\n }\n disableInspectMode()\n }\n }\n\n document.addEventListener('mousemove', onMouseMove, true)\n document.addEventListener('click', onClick, true)\n\n if (overlay) overlay._inspectHandlers = { onMouseMove, onClick }\n }\n\n function disableInspectMode() {\n isInspectMode = false\n if (overlay) overlay.classList.remove('active')\n document.body.style.cursor = ''\n if (overlay) overlay.innerHTML = ''\n\n // 更新悬浮 inspect 按钮状态\n if (inspectBtn) inspectBtn.classList.remove('active')\n\n const btn = panel && panel.querySelector('.claude-dev-server-btn-inspect')\n if (btn) btn.classList.remove('active')\n\n const handlers = overlay && overlay._inspectHandlers\n if (handlers) {\n document.removeEventListener('mousemove', handlers.onMouseMove, true)\n document.removeEventListener('click', handlers.onClick, true)\n }\n }\n\n async function inspectElement(location, el) {\n // 构建格式化的 prompt\n const filePath = location.url || location.file || 'unknown'\n const tagName = el.tagName ? el.tagName.toLowerCase() : ''\n const id = el.id ? '#' + el.id : ''\n\n // 对于 Tailwind CSS 等大量 class 的情况,只取前 2-3 个 class\n let className = ''\n if (el.className) {\n const classes = String(el.className).split(' ').filter(c => c)\n // 如果 class 太多(可能是 Tailwind),只取前 2 个\n if (classes.length > 5) {\n className = '.' + classes.slice(0, 2).join('.')\n } else {\n className = '.' + classes.join('.')\n }\n }\n\n const selector = tagName + id + className\n\n // 获取元素的文本内容(如果是文本节点或短文本元素)\n let textContent = ''\n if (el.nodeType === Node.TEXT_NODE) {\n textContent = el.textContent ? el.textContent.trim().substring(0, 50) : ''\n } else if (el.childNodes.length === 1 && el.childNodes[0].nodeType === Node.TEXT_NODE) {\n textContent = el.childNodes[0].textContent ? el.childNodes[0].textContent.trim().substring(0, 50) : ''\n }\n\n // 格式: @filename:line <selector> \"text content\" 或 @filename <selector> \"text content\"\n const linePart = location.line ? \\`:\\${location.line}\\` : ''\n let prompt = \\`@\\${filePath}\\${linePart} <\\${selector}>\\`\n if (textContent) {\n prompt += \\` \"\\${textContent}\"\\`\n }\n\n // 发送成功后展开面板\n const sendToTerminal = () => {\n if (!terminalIframe || !terminalIframe.contentWindow) {\n return false\n }\n\n const win = terminalIframe.contentWindow\n\n // Check if sendToTerminal function is available\n if (win.sendToTerminal) {\n win.sendToTerminal(prompt)\n console.log('[Claude Dev Server] Sent via sendToTerminal:', prompt)\n // 发送成功后展开面板\n setTimeout(() => togglePanel(true), 100)\n return true\n }\n\n return false\n }\n\n // Try immediately\n if (sendToTerminal()) {\n return\n }\n\n // If not ready, wait and retry\n let attempts = 0\n const maxAttempts = 50\n const retryInterval = setInterval(() => {\n attempts++\n if (sendToTerminal() || attempts >= maxAttempts) {\n clearInterval(retryInterval)\n if (attempts >= maxAttempts) {\n console.warn('[Claude Dev Server] Could not send to terminal after retries')\n }\n }\n }, 100)\n }\n\n function loadTerminalIframe(ttydUrl) {\n if (!terminalContainer || terminalIframe) return\n ttydWsUrl = ttydUrl\n\n // Create iframe pointing to ttyd/index.html with ttyd WebSocket URL\n terminalIframe = document.createElement('iframe')\n // Pass the ttyd WebSocket URL as query param\n terminalIframe.src = '/ttyd/index.html?ws=' + encodeURIComponent(ttydUrl)\n terminalIframe.allow = 'clipboard-read; clipboard-write'\n\n // Load event - iframe is ready\n terminalIframe.onload = () => {\n console.log('[Claude Dev Server] Terminal iframe loaded')\n }\n\n terminalContainer.appendChild(terminalIframe)\n }\n\n function createPanel() {\n if (panel) return\n\n panel = document.createElement('div')\n panel.className = 'claude-dev-server-panel'\n panel.innerHTML = \\`\n <div class=\"claude-dev-server-header\">\n <span class=\"claude-dev-server-title\">Claude Code</span>\n <div class=\"claude-dev-server-actions\">\n <button class=\"claude-dev-server-btn claude-dev-server-btn-inspect\" title=\"Inspect Element\">\n <svg width=\"12\" height=\"12\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\n <path d=\"M2 12h20M12 2v20M4.93 4.93l14.14 14.14M19.07 4.93L4.93 19.07\"/>\n </svg>\n Inspect\n </button>\n </div>\n <button class=\"claude-dev-server-close\" aria-label=\"Close\" title=\"Minimize to icon\">&times;</button>\n </div>\n <div class=\"claude-dev-server-terminal\"></div>\n \\`\n\n document.body.appendChild(panel)\n\n terminalContainer = panel.querySelector('.claude-dev-server-terminal')\n const closeBtn = panel.querySelector('.claude-dev-server-close')\n const inspectBtn = panel.querySelector('.claude-dev-server-btn-inspect')\n\n closeBtn && closeBtn.addEventListener('click', () => togglePanel(false))\n\n inspectBtn && inspectBtn.addEventListener('click', () => {\n if (isInspectMode) {\n disableInspectMode()\n } else {\n // 点击 Inspect 时先收起面板\n if (isOpen) {\n togglePanel(false)\n }\n enableInspectMode()\n }\n })\n }\n\n function togglePanel(force) {\n createPanel()\n if (typeof force === 'boolean') {\n isOpen = force\n } else {\n isOpen = !isOpen\n }\n\n if (panel) panel.classList.toggle('open', isOpen)\n if (toggleBtn) toggleBtn.classList.toggle('hidden', isOpen)\n\n if (isOpen) {\n // Focus iframe when panel opens\n if (terminalIframe && terminalIframe.contentWindow) {\n setTimeout(() => {\n if (terminalIframe.contentWindow.terminalInstance) {\n terminalIframe.contentWindow.terminalInstance.focus()\n }\n }, 100)\n }\n }\n }\n\n function connect(port) {\n const WS_URL = 'ws://localhost:' + port\n ws = new WebSocket(WS_URL)\n\n ws.onopen = () => {\n console.log('[Claude Dev Server] Connected to control server')\n }\n\n ws.onmessage = (e) => {\n try {\n const msg = JSON.parse(e.data)\n console.log('[Claude Dev Server] Received message:', msg.type, msg)\n if (msg.type === 'ready' && msg.ttydUrl) {\n loadTerminalIframe(msg.ttydUrl)\n } else if (msg.type === 'inspectResult') {\n if (msg.location) {\n showContextPanel(msg.location, null, false)\n }\n }\n } catch (err) {\n console.error('[Claude Dev Server] Message parse error:', err)\n }\n }\n\n ws.onclose = () => {\n console.log('[Claude Dev Server] Control WebSocket disconnected, reconnecting...')\n setTimeout(() => connect(port), 2000)\n }\n\n ws.onerror = (err) => {\n console.error('[Claude Dev Server] Control WebSocket error:', err)\n }\n }\n\n initWhenReady()\n\n document.addEventListener('keydown', (e) => {\n if ((e.metaKey || e.ctrlKey) && e.code === 'Backquote') {\n e.preventDefault()\n togglePanel()\n }\n if (e.key === 'Escape' && isOpen) {\n togglePanel(false)\n }\n if (e.key === 'Escape' && isInspectMode) {\n disableInspectMode()\n }\n })\n})()\n`\n","import { spawn } from 'child_process'\nimport { existsSync, readFileSync } from 'fs'\nimport { createHash } from 'crypto'\nimport { join, dirname, relative, sep } from 'path'\nimport http from 'http'\nimport { fileURLToPath } from 'url'\nimport { createProxyMiddleware } from 'http-proxy-middleware'\nimport { WebSocketServer } from 'ws'\nimport { createConnection } from 'net'\nimport { SourceMapConsumer } from 'source-map'\nimport { createWebSocketServer } from '../server/websocket.js'\nimport { CLIENT_SCRIPT, CLIENT_STYLES } from '../client/injection.js'\n\nconst __filename = fileURLToPath(import.meta.url)\nconst __dirname = dirname(__filename)\n\nexport interface UniversalServerOptions {\n port?: number\n claudePath?: string\n claudeArgs?: string[]\n cwd?: string\n server?: {\n type?: 'vite' | 'next' | 'webpack' | 'custom'\n command?: string\n port?: number\n }\n}\n\nexport async function startUniversalServer(options: UniversalServerOptions = {}) {\n const cwd = options.cwd || process.cwd()\n const port = options.port || 3000\n\n // 1. 检测项目类型\n const projectType = options.server?.type || detectProjectType(cwd)\n const targetCommand = options.server?.command || getDefaultCommand(projectType)\n\n console.log(`[Claude Dev Server] Detected project type: ${projectType}`)\n console.log(`[Claude Dev Server] Starting target server...`)\n\n // 2. 启动目标 dev server(不预设端口)\n const targetServer = spawnTargetServer(targetCommand, cwd)\n\n // 3. 等待并检测目标服务器实际监听的端口\n console.log(`[Claude Dev Server] Waiting for target server to start (PID: ${targetServer.pid})...`)\n const targetPort = await detectServerPort(targetServer, 30000)\n\n if (!targetPort) {\n throw new Error('Failed to detect target server port. Please check if the dev server started successfully.')\n }\n\n console.log(`[Claude Dev Server] Target server is running on port ${targetPort}`)\n\n // 4. 启动控制 WebSocket 服务器(ttyd + Claude)\n const wsServer = createWebSocketServer({\n port: 0, // 自动分配端口\n projectRoot: cwd,\n claudePath: options.claudePath || 'claude',\n claudeArgs: options.claudeArgs || [],\n })\n\n const { wsPort, ttydPort } = await wsServer.start()\n console.log(`[Claude Dev Server] Control server running on ws://localhost:${wsPort}`)\n console.log(`[Claude Dev Server] ttyd running on ws://localhost:${ttydPort}`)\n\n // 5. 启动 HTTP 代理服务器\n const proxyServer = createProxyServer(targetPort, wsPort, cwd)\n proxyServer.listen(port)\n console.log(`[Claude Dev Server] Proxy server running on http://localhost:${port}`)\n console.log(`\\n🚀 Ready! Open http://localhost:${port} in your browser`)\n\n // 清理处理\n const cleanup = () => {\n console.log('[Claude Dev Server] Shutting down...')\n targetServer.kill()\n wsServer.stop()\n proxyServer.close()\n process.exit(0)\n }\n\n process.on('SIGINT', cleanup)\n process.on('SIGTERM', cleanup)\n\n return { proxyServer, targetServer, wsServer }\n}\n\nfunction detectProjectType(cwd: string): 'vite' | 'next' | 'webpack' | 'custom' {\n if (existsSync(join(cwd, 'vite.config.ts')) || existsSync(join(cwd, 'vite.config.js'))) {\n return 'vite'\n }\n if (existsSync(join(cwd, 'next.config.js')) || existsSync(join(cwd, 'next.config.mjs'))) {\n return 'next'\n }\n if (existsSync(join(cwd, 'webpack.config.js')) || existsSync(join(cwd, 'webpack.config.ts'))) {\n return 'webpack'\n }\n return 'custom'\n}\n\nfunction getDefaultCommand(projectType: string): string {\n switch (projectType) {\n case 'vite': return 'npm run dev'\n case 'next': return 'npm run dev'\n case 'webpack': return 'npm run dev'\n default: return 'npm run dev'\n }\n}\n\nfunction spawnTargetServer(command: string, cwd: string) {\n const [cmd, ...args] = command.split(' ')\n const child = spawn(cmd, args, {\n cwd,\n stdio: 'pipe',\n env: process.env,\n })\n\n // Forward child's stdout/stderr to parent so user can see dev server output\n child.stdout?.pipe(process.stdout)\n child.stderr?.pipe(process.stderr)\n\n return child\n}\n\n/**\n * Detect the port that the dev server is listening on by parsing stdout.\n * Most dev servers announce their port on startup like: \"Local: http://localhost:5173\"\n */\nasync function detectServerPort(childProcess: any, timeout: number): Promise<number | null> {\n return new Promise((resolve) => {\n const timeoutId = setTimeout(() => {\n cleanup()\n resolve(null)\n }, timeout)\n\n let stdout = ''\n\n const onData = (chunk: Buffer) => {\n stdout += chunk.toString()\n\n // Match common port announcement patterns:\n // Vite: \"Local: http://localhost:5173/\"\n // Next.js: \"Local: http://localhost:3000\"\n // Webpack: \"http://localhost:8080/\"\n const patterns = [\n /localhost:(\\d{4,5})/,\n /127\\.0\\.0\\.1:(\\d{4,5})/,\n /0\\.0\\.0\\.0:(\\d{4,5})/,\n ]\n\n for (const pattern of patterns) {\n const match = stdout.match(pattern)\n if (match) {\n const port = parseInt(match[1], 10)\n console.log(`[Claude Dev Server] Detected port from stdout: ${port}`)\n cleanup()\n resolve(port)\n return\n }\n }\n }\n\n const cleanup = () => {\n clearTimeout(timeoutId)\n childProcess.stdout?.off('data', onData)\n }\n\n childProcess.stdout?.on('data', onData)\n })\n}\n\n/**\n * Handle source map resolution request.\n * Given a compiled file path and line/column, find the original source position.\n */\nasync function handleSourceMapRequest(\n projectRoot: string,\n filePath: string,\n line: number,\n column: number,\n targetPort?: number\n): Promise<{ file?: string; line?: number; column?: number; original?: any; error?: string }> {\n try {\n let content: string\n let fullPath = filePath\n\n // If filePath starts with / and looks like a URL path, fetch from Vite\n if (filePath.startsWith('/') && targetPort) {\n console.log('[Claude Dev Server] Fetching from Vite:', filePath)\n try {\n const response = await fetch(`http://localhost:${targetPort}${filePath}`)\n if (!response.ok) {\n console.log('[Claude Dev Server] Failed to fetch from Vite:', response.status)\n return { error: 'Failed to fetch from Vite' }\n }\n content = await response.text()\n console.log('[Claude Dev Server] Fetched', content.length, 'chars from Vite')\n } catch (e) {\n console.log('[Claude Dev Server] Fetch error:', e)\n return { error: 'Fetch error: ' + (e as Error).message }\n }\n } else {\n // If filePath is absolute, check if it's within projectRoot\n if (!filePath.startsWith('/')) {\n fullPath = join(projectRoot, filePath)\n }\n\n // Check if the file exists\n if (!existsSync(fullPath)) {\n console.log('[Claude Dev Server] File not found:', fullPath)\n return { error: 'File not found' }\n }\n\n // Read the file content\n content = readFileSync(fullPath, 'utf-8')\n\n console.log('[Claude Dev Server] Resolving source map for:', fullPath, 'at line:', line)\n }\n\n // Look for sourceMappingURL comment\n // Format: //# sourceMappingURL=filename.js.map or /*# sourceMappingURL=filename.js.map */\n // Or inline: //# sourceMappingURL=data:application/json;base64,...\n let sourceMapUrl: string | null = null\n const patterns = [\n /\\/\\/[@#]\\s*sourceMappingURL=([^\\s]+)/,\n /\\/\\*[@#]\\s*sourceMappingURL=([^\\s]+)\\s*\\*\\//\n ]\n\n for (const pattern of patterns) {\n const match = content.match(pattern)\n if (match) {\n sourceMapUrl = match[1]\n console.log('[Claude Dev Server] Found sourceMappingURL:', sourceMapUrl.substring(0, 100) + '...')\n break\n }\n }\n\n if (!sourceMapUrl) {\n console.log('[Claude Dev Server] No source map found in:', fullPath)\n return { file: relative(projectRoot, fullPath), line, column }\n }\n\n let sourceMapContent: string\n let sourceMap: any\n\n // Check if it's an inline source map (data URL)\n if (sourceMapUrl.startsWith('data:application/json;base64,') ||\n sourceMapUrl.startsWith('data:application/json;charset=utf-8;base64,')) {\n console.log('[Claude Dev Server] Found inline source map')\n // Extract base64 content\n const base64Data = sourceMapUrl.split(',', 2)[1]\n sourceMapContent = Buffer.from(base64Data, 'base64').toString('utf-8')\n try {\n sourceMap = JSON.parse(sourceMapContent)\n } catch (e) {\n console.log('[Claude Dev Server] Failed to parse inline source map:', e)\n return { file: relative(projectRoot, fullPath), line, column }\n }\n } else if (sourceMapUrl.startsWith('http://') || sourceMapUrl.startsWith('https://')) {\n // Remote source map - try to fetch via proxy\n console.log('[Claude Dev Server] Remote source map not supported:', sourceMapUrl)\n return { file: relative(projectRoot, fullPath), line, column }\n } else {\n // External source map file\n let sourceMapPath: string\n if (sourceMapUrl.startsWith('/')) {\n // Absolute path\n sourceMapPath = sourceMapUrl\n } else {\n // Relative path - resolve relative to the file\n sourceMapPath = join(dirname(fullPath), sourceMapUrl)\n }\n\n console.log('[Claude Dev Server] Reading external source map:', sourceMapPath)\n\n // Read the source map\n if (!existsSync(sourceMapPath)) {\n console.log('[Claude Dev Server] Source map file not found:', sourceMapPath)\n return { file: relative(projectRoot, fullPath), line, column }\n }\n\n sourceMapContent = readFileSync(sourceMapPath, 'utf-8')\n sourceMap = JSON.parse(sourceMapContent)\n }\n\n console.log('[Claude Dev Server] Source map loaded, sources:', sourceMap.sources?.length, 'mappings:', sourceMap.mappings?.substring(0, 50) + '...')\n\n // Use source-map library to find original position\n const consumer = await new SourceMapConsumer(sourceMap)\n const original = consumer.originalPositionFor({\n line: line,\n column: column\n })\n consumer.destroy()\n\n console.log('[Claude Dev Server] Source map result:', {\n generated: { line, column },\n original: original\n })\n\n if (original.source && original.line !== null) {\n // Convert source path to be relative to project root\n let originalFile = original.source\n if (originalFile.startsWith('webpack://')) {\n // Remove webpack:// prefix\n originalFile = originalFile.replace(/^webpack:\\/\\/[\\/\\\\]?/, '')\n }\n // For Vite, source might be relative\n if (!originalFile.startsWith('/')) {\n // Try to resolve relative to project root\n const possiblePath = join(projectRoot, originalFile)\n if (existsSync(possiblePath)) {\n originalFile = relative(projectRoot, possiblePath)\n }\n }\n\n console.log('[Claude Dev Server] Source map resolved:', {\n original: { file: originalFile, line: original.line, column: original.column },\n generated: { file: filePath, line, column }\n })\n\n return {\n file: originalFile,\n line: original.line,\n column: original.column || 1,\n original: {\n source: original.source,\n line: original.line,\n column: original.column,\n name: original.name\n }\n }\n }\n\n // No original position found, return generated\n return { file: relative(projectRoot, fullPath), line, column }\n } catch (err) {\n console.error('[Claude Dev Server] Source map resolution error:', err)\n return { error: String(err) }\n }\n}\n\n/**\n * Handle Next.js Turbopack source file lookup.\n * Given a page path, find the corresponding source file by parsing Turbopack chunks.\n */\nasync function handleTurbopackLookup(\n projectRoot: string,\n pagePath: string,\n targetPort: number\n): Promise<{ file?: string; line?: number; error?: string }> {\n try {\n console.log('[Claude Dev Server] Turbopack lookup for page:', pagePath)\n\n // Get the HTML page to find loaded chunk URLs\n const pageRes = await fetch(`http://localhost:${targetPort}${pagePath}`)\n if (!pageRes.ok) {\n return { error: 'Failed to fetch page' }\n }\n\n const html = await pageRes.text()\n const chunkUrls: string[] = []\n\n // Extract chunk URLs from HTML\n const scriptMatches = html.matchAll(/<script[^>]*src=\"([^\"]*\\/_next\\/static\\/chunks\\/[^\"]*)\"/g)\n for (const match of scriptMatches) {\n if (match[1]) {\n chunkUrls.push(match[1])\n }\n }\n\n console.log('[Claude Dev Server] Found', chunkUrls.length, 'chunk URLs')\n\n // Parse each chunk to find module paths\n for (const chunkUrl of chunkUrls) {\n try {\n const fullUrl = chunkUrl.startsWith('http') ? chunkUrl : `http://localhost:${targetPort}${chunkUrl}`\n const chunkRes = await fetch(fullUrl)\n if (!chunkRes.ok) continue\n\n const chunkContent = await chunkRes.text()\n\n // Extract module paths from Turbopack format\n // Format: \"[project]/path/to/file.tsx\" or \"[project]/path/to/file.tsx (ecmascript)\"\n const modulePathRegex = /\\[project\\]([^\\s\"]+\\.(tsx?|jsx?))/g\n const matches = [...chunkContent.matchAll(modulePathRegex)]\n\n for (const match of matches) {\n if (match[1]) {\n const sourcePath = match[1]\n // Remove [project] prefix and convert to relative path\n let relativePath = sourcePath.replace(/^\\[project\\]/, '')\n\n // Check if this path matches the current page\n // Normalize page path: /zh/login -> /login or /app/[lng]/(auth)/login/page.tsx\n const normalizedPagePath = pagePath.replace(/^\\/[^/]+/, '') // Remove language prefix\n\n if (relativePath.toLowerCase().includes(normalizedPagePath.toLowerCase()) ||\n relativePath.toLowerCase().includes('login')) {\n // Found matching source file\n console.log('[Claude Dev Server] Found source file:', relativePath)\n\n return {\n file: relativePath,\n line: undefined // Turbopack doesn't provide line numbers\n }\n }\n }\n }\n } catch (e) {\n console.log('[Claude Dev Server] Error fetching chunk:', chunkUrl, e)\n }\n }\n\n return { error: 'Source file not found for page: ' + pagePath }\n } catch (err) {\n console.error('[Claude Dev Server] Turbopack lookup error:', err)\n return { error: String(err) }\n }\n}\n\nfunction createProxyServer(targetPort: number, wsPort: number, projectRoot: string) {\n // 读取 ttyd 资源文件\n // 从当前模块路径计算 assets 目录\n // moduleDir 是 /Users/lloyd/moox/claude-dev-server/dist\n // 所以需要 assets 是 /Users/lloyd/moox/claude-dev-server/dist/assets\n const moduleDir = dirname(fileURLToPath(import.meta.url))\n const assetsPath = join(moduleDir, 'assets')\n\n let ttydHtml: string\n let ttydBridgeJs: string\n\n try {\n ttydHtml = readFileSync(join(assetsPath, 'ttyd-terminal.html'), 'utf-8')\n ttydBridgeJs = readFileSync(join(assetsPath, 'ttyd-bridge.js'), 'utf-8')\n } catch (e: any) {\n console.error('[Claude Dev Server] Failed to read ttyd assets from', assetsPath)\n console.error('[Claude Dev Server] moduleDir:', moduleDir)\n console.error('[Claude Dev Server] Error:', e.message)\n throw new Error('ttyd assets not found. Please run `npm run build` first.')\n }\n\n const server = http.createServer((req, res) => {\n // 处理 @claude-port 端点\n if (req.url === '/@claude-port') {\n res.setHeader('Content-Type', 'application/json')\n res.end(JSON.stringify({ port: wsPort }))\n return\n }\n\n // 处理 sourcemap 解析端点\n if (req.url?.startsWith('/@sourcemap?')) {\n const url = new URL(req.url, `http://localhost:${wsPort}`)\n const file = url.searchParams.get('file')\n const line = url.searchParams.get('line')\n const column = url.searchParams.get('col')\n\n if (file && line && column) {\n handleSourceMapRequest(projectRoot, file, parseInt(line), parseInt(column || '1'), targetPort)\n .then(result => {\n res.setHeader('Content-Type', 'application/json')\n res.end(JSON.stringify(result))\n })\n .catch(err => {\n console.error('[Claude Dev Server] Source map error:', err)\n res.setHeader('Content-Type', 'application/json')\n res.end(JSON.stringify({ error: err.message }))\n })\n return\n }\n }\n\n // 处理 Next.js/Turbopack 源文件查找端点\n if (req.url?.startsWith('/@sourcemap-lookup?')) {\n const url = new URL(req.url, `http://localhost:${wsPort}`)\n const page = url.searchParams.get('page')\n const framework = url.searchParams.get('framework')\n\n if (page && framework === 'nextjs') {\n handleTurbopackLookup(projectRoot, page, targetPort)\n .then(result => {\n res.setHeader('Content-Type', 'application/json')\n res.end(JSON.stringify(result))\n })\n .catch(err => {\n console.error('[Claude Dev Server] Turbopack lookup error:', err)\n res.setHeader('Content-Type', 'application/json')\n res.end(JSON.stringify({ error: err.message }))\n })\n return\n }\n }\n\n // 处理 /ttyd/* 路由\n if (req.url?.startsWith('/ttyd/')) {\n const urlPath = req.url.split('?')[0] // 移除查询参数\n\n if (urlPath === '/ttyd/index.html' || urlPath === '/ttyd/') {\n res.setHeader('Content-Type', 'text/html')\n res.end(ttydHtml)\n return\n }\n\n if (urlPath === '/ttyd/ttyd-bridge.js') {\n res.setHeader('Content-Type', 'application/javascript')\n res.end(ttydBridgeJs)\n return\n }\n\n // Handle ttyd token endpoint\n if (urlPath === '/ttyd/token' || urlPath === '/ttyd/index.html/token') {\n res.setHeader('Content-Type', 'application/json')\n res.end(JSON.stringify({ token: '' }))\n return\n }\n\n // 其他 /ttyd/* 路由返回 404\n res.statusCode = 404\n res.end('Not found')\n return\n }\n\n // 代理到目标服务器并注入脚本\n // Remove accept-encoding to get uncompressed content (needed for script injection)\n const proxyHeaders = { ...req.headers }\n delete proxyHeaders['accept-encoding']\n\n const options = {\n hostname: 'localhost',\n port: targetPort,\n path: req.url,\n method: req.method,\n headers: proxyHeaders,\n }\n\n const proxyReq = http.request(options, (proxyRes) => {\n // 注入脚本到 HTML\n if (proxyRes.headers['content-type']?.includes('text/html')) {\n const body: Buffer[] = []\n proxyRes.on('data', (chunk) => body.push(chunk))\n proxyRes.on('end', () => {\n const html = Buffer.concat(body).toString('utf8')\n const injected = injectScripts(html, wsPort, projectRoot)\n const statusCode = proxyRes.statusCode || 200\n res.writeHead(statusCode, {\n ...proxyRes.headers,\n 'content-length': Buffer.byteLength(injected),\n })\n res.end(injected)\n })\n } else {\n const statusCode = proxyRes.statusCode || 200\n res.writeHead(statusCode, proxyRes.headers)\n proxyRes.pipe(res)\n }\n })\n\n proxyReq.on('error', (err) => {\n console.error('[Claude Dev Server] Proxy error:', err)\n res.statusCode = 502\n res.end('Bad Gateway')\n })\n\n req.pipe(proxyReq)\n })\n\n // Handle WebSocket upgrade requests (e.g., Next.js webpack-hmr, Vite HMR)\n server.on('upgrade', (req: any, socket: any, head: Buffer) => {\n // Check if this is a WebSocket upgrade request\n if (req.headers['upgrade']?.toLowerCase() !== 'websocket') {\n return\n }\n\n console.log('[Claude Dev Server] WebSocket upgrade request:', req.url)\n\n // Create raw TCP connection to target server\n const targetSocket = createConnection(targetPort, 'localhost', () => {\n console.log('[Claude Dev Server] Connected to target WebSocket server')\n\n // Forward the upgrade request to target\n const upgradeRequest = [\n `${req.method} ${req.url} HTTP/1.1`,\n `Host: localhost:${targetPort}`,\n 'Upgrade: websocket',\n 'Connection: Upgrade',\n `Sec-WebSocket-Key: ${req.headers['sec-websocket-key']}`,\n `Sec-WebSocket-Version: ${req.headers['sec-websocket-version'] || '13'}`,\n ]\n\n if (req.headers['sec-websocket-protocol']) {\n upgradeRequest.push(`Sec-WebSocket-Protocol: ${req.headers['sec-websocket-protocol']}`)\n }\n if (req.headers['sec-websocket-extensions']) {\n upgradeRequest.push(`Sec-WebSocket-Extensions: ${req.headers['sec-websocket-extensions']}`)\n }\n\n targetSocket.write(upgradeRequest.join('\\r\\n') + '\\r\\n\\r\\n')\n\n // Forward the head (remaining data from the upgrade request)\n if (head && head.length > 0) {\n targetSocket.write(head)\n }\n })\n\n // Handle response from target server\n targetSocket.on('data', (data: Buffer) => {\n if (socket.writable) {\n socket.write(data)\n }\n })\n\n // Handle data from client\n socket.on('data', (data: Buffer) => {\n if (targetSocket.writable) {\n targetSocket.write(data)\n }\n })\n\n // Handle connection close\n socket.on('close', () => {\n console.log('[Claude Dev Server] Client WebSocket closed')\n targetSocket.destroy()\n })\n\n targetSocket.on('close', () => {\n console.log('[Claude Dev Server] Target WebSocket closed')\n socket.end()\n })\n\n // Handle errors\n socket.on('error', (err: Error) => {\n console.error('[Claude Dev Server] Client socket error:', err.message)\n targetSocket.destroy()\n })\n\n targetSocket.on('error', (err: Error) => {\n console.error('[Claude Dev Server] Target socket error:', err.message)\n socket.end()\n })\n })\n\n return server\n}\n\nfunction injectScripts(html: string, wsPort: number, projectRoot: string): string {\n const projectRootScript = `<script>window.__CLAUDE_PROJECT_ROOT__ = ${JSON.stringify(projectRoot)}</script>`\n\n return html.replace(\n '</head>',\n `<!-- Claude Dev Server -->\\n${CLIENT_STYLES}\\n${projectRootScript}\\n<script type=\"module\">${CLIENT_SCRIPT}</script>\\n</head>`\n ).replace(\n /wsPort:\\s*\\d+/,\n `wsPort: ${wsPort}`\n )\n}\n\n/**\n * Generate Sec-WebSocket-Accept header value for WebSocket handshake.\n * RFC 6455: base64(sha1(clientKey + GUID))\n */\nfunction generateWebSocketAccept(clientKey: string): string {\n const GUID = '258EAFA5-E914-47DA-95CA-C5AB0DC85B11'\n const hash = createHash('sha1')\n .update(clientKey + GUID)\n .digest('base64')\n return hash\n}\n"]}
package/dist/cli.js CHANGED
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import { startUniversalServer } from './chunk-DOLSA776.js';
2
+ import { startUniversalServer } from './chunk-3GUIIEFR.js';
3
3
  import { execSync } from 'child_process';
4
4
 
5
5
  async function checkTtyd() {
package/dist/index.js CHANGED
@@ -1,3 +1,3 @@
1
- export { startUniversalServer } from './chunk-DOLSA776.js';
1
+ export { startUniversalServer } from './chunk-3GUIIEFR.js';
2
2
  //# sourceMappingURL=index.js.map
3
3
  //# sourceMappingURL=index.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-dev-server",
3
- "version": "1.0.2",
3
+ "version": "1.1.1",
4
4
  "description": "Universal dev server wrapper with Claude Code AI assistant - works with Vite, Next.js, Webpack and more",
5
5
  "author": "lloydzhou",
6
6
  "license": "MIT",
@@ -41,8 +41,9 @@
41
41
  "webpack"
42
42
  ],
43
43
  "dependencies": {
44
- "ws": "^8.18.0",
45
- "http-proxy-middleware": "^2.0.6"
44
+ "http-proxy-middleware": "^2.0.6",
45
+ "source-map": "^0.7.6",
46
+ "ws": "^8.18.0"
46
47
  },
47
48
  "devDependencies": {
48
49
  "@types/node": "^20.14.11",
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/server/pty.ts","../src/server/source-map.ts","../src/server/websocket.ts","../src/client/injection.js","../src/universal/index.ts"],"names":["resolve","__filename","spawn"],"mappings":"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AA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,CAAA;AAoOtB,IAAM,aAAA,GAAgB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;A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","file":"chunk-DOLSA776.js","sourcesContent":["import { spawn } from 'child_process'\nimport { randomBytes } from 'crypto'\n\nexport interface PtyOptions {\n cwd: string\n claudePath: string\n args: string[]\n env?: Record<string, string>\n}\n\nexport function spawnClaudeCode(options: PtyOptions) {\n // Generate a random port for ttyd\n const port = 50000 + Math.floor(Math.random() * 10000)\n\n // Start ttyd with claude command\n const ttydProc = spawn('ttyd', [\n '--port', String(port),\n '--interface', '127.0.0.1',\n '--writable',\n options.claudePath,\n ...options.args,\n ], {\n cwd: options.cwd,\n env: {\n ...process.env,\n ...options.env,\n TERM: 'xterm-256color',\n FORCE_COLOR: '1',\n },\n })\n\n ttydProc.on('exit', (code) => {\n console.log(`[claude-dev-server] ttyd exited - code: ${code}`)\n })\n\n ttydProc.on('error', (err) => {\n console.error(`[claude-dev-server] ttyd error: ${err.message}`)\n })\n\n // Wait a bit for ttyd to start, then return the WebSocket URL\n return new Promise<{ wsUrl: string; process: typeof ttydProc; port: number }>((resolve, reject) => {\n ttydProc.stdout?.on('data', (data: Buffer) => {\n const msg = data.toString()\n console.log(`[ttyd] ${msg}`)\n })\n\n ttydProc.stderr?.on('data', (data: Buffer) => {\n const msg = data.toString()\n console.error(`[ttyd stderr] ${msg}`)\n })\n\n // Give ttyd a moment to start\n setTimeout(() => {\n resolve({\n wsUrl: `ws://127.0.0.1:${port}`,\n process: ttydProc,\n port,\n })\n }, 500)\n\n ttydProc.on('error', reject)\n })\n}\n","import { SourceMapConsumer } from 'source-map'\n\nexport interface SourcePosition {\n source: string\n line: number\n column: number\n name?: string\n}\n\nexport interface CodeLocation {\n file: string\n line: number\n column: number\n content?: string\n}\n\n// Store source maps by generated file path\nconst sourceMapCache = new Map<string, SourceMapConsumer>()\n\nexport async function parseSourceMap(sourceMapUrl: string, content: string): Promise<void> {\n try {\n const consumer = await new SourceMapConsumer(content)\n sourceMapCache.set(sourceMapUrl, consumer)\n } catch (err) {\n console.error('Failed to parse source map:', err)\n }\n}\n\nexport async function findOriginalPosition(\n generatedFile: string,\n line: number,\n column: number\n): Promise<SourcePosition | null> {\n const consumer = sourceMapCache.get(generatedFile)\n if (!consumer) {\n return null\n }\n\n try {\n const position = consumer.originalPositionFor({ line, column })\n if (position.source) {\n return {\n source: position.source,\n line: position.line || 1,\n column: position.column || 0,\n name: position.name,\n }\n }\n } catch (err) {\n console.error('Failed to find original position:', err)\n }\n\n return null\n}\n\nexport function generateVSCodeURI(location: CodeLocation): string {\n // Generate VS Code URI scheme for opening files\n // vscode://file/{path}:{line}:{col}\n const filePath = location.file.replace(/\\\\/g, '/')\n return `vscode://file/${filePath}:${location.line}:${location.column}`\n}\n\nexport function formatCodeContext(location: CodeLocation): string {\n return `\n📍 Code Location:\n File: ${location.file}\n Line: ${location.line}\n Column: ${location.column}\n`\n}\n\nexport function clearCache() {\n sourceMapCache.clear()\n}\n","import { WebSocketServer, WebSocket } from 'ws'\nimport { spawnClaudeCode } from './pty.js'\nimport { findOriginalPosition, parseSourceMap, CodeLocation, formatCodeContext } from './source-map.js'\nimport { readFile } from 'fs/promises'\nimport { resolve } from 'path'\n\nexport interface WebSocketServerOptions {\n port?: number\n projectRoot: string\n claudePath: string\n claudeArgs: string[]\n}\n\nexport function createWebSocketServer(options: WebSocketServerOptions) {\n let wss: WebSocketServer | null = null\n let port = options.port || 0\n let ttydInfo: { wsUrl: string; process: any; port: number } | null = null\n\n const start = async (): Promise<{ wsPort: number; ttydPort: number }> => {\n // Start ttyd first\n console.log('[claude-dev-server] Starting ttyd...')\n ttydInfo = await spawnClaudeCode({\n cwd: options.projectRoot,\n claudePath: options.claudePath,\n args: options.claudeArgs,\n })\n console.log(`[claude-dev-server] ttyd running at ${ttydInfo.wsUrl}`)\n\n // Also start a simple WebSocket server for inspect functionality\n return new Promise((resolve, reject) => {\n wss = new WebSocketServer({ port, host: '127.0.0.1' })\n\n wss.on('listening', () => {\n const address = wss!.address()\n if (address && typeof address === 'object') {\n port = address.port\n resolve({ wsPort: port, ttydPort: ttydInfo!.port })\n }\n })\n\n wss.on('error', (err) => {\n reject(err)\n })\n\n wss.on('connection', (ws: WebSocket) => {\n ws.on('message', async (message: Buffer) => {\n try {\n const msg = JSON.parse(message.toString())\n\n if (msg.type === 'inspect') {\n await handleInspect(msg, ws, options.projectRoot)\n } else if (msg.type === 'loadSourceMap') {\n await handleLoadSourceMap(msg, ws, options.projectRoot)\n }\n } catch (err) {\n // Ignore\n }\n })\n\n // Send ready message with ttyd URL\n ws.send(JSON.stringify({\n type: 'ready',\n ttydUrl: ttydInfo!.wsUrl\n }))\n })\n })\n }\n\n const stop = () => {\n ttydInfo?.process.kill()\n wss?.close()\n }\n\n return { start, stop }\n}\n\nasync function handleLoadSourceMap(\n msg: any,\n ws: WebSocket,\n projectRoot: string\n) {\n const { sourceMapUrl } = msg\n try {\n const mapPath = resolve(projectRoot, sourceMapUrl.replace(/^\\//, ''))\n const content = await readFile(mapPath, 'utf-8')\n await parseSourceMap(sourceMapUrl, content)\n ws.send(JSON.stringify({\n type: 'sourceMapLoaded',\n sourceMapUrl,\n success: true\n }))\n } catch (err) {\n ws.send(JSON.stringify({\n type: 'sourceMapLoaded',\n sourceMapUrl,\n success: false,\n error: (err as Error).message\n }))\n }\n}\n\nasync function handleInspect(\n msg: any,\n ws: WebSocket,\n projectRoot: string\n) {\n const { url, line, column, sourceMapUrl } = msg\n\n let location: CodeLocation | null = null\n\n if (sourceMapUrl) {\n const original = await findOriginalPosition(sourceMapUrl, line, column)\n if (original) {\n location = {\n file: resolve(projectRoot, original.source),\n line: original.line,\n column: original.column,\n }\n }\n }\n\n if (!location) {\n const match = url.match(/\\/@fs\\/(.+?)(?:\\?|$)|\\/@vite\\/(.+?)(?:\\?|$)/)\n if (match) {\n location = {\n file: decodeURIComponent(match[1] || match[2]),\n line,\n column,\n }\n }\n }\n\n ws.send(JSON.stringify({\n type: 'inspectResult',\n location: location ? {\n file: location.file,\n line: location.line,\n column: location.column,\n context: formatCodeContext(location)\n } : null\n }))\n}\n","// Client-side script - pure JavaScript (no TypeScript)\n// This file will be injected into the browser\n\nexport const CLIENT_STYLES = `\n<style id=\"claude-dev-server-styles\">\n.claude-dev-server-toggle {\n position: fixed;\n top: 20px;\n right: 20px;\n width: 44px;\n height: 44px;\n background: #1e1e1e;\n border: none;\n border-radius: 50%;\n box-shadow: 0 2px 12px rgba(0,0,0,0.3);\n cursor: pointer;\n z-index: 999999;\n display: flex;\n align-items: center;\n justify-content: center;\n font-size: 20px;\n transition: transform 0.2s, background 0.2s;\n}\n.claude-dev-server-toggle:hover {\n transform: scale(1.1);\n background: #2d2d2d;\n}\n.claude-dev-server-toggle.hidden {\n display: none;\n}\n.claude-dev-server-inspect-btn {\n position: fixed;\n top: 74px;\n right: 20px;\n width: 44px;\n height: 44px;\n background: #1e1e1e;\n border: none;\n border-radius: 50%;\n box-shadow: 0 2px 12px rgba(0,0,0,0.3);\n cursor: pointer;\n z-index: 999999;\n display: flex;\n align-items: center;\n justify-content: center;\n font-size: 16px;\n transition: transform 0.2s, background 0.2s;\n}\n.claude-dev-server-inspect-btn:hover {\n transform: scale(1.1);\n background: #2d2d2d;\n}\n.claude-dev-server-inspect-btn.active {\n background: #007acc;\n}\n.claude-dev-server-inspect-btn.hidden {\n display: none;\n}\n.claude-dev-server-toggle svg,\n.claude-dev-server-inspect-btn svg {\n width: 20px;\n height: 20px;\n flex-shrink: 0;\n}\n.claude-dev-server-panel {\n position: fixed;\n top: 0;\n right: 0;\n width: 650px;\n height: 100vh;\n background: #1e1e1e;\n color: #d4d4d4;\n font-family: 'SF Mono', 'Monaco', 'Cascadia Code', 'Consolas', monospace;\n font-size: 13px;\n box-shadow: -4px 0 20px rgba(0,0,0,0.3);\n transform: translateX(100%);\n transition: transform 0.2s ease;\n z-index: 999999;\n display: flex;\n flex-direction: column;\n}\n.claude-dev-server-panel.open {\n transform: translateX(0);\n}\n.claude-dev-server-header {\n padding: 8px 12px;\n background: #2d2d2d;\n border-bottom: 1px solid #3e3e3e;\n display: flex;\n justify-content: space-between;\n align-items: center;\n user-select: none;\n}\n.claude-dev-server-title {\n font-weight: 600;\n color: #fff;\n display: flex;\n align-items: center;\n gap: 8px;\n font-size: 13px;\n}\n.claude-dev-server-title::before {\n content: '🤖';\n font-size: 14px;\n}\n.claude-dev-server-actions {\n display: flex;\n gap: 6px;\n}\n.claude-dev-server-btn {\n background: #3e3e3e;\n border: none;\n color: #d4d4d4;\n cursor: pointer;\n font-family: inherit;\n font-size: 11px;\n padding: 4px 10px;\n border-radius: 3px;\n transition: background 0.15s;\n display: flex;\n align-items: center;\n gap: 4px;\n}\n.claude-dev-server-btn:hover {\n background: #4e4e4e;\n}\n.claude-dev-server-btn.active {\n background: #007acc;\n color: #fff;\n}\n.claude-dev-server-close {\n background: none;\n border: none;\n color: #858585;\n cursor: pointer;\n font-size: 18px;\n padding: 0;\n width: 20px;\n height: 20px;\n display: flex;\n align-items: center;\n justify-content: center;\n border-radius: 3px;\n transition: background 0.15s, color 0.15s;\n}\n.claude-dev-server-close:hover {\n background: #3e3e3e;\n color: #fff;\n}\n.claude-dev-server-terminal {\n flex: 1;\n overflow: hidden;\n position: relative;\n background: #000;\n}\n.claude-dev-server-terminal iframe {\n width: 100%;\n height: 100%;\n border: none;\n}\n.claude-dev-server-inspect-overlay {\n position: fixed;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n pointer-events: none;\n z-index: 999998;\n display: none;\n}\n.claude-dev-server-inspect-overlay.active {\n display: block;\n}\n.claude-dev-server-highlight {\n position: absolute;\n border: 2px solid #007acc;\n background: rgba(0, 122, 204, 0.1);\n pointer-events: none;\n transition: all 0.1s ease;\n}\n.claude-dev-server-highlight::after {\n content: attr(data-element);\n position: absolute;\n top: -20px;\n left: 0;\n background: #007acc;\n color: #fff;\n font-size: 10px;\n padding: 2px 4px;\n border-radius: 2px 2px 0 0;\n font-family: monospace;\n white-space: nowrap;\n}\n/* Responsive layout for portrait mode */\n@media (orientation: portrait) {\n .claude-dev-server-toggle {\n top: auto;\n bottom: 20px;\n right: 10px;\n width: 36px;\n height: 36px;\n }\n .claude-dev-server-inspect-btn {\n top: auto;\n bottom: 66px;\n right: 10px;\n width: 36px;\n height: 36px;\n }\n .claude-dev-server-panel {\n top: auto;\n bottom: 0;\n right: 0;\n left: 0;\n width: 100%;\n height: 80vh;\n transform: translateY(100%);\n box-shadow: 0 -4px 20px rgba(0,0,0,0.3);\n }\n .claude-dev-server-panel.open {\n transform: translateY(0);\n }\n .claude-dev-server-toggle svg,\n .claude-dev-server-inspect-btn svg {\n width: 16px;\n height: 16px;\n }\n}\n</style>\n`\n\nexport const CLIENT_SCRIPT = `\n(() => {\n let ws = null\n let panel = null\n let toggleBtn = null\n let inspectBtn = null // 悬浮 inspect 按钮\n let terminalContainer = null\n let terminalIframe = null\n let overlay = null\n let isOpen = false\n let isInspectMode = false\n let ttydWsUrl = null\n\n // Fetch the WebSocket port from the server\n async function getWsPort() {\n const res = await fetch('/@claude-port')\n const data = await res.json()\n return data.port\n }\n\n async function initWhenReady() {\n try {\n const port = await getWsPort()\n connect(port)\n } catch (err) {\n console.error('[Claude Dev Server] Failed to get port:', err)\n // Retry after 1 second\n setTimeout(initWhenReady, 1000)\n return\n }\n createToggleBtn()\n createInspectBtn()\n createOverlay()\n createPanel()\n }\n\n function createToggleBtn() {\n if (toggleBtn) return\n toggleBtn = document.createElement('button')\n toggleBtn.className = 'claude-dev-server-toggle'\n toggleBtn.innerHTML = '<svg width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" fill=\"none\"><path d=\"M11.376 24L10.776 23.544L10.44 22.8L10.776 21.312L11.16 19.392L11.472 17.856L11.76 15.96L11.928 15.336L11.904 15.288L11.784 15.312L10.344 17.28L8.16 20.232L6.432 22.056L6.024 22.224L5.304 21.864L5.376 21.192L5.784 20.616L8.16 17.568L9.6 15.672L10.536 14.592L10.512 14.448H10.464L4.128 18.576L3 18.72L2.496 18.264L2.568 17.52L2.808 17.28L4.704 15.96L9.432 13.32L9.504 13.08L9.432 12.96H9.192L8.4 12.912L5.712 12.84L3.384 12.744L1.104 12.624L0.528 12.504L0 11.784L0.048 11.424L0.528 11.112L1.224 11.16L2.736 11.28L5.016 11.424L6.672 11.52L9.12 11.784H9.504L9.552 11.616L9.432 11.52L9.336 11.424L6.96 9.84L4.416 8.16L3.072 7.176L2.352 6.672L1.992 6.216L1.848 5.208L2.496 4.488L3.384 4.56L3.6 4.608L4.488 5.304L6.384 6.768L8.88 8.616L9.24 8.904L9.408 8.808V8.736L9.24 8.472L7.896 6.024L6.456 3.528L5.808 2.496L5.64 1.872C5.576 1.656 5.544 1.416 5.544 1.152L6.288 0.144001L6.696 0L7.704 0.144001L8.112 0.504001L8.736 1.92L9.72 4.152L11.28 7.176L11.736 8.088L11.976 8.904L12.072 9.168H12.24V9.024L12.36 7.296L12.6 5.208L12.84 2.52L12.912 1.752L13.296 0.840001L14.04 0.360001L14.616 0.624001L15.096 1.32L15.024 1.752L14.76 3.6L14.184 6.504L13.824 8.472H14.04L14.28 8.208L15.264 6.912L16.92 4.848L17.64 4.032L18.504 3.12L19.056 2.688H20.088L20.832 3.816L20.496 4.992L19.44 6.336L18.552 7.464L17.28 9.168L16.512 10.536L16.584 10.632H16.752L19.608 10.008L21.168 9.744L22.992 9.432L23.832 9.816L23.928 10.2L23.592 11.016L21.624 11.496L19.32 11.952L15.888 12.768L15.84 12.792L15.888 12.864L17.424 13.008L18.096 13.056H19.728L22.752 13.272L23.544 13.8L24 14.424L23.928 14.928L22.704 15.528L21.072 15.144L17.232 14.232L15.936 13.92H15.744V14.016L16.848 15.096L18.84 16.896L21.36 19.224L21.48 19.8L21.168 20.28L20.832 20.232L18.624 18.552L17.76 17.808L15.84 16.2H15.72V16.368L16.152 17.016L18.504 20.544L18.624 21.624L18.456 21.96L17.832 22.176L17.184 22.056L15.792 20.136L14.376 17.952L13.224 16.008L13.104 16.104L12.408 23.352L12.096 23.712L11.376 24Z\" fill=\"#d97757\"/></svg>'\n toggleBtn.title = 'Open Claude Code (Cmd/Ctrl + \\`)'\n toggleBtn.setAttribute('data-dev-tool', 'true')\n toggleBtn.addEventListener('click', () => togglePanel(true))\n document.body.appendChild(toggleBtn)\n }\n\n function createInspectBtn() {\n if (inspectBtn) return\n inspectBtn = document.createElement('button')\n inspectBtn.className = 'claude-dev-server-inspect-btn'\n inspectBtn.innerHTML = '<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"20\" height=\"20\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"#fff\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M19 11V4a2 2 0 00-2-2H4a2 2 0 00-2 2v13a2 2 0 002 2h7\"/><path d=\"M12 12l4.166 10 1.48-4.355L22 16.166 12 12z\"/><path d=\"M18 18l3 3\"/></svg>'\n inspectBtn.title = 'Inspect Element (Cmd/Ctrl + Shift + I)'\n inspectBtn.setAttribute('data-dev-tool', 'true')\n inspectBtn.addEventListener('click', () => {\n if (isInspectMode) {\n disableInspectMode()\n } else {\n // 点击 Inspect 时先收起面板\n if (isOpen) {\n togglePanel(false)\n }\n enableInspectMode()\n }\n })\n document.body.appendChild(inspectBtn)\n }\n\n function createOverlay() {\n if (overlay) return\n overlay = document.createElement('div')\n overlay.className = 'claude-dev-server-inspect-overlay'\n document.body.appendChild(overlay)\n }\n\n function highlightElement(el) {\n if (!overlay) return\n overlay.innerHTML = ''\n const rect = el.getBoundingClientRect()\n const highlight = document.createElement('div')\n highlight.className = 'claude-dev-server-highlight'\n highlight.style.top = rect.top + 'px'\n highlight.style.left = rect.left + 'px'\n highlight.style.width = rect.width + 'px'\n highlight.style.height = rect.height + 'px'\n const className = el.className ? String(el.className) : ''\n highlight.dataset.element = el.tagName.toLowerCase() +\n (el.id ? '#' + el.id : '') +\n (className ? '.' + className.split(' ').join('.') : '')\n overlay.appendChild(highlight)\n }\n\n async function getSourceLocation(el) {\n let sourceFile = null\n let sourceLine = 1\n let sourceColumn = 1\n\n // Try to get original line number using source map\n async function getOriginalLine(generatedFile, generatedLine, generatedColumn) {\n try {\n // Find the script tag that corresponds to this file\n const scripts = Array.from(document.querySelectorAll('script[src]'))\n for (const script of scripts) {\n const src = script.getAttribute('src')\n if (src && (src.includes('/src/') || src.includes('/app.') || src.includes('/main.'))) {\n // Try to fetch and parse the source map\n const response = await fetch(src + '.map')\n if (response.ok) {\n const map = await response.json()\n // Use the source map to find the original position\n const consumer = await new (window.SourceMap || window.sourceMap).SourceMapConsumer(map)\n const original = consumer.originalPositionFor({\n line: generatedLine,\n column: generatedColumn\n })\n consumer.destroy()\n if (original.source && original.line !== null) {\n return { line: original.line, column: original.column || 1 }\n }\n }\n }\n }\n } catch (e) {\n console.log('[Claude Dev Server] Source map lookup failed:', e.message)\n }\n return null\n }\n\n // Try React DevTools - handle React 18's randomized suffix\n const elKeys = Object.keys(el)\n let fiberKey = elKeys.find(k => k === '__reactFiber__' || k === '__reactInternalInstance' || k.indexOf('__reactFiber') === 0)\n let propsKey = elKeys.find(k => k === '__reactProps__' || k.indexOf('__reactProps') === 0)\n\n if (fiberKey) {\n const fiber = el[fiberKey]\n console.log('[Claude Dev Server] Found fiber at key:', fiberKey)\n\n // Try to get _debugSource from different locations\n const debugSource = fiber._debugSource || fiber.elementType?._debugSource || fiber.type?._debugSource || fiber.alternate?._debugSource\n if (debugSource && debugSource.fileName) {\n sourceFile = debugSource.fileName\n sourceLine = debugSource.lineNumber || 1\n sourceColumn = debugSource.columnNumber || 1\n\n // Try to use source map to get the original line number\n const original = await getOriginalLine(sourceFile, sourceLine, sourceColumn)\n if (original) {\n sourceLine = original.line\n sourceColumn = original.column\n console.log('[Claude Dev Server] Original line from source map:', sourceLine)\n }\n\n // Convert absolute path to relative path using project root\n if (sourceFile.startsWith('/')) {\n const projectRoot = window.__CLAUDE_PROJECT_ROOT__\n if (projectRoot && sourceFile.startsWith(projectRoot)) {\n sourceFile = sourceFile.substring(projectRoot.length + 1)\n if (sourceFile.startsWith('/')) {\n sourceFile = sourceFile.substring(1)\n }\n }\n }\n console.log('[Claude Dev Server] Found React source:', sourceFile, sourceLine)\n } else {\n // Try going up the fiber tree\n let currentFiber = fiber\n let depth = 0\n while (currentFiber && depth < 20) {\n const ds = currentFiber._debugSource || currentFiber.elementType?._debugSource || currentFiber.type?._debugSource\n if (ds && ds.fileName) {\n sourceFile = ds.fileName\n sourceLine = ds.lineNumber || 1\n sourceColumn = ds.columnNumber || 1\n // Convert absolute path to relative path using project root\n if (sourceFile.startsWith('/')) {\n const projectRoot = window.__CLAUDE_PROJECT_ROOT__\n if (projectRoot && sourceFile.startsWith(projectRoot)) {\n sourceFile = sourceFile.substring(projectRoot.length + 1)\n if (sourceFile.startsWith('/')) {\n sourceFile = sourceFile.substring(1)\n }\n }\n }\n console.log('[Claude Dev Server] Found React source at depth', depth, ':', sourceFile, sourceLine)\n break\n }\n currentFiber = currentFiber.return || currentFiber.alternate\n depth++\n }\n }\n }\n\n // Try Vue component\n if (!sourceFile) {\n const vueComponent = el.__vueParentComponent || el.__vnode\n if (vueComponent) {\n const type = vueComponent.type || vueComponent.component\n if (type && type.__file) {\n sourceFile = type.__file\n console.log('[Claude Dev Server] Found Vue source:', sourceFile)\n }\n }\n }\n\n // Try Vite's HMR source map\n if (!sourceFile) {\n // Look for data-vite-dev-id or similar attributes\n const viteId = el.getAttribute('data-vite-dev-id')\n if (viteId) {\n // Extract file path from viteId (remove query params if any)\n const queryIndex = viteId.indexOf('?')\n sourceFile = '/' + (queryIndex >= 0 ? viteId.substring(0, queryIndex) : viteId)\n console.log('[Claude Dev Server] Found Vite ID:', viteId)\n }\n }\n\n // Check for inline script\n if (!sourceFile) {\n const inlineScripts = document.querySelectorAll('script:not([src])')\n if (inlineScripts.length > 0) {\n const pathParts = window.location.pathname.split('/')\n const fileName = pathParts[pathParts.length - 1] || 'index.html'\n sourceFile = '/' + fileName\n\n const html = document.documentElement.outerHTML\n const elId = el.id ? el.id : ''\n const elClass = el.className ? el.className : ''\n let elPattern\n if (elId) {\n elPattern = 'id=\"' + elId + '\"'\n } else if (elClass) {\n elPattern = 'class=\"' + elClass.split(' ')[0] + '\"'\n } else {\n elPattern = el.tagName.toLowerCase()\n }\n\n const matchIndex = html.indexOf(elPattern)\n if (matchIndex !== -1) {\n const beforeElement = html.substring(0, matchIndex)\n sourceLine = beforeElement.split('\\\\n').length\n }\n }\n }\n\n // Find main script\n if (!sourceFile) {\n const scripts = document.querySelectorAll('script[src]')\n for (const script of scripts) {\n const src = script.getAttribute('src')\n if (src && !src.includes('cdn') && !src.includes('node_modules') &&\n (src.includes('/src/') || src.includes('/app.') || src.includes('/main.'))) {\n sourceFile = src\n break\n }\n }\n }\n\n // Fallback\n if (!sourceFile) {\n const pathParts = window.location.pathname.split('/')\n const fileName = pathParts[pathParts.length - 1] || 'index.html'\n sourceFile = '/' + fileName\n }\n\n const elClassName = el.className ? String(el.className) : ''\n const selector = el.tagName.toLowerCase() +\n (el.id ? '#' + el.id : '') +\n (elClassName ? '.' + elClassName.split(' ').filter(c => c).join('.') : '')\n\n return {\n url: sourceFile,\n line: sourceLine,\n column: sourceColumn,\n selector: selector,\n hint: sourceFile ? 'File: ' + sourceFile : 'Element: ' + selector\n }\n }\n\n function enableInspectMode() {\n isInspectMode = true\n overlay && overlay.classList.add('active')\n document.body.style.cursor = 'crosshair'\n\n // 更新悬浮 inspect 按钮状态\n if (inspectBtn) inspectBtn.classList.add('active')\n\n // 检查元素是否属于 dev tools\n function isDevToolElement(el) {\n if (!el) return false\n // 检查元素本身\n if (el.dataset && el.dataset.devTool === 'true') return true\n // 检查父元素\n let parent = el.parentElement\n while (parent) {\n if (parent.dataset && parent.dataset.devTool === 'true') return true\n parent = parent.parentElement\n }\n return false\n }\n\n const onMouseMove = async (e) => {\n if (!isInspectMode) return\n const el = document.elementFromPoint(e.clientX, e.clientY)\n // 忽略 dev tools 元素和 overlay\n if (el && el !== overlay && !isDevToolElement(el) && (!overlay || !overlay.contains(el))) {\n highlightElement(el)\n }\n }\n\n const onClick = async (e) => {\n if (!isInspectMode) return\n e.preventDefault()\n e.stopPropagation()\n\n const el = document.elementFromPoint(e.clientX, e.clientY)\n // 忽略 dev tools 元素和 overlay\n if (el && el !== overlay && !isDevToolElement(el) && (!overlay || !overlay.contains(el))) {\n const location = await getSourceLocation(el)\n if (location) {\n await inspectElement(location, el)\n }\n disableInspectMode()\n }\n }\n\n document.addEventListener('mousemove', onMouseMove, true)\n document.addEventListener('click', onClick, true)\n\n if (overlay) overlay._inspectHandlers = { onMouseMove, onClick }\n }\n\n function disableInspectMode() {\n isInspectMode = false\n if (overlay) overlay.classList.remove('active')\n document.body.style.cursor = ''\n if (overlay) overlay.innerHTML = ''\n\n // 更新悬浮 inspect 按钮状态\n if (inspectBtn) inspectBtn.classList.remove('active')\n\n const btn = panel && panel.querySelector('.claude-dev-server-btn-inspect')\n if (btn) btn.classList.remove('active')\n\n const handlers = overlay && overlay._inspectHandlers\n if (handlers) {\n document.removeEventListener('mousemove', handlers.onMouseMove, true)\n document.removeEventListener('click', handlers.onClick, true)\n }\n }\n\n async function inspectElement(location, el) {\n // 构建格式化的 prompt\n const filePath = location.url || location.file || 'unknown'\n const tagName = el.tagName ? el.tagName.toLowerCase() : ''\n const id = el.id ? '#' + el.id : ''\n const className = el.className ? '.' + String(el.className).split(' ').filter(c => c).join('.') : ''\n const selector = tagName + id + className\n\n // 获取元素的文本内容(如果是文本节点或短文本元素)\n let textContent = ''\n if (el.nodeType === Node.TEXT_NODE) {\n textContent = el.textContent ? el.textContent.trim().substring(0, 50) : ''\n } else if (el.childNodes.length === 1 && el.childNodes[0].nodeType === Node.TEXT_NODE) {\n textContent = el.childNodes[0].textContent ? el.childNodes[0].textContent.trim().substring(0, 50) : ''\n }\n\n // 格式: @filename:line <selector> \"text content\"\n let prompt = \\`@\\${filePath}:\\${location.line} <\\${selector}>\\`\n if (textContent) {\n prompt += \\` \"\\${textContent}\"\\`\n }\n\n // 发送成功后展开面板\n const sendToTerminal = () => {\n if (!terminalIframe || !terminalIframe.contentWindow) {\n return false\n }\n\n const win = terminalIframe.contentWindow\n\n // Check if sendToTerminal function is available\n if (win.sendToTerminal) {\n win.sendToTerminal(prompt)\n console.log('[Claude Dev Server] Sent via sendToTerminal:', prompt)\n // 发送成功后展开面板\n setTimeout(() => togglePanel(true), 100)\n return true\n }\n\n return false\n }\n\n // Try immediately\n if (sendToTerminal()) {\n return\n }\n\n // If not ready, wait and retry\n let attempts = 0\n const maxAttempts = 50\n const retryInterval = setInterval(() => {\n attempts++\n if (sendToTerminal() || attempts >= maxAttempts) {\n clearInterval(retryInterval)\n if (attempts >= maxAttempts) {\n console.warn('[Claude Dev Server] Could not send to terminal after retries')\n }\n }\n }, 100)\n }\n\n function loadTerminalIframe(ttydUrl) {\n if (!terminalContainer || terminalIframe) return\n ttydWsUrl = ttydUrl\n\n // Create iframe pointing to ttyd/index.html with ttyd WebSocket URL\n terminalIframe = document.createElement('iframe')\n // Pass the ttyd WebSocket URL as query param\n terminalIframe.src = '/ttyd/index.html?ws=' + encodeURIComponent(ttydUrl)\n terminalIframe.allow = 'clipboard-read; clipboard-write'\n\n // Load event - iframe is ready\n terminalIframe.onload = () => {\n console.log('[Claude Dev Server] Terminal iframe loaded')\n }\n\n terminalContainer.appendChild(terminalIframe)\n }\n\n function createPanel() {\n if (panel) return\n\n panel = document.createElement('div')\n panel.className = 'claude-dev-server-panel'\n panel.innerHTML = \\`\n <div class=\"claude-dev-server-header\">\n <span class=\"claude-dev-server-title\">Claude Code</span>\n <div class=\"claude-dev-server-actions\">\n <button class=\"claude-dev-server-btn claude-dev-server-btn-inspect\" title=\"Inspect Element\">\n <svg width=\"12\" height=\"12\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\n <path d=\"M2 12h20M12 2v20M4.93 4.93l14.14 14.14M19.07 4.93L4.93 19.07\"/>\n </svg>\n Inspect\n </button>\n </div>\n <button class=\"claude-dev-server-close\" aria-label=\"Close\" title=\"Minimize to icon\">&times;</button>\n </div>\n <div class=\"claude-dev-server-terminal\"></div>\n \\`\n\n document.body.appendChild(panel)\n\n terminalContainer = panel.querySelector('.claude-dev-server-terminal')\n const closeBtn = panel.querySelector('.claude-dev-server-close')\n const inspectBtn = panel.querySelector('.claude-dev-server-btn-inspect')\n\n closeBtn && closeBtn.addEventListener('click', () => togglePanel(false))\n\n inspectBtn && inspectBtn.addEventListener('click', () => {\n if (isInspectMode) {\n disableInspectMode()\n } else {\n // 点击 Inspect 时先收起面板\n if (isOpen) {\n togglePanel(false)\n }\n enableInspectMode()\n }\n })\n }\n\n function togglePanel(force) {\n createPanel()\n if (typeof force === 'boolean') {\n isOpen = force\n } else {\n isOpen = !isOpen\n }\n\n if (panel) panel.classList.toggle('open', isOpen)\n if (toggleBtn) toggleBtn.classList.toggle('hidden', isOpen)\n\n if (isOpen) {\n // Focus iframe when panel opens\n if (terminalIframe && terminalIframe.contentWindow) {\n setTimeout(() => {\n if (terminalIframe.contentWindow.terminalInstance) {\n terminalIframe.contentWindow.terminalInstance.focus()\n }\n }, 100)\n }\n }\n }\n\n function connect(port) {\n const WS_URL = 'ws://localhost:' + port\n ws = new WebSocket(WS_URL)\n\n ws.onopen = () => {\n console.log('[Claude Dev Server] Connected to control server')\n }\n\n ws.onmessage = (e) => {\n try {\n const msg = JSON.parse(e.data)\n console.log('[Claude Dev Server] Received message:', msg.type, msg)\n if (msg.type === 'ready' && msg.ttydUrl) {\n loadTerminalIframe(msg.ttydUrl)\n } else if (msg.type === 'inspectResult') {\n if (msg.location) {\n showContextPanel(msg.location, null, false)\n }\n }\n } catch (err) {\n console.error('[Claude Dev Server] Message parse error:', err)\n }\n }\n\n ws.onclose = () => {\n console.log('[Claude Dev Server] Control WebSocket disconnected, reconnecting...')\n setTimeout(() => connect(port), 2000)\n }\n\n ws.onerror = (err) => {\n console.error('[Claude Dev Server] Control WebSocket error:', err)\n }\n }\n\n initWhenReady()\n\n document.addEventListener('keydown', (e) => {\n if ((e.metaKey || e.ctrlKey) && e.code === 'Backquote') {\n e.preventDefault()\n togglePanel()\n }\n if (e.key === 'Escape' && isOpen) {\n togglePanel(false)\n }\n if (e.key === 'Escape' && isInspectMode) {\n disableInspectMode()\n }\n })\n})()\n`\n","import { spawn } from 'child_process'\nimport { existsSync, readFileSync } from 'fs'\nimport { join, dirname } from 'path'\nimport http from 'http'\nimport { fileURLToPath } from 'url'\nimport { createProxyMiddleware } from 'http-proxy-middleware'\nimport { WebSocketServer } from 'ws'\nimport { createWebSocketServer } from '../server/websocket.js'\nimport { CLIENT_SCRIPT, CLIENT_STYLES } from '../client/injection.js'\n\nconst __filename = fileURLToPath(import.meta.url)\nconst __dirname = dirname(__filename)\n\nexport interface UniversalServerOptions {\n port?: number\n claudePath?: string\n claudeArgs?: string[]\n cwd?: string\n server?: {\n type?: 'vite' | 'next' | 'webpack' | 'custom'\n command?: string\n port?: number\n }\n}\n\nexport async function startUniversalServer(options: UniversalServerOptions = {}) {\n const cwd = options.cwd || process.cwd()\n const port = options.port || 3000\n\n // 1. 检测项目类型\n const projectType = options.server?.type || detectProjectType(cwd)\n const targetCommand = options.server?.command || getDefaultCommand(projectType)\n\n console.log(`[Claude Dev Server] Detected project type: ${projectType}`)\n console.log(`[Claude Dev Server] Starting target server...`)\n\n // 2. 启动目标 dev server(不预设端口)\n const targetServer = spawnTargetServer(targetCommand, cwd)\n\n // 3. 等待并检测目标服务器实际监听的端口\n console.log(`[Claude Dev Server] Waiting for target server to start (PID: ${targetServer.pid})...`)\n const targetPort = await detectServerPort(targetServer, 30000)\n\n if (!targetPort) {\n throw new Error('Failed to detect target server port. Please check if the dev server started successfully.')\n }\n\n console.log(`[Claude Dev Server] Target server is running on port ${targetPort}`)\n\n // 4. 启动控制 WebSocket 服务器(ttyd + Claude)\n const wsServer = createWebSocketServer({\n port: 0, // 自动分配端口\n projectRoot: cwd,\n claudePath: options.claudePath || 'claude',\n claudeArgs: options.claudeArgs || [],\n })\n\n const { wsPort, ttydPort } = await wsServer.start()\n console.log(`[Claude Dev Server] Control server running on ws://localhost:${wsPort}`)\n console.log(`[Claude Dev Server] ttyd running on ws://localhost:${ttydPort}`)\n\n // 5. 启动 HTTP 代理服务器\n const proxyServer = createProxyServer(targetPort, wsPort, cwd)\n proxyServer.listen(port)\n console.log(`[Claude Dev Server] Proxy server running on http://localhost:${port}`)\n console.log(`\\n🚀 Ready! Open http://localhost:${port} in your browser`)\n\n // 清理处理\n const cleanup = () => {\n console.log('[Claude Dev Server] Shutting down...')\n targetServer.kill()\n wsServer.stop()\n proxyServer.close()\n process.exit(0)\n }\n\n process.on('SIGINT', cleanup)\n process.on('SIGTERM', cleanup)\n\n return { proxyServer, targetServer, wsServer }\n}\n\nfunction detectProjectType(cwd: string): 'vite' | 'next' | 'webpack' | 'custom' {\n if (existsSync(join(cwd, 'vite.config.ts')) || existsSync(join(cwd, 'vite.config.js'))) {\n return 'vite'\n }\n if (existsSync(join(cwd, 'next.config.js')) || existsSync(join(cwd, 'next.config.mjs'))) {\n return 'next'\n }\n if (existsSync(join(cwd, 'webpack.config.js')) || existsSync(join(cwd, 'webpack.config.ts'))) {\n return 'webpack'\n }\n return 'custom'\n}\n\nfunction getDefaultCommand(projectType: string): string {\n switch (projectType) {\n case 'vite': return 'npm run dev'\n case 'next': return 'npm run dev'\n case 'webpack': return 'npm run dev'\n default: return 'npm run dev'\n }\n}\n\nfunction spawnTargetServer(command: string, cwd: string) {\n const [cmd, ...args] = command.split(' ')\n const child = spawn(cmd, args, {\n cwd,\n stdio: 'pipe',\n env: process.env,\n })\n\n // Forward child's stdout/stderr to parent so user can see dev server output\n child.stdout?.pipe(process.stdout)\n child.stderr?.pipe(process.stderr)\n\n return child\n}\n\n/**\n * Detect the port that the dev server is listening on by parsing stdout.\n * Most dev servers announce their port on startup like: \"Local: http://localhost:5173\"\n */\nasync function detectServerPort(childProcess: any, timeout: number): Promise<number | null> {\n return new Promise((resolve) => {\n const timeoutId = setTimeout(() => {\n cleanup()\n resolve(null)\n }, timeout)\n\n let stdout = ''\n\n const onData = (chunk: Buffer) => {\n stdout += chunk.toString()\n\n // Match common port announcement patterns:\n // Vite: \"Local: http://localhost:5173/\"\n // Next.js: \"Local: http://localhost:3000\"\n // Webpack: \"http://localhost:8080/\"\n const patterns = [\n /localhost:(\\d{4,5})/,\n /127\\.0\\.0\\.1:(\\d{4,5})/,\n /0\\.0\\.0\\.0:(\\d{4,5})/,\n ]\n\n for (const pattern of patterns) {\n const match = stdout.match(pattern)\n if (match) {\n const port = parseInt(match[1], 10)\n console.log(`[Claude Dev Server] Detected port from stdout: ${port}`)\n cleanup()\n resolve(port)\n return\n }\n }\n }\n\n const cleanup = () => {\n clearTimeout(timeoutId)\n childProcess.stdout?.off('data', onData)\n }\n\n childProcess.stdout?.on('data', onData)\n })\n}\n\nfunction createProxyServer(targetPort: number, wsPort: number, projectRoot: string) {\n // 读取 ttyd 资源文件\n // 从当前模块路径计算 assets 目录\n // moduleDir 是 /Users/lloyd/moox/claude-dev-server/dist\n // 所以需要 assets 是 /Users/lloyd/moox/claude-dev-server/dist/assets\n const moduleDir = dirname(fileURLToPath(import.meta.url))\n const assetsPath = join(moduleDir, 'assets')\n\n let ttydHtml: string\n let ttydBridgeJs: string\n\n try {\n ttydHtml = readFileSync(join(assetsPath, 'ttyd-terminal.html'), 'utf-8')\n ttydBridgeJs = readFileSync(join(assetsPath, 'ttyd-bridge.js'), 'utf-8')\n } catch (e: any) {\n console.error('[Claude Dev Server] Failed to read ttyd assets from', assetsPath)\n console.error('[Claude Dev Server] moduleDir:', moduleDir)\n console.error('[Claude Dev Server] Error:', e.message)\n throw new Error('ttyd assets not found. Please run `npm run build` first.')\n }\n\n return http.createServer((req, res) => {\n // 处理 @claude-port 端点\n if (req.url === '/@claude-port') {\n res.setHeader('Content-Type', 'application/json')\n res.end(JSON.stringify({ port: wsPort }))\n return\n }\n\n // 处理 /ttyd/* 路由\n if (req.url?.startsWith('/ttyd/')) {\n const urlPath = req.url.split('?')[0] // 移除查询参数\n\n if (urlPath === '/ttyd/index.html' || urlPath === '/ttyd/') {\n res.setHeader('Content-Type', 'text/html')\n res.end(ttydHtml)\n return\n }\n\n if (urlPath === '/ttyd/ttyd-bridge.js') {\n res.setHeader('Content-Type', 'application/javascript')\n res.end(ttydBridgeJs)\n return\n }\n\n // 其他 /ttyd/* 路由返回 404\n res.statusCode = 404\n res.end('Not found')\n return\n }\n\n // 代理到目标服务器并注入脚本\n const options = {\n hostname: 'localhost',\n port: targetPort,\n path: req.url,\n method: req.method,\n headers: req.headers,\n }\n\n const proxyReq = http.request(options, (proxyRes) => {\n // 注入脚本到 HTML\n if (proxyRes.headers['content-type']?.includes('text/html')) {\n const body: Buffer[] = []\n proxyRes.on('data', (chunk) => body.push(chunk))\n proxyRes.on('end', () => {\n const html = Buffer.concat(body).toString('utf8')\n const injected = injectScripts(html, wsPort, projectRoot)\n const statusCode = proxyRes.statusCode || 200\n res.writeHead(statusCode, {\n ...proxyRes.headers,\n 'content-length': Buffer.byteLength(injected),\n })\n res.end(injected)\n })\n } else {\n const statusCode = proxyRes.statusCode || 200\n res.writeHead(statusCode, proxyRes.headers)\n proxyRes.pipe(res)\n }\n })\n\n proxyReq.on('error', (err) => {\n console.error('[Claude Dev Server] Proxy error:', err)\n res.statusCode = 502\n res.end('Bad Gateway')\n })\n\n req.pipe(proxyReq)\n })\n}\n\nfunction injectScripts(html: string, wsPort: number, projectRoot: string): string {\n const projectRootScript = `<script>window.__CLAUDE_PROJECT_ROOT__ = ${JSON.stringify(projectRoot)}</script>`\n\n return html.replace(\n '</head>',\n `<!-- Claude Dev Server -->\\n${CLIENT_STYLES}\\n${projectRootScript}\\n<script type=\"module\">${CLIENT_SCRIPT}</script>\\n</head>`\n ).replace(\n /wsPort:\\s*\\d+/,\n `wsPort: ${wsPort}`\n )\n}\n"]}