autohand-cli 0.8.3 → 0.9.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +191 -74
- package/dist/{AgentRegistry-ODDXPAFR.js → AgentRegistry-GOV7FN2O.js} +2 -2
- package/dist/AgentRegistry-J2C6JBZ3.cjs +10 -0
- package/dist/{AutomodeManager-EOVHGRPP.js → AutomodeManager-APLLPEYJ.js} +31 -17
- package/dist/{AutomodeManager-6ATBE7Q5.cjs → AutomodeManager-MPSXT2RH.cjs} +38 -24
- package/dist/CommunitySkillsCache-22BB3J6H.cjs +8 -0
- package/dist/{CommunitySkillsCache-QLE57BN5.js → CommunitySkillsCache-ZACDPD4J.js} +2 -2
- package/dist/GitHubRegistryFetcher-CYJLF2XL.cjs +7 -0
- package/dist/{GitHubRegistryFetcher-NT5GFZXS.js → GitHubRegistryFetcher-J2BWPXJF.js} +1 -1
- package/dist/{HookManager-B2F35M27.js → HookManager-2S6AHOC3.js} +1 -1
- package/dist/{HookManager-PFAFE3FK.cjs → HookManager-TVEQI5ZU.cjs} +2 -2
- package/dist/{ImportWizard-OHRKBANZ.js → ImportWizard-AEBMAKXE.js} +33 -9
- package/dist/{ImportWizard-CJRZPPHL.cjs → ImportWizard-AF2XL6DS.cjs} +37 -13
- package/dist/LearnAdvisor-KKEQ5QCV.js +9 -0
- package/dist/LearnAdvisor-Q7RAYLPF.cjs +9 -0
- package/dist/{McpClientManager-NQ3EW2IF.cjs → McpClientManager-BNSKLHIN.cjs} +2 -2
- package/dist/{McpClientManager-VBIMGKXU.js → McpClientManager-SL35BR24.js} +1 -1
- package/dist/MemoryManager-PMNAEZMB.cjs +8 -0
- package/dist/{MemoryManager-2LAT7IHS.js → MemoryManager-QZRLTUB5.js} +2 -2
- package/dist/NVIDIAProvider-C3FWQOBZ.cjs +14 -0
- package/dist/NVIDIAProvider-SJPY3ONR.js +14 -0
- package/dist/{PermissionManager-7NQHRCHY.js → PermissionManager-MT7KBNNR.js} +3 -3
- package/dist/PermissionManager-URCNP5EC.cjs +11 -0
- package/dist/{ProjectProfiler-UG42W6WD.cjs → ProjectProfiler-CDAE7ECW.cjs} +1 -0
- package/dist/{ProjectProfiler-P4QJQWWA.js → ProjectProfiler-NZTJDRHD.js} +1 -0
- package/dist/ProviderFactory-56N3ECE4.cjs +12 -0
- package/dist/{ProviderFactory-2IYJ5OPW.js → ProviderFactory-RB4RVAF6.js} +4 -1
- package/dist/SessionManager-2IZGWASQ.cjs +10 -0
- package/dist/{SessionManager-4U4JFQ3C.js → SessionManager-554PHVPC.js} +2 -2
- package/dist/SkillsRegistry-GMRVONOQ.cjs +9 -0
- package/dist/{SkillsRegistry-RFEINXRT.js → SkillsRegistry-NHHMIUYJ.js} +2 -2
- package/dist/SubAgent-4GOR2FIJ.js +13 -0
- package/dist/SubAgent-ZJMDH4FI.cjs +13 -0
- package/dist/{SyncApiClient-B5RT2ECG.js → SyncApiClient-G3FM65IV.js} +1 -1
- package/dist/SyncApiClient-ODBDECOG.cjs +11 -0
- package/dist/about-3USFSGOH.js +15 -0
- package/dist/about-QR52U5DD.cjs +15 -0
- package/dist/acp-5Y2W6OZP.js +1556 -0
- package/dist/acp-GSYE75OB.cjs +1556 -0
- package/dist/actionExecutor-KHUU3Z3Q.cjs +28 -0
- package/dist/actionExecutor-PLQJZLZV.js +28 -0
- package/dist/add-dir-BTAD5OZS.cjs +11 -0
- package/dist/{add-dir-TUJM3PG5.js → add-dir-HCERJDBR.js} +3 -2
- package/dist/agent-B3EVT7NN.cjs +126 -0
- package/dist/agent-SATQ3WZ3.js +126 -0
- package/dist/agents/builtin/code-cleaner.md +1 -1
- package/dist/agents/builtin/docs-writer.md +1 -1
- package/dist/agents/builtin/researcher.md +2 -2
- package/dist/agents/builtin/reviewer.md +1 -1
- package/dist/agents/builtin/tester.md +1 -1
- package/dist/agents/builtin/todo-resolver.md +1 -1
- package/dist/agents-44CRJGM7.cjs +18 -0
- package/dist/agents-NN4ZAJ7R.js +18 -0
- package/dist/agents-new-6D6G4QS6.cjs +17 -0
- package/dist/agents-new-7GAQ25EK.js +17 -0
- package/dist/{autoSkill-LQEVQFIH.js → autoSkill-66PKCYTW.js} +2 -2
- package/dist/autoSkill-VQLGBELN.cjs +20 -0
- package/dist/automode-45DWV4TN.cjs +10 -0
- package/dist/{automode-R6P3VHLS.js → automode-BUF3VGXU.js} +2 -2
- package/dist/browserToolBridge-BXRQB4B4.cjs +58 -0
- package/dist/browserToolBridge-F5N66PE7.js +58 -0
- package/dist/chrome-Q2U73HZB.cjs +50 -0
- package/dist/chrome-Q54OALOK.cjs +21 -0
- package/dist/chrome-Y3T7EIHG.js +50 -0
- package/dist/chrome-ZNJ46OYW.js +21 -0
- package/dist/chromeSkill-53TH55PM.js +105 -0
- package/dist/chromeSkill-THW7N4IY.cjs +105 -0
- package/dist/{chunk-HXGBSJL5.cjs → chunk-22SCR3EV.cjs} +2 -2
- package/dist/chunk-26G7YGOW.js +367 -0
- package/dist/{chunk-3L53OA4E.cjs → chunk-2ESQR35E.cjs} +10 -10
- package/dist/chunk-2H5O745H.js +63 -0
- package/dist/{chunk-ULDM2SNB.js → chunk-2IW5YFJ4.js} +48 -47
- package/dist/{chunk-U55TWFJI.cjs → chunk-2VHB43IX.cjs} +24 -6
- package/dist/chunk-3EDDDZS2.cjs +5 -0
- package/dist/{chunk-X5VSP65C.cjs → chunk-3ICORF7M.cjs} +4 -4
- package/dist/chunk-3OF56EMA.cjs +197 -0
- package/dist/chunk-3UC6DMFI.cjs +93 -0
- package/dist/{chunk-64H4FRM3.cjs → chunk-3UHRCHKB.cjs} +32 -34
- package/dist/{chunk-JCLYQ2JC.js → chunk-3ZOOH44N.js} +12 -6
- package/dist/{chunk-TUD3Z3BD.js → chunk-44ZWOBTI.js} +136 -55
- package/dist/chunk-4567QNRB.js +444 -0
- package/dist/chunk-45CHNQM6.cjs +517 -0
- package/dist/chunk-46MTALKD.js +44 -0
- package/dist/{chunk-WHE2SWHU.js → chunk-4E7SHPNQ.js} +2 -2
- package/dist/chunk-4LMUDS2K.js +124 -0
- package/dist/{chunk-W7RYQJLO.cjs → chunk-4XFHP2SN.cjs} +46 -15
- package/dist/chunk-4XVLNOFA.js +360 -0
- package/dist/{chunk-GBHDROGL.js → chunk-575PXZX7.js} +16 -4
- package/dist/chunk-5A7ZL64N.js +139 -0
- package/dist/chunk-5GCQ5UMV.cjs +85 -0
- package/dist/chunk-5JTTM5SC.js +59 -0
- package/dist/{chunk-IETRBBMP.cjs → chunk-5K3CDSWZ.cjs} +108 -31
- package/dist/{chunk-KLWJIPU2.cjs → chunk-5SFV2NCY.cjs} +20 -16
- package/dist/chunk-5ZNYX4VH.cjs +71 -0
- package/dist/{chunk-A4IJHHV7.cjs → chunk-63ANVAOJ.cjs} +52 -11
- package/dist/{chunk-MYISNQH4.js → chunk-65LRPW5W.js} +1 -1
- package/dist/chunk-6AYJCDTA.js +360 -0
- package/dist/{chunk-X2MSVKDV.js → chunk-6HF6XSCC.js} +2 -2
- package/dist/{chunk-AEJH23FO.cjs → chunk-6JWCGQJU.cjs} +6 -6
- package/dist/chunk-6OECOAK5.cjs +360 -0
- package/dist/chunk-6R25D2H5.js +121 -0
- package/dist/chunk-7DJJGKM6.js +77 -0
- package/dist/{chunk-VG34MG2U.js → chunk-7IRQ7JEH.js} +9 -6
- package/dist/chunk-7MY4JHER.js +27 -0
- package/dist/chunk-7PJKTSRN.js +152 -0
- package/dist/{chunk-SKYG33B2.cjs → chunk-7PT646AL.cjs} +3 -3
- package/dist/chunk-7WAUCLLQ.js +229 -0
- package/dist/{chunk-U46VYPLR.cjs → chunk-7XTUL7ZD.cjs} +9 -9
- package/dist/chunk-AE2ILW25.cjs +152 -0
- package/dist/{chunk-T2M64VHA.cjs → chunk-AOKXYGBQ.cjs} +61 -41
- package/dist/chunk-AQ5PVHOC.cjs +113 -0
- package/dist/{chunk-N2BLVUPM.cjs → chunk-AZ47LKD5.cjs} +3 -3
- package/dist/chunk-B4HSNOIH.cjs +354 -0
- package/dist/chunk-B76FJKEJ.cjs +184 -0
- package/dist/chunk-BP37FVBM.js +85 -0
- package/dist/chunk-BQU3HAE7.js +21 -0
- package/dist/chunk-BYE7RQFZ.cjs +121 -0
- package/dist/chunk-C46B72VK.js +1034 -0
- package/dist/{chunk-HIVRCQS2.js → chunk-C5QIQQ37.js} +56 -25
- package/dist/chunk-CCIDL4SA.js +19931 -0
- package/dist/{chunk-XVUHNWMX.js → chunk-CECH6QQP.js} +17 -19
- package/dist/chunk-CFAWTLSC.js +13 -0
- package/dist/{chunk-ZYQMLKOK.cjs → chunk-CK4HAHVY.cjs} +212 -70
- package/dist/chunk-CO2AADYU.cjs +360 -0
- package/dist/chunk-CRTCZ4WL.cjs +19931 -0
- package/dist/chunk-CSTUTDTH.js +75 -0
- package/dist/{chunk-K2C56QGS.cjs → chunk-D546G7DA.cjs} +1650 -479
- package/dist/{chunk-CNBKZEX5.cjs → chunk-DA7NBAJK.cjs} +49 -17
- package/dist/{chunk-G4CAKI3V.js → chunk-DGBPWODD.js} +7 -2
- package/dist/chunk-DHUQZ53B.cjs +29 -0
- package/dist/{chunk-RGR6ME5J.cjs → chunk-DI57A4BX.cjs} +25 -28
- package/dist/{chunk-5K6NGAVM.js → chunk-DNSD7DVJ.js} +67 -52
- package/dist/{chunk-RDF37HKB.cjs → chunk-DSSCERHI.cjs} +3 -2
- package/dist/{chunk-NAJ4IZKD.cjs → chunk-EA2MS25U.cjs} +8 -6
- package/dist/{chunk-SJAVBCOA.js → chunk-EEHWALXA.js} +94 -3
- package/dist/chunk-ES2HJQ4N.js +37 -0
- package/dist/{chunk-G27PQQFD.js → chunk-EYHKL4QC.js} +1 -1
- package/dist/{chunk-SV2WA57F.js → chunk-FBQJZKFX.js} +38 -15
- package/dist/chunk-FFBDRUO5.cjs +59 -0
- package/dist/{chunk-N7LI55V4.js → chunk-FFHSCM5M.js} +15 -8
- package/dist/{chunk-SAHBLB3E.js → chunk-FP46B4X3.js} +208 -3
- package/dist/chunk-FQVG6ZHF.js +197 -0
- package/dist/{chunk-J2GSFVUV.cjs → chunk-GAOPJ5S3.cjs} +74 -59
- package/dist/{chunk-S52YW5ZQ.js → chunk-GNBBIAMD.js} +16 -19
- package/dist/chunk-GSDV75A5.js +715 -0
- package/dist/chunk-GWY26SUD.cjs +63 -0
- package/dist/{chunk-TUAITHWL.js → chunk-H426KOGY.js} +2 -1
- package/dist/{chunk-EZMINVLU.js → chunk-H4D2Q2AL.js} +5 -2
- package/dist/chunk-HC2Q6A3E.cjs +4967 -0
- package/dist/{chunk-3WVJEL7K.cjs → chunk-HDK2EHVH.cjs} +3 -1
- package/dist/{chunk-5IXII4HX.cjs → chunk-HG3ZASBH.cjs} +23 -16
- package/dist/chunk-HK5A54AV.cjs +280 -0
- package/dist/{chunk-LQGVEP3E.js → chunk-HQUSEWT4.js} +45 -13
- package/dist/chunk-HSWWM6AM.js +2340 -0
- package/dist/chunk-HTU4W3GB.js +183 -0
- package/dist/{chunk-HLHTG5ZU.cjs → chunk-HVCIB4SW.cjs} +14 -12
- package/dist/chunk-HXHE2KAO.js +385 -0
- package/dist/{chunk-LJFUXC56.cjs → chunk-HXQREVTA.cjs} +9 -6
- package/dist/{chunk-SLQAYV3W.js → chunk-HZWU6IO3.js} +11 -2
- package/dist/chunk-I2ZK3AOD.js +866 -0
- package/dist/chunk-IEPLQ47J.js +719 -0
- package/dist/chunk-J5HE6CUM.cjs +124 -0
- package/dist/{chunk-LIEXWM2M.js → chunk-JAQO6XDB.js} +19 -1
- package/dist/chunk-JB3JCYBJ.js +113 -0
- package/dist/chunk-JCUHCH37.cjs +247 -0
- package/dist/{chunk-FPHU2ES6.cjs → chunk-JM3ZTQUK.cjs} +19 -9
- package/dist/{chunk-NDMIPTV4.js → chunk-JMJRFSNJ.js} +8 -3
- package/dist/{chunk-ZLSGXMGQ.js → chunk-JMN3GZU6.js} +2 -0
- package/dist/chunk-JPFRI4L4.js +4967 -0
- package/dist/{chunk-BVKXEQVG.cjs → chunk-JV5HVCHU.cjs} +22 -10
- package/dist/chunk-KA2U6MY2.js +388 -0
- package/dist/{chunk-SEKD5FH3.cjs → chunk-KASXKDF2.cjs} +8 -3
- package/dist/chunk-KJ26T7GM.cjs +367 -0
- package/dist/chunk-KM7R5YPS.cjs +385 -0
- package/dist/{chunk-MBBY4ZIK.js → chunk-KMP6Y3LJ.js} +4 -1
- package/dist/chunk-KWXGDSYT.js +184 -0
- package/dist/{chunk-C5IJIM2V.cjs → chunk-L46OUKW5.cjs} +68 -37
- package/dist/{chunk-6UJMCWRY.js → chunk-L6OH44HL.js} +19 -9
- package/dist/chunk-LANGD5CO.js +280 -0
- package/dist/{chunk-APIXPPMT.js → chunk-LEZEWKPA.js} +1004 -39
- package/dist/{chunk-ULQ6MDSJ.cjs → chunk-LFDPTJYF.cjs} +43 -35
- package/dist/chunk-LJD7KR3L.cjs +44 -0
- package/dist/{chunk-NCC6ETZS.js → chunk-LUMV3DB2.js} +41 -33
- package/dist/{chunk-QXH5RCFI.js → chunk-M5DK524Z.js} +2 -2
- package/dist/chunk-M7B7RRBK.cjs +1034 -0
- package/dist/chunk-MANJ67D3.js +3897 -0
- package/dist/{chunk-HPHJ73GU.cjs → chunk-MGNXABSU.cjs} +9 -9
- package/dist/{chunk-6ZCULLCA.js → chunk-MMKZSJUN.js} +1 -1
- package/dist/{chunk-DVUHHH3B.cjs → chunk-MQQH4XQH.cjs} +4 -4
- package/dist/chunk-MUNUUFU7.cjs +21 -0
- package/dist/{chunk-DVZOENQ7.cjs → chunk-MZN6JBRZ.cjs} +9 -4
- package/dist/{chunk-OOKY3HPZ.js → chunk-NB6M6ESM.js} +50 -9
- package/dist/{chunk-BYONM7UM.js → chunk-NBGOIFKP.js} +265 -18
- package/dist/chunk-NHWHEW7M.cjs +75 -0
- package/dist/chunk-NQZH5CR2.cjs +468 -0
- package/dist/{chunk-EGPXJERY.cjs → chunk-NV6LCD2I.cjs} +119 -28
- package/dist/chunk-NVATU4V6.cjs +44 -0
- package/dist/{chunk-YZXUDM5X.js → chunk-NZ3ZWLUW.js} +204 -62
- package/dist/chunk-NZ7GCK7Y.cjs +92 -0
- package/dist/{chunk-VEKDGU2Q.cjs → chunk-NZQFKXM3.cjs} +49 -25
- package/dist/chunk-O5GI6SR3.js +59 -0
- package/dist/{chunk-56SWIDEL.cjs → chunk-OCATAKXV.cjs} +57 -56
- package/dist/{chunk-SKV2F3NM.js → chunk-OIGYWOL6.js} +1 -1
- package/dist/{chunk-IFFXSTOM.cjs → chunk-ONVYXTUT.cjs} +3 -3
- package/dist/chunk-OR67YXQK.cjs +13 -0
- package/dist/chunk-OV3T5MUH.cjs +715 -0
- package/dist/{chunk-OHUZKDGX.js → chunk-P4FUTSVK.js} +3 -3
- package/dist/chunk-P6SLT2F4.cjs +139 -0
- package/dist/{chunk-TNZRZQ7Q.js → chunk-PB7W7R72.js} +3 -78
- package/dist/chunk-PCM3N3CL.cjs +37 -0
- package/dist/{chunk-2AA5MFES.js → chunk-PGFLPURU.js} +8 -5
- package/dist/{chunk-DJDE4DTT.cjs → chunk-PIXQ2AVM.cjs} +25 -19
- package/dist/chunk-PNIAIOMZ.cjs +229 -0
- package/dist/{chunk-HTLINWX6.cjs → chunk-PQBYFEBL.cjs} +16 -13
- package/dist/{chunk-22D2CNTP.cjs → chunk-PSAH4ZQB.cjs} +5 -2
- package/dist/{chunk-3VTAFAL2.js → chunk-PTTZI4QZ.js} +16 -12
- package/dist/{chunk-WPVWQSL7.cjs → chunk-PY73W5MQ.cjs} +16 -13
- package/dist/{chunk-EGMZDTSL.js → chunk-PZZMIYII.js} +10 -2
- package/dist/{chunk-HBZU3RBZ.js → chunk-Q3BS6FPM.js} +34 -10
- package/dist/chunk-QBYGMMDD.js +517 -0
- package/dist/chunk-QLZOMZO5.cjs +388 -0
- package/dist/{chunk-ADUFCS4Q.cjs → chunk-QQ64HEHP.cjs} +160 -79
- package/dist/{chunk-L3TWPROA.js → chunk-QRQF3556.js} +44 -24
- package/dist/{chunk-YHD6TUIR.cjs → chunk-QW2RW2GY.cjs} +2 -0
- package/dist/chunk-RAKO7UN7.js +114 -0
- package/dist/{chunk-7TQH3CL4.cjs → chunk-RBXBH6EB.cjs} +78 -35
- package/dist/chunk-RFUKZIJF.cjs +78 -0
- package/dist/chunk-RS2E32YB.js +71 -0
- package/dist/{chunk-OLSBBZW6.cjs → chunk-S5JNQIGL.cjs} +5 -5
- package/dist/{chunk-FTYY5JJD.js → chunk-S5QKRA2V.js} +2 -2
- package/dist/chunk-SD3NTC7D.cjs +77 -0
- package/dist/{chunk-KPELYZ6L.js → chunk-SDLY4X3G.js} +2 -2
- package/dist/{chunk-IDFF5J2E.js → chunk-SFFEKZGC.js} +38 -7
- package/dist/{chunk-EGFT4PGW.js → chunk-SHRLFX6F.js} +8 -3
- package/dist/{chunk-3WICOC33.js → chunk-SNDDZG5H.js} +124 -84
- package/dist/{chunk-CZXGCVTR.cjs → chunk-SOVR7S3T.cjs} +2 -2
- package/dist/chunk-SPQ7QIQ6.js +78 -0
- package/dist/{chunk-XRZEUWKF.js → chunk-SRMGWMQO.js} +1 -1
- package/dist/{chunk-R33VKSH5.cjs → chunk-SVVP4UUZ.cjs} +11 -11
- package/dist/{chunk-7BTSG4ME.cjs → chunk-SY2P3Z5W.cjs} +1004 -39
- package/dist/chunk-SZD45IDG.js +468 -0
- package/dist/{chunk-3O3MOK5C.cjs → chunk-TE4SYTWR.cjs} +1114 -142
- package/dist/chunk-TH26BQJG.js +101 -0
- package/dist/{chunk-QNGEW5TC.js → chunk-TR6NECEZ.js} +1 -1
- package/dist/{chunk-YGN4CQIP.js → chunk-TYBPTKFT.js} +1 -1
- package/dist/{chunk-GJH7XMSK.js → chunk-U2QUMKCB.js} +8 -6
- package/dist/{chunk-47CKWKEX.cjs → chunk-U6PLSPMD.cjs} +9 -4
- package/dist/chunk-UOQECODR.js +34 -0
- package/dist/chunk-UR27UDTB.js +354 -0
- package/dist/chunk-UWZ4G3SQ.js +93 -0
- package/dist/{chunk-Y72HH2TF.cjs → chunk-V3SZLWEQ.cjs} +33 -24
- package/dist/chunk-V4HGZSKQ.cjs +183 -0
- package/dist/{chunk-3PDTTAKJ.js → chunk-V5ELP2XE.js} +19 -12
- package/dist/{chunk-3K2ESU53.cjs → chunk-VKOXFT4L.cjs} +2 -2
- package/dist/{chunk-XTB6VJVQ.cjs → chunk-VLGGMQUN.cjs} +6 -6
- package/dist/chunk-VQHSYAPZ.cjs +3897 -0
- package/dist/chunk-VUGOOGHB.js +400 -0
- package/dist/chunk-W5QLA6WP.cjs +866 -0
- package/dist/chunk-WBU4Q4GS.cjs +400 -0
- package/dist/{chunk-KWXVKLQ5.cjs → chunk-WGNMOVMT.cjs} +7 -82
- package/dist/chunk-WJYFLQ7G.cjs +27 -0
- package/dist/{chunk-RKJTGGMU.cjs → chunk-WKRDBCP2.cjs} +221 -16
- package/dist/{chunk-ZSPXQYG2.js → chunk-WNJXIACY.js} +7 -5
- package/dist/{chunk-AYS2ASM7.js → chunk-WPE2XHVX.js} +1 -1
- package/dist/chunk-WTB7AFL6.cjs +101 -0
- package/dist/{chunk-JYTDYJVW.js → chunk-X3TI5TJJ.js} +1 -1
- package/dist/chunk-X5P6QQOB.cjs +719 -0
- package/dist/{chunk-245KJE5Y.cjs → chunk-X7XMITIL.cjs} +14 -6
- package/dist/chunk-XF2WIKHR.cjs +34 -0
- package/dist/{chunk-DV2ZHK7B.cjs → chunk-XNIMSVS6.cjs} +49 -26
- package/dist/chunk-XW3XCK4E.cjs +59 -0
- package/dist/{chunk-N23UAW4I.js → chunk-XZVDYC5U.js} +7 -2
- package/dist/chunk-Y5BDE24P.cjs +444 -0
- package/dist/{chunk-NAGQ2PDC.js → chunk-YC5OBZQU.js} +1604 -433
- package/dist/chunk-YISPVAXO.cjs +2340 -0
- package/dist/{chunk-KQGPTCQJ.js → chunk-YQDI753V.js} +68 -25
- package/dist/{chunk-UJ2JSM6H.js → chunk-YVQI26H4.js} +2 -0
- package/dist/{chunk-NNPAM4HC.cjs → chunk-YWH55BWK.cjs} +15 -6
- package/dist/{chunk-X2YOZQIP.cjs → chunk-YWTIXHU6.cjs} +266 -19
- package/dist/{chunk-XX2ZO7DS.js → chunk-YXM6SA7P.js} +25 -16
- package/dist/chunk-YZWE7XSM.js +5 -0
- package/dist/chunk-ZASDSY7P.cjs +114 -0
- package/dist/{chunk-2UC22DJU.js → chunk-ZNGRLCFQ.js} +30 -4
- package/dist/{chunk-KANW6OYC.cjs → chunk-ZNYZB7XY.cjs} +34 -8
- package/dist/{chunk-NA6WQDYW.js → chunk-ZPLCAXJW.js} +1088 -116
- package/dist/{chunk-7UOUW76C.js → chunk-ZQAT5VT5.js} +101 -24
- package/dist/chunk-ZUYYHKQA.js +44 -0
- package/dist/clear-QJXU25IF.cjs +12 -0
- package/dist/{clear-A3N4GK2S.js → clear-Y5TO3RZA.js} +3 -3
- package/dist/communityInstaller-LXMVKVAV.cjs +22 -0
- package/dist/{communityInstaller-LOP2EDH5.js → communityInstaller-RVL4STPS.js} +8 -5
- package/dist/completion-JX4T2ONW.cjs +17 -0
- package/dist/completion-SE3XYG74.js +17 -0
- package/dist/config-3224PRW4.cjs +21 -0
- package/dist/{config-HPJPKTO6.js → config-7KPHKFTP.js} +7 -4
- package/dist/{constants-LISJW3DD.js → constants-EJFAWJQI.js} +1 -1
- package/dist/constants-MCCGUU5F.cjs +21 -0
- package/dist/{defaultHooks-IHSJR2AX.cjs → defaultHooks-2V2CQL63.cjs} +25 -10
- package/dist/{defaultHooks-KUKHK3AG.js → defaultHooks-TMHDU3FS.js} +25 -10
- package/dist/export-BJVIDZC4.js +15 -0
- package/dist/export-KU4557HK.cjs +15 -0
- package/dist/feature-K3LG5IJP.cjs +14 -0
- package/dist/feature-V4JF5TNJ.js +14 -0
- package/dist/features-5OJTLKS7.js +23 -0
- package/dist/features-VCBJQXCX.cjs +23 -0
- package/dist/feedback-D4437VE4.js +18 -0
- package/dist/feedback-VZPEHGFY.cjs +18 -0
- package/dist/fffSearchProvider-2YCNKOYD.js +412 -0
- package/dist/fffSearchProvider-W6627E2V.cjs +412 -0
- package/dist/{filesystem-Z7BWAWMZ.js → filesystem-L6DQKGWK.js} +3 -2
- package/dist/filesystem-PGUPCMVK.cjs +11 -0
- package/dist/go-X4E6BCD6.js +12 -0
- package/dist/go-XREVFS5I.cjs +12 -0
- package/dist/goal-4M3J6JS2.cjs +16 -0
- package/dist/goal-EI66BV7W.js +16 -0
- package/dist/help-GGFS7WHY.cjs +12 -0
- package/dist/{help-GFQXNZOK.js → help-MLIROWKM.js} +2 -2
- package/dist/{history-E3N6BJP7.js → history-GQJ6RZD6.js} +2 -2
- package/dist/history-J4LUIOSI.cjs +14 -0
- package/dist/hooks-FSRIUS6A.cjs +18 -0
- package/dist/hooks-PPFHCF7T.js +18 -0
- package/dist/i18n-CI6VFXL5.cjs +33 -0
- package/dist/{i18n-SY7QRM22.js → i18n-Q7UZJRPL.js} +1 -1
- package/dist/ide-BUSVE54P.js +15 -0
- package/dist/ide-YR27BPGM.cjs +15 -0
- package/dist/immediateCommandRouter-MTEHZXQX.js +15 -0
- package/dist/immediateCommandRouter-ROXU3MWT.cjs +15 -0
- package/dist/{import-W7SVLSTC.js → import-3VBKI6BN.js} +3 -3
- package/dist/{import-ADI37ZUR.cjs → import-C7S6UJMW.cjs} +3 -3
- package/dist/import-DBK4OCDF.cjs +10 -0
- package/dist/{import-KGKKZ3B7.js → import-U47DXCA7.js} +2 -2
- package/dist/index.cjs +837 -23011
- package/dist/index.d.cts +95 -0
- package/dist/index.d.ts +95 -0
- package/dist/index.js +864 -23038
- package/dist/init-OXTYS72D.cjs +10 -0
- package/dist/{init-7MFK626E.js → init-Q2O4R42R.js} +2 -2
- package/dist/inkMode-VUE6ZDLD.cjs +7 -0
- package/dist/inkMode-WBNFOSAT.js +7 -0
- package/dist/inputPrompt-3CFZDUBH.js +90 -0
- package/dist/inputPrompt-JMACL4EB.cjs +90 -0
- package/dist/language-EK3M6UBV.cjs +22 -0
- package/dist/language-Q3RUGGOW.js +22 -0
- package/dist/learn-3DSX7DYG.cjs +23 -0
- package/dist/learn-OPOH5L43.js +23 -0
- package/dist/login-GST7MWXJ.cjs +27 -0
- package/dist/login-Q7DZKAX4.js +27 -0
- package/dist/logout-2G7OUK7I.cjs +24 -0
- package/dist/logout-FNBBL2UI.js +24 -0
- package/dist/mcp-AH3MMUBU.js +21 -0
- package/dist/mcp-DW7R63IB.cjs +21 -0
- package/dist/{mcp-install-2FEROZTL.js → mcp-install-222PCKSW.js} +18 -10
- package/dist/{mcp-install-WM6BQRI5.cjs → mcp-install-IA4ZS2SV.cjs} +22 -14
- package/dist/memory-6NX3DAIY.cjs +10 -0
- package/dist/{memory-J73WZH2I.js → memory-VB46T5H3.js} +2 -2
- package/dist/model-64NAELFS.cjs +10 -0
- package/dist/{model-AES267IN.js → model-SJJG64AM.js} +2 -2
- package/dist/new-DU5SOOOY.cjs +12 -0
- package/dist/{new-5CLF3MKH.js → new-ERZ5C6CN.js} +3 -3
- package/dist/onboarding-2U2BV2KE.cjs +36 -0
- package/dist/onboarding-ZQXMPSMJ.js +36 -0
- package/dist/permissions-7ACNFK7A.js +10 -0
- package/dist/permissions-GSNNV7RJ.cjs +10 -0
- package/dist/plan-XEJMOT55.cjs +13 -0
- package/dist/{plan-65HMS5HQ.js → plan-YYUAXPTL.js} +3 -1
- package/dist/pr-review-CW6J7P62.cjs +9 -0
- package/dist/pr-review-YZSBQVT2.js +9 -0
- package/dist/{quit-XDZYRSPU.js → quit-WY6T267G.js} +2 -2
- package/dist/quit-XIRE2KRE.cjs +10 -0
- package/dist/rawMode-6W5AXAKI.cjs +7 -0
- package/dist/rawMode-GFNLXQPU.js +7 -0
- package/dist/{registry-PTHWERKC.js → registry-4DXUDKJN.js} +29 -44
- package/dist/{registry-IVT4G2RT.cjs → registry-UEO3ETZ7.cjs} +65 -80
- package/dist/repeat-P4FAPE3Y.cjs +17 -0
- package/dist/{repeat-EVCWUL6Z.js → repeat-RALE6AUO.js} +7 -3
- package/dist/resume-RQZ3WXBP.cjs +17 -0
- package/dist/resume-XSXZMDMD.js +17 -0
- package/dist/review-QHP2KP4Q.js +9 -0
- package/dist/review-UWHWQHCB.cjs +9 -0
- package/dist/ripgrep-67SCU2BA.cjs +9 -0
- package/dist/ripgrep-VHJQQ55W.js +9 -0
- package/dist/rpc-XH7QLN4H.js +3874 -0
- package/dist/rpc-YF54T7JU.cjs +3874 -0
- package/dist/search-VXXFGZWU.cjs +21 -0
- package/dist/search-YAWGX7P7.js +21 -0
- package/dist/{sessions-6GWEBMKS.js → sessions-NJYHJOEL.js} +2 -2
- package/dist/sessions-OSG3UJEZ.cjs +10 -0
- package/dist/settings-SO2UIGWV.cjs +42 -0
- package/dist/settings-ZXUQH3DO.js +42 -0
- package/dist/setup-KG7EGKF5.js +30 -0
- package/dist/setup-RSOPCQ57.cjs +30 -0
- package/dist/share-WFAGZ5PY.js +17 -0
- package/dist/share-ZU3SGACF.cjs +17 -0
- package/dist/skills-KBVAQAD2.cjs +29 -0
- package/dist/skills-NBTNDVAY.js +29 -0
- package/dist/{skills-ZZCIAS7C.js → skills-OB6RDW7D.js} +10 -7
- package/dist/{skills-PG542VEB.cjs → skills-ZROBG3RZ.cjs} +13 -10
- package/dist/{skills-install-SRC3Z2MS.js → skills-install-BHTIEMKH.js} +21 -70
- package/dist/{skills-install-67DOBPJC.cjs → skills-install-ILX6QVEF.cjs} +34 -83
- package/dist/skills-new-B45VQ2PP.cjs +18 -0
- package/dist/skills-new-YMRP2HNO.js +18 -0
- package/dist/slashCommands-53VYIBJU.js +105 -0
- package/dist/slashCommands-BG2RGGZ6.cjs +105 -0
- package/dist/status-CQ2IUOVK.cjs +24 -0
- package/dist/status-E7IGNVPC.js +24 -0
- package/dist/summarizer-DGPHE5IQ.js +17 -0
- package/dist/summarizer-JNXLUAQG.cjs +17 -0
- package/dist/sync-7C25MOT2.js +22 -0
- package/dist/{sync-VU2NSJ4O.js → sync-OCJN4ZSO.js} +3 -3
- package/dist/sync-XRP46IVG.cjs +40 -0
- package/dist/sync-ZMFVE7T4.cjs +22 -0
- package/dist/{teammate-SXRVXNQV.cjs → teammate-D77B6QRT.cjs} +31 -9
- package/dist/{teammate-SD26GR37.js → teammate-EZCMHOIL.js} +30 -8
- package/dist/templates-ARG2VRWW.cjs +11 -0
- package/dist/templates-UGVZV3KJ.js +11 -0
- package/dist/theme-KKRDE6P7.cjs +22 -0
- package/dist/theme-XF7XIWBQ.js +22 -0
- package/dist/tools-3PPTTKFV.js +9 -0
- package/dist/tools-THIQA7WC.cjs +9 -0
- package/dist/ui/questionModal.cjs +8 -5
- package/dist/ui/questionModal.js +7 -4
- package/dist/{undo-OL2EDBRY.js → undo-GNUTFXCW.js} +2 -2
- package/dist/undo-U4KN7QQM.cjs +10 -0
- package/dist/usage-QSTNSDAO.js +24 -0
- package/dist/usage-YDEMQBNQ.cjs +24 -0
- package/dist/web-3BA2WV37.cjs +37 -0
- package/dist/web-6FYGBX5K.js +37 -0
- package/dist/workspaceSafety-MDJGHK6D.cjs +9 -0
- package/dist/workspaceSafety-XOUMUBVB.js +9 -0
- package/dist/yolo-GF2YD7ZI.js +9 -0
- package/dist/yolo-OGDA7HNC.cjs +9 -0
- package/dist/yoloMode-3DJDA75U.cjs +17 -0
- package/dist/yoloMode-4JOOSU26.js +17 -0
- package/package.json +51 -49
- package/dist/AgentRegistry-HRPN6ZOF.cjs +0 -10
- package/dist/CommunitySkillsCache-KE435RAR.cjs +0 -8
- package/dist/GitHubRegistryFetcher-I45SESIL.cjs +0 -7
- package/dist/LearnAdvisor-FLBA6FDD.js +0 -9
- package/dist/LearnAdvisor-GG3CXQF3.cjs +0 -9
- package/dist/MemoryManager-2LQPIYVE.cjs +0 -8
- package/dist/PermissionManager-X57BXHJ6.cjs +0 -11
- package/dist/ProviderFactory-KPJOGQWF.cjs +0 -9
- package/dist/SessionManager-YBJAZXNO.cjs +0 -10
- package/dist/SkillsRegistry-7O72A6TZ.cjs +0 -9
- package/dist/SubAgent-JT4HZHN7.js +0 -11
- package/dist/SubAgent-VPNYDWAU.cjs +0 -11
- package/dist/SyncApiClient-HQXJL5BT.cjs +0 -11
- package/dist/about-4DB5KTHW.js +0 -12
- package/dist/about-LXAOXZFT.cjs +0 -12
- package/dist/actionExecutor-23JB2WUC.js +0 -19
- package/dist/actionExecutor-X5UEZSKH.cjs +0 -19
- package/dist/add-dir-YC37DMSF.cjs +0 -10
- package/dist/agents-YF3BSUU5.js +0 -12
- package/dist/agents-ZOUHPMYR.cjs +0 -12
- package/dist/agents-new-HSH4GQPG.js +0 -14
- package/dist/agents-new-VYUDOCE7.cjs +0 -14
- package/dist/autoSkill-X5W52WOE.cjs +0 -20
- package/dist/automode-SJGM7VEI.cjs +0 -10
- package/dist/chunk-ALYU6VTM.js +0 -105
- package/dist/chunk-B53A2NM2.js +0 -2030
- package/dist/chunk-BJXSNT46.js +0 -100
- package/dist/chunk-CB4E2T5N.cjs +0 -312
- package/dist/chunk-DNUOXBHL.js +0 -539
- package/dist/chunk-EFX2QSZX.cjs +0 -33
- package/dist/chunk-GCXYXLRA.cjs +0 -111
- package/dist/chunk-H2ZRHQQV.js +0 -33
- package/dist/chunk-HNRPK5MY.cjs +0 -85
- package/dist/chunk-HVKOZ2VP.cjs +0 -115
- package/dist/chunk-JJLYWH5Y.cjs +0 -100
- package/dist/chunk-LWUJFGOZ.js +0 -115
- package/dist/chunk-MERYP6AM.cjs +0 -539
- package/dist/chunk-MZAPWNAC.cjs +0 -207
- package/dist/chunk-NTSDP2WB.js +0 -226
- package/dist/chunk-OUZQXMHL.cjs +0 -226
- package/dist/chunk-PGESAU2W.cjs +0 -2030
- package/dist/chunk-SYVYLZZF.cjs +0 -24
- package/dist/chunk-SZOLA6FR.js +0 -111
- package/dist/chunk-VWDHR4HV.js +0 -168
- package/dist/chunk-Y45G6ZO5.cjs +0 -168
- package/dist/chunk-YRLYSQEQ.cjs +0 -105
- package/dist/chunk-ZYVS43MU.js +0 -312
- package/dist/clear-GK4IEUUS.cjs +0 -12
- package/dist/communityInstaller-XXC7RLE4.cjs +0 -19
- package/dist/completion-HWABSAEL.js +0 -14
- package/dist/completion-IUUVQG4D.cjs +0 -14
- package/dist/config-HF7WOLZF.cjs +0 -18
- package/dist/constants-PEO3P2SJ.cjs +0 -21
- package/dist/export-QJAV2FCZ.js +0 -12
- package/dist/export-XSRFXGWU.cjs +0 -12
- package/dist/feedback-4TCIL3ML.cjs +0 -15
- package/dist/feedback-SJ6VVEY3.js +0 -15
- package/dist/filesystem-W56QZUJF.cjs +0 -10
- package/dist/help-ISBVQL3S.cjs +0 -12
- package/dist/history-XQ4GTSFU.cjs +0 -14
- package/dist/hooks-CJNKJ5PF.js +0 -13
- package/dist/hooks-UTMBTAXT.cjs +0 -13
- package/dist/i18n-N7QQ7A5M.cjs +0 -33
- package/dist/ide-RTA4UJV4.js +0 -12
- package/dist/ide-VWVOLIFF.cjs +0 -12
- package/dist/immediateCommandRouter-BW34JNXL.js +0 -9
- package/dist/immediateCommandRouter-SHOVNB5X.cjs +0 -9
- package/dist/import-ZLJVONXH.cjs +0 -10
- package/dist/init-TBKAB4LZ.cjs +0 -10
- package/dist/language-MDSHEXHB.cjs +0 -18
- package/dist/language-PXTQSHIG.js +0 -18
- package/dist/learn-623TW5EK.cjs +0 -20
- package/dist/learn-BCPV7GM2.js +0 -20
- package/dist/localProjectPermissions-BHQXEWZJ.cjs +0 -18
- package/dist/localProjectPermissions-GMOUYQM6.js +0 -18
- package/dist/login-QMVEETWJ.js +0 -20
- package/dist/login-QYMXAL3K.cjs +0 -20
- package/dist/logout-2CMTDAOJ.js +0 -18
- package/dist/logout-ZOHVZAUK.cjs +0 -18
- package/dist/mcp-IUVKK65S.js +0 -18
- package/dist/mcp-TXC7PYG3.cjs +0 -18
- package/dist/memory-WRIHDEPK.cjs +0 -10
- package/dist/model-RLP75SF5.cjs +0 -10
- package/dist/new-HLSFL6A4.cjs +0 -12
- package/dist/permissions-GP6FTGZ2.js +0 -13
- package/dist/permissions-O6EKKPOG.cjs +0 -13
- package/dist/plan-MCAXDIM2.cjs +0 -11
- package/dist/quit-TQX6GXA5.cjs +0 -10
- package/dist/repeat-BSPS5TWD.cjs +0 -13
- package/dist/resume-2GOPDLL4.cjs +0 -13
- package/dist/resume-37IUVDA6.js +0 -13
- package/dist/search-7KUSHIBL.cjs +0 -17
- package/dist/search-OJGDRIMA.js +0 -17
- package/dist/sessions-CYYCHSQG.cjs +0 -10
- package/dist/settings-2D7CAO66.cjs +0 -30
- package/dist/settings-BXR6SBJP.js +0 -30
- package/dist/share-BXQY5IQU.js +0 -14
- package/dist/share-OSFXZBGS.cjs +0 -14
- package/dist/skills-FL6O6AOM.cjs +0 -26
- package/dist/skills-PNKQZRNK.js +0 -26
- package/dist/skills-new-XFMEHHIF.cjs +0 -15
- package/dist/skills-new-ZNZPHUKS.js +0 -15
- package/dist/slashCommands-LLCNPK3X.js +0 -76
- package/dist/slashCommands-RXZZS6RE.cjs +0 -76
- package/dist/status-BCECUJXT.cjs +0 -11
- package/dist/status-EFO7MQU3.js +0 -11
- package/dist/sync-IJYJ6KKM.js +0 -18
- package/dist/sync-LFT6SBPF.cjs +0 -18
- package/dist/sync-U7SDPBNZ.cjs +0 -40
- package/dist/theme-AWBHSG7J.cjs +0 -18
- package/dist/theme-TQLBPJ2E.js +0 -18
- package/dist/undo-IBBGP7A2.cjs +0 -10
package/dist/chunk-PGESAU2W.cjs
DELETED
|
@@ -1,2030 +0,0 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } } function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; }// src/providers/errors.ts
|
|
2
|
-
var ApiError = class extends Error {
|
|
3
|
-
constructor(message, code, httpStatus, retryable, retryAfterMs, rawDetail) {
|
|
4
|
-
super(message);
|
|
5
|
-
this.name = "ApiError";
|
|
6
|
-
this.code = code;
|
|
7
|
-
this.httpStatus = httpStatus;
|
|
8
|
-
this.retryable = retryable;
|
|
9
|
-
this.retryAfterMs = retryAfterMs;
|
|
10
|
-
this.rawDetail = rawDetail;
|
|
11
|
-
}
|
|
12
|
-
};
|
|
13
|
-
var FRIENDLY_MESSAGES = {
|
|
14
|
-
context_overflow: "The conversation is too long for this model. Try /undo to remove recent turns or /new to start fresh.",
|
|
15
|
-
model_not_found: "The requested model was not found. Use /model to select a different one.",
|
|
16
|
-
invalid_request: "The request was malformed and could not be processed.",
|
|
17
|
-
auth_failed: "Authentication failed. Please verify your API key in ~/.autohand/config.json.",
|
|
18
|
-
payment_required: "Payment required. Please check your account balance or billing settings.",
|
|
19
|
-
access_denied: "Access denied. Your API key may not have permission for this model.",
|
|
20
|
-
rate_limited: "Rate limit exceeded. Please wait a moment and try again, or choose a different model.",
|
|
21
|
-
server_error: "The AI service encountered an error. Please try again later.",
|
|
22
|
-
network_error: "Unable to connect to the AI service. Please check your internet connection.",
|
|
23
|
-
timeout: "The request timed out. The AI service may be experiencing high load.",
|
|
24
|
-
cancelled: "Request cancelled.",
|
|
25
|
-
unknown: "An unexpected error occurred. Please try again."
|
|
26
|
-
};
|
|
27
|
-
var MODEL_NOT_FOUND_PATTERNS = [
|
|
28
|
-
"invalid model",
|
|
29
|
-
"model does not exist",
|
|
30
|
-
"model not found",
|
|
31
|
-
"no endpoints found for model",
|
|
32
|
-
"does not exist or you do not have access",
|
|
33
|
-
// Catch "model 'xyz' not found" where 'not found' is separate from 'model'
|
|
34
|
-
"' not found",
|
|
35
|
-
'" not found'
|
|
36
|
-
];
|
|
37
|
-
var CONTEXT_OVERFLOW_PATTERNS = [
|
|
38
|
-
"maximum context length",
|
|
39
|
-
"context length exceeded",
|
|
40
|
-
"context is too long",
|
|
41
|
-
"prompt is too long",
|
|
42
|
-
"reduce the length",
|
|
43
|
-
"payload too large",
|
|
44
|
-
"context window",
|
|
45
|
-
"token limit",
|
|
46
|
-
"tokens exceeds",
|
|
47
|
-
"too many tokens"
|
|
48
|
-
];
|
|
49
|
-
var CANCEL_PATTERNS = [
|
|
50
|
-
"cancelled",
|
|
51
|
-
"canceled",
|
|
52
|
-
"aborted",
|
|
53
|
-
"user force closed"
|
|
54
|
-
];
|
|
55
|
-
var NETWORK_PATTERNS = [
|
|
56
|
-
"econnrefused",
|
|
57
|
-
"econnreset",
|
|
58
|
-
"enotfound",
|
|
59
|
-
"etimedout",
|
|
60
|
-
"fetch failed",
|
|
61
|
-
"network",
|
|
62
|
-
"unable to connect"
|
|
63
|
-
];
|
|
64
|
-
var TIMEOUT_PATTERNS = [
|
|
65
|
-
"timed out",
|
|
66
|
-
"timeout"
|
|
67
|
-
];
|
|
68
|
-
function classifyApiError(httpStatus, errorBody, headers) {
|
|
69
|
-
const lower = errorBody.toLowerCase();
|
|
70
|
-
if (httpStatus === 401) {
|
|
71
|
-
return makeError("auth_failed", httpStatus, false, errorBody, headers);
|
|
72
|
-
}
|
|
73
|
-
if (httpStatus === 402) {
|
|
74
|
-
return makeError("payment_required", httpStatus, false, errorBody, headers);
|
|
75
|
-
}
|
|
76
|
-
if (httpStatus === 403) {
|
|
77
|
-
return makeError("access_denied", httpStatus, false, errorBody, headers);
|
|
78
|
-
}
|
|
79
|
-
if (httpStatus === 404) {
|
|
80
|
-
return makeError("model_not_found", httpStatus, false, errorBody, headers);
|
|
81
|
-
}
|
|
82
|
-
if (httpStatus === 429) {
|
|
83
|
-
return makeError("rate_limited", httpStatus, true, errorBody, headers);
|
|
84
|
-
}
|
|
85
|
-
if (httpStatus === 504) {
|
|
86
|
-
return makeError("timeout", httpStatus, true, errorBody, headers);
|
|
87
|
-
}
|
|
88
|
-
if (httpStatus >= 500) {
|
|
89
|
-
return makeError("server_error", httpStatus, true, errorBody, headers);
|
|
90
|
-
}
|
|
91
|
-
if (httpStatus === 400) {
|
|
92
|
-
if (matchesAny(lower, MODEL_NOT_FOUND_PATTERNS)) {
|
|
93
|
-
return makeError("model_not_found", httpStatus, false, errorBody, headers);
|
|
94
|
-
}
|
|
95
|
-
if (matchesAny(lower, CONTEXT_OVERFLOW_PATTERNS)) {
|
|
96
|
-
return makeError("context_overflow", httpStatus, true, errorBody, headers);
|
|
97
|
-
}
|
|
98
|
-
return makeError("invalid_request", httpStatus, false, errorBody, headers);
|
|
99
|
-
}
|
|
100
|
-
if (matchesAny(lower, CANCEL_PATTERNS)) {
|
|
101
|
-
return makeError("cancelled", httpStatus, false, errorBody, headers);
|
|
102
|
-
}
|
|
103
|
-
if (matchesAny(lower, TIMEOUT_PATTERNS)) {
|
|
104
|
-
return makeError("timeout", httpStatus, true, errorBody, headers);
|
|
105
|
-
}
|
|
106
|
-
if (matchesAny(lower, NETWORK_PATTERNS)) {
|
|
107
|
-
return makeError("network_error", httpStatus, true, errorBody, headers);
|
|
108
|
-
}
|
|
109
|
-
if (httpStatus === 0 || httpStatus === void 0) {
|
|
110
|
-
if (matchesAny(lower, CONTEXT_OVERFLOW_PATTERNS)) {
|
|
111
|
-
return makeError("context_overflow", httpStatus, true, errorBody, headers);
|
|
112
|
-
}
|
|
113
|
-
if (matchesAny(lower, MODEL_NOT_FOUND_PATTERNS)) {
|
|
114
|
-
return makeError("model_not_found", httpStatus, false, errorBody, headers);
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
return makeError("unknown", httpStatus, true, errorBody, headers);
|
|
118
|
-
}
|
|
119
|
-
function matchesAny(lower, patterns) {
|
|
120
|
-
return patterns.some((p) => lower.includes(p));
|
|
121
|
-
}
|
|
122
|
-
function makeError(code, httpStatus, retryable, rawBody, headers) {
|
|
123
|
-
const friendlyMessage = FRIENDLY_MESSAGES[code];
|
|
124
|
-
const message = rawBody ? `${friendlyMessage}
|
|
125
|
-
${rawBody}` : friendlyMessage;
|
|
126
|
-
const retryAfterMs = parseRetryAfter(headers);
|
|
127
|
-
return new ApiError(message, code, httpStatus, retryable, retryAfterMs, rawBody);
|
|
128
|
-
}
|
|
129
|
-
function parseRetryAfter(headers) {
|
|
130
|
-
if (!headers) return void 0;
|
|
131
|
-
const value = headers.get("retry-after");
|
|
132
|
-
if (!value) return void 0;
|
|
133
|
-
const seconds = Number(value);
|
|
134
|
-
if (!Number.isNaN(seconds) && seconds > 0) {
|
|
135
|
-
return seconds * 1e3;
|
|
136
|
-
}
|
|
137
|
-
const dateMs = Date.parse(value);
|
|
138
|
-
if (!Number.isNaN(dateMs)) {
|
|
139
|
-
const delayMs = dateMs - Date.now();
|
|
140
|
-
return delayMs > 0 ? delayMs : void 0;
|
|
141
|
-
}
|
|
142
|
-
return void 0;
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
// src/providers/OllamaProvider.ts
|
|
146
|
-
var DEFAULT_TIMEOUT = 12e4;
|
|
147
|
-
var DEFAULT_CHUNK_TIMEOUT = 3e4;
|
|
148
|
-
var DEFAULT_MAX_RETRIES = 2;
|
|
149
|
-
var MAX_ALLOWED_RETRIES = 5;
|
|
150
|
-
var DEFAULT_RETRY_DELAY = 1e3;
|
|
151
|
-
var AVAILABILITY_TIMEOUT = 5e3;
|
|
152
|
-
var OllamaProvider = class {
|
|
153
|
-
constructor(config, networkSettings) {
|
|
154
|
-
this.disableTools = false;
|
|
155
|
-
this.baseUrl = config.baseUrl || "http://localhost:11434";
|
|
156
|
-
this.model = config.model || "llama3.2:latest";
|
|
157
|
-
const configuredRetries = _nullishCoalesce(_optionalChain([networkSettings, 'optionalAccess', _ => _.maxRetries]), () => ( DEFAULT_MAX_RETRIES));
|
|
158
|
-
this.maxRetries = Math.min(Math.max(0, configuredRetries), MAX_ALLOWED_RETRIES);
|
|
159
|
-
this.retryDelay = _nullishCoalesce(_optionalChain([networkSettings, 'optionalAccess', _2 => _2.retryDelay]), () => ( DEFAULT_RETRY_DELAY));
|
|
160
|
-
this.timeout = _nullishCoalesce(_optionalChain([networkSettings, 'optionalAccess', _3 => _3.timeout]), () => ( DEFAULT_TIMEOUT));
|
|
161
|
-
this.chunkTimeout = DEFAULT_CHUNK_TIMEOUT;
|
|
162
|
-
}
|
|
163
|
-
getName() {
|
|
164
|
-
return "ollama";
|
|
165
|
-
}
|
|
166
|
-
setModel(model) {
|
|
167
|
-
this.model = model;
|
|
168
|
-
}
|
|
169
|
-
async listModels() {
|
|
170
|
-
try {
|
|
171
|
-
const controller = new AbortController();
|
|
172
|
-
const timerId = setTimeout(() => controller.abort(), AVAILABILITY_TIMEOUT);
|
|
173
|
-
try {
|
|
174
|
-
const response = await fetch(`${this.baseUrl}/api/tags`, {
|
|
175
|
-
signal: controller.signal
|
|
176
|
-
});
|
|
177
|
-
if (!response.ok) {
|
|
178
|
-
return [];
|
|
179
|
-
}
|
|
180
|
-
const data = await response.json();
|
|
181
|
-
return data.models.map((m) => m.name);
|
|
182
|
-
} finally {
|
|
183
|
-
clearTimeout(timerId);
|
|
184
|
-
}
|
|
185
|
-
} catch (e) {
|
|
186
|
-
return [];
|
|
187
|
-
}
|
|
188
|
-
}
|
|
189
|
-
async isAvailable() {
|
|
190
|
-
try {
|
|
191
|
-
const controller = new AbortController();
|
|
192
|
-
const timerId = setTimeout(() => controller.abort(), AVAILABILITY_TIMEOUT);
|
|
193
|
-
try {
|
|
194
|
-
const response = await fetch(`${this.baseUrl}/api/tags`, {
|
|
195
|
-
signal: controller.signal
|
|
196
|
-
});
|
|
197
|
-
return response.ok;
|
|
198
|
-
} finally {
|
|
199
|
-
clearTimeout(timerId);
|
|
200
|
-
}
|
|
201
|
-
} catch (e2) {
|
|
202
|
-
return false;
|
|
203
|
-
}
|
|
204
|
-
}
|
|
205
|
-
async complete(request) {
|
|
206
|
-
const messages = request.messages.map((msg) => {
|
|
207
|
-
const mapped = {
|
|
208
|
-
role: msg.role,
|
|
209
|
-
content: _nullishCoalesce(msg.content, () => ( ""))
|
|
210
|
-
};
|
|
211
|
-
if (msg.name) {
|
|
212
|
-
mapped.name = msg.name;
|
|
213
|
-
}
|
|
214
|
-
if (msg.role === "tool" && msg.tool_call_id) {
|
|
215
|
-
mapped.tool_call_id = msg.tool_call_id;
|
|
216
|
-
}
|
|
217
|
-
if (msg.role === "assistant" && msg.tool_calls) {
|
|
218
|
-
mapped.tool_calls = msg.tool_calls;
|
|
219
|
-
}
|
|
220
|
-
return mapped;
|
|
221
|
-
});
|
|
222
|
-
const body = {
|
|
223
|
-
model: request.model || this.model,
|
|
224
|
-
messages,
|
|
225
|
-
stream: request.stream || false
|
|
226
|
-
};
|
|
227
|
-
if (request.temperature !== void 0) {
|
|
228
|
-
body.options = { temperature: request.temperature };
|
|
229
|
-
}
|
|
230
|
-
if (!this.disableTools && request.tools && request.tools.length > 0) {
|
|
231
|
-
body.tools = request.tools.map((tool) => ({
|
|
232
|
-
type: "function",
|
|
233
|
-
function: {
|
|
234
|
-
name: tool.name,
|
|
235
|
-
description: tool.description,
|
|
236
|
-
parameters: _nullishCoalesce(tool.parameters, () => ( { type: "object", properties: {} }))
|
|
237
|
-
}
|
|
238
|
-
}));
|
|
239
|
-
}
|
|
240
|
-
let lastError = null;
|
|
241
|
-
for (let attempt = 0; attempt <= this.maxRetries; attempt++) {
|
|
242
|
-
try {
|
|
243
|
-
return await this.makeRequest(body, request);
|
|
244
|
-
} catch (error) {
|
|
245
|
-
lastError = error;
|
|
246
|
-
if (this.isNonRetryableError(error)) {
|
|
247
|
-
throw error;
|
|
248
|
-
}
|
|
249
|
-
if (attempt < this.maxRetries) {
|
|
250
|
-
const delay = this.retryDelay * Math.pow(2, attempt);
|
|
251
|
-
await this.sleep(delay);
|
|
252
|
-
}
|
|
253
|
-
}
|
|
254
|
-
}
|
|
255
|
-
throw _nullishCoalesce(lastError, () => ( new ApiError(
|
|
256
|
-
"Failed to communicate with Ollama. Please try again.",
|
|
257
|
-
"network_error",
|
|
258
|
-
0,
|
|
259
|
-
true
|
|
260
|
-
)));
|
|
261
|
-
}
|
|
262
|
-
async makeRequest(body, request) {
|
|
263
|
-
let response;
|
|
264
|
-
try {
|
|
265
|
-
const timeoutController = new AbortController();
|
|
266
|
-
const timerId = setTimeout(() => timeoutController.abort(), this.timeout);
|
|
267
|
-
const combinedSignal = request.signal ? this.combineSignals(request.signal, timeoutController.signal) : timeoutController.signal;
|
|
268
|
-
try {
|
|
269
|
-
response = await fetch(`${this.baseUrl}/api/chat`, {
|
|
270
|
-
method: "POST",
|
|
271
|
-
headers: {
|
|
272
|
-
"Content-Type": "application/json"
|
|
273
|
-
},
|
|
274
|
-
body: JSON.stringify(body),
|
|
275
|
-
signal: combinedSignal
|
|
276
|
-
});
|
|
277
|
-
} finally {
|
|
278
|
-
clearTimeout(timerId);
|
|
279
|
-
}
|
|
280
|
-
} catch (error) {
|
|
281
|
-
const err = error;
|
|
282
|
-
if (err.name === "AbortError" && _optionalChain([request, 'access', _4 => _4.signal, 'optionalAccess', _5 => _5.aborted])) {
|
|
283
|
-
throw new ApiError("Request cancelled.", "cancelled", 0, false);
|
|
284
|
-
}
|
|
285
|
-
if (err.name === "AbortError") {
|
|
286
|
-
throw new ApiError(
|
|
287
|
-
`Ollama request timed out after ${this.timeout / 1e3}s. Local inference can be slow \u2014 consider increasing the timeout in your config.`,
|
|
288
|
-
"timeout",
|
|
289
|
-
0,
|
|
290
|
-
true
|
|
291
|
-
);
|
|
292
|
-
}
|
|
293
|
-
throw new ApiError(
|
|
294
|
-
`Cannot connect to Ollama at ${this.baseUrl}. Make sure Ollama is running (try 'ollama serve').`,
|
|
295
|
-
"network_error",
|
|
296
|
-
0,
|
|
297
|
-
true
|
|
298
|
-
);
|
|
299
|
-
}
|
|
300
|
-
if (!response.ok) {
|
|
301
|
-
const apiError = await this.buildApiError(response, body);
|
|
302
|
-
if (apiError === null) {
|
|
303
|
-
return this.makeRequest(body, request);
|
|
304
|
-
}
|
|
305
|
-
throw apiError;
|
|
306
|
-
}
|
|
307
|
-
if (request.stream) {
|
|
308
|
-
return this.handleStreamingResponse(response);
|
|
309
|
-
}
|
|
310
|
-
const data = await response.json();
|
|
311
|
-
let toolCalls;
|
|
312
|
-
if (data.message.tool_calls && Array.isArray(data.message.tool_calls)) {
|
|
313
|
-
toolCalls = data.message.tool_calls.map((tc, index) => ({
|
|
314
|
-
id: `ollama-tool-${Date.now()}-${index}`,
|
|
315
|
-
type: "function",
|
|
316
|
-
function: {
|
|
317
|
-
name: tc.function.name,
|
|
318
|
-
// Ollama returns arguments as object, convert to JSON string for consistency
|
|
319
|
-
arguments: JSON.stringify(tc.function.arguments)
|
|
320
|
-
}
|
|
321
|
-
}));
|
|
322
|
-
}
|
|
323
|
-
let usage;
|
|
324
|
-
if (data.prompt_eval_count !== void 0 || data.eval_count !== void 0) {
|
|
325
|
-
usage = {
|
|
326
|
-
promptTokens: _nullishCoalesce(data.prompt_eval_count, () => ( 0)),
|
|
327
|
-
completionTokens: _nullishCoalesce(data.eval_count, () => ( 0)),
|
|
328
|
-
totalTokens: (_nullishCoalesce(data.prompt_eval_count, () => ( 0))) + (_nullishCoalesce(data.eval_count, () => ( 0)))
|
|
329
|
-
};
|
|
330
|
-
}
|
|
331
|
-
return {
|
|
332
|
-
id: `ollama-${Date.now()}`,
|
|
333
|
-
created: Math.floor(new Date(data.created_at).getTime() / 1e3),
|
|
334
|
-
content: data.message.content,
|
|
335
|
-
toolCalls,
|
|
336
|
-
finishReason: _optionalChain([toolCalls, 'optionalAccess', _6 => _6.length]) ? "tool_calls" : "stop",
|
|
337
|
-
usage,
|
|
338
|
-
raw: data
|
|
339
|
-
};
|
|
340
|
-
}
|
|
341
|
-
/**
|
|
342
|
-
* Build an ApiError from a non-ok response.
|
|
343
|
-
*
|
|
344
|
-
* Returns `null` as a sentinel when the request was retried internally
|
|
345
|
-
* (e.g., disableTools retry). The caller must re-run makeRequest in
|
|
346
|
-
* that case.
|
|
347
|
-
*/
|
|
348
|
-
async buildApiError(response, body) {
|
|
349
|
-
let errorBody = "";
|
|
350
|
-
try {
|
|
351
|
-
errorBody = await response.text();
|
|
352
|
-
} catch (e3) {
|
|
353
|
-
}
|
|
354
|
-
if (errorBody.includes("does not support tools") && body.tools) {
|
|
355
|
-
console.warn(`Model ${body.model} does not support tools. Retrying without tool support.`);
|
|
356
|
-
this.disableTools = true;
|
|
357
|
-
delete body.tools;
|
|
358
|
-
return null;
|
|
359
|
-
}
|
|
360
|
-
if (response.status === 404) {
|
|
361
|
-
const baseError = classifyApiError(response.status, errorBody, response.headers);
|
|
362
|
-
return new ApiError(
|
|
363
|
-
`Model not found. Run 'ollama pull ${String(body.model)}' to download it.
|
|
364
|
-
${errorBody}`,
|
|
365
|
-
baseError.code,
|
|
366
|
-
baseError.httpStatus,
|
|
367
|
-
baseError.retryable,
|
|
368
|
-
baseError.retryAfterMs,
|
|
369
|
-
errorBody
|
|
370
|
-
);
|
|
371
|
-
}
|
|
372
|
-
return classifyApiError(response.status, errorBody, response.headers);
|
|
373
|
-
}
|
|
374
|
-
async handleStreamingResponse(response) {
|
|
375
|
-
const reader = _optionalChain([response, 'access', _7 => _7.body, 'optionalAccess', _8 => _8.getReader, 'call', _9 => _9()]);
|
|
376
|
-
if (!reader) {
|
|
377
|
-
throw new Error("No response body");
|
|
378
|
-
}
|
|
379
|
-
const decoder = new TextDecoder();
|
|
380
|
-
let fullContent = "";
|
|
381
|
-
let lastData = null;
|
|
382
|
-
let streamEndedWithDone = false;
|
|
383
|
-
try {
|
|
384
|
-
while (true) {
|
|
385
|
-
const chunkResult = await this.readWithTimeout(reader, this.chunkTimeout, fullContent);
|
|
386
|
-
if ("timedOut" in chunkResult) {
|
|
387
|
-
if (fullContent) {
|
|
388
|
-
return {
|
|
389
|
-
id: `ollama-${Date.now()}`,
|
|
390
|
-
created: lastData ? Math.floor(new Date(lastData.created_at).getTime() / 1e3) : Math.floor(Date.now() / 1e3),
|
|
391
|
-
content: fullContent,
|
|
392
|
-
finishReason: "length",
|
|
393
|
-
raw: lastData
|
|
394
|
-
};
|
|
395
|
-
}
|
|
396
|
-
throw new ApiError(
|
|
397
|
-
`Ollama stream timed out after ${this.chunkTimeout / 1e3}s with no data. Make sure Ollama is running and the model is loaded.`,
|
|
398
|
-
"timeout",
|
|
399
|
-
0,
|
|
400
|
-
true
|
|
401
|
-
);
|
|
402
|
-
}
|
|
403
|
-
const { done, value } = chunkResult;
|
|
404
|
-
if (done) {
|
|
405
|
-
break;
|
|
406
|
-
}
|
|
407
|
-
const chunk = decoder.decode(value, { stream: true });
|
|
408
|
-
const lines = chunk.split("\n").filter((line) => line.trim());
|
|
409
|
-
for (const line of lines) {
|
|
410
|
-
try {
|
|
411
|
-
const data = JSON.parse(line);
|
|
412
|
-
fullContent += data.message.content;
|
|
413
|
-
lastData = data;
|
|
414
|
-
if (data.done) {
|
|
415
|
-
streamEndedWithDone = true;
|
|
416
|
-
}
|
|
417
|
-
} catch (e4) {
|
|
418
|
-
}
|
|
419
|
-
}
|
|
420
|
-
if (streamEndedWithDone) {
|
|
421
|
-
break;
|
|
422
|
-
}
|
|
423
|
-
}
|
|
424
|
-
} finally {
|
|
425
|
-
reader.releaseLock();
|
|
426
|
-
}
|
|
427
|
-
const finishReason = streamEndedWithDone ? "stop" : "length";
|
|
428
|
-
return {
|
|
429
|
-
id: `ollama-${Date.now()}`,
|
|
430
|
-
created: lastData ? Math.floor(new Date(lastData.created_at).getTime() / 1e3) : Math.floor(Date.now() / 1e3),
|
|
431
|
-
content: fullContent,
|
|
432
|
-
finishReason,
|
|
433
|
-
raw: lastData
|
|
434
|
-
};
|
|
435
|
-
}
|
|
436
|
-
/**
|
|
437
|
-
* Read one chunk from the stream with a timeout.
|
|
438
|
-
* Returns `{ timedOut: true }` if the timeout fires before a chunk arrives.
|
|
439
|
-
*/
|
|
440
|
-
async readWithTimeout(reader, timeoutMs, _partialContent) {
|
|
441
|
-
let timerId;
|
|
442
|
-
const timeoutPromise = new Promise((resolve) => {
|
|
443
|
-
timerId = setTimeout(() => resolve({ timedOut: true }), timeoutMs);
|
|
444
|
-
});
|
|
445
|
-
try {
|
|
446
|
-
const result = await Promise.race([
|
|
447
|
-
reader.read(),
|
|
448
|
-
timeoutPromise
|
|
449
|
-
]);
|
|
450
|
-
return result;
|
|
451
|
-
} finally {
|
|
452
|
-
clearTimeout(timerId);
|
|
453
|
-
}
|
|
454
|
-
}
|
|
455
|
-
isNonRetryableError(error) {
|
|
456
|
-
if (error instanceof ApiError) {
|
|
457
|
-
return !error.retryable;
|
|
458
|
-
}
|
|
459
|
-
const classified = classifyApiError(0, error.message);
|
|
460
|
-
return !classified.retryable;
|
|
461
|
-
}
|
|
462
|
-
combineSignals(signal1, signal2) {
|
|
463
|
-
const controller = new AbortController();
|
|
464
|
-
const abort = () => controller.abort();
|
|
465
|
-
signal1.addEventListener("abort", abort, { once: true });
|
|
466
|
-
signal2.addEventListener("abort", abort, { once: true });
|
|
467
|
-
if (signal1.aborted || signal2.aborted) {
|
|
468
|
-
controller.abort();
|
|
469
|
-
}
|
|
470
|
-
return controller.signal;
|
|
471
|
-
}
|
|
472
|
-
sleep(ms) {
|
|
473
|
-
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
474
|
-
}
|
|
475
|
-
};
|
|
476
|
-
|
|
477
|
-
// src/providers/OpenAIProvider.ts
|
|
478
|
-
var OpenAIProvider = class {
|
|
479
|
-
constructor(config) {
|
|
480
|
-
this.baseUrl = config.baseUrl || "https://api.openai.com/v1";
|
|
481
|
-
this.apiKey = config.apiKey || "";
|
|
482
|
-
this.model = config.model || "gpt-4o";
|
|
483
|
-
}
|
|
484
|
-
getName() {
|
|
485
|
-
return "openai";
|
|
486
|
-
}
|
|
487
|
-
setModel(model) {
|
|
488
|
-
this.model = model;
|
|
489
|
-
}
|
|
490
|
-
async listModels() {
|
|
491
|
-
return [
|
|
492
|
-
"gpt-4o",
|
|
493
|
-
"gpt-4o-mini",
|
|
494
|
-
"gpt-4-turbo",
|
|
495
|
-
"gpt-4",
|
|
496
|
-
"gpt-3.5-turbo"
|
|
497
|
-
];
|
|
498
|
-
}
|
|
499
|
-
async isAvailable() {
|
|
500
|
-
try {
|
|
501
|
-
const response = await fetch(`${this.baseUrl}/models`, {
|
|
502
|
-
headers: {
|
|
503
|
-
"Authorization": `Bearer ${this.apiKey}`
|
|
504
|
-
}
|
|
505
|
-
});
|
|
506
|
-
return response.ok;
|
|
507
|
-
} catch (e5) {
|
|
508
|
-
return false;
|
|
509
|
-
}
|
|
510
|
-
}
|
|
511
|
-
async complete(request) {
|
|
512
|
-
const body = {
|
|
513
|
-
model: request.model || this.model,
|
|
514
|
-
messages: request.messages.map((msg) => {
|
|
515
|
-
const mapped = {
|
|
516
|
-
role: msg.role === "system" ? "system" : msg.role === "user" ? "user" : msg.role === "tool" ? "tool" : "assistant",
|
|
517
|
-
content: msg.content
|
|
518
|
-
};
|
|
519
|
-
if (msg.role === "tool" && msg.tool_call_id) {
|
|
520
|
-
mapped.tool_call_id = msg.tool_call_id;
|
|
521
|
-
}
|
|
522
|
-
if (msg.name) {
|
|
523
|
-
mapped.name = msg.name;
|
|
524
|
-
}
|
|
525
|
-
return mapped;
|
|
526
|
-
}),
|
|
527
|
-
temperature: request.temperature || 0.7,
|
|
528
|
-
max_tokens: request.maxTokens
|
|
529
|
-
};
|
|
530
|
-
if (request.tools && request.tools.length > 0) {
|
|
531
|
-
body.tools = request.tools.map((tool) => ({
|
|
532
|
-
type: "function",
|
|
533
|
-
function: {
|
|
534
|
-
name: tool.name,
|
|
535
|
-
description: tool.description,
|
|
536
|
-
parameters: _nullishCoalesce(tool.parameters, () => ( { type: "object", properties: {} }))
|
|
537
|
-
}
|
|
538
|
-
}));
|
|
539
|
-
if (request.toolChoice) {
|
|
540
|
-
body.tool_choice = request.toolChoice;
|
|
541
|
-
}
|
|
542
|
-
}
|
|
543
|
-
const response = await fetch(`${this.baseUrl}/chat/completions`, {
|
|
544
|
-
method: "POST",
|
|
545
|
-
headers: {
|
|
546
|
-
"Content-Type": "application/json",
|
|
547
|
-
"Authorization": `Bearer ${this.apiKey}`
|
|
548
|
-
},
|
|
549
|
-
body: JSON.stringify(body),
|
|
550
|
-
signal: request.signal
|
|
551
|
-
});
|
|
552
|
-
if (!response.ok) {
|
|
553
|
-
const error = await response.text();
|
|
554
|
-
throw new Error(`OpenAI API error: ${response.status} ${error}`);
|
|
555
|
-
}
|
|
556
|
-
const data = await response.json();
|
|
557
|
-
const message = data.choices[0].message;
|
|
558
|
-
const finishReason = data.choices[0].finish_reason;
|
|
559
|
-
let toolCalls;
|
|
560
|
-
if (message.tool_calls && Array.isArray(message.tool_calls)) {
|
|
561
|
-
toolCalls = message.tool_calls.map((tc) => ({
|
|
562
|
-
id: tc.id,
|
|
563
|
-
type: "function",
|
|
564
|
-
function: {
|
|
565
|
-
name: tc.function.name,
|
|
566
|
-
arguments: tc.function.arguments
|
|
567
|
-
}
|
|
568
|
-
}));
|
|
569
|
-
}
|
|
570
|
-
let usage;
|
|
571
|
-
if (data.usage) {
|
|
572
|
-
usage = {
|
|
573
|
-
promptTokens: data.usage.prompt_tokens,
|
|
574
|
-
completionTokens: data.usage.completion_tokens,
|
|
575
|
-
totalTokens: data.usage.total_tokens
|
|
576
|
-
};
|
|
577
|
-
}
|
|
578
|
-
return {
|
|
579
|
-
id: data.id,
|
|
580
|
-
created: data.created,
|
|
581
|
-
content: _nullishCoalesce(message.content, () => ( "")),
|
|
582
|
-
toolCalls,
|
|
583
|
-
finishReason,
|
|
584
|
-
usage,
|
|
585
|
-
raw: data
|
|
586
|
-
};
|
|
587
|
-
}
|
|
588
|
-
};
|
|
589
|
-
|
|
590
|
-
// src/providers/LlamaCppProvider.ts
|
|
591
|
-
var LlamaCppProvider = class {
|
|
592
|
-
constructor(config) {
|
|
593
|
-
const port = config.port || 8080;
|
|
594
|
-
this.baseUrl = config.baseUrl || `http://localhost:${port}`;
|
|
595
|
-
this.model = config.model || "llama-model";
|
|
596
|
-
}
|
|
597
|
-
getName() {
|
|
598
|
-
return "llamacpp";
|
|
599
|
-
}
|
|
600
|
-
setModel(model) {
|
|
601
|
-
this.model = model;
|
|
602
|
-
}
|
|
603
|
-
async listModels() {
|
|
604
|
-
try {
|
|
605
|
-
const response = await fetch(`${this.baseUrl}/v1/models`);
|
|
606
|
-
if (!response.ok) {
|
|
607
|
-
return this.model ? [this.model] : [];
|
|
608
|
-
}
|
|
609
|
-
const data = await response.json();
|
|
610
|
-
return _nullishCoalesce(_optionalChain([data, 'access', _10 => _10.data, 'optionalAccess', _11 => _11.map, 'call', _12 => _12((m) => m.id)]), () => ( [this.model]));
|
|
611
|
-
} catch (e6) {
|
|
612
|
-
return this.model ? [this.model] : [];
|
|
613
|
-
}
|
|
614
|
-
}
|
|
615
|
-
async isAvailable() {
|
|
616
|
-
try {
|
|
617
|
-
const response = await fetch(`${this.baseUrl}/health`);
|
|
618
|
-
return response.ok;
|
|
619
|
-
} catch (e7) {
|
|
620
|
-
return false;
|
|
621
|
-
}
|
|
622
|
-
}
|
|
623
|
-
async complete(request) {
|
|
624
|
-
const body = {
|
|
625
|
-
model: request.model || this.model,
|
|
626
|
-
messages: request.messages.map((msg) => {
|
|
627
|
-
const mapped = {
|
|
628
|
-
role: msg.role,
|
|
629
|
-
content: msg.content
|
|
630
|
-
};
|
|
631
|
-
if (msg.name) mapped.name = msg.name;
|
|
632
|
-
if (msg.role === "tool" && msg.tool_call_id) mapped.tool_call_id = msg.tool_call_id;
|
|
633
|
-
if (msg.role === "assistant" && msg.tool_calls) mapped.tool_calls = msg.tool_calls;
|
|
634
|
-
return mapped;
|
|
635
|
-
}),
|
|
636
|
-
temperature: _nullishCoalesce(request.temperature, () => ( 0.7)),
|
|
637
|
-
max_tokens: _nullishCoalesce(request.maxTokens, () => ( 4096)),
|
|
638
|
-
stream: false
|
|
639
|
-
};
|
|
640
|
-
if (request.tools && request.tools.length > 0) {
|
|
641
|
-
body.tools = request.tools.map((tool) => ({
|
|
642
|
-
type: "function",
|
|
643
|
-
function: {
|
|
644
|
-
name: tool.name,
|
|
645
|
-
description: tool.description,
|
|
646
|
-
parameters: _nullishCoalesce(tool.parameters, () => ( { type: "object", properties: {} }))
|
|
647
|
-
}
|
|
648
|
-
}));
|
|
649
|
-
}
|
|
650
|
-
const response = await fetch(`${this.baseUrl}/v1/chat/completions`, {
|
|
651
|
-
method: "POST",
|
|
652
|
-
headers: {
|
|
653
|
-
"Content-Type": "application/json"
|
|
654
|
-
},
|
|
655
|
-
body: JSON.stringify(body),
|
|
656
|
-
signal: request.signal
|
|
657
|
-
});
|
|
658
|
-
if (!response.ok) {
|
|
659
|
-
throw new Error(`llama.cpp API error: ${response.status} ${response.statusText}`);
|
|
660
|
-
}
|
|
661
|
-
const data = await response.json();
|
|
662
|
-
const choice = data.choices[0];
|
|
663
|
-
let toolCalls;
|
|
664
|
-
if (_optionalChain([choice, 'optionalAccess', _13 => _13.message, 'access', _14 => _14.tool_calls, 'optionalAccess', _15 => _15.length])) {
|
|
665
|
-
toolCalls = choice.message.tool_calls.map((tc) => ({
|
|
666
|
-
id: tc.id,
|
|
667
|
-
type: "function",
|
|
668
|
-
function: {
|
|
669
|
-
name: tc.function.name,
|
|
670
|
-
arguments: tc.function.arguments
|
|
671
|
-
}
|
|
672
|
-
}));
|
|
673
|
-
}
|
|
674
|
-
let usage;
|
|
675
|
-
if (data.usage) {
|
|
676
|
-
usage = {
|
|
677
|
-
promptTokens: data.usage.prompt_tokens,
|
|
678
|
-
completionTokens: data.usage.completion_tokens,
|
|
679
|
-
totalTokens: data.usage.total_tokens
|
|
680
|
-
};
|
|
681
|
-
}
|
|
682
|
-
const finishReason = _optionalChain([toolCalls, 'optionalAccess', _16 => _16.length]) ? "tool_calls" : _optionalChain([choice, 'optionalAccess', _17 => _17.finish_reason]) === "stop" || _optionalChain([choice, 'optionalAccess', _18 => _18.finish_reason]) === "length" || _optionalChain([choice, 'optionalAccess', _19 => _19.finish_reason]) === "content_filter" ? choice.finish_reason : "stop";
|
|
683
|
-
return {
|
|
684
|
-
id: data.id || `llamacpp-${Date.now()}`,
|
|
685
|
-
created: data.created || Math.floor(Date.now() / 1e3),
|
|
686
|
-
content: _nullishCoalesce(_optionalChain([choice, 'optionalAccess', _20 => _20.message, 'access', _21 => _21.content]), () => ( "")),
|
|
687
|
-
toolCalls,
|
|
688
|
-
finishReason,
|
|
689
|
-
usage,
|
|
690
|
-
raw: data
|
|
691
|
-
};
|
|
692
|
-
}
|
|
693
|
-
};
|
|
694
|
-
|
|
695
|
-
// src/providers/OpenRouterClient.ts
|
|
696
|
-
function sanitizeMessages(messages) {
|
|
697
|
-
return messages.map((msg) => {
|
|
698
|
-
const sanitized = {
|
|
699
|
-
role: msg.role,
|
|
700
|
-
content: msg.content
|
|
701
|
-
};
|
|
702
|
-
if (msg.role === "tool" && msg.tool_call_id) {
|
|
703
|
-
sanitized.tool_call_id = msg.tool_call_id;
|
|
704
|
-
}
|
|
705
|
-
if (msg.role === "assistant" && _optionalChain([msg, 'access', _22 => _22.tool_calls, 'optionalAccess', _23 => _23.length])) {
|
|
706
|
-
sanitized.tool_calls = msg.tool_calls;
|
|
707
|
-
}
|
|
708
|
-
if (msg.name) {
|
|
709
|
-
sanitized.name = msg.name;
|
|
710
|
-
}
|
|
711
|
-
return sanitized;
|
|
712
|
-
});
|
|
713
|
-
}
|
|
714
|
-
var DEFAULT_BASE_URL = "https://openrouter.ai/api/v1";
|
|
715
|
-
var DEFAULT_MAX_RETRIES2 = 3;
|
|
716
|
-
var MAX_ALLOWED_RETRIES2 = 5;
|
|
717
|
-
var DEFAULT_RETRY_DELAY2 = 1e3;
|
|
718
|
-
var DEFAULT_TIMEOUT2 = 3e4;
|
|
719
|
-
var OpenRouterClient = class {
|
|
720
|
-
constructor(settings, networkSettings) {
|
|
721
|
-
this.apiKey = _nullishCoalesce(settings.apiKey, () => ( ""));
|
|
722
|
-
this.baseUrl = _nullishCoalesce(settings.baseUrl, () => ( DEFAULT_BASE_URL));
|
|
723
|
-
this.defaultModel = settings.model;
|
|
724
|
-
const configuredRetries = _nullishCoalesce(_optionalChain([networkSettings, 'optionalAccess', _24 => _24.maxRetries]), () => ( DEFAULT_MAX_RETRIES2));
|
|
725
|
-
this.maxRetries = Math.min(
|
|
726
|
-
Math.max(0, configuredRetries),
|
|
727
|
-
MAX_ALLOWED_RETRIES2
|
|
728
|
-
);
|
|
729
|
-
this.retryDelay = _nullishCoalesce(_optionalChain([networkSettings, 'optionalAccess', _25 => _25.retryDelay]), () => ( DEFAULT_RETRY_DELAY2));
|
|
730
|
-
this.timeout = _nullishCoalesce(_optionalChain([networkSettings, 'optionalAccess', _26 => _26.timeout]), () => ( DEFAULT_TIMEOUT2));
|
|
731
|
-
}
|
|
732
|
-
setDefaultModel(model) {
|
|
733
|
-
this.defaultModel = model;
|
|
734
|
-
}
|
|
735
|
-
async complete(request) {
|
|
736
|
-
const payload = {
|
|
737
|
-
model: _nullishCoalesce(request.model, () => ( this.defaultModel)),
|
|
738
|
-
messages: sanitizeMessages(request.messages),
|
|
739
|
-
temperature: _nullishCoalesce(request.temperature, () => ( 0.2)),
|
|
740
|
-
max_tokens: _nullishCoalesce(request.maxTokens, () => ( 16e3)),
|
|
741
|
-
// Increased from 1000 to allow large file generation
|
|
742
|
-
stream: _nullishCoalesce(request.stream, () => ( false))
|
|
743
|
-
};
|
|
744
|
-
if (request.tools && request.tools.length > 0) {
|
|
745
|
-
payload.tools = request.tools.map((tool) => ({
|
|
746
|
-
type: "function",
|
|
747
|
-
function: {
|
|
748
|
-
name: tool.name,
|
|
749
|
-
description: tool.description,
|
|
750
|
-
parameters: _nullishCoalesce(tool.parameters, () => ( { type: "object", properties: {} }))
|
|
751
|
-
}
|
|
752
|
-
}));
|
|
753
|
-
if (request.toolChoice) {
|
|
754
|
-
payload.tool_choice = request.toolChoice;
|
|
755
|
-
}
|
|
756
|
-
}
|
|
757
|
-
const model = (_nullishCoalesce(request.model, () => ( this.defaultModel))).toLowerCase();
|
|
758
|
-
if (request.thinkingLevel && request.thinkingLevel !== "normal") {
|
|
759
|
-
if (model.includes("o1") || model.includes("o3")) {
|
|
760
|
-
if (request.thinkingLevel === "extended") {
|
|
761
|
-
payload.reasoning_effort = "high";
|
|
762
|
-
} else if (request.thinkingLevel === "none") {
|
|
763
|
-
payload.reasoning_effort = "low";
|
|
764
|
-
}
|
|
765
|
-
}
|
|
766
|
-
if (model.includes("claude") && request.thinkingLevel === "extended") {
|
|
767
|
-
payload.provider = {
|
|
768
|
-
anthropic: {
|
|
769
|
-
thinking: {
|
|
770
|
-
type: "enabled",
|
|
771
|
-
budget_tokens: 1e4
|
|
772
|
-
}
|
|
773
|
-
}
|
|
774
|
-
};
|
|
775
|
-
}
|
|
776
|
-
}
|
|
777
|
-
const headers = {
|
|
778
|
-
"Content-Type": "application/json",
|
|
779
|
-
"HTTP-Referer": "https://autohand.dev",
|
|
780
|
-
"X-OpenRouter-Title": "Autohand Code CLI",
|
|
781
|
-
"X-OpenRouter-Categories": "cli-agent"
|
|
782
|
-
};
|
|
783
|
-
if (this.apiKey) {
|
|
784
|
-
headers.Authorization = `Bearer ${this.apiKey}`;
|
|
785
|
-
}
|
|
786
|
-
const payloadJson = JSON.stringify(payload);
|
|
787
|
-
const payloadSizeBytes = payloadJson.length;
|
|
788
|
-
const maxPayloadSize = 5 * 1024 * 1024;
|
|
789
|
-
if (payloadSizeBytes > maxPayloadSize) {
|
|
790
|
-
const sizeMB = (payloadSizeBytes / (1024 * 1024)).toFixed(2);
|
|
791
|
-
throw new ApiError(
|
|
792
|
-
`Request payload too large (${sizeMB}MB). This usually happens when the conversation history grows too long. Try using /undo to remove recent turns or /new to start fresh.`,
|
|
793
|
-
"context_overflow",
|
|
794
|
-
400,
|
|
795
|
-
true
|
|
796
|
-
);
|
|
797
|
-
}
|
|
798
|
-
let lastError = null;
|
|
799
|
-
for (let attempt = 0; attempt <= this.maxRetries; attempt++) {
|
|
800
|
-
try {
|
|
801
|
-
const response = await this.makeRequest(
|
|
802
|
-
payload,
|
|
803
|
-
headers,
|
|
804
|
-
request.signal,
|
|
805
|
-
payloadJson
|
|
806
|
-
);
|
|
807
|
-
return response;
|
|
808
|
-
} catch (error) {
|
|
809
|
-
lastError = error;
|
|
810
|
-
if (this.isNonRetryableError(error)) {
|
|
811
|
-
throw error;
|
|
812
|
-
}
|
|
813
|
-
if (attempt < this.maxRetries) {
|
|
814
|
-
const delay = this.retryDelay * Math.pow(2, attempt);
|
|
815
|
-
await this.sleep(delay);
|
|
816
|
-
}
|
|
817
|
-
}
|
|
818
|
-
}
|
|
819
|
-
throw _nullishCoalesce(lastError, () => ( new ApiError("Failed to communicate with the AI service. Please try again.", "network_error", 0, true)));
|
|
820
|
-
}
|
|
821
|
-
async makeRequest(payload, headers, signal, preSerializedBody) {
|
|
822
|
-
let response;
|
|
823
|
-
try {
|
|
824
|
-
const timeoutController = new AbortController();
|
|
825
|
-
const timeoutId = setTimeout(
|
|
826
|
-
() => timeoutController.abort(),
|
|
827
|
-
this.timeout
|
|
828
|
-
);
|
|
829
|
-
const combinedSignal = signal ? this.combineSignals(signal, timeoutController.signal) : timeoutController.signal;
|
|
830
|
-
try {
|
|
831
|
-
response = await fetch(`${this.baseUrl}/chat/completions`, {
|
|
832
|
-
method: "POST",
|
|
833
|
-
headers,
|
|
834
|
-
body: _nullishCoalesce(preSerializedBody, () => ( JSON.stringify(payload))),
|
|
835
|
-
signal: combinedSignal
|
|
836
|
-
});
|
|
837
|
-
} finally {
|
|
838
|
-
clearTimeout(timeoutId);
|
|
839
|
-
}
|
|
840
|
-
} catch (error) {
|
|
841
|
-
const err = error;
|
|
842
|
-
if (err.name === "AbortError" && _optionalChain([signal, 'optionalAccess', _27 => _27.aborted])) {
|
|
843
|
-
throw new ApiError("Request cancelled.", "cancelled", 0, false);
|
|
844
|
-
}
|
|
845
|
-
if (err.name === "AbortError") {
|
|
846
|
-
throw new ApiError(
|
|
847
|
-
"Request timed out. The AI service may be experiencing high load.",
|
|
848
|
-
"timeout",
|
|
849
|
-
0,
|
|
850
|
-
true
|
|
851
|
-
);
|
|
852
|
-
}
|
|
853
|
-
throw new ApiError(
|
|
854
|
-
"Unable to connect to the AI service. Please check your internet connection.",
|
|
855
|
-
"network_error",
|
|
856
|
-
0,
|
|
857
|
-
true
|
|
858
|
-
);
|
|
859
|
-
}
|
|
860
|
-
if (!response.ok) {
|
|
861
|
-
throw await this.buildApiError(response);
|
|
862
|
-
}
|
|
863
|
-
const json = await response.json();
|
|
864
|
-
const message = _optionalChain([json, 'optionalAccess', _28 => _28.choices, 'optionalAccess', _29 => _29[0], 'optionalAccess', _30 => _30.message]);
|
|
865
|
-
const text = _nullishCoalesce(_optionalChain([message, 'optionalAccess', _31 => _31.content]), () => ( ""));
|
|
866
|
-
const finishReason = _optionalChain([json, 'optionalAccess', _32 => _32.choices, 'optionalAccess', _33 => _33[0], 'optionalAccess', _34 => _34.finish_reason]);
|
|
867
|
-
let toolCalls;
|
|
868
|
-
if (_optionalChain([message, 'optionalAccess', _35 => _35.tool_calls]) && Array.isArray(message.tool_calls)) {
|
|
869
|
-
toolCalls = message.tool_calls.map((tc) => {
|
|
870
|
-
const rawArgs = _optionalChain([tc, 'access', _36 => _36.function, 'optionalAccess', _37 => _37.arguments]);
|
|
871
|
-
return {
|
|
872
|
-
id: tc.id,
|
|
873
|
-
type: "function",
|
|
874
|
-
function: {
|
|
875
|
-
name: _nullishCoalesce(_optionalChain([tc, 'access', _38 => _38.function, 'optionalAccess', _39 => _39.name]), () => ( "")),
|
|
876
|
-
arguments: _nullishCoalesce(rawArgs, () => ( "{}"))
|
|
877
|
-
}
|
|
878
|
-
};
|
|
879
|
-
});
|
|
880
|
-
}
|
|
881
|
-
let usage;
|
|
882
|
-
if (_optionalChain([json, 'optionalAccess', _40 => _40.usage])) {
|
|
883
|
-
usage = {
|
|
884
|
-
promptTokens: _nullishCoalesce(json.usage.prompt_tokens, () => ( 0)),
|
|
885
|
-
completionTokens: _nullishCoalesce(json.usage.completion_tokens, () => ( 0)),
|
|
886
|
-
totalTokens: _nullishCoalesce(json.usage.total_tokens, () => ( 0))
|
|
887
|
-
};
|
|
888
|
-
}
|
|
889
|
-
return {
|
|
890
|
-
id: _nullishCoalesce(json.id, () => ( "autohand-local")),
|
|
891
|
-
created: _nullishCoalesce(json.created, () => ( Date.now())),
|
|
892
|
-
content: text,
|
|
893
|
-
toolCalls,
|
|
894
|
-
finishReason,
|
|
895
|
-
usage,
|
|
896
|
-
raw: json
|
|
897
|
-
};
|
|
898
|
-
}
|
|
899
|
-
async buildApiError(response) {
|
|
900
|
-
const status = response.status;
|
|
901
|
-
let errorDetail = "";
|
|
902
|
-
try {
|
|
903
|
-
const body = await response.json();
|
|
904
|
-
errorDetail = _optionalChain([body, 'optionalAccess', _41 => _41.error, 'optionalAccess', _42 => _42.message]) || _optionalChain([body, 'optionalAccess', _43 => _43.error]) || _optionalChain([body, 'optionalAccess', _44 => _44.message]) || "";
|
|
905
|
-
if (typeof errorDetail === "object") {
|
|
906
|
-
errorDetail = JSON.stringify(errorDetail);
|
|
907
|
-
}
|
|
908
|
-
} catch (e8) {
|
|
909
|
-
try {
|
|
910
|
-
errorDetail = await response.text();
|
|
911
|
-
} catch (e9) {
|
|
912
|
-
}
|
|
913
|
-
}
|
|
914
|
-
return classifyApiError(status, errorDetail, response.headers);
|
|
915
|
-
}
|
|
916
|
-
isNonRetryableError(error) {
|
|
917
|
-
if (error instanceof ApiError) {
|
|
918
|
-
return !error.retryable;
|
|
919
|
-
}
|
|
920
|
-
const classified = classifyApiError(0, error.message);
|
|
921
|
-
return !classified.retryable;
|
|
922
|
-
}
|
|
923
|
-
combineSignals(signal1, signal2) {
|
|
924
|
-
const controller = new AbortController();
|
|
925
|
-
const abort = () => controller.abort();
|
|
926
|
-
signal1.addEventListener("abort", abort, { once: true });
|
|
927
|
-
signal2.addEventListener("abort", abort, { once: true });
|
|
928
|
-
if (signal1.aborted || signal2.aborted) {
|
|
929
|
-
controller.abort();
|
|
930
|
-
}
|
|
931
|
-
return controller.signal;
|
|
932
|
-
}
|
|
933
|
-
sleep(ms) {
|
|
934
|
-
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
935
|
-
}
|
|
936
|
-
};
|
|
937
|
-
|
|
938
|
-
// src/providers/OpenRouterProvider.ts
|
|
939
|
-
var OpenRouterProvider = class {
|
|
940
|
-
constructor(config, networkSettings) {
|
|
941
|
-
this.client = new OpenRouterClient(config, networkSettings);
|
|
942
|
-
this.model = config.model;
|
|
943
|
-
}
|
|
944
|
-
getName() {
|
|
945
|
-
return "openrouter";
|
|
946
|
-
}
|
|
947
|
-
setModel(model) {
|
|
948
|
-
this.model = model;
|
|
949
|
-
this.client.setDefaultModel(model);
|
|
950
|
-
}
|
|
951
|
-
async listModels() {
|
|
952
|
-
return [
|
|
953
|
-
"anthropic/claude-3.5-sonnet",
|
|
954
|
-
"anthropic/claude-3-opus",
|
|
955
|
-
"google/gemini-pro-1.5",
|
|
956
|
-
"openai/gpt-4o",
|
|
957
|
-
"x-ai/grok-2-latest",
|
|
958
|
-
"meta-llama/llama-3.1-70b-instruct"
|
|
959
|
-
];
|
|
960
|
-
}
|
|
961
|
-
async isAvailable() {
|
|
962
|
-
return true;
|
|
963
|
-
}
|
|
964
|
-
async complete(request) {
|
|
965
|
-
return this.client.complete(request);
|
|
966
|
-
}
|
|
967
|
-
};
|
|
968
|
-
|
|
969
|
-
// src/utils/platform.ts
|
|
970
|
-
function isAppleSilicon() {
|
|
971
|
-
return process.platform === "darwin" && process.arch === "arm64";
|
|
972
|
-
}
|
|
973
|
-
function isMLXSupported() {
|
|
974
|
-
return isAppleSilicon();
|
|
975
|
-
}
|
|
976
|
-
|
|
977
|
-
// src/providers/MLXProvider.ts
|
|
978
|
-
var DEFAULT_TIMEOUT3 = 6e4;
|
|
979
|
-
var DEFAULT_MAX_RETRIES3 = 2;
|
|
980
|
-
var MAX_ALLOWED_RETRIES3 = 5;
|
|
981
|
-
var DEFAULT_RETRY_DELAY3 = 1e3;
|
|
982
|
-
var AVAILABILITY_TIMEOUT2 = 5e3;
|
|
983
|
-
var MLXProvider = class {
|
|
984
|
-
constructor(config, networkSettings) {
|
|
985
|
-
const port = config.port || 8080;
|
|
986
|
-
this.baseUrl = config.baseUrl || `http://localhost:${port}`;
|
|
987
|
-
this.model = config.model || "mlx-model";
|
|
988
|
-
const configuredRetries = _nullishCoalesce(_optionalChain([networkSettings, 'optionalAccess', _45 => _45.maxRetries]), () => ( DEFAULT_MAX_RETRIES3));
|
|
989
|
-
this.maxRetries = Math.min(Math.max(0, configuredRetries), MAX_ALLOWED_RETRIES3);
|
|
990
|
-
this.retryDelay = _nullishCoalesce(_optionalChain([networkSettings, 'optionalAccess', _46 => _46.retryDelay]), () => ( DEFAULT_RETRY_DELAY3));
|
|
991
|
-
this.timeout = _nullishCoalesce(_optionalChain([networkSettings, 'optionalAccess', _47 => _47.timeout]), () => ( DEFAULT_TIMEOUT3));
|
|
992
|
-
}
|
|
993
|
-
getName() {
|
|
994
|
-
return "mlx";
|
|
995
|
-
}
|
|
996
|
-
setModel(model) {
|
|
997
|
-
this.model = model;
|
|
998
|
-
}
|
|
999
|
-
async listModels() {
|
|
1000
|
-
if (!isMLXSupported()) {
|
|
1001
|
-
return [];
|
|
1002
|
-
}
|
|
1003
|
-
try {
|
|
1004
|
-
const controller = new AbortController();
|
|
1005
|
-
const timerId = setTimeout(() => controller.abort(), AVAILABILITY_TIMEOUT2);
|
|
1006
|
-
try {
|
|
1007
|
-
const response = await fetch(`${this.baseUrl}/v1/models`, {
|
|
1008
|
-
signal: controller.signal
|
|
1009
|
-
});
|
|
1010
|
-
if (!response.ok) {
|
|
1011
|
-
return this.model ? [this.model] : [];
|
|
1012
|
-
}
|
|
1013
|
-
const data = await response.json();
|
|
1014
|
-
return _nullishCoalesce(_optionalChain([data, 'access', _48 => _48.data, 'optionalAccess', _49 => _49.map, 'call', _50 => _50((m) => m.id)]), () => ( (this.model ? [this.model] : [])));
|
|
1015
|
-
} finally {
|
|
1016
|
-
clearTimeout(timerId);
|
|
1017
|
-
}
|
|
1018
|
-
} catch (e10) {
|
|
1019
|
-
return this.model ? [this.model] : [];
|
|
1020
|
-
}
|
|
1021
|
-
}
|
|
1022
|
-
async isAvailable() {
|
|
1023
|
-
if (!isMLXSupported()) {
|
|
1024
|
-
return false;
|
|
1025
|
-
}
|
|
1026
|
-
try {
|
|
1027
|
-
const controller = new AbortController();
|
|
1028
|
-
const timerId = setTimeout(() => controller.abort(), AVAILABILITY_TIMEOUT2);
|
|
1029
|
-
try {
|
|
1030
|
-
const response = await fetch(`${this.baseUrl}/v1/models`, {
|
|
1031
|
-
signal: controller.signal
|
|
1032
|
-
});
|
|
1033
|
-
return response.ok;
|
|
1034
|
-
} finally {
|
|
1035
|
-
clearTimeout(timerId);
|
|
1036
|
-
}
|
|
1037
|
-
} catch (e11) {
|
|
1038
|
-
return false;
|
|
1039
|
-
}
|
|
1040
|
-
}
|
|
1041
|
-
async complete(request) {
|
|
1042
|
-
if (!isMLXSupported()) {
|
|
1043
|
-
throw new Error("MLX is only supported on macOS with Apple Silicon");
|
|
1044
|
-
}
|
|
1045
|
-
const body = {
|
|
1046
|
-
model: request.model || this.model,
|
|
1047
|
-
messages: request.messages.map((msg) => {
|
|
1048
|
-
const mapped = {
|
|
1049
|
-
role: msg.role,
|
|
1050
|
-
content: msg.content
|
|
1051
|
-
};
|
|
1052
|
-
if (msg.name) mapped.name = msg.name;
|
|
1053
|
-
if (msg.role === "tool" && msg.tool_call_id) mapped.tool_call_id = msg.tool_call_id;
|
|
1054
|
-
if (msg.role === "assistant" && msg.tool_calls) mapped.tool_calls = msg.tool_calls;
|
|
1055
|
-
return mapped;
|
|
1056
|
-
}),
|
|
1057
|
-
temperature: _nullishCoalesce(request.temperature, () => ( 0.7)),
|
|
1058
|
-
max_tokens: _nullishCoalesce(request.maxTokens, () => ( 4096)),
|
|
1059
|
-
stream: false
|
|
1060
|
-
};
|
|
1061
|
-
if (request.tools && request.tools.length > 0) {
|
|
1062
|
-
body.tools = request.tools.map((tool) => ({
|
|
1063
|
-
type: "function",
|
|
1064
|
-
function: {
|
|
1065
|
-
name: tool.name,
|
|
1066
|
-
description: tool.description,
|
|
1067
|
-
parameters: _nullishCoalesce(tool.parameters, () => ( { type: "object", properties: {} }))
|
|
1068
|
-
}
|
|
1069
|
-
}));
|
|
1070
|
-
}
|
|
1071
|
-
let lastError = null;
|
|
1072
|
-
for (let attempt = 0; attempt <= this.maxRetries; attempt++) {
|
|
1073
|
-
try {
|
|
1074
|
-
return await this.makeRequest(body, request.signal);
|
|
1075
|
-
} catch (error) {
|
|
1076
|
-
lastError = error;
|
|
1077
|
-
if (this.isNonRetryableError(error)) {
|
|
1078
|
-
throw error;
|
|
1079
|
-
}
|
|
1080
|
-
if (attempt < this.maxRetries) {
|
|
1081
|
-
const delay = this.retryDelay * Math.pow(2, attempt);
|
|
1082
|
-
await this.sleep(delay);
|
|
1083
|
-
}
|
|
1084
|
-
}
|
|
1085
|
-
}
|
|
1086
|
-
throw _nullishCoalesce(lastError, () => ( new ApiError(
|
|
1087
|
-
"Failed to communicate with the MLX server. Please try again.",
|
|
1088
|
-
"network_error",
|
|
1089
|
-
0,
|
|
1090
|
-
true
|
|
1091
|
-
)));
|
|
1092
|
-
}
|
|
1093
|
-
async makeRequest(body, userSignal) {
|
|
1094
|
-
let response;
|
|
1095
|
-
try {
|
|
1096
|
-
const timeoutController = new AbortController();
|
|
1097
|
-
const timerId = setTimeout(() => timeoutController.abort(), this.timeout);
|
|
1098
|
-
const combinedSignal = userSignal ? this.combineSignals(userSignal, timeoutController.signal) : timeoutController.signal;
|
|
1099
|
-
try {
|
|
1100
|
-
response = await fetch(`${this.baseUrl}/v1/chat/completions`, {
|
|
1101
|
-
method: "POST",
|
|
1102
|
-
headers: {
|
|
1103
|
-
"Content-Type": "application/json"
|
|
1104
|
-
},
|
|
1105
|
-
body: JSON.stringify(body),
|
|
1106
|
-
signal: combinedSignal
|
|
1107
|
-
});
|
|
1108
|
-
} finally {
|
|
1109
|
-
clearTimeout(timerId);
|
|
1110
|
-
}
|
|
1111
|
-
} catch (error) {
|
|
1112
|
-
const err = error;
|
|
1113
|
-
if (err.name === "AbortError" && _optionalChain([userSignal, 'optionalAccess', _51 => _51.aborted])) {
|
|
1114
|
-
throw new ApiError("Request cancelled.", "cancelled", 0, false);
|
|
1115
|
-
}
|
|
1116
|
-
if (err.name === "AbortError") {
|
|
1117
|
-
throw new ApiError(
|
|
1118
|
-
`MLX server request timed out after ${this.timeout / 1e3}s. Local inference can be slow \u2014 consider increasing the timeout in your config.`,
|
|
1119
|
-
"timeout",
|
|
1120
|
-
0,
|
|
1121
|
-
true
|
|
1122
|
-
);
|
|
1123
|
-
}
|
|
1124
|
-
throw new ApiError(
|
|
1125
|
-
`Cannot connect to MLX server at ${this.baseUrl}. Make sure it is running.`,
|
|
1126
|
-
"network_error",
|
|
1127
|
-
0,
|
|
1128
|
-
true
|
|
1129
|
-
);
|
|
1130
|
-
}
|
|
1131
|
-
if (!response.ok) {
|
|
1132
|
-
throw await this.buildApiError(response);
|
|
1133
|
-
}
|
|
1134
|
-
const data = await response.json();
|
|
1135
|
-
const choice = data.choices[0];
|
|
1136
|
-
let toolCalls;
|
|
1137
|
-
if (_optionalChain([choice, 'optionalAccess', _52 => _52.message, 'access', _53 => _53.tool_calls, 'optionalAccess', _54 => _54.length])) {
|
|
1138
|
-
toolCalls = choice.message.tool_calls.map((tc) => ({
|
|
1139
|
-
id: tc.id,
|
|
1140
|
-
type: "function",
|
|
1141
|
-
function: {
|
|
1142
|
-
name: tc.function.name,
|
|
1143
|
-
arguments: tc.function.arguments
|
|
1144
|
-
}
|
|
1145
|
-
}));
|
|
1146
|
-
}
|
|
1147
|
-
let usage;
|
|
1148
|
-
if (data.usage) {
|
|
1149
|
-
usage = {
|
|
1150
|
-
promptTokens: data.usage.prompt_tokens,
|
|
1151
|
-
completionTokens: data.usage.completion_tokens,
|
|
1152
|
-
totalTokens: data.usage.total_tokens
|
|
1153
|
-
};
|
|
1154
|
-
}
|
|
1155
|
-
const finishReason = _optionalChain([toolCalls, 'optionalAccess', _55 => _55.length]) ? "tool_calls" : _optionalChain([choice, 'optionalAccess', _56 => _56.finish_reason]) === "stop" || _optionalChain([choice, 'optionalAccess', _57 => _57.finish_reason]) === "length" || _optionalChain([choice, 'optionalAccess', _58 => _58.finish_reason]) === "content_filter" ? choice.finish_reason : "stop";
|
|
1156
|
-
return {
|
|
1157
|
-
id: data.id || `mlx-${Date.now()}`,
|
|
1158
|
-
created: data.created || Math.floor(Date.now() / 1e3),
|
|
1159
|
-
content: _nullishCoalesce(_optionalChain([choice, 'optionalAccess', _59 => _59.message, 'access', _60 => _60.content]), () => ( "")),
|
|
1160
|
-
toolCalls,
|
|
1161
|
-
finishReason,
|
|
1162
|
-
usage,
|
|
1163
|
-
raw: data
|
|
1164
|
-
};
|
|
1165
|
-
}
|
|
1166
|
-
async buildApiError(response) {
|
|
1167
|
-
let errorDetail = "";
|
|
1168
|
-
try {
|
|
1169
|
-
const body = await response.json();
|
|
1170
|
-
const maybeError = _optionalChain([body, 'optionalAccess', _61 => _61.error]);
|
|
1171
|
-
if (maybeError && typeof maybeError === "object") {
|
|
1172
|
-
errorDetail = _nullishCoalesce(_optionalChain([maybeError, 'optionalAccess', _62 => _62.message]), () => ( ""));
|
|
1173
|
-
} else if (typeof maybeError === "string") {
|
|
1174
|
-
errorDetail = maybeError;
|
|
1175
|
-
} else if (typeof _optionalChain([body, 'optionalAccess', _63 => _63.message]) === "string") {
|
|
1176
|
-
errorDetail = body.message;
|
|
1177
|
-
}
|
|
1178
|
-
if (typeof errorDetail === "object") {
|
|
1179
|
-
errorDetail = JSON.stringify(errorDetail);
|
|
1180
|
-
}
|
|
1181
|
-
} catch (e12) {
|
|
1182
|
-
try {
|
|
1183
|
-
errorDetail = await response.text();
|
|
1184
|
-
} catch (e13) {
|
|
1185
|
-
}
|
|
1186
|
-
}
|
|
1187
|
-
return classifyApiError(response.status, errorDetail, response.headers);
|
|
1188
|
-
}
|
|
1189
|
-
isNonRetryableError(error) {
|
|
1190
|
-
if (error instanceof ApiError) {
|
|
1191
|
-
return !error.retryable;
|
|
1192
|
-
}
|
|
1193
|
-
const classified = classifyApiError(0, error.message);
|
|
1194
|
-
return !classified.retryable;
|
|
1195
|
-
}
|
|
1196
|
-
combineSignals(signal1, signal2) {
|
|
1197
|
-
const controller = new AbortController();
|
|
1198
|
-
const abort = () => controller.abort();
|
|
1199
|
-
signal1.addEventListener("abort", abort, { once: true });
|
|
1200
|
-
signal2.addEventListener("abort", abort, { once: true });
|
|
1201
|
-
if (signal1.aborted || signal2.aborted) {
|
|
1202
|
-
controller.abort();
|
|
1203
|
-
}
|
|
1204
|
-
return controller.signal;
|
|
1205
|
-
}
|
|
1206
|
-
sleep(ms) {
|
|
1207
|
-
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
1208
|
-
}
|
|
1209
|
-
};
|
|
1210
|
-
|
|
1211
|
-
// src/providers/LLMGatewayClient.ts
|
|
1212
|
-
function sanitizeMessages2(messages) {
|
|
1213
|
-
return messages.map((msg) => {
|
|
1214
|
-
const sanitized = {
|
|
1215
|
-
role: msg.role,
|
|
1216
|
-
content: msg.content
|
|
1217
|
-
};
|
|
1218
|
-
if (msg.role === "tool" && msg.tool_call_id) {
|
|
1219
|
-
sanitized.tool_call_id = msg.tool_call_id;
|
|
1220
|
-
}
|
|
1221
|
-
if (msg.role === "assistant" && _optionalChain([msg, 'access', _64 => _64.tool_calls, 'optionalAccess', _65 => _65.length])) {
|
|
1222
|
-
sanitized.tool_calls = msg.tool_calls;
|
|
1223
|
-
}
|
|
1224
|
-
if (msg.name) {
|
|
1225
|
-
sanitized.name = msg.name;
|
|
1226
|
-
}
|
|
1227
|
-
return sanitized;
|
|
1228
|
-
});
|
|
1229
|
-
}
|
|
1230
|
-
var DEFAULT_BASE_URL2 = "https://api.llmgateway.io/v1";
|
|
1231
|
-
var DEFAULT_MAX_RETRIES4 = 3;
|
|
1232
|
-
var MAX_ALLOWED_RETRIES4 = 5;
|
|
1233
|
-
var DEFAULT_RETRY_DELAY4 = 1e3;
|
|
1234
|
-
var DEFAULT_TIMEOUT4 = 3e4;
|
|
1235
|
-
var FRIENDLY_ERRORS = {
|
|
1236
|
-
400: "The request was malformed. This often happens when the context is too long. Try /undo to remove recent turns or /new to start fresh.",
|
|
1237
|
-
401: "Authentication failed. Please verify your LLM Gateway API key in ~/.autohand/config.json.",
|
|
1238
|
-
402: "Payment required. Please check your LLM Gateway account balance or billing settings.",
|
|
1239
|
-
403: "Access denied. Your API key may not have permission for this model.",
|
|
1240
|
-
404: "The requested model was not found. Use /model to select a different one.",
|
|
1241
|
-
429: "Rate limit exceeded. Please wait a moment and try again, or choose a different model.",
|
|
1242
|
-
500: "The LLM Gateway service encountered an internal error. Please try again later.",
|
|
1243
|
-
502: "The LLM Gateway service is temporarily unavailable. Please try again in a few moments.",
|
|
1244
|
-
503: "The LLM Gateway service is currently overloaded. Please try again later.",
|
|
1245
|
-
504: "The request timed out. The service may be experiencing high load."
|
|
1246
|
-
};
|
|
1247
|
-
var LLMGatewayClient = class {
|
|
1248
|
-
constructor(settings, networkSettings) {
|
|
1249
|
-
this.apiKey = _nullishCoalesce(settings.apiKey, () => ( ""));
|
|
1250
|
-
this.baseUrl = _nullishCoalesce(settings.baseUrl, () => ( DEFAULT_BASE_URL2));
|
|
1251
|
-
this.defaultModel = settings.model;
|
|
1252
|
-
const configuredRetries = _nullishCoalesce(_optionalChain([networkSettings, 'optionalAccess', _66 => _66.maxRetries]), () => ( DEFAULT_MAX_RETRIES4));
|
|
1253
|
-
this.maxRetries = Math.min(
|
|
1254
|
-
Math.max(0, configuredRetries),
|
|
1255
|
-
MAX_ALLOWED_RETRIES4
|
|
1256
|
-
);
|
|
1257
|
-
this.retryDelay = _nullishCoalesce(_optionalChain([networkSettings, 'optionalAccess', _67 => _67.retryDelay]), () => ( DEFAULT_RETRY_DELAY4));
|
|
1258
|
-
this.timeout = _nullishCoalesce(_optionalChain([networkSettings, 'optionalAccess', _68 => _68.timeout]), () => ( DEFAULT_TIMEOUT4));
|
|
1259
|
-
}
|
|
1260
|
-
setDefaultModel(model) {
|
|
1261
|
-
this.defaultModel = model;
|
|
1262
|
-
}
|
|
1263
|
-
async complete(request) {
|
|
1264
|
-
const payload = {
|
|
1265
|
-
model: _nullishCoalesce(request.model, () => ( this.defaultModel)),
|
|
1266
|
-
messages: sanitizeMessages2(request.messages),
|
|
1267
|
-
temperature: _nullishCoalesce(request.temperature, () => ( 0.2)),
|
|
1268
|
-
max_tokens: _nullishCoalesce(request.maxTokens, () => ( 16e3)),
|
|
1269
|
-
stream: _nullishCoalesce(request.stream, () => ( false))
|
|
1270
|
-
};
|
|
1271
|
-
if (request.tools && request.tools.length > 0) {
|
|
1272
|
-
payload.tools = request.tools.map((tool) => ({
|
|
1273
|
-
type: "function",
|
|
1274
|
-
function: {
|
|
1275
|
-
name: tool.name,
|
|
1276
|
-
description: tool.description,
|
|
1277
|
-
parameters: _nullishCoalesce(tool.parameters, () => ( { type: "object", properties: {} }))
|
|
1278
|
-
}
|
|
1279
|
-
}));
|
|
1280
|
-
if (request.toolChoice) {
|
|
1281
|
-
payload.tool_choice = request.toolChoice;
|
|
1282
|
-
}
|
|
1283
|
-
}
|
|
1284
|
-
const headers = {
|
|
1285
|
-
"Content-Type": "application/json"
|
|
1286
|
-
};
|
|
1287
|
-
if (this.apiKey) {
|
|
1288
|
-
headers.Authorization = `Bearer ${this.apiKey}`;
|
|
1289
|
-
}
|
|
1290
|
-
const payloadJson = JSON.stringify(payload);
|
|
1291
|
-
const payloadSizeBytes = payloadJson.length;
|
|
1292
|
-
const maxPayloadSize = 5 * 1024 * 1024;
|
|
1293
|
-
if (payloadSizeBytes > maxPayloadSize) {
|
|
1294
|
-
const sizeMB = (payloadSizeBytes / (1024 * 1024)).toFixed(2);
|
|
1295
|
-
throw new Error(
|
|
1296
|
-
`Request payload too large (${sizeMB}MB). This usually happens when the conversation history grows too long. Try using /undo to remove recent turns or /new to start fresh.`
|
|
1297
|
-
);
|
|
1298
|
-
}
|
|
1299
|
-
let lastError = null;
|
|
1300
|
-
for (let attempt = 0; attempt <= this.maxRetries; attempt++) {
|
|
1301
|
-
try {
|
|
1302
|
-
const response = await this.makeRequest(
|
|
1303
|
-
payload,
|
|
1304
|
-
headers,
|
|
1305
|
-
request.signal,
|
|
1306
|
-
payloadJson
|
|
1307
|
-
);
|
|
1308
|
-
return response;
|
|
1309
|
-
} catch (error) {
|
|
1310
|
-
lastError = error;
|
|
1311
|
-
if (this.isNonRetryableError(error)) {
|
|
1312
|
-
throw error;
|
|
1313
|
-
}
|
|
1314
|
-
if (attempt < this.maxRetries) {
|
|
1315
|
-
const delay = this.retryDelay * Math.pow(2, attempt);
|
|
1316
|
-
await this.sleep(delay);
|
|
1317
|
-
}
|
|
1318
|
-
}
|
|
1319
|
-
}
|
|
1320
|
-
throw _nullishCoalesce(lastError, () => ( new Error("Failed to communicate with LLM Gateway. Please try again.")));
|
|
1321
|
-
}
|
|
1322
|
-
async makeRequest(payload, headers, signal, preSerializedBody) {
|
|
1323
|
-
let response;
|
|
1324
|
-
try {
|
|
1325
|
-
const timeoutController = new AbortController();
|
|
1326
|
-
const timeoutId = setTimeout(
|
|
1327
|
-
() => timeoutController.abort(),
|
|
1328
|
-
this.timeout
|
|
1329
|
-
);
|
|
1330
|
-
const combinedSignal = signal ? this.combineSignals(signal, timeoutController.signal) : timeoutController.signal;
|
|
1331
|
-
try {
|
|
1332
|
-
response = await fetch(`${this.baseUrl}/chat/completions`, {
|
|
1333
|
-
method: "POST",
|
|
1334
|
-
headers,
|
|
1335
|
-
body: _nullishCoalesce(preSerializedBody, () => ( JSON.stringify(payload))),
|
|
1336
|
-
signal: combinedSignal
|
|
1337
|
-
});
|
|
1338
|
-
} finally {
|
|
1339
|
-
clearTimeout(timeoutId);
|
|
1340
|
-
}
|
|
1341
|
-
} catch (error) {
|
|
1342
|
-
const err = error;
|
|
1343
|
-
if (err.name === "AbortError" && _optionalChain([signal, 'optionalAccess', _69 => _69.aborted])) {
|
|
1344
|
-
throw new Error("Request cancelled.");
|
|
1345
|
-
}
|
|
1346
|
-
if (err.name === "AbortError") {
|
|
1347
|
-
throw new Error(
|
|
1348
|
-
"Request timed out. The LLM Gateway service may be experiencing high load."
|
|
1349
|
-
);
|
|
1350
|
-
}
|
|
1351
|
-
throw new Error(
|
|
1352
|
-
"Unable to connect to LLM Gateway. Please check your internet connection."
|
|
1353
|
-
);
|
|
1354
|
-
}
|
|
1355
|
-
if (!response.ok) {
|
|
1356
|
-
throw new Error(await this.buildFriendlyError(response));
|
|
1357
|
-
}
|
|
1358
|
-
const json = await response.json();
|
|
1359
|
-
const message = _optionalChain([json, 'optionalAccess', _70 => _70.choices, 'optionalAccess', _71 => _71[0], 'optionalAccess', _72 => _72.message]);
|
|
1360
|
-
const text = _nullishCoalesce(_optionalChain([message, 'optionalAccess', _73 => _73.content]), () => ( ""));
|
|
1361
|
-
const finishReason = _optionalChain([json, 'optionalAccess', _74 => _74.choices, 'optionalAccess', _75 => _75[0], 'optionalAccess', _76 => _76.finish_reason]);
|
|
1362
|
-
let toolCalls;
|
|
1363
|
-
if (_optionalChain([message, 'optionalAccess', _77 => _77.tool_calls]) && Array.isArray(message.tool_calls)) {
|
|
1364
|
-
toolCalls = message.tool_calls.map((tc) => {
|
|
1365
|
-
const rawArgs = _optionalChain([tc, 'access', _78 => _78.function, 'optionalAccess', _79 => _79.arguments]);
|
|
1366
|
-
return {
|
|
1367
|
-
id: tc.id,
|
|
1368
|
-
type: "function",
|
|
1369
|
-
function: {
|
|
1370
|
-
name: _nullishCoalesce(_optionalChain([tc, 'access', _80 => _80.function, 'optionalAccess', _81 => _81.name]), () => ( "")),
|
|
1371
|
-
arguments: _nullishCoalesce(rawArgs, () => ( "{}"))
|
|
1372
|
-
}
|
|
1373
|
-
};
|
|
1374
|
-
});
|
|
1375
|
-
}
|
|
1376
|
-
let usage;
|
|
1377
|
-
if (_optionalChain([json, 'optionalAccess', _82 => _82.usage])) {
|
|
1378
|
-
usage = {
|
|
1379
|
-
promptTokens: _nullishCoalesce(json.usage.prompt_tokens, () => ( 0)),
|
|
1380
|
-
completionTokens: _nullishCoalesce(json.usage.completion_tokens, () => ( 0)),
|
|
1381
|
-
totalTokens: _nullishCoalesce(json.usage.total_tokens, () => ( 0))
|
|
1382
|
-
};
|
|
1383
|
-
}
|
|
1384
|
-
return {
|
|
1385
|
-
id: _nullishCoalesce(json.id, () => ( "llmgateway-response")),
|
|
1386
|
-
created: _nullishCoalesce(json.created, () => ( Date.now())),
|
|
1387
|
-
content: text,
|
|
1388
|
-
toolCalls,
|
|
1389
|
-
finishReason,
|
|
1390
|
-
usage,
|
|
1391
|
-
raw: json
|
|
1392
|
-
};
|
|
1393
|
-
}
|
|
1394
|
-
async buildFriendlyError(response) {
|
|
1395
|
-
const status = response.status;
|
|
1396
|
-
let errorDetail = "";
|
|
1397
|
-
try {
|
|
1398
|
-
const body = await response.json();
|
|
1399
|
-
errorDetail = _optionalChain([body, 'optionalAccess', _83 => _83.error, 'optionalAccess', _84 => _84.message]) || _optionalChain([body, 'optionalAccess', _85 => _85.error]) || _optionalChain([body, 'optionalAccess', _86 => _86.message]) || "";
|
|
1400
|
-
if (typeof errorDetail === "object") {
|
|
1401
|
-
errorDetail = JSON.stringify(errorDetail);
|
|
1402
|
-
}
|
|
1403
|
-
} catch (e14) {
|
|
1404
|
-
try {
|
|
1405
|
-
errorDetail = await response.text();
|
|
1406
|
-
} catch (e15) {
|
|
1407
|
-
}
|
|
1408
|
-
}
|
|
1409
|
-
const friendlyMessage = FRIENDLY_ERRORS[status];
|
|
1410
|
-
if (friendlyMessage) {
|
|
1411
|
-
return errorDetail ? `${friendlyMessage}
|
|
1412
|
-
${errorDetail}` : friendlyMessage;
|
|
1413
|
-
}
|
|
1414
|
-
if (status >= 500) {
|
|
1415
|
-
const base = "The LLM Gateway service is temporarily unavailable. Please try again later.";
|
|
1416
|
-
return errorDetail ? `${base}
|
|
1417
|
-
(${status}: ${errorDetail})` : base;
|
|
1418
|
-
}
|
|
1419
|
-
if (status >= 400) {
|
|
1420
|
-
const base = "The request could not be processed.";
|
|
1421
|
-
return errorDetail ? `${base} (${status}: ${errorDetail})` : `${base} (HTTP ${status}) Please try again or adjust your prompt.`;
|
|
1422
|
-
}
|
|
1423
|
-
return errorDetail ? `An unexpected error occurred: ${errorDetail}` : "An unexpected error occurred. Please try again.";
|
|
1424
|
-
}
|
|
1425
|
-
isNonRetryableError(error) {
|
|
1426
|
-
const message = error.message.toLowerCase();
|
|
1427
|
-
if (message.includes("cancelled") || message.includes("aborted")) {
|
|
1428
|
-
return true;
|
|
1429
|
-
}
|
|
1430
|
-
if (message.includes("authentication") || message.includes("api key")) {
|
|
1431
|
-
return true;
|
|
1432
|
-
}
|
|
1433
|
-
if (message.includes("payment") || message.includes("access denied")) {
|
|
1434
|
-
return true;
|
|
1435
|
-
}
|
|
1436
|
-
if (message.includes("not found")) {
|
|
1437
|
-
return true;
|
|
1438
|
-
}
|
|
1439
|
-
return false;
|
|
1440
|
-
}
|
|
1441
|
-
combineSignals(signal1, signal2) {
|
|
1442
|
-
const controller = new AbortController();
|
|
1443
|
-
const abort = () => controller.abort();
|
|
1444
|
-
signal1.addEventListener("abort", abort);
|
|
1445
|
-
signal2.addEventListener("abort", abort);
|
|
1446
|
-
if (signal1.aborted || signal2.aborted) {
|
|
1447
|
-
controller.abort();
|
|
1448
|
-
}
|
|
1449
|
-
return controller.signal;
|
|
1450
|
-
}
|
|
1451
|
-
sleep(ms) {
|
|
1452
|
-
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
1453
|
-
}
|
|
1454
|
-
};
|
|
1455
|
-
|
|
1456
|
-
// src/providers/LLMGatewayProvider.ts
|
|
1457
|
-
var LLMGatewayProvider = class {
|
|
1458
|
-
constructor(config, networkSettings) {
|
|
1459
|
-
this.client = new LLMGatewayClient(config, networkSettings);
|
|
1460
|
-
this.model = config.model;
|
|
1461
|
-
}
|
|
1462
|
-
getName() {
|
|
1463
|
-
return "llmgateway";
|
|
1464
|
-
}
|
|
1465
|
-
setModel(model) {
|
|
1466
|
-
this.model = model;
|
|
1467
|
-
this.client.setDefaultModel(model);
|
|
1468
|
-
}
|
|
1469
|
-
async listModels() {
|
|
1470
|
-
return [
|
|
1471
|
-
"gpt-4o",
|
|
1472
|
-
"gpt-4o-mini",
|
|
1473
|
-
"gpt-4-turbo",
|
|
1474
|
-
"claude-3-5-sonnet-20241022",
|
|
1475
|
-
"claude-3-5-haiku-20241022",
|
|
1476
|
-
"gemini-1.5-pro",
|
|
1477
|
-
"gemini-1.5-flash"
|
|
1478
|
-
];
|
|
1479
|
-
}
|
|
1480
|
-
async isAvailable() {
|
|
1481
|
-
return true;
|
|
1482
|
-
}
|
|
1483
|
-
async complete(request) {
|
|
1484
|
-
return this.client.complete(request);
|
|
1485
|
-
}
|
|
1486
|
-
};
|
|
1487
|
-
|
|
1488
|
-
// src/providers/azure/tokenManager.ts
|
|
1489
|
-
var EXPIRY_BUFFER_MS = 5 * 60 * 1e3;
|
|
1490
|
-
var IMDS_ENDPOINT = "http://169.254.169.254/metadata/identity/oauth2/token";
|
|
1491
|
-
var COGNITIVE_SCOPE = "https://cognitiveservices.azure.com/.default";
|
|
1492
|
-
var AzureTokenManager = class {
|
|
1493
|
-
constructor() {
|
|
1494
|
-
this.cache = null;
|
|
1495
|
-
}
|
|
1496
|
-
async getToken(request) {
|
|
1497
|
-
if (request.authMethod === "api-key") {
|
|
1498
|
-
if (!request.apiKey) {
|
|
1499
|
-
throw new Error("API key is required for api-key authentication.");
|
|
1500
|
-
}
|
|
1501
|
-
return request.apiKey;
|
|
1502
|
-
}
|
|
1503
|
-
if (this.cache && !this.isTokenExpired()) {
|
|
1504
|
-
return this.cache.token;
|
|
1505
|
-
}
|
|
1506
|
-
if (request.authMethod === "entra-id") {
|
|
1507
|
-
return this.acquireEntraIdToken(request);
|
|
1508
|
-
}
|
|
1509
|
-
if (request.authMethod === "managed-identity") {
|
|
1510
|
-
return this.acquireManagedIdentityToken();
|
|
1511
|
-
}
|
|
1512
|
-
throw new Error(`Unsupported auth method: ${request.authMethod}`);
|
|
1513
|
-
}
|
|
1514
|
-
async getAuthHeaders(request) {
|
|
1515
|
-
if (request.authMethod === "api-key") {
|
|
1516
|
-
const key = await this.getToken(request);
|
|
1517
|
-
return { "api-key": key };
|
|
1518
|
-
}
|
|
1519
|
-
const token = await this.getToken(request);
|
|
1520
|
-
return { Authorization: `Bearer ${token}` };
|
|
1521
|
-
}
|
|
1522
|
-
async acquireEntraIdToken(request) {
|
|
1523
|
-
if (!request.tenantId) {
|
|
1524
|
-
throw new Error("tenantId is required for Entra ID authentication.");
|
|
1525
|
-
}
|
|
1526
|
-
if (!request.clientId) {
|
|
1527
|
-
throw new Error("clientId is required for Entra ID authentication.");
|
|
1528
|
-
}
|
|
1529
|
-
if (!request.clientSecret) {
|
|
1530
|
-
throw new Error("clientSecret is required for Entra ID authentication.");
|
|
1531
|
-
}
|
|
1532
|
-
const url = `https://login.microsoftonline.com/${request.tenantId}/oauth2/v2.0/token`;
|
|
1533
|
-
const body = new URLSearchParams({
|
|
1534
|
-
grant_type: "client_credentials",
|
|
1535
|
-
client_id: request.clientId,
|
|
1536
|
-
client_secret: request.clientSecret,
|
|
1537
|
-
scope: COGNITIVE_SCOPE
|
|
1538
|
-
});
|
|
1539
|
-
const response = await fetch(url, {
|
|
1540
|
-
method: "POST",
|
|
1541
|
-
headers: { "Content-Type": "application/x-www-form-urlencoded" },
|
|
1542
|
-
body: body.toString()
|
|
1543
|
-
});
|
|
1544
|
-
if (!response.ok) {
|
|
1545
|
-
const error = await response.json().catch(() => ({}));
|
|
1546
|
-
const description = error.error_description || error.error || `HTTP ${response.status}`;
|
|
1547
|
-
throw new Error(`Entra ID authentication failed: ${description}`);
|
|
1548
|
-
}
|
|
1549
|
-
const data = await response.json();
|
|
1550
|
-
this.cacheToken(data.access_token, data.expires_in);
|
|
1551
|
-
return data.access_token;
|
|
1552
|
-
}
|
|
1553
|
-
async acquireManagedIdentityToken() {
|
|
1554
|
-
const url = `${IMDS_ENDPOINT}?api-version=2018-02-01&resource=https://cognitiveservices.azure.com`;
|
|
1555
|
-
let response;
|
|
1556
|
-
try {
|
|
1557
|
-
response = await fetch(url, {
|
|
1558
|
-
headers: { Metadata: "true" }
|
|
1559
|
-
});
|
|
1560
|
-
} catch (e16) {
|
|
1561
|
-
throw new Error(
|
|
1562
|
-
"Managed Identity token acquisition failed. This auth method only works inside Azure VMs, App Service, or containers with managed identity enabled."
|
|
1563
|
-
);
|
|
1564
|
-
}
|
|
1565
|
-
if (!response.ok) {
|
|
1566
|
-
const error = await response.json().catch(() => ({}));
|
|
1567
|
-
throw new Error(
|
|
1568
|
-
`Managed Identity token error (${response.status}): ${error.error_description || error.error || "Unknown"}`
|
|
1569
|
-
);
|
|
1570
|
-
}
|
|
1571
|
-
const data = await response.json();
|
|
1572
|
-
this.cacheToken(data.access_token, data.expires_in);
|
|
1573
|
-
return data.access_token;
|
|
1574
|
-
}
|
|
1575
|
-
cacheToken(token, expiresInSeconds) {
|
|
1576
|
-
this.cache = {
|
|
1577
|
-
token,
|
|
1578
|
-
expiresAt: Date.now() + expiresInSeconds * 1e3
|
|
1579
|
-
};
|
|
1580
|
-
}
|
|
1581
|
-
isTokenExpired() {
|
|
1582
|
-
if (!this.cache) return true;
|
|
1583
|
-
return Date.now() >= this.cache.expiresAt - EXPIRY_BUFFER_MS;
|
|
1584
|
-
}
|
|
1585
|
-
};
|
|
1586
|
-
|
|
1587
|
-
// src/providers/AzureClient.ts
|
|
1588
|
-
function sanitizeMessages3(messages) {
|
|
1589
|
-
return messages.map((msg) => {
|
|
1590
|
-
const sanitized = {
|
|
1591
|
-
role: msg.role,
|
|
1592
|
-
content: msg.content
|
|
1593
|
-
};
|
|
1594
|
-
if (msg.role === "tool" && msg.tool_call_id) {
|
|
1595
|
-
sanitized.tool_call_id = msg.tool_call_id;
|
|
1596
|
-
}
|
|
1597
|
-
if (msg.role === "assistant" && _optionalChain([msg, 'access', _87 => _87.tool_calls, 'optionalAccess', _88 => _88.length])) {
|
|
1598
|
-
sanitized.tool_calls = msg.tool_calls;
|
|
1599
|
-
}
|
|
1600
|
-
if (msg.name) {
|
|
1601
|
-
sanitized.name = msg.name;
|
|
1602
|
-
}
|
|
1603
|
-
return sanitized;
|
|
1604
|
-
});
|
|
1605
|
-
}
|
|
1606
|
-
var DEFAULT_API_VERSION = "2024-10-21";
|
|
1607
|
-
var DEFAULT_MAX_RETRIES5 = 3;
|
|
1608
|
-
var MAX_ALLOWED_RETRIES5 = 5;
|
|
1609
|
-
var DEFAULT_RETRY_DELAY5 = 1e3;
|
|
1610
|
-
var DEFAULT_TIMEOUT5 = 3e4;
|
|
1611
|
-
var FRIENDLY_ERRORS2 = {
|
|
1612
|
-
400: "The request was malformed. This often happens when the context is too long. Try /undo to remove recent turns or /new to start fresh.",
|
|
1613
|
-
401: "Authentication failed. Please verify your Azure API key or credentials in ~/.autohand/config.json.",
|
|
1614
|
-
402: "Payment required. Please check your Azure subscription and billing settings.",
|
|
1615
|
-
403: "Access denied. Your credentials may not have permission for this Azure deployment.",
|
|
1616
|
-
404: "The Azure deployment was not found. Verify your resourceName and deploymentName in ~/.autohand/config.json.",
|
|
1617
|
-
429: "Rate limit exceeded. Please wait a moment and try again, or adjust your Azure deployment capacity.",
|
|
1618
|
-
500: "Azure OpenAI encountered an internal error. Please try again later.",
|
|
1619
|
-
502: "Azure OpenAI is temporarily unavailable. Please try again in a few moments.",
|
|
1620
|
-
503: "Azure OpenAI is currently overloaded. Please try again later.",
|
|
1621
|
-
504: "The request timed out. Azure OpenAI may be experiencing high load."
|
|
1622
|
-
};
|
|
1623
|
-
var AzureClient = class {
|
|
1624
|
-
constructor(options, networkSettings) {
|
|
1625
|
-
this.options = options;
|
|
1626
|
-
this.tokenManager = new AzureTokenManager();
|
|
1627
|
-
this.defaultModel = options.model;
|
|
1628
|
-
const configuredRetries = _nullishCoalesce(_optionalChain([networkSettings, 'optionalAccess', _89 => _89.maxRetries]), () => ( DEFAULT_MAX_RETRIES5));
|
|
1629
|
-
this.maxRetries = Math.min(
|
|
1630
|
-
Math.max(0, configuredRetries),
|
|
1631
|
-
MAX_ALLOWED_RETRIES5
|
|
1632
|
-
);
|
|
1633
|
-
this.retryDelay = _nullishCoalesce(_optionalChain([networkSettings, 'optionalAccess', _90 => _90.retryDelay]), () => ( DEFAULT_RETRY_DELAY5));
|
|
1634
|
-
this.timeout = _nullishCoalesce(_optionalChain([networkSettings, 'optionalAccess', _91 => _91.timeout]), () => ( DEFAULT_TIMEOUT5));
|
|
1635
|
-
}
|
|
1636
|
-
setDefaultModel(model) {
|
|
1637
|
-
this.defaultModel = model;
|
|
1638
|
-
}
|
|
1639
|
-
/**
|
|
1640
|
-
* Build the full Azure OpenAI endpoint URL.
|
|
1641
|
-
*
|
|
1642
|
-
* If baseUrl is provided:
|
|
1643
|
-
* {baseUrl}/chat/completions?api-version={apiVersion}
|
|
1644
|
-
*
|
|
1645
|
-
* If resourceName is a full URL (starts with https://):
|
|
1646
|
-
* {origin}/openai/deployments/{deploymentName}/chat/completions?api-version={apiVersion}
|
|
1647
|
-
* Supports all Azure endpoint domains:
|
|
1648
|
-
* - *.openai.azure.com (Azure OpenAI)
|
|
1649
|
-
* - *.services.ai.azure.com (Microsoft Foundry)
|
|
1650
|
-
* - *.cognitiveservices.azure.com (Azure AI Services)
|
|
1651
|
-
*
|
|
1652
|
-
* Otherwise, from resourceName + deploymentName:
|
|
1653
|
-
* https://{resourceName}.openai.azure.com/openai/deployments/{deploymentName}/chat/completions?api-version={apiVersion}
|
|
1654
|
-
*/
|
|
1655
|
-
buildEndpointUrl() {
|
|
1656
|
-
const apiVersion = _nullishCoalesce(this.options.apiVersion, () => ( DEFAULT_API_VERSION));
|
|
1657
|
-
if (this.options.baseUrl) {
|
|
1658
|
-
return `${this.options.baseUrl}/chat/completions?api-version=${apiVersion}`;
|
|
1659
|
-
}
|
|
1660
|
-
const { resourceName, deploymentName } = this.options;
|
|
1661
|
-
if (!resourceName || !deploymentName) {
|
|
1662
|
-
throw new Error(
|
|
1663
|
-
"Azure OpenAI requires either baseUrl or both resourceName and deploymentName in ~/.autohand/config.json."
|
|
1664
|
-
);
|
|
1665
|
-
}
|
|
1666
|
-
if (resourceName.startsWith("http://") || resourceName.startsWith("https://")) {
|
|
1667
|
-
try {
|
|
1668
|
-
const parsed = new URL(resourceName);
|
|
1669
|
-
return `${parsed.origin}/openai/deployments/${deploymentName}/chat/completions?api-version=${apiVersion}`;
|
|
1670
|
-
} catch (e17) {
|
|
1671
|
-
}
|
|
1672
|
-
}
|
|
1673
|
-
return `https://${resourceName}.openai.azure.com/openai/deployments/${deploymentName}/chat/completions?api-version=${apiVersion}`;
|
|
1674
|
-
}
|
|
1675
|
-
async complete(request) {
|
|
1676
|
-
const payload = {
|
|
1677
|
-
messages: sanitizeMessages3(request.messages),
|
|
1678
|
-
temperature: _nullishCoalesce(request.temperature, () => ( 0.2)),
|
|
1679
|
-
max_tokens: _nullishCoalesce(request.maxTokens, () => ( 16e3)),
|
|
1680
|
-
stream: _nullishCoalesce(request.stream, () => ( false))
|
|
1681
|
-
};
|
|
1682
|
-
if (request.tools && request.tools.length > 0) {
|
|
1683
|
-
payload.tools = request.tools.map((tool) => ({
|
|
1684
|
-
type: "function",
|
|
1685
|
-
function: {
|
|
1686
|
-
name: tool.name,
|
|
1687
|
-
description: tool.description,
|
|
1688
|
-
parameters: _nullishCoalesce(tool.parameters, () => ( { type: "object", properties: {} }))
|
|
1689
|
-
}
|
|
1690
|
-
}));
|
|
1691
|
-
if (request.toolChoice) {
|
|
1692
|
-
payload.tool_choice = request.toolChoice;
|
|
1693
|
-
}
|
|
1694
|
-
}
|
|
1695
|
-
const authHeaders = await this.tokenManager.getAuthHeaders({
|
|
1696
|
-
authMethod: this.options.authMethod,
|
|
1697
|
-
apiKey: this.options.apiKey,
|
|
1698
|
-
tenantId: this.options.tenantId,
|
|
1699
|
-
clientId: this.options.clientId,
|
|
1700
|
-
clientSecret: this.options.clientSecret
|
|
1701
|
-
});
|
|
1702
|
-
const headers = {
|
|
1703
|
-
"Content-Type": "application/json",
|
|
1704
|
-
...authHeaders
|
|
1705
|
-
};
|
|
1706
|
-
const payloadJson = JSON.stringify(payload);
|
|
1707
|
-
const payloadSizeBytes = payloadJson.length;
|
|
1708
|
-
const maxPayloadSize = 5 * 1024 * 1024;
|
|
1709
|
-
if (payloadSizeBytes > maxPayloadSize) {
|
|
1710
|
-
const sizeMB = (payloadSizeBytes / (1024 * 1024)).toFixed(2);
|
|
1711
|
-
throw new Error(
|
|
1712
|
-
`Request payload too large (${sizeMB}MB). This usually happens when the conversation history grows too long. Try using /undo to remove recent turns or /new to start fresh.`
|
|
1713
|
-
);
|
|
1714
|
-
}
|
|
1715
|
-
let lastError = null;
|
|
1716
|
-
for (let attempt = 0; attempt <= this.maxRetries; attempt++) {
|
|
1717
|
-
try {
|
|
1718
|
-
const response = await this.makeRequest(
|
|
1719
|
-
payload,
|
|
1720
|
-
headers,
|
|
1721
|
-
request.signal,
|
|
1722
|
-
payloadJson
|
|
1723
|
-
);
|
|
1724
|
-
return response;
|
|
1725
|
-
} catch (error) {
|
|
1726
|
-
lastError = error;
|
|
1727
|
-
if (this.isNonRetryableError(error)) {
|
|
1728
|
-
throw error;
|
|
1729
|
-
}
|
|
1730
|
-
if (attempt < this.maxRetries) {
|
|
1731
|
-
const delay = this.retryDelay * Math.pow(2, attempt);
|
|
1732
|
-
await this.sleep(delay);
|
|
1733
|
-
}
|
|
1734
|
-
}
|
|
1735
|
-
}
|
|
1736
|
-
throw _nullishCoalesce(lastError, () => ( new Error(
|
|
1737
|
-
"Failed to communicate with Azure OpenAI. Please try again."
|
|
1738
|
-
)));
|
|
1739
|
-
}
|
|
1740
|
-
async makeRequest(payload, headers, signal, preSerializedBody) {
|
|
1741
|
-
let response;
|
|
1742
|
-
const url = this.buildEndpointUrl();
|
|
1743
|
-
try {
|
|
1744
|
-
const timeoutController = new AbortController();
|
|
1745
|
-
const timeoutId = setTimeout(
|
|
1746
|
-
() => timeoutController.abort(),
|
|
1747
|
-
this.timeout
|
|
1748
|
-
);
|
|
1749
|
-
const combinedSignal = signal ? this.combineSignals(signal, timeoutController.signal) : timeoutController.signal;
|
|
1750
|
-
try {
|
|
1751
|
-
response = await fetch(url, {
|
|
1752
|
-
method: "POST",
|
|
1753
|
-
headers,
|
|
1754
|
-
body: _nullishCoalesce(preSerializedBody, () => ( JSON.stringify(payload))),
|
|
1755
|
-
signal: combinedSignal
|
|
1756
|
-
});
|
|
1757
|
-
} finally {
|
|
1758
|
-
clearTimeout(timeoutId);
|
|
1759
|
-
}
|
|
1760
|
-
} catch (error) {
|
|
1761
|
-
const err = error;
|
|
1762
|
-
if (err.name === "AbortError" && _optionalChain([signal, 'optionalAccess', _92 => _92.aborted])) {
|
|
1763
|
-
throw new Error("Request cancelled.");
|
|
1764
|
-
}
|
|
1765
|
-
if (err.name === "AbortError") {
|
|
1766
|
-
throw new Error(
|
|
1767
|
-
"Request timed out. Azure OpenAI may be experiencing high load."
|
|
1768
|
-
);
|
|
1769
|
-
}
|
|
1770
|
-
throw new Error(
|
|
1771
|
-
"Unable to connect to Azure OpenAI. Please check your internet connection and Azure configuration."
|
|
1772
|
-
);
|
|
1773
|
-
}
|
|
1774
|
-
if (!response.ok) {
|
|
1775
|
-
throw new Error(await this.buildFriendlyError(response));
|
|
1776
|
-
}
|
|
1777
|
-
const json = await response.json();
|
|
1778
|
-
const message = _optionalChain([json, 'optionalAccess', _93 => _93.choices, 'optionalAccess', _94 => _94[0], 'optionalAccess', _95 => _95.message]);
|
|
1779
|
-
const text = _nullishCoalesce(_optionalChain([message, 'optionalAccess', _96 => _96.content]), () => ( ""));
|
|
1780
|
-
const finishReason = _optionalChain([json, 'optionalAccess', _97 => _97.choices, 'optionalAccess', _98 => _98[0], 'optionalAccess', _99 => _99.finish_reason]);
|
|
1781
|
-
let toolCalls;
|
|
1782
|
-
if (_optionalChain([message, 'optionalAccess', _100 => _100.tool_calls]) && Array.isArray(message.tool_calls)) {
|
|
1783
|
-
toolCalls = message.tool_calls.map((tc) => {
|
|
1784
|
-
const rawArgs = _optionalChain([tc, 'access', _101 => _101.function, 'optionalAccess', _102 => _102.arguments]);
|
|
1785
|
-
return {
|
|
1786
|
-
id: tc.id,
|
|
1787
|
-
type: "function",
|
|
1788
|
-
function: {
|
|
1789
|
-
name: _nullishCoalesce(_optionalChain([tc, 'access', _103 => _103.function, 'optionalAccess', _104 => _104.name]), () => ( "")),
|
|
1790
|
-
arguments: _nullishCoalesce(rawArgs, () => ( "{}"))
|
|
1791
|
-
}
|
|
1792
|
-
};
|
|
1793
|
-
});
|
|
1794
|
-
}
|
|
1795
|
-
let usage;
|
|
1796
|
-
if (_optionalChain([json, 'optionalAccess', _105 => _105.usage])) {
|
|
1797
|
-
usage = {
|
|
1798
|
-
promptTokens: _nullishCoalesce(json.usage.prompt_tokens, () => ( 0)),
|
|
1799
|
-
completionTokens: _nullishCoalesce(json.usage.completion_tokens, () => ( 0)),
|
|
1800
|
-
totalTokens: _nullishCoalesce(json.usage.total_tokens, () => ( 0))
|
|
1801
|
-
};
|
|
1802
|
-
}
|
|
1803
|
-
return {
|
|
1804
|
-
id: _nullishCoalesce(json.id, () => ( "autohand-azure")),
|
|
1805
|
-
created: _nullishCoalesce(json.created, () => ( Date.now())),
|
|
1806
|
-
content: text,
|
|
1807
|
-
toolCalls,
|
|
1808
|
-
finishReason,
|
|
1809
|
-
usage,
|
|
1810
|
-
raw: json
|
|
1811
|
-
};
|
|
1812
|
-
}
|
|
1813
|
-
async buildFriendlyError(response) {
|
|
1814
|
-
const status = response.status;
|
|
1815
|
-
let errorDetail = "";
|
|
1816
|
-
try {
|
|
1817
|
-
const body = await response.json();
|
|
1818
|
-
errorDetail = _optionalChain([body, 'optionalAccess', _106 => _106.error, 'optionalAccess', _107 => _107.message]) || _optionalChain([body, 'optionalAccess', _108 => _108.error]) || _optionalChain([body, 'optionalAccess', _109 => _109.message]) || "";
|
|
1819
|
-
if (typeof errorDetail === "object") {
|
|
1820
|
-
errorDetail = JSON.stringify(errorDetail);
|
|
1821
|
-
}
|
|
1822
|
-
} catch (e18) {
|
|
1823
|
-
try {
|
|
1824
|
-
errorDetail = await response.text();
|
|
1825
|
-
} catch (e19) {
|
|
1826
|
-
}
|
|
1827
|
-
}
|
|
1828
|
-
const friendlyMessage = FRIENDLY_ERRORS2[status];
|
|
1829
|
-
if (friendlyMessage) {
|
|
1830
|
-
return errorDetail ? `${friendlyMessage}
|
|
1831
|
-
${errorDetail}` : friendlyMessage;
|
|
1832
|
-
}
|
|
1833
|
-
if (status >= 500) {
|
|
1834
|
-
const base = "Azure OpenAI is temporarily unavailable. Please try again later.";
|
|
1835
|
-
return errorDetail ? `${base}
|
|
1836
|
-
(${status}: ${errorDetail})` : base;
|
|
1837
|
-
}
|
|
1838
|
-
if (status >= 400) {
|
|
1839
|
-
const base = "The request could not be processed by Azure OpenAI.";
|
|
1840
|
-
return errorDetail ? `${base} (${status}: ${errorDetail})` : `${base} (HTTP ${status}) Please try again or adjust your prompt.`;
|
|
1841
|
-
}
|
|
1842
|
-
return errorDetail ? `An unexpected Azure OpenAI error occurred: ${errorDetail}` : "An unexpected Azure OpenAI error occurred. Please try again.";
|
|
1843
|
-
}
|
|
1844
|
-
isNonRetryableError(error) {
|
|
1845
|
-
const message = error.message.toLowerCase();
|
|
1846
|
-
if (message.includes("cancelled") || message.includes("aborted")) {
|
|
1847
|
-
return true;
|
|
1848
|
-
}
|
|
1849
|
-
if (message.includes("authentication") || message.includes("api key")) {
|
|
1850
|
-
return true;
|
|
1851
|
-
}
|
|
1852
|
-
if (message.includes("payment") || message.includes("access denied")) {
|
|
1853
|
-
return true;
|
|
1854
|
-
}
|
|
1855
|
-
if (message.includes("not found")) {
|
|
1856
|
-
return true;
|
|
1857
|
-
}
|
|
1858
|
-
return false;
|
|
1859
|
-
}
|
|
1860
|
-
combineSignals(signal1, signal2) {
|
|
1861
|
-
const controller = new AbortController();
|
|
1862
|
-
const abort = () => controller.abort();
|
|
1863
|
-
signal1.addEventListener("abort", abort);
|
|
1864
|
-
signal2.addEventListener("abort", abort);
|
|
1865
|
-
if (signal1.aborted || signal2.aborted) {
|
|
1866
|
-
controller.abort();
|
|
1867
|
-
}
|
|
1868
|
-
return controller.signal;
|
|
1869
|
-
}
|
|
1870
|
-
sleep(ms) {
|
|
1871
|
-
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
1872
|
-
}
|
|
1873
|
-
};
|
|
1874
|
-
|
|
1875
|
-
// src/providers/AzureProvider.ts
|
|
1876
|
-
var AzureProvider = class {
|
|
1877
|
-
constructor(config, networkSettings) {
|
|
1878
|
-
this.client = new AzureClient(
|
|
1879
|
-
{
|
|
1880
|
-
model: config.model,
|
|
1881
|
-
resourceName: config.resourceName,
|
|
1882
|
-
deploymentName: config.deploymentName,
|
|
1883
|
-
baseUrl: config.baseUrl,
|
|
1884
|
-
apiVersion: config.apiVersion,
|
|
1885
|
-
apiKey: config.apiKey,
|
|
1886
|
-
authMethod: _nullishCoalesce(config.authMethod, () => ( "api-key")),
|
|
1887
|
-
tenantId: config.tenantId,
|
|
1888
|
-
clientId: config.clientId,
|
|
1889
|
-
clientSecret: config.clientSecret
|
|
1890
|
-
},
|
|
1891
|
-
networkSettings
|
|
1892
|
-
);
|
|
1893
|
-
this.model = config.model;
|
|
1894
|
-
}
|
|
1895
|
-
getName() {
|
|
1896
|
-
return "azure";
|
|
1897
|
-
}
|
|
1898
|
-
setModel(model) {
|
|
1899
|
-
this.model = model;
|
|
1900
|
-
this.client.setDefaultModel(model);
|
|
1901
|
-
}
|
|
1902
|
-
async listModels() {
|
|
1903
|
-
return ["gpt-4o", "gpt-4o-mini", "gpt-4-turbo", "gpt-4", "gpt-3.5-turbo"];
|
|
1904
|
-
}
|
|
1905
|
-
async isAvailable() {
|
|
1906
|
-
return true;
|
|
1907
|
-
}
|
|
1908
|
-
async complete(request) {
|
|
1909
|
-
return this.client.complete(request);
|
|
1910
|
-
}
|
|
1911
|
-
};
|
|
1912
|
-
|
|
1913
|
-
// src/providers/ProviderFactory.ts
|
|
1914
|
-
var ProviderNotConfiguredError = class extends Error {
|
|
1915
|
-
constructor(providerName) {
|
|
1916
|
-
super(`PROVIDER_NOT_CONFIGURED:${providerName}`);
|
|
1917
|
-
this.providerName = providerName;
|
|
1918
|
-
this.name = "ProviderNotConfiguredError";
|
|
1919
|
-
}
|
|
1920
|
-
};
|
|
1921
|
-
var UnconfiguredProvider = class {
|
|
1922
|
-
constructor(providerName) {
|
|
1923
|
-
this.providerName = providerName;
|
|
1924
|
-
}
|
|
1925
|
-
getName() {
|
|
1926
|
-
return "unconfigured";
|
|
1927
|
-
}
|
|
1928
|
-
async complete(_request) {
|
|
1929
|
-
throw new ProviderNotConfiguredError(this.providerName);
|
|
1930
|
-
}
|
|
1931
|
-
async listModels() {
|
|
1932
|
-
return [];
|
|
1933
|
-
}
|
|
1934
|
-
async isAvailable() {
|
|
1935
|
-
return false;
|
|
1936
|
-
}
|
|
1937
|
-
setModel(_model) {
|
|
1938
|
-
}
|
|
1939
|
-
};
|
|
1940
|
-
var ProviderFactory = class {
|
|
1941
|
-
/**
|
|
1942
|
-
* Create an LLM provider based on configuration.
|
|
1943
|
-
* Returns an UnconfiguredProvider if the selected provider is not configured,
|
|
1944
|
-
* allowing the agent to handle it gracefully instead of crashing.
|
|
1945
|
-
*/
|
|
1946
|
-
static create(config) {
|
|
1947
|
-
const providerName = config.provider || "openrouter";
|
|
1948
|
-
switch (providerName) {
|
|
1949
|
-
case "ollama":
|
|
1950
|
-
if (!config.ollama) {
|
|
1951
|
-
return new UnconfiguredProvider("ollama");
|
|
1952
|
-
}
|
|
1953
|
-
return new OllamaProvider(config.ollama, config.network);
|
|
1954
|
-
case "openai":
|
|
1955
|
-
if (!config.openai) {
|
|
1956
|
-
return new UnconfiguredProvider("openai");
|
|
1957
|
-
}
|
|
1958
|
-
return new OpenAIProvider(config.openai);
|
|
1959
|
-
case "llamacpp":
|
|
1960
|
-
if (!config.llamacpp) {
|
|
1961
|
-
return new UnconfiguredProvider("llamacpp");
|
|
1962
|
-
}
|
|
1963
|
-
return new LlamaCppProvider(config.llamacpp);
|
|
1964
|
-
case "mlx":
|
|
1965
|
-
if (!config.mlx) {
|
|
1966
|
-
return new UnconfiguredProvider("mlx");
|
|
1967
|
-
}
|
|
1968
|
-
return new MLXProvider(config.mlx, config.network);
|
|
1969
|
-
case "llmgateway":
|
|
1970
|
-
if (!config.llmgateway) {
|
|
1971
|
-
return new UnconfiguredProvider("llmgateway");
|
|
1972
|
-
}
|
|
1973
|
-
return new LLMGatewayProvider(config.llmgateway, config.network);
|
|
1974
|
-
case "azure":
|
|
1975
|
-
if (!config.azure) {
|
|
1976
|
-
return new UnconfiguredProvider("azure");
|
|
1977
|
-
}
|
|
1978
|
-
return new AzureProvider(config.azure, config.network);
|
|
1979
|
-
case "openrouter":
|
|
1980
|
-
default:
|
|
1981
|
-
if (!config.openrouter) {
|
|
1982
|
-
return new UnconfiguredProvider("openrouter");
|
|
1983
|
-
}
|
|
1984
|
-
return new OpenRouterProvider(config.openrouter);
|
|
1985
|
-
}
|
|
1986
|
-
}
|
|
1987
|
-
/**
|
|
1988
|
-
* Get all available provider names.
|
|
1989
|
-
* MLX is only included on Apple Silicon (macOS + arm64).
|
|
1990
|
-
*/
|
|
1991
|
-
static getProviderNames() {
|
|
1992
|
-
const providers = ["openrouter", "ollama", "openai", "llamacpp", "llmgateway", "azure"];
|
|
1993
|
-
if (isMLXSupported()) {
|
|
1994
|
-
providers.push("mlx");
|
|
1995
|
-
}
|
|
1996
|
-
return providers;
|
|
1997
|
-
}
|
|
1998
|
-
/**
|
|
1999
|
-
* Check if a provider name is valid.
|
|
2000
|
-
* Note: This checks if the name is a valid provider type, not if it's available on this platform.
|
|
2001
|
-
* MLX is always a valid provider name, but may not be available on non-Apple Silicon systems.
|
|
2002
|
-
*/
|
|
2003
|
-
static isValidProvider(name) {
|
|
2004
|
-
const allProviders = ["openrouter", "ollama", "openai", "llamacpp", "mlx", "llmgateway", "azure"];
|
|
2005
|
-
return allProviders.includes(name);
|
|
2006
|
-
}
|
|
2007
|
-
};
|
|
2008
|
-
|
|
2009
|
-
|
|
2010
|
-
|
|
2011
|
-
|
|
2012
|
-
|
|
2013
|
-
|
|
2014
|
-
exports.ApiError = ApiError; exports.classifyApiError = classifyApiError; exports.ProviderNotConfiguredError = ProviderNotConfiguredError; exports.ProviderFactory = ProviderFactory;
|
|
2015
|
-
/**
|
|
2016
|
-
* @license
|
|
2017
|
-
* Copyright 2025 Autohand AI LLC
|
|
2018
|
-
* SPDX-License-Identifier: Apache-2.0
|
|
2019
|
-
*
|
|
2020
|
-
* Centralized API error classification.
|
|
2021
|
-
*
|
|
2022
|
-
* Every provider (OpenRouter, Azure, MLX, Ollama, LLMGateway) and every
|
|
2023
|
-
* consumer (agent.ts, RPC adapter, ACP adapter) delegates to this module
|
|
2024
|
-
* so error handling logic lives in one place.
|
|
2025
|
-
*/
|
|
2026
|
-
/**
|
|
2027
|
-
* @license
|
|
2028
|
-
* Copyright 2025 Autohand AI LLC
|
|
2029
|
-
* SPDX-License-Identifier: Apache-2.0
|
|
2030
|
-
*/
|