@hienlh/ppm 0.19.0 → 0.19.2

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 (508) hide show
  1. package/CHANGELOG.md +13 -1
  2. package/assets/skills/ppm/SKILL.md +1 -1
  3. package/assets/skills/ppm/references/cli-reference.md +4 -4
  4. package/assets/skills/ppm/references/http-api.md +1 -1
  5. package/dist/web/assets/{adaptive-context-menu-JfuUSqbe.js → adaptive-context-menu-BbuRVTLn.js} +1 -1
  6. package/dist/web/assets/{ai-resource-editor-MfdJKVM5.js → ai-resource-editor-BCCOUzox.js} +1 -1
  7. package/dist/web/assets/{ai-settings-section-CJE7jFsj.js → ai-settings-section-BEM1z7kp.js} +1 -1
  8. package/dist/web/assets/{angular-html-VzxSzTCy.js → angular-html-BlcrLjUx.js} +1 -1
  9. package/dist/web/assets/{angular-ts-CmJ4bmzM.js → angular-ts-BUjtceAJ.js} +1 -1
  10. package/dist/web/assets/{api-client-qRWu6_s5.js → api-client-BzWM7XPR.js} +1 -1
  11. package/dist/web/assets/api-client-D1CePgQ4.js +1 -0
  12. package/dist/web/assets/api-settings-CNtKkHqW.js +1 -0
  13. package/dist/web/assets/{api-settings-CPg4yGvS.js → api-settings-Cubuqjsu.js} +1 -1
  14. package/dist/web/assets/{api-themes-DsAp8m7m.js → api-themes-YsZgf6Xj.js} +1 -1
  15. package/dist/web/assets/{apl-do17bnVx.js → apl-B3g0v1pk.js} +1 -1
  16. package/dist/web/assets/{astro-ClY0e0OA.js → astro-FRnyn7cH.js} +1 -1
  17. package/dist/web/assets/{audio-preview-Fw935Mok.js → audio-preview-DWTRm4HC.js} +1 -1
  18. package/dist/web/assets/{blade-BXSdXDGq.js → blade-NLdOqUKw.js} +1 -1
  19. package/dist/web/assets/{bot-CWZAuloY.js → bot-D6sx11y0.js} +1 -1
  20. package/dist/web/assets/{bsl-MxsMKDUZ.js → bsl-CenHZfaU.js} +1 -1
  21. package/dist/web/assets/{button-CrwgGd1y.js → button-Cq8yixjf.js} +1 -1
  22. package/dist/web/assets/{cairo-4CIrzX_p.js → cairo-B_WR0aiQ.js} +1 -1
  23. package/dist/web/assets/chat-tab-CmXGsJGc.js +15 -0
  24. package/dist/web/assets/{chevron-down-D7CbB5f6.js → chevron-down-Dp0rpHb8.js} +1 -1
  25. package/dist/web/assets/chevron-right-De-Wz5Lz.js +1 -0
  26. package/dist/web/assets/circle-BCy0AUCo.js +1 -0
  27. package/dist/web/assets/{circle-question-mark-BBzFZrNh.js → circle-question-mark-CzR7YRc-.js} +1 -1
  28. package/dist/web/assets/clock-C1CQUQPZ.js +1 -0
  29. package/dist/web/assets/{cobol-phPOE89d.js → cobol-DOynhWYj.js} +1 -1
  30. package/dist/web/assets/code-DJgYrFzm.js +1 -0
  31. package/dist/web/assets/code-editor-DtHXzNZ1.js +7 -0
  32. package/dist/web/assets/{coffee-Cjuukfz3.js → coffee-Bn6dbw4P.js} +1 -1
  33. package/dist/web/assets/{conflict-editor-Ni0PPsKu.js → conflict-editor-Cgb-z12D.js} +1 -1
  34. package/dist/web/assets/{context-menu-BEAa1qhd.js → context-menu-DfWZAxQy.js} +1 -1
  35. package/dist/web/assets/{copy-CbPdNp7E.js → copy-BewHj66E.js} +1 -1
  36. package/dist/web/assets/{cpp-CT7tGBC3.js → cpp-CoZR9t8r.js} +1 -1
  37. package/dist/web/assets/{createLucideIcon-ConhwJnb.js → createLucideIcon-D8pyde1A.js} +1 -1
  38. package/dist/web/assets/{crystal-D8U1Qnqh.js → crystal-BSheONSg.js} +1 -1
  39. package/dist/web/assets/{csv-preview-Coc2oed1.js → csv-preview-BZMHfFH3.js} +1 -1
  40. package/dist/web/assets/{data-grid-overlay-editor-B8WQkZDB.js → data-grid-overlay-editor-gwPoVSCU.js} +1 -1
  41. package/dist/web/assets/{data-grid-types-KpjaKAci.js → data-grid-types-Dq_H1IPa.js} +1 -1
  42. package/dist/web/assets/{database-BqyeGogo.js → database-Y3ESDK4-.js} +1 -1
  43. package/dist/web/assets/database-viewer-GVaQVMdb.js +1 -0
  44. package/dist/web/assets/{dialog-DrL3WEXd.js → dialog-BSfuiTkJ.js} +1 -1
  45. package/dist/web/assets/{diff-viewer-CT0lmDc9.js → diff-viewer-BG5_fH1E.js} +1 -1
  46. package/dist/web/assets/{dist-C2_dKAa_.js → dist-aDSxTR_v.js} +1 -1
  47. package/dist/web/assets/{docx-preview-76KDw1eE.js → docx-preview-DqVDOMmv.js} +1 -1
  48. package/dist/web/assets/{download-DAF7SXcf.js → download-DG3_9xXy.js} +1 -1
  49. package/dist/web/assets/{edge-Db4ze6bw.js → edge-DTtoslQu.js} +1 -1
  50. package/dist/web/assets/{edit-diff-preview-DyC_-4My.js → edit-diff-preview-CXSbSUBX.js} +1 -1
  51. package/dist/web/assets/{elixir-RZPZ0472.js → elixir-CIBCZ3Zi.js} +1 -1
  52. package/dist/web/assets/{elm-DNZcQt_7.js → elm-BLmb7K1H.js} +1 -1
  53. package/dist/web/assets/{erb-DRONsZXs.js → erb-BBY8ApRd.js} +1 -1
  54. package/dist/web/assets/{erlang-DeG0-QwJ.js → erlang-D5Zb5_P0.js} +1 -1
  55. package/dist/web/assets/{esm-CZy-sSoK.js → esm-6F8v1jLL.js} +1 -1
  56. package/dist/web/assets/explorer-body-BPFnjxYj.js +2 -0
  57. package/dist/web/assets/explorer-window-content-Da1T9uM4.js +1 -0
  58. package/dist/web/assets/{extension-store-ClQpnxQr.js → extension-store-Bhu--fPX.js} +1 -1
  59. package/dist/web/assets/{extension-webview-BW5GuBnH.js → extension-webview-BSh6KGd7.js} +1 -1
  60. package/dist/web/assets/{external-link-ojDHcQxQ.js → external-link-B6Hl0U4X.js} +1 -1
  61. package/dist/web/assets/{eye-off-Rq1n2-oe.js → eye-off-CUdzgmX8.js} +1 -1
  62. package/dist/web/assets/{file-exclamation-point-Bpg-zDPP.js → file-exclamation-point-JzhTU2TX.js} +1 -1
  63. package/dist/web/assets/{file-store-28e44KP3.js → file-store-6HW2Lfl4.js} +1 -1
  64. package/dist/web/assets/{folder-symlink-LD-mr54V.js → folder-symlink-Dx1hOk8X.js} +1 -1
  65. package/dist/web/assets/{fortran-fixed-form-CK5VRJNd.js → fortran-fixed-form-CBdwkEUJ.js} +1 -1
  66. package/dist/web/assets/{fsharp-TVVgzHWp.js → fsharp-AT4XdVYH.js} +1 -1
  67. package/dist/web/assets/{gdresource-DiUcyewI.js → gdresource-D852fWBR.js} +1 -1
  68. package/dist/web/assets/{git-commit-CfOruFrg.js → git-commit-DWfWAyxy.js} +1 -1
  69. package/dist/web/assets/{git-log-panel-D1-YXZ0O.js → git-log-panel-DNC9fV0M.js} +1 -1
  70. package/dist/web/assets/{git-rebase-Bxyr24oc.js → git-rebase-C6opk8gr.js} +1 -1
  71. package/dist/web/assets/glide-data-grid-CICFjJon.js +141 -0
  72. package/dist/web/assets/{glimmer-js-CT5aJxz_.js → glimmer-js-DLi5mWYO.js} +1 -1
  73. package/dist/web/assets/{glimmer-ts-CRjP9Yey.js → glimmer-ts-D-scFX30.js} +1 -1
  74. package/dist/web/assets/{glsl-BX2--oI_.js → glsl-dbQzF4iA.js} +1 -1
  75. package/dist/web/assets/{graphql-BIQsvBxQ.js → graphql-D1m5Wen2.js} +1 -1
  76. package/dist/web/assets/group-chat-tab-DbUkcOaJ.js +1 -0
  77. package/dist/web/assets/{hack-CTWk0k1m.js → hack-CqYmpO7u.js} +1 -1
  78. package/dist/web/assets/{haml-DGh27hXc.js → haml-BXzKWEag.js} +1 -1
  79. package/dist/web/assets/{hand-DKSxDa5c.js → hand-x0ejq3fS.js} +1 -1
  80. package/dist/web/assets/{handlebars-BJL1jNrG.js → handlebars-DXnrtHtq.js} +1 -1
  81. package/dist/web/assets/{html-DALyDNqU.js → html-DhgQJ-xL.js} +1 -1
  82. package/dist/web/assets/{html-derivative-B4XcU57e.js → html-derivative-xZRft-YE.js} +1 -1
  83. package/dist/web/assets/{http-DfMo7nNu.js → http-B53mSj14.js} +1 -1
  84. package/dist/web/assets/{hurl-CGc_n2Qf.js → hurl-CsomVy04.js} +1 -1
  85. package/dist/web/assets/{hxml-40zZ1t4x.js → hxml-LffjWwKA.js} +1 -1
  86. package/dist/web/assets/{image-preview-Cp3XB8UO.js → image-preview-CNA6XMNS.js} +1 -1
  87. package/dist/web/assets/index-DTqbMiVc.js +52 -0
  88. package/dist/web/assets/{input-DTmI80l2.js → input-D0ol5jW0.js} +1 -1
  89. package/dist/web/assets/{jinja-D5zuIn9u.js → jinja-CCHURl7V.js} +1 -1
  90. package/dist/web/assets/{jison-Dxut9QwA.js → jison-7qBd8dnR.js} +1 -1
  91. package/dist/web/assets/{julia-Cu85tTPo.js → julia-BY9pnTcy.js} +1 -1
  92. package/dist/web/assets/{just-CctM4uLa.js → just-D7wRSNRr.js} +1 -1
  93. package/dist/web/assets/{keybindings-store-Bi0q9x4Q.js → keybindings-store-BGkB72gM.js} +1 -1
  94. package/dist/web/assets/keybindings-store-Bq6VWl5X.js +1 -0
  95. package/dist/web/assets/{keyboard-AdwmYDSu.js → keyboard-DBBaoQSu.js} +1 -1
  96. package/dist/web/assets/{latex-kpwS_CC7.js → latex-RgsCae55.js} +1 -1
  97. package/dist/web/assets/{lib-Ca7ai8xa.js → lib-v9x_qXgI.js} +1 -1
  98. package/dist/web/assets/{liquid-B8Rr0xue.js → liquid-CnDFzvOZ.js} +1 -1
  99. package/dist/web/assets/loader-circle-DouKgIXy.js +1 -0
  100. package/dist/web/assets/{lock-BEtt_eTO.js → lock-CNxUGpTk.js} +1 -1
  101. package/dist/web/assets/{lua-C_3gixwM.js → lua-0lj5W8sm.js} +1 -1
  102. package/dist/web/assets/markdown-renderer-DEAVpo0C.js +1 -0
  103. package/dist/web/assets/markdown-renderer-dbLIvU2M.js +3 -0
  104. package/dist/web/assets/{marko-B28rvNL-.js → marko-CQgqAhbV.js} +1 -1
  105. package/dist/web/assets/{mdc-DQ6-STFO.js → mdc-BkIcTiIy.js} +1 -1
  106. package/dist/web/assets/{media-url-BjvbfTTl.js → media-url-DDciwvLU.js} +1 -1
  107. package/dist/web/assets/{mobile-bottom-sheet-DXn5g2LB.js → mobile-bottom-sheet-pjcHKorA.js} +1 -1
  108. package/dist/web/assets/mobile-explorer-sheet-CD_kl-jA.js +1 -0
  109. package/dist/web/assets/{monitor-CXI_XpWJ.js → monitor-CxPV_fQI.js} +1 -1
  110. package/dist/web/assets/{monitor-smartphone-C1wUPOSg.js → monitor-smartphone-DLZ6Q05s.js} +1 -1
  111. package/dist/web/assets/{nextflow-B1xq2O8L.js → nextflow-DvIW68DS.js} +1 -1
  112. package/dist/web/assets/{nginx-xsLxRGxO.js → nginx-O-hft0gG.js} +1 -1
  113. package/dist/web/assets/{nim-wqcek3WO.js → nim-BxrR4HwQ.js} +1 -1
  114. package/dist/web/assets/notification-store-Dgbsw9p9.js +1 -0
  115. package/dist/web/assets/{number-overlay-editor-1c4sgZJt.js → number-overlay-editor-BoLRYuZT.js} +1 -1
  116. package/dist/web/assets/{overview-panel-Cr2xsHc6.js → overview-panel-l3g1gT5e.js} +1 -1
  117. package/dist/web/assets/{panel-store-Dv28vz3L.js → panel-store-UOV8wrfR.js} +1 -1
  118. package/dist/web/assets/{pdf-preview-DKeIIJTv.js → pdf-preview-B2CJ_EKv.js} +1 -1
  119. package/dist/web/assets/{pencil-CHKLOgLs.js → pencil-BlIIg6RH.js} +1 -1
  120. package/dist/web/assets/{perl-BH54gN9q.js → perl-Da-uNqmL.js} +1 -1
  121. package/dist/web/assets/{php-Br5ZrEZm.js → php-JMu45mEx.js} +1 -1
  122. package/dist/web/assets/play-DpEEBJTT.js +1 -0
  123. package/dist/web/assets/{plus-BxqD2FIZ.js → plus-BR0qewJI.js} +1 -1
  124. package/dist/web/assets/portal-container-context-D-495m5C.js +1 -0
  125. package/dist/web/assets/{postgres-viewer-BJPr_FYL.js → postgres-viewer-DstZwUai.js} +3 -3
  126. package/dist/web/assets/{process-table-BqTcAJ-h.js → process-table-kmQ2-8qJ.js} +1 -1
  127. package/dist/web/assets/{project-store-KHjFTCxf.js → project-store-DTUCS65m.js} +1 -1
  128. package/dist/web/assets/{pug-8VCubZ3H.js → pug-CLzyQhia.js} +1 -1
  129. package/dist/web/assets/{qml-D8AF1RZ6.js → qml-BHXfGB1l.js} +1 -1
  130. package/dist/web/assets/{razor-6v042IJW.js → razor-BqetJLN3.js} +1 -1
  131. package/dist/web/assets/{react-DHs6efqI.js → react-B549sK_T.js} +1 -1
  132. package/dist/web/assets/{refresh-cw-BALzw7L3.js → refresh-cw-BQU5g2CY.js} +1 -1
  133. package/dist/web/assets/remote-desktop-mobile-sheet-KKTRNjOL.js +2 -0
  134. package/dist/web/assets/{remote-desktop-mobile-view-DaoB49k4.js → remote-desktop-mobile-view-C1ST4-Fm.js} +1 -1
  135. package/dist/web/assets/{remote-desktop-readiness-gate--LJgsuoj.js → remote-desktop-readiness-gate-BH3BE5wA.js} +1 -1
  136. package/dist/web/assets/{remote-desktop-window-content-Cihx4PNZ.js → remote-desktop-window-content-BaZJQt_N.js} +1 -1
  137. package/dist/web/assets/{rotate-ccw-CuvNBM80.js → rotate-ccw-CpjRo90J.js} +1 -1
  138. package/dist/web/assets/{rst-mTGFo4AQ.js → rst-iQdJZ0BP.js} +1 -1
  139. package/dist/web/assets/{ruby-BBaOP636.js → ruby-CNwzZGdH.js} +1 -1
  140. package/dist/web/assets/{run-in-terminal-Zl5iIx-B.js → run-in-terminal-CBXmVGWa.js} +1 -1
  141. package/dist/web/assets/{sas-D5QEkIH2.js → sas-BpBIT2eR.js} +1 -1
  142. package/dist/web/assets/{save-Dxq-Zepc.js → save-DYLiVzU8.js} +1 -1
  143. package/dist/web/assets/{scroll-area-B5yhPpZo.js → scroll-area-lc0ZHORr.js} +1 -1
  144. package/dist/web/assets/{scss-C5Mu6y2u.js → scss-D1MotuEp.js} +1 -1
  145. package/dist/web/assets/{send-Cits8q0l.js → send-C_sLFt56.js} +1 -1
  146. package/dist/web/assets/{send-to-chat-Cv7ikFUC.js → send-to-chat-DJAdlvJ8.js} +1 -1
  147. package/dist/web/assets/{settings-store-Dp4i8zeA.js → settings-store-zbPkG7o-.js} +2 -2
  148. package/dist/web/assets/settings-tab-Cn-AqUP_.js +1 -0
  149. package/dist/web/assets/{settings-tab-BtMvvKAF.js → settings-tab-xyRpt2gq.js} +1 -1
  150. package/dist/web/assets/{shaderlab-mLnpMpN3.js → shaderlab-BNqmcPQi.js} +1 -1
  151. package/dist/web/assets/{shallow-BBJ_qoEF.js → shallow-CmPMAPte.js} +1 -1
  152. package/dist/web/assets/{shellsession-qdcDfUuk.js → shellsession-BBLOVOO-.js} +1 -1
  153. package/dist/web/assets/{shield-check-CH7F1P7m.js → shield-check-CUI36aSc.js} +1 -1
  154. package/dist/web/assets/{shield-off-C9HiF0-6.js → shield-off-GdkkVhb3.js} +1 -1
  155. package/dist/web/assets/{soy-CGGf1G43.js → soy-BpTGo9uy.js} +1 -1
  156. package/dist/web/assets/{sparkles-BnuLH6Ck.js → sparkles-Dh-_J2Rd.js} +1 -1
  157. package/dist/web/assets/{sparql-tQHB50VD.js → sparql-D7A3yKob.js} +1 -1
  158. package/dist/web/assets/{sql-query-editor-DZqnYIB7.js → sql-query-editor-B86NiZHI.js} +1 -1
  159. package/dist/web/assets/{sqlite-viewer-DXutos0e.js → sqlite-viewer-DB775F6C.js} +1 -1
  160. package/dist/web/assets/{stata-DVQNLMcR.js → stata-B7N56ptS.js} +1 -1
  161. package/dist/web/assets/{surrealql-lu576tBi.js → surrealql-CTmnD-lv.js} +1 -1
  162. package/dist/web/assets/{svelte-CSCrOBjT.js → svelte-BppwY9TR.js} +1 -1
  163. package/dist/web/assets/{switch-BPol3BFC.js → switch-BmjKoIpR.js} +1 -1
  164. package/dist/web/assets/{system-monitor-body-Ct8_GX_f.js → system-monitor-body-BDxHQvy9.js} +2 -2
  165. package/dist/web/assets/system-monitor-tab-BoTHLx6B.js +1 -0
  166. package/dist/web/assets/system-monitor-window-content-B9Y98UW6.js +1 -0
  167. package/dist/web/assets/{tab-host-window-content-C5kYP0-z.js → tab-host-window-content-C1j8zr68.js} +1 -1
  168. package/dist/web/assets/{tab-store-DS3YlUwR.js → tab-store-DzYSsw5a.js} +1 -1
  169. package/dist/web/assets/{table-BXDyImqk.js → table-D5Q4ap4W.js} +1 -1
  170. package/dist/web/assets/team-member-sheet-Cxz6h5Ne.js +2 -0
  171. package/dist/web/assets/{team-member-window-content-BJ_XFw8-.js → team-member-window-content-CUSuRHCB.js} +1 -1
  172. package/dist/web/assets/{templ-CPt8AS4Q.js → templ-CvTMKKra.js} +1 -1
  173. package/dist/web/assets/{terminal-tab-8bKQ3CWR.js → terminal-tab-B9I_HlES.js} +3 -3
  174. package/dist/web/assets/{tex-BrTByT7i.js → tex--c4POEUE.js} +1 -1
  175. package/dist/web/assets/{text-wrap-CzMjB6GD.js → text-wrap-DBvQD3kc.js} +1 -1
  176. package/dist/web/assets/{tool-cards-pIu_cmEL.js → tool-cards-D3KSzd2K.js} +4 -4
  177. package/dist/web/assets/{trash-2-BjShZ7if.js → trash-2-By4VMbC4.js} +1 -1
  178. package/dist/web/assets/{triangle-alert-qQ6Plydp.js → triangle-alert-CUTURFbG.js} +1 -1
  179. package/dist/web/assets/{ts-tags-DSqEZ7A8.js → ts-tags--kx7D-Hw.js} +1 -1
  180. package/dist/web/assets/{twig-DW9qc1bf.js → twig-UG9uMHGd.js} +1 -1
  181. package/dist/web/assets/{upload-7Zs6oaK5.js → upload-BNlX8D07.js} +1 -1
  182. package/dist/web/assets/{use-blob-url-DKbV7PM_.js → use-blob-url-Bdxopwwc.js} +1 -1
  183. package/dist/web/assets/{use-coarse-long-press-DGyC48NF.js → use-coarse-long-press-BmI_GVW5.js} +1 -1
  184. package/dist/web/assets/{use-is-mobile-ClE-UcPF.js → use-is-mobile-B9UZNwSm.js} +1 -1
  185. package/dist/web/assets/{use-monaco-theme-DaxAk3gB.js → use-monaco-theme-CVCye2kq.js} +1 -1
  186. package/dist/web/assets/use-open-team-member-D9EOB8-i.js +1 -0
  187. package/dist/web/assets/{use-remote-desktop-display-choice-BUA_1eaH.js → use-remote-desktop-display-choice-neUqyE3p.js} +1 -1
  188. package/dist/web/assets/{use-visual-viewport-CCX21c9w.js → use-visual-viewport-Sfb6CZ80.js} +1 -1
  189. package/dist/web/assets/{use-websocket-CWpG7PWN.js → use-websocket-xOOwBdwP.js} +1 -1
  190. package/dist/web/assets/vendor-markdown-Ls5AcU91.js +291 -0
  191. package/dist/web/assets/{vendor-ui-CXJtBULr.js → vendor-ui-CaeeFO0B.js} +1 -1
  192. package/dist/web/assets/video-preview-DvpgBDhM.js +1 -0
  193. package/dist/web/assets/{vue-html-CG5BKtq1.js → vue-html-8KEW6-I9.js} +1 -1
  194. package/dist/web/assets/{vue-CJNDL1g-.js → vue-jjTomlbt.js} +1 -1
  195. package/dist/web/assets/{vue-vine-Dd5HZ7T5.js → vue-vine-C5tohCkU.js} +1 -1
  196. package/dist/web/assets/{window-pip-registry-9H6Y-HqR.js → window-pip-registry-FdZdGF7h.js} +1 -1
  197. package/dist/web/assets/{window-store-CNzkeUyO.js → window-store-D0x_fxr0.js} +1 -1
  198. package/dist/web/assets/x-DiOUR_mp.js +1 -0
  199. package/dist/web/assets/{xml-DTOm5niz.js → xml-ByFFosqL.js} +1 -1
  200. package/dist/web/assets/{xsl-BD_kBC04.js → xsl-CZUwiLkj.js} +1 -1
  201. package/dist/web/index.html +59 -59
  202. package/dist/web/monacoeditorwork/css.worker.bundle.js +122 -122
  203. package/dist/web/monacoeditorwork/editor.worker.bundle.js +78 -78
  204. package/dist/web/monacoeditorwork/html.worker.bundle.js +110 -110
  205. package/dist/web/monacoeditorwork/json.worker.bundle.js +108 -108
  206. package/dist/web/monacoeditorwork/ts.worker.bundle.js +81 -81
  207. package/dist/web/sw.js +1 -1
  208. package/package.json +1 -1
  209. package/scripts/check-ppm-dir-usage.sh +0 -0
  210. package/scripts/install.sh +0 -0
  211. package/scripts/release.sh +0 -0
  212. package/src/providers/claude-agent-sdk.ts +51 -43
  213. package/src/providers/codex-app-server/codex-provider.ts +7 -1
  214. package/src/server/routes/chat.ts +28 -3
  215. package/src/server/ws/chat.ts +9 -4
  216. package/src/server/ws/terminal.ts +9 -3
  217. package/src/services/media-transcode/ffmpeg-capabilities.ts +48 -7
  218. package/src/services/named-tunnel/named-tunnel-retry.ts +26 -0
  219. package/src/services/named-tunnel/network-ready.ts +63 -0
  220. package/src/services/supervisor.ts +98 -0
  221. package/src/services/transcript-images.ts +14 -7
  222. package/src/services/upgrade.service.ts +7 -2
  223. package/src/shared/image-limits.ts +37 -0
  224. package/src/types/api.ts +3 -1
  225. package/src/types/chat.ts +6 -0
  226. package/src/types/extension-messages.ts +2 -0
  227. package/src/web/components/chat/attachment-chips.tsx +10 -0
  228. package/src/web/components/chat/chat-tab.tsx +37 -13
  229. package/src/web/components/chat/message-input.tsx +102 -11
  230. package/src/web/components/chat/session-debug-dialog.tsx +2 -1
  231. package/src/web/components/chat/user-message-parse.ts +33 -11
  232. package/src/web/components/database/database-viewer.tsx +34 -11
  233. package/src/web/components/database/extract-query-table.ts +24 -0
  234. package/src/web/components/database/glide-data-grid.tsx +11 -4
  235. package/src/web/components/database/glide-grid-types.ts +4 -0
  236. package/src/web/components/database/split-sql-statements.ts +116 -0
  237. package/src/web/components/database/sql-completion-provider.ts +1 -26
  238. package/src/web/components/database/sql-query-editor.tsx +2 -1
  239. package/src/web/components/database/use-database.ts +16 -1
  240. package/src/web/components/database/use-glide-cell-content.ts +11 -6
  241. package/src/web/components/editor/code-editor.tsx +30 -56
  242. package/src/web/hooks/use-chat.ts +6 -3
  243. package/src/web/lib/attachment-marker-split.ts +44 -0
  244. package/src/web/lib/image-animation.ts +51 -0
  245. package/src/web/lib/image-resize-limits.ts +47 -0
  246. package/src/web/lib/image-resize.ts +133 -0
  247. package/dist/web/assets/api-client-DLWwuUkJ.js +0 -1
  248. package/dist/web/assets/api-settings-BFuaZrNC.js +0 -1
  249. package/dist/web/assets/chat-tab-bSOVoC5N.js +0 -15
  250. package/dist/web/assets/chevron-right-D7Pn5agw.js +0 -1
  251. package/dist/web/assets/circle-4g2coECH.js +0 -1
  252. package/dist/web/assets/clock-DbiNiwCG.js +0 -1
  253. package/dist/web/assets/code-DdKXqtf_.js +0 -1
  254. package/dist/web/assets/code-editor-COTa37oe.js +0 -10
  255. package/dist/web/assets/database-viewer-Dta7lSSk.js +0 -1
  256. package/dist/web/assets/explorer-body-BGjR2Gbt.js +0 -2
  257. package/dist/web/assets/explorer-window-content-DAhHh5EQ.js +0 -1
  258. package/dist/web/assets/glide-data-grid-BSsqxe2v.js +0 -138
  259. package/dist/web/assets/group-chat-tab-CMtzoaFc.js +0 -1
  260. package/dist/web/assets/index-BkHlMfPD.js +0 -52
  261. package/dist/web/assets/keybindings-store-_KfovSz7.js +0 -1
  262. package/dist/web/assets/loader-circle-DD65e9-7.js +0 -1
  263. package/dist/web/assets/markdown-renderer-DroTmKbJ.js +0 -1
  264. package/dist/web/assets/markdown-renderer-ntDi3Tx-.js +0 -3
  265. package/dist/web/assets/mobile-explorer-sheet-BP6iU4ma.js +0 -1
  266. package/dist/web/assets/notification-store-II5Kw9rD.js +0 -1
  267. package/dist/web/assets/play-CPbVzGgq.js +0 -1
  268. package/dist/web/assets/portal-container-context-BmG4--I-.js +0 -1
  269. package/dist/web/assets/remote-desktop-mobile-sheet-CYZUjYTO.js +0 -2
  270. package/dist/web/assets/settings-tab-UPPycGRF.js +0 -1
  271. package/dist/web/assets/system-monitor-tab-DC_EerdI.js +0 -1
  272. package/dist/web/assets/system-monitor-window-content-D25DMNpw.js +0 -1
  273. package/dist/web/assets/team-member-sheet-DvmioeQL.js +0 -2
  274. package/dist/web/assets/use-open-team-member-CopUuDHG.js +0 -1
  275. package/dist/web/assets/vendor-markdown-CKTlmXbz.js +0 -291
  276. package/dist/web/assets/video-preview-C2_fdunE.js +0 -1
  277. package/dist/web/assets/x-Dzu9ecti.js +0 -1
  278. /package/dist/web/assets/{abap-Cjj2olMr.js → abap-C5DFF0s3.js} +0 -0
  279. /package/dist/web/assets/{actionscript-3-DCkW7kWC.js → actionscript-3-B0qyRNbc.js} +0 -0
  280. /package/dist/web/assets/{ada-AEWvZXuX.js → ada-bAJzplug.js} +0 -0
  281. /package/dist/web/assets/{andromeeda-D7cSpME6.js → andromeeda-DUCpISy6.js} +0 -0
  282. /package/dist/web/assets/{apache-DjvX0RSe.js → apache-BhAr84Z_.js} +0 -0
  283. /package/dist/web/assets/{apex-70xIZ0pH.js → apex-CRv8YnNq.js} +0 -0
  284. /package/dist/web/assets/{applescript-C3qKqxmA.js → applescript-CD-Cw8bt.js} +0 -0
  285. /package/dist/web/assets/{ara-iGt2zixk.js → ara-BUzeQYoG.js} +0 -0
  286. /package/dist/web/assets/{asciidoc-Bs2t-06W.js → asciidoc-CT2hTg-v.js} +0 -0
  287. /package/dist/web/assets/{asm-B-T0eqhE.js → asm-CAu35JeY.js} +0 -0
  288. /package/dist/web/assets/{aurora-x-D3h_9M3E.js → aurora-x-BeFJoIA1.js} +0 -0
  289. /package/dist/web/assets/{awk-CZwfwpPi.js → awk-Vau-DrGN.js} +0 -0
  290. /package/dist/web/assets/{ayu-dark-DulkglEA.js → ayu-dark-CLe-OTsQ.js} +0 -0
  291. /package/dist/web/assets/{ayu-light-DyNQiRpn.js → ayu-light-CJbQfu4M.js} +0 -0
  292. /package/dist/web/assets/{ayu-mirage-CcEt4xkI.js → ayu-mirage-GWaKwdrx.js} +0 -0
  293. /package/dist/web/assets/{ballerina-OhQPjyHS.js → ballerina-DWu0USI5.js} +0 -0
  294. /package/dist/web/assets/{bat-DqKcJTUn.js → bat-_07Huw7E.js} +0 -0
  295. /package/dist/web/assets/{beancount-BdXlMi8T.js → beancount-EcvogrcF.js} +0 -0
  296. /package/dist/web/assets/{berry-DvgY6BkY.js → berry-BBB87SY7.js} +0 -0
  297. /package/dist/web/assets/{bibtex-BSRJq0yM.js → bibtex-DbueS87A.js} +0 -0
  298. /package/dist/web/assets/{bicep-DpAdumKt.js → bicep-tcmXnUo4.js} +0 -0
  299. /package/dist/web/assets/{bird2-rlbtp8di.js → bird2-B2qwilIR.js} +0 -0
  300. /package/dist/web/assets/{c-AjOLBXIs.js → c-Z58oivSt.js} +0 -0
  301. /package/dist/web/assets/{c3-DqIXz4Ia.js → c3-U40cGT9p.js} +0 -0
  302. /package/dist/web/assets/{cadence-DAJtUpOE.js → cadence-BFcD-LyM.js} +0 -0
  303. /package/dist/web/assets/{catppuccin-frappe-DUPoeZfb.js → catppuccin-frappe-RkC9AX6J.js} +0 -0
  304. /package/dist/web/assets/{catppuccin-latte-CK3U68Ql.js → catppuccin-latte-Bsi4l-nO.js} +0 -0
  305. /package/dist/web/assets/{catppuccin-macchiato-BzteLnIX.js → catppuccin-macchiato-B8JIdTW-.js} +0 -0
  306. /package/dist/web/assets/{catppuccin-mocha-ozLd0aBR.js → catppuccin-mocha-oH8wwrDj.js} +0 -0
  307. /package/dist/web/assets/{clarity-6KqEoBfz.js → clarity-C3WNeRCR.js} +0 -0
  308. /package/dist/web/assets/{clojure-B_uBL5C7.js → clojure-CFoAz7u9.js} +0 -0
  309. /package/dist/web/assets/{cmake-B3nxBNmj.js → cmake-Bcsqp9yR.js} +0 -0
  310. /package/dist/web/assets/{codeowners-DkS-51at.js → codeowners-DC2Z7heP.js} +0 -0
  311. /package/dist/web/assets/{codeql-rpr1z_mR.js → codeql-BfG68Al5.js} +0 -0
  312. /package/dist/web/assets/{common-lisp-BfNbv_W6.js → common-lisp-Z5P-lKQe.js} +0 -0
  313. /package/dist/web/assets/{coq-EAW-1nw0.js → coq-BJslqrnv.js} +0 -0
  314. /package/dist/web/assets/{csharp-BE3Gn5bq.js → csharp-DhhZTOTs.js} +0 -0
  315. /package/dist/web/assets/{css-Cr27CfPY.js → css-DiFIpMfE.js} +0 -0
  316. /package/dist/web/assets/{csv-B5VtaPtH.js → csv-DViLbHxI.js} +0 -0
  317. /package/dist/web/assets/{cue-jm6lYNrx.js → cue-CoG3mg2I.js} +0 -0
  318. /package/dist/web/assets/{cypher-DXkvK-pn.js → cypher-jTxC8NAB.js} +0 -0
  319. /package/dist/web/assets/{d-CgG1_yzd.js → d-6hKIUAbU.js} +0 -0
  320. /package/dist/web/assets/{dark-plus-CALzcIVB.js → dark-plus-D_-np2Sr.js} +0 -0
  321. /package/dist/web/assets/{dart-BEVoLlWg.js → dart-CVpxLpVn.js} +0 -0
  322. /package/dist/web/assets/{dax-H0BW3Q_P.js → dax-BPqJ8qIY.js} +0 -0
  323. /package/dist/web/assets/{desktop-BtYq1l_0.js → desktop-u_y5_Hr5.js} +0 -0
  324. /package/dist/web/assets/{diff-CnBYdfTg.js → diff-Duj1XMrd.js} +0 -0
  325. /package/dist/web/assets/{dist-CJJKpgR7.js → dist-CEZ4plgH.js} +0 -0
  326. /package/dist/web/assets/{docker-COBitpld.js → docker-8UGSCc7N.js} +0 -0
  327. /package/dist/web/assets/{dotenv-CG0x1m7c.js → dotenv-DVnj8kyR.js} +0 -0
  328. /package/dist/web/assets/{dracula-CLCi47X-.js → dracula-D0cEmSg7.js} +0 -0
  329. /package/dist/web/assets/{dracula-soft-BMv5cCAV.js → dracula-soft-DfGS5Yfr.js} +0 -0
  330. /package/dist/web/assets/{dream-maker-1Bd4OSww.js → dream-maker-qG8kMGTF.js} +0 -0
  331. /package/dist/web/assets/{emacs-lisp-B-2zKLn-.js → emacs-lisp-iVqyMLyT.js} +0 -0
  332. /package/dist/web/assets/{everforest-dark-BGVynCbV.js → everforest-dark-Cruygd5U.js} +0 -0
  333. /package/dist/web/assets/{everforest-light--rn3R7Cm.js → everforest-light-BiJa8RHL.js} +0 -0
  334. /package/dist/web/assets/{fennel-SWg9Us_u.js → fennel-H6XSEIoz.js} +0 -0
  335. /package/dist/web/assets/{fish-B04oqgKk.js → fish-ho_7CxX6.js} +0 -0
  336. /package/dist/web/assets/{fluent-BefzAGMN.js → fluent-1l5ssJTF.js} +0 -0
  337. /package/dist/web/assets/{fortran-free-form-alw7jMxb.js → fortran-free-form-Bo1x233A.js} +0 -0
  338. /package/dist/web/assets/{gdscript-BAyhvjCx.js → gdscript-DffFMp_k.js} +0 -0
  339. /package/dist/web/assets/{gdshader-Crl09X-B.js → gdshader-CvnpAiAF.js} +0 -0
  340. /package/dist/web/assets/{genie-BSIGRHu7.js → genie-2eOvAEzW.js} +0 -0
  341. /package/dist/web/assets/{gherkin-DJhSnSWH.js → gherkin-DL-5OUlV.js} +0 -0
  342. /package/dist/web/assets/{github-dark-r8C6qwug.js → github-dark-DrTZprgG.js} +0 -0
  343. /package/dist/web/assets/{github-dark-default-FVtXjcm8.js → github-dark-default-_mDH8IMP.js} +0 -0
  344. /package/dist/web/assets/{github-dark-dimmed-CzrUPgSb.js → github-dark-dimmed-C48hJuLn.js} +0 -0
  345. /package/dist/web/assets/{github-dark-high-contrast-1s9rF5EB.js → github-dark-high-contrast-BQ-zGGNe.js} +0 -0
  346. /package/dist/web/assets/{github-light-BvIWCkl7.js → github-light-BYc90Yx8.js} +0 -0
  347. /package/dist/web/assets/{github-light-default-CVdhzrxV.js → github-light-default-Daecj4J5.js} +0 -0
  348. /package/dist/web/assets/{github-light-high-contrast-CBAf6hjF.js → github-light-high-contrast-DoafCbsU.js} +0 -0
  349. /package/dist/web/assets/{gleam-DvL1q7aa.js → gleam-hUWWjmnp.js} +0 -0
  350. /package/dist/web/assets/{gn-BG5NvwAf.js → gn-7PJ3R70o.js} +0 -0
  351. /package/dist/web/assets/{gnuplot-BARnM-zG.js → gnuplot-V-Pxck7b.js} +0 -0
  352. /package/dist/web/assets/{go-DXvjjq4q.js → go-ARF6DprD.js} +0 -0
  353. /package/dist/web/assets/{groovy-CRylSKhr.js → groovy-CKGBIPOz.js} +0 -0
  354. /package/dist/web/assets/{gruvbox-dark-hard-BVPjnqm7.js → gruvbox-dark-hard-Cm9wqdI3.js} +0 -0
  355. /package/dist/web/assets/{gruvbox-dark-medium-U_PDgq0a.js → gruvbox-dark-medium-Cmsd6dG8.js} +0 -0
  356. /package/dist/web/assets/{gruvbox-dark-soft-CkzNtpbr.js → gruvbox-dark-soft-BBGlGGCz.js} +0 -0
  357. /package/dist/web/assets/{gruvbox-light-hard-DfMmR2G8.js → gruvbox-light-hard-BCR7KmqG.js} +0 -0
  358. /package/dist/web/assets/{gruvbox-light-medium-BGCak-dd.js → gruvbox-light-medium-CP32bga8.js} +0 -0
  359. /package/dist/web/assets/{gruvbox-light-soft-DnpM3fkN.js → gruvbox-light-soft-qjxk8ps6.js} +0 -0
  360. /package/dist/web/assets/{haskell-DXgGIDD_.js → haskell-C5DzwGRa.js} +0 -0
  361. /package/dist/web/assets/{haxe-B7qlTpRz.js → haxe-C8f_jCVv.js} +0 -0
  362. /package/dist/web/assets/{hcl-ADWfVe_Q.js → hcl-DetJkf6E.js} +0 -0
  363. /package/dist/web/assets/{hjson-BaM9gLbq.js → hjson-DeZhwIdo.js} +0 -0
  364. /package/dist/web/assets/{hlsl-iqaRA4MG.js → hlsl-SMIDLRyX.js} +0 -0
  365. /package/dist/web/assets/{horizon-BPpTnhB9.js → horizon-BMAVDxMx.js} +0 -0
  366. /package/dist/web/assets/{horizon-bright-yBLg3QbE.js → horizon-bright-3BaV_VGD.js} +0 -0
  367. /package/dist/web/assets/{houston-eFOp0ySL.js → houston-BWBbEPvb.js} +0 -0
  368. /package/dist/web/assets/{hy-BALeMn3i.js → hy-DcsmE-Nm.js} +0 -0
  369. /package/dist/web/assets/{imba-CdGUm_GD.js → imba-CnkgnYMf.js} +0 -0
  370. /package/dist/web/assets/{ini-DG7imsqe.js → ini-BnvTdau7.js} +0 -0
  371. /package/dist/web/assets/{java-D5GTT474.js → java-BLFACXat.js} +0 -0
  372. /package/dist/web/assets/{javascript-V7WwJvRm.js → javascript-BKcFWENk.js} +0 -0
  373. /package/dist/web/assets/{json-B9FhwAgR.js → json-3AJtJADf.js} +0 -0
  374. /package/dist/web/assets/{json5--Abk7MUy.js → json5-DLyDVlwe.js} +0 -0
  375. /package/dist/web/assets/{jsonc-C8jb-Z6x.js → jsonc-B5DgF1jW.js} +0 -0
  376. /package/dist/web/assets/{jsonl-CaJNxfkm.js → jsonl-CV_O71a2.js} +0 -0
  377. /package/dist/web/assets/{jsonnet-CPrKMSCU.js → jsonnet-DZfPLgYz.js} +0 -0
  378. /package/dist/web/assets/{jssm-DEu-9_uo.js → jssm-Cr1zYOsY.js} +0 -0
  379. /package/dist/web/assets/{jsx-MfAsCWDW.js → jsx-VN-JS2_q.js} +0 -0
  380. /package/dist/web/assets/{kanagawa-dragon-DnbcAduF.js → kanagawa-dragon-D9CcT3xg.js} +0 -0
  381. /package/dist/web/assets/{kanagawa-lotus-yxsY3ew8.js → kanagawa-lotus-DLXsB8fr.js} +0 -0
  382. /package/dist/web/assets/{kanagawa-wave-C2RyvZaM.js → kanagawa-wave-weDs3Wjz.js} +0 -0
  383. /package/dist/web/assets/{kdl-CvssmvcU.js → kdl-0XF56jnT.js} +0 -0
  384. /package/dist/web/assets/{kotlin-jufO49mU.js → kotlin-BPkH9vzk.js} +0 -0
  385. /package/dist/web/assets/{kusto-BAFnylR7.js → kusto-DMVpADb9.js} +0 -0
  386. /package/dist/web/assets/{laserwave-BgxFC4Ge.js → laserwave-Cl6T2cGP.js} +0 -0
  387. /package/dist/web/assets/{lean-Bgt24V1D.js → lean-BvwzQGl1.js} +0 -0
  388. /package/dist/web/assets/{less-Bk_UaqsM.js → less-DI_03xkC.js} +0 -0
  389. /package/dist/web/assets/{light-plus-nMxFfInN.js → light-plus-hIiIsQN_.js} +0 -0
  390. /package/dist/web/assets/{llvm-3_PASDOO.js → llvm-B5hjjSWc.js} +0 -0
  391. /package/dist/web/assets/{log-CKm0CxB1.js → log-D28raQiE.js} +0 -0
  392. /package/dist/web/assets/{logo-DFP1_5U6.js → logo-6Cdd4ASb.js} +0 -0
  393. /package/dist/web/assets/{luau-BcZnoyh3.js → luau-CK9nKpOX.js} +0 -0
  394. /package/dist/web/assets/{make-CpNSwgwT.js → make-CVnxKCB6.js} +0 -0
  395. /package/dist/web/assets/{markdown-DhU3Djpv.js → markdown-B9mMQzD5.js} +0 -0
  396. /package/dist/web/assets/{material-theme-DD5L3OqF.js → material-theme-BZSs2o0j.js} +0 -0
  397. /package/dist/web/assets/{material-theme-darker-CG2_tD3R.js → material-theme-darker-BeAQBzQ_.js} +0 -0
  398. /package/dist/web/assets/{material-theme-lighter-CzNkUc9Y.js → material-theme-lighter-BAe0IXcs.js} +0 -0
  399. /package/dist/web/assets/{material-theme-ocean-BtLiAaF_.js → material-theme-ocean-CiPIf0Np.js} +0 -0
  400. /package/dist/web/assets/{material-theme-palenight-Brvjz035.js → material-theme-palenight-DJBb4_XN.js} +0 -0
  401. /package/dist/web/assets/{matlab-CCY0KtMD.js → matlab-CY0dbp_s.js} +0 -0
  402. /package/dist/web/assets/{mdx-CH6YY0aS.js → mdx-DU9zbF7h.js} +0 -0
  403. /package/dist/web/assets/{mermaid-Bqh3gFI0.js → mermaid-UmmjGwdh.js} +0 -0
  404. /package/dist/web/assets/{min-dark-BUwfTzI6.js → min-dark-C59GkzOm.js} +0 -0
  405. /package/dist/web/assets/{min-light-oLanXCOk.js → min-light-B9QglrdQ.js} +0 -0
  406. /package/dist/web/assets/{mipsasm-CTh3hPWK.js → mipsasm-Dc6Jsr_D.js} +0 -0
  407. /package/dist/web/assets/{mojo-DXdszZKa.js → mojo-D5ij-nGW.js} +0 -0
  408. /package/dist/web/assets/{monokai-TNE_tRxc.js → monokai-QAtE_yCP.js} +0 -0
  409. /package/dist/web/assets/{moonbit-jrk-Gx1X.js → moonbit-BMtIdiTL.js} +0 -0
  410. /package/dist/web/assets/{move-DItn61bH.js → move-BB_jMHYZ.js} +0 -0
  411. /package/dist/web/assets/{narrat-3kB4dkh0.js → narrat-BvvoKGVn.js} +0 -0
  412. /package/dist/web/assets/{nextflow-groovy-CSKtwETr.js → nextflow-groovy-CqxtAsnF.js} +0 -0
  413. /package/dist/web/assets/{night-owl-Dm91nSIq.js → night-owl-CLoE9kjt.js} +0 -0
  414. /package/dist/web/assets/{night-owl-light-riwrcPRg.js → night-owl-light-BwIFT5Xd.js} +0 -0
  415. /package/dist/web/assets/{nix-sLgl9zGF.js → nix-BKlnVT9r.js} +0 -0
  416. /package/dist/web/assets/{nord-TWkJPI6M.js → nord-B7R1KRAK.js} +0 -0
  417. /package/dist/web/assets/{nushell-CDZi4-Gf.js → nushell-CzUme-NJ.js} +0 -0
  418. /package/dist/web/assets/{objective-c-DW9PboNp.js → objective-c-D-UKKiOc.js} +0 -0
  419. /package/dist/web/assets/{objective-cpp-2XCCX6q1.js → objective-cpp-BucWn4Fx.js} +0 -0
  420. /package/dist/web/assets/{ocaml-CxPdnjrh.js → ocaml-EJCNXMGo.js} +0 -0
  421. /package/dist/web/assets/{odin-BJjlKpkM.js → odin-CEjnRU_3.js} +0 -0
  422. /package/dist/web/assets/{one-dark-pro-K1KOmBZ7.js → one-dark-pro-Bv469NX5.js} +0 -0
  423. /package/dist/web/assets/{one-light-DQB-vUyZ.js → one-light-BlzO5RWI.js} +0 -0
  424. /package/dist/web/assets/{openscad-DCoLwn4j.js → openscad-Czl6alLw.js} +0 -0
  425. /package/dist/web/assets/{pascal-z2ktbbWp.js → pascal-BI9fLvG4.js} +0 -0
  426. /package/dist/web/assets/{pkl-Dyq4-v6z.js → pkl-ChQLsyjU.js} +0 -0
  427. /package/dist/web/assets/{plastic-C4Rq5PnD.js → plastic-DGL3mtdL.js} +0 -0
  428. /package/dist/web/assets/{plsql-BCIjO63y.js → plsql-BJDjqcWp.js} +0 -0
  429. /package/dist/web/assets/{po-_kXpd_0n.js → po-DK5O3OpF.js} +0 -0
  430. /package/dist/web/assets/{poimandres-C9XggjB3.js → poimandres-CnuqZT4T.js} +0 -0
  431. /package/dist/web/assets/{polar-DV_1L04z.js → polar-DCsUxSsQ.js} +0 -0
  432. /package/dist/web/assets/{postcss-BuqS91mQ.js → postcss-8UjdFmis.js} +0 -0
  433. /package/dist/web/assets/{powerquery-DK13B2Dc.js → powerquery-CAp0CGQj.js} +0 -0
  434. /package/dist/web/assets/{powershell-tkuhhtuP.js → powershell-CbBsKZJP.js} +0 -0
  435. /package/dist/web/assets/{prisma-WNXfBtpr.js → prisma-BnNpXCJN.js} +0 -0
  436. /package/dist/web/assets/{prolog-BzEdNKfO.js → prolog-CC-l9E8Y.js} +0 -0
  437. /package/dist/web/assets/{proto-B5meVsrA.js → proto-CV_1k2rL.js} +0 -0
  438. /package/dist/web/assets/{puppet-C7h_p4r2.js → puppet-2D8HU2bv.js} +0 -0
  439. /package/dist/web/assets/{purescript-qH1KxAs6.js → purescript-BEJAORgU.js} +0 -0
  440. /package/dist/web/assets/{python-DeTIYd5V.js → python-BBdbb7nn.js} +0 -0
  441. /package/dist/web/assets/{qmldir-4Rm5AvD3.js → qmldir-D5ZYI4Ie.js} +0 -0
  442. /package/dist/web/assets/{qss-YRbeNtr0.js → qss-BOJVGyXE.js} +0 -0
  443. /package/dist/web/assets/{r-DHwHK0Jh.js → r-B5dlEtkj.js} +0 -0
  444. /package/dist/web/assets/{racket-gKn4SFLQ.js → racket-BUAEygJ6.js} +0 -0
  445. /package/dist/web/assets/{raku-DWTmAj3A.js → raku-BG0Ss0ID.js} +0 -0
  446. /package/dist/web/assets/{red-BFuHRZFS.js → red-C5yyQbtG.js} +0 -0
  447. /package/dist/web/assets/{reg-DYoOF5tH.js → reg-DHoBWtLf.js} +0 -0
  448. /package/dist/web/assets/{regexp-6rXQHD2-.js → regexp-DDtJrlKv.js} +0 -0
  449. /package/dist/web/assets/{rel-Z_tTruCU.js → rel-CR6WJDRD.js} +0 -0
  450. /package/dist/web/assets/{riscv-DfTfKuvU.js → riscv-2V9tC-nx.js} +0 -0
  451. /package/dist/web/assets/{ron-CsUtW8gQ.js → ron-QLTmqGUw.js} +0 -0
  452. /package/dist/web/assets/{rose-pine-Dng_vCo6.js → rose-pine-be0__X3A.js} +0 -0
  453. /package/dist/web/assets/{rose-pine-dawn-Ds88Nezf.js → rose-pine-dawn-n_WQJwsW.js} +0 -0
  454. /package/dist/web/assets/{rose-pine-moon-DWpzrqNz.js → rose-pine-moon-t2aLtzgB.js} +0 -0
  455. /package/dist/web/assets/{rosmsg-h2sO84oD.js → rosmsg-Ce1JD8vt.js} +0 -0
  456. /package/dist/web/assets/{rust-Dx86yfCi.js → rust-5fHH-ZoW.js} +0 -0
  457. /package/dist/web/assets/{sass-BwwVhdT9.js → sass-DPmGFiOJ.js} +0 -0
  458. /package/dist/web/assets/{scala-WJqNEN-w.js → scala-BRMVFbZF.js} +0 -0
  459. /package/dist/web/assets/{scheme-uwj-t1Rp.js → scheme-BMSJWevF.js} +0 -0
  460. /package/dist/web/assets/{sdbl-BYy5hgEz.js → sdbl-DTZ-pEHI.js} +0 -0
  461. /package/dist/web/assets/{shellscript-DWTW_w2K.js → shellscript-6-9yg5Cz.js} +0 -0
  462. /package/dist/web/assets/{slack-dark-BvhGq5vQ.js → slack-dark-DKJgiR7e.js} +0 -0
  463. /package/dist/web/assets/{slack-ochin-C9g0ZRFr.js → slack-ochin-WjehVgyK.js} +0 -0
  464. /package/dist/web/assets/{smalltalk-ZEiZ6i7U.js → smalltalk-BChf4SN1.js} +0 -0
  465. /package/dist/web/assets/{snazzy-light-29OA24q5.js → snazzy-light-C1WAwOx6.js} +0 -0
  466. /package/dist/web/assets/{solarized-dark-Bnq9IOn_.js → solarized-dark-BxREZrGx.js} +0 -0
  467. /package/dist/web/assets/{solarized-light-CYdI7T5g.js → solarized-light-DG29B1pR.js} +0 -0
  468. /package/dist/web/assets/{solidity-DcuIDB-_.js → solidity-bDbtGwdR.js} +0 -0
  469. /package/dist/web/assets/{splunk-D5ONE91g.js → splunk-Ceqx5njI.js} +0 -0
  470. /package/dist/web/assets/{sql-DIwJJDU-.js → sql-OwMEKbz8.js} +0 -0
  471. /package/dist/web/assets/{ssh-config-D8717tLI.js → ssh-config-WAR3lu2k.js} +0 -0
  472. /package/dist/web/assets/{stylus-Cm1lO4Y6.js → stylus-Cjmoz3d8.js} +0 -0
  473. /package/dist/web/assets/{swift-X8oWpSeU.js → swift-S_ZSBG1q.js} +0 -0
  474. /package/dist/web/assets/{synthwave-84-DF-t1UKY.js → synthwave-84-DiUeWMSh.js} +0 -0
  475. /package/dist/web/assets/{system-verilog-JYyHFpPf.js → system-verilog-RRjqPjUV.js} +0 -0
  476. /package/dist/web/assets/{systemd-Da-5Ma_c.js → systemd-BD3SkXoU.js} +0 -0
  477. /package/dist/web/assets/{talonscript-CXraVPho.js → talonscript-DWefISOm.js} +0 -0
  478. /package/dist/web/assets/{tasl-BCmWezH9.js → tasl-DCjSS-09.js} +0 -0
  479. /package/dist/web/assets/{tcl-BTufzfbf.js → tcl-DTYqT_66.js} +0 -0
  480. /package/dist/web/assets/{terraform-CtUA9c8W.js → terraform-CkbP5QnR.js} +0 -0
  481. /package/dist/web/assets/{tokyo-night-BCThYXmz.js → tokyo-night-qVSqO9xy.js} +0 -0
  482. /package/dist/web/assets/{toml-D5YlDr6Y.js → toml-D9F5Osqs.js} +0 -0
  483. /package/dist/web/assets/{tsv-CBB-P_Sl.js → tsv-C5tpHoxm.js} +0 -0
  484. /package/dist/web/assets/{tsx-D5B4juDp.js → tsx-CMWkGtGm.js} +0 -0
  485. /package/dist/web/assets/{turtle-D8PCeJ-v.js → turtle-Cn4z3xOi.js} +0 -0
  486. /package/dist/web/assets/{typescript-CMPnTawp.js → typescript-DSyAEWsn.js} +0 -0
  487. /package/dist/web/assets/{typespec-CHxldxIW.js → typespec-B93Qap7y.js} +0 -0
  488. /package/dist/web/assets/{typst-Q8CqX5OR.js → typst-DtlOmFlX.js} +0 -0
  489. /package/dist/web/assets/{v-Bf2hKAMu.js → v-Dfzf-szS.js} +0 -0
  490. /package/dist/web/assets/{vala-Cbete-UE.js → vala-CyiQZQ9f.js} +0 -0
  491. /package/dist/web/assets/{vb-CA-xWAcX.js → vb-C7DGSldd.js} +0 -0
  492. /package/dist/web/assets/{verilog-B3wb8CfT.js → verilog-kF-hClY2.js} +0 -0
  493. /package/dist/web/assets/{vesper-D6r3VJq_.js → vesper-BmKcADuz.js} +0 -0
  494. /package/dist/web/assets/{vhdl-C42S_797.js → vhdl-CDnb16KJ.js} +0 -0
  495. /package/dist/web/assets/{viml-BePrIAs8.js → viml-B8cs6Ady.js} +0 -0
  496. /package/dist/web/assets/{vitesse-black-DRqn61Ck.js → vitesse-black-BR6o9Gto.js} +0 -0
  497. /package/dist/web/assets/{vitesse-dark-39_8UE-w.js → vitesse-dark-Dye14-KB.js} +0 -0
  498. /package/dist/web/assets/{vitesse-light-B1jwrxDm.js → vitesse-light-BLpswIxf.js} +0 -0
  499. /package/dist/web/assets/{vyper-tDJv3imF.js → vyper-DU51Z97W.js} +0 -0
  500. /package/dist/web/assets/{wasm-DR7eskZH.js → wasm-CVqlJAR2.js} +0 -0
  501. /package/dist/web/assets/{wenyan-CezVfqN3.js → wenyan-DhCa3hCn.js} +0 -0
  502. /package/dist/web/assets/{wgsl-CwOcIiIG.js → wgsl-Bj1Ec4vv.js} +0 -0
  503. /package/dist/web/assets/{wikitext-C5coomYc.js → wikitext-C90DHNa0.js} +0 -0
  504. /package/dist/web/assets/{wit-C9yyX6-U.js → wit-DUgbOD7Z.js} +0 -0
  505. /package/dist/web/assets/{wolfram-BWi0rMAa.js → wolfram-BgwUqD1I.js} +0 -0
  506. /package/dist/web/assets/{yaml-CGXhKUtT.js → yaml-DEQulj-8.js} +0 -0
  507. /package/dist/web/assets/{zenscript-Do7WLoJh.js → zenscript-C7Jp7TgT.js} +0 -0
  508. /package/dist/web/assets/{zig-Cr80Qrru.js → zig-9x4qXYLg.js} +0 -0
@@ -1,4 +1,5 @@
1
1
  import { statSync } from "node:fs";
2
+ import { homedir } from "node:os";
2
3
  import { terminalService } from "../../services/terminal.service.ts";
3
4
  import { resolveProjectPath } from "../helpers/resolve-project.ts";
4
5
  import { assertAllowed, resolvePath } from "../../services/fs-path-guard.service.ts";
@@ -7,6 +8,9 @@ import { assertAllowed, resolvePath } from "../../services/fs-path-guard.service
7
8
  * Where a new shell starts. An explicit `cwd` (explorer "Open in Terminal") wins
8
9
  * over the project root and goes through the same allowlist as the filesystem
9
10
  * API, so a terminal cannot be spawned anywhere the explorer could not browse.
11
+ * Neither given (a project-less "run this command" — the remote-desktop
12
+ * checklist's `brew install ffmpeg` button) → the user's home, the same place the
13
+ * OS explorer starts browsing from.
10
14
  */
11
15
  function resolveStartDir(projectName: string | undefined, cwd: string | undefined): string {
12
16
  if (cwd) {
@@ -16,7 +20,7 @@ function resolveStartDir(projectName: string | undefined, cwd: string | undefine
16
20
  return resolved;
17
21
  }
18
22
  if (projectName) return resolveProjectPath(projectName);
19
- throw new Error("Session not found");
23
+ return homedir();
20
24
  }
21
25
 
22
26
  /** Control message prefix for resize commands */
@@ -36,8 +40,10 @@ export const terminalWebSocket = {
36
40
 
37
41
  let session = id !== "new" ? terminalService.get(id) : undefined;
38
42
 
39
- // If session doesn't exist and a start directory is known, create one
40
- if (!session && (projectName || cwd)) {
43
+ // A fresh socket ("new") always gets a shell project root, explicit cwd, or home — and
44
+ // so does a stale id that still knows its project/cwd (tab reopened after a restart).
45
+ // Only a stale id with no context left is "Session not found".
46
+ if (!session && (id === "new" || projectName || cwd)) {
41
47
  try {
42
48
  const startDir = resolveStartDir(projectName, cwd);
43
49
  // Create session with the requested ID — but TerminalService generates its own ID.
@@ -2,9 +2,15 @@
2
2
  * Discover ffmpeg/ffprobe on this machine and pick the fastest working H.264 encoder.
3
3
  *
4
4
  * ffmpeg is an *optional* dependency: without it PPM still plays browser-native
5
- * media (mp4/webm) via Range requests, it just cannot transcode AVI/MKV. The
6
- * result is cached for the process lifetime — the binary does not appear
7
- * mid-session, and probing hardware encoders costs a real encode each.
5
+ * media (mp4/webm) via Range requests, it just cannot transcode AVI/MKV. A
6
+ * positive result is cached for the process lifetime — probing hardware encoders
7
+ * costs a real encode each. A *negative* result is not: the remote-desktop
8
+ * checklist tells the user to install ffmpeg and promises to notice, so "not
9
+ * found" is re-checked on every call (one `which` + a few `stat`s).
10
+ *
11
+ * `PATH` alone is not enough to find it. A daemon started by launchd inherits
12
+ * `/usr/bin:/bin:/usr/sbin:/sbin` — Homebrew's `/opt/homebrew/bin` is not on it —
13
+ * so the well-known install directories are probed after `Bun.which`.
8
14
  *
9
15
  * Hardware encoders are verified by encoding a few synthetic frames rather than by
10
16
  * grepping `ffmpeg -encoders`: the list only says the build was compiled with
@@ -60,11 +66,42 @@ async function encoderWorks(ffmpeg: string, encoder: string): Promise<boolean> {
60
66
  }
61
67
  }
62
68
 
69
+ /** Where package managers put ffmpeg when it is not on the daemon's PATH. */
70
+ function wellKnownBinDirs(): string[] {
71
+ switch (process.platform) {
72
+ case "darwin": return ["/opt/homebrew/bin", "/usr/local/bin", "/opt/local/bin"];
73
+ case "win32": {
74
+ const local = process.env.LOCALAPPDATA;
75
+ return [
76
+ ...(local ? [`${local}\\Microsoft\\WinGet\\Links`] : []),
77
+ "C:\\ProgramData\\chocolatey\\bin", "C:\\ffmpeg\\bin",
78
+ ];
79
+ }
80
+ default: return ["/usr/local/bin", "/usr/bin", "/snap/bin", `${process.env.HOME ?? ""}/.local/bin`];
81
+ }
82
+ }
83
+
84
+ /** `Bun.which` on `searchPath` (the process PATH by default) first, then the well-known
85
+ * directories. Exported for tests. */
86
+ export function findFfmpegBinary(
87
+ name: "ffmpeg" | "ffprobe",
88
+ dirs: string[] = wellKnownBinDirs(),
89
+ searchPath: string | undefined = process.env.PATH,
90
+ ): string | null {
91
+ const onPath = Bun.which(name, searchPath ? { PATH: searchPath } : undefined);
92
+ if (onPath) return onPath;
93
+ for (const dir of dirs) {
94
+ const found = Bun.which(name, { PATH: dir });
95
+ if (found) return found;
96
+ }
97
+ return null;
98
+ }
99
+
63
100
  let cached: Promise<FfmpegCapabilities> | null = null;
64
101
 
65
102
  async function detect(): Promise<FfmpegCapabilities> {
66
- const ffmpeg = Bun.which("ffmpeg");
67
- const ffprobe = Bun.which("ffprobe");
103
+ const ffmpeg = findFfmpegBinary("ffmpeg");
104
+ const ffprobe = findFfmpegBinary("ffprobe");
68
105
  if (!ffmpeg) return { ffmpeg: null, ffprobe, encoder: null };
69
106
  for (const candidate of encoderCandidates()) {
70
107
  if (await encoderWorks(ffmpeg, candidate)) return { ffmpeg, ffprobe, encoder: candidate };
@@ -72,10 +109,14 @@ async function detect(): Promise<FfmpegCapabilities> {
72
109
  return { ffmpeg, ffprobe, encoder: null };
73
110
  }
74
111
 
75
- /** Cached capability lookup; the first call pays for the encoder probes. */
112
+ /** Cached capability lookup; the first call that finds ffmpeg pays for the encoder probes.
113
+ * "Not installed" is never cached — the user may be running `brew install ffmpeg` right now. */
76
114
  export function getFfmpegCapabilities(): Promise<FfmpegCapabilities> {
77
115
  if (!cached) {
78
- cached = detect().catch((e) => {
116
+ cached = detect().then((caps) => {
117
+ if (!caps.ffmpeg) cached = null;
118
+ return caps;
119
+ }).catch((e) => {
79
120
  cached = null; // let a later call retry after an unexpected failure
80
121
  throw e;
81
122
  });
@@ -0,0 +1,26 @@
1
+ /**
2
+ * Backoff schedule for re-attempting a named tunnel after it lost a spawn and
3
+ * the supervisor fell back to a quick URL.
4
+ *
5
+ * The fallback exists so a broken named setup never leaves the machine dark,
6
+ * but it used to be one-way: once quick was live, named was only retried on the
7
+ * next supervisor start or an explicit `retunnel`. That is wrong for a
8
+ * *transient* failure — observed live, a laptop waking from sleep spawns the
9
+ * connector before Wi-Fi is back, the 30s readiness wait expires, and the
10
+ * permanent hostname then serves 530 for hours even though the network
11
+ * recovered seconds later.
12
+ */
13
+
14
+ /** 1min, 5min, 15min — then stop and leave the warning standing. */
15
+ export const NAMED_RETRY_DELAYS_MS = [60_000, 300_000, 900_000] as const;
16
+
17
+ /**
18
+ * Delay before retry number `attempt` (0-based), or null once the schedule is
19
+ * exhausted. Bounded on purpose: a genuinely broken setup (deleted tunnel,
20
+ * revoked token) must settle on quick with a visible warning rather than
21
+ * restart the connector forever.
22
+ */
23
+ export function nextNamedRetryDelayMs(attempt: number): number | null {
24
+ if (!Number.isInteger(attempt) || attempt < 0) return null;
25
+ return NAMED_RETRY_DELAYS_MS[attempt] ?? null;
26
+ }
@@ -0,0 +1,63 @@
1
+ /**
2
+ * "Is the network back yet?" gate for the named-tunnel spawn path.
3
+ *
4
+ * A connector started before the machine's Wi-Fi has reassociated cannot
5
+ * register, and the readiness wait then expires on a failure that would have
6
+ * cleared itself seconds later. Observed live after a laptop resumed from
7
+ * sleep: the supervisor regenerated the tunnel immediately, named timed out,
8
+ * and the permanent hostname served 530 until someone intervened.
9
+ *
10
+ * Only the named path waits: a quick tunnel that fails is retried with a fresh
11
+ * URL anyway, while a named failure costs the user their fixed address.
12
+ */
13
+
14
+ /** Cheap, unauthenticated, and the exact host the connector needs anyway. */
15
+ const PROBE_URL = "https://api.cloudflare.com/client/v4/";
16
+
17
+ export interface NetworkReadyOptions {
18
+ /** Give up after this long and let the caller try regardless. */
19
+ timeoutMs?: number;
20
+ /** Wait between probes. */
21
+ intervalMs?: number;
22
+ /** Per-probe timeout. */
23
+ probeTimeoutMs?: number;
24
+ /** Injectable for tests; defaults to a HEAD against Cloudflare's API. */
25
+ probe?: () => Promise<boolean>;
26
+ sleep?: (ms: number) => Promise<void>;
27
+ now?: () => number;
28
+ }
29
+
30
+ const defaultSleep = (ms: number) => new Promise<void>((r) => setTimeout(r, ms));
31
+
32
+ async function defaultProbe(probeTimeoutMs: number): Promise<boolean> {
33
+ try {
34
+ // Any HTTP answer proves DNS + TCP + TLS to Cloudflare work; the status
35
+ // code is irrelevant (401/404 are just as good a signal as 200).
36
+ await fetch(PROBE_URL, { method: "HEAD", signal: AbortSignal.timeout(probeTimeoutMs) });
37
+ return true;
38
+ } catch {
39
+ return false;
40
+ }
41
+ }
42
+
43
+ /**
44
+ * Resolves `true` as soon as Cloudflare is reachable, `false` if the budget
45
+ * runs out. Never throws: a failed wait is a hint, not a veto — the caller
46
+ * still attempts the spawn, because a probe can fail for reasons (proxy, DNS
47
+ * filtering) that would not stop the connector itself.
48
+ */
49
+ export async function waitForCloudflareReachable(opts: NetworkReadyOptions = {}): Promise<boolean> {
50
+ const timeoutMs = opts.timeoutMs ?? 60_000;
51
+ const intervalMs = opts.intervalMs ?? 2_000;
52
+ const probeTimeoutMs = opts.probeTimeoutMs ?? 3_000;
53
+ const probe = opts.probe ?? (() => defaultProbe(probeTimeoutMs));
54
+ const sleep = opts.sleep ?? defaultSleep;
55
+ const now = opts.now ?? Date.now;
56
+
57
+ const deadline = now() + timeoutMs;
58
+ for (;;) {
59
+ if (await probe()) return true;
60
+ if (now() >= deadline) return false;
61
+ await sleep(intervalMs);
62
+ }
63
+ }
@@ -22,6 +22,8 @@ import {
22
22
  import type { ResolvedTunnelConfig, TunnelMode } from "./named-tunnel/named-tunnel-config.ts";
23
23
  import { readTunnelConfigFresh, chooseTunnelSpawn } from "./named-tunnel/named-tunnel-runtime.ts";
24
24
  import { waitForLogLine } from "./named-tunnel/named-tunnel-readiness.ts";
25
+ import { nextNamedRetryDelayMs } from "./named-tunnel/named-tunnel-retry.ts";
26
+ import { waitForCloudflareReachable } from "./named-tunnel/network-ready.ts";
25
27
  import { decideNamedProbeAction, publicHostnameIsOurs } from "./named-tunnel/named-tunnel-probe-state.ts";
26
28
  import { isCloudflaredPid } from "./tunnel-registry.service.ts";
27
29
  import { getQuickTunnelArgs } from "./cloudflared.service.ts";
@@ -102,6 +104,18 @@ let namedTunnelMode: ResolvedTunnelConfig | null = null;
102
104
  // but live=quick, and every status write / throttle / restart decision must
103
105
  // key off what's actually running.
104
106
  let lastSpawnMode: TunnelMode = "quick";
107
+ // How long to wait for the network before attempting a named tunnel.
108
+ const NETWORK_WAIT_MS = 60_000;
109
+ // Falling back to quick used to be permanent until the next supervisor start.
110
+ // These drive a bounded set of retries so a transient failure (network not up
111
+ // yet after a resume) heals itself instead of leaving the hostname dark.
112
+ let namedRetryAttempt = 0;
113
+ let namedRetryTimer: ReturnType<typeof setTimeout> | null = null;
114
+ // A retry that finds the machine still offline re-arms the same rung instead
115
+ // of burning it. Bounded so a laptop left off the network doesn't log a
116
+ // deferral every minute for the rest of its uptime.
117
+ let namedRetryDeferrals = 0;
118
+ const MAX_NAMED_RETRY_DEFERRALS = 10;
105
119
  // Set once the named probe has already tried a restart-and-hope; a second
106
120
  // consecutive failure then warns and stops instead of looping kills forever.
107
121
  let namedProbeRestartAttempted = false;
@@ -293,6 +307,72 @@ async function ensureBindablePort(preferred: number, host: string): Promise<numb
293
307
  * The trycloudflare URL will change — unavoidable, the old quick-tunnel session
294
308
  * is dead. Fire-and-forget: spawnTunnel's own loop owns liveness afterwards.
295
309
  */
310
+ /** Cancel a pending named retry — a newer decision supersedes it. */
311
+ function cancelNamedRetry(): void {
312
+ if (namedRetryTimer) { clearTimeout(namedRetryTimer); namedRetryTimer = null; }
313
+ }
314
+
315
+ /**
316
+ * After a downgrade to quick, try named again on a bounded backoff.
317
+ *
318
+ * Re-checks everything at fire time rather than trusting the state captured
319
+ * when the timer was set: the user may have switched to quick deliberately, a
320
+ * `retunnel` may already have restored named, or the supervisor may be going
321
+ * down. Any of those makes the retry pointless or actively wrong.
322
+ */
323
+ function scheduleNamedRetry(port: number): void {
324
+ cancelNamedRetry();
325
+ const delay = nextNamedRetryDelayMs(namedRetryAttempt);
326
+ if (delay === null) {
327
+ log("WARN", "Named tunnel still failing after the last retry — staying on the quick URL");
328
+ return;
329
+ }
330
+ const attemptNumber = namedRetryAttempt + 1;
331
+ log("INFO", `Named tunnel downgraded to quick — retrying in ${Math.round(delay / 1000)}s (#${attemptNumber})`);
332
+ namedRetryTimer = setTimeout(() => {
333
+ namedRetryTimer = null;
334
+ if (shuttingDown || getState() !== "running") return;
335
+ if (namedTunnelMode?.mode !== "named") return; // user switched to quick meanwhile
336
+ if (lastSpawnMode === "named") return; // already recovered
337
+ // Restarting tears down the quick tunnel that is currently serving the
338
+ // machine, so only do it once the network can actually carry a connector —
339
+ // otherwise a still-offline laptop loses its working temporary URL too and
340
+ // gets a new one for nothing. Offline: re-arm the same rung, don't burn it.
341
+ void waitForCloudflareReachable({ timeoutMs: 5_000, intervalMs: 1_000 }).then((reachable) => {
342
+ if (shuttingDown || getState() !== "running") return;
343
+ if (namedTunnelMode?.mode !== "named" || lastSpawnMode === "named") return;
344
+ if (!reachable) {
345
+ if (++namedRetryDeferrals > MAX_NAMED_RETRY_DEFERRALS) {
346
+ log("WARN", "Named retry abandoned — network still down after repeated attempts");
347
+ return;
348
+ }
349
+ log("INFO", `Named retry #${attemptNumber} deferred — network still down`);
350
+ scheduleNamedRetry(port);
351
+ return;
352
+ }
353
+ namedRetryDeferrals = 0;
354
+ namedRetryAttempt = attemptNumber;
355
+ log("INFO", `Retrying the named tunnel (#${attemptNumber})`);
356
+ restartTunnel(port);
357
+ });
358
+ }, delay);
359
+ namedRetryTimer.unref?.();
360
+ }
361
+
362
+ /**
363
+ * Called once per successful spawn: arm the retry ladder when we had to fall
364
+ * back to quick, clear it the moment named is actually live again.
365
+ */
366
+ function noteSpawnOutcomeForNamedRetry(mode: TunnelMode, downgraded: boolean, port: number): void {
367
+ if (mode === "named") {
368
+ cancelNamedRetry();
369
+ namedRetryAttempt = 0;
370
+ namedRetryDeferrals = 0;
371
+ return;
372
+ }
373
+ if (downgraded) scheduleNamedRetry(port);
374
+ }
375
+
296
376
  function restartTunnel(port: number) {
297
377
  lastTunnelRegenAt = Date.now();
298
378
  if (tunnelChild) { try { tunnelChild.kill(); } catch {} tunnelChild = null; }
@@ -744,6 +824,15 @@ export async function spawnTunnel(port: number, generation: number = ++tunnelGen
744
824
  }
745
825
 
746
826
  const attempts = buildTunnelAttempts(namedTunnelMode, port);
827
+ // Only the named path pays for this: its failure costs the user their fixed
828
+ // address, while a quick tunnel just retries with a new URL. Spawning a
829
+ // connector before the machine's network is back is the difference between
830
+ // "reconnects in 5s" and "hostname dark until someone notices".
831
+ if (attempts[0]?.mode === "named") {
832
+ const reachable = await waitForCloudflareReachable({ timeoutMs: NETWORK_WAIT_MS });
833
+ if (!reachable) log("WARN", `Cloudflare unreachable after ${NETWORK_WAIT_MS / 1000}s — attempting the tunnel anyway`);
834
+ if (generation !== tunnelGeneration) return; // superseded while waiting
835
+ }
747
836
  const logPath = cloudflaredLogPath();
748
837
  // Truncate stale content from a prior generation; best-effort — Windows can
749
838
  // silently fail this while a previous detached cloudflared still holds the
@@ -821,6 +910,7 @@ export async function spawnTunnel(port: number, generation: number = ++tunnelGen
821
910
  adoptedTunnelPid = winPid;
822
911
  tunnelChild = null;
823
912
  lastSpawnMode = successfulMode;
913
+ noteSpawnOutcomeForNamedRetry(successfulMode, downgraded, port);
824
914
  // `namedProbeRestartAttempted` is deliberately NOT touched here — see the
825
915
  // probe's `decideNamedProbeAction` state machine. A successful spawn only
826
916
  // proves cloudflared reconnected to Cloudflare's edge, not that the
@@ -908,6 +998,7 @@ export async function spawnTunnel(port: number, generation: number = ++tunnelGen
908
998
  }
909
999
 
910
1000
  lastSpawnMode = successfulMode;
1001
+ noteSpawnOutcomeForNamedRetry(successfulMode, downgraded, port);
911
1002
  // `namedProbeRestartAttempted` is deliberately NOT touched here — see the
912
1003
  // probe's `decideNamedProbeAction` state machine and the win32 branch above
913
1004
  // for why a bare spawn success must never re-arm the one-restart budget.
@@ -1708,6 +1799,7 @@ export async function softStop() {
1708
1799
  export function shutdown() {
1709
1800
  if (shuttingDown) return;
1710
1801
  shuttingDown = true;
1802
+ cancelNamedRetry();
1711
1803
  log("INFO", "Supervisor shutting down");
1712
1804
 
1713
1805
  // Unblock if paused
@@ -1910,6 +2002,10 @@ export async function runSupervisor(opts: {
1910
2002
  // over from the outage trips "restart once" after a handful of probes.
1911
2003
  namedProbeRestartAttempted = false;
1912
2004
  tunnelFailCount = 0;
2005
+ // A deliberate retunnel supersedes any pending automatic retry, and
2006
+ // restarts the ladder so a later transient failure gets a full budget.
2007
+ cancelNamedRetry();
2008
+ namedRetryAttempt = 0;
1913
2009
  restartTunnel(_opts.port);
1914
2010
  // Deliberate fall-through (no return): a bare `ppm restart` sends a
1915
2011
  // bare SIGUSR2 with no command file of its own, and a `retunnel` that
@@ -2059,6 +2155,8 @@ export async function runSupervisor(opts: {
2059
2155
  // rather than inheriting a stale exhausted one (flag AND counter).
2060
2156
  namedProbeRestartAttempted = false;
2061
2157
  tunnelFailCount = 0;
2158
+ cancelNamedRetry();
2159
+ namedRetryAttempt = 0;
2062
2160
  restartTunnel(_opts.port);
2063
2161
  });
2064
2162
  }
@@ -1,5 +1,6 @@
1
1
  import { readImageDimensions } from "./image-dimensions.ts";
2
2
  import { base64ByteLength, imagePlaceholderText } from "../shared/tool-result-content.ts";
3
+ import { MAX_IMAGE_DIMENSION } from "../shared/image-limits.ts";
3
4
 
4
5
  /**
5
6
  * Auditing and removal of image payloads inside a Claude Code session transcript.
@@ -11,9 +12,14 @@ import { base64ByteLength, imagePlaceholderText } from "../shared/tool-result-co
11
12
  *
12
13
  * Images live in two places, and they are not equally safe to remove. A tool result can be
13
14
  * produced again by re-reading the file it came from, so those are removed by default. An
14
- * image the user attached to a message usually exists nowhere else, so it is left alone
15
- * unless the caller opts in — which it must be able to do, because a single oversized
16
- * attachment is enough to make every later turn of the session fail outright.
15
+ * attachment is left alone unless the caller opts in.
16
+ *
17
+ * That default is now more cautious than it needs to be for images this app sent itself: the
18
+ * composer uploads a copy before embedding the payload, and leaves the path in the message, so
19
+ * such an attachment is as re-readable as a tool result. It stays opt-in because a transcript
20
+ * also collects attachments from elsewhere — another client, an older build, a session shared
21
+ * with the CLI — and for those the payload really is the last copy. The caller must be able to
22
+ * reach it either way: one oversized attachment fails every later turn of the session.
17
23
  *
18
24
  * A tool result's payload sits at `message.content[].content[]`; an attachment sits directly
19
25
  * at `message.content[]`. A record also carries an image-shaped echo at the top-level
@@ -28,7 +34,7 @@ import { base64ByteLength, imagePlaceholderText } from "../shared/tool-result-co
28
34
  * Comparisons here are `>=` rather than `>` for that reason: treating 2000px as acceptable
29
35
  * hides the one image the API is rejecting and makes a cleanup look like a no-op.
30
36
  */
31
- export const MANY_IMAGE_DIMENSION_LIMIT = 2000;
37
+ export const MANY_IMAGE_DIMENSION_LIMIT = MAX_IMAGE_DIMENSION;
32
38
 
33
39
  /** Base64 characters decoded to inspect a header — a JPEG frame can sit behind EXIF. */
34
40
  const HEADER_CHARS = 4096;
@@ -38,9 +44,10 @@ export type StripMode = "oversized" | "all";
38
44
  /** Which of the two image locations a scan or strip should cover. */
39
45
  export interface ImageScopeOpts {
40
46
  /**
41
- * Also cover images the user attached to their own messages. Off by default: the payload
42
- * in the transcript is typically the only remaining copy, so removing it is lossy in a way
43
- * removing a tool result is not.
47
+ * Also cover images the user attached to their own messages. Off by default, because a
48
+ * transcript may hold attachments this app never uploaded from another client or an older
49
+ * build for which the embedded payload is the last copy. Attachments the composer sent
50
+ * keep an uploaded file and its path, so removing those is recoverable.
44
51
  */
45
52
  includeAttachments?: boolean;
46
53
  }
@@ -117,10 +117,15 @@ let upgradeInProgress = false;
117
117
 
118
118
  /** Build the global-install command. Absolute runtime path (not a bare
119
119
  * "bun"/"npm") because autostart (launchd/systemd) may not have the runtime's
120
- * bin dir on $PATH, where a bare name fails with "Executable not found". */
120
+ * bin dir on $PATH, where a bare name fails with "Executable not found".
121
+ *
122
+ * bun gets `--no-cache`: it resolves versions from a cached package manifest
123
+ * and, right after a release, that manifest predates the version the update
124
+ * check just found — `bun install -g pkg@new` then fails with "No version
125
+ * matching … (but package exists)" until the cache happens to expire. */
121
126
  export function buildUpgradeCommand(method: "bun" | "npm", pkg: string): string[] {
122
127
  return method === "bun"
123
- ? [process.execPath, "install", "-g", pkg]
128
+ ? [process.execPath, "install", "-g", "--no-cache", pkg]
124
129
  : [resolveNpmBin(), "install", "-g", pkg];
125
130
  }
126
131
 
@@ -0,0 +1,37 @@
1
+ /**
2
+ * The per-image dimension ceiling the API applies, and the arithmetic for staying under it.
3
+ *
4
+ * Shared because both ends of the pipeline need the same number: the browser downscales an
5
+ * attachment before it is ever uploaded, and the transcript tooling reports on images that
6
+ * slipped in from elsewhere (another client, an older build) still carrying the full size.
7
+ */
8
+
9
+ /**
10
+ * Longest side, in pixels, at which the API starts refusing an image once a request carries
11
+ * several of them. The value is a ceiling, so an image measuring exactly this is already over
12
+ * it — comparisons are `>=`, and a downscale targets strictly below.
13
+ */
14
+ export const MAX_IMAGE_DIMENSION = 2000;
15
+
16
+ /**
17
+ * Dimensions that bring an image under the cap, or null when it is already small enough.
18
+ *
19
+ * Scales the longest side to one pixel below the cap and takes the other side with it, so the
20
+ * aspect ratio survives and the result cannot land back on the boundary through rounding.
21
+ */
22
+ export function fitWithin(
23
+ width: number,
24
+ height: number,
25
+ max: number = MAX_IMAGE_DIMENSION,
26
+ ): { width: number; height: number } | null {
27
+ if (!(width > 0) || !(height > 0)) return null;
28
+ const longest = Math.max(width, height);
29
+ if (longest < max) return null;
30
+
31
+ const target = max - 1;
32
+ const scale = target / longest;
33
+ return {
34
+ width: width >= height ? target : Math.max(1, Math.round(width * scale)),
35
+ height: height > width ? target : Math.max(1, Math.round(height * scale)),
36
+ };
37
+ }
package/src/types/api.ts CHANGED
@@ -23,7 +23,9 @@ export type TerminalWsMessage =
23
23
 
24
24
  /** WebSocket message types (chat) */
25
25
  export type ChatWsClientMessage =
26
- | { type: "message"; content: string; permissionMode?: string; priority?: 'now' | 'next' | 'later'; images?: Array<{ data: string; mediaType: string }>; model?: string; effort?: string; thinking?: boolean }
26
+ | { type: "message"; content: string; permissionMode?: string; priority?: 'now' | 'next' | 'later'; images?: Array<{ data: string; mediaType: string }>;
27
+ /** Uploaded paths for the same images, for providers that take a file not a payload. */
28
+ imagePaths?: string[]; model?: string; effort?: string; thinking?: boolean }
27
29
  | { type: "cancel" }
28
30
  | { type: "set_model"; model: string }
29
31
  | { type: "set_effort"; effort: string }
package/src/types/chat.ts CHANGED
@@ -2,6 +2,12 @@ export interface SendMessageOpts {
2
2
  permissionMode?: import("./config").PermissionMode | string;
3
3
  priority?: 'now' | 'next' | 'later';
4
4
  images?: Array<{ data: string; mediaType: string }>;
5
+ /**
6
+ * Uploaded paths for the same attachments, for providers that take a file rather than a
7
+ * payload. Codex's turn input accepts `localImage` by path and has no base64 form, so an
8
+ * image reaches it this way or not at all.
9
+ */
10
+ imagePaths?: string[];
5
11
  /** Per-session model override; falls back to provider config model when absent */
6
12
  model?: string;
7
13
  /** Override the provider's 1M-context setting for this call (false = never add the
@@ -22,6 +22,8 @@ export interface TreeItemMsg {
22
22
  description?: string;
23
23
  tooltip?: string;
24
24
  icon?: string;
25
+ /** Label wraps instead of truncating — for rows whose text is the content. */
26
+ wrap?: boolean;
25
27
  collapsibleState: "none" | "collapsed" | "expanded";
26
28
  command?: string;
27
29
  children?: TreeItemMsg[];
@@ -52,6 +52,16 @@ export function AttachmentChips({ attachments, onRemove }: AttachmentChipsProps)
52
52
  <Loader2 className="size-3 shrink-0 animate-spin text-text-subtle" />
53
53
  ) : att.status === "error" ? (
54
54
  <span className="text-error shrink-0" title="Upload failed">!</span>
55
+ ) : att.resized ? (
56
+ // Says what happened to the image. A screenshot arriving at the model smaller
57
+ // than the one that was pasted is worth stating rather than leaving to be
58
+ // discovered, and the measurement was already being recorded for nothing.
59
+ <span
60
+ className="shrink-0 text-[10px] text-text-subtle"
61
+ title={`Shrunk from ${att.resized.from.width}x${att.resized.from.height} to ${att.resized.to.width}x${att.resized.to.height}`}
62
+ >
63
+ {att.resized.to.width}&times;{att.resized.to.height}
64
+ </span>
55
65
  ) : null}
56
66
 
57
67
  {/* Remove button */}
@@ -2,6 +2,8 @@ import { useState, useCallback, useRef, useEffect } from "react";
2
2
  import { Loader2, Upload, X } from "lucide-react";
3
3
  import { toast } from "sonner";
4
4
  import { api, projectUrl } from "@/lib/api-client";
5
+ import { selectInlineImages } from "@/lib/image-resize-limits";
6
+ import { splitAttachmentMarkers } from "@/lib/attachment-marker-split";
5
7
  import { useChat } from "@/hooks/use-chat";
6
8
  import { useUsage } from "@/hooks/use-usage";
7
9
  import { useTabStore } from "@/stores/tab-store";
@@ -56,7 +58,7 @@ export function ChatTab({ metadata, tabId }: ChatTabProps) {
56
58
  );
57
59
 
58
60
  // Pending message to send after WS connects (replaces unreliable setTimeout)
59
- const pendingSendRef = useRef<{ content: string; permissionMode?: string } | null>(null);
61
+ const pendingSendRef = useRef<{ content: string; permissionMode?: string; images?: Array<{ data: string; mediaType: string }>; imagePaths?: string[] } | null>(null);
60
62
 
61
63
  // Drag-and-drop state
62
64
  const [isDragging, setIsDragging] = useState(false);
@@ -142,9 +144,9 @@ export function ChatTab({ metadata, tabId }: ChatTabProps) {
142
144
  // Flush pending message once WS connects (replaces unreliable setTimeout)
143
145
  useEffect(() => {
144
146
  if (isConnected && pendingSendRef.current) {
145
- const { content, permissionMode: pm } = pendingSendRef.current;
147
+ const { content, permissionMode: pm, images: pendingImages, imagePaths: pendingPaths } = pendingSendRef.current;
146
148
  pendingSendRef.current = null;
147
- sendMessage(content, { permissionMode: pm });
149
+ sendMessage(content, { permissionMode: pm, ...(pendingImages?.length && { images: pendingImages }), ...(pendingPaths?.length && { imagePaths: pendingPaths }) });
148
150
  }
149
151
  }, [isConnected, sendMessage]);
150
152
 
@@ -350,7 +352,11 @@ export function ChatTab({ metadata, tabId }: ChatTabProps) {
350
352
 
351
353
  /** Build message content with file references and inline text snippets prepended */
352
354
  const buildMessageWithAttachments = useCallback(
353
- (content: string, attachments: ChatAttachment[]): string => {
355
+ (
356
+ content: string,
357
+ attachments: ChatAttachment[],
358
+ inlineImages: Array<{ data: string; mediaType: string }> = [],
359
+ ): string => {
354
360
  if (attachments.length === 0) return content;
355
361
 
356
362
  const parts: string[] = [];
@@ -360,12 +366,27 @@ export function ChatTab({ metadata, tabId }: ChatTabProps) {
360
366
  if (att.textContent) parts.push(att.textContent);
361
367
  }
362
368
 
363
- // Server-uploaded file references
364
- const fileAtts = attachments.filter((a) => a.serverPath);
365
- if (fileAtts.length > 0) {
366
- const fileRefs = fileAtts.map((a) => a.serverPath!).join("\n");
369
+ // Server-uploaded file references. An image keeps its path even when the payload rides
370
+ // along with the message, because the transcript is what the chat re-renders from and
371
+ // the path is the only thing in it a thumbnail can be drawn from.
372
+ //
373
+ // An image sent inline says so in the marker. A bare path in a user message reads as an
374
+ // invitation to open it, and a model that takes it pays for the same picture twice —
375
+ // once inline, once as a tool result, both replayed on every later turn.
376
+ //
377
+ // Which marker an image gets follows the payloads that are actually going out, not
378
+ // `imageData` alone: the per-message caps can leave one behind, and announcing that its
379
+ // contents are included would hand the model neither the picture nor a reason to open
380
+ // the file. `inlineImages` defaults to none, so a caller that sends no payloads at all
381
+ // (the edit-fork path takes only text) gets the plain markers throughout.
382
+ const { inlineImagePaths, pathOnlyPaths } = splitAttachmentMarkers(attachments, inlineImages);
383
+ for (const path of inlineImagePaths) {
384
+ parts.push(`[Attached image (contents included in this message): ${path}]`);
385
+ }
386
+ if (pathOnlyPaths.length > 0) {
387
+ const fileRefs = pathOnlyPaths.join("\n");
367
388
  parts.push(
368
- fileAtts.length === 1
389
+ pathOnlyPaths.length === 1
369
390
  ? `[Attached file: ${fileRefs}]`
370
391
  : `[Attached files:\n${fileRefs}\n]`,
371
392
  );
@@ -379,8 +400,11 @@ export function ChatTab({ metadata, tabId }: ChatTabProps) {
379
400
 
380
401
  const handleSend = useCallback(
381
402
  async (content: string, attachments: ChatAttachment[] = [], priority?: MessagePriority) => {
382
- const fullContent = buildMessageWithAttachments(content, attachments);
383
- if (!fullContent.trim()) return;
403
+ const images = selectInlineImages(attachments);
404
+ const fullContent = buildMessageWithAttachments(content, attachments, images);
405
+ // Providers that take a file rather than a payload (codex) read these instead.
406
+ const imagePaths = attachments.filter((a) => a.isImage && a.serverPath).map((a) => a.serverPath!);
407
+ if (!fullContent.trim() && images.length === 0) return;
384
408
 
385
409
  if (!sessionId) {
386
410
  try {
@@ -394,14 +418,14 @@ export function ChatTab({ metadata, tabId }: ChatTabProps) {
394
418
  setSessionId(session.id);
395
419
  setProviderId(session.providerId);
396
420
  // Queue message — will be sent by effect when WS reports isConnected
397
- pendingSendRef.current = { content: fullContent, permissionMode };
421
+ pendingSendRef.current = { content: fullContent, permissionMode, images, imagePaths };
398
422
  return;
399
423
  } catch (e) {
400
424
  console.error("Failed to create session:", e);
401
425
  return;
402
426
  }
403
427
  }
404
- sendMessage(fullContent, { permissionMode, priority });
428
+ sendMessage(fullContent, { permissionMode, priority, ...(images.length > 0 && { images }), ...(imagePaths.length > 0 && { imagePaths }) });
405
429
  },
406
430
  [sessionId, providerId, projectName, sendMessage, buildMessageWithAttachments, permissionMode, metadata],
407
431
  );