@hienlh/ppm 0.18.2 → 0.18.4
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.
- package/CHANGELOG.md +22 -0
- package/CLAUDE.md +1 -0
- package/assets/skills/ppm/SKILL.md +1 -1
- package/assets/skills/ppm/references/http-api.md +6 -1
- package/dist/web/assets/{ai-resource-editor-B6CODu6u.js → ai-resource-editor-Bb-NNlp1.js} +1 -1
- package/dist/web/assets/{angular-html-C-Kjr43S.js → angular-html-jfMqUuRd.js} +1 -1
- package/dist/web/assets/{angular-ts-CaDDbZqm.js → angular-ts-wytn9PMm.js} +1 -1
- package/dist/web/assets/api-client-CzABTreM.js +1 -0
- package/dist/web/assets/{api-settings-CAhcNpj5.js → api-settings-DfNRPr1q.js} +1 -1
- package/dist/web/assets/api-settings-DlcoR5QT.js +1 -0
- package/dist/web/assets/{api-themes-CAjg_Bt9.js → api-themes-CEyYK7AK.js} +1 -1
- package/dist/web/assets/{apl-B1WsQFAb.js → apl-i-ahKx6O.js} +1 -1
- package/dist/web/assets/architecture-PBZL5I3N-Bs7wzj-W.js +1 -0
- package/dist/web/assets/arrow-right-DubGFkN8.js +1 -0
- package/dist/web/assets/{astro-Bdnz7nVp.js → astro-DVBCJfG6.js} +1 -1
- package/dist/web/assets/audio-preview-DG2Tw0cO.js +1 -0
- package/dist/web/assets/{blade-B2euh8sS.js → blade-BHlL8SqU.js} +1 -1
- package/dist/web/assets/{bsl-GNeMaxd0.js → bsl-D-UOfR1F.js} +1 -1
- package/dist/web/assets/button-B4MWPjWj.js +1 -0
- package/dist/web/assets/{cairo-D7G0W7-s.js → cairo-Bn6rucli.js} +1 -1
- package/dist/web/assets/chat-tab-CgAv-crh.js +15 -0
- package/dist/web/assets/{cobol-Q6YgysnI.js → cobol-Byn9FYKu.js} +1 -1
- package/dist/web/assets/code-editor-bX4jlgMy.js +10 -0
- package/dist/web/assets/{coffee-BjufWxVc.js → coffee-DjBl4gm-.js} +1 -1
- package/dist/web/assets/{conflict-editor-DnSltcyC.js → conflict-editor-B6hVffX3.js} +3 -3
- package/dist/web/assets/{context-menu-q-RHHAWz.js → context-menu-DkQdt6YA.js} +1 -1
- package/dist/web/assets/{cpp-CqWU0gCR.js → cpp-CKzic-3E.js} +1 -1
- package/dist/web/assets/{crystal-ylayVajF.js → crystal-Bkg-Ly_g.js} +1 -1
- package/dist/web/assets/{csv-preview-DnaFdgOB.js → csv-preview-hL4Z9HS5.js} +1 -1
- package/dist/web/assets/{data-grid-overlay-editor-BzPfsCfd.js → data-grid-overlay-editor-B1mTl3KS.js} +1 -1
- package/dist/web/assets/{database-viewer-C7uLy8Z1.js → database-viewer-CTK5kMaA.js} +1 -1
- package/dist/web/assets/diff-viewer-DZxnANY4.js +4 -0
- package/dist/web/assets/{docx-preview-Dd2nTW2v.js → docx-preview-CrAc7Qjv.js} +1 -1
- package/dist/web/assets/download-D9UYwY0-.js +1 -0
- package/dist/web/assets/{edge-DX4NxQfh.js → edge-DVbZnd3Y.js} +1 -1
- package/dist/web/assets/{edit-diff-preview-DfX-9om9.js → edit-diff-preview-HTZPC9VP.js} +1 -1
- package/dist/web/assets/{elixir-CSh3NjbV.js → elixir-Dwi8Wu7W.js} +1 -1
- package/dist/web/assets/{elm-DBzwFYq9.js → elm-DtIv-HXJ.js} +1 -1
- package/dist/web/assets/{erb-yxVOb8aB.js → erb-D3HEDz1x.js} +1 -1
- package/dist/web/assets/{erlang-5XIDkYrM.js → erlang-2MjTCzGz.js} +1 -1
- package/dist/web/assets/{esm-DO4el2OJ.js → esm-CZ8B8Ba2.js} +1 -1
- package/dist/web/assets/explorer-body-DykJXCTw.js +2 -0
- package/dist/web/assets/explorer-window-content-NS8Y0v8c.js +1 -0
- package/dist/web/assets/{extension-webview-Dk_zyoXZ.js → extension-webview-D6eywKPN.js} +1 -1
- package/dist/web/assets/{fortran-fixed-form-D4tE5VWY.js → fortran-fixed-form-BLyAtyPM.js} +1 -1
- package/dist/web/assets/{fsharp-By8soj6-.js → fsharp-Bxbn_gbr.js} +1 -1
- package/dist/web/assets/{gdresource-DoSwM7Hv.js → gdresource-sFMY4D2Q.js} +1 -1
- package/dist/web/assets/{git-commit-_ZiciHIx.js → git-commit-BzB9Lio5.js} +1 -1
- package/dist/web/assets/{git-log-panel-BLQSy6rC.js → git-log-panel-DAogZ9QV.js} +1 -1
- package/dist/web/assets/{git-rebase-C3Y5N9-s.js → git-rebase-CKSVd848.js} +1 -1
- package/dist/web/assets/gitGraph-HDMCJU4V-D7i54kbM.js +1 -0
- package/dist/web/assets/glide-data-grid-DxfJRN-r.js +138 -0
- package/dist/web/assets/{glimmer-js-BPBTXVlC.js → glimmer-js-BY8aExAJ.js} +1 -1
- package/dist/web/assets/{glimmer-ts-BQSZdy_D.js → glimmer-ts-Bak54dBX.js} +1 -1
- package/dist/web/assets/{glsl-CbQdxpU-.js → glsl-nR2eaJQe.js} +1 -1
- package/dist/web/assets/{graphql-GMlKikpY.js → graphql-CGPPFAU6.js} +1 -1
- package/dist/web/assets/group-chat-tab-DkVi6qL2.js +1 -0
- package/dist/web/assets/{hack-DyshcLec.js → hack-CXIMX-Ol.js} +1 -1
- package/dist/web/assets/{haml-rqCqWzsq.js → haml-tPY-9Pfm.js} +1 -1
- package/dist/web/assets/{handlebars-BCKwSWjR.js → handlebars-BuW1LT7T.js} +1 -1
- package/dist/web/assets/{html-Cg29xPR1.js → html-bgVcwZ-t.js} +1 -1
- package/dist/web/assets/{html-derivative-geltIVg8.js → html-derivative-D6-IxTgn.js} +1 -1
- package/dist/web/assets/{http-pG7Vp237.js → http-CJ1zyB-h.js} +1 -1
- package/dist/web/assets/{hurl-Di3Uu_Mc.js → hurl-CmoT7l3T.js} +1 -1
- package/dist/web/assets/{hxml-CRy4lbv8.js → hxml-CztOsL8o.js} +1 -1
- package/dist/web/assets/{image-preview-KubLSLNO.js → image-preview-Byjx8MoL.js} +1 -1
- package/dist/web/assets/index-Bucbpby-.css +2 -0
- package/dist/web/assets/index-CcN0TBm4.js +52 -0
- package/dist/web/assets/info-3K5VOQVL-BV41AzR9.js +1 -0
- package/dist/web/assets/{jinja-C8GyVnvd.js → jinja-B1pq3jJq.js} +1 -1
- package/dist/web/assets/{jison-BywnqPn6.js → jison-D8B5pCwA.js} +1 -1
- package/dist/web/assets/{julia-DTrO3T3B.js → julia-D0K3bwpU.js} +1 -1
- package/dist/web/assets/{just-CA3KSHPi.js → just-B9Jlddu-.js} +1 -1
- package/dist/web/assets/keybindings-store-DJIHqbLM.js +1 -0
- package/dist/web/assets/{latex-BC5xUMpc.js → latex-BPJSSE6j.js} +1 -1
- package/dist/web/assets/{liquid-Chd8tSyQ.js → liquid-lv10fo4U.js} +1 -1
- package/dist/web/assets/{lua-DHaEtwLv.js → lua-PNuaIBEF.js} +1 -1
- package/dist/web/assets/{markdown-renderer-B4uL6GVA.js → markdown-renderer-CXlEe3ma.js} +3 -3
- package/dist/web/assets/markdown-renderer-pYznJDbh.js +1 -0
- package/dist/web/assets/{marko-Desx_bC-.js → marko-Du3frD6X.js} +1 -1
- package/dist/web/assets/{mdc-DahWHc1t.js → mdc-BeDVcFRl.js} +1 -1
- package/dist/web/assets/media-url-C5vA0ha9.js +1 -0
- package/dist/web/assets/{mobile-bottom-sheet-BQt45q9T.js → mobile-bottom-sheet-CS3XMwFw.js} +1 -1
- package/dist/web/assets/mobile-explorer-sheet-BuqBlP3o.js +1 -0
- package/dist/web/assets/{nextflow-3A7lutCA.js → nextflow-DkcXP71s.js} +1 -1
- package/dist/web/assets/{nginx-XQda6Hdy.js → nginx-C5n8N_bt.js} +1 -1
- package/dist/web/assets/{nim-DHqlpwP1.js → nim-CIhyfs-A.js} +1 -1
- package/dist/web/assets/notification-store-Bcl5efY2.js +1 -0
- package/dist/web/assets/{number-overlay-editor-DgZtViPO.js → number-overlay-editor-NgOsWJ7x.js} +1 -1
- package/dist/web/assets/packet-RMMSAZCW-CaJoP44m.js +1 -0
- package/dist/web/assets/{panel-store-s6Qx49RK.js → panel-store-DCD82zc1.js} +1 -1
- package/dist/web/assets/{pdf-preview-xZQVUtaV.js → pdf-preview-F2DWVB85.js} +1 -1
- package/dist/web/assets/{perl-M-ncjACb.js → perl-Cf31sZYE.js} +1 -1
- package/dist/web/assets/{php-CKIajrZi.js → php-Dzl_MDIW.js} +1 -1
- package/dist/web/assets/pie-UPGHQEXC-DSGZmNw2.js +1 -0
- package/dist/web/assets/plus-DmjC2iRB.js +1 -0
- package/dist/web/assets/{postgres-viewer-Ds0FxBel.js → postgres-viewer-KZqPYfOB.js} +3 -3
- package/dist/web/assets/{project-store-DyaEpHVG.js → project-store-B8BkHXy5.js} +1 -1
- package/dist/web/assets/{pug-DO0u_gO4.js → pug-CxSYHrB3.js} +1 -1
- package/dist/web/assets/{qml-UEje1PAA.js → qml-DlEjGhwN.js} +1 -1
- package/dist/web/assets/radar-KQ55EAFF-Be4VUTs0.js +1 -0
- package/dist/web/assets/{razor-lCs5T1Ib.js → razor-KVt5uW14.js} +1 -1
- package/dist/web/assets/{rst-CZX_O0z4.js → rst-D5BwNTnw.js} +1 -1
- package/dist/web/assets/{ruby-idycVAyE.js → ruby-D5fejQwd.js} +1 -1
- package/dist/web/assets/run-in-terminal-B8TDtaM_.js +1 -0
- package/dist/web/assets/{sas-o5gTfUwp.js → sas-Bj7B1CB2.js} +1 -1
- package/dist/web/assets/{scss-CRTk3iBQ.js → scss-BB0CUoCL.js} +1 -1
- package/dist/web/assets/send-to-chat-CuBAVC8l.js +1 -0
- package/dist/web/assets/{settings-store-ChqHdT2V.js → settings-store-CY-4_U3H.js} +2 -2
- package/dist/web/assets/settings-tab-Khj-w6Sr.js +1 -0
- package/dist/web/assets/{shaderlab-3e5ln8ci.js → shaderlab-BdjEZUMc.js} +1 -1
- package/dist/web/assets/{shellsession-CGQjQ75I.js → shellsession-Bp1OA1rR.js} +1 -1
- package/dist/web/assets/{soy-CxY0VwX2.js → soy-DlYz3wsC.js} +1 -1
- package/dist/web/assets/{sparql-CBAGAFuq.js → sparql-CdfII5Jd.js} +1 -1
- package/dist/web/assets/sql-query-editor-Cp0_e2P1.js +1 -0
- package/dist/web/assets/{sqlite-viewer-CZYDknNa.js → sqlite-viewer-CIv3sagZ.js} +1 -1
- package/dist/web/assets/{stata-BoezhOvk.js → stata-BvUA6GUW.js} +1 -1
- package/dist/web/assets/{surrealql-CHEzAhu7.js → surrealql--DLNbTyI.js} +1 -1
- package/dist/web/assets/{svelte-y6xtu2zX.js → svelte-CBfSsG4n.js} +1 -1
- package/dist/web/assets/system-monitor-tab-CrssTOf4.js +1 -0
- package/dist/web/assets/{tab-store-C42sktPo.js → tab-store-DAsvpx4t.js} +1 -1
- package/dist/web/assets/team-member-window-content-D-hXax3c.js +1 -0
- package/dist/web/assets/{templ-DbG2y5yD.js → templ-Dv8zpKZW.js} +1 -1
- package/dist/web/assets/terminal-tab-BIQDG-Ti.js +3 -0
- package/dist/web/assets/{tex-CeKQhrQG.js → tex-CsrOiqh1.js} +1 -1
- package/dist/web/assets/tool-cards-Dx-Rrpri.js +8 -0
- package/dist/web/assets/treemap-KZPCXAKY-Cu2zgbxU.js +1 -0
- package/dist/web/assets/{ts-tags-iKKOXJjG.js → ts-tags-3T98k4EJ.js} +1 -1
- package/dist/web/assets/{twig-DK8Vrp-G.js → twig-u6QZ45FH.js} +1 -1
- package/dist/web/assets/{use-blob-url-DYtOHK7u.js → use-blob-url-BmlYYZ8L.js} +1 -1
- package/dist/web/assets/use-is-mobile-B8FA4kQf.js +1 -0
- package/dist/web/assets/use-monaco-theme-Djp1_fKi.js +1 -0
- package/dist/web/assets/use-websocket-CCYA9-a2.js +1 -0
- package/dist/web/assets/{vendor-mermaid-C977xNeE.js → vendor-mermaid-Bg4vE8QA.js} +1 -1
- package/dist/web/assets/video-preview-cvjNMjWA.js +1 -0
- package/dist/web/assets/{vue-JkIEObMV.js → vue-CxIsxtwn.js} +1 -1
- package/dist/web/assets/{vue-html-D8SqnU86.js → vue-html-BGL5qnuH.js} +1 -1
- package/dist/web/assets/{vue-vine-DJj7N4WD.js → vue-vine-DHPh_gMr.js} +1 -1
- package/dist/web/assets/{xml-C-00w9Rv.js → xml-C-vM2m7J.js} +1 -1
- package/dist/web/assets/{xsl-CVdCwPHV.js → xsl-DvTXNwuO.js} +1 -1
- package/dist/web/index.html +21 -20
- package/dist/web/sw.js +1 -1
- package/docs/codebase-summary.md +1 -0
- package/package.json +1 -1
- package/scripts/dev-quick-tunnel-retry.mjs +51 -0
- package/src/providers/claude-agent-sdk.ts +6 -0
- package/src/server/helpers/media-route-handlers.ts +66 -0
- package/src/server/helpers/range-file-response.ts +86 -0
- package/src/server/index.ts +24 -17
- package/src/server/middleware/auth.ts +3 -2
- package/src/server/routes/files.ts +40 -7
- package/src/server/routes/fs-browse.ts +43 -5
- package/src/server/routes/fs-upload.ts +32 -0
- package/src/server/routes/teams.ts +34 -0
- package/src/server/ws/chat.ts +64 -16
- package/src/server/ws/team-inbox-watcher.ts +61 -22
- package/src/server/ws/terminal.ts +24 -6
- package/src/services/fs-ops/fs-ops-upload.service.ts +120 -0
- package/src/services/media-transcode/ffmpeg-capabilities.ts +89 -0
- package/src/services/media-transcode/media-probe.ts +63 -0
- package/src/services/media-transcode/transcode-stream.ts +129 -0
- package/src/services/subagent-transcript-merger.ts +1 -1
- package/src/services/team-member-activity/member-activity.service.ts +117 -0
- package/src/services/team-member-activity/outbound-message-scanner.ts +121 -0
- package/src/services/team-member-activity/subagent-transcript-index.ts +95 -0
- package/src/services/team-member-activity/transcript-tail-summary.ts +125 -0
- package/src/shared/background-agent-status.ts +79 -0
- package/src/types/api.ts +1 -0
- package/src/types/chat.ts +6 -1
- package/src/web/components/chat/chat-history-bar.tsx +4 -2
- package/src/web/components/chat/chat-tab.tsx +1 -0
- package/src/web/components/chat/message-input.tsx +13 -6
- package/src/web/components/chat/mini-markdown.tsx +17 -0
- package/src/web/components/chat/send-message-card.tsx +134 -0
- package/src/web/components/chat/send-message-parse.ts +159 -0
- package/src/web/components/chat/team-activity-panel.tsx +107 -121
- package/src/web/components/chat/team-member-activity-row.tsx +92 -0
- package/src/web/components/chat/team-member-list.tsx +43 -0
- package/src/web/components/chat/team-member-window-content.tsx +97 -0
- package/src/web/components/chat/team-message-badges.ts +18 -0
- package/src/web/components/chat/team-message-list.tsx +88 -0
- package/src/web/components/chat/team-message-preview.ts +87 -0
- package/src/web/components/chat/tool-cards.tsx +100 -23
- package/src/web/components/editor/audio-preview.tsx +6 -7
- package/src/web/components/editor/video-player/use-video-keyboard-shortcuts.ts +62 -0
- package/src/web/components/editor/video-player/video-player-controls.tsx +103 -0
- package/src/web/components/editor/video-player/video-player.tsx +190 -0
- package/src/web/components/editor/video-player/video-settings-menu.tsx +92 -0
- package/src/web/components/editor/video-player/video-transform.ts +49 -0
- package/src/web/components/editor/video-preview.tsx +75 -14
- package/src/web/components/explorer/file-tree.tsx +6 -0
- package/src/web/components/explorer/tree-node-context-menu.tsx +5 -1
- package/src/web/components/floating-window/window-content-registry.ts +5 -0
- package/src/web/components/floating-window/window-store-types.ts +1 -1
- package/src/web/components/os-explorer/actions/explorer-actions-upload.ts +96 -0
- package/src/web/components/os-explorer/actions/use-explorer-actions.ts +23 -2
- package/src/web/components/os-explorer/actions/use-explorer-upload-actions.tsx +41 -0
- package/src/web/components/os-explorer/dnd/use-drop-transfer.tsx +10 -1
- package/src/web/components/os-explorer/dnd/use-entry-row-dnd.ts +8 -1
- package/src/web/components/os-explorer/dnd/use-external-file-drop.ts +84 -0
- package/src/web/components/os-explorer/dnd/use-path-drop-target.ts +46 -3
- package/src/web/components/os-explorer/explorer-body.tsx +1 -1
- package/src/web/components/os-explorer/explorer-context-menu.tsx +10 -0
- package/src/web/components/os-explorer/explorer-dialogs.tsx +2 -0
- package/src/web/components/os-explorer/explorer-sidebar.tsx +10 -5
- package/src/web/components/os-explorer/explorer-toolbar.tsx +32 -2
- package/src/web/components/os-explorer/mobile/mobile-bottom-toolbar.tsx +5 -1
- package/src/web/components/os-explorer/upload/collect-dropped-entries.ts +81 -0
- package/src/web/components/os-explorer/upload/upload-queue.ts +79 -0
- package/src/web/components/os-explorer/upload/use-upload-picker.tsx +74 -0
- package/src/web/components/os-explorer/views/column-row.tsx +3 -1
- package/src/web/components/os-explorer/views/column-view-column.tsx +6 -1
- package/src/web/components/os-explorer/views/icons-view-tile.tsx +1 -1
- package/src/web/components/os-explorer/views/icons-view.tsx +5 -1
- package/src/web/components/os-explorer/views/list-row.tsx +1 -1
- package/src/web/components/os-explorer/views/list-view.tsx +5 -1
- package/src/web/components/shared/markdown-code-block.tsx +3 -7
- package/src/web/components/terminal/terminal-tab.tsx +19 -25
- package/src/web/hooks/use-chat.ts +77 -15
- package/src/web/hooks/use-team-activity-feed.ts +92 -0
- package/src/web/hooks/use-terminal-command-queue.ts +108 -0
- package/src/web/hooks/use-terminal.ts +45 -3
- package/src/web/lib/fs-api.ts +9 -0
- package/src/web/lib/fs-upload-xhr.ts +62 -0
- package/src/web/lib/media-url.ts +51 -0
- package/src/web/lib/open-terminal-at.ts +22 -0
- package/src/web/lib/run-in-terminal.ts +55 -0
- package/src/web/lib/send-to-chat.ts +118 -0
- package/src/web/lib/ws-auth.ts +13 -0
- package/src/web/lib/ws-client.ts +10 -5
- package/.release-run.sh +0 -5
- package/dist/web/assets/api-client-CaRE4HYh.js +0 -1
- package/dist/web/assets/api-settings-DBcXfeJ8.js +0 -1
- package/dist/web/assets/architecture-PBZL5I3N-B4J2-O8Q.js +0 -1
- package/dist/web/assets/audio-preview-BkNAEqcB.js +0 -1
- package/dist/web/assets/chat-tab-B-MEsHYp.js +0 -13
- package/dist/web/assets/code-editor-bC1KAEUA.js +0 -10
- package/dist/web/assets/dialog-CkI7rwrh.js +0 -1
- package/dist/web/assets/diff-viewer-BwElk4q9.js +0 -4
- package/dist/web/assets/explorer-body-DzdXBlWw.js +0 -2
- package/dist/web/assets/explorer-window-content-Dgl1WTNs.js +0 -1
- package/dist/web/assets/gitGraph-HDMCJU4V-CtQ3yXN-.js +0 -1
- package/dist/web/assets/glide-data-grid-QZ_fgjIa.js +0 -138
- package/dist/web/assets/group-chat-tab-BbdVlKFv.js +0 -1
- package/dist/web/assets/index-D-xwjNyX.css +0 -2
- package/dist/web/assets/index-wd3FSHHn.js +0 -52
- package/dist/web/assets/info-3K5VOQVL-Cg0mIJn9.js +0 -1
- package/dist/web/assets/keybindings-store-Bp_c_C3z.js +0 -1
- package/dist/web/assets/markdown-renderer-2asl-3Kk.js +0 -1
- package/dist/web/assets/mobile-explorer-sheet-LWUDaNBV.js +0 -1
- package/dist/web/assets/notification-store-BasUnpt7.js +0 -1
- package/dist/web/assets/packet-RMMSAZCW-D79vAFR_.js +0 -1
- package/dist/web/assets/pie-UPGHQEXC-DYUOH68U.js +0 -1
- package/dist/web/assets/plus-9LatwJMx.js +0 -1
- package/dist/web/assets/radar-KQ55EAFF-B1eznNtd.js +0 -1
- package/dist/web/assets/settings-tab-vhatQ51-.js +0 -1
- package/dist/web/assets/sql-query-editor-BblB2Imt.js +0 -1
- package/dist/web/assets/system-monitor-tab-D8dy-RT_.js +0 -1
- package/dist/web/assets/terminal-tab-CfD22q5x.js +0 -3
- package/dist/web/assets/tool-cards-DNgw1yI6.js +0 -7
- package/dist/web/assets/treemap-KZPCXAKY-SeI2TxLs.js +0 -1
- package/dist/web/assets/use-is-mobile-DnFSkUG2.js +0 -1
- package/dist/web/assets/use-monaco-theme-DEbn-QT3.js +0 -1
- package/dist/web/assets/video-preview-DcUaFYyv.js +0 -1
- package/src/web/components/chat/team-activity-popover.tsx +0 -202
- /package/dist/web/assets/{abap-DrMyiWmK.js → abap-C1K0-wzc.js} +0 -0
- /package/dist/web/assets/{actionscript-3-BlSOyi0y.js → actionscript-3-CJhQVjLA.js} +0 -0
- /package/dist/web/assets/{ada-B_m96icB.js → ada-Bt8r9fME.js} +0 -0
- /package/dist/web/assets/{andromeeda-Ch3vOiZB.js → andromeeda-DF1I8qi3.js} +0 -0
- /package/dist/web/assets/{apache-CdsaXc1n.js → apache-CnlleE6P.js} +0 -0
- /package/dist/web/assets/{apex-0AnsBevk.js → apex-j0C6Xmk6.js} +0 -0
- /package/dist/web/assets/{api-client-rTAGdEKo.js → api-client-BSB14J4w.js} +0 -0
- /package/dist/web/assets/{applescript-CSxLolzy.js → applescript-DO7AnO8U.js} +0 -0
- /package/dist/web/assets/{apply-theme-CMQXnUAa.js → apply-theme-CNj1iA-v.js} +0 -0
- /package/dist/web/assets/{ara-DXZS0jA5.js → ara-i83AydZj.js} +0 -0
- /package/dist/web/assets/{asciidoc-DDy3pH2A.js → asciidoc-DbBA6-sF.js} +0 -0
- /package/dist/web/assets/{asm-Bd17hhqg.js → asm-B5S47Rgr.js} +0 -0
- /package/dist/web/assets/{aurora-x-C62HJRs-.js → aurora-x-De_IaSKw.js} +0 -0
- /package/dist/web/assets/{awk-Cc-nazni.js → awk-CfyLHXqS.js} +0 -0
- /package/dist/web/assets/{ayu-dark-BsfmNWCU.js → ayu-dark-DTLdZ1Si.js} +0 -0
- /package/dist/web/assets/{ayu-light-BZ4HLa4X.js → ayu-light-DTOfkzhu.js} +0 -0
- /package/dist/web/assets/{ayu-mirage-Bhyjyiqr.js → ayu-mirage-DsAECCMv.js} +0 -0
- /package/dist/web/assets/{ballerina-QiWgir3v.js → ballerina-CumGp9A7.js} +0 -0
- /package/dist/web/assets/{bat-DxtlkwN3.js → bat-Ca2SBbYN.js} +0 -0
- /package/dist/web/assets/{beancount-Bt21X7M_.js → beancount-CqyXBwwF.js} +0 -0
- /package/dist/web/assets/{berry-Cksre7lO.js → berry-yl-kJr_H.js} +0 -0
- /package/dist/web/assets/{bibtex-DkakgDlR.js → bibtex-DoFFszjV.js} +0 -0
- /package/dist/web/assets/{bicep-8crmcyXU.js → bicep-CWPi4nfK.js} +0 -0
- /package/dist/web/assets/{bird2-CewQ2I_Z.js → bird2-DXew-ut0.js} +0 -0
- /package/dist/web/assets/{bot-nk1k3ELu.js → bot-CunPuEdW.js} +0 -0
- /package/dist/web/assets/{c-C_6zE8Ha.js → c-clbySDvd.js} +0 -0
- /package/dist/web/assets/{c3-DLWB9-dp.js → c3-B65jvSM1.js} +0 -0
- /package/dist/web/assets/{cadence-D8qYS6pR.js → cadence-Cm8tQTHN.js} +0 -0
- /package/dist/web/assets/{can-open-in-ppm-BzVXoV-d.js → can-open-in-ppm-Cu-XYIgu.js} +0 -0
- /package/dist/web/assets/{catppuccin-frappe-Bh95lNRy.js → catppuccin-frappe-DLeGslh_.js} +0 -0
- /package/dist/web/assets/{catppuccin-latte-C1PlN3G0.js → catppuccin-latte-CR5GeHws.js} +0 -0
- /package/dist/web/assets/{catppuccin-macchiato-CldtgdK4.js → catppuccin-macchiato-p-XgjxhP.js} +0 -0
- /package/dist/web/assets/{catppuccin-mocha-BiwqxX9A.js → catppuccin-mocha-B8QOAZLl.js} +0 -0
- /package/dist/web/assets/{clarity-CGiU22jY.js → clarity-DPulbvKH.js} +0 -0
- /package/dist/web/assets/{clojure-COIC3doh.js → clojure-DFuWS20u.js} +0 -0
- /package/dist/web/assets/{cmake-DWUEQfHv.js → cmake-DejCqcu4.js} +0 -0
- /package/dist/web/assets/{codeowners-BNHiAQea.js → codeowners-BwAyqWIH.js} +0 -0
- /package/dist/web/assets/{codeql-BuKaoS0j.js → codeql-DgSwFjqI.js} +0 -0
- /package/dist/web/assets/{common-lisp-D5IKbxsd.js → common-lisp-CxIVJCtf.js} +0 -0
- /package/dist/web/assets/{coq-BMVS-p94.js → coq-DLSjSnmf.js} +0 -0
- /package/dist/web/assets/{csharp-DMlbXbM4.js → csharp-C1HHzXBp.js} +0 -0
- /package/dist/web/assets/{css-CjkJC89X.js → css-VXsMY--y.js} +0 -0
- /package/dist/web/assets/{csv-DoNVBBf0.js → csv-BEbcJdHz.js} +0 -0
- /package/dist/web/assets/{csv-parser-DHvoTjMF.js → csv-parser-r-Nk1oHT.js} +0 -0
- /package/dist/web/assets/{cue-DoTRbBg-.js → cue-BkDnFqUD.js} +0 -0
- /package/dist/web/assets/{cypher-DVsNdTT3.js → cypher-1cMqojSQ.js} +0 -0
- /package/dist/web/assets/{d-EX7BBSrv.js → d-BnYjRXtH.js} +0 -0
- /package/dist/web/assets/{dark-plus-P_YTHwlh.js → dark-plus-XfbaH7kZ.js} +0 -0
- /package/dist/web/assets/{dart-Do-rgU4S.js → dart-Ca7s-8hK.js} +0 -0
- /package/dist/web/assets/{data-grid-types-CUx8cusN.js → data-grid-types-BXwlFMzs.js} +0 -0
- /package/dist/web/assets/{dax-DxOieacu.js → dax-CmuOw-mY.js} +0 -0
- /package/dist/web/assets/{desktop-CvdV7SBf.js → desktop-C2gJ6Ovb.js} +0 -0
- /package/dist/web/assets/{diff-CNOetGen.js → diff-BCEo1Ej7.js} +0 -0
- /package/dist/web/assets/{dist-CMfLpPW_.js → dist-CUPbqNnL.js} +0 -0
- /package/dist/web/assets/{dist-DduBIVkD.js → dist-DOlBnxFX.js} +0 -0
- /package/dist/web/assets/{docker-DAlG4_q-.js → docker-DN2jlyAh.js} +0 -0
- /package/dist/web/assets/{dotenv-Dh3qf6yx.js → dotenv-NExMY2kS.js} +0 -0
- /package/dist/web/assets/{dracula-baAGmJYG.js → dracula-B9lbE01y.js} +0 -0
- /package/dist/web/assets/{dracula-soft-l7ZIyums.js → dracula-soft-CMBBfKF_.js} +0 -0
- /package/dist/web/assets/{dream-maker-DeRjdBn8.js → dream-maker-DEyDwFsd.js} +0 -0
- /package/dist/web/assets/{emacs-lisp-CB05-jQA.js → emacs-lisp-D_qP2zS6.js} +0 -0
- /package/dist/web/assets/{everforest-dark-CT-llQBT.js → everforest-dark-DbHa2U-p.js} +0 -0
- /package/dist/web/assets/{everforest-light-ByPJkTT7.js → everforest-light-BISkJchP.js} +0 -0
- /package/dist/web/assets/{fennel-CeMarbvn.js → fennel-6B22SfJR.js} +0 -0
- /package/dist/web/assets/{file-exclamation-point-COAwgNGw.js → file-exclamation-point-a8f3_w7a.js} +0 -0
- /package/dist/web/assets/{fish-BYrt6Xtb.js → fish-CubIIvJD.js} +0 -0
- /package/dist/web/assets/{fluent-Cr5VcAwh.js → fluent-DF55zQ09.js} +0 -0
- /package/dist/web/assets/{fortran-free-form-DQmKtrLa.js → fortran-free-form-DvOu8jAt.js} +0 -0
- /package/dist/web/assets/{gdscript-QTDKjzhY.js → gdscript-DCPxdLpq.js} +0 -0
- /package/dist/web/assets/{gdshader-Chr-5UYt.js → gdshader-CTZ381FO.js} +0 -0
- /package/dist/web/assets/{genie-kWHMWnt0.js → genie-C_QDtvgU.js} +0 -0
- /package/dist/web/assets/{gherkin-DtEBQFUo.js → gherkin-BcvK42tF.js} +0 -0
- /package/dist/web/assets/{github-dark-B5aiUta2.js → github-dark-C9afs_4w.js} +0 -0
- /package/dist/web/assets/{github-dark-default-BkxtsTqR.js → github-dark-default-CKGVrl3Z.js} +0 -0
- /package/dist/web/assets/{github-dark-dimmed-B9QjR_0K.js → github-dark-dimmed-C3dQZ_LC.js} +0 -0
- /package/dist/web/assets/{github-dark-high-contrast-D6bO-5c0.js → github-dark-high-contrast-DW78QukY.js} +0 -0
- /package/dist/web/assets/{github-light-Cg2ZW_CJ.js → github-light-BnP2dznS.js} +0 -0
- /package/dist/web/assets/{github-light-default-eD7qUVlQ.js → github-light-default-C2fPZzPG.js} +0 -0
- /package/dist/web/assets/{github-light-high-contrast-CFnlSVjd.js → github-light-high-contrast-BzIuUFR2.js} +0 -0
- /package/dist/web/assets/{gleam-BpYoOqIT.js → gleam-BpeKwEIO.js} +0 -0
- /package/dist/web/assets/{gn-BxwtZPau.js → gn-nUb_Mit0.js} +0 -0
- /package/dist/web/assets/{gnuplot-CRyiZDfl.js → gnuplot-D7I0v20p.js} +0 -0
- /package/dist/web/assets/{go-Dw1Qx94W.js → go-D7Qqska7.js} +0 -0
- /package/dist/web/assets/{groovy-zq_SbWk-.js → groovy-DS-pne5-.js} +0 -0
- /package/dist/web/assets/{gruvbox-dark-hard-DbbNy8-Q.js → gruvbox-dark-hard-C0W01fsq.js} +0 -0
- /package/dist/web/assets/{gruvbox-dark-medium-BQZ7Kj6C.js → gruvbox-dark-medium-Bqo6fQbg.js} +0 -0
- /package/dist/web/assets/{gruvbox-dark-soft-vNavErXi.js → gruvbox-dark-soft-D-XSM_fu.js} +0 -0
- /package/dist/web/assets/{gruvbox-light-hard-AOFbCd7i.js → gruvbox-light-hard-IVAwMJRl.js} +0 -0
- /package/dist/web/assets/{gruvbox-light-medium-DqtxND6H.js → gruvbox-light-medium-38ATiG7i.js} +0 -0
- /package/dist/web/assets/{gruvbox-light-soft-Cg0basu6.js → gruvbox-light-soft-DnZr3c_0.js} +0 -0
- /package/dist/web/assets/{haskell-CcsmcOez.js → haskell-uQcywkDH.js} +0 -0
- /package/dist/web/assets/{haxe-BimYY9X4.js → haxe-c6bI4_GT.js} +0 -0
- /package/dist/web/assets/{hcl-CTrXCK40.js → hcl-CQXTiEXq.js} +0 -0
- /package/dist/web/assets/{hjson-CmqqU6Qg.js → hjson-ChETf2bz.js} +0 -0
- /package/dist/web/assets/{hlsl-CuJtX_uf.js → hlsl-BMC4JGoE.js} +0 -0
- /package/dist/web/assets/{horizon-BGeSjDYJ.js → horizon-BusFlhLW.js} +0 -0
- /package/dist/web/assets/{horizon-bright-GYMgd3R0.js → horizon-bright-2oD0Mz7W.js} +0 -0
- /package/dist/web/assets/{houston--JD_5r2q.js → houston-DdbbjqFg.js} +0 -0
- /package/dist/web/assets/{hy-C9ILOiqk.js → hy-D_SOuqw9.js} +0 -0
- /package/dist/web/assets/{image-gallery-BWFpZAAP.js → image-gallery-CrM5fHKy.js} +0 -0
- /package/dist/web/assets/{imba-B6F3yus5.js → imba-BxLOHO5I.js} +0 -0
- /package/dist/web/assets/{ini-BfkQswMZ.js → ini-ox-CFcg0.js} +0 -0
- /package/dist/web/assets/{java-DEH5avQ1.js → java-Bzo8yIal.js} +0 -0
- /package/dist/web/assets/{javascript-Dr4yHuV2.js → javascript-DHuz9VXn.js} +0 -0
- /package/dist/web/assets/{json-BuR35ewf.js → json-0_y80FD5.js} +0 -0
- /package/dist/web/assets/{json5-Ny9ZiJAC.js → json5-CEQ50pGM.js} +0 -0
- /package/dist/web/assets/{jsonc-zYVuoZkR.js → jsonc-CfrLEAKA.js} +0 -0
- /package/dist/web/assets/{jsonl-B9xthb_H.js → jsonl-fKbrANC2.js} +0 -0
- /package/dist/web/assets/{jsonnet-CCoLJbMP.js → jsonnet-DKbkPaQ2.js} +0 -0
- /package/dist/web/assets/{jssm-W0GDlQXi.js → jssm-Dxxx1Hqu.js} +0 -0
- /package/dist/web/assets/{jsx-BrLJpWC-.js → jsx-DlJL-Oj3.js} +0 -0
- /package/dist/web/assets/{kanagawa-dragon-CuVwt_MO.js → kanagawa-dragon-CwN4SV1c.js} +0 -0
- /package/dist/web/assets/{kanagawa-lotus-B3W_617P.js → kanagawa-lotus-BHltayiE.js} +0 -0
- /package/dist/web/assets/{kanagawa-wave-CEx4MP5W.js → kanagawa-wave-D-KXPjw6.js} +0 -0
- /package/dist/web/assets/{kdl-DilRejnB.js → kdl-DvtoJdFo.js} +0 -0
- /package/dist/web/assets/{kotlin-Bq5JtKaa.js → kotlin-CTS4MLcx.js} +0 -0
- /package/dist/web/assets/{kusto-Bhp2KYog.js → kusto-HtSE_sKm.js} +0 -0
- /package/dist/web/assets/{laserwave-WA_7c5hu.js → laserwave-DaXZG9ge.js} +0 -0
- /package/dist/web/assets/{lean-AEGcXwS-.js → lean-BYtGUfWx.js} +0 -0
- /package/dist/web/assets/{less-NPqH-26u.js → less-BoxmU_Mk.js} +0 -0
- /package/dist/web/assets/{lib-BFetOULu.js → lib-BzxL1ARZ.js} +0 -0
- /package/dist/web/assets/{libesm-tR4XlhMj.js → libesm-ah-mr-sh.js} +0 -0
- /package/dist/web/assets/{light-plus-B4xW7cgY.js → light-plus-Bt5CyUuo.js} +0 -0
- /package/dist/web/assets/{llvm-CPyWNcDE.js → llvm-CSdk9q9V.js} +0 -0
- /package/dist/web/assets/{log-CeQmiQSz.js → log-B-kp_3CL.js} +0 -0
- /package/dist/web/assets/{logo-BO0VrjBm.js → logo-DdIsXFDO.js} +0 -0
- /package/dist/web/assets/{luau-B6GT9kA1.js → luau-DUBpLlzQ.js} +0 -0
- /package/dist/web/assets/{make-CwfmN7e_.js → make-B3JVjoW_.js} +0 -0
- /package/dist/web/assets/{markdown-Bbe4FlLp.js → markdown-Seon0vHs.js} +0 -0
- /package/dist/web/assets/{material-theme-iD9XbfsB.js → material-theme-Nq-ChX1P.js} +0 -0
- /package/dist/web/assets/{material-theme-darker-u7M3XGe7.js → material-theme-darker-CPV4ayXJ.js} +0 -0
- /package/dist/web/assets/{material-theme-lighter-BGdI48zn.js → material-theme-lighter-0kyjYqEd.js} +0 -0
- /package/dist/web/assets/{material-theme-ocean-_mk62F5q.js → material-theme-ocean-DGpAp5co.js} +0 -0
- /package/dist/web/assets/{material-theme-palenight-CxFbDyAu.js → material-theme-palenight-B0AUn0NG.js} +0 -0
- /package/dist/web/assets/{matlab-Z0RUE3V_.js → matlab-C2waytxs.js} +0 -0
- /package/dist/web/assets/{mdx-BZ5wPYzV.js → mdx-DAis6R6I.js} +0 -0
- /package/dist/web/assets/{mermaid-CY1_aP-h.js → mermaid-_rnT2YGD.js} +0 -0
- /package/dist/web/assets/{min-dark-DKWtxOre.js → min-dark-BSSjchUB.js} +0 -0
- /package/dist/web/assets/{min-light-e1iwHDfP.js → min-light-DomAFQsa.js} +0 -0
- /package/dist/web/assets/{mipsasm-BiDfQ4ir.js → mipsasm-bBgtvPyG.js} +0 -0
- /package/dist/web/assets/{mojo-DteFEHsj.js → mojo-Cp9BayW0.js} +0 -0
- /package/dist/web/assets/{monokai-BgPbE9xZ.js → monokai-CL_9gbmn.js} +0 -0
- /package/dist/web/assets/{moonbit-mEbRp-mA.js → moonbit-BS_fcr-P.js} +0 -0
- /package/dist/web/assets/{move-CogqZw7A.js → move-C79AH45U.js} +0 -0
- /package/dist/web/assets/{narrat-BD2qjmy1.js → narrat-CJ5s3BDf.js} +0 -0
- /package/dist/web/assets/{nextflow-groovy-BNX8GOD_.js → nextflow-groovy-ZHa4JrPJ.js} +0 -0
- /package/dist/web/assets/{night-owl-ed3GbziD.js → night-owl-Jkf9zFQK.js} +0 -0
- /package/dist/web/assets/{night-owl-light-B0j7O0Wn.js → night-owl-light-DRPeaG5J.js} +0 -0
- /package/dist/web/assets/{nix-DFDbcQQm.js → nix-D2Jet9DN.js} +0 -0
- /package/dist/web/assets/{nord-D93TxEQi.js → nord-CREVs22j.js} +0 -0
- /package/dist/web/assets/{nushell-3_H_XKja.js → nushell-CAiDi_mR.js} +0 -0
- /package/dist/web/assets/{objective-c-BM3u6nlm.js → objective-c-CVkmJkC1.js} +0 -0
- /package/dist/web/assets/{objective-cpp-DZ73h42g.js → objective-cpp-DIKDO1Ly.js} +0 -0
- /package/dist/web/assets/{ocaml-B1PU5gmh.js → ocaml-R9qtGQhs.js} +0 -0
- /package/dist/web/assets/{odin-D14L6aRL.js → odin-DTYNQqVr.js} +0 -0
- /package/dist/web/assets/{one-dark-pro-Bj-94Z8v.js → one-dark-pro-BSLWUJn5.js} +0 -0
- /package/dist/web/assets/{one-light-DTdGWyOZ.js → one-light-DbkRXG3t.js} +0 -0
- /package/dist/web/assets/{openscad-Ciq554Sj.js → openscad-BlPcSnmT.js} +0 -0
- /package/dist/web/assets/{pascal-Bfd4qdGi.js → pascal-B1Y46ZZB.js} +0 -0
- /package/dist/web/assets/{pkl-CTurDUfj.js → pkl-BhJSJbFv.js} +0 -0
- /package/dist/web/assets/{plastic-BLQfZXQp.js → plastic-BzxsCTpi.js} +0 -0
- /package/dist/web/assets/{plsql-DyYfP9kQ.js → plsql-DBba7dyP.js} +0 -0
- /package/dist/web/assets/{po-CgXdT8vQ.js → po-BvgLlnNo.js} +0 -0
- /package/dist/web/assets/{poimandres-DOg5fK6s.js → poimandres-DUaI_1JP.js} +0 -0
- /package/dist/web/assets/{polar-Dv4aoL6X.js → polar-C7uILwJn.js} +0 -0
- /package/dist/web/assets/{postcss-DxugHnJD.js → postcss-BgJlpFdw.js} +0 -0
- /package/dist/web/assets/{powerquery-CR3kzRyX.js → powerquery-Dn1MIpZu.js} +0 -0
- /package/dist/web/assets/{powershell-BE91xJQB.js → powershell-Bo-DYzOS.js} +0 -0
- /package/dist/web/assets/{prisma-B_3yUlRQ.js → prisma-CTLs5IdT.js} +0 -0
- /package/dist/web/assets/{prolog-DcT781Ht.js → prolog-QRGun3eG.js} +0 -0
- /package/dist/web/assets/{proto-CSf-w5k2.js → proto-B7PI7apn.js} +0 -0
- /package/dist/web/assets/{puppet-CLGO6QWl.js → puppet-CBq0tLrP.js} +0 -0
- /package/dist/web/assets/{purescript-Bah2McKd.js → purescript-DFewOQb2.js} +0 -0
- /package/dist/web/assets/{python-DUH8qy7N.js → python-iWuxKXvT.js} +0 -0
- /package/dist/web/assets/{qmldir-YIsHnjKq.js → qmldir-cXiW3AsF.js} +0 -0
- /package/dist/web/assets/{qss-CLmoVNA4.js → qss-DASwAbBL.js} +0 -0
- /package/dist/web/assets/{r-Pon-b-vz.js → r-TU2eaIPw.js} +0 -0
- /package/dist/web/assets/{racket-BA09P5Kg.js → racket-SkRKqrLY.js} +0 -0
- /package/dist/web/assets/{raku-DlpzU7Fe.js → raku-D31lthkZ.js} +0 -0
- /package/dist/web/assets/{react-w6wINGnd.js → react-I_EwPIZi.js} +0 -0
- /package/dist/web/assets/{red-l7VuCR6d.js → red-CTcQZevb.js} +0 -0
- /package/dist/web/assets/{reg-g_k7gQEr.js → reg-D8YAsTGf.js} +0 -0
- /package/dist/web/assets/{regexp-CT17B7I7.js → regexp-CD2uvAWx.js} +0 -0
- /package/dist/web/assets/{rel-DkPCS7Bp.js → rel-CAaI-1fg.js} +0 -0
- /package/dist/web/assets/{resolve-theme-DdB7Yo5Z.js → resolve-theme-CJvkL8hU.js} +0 -0
- /package/dist/web/assets/{riscv-CYuFTw30.js → riscv-DR-hJVIj.js} +0 -0
- /package/dist/web/assets/{ron-cY7u_yEu.js → ron-CuUCJ0n4.js} +0 -0
- /package/dist/web/assets/{rose-pine-Cr_h_Ubo.js → rose-pine-aFHxW48i.js} +0 -0
- /package/dist/web/assets/{rose-pine-dawn-rRewaSZg.js → rose-pine-dawn-Bqz2sBRY.js} +0 -0
- /package/dist/web/assets/{rose-pine-moon-3VX5Xwca.js → rose-pine-moon-wqyh0an4.js} +0 -0
- /package/dist/web/assets/{rosmsg-Du4ie5PU.js → rosmsg-CI1ywimO.js} +0 -0
- /package/dist/web/assets/{rust-B7xWL-Eh.js → rust-p7Imk4G_.js} +0 -0
- /package/dist/web/assets/{sass-oXbOG8de.js → sass-C8JfaqCV.js} +0 -0
- /package/dist/web/assets/{save-C4z1p51e.js → save-DRPrDfTt.js} +0 -0
- /package/dist/web/assets/{scala-nlZJhKHf.js → scala-DNxCW1By.js} +0 -0
- /package/dist/web/assets/{scheme-FZ9_Zy8D.js → scheme-BYuUPJoL.js} +0 -0
- /package/dist/web/assets/{sdbl-D_lyvXiu.js → sdbl-DbPeJPUr.js} +0 -0
- /package/dist/web/assets/{shellscript-DRWRboAd.js → shellscript-DMiHlR3q.js} +0 -0
- /package/dist/web/assets/{shield-check-C5laObJv.js → shield-check-8GObuUAn.js} +0 -0
- /package/dist/web/assets/{shield-off-DULFtyLS.js → shield-off-egKAZEwR.js} +0 -0
- /package/dist/web/assets/{slack-dark-BGk2uVHu.js → slack-dark-C4xGcQXI.js} +0 -0
- /package/dist/web/assets/{slack-ochin-PAoSX041.js → slack-ochin-BzgRt_wm.js} +0 -0
- /package/dist/web/assets/{smalltalk-qP0zvICo.js → smalltalk-LDeL9PYH.js} +0 -0
- /package/dist/web/assets/{snazzy-light-B7uccpH6.js → snazzy-light-CXE7a5j_.js} +0 -0
- /package/dist/web/assets/{solarized-dark-CbICLZs3.js → solarized-dark-DgUhY74x.js} +0 -0
- /package/dist/web/assets/{solarized-light-CprWhyjj.js → solarized-light-DD9C-H-9.js} +0 -0
- /package/dist/web/assets/{solidity-B7pcm2K6.js → solidity-CAbDB4D4.js} +0 -0
- /package/dist/web/assets/{splunk-Co2LL4J_.js → splunk-kQREPfIc.js} +0 -0
- /package/dist/web/assets/{sql-cETIdGnT.js → sql-CBzeNAD8.js} +0 -0
- /package/dist/web/assets/{ssh-config-DETG6JCD.js → ssh-config-L1euxrsM.js} +0 -0
- /package/dist/web/assets/{stylus-tmTre2-O.js → stylus-DvBnYniP.js} +0 -0
- /package/dist/web/assets/{swift-BeKQbULV.js → swift-DF5NDQp6.js} +0 -0
- /package/dist/web/assets/{synthwave-84-eCL4-Xdn.js → synthwave-84-DbBhB_Hq.js} +0 -0
- /package/dist/web/assets/{system-verilog-CviwWvEx.js → system-verilog-D8s2ZW_k.js} +0 -0
- /package/dist/web/assets/{systemd-WuoFIbBS.js → systemd-a5-XZJsG.js} +0 -0
- /package/dist/web/assets/{table-CHmxuh6M.js → table-BX2ZrJfM.js} +0 -0
- /package/dist/web/assets/{talonscript-B9VBu7VI.js → talonscript-BLa0URTW.js} +0 -0
- /package/dist/web/assets/{tasl-CN7r11ky.js → tasl-DIoV4XcC.js} +0 -0
- /package/dist/web/assets/{tcl-e1cdhe3h.js → tcl-ztYAdXfQ.js} +0 -0
- /package/dist/web/assets/{terraform-BEP0mfa_.js → terraform-DvpCCiOZ.js} +0 -0
- /package/dist/web/assets/{tokyo-night-BfA_dA37.js → tokyo-night-BkHlYUv0.js} +0 -0
- /package/dist/web/assets/{toml-BrDIfNnt.js → toml-acIrxos6.js} +0 -0
- /package/dist/web/assets/{tsv-CyfGyE4i.js → tsv-BJB5EVCT.js} +0 -0
- /package/dist/web/assets/{tsx-BrFA8Iwf.js → tsx-B1MmXGT3.js} +0 -0
- /package/dist/web/assets/{turtle-9GBtR0mP.js → turtle-DFceTiqd.js} +0 -0
- /package/dist/web/assets/{typescript-BuTRZ65V.js → typescript-Dz9wU5Y1.js} +0 -0
- /package/dist/web/assets/{typespec-CRiNNRXF.js → typespec-CPuYZijj.js} +0 -0
- /package/dist/web/assets/{typst-CmpTnnkl.js → typst-CfsCIYv8.js} +0 -0
- /package/dist/web/assets/{utils-B9H0d3Fh.js → utils-DoY4yTP-.js} +0 -0
- /package/dist/web/assets/{v-DnIi2aab.js → v-CGo0jcXA.js} +0 -0
- /package/dist/web/assets/{vala-B7VxCNZP.js → vala-AsSR0hrb.js} +0 -0
- /package/dist/web/assets/{vb-DGoQ3pBT.js → vb-C7tvbXv1.js} +0 -0
- /package/dist/web/assets/{vendor-xterm-ClRVIJ-k.js → vendor-xterm-BeaIjRrv.js} +0 -0
- /package/dist/web/assets/{verilog-zGOC4qwV.js → verilog--a33ND-p.js} +0 -0
- /package/dist/web/assets/{vesper-DXR9-06q.js → vesper-B_Z0h4pn.js} +0 -0
- /package/dist/web/assets/{vhdl-CCO0oz0l.js → vhdl-CWL5dZPN.js} +0 -0
- /package/dist/web/assets/{viml-Dxwr-QNN.js → viml-BglmT14Y.js} +0 -0
- /package/dist/web/assets/{vitesse-black-Bhah7RxE.js → vitesse-black-RKFsSqQI.js} +0 -0
- /package/dist/web/assets/{vitesse-dark-CGK6XVwc.js → vitesse-dark-D7zDWNmc.js} +0 -0
- /package/dist/web/assets/{vitesse-light-ne5kfw8h.js → vitesse-light-MAp87W5b.js} +0 -0
- /package/dist/web/assets/{vyper-ErDFqVoP.js → vyper-CgCBYg1G.js} +0 -0
- /package/dist/web/assets/{wasm-DtWvUWGr.js → wasm-BOG7ZeTT.js} +0 -0
- /package/dist/web/assets/{wasm-Bd258nS1.js → wasm-Bwl4txwp.js} +0 -0
- /package/dist/web/assets/{wenyan-EZe1IEYT.js → wenyan-DpShPKXZ.js} +0 -0
- /package/dist/web/assets/{wgsl-B1o5_I_9.js → wgsl-BakcCblK.js} +0 -0
- /package/dist/web/assets/{wikitext-C_rwp8An.js → wikitext-NroRWVAO.js} +0 -0
- /package/dist/web/assets/{wit-gbYtF3rY.js → wit-B5CM2pg6.js} +0 -0
- /package/dist/web/assets/{wolfram-DrcQFe2n.js → wolfram-7MTFBJpR.js} +0 -0
- /package/dist/web/assets/{x-B1qHmLdR.js → x-BcqfTY_7.js} +0 -0
- /package/dist/web/assets/{yaml-CzDAObLX.js → yaml-Bwr01G2K.js} +0 -0
- /package/dist/web/assets/{zenscript-B-J1phtg.js → zenscript-Dj8GATvi.js} +0 -0
- /package/dist/web/assets/{zig-BlzNia0O.js → zig-LAE-fbS-.js} +0 -0
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Serve a local file with HTTP Range support (RFC 9110 §14).
|
|
3
|
+
*
|
|
4
|
+
* `<video>`/`<audio>` and pdf.js fetch media in byte ranges: the browser asks for
|
|
5
|
+
* `Range: bytes=N-` when the user seeks and expects `206 Partial Content` back.
|
|
6
|
+
* Without `Accept-Ranges` + `Content-Length` on the 200 response the browser cannot
|
|
7
|
+
* even show the duration, so a `file.stream()` body (chunked, no length) breaks seeking.
|
|
8
|
+
*
|
|
9
|
+
* Bun ≥ 1.3.13 handles Range itself when the body is a `BunFile`, but PPM runs on
|
|
10
|
+
* whatever Bun the user has installed, so the parsing is done here and works on
|
|
11
|
+
* every version. Multi-range requests (`bytes=0-1,5-9`) fall back to the whole file,
|
|
12
|
+
* which the spec allows.
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
export interface ByteRange {
|
|
16
|
+
start: number;
|
|
17
|
+
/** Inclusive last byte. */
|
|
18
|
+
end: number;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Parse a single-range `Range` header against a resource of `size` bytes.
|
|
23
|
+
* Returns `null` when there is no usable range (absent header, malformed, multi-range)
|
|
24
|
+
* — caller should answer 200 with the whole file. Returns `"unsatisfiable"` when the
|
|
25
|
+
* range lies entirely outside the file — caller should answer 416.
|
|
26
|
+
*/
|
|
27
|
+
export function parseRangeHeader(header: string | null | undefined, size: number): ByteRange | "unsatisfiable" | null {
|
|
28
|
+
if (!header) return null;
|
|
29
|
+
const m = /^bytes=(\d*)-(\d*)$/.exec(header.trim());
|
|
30
|
+
if (!m) return null;
|
|
31
|
+
const [, rawStart, rawEnd] = m;
|
|
32
|
+
if (rawStart === "" && rawEnd === "") return null;
|
|
33
|
+
if (size <= 0) return "unsatisfiable";
|
|
34
|
+
|
|
35
|
+
// Suffix range `bytes=-500` → last 500 bytes.
|
|
36
|
+
if (rawStart === "") {
|
|
37
|
+
const suffix = Number(rawEnd);
|
|
38
|
+
if (suffix <= 0) return "unsatisfiable";
|
|
39
|
+
return { start: Math.max(0, size - suffix), end: size - 1 };
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const start = Number(rawStart);
|
|
43
|
+
if (start >= size) return "unsatisfiable";
|
|
44
|
+
// Open-ended `bytes=1024-` or an end past EOF are both clamped to the last byte.
|
|
45
|
+
const end = rawEnd === "" ? size - 1 : Math.min(Number(rawEnd), size - 1);
|
|
46
|
+
if (end < start) return "unsatisfiable";
|
|
47
|
+
return { start, end };
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Build the `Response` for `absPath`, honouring the request's `Range` header.
|
|
52
|
+
* `extraHeaders` are merged onto every response (Content-Disposition, Cache-Control…).
|
|
53
|
+
* `contentType` overrides the sniffed MIME type (used for forced downloads).
|
|
54
|
+
*/
|
|
55
|
+
export function rangeFileResponse(
|
|
56
|
+
absPath: string,
|
|
57
|
+
req: Request,
|
|
58
|
+
extraHeaders: Record<string, string> = {},
|
|
59
|
+
contentType?: string,
|
|
60
|
+
): Response {
|
|
61
|
+
const file = Bun.file(absPath);
|
|
62
|
+
const size = file.size;
|
|
63
|
+
const base: Record<string, string> = {
|
|
64
|
+
"Accept-Ranges": "bytes",
|
|
65
|
+
"Content-Type": contentType ?? (file.type || "application/octet-stream"),
|
|
66
|
+
...extraHeaders,
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
const range = parseRangeHeader(req.headers.get("range"), size);
|
|
70
|
+
if (range === null) {
|
|
71
|
+
// Whole file. Bun derives Content-Length from the BunFile body.
|
|
72
|
+
return new Response(file, { status: 200, headers: { ...base, "Content-Length": String(size) } });
|
|
73
|
+
}
|
|
74
|
+
if (range === "unsatisfiable") {
|
|
75
|
+
return new Response(null, { status: 416, headers: { ...base, "Content-Range": `bytes */${size}` } });
|
|
76
|
+
}
|
|
77
|
+
const { start, end } = range;
|
|
78
|
+
return new Response(file.slice(start, end + 1), {
|
|
79
|
+
status: 206,
|
|
80
|
+
headers: {
|
|
81
|
+
...base,
|
|
82
|
+
"Content-Range": `bytes ${start}-${end}/${size}`,
|
|
83
|
+
"Content-Length": String(end - start + 1),
|
|
84
|
+
},
|
|
85
|
+
});
|
|
86
|
+
}
|
package/src/server/index.ts
CHANGED
|
@@ -17,6 +17,7 @@ import { postgresRoutes } from "./routes/postgres.ts";
|
|
|
17
17
|
import { databaseRoutes } from "./routes/database.ts";
|
|
18
18
|
import { fsBrowseRoutes } from "./routes/fs-browse.ts";
|
|
19
19
|
import { fsOpsRoutes } from "./routes/fs-ops.ts";
|
|
20
|
+
import { fsUploadRoutes } from "./routes/fs-upload.ts";
|
|
20
21
|
import { fsSqliteRoutes } from "./routes/fs-sqlite.ts";
|
|
21
22
|
import { accountsRoutes } from "./routes/accounts.ts";
|
|
22
23
|
import { proxyRoutes } from "./routes/proxy.ts";
|
|
@@ -94,6 +95,16 @@ async function setupLogFile() {
|
|
|
94
95
|
});
|
|
95
96
|
}
|
|
96
97
|
|
|
98
|
+
/**
|
|
99
|
+
* WebSocket upgrades carry the session token as `?token=` (no headers on a
|
|
100
|
+
* browser handshake). When auth is disabled every upgrade is allowed.
|
|
101
|
+
*/
|
|
102
|
+
export function isWsUpgradeAuthorized(url: URL): boolean {
|
|
103
|
+
const authConfig = configService.get("auth");
|
|
104
|
+
if (!authConfig.enabled) return true;
|
|
105
|
+
return url.searchParams.get("token") === authConfig.token;
|
|
106
|
+
}
|
|
107
|
+
|
|
97
108
|
// Register database adapters at module load time
|
|
98
109
|
initAdapters();
|
|
99
110
|
|
|
@@ -155,6 +166,7 @@ app.route("/api/tunnels", tunnelRegistryRoutes);
|
|
|
155
166
|
// Filesystem operations (browse, list, read, write) — consolidated in fs-browse route
|
|
156
167
|
app.route("/api/fs", fsBrowseRoutes);
|
|
157
168
|
app.route("/api/fs", fsOpsRoutes);
|
|
169
|
+
app.route("/api/fs", fsUploadRoutes);
|
|
158
170
|
app.route("/api/fs/sqlite", fsSqliteRoutes);
|
|
159
171
|
|
|
160
172
|
// System resource monitoring (SSE + JSON)
|
|
@@ -818,30 +830,23 @@ if (process.argv.includes("__serve__")) {
|
|
|
818
830
|
return new Response("WebSocket upgrade failed", { status: 400 });
|
|
819
831
|
}
|
|
820
832
|
|
|
833
|
+
// Every socket except the health probe is authenticated at upgrade time.
|
|
834
|
+
// Browsers cannot send headers on a WebSocket handshake, so the session
|
|
835
|
+
// token travels as `?token=`; the terminal socket in particular hands out
|
|
836
|
+
// a shell, so an unauthenticated upgrade must never reach the handlers.
|
|
837
|
+
if (url.pathname.startsWith("/ws/") && !isWsUpgradeAuthorized(url)) {
|
|
838
|
+
return new Response("Unauthorized", { status: 401 });
|
|
839
|
+
}
|
|
840
|
+
|
|
821
841
|
if (url.pathname === "/ws/global") {
|
|
822
842
|
// App-wide event bus: owns file watching + cross-cutting broadcasts, so
|
|
823
|
-
// they no longer depend on a chat tab being mounted.
|
|
824
|
-
const authConfig = configService.get("auth");
|
|
825
|
-
if (authConfig.enabled) {
|
|
826
|
-
const token = url.searchParams.get("token");
|
|
827
|
-
if (token !== authConfig.token) {
|
|
828
|
-
return new Response("Unauthorized", { status: 401 });
|
|
829
|
-
}
|
|
830
|
-
}
|
|
843
|
+
// they no longer depend on a chat tab being mounted.
|
|
831
844
|
const upgraded = server.upgrade(req, { data: { type: "global" } });
|
|
832
845
|
if (upgraded) return undefined;
|
|
833
846
|
return new Response("WebSocket upgrade failed", { status: 400 });
|
|
834
847
|
}
|
|
835
848
|
|
|
836
849
|
if (url.pathname === "/ws/extensions") {
|
|
837
|
-
// Auth check for extension WS
|
|
838
|
-
const authConfig = configService.get("auth");
|
|
839
|
-
if (authConfig.enabled) {
|
|
840
|
-
const token = url.searchParams.get("token");
|
|
841
|
-
if (token !== authConfig.token) {
|
|
842
|
-
return new Response("Unauthorized", { status: 401 });
|
|
843
|
-
}
|
|
844
|
-
}
|
|
845
850
|
const upgraded = server.upgrade(req, { data: { type: "extensions" } });
|
|
846
851
|
if (upgraded) return undefined;
|
|
847
852
|
return new Response("WebSocket upgrade failed", { status: 400 });
|
|
@@ -854,8 +859,10 @@ if (process.argv.includes("__serve__")) {
|
|
|
854
859
|
const id = parts[5] ?? "";
|
|
855
860
|
|
|
856
861
|
if (wsType === "terminal") {
|
|
862
|
+
// Optional start directory (explorer "Open in Terminal"); validated in the handler.
|
|
863
|
+
const cwd = url.searchParams.get("cwd") ?? undefined;
|
|
857
864
|
const upgraded = server.upgrade(req, {
|
|
858
|
-
data: { type: "terminal", id, projectName },
|
|
865
|
+
data: { type: "terminal", id, projectName, cwd },
|
|
859
866
|
});
|
|
860
867
|
if (upgraded) return undefined;
|
|
861
868
|
return new Response("WebSocket upgrade failed", { status: 400 });
|
|
@@ -26,10 +26,11 @@ export async function authMiddleware(c: Context, next: Next) {
|
|
|
26
26
|
}
|
|
27
27
|
|
|
28
28
|
// Fallback: ?token= query param for SSE/EventSource & iframe embeds (can't set custom headers)
|
|
29
|
-
// Scoped to /stream
|
|
29
|
+
// Scoped to /stream, /files/raw, /files/transcode (<video src> cannot set headers) to avoid leaking token on all GET routes
|
|
30
30
|
if (c.req.method === "GET") {
|
|
31
31
|
const p = c.req.path;
|
|
32
|
-
|
|
32
|
+
const isMediaPath = p.endsWith("/files/raw") || p.endsWith("/files/transcode") || p === "/api/fs/raw" || p === "/api/fs/transcode";
|
|
33
|
+
if (p.endsWith("/stream") || isMediaPath || p.endsWith("/image")) {
|
|
33
34
|
const queryToken = c.req.query("token");
|
|
34
35
|
if (queryToken && queryToken === authConfig.token) {
|
|
35
36
|
return next();
|
|
@@ -5,6 +5,8 @@ import { fileService, SecurityError, NotFoundError, ValidationError } from "../.
|
|
|
5
5
|
import { readSystemFileSync } from "../../services/fs-browse.service.ts";
|
|
6
6
|
import { ok, err } from "../../types/api.ts";
|
|
7
7
|
import { errorStatus } from "../helpers/error-status.ts";
|
|
8
|
+
import { rangeFileResponse } from "../helpers/range-file-response.ts";
|
|
9
|
+
import { handleMediaProbe, handleMediaTranscode } from "../helpers/media-route-handlers.ts";
|
|
8
10
|
import mammoth from "mammoth";
|
|
9
11
|
|
|
10
12
|
type Env = { Variables: { projectPath: string; projectName: string } };
|
|
@@ -103,16 +105,47 @@ fileRoutes.get("/raw", (c) => {
|
|
|
103
105
|
}
|
|
104
106
|
if (!existsSync(absPath)) return c.json(err("File not found"), 404);
|
|
105
107
|
|
|
106
|
-
const file = Bun.file(absPath);
|
|
107
108
|
const download = c.req.query("download") === "true";
|
|
108
109
|
const filename = filePath.split("/").pop() ?? "download";
|
|
109
110
|
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
},
|
|
115
|
-
|
|
111
|
+
// Range-aware so <video>/<audio>/pdf.js can seek instead of buffering the whole file.
|
|
112
|
+
return rangeFileResponse(
|
|
113
|
+
absPath,
|
|
114
|
+
c.req.raw,
|
|
115
|
+
{ "Content-Disposition": download ? `attachment; filename="${filename}"` : "inline" },
|
|
116
|
+
download ? "application/octet-stream" : undefined,
|
|
117
|
+
);
|
|
118
|
+
} catch (e) {
|
|
119
|
+
return c.json(err((e as Error).message), errorStatus(e));
|
|
120
|
+
}
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
/** Resolve `?path=` inside the project or return the error response to send. */
|
|
124
|
+
function resolveProjectFile(c: { get(k: "projectPath"): string; req: { query(k: string): string | undefined } }): string | Response {
|
|
125
|
+
const projectPath = c.get("projectPath");
|
|
126
|
+
const filePath = c.req.query("path");
|
|
127
|
+
if (!filePath) return Response.json(err("Missing query parameter: path"), { status: 400 });
|
|
128
|
+
const absPath = resolve(projectPath, filePath);
|
|
129
|
+
if (!absPath.startsWith(projectPath)) return Response.json(err("Access denied"), { status: 403 });
|
|
130
|
+
if (!existsSync(absPath)) return Response.json(err("File not found"), { status: 404 });
|
|
131
|
+
return absPath;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/** GET /files/probe?path=... — codec/duration facts + whether ffmpeg transcoding is available */
|
|
135
|
+
fileRoutes.get("/probe", async (c) => {
|
|
136
|
+
try {
|
|
137
|
+
const abs = resolveProjectFile(c);
|
|
138
|
+
return abs instanceof Response ? abs : await handleMediaProbe(abs);
|
|
139
|
+
} catch (e) {
|
|
140
|
+
return c.json(err((e as Error).message), errorStatus(e));
|
|
141
|
+
}
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
/** GET /files/transcode?path=...&start=<sec> — ffmpeg → fragmented MP4 for videos the browser cannot decode */
|
|
145
|
+
fileRoutes.get("/transcode", async (c) => {
|
|
146
|
+
try {
|
|
147
|
+
const abs = resolveProjectFile(c);
|
|
148
|
+
return abs instanceof Response ? abs : await handleMediaTranscode(abs, c.req.raw, c.req.query("start"));
|
|
116
149
|
} catch (e) {
|
|
117
150
|
return c.json(err((e as Error).message), errorStatus(e));
|
|
118
151
|
}
|
|
@@ -21,6 +21,8 @@ import { attachmentDisposition } from "../../services/fs-ops/fs-content-disposit
|
|
|
21
21
|
import { fsErrorBody } from "../../services/fs-ops/fs-error-response.ts";
|
|
22
22
|
import { isImageExtension } from "../../shared/image-extensions.ts";
|
|
23
23
|
import { ok, err } from "../../types/api.ts";
|
|
24
|
+
import { rangeFileResponse } from "../helpers/range-file-response.ts";
|
|
25
|
+
import { handleMediaProbe, handleMediaTranscode } from "../helpers/media-route-handlers.ts";
|
|
24
26
|
import {
|
|
25
27
|
createDownloadToken,
|
|
26
28
|
consumeDownloadToken,
|
|
@@ -121,16 +123,52 @@ fsBrowseRoutes.get("/raw", async (c) => {
|
|
|
121
123
|
|
|
122
124
|
const download = c.req.query("download") === "true";
|
|
123
125
|
const filename = basename(resolved) || "download";
|
|
124
|
-
|
|
125
|
-
return
|
|
126
|
-
|
|
127
|
-
|
|
126
|
+
// Range-aware so <video>/<audio>/pdf.js can seek instead of buffering the whole file.
|
|
127
|
+
return rangeFileResponse(
|
|
128
|
+
resolved,
|
|
129
|
+
c.req.raw,
|
|
130
|
+
{
|
|
128
131
|
"Content-Disposition": download ? attachmentDisposition(filename) : "inline",
|
|
129
132
|
// A download is a one-shot transfer of a file the user may edit right
|
|
130
133
|
// after; caching it would hand out stale bytes on the next download.
|
|
131
134
|
"Cache-Control": download ? "no-store" : "private, max-age=3600",
|
|
132
135
|
},
|
|
133
|
-
|
|
136
|
+
download ? "application/octet-stream" : undefined,
|
|
137
|
+
);
|
|
138
|
+
} catch (e) {
|
|
139
|
+
const { body, status } = fail(e);
|
|
140
|
+
return c.json(body, status);
|
|
141
|
+
}
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
/** Resolve `?path=` for media routes: allowlisted, not inside ~/.ppm, and existing. */
|
|
145
|
+
async function resolveMediaPath(raw: string | undefined): Promise<string | Response> {
|
|
146
|
+
if (!raw) return Response.json(err("path is required"), { status: 400 });
|
|
147
|
+
const resolved = resolvePath(raw);
|
|
148
|
+
assertAllowed(resolved);
|
|
149
|
+
assertNotPpmDir(resolved);
|
|
150
|
+
assertNotPpmDir(await realPathOrSelf(resolved));
|
|
151
|
+
const info = await stat(resolved).catch(() => null);
|
|
152
|
+
if (!info?.isFile()) return Response.json(err("File not found"), { status: 404 });
|
|
153
|
+
return resolved;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
/** GET /api/fs/probe?path=/some/video — codec/duration facts + whether ffmpeg transcoding is available */
|
|
157
|
+
fsBrowseRoutes.get("/probe", async (c) => {
|
|
158
|
+
try {
|
|
159
|
+
const abs = await resolveMediaPath(c.req.query("path"));
|
|
160
|
+
return abs instanceof Response ? abs : await handleMediaProbe(abs);
|
|
161
|
+
} catch (e) {
|
|
162
|
+
const { body, status } = fail(e);
|
|
163
|
+
return c.json(body, status);
|
|
164
|
+
}
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
/** GET /api/fs/transcode?path=/some/video&start=<sec> — ffmpeg → fragmented MP4 */
|
|
168
|
+
fsBrowseRoutes.get("/transcode", async (c) => {
|
|
169
|
+
try {
|
|
170
|
+
const abs = await resolveMediaPath(c.req.query("path"));
|
|
171
|
+
return abs instanceof Response ? abs : await handleMediaTranscode(abs, c.req.raw, c.req.query("start"));
|
|
134
172
|
} catch (e) {
|
|
135
173
|
const { body, status } = fail(e);
|
|
136
174
|
return c.json(body, status);
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { Hono, type Context } from "hono";
|
|
2
|
+
import { err } from "../../types/api.ts";
|
|
3
|
+
import { fsErrorBody } from "../../services/fs-ops/fs-error-response.ts";
|
|
4
|
+
import { uploadFile } from "../../services/fs-ops/fs-ops-upload.service.ts";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Streaming upload door, mounted on the same `/api/fs` prefix as the browse/mutate routes.
|
|
8
|
+
* Split out of `fs-ops.ts` because a request body stream is a very different shape of
|
|
9
|
+
* handler than the JSON in/JSON out routes there.
|
|
10
|
+
*/
|
|
11
|
+
export const fsUploadRoutes = new Hono();
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* PUT /api/fs/upload?path=<absolute file path>&overwrite=0|1
|
|
15
|
+
*
|
|
16
|
+
* Body: the raw file bytes. Streamed straight to disk (see the service) — never buffered —
|
|
17
|
+
* so there is no per-file size limit here; the host disk is the only bound. Answers 409
|
|
18
|
+
* `EEXIST` when the target exists and `overwrite` was not set, matching every other
|
|
19
|
+
* `/api/fs` mutation's collision contract.
|
|
20
|
+
*/
|
|
21
|
+
fsUploadRoutes.put("/upload", async (c: Context) => {
|
|
22
|
+
try {
|
|
23
|
+
const path = c.req.query("path");
|
|
24
|
+
if (!path) return c.json(err("path is required"), 400);
|
|
25
|
+
const overwrite = c.req.query("overwrite") === "1";
|
|
26
|
+
const result = await uploadFile(path, c.req.raw.body, overwrite);
|
|
27
|
+
return c.json({ ok: true, data: result }, 201);
|
|
28
|
+
} catch (e) {
|
|
29
|
+
const { body, status } = fsErrorBody(e);
|
|
30
|
+
return c.json(body, status);
|
|
31
|
+
}
|
|
32
|
+
});
|
|
@@ -25,6 +25,40 @@ teamRoutes.get("/:name", async (c) => {
|
|
|
25
25
|
return c.json(ok(detail));
|
|
26
26
|
});
|
|
27
27
|
|
|
28
|
+
/**
|
|
29
|
+
* Live per-member activity. Separate from `/:name` because it is polled while the
|
|
30
|
+
* panel is open, and because its source is the agent transcripts rather than the
|
|
31
|
+
* team inboxes — the inboxes cannot say who is working right now.
|
|
32
|
+
*/
|
|
33
|
+
teamRoutes.get("/:name/activity", async (c) => {
|
|
34
|
+
const name = c.req.param("name");
|
|
35
|
+
if (!VALID_TEAM_NAME.test(name)) return c.json(err("Invalid team name"), 400);
|
|
36
|
+
const { readTeamMemberActivity, readTeamOutboundMessages } = await import(
|
|
37
|
+
"../../services/team-member-activity/member-activity.service.ts"
|
|
38
|
+
);
|
|
39
|
+
const projectPath = c.req.query("projectPath") ?? null;
|
|
40
|
+
const [members, outbound] = await Promise.all([
|
|
41
|
+
readTeamMemberActivity(name, projectPath),
|
|
42
|
+
readTeamOutboundMessages(name, projectPath),
|
|
43
|
+
]);
|
|
44
|
+
return c.json(ok({ members, outbound }));
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
/** Full transcript of one teammate's work session, for the member window. */
|
|
48
|
+
teamRoutes.get("/:name/members/:member/transcript", async (c) => {
|
|
49
|
+
const name = c.req.param("name");
|
|
50
|
+
const member = c.req.param("member");
|
|
51
|
+
if (!VALID_TEAM_NAME.test(name)) return c.json(err("Invalid team name"), 400);
|
|
52
|
+
if (!VALID_TEAM_NAME.test(member)) return c.json(err("Invalid member name"), 400);
|
|
53
|
+
const { resolveMemberTranscript } = await import(
|
|
54
|
+
"../../services/team-member-activity/member-activity.service.ts"
|
|
55
|
+
);
|
|
56
|
+
const path = resolveMemberTranscript(name, member, c.req.query("projectPath") ?? null);
|
|
57
|
+
if (!path) return c.json(err("Member transcript not found"), 404);
|
|
58
|
+
const { parseAgentTranscript } = await import("../../services/subagent-transcript-merger.ts");
|
|
59
|
+
return c.json(ok({ member, events: parseAgentTranscript(path) }));
|
|
60
|
+
});
|
|
61
|
+
|
|
28
62
|
teamRoutes.delete("/:name", async (c) => {
|
|
29
63
|
const name = c.req.param("name");
|
|
30
64
|
if (!VALID_TEAM_NAME.test(name)) {
|
package/src/server/ws/chat.ts
CHANGED
|
@@ -14,6 +14,7 @@ import { backgroundShellRegistry } from "../../services/background-shell-registr
|
|
|
14
14
|
import { basename } from "node:path";
|
|
15
15
|
import { configService } from "../../services/config.service.ts";
|
|
16
16
|
import { formatTurnUsageLog } from "../../shared/turn-usage.ts";
|
|
17
|
+
import { isTerminalAgentStatus } from "../../shared/background-agent-status.ts";
|
|
17
18
|
import { cacheReleaseDelayMs, selectWarmIdleEvictions } from "../../services/subprocess-retention.ts";
|
|
18
19
|
|
|
19
20
|
/** Resolve the SESSION's provider config — not the global default provider's.
|
|
@@ -86,6 +87,8 @@ interface SessionEntry {
|
|
|
86
87
|
teamNames: Set<string>;
|
|
87
88
|
/** toolUseId of a pending TeamCreate call */
|
|
88
89
|
pendingTeamCreate?: string;
|
|
90
|
+
/** Throttle marker for the filesystem probe that finds implicitly-created teams */
|
|
91
|
+
lastImplicitTeamProbe?: number;
|
|
89
92
|
/** Compact indicator state — sticky until turn ends or boundary received, synced on reconnect */
|
|
90
93
|
compactStatus?: "compacting" | null;
|
|
91
94
|
/** toolUseIds of Bash calls launched with run_in_background — spy is kept alive for these */
|
|
@@ -268,6 +271,47 @@ function bufferAndBroadcast(sessionId: string, event: unknown): void {
|
|
|
268
271
|
broadcast(sessionId, event);
|
|
269
272
|
}
|
|
270
273
|
|
|
274
|
+
/** How often a session may stat ~/.claude/teams looking for an implicit team. */
|
|
275
|
+
const IMPLICIT_TEAM_PROBE_INTERVAL_MS = 3_000;
|
|
276
|
+
|
|
277
|
+
/** Watch a team's inboxes and announce it to the session's clients. Idempotent. */
|
|
278
|
+
async function attachTeamWatcher(sessionId: string, teamName: string): Promise<void> {
|
|
279
|
+
const entry = activeSessions.get(sessionId);
|
|
280
|
+
if (!entry || entry.teamNames.has(teamName)) return;
|
|
281
|
+
entry.teamNames.add(teamName);
|
|
282
|
+
const { startTeamInboxWatcher } = await import("./team-inbox-watcher.ts");
|
|
283
|
+
const watcher = await startTeamInboxWatcher(teamName, {
|
|
284
|
+
onInboxUpdate: (tn, agent, msgs) => broadcast(sessionId, {
|
|
285
|
+
type: "team_inbox", teamName: tn, agent, messages: msgs,
|
|
286
|
+
}),
|
|
287
|
+
onConfigUpdate: (tn, config) => broadcast(sessionId, {
|
|
288
|
+
type: "team_updated", teamName: tn, team: config,
|
|
289
|
+
}),
|
|
290
|
+
});
|
|
291
|
+
// The session may have been torn down while the watcher was starting.
|
|
292
|
+
const live = activeSessions.get(sessionId);
|
|
293
|
+
if (!live) { watcher.cleanup(); return; }
|
|
294
|
+
live.teamWatchers.set(teamName, watcher);
|
|
295
|
+
bufferAndBroadcast(sessionId, { type: "team_detected", teamName });
|
|
296
|
+
console.log(`[chat] session=${sessionId} team detected: ${teamName}`);
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
/** Attach to the team Claude Code creates implicitly for this session.
|
|
300
|
+
* Current releases no longer expose a TeamCreate tool — a team materialises as
|
|
301
|
+
* ~/.claude/teams/<sessionId>/inboxes/ with no tool call to hook and no
|
|
302
|
+
* config.json, so the directory itself is the only reliable signal. */
|
|
303
|
+
async function detectImplicitTeam(sessionId: string): Promise<void> {
|
|
304
|
+
const entry = activeSessions.get(sessionId);
|
|
305
|
+
if (!entry || entry.teamNames.has(sessionId)) return;
|
|
306
|
+
const now = Date.now();
|
|
307
|
+
if (entry.lastImplicitTeamProbe && now - entry.lastImplicitTeamProbe < IMPLICIT_TEAM_PROBE_INTERVAL_MS) return;
|
|
308
|
+
entry.lastImplicitTeamProbe = now;
|
|
309
|
+
try {
|
|
310
|
+
const { teamExists } = await import("./team-inbox-watcher.ts");
|
|
311
|
+
if (await teamExists(sessionId)) await attachTeamWatcher(sessionId, sessionId);
|
|
312
|
+
} catch { /* teams dir unreadable — nothing to attach */ }
|
|
313
|
+
}
|
|
314
|
+
|
|
271
315
|
/** Transition session phase — guards same-phase, broadcasts phase_changed */
|
|
272
316
|
function setPhase(sessionId: string, phase: SessionPhase, elapsed?: number): void {
|
|
273
317
|
const entry = activeSessions.get(sessionId);
|
|
@@ -435,6 +479,12 @@ async function startSessionConsumer(sessionId: string, providerId: string, conte
|
|
|
435
479
|
const taskStatus = (ev as any).taskStatus as string | undefined;
|
|
436
480
|
const taskToolUseId = (ev as any).taskToolUseId as string | undefined;
|
|
437
481
|
const outputFile = (ev as any).outputFile as string | undefined;
|
|
482
|
+
// A backgrounded Agent reports its outcome only here — its tool_result was a
|
|
483
|
+
// launch ack the card must not read as "finished". Forward the terminal state so
|
|
484
|
+
// the card can settle. Harmless for background bash tasks: no card matches them.
|
|
485
|
+
if (sub === "task_notification" && taskToolUseId && isTerminalAgentStatus(taskStatus)) {
|
|
486
|
+
broadcast(sessionId, { type: "subagent_status", toolUseId: taskToolUseId, status: taskStatus });
|
|
487
|
+
}
|
|
438
488
|
if (taskId) {
|
|
439
489
|
// Ensure the shell is registered even if the spy missed the file (fallback).
|
|
440
490
|
if (!backgroundShellRegistry.get(sessionId, taskId) && outputFile) {
|
|
@@ -555,27 +605,17 @@ async function startSessionConsumer(sessionId: string, providerId: string, conte
|
|
|
555
605
|
if (ev.toolUseId && !entry.backgroundToolUseIds?.has(ev.toolUseId)) {
|
|
556
606
|
bashOutputSpy.stopSpy(ev.toolUseId);
|
|
557
607
|
}
|
|
558
|
-
// Detect team creation from TeamCreate tool_result
|
|
608
|
+
// Detect team creation from TeamCreate tool_result (legacy explicit teams)
|
|
559
609
|
if (entry.pendingTeamCreate && entry.pendingTeamCreate === ev.toolUseId) {
|
|
560
|
-
const { extractTeamName
|
|
610
|
+
const { extractTeamName } = await import("./team-inbox-watcher.ts");
|
|
561
611
|
const teamName = extractTeamName(ev.output ?? "");
|
|
562
612
|
console.log(`[chat] session=${sessionId} TeamCreate result matched, extracted teamName=${teamName}`);
|
|
563
|
-
if (teamName
|
|
564
|
-
entry.teamNames.add(teamName);
|
|
565
|
-
const watcher = startTeamInboxWatcher(teamName, {
|
|
566
|
-
onInboxUpdate: (tn, agent, msgs) => broadcast(sessionId, {
|
|
567
|
-
type: "team_inbox", teamName: tn, agent, messages: msgs,
|
|
568
|
-
}),
|
|
569
|
-
onConfigUpdate: (tn, config) => broadcast(sessionId, {
|
|
570
|
-
type: "team_updated", teamName: tn, team: config,
|
|
571
|
-
}),
|
|
572
|
-
});
|
|
573
|
-
entry.teamWatchers.set(teamName, watcher);
|
|
574
|
-
bufferAndBroadcast(sessionId, { type: "team_detected", teamName });
|
|
575
|
-
console.log(`[chat] session=${sessionId} team detected: ${teamName}`);
|
|
576
|
-
}
|
|
613
|
+
if (teamName) await attachTeamWatcher(sessionId, teamName);
|
|
577
614
|
entry.pendingTeamCreate = undefined;
|
|
578
615
|
}
|
|
616
|
+
// Implicit teams have no tool result to key off — the session's own team
|
|
617
|
+
// directory can appear after any Agent/SendMessage call, so poll (throttled).
|
|
618
|
+
void detectImplicitTeam(sessionId);
|
|
579
619
|
} else if (evType === "error") {
|
|
580
620
|
const errorDetail = ev.message ?? JSON.stringify(ev).slice(0, 500);
|
|
581
621
|
console.error(`[chat] session=${sessionId} error: ${errorDetail}`);
|
|
@@ -787,6 +827,10 @@ export const chatWebSocket = {
|
|
|
787
827
|
existing.clients.add(ws);
|
|
788
828
|
setupClientPing(existing, ws);
|
|
789
829
|
|
|
830
|
+
// A team created in an earlier turn (or before a server restart) leaves no
|
|
831
|
+
// live event to replay — re-attach from disk so the UI comes back.
|
|
832
|
+
void detectImplicitTeam(sessionId);
|
|
833
|
+
|
|
790
834
|
// Async: resolve title from SDK if in-memory title is generic (DB title takes priority)
|
|
791
835
|
if (!session?.title || session.title === "Chat" || session.title === "Resumed Chat") {
|
|
792
836
|
sdkListSessions({ dir: projectPath, limit: 50 }).then((sessions) => {
|
|
@@ -821,6 +865,10 @@ export const chatWebSocket = {
|
|
|
821
865
|
activeSessions.set(sessionId, newEntry);
|
|
822
866
|
setupClientPing(newEntry, ws);
|
|
823
867
|
|
|
868
|
+
// Resuming a session whose team already exists on disk (server restart, or a
|
|
869
|
+
// team created many turns ago) — re-attach the watcher.
|
|
870
|
+
void detectImplicitTeam(sessionId);
|
|
871
|
+
|
|
824
872
|
ws.send(JSON.stringify({
|
|
825
873
|
type: "session_state",
|
|
826
874
|
sessionId,
|