@4onstudios/iris-agent 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 (216) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +690 -0
  3. package/dist/api/acp/acpServer.d.ts +23 -0
  4. package/dist/api/acp/acpServer.js +814 -0
  5. package/dist/api/acp/index.d.ts +2 -0
  6. package/dist/api/acp/index.js +2 -0
  7. package/dist/api/acp/irisClient.d.ts +58 -0
  8. package/dist/api/acp/irisClient.js +214 -0
  9. package/dist/api/agent.d.ts +14 -0
  10. package/dist/api/agent.js +4171 -0
  11. package/dist/api/backendService.d.ts +4 -0
  12. package/dist/api/backendService.js +14 -0
  13. package/dist/api/core/agent/host/AgentContract.d.ts +82 -0
  14. package/dist/api/core/agent/host/AgentContract.js +1 -0
  15. package/dist/api/core/agent/host/AgentRegistry.d.ts +24 -0
  16. package/dist/api/core/agent/host/AgentRegistry.js +125 -0
  17. package/dist/api/core/agent/host/defaultRegistry.d.ts +15 -0
  18. package/dist/api/core/agent/host/defaultRegistry.js +46 -0
  19. package/dist/api/core/agent/host/externalAgentLifecycle.d.ts +65 -0
  20. package/dist/api/core/agent/host/externalAgentLifecycle.js +117 -0
  21. package/dist/api/core/agent/host/hostSessionManager.d.ts +111 -0
  22. package/dist/api/core/agent/host/hostSessionManager.js +352 -0
  23. package/dist/api/core/agent/host/index.d.ts +5 -0
  24. package/dist/api/core/agent/host/index.js +5 -0
  25. package/dist/api/core/agent/index.d.ts +87 -0
  26. package/dist/api/core/agent/index.js +1024 -0
  27. package/dist/api/core/agent/publicContracts.d.ts +35 -0
  28. package/dist/api/core/agent/publicContracts.js +103 -0
  29. package/dist/api/core/agent/tools/applyDiff.d.ts +51 -0
  30. package/dist/api/core/agent/tools/applyDiff.js +131 -0
  31. package/dist/api/core/agent/tools/backgroundTasks.d.ts +240 -0
  32. package/dist/api/core/agent/tools/backgroundTasks.js +313 -0
  33. package/dist/api/core/agent/tools/createDirectory.d.ts +43 -0
  34. package/dist/api/core/agent/tools/createDirectory.js +77 -0
  35. package/dist/api/core/agent/tools/deleteFile.d.ts +37 -0
  36. package/dist/api/core/agent/tools/deleteFile.js +66 -0
  37. package/dist/api/core/agent/tools/editFile.d.ts +73 -0
  38. package/dist/api/core/agent/tools/editFile.js +173 -0
  39. package/dist/api/core/agent/tools/executeCommand.d.ts +59 -0
  40. package/dist/api/core/agent/tools/executeCommand.js +250 -0
  41. package/dist/api/core/agent/tools/fileContent.d.ts +54 -0
  42. package/dist/api/core/agent/tools/fileContent.js +227 -0
  43. package/dist/api/core/agent/tools/findDefinition.d.ts +72 -0
  44. package/dist/api/core/agent/tools/findDefinition.js +109 -0
  45. package/dist/api/core/agent/tools/findReferences.d.ts +86 -0
  46. package/dist/api/core/agent/tools/findReferences.js +135 -0
  47. package/dist/api/core/agent/tools/formatDocument.d.ts +67 -0
  48. package/dist/api/core/agent/tools/formatDocument.js +90 -0
  49. package/dist/api/core/agent/tools/getCodeActions.d.ts +77 -0
  50. package/dist/api/core/agent/tools/getCodeActions.js +97 -0
  51. package/dist/api/core/agent/tools/getCodeCompletion.d.ts +77 -0
  52. package/dist/api/core/agent/tools/getCodeCompletion.js +174 -0
  53. package/dist/api/core/agent/tools/getCodeContext.d.ts +14 -0
  54. package/dist/api/core/agent/tools/getCodeContext.js +282 -0
  55. package/dist/api/core/agent/tools/getSignatureHelp.d.ts +61 -0
  56. package/dist/api/core/agent/tools/getSignatureHelp.js +85 -0
  57. package/dist/api/core/agent/tools/getSymbols.d.ts +50 -0
  58. package/dist/api/core/agent/tools/getSymbols.js +164 -0
  59. package/dist/api/core/agent/tools/getSymbolsLSP.d.ts +58 -0
  60. package/dist/api/core/agent/tools/getSymbolsLSP.js +147 -0
  61. package/dist/api/core/agent/tools/getTypeInfo.d.ts +70 -0
  62. package/dist/api/core/agent/tools/getTypeInfo.js +84 -0
  63. package/dist/api/core/agent/tools/getWorkspaceInfo.d.ts +80 -0
  64. package/dist/api/core/agent/tools/getWorkspaceInfo.js +281 -0
  65. package/dist/api/core/agent/tools/getWorkspaceSymbols.d.ts +53 -0
  66. package/dist/api/core/agent/tools/getWorkspaceSymbols.js +97 -0
  67. package/dist/api/core/agent/tools/grepSearch.d.ts +100 -0
  68. package/dist/api/core/agent/tools/grepSearch.js +211 -0
  69. package/dist/api/core/agent/tools/languageModelToolsIntegration.d.ts +13 -0
  70. package/dist/api/core/agent/tools/languageModelToolsIntegration.js +22 -0
  71. package/dist/api/core/agent/tools/listDirectory.d.ts +66 -0
  72. package/dist/api/core/agent/tools/listDirectory.js +161 -0
  73. package/dist/api/core/agent/tools/mcpTools.d.ts +48 -0
  74. package/dist/api/core/agent/tools/mcpTools.js +442 -0
  75. package/dist/api/core/agent/tools/queryKnowledgeGraph.d.ts +120 -0
  76. package/dist/api/core/agent/tools/queryKnowledgeGraph.js +306 -0
  77. package/dist/api/core/agent/tools/readFile.d.ts +92 -0
  78. package/dist/api/core/agent/tools/readFile.js +337 -0
  79. package/dist/api/core/agent/tools/renameFile.d.ts +49 -0
  80. package/dist/api/core/agent/tools/renameFile.js +86 -0
  81. package/dist/api/core/agent/tools/renameSymbol.d.ts +70 -0
  82. package/dist/api/core/agent/tools/renameSymbol.js +106 -0
  83. package/dist/api/core/agent/tools/runTerminalCommand.d.ts +72 -0
  84. package/dist/api/core/agent/tools/runTerminalCommand.js +69 -0
  85. package/dist/api/core/agent/tools/searchFiles.d.ts +82 -0
  86. package/dist/api/core/agent/tools/searchFiles.js +280 -0
  87. package/dist/api/core/agent/tools/terminalAutoApprove.d.ts +3 -0
  88. package/dist/api/core/agent/tools/terminalAutoApprove.js +676 -0
  89. package/dist/api/core/agent/tools/treeTraversal.d.ts +60 -0
  90. package/dist/api/core/agent/tools/treeTraversal.js +217 -0
  91. package/dist/api/core/agent/tools/webSearch.d.ts +37 -0
  92. package/dist/api/core/agent/tools/webSearch.js +80 -0
  93. package/dist/api/core/agent/tools/writeFile.d.ts +59 -0
  94. package/dist/api/core/agent/tools/writeFile.js +97 -0
  95. package/dist/api/core/agent/utils/capturedWorkspaceMutationBridge.d.ts +12 -0
  96. package/dist/api/core/agent/utils/capturedWorkspaceMutationBridge.js +67 -0
  97. package/dist/api/core/agent/utils/diffUtils.d.ts +14 -0
  98. package/dist/api/core/agent/utils/diffUtils.js +39 -0
  99. package/dist/api/core/agent/utils/environmentSnapshot.d.ts +42 -0
  100. package/dist/api/core/agent/utils/environmentSnapshot.js +213 -0
  101. package/dist/api/core/agent/utils/errorRecovery.d.ts +49 -0
  102. package/dist/api/core/agent/utils/errorRecovery.js +229 -0
  103. package/dist/api/core/agent/utils/multimodalTokenLimiter.d.ts +8 -0
  104. package/dist/api/core/agent/utils/multimodalTokenLimiter.js +238 -0
  105. package/dist/api/core/agent/utils/openRouterModelSettings.d.ts +12 -0
  106. package/dist/api/core/agent/utils/openRouterModelSettings.js +34 -0
  107. package/dist/api/core/agent/utils/pathRecovery.d.ts +1 -0
  108. package/dist/api/core/agent/utils/pathRecovery.js +29 -0
  109. package/dist/api/core/agent/utils/repoMapIndex.d.ts +3 -0
  110. package/dist/api/core/agent/utils/repoMapIndex.js +197 -0
  111. package/dist/api/core/agent/utils/skillsDiscovery.d.ts +10 -0
  112. package/dist/api/core/agent/utils/skillsDiscovery.js +50 -0
  113. package/dist/api/core/agent/utils/toolCallBudget.d.ts +9 -0
  114. package/dist/api/core/agent/utils/toolCallBudget.js +55 -0
  115. package/dist/api/core/agent/utils/toolLifecycle.d.ts +30 -0
  116. package/dist/api/core/agent/utils/toolLifecycle.js +361 -0
  117. package/dist/api/core/agent/utils/toolResultSafetyProcessor.d.ts +12 -0
  118. package/dist/api/core/agent/utils/toolResultSafetyProcessor.js +57 -0
  119. package/dist/api/core/agent/utils/workspaceMutationBridge.d.ts +13 -0
  120. package/dist/api/core/agent/utils/workspaceMutationBridge.js +149 -0
  121. package/dist/api/core/agent/utils/workspacePathGuard.d.ts +7 -0
  122. package/dist/api/core/agent/utils/workspacePathGuard.js +79 -0
  123. package/dist/api/core/containers/chat/toolResultSerialization.d.ts +8 -0
  124. package/dist/api/core/containers/chat/toolResultSerialization.js +71 -0
  125. package/dist/api/core/library/BrowserManager.d.ts +11 -0
  126. package/dist/api/core/library/BrowserManager.js +66 -0
  127. package/dist/api/core/library/desktopWorkspace.d.ts +1 -0
  128. package/dist/api/core/library/desktopWorkspace.js +3 -0
  129. package/dist/api/core/library/extensionManager.d.ts +10 -0
  130. package/dist/api/core/library/extensionManager.js +27 -0
  131. package/dist/api/core/library/knowledgeGraph.d.ts +268 -0
  132. package/dist/api/core/library/knowledgeGraph.js +989 -0
  133. package/dist/api/core/library/languageModelTools.d.ts +141 -0
  134. package/dist/api/core/library/languageModelTools.js +207 -0
  135. package/dist/api/core/library/localRuntime.d.ts +23 -0
  136. package/dist/api/core/library/localRuntime.js +144 -0
  137. package/dist/api/core/library/lsp/coreLsp.d.ts +367 -0
  138. package/dist/api/core/library/lsp/coreLsp.js +2076 -0
  139. package/dist/api/core/library/lsp/protocol.d.ts +25 -0
  140. package/dist/api/core/library/lsp/protocol.js +1 -0
  141. package/dist/api/core/library/lsp/serverManager.d.ts +37 -0
  142. package/dist/api/core/library/lsp/serverManager.js +427 -0
  143. package/dist/api/core/library/mcpServerProvider.d.ts +69 -0
  144. package/dist/api/core/library/mcpServerProvider.js +152 -0
  145. package/dist/api/core/library/mcpSettings.d.ts +20 -0
  146. package/dist/api/core/library/mcpSettings.js +131 -0
  147. package/dist/api/core/library/patternMatcher.d.ts +66 -0
  148. package/dist/api/core/library/patternMatcher.js +284 -0
  149. package/dist/api/core/library/regexEscape.d.ts +4 -0
  150. package/dist/api/core/library/regexEscape.js +4 -0
  151. package/dist/api/core/library/runtimeEventBus.d.ts +27 -0
  152. package/dist/api/core/library/runtimeEventBus.js +78 -0
  153. package/dist/api/core/library/safetyMiddleware.d.ts +98 -0
  154. package/dist/api/core/library/safetyMiddleware.js +215 -0
  155. package/dist/api/core/library/tauri.d.ts +71 -0
  156. package/dist/api/core/library/tauri.js +222 -0
  157. package/dist/api/core/library/tauriImport.d.ts +5 -0
  158. package/dist/api/core/library/tauriImport.js +7 -0
  159. package/dist/api/core/library/terminalAutoApproveSettings.d.ts +32 -0
  160. package/dist/api/core/library/terminalAutoApproveSettings.js +529 -0
  161. package/dist/api/core/library/workspaceIdentity.d.ts +10 -0
  162. package/dist/api/core/library/workspaceIdentity.js +49 -0
  163. package/dist/api/core/library/workspaceSummary.d.ts +19 -0
  164. package/dist/api/core/library/workspaceSummary.js +35 -0
  165. package/dist/api/core/skills/agent-customization/SKILL.md +27 -0
  166. package/dist/api/core/skills/bug-fix/SKILL.md +21 -0
  167. package/dist/api/core/skills/create-pr/SKILL.md +32 -0
  168. package/dist/api/core/skills/dev-server/SKILL.md +20 -0
  169. package/dist/api/core/skills/fix-suggestions/SKILL.md +21 -0
  170. package/dist/api/core/skills/github-search/SKILL.md +21 -0
  171. package/dist/api/core/skills/github-summary/SKILL.md +25 -0
  172. package/dist/api/core/skills/integration-tests/SKILL.md +29 -0
  173. package/dist/api/core/skills/pr-comments/SKILL.md +23 -0
  174. package/dist/api/core/skills/project-setup/SKILL.md +20 -0
  175. package/dist/api/core/skills/search-integration/SKILL.md +19 -0
  176. package/dist/api/core/skills/search-results/SKILL.md +19 -0
  177. package/dist/api/core/skills/typescript-upgrade/SKILL.md +21 -0
  178. package/dist/api/data/runStore.d.ts +46 -0
  179. package/dist/api/data/runStore.js +244 -0
  180. package/dist/api/helpers/agentUtils.d.ts +11 -0
  181. package/dist/api/helpers/agentUtils.js +23 -0
  182. package/dist/api/helpers/modelTokenLimits.d.ts +2 -0
  183. package/dist/api/helpers/modelTokenLimits.js +158 -0
  184. package/dist/api/helpers/observationalMemory.d.ts +10 -0
  185. package/dist/api/helpers/observationalMemory.js +36 -0
  186. package/dist/api/helpers/promptBudget.d.ts +30 -0
  187. package/dist/api/helpers/promptBudget.js +216 -0
  188. package/dist/api/helpers/resolveImageMessageParts.d.ts +9 -0
  189. package/dist/api/helpers/resolveImageMessageParts.js +140 -0
  190. package/dist/api/helpers/slashCommands.d.ts +31 -0
  191. package/dist/api/helpers/slashCommands.js +129 -0
  192. package/dist/api/helpers/tokenUsage.d.ts +19 -0
  193. package/dist/api/helpers/tokenUsage.js +81 -0
  194. package/dist/api/routes/fileRoutes.d.ts +2 -0
  195. package/dist/api/routes/fileRoutes.js +108 -0
  196. package/dist/api/routes/lspDocumentRoutes.d.ts +4 -0
  197. package/dist/api/routes/lspDocumentRoutes.js +84 -0
  198. package/dist/api/routes/lspHierarchyRoutes.d.ts +4 -0
  199. package/dist/api/routes/lspHierarchyRoutes.js +122 -0
  200. package/dist/api/routes/lspPositionRoutes.d.ts +4 -0
  201. package/dist/api/routes/lspPositionRoutes.js +263 -0
  202. package/dist/api/routes/lspQueryRoutes.d.ts +4 -0
  203. package/dist/api/routes/lspQueryRoutes.js +679 -0
  204. package/dist/api/routes/lspResolveRoutes.d.ts +4 -0
  205. package/dist/api/routes/lspResolveRoutes.js +143 -0
  206. package/dist/api/routes/lspSemanticDocumentRoutes.d.ts +4 -0
  207. package/dist/api/routes/lspSemanticDocumentRoutes.js +98 -0
  208. package/dist/api/routes/semanticRoutes.d.ts +2 -0
  209. package/dist/api/routes/semanticRoutes.js +126 -0
  210. package/dist/cli.d.ts +8 -0
  211. package/dist/cli.js +160 -0
  212. package/dist/index.d.ts +1 -0
  213. package/dist/index.js +1 -0
  214. package/dist/server.d.ts +2 -0
  215. package/dist/server.js +38 -0
  216. package/package.json +100 -0
@@ -0,0 +1,679 @@
1
+ import path from "path";
2
+ import { coreLsp } from "../core/library/lsp/coreLsp.js";
3
+ const resolveAbsolutePath = (workspacePath, filePath) => path.isAbsolute(filePath) ? filePath : path.join(workspacePath, filePath);
4
+ const buildFormatOptions = (options) => ({
5
+ tabSize: typeof options?.tabSize === "number" ? options.tabSize : 2,
6
+ insertSpaces: typeof options?.insertSpaces === "boolean" ? options.insertSpaces : true,
7
+ trimTrailingWhitespace: typeof options?.trimTrailingWhitespace === "boolean" ? options.trimTrailingWhitespace : true,
8
+ insertFinalNewline: typeof options?.insertFinalNewline === "boolean" ? options.insertFinalNewline : true,
9
+ trimFinalNewlines: typeof options?.trimFinalNewlines === "boolean" ? options.trimFinalNewlines : true,
10
+ });
11
+ export const registerLspQueryRoutes = (router, requireDesktopAuth) => {
12
+ router.post("/lsp/diagnostics", requireDesktopAuth, async (req, res) => {
13
+ try {
14
+ const { filePath, workspaceRoot } = req.body;
15
+ if (typeof filePath !== "string" || filePath.trim().length === 0) {
16
+ return res.status(400).json({ success: false, error: "filePath is required" });
17
+ }
18
+ const workspacePath = workspaceRoot || process.cwd();
19
+ const absolutePath = resolveAbsolutePath(workspacePath, filePath);
20
+ coreLsp.setRootPath(workspacePath);
21
+ const diagnostics = coreLsp.getDiagnosticsForFile(absolutePath);
22
+ return res.json({ success: true, diagnostics });
23
+ }
24
+ catch (error) {
25
+ const err = error;
26
+ return res.status(500).json({ success: false, error: err.message || "Failed to get diagnostics" });
27
+ }
28
+ });
29
+ router.post("/lsp/diagnostic", requireDesktopAuth, async (req, res) => {
30
+ try {
31
+ const { filePath, workspaceRoot } = req.body;
32
+ if (typeof filePath !== "string" || filePath.trim().length === 0) {
33
+ return res.status(400).json({ success: false, error: "filePath is required" });
34
+ }
35
+ const workspacePath = workspaceRoot || process.cwd();
36
+ const absolutePath = resolveAbsolutePath(workspacePath, filePath);
37
+ coreLsp.setRootPath(workspacePath);
38
+ const report = await coreLsp.getPullDiagnosticsForFile(absolutePath);
39
+ if (!report) {
40
+ return res.json({ success: false, error: "No diagnostic report available" });
41
+ }
42
+ return res.json({ success: true, report });
43
+ }
44
+ catch (error) {
45
+ const err = error;
46
+ return res.status(500).json({ success: false, error: err.message || "Failed to get diagnostic report" });
47
+ }
48
+ });
49
+ router.post("/lsp/workspace-diagnostic", requireDesktopAuth, async (req, res) => {
50
+ try {
51
+ const { workspaceRoot, languageId } = req.body;
52
+ const workspacePath = workspaceRoot || process.cwd();
53
+ coreLsp.setRootPath(workspacePath);
54
+ const report = await coreLsp.getWorkspacePullDiagnostics(languageId);
55
+ if (!report) {
56
+ return res.json({ success: false, error: "No workspace diagnostic report available" });
57
+ }
58
+ return res.json({ success: true, report });
59
+ }
60
+ catch (error) {
61
+ const err = error;
62
+ return res.status(500).json({ success: false, error: err.message || "Failed to get workspace diagnostic report" });
63
+ }
64
+ });
65
+ router.post("/lsp/code-actions", requireDesktopAuth, async (req, res) => {
66
+ try {
67
+ const { filePath, workspaceRoot, startLine, startCharacter, endLine, endCharacter, context, } = req.body;
68
+ if (typeof filePath !== "string" || filePath.trim().length === 0) {
69
+ return res.status(400).json({ success: false, error: "filePath is required" });
70
+ }
71
+ if (typeof startLine !== "number" ||
72
+ typeof startCharacter !== "number" ||
73
+ typeof endLine !== "number" ||
74
+ typeof endCharacter !== "number") {
75
+ return res.status(400).json({
76
+ success: false,
77
+ error: "startLine, startCharacter, endLine, and endCharacter are required",
78
+ });
79
+ }
80
+ const workspacePath = workspaceRoot || process.cwd();
81
+ const absolutePath = resolveAbsolutePath(workspacePath, filePath);
82
+ coreLsp.setRootPath(workspacePath);
83
+ const actions = await coreLsp.getCodeActions(absolutePath, startLine, startCharacter, endLine, endCharacter, context);
84
+ return res.json({ success: true, actions });
85
+ }
86
+ catch (error) {
87
+ const err = error;
88
+ return res.status(500).json({ success: false, error: err.message || "Failed to get code actions" });
89
+ }
90
+ });
91
+ router.post("/lsp/rename", requireDesktopAuth, async (req, res) => {
92
+ try {
93
+ const { filePath, workspaceRoot, line, character, newName } = req.body;
94
+ if (typeof filePath !== "string" || filePath.trim().length === 0) {
95
+ return res.status(400).json({ success: false, error: "filePath is required" });
96
+ }
97
+ if (typeof line !== "number" || typeof character !== "number" || typeof newName !== "string") {
98
+ return res.status(400).json({ success: false, error: "line, character, and newName are required" });
99
+ }
100
+ const workspacePath = workspaceRoot || process.cwd();
101
+ const absolutePath = resolveAbsolutePath(workspacePath, filePath);
102
+ coreLsp.setRootPath(workspacePath);
103
+ const result = await coreLsp.renameSymbol(absolutePath, line, character, newName);
104
+ return res.json({ success: true, edit: result });
105
+ }
106
+ catch (error) {
107
+ const err = error;
108
+ return res.status(500).json({ success: false, error: err.message || "Failed to rename symbol" });
109
+ }
110
+ });
111
+ router.post("/lsp/prepare-rename", requireDesktopAuth, async (req, res) => {
112
+ try {
113
+ const { filePath, workspaceRoot, line, character } = req.body;
114
+ if (typeof filePath !== "string" || filePath.trim().length === 0) {
115
+ return res.status(400).json({ success: false, error: "filePath is required" });
116
+ }
117
+ if (typeof line !== "number" || typeof character !== "number") {
118
+ return res.status(400).json({ success: false, error: "line and character are required" });
119
+ }
120
+ const workspacePath = workspaceRoot || process.cwd();
121
+ const absolutePath = resolveAbsolutePath(workspacePath, filePath);
122
+ coreLsp.setRootPath(workspacePath);
123
+ const result = await coreLsp.prepareRename(absolutePath, line, character);
124
+ return res.json({ success: true, range: result });
125
+ }
126
+ catch (error) {
127
+ const err = error;
128
+ return res.status(500).json({ success: false, error: err.message || "Failed to prepare rename" });
129
+ }
130
+ });
131
+ router.post("/lsp/format", requireDesktopAuth, async (req, res) => {
132
+ try {
133
+ const { filePath, workspaceRoot, options } = req.body;
134
+ if (typeof filePath !== "string" || filePath.trim().length === 0) {
135
+ return res.status(400).json({ success: false, error: "filePath is required" });
136
+ }
137
+ const workspacePath = workspaceRoot || process.cwd();
138
+ const absolutePath = resolveAbsolutePath(workspacePath, filePath);
139
+ const formatOptions = buildFormatOptions(options);
140
+ coreLsp.setRootPath(workspacePath);
141
+ const edits = await coreLsp.formatDocument(absolutePath, formatOptions);
142
+ return res.json({ success: true, edits });
143
+ }
144
+ catch (error) {
145
+ const err = error;
146
+ return res.status(500).json({ success: false, error: err.message || "Failed to format document" });
147
+ }
148
+ });
149
+ router.post("/lsp/range-format", requireDesktopAuth, async (req, res) => {
150
+ try {
151
+ const { filePath, workspaceRoot, startLine, startCharacter, endLine, endCharacter, options, } = req.body;
152
+ if (typeof filePath !== "string" || filePath.trim().length === 0) {
153
+ return res.status(400).json({ success: false, error: "filePath is required" });
154
+ }
155
+ if (typeof startLine !== "number" ||
156
+ typeof startCharacter !== "number" ||
157
+ typeof endLine !== "number" ||
158
+ typeof endCharacter !== "number") {
159
+ return res.status(400).json({
160
+ success: false,
161
+ error: "startLine, startCharacter, endLine, and endCharacter are required",
162
+ });
163
+ }
164
+ const workspacePath = workspaceRoot || process.cwd();
165
+ const absolutePath = resolveAbsolutePath(workspacePath, filePath);
166
+ const formatOptions = buildFormatOptions(options);
167
+ coreLsp.setRootPath(workspacePath);
168
+ const edits = await coreLsp.formatRange(absolutePath, startLine, startCharacter, endLine, endCharacter, formatOptions);
169
+ return res.json({ success: true, edits });
170
+ }
171
+ catch (error) {
172
+ const err = error;
173
+ return res.status(500).json({ success: false, error: err.message || "Failed to format range" });
174
+ }
175
+ });
176
+ router.post("/lsp/on-type-format", requireDesktopAuth, async (req, res) => {
177
+ try {
178
+ const { filePath, workspaceRoot, line, character, ch, options } = req.body;
179
+ if (typeof filePath !== "string" || filePath.trim().length === 0) {
180
+ return res.status(400).json({ success: false, error: "filePath is required" });
181
+ }
182
+ if (typeof line !== "number" ||
183
+ typeof character !== "number" ||
184
+ typeof ch !== "string" ||
185
+ ch.length === 0) {
186
+ return res.status(400).json({ success: false, error: "line, character, and ch are required" });
187
+ }
188
+ const workspacePath = workspaceRoot || process.cwd();
189
+ const absolutePath = resolveAbsolutePath(workspacePath, filePath);
190
+ const formatOptions = buildFormatOptions(options);
191
+ coreLsp.setRootPath(workspacePath);
192
+ const edits = await coreLsp.formatOnType(absolutePath, line, character, ch, formatOptions);
193
+ return res.json({ success: true, edits });
194
+ }
195
+ catch (error) {
196
+ const err = error;
197
+ return res.status(500).json({ success: false, error: err.message || "Failed on type formatting" });
198
+ }
199
+ });
200
+ router.post("/lsp/workspace-symbols", requireDesktopAuth, async (req, res) => {
201
+ try {
202
+ const { workspaceRoot, query, languageId } = req.body;
203
+ if (typeof query !== "string") {
204
+ return res.status(400).json({ success: false, error: "query is required" });
205
+ }
206
+ const workspacePath = workspaceRoot || process.cwd();
207
+ coreLsp.setRootPath(workspacePath);
208
+ const symbols = await coreLsp.getWorkspaceSymbols(query, languageId);
209
+ return res.json({ success: true, symbols });
210
+ }
211
+ catch (error) {
212
+ const err = error;
213
+ return res.status(500).json({ success: false, error: err.message || "Failed to get workspace symbols" });
214
+ }
215
+ });
216
+ router.post("/lsp/signature-help", requireDesktopAuth, async (req, res) => {
217
+ try {
218
+ const { filePath, workspaceRoot, line, character } = req.body;
219
+ if (typeof filePath !== "string" || filePath.trim().length === 0) {
220
+ return res.status(400).json({ success: false, error: "filePath is required" });
221
+ }
222
+ if (typeof line !== "number" || typeof character !== "number") {
223
+ return res.status(400).json({ success: false, error: "line and character are required" });
224
+ }
225
+ const workspacePath = workspaceRoot || process.cwd();
226
+ const absolutePath = resolveAbsolutePath(workspacePath, filePath);
227
+ coreLsp.setRootPath(workspacePath);
228
+ const signatureHelp = await coreLsp.getSignatureHelp(absolutePath, line, character);
229
+ return res.json({ success: true, signatureHelp });
230
+ }
231
+ catch (error) {
232
+ const err = error;
233
+ return res.status(500).json({ success: false, error: err.message || "Failed to get signature help" });
234
+ }
235
+ });
236
+ router.post("/lsp/configuration", requireDesktopAuth, async (req, res) => {
237
+ try {
238
+ const { workspaceRoot, settings } = req.body;
239
+ const workspacePath = workspaceRoot || process.cwd();
240
+ coreLsp.setRootPath(workspacePath);
241
+ await coreLsp.didChangeConfiguration(settings ?? {});
242
+ return res.json({ success: true });
243
+ }
244
+ catch (error) {
245
+ const err = error;
246
+ return res.status(500).json({ success: false, error: err.message || "Failed to update LSP configuration" });
247
+ }
248
+ });
249
+ router.post("/lsp/watched-files", requireDesktopAuth, async (req, res) => {
250
+ try {
251
+ const { workspaceRoot, changes } = req.body;
252
+ if (!Array.isArray(changes)) {
253
+ return res.status(400).json({ success: false, error: "changes array is required" });
254
+ }
255
+ const normalizedChanges = changes
256
+ .filter((change) => typeof change.filePath === "string" && typeof change.type === "number")
257
+ .map((change) => ({
258
+ filePath: String(change.filePath),
259
+ type: change.type,
260
+ }));
261
+ const workspacePath = workspaceRoot || process.cwd();
262
+ coreLsp.setRootPath(workspacePath);
263
+ await coreLsp.didChangeWatchedFiles(normalizedChanges);
264
+ return res.json({ success: true, count: normalizedChanges.length });
265
+ }
266
+ catch (error) {
267
+ const err = error;
268
+ return res.status(500).json({ success: false, error: err.message || "Failed to notify watched files" });
269
+ }
270
+ });
271
+ router.post("/lsp/will-save", requireDesktopAuth, async (req, res) => {
272
+ try {
273
+ const { filePath, workspaceRoot, reason } = req.body;
274
+ if (typeof filePath !== "string" || filePath.trim().length === 0) {
275
+ return res.status(400).json({ success: false, error: "filePath is required" });
276
+ }
277
+ const workspacePath = workspaceRoot || process.cwd();
278
+ const absolutePath = resolveAbsolutePath(workspacePath, filePath);
279
+ coreLsp.setRootPath(workspacePath);
280
+ coreLsp.willSaveDocument(absolutePath, typeof reason === "number" ? reason : 1);
281
+ return res.json({ success: true });
282
+ }
283
+ catch (error) {
284
+ const err = error;
285
+ return res.status(500).json({ success: false, error: err.message || "Failed to send willSave" });
286
+ }
287
+ });
288
+ router.post("/lsp/workspace-folders", requireDesktopAuth, async (req, res) => {
289
+ try {
290
+ const { workspaceRoot, added, removed } = req.body;
291
+ const normalizeFolder = (f) => ({
292
+ uri: typeof f.uri === "string" ? f.uri : "",
293
+ name: typeof f.name === "string" ? f.name : "",
294
+ });
295
+ const addedFolders = Array.isArray(added) ? added.map(normalizeFolder) : [];
296
+ const removedFolders = Array.isArray(removed) ? removed.map(normalizeFolder) : [];
297
+ const workspacePath = workspaceRoot || process.cwd();
298
+ coreLsp.setRootPath(workspacePath);
299
+ await coreLsp.didChangeWorkspaceFolders(addedFolders, removedFolders);
300
+ return res.json({ success: true, added: addedFolders.length, removed: removedFolders.length });
301
+ }
302
+ catch (error) {
303
+ const err = error;
304
+ return res.status(500).json({ success: false, error: err.message || "Failed to notify workspace folder changes" });
305
+ }
306
+ });
307
+ router.post("/lsp/will-create-files", requireDesktopAuth, async (req, res) => {
308
+ try {
309
+ const { workspaceRoot, filePaths } = req.body;
310
+ if (!Array.isArray(filePaths)) {
311
+ return res.status(400).json({ success: false, error: "filePaths array is required" });
312
+ }
313
+ const workspacePath = workspaceRoot || process.cwd();
314
+ const normalized = filePaths
315
+ .filter((filePath) => typeof filePath === "string" && filePath.trim().length > 0)
316
+ .map((filePath) => (path.isAbsolute(filePath) ? filePath : path.join(workspacePath, filePath)));
317
+ coreLsp.setRootPath(workspacePath);
318
+ const results = await coreLsp.willCreateFiles(normalized);
319
+ return res.json({ success: true, count: normalized.length, results });
320
+ }
321
+ catch (error) {
322
+ const err = error;
323
+ return res.status(500).json({ success: false, error: err.message || "Failed to notify willCreateFiles" });
324
+ }
325
+ });
326
+ router.post("/lsp/did-create-files", requireDesktopAuth, async (req, res) => {
327
+ try {
328
+ const { workspaceRoot, filePaths } = req.body;
329
+ if (!Array.isArray(filePaths)) {
330
+ return res.status(400).json({ success: false, error: "filePaths array is required" });
331
+ }
332
+ const workspacePath = workspaceRoot || process.cwd();
333
+ const normalized = filePaths
334
+ .filter((filePath) => typeof filePath === "string" && filePath.trim().length > 0)
335
+ .map((filePath) => (path.isAbsolute(filePath) ? filePath : path.join(workspacePath, filePath)));
336
+ coreLsp.setRootPath(workspacePath);
337
+ await coreLsp.didCreateFiles(normalized);
338
+ return res.json({ success: true, count: normalized.length });
339
+ }
340
+ catch (error) {
341
+ const err = error;
342
+ return res.status(500).json({ success: false, error: err.message || "Failed to notify didCreateFiles" });
343
+ }
344
+ });
345
+ router.post("/lsp/will-rename-files", requireDesktopAuth, async (req, res) => {
346
+ try {
347
+ const { workspaceRoot, files } = req.body;
348
+ if (!Array.isArray(files)) {
349
+ return res.status(400).json({ success: false, error: "files array is required" });
350
+ }
351
+ const workspacePath = workspaceRoot || process.cwd();
352
+ const normalized = files
353
+ .filter((file) => typeof file.oldFilePath === "string" &&
354
+ file.oldFilePath.trim().length > 0 &&
355
+ typeof file.newFilePath === "string" &&
356
+ file.newFilePath.trim().length > 0)
357
+ .map((file) => ({
358
+ oldFilePath: path.isAbsolute(file.oldFilePath)
359
+ ? file.oldFilePath
360
+ : path.join(workspacePath, String(file.oldFilePath)),
361
+ newFilePath: path.isAbsolute(file.newFilePath)
362
+ ? file.newFilePath
363
+ : path.join(workspacePath, String(file.newFilePath)),
364
+ }));
365
+ coreLsp.setRootPath(workspacePath);
366
+ const results = await coreLsp.willRenameFiles(normalized);
367
+ return res.json({ success: true, count: normalized.length, results });
368
+ }
369
+ catch (error) {
370
+ const err = error;
371
+ return res.status(500).json({ success: false, error: err.message || "Failed to notify willRenameFiles" });
372
+ }
373
+ });
374
+ router.post("/lsp/did-rename-files", requireDesktopAuth, async (req, res) => {
375
+ try {
376
+ const { workspaceRoot, files } = req.body;
377
+ if (!Array.isArray(files)) {
378
+ return res.status(400).json({ success: false, error: "files array is required" });
379
+ }
380
+ const workspacePath = workspaceRoot || process.cwd();
381
+ const normalized = files
382
+ .filter((file) => typeof file.oldFilePath === "string" &&
383
+ file.oldFilePath.trim().length > 0 &&
384
+ typeof file.newFilePath === "string" &&
385
+ file.newFilePath.trim().length > 0)
386
+ .map((file) => ({
387
+ oldFilePath: path.isAbsolute(file.oldFilePath)
388
+ ? file.oldFilePath
389
+ : path.join(workspacePath, String(file.oldFilePath)),
390
+ newFilePath: path.isAbsolute(file.newFilePath)
391
+ ? file.newFilePath
392
+ : path.join(workspacePath, String(file.newFilePath)),
393
+ }));
394
+ coreLsp.setRootPath(workspacePath);
395
+ await coreLsp.didRenameFiles(normalized);
396
+ return res.json({ success: true, count: normalized.length });
397
+ }
398
+ catch (error) {
399
+ const err = error;
400
+ return res.status(500).json({ success: false, error: err.message || "Failed to notify didRenameFiles" });
401
+ }
402
+ });
403
+ router.post("/lsp/will-delete-files", requireDesktopAuth, async (req, res) => {
404
+ try {
405
+ const { workspaceRoot, filePaths } = req.body;
406
+ if (!Array.isArray(filePaths)) {
407
+ return res.status(400).json({ success: false, error: "filePaths array is required" });
408
+ }
409
+ const workspacePath = workspaceRoot || process.cwd();
410
+ const normalized = filePaths
411
+ .filter((filePath) => typeof filePath === "string" && filePath.trim().length > 0)
412
+ .map((filePath) => (path.isAbsolute(filePath) ? filePath : path.join(workspacePath, filePath)));
413
+ coreLsp.setRootPath(workspacePath);
414
+ const results = await coreLsp.willDeleteFiles(normalized);
415
+ return res.json({ success: true, count: normalized.length, results });
416
+ }
417
+ catch (error) {
418
+ const err = error;
419
+ return res.status(500).json({ success: false, error: err.message || "Failed to notify willDeleteFiles" });
420
+ }
421
+ });
422
+ router.post("/lsp/did-delete-files", requireDesktopAuth, async (req, res) => {
423
+ try {
424
+ const { workspaceRoot, filePaths } = req.body;
425
+ if (!Array.isArray(filePaths)) {
426
+ return res.status(400).json({ success: false, error: "filePaths array is required" });
427
+ }
428
+ const workspacePath = workspaceRoot || process.cwd();
429
+ const normalized = filePaths
430
+ .filter((filePath) => typeof filePath === "string" && filePath.trim().length > 0)
431
+ .map((filePath) => (path.isAbsolute(filePath) ? filePath : path.join(workspacePath, filePath)));
432
+ coreLsp.setRootPath(workspacePath);
433
+ await coreLsp.didDeleteFiles(normalized);
434
+ return res.json({ success: true, count: normalized.length });
435
+ }
436
+ catch (error) {
437
+ const err = error;
438
+ return res.status(500).json({ success: false, error: err.message || "Failed to notify didDeleteFiles" });
439
+ }
440
+ });
441
+ router.post("/lsp/document-symbols", requireDesktopAuth, async (req, res) => {
442
+ try {
443
+ const { filePath, workspaceRoot } = req.body;
444
+ if (typeof filePath !== "string" || filePath.trim().length === 0) {
445
+ return res.status(400).json({ success: false, error: "filePath is required" });
446
+ }
447
+ const workspacePath = workspaceRoot || process.cwd();
448
+ const absolutePath = resolveAbsolutePath(workspacePath, filePath);
449
+ coreLsp.setRootPath(workspacePath);
450
+ const symbols = await coreLsp.getDocumentSymbols(absolutePath);
451
+ return res.json({ success: true, symbols: Array.isArray(symbols) ? symbols : [] });
452
+ }
453
+ catch (error) {
454
+ const err = error;
455
+ return res.status(500).json({ success: false, error: err.message || "Failed to get document symbols" });
456
+ }
457
+ });
458
+ router.post("/lsp/code-lens", requireDesktopAuth, async (req, res) => {
459
+ try {
460
+ const { filePath, workspaceRoot } = req.body;
461
+ if (typeof filePath !== "string" || filePath.trim().length === 0) {
462
+ return res.status(400).json({ success: false, error: "filePath is required" });
463
+ }
464
+ const workspacePath = workspaceRoot || process.cwd();
465
+ const absolutePath = resolveAbsolutePath(workspacePath, filePath);
466
+ coreLsp.setRootPath(workspacePath);
467
+ const codeLens = await coreLsp.getCodeLens(absolutePath);
468
+ return res.json({ success: true, codeLens: Array.isArray(codeLens) ? codeLens : [] });
469
+ }
470
+ catch (error) {
471
+ const err = error;
472
+ return res.status(500).json({ success: false, error: err.message || "Failed to get code lens" });
473
+ }
474
+ });
475
+ router.post("/lsp/folding-ranges", requireDesktopAuth, async (req, res) => {
476
+ try {
477
+ const { filePath, workspaceRoot } = req.body;
478
+ if (typeof filePath !== "string" || filePath.trim().length === 0) {
479
+ return res.status(400).json({ success: false, error: "filePath is required" });
480
+ }
481
+ const workspacePath = workspaceRoot || process.cwd();
482
+ const absolutePath = resolveAbsolutePath(workspacePath, filePath);
483
+ coreLsp.setRootPath(workspacePath);
484
+ const ranges = await coreLsp.getFoldingRange(absolutePath);
485
+ return res.json({ success: true, ranges: Array.isArray(ranges) ? ranges : [] });
486
+ }
487
+ catch (error) {
488
+ const err = error;
489
+ return res.status(500).json({ success: false, error: err.message || "Failed to get folding ranges" });
490
+ }
491
+ });
492
+ router.post("/lsp/document-colors", requireDesktopAuth, async (req, res) => {
493
+ try {
494
+ const { filePath, workspaceRoot } = req.body;
495
+ if (typeof filePath !== "string" || filePath.trim().length === 0) {
496
+ return res.status(400).json({ success: false, error: "filePath is required" });
497
+ }
498
+ const workspacePath = workspaceRoot || process.cwd();
499
+ const absolutePath = resolveAbsolutePath(workspacePath, filePath);
500
+ coreLsp.setRootPath(workspacePath);
501
+ const colors = await coreLsp.getDocumentColors(absolutePath);
502
+ return res.json({ success: true, colors: Array.isArray(colors) ? colors : [] });
503
+ }
504
+ catch (error) {
505
+ const err = error;
506
+ return res.status(500).json({ success: false, error: err.message || "Failed to get document colors" });
507
+ }
508
+ });
509
+ router.post("/lsp/color-presentations", requireDesktopAuth, async (req, res) => {
510
+ try {
511
+ const { filePath, workspaceRoot, color, startLine, startCharacter, endLine, endCharacter, } = req.body;
512
+ if (typeof filePath !== "string" || filePath.trim().length === 0) {
513
+ return res.status(400).json({ success: false, error: "filePath is required" });
514
+ }
515
+ if (color == null ||
516
+ typeof startLine !== "number" ||
517
+ typeof startCharacter !== "number" ||
518
+ typeof endLine !== "number" ||
519
+ typeof endCharacter !== "number") {
520
+ return res.status(400).json({
521
+ success: false,
522
+ error: "color, startLine, startCharacter, endLine, and endCharacter are required",
523
+ });
524
+ }
525
+ const workspacePath = workspaceRoot || process.cwd();
526
+ const absolutePath = resolveAbsolutePath(workspacePath, filePath);
527
+ coreLsp.setRootPath(workspacePath);
528
+ const presentations = await coreLsp.getColorPresentations(absolutePath, color, startLine, startCharacter, endLine, endCharacter);
529
+ return res.json({ success: true, presentations: Array.isArray(presentations) ? presentations : [] });
530
+ }
531
+ catch (error) {
532
+ const err = error;
533
+ return res.status(500).json({ success: false, error: err.message || "Failed to get color presentations" });
534
+ }
535
+ });
536
+ router.post("/lsp/document-links", requireDesktopAuth, async (req, res) => {
537
+ try {
538
+ const { filePath, workspaceRoot } = req.body;
539
+ if (typeof filePath !== "string" || filePath.trim().length === 0) {
540
+ return res.status(400).json({ success: false, error: "filePath is required" });
541
+ }
542
+ const workspacePath = workspaceRoot || process.cwd();
543
+ const absolutePath = resolveAbsolutePath(workspacePath, filePath);
544
+ coreLsp.setRootPath(workspacePath);
545
+ const links = await coreLsp.getDocumentLinks(absolutePath);
546
+ return res.json({ success: true, links: Array.isArray(links) ? links : [] });
547
+ }
548
+ catch (error) {
549
+ const err = error;
550
+ return res.status(500).json({ success: false, error: err.message || "Failed to get document links" });
551
+ }
552
+ });
553
+ router.post("/lsp/execute-command", requireDesktopAuth, async (req, res) => {
554
+ try {
555
+ const { workspaceRoot, command, args, languageId, filePath } = req.body;
556
+ if (typeof command !== "string" || command.trim().length === 0) {
557
+ return res.status(400).json({ success: false, error: "command is required" });
558
+ }
559
+ if (typeof languageId !== "string" &&
560
+ (typeof filePath !== "string" || filePath.trim().length === 0)) {
561
+ return res.status(400).json({ success: false, error: "languageId or filePath is required" });
562
+ }
563
+ const workspacePath = workspaceRoot || process.cwd();
564
+ coreLsp.setRootPath(workspacePath);
565
+ const absolutePath = typeof filePath === "string" && filePath.length > 0
566
+ ? (path.isAbsolute(filePath) ? filePath : path.join(workspacePath, filePath))
567
+ : undefined;
568
+ const result = await coreLsp.executeWorkspaceCommand(command, Array.isArray(args) ? args : [], typeof languageId === "string" ? languageId : undefined, absolutePath);
569
+ return res.json({ success: true, result });
570
+ }
571
+ catch (error) {
572
+ const err = error;
573
+ return res.status(500).json({ success: false, error: err.message || "Failed to execute command" });
574
+ }
575
+ });
576
+ router.post("/lsp/inlay-hints", requireDesktopAuth, async (req, res) => {
577
+ try {
578
+ const { filePath, workspaceRoot, startLine, startCharacter, endLine, endCharacter, } = req.body;
579
+ if (typeof filePath !== "string" || filePath.trim().length === 0) {
580
+ return res.status(400).json({ success: false, error: "filePath is required" });
581
+ }
582
+ if (typeof startLine !== "number" ||
583
+ typeof startCharacter !== "number" ||
584
+ typeof endLine !== "number" ||
585
+ typeof endCharacter !== "number") {
586
+ return res.status(400).json({
587
+ success: false,
588
+ error: "startLine, startCharacter, endLine, and endCharacter are required",
589
+ });
590
+ }
591
+ const workspacePath = workspaceRoot || process.cwd();
592
+ const absolutePath = resolveAbsolutePath(workspacePath, filePath);
593
+ coreLsp.setRootPath(workspacePath);
594
+ const hints = await coreLsp.getInlayHints(absolutePath, startLine, startCharacter, endLine, endCharacter);
595
+ return res.json({ success: true, hints: Array.isArray(hints) ? hints : [] });
596
+ }
597
+ catch (error) {
598
+ const err = error;
599
+ return res.status(500).json({ success: false, error: err.message || "Failed to get inlay hints" });
600
+ }
601
+ });
602
+ router.post("/lsp/inline-values", requireDesktopAuth, async (req, res) => {
603
+ try {
604
+ const { filePath, workspaceRoot, startLine, startCharacter, endLine, endCharacter, frameId, stoppedStartLine, stoppedStartCharacter, stoppedEndLine, stoppedEndCharacter, } = req.body;
605
+ if (typeof filePath !== "string" || filePath.trim().length === 0) {
606
+ return res.status(400).json({ success: false, error: "filePath is required" });
607
+ }
608
+ if (typeof startLine !== "number" ||
609
+ typeof startCharacter !== "number" ||
610
+ typeof endLine !== "number" ||
611
+ typeof endCharacter !== "number") {
612
+ return res.status(400).json({
613
+ success: false,
614
+ error: "startLine, startCharacter, endLine, and endCharacter are required",
615
+ });
616
+ }
617
+ const workspacePath = workspaceRoot || process.cwd();
618
+ const absolutePath = resolveAbsolutePath(workspacePath, filePath);
619
+ coreLsp.setRootPath(workspacePath);
620
+ const inlineValues = await coreLsp.getInlineValues(absolutePath, {
621
+ start: { line: startLine, character: startCharacter },
622
+ end: { line: endLine, character: endCharacter },
623
+ }, {
624
+ frameId: typeof frameId === "number" ? frameId : undefined,
625
+ stoppedLocation: typeof stoppedStartLine === "number" &&
626
+ typeof stoppedStartCharacter === "number" &&
627
+ typeof stoppedEndLine === "number" &&
628
+ typeof stoppedEndCharacter === "number"
629
+ ? {
630
+ start: { line: stoppedStartLine, character: stoppedStartCharacter },
631
+ end: { line: stoppedEndLine, character: stoppedEndCharacter },
632
+ }
633
+ : undefined,
634
+ });
635
+ return res.json({ success: true, inlineValues: Array.isArray(inlineValues) ? inlineValues : [] });
636
+ }
637
+ catch (error) {
638
+ const err = error;
639
+ return res.status(500).json({ success: false, error: err.message || "Failed to get inline values" });
640
+ }
641
+ });
642
+ router.post("/lsp/inline-completion", requireDesktopAuth, async (req, res) => {
643
+ try {
644
+ const { filePath, workspaceRoot, line, character, context } = req.body;
645
+ if (typeof filePath !== "string" || filePath.trim().length === 0) {
646
+ return res.status(400).json({ success: false, error: "filePath is required" });
647
+ }
648
+ if (typeof line !== "number" || typeof character !== "number") {
649
+ return res.status(400).json({ success: false, error: "line and character are required" });
650
+ }
651
+ const workspacePath = workspaceRoot || process.cwd();
652
+ const absolutePath = resolveAbsolutePath(workspacePath, filePath);
653
+ coreLsp.setRootPath(workspacePath);
654
+ const completion = await coreLsp.getInlineCompletion(absolutePath, line, character, context && typeof context === "object" ? context : {});
655
+ return res.json({ success: true, completion });
656
+ }
657
+ catch (error) {
658
+ const err = error;
659
+ return res.status(500).json({ success: false, error: err.message || "Failed to get inline completion" });
660
+ }
661
+ });
662
+ router.post("/lsp/will-save-wait-until", requireDesktopAuth, async (req, res) => {
663
+ try {
664
+ const { filePath, workspaceRoot, reason } = req.body;
665
+ if (typeof filePath !== "string" || filePath.trim().length === 0) {
666
+ return res.status(400).json({ success: false, error: "filePath is required" });
667
+ }
668
+ const workspacePath = workspaceRoot || process.cwd();
669
+ const absolutePath = resolveAbsolutePath(workspacePath, filePath);
670
+ coreLsp.setRootPath(workspacePath);
671
+ const edits = await coreLsp.willSaveWaitUntil(absolutePath, typeof reason === "number" ? reason : 1);
672
+ return res.json({ success: true, edits: Array.isArray(edits) ? edits : [] });
673
+ }
674
+ catch (error) {
675
+ const err = error;
676
+ return res.status(500).json({ success: false, error: err.message || "Failed to run willSaveWaitUntil" });
677
+ }
678
+ });
679
+ };