@ai-development-environment/server 0.0.29 → 0.0.31

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 (357) hide show
  1. package/package.json +1 -1
  2. package/prisma-runtime/prisma/migrations/20260719010000_add_build_reports/migration.sql +29 -0
  3. package/prisma-runtime/prisma/schema.prisma +23 -0
  4. package/standalone/.next/BUILD_ID +1 -1
  5. package/standalone/.next/app-path-routes-manifest.json +3 -0
  6. package/standalone/.next/build-manifest.json +6 -6
  7. package/standalone/.next/prerender-manifest.json +3 -3
  8. package/standalone/.next/routes-manifest.json +26 -0
  9. package/standalone/.next/server/app/[locale]/actions/page/build-manifest.json +3 -3
  10. package/standalone/.next/server/app/[locale]/actions/page.js.nft.json +1 -1
  11. package/standalone/.next/server/app/[locale]/actions/page_client-reference-manifest.js +1 -1
  12. package/standalone/.next/server/app/[locale]/agents/[id]/page/build-manifest.json +3 -3
  13. package/standalone/.next/server/app/[locale]/agents/[id]/page.js.nft.json +1 -1
  14. package/standalone/.next/server/app/[locale]/agents/[id]/page_client-reference-manifest.js +1 -1
  15. package/standalone/.next/server/app/[locale]/agents/page/build-manifest.json +3 -3
  16. package/standalone/.next/server/app/[locale]/agents/page.js.nft.json +1 -1
  17. package/standalone/.next/server/app/[locale]/agents/page_client-reference-manifest.js +1 -1
  18. package/standalone/.next/server/app/[locale]/build-data/page/build-manifest.json +3 -3
  19. package/standalone/.next/server/app/[locale]/build-data/page.js.nft.json +1 -1
  20. package/standalone/.next/server/app/[locale]/build-data/page_client-reference-manifest.js +1 -1
  21. package/standalone/.next/server/app/[locale]/builds/[buildId]/coverage/page/app-paths-manifest.json +3 -0
  22. package/standalone/.next/server/app/[locale]/builds/[buildId]/coverage/page/build-manifest.json +16 -0
  23. package/standalone/.next/server/app/[locale]/builds/[buildId]/coverage/page/next-font-manifest.json +11 -0
  24. package/standalone/.next/server/app/[locale]/builds/[buildId]/coverage/page/react-loadable-manifest.json +1 -0
  25. package/standalone/.next/server/app/[locale]/builds/[buildId]/coverage/page/server-reference-manifest.json +4 -0
  26. package/standalone/.next/server/app/[locale]/builds/[buildId]/coverage/page.js +16 -0
  27. package/standalone/.next/server/app/[locale]/builds/[buildId]/coverage/page.js.map +5 -0
  28. package/standalone/.next/server/app/[locale]/builds/[buildId]/coverage/page.js.nft.json +1 -0
  29. package/standalone/.next/server/app/[locale]/builds/[buildId]/coverage/page_client-reference-manifest.js +3 -0
  30. package/standalone/.next/server/app/[locale]/builds/[buildId]/page/build-manifest.json +3 -3
  31. package/standalone/.next/server/app/[locale]/builds/[buildId]/page.js.nft.json +1 -1
  32. package/standalone/.next/server/app/[locale]/builds/[buildId]/page_client-reference-manifest.js +1 -1
  33. package/standalone/.next/server/app/[locale]/builds/page/build-manifest.json +3 -3
  34. package/standalone/.next/server/app/[locale]/builds/page.js.nft.json +1 -1
  35. package/standalone/.next/server/app/[locale]/builds/page_client-reference-manifest.js +1 -1
  36. package/standalone/.next/server/app/[locale]/codebases/[codebaseId]/page/build-manifest.json +3 -3
  37. package/standalone/.next/server/app/[locale]/codebases/[codebaseId]/page.js.nft.json +1 -1
  38. package/standalone/.next/server/app/[locale]/codebases/[codebaseId]/page_client-reference-manifest.js +1 -1
  39. package/standalone/.next/server/app/[locale]/codebases/page/build-manifest.json +3 -3
  40. package/standalone/.next/server/app/[locale]/codebases/page.js.nft.json +1 -1
  41. package/standalone/.next/server/app/[locale]/codebases/page_client-reference-manifest.js +1 -1
  42. package/standalone/.next/server/app/[locale]/codebases/repositories/[repositoryId]/page/build-manifest.json +3 -3
  43. package/standalone/.next/server/app/[locale]/codebases/repositories/[repositoryId]/page.js.nft.json +1 -1
  44. package/standalone/.next/server/app/[locale]/codebases/repositories/[repositoryId]/page_client-reference-manifest.js +1 -1
  45. package/standalone/.next/server/app/[locale]/comments/page/build-manifest.json +3 -3
  46. package/standalone/.next/server/app/[locale]/comments/page.js.nft.json +1 -1
  47. package/standalone/.next/server/app/[locale]/comments/page_client-reference-manifest.js +1 -1
  48. package/standalone/.next/server/app/[locale]/jira/cache/page/build-manifest.json +3 -3
  49. package/standalone/.next/server/app/[locale]/jira/cache/page.js.nft.json +1 -1
  50. package/standalone/.next/server/app/[locale]/jira/cache/page_client-reference-manifest.js +1 -1
  51. package/standalone/.next/server/app/[locale]/jira/cache/tickets/[issueKey]/page/build-manifest.json +3 -3
  52. package/standalone/.next/server/app/[locale]/jira/cache/tickets/[issueKey]/page.js.nft.json +1 -1
  53. package/standalone/.next/server/app/[locale]/jira/cache/tickets/[issueKey]/page_client-reference-manifest.js +1 -1
  54. package/standalone/.next/server/app/[locale]/jira/tickets/[issueKey]/page/build-manifest.json +3 -3
  55. package/standalone/.next/server/app/[locale]/jira/tickets/[issueKey]/page.js.nft.json +1 -1
  56. package/standalone/.next/server/app/[locale]/jira/tickets/[issueKey]/page_client-reference-manifest.js +1 -1
  57. package/standalone/.next/server/app/[locale]/jira/tickets/page/build-manifest.json +3 -3
  58. package/standalone/.next/server/app/[locale]/jira/tickets/page.js.nft.json +1 -1
  59. package/standalone/.next/server/app/[locale]/jira/tickets/page_client-reference-manifest.js +1 -1
  60. package/standalone/.next/server/app/[locale]/jobs/[jobId]/page/build-manifest.json +3 -3
  61. package/standalone/.next/server/app/[locale]/jobs/[jobId]/page.js.nft.json +1 -1
  62. package/standalone/.next/server/app/[locale]/jobs/[jobId]/page_client-reference-manifest.js +1 -1
  63. package/standalone/.next/server/app/[locale]/page/build-manifest.json +3 -3
  64. package/standalone/.next/server/app/[locale]/page.js.nft.json +1 -1
  65. package/standalone/.next/server/app/[locale]/page_client-reference-manifest.js +1 -1
  66. package/standalone/.next/server/app/[locale]/pull-requests/[owner]/[repository]/[number]/page/build-manifest.json +3 -3
  67. package/standalone/.next/server/app/[locale]/pull-requests/[owner]/[repository]/[number]/page.js.nft.json +1 -1
  68. package/standalone/.next/server/app/[locale]/pull-requests/[owner]/[repository]/[number]/page_client-reference-manifest.js +1 -1
  69. package/standalone/.next/server/app/[locale]/pull-requests/page/build-manifest.json +3 -3
  70. package/standalone/.next/server/app/[locale]/pull-requests/page.js.nft.json +1 -1
  71. package/standalone/.next/server/app/[locale]/pull-requests/page_client-reference-manifest.js +1 -1
  72. package/standalone/.next/server/app/[locale]/settings/page/build-manifest.json +3 -3
  73. package/standalone/.next/server/app/[locale]/settings/page.js.nft.json +1 -1
  74. package/standalone/.next/server/app/[locale]/settings/page_client-reference-manifest.js +1 -1
  75. package/standalone/.next/server/app/[locale]/skills/[skillId]/page/build-manifest.json +3 -3
  76. package/standalone/.next/server/app/[locale]/skills/[skillId]/page.js.nft.json +1 -1
  77. package/standalone/.next/server/app/[locale]/skills/[skillId]/page_client-reference-manifest.js +1 -1
  78. package/standalone/.next/server/app/[locale]/skills/groups/[groupId]/page/build-manifest.json +3 -3
  79. package/standalone/.next/server/app/[locale]/skills/groups/[groupId]/page.js.nft.json +1 -1
  80. package/standalone/.next/server/app/[locale]/skills/groups/[groupId]/page_client-reference-manifest.js +1 -1
  81. package/standalone/.next/server/app/[locale]/skills/groups/page/build-manifest.json +3 -3
  82. package/standalone/.next/server/app/[locale]/skills/groups/page.js.nft.json +1 -1
  83. package/standalone/.next/server/app/[locale]/skills/groups/page_client-reference-manifest.js +1 -1
  84. package/standalone/.next/server/app/[locale]/skills/page/build-manifest.json +3 -3
  85. package/standalone/.next/server/app/[locale]/skills/page.js.nft.json +1 -1
  86. package/standalone/.next/server/app/[locale]/skills/page_client-reference-manifest.js +1 -1
  87. package/standalone/.next/server/app/[locale]/skills/sync/[runId]/page/build-manifest.json +3 -3
  88. package/standalone/.next/server/app/[locale]/skills/sync/[runId]/page.js.nft.json +1 -1
  89. package/standalone/.next/server/app/[locale]/skills/sync/[runId]/page_client-reference-manifest.js +1 -1
  90. package/standalone/.next/server/app/[locale]/tools/page/build-manifest.json +3 -3
  91. package/standalone/.next/server/app/[locale]/tools/page.js.nft.json +1 -1
  92. package/standalone/.next/server/app/[locale]/tools/page_client-reference-manifest.js +1 -1
  93. package/standalone/.next/server/app/[locale]/usage/page/build-manifest.json +3 -3
  94. package/standalone/.next/server/app/[locale]/usage/page.js.nft.json +1 -1
  95. package/standalone/.next/server/app/[locale]/usage/page_client-reference-manifest.js +1 -1
  96. package/standalone/.next/server/app/[locale]/worktrees/[worktreeId]/page/build-manifest.json +3 -3
  97. package/standalone/.next/server/app/[locale]/worktrees/[worktreeId]/page.js.nft.json +1 -1
  98. package/standalone/.next/server/app/[locale]/worktrees/[worktreeId]/page_client-reference-manifest.js +1 -1
  99. package/standalone/.next/server/app/[locale]/worktrees/page/build-manifest.json +3 -3
  100. package/standalone/.next/server/app/[locale]/worktrees/page.js.nft.json +1 -1
  101. package/standalone/.next/server/app/[locale]/worktrees/page_client-reference-manifest.js +1 -1
  102. package/standalone/.next/server/app/_global-error/page/build-manifest.json +3 -3
  103. package/standalone/.next/server/app/_global-error.html +1 -1
  104. package/standalone/.next/server/app/_global-error.rsc +1 -1
  105. package/standalone/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
  106. package/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  107. package/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  108. package/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  109. package/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  110. package/standalone/.next/server/app/_not-found/page/build-manifest.json +3 -3
  111. package/standalone/.next/server/app/_not-found.html +1 -1
  112. package/standalone/.next/server/app/_not-found.rsc +1 -1
  113. package/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
  114. package/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  115. package/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
  116. package/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  117. package/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  118. package/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
  119. package/standalone/.next/server/app/api/build-artifact-uploads/[uploadId]/route.js +1 -1
  120. package/standalone/.next/server/app/api/build-artifact-uploads/[uploadId]/route.js.nft.json +1 -1
  121. package/standalone/.next/server/app/api/builds/[buildId]/artifacts/[artifactId]/manifest.plist/route/app-paths-manifest.json +3 -0
  122. package/standalone/.next/server/app/api/builds/[buildId]/artifacts/[artifactId]/manifest.plist/route/build-manifest.json +9 -0
  123. package/standalone/.next/server/app/api/builds/[buildId]/artifacts/[artifactId]/manifest.plist/route/server-reference-manifest.json +4 -0
  124. package/standalone/.next/server/app/api/builds/[buildId]/artifacts/[artifactId]/manifest.plist/route.js +11 -0
  125. package/standalone/.next/server/app/api/builds/[buildId]/artifacts/[artifactId]/manifest.plist/route.js.map +5 -0
  126. package/standalone/.next/server/app/api/builds/[buildId]/artifacts/[artifactId]/manifest.plist/route.js.nft.json +1 -0
  127. package/standalone/.next/server/app/api/builds/[buildId]/artifacts/[artifactId]/manifest.plist/route_client-reference-manifest.js +3 -0
  128. package/standalone/.next/server/app/api/builds/[buildId]/artifacts/[artifactId]/route.js +4 -3
  129. package/standalone/.next/server/app/api/builds/[buildId]/artifacts/[artifactId]/route.js.nft.json +1 -1
  130. package/standalone/.next/server/app/api/codebases/by-path/route.js.nft.json +1 -1
  131. package/standalone/.next/server/app/api/codebases/route.js.nft.json +1 -1
  132. package/standalone/.next/server/app/api/graphql/route.js.nft.json +1 -1
  133. package/standalone/.next/server/app/api/mcp/route.js.nft.json +1 -1
  134. package/standalone/.next/server/app/api/tools/call/route.js.nft.json +1 -1
  135. package/standalone/.next/server/app/api/tools/catalog/route.js.nft.json +1 -1
  136. package/standalone/.next/server/app/api/worktrees/[worktreeId]/diff-image/route/app-paths-manifest.json +3 -0
  137. package/standalone/.next/server/app/api/worktrees/[worktreeId]/diff-image/route/build-manifest.json +9 -0
  138. package/standalone/.next/server/app/api/worktrees/[worktreeId]/diff-image/route/server-reference-manifest.json +4 -0
  139. package/standalone/.next/server/app/api/worktrees/[worktreeId]/diff-image/route.js +10 -0
  140. package/standalone/.next/server/app/api/worktrees/[worktreeId]/diff-image/route.js.map +5 -0
  141. package/standalone/.next/server/app/api/worktrees/[worktreeId]/diff-image/route.js.nft.json +1 -0
  142. package/standalone/.next/server/app/api/worktrees/[worktreeId]/diff-image/route_client-reference-manifest.js +3 -0
  143. package/standalone/.next/server/app-paths-manifest.json +3 -0
  144. package/standalone/.next/server/chunks/07_e_builds_[buildId]_artifacts_[artifactId]_manifest_plist_route_actions_18mzaf3.js +3 -0
  145. package/standalone/.next/server/chunks/1oeh_server_app_api_worktrees_[worktreeId]_diff-image_route_actions_0nie5n3.js +3 -0
  146. package/standalone/.next/server/chunks/[root-of-the-server]__08mnmfs._.js +3 -0
  147. package/standalone/.next/server/chunks/[root-of-the-server]__0906ea6._.js +3 -0
  148. package/standalone/.next/server/chunks/{[root-of-the-server]__0-ypkf7._.js → [root-of-the-server]__09v6swg._.js} +1 -1
  149. package/standalone/.next/server/chunks/{[root-of-the-server]__1wtng3p._.js → [root-of-the-server]__0aiyd05._.js} +1 -1
  150. package/standalone/.next/server/chunks/{[root-of-the-server]__09skp6r._.js → [root-of-the-server]__0cz4ap5._.js} +1 -1
  151. package/standalone/.next/server/chunks/[root-of-the-server]__0nvcazp._.js +3 -0
  152. package/standalone/.next/server/chunks/[root-of-the-server]__12807ia._.js +3 -0
  153. package/standalone/.next/server/chunks/[root-of-the-server]__15_clsj._.js +3 -0
  154. package/standalone/.next/server/chunks/[root-of-the-server]__1csx222._.js +3 -0
  155. package/standalone/.next/server/chunks/[root-of-the-server]__1ix62p0._.js +122 -0
  156. package/standalone/.next/server/chunks/[root-of-the-server]__1j-hk6b._.js +3 -0
  157. package/standalone/.next/server/chunks/{[root-of-the-server]__15aat79._.js → [root-of-the-server]__1j0u03u._.js} +1 -1
  158. package/standalone/.next/server/chunks/{[root-of-the-server]__0c02fce._.js → [root-of-the-server]__1oa-sy6._.js} +2 -2
  159. package/standalone/.next/server/chunks/[root-of-the-server]__1qmvsu_._.js +3 -0
  160. package/standalone/.next/server/chunks/[root-of-the-server]__1vxtcsq._.js +3 -0
  161. package/standalone/.next/server/chunks/[root-of-the-server]__1w59u6o._.js +3 -0
  162. package/standalone/.next/server/chunks/[root-of-the-server]__1z-3x8k._.js +3 -0
  163. package/standalone/.next/server/chunks/_0exkna-._.js +193 -8
  164. package/standalone/.next/server/chunks/_0jurkow._.js +1 -1
  165. package/standalone/.next/server/chunks/_0rx5rfm._.js +1 -1
  166. package/standalone/.next/server/chunks/_0tufgj0._.js +263 -78
  167. package/standalone/.next/server/chunks/_0w2eghf._.js +193 -8
  168. package/standalone/.next/server/chunks/_152rnkf._.js +17 -17
  169. package/standalone/.next/server/chunks/_16x77b_._.js +1 -1
  170. package/standalone/.next/server/chunks/_1dbqy8c._.js +17 -17
  171. package/standalone/.next/server/chunks/_1vrixpc._.js +17 -17
  172. package/standalone/.next/server/chunks/_1yfz-9u._.js +17 -17
  173. package/standalone/.next/server/chunks/node_modules_next_dist_esm_build_templates_app-route_08x37wy.js +6 -0
  174. package/standalone/.next/server/chunks/node_modules_next_dist_esm_build_templates_app-route_0eghv0a.js +3 -0
  175. package/standalone/.next/server/chunks/src_0o4-snj._.js +1 -1
  176. package/standalone/.next/server/chunks/ssr/1oeh_server_app_[locale]_builds_[buildId]_coverage_page_actions_20i_0zd.js +3 -0
  177. package/standalone/.next/server/chunks/ssr/[root-of-the-server]__08bbv6z._.js +9 -0
  178. package/standalone/.next/server/chunks/ssr/[root-of-the-server]__0ckj6wg._.js +13 -0
  179. package/standalone/.next/server/chunks/ssr/[root-of-the-server]__0eit075._.js +3 -0
  180. package/standalone/.next/server/chunks/ssr/[root-of-the-server]__0qy28ku._.js +3 -0
  181. package/standalone/.next/server/chunks/ssr/[root-of-the-server]__0tsac-e._.js +15 -0
  182. package/standalone/.next/server/chunks/ssr/{_19fwji0._.js → [root-of-the-server]__0uo4dhz._.js} +2 -2
  183. package/standalone/.next/server/chunks/ssr/[root-of-the-server]__15v1geo._.js +3 -0
  184. package/standalone/.next/server/chunks/ssr/[root-of-the-server]__1qm_j4s._.js +3 -0
  185. package/standalone/.next/server/chunks/ssr/[root-of-the-server]__1s6_fta._.js +3 -0
  186. package/standalone/.next/server/chunks/ssr/_0-o77zz._.js +3 -0
  187. package/standalone/.next/server/chunks/ssr/_00m30gw._.js +3 -0
  188. package/standalone/.next/server/chunks/ssr/_03961l6._.js +3 -0
  189. package/standalone/.next/server/chunks/ssr/{_00ye62k._.js → _08q5hnx._.js} +2 -2
  190. package/standalone/.next/server/chunks/ssr/_093--4s._.js +3 -0
  191. package/standalone/.next/server/chunks/ssr/{_1kaz2gq._.js → _09lh67h._.js} +2 -2
  192. package/standalone/.next/server/chunks/ssr/_0a1ligo._.js +3 -0
  193. package/standalone/.next/server/chunks/ssr/{_1h3p6oi._.js → _0cw_tqx._.js} +2 -2
  194. package/standalone/.next/server/chunks/ssr/_0dq-84j._.js +22 -0
  195. package/standalone/.next/server/chunks/ssr/_0dy1ovj._.js +3 -0
  196. package/standalone/.next/server/chunks/ssr/_0ezo890._.js +3 -0
  197. package/standalone/.next/server/chunks/ssr/{_1_1j9nd._.js → _0id1774._.js} +3 -3
  198. package/standalone/.next/server/chunks/ssr/_0j48up8._.js +1 -1
  199. package/standalone/.next/server/chunks/ssr/_0nhtqty._.js +1 -1
  200. package/standalone/.next/server/chunks/ssr/_0oh246z._.js +3 -0
  201. package/standalone/.next/server/chunks/ssr/_0q8sy0w._.js +3 -0
  202. package/standalone/.next/server/chunks/ssr/_0qra30j._.js +3 -0
  203. package/standalone/.next/server/chunks/ssr/_0t2jwg3._.js +109 -0
  204. package/standalone/.next/server/chunks/ssr/{_1zyjq9y._.js → _0ufkrvm._.js} +2 -2
  205. package/standalone/.next/server/chunks/ssr/{_07tglcd._.js → _0vjihfb._.js} +2 -10
  206. package/standalone/.next/server/chunks/ssr/_0xh-lyy._.js +3 -0
  207. package/standalone/.next/server/chunks/ssr/_15erjna._.js +3 -0
  208. package/standalone/.next/server/chunks/ssr/_17a3xuq._.js +3 -0
  209. package/standalone/.next/server/chunks/ssr/_17p59oz._.js +1 -1
  210. package/standalone/.next/server/chunks/ssr/_1bzibio._.js +109 -0
  211. package/standalone/.next/server/chunks/ssr/{_12ws_f2._.js → _1c2fde9._.js} +2 -2
  212. package/standalone/.next/server/chunks/ssr/{_0mr9u9p._.js → _1dnm1d4._.js} +2 -2
  213. package/standalone/.next/server/chunks/ssr/_1ekznx8._.js +3 -0
  214. package/standalone/.next/server/chunks/ssr/{_1r2rmfp._.js → _1f5k6bj._.js} +2 -2
  215. package/standalone/.next/server/chunks/ssr/_1k1tyz4._.js +3 -0
  216. package/standalone/.next/server/chunks/ssr/_1och_zk._.js +3 -0
  217. package/standalone/.next/server/chunks/ssr/{_0xrpdyy._.js → _1tpguta._.js} +2 -2
  218. package/standalone/.next/server/chunks/ssr/_1v1lo7c._.js +3 -0
  219. package/standalone/.next/server/chunks/ssr/_1vv-dg-._.js +3 -0
  220. package/standalone/.next/server/chunks/ssr/_1zgu74l._.js +1 -1
  221. package/standalone/.next/server/chunks/ssr/messages_de_json_[json]_cjs_1uig1ud._.js +1 -1
  222. package/standalone/.next/server/chunks/ssr/messages_en_json_[json]_cjs_206072-._.js +1 -1
  223. package/standalone/.next/server/chunks/ssr/messages_es_json_[json]_cjs_082yukd._.js +1 -1
  224. package/standalone/.next/server/chunks/ssr/messages_fr_json_[json]_cjs_0j6-w0i._.js +1 -1
  225. package/standalone/.next/server/chunks/ssr/node_modules_next_dist_esm_build_templates_app-page_1uzalot.js +4 -0
  226. package/standalone/.next/server/chunks/ssr/src_components_builds_build-detail-page_tsx_0q1lvps._.js +25 -9
  227. package/standalone/.next/server/chunks/ssr/src_components_builds_coverage-report-page_tsx_1nhwba1._.js +11 -0
  228. package/standalone/.next/server/chunks/ssr/src_components_codebases_repository-detail-page_tsx_20_7wk_._.js +5 -5
  229. package/standalone/.next/server/chunks/ssr/src_components_jira_cache-page_tsx_1znt1yl._.js +1 -1
  230. package/standalone/.next/server/chunks/ssr/src_components_skills_skill-sync-page_tsx_15sidhb._.js +1 -1
  231. package/standalone/.next/server/chunks/ssr/src_components_skills_skills-page_tsx_0o1nlbg._.js +3 -3
  232. package/standalone/.next/server/chunks/ssr/src_components_worktrees_worktree-detail-page_tsx_1oexgu_._.js +13 -5
  233. package/standalone/.next/server/functions-config-manifest.json +7 -1
  234. package/standalone/.next/server/middleware-build-manifest.js +6 -6
  235. package/standalone/.next/server/next-font-manifest.js +1 -1
  236. package/standalone/.next/server/next-font-manifest.json +4 -0
  237. package/standalone/.next/server/pages/404.html +1 -1
  238. package/standalone/.next/server/pages/500.html +1 -1
  239. package/standalone/.next/server/server-reference-manifest.js +1 -1
  240. package/standalone/.next/server/server-reference-manifest.json +1 -1
  241. package/standalone/.next/static/chunks/08ujwkhzkokhf.js +1 -0
  242. package/standalone/.next/static/chunks/{16tfdsma_h0r5.js → 0a82bgw37ev__.js} +1 -1
  243. package/standalone/.next/static/chunks/0cw4z7_m01m5f.js +31 -0
  244. package/standalone/.next/static/chunks/0emxr8tr7kji0.js +33 -0
  245. package/standalone/.next/static/chunks/0h8zu6aywqh1-.js +27 -0
  246. package/standalone/.next/static/chunks/0hwbkceik59kx.js +35 -0
  247. package/standalone/.next/static/chunks/0mq066ue0yufb.js +7 -0
  248. package/standalone/.next/static/chunks/0ng892ycfzv2q.js +1 -0
  249. package/standalone/.next/static/chunks/0tgtji0dbbefa.js +107 -0
  250. package/standalone/.next/static/chunks/15_8kla24ff9f.js +1 -0
  251. package/standalone/.next/static/chunks/16-l4kp9o217d.js +1 -0
  252. package/standalone/.next/static/chunks/175269y15-grc.js +48 -0
  253. package/standalone/.next/static/chunks/1c7ci5ai5qyfo.js +1 -0
  254. package/standalone/.next/static/chunks/1egccqmgk3kbi.js +1 -0
  255. package/standalone/.next/static/chunks/1gwvywh0e6i17.js +3 -0
  256. package/standalone/.next/static/chunks/1hehq94hslvbl.js +7 -0
  257. package/standalone/.next/static/chunks/1k5cw41bd4e5r.js +51 -0
  258. package/standalone/.next/static/chunks/{2rjf8o4g23no8.js → 1mprxk0uebv70.js} +1 -1
  259. package/standalone/.next/static/chunks/1rus13s58mkxy.js +1 -0
  260. package/standalone/.next/static/chunks/{2fzxwi39odugq.js → 1rwqeg_l_plzt.js} +1 -1
  261. package/standalone/.next/static/chunks/1u_jwc-bo10k3.js +1 -0
  262. package/standalone/.next/static/chunks/1w_zvfx_jdyv_.js +29 -0
  263. package/standalone/.next/static/chunks/2-wrd12dmwbo-.js +1 -0
  264. package/standalone/.next/static/chunks/208k3moxxd_53.js +51 -0
  265. package/standalone/.next/static/chunks/23kut2_l2y2-w.js +9 -0
  266. package/standalone/.next/static/chunks/{1nnp0crt0mi_-.js → 23xyv764-kx8z.js} +12 -12
  267. package/standalone/.next/static/chunks/{1wihkuyju-wa-.js → 25haryfoj4x3h.js} +1 -1
  268. package/standalone/.next/static/chunks/{0fr0zp4cwlyrr.js → 28ljy-uz6n_g9.js} +2 -2
  269. package/standalone/.next/static/chunks/{4013cp3x6bvuw.js → 2__bi79el56lb.js} +5 -5
  270. package/standalone/.next/static/chunks/2aq80hyo4l8a2.js +7 -0
  271. package/standalone/.next/static/chunks/{24a_ux27e8jj4.js → 2l_wayk-ioguc.js} +1 -1
  272. package/standalone/.next/static/chunks/2swvzbwm_dr9y.js +1 -0
  273. package/standalone/.next/static/chunks/2zwijj9t6y7gn.js +7 -0
  274. package/standalone/.next/static/chunks/{0m26h4h36cb_0.js → 3120xcqaqci82.js} +1 -1
  275. package/standalone/.next/static/chunks/{3-u7pvogc5_bx.js → 315cis1es_5t3.js} +12 -12
  276. package/standalone/.next/static/chunks/321feys_9vp2v.js +33 -0
  277. package/standalone/.next/static/chunks/{0d-e51sawr2y8.js → 34oyci46fsc5w.js} +1 -1
  278. package/standalone/.next/static/chunks/37mczlawlcx5l.js +1 -0
  279. package/standalone/.next/static/chunks/398uw5_4-ciaz.css +1 -0
  280. package/standalone/.next/static/chunks/{1tcqtaedz7922.js → 3d4ijy7zglgv5.js} +1 -1
  281. package/standalone/.next/static/chunks/3ir4b8po439x7.js +40 -0
  282. package/standalone/.next/static/chunks/3l2g83119oyl0.js +1 -0
  283. package/standalone/.next/static/chunks/3r-6igcofcwxj.js +107 -0
  284. package/standalone/.next/static/chunks/{0m--47yk9j-ec.js → 3tycetq7o8g3h.js} +1 -1
  285. package/standalone/.next/static/chunks/{0biu8z1f8c42u.js → 40ovzayigkz_-.js} +1 -1
  286. package/standalone/.next/static/chunks/41wq-96yyl0uw.js +1 -0
  287. package/standalone/.next/static/chunks/44xu63hsh11xp.js +1 -0
  288. package/standalone/.next/static/chunks/{turbopack-1rw7om86c18dg.js → turbopack-1v9ogj_5hv56l.js} +1 -1
  289. package/standalone/src/services/builds/artifact-cache.test.ts +241 -0
  290. package/standalone/src/services/builds/artifact-cache.ts +235 -0
  291. package/standalone/src/services/builds/artifact-transfer.ts +2 -1
  292. package/standalone/src/services/builds/builds.service.test.ts +55 -0
  293. package/standalone/src/services/builds/builds.service.ts +499 -17
  294. package/standalone/.next/server/chunks/[root-of-the-server]__02vqwey._.js +0 -3
  295. package/standalone/.next/server/chunks/[root-of-the-server]__1oahp4o._.js +0 -122
  296. package/standalone/.next/server/chunks/ssr/[root-of-the-server]__0361lgt._.js +0 -33
  297. package/standalone/.next/server/chunks/ssr/[root-of-the-server]__06f05jg._.js +0 -3
  298. package/standalone/.next/server/chunks/ssr/[root-of-the-server]__0ay4_3d._.js +0 -37
  299. package/standalone/.next/server/chunks/ssr/[root-of-the-server]__0dm8osz._.js +0 -3
  300. package/standalone/.next/server/chunks/ssr/[root-of-the-server]__1349-us._.js +0 -3
  301. package/standalone/.next/server/chunks/ssr/[root-of-the-server]__1xv798-._.js +0 -3
  302. package/standalone/.next/server/chunks/ssr/[root-of-the-server]__1z955gh._.js +0 -3
  303. package/standalone/.next/server/chunks/ssr/_0bvpmhm._.js +0 -101
  304. package/standalone/.next/server/chunks/ssr/_0eb36qa._.js +0 -3
  305. package/standalone/.next/server/chunks/ssr/_0ndnmkz._.js +0 -3
  306. package/standalone/.next/server/chunks/ssr/_0nm6upn._.js +0 -3
  307. package/standalone/.next/server/chunks/ssr/_0pe56xv._.js +0 -3
  308. package/standalone/.next/server/chunks/ssr/_0quhjav._.js +0 -3
  309. package/standalone/.next/server/chunks/ssr/_0w05v7m._.js +0 -3
  310. package/standalone/.next/server/chunks/ssr/_0xozjkz._.js +0 -3
  311. package/standalone/.next/server/chunks/ssr/_1-1mnp6._.js +0 -3
  312. package/standalone/.next/server/chunks/ssr/_1-7r8vu._.js +0 -3
  313. package/standalone/.next/server/chunks/ssr/_1-grolz._.js +0 -3
  314. package/standalone/.next/server/chunks/ssr/_10evhql._.js +0 -3
  315. package/standalone/.next/server/chunks/ssr/_142x9wn._.js +0 -22
  316. package/standalone/.next/server/chunks/ssr/_1_ta7g1._.js +0 -3
  317. package/standalone/.next/server/chunks/ssr/_1ey5mmy._.js +0 -3
  318. package/standalone/.next/server/chunks/ssr/_1fdyu2w._.js +0 -3
  319. package/standalone/.next/server/chunks/ssr/_1g3i459._.js +0 -15
  320. package/standalone/.next/server/chunks/ssr/_1g_aflc._.js +0 -3
  321. package/standalone/.next/server/chunks/ssr/_1lcwubl._.js +0 -3
  322. package/standalone/.next/server/chunks/ssr/_1lseili._.js +0 -3
  323. package/standalone/.next/server/chunks/ssr/_1za3z6k._.js +0 -101
  324. package/standalone/.next/server/chunks/ssr/_20ufqtr._.js +0 -3
  325. package/standalone/.next/static/chunks/0a3s519wuzjet.js +0 -1
  326. package/standalone/.next/static/chunks/0newuocvg03xy.js +0 -3
  327. package/standalone/.next/static/chunks/0rlomhlt6030e.js +0 -1
  328. package/standalone/.next/static/chunks/1802j8gq2g47d.js +0 -7
  329. package/standalone/.next/static/chunks/1_tnhhn4f122y.js +0 -9
  330. package/standalone/.next/static/chunks/1eny65xy2ho48.js +0 -1
  331. package/standalone/.next/static/chunks/1joeb4m-mmn69.js +0 -7
  332. package/standalone/.next/static/chunks/1l4bkigul15i3.js +0 -25
  333. package/standalone/.next/static/chunks/1qd6cse_d9ae1.js +0 -99
  334. package/standalone/.next/static/chunks/2-mxmazwsh-wz.js +0 -1
  335. package/standalone/.next/static/chunks/24sd_kmnetibb.js +0 -1
  336. package/standalone/.next/static/chunks/27k7_zc-iuppd.js +0 -1
  337. package/standalone/.next/static/chunks/2baaj4mpr6i-i.js +0 -1
  338. package/standalone/.next/static/chunks/2cj2ndz_e0hbz.js +0 -1
  339. package/standalone/.next/static/chunks/2fzwgr2iq_783.css +0 -1
  340. package/standalone/.next/static/chunks/2hv6pkoloju0g.js +0 -33
  341. package/standalone/.next/static/chunks/2jbygfc0lzu0t.js +0 -55
  342. package/standalone/.next/static/chunks/2n7u72nxziv_5.js +0 -7
  343. package/standalone/.next/static/chunks/2sqprazdqp7f7.js +0 -1
  344. package/standalone/.next/static/chunks/2x8qovg070057.js +0 -1
  345. package/standalone/.next/static/chunks/3-zh9b0tcttiw.js +0 -33
  346. package/standalone/.next/static/chunks/31wyih3wwkeid.js +0 -1
  347. package/standalone/.next/static/chunks/33y_b6dqfx3tk.js +0 -1
  348. package/standalone/.next/static/chunks/3h1pxh5fd97ym.js +0 -27
  349. package/standalone/.next/static/chunks/3jbfibit05k5o.js +0 -29
  350. package/standalone/.next/static/chunks/3jd8_7lna-j_x.js +0 -40
  351. package/standalone/.next/static/chunks/3jv2gnnn4vx-j.js +0 -43
  352. package/standalone/.next/static/chunks/3k0oakdayt9yt.js +0 -40
  353. package/standalone/.next/static/chunks/3pgngh8lg3be3.js +0 -99
  354. package/standalone/.next/static/chunks/43gw6_6hbw18w.js +0 -51
  355. /package/standalone/.next/static/{8uBWJ8MOV-OEPe88M3PhU → sJ09NBDjsLncJEkHNWbdR}/_buildManifest.js +0 -0
  356. /package/standalone/.next/static/{8uBWJ8MOV-OEPe88M3PhU → sJ09NBDjsLncJEkHNWbdR}/_clientMiddlewareManifest.js +0 -0
  357. /package/standalone/.next/static/{8uBWJ8MOV-OEPe88M3PhU → sJ09NBDjsLncJEkHNWbdR}/_ssgManifest.js +0 -0
@@ -0,0 +1,235 @@
1
+ import "server-only";
2
+
3
+ import { createHash, randomUUID } from "node:crypto";
4
+ import {
5
+ mkdir,
6
+ readdir,
7
+ readFile,
8
+ rename,
9
+ rm,
10
+ stat,
11
+ utimes,
12
+ writeFile,
13
+ } from "node:fs/promises";
14
+ import { tmpdir } from "node:os";
15
+ import { join } from "node:path";
16
+
17
+ import {
18
+ cancelArtifactTransfer,
19
+ expectArtifactTransfer,
20
+ } from "./artifact-transfer";
21
+ import { getServerServices } from "@/services/server-services";
22
+
23
+ export type MaterializedArtifact = {
24
+ path: string;
25
+ filename: string;
26
+ contentType: string;
27
+ size: number;
28
+ etag: string;
29
+ };
30
+
31
+ type Sidecar = {
32
+ filename: string;
33
+ contentType: string;
34
+ size: number;
35
+ };
36
+
37
+ // Overridable so the cache can be pointed at a volume with room for large
38
+ // packages, rather than whatever the temporary directory happens to allow.
39
+ const CACHE_DIRECTORY =
40
+ process.env.ARTIFACT_CACHE_DIRECTORY?.trim() ||
41
+ join(tmpdir(), "ade-build-artifact-cache");
42
+ const MAX_CACHE_BYTES =
43
+ Number(process.env.ARTIFACT_CACHE_MAX_BYTES) || 5 * 1024 ** 3;
44
+ const MAX_CACHE_AGE_MS = 6 * 60 * 60 * 1_000;
45
+
46
+ const globalCache = globalThis as typeof globalThis & {
47
+ buildArtifactMaterializations?: Map<string, Promise<MaterializedArtifact>>;
48
+ };
49
+ const inFlight =
50
+ globalCache.buildArtifactMaterializations ??
51
+ (globalCache.buildArtifactMaterializations = new Map());
52
+
53
+ /**
54
+ * Transfers are billed against wall-clock time on a link we do not control, so
55
+ * the ceiling scales with the artifact: two minutes of overhead plus roughly a
56
+ * second per megabyte, capped so a stalled agent cannot hold a request forever.
57
+ */
58
+ function transferTimeout(sizeBytes: number | null): number {
59
+ return Math.min(30 * 60_000, 120_000 + (sizeBytes ?? 0) / 1_000);
60
+ }
61
+
62
+ function cacheKey(artifactId: string, identity: string): string {
63
+ return createHash("sha1").update(`${artifactId}:${identity}`).digest("hex");
64
+ }
65
+
66
+ async function readSidecar(path: string): Promise<Sidecar | null> {
67
+ try {
68
+ const parsed: unknown = JSON.parse(await readFile(path, "utf8"));
69
+ if (!parsed || typeof parsed !== "object") return null;
70
+ const { filename, contentType, size } = parsed as Record<string, unknown>;
71
+ if (
72
+ typeof filename !== "string" ||
73
+ typeof contentType !== "string" ||
74
+ typeof size !== "number"
75
+ ) {
76
+ return null;
77
+ }
78
+ return { filename, contentType, size };
79
+ } catch {
80
+ return null;
81
+ }
82
+ }
83
+
84
+ async function readCached(key: string): Promise<MaterializedArtifact | null> {
85
+ const path = join(CACHE_DIRECTORY, key);
86
+ const sidecar = await readSidecar(`${path}.json`);
87
+ if (!sidecar) return null;
88
+ try {
89
+ const information = await stat(path);
90
+ if (!information.isFile() || information.size !== sidecar.size) return null;
91
+ const now = new Date();
92
+ await utimes(path, now, now).catch(() => {});
93
+ return { path, etag: `"${key}"`, ...sidecar };
94
+ } catch {
95
+ return null;
96
+ }
97
+ }
98
+
99
+ /**
100
+ * Drops the least recently used entries once the cache exceeds its budget, and
101
+ * anything past the maximum age. Entries are several hundred megabytes each, so
102
+ * skipping this would fill the temporary directory within a handful of builds.
103
+ *
104
+ * `keep` is the entry the caller is about to serve. It is exempt so that an
105
+ * artifact larger than the whole budget is still returned rather than deleted
106
+ * out from under the response that just fetched it.
107
+ */
108
+ async function prune(keep: string): Promise<void> {
109
+ let entries;
110
+ try {
111
+ entries = await readdir(CACHE_DIRECTORY);
112
+ } catch {
113
+ return;
114
+ }
115
+ const files: {
116
+ path: string;
117
+ size: number;
118
+ mtimeMs: number;
119
+ keep: boolean;
120
+ }[] = [];
121
+ for (const entry of entries) {
122
+ if (entry.endsWith(".json")) continue;
123
+ const path = join(CACHE_DIRECTORY, entry);
124
+ try {
125
+ const information = await stat(path);
126
+ if (!information.isFile()) continue;
127
+ files.push({
128
+ path,
129
+ size: information.size,
130
+ mtimeMs: information.mtimeMs,
131
+ keep: entry === keep,
132
+ });
133
+ } catch {
134
+ continue;
135
+ }
136
+ }
137
+ const expired = Date.now() - MAX_CACHE_AGE_MS;
138
+ files.sort((left, right) => left.mtimeMs - right.mtimeMs);
139
+ // The kept entry counts against the budget but is never a deletion candidate.
140
+ let total = files.reduce((sum, file) => sum + file.size, 0);
141
+ for (const file of files) {
142
+ if (file.keep) continue;
143
+ if (total <= MAX_CACHE_BYTES && file.mtimeMs >= expired) continue;
144
+ await rm(file.path, { force: true }).catch(() => {});
145
+ await rm(`${file.path}.json`, { force: true }).catch(() => {});
146
+ total -= file.size;
147
+ }
148
+ }
149
+
150
+ async function transfer(
151
+ buildId: string,
152
+ artifactId: string,
153
+ key: string,
154
+ sizeBytes: number | null,
155
+ ): Promise<MaterializedArtifact> {
156
+ const uploadId = randomUUID();
157
+ const services = getServerServices();
158
+ const build = await services.buildsService.getBuild(buildId);
159
+ if (!build?.agentId) throw new Error("Build agent is unavailable");
160
+
161
+ const timeoutMs = transferTimeout(sizeBytes);
162
+ try {
163
+ const [received] = await Promise.all([
164
+ expectArtifactTransfer(uploadId, build.agentId, timeoutMs),
165
+ services.buildsService.prepareArtifactDownload(
166
+ buildId,
167
+ artifactId,
168
+ uploadId,
169
+ timeoutMs,
170
+ ),
171
+ ]);
172
+ await mkdir(CACHE_DIRECTORY, { recursive: true, mode: 0o700 });
173
+ const path = join(CACHE_DIRECTORY, key);
174
+ // Both paths live under the temporary directory, so this rename is atomic
175
+ // and never crosses a filesystem boundary.
176
+ await rename(received.path, path);
177
+ const size = (await stat(path)).size;
178
+ const sidecar: Sidecar = {
179
+ // The agent chooses the filename — it appends .tar.gz when it packages a
180
+ // directory — so that decision has to survive into later cache hits.
181
+ filename: received.filename,
182
+ contentType: received.contentType,
183
+ size,
184
+ };
185
+ // Written after the rename so a torn write cannot leave a valid-looking entry.
186
+ await writeFile(`${path}.json`, JSON.stringify(sidecar), { mode: 0o600 });
187
+ // Awaited so the budget actually holds by the time the response is served;
188
+ // a directory scan is negligible next to the transfer that just finished.
189
+ await prune(key).catch(() => {});
190
+ return { path, etag: `"${key}"`, ...sidecar };
191
+ } catch (error) {
192
+ cancelArtifactTransfer(uploadId, error);
193
+ throw error;
194
+ }
195
+ }
196
+
197
+ /**
198
+ * Returns a local copy of a build artifact, fetching it from the agent only when
199
+ * it is not already cached.
200
+ *
201
+ * The agent holds the only copy, and every fetch costs a full upload of the
202
+ * whole file. iOS issues a probe plus several ranged requests per install, so
203
+ * serving those directly from the relay would re-transfer the package several
204
+ * times over. Caching first makes range requests answerable at all, and collapses
205
+ * concurrent requests for the same artifact into one transfer.
206
+ */
207
+ export async function materializeArtifact(
208
+ buildId: string,
209
+ artifactId: string,
210
+ ): Promise<MaterializedArtifact> {
211
+ const services = getServerServices();
212
+ const artifact = await services.buildsService.artifactForInstall(
213
+ buildId,
214
+ artifactId,
215
+ );
216
+ if (!artifact) throw new Error("Build artifact not found");
217
+
218
+ const key = cacheKey(artifactId, artifact.checksum ?? artifact.createdAt);
219
+ const cached = await readCached(key);
220
+ if (cached) return cached;
221
+
222
+ const existing = inFlight.get(key);
223
+ if (existing) return existing;
224
+
225
+ const pending = transfer(
226
+ buildId,
227
+ artifactId,
228
+ key,
229
+ artifact.sizeBytes,
230
+ ).finally(() => {
231
+ inFlight.delete(key);
232
+ });
233
+ inFlight.set(key, pending);
234
+ return pending;
235
+ }
@@ -31,12 +31,13 @@ const transfers =
31
31
  export function expectArtifactTransfer(
32
32
  uploadId: string,
33
33
  agentId: string,
34
+ timeoutMs = 170_000,
34
35
  ): Promise<ArtifactTransfer> {
35
36
  return new Promise((resolve, reject) => {
36
37
  const timeout = setTimeout(() => {
37
38
  transfers.delete(uploadId);
38
39
  reject(new Error("The agent did not upload the artifact in time"));
39
- }, 170_000);
40
+ }, timeoutMs);
40
41
  timeout.unref();
41
42
  transfers.set(uploadId, { agentId, resolve, reject, timeout });
42
43
  });
@@ -247,6 +247,61 @@ describe("BuildsService", () => {
247
247
  expect(createJob).not.toHaveBeenCalled();
248
248
  });
249
249
 
250
+ test("requires a report-capable agent for worktree coverage", async () => {
251
+ const legacyWorktree = {
252
+ ...worktree(),
253
+ baseBranchOverride: null,
254
+ codebase: { ...worktree().codebase, defaultBranch: "main" },
255
+ };
256
+ getPrismaClient.mockResolvedValue({
257
+ build: { findUnique: vi.fn().mockResolvedValue(null) },
258
+ worktree: { findUnique: vi.fn().mockResolvedValue(legacyWorktree) },
259
+ });
260
+ const createJob = vi.fn();
261
+
262
+ await expect(
263
+ new BuildsService(control(createJob)).startWorktreeCoverage({
264
+ worktreeId: "worktree-1",
265
+ configurationId: "configuration-1",
266
+ destination,
267
+ requestId: "coverage-1",
268
+ }),
269
+ ).rejects.toThrow("updated for worktree coverage");
270
+ expect(createJob).not.toHaveBeenCalled();
271
+ });
272
+
273
+ test("bounds worktree coverage history and excludes report payloads", async () => {
274
+ const findMany = vi.fn().mockResolvedValue([]);
275
+ getPrismaClient.mockResolvedValue({ buildReport: { findMany } });
276
+
277
+ await new BuildsService(control()).coverageHistory("worktree-1");
278
+
279
+ const query = findMany.mock.calls[0]![0];
280
+ expect(query).toMatchObject({
281
+ where: {
282
+ kind: "CODE_COVERAGE",
283
+ source: "WORKTREE",
284
+ build: { worktreeId: "worktree-1" },
285
+ },
286
+ take: 50,
287
+ select: {
288
+ summaryJson: true,
289
+ build: { select: { artifacts: expect.any(Object) } },
290
+ },
291
+ });
292
+ expect(query.select).not.toHaveProperty("dataJson");
293
+ expect(query).not.toHaveProperty("include");
294
+ });
295
+
296
+ test("does not materialize reports for build-list queries", async () => {
297
+ const findMany = vi.fn().mockResolvedValue([]);
298
+ getPrismaClient.mockResolvedValue({ build: { findMany } });
299
+
300
+ await new BuildsService(control()).builds();
301
+
302
+ expect(findMany.mock.calls[0]![0].include).not.toHaveProperty("reports");
303
+ });
304
+
250
305
  test("rebuilds with the original destination and selected settings", async () => {
251
306
  getPrismaClient.mockResolvedValue({
252
307
  build: {