next-ai-editor 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (55) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +213 -0
  3. package/dist/AIEditorProvider-Bs9zUVrL.cjs +1722 -0
  4. package/dist/AIEditorProvider-Bs9zUVrL.cjs.map +1 -0
  5. package/dist/AIEditorProvider-D-w9-GZb.js +1723 -0
  6. package/dist/AIEditorProvider-D-w9-GZb.js.map +1 -0
  7. package/dist/client/AIEditorProvider.d.ts +52 -0
  8. package/dist/client/AIEditorProvider.d.ts.map +1 -0
  9. package/dist/client/index.d.ts +3 -0
  10. package/dist/client/index.d.ts.map +1 -0
  11. package/dist/client.cjs +6 -0
  12. package/dist/client.cjs.map +1 -0
  13. package/dist/client.js +6 -0
  14. package/dist/client.js.map +1 -0
  15. package/dist/index-BFa7H-uO.js +1145 -0
  16. package/dist/index-BFa7H-uO.js.map +1 -0
  17. package/dist/index-DnoYi4f8.cjs +1162 -0
  18. package/dist/index-DnoYi4f8.cjs.map +1 -0
  19. package/dist/index.cjs +26 -0
  20. package/dist/index.cjs.map +1 -0
  21. package/dist/index.d.ts +4 -0
  22. package/dist/index.d.ts.map +1 -0
  23. package/dist/index.js +26 -0
  24. package/dist/index.js.map +1 -0
  25. package/dist/server/handlers/absolute-path.d.ts +3 -0
  26. package/dist/server/handlers/absolute-path.d.ts.map +1 -0
  27. package/dist/server/handlers/edit.d.ts +3 -0
  28. package/dist/server/handlers/edit.d.ts.map +1 -0
  29. package/dist/server/handlers/index.d.ts +18 -0
  30. package/dist/server/handlers/index.d.ts.map +1 -0
  31. package/dist/server/handlers/read.d.ts +3 -0
  32. package/dist/server/handlers/read.d.ts.map +1 -0
  33. package/dist/server/handlers/resolve.d.ts +3 -0
  34. package/dist/server/handlers/resolve.d.ts.map +1 -0
  35. package/dist/server/handlers/undo.d.ts +3 -0
  36. package/dist/server/handlers/undo.d.ts.map +1 -0
  37. package/dist/server/handlers/validate-session.d.ts +3 -0
  38. package/dist/server/handlers/validate-session.d.ts.map +1 -0
  39. package/dist/server/index.d.ts +11 -0
  40. package/dist/server/index.d.ts.map +1 -0
  41. package/dist/server/utils/ast.d.ts +39 -0
  42. package/dist/server/utils/ast.d.ts.map +1 -0
  43. package/dist/server/utils/file-system.d.ts +24 -0
  44. package/dist/server/utils/file-system.d.ts.map +1 -0
  45. package/dist/server/utils/source-map.d.ts +29 -0
  46. package/dist/server/utils/source-map.d.ts.map +1 -0
  47. package/dist/server.cjs +24 -0
  48. package/dist/server.cjs.map +1 -0
  49. package/dist/server.js +24 -0
  50. package/dist/server.js.map +1 -0
  51. package/dist/shared/storage.d.ts +53 -0
  52. package/dist/shared/storage.d.ts.map +1 -0
  53. package/dist/shared/types.d.ts +44 -0
  54. package/dist/shared/types.d.ts.map +1 -0
  55. package/package.json +87 -0
@@ -0,0 +1,1162 @@
1
+ "use strict";
2
+ const server = require("next/server");
3
+ const fs = require("fs/promises");
4
+ const parser = require("@babel/parser");
5
+ const anthropic = require("@langchain/anthropic");
6
+ const messages = require("@langchain/core/messages");
7
+ const path = require("path");
8
+ const traverse = require("@babel/traverse");
9
+ const t = require("@babel/types");
10
+ const sourceMap = require("@jridgewell/source-map");
11
+ const crypto = require("crypto");
12
+ function _interopNamespaceDefault(e) {
13
+ const n = Object.create(null, { [Symbol.toStringTag]: { value: "Module" } });
14
+ if (e) {
15
+ for (const k in e) {
16
+ if (k !== "default") {
17
+ const d = Object.getOwnPropertyDescriptor(e, k);
18
+ Object.defineProperty(n, k, d.get ? d : {
19
+ enumerable: true,
20
+ get: () => e[k]
21
+ });
22
+ }
23
+ }
24
+ }
25
+ n.default = e;
26
+ return Object.freeze(n);
27
+ }
28
+ const parser__namespace = /* @__PURE__ */ _interopNamespaceDefault(parser);
29
+ const t__namespace = /* @__PURE__ */ _interopNamespaceDefault(t);
30
+ function validateDevMode() {
31
+ if (process.env.NODE_ENV !== "development") {
32
+ return server.NextResponse.json(
33
+ { success: false, error: "Development mode only" },
34
+ { status: 403 }
35
+ );
36
+ }
37
+ return null;
38
+ }
39
+ function normalizePath(filePath) {
40
+ return filePath.replace(/^webpack-internal:\/\/\/\([^)]+\)\/\.?/, "").replace(/^webpack:\/\/[^/]*\//, "").replace(/^\.\//, "").replace(/\?.*$/, "");
41
+ }
42
+ async function resolveFilePath(projectRoot, normalizedPath) {
43
+ const candidates = [
44
+ path.resolve(projectRoot, normalizedPath),
45
+ path.resolve(projectRoot, "src", normalizedPath),
46
+ path.resolve(projectRoot, "app", normalizedPath)
47
+ ];
48
+ for (const candidate of candidates) {
49
+ if (!candidate.startsWith(projectRoot)) continue;
50
+ try {
51
+ await fs.access(candidate);
52
+ return candidate;
53
+ } catch {
54
+ }
55
+ }
56
+ return null;
57
+ }
58
+ function isPathSecure(absolutePath, projectRoot) {
59
+ return absolutePath.startsWith(projectRoot);
60
+ }
61
+ async function fileExists$1(filePath) {
62
+ try {
63
+ await fs.access(filePath);
64
+ return true;
65
+ } catch {
66
+ return false;
67
+ }
68
+ }
69
+ function parseFile(content) {
70
+ try {
71
+ return parser__namespace.parse(content, {
72
+ sourceType: "module",
73
+ plugins: [
74
+ "jsx",
75
+ "typescript",
76
+ "decorators-legacy",
77
+ "classProperties",
78
+ "optionalChaining",
79
+ "nullishCoalescingOperator"
80
+ ]
81
+ });
82
+ } catch (e) {
83
+ console.error("Parse error:", e);
84
+ return null;
85
+ }
86
+ }
87
+ function findTargetElement(ast, fileContent, options) {
88
+ const { componentName, lineNumber, elementContext } = options;
89
+ const matches = [];
90
+ let componentNode = null;
91
+ let componentStart = 0;
92
+ let componentEnd = Infinity;
93
+ let fallbackExportDefault = null;
94
+ traverse(ast, {
95
+ FunctionDeclaration(path2) {
96
+ var _a, _b, _c;
97
+ if (((_a = path2.node.id) == null ? void 0 : _a.name) === componentName) {
98
+ componentNode = path2.node;
99
+ componentStart = ((_b = path2.node.loc) == null ? void 0 : _b.start.line) || 0;
100
+ componentEnd = ((_c = path2.node.loc) == null ? void 0 : _c.end.line) || Infinity;
101
+ }
102
+ },
103
+ VariableDeclarator(path2) {
104
+ var _a, _b;
105
+ if (t__namespace.isIdentifier(path2.node.id) && path2.node.id.name === componentName) {
106
+ componentNode = path2.node;
107
+ const parent = path2.parentPath.parent;
108
+ componentStart = ((_a = parent == null ? void 0 : parent.loc) == null ? void 0 : _a.start.line) || 0;
109
+ componentEnd = ((_b = parent == null ? void 0 : parent.loc) == null ? void 0 : _b.end.line) || Infinity;
110
+ }
111
+ },
112
+ ExportDefaultDeclaration(path2) {
113
+ var _a, _b, _c, _d, _e;
114
+ if (t__namespace.isFunctionDeclaration(path2.node.declaration)) {
115
+ const funcName = (_a = path2.node.declaration.id) == null ? void 0 : _a.name;
116
+ if (funcName === componentName || !funcName) {
117
+ componentNode = path2.node;
118
+ componentStart = ((_b = path2.node.loc) == null ? void 0 : _b.start.line) || 0;
119
+ componentEnd = ((_c = path2.node.loc) == null ? void 0 : _c.end.line) || Infinity;
120
+ } else if (!componentNode) {
121
+ fallbackExportDefault = {
122
+ node: path2.node,
123
+ start: ((_d = path2.node.loc) == null ? void 0 : _d.start.line) || 0,
124
+ end: ((_e = path2.node.loc) == null ? void 0 : _e.end.line) || Infinity
125
+ };
126
+ }
127
+ }
128
+ }
129
+ });
130
+ if (!componentNode && fallbackExportDefault !== null) {
131
+ const fallback = fallbackExportDefault;
132
+ console.log(
133
+ `⚠️ Component "${componentName}" not found, using export default function as fallback`
134
+ );
135
+ componentNode = fallback.node;
136
+ componentStart = fallback.start;
137
+ componentEnd = fallback.end;
138
+ }
139
+ const allElementsByTag = /* @__PURE__ */ new Map();
140
+ traverse(ast, {
141
+ JSXElement(path2) {
142
+ const loc = path2.node.loc;
143
+ if (!loc) return;
144
+ const startLine = loc.start.line;
145
+ const endLine = loc.end.line;
146
+ if (startLine < componentStart || endLine > componentEnd) return;
147
+ const opening = path2.node.openingElement;
148
+ if (t__namespace.isJSXIdentifier(opening.name)) {
149
+ const tagName = opening.name.name;
150
+ if (!allElementsByTag.has(tagName)) {
151
+ allElementsByTag.set(tagName, []);
152
+ }
153
+ allElementsByTag.get(tagName).push({ node: path2.node, startLine, endLine, score: 0 });
154
+ }
155
+ if (elementContext) {
156
+ const score = scoreElementMatch(path2.node, elementContext, fileContent);
157
+ if (score > 0) {
158
+ matches.push({ node: path2.node, startLine, endLine, score });
159
+ }
160
+ } else if (Math.abs(startLine - lineNumber) <= 5) {
161
+ matches.push({
162
+ node: path2.node,
163
+ startLine,
164
+ endLine,
165
+ score: 100 - Math.abs(startLine - lineNumber)
166
+ });
167
+ }
168
+ }
169
+ });
170
+ if (matches.length === 0) {
171
+ if (componentNode && componentStart > 0) {
172
+ return {
173
+ startLine: componentStart,
174
+ endLine: componentEnd,
175
+ componentStart,
176
+ componentEnd
177
+ };
178
+ }
179
+ return null;
180
+ }
181
+ if ((elementContext == null ? void 0 : elementContext.nthOfType) && elementContext.tagName) {
182
+ const allOfTag = allElementsByTag.get(elementContext.tagName);
183
+ if (allOfTag && allOfTag.length >= elementContext.nthOfType) {
184
+ const target = allOfTag[elementContext.nthOfType - 1];
185
+ console.log(
186
+ ` Using nthOfType=${elementContext.nthOfType}: found ${allOfTag.length} <${elementContext.tagName}> elements`
187
+ );
188
+ return {
189
+ startLine: target.startLine,
190
+ endLine: target.endLine,
191
+ componentStart,
192
+ componentEnd
193
+ };
194
+ } else {
195
+ console.log(
196
+ ` nthOfType=${elementContext.nthOfType} but only found ${(allOfTag == null ? void 0 : allOfTag.length) || 0} <${elementContext.tagName}> elements`
197
+ );
198
+ }
199
+ }
200
+ matches.sort((a, b) => b.score - a.score);
201
+ const best = matches[0];
202
+ return {
203
+ startLine: best.startLine,
204
+ endLine: best.endLine,
205
+ componentStart,
206
+ componentEnd
207
+ };
208
+ }
209
+ function scoreElementMatch(node, context, fileContent) {
210
+ let score = 0;
211
+ const opening = node.openingElement;
212
+ if (t__namespace.isJSXIdentifier(opening.name)) {
213
+ if (opening.name.name === context.tagName) {
214
+ score += 50;
215
+ } else {
216
+ return 0;
217
+ }
218
+ } else if (t__namespace.isJSXMemberExpression(opening.name)) {
219
+ const fullName = getJSXMemberName(opening.name);
220
+ if (fullName === context.tagName || fullName.endsWith(`.${context.tagName}`)) {
221
+ score += 50;
222
+ } else {
223
+ return 0;
224
+ }
225
+ }
226
+ if (context.className) {
227
+ const classAttr = opening.attributes.find(
228
+ (attr) => t__namespace.isJSXAttribute(attr) && t__namespace.isJSXIdentifier(attr.name) && attr.name.name === "className"
229
+ );
230
+ if (classAttr && t__namespace.isJSXAttribute(classAttr)) {
231
+ const classValue = getAttributeValue(classAttr);
232
+ if (classValue && context.className.split(/\s+/).some((c) => classValue.includes(c))) {
233
+ score += 20;
234
+ }
235
+ }
236
+ }
237
+ if (context.textContent && node.loc) {
238
+ const elementCode = fileContent.split("\n").slice(node.loc.start.line - 1, node.loc.end.line).join("\n");
239
+ const normalizedContent = context.textContent.toLowerCase().trim();
240
+ const normalizedElement = elementCode.toLowerCase();
241
+ if (normalizedElement.includes(normalizedContent)) {
242
+ score += 30;
243
+ }
244
+ }
245
+ if (context.props) {
246
+ for (const [key, value] of Object.entries(context.props)) {
247
+ if (key.startsWith("_")) continue;
248
+ const attr = opening.attributes.find(
249
+ (a) => t__namespace.isJSXAttribute(a) && t__namespace.isJSXIdentifier(a.name) && a.name.name === key
250
+ );
251
+ if (attr) {
252
+ score += 5;
253
+ if (typeof value === "string" && t__namespace.isJSXAttribute(attr)) {
254
+ const attrValue = getAttributeValue(attr);
255
+ if (attrValue === value) score += 10;
256
+ }
257
+ }
258
+ }
259
+ }
260
+ return score;
261
+ }
262
+ function getJSXMemberName(node) {
263
+ if (t__namespace.isJSXIdentifier(node.object)) {
264
+ return `${node.object.name}.${node.property.name}`;
265
+ }
266
+ if (t__namespace.isJSXMemberExpression(node.object)) {
267
+ return `${getJSXMemberName(node.object)}.${node.property.name}`;
268
+ }
269
+ return node.property.name;
270
+ }
271
+ function getAttributeValue(attr) {
272
+ if (!attr.value) return null;
273
+ if (t__namespace.isStringLiteral(attr.value)) return attr.value.value;
274
+ if (t__namespace.isJSXExpressionContainer(attr.value) && t__namespace.isStringLiteral(attr.value.expression)) {
275
+ return attr.value.expression.value;
276
+ }
277
+ return null;
278
+ }
279
+ async function handleEdit(req) {
280
+ var _a;
281
+ const devModeError = validateDevMode();
282
+ if (devModeError) return devModeError;
283
+ try {
284
+ const body = await req.json();
285
+ const {
286
+ filePath,
287
+ lineNumber,
288
+ componentName,
289
+ suggestion,
290
+ elementContext,
291
+ editHistory
292
+ } = body;
293
+ const projectRoot = process.cwd();
294
+ const normalizedPath = normalizePath(filePath);
295
+ const absolutePath = await resolveFilePath(projectRoot, normalizedPath);
296
+ if (!absolutePath) {
297
+ return server.NextResponse.json(
298
+ { success: false, error: `File not found: ${normalizedPath}` },
299
+ { status: 404 }
300
+ );
301
+ }
302
+ const fileContent = await fs.readFile(absolutePath, "utf-8");
303
+ console.log(`[/edit] componentName="${componentName}", filePath="${filePath}"`);
304
+ const ast = parseFile(fileContent);
305
+ if (!ast) {
306
+ return server.NextResponse.json(
307
+ { success: false, error: "Failed to parse file" },
308
+ { status: 400 }
309
+ );
310
+ }
311
+ const target = findTargetElement(ast, fileContent, {
312
+ componentName,
313
+ lineNumber,
314
+ elementContext
315
+ });
316
+ if (!target) {
317
+ return server.NextResponse.json(
318
+ { success: false, error: "Could not locate target element" },
319
+ { status: 400 }
320
+ );
321
+ }
322
+ console.log(
323
+ `📍 Found element <${(elementContext == null ? void 0 : elementContext.tagName) || "component"}> at lines ${target.startLine}-${target.endLine} (component: ${target.componentStart}-${target.componentEnd})`
324
+ );
325
+ console.log(
326
+ ` Element context: tagName=${elementContext == null ? void 0 : elementContext.tagName}, nthOfType=${elementContext == null ? void 0 : elementContext.nthOfType}, textContent="${elementContext == null ? void 0 : elementContext.textContent}"`
327
+ );
328
+ const lines = fileContent.split("\n");
329
+ const foundElementCode = lines.slice(target.startLine - 1, Math.min(target.startLine + 2, target.endLine)).join("\n");
330
+ console.log(` Found element preview:
331
+ ${foundElementCode}`);
332
+ const targetCode = lines.slice(target.startLine - 1, target.endLine).join("\n");
333
+ const baseIndentation = ((_a = lines[target.startLine - 1].match(/^(\s*)/)) == null ? void 0 : _a[1]) || "";
334
+ if (target.componentStart <= 0 || target.componentEnd === Infinity) {
335
+ console.error("❌ Invalid component bounds detected");
336
+ return server.NextResponse.json({
337
+ success: false,
338
+ error: `Could not determine component bounds. Component: ${target.componentStart}-${target.componentEnd}`
339
+ });
340
+ }
341
+ const componentLines = lines.slice(
342
+ target.componentStart - 1,
343
+ target.componentEnd
344
+ );
345
+ const annotatedLines = componentLines.map((line, idx) => {
346
+ const lineNum = target.componentStart + idx;
347
+ const isTargetStart = lineNum === target.startLine;
348
+ const isTargetEnd = lineNum === target.endLine;
349
+ const isWithinTarget = lineNum >= target.startLine && lineNum <= target.endLine;
350
+ let annotation = "";
351
+ if (isTargetStart && isTargetEnd) {
352
+ annotation = " // ← CLICKED ELEMENT (single line)";
353
+ } else if (isTargetStart) {
354
+ annotation = " // ← CLICKED ELEMENT STARTS";
355
+ } else if (isTargetEnd) {
356
+ annotation = " // ← CLICKED ELEMENT ENDS";
357
+ } else if (isWithinTarget) {
358
+ annotation = " // ← (clicked element)";
359
+ }
360
+ return line + annotation;
361
+ });
362
+ const fullComponentCode = annotatedLines.join("\n");
363
+ const newCode = await generateEdit({
364
+ targetCode,
365
+ fullComponentCode,
366
+ suggestion,
367
+ elementContext,
368
+ baseIndentation,
369
+ targetLine: target.startLine,
370
+ targetEndLine: target.endLine,
371
+ componentStart: target.componentStart,
372
+ componentEnd: target.componentEnd,
373
+ editHistory: editHistory || []
374
+ });
375
+ if (!newCode) {
376
+ return server.NextResponse.json({
377
+ success: false,
378
+ error: "AI failed to generate valid edit"
379
+ });
380
+ }
381
+ console.log("Raw AI response length:", newCode.length);
382
+ console.log("Looking for // FULL_COMPONENT marker...");
383
+ const fullComponentMatch = newCode.match(/\/\/ FULL_COMPONENT\s*\n([\s\S]+)/);
384
+ let codeToApply = newCode;
385
+ let startLineToReplace = target.startLine;
386
+ let endLineToReplace = target.endLine;
387
+ if (fullComponentMatch) {
388
+ console.log("Found // FULL_COMPONENT marker, extracting full component code");
389
+ codeToApply = fullComponentMatch[1].trim();
390
+ startLineToReplace = target.componentStart;
391
+ endLineToReplace = target.componentEnd;
392
+ console.log(
393
+ `🔄 AI returned full component modification (lines ${startLineToReplace}-${endLineToReplace})`
394
+ );
395
+ console.log("Extracted component code length:", codeToApply.length);
396
+ console.log("Extracted component code (first 300 chars):", codeToApply.substring(0, 300));
397
+ } else {
398
+ console.log("No // FULL_COMPONENT marker found, treating as target element modification");
399
+ console.log(
400
+ `🔄 AI returned target element modification (lines ${startLineToReplace}-${endLineToReplace})`
401
+ );
402
+ }
403
+ console.log("Code to apply (first 200 chars):", codeToApply.substring(0, 200));
404
+ if (!validateGeneratedCode(codeToApply, targetCode, fileContent)) {
405
+ console.error("❌ Generated code failed validation");
406
+ console.error("=== Generated code START ===");
407
+ console.error(codeToApply);
408
+ console.error("=== Generated code END ===");
409
+ console.error(`Length: ${codeToApply.length} chars, ${codeToApply.split("\n").length} lines`);
410
+ return server.NextResponse.json({
411
+ success: false,
412
+ error: "Generated code is invalid - check server logs for details"
413
+ });
414
+ }
415
+ const newLines = [...lines];
416
+ newLines.splice(
417
+ startLineToReplace - 1,
418
+ endLineToReplace - startLineToReplace + 1,
419
+ ...codeToApply.split("\n")
420
+ );
421
+ await fs.writeFile(absolutePath, newLines.join("\n"), "utf-8");
422
+ console.log(`✅ Updated ${normalizedPath}`);
423
+ return server.NextResponse.json({
424
+ success: true,
425
+ fileSnapshot: fileContent,
426
+ // Original file content for undo
427
+ generatedCode: codeToApply,
428
+ // AI-generated code
429
+ modifiedLines: {
430
+ start: startLineToReplace,
431
+ end: endLineToReplace
432
+ }
433
+ });
434
+ } catch (error) {
435
+ console.error("AI Editor error:", error);
436
+ return server.NextResponse.json(
437
+ { success: false, error: String(error) },
438
+ { status: 500 }
439
+ );
440
+ }
441
+ }
442
+ async function generateEdit(options) {
443
+ const {
444
+ fullComponentCode,
445
+ suggestion,
446
+ baseIndentation,
447
+ editHistory
448
+ } = options;
449
+ const apiKey = process.env.ANTHROPIC_API_KEY;
450
+ if (!apiKey) throw new Error("ANTHROPIC_API_KEY not set");
451
+ const model = new anthropic.ChatAnthropic({
452
+ apiKey,
453
+ modelName: "claude-sonnet-4-5-20250929",
454
+ maxTokens: 4096,
455
+ temperature: 0
456
+ });
457
+ const systemPrompt = `You are a precise code editor for React/JSX components.
458
+
459
+ WHAT YOU'LL SEE:
460
+ - Full component code with line annotations
461
+ - The clicked element is marked with "// ← CLICKED ELEMENT STARTS" and "// ← CLICKED ELEMENT ENDS"
462
+ - Lines within the element have "// ← (clicked element)"
463
+ - These are just annotations to help you - they are NOT part of the actual code
464
+
465
+ YOUR TASK:
466
+ Modify ONLY the marked element (unless the request requires changing its parent/wrapper).
467
+
468
+ OUTPUT:
469
+ - Just the modified element code (WITHOUT the annotation comments)
470
+ - OR if broader changes needed: "// FULL_COMPONENT\\n" + complete modified component (WITHOUT annotation comments)
471
+
472
+ RULES:
473
+ - Output ONLY code, no explanations
474
+ - No markdown fences
475
+ - Do NOT include the "// ← CLICKED ELEMENT" annotation comments in your output
476
+ - Preserve indentation
477
+ - Make minimal changes
478
+ - Do NOT modify unrelated elements`;
479
+ let userPrompt = "";
480
+ if (editHistory.length > 0) {
481
+ userPrompt += "Previous edits made to this element:\n";
482
+ editHistory.forEach((item, idx) => {
483
+ userPrompt += `${idx + 1}. ${item.suggestion} ${item.success ? "(✓ applied)" : "(✗ failed)"}
484
+ `;
485
+ });
486
+ userPrompt += "\n";
487
+ }
488
+ userPrompt += `Component (clicked element is annotated with "// ← CLICKED ELEMENT" comments):
489
+
490
+ \`\`\`jsx
491
+ ${fullComponentCode}
492
+ \`\`\`
493
+
494
+ User request: "${suggestion}"
495
+ ${editHistory.length > 0 ? "\n(Build upon previous changes)" : ""}
496
+
497
+ Modify the annotated element to fulfill this request. Remember: do NOT include the annotation comments in your output.`;
498
+ try {
499
+ const response = await model.invoke([
500
+ new messages.SystemMessage(systemPrompt),
501
+ new messages.HumanMessage(userPrompt)
502
+ ]);
503
+ let code = typeof response.content === "string" ? response.content : String(response.content);
504
+ code = cleanGeneratedCode(code, baseIndentation);
505
+ return code || null;
506
+ } catch (e) {
507
+ console.error("AI generation error:", e);
508
+ return null;
509
+ }
510
+ }
511
+ function cleanGeneratedCode(code, baseIndentation) {
512
+ var _a, _b, _c;
513
+ const isFullComponent = code.trim().startsWith("// FULL_COMPONENT");
514
+ let marker = "";
515
+ if (isFullComponent) {
516
+ marker = "// FULL_COMPONENT\n";
517
+ code = code.replace(/^\/\/ FULL_COMPONENT\n?/, "");
518
+ }
519
+ code = code.replace(/^```[\w]*\n?/gm, "").replace(/\n?```$/gm, "").trim();
520
+ code = code.replace(/^(Here'?s?|Here is|Modified|Output|Result)[:\s]*/i, "").replace(/\s*(Done|Complete|That'?s? it)\.?$/i, "").trim();
521
+ code = code.replace(/\s*\/\/ ← CLICKED ELEMENT STARTS/g, "").replace(/\s*\/\/ ← CLICKED ELEMENT ENDS/g, "").replace(/\s*\/\/ ← CLICKED ELEMENT \(single line\)/g, "").replace(/\s*\/\/ ← \(clicked element\)/g, "").trim();
522
+ if (!code) return "";
523
+ if (!isFullComponent) {
524
+ const lines = code.split("\n");
525
+ const firstLineIndent = ((_b = (_a = lines[0]) == null ? void 0 : _a.match(/^(\s*)/)) == null ? void 0 : _b[1]) || "";
526
+ const indentDiff = baseIndentation.length - firstLineIndent.length;
527
+ if (indentDiff !== 0) {
528
+ for (let i = 0; i < lines.length; i++) {
529
+ if (lines[i].trim()) {
530
+ const currentIndent = ((_c = lines[i].match(/^(\s*)/)) == null ? void 0 : _c[1]) || "";
531
+ const newIndentLength = Math.max(
532
+ 0,
533
+ currentIndent.length + indentDiff
534
+ );
535
+ lines[i] = " ".repeat(newIndentLength) + lines[i].trimStart();
536
+ }
537
+ }
538
+ code = lines.join("\n");
539
+ }
540
+ }
541
+ return marker + code;
542
+ }
543
+ function validateGeneratedCode(newCode, originalCode, fileContent) {
544
+ try {
545
+ const isFullComponent = /^(export\s+)?(default\s+)?function\s+\w+/.test(newCode.trim()) || /^(export\s+)?(default\s+)?const\s+\w+\s*=/.test(newCode.trim());
546
+ if (isFullComponent) {
547
+ let codeToValidate = newCode;
548
+ if (fileContent) {
549
+ const interfaceMatches = fileContent.match(/^(interface|type)\s+\w+[^}]*\}/gm);
550
+ if (interfaceMatches) {
551
+ codeToValidate = interfaceMatches.join("\n\n") + "\n\n" + newCode;
552
+ }
553
+ }
554
+ parser__namespace.parse(codeToValidate, {
555
+ sourceType: "module",
556
+ plugins: ["jsx", "typescript"]
557
+ });
558
+ } else {
559
+ const wrapped = `function _() { return (${newCode}); }`;
560
+ parser__namespace.parse(wrapped, {
561
+ sourceType: "module",
562
+ plugins: ["jsx", "typescript"]
563
+ });
564
+ }
565
+ } catch (e) {
566
+ console.error("Generated code parse error:", e);
567
+ return false;
568
+ }
569
+ const origBraces = (originalCode.match(/[{}]/g) || []).length;
570
+ const newBraces = (newCode.match(/[{}]/g) || []).length;
571
+ const origTags = (originalCode.match(/[<>]/g) || []).length;
572
+ const newTags = (newCode.match(/[<>]/g) || []).length;
573
+ if (Math.abs(origBraces - newBraces) > 4 || Math.abs(origTags - newTags) > 4) {
574
+ console.warn(
575
+ `Structure changed significantly: braces ${origBraces}->${newBraces}, tags ${origTags}->${newTags}`
576
+ );
577
+ }
578
+ return true;
579
+ }
580
+ function parseDebugStack(stack) {
581
+ if (!stack) return null;
582
+ const stackStr = typeof stack === "string" ? stack : stack.stack || String(stack);
583
+ const frames = stackStr.split("\n");
584
+ const skipPatterns = [
585
+ "node_modules",
586
+ "SegmentViewNode",
587
+ "LayoutRouter",
588
+ "ErrorBoundary",
589
+ "fakeJSXCallSite"
590
+ ];
591
+ for (const frame of frames) {
592
+ if (skipPatterns.some((p) => frame.includes(p))) continue;
593
+ const match = frame.match(/at\s+(\w+)\s+\((.+?):(\d+):(\d+)\)?$/);
594
+ if (match) {
595
+ let filePath = match[2];
596
+ const line = parseInt(match[3], 10);
597
+ const column = parseInt(match[4], 10);
598
+ let chunkId;
599
+ const chunkMatch = filePath.match(/\?([^:]+)$/);
600
+ if (chunkMatch) {
601
+ chunkId = chunkMatch[1];
602
+ filePath = filePath.replace(/\?[^:]*$/, "");
603
+ }
604
+ filePath = cleanPathTurbopack(filePath);
605
+ if (!shouldSkip(filePath)) {
606
+ console.log("parseDebugStack extracted:", { filePath, line, column });
607
+ return { filePath, line, column, chunkId };
608
+ }
609
+ }
610
+ }
611
+ console.log(
612
+ "parseDebugStack: no valid frame found in stack:",
613
+ stackStr.substring(0, 200)
614
+ );
615
+ return null;
616
+ }
617
+ function cleanPathTurbopack(p) {
618
+ let cleaned = p;
619
+ cleaned = cleaned.replace(/^about:\/\/React\/Server\//, "");
620
+ if (cleaned.startsWith("file://")) {
621
+ cleaned = cleaned.replace(/^file:\/\//, "");
622
+ }
623
+ try {
624
+ if (cleaned.includes("%")) {
625
+ cleaned = decodeURIComponent(cleaned);
626
+ }
627
+ } catch (e) {
628
+ console.warn("Failed to decode URL-encoded path:", cleaned, e);
629
+ }
630
+ cleaned = cleaned.replace(/^webpack-internal:\/\/\/\([^)]+\)\/\.\//, "").replace(/^webpack-internal:\/\/\/\([^)]+\)\//, "").replace(/^webpack-internal:\/\//, "").replace(/^webpack:\/\/[^/]*\//, "").replace(/^\([^)]+\)\//, "").replace(/\?.*$/, "");
631
+ if (cleaned.startsWith(".next/") || path.isAbsolute(cleaned)) {
632
+ return cleaned;
633
+ }
634
+ return cleaned;
635
+ }
636
+ function cleanPath(p) {
637
+ return cleanPathTurbopack(p);
638
+ }
639
+ function normalizeSourcePath(source, projectRoot) {
640
+ let cleaned = cleanPath(source);
641
+ cleaned = cleaned.replace(/\\/g, "/");
642
+ if (cleaned.startsWith(projectRoot)) {
643
+ cleaned = cleaned.substring(projectRoot.length + 1);
644
+ }
645
+ return cleaned.replace(/^\/+/, "");
646
+ }
647
+ function shouldSkip(p) {
648
+ if (!p) return true;
649
+ return ["node_modules", "next/dist", "react-dom"].some((s) => p.includes(s));
650
+ }
651
+ async function resolveOriginalPosition(compiledPos, projectRoot) {
652
+ try {
653
+ console.log("resolveOriginalPosition called with:", compiledPos);
654
+ let compiledFilePath = compiledPos.filePath;
655
+ compiledFilePath = cleanPathTurbopack(compiledFilePath);
656
+ console.log("After cleanPathTurbopack:", compiledFilePath);
657
+ if (compiledFilePath.startsWith("http://") || compiledFilePath.startsWith("https://")) {
658
+ const url = new URL(compiledFilePath);
659
+ const pathname = url.pathname;
660
+ if (pathname.startsWith("/_next/")) {
661
+ const relativePath = pathname.substring("/_next/".length);
662
+ compiledFilePath = path.join(projectRoot, ".next", "dev", relativePath);
663
+ } else {
664
+ console.warn("Unexpected HTTP URL path:", pathname);
665
+ return null;
666
+ }
667
+ } else if (!path.isAbsolute(compiledFilePath)) {
668
+ if (compiledFilePath.startsWith(".next/")) {
669
+ compiledFilePath = path.resolve(projectRoot, compiledFilePath);
670
+ } else {
671
+ const resolved = path.resolve(projectRoot, compiledFilePath);
672
+ if (await fileExists(resolved)) {
673
+ compiledFilePath = resolved;
674
+ } else {
675
+ const possiblePaths = [
676
+ path.join(projectRoot, ".next", "dev", compiledFilePath),
677
+ path.join(projectRoot, compiledFilePath)
678
+ ];
679
+ for (const tryPath of possiblePaths) {
680
+ if (await fileExists(tryPath)) {
681
+ compiledFilePath = tryPath;
682
+ break;
683
+ }
684
+ }
685
+ }
686
+ }
687
+ } else {
688
+ compiledFilePath = path.normalize(compiledFilePath);
689
+ }
690
+ console.log("Normalized compiled file path:", compiledFilePath);
691
+ const compiledExists = await fileExists(compiledFilePath);
692
+ console.log(
693
+ "Compiled file exists:",
694
+ compiledExists,
695
+ "at:",
696
+ compiledFilePath
697
+ );
698
+ if (!compiledExists) {
699
+ console.error(
700
+ `Compiled file not found: ${compiledFilePath} (from parsed: ${compiledPos.filePath})`
701
+ );
702
+ }
703
+ const sourceMapPath = compiledFilePath + ".map";
704
+ console.log("Looking for source map at:", sourceMapPath);
705
+ const sourceMapExists = await fileExists(sourceMapPath);
706
+ console.log("Source map exists:", sourceMapExists);
707
+ if (!sourceMapExists) {
708
+ console.error(
709
+ `Source map not found: ${sourceMapPath} (from compiled: ${compiledPos.filePath}, normalized: ${compiledFilePath})`
710
+ );
711
+ const altPaths = [
712
+ compiledFilePath.replace(/\.js$/, ".map"),
713
+ path.join(
714
+ path.dirname(compiledFilePath),
715
+ path.basename(compiledFilePath) + ".map"
716
+ )
717
+ ];
718
+ console.log("Trying alternative paths:", altPaths);
719
+ for (const altPath of altPaths) {
720
+ if (await fileExists(altPath)) {
721
+ console.log("Found source map at alternative path:", altPath);
722
+ return await resolveFromSourceMap(altPath, compiledPos, projectRoot);
723
+ }
724
+ }
725
+ return null;
726
+ }
727
+ return await resolveFromSourceMap(sourceMapPath, compiledPos, projectRoot);
728
+ } catch (error) {
729
+ console.error("Error resolving source map:", error);
730
+ return null;
731
+ }
732
+ }
733
+ async function resolveFromSourceMap(sourceMapPath, compiledPos, projectRoot) {
734
+ try {
735
+ const sourceMapContent = await fs.readFile(sourceMapPath, "utf-8");
736
+ const sourceMap$1 = JSON.parse(sourceMapContent);
737
+ if ((!sourceMap$1.sources || sourceMap$1.sources.length === 0) && (!sourceMap$1.sections || sourceMap$1.sections.length === 0)) {
738
+ console.warn(
739
+ "Empty source map detected, cannot resolve position:",
740
+ sourceMapPath
741
+ );
742
+ return null;
743
+ }
744
+ if (sourceMap$1.sections) {
745
+ let matchingSection = null;
746
+ for (let i = sourceMap$1.sections.length - 1; i >= 0; i--) {
747
+ const section = sourceMap$1.sections[i];
748
+ const offset = section.offset;
749
+ const sectionStartLine1Indexed = offset.line + 1;
750
+ if (compiledPos.line > sectionStartLine1Indexed || compiledPos.line === sectionStartLine1Indexed && compiledPos.column >= offset.column) {
751
+ matchingSection = section;
752
+ break;
753
+ }
754
+ }
755
+ if (matchingSection && matchingSection.map) {
756
+ const sectionMap = matchingSection.map;
757
+ const offset = matchingSection.offset;
758
+ let consumer2;
759
+ try {
760
+ consumer2 = await new sourceMap.SourceMapConsumer(sectionMap);
761
+ } catch (error) {
762
+ console.error("Error creating SourceMapConsumer:", error);
763
+ return null;
764
+ }
765
+ try {
766
+ const adjustedLine = compiledPos.line - offset.line;
767
+ const adjustedColumn = compiledPos.line === offset.line + 1 ? compiledPos.column - offset.column : compiledPos.column;
768
+ const originalPos = consumer2.originalPositionFor({
769
+ line: adjustedLine,
770
+ column: adjustedColumn
771
+ });
772
+ if (originalPos.source && originalPos.line !== null) {
773
+ const source = normalizeSourcePath(
774
+ originalPos.source || "",
775
+ projectRoot
776
+ );
777
+ return {
778
+ source,
779
+ line: originalPos.line,
780
+ column: originalPos.column ?? 0
781
+ };
782
+ }
783
+ } finally {
784
+ consumer2.destroy();
785
+ }
786
+ }
787
+ return null;
788
+ }
789
+ const consumer = await new sourceMap.SourceMapConsumer(sourceMap$1);
790
+ try {
791
+ const originalPos = consumer.originalPositionFor({
792
+ line: compiledPos.line,
793
+ column: compiledPos.column
794
+ });
795
+ if (originalPos.source && originalPos.line !== null) {
796
+ const source = normalizeSourcePath(
797
+ originalPos.source || "",
798
+ projectRoot
799
+ );
800
+ return {
801
+ source,
802
+ line: originalPos.line,
803
+ column: originalPos.column ?? 0
804
+ };
805
+ }
806
+ } finally {
807
+ consumer.destroy();
808
+ }
809
+ return null;
810
+ } catch (error) {
811
+ console.error("Error resolving source map:", error);
812
+ return null;
813
+ }
814
+ }
815
+ async function fileExists(filePath) {
816
+ try {
817
+ await fs.access(filePath);
818
+ return true;
819
+ } catch {
820
+ return false;
821
+ }
822
+ }
823
+ async function getOriginalPositionFromDebugStack(debugStack, projectRoot) {
824
+ const compiledPos = parseDebugStack(debugStack);
825
+ if (!compiledPos) return null;
826
+ return await resolveOriginalPosition(compiledPos, projectRoot);
827
+ }
828
+ async function handleRead(req) {
829
+ var _a;
830
+ const devModeError = validateDevMode();
831
+ if (devModeError) return devModeError;
832
+ const { searchParams } = new URL(req.url);
833
+ let filePath = searchParams.get("path") || "";
834
+ let lineNumber = parseInt(searchParams.get("line") || "1");
835
+ const debugStack = searchParams.get("debugStack") || "";
836
+ const tagName = searchParams.get("tagName") || "";
837
+ const nthOfType = parseInt(searchParams.get("nthOfType") || "0");
838
+ const textContent = searchParams.get("textContent") || "";
839
+ const className = searchParams.get("className") || "";
840
+ const elementContext = tagName ? {
841
+ tagName,
842
+ nthOfType: nthOfType > 0 ? nthOfType : void 0,
843
+ textContent: textContent || void 0,
844
+ className: className || void 0
845
+ } : void 0;
846
+ const projectRoot = process.cwd();
847
+ if (debugStack) {
848
+ const compiledPos = parseDebugStack(debugStack);
849
+ if (compiledPos) {
850
+ const originalPos = await resolveOriginalPosition(
851
+ compiledPos,
852
+ projectRoot
853
+ );
854
+ if (originalPos) {
855
+ filePath = originalPos.source;
856
+ lineNumber = originalPos.line;
857
+ }
858
+ }
859
+ }
860
+ const normalizedPath = normalizePath(filePath);
861
+ const absolutePath = await resolveFilePath(projectRoot, normalizedPath);
862
+ if (!absolutePath) {
863
+ return server.NextResponse.json(
864
+ { success: false, error: "File not found" },
865
+ { status: 404 }
866
+ );
867
+ }
868
+ const content = await fs.readFile(absolutePath, "utf-8");
869
+ const ast = parseFile(content);
870
+ if (!ast) {
871
+ return server.NextResponse.json(
872
+ { success: false, error: "Failed to parse file" },
873
+ { status: 400 }
874
+ );
875
+ }
876
+ let componentName = "";
877
+ traverse(ast, {
878
+ ExportDefaultDeclaration(path2) {
879
+ var _a2;
880
+ if (t__namespace.isFunctionDeclaration(path2.node.declaration)) {
881
+ componentName = ((_a2 = path2.node.declaration.id) == null ? void 0 : _a2.name) || "";
882
+ }
883
+ },
884
+ ExportNamedDeclaration(path2) {
885
+ var _a2;
886
+ if (t__namespace.isFunctionDeclaration(path2.node.declaration)) {
887
+ componentName = ((_a2 = path2.node.declaration.id) == null ? void 0 : _a2.name) || "";
888
+ }
889
+ }
890
+ });
891
+ if (!componentName) {
892
+ return server.NextResponse.json({
893
+ success: true,
894
+ content,
895
+ lineStart: 1,
896
+ lineEnd: content.split("\n").length
897
+ });
898
+ }
899
+ const target = findTargetElement(ast, content, {
900
+ componentName,
901
+ lineNumber,
902
+ elementContext
903
+ });
904
+ if (!target) {
905
+ return server.NextResponse.json({
906
+ success: true,
907
+ content,
908
+ lineStart: 1,
909
+ lineEnd: content.split("\n").length
910
+ });
911
+ }
912
+ console.log(`[/read] Found element:`);
913
+ console.log(
914
+ ` Component: ${componentName} (lines ${target.componentStart}-${target.componentEnd})`
915
+ );
916
+ console.log(` Target element: lines ${target.startLine}-${target.endLine}`);
917
+ console.log(
918
+ ` Element context: tagName=${elementContext == null ? void 0 : elementContext.tagName}, nthOfType=${elementContext == null ? void 0 : elementContext.nthOfType}`
919
+ );
920
+ const foundLines = content.split("\n").slice(
921
+ target.startLine - 1,
922
+ Math.min(target.startLine + 2, target.endLine)
923
+ );
924
+ console.log(` Preview: ${(_a = foundLines[0]) == null ? void 0 : _a.trim()}`);
925
+ console.log(
926
+ ` textContent="${elementContext == null ? void 0 : elementContext.textContent}", className="${elementContext == null ? void 0 : elementContext.className}"`
927
+ );
928
+ const lines = content.split("\n");
929
+ const componentLines = lines.slice(
930
+ target.componentStart - 1,
931
+ target.componentEnd
932
+ );
933
+ const preview = componentLines.join("\n");
934
+ return server.NextResponse.json({
935
+ success: true,
936
+ content: preview,
937
+ lineStart: target.componentStart,
938
+ lineEnd: target.componentEnd,
939
+ targetStartLine: target.startLine,
940
+ targetEndLine: target.endLine,
941
+ componentName
942
+ // Return the actual component name parsed from code
943
+ });
944
+ }
945
+ async function handleUndo(req) {
946
+ const devModeError = validateDevMode();
947
+ if (devModeError) return devModeError;
948
+ try {
949
+ const body = await req.json();
950
+ const { filePath, content } = body;
951
+ if (!filePath || typeof content !== "string") {
952
+ return server.NextResponse.json(
953
+ {
954
+ success: false,
955
+ error: "Invalid request: filePath and content required"
956
+ },
957
+ { status: 400 }
958
+ );
959
+ }
960
+ const projectRoot = process.cwd();
961
+ const normalizedPath = normalizePath(filePath);
962
+ const absolutePath = await resolveFilePath(projectRoot, normalizedPath);
963
+ if (!absolutePath) {
964
+ return server.NextResponse.json(
965
+ { success: false, error: `File not found: ${normalizedPath}` },
966
+ { status: 404 }
967
+ );
968
+ }
969
+ if (!isPathSecure(absolutePath, projectRoot)) {
970
+ return server.NextResponse.json(
971
+ { success: false, error: "Access denied: File outside project root" },
972
+ { status: 403 }
973
+ );
974
+ }
975
+ await fs.writeFile(absolutePath, content, "utf-8");
976
+ console.log(`✅ Undo: Restored ${normalizedPath}`);
977
+ return server.NextResponse.json({ success: true });
978
+ } catch (error) {
979
+ console.error("Undo error:", error);
980
+ return server.NextResponse.json(
981
+ { success: false, error: String(error) },
982
+ { status: 500 }
983
+ );
984
+ }
985
+ }
986
+ async function handleResolve(req) {
987
+ const devModeError = validateDevMode();
988
+ if (devModeError) return devModeError;
989
+ try {
990
+ const body = await req.json();
991
+ const debugStack = body == null ? void 0 : body.debugStack;
992
+ if (!debugStack || typeof debugStack !== "string") {
993
+ return server.NextResponse.json(
994
+ { success: false, error: "Missing debugStack" },
995
+ { status: 400 }
996
+ );
997
+ }
998
+ const compiledPos = parseDebugStack(debugStack);
999
+ if (!compiledPos) {
1000
+ console.error("Could not parse debug stack:", debugStack);
1001
+ return server.NextResponse.json(
1002
+ { success: false, error: "Could not parse stack" },
1003
+ { status: 422 }
1004
+ );
1005
+ }
1006
+ console.log("Parsed compiled position:", compiledPos);
1007
+ const originalPos = await resolveOriginalPosition(
1008
+ compiledPos,
1009
+ process.cwd()
1010
+ );
1011
+ if (!originalPos) {
1012
+ console.error(
1013
+ "Source map lookup failed for:",
1014
+ compiledPos.filePath,
1015
+ "at line",
1016
+ compiledPos.line
1017
+ );
1018
+ return server.NextResponse.json(
1019
+ { success: false, error: "Source map lookup failed" },
1020
+ { status: 404 }
1021
+ );
1022
+ }
1023
+ console.log("Resolved original position:", originalPos);
1024
+ return server.NextResponse.json({
1025
+ success: true,
1026
+ filePath: originalPos.source,
1027
+ lineNumber: originalPos.line,
1028
+ columnNumber: originalPos.column ?? 0
1029
+ });
1030
+ } catch (error) {
1031
+ console.error("Source resolve error:", error);
1032
+ return server.NextResponse.json(
1033
+ { success: false, error: "Internal error" },
1034
+ { status: 500 }
1035
+ );
1036
+ }
1037
+ }
1038
+ async function handleAbsolutePath(req) {
1039
+ const devModeError = validateDevMode();
1040
+ if (devModeError) return devModeError;
1041
+ const { searchParams } = new URL(req.url);
1042
+ const filePath = searchParams.get("path") || "";
1043
+ if (!filePath) {
1044
+ return server.NextResponse.json(
1045
+ { success: false, error: "Missing path" },
1046
+ { status: 400 }
1047
+ );
1048
+ }
1049
+ const projectRoot = process.cwd();
1050
+ const normalizedPath = normalizePath(filePath);
1051
+ const absolutePath = await resolveFilePath(projectRoot, normalizedPath);
1052
+ if (!absolutePath) {
1053
+ return server.NextResponse.json(
1054
+ { success: false, error: "File not found" },
1055
+ { status: 404 }
1056
+ );
1057
+ }
1058
+ return server.NextResponse.json({
1059
+ success: true,
1060
+ absolutePath
1061
+ });
1062
+ }
1063
+ async function handleValidateSession(req) {
1064
+ const devModeError = validateDevMode();
1065
+ if (devModeError) return devModeError;
1066
+ try {
1067
+ const body = await req.json();
1068
+ const { filePath, lastKnownHash, lastKnownSize } = body;
1069
+ if (!filePath || !lastKnownHash || lastKnownSize === void 0) {
1070
+ return server.NextResponse.json(
1071
+ { success: false, error: "Missing required fields" },
1072
+ { status: 400 }
1073
+ );
1074
+ }
1075
+ const projectRoot = process.cwd();
1076
+ const normalizedPath = normalizePath(filePath);
1077
+ const absolutePath = await resolveFilePath(projectRoot, normalizedPath);
1078
+ if (!absolutePath) {
1079
+ return server.NextResponse.json(
1080
+ { success: false, error: "File not found" },
1081
+ { status: 404 }
1082
+ );
1083
+ }
1084
+ if (!isPathSecure(absolutePath, projectRoot)) {
1085
+ return server.NextResponse.json(
1086
+ { success: false, error: "Access denied" },
1087
+ { status: 403 }
1088
+ );
1089
+ }
1090
+ const content = await fs.readFile(absolutePath, "utf-8");
1091
+ const hash = crypto.createHash("sha256").update(content).digest("hex");
1092
+ const stats = await fs.stat(absolutePath);
1093
+ const isValid = hash === lastKnownHash && content.length === lastKnownSize;
1094
+ console.log(`[validate-session] ${filePath}:`);
1095
+ console.log(` Hash match: ${hash === lastKnownHash}`);
1096
+ console.log(` Size match: ${content.length === lastKnownSize}`);
1097
+ console.log(` Is valid: ${isValid}`);
1098
+ return server.NextResponse.json({
1099
+ success: true,
1100
+ isValid,
1101
+ currentHash: hash,
1102
+ currentSize: content.length,
1103
+ lastModifiedTime: stats.mtimeMs
1104
+ });
1105
+ } catch (error) {
1106
+ console.error("Validation error:", error);
1107
+ return server.NextResponse.json(
1108
+ { success: false, error: String(error) },
1109
+ { status: 500 }
1110
+ );
1111
+ }
1112
+ }
1113
+ async function handleAIEditorRequest(req, context) {
1114
+ const { path: path2 } = await context.params;
1115
+ const endpoint = path2[0];
1116
+ const method = req.method;
1117
+ switch (endpoint) {
1118
+ case "edit":
1119
+ if (method === "POST") return handleEdit(req);
1120
+ break;
1121
+ case "read":
1122
+ if (method === "GET") return handleRead(req);
1123
+ break;
1124
+ case "undo":
1125
+ if (method === "POST") return handleUndo(req);
1126
+ break;
1127
+ case "resolve":
1128
+ if (method === "POST") return handleResolve(req);
1129
+ break;
1130
+ case "absolute-path":
1131
+ if (method === "GET") return handleAbsolutePath(req);
1132
+ break;
1133
+ case "validate-session":
1134
+ if (method === "POST") return handleValidateSession(req);
1135
+ break;
1136
+ }
1137
+ return server.NextResponse.json(
1138
+ { error: `Unknown endpoint: ${endpoint}` },
1139
+ { status: 404 }
1140
+ );
1141
+ }
1142
+ exports.fileExists = fileExists$1;
1143
+ exports.findTargetElement = findTargetElement;
1144
+ exports.getAttributeValue = getAttributeValue;
1145
+ exports.getJSXMemberName = getJSXMemberName;
1146
+ exports.getOriginalPositionFromDebugStack = getOriginalPositionFromDebugStack;
1147
+ exports.handleAIEditorRequest = handleAIEditorRequest;
1148
+ exports.handleAbsolutePath = handleAbsolutePath;
1149
+ exports.handleEdit = handleEdit;
1150
+ exports.handleRead = handleRead;
1151
+ exports.handleResolve = handleResolve;
1152
+ exports.handleUndo = handleUndo;
1153
+ exports.handleValidateSession = handleValidateSession;
1154
+ exports.isPathSecure = isPathSecure;
1155
+ exports.normalizePath = normalizePath;
1156
+ exports.parseDebugStack = parseDebugStack;
1157
+ exports.parseFile = parseFile;
1158
+ exports.resolveFilePath = resolveFilePath;
1159
+ exports.resolveOriginalPosition = resolveOriginalPosition;
1160
+ exports.scoreElementMatch = scoreElementMatch;
1161
+ exports.validateDevMode = validateDevMode;
1162
+ //# sourceMappingURL=index-DnoYi4f8.cjs.map