@baique/pi-web-sky 0.1.35 → 0.1.37

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 (483) hide show
  1. package/.next/BUILD_ID +1 -1
  2. package/.next/app-path-routes-manifest.json +11 -11
  3. package/.next/build-manifest.json +4 -4
  4. package/.next/next-server.js.nft.json +1 -1
  5. package/.next/prerender-manifest.json +3 -3
  6. package/.next/react-loadable-manifest.json +2 -2
  7. package/.next/required-server-files.js +6 -6
  8. package/.next/required-server-files.json +6 -6
  9. package/.next/server/app/_global-error/page.js +2 -2
  10. package/.next/server/app/_global-error/page_client-reference-manifest.js +1 -1
  11. package/.next/server/app/_global-error.html +1 -1
  12. package/.next/server/app/_global-error.rsc +1 -1
  13. package/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  14. package/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
  15. package/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  16. package/.next/server/app/_not-found/page.js +1 -1
  17. package/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  18. package/.next/server/app/_not-found.html +1 -1
  19. package/.next/server/app/_not-found.rsc +3 -3
  20. package/.next/server/app/_not-found.segments/_full.segment.rsc +3 -3
  21. package/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +3 -3
  22. package/.next/server/app/_not-found.segments/_tree.segment.rsc +2 -2
  23. package/.next/server/app/api/agent/[id]/bash-output/route.js +2 -2
  24. package/.next/server/app/api/agent/[id]/bash-output/route_client-reference-manifest.js +1 -1
  25. package/.next/server/app/api/agent/[id]/events/route.js +3 -3
  26. package/.next/server/app/api/agent/[id]/events/route_client-reference-manifest.js +1 -1
  27. package/.next/server/app/api/agent/[id]/route.js +2 -2
  28. package/.next/server/app/api/agent/[id]/route_client-reference-manifest.js +1 -1
  29. package/.next/server/app/api/agent/new/route.js +1 -1
  30. package/.next/server/app/api/agent/new/route_client-reference-manifest.js +1 -1
  31. package/.next/server/app/api/agent/running/route.js +1 -1
  32. package/.next/server/app/api/agent/running/route_client-reference-manifest.js +1 -1
  33. package/.next/server/app/api/app-update/route.js +1 -1
  34. package/.next/server/app/api/app-update/route_client-reference-manifest.js +1 -1
  35. package/.next/server/app/api/auth/all-providers/route.js +1 -1
  36. package/.next/server/app/api/auth/all-providers/route_client-reference-manifest.js +1 -1
  37. package/.next/server/app/api/auth/api-key/[provider]/route.js +1 -1
  38. package/.next/server/app/api/auth/api-key/[provider]/route_client-reference-manifest.js +1 -1
  39. package/.next/server/app/api/auth/login/[provider]/route.js +3 -3
  40. package/.next/server/app/api/auth/login/[provider]/route_client-reference-manifest.js +1 -1
  41. package/.next/server/app/api/auth/logout/[provider]/route.js +1 -1
  42. package/.next/server/app/api/auth/logout/[provider]/route_client-reference-manifest.js +1 -1
  43. package/.next/server/app/api/auth/providers/route.js +1 -1
  44. package/.next/server/app/api/auth/providers/route_client-reference-manifest.js +1 -1
  45. package/.next/server/app/api/boards/[id]/canvas/route.js +1 -1
  46. package/.next/server/app/api/boards/[id]/canvas/route_client-reference-manifest.js +1 -1
  47. package/.next/server/app/api/boards/[id]/edges/[eid]/route.js +1 -1
  48. package/.next/server/app/api/boards/[id]/edges/[eid]/route_client-reference-manifest.js +1 -1
  49. package/.next/server/app/api/boards/[id]/edges/route.js +1 -1
  50. package/.next/server/app/api/boards/[id]/edges/route_client-reference-manifest.js +1 -1
  51. package/.next/server/app/api/boards/[id]/nodes/[nid]/route.js +1 -1
  52. package/.next/server/app/api/boards/[id]/nodes/[nid]/route_client-reference-manifest.js +1 -1
  53. package/.next/server/app/api/boards/[id]/nodes/route.js +1 -1
  54. package/.next/server/app/api/boards/[id]/nodes/route_client-reference-manifest.js +1 -1
  55. package/.next/server/app/api/boards/[id]/reconcile-task-edges/route.js +1 -1
  56. package/.next/server/app/api/boards/[id]/reconcile-task-edges/route_client-reference-manifest.js +1 -1
  57. package/.next/server/app/api/boards/[id]/route.js +1 -1
  58. package/.next/server/app/api/boards/[id]/route_client-reference-manifest.js +1 -1
  59. package/.next/server/app/api/boards/purge-orphans/route.js +1 -1
  60. package/.next/server/app/api/boards/purge-orphans/route_client-reference-manifest.js +1 -1
  61. package/.next/server/app/api/boards/reorder/route.js +1 -1
  62. package/.next/server/app/api/boards/reorder/route_client-reference-manifest.js +1 -1
  63. package/.next/server/app/api/boards/route.js +1 -1
  64. package/.next/server/app/api/boards/route_client-reference-manifest.js +1 -1
  65. package/.next/server/app/api/cwd/browse/route.js +1 -1
  66. package/.next/server/app/api/cwd/browse/route_client-reference-manifest.js +1 -1
  67. package/.next/server/app/api/cwd/validate/route.js +2 -2
  68. package/.next/server/app/api/cwd/validate/route_client-reference-manifest.js +1 -1
  69. package/.next/server/app/api/default-cwd/route.js +2 -2
  70. package/.next/server/app/api/default-cwd/route_client-reference-manifest.js +1 -1
  71. package/.next/server/app/api/file-index/route.js +2 -2
  72. package/.next/server/app/api/file-index/route_client-reference-manifest.js +1 -1
  73. package/.next/server/app/api/files/[...path]/route.js +2 -2
  74. package/.next/server/app/api/files/[...path]/route_client-reference-manifest.js +1 -1
  75. package/.next/server/app/api/git/diff/route.js +1 -1
  76. package/.next/server/app/api/git/diff/route_client-reference-manifest.js +1 -1
  77. package/.next/server/app/api/git/status/route.js +1 -1
  78. package/.next/server/app/api/git/status/route_client-reference-manifest.js +1 -1
  79. package/.next/server/app/api/home/route.js +1 -1
  80. package/.next/server/app/api/home/route_client-reference-manifest.js +1 -1
  81. package/.next/server/app/api/mcp/route.js +2 -2
  82. package/.next/server/app/api/mcp/route_client-reference-manifest.js +1 -1
  83. package/.next/server/app/api/models/route.js +2 -2
  84. package/.next/server/app/api/models/route_client-reference-manifest.js +1 -1
  85. package/.next/server/app/api/models-config/catalog/route.js +1 -1
  86. package/.next/server/app/api/models-config/catalog/route_client-reference-manifest.js +1 -1
  87. package/.next/server/app/api/models-config/discover/route.js +1 -1
  88. package/.next/server/app/api/models-config/discover/route_client-reference-manifest.js +1 -1
  89. package/.next/server/app/api/models-config/route.js +1 -1
  90. package/.next/server/app/api/models-config/route_client-reference-manifest.js +1 -1
  91. package/.next/server/app/api/models-config/test/route.js +1 -1
  92. package/.next/server/app/api/models-config/test/route_client-reference-manifest.js +1 -1
  93. package/.next/server/app/api/plugins/route.js +2 -2
  94. package/.next/server/app/api/plugins/route_client-reference-manifest.js +1 -1
  95. package/.next/server/app/api/project-trust/route.js +2 -2
  96. package/.next/server/app/api/project-trust/route_client-reference-manifest.js +1 -1
  97. package/.next/server/app/api/quota/route.js +1 -1
  98. package/.next/server/app/api/quota/route_client-reference-manifest.js +1 -1
  99. package/.next/server/app/api/search/route.js +2 -2
  100. package/.next/server/app/api/search/route_client-reference-manifest.js +1 -1
  101. package/.next/server/app/api/sessions/[id]/auto-name/route.js +3 -3
  102. package/.next/server/app/api/sessions/[id]/auto-name/route_client-reference-manifest.js +1 -1
  103. package/.next/server/app/api/sessions/[id]/context/route.js +2 -2
  104. package/.next/server/app/api/sessions/[id]/context/route_client-reference-manifest.js +1 -1
  105. package/.next/server/app/api/sessions/[id]/entries/[entryId]/thinking/route.js +2 -2
  106. package/.next/server/app/api/sessions/[id]/entries/[entryId]/thinking/route_client-reference-manifest.js +1 -1
  107. package/.next/server/app/api/sessions/[id]/export/route.js +2 -2
  108. package/.next/server/app/api/sessions/[id]/export/route_client-reference-manifest.js +1 -1
  109. package/.next/server/app/api/sessions/[id]/route.js +1 -1
  110. package/.next/server/app/api/sessions/[id]/route_client-reference-manifest.js +1 -1
  111. package/.next/server/app/api/sessions/[id]/state/route.js +2 -2
  112. package/.next/server/app/api/sessions/[id]/state/route_client-reference-manifest.js +1 -1
  113. package/.next/server/app/api/sessions/[id]/todos/route.js +2 -2
  114. package/.next/server/app/api/sessions/[id]/todos/route_client-reference-manifest.js +1 -1
  115. package/.next/server/app/api/sessions/route.js +2 -2
  116. package/.next/server/app/api/sessions/route_client-reference-manifest.js +1 -1
  117. package/.next/server/app/api/skills/check/route.js +1 -1
  118. package/.next/server/app/api/skills/check/route.js.nft.json +1 -1
  119. package/.next/server/app/api/skills/check/route_client-reference-manifest.js +1 -1
  120. package/.next/server/app/api/skills/install/route.js +2 -2
  121. package/.next/server/app/api/skills/install/route_client-reference-manifest.js +1 -1
  122. package/.next/server/app/api/skills/route.js +2 -31
  123. package/.next/server/app/api/skills/route.js.nft.json +1 -1
  124. package/.next/server/app/api/skills/route_client-reference-manifest.js +1 -1
  125. package/.next/server/app/api/skills/search/route.js +1 -1
  126. package/.next/server/app/api/skills/search/route_client-reference-manifest.js +1 -1
  127. package/.next/server/app/api/skills/update/route.js +1 -1
  128. package/.next/server/app/api/skills/update/route.js.nft.json +1 -1
  129. package/.next/server/app/api/skills/update/route_client-reference-manifest.js +1 -1
  130. package/.next/server/app/api/task-card-questions/[id]/answer/route.js +1 -1
  131. package/.next/server/app/api/task-card-questions/[id]/answer/route_client-reference-manifest.js +1 -1
  132. package/.next/server/app/api/task-card-questions/route.js +1 -1
  133. package/.next/server/app/api/task-card-questions/route_client-reference-manifest.js +1 -1
  134. package/.next/server/app/api/task-cards/[id]/route.js +1 -1
  135. package/.next/server/app/api/task-cards/[id]/route_client-reference-manifest.js +1 -1
  136. package/.next/server/app/api/task-cards/route.js +1 -1
  137. package/.next/server/app/api/task-cards/route_client-reference-manifest.js +1 -1
  138. package/.next/server/app/api/task-scheduler/status/route.js +1 -1
  139. package/.next/server/app/api/task-scheduler/status/route_client-reference-manifest.js +1 -1
  140. package/.next/server/app/api/tasks/[id]/board/route.js +1 -1
  141. package/.next/server/app/api/tasks/[id]/board/route_client-reference-manifest.js +1 -1
  142. package/.next/server/app/api/tasks/[id]/route.js +1 -1
  143. package/.next/server/app/api/tasks/[id]/route_client-reference-manifest.js +1 -1
  144. package/.next/server/app/api/tasks/reorder/route.js +1 -1
  145. package/.next/server/app/api/tasks/reorder/route_client-reference-manifest.js +1 -1
  146. package/.next/server/app/api/tasks/route.js +1 -1
  147. package/.next/server/app/api/tasks/route_client-reference-manifest.js +1 -1
  148. package/.next/server/app/api/terminal/[id]/events/route.js +3 -3
  149. package/.next/server/app/api/terminal/[id]/events/route_client-reference-manifest.js +1 -1
  150. package/.next/server/app/api/terminal/[id]/route.js +2 -2
  151. package/.next/server/app/api/terminal/[id]/route_client-reference-manifest.js +1 -1
  152. package/.next/server/app/api/terminal/route.js +2 -2
  153. package/.next/server/app/api/terminal/route_client-reference-manifest.js +1 -1
  154. package/.next/server/app/api/worktrees/route.js +2 -2
  155. package/.next/server/app/api/worktrees/route_client-reference-manifest.js +1 -1
  156. package/.next/server/app/favicon.ico/route.js +1 -1
  157. package/.next/server/app/index.html +1 -1
  158. package/.next/server/app/index.rsc +5 -5
  159. package/.next/server/app/index.segments/__PAGE__.segment.rsc +5 -5
  160. package/.next/server/app/index.segments/_full.segment.rsc +5 -5
  161. package/.next/server/app/index.segments/_tree.segment.rsc +2 -2
  162. package/.next/server/app/manifest.webmanifest/route.js +2 -2
  163. package/.next/server/app/manifest.webmanifest/route_client-reference-manifest.js +1 -1
  164. package/.next/server/app/page.js +3 -3
  165. package/.next/server/app/page_client-reference-manifest.js +1 -1
  166. package/.next/server/app-paths-manifest.json +11 -11
  167. package/.next/server/chunks/2404.js +2 -2
  168. package/.next/server/chunks/2430.js +4 -4
  169. package/.next/server/chunks/3500.js +30 -0
  170. package/.next/server/chunks/4226.js +2 -2
  171. package/.next/server/chunks/542.js +11 -6
  172. package/.next/server/chunks/5794.js +2 -2
  173. package/.next/server/chunks/6429.js +1 -1
  174. package/.next/server/chunks/898.js +2 -2
  175. package/.next/server/instrumentation.js +1 -1
  176. package/.next/server/middleware-build-manifest.js +1 -1
  177. package/.next/server/middleware-react-loadable-manifest.js +1 -1
  178. package/.next/server/middleware.js +3 -3
  179. package/.next/server/next-font-manifest.js +1 -1
  180. package/.next/server/next-font-manifest.json +1 -1
  181. package/.next/server/pages/404.html +1 -1
  182. package/.next/server/pages/500.html +1 -1
  183. package/.next/server/server-reference-manifest.json +1 -1
  184. package/.next/static/chunks/5315.dba7515ae6e139ae.js +4 -0
  185. package/.next/static/chunks/{7999.f4ab221861378788.js → 7999.879d5a20e8641752.js} +1 -1
  186. package/.next/static/chunks/app/_not-found/{page-af4fe23994cb3d66.js → page-8cea8d8de35f4da4.js} +1 -1
  187. package/.next/static/chunks/app/{layout-1d9a7a44fbc92d2e.js → layout-c4ca28ccc08bc975.js} +1 -1
  188. package/.next/static/chunks/app/{page-6d6779a81a0a7639.js → page-c9ead38525a85686.js} +3 -3
  189. package/.next/static/chunks/{main-app-b3e8a8d15d83e1f0.js → main-app-dd2385a1a5a588df.js} +1 -1
  190. package/.next/static/chunks/next/dist/client/components/builtin/global-error-be943dcb89867ab9.js +1 -0
  191. package/.next/static/chunks/{webpack-fa4f40085b1ec1ac.js → webpack-4bbf04891e910713.js} +1 -1
  192. package/.next/static/css/422c7dc0faa04d89.css +1 -0
  193. package/.next/trace +8 -9
  194. package/.next/trace-build +1 -1
  195. package/.next/types/app/api/agent/[id]/bash-output/route.ts +1 -1
  196. package/.next/types/app/api/agent/[id]/events/route.ts +1 -1
  197. package/.next/types/app/api/agent/[id]/route.ts +1 -1
  198. package/.next/types/app/api/agent/new/route.ts +1 -1
  199. package/.next/types/app/api/agent/running/route.ts +1 -1
  200. package/.next/types/app/api/app-update/route.ts +1 -1
  201. package/.next/types/app/api/auth/all-providers/route.ts +1 -1
  202. package/.next/types/app/api/auth/api-key/[provider]/route.ts +1 -1
  203. package/.next/types/app/api/auth/login/[provider]/route.ts +1 -1
  204. package/.next/types/app/api/auth/logout/[provider]/route.ts +1 -1
  205. package/.next/types/app/api/auth/providers/route.ts +1 -1
  206. package/.next/types/app/api/boards/[id]/canvas/route.ts +1 -1
  207. package/.next/types/app/api/boards/[id]/edges/[eid]/route.ts +1 -1
  208. package/.next/types/app/api/boards/[id]/edges/route.ts +1 -1
  209. package/.next/types/app/api/boards/[id]/nodes/[nid]/route.ts +1 -1
  210. package/.next/types/app/api/boards/[id]/nodes/route.ts +1 -1
  211. package/.next/types/app/api/boards/[id]/reconcile-task-edges/route.ts +1 -1
  212. package/.next/types/app/api/boards/[id]/route.ts +1 -1
  213. package/.next/types/app/api/boards/purge-orphans/route.ts +1 -1
  214. package/.next/types/app/api/boards/reorder/route.ts +1 -1
  215. package/.next/types/app/api/boards/route.ts +1 -1
  216. package/.next/types/app/api/cwd/browse/route.ts +1 -1
  217. package/.next/types/app/api/cwd/validate/route.ts +1 -1
  218. package/.next/types/app/api/default-cwd/route.ts +1 -1
  219. package/.next/types/app/api/file-index/route.ts +1 -1
  220. package/.next/types/app/api/files/[...path]/route.ts +1 -1
  221. package/.next/types/app/api/git/diff/route.ts +1 -1
  222. package/.next/types/app/api/git/status/route.ts +1 -1
  223. package/.next/types/app/api/home/route.ts +1 -1
  224. package/.next/types/app/api/mcp/route.ts +1 -1
  225. package/.next/types/app/api/models/route.ts +1 -1
  226. package/.next/types/app/api/models-config/catalog/route.ts +1 -1
  227. package/.next/types/app/api/models-config/discover/route.ts +1 -1
  228. package/.next/types/app/api/models-config/route.ts +1 -1
  229. package/.next/types/app/api/models-config/test/route.ts +1 -1
  230. package/.next/types/app/api/plugins/route.ts +1 -1
  231. package/.next/types/app/api/project-trust/route.ts +1 -1
  232. package/.next/types/app/api/quota/route.ts +1 -1
  233. package/.next/types/app/api/search/route.ts +1 -1
  234. package/.next/types/app/api/sessions/[id]/auto-name/route.ts +1 -1
  235. package/.next/types/app/api/sessions/[id]/context/route.ts +1 -1
  236. package/.next/types/app/api/sessions/[id]/entries/[entryId]/thinking/route.ts +1 -1
  237. package/.next/types/app/api/sessions/[id]/export/route.ts +1 -1
  238. package/.next/types/app/api/sessions/[id]/route.ts +1 -1
  239. package/.next/types/app/api/sessions/[id]/state/route.ts +1 -1
  240. package/.next/types/app/api/sessions/[id]/todos/route.ts +1 -1
  241. package/.next/types/app/api/sessions/route.ts +1 -1
  242. package/.next/types/app/api/skills/check/route.ts +1 -1
  243. package/.next/types/app/api/skills/install/route.ts +1 -1
  244. package/.next/types/app/api/skills/route.ts +1 -1
  245. package/.next/types/app/api/skills/search/route.ts +1 -1
  246. package/.next/types/app/api/skills/update/route.ts +1 -1
  247. package/.next/types/app/api/task-card-questions/[id]/answer/route.ts +1 -1
  248. package/.next/types/app/api/task-card-questions/route.ts +1 -1
  249. package/.next/types/app/api/task-cards/[id]/route.ts +1 -1
  250. package/.next/types/app/api/task-cards/route.ts +1 -1
  251. package/.next/types/app/api/task-scheduler/status/route.ts +1 -1
  252. package/.next/types/app/api/tasks/[id]/board/route.ts +1 -1
  253. package/.next/types/app/api/tasks/[id]/route.ts +1 -1
  254. package/.next/types/app/api/tasks/reorder/route.ts +1 -1
  255. package/.next/types/app/api/tasks/route.ts +1 -1
  256. package/.next/types/app/api/terminal/[id]/events/route.ts +1 -1
  257. package/.next/types/app/api/terminal/[id]/route.ts +1 -1
  258. package/.next/types/app/api/terminal/route.ts +1 -1
  259. package/.next/types/app/api/worktrees/route.ts +1 -1
  260. package/.next/types/app/layout.ts +1 -1
  261. package/.next/types/app/page.ts +1 -1
  262. package/bin/pi-web-sky.js +15 -20
  263. package/lib/agent-client.test.mjs +64 -0
  264. package/lib/agent-client.ts +53 -0
  265. package/lib/agent-event-connection.test.mjs +193 -0
  266. package/lib/agent-event-connection.ts +202 -0
  267. package/lib/agent-event-stream.test.mjs +155 -0
  268. package/lib/agent-event-stream.ts +132 -0
  269. package/lib/agent-event-wire.test.mjs +278 -0
  270. package/lib/agent-event-wire.ts +107 -0
  271. package/lib/agent-phase.ts +24 -0
  272. package/lib/allowed-roots.ts +31 -0
  273. package/lib/ansi.test.mjs +52 -0
  274. package/lib/ansi.ts +211 -0
  275. package/lib/api-types.ts +139 -0
  276. package/lib/app-update.test.mjs +27 -0
  277. package/lib/app-update.ts +28 -0
  278. package/lib/atomic-file.test.mjs +37 -0
  279. package/lib/atomic-file.ts +34 -0
  280. package/lib/audit-session.ts +235 -0
  281. package/lib/bash-output.test.mjs +63 -0
  282. package/lib/bash-output.ts +53 -0
  283. package/lib/bg-image.ts +129 -0
  284. package/lib/board-events.ts +83 -0
  285. package/lib/board-purge.test.mjs +66 -0
  286. package/lib/board-purge.ts +65 -0
  287. package/lib/board-search.test.mjs +110 -0
  288. package/lib/board-search.ts +63 -0
  289. package/lib/board-store.test.mjs +542 -0
  290. package/lib/board-store.ts +794 -0
  291. package/lib/board-types.ts +96 -0
  292. package/lib/board-utils.ts +17 -0
  293. package/lib/bounded-form-data.test.mjs +54 -0
  294. package/lib/bounded-form-data.ts +49 -0
  295. package/lib/browser-notifications.test.mjs +167 -0
  296. package/lib/browser-notifications.ts +104 -0
  297. package/lib/chat-lazy-load.test.mjs +140 -0
  298. package/lib/chat-lazy-load.ts +64 -0
  299. package/lib/clipboard-paths.test.mjs +140 -0
  300. package/lib/clipboard-paths.ts +192 -0
  301. package/lib/clipboard.ts +18 -0
  302. package/lib/compaction-summary.test.mjs +44 -0
  303. package/lib/compaction-summary.ts +34 -0
  304. package/lib/custom-ui-terminal.test.mjs +32 -0
  305. package/lib/custom-ui-terminal.ts +27 -0
  306. package/lib/deepseek-pricing.test.mjs +120 -0
  307. package/lib/deepseek-pricing.ts +71 -0
  308. package/lib/directory-browser.test.mjs +60 -0
  309. package/lib/directory-browser.ts +83 -0
  310. package/lib/draft-stash.test.mjs +91 -0
  311. package/lib/draft-stash.ts +102 -0
  312. package/lib/draft-store.ts +105 -0
  313. package/lib/dropdown-direction.ts +27 -0
  314. package/lib/extension-command.test.mjs +63 -0
  315. package/lib/extension-command.ts +81 -0
  316. package/lib/file-access.test.mjs +31 -0
  317. package/lib/file-access.ts +59 -0
  318. package/lib/file-dirent.test.mjs +56 -0
  319. package/lib/file-dirent.ts +17 -0
  320. package/lib/file-explorer-state.test.mjs +63 -0
  321. package/lib/file-explorer-state.ts +36 -0
  322. package/lib/file-fuzzy.test.mjs +29 -0
  323. package/lib/file-fuzzy.ts +189 -0
  324. package/lib/file-links.test.mjs +92 -0
  325. package/lib/file-links.ts +148 -0
  326. package/lib/file-paths.ts +43 -0
  327. package/lib/file-types.test.mjs +34 -0
  328. package/lib/file-types.ts +73 -0
  329. package/lib/file-upload.test.mjs +70 -0
  330. package/lib/file-upload.ts +59 -0
  331. package/lib/file-viewer-state.test.mjs +20 -0
  332. package/lib/file-viewer-state.ts +15 -0
  333. package/lib/frontmatter.test.mjs +55 -0
  334. package/lib/frontmatter.ts +87 -0
  335. package/lib/git-changes.test.mjs +52 -0
  336. package/lib/git-changes.ts +232 -0
  337. package/lib/git-status.ts +47 -0
  338. package/lib/git-types.ts +29 -0
  339. package/lib/http-dispatcher.test.mjs +83 -0
  340. package/lib/http-dispatcher.ts +86 -0
  341. package/lib/i18n/format.test.mjs +19 -0
  342. package/lib/i18n/format.ts +60 -0
  343. package/lib/i18n/messages/en.ts +662 -0
  344. package/lib/i18n/messages/zh-CN.ts +661 -0
  345. package/lib/i18n/registry.test.mjs +32 -0
  346. package/lib/i18n/registry.ts +43 -0
  347. package/lib/i18n/types.ts +15 -0
  348. package/lib/image-attachments.test.mjs +27 -0
  349. package/lib/image-attachments.ts +56 -0
  350. package/lib/initial-navigation.test.mjs +65 -0
  351. package/lib/initial-navigation.ts +15 -0
  352. package/lib/markdown.test.mjs +104 -0
  353. package/lib/markdown.ts +347 -0
  354. package/lib/message-display.test.mjs +130 -0
  355. package/lib/message-display.ts +47 -0
  356. package/lib/model-catalog.test.mjs +327 -0
  357. package/lib/model-catalog.ts +414 -0
  358. package/lib/model-discovery-auth.ts +58 -0
  359. package/lib/model-discovery.test.mjs +50 -0
  360. package/lib/model-discovery.ts +75 -0
  361. package/lib/model-id-preference.ts +46 -0
  362. package/lib/model-scope.test.mjs +193 -0
  363. package/lib/model-scope.ts +198 -0
  364. package/lib/models-cache.test.mjs +122 -0
  365. package/lib/models-cache.ts +90 -0
  366. package/lib/models-config-store.test.mjs +200 -0
  367. package/lib/models-config-store.ts +84 -0
  368. package/lib/next-config-esm.test.mjs +14 -0
  369. package/lib/next-config.test.mjs +13 -0
  370. package/lib/node-version.test.mjs +33 -0
  371. package/lib/normalize.test.mjs +54 -0
  372. package/lib/normalize.ts +63 -0
  373. package/lib/npx.ts +62 -0
  374. package/lib/panel-layout.test.mjs +56 -0
  375. package/lib/panel-layout.ts +52 -0
  376. package/lib/patch.test.mjs +110 -0
  377. package/lib/patch.ts +134 -0
  378. package/lib/path-security.ts +42 -0
  379. package/lib/paths.test.mjs +58 -0
  380. package/lib/paths.ts +71 -0
  381. package/lib/pi-types.ts +183 -0
  382. package/lib/pi-web-options.test.mjs +63 -0
  383. package/lib/process-lifecycle.test.mjs +83 -0
  384. package/lib/project-command-env.test.mjs +298 -0
  385. package/lib/project-command-env.ts +129 -0
  386. package/lib/project-groups.test.mjs +60 -0
  387. package/lib/project-groups.ts +53 -0
  388. package/lib/project-identity.test.mjs +37 -0
  389. package/lib/project-identity.ts +26 -0
  390. package/lib/project-tree.test.mjs +115 -0
  391. package/lib/project-tree.ts +176 -0
  392. package/lib/project-trust.test.mjs +117 -0
  393. package/lib/project-trust.ts +48 -0
  394. package/lib/prompt-recovery.test.mjs +33 -0
  395. package/lib/prompt-recovery.ts +47 -0
  396. package/lib/provider-api-key-route.test.mjs +12 -0
  397. package/lib/provider-credential-store.test.mjs +126 -0
  398. package/lib/provider-credential-store.ts +114 -0
  399. package/lib/provider-listing-runtime.ts +37 -0
  400. package/lib/provider-listing.test.mjs +114 -0
  401. package/lib/provider-listing.ts +118 -0
  402. package/lib/provider-services.ts +64 -0
  403. package/lib/request-security.test.mjs +256 -0
  404. package/lib/request-security.ts +136 -0
  405. package/lib/rpc-manager-shutdown.test.mjs +372 -0
  406. package/lib/rpc-manager-widgets.test.mjs +342 -0
  407. package/lib/rpc-manager.test.mjs +139 -0
  408. package/lib/rpc-manager.ts +1703 -0
  409. package/lib/rpc-session-info.test.mjs +93 -0
  410. package/lib/session-delete.test.mjs +53 -0
  411. package/lib/session-delete.ts +86 -0
  412. package/lib/session-file-references-core.ts +82 -0
  413. package/lib/session-file-references.test.mjs +91 -0
  414. package/lib/session-file-references.ts +29 -0
  415. package/lib/session-path.test.mjs +24 -0
  416. package/lib/session-path.ts +11 -0
  417. package/lib/session-reader.pagination.test.mjs +115 -0
  418. package/lib/session-reader.test.mjs +446 -0
  419. package/lib/session-reader.ts +548 -0
  420. package/lib/session-resolve.test.mjs +65 -0
  421. package/lib/session-row-context-menu.test.mjs +46 -0
  422. package/lib/session-row-context-menu.ts +29 -0
  423. package/lib/session-scanner.test.mjs +151 -0
  424. package/lib/session-scanner.ts +238 -0
  425. package/lib/session-search.test.mjs +173 -0
  426. package/lib/session-search.ts +284 -0
  427. package/lib/session-stats.ts +26 -0
  428. package/lib/session-timing.test.mjs +107 -0
  429. package/lib/session-timing.ts +45 -0
  430. package/lib/session-title.test.mjs +284 -0
  431. package/lib/session-title.ts +255 -0
  432. package/lib/skill-frontmatter.test.mjs +75 -0
  433. package/lib/skill-frontmatter.ts +40 -0
  434. package/lib/skill-lock.test.mjs +182 -0
  435. package/lib/skill-lock.ts +146 -0
  436. package/lib/skill-updates.test.mjs +182 -0
  437. package/lib/skill-updates.ts +263 -0
  438. package/lib/skills-service.ts +16 -0
  439. package/lib/slash-display.test.mjs +122 -0
  440. package/lib/slash-display.ts +30 -0
  441. package/lib/sqlite-db.test.mjs +140 -0
  442. package/lib/sqlite-db.ts +205 -0
  443. package/lib/startup-preferences.test.mjs +130 -0
  444. package/lib/startup-preferences.ts +65 -0
  445. package/lib/streaming-message.test.mjs +212 -0
  446. package/lib/streaming-message.ts +151 -0
  447. package/lib/subagent-widget.test.mjs +87 -0
  448. package/lib/subagent-widget.ts +109 -0
  449. package/lib/sync-room-server.mjs +137 -0
  450. package/lib/task-card-store.test.mjs +309 -0
  451. package/lib/task-card-store.ts +559 -0
  452. package/lib/task-scheduler.ts +498 -0
  453. package/lib/task-store.test.mjs +216 -0
  454. package/lib/task-store.ts +271 -0
  455. package/lib/terminal-input.test.mjs +46 -0
  456. package/lib/terminal-input.ts +63 -0
  457. package/lib/terminal-manager.test.mjs +71 -0
  458. package/lib/terminal-manager.ts +245 -0
  459. package/lib/tool-execution-progress.test.mjs +32 -0
  460. package/lib/tool-execution-progress.ts +29 -0
  461. package/lib/tool-names.ts +25 -0
  462. package/lib/tool-preset-preference.test.mjs +72 -0
  463. package/lib/tool-preset-preference.ts +41 -0
  464. package/lib/tool-presets.test.mjs +56 -0
  465. package/lib/tool-presets.ts +42 -0
  466. package/lib/turn-written-files.test.mjs +137 -0
  467. package/lib/turn-written-files.ts +60 -0
  468. package/lib/types.ts +349 -0
  469. package/lib/wallpaper-settings.ts +191 -0
  470. package/lib/web-auth.test.mjs +50 -0
  471. package/lib/web-auth.ts +45 -0
  472. package/lib/workspace-memory.test.mjs +129 -0
  473. package/lib/workspace-memory.ts +98 -0
  474. package/lib/worktree.test.mjs +50 -0
  475. package/lib/worktree.ts +250 -0
  476. package/package.json +3 -1
  477. package/server.mjs +34 -0
  478. package/.next/server/chunks/7513.js +0 -30
  479. package/.next/static/chunks/5315.03de3c6abd8b283e.js +0 -4
  480. package/.next/static/chunks/next/dist/client/components/builtin/global-error-00a4badaccb388e6.js +0 -1
  481. package/.next/static/css/cf25164cb7c04d95.css +0 -1
  482. /package/.next/static/{Ud6Y2ONkGuPVFbo1xZQvk → 7sDSz-wW-RWYnxh1l6-fD}/_buildManifest.js +0 -0
  483. /package/.next/static/{Ud6Y2ONkGuPVFbo1xZQvk → 7sDSz-wW-RWYnxh1l6-fD}/_ssgManifest.js +0 -0
@@ -0,0 +1,114 @@
1
+ import { chmodSync, existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
2
+ import { dirname, join } from "node:path";
3
+ import type { Credential } from "@earendil-works/pi-ai";
4
+ import { getAgentDir } from "@earendil-works/pi-coding-agent";
5
+ import lockfile from "proper-lockfile";
6
+ import type { ProviderCredentialType } from "@/lib/provider-listing";
7
+
8
+ const AUTH_FILE_WRITE_OPTIONS = { encoding: "utf-8" as const, mode: 0o600 };
9
+
10
+ export type CredentialRemovalResult =
11
+ | { status: "removed" }
12
+ | { status: "not_found" }
13
+ | { status: "type_mismatch"; storedType: string };
14
+
15
+ function ensureAuthFile(authPath: string): void {
16
+ const parent = dirname(authPath);
17
+ if (!existsSync(parent)) mkdirSync(parent, { recursive: true, mode: 0o700 });
18
+ if (!existsSync(authPath)) {
19
+ writeFileSync(authPath, "{}", AUTH_FILE_WRITE_OPTIONS);
20
+ chmodSync(authPath, 0o600);
21
+ }
22
+ }
23
+
24
+ function isRecord(value: unknown): value is Record<string, unknown> {
25
+ return typeof value === "object" && value !== null && !Array.isArray(value);
26
+ }
27
+
28
+ async function updateStoredCredentials<T>(
29
+ authPath: string,
30
+ update: (credentials: Record<string, unknown>) => { result: T; changed: boolean },
31
+ ): Promise<T> {
32
+ ensureAuthFile(authPath);
33
+
34
+ let lockCompromisedError: Error | undefined;
35
+ const release = await lockfile.lock(authPath, {
36
+ retries: {
37
+ retries: 10,
38
+ factor: 2,
39
+ minTimeout: 100,
40
+ maxTimeout: 10_000,
41
+ randomize: true,
42
+ },
43
+ stale: 30_000,
44
+ onCompromised: (error) => {
45
+ lockCompromisedError = error;
46
+ },
47
+ });
48
+
49
+ const throwIfCompromised = () => {
50
+ if (lockCompromisedError) throw lockCompromisedError;
51
+ };
52
+
53
+ try {
54
+ throwIfCompromised();
55
+ const parsed: unknown = JSON.parse(readFileSync(authPath, "utf-8"));
56
+ if (!isRecord(parsed)) throw new Error("Invalid auth.json: expected an object");
57
+
58
+ const { result, changed } = update(parsed);
59
+ if (changed) {
60
+ throwIfCompromised();
61
+ writeFileSync(authPath, JSON.stringify(parsed, null, 2), AUTH_FILE_WRITE_OPTIONS);
62
+ chmodSync(authPath, 0o600);
63
+ throwIfCompromised();
64
+ }
65
+ return result;
66
+ } finally {
67
+ try {
68
+ await release();
69
+ } catch {
70
+ // The compromised-lock error above is more useful than an unlock error.
71
+ }
72
+ }
73
+ }
74
+
75
+ /** Store a provider credential without triggering a model-catalog refresh. */
76
+ export function storeProviderCredential(
77
+ providerId: string,
78
+ credential: Credential,
79
+ authPath = join(getAgentDir(), "auth.json"),
80
+ ): Promise<void> {
81
+ return updateStoredCredentials(authPath, (credentials) => {
82
+ credentials[providerId] = credential;
83
+ return { result: undefined, changed: true };
84
+ });
85
+ }
86
+
87
+ /**
88
+ * Removes a provider credential only when its current stored type matches.
89
+ *
90
+ * The comparison and write share the same proper-lockfile lock used by pi's
91
+ * AuthStorage, so a concurrent login cannot be deleted by a stale UI request.
92
+ */
93
+ export async function removeStoredCredentialIfType(
94
+ providerId: string,
95
+ expectedType: ProviderCredentialType,
96
+ authPath = join(getAgentDir(), "auth.json"),
97
+ ): Promise<CredentialRemovalResult> {
98
+ return updateStoredCredentials<CredentialRemovalResult>(authPath, (credentials) => {
99
+ if (!Object.hasOwn(credentials, providerId)) {
100
+ return { result: { status: "not_found" }, changed: false };
101
+ }
102
+
103
+ const credential = credentials[providerId];
104
+ const storedType = isRecord(credential) && typeof credential.type === "string"
105
+ ? credential.type
106
+ : "unknown";
107
+ if (storedType !== expectedType) {
108
+ return { result: { status: "type_mismatch", storedType }, changed: false };
109
+ }
110
+
111
+ delete credentials[providerId];
112
+ return { result: { status: "removed" }, changed: true };
113
+ });
114
+ }
@@ -0,0 +1,37 @@
1
+ import type { ModelRuntime } from "@earendil-works/pi-coding-agent";
2
+ import type { ProviderCredentialType, ProviderListingInput } from "@/lib/provider-listing";
3
+
4
+ /**
5
+ * Adapter between `ModelRuntime` and the pure listing helpers in
6
+ * `lib/provider-listing.ts`.
7
+ */
8
+ export async function collectProviderListingInputs(
9
+ modelRuntime: ModelRuntime,
10
+ ): Promise<ProviderListingInput[]> {
11
+ const models = modelRuntime.getModels();
12
+
13
+ const credentialTypes = new Map<string, ProviderCredentialType>();
14
+ try {
15
+ for (const credential of await modelRuntime.listCredentials()) {
16
+ if (credential.type === "api_key" || credential.type === "oauth") {
17
+ credentialTypes.set(credential.providerId, credential.type);
18
+ }
19
+ }
20
+ } catch {
21
+ // A damaged auth.json must not empty the provider list; fall back to the
22
+ // per-provider auth status only.
23
+ }
24
+
25
+ return modelRuntime.getProviders().map((provider) => ({
26
+ id: provider.id,
27
+ name: provider.name,
28
+ hasApiKeyLogin: Boolean(provider.auth.apiKey?.login),
29
+ hasOAuth: Boolean(provider.auth.oauth),
30
+ ...(provider.auth.oauth?.name ? { oauthName: provider.auth.oauth.name } : {}),
31
+ status: modelRuntime.getProviderAuthStatus(provider.id),
32
+ ...(credentialTypes.has(provider.id)
33
+ ? { credentialType: credentialTypes.get(provider.id) }
34
+ : {}),
35
+ modelCount: models.filter((model) => model.provider === provider.id).length,
36
+ }));
37
+ }
@@ -0,0 +1,114 @@
1
+ import assert from "node:assert/strict";
2
+ import test from "node:test";
3
+
4
+ async function loadSubject() {
5
+ try {
6
+ const { createJiti } = await import("jiti");
7
+ return createJiti(import.meta.url).import("./provider-listing.ts");
8
+ } catch {
9
+ return import("./provider-listing.ts");
10
+ }
11
+ }
12
+
13
+ const { buildApiKeyProviderList, buildOAuthProviderList } = await loadSubject();
14
+
15
+ const provider = (overrides) => ({
16
+ id: "anthropic",
17
+ name: "Anthropic",
18
+ hasApiKeyLogin: true,
19
+ hasOAuth: true,
20
+ oauthName: "Anthropic (Claude Pro/Max)",
21
+ status: { configured: false },
22
+ modelCount: 12,
23
+ ...overrides,
24
+ });
25
+
26
+ test("lists an API-key-configured dual-auth provider in the API-key list (#309)", () => {
27
+ const providers = [provider({ status: { configured: true, source: "stored" }, credentialType: "api_key" })];
28
+
29
+ const apiKey = buildApiKeyProviderList(providers);
30
+ assert.deepEqual(apiKey, [{
31
+ id: "anthropic",
32
+ displayName: "Anthropic",
33
+ configured: true,
34
+ source: "stored",
35
+ modelCount: 12,
36
+ supportsOAuth: true,
37
+ }]);
38
+
39
+ // …and only as a not-logged-in entry in the OAuth list, so it renders once.
40
+ const oauth = buildOAuthProviderList(providers);
41
+ assert.equal(oauth.length, 1);
42
+ assert.equal(oauth[0].loggedIn, false);
43
+ assert.equal(oauth[0].name, "Anthropic (Claude Pro/Max)");
44
+ assert.equal(oauth[0].supportsApiKey, true);
45
+ });
46
+
47
+ test("an OAuth-authenticated provider counts as configured only in the OAuth list", () => {
48
+ const providers = [provider({ status: { configured: true, source: "stored" }, credentialType: "oauth" })];
49
+
50
+ assert.deepEqual(buildApiKeyProviderList(providers).map((p) => [p.id, p.configured, p.source]), [
51
+ ["anthropic", false, undefined],
52
+ ]);
53
+ assert.deepEqual(buildOAuthProviderList(providers).map((p) => [p.id, p.loggedIn]), [["anthropic", true]]);
54
+ });
55
+
56
+ test("switching auth method moves the provider between lists in both directions", () => {
57
+ // API key → OAuth: the API-key row goes inactive as the OAuth row lights up.
58
+ const afterOAuthLogin = [provider({ status: { configured: true, source: "stored" }, credentialType: "oauth" })];
59
+ assert.equal(buildApiKeyProviderList(afterOAuthLogin)[0].configured, false);
60
+ assert.equal(buildOAuthProviderList(afterOAuthLogin)[0].loggedIn, true);
61
+
62
+ // OAuth → API key: and back again.
63
+ const afterKeySaved = [provider({ status: { configured: true, source: "stored" }, credentialType: "api_key" })];
64
+ assert.equal(buildApiKeyProviderList(afterKeySaved)[0].configured, true);
65
+ assert.equal(buildOAuthProviderList(afterKeySaved)[0].loggedIn, false);
66
+ });
67
+
68
+ test("environment-provided keys are reported as configured", () => {
69
+ const providers = [provider({ id: "openai", name: "OpenAI", hasOAuth: false, oauthName: undefined, status: { configured: true, source: "environment" } })];
70
+
71
+ const apiKey = buildApiKeyProviderList(providers);
72
+ assert.equal(apiKey[0].configured, true);
73
+ assert.equal(apiKey[0].source, "environment");
74
+ assert.equal(apiKey[0].supportsOAuth, false);
75
+ assert.deepEqual(buildOAuthProviderList(providers), []);
76
+ });
77
+
78
+ test("custom models.json providers stay out of the API-key list", () => {
79
+ const providers = [
80
+ provider({ id: "acme-gateway", name: "acme-gateway", hasOAuth: false, oauthName: undefined, status: { configured: true, source: "models_json_key" } }),
81
+ provider({ id: "acme-cmd", name: "acme-cmd", hasOAuth: false, oauthName: undefined, status: { configured: true, source: "models_json_command" } }),
82
+ ];
83
+
84
+ assert.deepEqual(buildApiKeyProviderList(providers), []);
85
+ });
86
+
87
+ test("providers without an API-key login method are excluded", () => {
88
+ const providers = [provider({ id: "subscription-only", name: "Subscription Only", hasApiKeyLogin: false, oauthName: undefined })];
89
+
90
+ assert.deepEqual(buildApiKeyProviderList(providers), []);
91
+ assert.deepEqual(buildOAuthProviderList(providers).map((p) => p.name), ["Subscription Only"]);
92
+ });
93
+
94
+ test("keeps friendlier OAuth labels and falls back to the provider name", () => {
95
+ const providers = [
96
+ // openai-codex is OAuth-only in the pinned SDK — the label map still covers it.
97
+ provider({ id: "openai-codex", name: "OpenAI Codex", hasApiKeyLogin: false, oauthName: "Sign in with ChatGPT" }),
98
+ provider({ id: "github-copilot", name: "GitHub Copilot Provider", oauthName: "Copilot" }),
99
+ provider({ id: "xai", name: "xAI", oauthName: undefined }),
100
+ ];
101
+
102
+ assert.deepEqual(buildOAuthProviderList(providers).map((p) => p.name), [
103
+ "ChatGPT Plus/Pro",
104
+ "GitHub Copilot",
105
+ "xAI",
106
+ ]);
107
+ });
108
+
109
+ test("duplicate provider ids are collapsed", () => {
110
+ const providers = [provider({}), provider({ status: { configured: true, source: "stored" }, credentialType: "api_key" })];
111
+
112
+ assert.deepEqual(buildApiKeyProviderList(providers).map((p) => [p.id, p.configured]), [["anthropic", false]]);
113
+ assert.equal(buildOAuthProviderList(providers).length, 1);
114
+ });
@@ -0,0 +1,118 @@
1
+ /**
2
+ * Provider listing for the Models panel.
3
+ *
4
+ * Providers are listed by the auth methods they actually declare, not by a
5
+ * hardcoded id list. Anthropic and GitHub Copilot (and kimi-coding, openrouter,
6
+ * radius, xai) declare both an API key and OAuth; hardcoding them as "OAuth
7
+ * only" hid them from the API-key endpoint, while the OAuth endpoint separately
8
+ * excluded Anthropic, so a configured Anthropic account appeared in neither list
9
+ * (#309). Which providers are dual-auth is a property of the SDK's provider
10
+ * definitions and changes between releases — read it from `auth`, never assume
11
+ * it from an id.
12
+ */
13
+
14
+ /** Credential kinds pi stores in `auth.json`. */
15
+ export type ProviderCredentialType = "api_key" | "oauth";
16
+
17
+ /** Auth status sources that mean "this key comes from models.json". */
18
+ const CUSTOM_PROVIDER_SOURCES = new Set(["models_json_key", "models_json_command"]);
19
+
20
+ /** Friendlier names for OAuth entries whose provider name is not self-explanatory. */
21
+ const OAUTH_DISPLAY_NAMES: Record<string, string> = {
22
+ "openai-codex": "ChatGPT Plus/Pro",
23
+ "github-copilot": "GitHub Copilot",
24
+ };
25
+
26
+ export interface ProviderListingInput {
27
+ id: string;
28
+ /** Provider display name (`provider.name`). */
29
+ name: string;
30
+ /** True when the provider declares an API-key login method. */
31
+ hasApiKeyLogin: boolean;
32
+ /** True when the provider declares an OAuth login method. */
33
+ hasOAuth: boolean;
34
+ /** Label of the OAuth method, e.g. "Anthropic (Claude Pro/Max)". */
35
+ oauthName?: string;
36
+ /** Result of `ModelRuntime.getProviderAuthStatus()`. */
37
+ status: { configured: boolean; source?: string };
38
+ /** Type of the credential stored for this provider, when there is one. */
39
+ credentialType?: ProviderCredentialType;
40
+ modelCount: number;
41
+ }
42
+
43
+ export interface ApiKeyProviderListing {
44
+ id: string;
45
+ displayName: string;
46
+ configured: boolean;
47
+ source?: string;
48
+ modelCount: number;
49
+ /** True when the same provider can also be authenticated with OAuth. */
50
+ supportsOAuth: boolean;
51
+ }
52
+
53
+ export interface OAuthProviderListing {
54
+ id: string;
55
+ name: string;
56
+ usesCallbackServer: boolean;
57
+ loggedIn: boolean;
58
+ /** True when the same provider can also be authenticated with an API key. */
59
+ supportsApiKey: boolean;
60
+ }
61
+
62
+ function dedupeById(providers: readonly ProviderListingInput[]): ProviderListingInput[] {
63
+ const seen = new Set<string>();
64
+ const result: ProviderListingInput[] = [];
65
+ for (const provider of providers) {
66
+ if (seen.has(provider.id)) continue;
67
+ seen.add(provider.id);
68
+ result.push(provider);
69
+ }
70
+ return result;
71
+ }
72
+
73
+ /**
74
+ * Providers that can be authenticated with an API key.
75
+ *
76
+ * Custom `models.json` providers are excluded because the Models panel already
77
+ * renders them from `models.json` itself. A provider currently authenticated
78
+ * with an OAuth credential is reported as not configured here, so it is listed
79
+ * once — in the OAuth list — instead of twice.
80
+ */
81
+ export function buildApiKeyProviderList(
82
+ providers: readonly ProviderListingInput[],
83
+ ): ApiKeyProviderListing[] {
84
+ const result: ApiKeyProviderListing[] = [];
85
+ for (const provider of dedupeById(providers)) {
86
+ if (!provider.hasApiKeyLogin) continue;
87
+ if (provider.status.source && CUSTOM_PROVIDER_SOURCES.has(provider.status.source)) continue;
88
+
89
+ const configured = provider.status.configured && provider.credentialType !== "oauth";
90
+ result.push({
91
+ id: provider.id,
92
+ displayName: provider.name,
93
+ configured,
94
+ ...(configured && provider.status.source ? { source: provider.status.source } : {}),
95
+ modelCount: provider.modelCount,
96
+ supportsOAuth: provider.hasOAuth,
97
+ });
98
+ }
99
+ return result;
100
+ }
101
+
102
+ /** Providers that can be authenticated with OAuth. */
103
+ export function buildOAuthProviderList(
104
+ providers: readonly ProviderListingInput[],
105
+ ): OAuthProviderListing[] {
106
+ const result: OAuthProviderListing[] = [];
107
+ for (const provider of dedupeById(providers)) {
108
+ if (!provider.hasOAuth) continue;
109
+ result.push({
110
+ id: provider.id,
111
+ name: OAUTH_DISPLAY_NAMES[provider.id] ?? provider.oauthName ?? provider.name,
112
+ usesCallbackServer: false,
113
+ loggedIn: provider.credentialType === "oauth",
114
+ supportsApiKey: provider.hasApiKeyLogin,
115
+ });
116
+ }
117
+ return result;
118
+ }
@@ -0,0 +1,64 @@
1
+ import { stat } from "fs/promises";
2
+ import { resolve } from "path";
3
+ import {
4
+ createAgentSessionServices,
5
+ getAgentDir,
6
+ type AgentSessionServices,
7
+ } from "@earendil-works/pi-coding-agent";
8
+ import { getAllowedFileRoots, isExistingFilePathAllowed } from "@/lib/file-access";
9
+ import { projectTrustReloadOptions } from "@/lib/project-trust";
10
+
11
+ /** Error with an HTTP status, thrown by createProviderServices on bad input. */
12
+ export class ProviderServicesError extends Error {
13
+ constructor(
14
+ readonly status: number,
15
+ message: string,
16
+ ) {
17
+ super(message);
18
+ }
19
+ }
20
+
21
+ /**
22
+ * Create cwd-bound runtime services for the provider/auth listings.
23
+ *
24
+ * The Models panel must see exactly the providers the model selector sees.
25
+ * Extension-registered providers (e.g. `pi-commandcode-provider`'s
26
+ * "commandcode") are only loaded by `createAgentSessionServices()` — a bare
27
+ * `ModelRuntime.create()` skips the resource loader, so extension providers
28
+ * silently vanished from the auth lists while their models stayed visible.
29
+ *
30
+ * cwd is optional: when provided it must be an existing, allowed directory and
31
+ * the services honor its project-trust state (same as the model list). When
32
+ * omitted, services are created against the agent dir so globally-installed
33
+ * extensions still load while project extensions stay out.
34
+ */
35
+ export async function createProviderServices(
36
+ cwd?: string,
37
+ ): Promise<{ services: AgentSessionServices; cwd: string }> {
38
+ const agentDir = getAgentDir();
39
+ const resolvedCwd = cwd ? resolve(cwd) : agentDir;
40
+
41
+ if (cwd) {
42
+ try {
43
+ const cwdStat = await stat(resolvedCwd);
44
+ if (!cwdStat.isDirectory()) {
45
+ throw new ProviderServicesError(400, `Directory does not exist: ${resolvedCwd}`);
46
+ }
47
+ } catch (error) {
48
+ if (error instanceof ProviderServicesError) throw error;
49
+ throw new ProviderServicesError(400, `Directory does not exist: ${resolvedCwd}`);
50
+ }
51
+ const allowedRoots = await getAllowedFileRoots();
52
+ if (!isExistingFilePathAllowed(resolvedCwd, allowedRoots)) {
53
+ throw new ProviderServicesError(403, "Access denied");
54
+ }
55
+ }
56
+
57
+ const trustOptions = projectTrustReloadOptions(resolvedCwd, agentDir);
58
+ const services = await createAgentSessionServices({
59
+ cwd: resolvedCwd,
60
+ agentDir,
61
+ ...(trustOptions ? { resourceLoaderReloadOptions: trustOptions } : {}),
62
+ });
63
+ return { services, cwd: resolvedCwd };
64
+ }