next-ai-editor 0.2.6 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (97) hide show
  1. package/README.md +2 -2
  2. package/dist/index.cjs +2108 -34
  3. package/dist/index.cjs.map +1 -1
  4. package/dist/index.d.ts +187 -4
  5. package/dist/index.js +2108 -34
  6. package/dist/index.js.map +1 -1
  7. package/dist/next-ai-editor.css +489 -112
  8. package/package.json +4 -22
  9. package/client/package.json +0 -5
  10. package/dist/AIEditorProvider-CLgf1Vwa.cjs +0 -1488
  11. package/dist/AIEditorProvider-CLgf1Vwa.cjs.map +0 -1
  12. package/dist/AIEditorProvider-DWId5Qmv.js +0 -1489
  13. package/dist/AIEditorProvider-DWId5Qmv.js.map +0 -1
  14. package/dist/client/AIEditorProvider.d.ts +0 -21
  15. package/dist/client/AIEditorProvider.d.ts.map +0 -1
  16. package/dist/client/components/ChatPanel.d.ts +0 -18
  17. package/dist/client/components/ChatPanel.d.ts.map +0 -1
  18. package/dist/client/components/ControlPill.d.ts +0 -8
  19. package/dist/client/components/ControlPill.d.ts.map +0 -1
  20. package/dist/client/components/MessageItem.d.ts +0 -7
  21. package/dist/client/components/MessageItem.d.ts.map +0 -1
  22. package/dist/client/components/MessageList.d.ts +0 -7
  23. package/dist/client/components/MessageList.d.ts.map +0 -1
  24. package/dist/client/components/TaskHistoryPanel.d.ts +0 -10
  25. package/dist/client/components/TaskHistoryPanel.d.ts.map +0 -1
  26. package/dist/client/components/index.d.ts +0 -11
  27. package/dist/client/components/index.d.ts.map +0 -1
  28. package/dist/client/fiber-utils.d.ts +0 -35
  29. package/dist/client/fiber-utils.d.ts.map +0 -1
  30. package/dist/client/hooks/index.d.ts +0 -3
  31. package/dist/client/hooks/index.d.ts.map +0 -1
  32. package/dist/client/hooks/useChatStream.d.ts +0 -66
  33. package/dist/client/hooks/useChatStream.d.ts.map +0 -1
  34. package/dist/client/hooks/useHotReload.d.ts +0 -10
  35. package/dist/client/hooks/useHotReload.d.ts.map +0 -1
  36. package/dist/client/index.d.ts +0 -6
  37. package/dist/client/index.d.ts.map +0 -1
  38. package/dist/client/query-params.d.ts +0 -9
  39. package/dist/client/query-params.d.ts.map +0 -1
  40. package/dist/client.cjs +0 -12
  41. package/dist/client.cjs.map +0 -1
  42. package/dist/client.js +0 -12
  43. package/dist/client.js.map +0 -1
  44. package/dist/comments-BQ6AQ-eC.cjs +0 -1570
  45. package/dist/comments-BQ6AQ-eC.cjs.map +0 -1
  46. package/dist/comments-CjBQxjDP.js +0 -1553
  47. package/dist/comments-CjBQxjDP.js.map +0 -1
  48. package/dist/index.d.ts.map +0 -1
  49. package/dist/path-utils-Bai2xKx9.js +0 -36
  50. package/dist/path-utils-Bai2xKx9.js.map +0 -1
  51. package/dist/path-utils-DYzEWUGy.cjs +0 -35
  52. package/dist/path-utils-DYzEWUGy.cjs.map +0 -1
  53. package/dist/server/agent/sdk-client.d.ts +0 -54
  54. package/dist/server/agent/sdk-client.d.ts.map +0 -1
  55. package/dist/server/agent/session-store.d.ts +0 -101
  56. package/dist/server/agent/session-store.d.ts.map +0 -1
  57. package/dist/server/handlers/absolute-path.d.ts +0 -3
  58. package/dist/server/handlers/absolute-path.d.ts.map +0 -1
  59. package/dist/server/handlers/chat.d.ts +0 -6
  60. package/dist/server/handlers/chat.d.ts.map +0 -1
  61. package/dist/server/handlers/comments.d.ts +0 -10
  62. package/dist/server/handlers/comments.d.ts.map +0 -1
  63. package/dist/server/handlers/config.d.ts +0 -7
  64. package/dist/server/handlers/config.d.ts.map +0 -1
  65. package/dist/server/handlers/index.d.ts +0 -18
  66. package/dist/server/handlers/index.d.ts.map +0 -1
  67. package/dist/server/handlers/pages-router.d.ts +0 -21
  68. package/dist/server/handlers/pages-router.d.ts.map +0 -1
  69. package/dist/server/handlers/read.d.ts +0 -3
  70. package/dist/server/handlers/read.d.ts.map +0 -1
  71. package/dist/server/handlers/resolve.d.ts +0 -3
  72. package/dist/server/handlers/resolve.d.ts.map +0 -1
  73. package/dist/server/handlers/undo.d.ts +0 -3
  74. package/dist/server/handlers/undo.d.ts.map +0 -1
  75. package/dist/server/handlers/validate-session.d.ts +0 -3
  76. package/dist/server/handlers/validate-session.d.ts.map +0 -1
  77. package/dist/server/index.d.ts +0 -12
  78. package/dist/server/index.d.ts.map +0 -1
  79. package/dist/server/utils/ast.d.ts +0 -49
  80. package/dist/server/utils/ast.d.ts.map +0 -1
  81. package/dist/server/utils/file-system.d.ts +0 -24
  82. package/dist/server/utils/file-system.d.ts.map +0 -1
  83. package/dist/server/utils/source-map.d.ts +0 -39
  84. package/dist/server/utils/source-map.d.ts.map +0 -1
  85. package/dist/server.cjs +0 -29
  86. package/dist/server.cjs.map +0 -1
  87. package/dist/server.js +0 -29
  88. package/dist/server.js.map +0 -1
  89. package/dist/shared/comment-types.d.ts +0 -140
  90. package/dist/shared/comment-types.d.ts.map +0 -1
  91. package/dist/shared/path-utils.d.ts +0 -24
  92. package/dist/shared/path-utils.d.ts.map +0 -1
  93. package/dist/shared/storage.d.ts +0 -53
  94. package/dist/shared/storage.d.ts.map +0 -1
  95. package/dist/shared/types.d.ts +0 -74
  96. package/dist/shared/types.d.ts.map +0 -1
  97. package/server/package.json +0 -5
@@ -1,1553 +0,0 @@
1
- import { NextResponse } from "next/server";
2
- import fs, { readFile, writeFile, mkdir } from "fs/promises";
3
- import path, { join } from "path";
4
- import { SourceMapConsumer } from "@jridgewell/source-map";
5
- import { c as cleanPath, s as shouldSkipPath, n as normalizeSourcePath } from "./path-utils-Bai2xKx9.js";
6
- import * as parser from "@babel/parser";
7
- import traverse from "@babel/traverse";
8
- import * as t from "@babel/types";
9
- import crypto from "crypto";
10
- import { unstable_v2_resumeSession, unstable_v2_createSession } from "@anthropic-ai/claude-agent-sdk";
11
- import { existsSync } from "fs";
12
- function validateDevMode() {
13
- if (process.env.NODE_ENV !== "development") {
14
- return NextResponse.json(
15
- { success: false, error: "Development mode only" },
16
- { status: 403 }
17
- );
18
- }
19
- return null;
20
- }
21
- function normalizePath(filePath) {
22
- return filePath.replace(/^webpack-internal:\/\/\/\([^)]+\)\/\.?/, "").replace(/^webpack:\/\/[^/]*\//, "").replace(/^\.\//, "").replace(/\?.*$/, "");
23
- }
24
- async function resolveFilePath(projectRoot, normalizedPath) {
25
- const candidates = [
26
- path.resolve(projectRoot, normalizedPath),
27
- path.resolve(projectRoot, "src", normalizedPath),
28
- path.resolve(projectRoot, "app", normalizedPath)
29
- ];
30
- for (const candidate of candidates) {
31
- if (!candidate.startsWith(projectRoot)) continue;
32
- try {
33
- await fs.access(candidate);
34
- return candidate;
35
- } catch {
36
- }
37
- }
38
- return null;
39
- }
40
- function isPathSecure(absolutePath, projectRoot) {
41
- return absolutePath.startsWith(projectRoot);
42
- }
43
- async function fileExists$1(filePath) {
44
- try {
45
- await fs.access(filePath);
46
- return true;
47
- } catch {
48
- return false;
49
- }
50
- }
51
- function parseDebugStack(stack) {
52
- if (!stack) return null;
53
- const stackStr = typeof stack === "string" ? stack : stack.stack || String(stack);
54
- const frames = stackStr.split("\n");
55
- const skipPatterns = [
56
- "node_modules",
57
- "SegmentViewNode",
58
- "LayoutRouter",
59
- "ErrorBoundary",
60
- "fakeJSXCallSite"
61
- ];
62
- for (const frame of frames) {
63
- if (skipPatterns.some((p) => frame.includes(p))) continue;
64
- const match = frame.match(/at\s+(\w+)\s+\((.+?):(\d+):(\d+)\)?$/);
65
- if (match) {
66
- let filePath = match[2];
67
- const line = parseInt(match[3], 10);
68
- const column = parseInt(match[4], 10);
69
- let chunkId;
70
- const chunkMatch = filePath.match(/\?([^:]+)$/);
71
- if (chunkMatch) {
72
- chunkId = chunkMatch[1];
73
- filePath = filePath.replace(/\?[^:]*$/, "");
74
- }
75
- filePath = cleanPath(filePath);
76
- if (!shouldSkipPath(filePath)) {
77
- console.log("parseDebugStack extracted:", { filePath, line, column });
78
- return { filePath, line, column, chunkId };
79
- }
80
- }
81
- }
82
- console.log(
83
- "parseDebugStack: no valid frame found in stack:",
84
- stackStr.substring(0, 200)
85
- );
86
- return null;
87
- }
88
- function extractComponentNameFromStack(stack) {
89
- if (!stack) return null;
90
- const stackStr = typeof stack === "string" ? stack : stack.stack || String(stack);
91
- const frames = stackStr.split("\n");
92
- const skipPatterns = [
93
- "node_modules",
94
- "SegmentViewNode",
95
- "LayoutRouter",
96
- "ErrorBoundary",
97
- "fakeJSXCallSite",
98
- "react_stack_bottom_frame"
99
- ];
100
- for (const frame of frames) {
101
- if (skipPatterns.some((p) => frame.includes(p))) continue;
102
- const match = frame.match(/at\s+(\w+)\s+\(/);
103
- if (match && match[1]) {
104
- const componentName = match[1];
105
- if (componentName !== "Object" && componentName !== "anonymous") {
106
- return componentName;
107
- }
108
- }
109
- }
110
- return null;
111
- }
112
- function parseDebugStackFrames(stack) {
113
- if (!stack) return [];
114
- const stackStr = typeof stack === "string" ? stack : stack.stack || String(stack);
115
- const frames = stackStr.split("\n");
116
- const skipPatterns = [
117
- "node_modules",
118
- "SegmentViewNode",
119
- "LayoutRouter",
120
- "ErrorBoundary",
121
- "fakeJSXCallSite",
122
- "react_stack_bottom_frame"
123
- ];
124
- const positions = [];
125
- for (const frame of frames) {
126
- if (skipPatterns.some((p) => frame.includes(p))) continue;
127
- const match = frame.match(/at\s+(\w+)\s+\((.+?):(\d+):(\d+)\)?$/);
128
- if (match) {
129
- match[1];
130
- let filePath = match[2];
131
- const line = parseInt(match[3], 10);
132
- const column = parseInt(match[4], 10);
133
- let chunkId;
134
- const chunkMatch = filePath.match(/\?([^:]+)$/);
135
- if (chunkMatch) {
136
- chunkId = chunkMatch[1];
137
- filePath = filePath.replace(/\?[^:]*$/, "");
138
- }
139
- filePath = cleanPath(filePath);
140
- if (!shouldSkipPath(filePath)) {
141
- positions.push({ filePath, line, column, chunkId });
142
- if (positions.length >= 2) break;
143
- }
144
- }
145
- }
146
- console.log(`parseDebugStackFrames extracted ${positions.length} frames:`, positions);
147
- return positions;
148
- }
149
- async function resolveOriginalPosition(compiledPos, projectRoot) {
150
- try {
151
- console.log("resolveOriginalPosition called with:", compiledPos);
152
- let compiledFilePath = compiledPos.filePath;
153
- compiledFilePath = cleanPath(compiledFilePath);
154
- console.log("After cleanPath:", compiledFilePath);
155
- if (compiledFilePath.startsWith("http://") || compiledFilePath.startsWith("https://")) {
156
- const url = new URL(compiledFilePath);
157
- const pathname = url.pathname;
158
- if (pathname.startsWith("/_next/")) {
159
- const relativePath = pathname.substring("/_next/".length);
160
- compiledFilePath = path.join(projectRoot, ".next", "dev", relativePath);
161
- } else {
162
- console.warn("Unexpected HTTP URL path:", pathname);
163
- return null;
164
- }
165
- } else if (!path.isAbsolute(compiledFilePath)) {
166
- if (compiledFilePath.startsWith(".next/")) {
167
- compiledFilePath = path.resolve(projectRoot, compiledFilePath);
168
- } else {
169
- const resolved = path.resolve(projectRoot, compiledFilePath);
170
- if (await fileExists(resolved)) {
171
- compiledFilePath = resolved;
172
- } else {
173
- const possiblePaths = [
174
- path.join(projectRoot, ".next", "dev", compiledFilePath),
175
- path.join(projectRoot, compiledFilePath)
176
- ];
177
- for (const tryPath of possiblePaths) {
178
- if (await fileExists(tryPath)) {
179
- compiledFilePath = tryPath;
180
- break;
181
- }
182
- }
183
- }
184
- }
185
- } else {
186
- compiledFilePath = path.normalize(compiledFilePath);
187
- }
188
- console.log("Normalized compiled file path:", compiledFilePath);
189
- const compiledExists = await fileExists(compiledFilePath);
190
- console.log(
191
- "Compiled file exists:",
192
- compiledExists,
193
- "at:",
194
- compiledFilePath
195
- );
196
- if (!compiledExists) {
197
- console.error(
198
- `Compiled file not found: ${compiledFilePath} (from parsed: ${compiledPos.filePath})`
199
- );
200
- }
201
- const sourceMapPath = compiledFilePath + ".map";
202
- console.log("Looking for source map at:", sourceMapPath);
203
- const sourceMapExists = await fileExists(sourceMapPath);
204
- console.log("Source map exists:", sourceMapExists);
205
- if (!sourceMapExists) {
206
- console.error(
207
- `Source map not found: ${sourceMapPath} (from compiled: ${compiledPos.filePath}, normalized: ${compiledFilePath})`
208
- );
209
- const altPaths = [
210
- compiledFilePath.replace(/\.js$/, ".map"),
211
- path.join(
212
- path.dirname(compiledFilePath),
213
- path.basename(compiledFilePath) + ".map"
214
- )
215
- ];
216
- console.log("Trying alternative paths:", altPaths);
217
- for (const altPath of altPaths) {
218
- if (await fileExists(altPath)) {
219
- console.log("Found source map at alternative path:", altPath);
220
- return await resolveFromSourceMap(altPath, compiledPos, projectRoot);
221
- }
222
- }
223
- return null;
224
- }
225
- return await resolveFromSourceMap(sourceMapPath, compiledPos, projectRoot);
226
- } catch (error) {
227
- console.error("Error resolving source map:", error);
228
- return null;
229
- }
230
- }
231
- async function resolveFromSourceMap(sourceMapPath, compiledPos, projectRoot) {
232
- try {
233
- const sourceMapContent = await fs.readFile(sourceMapPath, "utf-8");
234
- const sourceMap = JSON.parse(sourceMapContent);
235
- if ((!sourceMap.sources || sourceMap.sources.length === 0) && (!sourceMap.sections || sourceMap.sections.length === 0)) {
236
- console.warn(
237
- "Empty source map detected, cannot resolve position:",
238
- sourceMapPath
239
- );
240
- return null;
241
- }
242
- if (sourceMap.sections) {
243
- let matchingSection = null;
244
- for (let i = sourceMap.sections.length - 1; i >= 0; i--) {
245
- const section = sourceMap.sections[i];
246
- const offset = section.offset;
247
- const sectionStartLine1Indexed = offset.line + 1;
248
- if (compiledPos.line > sectionStartLine1Indexed || compiledPos.line === sectionStartLine1Indexed && compiledPos.column >= offset.column) {
249
- matchingSection = section;
250
- break;
251
- }
252
- }
253
- if (matchingSection && matchingSection.map) {
254
- const sectionMap = matchingSection.map;
255
- const offset = matchingSection.offset;
256
- let consumer2;
257
- try {
258
- consumer2 = await new SourceMapConsumer(sectionMap);
259
- } catch (error) {
260
- console.error("Error creating SourceMapConsumer:", error);
261
- return null;
262
- }
263
- try {
264
- const adjustedLine = compiledPos.line - offset.line;
265
- const adjustedColumn = compiledPos.line === offset.line + 1 ? compiledPos.column - offset.column : compiledPos.column;
266
- const originalPos = consumer2.originalPositionFor({
267
- line: adjustedLine,
268
- column: adjustedColumn
269
- });
270
- if (originalPos.source && originalPos.line !== null) {
271
- const source = normalizeSourcePath(
272
- originalPos.source || "",
273
- projectRoot
274
- );
275
- return {
276
- source,
277
- line: originalPos.line,
278
- column: originalPos.column ?? 0
279
- };
280
- }
281
- } finally {
282
- consumer2.destroy();
283
- }
284
- }
285
- return null;
286
- }
287
- const consumer = await new SourceMapConsumer(sourceMap);
288
- try {
289
- const originalPos = consumer.originalPositionFor({
290
- line: compiledPos.line,
291
- column: compiledPos.column
292
- });
293
- if (originalPos.source && originalPos.line !== null) {
294
- const source = normalizeSourcePath(
295
- originalPos.source || "",
296
- projectRoot
297
- );
298
- return {
299
- source,
300
- line: originalPos.line,
301
- column: originalPos.column ?? 0
302
- };
303
- }
304
- } finally {
305
- consumer.destroy();
306
- }
307
- return null;
308
- } catch (error) {
309
- console.error("Error resolving source map:", error);
310
- return null;
311
- }
312
- }
313
- async function fileExists(filePath) {
314
- try {
315
- await fs.access(filePath);
316
- return true;
317
- } catch {
318
- return false;
319
- }
320
- }
321
- async function getOriginalPositionFromDebugStack(debugStack, projectRoot) {
322
- const compiledPos = parseDebugStack(debugStack);
323
- if (!compiledPos) return null;
324
- return await resolveOriginalPosition(compiledPos, projectRoot);
325
- }
326
- function parseFile(content) {
327
- try {
328
- return parser.parse(content, {
329
- sourceType: "module",
330
- plugins: [
331
- "jsx",
332
- "typescript",
333
- "decorators-legacy",
334
- "classProperties",
335
- "optionalChaining",
336
- "nullishCoalescingOperator"
337
- ]
338
- });
339
- } catch (e) {
340
- console.error("Parse error:", e);
341
- return null;
342
- }
343
- }
344
- function extractComponentName(ast) {
345
- let componentName = null;
346
- traverse(ast, {
347
- ExportDefaultDeclaration(path2) {
348
- var _a;
349
- if (t.isFunctionDeclaration(path2.node.declaration)) {
350
- componentName = ((_a = path2.node.declaration.id) == null ? void 0 : _a.name) || null;
351
- } else if (t.isArrowFunctionExpression(path2.node.declaration)) {
352
- componentName = "default";
353
- } else if (t.isIdentifier(path2.node.declaration)) {
354
- componentName = path2.node.declaration.name;
355
- }
356
- },
357
- ExportNamedDeclaration(path2) {
358
- var _a;
359
- if (t.isFunctionDeclaration(path2.node.declaration)) {
360
- componentName = ((_a = path2.node.declaration.id) == null ? void 0 : _a.name) || null;
361
- } else if (t.isVariableDeclaration(path2.node.declaration)) {
362
- const declarator = path2.node.declaration.declarations[0];
363
- if (t.isIdentifier(declarator.id)) {
364
- componentName = declarator.id.name;
365
- }
366
- } else if (path2.node.specifiers && path2.node.specifiers.length > 0) {
367
- const specifier = path2.node.specifiers[0];
368
- if (t.isExportSpecifier(specifier) && t.isIdentifier(specifier.exported)) {
369
- componentName = specifier.exported.name;
370
- }
371
- }
372
- }
373
- });
374
- return componentName;
375
- }
376
- function validateGeneratedCode(newCode, originalCode, fileContent) {
377
- try {
378
- const isFullComponent = /^(export\s+)?(default\s+)?function\s+\w+/.test(newCode.trim()) || /^(export\s+)?(default\s+)?const\s+\w+\s*=/.test(newCode.trim());
379
- if (isFullComponent) {
380
- let codeToValidate = newCode;
381
- if (fileContent) {
382
- const interfaceMatches = fileContent.match(
383
- /^(interface|type)\s+\w+[^}]*\}/gm
384
- );
385
- if (interfaceMatches) {
386
- codeToValidate = interfaceMatches.join("\n\n") + "\n\n" + newCode;
387
- }
388
- }
389
- parser.parse(codeToValidate, {
390
- sourceType: "module",
391
- plugins: ["jsx", "typescript"]
392
- });
393
- } else {
394
- const wrapped = `function _() { return (${newCode}); }`;
395
- parser.parse(wrapped, {
396
- sourceType: "module",
397
- plugins: ["jsx", "typescript"]
398
- });
399
- }
400
- } catch (e) {
401
- console.error("Generated code parse error:", e);
402
- return false;
403
- }
404
- const origBraces = (originalCode.match(/[{}]/g) || []).length;
405
- const newBraces = (newCode.match(/[{}]/g) || []).length;
406
- const origTags = (originalCode.match(/[<>]/g) || []).length;
407
- const newTags = (newCode.match(/[<>]/g) || []).length;
408
- if (Math.abs(origBraces - newBraces) > 4 || Math.abs(origTags - newTags) > 4) {
409
- console.warn(
410
- `Structure changed significantly: braces ${origBraces}->${newBraces}, tags ${origTags}->${newTags}`
411
- );
412
- }
413
- return true;
414
- }
415
- function findTargetElement(ast, fileContent, options) {
416
- const { componentName, lineNumber, elementContext } = options;
417
- let componentNode = null;
418
- let componentStart = 0;
419
- let componentEnd = Infinity;
420
- let fallbackExportDefault = null;
421
- traverse(ast, {
422
- FunctionDeclaration(path2) {
423
- var _a, _b, _c;
424
- if (((_a = path2.node.id) == null ? void 0 : _a.name) === componentName) {
425
- componentNode = path2.node;
426
- componentStart = ((_b = path2.node.loc) == null ? void 0 : _b.start.line) || 0;
427
- componentEnd = ((_c = path2.node.loc) == null ? void 0 : _c.end.line) || Infinity;
428
- }
429
- },
430
- VariableDeclarator(path2) {
431
- var _a, _b;
432
- if (t.isIdentifier(path2.node.id) && path2.node.id.name === componentName) {
433
- componentNode = path2.node;
434
- const parent = path2.parentPath.parent;
435
- componentStart = ((_a = parent == null ? void 0 : parent.loc) == null ? void 0 : _a.start.line) || 0;
436
- componentEnd = ((_b = parent == null ? void 0 : parent.loc) == null ? void 0 : _b.end.line) || Infinity;
437
- }
438
- },
439
- ExportDefaultDeclaration(path2) {
440
- var _a, _b, _c, _d, _e;
441
- if (t.isFunctionDeclaration(path2.node.declaration)) {
442
- const funcName = (_a = path2.node.declaration.id) == null ? void 0 : _a.name;
443
- if (funcName === componentName || !funcName) {
444
- componentNode = path2.node;
445
- componentStart = ((_b = path2.node.loc) == null ? void 0 : _b.start.line) || 0;
446
- componentEnd = ((_c = path2.node.loc) == null ? void 0 : _c.end.line) || Infinity;
447
- } else if (!componentNode) {
448
- fallbackExportDefault = {
449
- node: path2.node,
450
- start: ((_d = path2.node.loc) == null ? void 0 : _d.start.line) || 0,
451
- end: ((_e = path2.node.loc) == null ? void 0 : _e.end.line) || Infinity
452
- };
453
- }
454
- }
455
- }
456
- });
457
- if (!componentNode && fallbackExportDefault !== null) {
458
- const fallback = fallbackExportDefault;
459
- console.log(
460
- `⚠️ Component "${componentName}" not found, using export default function as fallback`
461
- );
462
- componentNode = fallback.node;
463
- componentStart = fallback.start;
464
- componentEnd = fallback.end;
465
- }
466
- const allElementsByTag = /* @__PURE__ */ new Map();
467
- const elementsAtLine = [];
468
- traverse(ast, {
469
- JSXElement(path2) {
470
- const loc = path2.node.loc;
471
- if (!loc) return;
472
- const startLine = loc.start.line;
473
- const endLine = loc.end.line;
474
- if (startLine < componentStart || endLine > componentEnd) return;
475
- const opening = path2.node.openingElement;
476
- if (t.isJSXIdentifier(opening.name)) {
477
- const tagName = opening.name.name;
478
- if (!allElementsByTag.has(tagName)) {
479
- allElementsByTag.set(tagName, []);
480
- }
481
- allElementsByTag.get(tagName).push({ node: path2.node, startLine, endLine, score: 0 });
482
- }
483
- if (startLine === lineNumber) {
484
- elementsAtLine.push({ node: path2.node, startLine, endLine, score: 0 });
485
- }
486
- }
487
- });
488
- if (elementsAtLine.length > 0) {
489
- if (elementsAtLine.length === 1) {
490
- const target = elementsAtLine[0];
491
- return {
492
- startLine: target.startLine,
493
- endLine: target.endLine,
494
- componentStart,
495
- componentEnd
496
- };
497
- }
498
- if (elementContext) {
499
- for (const elem of elementsAtLine) {
500
- if (t.isJSXElement(elem.node)) {
501
- const score = scoreElementMatch(elem.node, elementContext, fileContent);
502
- elem.score = score;
503
- }
504
- }
505
- elementsAtLine.sort((a, b) => b.score - a.score);
506
- if (elementsAtLine[0].score > 0) {
507
- return {
508
- startLine: elementsAtLine[0].startLine,
509
- endLine: elementsAtLine[0].endLine,
510
- componentStart,
511
- componentEnd
512
- };
513
- }
514
- }
515
- return {
516
- startLine: elementsAtLine[0].startLine,
517
- endLine: elementsAtLine[0].endLine,
518
- componentStart,
519
- componentEnd
520
- };
521
- }
522
- if (elementContext == null ? void 0 : elementContext.tagName) {
523
- const allOfTag = allElementsByTag.get(elementContext.tagName);
524
- if (allOfTag && allOfTag.length > 0) {
525
- if (elementContext.textContent || elementContext.className) {
526
- for (const elem of allOfTag) {
527
- if (t.isJSXElement(elem.node)) {
528
- elem.score = scoreElementMatch(elem.node, elementContext, fileContent);
529
- }
530
- }
531
- allOfTag.sort((a, b) => b.score - a.score);
532
- if (allOfTag[0].score > 50) {
533
- return {
534
- startLine: allOfTag[0].startLine,
535
- endLine: allOfTag[0].endLine,
536
- componentStart,
537
- componentEnd
538
- };
539
- }
540
- }
541
- if (elementContext.nthOfType && allOfTag.length >= elementContext.nthOfType) {
542
- const target = allOfTag[elementContext.nthOfType - 1];
543
- return {
544
- startLine: target.startLine,
545
- endLine: target.endLine,
546
- componentStart,
547
- componentEnd
548
- };
549
- }
550
- }
551
- }
552
- const nearbyElements = [];
553
- traverse(ast, {
554
- JSXElement(path2) {
555
- const loc = path2.node.loc;
556
- if (!loc) return;
557
- const startLine = loc.start.line;
558
- const endLine = loc.end.line;
559
- if (startLine < componentStart || endLine > componentEnd) return;
560
- if (Math.abs(startLine - lineNumber) <= 5) {
561
- const score = elementContext ? scoreElementMatch(path2.node, elementContext, fileContent) : 100 - Math.abs(startLine - lineNumber);
562
- nearbyElements.push({ node: path2.node, startLine, endLine, score });
563
- }
564
- }
565
- });
566
- if (nearbyElements.length > 0) {
567
- nearbyElements.sort((a, b) => b.score - a.score);
568
- return {
569
- startLine: nearbyElements[0].startLine,
570
- endLine: nearbyElements[0].endLine,
571
- componentStart,
572
- componentEnd
573
- };
574
- }
575
- if (componentNode && componentStart > 0) {
576
- return {
577
- startLine: componentStart,
578
- endLine: componentEnd,
579
- componentStart,
580
- componentEnd
581
- };
582
- }
583
- return null;
584
- }
585
- function scoreElementMatch(node, context, fileContent) {
586
- let score = 0;
587
- const opening = node.openingElement;
588
- if (t.isJSXIdentifier(opening.name)) {
589
- if (opening.name.name === context.tagName) {
590
- score += 50;
591
- } else {
592
- return 0;
593
- }
594
- } else if (t.isJSXMemberExpression(opening.name)) {
595
- const fullName = getJSXMemberName(opening.name);
596
- if (fullName === context.tagName || fullName.endsWith(`.${context.tagName}`)) {
597
- score += 50;
598
- } else {
599
- return 0;
600
- }
601
- }
602
- if (context.className) {
603
- const classAttr = opening.attributes.find(
604
- (attr) => t.isJSXAttribute(attr) && t.isJSXIdentifier(attr.name) && attr.name.name === "className"
605
- );
606
- if (classAttr && t.isJSXAttribute(classAttr)) {
607
- const classValue = getAttributeValue(classAttr);
608
- if (classValue && context.className.split(/\s+/).some((c) => classValue.includes(c))) {
609
- score += 20;
610
- }
611
- }
612
- }
613
- if (context.textContent && node.loc) {
614
- const elementCode = fileContent.split("\n").slice(node.loc.start.line - 1, node.loc.end.line).join("\n");
615
- const normalizedContent = context.textContent.toLowerCase().trim();
616
- const normalizedElement = elementCode.toLowerCase();
617
- if (normalizedElement.includes(normalizedContent)) {
618
- score += 30;
619
- }
620
- }
621
- if (context.props) {
622
- for (const [key, value] of Object.entries(context.props)) {
623
- if (key.startsWith("_")) continue;
624
- const attr = opening.attributes.find(
625
- (a) => t.isJSXAttribute(a) && t.isJSXIdentifier(a.name) && a.name.name === key
626
- );
627
- if (attr) {
628
- score += 5;
629
- if (typeof value === "string" && t.isJSXAttribute(attr)) {
630
- const attrValue = getAttributeValue(attr);
631
- if (attrValue === value) score += 10;
632
- }
633
- }
634
- }
635
- }
636
- return score;
637
- }
638
- function getJSXMemberName(node) {
639
- if (t.isJSXIdentifier(node.object)) {
640
- return `${node.object.name}.${node.property.name}`;
641
- }
642
- if (t.isJSXMemberExpression(node.object)) {
643
- return `${getJSXMemberName(node.object)}.${node.property.name}`;
644
- }
645
- return node.property.name;
646
- }
647
- function getAttributeValue(attr) {
648
- if (!attr.value) return null;
649
- if (t.isStringLiteral(attr.value)) return attr.value.value;
650
- if (t.isJSXExpressionContainer(attr.value) && t.isStringLiteral(attr.value.expression)) {
651
- return attr.value.expression.value;
652
- }
653
- return null;
654
- }
655
- async function handleRead(req) {
656
- const devModeError = validateDevMode();
657
- if (devModeError) return devModeError;
658
- const { searchParams } = new URL(req.url);
659
- let filePath = searchParams.get("path") || "";
660
- let lineNumber = parseInt(searchParams.get("line") || "1");
661
- const debugStack = searchParams.get("debugStack") || "";
662
- const tagName = searchParams.get("tagName") || "";
663
- const nthOfType = parseInt(searchParams.get("nthOfType") || "0");
664
- const textContent = searchParams.get("textContent") || "";
665
- const className = searchParams.get("className") || "";
666
- const elementContext = tagName ? {
667
- tagName,
668
- nthOfType: nthOfType > 0 ? nthOfType : void 0,
669
- textContent: textContent || void 0,
670
- className: className || void 0
671
- } : void 0;
672
- const parentFilePath = searchParams.get("parentFilePath") || "";
673
- const parentLine = parseInt(searchParams.get("parentLine") || "0");
674
- const parentComponentName = searchParams.get("parentComponentName") || "";
675
- const parentDebugStack = searchParams.get("parentDebugStack") || "";
676
- const childKey = searchParams.get("childKey") || "";
677
- const projectRoot = process.cwd();
678
- if (debugStack) {
679
- const compiledPos = parseDebugStack(debugStack);
680
- if (compiledPos) {
681
- const originalPos = await resolveOriginalPosition(
682
- compiledPos,
683
- projectRoot
684
- );
685
- if (originalPos) {
686
- filePath = originalPos.source;
687
- lineNumber = originalPos.line;
688
- }
689
- }
690
- }
691
- const normalizedPath = normalizePath(filePath);
692
- const absolutePath = await resolveFilePath(projectRoot, normalizedPath);
693
- if (!absolutePath) {
694
- return NextResponse.json(
695
- { success: false, error: "File not found" },
696
- { status: 404 }
697
- );
698
- }
699
- const content = await fs.readFile(absolutePath, "utf-8");
700
- const ast = parseFile(content);
701
- if (!ast) {
702
- return NextResponse.json(
703
- { success: false, error: "Failed to parse file" },
704
- { status: 400 }
705
- );
706
- }
707
- const componentName = extractComponentName(ast);
708
- if (!componentName) {
709
- return NextResponse.json({
710
- success: true,
711
- content,
712
- lineStart: 1,
713
- lineEnd: content.split("\n").length
714
- });
715
- }
716
- const target = findTargetElement(ast, content, {
717
- componentName,
718
- lineNumber,
719
- elementContext
720
- });
721
- if (!target) {
722
- return NextResponse.json({
723
- success: true,
724
- content,
725
- lineStart: 1,
726
- lineEnd: content.split("\n").length
727
- });
728
- }
729
- const lines = content.split("\n");
730
- const componentLines = lines.slice(
731
- target.componentStart - 1,
732
- target.componentEnd
733
- );
734
- const preview = componentLines.join("\n");
735
- let parentInstance = null;
736
- if (parentDebugStack) {
737
- try {
738
- let resolvedParentPath = parentFilePath;
739
- let resolvedParentLine = parentLine;
740
- let resolvedParentComponentName = parentComponentName;
741
- if (!resolvedParentComponentName && parentDebugStack) {
742
- resolvedParentComponentName = extractComponentNameFromStack(parentDebugStack) || "";
743
- }
744
- if (parentDebugStack) {
745
- const compiledPos = parseDebugStack(parentDebugStack);
746
- if (compiledPos) {
747
- const originalPos = await resolveOriginalPosition(
748
- compiledPos,
749
- projectRoot
750
- );
751
- if (originalPos) {
752
- resolvedParentPath = originalPos.source;
753
- resolvedParentLine = originalPos.line;
754
- }
755
- }
756
- }
757
- const normalizedParentPath = normalizePath(resolvedParentPath);
758
- const absoluteParentPath = await resolveFilePath(
759
- projectRoot,
760
- normalizedParentPath
761
- );
762
- if (absoluteParentPath && resolvedParentComponentName) {
763
- const parentContent = await fs.readFile(absoluteParentPath, "utf-8");
764
- const parentAst = parseFile(parentContent);
765
- if (parentAst) {
766
- let nthOfType2 = void 0;
767
- if (childKey) {
768
- const keyAsNumber = parseInt(childKey, 10);
769
- if (!isNaN(keyAsNumber)) {
770
- nthOfType2 = keyAsNumber + 1;
771
- }
772
- }
773
- const parentTarget = findTargetElement(parentAst, parentContent, {
774
- componentName: resolvedParentComponentName,
775
- lineNumber: 0,
776
- // Don't use line number - rely on element context to find correct instance
777
- elementContext: {
778
- tagName: componentName,
779
- // Search for child component usage
780
- nthOfType: nthOfType2,
781
- // Find specific instance if key is numeric
782
- textContent: textContent || void 0
783
- // Use text content to match the specific instance
784
- }
785
- });
786
- if (parentTarget) {
787
- const parentLines = parentContent.split("\n");
788
- const parentComponentLines = parentLines.slice(
789
- parentTarget.componentStart - 1,
790
- parentTarget.componentEnd
791
- );
792
- parentInstance = {
793
- filePath: resolvedParentPath,
794
- content: parentComponentLines.join("\n"),
795
- lineStart: parentTarget.componentStart,
796
- lineEnd: parentTarget.componentEnd,
797
- usageLineStart: parentTarget.startLine,
798
- usageLineEnd: parentTarget.endLine,
799
- componentName: resolvedParentComponentName
800
- };
801
- }
802
- }
803
- }
804
- } catch (error) {
805
- console.error("Error resolving parent instance:", error);
806
- }
807
- }
808
- return NextResponse.json({
809
- success: true,
810
- content: preview,
811
- lineStart: target.componentStart,
812
- lineEnd: target.componentEnd,
813
- targetStartLine: target.startLine,
814
- targetEndLine: target.endLine,
815
- componentName,
816
- // Return the actual component name parsed from code
817
- parentInstance
818
- // Optional: where this component is used
819
- });
820
- }
821
- async function handleUndo(req) {
822
- const devModeError = validateDevMode();
823
- if (devModeError) return devModeError;
824
- try {
825
- const body = await req.json();
826
- const { filePath, content } = body;
827
- if (!filePath || typeof content !== "string") {
828
- return NextResponse.json(
829
- {
830
- success: false,
831
- error: "Invalid request: filePath and content required"
832
- },
833
- { status: 400 }
834
- );
835
- }
836
- const projectRoot = process.cwd();
837
- const normalizedPath = normalizePath(filePath);
838
- const absolutePath = await resolveFilePath(projectRoot, normalizedPath);
839
- if (!absolutePath) {
840
- return NextResponse.json(
841
- { success: false, error: `File not found: ${normalizedPath}` },
842
- { status: 404 }
843
- );
844
- }
845
- if (!isPathSecure(absolutePath, projectRoot)) {
846
- return NextResponse.json(
847
- { success: false, error: "Access denied: File outside project root" },
848
- { status: 403 }
849
- );
850
- }
851
- await fs.writeFile(absolutePath, content, "utf-8");
852
- console.log(`✅ Undo: Restored ${normalizedPath}`);
853
- return NextResponse.json({ success: true });
854
- } catch (error) {
855
- console.error("Undo error:", error);
856
- return NextResponse.json(
857
- { success: false, error: String(error) },
858
- { status: 500 }
859
- );
860
- }
861
- }
862
- async function handleResolve(req) {
863
- const devModeError = validateDevMode();
864
- if (devModeError) return devModeError;
865
- try {
866
- const body = await req.json();
867
- const debugStack = body == null ? void 0 : body.debugStack;
868
- if (!debugStack || typeof debugStack !== "string") {
869
- return NextResponse.json(
870
- { success: false, error: "Missing debugStack" },
871
- { status: 400 }
872
- );
873
- }
874
- const compiledFrames = parseDebugStackFrames(debugStack);
875
- if (compiledFrames.length === 0) {
876
- const compiledPos = parseDebugStack(debugStack);
877
- if (!compiledPos) {
878
- console.error("Could not parse debug stack:", debugStack);
879
- return NextResponse.json(
880
- { success: false, error: "Could not parse stack" },
881
- { status: 422 }
882
- );
883
- }
884
- const originalPos = await resolveOriginalPosition(
885
- compiledPos,
886
- process.cwd()
887
- );
888
- if (!originalPos) {
889
- return NextResponse.json(
890
- { success: false, error: "Source map lookup failed" },
891
- { status: 404 }
892
- );
893
- }
894
- return NextResponse.json({
895
- success: true,
896
- filePath: originalPos.source,
897
- lineNumber: originalPos.line,
898
- columnNumber: originalPos.column ?? 0
899
- });
900
- }
901
- const resolvedFrames = [];
902
- for (const frame of compiledFrames) {
903
- const originalPos = await resolveOriginalPosition(frame, process.cwd());
904
- if (originalPos) {
905
- resolvedFrames.push({
906
- filePath: originalPos.source,
907
- lineNumber: originalPos.line,
908
- columnNumber: originalPos.column ?? 0
909
- });
910
- }
911
- }
912
- if (resolvedFrames.length === 0) {
913
- return NextResponse.json(
914
- { success: false, error: "Source map lookup failed for all frames" },
915
- { status: 404 }
916
- );
917
- }
918
- console.log("Resolved frames:", resolvedFrames);
919
- return NextResponse.json({
920
- success: true,
921
- filePath: resolvedFrames[0].filePath,
922
- lineNumber: resolvedFrames[0].lineNumber,
923
- columnNumber: resolvedFrames[0].columnNumber,
924
- frames: resolvedFrames
925
- // [componentDefinition, parentInstance]
926
- });
927
- } catch (error) {
928
- console.error("Source resolve error:", error);
929
- return NextResponse.json(
930
- { success: false, error: "Internal error" },
931
- { status: 500 }
932
- );
933
- }
934
- }
935
- async function handleAbsolutePath(req) {
936
- const devModeError = validateDevMode();
937
- if (devModeError) return devModeError;
938
- const { searchParams } = new URL(req.url);
939
- const filePath = searchParams.get("path") || "";
940
- if (!filePath) {
941
- return NextResponse.json(
942
- { success: false, error: "Missing path" },
943
- { status: 400 }
944
- );
945
- }
946
- const projectRoot = process.cwd();
947
- const normalizedPath = normalizePath(filePath);
948
- const absolutePath = await resolveFilePath(projectRoot, normalizedPath);
949
- if (!absolutePath) {
950
- return NextResponse.json(
951
- { success: false, error: "File not found" },
952
- { status: 404 }
953
- );
954
- }
955
- return NextResponse.json({
956
- success: true,
957
- absolutePath
958
- });
959
- }
960
- async function handleValidateSession(req) {
961
- const devModeError = validateDevMode();
962
- if (devModeError) return devModeError;
963
- try {
964
- const body = await req.json();
965
- const { filePath, lastKnownHash, lastKnownSize } = body;
966
- if (!filePath || !lastKnownHash || lastKnownSize === void 0) {
967
- return NextResponse.json(
968
- { success: false, error: "Missing required fields" },
969
- { status: 400 }
970
- );
971
- }
972
- const projectRoot = process.cwd();
973
- const normalizedPath = normalizePath(filePath);
974
- const absolutePath = await resolveFilePath(projectRoot, normalizedPath);
975
- if (!absolutePath) {
976
- return NextResponse.json(
977
- { success: false, error: "File not found" },
978
- { status: 404 }
979
- );
980
- }
981
- if (!isPathSecure(absolutePath, projectRoot)) {
982
- return NextResponse.json(
983
- { success: false, error: "Access denied" },
984
- { status: 403 }
985
- );
986
- }
987
- const content = await fs.readFile(absolutePath, "utf-8");
988
- const hash = crypto.createHash("sha256").update(content).digest("hex");
989
- const stats = await fs.stat(absolutePath);
990
- const isValid = hash === lastKnownHash && content.length === lastKnownSize;
991
- console.log(`[validate-session] ${filePath}:`);
992
- console.log(` Hash match: ${hash === lastKnownHash}`);
993
- console.log(` Size match: ${content.length === lastKnownSize}`);
994
- console.log(` Is valid: ${isValid}`);
995
- return NextResponse.json({
996
- success: true,
997
- isValid,
998
- currentHash: hash,
999
- currentSize: content.length,
1000
- lastModifiedTime: stats.mtimeMs
1001
- });
1002
- } catch (error) {
1003
- console.error("Validation error:", error);
1004
- return NextResponse.json(
1005
- { success: false, error: String(error) },
1006
- { status: 500 }
1007
- );
1008
- }
1009
- }
1010
- class AIEditorAgent {
1011
- constructor(session, projectRoot) {
1012
- this.session = session;
1013
- this.projectRoot = projectRoot;
1014
- }
1015
- /**
1016
- * Create a new agent instance
1017
- */
1018
- static async create(config) {
1019
- const sessionOptions = {
1020
- model: "claude-sonnet-4-5-20250929",
1021
- // Enable auto-apply for edits without permission prompts
1022
- permissionMode: "acceptEdits",
1023
- env: {
1024
- ...process.env,
1025
- ANTHROPIC_API_KEY: config.apiKey,
1026
- // Set working directory
1027
- PWD: config.projectRoot
1028
- },
1029
- // Explicitly allow core development tools
1030
- allowedTools: config.allowedTools || ["Read", "Edit", "Glob", "Grep"],
1031
- disallowedTools: config.disallowedTools || []
1032
- };
1033
- let session;
1034
- if (config.sessionId) {
1035
- session = unstable_v2_resumeSession(config.sessionId, sessionOptions);
1036
- } else {
1037
- session = unstable_v2_createSession(sessionOptions);
1038
- }
1039
- return new AIEditorAgent(session, config.projectRoot);
1040
- }
1041
- /**
1042
- * Send a message to the agent and stream responses
1043
- */
1044
- async *sendMessage(message, componentContext) {
1045
- let fullMessage = `<response_style>
1046
- KEEP RESPONSES EXTREMELY BRIEF AND STATUS-LIKE.
1047
- - Use terse status messages: "Reading file.tsx", "Changing color", "Done"
1048
- - NO conversational language (avoid: "I'll", "Let me", "Sure", "Great", "I can see")
1049
- - NO explanations unless explicitly asked
1050
- - State ONLY what you're doing, nothing more
1051
- - Format: Action + target (e.g., "Updating StatsCard.tsx:24")
1052
- </response_style>
1053
-
1054
- `;
1055
- if (componentContext) {
1056
- fullMessage += `[Component: ${componentContext.componentName} at ${componentContext.filePath}:${componentContext.lineNumber}]
1057
-
1058
- `;
1059
- }
1060
- fullMessage += message;
1061
- await this.session.send(fullMessage);
1062
- for await (const event of this.session.stream()) {
1063
- yield event;
1064
- }
1065
- }
1066
- /**
1067
- * Get the session ID
1068
- */
1069
- getSessionId() {
1070
- return this.session.sessionId;
1071
- }
1072
- /**
1073
- * Close the session
1074
- */
1075
- close() {
1076
- this.session.close();
1077
- }
1078
- /**
1079
- * Async disposal support
1080
- */
1081
- async [Symbol.asyncDispose]() {
1082
- await this.session[Symbol.asyncDispose]();
1083
- }
1084
- }
1085
- class AgentSessionStore {
1086
- // 5 minutes
1087
- constructor() {
1088
- this.sessions = /* @__PURE__ */ new Map();
1089
- this.SESSION_TIMEOUT = 1e3 * 60 * 30;
1090
- this.CLEANUP_INTERVAL = 1e3 * 60 * 5;
1091
- this.startCleanupTimer();
1092
- }
1093
- /**
1094
- * Create or resume a session
1095
- */
1096
- async createSession(config) {
1097
- const sessionId = config.sessionId || this.generateSessionId();
1098
- const existing = this.sessions.get(sessionId);
1099
- if (existing) {
1100
- existing.lastActive = Date.now();
1101
- existing.threadId = config.threadId;
1102
- return existing;
1103
- }
1104
- const agent = await AIEditorAgent.create({
1105
- apiKey: config.apiKey,
1106
- projectRoot: config.projectRoot,
1107
- sessionId: config.sessionId,
1108
- allowedTools: config.allowedTools,
1109
- disallowedTools: config.disallowedTools
1110
- });
1111
- const session = {
1112
- sessionId,
1113
- threadId: config.threadId,
1114
- agent,
1115
- createdAt: Date.now(),
1116
- lastActive: Date.now(),
1117
- isLocked: false
1118
- };
1119
- this.sessions.set(sessionId, session);
1120
- return session;
1121
- }
1122
- /**
1123
- * Get a session by ID
1124
- */
1125
- getSession(sessionId) {
1126
- const session = this.sessions.get(sessionId);
1127
- if (session) {
1128
- session.lastActive = Date.now();
1129
- }
1130
- return session;
1131
- }
1132
- /**
1133
- * Acquire a lock on a session to prevent concurrent edits
1134
- * Returns true if lock was acquired, false if already locked
1135
- */
1136
- acquireLock(sessionId) {
1137
- const session = this.sessions.get(sessionId);
1138
- if (!session) {
1139
- throw new Error(`Session ${sessionId} not found`);
1140
- }
1141
- if (session.isLocked) {
1142
- return false;
1143
- }
1144
- session.isLocked = true;
1145
- session.lastActive = Date.now();
1146
- return true;
1147
- }
1148
- /**
1149
- * Release a lock on a session
1150
- */
1151
- releaseLock(sessionId) {
1152
- const session = this.sessions.get(sessionId);
1153
- if (session) {
1154
- session.isLocked = false;
1155
- session.lastActive = Date.now();
1156
- }
1157
- }
1158
- /**
1159
- * Check if any session is currently locked (to prevent concurrent edits)
1160
- */
1161
- hasActiveLock() {
1162
- for (const session of this.sessions.values()) {
1163
- if (session.isLocked) {
1164
- return true;
1165
- }
1166
- }
1167
- return false;
1168
- }
1169
- /**
1170
- * Get all active sessions for a thread
1171
- */
1172
- getSessionsByThread(threadId) {
1173
- const sessions = [];
1174
- for (const session of this.sessions.values()) {
1175
- if (session.threadId === threadId) {
1176
- sessions.push(session);
1177
- }
1178
- }
1179
- return sessions;
1180
- }
1181
- /**
1182
- * Delete a session
1183
- */
1184
- deleteSession(sessionId) {
1185
- const session = this.sessions.get(sessionId);
1186
- if (session) {
1187
- session.agent.close();
1188
- this.sessions.delete(sessionId);
1189
- }
1190
- }
1191
- /**
1192
- * Delete all sessions for a thread
1193
- */
1194
- deleteSessionsByThread(threadId) {
1195
- for (const [sessionId, session] of this.sessions.entries()) {
1196
- if (session.threadId === threadId) {
1197
- session.agent.close();
1198
- this.sessions.delete(sessionId);
1199
- }
1200
- }
1201
- }
1202
- /**
1203
- * Get all active sessions
1204
- */
1205
- getAllSessions() {
1206
- return Array.from(this.sessions.values());
1207
- }
1208
- /**
1209
- * Clean up expired sessions
1210
- */
1211
- cleanupExpiredSessions() {
1212
- const now = Date.now();
1213
- const expiredSessions = [];
1214
- for (const [sessionId, session] of this.sessions.entries()) {
1215
- const inactiveTime = now - session.lastActive;
1216
- if (inactiveTime > this.SESSION_TIMEOUT && !session.isLocked) {
1217
- expiredSessions.push(sessionId);
1218
- }
1219
- }
1220
- for (const sessionId of expiredSessions) {
1221
- console.log(
1222
- `[AgentSessionStore] Cleaning up expired session: ${sessionId}`
1223
- );
1224
- this.deleteSession(sessionId);
1225
- }
1226
- if (expiredSessions.length > 0) {
1227
- console.log(
1228
- `[AgentSessionStore] Cleaned up ${expiredSessions.length} expired sessions`
1229
- );
1230
- }
1231
- }
1232
- /**
1233
- * Start periodic cleanup timer
1234
- */
1235
- startCleanupTimer() {
1236
- setInterval(() => {
1237
- this.cleanupExpiredSessions();
1238
- }, this.CLEANUP_INTERVAL);
1239
- }
1240
- /**
1241
- * Generate a unique session ID
1242
- */
1243
- generateSessionId() {
1244
- return `session-${Date.now()}-${Math.random().toString(36).substring(2, 11)}`;
1245
- }
1246
- /**
1247
- * Get statistics about the session store
1248
- */
1249
- getStats() {
1250
- return {
1251
- totalSessions: this.sessions.size,
1252
- lockedSessions: Array.from(this.sessions.values()).filter(
1253
- (s) => s.isLocked
1254
- ).length,
1255
- sessions: Array.from(this.sessions.values()).map((s) => ({
1256
- sessionId: s.sessionId,
1257
- threadId: s.threadId,
1258
- isLocked: s.isLocked,
1259
- ageMinutes: Math.floor((Date.now() - s.createdAt) / 1e3 / 60),
1260
- inactiveMinutes: Math.floor((Date.now() - s.lastActive) / 1e3 / 60)
1261
- }))
1262
- };
1263
- }
1264
- }
1265
- let sessionStore = null;
1266
- function getSessionStore() {
1267
- if (!sessionStore) {
1268
- sessionStore = new AgentSessionStore();
1269
- }
1270
- return sessionStore;
1271
- }
1272
- async function handleChat(req) {
1273
- const devModeError = validateDevMode();
1274
- if (devModeError) return devModeError;
1275
- try {
1276
- const body = await req.json();
1277
- const { sessionId, threadId, message, componentContext } = body;
1278
- if (!threadId || !message) {
1279
- return NextResponse.json(
1280
- { error: "threadId and message are required" },
1281
- { status: 400 }
1282
- );
1283
- }
1284
- const apiKey = process.env.ANTHROPIC_API_KEY;
1285
- if (!apiKey) {
1286
- return NextResponse.json(
1287
- { error: "ANTHROPIC_API_KEY not configured" },
1288
- { status: 500 }
1289
- );
1290
- }
1291
- const sessionStore2 = getSessionStore();
1292
- const projectRoot = process.cwd();
1293
- const session = await sessionStore2.createSession({
1294
- sessionId,
1295
- threadId,
1296
- apiKey,
1297
- projectRoot
1298
- });
1299
- if (!sessionStore2.acquireLock(session.sessionId)) {
1300
- return NextResponse.json(
1301
- {
1302
- error: "Another operation is in progress. Please wait for it to complete."
1303
- },
1304
- { status: 409 }
1305
- );
1306
- }
1307
- const encoder = new TextEncoder();
1308
- const stream = new ReadableStream({
1309
- async start(controller) {
1310
- try {
1311
- controller.enqueue(
1312
- encoder.encode(
1313
- `data: ${JSON.stringify({ type: "connected", sessionId: session.sessionId })}
1314
-
1315
- `
1316
- )
1317
- );
1318
- for await (const event of session.agent.sendMessage(
1319
- message,
1320
- componentContext
1321
- )) {
1322
- console.log("[AGENT EVENT]", event.type, JSON.stringify(event, null, 2));
1323
- controller.enqueue(
1324
- encoder.encode(`data: ${JSON.stringify(event)}
1325
-
1326
- `)
1327
- );
1328
- }
1329
- controller.enqueue(
1330
- encoder.encode(`data: ${JSON.stringify({ type: "done" })}
1331
-
1332
- `)
1333
- );
1334
- controller.close();
1335
- } catch (error) {
1336
- console.error("[handleChat] Error:", error);
1337
- controller.enqueue(
1338
- encoder.encode(
1339
- `data: ${JSON.stringify({ type: "error", error: error.message || String(error) })}
1340
-
1341
- `
1342
- )
1343
- );
1344
- controller.close();
1345
- } finally {
1346
- sessionStore2.releaseLock(session.sessionId);
1347
- }
1348
- }
1349
- });
1350
- return new Response(stream, {
1351
- headers: {
1352
- "Content-Type": "text/event-stream",
1353
- "Cache-Control": "no-cache",
1354
- Connection: "keep-alive"
1355
- }
1356
- });
1357
- } catch (error) {
1358
- console.error("[handleChat] Request error:", error);
1359
- return NextResponse.json(
1360
- { error: String(error) },
1361
- { status: 500 }
1362
- );
1363
- }
1364
- }
1365
- async function handleConfig() {
1366
- const enabled = process.env.NEXT_PUBLIC_AI_EDITOR_ENABLED === "true" || process.env.VITE_AI_EDITOR_ENABLED === "true" || process.env.REACT_APP_AI_EDITOR_ENABLED === "true";
1367
- return NextResponse.json({
1368
- enabled,
1369
- // Include NODE_ENV for debugging
1370
- nodeEnv: process.env.NODE_ENV
1371
- });
1372
- }
1373
- async function handleAIEditorRequest(req, context) {
1374
- const { path: path2 } = await context.params;
1375
- const endpoint = path2[0];
1376
- const method = req.method;
1377
- switch (endpoint) {
1378
- case "read":
1379
- if (method === "GET") return handleRead(req);
1380
- break;
1381
- case "undo":
1382
- if (method === "POST") return handleUndo(req);
1383
- break;
1384
- case "resolve":
1385
- if (method === "POST") return handleResolve(req);
1386
- break;
1387
- case "absolute-path":
1388
- if (method === "GET") return handleAbsolutePath(req);
1389
- break;
1390
- case "validate-session":
1391
- if (method === "POST") return handleValidateSession(req);
1392
- break;
1393
- case "chat":
1394
- if (method === "POST") return handleChat(req);
1395
- break;
1396
- case "config":
1397
- if (method === "GET") return handleConfig();
1398
- break;
1399
- }
1400
- return NextResponse.json(
1401
- { error: `Unknown endpoint: ${endpoint}` },
1402
- { status: 404 }
1403
- );
1404
- }
1405
- async function handleAIEditorPagesRequest(req, res) {
1406
- const { path: path2 } = req.query;
1407
- const endpoint = Array.isArray(path2) ? path2[0] : path2;
1408
- const method = req.method;
1409
- const url = `http://localhost:3000${req.url}`;
1410
- const headers = new Headers();
1411
- Object.entries(req.headers).forEach(([key, value]) => {
1412
- if (value) {
1413
- headers.set(key, Array.isArray(value) ? value.join(", ") : value);
1414
- }
1415
- });
1416
- const nextRequest = new Request(url, {
1417
- method: req.method || "GET",
1418
- headers,
1419
- body: method !== "GET" && method !== "HEAD" ? JSON.stringify(req.body) : void 0
1420
- });
1421
- let response;
1422
- switch (endpoint) {
1423
- case "read":
1424
- if (method === "GET") response = await handleRead(nextRequest);
1425
- else break;
1426
- break;
1427
- case "undo":
1428
- if (method === "POST") response = await handleUndo(nextRequest);
1429
- else break;
1430
- break;
1431
- case "resolve":
1432
- if (method === "POST") response = await handleResolve(nextRequest);
1433
- else break;
1434
- break;
1435
- case "absolute-path":
1436
- if (method === "GET") response = await handleAbsolutePath(nextRequest);
1437
- else break;
1438
- break;
1439
- case "validate-session":
1440
- if (method === "POST") response = await handleValidateSession(nextRequest);
1441
- else break;
1442
- break;
1443
- case "chat":
1444
- if (method === "POST") response = await handleChat(nextRequest);
1445
- else break;
1446
- break;
1447
- case "config":
1448
- if (method === "GET") response = await handleConfig();
1449
- else break;
1450
- break;
1451
- default:
1452
- res.status(404).json({ error: `Unknown endpoint: ${endpoint}` });
1453
- return;
1454
- }
1455
- if (!response) {
1456
- res.status(405).json({ error: `Method ${method} not allowed for ${endpoint}` });
1457
- return;
1458
- }
1459
- const data = await response.json();
1460
- res.status(response.status).json(data);
1461
- }
1462
- const STORAGE_DIR = ".ai-editor";
1463
- const STORAGE_FILE = "comments.json";
1464
- function getProjectRoot() {
1465
- return process.cwd();
1466
- }
1467
- function getStoragePath() {
1468
- return join(getProjectRoot(), STORAGE_DIR, STORAGE_FILE);
1469
- }
1470
- async function ensureStorageDir() {
1471
- const dir = join(getProjectRoot(), STORAGE_DIR);
1472
- if (!existsSync(dir)) {
1473
- await mkdir(dir, { recursive: true });
1474
- }
1475
- }
1476
- async function handleCommentsRequest(request) {
1477
- if (process.env.NODE_ENV !== "development") {
1478
- return NextResponse.json(
1479
- { error: "Comments API only available in development" },
1480
- { status: 403 }
1481
- );
1482
- }
1483
- try {
1484
- if (request.method === "GET") {
1485
- const storagePath = getStoragePath();
1486
- if (!existsSync(storagePath)) {
1487
- return NextResponse.json({ comments: [] });
1488
- }
1489
- const data = await readFile(storagePath, "utf-8");
1490
- const comments = JSON.parse(data);
1491
- return NextResponse.json({ comments });
1492
- }
1493
- if (request.method === "POST") {
1494
- const body = await request.json();
1495
- const { action } = body;
1496
- if (action === "save") {
1497
- const { comments } = body;
1498
- await ensureStorageDir();
1499
- const storagePath = getStoragePath();
1500
- await writeFile(storagePath, JSON.stringify(comments, null, 2), "utf-8");
1501
- return NextResponse.json({ success: true });
1502
- }
1503
- if (action === "clear") {
1504
- await ensureStorageDir();
1505
- const storagePath = getStoragePath();
1506
- await writeFile(storagePath, JSON.stringify([]), "utf-8");
1507
- return NextResponse.json({ success: true });
1508
- }
1509
- return NextResponse.json(
1510
- { error: "Invalid action" },
1511
- { status: 400 }
1512
- );
1513
- }
1514
- return NextResponse.json(
1515
- { error: "Method not allowed" },
1516
- { status: 405 }
1517
- );
1518
- } catch (error) {
1519
- console.error("Comment storage error:", error);
1520
- return NextResponse.json(
1521
- { error: "Internal server error" },
1522
- { status: 500 }
1523
- );
1524
- }
1525
- }
1526
- export {
1527
- handleAIEditorPagesRequest as a,
1528
- handleRead as b,
1529
- handleUndo as c,
1530
- handleResolve as d,
1531
- handleAbsolutePath as e,
1532
- handleValidateSession as f,
1533
- handleCommentsRequest as g,
1534
- handleAIEditorRequest as h,
1535
- isPathSecure as i,
1536
- fileExists$1 as j,
1537
- extractComponentName as k,
1538
- validateGeneratedCode as l,
1539
- findTargetElement as m,
1540
- normalizePath as n,
1541
- getJSXMemberName as o,
1542
- parseFile as p,
1543
- getAttributeValue as q,
1544
- resolveFilePath as r,
1545
- scoreElementMatch as s,
1546
- parseDebugStack as t,
1547
- extractComponentNameFromStack as u,
1548
- validateDevMode as v,
1549
- parseDebugStackFrames as w,
1550
- resolveOriginalPosition as x,
1551
- getOriginalPositionFromDebugStack as y
1552
- };
1553
- //# sourceMappingURL=comments-CjBQxjDP.js.map