claude-dev-server 1.2.2 → 1.2.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,667 @@
1
+ import { spawn } from 'child_process';
2
+ import { existsSync, readFileSync } from 'fs';
3
+ import { dirname, join, relative, resolve } from 'path';
4
+ import http from 'http';
5
+ import { fileURLToPath } from 'url';
6
+ import { createConnection } from 'net';
7
+ import { SourceMapConsumer } from 'source-map';
8
+ import { WebSocketServer } from 'ws';
9
+ import { readFile } from 'fs/promises';
10
+
11
+ // src/universal/index.ts
12
+ function spawnClaudeCode(options) {
13
+ const port = 5e4 + Math.floor(Math.random() * 1e4);
14
+ const ttydProc = spawn("ttyd", [
15
+ "--port",
16
+ String(port),
17
+ "--interface",
18
+ "127.0.0.1",
19
+ "--writable",
20
+ options.claudePath,
21
+ ...options.args
22
+ ], {
23
+ cwd: options.cwd,
24
+ env: {
25
+ ...process.env,
26
+ ...options.env,
27
+ TERM: "xterm-256color",
28
+ FORCE_COLOR: "1"
29
+ }
30
+ });
31
+ ttydProc.on("exit", (code) => {
32
+ console.log(`[claude-dev-server] ttyd exited - code: ${code}`);
33
+ });
34
+ ttydProc.on("error", (err) => {
35
+ console.error(`[claude-dev-server] ttyd error: ${err.message}`);
36
+ });
37
+ return new Promise((resolve2, reject) => {
38
+ ttydProc.stdout?.on("data", (data) => {
39
+ const msg = data.toString();
40
+ console.log(`[ttyd] ${msg}`);
41
+ });
42
+ ttydProc.stderr?.on("data", (data) => {
43
+ const msg = data.toString();
44
+ console.error(`[ttyd stderr] ${msg}`);
45
+ });
46
+ setTimeout(() => {
47
+ resolve2({
48
+ wsUrl: `ws://127.0.0.1:${port}`,
49
+ process: ttydProc,
50
+ port
51
+ });
52
+ }, 500);
53
+ ttydProc.on("error", reject);
54
+ });
55
+ }
56
+ var sourceMapCache = /* @__PURE__ */ new Map();
57
+ async function parseSourceMap(sourceMapUrl, content) {
58
+ try {
59
+ const consumer = await new SourceMapConsumer(content);
60
+ sourceMapCache.set(sourceMapUrl, consumer);
61
+ } catch (err) {
62
+ console.error("Failed to parse source map:", err);
63
+ }
64
+ }
65
+ async function findOriginalPosition(generatedFile, line, column) {
66
+ const consumer = sourceMapCache.get(generatedFile);
67
+ if (!consumer) {
68
+ return null;
69
+ }
70
+ try {
71
+ const position = consumer.originalPositionFor({ line, column });
72
+ if (position.source) {
73
+ return {
74
+ source: position.source,
75
+ line: position.line || 1,
76
+ column: position.column || 0,
77
+ name: position.name
78
+ };
79
+ }
80
+ } catch (err) {
81
+ console.error("Failed to find original position:", err);
82
+ }
83
+ return null;
84
+ }
85
+ function formatCodeContext(location) {
86
+ return `
87
+ \u{1F4CD} Code Location:
88
+ File: ${location.file}
89
+ Line: ${location.line}
90
+ Column: ${location.column}
91
+ `;
92
+ }
93
+ function createWebSocketServer(options) {
94
+ let wss = null;
95
+ let port = 0;
96
+ let ttydInfo = null;
97
+ const start = async () => {
98
+ console.log("[claude-dev-server] Starting ttyd...");
99
+ ttydInfo = await spawnClaudeCode({
100
+ cwd: options.projectRoot,
101
+ claudePath: options.claudePath,
102
+ args: options.claudeArgs
103
+ });
104
+ console.log(`[claude-dev-server] ttyd running at ${ttydInfo.wsUrl}`);
105
+ return new Promise((resolve2, reject) => {
106
+ wss = new WebSocketServer({ port, host: "127.0.0.1" });
107
+ wss.on("listening", () => {
108
+ const address = wss.address();
109
+ if (address && typeof address === "object") {
110
+ port = address.port;
111
+ resolve2({ wsPort: port, ttydPort: ttydInfo.port });
112
+ }
113
+ });
114
+ wss.on("error", (err) => {
115
+ reject(err);
116
+ });
117
+ wss.on("connection", (ws) => {
118
+ ws.on("message", async (message) => {
119
+ try {
120
+ const msg = JSON.parse(message.toString());
121
+ if (msg.type === "inspect") {
122
+ await handleInspect(msg, ws, options.projectRoot);
123
+ } else if (msg.type === "loadSourceMap") {
124
+ await handleLoadSourceMap(msg, ws, options.projectRoot);
125
+ }
126
+ } catch (err) {
127
+ }
128
+ });
129
+ ws.send(JSON.stringify({
130
+ type: "ready",
131
+ ttydUrl: ttydInfo.wsUrl
132
+ }));
133
+ });
134
+ });
135
+ };
136
+ const stop = () => {
137
+ ttydInfo?.process.kill();
138
+ wss?.close();
139
+ };
140
+ return { start, stop };
141
+ }
142
+ async function handleLoadSourceMap(msg, ws, projectRoot) {
143
+ const { sourceMapUrl } = msg;
144
+ try {
145
+ const mapPath = resolve(projectRoot, sourceMapUrl.replace(/^\//, ""));
146
+ const content = await readFile(mapPath, "utf-8");
147
+ await parseSourceMap(sourceMapUrl, content);
148
+ ws.send(JSON.stringify({
149
+ type: "sourceMapLoaded",
150
+ sourceMapUrl,
151
+ success: true
152
+ }));
153
+ } catch (err) {
154
+ ws.send(JSON.stringify({
155
+ type: "sourceMapLoaded",
156
+ sourceMapUrl,
157
+ success: false,
158
+ error: err.message
159
+ }));
160
+ }
161
+ }
162
+ async function handleInspect(msg, ws, projectRoot) {
163
+ const { url, line, column, sourceMapUrl } = msg;
164
+ let location = null;
165
+ if (sourceMapUrl) {
166
+ const original = await findOriginalPosition(sourceMapUrl, line, column);
167
+ if (original) {
168
+ location = {
169
+ file: resolve(projectRoot, original.source),
170
+ line: original.line,
171
+ column: original.column
172
+ };
173
+ }
174
+ }
175
+ if (!location) {
176
+ const match = url.match(/\/@fs\/(.+?)(?:\?|$)|\/@vite\/(.+?)(?:\?|$)/);
177
+ if (match) {
178
+ location = {
179
+ file: decodeURIComponent(match[1] || match[2]),
180
+ line,
181
+ column
182
+ };
183
+ }
184
+ }
185
+ ws.send(JSON.stringify({
186
+ type: "inspectResult",
187
+ location: location ? {
188
+ file: location.file,
189
+ line: location.line,
190
+ column: location.column,
191
+ context: formatCodeContext(location)
192
+ } : null
193
+ }));
194
+ }
195
+
196
+ // src/universal/index.ts
197
+ var __filename$1 = fileURLToPath(import.meta.url);
198
+ dirname(__filename$1);
199
+ async function startUniversalServer(options = {}) {
200
+ const cwd = options.cwd || process.cwd();
201
+ const port = options.port || 3e3;
202
+ const projectType = options.server?.type || detectProjectType(cwd);
203
+ const targetCommand = options.server?.command || getDefaultCommand(projectType);
204
+ console.log(`[Claude Dev Server] Detected project type: ${projectType}`);
205
+ console.log(`[Claude Dev Server] Starting target server...`);
206
+ const targetServer = spawnTargetServer(targetCommand, cwd);
207
+ console.log(`[Claude Dev Server] Waiting for target server to start (PID: ${targetServer.pid})...`);
208
+ const targetPort = await detectServerPort(targetServer, 3e4);
209
+ if (!targetPort) {
210
+ throw new Error("Failed to detect target server port. Please check if the dev server started successfully.");
211
+ }
212
+ console.log(`[Claude Dev Server] Target server is running on port ${targetPort}`);
213
+ const wsServer = createWebSocketServer({
214
+ port: 0,
215
+ // 自动分配端口
216
+ projectRoot: cwd,
217
+ claudePath: options.claudePath || "claude",
218
+ claudeArgs: options.claudeArgs || []
219
+ });
220
+ const { wsPort, ttydPort } = await wsServer.start();
221
+ console.log(`[Claude Dev Server] Control server running on ws://localhost:${wsPort}`);
222
+ console.log(`[Claude Dev Server] ttyd running on ws://localhost:${ttydPort}`);
223
+ const proxyServer = createProxyServer(targetPort, wsPort, cwd);
224
+ proxyServer.listen(port);
225
+ console.log(`[Claude Dev Server] Proxy server running on http://localhost:${port}`);
226
+ console.log(`
227
+ \u{1F680} Ready! Open http://localhost:${port} in your browser`);
228
+ const cleanup = () => {
229
+ console.log("[Claude Dev Server] Shutting down...");
230
+ targetServer.kill();
231
+ wsServer.stop();
232
+ proxyServer.close();
233
+ process.exit(0);
234
+ };
235
+ process.on("SIGINT", cleanup);
236
+ process.on("SIGTERM", cleanup);
237
+ return { proxyServer, targetServer, wsServer };
238
+ }
239
+ function detectProjectType(cwd) {
240
+ if (existsSync(join(cwd, "vite.config.ts")) || existsSync(join(cwd, "vite.config.js"))) {
241
+ return "vite";
242
+ }
243
+ if (existsSync(join(cwd, "next.config.js")) || existsSync(join(cwd, "next.config.mjs"))) {
244
+ return "next";
245
+ }
246
+ if (existsSync(join(cwd, "webpack.config.js")) || existsSync(join(cwd, "webpack.config.ts"))) {
247
+ return "webpack";
248
+ }
249
+ return "custom";
250
+ }
251
+ function getDefaultCommand(projectType) {
252
+ switch (projectType) {
253
+ case "vite":
254
+ return "npm run dev";
255
+ case "next":
256
+ return "npm run dev";
257
+ case "webpack":
258
+ return "npm run dev";
259
+ default:
260
+ return "npm run dev";
261
+ }
262
+ }
263
+ function spawnTargetServer(command, cwd) {
264
+ const [cmd, ...args] = command.split(" ");
265
+ const child = spawn(cmd, args, {
266
+ cwd,
267
+ stdio: "pipe",
268
+ env: process.env
269
+ });
270
+ child.stdout?.pipe(process.stdout);
271
+ child.stderr?.pipe(process.stderr);
272
+ return child;
273
+ }
274
+ async function detectServerPort(childProcess, timeout) {
275
+ return new Promise((resolve2) => {
276
+ const timeoutId = setTimeout(() => {
277
+ cleanup();
278
+ resolve2(null);
279
+ }, timeout);
280
+ let stdout = "";
281
+ const onData = (chunk) => {
282
+ stdout += chunk.toString();
283
+ const patterns = [
284
+ /localhost:(\d{4,5})/,
285
+ /127\.0\.0\.1:(\d{4,5})/,
286
+ /0\.0\.0\.0:(\d{4,5})/
287
+ ];
288
+ for (const pattern of patterns) {
289
+ const match = stdout.match(pattern);
290
+ if (match) {
291
+ const port = parseInt(match[1], 10);
292
+ console.log(`[Claude Dev Server] Detected port from stdout: ${port}`);
293
+ cleanup();
294
+ resolve2(port);
295
+ return;
296
+ }
297
+ }
298
+ };
299
+ const cleanup = () => {
300
+ clearTimeout(timeoutId);
301
+ childProcess.stdout?.off("data", onData);
302
+ };
303
+ childProcess.stdout?.on("data", onData);
304
+ });
305
+ }
306
+ async function handleSourceMapRequest(projectRoot, filePath, line, column, targetPort) {
307
+ try {
308
+ let content;
309
+ let fullPath = filePath;
310
+ if (filePath.startsWith("/") && targetPort) {
311
+ try {
312
+ const response = await fetch(`http://localhost:${targetPort}${filePath}`);
313
+ if (!response.ok) {
314
+ console.log("[Claude Dev Server] Failed to fetch from Dev Server:", response.status);
315
+ return { error: "Failed to fetch from Dev Server" };
316
+ }
317
+ content = await response.text();
318
+ console.log("[Claude Dev Server] Fetched", content.length, "chars from Dev Server");
319
+ } catch (e) {
320
+ console.log("[Claude Dev Server] Fetch error:", e);
321
+ return { error: "Fetch error: " + e.message };
322
+ }
323
+ } else {
324
+ if (!filePath.startsWith("/")) {
325
+ fullPath = join(projectRoot, filePath);
326
+ }
327
+ if (!existsSync(fullPath)) {
328
+ console.log("[Claude Dev Server] File not found:", fullPath);
329
+ return { error: "File not found" };
330
+ }
331
+ content = readFileSync(fullPath, "utf-8");
332
+ console.log("[Claude Dev Server] Resolving source map for:", fullPath, "at line:", line);
333
+ }
334
+ let sourceMapUrl = null;
335
+ const patterns = [
336
+ /\/\/[@#]\s*sourceMappingURL=([^\s]+)/,
337
+ /\/\*[@#]\s*sourceMappingURL=([^\s]+)\s*\*\//
338
+ ];
339
+ for (const pattern of patterns) {
340
+ const match = content.match(pattern);
341
+ if (match) {
342
+ sourceMapUrl = match[1];
343
+ console.log("[Claude Dev Server] Found sourceMappingURL:", sourceMapUrl.substring(0, 100) + "...");
344
+ break;
345
+ }
346
+ }
347
+ if (!sourceMapUrl) {
348
+ console.log("[Claude Dev Server] No source map found in:", fullPath);
349
+ return { file: relative(projectRoot, fullPath), line, column };
350
+ }
351
+ let sourceMapContent;
352
+ let sourceMap;
353
+ if (sourceMapUrl.startsWith("data:application/json;base64,") || sourceMapUrl.startsWith("data:application/json;charset=utf-8;base64,")) {
354
+ console.log("[Claude Dev Server] Found inline source map");
355
+ const base64Data = sourceMapUrl.split(",", 2)[1];
356
+ sourceMapContent = Buffer.from(base64Data, "base64").toString("utf-8");
357
+ try {
358
+ sourceMap = JSON.parse(sourceMapContent);
359
+ } catch (e) {
360
+ console.log("[Claude Dev Server] Failed to parse inline source map:", e);
361
+ return { file: relative(projectRoot, fullPath), line, column };
362
+ }
363
+ } else if (sourceMapUrl.startsWith("http://") || sourceMapUrl.startsWith("https://")) {
364
+ console.log("[Claude Dev Server] Remote source map not supported:", sourceMapUrl);
365
+ return { file: relative(projectRoot, fullPath), line, column };
366
+ } else {
367
+ let sourceMapPath;
368
+ if (sourceMapUrl.startsWith("/")) {
369
+ sourceMapPath = sourceMapUrl;
370
+ } else {
371
+ sourceMapPath = join(dirname(fullPath), sourceMapUrl);
372
+ }
373
+ console.log("[Claude Dev Server] Reading external source map:", sourceMapPath);
374
+ if (!existsSync(sourceMapPath)) {
375
+ console.log("[Claude Dev Server] Source map file not found:", sourceMapPath);
376
+ return { file: relative(projectRoot, fullPath), line, column };
377
+ }
378
+ sourceMapContent = readFileSync(sourceMapPath, "utf-8");
379
+ sourceMap = JSON.parse(sourceMapContent);
380
+ }
381
+ const consumer = await new SourceMapConsumer(sourceMap);
382
+ const original = consumer.originalPositionFor({
383
+ line,
384
+ column
385
+ });
386
+ consumer.destroy();
387
+ if (original.source && original.line !== null) {
388
+ let originalFile = original.source;
389
+ if (originalFile.startsWith("webpack://")) {
390
+ originalFile = originalFile.replace(/^webpack:\/\/[\/\\]?/, "");
391
+ }
392
+ if (!originalFile.startsWith("/")) {
393
+ const possiblePath = join(projectRoot, originalFile);
394
+ if (existsSync(possiblePath)) {
395
+ } else {
396
+ const fileName = originalFile.split("/").pop() || originalFile.split("\\").pop() || originalFile;
397
+ if (fileName === originalFile) {
398
+ if (filePath.startsWith("/")) {
399
+ originalFile = filePath.substring(1);
400
+ console.log("[Claude Dev Server] Source is just a filename, using filePath:", originalFile);
401
+ } else {
402
+ originalFile = relative(projectRoot, fullPath);
403
+ }
404
+ } else {
405
+ originalFile = relative(projectRoot, possiblePath);
406
+ }
407
+ }
408
+ }
409
+ return {
410
+ file: originalFile,
411
+ line: original.line,
412
+ column: original.column || 1,
413
+ original: {
414
+ source: original.source,
415
+ line: original.line,
416
+ column: original.column,
417
+ name: original.name
418
+ }
419
+ };
420
+ }
421
+ return { file: relative(projectRoot, fullPath), line, column };
422
+ } catch (err) {
423
+ console.error("[Claude Dev Server] Source map resolution error:", err);
424
+ return { error: String(err) };
425
+ }
426
+ }
427
+ async function handleTurbopackLookup(projectRoot, pagePath, targetPort) {
428
+ try {
429
+ console.log("[Claude Dev Server] Turbopack lookup for page:", pagePath);
430
+ const pageRes = await fetch(`http://localhost:${targetPort}${pagePath}`);
431
+ if (!pageRes.ok) {
432
+ return { error: "Failed to fetch page" };
433
+ }
434
+ const html = await pageRes.text();
435
+ const chunkUrls = [];
436
+ const scriptMatches = html.matchAll(/<script[^>]*src="([^"]*\/_next\/static\/chunks\/[^"]*)"/g);
437
+ for (const match of scriptMatches) {
438
+ if (match[1]) {
439
+ chunkUrls.push(match[1]);
440
+ }
441
+ }
442
+ console.log("[Claude Dev Server] Found", chunkUrls.length, "chunk URLs");
443
+ for (const chunkUrl of chunkUrls) {
444
+ try {
445
+ const fullUrl = chunkUrl.startsWith("http") ? chunkUrl : `http://localhost:${targetPort}${chunkUrl}`;
446
+ const chunkRes = await fetch(fullUrl);
447
+ if (!chunkRes.ok) continue;
448
+ const chunkContent = await chunkRes.text();
449
+ const modulePathRegex = /\[project\]([^\s"]+\.(tsx?|jsx?))/g;
450
+ const matches = [...chunkContent.matchAll(modulePathRegex)];
451
+ for (const match of matches) {
452
+ if (match[1]) {
453
+ const sourcePath = match[1];
454
+ let relativePath = sourcePath.replace(/^\[project\]/, "");
455
+ const normalizedPagePath = pagePath.replace(/^\/[^/]+/, "");
456
+ if (relativePath.toLowerCase().includes(normalizedPagePath.toLowerCase()) || relativePath.toLowerCase().includes("login")) {
457
+ console.log("[Claude Dev Server] Found source file:", relativePath);
458
+ return {
459
+ file: relativePath,
460
+ line: void 0
461
+ // Turbopack doesn't provide line numbers
462
+ };
463
+ }
464
+ }
465
+ }
466
+ } catch (e) {
467
+ console.log("[Claude Dev Server] Error fetching chunk:", chunkUrl, e);
468
+ }
469
+ }
470
+ return { error: "Source file not found for page: " + pagePath };
471
+ } catch (err) {
472
+ console.error("[Claude Dev Server] Turbopack lookup error:", err);
473
+ return { error: String(err) };
474
+ }
475
+ }
476
+ function isHtmlPageRequest(req) {
477
+ const accept = req.headers.accept || "";
478
+ const url = req.url || "";
479
+ if (!accept.includes("text/html")) {
480
+ return false;
481
+ }
482
+ if (url.startsWith("/@") || url.startsWith("/_next/") || url.startsWith("/ttyd") || url.startsWith("/dev.html")) {
483
+ return false;
484
+ }
485
+ return true;
486
+ }
487
+ function createProxyServer(targetPort, wsPort, projectRoot) {
488
+ const moduleDir = dirname(fileURLToPath(import.meta.url));
489
+ const assetsPath = join(moduleDir, "assets");
490
+ let ttydHtml;
491
+ let ttydBridgeJs;
492
+ let devHtml;
493
+ try {
494
+ ttydHtml = readFileSync(join(assetsPath, "ttyd-terminal.html"), "utf-8");
495
+ ttydBridgeJs = readFileSync(join(assetsPath, "ttyd-bridge.js"), "utf-8");
496
+ devHtml = readFileSync(join(assetsPath, "dev.html"), "utf-8");
497
+ } catch (e) {
498
+ console.error("[Claude Dev Server] Failed to read assets from", assetsPath);
499
+ console.error("[Claude Dev Server] moduleDir:", moduleDir);
500
+ console.error("[Claude Dev Server] Error:", e.message);
501
+ throw new Error("Assets not found. Please run `npm run build` first.");
502
+ }
503
+ const server = http.createServer((req, res) => {
504
+ const referer = req.headers.referer || "";
505
+ const isFromDevPage = referer.includes("dev.html");
506
+ if (req.url?.startsWith("/dev.html")) {
507
+ const urlParams = new URL(req.url || "", `http://${req.headers.host}`);
508
+ const originalPath = urlParams.searchParams.get("path") || "/";
509
+ const host = req.headers.host || "localhost:3000";
510
+ const origin = `http://${host}`;
511
+ const modifiedDevHtml = devHtml.replace(
512
+ /__CLAUDE_IFRAME_SRC__/g,
513
+ `${origin}${originalPath}`
514
+ ).replace(
515
+ /__CLAUDE_ORIGINAL_PATH__/g,
516
+ originalPath
517
+ );
518
+ res.setHeader("Content-Type", "text/html");
519
+ res.end(modifiedDevHtml);
520
+ return;
521
+ }
522
+ if (!isFromDevPage && isHtmlPageRequest(req)) {
523
+ const currentPath = req.url || "/";
524
+ const devPageUrl = `/dev.html?path=${encodeURIComponent(currentPath)}`;
525
+ res.writeHead(302, { "Location": devPageUrl });
526
+ res.end();
527
+ return;
528
+ }
529
+ if (req.url === "/@claude-port") {
530
+ res.setHeader("Content-Type", "application/json");
531
+ res.end(JSON.stringify({ port: wsPort }));
532
+ return;
533
+ }
534
+ if (req.url?.startsWith("/@sourcemap?")) {
535
+ const url = new URL(req.url, `http://localhost:${wsPort}`);
536
+ const file = url.searchParams.get("file");
537
+ const line = url.searchParams.get("line");
538
+ const column = url.searchParams.get("col");
539
+ if (file && line && column) {
540
+ handleSourceMapRequest(projectRoot, file, parseInt(line), parseInt(column || "1"), targetPort).then((result) => {
541
+ res.setHeader("Content-Type", "application/json");
542
+ res.end(JSON.stringify(result));
543
+ }).catch((err) => {
544
+ console.error("[Claude Dev Server] Source map error:", err);
545
+ res.setHeader("Content-Type", "application/json");
546
+ res.end(JSON.stringify({ error: err.message }));
547
+ });
548
+ return;
549
+ }
550
+ }
551
+ if (req.url?.startsWith("/@sourcemap-lookup?")) {
552
+ const url = new URL(req.url, `http://localhost:${wsPort}`);
553
+ const page = url.searchParams.get("page");
554
+ const framework = url.searchParams.get("framework");
555
+ if (page && framework === "nextjs") {
556
+ handleTurbopackLookup(projectRoot, page, targetPort).then((result) => {
557
+ res.setHeader("Content-Type", "application/json");
558
+ res.end(JSON.stringify(result));
559
+ }).catch((err) => {
560
+ console.error("[Claude Dev Server] Turbopack lookup error:", err);
561
+ res.setHeader("Content-Type", "application/json");
562
+ res.end(JSON.stringify({ error: err.message }));
563
+ });
564
+ return;
565
+ }
566
+ }
567
+ if (req.url?.startsWith("/ttyd/")) {
568
+ const urlPath = req.url.split("?")[0];
569
+ if (urlPath === "/ttyd/index.html" || urlPath === "/ttyd/") {
570
+ res.setHeader("Content-Type", "text/html");
571
+ res.end(ttydHtml);
572
+ return;
573
+ }
574
+ if (urlPath === "/ttyd/ttyd-bridge.js") {
575
+ res.setHeader("Content-Type", "application/javascript");
576
+ res.end(ttydBridgeJs);
577
+ return;
578
+ }
579
+ if (urlPath === "/ttyd/token" || urlPath === "/ttyd/index.html/token") {
580
+ res.setHeader("Content-Type", "application/json");
581
+ res.end(JSON.stringify({ token: "" }));
582
+ return;
583
+ }
584
+ res.statusCode = 404;
585
+ res.end("Not found");
586
+ return;
587
+ }
588
+ const proxyHeaders = { ...req.headers };
589
+ delete proxyHeaders["accept-encoding"];
590
+ const options = {
591
+ hostname: "localhost",
592
+ port: targetPort,
593
+ path: req.url,
594
+ method: req.method,
595
+ headers: proxyHeaders
596
+ };
597
+ const proxyReq = http.request(options, (proxyRes) => {
598
+ const statusCode = proxyRes.statusCode || 200;
599
+ res.writeHead(statusCode, proxyRes.headers);
600
+ proxyRes.pipe(res);
601
+ });
602
+ proxyReq.on("error", (err) => {
603
+ console.error("[Claude Dev Server] Proxy error:", err);
604
+ res.statusCode = 502;
605
+ res.end("Bad Gateway");
606
+ });
607
+ req.pipe(proxyReq);
608
+ });
609
+ server.on("upgrade", (req, socket, head) => {
610
+ if (req.headers["upgrade"]?.toLowerCase() !== "websocket") {
611
+ return;
612
+ }
613
+ console.log("[Claude Dev Server] WebSocket upgrade request:", req.url);
614
+ const targetSocket = createConnection(targetPort, "localhost", () => {
615
+ console.log("[Claude Dev Server] Connected to target WebSocket server");
616
+ const upgradeRequest = [
617
+ `${req.method} ${req.url} HTTP/1.1`,
618
+ `Host: localhost:${targetPort}`,
619
+ "Upgrade: websocket",
620
+ "Connection: Upgrade",
621
+ `Sec-WebSocket-Key: ${req.headers["sec-websocket-key"]}`,
622
+ `Sec-WebSocket-Version: ${req.headers["sec-websocket-version"] || "13"}`
623
+ ];
624
+ if (req.headers["sec-websocket-protocol"]) {
625
+ upgradeRequest.push(`Sec-WebSocket-Protocol: ${req.headers["sec-websocket-protocol"]}`);
626
+ }
627
+ if (req.headers["sec-websocket-extensions"]) {
628
+ upgradeRequest.push(`Sec-WebSocket-Extensions: ${req.headers["sec-websocket-extensions"]}`);
629
+ }
630
+ targetSocket.write(upgradeRequest.join("\r\n") + "\r\n\r\n");
631
+ if (head && head.length > 0) {
632
+ targetSocket.write(head);
633
+ }
634
+ });
635
+ targetSocket.on("data", (data) => {
636
+ if (socket.writable) {
637
+ socket.write(data);
638
+ }
639
+ });
640
+ socket.on("data", (data) => {
641
+ if (targetSocket.writable) {
642
+ targetSocket.write(data);
643
+ }
644
+ });
645
+ socket.on("close", () => {
646
+ console.log("[Claude Dev Server] Client WebSocket closed");
647
+ targetSocket.destroy();
648
+ });
649
+ targetSocket.on("close", () => {
650
+ console.log("[Claude Dev Server] Target WebSocket closed");
651
+ socket.end();
652
+ });
653
+ socket.on("error", (err) => {
654
+ console.error("[Claude Dev Server] Client socket error:", err.message);
655
+ targetSocket.destroy();
656
+ });
657
+ targetSocket.on("error", (err) => {
658
+ console.error("[Claude Dev Server] Target socket error:", err.message);
659
+ socket.end();
660
+ });
661
+ });
662
+ return server;
663
+ }
664
+
665
+ export { startUniversalServer };
666
+ //# sourceMappingURL=chunk-EEXRIRSF.js.map
667
+ //# sourceMappingURL=chunk-EEXRIRSF.js.map