next-ai-editor 0.1.2 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/AIEditorProvider-CKA2K_g2.js +1428 -0
- package/dist/AIEditorProvider-CKA2K_g2.js.map +1 -0
- package/dist/AIEditorProvider-C_zRSAuV.cjs +1427 -0
- package/dist/AIEditorProvider-C_zRSAuV.cjs.map +1 -0
- package/dist/client/AIEditorProvider.d.ts +1 -33
- package/dist/client/AIEditorProvider.d.ts.map +1 -1
- package/dist/client/components/ChatPanel.d.ts +18 -0
- package/dist/client/components/ChatPanel.d.ts.map +1 -0
- package/dist/client/components/ControlPill.d.ts +8 -0
- package/dist/client/components/ControlPill.d.ts.map +1 -0
- package/dist/client/components/MessageItem.d.ts +7 -0
- package/dist/client/components/MessageItem.d.ts.map +1 -0
- package/dist/client/components/MessageList.d.ts +7 -0
- package/dist/client/components/MessageList.d.ts.map +1 -0
- package/dist/client/components/TaskHistoryPanel.d.ts +10 -0
- package/dist/client/components/TaskHistoryPanel.d.ts.map +1 -0
- package/dist/client/components/index.d.ts +11 -0
- package/dist/client/components/index.d.ts.map +1 -0
- package/dist/client/hooks/index.d.ts +3 -0
- package/dist/client/hooks/index.d.ts.map +1 -0
- package/dist/client/hooks/useChatStream.d.ts +66 -0
- package/dist/client/hooks/useChatStream.d.ts.map +1 -0
- package/dist/client/hooks/useHotReload.d.ts +10 -0
- package/dist/client/hooks/useHotReload.d.ts.map +1 -0
- package/dist/client/index.d.ts +3 -0
- package/dist/client/index.d.ts.map +1 -1
- package/dist/client.cjs +7 -1
- package/dist/client.cjs.map +1 -1
- package/dist/client.js +8 -2
- package/dist/client.js.map +1 -1
- package/dist/{index-3OMXRwpD.js → comments-D3m0RsOO.js} +505 -26
- package/dist/comments-D3m0RsOO.js.map +1 -0
- package/dist/{index-9QODCOgD.cjs → comments-Daur80r4.cjs} +492 -13
- package/dist/comments-Daur80r4.cjs.map +1 -0
- package/dist/index.cjs +34 -26
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +22 -14
- package/dist/index.js.map +1 -1
- package/dist/next-ai-editor.css +880 -0
- package/dist/server/agent/sdk-client.d.ts +54 -0
- package/dist/server/agent/sdk-client.d.ts.map +1 -0
- package/dist/server/agent/session-store.d.ts +101 -0
- package/dist/server/agent/session-store.d.ts.map +1 -0
- package/dist/server/handlers/chat.d.ts +6 -0
- package/dist/server/handlers/chat.d.ts.map +1 -0
- package/dist/server/handlers/comments.d.ts +10 -0
- package/dist/server/handlers/comments.d.ts.map +1 -0
- package/dist/server/handlers/index.d.ts +2 -1
- package/dist/server/handlers/index.d.ts.map +1 -1
- package/dist/server/handlers/read.d.ts.map +1 -1
- package/dist/server/index.d.ts +1 -0
- package/dist/server/index.d.ts.map +1 -1
- package/dist/server/utils/ast.d.ts.map +1 -1
- package/dist/server/utils/source-map.d.ts +5 -0
- package/dist/server/utils/source-map.d.ts.map +1 -1
- package/dist/server.cjs +27 -25
- package/dist/server.cjs.map +1 -1
- package/dist/server.js +14 -12
- package/dist/shared/comment-types.d.ts +140 -0
- package/dist/shared/comment-types.d.ts.map +1 -0
- package/package.json +13 -4
- package/dist/AIEditorProvider-CFFnEtEB.js +0 -2170
- package/dist/AIEditorProvider-CFFnEtEB.js.map +0 -1
- package/dist/AIEditorProvider-CmiACRfw.cjs +0 -2169
- package/dist/AIEditorProvider-CmiACRfw.cjs.map +0 -1
- package/dist/index-3OMXRwpD.js.map +0 -1
- package/dist/index-9QODCOgD.cjs.map +0 -1
|
@@ -1,14 +1,16 @@
|
|
|
1
1
|
import { NextResponse } from "next/server";
|
|
2
|
-
import fs from "fs/promises";
|
|
2
|
+
import fs, { readFile, writeFile, mkdir } from "fs/promises";
|
|
3
3
|
import { ChatAnthropic } from "@langchain/anthropic";
|
|
4
4
|
import { SystemMessage, HumanMessage } from "@langchain/core/messages";
|
|
5
|
-
import path from "path";
|
|
5
|
+
import path, { join } from "path";
|
|
6
6
|
import * as parser from "@babel/parser";
|
|
7
7
|
import traverse from "@babel/traverse";
|
|
8
8
|
import * as t from "@babel/types";
|
|
9
9
|
import { SourceMapConsumer } from "@jridgewell/source-map";
|
|
10
10
|
import { c as cleanPath, s as shouldSkipPath, n as normalizeSourcePath } from "./path-utils-Bai2xKx9.js";
|
|
11
11
|
import crypto from "crypto";
|
|
12
|
+
import { unstable_v2_resumeSession, unstable_v2_createSession } from "@anthropic-ai/claude-agent-sdk";
|
|
13
|
+
import { existsSync } from "fs";
|
|
12
14
|
function validateDevMode() {
|
|
13
15
|
if (process.env.NODE_ENV !== "development") {
|
|
14
16
|
return NextResponse.json(
|
|
@@ -88,6 +90,11 @@ function extractComponentName(ast) {
|
|
|
88
90
|
if (t.isIdentifier(declarator.id)) {
|
|
89
91
|
componentName = declarator.id.name;
|
|
90
92
|
}
|
|
93
|
+
} else if (path2.node.specifiers && path2.node.specifiers.length > 0) {
|
|
94
|
+
const specifier = path2.node.specifiers[0];
|
|
95
|
+
if (t.isExportSpecifier(specifier) && t.isIdentifier(specifier.exported)) {
|
|
96
|
+
componentName = specifier.exported.name;
|
|
97
|
+
}
|
|
91
98
|
}
|
|
92
99
|
}
|
|
93
100
|
});
|
|
@@ -239,16 +246,34 @@ function findTargetElement(ast, fileContent, options) {
|
|
|
239
246
|
componentEnd
|
|
240
247
|
};
|
|
241
248
|
}
|
|
242
|
-
if (
|
|
249
|
+
if (elementContext == null ? void 0 : elementContext.tagName) {
|
|
243
250
|
const allOfTag = allElementsByTag.get(elementContext.tagName);
|
|
244
|
-
if (allOfTag && allOfTag.length
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
251
|
+
if (allOfTag && allOfTag.length > 0) {
|
|
252
|
+
if (elementContext.textContent || elementContext.className) {
|
|
253
|
+
for (const elem of allOfTag) {
|
|
254
|
+
if (t.isJSXElement(elem.node)) {
|
|
255
|
+
elem.score = scoreElementMatch(elem.node, elementContext, fileContent);
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
allOfTag.sort((a, b) => b.score - a.score);
|
|
259
|
+
if (allOfTag[0].score > 50) {
|
|
260
|
+
return {
|
|
261
|
+
startLine: allOfTag[0].startLine,
|
|
262
|
+
endLine: allOfTag[0].endLine,
|
|
263
|
+
componentStart,
|
|
264
|
+
componentEnd
|
|
265
|
+
};
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
if (elementContext.nthOfType && allOfTag.length >= elementContext.nthOfType) {
|
|
269
|
+
const target = allOfTag[elementContext.nthOfType - 1];
|
|
270
|
+
return {
|
|
271
|
+
startLine: target.startLine,
|
|
272
|
+
endLine: target.endLine,
|
|
273
|
+
componentStart,
|
|
274
|
+
componentEnd
|
|
275
|
+
};
|
|
276
|
+
}
|
|
252
277
|
}
|
|
253
278
|
}
|
|
254
279
|
const nearbyElements = [];
|
|
@@ -695,6 +720,30 @@ function parseDebugStack(stack) {
|
|
|
695
720
|
);
|
|
696
721
|
return null;
|
|
697
722
|
}
|
|
723
|
+
function extractComponentNameFromStack(stack) {
|
|
724
|
+
if (!stack) return null;
|
|
725
|
+
const stackStr = typeof stack === "string" ? stack : stack.stack || String(stack);
|
|
726
|
+
const frames = stackStr.split("\n");
|
|
727
|
+
const skipPatterns = [
|
|
728
|
+
"node_modules",
|
|
729
|
+
"SegmentViewNode",
|
|
730
|
+
"LayoutRouter",
|
|
731
|
+
"ErrorBoundary",
|
|
732
|
+
"fakeJSXCallSite",
|
|
733
|
+
"react_stack_bottom_frame"
|
|
734
|
+
];
|
|
735
|
+
for (const frame of frames) {
|
|
736
|
+
if (skipPatterns.some((p) => frame.includes(p))) continue;
|
|
737
|
+
const match = frame.match(/at\s+(\w+)\s+\(/);
|
|
738
|
+
if (match && match[1]) {
|
|
739
|
+
const componentName = match[1];
|
|
740
|
+
if (componentName !== "Object" && componentName !== "anonymous") {
|
|
741
|
+
return componentName;
|
|
742
|
+
}
|
|
743
|
+
}
|
|
744
|
+
}
|
|
745
|
+
return null;
|
|
746
|
+
}
|
|
698
747
|
function parseDebugStackFrames(stack) {
|
|
699
748
|
if (!stack) return [];
|
|
700
749
|
const stackStr = typeof stack === "string" ? stack : stack.stack || String(stack);
|
|
@@ -995,7 +1044,10 @@ async function handleRead(req) {
|
|
|
995
1044
|
let resolvedParentPath = parentFilePath;
|
|
996
1045
|
let resolvedParentLine = parentLine;
|
|
997
1046
|
let resolvedParentComponentName = parentComponentName;
|
|
998
|
-
if (resolvedParentComponentName && parentDebugStack) {
|
|
1047
|
+
if (!resolvedParentComponentName && parentDebugStack) {
|
|
1048
|
+
resolvedParentComponentName = extractComponentNameFromStack(parentDebugStack) || "";
|
|
1049
|
+
}
|
|
1050
|
+
if (parentDebugStack) {
|
|
999
1051
|
const compiledPos = parseDebugStack(parentDebugStack);
|
|
1000
1052
|
if (compiledPos) {
|
|
1001
1053
|
const originalPos = await resolveOriginalPosition(
|
|
@@ -1027,12 +1079,14 @@ async function handleRead(req) {
|
|
|
1027
1079
|
const parentTarget = findTargetElement(parentAst, parentContent, {
|
|
1028
1080
|
componentName: resolvedParentComponentName,
|
|
1029
1081
|
lineNumber: 0,
|
|
1030
|
-
// Don't use line number - rely on
|
|
1082
|
+
// Don't use line number - rely on element context to find correct instance
|
|
1031
1083
|
elementContext: {
|
|
1032
1084
|
tagName: componentName,
|
|
1033
1085
|
// Search for child component usage
|
|
1034
|
-
nthOfType: nthOfType2
|
|
1086
|
+
nthOfType: nthOfType2,
|
|
1035
1087
|
// Find specific instance if key is numeric
|
|
1088
|
+
textContent: textContent || void 0
|
|
1089
|
+
// Use text content to match the specific instance
|
|
1036
1090
|
}
|
|
1037
1091
|
});
|
|
1038
1092
|
if (parentTarget) {
|
|
@@ -1054,6 +1108,7 @@ async function handleRead(req) {
|
|
|
1054
1108
|
}
|
|
1055
1109
|
}
|
|
1056
1110
|
} catch (error) {
|
|
1111
|
+
console.error("Error resolving parent instance:", error);
|
|
1057
1112
|
}
|
|
1058
1113
|
}
|
|
1059
1114
|
return NextResponse.json({
|
|
@@ -1461,6 +1516,361 @@ Generate 6-8 initial suggestions:`;
|
|
|
1461
1516
|
return null;
|
|
1462
1517
|
}
|
|
1463
1518
|
}
|
|
1519
|
+
class AIEditorAgent {
|
|
1520
|
+
constructor(session, projectRoot) {
|
|
1521
|
+
this.session = session;
|
|
1522
|
+
this.projectRoot = projectRoot;
|
|
1523
|
+
}
|
|
1524
|
+
/**
|
|
1525
|
+
* Create a new agent instance
|
|
1526
|
+
*/
|
|
1527
|
+
static async create(config) {
|
|
1528
|
+
const sessionOptions = {
|
|
1529
|
+
model: "claude-sonnet-4-5-20250929",
|
|
1530
|
+
// Enable auto-apply for edits without permission prompts
|
|
1531
|
+
permissionMode: "acceptEdits",
|
|
1532
|
+
env: {
|
|
1533
|
+
...process.env,
|
|
1534
|
+
ANTHROPIC_API_KEY: config.apiKey,
|
|
1535
|
+
// Set working directory
|
|
1536
|
+
PWD: config.projectRoot
|
|
1537
|
+
},
|
|
1538
|
+
// Explicitly allow core development tools
|
|
1539
|
+
allowedTools: config.allowedTools || ["Read", "Edit", "Glob", "Grep"],
|
|
1540
|
+
disallowedTools: config.disallowedTools || []
|
|
1541
|
+
};
|
|
1542
|
+
let session;
|
|
1543
|
+
if (config.sessionId) {
|
|
1544
|
+
session = unstable_v2_resumeSession(config.sessionId, sessionOptions);
|
|
1545
|
+
} else {
|
|
1546
|
+
session = unstable_v2_createSession(sessionOptions);
|
|
1547
|
+
}
|
|
1548
|
+
return new AIEditorAgent(session, config.projectRoot);
|
|
1549
|
+
}
|
|
1550
|
+
/**
|
|
1551
|
+
* Send a message to the agent and stream responses
|
|
1552
|
+
*/
|
|
1553
|
+
async *sendMessage(message, componentContext) {
|
|
1554
|
+
let fullMessage = `<response_style>
|
|
1555
|
+
KEEP RESPONSES EXTREMELY BRIEF AND STATUS-LIKE.
|
|
1556
|
+
- Use terse status messages: "Reading file.tsx", "Changing color", "Done"
|
|
1557
|
+
- NO conversational language (avoid: "I'll", "Let me", "Sure", "Great", "I can see")
|
|
1558
|
+
- NO explanations unless explicitly asked
|
|
1559
|
+
- State ONLY what you're doing, nothing more
|
|
1560
|
+
- Format: Action + target (e.g., "Updating StatsCard.tsx:24")
|
|
1561
|
+
</response_style>
|
|
1562
|
+
|
|
1563
|
+
`;
|
|
1564
|
+
if (componentContext) {
|
|
1565
|
+
fullMessage += `[Component: ${componentContext.componentName} at ${componentContext.filePath}:${componentContext.lineNumber}]
|
|
1566
|
+
|
|
1567
|
+
`;
|
|
1568
|
+
}
|
|
1569
|
+
fullMessage += message;
|
|
1570
|
+
await this.session.send(fullMessage);
|
|
1571
|
+
for await (const event of this.session.stream()) {
|
|
1572
|
+
yield event;
|
|
1573
|
+
}
|
|
1574
|
+
}
|
|
1575
|
+
/**
|
|
1576
|
+
* Get the session ID
|
|
1577
|
+
*/
|
|
1578
|
+
getSessionId() {
|
|
1579
|
+
return this.session.sessionId;
|
|
1580
|
+
}
|
|
1581
|
+
/**
|
|
1582
|
+
* Close the session
|
|
1583
|
+
*/
|
|
1584
|
+
close() {
|
|
1585
|
+
this.session.close();
|
|
1586
|
+
}
|
|
1587
|
+
/**
|
|
1588
|
+
* Async disposal support
|
|
1589
|
+
*/
|
|
1590
|
+
async [Symbol.asyncDispose]() {
|
|
1591
|
+
await this.session[Symbol.asyncDispose]();
|
|
1592
|
+
}
|
|
1593
|
+
}
|
|
1594
|
+
class AgentSessionStore {
|
|
1595
|
+
// 5 minutes
|
|
1596
|
+
constructor() {
|
|
1597
|
+
this.sessions = /* @__PURE__ */ new Map();
|
|
1598
|
+
this.SESSION_TIMEOUT = 1e3 * 60 * 30;
|
|
1599
|
+
this.CLEANUP_INTERVAL = 1e3 * 60 * 5;
|
|
1600
|
+
this.startCleanupTimer();
|
|
1601
|
+
}
|
|
1602
|
+
/**
|
|
1603
|
+
* Create or resume a session
|
|
1604
|
+
*/
|
|
1605
|
+
async createSession(config) {
|
|
1606
|
+
const sessionId = config.sessionId || this.generateSessionId();
|
|
1607
|
+
const existing = this.sessions.get(sessionId);
|
|
1608
|
+
if (existing) {
|
|
1609
|
+
existing.lastActive = Date.now();
|
|
1610
|
+
existing.threadId = config.threadId;
|
|
1611
|
+
return existing;
|
|
1612
|
+
}
|
|
1613
|
+
const agent = await AIEditorAgent.create({
|
|
1614
|
+
apiKey: config.apiKey,
|
|
1615
|
+
projectRoot: config.projectRoot,
|
|
1616
|
+
sessionId: config.sessionId,
|
|
1617
|
+
allowedTools: config.allowedTools,
|
|
1618
|
+
disallowedTools: config.disallowedTools
|
|
1619
|
+
});
|
|
1620
|
+
const session = {
|
|
1621
|
+
sessionId,
|
|
1622
|
+
threadId: config.threadId,
|
|
1623
|
+
agent,
|
|
1624
|
+
createdAt: Date.now(),
|
|
1625
|
+
lastActive: Date.now(),
|
|
1626
|
+
isLocked: false
|
|
1627
|
+
};
|
|
1628
|
+
this.sessions.set(sessionId, session);
|
|
1629
|
+
return session;
|
|
1630
|
+
}
|
|
1631
|
+
/**
|
|
1632
|
+
* Get a session by ID
|
|
1633
|
+
*/
|
|
1634
|
+
getSession(sessionId) {
|
|
1635
|
+
const session = this.sessions.get(sessionId);
|
|
1636
|
+
if (session) {
|
|
1637
|
+
session.lastActive = Date.now();
|
|
1638
|
+
}
|
|
1639
|
+
return session;
|
|
1640
|
+
}
|
|
1641
|
+
/**
|
|
1642
|
+
* Acquire a lock on a session to prevent concurrent edits
|
|
1643
|
+
* Returns true if lock was acquired, false if already locked
|
|
1644
|
+
*/
|
|
1645
|
+
acquireLock(sessionId) {
|
|
1646
|
+
const session = this.sessions.get(sessionId);
|
|
1647
|
+
if (!session) {
|
|
1648
|
+
throw new Error(`Session ${sessionId} not found`);
|
|
1649
|
+
}
|
|
1650
|
+
if (session.isLocked) {
|
|
1651
|
+
return false;
|
|
1652
|
+
}
|
|
1653
|
+
session.isLocked = true;
|
|
1654
|
+
session.lastActive = Date.now();
|
|
1655
|
+
return true;
|
|
1656
|
+
}
|
|
1657
|
+
/**
|
|
1658
|
+
* Release a lock on a session
|
|
1659
|
+
*/
|
|
1660
|
+
releaseLock(sessionId) {
|
|
1661
|
+
const session = this.sessions.get(sessionId);
|
|
1662
|
+
if (session) {
|
|
1663
|
+
session.isLocked = false;
|
|
1664
|
+
session.lastActive = Date.now();
|
|
1665
|
+
}
|
|
1666
|
+
}
|
|
1667
|
+
/**
|
|
1668
|
+
* Check if any session is currently locked (to prevent concurrent edits)
|
|
1669
|
+
*/
|
|
1670
|
+
hasActiveLock() {
|
|
1671
|
+
for (const session of this.sessions.values()) {
|
|
1672
|
+
if (session.isLocked) {
|
|
1673
|
+
return true;
|
|
1674
|
+
}
|
|
1675
|
+
}
|
|
1676
|
+
return false;
|
|
1677
|
+
}
|
|
1678
|
+
/**
|
|
1679
|
+
* Get all active sessions for a thread
|
|
1680
|
+
*/
|
|
1681
|
+
getSessionsByThread(threadId) {
|
|
1682
|
+
const sessions = [];
|
|
1683
|
+
for (const session of this.sessions.values()) {
|
|
1684
|
+
if (session.threadId === threadId) {
|
|
1685
|
+
sessions.push(session);
|
|
1686
|
+
}
|
|
1687
|
+
}
|
|
1688
|
+
return sessions;
|
|
1689
|
+
}
|
|
1690
|
+
/**
|
|
1691
|
+
* Delete a session
|
|
1692
|
+
*/
|
|
1693
|
+
deleteSession(sessionId) {
|
|
1694
|
+
const session = this.sessions.get(sessionId);
|
|
1695
|
+
if (session) {
|
|
1696
|
+
session.agent.close();
|
|
1697
|
+
this.sessions.delete(sessionId);
|
|
1698
|
+
}
|
|
1699
|
+
}
|
|
1700
|
+
/**
|
|
1701
|
+
* Delete all sessions for a thread
|
|
1702
|
+
*/
|
|
1703
|
+
deleteSessionsByThread(threadId) {
|
|
1704
|
+
for (const [sessionId, session] of this.sessions.entries()) {
|
|
1705
|
+
if (session.threadId === threadId) {
|
|
1706
|
+
session.agent.close();
|
|
1707
|
+
this.sessions.delete(sessionId);
|
|
1708
|
+
}
|
|
1709
|
+
}
|
|
1710
|
+
}
|
|
1711
|
+
/**
|
|
1712
|
+
* Get all active sessions
|
|
1713
|
+
*/
|
|
1714
|
+
getAllSessions() {
|
|
1715
|
+
return Array.from(this.sessions.values());
|
|
1716
|
+
}
|
|
1717
|
+
/**
|
|
1718
|
+
* Clean up expired sessions
|
|
1719
|
+
*/
|
|
1720
|
+
cleanupExpiredSessions() {
|
|
1721
|
+
const now = Date.now();
|
|
1722
|
+
const expiredSessions = [];
|
|
1723
|
+
for (const [sessionId, session] of this.sessions.entries()) {
|
|
1724
|
+
const inactiveTime = now - session.lastActive;
|
|
1725
|
+
if (inactiveTime > this.SESSION_TIMEOUT && !session.isLocked) {
|
|
1726
|
+
expiredSessions.push(sessionId);
|
|
1727
|
+
}
|
|
1728
|
+
}
|
|
1729
|
+
for (const sessionId of expiredSessions) {
|
|
1730
|
+
console.log(
|
|
1731
|
+
`[AgentSessionStore] Cleaning up expired session: ${sessionId}`
|
|
1732
|
+
);
|
|
1733
|
+
this.deleteSession(sessionId);
|
|
1734
|
+
}
|
|
1735
|
+
if (expiredSessions.length > 0) {
|
|
1736
|
+
console.log(
|
|
1737
|
+
`[AgentSessionStore] Cleaned up ${expiredSessions.length} expired sessions`
|
|
1738
|
+
);
|
|
1739
|
+
}
|
|
1740
|
+
}
|
|
1741
|
+
/**
|
|
1742
|
+
* Start periodic cleanup timer
|
|
1743
|
+
*/
|
|
1744
|
+
startCleanupTimer() {
|
|
1745
|
+
setInterval(() => {
|
|
1746
|
+
this.cleanupExpiredSessions();
|
|
1747
|
+
}, this.CLEANUP_INTERVAL);
|
|
1748
|
+
}
|
|
1749
|
+
/**
|
|
1750
|
+
* Generate a unique session ID
|
|
1751
|
+
*/
|
|
1752
|
+
generateSessionId() {
|
|
1753
|
+
return `session-${Date.now()}-${Math.random().toString(36).substring(2, 11)}`;
|
|
1754
|
+
}
|
|
1755
|
+
/**
|
|
1756
|
+
* Get statistics about the session store
|
|
1757
|
+
*/
|
|
1758
|
+
getStats() {
|
|
1759
|
+
return {
|
|
1760
|
+
totalSessions: this.sessions.size,
|
|
1761
|
+
lockedSessions: Array.from(this.sessions.values()).filter(
|
|
1762
|
+
(s) => s.isLocked
|
|
1763
|
+
).length,
|
|
1764
|
+
sessions: Array.from(this.sessions.values()).map((s) => ({
|
|
1765
|
+
sessionId: s.sessionId,
|
|
1766
|
+
threadId: s.threadId,
|
|
1767
|
+
isLocked: s.isLocked,
|
|
1768
|
+
ageMinutes: Math.floor((Date.now() - s.createdAt) / 1e3 / 60),
|
|
1769
|
+
inactiveMinutes: Math.floor((Date.now() - s.lastActive) / 1e3 / 60)
|
|
1770
|
+
}))
|
|
1771
|
+
};
|
|
1772
|
+
}
|
|
1773
|
+
}
|
|
1774
|
+
let sessionStore = null;
|
|
1775
|
+
function getSessionStore() {
|
|
1776
|
+
if (!sessionStore) {
|
|
1777
|
+
sessionStore = new AgentSessionStore();
|
|
1778
|
+
}
|
|
1779
|
+
return sessionStore;
|
|
1780
|
+
}
|
|
1781
|
+
async function handleChat(req) {
|
|
1782
|
+
const devModeError = validateDevMode();
|
|
1783
|
+
if (devModeError) return devModeError;
|
|
1784
|
+
try {
|
|
1785
|
+
const body = await req.json();
|
|
1786
|
+
const { sessionId, threadId, message, componentContext } = body;
|
|
1787
|
+
if (!threadId || !message) {
|
|
1788
|
+
return NextResponse.json(
|
|
1789
|
+
{ error: "threadId and message are required" },
|
|
1790
|
+
{ status: 400 }
|
|
1791
|
+
);
|
|
1792
|
+
}
|
|
1793
|
+
const apiKey = process.env.ANTHROPIC_API_KEY;
|
|
1794
|
+
if (!apiKey) {
|
|
1795
|
+
return NextResponse.json(
|
|
1796
|
+
{ error: "ANTHROPIC_API_KEY not configured" },
|
|
1797
|
+
{ status: 500 }
|
|
1798
|
+
);
|
|
1799
|
+
}
|
|
1800
|
+
const sessionStore2 = getSessionStore();
|
|
1801
|
+
const projectRoot = process.cwd();
|
|
1802
|
+
const session = await sessionStore2.createSession({
|
|
1803
|
+
sessionId,
|
|
1804
|
+
threadId,
|
|
1805
|
+
apiKey,
|
|
1806
|
+
projectRoot
|
|
1807
|
+
});
|
|
1808
|
+
if (!sessionStore2.acquireLock(session.sessionId)) {
|
|
1809
|
+
return NextResponse.json(
|
|
1810
|
+
{
|
|
1811
|
+
error: "Another operation is in progress. Please wait for it to complete."
|
|
1812
|
+
},
|
|
1813
|
+
{ status: 409 }
|
|
1814
|
+
);
|
|
1815
|
+
}
|
|
1816
|
+
const encoder = new TextEncoder();
|
|
1817
|
+
const stream = new ReadableStream({
|
|
1818
|
+
async start(controller) {
|
|
1819
|
+
try {
|
|
1820
|
+
controller.enqueue(
|
|
1821
|
+
encoder.encode(
|
|
1822
|
+
`data: ${JSON.stringify({ type: "connected", sessionId: session.sessionId })}
|
|
1823
|
+
|
|
1824
|
+
`
|
|
1825
|
+
)
|
|
1826
|
+
);
|
|
1827
|
+
for await (const event of session.agent.sendMessage(
|
|
1828
|
+
message,
|
|
1829
|
+
componentContext
|
|
1830
|
+
)) {
|
|
1831
|
+
console.log("[AGENT EVENT]", event.type, JSON.stringify(event, null, 2));
|
|
1832
|
+
controller.enqueue(
|
|
1833
|
+
encoder.encode(`data: ${JSON.stringify(event)}
|
|
1834
|
+
|
|
1835
|
+
`)
|
|
1836
|
+
);
|
|
1837
|
+
}
|
|
1838
|
+
controller.enqueue(
|
|
1839
|
+
encoder.encode(`data: ${JSON.stringify({ type: "done" })}
|
|
1840
|
+
|
|
1841
|
+
`)
|
|
1842
|
+
);
|
|
1843
|
+
controller.close();
|
|
1844
|
+
} catch (error) {
|
|
1845
|
+
console.error("[handleChat] Error:", error);
|
|
1846
|
+
controller.enqueue(
|
|
1847
|
+
encoder.encode(
|
|
1848
|
+
`data: ${JSON.stringify({ type: "error", error: error.message || String(error) })}
|
|
1849
|
+
|
|
1850
|
+
`
|
|
1851
|
+
)
|
|
1852
|
+
);
|
|
1853
|
+
controller.close();
|
|
1854
|
+
} finally {
|
|
1855
|
+
sessionStore2.releaseLock(session.sessionId);
|
|
1856
|
+
}
|
|
1857
|
+
}
|
|
1858
|
+
});
|
|
1859
|
+
return new Response(stream, {
|
|
1860
|
+
headers: {
|
|
1861
|
+
"Content-Type": "text/event-stream",
|
|
1862
|
+
"Cache-Control": "no-cache",
|
|
1863
|
+
Connection: "keep-alive"
|
|
1864
|
+
}
|
|
1865
|
+
});
|
|
1866
|
+
} catch (error) {
|
|
1867
|
+
console.error("[handleChat] Request error:", error);
|
|
1868
|
+
return NextResponse.json(
|
|
1869
|
+
{ error: String(error) },
|
|
1870
|
+
{ status: 500 }
|
|
1871
|
+
);
|
|
1872
|
+
}
|
|
1873
|
+
}
|
|
1464
1874
|
async function handleAIEditorRequest(req, context) {
|
|
1465
1875
|
const { path: path2 } = await context.params;
|
|
1466
1876
|
const endpoint = path2[0];
|
|
@@ -1487,12 +1897,79 @@ async function handleAIEditorRequest(req, context) {
|
|
|
1487
1897
|
case "suggestions":
|
|
1488
1898
|
if (method === "GET") return handleSuggestions(req);
|
|
1489
1899
|
break;
|
|
1900
|
+
case "chat":
|
|
1901
|
+
if (method === "POST") return handleChat(req);
|
|
1902
|
+
break;
|
|
1490
1903
|
}
|
|
1491
1904
|
return NextResponse.json(
|
|
1492
1905
|
{ error: `Unknown endpoint: ${endpoint}` },
|
|
1493
1906
|
{ status: 404 }
|
|
1494
1907
|
);
|
|
1495
1908
|
}
|
|
1909
|
+
const STORAGE_DIR = ".ai-editor";
|
|
1910
|
+
const STORAGE_FILE = "comments.json";
|
|
1911
|
+
function getProjectRoot() {
|
|
1912
|
+
return process.cwd();
|
|
1913
|
+
}
|
|
1914
|
+
function getStoragePath() {
|
|
1915
|
+
return join(getProjectRoot(), STORAGE_DIR, STORAGE_FILE);
|
|
1916
|
+
}
|
|
1917
|
+
async function ensureStorageDir() {
|
|
1918
|
+
const dir = join(getProjectRoot(), STORAGE_DIR);
|
|
1919
|
+
if (!existsSync(dir)) {
|
|
1920
|
+
await mkdir(dir, { recursive: true });
|
|
1921
|
+
}
|
|
1922
|
+
}
|
|
1923
|
+
async function handleCommentsRequest(request) {
|
|
1924
|
+
if (process.env.NODE_ENV !== "development") {
|
|
1925
|
+
return NextResponse.json(
|
|
1926
|
+
{ error: "Comments API only available in development" },
|
|
1927
|
+
{ status: 403 }
|
|
1928
|
+
);
|
|
1929
|
+
}
|
|
1930
|
+
try {
|
|
1931
|
+
if (request.method === "GET") {
|
|
1932
|
+
const storagePath = getStoragePath();
|
|
1933
|
+
if (!existsSync(storagePath)) {
|
|
1934
|
+
return NextResponse.json({ comments: [] });
|
|
1935
|
+
}
|
|
1936
|
+
const data = await readFile(storagePath, "utf-8");
|
|
1937
|
+
const comments = JSON.parse(data);
|
|
1938
|
+
return NextResponse.json({ comments });
|
|
1939
|
+
}
|
|
1940
|
+
if (request.method === "POST") {
|
|
1941
|
+
const body = await request.json();
|
|
1942
|
+
const { action } = body;
|
|
1943
|
+
if (action === "save") {
|
|
1944
|
+
const { comments } = body;
|
|
1945
|
+
await ensureStorageDir();
|
|
1946
|
+
const storagePath = getStoragePath();
|
|
1947
|
+
await writeFile(storagePath, JSON.stringify(comments, null, 2), "utf-8");
|
|
1948
|
+
return NextResponse.json({ success: true });
|
|
1949
|
+
}
|
|
1950
|
+
if (action === "clear") {
|
|
1951
|
+
await ensureStorageDir();
|
|
1952
|
+
const storagePath = getStoragePath();
|
|
1953
|
+
await writeFile(storagePath, JSON.stringify([]), "utf-8");
|
|
1954
|
+
return NextResponse.json({ success: true });
|
|
1955
|
+
}
|
|
1956
|
+
return NextResponse.json(
|
|
1957
|
+
{ error: "Invalid action" },
|
|
1958
|
+
{ status: 400 }
|
|
1959
|
+
);
|
|
1960
|
+
}
|
|
1961
|
+
return NextResponse.json(
|
|
1962
|
+
{ error: "Method not allowed" },
|
|
1963
|
+
{ status: 405 }
|
|
1964
|
+
);
|
|
1965
|
+
} catch (error) {
|
|
1966
|
+
console.error("Comment storage error:", error);
|
|
1967
|
+
return NextResponse.json(
|
|
1968
|
+
{ error: "Internal server error" },
|
|
1969
|
+
{ status: 500 }
|
|
1970
|
+
);
|
|
1971
|
+
}
|
|
1972
|
+
}
|
|
1496
1973
|
export {
|
|
1497
1974
|
handleEdit as a,
|
|
1498
1975
|
handleRead as b,
|
|
@@ -1502,21 +1979,23 @@ export {
|
|
|
1502
1979
|
handleValidateSession as f,
|
|
1503
1980
|
handleSuggestions as g,
|
|
1504
1981
|
handleAIEditorRequest as h,
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
|
|
1508
|
-
|
|
1509
|
-
|
|
1982
|
+
handleCommentsRequest as i,
|
|
1983
|
+
isPathSecure as j,
|
|
1984
|
+
fileExists$1 as k,
|
|
1985
|
+
extractComponentName as l,
|
|
1986
|
+
validateGeneratedCode as m,
|
|
1510
1987
|
normalizePath as n,
|
|
1511
|
-
|
|
1988
|
+
findTargetElement as o,
|
|
1512
1989
|
parseFile as p,
|
|
1513
|
-
|
|
1990
|
+
getJSXMemberName as q,
|
|
1514
1991
|
resolveFilePath as r,
|
|
1515
1992
|
scoreElementMatch as s,
|
|
1516
|
-
|
|
1517
|
-
|
|
1993
|
+
getAttributeValue as t,
|
|
1994
|
+
parseDebugStack as u,
|
|
1518
1995
|
validateDevMode as v,
|
|
1519
|
-
|
|
1520
|
-
|
|
1996
|
+
extractComponentNameFromStack as w,
|
|
1997
|
+
parseDebugStackFrames as x,
|
|
1998
|
+
resolveOriginalPosition as y,
|
|
1999
|
+
getOriginalPositionFromDebugStack as z
|
|
1521
2000
|
};
|
|
1522
|
-
//# sourceMappingURL=
|
|
2001
|
+
//# sourceMappingURL=comments-D3m0RsOO.js.map
|