@organizaone/o1-code 0.11.3 → 0.12.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/bundled/qc-helper/docs/features/commands.md +1 -0
- package/chunks/{MaxSizedBox-4YD6N3PU.js → MaxSizedBox-JTZPQAQ5.js} +66 -66
- package/chunks/{StandaloneSessionPicker-I5FMAIU2.js → StandaloneSessionPicker-VFQEG54H.js} +88 -88
- package/chunks/{acp-subagent-executor-PRFEPDLD.js → acp-subagent-executor-QEOKYURZ.js} +70 -70
- package/chunks/{acpAgent-2SVJNE5B.js → acpAgent-5MQXC2XU.js} +229 -227
- package/chunks/{agent-QONNC6CU.js → agent-FKFYDUVP.js} +42 -42
- package/chunks/{agent-headless-5HHGBH6Z.js → agent-headless-TVPFCEOP.js} +42 -42
- package/chunks/all-providers-CAKB5HCH.js +73 -0
- package/chunks/{anthropicContentGenerator-2VK5J36U.js → anthropicContentGenerator-K2ELMX7W.js} +121 -73
- package/chunks/{bridge-376JROPC.js → bridge-33WHGPXZ.js} +71 -71
- package/chunks/{bwrap-execution-UZAMPG6F.js → bwrap-execution-FIGUBZCN.js} +6 -6
- package/chunks/{caption-audio-HGDJVVJZ.js → caption-audio-EK6K45W3.js} +8 -8
- package/chunks/{caption-image-ATZSXV7W.js → caption-image-WDXLQK6T.js} +6 -6
- package/chunks/{chunk-KRZBFGAT.js → chunk-32XL2YY2.js} +2345 -4252
- package/chunks/{chunk-MLHBCLEP.js → chunk-3EPSY3X7.js} +9 -9
- package/chunks/{chunk-GCAERMSL.js → chunk-3T72AXG7.js} +6 -6
- package/chunks/{chunk-ZG7HDQQN.js → chunk-3YUQQOUL.js} +13 -13
- package/chunks/{chunk-3DUYZD3L.js → chunk-46JJPOUF.js} +3 -3
- package/chunks/{chunk-7LLX5ROK.js → chunk-4G6OUQPH.js} +3 -3
- package/chunks/{chunk-QCONHGJR.js → chunk-4OUIZHHJ.js} +4 -4
- package/chunks/{chunk-QYUMJTBP.js → chunk-4SJO2I35.js} +1 -1
- package/chunks/{chunk-EB5V6K5E.js → chunk-4X3MIKOZ.js} +5 -5
- package/chunks/{chunk-HCE5HUQG.js → chunk-55BVGW3L.js} +4 -4
- package/chunks/{chunk-OJP3SONV.js → chunk-5AIPNJAB.js} +5 -5
- package/chunks/{chunk-ADBRWAV5.js → chunk-5NNMFA5J.js} +1 -1
- package/chunks/{chunk-UWV7KBPN.js → chunk-5OGI74MG.js} +2 -2
- package/chunks/{chunk-RZGFYBL5.js → chunk-5ZO4TG64.js} +4 -4
- package/chunks/{chunk-R3QYIDIU.js → chunk-62SWENB6.js} +2 -2
- package/chunks/{chunk-3KUQCZUE.js → chunk-65JWVPGE.js} +1 -1
- package/chunks/{chunk-REDO4D2W.js → chunk-65NGV7DR.js} +11 -11
- package/chunks/{chunk-HK7AV2JH.js → chunk-6O3ZQGW7.js} +1 -1
- package/chunks/{chunk-PLGVOOCJ.js → chunk-6WTLQ4BD.js} +1 -1
- package/chunks/{chunk-H2U6QRJX.js → chunk-72EBG4FJ.js} +3 -471
- package/chunks/{chunk-4OI2JVHJ.js → chunk-776VI66O.js} +2 -2
- package/chunks/{chunk-NWG5SKCQ.js → chunk-7JEEDCZF.js} +3 -3
- package/chunks/{chunk-VJFA7HHP.js → chunk-7LQNUREG.js} +1 -1
- package/chunks/chunk-7TK6OY2J.js +53 -0
- package/chunks/{chunk-5MX4BYWK.js → chunk-ADPTJZ7L.js} +3 -3
- package/chunks/{chunk-A4JNI44O.js → chunk-AKIPIOIW.js} +62 -36
- package/chunks/{chunk-IYLIZY56.js → chunk-BF6VCZFG.js} +4 -4
- package/chunks/{chunk-ZBV6TO3P.js → chunk-C7LS4YW3.js} +1 -1
- package/chunks/{chunk-W4SBUUTI.js → chunk-D56JIPBA.js} +3 -3
- package/chunks/{chunk-OKDNVNDE.js → chunk-DBPKYUKM.js} +5 -5
- package/chunks/{chunk-LBM7FFM5.js → chunk-DNJPSAMC.js} +1 -1
- package/chunks/{chunk-W6TGDESU.js → chunk-DOG2T422.js} +2 -2
- package/chunks/{chunk-J4QU4MC5.js → chunk-DOWMMDKU.js} +9 -9
- package/chunks/{chunk-VKBMJSAR.js → chunk-EMXZMR7F.js} +321 -109
- package/chunks/{chunk-653CF7X7.js → chunk-EO3GEEN5.js} +3 -3
- package/chunks/{chunk-RS2LNQEC.js → chunk-EVC2HZ5T.js} +3 -39
- package/chunks/{chunk-MIWMCUU5.js → chunk-EVIJNKSX.js} +17 -8
- package/chunks/{chunk-YR3UI5PL.js → chunk-EWBEXGRC.js} +10 -10
- package/chunks/{chunk-ALRR6EQ5.js → chunk-EXTCLF42.js} +2 -2
- package/chunks/{chunk-QE2GKG2B.js → chunk-F36DMAAF.js} +5 -5
- package/chunks/{chunk-C4WTDBKV.js → chunk-FEQM3UVX.js} +8 -8
- package/chunks/{chunk-V75J4GJ2.js → chunk-FJAZPBX2.js} +1 -1
- package/chunks/{chunk-QKTYOXBW.js → chunk-FJB4BIQY.js} +1 -1
- package/chunks/{chunk-QHENJTME.js → chunk-FL43SLBB.js} +36 -36
- package/chunks/{chunk-VPGICF4V.js → chunk-FOGGBV5D.js} +3 -3
- package/chunks/{chunk-JQPMOOCH.js → chunk-FXR7OHQM.js} +1 -1
- package/chunks/{chunk-DVRWYOU6.js → chunk-GQ2KSD3A.js} +1 -1
- package/chunks/{chunk-BLFBP3N2.js → chunk-GSMPZG6N.js} +1 -1
- package/chunks/{chunk-LK74HWZ4.js → chunk-H4FGPXI3.js} +63 -63
- package/chunks/{chunk-WDCHA7NA.js → chunk-H4K55KOQ.js} +5 -5
- package/chunks/{chunk-JHZM32LS.js → chunk-IKYYF5PP.js} +388 -620
- package/chunks/{chunk-SIZ4LTNU.js → chunk-J2WSYRAJ.js} +28 -28
- package/chunks/{chunk-LEBY6RZT.js → chunk-J33WTLCX.js} +5 -5
- package/chunks/{chunk-7M4NSLWM.js → chunk-JOBZWTVK.js} +1 -1
- package/chunks/{chunk-W4COM34L.js → chunk-K3RGDBVU.js} +3 -3
- package/chunks/{chunk-PQZRAMZG.js → chunk-KCBAWQGT.js} +19 -19
- package/chunks/{chunk-SAGRWAPN.js → chunk-KD2QZWRE.js} +12 -10
- package/chunks/{chunk-ZUQTYSNU.js → chunk-KEBHJIGX.js} +4 -4
- package/chunks/{chunk-AAXKDAOY.js → chunk-KYFNPY5G.js} +1 -1
- package/chunks/{chunk-HVOUAFTO.js → chunk-M2HUTNAI.js} +7 -7
- package/chunks/{chunk-QIRU74SA.js → chunk-MKBG6C2K.js} +1 -1
- package/chunks/{chunk-YPWPIBDR.js → chunk-MMVLDNOK.js} +5 -5
- package/chunks/{chunk-AQFTTYP6.js → chunk-MRR2UVN6.js} +4 -4
- package/chunks/{chunk-4O37MUA4.js → chunk-NIOE4OJE.js} +1 -1
- package/chunks/{chunk-AJSSTPJK.js → chunk-NLDWAN5E.js} +1 -1
- package/chunks/{chunk-OJSW65PQ.js → chunk-NOL6NAWN.js} +1 -1
- package/chunks/{chunk-EXG47R4V.js → chunk-NPGATJZA.js} +2 -2
- package/chunks/{chunk-XHYWPHWN.js → chunk-NQTVSCZM.js} +1 -1
- package/chunks/chunk-NTDRNHKB.js +484 -0
- package/chunks/{chunk-L7JLMUG3.js → chunk-NTHY5RR5.js} +2 -2
- package/chunks/{chunk-EIJNDD2V.js → chunk-O43YWGHK.js} +1 -1
- package/chunks/{chunk-6IAGZKHT.js → chunk-OF7SHQ7M.js} +3 -3
- package/chunks/{chunk-GXHNABHV.js → chunk-OP553VGL.js} +8 -8
- package/chunks/{chunk-L6MGGTHA.js → chunk-OS6CVNKI.js} +1 -1
- package/chunks/{chunk-7LFCBMVX.js → chunk-P4F73WS7.js} +1 -1
- package/chunks/{chunk-DG6LRVVN.js → chunk-PD4HVHCC.js} +1 -1
- package/chunks/{chunk-JZ4ZEW3K.js → chunk-Q5CQXESD.js} +1 -1
- package/chunks/{chunk-TK7HNIQM.js → chunk-QFMADEHM.js} +1 -1
- package/chunks/{chunk-U7C4TQUX.js → chunk-QFUVHWOM.js} +1 -1
- package/chunks/{chunk-WS7DJM5N.js → chunk-QJQOFPJ5.js} +18 -18
- package/chunks/{chunk-EFN3RELD.js → chunk-QNHUPN6K.js} +3 -3
- package/chunks/{chunk-O5F6BGMF.js → chunk-RE37G7GK.js} +7 -7
- package/chunks/{chunk-OKR7R26W.js → chunk-RRYTDYCK.js} +4 -4
- package/chunks/{chunk-BIP774WW.js → chunk-RS2CMJTM.js} +1 -1
- package/chunks/{chunk-3YG4OBGV.js → chunk-S5H5TQN4.js} +1 -1
- package/chunks/{chunk-W7NTX5RO.js → chunk-S5QFGJXY.js} +7 -7
- package/chunks/{chunk-ER3EHNTV.js → chunk-SPTU3FVH.js} +4 -4
- package/chunks/chunk-TC2TJAOF.js +1365 -0
- package/chunks/{chunk-HUMRTP2R.js → chunk-TFFOK3SJ.js} +1 -1
- package/chunks/{chunk-OD2XVV6S.js → chunk-TJRZBOWB.js} +6 -6
- package/chunks/chunk-TL23ZRID.js +327 -0
- package/chunks/{chunk-QMCDBQRJ.js → chunk-TP3M5WJ2.js} +1 -1
- package/chunks/chunk-TPHHBX2V.js +372 -0
- package/chunks/{chunk-FVO4TXS5.js → chunk-TXTBBSFN.js} +6 -6
- package/chunks/{chunk-WQ6CX6N3.js → chunk-U5W3UVUJ.js} +15 -15
- package/chunks/{chunk-FYQO4Y3N.js → chunk-UEBG3R2E.js} +19 -19
- package/chunks/{chunk-N7NBYHMJ.js → chunk-UNY7AZRV.js} +1 -1
- package/chunks/{chunk-UDWKKKF3.js → chunk-VBCZ36HL.js} +16 -12
- package/chunks/{chunk-DMVMQMWZ.js → chunk-VCQ6UT2V.js} +86 -86
- package/chunks/{chunk-5V3EOW36.js → chunk-VGJTZHV4.js} +28 -28
- package/chunks/{chunk-XOVXSDNN.js → chunk-VPFTCMMK.js} +1 -1
- package/chunks/{chunk-7H7YR3TX.js → chunk-VSGHHI2A.js} +1 -1
- package/chunks/{chunk-FQO3QDB5.js → chunk-VT3TJFQR.js} +1 -1
- package/chunks/{chunk-6LLJK4YT.js → chunk-VWSBLBKL.js} +2 -2
- package/chunks/{chunk-4FUKWWA7.js → chunk-W52IXLWZ.js} +2 -2
- package/chunks/{chunk-7I52LFBI.js → chunk-W7TFIXSV.js} +8 -6
- package/chunks/chunk-WBPIFM2X.js +2008 -0
- package/chunks/{chunk-AIQC2WHG.js → chunk-WDHSE635.js} +4 -4
- package/chunks/{chunk-M2EBVYXC.js → chunk-WK44MSGW.js} +1 -1
- package/chunks/{chunk-IKXBBMRT.js → chunk-WSEWAUUB.js} +1 -1
- package/chunks/{chunk-FJHDFJVI.js → chunk-WSHOWMUB.js} +1 -1
- package/chunks/{chunk-VBJH6XAM.js → chunk-WVGCMQD5.js} +1 -1
- package/chunks/{chunk-OAGT2TNR.js → chunk-XD6GHBUY.js} +9 -9
- package/chunks/{chunk-6D6Z2RPR.js → chunk-XDTO4IJ3.js} +2 -2
- package/chunks/{chunk-4AJNXOC5.js → chunk-XRY5FMOE.js} +2 -2
- package/chunks/{chunk-Z5UCVPGO.js → chunk-YII34AZ7.js} +13 -13
- package/chunks/{chunk-BSLKB37V.js → chunk-ZGPPP7FC.js} +30 -30
- package/chunks/{chunk-ZQBNP2ZQ.js → chunk-ZRO2FPEP.js} +4 -4
- package/chunks/{chunk-4RA6DVZM.js → chunk-ZUP2FFM5.js} +4 -4
- package/chunks/{codex-subagent-executor-BDV2OYSK.js → codex-subagent-executor-6GZ7UBTB.js} +70 -70
- package/chunks/{contextCommand-4XBRYOBZ.js → contextCommand-RK4SGQXM.js} +69 -69
- package/chunks/{core-runtime-IYMVEYKI.js → core-runtime-Z3MAZ6W4.js} +78 -78
- package/chunks/{create-sub-session-EAHSPBXY.js → create-sub-session-KWT7I6XG.js} +72 -72
- package/chunks/{daemon-git-worktree-guard-JPOH2ZV5.js → daemon-git-worktree-guard-RVYAPX2W.js} +68 -68
- package/chunks/{daemon-status-provider-GOJGNJ2Y.js → daemon-status-provider-7Q4AQLU7.js} +76 -76
- package/chunks/{daemon-trust-policy-X5QLCVP2.js → daemon-trust-policy-K6KAYOII.js} +74 -74
- package/chunks/{daemon-trust-policy-monitor-GOMIMXAS.js → daemon-trust-policy-monitor-L2HR4KDO.js} +74 -74
- package/chunks/{deferred-core-runtime-MFMRVOPT.js → deferred-core-runtime-B3GYIW7X.js} +66 -66
- package/chunks/{display-image-U6HZKF3G.js → display-image-ETU7QIAC.js} +2 -2
- package/chunks/{edit-N5WDGSAZ.js → edit-DHFV3SJW.js} +44 -44
- package/chunks/{en-C4TIYTOH.js → en-PCBCZAUA.js} +29 -2
- package/chunks/{enter-worktree-A2WD27HQ.js → enter-worktree-OZOIH6KH.js} +4 -4
- package/chunks/{enterPlanMode-52GAH43E.js → enterPlanMode-FHBSHN7T.js} +42 -42
- package/chunks/{environment-RXHNLDLZ.js → environment-SJ2O6HRJ.js} +69 -69
- package/chunks/{errors-D65OPVWO.js → errors-SLII5WDL.js} +69 -69
- package/chunks/{execution-sandbox-config-YE2XOT56.js → execution-sandbox-config-EYZYM2IC.js} +7 -7
- package/chunks/{execution-tool-RK3OB5IY.js → execution-tool-VRA7QCXP.js} +45 -45
- package/chunks/{execution-worker-R7E5B7CA.js → execution-worker-LT2EFVHR.js} +50 -50
- package/chunks/{exit-worktree-S7KUQ4HO.js → exit-worktree-W5TC4B4C.js} +4 -4
- package/chunks/{exitPlanMode-SRLMLRK5.js → exitPlanMode-XQ75FETK.js} +42 -42
- package/chunks/{fast-path-KOMJMO2I.js → fast-path-NQQ53WED.js} +2 -2
- package/chunks/{glob-MD6DOICU.js → glob-7SQOB5CX.js} +42 -42
- package/chunks/{goal-tools-FLJYRSX6.js → goal-tools-4GR2BFFX.js} +42 -42
- package/chunks/{handleAutoUpdate-LX4OGPKI.js → handleAutoUpdate-OLVCIBDN.js} +70 -70
- package/chunks/{i18n-CSJNAMVB.js → i18n-NXRBYDXM.js} +68 -68
- package/chunks/{image-gen-GRJ73O3N.js → image-gen-Q4E7L7EV.js} +3 -6
- package/chunks/{initializer-UN73MGL2.js → initializer-NGPV43CI.js} +74 -74
- package/chunks/{installationInfo-DOO2EVLV.js → installationInfo-U5NUDGQV.js} +66 -66
- package/chunks/{keychain-token-storage-NDVFNJI5.js → keychain-token-storage-SJOKJDWV.js} +2 -1
- package/chunks/{list-UMP3UXDE.js → list-WUD2DUO4.js} +77 -77
- package/chunks/{llm-4AHGUE2C.js → llm-3GFPNXO6.js} +142 -142
- package/chunks/{llm-content-generator-I62R6Q4I.js → llm-content-generator-5CTS3JEL.js} +8 -6
- package/chunks/{loadedSettingsAdapter-KHAGVCPO.js → loadedSettingsAdapter-45PMLPYE.js} +74 -74
- package/chunks/{local-execution-environment-DOKSKTNZ.js → local-execution-environment-XJ5Y5J5E.js} +50 -50
- package/chunks/{loggingContentGenerator-53CAIYCN.js → loggingContentGenerator-BJHOK6Q7.js} +69 -69
- package/chunks/{managed-npm-update-KTATRLFZ.js → managed-npm-update-TCLLJP27.js} +66 -66
- package/chunks/{mcp-7GPEHQCC.js → mcp-LJGCW5XK.js} +74 -74
- package/chunks/{memory-side-query-EHR7FGIB.js → memory-side-query-BSVTSKCB.js} +7 -7
- package/chunks/{model-call-collection-J33XHZPS.js → model-call-collection-GCSBAMPN.js} +7 -7
- package/chunks/{model-configuration-BK2FWMLQ.js → model-configuration-N6EASNX6.js} +75 -75
- package/chunks/{modelConfigUtils-RHOU5ENH.js → modelConfigUtils-4O46TEAJ.js} +68 -68
- package/chunks/{monitor-WXQOHZKP.js → monitor-OOKJVU73.js} +42 -42
- package/chunks/{nonInteractiveCli-332KOMYW.js → nonInteractiveCli-6H3CIYOC.js} +113 -113
- package/chunks/{nonInteractiveToolExecutor-ULCR2RA7.js → nonInteractiveToolExecutor-MWOHBQ3G.js} +43 -43
- package/chunks/{notebook-edit-HOKY5D6F.js → notebook-edit-CG5ZTY7N.js} +43 -43
- package/chunks/{ocr-image-ZJCSAMSQ.js → ocr-image-O6ECQNUE.js} +6 -6
- package/chunks/{omni-O37J5JI3.js → omni-MWVCLGTK.js} +40 -42
- package/chunks/{openaiContentGenerator-62C5JSNE.js → openaiContentGenerator-XDEGXNI6.js} +62 -59
- package/chunks/{openaiResponsesContentGenerator-JSS3U575.js → openaiResponsesContentGenerator-KVV57AK6.js} +40 -24
- package/chunks/{processUtils-Y66KDZSB.js → processUtils-7C3HIK43.js} +2 -2
- package/chunks/{prompt-terminal-ledger-MXYMD7UN.js → prompt-terminal-ledger-R4EB443Y.js} +67 -67
- package/chunks/{pt-TCLIZMUA.js → pt-3QNK7RYA.js} +29 -2
- package/chunks/{reactive-degrade-X2JSVX5V.js → reactive-degrade-6JECKK6A.js} +25 -23
- package/chunks/{read-file-UDFSKNBI.js → read-file-2V6MBYCS.js} +12 -11
- package/chunks/{recall-media-memory-tool-GAR6JWON.js → recall-media-memory-tool-WIFEB2OG.js} +5 -5
- package/chunks/{record-artifact-PL2T5AJY.js → record-artifact-IVE6QT4D.js} +3 -3
- package/chunks/{record-source-ANENC5K7.js → record-source-U53PRXYX.js} +5 -5
- package/chunks/{report-findings-RFO53756.js → report-findings-W75NCFXY.js} +4 -4
- package/chunks/{request-shutdown-LRWCJOEG.js → request-shutdown-E5QAHOAD.js} +4 -4
- package/chunks/{resumeHistoryUtils-ABWHIQME.js → resumeHistoryUtils-OA7LAAZ7.js} +72 -72
- package/chunks/{ripGrep-RMGB52RM.js → ripGrep-KR5EE3HU.js} +14 -13
- package/chunks/{run-o1-code-serve-KVOUMETW.js → run-o1-code-serve-RJHXITCF.js} +59 -59
- package/chunks/{runtime-shell-policy-Y3TGAQLT.js → runtime-shell-policy-Z53GWNM7.js} +1 -1
- package/chunks/{sandboxConfig-6UNSTBMJ.js → sandboxConfig-Q5GADSBO.js} +66 -66
- package/chunks/{scheduled-tasks-SHFPXK6P.js → scheduled-tasks-6FXAE77O.js} +75 -75
- package/chunks/{scheduler-N5K2SUQE.js → scheduler-W5XJHCSY.js} +68 -68
- package/chunks/{sdk-exporters-grpc-J2ILXEUS.js → sdk-exporters-grpc-FZO3Y43M.js} +2 -2
- package/chunks/{sdk-exporters-http-HGBOA62S.js → sdk-exporters-http-WYXH2KE4.js} +14 -14
- package/chunks/{sdk-impl-G2NWB5MF.js → sdk-impl-5PWLMFPQ.js} +20 -20
- package/chunks/{send-message-QOWWQKZJ.js → send-message-OSS7K7GE.js} +4 -4
- package/chunks/{serve-RQIMY7MC.js → serve-PRXYYEOA.js} +74 -74
- package/chunks/{server-AA4LTEQ2.js → server-UBJ5BVPH.js} +260 -260
- package/chunks/{session-4627GZ4S.js → session-62TX2TX2.js} +122 -122
- package/chunks/session-WTJAKLQE.js +34 -0
- package/chunks/{session-attachments-root-Y4BXBBHX.js → session-attachments-root-FW2TX3FF.js} +66 -66
- package/chunks/{session-pr-refresh-XXIT42IO.js → session-pr-refresh-R2Y6QHL7.js} +75 -75
- package/chunks/{settings-CHJS242C.js → settings-HJODN63R.js} +74 -74
- package/chunks/{shell-PDZIKVVS.js → shell-P3L2UMOI.js} +42 -42
- package/chunks/{skill-QOPISSJO.js → skill-3PV7IPAH.js} +14 -13
- package/chunks/{skill-settings-SSXT3JNN.js → skill-settings-TSXFLSXL.js} +74 -74
- package/chunks/{spawnChannel-MPL2AUYX.js → spawnChannel-VV2PWHJ2.js} +69 -69
- package/chunks/{standalone-update-ACWQFW36.js → standalone-update-4BRRDDO4.js} +69 -69
- package/chunks/{startInteractiveUI-PKMNIOMH.js → startInteractiveUI-KUDCHKGV.js} +446 -354
- package/chunks/{task-create-2BHPJUF3.js → task-create-WLFSUWNH.js} +5 -5
- package/chunks/{task-update-NGKKBFU5.js → task-update-STSLD6C5.js} +5 -5
- package/chunks/{team-create-PDT7MW4F.js → team-create-DXTHTZKI.js} +42 -42
- package/chunks/{team-plan-approval-4CFTZI2I.js → team-plan-approval-22HFWRXC.js} +42 -42
- package/chunks/{terminal-image-renderer-C65IVJHI.js → terminal-image-renderer-YBRDENRT.js} +66 -66
- package/chunks/{theme-manager-5FF3ROXZ.js → theme-manager-3GMAH5RD.js} +66 -66
- package/chunks/{tool-call-GVZHLCX6.js → tool-call-2R6GT3ZZ.js} +5 -5
- package/chunks/{tool-result-media-YFEIOZDR.js → tool-result-media-TPGTYKAD.js} +21 -23
- package/chunks/{tool-search-WNOVTFBP.js → tool-search-JYV3OQ6L.js} +19 -18
- package/chunks/{total-session-admission-Q6NUZVLI.js → total-session-admission-A27OSZFH.js} +71 -71
- package/chunks/{transcribe-audio-J3OVKT5D.js → transcribe-audio-R6YC24KU.js} +3 -3
- package/chunks/{trustedFolders-SFZUU4GO.js → trustedFolders-OVHIYR7G.js} +68 -68
- package/chunks/{understand-video-segments-OMAJP6Q7.js → understand-video-segments-2WK4T6PO.js} +8 -8
- package/chunks/{update-relaunch-ICPN37DV.js → update-relaunch-NJNXJ7YF.js} +5 -5
- package/chunks/{updateCheck-PRCH4Y2X.js → updateCheck-XCRLLMAH.js} +69 -69
- package/chunks/{upload-config-JG34DU4J.js → upload-config-QSD3T2AU.js} +10 -8
- package/chunks/{useAutoAcceptIndicator-FZVG3NNO.js → useAutoAcceptIndicator-PJMRWE6O.js} +77 -77
- package/chunks/{validateNonInterActiveAuth-XRY2O6CQ.js → validateNonInterActiveAuth-SD677WKS.js} +110 -110
- package/chunks/{version-IU45BCJE.js → version-FD6BAJ7Z.js} +1 -1
- package/chunks/{web-fetch-WO3GHRIO.js → web-fetch-4RUI7Y7U.js} +14 -16
- package/chunks/{web-search-4GJLGZQM.js → web-search-ROO2Y3AU.js} +12 -13
- package/chunks/web-shell-static-OOIIII74.js +1 -1
- package/chunks/{workflow-UG4V5VWR.js → workflow-OTLXZKSM.js} +45 -45
- package/chunks/{workspace-providers-status-IM5IUQXM.js → workspace-providers-status-E7XZW55K.js} +80 -80
- package/chunks/{workspace-registration-store-O36O7FTL.js → workspace-registration-store-NTRX2N6W.js} +1 -1
- package/chunks/{workspace-registry-TOE2NYGH.js → workspace-registry-OKP2RI5C.js} +71 -71
- package/chunks/{workspace-runtime-coordinator-LS7MYSDY.js → workspace-runtime-coordinator-774UWQBB.js} +72 -72
- package/chunks/{workspace-service-FL63YNEM.js → workspace-service-C3BQFCAC.js} +80 -80
- package/chunks/{workspace-skills-status-IXHCVUXS.js → workspace-skills-status-GJFMBOCP.js} +77 -77
- package/chunks/{workspace-trust-reconciler-WTJ52TKZ.js → workspace-trust-reconciler-3V6TVY5S.js} +80 -80
- package/chunks/{write-file-VTHULBGP.js → write-file-CE74OWAB.js} +45 -45
- package/chunks/{zh-KRQU675V.js → zh-GBDJHFG4.js} +1 -0
- package/chunks/{zh-TW-K657U7LC.js → zh-TW-WWN26WO4.js} +1 -0
- package/chunks/{zoom-image-TJNJJATC.js → zoom-image-EK6GNK4H.js} +12 -11
- package/cli.js +14 -14
- package/execution-worker.js +1 -1
- package/export-transcript-document.js +1 -1
- package/locales/en.js +46 -2
- package/locales/pt.js +46 -2
- package/locales/zh-TW.js +2 -0
- package/locales/zh.js +2 -0
- package/package.json +2 -2
- package/release-notes.json +13 -0
- package/web-shell/assets/{abnfDiagram-VCTEODGH-anMh0rnI.js → abnfDiagram-VCTEODGH-B1-jzC8p.js} +1 -1
- package/web-shell/assets/{arc-sPYpPSws.js → arc-CSOZi0KQ.js} +1 -1
- package/web-shell/assets/{architectureDiagram-5GKGNRK7-BKh0KCFR.js → architectureDiagram-5GKGNRK7-CPkIznhq.js} +1 -1
- package/web-shell/assets/{blockDiagram-NRAW4CY4-CUfywffW.js → blockDiagram-NRAW4CY4-B4CmWPlm.js} +1 -1
- package/web-shell/assets/{c4Diagram-UCG6FXSJ-CM2pLl5w.js → c4Diagram-UCG6FXSJ-CfbSKaAp.js} +1 -1
- package/web-shell/assets/channel-DtvUnSzo.js +1 -0
- package/web-shell/assets/{chunk-2Q5K7J3B-n9OTAsCE.js → chunk-2Q5K7J3B-DeXG-4SQ.js} +1 -1
- package/web-shell/assets/{chunk-5VM5RSS4-CC3dLmo9.js → chunk-5VM5RSS4-C8diV_rJ.js} +1 -1
- package/web-shell/assets/{chunk-F27PBJKO-pIte4woe.js → chunk-F27PBJKO-Dfz0hM4f.js} +1 -1
- package/web-shell/assets/{chunk-G27WJ6UU-Bd4q9Va0.js → chunk-G27WJ6UU-BE_M2PYM.js} +1 -1
- package/web-shell/assets/{chunk-JWPE2WC7-C1ornOGN.js → chunk-JWPE2WC7-CLsWUvS5.js} +1 -1
- package/web-shell/assets/{chunk-LCL6LL3I-w09p9W09.js → chunk-LCL6LL3I-DeyqYZUf.js} +1 -1
- package/web-shell/assets/{chunk-POPQ4Y6H-CHY7aAx4.js → chunk-POPQ4Y6H-C-6z-5n3.js} +1 -1
- package/web-shell/assets/{chunk-SVP7TREG-DKqS6ObW.js → chunk-SVP7TREG-DdwfG0ad.js} +1 -1
- package/web-shell/assets/{chunk-XXDRQBXY-DWqNHE5T.js → chunk-XXDRQBXY-X9aD75Ws.js} +1 -1
- package/web-shell/assets/classDiagram-DTDB5LWJ-B4Ulnn_s.js +1 -0
- package/web-shell/assets/classDiagram-v2-JRS7N3AN-B4Ulnn_s.js +1 -0
- package/web-shell/assets/{cose-bilkent-JH36ORCC-CTuORHW-.js → cose-bilkent-JH36ORCC-DNp5iXJM.js} +1 -1
- package/web-shell/assets/{cynefin-OW5HDTMX-D38bIRGV.js → cynefin-OW5HDTMX-DhhzkPLD.js} +1 -1
- package/web-shell/assets/{cynefinDiagram-5FMLGOSQ-zojG5SHE.js → cynefinDiagram-5FMLGOSQ-5eATS_mD.js} +1 -1
- package/web-shell/assets/{dagre-3AP2YEHR-B_mgdgxG.js → dagre-3AP2YEHR-DXUm0J5e.js} +1 -1
- package/web-shell/assets/{diagram-S7CK7UJ4-Boe4tqaP.js → diagram-S7CK7UJ4-DNrRshkn.js} +1 -1
- package/web-shell/assets/{diagram-UQ7AKVKN-DEFdgAk5.js → diagram-UQ7AKVKN-DS2yugJl.js} +1 -1
- package/web-shell/assets/{diagram-VSXAHHWV-lg-aIy2H.js → diagram-VSXAHHWV-Ds2io9aQ.js} +1 -1
- package/web-shell/assets/{diagram-VX7I27RA-B8GfIB8O.js → diagram-VX7I27RA-CGa-K6oj.js} +1 -1
- package/web-shell/assets/{diagram-Z3DM3KII-B9d7kR6t.js → diagram-Z3DM3KII-BW68o26L.js} +1 -1
- package/web-shell/assets/{ebnfDiagram-PWID7BFC-BbKPC4Lq.js → ebnfDiagram-PWID7BFC-BC4P1K1r.js} +1 -1
- package/web-shell/assets/{erDiagram-SSCWMZ5O-CRAgoOe5.js → erDiagram-SSCWMZ5O-CLs5DpAu.js} +1 -1
- package/web-shell/assets/{flowDiagram-A5DVABFB-Ur3ln_JM.js → flowDiagram-A5DVABFB-D76URsL1.js} +1 -1
- package/web-shell/assets/{ganttDiagram-EL5Y4UJY-C9BN2b5E.js → ganttDiagram-EL5Y4UJY-xVE4LPqO.js} +1 -1
- package/web-shell/assets/{gitGraphDiagram-WWUBYQGX-CRfkh-0v.js → gitGraphDiagram-WWUBYQGX-CIRl0tk3.js} +1 -1
- package/web-shell/assets/{index-DiA0wMqK.js → index-Bv4OUm3o.js} +1 -1
- package/web-shell/assets/{index-CeQlCdwD.js → index-KZq_3onf.js} +3 -3
- package/web-shell/assets/{infoDiagram-RXCK75RN-DK1-ESA4.js → infoDiagram-RXCK75RN-CBnubrlE.js} +1 -1
- package/web-shell/assets/{ishikawaDiagram-5VMMS53U-kxpIDlCX.js → ishikawaDiagram-5VMMS53U-qK-V6061.js} +1 -1
- package/web-shell/assets/{journeyDiagram-EYS64GPL-DQ9EbXpv.js → journeyDiagram-EYS64GPL-UUJYez4X.js} +1 -1
- package/web-shell/assets/{kanban-definition-3QL26DDD-UYveUTce.js → kanban-definition-3QL26DDD-BCcGvRtp.js} +1 -1
- package/web-shell/assets/{layout-BPonaHzT.js → layout-CSFWOY9q.js} +1 -1
- package/web-shell/assets/{linear-C-eJXPTS.js → linear-WUzB_hII.js} +1 -1
- package/web-shell/assets/{mermaid.core-BX1Kz_A0.js → mermaid.core-eLc_GDkM.js} +6 -6
- package/web-shell/assets/{mindmap-definition-FBJOCRG2-B6QyHOu0.js → mindmap-definition-FBJOCRG2-BfzdSe6H.js} +1 -1
- package/web-shell/assets/{pegDiagram-XKGWAZYB-Caflqhs8.js → pegDiagram-XKGWAZYB-B1kr5DxT.js} +1 -1
- package/web-shell/assets/{pieDiagram-E7YTZNPT-6zzkWzNt.js → pieDiagram-E7YTZNPT-CPFq2ZOW.js} +1 -1
- package/web-shell/assets/{quadrantDiagram-AXDQQJYC-BVL3cBz2.js → quadrantDiagram-AXDQQJYC-DuZmXZaP.js} +1 -1
- package/web-shell/assets/{railroadDiagram-O6MQD6OU-DqBEO3-Z.js → railroadDiagram-O6MQD6OU-DCjBx5S1.js} +1 -1
- package/web-shell/assets/{requirementDiagram-EFPCY7ZU-CBFGB-6Q.js → requirementDiagram-EFPCY7ZU-DISKvzR7.js} +1 -1
- package/web-shell/assets/{sankeyDiagram-P5KCCOFB-DOpW98KL.js → sankeyDiagram-P5KCCOFB-BugqzyC1.js} +1 -1
- package/web-shell/assets/{sequenceDiagram-WJ2MYXX4-CVtDqArI.js → sequenceDiagram-WJ2MYXX4-DLQyc2mn.js} +1 -1
- package/web-shell/assets/{sizeCapture-X5ZJPWSS-DQjMKuHP.js → sizeCapture-X5ZJPWSS-CDbelLiH.js} +1 -1
- package/web-shell/assets/{stateDiagram-HBIQ2CUA-D4n_VP2j.js → stateDiagram-HBIQ2CUA-Bw4uH6Y6.js} +1 -1
- package/web-shell/assets/stateDiagram-v2-4QOOHH4V-UrUNUyaU.js +1 -0
- package/web-shell/assets/{swimlanes-XN3QIQJK-Dvr4Gk5J.js → swimlanes-XN3QIQJK-BT-heLFQ.js} +1 -1
- package/web-shell/assets/swimlanesDiagram-VK2B7HYN-E8v9mXaE.js +8 -0
- package/web-shell/assets/{timeline-definition-24CTP7MA-DXsPhzEF.js → timeline-definition-24CTP7MA-ORqZ_woY.js} +1 -1
- package/web-shell/assets/{vennDiagram-4TSXK5OY-aA8X42UU.js → vennDiagram-4TSXK5OY-AgffOwo5.js} +1 -1
- package/web-shell/assets/{wardleyDiagram-VM6X3IG4-CuOx8tlj.js → wardleyDiagram-VM6X3IG4-CcqvB4mV.js} +1 -1
- package/web-shell/assets/{xychartDiagram-S5SC5T6Z-DIc2TzVm.js → xychartDiagram-S5SC5T6Z-BQBX00-a.js} +1 -1
- package/web-shell/index.html +1 -1
- package/web-shell/sw.js +1 -1
- package/chunks/chunk-7USF7X6U.js +0 -584
- package/chunks/chunk-ALIO4STD.js +0 -340
- package/chunks/chunk-KTOCX7IM.js +0 -27
- package/chunks/chunk-MWFHMYVA.js +0 -93
- package/chunks/chunk-PFRCGQAE.js +0 -48
- package/chunks/chunk-VIAJ74X3.js +0 -410
- package/web-shell/assets/channel-DRltBDks.js +0 -1
- package/web-shell/assets/classDiagram-DTDB5LWJ-Bs6uTgrQ.js +0 -1
- package/web-shell/assets/classDiagram-v2-JRS7N3AN-Bs6uTgrQ.js +0 -1
- package/web-shell/assets/stateDiagram-v2-4QOOHH4V-Cy-H3uc5.js +0 -1
- package/web-shell/assets/swimlanesDiagram-VK2B7HYN-BmzgGPpR.js +0 -8
- package/chunks/{chunk-BWRP5ZMP.js → chunk-DQZYZDIL.js} +3 -3
- package/chunks/{chunk-KV5QCRFB.js → chunk-IC57EPC7.js} +3 -3
- package/chunks/{chunk-YJLJUKJ7.js → chunk-IZSIFV6S.js} +3 -3
- package/chunks/{chunk-H5L5E5NI.js → chunk-ROO6F2BT.js} +3 -3
- package/chunks/{chunk-MDU4BZLN.js → chunk-YSZCVSC3.js} +0 -0
- package/chunks/{clip-audio-G2SG55MB.js → clip-audio-BJ42NS5B.js} +4 -4
- package/chunks/{clip-image-I3IFRW4Z.js → clip-image-FDBAIKGB.js} +3 -3
- package/chunks/{clip-video-XISLKGWO.js → clip-video-6KVC2IFL.js} +4 -4
- package/chunks/{convert-image-TXAOAPLS.js → convert-image-AYXBZ3TH.js} +3 -3
- package/chunks/{downsample-audio-SBMOSASE.js → downsample-audio-HSPMS25U.js} +4 -4
- package/chunks/{downsample-image-S5ZKCZEM.js → downsample-image-BFBX343I.js} +3 -3
- package/chunks/{downscale-video-LFLD2MYN.js → downscale-video-GQ5ZMJRX.js} +4 -4
- package/chunks/{extract-audio-TY277NLN.js → extract-audio-3LB2Q736.js} +4 -4
- package/chunks/{extract-keyframes-CXPABWEU.js → extract-keyframes-6ZP65BJF.js} +4 -4
|
@@ -0,0 +1,2008 @@
|
|
|
1
|
+
// Force strict mode and setup for ESM
|
|
2
|
+
"use strict";
|
|
3
|
+
import {
|
|
4
|
+
atomicWriteFile
|
|
5
|
+
} from "./chunk-EPJAMNLS.js";
|
|
6
|
+
import {
|
|
7
|
+
BaseTokenStorage
|
|
8
|
+
} from "./chunk-7TK6OY2J.js";
|
|
9
|
+
import {
|
|
10
|
+
Storage
|
|
11
|
+
} from "./chunk-S3HSUBBN.js";
|
|
12
|
+
import {
|
|
13
|
+
init_esbuild_shims
|
|
14
|
+
} from "./chunk-S2467S6L.js";
|
|
15
|
+
import {
|
|
16
|
+
__name
|
|
17
|
+
} from "./chunk-J2S4EL5Y.js";
|
|
18
|
+
|
|
19
|
+
// packages/core/src/providers/o1-connect/session.ts
|
|
20
|
+
init_esbuild_shims();
|
|
21
|
+
|
|
22
|
+
// packages/core/vendor/o1-connect/lib.mjs
|
|
23
|
+
init_esbuild_shims();
|
|
24
|
+
import { createHash } from "node:crypto";
|
|
25
|
+
import tls3 from "node:tls";
|
|
26
|
+
import { randomBytes as randomBytes2 } from "node:crypto";
|
|
27
|
+
import http3 from "node:http";
|
|
28
|
+
import https2 from "node:https";
|
|
29
|
+
import { isIP as isIP2 } from "node:net";
|
|
30
|
+
import { Duplex as Duplex2 } from "node:stream";
|
|
31
|
+
import tls2 from "node:tls";
|
|
32
|
+
import http from "node:http";
|
|
33
|
+
import { createHash as createHash2, randomBytes } from "node:crypto";
|
|
34
|
+
import http2 from "node:http";
|
|
35
|
+
import https from "node:https";
|
|
36
|
+
import { isIP } from "node:net";
|
|
37
|
+
import { Duplex } from "node:stream";
|
|
38
|
+
import tls from "node:tls";
|
|
39
|
+
import { createHash as createHash3, randomBytes as randomBytes3, timingSafeEqual } from "node:crypto";
|
|
40
|
+
import http5 from "node:http";
|
|
41
|
+
import http4 from "node:http";
|
|
42
|
+
import http6 from "node:http";
|
|
43
|
+
import tls4 from "node:tls";
|
|
44
|
+
var TUNNEL_PATH = "/tunnel";
|
|
45
|
+
var TUNNEL_PINS_PATH = "/tunnel/pins";
|
|
46
|
+
var TUNNEL_PROTOCOL = "o1gw-tunnel.v1";
|
|
47
|
+
var TUNNEL_SERVERNAME = "o1gw-tunnel";
|
|
48
|
+
var CODE_PREFIX = "o1gw1.";
|
|
49
|
+
var PIN = /^sha256\/[A-Za-z0-9+/]{43}=$/;
|
|
50
|
+
var TOKEN = /^[0-9a-f]{64}$/;
|
|
51
|
+
var DEVICE = /^[a-z0-9][a-z0-9_-]{0,31}$/i;
|
|
52
|
+
var MAX_PINS = 4;
|
|
53
|
+
var MAX_HOSTS = 4;
|
|
54
|
+
var ConnectionCodeError = class extends Error {
|
|
55
|
+
static {
|
|
56
|
+
__name(this, "ConnectionCodeError");
|
|
57
|
+
}
|
|
58
|
+
};
|
|
59
|
+
function pinOfSpki(spkiDer) {
|
|
60
|
+
return `sha256/${createHash("sha256").update(spkiDer).digest("base64")}`;
|
|
61
|
+
}
|
|
62
|
+
__name(pinOfSpki, "pinOfSpki");
|
|
63
|
+
function isPin(s) {
|
|
64
|
+
return PIN.test(s);
|
|
65
|
+
}
|
|
66
|
+
__name(isPin, "isPin");
|
|
67
|
+
function fingerprint(pin) {
|
|
68
|
+
if (!isPin(pin)) throw new ConnectionCodeError("not a pin");
|
|
69
|
+
const hex = Buffer.from(pin.slice("sha256/".length), "base64").toString("hex").toUpperCase();
|
|
70
|
+
return (hex.match(/.{4}/g) ?? []).join("-");
|
|
71
|
+
}
|
|
72
|
+
__name(fingerprint, "fingerprint");
|
|
73
|
+
function sniOfPin(pin) {
|
|
74
|
+
if (!isPin(pin)) throw new ConnectionCodeError("not a pin");
|
|
75
|
+
return `k-${Buffer.from(pin.slice("sha256/".length), "base64").toString("hex").slice(0, 20)}.o1gw.invalid`;
|
|
76
|
+
}
|
|
77
|
+
__name(sniOfPin, "sniOfPin");
|
|
78
|
+
function isLoopbackHost(hostname2) {
|
|
79
|
+
return hostname2 === "127.0.0.1" || hostname2 === "localhost" || hostname2 === "[::1]";
|
|
80
|
+
}
|
|
81
|
+
__name(isLoopbackHost, "isLoopbackHost");
|
|
82
|
+
function originOf(value, what) {
|
|
83
|
+
let url;
|
|
84
|
+
try {
|
|
85
|
+
url = new URL(value);
|
|
86
|
+
} catch {
|
|
87
|
+
throw new ConnectionCodeError(`the proxy ${what} is not a URL`);
|
|
88
|
+
}
|
|
89
|
+
if (!(url.protocol === "https:" || url.protocol === "http:" && isLoopbackHost(url.hostname))) {
|
|
90
|
+
throw new ConnectionCodeError(`the proxy ${what} must be https`);
|
|
91
|
+
}
|
|
92
|
+
if (url.pathname !== "/" || url.search || url.hash || url.username || url.password) {
|
|
93
|
+
throw new ConnectionCodeError(`the proxy ${what} must be the proxy's base URL`);
|
|
94
|
+
}
|
|
95
|
+
return url.origin;
|
|
96
|
+
}
|
|
97
|
+
__name(originOf, "originOf");
|
|
98
|
+
function validateCode(c) {
|
|
99
|
+
if (!c || typeof c !== "object") throw new ConnectionCodeError("the code does not hold a connection");
|
|
100
|
+
const o = c;
|
|
101
|
+
const url = originOf(String(o.url), "URL");
|
|
102
|
+
if (typeof o.device !== "string" || !DEVICE.test(o.device)) throw new ConnectionCodeError("the device name is not valid");
|
|
103
|
+
if (!Array.isArray(o.pins) || o.pins.length === 0 || o.pins.length > MAX_PINS || !o.pins.every((p) => typeof p === "string" && isPin(p))) {
|
|
104
|
+
throw new ConnectionCodeError("the pin list is not valid");
|
|
105
|
+
}
|
|
106
|
+
if (typeof o.token !== "string" || !TOKEN.test(o.token)) throw new ConnectionCodeError("the device token is not valid");
|
|
107
|
+
let urls;
|
|
108
|
+
if (o.urls !== void 0) {
|
|
109
|
+
if (!Array.isArray(o.urls) || o.urls.length === 0 || o.urls.some((u) => typeof u !== "string")) throw new ConnectionCodeError("the failover host list is not valid");
|
|
110
|
+
urls = o.urls.map((u, i) => originOf(u, `failover host ${i + 1}`));
|
|
111
|
+
const all = [url, ...urls];
|
|
112
|
+
if (new Set(all).size !== all.length) throw new ConnectionCodeError("the failover host list repeats a host");
|
|
113
|
+
if (all.length > MAX_HOSTS) throw new ConnectionCodeError(`the code names more than ${MAX_HOSTS} hosts`);
|
|
114
|
+
}
|
|
115
|
+
return { url, ...urls === void 0 ? {} : { urls }, device: o.device, pins: [...o.pins], token: o.token };
|
|
116
|
+
}
|
|
117
|
+
__name(validateCode, "validateCode");
|
|
118
|
+
function decodeConnectionCode(s) {
|
|
119
|
+
const text = s.trim();
|
|
120
|
+
if (!text.startsWith(CODE_PREFIX)) throw new ConnectionCodeError("this is not an o1-connect code (it starts with o1gw1.)");
|
|
121
|
+
let parsed;
|
|
122
|
+
try {
|
|
123
|
+
parsed = JSON.parse(Buffer.from(text.slice(CODE_PREFIX.length), "base64url").toString("utf8"));
|
|
124
|
+
} catch {
|
|
125
|
+
throw new ConnectionCodeError("the code is damaged: copy it again from the console");
|
|
126
|
+
}
|
|
127
|
+
return validateCode(parsed);
|
|
128
|
+
}
|
|
129
|
+
__name(decodeConnectionCode, "decodeConnectionCode");
|
|
130
|
+
var seen = /* @__PURE__ */ new WeakMap();
|
|
131
|
+
function peerCertificate(socket) {
|
|
132
|
+
const kept = seen.get(socket);
|
|
133
|
+
if (kept) return kept;
|
|
134
|
+
const cert = socket.getPeerX509Certificate();
|
|
135
|
+
if (cert) seen.set(socket, cert);
|
|
136
|
+
return cert;
|
|
137
|
+
}
|
|
138
|
+
__name(peerCertificate, "peerCertificate");
|
|
139
|
+
var POLL_PATH = "/tunnel/poll";
|
|
140
|
+
var POLL_VERSION = 1;
|
|
141
|
+
var SESSION_BYTES = 32;
|
|
142
|
+
var MAX_UP = 256 * 1024;
|
|
143
|
+
var MAX_DOWN = 1024 * 1024;
|
|
144
|
+
var POLL_WAIT_MS = 2e4;
|
|
145
|
+
var TUNNEL_BUSY = "tunnel_busy";
|
|
146
|
+
var REQUEST_HEAD = 1 + SESSION_BYTES + 8 + 8 + 1;
|
|
147
|
+
var RESPONSE_HEAD = 1 + SESSION_BYTES + 8 + 1;
|
|
148
|
+
var FLAG_CLOSE = 1;
|
|
149
|
+
var FLAG_POLL = 2;
|
|
150
|
+
var FLAG_OPEN = 4;
|
|
151
|
+
var FLAG_CLOSED = 1;
|
|
152
|
+
var PollFormatError = class extends Error {
|
|
153
|
+
static {
|
|
154
|
+
__name(this, "PollFormatError");
|
|
155
|
+
}
|
|
156
|
+
};
|
|
157
|
+
var ZERO_SESSION = Buffer.alloc(SESSION_BYTES);
|
|
158
|
+
function sessionOf(id) {
|
|
159
|
+
if (id.length !== SESSION_BYTES) throw new PollFormatError("the session id must be 32 bytes");
|
|
160
|
+
return id;
|
|
161
|
+
}
|
|
162
|
+
__name(sessionOf, "sessionOf");
|
|
163
|
+
function requestSession(id) {
|
|
164
|
+
if (sessionOf(id).equals(ZERO_SESSION)) throw new PollFormatError("the session id must not be all zero");
|
|
165
|
+
return id;
|
|
166
|
+
}
|
|
167
|
+
__name(requestSession, "requestSession");
|
|
168
|
+
function toU64(n, what) {
|
|
169
|
+
if (!Number.isSafeInteger(n) || n < 0) throw new PollFormatError(`${what} is out of range`);
|
|
170
|
+
return BigInt(n);
|
|
171
|
+
}
|
|
172
|
+
__name(toU64, "toU64");
|
|
173
|
+
function fromU64(b, at, what) {
|
|
174
|
+
const v = b.readBigUInt64BE(at);
|
|
175
|
+
if (v > BigInt(Number.MAX_SAFE_INTEGER)) throw new PollFormatError(`${what} is out of range`);
|
|
176
|
+
return Number(v);
|
|
177
|
+
}
|
|
178
|
+
__name(fromU64, "fromU64");
|
|
179
|
+
function encodeRequest(r) {
|
|
180
|
+
if (r.data.length > MAX_UP) throw new PollFormatError("the request carries too much data");
|
|
181
|
+
const head = Buffer.alloc(REQUEST_HEAD);
|
|
182
|
+
head[0] = POLL_VERSION;
|
|
183
|
+
requestSession(r.session).copy(head, 1);
|
|
184
|
+
head.writeBigUInt64BE(toU64(r.upOffset, "upOffset"), 33);
|
|
185
|
+
head.writeBigUInt64BE(toU64(r.ack, "ack"), 41);
|
|
186
|
+
head[49] = (r.close ? FLAG_CLOSE : 0) | (r.poll ? FLAG_POLL : 0) | (r.open ? FLAG_OPEN : 0);
|
|
187
|
+
return Buffer.concat([head, r.data]);
|
|
188
|
+
}
|
|
189
|
+
__name(encodeRequest, "encodeRequest");
|
|
190
|
+
function decodeResponse(b) {
|
|
191
|
+
if (b.length < RESPONSE_HEAD) throw new PollFormatError("the answer is too short");
|
|
192
|
+
if (b[0] !== POLL_VERSION) throw new PollFormatError("the answer is not a tunnel's");
|
|
193
|
+
if (b.length - RESPONSE_HEAD > MAX_DOWN) throw new PollFormatError("the answer carries too much data");
|
|
194
|
+
return {
|
|
195
|
+
session: Buffer.from(b.subarray(1, 33)),
|
|
196
|
+
downOffset: fromU64(b, 33, "downOffset"),
|
|
197
|
+
closed: (b[41] & FLAG_CLOSED) !== 0,
|
|
198
|
+
data: Buffer.from(b.subarray(RESPONSE_HEAD))
|
|
199
|
+
};
|
|
200
|
+
}
|
|
201
|
+
__name(decodeResponse, "decodeResponse");
|
|
202
|
+
var LOOPBACK = /^(localhost|127(?:\.\d{1,3}){3}|::1)$/i;
|
|
203
|
+
function bypassed(host, list) {
|
|
204
|
+
return list.split(",").map((s) => s.trim().toLowerCase()).filter(Boolean).some((entry) => entry === "*" || entry === host || entry.startsWith(".") && host.endsWith(entry));
|
|
205
|
+
}
|
|
206
|
+
__name(bypassed, "bypassed");
|
|
207
|
+
function proxyFor(target, env = process.env) {
|
|
208
|
+
const host = target.hostname.replace(/^\[|\]$/g, "").toLowerCase();
|
|
209
|
+
if (LOOPBACK.test(host)) return null;
|
|
210
|
+
const raw = (env.HTTPS_PROXY || env.https_proxy || "").trim();
|
|
211
|
+
if (!raw) return null;
|
|
212
|
+
if (bypassed(host, env.NO_PROXY || env.no_proxy || "")) return null;
|
|
213
|
+
let url;
|
|
214
|
+
try {
|
|
215
|
+
url = new URL(raw.includes("://") ? raw : `http://${raw}`);
|
|
216
|
+
} catch {
|
|
217
|
+
throw new Error("HTTPS_PROXY is not a valid URL");
|
|
218
|
+
}
|
|
219
|
+
if (url.protocol !== "http:") throw new Error("HTTPS_PROXY must be an http:// proxy URL");
|
|
220
|
+
const user = decodeURIComponent(url.username);
|
|
221
|
+
const pass = decodeURIComponent(url.password);
|
|
222
|
+
return {
|
|
223
|
+
host: url.hostname.replace(/^\[|\]$/g, ""),
|
|
224
|
+
port: Number(url.port || 80),
|
|
225
|
+
...user || pass ? { authorization: `Basic ${Buffer.from(`${user}:${pass}`).toString("base64")}` } : {}
|
|
226
|
+
};
|
|
227
|
+
}
|
|
228
|
+
__name(proxyFor, "proxyFor");
|
|
229
|
+
var ProxyRefusedError = class extends Error {
|
|
230
|
+
static {
|
|
231
|
+
__name(this, "ProxyRefusedError");
|
|
232
|
+
}
|
|
233
|
+
constructor(status) {
|
|
234
|
+
super(status === 407 ? "the network's proxy needs credentials (HTTP 407): put them in HTTPS_PROXY as http://user:password@host:port" : `the network's proxy refused the connection (HTTP ${status})`);
|
|
235
|
+
this.status = status;
|
|
236
|
+
}
|
|
237
|
+
status;
|
|
238
|
+
};
|
|
239
|
+
function connectVia(route, host, port) {
|
|
240
|
+
const authority = host.includes(":") ? `[${host}]:${port}` : `${host}:${port}`;
|
|
241
|
+
const req = http.request({
|
|
242
|
+
host: route.host,
|
|
243
|
+
port: route.port,
|
|
244
|
+
method: "CONNECT",
|
|
245
|
+
path: authority,
|
|
246
|
+
agent: false,
|
|
247
|
+
headers: { Host: authority, ...route.authorization ? { "Proxy-Authorization": route.authorization } : {} }
|
|
248
|
+
});
|
|
249
|
+
const socket = new Promise((resolve, reject) => {
|
|
250
|
+
req.on("connect", (res, sock, head) => {
|
|
251
|
+
if (res.statusCode !== 200) {
|
|
252
|
+
sock.destroy();
|
|
253
|
+
reject(new ProxyRefusedError(res.statusCode ?? 0));
|
|
254
|
+
return;
|
|
255
|
+
}
|
|
256
|
+
if (head.length) sock.unshift(head);
|
|
257
|
+
resolve(sock);
|
|
258
|
+
});
|
|
259
|
+
req.on("response", (res) => {
|
|
260
|
+
res.resume();
|
|
261
|
+
reject(new ProxyRefusedError(res.statusCode ?? 0));
|
|
262
|
+
});
|
|
263
|
+
req.on("error", reject);
|
|
264
|
+
});
|
|
265
|
+
req.end();
|
|
266
|
+
return { socket, abort: /* @__PURE__ */ __name(() => req.destroy(), "abort") };
|
|
267
|
+
}
|
|
268
|
+
__name(connectVia, "connectVia");
|
|
269
|
+
var OP = { cont: 0, text: 1, binary: 2, close: 8, ping: 9, pong: 10 };
|
|
270
|
+
var WsProtocolError = class extends Error {
|
|
271
|
+
static {
|
|
272
|
+
__name(this, "WsProtocolError");
|
|
273
|
+
}
|
|
274
|
+
};
|
|
275
|
+
function encodeFrame(opcode, payload, mask, fin = true) {
|
|
276
|
+
const n = payload.length;
|
|
277
|
+
const b0 = (fin ? 128 : 0) | opcode;
|
|
278
|
+
const m = mask ? 128 : 0;
|
|
279
|
+
let head;
|
|
280
|
+
if (n < 126) head = Buffer.from([b0, m | n]);
|
|
281
|
+
else if (n < 65536) head = Buffer.from([b0, m | 126, n >> 8, n & 255]);
|
|
282
|
+
else {
|
|
283
|
+
head = Buffer.alloc(10);
|
|
284
|
+
head[0] = b0;
|
|
285
|
+
head[1] = m | 127;
|
|
286
|
+
head.writeBigUInt64BE(BigInt(n), 2);
|
|
287
|
+
}
|
|
288
|
+
if (!mask) return Buffer.concat([head, payload]);
|
|
289
|
+
const body = Buffer.from(payload);
|
|
290
|
+
for (let i = 0; i < body.length; i++) body[i] ^= mask[i & 3];
|
|
291
|
+
return Buffer.concat([head, mask, body]);
|
|
292
|
+
}
|
|
293
|
+
__name(encodeFrame, "encodeFrame");
|
|
294
|
+
var FrameParser = class {
|
|
295
|
+
static {
|
|
296
|
+
__name(this, "FrameParser");
|
|
297
|
+
}
|
|
298
|
+
constructor(maxPayload = 16 * 1024 * 1024) {
|
|
299
|
+
this.maxPayload = maxPayload;
|
|
300
|
+
}
|
|
301
|
+
maxPayload;
|
|
302
|
+
buf = Buffer.alloc(0);
|
|
303
|
+
push(chunk) {
|
|
304
|
+
this.buf = this.buf.length ? Buffer.concat([this.buf, chunk]) : chunk;
|
|
305
|
+
const out = [];
|
|
306
|
+
for (; ; ) {
|
|
307
|
+
if (this.buf.length < 2) return out;
|
|
308
|
+
const b0 = this.buf[0];
|
|
309
|
+
const b1 = this.buf[1];
|
|
310
|
+
if (b0 & 112) throw new WsProtocolError("reserved bits set");
|
|
311
|
+
if (b1 & 128) throw new WsProtocolError("a server frame must not be masked");
|
|
312
|
+
const opcode = b0 & 15;
|
|
313
|
+
const fin = (b0 & 128) !== 0;
|
|
314
|
+
let n = b1 & 127;
|
|
315
|
+
let off = 2;
|
|
316
|
+
if (n === 126) {
|
|
317
|
+
if (this.buf.length < 4) return out;
|
|
318
|
+
n = this.buf.readUInt16BE(2);
|
|
319
|
+
off = 4;
|
|
320
|
+
} else if (n === 127) {
|
|
321
|
+
if (this.buf.length < 10) return out;
|
|
322
|
+
const big = this.buf.readBigUInt64BE(2);
|
|
323
|
+
if (big > BigInt(this.maxPayload)) throw new WsProtocolError("frame too large");
|
|
324
|
+
n = Number(big);
|
|
325
|
+
off = 10;
|
|
326
|
+
}
|
|
327
|
+
if (n > this.maxPayload) throw new WsProtocolError("frame too large");
|
|
328
|
+
if (opcode >= 8 && (n > 125 || !fin)) throw new WsProtocolError("bad control frame");
|
|
329
|
+
if (this.buf.length < off + n) return out;
|
|
330
|
+
out.push({ fin, opcode, payload: this.buf.subarray(off, off + n) });
|
|
331
|
+
this.buf = this.buf.subarray(off + n);
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
};
|
|
335
|
+
var TunnelBlockedError = class extends Error {
|
|
336
|
+
static {
|
|
337
|
+
__name(this, "TunnelBlockedError");
|
|
338
|
+
}
|
|
339
|
+
constructor(status, detail) {
|
|
340
|
+
super(detail ? `tunnel blocked: ${detail}` : `tunnel blocked (HTTP ${status})`);
|
|
341
|
+
this.status = status;
|
|
342
|
+
}
|
|
343
|
+
status;
|
|
344
|
+
};
|
|
345
|
+
var GUID = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
|
|
346
|
+
function defaultCa() {
|
|
347
|
+
const get = tls.getCACertificates;
|
|
348
|
+
if (!get) return void 0;
|
|
349
|
+
try {
|
|
350
|
+
return [...get("default"), ...get("system")];
|
|
351
|
+
} catch {
|
|
352
|
+
return void 0;
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
__name(defaultCa, "defaultCa");
|
|
356
|
+
function openWebSocket(url, opts) {
|
|
357
|
+
const secure = url.protocol === "https:";
|
|
358
|
+
const key = randomBytes(16).toString("base64");
|
|
359
|
+
const hostname2 = url.hostname.replace(/^\[|\]$/g, "");
|
|
360
|
+
const port = Number(url.port || (secure ? 443 : 80));
|
|
361
|
+
return new Promise((resolve, reject) => {
|
|
362
|
+
let done = false;
|
|
363
|
+
let req;
|
|
364
|
+
const connecting = {};
|
|
365
|
+
const settle = /* @__PURE__ */ __name((err, value) => {
|
|
366
|
+
if (done) return;
|
|
367
|
+
done = true;
|
|
368
|
+
clearTimeout(timer);
|
|
369
|
+
if (err) reject(err);
|
|
370
|
+
else resolve(value);
|
|
371
|
+
}, "settle");
|
|
372
|
+
const timer = setTimeout(() => {
|
|
373
|
+
connecting.abort?.();
|
|
374
|
+
req?.destroy();
|
|
375
|
+
settle(new Error("tunnel connect timeout"));
|
|
376
|
+
}, opts.timeoutMs ?? 15e3);
|
|
377
|
+
timer.unref();
|
|
378
|
+
const common = {
|
|
379
|
+
host: hostname2,
|
|
380
|
+
port,
|
|
381
|
+
path: url.pathname,
|
|
382
|
+
method: "GET",
|
|
383
|
+
headers: {
|
|
384
|
+
Host: url.host,
|
|
385
|
+
Connection: "Upgrade",
|
|
386
|
+
Upgrade: "websocket",
|
|
387
|
+
"Sec-WebSocket-Version": "13",
|
|
388
|
+
"Sec-WebSocket-Key": key,
|
|
389
|
+
"Sec-WebSocket-Protocol": opts.protocol,
|
|
390
|
+
...opts.userAgent ? { "User-Agent": opts.userAgent } : {}
|
|
391
|
+
}
|
|
392
|
+
};
|
|
393
|
+
const outerTls = { ...isIP(hostname2) ? {} : { servername: hostname2 }, rejectUnauthorized: false, ca: opts.ca ?? defaultCa() };
|
|
394
|
+
const upgrade = /* @__PURE__ */ __name((tunnelled) => {
|
|
395
|
+
if (done) {
|
|
396
|
+
tunnelled?.destroy();
|
|
397
|
+
return;
|
|
398
|
+
}
|
|
399
|
+
const r = tunnelled ? http2.request({ ...common, createConnection: /* @__PURE__ */ __name(() => secure ? tls.connect({ ...outerTls, socket: tunnelled }) : tunnelled, "createConnection") }) : secure ? https.request({ ...common, ...outerTls, agent: false }) : http2.request({ ...common, agent: false });
|
|
400
|
+
req = r;
|
|
401
|
+
r.on("response", (res) => {
|
|
402
|
+
res.resume();
|
|
403
|
+
settle(new TunnelBlockedError(res.statusCode ?? 0));
|
|
404
|
+
});
|
|
405
|
+
r.on("error", (err) => settle(err));
|
|
406
|
+
r.on("upgrade", (res, sock, head) => {
|
|
407
|
+
const accept = createHash2("sha1").update(key + GUID).digest("base64");
|
|
408
|
+
if (done || res.headers["sec-websocket-accept"] !== accept || res.headers["sec-websocket-protocol"] !== opts.protocol) {
|
|
409
|
+
sock.destroy();
|
|
410
|
+
settle(new TunnelBlockedError(101));
|
|
411
|
+
return;
|
|
412
|
+
}
|
|
413
|
+
const tlsSock = sock;
|
|
414
|
+
const outer = !secure ? "plain" : tlsSock.authorized ? "verified" : "intercepted";
|
|
415
|
+
const outerError = secure && !tlsSock.authorized && tlsSock.authorizationError ? String(tlsSock.authorizationError) : void 0;
|
|
416
|
+
settle(null, { stream: wrap(sock, head, opts.pingMs ?? 3e4), outer, ...outerError ? { outerError } : {} });
|
|
417
|
+
});
|
|
418
|
+
r.end();
|
|
419
|
+
}, "upgrade");
|
|
420
|
+
let route;
|
|
421
|
+
try {
|
|
422
|
+
route = proxyFor(url, opts.env ?? process.env);
|
|
423
|
+
} catch (err) {
|
|
424
|
+
settle(err);
|
|
425
|
+
return;
|
|
426
|
+
}
|
|
427
|
+
if (!route) {
|
|
428
|
+
upgrade();
|
|
429
|
+
return;
|
|
430
|
+
}
|
|
431
|
+
const via = connectVia(route, hostname2, port);
|
|
432
|
+
connecting.abort = via.abort;
|
|
433
|
+
via.socket.then(upgrade, (err) => settle(err instanceof ProxyRefusedError ? new TunnelBlockedError(err.status, err.message) : err));
|
|
434
|
+
});
|
|
435
|
+
}
|
|
436
|
+
__name(openWebSocket, "openWebSocket");
|
|
437
|
+
function wrap(sock, head, pingMs) {
|
|
438
|
+
const parser = new FrameParser();
|
|
439
|
+
let closeSent = false;
|
|
440
|
+
const send2 = /* @__PURE__ */ __name((opcode, payload, cb) => {
|
|
441
|
+
sock.write(encodeFrame(opcode, payload, randomBytes(4)), cb);
|
|
442
|
+
}, "send2");
|
|
443
|
+
const duplex = new Duplex({
|
|
444
|
+
read() {
|
|
445
|
+
sock.resume();
|
|
446
|
+
},
|
|
447
|
+
write(chunk, _enc, cb) {
|
|
448
|
+
send2(OP.binary, chunk, cb);
|
|
449
|
+
},
|
|
450
|
+
final(cb) {
|
|
451
|
+
if (!closeSent) {
|
|
452
|
+
closeSent = true;
|
|
453
|
+
send2(OP.close, Buffer.from([3, 232]));
|
|
454
|
+
}
|
|
455
|
+
sock.end();
|
|
456
|
+
cb();
|
|
457
|
+
},
|
|
458
|
+
destroy(err, cb) {
|
|
459
|
+
clearInterval(pinger);
|
|
460
|
+
sock.destroy();
|
|
461
|
+
cb(err);
|
|
462
|
+
}
|
|
463
|
+
});
|
|
464
|
+
const failFrame = /* @__PURE__ */ __name((err) => {
|
|
465
|
+
setImmediate(() => duplex.destroy(err));
|
|
466
|
+
}, "failFrame");
|
|
467
|
+
const onData = /* @__PURE__ */ __name((chunk) => {
|
|
468
|
+
let frames;
|
|
469
|
+
try {
|
|
470
|
+
frames = parser.push(chunk);
|
|
471
|
+
} catch (err) {
|
|
472
|
+
failFrame(err instanceof WsProtocolError ? err : new WsProtocolError(String(err)));
|
|
473
|
+
return;
|
|
474
|
+
}
|
|
475
|
+
for (const f of frames) {
|
|
476
|
+
if (f.opcode === OP.binary || f.opcode === OP.cont) {
|
|
477
|
+
if (!duplex.push(Buffer.from(f.payload))) sock.pause();
|
|
478
|
+
} else if (f.opcode === OP.ping) {
|
|
479
|
+
send2(OP.pong, Buffer.from(f.payload));
|
|
480
|
+
} else if (f.opcode === OP.close) {
|
|
481
|
+
if (!closeSent) {
|
|
482
|
+
closeSent = true;
|
|
483
|
+
send2(OP.close, Buffer.from(f.payload.subarray(0, 2)));
|
|
484
|
+
}
|
|
485
|
+
duplex.push(null);
|
|
486
|
+
sock.end();
|
|
487
|
+
} else if (f.opcode === OP.text) {
|
|
488
|
+
failFrame(new WsProtocolError("text frame on a binary tunnel"));
|
|
489
|
+
}
|
|
490
|
+
}
|
|
491
|
+
}, "onData");
|
|
492
|
+
sock.on("data", onData);
|
|
493
|
+
sock.on("end", () => duplex.push(null));
|
|
494
|
+
sock.on("error", (err) => duplex.destroy(err));
|
|
495
|
+
sock.on("close", () => {
|
|
496
|
+
clearInterval(pinger);
|
|
497
|
+
if (!duplex.destroyed) duplex.destroy();
|
|
498
|
+
});
|
|
499
|
+
const pinger = setInterval(() => send2(OP.ping, Buffer.alloc(0)), pingMs);
|
|
500
|
+
pinger.unref();
|
|
501
|
+
if (head.length) onData(head);
|
|
502
|
+
return duplex;
|
|
503
|
+
}
|
|
504
|
+
__name(wrap, "wrap");
|
|
505
|
+
var SessionGoneError = class extends Error {
|
|
506
|
+
static {
|
|
507
|
+
__name(this, "SessionGoneError");
|
|
508
|
+
}
|
|
509
|
+
constructor() {
|
|
510
|
+
super("tunnel session gone");
|
|
511
|
+
}
|
|
512
|
+
};
|
|
513
|
+
var EMPTY = Buffer.alloc(0);
|
|
514
|
+
var MAX_ANSWER = RESPONSE_HEAD + MAX_DOWN;
|
|
515
|
+
var FATAL_STATUS = /* @__PURE__ */ new Set([400, 403, 429]);
|
|
516
|
+
var delay = /* @__PURE__ */ __name((ms) => new Promise((r) => setTimeout(r, ms)), "delay");
|
|
517
|
+
function isBusy(r) {
|
|
518
|
+
if (r.status !== 503 || r.body.length > 4096) return false;
|
|
519
|
+
try {
|
|
520
|
+
return JSON.parse(r.body.toString("utf8")).error?.type === TUNNEL_BUSY;
|
|
521
|
+
} catch {
|
|
522
|
+
return false;
|
|
523
|
+
}
|
|
524
|
+
}
|
|
525
|
+
__name(isBusy, "isBusy");
|
|
526
|
+
function settings(o) {
|
|
527
|
+
return { timeoutMs: o.timeoutMs ?? 15e3, waitMs: o.waitMs ?? POLL_WAIT_MS, retryForMs: o.retryForMs ?? 3e4, busyForMs: o.busyForMs ?? 3e5, coalesceMs: o.coalesceMs ?? 5 };
|
|
528
|
+
}
|
|
529
|
+
__name(settings, "settings");
|
|
530
|
+
function makeExchanger(base, o) {
|
|
531
|
+
const secure = base.protocol === "https:";
|
|
532
|
+
const hostname2 = base.hostname.replace(/^\[|\]$/g, "");
|
|
533
|
+
const port = Number(base.port || (secure ? 443 : 80));
|
|
534
|
+
const outerTls = { ...isIP2(hostname2) ? {} : { servername: hostname2 }, rejectUnauthorized: false, ca: o.ca ?? defaultCa() };
|
|
535
|
+
const route = proxyFor(base, o.env ?? process.env);
|
|
536
|
+
const connectMs = o.timeoutMs ?? 15e3;
|
|
537
|
+
const agent = secure ? new https2.Agent({ keepAlive: true, maxSockets: 2, ...outerTls }) : new http3.Agent({ keepAlive: true, maxSockets: 2 });
|
|
538
|
+
if (route) {
|
|
539
|
+
agent.createConnection = (_opts, cb) => {
|
|
540
|
+
const via = connectVia(route, hostname2, port);
|
|
541
|
+
let settled = false;
|
|
542
|
+
const once = /* @__PURE__ */ __name((err, sock) => {
|
|
543
|
+
if (settled) {
|
|
544
|
+
sock?.destroy();
|
|
545
|
+
return;
|
|
546
|
+
}
|
|
547
|
+
settled = true;
|
|
548
|
+
clearTimeout(timer);
|
|
549
|
+
cb(err, sock);
|
|
550
|
+
}, "once");
|
|
551
|
+
const timer = setTimeout(() => {
|
|
552
|
+
once(new Error("tunnel connect timeout"));
|
|
553
|
+
via.abort();
|
|
554
|
+
}, connectMs);
|
|
555
|
+
via.socket.then(
|
|
556
|
+
(sock) => once(null, secure ? tls2.connect({ ...outerTls, socket: sock }) : sock),
|
|
557
|
+
(err) => once(err instanceof ProxyRefusedError ? new TunnelBlockedError(err.status, err.message) : err)
|
|
558
|
+
);
|
|
559
|
+
return void 0;
|
|
560
|
+
};
|
|
561
|
+
}
|
|
562
|
+
const post = /* @__PURE__ */ __name((body, timeoutMs) => new Promise((resolve, reject) => {
|
|
563
|
+
const req = (secure ? https2 : http3).request({
|
|
564
|
+
host: hostname2,
|
|
565
|
+
port,
|
|
566
|
+
path: POLL_PATH,
|
|
567
|
+
method: "POST",
|
|
568
|
+
agent,
|
|
569
|
+
...secure ? outerTls : {},
|
|
570
|
+
headers: {
|
|
571
|
+
Host: base.host,
|
|
572
|
+
"Content-Type": "application/octet-stream",
|
|
573
|
+
"Content-Length": String(body.length),
|
|
574
|
+
...o.userAgent ? { "User-Agent": o.userAgent } : {}
|
|
575
|
+
}
|
|
576
|
+
}, (res) => {
|
|
577
|
+
const sock = res.socket;
|
|
578
|
+
const outer = !secure ? "plain" : sock?.authorized ? "verified" : "intercepted";
|
|
579
|
+
const outerError = secure && !sock?.authorized && sock?.authorizationError ? String(sock.authorizationError) : void 0;
|
|
580
|
+
const chunks = [];
|
|
581
|
+
let size = 0;
|
|
582
|
+
res.on("data", (c) => {
|
|
583
|
+
size += c.length;
|
|
584
|
+
if (size > MAX_ANSWER) {
|
|
585
|
+
req.destroy(new Error("tunnel answer too large"));
|
|
586
|
+
return;
|
|
587
|
+
}
|
|
588
|
+
chunks.push(c);
|
|
589
|
+
});
|
|
590
|
+
res.on("end", () => resolve({ status: res.statusCode ?? 0, body: Buffer.concat(chunks), outer, ...outerError ? { outerError } : {} }));
|
|
591
|
+
res.on("error", reject);
|
|
592
|
+
});
|
|
593
|
+
req.setTimeout(timeoutMs, () => req.destroy(new Error("tunnel exchange timeout")));
|
|
594
|
+
req.on("error", reject);
|
|
595
|
+
req.end(body);
|
|
596
|
+
}), "post");
|
|
597
|
+
return { post, destroy: /* @__PURE__ */ __name(() => agent.destroy(), "destroy") };
|
|
598
|
+
}
|
|
599
|
+
__name(makeExchanger, "makeExchanger");
|
|
600
|
+
var PollStream = class {
|
|
601
|
+
static {
|
|
602
|
+
__name(this, "PollStream");
|
|
603
|
+
}
|
|
604
|
+
constructor(ex, session, opts) {
|
|
605
|
+
this.ex = ex;
|
|
606
|
+
this.session = session;
|
|
607
|
+
this.o = settings(opts);
|
|
608
|
+
this.duplex = new Duplex2({
|
|
609
|
+
read: /* @__PURE__ */ __name(() => void 0, "read"),
|
|
610
|
+
write: /* @__PURE__ */ __name((chunk, _enc, cb) => {
|
|
611
|
+
this.pending.push(Buffer.from(chunk));
|
|
612
|
+
this.pendingSize += chunk.length;
|
|
613
|
+
if (this.pendingSize > MAX_UP) this.held.push(() => cb());
|
|
614
|
+
else cb();
|
|
615
|
+
this.kickUp();
|
|
616
|
+
}, "write"),
|
|
617
|
+
final: /* @__PURE__ */ __name((cb) => {
|
|
618
|
+
this.finishing = true;
|
|
619
|
+
this.kickUp();
|
|
620
|
+
cb();
|
|
621
|
+
}, "final"),
|
|
622
|
+
destroy: /* @__PURE__ */ __name((err, cb) => {
|
|
623
|
+
this.shutdown(true);
|
|
624
|
+
cb(err);
|
|
625
|
+
}, "destroy")
|
|
626
|
+
});
|
|
627
|
+
void this.downLoop();
|
|
628
|
+
}
|
|
629
|
+
ex;
|
|
630
|
+
session;
|
|
631
|
+
duplex;
|
|
632
|
+
o;
|
|
633
|
+
/** Upstream bytes the proxy has confirmed. */
|
|
634
|
+
sent = 0;
|
|
635
|
+
/** Downstream bytes received. */
|
|
636
|
+
received = 0;
|
|
637
|
+
pending = [];
|
|
638
|
+
pendingSize = 0;
|
|
639
|
+
held = [];
|
|
640
|
+
upRunning = false;
|
|
641
|
+
finishing = false;
|
|
642
|
+
closed = false;
|
|
643
|
+
kickUp() {
|
|
644
|
+
if (!this.upRunning && !this.closed) void this.upLoop();
|
|
645
|
+
}
|
|
646
|
+
async upLoop() {
|
|
647
|
+
this.upRunning = true;
|
|
648
|
+
try {
|
|
649
|
+
await delay(this.o.coalesceMs);
|
|
650
|
+
while (!this.closed && this.pendingSize > 0) {
|
|
651
|
+
const data = this.takePending();
|
|
652
|
+
await this.exchange({ session: this.session, upOffset: this.sent, ack: this.received, close: false, poll: false, open: false, data }, this.o.timeoutMs);
|
|
653
|
+
this.sent += data.length;
|
|
654
|
+
while (this.held.length > 0 && this.pendingSize <= MAX_UP) this.held.shift()();
|
|
655
|
+
}
|
|
656
|
+
if (this.finishing && !this.closed && this.pendingSize === 0) this.shutdown(true);
|
|
657
|
+
} catch (err) {
|
|
658
|
+
this.fail(err);
|
|
659
|
+
} finally {
|
|
660
|
+
this.upRunning = false;
|
|
661
|
+
}
|
|
662
|
+
if (!this.closed && this.pendingSize > 0) this.kickUp();
|
|
663
|
+
}
|
|
664
|
+
takePending() {
|
|
665
|
+
const all = Buffer.concat(this.pending);
|
|
666
|
+
const data = all.subarray(0, MAX_UP);
|
|
667
|
+
const rest = all.subarray(MAX_UP);
|
|
668
|
+
this.pending = rest.length > 0 ? [rest] : [];
|
|
669
|
+
this.pendingSize = rest.length;
|
|
670
|
+
return data;
|
|
671
|
+
}
|
|
672
|
+
async downLoop() {
|
|
673
|
+
try {
|
|
674
|
+
while (!this.closed) {
|
|
675
|
+
const r = await this.exchange({ session: this.session, upOffset: this.sent, ack: this.received, close: false, poll: true, open: false, data: EMPTY }, this.o.waitMs + this.o.timeoutMs);
|
|
676
|
+
if (this.closed) return;
|
|
677
|
+
if (r.downOffset > this.received) throw new Error("tunnel stream out of order");
|
|
678
|
+
const skip = this.received - r.downOffset;
|
|
679
|
+
if (skip < r.data.length) {
|
|
680
|
+
const fresh = Buffer.from(r.data.subarray(skip));
|
|
681
|
+
this.received += fresh.length;
|
|
682
|
+
this.duplex.push(fresh);
|
|
683
|
+
}
|
|
684
|
+
if (r.closed) {
|
|
685
|
+
this.duplex.push(null);
|
|
686
|
+
this.shutdown(true);
|
|
687
|
+
return;
|
|
688
|
+
}
|
|
689
|
+
}
|
|
690
|
+
} catch (err) {
|
|
691
|
+
this.fail(err);
|
|
692
|
+
}
|
|
693
|
+
}
|
|
694
|
+
async exchange(req, timeoutMs) {
|
|
695
|
+
const body = encodeRequest(req);
|
|
696
|
+
let firstFailure = 0;
|
|
697
|
+
let firstBusy = 0;
|
|
698
|
+
let wait = 250;
|
|
699
|
+
for (; ; ) {
|
|
700
|
+
if (this.closed) throw new Error("tunnel closed");
|
|
701
|
+
let failure;
|
|
702
|
+
let busy = false;
|
|
703
|
+
try {
|
|
704
|
+
const r = await this.ex.post(body, timeoutMs);
|
|
705
|
+
if (r.status === 200) return decodeResponse(r.body);
|
|
706
|
+
if (r.status === 404) throw new SessionGoneError();
|
|
707
|
+
failure = new TunnelBlockedError(r.status);
|
|
708
|
+
if (FATAL_STATUS.has(r.status)) throw failure;
|
|
709
|
+
busy = isBusy(r);
|
|
710
|
+
} catch (err) {
|
|
711
|
+
if (err instanceof SessionGoneError || err instanceof PollFormatError) throw err;
|
|
712
|
+
if (err instanceof TunnelBlockedError && FATAL_STATUS.has(err.status)) throw err;
|
|
713
|
+
failure = err;
|
|
714
|
+
}
|
|
715
|
+
if (busy) {
|
|
716
|
+
const t2 = Date.now();
|
|
717
|
+
if (firstBusy === 0) firstBusy = t2;
|
|
718
|
+
if (t2 - firstBusy >= this.o.busyForMs) throw failure;
|
|
719
|
+
firstFailure = 0;
|
|
720
|
+
await delay(Math.min(wait, 1e3));
|
|
721
|
+
wait = Math.min(wait * 2, 4e3);
|
|
722
|
+
continue;
|
|
723
|
+
}
|
|
724
|
+
const t = Date.now();
|
|
725
|
+
if (firstFailure === 0) firstFailure = t;
|
|
726
|
+
if (t - firstFailure >= this.o.retryForMs) throw failure;
|
|
727
|
+
await delay(wait);
|
|
728
|
+
wait = Math.min(wait * 2, 4e3);
|
|
729
|
+
}
|
|
730
|
+
}
|
|
731
|
+
fail(err) {
|
|
732
|
+
if (this.closed) return;
|
|
733
|
+
this.closed = true;
|
|
734
|
+
this.ex.destroy();
|
|
735
|
+
this.duplex.destroy(err);
|
|
736
|
+
}
|
|
737
|
+
shutdown(sendClose) {
|
|
738
|
+
if (this.closed) return;
|
|
739
|
+
this.closed = true;
|
|
740
|
+
if (sendClose) {
|
|
741
|
+
const bye = encodeRequest({ session: this.session, upOffset: this.sent, ack: this.received, close: true, poll: false, open: false, data: EMPTY });
|
|
742
|
+
this.ex.post(bye, 5e3).catch(() => void 0).finally(() => this.ex.destroy());
|
|
743
|
+
} else {
|
|
744
|
+
this.ex.destroy();
|
|
745
|
+
}
|
|
746
|
+
if (!this.duplex.destroyed) this.duplex.destroy();
|
|
747
|
+
}
|
|
748
|
+
};
|
|
749
|
+
async function openPollStream(base, opts = {}) {
|
|
750
|
+
const ex = makeExchanger(base, opts);
|
|
751
|
+
const session = randomBytes2(SESSION_BYTES);
|
|
752
|
+
const open2 = encodeRequest({ session, upOffset: 0, ack: 0, close: false, poll: false, open: true, data: EMPTY });
|
|
753
|
+
const o = settings(opts);
|
|
754
|
+
let first;
|
|
755
|
+
let firstFailure = 0;
|
|
756
|
+
let wait = 100;
|
|
757
|
+
while (first === void 0) {
|
|
758
|
+
try {
|
|
759
|
+
first = await ex.post(open2, o.timeoutMs);
|
|
760
|
+
} catch (err) {
|
|
761
|
+
const t = Date.now();
|
|
762
|
+
if (firstFailure === 0) firstFailure = t;
|
|
763
|
+
if (err.code !== "ECONNRESET" || t - firstFailure >= o.retryForMs) {
|
|
764
|
+
ex.destroy();
|
|
765
|
+
throw err;
|
|
766
|
+
}
|
|
767
|
+
await delay(wait);
|
|
768
|
+
wait = Math.min(wait * 2, 4e3);
|
|
769
|
+
}
|
|
770
|
+
}
|
|
771
|
+
if (first.status !== 200) {
|
|
772
|
+
ex.destroy();
|
|
773
|
+
throw new TunnelBlockedError(first.status);
|
|
774
|
+
}
|
|
775
|
+
let answered;
|
|
776
|
+
try {
|
|
777
|
+
answered = decodeResponse(first.body).session;
|
|
778
|
+
} catch {
|
|
779
|
+
}
|
|
780
|
+
if (!answered?.equals(session)) {
|
|
781
|
+
ex.destroy();
|
|
782
|
+
throw new TunnelBlockedError(200, "the answer is not the tunnel's (a filter's page?)");
|
|
783
|
+
}
|
|
784
|
+
const stream = new PollStream(ex, session, opts).duplex;
|
|
785
|
+
return { stream, outer: first.outer, ...first.outerError ? { outerError: first.outerError } : {} };
|
|
786
|
+
}
|
|
787
|
+
__name(openPollStream, "openPollStream");
|
|
788
|
+
var IdentityMismatchError = class extends Error {
|
|
789
|
+
static {
|
|
790
|
+
__name(this, "IdentityMismatchError");
|
|
791
|
+
}
|
|
792
|
+
constructor(received) {
|
|
793
|
+
super("tunnel identity mismatch");
|
|
794
|
+
this.received = received;
|
|
795
|
+
}
|
|
796
|
+
received;
|
|
797
|
+
};
|
|
798
|
+
async function connectInner(base, pins, opts = {}) {
|
|
799
|
+
const outerOpts = { timeoutMs: opts.timeoutMs, ca: opts.ca, userAgent: opts.userAgent, env: opts.env };
|
|
800
|
+
const ws = opts.transport === "post" ? await openPollStream(base, outerOpts) : await openWebSocket(new URL(TUNNEL_PATH, base), { protocol: TUNNEL_PROTOCOL, ...outerOpts });
|
|
801
|
+
opts.onOuter?.({ outer: ws.outer, ...ws.outerError ? { outerError: ws.outerError } : {} });
|
|
802
|
+
const preferred = pins.at(-1);
|
|
803
|
+
const servername = preferred && isPin(preferred) ? sniOfPin(preferred) : TUNNEL_SERVERNAME;
|
|
804
|
+
const socket = tls3.connect({ socket: ws.stream, servername, minVersion: "TLSv1.3", rejectUnauthorized: false, ALPNProtocols: ["http/1.1"] });
|
|
805
|
+
ws.stream.on("close", () => socket.destroy());
|
|
806
|
+
ws.stream.on("end", () => socket.destroy());
|
|
807
|
+
ws.stream.on("error", () => socket.destroy());
|
|
808
|
+
socket.on("close", () => ws.stream.destroy());
|
|
809
|
+
try {
|
|
810
|
+
await new Promise((resolve, reject) => {
|
|
811
|
+
let settled = false;
|
|
812
|
+
const finish = /* @__PURE__ */ __name((err) => {
|
|
813
|
+
if (settled) return;
|
|
814
|
+
settled = true;
|
|
815
|
+
clearTimeout(timer);
|
|
816
|
+
socket.off("close", onClose);
|
|
817
|
+
socket.off("secureConnect", onSecure);
|
|
818
|
+
if (err === null) {
|
|
819
|
+
socket.off("error", onError);
|
|
820
|
+
resolve();
|
|
821
|
+
return;
|
|
822
|
+
}
|
|
823
|
+
socket.once("close", () => socket.off("error", onError));
|
|
824
|
+
reject(err);
|
|
825
|
+
}, "finish");
|
|
826
|
+
const onError = /* @__PURE__ */ __name((err) => finish(err), "onError");
|
|
827
|
+
const onClose = /* @__PURE__ */ __name(() => finish(new Error("tunnel closed during handshake")), "onClose");
|
|
828
|
+
const onSecure = /* @__PURE__ */ __name(() => finish(null), "onSecure");
|
|
829
|
+
const timer = setTimeout(() => finish(new Error("tunnel handshake timeout")), opts.timeoutMs ?? 15e3);
|
|
830
|
+
timer.unref();
|
|
831
|
+
socket.on("error", onError);
|
|
832
|
+
socket.on("close", onClose);
|
|
833
|
+
socket.on("secureConnect", onSecure);
|
|
834
|
+
});
|
|
835
|
+
} catch (err) {
|
|
836
|
+
socket.destroy();
|
|
837
|
+
throw err;
|
|
838
|
+
}
|
|
839
|
+
socket.on("error", () => socket.destroy());
|
|
840
|
+
const peer = peerCertificate(socket);
|
|
841
|
+
const received = peer ? pinOfSpki(peer.publicKey.export({ type: "spki", format: "der" })) : "none";
|
|
842
|
+
if (!pins.includes(received)) {
|
|
843
|
+
socket.destroy();
|
|
844
|
+
throw new IdentityMismatchError(received);
|
|
845
|
+
}
|
|
846
|
+
return socket;
|
|
847
|
+
}
|
|
848
|
+
__name(connectInner, "connectInner");
|
|
849
|
+
var TUNNEL_AGENT_MAX_SOCKETS = 4;
|
|
850
|
+
function canSwitch(err) {
|
|
851
|
+
if (err instanceof IdentityMismatchError || err instanceof BackoffError) return false;
|
|
852
|
+
if (err instanceof TunnelBlockedError && (err.status === 429 || err.status === 503)) return false;
|
|
853
|
+
return true;
|
|
854
|
+
}
|
|
855
|
+
__name(canSwitch, "canSwitch");
|
|
856
|
+
function reasonOf(err) {
|
|
857
|
+
if (err instanceof TunnelBlockedError) return err.status === 101 || err.status === 200 ? err.message : `HTTP ${err.status}`;
|
|
858
|
+
return err instanceof Error ? err.message : String(err);
|
|
859
|
+
}
|
|
860
|
+
__name(reasonOf, "reasonOf");
|
|
861
|
+
var BackoffError = class extends Error {
|
|
862
|
+
static {
|
|
863
|
+
__name(this, "BackoffError");
|
|
864
|
+
}
|
|
865
|
+
constructor(waitMs) {
|
|
866
|
+
super("tunnel identity mismatch - waiting before retry");
|
|
867
|
+
this.waitMs = waitMs;
|
|
868
|
+
}
|
|
869
|
+
waitMs;
|
|
870
|
+
};
|
|
871
|
+
var baseAddRequest = http4.Agent.prototype.addRequest;
|
|
872
|
+
var TunnelAgent = class extends http4.Agent {
|
|
873
|
+
static {
|
|
874
|
+
__name(this, "TunnelAgent");
|
|
875
|
+
}
|
|
876
|
+
constructor(target, backoff, events = {}) {
|
|
877
|
+
super({ keepAlive: true, maxSockets: TUNNEL_AGENT_MAX_SOCKETS, maxFreeSockets: TUNNEL_AGENT_MAX_SOCKETS });
|
|
878
|
+
this.target = target;
|
|
879
|
+
this.backoff = backoff;
|
|
880
|
+
this.events = events;
|
|
881
|
+
this.mode = target.transport ?? "auto";
|
|
882
|
+
this.using = this.mode === "post" ? "post" : "ws";
|
|
883
|
+
this.connect = target.connect ?? connectInner;
|
|
884
|
+
this.pins = [...target.pins];
|
|
885
|
+
this.targets = [target.url, ...target.urls ?? []];
|
|
886
|
+
this.on("free", () => this.drain());
|
|
887
|
+
}
|
|
888
|
+
target;
|
|
889
|
+
backoff;
|
|
890
|
+
events;
|
|
891
|
+
/** Tunnels being opened: not yet in `sockets`, where Node would count them. */
|
|
892
|
+
opening = 0;
|
|
893
|
+
/** Requests waiting for a tunnel, in arrival order. */
|
|
894
|
+
waiting = [];
|
|
895
|
+
using;
|
|
896
|
+
mode;
|
|
897
|
+
connect;
|
|
898
|
+
pins;
|
|
899
|
+
/** Every host the code names, first one first; `preferred` indexes it. */
|
|
900
|
+
targets;
|
|
901
|
+
preferred = 0;
|
|
902
|
+
/** Destroyed: no tunnel is opened any more, and one still opening is closed when it arrives. */
|
|
903
|
+
closed = false;
|
|
904
|
+
/**
|
|
905
|
+
* The pins new tunnels are checked against (and the newest one asked for),
|
|
906
|
+
* from now on: what pin-sync learned through a pinned tunnel. Tunnels
|
|
907
|
+
* already open keep going.
|
|
908
|
+
*/
|
|
909
|
+
setPins(pins) {
|
|
910
|
+
this.pins = [...pins];
|
|
911
|
+
}
|
|
912
|
+
get transport() {
|
|
913
|
+
return this.using;
|
|
914
|
+
}
|
|
915
|
+
live() {
|
|
916
|
+
let n = this.opening;
|
|
917
|
+
for (const list of Object.values(this.sockets)) n += list?.length ?? 0;
|
|
918
|
+
for (const list of Object.values(this.freeSockets)) n += list?.length ?? 0;
|
|
919
|
+
return n;
|
|
920
|
+
}
|
|
921
|
+
hasFree() {
|
|
922
|
+
return Object.values(this.freeSockets).some((list) => list?.some((s) => !s.destroyed));
|
|
923
|
+
}
|
|
924
|
+
canServe() {
|
|
925
|
+
return this.hasFree() || this.live() < TUNNEL_AGENT_MAX_SOCKETS;
|
|
926
|
+
}
|
|
927
|
+
/** Called by http.ClientRequest for every request; not in @types/node. */
|
|
928
|
+
addRequest(req, options) {
|
|
929
|
+
if (!this.canServe()) {
|
|
930
|
+
this.waiting.push({ req, options });
|
|
931
|
+
req.once("close", () => {
|
|
932
|
+
const i = this.waiting.findIndex((w) => w.req === req);
|
|
933
|
+
if (i >= 0) this.waiting.splice(i, 1);
|
|
934
|
+
});
|
|
935
|
+
return;
|
|
936
|
+
}
|
|
937
|
+
baseAddRequest.call(this, req, options);
|
|
938
|
+
}
|
|
939
|
+
drain() {
|
|
940
|
+
while (this.waiting.length > 0 && this.canServe()) {
|
|
941
|
+
const next = this.waiting.shift();
|
|
942
|
+
if (next.req.destroyed) continue;
|
|
943
|
+
baseAddRequest.call(this, next.req, next.options);
|
|
944
|
+
}
|
|
945
|
+
}
|
|
946
|
+
/** Whether destroy() ran: the agent opens nothing any more. */
|
|
947
|
+
get destroyed() {
|
|
948
|
+
return this.closed;
|
|
949
|
+
}
|
|
950
|
+
destroy() {
|
|
951
|
+
this.closed = true;
|
|
952
|
+
for (const { req } of this.waiting.splice(0)) {
|
|
953
|
+
const err = new Error("tunnel agent closed");
|
|
954
|
+
req.destroy(err);
|
|
955
|
+
process.nextTick(() => req.emit("error", err));
|
|
956
|
+
}
|
|
957
|
+
super.destroy();
|
|
958
|
+
}
|
|
959
|
+
// Node calls this with a callback for asynchronous sockets; returning nothing
|
|
960
|
+
// tells it to wait for the callback.
|
|
961
|
+
createConnection(_options, callback) {
|
|
962
|
+
const done = callback ?? (() => void 0);
|
|
963
|
+
if (this.closed) {
|
|
964
|
+
done(new Error("tunnel agent closed"), void 0);
|
|
965
|
+
return void 0;
|
|
966
|
+
}
|
|
967
|
+
const wait = this.backoff.blockedMs();
|
|
968
|
+
if (wait > 0) {
|
|
969
|
+
done(new BackoffError(wait), void 0);
|
|
970
|
+
return void 0;
|
|
971
|
+
}
|
|
972
|
+
this.opening += 1;
|
|
973
|
+
const dial = /* @__PURE__ */ __name((host, transport) => this.connect(this.targets[host], this.pins, { ca: this.target.ca, userAgent: this.target.userAgent, env: this.target.env, transport, onOuter: /* @__PURE__ */ __name((i) => this.events.onOuter?.(i), "onOuter") }), "dial");
|
|
974
|
+
const hosts = this.targets.map((_, i) => (this.preferred + i) % this.targets.length);
|
|
975
|
+
const transports = this.mode === "auto" ? this.using === "ws" ? ["ws", "post"] : ["post"] : [this.using === "post" ? "post" : "ws"];
|
|
976
|
+
const chain = transports.flatMap((transport) => hosts.map((host) => ({ host, transport })));
|
|
977
|
+
let firstErr = null;
|
|
978
|
+
const run = /* @__PURE__ */ __name((i) => {
|
|
979
|
+
const at = chain[i];
|
|
980
|
+
return dial(at.host, at.transport).then(
|
|
981
|
+
(socket) => {
|
|
982
|
+
if (this.preferred !== at.host) this.preferred = at.host;
|
|
983
|
+
if (this.mode === "auto" && this.using !== "post" && at.transport === "post") {
|
|
984
|
+
this.using = "post";
|
|
985
|
+
this.events.onTransport?.("post", firstErr === null ? null : `websocket blocked: ${reasonOf(firstErr)}`);
|
|
986
|
+
}
|
|
987
|
+
return socket;
|
|
988
|
+
},
|
|
989
|
+
(err) => {
|
|
990
|
+
if (!(err instanceof Error)) throw err;
|
|
991
|
+
if (firstErr === null) firstErr = err;
|
|
992
|
+
if (i + 1 < chain.length && canSwitch(err)) return run(i + 1);
|
|
993
|
+
throw err instanceof IdentityMismatchError ? err : firstErr;
|
|
994
|
+
}
|
|
995
|
+
);
|
|
996
|
+
}, "run");
|
|
997
|
+
run(0).then(
|
|
998
|
+
(socket) => {
|
|
999
|
+
this.opening -= 1;
|
|
1000
|
+
if (this.closed) {
|
|
1001
|
+
socket.destroy();
|
|
1002
|
+
done(new Error("tunnel agent closed"), void 0);
|
|
1003
|
+
return;
|
|
1004
|
+
}
|
|
1005
|
+
this.backoff.success();
|
|
1006
|
+
this.events.onConnected?.();
|
|
1007
|
+
socket.once("close", () => process.nextTick(() => this.drain()));
|
|
1008
|
+
done(null, socket);
|
|
1009
|
+
},
|
|
1010
|
+
(err) => {
|
|
1011
|
+
this.opening -= 1;
|
|
1012
|
+
if (err instanceof IdentityMismatchError) {
|
|
1013
|
+
this.backoff.failure();
|
|
1014
|
+
this.events.onMismatch?.(err.received);
|
|
1015
|
+
}
|
|
1016
|
+
done(err, void 0);
|
|
1017
|
+
this.drain();
|
|
1018
|
+
}
|
|
1019
|
+
);
|
|
1020
|
+
return void 0;
|
|
1021
|
+
}
|
|
1022
|
+
};
|
|
1023
|
+
function newLocalKey() {
|
|
1024
|
+
return `o1gwl_${randomBytes3(32).toString("base64url")}`;
|
|
1025
|
+
}
|
|
1026
|
+
__name(newLocalKey, "newLocalKey");
|
|
1027
|
+
function hashLocalKey(key) {
|
|
1028
|
+
return createHash3("sha256").update(key, "utf8").digest("hex");
|
|
1029
|
+
}
|
|
1030
|
+
__name(hashLocalKey, "hashLocalKey");
|
|
1031
|
+
var HOP = /* @__PURE__ */ new Set(["connection", "keep-alive", "proxy-authorization", "proxy-connection", "te", "trailer", "transfer-encoding", "upgrade", "host", "authorization", "x-api-key"]);
|
|
1032
|
+
var LOOPBACK2 = /* @__PURE__ */ new Set(["127.0.0.1", "localhost", "[::1]"]);
|
|
1033
|
+
function send(res, status, message) {
|
|
1034
|
+
const body = JSON.stringify({ type: "error", error: { type: "tunnel_error", message } });
|
|
1035
|
+
res.writeHead(status, { "content-type": "application/json", "content-length": Buffer.byteLength(body) }).end(body);
|
|
1036
|
+
}
|
|
1037
|
+
__name(send, "send");
|
|
1038
|
+
function keyOf(req) {
|
|
1039
|
+
const bearer = /^Bearer\s+(.+)$/i.exec(String(req.headers.authorization ?? "").trim());
|
|
1040
|
+
if (bearer) return bearer[1].trim();
|
|
1041
|
+
const k = req.headers["x-api-key"];
|
|
1042
|
+
return typeof k === "string" && k.trim() ? k.trim() : void 0;
|
|
1043
|
+
}
|
|
1044
|
+
__name(keyOf, "keyOf");
|
|
1045
|
+
function sameHash(a, b) {
|
|
1046
|
+
const x = Buffer.from(a, "hex");
|
|
1047
|
+
const y = Buffer.from(b, "hex");
|
|
1048
|
+
return x.length === y.length && x.length > 0 && timingSafeEqual(x, y);
|
|
1049
|
+
}
|
|
1050
|
+
__name(sameHash, "sameHash");
|
|
1051
|
+
var REPLAY_MAX_BYTES = 10 * 1024 * 1024;
|
|
1052
|
+
function deadTunnel(err, reusedSocket) {
|
|
1053
|
+
if (err instanceof SessionGoneError) return true;
|
|
1054
|
+
const code = err?.code;
|
|
1055
|
+
return reusedSocket && (code === "ECONNRESET" || code === "EPIPE");
|
|
1056
|
+
}
|
|
1057
|
+
__name(deadTunnel, "deadTunnel");
|
|
1058
|
+
function describeError(err) {
|
|
1059
|
+
if (err instanceof BackoffError) return `tunnel identity mismatch - waiting before retry (${Math.ceil(err.waitMs / 6e4)} min)`;
|
|
1060
|
+
if (err instanceof IdentityMismatchError) return "tunnel identity mismatch";
|
|
1061
|
+
if (err instanceof TunnelBlockedError) return err.message;
|
|
1062
|
+
return "tunnel unavailable";
|
|
1063
|
+
}
|
|
1064
|
+
__name(describeError, "describeError");
|
|
1065
|
+
function startLocalServer(o) {
|
|
1066
|
+
const server = http5.createServer((req, res) => {
|
|
1067
|
+
const started = Date.now();
|
|
1068
|
+
const route = (req.url ?? "/").split("?")[0].slice(0, 200);
|
|
1069
|
+
const hostname2 = String(req.headers.host ?? "").toLowerCase().replace(/:\d+$/, "");
|
|
1070
|
+
if (!LOOPBACK2.has(hostname2)) return send(res, 403, "this endpoint answers on 127.0.0.1 only");
|
|
1071
|
+
if (req.headers.origin !== void 0) return send(res, 403, "browser requests are not accepted");
|
|
1072
|
+
const key = keyOf(req);
|
|
1073
|
+
if (!key || !sameHash(hashLocalKey(key), o.localKeyHash)) return send(res, 401, "invalid or missing local key");
|
|
1074
|
+
const headers = {};
|
|
1075
|
+
for (const [name, value] of Object.entries(req.headers)) if (!HOP.has(name) && value !== void 0) headers[name] = value;
|
|
1076
|
+
headers.host = o.target.host;
|
|
1077
|
+
headers.authorization = `Bearer ${o.deviceToken}`;
|
|
1078
|
+
let kept = [];
|
|
1079
|
+
let keptBytes = 0;
|
|
1080
|
+
req.on("data", (c) => {
|
|
1081
|
+
if (!kept) return;
|
|
1082
|
+
keptBytes += c.length;
|
|
1083
|
+
if (keptBytes > REPLAY_MAX_BYTES) kept = null;
|
|
1084
|
+
else kept.push(c);
|
|
1085
|
+
});
|
|
1086
|
+
let upstream;
|
|
1087
|
+
const forward = /* @__PURE__ */ __name((replay) => {
|
|
1088
|
+
const via = replay ? { createConnection: /* @__PURE__ */ __name((opts, cb) => o.agent.createConnection(opts, cb), "createConnection") } : { agent: o.agent };
|
|
1089
|
+
const current = http5.request({ ...via, method: req.method, path: req.url, headers, setHost: false }, (up) => {
|
|
1090
|
+
const out = {};
|
|
1091
|
+
for (const [name, value] of Object.entries(up.headers)) if (!HOP.has(name) && value !== void 0) out[name] = value;
|
|
1092
|
+
res.writeHead(up.statusCode ?? 502, out);
|
|
1093
|
+
up.pipe(res);
|
|
1094
|
+
up.on("error", () => res.destroy());
|
|
1095
|
+
up.on("end", () => o.log?.(`${req.method} ${route} status=${up.statusCode} total=${Date.now() - started}ms`));
|
|
1096
|
+
});
|
|
1097
|
+
upstream = current;
|
|
1098
|
+
current.on("error", (err) => {
|
|
1099
|
+
if (!replay && !res.headersSent && kept && deadTunnel(err, current.reusedSocket)) {
|
|
1100
|
+
o.log?.(`${req.method} ${route} retry: the tunnel was gone`);
|
|
1101
|
+
const again = /* @__PURE__ */ __name(() => {
|
|
1102
|
+
if (kept && !res.destroyed) forward(true);
|
|
1103
|
+
else if (!res.destroyed) send(res, 502, describeError(err));
|
|
1104
|
+
}, "again");
|
|
1105
|
+
if (req.readableEnded) again();
|
|
1106
|
+
else {
|
|
1107
|
+
req.once("end", again);
|
|
1108
|
+
req.resume();
|
|
1109
|
+
}
|
|
1110
|
+
return;
|
|
1111
|
+
}
|
|
1112
|
+
o.log?.(`${req.method} ${route} failed: ${describeError(err)}`);
|
|
1113
|
+
if (res.headersSent) res.destroy();
|
|
1114
|
+
else if (err instanceof TunnelBlockedError && err.status === 429) send(res, 429, "Too many attempts");
|
|
1115
|
+
else send(res, 502, describeError(err));
|
|
1116
|
+
});
|
|
1117
|
+
if (replay) current.end(Buffer.concat(kept ?? []));
|
|
1118
|
+
else req.pipe(current);
|
|
1119
|
+
}, "forward");
|
|
1120
|
+
forward(false);
|
|
1121
|
+
res.on("close", () => {
|
|
1122
|
+
if (!res.writableFinished) upstream.destroy();
|
|
1123
|
+
});
|
|
1124
|
+
req.on("error", () => upstream.destroy());
|
|
1125
|
+
});
|
|
1126
|
+
return new Promise((resolve, reject) => {
|
|
1127
|
+
server.once("error", reject);
|
|
1128
|
+
server.listen(o.port, "127.0.0.1", () => {
|
|
1129
|
+
server.off("error", reject);
|
|
1130
|
+
resolve(server);
|
|
1131
|
+
});
|
|
1132
|
+
});
|
|
1133
|
+
}
|
|
1134
|
+
__name(startLocalServer, "startLocalServer");
|
|
1135
|
+
var IdentityBackoff = class {
|
|
1136
|
+
static {
|
|
1137
|
+
__name(this, "IdentityBackoff");
|
|
1138
|
+
}
|
|
1139
|
+
constructor(now = Date.now, baseMs = 6e4, maxMs = 60 * 6e4) {
|
|
1140
|
+
this.now = now;
|
|
1141
|
+
this.baseMs = baseMs;
|
|
1142
|
+
this.maxMs = maxMs;
|
|
1143
|
+
}
|
|
1144
|
+
now;
|
|
1145
|
+
baseMs;
|
|
1146
|
+
maxMs;
|
|
1147
|
+
failures = 0;
|
|
1148
|
+
until = 0;
|
|
1149
|
+
count = 0;
|
|
1150
|
+
since = null;
|
|
1151
|
+
blockedMs() {
|
|
1152
|
+
return Math.max(0, this.until - this.now());
|
|
1153
|
+
}
|
|
1154
|
+
failure() {
|
|
1155
|
+
const t = this.now();
|
|
1156
|
+
this.count += 1;
|
|
1157
|
+
this.since ??= t;
|
|
1158
|
+
this.until = t + Math.min(this.baseMs * 2 ** this.failures, this.maxMs);
|
|
1159
|
+
this.failures += 1;
|
|
1160
|
+
}
|
|
1161
|
+
success() {
|
|
1162
|
+
this.failures = 0;
|
|
1163
|
+
this.until = 0;
|
|
1164
|
+
this.count = 0;
|
|
1165
|
+
this.since = null;
|
|
1166
|
+
}
|
|
1167
|
+
snapshot() {
|
|
1168
|
+
return { mismatches: this.count, since: this.since, waitMs: this.blockedMs() };
|
|
1169
|
+
}
|
|
1170
|
+
};
|
|
1171
|
+
function mergePins(own, listed, via) {
|
|
1172
|
+
const unchanged = { pins: [...own], added: [], dropped: [] };
|
|
1173
|
+
if (via === null || !own.includes(via)) return unchanged;
|
|
1174
|
+
if (!Array.isArray(listed) || listed.length === 0 || listed.length > MAX_PINS || !listed.every((p) => typeof p === "string" && isPin(p))) return unchanged;
|
|
1175
|
+
const list = [...new Set(listed)];
|
|
1176
|
+
const added = list.filter((p) => !own.includes(p));
|
|
1177
|
+
if (list.includes(via)) {
|
|
1178
|
+
return { pins: list, added, dropped: own.filter((p) => !list.includes(p)) };
|
|
1179
|
+
}
|
|
1180
|
+
const room = Math.max(0, MAX_PINS - own.length);
|
|
1181
|
+
const learned = room > 0 ? added.slice(-room) : [];
|
|
1182
|
+
return { pins: [...own, ...learned], added: learned, dropped: [] };
|
|
1183
|
+
}
|
|
1184
|
+
__name(mergePins, "mergePins");
|
|
1185
|
+
function pinOfSocket(socket) {
|
|
1186
|
+
if (!(socket instanceof tls4.TLSSocket)) return null;
|
|
1187
|
+
const peer = peerCertificate(socket);
|
|
1188
|
+
return peer ? pinOfSpki(peer.publicKey.export({ type: "spki", format: "der" })) : null;
|
|
1189
|
+
}
|
|
1190
|
+
__name(pinOfSocket, "pinOfSocket");
|
|
1191
|
+
var MAX_BODY = 16 * 1024;
|
|
1192
|
+
var short = /* @__PURE__ */ __name((pin) => fingerprint(pin).slice(0, 19), "short");
|
|
1193
|
+
var PinSync = class {
|
|
1194
|
+
static {
|
|
1195
|
+
__name(this, "PinSync");
|
|
1196
|
+
}
|
|
1197
|
+
constructor(o) {
|
|
1198
|
+
this.o = o;
|
|
1199
|
+
this.current = [...o.pins];
|
|
1200
|
+
}
|
|
1201
|
+
o;
|
|
1202
|
+
current;
|
|
1203
|
+
last = Number.NEGATIVE_INFINITY;
|
|
1204
|
+
running = null;
|
|
1205
|
+
/** The pins in memory are not on disk yet (a save failed). */
|
|
1206
|
+
unsaved = false;
|
|
1207
|
+
/** config.bin belongs to another configuration now: nothing more is saved by this run. */
|
|
1208
|
+
saveStopped = false;
|
|
1209
|
+
get pins() {
|
|
1210
|
+
return [...this.current];
|
|
1211
|
+
}
|
|
1212
|
+
/** Asks the proxy now; one request at a time. */
|
|
1213
|
+
syncNow() {
|
|
1214
|
+
this.running ??= this.run().finally(() => {
|
|
1215
|
+
this.running = null;
|
|
1216
|
+
});
|
|
1217
|
+
return this.running;
|
|
1218
|
+
}
|
|
1219
|
+
/** Asks when the last try is older than the interval; failures are logged, never thrown. */
|
|
1220
|
+
maybeSync() {
|
|
1221
|
+
const now = (this.o.now ?? Date.now)();
|
|
1222
|
+
if (this.running || now - this.last < (this.o.intervalMs ?? 36e5)) return;
|
|
1223
|
+
this.last = now;
|
|
1224
|
+
this.syncNow().catch((err) => this.o.log?.(`pin check failed: ${err instanceof Error ? err.message : String(err)}`));
|
|
1225
|
+
}
|
|
1226
|
+
async run() {
|
|
1227
|
+
this.last = (this.o.now ?? Date.now)();
|
|
1228
|
+
const { listed, via } = await this.fetch();
|
|
1229
|
+
const merged = mergePins(this.current, listed, via);
|
|
1230
|
+
const changed = merged.pins.length !== this.current.length || merged.pins.some((p, i) => p !== this.current[i]);
|
|
1231
|
+
if (changed) {
|
|
1232
|
+
this.current = merged.pins;
|
|
1233
|
+
this.o.onChange?.(this.pins);
|
|
1234
|
+
this.o.log?.(`proxy keys updated through the pinned tunnel: ${merged.added.map((p) => `+${short(p)}`).concat(merged.dropped.map((p) => `-${short(p)}`)).join(" ")}`);
|
|
1235
|
+
}
|
|
1236
|
+
if ((changed || this.unsaved) && !this.saveStopped) {
|
|
1237
|
+
try {
|
|
1238
|
+
if (await this.o.save(this.pins) === false) {
|
|
1239
|
+
this.saveStopped = true;
|
|
1240
|
+
this.o.log?.(this.o.stoppedLine ?? "config.bin now holds another configuration (setup ran since): the proxy keys learned by this run are no longer saved");
|
|
1241
|
+
}
|
|
1242
|
+
this.unsaved = false;
|
|
1243
|
+
} catch (err) {
|
|
1244
|
+
this.unsaved = true;
|
|
1245
|
+
this.o.log?.(`could not save the proxy keys (tried again at the next check): ${err instanceof Error ? err.message : String(err)}`);
|
|
1246
|
+
}
|
|
1247
|
+
}
|
|
1248
|
+
return { ...merged, via };
|
|
1249
|
+
}
|
|
1250
|
+
fetch() {
|
|
1251
|
+
const { target } = this.o;
|
|
1252
|
+
return new Promise((resolve, reject) => {
|
|
1253
|
+
const req = http6.request({
|
|
1254
|
+
host: target.hostname,
|
|
1255
|
+
port: target.port,
|
|
1256
|
+
method: "GET",
|
|
1257
|
+
path: TUNNEL_PINS_PATH,
|
|
1258
|
+
agent: this.o.agent,
|
|
1259
|
+
setHost: false,
|
|
1260
|
+
headers: { host: target.host, authorization: `Bearer ${this.o.deviceToken}`, accept: "application/json" }
|
|
1261
|
+
}, (res) => {
|
|
1262
|
+
const via = pinOfSocket(res.socket);
|
|
1263
|
+
const chunks = [];
|
|
1264
|
+
let size = 0;
|
|
1265
|
+
res.on("data", (c) => {
|
|
1266
|
+
size += c.length;
|
|
1267
|
+
if (size > MAX_BODY) req.destroy(new Error("pin list too large"));
|
|
1268
|
+
else chunks.push(c);
|
|
1269
|
+
});
|
|
1270
|
+
res.on("error", reject);
|
|
1271
|
+
res.on("end", () => {
|
|
1272
|
+
if (res.statusCode !== 200) return reject(new Error(`the proxy answered ${res.statusCode ?? 0} for its pins`));
|
|
1273
|
+
try {
|
|
1274
|
+
resolve({ listed: JSON.parse(Buffer.concat(chunks).toString("utf8")).pins, via });
|
|
1275
|
+
} catch {
|
|
1276
|
+
reject(new Error("the proxy's pin list is not JSON"));
|
|
1277
|
+
}
|
|
1278
|
+
});
|
|
1279
|
+
});
|
|
1280
|
+
req.setTimeout(this.o.timeoutMs ?? 3e4, () => req.destroy(new Error("pin check timeout")));
|
|
1281
|
+
req.on("error", reject);
|
|
1282
|
+
req.end();
|
|
1283
|
+
});
|
|
1284
|
+
}
|
|
1285
|
+
};
|
|
1286
|
+
async function startTunnelSession(o) {
|
|
1287
|
+
const target = new URL(o.url);
|
|
1288
|
+
const failover = (o.urls ?? []).map((u) => new URL(u));
|
|
1289
|
+
const backoff = o.backoff ?? new IdentityBackoff();
|
|
1290
|
+
const agent = new TunnelAgent({ url: target, urls: failover, pins: o.pins, userAgent: o.userAgent, transport: o.transport }, backoff, {
|
|
1291
|
+
onOuter: o.onOuter,
|
|
1292
|
+
onConnected: /* @__PURE__ */ __name(() => {
|
|
1293
|
+
setImmediate(() => {
|
|
1294
|
+
if (!agent.destroyed) pinSync.maybeSync();
|
|
1295
|
+
});
|
|
1296
|
+
o.onConnected?.();
|
|
1297
|
+
}, "onConnected"),
|
|
1298
|
+
onMismatch: o.onMismatch,
|
|
1299
|
+
onTransport: o.onTransport
|
|
1300
|
+
});
|
|
1301
|
+
const pinSync = new PinSync({
|
|
1302
|
+
agent,
|
|
1303
|
+
target,
|
|
1304
|
+
deviceToken: o.deviceToken,
|
|
1305
|
+
pins: o.pins,
|
|
1306
|
+
log: o.log,
|
|
1307
|
+
onChange: /* @__PURE__ */ __name((pins) => agent.setPins(pins), "onChange"),
|
|
1308
|
+
save: o.savePins,
|
|
1309
|
+
stoppedLine: o.savePinsStoppedLine
|
|
1310
|
+
});
|
|
1311
|
+
let server;
|
|
1312
|
+
try {
|
|
1313
|
+
server = await startLocalServer({ port: o.port, localKeyHash: o.localKeyHash, deviceToken: o.deviceToken, target, agent, log: o.log });
|
|
1314
|
+
} catch (err) {
|
|
1315
|
+
agent.destroy();
|
|
1316
|
+
throw err;
|
|
1317
|
+
}
|
|
1318
|
+
let closing = null;
|
|
1319
|
+
const close = /* @__PURE__ */ __name(() => {
|
|
1320
|
+
closing ??= new Promise((resolve) => {
|
|
1321
|
+
agent.destroy();
|
|
1322
|
+
server.close(() => resolve());
|
|
1323
|
+
server.closeIdleConnections();
|
|
1324
|
+
setImmediate(() => server.closeAllConnections());
|
|
1325
|
+
});
|
|
1326
|
+
return closing;
|
|
1327
|
+
}, "close");
|
|
1328
|
+
return { agent, pinSync, server, target, port: server.address().port, close };
|
|
1329
|
+
}
|
|
1330
|
+
__name(startTunnelSession, "startTunnelSession");
|
|
1331
|
+
var VERSION = true ? "9.4.0" : "dev";
|
|
1332
|
+
var O1ConnectError = class extends Error {
|
|
1333
|
+
static {
|
|
1334
|
+
__name(this, "O1ConnectError");
|
|
1335
|
+
}
|
|
1336
|
+
code;
|
|
1337
|
+
constructor(code, message, options) {
|
|
1338
|
+
super(message, options);
|
|
1339
|
+
this.name = "O1ConnectError";
|
|
1340
|
+
this.code = code;
|
|
1341
|
+
}
|
|
1342
|
+
};
|
|
1343
|
+
var DEFAULT_NAME = "aipp-connect";
|
|
1344
|
+
var TRANSPORTS = ["auto", "websocket", "post"];
|
|
1345
|
+
function entryName(name) {
|
|
1346
|
+
if (name === void 0) return DEFAULT_NAME;
|
|
1347
|
+
if (typeof name !== "string" || name.trim() === "") throw new TypeError("name must be a non-empty string");
|
|
1348
|
+
return name;
|
|
1349
|
+
}
|
|
1350
|
+
__name(entryName, "entryName");
|
|
1351
|
+
function checkStore(store) {
|
|
1352
|
+
const s = store;
|
|
1353
|
+
if (!s || typeof s.get !== "function" || typeof s.set !== "function" || typeof s.delete !== "function") {
|
|
1354
|
+
throw new TypeError("store must implement get, set and delete");
|
|
1355
|
+
}
|
|
1356
|
+
return s;
|
|
1357
|
+
}
|
|
1358
|
+
__name(checkStore, "checkStore");
|
|
1359
|
+
var entryLocks = /* @__PURE__ */ new WeakMap();
|
|
1360
|
+
function withEntry(store, name, fn) {
|
|
1361
|
+
let byName = entryLocks.get(store);
|
|
1362
|
+
if (!byName) {
|
|
1363
|
+
byName = /* @__PURE__ */ new Map();
|
|
1364
|
+
entryLocks.set(store, byName);
|
|
1365
|
+
}
|
|
1366
|
+
const map = byName;
|
|
1367
|
+
const run = (map.get(name) ?? Promise.resolve()).then(fn);
|
|
1368
|
+
const tail = run.then(() => void 0, () => void 0);
|
|
1369
|
+
map.set(name, tail);
|
|
1370
|
+
void tail.then(() => {
|
|
1371
|
+
if (map.get(name) === tail) map.delete(name);
|
|
1372
|
+
});
|
|
1373
|
+
return run;
|
|
1374
|
+
}
|
|
1375
|
+
__name(withEntry, "withEntry");
|
|
1376
|
+
async function viaStore(what, call) {
|
|
1377
|
+
try {
|
|
1378
|
+
return await call();
|
|
1379
|
+
} catch (err) {
|
|
1380
|
+
throw new O1ConnectError("store_failed", `the secret store failed to ${what}`, { cause: err });
|
|
1381
|
+
}
|
|
1382
|
+
}
|
|
1383
|
+
__name(viaStore, "viaStore");
|
|
1384
|
+
function parseSaved(raw) {
|
|
1385
|
+
let o;
|
|
1386
|
+
try {
|
|
1387
|
+
o = JSON.parse(String(raw));
|
|
1388
|
+
} catch {
|
|
1389
|
+
throw new O1ConnectError("invalid_config", "the saved configuration is not JSON: run setup again");
|
|
1390
|
+
}
|
|
1391
|
+
if (!o || typeof o !== "object" || o.v !== 1) throw new O1ConnectError("invalid_config", "the saved configuration is not one this library knows: run setup again");
|
|
1392
|
+
let code;
|
|
1393
|
+
try {
|
|
1394
|
+
code = validateCode(o);
|
|
1395
|
+
} catch (err) {
|
|
1396
|
+
throw new O1ConnectError("invalid_config", `the saved configuration is not valid (${err instanceof Error ? err.message : String(err)}): run setup again`);
|
|
1397
|
+
}
|
|
1398
|
+
const transport = o.transport ?? "auto";
|
|
1399
|
+
if (!TRANSPORTS.includes(transport)) throw new O1ConnectError("invalid_config", "the saved transport is not auto, websocket or post: run setup again");
|
|
1400
|
+
return { v: 1, ...code, transport };
|
|
1401
|
+
}
|
|
1402
|
+
__name(parseSaved, "parseSaved");
|
|
1403
|
+
async function setup(code, opts) {
|
|
1404
|
+
const store = checkStore(opts?.store);
|
|
1405
|
+
if (typeof opts.onFingerprint !== "function") throw new TypeError("onFingerprint is required: every key's fingerprint must be confirmed by the user");
|
|
1406
|
+
const name = entryName(opts.name);
|
|
1407
|
+
const transport = opts.transport ?? "auto";
|
|
1408
|
+
if (!TRANSPORTS.includes(transport)) throw new TypeError("transport must be auto, websocket or post");
|
|
1409
|
+
let decoded;
|
|
1410
|
+
try {
|
|
1411
|
+
decoded = decodeConnectionCode(String(code ?? ""));
|
|
1412
|
+
} catch (err) {
|
|
1413
|
+
throw new O1ConnectError("invalid_code", err instanceof ConnectionCodeError ? err.message : "the connection code is not valid");
|
|
1414
|
+
}
|
|
1415
|
+
const fingerprints = decoded.pins.map((p) => fingerprint(p));
|
|
1416
|
+
const info = { device: decoded.device, url: decoded.url, ...decoded.urls ? { urls: [...decoded.urls] } : {} };
|
|
1417
|
+
if (await opts.onFingerprint([...fingerprints], { ...info }) !== true) {
|
|
1418
|
+
throw new O1ConnectError("fingerprint_rejected", "the proxy keys were not confirmed: nothing was saved");
|
|
1419
|
+
}
|
|
1420
|
+
const saved = { v: 1, ...decoded, transport };
|
|
1421
|
+
await withEntry(store, name, () => viaStore("save the configuration", () => store.set(name, JSON.stringify(saved))));
|
|
1422
|
+
return { ...info, fingerprints };
|
|
1423
|
+
}
|
|
1424
|
+
__name(setup, "setup");
|
|
1425
|
+
async function open(opts) {
|
|
1426
|
+
const store = checkStore(opts?.store);
|
|
1427
|
+
const name = entryName(opts.name);
|
|
1428
|
+
const port = opts.port ?? 0;
|
|
1429
|
+
if (!Number.isInteger(port) || port < 0 || port > 65535) throw new TypeError("port must be an integer from 0 to 65535");
|
|
1430
|
+
const raw = await viaStore("read the configuration", () => store.get(name));
|
|
1431
|
+
if (raw === null || raw === void 0) throw new O1ConnectError("not_set_up", "no o1-connect configuration in the store: run setup with a connection code");
|
|
1432
|
+
const cfg = parseSaved(raw);
|
|
1433
|
+
const log = opts.log ? (line) => {
|
|
1434
|
+
try {
|
|
1435
|
+
opts.log(line);
|
|
1436
|
+
} catch {
|
|
1437
|
+
}
|
|
1438
|
+
} : void 0;
|
|
1439
|
+
const apiKey = newLocalKey();
|
|
1440
|
+
let session;
|
|
1441
|
+
try {
|
|
1442
|
+
session = await startTunnelSession({
|
|
1443
|
+
url: cfg.url,
|
|
1444
|
+
urls: cfg.urls,
|
|
1445
|
+
pins: cfg.pins,
|
|
1446
|
+
deviceToken: cfg.token,
|
|
1447
|
+
transport: cfg.transport,
|
|
1448
|
+
port,
|
|
1449
|
+
localKeyHash: hashLocalKey(apiKey),
|
|
1450
|
+
// Only when the app asks (like the CLI's identify): absent, nothing about the client goes on the wire.
|
|
1451
|
+
userAgent: opts.identify === true ? `o1-connect-lib/${VERSION}` : void 0,
|
|
1452
|
+
log,
|
|
1453
|
+
onMismatch: /* @__PURE__ */ __name((received) => {
|
|
1454
|
+
log?.(`TUNNEL IDENTITY MISMATCH: received ${received}; possible interception`);
|
|
1455
|
+
try {
|
|
1456
|
+
opts.onMismatch?.(received);
|
|
1457
|
+
} catch {
|
|
1458
|
+
}
|
|
1459
|
+
}, "onMismatch"),
|
|
1460
|
+
onTransport: /* @__PURE__ */ __name((t, reason) => log?.(`${reason ?? "transport"}, using ${t}`), "onTransport"),
|
|
1461
|
+
// Pins learned during a key rotation: saved only while the store still holds this
|
|
1462
|
+
// configuration (same proxy, device and token), under the entry's lock; false (never
|
|
1463
|
+
// tried again by this connection) once setup replaced it or forget deleted it.
|
|
1464
|
+
savePinsStoppedLine: "the store now holds another configuration, or none (setup or forget ran since): the proxy keys learned by this connection are no longer saved",
|
|
1465
|
+
savePins: /* @__PURE__ */ __name((pins) => withEntry(store, name, async () => {
|
|
1466
|
+
const raw2 = await viaStore("read the configuration", () => store.get(name));
|
|
1467
|
+
if (raw2 === null || raw2 === void 0) return false;
|
|
1468
|
+
let current;
|
|
1469
|
+
try {
|
|
1470
|
+
current = parseSaved(raw2);
|
|
1471
|
+
} catch {
|
|
1472
|
+
return false;
|
|
1473
|
+
}
|
|
1474
|
+
if (current.url !== cfg.url || current.device !== cfg.device || current.token !== cfg.token) return false;
|
|
1475
|
+
await viaStore("save the configuration", () => store.set(name, JSON.stringify({ ...current, pins })));
|
|
1476
|
+
return true;
|
|
1477
|
+
}), "savePins")
|
|
1478
|
+
});
|
|
1479
|
+
} catch (err) {
|
|
1480
|
+
const code = err.code;
|
|
1481
|
+
if (code === "EADDRINUSE") throw new O1ConnectError("port_in_use", `port ${port} is in use`);
|
|
1482
|
+
if (code === "EACCES" || code === "EADDRNOTAVAIL") throw new O1ConnectError("port_unavailable", `cannot listen on 127.0.0.1:${port} (${code})`, { cause: err });
|
|
1483
|
+
throw err;
|
|
1484
|
+
}
|
|
1485
|
+
if (opts.check !== false) {
|
|
1486
|
+
try {
|
|
1487
|
+
await session.pinSync.syncNow();
|
|
1488
|
+
} catch (err) {
|
|
1489
|
+
await session.close();
|
|
1490
|
+
if (err instanceof IdentityMismatchError || err instanceof BackoffError) {
|
|
1491
|
+
throw new O1ConnectError("identity_mismatch", "the proxy presented a key that is not pinned: possible interception (nothing was sent)");
|
|
1492
|
+
}
|
|
1493
|
+
throw new O1ConnectError("tunnel_unavailable", `the tunnel did not answer: ${err instanceof Error ? err.message : String(err)}`);
|
|
1494
|
+
}
|
|
1495
|
+
}
|
|
1496
|
+
const baseUrl = `http://127.0.0.1:${session.port}`;
|
|
1497
|
+
log?.(`o1-connect library ${VERSION} for ${cfg.device} listening on ${baseUrl} (transport ${cfg.transport})`);
|
|
1498
|
+
return { baseUrl, openaiBaseUrl: `${baseUrl}/v1`, apiKey, port: session.port, device: cfg.device, close: session.close };
|
|
1499
|
+
}
|
|
1500
|
+
__name(open, "open");
|
|
1501
|
+
async function forget(opts) {
|
|
1502
|
+
const store = checkStore(opts?.store);
|
|
1503
|
+
const name = entryName(opts.name);
|
|
1504
|
+
await withEntry(store, name, () => viaStore("delete the configuration", () => store.delete(name)));
|
|
1505
|
+
}
|
|
1506
|
+
__name(forget, "forget");
|
|
1507
|
+
|
|
1508
|
+
// packages/core/src/mcp/token-storage/file-token-storage.ts
|
|
1509
|
+
init_esbuild_shims();
|
|
1510
|
+
import { promises as fs } from "node:fs";
|
|
1511
|
+
import * as path from "node:path";
|
|
1512
|
+
import * as os from "node:os";
|
|
1513
|
+
import * as crypto from "node:crypto";
|
|
1514
|
+
var UnreadableSecretFileError = class extends Error {
|
|
1515
|
+
static {
|
|
1516
|
+
__name(this, "UnreadableSecretFileError");
|
|
1517
|
+
}
|
|
1518
|
+
filePath;
|
|
1519
|
+
constructor(filePath, options) {
|
|
1520
|
+
super(
|
|
1521
|
+
`${filePath} cannot be decrypted on this machine: it was saved under another host name or user, or it is damaged`,
|
|
1522
|
+
options
|
|
1523
|
+
);
|
|
1524
|
+
this.name = "UnreadableSecretFileError";
|
|
1525
|
+
this.filePath = filePath;
|
|
1526
|
+
}
|
|
1527
|
+
};
|
|
1528
|
+
function isUnreadableSecretFileError(error) {
|
|
1529
|
+
return error instanceof UnreadableSecretFileError;
|
|
1530
|
+
}
|
|
1531
|
+
__name(isUnreadableSecretFileError, "isUnreadableSecretFileError");
|
|
1532
|
+
function backupStamp(date) {
|
|
1533
|
+
const pad = /* @__PURE__ */ __name((n) => String(n).padStart(2, "0"), "pad");
|
|
1534
|
+
return `${date.getFullYear()}${pad(date.getMonth() + 1)}${pad(date.getDate())}-${pad(date.getHours())}${pad(date.getMinutes())}${pad(date.getSeconds())}`;
|
|
1535
|
+
}
|
|
1536
|
+
__name(backupStamp, "backupStamp");
|
|
1537
|
+
async function setAsideUnreadableSecretFile(filePath, now = /* @__PURE__ */ new Date()) {
|
|
1538
|
+
const backupPath = `${filePath}.unreadable-${backupStamp(now)}.bak`;
|
|
1539
|
+
await fs.rename(filePath, backupPath);
|
|
1540
|
+
return backupPath;
|
|
1541
|
+
}
|
|
1542
|
+
__name(setAsideUnreadableSecretFile, "setAsideUnreadableSecretFile");
|
|
1543
|
+
var FileTokenStorage = class extends BaseTokenStorage {
|
|
1544
|
+
static {
|
|
1545
|
+
__name(this, "FileTokenStorage");
|
|
1546
|
+
}
|
|
1547
|
+
tokenFilePath;
|
|
1548
|
+
secretFilePath;
|
|
1549
|
+
encryptionKey;
|
|
1550
|
+
constructor(serviceName) {
|
|
1551
|
+
super(serviceName);
|
|
1552
|
+
const configDir = Storage.getGlobalO1CodeDir();
|
|
1553
|
+
this.tokenFilePath = path.join(configDir, "mcp-oauth-tokens-v2.json");
|
|
1554
|
+
this.secretFilePath = path.join(configDir, "extension-secrets-v1.json");
|
|
1555
|
+
this.encryptionKey = this.deriveEncryptionKey();
|
|
1556
|
+
}
|
|
1557
|
+
deriveEncryptionKey() {
|
|
1558
|
+
const salt = `${os.hostname()}-${os.userInfo().username}-o1-code`;
|
|
1559
|
+
return crypto.scryptSync("o1-code-oauth", salt, 32);
|
|
1560
|
+
}
|
|
1561
|
+
encrypt(text) {
|
|
1562
|
+
const iv = crypto.randomBytes(16);
|
|
1563
|
+
const cipher = crypto.createCipheriv("aes-256-gcm", this.encryptionKey, iv);
|
|
1564
|
+
let encrypted = cipher.update(text, "utf8", "hex");
|
|
1565
|
+
encrypted += cipher.final("hex");
|
|
1566
|
+
const authTag = cipher.getAuthTag();
|
|
1567
|
+
return iv.toString("hex") + ":" + authTag.toString("hex") + ":" + encrypted;
|
|
1568
|
+
}
|
|
1569
|
+
decrypt(encryptedData) {
|
|
1570
|
+
const parts = encryptedData.split(":");
|
|
1571
|
+
if (parts.length !== 3) {
|
|
1572
|
+
throw new Error("Invalid encrypted data format");
|
|
1573
|
+
}
|
|
1574
|
+
const iv = Buffer.from(parts[0], "hex");
|
|
1575
|
+
const authTag = Buffer.from(parts[1], "hex");
|
|
1576
|
+
const encrypted = parts[2];
|
|
1577
|
+
const decipher = crypto.createDecipheriv(
|
|
1578
|
+
"aes-256-gcm",
|
|
1579
|
+
this.encryptionKey,
|
|
1580
|
+
iv
|
|
1581
|
+
);
|
|
1582
|
+
decipher.setAuthTag(authTag);
|
|
1583
|
+
let decrypted = decipher.update(encrypted, "hex", "utf8");
|
|
1584
|
+
decrypted += decipher.final("utf8");
|
|
1585
|
+
return decrypted;
|
|
1586
|
+
}
|
|
1587
|
+
async ensureDirectoryExists() {
|
|
1588
|
+
const dir = path.dirname(this.tokenFilePath);
|
|
1589
|
+
await fs.mkdir(dir, { recursive: true, mode: 448 });
|
|
1590
|
+
}
|
|
1591
|
+
async loadTokens(allowMissing = false) {
|
|
1592
|
+
try {
|
|
1593
|
+
const data = await fs.readFile(this.tokenFilePath, "utf-8");
|
|
1594
|
+
const decrypted = this.decrypt(data);
|
|
1595
|
+
const tokens = JSON.parse(decrypted);
|
|
1596
|
+
return new Map(Object.entries(tokens));
|
|
1597
|
+
} catch (error) {
|
|
1598
|
+
const err = error;
|
|
1599
|
+
if (err.code === "ENOENT") {
|
|
1600
|
+
if (allowMissing) {
|
|
1601
|
+
return /* @__PURE__ */ new Map();
|
|
1602
|
+
}
|
|
1603
|
+
throw new Error("Token file does not exist");
|
|
1604
|
+
}
|
|
1605
|
+
if (err.message?.includes("Invalid encrypted data format") || err.message?.includes(
|
|
1606
|
+
"Unsupported state or unable to authenticate data"
|
|
1607
|
+
)) {
|
|
1608
|
+
throw new Error("Token file corrupted");
|
|
1609
|
+
}
|
|
1610
|
+
throw error;
|
|
1611
|
+
}
|
|
1612
|
+
}
|
|
1613
|
+
async saveTokens(tokens) {
|
|
1614
|
+
await this.ensureDirectoryExists();
|
|
1615
|
+
const data = Object.fromEntries(tokens);
|
|
1616
|
+
const json = JSON.stringify(data, null, 2);
|
|
1617
|
+
const encrypted = this.encrypt(json);
|
|
1618
|
+
await atomicWriteFile(this.tokenFilePath, encrypted, {
|
|
1619
|
+
mode: 384,
|
|
1620
|
+
forceMode: true,
|
|
1621
|
+
noFollow: true
|
|
1622
|
+
});
|
|
1623
|
+
}
|
|
1624
|
+
async getCredentials(serverName) {
|
|
1625
|
+
const tokens = await this.loadTokens();
|
|
1626
|
+
const credentials = tokens.get(serverName);
|
|
1627
|
+
if (!credentials) {
|
|
1628
|
+
return null;
|
|
1629
|
+
}
|
|
1630
|
+
if (this.isTokenExpired(credentials)) {
|
|
1631
|
+
return null;
|
|
1632
|
+
}
|
|
1633
|
+
return credentials;
|
|
1634
|
+
}
|
|
1635
|
+
async setCredentials(credentials) {
|
|
1636
|
+
this.validateCredentials(credentials);
|
|
1637
|
+
const tokens = await this.loadTokens(true);
|
|
1638
|
+
const updatedCredentials = {
|
|
1639
|
+
...credentials,
|
|
1640
|
+
updatedAt: Date.now()
|
|
1641
|
+
};
|
|
1642
|
+
tokens.set(credentials.serverName, updatedCredentials);
|
|
1643
|
+
await this.saveTokens(tokens);
|
|
1644
|
+
}
|
|
1645
|
+
async deleteCredentials(serverName) {
|
|
1646
|
+
const tokens = await this.loadTokens();
|
|
1647
|
+
if (!tokens.has(serverName)) {
|
|
1648
|
+
throw new Error(`No credentials found for ${serverName}`);
|
|
1649
|
+
}
|
|
1650
|
+
tokens.delete(serverName);
|
|
1651
|
+
if (tokens.size === 0) {
|
|
1652
|
+
try {
|
|
1653
|
+
await fs.unlink(this.tokenFilePath);
|
|
1654
|
+
} catch (error) {
|
|
1655
|
+
const err = error;
|
|
1656
|
+
if (err.code !== "ENOENT") {
|
|
1657
|
+
throw error;
|
|
1658
|
+
}
|
|
1659
|
+
}
|
|
1660
|
+
} else {
|
|
1661
|
+
await this.saveTokens(tokens);
|
|
1662
|
+
}
|
|
1663
|
+
}
|
|
1664
|
+
async listServers() {
|
|
1665
|
+
const tokens = await this.loadTokens();
|
|
1666
|
+
return Array.from(tokens.keys());
|
|
1667
|
+
}
|
|
1668
|
+
async getAllCredentials() {
|
|
1669
|
+
const tokens = await this.loadTokens();
|
|
1670
|
+
const result = /* @__PURE__ */ new Map();
|
|
1671
|
+
for (const [serverName, credentials] of tokens) {
|
|
1672
|
+
if (!this.isTokenExpired(credentials)) {
|
|
1673
|
+
result.set(serverName, credentials);
|
|
1674
|
+
}
|
|
1675
|
+
}
|
|
1676
|
+
return result;
|
|
1677
|
+
}
|
|
1678
|
+
async clearAll() {
|
|
1679
|
+
try {
|
|
1680
|
+
await fs.unlink(this.tokenFilePath);
|
|
1681
|
+
} catch (error) {
|
|
1682
|
+
const err = error;
|
|
1683
|
+
if (err.code !== "ENOENT") {
|
|
1684
|
+
throw error;
|
|
1685
|
+
}
|
|
1686
|
+
}
|
|
1687
|
+
}
|
|
1688
|
+
async loadSecrets() {
|
|
1689
|
+
let data;
|
|
1690
|
+
try {
|
|
1691
|
+
data = await fs.readFile(this.secretFilePath, "utf-8");
|
|
1692
|
+
} catch (error) {
|
|
1693
|
+
const err = error;
|
|
1694
|
+
if (err.code === "ENOENT") {
|
|
1695
|
+
return {};
|
|
1696
|
+
}
|
|
1697
|
+
throw error;
|
|
1698
|
+
}
|
|
1699
|
+
try {
|
|
1700
|
+
return JSON.parse(this.decrypt(data));
|
|
1701
|
+
} catch (error) {
|
|
1702
|
+
throw new UnreadableSecretFileError(this.secretFilePath, {
|
|
1703
|
+
cause: error
|
|
1704
|
+
});
|
|
1705
|
+
}
|
|
1706
|
+
}
|
|
1707
|
+
async saveSecrets(secrets) {
|
|
1708
|
+
await this.ensureDirectoryExists();
|
|
1709
|
+
const encrypted = this.encrypt(JSON.stringify(secrets));
|
|
1710
|
+
await atomicWriteFile(this.secretFilePath, encrypted, {
|
|
1711
|
+
mode: 384,
|
|
1712
|
+
forceMode: true,
|
|
1713
|
+
noFollow: true
|
|
1714
|
+
});
|
|
1715
|
+
}
|
|
1716
|
+
// The encrypted file is always usable, so the file backend is always available.
|
|
1717
|
+
async isAvailable() {
|
|
1718
|
+
return true;
|
|
1719
|
+
}
|
|
1720
|
+
async setSecret(key, value) {
|
|
1721
|
+
const secrets = await this.loadSecrets();
|
|
1722
|
+
(secrets[this.serviceName] ??= {})[key] = value;
|
|
1723
|
+
await this.saveSecrets(secrets);
|
|
1724
|
+
}
|
|
1725
|
+
async getSecret(key) {
|
|
1726
|
+
const secrets = await this.loadSecrets();
|
|
1727
|
+
return secrets[this.serviceName]?.[key] ?? null;
|
|
1728
|
+
}
|
|
1729
|
+
// Idempotent: deleting a missing secret is a no-op (the keychain backend
|
|
1730
|
+
// throws, but extension-settings callers do not depend on that signal).
|
|
1731
|
+
async deleteSecret(key) {
|
|
1732
|
+
const secrets = await this.loadSecrets();
|
|
1733
|
+
const bucket = secrets[this.serviceName];
|
|
1734
|
+
if (!bucket || !(key in bucket)) {
|
|
1735
|
+
return;
|
|
1736
|
+
}
|
|
1737
|
+
delete bucket[key];
|
|
1738
|
+
if (Object.keys(bucket).length === 0) {
|
|
1739
|
+
delete secrets[this.serviceName];
|
|
1740
|
+
}
|
|
1741
|
+
await this.saveSecrets(secrets);
|
|
1742
|
+
}
|
|
1743
|
+
async listSecrets() {
|
|
1744
|
+
const secrets = await this.loadSecrets();
|
|
1745
|
+
return Object.keys(secrets[this.serviceName] ?? {});
|
|
1746
|
+
}
|
|
1747
|
+
};
|
|
1748
|
+
|
|
1749
|
+
// packages/core/src/providers/o1-connect/store.ts
|
|
1750
|
+
init_esbuild_shims();
|
|
1751
|
+
|
|
1752
|
+
// packages/core/src/mcp/token-storage/hybrid-token-storage.ts
|
|
1753
|
+
init_esbuild_shims();
|
|
1754
|
+
|
|
1755
|
+
// packages/core/src/mcp/token-storage/types.ts
|
|
1756
|
+
init_esbuild_shims();
|
|
1757
|
+
var TokenStorageType = /* @__PURE__ */ ((TokenStorageType2) => {
|
|
1758
|
+
TokenStorageType2["KEYCHAIN"] = "keychain";
|
|
1759
|
+
TokenStorageType2["ENCRYPTED_FILE"] = "encrypted_file";
|
|
1760
|
+
return TokenStorageType2;
|
|
1761
|
+
})(TokenStorageType || {});
|
|
1762
|
+
|
|
1763
|
+
// packages/core/src/mcp/token-storage/hybrid-token-storage.ts
|
|
1764
|
+
var FORCE_FILE_STORAGE_ENV_VAR = "O1CODE_FORCE_FILE_STORAGE";
|
|
1765
|
+
var HybridTokenStorage = class extends BaseTokenStorage {
|
|
1766
|
+
static {
|
|
1767
|
+
__name(this, "HybridTokenStorage");
|
|
1768
|
+
}
|
|
1769
|
+
storage = null;
|
|
1770
|
+
storageType = null;
|
|
1771
|
+
storageInitPromise = null;
|
|
1772
|
+
constructor(serviceName) {
|
|
1773
|
+
super(serviceName);
|
|
1774
|
+
}
|
|
1775
|
+
async initializeStorage() {
|
|
1776
|
+
const forceFileStorage = process.env[FORCE_FILE_STORAGE_ENV_VAR] === "true";
|
|
1777
|
+
if (!forceFileStorage) {
|
|
1778
|
+
try {
|
|
1779
|
+
const { KeychainTokenStorage } = await import("./keychain-token-storage-SJOKJDWV.js");
|
|
1780
|
+
const keychainStorage = new KeychainTokenStorage(this.serviceName);
|
|
1781
|
+
const isAvailable = await keychainStorage.isAvailable();
|
|
1782
|
+
if (isAvailable) {
|
|
1783
|
+
this.storage = keychainStorage;
|
|
1784
|
+
this.storageType = "keychain" /* KEYCHAIN */;
|
|
1785
|
+
return this.storage;
|
|
1786
|
+
}
|
|
1787
|
+
} catch (_e) {
|
|
1788
|
+
}
|
|
1789
|
+
}
|
|
1790
|
+
this.storage = new FileTokenStorage(this.serviceName);
|
|
1791
|
+
this.storageType = "encrypted_file" /* ENCRYPTED_FILE */;
|
|
1792
|
+
return this.storage;
|
|
1793
|
+
}
|
|
1794
|
+
async getStorage() {
|
|
1795
|
+
if (this.storage !== null) {
|
|
1796
|
+
return this.storage;
|
|
1797
|
+
}
|
|
1798
|
+
if (!this.storageInitPromise) {
|
|
1799
|
+
this.storageInitPromise = this.initializeStorage();
|
|
1800
|
+
}
|
|
1801
|
+
return await this.storageInitPromise;
|
|
1802
|
+
}
|
|
1803
|
+
async getCredentials(serverName) {
|
|
1804
|
+
const storage = await this.getStorage();
|
|
1805
|
+
return storage.getCredentials(serverName);
|
|
1806
|
+
}
|
|
1807
|
+
async setCredentials(credentials) {
|
|
1808
|
+
const storage = await this.getStorage();
|
|
1809
|
+
await storage.setCredentials(credentials);
|
|
1810
|
+
}
|
|
1811
|
+
async deleteCredentials(serverName) {
|
|
1812
|
+
const storage = await this.getStorage();
|
|
1813
|
+
await storage.deleteCredentials(serverName);
|
|
1814
|
+
}
|
|
1815
|
+
async listServers() {
|
|
1816
|
+
const storage = await this.getStorage();
|
|
1817
|
+
return storage.listServers();
|
|
1818
|
+
}
|
|
1819
|
+
async getAllCredentials() {
|
|
1820
|
+
const storage = await this.getStorage();
|
|
1821
|
+
return storage.getAllCredentials();
|
|
1822
|
+
}
|
|
1823
|
+
async clearAll() {
|
|
1824
|
+
const storage = await this.getStorage();
|
|
1825
|
+
await storage.clearAll();
|
|
1826
|
+
}
|
|
1827
|
+
async getStorageType() {
|
|
1828
|
+
await this.getStorage();
|
|
1829
|
+
return this.storageType;
|
|
1830
|
+
}
|
|
1831
|
+
// Secret API — delegates to whichever backend is active (keychain when
|
|
1832
|
+
// available, otherwise the encrypted file). This is what lets sensitive
|
|
1833
|
+
// extension settings degrade gracefully to file storage without a keychain.
|
|
1834
|
+
async isAvailable() {
|
|
1835
|
+
const storage = await this.getStorage();
|
|
1836
|
+
return storage.isAvailable();
|
|
1837
|
+
}
|
|
1838
|
+
async setSecret(key, value) {
|
|
1839
|
+
const storage = await this.getStorage();
|
|
1840
|
+
await storage.setSecret(key, value);
|
|
1841
|
+
}
|
|
1842
|
+
async getSecret(key) {
|
|
1843
|
+
const storage = await this.getStorage();
|
|
1844
|
+
return storage.getSecret(key);
|
|
1845
|
+
}
|
|
1846
|
+
async deleteSecret(key) {
|
|
1847
|
+
const storage = await this.getStorage();
|
|
1848
|
+
await storage.deleteSecret(key);
|
|
1849
|
+
}
|
|
1850
|
+
async listSecrets() {
|
|
1851
|
+
const storage = await this.getStorage();
|
|
1852
|
+
return storage.listSecrets();
|
|
1853
|
+
}
|
|
1854
|
+
};
|
|
1855
|
+
|
|
1856
|
+
// packages/core/src/providers/o1-connect/store.ts
|
|
1857
|
+
var O1_CONNECT_SERVICE_NAME = "o1-code-organizaone";
|
|
1858
|
+
function createO1ConnectStore(storage = new HybridTokenStorage(O1_CONNECT_SERVICE_NAME)) {
|
|
1859
|
+
return {
|
|
1860
|
+
async get(name) {
|
|
1861
|
+
return storage.getSecret(name);
|
|
1862
|
+
},
|
|
1863
|
+
async set(name, value) {
|
|
1864
|
+
await storage.setSecret(name, value);
|
|
1865
|
+
},
|
|
1866
|
+
async delete(name) {
|
|
1867
|
+
await storage.deleteSecret(name);
|
|
1868
|
+
}
|
|
1869
|
+
};
|
|
1870
|
+
}
|
|
1871
|
+
__name(createO1ConnectStore, "createO1ConnectStore");
|
|
1872
|
+
|
|
1873
|
+
// packages/core/src/providers/o1-connect/session.ts
|
|
1874
|
+
async function setupO1Connect(code, options) {
|
|
1875
|
+
return setup(code.trim(), {
|
|
1876
|
+
store: options.store ?? createO1ConnectStore(),
|
|
1877
|
+
onFingerprint: options.onFingerprint,
|
|
1878
|
+
...options.transport ? { transport: options.transport } : {}
|
|
1879
|
+
});
|
|
1880
|
+
}
|
|
1881
|
+
__name(setupO1Connect, "setupO1Connect");
|
|
1882
|
+
async function openO1Connect(options = {}) {
|
|
1883
|
+
let connection;
|
|
1884
|
+
try {
|
|
1885
|
+
connection = await open({
|
|
1886
|
+
store: options.store ?? createO1ConnectStore(),
|
|
1887
|
+
...options.log ? { log: options.log } : {}
|
|
1888
|
+
});
|
|
1889
|
+
} catch (error) {
|
|
1890
|
+
if (isO1ConnectError(error) && error.code === "store_failed" && isUnreadableSecretFileError(error.cause)) {
|
|
1891
|
+
throw new O1ConnectError(
|
|
1892
|
+
"store_failed",
|
|
1893
|
+
describeO1ConnectError(error).message,
|
|
1894
|
+
{ cause: error.cause }
|
|
1895
|
+
);
|
|
1896
|
+
}
|
|
1897
|
+
throw error;
|
|
1898
|
+
}
|
|
1899
|
+
return {
|
|
1900
|
+
baseUrl: connection.baseUrl,
|
|
1901
|
+
openaiBaseUrl: connection.openaiBaseUrl,
|
|
1902
|
+
apiKey: connection.apiKey,
|
|
1903
|
+
device: connection.device,
|
|
1904
|
+
close: /* @__PURE__ */ __name(() => connection.close(), "close")
|
|
1905
|
+
};
|
|
1906
|
+
}
|
|
1907
|
+
__name(openO1Connect, "openO1Connect");
|
|
1908
|
+
async function forgetO1Connect(options = {}) {
|
|
1909
|
+
await forget({ store: options.store ?? createO1ConnectStore() });
|
|
1910
|
+
}
|
|
1911
|
+
__name(forgetO1Connect, "forgetO1Connect");
|
|
1912
|
+
async function setAsideUnreadableSecretStore(filePath) {
|
|
1913
|
+
return setAsideUnreadableSecretFile(filePath);
|
|
1914
|
+
}
|
|
1915
|
+
__name(setAsideUnreadableSecretStore, "setAsideUnreadableSecretStore");
|
|
1916
|
+
function o1ConnectBaseUrlFor(session, authType) {
|
|
1917
|
+
return authType === "openai" /* USE_OPENAI */ || authType === "openai-responses" /* USE_OPENAI_RESPONSES */ ? session.openaiBaseUrl : session.baseUrl;
|
|
1918
|
+
}
|
|
1919
|
+
__name(o1ConnectBaseUrlFor, "o1ConnectBaseUrlFor");
|
|
1920
|
+
function isO1ConnectError(error) {
|
|
1921
|
+
return error instanceof O1ConnectError;
|
|
1922
|
+
}
|
|
1923
|
+
__name(isO1ConnectError, "isO1ConnectError");
|
|
1924
|
+
function describeO1ConnectError(error) {
|
|
1925
|
+
if (isO1ConnectError(error) && error.code === "store_failed" && isUnreadableSecretFileError(error.cause)) {
|
|
1926
|
+
return {
|
|
1927
|
+
message: `The secret store at ${error.cause.filePath} cannot be decrypted on this machine: it was saved under another host name or user, or it is damaged. Setting up the connection code again offers to keep it as a .bak and start a new one.`,
|
|
1928
|
+
forgetAndSetUpAgain: false,
|
|
1929
|
+
unreadableSecretFile: error.cause.filePath
|
|
1930
|
+
};
|
|
1931
|
+
}
|
|
1932
|
+
if (!isO1ConnectError(error)) {
|
|
1933
|
+
return {
|
|
1934
|
+
message: error instanceof Error ? error.message : String(error),
|
|
1935
|
+
forgetAndSetUpAgain: false
|
|
1936
|
+
};
|
|
1937
|
+
}
|
|
1938
|
+
switch (error.code) {
|
|
1939
|
+
case "invalid_code":
|
|
1940
|
+
return {
|
|
1941
|
+
message: "That is not an o1-gateway connection code (it starts with o1gw1.), or it is damaged. Paste it again.",
|
|
1942
|
+
forgetAndSetUpAgain: false
|
|
1943
|
+
};
|
|
1944
|
+
case "fingerprint_rejected":
|
|
1945
|
+
return {
|
|
1946
|
+
message: "Nothing was saved: the fingerprints were not confirmed.",
|
|
1947
|
+
forgetAndSetUpAgain: false
|
|
1948
|
+
};
|
|
1949
|
+
case "not_set_up":
|
|
1950
|
+
return {
|
|
1951
|
+
message: "No o1-gateway connection is saved on this machine. Paste a connection code to set one up.",
|
|
1952
|
+
forgetAndSetUpAgain: false
|
|
1953
|
+
};
|
|
1954
|
+
case "invalid_config":
|
|
1955
|
+
return {
|
|
1956
|
+
message: "The saved o1-gateway connection is not one this version can read. Paste a connection code to set it up again.",
|
|
1957
|
+
forgetAndSetUpAgain: true
|
|
1958
|
+
};
|
|
1959
|
+
case "store_failed":
|
|
1960
|
+
return {
|
|
1961
|
+
message: `The secret store refused the connection (${error.cause instanceof Error ? error.cause.message : "locked or unavailable"}).`,
|
|
1962
|
+
forgetAndSetUpAgain: false
|
|
1963
|
+
};
|
|
1964
|
+
case "identity_mismatch":
|
|
1965
|
+
return {
|
|
1966
|
+
message: "The proxy's key is not the one you pinned: this network may be intercepting the connection. Nothing was sent. Do not use a direct connection on this network.",
|
|
1967
|
+
forgetAndSetUpAgain: false
|
|
1968
|
+
};
|
|
1969
|
+
case "tunnel_unavailable":
|
|
1970
|
+
return {
|
|
1971
|
+
message: "The tunnel to o1-gateway could not be opened (network, proxy, or a device that may not tunnel). Try again later; if it persists, paste a new connection code.",
|
|
1972
|
+
forgetAndSetUpAgain: false
|
|
1973
|
+
};
|
|
1974
|
+
case "port_in_use":
|
|
1975
|
+
case "port_unavailable":
|
|
1976
|
+
return {
|
|
1977
|
+
message: `No loopback port could be opened for the tunnel: ${error.message}`,
|
|
1978
|
+
forgetAndSetUpAgain: false
|
|
1979
|
+
};
|
|
1980
|
+
default:
|
|
1981
|
+
return { message: error.message, forgetAndSetUpAgain: false };
|
|
1982
|
+
}
|
|
1983
|
+
}
|
|
1984
|
+
__name(describeO1ConnectError, "describeO1ConnectError");
|
|
1985
|
+
|
|
1986
|
+
export {
|
|
1987
|
+
O1ConnectError,
|
|
1988
|
+
FileTokenStorage,
|
|
1989
|
+
TokenStorageType,
|
|
1990
|
+
HybridTokenStorage,
|
|
1991
|
+
setupO1Connect,
|
|
1992
|
+
openO1Connect,
|
|
1993
|
+
forgetO1Connect,
|
|
1994
|
+
setAsideUnreadableSecretStore,
|
|
1995
|
+
o1ConnectBaseUrlFor,
|
|
1996
|
+
isO1ConnectError,
|
|
1997
|
+
describeO1ConnectError
|
|
1998
|
+
};
|
|
1999
|
+
/**
|
|
2000
|
+
* @license
|
|
2001
|
+
* Copyright 2025 Google LLC
|
|
2002
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
2003
|
+
*/
|
|
2004
|
+
/**
|
|
2005
|
+
* @license
|
|
2006
|
+
* Copyright 2026 o1-code contributors
|
|
2007
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
2008
|
+
*/
|