@wular/coder 0.4.1 → 0.4.3

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 (378) hide show
  1. package/README.md +69 -70
  2. package/dist/actions-FzDNxMFf.js +20 -0
  3. package/dist/{adapter-BKVzTDnn.js → adapter-7SEiVsmp.js} +1 -1
  4. package/dist/{adapter-B0oYCI0S.js → adapter-BLEfUqoQ.js} +1 -1
  5. package/dist/{adapter-DPInwbJG.js → adapter-H4GlcKQz.js} +1 -1
  6. package/dist/{add-CE-70LFs.js → add-B50dIJGd.js} +1 -1
  7. package/dist/{add-BxpLHnvD.js → add-D7X3gxap.js} +1 -1
  8. package/dist/{add-ej0X9e3a.js → add-DOEUHIe7.js} +1 -1
  9. package/dist/{add-U-pty9qJ.js → add-QExQhTzi.js} +1 -1
  10. package/dist/{add-json-CEa-6luW.js → add-json-Ck4KIZrT.js} +1 -1
  11. package/dist/{agent-JdtZozvP.js → agent-BPtnEf1q.js} +1 -1
  12. package/dist/{agent-BsYx3Odz.js → agent-BlQ_dNNm.js} +2 -2
  13. package/dist/{alias-DX-Bm86x.js → alias-BwQBovke.js} +1 -1
  14. package/dist/{app-BT5f7zL5.js → app-y6BYm2eH.js} +1 -1
  15. package/dist/{approvals-C-AvFt6s.js → approvals-Cc9cq5T1.js} +2 -2
  16. package/dist/{approve-DOmpQRNd.js → approve-oO9k2fnK.js} +2 -2
  17. package/dist/{apps-CjfrZyM5.js → apps-CU2YalX8.js} +1 -1
  18. package/dist/{archive-BziJ1rQ-.js → archive-C5c_HQS1.js} +2 -2
  19. package/dist/{archive-BUB8gFKD.js → archive-CemlWlVY.js} +2 -2
  20. package/dist/archive-sweep-ehg-fWIt.js +1 -0
  21. package/dist/{args-BOEEdpWk.js → args-DcBCtXhz.js} +1 -1
  22. package/dist/{ask-dE3U3ss2.js → ask-Chu7Twu_.js} +2 -2
  23. package/dist/auth-BOyXPOtu.js +1 -0
  24. package/dist/auth-XbqKSbcD.js +1 -0
  25. package/dist/{chat-bridge-BS9WtP3X.js → chat-bridge-DXgZItGP.js} +1 -1
  26. package/dist/{chat-B_yhNTo0.js → chat-cknlIhVh.js} +1 -1
  27. package/dist/{cli-BilSDkvE.js → cli-B1ObVrL4.js} +5 -5
  28. package/dist/cli.d.ts +1 -1
  29. package/dist/cli.js +1 -1
  30. package/dist/{client-D9IoAuDD.js → client-31YzOj-i.js} +1 -1
  31. package/dist/{config-Da_cwoxI.js → config-DAE_qHYA.js} +1 -1
  32. package/dist/{create-C_iMJV4Y.js → create-CM8B8JxD.js} +1 -1
  33. package/dist/credentials-BXD4Qptk.js +1 -0
  34. package/dist/dash/manifest.json +934 -1014
  35. package/dist/dash/public/assets/chunks/{agent-pills-c9c4ac4d4a10df83.js → agent-pills-b6a6da91c9559d9f.js} +1 -1
  36. package/dist/dash/public/assets/chunks/{agent-settings-form-8be2ddab074aba6d.js → agent-settings-form-b615c677046cd6f2.js} +1 -1
  37. package/dist/dash/public/assets/chunks/agents-grid-f2e7b4ff9e11b6e3.js +1 -0
  38. package/dist/dash/public/assets/chunks/{badge-676b01e0c615bf7b.js → badge-215c7b2631dafc69.js} +1 -1
  39. package/dist/dash/public/assets/chunks/{card-e0901d68b79dfa93.js → card-e86dcc81326d2b14.js} +1 -1
  40. package/dist/dash/public/assets/chunks/{combo-41234c24668b1db1.js → combo-09e246dc9a517fa8.js} +1 -1
  41. package/dist/dash/public/assets/chunks/composer-417166b029f48c47.js +1 -0
  42. package/dist/dash/public/assets/chunks/composer-645ae8fa97a35280.js +1 -0
  43. package/dist/dash/public/assets/chunks/{confirm-f2fe4ba9905a89c6.js → confirm-5ae484241ad888fd.js} +1 -1
  44. package/dist/dash/public/assets/chunks/{connect-apps-aa2a3d82e48ed8c3.js → connect-apps-4147c45f45fde2b1.js} +1 -1
  45. package/dist/dash/public/assets/chunks/{connect-repositories-5389ec8701a3deac.js → connect-repositories-77591110836bdf03.js} +1 -1
  46. package/dist/dash/public/assets/chunks/credentials-list-e005a0783ec93465.js +2 -0
  47. package/dist/dash/public/assets/chunks/{custom-range-4761ab14f05149df.js → custom-range-463e119af4681c61.js} +1 -1
  48. package/dist/dash/public/assets/chunks/{editor-bfe8cd854f8001be.js → editor-804b84f4fe6539cd.js} +4 -4
  49. package/dist/dash/public/assets/chunks/{engines-list-f1191390f5715b39.js → engines-list-ceab51f13c690e8c.js} +1 -1
  50. package/dist/dash/public/assets/chunks/{field-901f1f35c18c39c9.js → field-aa0a4b98a976781e.js} +1 -1
  51. package/dist/dash/public/assets/chunks/filter-91144ac2eec6297f.js +1 -0
  52. package/dist/dash/public/assets/chunks/format-105c706345987fb2.js +1 -0
  53. package/dist/dash/public/assets/chunks/heads-a42817b32d2d7f08.js +1 -0
  54. package/dist/dash/public/assets/chunks/{instructions-052fa45a9daf6db6.js → instructions-3a1dcaff2d868aa5.js} +1 -1
  55. package/dist/dash/public/assets/chunks/instructions-6c6f981fffaa31e4.js +1 -0
  56. package/dist/dash/public/assets/chunks/{list-1d259660d7612c5f.js → list-7270361464fc3c04.js} +1 -1
  57. package/dist/dash/public/assets/chunks/{mcp-list-e3bebbbe831393b8.js → mcp-list-2e715d48666b54ad.js} +1 -1
  58. package/dist/dash/public/assets/chunks/{models-list-44f857a469ca04d5.js → models-list-d3c40f94c761a6e5.js} +1 -1
  59. package/dist/dash/public/assets/chunks/paged-3ddb6077bef42752.js +1 -0
  60. package/dist/dash/public/assets/chunks/{platform-actions-d1426602049926d2.js → platform-actions-271ec3b99a64280b.js} +1 -1
  61. package/dist/dash/public/assets/chunks/runners-list-767e49c7d0ec833a.js +1 -0
  62. package/dist/dash/public/assets/chunks/{segmented-358ff2b4992b8384.js → segmented-de4386ccaac7304d.js} +1 -1
  63. package/dist/dash/public/assets/chunks/{select-7ae25fd8a7d6c338.js → select-a90307706cffa6e0.js} +1 -1
  64. package/dist/dash/public/assets/chunks/{sentinel-c972a33dc69a5135.js → sentinel-b5e032b38604dfd3.js} +1 -1
  65. package/dist/dash/public/assets/chunks/shared-init-7f81ae7bc8c82438.js +2 -0
  66. package/dist/dash/public/assets/chunks/shared-init-933c7f9ee85be766.js +5 -0
  67. package/dist/dash/public/assets/chunks/{tail-7c27aab8ba268bd9.js → tail-4891e727ee526792.js} +1 -1
  68. package/dist/dash/public/assets/chunks/task-actions-ce854925a2dc78fd.js +1 -0
  69. package/dist/dash/public/assets/chunks/task-ddfddeaf07fb684c.js +1 -0
  70. package/dist/dash/public/assets/chunks/task-view-c37dcee08f21a279.js +5 -0
  71. package/dist/dash/public/assets/chunks/tasks-list-baef587999f322d8.js +1 -0
  72. package/dist/dash/public/assets/chunks/{toolbar-a75d2570f07cbb70.js → toolbar-8d98313606ad8a2d.js} +1 -1
  73. package/dist/dash/public/assets/chunks/{versions-list-fc2e0d2a7382e049.js → versions-list-532690efd9f1b0ea.js} +1 -1
  74. package/dist/dash/public/assets/dash-49ca85b59ac65c20.css +1 -0
  75. package/dist/dash/public/assets/dash-account-edc56e85dbaaabe0.css +1 -0
  76. package/dist/dash/public/assets/dash-agents-edba57b67e4005d0.css +1 -0
  77. package/dist/dash/public/assets/dash-agents-new-fe22253fbada2fbf.css +1 -0
  78. package/dist/dash/public/assets/dash-agents-param-slug-adf52e9dacb2d825.css +1 -0
  79. package/dist/dash/public/assets/dash-agents-param-slug-edit-a2937a347689ef29.css +1 -0
  80. package/dist/dash/public/assets/dash-agents-param-slug-platforms-265ba13db32a460d.css +1 -0
  81. package/dist/dash/public/assets/dash-agents-param-slug-settings-8afc52eb0372bc4b.css +1 -0
  82. package/dist/dash/public/assets/dash-agents-param-slug-tasks-6193e646be68dbd2.css +1 -0
  83. package/dist/dash/public/assets/dash-agents-param-slug-usage-ceadb3a1c911c402.css +1 -0
  84. package/dist/dash/public/assets/dash-settings-cf1997cd7f72e518.css +1 -0
  85. package/dist/dash/public/assets/dash-settings-param-tab-b44dac8e79a560e6.css +1 -0
  86. package/dist/dash/public/assets/dash-tasks-916c7100a8058aa3.css +1 -0
  87. package/dist/dash/public/assets/dash-tasks-param-id-194a6ffc20e872f2.css +1 -0
  88. package/dist/dash/public/assets/dash-usage-40940b289f85e0b6.css +1 -0
  89. package/dist/dash/public/assets/pnext-client-13aee92bef5c9e3f.js +1 -0
  90. package/dist/dash/public/assets/pnext-client-1f2686b2044f0df6.js +1 -0
  91. package/dist/dash/public/assets/pnext-client-26130234ebad7067.js +1 -0
  92. package/dist/dash/public/assets/pnext-client-311e35b0076bf406.js +1 -0
  93. package/dist/dash/public/assets/pnext-client-4ade1bf75c9c3d87.js +1 -0
  94. package/dist/dash/public/assets/pnext-client-61cd31b687ede1da.js +1 -0
  95. package/dist/dash/public/assets/pnext-client-61db65dc4223217a.js +1 -0
  96. package/dist/dash/public/assets/pnext-client-6ea656e7705e3f2a.js +1 -0
  97. package/dist/dash/public/assets/pnext-client-7fd0f793f0322cb9.js +1 -0
  98. package/dist/dash/public/assets/pnext-client-88159c0840fcfa4a.js +1 -0
  99. package/dist/dash/public/assets/pnext-client-b03f0c47c066b4f3.js +1 -0
  100. package/dist/dash/public/assets/{pnext-client-1b9ab0963a8ff77b.js → pnext-client-d6478835f543f901.js} +1 -1
  101. package/dist/dash/public/assets/pnext-client-dadb8359021ed590.js +1 -0
  102. package/dist/dash/public/assets/pnext-client-de07532d6aaebf2c.js +1 -0
  103. package/dist/dash/public/assets/pnext-client-f87a31890c8ff224.js +1 -0
  104. package/dist/dash/public/assets/pnext-client-ffe1c123a8e2a350.js +1 -0
  105. package/dist/dash/server/bundle-0.1.6-b29ec45cb963d47f/entry.js +2 -0
  106. package/dist/{default-vYPT26DR.js → default-BMvUMTFp.js} +1 -1
  107. package/dist/default-B_nKaX0-.js +1 -0
  108. package/dist/{delete-D8gEwoOj.js → delete-Bjteibld.js} +2 -2
  109. package/dist/{delete-z1_86nf6.js → delete-riCIKZwj.js} +2 -2
  110. package/dist/{disable-DVKd-0Xb.js → disable-DQC56YFy.js} +1 -1
  111. package/dist/{discover-Dvr_3_Xr.js → discover-Qanr5TEO.js} +1 -1
  112. package/dist/{discover-Df-a8K4R.js → discover-lgTYwoQp.js} +2 -2
  113. package/dist/dispatch-BsW_1m7e.js +14 -0
  114. package/dist/{docs-DKJnqZqt.js → docs-DFs7lOJp.js} +1 -1
  115. package/dist/{docs-BX65z2Iu.js → docs-DKROso-4.js} +2 -2
  116. package/dist/{enable-C8Zf_wpN.js → enable-D2DqZvcU.js} +1 -1
  117. package/dist/{env-CRf6wkM2.js → env-YRY3I60r.js} +1 -1
  118. package/dist/exec-DuUkLMqg.js +11 -0
  119. package/dist/executor-vMppVLB6.js +10 -0
  120. package/dist/flow/builtin/review.js +1 -1
  121. package/dist/flow/index.d.ts +1 -1
  122. package/dist/flow/index.js +1 -1
  123. package/dist/{flow-17k25MHt.js → flow-DmERAnR8.js} +1 -1
  124. package/dist/{github-actions-DwUzm4hC.js → github-actions-C_NkHVJq.js} +1 -1
  125. package/dist/http-D01Udu4u.js +1 -0
  126. package/dist/{index-CZjFX5zV.d.ts → index-B1L4zXZH.d.ts} +2 -1
  127. package/dist/{init-CoMfFxbG.js → init-Bd6JHzBc.js} +1 -1
  128. package/dist/integrations-B2CavXR1.js +5 -0
  129. package/dist/integrations-D1Dwy1kt.js +1 -0
  130. package/dist/lib/broker.js +2 -2
  131. package/dist/{list-DX-z3SBp.js → list-4Uo-0v9s.js} +1 -1
  132. package/dist/{list-BAKvxSbh.js → list-B0ujEZhd.js} +3 -3
  133. package/dist/{list-BYiG8Zav.js → list-B5oa1Bhy.js} +2 -2
  134. package/dist/{list-BxEywhHU.js → list-BjE_l6la.js} +1 -1
  135. package/dist/{list-VToARh3C.js → list-Ck9ibCs1.js} +1 -1
  136. package/dist/{list-CIW2joV1.js → list-DhgK4Zc4.js} +1 -1
  137. package/dist/{list-DpCf4G-H.js → list-KvL7AUhD.js} +2 -2
  138. package/dist/{list-C5w01-8q.js → list-byYLBl1U.js} +1 -1
  139. package/dist/{load-BpEsAn51.js → load-SnZd7xG1.js} +1 -1
  140. package/dist/{local-H3uvqEbq.js → local-BRy1EUq2.js} +1 -1
  141. package/dist/log-view-DeJstSkw.js +3 -0
  142. package/dist/{login-DkkslfBx.js → login-C-ahtDKV.js} +1 -1
  143. package/dist/{login-BFxX4jJy.js → login-zpwke8g1.js} +1 -1
  144. package/dist/{logout-BHAtw4Z8.js → logout-CI6Kf-T6.js} +1 -1
  145. package/dist/{maintenance-C5FklUw3.js → maintenance-BAzr9Jlr.js} +1 -1
  146. package/dist/{match-CXAjDhg7.js → match-DxV1d49D.js} +1 -1
  147. package/dist/{mcp-BgKb2Cdk.js → mcp-CmcL1MCG.js} +1 -1
  148. package/dist/{mcp-CCZOAo06.js → mcp-mhXwtGQT.js} +1 -1
  149. package/dist/{migrate-N-85_N7J.js → migrate-Czh3TEK5.js} +1 -1
  150. package/dist/{migrate-C4vM7DQN.js → migrate-w7KfzUAR.js} +1 -1
  151. package/dist/{model-Bs1StDo3.js → model-JRoTH9QK.js} +1 -1
  152. package/dist/models-C-S7HqSb.js +1 -0
  153. package/dist/{prompt-CEuBMNhk.js → prompt-CMXmt2l9.js} +4 -4
  154. package/dist/{push-CUC55Bqd.js → push-B8BhTyZs.js} +1 -1
  155. package/dist/queue-CirjEskI.js +117 -0
  156. package/dist/refresh-Bmy2r1Ee.js +1 -0
  157. package/dist/{remote-miQNTVRa.js → remote-CxFnEjZl.js} +1 -1
  158. package/dist/{remove-B93LNkZI.js → remove-C15WLaXT.js} +1 -1
  159. package/dist/{remove-C5_xggx7.js → remove-CTZYjjSr.js} +1 -1
  160. package/dist/{remove-eQ8y5C5p.js → remove-CTl-H0f0.js} +1 -1
  161. package/dist/{remove-CuH8a3L9.js → remove-DX2FVo5b.js} +1 -1
  162. package/dist/rename-D1g4TnHQ.js +1 -0
  163. package/dist/{result-BEtQZHaY.js → result-D49VN8PM.js} +3 -3
  164. package/dist/result-DGWyUHYU.js +18 -0
  165. package/dist/{resume-DYzKvyEw.js → resume-ywzc7z6A.js} +2 -2
  166. package/dist/{review-oIBeECZE.js → review-ZC0xG8XY.js} +2 -2
  167. package/dist/{rotate-key-IdMYH7gg.js → rotate-key-Bf8_99ck.js} +1 -1
  168. package/dist/{run-CTtd0Znc.js → run-D7SJ-ieT.js} +2 -2
  169. package/dist/run-DoRQSC_K.js +2 -0
  170. package/dist/{run-CawRGnOg.js → run-ukCf2Mdy.js} +1 -1
  171. package/dist/runner-D-REfOUe.js +1 -0
  172. package/dist/{runner-sUpWJ_k0.js → runner-wDYhkVsM.js} +1 -1
  173. package/dist/schema-DoNO0dPh.js +1 -0
  174. package/dist/sdk.d.ts +4 -2
  175. package/dist/sdk.js +1 -1
  176. package/dist/serve-BJ_Mbm2m.js +8 -0
  177. package/dist/{serve-CPnmrKyf.js → serve-C1MMhM1F.js} +2 -2
  178. package/dist/{serve-GmRO89s5.js → serve-DtrOHaoS.js} +2 -2
  179. package/dist/{serve-CE8YV2e9.js → serve-P9pFkEnE.js} +1 -1
  180. package/dist/{serve-BTeomYxW.js → serve-yjSw0B1a.js} +1 -1
  181. package/dist/{server-Kb22cWfF.js → server-DDWTnmbx.js} +1 -1
  182. package/dist/{setup-host-GFTUzzZJ.js → setup-host-L6Rnn5Cz.js} +1 -1
  183. package/dist/{shared-DVtzWhUT.js → shared-XsUX_ZZb.js} +1 -1
  184. package/dist/{show-COTZEYgc.js → show-CU-jqJ90.js} +1 -1
  185. package/dist/{show-CMUg8SJu.js → show-DVJmB2u-.js} +1 -1
  186. package/dist/{status-D26U--p8.js → status-CBydR7LE.js} +1 -1
  187. package/dist/{steer-C6uARLp5.js → steer-Cm_sHZqs.js} +2 -2
  188. package/dist/{stop-BrGI3hSS.js → stop-DJS7DAPJ.js} +1 -1
  189. package/dist/{stop-ByCAlbmN.js → stop-TrebIIuv.js} +2 -2
  190. package/dist/store-BrNMqJgk.js +42 -0
  191. package/dist/store-j8icrG0v.js +19 -0
  192. package/dist/task-C8lmcNsf.js +1 -0
  193. package/dist/{task-DeglY79z.js → task-CpUI19o2.js} +4 -4
  194. package/dist/task-CvSqiwxo.js +1 -0
  195. package/dist/{test-NEbhlxvo.js → test-CDKz4jY_.js} +1 -1
  196. package/dist/{tools-D0Zfs8Hx.js → tools-DBFeoosk.js} +1 -1
  197. package/dist/{types-I5Cwtl_K.d.ts → types-DMGBYB_K.d.ts} +3 -1
  198. package/dist/{unalias-C9NqmSDq.js → unalias-BfQFBnuK.js} +1 -1
  199. package/dist/{update-BTxYQj7Z.js → update-C4FFOie8.js} +1 -1
  200. package/dist/{upgrade-hA0yWBvx.js → upgrade-DLJqMnUQ.js} +2 -2
  201. package/dist/{usage-BhqoCjvE.js → usage-CSh-xwQ2.js} +1 -1
  202. package/dist/{usage-C4d9X2Uq.js → usage-D8mc4qXK.js} +1 -1
  203. package/dist/{watch-DtRuC0Wc.js → watch-CQu-tJKZ.js} +2 -2
  204. package/dist/{watch-CSdj24ct.js → watch-DmwaEvoi.js} +2 -2
  205. package/dist/{wire-BeT1Axeo.js → wire-DzGLZ3-O.js} +1 -1
  206. package/dist/worker-BLKTMLqa.js +1 -0
  207. package/dist/worker-nHemduM_.js +9 -0
  208. package/dist/{workflow-Dcmdooi8.js → workflow-IrouAus_.js} +1 -1
  209. package/docs/cli.md +145 -0
  210. package/docs/mcp.md +1 -1
  211. package/migrations/0000_initial.sql +8 -0
  212. package/migrations/meta/0000_snapshot.json +75 -1
  213. package/migrations/meta/_journal.json +1 -1
  214. package/package.json +3 -2
  215. package/plugins/agents/skills/coder/SKILL.md +1 -0
  216. package/plugins/claude/.claude-plugin/plugin.json +1 -1
  217. package/plugins/claude/skills/dispatch/SKILL.md +1 -1
  218. package/plugins/cursor/.cursor-plugin/plugin.json +1 -1
  219. package/plugins/cursor/skills/coder/SKILL.md +1 -0
  220. package/src/client/index.ts +10 -2
  221. package/src/client/types.ts +18 -0
  222. package/src/core/defaults.ts +36 -0
  223. package/src/core/task/log-view.ts +120 -6
  224. package/src/server/dash/app/dash/agents/(list)/agent-menu.tsx +4 -0
  225. package/src/server/dash/app/dash/agents/(list)/agents-grid.tsx +18 -35
  226. package/src/server/dash/app/dash/agents/(list)/loading.tsx +7 -1
  227. package/src/server/dash/app/dash/agents/[slug]/(tabs)/layout.tsx +2 -1
  228. package/src/server/dash/app/dash/layout.css +58 -1
  229. package/src/server/dash/app/dash/layout.tsx +29 -7
  230. package/src/server/dash/app/dash/settings/[tab]/credentials-list.tsx +20 -13
  231. package/src/server/dash/app/dash/settings/[tab]/page.tsx +1 -1
  232. package/src/server/dash/app/dash/settings/[tab]/runners-list.tsx +13 -12
  233. package/src/server/dash/app/dash/tasks/[id]/activity.tsx +49 -4
  234. package/src/server/dash/app/dash/tasks/[id]/task-actions.tsx +43 -6
  235. package/src/server/dash/app/dash/tasks/[id]/task-view.css +52 -31
  236. package/src/server/dash/app/dash/tasks/[id]/task-view.tsx +143 -51
  237. package/src/server/dash/app/dash/tasks/composer.css +4 -0
  238. package/src/server/dash/app/dash/tasks/composer.tsx +6 -5
  239. package/src/server/dash/app/dash/tasks/list/task.ts +29 -1
  240. package/src/server/dash/app/dash/tasks/list/tasks-list.css +20 -7
  241. package/src/server/dash/app/dash/tasks/list/tasks-list.tsx +256 -109
  242. package/src/server/dash/app/dash/tasks/list/tasks-section.tsx +9 -13
  243. package/src/server/dash/app/dash/tasks/page.tsx +3 -35
  244. package/src/server/dash/comps/frame/loading-card.tsx +4 -3
  245. package/src/server/dash/comps/frame/page-head.css +10 -0
  246. package/src/server/dash/comps/frame/page-head.tsx +3 -2
  247. package/src/server/dash/comps/frame/task-toasts.tsx +196 -50
  248. package/src/server/dash/comps/nav/side-toggle.tsx +16 -5
  249. package/src/server/dash/comps/ui/card.css +1 -0
  250. package/src/server/dash/comps/ui/filter.css +64 -0
  251. package/src/server/dash/comps/ui/filter.tsx +336 -0
  252. package/src/server/dash/comps/ui/icons.ts +2 -0
  253. package/src/server/dash/comps/ui/menu.tsx +9 -1
  254. package/src/server/dash/comps/ui/select.tsx +2 -2
  255. package/src/server/dash/comps/ui/toast.css +3 -2
  256. package/src/server/dash/comps/ui/toolbar.css +14 -0
  257. package/src/server/dash/utils/client.ts +21 -0
  258. package/src/server/dash/utils/format.ts +12 -4
  259. package/src/server/dash/utils/paged.ts +14 -6
  260. package/dist/actions-Mk3FSlpJ.js +0 -20
  261. package/dist/archive-sweep-CtBcARgN.js +0 -1
  262. package/dist/auth-BFny--17.js +0 -1
  263. package/dist/auth-DWHIzqbA.js +0 -1
  264. package/dist/credentials-B1jv6dkg.js +0 -1
  265. package/dist/dash/public/assets/chunks/agents-grid-951584b6f4c4314e.js +0 -1
  266. package/dist/dash/public/assets/chunks/composer-5534be50bc12385e.js +0 -1
  267. package/dist/dash/public/assets/chunks/composer-d10d4b1d54c4f751.js +0 -1
  268. package/dist/dash/public/assets/chunks/credentials-list-bc5bfcab058176c5.js +0 -2
  269. package/dist/dash/public/assets/chunks/format-eb03fc9d9744d960.js +0 -1
  270. package/dist/dash/public/assets/chunks/heads-f3bcd289663d48fa.js +0 -1
  271. package/dist/dash/public/assets/chunks/instructions-d0a4b0d95009c262.js +0 -1
  272. package/dist/dash/public/assets/chunks/paged-bc6fa0c2b9d52ed7.js +0 -1
  273. package/dist/dash/public/assets/chunks/runners-list-1901af8a5dea1f09.js +0 -1
  274. package/dist/dash/public/assets/chunks/shared-init-88cce66bb3cfe1e5.js +0 -5
  275. package/dist/dash/public/assets/chunks/shared-init-ffb3397e494137c9.js +0 -2
  276. package/dist/dash/public/assets/chunks/task-actions-8e1d93c788d001df.js +0 -1
  277. package/dist/dash/public/assets/chunks/task-fa8b82e95e771c4a.js +0 -1
  278. package/dist/dash/public/assets/chunks/task-view-6d7618d2820a5cb4.js +0 -3
  279. package/dist/dash/public/assets/chunks/tasks-list-f4784e5cbd0ed785.js +0 -1
  280. package/dist/dash/public/assets/dash-9e02d5d1a6d42ee3.css +0 -1
  281. package/dist/dash/public/assets/dash-account-04859899c1838ada.css +0 -1
  282. package/dist/dash/public/assets/dash-agents-9e678137267dec07.css +0 -1
  283. package/dist/dash/public/assets/dash-agents-new-26fa27b17ef9b8d8.css +0 -1
  284. package/dist/dash/public/assets/dash-agents-param-slug-b975c48f8abf3444.css +0 -1
  285. package/dist/dash/public/assets/dash-agents-param-slug-edit-e13fda599bb04843.css +0 -1
  286. package/dist/dash/public/assets/dash-agents-param-slug-platforms-70292932cb27bc96.css +0 -1
  287. package/dist/dash/public/assets/dash-agents-param-slug-settings-71f9a86251723fcc.css +0 -1
  288. package/dist/dash/public/assets/dash-agents-param-slug-tasks-fb8eb2ed71006601.css +0 -1
  289. package/dist/dash/public/assets/dash-agents-param-slug-usage-7c8740f1cdd52c71.css +0 -1
  290. package/dist/dash/public/assets/dash-settings-8eb818da462227e8.css +0 -1
  291. package/dist/dash/public/assets/dash-settings-param-tab-849a1a9542e273f5.css +0 -1
  292. package/dist/dash/public/assets/dash-tasks-81e510e0b6931b42.css +0 -1
  293. package/dist/dash/public/assets/dash-tasks-param-id-bbd2a83342f2ec10.css +0 -1
  294. package/dist/dash/public/assets/dash-usage-8e0259e03eaed1bd.css +0 -1
  295. package/dist/dash/public/assets/pnext-client-03b03b254e525e82.js +0 -1
  296. package/dist/dash/public/assets/pnext-client-1c7859bb888f4ef0.js +0 -1
  297. package/dist/dash/public/assets/pnext-client-3c748afe963fb05d.js +0 -1
  298. package/dist/dash/public/assets/pnext-client-4f47c2cae220f107.js +0 -1
  299. package/dist/dash/public/assets/pnext-client-6ab9fd9b53b794d2.js +0 -1
  300. package/dist/dash/public/assets/pnext-client-6c241786342498e4.js +0 -1
  301. package/dist/dash/public/assets/pnext-client-80b56d420f02b808.js +0 -1
  302. package/dist/dash/public/assets/pnext-client-83135c17bae850a3.js +0 -1
  303. package/dist/dash/public/assets/pnext-client-8e6fa4302ae0396f.js +0 -1
  304. package/dist/dash/public/assets/pnext-client-932c9d8cc0717fc8.js +0 -1
  305. package/dist/dash/public/assets/pnext-client-a42ae63d4de113f9.js +0 -1
  306. package/dist/dash/public/assets/pnext-client-d2ee6f8c1e337032.js +0 -1
  307. package/dist/dash/public/assets/pnext-client-d513f65ec23b7f20.js +0 -1
  308. package/dist/dash/public/assets/pnext-client-ecb88e2a8a109954.js +0 -1
  309. package/dist/dash/public/assets/pnext-client-f1545ee6270bbf9a.js +0 -1
  310. package/dist/dash/server/bundle-0.1.5-1508b6c99c0d1e68/boot-B2MFTRO2.js +0 -30
  311. package/dist/dash/server/bundle-0.1.5-1508b6c99c0d1e68/build-tier-XJJSL5FR.js +0 -2704
  312. package/dist/dash/server/bundle-0.1.5-1508b6c99c0d1e68/chunk-2HFY3DQT.js +0 -208
  313. package/dist/dash/server/bundle-0.1.5-1508b6c99c0d1e68/chunk-2ICEVELW.js +0 -834
  314. package/dist/dash/server/bundle-0.1.5-1508b6c99c0d1e68/chunk-3UPROT2C.js +0 -35
  315. package/dist/dash/server/bundle-0.1.5-1508b6c99c0d1e68/chunk-4P5CMIYT.js +0 -1582
  316. package/dist/dash/server/bundle-0.1.5-1508b6c99c0d1e68/chunk-4TKZKZ3Q.js +0 -131
  317. package/dist/dash/server/bundle-0.1.5-1508b6c99c0d1e68/chunk-5RDEILQB.js +0 -437
  318. package/dist/dash/server/bundle-0.1.5-1508b6c99c0d1e68/chunk-62KEQM43.js +0 -25
  319. package/dist/dash/server/bundle-0.1.5-1508b6c99c0d1e68/chunk-6KQSWT6D.js +0 -831
  320. package/dist/dash/server/bundle-0.1.5-1508b6c99c0d1e68/chunk-7DCNCW6O.js +0 -724
  321. package/dist/dash/server/bundle-0.1.5-1508b6c99c0d1e68/chunk-B4THCTQP.js +0 -22
  322. package/dist/dash/server/bundle-0.1.5-1508b6c99c0d1e68/chunk-C2LIV2MM.js +0 -172
  323. package/dist/dash/server/bundle-0.1.5-1508b6c99c0d1e68/chunk-CCATPVTW.js +0 -378
  324. package/dist/dash/server/bundle-0.1.5-1508b6c99c0d1e68/chunk-COXJAPTU.js +0 -89
  325. package/dist/dash/server/bundle-0.1.5-1508b6c99c0d1e68/chunk-E6IXL2NJ.js +0 -6270
  326. package/dist/dash/server/bundle-0.1.5-1508b6c99c0d1e68/chunk-FLGRBFAH.js +0 -1198
  327. package/dist/dash/server/bundle-0.1.5-1508b6c99c0d1e68/chunk-FTJVHFWD.js +0 -61
  328. package/dist/dash/server/bundle-0.1.5-1508b6c99c0d1e68/chunk-H523KSIH.js +0 -5151
  329. package/dist/dash/server/bundle-0.1.5-1508b6c99c0d1e68/chunk-HT6KKX6A.js +0 -20
  330. package/dist/dash/server/bundle-0.1.5-1508b6c99c0d1e68/chunk-JESFYECC.js +0 -438
  331. package/dist/dash/server/bundle-0.1.5-1508b6c99c0d1e68/chunk-JRPJM2XY.js +0 -17
  332. package/dist/dash/server/bundle-0.1.5-1508b6c99c0d1e68/chunk-L2IWDUXG.js +0 -107
  333. package/dist/dash/server/bundle-0.1.5-1508b6c99c0d1e68/chunk-L5P4Y4UC.js +0 -183
  334. package/dist/dash/server/bundle-0.1.5-1508b6c99c0d1e68/chunk-NQE46AZN.js +0 -647
  335. package/dist/dash/server/bundle-0.1.5-1508b6c99c0d1e68/chunk-PA4QEARJ.js +0 -129
  336. package/dist/dash/server/bundle-0.1.5-1508b6c99c0d1e68/chunk-PZ5AXOLW.js +0 -7146
  337. package/dist/dash/server/bundle-0.1.5-1508b6c99c0d1e68/chunk-SNVPTULL.js +0 -2397
  338. package/dist/dash/server/bundle-0.1.5-1508b6c99c0d1e68/chunk-T3QZT3PG.js +0 -220
  339. package/dist/dash/server/bundle-0.1.5-1508b6c99c0d1e68/chunk-VC5O2SDP.js +0 -72
  340. package/dist/dash/server/bundle-0.1.5-1508b6c99c0d1e68/chunk-WZX4V6CM.js +0 -231
  341. package/dist/dash/server/bundle-0.1.5-1508b6c99c0d1e68/chunk-XUUIDXY5.js +0 -627
  342. package/dist/dash/server/bundle-0.1.5-1508b6c99c0d1e68/chunk-ZHFDO3PR.js +0 -265
  343. package/dist/dash/server/bundle-0.1.5-1508b6c99c0d1e68/compile-CE2DN7ZA.js +0 -18
  344. package/dist/dash/server/bundle-0.1.5-1508b6c99c0d1e68/config-UREY7XJT.js +0 -16
  345. package/dist/dash/server/bundle-0.1.5-1508b6c99c0d1e68/config-loader-UI2XE3PZ.js +0 -17
  346. package/dist/dash/server/bundle-0.1.5-1508b6c99c0d1e68/entry.js +0 -219
  347. package/dist/dash/server/bundle-0.1.5-1508b6c99c0d1e68/generate-I2PPTDFD.js +0 -16
  348. package/dist/dash/server/bundle-0.1.5-1508b6c99c0d1e68/modules-UVOCE4C2.js +0 -48
  349. package/dist/dash/server/bundle-0.1.5-1508b6c99c0d1e68/pipeline-UW47K537.js +0 -963
  350. package/dist/dash/server/bundle-0.1.5-1508b6c99c0d1e68/proxy-ITGOVJBW.js +0 -26
  351. package/dist/dash/server/bundle-0.1.5-1508b6c99c0d1e68/register-4SA55SWT.js +0 -12579
  352. package/dist/dash/server/bundle-0.1.5-1508b6c99c0d1e68/router-JNKHLBCZ.js +0 -505
  353. package/dist/dash/server/bundle-0.1.5-1508b6c99c0d1e68/routes-PD6J2NXW.js +0 -54
  354. package/dist/dash/server/bundle-0.1.5-1508b6c99c0d1e68/tsconfig-defaults-ZXEH7RQK.js +0 -15
  355. package/dist/default-Bobztoxf.js +0 -1
  356. package/dist/dispatch-BKeAch9E.js +0 -14
  357. package/dist/exec-RZFLclKX.js +0 -11
  358. package/dist/executor-BvAA7Cgw.js +0 -10
  359. package/dist/http-Bkg_OQ6r.js +0 -1
  360. package/dist/integrations-5xCaXSd-.js +0 -1
  361. package/dist/integrations-BAgdVDhj.js +0 -5
  362. package/dist/log-view-dg0TlKLA.js +0 -1
  363. package/dist/models-_5LVGC3m.js +0 -1
  364. package/dist/queue-6io_NOP3.js +0 -116
  365. package/dist/refresh-BIrmrWYq.js +0 -1
  366. package/dist/rename-CYfy03FE.js +0 -1
  367. package/dist/result-BzaM3inj.js +0 -15
  368. package/dist/run-BDYl0CKl.js +0 -2
  369. package/dist/runner-D7ScbIHJ.js +0 -1
  370. package/dist/schema-CU2iFkp8.js +0 -1
  371. package/dist/serve-CQKZxzFE.js +0 -4
  372. package/dist/store-B94tSate.js +0 -42
  373. package/dist/store-DzqXS2wT.js +0 -8
  374. package/dist/task-C6VAg5WU.js +0 -1
  375. package/dist/task-CgOKgkEG.js +0 -1
  376. package/dist/worker-CXndxmGF.js +0 -9
  377. package/dist/worker-DX7P9kWl.js +0 -1
  378. package/src/server/dash/app/dash/token-from-hash.tsx +0 -17
@@ -37,12 +37,16 @@ export interface TaskDisplayRow {
37
37
  tool?: string;
38
38
  exitCode?: number;
39
39
  tokens?: TokenUsage;
40
+ result?: { ok: boolean; exitCode?: number; durationMs: number; detail: string };
41
+ state?: string;
40
42
  }
41
43
 
42
44
  export const isApproval = (kind: string) =>
43
45
  kind.includes('approval') || kind === 'sidecar-decision' || kind === 'auto-review';
44
46
 
45
- function stripCwd(text: string, cwd?: string): string {
47
+ const ANSI = /\x1b\[[0-9;?]*[ -/]*[@-~]|\x1b\][^\x07\x1b]*(?:\x07|\x1b\\)/g;
48
+
49
+ export function stripCwd(text: string, cwd?: string): string {
46
50
  return cwd ? text.split(`${cwd}/`).join('').split(cwd).join('.') : text;
47
51
  }
48
52
 
@@ -70,19 +74,38 @@ export function logEntry(line: string): TaskLogEntry | undefined {
70
74
  export class LogView {
71
75
  private prevKind: string | null = null;
72
76
  private lastAssistant = '';
73
- private lastTool: string | null = null;
74
77
  private lastToolText = '';
78
+ private openCalls: TaskDisplayRow[] = [];
79
+ // Engine invocation ids, so concurrent calls of one tool close with their own results.
80
+ private callIds = new WeakMap<TaskDisplayRow, string>();
81
+ private approval: TaskDisplayRow | undefined;
82
+ private approvals = new Map<string, TaskDisplayRow>();
75
83
  private lastUsageTotal = 0;
76
84
 
77
- constructor(private readonly cwd?: string) {}
85
+ constructor(
86
+ private readonly cwd?: string,
87
+ private readonly opts: { keepAnsi?: boolean } = {},
88
+ ) {}
78
89
 
79
90
  get lastAssistantMessage(): string {
80
91
  return this.lastAssistant;
81
92
  }
82
93
 
94
+ /** A result or verdict that completes an earlier call or approval returns that row again, keyed by its seq. */
83
95
  reduce(record: TaskLog): TaskDisplayRow[] {
84
96
  const entry = record.entry;
85
97
  const sourceKind = entry ? String(entry.kind ?? 'status') : 'line';
98
+ const earlier = !entry
99
+ ? undefined
100
+ : sourceKind === 'tool-result'
101
+ ? this.close(record, entry)
102
+ : isApproval(sourceKind)
103
+ ? this.decide(record, entry, sourceKind)
104
+ : undefined;
105
+ if (earlier) {
106
+ this.prevKind = sourceKind;
107
+ return [earlier];
108
+ }
86
109
  const row: TaskDisplayRow = {
87
110
  seq: record.seq,
88
111
  at: record.at,
@@ -109,6 +132,25 @@ export class LogView {
109
132
  row.kind = 'usage';
110
133
  row.tokens = tokens;
111
134
  row.title = `ctx ${tokenCount(tokens.input + tokens.cachedInput)} · out ${tokenCount(tokens.output)}`;
135
+ } else if (
136
+ (sourceKind === 'approval-decision' && entry.decision === 'escalate') ||
137
+ sourceKind === 'approval-escalated'
138
+ ) {
139
+ const summary = stripCwd(entry.summary ? String(entry.summary) : '', this.cwd);
140
+ const target =
141
+ /^(?:run command: |Run command\. |Apply file changes\. |\w+: )?([\s\S]*)$/.exec(
142
+ summary,
143
+ )![1]!;
144
+ const method = String(entry.method ?? '');
145
+ const name = method && !method.includes('/') ? method : this.openCalls.at(-1)?.tool;
146
+ const word = target.startsWith('{') ? '' : target.split(/\s+/)[0];
147
+ const reason = stripCwd(entry.reason ? String(entry.reason) : '', this.cwd);
148
+ row.kind = 'approval';
149
+ row.title = ['Approval', [name, word].filter(Boolean).join(' ')].filter(Boolean).join(' · ');
150
+ row.detail = [target, reason && `Policy · ${reason}`].filter(Boolean).join('\n');
151
+ row.state = sourceKind === 'approval-escalated' ? 'waiting' : 'escalated';
152
+ if (sourceKind === 'approval-escalated') this.approvals.set(String(entry.approvalId), row);
153
+ else this.approval = row;
112
154
  } else if (isApproval(sourceKind) && (entry.decision || !entry.message)) {
113
155
  row.kind = 'approval';
114
156
  const label =
@@ -141,18 +183,20 @@ export class LogView {
141
183
  row.durationMs = typeof entry.durationMs === 'number' ? entry.durationMs : 0;
142
184
  } else if (sourceKind === 'tool') {
143
185
  row.kind = 'tool';
144
- this.lastTool = entry.tool ? String(entry.tool) : null;
186
+ if (entry.tool) row.tool = String(entry.tool);
145
187
  this.lastToolText = row.title;
188
+ if (entry.callId) this.callIds.set(row, String(entry.callId));
189
+ this.openCalls.push(row);
146
190
  } else if (sourceKind === 'tool-result') {
147
191
  row.kind = 'tool-result';
192
+ row.title = this.output(raw);
148
193
  row.exitCode = typeof entry.exitCode === 'number' ? entry.exitCode : undefined;
149
194
  row.tone =
150
195
  (row.exitCode !== undefined && row.exitCode !== 0) || entry.isError === true
151
196
  ? 'error'
152
197
  : 'muted';
153
198
  row.durationMs = typeof entry.durationMs === 'number' ? entry.durationMs : 0;
154
- const tool = entry.tool ? String(entry.tool) : null;
155
- if (tool && tool !== this.lastTool) row.tool = tool;
199
+ if (entry.tool) row.tool = String(entry.tool);
156
200
  }
157
201
  }
158
202
  if (this.prevKind !== null) {
@@ -164,6 +208,76 @@ export class LogView {
164
208
  this.prevKind = sourceKind;
165
209
  return [row];
166
210
  }
211
+
212
+ // A result closes the call with its invocation id; without one, the oldest open call of its tool, or the latest call.
213
+ private close(record: TaskLog, entry: TaskLogEntry): TaskDisplayRow | undefined {
214
+ const tool = entry.tool ? String(entry.tool) : undefined;
215
+ const byId = entry.callId
216
+ ? this.openCalls.findIndex(call => this.callIds.get(call) === String(entry.callId))
217
+ : -1;
218
+ const index =
219
+ byId >= 0
220
+ ? byId
221
+ : tool
222
+ ? this.openCalls.findIndex(call => call.tool === tool)
223
+ : this.openCalls.length - 1;
224
+ if (index < 0) return undefined;
225
+
226
+ const [call] = this.openCalls.splice(index, 1);
227
+ const exitCode = typeof entry.exitCode === 'number' ? entry.exitCode : undefined;
228
+ const durationMs =
229
+ typeof entry.durationMs === 'number' ? entry.durationMs : record.at - call!.at || 0;
230
+ return {
231
+ ...call!,
232
+ result: {
233
+ ok: !exitCode && entry.isError !== true,
234
+ exitCode,
235
+ durationMs: Math.max(0, durationMs),
236
+ detail: this.output(entry.message ? String(entry.message) : ''),
237
+ },
238
+ };
239
+ }
240
+
241
+ // Sidecar verdicts and escalations follow the policy verdict before them; answers carry the approval id.
242
+ private decide(record: TaskLog, entry: TaskLogEntry, kind: string): TaskDisplayRow | undefined {
243
+ const id = String(entry.approvalId ?? '');
244
+ const accepted = entry.decision === 'accept';
245
+ if (kind === 'sidecar-decision' && this.approval) {
246
+ const reason = stripCwd(entry.reason ? String(entry.reason) : '', this.cwd);
247
+ const row = {
248
+ ...this.approval,
249
+ detail: [this.approval.detail, reason && `Sidecar · ${reason}`].filter(Boolean).join('\n'),
250
+ };
251
+ if (entry.decision === 'escalate') return (this.approval = row);
252
+ this.approval = undefined;
253
+ return settle(row, record, `${accepted ? 'accepted' : 'denied'} by sidecar`, accepted);
254
+ }
255
+ if (kind === 'approval-escalated' && this.approval) {
256
+ const row = { ...this.approval, state: 'waiting' };
257
+ this.approval = undefined;
258
+ this.approvals.set(id, row);
259
+ return row;
260
+ }
261
+ const open = this.approvals.get(id);
262
+ if (!open || (kind !== 'approval-answered' && kind !== 'approval-timeout')) return undefined;
263
+
264
+ this.approvals.delete(id);
265
+ return kind === 'approval-timeout'
266
+ ? settle(open, record, 'timed out', false)
267
+ : settle(open, record, `${accepted ? 'accepted' : 'denied'} by reviewer`, accepted);
268
+ }
269
+
270
+ private output(text: string): string {
271
+ return stripCwd(this.opts.keepAnsi ? text : text.replace(ANSI, ''), this.cwd);
272
+ }
273
+ }
274
+
275
+ function settle(row: TaskDisplayRow, record: TaskLog, state: string, ok: boolean): TaskDisplayRow {
276
+ return {
277
+ ...row,
278
+ state,
279
+ result: { ok, durationMs: Math.max(0, record.at - row.at || 0), detail: '' },
280
+ };
167
281
  }
168
282
 
169
283
  function tokenCount(n: number): string {
@@ -1,9 +1,13 @@
1
1
  import { Link } from '@wular/pnext/link';
2
2
 
3
+ import { iAgent } from '@/comps/ui/icons';
4
+ import { Icon } from '@/comps/ui/icon';
5
+
3
6
  /** Make a dashboard agent; importing from a repository starts from the same page. */
4
7
  export function NewAgentMenu() {
5
8
  return (
6
9
  <Link class="btn" href="/dash/agents/new">
10
+ <Icon d={iAgent} />
7
11
  New agent
8
12
  </Link>
9
13
  );
@@ -9,8 +9,7 @@ import type { AgentCardRow, IntegrationInfo, Paged } from '@coder/client/types';
9
9
  import { Empty } from '@/comps/ui/card';
10
10
  import { initial } from '@/utils/format';
11
11
  import { AgentPills, BrandIcon, Platform } from '@/app/dash/agents/agent/agent-pills';
12
- import { Menu, MenuRadio } from '@/comps/ui/menu';
13
- import { Icon } from '@/comps/ui/icon';
12
+ import { Filter } from '@/comps/ui/filter';
14
13
  import { NewAgentMenu } from './agent-menu';
15
14
  import { Search, Toolbar, queryOf, useFilters } from '@/comps/ui/toolbar';
16
15
  import { Tail } from '@/comps/ui/tail';
@@ -93,39 +92,23 @@ export function AgentsGrid({
93
92
  <>
94
93
  <Toolbar head={head} action={<NewAgentMenu />}>
95
94
  <Search value={values.q} label="Search agents" onInput={value => set('q', value, 250)} />
96
- <Menu
97
- class="filter-more"
98
- label="More filters"
99
- summaryClass={`field-btn${values.platform || values.type ? ' on' : ''}`}
100
- summary="Filter"
101
- >
102
- <p class="pop-label">Platform</p>
103
- <div role="group" aria-label="Platform">
104
- <MenuRadio on={!values.platform} onPick={() => set('platform', '')}>
105
- All platforms
106
- </MenuRadio>
107
- {catalog.map(entry => (
108
- <MenuRadio
109
- key={entry.id}
110
- on={values.platform === entry.id}
111
- onPick={() => set('platform', entry.id)}
112
- >
113
- <span class="pf">
114
- <BrandIcon brand={entry.brand} />
115
- {entry.name}
116
- </span>
117
- </MenuRadio>
118
- ))}
119
- </div>
120
- <p class="pop-label">Source</p>
121
- <div role="group" aria-label="Source">
122
- {KINDS.map(([value, label]) => (
123
- <MenuRadio key={value} on={values.type === value} onPick={() => set('type', value)}>
124
- {label}
125
- </MenuRadio>
126
- ))}
127
- </div>
128
- </Menu>
95
+ <Filter
96
+ name="agents"
97
+ facets={[
98
+ {
99
+ key: 'platform',
100
+ label: 'Platform',
101
+ options: catalog.map((entry): [string, string] => [entry.id, entry.name]),
102
+ icon: value => {
103
+ const entry = catalog.find(item => item.id === value);
104
+ return entry ? <BrandIcon brand={entry.brand} /> : undefined;
105
+ },
106
+ },
107
+ { key: 'type', label: 'Source', options: KINDS },
108
+ ]}
109
+ values={values}
110
+ onChange={set}
111
+ />
129
112
  </Toolbar>
130
113
  {children}
131
114
  {list.rows.length ? (
@@ -1,4 +1,5 @@
1
1
  import { HEADS } from '@/comps/frame/heads';
2
+ import { iAgent } from '@/comps/ui/icons';
2
3
  import { Page } from '@/comps/frame/page-head';
3
4
  import { LoadingToolbar } from '@/comps/frame/loading-card';
4
5
  import { Loading } from '@/comps/ui/card';
@@ -7,7 +8,12 @@ import { Loading } from '@/comps/ui/card';
7
8
  export default function AgentsLoading() {
8
9
  return (
9
10
  <Page>
10
- <LoadingToolbar head={HEADS.agents} search="Search agents" more action="New agent" />
11
+ <LoadingToolbar
12
+ head={HEADS.agents}
13
+ search="Search agents"
14
+ more
15
+ action={{ label: 'New agent', icon: iAgent }}
16
+ />
11
17
  <Loading label="Loading agents" />
12
18
  </Page>
13
19
  );
@@ -1,7 +1,7 @@
1
1
  import type { ComponentChildren } from 'preact';
2
2
  import { dynamic } from '@wular/pnext/dynamic';
3
3
 
4
- import { iGit, iPencil } from '@/comps/ui/icons';
4
+ import { iGit, iPencil, iPlus } from '@/comps/ui/icons';
5
5
  import { Link } from '@wular/pnext/link';
6
6
  import { load } from '@/api/load';
7
7
  import type { AgentRow, TasksPage } from '@coder/client/types';
@@ -56,6 +56,7 @@ function LoadingAgentHead({ back }: { back: ComponentChildren }) {
56
56
  back={back}
57
57
  actions={
58
58
  <button type="button" class="btn" disabled>
59
+ <Icon d={iPlus} />
59
60
  New task
60
61
  </button>
61
62
  }
@@ -40,7 +40,7 @@
40
40
  .side {
41
41
  position: sticky;
42
42
  top: 56px;
43
- z-index: 5;
43
+ z-index: 12;
44
44
  height: calc(100vh - 56px);
45
45
  display: flex;
46
46
  flex-direction: column;
@@ -248,6 +248,63 @@
248
248
  .side-min .side nav .n {
249
249
  display: none;
250
250
  }
251
+ /* collapsed icons and the sidebar toggle name themselves on hover and keyboard focus */
252
+ .side-min .side [data-tip],
253
+ .side-toggle {
254
+ position: relative;
255
+ }
256
+ .side-min .side [data-tip]::after,
257
+ .side-toggle::after {
258
+ content: attr(data-tip) / '';
259
+ position: absolute;
260
+ top: 50%;
261
+ left: calc(100% + 12px);
262
+ z-index: 30;
263
+ padding: 6px 10px;
264
+ border: 1px solid var(--line2);
265
+ background: var(--tile);
266
+ box-shadow: 0 12px 32px rgba(0, 0, 0, 0.18);
267
+ color: var(--fg);
268
+ font-size: 12px;
269
+ letter-spacing: normal;
270
+ white-space: nowrap;
271
+ transform: translateY(-50%);
272
+ pointer-events: none;
273
+ opacity: 0;
274
+ transition: opacity 0.1s;
275
+ }
276
+ /* a small arrow on the tooltip's edge, pointing back at the icon */
277
+ .side-min .side [data-tip]::before,
278
+ .side-toggle::before {
279
+ content: '';
280
+ position: absolute;
281
+ top: 50%;
282
+ left: calc(100% + 8px);
283
+ z-index: 31;
284
+ width: 8px;
285
+ height: 8px;
286
+ border-bottom: 1px solid var(--line2);
287
+ border-left: 1px solid var(--line2);
288
+ background: var(--tile);
289
+ transform: translateY(-50%) rotate(45deg);
290
+ pointer-events: none;
291
+ opacity: 0;
292
+ transition: opacity 0.1s;
293
+ }
294
+ .side-min .side [data-tip]:is(:hover, :focus-visible)::before,
295
+ .side-min .side [data-tip]:is(:hover, :focus-visible)::after,
296
+ .side-toggle:is(:hover, :focus-visible)::before,
297
+ .side-toggle:is(:hover, :focus-visible)::after {
298
+ opacity: 1;
299
+ }
300
+ .side-toggle:is(:hover, :focus-visible)::before,
301
+ .side-toggle:is(:hover, :focus-visible)::after {
302
+ transition-delay: 0.7s;
303
+ }
304
+ .side-min .menu:has(:popover-open) > [data-tip]::before,
305
+ .side-min .menu:has(:popover-open) > [data-tip]::after {
306
+ opacity: 0;
307
+ }
251
308
  .side-min .server > button {
252
309
  justify-content: center;
253
310
  padding: 10px 0;
@@ -31,7 +31,6 @@ import { Drawer } from '@/comps/frame/drawer';
31
31
  import { SignOut } from '@/app/dash/account/sign-out';
32
32
  import { CurrentLink } from '@/comps/nav/current-link';
33
33
  import { SideToggle } from '@/comps/nav/side-toggle';
34
- import { TokenFromHash } from './token-from-hash';
35
34
  import type { To } from '@/comps/nav/to';
36
35
  import { Menu } from '@/comps/ui/menu';
37
36
  import { TaskToasts } from '@/comps/frame/task-toasts';
@@ -170,17 +169,19 @@ function ServerMenu({ me, host }: { me?: Me; host: string }) {
170
169
  const memory = !me?.user;
171
170
  const local = me?.mode === 'local';
172
171
  const url = me?.server?.url ?? `https://${host}`;
172
+ const name = local ? 'Local server' : memory ? 'Memory server' : 'Cloud server';
173
173
 
174
174
  return (
175
175
  <Menu
176
176
  class="server"
177
177
  align="up"
178
178
  label="Server"
179
+ tip={name}
179
180
  summary={
180
181
  <>
181
182
  <i class={memory && !local ? 'dot' : 'dot ok'} />
182
183
  <span class="grow" title={`${me?.server?.name ?? 'Coder'} · ${new URL(url).host}`}>
183
- {local ? 'Local server' : memory ? 'Memory server' : 'Cloud server'}
184
+ {name}
184
185
  </span>
185
186
  <Icon d={iUpdown} />
186
187
  </>
@@ -212,6 +213,26 @@ async function ActiveCount({ count }: { count: Promise<TasksPage> }) {
212
213
  ) : null;
213
214
  }
214
215
 
216
+ // Without a saved choice, mid-width screens start with the sidebar collapsed, before first paint.
217
+ const SIDE_AUTO = `(() => {
218
+ const app = document.currentScript.parentElement;
219
+ const mid = matchMedia('(min-width: 761px) and (max-width: 1279px)');
220
+ const sync = () => /(?:^|; )side=/.test(document.cookie) || app.classList.toggle('side-min', mid.matches);
221
+ sync();
222
+ mid.addEventListener('change', sync);
223
+ })()`;
224
+
225
+ // `serve` opens /dash#token=…: the fragment never reaches the server, so the form posts it while the page loads, busy from first paint.
226
+ const TOKEN_FROM_HASH = `(() => {
227
+ const token = /(?:^|&)token=([^&]+)/.exec(location.hash.slice(1))?.[1];
228
+ if (!token) return;
229
+ history.replaceState(null, '', location.pathname + location.search);
230
+ const form = document.getElementById('token-form');
231
+ form.elements.token.value = decodeURIComponent(token);
232
+ form.querySelector('button').setAttribute('aria-busy', 'true');
233
+ form.submit();
234
+ })();`;
235
+
215
236
  function TokenPage({ request }: { request: Request }) {
216
237
  const url = new URL(request.url);
217
238
  const rejected = url.searchParams.get('token') === 'rejected';
@@ -238,7 +259,7 @@ function TokenPage({ request }: { request: Request }) {
238
259
  </p>
239
260
  ) : null}
240
261
  </form>
241
- <TokenFromHash />
262
+ <script dangerouslySetInnerHTML={{ __html: TOKEN_FROM_HASH }} />
242
263
  </AuthPanel>
243
264
  );
244
265
  }
@@ -284,10 +305,11 @@ export default function DashLayout({
284
305
  const { pathname, host } = new URL(request.url);
285
306
  const me = load(request).me();
286
307
  const active = load(request).tasks.list({ cursor: '', limit: 1, summary: true, counts: true });
287
- const collapsed = /(?:^|; )side=min(?:;|$)/.test(request.headers.get('cookie') ?? '');
308
+ const side = /(?:^|; )side=(min|max)(?:;|$)/.exec(request.headers.get('cookie') ?? '')?.[1];
288
309
 
289
310
  return (
290
- <div class={collapsed ? 'app side-min' : 'app'}>
311
+ <div class={side === 'min' ? 'app side-min' : 'app'}>
312
+ {side ? null : <script dangerouslySetInnerHTML={{ __html: SIDE_AUTO }} />}
291
313
  <header class="top">
292
314
  <div class="top-l">
293
315
  <Drawer />
@@ -307,7 +329,7 @@ export default function DashLayout({
307
329
  <nav aria-label="Dashboard">
308
330
  {navFor(request.headers.get('x-coder-mode') === 'local').map(
309
331
  ({ within, label, icon, ...to }) => (
310
- <CurrentLink key={within} {...to} within={within} title={label}>
332
+ <CurrentLink key={within} {...to} within={within} data-tip={label}>
311
333
  <Icon d={icon} />
312
334
  {label}
313
335
  {label === 'Tasks' ? (
@@ -320,7 +342,7 @@ export default function DashLayout({
320
342
  )}
321
343
  </nav>
322
344
  <div class="side-foot">
323
- <SideToggle collapsed={collapsed} />
345
+ <SideToggle collapsed={side === 'min'} />
324
346
  <WithMe me={me}>{who => <ServerMenu me={who} host={host} />}</WithMe>
325
347
  </div>
326
348
  </aside>
@@ -4,7 +4,7 @@ import './credentials-list.css';
4
4
 
5
5
  import { useEffect, useRef, useState } from 'preact/hooks';
6
6
 
7
- import { client } from '@/utils/client';
7
+ import { client, poll } from '@/utils/client';
8
8
  import { iCheck, iCopy, iDots } from '@/comps/ui/icons';
9
9
  import { useRouter } from '@wular/pnext/navigation/client';
10
10
  import type { CredentialRow, EngineStatus, Me } from '@coder/client/types';
@@ -336,23 +336,30 @@ export function EngineLogins({ status: initial }: { status: EngineStatus }) {
336
336
  const [status, setStatus] = useState(initial);
337
337
  const [busy, setBusy] = useState('');
338
338
  const [error, setError] = useState('');
339
- const waiting = useRef<ReturnType<typeof setInterval>>();
339
+ const waiting = useRef<() => void>();
340
340
 
341
341
  // A sign-in finishes in the browser the engine opened; the status says when.
342
342
  const watch = (engine: string) => {
343
- clearInterval(waiting.current);
343
+ waiting.current?.();
344
344
  const until = Date.now() + 5 * 60_000;
345
- waiting.current = setInterval(() => {
346
- void client.engines.status().then(next => {
347
- setStatus(next);
348
- if (next[engine as 'claude' | 'codex']?.signedIn || Date.now() > until) {
349
- clearInterval(waiting.current);
350
- setBusy('');
351
- }
352
- });
345
+ // A status read from a sign-in already stopped never stops the next one.
346
+ let current = true;
347
+ const stopPoll = poll(async () => {
348
+ const next = await client.engines.status();
349
+ if (!current) return;
350
+ setStatus(next);
351
+ if (next[engine as 'claude' | 'codex']?.signedIn || Date.now() > until) {
352
+ stop();
353
+ setBusy('');
354
+ }
353
355
  }, 2000);
356
+ const stop = () => {
357
+ current = false;
358
+ stopPoll();
359
+ };
360
+ waiting.current = stop;
354
361
  };
355
- useEffect(() => () => clearInterval(waiting.current), []);
362
+ useEffect(() => () => waiting.current?.(), []);
356
363
  const act = (engine: string, action: 'login' | 'logout') => {
357
364
  setBusy(`${engine}:${action}`);
358
365
  setError('');
@@ -413,7 +420,7 @@ export function EngineLogins({ status: initial }: { status: EngineStatus }) {
413
420
  type="button"
414
421
  class="btn ghost sm"
415
422
  onClick={() => {
416
- clearInterval(waiting.current);
423
+ waiting.current?.();
417
424
  setBusy('');
418
425
  }}
419
426
  >
@@ -5,7 +5,7 @@ import './credentials-list.css';
5
5
  import './list.css';
6
6
  import { dynamic } from '@wular/pnext/dynamic';
7
7
  import { notFound } from '@wular/pnext/navigation';
8
- import { DEFAULT_CONFIG } from '@coder/core/config';
8
+ import { DEFAULT_CONFIG } from '@coder/core/defaults';
9
9
 
10
10
  import { load } from '@/api/load';
11
11
  import { Settle, Stream } from '@/comps/frame/stream';
@@ -4,7 +4,7 @@ import './runners-list.css';
4
4
 
5
5
  import { useEffect, useRef, useState } from 'preact/hooks';
6
6
 
7
- import { client } from '@/utils/client';
7
+ import { client, poll } from '@/utils/client';
8
8
  import {
9
9
  iCheck,
10
10
  iCopy,
@@ -246,15 +246,19 @@ function AddRunner({
246
246
  useEffect(() => {
247
247
  if (!pairing) return;
248
248
  const until = Math.min(pairing.expiresAt, Date.now() + 15 * 60_000);
249
- const timer = setInterval(() => {
249
+ // A list read from a pairing already gone never ends the next one.
250
+ let current = true;
251
+ const stop = poll(async () => {
250
252
  if (Date.now() > until) return cancel();
251
- void client.runners.list().then(rows => {
252
- if (!rows.items.some(row => !known.includes(row.id))) return;
253
- onAdded(rows);
254
- cancel();
255
- });
253
+ const rows = await client.runners.list();
254
+ if (!current || !rows.items.some(row => !known.includes(row.id))) return;
255
+ onAdded(rows);
256
+ cancel();
256
257
  }, 2000);
257
- return () => clearInterval(timer);
258
+ return () => {
259
+ current = false;
260
+ stop();
261
+ };
258
262
  }, [pairing]);
259
263
 
260
264
  const cancel = () => {
@@ -411,10 +415,7 @@ export function RunnersList({ first, me }: { first: Runners; me: Me }) {
411
415
  const items = sortedRunners(rows.items);
412
416
 
413
417
  // Online dots stay fresh while the tab is open.
414
- useEffect(() => {
415
- const timer = setInterval(() => void client.runners.list().then(setRows, () => {}), 30_000);
416
- return () => clearInterval(timer);
417
- }, []);
418
+ useEffect(() => poll(() => client.runners.list().then(setRows), 30_000), []);
418
419
 
419
420
  const change = async (act: () => Promise<unknown>) => {
420
421
  setError('');