sliccy 6.211.0 → 6.212.0
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/dist/node-server/_shared/index.d.ts +1 -0
- package/dist/node-server/_shared/index.d.ts.map +1 -1
- package/dist/node-server/_shared/index.js +1 -0
- package/dist/node-server/_shared/index.js.map +1 -1
- package/dist/node-server/_shared/raw-fetch-protocol.d.ts +149 -0
- package/dist/node-server/_shared/raw-fetch-protocol.d.ts.map +1 -0
- package/dist/node-server/_shared/raw-fetch-protocol.js +296 -0
- package/dist/node-server/_shared/raw-fetch-protocol.js.map +1 -0
- package/dist/node-server/fetch-proxy-headers.d.ts +3 -1
- package/dist/node-server/fetch-proxy-headers.js +5 -1
- package/dist/node-server/index.js +14 -3
- package/dist/node-server/routes/fetch-proxy-raw.d.ts +43 -0
- package/dist/node-server/routes/fetch-proxy-raw.js +205 -0
- package/dist/node-server/routes/fetch-proxy.d.ts +23 -0
- package/dist/node-server/routes/fetch-proxy.js +4 -3
- package/dist/ui/.vite/manifest.json +182 -182
- package/dist/ui/assets/SKILL-CN4LOg1h.js +1 -0
- package/dist/ui/assets/{account-store-CwNcf5Wq.js → account-store-DdclMpWp.js} +1 -1
- package/dist/ui/assets/{account-store-nlKbTUhJ.js → account-store-DnIs5WZM.js} +1 -1
- package/dist/ui/assets/{adobe-DBVa9oOG.js → adobe-BNRJjQn6.js} +1 -1
- package/dist/ui/assets/{adobe-D7sqH2X_.js → adobe-w_RhhSWv.js} +1 -1
- package/dist/ui/assets/{agent-panels-B0VuA3mU.js → agent-panels-B-6e5X0F.js} +1 -1
- package/dist/ui/assets/{almost-bash-shell-headless-C0x2DyFh.js → almost-bash-shell-headless-D19kpvHY.js} +154 -149
- package/dist/ui/assets/{apply-layout-fciODM2t.js → apply-layout-oUk89jk1.js} +1 -1
- package/dist/ui/assets/{apps-RVxpwEfn.js → apps-oJ_nTgiK.js} +1 -1
- package/dist/ui/assets/{azure-openai-QS8TU-7m.js → azure-openai-BKBBD2Dq.js} +1 -1
- package/dist/ui/assets/{azure-openai-JxdRj-Iq.js → azure-openai-zqUyJv0c.js} +1 -1
- package/dist/ui/assets/{backend-hostfs-CITljQVo.js → backend-hostfs-BotdHD_S.js} +1 -1
- package/dist/ui/assets/{bsh-watchdog-BTB0exmo.js → bsh-watchdog-TYuawE-B.js} +1 -1
- package/dist/ui/assets/{cdp-Djm-AVXd.js → cdp-C0InCdRQ.js} +1 -1
- package/dist/ui/assets/{cerebras-DvIFsdoW.js → cerebras-ChWjfEi0.js} +1 -1
- package/dist/ui/assets/{cerebras-Czq0QoYg.js → cerebras-DL4U9Vjg.js} +1 -1
- package/dist/ui/assets/{client-CwxmlQM-.js → client-dISfU5Wf.js} +1 -1
- package/dist/ui/assets/{composer-speech-DgLXFFNQ.js → composer-speech-d-ejBNSK.js} +1 -1
- package/dist/ui/assets/{connect-surface-YjzKwUub.js → connect-surface-DNUrGg9g.js} +1 -1
- package/dist/ui/assets/{core-C7UGykIC.js → core-DZ0CleVD.js} +2 -2
- package/dist/ui/assets/{directed-approval-BMnSWjrJ.js → directed-approval-ol0Z6v9I.js} +1 -1
- package/dist/ui/assets/{emscripten-vfs-hook-CwbQLP-I.js → emscripten-vfs-hook-fCUbQRCb.js} +1 -1
- package/dist/ui/assets/{ensure-speech-assets-Bqf-swhU.js → ensure-speech-assets-S71yPbdv.js} +1 -1
- package/dist/ui/assets/{esbuild-wasm-DIcMVR3g.js → esbuild-wasm-llEEkCQu.js} +1 -1
- package/dist/ui/assets/{extension-ops-CF2mLDRW.js → extension-ops-CEprwZyb.js} +1 -1
- package/dist/ui/assets/{ffmpeg-wasm-B5jofhIa.js → ffmpeg-wasm-DGDKCZ27.js} +1 -1
- package/dist/ui/assets/{frozen-archive-writer-BV51qDTg.js → frozen-archive-writer-khicEqGd.js} +1 -1
- package/dist/ui/assets/{fs-ZnsLbva1.js → fs-CIR1p_m7.js} +1 -1
- package/dist/ui/assets/{fs-pBJBtnUm.js → fs-D6QJU-ZE.js} +2 -2
- package/dist/ui/assets/{gelatiere-store-B-PMFWOJ.js → gelatiere-store-CYdUzk1p.js} +1 -1
- package/dist/ui/assets/{gelatiere-unit-B7YUbTOe.js → gelatiere-unit-Dzkphhv5.js} +1 -1
- package/dist/ui/assets/{github-CRKKUq6c.js → github-8QTzsnR3.js} +1 -1
- package/dist/ui/assets/{github-q7aLpavT.js → github-BdQ6NNi6.js} +2 -2
- package/dist/ui/assets/{github-auth-DPBopdHy.js → github-auth-CgSRRmdA.js} +1 -1
- package/dist/ui/assets/{github-copilot-Cn5WeEVQ.js → github-copilot-Beq3TkEz.js} +1 -1
- package/dist/ui/assets/{github-copilot-BOqjn_pA.js → github-copilot-C6ZXCgIT.js} +1 -1
- package/dist/ui/assets/{github-install-Dlx1x9oP.js → github-install-BV6F_EyI.js} +1 -1
- package/dist/ui/assets/{github-zip-BKesEE7T.js → github-zip-nn7aW2E1.js} +1 -1
- package/dist/ui/assets/{guest-tool-gate-DbuM_BFG.js → guest-tool-gate-4cZHoDbf.js} +1 -1
- package/dist/ui/assets/{hear-BAlAHUln.js → hear-HND90jpV.js} +1 -1
- package/dist/ui/assets/{help-MPD6QjSi.js → help-Dgt88ySg.js} +1 -1
- package/dist/ui/assets/{installer-DR7izkMG.js → installer-BdG_mJUp.js} +1 -1
- package/dist/ui/assets/{interrupted-work-recovery-DKlfO3Dk.js → interrupted-work-recovery-niFgJlYA.js} +1 -1
- package/dist/ui/assets/{ipk-mamba-B7DsVAHB.js → ipk-mamba-DMPz0Na-.js} +1 -1
- package/dist/ui/assets/{js-realm-worker-Dj6E7cem.js → js-realm-worker-Dt6CkC-q.js} +2 -2
- package/dist/ui/assets/{jsh-executor-DOTwZy5W.js → jsh-executor-m_BRqJJW.js} +2 -2
- package/dist/ui/assets/{kernel-worker-C6evRbt8.js → kernel-worker-Bejt-Ojz.js} +10 -10
- package/dist/ui/assets/{lick-ws-bridge-DEO3vHuX.js → lick-ws-bridge-DoT1E5ir.js} +1 -1
- package/dist/ui/assets/{lick-ws-bridge-cpYoem5_.js → lick-ws-bridge-Nul1wz09.js} +1 -1
- package/dist/ui/assets/{live-session-curation-BM8tZX-r.js → live-session-curation-BKsScSRB.js} +1 -1
- package/dist/ui/assets/{live-session-snapshot-C2NMVRMF.js → live-session-snapshot-6V9mPUM5.js} +1 -1
- package/dist/ui/assets/{local-llm-Cfh9VuPq.js → local-llm-C0NdLczW.js} +1 -1
- package/dist/ui/assets/{local-llm-z-cBJlKf.js → local-llm-DoZ2LiSE.js} +1 -1
- package/dist/ui/assets/{magick-wasm-agWGi-e0.js → magick-wasm-BL5NYmcE.js} +1 -1
- package/dist/ui/assets/{main-tEtVPbXh.js → main-DmLxMDGT.js} +3 -3
- package/dist/ui/assets/{main-cherry-CbQUCau8.js → main-cherry-C3pK2VKW.js} +1 -1
- package/dist/ui/assets/{memory-curation-seam-CjBglHNa.js → memory-curation-seam-B_5OPYdP.js} +1 -1
- package/dist/ui/assets/{memory-health-CPzWzT1P.js → memory-health-CRH2Blzq.js} +1 -1
- package/dist/ui/assets/{memory-write-execute-DqWqBfwf.js → memory-write-execute-BfCExMYN.js} +1 -1
- package/dist/ui/assets/{merge-folded-cost-into-frozen-DCuPhNTk.js → merge-folded-cost-into-frozen-CoL4Bfzs.js} +1 -1
- package/dist/ui/assets/{mount-ydk0WHgG.js → mount-B3v77n4z.js} +3 -3
- package/dist/ui/assets/{mount-4dZRjkrQ.js → mount-TnWJttSG.js} +1 -1
- package/dist/ui/assets/{mount-bridge-client-Dz_UHbPE.js → mount-bridge-client-DkrmF1xh.js} +1 -1
- package/dist/ui/assets/{new-session-C5MEP_cK.js → new-session-C8MNLQeR.js} +2 -2
- package/dist/ui/assets/{oauth-bootstrap-BVUK3GEs.js → oauth-bootstrap-BW3jawLe.js} +2 -2
- package/dist/ui/assets/{oauth-service-DP3Rx6V_.js → oauth-service-B_A2Hp66.js} +1 -1
- package/dist/ui/assets/{onboarding-orchestrator-DftSEsKa.js → onboarding-orchestrator-Ca5D3wWS.js} +1 -1
- package/dist/ui/assets/{openai-codex-fJtQGV69.js → openai-codex-B-c0ZWNV.js} +1 -1
- package/dist/ui/assets/{openai-codex-Cwz1FT-z.js → openai-codex-CMsdDz1G.js} +1 -1
- package/dist/ui/assets/{openrouter-rcqoBXaw.js → openrouter-B7Sm8SVp.js} +1 -1
- package/dist/ui/assets/{openrouter-DmEz5WxK.js → openrouter-ChF-ckvt.js} +1 -1
- package/dist/ui/assets/{openrouter-free-C5O97xvy.js → openrouter-free-D6PoJTox.js} +1 -1
- package/dist/ui/assets/{openrouter-free-DRw-DHQn.js → openrouter-free-F9UehDw-.js} +1 -1
- package/dist/ui/assets/{openrouter-oauth-Csq32wt2.js → openrouter-oauth-CWDz3gd8.js} +1 -1
- package/dist/ui/assets/{openrouter-oauth-C4uIzDcA.js → openrouter-oauth-ClC2jnRu.js} +1 -1
- package/dist/ui/assets/{opfs-mount-C0Lus0Cz.js → opfs-mount-mkZhMkc1.js} +1 -1
- package/dist/ui/assets/{panel-rpc-handlers-B9LenTR0.js → panel-rpc-handlers-BGopQTZk.js} +2 -2
- package/dist/ui/assets/{panelize-shell-Dzogbbfc.js → panelize-shell-RRJ2rlAy.js} +1 -1
- package/dist/ui/assets/{pi-edit-execution-D9D4FDIQ.js → pi-edit-execution-Cn6P8kEd.js} +1 -1
- package/dist/ui/assets/{port-bridge-client-B-l7cQTC.js → port-bridge-client-DvO4atpq.js} +1 -1
- package/dist/ui/assets/{provider-CTPdODd7.js → provider-BkkOFpKq.js} +1 -1
- package/dist/ui/assets/{provider-Do-gc4OB.js → provider-CFbeLwlx.js} +2 -2
- package/dist/ui/assets/{provider-settings-DzDdkFMx.js → provider-settings-Df1UWum6.js} +1 -1
- package/dist/ui/assets/provider-store-access-440PUQnq.js +1 -0
- package/dist/ui/assets/provider-store-access-ioP6z6ei.js +1 -0
- package/dist/ui/assets/{providers-1_CCXdq3.js → providers-DelYSiJM.js} +2 -2
- package/dist/ui/assets/{providers-BdG-aC29.js → providers-narfgv33.js} +1 -1
- package/dist/ui/assets/proxied-fetch-Ba8A2Dlh.js +1 -0
- package/dist/ui/assets/proxied-fetch-raw-CzaxeyeU.js +1 -0
- package/dist/ui/assets/proxy-error-BVzVNPvv.js +1 -0
- package/dist/ui/assets/{quick-llm-B07QXQ0s.js → quick-llm-t5wZbj0G.js} +1 -1
- package/dist/ui/assets/{realm-inprocess-wC9VuVQf.js → realm-inprocess-DIZ0NkAs.js} +2 -2
- package/dist/ui/assets/{realm-unhandled-rejections-CEAHL08q.js → realm-unhandled-rejections-BIyM8soC.js} +1 -1
- package/dist/ui/assets/{realm-unhandled-rejections-CuAh-NpD.js → realm-unhandled-rejections-DzT6eKFi.js} +1 -1
- package/dist/ui/assets/{recovery-screen-mPjIBgPH.js → recovery-screen-DvZoe1eu.js} +1 -1
- package/dist/ui/assets/{redirect-uri-CBCnYKsl.js → redirect-uri-DvNqGm3U.js} +1 -1
- package/dist/ui/assets/{remote-terminal-view-DTmUlQlW.js → remote-terminal-view-BeYl7akD.js} +2 -2
- package/dist/ui/assets/{require-guards-CyhsDWYg.js → require-guards-Du4bUc9R.js} +1 -1
- package/dist/ui/assets/{resolver-BjX48SSL.js → resolver-Cuxoosmc.js} +4 -4
- package/dist/ui/assets/{rest-ops-CLliBy-9.js → rest-ops-B4SFZmC5.js} +1 -1
- package/dist/ui/assets/{restore-BSMRZD-l.js → restore-B7CtAq9P.js} +1 -1
- package/dist/ui/assets/{run-CyMKLTKU.js → run-BBYfg5ni.js} +1 -1
- package/dist/ui/assets/run-BUXpkB7_.js +14 -0
- package/dist/ui/assets/{run-CM8a7Cc3.js → run-BVpDhce0.js} +1 -1
- package/dist/ui/assets/{run-C8OfqJcF.js → run-BySbsyjF.js} +2 -2
- package/dist/ui/assets/{run-CJfA-Obt.js → run-C2YgBAHx.js} +1 -1
- package/dist/ui/assets/{run-DMO7-CuA.js → run-DVDUpupT.js} +1 -1
- package/dist/ui/assets/{run-BSt_FVlm.js → run-DkUy3OHx.js} +2 -2
- package/dist/ui/assets/{run-KLXVoIYy.js → run-DpwgbNbp.js} +1 -1
- package/dist/ui/assets/{run-BoxCVicT.js → run-NrNIVu5b.js} +3 -3
- package/dist/ui/assets/{run-CjBpwwpL.js → run-VCYF-r1b.js} +1 -1
- package/dist/ui/assets/{screencapture-run-C3Y7V44Z.js → screencapture-run-DUAmyetz.js} +1 -1
- package/dist/ui/assets/{secrets-bridge-client-BiJrSKN9.js → secrets-bridge-client-Cxg_UIbw.js} +1 -1
- package/dist/ui/assets/{session-command-memory-D9pTlYAY.js → session-command-memory-CwZxueuC.js} +2 -2
- package/dist/ui/assets/{session-jsonl-AoJrx9pb.js → session-jsonl-B0APtun9.js} +1 -1
- package/dist/ui/assets/{session-jsonl-CJzYIghQ.js → session-jsonl-Bf2fgFhg.js} +1 -1
- package/dist/ui/assets/{session-search-index-qOmoZUJy.js → session-search-index-Cj0yWWuZ.js} +1 -1
- package/dist/ui/assets/{setup-feature-flags-remote-BKACONdE.js → setup-feature-flags-remote-DobdVTx1.js} +1 -1
- package/dist/ui/assets/{setup-model-catalog-WWCUCy79.js → setup-model-catalog-D4LHaeXg.js} +1 -1
- package/dist/ui/assets/{shared-Crk4gYF8.js → shared-dvo4ZY2y.js} +1 -1
- package/dist/ui/assets/{signed-fetch-CwZ1-qSJ.js → signed-fetch-i5c9uHzg.js} +1 -1
- package/dist/ui/assets/{silent-renew-backoff-L4eA7UKO.js → silent-renew-backoff-BUgLOZtF.js} +1 -1
- package/dist/ui/assets/slicc-version-CyiNVflt.js +1 -0
- package/dist/ui/assets/{snapshot-D4DlVVCr.js → snapshot-CxBYeic7.js} +1 -1
- package/dist/ui/assets/{snapshot-features-Cp2XSvVD.js → snapshot-features-D9YdXtgT.js} +1 -1
- package/dist/ui/assets/{soundscape-CcHroDUu.js → soundscape-VpddA9af.js} +1 -1
- package/dist/ui/assets/{speak-5UMXEj6t.js → speak-DU6xYfaW.js} +1 -1
- package/dist/ui/assets/{sprinkle-manager-ByfWs52B.js → sprinkle-manager-B23UQfKW.js} +1 -1
- package/dist/ui/assets/{sprinkle-renderer-Dw0Oo-mB.js → sprinkle-renderer-D_eHK453.js} +1 -1
- package/dist/ui/assets/{ssh-ChyTtRdf.js → ssh-DYTFFLnN.js} +1 -1
- package/dist/ui/assets/{state-BPcLy39T.js → state-BEL8NQ3N.js} +1 -1
- package/dist/ui/assets/{store-BA5jRQLQ.js → store-C8pkV9xX.js} +1 -1
- package/dist/ui/assets/{store-BCOFBOmf.js → store-Ce8lHof4.js} +1 -1
- package/dist/ui/assets/{sudo-CZL3U0lj.js → sudo-Dz77pSRv.js} +1 -1
- package/dist/ui/assets/{supervisor-n5AQXVdz.js → supervisor-CODgsiun.js} +1 -1
- package/dist/ui/assets/{sync-dialog-CLv-AjS5.js → sync-dialog-DEd-6_ea.js} +1 -1
- package/dist/ui/assets/{transformers-env-DuP48CFD.js → transformers-env-DDxx-_yr.js} +1 -1
- package/dist/ui/assets/{tray-leader-oK24Tenu.js → tray-leader-PwhN5FVH.js} +1 -1
- package/dist/ui/assets/{tray-leave-runtime-BtmhGDJl.js → tray-leave-runtime-BLeKsvgA.js} +1 -1
- package/dist/ui/assets/{tray-runtime-config-CTm013-K.js → tray-runtime-config-CEuo4NwB.js} +1 -1
- package/dist/ui/assets/{tray-sidecar-x9PQcB2n.js → tray-sidecar-B5Rwvi0w.js} +1 -1
- package/dist/ui/assets/{turn-journal-CScxev4H.js → turn-journal-DgrDAXyz.js} +1 -1
- package/dist/ui/assets/{upgrade-detection-DS5evAII.js → upgrade-detection-DkrC9knp.js} +1 -1
- package/dist/ui/assets/{upskill-lick-skip-DsgHyVv5.js → upskill-lick-skip-x-HPkkoo.js} +1 -1
- package/dist/ui/assets/{voice-reply-ebSU1_qk.js → voice-reply-DN-og12Q.js} +1 -1
- package/dist/ui/assets/{wc-attach-525SvDTY.js → wc-attach-BxBMMvBD.js} +2 -2
- package/dist/ui/assets/{wc-browser-BWykEy-8.js → wc-browser-Bx_tyqby.js} +1 -1
- package/dist/ui/assets/{wc-computers-D3Avqz55.js → wc-computers--Hh_a1eS.js} +1 -1
- package/dist/ui/assets/{wc-detached-Cy1E6He4.js → wc-detached-Cfb7cdsS.js} +1 -1
- package/dist/ui/assets/{wc-extension-CVhWXV2w.js → wc-extension-DtmFrIBz.js} +2 -2
- package/dist/ui/assets/{wc-follower-C1kJ0lF_.js → wc-follower-CY8MJG2d.js} +3 -3
- package/dist/ui/assets/{wc-follower-oauth-qqfI5zJ5.js → wc-follower-oauth-Qp2Onk42.js} +1 -1
- package/dist/ui/assets/{wc-gelatiere-DFV5mBJW.js → wc-gelatiere-CIqpoRvp.js} +1 -1
- package/dist/ui/assets/{wc-live-Eu6ghZAo.js → wc-live-kIulpDGT.js} +5 -5
- package/dist/ui/assets/{wc-nav-BJ1Fg_hB.js → wc-nav-CKLAJB62.js} +2 -2
- package/dist/ui/assets/{wc-onboarding-D_k6jMGA.js → wc-onboarding-CCfbxIFk.js} +2 -2
- package/dist/ui/assets/{wc-permissions-CbwZeya-.js → wc-permissions-gkkaKIYQ.js} +2 -2
- package/dist/ui/assets/{wc-placeholder-DzHKpyEK.js → wc-placeholder-CPbGysCM.js} +3 -3
- package/dist/ui/assets/{wc-secret-request-bOcP6m9U.js → wc-secret-request-C-t_JIqx.js} +1 -1
- package/dist/ui/assets/{wc-settings-BNekAbpv.js → wc-settings-C3gOS2HO.js} +3 -3
- package/dist/ui/assets/{wc-shell-DdE40qZj.js → wc-shell-C5TUWPbD.js} +3 -3
- package/dist/ui/assets/{wc-shortcut-config-KdvTJp27.js → wc-shortcut-config-CxyrO44G.js} +1 -1
- package/dist/ui/assets/{wc-sprinkles-Bnnb88mU.js → wc-sprinkles-CdV_-XON.js} +2 -2
- package/dist/ui/assets/{wc-transcript-export-DWOEgP2-.js → wc-transcript-export-uviFziTO.js} +2 -2
- package/dist/ui/assets/{wc-tray-Dpx-EoMd.js → wc-tray-C_LlCq8n.js} +4 -4
- package/dist/ui/assets/welcome-detection-62JgQjpE.js +1 -0
- package/dist/ui/assets/{whisper-session-CB6rG-eD.js → whisper-session-DpmN2H-S.js} +2 -2
- package/dist/ui/assets/{xai-grok-CuIlQYpM.js → xai-grok-DqHHYruF.js} +1 -1
- package/dist/ui/assets/{xai-grok-BGPDFi9z.js → xai-grok-Qkm8D-bR.js} +1 -1
- package/dist/ui/index.html +3 -3
- package/dist/ui/packages/webapp/index.html +3 -3
- package/package.json +1 -1
- package/dist/ui/assets/SKILL-lcwLWMKp.js +0 -1
- package/dist/ui/assets/provider-store-access-BbvRUwZT.js +0 -1
- package/dist/ui/assets/provider-store-access-CrrW9eCi.js +0 -1
- package/dist/ui/assets/proxied-fetch-DXaTHROw.js +0 -1
- package/dist/ui/assets/run-Y1uUJvV7.js +0 -14
- package/dist/ui/assets/slicc-version-jRBuHQmT.js +0 -1
- package/dist/ui/assets/welcome-detection-BdEfH_d7.js +0 -1
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Raw mode of `/api/fetch-proxy` (#3571): the HTTP-client flavor the wasm
|
|
3
|
+
* realm's proxy forwards curl and git through. The wire contract lives in
|
|
4
|
+
* `@slicc/shared-ts` `raw-fetch-protocol.ts`; this module is the CLI/cloud
|
|
5
|
+
* float's side of it. Differences from the default route:
|
|
6
|
+
*
|
|
7
|
+
* - the request head (URL, method, ordered headers) arrives as JSON in
|
|
8
|
+
* `X-Slicc-Raw-Request`, so the browser's forbidden-header filter and the
|
|
9
|
+
* page's own headers (User-Agent, Sec-Fetch-*) never reach upstream;
|
|
10
|
+
* - redirects are manual: the 3xx, `Location` and every `Set-Cookie` go
|
|
11
|
+
* back to the caller in an ordered header list;
|
|
12
|
+
* - the answer is always `200` + `application/vnd.slicc.raw-fetch`: one
|
|
13
|
+
* response-head frame, then the body streamed through (Node `pipe`
|
|
14
|
+
* pauses upstream whenever the browser stops reading, though Chrome's
|
|
15
|
+
* `fetch` reads ahead of its JS reader, so in practice it rarely does).
|
|
16
|
+
*
|
|
17
|
+
* Secret handling is the default route's: masked values in headers, URL
|
|
18
|
+
* credentials and text bodies are unmasked for allowed domains only (403
|
|
19
|
+
* otherwise), `x-slicc-hmac-sign` is honored, and response header values and
|
|
20
|
+
* text bodies are scrubbed, including `Location`.
|
|
21
|
+
*/
|
|
22
|
+
import { decodeRawRequestHead, encodeRawResponseFrame, foldRawRequestHeaders, HMAC_SIGN_HEADER, isDecodedPartialResponse, isTextContentType, RAW_FETCH_BRIDGE_REQUEST_BODY_CAP, RAW_FETCH_CONTENT_TYPE, RAW_FETCH_PROBE_HEADER, RAW_FETCH_PROTOCOL_VERSION, RAW_FETCH_REQUEST_HEADER, rawAcceptEncoding, rawResponseHasBody, rawResponseHeaders, stripRawRequestHeaders, } from '../_shared/index.js';
|
|
23
|
+
import { applyHmacSigning, attachUpstreamAbort, injectRequestSecrets, streamUpstreamBody, unmaskRequestBody, } from './fetch-proxy.js';
|
|
24
|
+
const RAW_REQUEST_HEADER_LOWER = RAW_FETCH_REQUEST_HEADER.toLowerCase();
|
|
25
|
+
const RAW_PROBE_HEADER_LOWER = RAW_FETCH_PROBE_HEADER.toLowerCase();
|
|
26
|
+
class RequestBodyTooLargeError extends Error {
|
|
27
|
+
}
|
|
28
|
+
function sendProxyError(res, status, error) {
|
|
29
|
+
res.setHeader('X-Proxy-Error', '1');
|
|
30
|
+
res.status(status).json({ error });
|
|
31
|
+
}
|
|
32
|
+
/** Read the upload, refusing it as soon as it passes `limit`. */
|
|
33
|
+
async function readBoundedBody(req, limit) {
|
|
34
|
+
const declared = Number(req.headers['content-length']);
|
|
35
|
+
if (Number.isFinite(declared) && declared > limit)
|
|
36
|
+
throw new RequestBodyTooLargeError();
|
|
37
|
+
const chunks = [];
|
|
38
|
+
let size = 0;
|
|
39
|
+
for await (const chunk of req) {
|
|
40
|
+
const buf = Buffer.from(chunk);
|
|
41
|
+
size += buf.byteLength;
|
|
42
|
+
if (size > limit)
|
|
43
|
+
throw new RequestBodyTooLargeError();
|
|
44
|
+
chunks.push(buf);
|
|
45
|
+
}
|
|
46
|
+
return Buffer.concat(chunks);
|
|
47
|
+
}
|
|
48
|
+
/** Upstream response headers as an ordered list, each `Set-Cookie` separate. */
|
|
49
|
+
function upstreamHeaderList(upstream) {
|
|
50
|
+
const list = [];
|
|
51
|
+
upstream.headers.forEach((value, name) => {
|
|
52
|
+
if (name !== 'set-cookie')
|
|
53
|
+
list.push([name, value]);
|
|
54
|
+
});
|
|
55
|
+
for (const cookie of upstream.headers.getSetCookie())
|
|
56
|
+
list.push(['set-cookie', cookie]);
|
|
57
|
+
return list;
|
|
58
|
+
}
|
|
59
|
+
/** Build the upstream request with secrets injected, or name the forbidden one. */
|
|
60
|
+
async function prepareUpstream(secretProxy, head, rawBody) {
|
|
61
|
+
const headers = foldRawRequestHeaders(stripRawRequestHeaders(head.headers));
|
|
62
|
+
const hmacSpec = headers[HMAC_SIGN_HEADER];
|
|
63
|
+
delete headers[HMAC_SIGN_HEADER];
|
|
64
|
+
const acceptEncoding = rawAcceptEncoding(headers);
|
|
65
|
+
if (acceptEncoding !== undefined)
|
|
66
|
+
headers['accept-encoding'] = acceptEncoding;
|
|
67
|
+
let hostname = '';
|
|
68
|
+
try {
|
|
69
|
+
hostname = new URL(head.url).hostname;
|
|
70
|
+
}
|
|
71
|
+
catch {
|
|
72
|
+
// Malformed URL: the upstream fetch rejects it below.
|
|
73
|
+
}
|
|
74
|
+
const injection = injectRequestSecrets(secretProxy, headers, head.url, hostname);
|
|
75
|
+
if ('forbidden' in injection)
|
|
76
|
+
return injection;
|
|
77
|
+
const method = head.method.toUpperCase();
|
|
78
|
+
let body;
|
|
79
|
+
if (rawBody.length > 0 && method !== 'GET' && method !== 'HEAD') {
|
|
80
|
+
body = unmaskRequestBody(secretProxy, headers, rawBody, hostname);
|
|
81
|
+
}
|
|
82
|
+
const signing = await applyHmacSigning(secretProxy, headers, hmacSpec, body, hostname);
|
|
83
|
+
if (signing)
|
|
84
|
+
return signing;
|
|
85
|
+
const init = { method: head.method, headers, redirect: 'manual' };
|
|
86
|
+
if (body)
|
|
87
|
+
init.body = body;
|
|
88
|
+
return { url: injection.cleanedUrl, init };
|
|
89
|
+
}
|
|
90
|
+
/** Write the response-head frame, then stream (or skip) the body. */
|
|
91
|
+
function relayUpstream(res, head, upstream, secretProxy, detachClientClose) {
|
|
92
|
+
const hasBody = rawResponseHasBody(head.method, upstream.status) && upstream.body !== null;
|
|
93
|
+
// Text bodies pass the gunzip sniff and the secret scrub, so their length
|
|
94
|
+
// is not the upstream one; binary bodies pass through byte for byte.
|
|
95
|
+
const bodyRewritten = isTextContentType(upstream.headers.get('content-type') ?? '');
|
|
96
|
+
const headers = rawResponseHeaders({
|
|
97
|
+
method: head.method,
|
|
98
|
+
status: upstream.status,
|
|
99
|
+
headers: upstreamHeaderList(upstream),
|
|
100
|
+
bodyRewritten,
|
|
101
|
+
}).map(([name, value]) => [name, secretProxy.scrubResponse(value)]);
|
|
102
|
+
res.status(200);
|
|
103
|
+
res.setHeader('Content-Type', RAW_FETCH_CONTENT_TYPE);
|
|
104
|
+
res.setHeader('Cache-Control', 'no-store, no-cache');
|
|
105
|
+
res.write(encodeRawResponseFrame({
|
|
106
|
+
status: upstream.status,
|
|
107
|
+
statusText: upstream.statusText,
|
|
108
|
+
headers,
|
|
109
|
+
url: head.url,
|
|
110
|
+
}));
|
|
111
|
+
if (!hasBody || !upstream.body) {
|
|
112
|
+
void upstream.body?.cancel().catch(() => undefined);
|
|
113
|
+
res.end();
|
|
114
|
+
detachClientClose();
|
|
115
|
+
return;
|
|
116
|
+
}
|
|
117
|
+
streamUpstreamBody(res, upstream, secretProxy, detachClientClose);
|
|
118
|
+
}
|
|
119
|
+
/** Whether this `/api/fetch-proxy` request selected raw mode. */
|
|
120
|
+
export function isRawFetchProxyRequest(req) {
|
|
121
|
+
return typeof req.headers[RAW_REQUEST_HEADER_LOWER] === 'string';
|
|
122
|
+
}
|
|
123
|
+
/** Whether this `/api/fetch-proxy` request asks what raw mode can do. */
|
|
124
|
+
export function isRawFetchProbe(req) {
|
|
125
|
+
return req.headers[RAW_PROBE_HEADER_LOWER] !== undefined;
|
|
126
|
+
}
|
|
127
|
+
/** Answer the capability probe; nothing is fetched upstream. */
|
|
128
|
+
export function answerRawFetchProbe(res, maxRequestBodyBytes) {
|
|
129
|
+
const reply = {
|
|
130
|
+
rawFetch: RAW_FETCH_PROTOCOL_VERSION,
|
|
131
|
+
requestBodyStreaming: false,
|
|
132
|
+
maxRequestBodyBytes,
|
|
133
|
+
};
|
|
134
|
+
res.setHeader('Cache-Control', 'no-store');
|
|
135
|
+
res.status(200).json(reply);
|
|
136
|
+
}
|
|
137
|
+
export async function handleRawFetchProxy(req, res, deps) {
|
|
138
|
+
const { secretProxy, logger = console } = deps;
|
|
139
|
+
const limit = deps.maxRequestBodyBytes ?? RAW_FETCH_BRIDGE_REQUEST_BODY_CAP;
|
|
140
|
+
const head = decodeRawRequestHead(String(req.headers[RAW_REQUEST_HEADER_LOWER]));
|
|
141
|
+
if (!head) {
|
|
142
|
+
sendProxyError(res, 400, `Malformed ${RAW_FETCH_REQUEST_HEADER} header`);
|
|
143
|
+
return;
|
|
144
|
+
}
|
|
145
|
+
let rawBody;
|
|
146
|
+
try {
|
|
147
|
+
rawBody = await readBoundedBody(req, limit);
|
|
148
|
+
}
|
|
149
|
+
catch (err) {
|
|
150
|
+
if (!(err instanceof RequestBodyTooLargeError))
|
|
151
|
+
throw err;
|
|
152
|
+
logger.warn(`[fetch-proxy:raw] ${head.method} ${head.url} → 413`);
|
|
153
|
+
res.setHeader('Connection', 'close');
|
|
154
|
+
sendProxyError(res, 413, `Request body exceeds the ${limit} byte limit of this float`);
|
|
155
|
+
return;
|
|
156
|
+
}
|
|
157
|
+
logger.log(`[fetch-proxy:raw] ${head.method} ${head.url}`);
|
|
158
|
+
const prepared = await prepareUpstream(secretProxy, head, rawBody);
|
|
159
|
+
if ('forbidden' in prepared) {
|
|
160
|
+
const { secretName, hostname } = prepared.forbidden;
|
|
161
|
+
logger.warn(`[fetch-proxy:raw] ${head.method} ${head.url} → 403 (secret "${secretName}")`);
|
|
162
|
+
sendProxyError(res, 403, `Secret "${secretName}" is not allowed for domain "${hostname}"`);
|
|
163
|
+
return;
|
|
164
|
+
}
|
|
165
|
+
const upstreamAbort = attachUpstreamAbort(res);
|
|
166
|
+
prepared.init.signal = upstreamAbort.controller.signal;
|
|
167
|
+
let upstream;
|
|
168
|
+
try {
|
|
169
|
+
upstream = await fetch(prepared.url, prepared.init);
|
|
170
|
+
}
|
|
171
|
+
catch (err) {
|
|
172
|
+
upstreamAbort.detach();
|
|
173
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
174
|
+
logger.error(`[fetch-proxy:raw] ${head.method} ${head.url} ← 502 (${message})`);
|
|
175
|
+
sendProxyError(res, 502, `Proxy fetch failed: ${message}`);
|
|
176
|
+
return;
|
|
177
|
+
}
|
|
178
|
+
logger.log(`[fetch-proxy:raw] ${head.method} ${head.url} ← ${upstream.status}`);
|
|
179
|
+
if (isDecodedPartialResponse({ status: upstream.status, headers: upstreamHeaderList(upstream) })) {
|
|
180
|
+
upstreamAbort.detach();
|
|
181
|
+
await upstream.body?.cancel().catch(() => undefined);
|
|
182
|
+
logger.warn(`[fetch-proxy:raw] ${head.method} ${head.url} ← 206 encoded despite identity`);
|
|
183
|
+
sendProxyError(res, 502, 'Upstream answered a range request with an encoded partial body');
|
|
184
|
+
return;
|
|
185
|
+
}
|
|
186
|
+
relayUpstream(res, head, upstream, secretProxy, upstreamAbort.detach);
|
|
187
|
+
}
|
|
188
|
+
/**
|
|
189
|
+
* Mount raw mode ahead of the default `/api/fetch-proxy` handler; requests
|
|
190
|
+
* without `X-Slicc-Raw-Request` fall through to it untouched.
|
|
191
|
+
*/
|
|
192
|
+
export function registerRawFetchProxyRoute(app, deps) {
|
|
193
|
+
app.post('/api/fetch-proxy', (req, res, next) => {
|
|
194
|
+
if (isRawFetchProbe(req) && !isRawFetchProxyRequest(req)) {
|
|
195
|
+
answerRawFetchProbe(res, deps.maxRequestBodyBytes ?? RAW_FETCH_BRIDGE_REQUEST_BODY_CAP);
|
|
196
|
+
return;
|
|
197
|
+
}
|
|
198
|
+
if (!isRawFetchProxyRequest(req)) {
|
|
199
|
+
next();
|
|
200
|
+
return;
|
|
201
|
+
}
|
|
202
|
+
deps.activityTracker.recordActivity();
|
|
203
|
+
handleRawFetchProxy(req, res, deps).catch(next);
|
|
204
|
+
});
|
|
205
|
+
}
|
|
@@ -42,6 +42,29 @@ export declare function injectRequestSecrets(secretProxy: SecretProxyManager, he
|
|
|
42
42
|
} | {
|
|
43
43
|
cleanedUrl: string;
|
|
44
44
|
};
|
|
45
|
+
/**
|
|
46
|
+
* Apply the `x-slicc-hmac-sign` directive, if present: sign `body` with the
|
|
47
|
+
* named secret's real value and attach the hex result under the header the
|
|
48
|
+
* client requested. Mutates `headers` and always removes the sentinel
|
|
49
|
+
* (already stripped from `headers` by `buildForwardHeaders`'s skip-list;
|
|
50
|
+
* this only needs to read it off the raw request).
|
|
51
|
+
*/
|
|
52
|
+
export declare function applyHmacSigning(secretProxy: SecretProxyManager, headers: Record<string, string>, hmacSpec: string | undefined, body: Buffer | undefined, targetHostname: string): Promise<{
|
|
53
|
+
forbidden: ForbiddenSecret;
|
|
54
|
+
} | undefined>;
|
|
55
|
+
/**
|
|
56
|
+
* Unmask masked secrets in a text request body. Non-text bodies (git
|
|
57
|
+
* packfiles, octet-stream, images, …) are left untouched — `toString('utf-8')`
|
|
58
|
+
* on arbitrary bytes corrupts them, and masked values never appear in binary.
|
|
59
|
+
*
|
|
60
|
+
* Uses the request-side predicate so form POSTs count as text: swift-server
|
|
61
|
+
* unmasks them and a masked `client_secret` reaching an upstream OAuth token
|
|
62
|
+
* endpoint verbatim is a silent auth failure, not a corrupt-bytes risk (#2821).
|
|
63
|
+
* A form body then takes the encoding-aware `unmaskFormBody` path — a plain
|
|
64
|
+
* substring splice would corrupt it whenever the real secret carries a
|
|
65
|
+
* form-reserved character (base64 `+` / `/` / `=`).
|
|
66
|
+
*/
|
|
67
|
+
export declare function unmaskRequestBody(secretProxy: SecretProxyManager, headers: Record<string, string>, rawBody: Buffer, targetHostname: string): Buffer;
|
|
45
68
|
/**
|
|
46
69
|
* Buffer-aware UTF-8 secret scrubber. A `StringDecoder` keeps trailing partial
|
|
47
70
|
* multi-byte sequences out of the scrub so codepoints straddling a chunk
|
|
@@ -116,7 +116,7 @@ export function injectRequestSecrets(secretProxy, headers, targetUrl, targetHost
|
|
|
116
116
|
* (already stripped from `headers` by `buildForwardHeaders`'s skip-list;
|
|
117
117
|
* this only needs to read it off the raw request).
|
|
118
118
|
*/
|
|
119
|
-
async function applyHmacSigning(secretProxy, headers, hmacSpec, body, targetHostname) {
|
|
119
|
+
export async function applyHmacSigning(secretProxy, headers, hmacSpec, body, targetHostname) {
|
|
120
120
|
if (!hmacSpec)
|
|
121
121
|
return undefined;
|
|
122
122
|
const signResult = await secretProxy.signHmac(hmacSpec, body ?? Buffer.alloc(0), targetHostname);
|
|
@@ -142,7 +142,7 @@ async function applyHmacSigning(secretProxy, headers, hmacSpec, body, targetHost
|
|
|
142
142
|
* substring splice would corrupt it whenever the real secret carries a
|
|
143
143
|
* form-reserved character (base64 `+` / `/` / `=`).
|
|
144
144
|
*/
|
|
145
|
-
function unmaskRequestBody(secretProxy, headers, rawBody, targetHostname) {
|
|
145
|
+
export function unmaskRequestBody(secretProxy, headers, rawBody, targetHostname) {
|
|
146
146
|
const contentType = headers['content-type'] ?? headers['Content-Type'] ?? '';
|
|
147
147
|
if (!isTextRequestContentType(contentType) || !secretProxy.hasSecrets())
|
|
148
148
|
return rawBody;
|
|
@@ -268,7 +268,8 @@ export function streamUpstreamBody(res, upstream, secretProxy, detachClientClose
|
|
|
268
268
|
const decoded = isText
|
|
269
269
|
? createMaybeGunzipStream({
|
|
270
270
|
onDecided: (inflating) => {
|
|
271
|
-
|
|
271
|
+
// Raw mode (`fetch-proxy-raw.ts`) has already sent its head frame.
|
|
272
|
+
if (inflating && !res.headersSent)
|
|
272
273
|
res.removeHeader(FETCH_PROXY_CONTENT_LENGTH_HEADER);
|
|
273
274
|
},
|
|
274
275
|
})
|