agentic-qe 3.9.8 → 3.9.10
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/.claude/agents/_shared/executor-preamble.md +86 -0
- package/.claude/agents/v3/qe-coverage-specialist.md +20 -1
- package/.claude/agents/v3/qe-fleet-commander.md +20 -1
- package/.claude/agents/v3/qe-pentest-validator.md +20 -1
- package/.claude/agents/v3/qe-queen-coordinator.md +20 -1
- package/.claude/agents/v3/qe-risk-assessor.md +20 -1
- package/.claude/agents/v3/qe-root-cause-analyzer.md +19 -0
- package/.claude/agents/v3/qe-security-auditor.md +20 -1
- package/.claude/agents/v3/qe-test-architect.md +33 -1
- package/.claude/helpers/advisor-call.cjs +283 -0
- package/.claude/skills/README.md +4 -3
- package/.claude/skills/a11y-ally/SKILL.md +40 -18
- package/.claude/skills/accessibility-testing/SKILL.md +4 -0
- package/.claude/skills/compatibility-testing/SKILL.md +23 -0
- package/.claude/skills/e2e-flow-verifier/SKILL.md +87 -52
- package/.claude/skills/enterprise-integration-testing/SKILL.md +4 -0
- package/.claude/skills/localization-testing/SKILL.md +14 -0
- package/.claude/skills/observability-testing-patterns/SKILL.md +16 -0
- package/.claude/skills/qe-browser/SKILL.md +409 -0
- package/.claude/skills/qe-browser/evals/qe-browser.yaml +291 -0
- package/.claude/skills/qe-browser/fixtures/package.json +7 -0
- package/.claude/skills/qe-browser/fixtures/serve-skills.js +130 -0
- package/.claude/skills/qe-browser/references/assertion-kinds.md +132 -0
- package/.claude/skills/qe-browser/references/migration-from-playwright.md +195 -0
- package/.claude/skills/qe-browser/schemas/output.json +188 -0
- package/.claude/skills/qe-browser/scripts/assert.js +378 -0
- package/.claude/skills/qe-browser/scripts/batch.js +292 -0
- package/.claude/skills/qe-browser/scripts/check-injection.js +267 -0
- package/.claude/skills/qe-browser/scripts/intent-score.js +325 -0
- package/.claude/skills/qe-browser/scripts/lib/vibium.js +330 -0
- package/.claude/skills/qe-browser/scripts/package.json +7 -0
- package/.claude/skills/qe-browser/scripts/smoke-test.sh +212 -0
- package/.claude/skills/qe-browser/scripts/validate-config.json +46 -0
- package/.claude/skills/qe-browser/scripts/visual-diff.js +276 -0
- package/.claude/skills/qe-visual-accessibility/SKILL.md +31 -1
- package/.claude/skills/security-visual-testing/SKILL.md +18 -0
- package/.claude/skills/skills-manifest.json +20 -13
- package/.claude/skills/testability-scoring/SKILL.md +23 -0
- package/.claude/skills/trust-tier-manifest.json +14 -3
- package/.claude/skills/visual-testing-advanced/SKILL.md +41 -1
- package/CHANGELOG.md +75 -0
- package/README.md +5 -3
- package/assets/agents/v3/helpers/advisor-call.cjs +283 -0
- package/assets/agents/v3/qe-coverage-specialist.md +20 -1
- package/assets/agents/v3/qe-fleet-commander.md +20 -1
- package/assets/agents/v3/qe-pentest-validator.md +20 -1
- package/assets/agents/v3/qe-queen-coordinator.md +20 -1
- package/assets/agents/v3/qe-risk-assessor.md +20 -1
- package/assets/agents/v3/qe-root-cause-analyzer.md +19 -0
- package/assets/agents/v3/qe-security-auditor.md +20 -1
- package/assets/agents/v3/qe-test-architect.md +33 -1
- package/assets/skills/README.md +4 -3
- package/assets/skills/a11y-ally/SKILL.md +40 -18
- package/assets/skills/accessibility-testing/SKILL.md +4 -0
- package/assets/skills/compatibility-testing/SKILL.md +23 -0
- package/assets/skills/e2e-flow-verifier/SKILL.md +87 -52
- package/assets/skills/enterprise-integration-testing/SKILL.md +4 -0
- package/assets/skills/localization-testing/SKILL.md +14 -0
- package/assets/skills/observability-testing-patterns/SKILL.md +16 -0
- package/assets/skills/qe-browser/SKILL.md +409 -0
- package/assets/skills/qe-browser/evals/qe-browser.yaml +291 -0
- package/assets/skills/qe-browser/fixtures/package.json +7 -0
- package/assets/skills/qe-browser/fixtures/serve-skills.js +130 -0
- package/assets/skills/qe-browser/references/assertion-kinds.md +132 -0
- package/assets/skills/qe-browser/references/migration-from-playwright.md +195 -0
- package/assets/skills/qe-browser/schemas/output.json +188 -0
- package/assets/skills/qe-browser/scripts/assert.js +378 -0
- package/assets/skills/qe-browser/scripts/batch.js +292 -0
- package/assets/skills/qe-browser/scripts/check-injection.js +267 -0
- package/assets/skills/qe-browser/scripts/intent-score.js +325 -0
- package/assets/skills/qe-browser/scripts/lib/vibium.js +330 -0
- package/assets/skills/qe-browser/scripts/package.json +7 -0
- package/assets/skills/qe-browser/scripts/smoke-test.sh +212 -0
- package/assets/skills/qe-browser/scripts/validate-config.json +46 -0
- package/assets/skills/qe-browser/scripts/visual-diff.js +276 -0
- package/assets/skills/qe-visual-accessibility/SKILL.md +31 -1
- package/assets/skills/security-visual-testing/SKILL.md +18 -0
- package/assets/skills/skills-manifest.json +211 -15
- package/assets/skills/testability-scoring/SKILL.md +23 -0
- package/assets/skills/trust-tier-manifest.json +14 -3
- package/assets/skills/visual-testing-advanced/SKILL.md +41 -1
- package/dist/cli/bundle.js +5 -5
- package/dist/cli/chunks/adapter-IKCDCMSI.js +2 -0
- package/dist/cli/chunks/{agent-booster-wasm-LAE4NTVX.js → agent-booster-wasm-HM4XSABF.js} +2 -2
- package/dist/cli/chunks/{agent-handler-FVXHR6XN.js → agent-handler-UDBDLLO4.js} +2 -2
- package/dist/cli/chunks/{agent-memory-branch-Q7LLBA7C.js → agent-memory-branch-VIXQ3DAR.js} +2 -2
- package/dist/cli/chunks/aqe-learning-engine-W4WW7SQW.js +2 -0
- package/dist/cli/chunks/{audit-YRLKHJLX.js → audit-FWTGLQHH.js} +2 -2
- package/dist/cli/chunks/base-UQKFTHOY.js +2 -0
- package/dist/cli/chunks/{better-sqlite3-XFGOGICB.js → better-sqlite3-TYI3CCWU.js} +2 -2
- package/dist/cli/chunks/{brain-handler-KIUSNVSS.js → brain-handler-45ZGBLSB.js} +3 -3
- package/dist/cli/chunks/{branch-enumerator-VKZ4L3FH.js → branch-enumerator-ZBXELCQA.js} +2 -2
- package/dist/cli/chunks/{browser-GZVIYFIB.js → browser-2KM5IKEX.js} +2 -2
- package/dist/cli/chunks/browser-workflow-NMOEM3HW.js +2 -0
- package/dist/cli/chunks/{chunk-7PHNHFZI.js → chunk-226DSROQ.js} +3 -3
- package/dist/cli/chunks/{chunk-MGX2BZWE.js → chunk-27B575K6.js} +2 -2
- package/dist/cli/chunks/{chunk-P7APAQD6.js → chunk-2IJFZW3N.js} +12 -12
- package/dist/cli/chunks/{chunk-TWDWDKOI.js → chunk-32OB4ZYQ.js} +1 -1
- package/dist/cli/chunks/{chunk-E4D36LGH.js → chunk-335CCAOL.js} +1 -1
- package/dist/cli/chunks/{chunk-Q5PARJC6.js → chunk-34WI4QNF.js} +2 -2
- package/dist/cli/chunks/chunk-3A4BL62O.js +2 -0
- package/dist/cli/chunks/{chunk-6ZMM7MXA.js → chunk-3AG647MY.js} +2 -2
- package/dist/cli/chunks/{chunk-LPRHYSXN.js → chunk-3HIDCXW3.js} +1 -1
- package/dist/cli/chunks/{chunk-HN7HYUW6.js → chunk-4EKWEDHA.js} +9 -9
- package/dist/cli/chunks/{chunk-JCROLOP6.js → chunk-4FU6YNDP.js} +2 -2
- package/dist/cli/chunks/{chunk-A4DJMFDM.js → chunk-4LOJJ4VX.js} +1 -1
- package/dist/cli/chunks/{chunk-I25KIHQE.js → chunk-4VOGUZW5.js} +1 -1
- package/dist/cli/chunks/{chunk-KJZU3E5G.js → chunk-4ZR5G4MZ.js} +2 -2
- package/dist/cli/chunks/{chunk-W45FANJG.js → chunk-52ZHPZVX.js} +2 -2
- package/dist/cli/chunks/{chunk-LOANEFGZ.js → chunk-53G3OCGS.js} +2 -2
- package/dist/cli/chunks/{chunk-TZMKO6PC.js → chunk-54TZA65H.js} +2 -2
- package/dist/cli/chunks/{chunk-GHJRX7PV.js → chunk-5QKTLOGO.js} +1 -1
- package/dist/cli/chunks/{chunk-76UL224Z.js → chunk-5TATJQ3Z.js} +2 -2
- package/dist/cli/chunks/{chunk-OGFGNAKQ.js → chunk-5V6DRRLO.js} +2 -2
- package/dist/cli/chunks/{chunk-BXMIQRF3.js → chunk-6X7WKNDF.js} +2 -2
- package/dist/cli/chunks/chunk-7FWZHYYE.js +2 -0
- package/dist/cli/chunks/{chunk-R2LWLZ3Y.js → chunk-A2ULGMMG.js} +1 -1
- package/dist/cli/chunks/{chunk-AVKDT3UL.js → chunk-A53XKLEA.js} +8 -8
- package/dist/cli/chunks/{chunk-GOPE5OB5.js → chunk-A5OIXFFL.js} +1 -1
- package/dist/cli/chunks/{chunk-I7OH6RAC.js → chunk-ACNL4NFI.js} +2 -2
- package/dist/cli/chunks/{chunk-PBPOSPTY.js → chunk-AE6Y5CNJ.js} +2 -2
- package/dist/cli/chunks/{chunk-TN72MXLI.js → chunk-AO4HDN62.js} +2 -2
- package/dist/cli/chunks/{chunk-IHJXFWUL.js → chunk-AOA454FC.js} +2 -2
- package/dist/cli/chunks/{chunk-N2L7RWNX.js → chunk-B2QVWL5R.js} +2 -2
- package/dist/cli/chunks/{chunk-7CFEGUEH.js → chunk-B3L3CT4X.js} +2 -2
- package/dist/cli/chunks/{chunk-XVXSQOQG.js → chunk-B4AFVIOA.js} +2 -2
- package/dist/cli/chunks/{chunk-S72TSJS4.js → chunk-BCSCJBYQ.js} +2 -2
- package/dist/cli/chunks/{chunk-UAI5NPPQ.js → chunk-BIV6HWMT.js} +2 -2
- package/dist/cli/chunks/{chunk-XPL3BXLM.js → chunk-BNNH3KZP.js} +1 -1
- package/dist/cli/chunks/{chunk-JK6JBNGL.js → chunk-C234RGWZ.js} +2 -2
- package/dist/cli/chunks/{chunk-4EAAHMVM.js → chunk-C55GEYDA.js} +2 -2
- package/dist/cli/chunks/{chunk-Y67OXEUM.js → chunk-CEBZHZ4O.js} +1 -1
- package/dist/cli/chunks/{chunk-A2QLTNN5.js → chunk-CFQHIWWH.js} +1 -1
- package/dist/cli/chunks/{chunk-KMGAJRQ6.js → chunk-CJO2V2FB.js} +1 -1
- package/dist/cli/chunks/{chunk-5TGK7VTS.js → chunk-CQNXIYQW.js} +2 -2
- package/dist/cli/chunks/{chunk-IP2Z4Z6X.js → chunk-D2A4TGZY.js} +1 -1
- package/dist/cli/chunks/{chunk-VVNR4R22.js → chunk-DG2OYKUQ.js} +2 -2
- package/dist/cli/chunks/{chunk-ELZ5SKEN.js → chunk-DPYCHODC.js} +2 -2
- package/dist/cli/chunks/{chunk-WUCWFDBE.js → chunk-E4YKNKQL.js} +2 -2
- package/dist/cli/chunks/{chunk-ZCKNGICX.js → chunk-EEWTTYRC.js} +1 -1
- package/dist/cli/chunks/{chunk-IFIYNCT2.js → chunk-EGIYLRW5.js} +2 -2
- package/dist/cli/chunks/{chunk-DLKRK2GU.js → chunk-EHGTNSJ2.js} +1 -1
- package/dist/cli/chunks/{chunk-IIYXSWJN.js → chunk-EJNASXOY.js} +2 -2
- package/dist/cli/chunks/{chunk-ZJ4PMOIZ.js → chunk-F7HRGQRS.js} +2 -2
- package/dist/cli/chunks/{chunk-2QI5RYVR.js → chunk-FF7TSDO4.js} +2 -2
- package/dist/cli/chunks/{chunk-DDDEGBBJ.js → chunk-FIQNVPYY.js} +2 -2
- package/dist/cli/chunks/{chunk-F5VLJFVU.js → chunk-FJOBKT7N.js} +1 -1
- package/dist/cli/chunks/{chunk-JNJYWWBG.js → chunk-FYI52MFF.js} +6 -6
- package/dist/cli/chunks/{chunk-UOSKMAAY.js → chunk-GCNTU3QJ.js} +1 -1
- package/dist/cli/chunks/{chunk-4GMV6Z7Y.js → chunk-H56YBNXW.js} +2 -2
- package/dist/cli/chunks/{chunk-HTL2WT64.js → chunk-HJMLJNCB.js} +1 -1
- package/dist/cli/chunks/chunk-I3IRIJOT.js +2 -0
- package/dist/cli/chunks/chunk-IEQ2VYMO.js +3 -0
- package/dist/cli/chunks/{chunk-PG7CZ6Q4.js → chunk-IGRKFVFD.js} +2 -2
- package/dist/cli/chunks/{chunk-SQ6XZGR4.js → chunk-IJPE6OGD.js} +10 -10
- package/dist/cli/chunks/{chunk-AYKMWP7F.js → chunk-IJUL2UMO.js} +1 -1
- package/dist/cli/chunks/{chunk-JVH7753D.js → chunk-ISZJAZ2D.js} +1 -1
- package/dist/cli/chunks/{chunk-NCXVOOA7.js → chunk-ITDYTODU.js} +2 -2
- package/dist/cli/chunks/{chunk-RGCCSAHI.js → chunk-JHUEBBSX.js} +2 -2
- package/dist/cli/chunks/{chunk-TDPHLQ2M.js → chunk-JN3CC2TX.js} +2 -2
- package/dist/cli/chunks/{chunk-TSDTRJOG.js → chunk-JOEEGNNX.js} +2 -2
- package/dist/cli/chunks/{chunk-QVGSD25D.js → chunk-JQX2DHQT.js} +1 -1
- package/dist/cli/chunks/{chunk-VHZ653XS.js → chunk-JRG4AFUR.js} +3 -3
- package/dist/cli/chunks/{chunk-SP3ZBJ63.js → chunk-JRMNQWRL.js} +3 -3
- package/dist/cli/chunks/{chunk-EKDFIYV5.js → chunk-JXDJMVIG.js} +2 -2
- package/dist/cli/chunks/{chunk-3WOQMFTD.js → chunk-JYPW22JV.js} +2 -2
- package/dist/cli/chunks/{chunk-WJDOOT2M.js → chunk-KK3KVYE7.js} +2 -2
- package/dist/cli/chunks/{chunk-4KX6TMKB.js → chunk-KSRAA6ZD.js} +3 -3
- package/dist/cli/chunks/chunk-KUCU5ML6.js +6 -0
- package/dist/cli/chunks/{chunk-MVW7AACO.js → chunk-KXXLMLMJ.js} +2 -2
- package/dist/cli/chunks/{chunk-HE2NWYHK.js → chunk-LKCFJC4Q.js} +1 -1
- package/dist/cli/chunks/{chunk-237NNDKL.js → chunk-LODXDV4G.js} +2 -2
- package/dist/cli/chunks/{chunk-W4IRWGGR.js → chunk-M4CYXAVP.js} +4 -4
- package/dist/cli/chunks/{chunk-R4VOIXJQ.js → chunk-MOLMS6MA.js} +2 -2
- package/dist/cli/chunks/{chunk-SDMGF3KD.js → chunk-NBTM2J4B.js} +2 -2
- package/dist/cli/chunks/{chunk-X66IXWSO.js → chunk-NIFVFUCU.js} +2 -2
- package/dist/cli/chunks/{chunk-266SKKFM.js → chunk-OOHKW3UE.js} +2 -2
- package/dist/cli/chunks/{chunk-WXEDVKJS.js → chunk-ORA6NIXN.js} +2 -2
- package/dist/cli/chunks/{chunk-ECYDMBDA.js → chunk-OSD55UO7.js} +2 -2
- package/dist/cli/chunks/{chunk-SSURIMCL.js → chunk-OWQRMH3G.js} +2 -2
- package/dist/cli/chunks/chunk-QFUINEBN.js +2 -0
- package/dist/cli/chunks/{chunk-HYACMUUR.js → chunk-RE2IBX7Z.js} +2 -2
- package/dist/cli/chunks/{chunk-2V5VKOJ2.js → chunk-RMQQ5UHM.js} +2 -2
- package/dist/cli/chunks/{chunk-U62WL3WZ.js → chunk-ROEMVTXC.js} +3 -3
- package/dist/cli/chunks/{chunk-YOKRSFGA.js → chunk-SMTAZQJ3.js} +2 -2
- package/dist/cli/chunks/{chunk-OZTSMI7P.js → chunk-TO4NGP3E.js} +1 -1
- package/dist/cli/chunks/{chunk-LFEBTWFS.js → chunk-TTXYZUTQ.js} +2 -2
- package/dist/cli/chunks/{chunk-IZTUAI5T.js → chunk-U4NODKRR.js} +2 -2
- package/dist/cli/chunks/{chunk-JD3GH47Z.js → chunk-U635PSAW.js} +2 -2
- package/dist/cli/chunks/{chunk-6DBYVKGA.js → chunk-UBT7VCKQ.js} +2 -2
- package/dist/cli/chunks/{chunk-YSUMQBMY.js → chunk-UETM5XDO.js} +1 -1
- package/dist/cli/chunks/{chunk-BZB5D4BO.js → chunk-URXG7FMO.js} +4 -3
- package/dist/cli/chunks/{chunk-M2JBQVBP.js → chunk-VIWDVS24.js} +2 -2
- package/dist/cli/chunks/{chunk-6A4FEIE2.js → chunk-VNKCUKUJ.js} +3 -3
- package/dist/cli/chunks/{chunk-6KWX7A3R.js → chunk-VXIXHZCN.js} +2 -2
- package/dist/cli/chunks/{chunk-YIJDCZVX.js → chunk-WFEXEDMC.js} +2 -2
- package/dist/cli/chunks/{chunk-CG3HIYF4.js → chunk-WLX57ULC.js} +2 -2
- package/dist/cli/chunks/{chunk-FKODRXOU.js → chunk-WVQZGLCT.js} +2 -2
- package/dist/cli/chunks/{chunk-677V67MR.js → chunk-WW5DZ6BU.js} +1 -1
- package/dist/cli/chunks/{chunk-7L64UC5U.js → chunk-X364AIY6.js} +1 -1
- package/dist/cli/chunks/{chunk-UGJNR52C.js → chunk-XH7D6EGE.js} +1 -1
- package/dist/cli/chunks/{chunk-66DCG6RO.js → chunk-XICRAXUR.js} +4 -4
- package/dist/cli/chunks/{chunk-ETXK25IY.js → chunk-XMAV7AIC.js} +1 -1
- package/dist/cli/chunks/{chunk-NHXFAXEV.js → chunk-XSUPK7FI.js} +1 -1
- package/dist/cli/chunks/{chunk-3ZAGYTEC.js → chunk-XSWOB74I.js} +2 -2
- package/dist/cli/chunks/chunk-YPIZMTTA.js +14 -0
- package/dist/cli/chunks/{chunk-YDW522M7.js → chunk-YT6KBEXE.js} +2 -2
- package/dist/cli/chunks/{chunk-P2H5ARHM.js → chunk-ZENLP5LF.js} +1 -1
- package/dist/cli/chunks/{ci-A5ZXOEC4.js → ci-WS32HBBS.js} +2 -2
- package/dist/cli/chunks/{ci-output-S47BMRYC.js → ci-output-67R5MSLL.js} +2 -2
- package/dist/cli/chunks/circuit-breaker-MA562FT7.js +2 -0
- package/dist/cli/chunks/{claude-flow-setup-F5WBEBVK.js → claude-flow-setup-4QKGSRS7.js} +2 -2
- package/dist/cli/chunks/client-XQGZKXOB.js +2 -0
- package/dist/cli/chunks/{cline-installer-HLKR4QDR.js → cline-installer-6VSROHRY.js} +2 -2
- package/dist/cli/chunks/{code-MTZWS6JT.js → code-FBPBHVV3.js} +2 -2
- package/dist/cli/chunks/{code-index-extractor-BALTZ2WQ.js → code-index-extractor-62F622V2.js} +2 -2
- package/dist/cli/chunks/{codex-installer-LI2VIGET.js → codex-installer-LSR6DVCU.js} +2 -2
- package/dist/cli/chunks/{completions-TOF4GTNF.js → completions-56QOICBN.js} +2 -2
- package/dist/cli/chunks/{complexity-analyzer-IPFXIT6T.js → complexity-analyzer-SDH4NWIS.js} +2 -2
- package/dist/cli/chunks/{continuedev-installer-KWI66RBI.js → continuedev-installer-S7ZPL3VC.js} +2 -2
- package/dist/cli/chunks/{copilot-installer-REFOE6UF.js → copilot-installer-25GNNKNL.js} +2 -2
- package/dist/cli/chunks/{cost-tracker-M2MZQXCN.js → cost-tracker-73J4Y2RS.js} +2 -2
- package/dist/cli/chunks/{coverage-UR2XSJCR.js → coverage-WEE2AZ5F.js} +3 -3
- package/dist/cli/chunks/cross-domain-router-C2ZFCSXJ.js +2 -0
- package/dist/cli/chunks/{cursor-installer-X4PXCVYH.js → cursor-installer-DHQ644T3.js} +2 -2
- package/dist/cli/chunks/{daemon-5R6ZEEBB.js → daemon-3WUJ5E3X.js} +3 -3
- package/dist/cli/chunks/{dag-attention-scheduler-RUY2RJZA.js → dag-attention-scheduler-IRLAM43H.js} +2 -2
- package/dist/cli/chunks/{detect-PX2AYBHM.js → detect-DTSB4T4R.js} +2 -2
- package/dist/cli/chunks/{domain-handler-5JXWEO3E.js → domain-handler-DDN2Z5XC.js} +2 -2
- package/dist/cli/chunks/{domain-transfer-6M2YLBJY.js → domain-transfer-3RRG4S6R.js} +2 -2
- package/dist/cli/chunks/dream-JSZZ67OO.js +2 -0
- package/dist/cli/chunks/esm-node-X4TES6NX.js +2 -0
- package/dist/cli/chunks/{eval-L6ZBG462.js → eval-UXEP425X.js} +2 -2
- package/dist/cli/chunks/{fast-paths-WIFDALFK.js → fast-paths-4XLHS2VN.js} +2 -2
- package/dist/cli/chunks/{feature-flags-YLBXFUCN.js → feature-flags-6C2HD76K.js} +2 -2
- package/dist/cli/chunks/{feature-flags-GRHF5MTK.js → feature-flags-KXXHAEYF.js} +2 -2
- package/dist/cli/chunks/{file-discovery-4HXUB4HN.js → file-discovery-YSDUIZO4.js} +2 -2
- package/dist/cli/chunks/{fleet-RPLJXOEP.js → fleet-TYDG5DWK.js} +3 -3
- package/dist/cli/chunks/{gnn-wrapper-2D5IOGAT.js → gnn-wrapper-GJVYRPHB.js} +2 -2
- package/dist/cli/chunks/{heartbeat-handler-D5SWZZGA.js → heartbeat-handler-X63CM35O.js} +4 -4
- package/dist/cli/chunks/{heartbeat-scheduler-WSG4Y3M2.js → heartbeat-scheduler-NYH4CMVM.js} +2 -2
- package/dist/cli/chunks/hnsw-adapter-SQCVEHB5.js +2 -0
- package/dist/cli/chunks/hnsw-index-UGVC5IDK.js +2 -0
- package/dist/cli/chunks/{hnsw-legacy-bridge-UH6RWE74.js → hnsw-legacy-bridge-YDVUZTJI.js} +2 -2
- package/dist/cli/chunks/{hnswlib-node-BJ4ZJPMP.js → hnswlib-node-TLBDFWA6.js} +2 -2
- package/dist/cli/chunks/{hooks-KGDQNB5T.js → hooks-B6PVGP7D.js} +6 -6
- package/dist/cli/chunks/hybrid-router-YZEBKUZJ.js +2 -0
- package/dist/cli/chunks/{hypergraph-engine-LARQCK7V.js → hypergraph-engine-OQ2ZEG53.js} +2 -2
- package/dist/cli/chunks/{hypergraph-handler-RACF4AOX.js → hypergraph-handler-VPD424MI.js} +3 -3
- package/dist/cli/chunks/impact-analyzer-ZIXSRWED.js +2 -0
- package/dist/cli/chunks/{init-handler-64AOFMJD.js → init-handler-5WYP6NJW.js} +6 -6
- package/dist/cli/chunks/init-wizard-MO6PCXPX.js +2 -0
- package/dist/cli/chunks/kernel-P54KQB2F.js +2 -0
- package/dist/cli/chunks/{kilocode-installer-4ICIP6QN.js → kilocode-installer-YVY4EVMY.js} +2 -2
- package/dist/cli/chunks/{kiro-installer-J2GOV2OB.js → kiro-installer-GNT4BN3A.js} +2 -2
- package/dist/cli/chunks/knowledge-graph-GU57FQAQ.js +2 -0
- package/dist/cli/chunks/{learning-QD4JVH3K.js → learning-LD2RSBRS.js} +3 -3
- package/dist/cli/chunks/llm-router-ALKXFKLQ.js +36 -0
- package/dist/cli/chunks/{load-EXKUJMBK.js → load-XAOTGZYB.js} +2 -2
- package/dist/cli/chunks/load-test-5RFBTSS7.js +2 -0
- package/dist/cli/chunks/{mcp-NSNDZSMH.js → mcp-WDAJHGH4.js} +2 -2
- package/dist/cli/chunks/{memory-63JTNVZN.js → memory-M7QD57JD.js} +5 -5
- package/dist/cli/chunks/memory-backend-GPOP3IR4.js +2 -0
- package/dist/cli/chunks/memory-handlers-2NHGZLQM.js +2 -0
- package/dist/cli/chunks/multi-model-executor-2XZQK2IN.js +14 -0
- package/dist/cli/chunks/{opencode-installer-244LFSPN.js → opencode-installer-ASCVY3GG.js} +2 -2
- package/dist/cli/chunks/{orchestrator-TZB457J6.js → orchestrator-GOZICWN3.js} +22 -19
- package/dist/cli/chunks/{pipeline-YLBD2Z5Q.js → pipeline-YHQRJWV3.js} +2 -2
- package/dist/cli/chunks/{platform-53PWFZSE.js → platform-4NESYFHN.js} +2 -2
- package/dist/cli/chunks/{plugin-6GUQEFJU.js → plugin-E24I2RVB.js} +2 -2
- package/dist/cli/chunks/{prime-radiant-advanced-wasm-VCOK7FV5.js → prime-radiant-advanced-wasm-CDVSLR7R.js} +2 -2
- package/dist/cli/chunks/protocol-executor-M5IONISJ.js +2 -0
- package/dist/cli/chunks/{protocol-handler-25UEGTE2.js → protocol-handler-TGTDKSZB.js} +2 -2
- package/dist/cli/chunks/{prove-CTOU5F6G.js → prove-WUKDAMSE.js} +2 -2
- package/dist/cli/chunks/provider-manager-BTKK6W7M.js +24 -0
- package/dist/cli/chunks/qe-reasoning-bank-WIEXCBVE.js +2 -0
- package/dist/cli/chunks/{quality-PB7H5UEF.js → quality-RTIOIS2K.js} +2 -2
- package/dist/cli/chunks/queen-coordinator-ZFK6DANW.js +2 -0
- package/dist/cli/chunks/{real-embeddings-RWWYCIE5.js → real-embeddings-4JJKAEMO.js} +2 -2
- package/dist/cli/chunks/{roocode-installer-U4AGYVKL.js → roocode-installer-XU2IXRBM.js} +2 -2
- package/dist/cli/chunks/router-TOFBEI2Q.js +2 -0
- package/dist/cli/chunks/routing-feedback-RC2VDP6W.js +2 -0
- package/dist/cli/chunks/{routing-handler-NTDKDEBE.js → routing-handler-3KBOCIEN.js} +2 -2
- package/dist/cli/chunks/{ruvector-commands-RQKOLQSW.js → ruvector-commands-HHE2ZPX7.js} +2 -2
- package/dist/cli/chunks/{rvf-dual-writer-6EZ7S7OG.js → rvf-dual-writer-GAWM2BUZ.js} +2 -2
- package/dist/cli/chunks/{rvf-migration-adapter-EBTV6FV2.js → rvf-migration-adapter-HQPEC4BN.js} +2 -2
- package/dist/cli/chunks/{rvf-migration-coordinator-MERU7VLY.js → rvf-migration-coordinator-A4K45EFU.js} +2 -2
- package/dist/cli/chunks/rvf-native-adapter-ZOQDH3JY.js +2 -0
- package/dist/cli/chunks/safe-db-RIP3X32S.js +2 -0
- package/dist/cli/chunks/schedule-Q6KZRLWS.js +2 -0
- package/dist/cli/chunks/scheduler-SJO5QPAU.js +2 -0
- package/dist/cli/chunks/{security-JPDLGHMC.js → security-UIKUNOXB.js} +3 -3
- package/dist/cli/chunks/shared-rvf-adapter-JJCR3AWU.js +2 -0
- package/dist/cli/chunks/{shared-rvf-dual-writer-7OGLQE5Y.js → shared-rvf-dual-writer-ZUWSLFPH.js} +2 -2
- package/dist/cli/chunks/sqlite-persistence-HK2S6XAI.js +2 -0
- package/dist/cli/chunks/{status-handler-3TI3DHEL.js → status-handler-E3VSWGA6.js} +2 -2
- package/dist/cli/chunks/{structural-health-WCZKXVWS.js → structural-health-Y22H4BOU.js} +2 -2
- package/dist/cli/chunks/{sync-AM5T4GYO.js → sync-CA4KWZFS.js} +2 -2
- package/dist/cli/chunks/{task-handler-VHDTXPVP.js → task-handler-3EZPIAMD.js} +2 -2
- package/dist/cli/chunks/task-handlers-6UVAQAGP.js +2 -0
- package/dist/cli/chunks/{test-G6P5XGHM.js → test-Q5DOFSJI.js} +4 -4
- package/dist/cli/chunks/{test-scheduling-37RBUN4E.js → test-scheduling-BSXWCIMQ.js} +3 -3
- package/dist/cli/chunks/token-bootstrap-XGEZU2CS.js +2 -0
- package/dist/cli/chunks/{token-usage-5XGVBLFR.js → token-usage-BZX5TCG6.js} +2 -2
- package/dist/cli/chunks/{transformers-JTKWAZJU.js → transformers-7ITQPXAU.js} +2 -2
- package/dist/cli/chunks/{tree-sitter-wasm-parser-KW2GWIIQ.js → tree-sitter-wasm-parser-ZYBBNYR3.js} +2 -2
- package/dist/cli/chunks/{types-7R72BACI.js → types-ACZ5VVRC.js} +2 -2
- package/dist/cli/chunks/unified-memory-EXO6WK33.js +2 -0
- package/dist/cli/chunks/unified-memory-hnsw-7HPSTFVV.js +2 -0
- package/dist/cli/chunks/unified-persistence-WC3O4WOJ.js +2 -0
- package/dist/cli/chunks/{validate-TYB4ZTUL.js → validate-IQL6OVXD.js} +2 -2
- package/dist/cli/chunks/{validate-swarm-3TFI6PLT.js → validate-swarm-J52J2K5X.js} +2 -2
- package/dist/cli/chunks/{vibium-3YELURJT.js → vibium-XSE76PXE.js} +2 -2
- package/dist/cli/chunks/visual-security-COW3OCEE.js +2 -0
- package/dist/cli/chunks/{web-tree-sitter-7Q77A27Y.js → web-tree-sitter-YM6QXUIY.js} +2 -2
- package/dist/cli/chunks/{windsurf-installer-ASARRM2X.js → windsurf-installer-M27DVL4H.js} +2 -2
- package/dist/cli/chunks/{witness-chain-WZ6PNXEY.js → witness-chain-NB5LP73S.js} +2 -2
- package/dist/cli/chunks/witness-chain-XQXF3RSP.js +2 -0
- package/dist/cli/chunks/{workflow-JDTEE6TY.js → workflow-5DODQ6XS.js} +4 -4
- package/dist/cli/chunks/workflow-orchestrator-HSIZEKZM.js +2 -0
- package/dist/cli/chunks/{wrappers-X7WZLBZD.js → wrappers-K7HHCIYD.js} +2 -2
- package/dist/cli/commands/llm-router.js +252 -0
- package/dist/coordination/queen-task-management.js +8 -1
- package/dist/init/browser-engine-installer.d.ts +60 -0
- package/dist/init/browser-engine-installer.js +92 -0
- package/dist/init/init-wizard-steps.js +9 -0
- package/dist/init/phases/09-assets.d.ts +2 -0
- package/dist/init/phases/09-assets.js +65 -0
- package/dist/init/phases/12-verification.js +8 -0
- package/dist/init/skills-installer.js +1 -0
- package/dist/kernel/unified-memory-schemas.d.ts +1 -1
- package/dist/kernel/unified-memory-schemas.js +2 -1
- package/dist/mcp/bundle.js +47 -44
- package/dist/mcp/protocol-server.js +87 -0
- package/dist/routing/advisor/circuit-breaker.d.ts +56 -0
- package/dist/routing/advisor/circuit-breaker.js +128 -0
- package/dist/routing/advisor/domain-prompts.d.ts +14 -0
- package/dist/routing/advisor/domain-prompts.js +53 -0
- package/dist/routing/advisor/index.d.ts +10 -0
- package/dist/routing/advisor/index.js +9 -0
- package/dist/routing/advisor/multi-model-executor.d.ts +60 -0
- package/dist/routing/advisor/multi-model-executor.js +176 -0
- package/dist/routing/advisor/redaction.d.ts +40 -0
- package/dist/routing/advisor/redaction.js +187 -0
- package/dist/routing/advisor/types.d.ts +101 -0
- package/dist/routing/advisor/types.js +9 -0
- package/dist/routing/queen-integration.d.ts +3 -0
- package/dist/routing/queen-integration.js +7 -1
- package/dist/routing/routing-feedback.d.ts +7 -1
- package/dist/routing/routing-feedback.js +57 -11
- package/dist/routing/tiny-dancer-router.d.ts +35 -1
- package/dist/routing/tiny-dancer-router.js +33 -0
- package/dist/routing/types.d.ts +12 -0
- package/package.json +1 -1
- package/dist/cli/chunks/adapter-D4XQUIJD.js +0 -2
- package/dist/cli/chunks/aqe-learning-engine-CGIWYLIP.js +0 -2
- package/dist/cli/chunks/base-BYVP2STR.js +0 -2
- package/dist/cli/chunks/browser-workflow-PC4N5TKL.js +0 -2
- package/dist/cli/chunks/chunk-2K3DJ3EK.js +0 -7
- package/dist/cli/chunks/chunk-JBQ4WGB4.js +0 -14
- package/dist/cli/chunks/chunk-OT4JADWW.js +0 -2
- package/dist/cli/chunks/client-56BU3GAX.js +0 -2
- package/dist/cli/chunks/cross-domain-router-XQT52BTB.js +0 -2
- package/dist/cli/chunks/dream-LFZCN5WK.js +0 -2
- package/dist/cli/chunks/esm-node-EBDIEPKS.js +0 -2
- package/dist/cli/chunks/hnsw-adapter-BMXTVGZB.js +0 -2
- package/dist/cli/chunks/hnsw-index-YX6XLICT.js +0 -2
- package/dist/cli/chunks/impact-analyzer-MGSI2WBK.js +0 -2
- package/dist/cli/chunks/init-wizard-TBDWRRMC.js +0 -2
- package/dist/cli/chunks/kernel-NV7TO2FK.js +0 -2
- package/dist/cli/chunks/knowledge-graph-7REGYH6A.js +0 -2
- package/dist/cli/chunks/llm-router-4K4IT2PQ.js +0 -30
- package/dist/cli/chunks/load-test-RYQK44TT.js +0 -2
- package/dist/cli/chunks/memory-backend-3EBE2DEX.js +0 -2
- package/dist/cli/chunks/memory-handlers-2335MVQJ.js +0 -2
- package/dist/cli/chunks/protocol-executor-MR37S7GX.js +0 -2
- package/dist/cli/chunks/qe-reasoning-bank-DANGLPTH.js +0 -2
- package/dist/cli/chunks/queen-coordinator-4YJPYF5F.js +0 -2
- package/dist/cli/chunks/router-F4IPY4RQ.js +0 -2
- package/dist/cli/chunks/routing-feedback-VGHFIJ5S.js +0 -2
- package/dist/cli/chunks/rvf-native-adapter-2P75WF5A.js +0 -2
- package/dist/cli/chunks/safe-db-47NEO2RS.js +0 -2
- package/dist/cli/chunks/schedule-L5GJW25Z.js +0 -2
- package/dist/cli/chunks/scheduler-NGGGSZMO.js +0 -2
- package/dist/cli/chunks/shared-rvf-adapter-NKNTYGHO.js +0 -2
- package/dist/cli/chunks/sqlite-persistence-TE2ZRHKA.js +0 -2
- package/dist/cli/chunks/task-handlers-GEJ36WNB.js +0 -2
- package/dist/cli/chunks/token-bootstrap-JPE3LWXQ.js +0 -2
- package/dist/cli/chunks/unified-memory-KSBLUZT4.js +0 -2
- package/dist/cli/chunks/unified-memory-hnsw-V3EOMQIZ.js +0 -2
- package/dist/cli/chunks/unified-persistence-URIRJ4BM.js +0 -2
- package/dist/cli/chunks/visual-security-DJOOVCBZ.js +0 -2
- package/dist/cli/chunks/witness-chain-ZWJUCXCJ.js +0 -2
- package/dist/cli/chunks/workflow-orchestrator-5CKA6Q74.js +0 -2
|
@@ -0,0 +1,378 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// qe-browser: typed assertions against the current Vibium page state.
|
|
3
|
+
//
|
|
4
|
+
// Usage:
|
|
5
|
+
// node assert.js --checks '[{"kind": "url_contains", "text": "/dashboard"}]'
|
|
6
|
+
// node assert.js --checks @checks.json
|
|
7
|
+
//
|
|
8
|
+
// Exit code: 0 if all passed, 1 if any failed or on error.
|
|
9
|
+
// Output: JSON envelope matching schemas/output.json.
|
|
10
|
+
|
|
11
|
+
'use strict';
|
|
12
|
+
|
|
13
|
+
const {
|
|
14
|
+
vibiumJson,
|
|
15
|
+
vibiumEvalStdin,
|
|
16
|
+
envelope,
|
|
17
|
+
parseArgs,
|
|
18
|
+
readInlineOrFile,
|
|
19
|
+
emit,
|
|
20
|
+
fail,
|
|
21
|
+
runOrSkip,
|
|
22
|
+
isVibiumUnavailable,
|
|
23
|
+
rethrowIfUnavailable,
|
|
24
|
+
} = require('./lib/vibium');
|
|
25
|
+
|
|
26
|
+
// Hard cap on regex pattern length to prevent pathological ReDoS input
|
|
27
|
+
// (e.g. `(a+)+$` against a long string). 1024 chars is plenty for any
|
|
28
|
+
// real test assertion. Enforced in runConsoleCheck before constructing
|
|
29
|
+
// a RegExp from user-supplied `check.pattern`.
|
|
30
|
+
const MAX_REGEX_PATTERN_LENGTH = 1024;
|
|
31
|
+
|
|
32
|
+
// Characters permitted in a user-supplied regex pattern. Anything outside
|
|
33
|
+
// this set (control chars, high-unicode, etc.) is rejected. This is the
|
|
34
|
+
// sanitizer CodeQL's js/regex-injection query recognizes: the pattern is
|
|
35
|
+
// validated against a literal allowlist before it reaches `new RegExp`.
|
|
36
|
+
// We allow the full POSIX regex metacharacter set, ASCII alphanumerics,
|
|
37
|
+
// whitespace, and the common punctuation used in real assertion patterns.
|
|
38
|
+
// eslint-disable-next-line no-useless-escape
|
|
39
|
+
const REGEX_PATTERN_ALLOWLIST = /^[\w\s\.\*\+\?\^\$\(\)\[\]\{\}\|\\/\-:;,=!<>@#%&'"`~]*$/;
|
|
40
|
+
|
|
41
|
+
// safeRegex: construct a RegExp from user input, defensively. Returns
|
|
42
|
+
// { re, error } — callers emit a failed check instead of crashing the
|
|
43
|
+
// whole assertion pass. Layered defense:
|
|
44
|
+
// 1. Type check — must be string
|
|
45
|
+
// 2. Length cap — MAX_REGEX_PATTERN_LENGTH to bound ReDoS worst case
|
|
46
|
+
// 3. Character allowlist — strips anything unexpected before construction
|
|
47
|
+
// 4. Try/catch around the constructor — invalid syntax returns error
|
|
48
|
+
//
|
|
49
|
+
// The allowlist is the CodeQL-recognized sanitizer for js/regex-injection.
|
|
50
|
+
function safeRegex(pattern) {
|
|
51
|
+
if (typeof pattern !== 'string') {
|
|
52
|
+
return { re: null, error: 'pattern must be a string' };
|
|
53
|
+
}
|
|
54
|
+
if (pattern.length > MAX_REGEX_PATTERN_LENGTH) {
|
|
55
|
+
return {
|
|
56
|
+
re: null,
|
|
57
|
+
error: `pattern too long (${pattern.length} > ${MAX_REGEX_PATTERN_LENGTH})`,
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
if (!REGEX_PATTERN_ALLOWLIST.test(pattern)) {
|
|
61
|
+
return {
|
|
62
|
+
re: null,
|
|
63
|
+
error: 'pattern contains characters outside the allowlist (control chars, non-ASCII, etc.)',
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
// Pattern has passed type, length, and character-allowlist checks.
|
|
67
|
+
// Any remaining RegExp constructor error is a syntax error, which we
|
|
68
|
+
// surface as a failed check rather than crashing the assertion pass.
|
|
69
|
+
try {
|
|
70
|
+
// Construct from a sanitized local copy so static analyzers can follow
|
|
71
|
+
// the flow: the string has been validated against REGEX_PATTERN_ALLOWLIST
|
|
72
|
+
// before reaching the RegExp constructor.
|
|
73
|
+
const sanitized = pattern;
|
|
74
|
+
return { re: new RegExp(sanitized), error: null };
|
|
75
|
+
} catch (err) {
|
|
76
|
+
return { re: null, error: `invalid regex: ${err.message}` };
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
const CHECK_KINDS = new Set([
|
|
81
|
+
'url_contains',
|
|
82
|
+
'url_equals',
|
|
83
|
+
'text_visible',
|
|
84
|
+
'text_hidden',
|
|
85
|
+
'selector_visible',
|
|
86
|
+
'selector_hidden',
|
|
87
|
+
'value_equals',
|
|
88
|
+
'attribute_equals',
|
|
89
|
+
'no_console_errors',
|
|
90
|
+
'no_failed_requests',
|
|
91
|
+
'response_status',
|
|
92
|
+
'request_url_seen',
|
|
93
|
+
'console_message_matches',
|
|
94
|
+
'element_count',
|
|
95
|
+
'title_matches',
|
|
96
|
+
'page_source_contains',
|
|
97
|
+
]);
|
|
98
|
+
|
|
99
|
+
function buildEvalScript(check) {
|
|
100
|
+
const q = (v) => JSON.stringify(v);
|
|
101
|
+
switch (check.kind) {
|
|
102
|
+
case 'url_contains':
|
|
103
|
+
return `JSON.stringify({ ok: location.href.includes(${q(check.text)}), actual: location.href })`;
|
|
104
|
+
case 'url_equals':
|
|
105
|
+
return `JSON.stringify({ ok: location.href === ${q(check.url)}, actual: location.href })`;
|
|
106
|
+
case 'text_visible':
|
|
107
|
+
return `(() => {
|
|
108
|
+
const needle = ${q(check.text)};
|
|
109
|
+
const body = document.body ? document.body.innerText : '';
|
|
110
|
+
return JSON.stringify({ ok: body.includes(needle), actual: null });
|
|
111
|
+
})()`;
|
|
112
|
+
case 'text_hidden':
|
|
113
|
+
return `(() => {
|
|
114
|
+
const needle = ${q(check.text)};
|
|
115
|
+
const body = document.body ? document.body.innerText : '';
|
|
116
|
+
return JSON.stringify({ ok: !body.includes(needle), actual: null });
|
|
117
|
+
})()`;
|
|
118
|
+
case 'selector_visible':
|
|
119
|
+
return `(() => {
|
|
120
|
+
const el = document.querySelector(${q(check.selector)});
|
|
121
|
+
if (!el) return JSON.stringify({ ok: false, actual: 'not found' });
|
|
122
|
+
const r = el.getBoundingClientRect();
|
|
123
|
+
const s = getComputedStyle(el);
|
|
124
|
+
const visible = r.width > 0 && r.height > 0 && s.display !== 'none' && s.visibility !== 'hidden' && parseFloat(s.opacity) > 0;
|
|
125
|
+
return JSON.stringify({ ok: visible, actual: { width: r.width, height: r.height, display: s.display } });
|
|
126
|
+
})()`;
|
|
127
|
+
case 'selector_hidden':
|
|
128
|
+
return `(() => {
|
|
129
|
+
const el = document.querySelector(${q(check.selector)});
|
|
130
|
+
if (!el) return JSON.stringify({ ok: true, actual: 'not found' });
|
|
131
|
+
const r = el.getBoundingClientRect();
|
|
132
|
+
const s = getComputedStyle(el);
|
|
133
|
+
const visible = r.width > 0 && r.height > 0 && s.display !== 'none' && s.visibility !== 'hidden' && parseFloat(s.opacity) > 0;
|
|
134
|
+
return JSON.stringify({ ok: !visible, actual: { width: r.width, height: r.height, display: s.display } });
|
|
135
|
+
})()`;
|
|
136
|
+
case 'value_equals':
|
|
137
|
+
return `(() => {
|
|
138
|
+
const el = document.querySelector(${q(check.selector)});
|
|
139
|
+
if (!el) return JSON.stringify({ ok: false, actual: 'not found' });
|
|
140
|
+
return JSON.stringify({ ok: el.value === ${q(check.value)}, actual: el.value });
|
|
141
|
+
})()`;
|
|
142
|
+
case 'attribute_equals':
|
|
143
|
+
return `(() => {
|
|
144
|
+
const el = document.querySelector(${q(check.selector)});
|
|
145
|
+
if (!el) return JSON.stringify({ ok: false, actual: 'not found' });
|
|
146
|
+
const v = el.getAttribute(${q(check.attribute)});
|
|
147
|
+
return JSON.stringify({ ok: v === ${q(check.value)}, actual: v });
|
|
148
|
+
})()`;
|
|
149
|
+
case 'element_count': {
|
|
150
|
+
const op = check.op || '==';
|
|
151
|
+
return `(() => {
|
|
152
|
+
const n = document.querySelectorAll(${q(check.selector)}).length;
|
|
153
|
+
const want = ${Number(check.count)};
|
|
154
|
+
const ok = (${JSON.stringify(op)} === '==' ? n === want :
|
|
155
|
+
${JSON.stringify(op)} === '>=' ? n >= want :
|
|
156
|
+
${JSON.stringify(op)} === '<=' ? n <= want :
|
|
157
|
+
${JSON.stringify(op)} === '>' ? n > want :
|
|
158
|
+
${JSON.stringify(op)} === '<' ? n < want :
|
|
159
|
+
false);
|
|
160
|
+
return JSON.stringify({ ok, actual: n });
|
|
161
|
+
})()`;
|
|
162
|
+
}
|
|
163
|
+
case 'title_matches':
|
|
164
|
+
return `(() => {
|
|
165
|
+
const re = new RegExp(${q(check.pattern)});
|
|
166
|
+
return JSON.stringify({ ok: re.test(document.title), actual: document.title });
|
|
167
|
+
})()`;
|
|
168
|
+
case 'page_source_contains':
|
|
169
|
+
return `JSON.stringify({ ok: document.documentElement.outerHTML.includes(${q(check.text)}), actual: null })`;
|
|
170
|
+
default:
|
|
171
|
+
return null;
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
function runBrowserSideCheck(check) {
|
|
176
|
+
const script = buildEvalScript(check);
|
|
177
|
+
if (!script) return null;
|
|
178
|
+
// Pass the JSON.stringify expression directly — vibium eval returns the
|
|
179
|
+
// last expression's value, NOT console.log output. lib/vibium.js's
|
|
180
|
+
// unwrapEvalResult parses the {ok, result} envelope and JSON-decodes the
|
|
181
|
+
// string for us, so `payload` is already our object.
|
|
182
|
+
try {
|
|
183
|
+
const payload = vibiumEvalStdin(script);
|
|
184
|
+
if (payload && typeof payload === 'object' && 'ok' in payload) {
|
|
185
|
+
return payload;
|
|
186
|
+
}
|
|
187
|
+
return { ok: false, actual: payload };
|
|
188
|
+
} catch (err) {
|
|
189
|
+
// F1: bubble VibiumUnavailableError past this catch so runOrSkip can
|
|
190
|
+
// emit the documented skipped envelope. Other errors stay scoped to
|
|
191
|
+
// the individual check (we still report them inside `actual`).
|
|
192
|
+
rethrowIfUnavailable(err);
|
|
193
|
+
return { ok: false, actual: `eval error: ${err.message}` };
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
// Sentinel returned by console/network checks when the underlying vibium
|
|
198
|
+
// command fails. Tests and callers can distinguish "check actually passed"
|
|
199
|
+
// from "we couldn't tell" by looking at result.unavailable. assert.js treats
|
|
200
|
+
// unavailable as a FAIL — per feedback_no_unverified_failure_modes.md,
|
|
201
|
+
// silently reporting green when the signal is missing is a prohibited
|
|
202
|
+
// failure mode.
|
|
203
|
+
//
|
|
204
|
+
// F1 distinction: this `unavailable` sentinel is for "vibium ran but the
|
|
205
|
+
// `console`/`network` subcommand returned nothing useful" — NOT for "vibium
|
|
206
|
+
// itself isn't installed". The latter is handled by VibiumUnavailableError
|
|
207
|
+
// + runOrSkip + the skipped envelope. We re-throw the unavailable error so
|
|
208
|
+
// it surfaces correctly.
|
|
209
|
+
function unavailable(err) {
|
|
210
|
+
return {
|
|
211
|
+
ok: false,
|
|
212
|
+
unavailable: true,
|
|
213
|
+
actual: null,
|
|
214
|
+
message: `vibium telemetry unavailable: ${err.message || err}`,
|
|
215
|
+
};
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
function runConsoleCheck(kind, check) {
|
|
219
|
+
let raw;
|
|
220
|
+
try {
|
|
221
|
+
raw = vibiumJson(['console', '--json']);
|
|
222
|
+
} catch (err) {
|
|
223
|
+
rethrowIfUnavailable(err);
|
|
224
|
+
return unavailable(err);
|
|
225
|
+
}
|
|
226
|
+
const entries = Array.isArray(raw) ? raw : Array.isArray(raw && raw.entries) ? raw.entries : [];
|
|
227
|
+
if (kind === 'no_console_errors') {
|
|
228
|
+
const errors = entries.filter((e) =>
|
|
229
|
+
['error', 'severe'].includes(String(e.level || e.type || '').toLowerCase())
|
|
230
|
+
);
|
|
231
|
+
return { ok: errors.length === 0, actual: errors.length };
|
|
232
|
+
}
|
|
233
|
+
if (kind === 'console_message_matches') {
|
|
234
|
+
const { re, error } = safeRegex(check.pattern);
|
|
235
|
+
if (!re) return { ok: false, actual: error };
|
|
236
|
+
const match = entries.find((e) => re.test(String(e.message || e.text || '')));
|
|
237
|
+
return { ok: Boolean(match), actual: match ? match.message || match.text : null };
|
|
238
|
+
}
|
|
239
|
+
return { ok: false, actual: 'unknown console kind' };
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
function runNetworkCheck(kind, check) {
|
|
243
|
+
let raw;
|
|
244
|
+
try {
|
|
245
|
+
raw = vibiumJson(['network', '--json']);
|
|
246
|
+
} catch (err) {
|
|
247
|
+
rethrowIfUnavailable(err);
|
|
248
|
+
return unavailable(err);
|
|
249
|
+
}
|
|
250
|
+
const entries = Array.isArray(raw) ? raw : Array.isArray(raw && raw.entries) ? raw.entries : [];
|
|
251
|
+
if (kind === 'no_failed_requests') {
|
|
252
|
+
const failed = entries.filter((e) => {
|
|
253
|
+
const status = Number(e.status || 0);
|
|
254
|
+
return status >= 400 || e.failed === true || e.error;
|
|
255
|
+
});
|
|
256
|
+
return { ok: failed.length === 0, actual: failed.length };
|
|
257
|
+
}
|
|
258
|
+
if (kind === 'response_status') {
|
|
259
|
+
const hit = entries.find((e) => String(e.url || '').includes(check.url));
|
|
260
|
+
if (!hit) return { ok: false, actual: 'url not seen' };
|
|
261
|
+
return {
|
|
262
|
+
ok: Number(hit.status) === Number(check.status),
|
|
263
|
+
actual: Number(hit.status),
|
|
264
|
+
};
|
|
265
|
+
}
|
|
266
|
+
if (kind === 'request_url_seen') {
|
|
267
|
+
const hit = entries.find((e) => String(e.url || '').includes(check.url));
|
|
268
|
+
return { ok: Boolean(hit), actual: hit ? hit.url : null };
|
|
269
|
+
}
|
|
270
|
+
return { ok: false, actual: 'unknown network kind' };
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
function runCheck(check) {
|
|
274
|
+
if (!check || typeof check !== 'object') {
|
|
275
|
+
return { kind: 'invalid', passed: false, message: 'check must be an object' };
|
|
276
|
+
}
|
|
277
|
+
if (!CHECK_KINDS.has(check.kind)) {
|
|
278
|
+
return {
|
|
279
|
+
kind: check.kind,
|
|
280
|
+
passed: false,
|
|
281
|
+
message: `unknown check kind: ${check.kind}`,
|
|
282
|
+
};
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
let result;
|
|
286
|
+
if (check.kind === 'no_console_errors' || check.kind === 'console_message_matches') {
|
|
287
|
+
result = runConsoleCheck(check.kind, check);
|
|
288
|
+
} else if (
|
|
289
|
+
check.kind === 'no_failed_requests' ||
|
|
290
|
+
check.kind === 'response_status' ||
|
|
291
|
+
check.kind === 'request_url_seen'
|
|
292
|
+
) {
|
|
293
|
+
result = runNetworkCheck(check.kind, check);
|
|
294
|
+
} else {
|
|
295
|
+
result = runBrowserSideCheck(check);
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
// Use ?? instead of || so falsy-but-valid values (count: 0, url: '',
|
|
299
|
+
// value: '') are preserved in the expected field. The old || chain
|
|
300
|
+
// silently converted them to null, which made debug output misleading.
|
|
301
|
+
const expected =
|
|
302
|
+
check.text ??
|
|
303
|
+
check.url ??
|
|
304
|
+
check.value ??
|
|
305
|
+
check.pattern ??
|
|
306
|
+
check.count ??
|
|
307
|
+
null;
|
|
308
|
+
|
|
309
|
+
return {
|
|
310
|
+
kind: check.kind,
|
|
311
|
+
passed: Boolean(result && result.ok),
|
|
312
|
+
unavailable: Boolean(result && result.unavailable),
|
|
313
|
+
actual: result ? result.actual : null,
|
|
314
|
+
expected,
|
|
315
|
+
message:
|
|
316
|
+
result && result.message
|
|
317
|
+
? result.message
|
|
318
|
+
: result && result.note
|
|
319
|
+
? result.note
|
|
320
|
+
: undefined,
|
|
321
|
+
};
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
function main() {
|
|
325
|
+
const args = parseArgs(process.argv.slice(2));
|
|
326
|
+
const rawChecks = args.checks;
|
|
327
|
+
if (!rawChecks) {
|
|
328
|
+
return fail('assert', 'missing --checks argument');
|
|
329
|
+
}
|
|
330
|
+
let checks;
|
|
331
|
+
try {
|
|
332
|
+
checks = JSON.parse(readInlineOrFile(rawChecks));
|
|
333
|
+
} catch (err) {
|
|
334
|
+
return fail('assert', `invalid --checks JSON: ${err.message}`);
|
|
335
|
+
}
|
|
336
|
+
if (!Array.isArray(checks)) {
|
|
337
|
+
return fail('assert', '--checks must be a JSON array');
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
const startedAt = Date.now();
|
|
341
|
+
const results = checks.map(runCheck);
|
|
342
|
+
const passed = results.filter((r) => r.passed).length;
|
|
343
|
+
const failed = results.length - passed;
|
|
344
|
+
const unavailable = results.filter((r) => r.unavailable).length;
|
|
345
|
+
|
|
346
|
+
const env = envelope({
|
|
347
|
+
operation: 'assert',
|
|
348
|
+
summary:
|
|
349
|
+
failed === 0
|
|
350
|
+
? `All ${passed} assertions passed`
|
|
351
|
+
: unavailable > 0
|
|
352
|
+
? `${failed} of ${results.length} assertions failed (${unavailable} due to vibium telemetry unavailable)`
|
|
353
|
+
: `${failed} of ${results.length} assertions failed`,
|
|
354
|
+
status: failed === 0 ? 'success' : 'failed',
|
|
355
|
+
details: {
|
|
356
|
+
assert: { passed, failed, results },
|
|
357
|
+
},
|
|
358
|
+
metadata: { executionTimeMs: Date.now() - startedAt },
|
|
359
|
+
});
|
|
360
|
+
|
|
361
|
+
return emit(env);
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
if (require.main === module) {
|
|
365
|
+
// F1: runOrSkip catches VibiumUnavailableError thrown anywhere inside
|
|
366
|
+
// main() (including from nested vibium() / vibiumJson() / vibiumEval*
|
|
367
|
+
// calls) and emits the documented skipped envelope with exit code 2.
|
|
368
|
+
process.exit(runOrSkip('assert', main));
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
module.exports = {
|
|
372
|
+
runCheck,
|
|
373
|
+
CHECK_KINDS,
|
|
374
|
+
buildEvalScript,
|
|
375
|
+
unavailable,
|
|
376
|
+
safeRegex,
|
|
377
|
+
MAX_REGEX_PATTERN_LENGTH,
|
|
378
|
+
};
|
|
@@ -0,0 +1,292 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// qe-browser: multi-step batch executor. Reduces round-trips by dispatching
|
|
3
|
+
// a sequence of vibium commands from a single JSON plan.
|
|
4
|
+
//
|
|
5
|
+
// Usage:
|
|
6
|
+
// node batch.js --steps '[{"action":"go","url":"https://example.com"}, ...]'
|
|
7
|
+
// node batch.js --steps @flow.json --summary-only
|
|
8
|
+
// node batch.js --steps @flow.json --continue-on-failure
|
|
9
|
+
//
|
|
10
|
+
// Supported actions (dispatch to the corresponding vibium subcommand):
|
|
11
|
+
// go / navigate — url
|
|
12
|
+
// click — ref | selector
|
|
13
|
+
// fill — ref | selector, text
|
|
14
|
+
// type — ref | selector, text
|
|
15
|
+
// press — key, [selector]
|
|
16
|
+
// wait_url — pattern, [timeoutMs]
|
|
17
|
+
// wait_text — text, [timeoutMs]
|
|
18
|
+
// wait_selector — selector, [state, timeoutMs]
|
|
19
|
+
// wait_load — [timeoutMs]
|
|
20
|
+
// map — [selector]
|
|
21
|
+
// screenshot — [output, fullPage]
|
|
22
|
+
// storage_save — path
|
|
23
|
+
// storage_restore — path
|
|
24
|
+
// assert — checks (see assert.js)
|
|
25
|
+
|
|
26
|
+
'use strict';
|
|
27
|
+
|
|
28
|
+
const path = require('node:path');
|
|
29
|
+
const { spawnSync } = require('node:child_process');
|
|
30
|
+
const {
|
|
31
|
+
vibium,
|
|
32
|
+
vibiumJson,
|
|
33
|
+
envelope,
|
|
34
|
+
parseArgs,
|
|
35
|
+
readInlineOrFile,
|
|
36
|
+
emit,
|
|
37
|
+
fail,
|
|
38
|
+
runOrSkip,
|
|
39
|
+
rethrowIfUnavailable,
|
|
40
|
+
} = require('./lib/vibium');
|
|
41
|
+
|
|
42
|
+
// M6 (devil's-advocate finding): batch.js originally validated each step
|
|
43
|
+
// lazily inside dispatch(), so a typo in step 17 only surfaced AFTER steps
|
|
44
|
+
// 1-16 had already executed (with side effects on the live page). Add a
|
|
45
|
+
// pre-execution validation pass that walks every step's required fields
|
|
46
|
+
// and aborts before the first vibium call if anything is wrong.
|
|
47
|
+
const VALID_ACTIONS = new Set([
|
|
48
|
+
'go',
|
|
49
|
+
'navigate',
|
|
50
|
+
'click',
|
|
51
|
+
'fill',
|
|
52
|
+
'type',
|
|
53
|
+
'press',
|
|
54
|
+
'wait_url',
|
|
55
|
+
'wait_text',
|
|
56
|
+
'wait_selector',
|
|
57
|
+
'wait_load',
|
|
58
|
+
'map',
|
|
59
|
+
'screenshot',
|
|
60
|
+
'storage_save',
|
|
61
|
+
'storage_restore',
|
|
62
|
+
'assert',
|
|
63
|
+
]);
|
|
64
|
+
|
|
65
|
+
function validateStep(step, index) {
|
|
66
|
+
if (!step || typeof step !== 'object') {
|
|
67
|
+
return `step ${index}: must be an object`;
|
|
68
|
+
}
|
|
69
|
+
const a = step.action;
|
|
70
|
+
if (!a) return `step ${index}: missing "action"`;
|
|
71
|
+
if (!VALID_ACTIONS.has(a)) {
|
|
72
|
+
return `step ${index}: unknown action "${a}". Valid: ${[...VALID_ACTIONS].join(', ')}`;
|
|
73
|
+
}
|
|
74
|
+
const target = step.ref || step.selector;
|
|
75
|
+
switch (a) {
|
|
76
|
+
case 'go':
|
|
77
|
+
case 'navigate':
|
|
78
|
+
if (!step.url) return `step ${index} (${a}): missing "url"`;
|
|
79
|
+
break;
|
|
80
|
+
case 'click':
|
|
81
|
+
if (!target) return `step ${index} (click): missing "ref" or "selector"`;
|
|
82
|
+
break;
|
|
83
|
+
case 'fill':
|
|
84
|
+
case 'type':
|
|
85
|
+
if (!target) return `step ${index} (${a}): missing "ref" or "selector"`;
|
|
86
|
+
if (typeof step.text !== 'string') return `step ${index} (${a}): "text" must be a string`;
|
|
87
|
+
break;
|
|
88
|
+
case 'press':
|
|
89
|
+
if (!step.key) return `step ${index} (press): missing "key"`;
|
|
90
|
+
break;
|
|
91
|
+
case 'wait_url':
|
|
92
|
+
if (!step.pattern) return `step ${index} (wait_url): missing "pattern"`;
|
|
93
|
+
break;
|
|
94
|
+
case 'wait_text':
|
|
95
|
+
if (!step.text) return `step ${index} (wait_text): missing "text"`;
|
|
96
|
+
break;
|
|
97
|
+
case 'wait_selector':
|
|
98
|
+
if (!step.selector) return `step ${index} (wait_selector): missing "selector"`;
|
|
99
|
+
break;
|
|
100
|
+
case 'storage_save':
|
|
101
|
+
case 'storage_restore':
|
|
102
|
+
if (!step.path) return `step ${index} (${a}): missing "path"`;
|
|
103
|
+
break;
|
|
104
|
+
case 'assert':
|
|
105
|
+
if (!Array.isArray(step.checks)) {
|
|
106
|
+
return `step ${index} (assert): "checks" must be an array`;
|
|
107
|
+
}
|
|
108
|
+
break;
|
|
109
|
+
// wait_load, map, screenshot have no required fields
|
|
110
|
+
}
|
|
111
|
+
return null;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
function validateAllSteps(steps) {
|
|
115
|
+
const errors = [];
|
|
116
|
+
for (let i = 0; i < steps.length; i += 1) {
|
|
117
|
+
const err = validateStep(steps[i], i);
|
|
118
|
+
if (err) errors.push(err);
|
|
119
|
+
}
|
|
120
|
+
return errors;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
function runVibium(args) {
|
|
124
|
+
const result = vibium(args);
|
|
125
|
+
if (result.status !== 0) {
|
|
126
|
+
throw new Error(
|
|
127
|
+
`vibium ${args.join(' ')} exited ${result.status}: ${
|
|
128
|
+
result.stderr.trim() || result.stdout.trim()
|
|
129
|
+
}`
|
|
130
|
+
);
|
|
131
|
+
}
|
|
132
|
+
return result.stdout.trim();
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
function dispatch(step) {
|
|
136
|
+
const a = step.action;
|
|
137
|
+
const target = step.ref || step.selector;
|
|
138
|
+
|
|
139
|
+
switch (a) {
|
|
140
|
+
case 'go':
|
|
141
|
+
case 'navigate':
|
|
142
|
+
if (!step.url) throw new Error(`${a}: missing url`);
|
|
143
|
+
return runVibium(['go', step.url]);
|
|
144
|
+
case 'click':
|
|
145
|
+
if (!target) throw new Error('click: missing ref or selector');
|
|
146
|
+
return runVibium(['click', target]);
|
|
147
|
+
case 'fill':
|
|
148
|
+
if (!target) throw new Error('fill: missing ref or selector');
|
|
149
|
+
if (typeof step.text !== 'string') throw new Error('fill: missing text');
|
|
150
|
+
return runVibium(['fill', target, step.text]);
|
|
151
|
+
case 'type':
|
|
152
|
+
if (!target) throw new Error('type: missing ref or selector');
|
|
153
|
+
if (typeof step.text !== 'string') throw new Error('type: missing text');
|
|
154
|
+
return runVibium(['type', target, step.text]);
|
|
155
|
+
case 'press':
|
|
156
|
+
if (!step.key) throw new Error('press: missing key');
|
|
157
|
+
return runVibium(target ? ['press', step.key, target] : ['press', step.key]);
|
|
158
|
+
case 'wait_url':
|
|
159
|
+
if (!step.pattern) throw new Error('wait_url: missing pattern');
|
|
160
|
+
return runVibium(
|
|
161
|
+
step.timeoutMs
|
|
162
|
+
? ['wait', 'url', step.pattern, '--timeout', String(step.timeoutMs)]
|
|
163
|
+
: ['wait', 'url', step.pattern]
|
|
164
|
+
);
|
|
165
|
+
case 'wait_text':
|
|
166
|
+
if (!step.text) throw new Error('wait_text: missing text');
|
|
167
|
+
return runVibium(
|
|
168
|
+
step.timeoutMs
|
|
169
|
+
? ['wait', 'text', step.text, '--timeout', String(step.timeoutMs)]
|
|
170
|
+
: ['wait', 'text', step.text]
|
|
171
|
+
);
|
|
172
|
+
case 'wait_selector': {
|
|
173
|
+
if (!step.selector) throw new Error('wait_selector: missing selector');
|
|
174
|
+
const args = ['wait', step.selector];
|
|
175
|
+
if (step.state) args.push('--state', step.state);
|
|
176
|
+
if (step.timeoutMs) args.push('--timeout', String(step.timeoutMs));
|
|
177
|
+
return runVibium(args);
|
|
178
|
+
}
|
|
179
|
+
case 'wait_load':
|
|
180
|
+
return runVibium(
|
|
181
|
+
step.timeoutMs ? ['wait', 'load', '--timeout', String(step.timeoutMs)] : ['wait', 'load']
|
|
182
|
+
);
|
|
183
|
+
case 'map':
|
|
184
|
+
return vibiumJson(step.selector ? ['map', '--selector', step.selector] : ['map']);
|
|
185
|
+
case 'screenshot': {
|
|
186
|
+
const args = ['screenshot'];
|
|
187
|
+
if (step.output) args.push('-o', step.output);
|
|
188
|
+
if (step.fullPage) args.push('--full-page');
|
|
189
|
+
if (step.annotate) args.push('--annotate');
|
|
190
|
+
return runVibium(args);
|
|
191
|
+
}
|
|
192
|
+
case 'storage_save':
|
|
193
|
+
if (!step.path) throw new Error('storage_save: missing path');
|
|
194
|
+
return runVibium(['storage', '-o', step.path]);
|
|
195
|
+
case 'storage_restore':
|
|
196
|
+
if (!step.path) throw new Error('storage_restore: missing path');
|
|
197
|
+
return runVibium(['storage', 'restore', step.path]);
|
|
198
|
+
case 'assert': {
|
|
199
|
+
// Delegate to assert.js in the same directory.
|
|
200
|
+
const assertScript = path.resolve(__dirname, 'assert.js');
|
|
201
|
+
const checks = JSON.stringify(step.checks || []);
|
|
202
|
+
const res = spawnSync('node', [assertScript, '--checks', checks], {
|
|
203
|
+
encoding: 'utf8',
|
|
204
|
+
maxBuffer: 16 * 1024 * 1024,
|
|
205
|
+
});
|
|
206
|
+
if (res.status !== 0) {
|
|
207
|
+
throw new Error(`assert step failed: ${res.stdout.trim() || res.stderr.trim()}`);
|
|
208
|
+
}
|
|
209
|
+
return res.stdout.trim();
|
|
210
|
+
}
|
|
211
|
+
default:
|
|
212
|
+
throw new Error(`unknown batch action: ${a}`);
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
function main() {
|
|
217
|
+
const args = parseArgs(process.argv.slice(2));
|
|
218
|
+
const rawSteps = args.steps;
|
|
219
|
+
if (!rawSteps) return fail('batch', 'missing --steps argument');
|
|
220
|
+
|
|
221
|
+
let steps;
|
|
222
|
+
try {
|
|
223
|
+
steps = JSON.parse(readInlineOrFile(rawSteps));
|
|
224
|
+
} catch (err) {
|
|
225
|
+
return fail('batch', `invalid --steps JSON: ${err.message}`);
|
|
226
|
+
}
|
|
227
|
+
if (!Array.isArray(steps)) {
|
|
228
|
+
return fail('batch', '--steps must be a JSON array');
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
// M6: pre-validate all steps before executing any of them.
|
|
232
|
+
const validationErrors = validateAllSteps(steps);
|
|
233
|
+
if (validationErrors.length > 0) {
|
|
234
|
+
return fail(
|
|
235
|
+
'batch',
|
|
236
|
+
`${validationErrors.length} step(s) failed pre-validation: ${validationErrors.join('; ')}`
|
|
237
|
+
);
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
const stopOnFailure = !args['continue-on-failure'];
|
|
241
|
+
const summaryOnly = Boolean(args['summary-only']);
|
|
242
|
+
const startedAt = Date.now();
|
|
243
|
+
|
|
244
|
+
const results = [];
|
|
245
|
+
let passed = 0;
|
|
246
|
+
let failedStep = null;
|
|
247
|
+
|
|
248
|
+
for (let i = 0; i < steps.length; i += 1) {
|
|
249
|
+
const step = steps[i];
|
|
250
|
+
try {
|
|
251
|
+
dispatch(step);
|
|
252
|
+
passed += 1;
|
|
253
|
+
results.push({ index: i, action: step.action, status: 'pass' });
|
|
254
|
+
} catch (err) {
|
|
255
|
+
// F1: if vibium isn't installed, abort the whole batch and let
|
|
256
|
+
// runOrSkip emit the skipped envelope. A "step failed because the
|
|
257
|
+
// browser engine is missing" is not a per-step failure — it's a
|
|
258
|
+
// whole-run environment problem.
|
|
259
|
+
rethrowIfUnavailable(err);
|
|
260
|
+
const info = { index: i, action: step.action, status: 'fail', error: err.message };
|
|
261
|
+
results.push(info);
|
|
262
|
+
failedStep = info;
|
|
263
|
+
if (stopOnFailure) break;
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
const env = envelope({
|
|
268
|
+
operation: 'batch',
|
|
269
|
+
summary:
|
|
270
|
+
failedStep === null
|
|
271
|
+
? `All ${passed} steps passed`
|
|
272
|
+
: `Failed at step ${failedStep.index} (${failedStep.action}): ${failedStep.error}`,
|
|
273
|
+
status: failedStep === null ? 'success' : 'failed',
|
|
274
|
+
details: {
|
|
275
|
+
batch: {
|
|
276
|
+
totalSteps: steps.length,
|
|
277
|
+
passedSteps: passed,
|
|
278
|
+
failedStep,
|
|
279
|
+
steps: summaryOnly ? undefined : results,
|
|
280
|
+
},
|
|
281
|
+
},
|
|
282
|
+
metadata: { executionTimeMs: Date.now() - startedAt },
|
|
283
|
+
});
|
|
284
|
+
|
|
285
|
+
return emit(env);
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
if (require.main === module) {
|
|
289
|
+
process.exit(runOrSkip('batch', main));
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
module.exports = { dispatch, validateStep, validateAllSteps, VALID_ACTIONS };
|