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
|
@@ -10,6 +10,8 @@ const t = require("@babel/types");
|
|
|
10
10
|
const sourceMap = require("@jridgewell/source-map");
|
|
11
11
|
const pathUtils = require("./path-utils-DYzEWUGy.cjs");
|
|
12
12
|
const crypto = require("crypto");
|
|
13
|
+
const claudeAgentSdk = require("@anthropic-ai/claude-agent-sdk");
|
|
14
|
+
const fs$1 = require("fs");
|
|
13
15
|
function _interopNamespaceDefault(e) {
|
|
14
16
|
const n = Object.create(null, { [Symbol.toStringTag]: { value: "Module" } });
|
|
15
17
|
if (e) {
|
|
@@ -107,6 +109,11 @@ function extractComponentName(ast) {
|
|
|
107
109
|
if (t__namespace.isIdentifier(declarator.id)) {
|
|
108
110
|
componentName = declarator.id.name;
|
|
109
111
|
}
|
|
112
|
+
} else if (path2.node.specifiers && path2.node.specifiers.length > 0) {
|
|
113
|
+
const specifier = path2.node.specifiers[0];
|
|
114
|
+
if (t__namespace.isExportSpecifier(specifier) && t__namespace.isIdentifier(specifier.exported)) {
|
|
115
|
+
componentName = specifier.exported.name;
|
|
116
|
+
}
|
|
110
117
|
}
|
|
111
118
|
}
|
|
112
119
|
});
|
|
@@ -258,16 +265,34 @@ function findTargetElement(ast, fileContent, options) {
|
|
|
258
265
|
componentEnd
|
|
259
266
|
};
|
|
260
267
|
}
|
|
261
|
-
if (
|
|
268
|
+
if (elementContext == null ? void 0 : elementContext.tagName) {
|
|
262
269
|
const allOfTag = allElementsByTag.get(elementContext.tagName);
|
|
263
|
-
if (allOfTag && allOfTag.length
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
270
|
+
if (allOfTag && allOfTag.length > 0) {
|
|
271
|
+
if (elementContext.textContent || elementContext.className) {
|
|
272
|
+
for (const elem of allOfTag) {
|
|
273
|
+
if (t__namespace.isJSXElement(elem.node)) {
|
|
274
|
+
elem.score = scoreElementMatch(elem.node, elementContext, fileContent);
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
allOfTag.sort((a, b) => b.score - a.score);
|
|
278
|
+
if (allOfTag[0].score > 50) {
|
|
279
|
+
return {
|
|
280
|
+
startLine: allOfTag[0].startLine,
|
|
281
|
+
endLine: allOfTag[0].endLine,
|
|
282
|
+
componentStart,
|
|
283
|
+
componentEnd
|
|
284
|
+
};
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
if (elementContext.nthOfType && allOfTag.length >= elementContext.nthOfType) {
|
|
288
|
+
const target = allOfTag[elementContext.nthOfType - 1];
|
|
289
|
+
return {
|
|
290
|
+
startLine: target.startLine,
|
|
291
|
+
endLine: target.endLine,
|
|
292
|
+
componentStart,
|
|
293
|
+
componentEnd
|
|
294
|
+
};
|
|
295
|
+
}
|
|
271
296
|
}
|
|
272
297
|
}
|
|
273
298
|
const nearbyElements = [];
|
|
@@ -714,6 +739,30 @@ function parseDebugStack(stack) {
|
|
|
714
739
|
);
|
|
715
740
|
return null;
|
|
716
741
|
}
|
|
742
|
+
function extractComponentNameFromStack(stack) {
|
|
743
|
+
if (!stack) return null;
|
|
744
|
+
const stackStr = typeof stack === "string" ? stack : stack.stack || String(stack);
|
|
745
|
+
const frames = stackStr.split("\n");
|
|
746
|
+
const skipPatterns = [
|
|
747
|
+
"node_modules",
|
|
748
|
+
"SegmentViewNode",
|
|
749
|
+
"LayoutRouter",
|
|
750
|
+
"ErrorBoundary",
|
|
751
|
+
"fakeJSXCallSite",
|
|
752
|
+
"react_stack_bottom_frame"
|
|
753
|
+
];
|
|
754
|
+
for (const frame of frames) {
|
|
755
|
+
if (skipPatterns.some((p) => frame.includes(p))) continue;
|
|
756
|
+
const match = frame.match(/at\s+(\w+)\s+\(/);
|
|
757
|
+
if (match && match[1]) {
|
|
758
|
+
const componentName = match[1];
|
|
759
|
+
if (componentName !== "Object" && componentName !== "anonymous") {
|
|
760
|
+
return componentName;
|
|
761
|
+
}
|
|
762
|
+
}
|
|
763
|
+
}
|
|
764
|
+
return null;
|
|
765
|
+
}
|
|
717
766
|
function parseDebugStackFrames(stack) {
|
|
718
767
|
if (!stack) return [];
|
|
719
768
|
const stackStr = typeof stack === "string" ? stack : stack.stack || String(stack);
|
|
@@ -1014,7 +1063,10 @@ async function handleRead(req) {
|
|
|
1014
1063
|
let resolvedParentPath = parentFilePath;
|
|
1015
1064
|
let resolvedParentLine = parentLine;
|
|
1016
1065
|
let resolvedParentComponentName = parentComponentName;
|
|
1017
|
-
if (resolvedParentComponentName && parentDebugStack) {
|
|
1066
|
+
if (!resolvedParentComponentName && parentDebugStack) {
|
|
1067
|
+
resolvedParentComponentName = extractComponentNameFromStack(parentDebugStack) || "";
|
|
1068
|
+
}
|
|
1069
|
+
if (parentDebugStack) {
|
|
1018
1070
|
const compiledPos = parseDebugStack(parentDebugStack);
|
|
1019
1071
|
if (compiledPos) {
|
|
1020
1072
|
const originalPos = await resolveOriginalPosition(
|
|
@@ -1046,12 +1098,14 @@ async function handleRead(req) {
|
|
|
1046
1098
|
const parentTarget = findTargetElement(parentAst, parentContent, {
|
|
1047
1099
|
componentName: resolvedParentComponentName,
|
|
1048
1100
|
lineNumber: 0,
|
|
1049
|
-
// Don't use line number - rely on
|
|
1101
|
+
// Don't use line number - rely on element context to find correct instance
|
|
1050
1102
|
elementContext: {
|
|
1051
1103
|
tagName: componentName,
|
|
1052
1104
|
// Search for child component usage
|
|
1053
|
-
nthOfType: nthOfType2
|
|
1105
|
+
nthOfType: nthOfType2,
|
|
1054
1106
|
// Find specific instance if key is numeric
|
|
1107
|
+
textContent: textContent || void 0
|
|
1108
|
+
// Use text content to match the specific instance
|
|
1055
1109
|
}
|
|
1056
1110
|
});
|
|
1057
1111
|
if (parentTarget) {
|
|
@@ -1073,6 +1127,7 @@ async function handleRead(req) {
|
|
|
1073
1127
|
}
|
|
1074
1128
|
}
|
|
1075
1129
|
} catch (error) {
|
|
1130
|
+
console.error("Error resolving parent instance:", error);
|
|
1076
1131
|
}
|
|
1077
1132
|
}
|
|
1078
1133
|
return server.NextResponse.json({
|
|
@@ -1480,6 +1535,361 @@ Generate 6-8 initial suggestions:`;
|
|
|
1480
1535
|
return null;
|
|
1481
1536
|
}
|
|
1482
1537
|
}
|
|
1538
|
+
class AIEditorAgent {
|
|
1539
|
+
constructor(session, projectRoot) {
|
|
1540
|
+
this.session = session;
|
|
1541
|
+
this.projectRoot = projectRoot;
|
|
1542
|
+
}
|
|
1543
|
+
/**
|
|
1544
|
+
* Create a new agent instance
|
|
1545
|
+
*/
|
|
1546
|
+
static async create(config) {
|
|
1547
|
+
const sessionOptions = {
|
|
1548
|
+
model: "claude-sonnet-4-5-20250929",
|
|
1549
|
+
// Enable auto-apply for edits without permission prompts
|
|
1550
|
+
permissionMode: "acceptEdits",
|
|
1551
|
+
env: {
|
|
1552
|
+
...process.env,
|
|
1553
|
+
ANTHROPIC_API_KEY: config.apiKey,
|
|
1554
|
+
// Set working directory
|
|
1555
|
+
PWD: config.projectRoot
|
|
1556
|
+
},
|
|
1557
|
+
// Explicitly allow core development tools
|
|
1558
|
+
allowedTools: config.allowedTools || ["Read", "Edit", "Glob", "Grep"],
|
|
1559
|
+
disallowedTools: config.disallowedTools || []
|
|
1560
|
+
};
|
|
1561
|
+
let session;
|
|
1562
|
+
if (config.sessionId) {
|
|
1563
|
+
session = claudeAgentSdk.unstable_v2_resumeSession(config.sessionId, sessionOptions);
|
|
1564
|
+
} else {
|
|
1565
|
+
session = claudeAgentSdk.unstable_v2_createSession(sessionOptions);
|
|
1566
|
+
}
|
|
1567
|
+
return new AIEditorAgent(session, config.projectRoot);
|
|
1568
|
+
}
|
|
1569
|
+
/**
|
|
1570
|
+
* Send a message to the agent and stream responses
|
|
1571
|
+
*/
|
|
1572
|
+
async *sendMessage(message, componentContext) {
|
|
1573
|
+
let fullMessage = `<response_style>
|
|
1574
|
+
KEEP RESPONSES EXTREMELY BRIEF AND STATUS-LIKE.
|
|
1575
|
+
- Use terse status messages: "Reading file.tsx", "Changing color", "Done"
|
|
1576
|
+
- NO conversational language (avoid: "I'll", "Let me", "Sure", "Great", "I can see")
|
|
1577
|
+
- NO explanations unless explicitly asked
|
|
1578
|
+
- State ONLY what you're doing, nothing more
|
|
1579
|
+
- Format: Action + target (e.g., "Updating StatsCard.tsx:24")
|
|
1580
|
+
</response_style>
|
|
1581
|
+
|
|
1582
|
+
`;
|
|
1583
|
+
if (componentContext) {
|
|
1584
|
+
fullMessage += `[Component: ${componentContext.componentName} at ${componentContext.filePath}:${componentContext.lineNumber}]
|
|
1585
|
+
|
|
1586
|
+
`;
|
|
1587
|
+
}
|
|
1588
|
+
fullMessage += message;
|
|
1589
|
+
await this.session.send(fullMessage);
|
|
1590
|
+
for await (const event of this.session.stream()) {
|
|
1591
|
+
yield event;
|
|
1592
|
+
}
|
|
1593
|
+
}
|
|
1594
|
+
/**
|
|
1595
|
+
* Get the session ID
|
|
1596
|
+
*/
|
|
1597
|
+
getSessionId() {
|
|
1598
|
+
return this.session.sessionId;
|
|
1599
|
+
}
|
|
1600
|
+
/**
|
|
1601
|
+
* Close the session
|
|
1602
|
+
*/
|
|
1603
|
+
close() {
|
|
1604
|
+
this.session.close();
|
|
1605
|
+
}
|
|
1606
|
+
/**
|
|
1607
|
+
* Async disposal support
|
|
1608
|
+
*/
|
|
1609
|
+
async [Symbol.asyncDispose]() {
|
|
1610
|
+
await this.session[Symbol.asyncDispose]();
|
|
1611
|
+
}
|
|
1612
|
+
}
|
|
1613
|
+
class AgentSessionStore {
|
|
1614
|
+
// 5 minutes
|
|
1615
|
+
constructor() {
|
|
1616
|
+
this.sessions = /* @__PURE__ */ new Map();
|
|
1617
|
+
this.SESSION_TIMEOUT = 1e3 * 60 * 30;
|
|
1618
|
+
this.CLEANUP_INTERVAL = 1e3 * 60 * 5;
|
|
1619
|
+
this.startCleanupTimer();
|
|
1620
|
+
}
|
|
1621
|
+
/**
|
|
1622
|
+
* Create or resume a session
|
|
1623
|
+
*/
|
|
1624
|
+
async createSession(config) {
|
|
1625
|
+
const sessionId = config.sessionId || this.generateSessionId();
|
|
1626
|
+
const existing = this.sessions.get(sessionId);
|
|
1627
|
+
if (existing) {
|
|
1628
|
+
existing.lastActive = Date.now();
|
|
1629
|
+
existing.threadId = config.threadId;
|
|
1630
|
+
return existing;
|
|
1631
|
+
}
|
|
1632
|
+
const agent = await AIEditorAgent.create({
|
|
1633
|
+
apiKey: config.apiKey,
|
|
1634
|
+
projectRoot: config.projectRoot,
|
|
1635
|
+
sessionId: config.sessionId,
|
|
1636
|
+
allowedTools: config.allowedTools,
|
|
1637
|
+
disallowedTools: config.disallowedTools
|
|
1638
|
+
});
|
|
1639
|
+
const session = {
|
|
1640
|
+
sessionId,
|
|
1641
|
+
threadId: config.threadId,
|
|
1642
|
+
agent,
|
|
1643
|
+
createdAt: Date.now(),
|
|
1644
|
+
lastActive: Date.now(),
|
|
1645
|
+
isLocked: false
|
|
1646
|
+
};
|
|
1647
|
+
this.sessions.set(sessionId, session);
|
|
1648
|
+
return session;
|
|
1649
|
+
}
|
|
1650
|
+
/**
|
|
1651
|
+
* Get a session by ID
|
|
1652
|
+
*/
|
|
1653
|
+
getSession(sessionId) {
|
|
1654
|
+
const session = this.sessions.get(sessionId);
|
|
1655
|
+
if (session) {
|
|
1656
|
+
session.lastActive = Date.now();
|
|
1657
|
+
}
|
|
1658
|
+
return session;
|
|
1659
|
+
}
|
|
1660
|
+
/**
|
|
1661
|
+
* Acquire a lock on a session to prevent concurrent edits
|
|
1662
|
+
* Returns true if lock was acquired, false if already locked
|
|
1663
|
+
*/
|
|
1664
|
+
acquireLock(sessionId) {
|
|
1665
|
+
const session = this.sessions.get(sessionId);
|
|
1666
|
+
if (!session) {
|
|
1667
|
+
throw new Error(`Session ${sessionId} not found`);
|
|
1668
|
+
}
|
|
1669
|
+
if (session.isLocked) {
|
|
1670
|
+
return false;
|
|
1671
|
+
}
|
|
1672
|
+
session.isLocked = true;
|
|
1673
|
+
session.lastActive = Date.now();
|
|
1674
|
+
return true;
|
|
1675
|
+
}
|
|
1676
|
+
/**
|
|
1677
|
+
* Release a lock on a session
|
|
1678
|
+
*/
|
|
1679
|
+
releaseLock(sessionId) {
|
|
1680
|
+
const session = this.sessions.get(sessionId);
|
|
1681
|
+
if (session) {
|
|
1682
|
+
session.isLocked = false;
|
|
1683
|
+
session.lastActive = Date.now();
|
|
1684
|
+
}
|
|
1685
|
+
}
|
|
1686
|
+
/**
|
|
1687
|
+
* Check if any session is currently locked (to prevent concurrent edits)
|
|
1688
|
+
*/
|
|
1689
|
+
hasActiveLock() {
|
|
1690
|
+
for (const session of this.sessions.values()) {
|
|
1691
|
+
if (session.isLocked) {
|
|
1692
|
+
return true;
|
|
1693
|
+
}
|
|
1694
|
+
}
|
|
1695
|
+
return false;
|
|
1696
|
+
}
|
|
1697
|
+
/**
|
|
1698
|
+
* Get all active sessions for a thread
|
|
1699
|
+
*/
|
|
1700
|
+
getSessionsByThread(threadId) {
|
|
1701
|
+
const sessions = [];
|
|
1702
|
+
for (const session of this.sessions.values()) {
|
|
1703
|
+
if (session.threadId === threadId) {
|
|
1704
|
+
sessions.push(session);
|
|
1705
|
+
}
|
|
1706
|
+
}
|
|
1707
|
+
return sessions;
|
|
1708
|
+
}
|
|
1709
|
+
/**
|
|
1710
|
+
* Delete a session
|
|
1711
|
+
*/
|
|
1712
|
+
deleteSession(sessionId) {
|
|
1713
|
+
const session = this.sessions.get(sessionId);
|
|
1714
|
+
if (session) {
|
|
1715
|
+
session.agent.close();
|
|
1716
|
+
this.sessions.delete(sessionId);
|
|
1717
|
+
}
|
|
1718
|
+
}
|
|
1719
|
+
/**
|
|
1720
|
+
* Delete all sessions for a thread
|
|
1721
|
+
*/
|
|
1722
|
+
deleteSessionsByThread(threadId) {
|
|
1723
|
+
for (const [sessionId, session] of this.sessions.entries()) {
|
|
1724
|
+
if (session.threadId === threadId) {
|
|
1725
|
+
session.agent.close();
|
|
1726
|
+
this.sessions.delete(sessionId);
|
|
1727
|
+
}
|
|
1728
|
+
}
|
|
1729
|
+
}
|
|
1730
|
+
/**
|
|
1731
|
+
* Get all active sessions
|
|
1732
|
+
*/
|
|
1733
|
+
getAllSessions() {
|
|
1734
|
+
return Array.from(this.sessions.values());
|
|
1735
|
+
}
|
|
1736
|
+
/**
|
|
1737
|
+
* Clean up expired sessions
|
|
1738
|
+
*/
|
|
1739
|
+
cleanupExpiredSessions() {
|
|
1740
|
+
const now = Date.now();
|
|
1741
|
+
const expiredSessions = [];
|
|
1742
|
+
for (const [sessionId, session] of this.sessions.entries()) {
|
|
1743
|
+
const inactiveTime = now - session.lastActive;
|
|
1744
|
+
if (inactiveTime > this.SESSION_TIMEOUT && !session.isLocked) {
|
|
1745
|
+
expiredSessions.push(sessionId);
|
|
1746
|
+
}
|
|
1747
|
+
}
|
|
1748
|
+
for (const sessionId of expiredSessions) {
|
|
1749
|
+
console.log(
|
|
1750
|
+
`[AgentSessionStore] Cleaning up expired session: ${sessionId}`
|
|
1751
|
+
);
|
|
1752
|
+
this.deleteSession(sessionId);
|
|
1753
|
+
}
|
|
1754
|
+
if (expiredSessions.length > 0) {
|
|
1755
|
+
console.log(
|
|
1756
|
+
`[AgentSessionStore] Cleaned up ${expiredSessions.length} expired sessions`
|
|
1757
|
+
);
|
|
1758
|
+
}
|
|
1759
|
+
}
|
|
1760
|
+
/**
|
|
1761
|
+
* Start periodic cleanup timer
|
|
1762
|
+
*/
|
|
1763
|
+
startCleanupTimer() {
|
|
1764
|
+
setInterval(() => {
|
|
1765
|
+
this.cleanupExpiredSessions();
|
|
1766
|
+
}, this.CLEANUP_INTERVAL);
|
|
1767
|
+
}
|
|
1768
|
+
/**
|
|
1769
|
+
* Generate a unique session ID
|
|
1770
|
+
*/
|
|
1771
|
+
generateSessionId() {
|
|
1772
|
+
return `session-${Date.now()}-${Math.random().toString(36).substring(2, 11)}`;
|
|
1773
|
+
}
|
|
1774
|
+
/**
|
|
1775
|
+
* Get statistics about the session store
|
|
1776
|
+
*/
|
|
1777
|
+
getStats() {
|
|
1778
|
+
return {
|
|
1779
|
+
totalSessions: this.sessions.size,
|
|
1780
|
+
lockedSessions: Array.from(this.sessions.values()).filter(
|
|
1781
|
+
(s) => s.isLocked
|
|
1782
|
+
).length,
|
|
1783
|
+
sessions: Array.from(this.sessions.values()).map((s) => ({
|
|
1784
|
+
sessionId: s.sessionId,
|
|
1785
|
+
threadId: s.threadId,
|
|
1786
|
+
isLocked: s.isLocked,
|
|
1787
|
+
ageMinutes: Math.floor((Date.now() - s.createdAt) / 1e3 / 60),
|
|
1788
|
+
inactiveMinutes: Math.floor((Date.now() - s.lastActive) / 1e3 / 60)
|
|
1789
|
+
}))
|
|
1790
|
+
};
|
|
1791
|
+
}
|
|
1792
|
+
}
|
|
1793
|
+
let sessionStore = null;
|
|
1794
|
+
function getSessionStore() {
|
|
1795
|
+
if (!sessionStore) {
|
|
1796
|
+
sessionStore = new AgentSessionStore();
|
|
1797
|
+
}
|
|
1798
|
+
return sessionStore;
|
|
1799
|
+
}
|
|
1800
|
+
async function handleChat(req) {
|
|
1801
|
+
const devModeError = validateDevMode();
|
|
1802
|
+
if (devModeError) return devModeError;
|
|
1803
|
+
try {
|
|
1804
|
+
const body = await req.json();
|
|
1805
|
+
const { sessionId, threadId, message, componentContext } = body;
|
|
1806
|
+
if (!threadId || !message) {
|
|
1807
|
+
return server.NextResponse.json(
|
|
1808
|
+
{ error: "threadId and message are required" },
|
|
1809
|
+
{ status: 400 }
|
|
1810
|
+
);
|
|
1811
|
+
}
|
|
1812
|
+
const apiKey = process.env.ANTHROPIC_API_KEY;
|
|
1813
|
+
if (!apiKey) {
|
|
1814
|
+
return server.NextResponse.json(
|
|
1815
|
+
{ error: "ANTHROPIC_API_KEY not configured" },
|
|
1816
|
+
{ status: 500 }
|
|
1817
|
+
);
|
|
1818
|
+
}
|
|
1819
|
+
const sessionStore2 = getSessionStore();
|
|
1820
|
+
const projectRoot = process.cwd();
|
|
1821
|
+
const session = await sessionStore2.createSession({
|
|
1822
|
+
sessionId,
|
|
1823
|
+
threadId,
|
|
1824
|
+
apiKey,
|
|
1825
|
+
projectRoot
|
|
1826
|
+
});
|
|
1827
|
+
if (!sessionStore2.acquireLock(session.sessionId)) {
|
|
1828
|
+
return server.NextResponse.json(
|
|
1829
|
+
{
|
|
1830
|
+
error: "Another operation is in progress. Please wait for it to complete."
|
|
1831
|
+
},
|
|
1832
|
+
{ status: 409 }
|
|
1833
|
+
);
|
|
1834
|
+
}
|
|
1835
|
+
const encoder = new TextEncoder();
|
|
1836
|
+
const stream = new ReadableStream({
|
|
1837
|
+
async start(controller) {
|
|
1838
|
+
try {
|
|
1839
|
+
controller.enqueue(
|
|
1840
|
+
encoder.encode(
|
|
1841
|
+
`data: ${JSON.stringify({ type: "connected", sessionId: session.sessionId })}
|
|
1842
|
+
|
|
1843
|
+
`
|
|
1844
|
+
)
|
|
1845
|
+
);
|
|
1846
|
+
for await (const event of session.agent.sendMessage(
|
|
1847
|
+
message,
|
|
1848
|
+
componentContext
|
|
1849
|
+
)) {
|
|
1850
|
+
console.log("[AGENT EVENT]", event.type, JSON.stringify(event, null, 2));
|
|
1851
|
+
controller.enqueue(
|
|
1852
|
+
encoder.encode(`data: ${JSON.stringify(event)}
|
|
1853
|
+
|
|
1854
|
+
`)
|
|
1855
|
+
);
|
|
1856
|
+
}
|
|
1857
|
+
controller.enqueue(
|
|
1858
|
+
encoder.encode(`data: ${JSON.stringify({ type: "done" })}
|
|
1859
|
+
|
|
1860
|
+
`)
|
|
1861
|
+
);
|
|
1862
|
+
controller.close();
|
|
1863
|
+
} catch (error) {
|
|
1864
|
+
console.error("[handleChat] Error:", error);
|
|
1865
|
+
controller.enqueue(
|
|
1866
|
+
encoder.encode(
|
|
1867
|
+
`data: ${JSON.stringify({ type: "error", error: error.message || String(error) })}
|
|
1868
|
+
|
|
1869
|
+
`
|
|
1870
|
+
)
|
|
1871
|
+
);
|
|
1872
|
+
controller.close();
|
|
1873
|
+
} finally {
|
|
1874
|
+
sessionStore2.releaseLock(session.sessionId);
|
|
1875
|
+
}
|
|
1876
|
+
}
|
|
1877
|
+
});
|
|
1878
|
+
return new Response(stream, {
|
|
1879
|
+
headers: {
|
|
1880
|
+
"Content-Type": "text/event-stream",
|
|
1881
|
+
"Cache-Control": "no-cache",
|
|
1882
|
+
Connection: "keep-alive"
|
|
1883
|
+
}
|
|
1884
|
+
});
|
|
1885
|
+
} catch (error) {
|
|
1886
|
+
console.error("[handleChat] Request error:", error);
|
|
1887
|
+
return server.NextResponse.json(
|
|
1888
|
+
{ error: String(error) },
|
|
1889
|
+
{ status: 500 }
|
|
1890
|
+
);
|
|
1891
|
+
}
|
|
1892
|
+
}
|
|
1483
1893
|
async function handleAIEditorRequest(req, context) {
|
|
1484
1894
|
const { path: path2 } = await context.params;
|
|
1485
1895
|
const endpoint = path2[0];
|
|
@@ -1506,13 +1916,81 @@ async function handleAIEditorRequest(req, context) {
|
|
|
1506
1916
|
case "suggestions":
|
|
1507
1917
|
if (method === "GET") return handleSuggestions(req);
|
|
1508
1918
|
break;
|
|
1919
|
+
case "chat":
|
|
1920
|
+
if (method === "POST") return handleChat(req);
|
|
1921
|
+
break;
|
|
1509
1922
|
}
|
|
1510
1923
|
return server.NextResponse.json(
|
|
1511
1924
|
{ error: `Unknown endpoint: ${endpoint}` },
|
|
1512
1925
|
{ status: 404 }
|
|
1513
1926
|
);
|
|
1514
1927
|
}
|
|
1928
|
+
const STORAGE_DIR = ".ai-editor";
|
|
1929
|
+
const STORAGE_FILE = "comments.json";
|
|
1930
|
+
function getProjectRoot() {
|
|
1931
|
+
return process.cwd();
|
|
1932
|
+
}
|
|
1933
|
+
function getStoragePath() {
|
|
1934
|
+
return path.join(getProjectRoot(), STORAGE_DIR, STORAGE_FILE);
|
|
1935
|
+
}
|
|
1936
|
+
async function ensureStorageDir() {
|
|
1937
|
+
const dir = path.join(getProjectRoot(), STORAGE_DIR);
|
|
1938
|
+
if (!fs$1.existsSync(dir)) {
|
|
1939
|
+
await fs.mkdir(dir, { recursive: true });
|
|
1940
|
+
}
|
|
1941
|
+
}
|
|
1942
|
+
async function handleCommentsRequest(request) {
|
|
1943
|
+
if (process.env.NODE_ENV !== "development") {
|
|
1944
|
+
return server.NextResponse.json(
|
|
1945
|
+
{ error: "Comments API only available in development" },
|
|
1946
|
+
{ status: 403 }
|
|
1947
|
+
);
|
|
1948
|
+
}
|
|
1949
|
+
try {
|
|
1950
|
+
if (request.method === "GET") {
|
|
1951
|
+
const storagePath = getStoragePath();
|
|
1952
|
+
if (!fs$1.existsSync(storagePath)) {
|
|
1953
|
+
return server.NextResponse.json({ comments: [] });
|
|
1954
|
+
}
|
|
1955
|
+
const data = await fs.readFile(storagePath, "utf-8");
|
|
1956
|
+
const comments = JSON.parse(data);
|
|
1957
|
+
return server.NextResponse.json({ comments });
|
|
1958
|
+
}
|
|
1959
|
+
if (request.method === "POST") {
|
|
1960
|
+
const body = await request.json();
|
|
1961
|
+
const { action } = body;
|
|
1962
|
+
if (action === "save") {
|
|
1963
|
+
const { comments } = body;
|
|
1964
|
+
await ensureStorageDir();
|
|
1965
|
+
const storagePath = getStoragePath();
|
|
1966
|
+
await fs.writeFile(storagePath, JSON.stringify(comments, null, 2), "utf-8");
|
|
1967
|
+
return server.NextResponse.json({ success: true });
|
|
1968
|
+
}
|
|
1969
|
+
if (action === "clear") {
|
|
1970
|
+
await ensureStorageDir();
|
|
1971
|
+
const storagePath = getStoragePath();
|
|
1972
|
+
await fs.writeFile(storagePath, JSON.stringify([]), "utf-8");
|
|
1973
|
+
return server.NextResponse.json({ success: true });
|
|
1974
|
+
}
|
|
1975
|
+
return server.NextResponse.json(
|
|
1976
|
+
{ error: "Invalid action" },
|
|
1977
|
+
{ status: 400 }
|
|
1978
|
+
);
|
|
1979
|
+
}
|
|
1980
|
+
return server.NextResponse.json(
|
|
1981
|
+
{ error: "Method not allowed" },
|
|
1982
|
+
{ status: 405 }
|
|
1983
|
+
);
|
|
1984
|
+
} catch (error) {
|
|
1985
|
+
console.error("Comment storage error:", error);
|
|
1986
|
+
return server.NextResponse.json(
|
|
1987
|
+
{ error: "Internal server error" },
|
|
1988
|
+
{ status: 500 }
|
|
1989
|
+
);
|
|
1990
|
+
}
|
|
1991
|
+
}
|
|
1515
1992
|
exports.extractComponentName = extractComponentName;
|
|
1993
|
+
exports.extractComponentNameFromStack = extractComponentNameFromStack;
|
|
1516
1994
|
exports.fileExists = fileExists$1;
|
|
1517
1995
|
exports.findTargetElement = findTargetElement;
|
|
1518
1996
|
exports.getAttributeValue = getAttributeValue;
|
|
@@ -1520,6 +1998,7 @@ exports.getJSXMemberName = getJSXMemberName;
|
|
|
1520
1998
|
exports.getOriginalPositionFromDebugStack = getOriginalPositionFromDebugStack;
|
|
1521
1999
|
exports.handleAIEditorRequest = handleAIEditorRequest;
|
|
1522
2000
|
exports.handleAbsolutePath = handleAbsolutePath;
|
|
2001
|
+
exports.handleCommentsRequest = handleCommentsRequest;
|
|
1523
2002
|
exports.handleEdit = handleEdit;
|
|
1524
2003
|
exports.handleRead = handleRead;
|
|
1525
2004
|
exports.handleResolve = handleResolve;
|
|
@@ -1536,4 +2015,4 @@ exports.resolveOriginalPosition = resolveOriginalPosition;
|
|
|
1536
2015
|
exports.scoreElementMatch = scoreElementMatch;
|
|
1537
2016
|
exports.validateDevMode = validateDevMode;
|
|
1538
2017
|
exports.validateGeneratedCode = validateGeneratedCode;
|
|
1539
|
-
//# sourceMappingURL=
|
|
2018
|
+
//# sourceMappingURL=comments-Daur80r4.cjs.map
|