next-ai-editor 0.1.3 → 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-DrmEf13c.js → comments-D3m0RsOO.js} +440 -15
- package/dist/comments-D3m0RsOO.js.map +1 -0
- package/dist/{index-CNJqd4EQ.cjs → comments-Daur80r4.cjs} +426 -1
- package/dist/comments-Daur80r4.cjs.map +1 -0
- package/dist/index.cjs +34 -27
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +22 -15
- 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/index.d.ts +1 -0
- package/dist/server/index.d.ts.map +1 -1
- package/dist/server.cjs +27 -26
- package/dist/server.cjs.map +1 -1
- package/dist/server.js +14 -13
- 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-BGHm2xyU.cjs +0 -2167
- package/dist/AIEditorProvider-BGHm2xyU.cjs.map +0 -1
- package/dist/AIEditorProvider-CxdGjdLL.js +0 -2168
- package/dist/AIEditorProvider-CxdGjdLL.js.map +0 -1
- package/dist/index-CNJqd4EQ.cjs.map +0 -1
- package/dist/index-DrmEf13c.js.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(
|
|
@@ -1514,6 +1516,361 @@ Generate 6-8 initial suggestions:`;
|
|
|
1514
1516
|
return null;
|
|
1515
1517
|
}
|
|
1516
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
|
+
}
|
|
1517
1874
|
async function handleAIEditorRequest(req, context) {
|
|
1518
1875
|
const { path: path2 } = await context.params;
|
|
1519
1876
|
const endpoint = path2[0];
|
|
@@ -1540,12 +1897,79 @@ async function handleAIEditorRequest(req, context) {
|
|
|
1540
1897
|
case "suggestions":
|
|
1541
1898
|
if (method === "GET") return handleSuggestions(req);
|
|
1542
1899
|
break;
|
|
1900
|
+
case "chat":
|
|
1901
|
+
if (method === "POST") return handleChat(req);
|
|
1902
|
+
break;
|
|
1543
1903
|
}
|
|
1544
1904
|
return NextResponse.json(
|
|
1545
1905
|
{ error: `Unknown endpoint: ${endpoint}` },
|
|
1546
1906
|
{ status: 404 }
|
|
1547
1907
|
);
|
|
1548
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
|
+
}
|
|
1549
1973
|
export {
|
|
1550
1974
|
handleEdit as a,
|
|
1551
1975
|
handleRead as b,
|
|
@@ -1555,22 +1979,23 @@ export {
|
|
|
1555
1979
|
handleValidateSession as f,
|
|
1556
1980
|
handleSuggestions as g,
|
|
1557
1981
|
handleAIEditorRequest as h,
|
|
1558
|
-
|
|
1559
|
-
|
|
1560
|
-
|
|
1561
|
-
|
|
1562
|
-
|
|
1982
|
+
handleCommentsRequest as i,
|
|
1983
|
+
isPathSecure as j,
|
|
1984
|
+
fileExists$1 as k,
|
|
1985
|
+
extractComponentName as l,
|
|
1986
|
+
validateGeneratedCode as m,
|
|
1563
1987
|
normalizePath as n,
|
|
1564
|
-
|
|
1988
|
+
findTargetElement as o,
|
|
1565
1989
|
parseFile as p,
|
|
1566
|
-
|
|
1990
|
+
getJSXMemberName as q,
|
|
1567
1991
|
resolveFilePath as r,
|
|
1568
1992
|
scoreElementMatch as s,
|
|
1569
|
-
|
|
1570
|
-
|
|
1993
|
+
getAttributeValue as t,
|
|
1994
|
+
parseDebugStack as u,
|
|
1571
1995
|
validateDevMode as v,
|
|
1572
|
-
|
|
1573
|
-
|
|
1574
|
-
|
|
1996
|
+
extractComponentNameFromStack as w,
|
|
1997
|
+
parseDebugStackFrames as x,
|
|
1998
|
+
resolveOriginalPosition as y,
|
|
1999
|
+
getOriginalPositionFromDebugStack as z
|
|
1575
2000
|
};
|
|
1576
|
-
//# sourceMappingURL=
|
|
2001
|
+
//# sourceMappingURL=comments-D3m0RsOO.js.map
|