@seqyuan/annodex 0.1.10 → 0.1.12

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 (519) hide show
  1. package/app/api/agent/[id]/events/route.ts +94 -0
  2. package/app/api/agent/[id]/route.ts +83 -0
  3. package/app/api/agent/new/route.ts +53 -0
  4. package/app/api/auth/all-providers/route.ts +21 -0
  5. package/app/api/auth/api-key/[provider]/route.ts +7 -0
  6. package/app/api/auth/login/[provider]/route.ts +7 -0
  7. package/app/api/auth/login/route.ts +22 -0
  8. package/app/api/auth/logout/[provider]/route.ts +7 -0
  9. package/app/api/auth/providers/route.ts +15 -0
  10. package/app/api/auth/status/route.ts +6 -0
  11. package/app/api/default-cwd/route.ts +22 -0
  12. package/app/api/files/[...path]/route.ts +621 -0
  13. package/app/api/harness/route.ts +47 -0
  14. package/app/api/home/route.ts +6 -0
  15. package/app/api/internal/runtime/route.ts +26 -0
  16. package/app/api/models/route.ts +67 -0
  17. package/app/api/models-config/discover/route.ts +42 -0
  18. package/app/api/models-config/route.ts +152 -0
  19. package/app/api/models-config/test/route.ts +154 -0
  20. package/app/api/projects/browse/route.ts +51 -0
  21. package/app/api/projects/route.ts +83 -0
  22. package/app/api/reports/[id]/route.ts +108 -0
  23. package/app/api/search/route.ts +122 -0
  24. package/app/api/sessions/[id]/context/route.ts +23 -0
  25. package/app/api/sessions/[id]/route.ts +124 -0
  26. package/app/api/sessions/new/route.ts +5 -0
  27. package/app/api/sessions/route.ts +16 -0
  28. package/app/api/settings/route.ts +51 -0
  29. package/app/api/skills/install/route.ts +249 -0
  30. package/app/api/skills/route.ts +161 -0
  31. package/app/api/skills/search/route.ts +121 -0
  32. package/app/api/soul/route.ts +47 -0
  33. package/app/api/version/route.ts +55 -0
  34. package/app/globals.css +736 -0
  35. package/app/layout.tsx +40 -0
  36. package/app/login/page.tsx +133 -0
  37. package/app/page.tsx +10 -0
  38. package/components/AppShell.tsx +1058 -0
  39. package/components/ChatInput.tsx +1103 -0
  40. package/components/ChatMinimap.tsx +381 -0
  41. package/components/ChatWindow.tsx +576 -0
  42. package/components/CodeMirrorEditor.tsx +137 -0
  43. package/components/ConversationSearch.tsx +369 -0
  44. package/components/DataTableViewer.tsx +248 -0
  45. package/components/FileExplorer.tsx +758 -0
  46. package/components/FileIcons.tsx +241 -0
  47. package/components/FileViewer.tsx +1273 -0
  48. package/components/GlobalFileEditor.tsx +98 -0
  49. package/components/MarkdownRenderer.tsx +331 -0
  50. package/components/MermaidDiagram.tsx +80 -0
  51. package/components/MessageView.tsx +1141 -0
  52. package/components/ModelsConfig.tsx +1991 -0
  53. package/components/ProjectContext.tsx +252 -0
  54. package/components/ProjectFolderPicker.tsx +202 -0
  55. package/components/ProjectsConfig.tsx +288 -0
  56. package/components/ProviderIcons.tsx +91 -0
  57. package/components/ReportPanel.tsx +237 -0
  58. package/components/ResizeHandle.tsx +105 -0
  59. package/components/SessionSidebar.tsx +1464 -0
  60. package/components/SettingsDialog.tsx +287 -0
  61. package/components/SkillsConfig.tsx +1093 -0
  62. package/components/SubagentPanel.tsx +191 -0
  63. package/components/TabBar.tsx +115 -0
  64. package/components/ToolPanel.tsx +131 -0
  65. package/components/WidgetRenderer.tsx +505 -0
  66. package/components/viewers/DocumentToolbar.tsx +78 -0
  67. package/components/viewers/DocxViewer.tsx +97 -0
  68. package/components/viewers/PdfViewer.tsx +206 -0
  69. package/components/viewers/PptxViewer.tsx +240 -0
  70. package/components/viewers/XlsxViewer.tsx +143 -0
  71. package/hooks/useAgentSession.ts +710 -0
  72. package/hooks/useAudio.ts +50 -0
  73. package/hooks/useDragDrop.ts +52 -0
  74. package/hooks/useResizable.ts +60 -0
  75. package/hooks/useTheme.ts +85 -0
  76. package/lib/agent-client.ts +39 -0
  77. package/lib/annodex-config.ts +556 -0
  78. package/lib/auth-token.ts +74 -0
  79. package/lib/auth.ts +90 -0
  80. package/lib/brand.ts +5 -0
  81. package/lib/code-theme.ts +32 -0
  82. package/lib/codex-compat-proxy.ts +1603 -0
  83. package/lib/codex-home.ts +6 -0
  84. package/lib/codex-server.ts +796 -0
  85. package/lib/codex-session.ts +590 -0
  86. package/lib/codex-usage.ts +213 -0
  87. package/lib/file-paths.ts +34 -0
  88. package/lib/model-discovery.ts +379 -0
  89. package/lib/normalize.ts +30 -0
  90. package/lib/npx.ts +87 -0
  91. package/lib/pi-types.ts +49 -0
  92. package/lib/projects.ts +269 -0
  93. package/lib/provider-api.ts +88 -0
  94. package/lib/report-prompt.ts +61 -0
  95. package/lib/report-store.ts +597 -0
  96. package/lib/report-update-parser.ts +66 -0
  97. package/lib/rpc-manager.ts +668 -0
  98. package/lib/runtime-state.ts +117 -0
  99. package/lib/session-reader.ts +903 -0
  100. package/lib/session-runtime.ts +105 -0
  101. package/lib/subagent-progress.ts +279 -0
  102. package/lib/types.ts +241 -0
  103. package/lib/widget-export.ts +318 -0
  104. package/lib/widget-guidelines.ts +288 -0
  105. package/lib/widget-prompt.ts +76 -0
  106. package/lib/widget-utils.ts +523 -0
  107. package/package.json +23 -18
  108. package/postcss.config.mjs +8 -0
  109. package/proxy.ts +64 -0
  110. package/scripts/postinstall.cjs +25 -0
  111. package/tsconfig.json +41 -0
  112. package/.next/BUILD_ID +0 -1
  113. package/.next/app-path-routes-manifest.json +0 -39
  114. package/.next/build-manifest.json +0 -20
  115. package/.next/diagnostics/build-diagnostics.json +0 -6
  116. package/.next/diagnostics/framework.json +0 -1
  117. package/.next/export-marker.json +0 -6
  118. package/.next/images-manifest.json +0 -68
  119. package/.next/next-minimal-server.js.nft.json +0 -1
  120. package/.next/next-server.js.nft.json +0 -1
  121. package/.next/package.json +0 -1
  122. package/.next/prerender-manifest.json +0 -109
  123. package/.next/react-loadable-manifest.json +0 -2320
  124. package/.next/required-server-files.js +0 -343
  125. package/.next/required-server-files.json +0 -343
  126. package/.next/routes-manifest.json +0 -286
  127. package/.next/server/app/_global-error/page.js +0 -32
  128. package/.next/server/app/_global-error/page.js.nft.json +0 -1
  129. package/.next/server/app/_global-error/page_client-reference-manifest.js +0 -1
  130. package/.next/server/app/_global-error.html +0 -1
  131. package/.next/server/app/_global-error.meta +0 -16
  132. package/.next/server/app/_global-error.rsc +0 -14
  133. package/.next/server/app/_global-error.segments/_full.segment.rsc +0 -14
  134. package/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +0 -5
  135. package/.next/server/app/_global-error.segments/_global-error.segment.rsc +0 -5
  136. package/.next/server/app/_global-error.segments/_head.segment.rsc +0 -5
  137. package/.next/server/app/_global-error.segments/_index.segment.rsc +0 -5
  138. package/.next/server/app/_global-error.segments/_tree.segment.rsc +0 -1
  139. package/.next/server/app/_not-found/page.js +0 -2
  140. package/.next/server/app/_not-found/page.js.nft.json +0 -1
  141. package/.next/server/app/_not-found/page_client-reference-manifest.js +0 -1
  142. package/.next/server/app/_not-found.html +0 -1
  143. package/.next/server/app/_not-found.meta +0 -16
  144. package/.next/server/app/_not-found.rsc +0 -18
  145. package/.next/server/app/_not-found.segments/_full.segment.rsc +0 -18
  146. package/.next/server/app/_not-found.segments/_head.segment.rsc +0 -6
  147. package/.next/server/app/_not-found.segments/_index.segment.rsc +0 -5
  148. package/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +0 -5
  149. package/.next/server/app/_not-found.segments/_not-found.segment.rsc +0 -5
  150. package/.next/server/app/_not-found.segments/_tree.segment.rsc +0 -4
  151. package/.next/server/app/api/agent/[id]/events/route.js +0 -3
  152. package/.next/server/app/api/agent/[id]/events/route.js.nft.json +0 -1
  153. package/.next/server/app/api/agent/[id]/events/route_client-reference-manifest.js +0 -1
  154. package/.next/server/app/api/agent/[id]/route.js +0 -1
  155. package/.next/server/app/api/agent/[id]/route.js.nft.json +0 -1
  156. package/.next/server/app/api/agent/[id]/route_client-reference-manifest.js +0 -1
  157. package/.next/server/app/api/agent/new/route.js +0 -1
  158. package/.next/server/app/api/agent/new/route.js.nft.json +0 -1
  159. package/.next/server/app/api/agent/new/route_client-reference-manifest.js +0 -1
  160. package/.next/server/app/api/auth/all-providers/route.js +0 -1
  161. package/.next/server/app/api/auth/all-providers/route.js.nft.json +0 -1
  162. package/.next/server/app/api/auth/all-providers/route_client-reference-manifest.js +0 -1
  163. package/.next/server/app/api/auth/api-key/[provider]/route.js +0 -1
  164. package/.next/server/app/api/auth/api-key/[provider]/route.js.nft.json +0 -1
  165. package/.next/server/app/api/auth/api-key/[provider]/route_client-reference-manifest.js +0 -1
  166. package/.next/server/app/api/auth/login/[provider]/route.js +0 -1
  167. package/.next/server/app/api/auth/login/[provider]/route.js.nft.json +0 -1
  168. package/.next/server/app/api/auth/login/[provider]/route_client-reference-manifest.js +0 -1
  169. package/.next/server/app/api/auth/login/route.js +0 -1
  170. package/.next/server/app/api/auth/login/route.js.nft.json +0 -1
  171. package/.next/server/app/api/auth/login/route_client-reference-manifest.js +0 -1
  172. package/.next/server/app/api/auth/logout/[provider]/route.js +0 -1
  173. package/.next/server/app/api/auth/logout/[provider]/route.js.nft.json +0 -1
  174. package/.next/server/app/api/auth/logout/[provider]/route_client-reference-manifest.js +0 -1
  175. package/.next/server/app/api/auth/providers/route.js +0 -1
  176. package/.next/server/app/api/auth/providers/route.js.nft.json +0 -1
  177. package/.next/server/app/api/auth/providers/route_client-reference-manifest.js +0 -1
  178. package/.next/server/app/api/auth/status/route.js +0 -1
  179. package/.next/server/app/api/auth/status/route.js.nft.json +0 -1
  180. package/.next/server/app/api/auth/status/route_client-reference-manifest.js +0 -1
  181. package/.next/server/app/api/default-cwd/route.js +0 -1
  182. package/.next/server/app/api/default-cwd/route.js.nft.json +0 -1
  183. package/.next/server/app/api/default-cwd/route_client-reference-manifest.js +0 -1
  184. package/.next/server/app/api/files/[...path]/route.js +0 -4
  185. package/.next/server/app/api/files/[...path]/route.js.nft.json +0 -1
  186. package/.next/server/app/api/files/[...path]/route_client-reference-manifest.js +0 -1
  187. package/.next/server/app/api/harness/route.js +0 -1
  188. package/.next/server/app/api/harness/route.js.nft.json +0 -1
  189. package/.next/server/app/api/harness/route_client-reference-manifest.js +0 -1
  190. package/.next/server/app/api/home/route.js +0 -1
  191. package/.next/server/app/api/home/route.js.nft.json +0 -1
  192. package/.next/server/app/api/home/route_client-reference-manifest.js +0 -1
  193. package/.next/server/app/api/internal/runtime/route.js +0 -1
  194. package/.next/server/app/api/internal/runtime/route.js.nft.json +0 -1
  195. package/.next/server/app/api/internal/runtime/route_client-reference-manifest.js +0 -1
  196. package/.next/server/app/api/models/route.js +0 -1
  197. package/.next/server/app/api/models/route.js.nft.json +0 -1
  198. package/.next/server/app/api/models/route_client-reference-manifest.js +0 -1
  199. package/.next/server/app/api/models-config/discover/route.js +0 -1
  200. package/.next/server/app/api/models-config/discover/route.js.nft.json +0 -1
  201. package/.next/server/app/api/models-config/discover/route_client-reference-manifest.js +0 -1
  202. package/.next/server/app/api/models-config/route.js +0 -1
  203. package/.next/server/app/api/models-config/route.js.nft.json +0 -1
  204. package/.next/server/app/api/models-config/route_client-reference-manifest.js +0 -1
  205. package/.next/server/app/api/models-config/test/route.js +0 -1
  206. package/.next/server/app/api/models-config/test/route.js.nft.json +0 -1
  207. package/.next/server/app/api/models-config/test/route_client-reference-manifest.js +0 -1
  208. package/.next/server/app/api/projects/browse/route.js +0 -1
  209. package/.next/server/app/api/projects/browse/route.js.nft.json +0 -1
  210. package/.next/server/app/api/projects/browse/route_client-reference-manifest.js +0 -1
  211. package/.next/server/app/api/projects/route.js +0 -1
  212. package/.next/server/app/api/projects/route.js.nft.json +0 -1
  213. package/.next/server/app/api/projects/route_client-reference-manifest.js +0 -1
  214. package/.next/server/app/api/reports/[id]/route.js +0 -10
  215. package/.next/server/app/api/reports/[id]/route.js.nft.json +0 -1
  216. package/.next/server/app/api/reports/[id]/route_client-reference-manifest.js +0 -1
  217. package/.next/server/app/api/search/route.js +0 -1
  218. package/.next/server/app/api/search/route.js.nft.json +0 -1
  219. package/.next/server/app/api/search/route_client-reference-manifest.js +0 -1
  220. package/.next/server/app/api/sessions/[id]/context/route.js +0 -1
  221. package/.next/server/app/api/sessions/[id]/context/route.js.nft.json +0 -1
  222. package/.next/server/app/api/sessions/[id]/context/route_client-reference-manifest.js +0 -1
  223. package/.next/server/app/api/sessions/[id]/route.js +0 -1
  224. package/.next/server/app/api/sessions/[id]/route.js.nft.json +0 -1
  225. package/.next/server/app/api/sessions/[id]/route_client-reference-manifest.js +0 -1
  226. package/.next/server/app/api/sessions/new/route.js +0 -1
  227. package/.next/server/app/api/sessions/new/route.js.nft.json +0 -1
  228. package/.next/server/app/api/sessions/new/route_client-reference-manifest.js +0 -1
  229. package/.next/server/app/api/sessions/route.js +0 -1
  230. package/.next/server/app/api/sessions/route.js.nft.json +0 -1
  231. package/.next/server/app/api/sessions/route_client-reference-manifest.js +0 -1
  232. package/.next/server/app/api/settings/route.js +0 -1
  233. package/.next/server/app/api/settings/route.js.nft.json +0 -1
  234. package/.next/server/app/api/settings/route_client-reference-manifest.js +0 -1
  235. package/.next/server/app/api/skills/install/route.js +0 -5
  236. package/.next/server/app/api/skills/install/route.js.nft.json +0 -1
  237. package/.next/server/app/api/skills/install/route_client-reference-manifest.js +0 -1
  238. package/.next/server/app/api/skills/route.js +0 -6
  239. package/.next/server/app/api/skills/route.js.nft.json +0 -1
  240. package/.next/server/app/api/skills/route_client-reference-manifest.js +0 -1
  241. package/.next/server/app/api/skills/search/route.js +0 -1
  242. package/.next/server/app/api/skills/search/route.js.nft.json +0 -1
  243. package/.next/server/app/api/skills/search/route_client-reference-manifest.js +0 -1
  244. package/.next/server/app/api/soul/route.js +0 -1
  245. package/.next/server/app/api/soul/route.js.nft.json +0 -1
  246. package/.next/server/app/api/soul/route_client-reference-manifest.js +0 -1
  247. package/.next/server/app/api/version/route.js +0 -1
  248. package/.next/server/app/api/version/route.js.nft.json +0 -1
  249. package/.next/server/app/api/version/route_client-reference-manifest.js +0 -1
  250. package/.next/server/app/index.html +0 -1
  251. package/.next/server/app/index.meta +0 -14
  252. package/.next/server/app/index.rsc +0 -17
  253. package/.next/server/app/index.segments/__PAGE__.segment.rsc +0 -6
  254. package/.next/server/app/index.segments/_full.segment.rsc +0 -17
  255. package/.next/server/app/index.segments/_head.segment.rsc +0 -6
  256. package/.next/server/app/index.segments/_index.segment.rsc +0 -5
  257. package/.next/server/app/index.segments/_tree.segment.rsc +0 -4
  258. package/.next/server/app/login/page.js +0 -2
  259. package/.next/server/app/login/page.js.nft.json +0 -1
  260. package/.next/server/app/login/page_client-reference-manifest.js +0 -1
  261. package/.next/server/app/login.html +0 -1
  262. package/.next/server/app/login.meta +0 -15
  263. package/.next/server/app/login.rsc +0 -22
  264. package/.next/server/app/login.segments/_full.segment.rsc +0 -22
  265. package/.next/server/app/login.segments/_head.segment.rsc +0 -6
  266. package/.next/server/app/login.segments/_index.segment.rsc +0 -5
  267. package/.next/server/app/login.segments/_tree.segment.rsc +0 -4
  268. package/.next/server/app/login.segments/login/__PAGE__.segment.rsc +0 -9
  269. package/.next/server/app/login.segments/login.segment.rsc +0 -5
  270. package/.next/server/app/page.js +0 -261
  271. package/.next/server/app/page.js.nft.json +0 -1
  272. package/.next/server/app/page_client-reference-manifest.js +0 -1
  273. package/.next/server/app-paths-manifest.json +0 -39
  274. package/.next/server/chunks/1048.js +0 -1
  275. package/.next/server/chunks/1367.js +0 -77
  276. package/.next/server/chunks/1381.js +0 -1
  277. package/.next/server/chunks/165.js +0 -1
  278. package/.next/server/chunks/1681.js +0 -215
  279. package/.next/server/chunks/1688.js +0 -45
  280. package/.next/server/chunks/1703.js +0 -79
  281. package/.next/server/chunks/1712.js +0 -43
  282. package/.next/server/chunks/1813.js +0 -1
  283. package/.next/server/chunks/2325.js +0 -80
  284. package/.next/server/chunks/258.js +0 -1
  285. package/.next/server/chunks/2671.js +0 -287
  286. package/.next/server/chunks/2778.js +0 -1
  287. package/.next/server/chunks/2943.js +0 -1
  288. package/.next/server/chunks/3031.js +0 -226
  289. package/.next/server/chunks/3181.js +0 -1
  290. package/.next/server/chunks/3493.js +0 -1
  291. package/.next/server/chunks/3672.js +0 -1
  292. package/.next/server/chunks/3701.js +0 -104
  293. package/.next/server/chunks/4013.js +0 -1
  294. package/.next/server/chunks/402.js +0 -2
  295. package/.next/server/chunks/4035.js +0 -80
  296. package/.next/server/chunks/4248.js +0 -153
  297. package/.next/server/chunks/4367.js +0 -1
  298. package/.next/server/chunks/4406.js +0 -141
  299. package/.next/server/chunks/4741.js +0 -18
  300. package/.next/server/chunks/4768.js +0 -1
  301. package/.next/server/chunks/4858.js +0 -148
  302. package/.next/server/chunks/4980.js +0 -1
  303. package/.next/server/chunks/5155.js +0 -5
  304. package/.next/server/chunks/5293.js +0 -166
  305. package/.next/server/chunks/5399.js +0 -8
  306. package/.next/server/chunks/5409.js +0 -1
  307. package/.next/server/chunks/5797.js +0 -93
  308. package/.next/server/chunks/5851.js +0 -36
  309. package/.next/server/chunks/6206.js +0 -1
  310. package/.next/server/chunks/6296.js +0 -1
  311. package/.next/server/chunks/63.js +0 -45
  312. package/.next/server/chunks/6346.js +0 -1
  313. package/.next/server/chunks/6406.js +0 -23
  314. package/.next/server/chunks/642.js +0 -1
  315. package/.next/server/chunks/6429.js +0 -50
  316. package/.next/server/chunks/6729.js +0 -64
  317. package/.next/server/chunks/6907.js +0 -115
  318. package/.next/server/chunks/6980.js +0 -1
  319. package/.next/server/chunks/7073.js +0 -24
  320. package/.next/server/chunks/7233.js +0 -24
  321. package/.next/server/chunks/7307.js +0 -1
  322. package/.next/server/chunks/7362.js +0 -9
  323. package/.next/server/chunks/7567.js +0 -29
  324. package/.next/server/chunks/7765.js +0 -1
  325. package/.next/server/chunks/7890.js +0 -1
  326. package/.next/server/chunks/8065.js +0 -1
  327. package/.next/server/chunks/8238.js +0 -34
  328. package/.next/server/chunks/8276.js +0 -1
  329. package/.next/server/chunks/8336.js +0 -1
  330. package/.next/server/chunks/8477.js +0 -3
  331. package/.next/server/chunks/8490.js +0 -1
  332. package/.next/server/chunks/8916.js +0 -1
  333. package/.next/server/chunks/9280.js +0 -252
  334. package/.next/server/chunks/9315.js +0 -1
  335. package/.next/server/chunks/9537.js +0 -90
  336. package/.next/server/chunks/966.js +0 -1
  337. package/.next/server/chunks/9818.js +0 -21
  338. package/.next/server/chunks/static/media/pdf.worker.min.c476e1a0.mjs +0 -6
  339. package/.next/server/functions-config-manifest.json +0 -16
  340. package/.next/server/interception-route-rewrite-manifest.js +0 -1
  341. package/.next/server/middleware-build-manifest.js +0 -1
  342. package/.next/server/middleware-manifest.json +0 -6
  343. package/.next/server/middleware-react-loadable-manifest.js +0 -1
  344. package/.next/server/middleware.js +0 -18
  345. package/.next/server/middleware.js.nft.json +0 -1
  346. package/.next/server/next-font-manifest.js +0 -1
  347. package/.next/server/next-font-manifest.json +0 -1
  348. package/.next/server/pages/404.html +0 -1
  349. package/.next/server/pages/500.html +0 -1
  350. package/.next/server/pages-manifest.json +0 -4
  351. package/.next/server/prefetch-hints.json +0 -1
  352. package/.next/server/server-reference-manifest.js +0 -1
  353. package/.next/server/server-reference-manifest.json +0 -1
  354. package/.next/server/webpack-runtime.js +0 -1
  355. package/.next/static/6cuMSvcr0FVO-GiK5RJZh/_buildManifest.js +0 -1
  356. package/.next/static/6cuMSvcr0FVO-GiK5RJZh/_ssgManifest.js +0 -1
  357. package/.next/static/chunks/0b9a0da7.9075af772487e743.js +0 -62
  358. package/.next/static/chunks/1413.922d232de90c0c41.js +0 -115
  359. package/.next/static/chunks/1643.467a526a1f24f54d.js +0 -24
  360. package/.next/static/chunks/1852.5543122f11aa7fed.js +0 -1
  361. package/.next/static/chunks/1960.b1e26436d7a5f586.js +0 -1
  362. package/.next/static/chunks/2170a4aa.4213bb2183c9cdf9.js +0 -1
  363. package/.next/static/chunks/2274.6cd173f80a1405a2.js +0 -21
  364. package/.next/static/chunks/2419.347fdfe3c170854d.js +0 -166
  365. package/.next/static/chunks/2619.9aac8983f30c7c8a.js +0 -1
  366. package/.next/static/chunks/2623.d20fabd8e18197c6.js +0 -287
  367. package/.next/static/chunks/2729.f5365061a849d659.js +0 -34
  368. package/.next/static/chunks/2821.934bcf60fbdc28c6.js +0 -1
  369. package/.next/static/chunks/2918becc.abff2ece1de37bc1.js +0 -153
  370. package/.next/static/chunks/2947.114e51cb06d1c01a.js +0 -23
  371. package/.next/static/chunks/3079.4c511fa1144e3adf.js +0 -79
  372. package/.next/static/chunks/3274.208ca44844cd7d95.js +0 -148
  373. package/.next/static/chunks/3308.465a94263d04bfea.js +0 -73
  374. package/.next/static/chunks/3325.e4bfe1ca657f3b5b.js +0 -80
  375. package/.next/static/chunks/3506.2a7eaa08b9f55337.js +0 -90
  376. package/.next/static/chunks/363642f4-043c1475ab9af70e.js +0 -1
  377. package/.next/static/chunks/3794-123fdf632563f469.js +0 -32
  378. package/.next/static/chunks/3837.a755ccfe6f9c1c1c.js +0 -5
  379. package/.next/static/chunks/394.91597771688df6d0.js +0 -1
  380. package/.next/static/chunks/3997.1009c06025691712.js +0 -1
  381. package/.next/static/chunks/4453.91a357dc43c21745.js +0 -1
  382. package/.next/static/chunks/4491.44fdf20580ac72bd.js +0 -24
  383. package/.next/static/chunks/4829.cf1d50e43e6d9db5.js +0 -1
  384. package/.next/static/chunks/498.fe1d9da9ecad6c36.js +0 -1
  385. package/.next/static/chunks/4bd1b696-e356ca5ba0218e27.js +0 -1
  386. package/.next/static/chunks/5019.b5a1a2b8daf17525.js +0 -1
  387. package/.next/static/chunks/5034.8f16c3fa3ce75411.js +0 -1
  388. package/.next/static/chunks/5074.d16651da01ec4e02.js +0 -1
  389. package/.next/static/chunks/51fb665c.0950e1b79671348d.js +0 -45
  390. package/.next/static/chunks/532.5956ed631aff722b.js +0 -9
  391. package/.next/static/chunks/5326.69460442bdcd6cd3.js +0 -1
  392. package/.next/static/chunks/5403.ff110bf5bf600758.js +0 -64
  393. package/.next/static/chunks/547.902a733488cfe3f7.js +0 -77
  394. package/.next/static/chunks/5567.540d7fc108ad6ee5.js +0 -215
  395. package/.next/static/chunks/5590.ef62922166d308b4.js +0 -1
  396. package/.next/static/chunks/5690.9d6eb1edb1399995.js +0 -1
  397. package/.next/static/chunks/5749.25faee4a1e55b854.js +0 -226
  398. package/.next/static/chunks/58bb9007.1ccb6bba34b4c635.js +0 -80
  399. package/.next/static/chunks/6121.f3f43f1896ea0cd9.js +0 -1
  400. package/.next/static/chunks/6600.583c88eef37aa524.js +0 -1
  401. package/.next/static/chunks/6696.a41aec266e657d54.js +0 -141
  402. package/.next/static/chunks/6922.42148793782d2fe7.js +0 -1
  403. package/.next/static/chunks/7006.e191611ffc2b9528.js +0 -43
  404. package/.next/static/chunks/7343.9fbb58204d8ac681.js +0 -1
  405. package/.next/static/chunks/73972abe.25a4cffa03b2bcef.js +0 -119
  406. package/.next/static/chunks/7547.58bda8a2aabba0d4.js +0 -93
  407. package/.next/static/chunks/7648.4ae2f183b4db0353.js +0 -1
  408. package/.next/static/chunks/7874.8db6929b94cdf697.js +0 -1
  409. package/.next/static/chunks/7959.1f20a35df316216a.js +0 -104
  410. package/.next/static/chunks/83.85d62d7fc9850b75.js +0 -29
  411. package/.next/static/chunks/8436.cab94b59cca0a8ff.js +0 -1
  412. package/.next/static/chunks/8451.ff6ff72b57dc52e1.js +0 -1
  413. package/.next/static/chunks/8489.45f22859734f514f.js +0 -36
  414. package/.next/static/chunks/8568.f85d8b36fc9a9037.js +0 -1
  415. package/.next/static/chunks/8771-3e14b6810486df1f.js +0 -1
  416. package/.next/static/chunks/8863.be51033a67436277.js +0 -1
  417. package/.next/static/chunks/90542734.dc1a2723e4f6affb.js +0 -1
  418. package/.next/static/chunks/9500.1488aec06ee78127.js +0 -1
  419. package/.next/static/chunks/9633.155548b5fca6e580.js +0 -1
  420. package/.next/static/chunks/9779.673004a62d70e36a.js +0 -1
  421. package/.next/static/chunks/app/_global-error/page-cc518af6b1ffb191.js +0 -1
  422. package/.next/static/chunks/app/_not-found/page-c72daab99269beff.js +0 -1
  423. package/.next/static/chunks/app/api/agent/[id]/events/route-cc518af6b1ffb191.js +0 -1
  424. package/.next/static/chunks/app/api/agent/[id]/route-cc518af6b1ffb191.js +0 -1
  425. package/.next/static/chunks/app/api/agent/new/route-cc518af6b1ffb191.js +0 -1
  426. package/.next/static/chunks/app/api/auth/all-providers/route-cc518af6b1ffb191.js +0 -1
  427. package/.next/static/chunks/app/api/auth/api-key/[provider]/route-cc518af6b1ffb191.js +0 -1
  428. package/.next/static/chunks/app/api/auth/login/[provider]/route-cc518af6b1ffb191.js +0 -1
  429. package/.next/static/chunks/app/api/auth/login/route-cc518af6b1ffb191.js +0 -1
  430. package/.next/static/chunks/app/api/auth/logout/[provider]/route-cc518af6b1ffb191.js +0 -1
  431. package/.next/static/chunks/app/api/auth/providers/route-cc518af6b1ffb191.js +0 -1
  432. package/.next/static/chunks/app/api/auth/status/route-cc518af6b1ffb191.js +0 -1
  433. package/.next/static/chunks/app/api/default-cwd/route-cc518af6b1ffb191.js +0 -1
  434. package/.next/static/chunks/app/api/files/[...path]/route-cc518af6b1ffb191.js +0 -1
  435. package/.next/static/chunks/app/api/harness/route-cc518af6b1ffb191.js +0 -1
  436. package/.next/static/chunks/app/api/home/route-cc518af6b1ffb191.js +0 -1
  437. package/.next/static/chunks/app/api/internal/runtime/route-cc518af6b1ffb191.js +0 -1
  438. package/.next/static/chunks/app/api/models/route-cc518af6b1ffb191.js +0 -1
  439. package/.next/static/chunks/app/api/models-config/discover/route-cc518af6b1ffb191.js +0 -1
  440. package/.next/static/chunks/app/api/models-config/route-cc518af6b1ffb191.js +0 -1
  441. package/.next/static/chunks/app/api/models-config/test/route-cc518af6b1ffb191.js +0 -1
  442. package/.next/static/chunks/app/api/projects/browse/route-cc518af6b1ffb191.js +0 -1
  443. package/.next/static/chunks/app/api/projects/route-cc518af6b1ffb191.js +0 -1
  444. package/.next/static/chunks/app/api/reports/[id]/route-cc518af6b1ffb191.js +0 -1
  445. package/.next/static/chunks/app/api/search/route-cc518af6b1ffb191.js +0 -1
  446. package/.next/static/chunks/app/api/sessions/[id]/context/route-cc518af6b1ffb191.js +0 -1
  447. package/.next/static/chunks/app/api/sessions/[id]/route-cc518af6b1ffb191.js +0 -1
  448. package/.next/static/chunks/app/api/sessions/new/route-cc518af6b1ffb191.js +0 -1
  449. package/.next/static/chunks/app/api/sessions/route-cc518af6b1ffb191.js +0 -1
  450. package/.next/static/chunks/app/api/settings/route-cc518af6b1ffb191.js +0 -1
  451. package/.next/static/chunks/app/api/skills/install/route-cc518af6b1ffb191.js +0 -1
  452. package/.next/static/chunks/app/api/skills/route-cc518af6b1ffb191.js +0 -1
  453. package/.next/static/chunks/app/api/skills/search/route-cc518af6b1ffb191.js +0 -1
  454. package/.next/static/chunks/app/api/soul/route-cc518af6b1ffb191.js +0 -1
  455. package/.next/static/chunks/app/api/version/route-cc518af6b1ffb191.js +0 -1
  456. package/.next/static/chunks/app/layout-be148b7ae915b22a.js +0 -1
  457. package/.next/static/chunks/app/login/page-ebf0e6de99062783.js +0 -1
  458. package/.next/static/chunks/app/page-c45d98ea81c548ca.js +0 -260
  459. package/.next/static/chunks/d3ac728e.7964f816a1ca64e5.js +0 -1
  460. package/.next/static/chunks/framework-711ef29bc66f648c.js +0 -1
  461. package/.next/static/chunks/main-app-45a0f19af99d61b6.js +0 -1
  462. package/.next/static/chunks/main-f74964b7ae52493e.js +0 -5
  463. package/.next/static/chunks/next/dist/client/components/builtin/app-error-cc518af6b1ffb191.js +0 -1
  464. package/.next/static/chunks/next/dist/client/components/builtin/forbidden-cc518af6b1ffb191.js +0 -1
  465. package/.next/static/chunks/next/dist/client/components/builtin/global-error-9bfa08b9491621f2.js +0 -1
  466. package/.next/static/chunks/next/dist/client/components/builtin/not-found-cc518af6b1ffb191.js +0 -1
  467. package/.next/static/chunks/next/dist/client/components/builtin/unauthorized-cc518af6b1ffb191.js +0 -1
  468. package/.next/static/chunks/polyfills-42372ed130431b0a.js +0 -1
  469. package/.next/static/chunks/webpack-fcf4a889ecbd753c.js +0 -1
  470. package/.next/static/css/45029451a1d7255d.css +0 -3
  471. package/.next/static/media/15605e25b523335c-s.woff2 +0 -0
  472. package/.next/static/media/1a3dce5cfb5f7760-s.woff2 +0 -0
  473. package/.next/static/media/1cdd02902f937a18-s.woff2 +0 -0
  474. package/.next/static/media/4c4b3b30b6bcb2be-s.woff2 +0 -0
  475. package/.next/static/media/641a7b8a5800ee0e-s.woff2 +0 -0
  476. package/.next/static/media/7deddc85b7ffd1dc-s.p.woff2 +0 -0
  477. package/.next/static/media/ec14413c594b3356-s.p.woff2 +0 -0
  478. package/.next/static/media/pdf.worker.min.29aaf158.mjs +0 -6
  479. package/.next/trace +0 -74
  480. package/.next/trace-build +0 -1
  481. package/.next/types/app/api/agent/[id]/events/route.ts +0 -351
  482. package/.next/types/app/api/agent/[id]/route.ts +0 -351
  483. package/.next/types/app/api/agent/new/route.ts +0 -351
  484. package/.next/types/app/api/auth/all-providers/route.ts +0 -351
  485. package/.next/types/app/api/auth/api-key/[provider]/route.ts +0 -351
  486. package/.next/types/app/api/auth/login/[provider]/route.ts +0 -351
  487. package/.next/types/app/api/auth/login/route.ts +0 -351
  488. package/.next/types/app/api/auth/logout/[provider]/route.ts +0 -351
  489. package/.next/types/app/api/auth/providers/route.ts +0 -351
  490. package/.next/types/app/api/auth/status/route.ts +0 -351
  491. package/.next/types/app/api/default-cwd/route.ts +0 -351
  492. package/.next/types/app/api/files/[...path]/route.ts +0 -351
  493. package/.next/types/app/api/harness/route.ts +0 -351
  494. package/.next/types/app/api/home/route.ts +0 -351
  495. package/.next/types/app/api/internal/runtime/route.ts +0 -351
  496. package/.next/types/app/api/models/route.ts +0 -351
  497. package/.next/types/app/api/models-config/discover/route.ts +0 -351
  498. package/.next/types/app/api/models-config/route.ts +0 -351
  499. package/.next/types/app/api/models-config/test/route.ts +0 -351
  500. package/.next/types/app/api/projects/browse/route.ts +0 -351
  501. package/.next/types/app/api/projects/route.ts +0 -351
  502. package/.next/types/app/api/reports/[id]/route.ts +0 -351
  503. package/.next/types/app/api/search/route.ts +0 -351
  504. package/.next/types/app/api/sessions/[id]/context/route.ts +0 -351
  505. package/.next/types/app/api/sessions/[id]/route.ts +0 -351
  506. package/.next/types/app/api/sessions/new/route.ts +0 -351
  507. package/.next/types/app/api/sessions/route.ts +0 -351
  508. package/.next/types/app/api/settings/route.ts +0 -351
  509. package/.next/types/app/api/skills/install/route.ts +0 -351
  510. package/.next/types/app/api/skills/route.ts +0 -351
  511. package/.next/types/app/api/skills/search/route.ts +0 -351
  512. package/.next/types/app/api/soul/route.ts +0 -351
  513. package/.next/types/app/api/version/route.ts +0 -351
  514. package/.next/types/app/layout.ts +0 -87
  515. package/.next/types/app/login/page.ts +0 -87
  516. package/.next/types/app/page.ts +0 -87
  517. package/.next/types/package.json +0 -1
  518. package/.next/types/routes.d.ts +0 -106
  519. package/.next/types/validator.ts +0 -376
@@ -0,0 +1,621 @@
1
+ import { NextRequest, NextResponse } from "next/server";
2
+ import fs from "fs";
3
+ import path from "path";
4
+ import { listAllSessions } from "@/lib/session-reader";
5
+
6
+ const IGNORED_NAMES = new Set([
7
+ "node_modules", ".git", ".next", "dist", "build", "__pycache__",
8
+ ".turbo", ".cache", "coverage", ".pytest_cache", ".mypy_cache",
9
+ "target", "vendor", ".DS_Store", ".git",
10
+ ]);
11
+
12
+ const IGNORED_SUFFIXES = [".pyc"];
13
+
14
+ const TEXT_PREVIEW_MAX_BYTES = 256 * 1024;
15
+ const TABLE_PREVIEW_MAX_BYTES = 2 * 1024 * 1024;
16
+ const LONG_TEXT_PREVIEW_MAX_BYTES = 2 * 1024 * 1024;
17
+ const IMAGE_PREVIEW_MAX_BYTES = 10 * 1024 * 1024;
18
+
19
+ const IMAGE_EXT_TO_MIME: Record<string, string> = {
20
+ png: "image/png",
21
+ jpg: "image/jpeg",
22
+ jpeg: "image/jpeg",
23
+ gif: "image/gif",
24
+ webp: "image/webp",
25
+ svg: "image/svg+xml",
26
+ bmp: "image/bmp",
27
+ ico: "image/x-icon",
28
+ avif: "image/avif",
29
+ };
30
+
31
+ const AUDIO_EXT_TO_MIME: Record<string, string> = {
32
+ mp3: "audio/mpeg",
33
+ wav: "audio/wav",
34
+ ogg: "audio/ogg",
35
+ oga: "audio/ogg",
36
+ opus: "audio/ogg",
37
+ m4a: "audio/mp4",
38
+ aac: "audio/aac",
39
+ flac: "audio/flac",
40
+ weba: "audio/webm",
41
+ webm: "audio/webm",
42
+ };
43
+
44
+ const DOC_EXT_TO_MIME: Record<string, string> = {
45
+ pdf: "application/pdf",
46
+ docx: "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
47
+ xlsx: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
48
+ pptx: "application/vnd.openxmlformats-officedocument.presentationml.presentation",
49
+ xls: "application/vnd.ms-excel",
50
+ doc: "application/msword",
51
+ ppt: "application/vnd.ms-powerpoint",
52
+ };
53
+
54
+ const DOC_PREVIEW_MAX_BYTES = 50 * 1024 * 1024; // 50MB
55
+ const UPLOAD_MAX_FILE_BYTES = 50 * 1024 * 1024; // 50MB
56
+ const UPLOAD_MAX_TOTAL_BYTES = 200 * 1024 * 1024; // 200MB
57
+
58
+ function getExt(filePath: string): string {
59
+ const ext = path.basename(filePath).toLowerCase().split(".").pop() ?? "";
60
+ return ext;
61
+ }
62
+
63
+ function getImageMime(filePath: string): string | null {
64
+ return IMAGE_EXT_TO_MIME[getExt(filePath)] ?? null;
65
+ }
66
+
67
+ function getAudioMime(filePath: string): string | null {
68
+ return AUDIO_EXT_TO_MIME[getExt(filePath)] ?? null;
69
+ }
70
+
71
+ function getDocMime(filePath: string): string | null {
72
+ return DOC_EXT_TO_MIME[getExt(filePath)] ?? null;
73
+ }
74
+
75
+ const EXT_TO_LANGUAGE: Record<string, string> = {
76
+ ts: "typescript", tsx: "typescript", js: "javascript", jsx: "javascript",
77
+ mjs: "javascript", cjs: "javascript", py: "python", rb: "ruby",
78
+ go: "go", rs: "rust", java: "java", kt: "kotlin", swift: "swift",
79
+ c: "c", cpp: "cpp", h: "c", hpp: "cpp", cs: "csharp",
80
+ html: "html", htm: "html", css: "css", scss: "css", less: "css",
81
+ json: "json", jsonl: "json", yaml: "yaml", yml: "yaml",
82
+ toml: "toml", xml: "xml", md: "markdown", mdx: "markdown",
83
+ csv: "csv", tsv: "tsv",
84
+ log: "text",
85
+ sh: "bash", bash: "bash", zsh: "bash", fish: "bash",
86
+ sql: "sql", graphql: "graphql", gql: "graphql",
87
+ dockerfile: "dockerfile", tf: "hcl", hcl: "hcl",
88
+ env: "bash", gitignore: "bash", txt: "text",
89
+ };
90
+
91
+ function getLanguage(filePath: string): string {
92
+ const base = path.basename(filePath).toLowerCase();
93
+ // Special full-name matches
94
+ if (base === "dockerfile" || base.startsWith("dockerfile.")) return "dockerfile";
95
+ if (base === ".env" || base.startsWith(".env.")) return "bash";
96
+ if (base === "makefile" || base === "gnumakefile") return "makefile";
97
+ const ext = base.split(".").pop() ?? "";
98
+ return EXT_TO_LANGUAGE[ext] ?? "text";
99
+ }
100
+
101
+ function getTextPreviewLimit(filePath: string): number {
102
+ const ext = getExt(filePath);
103
+ if (ext === "csv" || ext === "tsv") return TABLE_PREVIEW_MAX_BYTES;
104
+ if (ext === "md" || ext === "mdx" || ext === "txt" || ext === "log") return LONG_TEXT_PREVIEW_MAX_BYTES;
105
+ return TEXT_PREVIEW_MAX_BYTES;
106
+ }
107
+
108
+ function formatLimit(bytes: number): string {
109
+ const mb = bytes / 1024 / 1024;
110
+ if (mb >= 1) return `${Math.round(mb * 10) / 10}MB`;
111
+ return `${Math.round(bytes / 1024)}KB`;
112
+ }
113
+
114
+ function sanitizeUploadName(name: string): string | null {
115
+ const clean = name.replace(/\0/g, "").trim();
116
+ if (!clean || clean === "." || clean === "..") return null;
117
+ if (clean.includes("/") || clean.includes("\\")) return null;
118
+ return clean;
119
+ }
120
+
121
+ function getUploadCandidate(dirPath: string, fileName: string, index: number): { name: string; path: string } {
122
+ const ext = path.extname(fileName);
123
+ const stem = fileName.slice(0, fileName.length - ext.length) || fileName;
124
+ const name = index === 0 ? fileName : `${stem} (${index})${ext}`;
125
+ return { name, path: path.join(dirPath, name) };
126
+ }
127
+
128
+ function isNodeFileError(error: unknown, code: string): boolean {
129
+ return typeof error === "object" && error !== null && "code" in error && error.code === code;
130
+ }
131
+
132
+ function isFormUploadFile(value: FormDataEntryValue): value is File {
133
+ return (
134
+ typeof value === "object" &&
135
+ value !== null &&
136
+ "name" in value &&
137
+ "size" in value &&
138
+ "arrayBuffer" in value
139
+ );
140
+ }
141
+
142
+ // Short-TTL cache for the allowed-roots set. Without this, every file list/read
143
+ // request re-scans every saved session just to check access. 5s is short
144
+ // enough that newly-created cwds appear promptly; stored on globalThis so it
145
+ // survives Next.js hot-reload.
146
+ declare global {
147
+ var __annodexAllowedRootsCache: { roots: Set<string>; expiresAt: number } | undefined;
148
+ }
149
+
150
+ const ALLOWED_ROOTS_TTL_MS = 5_000;
151
+ const WINDOWS_ABSOLUTE_RE = /^[a-zA-Z]:[\\/]/;
152
+
153
+ function normalizeSlashes(filePath: string): string {
154
+ return filePath.replace(/\\/g, "/");
155
+ }
156
+
157
+ function isWindowsAbsolutePath(filePath: string): boolean {
158
+ return WINDOWS_ABSOLUTE_RE.test(filePath) || filePath.startsWith("\\\\") || filePath.startsWith("//");
159
+ }
160
+
161
+ function filePathFromSegments(segments: string[]): string {
162
+ const joined = segments.join("/");
163
+ const slashJoined = normalizeSlashes(joined);
164
+ if (isWindowsAbsolutePath(slashJoined)) return slashJoined;
165
+ return "/" + joined.replace(/^\/+/, "");
166
+ }
167
+
168
+ async function getAllowedRoots(): Promise<Set<string>> {
169
+ const now = Date.now();
170
+ const cached = globalThis.__annodexAllowedRootsCache;
171
+ if (cached && cached.expiresAt > now) return cached.roots;
172
+
173
+ const sessions = await listAllSessions();
174
+ const roots = new Set<string>();
175
+ for (const s of sessions) {
176
+ if (s.cwd) roots.add(s.cwd);
177
+ }
178
+ // Also allow manually registered projects and default workspace directories.
179
+ const home = (await import("os")).homedir();
180
+ const { existsSync, readFileSync, readdirSync } = await import("fs");
181
+ for (const registryPath of [
182
+ path.join(home, ".config", "annodex", "annodex-projects.json"),
183
+ path.join(home, ".config", "annodex", "annovibe-projects.json"),
184
+ ]) {
185
+ if (!existsSync(registryPath)) continue;
186
+ try {
187
+ const raw = JSON.parse(readFileSync(registryPath, "utf8")) as {
188
+ projects?: Record<string, { cwd?: unknown; hidden?: unknown }>;
189
+ };
190
+ for (const [key, record] of Object.entries(raw.projects ?? {})) {
191
+ if (record?.hidden === true) continue;
192
+ const cwd = typeof record?.cwd === "string" ? record.cwd : key;
193
+ if (cwd) roots.add(cwd);
194
+ }
195
+ } catch {
196
+ // ignore malformed project registry
197
+ }
198
+ }
199
+ for (const workspaceRoot of [
200
+ path.join(home, "annodex", "workspace"),
201
+ path.join(home, "annovibe", "workspace"),
202
+ ]) {
203
+ if (existsSync(workspaceRoot)) roots.add(workspaceRoot);
204
+ }
205
+ try {
206
+ for (const name of readdirSync(home)) {
207
+ if (/^pi-cwd-\d{8}$/.test(name)) {
208
+ roots.add(path.join(home, name));
209
+ }
210
+ }
211
+ } catch {
212
+ // ignore if home is unreadable
213
+ }
214
+
215
+ globalThis.__annodexAllowedRootsCache = { roots, expiresAt: now + ALLOWED_ROOTS_TTL_MS };
216
+ return roots;
217
+ }
218
+
219
+ function isPathAllowed(target: string, allowedRoots: Set<string>): boolean {
220
+ for (const root of allowedRoots) {
221
+ const useWindowsRules = isWindowsAbsolutePath(target) || isWindowsAbsolutePath(root);
222
+ const resolver = useWindowsRules ? path.win32 : path;
223
+ const sep = useWindowsRules ? "\\" : path.sep;
224
+ const normalized = resolver.resolve(target);
225
+ const normalizedRoot = resolver.resolve(root);
226
+ const comparable = useWindowsRules ? normalized.toLowerCase() : normalized;
227
+ const comparableRoot = useWindowsRules ? normalizedRoot.toLowerCase() : normalizedRoot;
228
+ const rootWithSep = comparableRoot.endsWith(sep) ? comparableRoot : comparableRoot + sep;
229
+ if (comparable === comparableRoot || comparable.startsWith(rootWithSep)) {
230
+ return true;
231
+ }
232
+ }
233
+ return false;
234
+ }
235
+
236
+ function createFileBodyStream(filePath: string, range?: { start: number; end: number }): ReadableStream<Uint8Array> {
237
+ const fileStream = fs.createReadStream(filePath, range);
238
+ let closed = false;
239
+
240
+ return new ReadableStream<Uint8Array>({
241
+ start(controller) {
242
+ fileStream.on("data", (chunk: Buffer) => {
243
+ if (closed) return;
244
+ try {
245
+ controller.enqueue(new Uint8Array(chunk));
246
+ } catch {
247
+ closed = true;
248
+ fileStream.destroy();
249
+ }
250
+ });
251
+ fileStream.once("end", () => {
252
+ if (closed) return;
253
+ closed = true;
254
+ try {
255
+ controller.close();
256
+ } catch {
257
+ // The browser may cancel media probes before the file stream ends.
258
+ }
259
+ });
260
+ fileStream.once("error", (error) => {
261
+ if (closed) return;
262
+ closed = true;
263
+ try {
264
+ controller.error(error);
265
+ } catch {
266
+ // The response was already abandoned by the client.
267
+ }
268
+ });
269
+ },
270
+ cancel() {
271
+ closed = true;
272
+ fileStream.destroy();
273
+ },
274
+ });
275
+ }
276
+
277
+ function streamFile(filePath: string, stat: fs.Stats, contentType: string, rangeHeader: string | null): Response {
278
+ const headers = {
279
+ "Content-Type": contentType,
280
+ "Cache-Control": "no-cache",
281
+ "Accept-Ranges": "bytes",
282
+ };
283
+
284
+ if (!rangeHeader) {
285
+ return new Response(createFileBodyStream(filePath), {
286
+ headers: {
287
+ ...headers,
288
+ "Content-Length": String(stat.size),
289
+ },
290
+ });
291
+ }
292
+
293
+ const match = /^bytes=(\d*)-(\d*)$/.exec(rangeHeader);
294
+ if (!match) {
295
+ return new Response(null, {
296
+ status: 416,
297
+ headers: {
298
+ ...headers,
299
+ "Content-Range": `bytes */${stat.size}`,
300
+ },
301
+ });
302
+ }
303
+
304
+ let start = match[1] ? Number(match[1]) : 0;
305
+ let end = match[2] ? Number(match[2]) : stat.size - 1;
306
+ if (!match[1] && match[2]) {
307
+ const suffixLength = Number(match[2]);
308
+ start = Math.max(stat.size - suffixLength, 0);
309
+ end = stat.size - 1;
310
+ }
311
+
312
+ if (!Number.isFinite(start) || !Number.isFinite(end) || start < 0 || end < start || start >= stat.size) {
313
+ return new Response(null, {
314
+ status: 416,
315
+ headers: {
316
+ ...headers,
317
+ "Content-Range": `bytes */${stat.size}`,
318
+ },
319
+ });
320
+ }
321
+
322
+ end = Math.min(end, stat.size - 1);
323
+ const chunkSize = end - start + 1;
324
+ return new Response(createFileBodyStream(filePath, { start, end }), {
325
+ status: 206,
326
+ headers: {
327
+ ...headers,
328
+ "Content-Length": String(chunkSize),
329
+ "Content-Range": `bytes ${start}-${end}/${stat.size}`,
330
+ },
331
+ });
332
+ }
333
+
334
+ export async function GET(
335
+ request: NextRequest,
336
+ { params }: { params: Promise<{ path: string[] }> }
337
+ ) {
338
+ try {
339
+ const { path: segments } = await params;
340
+ const filePath = filePathFromSegments(segments);
341
+ const type = request.nextUrl.searchParams.get("type") ?? "list";
342
+
343
+ const allowedRoots = await getAllowedRoots();
344
+ if (!isPathAllowed(filePath, allowedRoots)) {
345
+ return NextResponse.json({ error: "Access denied" }, { status: 403 });
346
+ }
347
+
348
+ let stat: fs.Stats;
349
+ try {
350
+ stat = fs.statSync(filePath);
351
+ } catch {
352
+ return NextResponse.json({ error: "Not found" }, { status: 404 });
353
+ }
354
+
355
+ if (type === "read") {
356
+ if (!stat.isFile()) {
357
+ return NextResponse.json({ error: "Not a file" }, { status: 400 });
358
+ }
359
+ const imageMime = getImageMime(filePath);
360
+ if (imageMime) {
361
+ if (stat.size > IMAGE_PREVIEW_MAX_BYTES) {
362
+ return NextResponse.json({ error: "Image too large (>10MB)" }, { status: 413 });
363
+ }
364
+ return streamFile(filePath, stat, imageMime, request.headers.get("range"));
365
+ }
366
+ const audioMime = getAudioMime(filePath);
367
+ if (audioMime) {
368
+ return streamFile(filePath, stat, audioMime, request.headers.get("range"));
369
+ }
370
+ const docMime = getDocMime(filePath);
371
+ if (docMime) {
372
+ if (stat.size > DOC_PREVIEW_MAX_BYTES) {
373
+ return NextResponse.json({ error: "Document too large (>50MB)" }, { status: 413 });
374
+ }
375
+ return streamFile(filePath, stat, docMime, request.headers.get("range"));
376
+ }
377
+ const textPreviewLimit = getTextPreviewLimit(filePath);
378
+ if (stat.size > textPreviewLimit) {
379
+ return NextResponse.json({ error: `File too large for preview (>${Math.round(textPreviewLimit / 1024 / 1024 * 10) / 10}MB)` }, { status: 413 });
380
+ }
381
+ const content = fs.readFileSync(filePath, "utf-8");
382
+ const language = getLanguage(filePath);
383
+ return NextResponse.json({ content, language, size: stat.size, mtime: stat.mtime.toISOString() });
384
+ }
385
+
386
+ if (type === "watch") {
387
+ if (!stat.isFile()) {
388
+ return NextResponse.json({ error: "Not a file" }, { status: 400 });
389
+ }
390
+ let watcher: fs.FSWatcher | null = null;
391
+ const stream = new ReadableStream({
392
+ start(controller) {
393
+ const send = (eventName: string, data: Record<string, unknown>) => {
394
+ const payload = `event: ${eventName}\ndata: ${JSON.stringify(data)}\n\n`;
395
+ try {
396
+ controller.enqueue(new TextEncoder().encode(payload));
397
+ } catch {
398
+ // client disconnected
399
+ }
400
+ };
401
+ // Send initial ping so client knows connection is live
402
+ send("connected", { filePath });
403
+ try {
404
+ watcher = fs.watch(filePath, () => {
405
+ try {
406
+ const s = fs.statSync(filePath);
407
+ send("change", { mtime: s.mtime.toISOString(), size: s.size });
408
+ } catch {
409
+ send("change", { mtime: new Date().toISOString(), size: 0 });
410
+ }
411
+ });
412
+ watcher.on("error", () => {
413
+ try { controller.close(); } catch { /* ignore */ }
414
+ });
415
+ } catch {
416
+ send("error", { message: "Failed to watch file" });
417
+ controller.close();
418
+ }
419
+ },
420
+ cancel() {
421
+ try { watcher?.close(); } catch { /* ignore */ }
422
+ },
423
+ });
424
+ return new Response(stream, {
425
+ headers: {
426
+ "Content-Type": "text/event-stream",
427
+ "Cache-Control": "no-cache, no-transform",
428
+ Connection: "keep-alive",
429
+ "X-Accel-Buffering": "no",
430
+ },
431
+ });
432
+ }
433
+
434
+ // type === "list"
435
+ if (!stat.isDirectory()) {
436
+ return NextResponse.json({ error: "Not a directory" }, { status: 400 });
437
+ }
438
+
439
+ const names = fs.readdirSync(filePath);
440
+ const entries = names
441
+ .filter((name) => !IGNORED_NAMES.has(name) && !IGNORED_SUFFIXES.some((s) => name.endsWith(s)))
442
+ .map((name) => {
443
+ const full = path.join(filePath, name);
444
+ try {
445
+ const s = fs.statSync(full);
446
+ return {
447
+ name,
448
+ isDir: s.isDirectory(),
449
+ size: s.isFile() ? s.size : 0,
450
+ modified: s.mtime.toISOString(),
451
+ };
452
+ } catch {
453
+ return null;
454
+ }
455
+ })
456
+ .filter(Boolean)
457
+ .sort((a, b) => {
458
+ // Dirs first, then files, both alphabetically
459
+ if (a!.isDir !== b!.isDir) return a!.isDir ? -1 : 1;
460
+ return a!.name.localeCompare(b!.name);
461
+ });
462
+
463
+ return NextResponse.json({ entries, path: filePath });
464
+ } catch (error) {
465
+ return NextResponse.json({ error: String(error) }, { status: 500 });
466
+ }
467
+ }
468
+
469
+ export async function PUT(
470
+ request: NextRequest,
471
+ { params }: { params: Promise<{ path: string[] }> }
472
+ ) {
473
+ try {
474
+ const { path: segments } = await params;
475
+ const filePath = filePathFromSegments(segments);
476
+
477
+ const allowedRoots = await getAllowedRoots();
478
+ if (!isPathAllowed(filePath, allowedRoots)) {
479
+ return NextResponse.json({ error: "Access denied" }, { status: 403 });
480
+ }
481
+
482
+ let stat: fs.Stats;
483
+ try {
484
+ stat = fs.statSync(filePath);
485
+ } catch {
486
+ return NextResponse.json({ error: "Not found" }, { status: 404 });
487
+ }
488
+
489
+ if (!stat.isFile()) {
490
+ return NextResponse.json({ error: "Not a file" }, { status: 400 });
491
+ }
492
+
493
+ const body = await request.json() as { content?: string; baseMtime?: string };
494
+ if (typeof body.content !== "string") {
495
+ return NextResponse.json({ error: "Missing 'content' field" }, { status: 400 });
496
+ }
497
+
498
+ if (body.baseMtime && body.baseMtime !== stat.mtime.toISOString()) {
499
+ return NextResponse.json({
500
+ error: "File changed on disk since it was opened",
501
+ conflict: true,
502
+ mtime: stat.mtime.toISOString(),
503
+ }, { status: 409 });
504
+ }
505
+
506
+ const textLimit = getTextPreviewLimit(filePath);
507
+ if (Buffer.byteLength(body.content, "utf-8") > textLimit) {
508
+ return NextResponse.json({ error: `Content too large (>${formatLimit(textLimit)})` }, { status: 413 });
509
+ }
510
+
511
+ fs.writeFileSync(filePath, body.content, "utf-8");
512
+ return NextResponse.json({ success: true, mtime: fs.statSync(filePath).mtime.toISOString() });
513
+ } catch (error) {
514
+ return NextResponse.json({ error: String(error) }, { status: 500 });
515
+ }
516
+ }
517
+
518
+ export async function POST(
519
+ request: NextRequest,
520
+ { params }: { params: Promise<{ path: string[] }> }
521
+ ) {
522
+ try {
523
+ const { path: segments } = await params;
524
+ const dirPath = filePathFromSegments(segments);
525
+ const type = request.nextUrl.searchParams.get("type");
526
+
527
+ if (type !== "upload") {
528
+ return NextResponse.json({ error: "Unsupported operation" }, { status: 400 });
529
+ }
530
+
531
+ const allowedRoots = await getAllowedRoots();
532
+ if (!isPathAllowed(dirPath, allowedRoots)) {
533
+ return NextResponse.json({ error: "Access denied" }, { status: 403 });
534
+ }
535
+
536
+ let stat: fs.Stats;
537
+ try {
538
+ stat = fs.statSync(dirPath);
539
+ } catch {
540
+ return NextResponse.json({ error: "Target directory not found" }, { status: 404 });
541
+ }
542
+
543
+ if (!stat.isDirectory()) {
544
+ return NextResponse.json({ error: "Target is not a directory" }, { status: 400 });
545
+ }
546
+
547
+ try {
548
+ fs.accessSync(dirPath, fs.constants.W_OK);
549
+ } catch {
550
+ return NextResponse.json({ error: "No write permission for target directory" }, { status: 403 });
551
+ }
552
+
553
+ const formData = await request.formData();
554
+ const values = formData.getAll("files");
555
+ const files = values.filter(isFormUploadFile);
556
+ if (files.length === 0) {
557
+ return NextResponse.json({ error: "No files uploaded" }, { status: 400 });
558
+ }
559
+
560
+ const totalBytes = files.reduce((sum, file) => sum + file.size, 0);
561
+ if (totalBytes > UPLOAD_MAX_TOTAL_BYTES) {
562
+ return NextResponse.json({ error: "Upload batch too large (>200MB)" }, { status: 413 });
563
+ }
564
+
565
+ const uploaded: Array<{ originalName: string; name: string; path: string; size: number }> = [];
566
+ const failed: Array<{ name: string; error: string }> = [];
567
+
568
+ for (const file of files) {
569
+ const originalName = file.name || "untitled";
570
+ const safeName = sanitizeUploadName(originalName);
571
+ if (!safeName) {
572
+ failed.push({ name: originalName, error: "Invalid file name" });
573
+ continue;
574
+ }
575
+ if (file.size > UPLOAD_MAX_FILE_BYTES) {
576
+ failed.push({ name: originalName, error: "File too large (>50MB)" });
577
+ continue;
578
+ }
579
+
580
+ try {
581
+ const buffer = Buffer.from(await file.arrayBuffer());
582
+ let saved = false;
583
+ for (let attempt = 0; attempt < 1000; attempt += 1) {
584
+ const target = getUploadCandidate(dirPath, safeName, attempt);
585
+ try {
586
+ fs.writeFileSync(target.path, buffer, { flag: "wx" });
587
+ uploaded.push({
588
+ originalName,
589
+ name: target.name,
590
+ path: target.path,
591
+ size: file.size,
592
+ });
593
+ saved = true;
594
+ break;
595
+ } catch (error) {
596
+ if (isNodeFileError(error, "EEXIST")) continue;
597
+ throw error;
598
+ }
599
+ }
600
+ if (!saved) {
601
+ failed.push({ name: originalName, error: "Could not find an available file name" });
602
+ }
603
+ } catch (error) {
604
+ const message = error instanceof Error ? error.message : String(error);
605
+ if (message.includes("EACCES") || message.includes("EPERM")) {
606
+ failed.push({ name: originalName, error: "No write permission" });
607
+ } else {
608
+ failed.push({ name: originalName, error: message });
609
+ }
610
+ }
611
+ }
612
+
613
+ return NextResponse.json({
614
+ success: failed.length === 0,
615
+ uploaded,
616
+ failed,
617
+ }, { status: uploaded.length > 0 ? 200 : 400 });
618
+ } catch (error) {
619
+ return NextResponse.json({ error: String(error) }, { status: 500 });
620
+ }
621
+ }
@@ -0,0 +1,47 @@
1
+ import { NextResponse } from "next/server";
2
+ import { readFileSync, writeFileSync, existsSync } from "fs";
3
+ import { join } from "path";
4
+ import { homedir } from "os";
5
+
6
+ function getConfigDir(): string {
7
+ return join(homedir(), ".config", "annodex");
8
+ }
9
+
10
+ function getHarnessPath(): string {
11
+ const dir = getConfigDir();
12
+ const upper = join(dir, "HARNESS.md");
13
+ const lower = join(dir, "harness.md");
14
+ // Read: uppercase first, fall back to lowercase
15
+ if (existsSync(upper)) return upper;
16
+ return lower;
17
+ }
18
+
19
+ // GET /api/harness — read global HARNESS.md
20
+ export async function GET() {
21
+ try {
22
+ const path = getHarnessPath();
23
+ if (!existsSync(path)) {
24
+ return NextResponse.json({ content: "", exists: false });
25
+ }
26
+ const content = readFileSync(path, "utf-8");
27
+ return NextResponse.json({ content, exists: true, size: Buffer.byteLength(content, "utf-8") });
28
+ } catch (error) {
29
+ return NextResponse.json({ error: String(error) }, { status: 500 });
30
+ }
31
+ }
32
+
33
+ // PUT /api/harness — write global HARNESS.md (always uppercase)
34
+ export async function PUT(req: Request) {
35
+ try {
36
+ const body = await req.json() as { content?: string };
37
+ if (typeof body.content !== "string") {
38
+ return NextResponse.json({ error: "Missing 'content' field" }, { status: 400 });
39
+ }
40
+ // Always write uppercase
41
+ const path = join(getConfigDir(), "HARNESS.md");
42
+ writeFileSync(path, body.content, "utf-8");
43
+ return NextResponse.json({ success: true });
44
+ } catch (error) {
45
+ return NextResponse.json({ error: String(error) }, { status: 500 });
46
+ }
47
+ }
@@ -0,0 +1,6 @@
1
+ import { NextResponse } from "next/server";
2
+ import { homedir } from "os";
3
+
4
+ export async function GET() {
5
+ return NextResponse.json({ home: homedir() });
6
+ }
@@ -0,0 +1,26 @@
1
+ import { NextResponse } from "next/server";
2
+ import { getRuntimeStatus } from "@/lib/rpc-manager";
3
+
4
+ export const dynamic = "force-dynamic";
5
+
6
+ const CURRENT_VERSION = process.env.NEXT_PUBLIC_APP_VERSION ?? "0.0.0";
7
+
8
+ function isAuthorized(req: Request): boolean {
9
+ const token = process.env.ANNODEX_INTERNAL_TOKEN ?? process.env.ANNOVIBE_INTERNAL_TOKEN;
10
+ if (!token) return false;
11
+ return req.headers.get("x-annodex-internal-token") === token
12
+ || req.headers.get("x-annovibe-internal-token") === token;
13
+ }
14
+
15
+ export async function GET(req: Request) {
16
+ if (!isAuthorized(req)) {
17
+ return NextResponse.json({ error: "Forbidden" }, { status: 403 });
18
+ }
19
+
20
+ return NextResponse.json({
21
+ runningVersion: CURRENT_VERSION,
22
+ pid: process.pid,
23
+ uptimeSeconds: Math.round(process.uptime()),
24
+ ...getRuntimeStatus(),
25
+ });
26
+ }