@jetcrab/pi-desk 1.1.13-dev → 1.1.16-dev

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 (268) hide show
  1. package/package.json +2 -2
  2. package/src/app/api/model-settings/preview/route.ts +1 -0
  3. package/src/client/l2_biz/settings/hooks/l2-use-model-provider-settings.ts +54 -10
  4. package/src/client/l2_biz/settings/hooks/l2-use-model-settings.ts +37 -9
  5. package/src/client/l2_biz/settings/l2-model-settings-biz.ts +26 -12
  6. package/src/client/l2_biz/settings/views/l2-model-provider-settings.tsx +3 -2
  7. package/src/client/l2_biz/settings/views/l2-model-replacement-preview.tsx +34 -0
  8. package/src/client/l4_foundation/ui/l4-confirm-dialog.tsx +9 -1
  9. package/src/common/l2_biz/model-settings/l2-model-settings-contract.ts +23 -1
  10. package/src/common/l2_biz/settings/l2-settings-messages.ts +8 -0
  11. package/src/server/l1_entry/api/l1-model-settings-preview-route.ts +32 -0
  12. package/src/server/l2_biz/model-settings/l2-model-settings.ts +9 -0
  13. package/src/server/l2_biz/plugin-management/l2-plugin-management.ts +159 -52
  14. package/src/server/l4_foundation/model-settings/l4-model-settings-store.ts +161 -38
  15. package/src/server/l4_foundation/model-settings/l4-model-settings-types.ts +11 -0
  16. package/src/server/l4_foundation/model-settings/l4-model-settings.ts +1 -0
  17. package/src/server/l4_foundation/pi/l4-pi-package-maintenance-runner.mts +56 -65
  18. package/src/server/l4_foundation/pi/l4-pi-package-maintenance.ts +2 -0
  19. package/src/server/l4_foundation/pi/l4-pi-package-reinstall-runner.mts +49 -0
  20. package/src/server/l4_foundation/pi/l4-pi-package-root-consistency.ts +6 -2
  21. package/src/server/l4_foundation/pi/l4-pi-plugin-probe.ts +3 -1
  22. package/src/server/l4_foundation/pi/l4-pi-runner-process.ts +9 -0
  23. package/temp/build/pi-desk/release/.next/BUILD_ID +1 -1
  24. package/temp/build/pi-desk/release/.next/app-path-routes-manifest.json +1 -0
  25. package/temp/build/pi-desk/release/.next/build-manifest.json +2 -2
  26. package/temp/build/pi-desk/release/.next/required-server-files.js +2 -2
  27. package/temp/build/pi-desk/release/.next/required-server-files.json +2 -2
  28. package/temp/build/pi-desk/release/.next/routes-manifest.json +6 -0
  29. package/temp/build/pi-desk/release/.next/server/app/_global-error/page.js +1 -1
  30. package/temp/build/pi-desk/release/.next/server/app/_global-error/page_client-reference-manifest.js +1 -1
  31. package/temp/build/pi-desk/release/.next/server/app/_global-error.html +1 -1
  32. package/temp/build/pi-desk/release/.next/server/app/_global-error.rsc +1 -1
  33. package/temp/build/pi-desk/release/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  34. package/temp/build/pi-desk/release/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
  35. package/temp/build/pi-desk/release/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  36. package/temp/build/pi-desk/release/.next/server/app/_not-found/page.js +1 -1
  37. package/temp/build/pi-desk/release/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  38. package/temp/build/pi-desk/release/.next/server/app/api/auth/login/route.js +1 -1
  39. package/temp/build/pi-desk/release/.next/server/app/api/auth/logout/route.js +1 -1
  40. package/temp/build/pi-desk/release/.next/server/app/api/auth-settings/get/route.js +1 -1
  41. package/temp/build/pi-desk/release/.next/server/app/api/auth-settings/replace/route.js +1 -1
  42. package/temp/build/pi-desk/release/.next/server/app/api/capability-modes/get/route.js +1 -1
  43. package/temp/build/pi-desk/release/.next/server/app/api/capability-modes/replace/route.js +1 -1
  44. package/temp/build/pi-desk/release/.next/server/app/api/chat/context/get/route.js +1 -1
  45. package/temp/build/pi-desk/release/.next/server/app/api/chat/images/get/route.js +1 -1
  46. package/temp/build/pi-desk/release/.next/server/app/api/chat/messages/get/route.js +1 -1
  47. package/temp/build/pi-desk/release/.next/server/app/api/health/route.js +1 -1
  48. package/temp/build/pi-desk/release/.next/server/app/api/mcp-check/get/route.js +1 -1
  49. package/temp/build/pi-desk/release/.next/server/app/api/mcp-settings/del/route.js +1 -1
  50. package/temp/build/pi-desk/release/.next/server/app/api/mcp-settings/get/route.js +1 -1
  51. package/temp/build/pi-desk/release/.next/server/app/api/mcp-settings/replace/route.js +1 -1
  52. package/temp/build/pi-desk/release/.next/server/app/api/model-catalog/list/route.js +1 -1
  53. package/temp/build/pi-desk/release/.next/server/app/api/model-settings/get/route.js +1 -1
  54. package/temp/build/pi-desk/release/.next/server/app/api/model-settings/preview/route.js +1 -0
  55. package/temp/build/pi-desk/release/.next/server/app/api/model-settings/preview/route_client-reference-manifest.js +1 -0
  56. package/temp/build/pi-desk/release/.next/server/app/api/model-settings/replace/route.js +1 -1
  57. package/temp/build/pi-desk/release/.next/server/app/api/pi/directories/list/route.js +1 -1
  58. package/temp/build/pi-desk/release/.next/server/app/api/pi/directory-entries/list/route.js +1 -1
  59. package/temp/build/pi-desk/release/.next/server/app/api/pi/directory-ignores/list/route.js +1 -1
  60. package/temp/build/pi-desk/release/.next/server/app/api/pi/directory-ignores/replace/route.js +1 -1
  61. package/temp/build/pi-desk/release/.next/server/app/api/pi/models/list/route.js +1 -1
  62. package/temp/build/pi-desk/release/.next/server/app/api/pi/session-user-messages/list/route.js +1 -1
  63. package/temp/build/pi-desk/release/.next/server/app/api/pi/sessions/list/route.js +1 -1
  64. package/temp/build/pi-desk/release/.next/server/app/api/plugin-catalog/get/route.js +1 -1
  65. package/temp/build/pi-desk/release/.next/server/app/api/plugin-catalog/search/route.js +1 -1
  66. package/temp/build/pi-desk/release/.next/server/app/api/plugin-catalog/settings-get/route.js +1 -1
  67. package/temp/build/pi-desk/release/.next/server/app/api/plugin-catalog/settings-replace/route.js +1 -1
  68. package/temp/build/pi-desk/release/.next/server/app/api/plugins/add/route.js +1 -1
  69. package/temp/build/pi-desk/release/.next/server/app/api/plugins/apply/route.js +1 -1
  70. package/temp/build/pi-desk/release/.next/server/app/api/plugins/batch/route.js +1 -1
  71. package/temp/build/pi-desk/release/.next/server/app/api/plugins/browser-resources/[resourceGroup]/[...resourcePath]/route.js +1 -1
  72. package/temp/build/pi-desk/release/.next/server/app/api/plugins/del/route.js +1 -1
  73. package/temp/build/pi-desk/release/.next/server/app/api/plugins/enabled-set/route.js +1 -1
  74. package/temp/build/pi-desk/release/.next/server/app/api/plugins/get/route.js +1 -1
  75. package/temp/build/pi-desk/release/.next/server/app/api/plugins/host-runtime/[runtimeVersion]/[...resourcePath]/route.js +1 -1
  76. package/temp/build/pi-desk/release/.next/server/app/api/plugins/list/route.js +1 -1
  77. package/temp/build/pi-desk/release/.next/server/app/api/plugins/reload/route.js +1 -1
  78. package/temp/build/pi-desk/release/.next/server/app/api/plugins/update/route.js +1 -1
  79. package/temp/build/pi-desk/release/.next/server/app/api/project-files/get/route.js +1 -1
  80. package/temp/build/pi-desk/release/.next/server/app/api/project-files/list/route.js +1 -1
  81. package/temp/build/pi-desk/release/.next/server/app/api/project-files/reveal/route.js +1 -1
  82. package/temp/build/pi-desk/release/.next/server/app/api/project-files/search/route.js +1 -1
  83. package/temp/build/pi-desk/release/.next/server/app/api/project-git/get/route.js +1 -1
  84. package/temp/build/pi-desk/release/.next/server/app/api/project-git-branches/list/route.js +1 -1
  85. package/temp/build/pi-desk/release/.next/server/app/api/project-git-changes/list/route.js +1 -1
  86. package/temp/build/pi-desk/release/.next/server/app/api/project-git-diff/get/route.js +1 -1
  87. package/temp/build/pi-desk/release/.next/server/app/api/project-git-heads/get/route.js +1 -1
  88. package/temp/build/pi-desk/release/.next/server/app/api/project-git-log/list/route.js +1 -1
  89. package/temp/build/pi-desk/release/.next/server/app/api/project-model-default/get/route.js +1 -1
  90. package/temp/build/pi-desk/release/.next/server/app/api/project-model-default/replace/route.js +1 -1
  91. package/temp/build/pi-desk/release/.next/server/app/api/skill-files/get/route.js +1 -1
  92. package/temp/build/pi-desk/release/.next/server/app/api/skill-files/list/route.js +1 -1
  93. package/temp/build/pi-desk/release/.next/server/app/api/skill-files/replace/route.js +1 -1
  94. package/temp/build/pi-desk/release/.next/server/app/api/skills/list/route.js +1 -1
  95. package/temp/build/pi-desk/release/.next/server/app/api/task-center/images/get/route.js +1 -1
  96. package/temp/build/pi-desk/release/.next/server/app/api/task-center/messages/get/route.js +1 -1
  97. package/temp/build/pi-desk/release/.next/server/app/api/work-session-git-branch/add/route.js +1 -1
  98. package/temp/build/pi-desk/release/.next/server/app/api/work-session-git-branch/replace/route.js +1 -1
  99. package/temp/build/pi-desk/release/.next/server/app/api/work-session-tree/get/route.js +1 -1
  100. package/temp/build/pi-desk/release/.next/server/app/api/work-session-tree-entry/get/route.js +1 -1
  101. package/temp/build/pi-desk/release/.next/server/app/api/work-sessions/add/route.js +1 -1
  102. package/temp/build/pi-desk/release/.next/server/app/api/work-sessions/branch/route.js +1 -1
  103. package/temp/build/pi-desk/release/.next/server/app/api/work-sessions/del/route.js +1 -1
  104. package/temp/build/pi-desk/release/.next/server/app/api/work-sessions/rename/route.js +1 -1
  105. package/temp/build/pi-desk/release/.next/server/app/api/work-sessions/replace/route.js +1 -1
  106. package/temp/build/pi-desk/release/.next/server/app/api/work-sessions/sort/route.js +1 -1
  107. package/temp/build/pi-desk/release/.next/server/app/favicon.ico/route.js +1 -1
  108. package/temp/build/pi-desk/release/.next/server/app/login/page.js +1 -1
  109. package/temp/build/pi-desk/release/.next/server/app/login/page_client-reference-manifest.js +1 -1
  110. package/temp/build/pi-desk/release/.next/server/app/page.js +16 -16
  111. package/temp/build/pi-desk/release/.next/server/app/page_client-reference-manifest.js +1 -1
  112. package/temp/build/pi-desk/release/.next/server/app-paths-manifest.json +1 -0
  113. package/temp/build/pi-desk/release/.next/server/chunks/35985.js +1 -1
  114. package/temp/build/pi-desk/release/.next/server/chunks/50520.js +5 -5
  115. package/temp/build/pi-desk/release/.next/server/chunks/78262.js +3 -3
  116. package/temp/build/pi-desk/release/.next/server/chunks/82499.js +4 -4
  117. package/temp/build/pi-desk/release/.next/server/middleware-build-manifest.js +1 -1
  118. package/temp/build/pi-desk/release/.next/server/pages/500.html +1 -1
  119. package/temp/build/pi-desk/release/.next/static/chunks/app/{api/auth-settings/get/route-5137efe26947191b.js → _global-error/page-9969fe0baa238786.js} +1 -1
  120. package/temp/build/pi-desk/release/.next/static/chunks/app/{api/auth-settings/replace/route-5137efe26947191b.js → _not-found/page-9969fe0baa238786.js} +1 -1
  121. package/temp/build/pi-desk/release/.next/static/chunks/app/api/auth/login/route-9969fe0baa238786.js +1 -0
  122. package/temp/build/pi-desk/release/.next/static/chunks/app/api/auth/logout/route-9969fe0baa238786.js +1 -0
  123. package/temp/build/pi-desk/release/.next/static/chunks/app/{_global-error/page-5137efe26947191b.js → api/auth-settings/get/route-9969fe0baa238786.js} +1 -1
  124. package/temp/build/pi-desk/release/.next/static/chunks/app/{_not-found/page-5137efe26947191b.js → api/auth-settings/replace/route-9969fe0baa238786.js} +1 -1
  125. package/temp/build/pi-desk/release/.next/static/chunks/app/api/capability-modes/get/route-9969fe0baa238786.js +1 -0
  126. package/temp/build/pi-desk/release/.next/static/chunks/app/api/capability-modes/replace/route-9969fe0baa238786.js +1 -0
  127. package/temp/build/pi-desk/release/.next/static/chunks/app/api/chat/context/get/route-9969fe0baa238786.js +1 -0
  128. package/temp/build/pi-desk/release/.next/static/chunks/app/api/chat/images/get/route-9969fe0baa238786.js +1 -0
  129. package/temp/build/pi-desk/release/.next/static/chunks/app/api/chat/messages/get/route-9969fe0baa238786.js +1 -0
  130. package/temp/build/pi-desk/release/.next/static/chunks/app/api/health/route-9969fe0baa238786.js +1 -0
  131. package/temp/build/pi-desk/release/.next/static/chunks/app/api/mcp-check/get/route-9969fe0baa238786.js +1 -0
  132. package/temp/build/pi-desk/release/.next/static/chunks/app/api/mcp-settings/del/route-9969fe0baa238786.js +1 -0
  133. package/temp/build/pi-desk/release/.next/static/chunks/app/api/mcp-settings/get/route-9969fe0baa238786.js +1 -0
  134. package/temp/build/pi-desk/release/.next/static/chunks/app/api/mcp-settings/replace/route-9969fe0baa238786.js +1 -0
  135. package/temp/build/pi-desk/release/.next/static/chunks/app/api/model-catalog/list/route-9969fe0baa238786.js +1 -0
  136. package/temp/build/pi-desk/release/.next/static/chunks/app/api/model-settings/get/route-9969fe0baa238786.js +1 -0
  137. package/temp/build/pi-desk/release/.next/static/chunks/app/api/model-settings/preview/route-9969fe0baa238786.js +1 -0
  138. package/temp/build/pi-desk/release/.next/static/chunks/app/api/model-settings/replace/route-9969fe0baa238786.js +1 -0
  139. package/temp/build/pi-desk/release/.next/static/chunks/app/api/pi/directories/list/route-9969fe0baa238786.js +1 -0
  140. package/temp/build/pi-desk/release/.next/static/chunks/app/api/pi/directory-entries/list/route-9969fe0baa238786.js +1 -0
  141. package/temp/build/pi-desk/release/.next/static/chunks/app/api/pi/directory-ignores/list/route-9969fe0baa238786.js +1 -0
  142. package/temp/build/pi-desk/release/.next/static/chunks/app/api/pi/directory-ignores/replace/route-9969fe0baa238786.js +1 -0
  143. package/temp/build/pi-desk/release/.next/static/chunks/app/api/pi/models/list/route-9969fe0baa238786.js +1 -0
  144. package/temp/build/pi-desk/release/.next/static/chunks/app/api/pi/session-user-messages/list/route-9969fe0baa238786.js +1 -0
  145. package/temp/build/pi-desk/release/.next/static/chunks/app/api/pi/sessions/list/route-9969fe0baa238786.js +1 -0
  146. package/temp/build/pi-desk/release/.next/static/chunks/app/api/plugin-catalog/get/route-9969fe0baa238786.js +1 -0
  147. package/temp/build/pi-desk/release/.next/static/chunks/app/api/plugin-catalog/search/route-9969fe0baa238786.js +1 -0
  148. package/temp/build/pi-desk/release/.next/static/chunks/app/api/plugin-catalog/settings-get/route-9969fe0baa238786.js +1 -0
  149. package/temp/build/pi-desk/release/.next/static/chunks/app/api/plugin-catalog/settings-replace/route-9969fe0baa238786.js +1 -0
  150. package/temp/build/pi-desk/release/.next/static/chunks/app/api/plugins/add/route-9969fe0baa238786.js +1 -0
  151. package/temp/build/pi-desk/release/.next/static/chunks/app/api/plugins/apply/route-9969fe0baa238786.js +1 -0
  152. package/temp/build/pi-desk/release/.next/static/chunks/app/api/plugins/batch/route-9969fe0baa238786.js +1 -0
  153. package/temp/build/pi-desk/release/.next/static/chunks/app/api/plugins/browser-resources/[resourceGroup]/[...resourcePath]/route-9969fe0baa238786.js +1 -0
  154. package/temp/build/pi-desk/release/.next/static/chunks/app/api/plugins/del/route-9969fe0baa238786.js +1 -0
  155. package/temp/build/pi-desk/release/.next/static/chunks/app/api/plugins/enabled-set/route-9969fe0baa238786.js +1 -0
  156. package/temp/build/pi-desk/release/.next/static/chunks/app/api/plugins/get/route-9969fe0baa238786.js +1 -0
  157. package/temp/build/pi-desk/release/.next/static/chunks/app/api/plugins/host-runtime/[runtimeVersion]/[...resourcePath]/route-9969fe0baa238786.js +1 -0
  158. package/temp/build/pi-desk/release/.next/static/chunks/app/api/plugins/list/route-9969fe0baa238786.js +1 -0
  159. package/temp/build/pi-desk/release/.next/static/chunks/app/api/plugins/reload/route-9969fe0baa238786.js +1 -0
  160. package/temp/build/pi-desk/release/.next/static/chunks/app/api/plugins/update/route-9969fe0baa238786.js +1 -0
  161. package/temp/build/pi-desk/release/.next/static/chunks/app/api/project-files/get/route-9969fe0baa238786.js +1 -0
  162. package/temp/build/pi-desk/release/.next/static/chunks/app/api/project-files/list/route-9969fe0baa238786.js +1 -0
  163. package/temp/build/pi-desk/release/.next/static/chunks/app/api/project-files/reveal/route-9969fe0baa238786.js +1 -0
  164. package/temp/build/pi-desk/release/.next/static/chunks/app/api/project-files/search/route-9969fe0baa238786.js +1 -0
  165. package/temp/build/pi-desk/release/.next/static/chunks/app/api/project-git/get/route-9969fe0baa238786.js +1 -0
  166. package/temp/build/pi-desk/release/.next/static/chunks/app/api/project-git-branches/list/route-9969fe0baa238786.js +1 -0
  167. package/temp/build/pi-desk/release/.next/static/chunks/app/api/project-git-changes/list/route-9969fe0baa238786.js +1 -0
  168. package/temp/build/pi-desk/release/.next/static/chunks/app/api/project-git-diff/get/route-9969fe0baa238786.js +1 -0
  169. package/temp/build/pi-desk/release/.next/static/chunks/app/api/project-git-heads/get/route-9969fe0baa238786.js +1 -0
  170. package/temp/build/pi-desk/release/.next/static/chunks/app/api/project-git-log/list/route-9969fe0baa238786.js +1 -0
  171. package/temp/build/pi-desk/release/.next/static/chunks/app/api/project-model-default/get/route-9969fe0baa238786.js +1 -0
  172. package/temp/build/pi-desk/release/.next/static/chunks/app/api/project-model-default/replace/route-9969fe0baa238786.js +1 -0
  173. package/temp/build/pi-desk/release/.next/static/chunks/app/api/skill-files/get/route-9969fe0baa238786.js +1 -0
  174. package/temp/build/pi-desk/release/.next/static/chunks/app/api/skill-files/list/route-9969fe0baa238786.js +1 -0
  175. package/temp/build/pi-desk/release/.next/static/chunks/app/api/skill-files/replace/route-9969fe0baa238786.js +1 -0
  176. package/temp/build/pi-desk/release/.next/static/chunks/app/api/skills/list/route-9969fe0baa238786.js +1 -0
  177. package/temp/build/pi-desk/release/.next/static/chunks/app/api/task-center/images/get/route-9969fe0baa238786.js +1 -0
  178. package/temp/build/pi-desk/release/.next/static/chunks/app/api/task-center/messages/get/route-9969fe0baa238786.js +1 -0
  179. package/temp/build/pi-desk/release/.next/static/chunks/app/api/work-session-git-branch/add/route-9969fe0baa238786.js +1 -0
  180. package/temp/build/pi-desk/release/.next/static/chunks/app/api/work-session-git-branch/replace/route-9969fe0baa238786.js +1 -0
  181. package/temp/build/pi-desk/release/.next/static/chunks/app/api/work-session-tree/get/route-9969fe0baa238786.js +1 -0
  182. package/temp/build/pi-desk/release/.next/static/chunks/app/api/work-session-tree-entry/get/route-9969fe0baa238786.js +1 -0
  183. package/temp/build/pi-desk/release/.next/static/chunks/app/api/work-sessions/add/route-9969fe0baa238786.js +1 -0
  184. package/temp/build/pi-desk/release/.next/static/chunks/app/api/work-sessions/branch/route-9969fe0baa238786.js +1 -0
  185. package/temp/build/pi-desk/release/.next/static/chunks/app/api/work-sessions/del/route-9969fe0baa238786.js +1 -0
  186. package/temp/build/pi-desk/release/.next/static/chunks/app/api/work-sessions/rename/route-9969fe0baa238786.js +1 -0
  187. package/temp/build/pi-desk/release/.next/static/chunks/app/api/work-sessions/replace/route-9969fe0baa238786.js +1 -0
  188. package/temp/build/pi-desk/release/.next/static/chunks/app/api/work-sessions/sort/route-9969fe0baa238786.js +1 -0
  189. package/temp/build/pi-desk/release/.next/static/chunks/app/{layout-358978b7cfe69a5f.js → layout-c0e1c5cef8620cf8.js} +2 -2
  190. package/temp/build/pi-desk/release/.next/static/chunks/app/page-df6045b319f52088.js +158 -0
  191. package/temp/build/pi-desk/release/.next/static/chunks/next/dist/client/components/builtin/app-error-9969fe0baa238786.js +1 -0
  192. package/temp/build/pi-desk/release/.next/static/chunks/next/dist/client/components/builtin/forbidden-9969fe0baa238786.js +1 -0
  193. package/temp/build/pi-desk/release/.next/static/chunks/next/dist/client/components/builtin/unauthorized-9969fe0baa238786.js +1 -0
  194. package/temp/build/pi-desk/release/.next/static/css/6427d02b6edf406f.css +1 -0
  195. package/temp/build/pi-desk/release/.next/static/uj2IMtGMwxhDXhTVi7sw5/_buildManifest.js +1 -0
  196. package/temp/build/pi-desk/release/.next/trace-build +1 -1
  197. package/temp/build/pi-desk/release/.next/static/YdOQ7klRBI5tJb5NpDVLW/_buildManifest.js +0 -1
  198. package/temp/build/pi-desk/release/.next/static/chunks/app/api/auth/login/route-5137efe26947191b.js +0 -1
  199. package/temp/build/pi-desk/release/.next/static/chunks/app/api/auth/logout/route-5137efe26947191b.js +0 -1
  200. package/temp/build/pi-desk/release/.next/static/chunks/app/api/capability-modes/get/route-5137efe26947191b.js +0 -1
  201. package/temp/build/pi-desk/release/.next/static/chunks/app/api/capability-modes/replace/route-5137efe26947191b.js +0 -1
  202. package/temp/build/pi-desk/release/.next/static/chunks/app/api/chat/context/get/route-5137efe26947191b.js +0 -1
  203. package/temp/build/pi-desk/release/.next/static/chunks/app/api/chat/images/get/route-5137efe26947191b.js +0 -1
  204. package/temp/build/pi-desk/release/.next/static/chunks/app/api/chat/messages/get/route-5137efe26947191b.js +0 -1
  205. package/temp/build/pi-desk/release/.next/static/chunks/app/api/health/route-5137efe26947191b.js +0 -1
  206. package/temp/build/pi-desk/release/.next/static/chunks/app/api/mcp-check/get/route-5137efe26947191b.js +0 -1
  207. package/temp/build/pi-desk/release/.next/static/chunks/app/api/mcp-settings/del/route-5137efe26947191b.js +0 -1
  208. package/temp/build/pi-desk/release/.next/static/chunks/app/api/mcp-settings/get/route-5137efe26947191b.js +0 -1
  209. package/temp/build/pi-desk/release/.next/static/chunks/app/api/mcp-settings/replace/route-5137efe26947191b.js +0 -1
  210. package/temp/build/pi-desk/release/.next/static/chunks/app/api/model-catalog/list/route-5137efe26947191b.js +0 -1
  211. package/temp/build/pi-desk/release/.next/static/chunks/app/api/model-settings/get/route-5137efe26947191b.js +0 -1
  212. package/temp/build/pi-desk/release/.next/static/chunks/app/api/model-settings/replace/route-5137efe26947191b.js +0 -1
  213. package/temp/build/pi-desk/release/.next/static/chunks/app/api/pi/directories/list/route-5137efe26947191b.js +0 -1
  214. package/temp/build/pi-desk/release/.next/static/chunks/app/api/pi/directory-entries/list/route-5137efe26947191b.js +0 -1
  215. package/temp/build/pi-desk/release/.next/static/chunks/app/api/pi/directory-ignores/list/route-5137efe26947191b.js +0 -1
  216. package/temp/build/pi-desk/release/.next/static/chunks/app/api/pi/directory-ignores/replace/route-5137efe26947191b.js +0 -1
  217. package/temp/build/pi-desk/release/.next/static/chunks/app/api/pi/models/list/route-5137efe26947191b.js +0 -1
  218. package/temp/build/pi-desk/release/.next/static/chunks/app/api/pi/session-user-messages/list/route-5137efe26947191b.js +0 -1
  219. package/temp/build/pi-desk/release/.next/static/chunks/app/api/pi/sessions/list/route-5137efe26947191b.js +0 -1
  220. package/temp/build/pi-desk/release/.next/static/chunks/app/api/plugin-catalog/get/route-5137efe26947191b.js +0 -1
  221. package/temp/build/pi-desk/release/.next/static/chunks/app/api/plugin-catalog/search/route-5137efe26947191b.js +0 -1
  222. package/temp/build/pi-desk/release/.next/static/chunks/app/api/plugin-catalog/settings-get/route-5137efe26947191b.js +0 -1
  223. package/temp/build/pi-desk/release/.next/static/chunks/app/api/plugin-catalog/settings-replace/route-5137efe26947191b.js +0 -1
  224. package/temp/build/pi-desk/release/.next/static/chunks/app/api/plugins/add/route-5137efe26947191b.js +0 -1
  225. package/temp/build/pi-desk/release/.next/static/chunks/app/api/plugins/apply/route-5137efe26947191b.js +0 -1
  226. package/temp/build/pi-desk/release/.next/static/chunks/app/api/plugins/batch/route-5137efe26947191b.js +0 -1
  227. package/temp/build/pi-desk/release/.next/static/chunks/app/api/plugins/browser-resources/[resourceGroup]/[...resourcePath]/route-5137efe26947191b.js +0 -1
  228. package/temp/build/pi-desk/release/.next/static/chunks/app/api/plugins/del/route-5137efe26947191b.js +0 -1
  229. package/temp/build/pi-desk/release/.next/static/chunks/app/api/plugins/enabled-set/route-5137efe26947191b.js +0 -1
  230. package/temp/build/pi-desk/release/.next/static/chunks/app/api/plugins/get/route-5137efe26947191b.js +0 -1
  231. package/temp/build/pi-desk/release/.next/static/chunks/app/api/plugins/host-runtime/[runtimeVersion]/[...resourcePath]/route-5137efe26947191b.js +0 -1
  232. package/temp/build/pi-desk/release/.next/static/chunks/app/api/plugins/list/route-5137efe26947191b.js +0 -1
  233. package/temp/build/pi-desk/release/.next/static/chunks/app/api/plugins/reload/route-5137efe26947191b.js +0 -1
  234. package/temp/build/pi-desk/release/.next/static/chunks/app/api/plugins/update/route-5137efe26947191b.js +0 -1
  235. package/temp/build/pi-desk/release/.next/static/chunks/app/api/project-files/get/route-5137efe26947191b.js +0 -1
  236. package/temp/build/pi-desk/release/.next/static/chunks/app/api/project-files/list/route-5137efe26947191b.js +0 -1
  237. package/temp/build/pi-desk/release/.next/static/chunks/app/api/project-files/reveal/route-5137efe26947191b.js +0 -1
  238. package/temp/build/pi-desk/release/.next/static/chunks/app/api/project-files/search/route-5137efe26947191b.js +0 -1
  239. package/temp/build/pi-desk/release/.next/static/chunks/app/api/project-git/get/route-5137efe26947191b.js +0 -1
  240. package/temp/build/pi-desk/release/.next/static/chunks/app/api/project-git-branches/list/route-5137efe26947191b.js +0 -1
  241. package/temp/build/pi-desk/release/.next/static/chunks/app/api/project-git-changes/list/route-5137efe26947191b.js +0 -1
  242. package/temp/build/pi-desk/release/.next/static/chunks/app/api/project-git-diff/get/route-5137efe26947191b.js +0 -1
  243. package/temp/build/pi-desk/release/.next/static/chunks/app/api/project-git-heads/get/route-5137efe26947191b.js +0 -1
  244. package/temp/build/pi-desk/release/.next/static/chunks/app/api/project-git-log/list/route-5137efe26947191b.js +0 -1
  245. package/temp/build/pi-desk/release/.next/static/chunks/app/api/project-model-default/get/route-5137efe26947191b.js +0 -1
  246. package/temp/build/pi-desk/release/.next/static/chunks/app/api/project-model-default/replace/route-5137efe26947191b.js +0 -1
  247. package/temp/build/pi-desk/release/.next/static/chunks/app/api/skill-files/get/route-5137efe26947191b.js +0 -1
  248. package/temp/build/pi-desk/release/.next/static/chunks/app/api/skill-files/list/route-5137efe26947191b.js +0 -1
  249. package/temp/build/pi-desk/release/.next/static/chunks/app/api/skill-files/replace/route-5137efe26947191b.js +0 -1
  250. package/temp/build/pi-desk/release/.next/static/chunks/app/api/skills/list/route-5137efe26947191b.js +0 -1
  251. package/temp/build/pi-desk/release/.next/static/chunks/app/api/task-center/images/get/route-5137efe26947191b.js +0 -1
  252. package/temp/build/pi-desk/release/.next/static/chunks/app/api/task-center/messages/get/route-5137efe26947191b.js +0 -1
  253. package/temp/build/pi-desk/release/.next/static/chunks/app/api/work-session-git-branch/add/route-5137efe26947191b.js +0 -1
  254. package/temp/build/pi-desk/release/.next/static/chunks/app/api/work-session-git-branch/replace/route-5137efe26947191b.js +0 -1
  255. package/temp/build/pi-desk/release/.next/static/chunks/app/api/work-session-tree/get/route-5137efe26947191b.js +0 -1
  256. package/temp/build/pi-desk/release/.next/static/chunks/app/api/work-session-tree-entry/get/route-5137efe26947191b.js +0 -1
  257. package/temp/build/pi-desk/release/.next/static/chunks/app/api/work-sessions/add/route-5137efe26947191b.js +0 -1
  258. package/temp/build/pi-desk/release/.next/static/chunks/app/api/work-sessions/branch/route-5137efe26947191b.js +0 -1
  259. package/temp/build/pi-desk/release/.next/static/chunks/app/api/work-sessions/del/route-5137efe26947191b.js +0 -1
  260. package/temp/build/pi-desk/release/.next/static/chunks/app/api/work-sessions/rename/route-5137efe26947191b.js +0 -1
  261. package/temp/build/pi-desk/release/.next/static/chunks/app/api/work-sessions/replace/route-5137efe26947191b.js +0 -1
  262. package/temp/build/pi-desk/release/.next/static/chunks/app/api/work-sessions/sort/route-5137efe26947191b.js +0 -1
  263. package/temp/build/pi-desk/release/.next/static/chunks/app/page-cc05e71bf51adb57.js +0 -158
  264. package/temp/build/pi-desk/release/.next/static/chunks/next/dist/client/components/builtin/app-error-5137efe26947191b.js +0 -1
  265. package/temp/build/pi-desk/release/.next/static/chunks/next/dist/client/components/builtin/forbidden-5137efe26947191b.js +0 -1
  266. package/temp/build/pi-desk/release/.next/static/chunks/next/dist/client/components/builtin/unauthorized-5137efe26947191b.js +0 -1
  267. package/temp/build/pi-desk/release/.next/static/css/095a062e18f96657.css +0 -1
  268. /package/temp/build/pi-desk/release/.next/static/{YdOQ7klRBI5tJb5NpDVLW → uj2IMtGMwxhDXhTVi7sw5}/_ssgManifest.js +0 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jetcrab/pi-desk",
3
- "version": "1.1.13-dev",
3
+ "version": "1.1.16-dev",
4
4
  "license": "Apache-2.0",
5
5
  "repository": {
6
6
  "type": "git",
@@ -69,7 +69,7 @@
69
69
  "viewerjs": "^1.11.8",
70
70
  "ws": "^8.21.3",
71
71
  "zod": "^4.4.3",
72
- "@jetcrab/pi-desk-sdk": "^1.1.4-dev"
72
+ "@jetcrab/pi-desk-sdk": "^1.1.6-dev"
73
73
  },
74
74
  "devDependencies": {
75
75
  "@earendil-works/pi-agent-core": "1.2.0",
@@ -0,0 +1 @@
1
+ export { POST } from '@server/l1_entry/api/l1-model-settings-preview-route'
@@ -1,7 +1,8 @@
1
1
  import { useEffect, useRef, useState } from 'react'
2
2
  import type {
3
3
  L2ModelConfig,
4
- L2ModelProviderConfig
4
+ L2ModelProviderConfig,
5
+ L2ModelReplacement
5
6
  } from '@common/l2_biz/model-settings/l2-model-settings-contract'
6
7
 
7
8
  export interface ModelInputState {
@@ -45,7 +46,7 @@ function emptyModel(): L2ModelConfig {
45
46
 
46
47
  export function useL2ModelProviderSettings(
47
48
  providers: L2ModelProviderConfig[],
48
- onChange: (providers: L2ModelProviderConfig[]) => void,
49
+ onChange: (providers: L2ModelProviderConfig[], replacements: L2ModelReplacement[]) => void,
49
50
  onInputState: (state: ModelInputState) => void,
50
51
  resetVersion: number
51
52
  ): {
@@ -79,6 +80,16 @@ export function useL2ModelProviderSettings(
79
80
  )
80
81
  )
81
82
  const nextKey = useRef(0)
83
+ const originalModels = useRef(
84
+ new Map(
85
+ providers.flatMap((provider, providerIndex) =>
86
+ provider.models.map((model, modelIndex) => [
87
+ `${providerIndex}:${modelIndex}`,
88
+ { provider: provider.provider, modelId: model.modelId }
89
+ ])
90
+ )
91
+ )
92
+ )
82
93
  const [inputStates, setInputStates] = useState<Record<string, ModelInputState>>({})
83
94
  const [serviceIndex, setServiceIndex] = useState<number | null>(null)
84
95
  const [newService, setNewService] = useState(false)
@@ -91,6 +102,14 @@ export function useL2ModelProviderSettings(
91
102
  useEffect(() => {
92
103
  queueMicrotask(() => {
93
104
  const current = latestProviders.current
105
+ originalModels.current = new Map(
106
+ current.flatMap((provider, providerIndex) =>
107
+ provider.models.map((model, modelIndex) => [
108
+ `${providerIndex}:${modelIndex}`,
109
+ { provider: provider.provider, modelId: model.modelId }
110
+ ])
111
+ )
112
+ )
94
113
  setInputStates({})
95
114
  setModelKeys(
96
115
  current.map((provider, providerIndex) =>
@@ -120,8 +139,22 @@ export function useL2ModelProviderSettings(
120
139
  )
121
140
  }
122
141
 
142
+ function changeProviders(next: L2ModelProviderConfig[], keys = modelKeys): void {
143
+ const replacements: L2ModelReplacement[] = []
144
+ next.forEach((provider, providerIndex) => {
145
+ provider.models.forEach((model, modelIndex) => {
146
+ const from = originalModels.current.get(keys[providerIndex][modelIndex])
147
+ const to = { provider: provider.provider.trim(), modelId: model.modelId.trim() }
148
+ if (from && (from.provider !== to.provider || from.modelId !== to.modelId)) {
149
+ replacements.push({ from, to })
150
+ }
151
+ })
152
+ })
153
+ onChange(next, replacements)
154
+ }
155
+
123
156
  function updateModel(providerIndex: number, modelIndex: number, model: L2ModelConfig): void {
124
- onChange(
157
+ changeProviders(
125
158
  providers.map((provider, index) =>
126
159
  index === providerIndex
127
160
  ? {
@@ -155,10 +188,12 @@ export function useL2ModelProviderSettings(
155
188
  closeService,
156
189
  applyService: (provider) => {
157
190
  if (newService) {
158
- onChange([...providers, provider])
191
+ changeProviders([...providers, provider], [...modelKeys, []])
159
192
  setModelKeys((keys) => [...keys, []])
160
193
  } else {
161
- onChange(providers.map((current, index) => (index === serviceIndex ? provider : current)))
194
+ changeProviders(
195
+ providers.map((current, index) => (index === serviceIndex ? provider : current))
196
+ )
162
197
  }
163
198
  closeService()
164
199
  },
@@ -166,7 +201,10 @@ export function useL2ModelProviderSettings(
166
201
  if (serviceIndex === null) return
167
202
  removeInputKeys(modelKeys[serviceIndex])
168
203
  const nextProviders = providers.filter((_, index) => index !== serviceIndex)
169
- onChange(nextProviders)
204
+ changeProviders(
205
+ nextProviders,
206
+ modelKeys.filter((_, index) => index !== serviceIndex)
207
+ )
170
208
  setModelKeys((keys) => keys.filter((_, index) => index !== serviceIndex))
171
209
  setSelection((current) => {
172
210
  if (!current || current.providerIndex === serviceIndex) {
@@ -182,14 +220,15 @@ export function useL2ModelProviderSettings(
182
220
  },
183
221
  addModel: (providerIndex) => {
184
222
  const provider = providers[providerIndex]
185
- onChange(
223
+ const key = `new-${++nextKey.current}`
224
+ changeProviders(
186
225
  providers.map((current, index) =>
187
226
  index === providerIndex
188
227
  ? { ...current, models: [...current.models, emptyModel()] }
189
228
  : current
190
- )
229
+ ),
230
+ modelKeys.map((current, index) => (index === providerIndex ? [...current, key] : current))
191
231
  )
192
- const key = `new-${++nextKey.current}`
193
232
  setModelKeys((keys) =>
194
233
  keys.map((current, index) => (index === providerIndex ? [...current, key] : current))
195
234
  )
@@ -213,7 +252,12 @@ export function useL2ModelProviderSettings(
213
252
  ? { ...provider, models: provider.models.filter((_, index) => index !== modelIndex) }
214
253
  : provider
215
254
  )
216
- onChange(nextProviders)
255
+ changeProviders(
256
+ nextProviders,
257
+ modelKeys.map((current, index) =>
258
+ index === providerIndex ? current.filter((_, index) => index !== modelIndex) : current
259
+ )
260
+ )
217
261
  setModelKeys((keys) =>
218
262
  keys.map((current, index) =>
219
263
  index === providerIndex ? current.filter((_, index) => index !== modelIndex) : current
@@ -1,16 +1,18 @@
1
- import { useCallback, useEffect, useMemo, useRef, useState } from 'react'
1
+ import { createElement, useCallback, useEffect, useMemo, useRef, useState } from 'react'
2
2
  import { useTranslation } from 'react-i18next'
3
3
  import type { BrowserBeforeLeaveHandler } from '@jetcrab/pi-desk-sdk/browser'
4
4
  import type {
5
5
  L2AccountModelSelection,
6
6
  L2ModelPreset,
7
7
  L2ModelProviderConfig,
8
+ L2ModelReplacement,
8
9
  L2ModelSettingsGetResponse,
9
10
  L2ModelSettingsReplaceRequest
10
11
  } from '@common/l2_biz/model-settings/l2-model-settings-contract'
11
12
  import { useL4AppSocket } from '@client/l4_foundation/realtime/app-socket/l4-app-socket'
12
13
  import { useL4AppToast } from '@client/l4_foundation/ui/l4-app-toast'
13
14
  import { useL4ConfirmDialog } from '@client/l4_foundation/ui/l4-confirm-dialog'
15
+ import { L2ModelReplacementPreview } from '../views/l2-model-replacement-preview'
14
16
  import { createL2ModelSettingsBiz, type L2ModelSettingsBiz } from '../l2-model-settings-biz'
15
17
  import {
16
18
  readL2ModelSettingsTab,
@@ -53,7 +55,7 @@ interface ModelSettingsState {
53
55
  setServiceDirty: (dirty: boolean) => void
54
56
  dialog: React.JSX.Element | null
55
57
  setProjectDefaultDirty: (dirty: boolean) => void
56
- changeProviders: (providers: L2ModelProviderConfig[]) => void
58
+ changeProviders: (providers: L2ModelProviderConfig[], replacements: L2ModelReplacement[]) => void
57
59
  changePresets: (presets: L2ModelPreset[]) => void
58
60
  selectTab: (tab: ModelSettingsTab) => void
59
61
  retry: () => void
@@ -95,6 +97,7 @@ export function useL2ModelSettings(
95
97
  const [nativeText, setNativeText] = useState('')
96
98
  const [nativeError, setNativeError] = useState<string | null>(null)
97
99
  const savingRef = useRef(false)
100
+ const modelReplacements = useRef<L2ModelReplacement[]>([])
98
101
  const nativeBaseline = settings ? JSON.stringify(settings.nativeConfig, null, 2) : ''
99
102
  const nativeDirty = nativeOpen && nativeText !== nativeBaseline
100
103
  const formDirty = providersDirty || presetsDirty || accountModelsDirty || inputState.dirty
@@ -127,6 +130,7 @@ export function useL2ModelSettings(
127
130
  }, [dirty])
128
131
 
129
132
  const applySettings = useCallback((result: L2ModelSettingsGetResponse): void => {
133
+ modelReplacements.current = []
130
134
  setSettings(result)
131
135
  setProviders(structuredClone(result.providers))
132
136
  setPresets(structuredClone(result.presets))
@@ -218,7 +222,35 @@ export function useL2ModelSettings(
218
222
  return !originalModels.has(key) || currentModels.has(key)
219
223
  })
220
224
  const removedPresetCount = presets.length - nextPresets.length
221
- if (
225
+ const input: L2ModelSettingsReplaceRequest = {
226
+ ...(providersDirty || inputState.dirty ? { providers } : {}),
227
+ ...(accountModelsDirty ? { accountModels } : {}),
228
+ ...(presetsDirty || removedPresetCount > 0 ? { presets: nextPresets } : {}),
229
+ ...(modelReplacements.current.length
230
+ ? { modelReplacements: modelReplacements.current }
231
+ : {})
232
+ }
233
+ const { projectDefaults } = modelReplacements.current.length
234
+ ? await biz.previewSettings(input)
235
+ : { projectDefaults: [] }
236
+ if (projectDefaults.length > 0) {
237
+ const description = [
238
+ t('replaceProjectDefaultsDescription'),
239
+ ...(removedPresetCount > 0
240
+ ? [t('removeInvalidPresets', { count: removedPresetCount })]
241
+ : [])
242
+ ].join(' ')
243
+ if (
244
+ !(await confirm({
245
+ title: t('replaceProjectDefaultsTitle'),
246
+ description,
247
+ content: createElement(L2ModelReplacementPreview, { projectDefaults }),
248
+ confirmLabel: t('confirmAndSaveModels'),
249
+ confirmVariant: 'default'
250
+ }))
251
+ )
252
+ return
253
+ } else if (
222
254
  removedPresetCount > 0 &&
223
255
  !(await confirm({
224
256
  title: t('removeInvalidPresetsTitle'),
@@ -229,11 +261,6 @@ export function useL2ModelSettings(
229
261
  return
230
262
  }
231
263
 
232
- const input: L2ModelSettingsReplaceRequest = {
233
- ...(providersDirty || inputState.dirty ? { providers } : {}),
234
- ...(accountModelsDirty ? { accountModels } : {}),
235
- ...(presetsDirty || removedPresetCount > 0 ? { presets: nextPresets } : {})
236
- }
237
264
  await biz.replaceSettings(input)
238
265
  applySettings(await biz.getSettings())
239
266
  setProvidersDirty(false)
@@ -375,7 +402,8 @@ export function useL2ModelSettings(
375
402
  setServiceDirty,
376
403
  dialog,
377
404
  setProjectDefaultDirty,
378
- changeProviders: (nextProviders) => {
405
+ changeProviders: (nextProviders, replacements) => {
406
+ modelReplacements.current = replacements
379
407
  setProviders(nextProviders)
380
408
  setProvidersDirty(JSON.stringify(nextProviders) !== JSON.stringify(settings?.providers))
381
409
  },
@@ -4,6 +4,7 @@ import {
4
4
  L2ModelCatalogListRequestSchema,
5
5
  L2ModelCatalogListResponseSchema,
6
6
  L2ModelSettingsGetResponseSchema,
7
+ L2ModelSettingsPreviewResponseSchema,
7
8
  L2ModelSettingsReplaceRequestSchema,
8
9
  L2ModelSettingsReplaceResponseSchema,
9
10
  L2ProjectModelDefaultGetResponseSchema,
@@ -12,6 +13,7 @@ import {
12
13
  type L2ModelCatalogListRequest,
13
14
  type L2ModelCatalogListResponse,
14
15
  type L2ModelSettingsGetResponse,
16
+ type L2ModelSettingsPreviewResponse,
15
17
  type L2ModelSettingsReplaceRequest,
16
18
  type L2ProjectModelDefaultGetResponse,
17
19
  type L2ProjectModelDefaultReplaceRequest
@@ -25,6 +27,7 @@ import { saveL2ProjectModelDefaultCache } from './l2-model-settings-cache'
25
27
  export interface L2ModelSettingsBiz {
26
28
  parseNativeConfig: (text: string) => L2ModelNativeConfig
27
29
  getSettings: () => Promise<L2ModelSettingsGetResponse>
30
+ previewSettings: (input: L2ModelSettingsReplaceRequest) => Promise<L2ModelSettingsPreviewResponse>
28
31
  replaceSettings: (input: L2ModelSettingsReplaceRequest) => Promise<void>
29
32
  listCatalog: (input: L2ModelCatalogListRequest) => Promise<L2ModelCatalogListResponse>
30
33
  listDirectories: () => Promise<z.infer<typeof L2PiDirectoryListResponseSchema>>
@@ -32,6 +35,21 @@ export interface L2ModelSettingsBiz {
32
35
  replaceProjectDefault: (input: L2ProjectModelDefaultReplaceRequest) => Promise<void>
33
36
  }
34
37
 
38
+ function parseSettingsInput(input: L2ModelSettingsReplaceRequest): L2ModelSettingsReplaceRequest {
39
+ const parsed = L2ModelSettingsReplaceRequestSchema.safeParse(input)
40
+ if (!parsed.success) {
41
+ const field = parsed.error.issues[0]?.path.at(-1)
42
+ if (field === 'modelId') {
43
+ throw new Error('请填写模型 ID 后再保存配置。')
44
+ }
45
+ if (field === 'provider') {
46
+ throw new Error('请填写 Provider ID 后再保存配置。')
47
+ }
48
+ throw new Error('模型配置不完整或格式不正确,请检查必填字段和数值。')
49
+ }
50
+ return parsed.data
51
+ }
52
+
35
53
  export function createL2ModelSettingsBiz(clientId: string): L2ModelSettingsBiz {
36
54
  let settingsRequest: Promise<L2ModelSettingsGetResponse> | null = null
37
55
  return {
@@ -52,22 +70,18 @@ export function createL2ModelSettingsBiz(clientId: string): L2ModelSettingsBiz {
52
70
  }
53
71
  return settingsRequest
54
72
  },
73
+ previewSettings: (input) =>
74
+ request(
75
+ clientId,
76
+ '/api/model-settings/preview',
77
+ parseSettingsInput(input),
78
+ L2ModelSettingsPreviewResponseSchema
79
+ ),
55
80
  async replaceSettings(input): Promise<void> {
56
- const parsed = L2ModelSettingsReplaceRequestSchema.safeParse(input)
57
- if (!parsed.success) {
58
- const field = parsed.error.issues[0]?.path.at(-1)
59
- if (field === 'modelId') {
60
- throw new Error('请填写模型 ID 后再保存配置。')
61
- }
62
- if (field === 'provider') {
63
- throw new Error('请填写 Provider ID 后再保存配置。')
64
- }
65
- throw new Error('模型配置不完整或格式不正确,请检查必填字段和数值。')
66
- }
67
81
  await request(
68
82
  clientId,
69
83
  '/api/model-settings/replace',
70
- parsed.data,
84
+ parseSettingsInput(input),
71
85
  L2ModelSettingsReplaceResponseSchema
72
86
  )
73
87
  },
@@ -16,7 +16,8 @@ import type {
16
16
  L2ModelSettingsGetResponse,
17
17
  L2ModelOption,
18
18
  L2ModelPreset,
19
- L2ModelProviderConfig
19
+ L2ModelProviderConfig,
20
+ L2ModelReplacement
20
21
  } from '@common/l2_biz/model-settings/l2-model-settings-contract'
21
22
  import { Button } from '@client/l4_foundation/ui/shadcn/button'
22
23
  import { Input } from '@client/l4_foundation/ui/shadcn/input'
@@ -61,7 +62,7 @@ interface Props {
61
62
  savedProviders: L2ModelProviderConfig[]
62
63
  presets: L2ModelPreset[]
63
64
  resetVersion: number
64
- onChange: (providers: L2ModelProviderConfig[]) => void
65
+ onChange: (providers: L2ModelProviderConfig[], replacements: L2ModelReplacement[]) => void
65
66
  onPresetsChange: (presets: L2ModelPreset[]) => void
66
67
  onInputState: (state: ModelInputState) => void
67
68
  onServiceDirtyChange: (dirty: boolean) => void
@@ -0,0 +1,34 @@
1
+ import { useTranslation } from 'react-i18next'
2
+ import type { L2ModelSettingsPreviewResponse } from '@common/l2_biz/model-settings/l2-model-settings-contract'
3
+
4
+ const THINKING_LABELS = {
5
+ off: 'thinkingOff',
6
+ minimal: 'thinkingMinimal',
7
+ low: 'thinkingLow',
8
+ medium: 'thinkingMedium',
9
+ high: 'thinkingHigh',
10
+ xhigh: 'thinkingXhigh',
11
+ max: 'thinkingMax'
12
+ } as const
13
+
14
+ export function L2ModelReplacementPreview({
15
+ projectDefaults
16
+ }: L2ModelSettingsPreviewResponse): React.JSX.Element {
17
+ const { t } = useTranslation('settings')
18
+ return (
19
+ <ul className="space-y-3 px-4 pb-4">
20
+ {projectDefaults.map(({ cwd, from, to }) => (
21
+ <li key={cwd} className="space-y-1">
22
+ <p className="break-all font-medium">{cwd}</p>
23
+ <p className="break-all text-muted-foreground">
24
+ {from.provider}/{from.modelId} · {t(THINKING_LABELS[from.thinkingLevel])}
25
+ <span className="mx-1.5">→</span>
26
+ <span className="text-foreground">
27
+ {to.provider}/{to.modelId} · {t(THINKING_LABELS[to.thinkingLevel])}
28
+ </span>
29
+ </p>
30
+ </li>
31
+ ))}
32
+ </ul>
33
+ )
34
+ }
@@ -4,6 +4,7 @@ import { useEffect, useRef, useState } from 'react'
4
4
  import { useTranslation } from 'react-i18next'
5
5
  import {
6
6
  L4AppDialogContent,
7
+ L4AppDialogBody,
7
8
  L4AppDialogDescription,
8
9
  L4AppDialogFooter,
9
10
  L4AppDialogHeader,
@@ -15,7 +16,9 @@ import { Button } from './shadcn/button'
15
16
  interface ConfirmOptions {
16
17
  title: string
17
18
  description: string
19
+ content?: React.ReactNode
18
20
  confirmLabel?: string
21
+ confirmVariant?: 'default' | 'destructive'
19
22
  }
20
23
 
21
24
  export function useL4ConfirmDialog(): {
@@ -67,11 +70,16 @@ export function useL4ConfirmDialog(): {
67
70
  {options.description}
68
71
  </L4AppDialogDescription>
69
72
  </L4AppDialogHeader>
73
+ {options.content ? (
74
+ <L4AppDialogBody className="grid grid-rows-[minmax(0,1fr)]">
75
+ {options.content}
76
+ </L4AppDialogBody>
77
+ ) : null}
70
78
  <L4AppDialogFooter>
71
79
  <Button ref={cancelRef} variant="outline" onClick={() => settle(false)}>
72
80
  {t('cancel')}
73
81
  </Button>
74
- <Button variant="destructive" onClick={() => settle(true)}>
82
+ <Button variant={options.confirmVariant ?? 'destructive'} onClick={() => settle(true)}>
75
83
  {options.confirmLabel ?? t('delete')}
76
84
  </Button>
77
85
  </L4AppDialogFooter>
@@ -184,11 +184,20 @@ export const L2ModelSettingsGetResponseSchema = z
184
184
  })
185
185
  .strict()
186
186
 
187
+ const L2ModelIdentitySchema = z
188
+ .object({ provider: L2ModelProviderIdSchema, modelId: L2ModelIdSchema })
189
+ .strict()
190
+
191
+ export const L2ModelReplacementSchema = z
192
+ .object({ from: L2ModelIdentitySchema, to: L2ModelIdentitySchema })
193
+ .strict()
194
+
187
195
  const L2ModelSettingsReplaceFormSchema = z
188
196
  .object({
189
197
  providers: z.array(L2ModelProviderConfigSchema).optional(),
190
198
  presets: z.array(L2ModelPresetSchema).optional(),
191
- accountModels: z.array(L2AccountModelSelectionSchema).optional()
199
+ accountModels: z.array(L2AccountModelSelectionSchema).optional(),
200
+ modelReplacements: z.array(L2ModelReplacementSchema).optional()
192
201
  })
193
202
  .strict()
194
203
  .refine(
@@ -208,6 +217,17 @@ export const L2ModelSettingsReplaceRequestSchema = z.union([
208
217
 
209
218
  export const L2ModelSettingsReplaceResponseSchema = z.object({}).strict()
210
219
 
220
+ export const L2ModelSettingsPreviewRequestSchema = L2ModelSettingsReplaceRequestSchema
221
+ export const L2ModelSettingsPreviewResponseSchema = z
222
+ .object({
223
+ projectDefaults: z.array(
224
+ z
225
+ .object({ cwd: L3PiCwdSchema, from: L2ModelSelectionSchema, to: L2ModelSelectionSchema })
226
+ .strict()
227
+ )
228
+ })
229
+ .strict()
230
+
211
231
  export const L2ModelCatalogListRequestSchema = z
212
232
  .object({
213
233
  query: z.string().trim().max(256),
@@ -299,6 +319,8 @@ export type L2ModelPreset = z.infer<typeof L2ModelPresetSchema>
299
319
  export type L2ModelOption = z.infer<typeof L2ModelOptionSchema>
300
320
  export type L2ModelSelection = z.infer<typeof L2ModelSelectionSchema>
301
321
  export type L2ModelSettingsGetResponse = z.infer<typeof L2ModelSettingsGetResponseSchema>
322
+ export type L2ModelReplacement = z.infer<typeof L2ModelReplacementSchema>
323
+ export type L2ModelSettingsPreviewResponse = z.infer<typeof L2ModelSettingsPreviewResponseSchema>
302
324
  export type L2ModelSettingsReplaceRequest = z.infer<typeof L2ModelSettingsReplaceRequestSchema>
303
325
  export type L2ModelCatalogListRequest = z.infer<typeof L2ModelCatalogListRequestSchema>
304
326
  export type L2ModelCatalogSource = z.infer<typeof L2ModelCatalogSourceSchema>
@@ -108,6 +108,10 @@ export const L2_SETTINGS_MESSAGES = {
108
108
  removeInvalidPresets_other:
109
109
  '{{count}} common combinations refer to removed models. Saving will remove them.',
110
110
  removeAndSave: 'Remove and save',
111
+ replaceProjectDefaultsTitle: 'Update project default models',
112
+ replaceProjectDefaultsDescription:
113
+ 'The following projects will use the new model settings. Confirm to save all changes together.',
114
+ confirmAndSaveModels: 'Confirm and save',
111
115
  modelSaved: 'Model configuration saved.',
112
116
  modelSaveFailed: 'Could not save the model configuration',
113
117
  catalogUpdatedAt: 'Reference catalog updated at {{time}}',
@@ -400,6 +404,10 @@ export const L2_SETTINGS_MESSAGES = {
400
404
  removeInvalidPresets_other:
401
405
  '有 {{count}} 个常用组合引用了已删除模型,保存配置将同时删除这些组合。',
402
406
  removeAndSave: '删除并保存',
407
+ replaceProjectDefaultsTitle: '同步项目默认模型',
408
+ replaceProjectDefaultsDescription:
409
+ '以下项目的默认模型将替换为本次设置的新模型,确认后一起保存。',
410
+ confirmAndSaveModels: '确认并保存',
403
411
  modelSaved: '模型配置已保存。',
404
412
  modelSaveFailed: '模型配置保存失败',
405
413
  catalogUpdatedAt: '参考目录更新于 {{time}}',
@@ -0,0 +1,32 @@
1
+ import 'server-only'
2
+
3
+ import { L2ModelSettingsPreviewRequestSchema } from '@common/l2_biz/model-settings/l2-model-settings-contract'
4
+ import { previewL2ModelSettings } from '@server/l2_biz/model-settings/l2-model-settings'
5
+ import {
6
+ createL4ApiErrorResponse,
7
+ createL4ApiSuccessResponse
8
+ } from '@server/l4_foundation/http/l4-api-response'
9
+
10
+ export async function POST(request: Request): Promise<Response> {
11
+ let payload: unknown
12
+ try {
13
+ payload = await request.json()
14
+ } catch {
15
+ return createL4ApiErrorResponse(400, '请求 JSON 格式无效', { key: 'errors:invalidJson' })
16
+ }
17
+ const input = L2ModelSettingsPreviewRequestSchema.safeParse(payload)
18
+ if (!input.success)
19
+ return createL4ApiErrorResponse(400, '模型配置格式无效', { key: 'errors:modelSettingsInvalid' })
20
+
21
+ try {
22
+ return createL4ApiSuccessResponse(await previewL2ModelSettings(input.data))
23
+ } catch (error) {
24
+ console.error('[Pi Desk][ModelSettingsPreviewApi] 预览模型配置失败', {
25
+ message: error instanceof Error ? error.message : String(error)
26
+ })
27
+ return createL4ApiErrorResponse(
28
+ 400,
29
+ error instanceof Error && error.message ? error.message : '预览模型配置失败'
30
+ )
31
+ }
32
+ }
@@ -3,10 +3,12 @@ import 'server-only'
3
3
  import {
4
4
  L2ModelCatalogListResponseSchema,
5
5
  L2ModelSettingsGetResponseSchema,
6
+ L2ModelSettingsPreviewResponseSchema,
6
7
  L2ProjectModelDefaultGetResponseSchema,
7
8
  type L2ModelCatalogListRequest,
8
9
  type L2ModelCatalogListResponse,
9
10
  type L2ModelSettingsGetResponse,
11
+ type L2ModelSettingsPreviewResponse,
10
12
  type L2ModelSettingsReplaceRequest,
11
13
  type L2ProjectModelDefaultGetResponse,
12
14
  type L2ProjectModelDefaultReplaceRequest
@@ -15,6 +17,7 @@ import {
15
17
  listL4ModelCatalog,
16
18
  readL4ModelSettings,
17
19
  readL4ProjectModelDefault,
20
+ previewL4ModelSettings,
18
21
  replaceL4ModelSettings,
19
22
  replaceL4ProjectModelDefault
20
23
  } from '@server/l4_foundation/model-settings/l4-model-settings'
@@ -23,6 +26,12 @@ export async function getL2ModelSettings(): Promise<L2ModelSettingsGetResponse>
23
26
  return L2ModelSettingsGetResponseSchema.parse(await readL4ModelSettings())
24
27
  }
25
28
 
29
+ export async function previewL2ModelSettings(
30
+ input: L2ModelSettingsReplaceRequest
31
+ ): Promise<L2ModelSettingsPreviewResponse> {
32
+ return L2ModelSettingsPreviewResponseSchema.parse(await previewL4ModelSettings(input))
33
+ }
34
+
26
35
  export async function replaceL2ModelSettings(input: L2ModelSettingsReplaceRequest): Promise<void> {
27
36
  await replaceL4ModelSettings(input)
28
37
  }