bloby-bot 0.30.1 → 0.32.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist-bloby/assets/angular-html-DxXh-QMA.js +1 -0
- package/dist-bloby/assets/{angular-ts-BJPoZ_vo.js → angular-ts-BNEmshfa.js} +1 -1
- package/dist-bloby/assets/{apl-CyVuPSCb.js → apl-CD5excG6.js} +1 -1
- package/dist-bloby/assets/{astro-CKc3-gwH.js → astro-BsuvMd51.js} +1 -1
- package/dist-bloby/assets/{blade-DMAsVBaV.js → blade-BzCfsa5s.js} +1 -1
- package/dist-bloby/assets/{bloby-CqtSjokc.js → bloby-B1w9ReAR.js} +19 -19
- package/dist-bloby/assets/{bsl-B1qbqNES.js → bsl-BODK--Fo.js} +1 -1
- package/dist-bloby/assets/c-QpHxjkb9.js +1 -0
- package/dist-bloby/assets/{cairo-BDj8wWhy.js → cairo-BwpHIzLA.js} +1 -1
- package/dist-bloby/assets/{cobol-hBjMZW-R.js → cobol-Dvyhsqyz.js} +1 -1
- package/dist-bloby/assets/{coffee-B4OtR3w-.js → coffee-CyaHgmLy.js} +1 -1
- package/dist-bloby/assets/cpp-DlbkFQj0.js +1 -0
- package/dist-bloby/assets/{crystal-x3k4USY-.js → crystal-CR4Yni1K.js} +1 -1
- package/dist-bloby/assets/css-jgrsA6xW.js +1 -0
- package/dist-bloby/assets/{edge-B-9TrlZC.js → edge-CeStCG6d.js} +1 -1
- package/dist-bloby/assets/{elixir-CXCzbj1r.js → elixir-CNfWtFyr.js} +1 -1
- package/dist-bloby/assets/{elm-BdRiwmde.js → elm-WhyRrsYH.js} +1 -1
- package/dist-bloby/assets/{erb-C9tvRzx3.js → erb-BvJkfco6.js} +1 -1
- package/dist-bloby/assets/{erlang-Bc4hQuqa.js → erlang-CjZU-VwD.js} +1 -1
- package/dist-bloby/assets/{fortran-fixed-form-B02FOYI-.js → fortran-fixed-form-CLgNuKMV.js} +1 -1
- package/dist-bloby/assets/{fsharp-DhdR18vN.js → fsharp-kVgJ9XJy.js} +1 -1
- package/dist-bloby/assets/{gdresource-DgnrMPIx.js → gdresource-DDsMuL8p.js} +1 -1
- package/dist-bloby/assets/{git-commit-DGJi7qBD.js → git-commit-BKhxSJdF.js} +1 -1
- package/dist-bloby/assets/{git-rebase-HwDSB2qJ.js → git-rebase-C1GyNmcX.js} +1 -1
- package/dist-bloby/assets/{glimmer-js-B6LF_BMd.js → glimmer-js-Cl3u7vjy.js} +1 -1
- package/dist-bloby/assets/{glimmer-ts-Cn42BUKe.js → glimmer-ts-CtPlRgiA.js} +1 -1
- package/dist-bloby/assets/globals-B1T5ANQV.js +18 -0
- package/dist-bloby/assets/globals-CcIc79IH.css +2 -0
- package/dist-bloby/assets/glsl-Bz5r7Lln.js +1 -0
- package/dist-bloby/assets/graphql-IV9ZCUty.js +1 -0
- package/dist-bloby/assets/{hack-DDEqRjBe.js → hack-XsNdYtzr.js} +1 -1
- package/dist-bloby/assets/haml-BDRqXh7d.js +1 -0
- package/dist-bloby/assets/{handlebars-g9Qe0ao2.js → handlebars-Rv2-mTii.js} +1 -1
- package/dist-bloby/assets/highlighted-body-OFNGDK62-CHkKxiLr.js +1 -0
- package/dist-bloby/assets/html-BkYSbpwH.js +1 -0
- package/dist-bloby/assets/{html-derivative-D-XvNBA6.js → html-derivative-CNiypDIO.js} +1 -1
- package/dist-bloby/assets/{http-qlhghEti.js → http-Vgn8hmbu.js} +1 -1
- package/dist-bloby/assets/{hurl-CHIKfBKk.js → hurl-C_EA75yu.js} +1 -1
- package/dist-bloby/assets/{hxml-adC65L_J.js → hxml-YehGxZ3T.js} +1 -1
- package/dist-bloby/assets/java-div3J5pm.js +1 -0
- package/dist-bloby/assets/javascript-D7sYfeLa.js +1 -0
- package/dist-bloby/assets/{jinja-BHZt0_Dn.js → jinja-BovBDArb.js} +1 -1
- package/dist-bloby/assets/{jison-TqJYWKkW.js → jison-C7UZOdoh.js} +1 -1
- package/dist-bloby/assets/json-DsAnzN7U.js +1 -0
- package/dist-bloby/assets/jsx-CftCCFil.js +1 -0
- package/dist-bloby/assets/jsx-runtime-DsY8nUeV.js +1 -0
- package/dist-bloby/assets/{julia-CW07fy07.js → julia-D8hM3XBr.js} +1 -1
- package/dist-bloby/assets/{just-DBcWo9WY.js → just-CHnwfRRG.js} +1 -1
- package/dist-bloby/assets/{latex-D6lXiPTF.js → latex-Ccu-ZO0T.js} +1 -1
- package/dist-bloby/assets/{liquid-DhYYAQ41.js → liquid-BRbNyfzr.js} +1 -1
- package/dist-bloby/assets/lua-BcwT4KiU.js +1 -0
- package/dist-bloby/assets/{marko-xtrIBdcL.js → marko-Both7s0f.js} +1 -1
- package/dist-bloby/assets/{mdc-DyZVNnwP.js → mdc-7TpRllSI.js} +1 -1
- package/dist-bloby/assets/mermaid-GHXKKRXX-LsCge3XG.js +1 -0
- package/dist-bloby/assets/{nextflow-JEyYUlIq.js → nextflow-C2lnptGp.js} +1 -1
- package/dist-bloby/assets/{nginx-CGgm1VOJ.js → nginx-1MGd6YK-.js} +1 -1
- package/dist-bloby/assets/{nim-Br7S817Z.js → nim-CXWhMB6N.js} +1 -1
- package/dist-bloby/assets/onboard-DdL-0meC.js +1 -0
- package/dist-bloby/assets/{perl-DqDX-7VG.js → perl-s-whHxj8.js} +1 -1
- package/dist-bloby/assets/{php-DYSqNNGJ.js → php-KdIqqcNq.js} +1 -1
- package/dist-bloby/assets/{pug-Bob3gNio.js → pug-Cj_1QUAx.js} +1 -1
- package/dist-bloby/assets/{qml-CsMafcqN.js → qml-BQHwZF2t.js} +1 -1
- package/dist-bloby/assets/r-BW3neTZ6.js +1 -0
- package/dist-bloby/assets/{razor-B4PzhaZX.js → razor-D4oLdRr2.js} +1 -1
- package/dist-bloby/assets/regexp-CK2Vecnb.js +1 -0
- package/dist-bloby/assets/rolldown-runtime-C-57s9n3.js +1 -0
- package/dist-bloby/assets/{rst-BXuz-2b8.js → rst-kaCCXG10.js} +1 -1
- package/dist-bloby/assets/{ruby-CXP5_4kq.js → ruby-DIAu88GV.js} +1 -1
- package/dist-bloby/assets/{sas-Cov9PzAD.js → sas-DW2U5bKI.js} +1 -1
- package/dist-bloby/assets/scss-CpUwMEPb.js +1 -0
- package/dist-bloby/assets/{shaderlab-DZ3Lsiak.js → shaderlab-CCgEvKkt.js} +1 -1
- package/dist-bloby/assets/shellscript-BhywzMlr.js +1 -0
- package/dist-bloby/assets/{shellsession-BHLVblBB.js → shellsession-vB5Hy6UU.js} +1 -1
- package/dist-bloby/assets/{soy-e0u3-wIA.js → soy-BNDHXm4e.js} +1 -1
- package/dist-bloby/assets/{sparql-B9c85a_o.js → sparql-DH0hFmAi.js} +1 -1
- package/dist-bloby/assets/sql-D395ukne.js +1 -0
- package/dist-bloby/assets/{stata-B1mLEMrz.js → stata-BHXdpuN3.js} +1 -1
- package/dist-bloby/assets/{surrealql-BTKo2XFY.js → surrealql-eiBkI2Jy.js} +1 -1
- package/dist-bloby/assets/{svelte-B7URZ-wm.js → svelte-DcqzCcP6.js} +1 -1
- package/dist-bloby/assets/{templ-DVRYIgGq.js → templ-DpMCsFaT.js} +1 -1
- package/dist-bloby/assets/{tex-CdJ24HJj.js → tex-B71AZ6Vb.js} +1 -1
- package/dist-bloby/assets/{ts-tags-CCSbelDn.js → ts-tags-CRuJWBGV.js} +1 -1
- package/dist-bloby/assets/tsx-CEFbrBOd.js +1 -0
- package/dist-bloby/assets/{twig-Dn4ruiVl.js → twig-CuSIh_aH.js} +1 -1
- package/dist-bloby/assets/typescript-CV9iMaP7.js +1 -0
- package/dist-bloby/assets/{vue-D19cWHwY.js → vue-YbgnWnT2.js} +1 -1
- package/dist-bloby/assets/{vue-html-_j-KC7Cs.js → vue-html-CXsio3-X.js} +1 -1
- package/dist-bloby/assets/{vue-vine-BC03MN6c.js → vue-vine-C8pVogU0.js} +1 -1
- package/dist-bloby/assets/xml-Zmzpltp0.js +1 -0
- package/dist-bloby/assets/{xsl-DR-zOeBm.js → xsl-CqzL2w2F.js} +1 -1
- package/dist-bloby/assets/yaml-Dc_auW1I.js +1 -0
- package/dist-bloby/bloby.html +5 -4
- package/dist-bloby/onboard.html +5 -4
- package/package.json +1 -1
- package/supervisor/bloby-agent.ts +65 -633
- package/supervisor/chat/OnboardWizard.tsx +315 -69
- package/supervisor/harnesses/claude.ts +684 -0
- package/supervisor/harnesses/codex.ts +73 -0
- package/supervisor/harnesses/types.ts +81 -0
- package/supervisor/index.ts +1 -1
- package/worker/codex-auth.ts +362 -137
- package/worker/index.ts +30 -4
- package/dist-bloby/assets/angular-html-olSuI25H.js +0 -1
- package/dist-bloby/assets/c-BvB8CVo8.js +0 -1
- package/dist-bloby/assets/cpp-BblbpnyR.js +0 -1
- package/dist-bloby/assets/css-zY4NE9yA.js +0 -1
- package/dist-bloby/assets/globals-LpXKrQgt.css +0 -2
- package/dist-bloby/assets/globals-zKQhzQOX.js +0 -18
- package/dist-bloby/assets/glsl-CEMf-THb.js +0 -1
- package/dist-bloby/assets/graphql-dBCd2Czl.js +0 -1
- package/dist-bloby/assets/haml-Ck5pVAEG.js +0 -1
- package/dist-bloby/assets/highlighted-body-OFNGDK62-B0W8hCx-.js +0 -1
- package/dist-bloby/assets/html-CzX0w7K_.js +0 -1
- package/dist-bloby/assets/java-BqKEu7zh.js +0 -1
- package/dist-bloby/assets/javascript-CzjRtS1s.js +0 -1
- package/dist-bloby/assets/json-CdFmZMJc.js +0 -1
- package/dist-bloby/assets/jsx-BL6Wh6Qw.js +0 -1
- package/dist-bloby/assets/jsx-runtime-C0W9Wf2W.js +0 -1
- package/dist-bloby/assets/lua-B4ezzssg.js +0 -1
- package/dist-bloby/assets/mermaid-GHXKKRXX-Dr8I7wwH.js +0 -1
- package/dist-bloby/assets/onboard-C1yXK8uT.js +0 -1
- package/dist-bloby/assets/r-CGji9_vM.js +0 -1
- package/dist-bloby/assets/regexp-CvWG4lI-.js +0 -1
- package/dist-bloby/assets/scss-Efq7Dmbs.js +0 -1
- package/dist-bloby/assets/shellscript-BbEstezf.js +0 -1
- package/dist-bloby/assets/sql-CB6kO6Km.js +0 -1
- package/dist-bloby/assets/tsx-DrzBggF8.js +0 -1
- package/dist-bloby/assets/typescript-COZIw7xM.js +0 -1
- package/dist-bloby/assets/xml-BlDEvPia.js +0 -1
- package/dist-bloby/assets/yaml-iUXljnLs.js +0 -1
- /package/dist-bloby/assets/{abap-smLpETzL.js → abap--vzi7Rxy.js} +0 -0
- /package/dist-bloby/assets/{actionscript-3-o-aRvjY5.js → actionscript-3-CV21uODD.js} +0 -0
- /package/dist-bloby/assets/{ada-D5RRX1i4.js → ada-DpXSEmbw.js} +0 -0
- /package/dist-bloby/assets/{andromeeda-N4z3YzmR.js → andromeeda-C1V0Cb0M.js} +0 -0
- /package/dist-bloby/assets/{apache-DttKlSqg.js → apache-CRmVjs8c.js} +0 -0
- /package/dist-bloby/assets/{apex-DL7j-a2n.js → apex-DIIc4r7E.js} +0 -0
- /package/dist-bloby/assets/{applescript-DCbL_2FX.js → applescript-DMX6NGuU.js} +0 -0
- /package/dist-bloby/assets/{ara-DqheWJCe.js → ara-3SArX2Zn.js} +0 -0
- /package/dist-bloby/assets/{asciidoc-BH2sf2K4.js → asciidoc-CnVFjb7X.js} +0 -0
- /package/dist-bloby/assets/{asm-rfjOMz3U.js → asm-Cb-CgaBD.js} +0 -0
- /package/dist-bloby/assets/{aurora-x-CND-FqEb.js → aurora-x-CjvoFyi0.js} +0 -0
- /package/dist-bloby/assets/{awk-CMV5n9Z7.js → awk-BYjsWVhV.js} +0 -0
- /package/dist-bloby/assets/{ayu-dark-on7xzq_B.js → ayu-dark-C4UVdT_g.js} +0 -0
- /package/dist-bloby/assets/{ayu-light-DnGeGGc2.js → ayu-light-CFJ-dST-.js} +0 -0
- /package/dist-bloby/assets/{ayu-mirage-DG3LlYC_.js → ayu-mirage-BxM9D2Mi.js} +0 -0
- /package/dist-bloby/assets/{ballerina-BjMqJjlr.js → ballerina-BsKCUtAm.js} +0 -0
- /package/dist-bloby/assets/{bat-BaVbo_qw.js → bat-CJlQOahT.js} +0 -0
- /package/dist-bloby/assets/{beancount-DBt5Mbc_.js → beancount-Cyf6Xlzd.js} +0 -0
- /package/dist-bloby/assets/{berry-Cyjuxiss.js → berry-DHYmFnza.js} +0 -0
- /package/dist-bloby/assets/{bibtex-BV_0nsPq.js → bibtex-BUB9btZd.js} +0 -0
- /package/dist-bloby/assets/{bicep-C2gEwu4J.js → bicep-CNVX4dQj.js} +0 -0
- /package/dist-bloby/assets/{bird2-BRyaEJd0.js → bird2-LZP25z1s.js} +0 -0
- /package/dist-bloby/assets/{c3-DTTbp5N2.js → c3-BI41OB2W.js} +0 -0
- /package/dist-bloby/assets/{cadence-CPz83cmI.js → cadence-BDnzkZFt.js} +0 -0
- /package/dist-bloby/assets/{catppuccin-frappe-Dr22sraq.js → catppuccin-frappe-DpT1lCYX.js} +0 -0
- /package/dist-bloby/assets/{catppuccin-latte-C57EeX8c.js → catppuccin-latte-qMMse1WJ.js} +0 -0
- /package/dist-bloby/assets/{catppuccin-macchiato-CpheI4uD.js → catppuccin-macchiato-DMu0Ac9B.js} +0 -0
- /package/dist-bloby/assets/{catppuccin-mocha-D8Lmdks_.js → catppuccin-mocha-DicTaD9V.js} +0 -0
- /package/dist-bloby/assets/{clarity-CFJ2Yc3q.js → clarity-CPz-yzAn.js} +0 -0
- /package/dist-bloby/assets/{clojure-6R01QgJC.js → clojure-BxJLFKTj.js} +0 -0
- /package/dist-bloby/assets/{cmake-D5rSbMir.js → cmake-BmC4o4J9.js} +0 -0
- /package/dist-bloby/assets/{codeowners-_3A-ClkN.js → codeowners-BPHDuybI.js} +0 -0
- /package/dist-bloby/assets/{codeql-DlIhJTR7.js → codeql-DYWyWzRY.js} +0 -0
- /package/dist-bloby/assets/{common-lisp-BvQzz173.js → common-lisp-CsDdrwfa.js} +0 -0
- /package/dist-bloby/assets/{coq-DTNXReIC.js → coq-BZNwxllS.js} +0 -0
- /package/dist-bloby/assets/{csharp-BDKQY1K9.js → csharp-BLhQzhUA.js} +0 -0
- /package/dist-bloby/assets/{csv-DNUpVBxl.js → csv-DPc9UZFt.js} +0 -0
- /package/dist-bloby/assets/{cue-DDdBc2s3.js → cue-Bat8Mt8Y.js} +0 -0
- /package/dist-bloby/assets/{cypher-CguaznO2.js → cypher-CTnWkM4Y.js} +0 -0
- /package/dist-bloby/assets/{d-CHcoyjv3.js → d-CccbtpLU.js} +0 -0
- /package/dist-bloby/assets/{dark-plus-CqeW_0kH.js → dark-plus-CKh7uIvz.js} +0 -0
- /package/dist-bloby/assets/{dart-RM3aRphk.js → dart-SbRHwU1n.js} +0 -0
- /package/dist-bloby/assets/{dax-nbnY1d79.js → dax-vVKSTwd9.js} +0 -0
- /package/dist-bloby/assets/{desktop-BD76cvxT.js → desktop-rdWGepy4.js} +0 -0
- /package/dist-bloby/assets/{diff-CdkWtnkL.js → diff-G5H-0DNT.js} +0 -0
- /package/dist-bloby/assets/{docker-ZyPHc6zB.js → docker-CC20slQp.js} +0 -0
- /package/dist-bloby/assets/{dotenv-BdJZEwA6.js → dotenv-C36c6TdP.js} +0 -0
- /package/dist-bloby/assets/{dracula-CtqCPLGa.js → dracula-nNMQZYa7.js} +0 -0
- /package/dist-bloby/assets/{dracula-soft-DmBpwRAy.js → dracula-soft-BPmUsSlJ.js} +0 -0
- /package/dist-bloby/assets/{dream-maker-BJL4qlg3.js → dream-maker-DleGc9Hy.js} +0 -0
- /package/dist-bloby/assets/{emacs-lisp-_LyYa2Ky.js → emacs-lisp-D_QlcMHs.js} +0 -0
- /package/dist-bloby/assets/{everforest-dark-CrbyKk4n.js → everforest-dark-FkVvIMUl.js} +0 -0
- /package/dist-bloby/assets/{everforest-light-DRbSh8Tm.js → everforest-light-d3V1wTAd.js} +0 -0
- /package/dist-bloby/assets/{fennel-CC2BkRsS.js → fennel-BEssPROc.js} +0 -0
- /package/dist-bloby/assets/{fish-D2ZCzqcE.js → fish-Cz5yLEyJ.js} +0 -0
- /package/dist-bloby/assets/{fluent-DKAm3m7q.js → fluent-TGO8KajN.js} +0 -0
- /package/dist-bloby/assets/{fortran-free-form-BPTf0lbT.js → fortran-free-form-CHQXX5JB.js} +0 -0
- /package/dist-bloby/assets/{gdscript-CsadCSaZ.js → gdscript-DBkVGsC6.js} +0 -0
- /package/dist-bloby/assets/{gdshader-PfnN_8tR.js → gdshader-CWQhZP7Q.js} +0 -0
- /package/dist-bloby/assets/{genie-Bj-orPAV.js → genie-7iw14YxB.js} +0 -0
- /package/dist-bloby/assets/{gherkin-COVWpJ-Q.js → gherkin-D4AlAQSH.js} +0 -0
- /package/dist-bloby/assets/{github-dark-ipGuFGYC.js → github-dark-CpBni1SQ.js} +0 -0
- /package/dist-bloby/assets/{github-dark-default-DX3IG_X0.js → github-dark-default-Cr5Q59Ad.js} +0 -0
- /package/dist-bloby/assets/{github-dark-dimmed-CH6p_7ez.js → github-dark-dimmed-WEORr92a.js} +0 -0
- /package/dist-bloby/assets/{github-dark-high-contrast-Bg2sSao5.js → github-dark-high-contrast-uM2plAzl.js} +0 -0
- /package/dist-bloby/assets/{github-light-BxjqOQl0.js → github-light-BROm54vS.js} +0 -0
- /package/dist-bloby/assets/{github-light-default-DwpDd1IQ.js → github-light-default-jSwgX00f.js} +0 -0
- /package/dist-bloby/assets/{github-light-high-contrast-C0QeixPf.js → github-light-high-contrast-Bp9ESXtp.js} +0 -0
- /package/dist-bloby/assets/{gleam-BF2NigGj.js → gleam-D1LXnIj0.js} +0 -0
- /package/dist-bloby/assets/{gn-CMIt9roj.js → gn-CReOiE5m.js} +0 -0
- /package/dist-bloby/assets/{gnuplot-DgWlRTEj.js → gnuplot-DufViamc.js} +0 -0
- /package/dist-bloby/assets/{go-DLnOLj8q.js → go-DC_Zm44P.js} +0 -0
- /package/dist-bloby/assets/{groovy-B9qLWIkE.js → groovy-DEGDVBi0.js} +0 -0
- /package/dist-bloby/assets/{gruvbox-dark-hard-BIMX9ePb.js → gruvbox-dark-hard-C-zANgxR.js} +0 -0
- /package/dist-bloby/assets/{gruvbox-dark-medium-DxuwyVzM.js → gruvbox-dark-medium-DY7Z6TBK.js} +0 -0
- /package/dist-bloby/assets/{gruvbox-dark-soft-ba2fjht0.js → gruvbox-dark-soft-Bpj6owGh.js} +0 -0
- /package/dist-bloby/assets/{gruvbox-light-hard-DAJK2PqC.js → gruvbox-light-hard-DVwTvHam.js} +0 -0
- /package/dist-bloby/assets/{gruvbox-light-medium-WRifDAJD.js → gruvbox-light-medium-98PeoD5Y.js} +0 -0
- /package/dist-bloby/assets/{gruvbox-light-soft-B0dBOoVB.js → gruvbox-light-soft-BHI2PZTB.js} +0 -0
- /package/dist-bloby/assets/{haskell-BaGpmm_c.js → haskell-B-K-GoPf.js} +0 -0
- /package/dist-bloby/assets/{haxe-DfXTCEOz.js → haxe-md5t_Y_E.js} +0 -0
- /package/dist-bloby/assets/{hcl-CbCtpSbi.js → hcl-qSvp9jTg.js} +0 -0
- /package/dist-bloby/assets/{hjson-_4-w-pHU.js → hjson-BNeV6JDA.js} +0 -0
- /package/dist-bloby/assets/{hlsl-DpuYn7pp.js → hlsl-CFf5czm3.js} +0 -0
- /package/dist-bloby/assets/{horizon-C5RtD1fY.js → horizon-C4a2pCDo.js} +0 -0
- /package/dist-bloby/assets/{horizon-bright-pEOice34.js → horizon-bright-Bgpx-0aF.js} +0 -0
- /package/dist-bloby/assets/{houston-A0zg9vNP.js → houston-BZHNDhe5.js} +0 -0
- /package/dist-bloby/assets/{hy-t9zDXBje.js → hy-hI-CUgFt.js} +0 -0
- /package/dist-bloby/assets/{imba-Bxu-TDfw.js → imba-Dr5pZl3x.js} +0 -0
- /package/dist-bloby/assets/{ini-D-v34c8N.js → ini-KxzJ-qxg.js} +0 -0
- /package/dist-bloby/assets/{json5-D0XL1D5m.js → json5-SRM7tKmT.js} +0 -0
- /package/dist-bloby/assets/{jsonc-BBgf3TX3.js → jsonc-Cix1XnqC.js} +0 -0
- /package/dist-bloby/assets/{jsonl-KrKLbtB7.js → jsonl-BzKwo-fD.js} +0 -0
- /package/dist-bloby/assets/{jsonnet-B2ZwWMOt.js → jsonnet-DaiLv8or.js} +0 -0
- /package/dist-bloby/assets/{jssm-ByRpI9-B.js → jssm-B1pV58QJ.js} +0 -0
- /package/dist-bloby/assets/{kanagawa-dragon-CtPmcCMp.js → kanagawa-dragon-BihvT_N-.js} +0 -0
- /package/dist-bloby/assets/{kanagawa-lotus-DpOXEGCa.js → kanagawa-lotus-DQN_bzSt.js} +0 -0
- /package/dist-bloby/assets/{kanagawa-wave-CLVCyc-n.js → kanagawa-wave-Bvz_F3hD.js} +0 -0
- /package/dist-bloby/assets/{kdl-6FPPEH4H.js → kdl-DupMG4Bk.js} +0 -0
- /package/dist-bloby/assets/{kotlin-D4L3Nd2D.js → kotlin-rOGI_fXV.js} +0 -0
- /package/dist-bloby/assets/{kusto-D-mmc07O.js → kusto-CHXnQR8L.js} +0 -0
- /package/dist-bloby/assets/{laserwave-7fEj4XGb.js → laserwave-CPMyzJF3.js} +0 -0
- /package/dist-bloby/assets/{lean-BMVMuP2E.js → lean-B76KugVy.js} +0 -0
- /package/dist-bloby/assets/{less-C95PyOu0.js → less-DwUMKQAG.js} +0 -0
- /package/dist-bloby/assets/{light-plus-DHI2dh3p.js → light-plus-C6sdUQiS.js} +0 -0
- /package/dist-bloby/assets/{llvm-Dy3qORTl.js → llvm-DumY6MCH.js} +0 -0
- /package/dist-bloby/assets/{log-BQop7fzg.js → log-DdP6k-FU.js} +0 -0
- /package/dist-bloby/assets/{logo-DKtnYmrp.js → logo-Cx0gX691.js} +0 -0
- /package/dist-bloby/assets/{luau-Dha4KG0q.js → luau-CI4wJBxI.js} +0 -0
- /package/dist-bloby/assets/{make-DfmS6buu.js → make-BlyBJiAs.js} +0 -0
- /package/dist-bloby/assets/{markdown-BaI9LyM4.js → markdown-BFgBvytj.js} +0 -0
- /package/dist-bloby/assets/{material-theme-Dm6ETY8i.js → material-theme-CyGvAAeY.js} +0 -0
- /package/dist-bloby/assets/{material-theme-darker-BeyjmM5Z.js → material-theme-darker-DJAOSarR.js} +0 -0
- /package/dist-bloby/assets/{material-theme-lighter-CCOYYzKL.js → material-theme-lighter-Cb7t96j2.js} +0 -0
- /package/dist-bloby/assets/{material-theme-ocean-CUtPiiX3.js → material-theme-ocean-EZZ55_HD.js} +0 -0
- /package/dist-bloby/assets/{material-theme-palenight-2L4zMwIM.js → material-theme-palenight-DtmMSfya.js} +0 -0
- /package/dist-bloby/assets/{matlab-BT79Cq_j.js → matlab-BFLobVcp.js} +0 -0
- /package/dist-bloby/assets/{mdx-CxGBwLNt.js → mdx-Baux4qlv.js} +0 -0
- /package/dist-bloby/assets/{mermaid-DLf3axBN.js → mermaid-DHr7T0Rt.js} +0 -0
- /package/dist-bloby/assets/{min-dark-B8TUPGHg.js → min-dark-C9N1U4-e.js} +0 -0
- /package/dist-bloby/assets/{min-light-BDOoBxcA.js → min-light-Ce9oxBvH.js} +0 -0
- /package/dist-bloby/assets/{mipsasm-DXuU_0cI.js → mipsasm-Cgb1v7yV.js} +0 -0
- /package/dist-bloby/assets/{mojo-1Omp2pIo.js → mojo-BnTBKscO.js} +0 -0
- /package/dist-bloby/assets/{monokai-E_VBmXGZ.js → monokai-BGD_YBH-.js} +0 -0
- /package/dist-bloby/assets/{moonbit-DZzq699o.js → moonbit-HXw0ZM4h.js} +0 -0
- /package/dist-bloby/assets/{move-DxDAOJVd.js → move-DG2efXqI.js} +0 -0
- /package/dist-bloby/assets/{narrat-CxDcPPb9.js → narrat-BSasSB5t.js} +0 -0
- /package/dist-bloby/assets/{nextflow-groovy-wvmJO4hi.js → nextflow-groovy-DXLl8k8E.js} +0 -0
- /package/dist-bloby/assets/{night-owl-DefhalNX.js → night-owl-DxqNTXDa.js} +0 -0
- /package/dist-bloby/assets/{night-owl-light-byb3AbEn.js → night-owl-light-MPn8GCyh.js} +0 -0
- /package/dist-bloby/assets/{nix-DsFTDHWT.js → nix-BpnttlWr.js} +0 -0
- /package/dist-bloby/assets/{nord-D5rQzHT-.js → nord-Mt6M_qHE.js} +0 -0
- /package/dist-bloby/assets/{nushell-BQ_rO0AM.js → nushell-CS7Mvg-G.js} +0 -0
- /package/dist-bloby/assets/{objective-c-BMq6JT_m.js → objective-c-U4EuJXJs.js} +0 -0
- /package/dist-bloby/assets/{objective-cpp-DRwojuGv.js → objective-cpp-CE_PrXRW.js} +0 -0
- /package/dist-bloby/assets/{ocaml-e4_gKO6E.js → ocaml-ZsxDKzyv.js} +0 -0
- /package/dist-bloby/assets/{odin-mdTPwYOg.js → odin-CDoDiNYU.js} +0 -0
- /package/dist-bloby/assets/{one-dark-pro-DxXww9jg.js → one-dark-pro-B9P_I1yh.js} +0 -0
- /package/dist-bloby/assets/{one-light-D9c03w7t.js → one-light-Cv3j6GXq.js} +0 -0
- /package/dist-bloby/assets/{openscad-D2aTdNfr.js → openscad-CfJwW9dY.js} +0 -0
- /package/dist-bloby/assets/{pascal-D4n_G8vj.js → pascal-CjE0UTC7.js} +0 -0
- /package/dist-bloby/assets/{pkl-DcjhAp0L.js → pkl-BN6EkHr9.js} +0 -0
- /package/dist-bloby/assets/{plastic-uOsw7z1x.js → plastic-BCnZnfMk.js} +0 -0
- /package/dist-bloby/assets/{plsql-BSy3Ag0J.js → plsql-1LW-CqWM.js} +0 -0
- /package/dist-bloby/assets/{po-Be96RYkA.js → po-Cf5hRC9x.js} +0 -0
- /package/dist-bloby/assets/{poimandres-CyO4HORD.js → poimandres-B_zC30Vt.js} +0 -0
- /package/dist-bloby/assets/{polar-BmhMKWum.js → polar-S6fWkEw8.js} +0 -0
- /package/dist-bloby/assets/{postcss-BTQso3H9.js → postcss-cZXfRCsF.js} +0 -0
- /package/dist-bloby/assets/{powerquery-DVyQv3M2.js → powerquery-CoRQQdNP.js} +0 -0
- /package/dist-bloby/assets/{powershell-BmtCGJVD.js → powershell-B2VxwRch.js} +0 -0
- /package/dist-bloby/assets/{prisma-CCFA9rEj.js → prisma-sLrt38jj.js} +0 -0
- /package/dist-bloby/assets/{prolog-DWGEEo78.js → prolog-CL99toUJ.js} +0 -0
- /package/dist-bloby/assets/{proto-C3eop1kM.js → proto-CtiBQJNn.js} +0 -0
- /package/dist-bloby/assets/{puppet-C0_7Nv5A.js → puppet-BlbpxWq2.js} +0 -0
- /package/dist-bloby/assets/{purescript-Bv-B8yRw.js → purescript-DPcVkoDV.js} +0 -0
- /package/dist-bloby/assets/{python-CKg5r0C3.js → python-BLpCXkJ6.js} +0 -0
- /package/dist-bloby/assets/{qmldir-Cqfb4Lox.js → qmldir-BcwbwUc8.js} +0 -0
- /package/dist-bloby/assets/{qss-Csho9BH8.js → qss-erDfnnzG.js} +0 -0
- /package/dist-bloby/assets/{racket-D8-uWeQH.js → racket-Dch6VQku.js} +0 -0
- /package/dist-bloby/assets/{raku-Cj4JSa8p.js → raku-Db8mfsiE.js} +0 -0
- /package/dist-bloby/assets/{red-z4H4llUY.js → red-DWyyM82l.js} +0 -0
- /package/dist-bloby/assets/{reg-DXGFiJSl.js → reg-3F9yjO5J.js} +0 -0
- /package/dist-bloby/assets/{rel-CunnVTBK.js → rel-CQq8nxxL.js} +0 -0
- /package/dist-bloby/assets/{riscv-C8BcOCkS.js → riscv-CsUwotvK.js} +0 -0
- /package/dist-bloby/assets/{ron-DiDyR9oa.js → ron-BwlS4rWu.js} +0 -0
- /package/dist-bloby/assets/{rose-pine-BADquuIH.js → rose-pine-DEj8gOox.js} +0 -0
- /package/dist-bloby/assets/{rose-pine-dawn-BLcEeBoh.js → rose-pine-dawn-DjarqQ2I.js} +0 -0
- /package/dist-bloby/assets/{rose-pine-moon-BK_tLY1Y.js → rose-pine-moon-D7ffkR_D.js} +0 -0
- /package/dist-bloby/assets/{rosmsg-CHmL1Uyj.js → rosmsg-BmwwVMq5.js} +0 -0
- /package/dist-bloby/assets/{rust-DjJFInu3.js → rust-DsIgDjpj.js} +0 -0
- /package/dist-bloby/assets/{sass-D3xZ5o-v.js → sass-CkIFK_bT.js} +0 -0
- /package/dist-bloby/assets/{scala-B0IRml5w.js → scala-C0TSxhxD.js} +0 -0
- /package/dist-bloby/assets/{scheme-BhwcV3Av.js → scheme-e-5nJ58U.js} +0 -0
- /package/dist-bloby/assets/{sdbl-BmWYR-pI.js → sdbl-DOe8Nral.js} +0 -0
- /package/dist-bloby/assets/{slack-dark-DOnVms5P.js → slack-dark-CClHvLju.js} +0 -0
- /package/dist-bloby/assets/{slack-ochin-DcRTft4q.js → slack-ochin-DwCpSVBI.js} +0 -0
- /package/dist-bloby/assets/{smalltalk-DVQuUMII.js → smalltalk-fjkC4HKG.js} +0 -0
- /package/dist-bloby/assets/{snazzy-light-x9CLJ1W9.js → snazzy-light-2nD216w0.js} +0 -0
- /package/dist-bloby/assets/{solarized-dark-BB1sh-pe.js → solarized-dark-BkxMSETv.js} +0 -0
- /package/dist-bloby/assets/{solarized-light-9-RPS998.js → solarized-light-BScqQ1C4.js} +0 -0
- /package/dist-bloby/assets/{solidity-gQ4WXILB.js → solidity-DbOxJfAY.js} +0 -0
- /package/dist-bloby/assets/{splunk-OB48_RcD.js → splunk-BW0l10cw.js} +0 -0
- /package/dist-bloby/assets/{ssh-config-DA9YMRGV.js → ssh-config-XN7RWvZy.js} +0 -0
- /package/dist-bloby/assets/{stylus-sKwvsUaC.js → stylus-D69is74S.js} +0 -0
- /package/dist-bloby/assets/{swift-C72NbAtg.js → swift-CqNHAbQB.js} +0 -0
- /package/dist-bloby/assets/{synthwave-84-eJ9uRN_S.js → synthwave-84-Ct3Np89a.js} +0 -0
- /package/dist-bloby/assets/{system-verilog-CnWMF7Nq.js → system-verilog-CHbvBf8h.js} +0 -0
- /package/dist-bloby/assets/{systemd-DMzItW5N.js → systemd-BBC3Z_R4.js} +0 -0
- /package/dist-bloby/assets/{talonscript-DopQCWDL.js → talonscript-DhYQB2ic.js} +0 -0
- /package/dist-bloby/assets/{tasl-C28SYKbq.js → tasl-CNtai3BH.js} +0 -0
- /package/dist-bloby/assets/{tcl-DHq7zmoF.js → tcl-DFzyQSXW.js} +0 -0
- /package/dist-bloby/assets/{terraform-CogS1YKX.js → terraform-BijD6Khb.js} +0 -0
- /package/dist-bloby/assets/{tokyo-night-B9N7LyrF.js → tokyo-night-Dd5BGAxh.js} +0 -0
- /package/dist-bloby/assets/{toml-BH_iPsgK.js → toml-DZofdujH.js} +0 -0
- /package/dist-bloby/assets/{tsv-BIkdBZX1.js → tsv-DOp6g7sz.js} +0 -0
- /package/dist-bloby/assets/{turtle-CqNXyJWZ.js → turtle-BKcRDROw.js} +0 -0
- /package/dist-bloby/assets/{typespec-CQ9LYD_e.js → typespec-Mmt3-2Cw.js} +0 -0
- /package/dist-bloby/assets/{typst-T-Fsk-Sv.js → typst-CndOdNo_.js} +0 -0
- /package/dist-bloby/assets/{v-BhMEOw9x.js → v-DjlZIA0C.js} +0 -0
- /package/dist-bloby/assets/{vala-DzajMnwt.js → vala-C5Nm5NXo.js} +0 -0
- /package/dist-bloby/assets/{vb-7Xr8dEYW.js → vb-zZeWbE2_.js} +0 -0
- /package/dist-bloby/assets/{verilog-AvXtwZir.js → verilog-CyZIVfoz.js} +0 -0
- /package/dist-bloby/assets/{vesper-CtgQQ2sf.js → vesper-B0BMGbco.js} +0 -0
- /package/dist-bloby/assets/{vhdl-BHu0zg1P.js → vhdl-DH1mJdjZ.js} +0 -0
- /package/dist-bloby/assets/{viml-Di0QpLfC.js → viml-e-oP1x-l.js} +0 -0
- /package/dist-bloby/assets/{vitesse-black-jXdfyqgD.js → vitesse-black-BRAUsA4Q.js} +0 -0
- /package/dist-bloby/assets/{vitesse-dark-B88PxG1r.js → vitesse-dark-Dyg71HnT.js} +0 -0
- /package/dist-bloby/assets/{vitesse-light-BXxZh2iV.js → vitesse-light-BJKZKqvd.js} +0 -0
- /package/dist-bloby/assets/{vyper-C3R5yXgx.js → vyper-BRPzCrki.js} +0 -0
- /package/dist-bloby/assets/{wasm-Chsuwt1A.js → wasm-DYMpDie-.js} +0 -0
- /package/dist-bloby/assets/{wasm-CEUrWlVz.js → wasm-Drz0Uqci.js} +0 -0
- /package/dist-bloby/assets/{wenyan-BonVcr2i.js → wenyan-BiJ0NePu.js} +0 -0
- /package/dist-bloby/assets/{wgsl-D59WYmNq.js → wgsl-CNe_V6SG.js} +0 -0
- /package/dist-bloby/assets/{wikitext-ariTI2Wk.js → wikitext-DZrjZKWn.js} +0 -0
- /package/dist-bloby/assets/{wit-EA4fa3Dt.js → wit-BUTESHa8.js} +0 -0
- /package/dist-bloby/assets/{wolfram-DDp7LhyU.js → wolfram-COEZldPx.js} +0 -0
- /package/dist-bloby/assets/{zenscript-4WaROUyc.js → zenscript-DThCxjUC.js} +0 -0
- /package/dist-bloby/assets/{zig-CJoLJGwV.js → zig-Bu6QWpR2.js} +0 -0
package/worker/codex-auth.ts
CHANGED
|
@@ -1,7 +1,13 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Codex OAuth PKCE flow for ChatGPT Plus/Pro subscription authentication.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
4
|
+
* Paste-back flow (no local HTTP callback) — the dashboard is typically
|
|
5
|
+
* served from a Pi via Cloudflare tunnel, so a browser-side `localhost:1455`
|
|
6
|
+
* callback can't reach the host running this code. We send the user through
|
|
7
|
+
* OpenAI's auth page; their browser redirects to the (unreachable) callback
|
|
8
|
+
* URL but its URL bar contains the `code`. The user pastes that URL or code
|
|
9
|
+
* back into the wizard, which POSTs it here for token exchange.
|
|
10
|
+
*
|
|
5
11
|
* Credentials are stored in ~/.codex/auth.json in the same shape Codex CLI
|
|
6
12
|
* itself writes, so a spawned `codex app-server` process can use them directly.
|
|
7
13
|
*
|
|
@@ -20,7 +26,6 @@
|
|
|
20
26
|
*/
|
|
21
27
|
|
|
22
28
|
import crypto from 'crypto';
|
|
23
|
-
import http from 'http';
|
|
24
29
|
import fs from 'fs';
|
|
25
30
|
import path from 'path';
|
|
26
31
|
import os from 'os';
|
|
@@ -32,7 +37,6 @@ const OAUTH_CONFIG = {
|
|
|
32
37
|
REDIRECT_URI: 'http://localhost:1455/auth/callback',
|
|
33
38
|
CLIENT_ID: 'app_EMoamEEZ73f0CkXaXp7hrann',
|
|
34
39
|
SCOPES: 'openid profile email offline_access',
|
|
35
|
-
PORT: 1455,
|
|
36
40
|
};
|
|
37
41
|
|
|
38
42
|
const AUTH_DIR = path.join(os.homedir(), '.codex');
|
|
@@ -44,7 +48,6 @@ const REFRESH_LEEWAY_MS = 5 * 60 * 1000;
|
|
|
44
48
|
|
|
45
49
|
let codeVerifier: string | null = null;
|
|
46
50
|
let oauthState: string | null = null;
|
|
47
|
-
let callbackServers: http.Server[] = [];
|
|
48
51
|
|
|
49
52
|
interface AuthDotJson {
|
|
50
53
|
OPENAI_API_KEY: string | null;
|
|
@@ -87,7 +90,7 @@ function decodeJwt(token: string): Record<string, any> | null {
|
|
|
87
90
|
}
|
|
88
91
|
}
|
|
89
92
|
|
|
90
|
-
/** Read the JWT `exp` claim as a Unix epoch (
|
|
93
|
+
/** Read the JWT `exp` claim as a Unix epoch (ms). Null if missing/invalid. */
|
|
91
94
|
function jwtExpiryMs(token: string): number | null {
|
|
92
95
|
const payload = decodeJwt(token);
|
|
93
96
|
if (!payload || typeof payload.exp !== 'number') return null;
|
|
@@ -104,7 +107,6 @@ function extractAccountId(idToken: string): string | undefined {
|
|
|
104
107
|
/** One-shot migration from the old `codedeck-auth.json` layout (pre-codex-native). */
|
|
105
108
|
function migrateLegacyFile(): void {
|
|
106
109
|
if (fs.existsSync(AUTH_FILE)) {
|
|
107
|
-
// If auth.json already has chatgpt tokens, nothing to do.
|
|
108
110
|
const existing = readAuthFile();
|
|
109
111
|
if (existing?.tokens?.refresh_token) return;
|
|
110
112
|
}
|
|
@@ -156,46 +158,6 @@ function storeTokens(tokens: { access_token: string; refresh_token?: string; id_
|
|
|
156
158
|
writeAuthFile(next);
|
|
157
159
|
}
|
|
158
160
|
|
|
159
|
-
async function exchangeCode(code: string): Promise<{ success: boolean; error?: string }> {
|
|
160
|
-
if (!codeVerifier) {
|
|
161
|
-
return { success: false, error: 'No code verifier — OAuth flow was not started.' };
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
try {
|
|
165
|
-
const payload = new URLSearchParams({
|
|
166
|
-
grant_type: 'authorization_code',
|
|
167
|
-
client_id: OAUTH_CONFIG.CLIENT_ID,
|
|
168
|
-
code,
|
|
169
|
-
redirect_uri: OAUTH_CONFIG.REDIRECT_URI,
|
|
170
|
-
code_verifier: codeVerifier,
|
|
171
|
-
});
|
|
172
|
-
|
|
173
|
-
const response = await fetch(OAUTH_CONFIG.TOKEN_URL, {
|
|
174
|
-
method: 'POST',
|
|
175
|
-
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
|
|
176
|
-
body: payload.toString(),
|
|
177
|
-
});
|
|
178
|
-
|
|
179
|
-
if (!response.ok) {
|
|
180
|
-
return { success: false, error: `Authentication failed (${response.status}). Please try again.` };
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
const tokens = await response.json();
|
|
184
|
-
if (!tokens.access_token) {
|
|
185
|
-
return { success: false, error: 'OAuth response missing access_token.' };
|
|
186
|
-
}
|
|
187
|
-
storeTokens(tokens);
|
|
188
|
-
codeVerifier = null;
|
|
189
|
-
oauthState = null;
|
|
190
|
-
log.ok('Codex credentials stored');
|
|
191
|
-
// Clean up the legacy file on first successful new auth.
|
|
192
|
-
try { fs.unlinkSync(LEGACY_AUTH_FILE); } catch {}
|
|
193
|
-
return { success: true };
|
|
194
|
-
} catch (err: any) {
|
|
195
|
-
return { success: false, error: err.message };
|
|
196
|
-
}
|
|
197
|
-
}
|
|
198
|
-
|
|
199
161
|
async function refreshTokens(refreshToken: string): Promise<boolean> {
|
|
200
162
|
try {
|
|
201
163
|
log.ok('Codex: refreshing access token...');
|
|
@@ -226,108 +188,124 @@ async function refreshTokens(refreshToken: string): Promise<boolean> {
|
|
|
226
188
|
}
|
|
227
189
|
}
|
|
228
190
|
|
|
229
|
-
|
|
191
|
+
/**
|
|
192
|
+
* Parse what the user pasted — accepts:
|
|
193
|
+
* - the full callback URL (`http://localhost:1455/auth/callback?code=...&state=...`)
|
|
194
|
+
* - just the query string (`?code=...&state=...` or `code=...&state=...`)
|
|
195
|
+
* - just the raw code (`ac_XXX...`)
|
|
196
|
+
*/
|
|
197
|
+
function parsePastedInput(input: string): { code: string; state?: string } | { error: string } {
|
|
198
|
+
const trimmed = input.trim();
|
|
199
|
+
if (!trimmed) return { error: 'Paste the URL or code from your browser.' };
|
|
200
|
+
|
|
201
|
+
// Full URL or just a path
|
|
202
|
+
if (/^https?:\/\//i.test(trimmed) || trimmed.startsWith('/')) {
|
|
203
|
+
try {
|
|
204
|
+
const url = new URL(trimmed.startsWith('/') ? `http://x${trimmed}` : trimmed);
|
|
205
|
+
const code = url.searchParams.get('code');
|
|
206
|
+
if (!code) return { error: 'URL is missing the code parameter.' };
|
|
207
|
+
return { code, state: url.searchParams.get('state') || undefined };
|
|
208
|
+
} catch {
|
|
209
|
+
return { error: 'Could not parse the pasted URL.' };
|
|
210
|
+
}
|
|
211
|
+
}
|
|
230
212
|
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
213
|
+
// Bare query string
|
|
214
|
+
if (trimmed.includes('=') && (trimmed.includes('&') || trimmed.startsWith('?'))) {
|
|
215
|
+
try {
|
|
216
|
+
const qs = trimmed.startsWith('?') ? trimmed.slice(1) : trimmed;
|
|
217
|
+
const params = new URLSearchParams(qs);
|
|
218
|
+
const code = params.get('code');
|
|
219
|
+
if (!code) return { error: 'Query string is missing the code parameter.' };
|
|
220
|
+
return { code, state: params.get('state') || undefined };
|
|
221
|
+
} catch {
|
|
222
|
+
return { error: 'Could not parse the pasted query string.' };
|
|
223
|
+
}
|
|
224
|
+
}
|
|
234
225
|
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
226
|
+
// Treat as raw code
|
|
227
|
+
return { code: trimmed };
|
|
228
|
+
}
|
|
238
229
|
|
|
239
|
-
|
|
240
|
-
if (!req.url?.startsWith('/auth/callback')) {
|
|
241
|
-
res.writeHead(404);
|
|
242
|
-
res.end();
|
|
243
|
-
return;
|
|
244
|
-
}
|
|
230
|
+
/* ── Public API ── */
|
|
245
231
|
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
232
|
+
export function startCodexOAuth(): { success: boolean; authUrl?: string; error?: string } {
|
|
233
|
+
codeVerifier = crypto.randomBytes(32).toString('base64url');
|
|
234
|
+
const codeChallenge = crypto.createHash('sha256').update(codeVerifier).digest('base64url');
|
|
235
|
+
oauthState = crypto.randomUUID();
|
|
236
|
+
|
|
237
|
+
const params = new URLSearchParams({
|
|
238
|
+
response_type: 'code',
|
|
239
|
+
client_id: OAUTH_CONFIG.CLIENT_ID,
|
|
240
|
+
redirect_uri: OAUTH_CONFIG.REDIRECT_URI,
|
|
241
|
+
scope: OAUTH_CONFIG.SCOPES,
|
|
242
|
+
code_challenge: codeChallenge,
|
|
243
|
+
code_challenge_method: 'S256',
|
|
244
|
+
state: oauthState,
|
|
245
|
+
id_token_add_organizations: 'true',
|
|
246
|
+
codex_cli_simplified_flow: 'true',
|
|
247
|
+
});
|
|
251
248
|
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
<p style="color:#888">${error || 'You can close this tab and return to Bloby.'}</p>
|
|
256
|
-
</body></html>`);
|
|
249
|
+
log.ok('Codex OAuth flow started (paste-back mode)');
|
|
250
|
+
return { success: true, authUrl: `${OAUTH_CONFIG.AUTHORIZE_URL}?${params.toString()}` };
|
|
251
|
+
}
|
|
257
252
|
|
|
258
|
-
|
|
253
|
+
export async function exchangeCodexCode(input: string): Promise<{ success: boolean; error?: string }> {
|
|
254
|
+
if (!codeVerifier || !oauthState) {
|
|
255
|
+
return { success: false, error: 'Authentication wasn\'t started. Click "Authenticate" first.' };
|
|
256
|
+
}
|
|
259
257
|
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
};
|
|
258
|
+
const parsed = parsePastedInput(input);
|
|
259
|
+
if ('error' in parsed) return { success: false, error: parsed.error };
|
|
263
260
|
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
{ host: '::1', required: false }, // tolerate machines without IPv6
|
|
270
|
-
];
|
|
271
|
-
|
|
272
|
-
let resolved = false;
|
|
273
|
-
let pendingBinds = bindHosts.length;
|
|
274
|
-
let bindFailures = 0;
|
|
275
|
-
|
|
276
|
-
const finishWithSuccess = () => {
|
|
277
|
-
if (resolved) return;
|
|
278
|
-
resolved = true;
|
|
279
|
-
log.ok(`Codex OAuth callback servers listening on port ${OAUTH_CONFIG.PORT} (${callbackServers.length} bind${callbackServers.length === 1 ? '' : 's'})`);
|
|
280
|
-
const params = new URLSearchParams({
|
|
281
|
-
response_type: 'code',
|
|
282
|
-
client_id: OAUTH_CONFIG.CLIENT_ID,
|
|
283
|
-
redirect_uri: OAUTH_CONFIG.REDIRECT_URI,
|
|
284
|
-
scope: OAUTH_CONFIG.SCOPES,
|
|
285
|
-
code_challenge: codeChallenge,
|
|
286
|
-
code_challenge_method: 'S256',
|
|
287
|
-
state: oauthState!,
|
|
288
|
-
id_token_add_organizations: 'true',
|
|
289
|
-
codex_cli_simplified_flow: 'true',
|
|
290
|
-
});
|
|
291
|
-
resolve({ success: true, authUrl: `${OAUTH_CONFIG.AUTHORIZE_URL}?${params.toString()}` });
|
|
261
|
+
if (parsed.state && parsed.state !== oauthState) {
|
|
262
|
+
log.warn(`Codex OAuth: state mismatch (got=${parsed.state}, expected=${oauthState})`);
|
|
263
|
+
return {
|
|
264
|
+
success: false,
|
|
265
|
+
error: 'State mismatch — start the flow again from the wizard.',
|
|
292
266
|
};
|
|
267
|
+
}
|
|
293
268
|
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
};
|
|
269
|
+
try {
|
|
270
|
+
const payload = new URLSearchParams({
|
|
271
|
+
grant_type: 'authorization_code',
|
|
272
|
+
client_id: OAUTH_CONFIG.CLIENT_ID,
|
|
273
|
+
code: parsed.code,
|
|
274
|
+
redirect_uri: OAUTH_CONFIG.REDIRECT_URI,
|
|
275
|
+
code_verifier: codeVerifier,
|
|
276
|
+
});
|
|
303
277
|
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
if (required) {
|
|
310
|
-
finishWithError(err);
|
|
311
|
-
} else {
|
|
312
|
-
bindFailures++;
|
|
313
|
-
pendingBinds--;
|
|
314
|
-
// If only the optional bind failed but we already have at least one
|
|
315
|
-
// server up, that's still a success.
|
|
316
|
-
if (pendingBinds === 0 && callbackServers.length > 0) finishWithSuccess();
|
|
317
|
-
}
|
|
318
|
-
});
|
|
278
|
+
const response = await fetch(OAUTH_CONFIG.TOKEN_URL, {
|
|
279
|
+
method: 'POST',
|
|
280
|
+
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
|
|
281
|
+
body: payload.toString(),
|
|
282
|
+
});
|
|
319
283
|
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
284
|
+
if (!response.ok) {
|
|
285
|
+
const body = await response.text().catch(() => '');
|
|
286
|
+
log.warn(`Codex OAuth exchange failed (${response.status}): ${body.slice(0, 200)}`);
|
|
287
|
+
return {
|
|
288
|
+
success: false,
|
|
289
|
+
error: `Authentication failed (${response.status}). Codes are single-use — start over and paste a fresh one.`,
|
|
290
|
+
};
|
|
325
291
|
}
|
|
326
|
-
|
|
292
|
+
|
|
293
|
+
const tokens = await response.json();
|
|
294
|
+
if (!tokens.access_token) {
|
|
295
|
+
return { success: false, error: 'OAuth response missing access_token.' };
|
|
296
|
+
}
|
|
297
|
+
storeTokens(tokens);
|
|
298
|
+
codeVerifier = null;
|
|
299
|
+
oauthState = null;
|
|
300
|
+
try { fs.unlinkSync(LEGACY_AUTH_FILE); } catch {}
|
|
301
|
+
log.ok('Codex credentials stored');
|
|
302
|
+
return { success: true };
|
|
303
|
+
} catch (err: any) {
|
|
304
|
+
return { success: false, error: err.message };
|
|
305
|
+
}
|
|
327
306
|
}
|
|
328
307
|
|
|
329
308
|
export function cancelCodexOAuth(): void {
|
|
330
|
-
stopCallbackServer();
|
|
331
309
|
codeVerifier = null;
|
|
332
310
|
oauthState = null;
|
|
333
311
|
}
|
|
@@ -392,11 +370,258 @@ export function readCodexAccessToken(): string | null {
|
|
|
392
370
|
return token;
|
|
393
371
|
}
|
|
394
372
|
|
|
395
|
-
/*
|
|
373
|
+
/* ────────────────────────────────────────────────────────────────────────────
|
|
374
|
+
* Device-code flow — preferred for headless / remote dashboards.
|
|
375
|
+
*
|
|
376
|
+
* 1. POST {AUTH_BASE}/api/accounts/deviceauth/usercode body {client_id}
|
|
377
|
+
* → { device_auth_id, user_code, interval }
|
|
378
|
+
* 2. Poll POST {AUTH_BASE}/api/accounts/deviceauth/token body {device_auth_id, user_code}
|
|
379
|
+
* 403/404 = pending, 2xx = { authorization_code, code_challenge, code_verifier }
|
|
380
|
+
* 3. POST {AUTH_BASE}/oauth/token body {grant_type, client_id, code, code_verifier,
|
|
381
|
+
* redirect_uri="{AUTH_BASE}/deviceauth/callback"} → standard token response
|
|
382
|
+
*
|
|
383
|
+
* The user opens DEVICE_VERIFICATION_URL and types user_code there. We poll
|
|
384
|
+
* in the background; the wizard polls /api/auth/codex/device/status for the
|
|
385
|
+
* current state.
|
|
386
|
+
* ──────────────────────────────────────────────────────────────────────────── */
|
|
387
|
+
|
|
388
|
+
const AUTH_BASE = 'https://auth.openai.com';
|
|
389
|
+
const DEVICE_USER_CODE_URL = `${AUTH_BASE}/api/accounts/deviceauth/usercode`;
|
|
390
|
+
const DEVICE_POLL_URL = `${AUTH_BASE}/api/accounts/deviceauth/token`;
|
|
391
|
+
const DEVICE_REDIRECT_URI = `${AUTH_BASE}/deviceauth/callback`;
|
|
392
|
+
const DEVICE_VERIFICATION_URL = `${AUTH_BASE}/codex/device`;
|
|
393
|
+
const DEVICE_TIMEOUT_MS = 15 * 60 * 1000;
|
|
394
|
+
const DEVICE_DEFAULT_INTERVAL_SEC = 5;
|
|
395
|
+
|
|
396
|
+
interface DeviceLoginState {
|
|
397
|
+
/** Bumped on each `startDeviceCodeLogin` so a stale poll loop can self-cancel. */
|
|
398
|
+
generation: number;
|
|
399
|
+
state: 'idle' | 'pending' | 'success' | 'error';
|
|
400
|
+
userCode?: string;
|
|
401
|
+
verificationUrl?: string;
|
|
402
|
+
expiresAt?: number; // epoch ms
|
|
403
|
+
error?: string;
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
let deviceLogin: DeviceLoginState = { generation: 0, state: 'idle' };
|
|
407
|
+
|
|
408
|
+
export function getDeviceCodeStatus(): {
|
|
409
|
+
state: DeviceLoginState['state'];
|
|
410
|
+
userCode?: string;
|
|
411
|
+
verificationUrl?: string;
|
|
412
|
+
expiresInSec?: number;
|
|
413
|
+
error?: string;
|
|
414
|
+
} {
|
|
415
|
+
const expiresInSec = deviceLogin.expiresAt
|
|
416
|
+
? Math.max(0, Math.round((deviceLogin.expiresAt - Date.now()) / 1000))
|
|
417
|
+
: undefined;
|
|
418
|
+
return {
|
|
419
|
+
state: deviceLogin.state,
|
|
420
|
+
userCode: deviceLogin.userCode,
|
|
421
|
+
verificationUrl: deviceLogin.verificationUrl,
|
|
422
|
+
expiresInSec,
|
|
423
|
+
error: deviceLogin.error,
|
|
424
|
+
};
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
export function cancelDeviceCodeLogin(): void {
|
|
428
|
+
// Bumping the generation makes any in-flight poll loop a no-op on its next tick.
|
|
429
|
+
deviceLogin = { generation: deviceLogin.generation + 1, state: 'idle' };
|
|
430
|
+
log.ok('Codex device-code login cancelled');
|
|
431
|
+
}
|
|
396
432
|
|
|
397
|
-
function
|
|
398
|
-
|
|
399
|
-
|
|
433
|
+
export async function startDeviceCodeLogin(): Promise<{
|
|
434
|
+
success: boolean;
|
|
435
|
+
userCode?: string;
|
|
436
|
+
verificationUrl?: string;
|
|
437
|
+
error?: string;
|
|
438
|
+
}> {
|
|
439
|
+
// Cancel any existing in-flight login before starting a new one.
|
|
440
|
+
const generation = deviceLogin.generation + 1;
|
|
441
|
+
deviceLogin = { generation, state: 'pending' };
|
|
442
|
+
|
|
443
|
+
try {
|
|
444
|
+
const res = await fetch(DEVICE_USER_CODE_URL, {
|
|
445
|
+
method: 'POST',
|
|
446
|
+
headers: {
|
|
447
|
+
'Content-Type': 'application/json',
|
|
448
|
+
'User-Agent': 'bloby-bot',
|
|
449
|
+
},
|
|
450
|
+
body: JSON.stringify({ client_id: OAUTH_CONFIG.CLIENT_ID }),
|
|
451
|
+
});
|
|
452
|
+
|
|
453
|
+
if (!res.ok) {
|
|
454
|
+
const body = await res.text().catch(() => '');
|
|
455
|
+
const error = `Failed to request device code (${res.status}). ${body.slice(0, 200)}`;
|
|
456
|
+
deviceLogin = { generation, state: 'error', error };
|
|
457
|
+
log.warn(`Codex device-code start failed: ${error}`);
|
|
458
|
+
return { success: false, error };
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
const data = await res.json();
|
|
462
|
+
const userCode: string = data.user_code || data.usercode;
|
|
463
|
+
const deviceAuthId: string = data.device_auth_id;
|
|
464
|
+
const intervalSec: number = Number(data.interval) || DEVICE_DEFAULT_INTERVAL_SEC;
|
|
465
|
+
if (!userCode || !deviceAuthId) {
|
|
466
|
+
const error = 'Device code response missing user_code or device_auth_id.';
|
|
467
|
+
deviceLogin = { generation, state: 'error', error };
|
|
468
|
+
return { success: false, error };
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
deviceLogin = {
|
|
472
|
+
generation,
|
|
473
|
+
state: 'pending',
|
|
474
|
+
userCode,
|
|
475
|
+
verificationUrl: DEVICE_VERIFICATION_URL,
|
|
476
|
+
expiresAt: Date.now() + DEVICE_TIMEOUT_MS,
|
|
477
|
+
};
|
|
478
|
+
log.ok(`Codex device-code login started (code=${userCode}, poll every ${intervalSec}s)`);
|
|
479
|
+
|
|
480
|
+
// Fire-and-forget background poll.
|
|
481
|
+
void pollDeviceCode(generation, deviceAuthId, userCode, intervalSec);
|
|
482
|
+
|
|
483
|
+
return { success: true, userCode, verificationUrl: DEVICE_VERIFICATION_URL };
|
|
484
|
+
} catch (err: any) {
|
|
485
|
+
const error = err?.message || String(err);
|
|
486
|
+
deviceLogin = { generation, state: 'error', error };
|
|
487
|
+
log.warn(`Codex device-code start error: ${error}`);
|
|
488
|
+
return { success: false, error };
|
|
400
489
|
}
|
|
401
|
-
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
async function pollDeviceCode(
|
|
493
|
+
generation: number,
|
|
494
|
+
deviceAuthId: string,
|
|
495
|
+
userCode: string,
|
|
496
|
+
intervalSec: number,
|
|
497
|
+
): Promise<void> {
|
|
498
|
+
const startedAt = Date.now();
|
|
499
|
+
|
|
500
|
+
while (true) {
|
|
501
|
+
// Stale generation = a newer login was started or this one was cancelled.
|
|
502
|
+
if (deviceLogin.generation !== generation) {
|
|
503
|
+
log.ok(`Codex device-code poll for gen ${generation} stopped (superseded)`);
|
|
504
|
+
return;
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
if (Date.now() - startedAt > DEVICE_TIMEOUT_MS) {
|
|
508
|
+
deviceLogin = {
|
|
509
|
+
generation,
|
|
510
|
+
state: 'error',
|
|
511
|
+
error: 'Device-code login timed out (15 minutes). Try again.',
|
|
512
|
+
};
|
|
513
|
+
log.warn('Codex device-code login timed out');
|
|
514
|
+
return;
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
let res: Response;
|
|
518
|
+
try {
|
|
519
|
+
res = await fetch(DEVICE_POLL_URL, {
|
|
520
|
+
method: 'POST',
|
|
521
|
+
headers: {
|
|
522
|
+
'Content-Type': 'application/json',
|
|
523
|
+
'User-Agent': 'bloby-bot',
|
|
524
|
+
},
|
|
525
|
+
body: JSON.stringify({ device_auth_id: deviceAuthId, user_code: userCode }),
|
|
526
|
+
});
|
|
527
|
+
} catch (err: any) {
|
|
528
|
+
// Transient network errors — log and keep polling within timeout.
|
|
529
|
+
log.warn(`Codex device-code poll network error: ${err?.message || err}`);
|
|
530
|
+
await sleep(intervalSec * 1000);
|
|
531
|
+
continue;
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
if (res.status === 403 || res.status === 404) {
|
|
535
|
+
// Pending — user hasn't approved yet.
|
|
536
|
+
await sleep(intervalSec * 1000);
|
|
537
|
+
continue;
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
if (!res.ok) {
|
|
541
|
+
const body = await res.text().catch(() => '');
|
|
542
|
+
const error = `Device-code poll failed (${res.status}). ${body.slice(0, 200)}`;
|
|
543
|
+
if (deviceLogin.generation === generation) {
|
|
544
|
+
deviceLogin = { generation, state: 'error', error };
|
|
545
|
+
}
|
|
546
|
+
log.warn(error);
|
|
547
|
+
return;
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
// 2xx — server returned the authorization_code + PKCE verifier.
|
|
551
|
+
let data: { authorization_code?: string; code_verifier?: string };
|
|
552
|
+
try {
|
|
553
|
+
data = await res.json();
|
|
554
|
+
} catch (err: any) {
|
|
555
|
+
const error = `Device-code poll: malformed JSON (${err?.message || err})`;
|
|
556
|
+
if (deviceLogin.generation === generation) {
|
|
557
|
+
deviceLogin = { generation, state: 'error', error };
|
|
558
|
+
}
|
|
559
|
+
return;
|
|
560
|
+
}
|
|
561
|
+
|
|
562
|
+
if (!data.authorization_code || !data.code_verifier) {
|
|
563
|
+
const error = 'Device-code poll: response missing authorization_code or code_verifier.';
|
|
564
|
+
if (deviceLogin.generation === generation) {
|
|
565
|
+
deviceLogin = { generation, state: 'error', error };
|
|
566
|
+
}
|
|
567
|
+
return;
|
|
568
|
+
}
|
|
569
|
+
|
|
570
|
+
// Final step: exchange for tokens.
|
|
571
|
+
const exchanged = await exchangeDeviceCode(data.authorization_code, data.code_verifier);
|
|
572
|
+
if (deviceLogin.generation !== generation) return;
|
|
573
|
+
|
|
574
|
+
if (exchanged.success) {
|
|
575
|
+
deviceLogin = { generation, state: 'success' };
|
|
576
|
+
log.ok('Codex device-code login complete');
|
|
577
|
+
} else {
|
|
578
|
+
deviceLogin = {
|
|
579
|
+
generation,
|
|
580
|
+
state: 'error',
|
|
581
|
+
error: exchanged.error || 'Token exchange failed.',
|
|
582
|
+
};
|
|
583
|
+
}
|
|
584
|
+
return;
|
|
585
|
+
}
|
|
586
|
+
}
|
|
587
|
+
|
|
588
|
+
async function exchangeDeviceCode(
|
|
589
|
+
authorizationCode: string,
|
|
590
|
+
codeVerifierFromServer: string,
|
|
591
|
+
): Promise<{ success: boolean; error?: string }> {
|
|
592
|
+
try {
|
|
593
|
+
const payload = new URLSearchParams({
|
|
594
|
+
grant_type: 'authorization_code',
|
|
595
|
+
client_id: OAUTH_CONFIG.CLIENT_ID,
|
|
596
|
+
code: authorizationCode,
|
|
597
|
+
redirect_uri: DEVICE_REDIRECT_URI,
|
|
598
|
+
code_verifier: codeVerifierFromServer,
|
|
599
|
+
});
|
|
600
|
+
const res = await fetch(OAUTH_CONFIG.TOKEN_URL, {
|
|
601
|
+
method: 'POST',
|
|
602
|
+
headers: {
|
|
603
|
+
'Content-Type': 'application/x-www-form-urlencoded',
|
|
604
|
+
'User-Agent': 'bloby-bot',
|
|
605
|
+
},
|
|
606
|
+
body: payload.toString(),
|
|
607
|
+
});
|
|
608
|
+
if (!res.ok) {
|
|
609
|
+
const body = await res.text().catch(() => '');
|
|
610
|
+
log.warn(`Codex device-code token exchange failed (${res.status}): ${body.slice(0, 200)}`);
|
|
611
|
+
return { success: false, error: `Token exchange failed (${res.status}).` };
|
|
612
|
+
}
|
|
613
|
+
const tokens = await res.json();
|
|
614
|
+
if (!tokens.access_token) {
|
|
615
|
+
return { success: false, error: 'Token exchange response missing access_token.' };
|
|
616
|
+
}
|
|
617
|
+
storeTokens(tokens);
|
|
618
|
+
try { fs.unlinkSync(LEGACY_AUTH_FILE); } catch {}
|
|
619
|
+
return { success: true };
|
|
620
|
+
} catch (err: any) {
|
|
621
|
+
return { success: false, error: err?.message || String(err) };
|
|
622
|
+
}
|
|
623
|
+
}
|
|
624
|
+
|
|
625
|
+
function sleep(ms: number): Promise<void> {
|
|
626
|
+
return new Promise((r) => setTimeout(r, ms));
|
|
402
627
|
}
|
package/worker/index.ts
CHANGED
|
@@ -9,7 +9,10 @@ import { initDb, closeDb, listConversations, createConversation, deleteConversat
|
|
|
9
9
|
import webpush from 'web-push';
|
|
10
10
|
import { TOTP } from 'otpauth';
|
|
11
11
|
import QRCode from 'qrcode';
|
|
12
|
-
import {
|
|
12
|
+
import {
|
|
13
|
+
startCodexOAuth, cancelCodexOAuth, getCodexAuthStatus, exchangeCodexCode,
|
|
14
|
+
startDeviceCodeLogin, getDeviceCodeStatus, cancelDeviceCodeLogin,
|
|
15
|
+
} from './codex-auth.js';
|
|
13
16
|
import { startClaudeOAuth, exchangeClaudeCode, getClaudeAuthStatus, readClaudeAccessToken } from './claude-auth.js';
|
|
14
17
|
import { checkAvailability, registerHandle, claimReservedHandle, releaseHandle, updateTunnelUrl, startHeartbeat, stopHeartbeat } from '../shared/relay.js';
|
|
15
18
|
import { ensureFileDirs } from '../supervisor/file-saver.js';
|
|
@@ -171,9 +174,17 @@ app.post('/api/context/clear', (_, res) => {
|
|
|
171
174
|
|
|
172
175
|
// ── Codex OAuth routes ──
|
|
173
176
|
|
|
174
|
-
app.post('/api/auth/codex/start',
|
|
175
|
-
|
|
176
|
-
|
|
177
|
+
app.post('/api/auth/codex/start', (_req, res) => {
|
|
178
|
+
res.json(startCodexOAuth());
|
|
179
|
+
});
|
|
180
|
+
|
|
181
|
+
app.post('/api/auth/codex/exchange', async (req, res) => {
|
|
182
|
+
const { code } = req.body || {};
|
|
183
|
+
if (!code || typeof code !== 'string') {
|
|
184
|
+
res.json({ success: false, error: 'No code provided' });
|
|
185
|
+
return;
|
|
186
|
+
}
|
|
187
|
+
res.json(await exchangeCodexCode(code));
|
|
177
188
|
});
|
|
178
189
|
|
|
179
190
|
app.post('/api/auth/codex/cancel', (_req, res) => {
|
|
@@ -185,6 +196,21 @@ app.get('/api/auth/codex/status', async (_req, res) => {
|
|
|
185
196
|
res.json(await getCodexAuthStatus());
|
|
186
197
|
});
|
|
187
198
|
|
|
199
|
+
// ── Codex device-code routes (preferred for headless dashboards) ──
|
|
200
|
+
|
|
201
|
+
app.post('/api/auth/codex/device/start', async (_req, res) => {
|
|
202
|
+
res.json(await startDeviceCodeLogin());
|
|
203
|
+
});
|
|
204
|
+
|
|
205
|
+
app.get('/api/auth/codex/device/status', (_req, res) => {
|
|
206
|
+
res.json(getDeviceCodeStatus());
|
|
207
|
+
});
|
|
208
|
+
|
|
209
|
+
app.post('/api/auth/codex/device/cancel', (_req, res) => {
|
|
210
|
+
cancelDeviceCodeLogin();
|
|
211
|
+
res.json({ ok: true });
|
|
212
|
+
});
|
|
213
|
+
|
|
188
214
|
// ── Claude OAuth routes ──
|
|
189
215
|
|
|
190
216
|
app.post('/api/auth/claude/start', (_req, res) => {
|