@hienlh/ppm 0.18.12 → 0.18.14
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/AGENTS.md +17 -5
- package/CHANGELOG.md +27 -0
- package/CLAUDE.md +3 -0
- package/README.md +6 -6
- package/assets/skills/ppm/SKILL.md +1 -1
- package/assets/skills/ppm/references/cli-reference.md +4 -4
- package/assets/skills/ppm/references/http-api.md +19 -1
- package/dist/web/assets/{adaptive-context-menu-DGCKKeTy.js → adaptive-context-menu-DCZtgOL7.js} +1 -1
- package/dist/web/assets/{ai-resource-editor-BOHHTTe7.js → ai-resource-editor-D5CPBg06.js} +1 -1
- package/dist/web/assets/{ai-settings-section-BigC_nZR.js → ai-settings-section-DY-t1bK8.js} +1 -1
- package/dist/web/assets/{angular-html-BYPaydvh.js → angular-html-CTvM4ulF.js} +1 -1
- package/dist/web/assets/{angular-ts-Bb4AMKlk.js → angular-ts-B_vSkjco.js} +1 -1
- package/dist/web/assets/api-client-BogqsFna.js +1 -0
- package/dist/web/assets/{api-settings-Bd3M4YpI.js → api-settings-B2iNXQ0c.js} +1 -1
- package/dist/web/assets/api-settings-DRF9oYkn.js +1 -0
- package/dist/web/assets/{api-themes-Cpx419SL.js → api-themes-DrWnJbhs.js} +1 -1
- package/dist/web/assets/{apl-DB2VjFCS.js → apl-DywzTry0.js} +1 -1
- package/dist/web/assets/architecture-PBZL5I3N-nw5-j_I_.js +1 -0
- package/dist/web/assets/{astro-BeoJdVpk.js → astro-1JaRgaSb.js} +1 -1
- package/dist/web/assets/{audio-preview-BgFuTn-0.js → audio-preview-DAuGvOiK.js} +1 -1
- package/dist/web/assets/{blade-Cf2q1sPf.js → blade-CDeeJWBb.js} +1 -1
- package/dist/web/assets/{bsl-W1kOaNpu.js → bsl-BfgoPVYR.js} +1 -1
- package/dist/web/assets/{button-5zTrkEoN.js → button-tNy0ba3y.js} +1 -1
- package/dist/web/assets/{cairo-BoAZFcje.js → cairo-C26gNwWW.js} +1 -1
- package/dist/web/assets/chat-tab-IcnU3nqQ.js +15 -0
- package/dist/web/assets/{cobol-B0Nm7RXj.js → cobol-D92nBnON.js} +1 -1
- package/dist/web/assets/code-editor-Dno5Srkm.js +10 -0
- package/dist/web/assets/{coffee-BakMn3on.js → coffee-CIWH40R1.js} +1 -1
- package/dist/web/assets/{conflict-editor-BUs5k9q_.js → conflict-editor-CDp0I3mD.js} +1 -1
- package/dist/web/assets/{context-menu-Bq92km4s.js → context-menu-BO1c9jKy.js} +1 -1
- package/dist/web/assets/{cpp-BRAJQHK2.js → cpp-DOrBqXCn.js} +1 -1
- package/dist/web/assets/{crystal-kqqO3zkK.js → crystal-BNKqmHux.js} +1 -1
- package/dist/web/assets/{csv-preview-ByoDgLAZ.js → csv-preview-D4svNDAq.js} +1 -1
- package/dist/web/assets/{data-grid-overlay-editor-Brkf9z41.js → data-grid-overlay-editor-BqV0AxZg.js} +1 -1
- package/dist/web/assets/{database-viewer-Btc3h8Yu.js → database-viewer-BCvv_Bwf.js} +1 -1
- package/dist/web/assets/{dialog-DwtGlowE.js → dialog-DcLGDwaX.js} +1 -1
- package/dist/web/assets/diff-viewer-D41fCFAi.js +4 -0
- package/dist/web/assets/{docx-preview-DAD_vg59.js → docx-preview-DfaQn9v7.js} +1 -1
- package/dist/web/assets/{edge-Cq1ZKZuE.js → edge-Dbjp42Pt.js} +1 -1
- package/dist/web/assets/{edit-diff-preview-B_VE7ppQ.js → edit-diff-preview-BqcUuFCc.js} +1 -1
- package/dist/web/assets/{elixir-BM_JoJRk.js → elixir-na3b1pYy.js} +1 -1
- package/dist/web/assets/{elm-Dw7oO08Y.js → elm-_Nalil1R.js} +1 -1
- package/dist/web/assets/{erb-CfKOGAfk.js → erb-CW_7dubf.js} +1 -1
- package/dist/web/assets/{erlang-DlDmGVeE.js → erlang-DmZ7Zzb0.js} +1 -1
- package/dist/web/assets/{esm-DGAP0GFh.js → esm-CwyBetO1.js} +1 -1
- package/dist/web/assets/explorer-body-DB1L30pa.js +2 -0
- package/dist/web/assets/explorer-window-content-Cwlt3C3I.js +1 -0
- package/dist/web/assets/{extension-store-Cpfh2vMm.js → extension-store-DUaZINyC.js} +1 -1
- package/dist/web/assets/{extension-webview-B2OnjuCu.js → extension-webview-BW_mccb3.js} +1 -1
- package/dist/web/assets/{file-store-aOERIEo-.js → file-store-yReVbR8q.js} +1 -1
- package/dist/web/assets/{fortran-fixed-form-XnAhQWxS.js → fortran-fixed-form-DB2A8L4-.js} +1 -1
- package/dist/web/assets/{fsharp-CdNvy0PG.js → fsharp-BYHrti7z.js} +1 -1
- package/dist/web/assets/{gdresource-DxuG5i8i.js → gdresource-D5GBin40.js} +1 -1
- package/dist/web/assets/{git-commit-3lMonsur.js → git-commit-CUiOBoIL.js} +1 -1
- package/dist/web/assets/{git-log-panel-Co0PBPHC.js → git-log-panel-C1QDkmgl.js} +1 -1
- package/dist/web/assets/{git-rebase-N8Z9kIMf.js → git-rebase-DOxGnpdw.js} +1 -1
- package/dist/web/assets/gitGraph-HDMCJU4V-C1xf87gN.js +1 -0
- package/dist/web/assets/glide-data-grid-Dz8lx6n2.js +138 -0
- package/dist/web/assets/{glimmer-js-Dp9OXiib.js → glimmer-js-Begy4UT7.js} +1 -1
- package/dist/web/assets/{glimmer-ts-CDVKBcDj.js → glimmer-ts-rPApRY8N.js} +1 -1
- package/dist/web/assets/{glsl-DPJxI4us.js → glsl-wrbPvrBL.js} +1 -1
- package/dist/web/assets/{graphql-BrFhaQQl.js → graphql-Cu4-NR__.js} +1 -1
- package/dist/web/assets/group-chat-tab-DFwr-pig.js +1 -0
- package/dist/web/assets/{hack-BzXbDMlA.js → hack-Ddo93XKD.js} +1 -1
- package/dist/web/assets/{haml-CPpkX0s5.js → haml-BFX_1i8j.js} +1 -1
- package/dist/web/assets/{handlebars-BqrApy52.js → handlebars-5gFKA4H5.js} +1 -1
- package/dist/web/assets/{html-DmjUl-OA.js → html-C06I-zeO.js} +1 -1
- package/dist/web/assets/{html-derivative-Cnd6XFR5.js → html-derivative-e5JLAFZF.js} +1 -1
- package/dist/web/assets/{http-DMLWYun2.js → http-CrAAhSU7.js} +1 -1
- package/dist/web/assets/{hurl-CKkCVxbj.js → hurl-T1V3SX9T.js} +1 -1
- package/dist/web/assets/{hxml-CAx19TtQ.js → hxml-B056LLKN.js} +1 -1
- package/dist/web/assets/{image-preview-DHUoylQ1.js → image-preview-DWyuCYT3.js} +1 -1
- package/dist/web/assets/index-DWpNR3nB.css +2 -0
- package/dist/web/assets/index-oUKV5nIA.js +52 -0
- package/dist/web/assets/info-3K5VOQVL-C2z7FTDe.js +1 -0
- package/dist/web/assets/{input-DcEk7mA3.js → input-DouJ9Zvt.js} +1 -1
- package/dist/web/assets/{jinja-BmB9YN9l.js → jinja-DGdtRh43.js} +1 -1
- package/dist/web/assets/{jison-BaVsKKpw.js → jison-Ah6QobPS.js} +1 -1
- package/dist/web/assets/{julia-BgVBLveX.js → julia-BJeGOK0K.js} +1 -1
- package/dist/web/assets/{just-C2ENIbl9.js → just-iw9oRs7j.js} +1 -1
- package/dist/web/assets/{keybindings-store-BGIyqXHI.js → keybindings-store-0Wqfa7Qp.js} +1 -1
- package/dist/web/assets/keybindings-store-d54pv2NK.js +1 -0
- package/dist/web/assets/{latex-BiDIWxKL.js → latex-DHjj5hyN.js} +1 -1
- package/dist/web/assets/{liquid-NxpapreE.js → liquid-B-FFecDc.js} +1 -1
- package/dist/web/assets/{lua-DVepQvwH.js → lua-DDianJp_.js} +1 -1
- package/dist/web/assets/{markdown-renderer-DlcDhgrF.js → markdown-renderer-BHiUhQN8.js} +3 -3
- package/dist/web/assets/markdown-renderer-CnE36_gd.js +1 -0
- package/dist/web/assets/{marko-CJ2606Xl.js → marko-BW5SBVng.js} +1 -1
- package/dist/web/assets/{mdc-D2bjqclE.js → mdc-B9gL9bjl.js} +1 -1
- package/dist/web/assets/{media-url-H17XJF-I.js → media-url-DUK-k0bB.js} +1 -1
- package/dist/web/assets/{mobile-bottom-sheet-g-r2hCFa.js → mobile-bottom-sheet-CJ7ApOkI.js} +1 -1
- package/dist/web/assets/mobile-explorer-sheet-BzjlS1Xn.js +1 -0
- package/dist/web/assets/{nextflow-BtJwUbfF.js → nextflow-dTluY6qy.js} +1 -1
- package/dist/web/assets/{nginx-j4gyIhHE.js → nginx-DlwghGI-.js} +1 -1
- package/dist/web/assets/{nim-ClH_gjP5.js → nim-N9jddb5D.js} +1 -1
- package/dist/web/assets/notification-store-C7UBqxYP.js +1 -0
- package/dist/web/assets/{number-overlay-editor-C1EqJnWq.js → number-overlay-editor-BVXM7aAs.js} +1 -1
- package/dist/web/assets/{overview-panel-vTeeJYVZ.js → overview-panel-DCCDmtCQ.js} +1 -1
- package/dist/web/assets/packet-RMMSAZCW-DQUxN0wU.js +1 -0
- package/dist/web/assets/{panel-store-BSnkjgHt.js → panel-store-B4rijFKN.js} +1 -1
- package/dist/web/assets/{pdf-preview-CqFGRY0Z.js → pdf-preview-D7yLmAZW.js} +1 -1
- package/dist/web/assets/{perl-C9H7X7CT.js → perl-CkmpYKa_.js} +1 -1
- package/dist/web/assets/{php-Bx2U-cQ_.js → php-BwZ3KDPo.js} +1 -1
- package/dist/web/assets/pie-UPGHQEXC-BOr9ek4Q.js +1 -0
- package/dist/web/assets/{postgres-viewer-DHwcQucn.js → postgres-viewer--CRN1fvO.js} +3 -3
- package/dist/web/assets/{process-table-BGRJNlpn.js → process-table-CXC10GSy.js} +1 -1
- package/dist/web/assets/{project-store--nvQiy75.js → project-store-BuY7rxn_.js} +1 -1
- package/dist/web/assets/{pug-Cw74sKsE.js → pug-B3rBPqQo.js} +1 -1
- package/dist/web/assets/{qml-BXD94Eqz.js → qml-BfDbWFmg.js} +1 -1
- package/dist/web/assets/radar-KQ55EAFF-DSErxZFb.js +1 -0
- package/dist/web/assets/{razor-DZ0-Ge0S.js → razor-B1ww1r38.js} +1 -1
- package/dist/web/assets/{rst-Dtdh0QUj.js → rst-C0MSq6m-.js} +1 -1
- package/dist/web/assets/{ruby-BC51BW2Q.js → ruby-ClIZ1wb3.js} +1 -1
- package/dist/web/assets/{run-in-terminal-Sd3KyjN4.js → run-in-terminal-4efbW2Xv.js} +1 -1
- package/dist/web/assets/{sas-BN8i4qOU.js → sas-DR0j3o4_.js} +1 -1
- package/dist/web/assets/{scroll-area-0gljZ6lF.js → scroll-area-ZP8wcwTI.js} +1 -1
- package/dist/web/assets/{scss-H-QtA7Rz.js → scss-Vh3JLayR.js} +1 -1
- package/dist/web/assets/{send-to-chat-u6m8l-rH.js → send-to-chat-BNrEpAyC.js} +1 -1
- package/dist/web/assets/{settings-store-CHY6Kzin.js → settings-store-OLJE7dPH.js} +2 -2
- package/dist/web/assets/{settings-tab-IY6K1MiZ.js → settings-tab-B1rvHFc6.js} +1 -1
- package/dist/web/assets/settings-tab-BUlmPJ2w.js +1 -0
- package/dist/web/assets/{shaderlab-CalNsj_x.js → shaderlab-0rHRnQ18.js} +1 -1
- package/dist/web/assets/{shellsession-BFrcoR8x.js → shellsession-Dxdw6D8c.js} +1 -1
- package/dist/web/assets/{soy-CNbku7vP.js → soy-BHlSNj1y.js} +1 -1
- package/dist/web/assets/{sparql-BN_orcVp.js → sparql-B2QaPpni.js} +1 -1
- package/dist/web/assets/{sql-query-editor-C6pC3I8l.js → sql-query-editor-R_iKjPP9.js} +1 -1
- package/dist/web/assets/{sqlite-viewer-DTr5PzUb.js → sqlite-viewer-b2onA0SP.js} +1 -1
- package/dist/web/assets/{stata-C6a5Ijzq.js → stata-CwIxjsEJ.js} +1 -1
- package/dist/web/assets/{surrealql-ClplI_HY.js → surrealql-C6vo-rp-.js} +1 -1
- package/dist/web/assets/{svelte-DNm-UNXB.js → svelte-LLdJ6rlH.js} +1 -1
- package/dist/web/assets/{switch-DKvKya9y.js → switch-DV-BDjyk.js} +1 -1
- package/dist/web/assets/system-monitor-body-DPYQlBEt.js +2 -0
- package/dist/web/assets/system-monitor-tab-CPQdWv27.js +1 -0
- package/dist/web/assets/{system-monitor-window-content-BUqRsPK2.js → system-monitor-window-content-BPif0azo.js} +1 -1
- package/dist/web/assets/tab-host-window-content-BnAN6m4K.js +1 -0
- package/dist/web/assets/{tab-store-CJuJRapl.js → tab-store-tFnyPmm4.js} +1 -1
- package/dist/web/assets/team-member-sheet-Bu2wG1sm.js +2 -0
- package/dist/web/assets/{team-member-window-content-T4Q5JI-G.js → team-member-window-content-BSyU-5Uc.js} +1 -1
- package/dist/web/assets/{templ-CSdqVobu.js → templ-CJGvhEP5.js} +1 -1
- package/dist/web/assets/{terminal-tab-Bb2rZegQ.js → terminal-tab-Ncor0Rfu.js} +3 -3
- package/dist/web/assets/{tex-DzSJ4kcZ.js → tex-psXWhc4t.js} +1 -1
- package/dist/web/assets/{tool-cards-lhqhrDTT.js → tool-cards-DdVP1wrD.js} +4 -4
- package/dist/web/assets/treemap-KZPCXAKY-C17p0LQw.js +1 -0
- package/dist/web/assets/{ts-tags-C28w3bTg.js → ts-tags-D5Ar_ZCO.js} +1 -1
- package/dist/web/assets/{twig-i5jY3QXB.js → twig-vJgGC-qY.js} +1 -1
- package/dist/web/assets/{use-blob-url-Dsq9OjTh.js → use-blob-url-BOVGWg9F.js} +1 -1
- package/dist/web/assets/use-coarse-long-press-BmI_GVW5.js +1 -0
- package/dist/web/assets/use-monaco-theme-BshnLXoY.js +1 -0
- package/dist/web/assets/{use-open-team-member-DkiEjFYE.js → use-open-team-member-CY3wOdWL.js} +1 -1
- package/dist/web/assets/{use-websocket-B8R50f7f.js → use-websocket-B7xjNsyD.js} +1 -1
- package/dist/web/assets/{vendor-mermaid-By4AVvJY.js → vendor-mermaid-999ksD45.js} +1 -1
- package/dist/web/assets/video-preview-7F72sRwf.js +1 -0
- package/dist/web/assets/{vue-Ceyet_lv.js → vue-BWngFyav.js} +1 -1
- package/dist/web/assets/{vue-html-GuU7g_hg.js → vue-html-BN5ZLu-H.js} +1 -1
- package/dist/web/assets/{vue-vine-CxXoaJr2.js → vue-vine-BZHnO8yV.js} +1 -1
- package/dist/web/assets/window-pip-registry-BnYU83LE.js +1 -0
- package/dist/web/assets/{window-store-CsEW6jiE.js → window-store-CQ1lghvv.js} +1 -1
- package/dist/web/assets/{xml-CbqOVhkP.js → xml-DS2beZHp.js} +1 -1
- package/dist/web/assets/{xsl-KfAmCauy.js → xsl-Ciiku1Bq.js} +1 -1
- package/dist/web/index.html +47 -45
- package/dist/web/sw.js +1 -1
- package/docs/architecture/ai-chat-and-providers.md +309 -0
- package/docs/architecture/data-and-storage.md +356 -0
- package/docs/architecture/extensions.md +496 -0
- package/docs/architecture/integrations.md +214 -0
- package/docs/architecture/workspace-and-ui.md +399 -0
- package/docs/claude-agent-sdk-reference.md +1 -1
- package/docs/code-patterns.md +361 -0
- package/docs/code-standards.md +8 -358
- package/docs/codebase-subsystems.md +364 -0
- package/docs/codebase-summary.md +34 -379
- package/docs/deployment-guide.md +280 -490
- package/docs/design-guidelines.md +4 -3
- package/docs/lessons-learned.md +27 -10
- package/docs/project-changelog.md +18 -806
- package/docs/project-overview-pdr.md +117 -111
- package/docs/project-roadmap.md +89 -133
- package/docs/system-architecture.md +77 -1826
- package/package.json +2 -2
- package/src/cli/commands/autostart.ts +2 -2
- package/src/cli/commands/config-cmd.ts +12 -3
- package/src/cli/commands/status.ts +3 -2
- package/src/cli/commands/stop.ts +32 -4
- package/src/server/index.ts +17 -3
- package/src/server/routes/chat-unread.ts +36 -0
- package/src/server/routes/named-tunnel.ts +132 -0
- package/src/server/routes/tunnel.ts +13 -0
- package/src/server/ws/chat.ts +5 -2
- package/src/services/autostart-register.ts +47 -29
- package/src/services/cloud.service.ts +26 -1
- package/src/services/config-secret-keys.ts +48 -0
- package/src/services/config.service.ts +1 -1
- package/src/services/db.service.ts +62 -12
- package/src/services/edge-forwarder.ts +191 -191
- package/src/services/extension-rpc-handlers.ts +9 -1
- package/src/services/file-watcher/watch-tree.ts +67 -33
- package/src/services/file-watcher.service.ts +28 -10
- package/src/services/fs-credential-path-guard.ts +103 -0
- package/src/services/fs-path-guard.service.ts +14 -66
- package/src/services/named-tunnel/cloudflare-dns-api.ts +40 -0
- package/src/services/named-tunnel/cloudflare-zone-api.ts +28 -0
- package/src/services/named-tunnel/cloudflared-cert.ts +95 -0
- package/src/services/named-tunnel/cloudflared-exec.ts +50 -0
- package/src/services/named-tunnel/cloudflared-login-helpers.ts +98 -0
- package/src/services/named-tunnel/cloudflared-login.service.ts +182 -0
- package/src/services/named-tunnel/hostname-rules.ts +58 -0
- package/src/services/named-tunnel/login-output-parser.ts +36 -0
- package/src/services/named-tunnel/named-tunnel-args.ts +110 -0
- package/src/services/named-tunnel/named-tunnel-config.ts +67 -0
- package/src/services/named-tunnel/named-tunnel-probe-state.ts +95 -0
- package/src/services/named-tunnel/named-tunnel-readiness.ts +59 -0
- package/src/services/named-tunnel/named-tunnel-runtime.ts +42 -0
- package/src/services/named-tunnel/named-tunnel-setup-confirm.ts +98 -0
- package/src/services/named-tunnel/named-tunnel-setup.service.ts +191 -0
- package/src/services/supervisor-state.ts +67 -2
- package/src/services/supervisor.ts +454 -157
- package/src/services/tunnel-registry-parse.ts +27 -0
- package/src/services/tunnel-registry.service.ts +2 -15
- package/src/services/tunnel.service.ts +12 -3
- package/src/services/windows-process-tree.ts +351 -351
- package/src/types/config.ts +31 -0
- package/src/web/app.tsx +5 -0
- package/src/web/components/chat/chat-history-bar.tsx +95 -98
- package/src/web/components/chat/chat-toolbar-overflow-menu.tsx +57 -0
- package/src/web/components/chat/session-debug-dialog.tsx +35 -27
- package/src/web/components/floating-window/floating-window.tsx +8 -1
- package/src/web/components/floating-window/open-tab-in-pip.ts +78 -0
- package/src/web/components/floating-window/window-pip-registry.ts +46 -0
- package/src/web/components/layout/cloud-share-named-tunnel-row.tsx +111 -0
- package/src/web/components/layout/cloud-share-popover.tsx +56 -14
- package/src/web/components/layout/tab-pop-out-menu-item.tsx +25 -2
- package/src/web/components/shared/connection-lost-overlay.tsx +44 -5
- package/src/web/components/tunnels/named-tunnel/hostname-validation.ts +14 -0
- package/src/web/components/tunnels/named-tunnel/named-tunnel-copy.ts +96 -0
- package/src/web/components/tunnels/named-tunnel/named-tunnel-hostname-field.tsx +51 -0
- package/src/web/components/tunnels/named-tunnel/named-tunnel-login-step.tsx +105 -0
- package/src/web/components/tunnels/named-tunnel/named-tunnel-section.tsx +159 -0
- package/src/web/components/tunnels/named-tunnel/named-tunnel-setup-content.tsx +197 -0
- package/src/web/components/tunnels/named-tunnel/named-tunnel-setup-popup.tsx +57 -0
- package/src/web/components/tunnels/named-tunnel/named-tunnel-step-reducer.ts +191 -0
- package/src/web/components/tunnels/named-tunnel/use-named-tunnel-setup.ts +212 -0
- package/src/web/components/tunnels/tunnel-manager-tab.tsx +4 -0
- package/src/web/hooks/use-global-events.ts +3 -1
- package/src/web/lib/api-named-tunnel.ts +54 -0
- package/src/web/lib/last-known-local-url.ts +23 -0
- package/src/web/stores/notification-store.ts +20 -1
- package/dist/web/assets/api-client-DTZEgjBQ.js +0 -1
- package/dist/web/assets/api-settings-kd1j4iR8.js +0 -1
- package/dist/web/assets/architecture-PBZL5I3N-8dCMT9r7.js +0 -1
- package/dist/web/assets/chat-tab-E3bf_V8X.js +0 -15
- package/dist/web/assets/code-editor-BUlM2gXk.js +0 -10
- package/dist/web/assets/diff-viewer-CZaDnR4w.js +0 -4
- package/dist/web/assets/explorer-body-9aBmyVpZ.js +0 -2
- package/dist/web/assets/explorer-window-content-DRtqoSPA.js +0 -1
- package/dist/web/assets/gitGraph-HDMCJU4V-2e_8dfbs.js +0 -1
- package/dist/web/assets/glide-data-grid-C4_zIneN.js +0 -138
- package/dist/web/assets/group-chat-tab-rOd8J8GC.js +0 -1
- package/dist/web/assets/index-BImUaigO.js +0 -52
- package/dist/web/assets/index-CSfi6DSU.css +0 -2
- package/dist/web/assets/info-3K5VOQVL-BZi1Gha1.js +0 -1
- package/dist/web/assets/keybindings-store-CzV2sPls.js +0 -1
- package/dist/web/assets/markdown-renderer-CcbR33VG.js +0 -1
- package/dist/web/assets/mobile-explorer-sheet-SIwQXlwA.js +0 -1
- package/dist/web/assets/notification-store-DV5QtUTg.js +0 -1
- package/dist/web/assets/packet-RMMSAZCW-o5htEcin.js +0 -1
- package/dist/web/assets/pie-UPGHQEXC-BXAIFSAD.js +0 -1
- package/dist/web/assets/radar-KQ55EAFF-Dbt2Aebl.js +0 -1
- package/dist/web/assets/settings-tab-Dic6TKkU.js +0 -1
- package/dist/web/assets/system-monitor-body-iwGiCns9.js +0 -2
- package/dist/web/assets/system-monitor-tab-DdXQhEel.js +0 -1
- package/dist/web/assets/tab-host-window-content-BnqGPu8a.js +0 -1
- package/dist/web/assets/team-member-sheet-LkAFSCvb.js +0 -2
- package/dist/web/assets/treemap-KZPCXAKY-DUbnl9dz.js +0 -1
- package/dist/web/assets/use-monaco-theme-BZvlsoBv.js +0 -1
- package/dist/web/assets/video-preview-m0-3p7QZ.js +0 -1
- package/packages/vscode-compat/node_modules/.bin/tsc.bunx +0 -0
- package/packages/vscode-compat/node_modules/.bin/tsc.exe +0 -0
- package/packages/vscode-compat/node_modules/.bin/tsserver.bunx +0 -0
- package/packages/vscode-compat/node_modules/.bin/tsserver.exe +0 -0
- /package/dist/web/assets/{abap-C6nB04tn.js → abap-CjzrhAS2.js} +0 -0
- /package/dist/web/assets/{actionscript-3-Vx4rw2sC.js → actionscript-3-B59Kbl-9.js} +0 -0
- /package/dist/web/assets/{ada-Ccd8vM20.js → ada-B7gItKAE.js} +0 -0
- /package/dist/web/assets/{andromeeda-DnBX5xMg.js → andromeeda-Dy5zcy0Q.js} +0 -0
- /package/dist/web/assets/{apache-B-9_sx0g.js → apache-B6T9jrc0.js} +0 -0
- /package/dist/web/assets/{apex-_wPVBYoL.js → apex-CTsIxDQw.js} +0 -0
- /package/dist/web/assets/{api-client-aQb5hHE5.js → api-client-oY1tC3tM.js} +0 -0
- /package/dist/web/assets/{applescript-CcvD-qr0.js → applescript-DuycKmmb.js} +0 -0
- /package/dist/web/assets/{apply-theme-BeLRfsSu.js → apply-theme-r8tKDrCR.js} +0 -0
- /package/dist/web/assets/{ara-C40Ztoms.js → ara-CISL3599.js} +0 -0
- /package/dist/web/assets/{asciidoc-CybYkJV4.js → asciidoc-BXyE2Lpr.js} +0 -0
- /package/dist/web/assets/{asm-9eOKwzR0.js → asm-y258phtP.js} +0 -0
- /package/dist/web/assets/{aurora-x-B5ZH4t7t.js → aurora-x-Dq3Jc78M.js} +0 -0
- /package/dist/web/assets/{awk-Dqilob-2.js → awk-DxL6XSJv.js} +0 -0
- /package/dist/web/assets/{ayu-dark-CPgRhf5r.js → ayu-dark-DjoK6Evo.js} +0 -0
- /package/dist/web/assets/{ayu-light-Do06Q52f.js → ayu-light-CKKXBLMI.js} +0 -0
- /package/dist/web/assets/{ayu-mirage-_BhxcrxY.js → ayu-mirage-DGXSrrXB.js} +0 -0
- /package/dist/web/assets/{ballerina-DDwK54QW.js → ballerina-DZSXFmUf.js} +0 -0
- /package/dist/web/assets/{bat-BGyGlzGk.js → bat-BNsZk6nY.js} +0 -0
- /package/dist/web/assets/{beancount-CRWBdapI.js → beancount-cPl4gJct.js} +0 -0
- /package/dist/web/assets/{berry-DbSFn2gO.js → berry-DGAodERk.js} +0 -0
- /package/dist/web/assets/{bibtex-QpsV9gm9.js → bibtex-DftVoRif.js} +0 -0
- /package/dist/web/assets/{bicep-CxS8CUve.js → bicep-Suioyyzh.js} +0 -0
- /package/dist/web/assets/{bird2-CuZhQzYR.js → bird2-DjU24I2P.js} +0 -0
- /package/dist/web/assets/{c-9bnDqE3g.js → c-DFi8Y_nN.js} +0 -0
- /package/dist/web/assets/{c3-JMZTrOhA.js → c3-1eSHnH5V.js} +0 -0
- /package/dist/web/assets/{cadence-DsDNayJS.js → cadence-DLLsFwQP.js} +0 -0
- /package/dist/web/assets/{can-open-in-ppm-BJfpynya.js → can-open-in-ppm-in727eFQ.js} +0 -0
- /package/dist/web/assets/{catppuccin-frappe-DYIKXXBe.js → catppuccin-frappe-DDj0JEiY.js} +0 -0
- /package/dist/web/assets/{catppuccin-latte-IYYOnUJ8.js → catppuccin-latte-DPyANHyR.js} +0 -0
- /package/dist/web/assets/{catppuccin-macchiato-BrJ_7mfS.js → catppuccin-macchiato-CO-h8VTT.js} +0 -0
- /package/dist/web/assets/{catppuccin-mocha-gzSu7v7-.js → catppuccin-mocha-BCmbN2IZ.js} +0 -0
- /package/dist/web/assets/{clarity-DLzsC0Hb.js → clarity-C0pYsZjs.js} +0 -0
- /package/dist/web/assets/{clipboard-7fw0Shic.js → clipboard-DBLGKMrH.js} +0 -0
- /package/dist/web/assets/{clojure-DS1BvoTt.js → clojure-JFr6s8iZ.js} +0 -0
- /package/dist/web/assets/{cmake-CDvsJwYp.js → cmake-EEcLEgVy.js} +0 -0
- /package/dist/web/assets/{codeowners-BjsYiflJ.js → codeowners-Deh3Lx0u.js} +0 -0
- /package/dist/web/assets/{codeql-CG-7ARat.js → codeql-C317igT5.js} +0 -0
- /package/dist/web/assets/{common-lisp-DSO1Lbh9.js → common-lisp-DepbATZs.js} +0 -0
- /package/dist/web/assets/{coq-DYdh3oP_.js → coq-MxgxU5ya.js} +0 -0
- /package/dist/web/assets/{csharp-DxEBz5s0.js → csharp-eEFXdsAq.js} +0 -0
- /package/dist/web/assets/{css-dal3Q-8C.js → css-Db-syWW9.js} +0 -0
- /package/dist/web/assets/{csv-CpqjMGiA.js → csv-DHeYcbaq.js} +0 -0
- /package/dist/web/assets/{csv-parser-3umBA4NY.js → csv-parser-_JWMLGl3.js} +0 -0
- /package/dist/web/assets/{cue-DinoJ_5k.js → cue-BmUNowrt.js} +0 -0
- /package/dist/web/assets/{cypher-DzxqmkUW.js → cypher-XjXHgCNS.js} +0 -0
- /package/dist/web/assets/{d-BxNwJuEC.js → d-BWJnZtig.js} +0 -0
- /package/dist/web/assets/{dark-plus-Clmv3V-0.js → dark-plus-Db7umas1.js} +0 -0
- /package/dist/web/assets/{dart-BWVtXD35.js → dart-DSI4HYh1.js} +0 -0
- /package/dist/web/assets/{data-grid-types-B4n51Fck.js → data-grid-types-CfVHh9gv.js} +0 -0
- /package/dist/web/assets/{dax-DsImuWgJ.js → dax-yw1JsAni.js} +0 -0
- /package/dist/web/assets/{desktop-B0jJuG2r.js → desktop-C2QO4RvD.js} +0 -0
- /package/dist/web/assets/{diff-CWqAAxoE.js → diff-Drt8aEyp.js} +0 -0
- /package/dist/web/assets/{dist-B-EXdSYE.js → dist-DNRvhVdz.js} +0 -0
- /package/dist/web/assets/{dist-C0eNNfbt.js → dist-VBxOHLmN.js} +0 -0
- /package/dist/web/assets/{docker-CYFCJdIL.js → docker-ByDKbmLl.js} +0 -0
- /package/dist/web/assets/{dotenv-CKDUjURg.js → dotenv-BkfOLytR.js} +0 -0
- /package/dist/web/assets/{dracula-CSoPspHE.js → dracula-DAmrm-Un.js} +0 -0
- /package/dist/web/assets/{dracula-soft-BuFjw5L1.js → dracula-soft-BUOEnKqR.js} +0 -0
- /package/dist/web/assets/{dream-maker-B6PdDRZu.js → dream-maker-DQBIfSiO.js} +0 -0
- /package/dist/web/assets/{emacs-lisp-CMcXvD0G.js → emacs-lisp-Blh5kBD3.js} +0 -0
- /package/dist/web/assets/{everforest-dark-cU8-wYzi.js → everforest-dark-CEzhDxHq.js} +0 -0
- /package/dist/web/assets/{everforest-light-KhorllqK.js → everforest-light-CPk_5Khm.js} +0 -0
- /package/dist/web/assets/{external-link-B8BynhvY.js → external-link-B6Hl0U4X.js} +0 -0
- /package/dist/web/assets/{fennel-BCELZK87.js → fennel-B1MT_EBZ.js} +0 -0
- /package/dist/web/assets/{file-exclamation-point-CZ4DiR75.js → file-exclamation-point-JzhTU2TX.js} +0 -0
- /package/dist/web/assets/{fish-BQc0y_jy.js → fish-C_HGcewt.js} +0 -0
- /package/dist/web/assets/{fluent-BTINnwF6.js → fluent-Dp1Iclmx.js} +0 -0
- /package/dist/web/assets/{folder-symlink-B8XrtvQK.js → folder-symlink-Dx1hOk8X.js} +0 -0
- /package/dist/web/assets/{format-bytes-WVcFxJuT.js → format-bytes-53WBtyi2.js} +0 -0
- /package/dist/web/assets/{fortran-free-form-BQim9wDz.js → fortran-free-form-B_-UA4XC.js} +0 -0
- /package/dist/web/assets/{gdscript-03w5u8un.js → gdscript-Bm_JpjS_.js} +0 -0
- /package/dist/web/assets/{gdshader-Dp2daNzU.js → gdshader-C_PNmfYU.js} +0 -0
- /package/dist/web/assets/{genie-CoNcjPiD.js → genie-B5rmR2TF.js} +0 -0
- /package/dist/web/assets/{gherkin-NrPOSPQL.js → gherkin-BTJ34TJ9.js} +0 -0
- /package/dist/web/assets/{github-dark-z4RE9ZqA.js → github-dark-DOkNVj9H.js} +0 -0
- /package/dist/web/assets/{github-dark-default-DOKw-1cI.js → github-dark-default-DTsmiyVo.js} +0 -0
- /package/dist/web/assets/{github-dark-dimmed-DJRNMkEA.js → github-dark-dimmed-CjbxAe9s.js} +0 -0
- /package/dist/web/assets/{github-dark-high-contrast-Bj7jJ_Gs.js → github-dark-high-contrast-r_M8F_k7.js} +0 -0
- /package/dist/web/assets/{github-light-B4k_yMFu.js → github-light-BzfxyMyb.js} +0 -0
- /package/dist/web/assets/{github-light-default-BHc-Uzhs.js → github-light-default-BOqiBIy5.js} +0 -0
- /package/dist/web/assets/{github-light-high-contrast-CHisIywL.js → github-light-high-contrast-DQzBrDM4.js} +0 -0
- /package/dist/web/assets/{gleam-CfbypZ4q.js → gleam-BWmuAtD8.js} +0 -0
- /package/dist/web/assets/{gn-YoAjyhJa.js → gn-CNQPjiZW.js} +0 -0
- /package/dist/web/assets/{gnuplot-xVkc1f1c.js → gnuplot-BlHjcPMe.js} +0 -0
- /package/dist/web/assets/{go-GPCwP3Gu.js → go-Bn8Ba9uz.js} +0 -0
- /package/dist/web/assets/{groovy-Crh4Yww9.js → groovy-C1Y1_F4V.js} +0 -0
- /package/dist/web/assets/{gruvbox-dark-hard-BluUFuDS.js → gruvbox-dark-hard-DueOsHav.js} +0 -0
- /package/dist/web/assets/{gruvbox-dark-medium-Cxk1FWQp.js → gruvbox-dark-medium-DaQjD538.js} +0 -0
- /package/dist/web/assets/{gruvbox-dark-soft-CHXMiFmj.js → gruvbox-dark-soft-CbbX2WOm.js} +0 -0
- /package/dist/web/assets/{gruvbox-light-hard-B6eKPjAD.js → gruvbox-light-hard-E2M70ljZ.js} +0 -0
- /package/dist/web/assets/{gruvbox-light-medium-pDwMm1ut.js → gruvbox-light-medium-B_DDLY6h.js} +0 -0
- /package/dist/web/assets/{gruvbox-light-soft-Cxu6kHqK.js → gruvbox-light-soft-2SZmyC1F.js} +0 -0
- /package/dist/web/assets/{haskell-C6JKHjbK.js → haskell-D8DeoXtG.js} +0 -0
- /package/dist/web/assets/{haxe-BuBNdF0a.js → haxe-BmZm7B0W.js} +0 -0
- /package/dist/web/assets/{hcl-DZpDdGj2.js → hcl-CKnBM3ng.js} +0 -0
- /package/dist/web/assets/{hjson-CBkhYRuo.js → hjson-Ct9t4-ci.js} +0 -0
- /package/dist/web/assets/{hlsl-C5nMxnp9.js → hlsl-BZSASBqS.js} +0 -0
- /package/dist/web/assets/{horizon-VAFvA6R6.js → horizon-CzX1AZ08.js} +0 -0
- /package/dist/web/assets/{horizon-bright-BC0xn7T3.js → horizon-bright-CCVNeRIP.js} +0 -0
- /package/dist/web/assets/{houston-BxrUivmz.js → houston-DBlJN7xc.js} +0 -0
- /package/dist/web/assets/{hy-DLtOXoaH.js → hy-Dyc6h6Xb.js} +0 -0
- /package/dist/web/assets/{image-gallery-Gnf7TdCR.js → image-gallery-DujlrjOC.js} +0 -0
- /package/dist/web/assets/{imba-BdpFXM2W.js → imba-Bauru7s2.js} +0 -0
- /package/dist/web/assets/{ini-BInJO_T0.js → ini-SjdwtaTp.js} +0 -0
- /package/dist/web/assets/{java-Dxuddnkb.js → java-D2r0ocpf.js} +0 -0
- /package/dist/web/assets/{javascript-CVamJfLY.js → javascript-y-lof7iP.js} +0 -0
- /package/dist/web/assets/{json-BLZ0ozYk.js → json-DO4LSx5A.js} +0 -0
- /package/dist/web/assets/{json5-BnXzmMKs.js → json5-BEwWf-W3.js} +0 -0
- /package/dist/web/assets/{jsonc-CSgkSga7.js → jsonc-OBEFv7gP.js} +0 -0
- /package/dist/web/assets/{jsonl-CY5W2nF2.js → jsonl-DOyW4Mg3.js} +0 -0
- /package/dist/web/assets/{jsonnet-CEJYH3sF.js → jsonnet-BL5R2CpP.js} +0 -0
- /package/dist/web/assets/{jssm-CsJc7Uhk.js → jssm-ksPusAyg.js} +0 -0
- /package/dist/web/assets/{jsx-jCiRaHU7.js → jsx-B6XEu6O-.js} +0 -0
- /package/dist/web/assets/{kanagawa-dragon-DfoA_GxX.js → kanagawa-dragon-JBVe_ji_.js} +0 -0
- /package/dist/web/assets/{kanagawa-lotus-BOLYJpWd.js → kanagawa-lotus-ChprO68_.js} +0 -0
- /package/dist/web/assets/{kanagawa-wave-DfKlbT1G.js → kanagawa-wave-D_uB_R0Q.js} +0 -0
- /package/dist/web/assets/{kdl-CmssIFTi.js → kdl-pf5B8N_m.js} +0 -0
- /package/dist/web/assets/{kotlin-BRsNgp6x.js → kotlin-BdF0METC.js} +0 -0
- /package/dist/web/assets/{kusto-T8X9jUab.js → kusto-BneVP-OU.js} +0 -0
- /package/dist/web/assets/{laserwave-DkJMVJ8Z.js → laserwave-C4-UPIyG.js} +0 -0
- /package/dist/web/assets/{lean-C65KGVPq.js → lean-BN6Jdymq.js} +0 -0
- /package/dist/web/assets/{less-exIPdDlf.js → less-Cx-fdlDC.js} +0 -0
- /package/dist/web/assets/{lib-mC1NZfuw.js → lib-CMuBa85c.js} +0 -0
- /package/dist/web/assets/{libesm-DSutUtl1.js → libesm-BB74PRsH.js} +0 -0
- /package/dist/web/assets/{light-plus-DiHUXAZt.js → light-plus-DFnnn_iL.js} +0 -0
- /package/dist/web/assets/{llvm-CJBnEzFM.js → llvm-BGxtW0Nw.js} +0 -0
- /package/dist/web/assets/{loader-circle-gSPVxfvH.js → loader-circle-Dpqcv0eS.js} +0 -0
- /package/dist/web/assets/{lock-Did-87UV.js → lock-DldbT31x.js} +0 -0
- /package/dist/web/assets/{log-DxMyUMuP.js → log-kQb4eJnj.js} +0 -0
- /package/dist/web/assets/{logo-CC9NKzca.js → logo-DqAzaq7s.js} +0 -0
- /package/dist/web/assets/{luau-Bw10I6zf.js → luau-Dc2cp-zS.js} +0 -0
- /package/dist/web/assets/{make-C_6M90m6.js → make-DYg9dwaJ.js} +0 -0
- /package/dist/web/assets/{markdown-bR9_R-LF.js → markdown-O4EPpS3K.js} +0 -0
- /package/dist/web/assets/{material-theme-BUx2Qujn.js → material-theme-ClP8yfQ2.js} +0 -0
- /package/dist/web/assets/{material-theme-darker-CRJrPgKm.js → material-theme-darker-LOK55hGu.js} +0 -0
- /package/dist/web/assets/{material-theme-lighter-CUAYppPa.js → material-theme-lighter-j72tzZqi.js} +0 -0
- /package/dist/web/assets/{material-theme-ocean-DuINF1fg.js → material-theme-ocean-C19LKClA.js} +0 -0
- /package/dist/web/assets/{material-theme-palenight-W6_icGCj.js → material-theme-palenight-DJ5tN2ax.js} +0 -0
- /package/dist/web/assets/{matlab-rfqn_b3z.js → matlab-DQNZ_NU8.js} +0 -0
- /package/dist/web/assets/{mdx-Bh1B_YOJ.js → mdx-CJayB7XK.js} +0 -0
- /package/dist/web/assets/{mermaid-Dxf6Ya2k.js → mermaid-Cb0TRX92.js} +0 -0
- /package/dist/web/assets/{min-dark-CivZ2L0h.js → min-dark-CWM8Bo-m.js} +0 -0
- /package/dist/web/assets/{min-light-B1Ykqtma.js → min-light-BBR1sjS2.js} +0 -0
- /package/dist/web/assets/{mipsasm-DynHaM0d.js → mipsasm-Jc7u7YSS.js} +0 -0
- /package/dist/web/assets/{mojo-Clw5PbGH.js → mojo-CTB0cutd.js} +0 -0
- /package/dist/web/assets/{monitor-Dv8vn_6O.js → monitor-4vWfqzuN.js} +0 -0
- /package/dist/web/assets/{monokai-DFIooFZT.js → monokai-CvM_YNKx.js} +0 -0
- /package/dist/web/assets/{moonbit-DnBSZWGM.js → moonbit-CbFEfYYM.js} +0 -0
- /package/dist/web/assets/{move-CCcL2PfL.js → move-BT38p0kT.js} +0 -0
- /package/dist/web/assets/{narrat-BDarg5Lk.js → narrat-55QZ_kOG.js} +0 -0
- /package/dist/web/assets/{nextflow-groovy-DrZOeCNW.js → nextflow-groovy-Bg8peyqg.js} +0 -0
- /package/dist/web/assets/{night-owl-BnZZCK7v.js → night-owl-DzS77iPK.js} +0 -0
- /package/dist/web/assets/{night-owl-light-CEwBsVHS.js → night-owl-light-DER7fGnx.js} +0 -0
- /package/dist/web/assets/{nix-amCzOPMR.js → nix-BSI8YesN.js} +0 -0
- /package/dist/web/assets/{nord-BIs2BC6q.js → nord-9bn7iNWo.js} +0 -0
- /package/dist/web/assets/{nushell-Y02fo4yw.js → nushell-qDVZn_lw.js} +0 -0
- /package/dist/web/assets/{objective-c-7xOZtWW1.js → objective-c-Blf-ATos.js} +0 -0
- /package/dist/web/assets/{objective-cpp-eVtjUvVB.js → objective-cpp-Bz8bAq47.js} +0 -0
- /package/dist/web/assets/{ocaml-Bi6ZwqG4.js → ocaml-B9heAsc3.js} +0 -0
- /package/dist/web/assets/{odin-8xf1Y0S9.js → odin-BaFYnOwO.js} +0 -0
- /package/dist/web/assets/{one-dark-pro-hVv-_y_b.js → one-dark-pro-D6B_hSe-.js} +0 -0
- /package/dist/web/assets/{one-light-CJJ3MtHt.js → one-light-Ba-f0wyk.js} +0 -0
- /package/dist/web/assets/{openscad-B2HcywLk.js → openscad-UvYyL2MK.js} +0 -0
- /package/dist/web/assets/{pascal-C7fllV_7.js → pascal-B4-qvEoG.js} +0 -0
- /package/dist/web/assets/{pkl-qvLC1IWP.js → pkl-B146nqGx.js} +0 -0
- /package/dist/web/assets/{plastic-BT1wHZv2.js → plastic-L38t-Id3.js} +0 -0
- /package/dist/web/assets/{play-4MZi1Axp.js → play-CpCAoQRZ.js} +0 -0
- /package/dist/web/assets/{plsql-1MsmdmJD.js → plsql-ItOG5Cca.js} +0 -0
- /package/dist/web/assets/{plus-BhMZqFsN.js → plus-BR0qewJI.js} +0 -0
- /package/dist/web/assets/{po-BigskytD.js → po-imiCTaNX.js} +0 -0
- /package/dist/web/assets/{poimandres-DuklniF7.js → poimandres-Bu6sIba9.js} +0 -0
- /package/dist/web/assets/{polar-e2pjN_sp.js → polar-CTeGoFxV.js} +0 -0
- /package/dist/web/assets/{portal-container-context-Cm5HipFr.js → portal-container-context-a399Hd9F.js} +0 -0
- /package/dist/web/assets/{postcss-DtAeYjWw.js → postcss-jvFe6GNE.js} +0 -0
- /package/dist/web/assets/{powerquery-Cr2PtehI.js → powerquery-D9Dw1hJp.js} +0 -0
- /package/dist/web/assets/{powershell-BxFTGLJ4.js → powershell-CZqvYQtg.js} +0 -0
- /package/dist/web/assets/{prisma-COWJiwdZ.js → prisma-B4D2nO_7.js} +0 -0
- /package/dist/web/assets/{prolog-DFvGKRx6.js → prolog-CQbyUcCF.js} +0 -0
- /package/dist/web/assets/{proto-CcK80lFt.js → proto-Cwpo0cW1.js} +0 -0
- /package/dist/web/assets/{puppet-BF60bZ4F.js → puppet-CKxCwBod.js} +0 -0
- /package/dist/web/assets/{purescript-zL_69MzU.js → purescript-B-MyXMMp.js} +0 -0
- /package/dist/web/assets/{python-CIwKhfb_.js → python-D6QbSR2S.js} +0 -0
- /package/dist/web/assets/{qmldir-CXNe04Lo.js → qmldir-BpTSVcw_.js} +0 -0
- /package/dist/web/assets/{qss-C8lAWe6G.js → qss-DBc4gbpe.js} +0 -0
- /package/dist/web/assets/{r-CL5ZVL3_.js → r-i9jMMi0s.js} +0 -0
- /package/dist/web/assets/{racket-BBfh0uh0.js → racket-BWNaxU7C.js} +0 -0
- /package/dist/web/assets/{raku-o6E8R5Q3.js → raku-fqTZLn7L.js} +0 -0
- /package/dist/web/assets/{react-CBTnT4sY.js → react-X4jUntEf.js} +0 -0
- /package/dist/web/assets/{red-GomRF2Xp.js → red-D-JEcuzA.js} +0 -0
- /package/dist/web/assets/{refresh-cw-B06yaBrW.js → refresh-cw-B9iv7csb.js} +0 -0
- /package/dist/web/assets/{reg-_ffDIvx9.js → reg-BbirlU1S.js} +0 -0
- /package/dist/web/assets/{regexp-Bl5sF65W.js → regexp-D8VTFrtg.js} +0 -0
- /package/dist/web/assets/{rel-DgMdqDuj.js → rel-ByQGKiNM.js} +0 -0
- /package/dist/web/assets/{resolve-theme-CRt_7wpd.js → resolve-theme-Cqb9zWYc.js} +0 -0
- /package/dist/web/assets/{riscv-8b3-Ye4X.js → riscv-B5nqCfLE.js} +0 -0
- /package/dist/web/assets/{ron-DrmU1KHP.js → ron-DOVh_iZR.js} +0 -0
- /package/dist/web/assets/{rose-pine-dawn-DEfhNlFH.js → rose-pine-dawn-B7hkmmWy.js} +0 -0
- /package/dist/web/assets/{rose-pine-C_-E2tF1.js → rose-pine-jp51ZUzi.js} +0 -0
- /package/dist/web/assets/{rose-pine-moon-BdvX3gdn.js → rose-pine-moon-Ff063yLj.js} +0 -0
- /package/dist/web/assets/{rosmsg-B6GhGpd5.js → rosmsg-Cyk7Il8z.js} +0 -0
- /package/dist/web/assets/{rotate-ccw-COXu_9oj.js → rotate-ccw-DnuPzJRE.js} +0 -0
- /package/dist/web/assets/{rust-DmC97M1-.js → rust-DRNM5epF.js} +0 -0
- /package/dist/web/assets/{sass-B0tJOHLM.js → sass-D_cB0-M1.js} +0 -0
- /package/dist/web/assets/{save-CtIGOjmx.js → save-DxonD4y1.js} +0 -0
- /package/dist/web/assets/{scala-n8zaFOXv.js → scala-Crl-1kKS.js} +0 -0
- /package/dist/web/assets/{scheme-BKD9ztBU.js → scheme-DCaWyEJ0.js} +0 -0
- /package/dist/web/assets/{sdbl-D8gB5-rS.js → sdbl-BbXNqwtX.js} +0 -0
- /package/dist/web/assets/{send-BlyNJAzy.js → send-B-t5Mz26.js} +0 -0
- /package/dist/web/assets/{shallow-DYo6goJ4.js → shallow-4zAfs2KW.js} +0 -0
- /package/dist/web/assets/{shellscript-BfXlf_Ao.js → shellscript-BAgksBhW.js} +0 -0
- /package/dist/web/assets/{shield-check-B0iGUiOK.js → shield-check-CdTiK1j2.js} +0 -0
- /package/dist/web/assets/{shield-off-BNLWBhcq.js → shield-off-DuetkXKM.js} +0 -0
- /package/dist/web/assets/{slack-dark-CylEddse.js → slack-dark-GB-ElUW_.js} +0 -0
- /package/dist/web/assets/{slack-ochin-w8wg8vuC.js → slack-ochin-tU6CFD3S.js} +0 -0
- /package/dist/web/assets/{smalltalk-hiAXA64u.js → smalltalk-DSVGUbtL.js} +0 -0
- /package/dist/web/assets/{snazzy-light-BwlV3Xty.js → snazzy-light-DZhi3PoS.js} +0 -0
- /package/dist/web/assets/{solarized-dark-vmNGdykz.js → solarized-dark-D6AMCco7.js} +0 -0
- /package/dist/web/assets/{solarized-light-DGv_0JXq.js → solarized-light-DepuTWSE.js} +0 -0
- /package/dist/web/assets/{solidity-Y7QSH0B1.js → solidity-CsxN618l.js} +0 -0
- /package/dist/web/assets/{splunk-BtgpLjHi.js → splunk-CF1m0Kkv.js} +0 -0
- /package/dist/web/assets/{sql-CRHYhOia.js → sql-DvyMDgrB.js} +0 -0
- /package/dist/web/assets/{ssh-config-DOqSs3au.js → ssh-config-a8sDo-Q8.js} +0 -0
- /package/dist/web/assets/{stylus-crCAEaBT.js → stylus-DCJ-6AjT.js} +0 -0
- /package/dist/web/assets/{swift-eE3rT7TC.js → swift-c-ZFxyPP.js} +0 -0
- /package/dist/web/assets/{synthwave-84-BWy14LBX.js → synthwave-84-9NPgBcuv.js} +0 -0
- /package/dist/web/assets/{system-verilog-DC0-DZM4.js → system-verilog-DPD02IOo.js} +0 -0
- /package/dist/web/assets/{systemd-to5p6N8I.js → systemd-BUr5HRtU.js} +0 -0
- /package/dist/web/assets/{table-Bbc783ko.js → table-FLU8ei8b.js} +0 -0
- /package/dist/web/assets/{talonscript-DoTcU8wm.js → talonscript-BNBQV5YJ.js} +0 -0
- /package/dist/web/assets/{tasl-cNZvUs95.js → tasl-BETjpSyu.js} +0 -0
- /package/dist/web/assets/{tcl-mZE8sZH8.js → tcl-Clp_AZiC.js} +0 -0
- /package/dist/web/assets/{terraform-CqHPpIfu.js → terraform-B0kZ0lim.js} +0 -0
- /package/dist/web/assets/{text-wrap-CBT4Gxtg.js → text-wrap-B05VGRa-.js} +0 -0
- /package/dist/web/assets/{tokyo-night-Tp2rTIZd.js → tokyo-night-oOJ0a7Dk.js} +0 -0
- /package/dist/web/assets/{toml-CU_vscgn.js → toml-DOPCSumy.js} +0 -0
- /package/dist/web/assets/{trash-2-Bk4yYlck.js → trash-2-CTXlTflF.js} +0 -0
- /package/dist/web/assets/{triangle-alert-B8EYecpe.js → triangle-alert-BRq0VAfd.js} +0 -0
- /package/dist/web/assets/{tsv-ik7iFB9D.js → tsv-D6qUGf84.js} +0 -0
- /package/dist/web/assets/{tsx-BMvvMKiv.js → tsx-B6qYhT6O.js} +0 -0
- /package/dist/web/assets/{turtle-DBqKCq9o.js → turtle-C0Rjzw73.js} +0 -0
- /package/dist/web/assets/{typescript-DXJDmMZZ.js → typescript-BiydShEu.js} +0 -0
- /package/dist/web/assets/{typespec-CBseCMtZ.js → typespec-DjmF6Aww.js} +0 -0
- /package/dist/web/assets/{typst-4oY25U9y.js → typst-CPuscfcL.js} +0 -0
- /package/dist/web/assets/{use-is-mobile-D1weanbj.js → use-is-mobile-b94q8RvF.js} +0 -0
- /package/dist/web/assets/{utils-BNRH507E.js → utils-C9OjCrou.js} +0 -0
- /package/dist/web/assets/{v-BXaZ_1oy.js → v-DE4vpLLQ.js} +0 -0
- /package/dist/web/assets/{vala-BImWJE7z.js → vala-CmUF2pS5.js} +0 -0
- /package/dist/web/assets/{vb-CLfj2_sy.js → vb-BvcRoX4b.js} +0 -0
- /package/dist/web/assets/{vendor-xterm-_yyu5n32.js → vendor-xterm-K8kpLxdb.js} +0 -0
- /package/dist/web/assets/{verilog-BWa8Ed2b.js → verilog-BGIzyH3R.js} +0 -0
- /package/dist/web/assets/{vesper-BGj1cg6m.js → vesper-CG7IrHQQ.js} +0 -0
- /package/dist/web/assets/{vhdl-mByK7mlx.js → vhdl-DMSumomw.js} +0 -0
- /package/dist/web/assets/{viml-CugmlW5g.js → viml-Drt7AEmU.js} +0 -0
- /package/dist/web/assets/{vitesse-black-vPdpy9TL.js → vitesse-black-DmsjfN0b.js} +0 -0
- /package/dist/web/assets/{vitesse-dark-BuQK3dUh.js → vitesse-dark-Dl55_WeJ.js} +0 -0
- /package/dist/web/assets/{vitesse-light-B3nz1XL9.js → vitesse-light-Crbv9aGg.js} +0 -0
- /package/dist/web/assets/{vyper-DH_GTIHG.js → vyper-CB7psEJj.js} +0 -0
- /package/dist/web/assets/{wasm-DfVbVJlW.js → wasm-BY1tuM5m.js} +0 -0
- /package/dist/web/assets/{wasm-C-LF9GjX.js → wasm-Dck8KPgP.js} +0 -0
- /package/dist/web/assets/{wenyan-CzQkHyHx.js → wenyan-DhewQ68b.js} +0 -0
- /package/dist/web/assets/{wgsl-Du_M1DW9.js → wgsl-DlmiP456.js} +0 -0
- /package/dist/web/assets/{wikitext-BWN_w5S7.js → wikitext-cQCnqNgE.js} +0 -0
- /package/dist/web/assets/{wit-BR13v5HD.js → wit-S5MaCngB.js} +0 -0
- /package/dist/web/assets/{wolfram-BoXxp_Xl.js → wolfram-Bh8n_9zr.js} +0 -0
- /package/dist/web/assets/{x-nhriOX9o.js → x-YDu4m2Iq.js} +0 -0
- /package/dist/web/assets/{yaml-DpR_EF-d.js → yaml-BoK_17l9.js} +0 -0
- /package/dist/web/assets/{zenscript-C-EdUtUd.js → zenscript-DB3ZFwNJ.js} +0 -0
- /package/dist/web/assets/{zig-BD4eA5nU.js → zig-Bv1BWmfT.js} +0 -0
|
@@ -3,7 +3,7 @@ import { resolve } from "node:path";
|
|
|
3
3
|
import { mkdirSync, existsSync } from "node:fs";
|
|
4
4
|
import { encrypt, decrypt } from "../lib/account-crypto.ts";
|
|
5
5
|
import { getPpmDir } from "./ppm-dir.ts";
|
|
6
|
-
export const CURRENT_SCHEMA_VERSION =
|
|
6
|
+
export const CURRENT_SCHEMA_VERSION = 42;
|
|
7
7
|
|
|
8
8
|
let db: Database | null = null;
|
|
9
9
|
let dbProfile: string | null = null;
|
|
@@ -899,6 +899,34 @@ function runMigrations(database: Database): void {
|
|
|
899
899
|
try { database.exec("ALTER TABLE turn_usage ADD COLUMN account_label TEXT"); } catch { /* column exists */ }
|
|
900
900
|
database.exec("PRAGMA user_version = 41;");
|
|
901
901
|
}
|
|
902
|
+
|
|
903
|
+
if (current < 42) {
|
|
904
|
+
// Unread rows created before `incrementSessionUnread` learned the project name have
|
|
905
|
+
// `project_name IS NULL`. They could not heal on their own: the bell refuses to open a
|
|
906
|
+
// session it cannot place, so the connection that would have filled the name never
|
|
907
|
+
// happens, and the entry comes back on every reload. Fill them from the mapping that
|
|
908
|
+
// already knows, same shape as the v11 backfill above.
|
|
909
|
+
database.exec(`
|
|
910
|
+
UPDATE session_metadata SET
|
|
911
|
+
project_name = COALESCE(project_name, (
|
|
912
|
+
SELECT project_name FROM session_map
|
|
913
|
+
WHERE session_map.sdk_id = session_metadata.session_id
|
|
914
|
+
OR session_map.ppm_id = session_metadata.session_id
|
|
915
|
+
)),
|
|
916
|
+
project_path = COALESCE(project_path, (
|
|
917
|
+
SELECT project_path FROM session_map
|
|
918
|
+
WHERE session_map.sdk_id = session_metadata.session_id
|
|
919
|
+
OR session_map.ppm_id = session_metadata.session_id
|
|
920
|
+
))
|
|
921
|
+
WHERE project_name IS NULL OR project_path IS NULL
|
|
922
|
+
`);
|
|
923
|
+
database.exec(`
|
|
924
|
+
UPDATE session_metadata SET project_path = (
|
|
925
|
+
SELECT path FROM projects WHERE projects.name = session_metadata.project_name
|
|
926
|
+
) WHERE project_path IS NULL AND project_name IS NOT NULL
|
|
927
|
+
`);
|
|
928
|
+
database.exec("PRAGMA user_version = 42;");
|
|
929
|
+
}
|
|
902
930
|
}
|
|
903
931
|
|
|
904
932
|
// ---------------------------------------------------------------------------
|
|
@@ -1161,17 +1189,26 @@ export interface UnreadEntry {
|
|
|
1161
1189
|
sessionTitle: string | null;
|
|
1162
1190
|
}
|
|
1163
1191
|
|
|
1164
|
-
/** Increment unread count for a session and set the notification type + title snapshot
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1192
|
+
/** Increment unread count for a session and set the notification type + title snapshot.
|
|
1193
|
+
*
|
|
1194
|
+
* Row-creating, like {@link setSessionUnread}. A session PPM did not create — resumed
|
|
1195
|
+
* here, or started in the CLI — has no `session_metadata` row, and an UPDATE against a
|
|
1196
|
+
* missing row silently dropped the unread entry altogether.
|
|
1197
|
+
*
|
|
1198
|
+
* `project_name` is filled in the same statement so the bell can group and open the
|
|
1199
|
+
* session, and the COALESCE keeps the *stored* name: a name arriving later is not
|
|
1200
|
+
* evidence the session moved, and letting it win would let a stale name in a persisted
|
|
1201
|
+
* tab's URL overwrite a good one. */
|
|
1202
|
+
export function incrementSessionUnread(sessionId: string, type: string, title?: string | null, projectName?: string | null): void {
|
|
1203
|
+
getDb().query(
|
|
1204
|
+
`INSERT INTO session_metadata (session_id, unread_count, unread_type, last_known_title, project_name)
|
|
1205
|
+
VALUES (?, 1, ?, ?, ?)
|
|
1206
|
+
ON CONFLICT(session_id) DO UPDATE SET
|
|
1207
|
+
unread_count = session_metadata.unread_count + 1,
|
|
1208
|
+
unread_type = excluded.unread_type,
|
|
1209
|
+
last_known_title = COALESCE(excluded.last_known_title, session_metadata.last_known_title),
|
|
1210
|
+
project_name = COALESCE(session_metadata.project_name, excluded.project_name)`,
|
|
1211
|
+
).run(sessionId, type, title ?? null, projectName ?? null);
|
|
1175
1212
|
}
|
|
1176
1213
|
|
|
1177
1214
|
/** Manually mark a session unread (idempotent: SETS count to 1, not increment; row-creating).
|
|
@@ -1188,6 +1225,19 @@ export function setSessionUnread(sessionId: string, type: string, title?: string
|
|
|
1188
1225
|
).run(sessionId, type, title ?? null, projectName ?? null);
|
|
1189
1226
|
}
|
|
1190
1227
|
|
|
1228
|
+
/** Mark several sessions as read in one transaction.
|
|
1229
|
+
* Clearing is project-agnostic — the read route never looked at the project segment —
|
|
1230
|
+
* so this works for rows whose project is unknown or names a project that no longer
|
|
1231
|
+
* exists, which is exactly the set that could not be cleared before. */
|
|
1232
|
+
export function clearSessionUnreadMany(sessionIds: string[]): void {
|
|
1233
|
+
if (sessionIds.length === 0) return;
|
|
1234
|
+
const db = getDb();
|
|
1235
|
+
const stmt = db.query("UPDATE session_metadata SET unread_count = 0, unread_type = NULL WHERE session_id = ?");
|
|
1236
|
+
db.transaction(() => {
|
|
1237
|
+
for (const id of sessionIds) stmt.run(id);
|
|
1238
|
+
})();
|
|
1239
|
+
}
|
|
1240
|
+
|
|
1191
1241
|
/** Mark a session as read (reset unread count) */
|
|
1192
1242
|
export function clearSessionUnread(sessionId: string): void {
|
|
1193
1243
|
getDb().query(
|
|
@@ -1,191 +1,191 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Edge forwarder — owns the public port and pipes raw TCP to the server's
|
|
3
|
-
* current loopback port.
|
|
4
|
-
*
|
|
5
|
-
* ─── CRITICAL INVARIANT: THIS PROCESS MUST NEVER SPAWN A CHILD ───
|
|
6
|
-
*
|
|
7
|
-
* On Windows a child spawned with fd stdio inherits every inheritable handle,
|
|
8
|
-
* including a listening socket. When such a descendant is orphaned it keeps the
|
|
9
|
-
* socket open, so the port stays in LISTEN under a dead PID and can never be
|
|
10
|
-
* rebound — a "zombie port". The server hits this constantly because it spawns
|
|
11
|
-
* chat/tool/MCP subprocesses; those inherit its listener and wedge its port.
|
|
12
|
-
*
|
|
13
|
-
* The edge exists precisely because it spawns nothing, so its socket cannot be
|
|
14
|
-
* inherited and its port cannot zombie. cloudflared can therefore stay pinned
|
|
15
|
-
* to it forever and the public URL stops rotating. Adding any child-process
|
|
16
|
-
* call to this file reintroduces the exact bug the edge was built to remove.
|
|
17
|
-
*
|
|
18
|
-
* Raw TCP, never HTTP: WebSocket upgrades and SSE streams pass through
|
|
19
|
-
* untouched, and forwarded requests keep the headers cloudflared set.
|
|
20
|
-
*/
|
|
21
|
-
import net from "node:net";
|
|
22
|
-
import { resolveTargetPort, _resetTargetCache } from "./edge-target-resolver.ts";
|
|
23
|
-
|
|
24
|
-
/** Delay between attempts while waiting for the server to come back. */
|
|
25
|
-
const CONNECT_RETRY_MS = 250;
|
|
26
|
-
/**
|
|
27
|
-
* How long a client connection waits for a reachable server before giving up.
|
|
28
|
-
* During an upgrade the server is briefly absent; refusing instantly would
|
|
29
|
-
* surface as a broken page rather than a slow one.
|
|
30
|
-
*/
|
|
31
|
-
const CONNECT_WINDOW_MS = 5000;
|
|
32
|
-
|
|
33
|
-
const sleep = (ms: number) => new Promise((r) => setTimeout(r, ms));
|
|
34
|
-
|
|
35
|
-
/** Resolve once the socket is connected; reject on the first connect error. */
|
|
36
|
-
function connectUpstream(port: number): Promise<net.Socket> {
|
|
37
|
-
return new Promise((resolvePromise, reject) => {
|
|
38
|
-
const upstream = net.connect({
|
|
39
|
-
port,
|
|
40
|
-
host: "127.0.0.1", // never taken from status.json — loopback only
|
|
41
|
-
allowHalfOpen: true,
|
|
42
|
-
});
|
|
43
|
-
const onError = (err: Error) => {
|
|
44
|
-
upstream.destroy();
|
|
45
|
-
reject(err);
|
|
46
|
-
};
|
|
47
|
-
upstream.once("error", onError);
|
|
48
|
-
upstream.once("connect", () => {
|
|
49
|
-
upstream.removeListener("error", onError);
|
|
50
|
-
resolvePromise(upstream);
|
|
51
|
-
});
|
|
52
|
-
});
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
/**
|
|
56
|
-
* Largest amount of client data held while waiting for the server. Bounds the
|
|
57
|
-
* memory a slow upload can pin during an upgrade window.
|
|
58
|
-
*/
|
|
59
|
-
const MAX_PENDING_BYTES = 8 * 1024 * 1024;
|
|
60
|
-
|
|
61
|
-
/**
|
|
62
|
-
* Pipe one client connection to the server.
|
|
63
|
-
*
|
|
64
|
-
* Resolving the upstream is async, so the first bytes usually arrive before
|
|
65
|
-
* there is anywhere to send them. They must be captured by a `data` listener
|
|
66
|
-
* attached synchronously on this very tick and replayed once the upstream
|
|
67
|
-
* exists.
|
|
68
|
-
*
|
|
69
|
-
* `socket.pause()` is NOT a substitute — verified against Bun 1.3.13: bytes
|
|
70
|
-
* that arrive while paused are lost, and a later `pipe()`/`resume()` does not
|
|
71
|
-
* bring them back. A socket with no `data` listener drops them outright too.
|
|
72
|
-
* Both variants silently swallowed the first request in testing.
|
|
73
|
-
*/
|
|
74
|
-
async function handleConnection(
|
|
75
|
-
client: net.Socket,
|
|
76
|
-
connectWindowMs: number,
|
|
77
|
-
): Promise<void> {
|
|
78
|
-
const pending: Buffer[] = [];
|
|
79
|
-
let pendingBytes = 0;
|
|
80
|
-
let clientEnded = false;
|
|
81
|
-
const onEarlyData = (chunk: Buffer) => {
|
|
82
|
-
pendingBytes += chunk.length;
|
|
83
|
-
if (pendingBytes > MAX_PENDING_BYTES) {
|
|
84
|
-
client.destroy();
|
|
85
|
-
return;
|
|
86
|
-
}
|
|
87
|
-
pending.push(chunk);
|
|
88
|
-
};
|
|
89
|
-
client.on("data", onEarlyData);
|
|
90
|
-
client.once("end", () => { clientEnded = true; });
|
|
91
|
-
|
|
92
|
-
let upstream: net.Socket | null = null;
|
|
93
|
-
let clientGone = false;
|
|
94
|
-
|
|
95
|
-
// A socket error with no listener is an unhandled 'error' event, which would
|
|
96
|
-
// take the whole edge process — and the public port — down with it.
|
|
97
|
-
const teardown = () => {
|
|
98
|
-
upstream?.destroy();
|
|
99
|
-
client.destroy();
|
|
100
|
-
};
|
|
101
|
-
client.on("error", teardown);
|
|
102
|
-
client.once("close", () => { clientGone = true; });
|
|
103
|
-
|
|
104
|
-
const deadline = Date.now() + connectWindowMs;
|
|
105
|
-
while (!clientGone && Date.now() < deadline) {
|
|
106
|
-
const port = resolveTargetPort();
|
|
107
|
-
if (port !== null) {
|
|
108
|
-
try {
|
|
109
|
-
upstream = await connectUpstream(port);
|
|
110
|
-
break;
|
|
111
|
-
} catch {
|
|
112
|
-
// Server not up yet (or moved mid-restart) — re-resolve and retry.
|
|
113
|
-
_resetTargetCache();
|
|
114
|
-
}
|
|
115
|
-
}
|
|
116
|
-
await sleep(CONNECT_RETRY_MS);
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
if (clientGone) return;
|
|
120
|
-
if (!upstream) {
|
|
121
|
-
client.destroy();
|
|
122
|
-
return;
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
upstream.on("error", teardown);
|
|
126
|
-
|
|
127
|
-
// Replay what arrived during the connect, then hand both directions to the
|
|
128
|
-
// stream machinery. Removing the listener and piping happen in the same tick,
|
|
129
|
-
// so no chunk can slip through the gap.
|
|
130
|
-
client.off("data", onEarlyData);
|
|
131
|
-
for (const chunk of pending) upstream.write(chunk);
|
|
132
|
-
pending.length = 0;
|
|
133
|
-
|
|
134
|
-
client.pipe(upstream);
|
|
135
|
-
upstream.pipe(client);
|
|
136
|
-
|
|
137
|
-
// The client may have half-closed while we were still connecting.
|
|
138
|
-
if (clientEnded) upstream.end();
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
export interface EdgeForwarderOptions {
|
|
142
|
-
publicPort: number;
|
|
143
|
-
host?: string;
|
|
144
|
-
/** How long a client waits for a reachable server. Defaults to 5s. */
|
|
145
|
-
connectWindowMs?: number;
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
/**
|
|
149
|
-
* Bind the public port and start forwarding. Resolves once listening so the
|
|
150
|
-
* caller can fail fast when the port is unavailable.
|
|
151
|
-
*/
|
|
152
|
-
export function startEdgeForwarder(
|
|
153
|
-
opts: EdgeForwarderOptions,
|
|
154
|
-
): Promise<net.Server> {
|
|
155
|
-
const host = opts.host ?? "0.0.0.0";
|
|
156
|
-
const connectWindowMs = opts.connectWindowMs ?? CONNECT_WINDOW_MS;
|
|
157
|
-
return new Promise((resolvePromise, reject) => {
|
|
158
|
-
// allowHalfOpen keeps the other direction alive when one side sends FIN —
|
|
159
|
-
// long-lived chat WS and SSE streams depend on it.
|
|
160
|
-
const server = net.createServer({ allowHalfOpen: true }, (client) => {
|
|
161
|
-
void handleConnection(client, connectWindowMs);
|
|
162
|
-
});
|
|
163
|
-
server.once("error", reject);
|
|
164
|
-
server.listen(opts.publicPort, host, () => {
|
|
165
|
-
server.removeListener("error", reject);
|
|
166
|
-
resolvePromise(server);
|
|
167
|
-
});
|
|
168
|
-
});
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
// ─── Process entry ─────────────────────────────────────────────────────
|
|
172
|
-
// Mirrors the `__serve__` guard in src/server/index.ts: the supervisor
|
|
173
|
-
// re-invokes the binary as `<bin> __edge__ <publicPort> <host>`.
|
|
174
|
-
if (process.argv.includes("__edge__")) {
|
|
175
|
-
const idx = process.argv.indexOf("__edge__");
|
|
176
|
-
const publicPort = parseInt(process.argv[idx + 1] ?? "", 10);
|
|
177
|
-
const host = process.argv[idx + 2] ?? "0.0.0.0";
|
|
178
|
-
|
|
179
|
-
if (!Number.isInteger(publicPort) || publicPort <= 0 || publicPort > 65535) {
|
|
180
|
-
process.stderr.write(`[edge] Invalid public port: ${process.argv[idx + 1]}\n`);
|
|
181
|
-
process.exit(2);
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
try {
|
|
185
|
-
await startEdgeForwarder({ publicPort, host });
|
|
186
|
-
process.stderr.write(`[edge] Listening on ${host}:${publicPort}\n`);
|
|
187
|
-
} catch (err) {
|
|
188
|
-
process.stderr.write(`[edge] Failed to bind ${host}:${publicPort}: ${err}\n`);
|
|
189
|
-
process.exit(1);
|
|
190
|
-
}
|
|
191
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* Edge forwarder — owns the public port and pipes raw TCP to the server's
|
|
3
|
+
* current loopback port.
|
|
4
|
+
*
|
|
5
|
+
* ─── CRITICAL INVARIANT: THIS PROCESS MUST NEVER SPAWN A CHILD ───
|
|
6
|
+
*
|
|
7
|
+
* On Windows a child spawned with fd stdio inherits every inheritable handle,
|
|
8
|
+
* including a listening socket. When such a descendant is orphaned it keeps the
|
|
9
|
+
* socket open, so the port stays in LISTEN under a dead PID and can never be
|
|
10
|
+
* rebound — a "zombie port". The server hits this constantly because it spawns
|
|
11
|
+
* chat/tool/MCP subprocesses; those inherit its listener and wedge its port.
|
|
12
|
+
*
|
|
13
|
+
* The edge exists precisely because it spawns nothing, so its socket cannot be
|
|
14
|
+
* inherited and its port cannot zombie. cloudflared can therefore stay pinned
|
|
15
|
+
* to it forever and the public URL stops rotating. Adding any child-process
|
|
16
|
+
* call to this file reintroduces the exact bug the edge was built to remove.
|
|
17
|
+
*
|
|
18
|
+
* Raw TCP, never HTTP: WebSocket upgrades and SSE streams pass through
|
|
19
|
+
* untouched, and forwarded requests keep the headers cloudflared set.
|
|
20
|
+
*/
|
|
21
|
+
import net from "node:net";
|
|
22
|
+
import { resolveTargetPort, _resetTargetCache } from "./edge-target-resolver.ts";
|
|
23
|
+
|
|
24
|
+
/** Delay between attempts while waiting for the server to come back. */
|
|
25
|
+
const CONNECT_RETRY_MS = 250;
|
|
26
|
+
/**
|
|
27
|
+
* How long a client connection waits for a reachable server before giving up.
|
|
28
|
+
* During an upgrade the server is briefly absent; refusing instantly would
|
|
29
|
+
* surface as a broken page rather than a slow one.
|
|
30
|
+
*/
|
|
31
|
+
const CONNECT_WINDOW_MS = 5000;
|
|
32
|
+
|
|
33
|
+
const sleep = (ms: number) => new Promise((r) => setTimeout(r, ms));
|
|
34
|
+
|
|
35
|
+
/** Resolve once the socket is connected; reject on the first connect error. */
|
|
36
|
+
function connectUpstream(port: number): Promise<net.Socket> {
|
|
37
|
+
return new Promise((resolvePromise, reject) => {
|
|
38
|
+
const upstream = net.connect({
|
|
39
|
+
port,
|
|
40
|
+
host: "127.0.0.1", // never taken from status.json — loopback only
|
|
41
|
+
allowHalfOpen: true,
|
|
42
|
+
});
|
|
43
|
+
const onError = (err: Error) => {
|
|
44
|
+
upstream.destroy();
|
|
45
|
+
reject(err);
|
|
46
|
+
};
|
|
47
|
+
upstream.once("error", onError);
|
|
48
|
+
upstream.once("connect", () => {
|
|
49
|
+
upstream.removeListener("error", onError);
|
|
50
|
+
resolvePromise(upstream);
|
|
51
|
+
});
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Largest amount of client data held while waiting for the server. Bounds the
|
|
57
|
+
* memory a slow upload can pin during an upgrade window.
|
|
58
|
+
*/
|
|
59
|
+
const MAX_PENDING_BYTES = 8 * 1024 * 1024;
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Pipe one client connection to the server.
|
|
63
|
+
*
|
|
64
|
+
* Resolving the upstream is async, so the first bytes usually arrive before
|
|
65
|
+
* there is anywhere to send them. They must be captured by a `data` listener
|
|
66
|
+
* attached synchronously on this very tick and replayed once the upstream
|
|
67
|
+
* exists.
|
|
68
|
+
*
|
|
69
|
+
* `socket.pause()` is NOT a substitute — verified against Bun 1.3.13: bytes
|
|
70
|
+
* that arrive while paused are lost, and a later `pipe()`/`resume()` does not
|
|
71
|
+
* bring them back. A socket with no `data` listener drops them outright too.
|
|
72
|
+
* Both variants silently swallowed the first request in testing.
|
|
73
|
+
*/
|
|
74
|
+
async function handleConnection(
|
|
75
|
+
client: net.Socket,
|
|
76
|
+
connectWindowMs: number,
|
|
77
|
+
): Promise<void> {
|
|
78
|
+
const pending: Buffer[] = [];
|
|
79
|
+
let pendingBytes = 0;
|
|
80
|
+
let clientEnded = false;
|
|
81
|
+
const onEarlyData = (chunk: Buffer) => {
|
|
82
|
+
pendingBytes += chunk.length;
|
|
83
|
+
if (pendingBytes > MAX_PENDING_BYTES) {
|
|
84
|
+
client.destroy();
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
pending.push(chunk);
|
|
88
|
+
};
|
|
89
|
+
client.on("data", onEarlyData);
|
|
90
|
+
client.once("end", () => { clientEnded = true; });
|
|
91
|
+
|
|
92
|
+
let upstream: net.Socket | null = null;
|
|
93
|
+
let clientGone = false;
|
|
94
|
+
|
|
95
|
+
// A socket error with no listener is an unhandled 'error' event, which would
|
|
96
|
+
// take the whole edge process — and the public port — down with it.
|
|
97
|
+
const teardown = () => {
|
|
98
|
+
upstream?.destroy();
|
|
99
|
+
client.destroy();
|
|
100
|
+
};
|
|
101
|
+
client.on("error", teardown);
|
|
102
|
+
client.once("close", () => { clientGone = true; });
|
|
103
|
+
|
|
104
|
+
const deadline = Date.now() + connectWindowMs;
|
|
105
|
+
while (!clientGone && Date.now() < deadline) {
|
|
106
|
+
const port = resolveTargetPort();
|
|
107
|
+
if (port !== null) {
|
|
108
|
+
try {
|
|
109
|
+
upstream = await connectUpstream(port);
|
|
110
|
+
break;
|
|
111
|
+
} catch {
|
|
112
|
+
// Server not up yet (or moved mid-restart) — re-resolve and retry.
|
|
113
|
+
_resetTargetCache();
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
await sleep(CONNECT_RETRY_MS);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
if (clientGone) return;
|
|
120
|
+
if (!upstream) {
|
|
121
|
+
client.destroy();
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
upstream.on("error", teardown);
|
|
126
|
+
|
|
127
|
+
// Replay what arrived during the connect, then hand both directions to the
|
|
128
|
+
// stream machinery. Removing the listener and piping happen in the same tick,
|
|
129
|
+
// so no chunk can slip through the gap.
|
|
130
|
+
client.off("data", onEarlyData);
|
|
131
|
+
for (const chunk of pending) upstream.write(chunk);
|
|
132
|
+
pending.length = 0;
|
|
133
|
+
|
|
134
|
+
client.pipe(upstream);
|
|
135
|
+
upstream.pipe(client);
|
|
136
|
+
|
|
137
|
+
// The client may have half-closed while we were still connecting.
|
|
138
|
+
if (clientEnded) upstream.end();
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
export interface EdgeForwarderOptions {
|
|
142
|
+
publicPort: number;
|
|
143
|
+
host?: string;
|
|
144
|
+
/** How long a client waits for a reachable server. Defaults to 5s. */
|
|
145
|
+
connectWindowMs?: number;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* Bind the public port and start forwarding. Resolves once listening so the
|
|
150
|
+
* caller can fail fast when the port is unavailable.
|
|
151
|
+
*/
|
|
152
|
+
export function startEdgeForwarder(
|
|
153
|
+
opts: EdgeForwarderOptions,
|
|
154
|
+
): Promise<net.Server> {
|
|
155
|
+
const host = opts.host ?? "0.0.0.0";
|
|
156
|
+
const connectWindowMs = opts.connectWindowMs ?? CONNECT_WINDOW_MS;
|
|
157
|
+
return new Promise((resolvePromise, reject) => {
|
|
158
|
+
// allowHalfOpen keeps the other direction alive when one side sends FIN —
|
|
159
|
+
// long-lived chat WS and SSE streams depend on it.
|
|
160
|
+
const server = net.createServer({ allowHalfOpen: true }, (client) => {
|
|
161
|
+
void handleConnection(client, connectWindowMs);
|
|
162
|
+
});
|
|
163
|
+
server.once("error", reject);
|
|
164
|
+
server.listen(opts.publicPort, host, () => {
|
|
165
|
+
server.removeListener("error", reject);
|
|
166
|
+
resolvePromise(server);
|
|
167
|
+
});
|
|
168
|
+
});
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
// ─── Process entry ─────────────────────────────────────────────────────
|
|
172
|
+
// Mirrors the `__serve__` guard in src/server/index.ts: the supervisor
|
|
173
|
+
// re-invokes the binary as `<bin> __edge__ <publicPort> <host>`.
|
|
174
|
+
if (process.argv.includes("__edge__")) {
|
|
175
|
+
const idx = process.argv.indexOf("__edge__");
|
|
176
|
+
const publicPort = parseInt(process.argv[idx + 1] ?? "", 10);
|
|
177
|
+
const host = process.argv[idx + 2] ?? "0.0.0.0";
|
|
178
|
+
|
|
179
|
+
if (!Number.isInteger(publicPort) || publicPort <= 0 || publicPort > 65535) {
|
|
180
|
+
process.stderr.write(`[edge] Invalid public port: ${process.argv[idx + 1]}\n`);
|
|
181
|
+
process.exit(2);
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
try {
|
|
185
|
+
await startEdgeForwarder({ publicPort, host });
|
|
186
|
+
process.stderr.write(`[edge] Listening on ${host}:${publicPort}\n`);
|
|
187
|
+
} catch (err) {
|
|
188
|
+
process.stderr.write(`[edge] Failed to bind ${host}:${publicPort}: ${err}\n`);
|
|
189
|
+
process.exit(1);
|
|
190
|
+
}
|
|
191
|
+
}
|
|
@@ -8,6 +8,7 @@ import type { RpcChannel } from "./extension-rpc.ts";
|
|
|
8
8
|
import { contributionRegistry } from "./contribution-registry.ts";
|
|
9
9
|
import { broadcastExtMsg, requestFromBrowser } from "../server/ws/extensions.ts";
|
|
10
10
|
import { getProjects } from "./db.service.ts";
|
|
11
|
+
import { isSecretConfigKey, redactSecretConfigValue } from "./config-secret-keys.ts";
|
|
11
12
|
|
|
12
13
|
let requestIdCounter = 0;
|
|
13
14
|
function nextRequestId(): string {
|
|
@@ -153,11 +154,18 @@ export function registerVscodeCompatHandlers(rpc: RpcChannel): void {
|
|
|
153
154
|
// --- workspace config ---
|
|
154
155
|
rpc.onRequest("workspace:config:get", async (params) => {
|
|
155
156
|
const [key] = params as [string];
|
|
157
|
+
// An extension gets no signal beyond "not present" for a secret key —
|
|
158
|
+
// same not-found shape as a key that genuinely does not exist.
|
|
159
|
+
if (isSecretConfigKey(key)) return null;
|
|
156
160
|
// Read from PPM config service
|
|
157
161
|
try {
|
|
158
162
|
const { configService } = await import("./config.service.ts");
|
|
159
163
|
const config = configService.getAll() as unknown as Record<string, unknown>;
|
|
160
|
-
|
|
164
|
+
const value = getNestedValue(config, key) ?? null;
|
|
165
|
+
// A request for an ANCESTOR of a secret key (e.g. "tunnel", "auth")
|
|
166
|
+
// is not caught by isSecretConfigKey above and still holds the raw
|
|
167
|
+
// object here, so deep-redact before it crosses the RPC boundary.
|
|
168
|
+
return value === null ? null : redactSecretConfigValue(key, value);
|
|
161
169
|
} catch {
|
|
162
170
|
return null;
|
|
163
171
|
}
|
|
@@ -8,23 +8,50 @@ import { RecreatedDirPoller } from "./recreated-dir-poller.ts";
|
|
|
8
8
|
* near the number of *interesting* ones.
|
|
9
9
|
*
|
|
10
10
|
* `fs.watch(root, { recursive: true })` is a single call, but on Linux the runtime
|
|
11
|
-
* expands it into one inotify watch per
|
|
12
|
-
* Filtering those events on arrival (what this
|
|
13
|
-
* full cost, which is how a handful of projects
|
|
14
|
-
* the machine against `fs.inotify.max_user_watches`.
|
|
11
|
+
* expands it into one inotify watch per *entry* — every file as well as every
|
|
12
|
+
* directory, dependency trees included. Filtering those events on arrival (what this
|
|
13
|
+
* service used to do) still pays the full cost, which is how a handful of projects
|
|
14
|
+
* reached ~360k watches and pushed the machine against `fs.inotify.max_user_watches`.
|
|
15
15
|
*
|
|
16
|
-
* So coverage is chosen up front
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
16
|
+
* So coverage is chosen up front, and the shape depends on what the platform's
|
|
17
|
+
* recursive watch actually is.
|
|
18
|
+
*
|
|
19
|
+
* On Windows and macOS it is a kernel-side subtree watch (`ReadDirectoryChangesW`,
|
|
20
|
+
* FSEvents): one handle covers a whole subtree, it tracks directories created later,
|
|
21
|
+
* and it does not traverse reparse points. Measured on Windows 11 / Bun 1.3.10 over a
|
|
22
|
+
* 2,441-directory tree, one recursive handle attaches in 80ms for +7.0MB where one
|
|
23
|
+
* handle per directory takes 965ms for +25.2MB. So a subtree with no ignored directory
|
|
24
|
+
* inside it gets a single recursive watch there.
|
|
25
|
+
*
|
|
26
|
+
* On Linux there is no such call. Bun emulates it by walking the tree and opening a
|
|
27
|
+
* watch per entry, and that cost does not depend on the shape: measured over 4
|
|
28
|
+
* directories holding 600 files, one recursive handle and one non-recursive handle per
|
|
29
|
+
* directory both come to 604 descriptors on a single inotify instance. So this split
|
|
30
|
+
* buys nothing in inotify terms — the handle count was never what `max_user_instances`
|
|
31
|
+
* counts — and it is not why Linux takes a handle per directory.
|
|
32
|
+
*
|
|
33
|
+
* What it buys is coverage. The emulation differs from this scan in two ways: it does
|
|
34
|
+
* not pick up directories created after it attached, and it walks through symlinks this
|
|
35
|
+
* scan deliberately skips — a pnpm workspace with a symlinked `node_modules` had its
|
|
36
|
+
* whole store watched, one open descriptor per file, which exhausted the process at
|
|
37
|
+
* ~724k descriptors.
|
|
38
|
+
*
|
|
39
|
+
* Either way the walk below is what prunes ~92% of the directories in a typical repo,
|
|
40
|
+
* and an ignored branch is never handed to the runtime.
|
|
23
41
|
*/
|
|
24
42
|
|
|
25
43
|
/** Coalesce a burst of directory churn into a single subtree rebuild. */
|
|
26
44
|
const REBUILD_DEBOUNCE_MS = 500;
|
|
27
45
|
|
|
46
|
+
/**
|
|
47
|
+
* Whether `{ recursive: true }` is a real subtree watch rather than a per-directory walk.
|
|
48
|
+
* Named platforms rather than `!== "linux"`, so a runtime nobody has measured here —
|
|
49
|
+
* freebsd, say — takes the conservative walk instead of inheriting a promise about
|
|
50
|
+
* `ReadDirectoryChangesW`. `RecreatedDirPoller` is derived from this for the same reason:
|
|
51
|
+
* the emulation is a different mechanism with different defects.
|
|
52
|
+
*/
|
|
53
|
+
const NATIVE_RECURSIVE = process.platform === "win32" || process.platform === "darwin";
|
|
54
|
+
|
|
28
55
|
export interface WatchTreeOptions {
|
|
29
56
|
/** Absolute path of the directory to watch. */
|
|
30
57
|
root: string;
|
|
@@ -81,7 +108,10 @@ export class WatchTree {
|
|
|
81
108
|
constructor(private readonly options: WatchTreeOptions) {
|
|
82
109
|
// Only Bun on Linux has the stale-watch defect; elsewhere re-watching works
|
|
83
110
|
// and paying for polling would be pure waste.
|
|
84
|
-
|
|
111
|
+
// Tied to NATIVE_RECURSIVE rather than re-testing the platform: the poller stands in
|
|
112
|
+
// for a defect of the same emulation, and `attach()` relies on exactly one of the two
|
|
113
|
+
// being in play. Deriving it here keeps that from drifting apart.
|
|
114
|
+
this.poller = !NATIVE_RECURSIVE
|
|
85
115
|
? new RecreatedDirPoller({ onChange: (abs) => this.reportAbs(abs) })
|
|
86
116
|
: null;
|
|
87
117
|
}
|
|
@@ -135,13 +165,23 @@ export class WatchTree {
|
|
|
135
165
|
}
|
|
136
166
|
|
|
137
167
|
for (const entry of entries) {
|
|
138
|
-
//
|
|
139
|
-
//
|
|
140
|
-
|
|
141
|
-
|
|
168
|
+
// A symlink named like an ignored directory still has to mark the subtree, or an
|
|
169
|
+
// ancestor takes a recursive watch over a `node_modules` this walk never entered —
|
|
170
|
+
// which is how a pnpm store got watched through the link. Tested before
|
|
171
|
+
// isDirectory(), which is false for symlinks. Plain files are deliberately left out:
|
|
172
|
+
// IGNORED_DIRS holds names like `build`, `out` and `target`, and a compiled binary
|
|
173
|
+
// called `build` is no reason to cost everything around it its recursive coverage.
|
|
174
|
+
// A symlink to a *file* is caught here too, since telling it from a link to a
|
|
175
|
+
// directory needs a stat() the walk would otherwise never make. It holds no subtree,
|
|
176
|
+
// so marking it only forfeits recursive coverage — the safe direction, and it takes
|
|
177
|
+
// a file named exactly `node_modules` or `target` to happen at all.
|
|
178
|
+
if (isIgnoredDirName(entry.name) && (entry.isDirectory() || entry.isSymbolicLink())) {
|
|
142
179
|
node.hasIgnored = true;
|
|
143
180
|
continue;
|
|
144
181
|
}
|
|
182
|
+
// isDirectory() is false for symlinks, which keeps link cycles and
|
|
183
|
+
// duplicate coverage (pnpm stores, workspace links) out of the walk.
|
|
184
|
+
if (!entry.isDirectory()) continue;
|
|
145
185
|
if (budget.left <= 0) {
|
|
146
186
|
// Unscanned directories remain: mark the node so no ancestor covers them
|
|
147
187
|
// recursively, since we cannot know what they hold.
|
|
@@ -161,23 +201,17 @@ export class WatchTree {
|
|
|
161
201
|
private attach(node: ScanNode): void {
|
|
162
202
|
const fitsWholeSubtree = this.covered + node.size <= this.options.maxDirs;
|
|
163
203
|
|
|
164
|
-
// A path we have watched before is being re-attached, so this directory was
|
|
165
|
-
//
|
|
166
|
-
//
|
|
167
|
-
//
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
for (const child of node.dirs) this.attach(child);
|
|
176
|
-
return;
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
// One recursive watch for a subtree the runtime can safely expand on its own.
|
|
180
|
-
if (!node.hasIgnored && fitsWholeSubtree && this.addWatcher(node.path, true, node.size)) return;
|
|
204
|
+
// A path we have watched before is being re-attached, so this directory was deleted
|
|
205
|
+
// and recreated, and on Bun + Linux its watcher will never fire again — poll its own
|
|
206
|
+
// entries instead. It needs no special coverage beyond that: the poller only exists
|
|
207
|
+
// where the recursive branch below is off, so the per-directory walk it falls through
|
|
208
|
+
// to is already the watch-on-a-path-the-runtime-still-honours this case wants.
|
|
209
|
+
if (this.poller && this.everAttached.has(node.path)) this.poller.add(node.path);
|
|
210
|
+
|
|
211
|
+
// One recursive watch for a subtree the runtime can safely expand on its own —
|
|
212
|
+
// only where that is a kernel-side subtree watch rather than a per-directory walk.
|
|
213
|
+
if (NATIVE_RECURSIVE && !node.hasIgnored && fitsWholeSubtree
|
|
214
|
+
&& this.addWatcher(node.path, true, node.size)) return;
|
|
181
215
|
|
|
182
216
|
// Otherwise cover this directory alone and recurse — a failed recursive attach
|
|
183
217
|
// falls through to here too, so one unwatchable directory cannot silently drop
|