onbuzz 3.4.0 → 3.6.2
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/package.json +1 -1
- package/scripts/bump-version.js +116 -0
- package/src/__test-utils__/fixtures/malformedJson.js +31 -0
- package/src/__test-utils__/globalSetup.js +9 -0
- package/src/__test-utils__/globalTeardown.js +12 -0
- package/src/__test-utils__/mockFactories.js +101 -0
- package/src/analyzers/__tests__/CSSAnalyzer.test.js +41 -0
- package/src/analyzers/__tests__/ConfigValidator.test.js +362 -0
- package/src/analyzers/__tests__/ESLintAnalyzer.test.js +271 -0
- package/src/analyzers/__tests__/JavaScriptAnalyzer.test.js +40 -0
- package/src/analyzers/__tests__/PrettierFormatter.test.js +197 -0
- package/src/analyzers/__tests__/PythonAnalyzer.test.js +208 -0
- package/src/analyzers/__tests__/SecurityAnalyzer.test.js +303 -0
- package/src/analyzers/__tests__/SparrowAnalyzer.test.js +270 -0
- package/src/analyzers/__tests__/TypeScriptAnalyzer.test.js +187 -0
- package/src/core/__tests__/agentPool.test.js +601 -0
- package/src/core/__tests__/agentScheduler.test.js +576 -0
- package/src/core/__tests__/contextManager.test.js +252 -0
- package/src/core/__tests__/flowExecutor.test.js +262 -0
- package/src/core/__tests__/messageProcessor.test.js +627 -0
- package/src/core/__tests__/orchestrator.test.js +257 -0
- package/src/core/__tests__/stateManager.test.js +375 -0
- package/src/core/agentPool.js +26 -4
- package/src/core/agentScheduler.js +79 -21
- package/src/core/messageProcessor.js +110 -2
- package/src/index.js +27 -11
- package/src/interfaces/__tests__/imageServing.test.js +228 -0
- package/src/interfaces/terminal/__tests__/smoke/imports.test.js +3 -5
- package/src/interfaces/webServer.js +97 -13
- package/src/services/__tests__/agentActivityService.test.js +319 -0
- package/src/services/__tests__/apiKeyManager.test.js +206 -0
- package/src/services/__tests__/benchmarkService.test.js +184 -0
- package/src/services/__tests__/budgetService.test.js +211 -0
- package/src/services/__tests__/contextInjectionService.test.js +205 -0
- package/src/services/__tests__/conversationCompactionService.test.js +280 -0
- package/src/services/__tests__/credentialVault.test.js +469 -0
- package/src/services/__tests__/errorHandler.test.js +314 -0
- package/src/services/__tests__/fileAttachmentService.test.js +278 -0
- package/src/services/__tests__/flowContextService.test.js +199 -0
- package/src/services/__tests__/memoryService.test.js +450 -0
- package/src/services/__tests__/modelRouterService.test.js +388 -0
- package/src/services/__tests__/modelsService.test.js +261 -0
- package/src/services/__tests__/portRegistry.test.js +123 -0
- package/src/services/__tests__/projectDetector.test.js +34 -0
- package/src/services/__tests__/promptService.test.js +242 -0
- package/src/services/__tests__/qualityInspector.test.js +97 -0
- package/src/services/__tests__/scheduleService.test.js +308 -0
- package/src/services/__tests__/serviceRegistry.test.js +74 -0
- package/src/services/__tests__/skillsService.test.js +402 -0
- package/src/services/__tests__/tokenCountingService.test.js +48 -0
- package/src/services/conversationCompactionService.js +2 -2
- package/src/services/visualEditorServer.js +26 -7
- package/src/tools/__tests__/agentCommunicationTool.test.js +500 -0
- package/src/tools/__tests__/agentDelayTool.test.js +342 -0
- package/src/tools/__tests__/asyncToolManager.test.js +344 -0
- package/src/tools/__tests__/baseTool.test.js +420 -0
- package/src/tools/__tests__/codeMapTool.test.js +348 -0
- package/src/tools/__tests__/fileContentReplaceTool.test.js +309 -0
- package/src/tools/__tests__/fileTreeTool.test.js +274 -0
- package/src/tools/__tests__/filesystemTool.test.js +717 -0
- package/src/tools/__tests__/helpTool.test.js +204 -0
- package/src/tools/__tests__/jobDoneTool.test.js +296 -0
- package/src/tools/__tests__/memoryTool.test.js +297 -0
- package/src/tools/__tests__/seekTool.test.js +282 -0
- package/src/tools/__tests__/skillsTool.test.js +226 -0
- package/src/tools/__tests__/staticAnalysisTool.test.js +509 -0
- package/src/tools/__tests__/taskManagerTool.test.js +725 -0
- package/src/tools/__tests__/terminalTool.test.js +384 -0
- package/src/tools/__tests__/userPromptTool.test.js +297 -0
- package/src/tools/__tests__/webTool.e2e.test.js +25 -11
- package/src/tools/imageTool.js +41 -5
- package/src/tools/webTool.js +161 -48
- package/src/types/__tests__/agent.test.js +499 -0
- package/src/types/__tests__/contextReference.test.js +606 -0
- package/src/types/__tests__/conversation.test.js +555 -0
- package/src/types/__tests__/toolCommand.test.js +584 -0
- package/src/types/contextReference.js +1 -1
- package/src/utilities/__tests__/attachmentValidator.test.js +80 -0
- package/src/utilities/__tests__/configManager.test.js +397 -0
- package/src/utilities/__tests__/constants.test.js +49 -0
- package/src/utilities/__tests__/directoryAccessManager.test.js +388 -0
- package/src/utilities/__tests__/fileProcessor.test.js +104 -0
- package/src/utilities/__tests__/jsonRepair.test.js +104 -0
- package/src/utilities/__tests__/logger.test.js +129 -0
- package/src/utilities/__tests__/platformUtils.test.js +87 -0
- package/src/utilities/__tests__/structuredFileValidator.test.js +263 -0
- package/src/utilities/__tests__/tagParser.test.js +887 -0
- package/src/utilities/__tests__/toolConstants.test.js +94 -0
- package/src/utilities/tagParser.js +2 -2
- package/web-ui/build/index.html +2 -2
- package/web-ui/build/static/1c-8PZzOTzp.js +1 -0
- package/web-ui/build/static/abap-Bcx_Au1F.js +1 -0
- package/web-ui/build/static/abnf-BKTLqpWA.js +1 -0
- package/web-ui/build/static/abnf-J05BAvJt.js +1 -0
- package/web-ui/build/static/accesslog-Cp8_lqVY.js +1 -0
- package/web-ui/build/static/actionscript-BK0UaMrm.js +1 -0
- package/web-ui/build/static/actionscript-CyqZUddh.js +1 -0
- package/web-ui/build/static/ada-BNirS6Nr.js +1 -0
- package/web-ui/build/static/ada-BSFWcT1O.js +1 -0
- package/web-ui/build/static/agda-D0NJDJg7.js +1 -0
- package/web-ui/build/static/al-rWARKtwb.js +1 -0
- package/web-ui/build/static/angelscript-fCehtOYk.js +1 -0
- package/web-ui/build/static/antlr4-Dn9jrnZN.js +1 -0
- package/web-ui/build/static/apache-DaQCsvNW.js +1 -0
- package/web-ui/build/static/apacheconf-dY4i0Xvz.js +1 -0
- package/web-ui/build/static/apex-vhS4SI46.js +1 -0
- package/web-ui/build/static/apl-CKRkxH90.js +1 -0
- package/web-ui/build/static/applescript-CWmpQIEB.js +1 -0
- package/web-ui/build/static/applescript-DBaX7Uqo.js +1 -0
- package/web-ui/build/static/aql-8s41lrIa.js +1 -0
- package/web-ui/build/static/arcade-w2_RhAcq.js +1 -0
- package/web-ui/build/static/arduino-I7BtZTu6.js +1 -0
- package/web-ui/build/static/arduino-h2LZErKQ.js +1 -0
- package/web-ui/build/static/arff-C543-5a1.js +1 -0
- package/web-ui/build/static/armasm-DyZdFOzz.js +1 -0
- package/web-ui/build/static/asciidoc-ZzENlACu.js +1 -0
- package/web-ui/build/static/asciidoc-_j9x9bUz.js +1 -0
- package/web-ui/build/static/asm6502-CsNsmBfq.js +1 -0
- package/web-ui/build/static/asmatmel-CkIVf_tD.js +1 -0
- package/web-ui/build/static/aspectj-C6AQLme_.js +1 -0
- package/web-ui/build/static/aspnet-5AkdiVyL.js +1 -0
- package/web-ui/build/static/autohotkey-BRZVABiS.js +1 -0
- package/web-ui/build/static/autohotkey-DVTmfk_f.js +1 -0
- package/web-ui/build/static/autoit-3UEcWu5a.js +1 -0
- package/web-ui/build/static/autoit-BDByIKSH.js +1 -0
- package/web-ui/build/static/avisynth-BHc4uUkP.js +1 -0
- package/web-ui/build/static/avrasm-BAPq8_aI.js +1 -0
- package/web-ui/build/static/avro-idl-BKEBYUtv.js +1 -0
- package/web-ui/build/static/awk-CBCkArRT.js +1 -0
- package/web-ui/build/static/axapta-DlOgnXSZ.js +1 -0
- package/web-ui/build/static/bash-C6Brp5OE.js +1 -0
- package/web-ui/build/static/bash-DkEO7JRq.js +1 -0
- package/web-ui/build/static/basic-DG6TYB0R.js +1 -0
- package/web-ui/build/static/basic-DRPcNfAn.js +1 -0
- package/web-ui/build/static/batch-DdjZ5KC1.js +1 -0
- package/web-ui/build/static/bbcode-DCXEEs2w.js +1 -0
- package/web-ui/build/static/bicep-CpLhfOwt.js +1 -0
- package/web-ui/build/static/birb-DNWkqgQm.js +1 -0
- package/web-ui/build/static/bison-DwxbQHJ9.js +1 -0
- package/web-ui/build/static/bnf-Cgnt7npj.js +1 -0
- package/web-ui/build/static/bnf-DSTq_eu9.js +1 -0
- package/web-ui/build/static/brainfuck-Bi8mGutW.js +1 -0
- package/web-ui/build/static/brainfuck-DOWfqVtR.js +1 -0
- package/web-ui/build/static/brightscript-D95pbP-v.js +1 -0
- package/web-ui/build/static/bro-BrDVwXeg.js +1 -0
- package/web-ui/build/static/bsl-BMoXI84g.js +1 -0
- package/web-ui/build/static/c-CKH4C7-Z.js +1 -0
- package/web-ui/build/static/c-Z0txyaeJ.js +1 -0
- package/web-ui/build/static/c-like-Dzm9dMmR.js +1 -0
- package/web-ui/build/static/cal-DoyAwiUt.js +1 -0
- package/web-ui/build/static/capnproto-DeIi9LOH.js +1 -0
- package/web-ui/build/static/ceylon-Coim6DIe.js +1 -0
- package/web-ui/build/static/cfscript-CwsndC-j.js +1 -0
- package/web-ui/build/static/chaiscript-D6Aq-PSv.js +1 -0
- package/web-ui/build/static/cil-vi56VRk_.js +1 -0
- package/web-ui/build/static/clean-BfpKrTdp.js +1 -0
- package/web-ui/build/static/clojure-DUtl6BaB.js +1 -0
- package/web-ui/build/static/clojure-DXJHtDlY.js +1 -0
- package/web-ui/build/static/clojure-repl-BxwP5C3g.js +1 -0
- package/web-ui/build/static/cmake-C9_VZ1vH.js +1 -0
- package/web-ui/build/static/cmake-dplO-PGD.js +1 -0
- package/web-ui/build/static/cobol-DsZhu02V.js +1 -0
- package/web-ui/build/static/coffeescript-Cw9jtGNP.js +1 -0
- package/web-ui/build/static/coffeescript-DvDt4T2l.js +1 -0
- package/web-ui/build/static/concurnas-Bzc_Dcdd.js +1 -0
- package/web-ui/build/static/coq-Cv-5BqGo.js +1 -0
- package/web-ui/build/static/coq-DWFe2ssK.js +1 -0
- package/web-ui/build/static/cos-D6Lc6Cah.js +1 -0
- package/web-ui/build/static/cpp-BFmLjd76.js +1 -0
- package/web-ui/build/static/cpp-DVQgbHji.js +1 -0
- package/web-ui/build/static/crmsh-Cqveth9p.js +1 -0
- package/web-ui/build/static/crystal-0syYaH4Y.js +1 -0
- package/web-ui/build/static/crystal-Noptp-kr.js +1 -0
- package/web-ui/build/static/csharp-B799cFMH.js +1 -0
- package/web-ui/build/static/csharp-_HlvMZzJ.js +1 -0
- package/web-ui/build/static/cshtml-CnwOXlhP.js +1 -0
- package/web-ui/build/static/csp-1ffxIG_-.js +1 -0
- package/web-ui/build/static/csp-Bws60bPu.js +1 -0
- package/web-ui/build/static/css-BGdwXzpm.js +1 -0
- package/web-ui/build/static/css-extras-DZCECiOa.js +1 -0
- package/web-ui/build/static/csv-FMFGT0T4.js +1 -0
- package/web-ui/build/static/cypher-DnXoEwRp.js +1 -0
- package/web-ui/build/static/d-CBrts1xB.js +1 -0
- package/web-ui/build/static/d-qrJLxk2L.js +1 -0
- package/web-ui/build/static/dart-3vBSXJVV.js +1 -0
- package/web-ui/build/static/dart-Cj5b7BV9.js +1 -0
- package/web-ui/build/static/dataweave-BuFf63rk.js +1 -0
- package/web-ui/build/static/dax-Cl-se1JI.js +1 -0
- package/web-ui/build/static/delphi-CLkRb26y.js +1 -0
- package/web-ui/build/static/dhall--TIL2Z--.js +1 -0
- package/web-ui/build/static/diff-Bn-XL2om.js +1 -0
- package/web-ui/build/static/diff-BsTwly4w.js +1 -0
- package/web-ui/build/static/django-BfRtHnTS.js +1 -0
- package/web-ui/build/static/django-Dm9O4e3A.js +1 -0
- package/web-ui/build/static/dns-BIVEp3uD.js +1 -0
- package/web-ui/build/static/dns-zone-file-CO7LnOdh.js +1 -0
- package/web-ui/build/static/docker-BhwMip1R.js +1 -0
- package/web-ui/build/static/dockerfile-8Tjw9_jF.js +1 -0
- package/web-ui/build/static/dos-CRMiAo46.js +1 -0
- package/web-ui/build/static/dot-DPpW7LrJ.js +1 -0
- package/web-ui/build/static/dsconfig-D0zbYilI.js +1 -0
- package/web-ui/build/static/dts-C_-yqWEL.js +1 -0
- package/web-ui/build/static/dust-CucJgqnE.js +1 -0
- package/web-ui/build/static/ebnf-CCHK0H6j.js +1 -0
- package/web-ui/build/static/ebnf-CDdAcveH.js +1 -0
- package/web-ui/build/static/editorconfig-f-5b95UM.js +1 -0
- package/web-ui/build/static/eiffel-MmghFce7.js +1 -0
- package/web-ui/build/static/ejs-CPMN4-jk.js +1 -0
- package/web-ui/build/static/elixir-57Ldyw8h.js +1 -0
- package/web-ui/build/static/elixir-DTxnmhIt.js +1 -0
- package/web-ui/build/static/elm-ClV9zQoT.js +1 -0
- package/web-ui/build/static/elm-pX-i6o7U.js +1 -0
- package/web-ui/build/static/erb-BPeO9smT.js +1 -0
- package/web-ui/build/static/erb-BnZQ4STz.js +1 -0
- package/web-ui/build/static/erlang-DPtI7VK_.js +1 -0
- package/web-ui/build/static/erlang-KG5mg5wN.js +1 -0
- package/web-ui/build/static/erlang-repl-BqPVXZ3Y.js +1 -0
- package/web-ui/build/static/etlua-BRc0Qbbs.js +1 -0
- package/web-ui/build/static/excel-BAlZ9Hkj.js +1 -0
- package/web-ui/build/static/excel-formula-BOW-bnHh.js +1 -0
- package/web-ui/build/static/factor-DCCsCpGM.js +1 -0
- package/web-ui/build/static/false-CnqnCzBU.js +1 -0
- package/web-ui/build/static/firestore-security-rules-DtkQ3uEq.js +1 -0
- package/web-ui/build/static/fix-CfPjl4Xr.js +1 -0
- package/web-ui/build/static/flix-BCA3BceS.js +1 -0
- package/web-ui/build/static/flow-C-5ewqYW.js +1 -0
- package/web-ui/build/static/fortran-CfDtl8An.js +1 -0
- package/web-ui/build/static/fortran-DQ_knNPt.js +1 -0
- package/web-ui/build/static/fsharp-CcVQ3IqX.js +1 -0
- package/web-ui/build/static/fsharp-olQ6ojCa.js +1 -0
- package/web-ui/build/static/ftl-DIWHDyWt.js +1 -0
- package/web-ui/build/static/gams-_NVFTSj5.js +1 -0
- package/web-ui/build/static/gap-DIG5TV2b.js +1 -0
- package/web-ui/build/static/gauss-C8rjPjTG.js +1 -0
- package/web-ui/build/static/gcode-8wJu4gcL.js +1 -0
- package/web-ui/build/static/gcode-DjHf417I.js +1 -0
- package/web-ui/build/static/gdscript-BZdmRCYu.js +1 -0
- package/web-ui/build/static/gedcom-BVFJ8C_Q.js +1 -0
- package/web-ui/build/static/gherkin-7NQkoaub.js +1 -0
- package/web-ui/build/static/gherkin-bSpNbJ48.js +1 -0
- package/web-ui/build/static/git-BRY_UXsc.js +1 -0
- package/web-ui/build/static/glsl-Ck6ShGRC.js +1 -0
- package/web-ui/build/static/glsl-jwCJ0Pmg.js +1 -0
- package/web-ui/build/static/gml-BAjG4Kr2.js +1 -0
- package/web-ui/build/static/gml-BnhKb5Da.js +1 -0
- package/web-ui/build/static/gn-Dux09iX8.js +1 -0
- package/web-ui/build/static/go-BOG-9Cqk.js +1 -0
- package/web-ui/build/static/go-BWZB_3b5.js +1 -0
- package/web-ui/build/static/go-module-BVLW7KBE.js +1 -0
- package/web-ui/build/static/golo-BD_SOfwL.js +1 -0
- package/web-ui/build/static/gradle-zSadWOD2.js +1 -0
- package/web-ui/build/static/graphql-BQlyj78B.js +1 -0
- package/web-ui/build/static/groovy-BurRMqQS.js +1 -0
- package/web-ui/build/static/groovy-SP58zwlE.js +1 -0
- package/web-ui/build/static/haml-eZ5ah5KY.js +1 -0
- package/web-ui/build/static/haml-lFC47IZb.js +1 -0
- package/web-ui/build/static/handlebars-B4UXrB-Q.js +1 -0
- package/web-ui/build/static/handlebars-CQ-Q5HnC.js +1 -0
- package/web-ui/build/static/haskell-AQrRyTSy.js +1 -0
- package/web-ui/build/static/haskell-BZTSbaB_.js +1 -0
- package/web-ui/build/static/haxe-5l1X6ESp.js +1 -0
- package/web-ui/build/static/haxe-DBn90muG.js +1 -0
- package/web-ui/build/static/hcl-CnMewPLM.js +1 -0
- package/web-ui/build/static/hlsl-RAtuBzr5.js +1 -0
- package/web-ui/build/static/hoon-CSqRU_4M.js +1 -0
- package/web-ui/build/static/hpkp-_gNbXcHt.js +1 -0
- package/web-ui/build/static/hsp-DY1V4au3.js +1 -0
- package/web-ui/build/static/hsts-j5z2jJo8.js +1 -0
- package/web-ui/build/static/htmlbars-C5EHvatr.js +1 -0
- package/web-ui/build/static/http-DgWgQrZh.js +1 -0
- package/web-ui/build/static/http-DphJL0q2.js +1 -0
- package/web-ui/build/static/hy-DnBqjPsB.js +1 -0
- package/web-ui/build/static/ichigojam-DeiCOKyF.js +1 -0
- package/web-ui/build/static/icon-CWANFWY5.js +1 -0
- package/web-ui/build/static/icu-message-format-C7w3vpgC.js +1 -0
- package/web-ui/build/static/idris-BeD8eULz.js +1 -0
- package/web-ui/build/static/iecst-BIznHXqY.js +1 -0
- package/web-ui/build/static/ignore-BcFgcNaS.js +1 -0
- package/web-ui/build/static/index-D8uVofpo.js +13 -0
- package/web-ui/build/static/index-DPFadqM6.css +1 -0
- package/web-ui/build/static/index-SkOgWEAU.js +1 -0
- package/web-ui/build/static/index-Vd3WlhtC.js +747 -0
- package/web-ui/build/static/inform7-CkQD_jz-.js +1 -0
- package/web-ui/build/static/inform7-phQiuDty.js +1 -0
- package/web-ui/build/static/ini-Bw_QAbzV.js +1 -0
- package/web-ui/build/static/ini-CB8ZxX7y.js +1 -0
- package/web-ui/build/static/io-D6IgpCmL.js +1 -0
- package/web-ui/build/static/irpf90-Ctj0koST.js +1 -0
- package/web-ui/build/static/isbl-D2mGcH87.js +1 -0
- package/web-ui/build/static/j-KvHmDBWH.js +1 -0
- package/web-ui/build/static/java-BeBIdo5i.js +1 -0
- package/web-ui/build/static/java-llFZkHLe.js +1 -0
- package/web-ui/build/static/javadoc-DXvQGu0s.js +1 -0
- package/web-ui/build/static/javadoclike-B5qdA9KZ.js +1 -0
- package/web-ui/build/static/javascript-De6HzHvc.js +1 -0
- package/web-ui/build/static/javastacktrace-C5MolKiP.js +1 -0
- package/web-ui/build/static/jboss-cli-2TXd54zo.js +1 -0
- package/web-ui/build/static/jexl-W4AVA9fi.js +1 -0
- package/web-ui/build/static/jolie-DTJKRMZN.js +1 -0
- package/web-ui/build/static/jq-BYg-QQKh.js +1 -0
- package/web-ui/build/static/js-extras-BrYWd2VE.js +1 -0
- package/web-ui/build/static/js-templates-DorYpbHq.js +1 -0
- package/web-ui/build/static/jsdoc-CRF8n9pZ.js +1 -0
- package/web-ui/build/static/json-Dlcd7rla.js +1 -0
- package/web-ui/build/static/json-rhOJZzzZ.js +1 -0
- package/web-ui/build/static/json5-hTq1nNIW.js +1 -0
- package/web-ui/build/static/jsonp-CMg9Xvol.js +1 -0
- package/web-ui/build/static/jsstacktrace-hEeYxHtB.js +1 -0
- package/web-ui/build/static/jsx-B7PtA8WD.js +1 -0
- package/web-ui/build/static/julia-CNiEEY-n.js +1 -0
- package/web-ui/build/static/julia-eE0_SJlc.js +1 -0
- package/web-ui/build/static/julia-repl-CUJTTT4C.js +1 -0
- package/web-ui/build/static/keepalived-GUWJBqyD.js +1 -0
- package/web-ui/build/static/keyman-Bdf9MJyu.js +1 -0
- package/web-ui/build/static/kotlin-Ch6Bej5W.js +1 -0
- package/web-ui/build/static/kotlin-DFJ7D7Lw.js +1 -0
- package/web-ui/build/static/kumir-DJLIjcCs.js +1 -0
- package/web-ui/build/static/kusto-BM0YTwU4.js +1 -0
- package/web-ui/build/static/lasso-Z1DVS84K.js +1 -0
- package/web-ui/build/static/latex-BWbw71RK.js +1 -0
- package/web-ui/build/static/latex-CMzqmbhw.js +1 -0
- package/web-ui/build/static/latte-C1g8_grc.js +1 -0
- package/web-ui/build/static/ldif-C6QH3OIL.js +1 -0
- package/web-ui/build/static/leaf-CbR--ZsH.js +1 -0
- package/web-ui/build/static/less-ClVrKh2Z.js +1 -0
- package/web-ui/build/static/less-DNSxm8UA.js +1 -0
- package/web-ui/build/static/lilypond-lTzf_sWt.js +1 -0
- package/web-ui/build/static/liquid-Bn91mVfC.js +1 -0
- package/web-ui/build/static/lisp-CU3bHohQ.js +1 -0
- package/web-ui/build/static/lisp-DbgzE9W8.js +1 -0
- package/web-ui/build/static/livecodeserver-Cse1Uz3H.js +1 -0
- package/web-ui/build/static/livescript-BaxbgzWP.js +1 -0
- package/web-ui/build/static/livescript-nJt61DBy.js +1 -0
- package/web-ui/build/static/llvm-DBboo6UI.js +1 -0
- package/web-ui/build/static/llvm-vIy7XYVy.js +1 -0
- package/web-ui/build/static/log-CT7nfoDW.js +1 -0
- package/web-ui/build/static/lolcode-CUKVytZh.js +1 -0
- package/web-ui/build/static/lsl-CsAOlGF2.js +1 -0
- package/web-ui/build/static/lua-BuU2FFxP.js +1 -0
- package/web-ui/build/static/lua-CiDuKQaa.js +1 -0
- package/web-ui/build/static/magma-7vR0zcmS.js +1 -0
- package/web-ui/build/static/makefile-Buiz-Msh.js +1 -0
- package/web-ui/build/static/makefile-DXW_-6OY.js +1 -0
- package/web-ui/build/static/markdown-Bk5DUoGY.js +1 -0
- package/web-ui/build/static/markdown-CRS5W_Ai.js +1 -0
- package/web-ui/build/static/markup-templating-24odpmF3.js +1 -0
- package/web-ui/build/static/mathematica-BxcwhJUp.js +1 -0
- package/web-ui/build/static/matlab-3pJYx_Fb.js +1 -0
- package/web-ui/build/static/matlab-BqlRrzMf.js +1 -0
- package/web-ui/build/static/maxima-DlCfUpcj.js +1 -0
- package/web-ui/build/static/maxscript-Cu_gCaFU.js +1 -0
- package/web-ui/build/static/mel-D7iQ-5Up.js +1 -0
- package/web-ui/build/static/mel-DzBKNpoN.js +1 -0
- package/web-ui/build/static/mercury-Dfrb-i8A.js +1 -0
- package/web-ui/build/static/mermaid-WN7V2_Eq.js +1 -0
- package/web-ui/build/static/mipsasm-CcijzL0q.js +1 -0
- package/web-ui/build/static/mizar-Bk68zACP.js +1 -0
- package/web-ui/build/static/mizar-Twc2-iZ4.js +1 -0
- package/web-ui/build/static/mojolicious-DBbo2S7X.js +1 -0
- package/web-ui/build/static/mongodb-2RsFIjgg.js +1 -0
- package/web-ui/build/static/monkey-CPXtQ0Bf.js +1 -0
- package/web-ui/build/static/monkey-DjV7Wcek.js +1 -0
- package/web-ui/build/static/moonscript-B5M5as70.js +1 -0
- package/web-ui/build/static/moonscript-D1BHW4Il.js +1 -0
- package/web-ui/build/static/n1ql-D0heNDBD.js +1 -0
- package/web-ui/build/static/n1ql-DfHqXQD7.js +1 -0
- package/web-ui/build/static/n4js-CaPf44Dz.js +1 -0
- package/web-ui/build/static/nand2tetris-hdl-D1nf9mn4.js +1 -0
- package/web-ui/build/static/naniscript-DnCnu5ZX.js +1 -0
- package/web-ui/build/static/nasm-BZrSaMsK.js +1 -0
- package/web-ui/build/static/neon-D29Grm2v.js +1 -0
- package/web-ui/build/static/nevod-DgSNbQkE.js +1 -0
- package/web-ui/build/static/nginx-BAaDGDfT.js +1 -0
- package/web-ui/build/static/nginx-BvJ1lrHX.js +1 -0
- package/web-ui/build/static/nim--9zzVe5F.js +1 -0
- package/web-ui/build/static/nim-Br1relpU.js +1 -0
- package/web-ui/build/static/nix--0ftErCy.js +1 -0
- package/web-ui/build/static/nix-104ztQqr.js +1 -0
- package/web-ui/build/static/node-repl-BUMAf7_p.js +1 -0
- package/web-ui/build/static/nsis-BaeKybNA.js +1 -0
- package/web-ui/build/static/nsis-CdZEv2iA.js +1 -0
- package/web-ui/build/static/objectivec-DBB4ymdg.js +1 -0
- package/web-ui/build/static/objectivec-kFYXC6g4.js +1 -0
- package/web-ui/build/static/ocaml-D1GXvN7c.js +1 -0
- package/web-ui/build/static/ocaml-D80jRMPE.js +1 -0
- package/web-ui/build/static/opencl-fb7BfRdO.js +1 -0
- package/web-ui/build/static/openqasm-CWUBrR2w.js +1 -0
- package/web-ui/build/static/openscad-Dim7ILSL.js +1 -0
- package/web-ui/build/static/oxygene-BSwApkwz.js +1 -0
- package/web-ui/build/static/oz-CMtRoi5F.js +1 -0
- package/web-ui/build/static/parigp-AH8cZ38D.js +1 -0
- package/web-ui/build/static/parser-bBNjuhG3.js +1 -0
- package/web-ui/build/static/parser3-DUtoWEAd.js +1 -0
- package/web-ui/build/static/pascal-Cr3DPIYT.js +1 -0
- package/web-ui/build/static/pascaligo-pWW12jfs.js +1 -0
- package/web-ui/build/static/pcaxis-DBw9rxmr.js +1 -0
- package/web-ui/build/static/peoplecode-aCpMPm_s.js +1 -0
- package/web-ui/build/static/perl-BpZ7GmJ3.js +1 -0
- package/web-ui/build/static/perl-fnHTrqJL.js +1 -0
- package/web-ui/build/static/pf-Dz352ty7.js +1 -0
- package/web-ui/build/static/pgsql-CHPUdlI_.js +1 -0
- package/web-ui/build/static/php-BRwItjmS.js +1 -0
- package/web-ui/build/static/php-CrX_kswO.js +1 -0
- package/web-ui/build/static/php-extras-BmeRXDSO.js +1 -0
- package/web-ui/build/static/php-template-B0MFJ9RR.js +1 -0
- package/web-ui/build/static/phpdoc-wAPkJj9X.js +1 -0
- package/web-ui/build/static/plaintext-CmPk1gvP.js +1 -0
- package/web-ui/build/static/plsql-pWVw0sCJ.js +1 -0
- package/web-ui/build/static/pony-B4SXhyDS.js +1 -0
- package/web-ui/build/static/powerquery-ZJ28bdRR.js +1 -0
- package/web-ui/build/static/powershell-CWg1ca6z.js +1 -0
- package/web-ui/build/static/powershell-Dnl0aBXc.js +1 -0
- package/web-ui/build/static/processing-CbYVU7hZ.js +1 -0
- package/web-ui/build/static/processing-DnroirEw.js +1 -0
- package/web-ui/build/static/profile-DLNc-MTA.js +1 -0
- package/web-ui/build/static/prolog-4KlPFQus.js +1 -0
- package/web-ui/build/static/prolog-CtUicY87.js +1 -0
- package/web-ui/build/static/promql-C_i6OJVg.js +1 -0
- package/web-ui/build/static/properties-Cj0lBOSP.js +1 -0
- package/web-ui/build/static/properties-vGFibcz9.js +1 -0
- package/web-ui/build/static/protobuf-BOIGxbSP.js +1 -0
- package/web-ui/build/static/protobuf-CQ3su-J7.js +1 -0
- package/web-ui/build/static/psl-DeG5_YUF.js +1 -0
- package/web-ui/build/static/pug-BieVVXYz.js +1 -0
- package/web-ui/build/static/puppet-Ba40SVKU.js +1 -0
- package/web-ui/build/static/puppet-D7BzrcGt.js +1 -0
- package/web-ui/build/static/pure-DZnkz1iT.js +1 -0
- package/web-ui/build/static/purebasic-CLLZW_6G.js +1 -0
- package/web-ui/build/static/purebasic-CYPZo_H6.js +1 -0
- package/web-ui/build/static/purescript-Dyjfu5Id.js +1 -0
- package/web-ui/build/static/python-BdIWKxdN.js +1 -0
- package/web-ui/build/static/python-ofKsqxv7.js +1 -0
- package/web-ui/build/static/python-repl-DiTYb1xK.js +1 -0
- package/web-ui/build/static/q-B4P0If_I.js +1 -0
- package/web-ui/build/static/q-t_17xfY8.js +1 -0
- package/web-ui/build/static/qml-B5WhiN48.js +1 -0
- package/web-ui/build/static/qml-Dq0cESXJ.js +1 -0
- package/web-ui/build/static/qore-DCx30XRf.js +1 -0
- package/web-ui/build/static/qsharp-UrBScekp.js +1 -0
- package/web-ui/build/static/r-B0Ty1RKQ.js +1 -0
- package/web-ui/build/static/r-B0za8QKS.js +1 -0
- package/web-ui/build/static/racket-Dj6WEyhS.js +1 -0
- package/web-ui/build/static/reason-dj9hJSfr.js +1 -0
- package/web-ui/build/static/reasonml-B-q5_wag.js +1 -0
- package/web-ui/build/static/regex-4HEc5C1m.js +1 -0
- package/web-ui/build/static/rego-BdQe18RK.js +1 -0
- package/web-ui/build/static/renpy-CVMA2llL.js +1 -0
- package/web-ui/build/static/rest-9B4JWVGr.js +1 -0
- package/web-ui/build/static/rib-DR-U8OaT.js +1 -0
- package/web-ui/build/static/rip-Bu2t_rFZ.js +1 -0
- package/web-ui/build/static/roboconf-CJeXD5-I.js +1 -0
- package/web-ui/build/static/roboconf-DzDTVrdM.js +1 -0
- package/web-ui/build/static/robotframework-CR7KyPpN.js +1 -0
- package/web-ui/build/static/routeros-B2741z2k.js +1 -0
- package/web-ui/build/static/rsl-B9F_ZCgv.js +1 -0
- package/web-ui/build/static/ruby-I2JTNgyY.js +1 -0
- package/web-ui/build/static/ruby-QGDPOmJX.js +1 -0
- package/web-ui/build/static/ruleslanguage-CGzXEUCO.js +1 -0
- package/web-ui/build/static/rust-BxW5-WOm.js +1 -0
- package/web-ui/build/static/rust-CSOA43di.js +1 -0
- package/web-ui/build/static/sas-Bclfx4g3.js +1 -0
- package/web-ui/build/static/sas-xbQaiYjT.js +1 -0
- package/web-ui/build/static/sass-DJPbdNwd.js +1 -0
- package/web-ui/build/static/scala-Bo18NtHQ.js +1 -0
- package/web-ui/build/static/scala-Cy0JH-SG.js +1 -0
- package/web-ui/build/static/scheme-BjcWWjIx.js +1 -0
- package/web-ui/build/static/scheme-DQdj8PzN.js +1 -0
- package/web-ui/build/static/scilab-Bn1KHdK-.js +1 -0
- package/web-ui/build/static/scss-B1twkZBz.js +1 -0
- package/web-ui/build/static/scss-DmOuMI4v.js +1 -0
- package/web-ui/build/static/shell-BUlkJG0S.js +1 -0
- package/web-ui/build/static/shell-session-Bke-svxA.js +1 -0
- package/web-ui/build/static/smali-Ch9S16HV.js +1 -0
- package/web-ui/build/static/smali-D_yDr_Aj.js +1 -0
- package/web-ui/build/static/smalltalk-B9TfQ5Md.js +1 -0
- package/web-ui/build/static/smalltalk-EwbZxZsR.js +1 -0
- package/web-ui/build/static/smarty-9kDPpeSm.js +1 -0
- package/web-ui/build/static/sml-2fEfT7rd.js +1 -0
- package/web-ui/build/static/sml-BiwoLNk7.js +1 -0
- package/web-ui/build/static/solidity-n_x8Oe0h.js +1 -0
- package/web-ui/build/static/solution-file-B2mvjI3e.js +1 -0
- package/web-ui/build/static/soy-DPkgKBIS.js +1 -0
- package/web-ui/build/static/sparql-Cy95tds0.js +1 -0
- package/web-ui/build/static/splunk-spl-Ym3z9ouN.js +1 -0
- package/web-ui/build/static/sqf-CXZTG8WE.js +1 -0
- package/web-ui/build/static/sqf-Cwi3yg7f.js +1 -0
- package/web-ui/build/static/sql-DPxSQY4S.js +1 -0
- package/web-ui/build/static/sql-peh7ijGj.js +1 -0
- package/web-ui/build/static/sql_more-0YAbAuPw.js +1 -0
- package/web-ui/build/static/squirrel-CphzjV0e.js +1 -0
- package/web-ui/build/static/stan-0-xZ95-O.js +1 -0
- package/web-ui/build/static/stan-CaI4__2g.js +1 -0
- package/web-ui/build/static/stata-BrbzrGSs.js +1 -0
- package/web-ui/build/static/step21-C_qeyVLw.js +1 -0
- package/web-ui/build/static/stylus-Btycb2sZ.js +1 -0
- package/web-ui/build/static/stylus-FoBJ7jki.js +1 -0
- package/web-ui/build/static/subunit-Dpg-m04-.js +1 -0
- package/web-ui/build/static/swift-Cr9uZmgb.js +1 -0
- package/web-ui/build/static/swift-hGLFtD7e.js +1 -0
- package/web-ui/build/static/systemd-Bls2D9Vj.js +1 -0
- package/web-ui/build/static/t4-cs-C4qDO-jJ.js +1 -0
- package/web-ui/build/static/t4-templating-BbCFPMPO.js +1 -0
- package/web-ui/build/static/t4-vb-D1zoEccT.js +1 -0
- package/web-ui/build/static/taggerscript-CWHk9Gih.js +1 -0
- package/web-ui/build/static/tap-Bjt0UnzV.js +1 -0
- package/web-ui/build/static/tap-BnHKwLQs.js +1 -0
- package/web-ui/build/static/tcl-Zo9kx4y-.js +1 -0
- package/web-ui/build/static/tcl-fzLmefkt.js +1 -0
- package/web-ui/build/static/textile-9lIlUPH5.js +1 -0
- package/web-ui/build/static/thrift-M3K6r5Cy.js +1 -0
- package/web-ui/build/static/toml-HpaKqckc.js +1 -0
- package/web-ui/build/static/tp-DFKuxrKj.js +1 -0
- package/web-ui/build/static/tremor-D4_bUtMB.js +1 -0
- package/web-ui/build/static/tsx-o1RT-T90.js +1 -0
- package/web-ui/build/static/tt2-1xDqcN_2.js +1 -0
- package/web-ui/build/static/turtle-Dlt-aGky.js +1 -0
- package/web-ui/build/static/twig-CJ_BnGSR.js +1 -0
- package/web-ui/build/static/twig-CjsiSQb6.js +1 -0
- package/web-ui/build/static/typescript-B8B9zUn-.js +1 -0
- package/web-ui/build/static/typescript-D0Jgo8O7.js +1 -0
- package/web-ui/build/static/typoscript-C8Qke4ZB.js +1 -0
- package/web-ui/build/static/unrealscript-YxJdDNZ3.js +1 -0
- package/web-ui/build/static/uorazor-CtEVnqBv.js +1 -0
- package/web-ui/build/static/uri-YdaiQl4c.js +1 -0
- package/web-ui/build/static/v-CIyttMDD.js +1 -0
- package/web-ui/build/static/vala-DGslcym_.js +1 -0
- package/web-ui/build/static/vala-GFPx3uEJ.js +1 -0
- package/web-ui/build/static/vbnet-B20itab-.js +1 -0
- package/web-ui/build/static/vbnet-BdoN6egk.js +1 -0
- package/web-ui/build/static/vbscript-PHVh6Fp_.js +1 -0
- package/web-ui/build/static/vbscript-html-woH1VZ7U.js +1 -0
- package/web-ui/build/static/velocity-DtVfCZeg.js +1 -0
- package/web-ui/build/static/verilog-Bt6edXvM.js +1 -0
- package/web-ui/build/static/verilog-k_7lr9Zq.js +1 -0
- package/web-ui/build/static/vhdl-BMzOgOeK.js +1 -0
- package/web-ui/build/static/vhdl-BcAbtPG6.js +1 -0
- package/web-ui/build/static/vim-DrinG9a4.js +1 -0
- package/web-ui/build/static/vim-WihLATJL.js +1 -0
- package/web-ui/build/static/visual-basic-CJnvgPjM.js +1 -0
- package/web-ui/build/static/warpscript-zMlbUoZs.js +1 -0
- package/web-ui/build/static/wasm-GUnfTBUL.js +1 -0
- package/web-ui/build/static/web-idl-CfaLTG_r.js +1 -0
- package/web-ui/build/static/wiki-13AlLoOc.js +1 -0
- package/web-ui/build/static/wolfram-zHocYNXW.js +1 -0
- package/web-ui/build/static/wren-Byq862Iu.js +1 -0
- package/web-ui/build/static/x86asm-CLcOnePY.js +1 -0
- package/web-ui/build/static/xeora-BVHqWOFS.js +1 -0
- package/web-ui/build/static/xl-lXi8OYfr.js +1 -0
- package/web-ui/build/static/xml-KZjGBKxi.js +1 -0
- package/web-ui/build/static/xml-doc-DrQSDcEW.js +1 -0
- package/web-ui/build/static/xojo-DosHeFXU.js +1 -0
- package/web-ui/build/static/xquery-BZN1F14Q.js +1 -0
- package/web-ui/build/static/xquery-Cnz7ZLFr.js +1 -0
- package/web-ui/build/static/yaml-BzXOcy9u.js +1 -0
- package/web-ui/build/static/yaml-C207y5bt.js +1 -0
- package/web-ui/build/static/yang-ByrBdDIg.js +1 -0
- package/web-ui/build/static/zephir-bahTa7of.js +1 -0
- package/web-ui/build/static/zig-BlFYhdtC.js +1 -0
- package/src/tools/browserTool.js +0 -897
- package/src/utilities/platformUtils.test.js +0 -98
- package/web-ui/build/static/index-SmQFfvBs.js +0 -746
- package/web-ui/build/static/index-V2ySwjHp.css +0 -1
|
@@ -92,12 +92,10 @@ describe('Terminal UI Infrastructure - Imports', () => {
|
|
|
92
92
|
});
|
|
93
93
|
|
|
94
94
|
test('useConnection hook can be imported', async () => {
|
|
95
|
-
const
|
|
95
|
+
const mod = await import('../../state/useConnection.js');
|
|
96
96
|
|
|
97
|
-
expect(useConnection).toBeDefined();
|
|
98
|
-
expect(typeof useConnection).toBe('function');
|
|
99
|
-
expect(useConnectionStatus).toBeDefined();
|
|
100
|
-
expect(typeof useConnectionStatus).toBe('function');
|
|
97
|
+
expect(mod.useConnection).toBeDefined();
|
|
98
|
+
expect(typeof mod.useConnection).toBe('function');
|
|
101
99
|
});
|
|
102
100
|
});
|
|
103
101
|
|
|
@@ -1946,15 +1946,6 @@ class WebServer {
|
|
|
1946
1946
|
try {
|
|
1947
1947
|
const { sessionId, filename } = req.params;
|
|
1948
1948
|
|
|
1949
|
-
// Security validation: Check if sessionId exists
|
|
1950
|
-
const session = this.sessions.get(sessionId);
|
|
1951
|
-
if (!session) {
|
|
1952
|
-
return res.status(HTTP_STATUS.FORBIDDEN).json({
|
|
1953
|
-
success: false,
|
|
1954
|
-
error: 'Invalid session'
|
|
1955
|
-
});
|
|
1956
|
-
}
|
|
1957
|
-
|
|
1958
1949
|
// Security validation: Check filename for path traversal attempts
|
|
1959
1950
|
const normalizedFilename = path.basename(filename);
|
|
1960
1951
|
if (normalizedFilename !== filename || filename.includes('..') || filename.includes('/') || filename.includes('\\')) {
|
|
@@ -1964,19 +1955,32 @@ class WebServer {
|
|
|
1964
1955
|
});
|
|
1965
1956
|
}
|
|
1966
1957
|
|
|
1958
|
+
// Session may or may not still exist (server restarts lose session map)
|
|
1959
|
+
const session = this.sessions.get(sessionId);
|
|
1960
|
+
const projectDir = session?.projectDir || process.cwd();
|
|
1961
|
+
|
|
1967
1962
|
// Try to locate the image in multiple possible locations
|
|
1968
1963
|
let imagePath = null;
|
|
1969
1964
|
const searchPaths = [
|
|
1970
1965
|
// 1. Session's project directory images folder
|
|
1971
|
-
path.join(
|
|
1966
|
+
path.join(projectDir, 'images', normalizedFilename),
|
|
1972
1967
|
// 2. Temp directory for this session
|
|
1973
1968
|
path.join('/tmp/loxia-images', sessionId, normalizedFilename),
|
|
1974
1969
|
// 3. General temp images directory
|
|
1975
|
-
path.join('/tmp/loxia-images', normalizedFilename)
|
|
1970
|
+
path.join('/tmp/loxia-images', normalizedFilename),
|
|
1971
|
+
// 4. OS temp dir (Windows: %TEMP%, Linux: /tmp)
|
|
1972
|
+
path.join(process.env.TEMP || process.env.TMP || '/tmp', 'loxia-images', sessionId, normalizedFilename),
|
|
1973
|
+
path.join(process.env.TEMP || process.env.TMP || '/tmp', 'loxia-images', normalizedFilename)
|
|
1976
1974
|
];
|
|
1977
1975
|
|
|
1978
|
-
//
|
|
1979
|
-
|
|
1976
|
+
// 5. Search ALL known project dirs from active sessions (not just current)
|
|
1977
|
+
for (const [, sess] of this.sessions) {
|
|
1978
|
+
if (sess?.projectDir && sess.projectDir !== projectDir) {
|
|
1979
|
+
searchPaths.push(path.join(sess.projectDir, 'images', normalizedFilename));
|
|
1980
|
+
}
|
|
1981
|
+
}
|
|
1982
|
+
|
|
1983
|
+
// 6. Search in agent working directories AND agent projectDirs
|
|
1980
1984
|
if (this.orchestrator?.agentPool) {
|
|
1981
1985
|
try {
|
|
1982
1986
|
const agents = await this.orchestrator.agentPool.getAllAgents();
|
|
@@ -1986,6 +1990,10 @@ class WebServer {
|
|
|
1986
1990
|
path.join(agent.directoryAccess.workingDirectory, 'images', normalizedFilename)
|
|
1987
1991
|
);
|
|
1988
1992
|
}
|
|
1993
|
+
// Also check the agent's own projectDir (may differ from session projectDir)
|
|
1994
|
+
if (agent.projectDir) {
|
|
1995
|
+
searchPaths.push(path.join(agent.projectDir, 'images', normalizedFilename));
|
|
1996
|
+
}
|
|
1989
1997
|
}
|
|
1990
1998
|
} catch (error) {
|
|
1991
1999
|
this.logger.warn('Failed to get agent working directories for image search', {
|
|
@@ -1994,6 +2002,12 @@ class WebServer {
|
|
|
1994
2002
|
}
|
|
1995
2003
|
}
|
|
1996
2004
|
|
|
2005
|
+
// 7. Common user project directory (fallback for when session is gone)
|
|
2006
|
+
const homeDir = process.env.USERPROFILE || process.env.HOME || '';
|
|
2007
|
+
if (homeDir) {
|
|
2008
|
+
searchPaths.push(path.join(homeDir, 'Loxia', 'images', normalizedFilename));
|
|
2009
|
+
}
|
|
2010
|
+
|
|
1997
2011
|
// Find the first existing file
|
|
1998
2012
|
for (const searchPath of searchPaths) {
|
|
1999
2013
|
try {
|
|
@@ -2576,6 +2590,76 @@ class WebServer {
|
|
|
2576
2590
|
}
|
|
2577
2591
|
});
|
|
2578
2592
|
|
|
2593
|
+
// ============================================
|
|
2594
|
+
// Artifacts API Endpoint
|
|
2595
|
+
// ============================================
|
|
2596
|
+
|
|
2597
|
+
// DEBUG: Inject a test artifact into an agent (remove after testing)
|
|
2598
|
+
this.app.post('/api/agents/:agentId/artifacts/test', async (req, res) => {
|
|
2599
|
+
try {
|
|
2600
|
+
const { agentId } = req.params;
|
|
2601
|
+
const agent = await this.orchestrator.agentPool.getAgent(agentId);
|
|
2602
|
+
if (!agent) return res.status(404).json({ success: false, error: 'Agent not found' });
|
|
2603
|
+
|
|
2604
|
+
if (!agent.artifacts) agent.artifacts = {};
|
|
2605
|
+
const testPath = (agent.directoryAccess?.workingDirectory || '/test') + '/test-artifact.js';
|
|
2606
|
+
const displayPath = 'test-artifact.js';
|
|
2607
|
+
|
|
2608
|
+
agent.artifacts[testPath] = {
|
|
2609
|
+
displayPath,
|
|
2610
|
+
versions: [{
|
|
2611
|
+
id: `v-${Date.now()}`,
|
|
2612
|
+
content: '// Test artifact\nconsole.log("Hello from artifacts!");',
|
|
2613
|
+
timestamp: new Date().toISOString(),
|
|
2614
|
+
action: 'write',
|
|
2615
|
+
size: 50,
|
|
2616
|
+
fullPath: testPath
|
|
2617
|
+
}]
|
|
2618
|
+
};
|
|
2619
|
+
|
|
2620
|
+
// Broadcast via WebSocket
|
|
2621
|
+
const sessionIds = this.orchestrator.webSocketManager?.getSessionsForAgent?.(agentId) || [];
|
|
2622
|
+
for (const sid of sessionIds) {
|
|
2623
|
+
this.orchestrator.webSocketManager.broadcastToSession(sid, {
|
|
2624
|
+
type: 'artifacts_updated',
|
|
2625
|
+
data: { agentId, artifacts: agent.artifacts, workingDirectory: agent.directoryAccess?.workingDirectory || '' }
|
|
2626
|
+
});
|
|
2627
|
+
}
|
|
2628
|
+
|
|
2629
|
+
res.json({ success: true, artifactCount: Object.keys(agent.artifacts).length });
|
|
2630
|
+
} catch (error) {
|
|
2631
|
+
res.status(500).json({ success: false, error: error.message });
|
|
2632
|
+
}
|
|
2633
|
+
});
|
|
2634
|
+
|
|
2635
|
+
// Get artifacts (files written by the agent) with version history
|
|
2636
|
+
this.app.get('/api/agents/:agentId/artifacts', async (req, res) => {
|
|
2637
|
+
try {
|
|
2638
|
+
const { agentId } = req.params;
|
|
2639
|
+
const agent = await this.orchestrator.agentPool.getAgent(agentId);
|
|
2640
|
+
|
|
2641
|
+
if (!agent) {
|
|
2642
|
+
return res.status(404).json({ success: false, error: 'Agent not found' });
|
|
2643
|
+
}
|
|
2644
|
+
|
|
2645
|
+
// Return the artifacts map (or empty if none yet)
|
|
2646
|
+
// { [filePath]: { displayPath, versions: [{ id, content, timestamp, action, size }] } }
|
|
2647
|
+
res.json({
|
|
2648
|
+
success: true,
|
|
2649
|
+
agentId,
|
|
2650
|
+
artifacts: agent.artifacts || {},
|
|
2651
|
+
workingDirectory: agent.directoryAccess?.workingDirectory || ''
|
|
2652
|
+
});
|
|
2653
|
+
|
|
2654
|
+
} catch (error) {
|
|
2655
|
+
this.logger.error('Failed to get agent artifacts', {
|
|
2656
|
+
agentId: req.params.agentId,
|
|
2657
|
+
error: error.message
|
|
2658
|
+
});
|
|
2659
|
+
res.status(500).json({ success: false, error: error.message });
|
|
2660
|
+
}
|
|
2661
|
+
});
|
|
2662
|
+
|
|
2579
2663
|
// ============================================
|
|
2580
2664
|
// Terminal Tasks API Endpoints
|
|
2581
2665
|
// ============================================
|
|
@@ -0,0 +1,319 @@
|
|
|
1
|
+
import { jest, describe, test, expect, beforeEach } from '@jest/globals';
|
|
2
|
+
import { createMockLogger } from '../../__test-utils__/mockFactories.js';
|
|
3
|
+
import {
|
|
4
|
+
shouldAgentBeActive,
|
|
5
|
+
getActiveAgents,
|
|
6
|
+
getAllAgentActivityStatus,
|
|
7
|
+
shouldSkipIteration,
|
|
8
|
+
hasPendingTasks,
|
|
9
|
+
getMessageQueueStatus,
|
|
10
|
+
isAgentDelayed,
|
|
11
|
+
isAgentPaused,
|
|
12
|
+
isExecutingTools
|
|
13
|
+
} from '../agentActivityService.js';
|
|
14
|
+
|
|
15
|
+
// Helper to create a base active agent
|
|
16
|
+
function makeAgent(overrides = {}) {
|
|
17
|
+
return {
|
|
18
|
+
id: 'agent-1',
|
|
19
|
+
name: 'Test Agent',
|
|
20
|
+
sessionId: 'session-1',
|
|
21
|
+
status: 'active',
|
|
22
|
+
mode: 'agent',
|
|
23
|
+
taskList: { tasks: [] },
|
|
24
|
+
messageQueues: {},
|
|
25
|
+
delayEndTime: null,
|
|
26
|
+
pausedUntil: null,
|
|
27
|
+
awaitingUserInput: null,
|
|
28
|
+
stopRequested: false,
|
|
29
|
+
toolExecutionInProgress: false,
|
|
30
|
+
ttl: null,
|
|
31
|
+
...overrides
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
describe('agentActivityService', () => {
|
|
36
|
+
describe('hasPendingTasks', () => {
|
|
37
|
+
test('returns false when taskList is null or missing', () => {
|
|
38
|
+
expect(hasPendingTasks({})).toBe(false);
|
|
39
|
+
expect(hasPendingTasks({ taskList: null })).toBe(false);
|
|
40
|
+
expect(hasPendingTasks({ taskList: {} })).toBe(false);
|
|
41
|
+
expect(hasPendingTasks({ taskList: { tasks: 'not-array' } })).toBe(false);
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
test('returns true when there are pending tasks', () => {
|
|
45
|
+
const agent = makeAgent({
|
|
46
|
+
taskList: { tasks: [{ status: 'pending' }] }
|
|
47
|
+
});
|
|
48
|
+
expect(hasPendingTasks(agent)).toBe(true);
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
test('returns true when there are in_progress tasks', () => {
|
|
52
|
+
const agent = makeAgent({
|
|
53
|
+
taskList: { tasks: [{ status: 'completed' }, { status: 'in_progress' }] }
|
|
54
|
+
});
|
|
55
|
+
expect(hasPendingTasks(agent)).toBe(true);
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
test('returns false when all tasks are completed', () => {
|
|
59
|
+
const agent = makeAgent({
|
|
60
|
+
taskList: { tasks: [{ status: 'completed' }, { status: 'failed' }] }
|
|
61
|
+
});
|
|
62
|
+
expect(hasPendingTasks(agent)).toBe(false);
|
|
63
|
+
});
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
describe('getMessageQueueStatus', () => {
|
|
67
|
+
test('returns zero counts for empty queues', () => {
|
|
68
|
+
const result = getMessageQueueStatus({});
|
|
69
|
+
expect(result.hasMessages).toBe(false);
|
|
70
|
+
expect(result.counts.total).toBe(0);
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
test('counts messages across all queues', () => {
|
|
74
|
+
const agent = makeAgent({
|
|
75
|
+
messageQueues: {
|
|
76
|
+
toolResults: [{ id: 1 }],
|
|
77
|
+
interAgentMessages: [{ id: 2 }, { id: 3 }],
|
|
78
|
+
userMessages: [{ id: 4 }]
|
|
79
|
+
}
|
|
80
|
+
});
|
|
81
|
+
const result = getMessageQueueStatus(agent);
|
|
82
|
+
expect(result.hasMessages).toBe(true);
|
|
83
|
+
expect(result.hasUserMessages).toBe(true);
|
|
84
|
+
expect(result.hasInterAgentMessages).toBe(true);
|
|
85
|
+
expect(result.hasToolResults).toBe(true);
|
|
86
|
+
expect(result.counts.total).toBe(4);
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
test('handles non-array queue values', () => {
|
|
90
|
+
const agent = makeAgent({
|
|
91
|
+
messageQueues: { toolResults: 'not-array', userMessages: null }
|
|
92
|
+
});
|
|
93
|
+
const result = getMessageQueueStatus(agent);
|
|
94
|
+
expect(result.counts.toolResults).toBe(0);
|
|
95
|
+
expect(result.counts.userMessages).toBe(0);
|
|
96
|
+
});
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
describe('isAgentDelayed', () => {
|
|
100
|
+
test('returns false when no delayEndTime', () => {
|
|
101
|
+
expect(isAgentDelayed(makeAgent())).toBe(false);
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
test('returns true when delay is in the future', () => {
|
|
105
|
+
const future = new Date(Date.now() + 60000).toISOString();
|
|
106
|
+
expect(isAgentDelayed(makeAgent({ delayEndTime: future }))).toBe(true);
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
test('returns false when delay is in the past', () => {
|
|
110
|
+
const past = new Date(Date.now() - 60000).toISOString();
|
|
111
|
+
expect(isAgentDelayed(makeAgent({ delayEndTime: past }))).toBe(false);
|
|
112
|
+
});
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
describe('isAgentPaused', () => {
|
|
116
|
+
test('returns false for active, non-paused agent', () => {
|
|
117
|
+
expect(isAgentPaused(makeAgent())).toBe(false);
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
test('returns true for paused status without expiry', () => {
|
|
121
|
+
expect(isAgentPaused(makeAgent({ status: 'paused' }))).toBe(true);
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
test('returns true for paused status with future expiry', () => {
|
|
125
|
+
const future = new Date(Date.now() + 60000).toISOString();
|
|
126
|
+
expect(isAgentPaused(makeAgent({ status: 'paused', pausedUntil: future }))).toBe(true);
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
test('returns false for paused status with past expiry', () => {
|
|
130
|
+
const past = new Date(Date.now() - 60000).toISOString();
|
|
131
|
+
expect(isAgentPaused(makeAgent({ status: 'paused', pausedUntil: past }))).toBe(false);
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
test('returns true when pausedUntil is in future even without paused status', () => {
|
|
135
|
+
const future = new Date(Date.now() + 60000).toISOString();
|
|
136
|
+
expect(isAgentPaused(makeAgent({ pausedUntil: future }))).toBe(true);
|
|
137
|
+
});
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
describe('isExecutingTools', () => {
|
|
141
|
+
test('returns true when toolExecutionInProgress is true', () => {
|
|
142
|
+
expect(isExecutingTools({ toolExecutionInProgress: true })).toBe(true);
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
test('returns false otherwise', () => {
|
|
146
|
+
expect(isExecutingTools({ toolExecutionInProgress: false })).toBe(false);
|
|
147
|
+
expect(isExecutingTools({})).toBe(false);
|
|
148
|
+
});
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
describe('shouldAgentBeActive', () => {
|
|
152
|
+
test('returns inactive for null agent', () => {
|
|
153
|
+
const result = shouldAgentBeActive(null);
|
|
154
|
+
expect(result.active).toBe(false);
|
|
155
|
+
expect(result.reason).toBe('agent-not-found');
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
test('returns inactive for non-active status', () => {
|
|
159
|
+
const result = shouldAgentBeActive(makeAgent({ status: 'idle' }));
|
|
160
|
+
expect(result.active).toBe(false);
|
|
161
|
+
expect(result.reason).toBe('agent-inactive-status');
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
test('returns inactive when delayed', () => {
|
|
165
|
+
const future = new Date(Date.now() + 60000).toISOString();
|
|
166
|
+
const result = shouldAgentBeActive(makeAgent({ delayEndTime: future }));
|
|
167
|
+
expect(result.active).toBe(false);
|
|
168
|
+
expect(result.reason).toBe('agent-delayed');
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
test('returns inactive when paused', () => {
|
|
172
|
+
const result = shouldAgentBeActive(makeAgent({ status: 'active', pausedUntil: new Date(Date.now() + 60000).toISOString() }));
|
|
173
|
+
expect(result.active).toBe(false);
|
|
174
|
+
expect(result.reason).toBe('agent-paused');
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
test('returns inactive when awaiting user input', () => {
|
|
178
|
+
const result = shouldAgentBeActive(makeAgent({ awaitingUserInput: { type: 'credentials' } }));
|
|
179
|
+
expect(result.active).toBe(false);
|
|
180
|
+
expect(result.reason).toBe('awaiting-user-input');
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
test('returns inactive when stop requested', () => {
|
|
184
|
+
const result = shouldAgentBeActive(makeAgent({ stopRequested: true }));
|
|
185
|
+
expect(result.active).toBe(false);
|
|
186
|
+
expect(result.reason).toBe('stop-requested');
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
test('returns active when TTL remaining', () => {
|
|
190
|
+
const result = shouldAgentBeActive(makeAgent({ ttl: 3 }));
|
|
191
|
+
expect(result.active).toBe(true);
|
|
192
|
+
expect(result.reason).toBe('has-ttl-remaining');
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
test('AGENT mode: active when has pending tasks', () => {
|
|
196
|
+
const result = shouldAgentBeActive(makeAgent({
|
|
197
|
+
taskList: { tasks: [{ status: 'pending' }] }
|
|
198
|
+
}));
|
|
199
|
+
expect(result.active).toBe(true);
|
|
200
|
+
expect(result.reason).toBe('has-pending-tasks');
|
|
201
|
+
});
|
|
202
|
+
|
|
203
|
+
test('AGENT mode: inactive when no pending tasks', () => {
|
|
204
|
+
const result = shouldAgentBeActive(makeAgent({
|
|
205
|
+
taskList: { tasks: [{ status: 'completed' }] }
|
|
206
|
+
}));
|
|
207
|
+
expect(result.active).toBe(false);
|
|
208
|
+
expect(result.reason).toBe('no-pending-work');
|
|
209
|
+
});
|
|
210
|
+
|
|
211
|
+
test('CHAT mode: active when has user messages', () => {
|
|
212
|
+
const result = shouldAgentBeActive(makeAgent({
|
|
213
|
+
mode: 'chat',
|
|
214
|
+
messageQueues: { userMessages: [{ id: 1 }] }
|
|
215
|
+
}));
|
|
216
|
+
expect(result.active).toBe(true);
|
|
217
|
+
expect(result.reason).toBe('has-user-messages');
|
|
218
|
+
});
|
|
219
|
+
|
|
220
|
+
test('CHAT mode: active when has inter-agent messages', () => {
|
|
221
|
+
const result = shouldAgentBeActive(makeAgent({
|
|
222
|
+
mode: 'chat',
|
|
223
|
+
messageQueues: { interAgentMessages: [{ id: 1 }] }
|
|
224
|
+
}));
|
|
225
|
+
expect(result.active).toBe(true);
|
|
226
|
+
expect(result.reason).toBe('has-inter-agent-messages');
|
|
227
|
+
});
|
|
228
|
+
|
|
229
|
+
test('CHAT mode: inactive with only tool results', () => {
|
|
230
|
+
const result = shouldAgentBeActive(makeAgent({
|
|
231
|
+
mode: 'chat',
|
|
232
|
+
messageQueues: { toolResults: [{ id: 1 }] }
|
|
233
|
+
}));
|
|
234
|
+
expect(result.active).toBe(false);
|
|
235
|
+
expect(result.reason).toBe('chat-mode-no-messages');
|
|
236
|
+
expect(result.details).toContain('tool results');
|
|
237
|
+
});
|
|
238
|
+
|
|
239
|
+
test('CHAT mode: inactive with no messages', () => {
|
|
240
|
+
const result = shouldAgentBeActive(makeAgent({ mode: 'chat' }));
|
|
241
|
+
expect(result.active).toBe(false);
|
|
242
|
+
expect(result.reason).toBe('chat-mode-no-messages');
|
|
243
|
+
});
|
|
244
|
+
|
|
245
|
+
test('returns unknown mode for unrecognized mode', () => {
|
|
246
|
+
const result = shouldAgentBeActive(makeAgent({ mode: 'weird' }));
|
|
247
|
+
expect(result.active).toBe(false);
|
|
248
|
+
expect(result.reason).toBe('unknown-mode');
|
|
249
|
+
});
|
|
250
|
+
});
|
|
251
|
+
|
|
252
|
+
describe('getActiveAgents', () => {
|
|
253
|
+
test('filters active agents from an array', () => {
|
|
254
|
+
const agents = [
|
|
255
|
+
makeAgent({ id: 'a1', taskList: { tasks: [{ status: 'pending' }] } }),
|
|
256
|
+
makeAgent({ id: 'a2', status: 'idle' }),
|
|
257
|
+
makeAgent({ id: 'a3', taskList: { tasks: [{ status: 'pending' }] } })
|
|
258
|
+
];
|
|
259
|
+
const active = getActiveAgents(agents);
|
|
260
|
+
expect(active).toHaveLength(2);
|
|
261
|
+
expect(active.map(a => a.agentId)).toEqual(['a1', 'a3']);
|
|
262
|
+
});
|
|
263
|
+
|
|
264
|
+
test('works with a Map input', () => {
|
|
265
|
+
const agents = new Map();
|
|
266
|
+
agents.set('a1', makeAgent({ id: 'a1', taskList: { tasks: [{ status: 'pending' }] } }));
|
|
267
|
+
agents.set('a2', makeAgent({ id: 'a2', status: 'idle' }));
|
|
268
|
+
const active = getActiveAgents(agents);
|
|
269
|
+
expect(active).toHaveLength(1);
|
|
270
|
+
});
|
|
271
|
+
});
|
|
272
|
+
|
|
273
|
+
describe('getAllAgentActivityStatus', () => {
|
|
274
|
+
test('returns detailed status for all agents', () => {
|
|
275
|
+
const agents = [
|
|
276
|
+
makeAgent({ id: 'a1', name: 'Agent 1', taskList: { tasks: [{ status: 'pending' }] } })
|
|
277
|
+
];
|
|
278
|
+
const statuses = getAllAgentActivityStatus(agents);
|
|
279
|
+
expect(statuses).toHaveLength(1);
|
|
280
|
+
expect(statuses[0].agentId).toBe('a1');
|
|
281
|
+
expect(statuses[0].active).toBe(true);
|
|
282
|
+
expect(statuses[0]).toHaveProperty('queueCounts');
|
|
283
|
+
expect(statuses[0]).toHaveProperty('hasPendingTasks');
|
|
284
|
+
expect(statuses[0]).toHaveProperty('isExecutingTools');
|
|
285
|
+
});
|
|
286
|
+
|
|
287
|
+
test('works with Map input', () => {
|
|
288
|
+
const map = new Map();
|
|
289
|
+
map.set('a1', makeAgent({ id: 'a1' }));
|
|
290
|
+
const statuses = getAllAgentActivityStatus(map);
|
|
291
|
+
expect(statuses).toHaveLength(1);
|
|
292
|
+
});
|
|
293
|
+
});
|
|
294
|
+
|
|
295
|
+
describe('shouldSkipIteration', () => {
|
|
296
|
+
test('returns skip for null agent', () => {
|
|
297
|
+
const result = shouldSkipIteration(null);
|
|
298
|
+
expect(result.skip).toBe(true);
|
|
299
|
+
});
|
|
300
|
+
|
|
301
|
+
test('returns skip when delayed', () => {
|
|
302
|
+
const future = new Date(Date.now() + 60000).toISOString();
|
|
303
|
+
const result = shouldSkipIteration(makeAgent({ delayEndTime: future }));
|
|
304
|
+
expect(result.skip).toBe(true);
|
|
305
|
+
});
|
|
306
|
+
|
|
307
|
+
test('returns skip when paused with future expiry', () => {
|
|
308
|
+
const future = new Date(Date.now() + 60000).toISOString();
|
|
309
|
+
const result = shouldSkipIteration(makeAgent({ pausedUntil: future }));
|
|
310
|
+
expect(result.skip).toBe(true);
|
|
311
|
+
});
|
|
312
|
+
|
|
313
|
+
test('returns no skip for normal agent', () => {
|
|
314
|
+
const result = shouldSkipIteration(makeAgent());
|
|
315
|
+
expect(result.skip).toBe(false);
|
|
316
|
+
expect(result.reason).toBeNull();
|
|
317
|
+
});
|
|
318
|
+
});
|
|
319
|
+
});
|
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
import { jest, describe, test, expect, beforeEach } from '@jest/globals';
|
|
2
|
+
import { createMockLogger } from '../../__test-utils__/mockFactories.js';
|
|
3
|
+
|
|
4
|
+
// Mock fs/promises, crypto, os, and userDataDir before importing
|
|
5
|
+
const mockFs = {
|
|
6
|
+
readFile: jest.fn(),
|
|
7
|
+
writeFile: jest.fn()
|
|
8
|
+
};
|
|
9
|
+
const mockCrypto = {
|
|
10
|
+
randomBytes: jest.fn(() => Buffer.alloc(32, 'a')),
|
|
11
|
+
pbkdf2Sync: jest.fn(() => Buffer.alloc(32, 'k')),
|
|
12
|
+
createCipheriv: jest.fn(),
|
|
13
|
+
createDecipheriv: jest.fn()
|
|
14
|
+
};
|
|
15
|
+
const mockOs = {
|
|
16
|
+
hostname: jest.fn(() => 'test-host'),
|
|
17
|
+
homedir: jest.fn(() => '/home/test'),
|
|
18
|
+
userInfo: jest.fn(() => ({ username: 'testuser' }))
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
jest.unstable_mockModule('fs', () => ({
|
|
22
|
+
promises: mockFs
|
|
23
|
+
}));
|
|
24
|
+
|
|
25
|
+
jest.unstable_mockModule('crypto', () => ({
|
|
26
|
+
default: mockCrypto,
|
|
27
|
+
...mockCrypto
|
|
28
|
+
}));
|
|
29
|
+
|
|
30
|
+
jest.unstable_mockModule('os', () => ({
|
|
31
|
+
default: mockOs,
|
|
32
|
+
...mockOs
|
|
33
|
+
}));
|
|
34
|
+
|
|
35
|
+
jest.unstable_mockModule('../../utilities/userDataDir.js', () => ({
|
|
36
|
+
getUserDataPaths: jest.fn(() => ({ settings: '/fake/settings', attachments: '/fake/attachments' })),
|
|
37
|
+
ensureUserDataDirs: jest.fn(async () => {})
|
|
38
|
+
}));
|
|
39
|
+
|
|
40
|
+
const { default: ApiKeyManager } = await import('../apiKeyManager.js');
|
|
41
|
+
|
|
42
|
+
describe('ApiKeyManager', () => {
|
|
43
|
+
let manager;
|
|
44
|
+
let logger;
|
|
45
|
+
|
|
46
|
+
beforeEach(() => {
|
|
47
|
+
jest.clearAllMocks();
|
|
48
|
+
logger = createMockLogger();
|
|
49
|
+
manager = new ApiKeyManager(logger);
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
test('constructor initializes with empty keys', () => {
|
|
53
|
+
expect(manager.keys.loxiaApiKey).toBeNull();
|
|
54
|
+
expect(manager.keys.vendorKeys).toEqual({});
|
|
55
|
+
expect(manager.initialized).toBe(false);
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
test('initialize sets up persistence and loads keys', async () => {
|
|
59
|
+
// Mock salt file read failure (new salt generation)
|
|
60
|
+
mockFs.readFile.mockRejectedValueOnce(new Error('ENOENT'));
|
|
61
|
+
// Mock writeFile for salt
|
|
62
|
+
mockFs.writeFile.mockResolvedValue(undefined);
|
|
63
|
+
// Mock readFile for loadFromDisk - ENOENT (no existing keys)
|
|
64
|
+
const enoent = new Error('No file');
|
|
65
|
+
enoent.code = 'ENOENT';
|
|
66
|
+
mockFs.readFile.mockRejectedValueOnce(enoent);
|
|
67
|
+
|
|
68
|
+
await manager.initialize();
|
|
69
|
+
expect(manager.initialized).toBe(true);
|
|
70
|
+
expect(manager.persistenceFile).toContain('api-keys.enc');
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
test('initialize only runs once', async () => {
|
|
74
|
+
manager.initialized = true;
|
|
75
|
+
await manager.initialize();
|
|
76
|
+
// Should not call ensureUserDataDirs if already initialized
|
|
77
|
+
expect(logger.info).not.toHaveBeenCalled();
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
test('initialize handles errors gracefully', async () => {
|
|
81
|
+
const { ensureUserDataDirs } = await import('../../utilities/userDataDir.js');
|
|
82
|
+
ensureUserDataDirs.mockRejectedValueOnce(new Error('disk full'));
|
|
83
|
+
|
|
84
|
+
await manager.initialize();
|
|
85
|
+
expect(logger.warn).toHaveBeenCalled();
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
test('_getMachineIdentifier returns colon-separated string', () => {
|
|
89
|
+
const id = manager._getMachineIdentifier();
|
|
90
|
+
expect(id).toContain('test-host');
|
|
91
|
+
expect(id).toContain('testuser');
|
|
92
|
+
expect(id).toContain('loxia-api-key-encryption-v1');
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
test('_encrypt throws when no encryption key', () => {
|
|
96
|
+
expect(() => manager._encrypt('test')).toThrow('Encryption key not initialized');
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
test('_decrypt throws when no encryption key', () => {
|
|
100
|
+
expect(() => manager._decrypt('test')).toThrow('Encryption key not initialized');
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
test('setSessionKeys stores loxia key and vendor keys', async () => {
|
|
104
|
+
manager.persistenceFile = null; // skip persist
|
|
105
|
+
await manager.setSessionKeys('sess-1', {
|
|
106
|
+
loxiaApiKey: 'loxia-key-123',
|
|
107
|
+
vendorKeys: { anthropic: 'anth-key' }
|
|
108
|
+
});
|
|
109
|
+
expect(manager.keys.loxiaApiKey).toBe('loxia-key-123');
|
|
110
|
+
expect(manager.keys.vendorKeys.anthropic).toBe('anth-key');
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
test('setSessionKeys merges vendor keys', async () => {
|
|
114
|
+
manager.persistenceFile = null;
|
|
115
|
+
manager.keys.vendorKeys = { openai: 'oai-key' };
|
|
116
|
+
await manager.setSessionKeys('sess-1', {
|
|
117
|
+
vendorKeys: { anthropic: 'anth-key' }
|
|
118
|
+
});
|
|
119
|
+
expect(manager.keys.vendorKeys.openai).toBe('oai-key');
|
|
120
|
+
expect(manager.keys.vendorKeys.anthropic).toBe('anth-key');
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
test('getSessionKeys returns keys regardless of sessionId', () => {
|
|
124
|
+
manager.keys.loxiaApiKey = 'test-key';
|
|
125
|
+
const keys = manager.getSessionKeys('any-session');
|
|
126
|
+
expect(keys.loxiaApiKey).toBe('test-key');
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
test('getKeysForRequest returns loxia key for platform-provided', () => {
|
|
130
|
+
manager.keys.loxiaApiKey = 'loxia-123';
|
|
131
|
+
const result = manager.getKeysForRequest('sess', { platformProvided: true, vendor: 'anthropic' });
|
|
132
|
+
expect(result.loxiaApiKey).toBe('loxia-123');
|
|
133
|
+
expect(result.vendorApiKey).toBeNull();
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
test('getKeysForRequest returns vendor key for direct access', () => {
|
|
137
|
+
manager.keys.loxiaApiKey = 'loxia-123';
|
|
138
|
+
manager.keys.vendorKeys = { anthropic: 'anth-key' };
|
|
139
|
+
const result = manager.getKeysForRequest('sess', { platformProvided: false, vendor: 'anthropic' });
|
|
140
|
+
expect(result.loxiaApiKey).toBe('loxia-123');
|
|
141
|
+
expect(result.vendorApiKey).toBe('anth-key');
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
test('getKeysForRequest returns null vendor key when no vendor specified', () => {
|
|
145
|
+
const result = manager.getKeysForRequest('sess', {});
|
|
146
|
+
expect(result.vendorApiKey).toBeNull();
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
test('removeSessionKeys clears all keys and returns true if had keys', async () => {
|
|
150
|
+
manager.persistenceFile = null;
|
|
151
|
+
manager.keys.loxiaApiKey = 'some-key';
|
|
152
|
+
const hadKeys = await manager.removeSessionKeys('sess');
|
|
153
|
+
expect(hadKeys).toBe(true);
|
|
154
|
+
expect(manager.keys.loxiaApiKey).toBeNull();
|
|
155
|
+
expect(manager.keys.vendorKeys).toEqual({});
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
test('removeSessionKeys returns false if no keys existed', async () => {
|
|
159
|
+
const hadKeys = await manager.removeSessionKeys('sess');
|
|
160
|
+
expect(hadKeys).toBe(false);
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
test('setGlobalKeys delegates to setSessionKeys', async () => {
|
|
164
|
+
manager.persistenceFile = null;
|
|
165
|
+
await manager.setGlobalKeys({ loxiaApiKey: 'global-key' });
|
|
166
|
+
expect(manager.keys.loxiaApiKey).toBe('global-key');
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
test('getActiveSessions returns empty array', () => {
|
|
170
|
+
expect(manager.getActiveSessions()).toEqual([]);
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
test('cleanupExpiredSessions returns 0', () => {
|
|
174
|
+
expect(manager.cleanupExpiredSessions()).toBe(0);
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
test('persist does nothing when not initialized', async () => {
|
|
178
|
+
manager.persistenceFile = null;
|
|
179
|
+
await manager.persist();
|
|
180
|
+
expect(mockFs.writeFile).not.toHaveBeenCalled();
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
test('loadFromDisk does nothing when not initialized', async () => {
|
|
184
|
+
manager.persistenceFile = null;
|
|
185
|
+
await manager.loadFromDisk();
|
|
186
|
+
// No errors expected
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
test('loadFromDisk handles ENOENT gracefully', async () => {
|
|
190
|
+
manager.persistenceFile = '/fake/file';
|
|
191
|
+
manager.encryptionKey = Buffer.alloc(32);
|
|
192
|
+
const err = new Error('not found');
|
|
193
|
+
err.code = 'ENOENT';
|
|
194
|
+
mockFs.readFile.mockRejectedValueOnce(err);
|
|
195
|
+
await manager.loadFromDisk();
|
|
196
|
+
expect(logger.debug).toHaveBeenCalled();
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
test('loadFromDisk handles decrypt errors gracefully', async () => {
|
|
200
|
+
manager.persistenceFile = '/fake/file';
|
|
201
|
+
manager.encryptionKey = Buffer.alloc(32);
|
|
202
|
+
mockFs.readFile.mockRejectedValueOnce(new Error('bad data'));
|
|
203
|
+
await manager.loadFromDisk();
|
|
204
|
+
expect(logger.warn).toHaveBeenCalled();
|
|
205
|
+
});
|
|
206
|
+
});
|