0sec-cli 0.25.0 → 0.26.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/0.js +41 -40
- package/chunks/{adapt-loop-L2PXCD4J.js → adapt-loop-P4HKONB5.js} +4 -4
- package/chunks/{artifact-scraper-W3LGLXBM.js → artifact-scraper-IIRJNYKT.js} +2 -2
- package/chunks/{assumption-mining-7IJ6ZWZE.js → assumption-mining-YA6VJ2V6.js} +17 -17
- package/chunks/{backend-connections-MYH4QRQG.js → backend-connections-64PKYKL7.js} +3 -3
- package/chunks/{chunk-EEP6GBUO.js → chunk-3MWQNNJU.js} +151 -58
- package/chunks/{chunk-ZZV56ACK.js → chunk-3VQ5EZKG.js} +9 -9
- package/chunks/{chunk-I4VIAQHM.js → chunk-6GV5G6U3.js} +1 -1
- package/chunks/{chunk-OFPF26WL.js → chunk-6SNOKNCS.js} +2 -2
- package/chunks/{chunk-YXTV2FRE.js → chunk-72PU46MS.js} +66 -112
- package/chunks/{chunk-NO543ZBU.js → chunk-A7G6S5RA.js} +1 -1
- package/chunks/{chunk-ITEMDJAB.js → chunk-AFJ2UMPR.js} +2 -2
- package/chunks/{chunk-FOLTHXRH.js → chunk-B7XL6RON.js} +1 -1
- package/chunks/{chunk-76CLUI54.js → chunk-BM6EYP2D.js} +3 -3
- package/chunks/{chunk-NBLKIQCW.js → chunk-D4M5WURF.js} +2 -2
- package/chunks/{chunk-DHNYJ2ML.js → chunk-EHNVP6QB.js} +1 -1
- package/chunks/{chunk-C6ZMPXN2.js → chunk-FBZBJYZS.js} +3 -3
- package/chunks/{chunk-S3B6HFSM.js → chunk-GYEYTNHK.js} +1 -1
- package/chunks/{chunk-EIONVEFN.js → chunk-HS2NQ7K6.js} +192 -36
- package/chunks/{chunk-HJKJ4NQP.js → chunk-HUZ2EC2R.js} +1 -1
- package/chunks/{chunk-5H2PXZ7Q.js → chunk-IUUNIJPM.js} +1 -1
- package/chunks/{chunk-3DF55BWV.js → chunk-JGDA6EIJ.js} +2 -2
- package/chunks/{chunk-GOUGG7IQ.js → chunk-K3PTYN43.js} +2 -2
- package/chunks/{chunk-7TIWL6E5.js → chunk-KCABBZHN.js} +7 -7
- package/chunks/{chunk-ACYZMJHC.js → chunk-LRD5N2TQ.js} +1 -1
- package/chunks/{chunk-ITHDAIRY.js → chunk-MNK373HF.js} +1 -1
- package/chunks/{chunk-JCRQV4FE.js → chunk-MOEVVXML.js} +2 -2
- package/chunks/{chunk-3HMO42M2.js → chunk-MOGWEHSC.js} +68 -771
- package/chunks/{chunk-TFKY6D7O.js → chunk-N7YCONYR.js} +1 -1
- package/chunks/{chunk-FI2H6HV2.js → chunk-NA3FKCI2.js} +1 -1
- package/chunks/{chunk-PMAKX6KP.js → chunk-NABVDAAO.js} +2 -2
- package/chunks/{chunk-YYFODVZG.js → chunk-NNMOTHKC.js} +4 -4
- package/chunks/{chunk-RBTJYREN.js → chunk-OILVEROI.js} +17 -4
- package/chunks/{chunk-GXSECFUE.js → chunk-PAMJEXE2.js} +1 -1
- package/chunks/{chunk-2J3STRJT.js → chunk-R7VWEY45.js} +1 -1
- package/chunks/{chunk-27SXCYWH.js → chunk-RZZ6VPRY.js} +1 -1
- package/chunks/{chunk-HFUOUT2B.js → chunk-SIR7XW2S.js} +1392 -826
- package/chunks/{chunk-HDJSHLRM.js → chunk-SSPP64FF.js} +2 -2
- package/chunks/{chunk-WK6VUCKK.js → chunk-STPHTPSD.js} +1 -1
- package/chunks/{chunk-SEWZ76MN.js → chunk-UGCTPKJG.js} +14 -6
- package/chunks/{chunk-MY44EZKD.js → chunk-ULRLUXSS.js} +1 -1
- package/chunks/{chunk-BWUW6ZQR.js → chunk-UQS7XCBV.js} +75 -60
- package/chunks/{chunk-6TA3CWND.js → chunk-V2KJQVYE.js} +42 -21
- package/chunks/{chunk-RFK5F45W.js → chunk-VJBEB2K6.js} +1 -1
- package/chunks/{chunk-D4IBEO2K.js → chunk-VWE36MUN.js} +1 -1
- package/chunks/{chunk-B5TIXHCO.js → chunk-WNWDQ2TO.js} +1 -1
- package/chunks/{chunk-S2WCYEK2.js → chunk-WPJMA5AK.js} +2 -2
- package/chunks/{chunk-FI5YL4R2.js → chunk-XJ2TBEV7.js} +3 -3
- package/chunks/{chunk-3T5R7P4T.js → chunk-XOVYV7TT.js} +4 -4
- package/chunks/{chunk-DJGJECBV.js → chunk-YGYJ2FTH.js} +4 -3
- package/chunks/{chunk-ZWAWZU6C.js → chunk-YJGPPPUB.js} +37 -9
- package/chunks/chunk-YP2DHYNI.js +650 -0
- package/chunks/{chunk-DMDWIW3E.js → chunk-Z2VLJEQJ.js} +4 -4
- package/chunks/{chunk-EQBC2ZWL.js → chunk-ZPVSDB63.js} +1604 -246
- package/chunks/{codex-models-KCUIVS3R.js → codex-models-CI22KIHY.js} +7 -7
- package/chunks/{commands-MPPAADGY.js → commands-C2WRSCNB.js} +210 -101
- package/chunks/dashboard-XO2X2SPD.js +75 -0
- package/chunks/{db-R2AQSESC.js → db-FU3OIGQK.js} +3 -3
- package/chunks/{deep-review-Q6BJKEV4.js → deep-review-5AL6QYTK.js} +30 -30
- package/chunks/{disclose-GGULOW27.js → disclose-YPV5KIUU.js} +3 -3
- package/chunks/{dist-6RNAVFAY.js → dist-ONIRKDH6.js} +29 -29
- package/chunks/{dist-OLGNKDB2.js → dist-QLMPTFT7.js} +2 -2
- package/chunks/{dist-DWN7U5OV.js → dist-QXJKD743.js} +1 -1
- package/chunks/{eval-runner-WCTTIYN4.js → eval-runner-D3RUCU3R.js} +17 -17
- package/chunks/{exploit-agent-M3KUMZJH.js → exploit-agent-4E4HXKUA.js} +1 -1
- package/chunks/{exploit-autoclimb-QTTWC5WM.js → exploit-autoclimb-5GRXI6VQ.js} +1 -1
- package/chunks/{exploit-climb-E4KDELTN.js → exploit-climb-AHQEJ2I2.js} +2 -2
- package/chunks/{fix-U5FFJD4F.js → fix-CHD7LXDS.js} +7 -7
- package/chunks/{harness-FMKIMEH5.js → harness-E63KDHPG.js} +3 -3
- package/chunks/{hunt-scan-RYPWRUQD.js → hunt-scan-ME2Z4CTC.js} +16 -16
- package/chunks/{kernel-primitive-CVG2E7MJ.js → kernel-primitive-S566GXWP.js} +2 -2
- package/chunks/{kernel-vm-runner-62KISULQ.js → kernel-vm-runner-KREBY4DC.js} +2 -2
- package/chunks/{local-engine-WP3I7K7G.js → local-engine-ZRTZVSSX.js} +5 -5
- package/chunks/managed-operation-ZMLLRCCQ.js +59 -0
- package/chunks/{native-loop-HC77R5NQ.js → native-loop-UV3Z4Z2F.js} +16 -16
- package/chunks/{operator-services-E2NONBQS.js → operator-services-JOCREEBA.js} +35 -35
- package/chunks/{orchestrate-Z3DW265M.js → orchestrate-ZF6ZTSIQ.js} +30 -30
- package/chunks/{prepare-7DTS6RJT.js → prepare-PLDVGLDX.js} +5 -5
- package/chunks/{provider-model-discovery-QGUGL3XW.js → provider-model-discovery-3X6DON3Y.js} +7 -2
- package/chunks/{remote-workflow-runtime-6CEVRZXS.js → remote-workflow-runtime-GOI4TYP5.js} +2 -2
- package/chunks/routing-WCQLO5SP.js +61 -0
- package/chunks/{runtime-W6JNDQRO.js → runtime-6ZIXFTW5.js} +2 -2
- package/chunks/{runtime-PZJ77QES.js → runtime-TVH5ORUK.js} +11 -7
- package/chunks/{scan-stream-6VOJTHTR.js → scan-stream-HJVTWZ44.js} +4 -4
- package/chunks/{scope-FHXW6EX6.js → scope-RCYXVGJR.js} +2 -2
- package/chunks/service-plugins-TN6KDX4L.js +63 -0
- package/chunks/{session-store-DBBI544Y.js → session-store-6QR3CVUA.js} +2 -2
- package/chunks/{variant-candidates-DPR6DJ3N.js → variant-candidates-SGLKPPHI.js} +7 -7
- package/chunks/{web-recon-prepass-HWDHDQMN.js → web-recon-prepass-GO7CTMVQ.js} +5 -5
- package/chunks/{workflow-runtime-PLB32XC3.js → workflow-runtime-CQTDASFA.js} +43 -43
- package/dashboard/assets/{abnfDiagram-VCTEODGH-6e39CQKz.js → abnfDiagram-VCTEODGH-DDZsU70-.js} +1 -1
- package/dashboard/assets/{arc-ClYun86B.js → arc-iDXNTSL6.js} +1 -1
- package/dashboard/assets/{architecture-7GRP2DOG-rM-lWo7E.js → architecture-7GRP2DOG-DJcY9RFg.js} +1 -1
- package/dashboard/assets/{architectureDiagram-5GKGNRK7-C9gEGZIU.js → architectureDiagram-5GKGNRK7-d0ZuOniQ.js} +1 -1
- package/dashboard/assets/{blockDiagram-I7D4REHJ-Dgh2n5Lf.js → blockDiagram-I7D4REHJ-B-EWfMy1.js} +1 -1
- package/dashboard/assets/{c4Diagram-7LVT6UL2-CsorW08z.js → c4Diagram-7LVT6UL2-KcZRuXZC.js} +1 -1
- package/dashboard/assets/channel-CeWMIm0w.js +1 -0
- package/dashboard/assets/{chunk-4HAMMTFA-4_b_LGGQ.js → chunk-4HAMMTFA-DXWulBlo.js} +1 -1
- package/dashboard/assets/{chunk-75Z2AOVW-qzNlKGCp.js → chunk-75Z2AOVW-RiLZItrz.js} +1 -1
- package/dashboard/assets/{chunk-DU6HZSFF-SAPICW28.js → chunk-DU6HZSFF-MBcaIyTX.js} +1 -1
- package/dashboard/assets/{chunk-F27PBJKO-DlLotIQx.js → chunk-F27PBJKO-kLWCBX_W.js} +1 -1
- package/dashboard/assets/{chunk-GMAD6QVW-DNnr65Nb.js → chunk-GMAD6QVW-BeQeSjpW.js} +1 -1
- package/dashboard/assets/{chunk-GVQU2GXP-DTwv0VfU.js → chunk-GVQU2GXP-CjCMlOIB.js} +1 -1
- package/dashboard/assets/{chunk-IMKFNOWR-B1fk1q_W.js → chunk-IMKFNOWR-BwNst53D.js} +1 -1
- package/dashboard/assets/{chunk-L3NEJ4N5-ByVjFIox.js → chunk-L3NEJ4N5-CHXWQrkL.js} +1 -1
- package/dashboard/assets/{chunk-OSK3NFVY-DUDCzjbh.js → chunk-OSK3NFVY-B2vd19Ed.js} +1 -1
- package/dashboard/assets/{chunk-P2QGCYS3-D_D5qEso.js → chunk-P2QGCYS3-Dk7Fyh6n.js} +1 -1
- package/dashboard/assets/{chunk-POPQ4Y6H-CetM-tVm.js → chunk-POPQ4Y6H-CPAgOvYe.js} +1 -1
- package/dashboard/assets/{chunk-PWAF6VOD-BcJX4exl.js → chunk-PWAF6VOD-BotyfMha.js} +1 -1
- package/dashboard/assets/{chunk-SHT3W25Y-BWQql238.js → chunk-SHT3W25Y-Buz5gLRP.js} +1 -1
- package/dashboard/assets/{chunk-SVP7TREG-BSLOD1JE.js → chunk-SVP7TREG-_rpYEZcs.js} +1 -1
- package/dashboard/assets/{chunk-TICWLB2K-DReOl-g_.js → chunk-TICWLB2K-DOJxPoec.js} +1 -1
- package/dashboard/assets/{chunk-XXDRQBXY-DSdSMeei.js → chunk-XXDRQBXY-EXxKJbAs.js} +1 -1
- package/dashboard/assets/classDiagram-ZZMXUADV-DV_h1NoS.js +1 -0
- package/dashboard/assets/classDiagram-v2-VYDZK3BY-DV_h1NoS.js +1 -0
- package/dashboard/assets/{common-CRRsylCH.js → common-VLJRx-Jq.js} +1 -1
- package/dashboard/assets/{cose-bilkent-JH36ORCC-fg-DIBqt.js → cose-bilkent-JH36ORCC-B1SVC57o.js} +1 -1
- package/dashboard/assets/{cynefin-OW5HDTMX-XmrbEIFh.js → cynefin-OW5HDTMX-BzhRorj4.js} +1 -1
- package/dashboard/assets/{cynefinDiagram-5FMLGOSQ-CoQ7QEPl.js → cynefinDiagram-5FMLGOSQ-JXzRmldX.js} +1 -1
- package/dashboard/assets/{dagre-GXQ25YYZ-qtQtsg6c.js → dagre-GXQ25YYZ-1LZ-X0-Z.js} +1 -1
- package/dashboard/assets/{diagram-S7CK7UJ4-B_ts4_58.js → diagram-S7CK7UJ4-C_-J_LQ4.js} +1 -1
- package/dashboard/assets/{diagram-UQ7AKVKN-DwHbbxFy.js → diagram-UQ7AKVKN-CnxL1k8g.js} +1 -1
- package/dashboard/assets/{diagram-VSXAHHWV-2Tg_RG89.js → diagram-VSXAHHWV-WzSb3aby.js} +1 -1
- package/dashboard/assets/{diagram-VX7I27RA-BFYp-YjF.js → diagram-VX7I27RA-DKhrMInE.js} +1 -1
- package/dashboard/assets/{diagram-Z3DM3KII-DrPiS4V0.js → diagram-Z3DM3KII-CDhI2WGP.js} +1 -1
- package/dashboard/assets/{dist-cbVssBvx.js → dist-B0bfBMeP.js} +1 -1
- package/dashboard/assets/{ebnfDiagram-PWID7BFC-DCMOZtU2.js → ebnfDiagram-PWID7BFC-iL5NQ1PE.js} +1 -1
- package/dashboard/assets/{erDiagram-RLTQ6QDP-CW4rUJGU.js → erDiagram-RLTQ6QDP-RK2Voa5O.js} +1 -1
- package/dashboard/assets/{eventmodeling-NTZA5JFV-C2q-Aaav.js → eventmodeling-NTZA5JFV-WgE7OJBb.js} +1 -1
- package/dashboard/assets/flowDiagram-HODETNUW-B9YcQ0Aq.js +1 -0
- package/dashboard/assets/{ganttDiagram-EL5Y4UJY-C1AqcwIF.js → ganttDiagram-EL5Y4UJY-D8CJXHRF.js} +1 -1
- package/dashboard/assets/{gitGraph-4MIJSDKK-DYR76gtd.js → gitGraph-4MIJSDKK-BuZ9Lg04.js} +1 -1
- package/dashboard/assets/{gitGraphDiagram-WWUBYQGX-B7hL98CE.js → gitGraphDiagram-WWUBYQGX-LEJMh54j.js} +1 -1
- package/dashboard/assets/{info-A6RAGUB7-TXB43LiH.js → info-A6RAGUB7-BL5k3pev.js} +1 -1
- package/dashboard/assets/{infoDiagram-27XIBGKW-Dk5dXmh_.js → infoDiagram-27XIBGKW-LJvntUnZ.js} +1 -1
- package/dashboard/assets/{ishikawaDiagram-5VMMS53U-BQDuNMka.js → ishikawaDiagram-5VMMS53U-BYTYgKCG.js} +1 -1
- package/dashboard/assets/{journeyDiagram-3NMN7TZE-DkustddD.js → journeyDiagram-3NMN7TZE-G25qgQgO.js} +1 -1
- package/dashboard/assets/{kanban-definition-UXKFOSKX-BW7Ann84.js → kanban-definition-UXKFOSKX-Bk3uLl1i.js} +1 -1
- package/dashboard/assets/{line-DVxcLeru.js → line-i4qHBaWO.js} +1 -1
- package/dashboard/assets/{linear-DOM_l9bO.js → linear-DmrqRsKG.js} +1 -1
- package/dashboard/assets/{mermaid-parser.core-CKg9fDvJ.js → mermaid-parser.core-BxbKr4QF.js} +3 -3
- package/dashboard/assets/{mermaid.core-DruW_5-Y.js → mermaid.core-Cltee6FS.js} +4 -4
- package/dashboard/assets/{mindmap-definition-YA3MSWOX-Di5W5VB3.js → mindmap-definition-YA3MSWOX-DkzO2JgS.js} +1 -1
- package/dashboard/assets/operations-B4_timQX.css +1 -0
- package/dashboard/assets/operations-DwppJviL.js +199 -0
- package/dashboard/assets/{packet-AYTQ26CC-BABwqtNu.js → packet-AYTQ26CC-uZQUKnrE.js} +1 -1
- package/dashboard/assets/{pegDiagram-XKGWAZYB-Cx4ABT6q.js → pegDiagram-XKGWAZYB-CrOEi00T.js} +1 -1
- package/dashboard/assets/{pie-WAS4IAKB-CNdXyrze.js → pie-WAS4IAKB-Dn9_tpQT.js} +1 -1
- package/dashboard/assets/{pieDiagram-E7YTZNPT-BilfQbIs.js → pieDiagram-E7YTZNPT-D-niUkzc.js} +1 -1
- package/dashboard/assets/{quadrantDiagram-AXDQQJYC-BVmMQHE5.js → quadrantDiagram-AXDQQJYC-CJOTiG5V.js} +1 -1
- package/dashboard/assets/{radar-RG4KPBEZ-fKNmDeoK.js → radar-RG4KPBEZ-DIIVz4e8.js} +1 -1
- package/dashboard/assets/{railroad-74A4TZTK-DhcvDCOh.js → railroad-74A4TZTK-1kiqDEID.js} +1 -1
- package/dashboard/assets/{railroad-abnf-HS5TGJTU-BTRVAmf0.js → railroad-abnf-HS5TGJTU-DK6rQPZR.js} +1 -1
- package/dashboard/assets/{railroad-ebnf-LZEXJU2U-CwS8fgC_.js → railroad-ebnf-LZEXJU2U-C6cOgYW4.js} +1 -1
- package/dashboard/assets/{railroad-peg-WCYAUIDC-CXuhXlNp.js → railroad-peg-WCYAUIDC-CQ3ptKef.js} +1 -1
- package/dashboard/assets/{railroadDiagram-O6MQD6OU-DnBru-jf.js → railroadDiagram-O6MQD6OU-nEfkpa-m.js} +1 -1
- package/dashboard/assets/{requirementDiagram-BXWQKSXE-FwdvUZ7_.js → requirementDiagram-BXWQKSXE-CdFMY8tG.js} +1 -1
- package/dashboard/assets/{sankeyDiagram-P5KCCOFB-BQvDORai.js → sankeyDiagram-P5KCCOFB-C0KxrFYv.js} +1 -1
- package/dashboard/assets/{sequenceDiagram-WJ2MYXX4-FWYQLzcY.js → sequenceDiagram-WJ2MYXX4-DeZ5ATiu.js} +1 -1
- package/dashboard/assets/{src-BwkPhlcp.js → src-Cg-chcoE.js} +1 -1
- package/dashboard/assets/{stateDiagram-D77RDMKH-CjPBd_Sa.js → stateDiagram-D77RDMKH-B9MwWL5B.js} +1 -1
- package/dashboard/assets/stateDiagram-v2-MP3YSRHH-SucFndhx.js +1 -0
- package/dashboard/assets/{swimlanes-42K2YHIH-SQLNsa4J.js → swimlanes-42K2YHIH-DB--fwQE.js} +1 -1
- package/dashboard/assets/swimlanesDiagram-VR7AAH4N-BzH-rIaK.js +8 -0
- package/dashboard/assets/{timeline-definition-24CTP7MA-BxCf20k1.js → timeline-definition-24CTP7MA-DLkkTbWb.js} +1 -1
- package/dashboard/assets/{treeView-Q6P3EWNA-BGLYAyGp.js → treeView-Q6P3EWNA-XkZ9rl9H.js} +1 -1
- package/dashboard/assets/{treemap-WGGIJYW6-BlzAwncU.js → treemap-WGGIJYW6-JCtVKxCE.js} +1 -1
- package/dashboard/assets/{vennDiagram-4TSXK5OY-D76RIZLz.js → vennDiagram-4TSXK5OY-DEb-x29Z.js} +1 -1
- package/dashboard/assets/{wardley-WFR3VGLG-Ba_reiVw.js → wardley-WFR3VGLG-DhI7ROY3.js} +1 -1
- package/dashboard/assets/{wardleyDiagram-VM6X3IG4-Cux4coOD.js → wardleyDiagram-VM6X3IG4-KNckudc5.js} +1 -1
- package/dashboard/assets/{xychartDiagram-S5SC5T6Z-Ch5mvnlH.js → xychartDiagram-S5SC5T6Z-D4c0ynCk.js} +1 -1
- package/dashboard/index.html +13 -2
- package/package.json +1 -1
- package/worker-assets/demo-target/README.md +7 -3
- package/chunks/chunk-AL32F74R.js +0 -1123
- package/chunks/dashboard-MN7GZLLX.js +0 -75
- package/chunks/managed-operation-642WPTLC.js +0 -59
- package/chunks/routing-ITJZBJIU.js +0 -61
- package/chunks/service-plugins-CN6Q2NSN.js +0 -63
- package/dashboard/assets/channel-BBlDqIBl.js +0 -1
- package/dashboard/assets/classDiagram-ZZMXUADV-CRH2kB8G.js +0 -1
- package/dashboard/assets/classDiagram-v2-VYDZK3BY-CRH2kB8G.js +0 -1
- package/dashboard/assets/flowDiagram-HODETNUW-3NOabgG3.js +0 -1
- package/dashboard/assets/operations-B-t_k60U.js +0 -194
- package/dashboard/assets/operations-D2ZKZFOF.css +0 -1
- package/dashboard/assets/stateDiagram-v2-MP3YSRHH-DHmenujm.js +0 -1
- package/dashboard/assets/swimlanesDiagram-VR7AAH4N-D1bH4ETZ.js +0 -8
|
@@ -3,11 +3,11 @@ import { createRequire as __0CreateRequire } from "node:module";
|
|
|
3
3
|
const require = __0CreateRequire(import.meta.url);
|
|
4
4
|
import {
|
|
5
5
|
getRuntimeAvailability
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-MNK373HF.js";
|
|
7
7
|
import {
|
|
8
8
|
ServicePluginServices,
|
|
9
9
|
consoleExecutionProfile
|
|
10
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-KCABBZHN.js";
|
|
11
11
|
import {
|
|
12
12
|
accountEnvPatch,
|
|
13
13
|
addAccount,
|
|
@@ -29,7 +29,7 @@ import {
|
|
|
29
29
|
setActiveAccount,
|
|
30
30
|
updateSetting,
|
|
31
31
|
validateConnectionConfig
|
|
32
|
-
} from "./chunk-
|
|
32
|
+
} from "./chunk-OILVEROI.js";
|
|
33
33
|
import {
|
|
34
34
|
DEFAULT_SETTINGS,
|
|
35
35
|
PROVIDERS,
|
|
@@ -48,26 +48,26 @@ import {
|
|
|
48
48
|
syncThemeChoices,
|
|
49
49
|
validateTheme,
|
|
50
50
|
writeInstalledTheme
|
|
51
|
-
} from "./chunk-
|
|
51
|
+
} from "./chunk-V2KJQVYE.js";
|
|
52
52
|
import {
|
|
53
53
|
DEFAULT_REGISTRY_URL,
|
|
54
54
|
createConsoleRuntime,
|
|
55
55
|
listProjectReviewChecks,
|
|
56
56
|
updateProjectReviewChecks
|
|
57
|
-
} from "./chunk-
|
|
57
|
+
} from "./chunk-3MWQNNJU.js";
|
|
58
58
|
import {
|
|
59
59
|
getRuntimeMetadata
|
|
60
|
-
} from "./chunk-
|
|
60
|
+
} from "./chunk-VWE36MUN.js";
|
|
61
61
|
import {
|
|
62
62
|
TOOL_DEFINITIONS,
|
|
63
63
|
getToolsForRole
|
|
64
|
-
} from "./chunk-
|
|
64
|
+
} from "./chunk-SIR7XW2S.js";
|
|
65
65
|
import {
|
|
66
66
|
CodexCatalogRefreshError
|
|
67
|
-
} from "./chunk-
|
|
67
|
+
} from "./chunk-PAMJEXE2.js";
|
|
68
68
|
import {
|
|
69
69
|
LlmApiRuntime
|
|
70
|
-
} from "./chunk-
|
|
70
|
+
} from "./chunk-HS2NQ7K6.js";
|
|
71
71
|
import {
|
|
72
72
|
aggregateCapabilities,
|
|
73
73
|
disable,
|
|
@@ -77,7 +77,7 @@ import {
|
|
|
77
77
|
readEnablement,
|
|
78
78
|
readInstalledPlugin,
|
|
79
79
|
writeEnablement
|
|
80
|
-
} from "./chunk-
|
|
80
|
+
} from "./chunk-RZZ6VPRY.js";
|
|
81
81
|
import {
|
|
82
82
|
MODEL_PRICING,
|
|
83
83
|
VERSION,
|
|
@@ -85,15 +85,1097 @@ import {
|
|
|
85
85
|
homeStateDir,
|
|
86
86
|
modelProvider,
|
|
87
87
|
registerModelPricing
|
|
88
|
-
} from "./chunk-
|
|
88
|
+
} from "./chunk-GYEYTNHK.js";
|
|
89
89
|
|
|
90
90
|
// packages/cli/src/web/operator-services.ts
|
|
91
91
|
import { execFileSync } from "node:child_process";
|
|
92
92
|
import { realpathSync, statSync } from "node:fs";
|
|
93
|
-
import { basename, resolve } from "node:path";
|
|
94
|
-
import { randomUUID as
|
|
93
|
+
import { basename, resolve as resolve3 } from "node:path";
|
|
94
|
+
import { randomUUID as randomUUID3 } from "node:crypto";
|
|
95
|
+
|
|
96
|
+
// packages/cli/src/tui/chatgpt-plan-store.ts
|
|
97
|
+
import { randomUUID } from "node:crypto";
|
|
98
|
+
import { closeSync, existsSync, fsyncSync, lstatSync, mkdirSync, openSync, readFileSync, renameSync, unlinkSync, writeFileSync } from "node:fs";
|
|
99
|
+
import { join } from "node:path";
|
|
100
|
+
|
|
101
|
+
// packages/cli/src/tui/chatgpt-plan-auth.ts
|
|
102
|
+
import { createHash, randomBytes } from "node:crypto";
|
|
103
|
+
import { createServer } from "node:http";
|
|
104
|
+
|
|
105
|
+
// node_modules/.pnpm/jose@6.2.12/node_modules/jose/dist/webapi/lib/buffer_utils.js
|
|
106
|
+
var encoder = new TextEncoder();
|
|
107
|
+
var decoder = new TextDecoder();
|
|
108
|
+
var strictDecoder = new TextDecoder("utf-8", { fatal: true });
|
|
109
|
+
var MAX_INT32 = 2 ** 32;
|
|
110
|
+
function concat(...buffers) {
|
|
111
|
+
const size = buffers.reduce((acc, { length }) => acc + length, 0), buf = new Uint8Array(size);
|
|
112
|
+
let i = 0;
|
|
113
|
+
for (const buffer of buffers)
|
|
114
|
+
buf.set(buffer, i), i += buffer.length;
|
|
115
|
+
return buf;
|
|
116
|
+
}
|
|
117
|
+
var NON_ASCII = /[^\x00-\x7f]/;
|
|
118
|
+
function encode(string) {
|
|
119
|
+
if (typeof string == "string" && string.length >= 128) {
|
|
120
|
+
if (NON_ASCII.test(string))
|
|
121
|
+
throw new TypeError("non-ASCII string encountered in encode()");
|
|
122
|
+
return encoder.encode(string);
|
|
123
|
+
}
|
|
124
|
+
const bytes = new Uint8Array(string.length);
|
|
125
|
+
for (let i = 0; i < string.length; i++) {
|
|
126
|
+
const code = string.charCodeAt(i);
|
|
127
|
+
if (code > 127)
|
|
128
|
+
throw new TypeError("non-ASCII string encountered in encode()");
|
|
129
|
+
bytes[i] = code;
|
|
130
|
+
}
|
|
131
|
+
return bytes;
|
|
132
|
+
}
|
|
133
|
+
function decodeBase64(encoded, url = false) {
|
|
134
|
+
if (Uint8Array.fromBase64)
|
|
135
|
+
return Uint8Array.fromBase64(encoded, { alphabet: url ? "base64url" : "base64" });
|
|
136
|
+
if (url) {
|
|
137
|
+
if (encoded.includes("+") || encoded.includes("/"))
|
|
138
|
+
throw new TypeError("Invalid base64url");
|
|
139
|
+
encoded = encoded.replace(/-/g, "+").replace(/_/g, "/");
|
|
140
|
+
}
|
|
141
|
+
const binary = atob(encoded), bytes = new Uint8Array(binary.length);
|
|
142
|
+
for (let i = 0; i < binary.length; i++)
|
|
143
|
+
bytes[i] = binary.charCodeAt(i);
|
|
144
|
+
return bytes;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
// node_modules/.pnpm/jose@6.2.12/node_modules/jose/dist/webapi/util/errors.js
|
|
148
|
+
var JOSEError = class extends Error {
|
|
149
|
+
static code = "ERR_JOSE_GENERIC";
|
|
150
|
+
code = "ERR_JOSE_GENERIC";
|
|
151
|
+
constructor(message2, options) {
|
|
152
|
+
super(message2, options), this.name = this.constructor.name, Error.captureStackTrace?.(this, this.constructor);
|
|
153
|
+
}
|
|
154
|
+
};
|
|
155
|
+
var JWTClaimValidationFailed = class extends JOSEError {
|
|
156
|
+
static code = "ERR_JWT_CLAIM_VALIDATION_FAILED";
|
|
157
|
+
code = "ERR_JWT_CLAIM_VALIDATION_FAILED";
|
|
158
|
+
claim;
|
|
159
|
+
reason;
|
|
160
|
+
payload;
|
|
161
|
+
constructor(message2, payload, claim = "unspecified", reason = "unspecified") {
|
|
162
|
+
super(message2, { cause: { claim, reason, payload } }), this.claim = claim, this.reason = reason, this.payload = payload;
|
|
163
|
+
}
|
|
164
|
+
};
|
|
165
|
+
var JWTExpired = class extends JOSEError {
|
|
166
|
+
static code = "ERR_JWT_EXPIRED";
|
|
167
|
+
code = "ERR_JWT_EXPIRED";
|
|
168
|
+
claim;
|
|
169
|
+
reason;
|
|
170
|
+
payload;
|
|
171
|
+
constructor(message2, payload, claim = "unspecified", reason = "unspecified") {
|
|
172
|
+
super(message2, { cause: { claim, reason, payload } }), this.claim = claim, this.reason = reason, this.payload = payload;
|
|
173
|
+
}
|
|
174
|
+
};
|
|
175
|
+
var JOSEAlgNotAllowed = class extends JOSEError {
|
|
176
|
+
static code = "ERR_JOSE_ALG_NOT_ALLOWED";
|
|
177
|
+
code = "ERR_JOSE_ALG_NOT_ALLOWED";
|
|
178
|
+
};
|
|
179
|
+
var JOSENotSupported = class extends JOSEError {
|
|
180
|
+
static code = "ERR_JOSE_NOT_SUPPORTED";
|
|
181
|
+
code = "ERR_JOSE_NOT_SUPPORTED";
|
|
182
|
+
};
|
|
183
|
+
var JWSInvalid = class extends JOSEError {
|
|
184
|
+
static code = "ERR_JWS_INVALID";
|
|
185
|
+
code = "ERR_JWS_INVALID";
|
|
186
|
+
};
|
|
187
|
+
var JWTInvalid = class extends JOSEError {
|
|
188
|
+
static code = "ERR_JWT_INVALID";
|
|
189
|
+
code = "ERR_JWT_INVALID";
|
|
190
|
+
};
|
|
191
|
+
var JWKSInvalid = class extends JOSEError {
|
|
192
|
+
static code = "ERR_JWKS_INVALID";
|
|
193
|
+
code = "ERR_JWKS_INVALID";
|
|
194
|
+
};
|
|
195
|
+
var JWKSNoMatchingKey = class extends JOSEError {
|
|
196
|
+
static code = "ERR_JWKS_NO_MATCHING_KEY";
|
|
197
|
+
code = "ERR_JWKS_NO_MATCHING_KEY";
|
|
198
|
+
constructor(message2 = "no applicable key found in the JSON Web Key Set", options) {
|
|
199
|
+
super(message2, options);
|
|
200
|
+
}
|
|
201
|
+
};
|
|
202
|
+
var JWKSMultipleMatchingKeys = class extends JOSEError {
|
|
203
|
+
[Symbol.asyncIterator] = async function* () {
|
|
204
|
+
};
|
|
205
|
+
static code = "ERR_JWKS_MULTIPLE_MATCHING_KEYS";
|
|
206
|
+
code = "ERR_JWKS_MULTIPLE_MATCHING_KEYS";
|
|
207
|
+
constructor(message2 = "multiple matching keys found in the JSON Web Key Set", options) {
|
|
208
|
+
super(message2, options);
|
|
209
|
+
}
|
|
210
|
+
};
|
|
211
|
+
var JWKSTimeout = class extends JOSEError {
|
|
212
|
+
static code = "ERR_JWKS_TIMEOUT";
|
|
213
|
+
code = "ERR_JWKS_TIMEOUT";
|
|
214
|
+
constructor(message2 = "request timed out", options) {
|
|
215
|
+
super(message2, options);
|
|
216
|
+
}
|
|
217
|
+
};
|
|
218
|
+
var JWSSignatureVerificationFailed = class extends JOSEError {
|
|
219
|
+
static code = "ERR_JWS_SIGNATURE_VERIFICATION_FAILED";
|
|
220
|
+
code = "ERR_JWS_SIGNATURE_VERIFICATION_FAILED";
|
|
221
|
+
constructor(message2 = "signature verification failed", options) {
|
|
222
|
+
super(message2, options);
|
|
223
|
+
}
|
|
224
|
+
};
|
|
225
|
+
|
|
226
|
+
// node_modules/.pnpm/jose@6.2.12/node_modules/jose/dist/webapi/util/base64url.js
|
|
227
|
+
var invalid = "The input to be decoded is not correctly encoded.";
|
|
228
|
+
function decode(input) {
|
|
229
|
+
try {
|
|
230
|
+
return decodeBase64(typeof input == "string" ? input : decoder.decode(input), true);
|
|
231
|
+
} catch (cause) {
|
|
232
|
+
throw new TypeError(invalid, { cause });
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
// node_modules/.pnpm/jose@6.2.12/node_modules/jose/dist/webapi/lib/validate.js
|
|
237
|
+
function isObject(input) {
|
|
238
|
+
if (typeof input != "object" || input === null || Object.prototype.toString.call(input) !== "[object Object]")
|
|
239
|
+
return false;
|
|
240
|
+
const prototype = Object.getPrototypeOf(input);
|
|
241
|
+
return prototype === null || Object.getPrototypeOf(prototype) === null;
|
|
242
|
+
}
|
|
243
|
+
function isJwkSet(input) {
|
|
244
|
+
return isObject(input) && Array.isArray(input.keys) && Array.from(input.keys).every(isObject);
|
|
245
|
+
}
|
|
246
|
+
function isDisjoint(...headers) {
|
|
247
|
+
const parameters = /* @__PURE__ */ new Set();
|
|
248
|
+
for (const header of headers)
|
|
249
|
+
if (header)
|
|
250
|
+
for (const parameter of Object.keys(header)) {
|
|
251
|
+
if (parameters.has(parameter))
|
|
252
|
+
return false;
|
|
253
|
+
parameters.add(parameter);
|
|
254
|
+
}
|
|
255
|
+
return true;
|
|
256
|
+
}
|
|
257
|
+
function decodeBase64url(value, label, ErrorClass) {
|
|
258
|
+
try {
|
|
259
|
+
return decode(value);
|
|
260
|
+
} catch {
|
|
261
|
+
throw new ErrorClass(`Failed to base64url decode the ${label}`);
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
function encodeBase64url(value, label, ErrorClass) {
|
|
265
|
+
try {
|
|
266
|
+
return encode(value);
|
|
267
|
+
} catch {
|
|
268
|
+
throw new ErrorClass(`The ${label} is not a valid base64url string`);
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
function parseJoseHeader(b64, ErrorClass, message2) {
|
|
272
|
+
let parsed;
|
|
273
|
+
try {
|
|
274
|
+
parsed = JSON.parse(strictDecoder.decode(decode(b64)));
|
|
275
|
+
} catch {
|
|
276
|
+
throw new ErrorClass(message2);
|
|
277
|
+
}
|
|
278
|
+
if (!isObject(parsed))
|
|
279
|
+
throw new ErrorClass(message2);
|
|
280
|
+
return parsed;
|
|
281
|
+
}
|
|
282
|
+
var JWS_RECOGNIZED = { __proto__: null, b64: true };
|
|
283
|
+
function validateAlgorithms(option, algorithms) {
|
|
284
|
+
if (algorithms !== void 0 && (!Array.isArray(algorithms) || algorithms.some((s) => typeof s != "string")))
|
|
285
|
+
throw new TypeError(`"${option}" option must be an array of strings`);
|
|
286
|
+
return algorithms === void 0 ? void 0 : new Set(algorithms);
|
|
287
|
+
}
|
|
288
|
+
function validateCrit(Err, recognizedDefault, recognizedOption, protectedHeader, joseHeader) {
|
|
289
|
+
if (joseHeader.crit !== void 0 && protectedHeader?.crit === void 0)
|
|
290
|
+
throw new Err('"crit" (Critical) Header Parameter MUST be integrity protected');
|
|
291
|
+
if (!protectedHeader || protectedHeader.crit === void 0)
|
|
292
|
+
return [];
|
|
293
|
+
if (!Array.isArray(protectedHeader.crit) || protectedHeader.crit.length === 0 || protectedHeader.crit.some((input) => typeof input != "string" || input.length === 0))
|
|
294
|
+
throw new Err('"crit" (Critical) Header Parameter MUST be an array of non-empty strings when present');
|
|
295
|
+
const recognized = recognizedOption === void 0 ? recognizedDefault : { __proto__: null, ...recognizedOption, ...recognizedDefault };
|
|
296
|
+
for (const parameter of protectedHeader.crit) {
|
|
297
|
+
if (!(parameter in recognized))
|
|
298
|
+
throw new JOSENotSupported(`Extension Header Parameter "${parameter}" is not recognized`);
|
|
299
|
+
if (!Object.hasOwn(joseHeader, parameter) || joseHeader[parameter] === void 0)
|
|
300
|
+
throw new Err(`Extension Header Parameter "${parameter}" is missing`);
|
|
301
|
+
if (recognized[parameter] && (!Object.hasOwn(protectedHeader, parameter) || protectedHeader[parameter] === void 0))
|
|
302
|
+
throw new Err(`Extension Header Parameter "${parameter}" MUST be integrity protected`);
|
|
303
|
+
}
|
|
304
|
+
return protectedHeader.crit;
|
|
305
|
+
}
|
|
306
|
+
function validateB64(protectedHeader, extensions) {
|
|
307
|
+
if (extensions.includes("b64")) {
|
|
308
|
+
const b64 = protectedHeader.b64;
|
|
309
|
+
if (typeof b64 != "boolean")
|
|
310
|
+
throw new JWSInvalid('The "b64" (base64url-encode payload) Header Parameter must be a boolean');
|
|
311
|
+
return b64;
|
|
312
|
+
}
|
|
313
|
+
return true;
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
// node_modules/.pnpm/jose@6.2.12/node_modules/jose/dist/webapi/lib/key.js
|
|
317
|
+
var tag = (key) => key[Symbol.toStringTag];
|
|
318
|
+
var jwkMatchesOp = (entry, key, usage) => {
|
|
319
|
+
const { alg } = entry;
|
|
320
|
+
if (key.use !== void 0) {
|
|
321
|
+
const expected = usage === "sign" || usage === "verify" ? "sig" : "enc";
|
|
322
|
+
if (key.use !== expected)
|
|
323
|
+
throw new TypeError(`Invalid key for this operation, its "use" must be "${expected}" when present`);
|
|
324
|
+
}
|
|
325
|
+
if (key.alg !== void 0 && key.alg !== alg)
|
|
326
|
+
throw new TypeError(`Invalid key for this operation, its "alg" must be "${alg}" when present`);
|
|
327
|
+
if (Array.isArray(key.key_ops)) {
|
|
328
|
+
const expectedKeyOp = usage === "encrypt" || usage === "decrypt" ? entry.ops?.[usage === "encrypt" ? 0 : 1] : usage;
|
|
329
|
+
if (expectedKeyOp && !key.key_ops.includes(expectedKeyOp))
|
|
330
|
+
throw new TypeError(`Invalid key for this operation, its "key_ops" must include "${expectedKeyOp}" when present`);
|
|
331
|
+
}
|
|
332
|
+
};
|
|
333
|
+
async function prepareKey(entry, key, usage) {
|
|
334
|
+
const { alg, secret } = entry, privateKey = usage === "decrypt" || usage === "sign";
|
|
335
|
+
if (secret && key instanceof Uint8Array)
|
|
336
|
+
return key;
|
|
337
|
+
let normalized, keyObject;
|
|
338
|
+
if (isObject(key)) {
|
|
339
|
+
if (normalized = normalizeJwk(key), typeof normalized.kty != "string")
|
|
340
|
+
throw invalidKeyType(alg, key, secret);
|
|
341
|
+
if (!(secret ? normalized.kty === "oct" && typeof normalized.k == "string" : normalized.kty !== "oct" && (privateKey ? normalized.kty === "AKP" && typeof normalized.priv == "string" || typeof normalized.d == "string" : normalized.d === void 0 && normalized.priv === void 0)))
|
|
342
|
+
throw new TypeError(secret ? 'JSON Web Key for symmetric algorithms must have JWK "kty" (Key Type) equal to "oct" and the JWK "k" (Key Value) present' : `JSON Web Key for this operation must be a ${privateKey ? "private" : "public"} JWK`);
|
|
343
|
+
if (jwkMatchesOp(entry, normalized, usage), normalized.kty === "oct")
|
|
344
|
+
return decode(normalized.k);
|
|
345
|
+
if (!Object.isFrozen(key)) {
|
|
346
|
+
const { key_ops } = key;
|
|
347
|
+
Array.isArray(key_ops) && Object.freeze(key_ops), Object.freeze(key);
|
|
348
|
+
}
|
|
349
|
+
} else {
|
|
350
|
+
if (!isKeyLike(key))
|
|
351
|
+
throw invalidKeyType(alg, key, secret);
|
|
352
|
+
const expectedType = secret ? "secret" : privateKey ? "private" : "public";
|
|
353
|
+
if (key.type !== expectedType && (secret || ["secret", "public", "private"].includes(key.type)))
|
|
354
|
+
throw new TypeError(`${tag(key)} instances must be of type "${expectedType}" for the ${alg} algorithm`);
|
|
355
|
+
if (isCryptoKey(key))
|
|
356
|
+
return key;
|
|
357
|
+
if (keyObject = key, keyObject.type === "secret")
|
|
358
|
+
return keyObject.export();
|
|
359
|
+
}
|
|
360
|
+
cache ||= /* @__PURE__ */ new WeakMap();
|
|
361
|
+
const cacheKey = key;
|
|
362
|
+
let cached = cache.get(cacheKey);
|
|
363
|
+
if (cached?.[alg])
|
|
364
|
+
return cached[alg];
|
|
365
|
+
if (cached || cache.set(cacheKey, cached = {}), keyObject && typeof keyObject.toCryptoKey == "function") {
|
|
366
|
+
const isPublic = keyObject.type === "public", crv = nist[keyObject.asymmetricKeyDetails?.namedCurve], params = entry.resolve?.({ crv, asymmetricKeyType: keyObject.asymmetricKeyType }) ?? entry.subtle;
|
|
367
|
+
return cached[alg] = keyObject.toCryptoKey(params, isPublic, entry.usages[isPublic ? 0 : 1]);
|
|
368
|
+
}
|
|
369
|
+
return normalized ??= keyObject.export({ format: "jwk" }), normalized.alg = alg, cached[alg] = await jwkToKey(entry, normalized);
|
|
370
|
+
}
|
|
371
|
+
var cache;
|
|
372
|
+
var nist = {
|
|
373
|
+
__proto__: null,
|
|
374
|
+
prime256v1: "P-256",
|
|
375
|
+
secp384r1: "P-384",
|
|
376
|
+
secp521r1: "P-521"
|
|
377
|
+
};
|
|
378
|
+
var isCryptoKey = (key) => {
|
|
379
|
+
if (key?.[Symbol.toStringTag] === "CryptoKey")
|
|
380
|
+
return true;
|
|
381
|
+
try {
|
|
382
|
+
return key instanceof CryptoKey;
|
|
383
|
+
} catch {
|
|
384
|
+
return false;
|
|
385
|
+
}
|
|
386
|
+
};
|
|
387
|
+
var isKeyObject = (key) => key?.[Symbol.toStringTag] === "KeyObject";
|
|
388
|
+
var isKeyLike = (key) => isCryptoKey(key) || isKeyObject(key);
|
|
389
|
+
function message(msg, actual, ...types) {
|
|
390
|
+
if (types.length > 2) {
|
|
391
|
+
const last = types.pop();
|
|
392
|
+
msg += `one of type ${types.join(", ")}, or ${last}.`;
|
|
393
|
+
} else types.length === 2 ? msg += `one of type ${types[0]} or ${types[1]}.` : msg += `of type ${types[0]}.`;
|
|
394
|
+
return actual == null ? msg += ` Received ${actual}` : typeof actual == "function" && actual.name ? msg += ` Received function ${actual.name}` : typeof actual == "object" && actual != null && actual.constructor?.name && (msg += ` Received an instance of ${actual.constructor.name}`), msg;
|
|
395
|
+
}
|
|
396
|
+
function invalidKeyType(alg, actual, secret) {
|
|
397
|
+
const types = ["CryptoKey", "KeyObject", "JSON Web Key"];
|
|
398
|
+
return secret && types.push("Uint8Array"), new TypeError(message(`Key for the ${alg} algorithm must be `, actual, ...types));
|
|
399
|
+
}
|
|
400
|
+
var unusable = (name, prop = "algorithm.name") => new TypeError(`CryptoKey does not support this operation, its ${prop} must be ${name}`);
|
|
401
|
+
function checkUsage(key, usage) {
|
|
402
|
+
if (usage && !key.usages.includes(usage))
|
|
403
|
+
throw new TypeError(`CryptoKey does not support this operation, its usages must include ${usage}.`);
|
|
404
|
+
}
|
|
405
|
+
function checkModulusLength(alg, key) {
|
|
406
|
+
const { modulusLength } = key.algorithm;
|
|
407
|
+
if (typeof modulusLength != "number" || modulusLength < 2048)
|
|
408
|
+
throw new TypeError(`${alg} requires key modulusLength to be 2048 bits or larger`);
|
|
409
|
+
}
|
|
410
|
+
function checkCryptoKey(key, expected, usage) {
|
|
411
|
+
const algorithm = key.algorithm;
|
|
412
|
+
if (algorithm.name !== expected.name)
|
|
413
|
+
throw unusable(expected.name);
|
|
414
|
+
if (expected.hash && algorithm.hash?.name !== expected.hash)
|
|
415
|
+
throw unusable(expected.hash, "algorithm.hash");
|
|
416
|
+
if (expected.namedCurve && algorithm.namedCurve !== expected.namedCurve)
|
|
417
|
+
throw unusable(expected.namedCurve, "algorithm.namedCurve");
|
|
418
|
+
if (expected.length !== void 0 && algorithm.length !== expected.length)
|
|
419
|
+
throw unusable(expected.length, "algorithm.length");
|
|
420
|
+
checkUsage(key, usage);
|
|
421
|
+
}
|
|
422
|
+
function snapshotJwk(jwk) {
|
|
423
|
+
return { __proto__: null, ...jwk };
|
|
424
|
+
}
|
|
425
|
+
function normalizeJwk(jwk) {
|
|
426
|
+
const normalized = snapshotJwk(jwk);
|
|
427
|
+
if (normalized.ext !== void 0 && typeof normalized.ext != "boolean")
|
|
428
|
+
throw new TypeError('"ext" (Extractable) Parameter must be a boolean');
|
|
429
|
+
if (normalized.key_ops !== void 0) {
|
|
430
|
+
const value = normalized.key_ops, keyOps = Array.isArray(value) ? [...value] : void 0;
|
|
431
|
+
if (!keyOps || keyOps.some((operation) => typeof operation != "string") || new Set(keyOps).size !== keyOps.length)
|
|
432
|
+
throw new TypeError('"key_ops" (Key Operations) Parameter must be an array of unique strings');
|
|
433
|
+
normalized.key_ops = keyOps;
|
|
434
|
+
}
|
|
435
|
+
return normalized;
|
|
436
|
+
}
|
|
437
|
+
async function jwkToKey(entry, jwk, extractable) {
|
|
438
|
+
if (!entry.kty.includes(jwk.kty))
|
|
439
|
+
throw new JOSENotSupported('Invalid or unsupported JWK "alg" (Algorithm) Parameter value');
|
|
440
|
+
const algorithm = entry.resolve?.({ kty: jwk.kty, crv: jwk.crv }) ?? entry.subtle, isPrivate = !!(jwk.d || jwk.priv), keyData = { ...jwk, ext: extractable ?? jwk.ext };
|
|
441
|
+
return keyData.kty !== "AKP" && delete keyData.alg, delete keyData.use, crypto.subtle.importKey("jwk", keyData, algorithm, keyData.ext ?? !isPrivate, jwk.key_ops ?? entry.usages[isPrivate ? 1 : 0]);
|
|
442
|
+
}
|
|
443
|
+
async function rawKey(key, expected, usage, extractable = false) {
|
|
444
|
+
return key instanceof Uint8Array && (key = await crypto.subtle.importKey("raw", key, expected, extractable, [usage])), checkCryptoKey(key, expected, usage), key;
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
// node_modules/.pnpm/jose@6.2.12/node_modules/jose/dist/webapi/lib/key_descriptor.js
|
|
448
|
+
function table(entries) {
|
|
449
|
+
const out = { __proto__: null };
|
|
450
|
+
for (const alg in entries)
|
|
451
|
+
out[alg] = { ...entries[alg], alg };
|
|
452
|
+
return out;
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
// node_modules/.pnpm/jose@6.2.12/node_modules/jose/dist/webapi/lib/jws_algorithms.js
|
|
456
|
+
var sig = [["verify"], ["sign"]];
|
|
457
|
+
function hmac(bits) {
|
|
458
|
+
const subtle = { name: "HMAC", hash: `SHA-${bits}` };
|
|
459
|
+
return { kty: ["oct"], secret: true, subtle, signing: subtle, usages: sig };
|
|
460
|
+
}
|
|
461
|
+
function rsa(bits, saltLength) {
|
|
462
|
+
const subtle = { name: saltLength ? "RSA-PSS" : "RSASSA-PKCS1-v1_5", hash: `SHA-${bits}` };
|
|
463
|
+
return {
|
|
464
|
+
kty: ["RSA"],
|
|
465
|
+
subtle,
|
|
466
|
+
signing: saltLength ? { ...subtle, saltLength } : subtle,
|
|
467
|
+
usages: sig,
|
|
468
|
+
minRsaBits: 2048
|
|
469
|
+
};
|
|
470
|
+
}
|
|
471
|
+
function ecdsa(crv, bits) {
|
|
472
|
+
return {
|
|
473
|
+
kty: ["EC"],
|
|
474
|
+
crv,
|
|
475
|
+
subtle: { name: "ECDSA", namedCurve: crv },
|
|
476
|
+
signing: { name: "ECDSA", hash: `SHA-${bits}` },
|
|
477
|
+
usages: sig
|
|
478
|
+
};
|
|
479
|
+
}
|
|
480
|
+
function eddsa() {
|
|
481
|
+
const subtle = { name: "Ed25519" };
|
|
482
|
+
return {
|
|
483
|
+
kty: ["OKP"],
|
|
484
|
+
crv: "Ed25519",
|
|
485
|
+
subtle,
|
|
486
|
+
signing: subtle,
|
|
487
|
+
usages: sig
|
|
488
|
+
};
|
|
489
|
+
}
|
|
490
|
+
function mldsa(bits) {
|
|
491
|
+
const subtle = { name: `ML-DSA-${bits}` };
|
|
492
|
+
return {
|
|
493
|
+
kty: ["AKP"],
|
|
494
|
+
subtle,
|
|
495
|
+
signing: subtle,
|
|
496
|
+
usages: sig
|
|
497
|
+
};
|
|
498
|
+
}
|
|
499
|
+
var JWS = table({
|
|
500
|
+
HS256: hmac(256),
|
|
501
|
+
HS384: hmac(384),
|
|
502
|
+
HS512: hmac(512),
|
|
503
|
+
RS256: rsa(256),
|
|
504
|
+
RS384: rsa(384),
|
|
505
|
+
RS512: rsa(512),
|
|
506
|
+
PS256: rsa(256, 32),
|
|
507
|
+
PS384: rsa(384, 48),
|
|
508
|
+
PS512: rsa(512, 64),
|
|
509
|
+
ES256: ecdsa("P-256", 256),
|
|
510
|
+
ES384: ecdsa("P-384", 384),
|
|
511
|
+
ES512: ecdsa("P-521", 512),
|
|
512
|
+
EdDSA: eddsa(),
|
|
513
|
+
Ed25519: eddsa(),
|
|
514
|
+
"ML-DSA-44": mldsa(44),
|
|
515
|
+
"ML-DSA-65": mldsa(65),
|
|
516
|
+
"ML-DSA-87": mldsa(87)
|
|
517
|
+
});
|
|
518
|
+
function jwsAlgorithm(alg) {
|
|
519
|
+
const entry = typeof alg == "string" ? JWS[alg] : void 0;
|
|
520
|
+
if (!entry)
|
|
521
|
+
throw new JOSENotSupported(`alg ${alg} is not supported either by JOSE or your javascript runtime`);
|
|
522
|
+
return entry;
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
// node_modules/.pnpm/jose@6.2.12/node_modules/jose/dist/webapi/lib/jws_verify.js
|
|
526
|
+
function prepareVerify(options) {
|
|
527
|
+
return [options && validateAlgorithms("algorithms", options.algorithms), options?.crit];
|
|
528
|
+
}
|
|
529
|
+
function parseProtectedHeader(encodedProtected) {
|
|
530
|
+
return encodedProtected === void 0 ? {} : parseJoseHeader(encodedProtected, JWSInvalid, "JWS Protected Header is invalid");
|
|
531
|
+
}
|
|
532
|
+
function encodeCompactUnencodedPayload(payload) {
|
|
533
|
+
try {
|
|
534
|
+
return encode(payload);
|
|
535
|
+
} catch {
|
|
536
|
+
throw new JWSInvalid("JWS Compact Serialization payload must use only ASCII characters");
|
|
537
|
+
}
|
|
538
|
+
}
|
|
539
|
+
async function verifySignature(jws, shared, key, encodeUnencodedPayload, parsedProtected) {
|
|
540
|
+
const { protected: encodedProtected, header, payload: inputPayload } = jws, parsedProt = parsedProtected ?? parseProtectedHeader(encodedProtected);
|
|
541
|
+
if (!isDisjoint(parsedProt, header))
|
|
542
|
+
throw new JWSInvalid("JWS Protected and JWS Unprotected Header Parameter names must be disjoint");
|
|
543
|
+
const joseHeader = { ...parsedProt, ...header }, b64 = validateB64(parsedProt, validateCrit(JWSInvalid, JWS_RECOGNIZED, shared[1], parsedProt, joseHeader)), { alg } = joseHeader;
|
|
544
|
+
if (typeof alg != "string" || !alg)
|
|
545
|
+
throw new JWSInvalid('JWS "alg" (Algorithm) Header Parameter missing or invalid');
|
|
546
|
+
if (shared[0] && !shared[0].has(alg))
|
|
547
|
+
throw new JOSEAlgNotAllowed('"alg" (Algorithm) Header Parameter value not allowed');
|
|
548
|
+
if (b64) {
|
|
549
|
+
if (typeof inputPayload != "string")
|
|
550
|
+
throw new JWSInvalid("JWS Payload must be a string");
|
|
551
|
+
} else if (typeof inputPayload != "string" && !(inputPayload instanceof Uint8Array))
|
|
552
|
+
throw new JWSInvalid("JWS Payload must be a string or an Uint8Array instance");
|
|
553
|
+
const signingPayload = b64 || typeof inputPayload != "string" ? inputPayload : encodeUnencodedPayload(inputPayload);
|
|
554
|
+
let resolvedKey = false;
|
|
555
|
+
typeof key == "function" && (key = await key(parsedProt, jws), resolvedKey = true);
|
|
556
|
+
const entry = jwsAlgorithm(alg), data = concat(encodedProtected !== void 0 ? encode(encodedProtected) : new Uint8Array(), encode("."), typeof signingPayload == "string" ? shared[2] ??= encodeBase64url(signingPayload, "payload", JWSInvalid) : signingPayload), signature = decodeBase64url(jws.signature, "signature", JWSInvalid), k = await prepareKey(entry, key, "verify"), cryptoKey = await rawKey(k, entry.subtle, "verify");
|
|
557
|
+
entry.minRsaBits && checkModulusLength(entry.alg, cryptoKey);
|
|
558
|
+
let verified = false;
|
|
559
|
+
try {
|
|
560
|
+
verified = await crypto.subtle.verify(entry.signing, cryptoKey, signature, data);
|
|
561
|
+
} catch {
|
|
562
|
+
}
|
|
563
|
+
if (!verified)
|
|
564
|
+
throw new JWSSignatureVerificationFailed();
|
|
565
|
+
const result = { payload: typeof signingPayload == "string" ? decodeBase64url(signingPayload, "payload", JWSInvalid) : signingPayload };
|
|
566
|
+
return encodedProtected !== void 0 && (result.protectedHeader = parsedProt), header !== void 0 && (result.unprotectedHeader = header), resolvedKey ? [{ ...result, key: k }, b64] : [result, b64];
|
|
567
|
+
}
|
|
568
|
+
async function verifyCompact(jws, shared, key) {
|
|
569
|
+
if (jws instanceof Uint8Array && (jws = decoder.decode(jws)), typeof jws != "string")
|
|
570
|
+
throw new JWSInvalid("Compact JWS must be a string or Uint8Array");
|
|
571
|
+
const { 0: protectedHeader, 1: payload, 2: signature, length } = jws.split(".");
|
|
572
|
+
if (length !== 3)
|
|
573
|
+
throw new JWSInvalid("Invalid Compact JWS");
|
|
574
|
+
return verifySignature({ payload, protected: protectedHeader, signature }, shared, key, encodeCompactUnencodedPayload);
|
|
575
|
+
}
|
|
576
|
+
|
|
577
|
+
// node_modules/.pnpm/jose@6.2.12/node_modules/jose/dist/webapi/lib/jwt_claims_set.js
|
|
578
|
+
var epoch = (date) => Math.floor(date.getTime() / 1e3);
|
|
579
|
+
var multipliers = {
|
|
580
|
+
s: 1,
|
|
581
|
+
m: 60,
|
|
582
|
+
h: 3600,
|
|
583
|
+
d: 86400,
|
|
584
|
+
w: 604800,
|
|
585
|
+
y: 31557600
|
|
586
|
+
};
|
|
587
|
+
var REGEX = /^(\+|\-)? ?(\d+|\d+\.\d+) ?(seconds?|secs?|s|minutes?|mins?|m|hours?|hrs?|h|days?|d|weeks?|w|years?|yrs?|y)(?: (ago|from now))?$/i;
|
|
588
|
+
var checkFailed = "check_failed";
|
|
589
|
+
function invalidDuration() {
|
|
590
|
+
throw new TypeError("Invalid time period format");
|
|
591
|
+
}
|
|
592
|
+
function secs(str) {
|
|
593
|
+
typeof str != "string" && invalidDuration();
|
|
594
|
+
const matched = REGEX.exec(str);
|
|
595
|
+
(!matched || matched[4] && matched[1]) && invalidDuration();
|
|
596
|
+
const value = parseFloat(matched[2]), numericDate2 = Math.round(value * multipliers[matched[3][0].toLowerCase()]);
|
|
597
|
+
return Number.isFinite(numericDate2) || invalidDuration(), matched[1] === "-" || matched[4] === "ago" ? -numericDate2 : numericDate2;
|
|
598
|
+
}
|
|
599
|
+
function validateInput(label, input) {
|
|
600
|
+
if (!Number.isFinite(input))
|
|
601
|
+
throw new TypeError(`Invalid ${label} input`);
|
|
602
|
+
return input;
|
|
603
|
+
}
|
|
604
|
+
var normalizeTyp = (value) => {
|
|
605
|
+
const normalized = value.toLowerCase();
|
|
606
|
+
return value.includes("/") ? normalized : `application/${normalized}`;
|
|
607
|
+
};
|
|
608
|
+
var checkAudiencePresence = (audPayload, audOption) => typeof audPayload == "string" ? audOption.includes(audPayload) : Array.isArray(audPayload) ? audOption.some((aud) => audPayload.includes(aud)) : false;
|
|
609
|
+
function validateNumericDate(payload, claim, required = false) {
|
|
610
|
+
const value = payload[claim];
|
|
611
|
+
if (!(value === void 0 && !required)) {
|
|
612
|
+
if (typeof value != "number")
|
|
613
|
+
throw new JWTClaimValidationFailed(`"${claim}" claim must be a number`, payload, claim, "invalid");
|
|
614
|
+
return value;
|
|
615
|
+
}
|
|
616
|
+
}
|
|
617
|
+
function unexpectedClaim(payload, claim) {
|
|
618
|
+
throw new JWTClaimValidationFailed(`unexpected "${claim}" claim value`, payload, claim, checkFailed);
|
|
619
|
+
}
|
|
620
|
+
function validateClaimsSet(protectedHeader, encodedPayload, options = {}) {
|
|
621
|
+
let payload;
|
|
622
|
+
try {
|
|
623
|
+
payload = JSON.parse(strictDecoder.decode(encodedPayload));
|
|
624
|
+
} catch {
|
|
625
|
+
}
|
|
626
|
+
if (!isObject(payload))
|
|
627
|
+
throw new JWTInvalid("JWT Claims Set must be a top-level JSON object");
|
|
628
|
+
const { typ } = options;
|
|
629
|
+
if (typ !== void 0 && (typeof protectedHeader.typ != "string" || normalizeTyp(protectedHeader.typ) !== normalizeTyp(typ)))
|
|
630
|
+
throw new JWTClaimValidationFailed('unexpected "typ" JWT header value', payload, "typ", checkFailed);
|
|
631
|
+
const { requiredClaims = [], issuer, subject, audience, maxTokenAge } = options, presenceCheck = [...requiredClaims];
|
|
632
|
+
maxTokenAge !== void 0 && presenceCheck.push("iat"), audience !== void 0 && presenceCheck.push("aud"), subject !== void 0 && presenceCheck.push("sub"), issuer !== void 0 && presenceCheck.push("iss");
|
|
633
|
+
for (const claim of new Set(presenceCheck.reverse()))
|
|
634
|
+
if (!Object.hasOwn(payload, claim))
|
|
635
|
+
throw new JWTClaimValidationFailed(`missing required "${claim}" claim`, payload, claim, "missing");
|
|
636
|
+
issuer !== void 0 && !(Array.isArray(issuer) ? issuer : [issuer]).includes(payload.iss) && unexpectedClaim(payload, "iss"), subject !== void 0 && payload.sub !== subject && unexpectedClaim(payload, "sub"), audience !== void 0 && !checkAudiencePresence(payload.aud, typeof audience == "string" ? [audience] : audience) && unexpectedClaim(payload, "aud");
|
|
637
|
+
const { clockTolerance } = options;
|
|
638
|
+
let tolerance = 0;
|
|
639
|
+
if (typeof clockTolerance == "string")
|
|
640
|
+
tolerance = secs(clockTolerance);
|
|
641
|
+
else if (clockTolerance !== void 0) {
|
|
642
|
+
if (typeof clockTolerance != "number")
|
|
643
|
+
throw new TypeError("Invalid clockTolerance option type");
|
|
644
|
+
tolerance = clockTolerance;
|
|
645
|
+
}
|
|
646
|
+
validateInput("clockTolerance option", tolerance);
|
|
647
|
+
const { currentDate } = options, now = validateInput("currentDate option", epoch(currentDate === void 0 ? /* @__PURE__ */ new Date() : currentDate)), iat = validateNumericDate(payload, "iat", maxTokenAge !== void 0), nbf = validateNumericDate(payload, "nbf");
|
|
648
|
+
if (nbf !== void 0 && nbf > now + tolerance)
|
|
649
|
+
throw new JWTClaimValidationFailed('"nbf" claim timestamp check failed', payload, "nbf", checkFailed);
|
|
650
|
+
const exp = validateNumericDate(payload, "exp");
|
|
651
|
+
if (exp !== void 0 && exp <= now - tolerance)
|
|
652
|
+
throw new JWTExpired('"exp" claim timestamp check failed', payload, "exp", checkFailed);
|
|
653
|
+
if (maxTokenAge !== void 0) {
|
|
654
|
+
const age = now - iat, max = validateInput("maxTokenAge option", typeof maxTokenAge == "number" ? maxTokenAge : secs(maxTokenAge));
|
|
655
|
+
if (age - tolerance > max)
|
|
656
|
+
throw new JWTExpired('"iat" claim timestamp check failed (too far in the past)', payload, "iat", checkFailed);
|
|
657
|
+
if (age < -tolerance)
|
|
658
|
+
throw new JWTClaimValidationFailed('"iat" claim timestamp check failed (it should be in the past)', payload, "iat", checkFailed);
|
|
659
|
+
}
|
|
660
|
+
return payload;
|
|
661
|
+
}
|
|
662
|
+
|
|
663
|
+
// node_modules/.pnpm/jose@6.2.12/node_modules/jose/dist/webapi/jwt/verify.js
|
|
664
|
+
async function jwtVerify(jwt, key, options) {
|
|
665
|
+
const [verified, b64] = await verifyCompact(jwt, prepareVerify(options), key);
|
|
666
|
+
if (!b64)
|
|
667
|
+
throw new JWTInvalid("JWTs MUST NOT use unencoded payload");
|
|
668
|
+
const payload = validateClaimsSet(verified.protectedHeader, verified.payload, options);
|
|
669
|
+
return { ...verified, payload };
|
|
670
|
+
}
|
|
671
|
+
|
|
672
|
+
// node_modules/.pnpm/jose@6.2.12/node_modules/jose/dist/webapi/jwks/local.js
|
|
673
|
+
function isUsableJWK(jwk, entry, alg, kid) {
|
|
674
|
+
const { kty, key_ops: keyOps, ext, kid: jwkKid, alg: jwkAlg, use, crv } = jwk;
|
|
675
|
+
return (ext === void 0 || typeof ext == "boolean") && (keyOps === void 0 || Array.isArray(keyOps) && keyOps.every((operation, index) => typeof operation == "string" && keyOps.indexOf(operation) === index) && keyOps.includes("verify")) && entry.kty.includes(kty) && (kid === void 0 || typeof kid == "string" && kid === jwkKid) && (jwkAlg === void 0 ? kty !== "AKP" : alg === jwkAlg) && (use === void 0 || use === "sig") && (!entry.crv || crv === entry.crv);
|
|
676
|
+
}
|
|
677
|
+
async function importWithAlgCache(cache2, jwk, entry) {
|
|
678
|
+
const cached = cache2.get(jwk) || cache2.set(jwk, {}).get(jwk), { alg } = entry;
|
|
679
|
+
if (cached[alg] === void 0) {
|
|
680
|
+
const pending = jwkToKey(entry, jwk, true).then((key) => {
|
|
681
|
+
if (key.type !== "public")
|
|
682
|
+
throw new JWKSInvalid("JSON Web Key Set members must be public keys");
|
|
683
|
+
return cached[alg] = key, key;
|
|
684
|
+
}).catch((error) => {
|
|
685
|
+
throw cached[alg] === pending && delete cached[alg], error;
|
|
686
|
+
});
|
|
687
|
+
cached[alg] = pending;
|
|
688
|
+
}
|
|
689
|
+
return cached[alg];
|
|
690
|
+
}
|
|
691
|
+
function createLocalJWKSet(jwks) {
|
|
692
|
+
let snapshot;
|
|
693
|
+
try {
|
|
694
|
+
snapshot = structuredClone(jwks);
|
|
695
|
+
} catch {
|
|
696
|
+
}
|
|
697
|
+
if (!isJwkSet(snapshot))
|
|
698
|
+
throw new JWKSInvalid("JSON Web Key Set malformed");
|
|
699
|
+
const metadata = snapshot.keys.map((jwk) => {
|
|
700
|
+
const normalized = snapshotJwk(jwk);
|
|
701
|
+
return Array.isArray(normalized.key_ops) && (normalized.key_ops = [...normalized.key_ops]), normalized;
|
|
702
|
+
}), cached = /* @__PURE__ */ new WeakMap();
|
|
703
|
+
return Object.defineProperty(async (protectedHeader, token) => {
|
|
704
|
+
const { alg, kid } = { ...protectedHeader, ...token?.header }, entry = typeof alg == "string" ? JWS[alg] : void 0;
|
|
705
|
+
if (!entry || entry.secret)
|
|
706
|
+
throw new JOSENotSupported('Unsupported "alg" value for a JSON Web Key Set');
|
|
707
|
+
const candidates = snapshot.keys.filter((_, index) => isUsableJWK(metadata[index], entry, alg, kid)), { 0: jwk, length } = candidates;
|
|
708
|
+
if (!length)
|
|
709
|
+
throw new JWKSNoMatchingKey();
|
|
710
|
+
if (length !== 1) {
|
|
711
|
+
const error = new JWKSMultipleMatchingKeys();
|
|
712
|
+
throw error[Symbol.asyncIterator] = async function* () {
|
|
713
|
+
for (const jwk2 of candidates)
|
|
714
|
+
try {
|
|
715
|
+
yield await importWithAlgCache(cached, jwk2, entry);
|
|
716
|
+
} catch {
|
|
717
|
+
}
|
|
718
|
+
}, error;
|
|
719
|
+
}
|
|
720
|
+
return importWithAlgCache(cached, jwk, entry);
|
|
721
|
+
}, "jwks", {
|
|
722
|
+
value: () => structuredClone(snapshot)
|
|
723
|
+
});
|
|
724
|
+
}
|
|
725
|
+
|
|
726
|
+
// node_modules/.pnpm/jose@6.2.12/node_modules/jose/dist/webapi/jwks/remote.js
|
|
727
|
+
function isCloudflareWorkers() {
|
|
728
|
+
return typeof WebSocketPair < "u" || typeof navigator < "u" && navigator.userAgent === "Cloudflare-Workers" || typeof EdgeRuntime < "u" && EdgeRuntime === "vercel";
|
|
729
|
+
}
|
|
730
|
+
var USER_AGENT;
|
|
731
|
+
(typeof navigator > "u" || !navigator.userAgent?.startsWith?.("Mozilla/5.0 ")) && (USER_AGENT = "jose/v6.2.12");
|
|
732
|
+
var customFetch = /* @__PURE__ */ Symbol();
|
|
733
|
+
async function fetchJwks(url, headers, signal, fetchImpl = fetch) {
|
|
734
|
+
const response = await fetchImpl(url, {
|
|
735
|
+
method: "GET",
|
|
736
|
+
signal,
|
|
737
|
+
redirect: "manual",
|
|
738
|
+
headers
|
|
739
|
+
}).catch((err) => {
|
|
740
|
+
throw err.name === "TimeoutError" ? new JWKSTimeout() : err;
|
|
741
|
+
});
|
|
742
|
+
if (response.status !== 200)
|
|
743
|
+
throw new JOSEError("Expected 200 OK from the JSON Web Key Set HTTP response");
|
|
744
|
+
try {
|
|
745
|
+
return await response.json();
|
|
746
|
+
} catch {
|
|
747
|
+
throw new JOSEError("Failed to parse the JSON Web Key Set HTTP response as JSON");
|
|
748
|
+
}
|
|
749
|
+
}
|
|
750
|
+
var jwksCache = /* @__PURE__ */ Symbol();
|
|
751
|
+
function isFreshFor(timestamp, duration) {
|
|
752
|
+
return Number.isFinite(timestamp) && Date.now() < timestamp + duration;
|
|
753
|
+
}
|
|
754
|
+
function validateDuration(value, fallback, option) {
|
|
755
|
+
if (Number.isNaN(value))
|
|
756
|
+
throw new TypeError(`"${option}" option must not be NaN`);
|
|
757
|
+
return typeof value == "number" ? value : fallback;
|
|
758
|
+
}
|
|
759
|
+
function createRemoteJWKSet(url, options) {
|
|
760
|
+
if (!(url instanceof URL))
|
|
761
|
+
throw new TypeError("url must be an instance of URL");
|
|
762
|
+
const href = new URL(url.href).href, opts = options ?? {}, timeoutOption = opts.timeoutDuration;
|
|
763
|
+
if (typeof timeoutOption == "number" && (!Number.isInteger(timeoutOption) || timeoutOption < 0))
|
|
764
|
+
throw new TypeError('"timeoutDuration" option must be a non-negative integer');
|
|
765
|
+
const timeoutDuration = typeof timeoutOption == "number" ? timeoutOption : 5e3, cooldownDuration = validateDuration(opts.cooldownDuration, 3e4, "cooldownDuration"), cacheMaxAge = validateDuration(opts.cacheMaxAge, 6e5, "cacheMaxAge"), headers = new Headers(opts.headers);
|
|
766
|
+
USER_AGENT && !headers.has("User-Agent") && headers.set("User-Agent", USER_AGENT), headers.has("accept") || headers.set("accept", "application/json, application/jwk-set+json");
|
|
767
|
+
const fetchImpl = opts[customFetch], cache2 = opts[jwksCache];
|
|
768
|
+
let jwksTimestamp, pendingFetch, reloadSequence = 0, appliedSequence = 0, local;
|
|
769
|
+
if (cache2 && typeof cache2 == "object") {
|
|
770
|
+
const { uat, jwks } = cache2;
|
|
771
|
+
isFreshFor(uat, cacheMaxAge) && isJwkSet(jwks) && (jwksTimestamp = uat, local = createLocalJWKSet(jwks));
|
|
772
|
+
}
|
|
773
|
+
const reload = async () => {
|
|
774
|
+
if (pendingFetch && isCloudflareWorkers() && (pendingFetch = void 0), !pendingFetch) {
|
|
775
|
+
const sequence = ++reloadSequence, current = pendingFetch = fetchJwks(href, headers, AbortSignal.timeout(timeoutDuration), fetchImpl).then((json) => {
|
|
776
|
+
const next = createLocalJWKSet(json);
|
|
777
|
+
if (sequence <= appliedSequence)
|
|
778
|
+
return;
|
|
779
|
+
local = next;
|
|
780
|
+
const updatedAt = Date.now();
|
|
781
|
+
cache2 && (cache2.uat = updatedAt, cache2.jwks = json), jwksTimestamp = updatedAt, appliedSequence = sequence;
|
|
782
|
+
}).finally(() => {
|
|
783
|
+
pendingFetch === current && (pendingFetch = void 0);
|
|
784
|
+
});
|
|
785
|
+
}
|
|
786
|
+
await pendingFetch;
|
|
787
|
+
};
|
|
788
|
+
return Object.defineProperties(async (protectedHeader, token) => {
|
|
789
|
+
(!local || !isFreshFor(jwksTimestamp, cacheMaxAge)) && await reload();
|
|
790
|
+
try {
|
|
791
|
+
return await local(protectedHeader, token);
|
|
792
|
+
} catch (err) {
|
|
793
|
+
if (err instanceof JWKSNoMatchingKey && !isFreshFor(jwksTimestamp, cooldownDuration))
|
|
794
|
+
return await reload(), local(protectedHeader, token);
|
|
795
|
+
throw err;
|
|
796
|
+
}
|
|
797
|
+
}, {
|
|
798
|
+
coolingDown: {
|
|
799
|
+
get: () => isFreshFor(jwksTimestamp, cooldownDuration),
|
|
800
|
+
enumerable: true
|
|
801
|
+
},
|
|
802
|
+
fresh: {
|
|
803
|
+
get: () => isFreshFor(jwksTimestamp, cacheMaxAge),
|
|
804
|
+
enumerable: true
|
|
805
|
+
},
|
|
806
|
+
reload: {
|
|
807
|
+
value: reload,
|
|
808
|
+
enumerable: true
|
|
809
|
+
},
|
|
810
|
+
reloading: {
|
|
811
|
+
get: () => !!pendingFetch,
|
|
812
|
+
enumerable: true
|
|
813
|
+
},
|
|
814
|
+
jwks: {
|
|
815
|
+
value: () => local?.jwks(),
|
|
816
|
+
enumerable: true
|
|
817
|
+
}
|
|
818
|
+
});
|
|
819
|
+
}
|
|
820
|
+
|
|
821
|
+
// packages/cli/src/tui/chatgpt-plan-auth.ts
|
|
822
|
+
var SIWC_ISSUER = "https://auth.openai.com";
|
|
823
|
+
var SIWC_RESOURCE = "https://api.openai.com/v1";
|
|
824
|
+
var SIWC_SCOPES = ["openid", "profile", "email", "offline_access", "resource.invoke", "chatgpt.tokens.use.direct"];
|
|
825
|
+
var PATH = "/auth/callback";
|
|
826
|
+
var object = (value) => value !== null && typeof value === "object" && !Array.isArray(value) ? value : {};
|
|
827
|
+
function credential(value) {
|
|
828
|
+
if (typeof value !== "string" || !value || value.length > 65536 || /[^\x21-\x7e]/.test(value)) throw new Error("The provider returned invalid authentication data.");
|
|
829
|
+
return value;
|
|
830
|
+
}
|
|
831
|
+
async function readSiwcJson(url, init, transport = {}) {
|
|
832
|
+
const signal = init.signal ? AbortSignal.any([init.signal, AbortSignal.timeout(3e4)]) : AbortSignal.timeout(3e4);
|
|
833
|
+
let response;
|
|
834
|
+
try {
|
|
835
|
+
response = await (transport.fetch ?? fetch)(url, { ...init, signal, redirect: "error" });
|
|
836
|
+
} catch {
|
|
837
|
+
throw new Error("ChatGPT sign-in could not reach its authentication service. Retry the connection.");
|
|
838
|
+
}
|
|
839
|
+
if (!response.ok) throw new Error(response.status === 400 || response.status === 401 ? "ChatGPT authentication was rejected. Sign in again." : `ChatGPT authentication is unavailable (HTTP ${response.status}).`);
|
|
840
|
+
const reader = response.body?.getReader();
|
|
841
|
+
if (!reader) throw new Error("ChatGPT returned an empty authentication response.");
|
|
842
|
+
let bytes = 0, raw = "";
|
|
843
|
+
const decoder2 = new TextDecoder("utf-8", { fatal: true });
|
|
844
|
+
try {
|
|
845
|
+
for (; ; ) {
|
|
846
|
+
const chunk = await reader.read();
|
|
847
|
+
if (chunk.done) break;
|
|
848
|
+
bytes += chunk.value.byteLength;
|
|
849
|
+
if (bytes > 1048576) throw new Error("ChatGPT authentication response exceeded its limit.");
|
|
850
|
+
raw += decoder2.decode(chunk.value, { stream: true });
|
|
851
|
+
}
|
|
852
|
+
raw += decoder2.decode();
|
|
853
|
+
try {
|
|
854
|
+
return JSON.parse(raw);
|
|
855
|
+
} catch {
|
|
856
|
+
throw new Error("ChatGPT returned an invalid authentication response.");
|
|
857
|
+
}
|
|
858
|
+
} finally {
|
|
859
|
+
await reader.cancel().catch(() => void 0);
|
|
860
|
+
reader.releaseLock();
|
|
861
|
+
}
|
|
862
|
+
}
|
|
863
|
+
function trustedEndpoint(value) {
|
|
864
|
+
if (typeof value !== "string") throw new Error("ChatGPT authentication configuration is incomplete.");
|
|
865
|
+
const url = new URL(value);
|
|
866
|
+
if (url.origin !== SIWC_ISSUER || url.username || url.password || url.hash || url.search) throw new Error("ChatGPT authentication configuration contains an untrusted endpoint.");
|
|
867
|
+
return url.href;
|
|
868
|
+
}
|
|
869
|
+
async function siwcMetadata(transport, signal) {
|
|
870
|
+
const data = object(await readSiwcJson(`${SIWC_ISSUER}/.well-known/openid-configuration`, { signal }, transport));
|
|
871
|
+
if (data.issuer !== SIWC_ISSUER) throw new Error("ChatGPT authentication issuer did not match.");
|
|
872
|
+
return { token: trustedEndpoint(data.token_endpoint), jwks: trustedEndpoint(data.jwks_uri), revoke: trustedEndpoint(data.revocation_endpoint) };
|
|
873
|
+
}
|
|
874
|
+
async function validateSiwcTokens(value, clientId, metadata, transport, signal, expected) {
|
|
875
|
+
const data = object(value), previous = expected?.previous;
|
|
876
|
+
const idToken = data.id_token === void 0 && previous ? previous.idToken : credential(data.id_token);
|
|
877
|
+
const now = transport.now?.() ?? Date.now();
|
|
878
|
+
let identity;
|
|
879
|
+
if (data.id_token === void 0 && previous && previous.clientId === clientId && previous.issuer === SIWC_ISSUER) {
|
|
880
|
+
identity = { sub: previous.subject, email: previous.email, iat: Math.floor(now / 1e3) };
|
|
881
|
+
} else {
|
|
882
|
+
const keys = object(await readSiwcJson(metadata.jwks, { signal }, transport));
|
|
883
|
+
if (!Array.isArray(keys.keys)) throw new Error("ChatGPT returned invalid signing keys.");
|
|
884
|
+
try {
|
|
885
|
+
identity = (await jwtVerify(idToken, createLocalJWKSet(keys), { issuer: SIWC_ISSUER, audience: clientId, algorithms: ["RS256", "ES256"], currentDate: new Date(now), requiredClaims: ["sub", "iat", "exp"] })).payload;
|
|
886
|
+
} catch {
|
|
887
|
+
throw new Error("ChatGPT identity verification failed. Sign in again.");
|
|
888
|
+
}
|
|
889
|
+
}
|
|
890
|
+
if (typeof identity.sub !== "string" || !identity.sub || typeof identity.iat !== "number" || identity.iat > now / 1e3 + 60 || expected?.nonce !== void 0 && identity.nonce !== expected.nonce || expected?.subject !== void 0 && identity.sub !== expected.subject || identity.azp !== void 0 && identity.azp !== clientId || Array.isArray(identity.aud) && identity.aud.length > 1 && identity.azp !== clientId) throw new Error("ChatGPT identity did not match this sign-in.");
|
|
891
|
+
const scopes = typeof data.scope === "string" ? [...new Set(data.scope.split(/\s+/).filter(Boolean))] : data.scope === void 0 ? expected?.scopes : void 0;
|
|
892
|
+
if (!scopes || !scopes.includes("openid")) throw new Error("ChatGPT did not return the granted identity permissions.");
|
|
893
|
+
const sharing = scopes.includes("chatgpt.tokens.use.direct");
|
|
894
|
+
const accessToken = data.access_token !== void 0 ? credential(data.access_token) : void 0;
|
|
895
|
+
const refreshToken = data.refresh_token !== void 0 ? credential(data.refresh_token) : previous?.refreshToken;
|
|
896
|
+
if (data.token_type !== "Bearer" || sharing && (!accessToken || !refreshToken || !scopes.includes("resource.invoke") || !scopes.includes("offline_access")) || typeof data.expires_in !== "number" || !Number.isFinite(data.expires_in) || data.expires_in <= 0 || data.expires_in > 86400) throw new Error("ChatGPT did not return a usable token grant.");
|
|
897
|
+
const earliest = data.earliest_refresh_at;
|
|
898
|
+
const earliestRefreshAt = typeof earliest === "number" ? earliest * 1e3 : typeof earliest === "string" ? Date.parse(earliest) : void 0;
|
|
899
|
+
if (earliestRefreshAt !== void 0 && !Number.isFinite(earliestRefreshAt)) throw new Error("ChatGPT returned an invalid refresh schedule.");
|
|
900
|
+
const email = typeof identity.email === "string" && identity.email.length <= 320 && !/[\x00-\x1f\x7f]/.test(identity.email) ? identity.email : void 0;
|
|
901
|
+
return { clientId, subject: identity.sub, issuer: SIWC_ISSUER, ...email ? { email } : {}, scopes, idToken, ...accessToken ? { accessToken } : {}, ...refreshToken ? { refreshToken } : {}, expiresAt: now + data.expires_in * 1e3, ...earliestRefreshAt !== void 0 ? { earliestRefreshAt } : {}, status: "connected" };
|
|
902
|
+
}
|
|
903
|
+
async function refreshSiwcProfile(profile, transport = {}, signal, checkpoint) {
|
|
904
|
+
if (!profile.refreshToken || profile.status !== "connected") throw new Error("Sign in with ChatGPT again before continuing.");
|
|
905
|
+
const metadata = await siwcMetadata(transport, signal);
|
|
906
|
+
const value = await readSiwcJson(metadata.token, { method: "POST", signal, headers: { "Content-Type": "application/x-www-form-urlencoded" }, body: new URLSearchParams({ grant_type: "refresh_token", client_id: profile.clientId, refresh_token: profile.refreshToken, resource: SIWC_RESOURCE }).toString() }, transport);
|
|
907
|
+
const body = object(value), receipt = {};
|
|
908
|
+
for (const key of ["access_token", "refresh_token", "id_token", "token_type", "expires_in", "scope", "earliest_refresh_at"]) {
|
|
909
|
+
const field = body[key];
|
|
910
|
+
if (field !== void 0) receipt[key] = typeof field === "string" && field.length <= 65536 || typeof field === "number" || typeof field === "boolean" || field === null ? field : null;
|
|
911
|
+
}
|
|
912
|
+
await checkpoint?.(receipt);
|
|
913
|
+
return { id: profile.id, ...await validateSiwcTokens(value, profile.clientId, metadata, transport, signal, { subject: profile.subject, scopes: profile.scopes, previous: profile }) };
|
|
914
|
+
}
|
|
915
|
+
async function loginSiwc(options) {
|
|
916
|
+
if (!/^urn:uuid:[a-f0-9]{8}-[a-f0-9]{4}-4[a-f0-9]{3}-[89ab][a-f0-9]{3}-[a-f0-9]{12}$/.test(options.hostId)) throw new Error("ChatGPT sign-in requires this installation's stable host identity.");
|
|
917
|
+
const abort = new AbortController();
|
|
918
|
+
const signal = options.signal ? AbortSignal.any([options.signal, abort.signal, AbortSignal.timeout(3e5)]) : AbortSignal.any([abort.signal, AbortSignal.timeout(3e5)]);
|
|
919
|
+
const state = randomBytes(32).toString("base64url"), nonce = randomBytes(32).toString("base64url"), verifier = randomBytes(64).toString("base64url");
|
|
920
|
+
let resolveCallback, rejectCallback;
|
|
921
|
+
const callback = new Promise((resolve4, reject) => {
|
|
922
|
+
resolveCallback = resolve4;
|
|
923
|
+
rejectCallback = reject;
|
|
924
|
+
});
|
|
925
|
+
void callback.catch(() => void 0);
|
|
926
|
+
let consumed = false, redirect = "";
|
|
927
|
+
const server = createServer((req, res) => {
|
|
928
|
+
let url;
|
|
929
|
+
try {
|
|
930
|
+
if (!redirect) throw new Error();
|
|
931
|
+
url = new URL(req.url ?? "/", redirect);
|
|
932
|
+
} catch {
|
|
933
|
+
res.writeHead(400);
|
|
934
|
+
res.end("Invalid callback.");
|
|
935
|
+
return;
|
|
936
|
+
}
|
|
937
|
+
if (consumed || req.method !== "GET" || req.headers.host !== new URL(redirect).host || url.origin !== new URL(redirect).origin || url.pathname !== PATH || url.searchParams.getAll("state").length !== 1 || url.searchParams.get("state") !== state) {
|
|
938
|
+
res.writeHead(400, { "Cache-Control": "no-store" });
|
|
939
|
+
res.end("This sign-in callback is not valid.");
|
|
940
|
+
return;
|
|
941
|
+
}
|
|
942
|
+
consumed = true;
|
|
943
|
+
res.writeHead(200, { "Cache-Control": "no-store", "Content-Type": "text/plain; charset=utf-8", "Content-Security-Policy": "default-src 'none'", "Referrer-Policy": "no-referrer" });
|
|
944
|
+
res.end("Return to 0 to finish connecting ChatGPT.");
|
|
945
|
+
resolveCallback(url.searchParams);
|
|
946
|
+
});
|
|
947
|
+
const stopped = () => rejectCallback(new Error("ChatGPT sign-in was cancelled or timed out."));
|
|
948
|
+
signal.addEventListener("abort", stopped, { once: true });
|
|
949
|
+
try {
|
|
950
|
+
signal.throwIfAborted();
|
|
951
|
+
await new Promise((resolve4, reject) => {
|
|
952
|
+
server.once("error", () => reject(new Error("The local sign-in callback could not start.")));
|
|
953
|
+
server.listen(0, "127.0.0.1", resolve4);
|
|
954
|
+
});
|
|
955
|
+
const address = server.address();
|
|
956
|
+
if (!address || typeof address === "string") throw new Error("The local sign-in callback could not start.");
|
|
957
|
+
redirect = `http://127.0.0.1:${address.port}${PATH}`;
|
|
958
|
+
const url = new URL(`${SIWC_ISSUER}/api/accounts/authorize`);
|
|
959
|
+
const previous = options.previous;
|
|
960
|
+
const parameters = { client_id: previous?.clientId ?? "dynamic_agent_client", ...previous ? {} : { agent_name_hint: "0" }, ext_agent_host_id: options.hostId, response_type: "code", redirect_uri: redirect, scope: SIWC_SCOPES.join(" "), resource: SIWC_RESOURCE, state, nonce, code_challenge_method: "S256", code_challenge: createHash("sha256").update(verifier).digest("base64url") };
|
|
961
|
+
for (const [key, value2] of Object.entries(parameters)) url.searchParams.set(key, value2);
|
|
962
|
+
void Promise.resolve().then(() => options.onAuthorize(url.href)).catch(() => rejectCallback(new Error("The browser sign-in could not be opened.")));
|
|
963
|
+
const params = await callback;
|
|
964
|
+
signal.throwIfAborted();
|
|
965
|
+
if (params.has("error")) throw new Error("ChatGPT sign-in was not authorized. No account was changed.");
|
|
966
|
+
const clientId = params.has("client_id") ? credential(params.get("client_id")) : previous?.clientId;
|
|
967
|
+
if (!clientId || clientId === "dynamic_agent_client" || params.getAll("client_id").length > 1 || previous && clientId !== previous.clientId || params.getAll("code").length !== 1) throw new Error("ChatGPT registration did not match this sign-in.");
|
|
968
|
+
const code = credential(params.get("code")), metadata = await siwcMetadata(options, signal);
|
|
969
|
+
const value = await readSiwcJson(metadata.token, { method: "POST", signal, headers: { "Content-Type": "application/x-www-form-urlencoded" }, body: new URLSearchParams({ grant_type: "authorization_code", client_id: clientId, code, code_verifier: verifier, redirect_uri: redirect, resource: SIWC_RESOURCE }).toString() }, options);
|
|
970
|
+
return await validateSiwcTokens(value, clientId, metadata, options, signal, { nonce, ...previous ? { subject: previous.subject } : {} });
|
|
971
|
+
} finally {
|
|
972
|
+
abort.abort();
|
|
973
|
+
signal.removeEventListener("abort", stopped);
|
|
974
|
+
server.closeAllConnections();
|
|
975
|
+
await new Promise((resolve4) => {
|
|
976
|
+
if (!server.listening) resolve4();
|
|
977
|
+
else server.close(() => resolve4());
|
|
978
|
+
});
|
|
979
|
+
}
|
|
980
|
+
}
|
|
981
|
+
|
|
982
|
+
// packages/cli/src/tui/chatgpt-plan-store.ts
|
|
983
|
+
function paths(homeDir) {
|
|
984
|
+
const directory = join(homeStateDir(homeDir), "chatgpt-plan");
|
|
985
|
+
return { directory, file: join(directory, "accounts.json"), lock: join(directory, "write.lock") };
|
|
986
|
+
}
|
|
987
|
+
function checkPrivate(path, directory) {
|
|
988
|
+
const stat3 = lstatSync(path);
|
|
989
|
+
if (stat3.isSymbolicLink() || (directory ? !stat3.isDirectory() : !stat3.isFile()) || process.platform !== "win32" && ((stat3.mode & 63) !== 0 || process.getuid && stat3.uid !== process.getuid())) throw new Error("ChatGPT connection storage must be private and owned by this user.");
|
|
990
|
+
}
|
|
991
|
+
function read(homeDir) {
|
|
992
|
+
const { directory, file } = paths(homeDir);
|
|
993
|
+
if (!existsSync(directory)) return void 0;
|
|
994
|
+
checkPrivate(directory, true);
|
|
995
|
+
if (!existsSync(file)) return void 0;
|
|
996
|
+
checkPrivate(file, false);
|
|
997
|
+
if (lstatSync(file).size > 4194304) throw new Error("ChatGPT connection storage exceeded its limit.");
|
|
998
|
+
let data;
|
|
999
|
+
try {
|
|
1000
|
+
data = JSON.parse(readFileSync(file, "utf8"));
|
|
1001
|
+
} catch {
|
|
1002
|
+
throw new Error("ChatGPT connection storage is invalid.");
|
|
1003
|
+
}
|
|
1004
|
+
if (data.version !== 1 || !Number.isSafeInteger(data.revision) || data.revision < 0 || !/^urn:uuid:[a-f0-9]{8}-[a-f0-9]{4}-4[a-f0-9]{3}-[89ab][a-f0-9]{3}-[a-f0-9]{12}$/.test(data.hostId) || !Array.isArray(data.profiles) || data.profiles.length > 100) throw new Error("ChatGPT connection storage is invalid.");
|
|
1005
|
+
const ids = /* @__PURE__ */ new Set();
|
|
1006
|
+
for (const p of data.profiles) {
|
|
1007
|
+
if (!p || typeof p.id !== "string" || ids.has(p.id) || typeof p.subject !== "string" || !p.subject || p.issuer !== SIWC_ISSUER || !["connected", "signed-out", "reauth-required"].includes(p.status) || !Array.isArray(p.scopes) || !p.scopes.every((scope) => typeof scope === "string") || typeof p.expiresAt !== "number" || !Number.isFinite(p.expiresAt) || typeof p.idToken !== "string") throw new Error("ChatGPT connection storage is invalid.");
|
|
1008
|
+
if (p.epoch !== void 0 && (!Number.isSafeInteger(p.epoch) || p.epoch < 0)) throw new Error("ChatGPT connection generation is invalid.");
|
|
1009
|
+
credential(p.clientId);
|
|
1010
|
+
if (p.clientId === "dynamic_agent_client") throw new Error("ChatGPT connection storage has no issued registration.");
|
|
1011
|
+
if (p.accessToken !== void 0) credential(p.accessToken);
|
|
1012
|
+
if (p.refreshToken !== void 0) credential(p.refreshToken);
|
|
1013
|
+
ids.add(p.id);
|
|
1014
|
+
}
|
|
1015
|
+
if (data.activeId !== void 0 && !ids.has(data.activeId)) throw new Error("ChatGPT connection selection is invalid.");
|
|
1016
|
+
return data;
|
|
1017
|
+
}
|
|
1018
|
+
function write(store, homeDir) {
|
|
1019
|
+
const { directory, file } = paths(homeDir);
|
|
1020
|
+
checkPrivate(directory, true);
|
|
1021
|
+
const disk = read(homeDir);
|
|
1022
|
+
if ((disk?.revision ?? 0) !== store.revision) throw new Error("ChatGPT connection storage changed. Retry the operation.");
|
|
1023
|
+
const next = { ...store, revision: store.revision + 1 }, temp = join(directory, `${randomUUID()}.tmp`);
|
|
1024
|
+
const bytes = JSON.stringify(next);
|
|
1025
|
+
if (Buffer.byteLength(bytes) > 4194304) throw new Error("ChatGPT connection storage exceeded its limit.");
|
|
1026
|
+
const fd = openSync(temp, "wx", 384);
|
|
1027
|
+
try {
|
|
1028
|
+
writeFileSync(fd, bytes);
|
|
1029
|
+
fsyncSync(fd);
|
|
1030
|
+
} finally {
|
|
1031
|
+
closeSync(fd);
|
|
1032
|
+
}
|
|
1033
|
+
try {
|
|
1034
|
+
renameSync(temp, file);
|
|
1035
|
+
store.revision = next.revision;
|
|
1036
|
+
if (process.platform !== "win32") {
|
|
1037
|
+
const parent = openSync(directory, "r");
|
|
1038
|
+
try {
|
|
1039
|
+
fsyncSync(parent);
|
|
1040
|
+
} finally {
|
|
1041
|
+
closeSync(parent);
|
|
1042
|
+
}
|
|
1043
|
+
}
|
|
1044
|
+
} finally {
|
|
1045
|
+
if (existsSync(temp)) unlinkSync(temp);
|
|
1046
|
+
}
|
|
1047
|
+
}
|
|
1048
|
+
async function locked(homeDir, operation) {
|
|
1049
|
+
const { directory, lock } = paths(homeDir);
|
|
1050
|
+
mkdirSync(directory, { recursive: true, mode: 448 });
|
|
1051
|
+
checkPrivate(directory, true);
|
|
1052
|
+
let fd;
|
|
1053
|
+
const deadline = Date.now() + 5e3;
|
|
1054
|
+
for (; ; ) {
|
|
1055
|
+
try {
|
|
1056
|
+
fd = openSync(lock, "wx", 384);
|
|
1057
|
+
writeFileSync(fd, String(process.pid));
|
|
1058
|
+
break;
|
|
1059
|
+
} catch (error) {
|
|
1060
|
+
if (error.code !== "EEXIST" || Date.now() >= deadline) throw new Error("ChatGPT connection storage is busy. Close other connection operations and retry; an interrupted operation may require removing its stale lock.");
|
|
1061
|
+
await new Promise((resolve4) => setTimeout(resolve4, 25));
|
|
1062
|
+
}
|
|
1063
|
+
}
|
|
1064
|
+
try {
|
|
1065
|
+
return await operation(read(homeDir) ?? { version: 1, revision: 0, hostId: `urn:uuid:${randomUUID()}`, profiles: [] });
|
|
1066
|
+
} finally {
|
|
1067
|
+
closeSync(fd);
|
|
1068
|
+
unlinkSync(lock);
|
|
1069
|
+
}
|
|
1070
|
+
}
|
|
1071
|
+
function listChatGptPlanProfiles(homeDir) {
|
|
1072
|
+
const store = read(homeDir);
|
|
1073
|
+
return store?.profiles.map((p, index) => ({ id: p.id, label: `${p.email ?? "ChatGPT account"} \xB7 Connection ${index + 1}`, active: store.activeId === p.id, status: p.status, sharing: p.status === "connected" && p.scopes.includes("chatgpt.tokens.use.direct") && p.scopes.includes("resource.invoke") })) ?? [];
|
|
1074
|
+
}
|
|
1075
|
+
async function signInChatGptPlan(options) {
|
|
1076
|
+
const prepared = await locked(options.homeDir, async (store) => {
|
|
1077
|
+
const previous = options.profileId ? store.profiles.find((p) => p.id === options.profileId) : void 0;
|
|
1078
|
+
if (options.profileId && !previous) throw new Error("The selected ChatGPT connection no longer exists.");
|
|
1079
|
+
if (!previous && store.profiles.length >= 100) throw new Error("This host already has 100 ChatGPT registrations. Reconnect an existing connection.");
|
|
1080
|
+
write(store, options.homeDir);
|
|
1081
|
+
return { hostId: store.hostId, previous };
|
|
1082
|
+
});
|
|
1083
|
+
const result = await loginSiwc({ ...options, ...prepared });
|
|
1084
|
+
options.signal?.throwIfAborted();
|
|
1085
|
+
await locked(options.homeDir, async (store) => {
|
|
1086
|
+
options.signal?.throwIfAborted();
|
|
1087
|
+
const existing = options.profileId ? store.profiles.find((p) => p.id === options.profileId) : void 0;
|
|
1088
|
+
if (options.profileId && (!existing || existing.clientId !== result.clientId || existing.subject !== result.subject || (existing.epoch ?? 0) !== (prepared.previous?.epoch ?? 0))) throw new Error("The selected ChatGPT registration changed during sign-in.");
|
|
1089
|
+
const profile = { id: existing?.id ?? randomUUID(), ...result, epoch: (existing?.epoch ?? 0) + 1 };
|
|
1090
|
+
store.profiles = [...store.profiles.filter((p) => p.id !== profile.id), profile];
|
|
1091
|
+
store.activeId = profile.id;
|
|
1092
|
+
write(store, options.homeDir);
|
|
1093
|
+
});
|
|
1094
|
+
}
|
|
1095
|
+
async function selectChatGptPlanProfile(profileId, homeDir) {
|
|
1096
|
+
await locked(homeDir, async (store) => {
|
|
1097
|
+
if (!store.profiles.some((p) => p.id === profileId && p.status === "connected")) throw new Error("Sign in to this ChatGPT connection before selecting it.");
|
|
1098
|
+
store.activeId = profileId;
|
|
1099
|
+
write(store, homeDir);
|
|
1100
|
+
});
|
|
1101
|
+
}
|
|
1102
|
+
function getSiwcConnection(homeDir, profileId, transport = {}) {
|
|
1103
|
+
const current = read(homeDir), selected = profileId ?? current?.activeId, profile = current?.profiles.find((p) => p.id === selected);
|
|
1104
|
+
if (!current || !profile || profile.status !== "connected") throw new Error("Connect your ChatGPT plan in Connections before choosing this provider.");
|
|
1105
|
+
const binding = { accountId: profile.subject, clientId: profile.clientId, hostId: current.hostId };
|
|
1106
|
+
return { ...binding, resolveCredentials: async (signal) => {
|
|
1107
|
+
signal?.throwIfAborted();
|
|
1108
|
+
const result = await locked(homeDir, async (store) => {
|
|
1109
|
+
signal?.throwIfAborted();
|
|
1110
|
+
let p = store.profiles.find((p2) => p2.id === selected);
|
|
1111
|
+
if (!p || p.status !== "connected" || p.subject !== binding.accountId || p.clientId !== binding.clientId || store.hostId !== binding.hostId) throw new Error("This ChatGPT registration is no longer available. Reconnect it.");
|
|
1112
|
+
if (p.pendingRotation !== void 0) {
|
|
1113
|
+
const next = await validateSiwcTokens(p.pendingRotation, p.clientId, await siwcMetadata(transport), transport, void 0, { subject: p.subject, scopes: p.scopes, previous: p });
|
|
1114
|
+
p = { id: p.id, ...next, epoch: p.epoch };
|
|
1115
|
+
store.profiles = store.profiles.map((row) => row.id === p.id ? p : row);
|
|
1116
|
+
write(store, homeDir);
|
|
1117
|
+
}
|
|
1118
|
+
if (!p.scopes.includes("chatgpt.tokens.use.direct") || !p.scopes.includes("resource.invoke")) throw new Error("ChatGPT plan usage is not enabled for this connection. Sign in and grant plan usage, or use an API key.");
|
|
1119
|
+
signal?.throwIfAborted();
|
|
1120
|
+
const now = transport.now?.() ?? Date.now();
|
|
1121
|
+
if (p.expiresAt <= now + 6e4 && (p.earliestRefreshAt === void 0 || p.earliestRefreshAt <= now)) {
|
|
1122
|
+
if (Buffer.byteLength(JSON.stringify(store)) + 1048576 > 4194304) throw new Error("ChatGPT connection storage is full. Sign out of unused connections before refreshing.");
|
|
1123
|
+
const next = await refreshSiwcProfile(p, transport, void 0, async (value) => {
|
|
1124
|
+
p.pendingRotation = value;
|
|
1125
|
+
write(store, homeDir);
|
|
1126
|
+
});
|
|
1127
|
+
p = { ...next, epoch: p.epoch };
|
|
1128
|
+
store.profiles = store.profiles.map((row) => row.id === p.id ? p : row);
|
|
1129
|
+
write(store, homeDir);
|
|
1130
|
+
}
|
|
1131
|
+
if (p.expiresAt <= now || !p.accessToken || !p.scopes.includes("chatgpt.tokens.use.direct") || !p.scopes.includes("resource.invoke")) throw new Error("ChatGPT plan usage is not enabled for this connection. Sign in and grant plan usage, or use an API key.");
|
|
1132
|
+
return { accessToken: p.accessToken, scopes: [...p.scopes] };
|
|
1133
|
+
});
|
|
1134
|
+
signal?.throwIfAborted();
|
|
1135
|
+
return result;
|
|
1136
|
+
} };
|
|
1137
|
+
}
|
|
1138
|
+
async function signOutChatGptPlan(profileId, homeDir, transport = {}) {
|
|
1139
|
+
return locked(homeDir, async (store) => {
|
|
1140
|
+
const p = store.profiles.find((row) => row.id === profileId);
|
|
1141
|
+
if (!p) throw new Error("ChatGPT connection not found.");
|
|
1142
|
+
const pending = p.pendingRotation && typeof p.pendingRotation === "object" ? p.pendingRotation : void 0;
|
|
1143
|
+
let refreshToken = p.refreshToken, unknownRotation = false;
|
|
1144
|
+
if (pending?.refresh_token !== void 0) {
|
|
1145
|
+
try {
|
|
1146
|
+
refreshToken = credential(pending.refresh_token);
|
|
1147
|
+
} catch {
|
|
1148
|
+
unknownRotation = true;
|
|
1149
|
+
}
|
|
1150
|
+
}
|
|
1151
|
+
let revoked = !refreshToken && !unknownRotation;
|
|
1152
|
+
if (refreshToken) {
|
|
1153
|
+
try {
|
|
1154
|
+
const metadata2 = await siwcMetadata(transport);
|
|
1155
|
+
const response = await (transport.fetch ?? fetch)(metadata2.revoke, { method: "POST", redirect: "error", signal: AbortSignal.timeout(15e3), headers: { "Content-Type": "application/x-www-form-urlencoded" }, body: new URLSearchParams({ token: refreshToken, token_type_hint: "refresh_token", client_id: p.clientId }).toString() });
|
|
1156
|
+
revoked = response.ok && !unknownRotation;
|
|
1157
|
+
} catch {
|
|
1158
|
+
}
|
|
1159
|
+
}
|
|
1160
|
+
const { accessToken: _access, refreshToken: _refresh, pendingRotation: _pending, ...metadata } = p;
|
|
1161
|
+
store.profiles = store.profiles.map((row) => row.id === p.id ? { ...metadata, epoch: (p.epoch ?? 0) + 1, idToken: "", scopes: [], expiresAt: 0, status: "signed-out" } : row);
|
|
1162
|
+
write(store, homeDir);
|
|
1163
|
+
return { revoked };
|
|
1164
|
+
});
|
|
1165
|
+
}
|
|
95
1166
|
|
|
96
1167
|
// packages/cli/src/tui/model-preference.ts
|
|
1168
|
+
async function createSelectedRuntime(config, allowChatGptPlan = true) {
|
|
1169
|
+
if (config.provider !== "chatgpt-plan") return createConsoleRuntime(config);
|
|
1170
|
+
if (!allowChatGptPlan) throw new Error("Native ChatGPT plan sign-in is available only in a user-controlled local open-source runtime. Use an API key for hosted or shared workspaces.");
|
|
1171
|
+
const connection = config.siwcConnection ?? getSiwcConnection(config.env?.HOME);
|
|
1172
|
+
if (config.model) return createConsoleRuntime({ ...config, siwcConnection: connection });
|
|
1173
|
+
const probe = createConsoleRuntime({ ...config, model: "probe", siwcConnection: connection });
|
|
1174
|
+
const catalog = await probe.availableModelCatalog(AbortSignal.timeout(1e4));
|
|
1175
|
+
if (!catalog.length) throw new Error("This ChatGPT connection returned no models. Check plan sharing in ChatGPT Settings.");
|
|
1176
|
+
probe.reconfigure({ model: catalog[0].id });
|
|
1177
|
+
return probe;
|
|
1178
|
+
}
|
|
97
1179
|
async function createPreferredConsoleRuntime(config, options = {}) {
|
|
98
1180
|
const env = config.env ?? process.env;
|
|
99
1181
|
const envModel = env["ZERO_MODEL"]?.trim();
|
|
@@ -105,7 +1187,7 @@ async function createPreferredConsoleRuntime(config, options = {}) {
|
|
|
105
1187
|
let runtime;
|
|
106
1188
|
let retainedModel = false;
|
|
107
1189
|
if (inherited && !envModel && !envProvider) {
|
|
108
|
-
runtime =
|
|
1190
|
+
runtime = await createSelectedRuntime({ ...config, model: void 0 }, options.allowChatGptPlan);
|
|
109
1191
|
if (runtime.connectionIdentity() === options.inheritedConnectionIdentity) {
|
|
110
1192
|
runtime.reconfigure({ model: config.model });
|
|
111
1193
|
retainedModel = config.model !== void 0;
|
|
@@ -115,7 +1197,7 @@ async function createPreferredConsoleRuntime(config, options = {}) {
|
|
|
115
1197
|
const saved = getSettings().modelPreference;
|
|
116
1198
|
if (saved && (config.provider === void 0 || config.provider === saved.providerId)) {
|
|
117
1199
|
try {
|
|
118
|
-
const candidate =
|
|
1200
|
+
const candidate = await createSelectedRuntime({ ...config, model: saved.model, provider: saved.providerId }, options.allowChatGptPlan);
|
|
119
1201
|
if (candidate.connectionIdentity() === saved.connectionIdentity) {
|
|
120
1202
|
runtime = candidate;
|
|
121
1203
|
retainedModel = true;
|
|
@@ -124,7 +1206,7 @@ async function createPreferredConsoleRuntime(config, options = {}) {
|
|
|
124
1206
|
}
|
|
125
1207
|
}
|
|
126
1208
|
}
|
|
127
|
-
runtime ??=
|
|
1209
|
+
runtime ??= await createSelectedRuntime(config, options.allowChatGptPlan);
|
|
128
1210
|
if (!explicitChoice && runtime.getConfigurationDiagnostics().provider === "chatgpt-codex") {
|
|
129
1211
|
let catalog;
|
|
130
1212
|
try {
|
|
@@ -156,8 +1238,8 @@ function saveAppliedModelPreference(runtime) {
|
|
|
156
1238
|
}
|
|
157
1239
|
|
|
158
1240
|
// packages/cli/src/tui/model-catalog-sync.ts
|
|
159
|
-
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
160
|
-
import { dirname, join } from "node:path";
|
|
1241
|
+
import { existsSync as existsSync2, mkdirSync as mkdirSync2, readFileSync as readFileSync2, writeFileSync as writeFileSync2 } from "node:fs";
|
|
1242
|
+
import { dirname, join as join2 } from "node:path";
|
|
161
1243
|
|
|
162
1244
|
// packages/cli/src/tui/model-catalog.offline.ts
|
|
163
1245
|
var OFFLINE_MODEL_CATALOG = [
|
|
@@ -257,7 +1339,7 @@ function normalizedModel(value) {
|
|
|
257
1339
|
return model;
|
|
258
1340
|
}
|
|
259
1341
|
function catalogCachePath(opts = {}) {
|
|
260
|
-
return opts.cachePath ??
|
|
1342
|
+
return opts.cachePath ?? join2(homeStateDir(), CATALOG_CACHE_FILENAME);
|
|
261
1343
|
}
|
|
262
1344
|
function normalizeModelsDev(raw) {
|
|
263
1345
|
if (typeof raw !== "object" || raw === null || Array.isArray(raw)) return [];
|
|
@@ -286,38 +1368,38 @@ function normalizeModelsDev(raw) {
|
|
|
286
1368
|
}
|
|
287
1369
|
function readCache(path) {
|
|
288
1370
|
try {
|
|
289
|
-
if (!
|
|
290
|
-
const parsed = JSON.parse(
|
|
1371
|
+
if (!existsSync2(path)) return null;
|
|
1372
|
+
const parsed = JSON.parse(readFileSync2(path, "utf8"));
|
|
291
1373
|
if (typeof parsed !== "object" || parsed === null || !Array.isArray(parsed.models) || !Number.isFinite(parsed.fetchedAt) || parsed.fetchedAt < 0 || typeof parsed.source !== "string") {
|
|
292
1374
|
return null;
|
|
293
1375
|
}
|
|
294
|
-
const
|
|
295
|
-
const models =
|
|
1376
|
+
const cache2 = parsed;
|
|
1377
|
+
const models = cache2.models.flatMap((row) => {
|
|
296
1378
|
const model = normalizedModel(row);
|
|
297
1379
|
return model ? [model] : [];
|
|
298
1380
|
});
|
|
299
|
-
return { fetchedAt:
|
|
1381
|
+
return { fetchedAt: cache2.fetchedAt, source: cache2.source, models };
|
|
300
1382
|
} catch {
|
|
301
1383
|
return null;
|
|
302
1384
|
}
|
|
303
1385
|
}
|
|
304
|
-
function writeCache(path,
|
|
1386
|
+
function writeCache(path, cache2) {
|
|
305
1387
|
try {
|
|
306
|
-
|
|
307
|
-
|
|
1388
|
+
mkdirSync2(dirname(path), { recursive: true });
|
|
1389
|
+
writeFileSync2(path, JSON.stringify(cache2, null, 2), { mode: 384 });
|
|
308
1390
|
} catch {
|
|
309
1391
|
}
|
|
310
1392
|
}
|
|
311
|
-
function isCacheFresh(
|
|
312
|
-
if (!
|
|
1393
|
+
function isCacheFresh(cache2, opts = {}) {
|
|
1394
|
+
if (!cache2) return false;
|
|
313
1395
|
const now = (opts.now ?? Date.now)();
|
|
314
1396
|
const ttl = opts.ttlMs ?? CATALOG_TTL_MS;
|
|
315
|
-
const age = now -
|
|
1397
|
+
const age = now - cache2.fetchedAt;
|
|
316
1398
|
return Number.isFinite(age) && age >= 0 && age < ttl;
|
|
317
1399
|
}
|
|
318
1400
|
function loadCatalogModels(opts = {}) {
|
|
319
|
-
const
|
|
320
|
-
if (
|
|
1401
|
+
const cache2 = readCache(catalogCachePath(opts));
|
|
1402
|
+
if (cache2 && cache2.models.length > 0) return cache2;
|
|
321
1403
|
return { fetchedAt: 0, source: "offline", models: OFFLINE_MODEL_CATALOG };
|
|
322
1404
|
}
|
|
323
1405
|
async function syncModelCatalog(opts = {}) {
|
|
@@ -337,13 +1419,13 @@ async function syncModelCatalog(opts = {}) {
|
|
|
337
1419
|
const json = await res.json();
|
|
338
1420
|
const models = normalizeModelsDev(json);
|
|
339
1421
|
if (models.length === 0) return null;
|
|
340
|
-
const
|
|
1422
|
+
const cache2 = {
|
|
341
1423
|
fetchedAt: (opts.now ?? Date.now)(),
|
|
342
1424
|
source: url,
|
|
343
1425
|
models
|
|
344
1426
|
};
|
|
345
|
-
writeCache(path,
|
|
346
|
-
return
|
|
1427
|
+
writeCache(path, cache2);
|
|
1428
|
+
return cache2;
|
|
347
1429
|
} catch {
|
|
348
1430
|
return null;
|
|
349
1431
|
} finally {
|
|
@@ -416,24 +1498,51 @@ function buildFullModelCatalog(currentModel, opts = {}) {
|
|
|
416
1498
|
}
|
|
417
1499
|
|
|
418
1500
|
// packages/cli/src/tui/available-models.ts
|
|
1501
|
+
var DISCOVERY_TIMEOUT_MS = 1e4;
|
|
1502
|
+
async function boundedCatalog(operation, callerSignal) {
|
|
1503
|
+
const deadline = new AbortController();
|
|
1504
|
+
const signal = callerSignal ? AbortSignal.any([callerSignal, deadline.signal]) : deadline.signal;
|
|
1505
|
+
const timer = setTimeout(() => deadline.abort(), DISCOVERY_TIMEOUT_MS);
|
|
1506
|
+
let onAbort;
|
|
1507
|
+
try {
|
|
1508
|
+
return await new Promise((resolve4, reject) => {
|
|
1509
|
+
onAbort = () => reject(new Error(deadline.signal.aborted ? "Model discovery timed out. Retry this connection." : "Model discovery cancelled."));
|
|
1510
|
+
signal.addEventListener("abort", onAbort, { once: true });
|
|
1511
|
+
if (signal.aborted) {
|
|
1512
|
+
onAbort();
|
|
1513
|
+
return;
|
|
1514
|
+
}
|
|
1515
|
+
Promise.resolve().then(() => {
|
|
1516
|
+
signal.throwIfAborted();
|
|
1517
|
+
return operation(signal);
|
|
1518
|
+
}).then(resolve4, reject);
|
|
1519
|
+
});
|
|
1520
|
+
} finally {
|
|
1521
|
+
clearTimeout(timer);
|
|
1522
|
+
if (onAbort) signal.removeEventListener("abort", onAbort);
|
|
1523
|
+
}
|
|
1524
|
+
}
|
|
419
1525
|
async function discoverConnectionModels(env, providerIds, signal) {
|
|
420
1526
|
const results = await Promise.all(providerIds.map(async (providerId) => {
|
|
421
1527
|
try {
|
|
422
|
-
const
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
1528
|
+
const models = await boundedCatalog(async (discoverySignal) => {
|
|
1529
|
+
const runtime = new LlmApiRuntime({
|
|
1530
|
+
type: "api",
|
|
1531
|
+
timeout: DISCOVERY_TIMEOUT_MS,
|
|
1532
|
+
provider: providerId,
|
|
1533
|
+
model: "probe",
|
|
1534
|
+
...providerId === "chatgpt-plan" ? { siwcConnection: getSiwcConnection(env.HOME) } : {},
|
|
1535
|
+
env: { ...env, ZERO_SKIP_PROVIDER_BANNER: "1" }
|
|
1536
|
+
});
|
|
1537
|
+
return runtime.availableModelCatalog(discoverySignal);
|
|
1538
|
+
}, signal);
|
|
430
1539
|
return { models: models.map((model) => ({ ...model, provider: providerId })), diagnostics: [] };
|
|
431
1540
|
} catch (error) {
|
|
432
|
-
let
|
|
1541
|
+
let message2 = error instanceof Error ? error.message : "Model discovery failed.";
|
|
433
1542
|
for (const [name, value] of Object.entries(env)) {
|
|
434
|
-
if (value && /KEY|TOKEN|SECRET|PASSWORD/i.test(name))
|
|
1543
|
+
if (value && /KEY|TOKEN|SECRET|PASSWORD/i.test(name)) message2 = message2.split(value).join("[redacted]");
|
|
435
1544
|
}
|
|
436
|
-
return { models: [], diagnostics: [{ providerId, message:
|
|
1545
|
+
return { models: [], diagnostics: [{ providerId, message: message2.slice(0, 1e3) }] };
|
|
437
1546
|
}
|
|
438
1547
|
}));
|
|
439
1548
|
return { models: results.flatMap((result) => result.models), diagnostics: results.flatMap((result) => result.diagnostics) };
|
|
@@ -479,28 +1588,28 @@ function resolveContextLimit(identity, opts = {}) {
|
|
|
479
1588
|
return family === null ? null : { tokens: family, source: "known-family" };
|
|
480
1589
|
};
|
|
481
1590
|
const load = opts.loadModels ?? loadCatalogModels;
|
|
482
|
-
const
|
|
483
|
-
const row =
|
|
1591
|
+
const cache2 = load(opts.sync ?? {});
|
|
1592
|
+
const row = cache2.models.find((model) => sameByokModel(model, identity));
|
|
484
1593
|
const tokens = row ? positiveTokens(row.contextTokens) : null;
|
|
485
1594
|
if (tokens !== null) {
|
|
486
|
-
return { tokens, source:
|
|
1595
|
+
return { tokens, source: cache2.source === "offline" ? "offline-catalog" : "synced-catalog" };
|
|
487
1596
|
}
|
|
488
1597
|
return familyFallback();
|
|
489
1598
|
}
|
|
490
1599
|
|
|
491
1600
|
// packages/cli/src/tui/device-auth.ts
|
|
492
|
-
import { createHash, randomBytes, randomUUID } from "node:crypto";
|
|
493
|
-
import { mkdirSync as
|
|
494
|
-
import { createServer } from "node:http";
|
|
1601
|
+
import { createHash as createHash2, randomBytes as randomBytes2, randomUUID as randomUUID2 } from "node:crypto";
|
|
1602
|
+
import { mkdirSync as mkdirSync3, readFileSync as readFileSync3, writeFileSync as writeFileSync3 } from "node:fs";
|
|
1603
|
+
import { createServer as createServer2 } from "node:http";
|
|
495
1604
|
import { arch, hostname, platform as platform2, release, version as osVersion } from "node:os";
|
|
496
|
-
import { join as
|
|
1605
|
+
import { join as join3 } from "node:path";
|
|
497
1606
|
|
|
498
1607
|
// packages/cli/src/open-browser.ts
|
|
499
1608
|
import { spawn } from "node:child_process";
|
|
500
1609
|
import { platform } from "node:os";
|
|
501
1610
|
function defaultOpenBrowser(url) {
|
|
502
1611
|
const plat = platform();
|
|
503
|
-
return new Promise((
|
|
1612
|
+
return new Promise((resolve4, reject) => {
|
|
504
1613
|
const options = { detached: true, stdio: "ignore", shell: false };
|
|
505
1614
|
const child = plat === "darwin" ? spawn("open", [url], options) : plat === "win32" ? spawn("powershell.exe", [
|
|
506
1615
|
"-NoProfile",
|
|
@@ -511,7 +1620,7 @@ function defaultOpenBrowser(url) {
|
|
|
511
1620
|
child.once("error", reject);
|
|
512
1621
|
child.once("spawn", () => {
|
|
513
1622
|
child.unref();
|
|
514
|
-
|
|
1623
|
+
resolve4();
|
|
515
1624
|
});
|
|
516
1625
|
});
|
|
517
1626
|
}
|
|
@@ -546,17 +1655,17 @@ var DEFAULT_INTERVAL_SECONDS = 5;
|
|
|
546
1655
|
var SLOW_DOWN_INCREMENT_SECONDS = 5;
|
|
547
1656
|
var DEFAULT_EXPIRES_IN_SECONDS = 900;
|
|
548
1657
|
var defaultFetch = (url, init) => globalThis.fetch(url, init);
|
|
549
|
-
var defaultSleep = (ms) => new Promise((
|
|
1658
|
+
var defaultSleep = (ms) => new Promise((resolve4) => setTimeout(resolve4, ms));
|
|
550
1659
|
var PKCE_TIMEOUT_SECONDS = 300;
|
|
551
1660
|
var base64url = (buffer) => buffer.toString("base64url");
|
|
552
1661
|
var defaultCreatePkcePair = () => {
|
|
553
|
-
const verifier = base64url(
|
|
554
|
-
const challenge = base64url(
|
|
1662
|
+
const verifier = base64url(randomBytes2(32));
|
|
1663
|
+
const challenge = base64url(createHash2("sha256").update(verifier).digest());
|
|
555
1664
|
return { verifier, challenge };
|
|
556
1665
|
};
|
|
557
1666
|
var defaultServerFactory = async ({ onRedirect }) => {
|
|
558
1667
|
let delivered = false;
|
|
559
|
-
const server =
|
|
1668
|
+
const server = createServer2((req, res) => {
|
|
560
1669
|
const requestUrl = new URL(req.url ?? "/", "http://127.0.0.1");
|
|
561
1670
|
if (requestUrl.pathname !== "/callback") {
|
|
562
1671
|
res.statusCode = 404;
|
|
@@ -572,9 +1681,9 @@ var defaultServerFactory = async ({ onRedirect }) => {
|
|
|
572
1681
|
delivered = true;
|
|
573
1682
|
onRedirect({ code, error });
|
|
574
1683
|
});
|
|
575
|
-
await new Promise((
|
|
1684
|
+
await new Promise((resolve4, reject) => {
|
|
576
1685
|
server.once("error", reject);
|
|
577
|
-
server.listen(0, "127.0.0.1", () =>
|
|
1686
|
+
server.listen(0, "127.0.0.1", () => resolve4());
|
|
578
1687
|
});
|
|
579
1688
|
const address = server.address();
|
|
580
1689
|
const port = typeof address === "object" && address !== null ? address.port : 0;
|
|
@@ -591,7 +1700,7 @@ function isPlainObject(value) {
|
|
|
591
1700
|
}
|
|
592
1701
|
function startDeviceAuth(config, options) {
|
|
593
1702
|
const env = options.env ?? process.env;
|
|
594
|
-
const
|
|
1703
|
+
const transport = options.fetch ?? defaultFetch;
|
|
595
1704
|
const now = options.now ?? Date.now;
|
|
596
1705
|
const sleep = options.sleep ?? defaultSleep;
|
|
597
1706
|
const openBrowser = options.openBrowser ?? defaultOpenBrowser;
|
|
@@ -599,19 +1708,56 @@ function startDeviceAuth(config, options) {
|
|
|
599
1708
|
const lines = [];
|
|
600
1709
|
let settled = false;
|
|
601
1710
|
let cancelled = false;
|
|
1711
|
+
const controller = new AbortController();
|
|
1712
|
+
let deadlineTimer;
|
|
1713
|
+
const bounded = (work) => new Promise((resolve4, reject) => {
|
|
1714
|
+
const abort = () => {
|
|
1715
|
+
cleanup();
|
|
1716
|
+
reject(controller.signal.reason);
|
|
1717
|
+
};
|
|
1718
|
+
const cleanup = () => controller.signal.removeEventListener("abort", abort);
|
|
1719
|
+
if (controller.signal.aborted) {
|
|
1720
|
+
abort();
|
|
1721
|
+
return;
|
|
1722
|
+
}
|
|
1723
|
+
controller.signal.addEventListener("abort", abort, { once: true });
|
|
1724
|
+
work.then((value) => {
|
|
1725
|
+
cleanup();
|
|
1726
|
+
resolve4(value);
|
|
1727
|
+
}, (error) => {
|
|
1728
|
+
cleanup();
|
|
1729
|
+
reject(error);
|
|
1730
|
+
});
|
|
1731
|
+
});
|
|
1732
|
+
const doFetch = async (url, init) => {
|
|
1733
|
+
controller.signal.throwIfAborted();
|
|
1734
|
+
const response = await bounded(transport(url, { ...init, signal: controller.signal }));
|
|
1735
|
+
return { ...response, ok: response.ok, status: response.status, json: () => bounded(response.json()) };
|
|
1736
|
+
};
|
|
1737
|
+
const armDeadline = (ms) => {
|
|
1738
|
+
if (deadlineTimer !== void 0) clearTimeout(deadlineTimer);
|
|
1739
|
+
deadlineTimer = setTimeout(() => {
|
|
1740
|
+
finish("failed", `${label} sign-in timed out. Start again.`);
|
|
1741
|
+
controller.abort(new Error("Sign-in timed out"));
|
|
1742
|
+
cancelWaiter?.();
|
|
1743
|
+
}, ms);
|
|
1744
|
+
deadlineTimer.unref?.();
|
|
1745
|
+
};
|
|
602
1746
|
const pushLine = (text2) => {
|
|
603
1747
|
const value = sanitizeTuiText(text2);
|
|
604
1748
|
if (value.length === 0) return;
|
|
605
1749
|
lines.push(value);
|
|
606
1750
|
if (lines.length > MAX_VISIBLE_LINES) lines.shift();
|
|
607
1751
|
};
|
|
608
|
-
const publish = (phase,
|
|
609
|
-
|
|
1752
|
+
const publish = (phase, message2) => {
|
|
1753
|
+
if (settled && phase === "running") return;
|
|
1754
|
+
options.onUpdate({ phase, lines: [...lines], message: message2 });
|
|
610
1755
|
};
|
|
611
|
-
const finish = (phase,
|
|
1756
|
+
const finish = (phase, message2) => {
|
|
612
1757
|
if (settled) return;
|
|
613
1758
|
settled = true;
|
|
614
|
-
|
|
1759
|
+
if (deadlineTimer !== void 0) clearTimeout(deadlineTimer);
|
|
1760
|
+
publish(phase, message2);
|
|
615
1761
|
};
|
|
616
1762
|
const requestDeviceCode = async (cfg) => {
|
|
617
1763
|
const params = new URLSearchParams({ client_id: cfg.clientId });
|
|
@@ -692,6 +1838,7 @@ function startDeviceAuth(config, options) {
|
|
|
692
1838
|
}
|
|
693
1839
|
};
|
|
694
1840
|
const persist = (record, connectedMessage) => {
|
|
1841
|
+
if (settled || cancelled || controller.signal.aborted) return;
|
|
695
1842
|
const { store } = addAccount(loadAccountStore(options.homeDir), config.providerId, record);
|
|
696
1843
|
if (!saveAccountStore(store, options.homeDir)) {
|
|
697
1844
|
finish("failed", "Sign-in completed, but the private credential account store could not be saved. Try again after restoring access to the local state directory.");
|
|
@@ -720,15 +1867,28 @@ function startDeviceAuth(config, options) {
|
|
|
720
1867
|
pushLine(`Visit: ${device.verification_uri}`);
|
|
721
1868
|
publish("running", `Complete the ${label} device sign-in in your browser.`);
|
|
722
1869
|
try {
|
|
723
|
-
await openBrowser(openUrl);
|
|
1870
|
+
await bounded(Promise.resolve(openBrowser(openUrl)));
|
|
724
1871
|
} catch {
|
|
725
1872
|
pushLine("Could not open a browser automatically; open the URL above.");
|
|
726
1873
|
publish("running", `Complete the ${label} device sign-in in your browser.`);
|
|
727
1874
|
}
|
|
1875
|
+
if (settled) return;
|
|
728
1876
|
let intervalSeconds = device.interval ?? DEFAULT_INTERVAL_SECONDS;
|
|
729
1877
|
const deadline = now() + (device.expires_in ?? DEFAULT_EXPIRES_IN_SECONDS) * 1e3;
|
|
1878
|
+
armDeadline(Math.max(0, deadline - now()));
|
|
730
1879
|
while (!cancelled) {
|
|
731
|
-
await sleep(intervalSeconds * 1e3);
|
|
1880
|
+
if (options.sleep) await bounded(sleep(intervalSeconds * 1e3));
|
|
1881
|
+
else await bounded(new Promise((resolve4, reject) => {
|
|
1882
|
+
const abort = () => {
|
|
1883
|
+
clearTimeout(timer);
|
|
1884
|
+
reject(controller.signal.reason);
|
|
1885
|
+
};
|
|
1886
|
+
const timer = setTimeout(() => {
|
|
1887
|
+
controller.signal.removeEventListener("abort", abort);
|
|
1888
|
+
resolve4();
|
|
1889
|
+
}, intervalSeconds * 1e3);
|
|
1890
|
+
controller.signal.addEventListener("abort", abort, { once: true });
|
|
1891
|
+
}));
|
|
732
1892
|
if (cancelled) break;
|
|
733
1893
|
if (now() >= deadline) {
|
|
734
1894
|
finish("failed", `${label} device sign-in timed out. Start again.`);
|
|
@@ -795,8 +1955,8 @@ function startDeviceAuth(config, options) {
|
|
|
795
1955
|
const serverFactory = options.serverFactory ?? defaultServerFactory;
|
|
796
1956
|
let resolveOutcome = () => {
|
|
797
1957
|
};
|
|
798
|
-
const outcomePromise = new Promise((
|
|
799
|
-
resolveOutcome =
|
|
1958
|
+
const outcomePromise = new Promise((resolve4) => {
|
|
1959
|
+
resolveOutcome = resolve4;
|
|
800
1960
|
});
|
|
801
1961
|
let server;
|
|
802
1962
|
try {
|
|
@@ -808,13 +1968,13 @@ function startDeviceAuth(config, options) {
|
|
|
808
1968
|
return;
|
|
809
1969
|
}
|
|
810
1970
|
cancelWaiter = () => resolveOutcome({ type: "cancelled" });
|
|
811
|
-
if (cancelled) {
|
|
1971
|
+
if (cancelled || settled) {
|
|
812
1972
|
server.close();
|
|
813
1973
|
finish("cancelled", `${label} browser sign-in cancelled.`);
|
|
814
1974
|
return;
|
|
815
1975
|
}
|
|
816
1976
|
const redirectUri = `http://localhost:${server.port}/callback`;
|
|
817
|
-
const state = base64url(
|
|
1977
|
+
const state = base64url(randomBytes2(16)).replace(/[^a-zA-Z0-9]/g, "").slice(0, 32);
|
|
818
1978
|
const authorizeUrl = new URL(cfg.authorizeUrl);
|
|
819
1979
|
authorizeUrl.searchParams.set(cfg.redirectParam ?? "redirect_uri", redirectUri);
|
|
820
1980
|
authorizeUrl.searchParams.set("code_challenge", challenge);
|
|
@@ -829,12 +1989,17 @@ function startDeviceAuth(config, options) {
|
|
|
829
1989
|
pushLine(`Sign in at: ${openUrl}`);
|
|
830
1990
|
publish("running", `Complete the ${label} sign-in in your browser.`);
|
|
831
1991
|
try {
|
|
832
|
-
await openBrowser(openUrl);
|
|
1992
|
+
await bounded(Promise.resolve(openBrowser(openUrl)));
|
|
833
1993
|
} catch {
|
|
834
1994
|
pushLine("Could not open a browser automatically; open the URL above.");
|
|
835
1995
|
publish("running", `Complete the ${label} sign-in in your browser.`);
|
|
836
1996
|
}
|
|
837
|
-
void sleep(PKCE_TIMEOUT_SECONDS * 1e3).then(() =>
|
|
1997
|
+
if (options.sleep) void bounded(sleep(PKCE_TIMEOUT_SECONDS * 1e3)).then(() => {
|
|
1998
|
+
finish("failed", `${label} browser sign-in timed out. Start again.`);
|
|
1999
|
+
controller.abort(new Error("Sign-in timed out"));
|
|
2000
|
+
resolveOutcome({ type: "timeout" });
|
|
2001
|
+
}, () => {
|
|
2002
|
+
});
|
|
838
2003
|
const outcome = await outcomePromise;
|
|
839
2004
|
server.close();
|
|
840
2005
|
if (outcome.type === "cancelled" || cancelled) {
|
|
@@ -873,28 +2038,32 @@ function startDeviceAuth(config, options) {
|
|
|
873
2038
|
persist(record, `${label} connected through browser sign-in.`);
|
|
874
2039
|
};
|
|
875
2040
|
let cancelWaiter;
|
|
876
|
-
|
|
2041
|
+
armDeadline((config.kind === "pkce-loopback" ? PKCE_TIMEOUT_SECONDS : DEFAULT_EXPIRES_IN_SECONDS) * 1e3);
|
|
2042
|
+
void (config.kind === "pkce-loopback" ? runPkceLoopback(config) : runDeviceCode(config)).catch((error) => {
|
|
2043
|
+
finish(cancelled ? "cancelled" : "failed", cancelled ? `${label} sign-in cancelled.` : error instanceof Error ? error.message : "Sign-in failed.");
|
|
2044
|
+
});
|
|
877
2045
|
return {
|
|
878
2046
|
cancel: () => {
|
|
879
2047
|
if (settled || cancelled) return;
|
|
880
2048
|
cancelled = true;
|
|
2049
|
+
finish("cancelled", `${label} sign-in cancelled.`);
|
|
2050
|
+
controller.abort(new Error("Sign-in cancelled"));
|
|
881
2051
|
cancelWaiter?.();
|
|
882
2052
|
}
|
|
883
2053
|
};
|
|
884
2054
|
}
|
|
885
|
-
var KIMI_CLIENT_VERSION = "0.16.3";
|
|
886
2055
|
var KIMI_DEVICE_ID_FILENAME = "kimi-device-id";
|
|
887
2056
|
function kimiDeviceId(homeDir) {
|
|
888
|
-
const idPath =
|
|
2057
|
+
const idPath = join3(homeStateDir(homeDir), KIMI_DEVICE_ID_FILENAME);
|
|
889
2058
|
try {
|
|
890
|
-
const existing =
|
|
2059
|
+
const existing = readFileSync3(idPath, "utf8").trim();
|
|
891
2060
|
if (existing) return existing;
|
|
892
2061
|
} catch {
|
|
893
2062
|
}
|
|
894
|
-
const deviceId =
|
|
2063
|
+
const deviceId = randomUUID2().replace(/-/g, "");
|
|
895
2064
|
try {
|
|
896
|
-
|
|
897
|
-
|
|
2065
|
+
mkdirSync3(homeStateDir(homeDir), { recursive: true });
|
|
2066
|
+
writeFileSync3(idPath, `${deviceId}
|
|
898
2067
|
`, { mode: 384 });
|
|
899
2068
|
} catch {
|
|
900
2069
|
}
|
|
@@ -911,129 +2080,93 @@ function kimiDeviceModel() {
|
|
|
911
2080
|
}
|
|
912
2081
|
function kimiFingerprintHeaders(homeDir) {
|
|
913
2082
|
return {
|
|
914
|
-
"User-Agent":
|
|
915
|
-
"X-Msh-Platform": "
|
|
916
|
-
"X-Msh-Version":
|
|
2083
|
+
"User-Agent": "0security",
|
|
2084
|
+
"X-Msh-Platform": "0security",
|
|
2085
|
+
"X-Msh-Version": "0",
|
|
917
2086
|
"X-Msh-Device-Name": sanitizeHeaderValue(hostname()),
|
|
918
2087
|
"X-Msh-Device-Model": sanitizeHeaderValue(kimiDeviceModel()),
|
|
919
2088
|
"X-Msh-Os-Version": sanitizeHeaderValue(osVersion()),
|
|
920
2089
|
"X-Msh-Device-Id": sanitizeHeaderValue(kimiDeviceId(homeDir))
|
|
921
2090
|
};
|
|
922
2091
|
}
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
// installed-app client_id/secret, the accounts.google.com authorize endpoint,
|
|
1002
|
-
// and the standard oauth2.googleapis.com/token FORM exchange:
|
|
1003
|
-
// authorize https://accounts.google.com/o/oauth2/v2/auth
|
|
1004
|
-
// (response_type=code, access_type=offline, prompt=consent, S256, state)
|
|
1005
|
-
// token https://oauth2.googleapis.com/token (form-encoded)
|
|
1006
|
-
// scopes: cloud-platform + userinfo.email + userinfo.profile.
|
|
1007
|
-
// The token response's access_token/refresh_token/expires_in are stored as an
|
|
1008
|
-
// oauth record (buildOAuthRecord): accountEnvPatch writes access_token to
|
|
1009
|
-
// ZERO_GEMINI_ACCESS_TOKEN (envVars[0]) and refresh_token to
|
|
1010
|
-
// ZERO_GEMINI_OAUTH_REFRESH_TOKEN (the /REFRESH/i var). The Code Assist
|
|
1011
|
-
// PROJECT is resolved later, at request time, in llm-api — NOT here.
|
|
1012
|
-
//
|
|
1013
|
-
// Redirect path: the shared loopback server answers `/callback`; Google's
|
|
1014
|
-
// installed-app clients accept ANY loopback path (RFC 8252 §7.3), so the
|
|
1015
|
-
// gemini-cli's `/oauth2callback` and our `/callback` are equally valid. The
|
|
1016
|
-
// engine threads the exact redirect_uri it built into the token exchange, so
|
|
1017
|
-
// the two always agree.
|
|
1018
|
-
google: {
|
|
1019
|
-
kind: "pkce-loopback",
|
|
1020
|
-
providerId: "google",
|
|
1021
|
-
authorizeUrl: "https://accounts.google.com/o/oauth2/v2/auth",
|
|
1022
|
-
keysUrl: "https://oauth2.googleapis.com/token",
|
|
1023
|
-
// Public installed-app credentials (as embedded by gemini-cli/opencode);
|
|
1024
|
-
// split only to avoid a secret-scanner false positive — value is intact.
|
|
1025
|
-
clientId: "681255809395-oo8ft2oprdrnp9e3aqf6av3hmdib135j.apps.googleusercontent.com",
|
|
1026
|
-
clientSecret: "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",
|
|
1027
|
-
exchangeBodyMode: "form",
|
|
1028
|
-
authorizeParams: { response_type: "code", access_type: "offline", prompt: "consent" },
|
|
1029
|
-
scopes: [
|
|
1030
|
-
"https://www.googleapis.com/auth/cloud-platform",
|
|
1031
|
-
"https://www.googleapis.com/auth/userinfo.email",
|
|
1032
|
-
"https://www.googleapis.com/auth/userinfo.profile"
|
|
1033
|
-
],
|
|
1034
|
-
toAccountRecord: buildOAuthRecord
|
|
1035
|
-
}
|
|
1036
|
-
};
|
|
2092
|
+
function createProviderDeviceAuth(env = process.env) {
|
|
2093
|
+
const clientId = (name) => env[name]?.trim() || void 0;
|
|
2094
|
+
return {
|
|
2095
|
+
// Endpoints use the provider protocol; client identity must be registered for 0.
|
|
2096
|
+
...clientId("ZERO_XAI_OAUTH_CLIENT_ID") ? { xai: {
|
|
2097
|
+
kind: "device-code",
|
|
2098
|
+
providerId: "xai",
|
|
2099
|
+
deviceCodeUrl: "https://auth.x.ai/oauth2/device/code",
|
|
2100
|
+
tokenUrl: "https://auth.x.ai/oauth2/token",
|
|
2101
|
+
clientId: clientId("ZERO_XAI_OAUTH_CLIENT_ID"),
|
|
2102
|
+
scopes: ["openid", "profile", "email", "offline_access", "grok-cli:access", "api:access"],
|
|
2103
|
+
toAccountRecord: buildOAuthRecord
|
|
2104
|
+
} } : {},
|
|
2105
|
+
// Never borrow the official Kimi CLI client identity for a new 0 sign-in.
|
|
2106
|
+
// Opt-in is only for an operator who has registered a client for 0.
|
|
2107
|
+
...clientId("ZERO_KIMI_OAUTH_CLIENT_ID") ? { kimi: {
|
|
2108
|
+
kind: "device-code",
|
|
2109
|
+
providerId: "kimi",
|
|
2110
|
+
deviceCodeUrl: "https://auth.kimi.com/api/oauth/device_authorization",
|
|
2111
|
+
tokenUrl: "https://auth.kimi.com/api/oauth/token",
|
|
2112
|
+
clientId: clientId("ZERO_KIMI_OAUTH_CLIENT_ID"),
|
|
2113
|
+
scopes: [],
|
|
2114
|
+
buildHeaders: kimiFingerprintHeaders,
|
|
2115
|
+
toAccountRecord: buildOAuthRecord
|
|
2116
|
+
} } : {},
|
|
2117
|
+
// Own GitHub app registration must also be entitled to the Copilot service.
|
|
2118
|
+
...clientId("ZERO_COPILOT_OAUTH_CLIENT_ID") ? { copilot: {
|
|
2119
|
+
kind: "device-code",
|
|
2120
|
+
providerId: "copilot",
|
|
2121
|
+
deviceCodeUrl: "https://github.com/login/device/code",
|
|
2122
|
+
tokenUrl: "https://github.com/login/oauth/access_token",
|
|
2123
|
+
clientId: clientId("ZERO_COPILOT_OAUTH_CLIENT_ID"),
|
|
2124
|
+
scopes: ["read:user"],
|
|
2125
|
+
toAccountRecord: buildOAuthRecord
|
|
2126
|
+
} } : {},
|
|
2127
|
+
// Matches oh-my-pi rules/auth/openrouter.kdl (fetched 2026-09-14):
|
|
2128
|
+
// https://github.com/can1357/oh-my-pi/blob/main/packages/catalog/src/compat/rules/auth/openrouter.kdl
|
|
2129
|
+
// login "oauth-code" pkce=#true, standard authorize params OFF: open
|
|
2130
|
+
// https://openrouter.ai/auth?callback_url=<loopback>&code_challenge=<c>&code_challenge_method=S256
|
|
2131
|
+
// (CLIENT-ID-LESS — no client_id param), then POST JSON
|
|
2132
|
+
// { code, code_verifier, code_challenge_method: "S256" }
|
|
2133
|
+
// to https://openrouter.ai/api/v1/auth/keys, which PROVISIONS a durable
|
|
2134
|
+
// `sk-or-...` API key. The kdl's `credential { access "key" }` names the
|
|
2135
|
+
// response field `key` (buildOpenRouterKeyRecord). Stored as an api_key record
|
|
2136
|
+
// and written to OPENROUTER_API_KEY (envVars[0]) by accountEnvPatch, which
|
|
2137
|
+
// llm-api already reads — so no llm-api change.
|
|
2138
|
+
openrouter: {
|
|
2139
|
+
kind: "pkce-loopback",
|
|
2140
|
+
providerId: "openrouter",
|
|
2141
|
+
authorizeUrl: "https://openrouter.ai/auth",
|
|
2142
|
+
keysUrl: "https://openrouter.ai/api/v1/auth/keys",
|
|
2143
|
+
redirectParam: "callback_url",
|
|
2144
|
+
scopes: [],
|
|
2145
|
+
toAccountRecord: buildOpenRouterKeyRecord
|
|
2146
|
+
},
|
|
2147
|
+
// Google browser sign-in requires a 0 registration entitled to Code Assist.
|
|
2148
|
+
...clientId("ZERO_GOOGLE_OAUTH_CLIENT_ID") ? { google: {
|
|
2149
|
+
kind: "pkce-loopback",
|
|
2150
|
+
providerId: "google",
|
|
2151
|
+
authorizeUrl: "https://accounts.google.com/o/oauth2/v2/auth",
|
|
2152
|
+
keysUrl: "https://oauth2.googleapis.com/token",
|
|
2153
|
+
clientId: clientId("ZERO_GOOGLE_OAUTH_CLIENT_ID"),
|
|
2154
|
+
...env.ZERO_GOOGLE_OAUTH_CLIENT_SECRET ? { clientSecret: env.ZERO_GOOGLE_OAUTH_CLIENT_SECRET } : {},
|
|
2155
|
+
exchangeBodyMode: "form",
|
|
2156
|
+
authorizeParams: { response_type: "code", access_type: "offline", prompt: "consent" },
|
|
2157
|
+
scopes: [
|
|
2158
|
+
"https://www.googleapis.com/auth/cloud-platform",
|
|
2159
|
+
"https://www.googleapis.com/auth/userinfo.email",
|
|
2160
|
+
"https://www.googleapis.com/auth/userinfo.profile"
|
|
2161
|
+
],
|
|
2162
|
+
toAccountRecord: (tokenResponse) => ({
|
|
2163
|
+
...buildOAuthRecord(tokenResponse),
|
|
2164
|
+
oauthClient: { clientId: clientId("ZERO_GOOGLE_OAUTH_CLIENT_ID"), ...env.ZERO_GOOGLE_OAUTH_CLIENT_SECRET ? { clientSecret: env.ZERO_GOOGLE_OAUTH_CLIENT_SECRET } : {} }
|
|
2165
|
+
})
|
|
2166
|
+
} } : {}
|
|
2167
|
+
};
|
|
2168
|
+
}
|
|
2169
|
+
var PROVIDER_DEVICE_AUTH = createProviderDeviceAuth();
|
|
1037
2170
|
function buildOAuthRecord(tokenResponse) {
|
|
1038
2171
|
const tokens = {};
|
|
1039
2172
|
if (typeof tokenResponse.access_token === "string") tokens.accessToken = tokenResponse.access_token;
|
|
@@ -1052,14 +2185,14 @@ function buildOpenRouterKeyRecord(exchange) {
|
|
|
1052
2185
|
}
|
|
1053
2186
|
|
|
1054
2187
|
// packages/cli/src/tui/plugin-service.ts
|
|
1055
|
-
import { chmodSync, mkdirSync as
|
|
1056
|
-
import { join as
|
|
2188
|
+
import { chmodSync, mkdirSync as mkdirSync4, writeFileSync as writeFileSync4 } from "node:fs";
|
|
2189
|
+
import { join as join4 } from "node:path";
|
|
1057
2190
|
function coreLoader(injected) {
|
|
1058
2191
|
if (typeof injected === "function") return injected;
|
|
1059
2192
|
if (injected) return async () => injected;
|
|
1060
2193
|
let cached;
|
|
1061
2194
|
return () => {
|
|
1062
|
-
if (!cached) cached = import("./dist-
|
|
2195
|
+
if (!cached) cached = import("./dist-ONIRKDH6.js");
|
|
1063
2196
|
return cached;
|
|
1064
2197
|
};
|
|
1065
2198
|
}
|
|
@@ -1183,17 +2316,17 @@ function createPluginService(deps = {}) {
|
|
|
1183
2316
|
if (!c.ensurePluginsRoot(root)) {
|
|
1184
2317
|
return { ok: false, message: `Could not create the plugins root at ${root}.` };
|
|
1185
2318
|
}
|
|
1186
|
-
const dir =
|
|
1187
|
-
|
|
2319
|
+
const dir = join4(root, item.id);
|
|
2320
|
+
mkdirSync4(dir, { recursive: true, mode: c.PLUGIN_DIR_MODE });
|
|
1188
2321
|
chmodSync(dir, c.PLUGIN_DIR_MODE);
|
|
1189
|
-
const manifestPath =
|
|
1190
|
-
|
|
2322
|
+
const manifestPath = join4(dir, c.PLUGIN_MANIFEST_FILE);
|
|
2323
|
+
writeFileSync4(manifestPath, `${JSON.stringify(entry.manifest, null, 2)}
|
|
1191
2324
|
`, {
|
|
1192
2325
|
mode: c.PLUGIN_FILE_MODE
|
|
1193
2326
|
});
|
|
1194
2327
|
chmodSync(manifestPath, c.PLUGIN_FILE_MODE);
|
|
1195
|
-
const entryPath =
|
|
1196
|
-
|
|
2328
|
+
const entryPath = join4(dir, c.PLUGIN_ENTRY_FILE);
|
|
2329
|
+
writeFileSync4(entryPath, entryBody, { mode: c.PLUGIN_FILE_MODE });
|
|
1197
2330
|
chmodSync(entryPath, c.PLUGIN_FILE_MODE);
|
|
1198
2331
|
return {
|
|
1199
2332
|
ok: true,
|
|
@@ -1420,7 +2553,7 @@ function coreLoader2(injected) {
|
|
|
1420
2553
|
if (injected) return async () => injected;
|
|
1421
2554
|
let cached;
|
|
1422
2555
|
return () => {
|
|
1423
|
-
if (!cached) cached = import("./dist-
|
|
2556
|
+
if (!cached) cached = import("./dist-ONIRKDH6.js");
|
|
1424
2557
|
return cached;
|
|
1425
2558
|
};
|
|
1426
2559
|
}
|
|
@@ -1599,6 +2732,60 @@ async function createSessionPluginHostManager(deps = {}) {
|
|
|
1599
2732
|
return { current, acquire, refresh, runPlugin, onChanged, dispose };
|
|
1600
2733
|
}
|
|
1601
2734
|
|
|
2735
|
+
// packages/cli/src/web/chatgpt-plan-controller.ts
|
|
2736
|
+
var ChatGptPlanAuthController = class {
|
|
2737
|
+
#options;
|
|
2738
|
+
#abort;
|
|
2739
|
+
#epoch = 0;
|
|
2740
|
+
#status = { phase: "idle", message: "Connect your ChatGPT account.", lines: [], verificationUrl: null, userCode: null, available: true };
|
|
2741
|
+
constructor(options = {}) {
|
|
2742
|
+
this.#options = options;
|
|
2743
|
+
}
|
|
2744
|
+
status() {
|
|
2745
|
+
if (this.#status.phase !== "running" && this.#status.phase !== "failed" && this.#status.phase !== "cancelled") {
|
|
2746
|
+
const active = (this.#options.list ?? listChatGptPlanProfiles)(this.#options.homeDir).find((p) => p.active && p.status === "connected");
|
|
2747
|
+
if (active) return { ...this.#status, phase: "connected", message: active.sharing ? "ChatGPT plan sharing is connected." : "ChatGPT identity is connected. Grant plan usage to use models.", lines: [], verificationUrl: null };
|
|
2748
|
+
}
|
|
2749
|
+
return { ...this.#status, lines: [...this.#status.lines] };
|
|
2750
|
+
}
|
|
2751
|
+
start(profileId) {
|
|
2752
|
+
if (this.#abort) return this.status();
|
|
2753
|
+
const epoch2 = ++this.#epoch, abort = new AbortController();
|
|
2754
|
+
this.#abort = abort;
|
|
2755
|
+
this.#status = { ...this.#status, phase: "running", message: "Starting ChatGPT sign-in.", verificationUrl: null, lines: [] };
|
|
2756
|
+
void Promise.resolve().then(() => {
|
|
2757
|
+
abort.signal.throwIfAborted();
|
|
2758
|
+
return (this.#options.signIn ?? signInChatGptPlan)({
|
|
2759
|
+
homeDir: this.#options.homeDir,
|
|
2760
|
+
profileId,
|
|
2761
|
+
signal: abort.signal,
|
|
2762
|
+
onAuthorize: (value) => {
|
|
2763
|
+
if (epoch2 !== this.#epoch || abort.signal.aborted) return;
|
|
2764
|
+
const url = new URL(value);
|
|
2765
|
+
if (url.protocol !== "https:" || url.hostname !== "auth.openai.com" || url.port || url.username || url.password || url.hash || ["access_token", "refresh_token", "id_token", "id_token_hint", "api_key"].some((key) => url.searchParams.has(key))) throw new Error("Unsafe authorization destination.");
|
|
2766
|
+
this.#status = { ...this.#status, message: "Continue in your browser to connect ChatGPT.", verificationUrl: url.href };
|
|
2767
|
+
}
|
|
2768
|
+
});
|
|
2769
|
+
}).then(() => {
|
|
2770
|
+
if (epoch2 !== this.#epoch || abort.signal.aborted) return;
|
|
2771
|
+
this.#status = { ...this.#status, phase: "connected", verificationUrl: null, message: "ChatGPT sign-in completed." };
|
|
2772
|
+
}, () => {
|
|
2773
|
+
if (epoch2 !== this.#epoch || abort.signal.aborted) return;
|
|
2774
|
+
this.#status = { ...this.#status, phase: "failed", verificationUrl: null, message: "ChatGPT sign-in failed. Retry the connection." };
|
|
2775
|
+
}).finally(() => {
|
|
2776
|
+
if (epoch2 === this.#epoch) this.#abort = void 0;
|
|
2777
|
+
});
|
|
2778
|
+
return this.status();
|
|
2779
|
+
}
|
|
2780
|
+
cancel() {
|
|
2781
|
+
++this.#epoch;
|
|
2782
|
+
this.#abort?.abort();
|
|
2783
|
+
this.#abort = void 0;
|
|
2784
|
+
this.#status = { ...this.#status, phase: "cancelled", message: "ChatGPT sign-in cancelled.", verificationUrl: null, lines: [] };
|
|
2785
|
+
return this.status();
|
|
2786
|
+
}
|
|
2787
|
+
};
|
|
2788
|
+
|
|
1602
2789
|
// packages/cli/src/web/codex-auth-controller.ts
|
|
1603
2790
|
import { spawnSync as spawnSync2 } from "node:child_process";
|
|
1604
2791
|
|
|
@@ -1642,8 +2829,8 @@ function startCodexDeviceAuth(options) {
|
|
|
1642
2829
|
let settled = false;
|
|
1643
2830
|
let cancelled = false;
|
|
1644
2831
|
let browserOpened = false;
|
|
1645
|
-
const publish = (phase,
|
|
1646
|
-
options.onUpdate({ phase, lines: [...lines], message });
|
|
2832
|
+
const publish = (phase, message2) => {
|
|
2833
|
+
options.onUpdate({ phase, lines: [...lines], message: message2 });
|
|
1647
2834
|
};
|
|
1648
2835
|
const tryOpenBrowser = (text2) => {
|
|
1649
2836
|
if (browserOpened) return;
|
|
@@ -1674,14 +2861,14 @@ function startCodexDeviceAuth(options) {
|
|
|
1674
2861
|
}
|
|
1675
2862
|
publish("running", "Complete the ChatGPT Codex device sign-in in your browser.");
|
|
1676
2863
|
};
|
|
1677
|
-
const finish = (phase,
|
|
2864
|
+
const finish = (phase, message2) => {
|
|
1678
2865
|
if (settled) return;
|
|
1679
2866
|
settled = true;
|
|
1680
2867
|
if (pending.trim().length > 0) {
|
|
1681
2868
|
lines.push(pending.trim());
|
|
1682
2869
|
if (lines.length > MAX_VISIBLE_LINES2) lines.shift();
|
|
1683
2870
|
}
|
|
1684
|
-
publish(phase,
|
|
2871
|
+
publish(phase, message2);
|
|
1685
2872
|
};
|
|
1686
2873
|
let child;
|
|
1687
2874
|
try {
|
|
@@ -1814,10 +3001,109 @@ var CodexAuthController = class {
|
|
|
1814
3001
|
}
|
|
1815
3002
|
};
|
|
1816
3003
|
|
|
3004
|
+
// packages/cli/src/web/workspace-directories.ts
|
|
3005
|
+
import { opendir, realpath, stat } from "node:fs/promises";
|
|
3006
|
+
import { homedir } from "node:os";
|
|
3007
|
+
import { dirname as dirname2, join as join5, resolve } from "node:path";
|
|
3008
|
+
var MAX_FOLDERS = 200;
|
|
3009
|
+
var MAX_SCANNED_ENTRIES = 2e3;
|
|
3010
|
+
async function listWorkspaceDirectories(input) {
|
|
3011
|
+
if (input !== void 0 && (typeof input !== "string" || input.length > 4096 || input.includes("\0"))) throw new Error("Enter a valid folder path.");
|
|
3012
|
+
const homePath = await realpath(homedir()).catch(() => resolve(homedir()));
|
|
3013
|
+
let path;
|
|
3014
|
+
try {
|
|
3015
|
+
path = await realpath(typeof input === "string" && input.trim() ? resolve(input.trim()) : homePath);
|
|
3016
|
+
} catch {
|
|
3017
|
+
throw new Error("That folder could not be found on this host.");
|
|
3018
|
+
}
|
|
3019
|
+
if (!(await stat(path)).isDirectory()) throw new Error("Choose a folder rather than a file.");
|
|
3020
|
+
let directory;
|
|
3021
|
+
try {
|
|
3022
|
+
directory = await opendir(path);
|
|
3023
|
+
} catch {
|
|
3024
|
+
throw new Error("This host cannot open that folder. Check its permissions or enter another path.");
|
|
3025
|
+
}
|
|
3026
|
+
const entries = [];
|
|
3027
|
+
let scanned = 0;
|
|
3028
|
+
let truncated = false;
|
|
3029
|
+
for await (const entry of directory) {
|
|
3030
|
+
if (++scanned > MAX_SCANNED_ENTRIES) {
|
|
3031
|
+
truncated = true;
|
|
3032
|
+
break;
|
|
3033
|
+
}
|
|
3034
|
+
if (!entry.isDirectory() && !entry.isSymbolicLink()) continue;
|
|
3035
|
+
try {
|
|
3036
|
+
const canonical = await realpath(join5(path, entry.name));
|
|
3037
|
+
if (!(await stat(canonical)).isDirectory()) continue;
|
|
3038
|
+
if (entries.length >= MAX_FOLDERS) {
|
|
3039
|
+
truncated = true;
|
|
3040
|
+
break;
|
|
3041
|
+
}
|
|
3042
|
+
entries.push({ name: entry.name, path: canonical });
|
|
3043
|
+
} catch {
|
|
3044
|
+
}
|
|
3045
|
+
}
|
|
3046
|
+
entries.sort((a, b) => a.name.localeCompare(b.name, void 0, { numeric: true, sensitivity: "base" }));
|
|
3047
|
+
const parent = dirname2(path);
|
|
3048
|
+
return { path, parentPath: parent === path ? null : parent, homePath, entries, truncated };
|
|
3049
|
+
}
|
|
3050
|
+
|
|
3051
|
+
// packages/cli/src/web/workspace-folder-picker.ts
|
|
3052
|
+
import { execFile } from "node:child_process";
|
|
3053
|
+
import { realpath as realpath2, stat as stat2 } from "node:fs/promises";
|
|
3054
|
+
import { resolve as resolve2 } from "node:path";
|
|
3055
|
+
import { promisify } from "node:util";
|
|
3056
|
+
var execute = promisify(execFile);
|
|
3057
|
+
var PICKER_SCRIPT = `on run argv
|
|
3058
|
+
activate
|
|
3059
|
+
try
|
|
3060
|
+
if (count of argv) > 0 then
|
|
3061
|
+
set chosen to choose folder with prompt "Choose the folder where Zero works" default location (POSIX file (item 1 of argv))
|
|
3062
|
+
else
|
|
3063
|
+
set chosen to choose folder with prompt "Choose the folder where Zero works"
|
|
3064
|
+
end if
|
|
3065
|
+
return POSIX path of chosen
|
|
3066
|
+
on error number -128
|
|
3067
|
+
return ""
|
|
3068
|
+
end try
|
|
3069
|
+
end run`;
|
|
3070
|
+
var pickerActive = false;
|
|
3071
|
+
async function chooseWorkspaceFolder(input, dependencies = {}) {
|
|
3072
|
+
if (input !== void 0 && (typeof input !== "string" || input.length > 4096 || input.includes("\0"))) throw new Error("Enter a valid folder path.");
|
|
3073
|
+
if ((dependencies.platform ?? process.platform) !== "darwin") return { supported: false, path: null };
|
|
3074
|
+
if (pickerActive) throw new Error("A folder chooser is already open on this host.");
|
|
3075
|
+
pickerActive = true;
|
|
3076
|
+
try {
|
|
3077
|
+
const args = ["-e", PICKER_SCRIPT];
|
|
3078
|
+
if (typeof input === "string" && input.trim()) {
|
|
3079
|
+
const initial = await realpath2(resolve2(input));
|
|
3080
|
+
if (!(await stat2(initial)).isDirectory()) throw new Error("Choose a folder rather than a file.");
|
|
3081
|
+
args.push(initial);
|
|
3082
|
+
}
|
|
3083
|
+
let stdout;
|
|
3084
|
+
try {
|
|
3085
|
+
({ stdout } = await (dependencies.run ?? execute)("/usr/bin/osascript", args, { encoding: "utf8", timeout: 12e4, maxBuffer: 16384, windowsHide: true }));
|
|
3086
|
+
} catch {
|
|
3087
|
+
throw new Error("The folder chooser could not complete on this host. Try again or enter a folder path.");
|
|
3088
|
+
}
|
|
3089
|
+
const selected = stdout.replace(/\r?\n$/, "");
|
|
3090
|
+
if (!selected) return { supported: true, path: null };
|
|
3091
|
+
if (selected.length > 4096 || selected.includes("\0")) throw new Error("The folder chooser returned an invalid path.");
|
|
3092
|
+
const path = await realpath2(selected);
|
|
3093
|
+
if (!(await stat2(path)).isDirectory()) throw new Error("The folder chooser did not return an existing folder.");
|
|
3094
|
+
return { supported: true, path };
|
|
3095
|
+
} finally {
|
|
3096
|
+
pickerActive = false;
|
|
3097
|
+
}
|
|
3098
|
+
}
|
|
3099
|
+
|
|
1817
3100
|
// packages/cli/src/web/operator-services.ts
|
|
1818
3101
|
var MODEL_ROLES = ["discovery", "attack", "verify", "report", "audit", "review"];
|
|
1819
3102
|
var reservedToolNames = Object.values(TOOL_DEFINITIONS).map((tool) => tool.name);
|
|
1820
3103
|
var runtimeEnvironments = /* @__PURE__ */ new WeakMap();
|
|
3104
|
+
function assertPlanAllowed(providerId, policy) {
|
|
3105
|
+
if (providerId === "chatgpt-plan" && policy.allowChatGptPlan === false) throw new OperatorError(403, "local_only", "Native ChatGPT plan connections are available only in the local open-source application.");
|
|
3106
|
+
}
|
|
1821
3107
|
var runtimeContextLimits = /* @__PURE__ */ new WeakMap();
|
|
1822
3108
|
var pluginManager;
|
|
1823
3109
|
var pluginManagerPromise;
|
|
@@ -1862,7 +3148,7 @@ function webRuntimeEnv() {
|
|
|
1862
3148
|
return Object.fromEntries(Object.entries(env).filter((entry) => entry[1] !== void 0));
|
|
1863
3149
|
}
|
|
1864
3150
|
function publicMessage(error) {
|
|
1865
|
-
let
|
|
3151
|
+
let message2 = error instanceof Error ? error.message : String(error);
|
|
1866
3152
|
const secrets = /* @__PURE__ */ new Set();
|
|
1867
3153
|
for (const provider2 of PROVIDERS) for (const variable of provider2.envVars) {
|
|
1868
3154
|
const value = process.env[variable];
|
|
@@ -1875,8 +3161,8 @@ function publicMessage(error) {
|
|
|
1875
3161
|
const env = { ...process.env };
|
|
1876
3162
|
maybeLoadCodexAuth({ env });
|
|
1877
3163
|
for (const variable of PROVIDERS.find((provider2) => provider2.id === "chatgpt-codex").envVars) if (env[variable]) secrets.add(env[variable]);
|
|
1878
|
-
for (const secret of secrets) if (secret)
|
|
1879
|
-
return
|
|
3164
|
+
for (const secret of secrets) if (secret) message2 = message2.split(secret).join("[redacted]");
|
|
3165
|
+
return message2.replace(/(https?:\/\/)[^\s/@]+:[^\s/@]+@/g, "$1[redacted]@").replace(/([?&](?:api[_-]?key|access_token|refresh_token|id_token|token|secret|password)=)[^&\s"'<>]+/gi, "$1[redacted]").slice(0, 4e3);
|
|
1880
3166
|
}
|
|
1881
3167
|
function runtimeDiagnostics(runtime) {
|
|
1882
3168
|
const diagnostics = runtime.getConfigurationDiagnostics();
|
|
@@ -1957,21 +3243,31 @@ async function validateRuntimeModels(runtime) {
|
|
|
1957
3243
|
if (model !== "auto" && !models.some((entry) => entry.id === model.replace(/^openai\//, ""))) throw new OperatorError(409, "model_not_available", "A role model is not available to this ChatGPT account.");
|
|
1958
3244
|
}
|
|
1959
3245
|
}
|
|
3246
|
+
if (diagnostics.provider === "chatgpt-plan") {
|
|
3247
|
+
const models = await runtime.availableModelCatalog(AbortSignal.timeout(1e4));
|
|
3248
|
+
if (!models.some((model) => model.id === runtime.resolvedModel())) throw new OperatorError(409, "model_not_available", "This model is not available to the connected ChatGPT plan.");
|
|
3249
|
+
for (const model of Object.values(runtime.modelSelection().agentModels)) {
|
|
3250
|
+
if (model !== "auto" && !models.some((entry) => entry.id === model)) throw new OperatorError(409, "model_not_available", "A role model is not available to the connected ChatGPT plan.");
|
|
3251
|
+
}
|
|
3252
|
+
}
|
|
1960
3253
|
for (const role of Object.keys(runtime.modelSelection().agentModels)) {
|
|
1961
3254
|
if (runtime.modelSelection().agentModels[role] !== "auto") await runtime.forkForSubagent(12e4, { role });
|
|
1962
3255
|
}
|
|
1963
3256
|
}
|
|
1964
|
-
async function createWebConsoleRuntime(selection = {}) {
|
|
3257
|
+
async function createWebConsoleRuntime(selection = {}, policy = {}) {
|
|
1965
3258
|
validateSelection(selection);
|
|
3259
|
+
assertPlanAllowed(selection.providerId, policy);
|
|
1966
3260
|
const env = webRuntimeEnv();
|
|
3261
|
+
if (!selection.providerId) assertPlanAllowed(env.ZERO_SELECTED_PROVIDER || env.ZERO_PROVIDER || env.ZERO_FORCE_PROVIDER, policy);
|
|
1967
3262
|
const { runtime } = await createPreferredConsoleRuntime({
|
|
1968
3263
|
provider: selection.providerId,
|
|
1969
3264
|
model: selection.model,
|
|
1970
3265
|
agentModels: selection.agentModels,
|
|
1971
3266
|
singleModel: selection.singleModel,
|
|
1972
3267
|
autoRoute: selection.autoRoute,
|
|
1973
|
-
env
|
|
1974
|
-
|
|
3268
|
+
env,
|
|
3269
|
+
...selection.providerId === "chatgpt-plan" ? { siwcConnection: getSiwcConnection(env.HOME) } : {}
|
|
3270
|
+
}, policy);
|
|
1975
3271
|
await validateRuntimeModels(runtime);
|
|
1976
3272
|
if (selection.reasoningEffort !== void 0) {
|
|
1977
3273
|
try {
|
|
@@ -1984,9 +3280,14 @@ async function createWebConsoleRuntime(selection = {}) {
|
|
|
1984
3280
|
if (selection.model !== void 0) saveAppliedModelPreference(runtime);
|
|
1985
3281
|
return { runtime, info: describeWebConsoleRuntime(runtime) };
|
|
1986
3282
|
}
|
|
1987
|
-
async function reloadWebConsoleRuntimeConnection(runtime) {
|
|
3283
|
+
async function reloadWebConsoleRuntimeConnection(runtime, policy = {}) {
|
|
1988
3284
|
const providerId = runtime.resolvedProvider();
|
|
1989
3285
|
const model = runtime.resolvedModel();
|
|
3286
|
+
assertPlanAllowed(providerId, policy);
|
|
3287
|
+
if (providerId === "chatgpt-plan") {
|
|
3288
|
+
await validateRuntimeModels(runtime);
|
|
3289
|
+
return describeWebConsoleRuntime(runtime);
|
|
3290
|
+
}
|
|
1990
3291
|
const effort = runtime.reasoningConfiguration()?.effort;
|
|
1991
3292
|
const env = webRuntimeEnv();
|
|
1992
3293
|
const { runtime: candidate } = await createPreferredConsoleRuntime({
|
|
@@ -1994,7 +3295,7 @@ async function reloadWebConsoleRuntimeConnection(runtime) {
|
|
|
1994
3295
|
model,
|
|
1995
3296
|
...runtime.modelSelection(),
|
|
1996
3297
|
env
|
|
1997
|
-
});
|
|
3298
|
+
}, policy);
|
|
1998
3299
|
if (candidate.resolvedProvider() !== providerId || candidate.resolvedModel() !== model) throw new OperatorError(409, "connection_changed", "Reconnect the conversation's selected provider and model first.");
|
|
1999
3300
|
await validateRuntimeModels(candidate);
|
|
2000
3301
|
if (effort !== void 0) candidate.setReasoningEffort(effort);
|
|
@@ -2005,25 +3306,29 @@ async function reloadWebConsoleRuntimeConnection(runtime) {
|
|
|
2005
3306
|
else runtimeContextLimits.delete(runtime);
|
|
2006
3307
|
return describeWebConsoleRuntime(runtime);
|
|
2007
3308
|
}
|
|
2008
|
-
async function applyWebConsoleRuntimeSelection(runtime, selection) {
|
|
3309
|
+
async function applyWebConsoleRuntimeSelection(runtime, selection, policy = {}) {
|
|
2009
3310
|
validateSelection(selection);
|
|
2010
3311
|
const currentProvider = runtime.resolvedProvider();
|
|
2011
3312
|
let providerId = selection.providerId;
|
|
2012
3313
|
if (providerId === void 0 && selection.model !== void 0) {
|
|
2013
3314
|
const inferred = modelProvider(selection.model);
|
|
2014
|
-
if (inferred !== "unknown" && inferred !== currentProvider && !(
|
|
3315
|
+
if (inferred !== "unknown" && inferred !== currentProvider && !(["chatgpt-codex", "chatgpt-plan"].includes(currentProvider) && inferred === "openai")) providerId = inferred;
|
|
2015
3316
|
}
|
|
2016
3317
|
const targetProvider = providerId ?? currentProvider;
|
|
3318
|
+
assertPlanAllowed(targetProvider, policy);
|
|
2017
3319
|
let candidate;
|
|
3320
|
+
let planConnection;
|
|
2018
3321
|
if (targetProvider !== currentProvider) {
|
|
2019
3322
|
const env = webRuntimeEnv();
|
|
3323
|
+
if (targetProvider === "chatgpt-plan") planConnection = getSiwcConnection(env.HOME);
|
|
2020
3324
|
const created = await createPreferredConsoleRuntime({
|
|
2021
3325
|
provider: targetProvider,
|
|
2022
3326
|
model: selection.model,
|
|
2023
3327
|
env,
|
|
2024
3328
|
...runtime.modelSelection(),
|
|
2025
|
-
...selection
|
|
2026
|
-
|
|
3329
|
+
...selection,
|
|
3330
|
+
...planConnection ? { siwcConnection: planConnection } : {}
|
|
3331
|
+
}, policy);
|
|
2027
3332
|
candidate = created.runtime;
|
|
2028
3333
|
await validateRuntimeModels(candidate);
|
|
2029
3334
|
runtimeEnvironments.set(candidate, env);
|
|
@@ -2042,6 +3347,7 @@ async function applyWebConsoleRuntimeSelection(runtime, selection) {
|
|
|
2042
3347
|
runtime.reconfigure({
|
|
2043
3348
|
model: candidate.resolvedModel(),
|
|
2044
3349
|
provider: targetProvider,
|
|
3350
|
+
...planConnection ? { siwcConnection: planConnection } : {},
|
|
2045
3351
|
...candidate.modelSelection(),
|
|
2046
3352
|
...targetProvider !== currentProvider ? { env: runtimeEnvironments.get(candidate) } : {}
|
|
2047
3353
|
});
|
|
@@ -2054,15 +3360,15 @@ async function applyWebConsoleRuntimeSelection(runtime, selection) {
|
|
|
2054
3360
|
return describeWebConsoleRuntime(runtime);
|
|
2055
3361
|
}
|
|
2056
3362
|
var OperatorError = class extends Error {
|
|
2057
|
-
constructor(status, code,
|
|
2058
|
-
super(
|
|
3363
|
+
constructor(status, code, message2) {
|
|
3364
|
+
super(message2);
|
|
2059
3365
|
this.status = status;
|
|
2060
3366
|
this.code = code;
|
|
2061
3367
|
}
|
|
2062
3368
|
status;
|
|
2063
3369
|
code;
|
|
2064
3370
|
};
|
|
2065
|
-
function
|
|
3371
|
+
function object2(input) {
|
|
2066
3372
|
if (!input || typeof input !== "object" || Array.isArray(input)) throw new OperatorError(400, "invalid_input", "Expected a JSON object.");
|
|
2067
3373
|
return input;
|
|
2068
3374
|
}
|
|
@@ -2088,12 +3394,16 @@ function settingsView() {
|
|
|
2088
3394
|
var WebOperatorServices = class {
|
|
2089
3395
|
#servicePlugins = new ServicePluginServices();
|
|
2090
3396
|
#codex = new CodexAuthController();
|
|
3397
|
+
#plan = new ChatGptPlanAuthController();
|
|
3398
|
+
#allowChatGptPlan;
|
|
2091
3399
|
#auth = /* @__PURE__ */ new Map();
|
|
3400
|
+
#catalogRefresh;
|
|
2092
3401
|
#registryItems = /* @__PURE__ */ new Map();
|
|
2093
3402
|
#registryError = null;
|
|
2094
3403
|
#feedback = null;
|
|
2095
3404
|
constructor(options = {}) {
|
|
2096
3405
|
isTurnActive = options.isTurnActive ?? (() => false);
|
|
3406
|
+
this.#allowChatGptPlan = options.allowChatGptPlan !== false;
|
|
2097
3407
|
}
|
|
2098
3408
|
#publicAuth(status) {
|
|
2099
3409
|
return {
|
|
@@ -2103,18 +3413,34 @@ var WebOperatorServices = class {
|
|
|
2103
3413
|
};
|
|
2104
3414
|
}
|
|
2105
3415
|
#authStatus(id, configured) {
|
|
3416
|
+
if (id === "chatgpt-plan") return this.#plan.status();
|
|
2106
3417
|
if (id === "chatgpt-codex") return this.#publicAuth(this.#codex.status());
|
|
2107
3418
|
const flow = this.#auth.get(id);
|
|
2108
3419
|
if (flow?.status.phase === "running") return { ...flow.status, lines: [...flow.status.lines] };
|
|
2109
3420
|
if (configured) return webAuthStatus({ phase: "connected", message: "Credentials are configured. The first request verifies account access.", lines: [] }, Boolean(PROVIDER_DEVICE_AUTH[id]));
|
|
2110
3421
|
if (flow && flow.status.phase !== "connected") return { ...flow.status, lines: [...flow.status.lines] };
|
|
2111
|
-
return webAuthStatus({ phase: "idle", message: "Not connected.", lines: [] }, Boolean(PROVIDER_DEVICE_AUTH[id]));
|
|
3422
|
+
return webAuthStatus({ phase: "idle", message: PROVIDERS.find((p) => p.id === id)?.hint ?? "Not connected.", lines: [] }, Boolean(PROVIDER_DEVICE_AUTH[id]));
|
|
2112
3423
|
}
|
|
2113
3424
|
#providers() {
|
|
2114
3425
|
const env = webRuntimeEnv();
|
|
2115
3426
|
const store = loadAccountStore();
|
|
2116
3427
|
const configs = loadConnectionConfigs();
|
|
2117
|
-
return PROVIDERS.map((info) => {
|
|
3428
|
+
return PROVIDERS.filter((info) => this.#allowChatGptPlan || info.id !== "chatgpt-plan").map((info) => {
|
|
3429
|
+
if (info.id === "chatgpt-plan") {
|
|
3430
|
+
const profiles = listChatGptPlanProfiles();
|
|
3431
|
+
const active = profiles.find((p) => p.active && p.status === "connected");
|
|
3432
|
+
return {
|
|
3433
|
+
id: info.id,
|
|
3434
|
+
label: info.label,
|
|
3435
|
+
methods: info.methods,
|
|
3436
|
+
configured: Boolean(active),
|
|
3437
|
+
source: active ? "account-store" : null,
|
|
3438
|
+
diagnostics: { valid: Boolean(active?.sharing), reason: active?.sharing ? null : active ? "plan_sharing_required" : "missing_connection", message: active?.sharing ? null : active ? "ChatGPT identity is connected. Grant plan usage to use models." : "Connect your ChatGPT account." },
|
|
3439
|
+
accounts: profiles.map((p) => ({ accountId: p.id, kind: "oauth", active: p.active, label: p.label, status: p.status, sharing: p.sharing })),
|
|
3440
|
+
configuration: {},
|
|
3441
|
+
auth: this.#plan.status()
|
|
3442
|
+
};
|
|
3443
|
+
}
|
|
2118
3444
|
let diagnostics = {
|
|
2119
3445
|
valid: false,
|
|
2120
3446
|
reason: "missing_key",
|
|
@@ -2139,7 +3465,7 @@ var WebOperatorServices = class {
|
|
|
2139
3465
|
return {
|
|
2140
3466
|
id: info.id,
|
|
2141
3467
|
label: info.id === "openai" ? "OpenAI / Custom AI Provider" : info.label,
|
|
2142
|
-
methods: info.methods,
|
|
3468
|
+
methods: info.methods.includes("api-key") && !PROVIDER_DEVICE_AUTH[info.id] ? info.methods.filter((method) => method !== "oauth") : info.methods,
|
|
2143
3469
|
configured,
|
|
2144
3470
|
source: configured ? environment ? "environment" : store.providers[info.id] ? "account-store" : "codex-file" : null,
|
|
2145
3471
|
diagnostics,
|
|
@@ -2151,16 +3477,23 @@ var WebOperatorServices = class {
|
|
|
2151
3477
|
}
|
|
2152
3478
|
async #models(providerId) {
|
|
2153
3479
|
if (providerId !== void 0) providerId = provider(providerId).id;
|
|
3480
|
+
assertPlanAllowed(providerId, { allowChatGptPlan: this.#allowChatGptPlan });
|
|
2154
3481
|
const env = webRuntimeEnv();
|
|
2155
3482
|
const ids = providerStates(env).filter((state) => state.configured && (!providerId || state.id === providerId)).map((state) => state.id);
|
|
2156
|
-
|
|
3483
|
+
if (this.#allowChatGptPlan && (!providerId || providerId === "chatgpt-plan") && listChatGptPlanProfiles().some((p) => p.active && p.status === "connected" && p.sharing)) ids.push("chatgpt-plan");
|
|
3484
|
+
if (!this.#catalogRefresh) {
|
|
3485
|
+
this.#catalogRefresh = Promise.resolve().then(() => syncModelCatalog()).catch(() => null).finally(() => {
|
|
3486
|
+
this.#catalogRefresh = void 0;
|
|
3487
|
+
});
|
|
3488
|
+
}
|
|
3489
|
+
const discovered = await discoverConnectionModels(env, ids);
|
|
2157
3490
|
const metadata = new Map(buildFullModelCatalog().map((model) => [`${model.provider}/${model.id}`, model]));
|
|
2158
3491
|
const rows = discovered.models.map((model) => ({
|
|
2159
3492
|
id: model.id,
|
|
2160
3493
|
provider: model.provider,
|
|
2161
|
-
price: model.provider === "chatgpt-codex" ? "Included in subscription" : model.provider === "cline" && model.id.startsWith("cline-pass/") ? "ClinePass plan (access unverified)" : metadata.get(`${model.provider}/${model.id}`)?.price ?? "Unknown rate",
|
|
3494
|
+
price: model.provider === "chatgpt-plan" ? "Shared ChatGPT allowance" : model.provider === "chatgpt-codex" ? "Included in subscription" : model.provider === "cline" && model.id.startsWith("cline-pass/") ? "ClinePass plan (access unverified)" : metadata.get(`${model.provider}/${model.id}`)?.price ?? "Unknown rate",
|
|
2162
3495
|
contextWindowTokens: model.contextTokens ?? resolveContextLimit({ modelId: model.id, providerId: model.provider })?.tokens ?? null,
|
|
2163
|
-
source: model.source === "catalog" ? "public-catalog" : "account"
|
|
3496
|
+
source: model.provider !== "chatgpt-plan" && model.source === "catalog" ? "public-catalog" : "account"
|
|
2164
3497
|
}));
|
|
2165
3498
|
const configuredModel = env.OPENAI_MODEL;
|
|
2166
3499
|
if (configuredModel && ids.includes("openai") && !rows.some((row) => row.provider === "openai" && row.id === configuredModel)) {
|
|
@@ -2174,9 +3507,20 @@ var WebOperatorServices = class {
|
|
|
2174
3507
|
};
|
|
2175
3508
|
}
|
|
2176
3509
|
async #connections(input) {
|
|
2177
|
-
const body =
|
|
3510
|
+
const body = object2(input);
|
|
2178
3511
|
const info = provider(body.providerId);
|
|
2179
3512
|
const action = text(body.action, "connection action", 32);
|
|
3513
|
+
assertPlanAllowed(info.id, { allowChatGptPlan: this.#allowChatGptPlan });
|
|
3514
|
+
if (info.id === "chatgpt-plan") {
|
|
3515
|
+
const id = text(body.accountId, "account ID", 128);
|
|
3516
|
+
if (action === "activate") await selectChatGptPlanProfile(id);
|
|
3517
|
+
else if (action === "disconnect") {
|
|
3518
|
+
this.#plan.cancel();
|
|
3519
|
+
const result = await signOutChatGptPlan(id);
|
|
3520
|
+
return { providers: this.#providers(), revoked: result.revoked, message: result.revoked ? "Signed out locally and provider revocation completed." : "Signed out locally. Provider revocation could not be confirmed; review the connection in ChatGPT Settings." };
|
|
3521
|
+
} else throw new OperatorError(400, "unsupported_auth", "Use native ChatGPT browser sign-in.");
|
|
3522
|
+
return { providers: this.#providers() };
|
|
3523
|
+
}
|
|
2180
3524
|
if (action === "configure") {
|
|
2181
3525
|
const config = validateConnectionConfig(info.id, {
|
|
2182
3526
|
...body.baseUrl !== void 0 ? { baseUrl: text(body.baseUrl, "API endpoint") } : {},
|
|
@@ -2215,11 +3559,17 @@ var WebOperatorServices = class {
|
|
|
2215
3559
|
if (!saveAccountStore(store)) throw new OperatorError(500, "persistence_failed", "Could not save the private credential account store.");
|
|
2216
3560
|
return { providers: this.#providers() };
|
|
2217
3561
|
}
|
|
2218
|
-
#deviceAuth(id, method) {
|
|
3562
|
+
#deviceAuth(id, method, input) {
|
|
2219
3563
|
const info = provider(id);
|
|
3564
|
+
assertPlanAllowed(info.id, { allowChatGptPlan: this.#allowChatGptPlan });
|
|
3565
|
+
if (info.id === "chatgpt-plan") {
|
|
3566
|
+
const body = input === void 0 ? {} : object2(input);
|
|
3567
|
+
const profileId = body.profileId === void 0 ? void 0 : text(body.profileId, "profile ID", 128);
|
|
3568
|
+
return { status: method === "DELETE" ? this.#plan.cancel() : this.#plan.start(profileId) };
|
|
3569
|
+
}
|
|
2220
3570
|
if (info.id === "chatgpt-codex") return { status: this.#publicAuth(method === "DELETE" ? this.#codex.cancel() : this.#codex.start()) };
|
|
2221
3571
|
const config = PROVIDER_DEVICE_AUTH[info.id];
|
|
2222
|
-
if (!config) throw new OperatorError(409, "auth_unavailable",
|
|
3572
|
+
if (!config) throw new OperatorError(409, "auth_unavailable", info.hint);
|
|
2223
3573
|
const previous = this.#auth.get(info.id);
|
|
2224
3574
|
if (method === "DELETE") {
|
|
2225
3575
|
previous?.session?.cancel();
|
|
@@ -2314,7 +3664,7 @@ var WebOperatorServices = class {
|
|
|
2314
3664
|
}
|
|
2315
3665
|
async #pluginAction(action, input) {
|
|
2316
3666
|
if (action === "run" && consoleExecutionProfile() === "smolvm") throw new OperatorError(409, "isolated_execution_required", "Plugins load automatically inside new SmolVM chats. Start a new chat to use an approved plugin.");
|
|
2317
|
-
const body =
|
|
3667
|
+
const body = object2(input);
|
|
2318
3668
|
const id = text(body.id, "plugin ID", 64);
|
|
2319
3669
|
if (action === "install") {
|
|
2320
3670
|
const kind = body.kind === "theme" ? "theme" : "plugin";
|
|
@@ -2355,7 +3705,7 @@ var WebOperatorServices = class {
|
|
|
2355
3705
|
const path = pathname.slice("/api/console/".length);
|
|
2356
3706
|
const servicePluginResult = await this.#servicePlugins.handle(path, method, input);
|
|
2357
3707
|
if (servicePluginResult) return servicePluginResult;
|
|
2358
|
-
if (!/^(providers(?:\/[^/]+(?:\/device-auth)?)?|models|connections|settings(?:\/reset)?|themes|plugins(?:\/(?:install|enable|disable|run))?|doctor|tools|project|checks|feedback)$/.test(path)) return null;
|
|
3708
|
+
if (!/^(providers(?:\/[^/]+(?:\/device-auth)?)?|models|connections|settings(?:\/reset)?|themes|plugins(?:\/(?:install|enable|disable|run))?|doctor|tools|project(?:\/(?:directories|choose-directory))?|checks|feedback)$/.test(path)) return null;
|
|
2359
3709
|
try {
|
|
2360
3710
|
let data;
|
|
2361
3711
|
let status = 200;
|
|
@@ -2363,15 +3713,16 @@ var WebOperatorServices = class {
|
|
|
2363
3713
|
else if (path === "models" && method === "GET") data = await this.#models(query.get("providerId") ?? void 0);
|
|
2364
3714
|
else if (path === "connections" && method === "POST") data = await this.#connections(input);
|
|
2365
3715
|
else if (/^providers\/[^/]+\/device-auth$/.test(path) && ["POST", "DELETE"].includes(method)) {
|
|
2366
|
-
data = this.#deviceAuth(decodeURIComponent(path.split("/")[1]), method);
|
|
3716
|
+
data = this.#deviceAuth(decodeURIComponent(path.split("/")[1]), method, input);
|
|
2367
3717
|
status = method === "POST" ? 202 : 200;
|
|
2368
3718
|
} else if (/^providers\/[^/]+$/.test(path) && method === "GET") {
|
|
2369
3719
|
const info = provider(decodeURIComponent(path.split("/")[1]));
|
|
3720
|
+
assertPlanAllowed(info.id, { allowChatGptPlan: this.#allowChatGptPlan });
|
|
2370
3721
|
const state = this.#providers().find((entry) => entry.id === info.id);
|
|
2371
3722
|
data = { provider: state, status: state.auth };
|
|
2372
3723
|
} else if (path === "settings" && method === "GET") data = settingsView();
|
|
2373
3724
|
else if (path === "settings" && method === "PATCH") {
|
|
2374
|
-
const body =
|
|
3725
|
+
const body = object2(input);
|
|
2375
3726
|
const key = text(body.key, "setting", 128);
|
|
2376
3727
|
const def = SETTING_DEFS.find((entry) => entry.key === key);
|
|
2377
3728
|
const internalBoolean = key === "onboardingCompleted" || key === "diagnosticReportingPrompted";
|
|
@@ -2383,7 +3734,7 @@ var WebOperatorServices = class {
|
|
|
2383
3734
|
data = { ...settingsView(), persisted, ...!persisted ? { error: "Settings changed in this process but could not be saved to disk.", code: "persistence_failed" } : {} };
|
|
2384
3735
|
if (!persisted) status = 507;
|
|
2385
3736
|
} else if (path === "settings/reset" && method === "POST") {
|
|
2386
|
-
const body =
|
|
3737
|
+
const body = object2(input);
|
|
2387
3738
|
const keys = body.keys ?? SETTING_DEFS.map((def) => def.key);
|
|
2388
3739
|
if (!Array.isArray(keys) || keys.some((key) => typeof key !== "string" || !SETTING_DEFS.some((def) => def.key === key))) throw new OperatorError(400, "invalid_setting", "Reset keys must name declared settings.");
|
|
2389
3740
|
const persisted = resetSettings(DEFAULT_SETTINGS, keys);
|
|
@@ -2408,8 +3759,12 @@ var WebOperatorServices = class {
|
|
|
2408
3759
|
} else if (path === "tools" && method === "GET") {
|
|
2409
3760
|
const roleTools = Object.fromEntries(MODEL_ROLES.map((role) => [role, new Set(getToolsForRole(role, { hasScope: true, webMode: true, hasBrowser: true, allowScanners: true }).map((tool) => tool.name))]));
|
|
2410
3761
|
data = { roles: MODEL_ROLES, tools: Object.values(TOOL_DEFINITIONS).map(({ name, description }) => ({ name, description, roles: MODEL_ROLES.filter((role) => roleTools[role].has(name)) })), plugins: pluginManager?.current().toolDefinitions().map(({ name, description }) => ({ name, description })) ?? [] };
|
|
3762
|
+
} else if (path === "project/choose-directory" && method === "POST") {
|
|
3763
|
+
data = await chooseWorkspaceFolder(object2(input).path);
|
|
3764
|
+
} else if (path === "project/directories" && method === "POST") {
|
|
3765
|
+
data = await listWorkspaceDirectories(object2(input).path);
|
|
2411
3766
|
} else if (path === "project" && method === "GET") {
|
|
2412
|
-
const projectPath = realpathSync(
|
|
3767
|
+
const projectPath = realpathSync(resolve3(query.get("path") ?? process.cwd()));
|
|
2413
3768
|
if (!statSync(projectPath).isDirectory()) throw new OperatorError(400, "invalid_project", "Project path must name a local directory.");
|
|
2414
3769
|
const git = { root: null, branch: null, dirty: false, error: null };
|
|
2415
3770
|
try {
|
|
@@ -2422,15 +3777,15 @@ var WebOperatorServices = class {
|
|
|
2422
3777
|
data = { path: projectPath, name: basename(projectPath), git };
|
|
2423
3778
|
} else if (path === "checks" && method === "GET") data = listProjectReviewChecks(query.get("path") ?? process.cwd());
|
|
2424
3779
|
else if (path === "checks" && method === "POST") {
|
|
2425
|
-
const body =
|
|
2426
|
-
const mutation =
|
|
3780
|
+
const body = object2(input);
|
|
3781
|
+
const mutation = object2(body.mutation);
|
|
2427
3782
|
const action = text(mutation.action, "review check action", 32);
|
|
2428
3783
|
if (!["propose", "add", "enable", "disable", "set", "remove"].includes(action)) throw new OperatorError(400, "invalid_action", "Unsupported review check action.");
|
|
2429
3784
|
if (mutation.approved !== void 0 && typeof mutation.approved !== "boolean") throw new OperatorError(400, "invalid_approval", "Approval must be an explicit boolean.");
|
|
2430
3785
|
if (mutation.expectedRevision !== void 0 && (!Number.isSafeInteger(mutation.expectedRevision) || Number(mutation.expectedRevision) < 1)) throw new OperatorError(400, "invalid_revision", "Expected revision must be a positive integer.");
|
|
2431
3786
|
data = updateProjectReviewChecks(body.path === void 0 ? process.cwd() : text(body.path, "project path"), mutation);
|
|
2432
3787
|
} else if (path === "feedback" && method === "POST") {
|
|
2433
|
-
const body =
|
|
3788
|
+
const body = object2(input);
|
|
2434
3789
|
if (body.action === "cancel") {
|
|
2435
3790
|
this.#feedback = null;
|
|
2436
3791
|
data = { cancelled: true, submitted: false };
|
|
@@ -2449,7 +3804,7 @@ var WebOperatorServices = class {
|
|
|
2449
3804
|
if (body.submit === true) {
|
|
2450
3805
|
const preview = buildSubmitPreview(payload);
|
|
2451
3806
|
if (!preview) throw new OperatorError(409, "submission_unavailable", "Local feedback was saved, but reporting is opted out or no supported HTTPS destination is available.");
|
|
2452
|
-
const id =
|
|
3807
|
+
const id = randomUUID3();
|
|
2453
3808
|
this.#feedback = { id, payload, preview };
|
|
2454
3809
|
data = { saved: true, path: saved.path, submitted: false, previewId: id, preview: { ...preview, url: publicMessage(preview.url) } };
|
|
2455
3810
|
} else data = { saved: true, path: saved.path, submitted: false };
|
|
@@ -2461,6 +3816,7 @@ var WebOperatorServices = class {
|
|
|
2461
3816
|
}
|
|
2462
3817
|
}
|
|
2463
3818
|
dispose() {
|
|
3819
|
+
this.#plan.cancel();
|
|
2464
3820
|
this.#codex.cancel();
|
|
2465
3821
|
for (const flow of this.#auth.values()) flow.session?.cancel();
|
|
2466
3822
|
pluginManager?.dispose();
|
|
@@ -2470,6 +3826,8 @@ var WebOperatorServices = class {
|
|
|
2470
3826
|
export {
|
|
2471
3827
|
sanitizeComposerText,
|
|
2472
3828
|
createSessionPluginHostManager,
|
|
3829
|
+
jwtVerify,
|
|
3830
|
+
createRemoteJWKSet,
|
|
2473
3831
|
getWebConsolePluginHostManager,
|
|
2474
3832
|
flushWebConsolePlugins,
|
|
2475
3833
|
describeWebConsoleRuntime,
|